From 61500001df0193a58f64b418dd30d5ebb965e466 Mon Sep 17 00:00:00 2001 From: tanjinzhou <415800467@qq.com> Date: Thu, 16 Jul 2020 18:31:20 +0800 Subject: [PATCH] feat: update tree --- components/_util/props-util.js | 1 + components/tree/DirectoryTree.jsx | 80 +++++++++++++-------------- components/tree/Tree.jsx | 84 ++++++++++++----------------- components/tree/index.jsx | 10 ++-- components/tree/util.js | 2 +- components/vc-tree/src/Tree.jsx | 49 +++++++++-------- components/vc-tree/src/TreeNode.jsx | 52 +++++++++++------- components/vc-tree/src/util.js | 16 +++--- examples/App.vue | 2 +- examples/index.js | 2 + 10 files changed, 146 insertions(+), 152 deletions(-) diff --git a/components/_util/props-util.js b/components/_util/props-util.js index 39a3536ac..6b2664509 100644 --- a/components/_util/props-util.js +++ b/components/_util/props-util.js @@ -113,6 +113,7 @@ const getAllChildren = ele => { return ele.children || componentOptions.children || []; }; const getSlotOptions = ele => { + throw Error('使用 .type 直接取值'); if (ele.fnOptions) { // 函数式组件 return ele.fnOptions; diff --git a/components/tree/DirectoryTree.jsx b/components/tree/DirectoryTree.jsx index 681e08376..78f646278 100644 --- a/components/tree/DirectoryTree.jsx +++ b/components/tree/DirectoryTree.jsx @@ -1,10 +1,11 @@ +import { inject } from 'vue'; import omit from 'omit.js'; import debounce from 'lodash/debounce'; import FolderOpenOutlined from '@ant-design/icons-vue/FolderOpenOutlined'; import FolderOutlined from '@ant-design/icons-vue/FolderOutlined'; import FileOutlined from '@ant-design/icons-vue/FileOutlined'; import PropTypes from '../_util/vue-types'; -import warning from '../_util/warning'; +import classNames from 'classnames'; import { conductExpandParent, convertTreeToEntities } from '../vc-tree/src/util'; import Tree, { TreeProps } from './Tree'; import { @@ -14,12 +15,7 @@ import { getFullKeyListByTreeData, } from './util'; import BaseMixin from '../_util/BaseMixin'; -import { - initDefaultProps, - getOptionProps, - getListeners, - getComponentFromProp, -} from '../_util/props-util'; +import { initDefaultProps, getOptionProps, getComponent, getSlot } from '../_util/props-util'; import { ConfigConsumerProps } from '../config-provider'; // export type ExpandAction = false | 'click' | 'dblclick'; export interface @@ -27,7 +23,7 @@ import { ConfigConsumerProps } from '../config-provider'; // export interface DirectoryTreeState { expandedKeys?: string[]; // selectedKeys?: string[]; } -function getIcon(props, h) { +function getIcon(props) { const { isLeaf, expanded } = props; if (isLeaf) { return ; @@ -38,10 +34,11 @@ function getIcon(props, h) { export default { name: 'ADirectoryTree', mixins: [BaseMixin], - model: { - prop: 'checkedKeys', - event: 'check', - }, + inheritAttrs: false, + // model: { + // prop: 'checkedKeys', + // event: 'check', + // }, props: initDefaultProps( { ...TreeProps(), @@ -52,14 +49,10 @@ export default { expandAction: 'click', }, ), - - // state: DirectoryTreeState; onDebounceExpand: (event, node: AntTreeNode) => - // void; // Shift click usage lastSelectedKey?: string; cachedSelectedKeys?: - // string[]; - inject: { - configProvider: { - default: () => ConfigConsumerProps, - }, + setup() { + return { + configProvider: inject('configProvider', ConfigConsumerProps), + }; }, data() { const props = getOptionProps(this); @@ -83,6 +76,7 @@ export default { } this.onDebounceExpand = debounce(this.expandFolderNode, 200, { leading: true }); + this.children = null; return { _selectedKeys: [], _expandedKeys: [], @@ -130,7 +124,7 @@ export default { onSelect(keys, event) { const { multiple } = this.$props; - const children = this.$slots.default || []; + const children = this.children || []; const { _expandedKeys: expandedKeys = [] } = this.$data; const { node, nativeEvent } = event; const { eventKey = '' } = node; @@ -178,6 +172,9 @@ export default { this.setUncontrolledState(newState); }, + setTreeRef(node) { + this.tree = node; + }, expandFolderNode(event, node) { const { isLeaf } = node; @@ -186,9 +183,9 @@ export default { return; } - if (this.$refs.tree.$refs.tree) { + if (this.tree.tree) { // Get internal vc-tree - const internalTree = this.$refs.tree.$refs.tree; + const internalTree = this.tree.tree; // Call internal rc-tree expand function // https://github.com/ant-design/ant-design/issues/12567 @@ -208,31 +205,28 @@ export default { }, render() { + this.children = getSlot(this); const { prefixCls: customizePrefixCls, ...props } = getOptionProps(this); const getPrefixCls = this.configProvider.getPrefixCls; const prefixCls = getPrefixCls('tree', customizePrefixCls); const { _expandedKeys: expandedKeys, _selectedKeys: selectedKeys } = this.$data; - const listeners = getListeners(this); - warning(!listeners.doubleclick, '`doubleclick` is deprecated. please use `dblclick` instead.'); + const { class: className, ...restAttrs } = this.$attrs; + const connectClassName = classNames(`${prefixCls}-directory`, className); const treeProps = { - props: { - icon: getIcon, - ...props, - prefixCls, - expandedKeys, - selectedKeys, - switcherIcon: getComponentFromProp(this, 'switcherIcon'), - }, - ref: 'tree', - class: `${prefixCls}-directory`, - on: { - ...omit(listeners, ['update:selectedKeys']), - select: this.onSelect, - click: this.onClick, - dblclick: this.onDoubleClick, - expand: this.onExpand, - }, + icon: getIcon, + ...props, + ...omit(restAttrs, ['onUpdate:selectedKeys']), + prefixCls, + expandedKeys, + selectedKeys, + switcherIcon: getComponent(this, 'switcherIcon'), + ref: this.setTreeRef, + class: connectClassName, + onSelect: this.onSelect, + onClick: this.onClick, + onDblclick: this.onDoubleClick, + onExpand: this.onExpand, }; - return {this.$slots.default}; + return {this.children}; }, }; diff --git a/components/tree/Tree.jsx b/components/tree/Tree.jsx index 5b24587b4..ec2b4bd7a 100644 --- a/components/tree/Tree.jsx +++ b/components/tree/Tree.jsx @@ -1,4 +1,5 @@ -import warning from 'warning'; +import { inject } from 'vue'; +import classNames from 'classnames'; import LoadingOutlined from '@ant-design/icons-vue/LoadingOutlined'; import FileOutlined from '@ant-design/icons-vue/FileOutlined'; import CaretDownFilled from '@ant-design/icons-vue/CaretDownFilled'; @@ -7,13 +8,7 @@ import PlusSquareOutlined from '@ant-design/icons-vue/PlusSquareOutlined'; import { Tree as VcTree, TreeNode } from '../vc-tree'; import animation from '../_util/openAnimation'; import PropTypes from '../_util/vue-types'; -import { - initDefaultProps, - getOptionProps, - filterEmpty, - getComponentFromProp, - getListeners, -} from '../_util/props-util'; +import { initDefaultProps, getOptionProps, getComponent, getSlot } from '../_util/props-util'; import { cloneElement } from '../_util/vnode'; import { ConfigConsumerProps } from '../config-provider'; @@ -89,7 +84,6 @@ function TreeProps() { prefixCls: PropTypes.string, filterTreeNode: PropTypes.func, openAnimation: PropTypes.any, - treeNodes: PropTypes.array, treeData: PropTypes.array, /** * @default{title,key,children} @@ -104,27 +98,20 @@ export { TreeProps }; export default { name: 'ATree', - model: { - prop: 'checkedKeys', - event: 'check', - }, + inheritAttrs: false, props: initDefaultProps(TreeProps(), { checkable: false, showIcon: false, openAnimation: { - on: animation, - props: { appear: null }, + ...animation, + appear: null, }, blockNode: false, }), - inject: { - configProvider: { default: () => ConfigConsumerProps }, - }, - created() { - warning( - !('treeNodes' in getOptionProps(this)), - '`treeNodes` is deprecated. please use treeData instead.', - ); + setup() { + return { + configProvider: inject('configProvider', ConfigConsumerProps), + }; }, TreeNode, methods: { @@ -146,34 +133,32 @@ export default { }); } return showLine ? ( - expanded ? - : + expanded ? ( + + ) : ( + ) ) : ( ); }, updateTreeData(treeData) { - const { $slots, $scopedSlots } = this; + const { $slots } = this; const defaultFields = { children: 'children', title: 'title', key: 'key' }; const replaceFields = { ...defaultFields, ...this.$props.replaceFields }; return treeData.map(item => { const key = item[replaceFields.key]; const children = item[replaceFields.children]; - const { on = {}, slots = {}, scopedSlots = {}, class: cls, style, ...restProps } = item; + const { slots = {}, scopedSlots = {}, class: cls, style, ...restProps } = item; const treeNodeProps = { ...restProps, - icon: $scopedSlots[scopedSlots.icon] || $slots[slots.icon] || restProps.icon, + icon: $slots[scopedSlots.icon] || $slots[slots.icon] || restProps.icon, switcherIcon: - $scopedSlots[scopedSlots.switcherIcon] || + $slots[scopedSlots.switcherIcon] || $slots[slots.switcherIcon] || restProps.switcherIcon, - title: - $scopedSlots[scopedSlots.title] || - $slots[slots.title] || - restProps[replaceFields.title], + title: $slots[scopedSlots.title] || $slots[slots.title] || restProps[replaceFields.title], dataRef: item, - on, key, class: cls, style, @@ -184,37 +169,38 @@ export default { return treeNodeProps; }); }, + setTreeRef(node) { + this.tree = node; + }, }, render() { const props = getOptionProps(this); - const { $slots, $scopedSlots } = this; const { prefixCls: customizePrefixCls, showIcon, treeNodes, blockNode } = props; const getPrefixCls = this.configProvider.getPrefixCls; const prefixCls = getPrefixCls('tree', customizePrefixCls); - const switcherIcon = getComponentFromProp(this, 'switcherIcon'); + const switcherIcon = getComponent(this, 'switcherIcon'); const checkable = props.checkable; let treeData = props.treeData || treeNodes; if (treeData) { treeData = this.updateTreeData(treeData); } + const { class: className, ...restAttrs } = this.$attrs; const vcTreeProps = { - props: { - ...props, - prefixCls, - checkable: checkable ? : checkable, - children: filterEmpty($scopedSlots.default ? $scopedSlots.default() : $slots.default), - __propsSymbol__: Symbol(), - switcherIcon: nodeProps => this.renderSwitcherIcon(prefixCls, switcherIcon, nodeProps), - }, - on: getListeners(this), - ref: 'tree', - class: { + ...props, + prefixCls, + checkable: checkable ? : checkable, + children: getSlot(this), + __propsSymbol__: Symbol(), + switcherIcon: nodeProps => this.renderSwitcherIcon(prefixCls, switcherIcon, nodeProps), + ref: this.setTreeRef, + ...restAttrs, + class: classNames(className, { [`${prefixCls}-icon-hide`]: !showIcon, [`${prefixCls}-block-node`]: blockNode, - }, + }), }; if (treeData) { - vcTreeProps.props.treeData = treeData; + vcTreeProps.treeData = treeData; } return ; }, diff --git a/components/tree/index.jsx b/components/tree/index.jsx index c46914ab7..a3c9274df 100644 --- a/components/tree/index.jsx +++ b/components/tree/index.jsx @@ -1,15 +1,13 @@ import Tree from './Tree'; import DirectoryTree from './DirectoryTree'; -import Base from '../base'; Tree.TreeNode.name = 'ATreeNode'; Tree.DirectoryTree = DirectoryTree; /* istanbul ignore next */ -Tree.install = function(Vue) { - Vue.use(Base); - Vue.component(Tree.name, Tree); - Vue.component(Tree.TreeNode.name, Tree.TreeNode); - Vue.component(DirectoryTree.name, DirectoryTree); +Tree.install = function(app) { + app.component(Tree.name, Tree); + app.component(Tree.TreeNode.name, Tree.TreeNode); + app.component(DirectoryTree.name, DirectoryTree); }; export default Tree; diff --git a/components/tree/util.js b/components/tree/util.js index 496acd15f..fcba30ba4 100644 --- a/components/tree/util.js +++ b/components/tree/util.js @@ -13,7 +13,7 @@ function traverseNodesKey(rootChildren, callback) { function processNode(node) { const { key } = node; - const children = getSlots(node).default; + const children = getSlots(node); if (callback(key, node) !== false) { traverseNodesKey(typeof children === 'function' ? children() : children, callback); } diff --git a/components/vc-tree/src/Tree.jsx b/components/vc-tree/src/Tree.jsx index 6647e37be..c64ddaac0 100644 --- a/components/vc-tree/src/Tree.jsx +++ b/components/vc-tree/src/Tree.jsx @@ -4,7 +4,6 @@ import warning from 'warning'; import { hasProp, initDefaultProps, getOptionProps, getSlots } from '../../_util/props-util'; import { cloneElement } from '../../_util/vnode'; import BaseMixin from '../../_util/BaseMixin'; -import proxyComponent from '../../_util/proxyComponent'; import { convertTreeToEntities, convertDataToTree, @@ -20,6 +19,7 @@ import { mapChildren, conductCheck, warnOnlyTreeNode, + getDataAndAria, } from './util'; /** @@ -39,6 +39,7 @@ function getWatch(keys = []) { const Tree = { name: 'Tree', + inheritAttrs: false, mixins: [BaseMixin], props: initDefaultProps( { @@ -85,7 +86,7 @@ const Tree = { openTransitionName: PropTypes.string, openAnimation: PropTypes.oneOfType([PropTypes.string, PropTypes.object]), switcherIcon: PropTypes.any, - _propsSymbol: PropTypes.any, + __propsSymbol__: PropTypes.any, }, { prefixCls: 'rc-tree', @@ -169,7 +170,7 @@ const Tree = { // Check if `treeData` or `children` changed and save into the state. if (needSync('treeData')) { - treeNode = convertDataToTree(this.$createElement, props.treeData); + treeNode = convertDataToTree(props.treeData); } else if (needSync('children')) { treeNode = props.children; } @@ -248,7 +249,7 @@ const Tree = { onNodeDragStart(event, node) { const { _expandedKeys } = this.$data; const { eventKey } = node; - const children = getSlots(node).default; + const children = getSlots(node); this.dragNode = node; this.setState({ @@ -272,7 +273,7 @@ const Tree = { const { _expandedKeys: expandedKeys } = this.$data; const { pos, eventKey } = node; - if (!this.dragNode || !node.$refs.selectHandle) return; + if (!this.dragNode || !node.selectHandle) return; const dropPosition = calcDropPosition(event, node); @@ -319,7 +320,7 @@ const Tree = { const { eventKey } = node; const { _dragOverNodeKey, _dropPosition } = this.$data; // Update drag position - if (this.dragNode && eventKey === _dragOverNodeKey && node.$refs.selectHandle) { + if (this.dragNode && eventKey === _dragOverNodeKey && node.selectHandle) { const dropPosition = calcDropPosition(event, node); if (dropPosition === _dropPosition) return; @@ -479,6 +480,7 @@ const Tree = { _halfCheckedKeys: halfCheckedKeys, }); } + this.$emit('update:checkedKeys', checkedObj); this.__emit('check', checkedObj, eventObj); }, onNodeLoad(treeNode) { @@ -637,21 +639,19 @@ const Tree = { } return cloneElement(child, { - props: { - eventKey: key, - expanded: expandedKeys.indexOf(key) !== -1, - selected: selectedKeys.indexOf(key) !== -1, - loaded: loadedKeys.indexOf(key) !== -1, - loading: loadingKeys.indexOf(key) !== -1, - checked: this.isKeyChecked(key), - halfChecked: halfCheckedKeys.indexOf(key) !== -1, - pos, + eventKey: key, + expanded: expandedKeys.indexOf(key) !== -1, + selected: selectedKeys.indexOf(key) !== -1, + loaded: loadedKeys.indexOf(key) !== -1, + loading: loadingKeys.indexOf(key) !== -1, + checked: this.isKeyChecked(key), + halfChecked: halfCheckedKeys.indexOf(key) !== -1, + pos, - // [Legacy] Drag props - dragOver: dragOverNodeKey === key && dropPosition === 0, - dragOverGapTop: dragOverNodeKey === key && dropPosition === -1, - dragOverGapBottom: dragOverNodeKey === key && dropPosition === 1, - }, + // [Legacy] Drag props + dragOver: dragOverNodeKey === key && dropPosition === 0, + dragOverGapTop: dragOverNodeKey === key && dropPosition === -1, + dragOverGapBottom: dragOverNodeKey === key && dropPosition === 1, key, }); }, @@ -660,12 +660,15 @@ const Tree = { render() { const { _treeNode: treeNode } = this.$data; const { prefixCls, focusable, showLine, tabIndex = 0 } = this.$props; - + const domProps = getDataAndAria({ ...this.$props, ...this.$attrs }); + const { class: className, style } = this.$attrs; return (
    ({}) }, vcTreeNode: { default: () => ({}) }, }, - provide() { - return { - vcTreeNode: this, - }; + created() { + provide('vcTreeNode', this); }, - // Isomorphic needn't load data in server side mounted() { const { @@ -234,6 +239,10 @@ const TreeNode = { } = this; onNodeExpand(e, this); }, + // Drag usage + setSelectHandle(node) { + this.selectHandle = node; + }, getNodeChildren() { const { @@ -336,8 +345,8 @@ const TreeNode = { vcTree: { prefixCls }, } = this; const switcherIcon = - getComponentFromProp(this, 'switcherIcon', {}, false) || - getComponentFromProp(this.vcTree, 'switcherIcon', {}, false); + getComponent(this, 'switcherIcon', {}, false) || + getComponent(this.vcTree, 'switcherIcon', {}, false); if (this.isLeaf2()) { return ( {typeof currentIcon === 'function' - ? currentIcon({ ...this.$props, ...this.$props.dataRef }, h) + ? currentIcon({ ...this.$props, ...this.$props.dataRef }) : currentIcon} ) : ( @@ -445,17 +454,16 @@ const TreeNode = { let $title = currentTitle ? ( {typeof currentTitle === 'function' - ? currentTitle({ ...this.$props, ...this.$props.dataRef }, h) + ? currentTitle({ ...this.$props, ...this.$props.dataRef }) : currentTitle} ) : ( {defaultTitle} ); - return ( {this.renderSwitcher()} {this.renderCheckbox()} - {this.renderSelector(h)} + {this.renderSelector()} {this.renderChildren()} ); diff --git a/components/vc-tree/src/util.js b/components/vc-tree/src/util.js index cf5cfc1d4..5ee8dd1e9 100644 --- a/components/vc-tree/src/util.js +++ b/components/vc-tree/src/util.js @@ -1,8 +1,7 @@ /* eslint no-loop-func: 0*/ import warning from 'warning'; -import omit from 'omit.js'; import TreeNode from './TreeNode'; -import { getSlotOptions, getOptionProps } from '../../_util/props-util'; +import { getOptionProps } from '../../_util/props-util'; const DRAG_SIDE_RANGE = 0.25; const DRAG_MIN_GAP = 2; @@ -41,7 +40,7 @@ export function getPosition(level, index) { } export function isTreeNode(node) { - return getSlotOptions(node).isTreeNode; + return typeof node.type === 'object' && node.type.isTreeNode; } export function getNodeChildren(children = []) { @@ -55,7 +54,7 @@ export function isCheckDisabled(node) { export function traverseTreeNodes(treeNodes, callback) { function processNode(node, index, parent) { - const children = node ? node.componentOptions.children : treeNodes; + const children = node ? node.children?.default() : treeNodes; const pos = node ? getPosition(parent.pos, index) : 0; // Filter children @@ -111,7 +110,7 @@ export function getDragNodesKeys(treeNodes, node) { export function calcDropPosition(event, treeNode) { const { clientY } = event; - const { top, bottom, height } = treeNode.$refs.selectHandle.getBoundingClientRect(); + const { top, bottom, height } = treeNode.selectHandle.getBoundingClientRect(); const des = Math.max(height * DRAG_SIDE_RANGE, DRAG_MIN_GAP); if (clientY <= top + des) { @@ -156,20 +155,19 @@ export function calcSelectedKeys(selectedKeys, props) { const internalProcessProps = (props = {}) => { return { - props: omit(props, ['on', 'key', 'class', 'className', 'style']), - on: props.on || {}, + ...props, class: props.class || props.className, style: props.style, key: props.key, }; }; -export function convertDataToTree(h, treeData, processor) { +export function convertDataToTree(treeData, processor) { if (!treeData) return []; const { processProps = internalProcessProps } = processor || {}; const list = Array.isArray(treeData) ? treeData : [treeData]; return list.map(({ children, ...props }) => { - const childrenNodes = convertDataToTree(h, children, processor); + const childrenNodes = convertDataToTree(children, processor); return {childrenNodes}; }); } diff --git a/examples/App.vue b/examples/App.vue index 22a8dc78f..23d1d7dac 100644 --- a/examples/App.vue +++ b/examples/App.vue @@ -4,7 +4,7 @@