mirror of
https://gitee.com/ant-design/ant-design.git
synced 2024-11-30 02:59:04 +08:00
parent
ff24f1a978
commit
a825ed4e66
@ -48,7 +48,7 @@
|
||||
height: @checkbox-size;
|
||||
border: @border-width-base @border-style-base @border-color-base;
|
||||
border-radius: @border-radius-sm;
|
||||
background-color: #fff;
|
||||
background-color: @checkbox-check-color;
|
||||
transition: all .3s;
|
||||
|
||||
&:after {
|
||||
@ -61,7 +61,7 @@
|
||||
display: table;
|
||||
width: @check-width;
|
||||
height: @check-height;
|
||||
border: 2px solid #fff;
|
||||
border: 2px solid @checkbox-check-color;
|
||||
border-top: 0;
|
||||
border-left: 0;
|
||||
content: ' ';
|
||||
@ -105,7 +105,7 @@
|
||||
transform: rotate(45deg) scale(1);
|
||||
position: absolute;
|
||||
display: table;
|
||||
border: 2px solid #fff;
|
||||
border: 2px solid @checkbox-check-color;
|
||||
border-top: 0;
|
||||
border-left: 0;
|
||||
content: ' ';
|
||||
|
@ -157,6 +157,7 @@
|
||||
// Checkbox
|
||||
@checkbox-size : 16px;
|
||||
@checkbox-color : @primary-color;
|
||||
@checkbox-check-color : #fff;
|
||||
|
||||
// Radio
|
||||
@radio-size : 16px;
|
||||
@ -466,6 +467,13 @@
|
||||
@slider-disabled-color: @disabled-color;
|
||||
@slider-disabled-background-color: @component-background;
|
||||
|
||||
// Tree
|
||||
// ---
|
||||
@tree-title-height: 24px;
|
||||
@tree-child-padding: 18px;
|
||||
@tree-directory-selected-color: #fff;
|
||||
@tree-directory-selected-bg: @primary-color;
|
||||
|
||||
// Collapse
|
||||
// ---
|
||||
@collapse-header-padding: 12px 0 12px 40px;
|
||||
|
210
components/tree/DirectoryTree.tsx
Normal file
210
components/tree/DirectoryTree.tsx
Normal file
@ -0,0 +1,210 @@
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import omit from 'omit.js';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { getFullKeyList, calcExpandedKeys } from 'rc-tree/lib/util';
|
||||
|
||||
import Tree, {
|
||||
TreeProps, AntdTreeNodeAttribute,
|
||||
AntTreeNodeExpandedEvent, AntTreeNodeSelectedEvent, AntTreeNode,
|
||||
} from './Tree';
|
||||
import { calcRangeKeys } from './util';
|
||||
import Icon from '../icon';
|
||||
|
||||
export type ExpandAction = false | 'click' | 'doubleClick';
|
||||
|
||||
export interface DirectoryTreeProps extends TreeProps {
|
||||
expandAction?: ExpandAction;
|
||||
}
|
||||
|
||||
export interface DirectoryTreeState {
|
||||
expandedKeys?: string[];
|
||||
selectedKeys?: string[];
|
||||
}
|
||||
|
||||
function getIcon(props: AntdTreeNodeAttribute): React.ReactNode {
|
||||
const { isLeaf, expanded } = props;
|
||||
if (isLeaf) {
|
||||
return <Icon type="file" />;
|
||||
}
|
||||
return <Icon type={expanded ? 'folder-open' : 'folder'} />;
|
||||
}
|
||||
|
||||
export default class DirectoryTree extends React.Component<DirectoryTreeProps, DirectoryTreeState> {
|
||||
static defaultProps = {
|
||||
prefixCls: 'ant-tree',
|
||||
showIcon: true,
|
||||
expandAction: 'click',
|
||||
};
|
||||
|
||||
state: DirectoryTreeState;
|
||||
onDebounceExpand: (event: React.MouseEvent<HTMLElement>, node: AntTreeNode) => void;
|
||||
|
||||
// Shift click usage
|
||||
lastSelectedKey?: string;
|
||||
cachedSelectedKeys?: string[];
|
||||
|
||||
constructor(props: DirectoryTreeProps) {
|
||||
super(props);
|
||||
|
||||
const { defaultExpandAll, defaultExpandParent, expandedKeys, defaultExpandedKeys } = props;
|
||||
|
||||
// Selected keys
|
||||
this.state = {
|
||||
selectedKeys: props.selectedKeys || props.defaultSelectedKeys || [],
|
||||
};
|
||||
|
||||
// Expanded keys
|
||||
if (defaultExpandAll) {
|
||||
this.state.expandedKeys = getFullKeyList(props.children);
|
||||
} else if (defaultExpandParent) {
|
||||
this.state.expandedKeys = calcExpandedKeys(expandedKeys || defaultExpandedKeys, props);
|
||||
} else {
|
||||
this.state.expandedKeys = defaultExpandedKeys;
|
||||
}
|
||||
|
||||
this.onDebounceExpand = debounce(this.expandFolderNode, 200, {
|
||||
leading: true,
|
||||
});
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps: DirectoryTreeProps) {
|
||||
if ('expandedKeys' in nextProps) {
|
||||
this.setState({ expandedKeys: nextProps.expandedKeys });
|
||||
}
|
||||
if ('selectedKeys' in nextProps) {
|
||||
this.setState({ selectedKeys: nextProps.selectedKeys });
|
||||
}
|
||||
}
|
||||
|
||||
onExpand = (expandedKeys: string[], info: AntTreeNodeExpandedEvent) => {
|
||||
const { onExpand } = this.props;
|
||||
|
||||
this.setUncontrolledState({ expandedKeys });
|
||||
|
||||
// Call origin function
|
||||
if (onExpand) {
|
||||
return onExpand(expandedKeys, info);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
onClick = (event: React.MouseEvent<HTMLElement>, node: AntTreeNode) => {
|
||||
const { onClick, expandAction } = this.props;
|
||||
|
||||
// Expand the tree
|
||||
if (expandAction === 'click') {
|
||||
this.onDebounceExpand(event, node);
|
||||
}
|
||||
|
||||
if (onClick) {
|
||||
onClick(event, node);
|
||||
}
|
||||
}
|
||||
|
||||
onDoubleClick = (event: React.MouseEvent<HTMLElement>, node: AntTreeNode) => {
|
||||
const { onDoubleClick, expandAction } = this.props;
|
||||
|
||||
// Expand the tree
|
||||
if (expandAction === 'doubleClick') {
|
||||
this.onDebounceExpand(event, node);
|
||||
}
|
||||
|
||||
if (onDoubleClick) {
|
||||
onDoubleClick(event, node);
|
||||
}
|
||||
}
|
||||
|
||||
onSelect = (keys: string[], event: AntTreeNodeSelectedEvent) => {
|
||||
const { onSelect, multiple, children } = this.props;
|
||||
const { expandedKeys = [], selectedKeys = [] } = this.state;
|
||||
const { node, nativeEvent } = event;
|
||||
const { eventKey = '' } = node.props;
|
||||
|
||||
const newState: DirectoryTreeState = {};
|
||||
|
||||
// Windows / Mac single pick
|
||||
const ctrlPick: boolean = nativeEvent.ctrlKey || nativeEvent.metaKey;
|
||||
const shiftPick: boolean = nativeEvent.shiftKey;
|
||||
|
||||
// Generate new selected keys
|
||||
let newSelectedKeys = selectedKeys.slice();
|
||||
if (multiple && ctrlPick) {
|
||||
// Control click
|
||||
newSelectedKeys = keys;
|
||||
this.lastSelectedKey = eventKey;
|
||||
this.cachedSelectedKeys = newSelectedKeys;
|
||||
} else if (multiple && shiftPick) {
|
||||
// Shift click
|
||||
newSelectedKeys = Array.from(new Set([
|
||||
...this.cachedSelectedKeys || [],
|
||||
...calcRangeKeys(children, expandedKeys, eventKey, this.lastSelectedKey),
|
||||
]));
|
||||
} else {
|
||||
// Single click
|
||||
newSelectedKeys = [eventKey];
|
||||
this.lastSelectedKey = eventKey;
|
||||
this.cachedSelectedKeys = newSelectedKeys;
|
||||
}
|
||||
newState.selectedKeys = newSelectedKeys;
|
||||
|
||||
if (onSelect) {
|
||||
onSelect(newSelectedKeys, event);
|
||||
}
|
||||
|
||||
this.setUncontrolledState(newState);
|
||||
}
|
||||
|
||||
expandFolderNode = (event: React.MouseEvent<HTMLElement>, node: AntTreeNode) => {
|
||||
const { expandedKeys = [] } = this.state;
|
||||
const { eventKey = '', expanded, isLeaf } = node.props;
|
||||
|
||||
if (isLeaf || event.shiftKey || event.metaKey || event.ctrlKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
let newExpandedKeys: string[] = expandedKeys.slice();
|
||||
const index = newExpandedKeys.indexOf(eventKey);
|
||||
|
||||
if (expanded && index >= 0) {
|
||||
newExpandedKeys.splice(index, 1);
|
||||
} else if (!expanded && index === -1) {
|
||||
newExpandedKeys.push(eventKey);
|
||||
}
|
||||
|
||||
this.setUncontrolledState({
|
||||
expandedKeys: newExpandedKeys,
|
||||
});
|
||||
}
|
||||
|
||||
setUncontrolledState = (state: DirectoryTreeState) => {
|
||||
const newState = omit(state, Object.keys(this.props));
|
||||
if (Object.keys(newState).length) {
|
||||
this.setState(newState);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { prefixCls, className, ...props } = this.props;
|
||||
const { expandedKeys, selectedKeys } = this.state;
|
||||
|
||||
const connectClassName = classNames(`${prefixCls}-directory`, className);
|
||||
|
||||
return (
|
||||
<Tree
|
||||
icon={getIcon}
|
||||
{...props}
|
||||
|
||||
prefixCls={prefixCls}
|
||||
className={connectClassName}
|
||||
expandedKeys={expandedKeys}
|
||||
selectedKeys={selectedKeys}
|
||||
onSelect={this.onSelect}
|
||||
onClick={this.onClick}
|
||||
onDoubleClick={this.onDoubleClick}
|
||||
onExpand={this.onExpand}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
156
components/tree/Tree.tsx
Normal file
156
components/tree/Tree.tsx
Normal file
@ -0,0 +1,156 @@
|
||||
import * as React from 'react';
|
||||
import RcTree, { TreeNode } from 'rc-tree';
|
||||
import DirectoryTree from './DirectoryTree';
|
||||
import classNames from 'classnames';
|
||||
import animation from '../_util/openAnimation';
|
||||
|
||||
export interface AntdTreeNodeAttribute {
|
||||
eventKey: string;
|
||||
prefixCls: string;
|
||||
className: string;
|
||||
expanded: boolean;
|
||||
selected: boolean;
|
||||
checked: boolean;
|
||||
halfChecked: boolean;
|
||||
children: React.ReactNode;
|
||||
title: React.ReactNode;
|
||||
pos: string;
|
||||
dragOver: boolean;
|
||||
dragOverGapTop: boolean;
|
||||
dragOverGapBottom: boolean;
|
||||
isLeaf: boolean;
|
||||
selectable: boolean;
|
||||
disabled: boolean;
|
||||
disableCheckbox: boolean;
|
||||
}
|
||||
export interface AntTreeNodeProps {
|
||||
disabled?: boolean;
|
||||
disableCheckbox?: boolean;
|
||||
title?: string | React.ReactNode;
|
||||
key?: string;
|
||||
eventKey?: string;
|
||||
isLeaf?: boolean;
|
||||
checked?: boolean;
|
||||
expanded?: boolean;
|
||||
selected?: boolean;
|
||||
icon?: (nodeProps: AntdTreeNodeAttribute) => React.ReactNode | React.ReactNode;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
export interface AntTreeNode extends React.Component<AntTreeNodeProps, {}> {}
|
||||
|
||||
export interface AntTreeNodeBaseEvent {
|
||||
node: AntTreeNode;
|
||||
nativeEvent: MouseEvent;
|
||||
}
|
||||
|
||||
export interface AntTreeNodeCheckedEvent extends AntTreeNodeBaseEvent {
|
||||
event: 'check';
|
||||
checked?: boolean;
|
||||
checkedNodes?: AntTreeNode[];
|
||||
}
|
||||
|
||||
export interface AntTreeNodeSelectedEvent extends AntTreeNodeBaseEvent {
|
||||
event: 'select';
|
||||
selected?: boolean;
|
||||
selectedNodes?: AntTreeNode[];
|
||||
}
|
||||
|
||||
export interface AntTreeNodeExpandedEvent extends AntTreeNodeBaseEvent {
|
||||
expanded?: boolean;
|
||||
}
|
||||
|
||||
export interface AntTreeNodeMouseEvent {
|
||||
node: AntTreeNode;
|
||||
event: React.MouseEventHandler<any>;
|
||||
}
|
||||
|
||||
export interface TreeProps {
|
||||
showLine?: boolean;
|
||||
className?: string;
|
||||
/** 是否支持多选 */
|
||||
multiple?: boolean;
|
||||
/** 是否自动展开父节点 */
|
||||
autoExpandParent?: boolean;
|
||||
/** checkable状态下节点选择完全受控(父子节点选中状态不再关联)*/
|
||||
checkStrictly?: boolean;
|
||||
/** 是否支持选中 */
|
||||
checkable?: boolean;
|
||||
/** 默认展开所有树节点 */
|
||||
defaultExpandAll?: boolean;
|
||||
/** 默认展开对应树节点 */
|
||||
defaultExpandParent?: boolean;
|
||||
/** 默认展开指定的树节点 */
|
||||
defaultExpandedKeys?: string[];
|
||||
/** (受控)展开指定的树节点 */
|
||||
expandedKeys?: string[];
|
||||
/** (受控)选中复选框的树节点 */
|
||||
checkedKeys?: string[] | { checked: string[]; halfChecked: string[] };
|
||||
/** 默认选中复选框的树节点 */
|
||||
defaultCheckedKeys?: string[];
|
||||
/** (受控)设置选中的树节点 */
|
||||
selectedKeys?: string[];
|
||||
/** 默认选中的树节点 */
|
||||
defaultSelectedKeys?: string[];
|
||||
/** 展开/收起节点时触发 */
|
||||
onExpand?: (expandedKeys: string[], info: AntTreeNodeExpandedEvent) => void | PromiseLike<any>;
|
||||
/** 点击复选框触发 */
|
||||
onCheck?: (checkedKeys: string[], e: AntTreeNodeCheckedEvent) => void;
|
||||
/** 点击树节点触发 */
|
||||
onSelect?: (selectedKeys: string[], e: AntTreeNodeSelectedEvent) => void;
|
||||
/** 单击树节点触发 */
|
||||
onClick?: (e: React.MouseEvent<HTMLElement>, node: AntTreeNode) => void;
|
||||
/** 双击树节点触发 */
|
||||
onDoubleClick?: (e: React.MouseEvent<HTMLElement>, node: AntTreeNode) => void;
|
||||
/** filter some AntTreeNodes as you need. it should return true */
|
||||
filterAntTreeNode?: (node: AntTreeNode) => boolean;
|
||||
/** 异步加载数据 */
|
||||
loadData?: (node: AntTreeNode) => PromiseLike<any>;
|
||||
/** 响应右键点击 */
|
||||
onRightClick?: (options: AntTreeNodeMouseEvent) => void;
|
||||
/** 设置节点可拖拽(IE>8)*/
|
||||
draggable?: boolean;
|
||||
/** 开始拖拽时调用 */
|
||||
onDragStart?: (options: AntTreeNodeMouseEvent) => void;
|
||||
/** dragenter 触发时调用 */
|
||||
onDragEnter?: (options: AntTreeNodeMouseEvent) => void;
|
||||
/** dragover 触发时调用 */
|
||||
onDragOver?: (options: AntTreeNodeMouseEvent) => void;
|
||||
/** dragleave 触发时调用 */
|
||||
onDragLeave?: (options: AntTreeNodeMouseEvent) => void;
|
||||
/** drop 触发时调用 */
|
||||
onDrop?: (options: AntTreeNodeMouseEvent) => void;
|
||||
style?: React.CSSProperties;
|
||||
showIcon?: boolean;
|
||||
icon?: (nodeProps: AntdTreeNodeAttribute) => React.ReactNode | React.ReactNode;
|
||||
prefixCls?: string;
|
||||
filterTreeNode?: (node: AntTreeNode) => boolean;
|
||||
children?: React.ReactNode | React.ReactNode[];
|
||||
}
|
||||
|
||||
export default class Tree extends React.Component<TreeProps, any> {
|
||||
static TreeNode = TreeNode;
|
||||
static DirectoryTree = DirectoryTree;
|
||||
|
||||
static defaultProps = {
|
||||
prefixCls: 'ant-tree',
|
||||
checkable: false,
|
||||
showIcon: false,
|
||||
openAnimation: animation,
|
||||
};
|
||||
|
||||
render() {
|
||||
const props = this.props;
|
||||
const { prefixCls, className, showIcon } = props;
|
||||
let checkable = props.checkable;
|
||||
return (
|
||||
<RcTree
|
||||
{...props}
|
||||
className={classNames(!showIcon && `${prefixCls}-icon-hide`, className)}
|
||||
checkable={checkable ? <span className={`${prefixCls}-checkbox-inner`} /> : checkable}
|
||||
>
|
||||
{this.props.children}
|
||||
</RcTree>
|
||||
);
|
||||
}
|
||||
}
|
@ -2,12 +2,12 @@
|
||||
|
||||
exports[`renders ./components/tree/demo/basic.md correctly 1`] = `
|
||||
<ul
|
||||
class="ant-tree"
|
||||
class="ant-tree ant-tree-icon-hide"
|
||||
role="tree-node"
|
||||
unselectable="on"
|
||||
>
|
||||
<li
|
||||
class=""
|
||||
class="ant-tree-treenode-switcher-open ant-tree-treenode-checkbox-checked"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher_open"
|
||||
@ -34,7 +34,7 @@ exports[`renders ./components/tree/demo/basic.md correctly 1`] = `
|
||||
data-expanded="true"
|
||||
>
|
||||
<li
|
||||
class="ant-tree-treenode-disabled"
|
||||
class="ant-tree-treenode-disabled ant-tree-treenode-switcher-open ant-tree-treenode-checkbox-checked ant-tree-treenode-selected"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher_open"
|
||||
@ -61,7 +61,7 @@ exports[`renders ./components/tree/demo/basic.md correctly 1`] = `
|
||||
data-expanded="true"
|
||||
>
|
||||
<li
|
||||
class=""
|
||||
class="ant-tree-treenode-switcher-close"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher-noop"
|
||||
@ -85,7 +85,7 @@ exports[`renders ./components/tree/demo/basic.md correctly 1`] = `
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
class=""
|
||||
class="ant-tree-treenode-switcher-close"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher-noop"
|
||||
@ -111,7 +111,7 @@ exports[`renders ./components/tree/demo/basic.md correctly 1`] = `
|
||||
</ul>
|
||||
</li>
|
||||
<li
|
||||
class=""
|
||||
class="ant-tree-treenode-switcher-open ant-tree-treenode-checkbox-checked"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher_open"
|
||||
@ -138,7 +138,7 @@ exports[`renders ./components/tree/demo/basic.md correctly 1`] = `
|
||||
data-expanded="true"
|
||||
>
|
||||
<li
|
||||
class=""
|
||||
class="ant-tree-treenode-switcher-close ant-tree-treenode-checkbox-checked"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher-noop"
|
||||
@ -174,12 +174,12 @@ exports[`renders ./components/tree/demo/basic.md correctly 1`] = `
|
||||
|
||||
exports[`renders ./components/tree/demo/basic-controlled.md correctly 1`] = `
|
||||
<ul
|
||||
class="ant-tree"
|
||||
class="ant-tree ant-tree-icon-hide"
|
||||
role="tree-node"
|
||||
unselectable="on"
|
||||
>
|
||||
<li
|
||||
class=""
|
||||
class="ant-tree-treenode-switcher-open ant-tree-treenode-checkbox-indeterminate"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher_open"
|
||||
@ -206,7 +206,7 @@ exports[`renders ./components/tree/demo/basic-controlled.md correctly 1`] = `
|
||||
data-expanded="true"
|
||||
>
|
||||
<li
|
||||
class=""
|
||||
class="ant-tree-treenode-switcher-open ant-tree-treenode-checkbox-checked"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher_open"
|
||||
@ -233,7 +233,7 @@ exports[`renders ./components/tree/demo/basic-controlled.md correctly 1`] = `
|
||||
data-expanded="true"
|
||||
>
|
||||
<li
|
||||
class=""
|
||||
class="ant-tree-treenode-switcher-close ant-tree-treenode-checkbox-checked"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher-noop"
|
||||
@ -257,7 +257,7 @@ exports[`renders ./components/tree/demo/basic-controlled.md correctly 1`] = `
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
class=""
|
||||
class="ant-tree-treenode-switcher-close ant-tree-treenode-checkbox-checked"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher-noop"
|
||||
@ -281,7 +281,7 @@ exports[`renders ./components/tree/demo/basic-controlled.md correctly 1`] = `
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
class=""
|
||||
class="ant-tree-treenode-switcher-close ant-tree-treenode-checkbox-checked"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher-noop"
|
||||
@ -307,7 +307,7 @@ exports[`renders ./components/tree/demo/basic-controlled.md correctly 1`] = `
|
||||
</ul>
|
||||
</li>
|
||||
<li
|
||||
class=""
|
||||
class="ant-tree-treenode-switcher-open"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher_open"
|
||||
@ -334,7 +334,7 @@ exports[`renders ./components/tree/demo/basic-controlled.md correctly 1`] = `
|
||||
data-expanded="true"
|
||||
>
|
||||
<li
|
||||
class=""
|
||||
class="ant-tree-treenode-switcher-close"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher-noop"
|
||||
@ -358,7 +358,7 @@ exports[`renders ./components/tree/demo/basic-controlled.md correctly 1`] = `
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
class=""
|
||||
class="ant-tree-treenode-switcher-close"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher-noop"
|
||||
@ -382,7 +382,7 @@ exports[`renders ./components/tree/demo/basic-controlled.md correctly 1`] = `
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
class=""
|
||||
class="ant-tree-treenode-switcher-close"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher-noop"
|
||||
@ -408,7 +408,7 @@ exports[`renders ./components/tree/demo/basic-controlled.md correctly 1`] = `
|
||||
</ul>
|
||||
</li>
|
||||
<li
|
||||
class=""
|
||||
class="ant-tree-treenode-switcher-close"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher-noop"
|
||||
@ -434,7 +434,7 @@ exports[`renders ./components/tree/demo/basic-controlled.md correctly 1`] = `
|
||||
</ul>
|
||||
</li>
|
||||
<li
|
||||
class=""
|
||||
class="ant-tree-treenode-switcher-close"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher_close"
|
||||
@ -458,7 +458,7 @@ exports[`renders ./components/tree/demo/basic-controlled.md correctly 1`] = `
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
class=""
|
||||
class="ant-tree-treenode-switcher-close"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher-noop"
|
||||
@ -491,7 +491,7 @@ exports[`renders ./components/tree/demo/customized-icon.md correctly 1`] = `
|
||||
unselectable="on"
|
||||
>
|
||||
<li
|
||||
class=""
|
||||
class="ant-tree-treenode-switcher-open"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher_open"
|
||||
@ -518,7 +518,7 @@ exports[`renders ./components/tree/demo/customized-icon.md correctly 1`] = `
|
||||
data-expanded="true"
|
||||
>
|
||||
<li
|
||||
class=""
|
||||
class="ant-tree-treenode-switcher-open ant-tree-treenode-selected"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher-noop"
|
||||
@ -542,7 +542,7 @@ exports[`renders ./components/tree/demo/customized-icon.md correctly 1`] = `
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
class=""
|
||||
class="ant-tree-treenode-switcher-open"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher-noop"
|
||||
@ -570,14 +570,177 @@ exports[`renders ./components/tree/demo/customized-icon.md correctly 1`] = `
|
||||
</ul>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/tree/demo/draggable.md correctly 1`] = `
|
||||
exports[`renders ./components/tree/demo/directory.md correctly 1`] = `
|
||||
<ul
|
||||
class="ant-tree draggable-tree"
|
||||
class="ant-tree ant-tree-directory"
|
||||
role="tree-node"
|
||||
unselectable="on"
|
||||
>
|
||||
<li
|
||||
class=""
|
||||
class="ant-tree-treenode-switcher-open"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher_open"
|
||||
/>
|
||||
<span
|
||||
class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-open"
|
||||
title="parent 0"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-iconEle ant-tree-icon__customize"
|
||||
>
|
||||
<i
|
||||
class="anticon anticon-folder-open"
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="ant-tree-title"
|
||||
>
|
||||
parent 0
|
||||
</span>
|
||||
</span>
|
||||
<ul
|
||||
class="ant-tree-child-tree ant-tree-child-tree-open"
|
||||
data-expanded="true"
|
||||
>
|
||||
<li
|
||||
class=""
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher-noop"
|
||||
/>
|
||||
<span
|
||||
class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"
|
||||
title="leaf 0-0"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-iconEle ant-tree-icon__customize"
|
||||
>
|
||||
<i
|
||||
class="anticon anticon-file"
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="ant-tree-title"
|
||||
>
|
||||
leaf 0-0
|
||||
</span>
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
class=""
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher-noop"
|
||||
/>
|
||||
<span
|
||||
class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"
|
||||
title="leaf 0-1"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-iconEle ant-tree-icon__customize"
|
||||
>
|
||||
<i
|
||||
class="anticon anticon-file"
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="ant-tree-title"
|
||||
>
|
||||
leaf 0-1
|
||||
</span>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li
|
||||
class="ant-tree-treenode-switcher-open"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher_open"
|
||||
/>
|
||||
<span
|
||||
class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-open"
|
||||
title="parent 1"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-iconEle ant-tree-icon__customize"
|
||||
>
|
||||
<i
|
||||
class="anticon anticon-folder-open"
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="ant-tree-title"
|
||||
>
|
||||
parent 1
|
||||
</span>
|
||||
</span>
|
||||
<ul
|
||||
class="ant-tree-child-tree ant-tree-child-tree-open"
|
||||
data-expanded="true"
|
||||
>
|
||||
<li
|
||||
class=""
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher-noop"
|
||||
/>
|
||||
<span
|
||||
class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"
|
||||
title="leaf 1-0"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-iconEle ant-tree-icon__customize"
|
||||
>
|
||||
<i
|
||||
class="anticon anticon-file"
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="ant-tree-title"
|
||||
>
|
||||
leaf 1-0
|
||||
</span>
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
class=""
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher-noop"
|
||||
/>
|
||||
<span
|
||||
class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-normal"
|
||||
title="leaf 1-1"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-iconEle ant-tree-icon__customize"
|
||||
>
|
||||
<i
|
||||
class="anticon anticon-file"
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="ant-tree-title"
|
||||
>
|
||||
leaf 1-1
|
||||
</span>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/tree/demo/draggable.md correctly 1`] = `
|
||||
<ul
|
||||
class="ant-tree ant-tree-icon-hide draggable-tree"
|
||||
role="tree-node"
|
||||
unselectable="on"
|
||||
>
|
||||
<li
|
||||
class="ant-tree-treenode-switcher-open"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher_open"
|
||||
@ -599,7 +762,7 @@ exports[`renders ./components/tree/demo/draggable.md correctly 1`] = `
|
||||
data-expanded="true"
|
||||
>
|
||||
<li
|
||||
class=""
|
||||
class="ant-tree-treenode-switcher-open"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher_open"
|
||||
@ -621,7 +784,7 @@ exports[`renders ./components/tree/demo/draggable.md correctly 1`] = `
|
||||
data-expanded="true"
|
||||
>
|
||||
<li
|
||||
class=""
|
||||
class="ant-tree-treenode-switcher-open"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher-noop"
|
||||
@ -640,7 +803,7 @@ exports[`renders ./components/tree/demo/draggable.md correctly 1`] = `
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
class=""
|
||||
class="ant-tree-treenode-switcher-close"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher-noop"
|
||||
@ -659,7 +822,7 @@ exports[`renders ./components/tree/demo/draggable.md correctly 1`] = `
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
class=""
|
||||
class="ant-tree-treenode-switcher-close"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher-noop"
|
||||
@ -680,7 +843,7 @@ exports[`renders ./components/tree/demo/draggable.md correctly 1`] = `
|
||||
</ul>
|
||||
</li>
|
||||
<li
|
||||
class=""
|
||||
class="ant-tree-treenode-switcher-close"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher_close"
|
||||
@ -699,7 +862,7 @@ exports[`renders ./components/tree/demo/draggable.md correctly 1`] = `
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
class=""
|
||||
class="ant-tree-treenode-switcher-close"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher-noop"
|
||||
@ -720,7 +883,7 @@ exports[`renders ./components/tree/demo/draggable.md correctly 1`] = `
|
||||
</ul>
|
||||
</li>
|
||||
<li
|
||||
class=""
|
||||
class="ant-tree-treenode-switcher-close"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher_close"
|
||||
@ -739,7 +902,7 @@ exports[`renders ./components/tree/demo/draggable.md correctly 1`] = `
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
class=""
|
||||
class="ant-tree-treenode-switcher-close"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher-noop"
|
||||
@ -762,12 +925,12 @@ exports[`renders ./components/tree/demo/draggable.md correctly 1`] = `
|
||||
|
||||
exports[`renders ./components/tree/demo/dynamic.md correctly 1`] = `
|
||||
<ul
|
||||
class="ant-tree"
|
||||
class="ant-tree ant-tree-icon-hide"
|
||||
role="tree-node"
|
||||
unselectable="on"
|
||||
>
|
||||
<li
|
||||
class=""
|
||||
class="ant-tree-treenode-switcher-close"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher_close"
|
||||
@ -784,7 +947,7 @@ exports[`renders ./components/tree/demo/dynamic.md correctly 1`] = `
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
class=""
|
||||
class="ant-tree-treenode-switcher-close"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher_close"
|
||||
@ -822,12 +985,12 @@ exports[`renders ./components/tree/demo/dynamic.md correctly 1`] = `
|
||||
|
||||
exports[`renders ./components/tree/demo/line.md correctly 1`] = `
|
||||
<ul
|
||||
class="ant-tree ant-tree-show-line"
|
||||
class="ant-tree ant-tree-icon-hide ant-tree-show-line"
|
||||
role="tree-node"
|
||||
unselectable="on"
|
||||
>
|
||||
<li
|
||||
class=""
|
||||
class="ant-tree-treenode-switcher-open"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher_open"
|
||||
@ -847,7 +1010,7 @@ exports[`renders ./components/tree/demo/line.md correctly 1`] = `
|
||||
data-expanded="true"
|
||||
>
|
||||
<li
|
||||
class=""
|
||||
class="ant-tree-treenode-switcher-open"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher_open"
|
||||
@ -867,7 +1030,7 @@ exports[`renders ./components/tree/demo/line.md correctly 1`] = `
|
||||
data-expanded="true"
|
||||
>
|
||||
<li
|
||||
class=""
|
||||
class="ant-tree-treenode-switcher-close"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher-noop"
|
||||
@ -884,7 +1047,7 @@ exports[`renders ./components/tree/demo/line.md correctly 1`] = `
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
class=""
|
||||
class="ant-tree-treenode-switcher-close"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher-noop"
|
||||
@ -901,7 +1064,7 @@ exports[`renders ./components/tree/demo/line.md correctly 1`] = `
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
class=""
|
||||
class="ant-tree-treenode-switcher-close"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher-noop"
|
||||
@ -920,7 +1083,7 @@ exports[`renders ./components/tree/demo/line.md correctly 1`] = `
|
||||
</ul>
|
||||
</li>
|
||||
<li
|
||||
class=""
|
||||
class="ant-tree-treenode-switcher-close"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher_close"
|
||||
@ -937,7 +1100,7 @@ exports[`renders ./components/tree/demo/line.md correctly 1`] = `
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
class=""
|
||||
class="ant-tree-treenode-switcher-close"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher_close"
|
||||
@ -978,12 +1141,12 @@ exports[`renders ./components/tree/demo/search.md correctly 1`] = `
|
||||
</span>
|
||||
</span>
|
||||
<ul
|
||||
class="ant-tree"
|
||||
class="ant-tree ant-tree-icon-hide"
|
||||
role="tree-node"
|
||||
unselectable="on"
|
||||
>
|
||||
<li
|
||||
class=""
|
||||
class="ant-tree-treenode-switcher-close"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher_close"
|
||||
@ -1005,7 +1168,7 @@ exports[`renders ./components/tree/demo/search.md correctly 1`] = `
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
class=""
|
||||
class="ant-tree-treenode-switcher-close"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher_close"
|
||||
@ -1027,7 +1190,7 @@ exports[`renders ./components/tree/demo/search.md correctly 1`] = `
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
class=""
|
||||
class="ant-tree-treenode-switcher-close"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher-noop"
|
||||
|
1265
components/tree/__tests__/__snapshots__/directory.test.js.snap
Normal file
1265
components/tree/__tests__/__snapshots__/directory.test.js.snap
Normal file
File diff suppressed because it is too large
Load Diff
104
components/tree/__tests__/directory.test.js
Normal file
104
components/tree/__tests__/directory.test.js
Normal file
@ -0,0 +1,104 @@
|
||||
import React from 'react';
|
||||
import { mount, render } from 'enzyme';
|
||||
import Tree from '../index';
|
||||
|
||||
const DirectoryTree = Tree.DirectoryTree;
|
||||
const TreeNode = Tree.TreeNode;
|
||||
|
||||
describe('Directory Tree', () => {
|
||||
beforeAll(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
function createTree(props) {
|
||||
return (
|
||||
<DirectoryTree {...props}>
|
||||
<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>
|
||||
</DirectoryTree>
|
||||
);
|
||||
}
|
||||
|
||||
describe('expand', () => {
|
||||
it('click', () => {
|
||||
const wrapper = mount(createTree());
|
||||
|
||||
wrapper.find(TreeNode).find('.ant-tree-node-content-wrapper').at(0).simulate('click');
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
jest.runAllTimers();
|
||||
wrapper.find(TreeNode).find('.ant-tree-node-content-wrapper').at(0).simulate('click');
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('double click', () => {
|
||||
const wrapper = mount(createTree({ expandAction: 'doubleClick' }));
|
||||
|
||||
wrapper.find(TreeNode).find('.ant-tree-node-content-wrapper').at(0).simulate('doubleClick');
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
jest.runAllTimers();
|
||||
wrapper.find(TreeNode).find('.ant-tree-node-content-wrapper').at(0).simulate('doubleClick');
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
it('defaultExpandAll', () => {
|
||||
const wrapper = render(createTree({ defaultExpandAll: true }));
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('defaultExpandParent', () => {
|
||||
const wrapper = render(createTree({ defaultExpandParent: true }));
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('expandedKeys update', () => {
|
||||
const wrapper = mount(createTree());
|
||||
wrapper.setProps({ expandedKeys: ['0-1'] });
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('selectedKeys update', () => {
|
||||
const wrapper = mount(createTree({ defaultExpandAll: true }));
|
||||
wrapper.setProps({ selectedKeys: ['0-1-0'] });
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('group select', () => {
|
||||
let nativeEventProto = null;
|
||||
const wrapper = mount(createTree({
|
||||
defaultExpandAll: true,
|
||||
expandAction: 'doubleClick',
|
||||
multiple: true,
|
||||
onClick: (e) => {
|
||||
nativeEventProto = Object.getPrototypeOf(e.nativeEvent);
|
||||
},
|
||||
}));
|
||||
|
||||
wrapper.find(TreeNode).find('.ant-tree-node-content-wrapper').at(0).simulate('click');
|
||||
|
||||
// React not simulate full of NativeEvent. Hook it.
|
||||
// Ref: https://github.com/facebook/react/blob/master/packages/react-dom/src/test-utils/ReactTestUtils.js#L360
|
||||
nativeEventProto.ctrlKey = true;
|
||||
|
||||
wrapper.find(TreeNode).find('.ant-tree-node-content-wrapper').at(1).simulate('click');
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
|
||||
delete nativeEventProto.ctrlKey;
|
||||
nativeEventProto.shiftKey = true;
|
||||
|
||||
wrapper.find(TreeNode).find('.ant-tree-node-content-wrapper').at(4).simulate('click');
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
|
||||
delete nativeEventProto.shiftKey;
|
||||
});
|
||||
});
|
35
components/tree/__tests__/util.test.js
Normal file
35
components/tree/__tests__/util.test.js
Normal file
@ -0,0 +1,35 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import Tree from '../index';
|
||||
import { calcRangeKeys } from '../util';
|
||||
|
||||
const TreeNode = Tree.TreeNode;
|
||||
|
||||
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 { children } = wrapper.find(Tree).props();
|
||||
const keys = calcRangeKeys(children, ['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());
|
||||
});
|
||||
});
|
51
components/tree/demo/directory.md
Normal file
51
components/tree/demo/directory.md
Normal file
@ -0,0 +1,51 @@
|
||||
---
|
||||
order: 7
|
||||
title:
|
||||
zh-CN: 目录
|
||||
en-US: directory
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
内置的目录树,`multiple` 模式支持 `ctrl(Windows)` / `command(Mac)` 复选。
|
||||
|
||||
## en-US
|
||||
|
||||
Built-in directory tree. `multiple` support `ctrl(Windows)` / `command(Mac)` selection.
|
||||
|
||||
````jsx
|
||||
import { Tree } from 'antd';
|
||||
const DirectoryTree = Tree.DirectoryTree;
|
||||
const TreeNode = Tree.TreeNode;
|
||||
|
||||
class Demo extends React.Component {
|
||||
onSelect = () => {
|
||||
console.log('Trigger Select');
|
||||
};
|
||||
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>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ReactDOM.render(<Demo />, mountNode);
|
||||
````
|
@ -55,6 +55,13 @@ Almost anything can be represented in a tree structure. Examples include directo
|
||||
| selectable | Set whether the treeNode can be selected | boolean | true |
|
||||
| title | Title | string\|ReactNode | '---' |
|
||||
|
||||
### DirectoryTree props
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| expandAction | Directory open logic, optional `false` `'click'` `'doubleClick'` | string | click |
|
||||
|
||||
|
||||
## Note
|
||||
|
||||
Before `3.4.0`:
|
||||
|
@ -1,132 +1,15 @@
|
||||
import * as React from 'react';
|
||||
import RcTree, { TreeNode } from 'rc-tree';
|
||||
import animation from '../_util/openAnimation';
|
||||
import Tree from './Tree';
|
||||
|
||||
export interface AntdTreeNodeAttribute {
|
||||
eventKey: string;
|
||||
prefixCls: string;
|
||||
className: string;
|
||||
expanded: boolean;
|
||||
selected: boolean;
|
||||
checked: boolean;
|
||||
halfChecked: boolean;
|
||||
children: React.ReactNode;
|
||||
title: React.ReactNode;
|
||||
pos: string;
|
||||
dragOver: boolean;
|
||||
dragOverGapTop: boolean;
|
||||
dragOverGapBottom: boolean;
|
||||
isLeaf: boolean;
|
||||
selectable: boolean;
|
||||
disabled: boolean;
|
||||
disableCheckbox: boolean;
|
||||
}
|
||||
export interface AntTreeNodeProps {
|
||||
disabled?: boolean;
|
||||
disableCheckbox?: boolean;
|
||||
title?: string | React.ReactNode;
|
||||
key?: string;
|
||||
isLeaf?: boolean;
|
||||
icon?: (treeNode: AntdTreeNodeAttribute) => React.ReactNode | React.ReactNode;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
export {
|
||||
TreeProps,
|
||||
AntTreeNode,
|
||||
AntTreeNodeMouseEvent, AntTreeNodeExpandedEvent, AntTreeNodeCheckedEvent, AntTreeNodeSelectedEvent,
|
||||
AntdTreeNodeAttribute, AntTreeNodeProps,
|
||||
} from './Tree';
|
||||
|
||||
export interface AntTreeNode extends React.Component<AntTreeNodeProps, {}> {}
|
||||
export {
|
||||
ExpandAction as DirectoryTreeExpandAction,
|
||||
DirectoryTreeProps,
|
||||
} from './DirectoryTree';
|
||||
|
||||
export interface AntTreeNodeEvent {
|
||||
event: 'check' | 'select';
|
||||
node: AntTreeNode;
|
||||
checked?: boolean;
|
||||
checkedNodes?: AntTreeNode[];
|
||||
selected?: boolean;
|
||||
selectedNodes?: AntTreeNode[];
|
||||
}
|
||||
|
||||
export interface AntTreeNodeMouseEvent {
|
||||
node: AntTreeNode;
|
||||
event: React.MouseEventHandler<any>;
|
||||
}
|
||||
|
||||
export interface TreeProps {
|
||||
showLine?: boolean;
|
||||
className?: string;
|
||||
/** 是否支持多选 */
|
||||
multiple?: boolean;
|
||||
/** 是否自动展开父节点 */
|
||||
autoExpandParent?: boolean;
|
||||
/** checkable状态下节点选择完全受控(父子节点选中状态不再关联)*/
|
||||
checkStrictly?: boolean;
|
||||
/** 是否支持选中 */
|
||||
checkable?: boolean;
|
||||
/** 默认展开所有树节点 */
|
||||
defaultExpandAll?: boolean;
|
||||
/** 默认展开指定的树节点 */
|
||||
defaultExpandedKeys?: string[];
|
||||
/** (受控)展开指定的树节点 */
|
||||
expandedKeys?: string[];
|
||||
/** (受控)选中复选框的树节点 */
|
||||
checkedKeys?: string[] | { checked: string[]; halfChecked: string[] };
|
||||
/** 默认选中复选框的树节点 */
|
||||
defaultCheckedKeys?: string[];
|
||||
/** (受控)设置选中的树节点 */
|
||||
selectedKeys?: string[];
|
||||
/** 默认选中的树节点 */
|
||||
defaultSelectedKeys?: string[];
|
||||
/** 展开/收起节点时触发 */
|
||||
onExpand?: (
|
||||
expandedKeys: string[],
|
||||
info: { node: AntTreeNode; expanded: boolean; },
|
||||
) => void | PromiseLike<any>;
|
||||
/** 点击复选框触发 */
|
||||
onCheck?: (checkedKeys: string[], e: AntTreeNodeEvent) => void;
|
||||
/** 点击树节点触发 */
|
||||
onSelect?: (selectedKeys: string[], e: AntTreeNodeEvent) => void;
|
||||
/** filter some AntTreeNodes as you need. it should return true */
|
||||
filterAntTreeNode?: (node: AntTreeNode) => boolean;
|
||||
/** 异步加载数据 */
|
||||
loadData?: (node: AntTreeNode) => PromiseLike<any>;
|
||||
/** 响应右键点击 */
|
||||
onRightClick?: (options: AntTreeNodeMouseEvent) => void;
|
||||
/** 设置节点可拖拽(IE>8)*/
|
||||
draggable?: boolean;
|
||||
/** 开始拖拽时调用 */
|
||||
onDragStart?: (options: AntTreeNodeMouseEvent) => void;
|
||||
/** dragenter 触发时调用 */
|
||||
onDragEnter?: (options: AntTreeNodeMouseEvent) => void;
|
||||
/** dragover 触发时调用 */
|
||||
onDragOver?: (options: AntTreeNodeMouseEvent) => void;
|
||||
/** dragleave 触发时调用 */
|
||||
onDragLeave?: (options: AntTreeNodeMouseEvent) => void;
|
||||
/** drop 触发时调用 */
|
||||
onDrop?: (options: AntTreeNodeMouseEvent) => void;
|
||||
style?: React.CSSProperties;
|
||||
showIcon?: boolean;
|
||||
prefixCls?: string;
|
||||
filterTreeNode?: (node: AntTreeNode) => boolean;
|
||||
}
|
||||
|
||||
export default class Tree extends React.Component<TreeProps, any> {
|
||||
static TreeNode = TreeNode;
|
||||
|
||||
static defaultProps = {
|
||||
prefixCls: 'ant-tree',
|
||||
checkable: false,
|
||||
showIcon: false,
|
||||
openAnimation: animation,
|
||||
};
|
||||
|
||||
render() {
|
||||
const props = this.props;
|
||||
const { prefixCls, className } = props;
|
||||
let checkable = props.checkable;
|
||||
return (
|
||||
<RcTree
|
||||
{...props}
|
||||
className={className}
|
||||
checkable={checkable ? <span className={`${prefixCls}-checkbox-inner`} /> : checkable}
|
||||
>
|
||||
{this.props.children}
|
||||
</RcTree>
|
||||
);
|
||||
}
|
||||
}
|
||||
export default Tree;
|
||||
|
@ -56,6 +56,13 @@ subtitle: 树形控件
|
||||
| selectable | 设置节点是否可被选中 | boolean | true |
|
||||
| title | 标题 | string\|ReactNode | '---' |
|
||||
|
||||
### DirectoryTree props
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| expandAction | 目录展开逻辑,可选 `false` `'click'` `'doubleClick'` | string | click |
|
||||
|
||||
|
||||
## 注意
|
||||
|
||||
在 `3.4.0` 之前:
|
||||
|
95
components/tree/style/directory.less
Normal file
95
components/tree/style/directory.less
Normal file
@ -0,0 +1,95 @@
|
||||
@import "../../style/themes/default";
|
||||
|
||||
@tree-prefix-cls: ~"@{ant-prefix}-tree";
|
||||
|
||||
.@{tree-prefix-cls} {
|
||||
&.@{tree-prefix-cls}-directory {
|
||||
position: relative;
|
||||
|
||||
// Stretch selector width
|
||||
> li,
|
||||
.@{tree-prefix-cls}-child-tree > li {
|
||||
span {
|
||||
&.@{tree-prefix-cls}-switcher {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
|
||||
&.@{tree-prefix-cls}-switcher-noop {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.@{tree-prefix-cls}-checkbox {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&.@{tree-prefix-cls}-node-content-wrapper {
|
||||
user-select: none;
|
||||
border-radius: 0;
|
||||
|
||||
&:hover {
|
||||
background: transparent;
|
||||
|
||||
&:before {
|
||||
background: @item-hover-bg;
|
||||
}
|
||||
}
|
||||
|
||||
&.@{tree-prefix-cls}-node-selected {
|
||||
color: @tree-directory-selected-color;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: @tree-title-height;
|
||||
transition: all .3s;
|
||||
}
|
||||
|
||||
> span {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.@{tree-prefix-cls}-treenode-selected {
|
||||
> span {
|
||||
&.@{tree-prefix-cls}-switcher {
|
||||
color: @tree-directory-selected-color;
|
||||
}
|
||||
|
||||
&.@{tree-prefix-cls}-checkbox {
|
||||
.@{tree-prefix-cls}-checkbox-inner {
|
||||
border-color: @primary-color;
|
||||
}
|
||||
|
||||
&.@{tree-prefix-cls}-checkbox-checked {
|
||||
&:after {
|
||||
border-color: @checkbox-check-color;
|
||||
}
|
||||
|
||||
.@{tree-prefix-cls}-checkbox-inner {
|
||||
background: @checkbox-check-color;
|
||||
|
||||
&:after {
|
||||
border-color: @primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.@{tree-prefix-cls}-node-content-wrapper {
|
||||
&:before {
|
||||
background: @tree-directory-selected-bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
@import "../../style/mixins/index";
|
||||
@import "../../checkbox/style/mixin";
|
||||
@import "./mixin";
|
||||
@import "./directory";
|
||||
|
||||
@tree-prefix-cls: ~"@{ant-prefix}-tree";
|
||||
@tree-showline-icon-color: @text-color-secondary;
|
||||
@ -58,9 +59,37 @@
|
||||
font-weight: 500 !important;
|
||||
}
|
||||
}
|
||||
|
||||
// When node is loading
|
||||
&.@{tree-prefix-cls}-treenode-loading {
|
||||
span {
|
||||
&.@{tree-prefix-cls}-switcher {
|
||||
&.@{tree-prefix-cls}-switcher_open,
|
||||
&.@{tree-prefix-cls}-switcher_close {
|
||||
&:before {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
.iconfont-font("\E64D");
|
||||
animation: loadingCircle 1s infinite linear;
|
||||
color: @primary-color;
|
||||
transform: none;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
:root &:after {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
padding: 0 0 0 18px;
|
||||
padding: 0 0 0 @tree-child-padding;
|
||||
}
|
||||
.@{tree-prefix-cls}-node-content-wrapper {
|
||||
display: inline-block;
|
||||
@ -72,9 +101,8 @@
|
||||
vertical-align: top;
|
||||
color: @text-color;
|
||||
transition: all .3s;
|
||||
position: relative;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
height: @tree-title-height;
|
||||
line-height: @tree-title-height;
|
||||
&:hover {
|
||||
background-color: @item-hover-bg;
|
||||
}
|
||||
@ -91,7 +119,7 @@
|
||||
margin: 0;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
line-height: @tree-title-height;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
border: 0 none;
|
||||
@ -99,21 +127,10 @@
|
||||
outline: none;
|
||||
text-align: center;
|
||||
}
|
||||
&.@{tree-prefix-cls}-icon_loading {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 1px;
|
||||
background: #fff;
|
||||
transform: translateX(-100%);
|
||||
transition: all .3s;
|
||||
&:after {
|
||||
display: inline-block;
|
||||
.iconfont-font("\E64D");
|
||||
animation: loadingCircle 1s infinite linear;
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
&.@{tree-prefix-cls}-switcher {
|
||||
position: relative;
|
||||
|
||||
&.@{tree-prefix-cls}-switcher-noop {
|
||||
cursor: default;
|
||||
}
|
||||
@ -137,6 +154,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> li {
|
||||
&:first-child {
|
||||
padding-top: 7px;
|
||||
@ -200,4 +218,12 @@
|
||||
margin: 22px 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.@{tree-prefix-cls}-icon-hide {
|
||||
.@{tree-prefix-cls}-treenode-loading {
|
||||
.@{tree-prefix-cls}-iconEle {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
56
components/tree/util.ts
Normal file
56
components/tree/util.ts
Normal file
@ -0,0 +1,56 @@
|
||||
import * as React from 'react';
|
||||
import { traverseTreeNodes } from 'rc-tree/lib/util';
|
||||
|
||||
export interface TraverseData {
|
||||
key: string,
|
||||
}
|
||||
|
||||
enum Record {
|
||||
None,
|
||||
Start,
|
||||
End,
|
||||
}
|
||||
|
||||
/** 计算选中范围,只考虑expanded情况以优化性能 */
|
||||
export function calcRangeKeys(nodeList: React.ReactNode | React.ReactNode[], expandedKeys: string[], startKey?: string, endKey?: string): string[] {
|
||||
const keys: string[] = [];
|
||||
let record: Record = Record.None;
|
||||
|
||||
if (startKey && startKey === endKey) {
|
||||
return [startKey];
|
||||
}
|
||||
if (!startKey || !endKey) {
|
||||
return [];
|
||||
}
|
||||
|
||||
function matchKey(key: string) {
|
||||
return key === startKey || key === endKey;
|
||||
}
|
||||
|
||||
traverseTreeNodes(nodeList, ({ key }: TraverseData) => {
|
||||
if (record === Record.End) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (matchKey(key)) {
|
||||
// Match test
|
||||
keys.push(key);
|
||||
|
||||
if (record === Record.None) {
|
||||
record = Record.Start;
|
||||
} else if (record === Record.Start) {
|
||||
record = Record.End;
|
||||
return false;
|
||||
}
|
||||
} else if (record === Record.Start) {
|
||||
// Append selection
|
||||
keys.push(key);
|
||||
}
|
||||
|
||||
if (expandedKeys.indexOf(key) === -1) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
return keys;
|
||||
}
|
@ -75,7 +75,7 @@
|
||||
"rc-tabs": "~9.2.0",
|
||||
"rc-time-picker": "~3.3.0",
|
||||
"rc-tooltip": "~3.7.0",
|
||||
"rc-tree": "~1.8.0",
|
||||
"rc-tree": "~1.11.0",
|
||||
"rc-tree-select": "~1.12.0",
|
||||
"rc-upload": "~2.4.0",
|
||||
"rc-util": "^4.0.4",
|
||||
|
1
typings/custom-typings.d.ts
vendored
1
typings/custom-typings.d.ts
vendored
@ -43,6 +43,7 @@ declare module 'rc-menu';
|
||||
declare module 'rc-tabs*';
|
||||
|
||||
declare module 'rc-tree';
|
||||
declare module 'rc-tree/lib/util';
|
||||
|
||||
declare module 'rc-tooltip*';
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user