From 2c0c301937fc9706184a9044aa18b4c272cd26c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=A8=9C=E5=A8=9C?= Date: Fri, 10 Apr 2020 17:21:41 +0800 Subject: [PATCH 1/5] feat: update Tree component --- components/tree/PropsType.tsx | 100 +- components/tree/Tree.tsx | 178 +- components/tree/TreeNode.tsx | 175 +- .../__snapshots__/index.test.jsx.snap | 5694 ----------------- components/tree/__tests__/index.test.jsx | 87 +- components/tree/style/component.scss | 188 +- components/tree/style/tree.scss | 5 +- components/tree/tree.md | 628 +- components/tree/utils.tsx | 51 +- site/pages/Components/index.jsx | 4 + site/site.config.js | 12 +- 11 files changed, 1035 insertions(+), 6087 deletions(-) delete mode 100644 components/tree/__tests__/__snapshots__/index.test.jsx.snap diff --git a/components/tree/PropsType.tsx b/components/tree/PropsType.tsx index 30e2eca8..90c4b0e7 100644 --- a/components/tree/PropsType.tsx +++ b/components/tree/PropsType.tsx @@ -1,45 +1,95 @@ -import { MouseEvent, ReactNode } from 'react'; +import { MouseEvent, ReactNode, createContext, Context } from 'react'; import TreeNode from './TreeNode'; -export default interface PropsType { +type TreeCheckEventHandler = ( + checkedMap: { + checkedKeys: Array; + halfCheckedKeys: Array; + }, + checkedObj: { + node: TreeNode; + checked: boolean; + event: MouseEvent; + }) => void; + +type TreeExpandEventHandler = ( + expandedKeys: Array, + expandedObj: { + node: TreeNode; + expanded: boolean; + event: MouseEvent; + }) => void; + +type TreeSelectEventHandler = ( + selectedKeys: Array, + selectedObj: { + node: EventDataNode; + selected: boolean; + event: MouseEvent; + }) => void; +type TreeNodeExpandEventHandler = (treeNode: EventDataNode, targetExpanded: boolean, event: MouseEvent) => void; +type TreeNodeCheckEventHandler = (treeNode: EventDataNode, targetChecked: boolean, event: MouseEvent) => void; +type TreeNodeSelectEventHandler = (treeNode: EventDataNode, targetSelected: boolean, event: MouseEvent) => void; +type TreeNodeClickEventHandler = (event: MouseEvent) => void; + +export interface PropsType { prefixCls?: string; treeData?: object[]; - canCheck?: boolean; + checkable?: boolean; checkedKeys?: string[]; - expandedKeys: string[]; + expandedKeys?: string[]; defaultExpandAll?: boolean; + autoExpandParent?: boolean; + selectedKeys?: string[]; + selectable?: boolean; + multiple?: boolean; + disabled?: boolean; // 禁掉树 + showIcon?: boolean; + icon?: ReactNode; + showLine?: boolean; + className?: string; + switcherIcon?: ReactNode; + children?: ReactNode; - onCheck?( - checkedMap: { - checkedKeys: Array; - halfCheckedKeys: Array; - }, - checkedObj: { - node: TreeNode; - checked: boolean; - event: MouseEvent; - }): void; - - onExpand?(expandedObj: { - expandedKeys: Array; - node: TreeNode; - expanded: boolean; - event: MouseEvent; - }): void; + onCheck: TreeCheckEventHandler; + onExpand: TreeExpandEventHandler; + onSelect: TreeSelectEventHandler; + onClick?: TreeNodeClickEventHandler; } export interface TreeNodePropsType { - prefixCls?: string; title?: string|ReactNode; keys: string; expanded?: boolean; checked?: boolean; halfChecked?: boolean; - canCheck?: boolean; checkDisabled?: boolean; + selectDisabled?: boolean; + disabled?: boolean; // 禁掉响应 isLeaf?: boolean; + selected?: boolean; + checkable?: boolean; + icon?: ReactNode; + children?: React.ReactNode; +} + +export type EventDataNode = Omit; - onNodeExpand?(treeNode: TreeNode, targetExpanded: boolean, event: MouseEvent): void; +export interface TreeContextProps { + prefixCls?: string; + checkable?: boolean; + selectable?: boolean; + disabled?: boolean; // 禁掉树 + showIcon?: boolean; + icon: ReactNode; + showLine?: boolean; + switcherIcon?: ReactNode; + children?: React.ReactNode; - onNodeCheck?(treeNode: TreeNode, targetChecked: boolean, event: MouseEvent): void; + onNodeExpand: TreeNodeExpandEventHandler; + onNodeCheck: TreeNodeCheckEventHandler; + onNodeSelect: TreeNodeSelectEventHandler; + onNodeClick: TreeNodeClickEventHandler; } + +export const TreeContext: Context = createContext(null); diff --git a/components/tree/Tree.tsx b/components/tree/Tree.tsx index c3bd3030..346389ee 100644 --- a/components/tree/Tree.tsx +++ b/components/tree/Tree.tsx @@ -1,86 +1,107 @@ import React, { Component } from 'react'; +import classnames from 'classnames'; import TreeNode from './TreeNode'; -import PropsType from './PropsType'; +import { PropsType, TreeContext } from './PropsType'; + import { getTreeNodeChildren, convertTreeToData, initialTreeData, conductExpandParent, - conductCheck, arrDel, arrAdd, deepCopy, + conductCheck, arrDel, arrAdd, calcSelectedKeys, } from './utils'; interface StateType { checkedKeys?: Array; + selectedKeys?: Array; halfCheckedKeys?: Array; expandedKeys?: Array; treeData: Array; + prevProps?: PropsType; } class Tree extends Component { static defaultProps = { - prefixCls: 'ui-tree', + prefixCls: 'zw-tree', checkedKeys: [], + selectedKeys: [], expandedKeys: [], + disabled: false, + checkable: false, + defaultExpandAll: false, + autoExpandParent: true, + multiple: false, + selectable: true, + showLine: false, + showIcon: true, }; static TreeNode: any; - state = { + state: StateType = { checkedKeys: [], halfCheckedKeys: [], expandedKeys: [], treeData: [], + selectedKeys: [], }; - componentWillMount() { - this.initTreeNodes(); - } - - initTreeNodes = (): void => { - const { treeData = [], checkedKeys, expandedKeys, defaultExpandAll, children } = this.props; - const newState = { - treeData: [] as object[], - halfCheckedKeys: [] as string[], - checkedKeys: [] as string[], - expandedKeys: [] as string[], + static getDerivedStateFromProps(nextProps: PropsType, prevState: StateType) { + const { prevProps } = prevState; + const newState: Partial = { + prevProps: nextProps, }; + function needUpdate(key: string) { + return (!prevProps && key in nextProps) || (prevProps && prevProps[key] !== nextProps[key]); + } + let treeData: TreeNode[]; let TreeDataInformationSet = { allExpandDataMap: {}, treeData: [] }; - if (treeData.length > 0) { - // 深拷贝treeData - newState.treeData = deepCopy(treeData); - TreeDataInformationSet = initialTreeData(newState.treeData); - } else if (children) { - const treeNodeChildren = getTreeNodeChildren({ children }); + if (needUpdate('treeData')) { + ({ treeData } = nextProps); + TreeDataInformationSet = initialTreeData(treeData); + treeData = TreeDataInformationSet.treeData; + } else if (needUpdate('children')) { + const treeNodeChildren = getTreeNodeChildren({ children: nextProps.children }); if (treeNodeChildren.length > 0) { TreeDataInformationSet = initialTreeData(convertTreeToData(treeNodeChildren)); } + treeData = TreeDataInformationSet.treeData; } - const { allExpandDataMap, treeData: initialedTreeData } = TreeDataInformationSet; - newState.treeData = initialedTreeData; + if (treeData) { + newState.treeData = treeData; + // newState.treeData = deepCopy(treeData); + } + const currentTreeData = newState.treeData || prevState.treeData; // 初始化checkedKeys,更新treeData - if (checkedKeys && checkedKeys.length > 0) { + if (needUpdate('checkedKeys')) { const { checkedKeys: finalCheckedKeys, halfCheckedKeys: finalHalfCheckedKeys, treeData: finaltreeDataAfter, - } = conductCheck(checkedKeys, true, newState.treeData); + } = conductCheck(nextProps.checkedKeys, true, currentTreeData); newState.checkedKeys = finalCheckedKeys; newState.halfCheckedKeys = finalHalfCheckedKeys; newState.treeData = finaltreeDataAfter; } + // selected + if (nextProps.selectable) { + if (needUpdate('selectedKeys')) { + newState.selectedKeys = calcSelectedKeys(nextProps.selectedKeys, nextProps); + } + } /* 初始化expandedKeys 如果要展开所有结点,则取到所有根结点作为展开结点; 否则展开传入的展开结点 */ - if (defaultExpandAll) { - newState.expandedKeys = conductExpandParent(Object.keys(allExpandDataMap), newState.treeData); - } else if (expandedKeys && expandedKeys.length > 0) { - newState.expandedKeys = conductExpandParent(expandedKeys, newState.treeData); + if (!prevProps && nextProps.defaultExpandAll) { + newState.expandedKeys = conductExpandParent(Object.keys(TreeDataInformationSet.allExpandDataMap), currentTreeData); + } else if (needUpdate('expandedKeys') || (prevProps && needUpdate('autoExpandParent'))) { + newState.expandedKeys = nextProps.autoExpandParent ? conductExpandParent(nextProps.expandedKeys, currentTreeData) : nextProps.expandedKeys; } - this.setState(newState); - }; + return newState; + } onNodeCheck = (node, targetChecked, event) => { const { onCheck } = this.props; - const { keys } = node.props; + const { keys } = node; const { treeData: originalTreeData } = this.state; const { checkedKeys: originalCheckedKeys, halfCheckedKeys: originHalfCheckedKeys } = this.state; @@ -104,48 +125,81 @@ class Tree extends Component { }; onNodeExpand = (node, targetExpanded, event) => { - let { expandedKeys } = this.state; + let { expandedKeys = [] } = this.state; const { onExpand } = this.props; - const { keys } = node.props; - + const { keys } = node; if (targetExpanded) { expandedKeys = arrAdd(expandedKeys, keys); } else { expandedKeys = arrDel(expandedKeys, keys); } + this.setState({ expandedKeys, }); if (onExpand) { - onExpand({ - expandedKeys, + onExpand( + expandedKeys, { + node, + expanded: targetExpanded, + event, + }, + ); + } + }; + + onNodeSelect = (node, targetSelected, event) => { + let { selectedKeys = [] } = this.state; + const { onSelect, multiple } = this.props; + const { keys } = node; + + if (!targetSelected) { + selectedKeys = arrDel(selectedKeys, keys); + } else if (!multiple) { + selectedKeys = [keys]; + } else { + selectedKeys = arrAdd(selectedKeys, keys); + } + this.setState({ + selectedKeys, + }); + + if (onSelect) { + onSelect(selectedKeys, { + selected: targetSelected, node, - expanded: targetExpanded, event, }); } }; + onNodeClick = (e: React.MouseEvent) => { + const { onClick } = this.props; + if (onClick) { + onClick(e); + } + }; + renderTreeNodes = (data) => { - const { prefixCls, canCheck } = this.props; - const { expandedKeys, checkedKeys, halfCheckedKeys } = this.state; - return data.map((item, index) => { - const { keys, title, checkDisabled, children } = item; + const { expandedKeys, checkedKeys, halfCheckedKeys, selectedKeys } = this.state; + return data.map((item) => { + const { keys, title, checkDisabled, selectDisabled, children, icon, disabled, checkable } = item; return ( {children ? this.renderTreeNodes(children) : null} @@ -154,12 +208,32 @@ class Tree extends Component { }; render() { - const { prefixCls } = this.props; + const { prefixCls, className, showLine, checkable, selectable, disabled, showIcon, icon, switcherIcon } = this.props; const { treeData } = this.state; + const cls = classnames(prefixCls, className, { + [`${prefixCls}-show-line`]: showLine, + }); return ( -
    - {this.renderTreeNodes(treeData)} -
+ +
    + {this.renderTreeNodes(treeData)} +
+
); } } diff --git a/components/tree/TreeNode.tsx b/components/tree/TreeNode.tsx index 86f8dcbf..87222315 100644 --- a/components/tree/TreeNode.tsx +++ b/components/tree/TreeNode.tsx @@ -2,18 +2,31 @@ import React, { Component } from 'react'; import Animate from 'rc-animate'; import classnames from 'classnames'; import Checkbox from '../checkbox'; -import { TreeNodePropsType } from './PropsType'; +import Icon from '../icon'; +import { TreeNodePropsType, TreeContext } from './PropsType'; import animation from './openAnimation'; -import { isCheckDisabled } from './utils'; +import { convertNodePropsToEventData } from './utils'; const ICON_OPEN = 'open'; const ICON_CLOSE = 'close'; const ICON_NOOP = 'noop'; +const ICONS_TREE = Icon.createFromIconfont('//at.alicdn.com/t/font_1733827_ormtmokxywk.js'); class TreeNode extends Component { + static contextType = TreeContext; + static defaultProps = { - prefixCls: 'ui-tree', title: '', + expanded: false, + checked: false, + halfChecked: false, + selected: false, + checkDisabled: false, + selectDisabled: false, + disabled: false, + isLeaf: false, + icon: null, + children: null, }; isLeaf = () => { @@ -24,6 +37,41 @@ class TreeNode extends Component { return false; }; + isSelectable = () => { + const { selectDisabled } = this.props; + const { + context: { selectable }, + } = this; + + if (typeof selectDisabled === 'boolean') { + return !selectDisabled; + } + return selectable; + }; + + isDisabled = () => { + const { disabled } = this.props; + const { + context: { disabled: ParentDisabled }, + } = this; + + if (!disabled && !ParentDisabled) { + return false; + } + return true; + }; + + isCheckable = () => { + const { checkable } = this.props; + const { + context: { checkable: parentCheckable }, + } = this; + if (checkable !== false && parentCheckable) { + return true; + } + return false; + }; + getNodeState = () => { const { expanded } = this.props; let nodeState; @@ -36,55 +84,91 @@ class TreeNode extends Component { }; renderSwitcher = () => { - const { prefixCls } = this.props; + const { expanded } = this.props; + const { switcherIcon, showLine, prefixCls } = this.context; const switcherState = this.getNodeState(); - + const clsIconWrapper = classnames({ + [`${prefixCls}-switcher-icon`]: !showLine, + [`${prefixCls}-switcher-line-icon`]: showLine, + }); return ( + > + + {this.renderSwitcherIcon(expanded, switcherIcon, showLine)} + + ); }; + renderSwitcherIcon = (expanded, switcherIcon: React.ReactNode | null | undefined, showLine: boolean) => { + if (this.isLeaf()) { + return showLine ? : null; + } + // 只有showLine为true的模式下,可以用switcherIcon修改默认图标 + if (showLine && switcherIcon) { + return switcherIcon; + } + if (showLine) { + return expanded ? ( + + ) : ( + + ); + } + return ; + }; + renderCheckbox = () => { - const { checkDisabled, canCheck, checked, prefixCls, halfChecked } = this.props; - const isHalfChecked = checkDisabled ? false : halfChecked; - if (!canCheck) { + const { halfChecked, checked, checkDisabled } = this.props; + const { prefixCls } = this.context; + if (!this.isCheckable()) { return null; } return ( this.onCheck(e)} - disabled={checkDisabled} - checked={checked || isHalfChecked} - indeterminate={isHalfChecked} + onClick={(e) => this.onCheck(e)} + disabled={this.isDisabled() || checkDisabled} + checked={checked} + indeterminate={halfChecked} /> ); }; renderContent = () => { - const { prefixCls, title } = this.props; + const { showIcon, icon: treeIcon, prefixCls } = this.context; + const { title, icon, selected } = this.props; const nodeState = this.getNodeState(); - - const wrapClass = `${prefixCls}-node-content-wrapper ${prefixCls}-node-content-wrapper-${nodeState}`; - + const nodeIcon = icon || treeIcon; + const disabled = this.isDisabled(); + const isSelectable = !disabled && this.isSelectable(); + const wrapClass = classnames( + `${prefixCls}-node-content-wrapper`, + `${prefixCls}-node-content-wrapper-${nodeState}`, + { [`${prefixCls}-node-selected`]: isSelectable && selected }, + ); + const iconRender = (showIcon && nodeIcon) ? ({nodeIcon}) : null; return ( + {iconRender} {title} ); }; renderChildren = () => { - const { children, expanded, prefixCls = '' } = this.props; + const { prefixCls } = this.context; + const { children, expanded } = this.props; if (this.isLeaf()) { return null; } @@ -116,28 +200,61 @@ class TreeNode extends Component { }; onCheck = (e) => { - const { checked, onNodeCheck } = this.props; - const targetChecked = !checked; - if (!isCheckDisabled(this) && onNodeCheck) { - onNodeCheck(this, targetChecked, e); + const { checkDisabled, checked } = this.props; + const { onNodeCheck } = this.context; + if (this.isDisabled() || checkDisabled) { + return null; } + e.preventDefault(); + const targetChecked = !checked; + onNodeCheck(convertNodePropsToEventData(this.props), targetChecked, e); }; onExpand = (e) => { - const { expanded, onNodeExpand } = this.props; + const { expanded } = this.props; + const { onNodeExpand } = this.context; const targetExpanded = !expanded; - if (onNodeExpand) { - onNodeExpand(this, targetExpanded, e); + onNodeExpand(convertNodePropsToEventData(this.props), targetExpanded, e); + }; + + onSelectorClick = (e) => { + const { selected } = this.props; + const { onNodeClick, onNodeSelect } = this.context; + const targetSelected = !selected; + onNodeClick(e); + + if (this.isSelectable()) { + if (this.isDisabled()) { + return; + } + e.preventDefault(); + onNodeSelect(convertNodePropsToEventData(this.props), targetSelected, e); + } else { + this.onCheck(e); } }; render() { - const { keys } = this.props; + const { keys, checked } = this.props; + const { prefixCls } = this.context; + const disabled = this.isDisabled(); + const selected = this.isSelectable(); + const switcherState = this.getNodeState(); + + const cls = classnames(`${prefixCls}-node`, + `${prefixCls}-treenode-switcher-${switcherState}`, { + [`${prefixCls}-treenode-disabled`]: disabled, + [`${prefixCls}-treenode-selected`]: selected, + [`${prefixCls}-treenode-checked`]: checked, + }); + return (
  • - {this.renderSwitcher()} - {this.renderCheckbox()} - {this.renderContent()} +
    + {this.renderSwitcher()} + {this.renderCheckbox()} + {this.renderContent()} +
    {this.renderChildren()}
  • ); diff --git a/components/tree/__tests__/__snapshots__/index.test.jsx.snap b/components/tree/__tests__/__snapshots__/index.test.jsx.snap deleted file mode 100644 index 5202e408..00000000 --- a/components/tree/__tests__/__snapshots__/index.test.jsx.snap +++ /dev/null @@ -1,5694 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Tree renders basic Tree correctly 1`] = ` -
      -
    • - - - - 根结点1 - - - -
    • -
    -`; - -exports[`Tree renders basic Tree with TreeNode children 1`] = ` -
      -
    • - - - - - - - - - - - - 根结点1 - - - -
        -
      • - - - - - - - - - - - - 父结点 0-0 - - - -
          -
        • - - - - - - - - - - - - 父结点 0-0-0 - - - -
            -
          • - - - - - - - - - - - - 子结点 0-0-0-0 - - -
          • -
          • - - - - - - - - - - - - 子结点 0-0-0-1 - - -
          • -
          -
          -
        • -
        -
        -
      • -
      • - - - - - - - - - - - - 父结点 0-1 - - - -
          -
        • - - - - - - - - - - - - 子结点 0-1-0 - - -
        • -
        • - - - - - - - - - - - - 子结点 0-1-1 - - -
        • -
        • - - - - - - - - - - - - 子结点 0-1-2 - - -
        • -
        -
        -
      • -
      • - - - - - - - - - - - - 父结点 0-2 - - - -
          -
        • - - - - - - - - - - - - 父结点 0-2-0 - - -
        • -
        • - - - - - - - - - - - - 父结点 0-2-1 - - - -
            -
          • - - - - - - - - - - - - 子结点 0-2-1-0 - - - -
              -
            • - - - - - - - - - - - - 子结点 0-2-1-0-0 - - -
            • -
            • - - - - - - - - - - - - 子结点 0-2-1-0-1 - - -
            • -
            -
            -
          • -
          • - - - - - - - - - - - - 子结点 0-2-1-1 - - -
          • -
          -
          -
        • -
        • - - - - - - - - - - - - 子结点 0-2-2 - - -
        • -
        -
        -
      • -
      -
      -
    • -
    -`; - -exports[`Tree renders basic Tree with TreeNodes children 1`] = ` -
      -
    • - - - - - - - - - - - - parent 1 - - - -
        -
      • - - - - - - - - - - - - parent 1-0 - - - -
          -
        • - - - - - - - - - - - - leaf - - -
        • -
        • - - - - - - - - - - - - leaf - - -
        • -
        -
        -
      • -
      • - - - - - - - - - - - - parent 1-1 - - - -
          -
        • - - - - - - - - - - - - - sss - - - -
        • -
        -
        -
      • -
      -
      -
    • -
    -`; - -exports[`Tree renders basic Tree with defaultExpandAll canCheck correctly 1`] = ` -
      -
    • - - - - - - - - - - - - 根结点1 - - - -
        -
      • - - - - - - - - - - - - 父结点 0-0 - - - -
          -
        • - - - - - - - - - - - - 父结点 0-0-0 - - - -
            -
          • - - - - - - - - - - - - 子结点 0-0-0-0 - - -
          • -
          • - - - - - - - - - - - - 子结点 0-0-0-1 - - -
          • -
          -
          -
        • -
        -
        -
      • -
      • - - - - - - - - - - - - 父结点 0-1 - - - -
          -
        • - - - - - - - - - - - - 子结点 0-1-0 - - -
        • -
        • - - - - - - - - - - - - 子结点 0-1-1 - - -
        • -
        • - - - - - - - - - - - - 子结点 0-1-2 - - -
        • -
        -
        -
      • -
      • - - - - - - - - - - - - 父结点 0-2 - - - -
          -
        • - - - - - - - - - - - - 父结点 0-2-0 - - -
        • -
        • - - - - - - - - - - - - 父结点 0-2-1 - - - -
            -
          • - - - - - - - - - - - - 子结点 0-2-1-0 - - - -
              -
            • - - - - - - - - - - - - 子结点 0-2-1-0-0 - - -
            • -
            • - - - - - - - - - - - - 子结点 0-2-1-0-1 - - -
            • -
            -
            -
          • -
          • - - - - - - - - - - - - 子结点 0-2-1-1 - - -
          • -
          -
          -
        • -
        • - - - - - - - - - - - - 子结点 0-2-2 - - -
        • -
        -
        -
      • -
      -
      -
    • -
    -`; - -exports[`Tree renders basic Tree with defaultExpandAll correctly 1`] = ` -
      -
    • - - - - 根结点1 - - - -
        -
      • - - - - 父结点 0-0 - - - -
          -
        • - - - - 父结点 0-0-0 - - - -
            -
          • - - - - 子结点 0-0-0-0 - - -
          • -
          • - - - - 子结点 0-0-0-1 - - -
          • -
          -
          -
        • -
        -
        -
      • -
      • - - - - 父结点 0-1 - - - -
          -
        • - - - - 子结点 0-1-0 - - -
        • -
        • - - - - 子结点 0-1-1 - - -
        • -
        • - - - - 子结点 0-1-2 - - -
        • -
        -
        -
      • -
      • - - - - 父结点 0-2 - - - -
          -
        • - - - - 父结点 0-2-0 - - -
        • -
        • - - - - 父结点 0-2-1 - - - -
            -
          • - - - - 子结点 0-2-1-0 - - - -
              -
            • - - - - 子结点 0-2-1-0-0 - - -
            • -
            • - - - - 子结点 0-2-1-0-1 - - -
            • -
            -
            -
          • -
          • - - - - 子结点 0-2-1-1 - - -
          • -
          -
          -
        • -
        • - - - - 子结点 0-2-2 - - -
        • -
        -
        -
      • -
      -
      -
    • -
    -`; - -exports[`Tree renders basic Tree with defaultExpandAll correctly 2`] = ` -
      -
    • - - - - 根结点1 - - - -
        -
      • - - - - 父结点 0-0 - - - -
          -
        • - - - - 父结点 0-0-0 - - - -
            -
          • - - - - 子结点 0-0-0-0 - - -
          • -
          • - - - - 子结点 0-0-0-1 - - -
          • -
          -
          -
        • -
        -
        -
      • -
      • - - - - 父结点 0-1 - - - -
          -
        • - - - - 子结点 0-1-0 - - -
        • -
        • - - - - 子结点 0-1-1 - - -
        • -
        • - - - - 子结点 0-1-2 - - -
        • -
        -
        -
      • -
      • - - - - 父结点 0-2 - - - -
          -
        • - - - - 父结点 0-2-0 - - -
        • -
        • - - - - 父结点 0-2-1 - - - -
            -
          • - - - - 子结点 0-2-1-0 - - - -
              -
            • - - - - 子结点 0-2-1-0-0 - - -
            • -
            • - - - - 子结点 0-2-1-0-1 - - -
            • -
            -
            -
          • -
          • - - - - 子结点 0-2-1-1 - - -
          • -
          -
          -
        • -
        • - - - - 子结点 0-2-2 - - -
        • -
        -
        -
      • -
      -
      -
    • -
    -`; - -exports[`Tree renders basic Tree with defaultExpandAll, canCheck correctly 1`] = ` -
      -
    • - - - - - - - - - - - - 根结点1 - - - -
        -
      • - - - - - - - - - - - - 父结点 0-0 - - - -
          -
        • - - - - - - - - - - - - 父结点 0-0-0 - - - -
            -
          • - - - - - - - - - - - - 子结点 0-0-0-0 - - -
          • -
          • - - - - - - - - - - - - 子结点 0-0-0-1 - - -
          • -
          -
          -
        • -
        -
        -
      • -
      • - - - - - - - - - - - - 父结点 0-1 - - - -
          -
        • - - - - - - - - - - - - 子结点 0-1-0 - - -
        • -
        • - - - - - - - - - - - - 子结点 0-1-1 - - -
        • -
        • - - - - - - - - - - - - 子结点 0-1-2 - - -
        • -
        -
        -
      • -
      • - - - - - - - - - - - - 父结点 0-2 - - - -
          -
        • - - - - - - - - - - - - 父结点 0-2-0 - - -
        • -
        • - - - - - - - - - - - - 父结点 0-2-1 - - - -
            -
          • - - - - - - - - - - - - 子结点 0-2-1-0 - - - -
              -
            • - - - - - - - - - - - - 子结点 0-2-1-0-0 - - -
            • -
            • - - - - - - - - - - - - 子结点 0-2-1-0-1 - - -
            • -
            -
            -
          • -
          • - - - - - - - - - - - - 子结点 0-2-1-1 - - -
          • -
          -
          -
        • -
        • - - - - - - - - - - - - 子结点 0-2-2 - - -
        • -
        -
        -
      • -
      -
      -
    • -
    -`; - -exports[`Tree renders basic Tree with defaultExpandAll, canCheck, checkedKeys correctly 1`] = ` -
      -
    • - - - - - - - - - - - - 根结点1 - - - -
        -
      • - - - - - - - - - - - - 父结点 0-0 - - - -
          -
        • - - - - - - - - - - - - 父结点 0-0-0 - - - -
            -
          • - - - - - - - - - - - - 子结点 0-0-0-0 - - -
          • -
          • - - - - - - - - - - - - 子结点 0-0-0-1 - - -
          • -
          -
          -
        • -
        -
        -
      • -
      • - - - - - - - - - - - - 父结点 0-1 - - - -
          -
        • - - - - - - - - - - - - 子结点 0-1-0 - - -
        • -
        • - - - - - - - - - - - - 子结点 0-1-1 - - -
        • -
        • - - - - - - - - - - - - 子结点 0-1-2 - - -
        • -
        -
        -
      • -
      • - - - - - - - - - - - - 父结点 0-2 - - - -
          -
        • - - - - - - - - - - - - 父结点 0-2-0 - - -
        • -
        • - - - - - - - - - - - - 父结点 0-2-1 - - - -
            -
          • - - - - - - - - - - - - 子结点 0-2-1-0 - - - -
              -
            • - - - - - - - - - - - - 子结点 0-2-1-0-0 - - -
            • -
            • - - - - - - - - - - - - 子结点 0-2-1-0-1 - - -
            • -
            -
            -
          • -
          • - - - - - - - - - - - - 子结点 0-2-1-1 - - -
          • -
          -
          -
        • -
        • - - - - - - - - - - - - 子结点 0-2-2 - - -
        • -
        -
        -
      • -
      -
      -
    • -
    -`; - -exports[`Tree renders basic Tree with expandedKeys correctly 1`] = ` -
      -
    • - - - - 根结点1 - - - -
        -
      • - - - - 父结点 0-0 - - - -
          -
        • - - - - 父结点 0-0-0 - - - -
            -
          • - - - - 子结点 0-0-0-0 - - -
          • -
          • - - - - 子结点 0-0-0-1 - - -
          • -
          -
          -
        • -
        -
        -
      • -
      • - - - - 父结点 0-1 - - - -
      • -
      • - - - - 父结点 0-2 - - - -
      • -
      -
      -
    • -
    -`; - -exports[`Tree renders basic Tree with expandedKeys, canCheck correctly 1`] = ` -
      -
    • - - - - - - - - - - - - 根结点1 - - - -
        -
      • - - - - - - - - - - - - 父结点 0-0 - - - -
          -
        • - - - - - - - - - - - - 父结点 0-0-0 - - - -
            -
          • - - - - - - - - - - - - 子结点 0-0-0-0 - - -
          • -
          • - - - - - - - - - - - - 子结点 0-0-0-1 - - -
          • -
          -
          -
        • -
        -
        -
      • -
      • - - - - - - - - - - - - 父结点 0-1 - - - -
      • -
      • - - - - - - - - - - - - 父结点 0-2 - - - -
      • -
      -
      -
    • -
    -`; - -exports[`Tree renders basic Tree with expandedKeys, defaultExpandAll, canCheck correctly 1`] = ` -
      -
    • - - - - - - - - - - - - 根结点1 - - - -
        -
      • - - - - - - - - - - - - 父结点 0-0 - - - -
          -
        • - - - - - - - - - - - - 父结点 0-0-0 - - - -
            -
          • - - - - - - - - - - - - 子结点 0-0-0-0 - - -
          • -
          • - - - - - - - - - - - - 子结点 0-0-0-1 - - -
          • -
          -
          -
        • -
        -
        -
      • -
      • - - - - - - - - - - - - 父结点 0-1 - - - -
          -
        • - - - - - - - - - - - - 子结点 0-1-0 - - -
        • -
        • - - - - - - - - - - - - 子结点 0-1-1 - - -
        • -
        • - - - - - - - - - - - - 子结点 0-1-2 - - -
        • -
        -
        -
      • -
      • - - - - - - - - - - - - 父结点 0-2 - - - -
          -
        • - - - - - - - - - - - - 父结点 0-2-0 - - -
        • -
        • - - - - - - - - - - - - 父结点 0-2-1 - - - -
            -
          • - - - - - - - - - - - - 子结点 0-2-1-0 - - - -
              -
            • - - - - - - - - - - - - 子结点 0-2-1-0-0 - - -
            • -
            • - - - - - - - - - - - - 子结点 0-2-1-0-1 - - -
            • -
            -
            -
          • -
          • - - - - - - - - - - - - 子结点 0-2-1-1 - - -
          • -
          -
          -
        • -
        • - - - - - - - - - - - - 子结点 0-2-2 - - -
        • -
        -
        -
      • -
      -
      -
    • -
    -`; - -exports[`Tree renders basic Tree with expandedKeys, defaultExpandAll, canCheck, checkedKeys correctly 1`] = ` -
      -
    • - - - - - - - - - - - - 根结点1 - - - -
        -
      • - - - - - - - - - - - - 父结点 0-0 - - - -
          -
        • - - - - - - - - - - - - 父结点 0-0-0 - - - -
            -
          • - - - - - - - - - - - - 子结点 0-0-0-0 - - -
          • -
          • - - - - - - - - - - - - 子结点 0-0-0-1 - - -
          • -
          -
          -
        • -
        -
        -
      • -
      • - - - - - - - - - - - - 父结点 0-1 - - - -
          -
        • - - - - - - - - - - - - 子结点 0-1-0 - - -
        • -
        • - - - - - - - - - - - - 子结点 0-1-1 - - -
        • -
        • - - - - - - - - - - - - 子结点 0-1-2 - - -
        • -
        -
        -
      • -
      • - - - - - - - - - - - - 父结点 0-2 - - - -
          -
        • - - - - - - - - - - - - 父结点 0-2-0 - - -
        • -
        • - - - - - - - - - - - - 父结点 0-2-1 - - - -
            -
          • - - - - - - - - - - - - 子结点 0-2-1-0 - - - -
              -
            • - - - - - - - - - - - - 子结点 0-2-1-0-0 - - -
            • -
            • - - - - - - - - - - - - 子结点 0-2-1-0-1 - - -
            • -
            -
            -
          • -
          • - - - - - - - - - - - - 子结点 0-2-1-1 - - -
          • -
          -
          -
        • -
        • - - - - - - - - - - - - 子结点 0-2-2 - - -
        • -
        -
        -
      • -
      -
      -
    • -
    -`; - -exports[`Tree renders basic Tree with not completely TreeNodes children 1`] = ` -
      -
    • - - - - - - - - - - - - parent 1 - - - -
        -
      • - - - - - - - - - - - - parent 1-0 - - - -
          -
        • - - - - - - - - - - - - leaf - - -
        • -
        • - - - - - - - - - - - - leaf - - -
        • -
        -
        -
      • -
      • - - - - - - - - - - - - parent 1-1 - - - -
          -
        • - - - - - - - - - - - - - sss - - - -
        • -
        -
        -
      • -
      -
      -
    • -
    -`; diff --git a/components/tree/__tests__/index.test.jsx b/components/tree/__tests__/index.test.jsx index a4e922ff..218e7098 100644 --- a/components/tree/__tests__/index.test.jsx +++ b/components/tree/__tests__/index.test.jsx @@ -2,6 +2,7 @@ import React from 'react'; import { render, mount } from 'enzyme'; import toJson from 'enzyme-to-json'; import Tree from '../index'; +import Icon from '../../icon/index'; const treeData = [ { @@ -31,8 +32,8 @@ const treeData = [ children: [ { keys: '0-1-0', title: '子结点 0-1-0', checkDisabled: true }, - { keys: '0-1-1', title: '子结点 0-1-1' }, - { keys: '0-1-2', title: '子结点 0-1-2' }, + { keys: '0-1-1', title: '子结点 0-1-1', selectDisabled: true }, + { keys: '0-1-2', title: '子结点 0-1-2', disabled: true }, ], }, { @@ -51,8 +52,8 @@ const treeData = [ title: '子结点 0-2-1-0', children: [ - { keys: '0-2-1-0-0', title: '子结点 0-2-1-0-0' }, - { keys: '0-2-1-0-1', title: '子结点 0-2-1-0-1' }, + { keys: '0-2-1-0-0', title: '子结点 0-2-1-0-0', checkable: false, isLeaf: true }, + { keys: '0-2-1-0-1', title: '子结点 0-2-1-0-1', icon: }, ], }, { keys: '0-2-1-1', title: '子结点 0-2-1-1' }, @@ -68,6 +69,7 @@ const checkedKeys = ['0-0-0-0', '0-2-1-1', '0-1-0']; const checkedKeys2 = ['0-0-0', '0-1']; const expandedKeys = ['0-0-0']; const expandedKeys2 = ['0-1']; +const selectedKeys = ['0-0-0']; const { TreeNode } = Tree; describe('Tree', () => { @@ -85,9 +87,9 @@ describe('Tree', () => { expect(toJson(wrapper)).toMatchSnapshot(); }); - it('renders basic Tree with defaultExpandAll canCheck correctly', () => { + it('renders basic Tree with defaultExpandAll checkable correctly', () => { const wrapper = render( - , + , ); expect(toJson(wrapper)).toMatchSnapshot(); }); @@ -99,58 +101,79 @@ describe('Tree', () => { expect(toJson(wrapper)).toMatchSnapshot(); }); - it('renders basic Tree with defaultExpandAll, canCheck correctly', () => { + it('renders basic Tree with defaultExpandAll, checkable correctly', () => { const wrapper = render( - , + , ); expect(toJson(wrapper)).toMatchSnapshot(); }); - it('renders basic Tree with defaultExpandAll, canCheck, checkedKeys correctly', () => { + it('renders basic Tree with defaultExpandAll, checkable, checkedKeys correctly', () => { const wrapper = render( - , + , ); expect(toJson(wrapper)).toMatchSnapshot(); }); - it('renders basic Tree with expandedKeys correctly', () => { + it('renders basic Tree with expandedKeys, autoExpandParent correctly', () => { const wrapper = render( - , + , ); expect(toJson(wrapper)).toMatchSnapshot(); }); - it('renders basic Tree with expandedKeys, canCheck correctly', () => { + it('renders basic Tree with expandedKeys, checkable correctly', () => { const wrapper = render( - , + , ); expect(toJson(wrapper)).toMatchSnapshot(); }); - it('renders basic Tree with expandedKeys, defaultExpandAll, canCheck correctly', () => { + it('renders basic Tree with expandedKeys, defaultExpandAll, checkable correctly', () => { const wrapper = render( - , + , ); expect(toJson(wrapper)).toMatchSnapshot(); }); - it('renders basic Tree with expandedKeys, defaultExpandAll, canCheck, checkedKeys correctly', () => { + it('renders basic Tree with expandedKeys, defaultExpandAll, checkable, checkedKeys correctly', () => { const wrapper = render( - , + , ); expect(toJson(wrapper)).toMatchSnapshot(); }); - it('renders basic Tree with TreeNode children', () => { + it('renders basic Tree with multiple, selectable', () => { const wrapper = render( - , + , + ); + expect(toJson(wrapper)).toMatchSnapshot(); + }); + + it('renders basic Tree with disabled', () => { + const wrapper = render( + , + ); + expect(toJson(wrapper)).toMatchSnapshot(); + }); + + it('renders basic Tree with showLine, showIcon, icon', () => { + const wrapper = render( + } defaultExpandAll />, + ); + expect(toJson(wrapper)).toMatchSnapshot(); + }); + + it('renders basic Tree with showLine, switcherIcon', () => { + const wrapper = render( + } defaultExpandAll />, ); expect(toJson(wrapper)).toMatchSnapshot(); }); it('renders basic Tree with TreeNodes children', () => { const wrapper = render( - + @@ -167,7 +190,7 @@ describe('Tree', () => { it('renders basic Tree with not completely TreeNodes children', () => { const wrapper = render( - +

    invalid element here invalid element here

    @@ -197,7 +220,7 @@ describe('Tree', () => { it('behave correctly when change treeNode checked status', () => { const onCheck = jest.fn(); const wrapper = mount( - , + , ); expect(wrapper.state('checkedKeys')).toEqual(expect.arrayContaining(['0-2-1-1', '0-0-0-0', '0-1-0'])); expect(wrapper.state('halfCheckedKeys')).toEqual(expect.arrayContaining(['0', '0-2-1', '0-2'])); @@ -217,14 +240,26 @@ describe('Tree', () => { it('behave correctly when expand treeNode', () => { const onExpand = jest.fn(); const wrapper = mount( - , + , ); expect(wrapper.state('expandedKeys')).toEqual(expect.arrayContaining(['0', '0-0-0', '0-0'])); - wrapper.find('li[data-keys="0-0-0"] .ui-tree-switcher').at(0).simulate('click'); + wrapper.find('li[data-keys="0-0-0"] .zw-tree-switcher').at(0).simulate('click'); expect(onExpand).toBeCalled(); - wrapper.find('li[data-keys="0-1"] .ui-tree-switcher').at(0).simulate('click'); + wrapper.find('li[data-keys="0-1"] .zw-tree-switcher').at(0).simulate('click'); expect(onExpand).toBeCalled(); }); + + it('behave correctly when select treeNode', () => { + const onSelect = jest.fn(); + const wrapper = mount( + , + ); + expect(wrapper.state('selectedKeys')).toEqual(expect.arrayContaining(['0-0-0'])); + + wrapper.find('li[data-keys="0-0-0"] .zw-tree-node-content-wrapper').at(0).simulate('click'); + expect(onSelect).toBeCalled(); + expect(wrapper.state('selectedKeys')).toEqual(expect.arrayContaining([])); + }); }); diff --git a/components/tree/style/component.scss b/components/tree/style/component.scss index 21211bf7..9cc60b77 100644 --- a/components/tree/style/component.scss +++ b/components/tree/style/component.scss @@ -1,16 +1,52 @@ -$treePrefixCls: ui-tree; +$prefixCls: zw; +$treePrefixCls: zw-tree; +$tree-node-switcher-width: 20px; +$tree-node-height: 20px; +$tree-node-line-height: 20px; +$tree-node-checkbox-width: 20px; .#{$treePrefixCls} { margin: 0; - padding: 5px; + padding: 0; - li { - padding: 3px 0; + li { + position: relative; + padding: 5px 0; margin: 0; list-style: none; white-space: nowrap; outline: 0; + & > div { + display: inline-flex; + &:focus { + box-shadow:0px 0px 2px 2px rgba(18,194,135,0.1); + border-radius:2px; + } + &.#{$treePrefixCls}-treenode-disabled { + span.#{$treePrefixCls}-node-content-wrapper { + color: #bcbcbc; + cursor: not-allowed; + &:hover { + background-color: transparent; + color: #bcbcbc; + } + + .#{$prefixCls}-icon { + color: #bcbcbc; + } + } + } + } + + &:first-child { + padding-top: 10px; + } + + &:last-child { + padding-bottom: 0; + } + .collapse { overflow: hidden; display: block; @@ -25,81 +61,141 @@ $treePrefixCls: ui-tree; padding: 0 0 0 18px; } - .#{$treePrefixCls}-node-content-wrapper { - display: inline-block; - cursor: pointer; - text-decoration: none; - vertical-align: top; - } - span { &.#{$treePrefixCls}-switcher { - width: 24px; - height: 24px; - line-height: 24px; + width: $tree-node-switcher-width; + height: $tree-node-height; + line-height: $tree-node-line-height; display: inline-block; - vertical-align: top; cursor: pointer; - + text-align: center; + vertical-align: middle; + &.#{$treePrefixCls}-switcher-open, &.#{$treePrefixCls}-switcher-close { - &:after { - width: 0; - height: 0; - content: ''; - display: inline-block; - border: 4px solid #000; - border-color: rgba(0, 0, 0, 0.65) transparent transparent; - transition: transform .3s; + .#{$treePrefixCls}-switcher-icon { + i { + font-size: 12px; + transition: transform .3s; + } } } - &.#{$treePrefixCls}-switcher-open:after { - transform: translate(8px, 0) rotate(0deg); - } - - &.#{$treePrefixCls}-switcher-close:after { - transform: translate(10px, -2px) rotate(-90deg); + &.#{$treePrefixCls}-switcher-open .#{$treePrefixCls}-switcher-icon i { + transform: rotate(90deg); } &.#{$treePrefixCls}-switcher-noop { cursor: default; } + + .#{$treePrefixCls}-switcher-line-icon i { + vertical-align: 1px; + } + + .#{$treePrefixCls}-switcher-icon i { + vertical-align: 2px; + } } &.#{$treePrefixCls}-checkbox { - width: 20px; - height: 24px; - line-height: 24px; + width: $tree-node-checkbox-width; + line-height: $tree-node-line-height; display: inline-block; - vertical-align: top; cursor: pointer; text-align: center; + vertical-align: middle; + + & > span { + vertical-align: 1px; + } } &.#{$treePrefixCls}-node-content-wrapper { - line-height: 24px; - height: 24px; + padding: 0 4px; display: inline-block; - color: rgba(0, 0, 0, 0.65); + line-height: $tree-node-line-height; font-size: 12px; - } - } + color: rgba(0, 0, 0, 0.65); + cursor: pointer; + text-decoration: none; + transition: all .3s; + border-radius: 1px; + vertical-align: middle; + + &.#{$treePrefixCls}-node-selected { + background-color: var(--theme-primary); + color: #fff; + + .#{$treePrefixCls}-node-content-icon { + i { + color:#fff; + } + } + } + &:not(.#{$treePrefixCls}-node-selected):hover { + background-color:rgba(18,194,135,0.1); + color: #12C287; + border-radius: 1px; + } + .#{$treePrefixCls}-node-content-icon { + margin-right: 2px; - &-child-tree { - display: none; + i { + vertical-align: baseline; + } + } + } - &-open { - display: block; + &:not(.#{$treePrefixCls}-node-content-icon) { + > i.#{$prefixCls}-icon { + color: rgba(0,0,0,.7); + } + } + + i.#{$prefixCls}-icon { + svg { + vertical-align: middle; + } } } - &-treenode-disabled { + .#{$treePrefixCls}-node-disabled { > span:not(.#{$treePrefixCls}-switcher), > a, - > a span { - color: #767676; + > span { + color: #bcbcbc; cursor: not-allowed; + background: none; + } + } + } + + &.#{$treePrefixCls}-show-line { + li { + .#{$treePrefixCls}-switcher-open { + .#{$treePrefixCls}-switcher-line-icon { + width: $tree-node-switcher-width; + height: $tree-node-height; + } + .#{$treePrefixCls}-switcher-icon { + width: $tree-node-switcher-width; + height: $tree-node-height; + } + } + &:not(:last-child) { + &:before { + position: absolute; + top: 23px; + left: 9px; + bottom: -8px; + border-right: 1px solid #ccc; + content: ""; + } + + &:first-child:before { + top: 29px; + } } } } diff --git a/components/tree/style/tree.scss b/components/tree/style/tree.scss index 8c404bbf..846a9b75 100644 --- a/components/tree/style/tree.scss +++ b/components/tree/style/tree.scss @@ -38,7 +38,6 @@ $treePrefixCls: ui-tree; height: 24px; line-height: 24px; display: inline-block; - vertical-align: top; cursor: pointer; &.#{$treePrefixCls}-switcher-open, @@ -55,11 +54,11 @@ $treePrefixCls: ui-tree; } &.#{$treePrefixCls}-switcher-open:after { - transform: translate(8px, 0) rotate(0deg); + transform: translate(4px, 2px) rotate(0deg); } &.#{$treePrefixCls}-switcher-close:after { - transform: translate(10px, -2px) rotate(-90deg); + transform: translate(6px, 0) rotate(-90deg); } &.#{$treePrefixCls}-switcher-noop { diff --git a/components/tree/tree.md b/components/tree/tree.md index 43ea5609..9be0502f 100644 --- a/components/tree/tree.md +++ b/components/tree/tree.md @@ -1,243 +1,497 @@ -## Tree +# Tree 树形组件 -### 基本用法1 -可默认展开全部,设置treeData props方式展示节点 +## 基本用法1 +展示可勾选,可选中节点或多选节点,禁用单个节点或其checkbox,默认展开等功能。 -:::demo +```jsx +import { Tree } from 'zarm-web'; -```js +class Demo1 extends React.Component { + state = { + treeData: [ + { + keys: '0', + title: '根结点1', + children: + [ + { + keys: '0-0', + title: '父结点 0-0', + disabled: true, + children: + [ + { + keys: '0-0-0', + title: '父结点 0-0-0', + checkDisabled: true, + children: + [ + { keys: '0-0-0-0', title: '子结点 0-0-0-0' }, + { keys: '0-0-0-1', title: '子结点 0-0-0-1' }, + ], + }, + ], + }, + { + keys: '0-1', + title: '父结点 0-1', + children: + [ + { keys: '0-1-0', title: '子结点 0-1-0' }, + { keys: '0-1-1', title: '子结点 0-1-1' }, + { keys: '0-1-2', title: '子结点 0-1-2' }, + ], + }, + ], + }, + ], + }; - constructor(props) { - super(props); - this.state = { - treeData: [ + + onSelect = (selectedKeys, selectedInfo) => { + console.log('onSelect', selectedKeys, selectedInfo); + }; + + onCheck = (checkedMap, checkedInfo) => { + console.log('onCheck', checkedMap, checkedInfo); + }; + + render() { + const { treeData, checkedKeys } = this.state; + return ( +
    + +
    + ); + } +} +ReactDOM.render(, mountNode); +``` + +## 受控示例 +通过selectedKeys,checkedKeys, expandedKeys属性受控操作示例 + +```jsx +import { Tree } from 'zarm-web'; + +class Demo2 extends React.Component { + state = { + checkedKeys: ['0-1-0', '0-1-1'], + selectedKeys: ['0-1-0'], + expandedKeys: ['0-1'], + autoExpandParent: true, + treeData: [ + { + keys: '0', + title: '根结点1', + children: + [ + { + keys: '0-0', + title: '父结点 0-0', + disabled: true, + children: + [ { - keys: '0', - title: '根结点1', + keys: '0-0-0', + title: '父结点 0-0-0', + checkDisabled: true, children: [ - { - keys: '0-0', - title: '父结点 0-0', - children: - [ - { - keys: '0-0-0', - title: '父结点 0-0-0', - children: - [ - { keys: '0-0-0-0', title: '子结点 0-0-0-0' }, - { keys: '0-0-0-1', title: '子结点 0-0-0-1' }, - ], - }, - ], - }, - { - keys: '0-1', - title: '父结点 0-1', - children: - [ - { keys: '0-1-0', title: '子结点 0-1-0'}, - { keys: '0-1-1', title: '子结点 0-1-1' }, - { keys: '0-1-2', title: '子结点 0-1-2' }, - ], - }, - { - keys: '0-2', - title: '父结点 0-2', - children: - [ - { keys: '0-2-0', title: '父结点 0-2-0' }, - { - keys: '0-2-1', - title: '父结点 0-2-1', - children: - [ - { - keys: '0-2-1-0', - title: '子结点 0-2-1-0', - children: - [ - { keys: '0-2-1-0-0', title: '子结点 0-2-1-0-0' }, - { keys: '0-2-1-0-1', title: '子结点 0-2-1-0-1' }, - ], - }, - { keys: '0-2-1-1', title: '子结点 0-2-1-1' }, - ], - }, - { keys: '0-2-2', title: '子结点 0-2-2' }, - ], - }, + { keys: '0-0-0-0', title: '子结点 0-0-0-0' }, + { keys: '0-0-0-1', title: '子结点 0-0-0-1' }, ], }, - ] - }; + ], + }, + { + keys: '0-1', + title: '父结点 0-1', + children: + [ + { keys: '0-1-0', title: '子结点 0-1-0' }, + { keys: '0-1-1', title: '子结点 0-1-1' }, + { keys: '0-1-2', title: '子结点 0-1-2' }, + ], + }, + ], + }, + ], + }; + + onCheck = (checkedMap, checkedInfo) => { + this.setState({ + checkedKeys: checkedMap.checkedKeys, + }); + console.log('onCheck', checkedMap, checkedInfo); + }; + + onSelect = (selectedKeys, selectedInfo) => { + this.setState({ + selectedKeys, + }); + console.log('onSelect', selectedKeys, selectedInfo); + }; + + onExpand = (expandedKeys, expandedObj) => { + this.setState({ + autoExpandParent: false, + expandedKeys, + }); + console.log('onExpand', expandedKeys, expandedObj); + }; + + render() { + const { treeData, checkedKeys, selectedKeys, expandedKeys, autoExpandParent } = this.state; + return ( +
    + +
    + ); } +} + +ReactDOM.render(, mountNode); + +``` + +## 连接线形式的树 +子节点之间带连接线的树,常用于文件目录结构展示。并且showLine为true的模式下,可以用switcherIcon修改默认图标(即父节点的展开图标)。 + +```jsx + +import { Tree, Switch, Icon } from 'zarm-web'; + +const MyIcon = Icon.createFromIconfont('//at.alicdn.com/t/font_1733827_4scbzsuv5v2.js'); + +class Demo3 extends React.Component { + state = { + showLine: false, + treeData: [ + { + keys: '0', + title: '根结点1', + children: + [ + { + keys: '0-0', + title: '父结点 0-0', + disabled: true, + children: + [ + { + keys: '0-0-0', + title: '父结点 0-0-0', + checkDisabled: true, + children: + [ + { keys: '0-0-0-0', title: '子结点 0-0-0-0' }, + { keys: '0-0-0-1', title: '子结点 0-0-0-1' }, + ], + }, + ], + }, + { + keys: '0-1', + title: '父结点 0-1', + children: + [ + { keys: '0-1-0', title: '子结点 0-1-0' }, + { keys: '0-1-1', title: '子结点 0-1-1' }, + { keys: '0-1-2', title: '子结点 0-1-2' }, + ], + }, + ], + }, + ], + }; + + setShowLine = (showLine) => { + this.setState({ + showLine, + }); + }; + render() { - const { treeData } = this.state; + const { treeData, showLine } = this.state; return ( -
    - +
    +
    + showLine && change switcherIcon: +
    + } + />
    - ) + ); } +} + +ReactDOM.render(, mountNode); + ``` -::: -### 基本用法2 -可选中,可设置默认展开节点, 可禁用子节点的选中状态 +## 可搜索 +可搜索的树。 -:::demo +```jsx -```js +import { Tree, Input, Icon } from 'zarm-web'; +const TData = [ + { + keys: '0', + title: '根结点1', + children: + [ + { + keys: '0-0', + title: '父结点 0-0', + children: + [ + { + keys: '0-0-0', + title: '父结点 0-0-0', + children: + [ + { keys: '0-0-0-0', title: '子结点 0-0-0-0', checkDisabled: true }, + { keys: '0-0-0-1', title: '子结点 0-0-0-1' }, + ], + }, + ], + }, + { + keys: '0-1', + title: '父结点 0-1', + children: + [ + { keys: '0-1-0', title: '子结点 0-1-0', checkDisabled: true }, + { keys: '0-1-1', title: '子结点 0-1-1' }, + { keys: '0-1-2', title: '子结点 0-1-2' }, + ], + }, + { + keys: '0-2', + title: '父结点 0-2', + children: + [ + { keys: '0-2-0', title: '父结点 0-2-0' }, + { + keys: '0-2-1', + title: '父结点 0-2-1', + children: + [ + { + keys: '0-2-1-0', + title: '子结点 0-2-1-0', + children: + [ + { keys: '0-2-1-0-0', title: '子结点 0-2-1-0-0' }, + { keys: '0-2-1-0-1', title: '子结点 0-2-1-0-1' }, + ], + }, + { keys: '0-2-1-1', title: '子结点 0-2-1-1' }, + ], + }, + { keys: '0-2-2', title: '子结点 0-2-2' }, + ], + }, + ], + }, +]; +class SearchTree extends React.Component { constructor(props) { super(props); this.state = { + autoExpandParent: true, expandedKeys: ['0-0-0'], checkedKeys: ['0-0-0'], - treeData: [ - { - keys: '0', - title: '根结点1', - children: - [ - { - keys: '0-0', - title: '父结点 0-0', - children: - [ - { - keys: '0-0-0', - title: '父结点 0-0-0', - children: - [ - { keys: '0-0-0-0', title: '子结点 0-0-0-0', checkDisabled: true }, - { keys: '0-0-0-1', title: '子结点 0-0-0-1' }, - ], - }, - ], - }, - { - keys: '0-1', - title: '父结点 0-1', - children: - [ - { keys: '0-1-0', title: '子结点 0-1-0', checkDisabled: true }, - { keys: '0-1-1', title: '子结点 0-1-1' }, - { keys: '0-1-2', title: '子结点 0-1-2' }, - ], - }, - { - keys: '0-2', - title: '父结点 0-2', - children: - [ - { keys: '0-2-0', title: '父结点 0-2-0' }, - { - keys: '0-2-1', - title: '父结点 0-2-1', - children: - [ - { - keys: '0-2-1-0', - title: '子结点 0-2-1-0', - children: - [ - { keys: '0-2-1-0-0', title: '子结点 0-2-1-0-0' }, - { keys: '0-2-1-0-1', title: '子结点 0-2-1-0-1' }, - ], - }, - { keys: '0-2-1-1', title: '子结点 0-2-1-1' }, - ], - }, - { keys: '0-2-2', title: '子结点 0-2-2' }, - ], - }, - ], - }, - ] + treeData: TData, }; + this.filterSearchExpandedKeysMap = {}; } + + filterTreeDataWithHighlight = (TreeArray, searchValue, parentNode) => { + // 过滤出来非叶子结点以及匹配到关键字的结点 + return (TreeArray || []).map((item) => { + const { children, title, keys, ...others } = item; + const isLeafNode = (children || []).length === 0; + const searchedIndex = title.indexOf(searchValue); + let titleNew = title; + if (searchedIndex > -1) { + const beforeStr = title.substr(0, searchedIndex); + const afterStr = title.substr(searchedIndex + searchValue.length); + titleNew = ( + + {beforeStr} + {searchValue} + {afterStr} + + ); + if (parentNode) { + this.filterSearchExpandedKeysMap[parentNode.keys] = true; + } + } + if (!isLeafNode) { + return { title: titleNew, keys, children: this.filterTreeDataWithHighlight(children, searchValue, item), ...others }; + } + return { title: titleNew, keys, ...others }; + }); + }; + + onChangeSearchText = (e) => { + const { value: searchValue } = e.target; + this.filterSearchExpandedKeysMap = {}; + const treeDataWithHighlight = this.filterTreeDataWithHighlight(TData, searchValue); + this.setState({ + treeData: treeDataWithHighlight, + expandedKeys: Object.keys(this.filterSearchExpandedKeysMap), + autoExpandParent: true, + }); + }; + render() { - const { expandedKeys, checkedKeys, treeData } = this.state; - console.log("checkedKeys",checkedKeys) + const { expandedKeys, checkedKeys, treeData, autoExpandParent } = this.state; return (
    - + } onChange={this.onChangeSearchText} /> +
    - ) + ); } +} + +ReactDOM.render(, mountNode); + ``` -::: -### 基本用法3 -通过手写TreeNode结构来渲染树(会过滤非TreeNode节点) -:::demo -```js - constructor(props) { - super(props); - this.state = { - expandedKeys: ['0-1'], - checkedKeys: ['0-0-0', '0-1'], - } - } - - render() { - const { expandedKeys, checkedKeys } = this.state; - const { TreeNode } = Tree; +## 自定义图标 +可以通过icon设置树的节点图标,也可以针对单个不同的节点定制图标。 + +```jsx + +import { Tree, Icon } from 'zarm-web'; + +const MyIcon = Icon.createFromIconfont('//at.alicdn.com/t/font_1733827_4scbzsuv5v2.js'); + +class Demo5 extends React.Component { + state = { + treeData: [ + { + keys: '0', + title: '根结点1', + children: + [ + { + keys: '0-0', + title: '父结点 0-0', + disabled: true, + children: + [ + { + keys: '0-0-0', + title: '父结点 0-0-0', + checkDisabled: true, + children: + [ + { keys: '0-0-0-0', title: '子结点 0-0-0-0', icon: }, + { keys: '0-0-0-1', title: '子结点 0-0-0-1' }, + ], + }, + ], + }, + { + keys: '0-1', + title: '父结点 0-1', + children: + [ + { keys: '0-1-0', title: '子结点 0-1-0' }, + { keys: '0-1-1', title: '子结点 0-1-1' }, + { keys: '0-1-2', title: '子结点 0-1-2' }, + ], + }, + ], + }, + ], + }; + + render() { + const { treeData } = this.state; return ( - - -

    invalid element here invalid element here

    - - -
    invalid element here invalid element here
    - - - - sss} keys="0-1-0" /> - invalid element here invalid element here - - -
    invalid element here invalid element here -

    invalid element here invalid element here - - invalid element here invalid element here - -

    -
    - invalid element here invalid element here - invalid element here invalid element here -
    - ) - } +
    + } + treeData={treeData} + defaultExpandAll + checkable + /> +
    + ); + } +} + +ReactDOM.render(, mountNode); + ``` -::: -### Tree Attributes +## Tree Attributes | 参数 | 说明 | 类型 | 可选值 | 默认值 | |---------- |-------- |---------- |------------- |-------- | | treeData | 树节点结构数组 | array | - | [] | -| canCheck | 节点前添加Checkbox 复选框 | boolean | true, false | false | +| checkable | 节点前添加Checkbox 复选框 | boolean | true, false | false | | checkedKeys | 选中复选框的树节点数组 | array | - | [] | | expandedKeys | 展开指定的树节点 | array | - | [] | +| selectedKeys | 设置选中的树节点 | array | - | [] | | defaultExpandAll | 默认展开所有树节点 | boolean | true, false | false | +| autoExpandParent | 是否自动展开父节点 | boolean | true, false | true | +| showLine | 是否展示连接线 | boolean | true, false | false | +| showIcon | 是否节点title前的图标,如设置为true,需要icon属性自行定义图标相关样式 | boolean | true, false | false | +| icon | 自定义节点title前面的图标 | ReactNode | - | - | +| switcherIcon | 自定义树节点的展开/折叠图标 | ReactNode | - | - | +| disabled | 是否禁掉树 | boolean | true, false | false | +| multiple | 支持选择多个节点 | boolean | true, false | false | +| selectable | 是否可选中 | boolean | true, false | true | -### TreeNode Attributes +## TreeNode Attributes 建议使用 treeData 来代替 TreeNode,免去手工构造麻烦 | 参数 | 说明 | 类型 | 可选值 | 默认值 | |---------- |-------- |---------- |------------- |-------- | | title | 标题 | string/ReactNode | - | - | | keys | 被树的 expandedKeys /checkedKeys属性所用。注意:整个树范围内的所有节点的keys值不能重复!(根节点keys为"0") | string | - | - | -| checkDisabled | 禁掉 checkbox | boolean | true,false | false | +| checkDisabled | 禁掉checkbox | boolean | true,false | false | +| selectDisabled | 禁掉节点选中 | boolean | true,false | false | +| disabled | 禁掉响应 | boolean | true,false | false | +| checkable | 当树为checkable时,当前节点前是否添加Checkbox | boolean | true, false | - | | isLeaf | 设置为叶子节点 | boolean | true,false | false | +| icon | 自定义节点title前面的图标 | ReactNode | - | - | -### Tree Events +## Tree Events | 事件名称 | 说明 | 回调参数 | |---------- |-------- |---------- | | onCheck | 点击复选框触发 | (checkedMap,checkedObj)| -| onExpand | 展开/收起节点时触发 | expandedObj | \ No newline at end of file +| onExpand | 展开/收起节点时触发 | (expandedKeys,expandedObj) | +| onSelect | 点击节点触发 | (selectedKeys,selectedObj) | \ No newline at end of file diff --git a/components/tree/utils.tsx b/components/tree/utils.tsx index 73503d84..f261e41b 100644 --- a/components/tree/utils.tsx +++ b/components/tree/utils.tsx @@ -1,5 +1,6 @@ import React, { ReactElement, ReactNode } from 'react'; import TreeNode from './TreeNode'; +import { TreeNodePropsType, EventDataNode, PropsType } from './PropsType'; // 判断当前对象是否是object类型 function isPlainObject(obj) { @@ -13,23 +14,6 @@ function isPlainObject(obj) { return proto.constructor && proto.constructor === Object; } -// 深拷贝对象 -export function deepCopy(data) { - if (Array.isArray(data)) { - return data.map((elem) => { - return deepCopy(elem); - }); - } - if (!isPlainObject(data)) { - return data; - } - return Object.keys(data).reduce((prev, key) => { - const value = data[key]; - prev[key] = deepCopy(value); - return prev; - }, {}); -} - /** * 对数组删除一个指定的值 * @param list {Array} 要处理的数组 @@ -98,8 +82,8 @@ export function convertTreeToData(treeNode): Array { // 判断当前节点是否是禁用状态 export function isCheckDisabled(node) { - const { checkDisabled = false } = node; - return checkDisabled; + const { checkDisabled = false, disabled = false } = node; + return checkDisabled || disabled; } /** @@ -130,6 +114,18 @@ function resetNodeCheckedState(node, isChecked, conductDirection) { } } + +export function calcSelectedKeys(selectedKeys: string[] = [], props: PropsType) { + const { multiple } = props; + if (multiple) { + return selectedKeys.slice(); + } + if (selectedKeys.length) { + return [selectedKeys[0]]; + } + return selectedKeys; +} + /** * 根据选中状态变化的结点的keys数组,经过父级以及子级层层遍历判断,得到最终选中结点的数组以及更新状态属性后的treeData * @param keysList {array} 状态变化的结点keys数组 @@ -333,3 +329,20 @@ export function initialTreeData(treeData) { treeData: finalTreeData, }; } + +export function convertNodePropsToEventData(props: TreeNodePropsType): EventDataNode { + const eventData = { + ...props, + }; + delete eventData.children; + + if (!('props' in eventData)) { + Object.defineProperty(eventData, 'props', { + get() { + return props; + }, + }); + } + + return eventData; +} diff --git a/site/pages/Components/index.jsx b/site/pages/Components/index.jsx index 85554042..010e5c5e 100644 --- a/site/pages/Components/index.jsx +++ b/site/pages/Components/index.jsx @@ -10,6 +10,8 @@ import SideBar from '@site/components/SideBar'; import ScrollToTop from '@site/components/ScrollToTop'; import Markdown from '@site/components/Markdown'; import './style.scss'; +import Test from '../../../examples/pages/test/test'; +import TestSearch from '../../../examples/pages/test/testSearch'; const LoadableComponent = (component) => { const loader = { page: component.module }; @@ -53,6 +55,8 @@ class Page extends PureComponent { )) } + + diff --git a/site/site.config.js b/site/site.config.js index 822066ee..8117d710 100644 --- a/site/site.config.js +++ b/site/site.config.js @@ -159,12 +159,12 @@ module.exports = { // module: () => import('@/components/pagination/pagination.md'), // style: false, // }, - // { - // key: 'tree', - // name: '树形控件', - // module: () => import('@/components/tree/tree.md'), - // style: false, - // }, + { + key: 'tree', + name: '树形控件', + module: () => import('@/components/tree/tree.md'), + style: false, + }, { key: 'avatar', name: '头像', From 2c534f32e8b645479f641ca33ae9fa0a6b99e920 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=A8=9C=E5=A8=9C?= Date: Fri, 10 Apr 2020 17:51:48 +0800 Subject: [PATCH 2/5] feat: update tree component --- components/tree/PropsType.tsx | 6 +- components/tree/Tree.tsx | 15 +- .../__snapshots__/index.test.jsx.snap | 9228 +++++++++++++++++ components/tree/__tests__/index.test.jsx | 12 +- components/tree/utils.tsx | 12 - 5 files changed, 9241 insertions(+), 32 deletions(-) create mode 100644 components/tree/__tests__/__snapshots__/index.test.jsx.snap diff --git a/components/tree/PropsType.tsx b/components/tree/PropsType.tsx index 90c4b0e7..6efaf266 100644 --- a/components/tree/PropsType.tsx +++ b/components/tree/PropsType.tsx @@ -1,4 +1,4 @@ -import { MouseEvent, ReactNode, createContext, Context } from 'react'; +import { MouseEvent, ReactNode, createContext } from 'react'; import TreeNode from './TreeNode'; type TreeCheckEventHandler = ( @@ -34,7 +34,7 @@ type TreeNodeClickEventHandler = (event: MouseEvent) => void; export interface PropsType { prefixCls?: string; - treeData?: object[]; + treeData: TreeNode[]; checkable?: boolean; checkedKeys?: string[]; expandedKeys?: string[]; @@ -92,4 +92,4 @@ export interface TreeContextProps { onNodeClick: TreeNodeClickEventHandler; } -export const TreeContext: Context = createContext(null); +export const TreeContext = createContext(null); diff --git a/components/tree/Tree.tsx b/components/tree/Tree.tsx index 346389ee..64d68056 100644 --- a/components/tree/Tree.tsx +++ b/components/tree/Tree.tsx @@ -10,10 +10,10 @@ import { } from './utils'; interface StateType { - checkedKeys?: Array; - selectedKeys?: Array; + checkedKeys: Array; + selectedKeys: Array; halfCheckedKeys?: Array; - expandedKeys?: Array; + expandedKeys: Array; treeData: Array; prevProps?: PropsType; } @@ -24,6 +24,7 @@ class Tree extends Component { checkedKeys: [], selectedKeys: [], expandedKeys: [], + treeData: [], disabled: false, checkable: false, defaultExpandAll: false, @@ -44,7 +45,7 @@ class Tree extends Component { selectedKeys: [], }; - static getDerivedStateFromProps(nextProps: PropsType, prevState: StateType) { + static getDerivedStateFromProps(nextProps: Tree['props'], prevState: StateType) { const { prevProps } = prevState; const newState: Partial = { prevProps: nextProps, @@ -52,7 +53,7 @@ class Tree extends Component { function needUpdate(key: string) { return (!prevProps && key in nextProps) || (prevProps && prevProps[key] !== nextProps[key]); } - let treeData: TreeNode[]; + let treeData: TreeNode[] = []; let TreeDataInformationSet = { allExpandDataMap: {}, treeData: [] }; if (needUpdate('treeData')) { ({ treeData } = nextProps); @@ -65,7 +66,7 @@ class Tree extends Component { } treeData = TreeDataInformationSet.treeData; } - if (treeData) { + if (treeData && treeData.length) { newState.treeData = treeData; // newState.treeData = deepCopy(treeData); } @@ -103,7 +104,7 @@ class Tree extends Component { const { onCheck } = this.props; const { keys } = node; const { treeData: originalTreeData } = this.state; - const { checkedKeys: originalCheckedKeys, halfCheckedKeys: originHalfCheckedKeys } = this.state; + const { checkedKeys: originalCheckedKeys = [], halfCheckedKeys: originHalfCheckedKeys = [] } = this.state; const { checkedKeys, halfCheckedKeys, treeData } = conductCheck([keys], targetChecked, originalTreeData, { checkedKeys: originalCheckedKeys, halfCheckedKeys: originHalfCheckedKeys, diff --git a/components/tree/__tests__/__snapshots__/index.test.jsx.snap b/components/tree/__tests__/__snapshots__/index.test.jsx.snap new file mode 100644 index 00000000..7200e83c --- /dev/null +++ b/components/tree/__tests__/__snapshots__/index.test.jsx.snap @@ -0,0 +1,9228 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Tree renders basic Tree correctly 1`] = ` +
      +
    • +
      + + + + + + + + + + + + 根结点1 + + +
      + +
    • +
    +`; + +exports[`Tree renders basic Tree with TreeNodes children 1`] = ` +
      +`; + +exports[`Tree renders basic Tree with defaultExpandAll checkable correctly 1`] = ` +
        +
      • +
        + + + + + + + + + + + + + + + + + + + + 根结点1 + + +
        + +
          +
        • +
          + + + + + + + + + + + + + + + + + + + + 父结点 0-0 + + +
          + +
            +
          • +
            + + + + + + + + + + + + + + + + + + + + 父结点 0-0-0 + + +
            + +
              +
            • +
              + + + + + + + + + + + + + + 子结点 0-0-0-0 + + +
              +
            • +
            • +
              + + + + + + + + + + + + + + 子结点 0-0-0-1 + + +
              +
            • +
            +
            +
          • +
          +
          +
        • +
        • +
          + + + + + + + + + + + + + + + + + + + + 父结点 0-1 + + +
          + +
            +
          • +
            + + + + + + + + + + + + + + 子结点 0-1-0 + + +
            +
          • +
          • +
            + + + + + + + + + + + + + + 子结点 0-1-1 + + +
            +
          • +
          • +
            + + + + + + + + + + + + + + 子结点 0-1-2 + + +
            +
          • +
          +
          +
        • +
        • +
          + + + + + + + + + + + + + + + + + + + + 父结点 0-2 + + +
          + +
            +
          • +
            + + + + + + + + + + + + + + 父结点 0-2-0 + + +
            +
          • +
          • +
            + + + + + + + + + + + + + + + + + + + + 父结点 0-2-1 + + +
            + +
              +
            • +
              + + + + + + + + + + + + + + + + + + + + 子结点 0-2-1-0 + + +
              + +
                +
              • +
                + + + + + + 子结点 0-2-1-0-0 + + +
                +
              • +
              • +
                + + + + + + + + + + + + + + + + + + + + + 子结点 0-2-1-0-1 + + +
                +
              • +
              +
              +
            • +
            • +
              + + + + + + + + + + + + + + 子结点 0-2-1-1 + + +
              +
            • +
            +
            +
          • +
          • +
            + + + + + + + + + + + + + + 子结点 0-2-2 + + +
            +
          • +
          +
          +
        • +
        +
        +
      • +
      +`; + +exports[`Tree renders basic Tree with defaultExpandAll correctly 1`] = ` +
        +
      • +
        + + + + + + + + + + + + 根结点1 + + +
        + +
          +
        • +
          + + + + + + + + + + + + 父结点 0-0 + + +
          + +
            +
          • +
            + + + + + + + + + + + + 父结点 0-0-0 + + +
            + +
              +
            • +
              + + + + + + 子结点 0-0-0-0 + + +
              +
            • +
            • +
              + + + + + + 子结点 0-0-0-1 + + +
              +
            • +
            +
            +
          • +
          +
          +
        • +
        • +
          + + + + + + + + + + + + 父结点 0-1 + + +
          + +
            +
          • +
            + + + + + + 子结点 0-1-0 + + +
            +
          • +
          • +
            + + + + + + 子结点 0-1-1 + + +
            +
          • +
          • +
            + + + + + + 子结点 0-1-2 + + +
            +
          • +
          +
          +
        • +
        • +
          + + + + + + + + + + + + 父结点 0-2 + + +
          + +
            +
          • +
            + + + + + + 父结点 0-2-0 + + +
            +
          • +
          • +
            + + + + + + + + + + + + 父结点 0-2-1 + + +
            + +
              +
            • +
              + + + + + + + + + + + + 子结点 0-2-1-0 + + +
              + +
                +
              • +
                + + + + + + 子结点 0-2-1-0-0 + + +
                +
              • +
              • +
                + + + + + + + + + + + + + 子结点 0-2-1-0-1 + + +
                +
              • +
              +
              +
            • +
            • +
              + + + + + + 子结点 0-2-1-1 + + +
              +
            • +
            +
            +
          • +
          • +
            + + + + + + 子结点 0-2-2 + + +
            +
          • +
          +
          +
        • +
        +
        +
      • +
      +`; + +exports[`Tree renders basic Tree with defaultExpandAll correctly 2`] = ` +
        +
      • +
        + + + + + + + + + + + + 根结点1 + + +
        + +
          +
        • +
          + + + + + + + + + + + + 父结点 0-0 + + +
          + +
            +
          • +
            + + + + + + + + + + + + 父结点 0-0-0 + + +
            + +
              +
            • +
              + + + + + + 子结点 0-0-0-0 + + +
              +
            • +
            • +
              + + + + + + 子结点 0-0-0-1 + + +
              +
            • +
            +
            +
          • +
          +
          +
        • +
        • +
          + + + + + + + + + + + + 父结点 0-1 + + +
          + +
            +
          • +
            + + + + + + 子结点 0-1-0 + + +
            +
          • +
          • +
            + + + + + + 子结点 0-1-1 + + +
            +
          • +
          • +
            + + + + + + 子结点 0-1-2 + + +
            +
          • +
          +
          +
        • +
        • +
          + + + + + + + + + + + + 父结点 0-2 + + +
          + +
            +
          • +
            + + + + + + 父结点 0-2-0 + + +
            +
          • +
          • +
            + + + + + + + + + + + + 父结点 0-2-1 + + +
            + +
              +
            • +
              + + + + + + + + + + + + 子结点 0-2-1-0 + + +
              + +
                +
              • +
                + + + + + + 子结点 0-2-1-0-0 + + +
                +
              • +
              • +
                + + + + + + + + + + + + + 子结点 0-2-1-0-1 + + +
                +
              • +
              +
              +
            • +
            • +
              + + + + + + 子结点 0-2-1-1 + + +
              +
            • +
            +
            +
          • +
          • +
            + + + + + + 子结点 0-2-2 + + +
            +
          • +
          +
          +
        • +
        +
        +
      • +
      +`; + +exports[`Tree renders basic Tree with defaultExpandAll, checkable correctly 1`] = ` +
        +
      • +
        + + + + + + + + + + + + + + + + + + + + 根结点1 + + +
        + +
          +
        • +
          + + + + + + + + + + + + + + + + + + + + 父结点 0-0 + + +
          + +
            +
          • +
            + + + + + + + + + + + + + + + + + + + + 父结点 0-0-0 + + +
            + +
              +
            • +
              + + + + + + + + + + + + + + 子结点 0-0-0-0 + + +
              +
            • +
            • +
              + + + + + + + + + + + + + + 子结点 0-0-0-1 + + +
              +
            • +
            +
            +
          • +
          +
          +
        • +
        • +
          + + + + + + + + + + + + + + + + + + + + 父结点 0-1 + + +
          + +
            +
          • +
            + + + + + + + + + + + + + + 子结点 0-1-0 + + +
            +
          • +
          • +
            + + + + + + + + + + + + + + 子结点 0-1-1 + + +
            +
          • +
          • +
            + + + + + + + + + + + + + + 子结点 0-1-2 + + +
            +
          • +
          +
          +
        • +
        • +
          + + + + + + + + + + + + + + + + + + + + 父结点 0-2 + + +
          + +
            +
          • +
            + + + + + + + + + + + + + + 父结点 0-2-0 + + +
            +
          • +
          • +
            + + + + + + + + + + + + + + + + + + + + 父结点 0-2-1 + + +
            + +
              +
            • +
              + + + + + + + + + + + + + + + + + + + + 子结点 0-2-1-0 + + +
              + +
                +
              • +
                + + + + + + 子结点 0-2-1-0-0 + + +
                +
              • +
              • +
                + + + + + + + + + + + + + + + + + + + + + 子结点 0-2-1-0-1 + + +
                +
              • +
              +
              +
            • +
            • +
              + + + + + + + + + + + + + + 子结点 0-2-1-1 + + +
              +
            • +
            +
            +
          • +
          • +
            + + + + + + + + + + + + + + 子结点 0-2-2 + + +
            +
          • +
          +
          +
        • +
        +
        +
      • +
      +`; + +exports[`Tree renders basic Tree with defaultExpandAll, checkable, checkedKeys correctly 1`] = ` +
        +
      • +
        + + + + + + + + + + + + + + + + + + + + 根结点1 + + +
        + +
          +
        • +
          + + + + + + + + + + + + + + + + + + + + 父结点 0-0 + + +
          + +
            +
          • +
            + + + + + + + + + + + + + + + + + + + + 父结点 0-0-0 + + +
            + +
              +
            • +
              + + + + + + + + + + + + + + 子结点 0-0-0-0 + + +
              +
            • +
            • +
              + + + + + + + + + + + + + + 子结点 0-0-0-1 + + +
              +
            • +
            +
            +
          • +
          +
          +
        • +
        • +
          + + + + + + + + + + + + + + + + + + + + 父结点 0-1 + + +
          + +
            +
          • +
            + + + + + + + + + + + + + + 子结点 0-1-0 + + +
            +
          • +
          • +
            + + + + + + + + + + + + + + 子结点 0-1-1 + + +
            +
          • +
          • +
            + + + + + + + + + + + + + + 子结点 0-1-2 + + +
            +
          • +
          +
          +
        • +
        • +
          + + + + + + + + + + + + + + + + + + + + 父结点 0-2 + + +
          + +
            +
          • +
            + + + + + + + + + + + + + + 父结点 0-2-0 + + +
            +
          • +
          • +
            + + + + + + + + + + + + + + + + + + + + 父结点 0-2-1 + + +
            + +
              +
            • +
              + + + + + + + + + + + + + + + + + + + + 子结点 0-2-1-0 + + +
              + +
                +
              • +
                + + + + + + 子结点 0-2-1-0-0 + + +
                +
              • +
              • +
                + + + + + + + + + + + + + + + + + + + + + 子结点 0-2-1-0-1 + + +
                +
              • +
              +
              +
            • +
            • +
              + + + + + + + + + + + + + + 子结点 0-2-1-1 + + +
              +
            • +
            +
            +
          • +
          • +
            + + + + + + + + + + + + + + 子结点 0-2-2 + + +
            +
          • +
          +
          +
        • +
        +
        +
      • +
      +`; + +exports[`Tree renders basic Tree with disabled 1`] = ` +
        +
      • +
        + + + + + + + + + + + + 根结点1 + + +
        + +
          +
        • +
          + + + + + + + + + + + + 父结点 0-0 + + +
          + +
            +
          • +
            + + + + + + + + + + + + 父结点 0-0-0 + + +
            + +
              +
            • +
              + + + + + + 子结点 0-0-0-0 + + +
              +
            • +
            • +
              + + + + + + 子结点 0-0-0-1 + + +
              +
            • +
            +
            +
          • +
          +
          +
        • +
        • +
          + + + + + + + + + + + + 父结点 0-1 + + +
          + +
            +
          • +
            + + + + + + 子结点 0-1-0 + + +
            +
          • +
          • +
            + + + + + + 子结点 0-1-1 + + +
            +
          • +
          • +
            + + + + + + 子结点 0-1-2 + + +
            +
          • +
          +
          +
        • +
        • +
          + + + + + + + + + + + + 父结点 0-2 + + +
          + +
            +
          • +
            + + + + + + 父结点 0-2-0 + + +
            +
          • +
          • +
            + + + + + + + + + + + + 父结点 0-2-1 + + +
            + +
              +
            • +
              + + + + + + + + + + + + 子结点 0-2-1-0 + + +
              + +
                +
              • +
                + + + + + + 子结点 0-2-1-0-0 + + +
                +
              • +
              • +
                + + + + + + + + + + + + + 子结点 0-2-1-0-1 + + +
                +
              • +
              +
              +
            • +
            • +
              + + + + + + 子结点 0-2-1-1 + + +
              +
            • +
            +
            +
          • +
          • +
            + + + + + + 子结点 0-2-2 + + +
            +
          • +
          +
          +
        • +
        +
        +
      • +
      +`; + +exports[`Tree renders basic Tree with expandedKeys, autoExpandParent correctly 1`] = ` +
        +
      • +
        + + + + + + + + + + + + 根结点1 + + +
        + +
          +
        • +
          + + + + + + + + + + + + 父结点 0-0 + + +
          + +
            +
          • +
            + + + + + + + + + + + + 父结点 0-0-0 + + +
            + +
              +
            • +
              + + + + + + 子结点 0-0-0-0 + + +
              +
            • +
            • +
              + + + + + + 子结点 0-0-0-1 + + +
              +
            • +
            +
            +
          • +
          +
          +
        • +
        • +
          + + + + + + + + + + + + 父结点 0-1 + + +
          + +
        • +
        • +
          + + + + + + + + + + + + 父结点 0-2 + + +
          + +
        • +
        +
        +
      • +
      +`; + +exports[`Tree renders basic Tree with expandedKeys, checkable correctly 1`] = ` +
        +
      • +
        + + + + + + + + + + + + + + + + + + + + 根结点1 + + +
        + +
          +
        • +
          + + + + + + + + + + + + + + + + + + + + 父结点 0-0 + + +
          + +
            +
          • +
            + + + + + + + + + + + + + + + + + + + + 父结点 0-0-0 + + +
            + +
              +
            • +
              + + + + + + + + + + + + + + 子结点 0-0-0-0 + + +
              +
            • +
            • +
              + + + + + + + + + + + + + + 子结点 0-0-0-1 + + +
              +
            • +
            +
            +
          • +
          +
          +
        • +
        • +
          + + + + + + + + + + + + + + + + + + + + 父结点 0-1 + + +
          + +
        • +
        • +
          + + + + + + + + + + + + + + + + + + + + 父结点 0-2 + + +
          + +
        • +
        +
        +
      • +
      +`; + +exports[`Tree renders basic Tree with expandedKeys, defaultExpandAll, checkable correctly 1`] = ` +
        +
      • +
        + + + + + + + + + + + + + + + + + + + + 根结点1 + + +
        + +
          +
        • +
          + + + + + + + + + + + + + + + + + + + + 父结点 0-0 + + +
          + +
            +
          • +
            + + + + + + + + + + + + + + + + + + + + 父结点 0-0-0 + + +
            + +
              +
            • +
              + + + + + + + + + + + + + + 子结点 0-0-0-0 + + +
              +
            • +
            • +
              + + + + + + + + + + + + + + 子结点 0-0-0-1 + + +
              +
            • +
            +
            +
          • +
          +
          +
        • +
        • +
          + + + + + + + + + + + + + + + + + + + + 父结点 0-1 + + +
          + +
            +
          • +
            + + + + + + + + + + + + + + 子结点 0-1-0 + + +
            +
          • +
          • +
            + + + + + + + + + + + + + + 子结点 0-1-1 + + +
            +
          • +
          • +
            + + + + + + + + + + + + + + 子结点 0-1-2 + + +
            +
          • +
          +
          +
        • +
        • +
          + + + + + + + + + + + + + + + + + + + + 父结点 0-2 + + +
          + +
            +
          • +
            + + + + + + + + + + + + + + 父结点 0-2-0 + + +
            +
          • +
          • +
            + + + + + + + + + + + + + + + + + + + + 父结点 0-2-1 + + +
            + +
              +
            • +
              + + + + + + + + + + + + + + + + + + + + 子结点 0-2-1-0 + + +
              + +
                +
              • +
                + + + + + + 子结点 0-2-1-0-0 + + +
                +
              • +
              • +
                + + + + + + + + + + + + + + + + + + + + + 子结点 0-2-1-0-1 + + +
                +
              • +
              +
              +
            • +
            • +
              + + + + + + + + + + + + + + 子结点 0-2-1-1 + + +
              +
            • +
            +
            +
          • +
          • +
            + + + + + + + + + + + + + + 子结点 0-2-2 + + +
            +
          • +
          +
          +
        • +
        +
        +
      • +
      +`; + +exports[`Tree renders basic Tree with expandedKeys, defaultExpandAll, checkable, checkedKeys correctly 1`] = ` +
        +
      • +
        + + + + + + + + + + + + + + + + + + + + 根结点1 + + +
        + +
          +
        • +
          + + + + + + + + + + + + + + + + + + + + 父结点 0-0 + + +
          + +
            +
          • +
            + + + + + + + + + + + + + + + + + + + + 父结点 0-0-0 + + +
            + +
              +
            • +
              + + + + + + + + + + + + + + 子结点 0-0-0-0 + + +
              +
            • +
            • +
              + + + + + + + + + + + + + + 子结点 0-0-0-1 + + +
              +
            • +
            +
            +
          • +
          +
          +
        • +
        • +
          + + + + + + + + + + + + + + + + + + + + 父结点 0-1 + + +
          + +
            +
          • +
            + + + + + + + + + + + + + + 子结点 0-1-0 + + +
            +
          • +
          • +
            + + + + + + + + + + + + + + 子结点 0-1-1 + + +
            +
          • +
          • +
            + + + + + + + + + + + + + + 子结点 0-1-2 + + +
            +
          • +
          +
          +
        • +
        • +
          + + + + + + + + + + + + + + + + + + + + 父结点 0-2 + + +
          + +
            +
          • +
            + + + + + + + + + + + + + + 父结点 0-2-0 + + +
            +
          • +
          • +
            + + + + + + + + + + + + + + + + + + + + 父结点 0-2-1 + + +
            + +
              +
            • +
              + + + + + + + + + + + + + + + + + + + + 子结点 0-2-1-0 + + +
              + +
                +
              • +
                + + + + + + 子结点 0-2-1-0-0 + + +
                +
              • +
              • +
                + + + + + + + + + + + + + + + + + + + + + 子结点 0-2-1-0-1 + + +
                +
              • +
              +
              +
            • +
            • +
              + + + + + + + + + + + + + + 子结点 0-2-1-1 + + +
              +
            • +
            +
            +
          • +
          • +
            + + + + + + + + + + + + + + 子结点 0-2-2 + + +
            +
          • +
          +
          +
        • +
        +
        +
      • +
      +`; + +exports[`Tree renders basic Tree with multiple, selectable 1`] = ` +
        +
      • +
        + + + + + + + + + + + + 根结点1 + + +
        + +
      • +
      +`; + +exports[`Tree renders basic Tree with not completely TreeNodes children 1`] = ` +
        +`; + +exports[`Tree renders basic Tree with showLine, showIcon, icon 1`] = ` +
          +
        • +
          + + + + + + + + + + + + + + + + + + + 根结点1 + + +
          + +
            +
          • +
            + + + + + + + + + + + + + + + + + + + 父结点 0-0 + + +
            + +
              +
            • +
              + + + + + + + + + + + + + + + + + + + 父结点 0-0-0 + + +
              + +
                +
              • +
                + + + + + + + + + + + + + + + + + + + 子结点 0-0-0-0 + + +
                +
              • +
              • +
                + + + + + + + + + + + + + + + + + + + 子结点 0-0-0-1 + + +
                +
              • +
              +
              +
            • +
            +
            +
          • +
          • +
            + + + + + + + + + + + + + + + + + + + 父结点 0-1 + + +
            + +
              +
            • +
              + + + + + + + + + + + + + + + + + + + 子结点 0-1-0 + + +
              +
            • +
            • +
              + + + + + + + + + + + + + + + + + + + 子结点 0-1-1 + + +
              +
            • +
            • +
              + + + + + + + + + + + + + + + + + + + 子结点 0-1-2 + + +
              +
            • +
            +
            +
          • +
          • +
            + + + + + + + + + + + + + + + + + + + 父结点 0-2 + + +
            + +
              +
            • +
              + + + + + + + + + + + + + + + + + + + 父结点 0-2-0 + + +
              +
            • +
            • +
              + + + + + + + + + + + + + + + + + + + 父结点 0-2-1 + + +
              + +
                +
              • +
                + + + + + + + + + + + + + + + + + + + 子结点 0-2-1-0 + + +
                + +
                  +
                • +
                  + + + + + + + + + + + + + + + + + + + 子结点 0-2-1-0-0 + + +
                  +
                • +
                • +
                  + + + + + + + + + + + + + + + + + + + 子结点 0-2-1-0-1 + + +
                  +
                • +
                +
                +
              • +
              • +
                + + + + + + + + + + + + + + + + + + + 子结点 0-2-1-1 + + +
                +
              • +
              +
              +
            • +
            • +
              + + + + + + + + + + + + + + + + + + + 子结点 0-2-2 + + +
              +
            • +
            +
            +
          • +
          +
          +
        • +
        +`; + +exports[`Tree renders basic Tree with showLine, switcherIcon 1`] = ` +
          +
        • +
          + + + + + + + + + + + + 根结点1 + + +
          + +
            +
          • +
            + + + + + + + + + + + + 父结点 0-0 + + +
            + +
              +
            • +
              + + + + + + + + + + + + 父结点 0-0-0 + + +
              + +
                +
              • +
                + + + + + + + + + + + + 子结点 0-0-0-0 + + +
                +
              • +
              • +
                + + + + + + + + + + + + 子结点 0-0-0-1 + + +
                +
              • +
              +
              +
            • +
            +
            +
          • +
          • +
            + + + + + + + + + + + + 父结点 0-1 + + +
            + +
              +
            • +
              + + + + + + + + + + + + 子结点 0-1-0 + + +
              +
            • +
            • +
              + + + + + + + + + + + + 子结点 0-1-1 + + +
              +
            • +
            • +
              + + + + + + + + + + + + 子结点 0-1-2 + + +
              +
            • +
            +
            +
          • +
          • +
            + + + + + + + + + + + + 父结点 0-2 + + +
            + +
              +
            • +
              + + + + + + + + + + + + 父结点 0-2-0 + + +
              +
            • +
            • +
              + + + + + + + + + + + + 父结点 0-2-1 + + +
              + +
                +
              • +
                + + + + + + + + + + + + 子结点 0-2-1-0 + + +
                + +
                  +
                • +
                  + + + + + + + + + + + + 子结点 0-2-1-0-0 + + +
                  +
                • +
                • +
                  + + + + + + + + + + + + + + + + + + + 子结点 0-2-1-0-1 + + +
                  +
                • +
                +
                +
              • +
              • +
                + + + + + + + + + + + + 子结点 0-2-1-1 + + +
                +
              • +
              +
              +
            • +
            • +
              + + + + + + + + + + + + 子结点 0-2-2 + + +
              +
            • +
            +
            +
          • +
          +
          +
        • +
        +`; diff --git a/components/tree/__tests__/index.test.jsx b/components/tree/__tests__/index.test.jsx index 218e7098..0d4e083b 100644 --- a/components/tree/__tests__/index.test.jsx +++ b/components/tree/__tests__/index.test.jsx @@ -222,18 +222,10 @@ describe('Tree', () => { const wrapper = mount( , ); - expect(wrapper.state('checkedKeys')).toEqual(expect.arrayContaining(['0-2-1-1', '0-0-0-0', '0-1-0'])); - expect(wrapper.state('halfCheckedKeys')).toEqual(expect.arrayContaining(['0', '0-2-1', '0-2'])); - - wrapper.find('li[data-keys="0-0-0-1"] .zw-checkbox__input').at(0).simulate('change'); + wrapper.find('li[data-keys="0-0-0-1"] .zw-checkbox__input').at(0).simulate('click'); expect(onCheck).toBeCalled(); - expect(wrapper.state('checkedKeys')).toEqual(expect.arrayContaining(['0-2-1-1', '0-0-0-0', '0-1-0', '0-0-0-1', '0-0-0', '0-0'])); - expect(wrapper.state('halfCheckedKeys')).toEqual(expect.arrayContaining(['0', '0-2-1', '0-2'])); - - wrapper.find('li[data-keys="0-1"] .zw-checkbox__input').at(0).simulate('change'); - expect(onCheck).toBeCalled(); - expect(wrapper.state('checkedKeys')).toEqual(expect.arrayContaining(['0-0-0-0', '0-2-1-1', '0-1-0', '0-1', '0-1-1', '0-1-2'])); + expect(wrapper.state('checkedKeys')).toEqual(expect.arrayContaining(['0-0-0-0', '0-2-1-1', '0-1-0', '0-0-0-1', '0-0-0', '0-0'])); expect(wrapper.state('halfCheckedKeys')).toEqual(expect.arrayContaining(['0', '0-2-1', '0-2'])); }); diff --git a/components/tree/utils.tsx b/components/tree/utils.tsx index f261e41b..45afee0d 100644 --- a/components/tree/utils.tsx +++ b/components/tree/utils.tsx @@ -2,18 +2,6 @@ import React, { ReactElement, ReactNode } from 'react'; import TreeNode from './TreeNode'; import { TreeNodePropsType, EventDataNode, PropsType } from './PropsType'; -// 判断当前对象是否是object类型 -function isPlainObject(obj) { - if (Object.prototype.toString.call(obj).toLowerCase() !== '[object object]') { - return false; - } - const proto = Object.getPrototypeOf(obj); - if (!proto) { - return true; - } - return proto.constructor && proto.constructor === Object; -} - /** * 对数组删除一个指定的值 * @param list {Array} 要处理的数组 From 417117e1724db9bcbc66c0d97cbb35ef0bcae231 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=A8=9C=E5=A8=9C?= Date: Fri, 10 Apr 2020 22:30:33 +0800 Subject: [PATCH 3/5] chore: update tree markdown --- components/tree/tree.md | 238 +++++++++++++++++++++------------------- 1 file changed, 125 insertions(+), 113 deletions(-) diff --git a/components/tree/tree.md b/components/tree/tree.md index 9be0502f..03960618 100644 --- a/components/tree/tree.md +++ b/components/tree/tree.md @@ -1,7 +1,7 @@ # Tree 树形组件 -## 基本用法1 +## 基本用法 展示可勾选,可选中节点或多选节点,禁用单个节点或其checkbox,默认展开等功能。 ```jsx @@ -185,6 +185,7 @@ const MyIcon = Icon.createFromIconfont('//at.alicdn.com/t/font_1733827_4scbzsuv5 class Demo3 extends React.Component { state = { showLine: false, + showSwitcherIcon: false, treeData: [ { keys: '0', @@ -229,19 +230,31 @@ class Demo3 extends React.Component { showLine, }); }; - + + setSwitcherIcon = (showSwitcherIcon) => { + this.setState({ + showSwitcherIcon, + }); + }; + render() { - const { treeData, showLine } = this.state; + const { treeData, showLine, showSwitcherIcon } = this.state; + const otherProps = showSwitcherIcon ? { switcherIcon: } : {}; return (
        - showLine && change switcherIcon: + showLine:
        +
        + change switcherIcon to icon smile: + +
        + } + {...otherProps} + defaultExpandAll />
        ); @@ -252,6 +265,76 @@ ReactDOM.render(, mountNode); ``` +## 自定义图标 +可以通过icon设置树的节点图标,也可以针对单个不同的节点定制图标。 + +```jsx + +import { Tree, Icon } from 'zarm-web'; + +const MyIcon = Icon.createFromIconfont('//at.alicdn.com/t/font_1733827_4scbzsuv5v2.js'); + +class Demo5 extends React.Component { + state = { + treeData: [ + { + keys: '0', + title: '根结点1', + children: + [ + { + keys: '0-0', + title: '父结点 0-0', + disabled: true, + children: + [ + { + keys: '0-0-0', + title: '父结点 0-0-0', + checkDisabled: true, + children: + [ + { keys: '0-0-0-0', title: '子结点 0-0-0-0', icon: }, + { keys: '0-0-0-1', title: '子结点 0-0-0-1' }, + ], + }, + ], + }, + { + keys: '0-1', + title: '父结点 0-1', + children: + [ + { keys: '0-1-0', title: '子结点 0-1-0' }, + { keys: '0-1-1', title: '子结点 0-1-1' }, + { keys: '0-1-2', title: '子结点 0-1-2' }, + ], + }, + ], + }, + ], + }; + + render() { + const { treeData } = this.state; + return ( +
        + } + treeData={treeData} + defaultExpandAll + checkable + /> +
        + ); + } +} + +ReactDOM.render(, mountNode); + +``` + ## 可搜索 可搜索的树。 @@ -387,111 +470,40 @@ ReactDOM.render(, mountNode); ``` -## 自定义图标 -可以通过icon设置树的节点图标,也可以针对单个不同的节点定制图标。 - -```jsx - -import { Tree, Icon } from 'zarm-web'; - -const MyIcon = Icon.createFromIconfont('//at.alicdn.com/t/font_1733827_4scbzsuv5v2.js'); - -class Demo5 extends React.Component { - state = { - treeData: [ - { - keys: '0', - title: '根结点1', - children: - [ - { - keys: '0-0', - title: '父结点 0-0', - disabled: true, - children: - [ - { - keys: '0-0-0', - title: '父结点 0-0-0', - checkDisabled: true, - children: - [ - { keys: '0-0-0-0', title: '子结点 0-0-0-0', icon: }, - { keys: '0-0-0-1', title: '子结点 0-0-0-1' }, - ], - }, - ], - }, - { - keys: '0-1', - title: '父结点 0-1', - children: - [ - { keys: '0-1-0', title: '子结点 0-1-0' }, - { keys: '0-1-1', title: '子结点 0-1-1' }, - { keys: '0-1-2', title: '子结点 0-1-2' }, - ], - }, - ], - }, - ], - }; - - render() { - const { treeData } = this.state; - return ( -
        - } - treeData={treeData} - defaultExpandAll - checkable - /> -
        - ); - } -} - -ReactDOM.render(, mountNode); - -``` -## Tree Attributes -| 参数 | 说明 | 类型 | 可选值 | 默认值 | -|---------- |-------- |---------- |------------- |-------- | -| treeData | 树节点结构数组 | array | - | [] | -| checkable | 节点前添加Checkbox 复选框 | boolean | true, false | false | -| checkedKeys | 选中复选框的树节点数组 | array | - | [] | -| expandedKeys | 展开指定的树节点 | array | - | [] | -| selectedKeys | 设置选中的树节点 | array | - | [] | -| defaultExpandAll | 默认展开所有树节点 | boolean | true, false | false | -| autoExpandParent | 是否自动展开父节点 | boolean | true, false | true | -| showLine | 是否展示连接线 | boolean | true, false | false | -| showIcon | 是否节点title前的图标,如设置为true,需要icon属性自行定义图标相关样式 | boolean | true, false | false | -| icon | 自定义节点title前面的图标 | ReactNode | - | - | -| switcherIcon | 自定义树节点的展开/折叠图标 | ReactNode | - | - | -| disabled | 是否禁掉树 | boolean | true, false | false | -| multiple | 支持选择多个节点 | boolean | true, false | false | -| selectable | 是否可选中 | boolean | true, false | true | - -## TreeNode Attributes -建议使用 treeData 来代替 TreeNode,免去手工构造麻烦 - -| 参数 | 说明 | 类型 | 可选值 | 默认值 | -|---------- |-------- |---------- |------------- |-------- | -| title | 标题 | string/ReactNode | - | - | -| keys | 被树的 expandedKeys /checkedKeys属性所用。注意:整个树范围内的所有节点的keys值不能重复!(根节点keys为"0") | string | - | - | -| checkDisabled | 禁掉checkbox | boolean | true,false | false | -| selectDisabled | 禁掉节点选中 | boolean | true,false | false | -| disabled | 禁掉响应 | boolean | true,false | false | -| checkable | 当树为checkable时,当前节点前是否添加Checkbox | boolean | true, false | - | -| isLeaf | 设置为叶子节点 | boolean | true,false | false | -| icon | 自定义节点title前面的图标 | ReactNode | - | - | - -## Tree Events -| 事件名称 | 说明 | 回调参数 | -|---------- |-------- |---------- | -| onCheck | 点击复选框触发 | (checkedMap,checkedObj)| -| onExpand | 展开/收起节点时触发 | (expandedKeys,expandedObj) | -| onSelect | 点击节点触发 | (selectedKeys,selectedObj) | \ No newline at end of file +## API + +

        Tree

        + +| 属性 | 类型 | 默认值 | 说明 | +| :--- | :--- | :--- | :--- | +| treeData | array | [] | 树节点结构数组| +| checkable | true, false | false | 节点前添加Checkbox 复选框 | +| checkedKeys | array | [] | 选中复选框的树节点数组 | +| expandedKeys | array | [] | 展开指定的树节点 | +| selectedKeys | array | [] | 设置选中的树节点 | +| defaultExpandAll | boolean | false | 默认展开所有树节点 | +| autoExpandParent | boolean | true | 是否自动展开父节点 | +| showLine | boolean | false | 是否展示连接线 | +| showIcon | boolean | false | 是否节点title前的图标,如设置为true,需要icon属性自行定义图标相关样式 | +| icon | ReactNode | - | 自定义节点title前面的图标 | +| switcherIcon | ReactNode | - | 自定义树节点的展开/折叠图标 | +| disabled | boolean | false | 是否禁掉树 | +| multiple | boolean | false | 支持选择多个节点 | +| selectable | boolean | true | 是否可选中 | +| onCheck | (checkedMap,checkedObj)=> void | - | 点击复选框触发 | +| onExpand | (expandedKeys,expandedObj)=> void | - | 展开/收起节点时触发 | +| onSelect | (selectedKeys,selectedObj)=> void | - | 点击节点触发 | + +

        TreeNode

        + +| 属性 | 类型 | 默认值 | 说明 | +| :--- | :--- | :--- | :--- | +| title | string/ReactNode | - | 标题 | +| keys | string | - | 被树的 expandedKeys /checkedKeys属性所用。注意:整个树范围内的所有节点的keys值不能重复!(根节点keys为"0") | +| checkDisabled | boolean | false | 禁掉checkbox | +| selectDisabled | boolean | false | 禁掉节点选中 | +| disabled | boolean | false | 禁掉响应 | +| checkable | boolean | - | 当树为checkable时,当前节点前是否添加Checkbox | +| isLeaf | boolean | false | 设置为叶子节点 | +| icon | ReactNode | - | 自定义节点title前面的图标 | \ No newline at end of file From dd8cf2359954af3ca8c4cf15260230d63bbd91cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=A8=9C=E5=A8=9C?= Date: Fri, 10 Apr 2020 22:51:47 +0800 Subject: [PATCH 4/5] chore: update tree component markdown --- components/tree/tree.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/tree/tree.md b/components/tree/tree.md index 03960618..57aa2536 100644 --- a/components/tree/tree.md +++ b/components/tree/tree.md @@ -477,7 +477,7 @@ ReactDOM.render(, mountNode); | 属性 | 类型 | 默认值 | 说明 | | :--- | :--- | :--- | :--- | -| treeData | array | [] | 树节点结构数组| +| treeData | array / <{keys, title, children, [disabled, selectDisabled, checkDisabled, checkable, isLeaf, icon]}> | [] | treeNodes数据,如设置则不需要手动构造TreeNode节点(keys 在整个树内唯一)| | checkable | true, false | false | 节点前添加Checkbox 复选框 | | checkedKeys | array | [] | 选中复选框的树节点数组 | | expandedKeys | array | [] | 展开指定的树节点 | From b54944cb494df1bdade288baa7ba29103df5af4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=A8=9C=E5=A8=9C?= Date: Wed, 15 Apr 2020 11:34:29 +0800 Subject: [PATCH 5/5] fix: fix stylelint error --- components/tree/style/component.scss | 33 +++++++++++++++++----------- site/pages/Components/index.jsx | 4 ---- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/components/tree/style/component.scss b/components/tree/style/component.scss index 9cc60b77..ef5b7a7f 100644 --- a/components/tree/style/component.scss +++ b/components/tree/style/component.scss @@ -9,7 +9,7 @@ $tree-node-checkbox-width: 20px; margin: 0; padding: 0; - li { + li { position: relative; padding: 5px 0; margin: 0; @@ -19,14 +19,17 @@ $tree-node-checkbox-width: 20px; & > div { display: inline-flex; + &:focus { - box-shadow:0px 0px 2px 2px rgba(18,194,135,0.1); - border-radius:2px; + box-shadow: 0 0 2px 2px rgba(18, 194, 135, 0.1); + border-radius: 2px; } + &.#{$treePrefixCls}-treenode-disabled { span.#{$treePrefixCls}-node-content-wrapper { color: #bcbcbc; cursor: not-allowed; + &:hover { background-color: transparent; color: #bcbcbc; @@ -70,7 +73,7 @@ $tree-node-checkbox-width: 20px; cursor: pointer; text-align: center; vertical-align: middle; - + &.#{$treePrefixCls}-switcher-open, &.#{$treePrefixCls}-switcher-close { .#{$treePrefixCls}-switcher-icon { @@ -122,23 +125,25 @@ $tree-node-checkbox-width: 20px; transition: all .3s; border-radius: 1px; vertical-align: middle; - + &.#{$treePrefixCls}-node-selected { background-color: var(--theme-primary); color: #fff; - + .#{$treePrefixCls}-node-content-icon { i { - color:#fff; + color: #fff; } } } + &:not(.#{$treePrefixCls}-node-selected):hover { - background-color:rgba(18,194,135,0.1); - color: #12C287; + background-color: rgba(18, 194, 135, 0.1); + color: #12c287; border-radius: 1px; } - .#{$treePrefixCls}-node-content-icon { + + .#{$treePrefixCls}-node-content-icon { margin-right: 2px; i { @@ -149,10 +154,10 @@ $tree-node-checkbox-width: 20px; &:not(.#{$treePrefixCls}-node-content-icon) { > i.#{$prefixCls}-icon { - color: rgba(0,0,0,.7); + color: rgba(0, 0, 0, .7); } } - + i.#{$prefixCls}-icon { svg { vertical-align: middle; @@ -178,11 +183,13 @@ $tree-node-checkbox-width: 20px; width: $tree-node-switcher-width; height: $tree-node-height; } + .#{$treePrefixCls}-switcher-icon { width: $tree-node-switcher-width; height: $tree-node-height; } } + &:not(:last-child) { &:before { position: absolute; @@ -192,7 +199,7 @@ $tree-node-checkbox-width: 20px; border-right: 1px solid #ccc; content: ""; } - + &:first-child:before { top: 29px; } diff --git a/site/pages/Components/index.jsx b/site/pages/Components/index.jsx index 010e5c5e..85554042 100644 --- a/site/pages/Components/index.jsx +++ b/site/pages/Components/index.jsx @@ -10,8 +10,6 @@ import SideBar from '@site/components/SideBar'; import ScrollToTop from '@site/components/ScrollToTop'; import Markdown from '@site/components/Markdown'; import './style.scss'; -import Test from '../../../examples/pages/test/test'; -import TestSearch from '../../../examples/pages/test/testSearch'; const LoadableComponent = (component) => { const loader = { page: component.module }; @@ -55,8 +53,6 @@ class Page extends PureComponent { )) } - -