feat: Tree support virtual scroll (#18172)

* update tree deps

* add part of style

* flex grid

* update disabled

* update demo

* second demo

* add draggable style

* update demo

* update rc-tree version

* temp md

* update tree deps

* update icon demo

* update style

* update less

* update deps

* clean up

* update test case

* fix show line

* update snapshot

* fix lint

* update style

* update deps
This commit is contained in:
zombieJ 2019-08-12 13:22:36 +08:00 committed by GitHub
parent b30dc1c3bc
commit 8114516496
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 4341 additions and 3798 deletions

View File

@ -16769,229 +16769,271 @@ exports[`ConfigProvider components Transfer prefixCls 1`] = `
exports[`ConfigProvider components Tree configProvider 1`] = `
<div>
<ul
class="config-tree config-tree-icon-hide"
<div
class="config-tree-list config-tree config-tree-icon-hide"
role="tree"
unselectable="on"
>
<li
class="config-tree-treenode-switcher-close"
role="treeitem"
>
<span
class="config-tree-switcher config-tree-switcher-noop"
/>
<span
class="config-tree-node-content-wrapper config-tree-node-content-wrapper-normal"
title="bamboo"
<div>
<div
class="config-tree-list-holder-inner"
style="display:flex;flex-direction:column"
>
<span
class="config-tree-title"
<div
class="config-tree-treenode config-tree-treenode-switcher-close"
role="treeitem"
tabindex="0"
>
bamboo
</span>
</span>
</li>
</ul>
<ul
class="config-tree config-tree-directory"
role="tree"
unselectable="on"
>
<li
class="config-tree-treenode-switcher-close"
role="treeitem"
>
<span
class="config-tree-switcher config-tree-switcher-noop"
/>
<span
class="config-tree-node-content-wrapper config-tree-node-content-wrapper-normal"
title="bamboo"
>
<span
class="config-tree-iconEle config-tree-icon__customize"
>
<i
aria-label="icon: folder"
class="anticon anticon-folder"
<span
class="config-tree-switcher config-tree-switcher-noop"
/>
<span
class="config-tree-node-content-wrapper config-tree-node-content-wrapper-normal"
title="bamboo"
>
<svg
aria-hidden="true"
class=""
data-icon="folder"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
<span
class="config-tree-title"
>
<path
d="M880 298.4H521L403.7 186.2a8.15 8.15 0 0 0-5.5-2.2H144c-17.7 0-32 14.3-32 32v592c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V330.4c0-17.7-14.3-32-32-32zM840 768H184V256h188.5l119.6 114.4H840V768z"
/>
</svg>
</i>
</span>
<span
class="config-tree-title"
bamboo
</span>
</span>
</div>
</div>
</div>
</div>
<div
class="config-tree-list config-tree config-tree-directory config-tree-block-node"
role="tree"
>
<div>
<div
class="config-tree-list-holder-inner"
style="display:flex;flex-direction:column"
>
<div
class="config-tree-treenode config-tree-treenode-switcher-close"
role="treeitem"
tabindex="0"
>
bamboo
</span>
</span>
</li>
</ul>
<span
class="config-tree-switcher config-tree-switcher-noop"
/>
<span
class="config-tree-node-content-wrapper config-tree-node-content-wrapper-normal"
title="bamboo"
>
<span
class="config-tree-iconEle config-tree-icon__customize"
>
<i
aria-label="icon: folder"
class="anticon anticon-folder"
>
<svg
aria-hidden="true"
class=""
data-icon="folder"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M880 298.4H521L403.7 186.2a8.15 8.15 0 0 0-5.5-2.2H144c-17.7 0-32 14.3-32 32v592c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V330.4c0-17.7-14.3-32-32-32zM840 768H184V256h188.5l119.6 114.4H840V768z"
/>
</svg>
</i>
</span>
<span
class="config-tree-title"
>
bamboo
</span>
</span>
</div>
</div>
</div>
</div>
</div>
`;
exports[`ConfigProvider components Tree normal 1`] = `
<div>
<ul
class="ant-tree ant-tree-icon-hide"
<div
class="ant-tree-list ant-tree ant-tree-icon-hide"
role="tree"
unselectable="on"
>
<li
class="ant-tree-treenode-switcher-close"
role="treeitem"
>
<span
class="ant-tree-switcher ant-tree-switcher-noop"
/>
<span
class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"
title="bamboo"
<div>
<div
class="ant-tree-list-holder-inner"
style="display:flex;flex-direction:column"
>
<span
class="ant-tree-title"
<div
class="ant-tree-treenode ant-tree-treenode-switcher-close"
role="treeitem"
tabindex="0"
>
bamboo
</span>
</span>
</li>
</ul>
<ul
class="ant-tree ant-tree-directory"
role="tree"
unselectable="on"
>
<li
class="ant-tree-treenode-switcher-close"
role="treeitem"
>
<span
class="ant-tree-switcher ant-tree-switcher-noop"
/>
<span
class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"
title="bamboo"
>
<span
class="ant-tree-iconEle ant-tree-icon__customize"
>
<i
aria-label="icon: folder"
class="anticon anticon-folder"
<span
class="ant-tree-switcher ant-tree-switcher-noop"
/>
<span
class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"
title="bamboo"
>
<svg
aria-hidden="true"
class=""
data-icon="folder"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
<span
class="ant-tree-title"
>
<path
d="M880 298.4H521L403.7 186.2a8.15 8.15 0 0 0-5.5-2.2H144c-17.7 0-32 14.3-32 32v592c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V330.4c0-17.7-14.3-32-32-32zM840 768H184V256h188.5l119.6 114.4H840V768z"
/>
</svg>
</i>
</span>
<span
class="ant-tree-title"
bamboo
</span>
</span>
</div>
</div>
</div>
</div>
<div
class="ant-tree-list ant-tree ant-tree-directory ant-tree-block-node"
role="tree"
>
<div>
<div
class="ant-tree-list-holder-inner"
style="display:flex;flex-direction:column"
>
<div
class="ant-tree-treenode ant-tree-treenode-switcher-close"
role="treeitem"
tabindex="0"
>
bamboo
</span>
</span>
</li>
</ul>
<span
class="ant-tree-switcher ant-tree-switcher-noop"
/>
<span
class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"
title="bamboo"
>
<span
class="ant-tree-iconEle ant-tree-icon__customize"
>
<i
aria-label="icon: folder"
class="anticon anticon-folder"
>
<svg
aria-hidden="true"
class=""
data-icon="folder"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M880 298.4H521L403.7 186.2a8.15 8.15 0 0 0-5.5-2.2H144c-17.7 0-32 14.3-32 32v592c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V330.4c0-17.7-14.3-32-32-32zM840 768H184V256h188.5l119.6 114.4H840V768z"
/>
</svg>
</i>
</span>
<span
class="ant-tree-title"
>
bamboo
</span>
</span>
</div>
</div>
</div>
</div>
</div>
`;
exports[`ConfigProvider components Tree prefixCls 1`] = `
<div>
<ul
class="prefix-Tree prefix-Tree-icon-hide"
<div
class="prefix-Tree-list prefix-Tree prefix-Tree-icon-hide"
role="tree"
unselectable="on"
>
<li
class="prefix-Tree-treenode-switcher-close"
role="treeitem"
>
<span
class="prefix-Tree-switcher prefix-Tree-switcher-noop"
/>
<span
class="prefix-Tree-node-content-wrapper prefix-Tree-node-content-wrapper-normal"
title="bamboo"
<div>
<div
class="prefix-Tree-list-holder-inner"
style="display:flex;flex-direction:column"
>
<span
class="prefix-Tree-title"
<div
class="prefix-Tree-treenode prefix-Tree-treenode-switcher-close"
role="treeitem"
tabindex="0"
>
bamboo
</span>
</span>
</li>
</ul>
<ul
class="prefix-Tree prefix-Tree-directory"
role="tree"
unselectable="on"
>
<li
class="prefix-Tree-treenode-switcher-close"
role="treeitem"
>
<span
class="prefix-Tree-switcher prefix-Tree-switcher-noop"
/>
<span
class="prefix-Tree-node-content-wrapper prefix-Tree-node-content-wrapper-normal"
title="bamboo"
>
<span
class="prefix-Tree-iconEle prefix-Tree-icon__customize"
>
<i
aria-label="icon: folder"
class="anticon anticon-folder"
<span
class="prefix-Tree-switcher prefix-Tree-switcher-noop"
/>
<span
class="prefix-Tree-node-content-wrapper prefix-Tree-node-content-wrapper-normal"
title="bamboo"
>
<svg
aria-hidden="true"
class=""
data-icon="folder"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
<span
class="prefix-Tree-title"
>
<path
d="M880 298.4H521L403.7 186.2a8.15 8.15 0 0 0-5.5-2.2H144c-17.7 0-32 14.3-32 32v592c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V330.4c0-17.7-14.3-32-32-32zM840 768H184V256h188.5l119.6 114.4H840V768z"
/>
</svg>
</i>
</span>
<span
class="prefix-Tree-title"
bamboo
</span>
</span>
</div>
</div>
</div>
</div>
<div
class="prefix-Tree-list prefix-Tree prefix-Tree-directory prefix-Tree-block-node"
role="tree"
>
<div>
<div
class="prefix-Tree-list-holder-inner"
style="display:flex;flex-direction:column"
>
<div
class="prefix-Tree-treenode prefix-Tree-treenode-switcher-close"
role="treeitem"
tabindex="0"
>
bamboo
</span>
</span>
</li>
</ul>
<span
class="prefix-Tree-switcher prefix-Tree-switcher-noop"
/>
<span
class="prefix-Tree-node-content-wrapper prefix-Tree-node-content-wrapper-normal"
title="bamboo"
>
<span
class="prefix-Tree-iconEle prefix-Tree-icon__customize"
>
<i
aria-label="icon: folder"
class="anticon anticon-folder"
>
<svg
aria-hidden="true"
class=""
data-icon="folder"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M880 298.4H521L403.7 186.2a8.15 8.15 0 0 0-5.5-2.2H144c-17.7 0-32 14.3-32 32v592c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V330.4c0-17.7-14.3-32-32-32zM840 768H184V256h188.5l119.6 114.4H840V768z"
/>
</svg>
</i>
</span>
<span
class="prefix-Tree-title"
>
bamboo
</span>
</span>
</div>
</div>
</div>
</div>
</div>
`;

View File

@ -1,7 +1,7 @@
import * as React from 'react';
import classNames from 'classnames';
import Icon from '../icon';
import CSSMotion from 'rc-animate/lib/CSSMotion';
import Icon from '../icon';
import Col, { ColProps } from '../grid/col';
import { ValidateStatus } from './FormItem';
import { FormContext } from './context';

View File

@ -1,6 +1,6 @@
import * as React from 'react';
import warning from '../_util/warning';
import { List } from 'rc-field-form';
import warning from '../_util/warning';
interface FieldData {
name: number;

View File

@ -2547,89 +2547,100 @@ exports[`renders ./components/transfer/demo/tree-transfer.md correctly 1`] = `
<div
class="ant-transfer-list-body-customize-wrapper"
>
<ul
class="ant-tree ant-tree-icon-hide ant-tree-block-node"
<div
class="ant-tree-list ant-tree ant-tree-icon-hide ant-tree-block-node"
role="tree"
unselectable="on"
>
<li
class="ant-tree-treenode-switcher-open"
role="treeitem"
>
<span
class="ant-tree-switcher ant-tree-switcher-noop"
/>
<span
class="ant-tree-checkbox"
<div>
<div
class="ant-tree-list-holder-inner"
style="display:flex;flex-direction:column"
>
<span
class="ant-tree-checkbox-inner"
/>
</span>
<span
class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"
title="0-0"
>
<span
class="ant-tree-title"
>
0-0
</span>
</span>
</li>
<li
class="ant-tree-treenode-switcher-open"
role="treeitem"
>
<span
class="ant-tree-switcher ant-tree-switcher_open"
>
<i
aria-label="icon: caret-down"
class="anticon anticon-caret-down ant-tree-switcher-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="caret-down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="0 0 1024 1024"
width="1em"
>
<path
d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"
/>
</svg>
</i>
</span>
<span
class="ant-tree-checkbox"
>
<span
class="ant-tree-checkbox-inner"
/>
</span>
<span
class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-open"
title="0-1"
>
<span
class="ant-tree-title"
>
0-1
</span>
</span>
<ul
class="ant-tree-child-tree ant-tree-child-tree-open"
data-expanded="true"
role="group"
>
<li
class="ant-tree-treenode-switcher-open"
<div
class="ant-tree-treenode ant-tree-treenode-switcher-open"
role="treeitem"
tabindex="0"
>
<span
class="ant-tree-switcher ant-tree-switcher-noop"
/>
<span
class="ant-tree-checkbox"
>
<span
class="ant-tree-checkbox-inner"
/>
</span>
<span
class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"
title="0-0"
>
<span
class="ant-tree-title"
>
0-0
</span>
</span>
</div>
<div
class="ant-tree-treenode ant-tree-treenode-switcher-open"
role="treeitem"
tabindex="0"
>
<span
class="ant-tree-switcher ant-tree-switcher_open"
>
<i
aria-label="icon: caret-down"
class="anticon anticon-caret-down ant-tree-switcher-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="caret-down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="0 0 1024 1024"
width="1em"
>
<path
d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"
/>
</svg>
</i>
</span>
<span
class="ant-tree-checkbox"
>
<span
class="ant-tree-checkbox-inner"
/>
</span>
<span
class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-open"
title="0-1"
>
<span
class="ant-tree-title"
>
0-1
</span>
</span>
</div>
<div
class="ant-tree-treenode ant-tree-treenode-switcher-open"
role="treeitem"
tabindex="0"
>
<span
aria-hidden="true"
class="ant-tree-indent"
>
<span
class="ant-tree-indent-unit ant-tree-indent-unit-start"
/>
</span>
<span
class="ant-tree-switcher ant-tree-switcher-noop"
/>
@ -2650,11 +2661,20 @@ exports[`renders ./components/transfer/demo/tree-transfer.md correctly 1`] = `
0-1-0
</span>
</span>
</li>
<li
class="ant-tree-treenode-switcher-open"
</div>
<div
class="ant-tree-treenode ant-tree-treenode-switcher-open"
role="treeitem"
tabindex="0"
>
<span
aria-hidden="true"
class="ant-tree-indent"
>
<span
class="ant-tree-indent-unit ant-tree-indent-unit-end"
/>
</span>
<span
class="ant-tree-switcher ant-tree-switcher-noop"
/>
@ -2675,35 +2695,36 @@ exports[`renders ./components/transfer/demo/tree-transfer.md correctly 1`] = `
0-1-1
</span>
</span>
</li>
</ul>
</li>
<li
class="ant-tree-treenode-switcher-open"
role="treeitem"
>
<span
class="ant-tree-switcher ant-tree-switcher-noop"
/>
<span
class="ant-tree-checkbox"
>
<span
class="ant-tree-checkbox-inner"
/>
</span>
<span
class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"
title="0-3"
>
<span
class="ant-tree-title"
</div>
<div
class="ant-tree-treenode ant-tree-treenode-switcher-open"
role="treeitem"
tabindex="0"
>
0-3
</span>
</span>
</li>
</ul>
<span
class="ant-tree-switcher ant-tree-switcher-noop"
/>
<span
class="ant-tree-checkbox"
>
<span
class="ant-tree-checkbox-inner"
/>
</span>
<span
class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"
title="0-3"
>
<span
class="ant-tree-title"
>
0-3
</span>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@ -2,7 +2,8 @@ import * as React from 'react';
import classNames from 'classnames';
import omit from 'omit.js';
import debounce from 'lodash/debounce';
import { conductExpandParent, convertTreeToEntities } from 'rc-tree/lib/util';
import { conductExpandParent } from 'rc-tree/lib/util';
import { convertDataToEntities, convertTreeToData } from 'rc-tree/lib/utils/treeUtil';
import { polyfill } from 'react-lifecycles-compat';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
@ -13,7 +14,7 @@ import Tree, {
AntTreeNodeSelectedEvent,
AntTreeNode,
} from './Tree';
import { calcRangeKeys, getFullKeyList, convertDirectoryKeysToNodes } from './util';
import { calcRangeKeys, convertDirectoryKeysToNodes } from './util';
import Icon from '../icon';
export type ExpandAction = false | 'click' | 'doubleClick';
@ -35,6 +36,10 @@ function getIcon(props: AntdTreeNodeAttribute): React.ReactNode {
return <Icon type={expanded ? 'folder-open' : 'folder'} />;
}
function getTreeData({ treeData, children }: DirectoryTreeProps) {
return treeData || convertTreeToData(children);
}
class DirectoryTree extends React.Component<DirectoryTreeProps, DirectoryTreeState> {
static defaultProps = {
showIcon: true,
@ -66,14 +71,8 @@ class DirectoryTree extends React.Component<DirectoryTreeProps, DirectoryTreeSta
constructor(props: DirectoryTreeProps) {
super(props);
const {
defaultExpandAll,
defaultExpandParent,
expandedKeys,
defaultExpandedKeys,
children,
} = props;
const { keyEntities } = convertTreeToEntities(children);
const { defaultExpandAll, defaultExpandParent, expandedKeys, defaultExpandedKeys } = props;
const { keyEntities } = convertDataToEntities(getTreeData(props));
// Selected keys
this.state = {
@ -82,7 +81,7 @@ class DirectoryTree extends React.Component<DirectoryTreeProps, DirectoryTreeSta
// Expanded keys
if (defaultExpandAll) {
this.state.expandedKeys = getFullKeyList(props.children);
this.state.expandedKeys = Object.keys(keyEntities);
} else if (defaultExpandParent) {
this.state.expandedKeys = conductExpandParent(
expandedKeys || defaultExpandedKeys,
@ -137,11 +136,12 @@ class DirectoryTree extends React.Component<DirectoryTreeProps, DirectoryTreeSta
};
onSelect = (keys: string[], event: AntTreeNodeSelectedEvent) => {
const { onSelect, multiple, children } = this.props;
const { onSelect, multiple } = this.props;
const { expandedKeys = [] } = this.state;
const { node, nativeEvent } = event;
const { eventKey = '' } = node.props;
const treeData = getTreeData(this.props);
const newState: DirectoryTreeState = {};
// We need wrap this event since some value is not same
@ -161,22 +161,22 @@ class DirectoryTree extends React.Component<DirectoryTreeProps, DirectoryTreeSta
newSelectedKeys = keys;
this.lastSelectedKey = eventKey;
this.cachedSelectedKeys = newSelectedKeys;
newEvent.selectedNodes = convertDirectoryKeysToNodes(children, newSelectedKeys);
newEvent.selectedNodes = convertDirectoryKeysToNodes(treeData, newSelectedKeys);
} else if (multiple && shiftPick) {
// Shift click
newSelectedKeys = Array.from(
new Set([
...(this.cachedSelectedKeys || []),
...calcRangeKeys(children, expandedKeys, eventKey, this.lastSelectedKey),
...calcRangeKeys(treeData, expandedKeys, eventKey, this.lastSelectedKey),
]),
);
newEvent.selectedNodes = convertDirectoryKeysToNodes(children, newSelectedKeys);
newEvent.selectedNodes = convertDirectoryKeysToNodes(treeData, newSelectedKeys);
} else {
// Single click
newSelectedKeys = [eventKey];
this.lastSelectedKey = eventKey;
this.cachedSelectedKeys = newSelectedKeys;
newEvent.selectedNodes = [event.node];
newEvent.selectedNodes = [event.node.props.data];
}
newState.selectedKeys = newSelectedKeys;
@ -224,6 +224,7 @@ class DirectoryTree extends React.Component<DirectoryTreeProps, DirectoryTreeSta
<Tree
icon={getIcon}
ref={this.setTreeRef}
blockNode
{...props}
prefixCls={prefixCls}
className={connectClassName}

View File

@ -1,6 +1,7 @@
import * as React from 'react';
import RcTree, { TreeNode } from 'rc-tree';
import classNames from 'classnames';
import { DataNode } from 'rc-tree/lib/interface';
import DirectoryTree from './DirectoryTree';
import Icon from '../icon';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
@ -61,7 +62,7 @@ export interface AntTreeNodeCheckedEvent extends AntTreeNodeBaseEvent {
export interface AntTreeNodeSelectedEvent extends AntTreeNodeBaseEvent {
event: 'select';
selected?: boolean;
selectedNodes?: AntTreeNode[];
selectedNodes?: DataNode[];
}
export interface AntTreeNodeExpandedEvent extends AntTreeNodeBaseEvent {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,34 +1,19 @@
import React from 'react';
import { mount } from 'enzyme';
import Tree from '../index';
import { calcRangeKeys } from '../util';
const { TreeNode } = Tree;
describe('Tree util', () => {
it('calc range keys', () => {
const wrapper = mount(
<Tree>
<TreeNode key="0-0">
<TreeNode key="0-0-0" />
<TreeNode key="0-0-1" />
</TreeNode>
<TreeNode key="0-1">
<TreeNode key="0-1-0" />
<TreeNode key="0-1-1" />
</TreeNode>
<TreeNode key="0-2">
<TreeNode key="0-2-0">
<TreeNode key="0-2-0-0" />
<TreeNode key="0-2-0-1" />
<TreeNode key="0-2-0-2" />
</TreeNode>
</TreeNode>
</Tree>,
);
const treeData = [
{ key: '0-0', children: [{ key: '0-0-0' }, { key: '0-0-1' }] },
{ key: '0-1', children: [{ key: '0-1-0' }, { key: '0-1-1' }] },
{
key: '0-2',
children: [
{ key: '0-2-0', children: [{ key: '0-2-0-0' }, { key: '0-2-0-1' }, { key: '0-2-0-2' }] },
],
},
];
const { children } = wrapper.find(Tree).props();
const keys = calcRangeKeys(children, ['0-0', '0-2', '0-2-0'], '0-2-0-1', '0-0-0');
const keys = calcRangeKeys(treeData, ['0-0', '0-2', '0-2-0'], '0-2-0-1', '0-0-0');
const target = ['0-0-0', '0-0-1', '0-1', '0-2', '0-2-0', '0-2-0-0', '0-2-0-1'];
expect(keys.sort()).toEqual(target.sort());
});

View File

@ -13,7 +13,7 @@ title:
Controlled mode lets parent nodes reflect the status of child nodes more intelligently.
```jsx
```tsx
import { Tree } from 'antd';
const { TreeNode } = Tree;
@ -62,63 +62,44 @@ const treeData = [
},
];
class Demo extends React.Component {
state = {
expandedKeys: ['0-0-0', '0-0-1'],
autoExpandParent: true,
checkedKeys: ['0-0-0'],
selectedKeys: [],
};
const Demo = () => {
const [expandedKeys, setExpandedKeys] = React.useState<string[]>(['0-0-0', '0-0-1']);
const [checkedKeys, setCheckedKeys] = React.useState<string[]>(['0-0-0']);
const [selectedKeys, setSelectedKeys] = React.useState<string[]>([]);
const [autoExpandParent, setAutoExpandParent] = React.useState<boolean>(true);
onExpand = expandedKeys => {
const onExpand = expandedKeys => {
console.log('onExpand', expandedKeys);
// if not set autoExpandParent to false, if children expanded, parent can not collapse.
// or, you can remove all expanded children keys.
this.setState({
expandedKeys,
autoExpandParent: false,
});
setExpandedKeys(expandedKeys);
setAutoExpandParent(false);
};
onCheck = checkedKeys => {
const onCheck = checkedKeys => {
console.log('onCheck', checkedKeys);
this.setState({ checkedKeys });
setCheckedKeys(checkedKeys);
};
onSelect = (selectedKeys, info) => {
const onSelect = (selectedKeys, info) => {
console.log('onSelect', info);
this.setState({ selectedKeys });
setSelectedKeys(selectedKeys);
};
renderTreeNodes = data =>
data.map(item => {
if (item.children) {
return (
<TreeNode title={item.title} key={item.key} dataRef={item}>
{this.renderTreeNodes(item.children)}
</TreeNode>
);
}
return <TreeNode key={item.key} {...item} />;
});
render() {
return (
<Tree
checkable
onExpand={this.onExpand}
expandedKeys={this.state.expandedKeys}
autoExpandParent={this.state.autoExpandParent}
onCheck={this.onCheck}
checkedKeys={this.state.checkedKeys}
onSelect={this.onSelect}
selectedKeys={this.state.selectedKeys}
>
{this.renderTreeNodes(treeData)}
</Tree>
);
}
}
return (
<Tree
checkable
onExpand={onExpand}
expandedKeys={expandedKeys}
autoExpandParent={autoExpandParent}
onCheck={onCheck}
checkedKeys={checkedKeys}
onSelect={onSelect}
selectedKeys={selectedKeys}
treeData={treeData}
/>
);
};
ReactDOM.render(<Demo />, mountNode);
```

View File

@ -13,43 +13,62 @@ title:
The most basic usage, tell you how to use checkable, selectable, disabled, defaultExpandKeys, and etc.
```jsx
```tsx
import { Tree } from 'antd';
const { TreeNode } = Tree;
class Demo extends React.Component {
onSelect = (selectedKeys, info) => {
const treeData = [
{
title: 'parent 1',
key: '0-0',
children: [
{
title: 'parent 1-0',
key: '0-0-0',
disabled: true,
children: [
{
title: 'leaf',
key: '0-0-0-0',
disableCheckbox: true,
},
{
title: 'leaf',
key: '0-0-0-1',
},
],
},
{
title: 'parent 1-1',
key: '0-0-1',
children: [{ title: <span style={{ color: '#1890ff' }}>sss</span>, key: '0-0-1-0' }],
},
],
},
];
const Demo = () => {
const onSelect = (selectedKeys, info) => {
console.log('selected', selectedKeys, info);
};
onCheck = (checkedKeys, info) => {
const onCheck = (checkedKeys, info) => {
console.log('onCheck', checkedKeys, info);
};
render() {
return (
<Tree
checkable
defaultExpandedKeys={['0-0-0', '0-0-1']}
defaultSelectedKeys={['0-0-0', '0-0-1']}
defaultCheckedKeys={['0-0-0', '0-0-1']}
onSelect={this.onSelect}
onCheck={this.onCheck}
>
<TreeNode title="parent 1" key="0-0">
<TreeNode title="parent 1-0" key="0-0-0" disabled>
<TreeNode title="leaf" key="0-0-0-0" disableCheckbox />
<TreeNode title="leaf" key="0-0-0-1" />
</TreeNode>
<TreeNode title="parent 1-1" key="0-0-1">
<TreeNode title={<span style={{ color: '#1890ff' }}>sss</span>} key="0-0-1-0" />
</TreeNode>
</TreeNode>
</Tree>
);
}
}
return (
<Tree
checkable
defaultExpandedKeys={['0-0-0', '0-0-1']}
defaultSelectedKeys={['0-0-0', '0-0-1']}
defaultCheckedKeys={['0-0-0', '0-0-1']}
onSelect={onSelect}
onCheck={onCheck}
treeData={treeData}
/>
);
};
ReactDOM.render(<Demo />, mountNode);
```

View File

@ -16,7 +16,25 @@ You can customize icons for different nodes.
```jsx
import { Tree, Icon } from 'antd';
const { TreeNode } = Tree;
const treeData = [
{
title: 'parent 1',
key: '0-0',
icon: <Icon type="smile-o" />,
children: [
{
title: 'leaf',
key: '0-0-0',
icon: <Icon type="meh-o" />,
},
{
title: 'leaf',
key: '0-0-1',
icon: ({ selected }) => <Icon type="frown" theme={selected ? 'filled' : 'outlined'} />,
},
],
},
];
ReactDOM.render(
<Tree
@ -24,16 +42,8 @@ ReactDOM.render(
defaultExpandAll
defaultSelectedKeys={['0-0-0']}
switcherIcon={<Icon type="down" />}
>
<TreeNode icon={<Icon type="smile-o" />} title="parent 1" key="0-0">
<TreeNode icon={<Icon type="meh-o" />} title="leaf" key="0-0-0" />
<TreeNode
icon={({ selected }) => <Icon type={selected ? 'frown' : 'frown-o'} />}
title="leaf"
key="0-0-1"
/>
</TreeNode>
</Tree>,
treeData={treeData}
/>,
mountNode,
);
```

View File

@ -13,35 +13,49 @@ title:
Built-in directory tree. `multiple` support `ctrl(Windows)` / `command(Mac)` selection.
```jsx
```tsx
import { Tree } from 'antd';
const { TreeNode, DirectoryTree } = Tree;
const { DirectoryTree } = Tree;
class Demo extends React.Component {
onSelect = (keys, event) => {
const treeData = [
{
title: 'parent 0',
key: '0-0',
children: [
{ title: 'leaf 0-0', key: '0-0-0', isLeaf: true },
{ title: 'leaf 0-1', key: '0-0-1', isLeaf: true },
],
},
{
title: 'parent 1',
key: '0-1',
children: [
{ title: 'leaf 1-0', key: '0-1-0', isLeaf: true },
{ title: 'leaf 1-1', key: '0-1-1', isLeaf: true },
],
},
];
const Demo: React.FC<{}> = () => {
const onSelect = (keys, event) => {
console.log('Trigger Select', keys, event);
};
onExpand = () => {
const onExpand = () => {
console.log('Trigger Expand');
};
render() {
return (
<DirectoryTree multiple defaultExpandAll onSelect={this.onSelect} onExpand={this.onExpand}>
<TreeNode title="parent 0" key="0-0">
<TreeNode title="leaf 0-0" key="0-0-0" isLeaf />
<TreeNode title="leaf 0-1" key="0-0-1" isLeaf />
</TreeNode>
<TreeNode title="parent 1" key="0-1">
<TreeNode title="leaf 1-0" key="0-1-0" isLeaf />
<TreeNode title="leaf 1-1" key="0-1-1" isLeaf />
</TreeNode>
</DirectoryTree>
);
}
}
return (
<DirectoryTree
multiple
defaultExpandAll
onSelect={onSelect}
onExpand={onExpand}
treeData={treeData}
/>
);
};
ReactDOM.render(<Demo />, mountNode);
```

View File

@ -16,8 +16,6 @@ Drag treeNode to insert after the other treeNode or insert into the other parent
```jsx
import { Tree } from 'antd';
const { TreeNode } = Tree;
const x = 3;
const y = 2;
const z = 1;
@ -123,17 +121,6 @@ class Demo extends React.Component {
};
render() {
const loop = data =>
data.map(item => {
if (item.children && item.children.length) {
return (
<TreeNode key={item.key} title={item.title}>
{loop(item.children)}
</TreeNode>
);
}
return <TreeNode key={item.key} title={item.title} />;
});
return (
<Tree
className="draggable-tree"
@ -142,9 +129,8 @@ class Demo extends React.Component {
blockNode
onDragEnter={this.onDragEnter}
onDrop={this.onDrop}
>
{loop(this.state.gData)}
</Tree>
treeData={this.state.gData}
/>
);
}
}

View File

@ -13,12 +13,41 @@ title:
To load data asynchronously when click to expand a treeNode.
```jsx
```tsx
import { Tree } from 'antd';
const { TreeNode } = Tree;
class Demo extends React.Component {
const initTreeDate = [
{ title: 'Expand to load', key: '0' },
{ title: 'Expand to load', key: '1' },
{ title: 'Tree Node', key: '2', isLeaf: true },
];
const Demo: React.FC<{}> = () => {
const [treeData, setTreeData] = React.useState(initTreeDate);
function onLoadData({ props: { data } }) {
return new Promise(resolve => {
if (data.children) {
resolve();
return;
}
setTimeout(() => {
data.children = [
{ title: 'Child Node', key: `${data.key}-0` },
{ title: 'Child Node', key: `${data.key}-1` },
];
setTreeData([...treeData]);
resolve();
}, 1000);
});
}
return <Tree loadData={onLoadData} treeData={treeData} />;
};
class Demo1 extends React.Component {
state = {
treeData: [
{ title: 'Expand to load', key: '0' },
@ -27,8 +56,10 @@ class Demo extends React.Component {
],
};
onLoadData = treeNode =>
new Promise(resolve => {
onLoadData = treeNode => {
const { treeData } = this.state;
return new Promise(resolve => {
const { props } = treeNode;
if (treeNode.props.children) {
resolve();
return;
@ -44,21 +75,10 @@ class Demo extends React.Component {
resolve();
}, 1000);
});
renderTreeNodes = data =>
data.map(item => {
if (item.children) {
return (
<TreeNode title={item.title} key={item.key} dataRef={item}>
{this.renderTreeNodes(item.children)}
</TreeNode>
);
}
return <TreeNode key={item.key} {...item} dataRef={item} />;
});
};
render() {
return <Tree loadData={this.onLoadData}>{this.renderTreeNodes(this.state.treeData)}</Tree>;
return <Tree loadData={this.onLoadData} treeData={this.state.treeData} />;
}
}

View File

@ -13,37 +13,44 @@ title:
Tree With Line
```jsx
```tsx
import { Tree } from 'antd';
const { TreeNode } = Tree;
const treeData = [
{
title: 'parent 1',
key: '0-0',
children: [
{
title: 'parent 1-0',
key: '0-0-0',
children: [
{ title: 'leaf', key: '0-0-0-0' },
{ title: 'leaf', key: '0-0-0-1' },
{ title: 'leaf', key: '0-0-0-2' },
],
},
{
title: 'parent 1-1',
key: '0-0-1',
children: [{ title: 'leaf', key: '0-0-1-0' }],
},
{
title: 'parent 1-2',
key: '0-0-2',
children: [{ title: 'leaf', key: '0-0-2-0' }, { title: 'leaf', key: '0-0-2-1' }],
},
],
},
];
class Demo extends React.Component {
onSelect = (selectedKeys, info) => {
const Demo: React.FC<{}> = () => {
const onSelect = (selectedKeys, info) => {
console.log('selected', selectedKeys, info);
};
render() {
return (
<Tree showLine defaultExpandedKeys={['0-0-0']} onSelect={this.onSelect}>
<TreeNode title="parent 1" key="0-0">
<TreeNode title="parent 1-0" key="0-0-0">
<TreeNode title="leaf" key="0-0-0-0" />
<TreeNode title="leaf" key="0-0-0-1" />
<TreeNode title="leaf" key="0-0-0-2" />
</TreeNode>
<TreeNode title="parent 1-1" key="0-0-1">
<TreeNode title="leaf" key="0-0-1-0" />
</TreeNode>
<TreeNode title="parent 1-2" key="0-0-2">
<TreeNode title="leaf" key="0-0-2-0" />
<TreeNode title="leaf" key="0-0-2-1" />
</TreeNode>
</TreeNode>
</Tree>
);
}
}
return <Tree showLine defaultExpandedKeys={['0-0-0']} onSelect={onSelect} treeData={treeData} />;
};
ReactDOM.render(<Demo />, mountNode);
```

View File

@ -16,7 +16,6 @@ Searchable Tree.
```jsx
import { Tree, Input } from 'antd';
const { TreeNode } = Tree;
const { Search } = Input;
const x = 3;
@ -124,13 +123,13 @@ class SearchTree extends React.Component {
<span>{item.title}</span>
);
if (item.children) {
return (
<TreeNode key={item.key} title={title}>
{loop(item.children)}
</TreeNode>
);
return { title, key: item.key, children: loop(item.children) };
}
return <TreeNode key={item.key} title={title} />;
return {
title,
key: item.key,
};
});
return (
<div>
@ -139,9 +138,8 @@ class SearchTree extends React.Component {
onExpand={this.onExpand}
expandedKeys={expandedKeys}
autoExpandParent={autoExpandParent}
>
{loop(gData)}
</Tree>
treeData={loop(gData)}
/>
</div>
);
}

View File

@ -4,90 +4,69 @@
.@{tree-prefix-cls} {
&.@{tree-prefix-cls}-directory {
position: relative;
// ================== TreeNode ==================
.@{tree-prefix-cls}-treenode {
position: relative;
// Stretch selector width
> li,
.@{tree-prefix-cls}-child-tree > li {
span {
&.@{tree-prefix-cls}-switcher {
position: relative;
z-index: 1;
// Hover color
&::before {
position: absolute;
top: 0;
right: 0;
bottom: 4px;
left: 0;
transition: background-color 0.3s;
content: '';
pointer-events: none;
}
&.@{tree-prefix-cls}-switcher-noop {
pointer-events: none;
}
}
&.@{tree-prefix-cls}-checkbox {
position: relative;
z-index: 1;
}
&.@{tree-prefix-cls}-node-content-wrapper {
border-radius: 0;
user-select: none;
&:hover {
background: transparent;
&::before {
background: @item-hover-bg;
}
}
&.@{tree-prefix-cls}-node-selected {
color: @tree-directory-selected-color;
background: transparent;
}
&::before {
position: absolute;
right: 0;
left: 0;
height: @tree-title-height;
transition: all 0.3s;
content: '';
}
> span {
position: relative;
z-index: 1;
}
&:hover {
&::before {
background: @item-hover-bg;
}
}
&.@{tree-prefix-cls}-treenode-selected {
> span {
&.@{tree-prefix-cls}-switcher {
color: @tree-directory-selected-color;
}
// Elements
> * {
z-index: 1;
}
&.@{tree-prefix-cls}-checkbox {
.@{tree-prefix-cls}-checkbox-inner {
border-color: @primary-color;
}
// >>> Switcher
.@{tree-prefix-cls}-switcher {
transition: color 0.3s;
}
&.@{tree-prefix-cls}-checkbox-checked {
&::after {
border-color: @checkbox-check-color;
}
// >>> Title
.@{tree-prefix-cls}-node-content-wrapper {
border-radius: 0;
user-select: none;
.@{tree-prefix-cls}-checkbox-inner {
background: @checkbox-check-color;
&:hover {
background: transparent;
}
&::after {
border-color: @primary-color;
}
}
}
}
&.@{tree-prefix-cls}-node-selected {
color: @tree-directory-selected-color;
background: transparent;
}
}
&.@{tree-prefix-cls}-node-content-wrapper {
&::before {
background: @tree-directory-selected-bg;
}
}
// ============= Selected =============
&-selected {
&:hover::before,
&::before {
background: @primary-color;
}
// >>> Switcher
.@{tree-prefix-cls}-switcher {
color: @tree-directory-selected-color;
}
// >>> Title
.@{tree-prefix-cls}-node-content-wrapper {
color: @tree-directory-selected-color;
background: transparent;
}
}
}

View File

@ -5,272 +5,195 @@
@import './directory';
@tree-prefix-cls: ~'@{ant-prefix}-tree';
@tree-showline-icon-color: @text-color-secondary;
@tree-node-padding: 4px;
@tree-node-prefix-cls: ~'@{tree-prefix-cls}-treenode';
@tree-motion: ~'@{ant-prefix}-motion-collapse';
.antCheckboxFn(@checkbox-prefix-cls: ~'@{ant-prefix}-tree-checkbox');
.@{tree-prefix-cls} {
/* see https://github.com/ant-design/ant-design/issues/16259 */
&-checkbox-checked::after {
position: absolute;
top: 16.67%;
left: 0;
width: 100%;
height: 66.67%;
}
.reset-component;
background: @component-background;
margin: 0;
padding: 0;
ol,
ul {
margin: 0;
padding: 0;
list-style: none;
// =================== Virtual List ===================
&-list-holder-inner {
align-items: flex-start;
}
li {
margin: 0;
padding: @tree-node-padding 0;
white-space: nowrap;
list-style: none;
outline: 0;
span[draggable],
span[draggable='true'] {
line-height: @tree-title-height - 4px;
border-top: 2px transparent solid;
border-bottom: 2px transparent solid;
user-select: none;
/* Required to make elements draggable in old WebKit */
-khtml-user-drag: element;
-webkit-user-drag: element;
}
&.drag-over {
> span[draggable] {
color: white;
background-color: @primary-color;
opacity: 0.8;
}
}
&.drag-over-gap-top {
> span[draggable] {
border-top-color: @primary-color;
}
}
&.drag-over-gap-bottom {
> span[draggable] {
border-bottom-color: @primary-color;
}
}
&.filter-node {
> span {
color: @highlight-color !important;
font-weight: 500 !important;
}
}
&.@{tree-prefix-cls}-block-node {
.@{tree-prefix-cls}-list-holder-inner {
align-items: stretch;
// When node is loading
&.@{tree-prefix-cls}-treenode-loading {
span {
&.@{tree-prefix-cls}-switcher {
&.@{tree-prefix-cls}-switcher_open,
&.@{tree-prefix-cls}-switcher_close {
.@{tree-prefix-cls}-switcher-loading-icon {
position: absolute;
left: 0;
display: inline-block;
width: 24px;
height: @tree-title-height;
color: @primary-color;
font-size: 14px;
transform: none;
svg {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
}
}
// >>> Title
.@{tree-prefix-cls}-node-content-wrapper {
flex: auto;
}
}
}
:root &::after {
opacity: 0;
}
}
// ===================== TreeNode =====================
.@{tree-node-prefix-cls} {
display: flex;
align-items: flex-start;
padding: 0 0 4px 0;
outline: none;
// Disabled
&-disabled {
// >>> Title
.@{tree-prefix-cls}-node-content-wrapper {
color: @disabled-color;
cursor: not-allowed;
&:hover {
background: transparent;
}
}
}
}
ul {
margin: 0;
padding: 0 0 0 @tree-child-padding;
}
.@{tree-prefix-cls}-node-content-wrapper {
// >>> Indent
&-indent {
align-self: stretch;
user-select: none;
&-unit {
display: inline-block;
height: @tree-title-height;
margin: 0;
padding: 0 5px;
color: @text-color;
line-height: @tree-title-height;
text-decoration: none;
vertical-align: top;
border-radius: @border-radius-sm;
cursor: pointer;
transition: all 0.3s;
&:hover {
background-color: @item-hover-bg;
}
&.@{tree-prefix-cls}-node-selected {
background-color: @primary-2;
width: @tree-title-height;
}
}
// >>> Switcher
& &-switcher {
.antTreeSwitcherIcon();
width: @tree-title-height;
height: @tree-title-height;
margin: 0;
line-height: @tree-title-height;
text-align: center;
cursor: pointer;
&-noop {
cursor: default;
}
&_close {
.@{tree-prefix-cls}-switcher-icon {
svg {
transform: rotate(-90deg);
}
}
}
span {
&.@{tree-prefix-cls}-checkbox {
top: initial;
height: @tree-title-height;
margin: 0 4px 0 2px;
padding: ((@tree-title-height - 16px) / 2) 0;
}
&.@{tree-prefix-cls}-switcher,
&.@{tree-prefix-cls}-iconEle {
display: inline-block;
width: 24px;
height: @tree-title-height;
margin: 0;
line-height: @tree-title-height;
text-align: center;
vertical-align: top;
border: 0 none;
outline: none;
cursor: pointer;
}
}
&.@{tree-prefix-cls}-switcher {
// >>> Checkbox
& &-checkbox {
top: initial;
margin: ((@tree-title-height - @checkbox-size) / 2) 8px 0 0;
}
// >>> Title
& &-node-content-wrapper {
min-height: @tree-title-height;
margin: 0;
padding: 0 4px;
color: inherit;
line-height: @tree-title-height;
background: transparent;
border-radius: @border-radius-sm;
cursor: pointer;
transition: all 0.3s;
&:hover {
background-color: @item-hover-bg;
}
&.@{tree-prefix-cls}-node-selected {
background-color: @primary-2;
}
// Icon
.@{tree-prefix-cls}-iconEle {
display: inline-block;
width: @tree-title-height;
height: @tree-title-height;
line-height: @tree-title-height;
text-align: center;
vertical-align: top;
}
}
// ===================== Loading ======================
.@{tree-node-prefix-cls}-loading {
// Icon
.@{tree-prefix-cls}-iconEle {
display: none;
}
}
// ==================== Draggable =====================
&-node-content-wrapper[draggable='true'] {
line-height: @tree-title-height - 4px;
border-top: 2px transparent solid;
border-bottom: 2px transparent solid;
user-select: none;
}
.@{tree-node-prefix-cls}.drag-over {
> [draggable] {
color: white;
background-color: @primary-color;
opacity: 0.8;
}
}
.@{tree-node-prefix-cls}.drag-over-gap-top {
> [draggable] {
border-top-color: @primary-color;
}
}
.@{tree-node-prefix-cls}.drag-over-gap-bottom {
> [draggable] {
border-bottom-color: @primary-color;
}
}
// ==================== Show Line =====================
&-show-line {
// ================ Indent lines ================
.@{tree-prefix-cls}-indent {
&-unit {
position: relative;
height: 100%;
&.@{tree-prefix-cls}-switcher-noop {
cursor: default;
&::before {
position: absolute;
top: calc(100% - 4px);
right: -@tree-title-height / 2;
bottom: -@tree-title-height - 4px;
border-right: 1px solid @border-color-base;
content: '';
}
&.@{tree-prefix-cls}-switcher_open {
.antTreeSwitcherIcon();
}
&.@{tree-prefix-cls}-switcher_close {
.antTreeSwitcherIcon();
.@{tree-prefix-cls}-switcher-icon {
svg {
transform: rotate(-90deg);
}
&-end {
&::before {
display: none;
}
}
}
}
&:last-child > span {
&.@{tree-prefix-cls}-switcher,
&.@{tree-prefix-cls}-iconEle {
/* Motion should hide line of measure */
.@{tree-node-prefix-cls}-motion:not(.@{tree-motion}-leave):not(.@{tree-motion}-appear-active) {
.@{tree-prefix-cls}-indent-unit {
&::before {
display: none;
}
}
}
}
> li {
&:first-child {
padding-top: 7px;
}
&:last-child {
padding-bottom: 7px;
}
}
&-child-tree {
// https://github.com/ant-design/ant-design/issues/14958
> li {
// Provide additional padding between top child node and parent node
&:first-child {
padding-top: 2 * @tree-node-padding;
}
// Hide additional padding between last child node and next parent node
&:last-child {
padding-bottom: 0;
}
}
}
li&-treenode-disabled {
> span:not(.@{tree-prefix-cls}-switcher),
> .@{tree-prefix-cls}-node-content-wrapper,
> .@{tree-prefix-cls}-node-content-wrapper span {
color: @disabled-color;
cursor: not-allowed;
}
> .@{tree-prefix-cls}-node-content-wrapper:hover {
background: transparent;
}
}
&-icon__open {
margin-right: 2px;
vertical-align: top;
}
&-icon__close {
margin-right: 2px;
vertical-align: top;
}
// Tree with line
&&-show-line {
li {
position: relative;
span {
&.@{tree-prefix-cls}-switcher {
color: @tree-showline-icon-color;
background: @component-background;
&.@{tree-prefix-cls}-switcher-noop {
.antTreeShowLineIcon('tree-doc-icon');
}
&.@{tree-prefix-cls}-switcher_open {
.antTreeShowLineIcon('tree-showline-open-icon');
}
&.@{tree-prefix-cls}-switcher_close {
.antTreeShowLineIcon('tree-showline-close-icon');
}
}
}
}
li:not(:last-child)::before {
position: absolute;
left: 12px;
width: 1px;
height: 100%;
height: calc(100% - 22px); // Remove additional height if support
margin: 22px 0 0;
border-left: 1px solid @border-color-base;
content: ' ';
}
}
&.@{tree-prefix-cls}-icon-hide {
.@{tree-prefix-cls}-treenode-loading {
.@{tree-prefix-cls}-iconEle {
display: none;
}
}
}
&.@{tree-prefix-cls}-block-node {
li {
.@{tree-prefix-cls}-node-content-wrapper {
width: ~'calc(100% - 24px)';
}
span {
&.@{tree-prefix-cls}-checkbox {
+ .@{tree-prefix-cls}-node-content-wrapper {
width: ~'calc(100% - 46px)';
}
}
}
// ============== Cover Background ==============
.@{tree-prefix-cls}-switcher {
z-index: 1;
background: @component-background;
}
}
}

View File

@ -1,6 +1,4 @@
import * as React from 'react';
import { getNodeChildren, convertTreeToEntities } from 'rc-tree/lib/util';
import { AntTreeNodeProps, AntTreeNode } from './Tree';
import { DataNode } from 'rc-tree/lib/interface';
enum Record {
None,
@ -8,34 +6,23 @@ enum Record {
End,
}
// TODO: Move this logic into `rc-tree`
function traverseNodesKey(
rootChildren: React.ReactNode | React.ReactNode[],
callback: (key: string | number | null, node: AntTreeNode) => boolean,
treeData: DataNode[],
callback: (key: string | number | null, node: DataNode) => boolean,
) {
const nodeList: React.ReactNode[] = getNodeChildren(rootChildren) || [];
function processNode(node: React.ReactElement<AntTreeNodeProps>) {
const {
key,
props: { children },
} = node;
if (callback(key, node as any) !== false) {
traverseNodesKey(children, callback);
function processNode(dataNode: DataNode) {
const { key, children } = dataNode;
if (callback(key, dataNode) !== false) {
traverseNodesKey(children || [], callback);
}
}
nodeList.forEach(processNode);
}
export function getFullKeyList(children: React.ReactNode | React.ReactNode[]) {
const { keyEntities } = convertTreeToEntities(children);
return Object.keys(keyEntities);
treeData.forEach(processNode);
}
/** 计算选中范围只考虑expanded情况以优化性能 */
export function calcRangeKeys(
rootChildren: React.ReactNode | React.ReactNode[],
treeData: DataNode[],
expandedKeys: string[],
startKey?: string,
endKey?: string,
@ -54,7 +41,7 @@ export function calcRangeKeys(
return key === startKey || key === endKey;
}
traverseNodesKey(rootChildren, (key: string) => {
traverseNodesKey(treeData, (key: string) => {
if (record === Record.End) {
return false;
}
@ -84,13 +71,10 @@ export function calcRangeKeys(
return keys;
}
export function convertDirectoryKeysToNodes(
rootChildren: React.ReactNode | React.ReactNode[],
keys: string[],
) {
export function convertDirectoryKeysToNodes(treeData: DataNode[], keys: string[]) {
const restKeys: string[] = [...keys];
const nodes: AntTreeNode[] = [];
traverseNodesKey(rootChildren, (key: string, node: AntTreeNode) => {
const nodes: DataNode[] = [];
traverseNodesKey(treeData, (key: string, node: DataNode) => {
const index = restKeys.indexOf(key);
if (index !== -1) {
nodes.push(node);

View File

@ -85,7 +85,7 @@
"rc-tabs": "~9.6.4",
"rc-time-picker": "~3.7.1",
"rc-tooltip": "~3.7.3",
"rc-tree": "~2.1.0",
"rc-tree": "~3.0.0-alpha.9",
"rc-tree-select": "~2.9.1",
"rc-trigger": "^2.6.2",
"rc-upload": "~2.7.0",
@ -104,7 +104,7 @@
"@sentry/browser": "^5.4.0",
"@types/classnames": "^2.2.8",
"@types/prop-types": "^15.7.1",
"@types/react": "~16.8.19",
"@types/react": "~16.9.1",
"@types/react-dom": "^16.8.4",
"@types/react-intl": "^2.3.17",
"@types/warning": "^3.0.0",
@ -160,13 +160,13 @@
"rc-queue-anim": "^1.6.12",
"rc-scroll-anim": "^2.5.8",
"rc-tween-one": "^2.4.1",
"react": "^16.5.2",
"react": "^16.9.0",
"react-color": "^2.17.3",
"react-copy-to-clipboard": "^5.0.1",
"react-dnd": "^9.0.0",
"react-dnd-html5-backend": "^9.0.0",
"react-document-title": "^2.0.3",
"react-dom": "^16.5.2",
"react-dom": "^16.9.0",
"react-github-button": "^0.1.11",
"react-highlight-words": "^0.16.0",
"react-infinite-scroller": "^1.2.4",