From ecc8dd4c7dedf403c40c9b1abfacb178a1f7fcc2 Mon Sep 17 00:00:00 2001 From: tjz <415800467@qq.com> Date: Fri, 23 Mar 2018 21:30:20 +0800 Subject: [PATCH 1/8] add tree --- components/checkbox/Checkbox.jsx | 1 - components/dropdown/index.en-US.md | 4 +- components/dropdown/index.zh-CN.md | 4 +- components/trigger/index.md | 2 +- components/vc-tree/assets/icons.png | Bin 0 -> 11173 bytes components/vc-tree/assets/index.less | 192 ++++++++ components/vc-tree/assets/line.gif | Bin 0 -> 45 bytes components/vc-tree/assets/loading.gif | Bin 0 -> 381 bytes components/vc-tree/index.js | 3 + components/vc-tree/src/Tree.jsx | 610 ++++++++++++++++++++++++++ components/vc-tree/src/TreeNode.jsx | 585 ++++++++++++++++++++++++ components/vc-tree/src/index.js | 6 + components/vc-tree/src/util.js | 399 +++++++++++++++++ 13 files changed, 1800 insertions(+), 6 deletions(-) create mode 100644 components/vc-tree/assets/icons.png create mode 100644 components/vc-tree/assets/index.less create mode 100644 components/vc-tree/assets/line.gif create mode 100644 components/vc-tree/assets/loading.gif create mode 100644 components/vc-tree/index.js create mode 100644 components/vc-tree/src/Tree.jsx create mode 100644 components/vc-tree/src/TreeNode.jsx create mode 100644 components/vc-tree/src/index.js create mode 100644 components/vc-tree/src/util.js diff --git a/components/checkbox/Checkbox.jsx b/components/checkbox/Checkbox.jsx index 722ed86e9..3056fc5c1 100644 --- a/components/checkbox/Checkbox.jsx +++ b/components/checkbox/Checkbox.jsx @@ -20,7 +20,6 @@ export default { }, inject: { checkboxGroupContext: { default: null }, - test: { default: null }, }, data () { const { checkboxGroupContext, checked, defaultChecked, value } = this diff --git a/components/dropdown/index.en-US.md b/components/dropdown/index.en-US.md index 4c872bd8c..e0f7c5b56 100644 --- a/components/dropdown/index.en-US.md +++ b/components/dropdown/index.en-US.md @@ -8,7 +8,7 @@ | getPopupContainer | to set the container of the dropdown menu. The default is to create a `div` element in `body`, you can reset it to the scrolling area and make a relative reposition. [example](https://codepen.io/afc163/pen/zEjNOy?editors=0010) | Function(triggerNode) | `() => document.body` | | overlay(slot) | the dropdown menu | [Menu](#/us/components/menu) | - | | placement | placement of pop menu: `bottomLeft` `bottomCenter` `bottomRight` `topLeft` `topCenter` `topRight` | String | `bottomLeft` | -| trigger | the trigger mode which executes the drop-down action | Array<`click`\|`hover`\|`contextMenu`> | `['hover']` | +| trigger | the trigger mode which executes the drop-down action | Array<`click`\|`hover`\|`contextmenu`> | `['hover']` | | visible(v-model) | whether the dropdown menu is visible | boolean | - | ### events @@ -30,7 +30,7 @@ You should use [Menu](#/us/components/menu/) as `overlay`. The menu items and di | overlay(slot) | the dropdown menu | [Menu](#/us/components/menu) | - | | placement | placement of pop menu: `bottomLeft` `bottomCenter` `bottomRight` `topLeft` `topCenter` `topRight` | String | `bottomLeft` | | size | size of the button, the same as [Button](#/us/components/button) | string | `default` | -| trigger | the trigger mode which executes the drop-down action | Array<`click`\|`hover`\|`contextMenu`> | `['hover']` | +| trigger | the trigger mode which executes the drop-down action | Array<`click`\|`hover`\|`contextmenu`> | `['hover']` | | type | type of the button, the same as [Button](#/us/components/button) | string | `default` | | visible | whether the dropdown menu is visible | boolean | - | diff --git a/components/dropdown/index.zh-CN.md b/components/dropdown/index.zh-CN.md index 8c4f839ff..b65b5cf1d 100644 --- a/components/dropdown/index.zh-CN.md +++ b/components/dropdown/index.zh-CN.md @@ -8,7 +8,7 @@ | getPopupContainer | 菜单渲染父节点。默认渲染到 body 上,如果你遇到菜单滚动定位问题,试试修改为滚动的区域,并相对其定位。 | Function(triggerNode) | `() => document.body` | | overlay(slot) | 菜单 | [Menu](#/cn/components/menu) | - | | placement | 菜单弹出位置:`bottomLeft` `bottomCenter` `bottomRight` `topLeft` `topCenter` `topRight` | String | `bottomLeft` | -| trigger | 触发下拉的行为 | Array<`click`\|`hover`\|`contextMenu`> | `['hover']` | +| trigger | 触发下拉的行为 | Array<`click`\|`hover`\|`contextmenu`> | `['hover']` | | visible(v-model) | 菜单是否显示 | boolean | - | `overlay` 菜单使用 [Menu](#/cn/components/menu/),还包括菜单项 `Menu.Item`,分割线 `Menu.Divider`。 @@ -31,7 +31,7 @@ | overlay(slot) | 菜单 | [Menu](#/cn/components/menu/) | - | | placement | 菜单弹出位置:`bottomLeft` `bottomCenter` `bottomRight` `topLeft` `topCenter` `topRight` | String | `bottomLeft` | | size | 按钮大小,和 [Button](#/cn/components/button/) 一致 | string | 'default' | -| trigger | 触发下拉的行为 | Array<`click`\|`hover`\|`contextMenu`> | `['hover']` | +| trigger | 触发下拉的行为 | Array<`click`\|`hover`\|`contextmenu`> | `['hover']` | | type | 按钮类型,和 [Button](#/cn/components/button/) 一致 | string | 'default' | | visible(v-model) | 菜单是否显示 | boolean | - | diff --git a/components/trigger/index.md b/components/trigger/index.md index 69f9cb643..bae1a0f2a 100644 --- a/components/trigger/index.md +++ b/components/trigger/index.md @@ -40,7 +40,7 @@ action string[] ['hover'] - which actions cause popup shown. enum of 'hover','click','focus','contextMenu' + which actions cause popup shown. enum of 'hover','click','focus','contextmenu' mouseEnterDelay diff --git a/components/vc-tree/assets/icons.png b/components/vc-tree/assets/icons.png new file mode 100644 index 0000000000000000000000000000000000000000..ffda01ef1cccc398ee4e2327f4093ba1130a4961 GIT binary patch literal 11173 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 X3wf1`XyoU;TL_tNiW1dgpn(4aGkUu% literal 0 HcmV?d00001 diff --git a/components/vc-tree/assets/index.less b/components/vc-tree/assets/index.less new file mode 100644 index 000000000..cde71e949 --- /dev/null +++ b/components/vc-tree/assets/index.less @@ -0,0 +1,192 @@ +@treePrefixCls: rc-tree; +.@{treePrefixCls} { + margin: 0; + padding: 5px; + li { + padding: 0; + margin: 0; + list-style: none; + white-space: nowrap; + outline: 0; + .draggable { + color: #333; + -moz-user-select: none; + -khtml-user-select: none; + -webkit-user-select: none; + user-select: none; + /* Required to make elements draggable in old WebKit */ + -khtml-user-drag: element; + -webkit-user-drag: element; + } + &.drag-over { + > .draggable { + background-color: #316ac5; + color: white; + border: 1px #316ac5 solid; + opacity: 0.8; + } + } + &.drag-over-gap-top { + > .draggable { + border-top: 2px blue solid; + } + } + &.drag-over-gap-bottom { + > .draggable { + border-bottom: 2px blue solid; + } + } + &.filter-node { + > .@{treePrefixCls}-node-content-wrapper { + color: #a60000!important; + font-weight: bold!important; + } + } + ul { + margin: 0; + padding: 0 0 0 18px; + } + .@{treePrefixCls}-node-content-wrapper { + display: inline-block; + padding: 1px 3px 0 0; + margin: 0; + cursor: pointer; + height: 17px; + text-decoration: none; + vertical-align: top; + } + span { + &.@{treePrefixCls}-switcher, + &.@{treePrefixCls}-checkbox, + &.@{treePrefixCls}-iconEle { + line-height: 16px; + margin-right: 2px; + width: 16px; + height: 16px; + display: inline-block; + vertical-align: middle; + border: 0 none; + cursor: pointer; + outline: none; + background-color: transparent; + background-repeat: no-repeat; + background-attachment: scroll; + background-image: url('data:image/png;base64,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'); + + &.@{treePrefixCls}-icon__customize { + background-image: none; + } + } + &.@{treePrefixCls}-icon_loading { + margin-right: 2px; + vertical-align: top; + background: url('data:image/gif;base64,R0lGODlhEAAQAKIGAMLY8YSx5HOm4Mjc88/g9Ofw+v///wAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFCgAGACwAAAAAEAAQAAADMGi6RbUwGjKIXCAA016PgRBElAVlG/RdLOO0X9nK61W39qvqiwz5Ls/rRqrggsdkAgAh+QQFCgAGACwCAAAABwAFAAADD2hqELAmiFBIYY4MAutdCQAh+QQFCgAGACwGAAAABwAFAAADD1hU1kaDOKMYCGAGEeYFCQAh+QQFCgAGACwKAAIABQAHAAADEFhUZjSkKdZqBQG0IELDQAIAIfkEBQoABgAsCgAGAAUABwAAAxBoVlRKgyjmlAIBqCDCzUoCACH5BAUKAAYALAYACgAHAAUAAAMPaGpFtYYMAgJgLogA610JACH5BAUKAAYALAIACgAHAAUAAAMPCAHWFiI4o1ghZZJB5i0JACH5BAUKAAYALAAABgAFAAcAAAMQCAFmIaEp1motpDQySMNFAgA7') no-repeat scroll 0 0 transparent; + } + &.@{treePrefixCls}-switcher { + &.@{treePrefixCls}-switcher-noop { + cursor: auto; + } + &.@{treePrefixCls}-switcher_open { + background-position: -93px -56px; + } + &.@{treePrefixCls}-switcher_close { + background-position: -75px -56px; + } + } + &.@{treePrefixCls}-checkbox { + width: 13px; + height: 13px; + margin: 0 3px; + background-position: 0 0; + &-checked { + background-position: -14px 0; + } + &-indeterminate { + background-position: -14px -28px; + } + &-disabled { + background-position: 0 -56px; + } + &.@{treePrefixCls}-checkbox-checked.@{treePrefixCls}-checkbox-disabled { + background-position: -14px -56px; + } + &.@{treePrefixCls}-checkbox-indeterminate.@{treePrefixCls}-checkbox-disabled { + position: relative; + background: #ccc; + border-radius: 3px; + &::after { + content: ' '; + -webkit-transform: scale(1); + transform: scale(1); + position: absolute; + left: 3px; + top: 5px; + width: 5px; + height: 0; + border: 2px solid #fff; + border-top: 0; + border-left: 0; + } + } + } + } + } + &:not(.@{treePrefixCls}-show-line) { + .@{treePrefixCls}-switcher-noop { + background: none; + } + } + &.@{treePrefixCls}-show-line { + li:not(:last-child) { + > ul { + background: url('data:image/gif;base64,R0lGODlhCQACAIAAAMzMzP///yH5BAEAAAEALAAAAAAJAAIAAAIEjI9pUAA7') 0 0 repeat-y; + } + > .@{treePrefixCls}-switcher-noop { + background-position: -56px -18px; + } + } + li:last-child { + > .@{treePrefixCls}-switcher-noop { + background-position: -56px -36px; + } + } + } + &-child-tree { + display: none; + &-open { + display: block; + } + } + &-treenode-disabled { + >span:not(.@{treePrefixCls}-switcher), + >a, + >a span { + color: #ccc; + cursor: not-allowed; + } + } + &-node-selected { + background-color: #ffe6b0; + border: 1px #ffb951 solid; + opacity: 0.8; + } + &-icon__open { + margin-right: 2px; + background-position: -110px -16px; + vertical-align: top; + } + &-icon__close { + margin-right: 2px; + background-position: -110px 0; + vertical-align: top; + } + &-icon__docu { + margin-right: 2px; + background-position: -110px -32px; + vertical-align: top; + } + &-icon__customize { + margin-right: 2px; + vertical-align: top; + } +} diff --git a/components/vc-tree/assets/line.gif b/components/vc-tree/assets/line.gif new file mode 100644 index 0000000000000000000000000000000000000000..d561d36a915776730eb3069cee4c949f027667ed GIT binary patch literal 45 xcmZ?wbhEHbT_)p@)n z{^qIIq1(T$e$~zMQ}6t1w^a|Cj;Amo3}FHq!p^`7G=x7ROJIXqM}S9S9}m;(SWbi* zHiVjpkZW$u7K%QH zEX1o> { + const { expandedKeys } = this.state + const { onDragEnter } = this.props + const { pos, eventKey } = node.props + + const dropPosition = calcDropPosition(event, node) + + // Skip if drag node is self + if ( + this.dragNode.props.eventKey === eventKey && + dropPosition === 0 + ) { + this.setState({ + dragOverNodeKey: '', + dropPosition: null, + }) + return + } + + // Ref: https://github.com/react-component/tree/issues/132 + // Add timeout to let onDragLevel fire before onDragEnter, + // so that we can clean drag props for onDragLeave node. + // Macro task for this: + // https://html.spec.whatwg.org/multipage/webappapis.html#clean-up-after-running-script + setTimeout(() => { + // Update drag over node + this.setState({ + dragOverNodeKey: eventKey, + dropPosition, + }) + + // Side effect for delay drag + if (!this.delayedDragEnterLogic) { + this.delayedDragEnterLogic = {} + } + Object.keys(this.delayedDragEnterLogic).forEach((key) => { + clearTimeout(this.delayedDragEnterLogic[key]) + }) + this.delayedDragEnterLogic[pos] = setTimeout(() => { + const newExpandedKeys = arrAdd(expandedKeys, eventKey) + this.setState({ + expandedKeys: newExpandedKeys, + }) + + if (onDragEnter) { + onDragEnter({ event, node, expandedKeys: newExpandedKeys }) + } + }, 400) + }, 0) + }; + onNodeDragOver = (event, node) => { + const { onDragOver } = this.props + if (onDragOver) { + onDragOver({ event, node }) + } + }; + onNodeDragLeave = (event, node) => { + const { onDragLeave } = this.props + + this.setState({ + dragOverNodeKey: '', + }) + + if (onDragLeave) { + onDragLeave({ event, node }) + } + }; + onNodeDragEnd = (event, node) => { + const { onDragEnd } = this.props + this.setState({ + dragOverNodeKey: '', + }) + if (onDragEnd) { + onDragEnd({ event, node }) + } + }; + onNodeDrop = (event, node) => { + const { dragNodesKeys, dropPosition } = this.state + const { onDrop } = this.props + const { eventKey, pos } = node.props + + this.setState({ + dragOverNodeKey: '', + dropNodeKey: eventKey, + }) + + if (dragNodesKeys.indexOf(eventKey) !== -1) { + warning(false, 'Can not drop to dragNode(include it\'s children node)') + return + } + + const posArr = posToArr(pos) + + const dropResult = { + event, + node, + dragNode: this.dragNode, + dragNodesKeys: dragNodesKeys.slice(), + dropPosition: dropPosition + Number(posArr[posArr.length - 1]), + } + + if (dropPosition !== 0) { + dropResult.dropToGap = true + } + + if (onDrop) { + onDrop(dropResult) + } + }; + + onNodeSelect = (e, treeNode) => { + let { selectedKeys } = this.state + const { onSelect, multiple, children } = this.props + const { selected, eventKey } = treeNode.props + const targetSelected = !selected + + // Update selected keys + if (!targetSelected) { + selectedKeys = arrDel(selectedKeys, eventKey) + } else if (!multiple) { + selectedKeys = [eventKey] + } else { + selectedKeys = arrAdd(selectedKeys, eventKey) + } + + // [Legacy] Not found related usage in doc or upper libs + // [Legacy] TODO: add optimize prop to skip node process + const selectedNodes = [] + if (selectedKeys.length) { + traverseTreeNodes(children, ({ node, key }) => { + if (selectedKeys.indexOf(key) !== -1) { + selectedNodes.push(node) + } + }) + } + + this.setUncontrolledState({ selectedKeys }) + + if (onSelect) { + const eventObj = { + event: 'select', + selected: targetSelected, + node: treeNode, + selectedNodes, + } + onSelect(selectedKeys, eventObj) + } + }; + + /** + * This will cache node check status to optimize update process. + * When Tree get trigger `onCheckConductFinished` will flush all the update. + */ + onBatchNodeCheck = (key, checked, halfChecked, startNode) => { + if (startNode) { + this.checkedBatch = { + treeNode: startNode, + checked, + list: [], + } + } + + // This code should never called + if (!this.checkedBatch) { + this.checkedBatch = { + list: [], + } + warning( + false, + 'Checked batch not init. This should be a bug. Please fire a issue.' + ) + } + + this.checkedBatch.list.push({ key, checked, halfChecked }) + }; + + /** + * When top `onCheckConductFinished` called, will execute all batch update. + * And trigger `onCheck` event. + */ + onCheckConductFinished = () => { + const { checkedKeys, halfCheckedKeys } = this.state + const { onCheck, checkStrictly, children } = this.props + + // Use map to optimize update speed + const checkedKeySet = {} + const halfCheckedKeySet = {} + + checkedKeys.forEach(key => { + checkedKeySet[key] = true + }) + halfCheckedKeys.forEach(key => { + halfCheckedKeySet[key] = true + }) + + // Batch process + this.checkedBatch.list.forEach(({ key, checked, halfChecked }) => { + checkedKeySet[key] = checked + halfCheckedKeySet[key] = halfChecked + }) + const newCheckedKeys = Object.keys(checkedKeySet).filter(key => checkedKeySet[key]) + const newHalfCheckedKeys = Object.keys(halfCheckedKeySet).filter(key => halfCheckedKeySet[key]) + + // Trigger onChecked + let selectedObj + + const eventObj = { + event: 'check', + node: this.checkedBatch.treeNode, + checked: this.checkedBatch.checked, + } + + if (checkStrictly) { + selectedObj = getStrictlyValue(newCheckedKeys, newHalfCheckedKeys) + + // [Legacy] TODO: add optimize prop to skip node process + eventObj.checkedNodes = [] + traverseTreeNodes(children, ({ node, key }) => { + if (checkedKeySet[key]) { + eventObj.checkedNodes.push(node) + } + }) + + this.setUncontrolledState({ checkedKeys: newCheckedKeys }) + } else { + selectedObj = newCheckedKeys + + // [Legacy] TODO: add optimize prop to skip node process + eventObj.checkedNodes = [] + eventObj.checkedNodesPositions = [] // [Legacy] TODO: not in API + eventObj.halfCheckedKeys = newHalfCheckedKeys // [Legacy] TODO: not in API + traverseTreeNodes(children, ({ node, pos, key }) => { + if (checkedKeySet[key]) { + eventObj.checkedNodes.push(node) + eventObj.checkedNodesPositions.push({ node, pos }) + } + }) + + this.setUncontrolledState({ + checkedKeys: newCheckedKeys, + halfCheckedKeys: newHalfCheckedKeys, + }) + } + + if (onCheck) { + onCheck(selectedObj, eventObj) + } + + // Clean up + this.checkedBatch = null + }; + + onNodeExpand = (e, treeNode) => { + let { expandedKeys } = this.state + const { onExpand, loadData } = this.props + const { eventKey, expanded } = treeNode.props + + // Update selected keys + const index = expandedKeys.indexOf(eventKey) + const targetExpanded = !expanded + + warning( + (expanded && index !== -1) || (!expanded && index === -1) + , 'Expand state not sync with index check') + + if (targetExpanded) { + expandedKeys = arrAdd(expandedKeys, eventKey) + } else { + expandedKeys = arrDel(expandedKeys, eventKey) + } + + this.setUncontrolledState({ expandedKeys }) + + if (onExpand) { + onExpand(expandedKeys, { node: treeNode, expanded: targetExpanded }) + } + + // Async Load data + if (targetExpanded && loadData) { + return loadData(treeNode).then(() => { + // [Legacy] Refresh logic + this.setUncontrolledState({ expandedKeys }) + }) + } + + return null + }; + + onNodeMouseEnter = (event, node) => { + const { onMouseEnter } = this.props + if (onMouseEnter) { + onMouseEnter({ event, node }) + } + }; + + onNodeMouseLeave = (event, node) => { + const { onMouseLeave } = this.props + if (onMouseLeave) { + onMouseLeave({ event, node }) + } + }; + + onNodeContextMenu = (event, node) => { + const { onRightClick } = this.props + if (onRightClick) { + event.preventDefault() + onRightClick({ event, node }) + } + }; + + /** + * Sync state with props if needed + */ + getSyncProps = (props = {}, prevProps) => { + let needSync = false + const newState = {} + const myPrevProps = prevProps || {} + + function checkSync (name) { + if (props[name] !== myPrevProps[name]) { + needSync = true + return true + } + return false + } + + // Children change will affect check box status. + // And no need to check when prev props not provided + if (prevProps && checkSync('children')) { + const { checkedKeys = [], halfCheckedKeys = [] } = + calcCheckedKeys(props.checkedKeys || this.state.checkedKeys, props) || {} + newState.checkedKeys = checkedKeys + newState.halfCheckedKeys = halfCheckedKeys + } + + if (checkSync('expandedKeys')) { + newState.expandedKeys = calcExpandedKeys(props.expandedKeys, props) + } + + if (checkSync('selectedKeys')) { + newState.selectedKeys = calcSelectedKeys(props.selectedKeys, props) + } + + if (checkSync('checkedKeys')) { + const { checkedKeys = [], halfCheckedKeys = [] } = + calcCheckedKeys(props.checkedKeys, props) || {} + newState.checkedKeys = checkedKeys + newState.halfCheckedKeys = halfCheckedKeys + } + + return needSync ? newState : null + }; + + /** + * Only update the value which is not in props + */ + setUncontrolledState = (state) => { + let needSync = false + const newState = {} + + Object.keys(state).forEach(name => { + if (name in this.props) return + + needSync = true + newState[name] = state[name] + }) + + this.setState(needSync ? newState : null) + }; + + isKeyChecked = (key) => { + const { checkedKeys = [] } = this.state + return checkedKeys.indexOf(key) !== -1 + }; + + /** + * [Legacy] Original logic use `key` as tracking clue. + * We have to use `cloneElement` to pass `key`. + */ + renderTreeNode = (child, index, level = 0) => { + const { + expandedKeys = [], selectedKeys = [], halfCheckedKeys = [], + dragOverNodeKey, dropPosition, + } = this.state + const {} = this.props + const pos = getPosition(level, index) + const key = child.key || pos + + return React.cloneElement(child, { + eventKey: key, + expanded: expandedKeys.indexOf(key) !== -1, + selected: selectedKeys.indexOf(key) !== -1, + checked: this.isKeyChecked(key), + halfChecked: halfCheckedKeys.indexOf(key) !== -1, + pos, + + // [Legacy] Drag props + dragOver: dragOverNodeKey === key && dropPosition === 0, + dragOverGapTop: dragOverNodeKey === key && dropPosition === -1, + dragOverGapBottom: dragOverNodeKey === key && dropPosition === 1, + }) + }; + + render () { + const { + prefixCls, className, focusable, + showLine, + children, + } = this.props + const domProps = {} + + // [Legacy] Commit: 0117f0c9db0e2956e92cb208f51a42387dfcb3d1 + if (focusable) { + domProps.tabIndex = '0' + domProps.onKeyDown = this.onKeyDown + } + + return ( +
    + {React.Children.map(children, this.renderTreeNode, this)} +
+ ) + } +} + +export default Tree diff --git a/components/vc-tree/src/TreeNode.jsx b/components/vc-tree/src/TreeNode.jsx new file mode 100644 index 000000000..155585404 --- /dev/null +++ b/components/vc-tree/src/TreeNode.jsx @@ -0,0 +1,585 @@ +import PropTypes from '../../_util/vue-types' +import classNames from 'classnames' +import warning from 'warning' +import { contextTypes } from './Tree' +import { getPosition, getNodeChildren, isCheckDisabled, traverseTreeNodes } from './util' +import { initDefaultProps, getOptionProps, filterEmpty } from '../../_util/props-util' + +const ICON_OPEN = 'open' +const ICON_CLOSE = 'close' + +const LOAD_STATUS_NONE = 0 +const LOAD_STATUS_LOADING = 1 +const LOAD_STATUS_LOADED = 2 +const LOAD_STATUS_FAILED = 0 // Action align, let's make failed same as init. + +const defaultTitle = '---' + +let onlyTreeNodeWarned = false // Only accept TreeNode + +export const nodeContextTypes = { + ...contextTypes, + rcTreeNode: PropTypes.shape({ + onUpCheckConduct: PropTypes.func, + }), +} + +const TreeNode = { + props: initDefaultProps({ + eventKey: PropTypes.string, // Pass by parent `cloneElement` + prefixCls: PropTypes.string, + // className: PropTypes.string, + root: PropTypes.object, + // onSelect: PropTypes.func, + + // By parent + expanded: PropTypes.bool, + selected: PropTypes.bool, + checked: PropTypes.bool, + halfChecked: PropTypes.bool, + title: PropTypes.node, + pos: PropTypes.string, + dragOver: PropTypes.bool, + dragOverGapTop: PropTypes.bool, + dragOverGapBottom: PropTypes.bool, + + // By user + isLeaf: PropTypes.bool, + selectable: PropTypes.bool, + disabled: PropTypes.bool, + disableCheckbox: PropTypes.bool, + icon: PropTypes.any, + }, { + title: defaultTitle, + }), + + data () { + return { + loadStatus: LOAD_STATUS_NONE, + dragNodeHighlight: false, + } + }, + inject: { + context: { default: {}}, + }, + provide: { + ...this.context, + rcTreeNode: this, + }, + + // Isomorphic needn't load data in server side + mounted () { + this.$nextTick(() => { + this.syncLoadData(this.$props) + }) + }, + + componentWillReceiveProps (nextProps) { + this.syncLoadData(nextProps) + }, + + onUpCheckConduct (treeNode, nodeChecked, nodeHalfChecked) { + const { pos: nodePos } = getOptionProps(treeNode) + const { eventKey, pos, checked, halfChecked } = this + const { + rcTree: { checkStrictly, isKeyChecked, onBatchNodeCheck, onCheckConductFinished }, + rcTreeNode: { onUpCheckConduct } = {}, + } = this.context + + // Stop conduct when current node is disabled + if (isCheckDisabled(this)) { + onCheckConductFinished() + return + } + + const children = this.getNodeChildren() + + let checkedCount = nodeChecked ? 1 : 0 + + // Statistic checked count + children.forEach((node, index) => { + const childPos = getPosition(pos, index) + + if (nodePos === childPos || isCheckDisabled(node)) { + return + } + + if (isKeyChecked(node.key || childPos)) { + checkedCount += 1 + } + }) + + // Static enabled children count + const enabledChildrenCount = children + .filter(node => !isCheckDisabled(node)) + .length + + // checkStrictly will not conduct check status + const nextChecked = checkStrictly ? checked : enabledChildrenCount === checkedCount + const nextHalfChecked = checkStrictly // propagated or child checked + ? halfChecked : (nodeHalfChecked || (checkedCount > 0 && !nextChecked)) + + // Add into batch update + if (checked !== nextChecked || halfChecked !== nextHalfChecked) { + onBatchNodeCheck(eventKey, nextChecked, nextHalfChecked) + + if (onUpCheckConduct) { + onUpCheckConduct(this, nextChecked, nextHalfChecked) + } else { + // Flush all the update + onCheckConductFinished() + } + } else { + // Flush all the update + onCheckConductFinished() + } + }, + + onDownCheckConduct (nodeChecked) { + const { $slots } = this + const children = $slots.default || [] + const { rcTree: { checkStrictly, isKeyChecked, onBatchNodeCheck }} = this.context + if (checkStrictly) return + + traverseTreeNodes(children, ({ node, key }) => { + if (isCheckDisabled(node)) return false + + if (nodeChecked !== isKeyChecked(key)) { + onBatchNodeCheck(key, nodeChecked, false) + } + }) + }, + + onSelectorClick (e) { + if (this.isSelectable()) { + this.onSelect(e) + } else { + this.onCheck(e) + } + }, + + onSelect (e) { + if (this.isDisabled()) return + + const { rcTree: { onNodeSelect }} = this.context + e.preventDefault() + onNodeSelect(e, this) + }, + + onCheck (e) { + if (this.isDisabled()) return + + const { disableCheckbox, checked, eventKey } = this + const { + rcTree: { checkable, onBatchNodeCheck, onCheckConductFinished }, + rcTreeNode: { onUpCheckConduct } = {}, + } = this.context + + if (!checkable || disableCheckbox) return + + e.preventDefault() + const targetChecked = !checked + onBatchNodeCheck(eventKey, targetChecked, false, this) + + // Children conduct + this.onDownCheckConduct(targetChecked) + + // Parent conduct + if (onUpCheckConduct) { + onUpCheckConduct(this, targetChecked, false) + } else { + onCheckConductFinished() + } + }, + + onMouseEnter (e) { + const { rcTree: { onNodeMouseEnter }} = this.context + onNodeMouseEnter(e, this) + }, + + onMouseLeave (e) { + const { rcTree: { onNodeMouseLeave }} = this.context + onNodeMouseLeave(e, this) + }, + + onContextMenu (e) { + const { rcTree: { onNodeContextMenu }} = this.context + onNodeContextMenu(e, this) + }, + + onDragStart (e) { + const { rcTree: { onNodeDragStart }} = this.context + + e.stopPropagation() + this.setState({ + dragNodeHighlight: true, + }) + onNodeDragStart(e, this) + + try { + // ie throw error + // firefox-need-it + e.dataTransfer.setData('text/plain', '') + } catch (error) { + // empty + } + }, + + onDragEnter (e) { + const { rcTree: { onNodeDragEnter }} = this.context + + e.preventDefault() + e.stopPropagation() + onNodeDragEnter(e, this) + }, + + onDragOver (e) { + const { rcTree: { onNodeDragOver }} = this.context + + e.preventDefault() + e.stopPropagation() + onNodeDragOver(e, this) + }, + + onDragLeave (e) { + const { rcTree: { onNodeDragLeave }} = this.context + + e.stopPropagation() + onNodeDragLeave(e, this) + }, + + onDragEnd (e) { + const { rcTree: { onNodeDragEnd }} = this.context + + e.stopPropagation() + this.setState({ + dragNodeHighlight: false, + }) + onNodeDragEnd(e, this) + }, + + onDrop (e) { + const { rcTree: { onNodeDrop }} = this.context + + e.preventDefault() + e.stopPropagation() + this.setState({ + dragNodeHighlight: false, + }) + onNodeDrop(e, this) + }, + + // Disabled item still can be switch + onExpand (e) { + const { rcTree: { onNodeExpand }} = this.context + const callbackPromise = onNodeExpand(e, this) + + // Promise like + if (callbackPromise && callbackPromise.then) { + this.setState({ loadStatus: LOAD_STATUS_LOADING }) + + callbackPromise.then(() => { + this.setState({ loadStatus: LOAD_STATUS_LOADED }) + }).catch(() => { + this.setState({ loadStatus: LOAD_STATUS_FAILED }) + }) + } + }, + + // Drag usage + setSelectHandle (node) { + this.selectHandle = node + }, + + getNodeChildren () { + const { $slots: { default: children }} = this + const originList = filterEmpty(children) + const targetList = getNodeChildren(originList) + + if (originList.length !== targetList.length && !onlyTreeNodeWarned) { + onlyTreeNodeWarned = true + warning(false, 'Tree only accept TreeNode as children.') + } + + return targetList + }, + + getNodeState () { + const { expanded } = this + + if (this.isLeaf()) { + return null + } + + return expanded ? ICON_OPEN : ICON_CLOSE + }, + + isLeaf () { + const { isLeaf, loadStatus } = this + const { rcTree: { loadData }} = this.context + + const hasChildren = this.getNodeChildren().length !== 0 + + return ( + isLeaf || + (!loadData && !hasChildren) || + (loadData && loadStatus === LOAD_STATUS_LOADED && !hasChildren) + ) + }, + + isDisabled () { + const { disabled } = this + const { rcTree: { disabled: treeDisabled }} = this.context + + // Follow the logic of Selectable + if (disabled === false) { + return false + } + + return !!(treeDisabled || disabled) + }, + + isSelectable () { + const { selectable } = this + const { rcTree: { selectable: treeSelectable }} = this.context + + // Ignore when selectable is undefined or null + if (typeof selectable === 'boolean') { + return selectable + } + + return treeSelectable + }, + + // Load data to avoid default expanded tree without data + syncLoadData (props) { + const { loadStatus } = this + const { expanded } = props + const { rcTree: { loadData }} = this.context + + if (loadData && loadStatus === LOAD_STATUS_NONE && expanded && !this.isLeaf()) { + this.setState({ loadStatus: LOAD_STATUS_LOADING }) + + loadData(this).then(() => { + this.setState({ loadStatus: LOAD_STATUS_LOADED }) + }).catch(() => { + this.setState({ loadStatus: LOAD_STATUS_FAILED }) + }) + } + }, + + // Switcher + renderSwitcher () { + const { expanded } = this + const { rcTree: { prefixCls }} = this.context + + if (this.isLeaf()) { + return + } + + return ( + + ) + }, + + // Checkbox + renderCheckbox () { + const { checked, halfChecked, disableCheckbox } = this + const { rcTree: { prefixCls, checkable }} = this.context + const disabled = this.isDisabled() + + if (!checkable) return null + + // [Legacy] Custom element should be separate with `checkable` in future + const $custom = typeof checkable !== 'boolean' ? checkable : null + + return ( + + {$custom} + + ) + }, + + renderIcon () { + const { loadStatus } = this + const { rcTree: { prefixCls }} = this.context + + return ( + + ) + }, + + // Icon + Title + renderSelector () { + const { title, selected, icon, loadStatus, dragNodeHighlight } = this + const { rcTree: { prefixCls, showIcon, draggable, loadData }} = this.context + const disabled = this.isDisabled() + + const wrapClass = `${prefixCls}-node-content-wrapper` + + // Icon - Still show loading icon when loading without showIcon + let $icon + + if (showIcon) { + $icon = icon ? ( + + {typeof icon === 'function' + ? icon(this.$props) : icon} + + ) : this.renderIcon() + } else if (loadData && loadStatus === LOAD_STATUS_LOADING) { + $icon = this.renderIcon() + } + + // Title + const $title = {title} + + return ( + + {$icon}{$title} + + ) + }, + + // Children list wrapped with `Animation` + renderChildren () { + const { expanded, pos } = this + const { rcTree: { + prefixCls, + openTransitionName, openAnimation, + renderTreeNode, + }} = this.context + + // [Legacy] Animation control + const renderFirst = this.renderFirst + this.renderFirst = 1 + let transitionAppear = true + if (!renderFirst && expanded) { + transitionAppear = false + } + + const animProps = {} + if (openTransitionName) { + animProps.transitionName = openTransitionName + } else if (typeof openAnimation === 'object') { + animProps.animation = { ...openAnimation } + if (!transitionAppear) { + delete animProps.animation.appear + } + } + + // Children TreeNode + const nodeList = this.getNodeChildren() + + if (nodeList.length === 0) { + return null + } + + let $children + if (expanded) { + $children = ( +
    + {nodeList.map((node, index) => ( + renderTreeNode(node, index, pos) + ))} +
+ ) + } + + return ( + + {$children} + + ) + }, + + render () { + const { + dragOver, dragOverGapTop, dragOverGapBottom, + } = this + const { rcTree: { + prefixCls, + filterTreeNode, + }} = this.context + const disabled = this.isDisabled() + + return ( +
  • + {this.renderSwitcher()} + {this.renderCheckbox()} + {this.renderSelector()} + {this.renderChildren()} +
  • + ) + }, +} + +TreeNode.isTreeNode = 1 + +export default TreeNode diff --git a/components/vc-tree/src/index.js b/components/vc-tree/src/index.js new file mode 100644 index 000000000..d053f4c9f --- /dev/null +++ b/components/vc-tree/src/index.js @@ -0,0 +1,6 @@ +import Tree from './Tree' +import TreeNode from './TreeNode' +Tree.TreeNode = TreeNode + +export { TreeNode } +export default Tree diff --git a/components/vc-tree/src/util.js b/components/vc-tree/src/util.js new file mode 100644 index 000000000..41e39ed08 --- /dev/null +++ b/components/vc-tree/src/util.js @@ -0,0 +1,399 @@ +/* eslint no-loop-func: 0*/ +import { Children } from 'react' +import warning from 'warning' + +export function arrDel (list, value) { + const clone = list.slice() + const index = clone.indexOf(value) + if (index >= 0) { + clone.splice(index, 1) + } + return clone +} + +export function arrAdd (list, value) { + const clone = list.slice() + if (clone.indexOf(value) === -1) { + clone.push(value) + } + return clone +} + +export function posToArr (pos) { + return pos.split('-') +} + +// Only used when drag, not affect SSR. +export function getOffset (ele) { + if (!ele.getClientRects().length) { + return { top: 0, left: 0 } + } + + const rect = ele.getBoundingClientRect() + if (rect.width || rect.height) { + const doc = ele.ownerDocument + const win = doc.defaultView + const docElem = doc.documentElement + + return { + top: rect.top + win.pageYOffset - docElem.clientTop, + left: rect.left + win.pageXOffset - docElem.clientLeft, + } + } + + return rect +} + +export function getPosition (level, index) { + return `${level}-${index}` +} + +export function getNodeChildren (children) { + const childList = Array.isArray(children) ? children : [children] + return childList + .filter(child => child && child.type && child.type.isTreeNode) +} + +export function isCheckDisabled (node) { + const { disabled, disableCheckbox } = node.props || {} + return !!(disabled || disableCheckbox) +} + +export function traverseTreeNodes (treeNodes, subTreeData, callback) { + if (typeof subTreeData === 'function') { + callback = subTreeData + subTreeData = false + } + + function processNode (node, index, parent) { + const children = node ? node.props.children : treeNodes + const pos = node ? getPosition(parent.pos, index) : 0 + + // Filter children + const childList = getNodeChildren(children) + + // Process node if is not root + if (node) { + const data = { + node, + index, + pos, + key: node.key || pos, + parentPos: parent.node ? parent.pos : null, + } + + // Children data is not must have + if (subTreeData) { + // Statistic children + const subNodes = [] + Children.forEach(childList, (subNode, subIndex) => { + // Provide limit snapshot + const subPos = getPosition(pos, index) + subNodes.push({ + node: subNode, + key: subNode.key || subPos, + pos: subPos, + index: subIndex, + }) + }) + data.subNodes = subNodes + } + + // Can break traverse by return false + if (callback(data) === false) { + return + } + } + + // Process children node + Children.forEach(childList, (subNode, subIndex) => { + processNode(subNode, subIndex, { node, pos }) + }) + } + + processNode(null) +} + +/** + * [Legacy] Return halfChecked when it has value. + * @param checkedKeys + * @param halfChecked + * @returns {*} + */ +export function getStrictlyValue (checkedKeys, halfChecked) { + if (halfChecked) { + return { checked: checkedKeys, halfChecked } + } + return checkedKeys +} + +export function getFullKeyList (treeNodes) { + const keyList = [] + traverseTreeNodes(treeNodes, ({ key }) => { + keyList.push(key) + }) + return keyList +} + +/** + * Check position relation. + * @param parentPos + * @param childPos + * @param directly only directly parent can be true + * @returns {boolean} + */ +export function isParent (parentPos, childPos, directly = false) { + if (!parentPos || !childPos || parentPos.length > childPos.length) return false + + const parentPath = posToArr(parentPos) + const childPath = posToArr(childPos) + + // Directly check + if (directly && parentPath.length !== childPath.length - 1) return false + + const len = parentPath.length + for (let i = 0; i < len; i += 1) { + if (parentPath[i] !== childPath[i]) return false + } + + return true +} + +/** + * Statistic TreeNodes info + * @param treeNodes + * @returns {{}} + */ +export function getNodesStatistic (treeNodes) { + const statistic = { + keyNodes: {}, + posNodes: {}, + nodeList: [], + } + + traverseTreeNodes(treeNodes, true, ({ node, index, pos, key, subNodes, parentPos }) => { + const data = { node, index, pos, key, subNodes, parentPos } + statistic.keyNodes[key] = data + statistic.posNodes[pos] = data + statistic.nodeList.push(data) + }) + + return statistic +} + +export function getDragNodesKeys (treeNodes, node) { + const { eventKey, pos } = node.props + const dragNodesKeys = [] + + traverseTreeNodes(treeNodes, ({ pos: nodePos, key }) => { + if (isParent(pos, nodePos)) { + dragNodesKeys.push(key) + } + }) + dragNodesKeys.push(eventKey || pos) + return dragNodesKeys +} + +export function calcDropPosition (event, treeNode) { + const offsetTop = getOffset(treeNode.selectHandle).top + const offsetHeight = treeNode.selectHandle.offsetHeight + const pageY = event.pageY + const gapHeight = 2 // [Legacy] TODO: remove hard code + if (pageY > offsetTop + offsetHeight - gapHeight) { + return 1 + } + if (pageY < offsetTop + gapHeight) { + return -1 + } + return 0 +} + +/** + * Auto expand all related node when sub node is expanded + * @param keyList + * @param props + * @returns [string] + */ +export function calcExpandedKeys (keyList, props) { + if (!keyList) { + return [] + } + + const { autoExpandParent, children } = props + + // Do nothing if not auto expand parent + if (!autoExpandParent) { + return keyList + } + + // Fill parent expanded keys + const { keyNodes, nodeList } = getNodesStatistic(children) + const needExpandKeys = {} + const needExpandPathList = [] + + // Fill expanded nodes + keyList.forEach((key) => { + const node = keyNodes[key] + if (node) { + needExpandKeys[key] = true + needExpandPathList.push(node.pos) + } + }) + + // Match parent by path + nodeList.forEach(({ pos, key }) => { + if (needExpandPathList.some(childPos => isParent(pos, childPos))) { + needExpandKeys[key] = true + } + }) + + const calcExpandedKeyList = Object.keys(needExpandKeys) + + // [Legacy] Return origin keyList if calc list is empty + return calcExpandedKeyList.length ? calcExpandedKeyList : keyList +} + +/** + * Return selectedKeys according with multiple prop + * @param selectedKeys + * @param props + * @returns [string] + */ +export function calcSelectedKeys (selectedKeys, props) { + if (!selectedKeys) { + return undefined + } + + const { multiple } = props + if (multiple) { + return selectedKeys.slice() + } + + if (selectedKeys.length) { + return [selectedKeys[0]] + } + return selectedKeys +} + +/** + * Check conduct is by key level. It pass though up & down. + * When conduct target node is check means already conducted will be skip. + * @param treeNodes + * @param checkedKeys + * @returns {{checkedKeys: Array, halfCheckedKeys: Array}} + */ +export function calcCheckStateConduct (treeNodes, checkedKeys) { + const { keyNodes, posNodes } = getNodesStatistic(treeNodes) + + const tgtCheckedKeys = {} + const tgtHalfCheckedKeys = {} + + // Conduct up + function conductUp (key, halfChecked) { + if (tgtCheckedKeys[key]) return + + const { subNodes = [], parentPos, node } = keyNodes[key] + if (isCheckDisabled(node)) return + + const allSubChecked = !halfChecked && subNodes + .filter(sub => !isCheckDisabled(sub.node)) + .every(sub => tgtCheckedKeys[sub.key]) + + if (allSubChecked) { + tgtCheckedKeys[key] = true + } else { + tgtHalfCheckedKeys[key] = true + } + + if (parentPos !== null) { + conductUp(posNodes[parentPos].key, !allSubChecked) + } + } + + // Conduct down + function conductDown (key) { + if (tgtCheckedKeys[key]) return + const { subNodes = [], node } = keyNodes[key] + + if (isCheckDisabled(node)) return + + tgtCheckedKeys[key] = true + + subNodes.forEach((sub) => { + conductDown(sub.key) + }) + } + + function conduct (key) { + if (!keyNodes[key]) { + warning(false, `'${key}' does not exist in the tree.`) + return + } + + const { subNodes = [], parentPos, node } = keyNodes[key] + if (isCheckDisabled(node)) return + + tgtCheckedKeys[key] = true + + // Conduct down + subNodes + .filter(sub => !isCheckDisabled(sub.node)) + .forEach((sub) => { + conductDown(sub.key) + }) + + // Conduct up + if (parentPos !== null) { + conductUp(posNodes[parentPos].key) + } + } + + checkedKeys.forEach((key) => { + conduct(key) + }) + + return { + checkedKeys: Object.keys(tgtCheckedKeys), + halfCheckedKeys: Object.keys(tgtHalfCheckedKeys) + .filter(key => !tgtCheckedKeys[key]), + } +} + +/** + * Calculate the value of checked and halfChecked keys. + * This should be only run in init or props changed. + */ +export function calcCheckedKeys (keys, props) { + const { checkable, children, checkStrictly } = props + + if (!checkable || !keys) { + return null + } + + // Convert keys to object format + let keyProps + if (Array.isArray(keys)) { + // [Legacy] Follow the api doc + keyProps = { + checkedKeys: keys, + halfCheckedKeys: undefined, + } + } else if (typeof keys === 'object') { + keyProps = { + checkedKeys: keys.checked || undefined, + halfCheckedKeys: keys.halfChecked || undefined, + } + } else { + warning(false, '`CheckedKeys` is not an array or an object') + return null + } + + // Do nothing if is checkStrictly mode + if (checkStrictly) { + return keyProps + } + + // Conduct calculate the check status + const { checkedKeys = [] } = keyProps + return calcCheckStateConduct(children, checkedKeys) +} From 2ae63c13df90f87ff811be115244a8fdc3671d0c Mon Sep 17 00:00:00 2001 From: tjz <415800467@qq.com> Date: Sat, 24 Mar 2018 17:12:22 +0800 Subject: [PATCH 2/8] add vc-tree --- components/vc-tree/demo/basic.jsx | 110 ++++ components/vc-tree/demo/basic.less | 6 + components/vc-tree/src/Tree.jsx | 830 ++++++++++++++------------- components/vc-tree/src/TreeNode.jsx | 848 ++++++++++++++-------------- components/vc-tree/src/index.js | 21 +- components/vc-tree/src/util.js | 27 +- examples/routes.js | 2 +- 7 files changed, 987 insertions(+), 857 deletions(-) create mode 100644 components/vc-tree/demo/basic.jsx create mode 100644 components/vc-tree/demo/basic.less diff --git a/components/vc-tree/demo/basic.jsx b/components/vc-tree/demo/basic.jsx new file mode 100644 index 000000000..322d26d96 --- /dev/null +++ b/components/vc-tree/demo/basic.jsx @@ -0,0 +1,110 @@ +/* eslint no-console:0 */ +/* eslint no-alert:0 */ +import PropTypes from '../../_util/vue-types' +import Tree, { TreeNode } from '../index' +import '../assets/index.less' +import './basic.less' + +export default { + props: { + keys: PropTypes.array.def(['0-0-0-0']), + }, + data () { + const keys = this.keys + return { + defaultExpandedKeys: keys, + defaultSelectedKeys: keys, + defaultCheckedKeys: keys, + switchIt: true, + showMore: false, + } + }, + methods: { + onExpand (expandedKeys) { + console.log('onExpand', expandedKeys, arguments) + }, + onSelect (selectedKeys, info) { + console.log('selected', selectedKeys, info) + this.selKey = info.node.$options.propsData.eventKey + }, + onCheck (checkedKeys, info) { + console.log('onCheck', checkedKeys, info) + }, + onEdit () { + setTimeout(() => { + console.log('current key: ', this.selKey) + }, 0) + }, + onDel (e) { + if (!window.confirm('sure to delete?')) { + return + } + e.stopPropagation() + }, + toggleChildren () { + this.showMore = !this.showMore + }, + }, + + render () { + const customLabel = ( + operations: + Edit  +   + Delete + ) + return (
    +

    simple

    + {/* + + + + + + + + + + + + + + + */} + +

    Check on Click TreeNode

    + + + + + + + + {this.showMore ? + + + : null} + + +
    ) + }, +} + diff --git a/components/vc-tree/demo/basic.less b/components/vc-tree/demo/basic.less new file mode 100644 index 000000000..cca0a1860 --- /dev/null +++ b/components/vc-tree/demo/basic.less @@ -0,0 +1,6 @@ +.rc-tree li a.rc-tree-node-selected{ + .cus-label { + background-color: white; + border: none; + } +} diff --git a/components/vc-tree/src/Tree.jsx b/components/vc-tree/src/Tree.jsx index 53a1b9646..6c042a530 100644 --- a/components/vc-tree/src/Tree.jsx +++ b/components/vc-tree/src/Tree.jsx @@ -1,7 +1,9 @@ import PropTypes from '../../_util/vue-types' import classNames from 'classnames' import warning from 'warning' -import { initDefaultProps, getOptionProps, filterEmpty } from '../../_util/props-util' +import { initDefaultProps, getOptionProps } from '../../_util/props-util' +import { cloneElement } from '../../_util/vnode' +import BaseMixin from '../../_util/BaseMixin' import { traverseTreeNodes, getStrictlyValue, getFullKeyList, getPosition, getDragNodesKeys, @@ -54,6 +56,8 @@ export const contextTypes = { } const Tree = { + name: 'Tree', + mixins: [BaseMixin], props: initDefaultProps({ prefixCls: PropTypes.string, showLine: PropTypes.bool, @@ -79,9 +83,9 @@ const Tree = { ]), defaultSelectedKeys: PropTypes.arrayOf(PropTypes.string), selectedKeys: PropTypes.arrayOf(PropTypes.string), - //onExpand: PropTypes.func, - //onCheck: PropTypes.func, - //onSelect: PropTypes.func, + // onExpand: PropTypes.func, + // onCheck: PropTypes.func, + // onSelect: PropTypes.func, loadData: PropTypes.func, // onMouseEnter: PropTypes.func, // onMouseLeave: PropTypes.func, @@ -95,6 +99,7 @@ const Tree = { filterTreeNode: PropTypes.func, openTransitionName: PropTypes.string, openAnimation: PropTypes.oneOfType([PropTypes.string, PropTypes.object]), + children: PropTypes.any, }, { prefixCls: 'rc-tree', showLine: false, @@ -122,489 +127,476 @@ const Tree = { defaultCheckedKeys, defaultSelectedKeys, } = props - + const children = this.$slots.default // Sync state with props const { checkedKeys = [], halfCheckedKeys = [] } = - calcCheckedKeys(defaultCheckedKeys, props) || {} + calcCheckedKeys(defaultCheckedKeys, props, children) || {} // Cache for check status to optimize this.checkedBatch = null - + this.propsToStateMap = { + expandedKeys: 'sExpandedKeys', + selectedKeys: 'sSelectedKeys', + checkedKeys: 'sCheckedKeys', + halfCheckedKeys: 'sHalfCheckedKeys', + } return { sExpandedKeys: defaultExpandAll - ? getFullKeyList(this.$slots.default) - : calcExpandedKeys(defaultExpandedKeys, props), - sSelectedKeys: calcSelectedKeys(defaultSelectedKeys, props), - sCheckedKeys, - sHalfCheckedKeys, + ? getFullKeyList(children) + : calcExpandedKeys(defaultExpandedKeys, props, children), + sSelectedKeys: calcSelectedKeys(defaultSelectedKeys, props, children), + sCheckedKeys: checkedKeys, + sHalfCheckedKeys: halfCheckedKeys, ...(this.getSyncProps(props) || {}), - } + dragOverNodeKey: '', + dropPosition: null, + } }, - provide: { - rcTree: this, - }, - - - componentWillReceiveProps (nextProps) { - // React 16 will not trigger update if new state is null - this.setState(this.getSyncProps(nextProps, this.props)) - }, - - onNodeDragStart (event, node) { - const { expandedKeys } = this.state - const { onDragStart } = this.props - const { eventKey, children } = node.props - - this.dragNode = node - - this.setState({ - dragNodesKeys: getDragNodesKeys(children, node), - expandedKeys: arrDel(expandedKeys, eventKey), - }) - - if (onDragStart) { - onDragStart({ event, node }) + provide () { + return { + vcTree: this, } }, - /** - * [Legacy] Select handler is less small than node, - * so that this will trigger when drag enter node or select handler. - * This is a little tricky if customize css without padding. - * Better for use mouse move event to refresh drag state. - * But let's just keep it to avoid event trigger logic change. - */ - onNodeDragEnter = (event, node) => { - const { expandedKeys } = this.state - const { onDragEnter } = this.props - const { pos, eventKey } = node.props + watch: { + children (val) { + const { checkedKeys = [], halfCheckedKeys = [] } = calcCheckedKeys(this.checkedKeys || this.sCheckedKeys, this.$props, this.$slots.default) || {} + this.sCheckedKeys = checkedKeys + this.sHalfCheckedKeys = halfCheckedKeys + }, + expandedKeys (val) { + this.sExpandedKeys = calcExpandedKeys(this.expandedKeys, this.$props, this.$slots.default) + }, + selectedKeys (val) { + this.sSelectedKeys = calcSelectedKeys(this.selectedKeys, this.$props, this.$slots.default) + }, + checkedKeys (val) { + const { checkedKeys = [], halfCheckedKeys = [] } = calcCheckedKeys(this.checkedKeys, this.$props, this.$slots.default) || {} + this.sCheckedKeys = checkedKeys + this.sHalfCheckedKeys = halfCheckedKeys + }, + }, - const dropPosition = calcDropPosition(event, node) + // componentWillReceiveProps (nextProps) { + // // React 16 will not trigger update if new state is null + // this.setState(this.getSyncProps(nextProps, this.props)) + // }, + + methods: { + onNodeDragStart (event, node) { + const { sExpandedKeys } = this + const { eventKey, children } = node.props + + this.dragNode = node - // Skip if drag node is self - if ( - this.dragNode.props.eventKey === eventKey && - dropPosition === 0 - ) { this.setState({ - dragOverNodeKey: '', - dropPosition: null, + dragNodesKeys: getDragNodesKeys(children, node), + sExpandedKeys: arrDel(sExpandedKeys, eventKey), }) - return - } + this.__emit('dragstart', { event, node }) + }, - // Ref: https://github.com/react-component/tree/issues/132 - // Add timeout to let onDragLevel fire before onDragEnter, - // so that we can clean drag props for onDragLeave node. - // Macro task for this: - // https://html.spec.whatwg.org/multipage/webappapis.html#clean-up-after-running-script - setTimeout(() => { - // Update drag over node - this.setState({ - dragOverNodeKey: eventKey, - dropPosition, - }) + /** + * [Legacy] Select handler is less small than node, + * so that this will trigger when drag enter node or select handler. + * This is a little tricky if customize css without padding. + * Better for use mouse move event to refresh drag state. + * But let's just keep it to avoid event trigger logic change. + */ + onNodeDragEnter (event, node) { + const { sExpandedKeys } = this + const { pos, eventKey } = node.props - // Side effect for delay drag - if (!this.delayedDragEnterLogic) { - this.delayedDragEnterLogic = {} - } - Object.keys(this.delayedDragEnterLogic).forEach((key) => { - clearTimeout(this.delayedDragEnterLogic[key]) - }) - this.delayedDragEnterLogic[pos] = setTimeout(() => { - const newExpandedKeys = arrAdd(expandedKeys, eventKey) + const dropPosition = calcDropPosition(event, node) + + // Skip if drag node is self + if ( + this.dragNode.props.eventKey === eventKey && + dropPosition === 0 + ) { this.setState({ - expandedKeys: newExpandedKeys, + dragOverNodeKey: '', + dropPosition: null, + }) + return + } + + // Ref: https://github.com/react-component/tree/issues/132 + // Add timeout to let onDragLevel fire before onDragEnter, + // so that we can clean drag props for onDragLeave node. + // Macro task for this: + // https://html.spec.whatwg.org/multipage/webappapis.html#clean-up-after-running-script + setTimeout(() => { + // Update drag over node + this.setState({ + dragOverNodeKey: eventKey, + dropPosition, }) - if (onDragEnter) { - onDragEnter({ event, node, expandedKeys: newExpandedKeys }) - } - }, 400) - }, 0) - }; - onNodeDragOver = (event, node) => { - const { onDragOver } = this.props - if (onDragOver) { - onDragOver({ event, node }) - } - }; - onNodeDragLeave = (event, node) => { - const { onDragLeave } = this.props - - this.setState({ - dragOverNodeKey: '', - }) - - if (onDragLeave) { - onDragLeave({ event, node }) - } - }; - onNodeDragEnd = (event, node) => { - const { onDragEnd } = this.props - this.setState({ - dragOverNodeKey: '', - }) - if (onDragEnd) { - onDragEnd({ event, node }) - } - }; - onNodeDrop = (event, node) => { - const { dragNodesKeys, dropPosition } = this.state - const { onDrop } = this.props - const { eventKey, pos } = node.props - - this.setState({ - dragOverNodeKey: '', - dropNodeKey: eventKey, - }) - - if (dragNodesKeys.indexOf(eventKey) !== -1) { - warning(false, 'Can not drop to dragNode(include it\'s children node)') - return - } - - const posArr = posToArr(pos) - - const dropResult = { - event, - node, - dragNode: this.dragNode, - dragNodesKeys: dragNodesKeys.slice(), - dropPosition: dropPosition + Number(posArr[posArr.length - 1]), - } - - if (dropPosition !== 0) { - dropResult.dropToGap = true - } - - if (onDrop) { - onDrop(dropResult) - } - }; - - onNodeSelect = (e, treeNode) => { - let { selectedKeys } = this.state - const { onSelect, multiple, children } = this.props - const { selected, eventKey } = treeNode.props - const targetSelected = !selected - - // Update selected keys - if (!targetSelected) { - selectedKeys = arrDel(selectedKeys, eventKey) - } else if (!multiple) { - selectedKeys = [eventKey] - } else { - selectedKeys = arrAdd(selectedKeys, eventKey) - } - - // [Legacy] Not found related usage in doc or upper libs - // [Legacy] TODO: add optimize prop to skip node process - const selectedNodes = [] - if (selectedKeys.length) { - traverseTreeNodes(children, ({ node, key }) => { - if (selectedKeys.indexOf(key) !== -1) { - selectedNodes.push(node) + // Side effect for delay drag + if (!this.delayedDragEnterLogic) { + this.delayedDragEnterLogic = {} } + Object.keys(this.delayedDragEnterLogic).forEach((key) => { + clearTimeout(this.delayedDragEnterLogic[key]) + }) + this.delayedDragEnterLogic[pos] = setTimeout(() => { + const newExpandedKeys = arrAdd(sExpandedKeys, eventKey) + this.setState({ + sExpandedKeys: newExpandedKeys, + }) + this.__emit('dragenter', { event, node, expandedKeys: newExpandedKeys }) + }, 400) + }, 0) + }, + onNodeDragOver (event, node) { + this.__emit('dragover', { event, node }) + }, + onNodeDragLeave (event, node) { + this.setState({ + dragOverNodeKey: '', }) - } + this.__emit('dragleave', { event, node }) + }, + onNodeDragEnd (event, node) { + this.setState({ + dragOverNodeKey: '', + }) + this.__emit('dragend', { event, node }) + }, + onNodeDrop (event, node) { + const { dragNodesKeys, dropPosition } = this - this.setUncontrolledState({ selectedKeys }) + const { eventKey, pos } = node.props + + this.setState({ + dragOverNodeKey: '', + dropNodeKey: eventKey, + }) + + if (dragNodesKeys.indexOf(eventKey) !== -1) { + warning(false, 'Can not drop to dragNode(include it\'s children node)') + return + } + + const posArr = posToArr(pos) + + const dropResult = { + event, + node, + dragNode: this.dragNode, + dragNodesKeys: dragNodesKeys.slice(), + dropPosition: dropPosition + Number(posArr[posArr.length - 1]), + } + + if (dropPosition !== 0) { + dropResult.dropToGap = true + } + this.__emit('drop', dropResult) + }, + + onNodeSelect (e, treeNode) { + const { sSelectedKeys, multiple, $slots: { default: children }} = this + const { selected, eventKey } = getOptionProps(treeNode) + const targetSelected = !selected + let selectedKeys = sSelectedKeys + // Update selected keys + if (!targetSelected) { + selectedKeys = arrDel(selectedKeys, eventKey) + } else if (!multiple) { + selectedKeys = [eventKey] + } else { + selectedKeys = arrAdd(selectedKeys, eventKey) + } + + // [Legacy] Not found related usage in doc or upper libs + // [Legacy] TODO: add optimize prop to skip node process + const selectedNodes = [] + if (selectedKeys.length) { + traverseTreeNodes(children, ({ node, key }) => { + if (selectedKeys.indexOf(key) !== -1) { + selectedNodes.push(node) + } + }) + } + + this.setUncontrolledState({ selectedKeys }) - if (onSelect) { const eventObj = { event: 'select', selected: targetSelected, node: treeNode, selectedNodes, } - onSelect(selectedKeys, eventObj) - } - }; + this.__emit('select', selectedKeys, eventObj) + }, - /** - * This will cache node check status to optimize update process. - * When Tree get trigger `onCheckConductFinished` will flush all the update. - */ - onBatchNodeCheck = (key, checked, halfChecked, startNode) => { - if (startNode) { - this.checkedBatch = { - treeNode: startNode, - checked, - list: [], + /** + * This will cache node check status to optimize update process. + * When Tree get trigger `onCheckConductFinished` will flush all the update. + */ + onBatchNodeCheck (key, checked, halfChecked, startNode) { + if (startNode) { + this.checkedBatch = { + treeNode: startNode, + checked, + list: [], + } } - } - // This code should never called - if (!this.checkedBatch) { - this.checkedBatch = { - list: [], + // This code should never called + if (!this.checkedBatch) { + this.checkedBatch = { + list: [], + } + warning( + false, + 'Checked batch not init. This should be a bug. Please fire a issue.' + ) } + + this.checkedBatch.list.push({ key, checked, halfChecked }) + }, + + /** + * When top `onCheckConductFinished` called, will execute all batch update. + * And trigger `onCheck` event. + */ + onCheckConductFinished () { + const { sCheckedKeys, sHalfCheckedKeys, checkStrictly, $slots: { default: children }} = this + + // Use map to optimize update speed + const checkedKeySet = {} + const halfCheckedKeySet = {} + + sCheckedKeys.forEach(key => { + checkedKeySet[key] = true + }) + sHalfCheckedKeys.forEach(key => { + halfCheckedKeySet[key] = true + }) + + // Batch process + this.checkedBatch.list.forEach(({ key, checked, halfChecked }) => { + checkedKeySet[key] = checked + halfCheckedKeySet[key] = halfChecked + }) + const newCheckedKeys = Object.keys(checkedKeySet).filter(key => checkedKeySet[key]) + const newHalfCheckedKeys = Object.keys(halfCheckedKeySet).filter(key => halfCheckedKeySet[key]) + + // Trigger onChecked + let selectedObj + + const eventObj = { + event: 'check', + node: this.checkedBatch.treeNode, + checked: this.checkedBatch.checked, + } + + if (checkStrictly) { + selectedObj = getStrictlyValue(newCheckedKeys, newHalfCheckedKeys) + + // [Legacy] TODO: add optimize prop to skip node process + eventObj.checkedNodes = [] + traverseTreeNodes(children, ({ node, key }) => { + if (checkedKeySet[key]) { + eventObj.checkedNodes.push(node) + } + }) + + this.setUncontrolledState({ checkedKeys: newCheckedKeys }) + } else { + selectedObj = newCheckedKeys + + // [Legacy] TODO: add optimize prop to skip node process + eventObj.checkedNodes = [] + eventObj.checkedNodesPositions = [] // [Legacy] TODO: not in API + eventObj.halfCheckedKeys = newHalfCheckedKeys // [Legacy] TODO: not in API + traverseTreeNodes(children, ({ node, pos, key }) => { + if (checkedKeySet[key]) { + eventObj.checkedNodes.push(node) + eventObj.checkedNodesPositions.push({ node, pos }) + } + }) + + this.setUncontrolledState({ + checkedKeys: newCheckedKeys, + halfCheckedKeys: newHalfCheckedKeys, + }) + } + this.__emit('check', selectedObj, eventObj) + + // Clean up + this.checkedBatch = null + }, + + onNodeExpand (e, treeNode) { + const { sExpandedKeys, loadData } = this + let expandedKeys = [...sExpandedKeys] + const { eventKey, expanded } = getOptionProps(treeNode) + + // Update selected keys + const index = expandedKeys.indexOf(eventKey) + const targetExpanded = !expanded + warning( - false, - 'Checked batch not init. This should be a bug. Please fire a issue.' - ) - } + (expanded && index !== -1) || (!expanded && index === -1) + , 'Expand state not sync with index check') - this.checkedBatch.list.push({ key, checked, halfChecked }) - }; - - /** - * When top `onCheckConductFinished` called, will execute all batch update. - * And trigger `onCheck` event. - */ - onCheckConductFinished = () => { - const { checkedKeys, halfCheckedKeys } = this.state - const { onCheck, checkStrictly, children } = this.props - - // Use map to optimize update speed - const checkedKeySet = {} - const halfCheckedKeySet = {} - - checkedKeys.forEach(key => { - checkedKeySet[key] = true - }) - halfCheckedKeys.forEach(key => { - halfCheckedKeySet[key] = true - }) - - // Batch process - this.checkedBatch.list.forEach(({ key, checked, halfChecked }) => { - checkedKeySet[key] = checked - halfCheckedKeySet[key] = halfChecked - }) - const newCheckedKeys = Object.keys(checkedKeySet).filter(key => checkedKeySet[key]) - const newHalfCheckedKeys = Object.keys(halfCheckedKeySet).filter(key => halfCheckedKeySet[key]) - - // Trigger onChecked - let selectedObj - - const eventObj = { - event: 'check', - node: this.checkedBatch.treeNode, - checked: this.checkedBatch.checked, - } - - if (checkStrictly) { - selectedObj = getStrictlyValue(newCheckedKeys, newHalfCheckedKeys) - - // [Legacy] TODO: add optimize prop to skip node process - eventObj.checkedNodes = [] - traverseTreeNodes(children, ({ node, key }) => { - if (checkedKeySet[key]) { - eventObj.checkedNodes.push(node) - } - }) - - this.setUncontrolledState({ checkedKeys: newCheckedKeys }) - } else { - selectedObj = newCheckedKeys - - // [Legacy] TODO: add optimize prop to skip node process - eventObj.checkedNodes = [] - eventObj.checkedNodesPositions = [] // [Legacy] TODO: not in API - eventObj.halfCheckedKeys = newHalfCheckedKeys // [Legacy] TODO: not in API - traverseTreeNodes(children, ({ node, pos, key }) => { - if (checkedKeySet[key]) { - eventObj.checkedNodes.push(node) - eventObj.checkedNodesPositions.push({ node, pos }) - } - }) - - this.setUncontrolledState({ - checkedKeys: newCheckedKeys, - halfCheckedKeys: newHalfCheckedKeys, - }) - } - - if (onCheck) { - onCheck(selectedObj, eventObj) - } - - // Clean up - this.checkedBatch = null - }; - - onNodeExpand = (e, treeNode) => { - let { expandedKeys } = this.state - const { onExpand, loadData } = this.props - const { eventKey, expanded } = treeNode.props - - // Update selected keys - const index = expandedKeys.indexOf(eventKey) - const targetExpanded = !expanded - - warning( - (expanded && index !== -1) || (!expanded && index === -1) - , 'Expand state not sync with index check') - - if (targetExpanded) { - expandedKeys = arrAdd(expandedKeys, eventKey) - } else { - expandedKeys = arrDel(expandedKeys, eventKey) - } - - this.setUncontrolledState({ expandedKeys }) - - if (onExpand) { - onExpand(expandedKeys, { node: treeNode, expanded: targetExpanded }) - } - - // Async Load data - if (targetExpanded && loadData) { - return loadData(treeNode).then(() => { - // [Legacy] Refresh logic - this.setUncontrolledState({ expandedKeys }) - }) - } - - return null - }; - - onNodeMouseEnter = (event, node) => { - const { onMouseEnter } = this.props - if (onMouseEnter) { - onMouseEnter({ event, node }) - } - }; - - onNodeMouseLeave = (event, node) => { - const { onMouseLeave } = this.props - if (onMouseLeave) { - onMouseLeave({ event, node }) - } - }; - - onNodeContextMenu = (event, node) => { - const { onRightClick } = this.props - if (onRightClick) { - event.preventDefault() - onRightClick({ event, node }) - } - }; - - /** - * Sync state with props if needed - */ - getSyncProps = (props = {}, prevProps) => { - let needSync = false - const newState = {} - const myPrevProps = prevProps || {} - - function checkSync (name) { - if (props[name] !== myPrevProps[name]) { - needSync = true - return true + if (targetExpanded) { + expandedKeys = arrAdd(expandedKeys, eventKey) + } else { + expandedKeys = arrDel(expandedKeys, eventKey) } - return false - } - // Children change will affect check box status. - // And no need to check when prev props not provided - if (prevProps && checkSync('children')) { - const { checkedKeys = [], halfCheckedKeys = [] } = - calcCheckedKeys(props.checkedKeys || this.state.checkedKeys, props) || {} - newState.checkedKeys = checkedKeys - newState.halfCheckedKeys = halfCheckedKeys - } + this.setUncontrolledState({ expandedKeys }) + this.__emit('expand', expandedKeys, { node: treeNode, expanded: targetExpanded }) - if (checkSync('expandedKeys')) { - newState.expandedKeys = calcExpandedKeys(props.expandedKeys, props) - } + // Async Load data + if (targetExpanded && loadData) { + return loadData(treeNode).then(() => { + // [Legacy] Refresh logic + this.setUncontrolledState({ expandedKeys }) + }) + } - if (checkSync('selectedKeys')) { - newState.selectedKeys = calcSelectedKeys(props.selectedKeys, props) - } + return null + }, - if (checkSync('checkedKeys')) { - const { checkedKeys = [], halfCheckedKeys = [] } = - calcCheckedKeys(props.checkedKeys, props) || {} - newState.checkedKeys = checkedKeys - newState.halfCheckedKeys = halfCheckedKeys - } + onNodeMouseEnter (event, node) { + this.__emit('mouseenter', { event, node }) + }, - return needSync ? newState : null - }; + onNodeMouseLeave (event, node) { + this.__emit('mouseleave', { event, node }) + }, - /** - * Only update the value which is not in props - */ - setUncontrolledState = (state) => { - let needSync = false - const newState = {} + onNodeContextMenu (event, node) { + event.preventDefault() + this.__emit('rightClick', { event, node }) + }, - Object.keys(state).forEach(name => { - if (name in this.props) return + /** + * Sync state with props if needed + */ + getSyncProps (props = {}, prevProps) { + let needSync = false + const newState = {} + const myPrevProps = prevProps || {} + const children = this.$slots.default + function checkSync (name) { + if (props[name] !== myPrevProps[name]) { + needSync = true + return true + } + return false + } - needSync = true - newState[name] = state[name] - }) + // Children change will affect check box status. + // And no need to check when prev props not provided + if (prevProps && checkSync('children')) { + const { checkedKeys = [], halfCheckedKeys = [] } = + calcCheckedKeys(props.checkedKeys || this.sCheckedKeys, props, children) || {} + newState.sCheckedKeys = checkedKeys + newState.sHalfCheckedKeys = halfCheckedKeys + } - this.setState(needSync ? newState : null) - }; + if (checkSync('expandedKeys')) { + newState.sExpandedKeys = calcExpandedKeys(props.expandedKeys, props, children) + } - isKeyChecked = (key) => { - const { checkedKeys = [] } = this.state - return checkedKeys.indexOf(key) !== -1 - }; + if (checkSync('selectedKeys')) { + newState.sSelectedKeys = calcSelectedKeys(props.selectedKeys, props, children) + } - /** - * [Legacy] Original logic use `key` as tracking clue. - * We have to use `cloneElement` to pass `key`. - */ - renderTreeNode = (child, index, level = 0) => { - const { - expandedKeys = [], selectedKeys = [], halfCheckedKeys = [], - dragOverNodeKey, dropPosition, - } = this.state - const {} = this.props - const pos = getPosition(level, index) - const key = child.key || pos + if (checkSync('checkedKeys')) { + const { checkedKeys = [], halfCheckedKeys = [] } = + calcCheckedKeys(props.checkedKeys, props, children) || {} + newState.sCheckedKeys = checkedKeys + newState.sHalfCheckedKeys = halfCheckedKeys + } - return React.cloneElement(child, { - eventKey: key, - expanded: expandedKeys.indexOf(key) !== -1, - selected: selectedKeys.indexOf(key) !== -1, - checked: this.isKeyChecked(key), - halfChecked: halfCheckedKeys.indexOf(key) !== -1, - pos, + return needSync ? newState : null + }, - // [Legacy] Drag props - dragOver: dragOverNodeKey === key && dropPosition === 0, - dragOverGapTop: dragOverNodeKey === key && dropPosition === -1, - dragOverGapBottom: dragOverNodeKey === key && dropPosition === 1, - }) - }; + /** + * Only update the value which is not in props + */ + setUncontrolledState (state) { + let needSync = false + const newState = {} + const props = getOptionProps(this) + Object.keys(state).forEach(name => { + if (name in props) return + + needSync = true + const key = this.propsToStateMap[name] + newState[key] = state[name] + }) + + this.setState(needSync ? newState : null) + }, + + isKeyChecked (key) { + const { sCheckedKeys = [] } = this + return sCheckedKeys.indexOf(key) !== -1 + }, + + /** + * [Legacy] Original logic use `key` as tracking clue. + * We have to use `cloneElement` to pass `key`. + */ + renderTreeNode (child, index, level = 0) { + const { + sExpandedKeys = [], sSelectedKeys = [], sHalfCheckedKeys = [], + dragOverNodeKey, dropPosition, + } = this + const pos = getPosition(level, index) + const key = child.key || pos + + return cloneElement(child, { + props: { + eventKey: key, + expanded: sExpandedKeys.indexOf(key) !== -1, + selected: sSelectedKeys.indexOf(key) !== -1, + checked: this.isKeyChecked(key), + halfChecked: sHalfCheckedKeys.indexOf(key) !== -1, + pos, + + // [Legacy] Drag props + dragOver: dragOverNodeKey === key && dropPosition === 0, + dragOverGapTop: dragOverNodeKey === key && dropPosition === -1, + dragOverGapBottom: dragOverNodeKey === key && dropPosition === 1, + }, + + }) + }, + }, render () { const { - prefixCls, className, focusable, + prefixCls, focusable, showLine, - children, - } = this.props + $slots: { default: children = [] }, + } = this const domProps = {} - // [Legacy] Commit: 0117f0c9db0e2956e92cb208f51a42387dfcb3d1 - if (focusable) { - domProps.tabIndex = '0' - domProps.onKeyDown = this.onKeyDown - } - return (
      {}} > - {React.Children.map(children, this.renderTreeNode, this)} + {children.map(this.renderTreeNode)}
    ) - } + }, } export default Tree diff --git a/components/vc-tree/src/TreeNode.jsx b/components/vc-tree/src/TreeNode.jsx index 155585404..fb5ce8a86 100644 --- a/components/vc-tree/src/TreeNode.jsx +++ b/components/vc-tree/src/TreeNode.jsx @@ -4,6 +4,8 @@ import warning from 'warning' import { contextTypes } from './Tree' import { getPosition, getNodeChildren, isCheckDisabled, traverseTreeNodes } from './util' import { initDefaultProps, getOptionProps, filterEmpty } from '../../_util/props-util' +import BaseMixin from '../../_util/BaseMixin' +import getTransitionProps from '../../_util/getTransitionProps' const ICON_OPEN = 'open' const ICON_CLOSE = 'close' @@ -19,12 +21,14 @@ let onlyTreeNodeWarned = false // Only accept TreeNode export const nodeContextTypes = { ...contextTypes, - rcTreeNode: PropTypes.shape({ + vcTreeNode: PropTypes.shape({ onUpCheckConduct: PropTypes.func, }), } const TreeNode = { + name: 'TreeNode', + mixins: [BaseMixin], props: initDefaultProps({ eventKey: PropTypes.string, // Pass by parent `cloneElement` prefixCls: PropTypes.string, @@ -37,7 +41,7 @@ const TreeNode = { selected: PropTypes.bool, checked: PropTypes.bool, halfChecked: PropTypes.bool, - title: PropTypes.node, + title: PropTypes.any, pos: PropTypes.string, dragOver: PropTypes.bool, dragOverGapTop: PropTypes.bool, @@ -60,500 +64,500 @@ const TreeNode = { } }, inject: { - context: { default: {}}, + vcTree: { default: {}}, }, - provide: { - ...this.context, - rcTreeNode: this, + provide () { + return { + vcTree: this.vcTree, + vcTreeNode: this, + } }, // Isomorphic needn't load data in server side mounted () { - this.$nextTick(() => { - this.syncLoadData(this.$props) - }) + this.syncLoadData(this.$props) + }, + watch: { + expanded (val) { + this.syncLoadData({ expanded: val }) + }, }, - componentWillReceiveProps (nextProps) { - this.syncLoadData(nextProps) - }, + methods: { + onUpCheckConduct (treeNode, nodeChecked, nodeHalfChecked) { + const { pos: nodePos } = getOptionProps(treeNode) + const { eventKey, pos, checked, halfChecked } = this + const { + vcTree: { checkStrictly, isKeyChecked, onBatchNodeCheck, onCheckConductFinished }, + vcTreeNode: { onUpCheckConduct } = {}, + } = this - onUpCheckConduct (treeNode, nodeChecked, nodeHalfChecked) { - const { pos: nodePos } = getOptionProps(treeNode) - const { eventKey, pos, checked, halfChecked } = this - const { - rcTree: { checkStrictly, isKeyChecked, onBatchNodeCheck, onCheckConductFinished }, - rcTreeNode: { onUpCheckConduct } = {}, - } = this.context - - // Stop conduct when current node is disabled - if (isCheckDisabled(this)) { - onCheckConductFinished() - return - } - - const children = this.getNodeChildren() - - let checkedCount = nodeChecked ? 1 : 0 - - // Statistic checked count - children.forEach((node, index) => { - const childPos = getPosition(pos, index) - - if (nodePos === childPos || isCheckDisabled(node)) { + // Stop conduct when current node is disabled + if (isCheckDisabled(this)) { + onCheckConductFinished() return } - if (isKeyChecked(node.key || childPos)) { - checkedCount += 1 - } - }) + const children = this.getNodeChildren() - // Static enabled children count - const enabledChildrenCount = children - .filter(node => !isCheckDisabled(node)) - .length + let checkedCount = nodeChecked ? 1 : 0 - // checkStrictly will not conduct check status - const nextChecked = checkStrictly ? checked : enabledChildrenCount === checkedCount - const nextHalfChecked = checkStrictly // propagated or child checked - ? halfChecked : (nodeHalfChecked || (checkedCount > 0 && !nextChecked)) + // Statistic checked count + children.forEach((node, index) => { + const childPos = getPosition(pos, index) - // Add into batch update - if (checked !== nextChecked || halfChecked !== nextHalfChecked) { - onBatchNodeCheck(eventKey, nextChecked, nextHalfChecked) + if (nodePos === childPos || isCheckDisabled(node)) { + return + } + if (isKeyChecked(node.key || childPos)) { + checkedCount += 1 + } + }) - if (onUpCheckConduct) { - onUpCheckConduct(this, nextChecked, nextHalfChecked) + // Static enabled children count + const enabledChildrenCount = children + .filter(node => !isCheckDisabled(node)) + .length + + // checkStrictly will not conduct check status + const nextChecked = checkStrictly ? checked : enabledChildrenCount === checkedCount + const nextHalfChecked = checkStrictly // propagated or child checked + ? halfChecked : (nodeHalfChecked || (checkedCount > 0 && !nextChecked)) + + // Add into batch update + if (checked !== nextChecked || halfChecked !== nextHalfChecked) { + onBatchNodeCheck(eventKey, nextChecked, nextHalfChecked) + + if (onUpCheckConduct) { + onUpCheckConduct(this, nextChecked, nextHalfChecked) + } else { + // Flush all the update + onCheckConductFinished() + } } else { // Flush all the update onCheckConductFinished() } - } else { - // Flush all the update - onCheckConductFinished() - } - }, + }, - onDownCheckConduct (nodeChecked) { - const { $slots } = this - const children = $slots.default || [] - const { rcTree: { checkStrictly, isKeyChecked, onBatchNodeCheck }} = this.context - if (checkStrictly) return + onDownCheckConduct (nodeChecked) { + const { $slots } = this + const children = $slots.default || [] + const { vcTree: { checkStrictly, isKeyChecked, onBatchNodeCheck }} = this + if (checkStrictly) return - traverseTreeNodes(children, ({ node, key }) => { - if (isCheckDisabled(node)) return false + traverseTreeNodes(children, ({ node, key }) => { + if (isCheckDisabled(node)) return false - if (nodeChecked !== isKeyChecked(key)) { - onBatchNodeCheck(key, nodeChecked, false) + if (nodeChecked !== isKeyChecked(key)) { + onBatchNodeCheck(key, nodeChecked, false) + } + }) + }, + + onSelectorClick (e) { + if (this.isSelectable()) { + this.onSelect(e) + } else { + this.onCheck(e) } - }) - }, + }, - onSelectorClick (e) { - if (this.isSelectable()) { - this.onSelect(e) - } else { - this.onCheck(e) - } - }, + onSelect (e) { + if (this.isDisabled()) return - onSelect (e) { - if (this.isDisabled()) return + const { vcTree: { onNodeSelect }} = this + e.preventDefault() + onNodeSelect(e, this) + }, - const { rcTree: { onNodeSelect }} = this.context - e.preventDefault() - onNodeSelect(e, this) - }, + onCheck (e) { + if (this.isDisabled()) return - onCheck (e) { - if (this.isDisabled()) return + const { disableCheckbox, checked, eventKey } = this + const { + vcTree: { checkable, onBatchNodeCheck, onCheckConductFinished }, + vcTreeNode: { onUpCheckConduct } = {}, + } = this - const { disableCheckbox, checked, eventKey } = this - const { - rcTree: { checkable, onBatchNodeCheck, onCheckConductFinished }, - rcTreeNode: { onUpCheckConduct } = {}, - } = this.context + if (!checkable || disableCheckbox) return - if (!checkable || disableCheckbox) return + e.preventDefault() + const targetChecked = !checked + onBatchNodeCheck(eventKey, targetChecked, false, this) - e.preventDefault() - const targetChecked = !checked - onBatchNodeCheck(eventKey, targetChecked, false, this) + // Children conduct + this.onDownCheckConduct(targetChecked) - // Children conduct - this.onDownCheckConduct(targetChecked) + // Parent conduct + if (onUpCheckConduct) { + onUpCheckConduct(this, targetChecked, false) + } else { + onCheckConductFinished() + } + }, - // Parent conduct - if (onUpCheckConduct) { - onUpCheckConduct(this, targetChecked, false) - } else { - onCheckConductFinished() - } - }, + onMouseEnter (e) { + const { vcTree: { onNodeMouseEnter }} = this + onNodeMouseEnter(e, this) + }, - onMouseEnter (e) { - const { rcTree: { onNodeMouseEnter }} = this.context - onNodeMouseEnter(e, this) - }, + onMouseLeave (e) { + const { vcTree: { onNodeMouseLeave }} = this + onNodeMouseLeave(e, this) + }, - onMouseLeave (e) { - const { rcTree: { onNodeMouseLeave }} = this.context - onNodeMouseLeave(e, this) - }, + onContextMenu (e) { + const { vcTree: { onNodeContextMenu }} = this + onNodeContextMenu(e, this) + }, - onContextMenu (e) { - const { rcTree: { onNodeContextMenu }} = this.context - onNodeContextMenu(e, this) - }, + onDragStart (e) { + const { vcTree: { onNodeDragStart }} = this - onDragStart (e) { - const { rcTree: { onNodeDragStart }} = this.context - - e.stopPropagation() - this.setState({ - dragNodeHighlight: true, - }) - onNodeDragStart(e, this) - - try { - // ie throw error - // firefox-need-it - e.dataTransfer.setData('text/plain', '') - } catch (error) { - // empty - } - }, - - onDragEnter (e) { - const { rcTree: { onNodeDragEnter }} = this.context - - e.preventDefault() - e.stopPropagation() - onNodeDragEnter(e, this) - }, - - onDragOver (e) { - const { rcTree: { onNodeDragOver }} = this.context - - e.preventDefault() - e.stopPropagation() - onNodeDragOver(e, this) - }, - - onDragLeave (e) { - const { rcTree: { onNodeDragLeave }} = this.context - - e.stopPropagation() - onNodeDragLeave(e, this) - }, - - onDragEnd (e) { - const { rcTree: { onNodeDragEnd }} = this.context - - e.stopPropagation() - this.setState({ - dragNodeHighlight: false, - }) - onNodeDragEnd(e, this) - }, - - onDrop (e) { - const { rcTree: { onNodeDrop }} = this.context - - e.preventDefault() - e.stopPropagation() - this.setState({ - dragNodeHighlight: false, - }) - onNodeDrop(e, this) - }, - - // Disabled item still can be switch - onExpand (e) { - const { rcTree: { onNodeExpand }} = this.context - const callbackPromise = onNodeExpand(e, this) - - // Promise like - if (callbackPromise && callbackPromise.then) { - this.setState({ loadStatus: LOAD_STATUS_LOADING }) - - callbackPromise.then(() => { - this.setState({ loadStatus: LOAD_STATUS_LOADED }) - }).catch(() => { - this.setState({ loadStatus: LOAD_STATUS_FAILED }) + e.stopPropagation() + this.setState({ + dragNodeHighlight: true, }) - } - }, + onNodeDragStart(e, this) - // Drag usage - setSelectHandle (node) { - this.selectHandle = node - }, + try { + // ie throw error + // firefox-need-it + e.dataTransfer.setData('text/plain', '') + } catch (error) { + // empty + } + }, - getNodeChildren () { - const { $slots: { default: children }} = this - const originList = filterEmpty(children) - const targetList = getNodeChildren(originList) + onDragEnter (e) { + const { vcTree: { onNodeDragEnter }} = this - if (originList.length !== targetList.length && !onlyTreeNodeWarned) { - onlyTreeNodeWarned = true - warning(false, 'Tree only accept TreeNode as children.') - } + e.preventDefault() + e.stopPropagation() + onNodeDragEnter(e, this) + }, - return targetList - }, + onDragOver (e) { + const { vcTree: { onNodeDragOver }} = this - getNodeState () { - const { expanded } = this + e.preventDefault() + e.stopPropagation() + onNodeDragOver(e, this) + }, - if (this.isLeaf()) { - return null - } + onDragLeave (e) { + const { vcTree: { onNodeDragLeave }} = this - return expanded ? ICON_OPEN : ICON_CLOSE - }, + e.stopPropagation() + onNodeDragLeave(e, this) + }, - isLeaf () { - const { isLeaf, loadStatus } = this - const { rcTree: { loadData }} = this.context + onDragEnd (e) { + const { vcTree: { onNodeDragEnd }} = this - const hasChildren = this.getNodeChildren().length !== 0 - - return ( - isLeaf || - (!loadData && !hasChildren) || - (loadData && loadStatus === LOAD_STATUS_LOADED && !hasChildren) - ) - }, - - isDisabled () { - const { disabled } = this - const { rcTree: { disabled: treeDisabled }} = this.context - - // Follow the logic of Selectable - if (disabled === false) { - return false - } - - return !!(treeDisabled || disabled) - }, - - isSelectable () { - const { selectable } = this - const { rcTree: { selectable: treeSelectable }} = this.context - - // Ignore when selectable is undefined or null - if (typeof selectable === 'boolean') { - return selectable - } - - return treeSelectable - }, - - // Load data to avoid default expanded tree without data - syncLoadData (props) { - const { loadStatus } = this - const { expanded } = props - const { rcTree: { loadData }} = this.context - - if (loadData && loadStatus === LOAD_STATUS_NONE && expanded && !this.isLeaf()) { - this.setState({ loadStatus: LOAD_STATUS_LOADING }) - - loadData(this).then(() => { - this.setState({ loadStatus: LOAD_STATUS_LOADED }) - }).catch(() => { - this.setState({ loadStatus: LOAD_STATUS_FAILED }) + e.stopPropagation() + this.setState({ + dragNodeHighlight: false, }) - } - }, + onNodeDragEnd(e, this) + }, - // Switcher - renderSwitcher () { - const { expanded } = this - const { rcTree: { prefixCls }} = this.context + onDrop (e) { + const { vcTree: { onNodeDrop }} = this - if (this.isLeaf()) { - return - } + e.preventDefault() + e.stopPropagation() + this.setState({ + dragNodeHighlight: false, + }) + onNodeDrop(e, this) + }, - return ( - - ) - }, + // Disabled item still can be switch + onExpand (e) { + const { vcTree: { onNodeExpand }} = this + const callbackPromise = onNodeExpand(e, this) - // Checkbox - renderCheckbox () { - const { checked, halfChecked, disableCheckbox } = this - const { rcTree: { prefixCls, checkable }} = this.context - const disabled = this.isDisabled() + // Promise like + if (callbackPromise && callbackPromise.then) { + this.setState({ loadStatus: LOAD_STATUS_LOADING }) - if (!checkable) return null + callbackPromise.then(() => { + this.setState({ loadStatus: LOAD_STATUS_LOADED }) + }).catch(() => { + this.setState({ loadStatus: LOAD_STATUS_FAILED }) + }) + } + }, - // [Legacy] Custom element should be separate with `checkable` in future - const $custom = typeof checkable !== 'boolean' ? checkable : null + // Drag usage + setSelectHandle (node) { + this.selectHandle = node + }, - return ( - - {$custom} - - ) - }, + getNodeChildren () { + const { $slots: { default: children }} = this + const originList = filterEmpty(children) + const targetList = getNodeChildren(originList) - renderIcon () { - const { loadStatus } = this - const { rcTree: { prefixCls }} = this.context + if (originList.length !== targetList.length && !onlyTreeNodeWarned) { + onlyTreeNodeWarned = true + warning(false, 'Tree only accept TreeNode as children.') + } - return ( - - ) - }, + return targetList + }, - // Icon + Title - renderSelector () { - const { title, selected, icon, loadStatus, dragNodeHighlight } = this - const { rcTree: { prefixCls, showIcon, draggable, loadData }} = this.context - const disabled = this.isDisabled() + getNodeState () { + const { expanded } = this - const wrapClass = `${prefixCls}-node-content-wrapper` + if (this.isLeaf2()) { + return null + } - // Icon - Still show loading icon when loading without showIcon - let $icon + return expanded ? ICON_OPEN : ICON_CLOSE + }, - if (showIcon) { - $icon = icon ? ( + isLeaf2 () { + const { isLeaf, loadStatus } = this + const { vcTree: { loadData }} = this + + const hasChildren = this.getNodeChildren().length !== 0 + + return ( + isLeaf || + (!loadData && !hasChildren) || + (loadData && loadStatus === LOAD_STATUS_LOADED && !hasChildren) + ) + }, + + isDisabled () { + const { disabled } = this + const { vcTree: { disabled: treeDisabled }} = this + + // Follow the logic of Selectable + if (disabled === false) { + return false + } + + return !!(treeDisabled || disabled) + }, + + isSelectable () { + const { selectable } = this + const { vcTree: { selectable: treeSelectable }} = this + + // Ignore when selectable is undefined or null + if (typeof selectable === 'boolean') { + return selectable + } + + return treeSelectable + }, + + // Load data to avoid default expanded tree without data + syncLoadData (props) { + const { loadStatus } = this + const { expanded } = props + const { vcTree: { loadData }} = this + + if (loadData && loadStatus === LOAD_STATUS_NONE && expanded && !this.isLeaf2()) { + this.setState({ loadStatus: LOAD_STATUS_LOADING }) + + loadData(this).then(() => { + this.setState({ loadStatus: LOAD_STATUS_LOADED }) + }).catch(() => { + this.setState({ loadStatus: LOAD_STATUS_FAILED }) + }) + } + }, + + // Switcher + renderSwitcher () { + const { expanded } = this + const { vcTree: { prefixCls }} = this + + if (this.isLeaf2()) { + return + } + + return ( + + ) + }, + + // Checkbox + renderCheckbox () { + const { checked, halfChecked, disableCheckbox } = this + const { vcTree: { prefixCls, checkable }} = this + const disabled = this.isDisabled() + + if (!checkable) return null + + // [Legacy] Custom element should be separate with `checkable` in future + const $custom = typeof checkable !== 'boolean' ? checkable : null + + return ( + + {$custom} + + ) + }, + + renderIcon () { + const { loadStatus } = this + const { vcTree: { prefixCls }} = this + + return ( - {typeof icon === 'function' - ? icon(this.$props) : icon} - - ) : this.renderIcon() - } else if (loadData && loadStatus === LOAD_STATUS_LOADING) { - $icon = this.renderIcon() - } - - // Title - const $title = {title} - - return ( - - {$icon}{$title} - - ) - }, - - // Children list wrapped with `Animation` - renderChildren () { - const { expanded, pos } = this - const { rcTree: { - prefixCls, - openTransitionName, openAnimation, - renderTreeNode, - }} = this.context - - // [Legacy] Animation control - const renderFirst = this.renderFirst - this.renderFirst = 1 - let transitionAppear = true - if (!renderFirst && expanded) { - transitionAppear = false - } - - const animProps = {} - if (openTransitionName) { - animProps.transitionName = openTransitionName - } else if (typeof openAnimation === 'object') { - animProps.animation = { ...openAnimation } - if (!transitionAppear) { - delete animProps.animation.appear - } - } - - // Children TreeNode - const nodeList = this.getNodeChildren() - - if (nodeList.length === 0) { - return null - } - - let $children - if (expanded) { - $children = ( -
      - {nodeList.map((node, index) => ( - renderTreeNode(node, index, pos) - ))} -
    + /> ) - } + }, - return ( - - {$children} - - ) + // Icon + Title + renderSelector () { + const { title, selected, icon, loadStatus, dragNodeHighlight } = this + const { vcTree: { prefixCls, showIcon, draggable, loadData }} = this + const disabled = this.isDisabled() + + const wrapClass = `${prefixCls}-node-content-wrapper` + + // Icon - Still show loading icon when loading without showIcon + let $icon + + if (showIcon) { + $icon = icon ? ( + + {typeof icon === 'function' + ? icon(this.$props) : icon} + + ) : this.renderIcon() + } else if (loadData && loadStatus === LOAD_STATUS_LOADING) { + $icon = this.renderIcon() + } + + // Title + const $title = {title} + + return ( + + {$icon}{$title} + + ) + }, + + // Children list wrapped with `Animation` + renderChildren () { + const { expanded, pos } = this + const { vcTree: { + prefixCls, + openTransitionName, openAnimation, + renderTreeNode, + }} = this + + // [Legacy] Animation control + const renderFirst = this.renderFirst + this.renderFirst = 1 + let transitionAppear = true + if (!renderFirst && expanded) { + transitionAppear = false + } + + let animProps = {} + if (openTransitionName) { + animProps = getTransitionProps(openTransitionName, { appear: transitionAppear }) + } else if (typeof openAnimation === 'object') { + animProps = { ...openAnimation } + if (!transitionAppear) { + delete animProps.props.appear + } + } + + // Children TreeNode + const nodeList = this.getNodeChildren() + + if (nodeList.length === 0) { + return null + } + + let $children + if (expanded) { + $children = ( +
      + {nodeList.map((node, index) => ( + renderTreeNode(node, index, pos) + ))} +
    + ) + } + + return ( + + {$children} + + ) + }, }, render () { const { dragOver, dragOverGapTop, dragOverGapBottom, } = this - const { rcTree: { + const { vcTree: { prefixCls, filterTreeNode, - }} = this.context + }} = this const disabled = this.isDisabled() return ( diff --git a/components/vc-tree/src/index.js b/components/vc-tree/src/index.js index d053f4c9f..e8c0e81b5 100644 --- a/components/vc-tree/src/index.js +++ b/components/vc-tree/src/index.js @@ -1,6 +1,25 @@ +import { getOptionProps } from '../../_util/props-util' import Tree from './Tree' import TreeNode from './TreeNode' Tree.TreeNode = TreeNode +// +const NewTree = { + TreeNode: TreeNode, + props: Tree.props, + render () { + const { $listeners, $slots } = this + const treeProps = { + props: { + ...getOptionProps(this), + children: $slots.default, + }, + on: $listeners, + } + return ( + {$slots.default} + ) + }, +} export { TreeNode } -export default Tree +export default NewTree diff --git a/components/vc-tree/src/util.js b/components/vc-tree/src/util.js index 41e39ed08..712853249 100644 --- a/components/vc-tree/src/util.js +++ b/components/vc-tree/src/util.js @@ -1,6 +1,6 @@ /* eslint no-loop-func: 0*/ -import { Children } from 'react' import warning from 'warning' +import { getSlotOptions, getOptionProps } from '../../_util/props-util' export function arrDel (list, value) { const clone = list.slice() @@ -48,14 +48,13 @@ export function getPosition (level, index) { return `${level}-${index}` } -export function getNodeChildren (children) { - const childList = Array.isArray(children) ? children : [children] - return childList - .filter(child => child && child.type && child.type.isTreeNode) +export function getNodeChildren (children = []) { + return children + .filter(child => getSlotOptions(child).isTreeNode) } export function isCheckDisabled (node) { - const { disabled, disableCheckbox } = node.props || {} + const { disabled, disableCheckbox } = getOptionProps(node) || {} return !!(disabled || disableCheckbox) } @@ -66,7 +65,7 @@ export function traverseTreeNodes (treeNodes, subTreeData, callback) { } function processNode (node, index, parent) { - const children = node ? node.props.children : treeNodes + const children = node ? node.componentOptions.children : treeNodes const pos = node ? getPosition(parent.pos, index) : 0 // Filter children @@ -86,7 +85,7 @@ export function traverseTreeNodes (treeNodes, subTreeData, callback) { if (subTreeData) { // Statistic children const subNodes = [] - Children.forEach(childList, (subNode, subIndex) => { + childList.forEach((subNode, subIndex) => { // Provide limit snapshot const subPos = getPosition(pos, index) subNodes.push({ @@ -106,7 +105,7 @@ export function traverseTreeNodes (treeNodes, subTreeData, callback) { } // Process children node - Children.forEach(childList, (subNode, subIndex) => { + childList.forEach((subNode, subIndex) => { processNode(subNode, subIndex, { node, pos }) }) } @@ -182,7 +181,7 @@ export function getNodesStatistic (treeNodes) { } export function getDragNodesKeys (treeNodes, node) { - const { eventKey, pos } = node.props + const { eventKey, pos } = getOptionProps(node) const dragNodesKeys = [] traverseTreeNodes(treeNodes, ({ pos: nodePos, key }) => { @@ -214,12 +213,12 @@ export function calcDropPosition (event, treeNode) { * @param props * @returns [string] */ -export function calcExpandedKeys (keyList, props) { +export function calcExpandedKeys (keyList, props, children = []) { if (!keyList) { return [] } - const { autoExpandParent, children } = props + const { autoExpandParent } = props // Do nothing if not auto expand parent if (!autoExpandParent) { @@ -363,8 +362,8 @@ export function calcCheckStateConduct (treeNodes, checkedKeys) { * Calculate the value of checked and halfChecked keys. * This should be only run in init or props changed. */ -export function calcCheckedKeys (keys, props) { - const { checkable, children, checkStrictly } = props +export function calcCheckedKeys (keys, props, children = []) { + const { checkable, checkStrictly } = props if (!checkable || !keys) { return null diff --git a/examples/routes.js b/examples/routes.js index 73e054488..41b6a33fc 100644 --- a/examples/routes.js +++ b/examples/routes.js @@ -3,7 +3,7 @@ const AsyncComp = () => { const hashs = window.location.hash.split('/') const d = hashs[hashs.length - 1] return { - component: import(`../components/popconfirm/demo/${d}`), + component: import(`../components/vc-tree/demo/${d}`), } } export default [ From 53ed4f9a91fa158c881b44eb0778df709150841b Mon Sep 17 00:00:00 2001 From: tjz <415800467@qq.com> Date: Sat, 24 Mar 2018 21:55:00 +0800 Subject: [PATCH 3/8] add vc-table --- components/vc-table | 1 + 1 file changed, 1 insertion(+) create mode 160000 components/vc-table diff --git a/components/vc-table b/components/vc-table new file mode 160000 index 000000000..7cd6abab4 --- /dev/null +++ b/components/vc-table @@ -0,0 +1 @@ +Subproject commit 7cd6abab4a2bd55adc7a9207c23feb62c3cd015b From dd73d412e52fa792d3eaa862c9119405e98586b3 Mon Sep 17 00:00:00 2001 From: tjz <415800467@qq.com> Date: Sat, 24 Mar 2018 22:00:49 +0800 Subject: [PATCH 4/8] fix --- components/vc-table | 1 - 1 file changed, 1 deletion(-) delete mode 160000 components/vc-table diff --git a/components/vc-table b/components/vc-table deleted file mode 160000 index 7cd6abab4..000000000 --- a/components/vc-table +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 7cd6abab4a2bd55adc7a9207c23feb62c3cd015b From e95c22b681530b7914e732a92725ee8dbe79a5c3 Mon Sep 17 00:00:00 2001 From: tjz <415800467@qq.com> Date: Sat, 24 Mar 2018 22:02:24 +0800 Subject: [PATCH 5/8] add vc-table --- components/vc-table/assets/animation.less | 59 +++ components/vc-table/assets/bordered.less | 11 + components/vc-table/assets/index.less | 225 +++++++++ components/vc-table/demo/animation.js | 70 +++ components/vc-table/demo/childrenIndent.js | 87 ++++ components/vc-table/demo/className.js | 45 ++ components/vc-table/demo/colspan-rowspan.js | 107 ++++ components/vc-table/demo/column-resize.js | 84 ++++ components/vc-table/demo/dropdown.js | 111 ++++ components/vc-table/demo/expandedRowRender.js | 114 +++++ .../vc-table/demo/fixedColumns-auto-height.js | 46 ++ components/vc-table/demo/fixedColumns.js | 45 ++ .../vc-table/demo/fixedColumnsAndHeader.js | 39 ++ .../fixedColumnsAndHeaderSyncRowHeight.js | 39 ++ .../vc-table/demo/fixedColumnsWhenResize.js | 45 ++ components/vc-table/demo/grouping-columns.js | 100 ++++ components/vc-table/demo/hide-header.js | 34 ++ components/vc-table/demo/jsx.js | 49 ++ components/vc-table/demo/key.js | 70 +++ components/vc-table/demo/nested.js | 45 ++ components/vc-table/demo/no-data.js | 26 + components/vc-table/demo/react-dnd.js | 187 +++++++ components/vc-table/demo/rowAndCellClick.js | 105 ++++ components/vc-table/demo/scrollX.js | 34 ++ components/vc-table/demo/scrollXY.js | 45 ++ components/vc-table/demo/scrollY.js | 62 +++ components/vc-table/demo/simple.js | 30 ++ components/vc-table/demo/styled-components.js | 45 ++ components/vc-table/demo/subTable.js | 63 +++ components/vc-table/demo/title-and-footer.js | 35 ++ components/vc-table/index.js | 9 + components/vc-table/src/BaseTable.jsx | 189 +++++++ components/vc-table/src/BodyTable.jsx | 117 +++++ components/vc-table/src/ColGroup.jsx | 53 ++ components/vc-table/src/Column.jsx | 23 + components/vc-table/src/ColumnGroup.jsx | 9 + components/vc-table/src/ColumnManager.js | 150 ++++++ components/vc-table/src/ExpandIcon.jsx | 34 ++ components/vc-table/src/ExpandableRow.js | 126 +++++ components/vc-table/src/ExpandableTable.js | 223 ++++++++ components/vc-table/src/HeadTable.jsx | 56 +++ components/vc-table/src/Table.js | 475 ++++++++++++++++++ components/vc-table/src/TableCell.jsx | 104 ++++ components/vc-table/src/TableHeader.jsx | 85 ++++ components/vc-table/src/TableHeaderRow.js | 53 ++ components/vc-table/src/TableRow.js | 291 +++++++++++ components/vc-table/src/utils.js | 84 ++++ 47 files changed, 4138 insertions(+) create mode 100644 components/vc-table/assets/animation.less create mode 100644 components/vc-table/assets/bordered.less create mode 100644 components/vc-table/assets/index.less create mode 100644 components/vc-table/demo/animation.js create mode 100644 components/vc-table/demo/childrenIndent.js create mode 100644 components/vc-table/demo/className.js create mode 100644 components/vc-table/demo/colspan-rowspan.js create mode 100644 components/vc-table/demo/column-resize.js create mode 100644 components/vc-table/demo/dropdown.js create mode 100644 components/vc-table/demo/expandedRowRender.js create mode 100644 components/vc-table/demo/fixedColumns-auto-height.js create mode 100644 components/vc-table/demo/fixedColumns.js create mode 100644 components/vc-table/demo/fixedColumnsAndHeader.js create mode 100644 components/vc-table/demo/fixedColumnsAndHeaderSyncRowHeight.js create mode 100644 components/vc-table/demo/fixedColumnsWhenResize.js create mode 100644 components/vc-table/demo/grouping-columns.js create mode 100644 components/vc-table/demo/hide-header.js create mode 100644 components/vc-table/demo/jsx.js create mode 100644 components/vc-table/demo/key.js create mode 100644 components/vc-table/demo/nested.js create mode 100644 components/vc-table/demo/no-data.js create mode 100644 components/vc-table/demo/react-dnd.js create mode 100644 components/vc-table/demo/rowAndCellClick.js create mode 100644 components/vc-table/demo/scrollX.js create mode 100644 components/vc-table/demo/scrollXY.js create mode 100644 components/vc-table/demo/scrollY.js create mode 100644 components/vc-table/demo/simple.js create mode 100644 components/vc-table/demo/styled-components.js create mode 100644 components/vc-table/demo/subTable.js create mode 100644 components/vc-table/demo/title-and-footer.js create mode 100644 components/vc-table/index.js create mode 100644 components/vc-table/src/BaseTable.jsx create mode 100644 components/vc-table/src/BodyTable.jsx create mode 100644 components/vc-table/src/ColGroup.jsx create mode 100644 components/vc-table/src/Column.jsx create mode 100644 components/vc-table/src/ColumnGroup.jsx create mode 100644 components/vc-table/src/ColumnManager.js create mode 100644 components/vc-table/src/ExpandIcon.jsx create mode 100644 components/vc-table/src/ExpandableRow.js create mode 100644 components/vc-table/src/ExpandableTable.js create mode 100644 components/vc-table/src/HeadTable.jsx create mode 100644 components/vc-table/src/Table.js create mode 100644 components/vc-table/src/TableCell.jsx create mode 100644 components/vc-table/src/TableHeader.jsx create mode 100644 components/vc-table/src/TableHeaderRow.js create mode 100644 components/vc-table/src/TableRow.js create mode 100644 components/vc-table/src/utils.js diff --git a/components/vc-table/assets/animation.less b/components/vc-table/assets/animation.less new file mode 100644 index 000000000..a6d4e9281 --- /dev/null +++ b/components/vc-table/assets/animation.less @@ -0,0 +1,59 @@ + +.move-enter, .move-appear { + opacity: 0; + animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); + animation-duration: 2.5s; + animation-fill-mode: both; + animation-play-state: paused; +} + +.move-leave { + animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); + animation-duration: .5s; + animation-fill-mode: both; + animation-play-state: paused; +} + +.move-enter.move-enter-active, .move-appear.move-enter-active { + animation-name: moveLeftIn; + animation-play-state: running; +} + +.move-leave.move-leave-active { + animation-name: moveRightOut; + animation-play-state: running; +} + +@keyframes moveLeftIn { + 0% { + transform-origin: 0 0; + transform: translateX(30px); + opacity: 0; + background: #fff6de; + } + 20% { + transform-origin: 0 0; + transform: translateX(0); + opacity: 1; + } + 80%{ + background: #fff6de; + } + 100%{ + background: transparent; + opacity: 1; + } +} + +@keyframes moveRightOut { + 0% { + transform-origin: 0 0; + transform: translateX(0); + opacity: 1; + } + 100% { + transform-origin: 0 0; + transform: translateX(-30px); + opacity: 0; + } +} diff --git a/components/vc-table/assets/bordered.less b/components/vc-table/assets/bordered.less new file mode 100644 index 000000000..f3ad2e84a --- /dev/null +++ b/components/vc-table/assets/bordered.less @@ -0,0 +1,11 @@ +@tablePrefixCls: rc-table; +@table-border-color: #e9e9e9; + +.@{tablePrefixCls}.bordered { + table { + border-collapse: collapse; + } + th, td { + border: 1px solid @table-border-color; + } +} diff --git a/components/vc-table/assets/index.less b/components/vc-table/assets/index.less new file mode 100644 index 000000000..173d5f1eb --- /dev/null +++ b/components/vc-table/assets/index.less @@ -0,0 +1,225 @@ +@tablePrefixCls: rc-table; +@text-color : #666; +@font-size-base : 12px; +@line-height: 1.5; +@table-border-color: #e9e9e9; +@table-head-background-color: #f7f7f7; +@vertical-padding: 16px; +@horizontal-padding: 8px; + +.@{tablePrefixCls} { + font-size: @font-size-base; + color: @text-color; + transition: opacity 0.3s ease; + position: relative; + line-height: @line-height; + overflow: hidden; + + .@{tablePrefixCls}-scroll { + overflow: auto; + table { + width: auto; + min-width: 100%; + } + } + + .@{tablePrefixCls}-header { + overflow: hidden; + background: @table-head-background-color; + } + + &-fixed-header &-body { + background: #fff; + position: relative; + } + + &-fixed-header &-body-inner { + height: 100%; + overflow: scroll; + } + + &-fixed-header &-scroll &-header { + overflow-x: scroll; + padding-bottom: 20px; + margin-bottom: -20px; + overflow-y: scroll; + box-sizing: border-box; + } + + .@{tablePrefixCls}-title { + padding: @vertical-padding @horizontal-padding; + border-top: 1px solid @table-border-color; + } + + .@{tablePrefixCls}-content { + position: relative; + } + + .@{tablePrefixCls}-footer { + padding: @vertical-padding @horizontal-padding; + border-bottom: 1px solid @table-border-color; + } + + .@{tablePrefixCls}-placeholder { + padding: 16px 8px; + background: #fff; + border-bottom: 1px solid @table-border-color; + text-align: center; + position: relative; + &-fixed-columns { + position: absolute; + bottom: 0; + width: 100%; + background: transparent; + pointer-events: none; + } + } + + table { + width: 100%; + border-collapse: separate; + text-align: left; + } + + th { + background: @table-head-background-color; + font-weight: bold; + transition: background .3s ease; + } + + td { + border-bottom: 1px solid @table-border-color; + &:empty:after { + content: '.'; // empty cell placeholder + visibility: hidden; + } + } + + tr { + transition: all .3s ease; + &:hover { + background: #eaf8fe; + } + &.@{tablePrefixCls}-row-hover { + background: #eaf8fe; + } + } + + th, td { + padding: @vertical-padding @horizontal-padding; + white-space: nowrap; + } +} + +.@{tablePrefixCls} { + &-expand-icon-col { + width: 34px; + } + &-row, &-expanded-row { + &-expand-icon { + cursor: pointer; + display: inline-block; + width: 16px; + height: 16px; + text-align: center; + line-height: 16px; + border: 1px solid @table-border-color; + user-select: none; + background: #fff; + } + &-spaced { + visibility: hidden; + } + &-spaced:after { + content: '.' + } + + &-expanded:after { + content: '-' + } + + &-collapsed:after { + content: '+' + } + } + tr&-expanded-row { + background: #f7f7f7; + &:hover { + background: #f7f7f7; + } + } + &-column-hidden { + display: none; + } + &-prev-columns-page, + &-next-columns-page { + cursor: pointer; + color: #666; + z-index: 1; + &:hover { + color: #2db7f5; + } + &-disabled { + cursor: not-allowed; + color: #999; + &:hover { + color: #999; + } + } + } + &-prev-columns-page { + margin-right: 8px; + &:before { + content: '<'; + } + } + &-next-columns-page { + float: right; + &:before { + content: '>'; + } + } + + &-fixed-left, + &-fixed-right { + position: absolute; + top: 0; + overflow: hidden; + table { + width: auto; + background: #fff; + } + } + + &-fixed-left { + left: 0; + box-shadow: 4px 0 4px rgba(100, 100, 100, 0.1); + & .@{tablePrefixCls}-body-inner { + margin-right: -20px; + padding-right: 20px; + } + .@{tablePrefixCls}-fixed-header & .@{tablePrefixCls}-body-inner { + padding-right: 0; + } + } + + &-fixed-right { + right: 0; + box-shadow: -4px 0 4px rgba(100, 100, 100, 0.1); + + // hide expand row content in right fixed Table + // https://github.com/ant-design/ant-design/issues/1898 + .@{tablePrefixCls}-expanded-row { + color: transparent; + pointer-events: none; + } + } + + &&-scroll-position-left &-fixed-left { + box-shadow: none; + } + + &&-scroll-position-right &-fixed-right { + box-shadow: none; + } +} diff --git a/components/vc-table/demo/animation.js b/components/vc-table/demo/animation.js new file mode 100644 index 000000000..6f379dcdc --- /dev/null +++ b/components/vc-table/demo/animation.js @@ -0,0 +1,70 @@ +/* eslint-disable no-console,func-names,react/no-multi-comp */ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Table from 'rc-table'; +import Animate from 'rc-animate'; +import 'rc-table/assets/index.less'; +import 'rc-table/assets/animation.less'; + +const AnimateBody = (props) => + ; + +class Demo extends React.Component { + constructor(props) { + super(props); + this.columns = [ + { title: 'title1', dataIndex: 'a', key: 'a', width: 100 }, + { id: '123', title: 'title2', dataIndex: 'b', key: 'b', width: 100 }, + { title: 'title3', dataIndex: 'c', key: 'c', width: 200 }, + { + title: 'Operations', dataIndex: '', key: 'd', render: (text, record) => + this.onDelete(record.key, e)} href="#">Delete, + }, + ]; + this.state = { + data: [ + { a: '123', key: '1' }, + { a: 'cdd', b: 'edd', key: '2' }, + { a: '1333', c: 'eee', key: '3' }, + ], + }; + } + + onDelete(key, e) { + console.log('Delete', key); + e.preventDefault(); + const data = this.state.data.filter(item => item.key !== key); + this.setState({ data }); + } + + onAdd() { + const data = [...this.state.data]; + data.push({ + a: 'new data', + b: 'new data', + c: 'new data', + key: Date.now(), + }); + this.setState({ data }); + } + + render() { + return ( +
    +

    Table row with animation

    + + + + ); + } +} +ReactDOM.render( + , + document.getElementById('__react-content') +); diff --git a/components/vc-table/demo/childrenIndent.js b/components/vc-table/demo/childrenIndent.js new file mode 100644 index 000000000..4b226a887 --- /dev/null +++ b/components/vc-table/demo/childrenIndent.js @@ -0,0 +1,87 @@ +/* eslint-disable no-console,func-names,react/no-multi-comp */ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Table from 'rc-table'; +import 'rc-table/assets/index.less'; + +const columns = [{ + title: 'Name', + dataIndex: 'name', + key: 'name', + width: 400, +}, { + title: 'Age', + dataIndex: 'age', + key: 'age', + width: 100, +}, { + title: 'Address', + dataIndex: 'address', + key: 'address', + width: 200, +}, { + title: 'Operations', + dataIndex: 'operation', + key: 'x', + width: 150, +}]; + +const data = [{ + key: 1, + name: 'a', + age: 32, + address: 'I am a', + children: [{ + key: 11, + name: 'aa', + age: 33, + address: 'I am aa', + }, { + key: 12, + name: 'ab', + age: 33, + address: 'I am ab', + children: [{ + key: 121, + name: 'aba', + age: 33, + address: 'I am aba', + }], + }, { + key: 13, + name: 'ac', + age: 33, + address: 'I am ac', + children: [{ + key: 131, + name: 'aca', + age: 33, + address: 'I am aca', + children: [{ + key: 1311, + name: 'acaa', + age: 33, + address: 'I am acaa', + }, { + key: 1312, + name: 'acab', + age: 33, + address: 'I am acab', + }], + }], + }], +}, { + key: 2, + name: 'b', + age: 32, + address: 'I am b', +}]; + +function onExpand(expanded, record) { + console.log('onExpand', expanded, record); +} + +ReactDOM.render( +
    , + document.getElementById('__react-content') +); diff --git a/components/vc-table/demo/className.js b/components/vc-table/demo/className.js new file mode 100644 index 000000000..77f631174 --- /dev/null +++ b/components/vc-table/demo/className.js @@ -0,0 +1,45 @@ +/* eslint-disable no-console,func-names,react/no-multi-comp */ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Table from 'rc-table'; +import 'rc-table/assets/index.less'; + +const columns = [ + { title: 'title1', dataIndex: 'a', + className: 'a', + key: 'a', width: 100 }, + { id: '123', title: 'title2', dataIndex: 'b', + className: 'b', + key: 'b', width: 100 }, + { title: 'title3', dataIndex: 'c', + className: 'c', + key: 'c', width: 200 }, + { + title: 'Operations', dataIndex: '', + className: 'd', + key: 'd', render() { + return Operations; + }, + }, +]; + +const data = [ + { a: '123', key: '1' }, + { a: 'cdd', b: 'edd', key: '2' }, + { a: '1333', c: 'eee', d: 2, key: '3' }, +]; + +ReactDOM.render( +
    +

    rowClassName and className

    +
    `row-${i}`} + expandedRowRender={record =>

    extra: {record.a}

    } + expandedRowClassName={(record, i) => `ex-row-${i}`} + data={data} + className="table" + /> + , + document.getElementById('__react-content') +); diff --git a/components/vc-table/demo/colspan-rowspan.js b/components/vc-table/demo/colspan-rowspan.js new file mode 100644 index 000000000..c0e9b9187 --- /dev/null +++ b/components/vc-table/demo/colspan-rowspan.js @@ -0,0 +1,107 @@ +/* eslint-disable no-console,func-names,react/no-multi-comp */ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Table from 'rc-table'; +import 'rc-table/assets/index.less'; + +const columns = [ + { title: '手机号', dataIndex: 'a', colSpan: 2, width: 100, key: 'a', render(o, row, index) { + const obj = { + children: o, + props: {}, + }; + // 设置第一行为链接 + if (index === 0) { + obj.children = {o}; + } + // 第5行合并两列 + if (index === 4) { + obj.props.colSpan = 2; + } + + if (index === 5) { + obj.props.colSpan = 6; + } + return obj; + } }, + { title: '电话', dataIndex: 'b', colSpan: 0, width: 100, key: 'b', render(o, row, index) { + const obj = { + children: o, + props: {}, + }; + // 列合并掉的表格设置colSpan=0,不会去渲染 + if (index === 4 || index === 5) { + obj.props.colSpan = 0; + } + return obj; + } }, + { title: 'Name', dataIndex: 'c', width: 100, key: 'c', render(o, row, index) { + const obj = { + children: o, + props: {}, + }; + + if (index === 5) { + obj.props.colSpan = 0; + } + return obj; + } }, + { title: 'Address', dataIndex: 'd', width: 200, key: 'd', render(o, row, index) { + const obj = { + children: o, + props: {}, + }; + if (index === 0) { + obj.props.rowSpan = 2; + } + if (index === 1 || index === 5) { + obj.props.rowSpan = 0; + } + + return obj; + } }, + { title: 'Gender', dataIndex: 'e', width: 200, key: 'e', render(o, row, index) { + const obj = { + children: o, + props: {}, + }; + if (index === 5) { + obj.props.colSpan = 0; + } + return obj; + } }, + { + title: 'Operations', dataIndex: '', key: 'f', + render(o, row, index) { + if (index === 5) { + return { + props: { + colSpan: 0, + }, + }; + } + return Operations; + }, + }, +]; + +const data = [ + { a: '13812340987', b: '0571-12345678', c: '张三', d: '文一西路', e: 'Male', key: '1' }, + { a: '13812340986', b: '0571-98787658', c: '张夫人', d: '文一西路', e: 'Female', key: '2' }, + { a: '13812988888', b: '0571-099877', c: '李四', d: '文二西路', e: 'Male', key: '3' }, + { a: '1381200008888', b: '0571-099877', c: '王五', d: '文二西路', e: 'Male', key: '4' }, + { a: '0571-88888110', c: '李警官', d: '武林门', e: 'Male', key: '5' }, + { a: '资料统计完毕于xxxx年xxx月xxx日', key: '6' }, +]; + +ReactDOM.render( +
    +

    colSpan & rowSpan

    +
    + , + document.getElementById('__react-content') +); diff --git a/components/vc-table/demo/column-resize.js b/components/vc-table/demo/column-resize.js new file mode 100644 index 000000000..7a56e580d --- /dev/null +++ b/components/vc-table/demo/column-resize.js @@ -0,0 +1,84 @@ +/* eslint-disable no-console,func-names,react/no-multi-comp */ +import React from 'react'; +import ReactDOM from 'react-dom'; +import PropTypes from 'prop-types'; +import Table from 'rc-table'; +import 'rc-table/assets/index.less'; +import { Resizable } from 'react-resizable'; +import 'react-resizable/css/styles.css'; + +const ResizeableTitle = (props) => { + const { onResize, width, ...restProps } = props; + + if (!width) { + return + ) + } + + let leafColumns + + if (fixed === 'left') { + leafColumns = table.columnManager.leftLeafColumns() + } else if (fixed === 'right') { + leafColumns = table.columnManager.rightLeafColumns() + } else { + leafColumns = table.columnManager.leafColumns() + } + cols = cols.concat( + leafColumns.map(c => { + return ( + + ) + }) + ) + return ( + + {cols} + + ) + }, + +} + diff --git a/components/vc-table/src/Column.jsx b/components/vc-table/src/Column.jsx new file mode 100644 index 000000000..8ad12648c --- /dev/null +++ b/components/vc-table/src/Column.jsx @@ -0,0 +1,23 @@ +import PropTypes from '../../_util/vue-types' + +export default { + name: 'Column', + props: { + colSpan: PropTypes.number, + title: PropTypes.any, + dataIndex: PropTypes.string, + width: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.string, + ]), + fixed: PropTypes.oneOf([ + true, + 'left', + 'right', + ]), + render: PropTypes.func, + // onCellClick: PropTypes.func, + // onCell: PropTypes.func, + // onHeaderCell: PropTypes.func, + }, +} diff --git a/components/vc-table/src/ColumnGroup.jsx b/components/vc-table/src/ColumnGroup.jsx new file mode 100644 index 000000000..ad329cd15 --- /dev/null +++ b/components/vc-table/src/ColumnGroup.jsx @@ -0,0 +1,9 @@ +import PropTypes from '../../_util/vue-types' + +export default { + name: 'ColumnGroup', + props: { + title: PropTypes.any, + }, + isTableColumnGroup: true, +} diff --git a/components/vc-table/src/ColumnManager.js b/components/vc-table/src/ColumnManager.js new file mode 100644 index 000000000..a90756c14 --- /dev/null +++ b/components/vc-table/src/ColumnManager.js @@ -0,0 +1,150 @@ +export default class ColumnManager { + _cached = {} + + constructor (columns, elements) { + this.columns = columns || this.normalize(elements) + } + + isAnyColumnsFixed () { + return this._cache('isAnyColumnsFixed', () => { + return this.columns.some(column => !!column.fixed) + }) + } + + isAnyColumnsLeftFixed () { + return this._cache('isAnyColumnsLeftFixed', () => { + return this.columns.some( + column => column.fixed === 'left' || column.fixed === true + ) + }) + } + + isAnyColumnsRightFixed () { + return this._cache('isAnyColumnsRightFixed', () => { + return this.columns.some( + column => column.fixed === 'right' + ) + }) + } + + leftColumns () { + return this._cache('leftColumns', () => { + return this.groupedColumns().filter( + column => column.fixed === 'left' || column.fixed === true + ) + }) + } + + rightColumns () { + return this._cache('rightColumns', () => { + return this.groupedColumns().filter( + column => column.fixed === 'right' + ) + }) + } + + leafColumns () { + return this._cache('leafColumns', () => + this._leafColumns(this.columns) + ) + } + + leftLeafColumns () { + return this._cache('leftLeafColumns', () => + this._leafColumns(this.leftColumns()) + ) + } + + rightLeafColumns () { + return this._cache('rightLeafColumns', () => + this._leafColumns(this.rightColumns()) + ) + } + + // add appropriate rowspan and colspan to column + groupedColumns () { + return this._cache('groupedColumns', () => { + const _groupColumns = (columns, currentRow = 0, parentColumn = {}, rows = []) => { + // track how many rows we got + rows[currentRow] = rows[currentRow] || [] + const grouped = [] + const setRowSpan = column => { + const rowSpan = rows.length - currentRow + if (column && + !column.children && // parent columns are supposed to be one row + rowSpan > 1 && + (!column.rowSpan || column.rowSpan < rowSpan) + ) { + column.rowSpan = rowSpan + } + } + columns.forEach((column, index) => { + const newColumn = { ...column } + rows[currentRow].push(newColumn) + parentColumn.colSpan = parentColumn.colSpan || 0 + if (newColumn.children && newColumn.children.length > 0) { + newColumn.children = _groupColumns(newColumn.children, currentRow + 1, newColumn, rows) + parentColumn.colSpan = parentColumn.colSpan + newColumn.colSpan + } else { + parentColumn.colSpan++ + } + // update rowspan to all same row columns + for (let i = 0; i < rows[currentRow].length - 1; ++i) { + setRowSpan(rows[currentRow][i]) + } + // last column, update rowspan immediately + if (index + 1 === columns.length) { + setRowSpan(newColumn) + } + grouped.push(newColumn) + }) + return grouped + } + return _groupColumns(this.columns) + }) + } + + normalize (elements) { + const columns = [] + elements.forEach(element => { + if (!element.tag) { + return + } + debugger + const column = { ...element.props } + if (element.key) { + column.key = element.key + } + if (element.type.isTableColumnGroup) { + column.children = this.normalize(column.children) + } + columns.push(column) + }) + return columns + } + + reset (columns, elements) { + this.columns = columns || this.normalize(elements) + this._cached = {} + } + + _cache (name, fn) { + if (name in this._cached) { + return this._cached[name] + } + this._cached[name] = fn() + return this._cached[name] + } + + _leafColumns (columns) { + const leafColumns = [] + columns.forEach(column => { + if (!column.children) { + leafColumns.push(column) + } else { + leafColumns.push(...this._leafColumns(column.children)) + } + }) + return leafColumns + } +} diff --git a/components/vc-table/src/ExpandIcon.jsx b/components/vc-table/src/ExpandIcon.jsx new file mode 100644 index 000000000..490386d43 --- /dev/null +++ b/components/vc-table/src/ExpandIcon.jsx @@ -0,0 +1,34 @@ +import PropTypes from '../../_util/vue-types' +import BaseMixin from '../../_util/BaseMixin' +export default { + mixins: [BaseMixin], + name: 'ExpandIcon', + props: { + record: PropTypes.object, + prefixCls: PropTypes.string, + expandable: PropTypes.any, + expanded: PropTypes.bool, + needIndentSpaced: PropTypes.bool, + }, + methods: { + onExpand (e) { + this.__emit('expand', this.record, e) + }, + }, + + render () { + const { expandable, prefixCls, onExpand, needIndentSpaced, expanded } = this + if (expandable) { + const expandClassName = expanded ? 'expanded' : 'collapsed' + return ( + + ) + } else if (needIndentSpaced) { + return + } + return null + }, +} diff --git a/components/vc-table/src/ExpandableRow.js b/components/vc-table/src/ExpandableRow.js new file mode 100644 index 000000000..895cd886c --- /dev/null +++ b/components/vc-table/src/ExpandableRow.js @@ -0,0 +1,126 @@ +import PropTypes from '../../_util/vue-types' +import ExpandIcon from './ExpandIcon' +import BaseMixin from '../../_util/BaseMixin' + +const ExpandableRow = { + mixins: [BaseMixin], + name: 'ExpandableRow', + props: { + prefixCls: PropTypes.string.isRequired, + rowKey: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.number, + ]).isRequired, + fixed: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.bool, + ]), + record: PropTypes.object.isRequired, + indentSize: PropTypes.number, + needIndentSpaced: PropTypes.bool.isRequired, + expandRowByClick: PropTypes.bool, + expanded: PropTypes.bool.isRequired, + expandIconAsCell: PropTypes.bool, + expandIconColumnIndex: PropTypes.number, + childrenColumnName: PropTypes.string, + expandedRowRender: PropTypes.func, + // onExpandedChange: PropTypes.func.isRequired, + // onRowClick: PropTypes.func, + // children: PropTypes.func.isRequired, + }, + + beforeDestroy () { + this.handleDestroy() + }, + + hasExpandIcon (columnIndex) { + const { expandRowByClick } = this + return !this.expandIconAsCell && + !expandRowByClick && + columnIndex === this.expandIconColumnIndex + }, + + handleExpandChange (record, event) { + const { expanded, rowKey } = this + this.__emit('expandedChange', !expanded, record, event, rowKey) + }, + + handleDestroy () { + const { rowKey, record } = this + this.__emit('expandedChange', false, record, null, rowKey, true) + }, + + handleRowClick (record, index, event) { + const { expandRowByClick } = this + if (expandRowByClick) { + this.handleExpandChange(record, event) + } + this.__emit('rowClick', record, index, event) + }, + + renderExpandIcon () { + const { prefixCls, expanded, record, needIndentSpaced } = this + + return ( + + ) + }, + + renderExpandIconCell (cells) { + if (!this.expandIconAsCell) { + return + } + const { prefixCls } = this + + cells.push( + + ) + }, + + render () { + const { + childrenColumnName, + expandedRowRender, + indentSize, + record, + fixed, + $scopedSlots, + } = this + + this.expandIconAsCell = fixed !== 'right' ? this.expandIconAsCell : false + this.expandIconColumnIndex = fixed !== 'right' ? this.expandIconColumnIndex : -1 + const childrenData = record[childrenColumnName] + this.expandable = !!(childrenData || expandedRowRender) + + const expandableRowProps = { + props: { + indentSize, + hasExpandIcon: this.hasExpandIcon, + renderExpandIcon: this.renderExpandIcon, + renderExpandIconCell: this.renderExpandIconCell, + }, + on: { + rowClick: this.handleRowClick, + }, + + } + + return $scopedSlots.default && $scopedSlots.default(expandableRowProps) + }, +} + +export default connect(({ expandedRowKeys }, { rowKey }) => ({ + expanded: !!~expandedRowKeys.indexOf(rowKey), +}))(ExpandableRow) diff --git a/components/vc-table/src/ExpandableTable.js b/components/vc-table/src/ExpandableTable.js new file mode 100644 index 000000000..025a59872 --- /dev/null +++ b/components/vc-table/src/ExpandableTable.js @@ -0,0 +1,223 @@ +import React from 'react' +import PropTypes from 'prop-types' +import { connect } from 'mini-store' +import TableRow from './TableRow' +import { remove } from './utils' + +class ExpandableTable extends React.Component { + static propTypes = { + expandIconAsCell: PropTypes.bool, + expandedRowKeys: PropTypes.array, + expandedRowClassName: PropTypes.func, + defaultExpandAllRows: PropTypes.bool, + defaultExpandedRowKeys: PropTypes.array, + expandIconColumnIndex: PropTypes.number, + expandedRowRender: PropTypes.func, + childrenColumnName: PropTypes.string, + indentSize: PropTypes.number, + onExpand: PropTypes.func, + onExpandedRowsChange: PropTypes.func, + columnManager: PropTypes.object.isRequired, + store: PropTypes.object.isRequired, + prefixCls: PropTypes.string.isRequired, + data: PropTypes.array, + children: PropTypes.func.isRequired, + } + + static defaultProps = { + expandIconAsCell: false, + expandedRowClassName: () => '', + expandIconColumnIndex: 0, + defaultExpandAllRows: false, + defaultExpandedRowKeys: [], + childrenColumnName: 'children', + indentSize: 15, + onExpand () {}, + onExpandedRowsChange () {}, + } + + constructor (props) { + super(props) + + const { + data, + childrenColumnName, + defaultExpandAllRows, + expandedRowKeys, + defaultExpandedRowKeys, + getRowKey, + } = props + + let finnalExpandedRowKeys = [] + let rows = [...data] + + if (defaultExpandAllRows) { + for (let i = 0; i < rows.length; i++) { + const row = rows[i] + finnalExpandedRowKeys.push(getRowKey(row, i)) + rows = rows.concat(row[childrenColumnName] || []) + } + } else { + finnalExpandedRowKeys = expandedRowKeys || defaultExpandedRowKeys + } + + this.columnManager = props.columnManager + this.store = props.store + + this.store.setState({ + expandedRowsHeight: {}, + expandedRowKeys: finnalExpandedRowKeys, + }) + } + + componentWillReceiveProps (nextProps) { + if ('expandedRowKeys' in nextProps) { + this.store.setState({ + expandedRowKeys: nextProps.expandedRowKeys, + }) + } + } + + handleExpandChange = (expanded, record, event, rowKey, destroy = false) => { + if (event) { + event.preventDefault() + event.stopPropagation() + } + + const { onExpandedRowsChange, onExpand } = this.props + let { expandedRowKeys } = this.store.getState() + + if (expanded) { + // row was expaned + expandedRowKeys = [...expandedRowKeys, rowKey] + } else { + // row was collapse + const expandedRowIndex = expandedRowKeys.indexOf(rowKey) + if (expandedRowIndex !== -1) { + expandedRowKeys = remove(expandedRowKeys, rowKey) + } + } + + if (!this.props.expandedRowKeys) { + this.store.setState({ expandedRowKeys }) + } + + onExpandedRowsChange(expandedRowKeys) + if (!destroy) { + onExpand(expanded, record) + } + } + + renderExpandIndentCell = (rows, fixed) => { + const { prefixCls, expandIconAsCell } = this.props + if (!expandIconAsCell || fixed === 'right' || !rows.length) { + return + } + + const iconColumn = { + key: 'rc-table-expand-icon-cell', + className: `${prefixCls}-expand-icon-th`, + title: '', + rowSpan: rows.length, + } + + rows[0].unshift({ ...iconColumn, column: iconColumn }) + } + + renderExpandedRow (record, index, render, className, ancestorKeys, indent, fixed) { + const { prefixCls, expandIconAsCell, indentSize } = this.props + let colCount + if (fixed === 'left') { + colCount = this.columnManager.leftLeafColumns().length + } else if (fixed === 'right') { + colCount = this.columnManager.rightLeafColumns().length + } else { + colCount = this.columnManager.leafColumns().length + } + const columns = [{ + key: 'extra-row', + render: () => ({ + props: { + colSpan: colCount, + }, + children: fixed !== 'right' ? render(record, index, indent) : ' ', + }), + }] + if (expandIconAsCell && fixed !== 'right') { + columns.unshift({ + key: 'expand-icon-placeholder', + render: () => null, + }) + } + const parentKey = ancestorKeys[ancestorKeys.length - 1] + const rowKey = `${parentKey}-extra-row` + const components = { + body: { + row: 'tr', + cell: 'td', + }, + } + + return ( + + ) + } + + renderRows = (renderRows, rows, record, index, indent, fixed, parentKey, ancestorKeys) => { + const { expandedRowClassName, expandedRowRender, childrenColumnName } = this.props + const childrenData = record[childrenColumnName] + const nextAncestorKeys = [...ancestorKeys, parentKey] + const nextIndent = indent + 1 + + if (expandedRowRender) { + rows.push( + this.renderExpandedRow( + record, + index, + expandedRowRender, + expandedRowClassName(record, index, indent), + nextAncestorKeys, + nextIndent, + fixed, + ), + ) + } + + if (childrenData) { + rows.push( + ...renderRows( + childrenData, + nextIndent, + nextAncestorKeys, + ) + ) + } + } + + render () { + const { data, childrenColumnName, children } = this.props + const needIndentSpaced = data.some(record => record[childrenColumnName]) + + return children({ + props: this.props, + needIndentSpaced, + renderRows: this.renderRows, + handleExpandChange: this.handleExpandChange, + renderExpandIndentCell: this.renderExpandIndentCell, + }) + } +} + +export default connect()(ExpandableTable) diff --git a/components/vc-table/src/HeadTable.jsx b/components/vc-table/src/HeadTable.jsx new file mode 100644 index 000000000..e2095b364 --- /dev/null +++ b/components/vc-table/src/HeadTable.jsx @@ -0,0 +1,56 @@ +import PropTypes from '../../_util/vue-types' +import { measureScrollbar } from './utils' +import BaseTable from './BaseTable' + +export default { + name: 'HeadTable', + props: { + fixed: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.bool, + ]), + columns: PropTypes.array.isRequired, + tableClassName: PropTypes.string.isRequired, + handleBodyScrollLeft: PropTypes.func.isRequired, + expander: PropTypes.object.isRequired, + }, + render () { + const { columns, fixed, tableClassName, handleBodyScrollLeft, expander, table } = this + const { prefixCls, scroll, showHeader } = table + let { useFixedHeader } = table + const headStyle = {} + + if (scroll.y) { + useFixedHeader = true + // Add negative margin bottom for scroll bar overflow bug + const scrollbarWidth = measureScrollbar('horizontal') + if (scrollbarWidth > 0 && !fixed) { + headStyle.marginBottom = `-${scrollbarWidth}px` + headStyle.paddingBottom = '0px' + } + } + + if (!useFixedHeader || !showHeader) { + return null + } + return ( +
    + +
    + ) + }, + +} diff --git a/components/vc-table/src/Table.js b/components/vc-table/src/Table.js new file mode 100644 index 000000000..566e522de --- /dev/null +++ b/components/vc-table/src/Table.js @@ -0,0 +1,475 @@ +import React from 'react' +import PropTypes from 'prop-types' +import { debounce, warningOnce } from './utils' +import shallowequal from 'shallowequal' +import addEventListener from 'rc-util/lib/Dom/addEventListener' +import { Provider, create } from 'mini-store' +import merge from 'lodash/merge' +import ColumnManager from './ColumnManager' +import classes from 'component-classes' +import HeadTable from './HeadTable' +import BodyTable from './BodyTable' +import ExpandableTable from './ExpandableTable' + +export default class Table extends React.Component { + static propTypes = { + data: PropTypes.array, + useFixedHeader: PropTypes.bool, + columns: PropTypes.array, + prefixCls: PropTypes.string, + bodyStyle: PropTypes.object, + style: PropTypes.object, + rowKey: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), + rowClassName: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), + onRow: PropTypes.func, + onHeaderRow: PropTypes.func, + onRowClick: PropTypes.func, + onRowDoubleClick: PropTypes.func, + onRowContextMenu: PropTypes.func, + onRowMouseEnter: PropTypes.func, + onRowMouseLeave: PropTypes.func, + showHeader: PropTypes.bool, + title: PropTypes.func, + id: PropTypes.string, + footer: PropTypes.func, + emptyText: PropTypes.oneOfType([PropTypes.node, PropTypes.func]), + scroll: PropTypes.object, + rowRef: PropTypes.func, + getBodyWrapper: PropTypes.func, + children: PropTypes.node, + components: PropTypes.shape({ + table: PropTypes.any, + header: PropTypes.shape({ + wrapper: PropTypes.any, + row: PropTypes.any, + cell: PropTypes.any, + }), + body: PropTypes.shape({ + wrapper: PropTypes.any, + row: PropTypes.any, + cell: PropTypes.any, + }), + }), + ...ExpandableTable.PropTypes, + } + + static childContextTypes = { + table: PropTypes.any, + components: PropTypes.any, + } + + static defaultProps = { + data: [], + useFixedHeader: false, + rowKey: 'key', + rowClassName: () => '', + onRow () {}, + onHeaderRow () {}, + prefixCls: 'rc-table', + bodyStyle: {}, + style: {}, + showHeader: true, + scroll: {}, + rowRef: () => null, + emptyText: () => 'No Data', + } + + constructor (props) { + super(props); + + [ + 'onRowClick', + 'onRowDoubleClick', + 'onRowContextMenu', + 'onRowMouseEnter', + 'onRowMouseLeave', + ].forEach(name => { + warningOnce( + props[name] === undefined, + `${name} is deprecated, please use onRow instead.`, + ) + }) + + warningOnce( + props.getBodyWrapper === undefined, + 'getBodyWrapper is deprecated, please use custom components instead.', + ) + + this.columnManager = new ColumnManager(props.columns, props.children) + + this.store = create({ + currentHoverKey: null, + fixedColumnsHeadRowsHeight: [], + fixedColumnsBodyRowsHeight: [], + }) + + this.setScrollPosition('left') + + this.debouncedWindowResize = debounce(this.handleWindowResize, 150) + } + + getChildContext () { + return { + table: { + props: this.props, + columnManager: this.columnManager, + saveRef: this.saveRef, + components: merge({ + table: 'table', + header: { + wrapper: 'thead', + row: 'tr', + cell: 'th', + }, + body: { + wrapper: 'tbody', + row: 'tr', + cell: 'td', + }, + }, this.props.components), + }, + } + } + + componentDidMount () { + if (this.columnManager.isAnyColumnsFixed()) { + this.handleWindowResize() + this.resizeEvent = addEventListener( + window, 'resize', this.debouncedWindowResize + ) + } + } + + componentWillReceiveProps (nextProps) { + if (nextProps.columns && nextProps.columns !== this.props.columns) { + this.columnManager.reset(nextProps.columns) + } else if (nextProps.children !== this.props.children) { + this.columnManager.reset(null, nextProps.children) + } + } + + componentDidUpdate (prevProps) { + if (this.columnManager.isAnyColumnsFixed()) { + this.handleWindowResize() + if (!this.resizeEvent) { + this.resizeEvent = addEventListener( + window, 'resize', this.debouncedWindowResize + ) + } + } + // when table changes to empty, reset scrollLeft + if (prevProps.data.length > 0 && this.props.data.length === 0 && this.hasScrollX()) { + this.resetScrollX() + } + } + + componentWillUnmount () { + if (this.resizeEvent) { + this.resizeEvent.remove() + } + if (this.debouncedWindowResize) { + this.debouncedWindowResize.cancel() + } + } + + getRowKey = (record, index) => { + const rowKey = this.props.rowKey + const key = (typeof rowKey === 'function') + ? rowKey(record, index) : record[rowKey] + warningOnce( + key !== undefined, + 'Each record in table should have a unique `key` prop,' + + 'or set `rowKey` to an unique primary key.' + ) + return key === undefined ? index : key + } + + setScrollPosition (position) { + this.scrollPosition = position + if (this.tableNode) { + const { prefixCls } = this.props + if (position === 'both') { + classes(this.tableNode) + .remove(new RegExp(`^${prefixCls}-scroll-position-.+$`)) + .add(`${prefixCls}-scroll-position-left`) + .add(`${prefixCls}-scroll-position-right`) + } else { + classes(this.tableNode) + .remove(new RegExp(`^${prefixCls}-scroll-position-.+$`)) + .add(`${prefixCls}-scroll-position-${position}`) + } + } + } + + setScrollPositionClassName () { + const node = this.bodyTable + const scrollToLeft = node.scrollLeft === 0 + const scrollToRight = node.scrollLeft + 1 >= + node.children[0].getBoundingClientRect().width - + node.getBoundingClientRect().width + if (scrollToLeft && scrollToRight) { + this.setScrollPosition('both') + } else if (scrollToLeft) { + this.setScrollPosition('left') + } else if (scrollToRight) { + this.setScrollPosition('right') + } else if (this.scrollPosition !== 'middle') { + this.setScrollPosition('middle') + } + } + + handleWindowResize = () => { + this.syncFixedTableRowHeight() + this.setScrollPositionClassName() + } + + syncFixedTableRowHeight = () => { + const tableRect = this.tableNode.getBoundingClientRect() + // If tableNode's height less than 0, suppose it is hidden and don't recalculate rowHeight. + // see: https://github.com/ant-design/ant-design/issues/4836 + if (tableRect.height !== undefined && tableRect.height <= 0) { + return + } + const { prefixCls } = this.props + const headRows = this.headTable + ? this.headTable.querySelectorAll('thead') + : this.bodyTable.querySelectorAll('thead') + const bodyRows = this.bodyTable.querySelectorAll(`.${prefixCls}-row`) || [] + const fixedColumnsHeadRowsHeight = [].map.call( + headRows, row => row.getBoundingClientRect().height || 'auto' + ) + const fixedColumnsBodyRowsHeight = [].map.call( + bodyRows, row => row.getBoundingClientRect().height || 'auto' + ) + const state = this.store.getState() + if (shallowequal(state.fixedColumnsHeadRowsHeight, fixedColumnsHeadRowsHeight) && + shallowequal(state.fixedColumnsBodyRowsHeight, fixedColumnsBodyRowsHeight)) { + return + } + + this.store.setState({ + fixedColumnsHeadRowsHeight, + fixedColumnsBodyRowsHeight, + }) + } + + resetScrollX () { + if (this.headTable) { + this.headTable.scrollLeft = 0 + } + if (this.bodyTable) { + this.bodyTable.scrollLeft = 0 + } + } + + hasScrollX () { + const { scroll = {}} = this.props + return 'x' in scroll + } + + handleBodyScrollLeft = (e) => { + // Fix https://github.com/ant-design/ant-design/issues/7635 + if (e.currentTarget !== e.target) { + return + } + const target = e.target + const { scroll = {}} = this.props + const { headTable, bodyTable } = this + if (target.scrollLeft !== this.lastScrollLeft && scroll.x) { + if (target === bodyTable && headTable) { + headTable.scrollLeft = target.scrollLeft + } else if (target === headTable && bodyTable) { + bodyTable.scrollLeft = target.scrollLeft + } + this.setScrollPositionClassName() + } + // Remember last scrollLeft for scroll direction detecting. + this.lastScrollLeft = target.scrollLeft + } + + handleBodyScrollTop = (e) => { + const target = e.target + const { scroll = {}} = this.props + const { headTable, bodyTable, fixedColumnsBodyLeft, fixedColumnsBodyRight } = this + if (target.scrollTop !== this.lastScrollTop && scroll.y && target !== headTable) { + const scrollTop = target.scrollTop + if (fixedColumnsBodyLeft && target !== fixedColumnsBodyLeft) { + fixedColumnsBodyLeft.scrollTop = scrollTop + } + if (fixedColumnsBodyRight && target !== fixedColumnsBodyRight) { + fixedColumnsBodyRight.scrollTop = scrollTop + } + if (bodyTable && target !== bodyTable) { + bodyTable.scrollTop = scrollTop + } + } + // Remember last scrollTop for scroll direction detecting. + this.lastScrollTop = target.scrollTop + } + + handleBodyScroll = (e) => { + this.handleBodyScrollLeft(e) + this.handleBodyScrollTop(e) + } + + saveRef = (name) => (node) => { + this[name] = node + } + + renderMainTable () { + const { scroll, prefixCls } = this.props + const isAnyColumnsFixed = this.columnManager.isAnyColumnsFixed() + const scrollable = isAnyColumnsFixed || scroll.x || scroll.y + + const table = [ + this.renderTable({ + columns: this.columnManager.groupedColumns(), + isAnyColumnsFixed, + }), + this.renderEmptyText(), + this.renderFooter(), + ] + + return scrollable ? ( +
    {table}
    + ) : table + } + + renderLeftFixedTable () { + const { prefixCls } = this.props + + return ( +
    + {this.renderTable({ + columns: this.columnManager.leftColumns(), + fixed: 'left', + })} +
    + ) + } + + renderRightFixedTable () { + const { prefixCls } = this.props + + return ( +
    + {this.renderTable({ + columns: this.columnManager.rightColumns(), + fixed: 'right', + })} +
    + ) + } + + renderTable (options) { + const { columns, fixed, isAnyColumnsFixed } = options + const { prefixCls, scroll = {}} = this.props + const tableClassName = (scroll.x || fixed) ? `${prefixCls}-fixed` : '' + + const headTable = ( + + ) + + const bodyTable = ( + + ) + + return [headTable, bodyTable] + } + + renderTitle () { + const { title, prefixCls } = this.props + return title ? ( +
    + {title(this.props.data)} +
    + ) : null + } + + renderFooter () { + const { footer, prefixCls } = this.props + return footer ? ( +
    + {footer(this.props.data)} +
    + ) : null + } + + renderEmptyText () { + const { emptyText, prefixCls, data } = this.props + if (data.length) { + return null + } + const emptyClassName = `${prefixCls}-placeholder` + return ( +
    + {(typeof emptyText === 'function') ? emptyText() : emptyText} +
    + ) + } + + render () { + const props = this.props + const prefixCls = props.prefixCls + + let className = props.prefixCls + if (props.className) { + className += ` ${props.className}` + } + if (props.useFixedHeader || (props.scroll && props.scroll.y)) { + className += ` ${prefixCls}-fixed-header` + } + if (this.scrollPosition === 'both') { + className += ` ${prefixCls}-scroll-position-left ${prefixCls}-scroll-position-right` + } else { + className += ` ${prefixCls}-scroll-position-${this.scrollPosition}` + } + const hasLeftFixed = this.columnManager.isAnyColumnsLeftFixed() + const hasRightFixed = this.columnManager.isAnyColumnsRightFixed() + + return ( + + + {(expander) => { + this.expander = expander + return ( +
    + {this.renderTitle()} +
    + {this.renderMainTable()} + {hasLeftFixed && this.renderLeftFixedTable()} + {hasRightFixed && this.renderRightFixedTable()} +
    +
    + ) + }} +
    +
    + ) + } +} diff --git a/components/vc-table/src/TableCell.jsx b/components/vc-table/src/TableCell.jsx new file mode 100644 index 000000000..41bccae5e --- /dev/null +++ b/components/vc-table/src/TableCell.jsx @@ -0,0 +1,104 @@ +import PropTypes from '../../_util/vue-types' +import get from 'lodash/get' + +export default { + name: 'TableCell', + props: { + record: PropTypes.object, + prefixCls: PropTypes.string, + index: PropTypes.number, + indent: PropTypes.number, + indentSize: PropTypes.number, + column: PropTypes.object, + expandIcon: PropTypes.node, + component: PropTypes.any, + }, + methods: { + isInvalidRenderCellText (text) { + debugger + return text && + Object.prototype.toString.call(text) === '[object Object]' + }, + + handleClick (e) { + const { record, column: { onCellClick }} = this + if (onCellClick) { + onCellClick(record, e) + } + }, + }, + + render () { + const { + record, + indentSize, + prefixCls, + indent, + index, + expandIcon, + column, + component: BodyCell, + } = this + const { dataIndex, render } = column + + // We should return undefined if no dataIndex is specified, but in order to + // be compatible with object-path's behavior, we return the record object instead. + let text + if (typeof dataIndex === 'number') { + text = get(record, dataIndex) + } else if (!dataIndex || dataIndex.length === 0) { + text = record + } else { + text = get(record, dataIndex) + } + let tdProps = {} + let colSpan + let rowSpan + + if (render) { + text = render(text, record, index) + if (this.isInvalidRenderCellText(text)) { + tdProps = text.props || tdProps + colSpan = tdProps.colSpan + rowSpan = tdProps.rowSpan + text = text.children + } + } + + if (column.onCell) { + tdProps = { ...tdProps, ...column.onCell(record) } + } + + // Fix https://github.com/ant-design/ant-design/issues/1202 + if (this.isInvalidRenderCellText(text)) { + text = null + } + + const indentText = expandIcon ? ( + + ) : null + + if (rowSpan === 0 || colSpan === 0) { + return null + } + + if (column.align) { + tdProps.style = { textAlign: column.align } + } + console.log('tdProps', tdProps) + + return ( + + {indentText} + {expandIcon} + {text} + + ) + }, +} diff --git a/components/vc-table/src/TableHeader.jsx b/components/vc-table/src/TableHeader.jsx new file mode 100644 index 000000000..b0a1be40f --- /dev/null +++ b/components/vc-table/src/TableHeader.jsx @@ -0,0 +1,85 @@ +import PropTypes from '../../_util/vue-types' +import TableHeaderRow from './TableHeaderRow' + +function getHeaderRows (columns, currentRow = 0, rows) { + rows = rows || [] + rows[currentRow] = rows[currentRow] || [] + + columns.forEach(column => { + if (column.rowSpan && rows.length < column.rowSpan) { + while (rows.length < column.rowSpan) { + rows.push([]) + } + } + const cell = { + key: column.key, + className: column.className || '', + children: column.title, + column, + } + if (column.children) { + getHeaderRows(column.children, currentRow + 1, rows) + } + if ('colSpan' in column) { + cell.colSpan = column.colSpan + } + if ('rowSpan' in column) { + cell.rowSpan = column.rowSpan + } + if (cell.colSpan !== 0) { + rows[currentRow].push(cell) + } + }) + return rows.filter(row => row.length > 0) +} + +export default { + name: 'TableHeader', + props: { + fixed: PropTypes.string, + columns: PropTypes.array.isRequired, + expander: PropTypes.object.isRequired, + + }, + methods: { + onHeaderRow () { + this.table.__emit('headerRow', ...arguments) + }, + }, + + render () { + const { components, prefixCls, showHeader } = this.table + const { expander, columns, fixed, onHeaderRow } = this + + if (!showHeader) { + return null + } + + const rows = getHeaderRows(columns) + + expander.renderExpandIndentCell(rows, fixed) + + const HeaderWrapper = components.header.wrapper + + return ( + + { + rows.map((row, index) => ( + + )) + } + + ) + }, + +} + diff --git a/components/vc-table/src/TableHeaderRow.js b/components/vc-table/src/TableHeaderRow.js new file mode 100644 index 000000000..d801ba46d --- /dev/null +++ b/components/vc-table/src/TableHeaderRow.js @@ -0,0 +1,53 @@ +import React from 'react' +import { connect } from 'mini-store' + +function TableHeaderRow ({ row, index, height, components, onHeaderRow }) { + const HeaderRow = components.header.row + const HeaderCell = components.header.cell + const rowProps = onHeaderRow(row.map(cell => cell.column), index) + const customStyle = rowProps ? rowProps.style : {} + const style = { height, ...customStyle } + + return ( + + {row.map((cell, i) => { + const { column, ...cellProps } = cell + const customProps = column.onHeaderCell ? column.onHeaderCell(column) : {} + if (column.align) { + cellProps.style = { textAlign: column.align } + } + return ( + + ) + })} + + ) +} + +function getRowHeight (state, props) { + const { fixedColumnsHeadRowsHeight } = state + const { columns, rows, fixed } = props + const headerHeight = fixedColumnsHeadRowsHeight[0] + + if (!fixed) { + return null + } + + if (headerHeight && columns) { + if (headerHeight === 'auto') { + return 'auto' + } + return headerHeight / rows.length + } + return null +} + +export default connect((state, props) => { + return { + height: getRowHeight(state, props), + } +})(TableHeaderRow) diff --git a/components/vc-table/src/TableRow.js b/components/vc-table/src/TableRow.js new file mode 100644 index 000000000..3348bb559 --- /dev/null +++ b/components/vc-table/src/TableRow.js @@ -0,0 +1,291 @@ +import React from 'react' +import ReactDOM from 'react-dom' +import PropTypes from 'prop-types' +import { connect } from 'mini-store' +import TableCell from './TableCell' +import { warningOnce } from './utils' + +class TableRow extends React.Component { + static propTypes = { + onRow: PropTypes.func, + onRowClick: PropTypes.func, + onRowDoubleClick: PropTypes.func, + onRowContextMenu: PropTypes.func, + onRowMouseEnter: PropTypes.func, + onRowMouseLeave: PropTypes.func, + record: PropTypes.object, + prefixCls: PropTypes.string, + onHover: PropTypes.func, + columns: PropTypes.array, + height: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.number, + ]), + index: PropTypes.number, + rowKey: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.number, + ]).isRequired, + className: PropTypes.string, + indent: PropTypes.number, + indentSize: PropTypes.number, + hasExpandIcon: PropTypes.func.isRequired, + hovered: PropTypes.bool.isRequired, + visible: PropTypes.bool.isRequired, + store: PropTypes.object.isRequired, + fixed: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.bool, + ]), + renderExpandIcon: PropTypes.func, + renderExpandIconCell: PropTypes.func, + components: PropTypes.any, + expandedRow: PropTypes.bool, + isAnyColumnsFixed: PropTypes.bool, + ancestorKeys: PropTypes.array.isRequired, + } + + static defaultProps = { + onRow () {}, + expandIconColumnIndex: 0, + expandRowByClick: false, + onHover () {}, + hasExpandIcon () {}, + renderExpandIcon () {}, + renderExpandIconCell () {}, + } + + constructor (props) { + super(props) + + this.shouldRender = props.visible + } + + componentDidMount () { + if (this.shouldRender) { + this.saveRowRef() + } + } + + componentWillReceiveProps (nextProps) { + if (this.props.visible || (!this.props.visible && nextProps.visible)) { + this.shouldRender = true + } + } + + shouldComponentUpdate (nextProps) { + return !!(this.props.visible || nextProps.visible) + } + + componentDidUpdate () { + if (this.shouldRender && !this.rowRef) { + this.saveRowRef() + } + } + + onRowClick = (event) => { + const { record, index, onRowClick } = this.props + if (onRowClick) { + onRowClick(record, index, event) + } + } + + onRowDoubleClick = (event) => { + const { record, index, onRowDoubleClick } = this.props + if (onRowDoubleClick) { + onRowDoubleClick(record, index, event) + } + } + + onContextMenu = (event) => { + const { record, index, onRowContextMenu } = this.props + if (onRowContextMenu) { + onRowContextMenu(record, index, event) + } + } + + onMouseEnter = (event) => { + const { record, index, onRowMouseEnter, onHover, rowKey } = this.props + onHover(true, rowKey) + if (onRowMouseEnter) { + onRowMouseEnter(record, index, event) + } + } + + onMouseLeave = (event) => { + const { record, index, onRowMouseLeave, onHover, rowKey } = this.props + onHover(false, rowKey) + if (onRowMouseLeave) { + onRowMouseLeave(record, index, event) + } + } + + setExpanedRowHeight () { + const { store, rowKey } = this.props + let { expandedRowsHeight } = store.getState() + const height = this.rowRef.getBoundingClientRect().height + expandedRowsHeight = { + ...expandedRowsHeight, + [rowKey]: height, + } + store.setState({ expandedRowsHeight }) + } + + setRowHeight () { + const { store, index } = this.props + const fixedColumnsBodyRowsHeight = store.getState().fixedColumnsBodyRowsHeight.slice() + const height = this.rowRef.getBoundingClientRect().height + fixedColumnsBodyRowsHeight[index] = height + store.setState({ fixedColumnsBodyRowsHeight }) + } + + getStyle () { + const { height, visible } = this.props + + if (height && height !== this.style.height) { + this.style = { ...this.style, height } + } + + if (!visible && !this.style.display) { + this.style = { ...this.style, display: 'none' } + } + + return this.style + } + + saveRowRef () { + this.rowRef = ReactDOM.findDOMNode(this) + + const { isAnyColumnsFixed, fixed, expandedRow, ancestorKeys } = this.props + + if (!isAnyColumnsFixed) { + return + } + + if (!fixed && expandedRow) { + this.setExpanedRowHeight() + } + + if (!fixed && ancestorKeys.length >= 0) { + this.setRowHeight() + } + } + + render () { + if (!this.shouldRender) { + return null + } + + const { + prefixCls, + columns, + record, + index, + onRow, + indent, + indentSize, + hovered, + height, + visible, + components, + hasExpandIcon, + renderExpandIcon, + renderExpandIconCell, + } = this.props + + const BodyRow = components.body.row + const BodyCell = components.body.cell + + let { className } = this.props + + if (hovered) { + className += ` ${prefixCls}-hover` + } + + const cells = [] + + renderExpandIconCell(cells) + + for (let i = 0; i < columns.length; i++) { + const column = columns[i] + + warningOnce( + column.onCellClick === undefined, + 'column[onCellClick] is deprecated, please use column[onCell] instead.', + ) + + cells.push( + + ) + } + + const rowClassName = + `${prefixCls} ${className} ${prefixCls}-level-${indent}`.trim() + + const rowProps = onRow(record, index) + const customStyle = rowProps ? rowProps.style : {} + let style = { height } + + if (!visible) { + style.display = 'none' + } + + style = { ...style, ...customStyle } + + return ( + + {cells} + + ) + } +} + +function getRowHeight (state, props) { + const { expandedRowsHeight, fixedColumnsBodyRowsHeight } = state + const { fixed, index, rowKey } = props + + if (!fixed) { + return null + } + + if (expandedRowsHeight[rowKey]) { + return expandedRowsHeight[rowKey] + } + + if (fixedColumnsBodyRowsHeight[index]) { + return fixedColumnsBodyRowsHeight[index] + } + + return null +} + +export default connect((state, props) => { + const { currentHoverKey, expandedRowKeys } = state + const { rowKey, ancestorKeys } = props + const visible = ancestorKeys.length === 0 || ancestorKeys.every(k => ~expandedRowKeys.indexOf(k)) + + return ({ + visible, + hovered: currentHoverKey === rowKey, + height: getRowHeight(state, props), + }) +})(TableRow) diff --git a/components/vc-table/src/utils.js b/components/vc-table/src/utils.js new file mode 100644 index 000000000..5d3ef7c37 --- /dev/null +++ b/components/vc-table/src/utils.js @@ -0,0 +1,84 @@ +import warning from 'warning' + +let scrollbarSize + +// Measure scrollbar width for padding body during modal show/hide +const scrollbarMeasure = { + position: 'absolute', + top: '-9999px', + width: '50px', + height: '50px', + overflow: 'scroll', +} + +export function measureScrollbar (direction = 'vertical') { + if (typeof document === 'undefined' || typeof window === 'undefined') { + return 0 + } + if (scrollbarSize) { + return scrollbarSize + } + const scrollDiv = document.createElement('div') + for (const scrollProp in scrollbarMeasure) { + if (scrollbarMeasure.hasOwnProperty(scrollProp)) { + scrollDiv.style[scrollProp] = scrollbarMeasure[scrollProp] + } + } + document.body.appendChild(scrollDiv) + let size = 0 + if (direction === 'vertical') { + size = scrollDiv.offsetWidth - scrollDiv.clientWidth + } else if (direction === 'horizontal') { + size = scrollDiv.offsetHeight - scrollDiv.clientHeight + } + + document.body.removeChild(scrollDiv) + scrollbarSize = size + return scrollbarSize +} + +export function debounce (func, wait, immediate) { + let timeout + function debounceFunc () { + const context = this + const args = arguments + // https://fb.me/react-event-pooling + if (args[0] && args[0].persist) { + args[0].persist() + } + const later = () => { + timeout = null + if (!immediate) { + func.apply(context, args) + } + } + const callNow = immediate && !timeout + clearTimeout(timeout) + timeout = setTimeout(later, wait) + if (callNow) { + func.apply(context, args) + } + } + debounceFunc.cancel = function cancel () { + if (timeout) { + clearTimeout(timeout) + timeout = null + } + } + return debounceFunc +} + +const warned = {} +export function warningOnce (condition, format, args) { + if (!warned[format]) { + warning(condition, format, args) + warned[format] = !condition + } +} + +export function remove (array, item) { + const index = array.indexOf(item) + const front = array.slice(0, index) + const last = array.slice(index + 1, array.length) + return front.concat(last) +} From 063078b42cdbf4ea7425fd7ff3df1f612a79b3b1 Mon Sep 17 00:00:00 2001 From: tjz <415800467@qq.com> Date: Sun, 25 Mar 2018 18:07:04 +0800 Subject: [PATCH 6/8] add vc-table --- components/_util/store/PropTypes.js | 7 + components/_util/store/Provider.jsx | 16 + components/_util/store/connect.jsx | 85 +++ components/_util/store/create.js | 30 ++ components/_util/store/index.js | 5 + components/vc-table/assets/index.less | 2 + components/vc-table/assets/normalize.css | 220 ++++++++ components/vc-table/demo/animation.js | 70 --- components/vc-table/demo/childrenIndent.js | 30 +- components/vc-table/demo/className.js | 45 -- components/vc-table/demo/colspan-rowspan.js | 107 ---- components/vc-table/demo/column-resize.js | 84 --- components/vc-table/demo/dropdown.js | 111 ---- components/vc-table/demo/expandedRowRender.js | 114 ---- .../vc-table/demo/fixedColumns-auto-height.js | 46 -- components/vc-table/demo/fixedColumns.js | 45 -- .../vc-table/demo/fixedColumnsAndHeader.js | 39 -- .../fixedColumnsAndHeaderSyncRowHeight.js | 39 -- .../vc-table/demo/fixedColumnsWhenResize.js | 45 -- components/vc-table/demo/grouping-columns.js | 100 ---- components/vc-table/demo/hide-header.js | 34 -- components/vc-table/demo/jsx.js | 49 -- components/vc-table/demo/key.js | 70 --- components/vc-table/demo/nested.js | 45 -- components/vc-table/demo/no-data.js | 26 - components/vc-table/demo/react-dnd.js | 187 ------- components/vc-table/demo/rowAndCellClick.js | 105 ---- components/vc-table/demo/scrollX.js | 34 -- components/vc-table/demo/scrollXY.js | 45 -- components/vc-table/demo/scrollY.js | 62 --- components/vc-table/demo/simple.js | 30 -- components/vc-table/demo/styled-components.js | 45 -- components/vc-table/demo/subTable.js | 63 --- components/vc-table/demo/title-and-footer.js | 35 -- components/vc-table/src/BaseTable.jsx | 22 +- components/vc-table/src/ColGroup.jsx | 12 +- .../{ColumnManager.js => ColumnManager.jsx} | 3 +- .../{ExpandableRow.js => ExpandableRow.jsx} | 99 ++-- components/vc-table/src/ExpandableTable.js | 223 -------- components/vc-table/src/ExpandableTable.jsx | 223 ++++++++ components/vc-table/src/HeadTable.jsx | 3 + components/vc-table/src/Table.js | 475 ---------------- components/vc-table/src/Table.jsx | 507 ++++++++++++++++++ components/vc-table/src/TableCell.jsx | 4 +- components/vc-table/src/TableHeader.jsx | 5 +- components/vc-table/src/TableHeaderRow.js | 53 -- components/vc-table/src/TableHeaderRow.jsx | 71 +++ .../src/{TableRow.js => TableRow.jsx} | 233 ++++---- examples/routes.js | 2 +- package-lock.json | 65 +-- 50 files changed, 1406 insertions(+), 2664 deletions(-) create mode 100644 components/_util/store/PropTypes.js create mode 100644 components/_util/store/Provider.jsx create mode 100644 components/_util/store/connect.jsx create mode 100644 components/_util/store/create.js create mode 100644 components/_util/store/index.js create mode 100644 components/vc-table/assets/normalize.css delete mode 100644 components/vc-table/demo/animation.js delete mode 100644 components/vc-table/demo/className.js delete mode 100644 components/vc-table/demo/colspan-rowspan.js delete mode 100644 components/vc-table/demo/column-resize.js delete mode 100644 components/vc-table/demo/dropdown.js delete mode 100644 components/vc-table/demo/expandedRowRender.js delete mode 100644 components/vc-table/demo/fixedColumns-auto-height.js delete mode 100644 components/vc-table/demo/fixedColumns.js delete mode 100644 components/vc-table/demo/fixedColumnsAndHeader.js delete mode 100644 components/vc-table/demo/fixedColumnsAndHeaderSyncRowHeight.js delete mode 100644 components/vc-table/demo/fixedColumnsWhenResize.js delete mode 100644 components/vc-table/demo/grouping-columns.js delete mode 100644 components/vc-table/demo/hide-header.js delete mode 100644 components/vc-table/demo/jsx.js delete mode 100644 components/vc-table/demo/key.js delete mode 100644 components/vc-table/demo/nested.js delete mode 100644 components/vc-table/demo/no-data.js delete mode 100644 components/vc-table/demo/react-dnd.js delete mode 100644 components/vc-table/demo/rowAndCellClick.js delete mode 100644 components/vc-table/demo/scrollX.js delete mode 100644 components/vc-table/demo/scrollXY.js delete mode 100644 components/vc-table/demo/scrollY.js delete mode 100644 components/vc-table/demo/simple.js delete mode 100644 components/vc-table/demo/styled-components.js delete mode 100644 components/vc-table/demo/subTable.js delete mode 100644 components/vc-table/demo/title-and-footer.js rename components/vc-table/src/{ColumnManager.js => ColumnManager.jsx} (99%) rename components/vc-table/src/{ExpandableRow.js => ExpandableRow.jsx} (56%) delete mode 100644 components/vc-table/src/ExpandableTable.js create mode 100644 components/vc-table/src/ExpandableTable.jsx delete mode 100644 components/vc-table/src/Table.js create mode 100644 components/vc-table/src/Table.jsx delete mode 100644 components/vc-table/src/TableHeaderRow.js create mode 100644 components/vc-table/src/TableHeaderRow.jsx rename components/vc-table/src/{TableRow.js => TableRow.jsx} (52%) diff --git a/components/_util/store/PropTypes.js b/components/_util/store/PropTypes.js new file mode 100644 index 000000000..603ffe6bb --- /dev/null +++ b/components/_util/store/PropTypes.js @@ -0,0 +1,7 @@ +import PropTypes from '../vue-types' + +export const storeShape = PropTypes.shape({ + subscribe: PropTypes.func.isRequired, + setState: PropTypes.func.isRequired, + getState: PropTypes.func.isRequired, +}) diff --git a/components/_util/store/Provider.jsx b/components/_util/store/Provider.jsx new file mode 100644 index 000000000..0b01a3397 --- /dev/null +++ b/components/_util/store/Provider.jsx @@ -0,0 +1,16 @@ + +import { storeShape } from './PropTypes' +export default { + name: 'StoreProvider', + props: { + store: storeShape.isRequired, + }, + provide () { + return { + _store: this.$props, + } + }, + render () { + return this.$slots.default[0] + }, +} diff --git a/components/_util/store/connect.jsx b/components/_util/store/connect.jsx new file mode 100644 index 000000000..40296f406 --- /dev/null +++ b/components/_util/store/connect.jsx @@ -0,0 +1,85 @@ +import shallowEqual from 'shallowequal' +import omit from 'omit.js' +import { getOptionProps } from '../props-util' +import PropTypes from '../vue-types' + +function getDisplayName (WrappedComponent) { + return WrappedComponent.name || 'Component' +} + +const defaultMapStateToProps = () => ({}) +export default function connect (mapStateToProps) { + const shouldSubscribe = !!mapStateToProps + const finnalMapStateToProps = mapStateToProps || defaultMapStateToProps + return function wrapWithConnect (WrappedComponent) { + const tempProps = omit(WrappedComponent.props || {}, ['store']) + const props = {} + Object.keys(tempProps).forEach(k => { props[k] = PropTypes.any }) + const Connect = { + name: `Connect_${getDisplayName(WrappedComponent)}`, + props, + inject: { + _store: { default: {}}, + }, + data () { + this.store = this._store.store + return { + subscribed: finnalMapStateToProps(this.store.getState(), this.$props), + } + }, + mounted () { + this.trySubscribe() + }, + + beforeDestroy () { + this.tryUnsubscribe() + }, + methods: { + handleChange () { + if (!this.unsubscribe) { + return + } + + const nextState = finnalMapStateToProps(this.store.getState(), this.$props) + if (!shallowEqual(this.nextState, nextState)) { + this.nextState = nextState + this.subscribed = nextState + } + }, + + trySubscribe () { + if (shouldSubscribe) { + this.unsubscribe = this.store.subscribe(this.handleChange) + this.handleChange() + } + }, + + tryUnsubscribe () { + if (this.unsubscribe) { + this.unsubscribe() + this.unsubscribe = null + } + }, + }, + render () { + const { $listeners, $slots, $attrs, $scopedSlots, subscribed, store } = this + const props = getOptionProps(this) + const wrapProps = { + props: { + ...props, + ...subscribed, + store, + }, + on: $listeners, + attrs: $attrs, + slots: $slots, + scopedSlots: $scopedSlots, + } + return ( + + ) + }, + } + return Connect + } +} diff --git a/components/_util/store/create.js b/components/_util/store/create.js new file mode 100644 index 000000000..57ce3e083 --- /dev/null +++ b/components/_util/store/create.js @@ -0,0 +1,30 @@ +export default function create (initialState) { + let state = initialState + const listeners = [] + + function setState (partial) { + state = { ...state, ...partial } + for (let i = 0; i < listeners.length; i++) { + listeners[i]() + } + } + + function getState () { + return state + } + + function subscribe (listener) { + listeners.push(listener) + + return function unsubscribe () { + const index = listeners.indexOf(listener) + listeners.splice(index, 1) + } + } + + return { + setState, + getState, + subscribe, + } +} diff --git a/components/_util/store/index.js b/components/_util/store/index.js new file mode 100644 index 000000000..62d58a9a3 --- /dev/null +++ b/components/_util/store/index.js @@ -0,0 +1,5 @@ +export { default as Provider } from './Provider' + +export { default as connect } from './connect' + +export { default as create } from './create' diff --git a/components/vc-table/assets/index.less b/components/vc-table/assets/index.less index 173d5f1eb..f6c6faa4a 100644 --- a/components/vc-table/assets/index.less +++ b/components/vc-table/assets/index.less @@ -1,3 +1,5 @@ +@import 'normalize.css'; + @tablePrefixCls: rc-table; @text-color : #666; @font-size-base : 12px; diff --git a/components/vc-table/assets/normalize.css b/components/vc-table/assets/normalize.css new file mode 100644 index 000000000..a5b29a0c0 --- /dev/null +++ b/components/vc-table/assets/normalize.css @@ -0,0 +1,220 @@ +/*! normalize.css v3.0.1 | MIT License | git.io/normalize */ + +html { + font-family: sans-serif; + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100% +} + +body { + margin: 0 +} + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +nav, +section, +summary { + display: block +} + +audio, +canvas, +progress, +video { + display: inline-block; + vertical-align: baseline +} + +audio:not([controls]) { + display: none; + height: 0 +} + +[hidden], +template { + display: none +} + +a { + background: 0 0 +} + +a:active, +a:hover { + outline: 0 +} + +abbr[title] { + border-bottom: 1px dotted +} + +b, +strong { + font-weight: 700 +} + +dfn { + font-style: italic +} + +h1 { + font-size: 2em; + margin: .67em 0 +} + +mark { + background: #ff0; + color: #000 +} + +small { + font-size: 80% +} + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline +} + +sup { + top: -.5em +} + +sub { + bottom: -.25em +} + +img { + border: 0 +} + +svg:not(:root) { + overflow: hidden +} + +figure { + margin: 1em 40px +} + +hr { + -moz-box-sizing: content-box; + box-sizing: content-box; + height: 0 +} + +pre { + overflow: auto +} + +code, +kbd, +pre, +samp { + font-family: monospace, monospace; + font-size: 1em +} + +button, +input, +optgroup, +select, +textarea { + color: inherit; + font: inherit; + margin: 0 +} + +button { + overflow: visible +} + +button, +select { + text-transform: none +} + +button, +html input[type=button], +input[type=reset], +input[type=submit] { + -webkit-appearance: button; + cursor: pointer +} + +button[disabled], +html input[disabled] { + cursor: default +} + +button::-moz-focus-inner, +input::-moz-focus-inner { + border: 0; + padding: 0 +} + +input { + line-height: normal +} + +input[type=checkbox], +input[type=radio] { + box-sizing: border-box; + padding: 0 +} + +input[type=number]::-webkit-inner-spin-button, +input[type=number]::-webkit-outer-spin-button { + height: auto +} + +input[type=search] { + -webkit-appearance: textfield; + -moz-box-sizing: content-box; + -webkit-box-sizing: content-box; + box-sizing: content-box +} + +input[type=search]::-webkit-search-cancel-button, +input[type=search]::-webkit-search-decoration { + -webkit-appearance: none +} + +fieldset { + border: 1px solid silver; + margin: 0 2px; + padding: .35em .625em .75em +} + +legend { + border: 0; + padding: 0 +} + +textarea { + overflow: auto +} + +optgroup { + font-weight: 700 +} + +table { + border-collapse: collapse; + border-spacing: 0 +} + +td, +th { + padding: 0 +} \ No newline at end of file diff --git a/components/vc-table/demo/animation.js b/components/vc-table/demo/animation.js deleted file mode 100644 index 6f379dcdc..000000000 --- a/components/vc-table/demo/animation.js +++ /dev/null @@ -1,70 +0,0 @@ -/* eslint-disable no-console,func-names,react/no-multi-comp */ -import React from 'react'; -import ReactDOM from 'react-dom'; -import Table from 'rc-table'; -import Animate from 'rc-animate'; -import 'rc-table/assets/index.less'; -import 'rc-table/assets/animation.less'; - -const AnimateBody = (props) => - ; - -class Demo extends React.Component { - constructor(props) { - super(props); - this.columns = [ - { title: 'title1', dataIndex: 'a', key: 'a', width: 100 }, - { id: '123', title: 'title2', dataIndex: 'b', key: 'b', width: 100 }, - { title: 'title3', dataIndex: 'c', key: 'c', width: 200 }, - { - title: 'Operations', dataIndex: '', key: 'd', render: (text, record) => - this.onDelete(record.key, e)} href="#">Delete, - }, - ]; - this.state = { - data: [ - { a: '123', key: '1' }, - { a: 'cdd', b: 'edd', key: '2' }, - { a: '1333', c: 'eee', key: '3' }, - ], - }; - } - - onDelete(key, e) { - console.log('Delete', key); - e.preventDefault(); - const data = this.state.data.filter(item => item.key !== key); - this.setState({ data }); - } - - onAdd() { - const data = [...this.state.data]; - data.push({ - a: 'new data', - b: 'new data', - c: 'new data', - key: Date.now(), - }); - this.setState({ data }); - } - - render() { - return ( -
    -

    Table row with animation

    - -
    ; + } + + return ( + + + + ); +}; + +ResizeableTitle.propTypes = { + onResize: PropTypes.func.isRequired, + width: PropTypes.number, +}; + +class Demo extends React.Component { + state = { + columns: [ + { title: 'title1', dataIndex: 'a', key: 'a', width: 100 }, + { id: '123', title: 'title2', dataIndex: 'b', key: 'b', width: 100 }, + { title: 'title3', dataIndex: 'c', key: 'c', width: 200 }, + { + title: 'Operations', dataIndex: '', key: 'd', render() { + return Operations; + }, + }, + ], + } + + components = { + header: { + cell: ResizeableTitle, + }, + } + + data = [ + { a: '123', key: '1' }, + { a: 'cdd', b: 'edd', key: '2' }, + { a: '1333', c: 'eee', d: 2, key: '3' }, + ] + + handleResize = index => (e, { size }) => { + this.setState(({ columns }) => { + const nextColumns = [...columns]; + nextColumns[index] = { + ...nextColumns[index], + width: size.width, + }; + return { columns: nextColumns }; + }); + } + + render() { + const columns = this.state.columns.map((col, index) => ({ + ...col, + onHeaderCell: (column) => ({ + width: column.width, + onResize: this.handleResize(index), + }), + })); + + return ( +
    +

    Integrate with react-resizable

    + + + ); + } +} + +ReactDOM.render(, document.getElementById('__react-content')); diff --git a/components/vc-table/demo/dropdown.js b/components/vc-table/demo/dropdown.js new file mode 100644 index 000000000..38c2f5e2c --- /dev/null +++ b/components/vc-table/demo/dropdown.js @@ -0,0 +1,111 @@ +/* eslint-disable no-console,func-names,react/no-multi-comp */ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Table from 'rc-table'; +import Menu, { Item, Divider } from 'rc-menu'; +import DropDown from 'rc-dropdown'; +import 'rc-table/assets/index.less'; +import 'rc-dropdown/assets/index.css'; +import 'rc-menu/assets/index.css'; + +const data = []; +for (let i = 0; i < 10; i++) { + data.push({ + key: i, + a: `a${i}`, + b: `b${i}`, + c: `c${i}`, + }); +} + +class Demo extends React.Component { + state = { + visible: false, + } + + filters = [] + + handleVisibleChange = (visible) => { + this.setState({ visible }); + } + + handleSelect = (selected) => { + this.filters.push(selected); + } + + handleDeselect = (key) => { + const index = this.filters.indexOf(key); + if (index !== -1) { + this.filters.splice(index, 1); + } + } + + confirmFilter = () => { + console.log(this.filters.join(',')); + this.setState({ + visible: false, + }); + } + + render() { + const menu = ( + + one + two + three + + + + + + ); + + const columns = [ + { + title: ( +
    + title1 + + filter + +
    + ), key: 'a', dataIndex: 'a', width: 100, + }, + { title: 'title2', key: 'b', dataIndex: 'b', width: 100 }, + { title: 'title3', key: 'c', dataIndex: 'c', width: 200 }, + ]; + + return ( +
    record.key} + /> + ); + } +} + +ReactDOM.render( +
    +

    use dropdown

    + +
    , + document.getElementById('__react-content') +); diff --git a/components/vc-table/demo/expandedRowRender.js b/components/vc-table/demo/expandedRowRender.js new file mode 100644 index 000000000..6e04bcc12 --- /dev/null +++ b/components/vc-table/demo/expandedRowRender.js @@ -0,0 +1,114 @@ +/* eslint-disable no-console,func-names,react/no-multi-comp */ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Table from 'rc-table'; +import 'rc-table/assets/index.less'; + +const tableData = [ + { key: 0, a: '123' }, + { key: 1, a: 'cdd', b: 'edd' }, + { key: 2, a: '1333', c: 'eee', d: 2 }, +]; + +class Demo extends React.Component { + state = { + data: tableData, + expandedRowKeys: [], + expandIconAsCell: true, + expandRowByClick: false, + } + + onExpand = (expanded, record) => { + console.log('onExpand', expanded, record); + } + + onExpandedRowsChange = (rows) => { + this.setState({ + expandedRowKeys: rows, + }); + } + + onExpandIconAsCellChange = (e) => { + this.setState({ + expandIconAsCell: e.target.checked, + }); + } + + onExpandRowByClickChange = (e) => { + this.setState({ + expandRowByClick: e.target.checked, + }); + } + + columns = [ + { title: 'title 1', dataIndex: 'a', key: 'a', width: 100 }, + { title: 'title 2', dataIndex: 'b', key: 'b', width: 100 }, + { title: 'title 3', dataIndex: 'c', key: 'c', width: 200 }, + { title: 'Operation', dataIndex: '', key: 'x', render: this.renderAction }, + ] + + toggleButton() { + if (this.state.expandedRowKeys.length) { + const closeAll = () => this.setState({ expandedRowKeys: [] }); + return ; + } + const openAll = () => this.setState({ expandedRowKeys: [0, 1, 2] }); + return ; + } + + remove(index) { + const data = this.state.data; + data.splice(index, 1); + this.setState({ data }); + } + + expandedRowRender(record) { + // console.log(record); + return

    extra: {record.a}

    ; + } + + renderAction(o, row, index) { + return this.remove(index)}>Delete; + } + + render() { + const { expandIconAsCell, expandRowByClick, expandedRowKeys, data } = this.state; + return ( +
    + {this.toggleButton()} + + + expandIconAsCell + + + expandRowByClick +
    + + ); + } +} + +ReactDOM.render( +
    +

    expandedRowRender

    + +
    , + document.getElementById('__react-content') +); diff --git a/components/vc-table/demo/fixedColumns-auto-height.js b/components/vc-table/demo/fixedColumns-auto-height.js new file mode 100644 index 000000000..5a2973333 --- /dev/null +++ b/components/vc-table/demo/fixedColumns-auto-height.js @@ -0,0 +1,46 @@ +/* eslint-disable no-console,func-names,react/no-multi-comp */ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Table from 'rc-table'; +import 'rc-table/assets/index.less'; + +const columns = [ + { title: 'title1', dataIndex: 'a', key: 'a', width: 100, fixed: 'left' }, + { title: 'title2', dataIndex: 'b', key: 'b', width: 100, fixed: 'left' }, + { title: 'title3', dataIndex: 'c', key: 'c' }, + { title: 'title4', dataIndex: 'b', key: 'd' }, + { title: 'title5', dataIndex: 'b', key: 'e' }, + { title: 'title6', dataIndex: 'b', key: 'f', + render: () =>
    我很高
    }, + { title: 'title7', dataIndex: 'b', key: 'g' }, + { title: 'title8', dataIndex: 'b', key: 'h' }, + { title: 'title9', dataIndex: 'b', key: 'i' }, + { title: 'title10', dataIndex: 'b', key: 'j' }, + { title: 'title11', dataIndex: 'b', key: 'k' }, + { title: 'title12', dataIndex: 'b', key: 'l', width: 100, fixed: 'right' }, +]; + +const data = [ + { a: '123', b: 'xxxxxxxx', d: 3, key: '1', title: 'hello' }, + { a: 'cdd', b: 'edd12221', d: 3, key: '2', title: 'hello' }, + { a: '133', c: 'edd12221', d: 2, key: '3', title: 'hello' }, + { a: '133', c: 'edd12221', d: 2, key: '4', title: 'hello' }, + { a: '133', c: 'edd12221', d: 2, key: '5', title: 'hello' }, + { a: '133', c: 'edd12221', d: 2, key: '6', title: 'hello' }, + { a: '133', c: 'edd12221', d: 2, key: '7', title: 'hello' }, + { a: '133', c: 'edd12221', d: 2, key: '8', title: 'hello' }, + { a: '133', c: 'edd12221', d: 2, key: '9', title: 'hello' }, +]; + +ReactDOM.render( +
    +

    Fixed columns

    +
    record.title} + expandIconAsCell + scroll={{ x: 1200 }} + data={data} + /> + +, document.getElementById('__react-content')); diff --git a/components/vc-table/demo/fixedColumns.js b/components/vc-table/demo/fixedColumns.js new file mode 100644 index 000000000..ed05451dc --- /dev/null +++ b/components/vc-table/demo/fixedColumns.js @@ -0,0 +1,45 @@ +/* eslint-disable no-console,func-names,react/no-multi-comp */ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Table from 'rc-table'; +import 'rc-table/assets/index.less'; + +const columns = [ + { title: 'title1', dataIndex: 'a', key: 'a', width: 100, fixed: 'left' }, + { title: 'title2', dataIndex: 'b', key: 'b', width: 100, fixed: 'left' }, + { title: 'title3', dataIndex: 'c', key: 'c' }, + { title: 'title4', dataIndex: 'b', key: 'd' }, + { title: 'title5', dataIndex: 'b', key: 'e' }, + { title: 'title6', dataIndex: 'b', key: 'f' }, + { title:
    title7


    Hello world!
    , dataIndex: 'b', key: 'g' }, + { title: 'title8', dataIndex: 'b', key: 'h' }, + { title: 'title9', dataIndex: 'b', key: 'i' }, + { title: 'title10', dataIndex: 'b', key: 'j' }, + { title: 'title11', dataIndex: 'b', key: 'k' }, + { title: 'title12', dataIndex: 'b', key: 'l', width: 100, fixed: 'right' }, +]; + +const data = [ + { a: '123', b: 'xxxxxxxx', d: 3, key: '1' }, + { a: 'cdd', b: 'edd12221', d: 3, key: '2' }, + { a: '133', c: 'edd12221', d: 2, key: '3' }, + { a: '133', c: 'edd12221', d: 2, key: '4' }, + { a: '133', c: 'edd12221', d: 2, key: '5' }, + { a: '133', c: 'edd12221', d: 2, key: '6' }, + { a: '133', c: 'edd12221', d: 2, key: '7' }, + { a: '133', c: 'edd12221', d: 2, key: '8' }, + { a: '133', c: 'edd12221', d: 2, key: '9' }, +]; + +ReactDOM.render( +
    +

    Fixed columns

    +
    record.title} + expandIconAsCell + scroll={{ x: 1200 }} + data={data} + /> + +, document.getElementById('__react-content')); diff --git a/components/vc-table/demo/fixedColumnsAndHeader.js b/components/vc-table/demo/fixedColumnsAndHeader.js new file mode 100644 index 000000000..e62776a59 --- /dev/null +++ b/components/vc-table/demo/fixedColumnsAndHeader.js @@ -0,0 +1,39 @@ +/* eslint-disable no-console,func-names,react/no-multi-comp */ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Table from 'rc-table'; +import 'rc-table/assets/index.less'; + +const columns = [ + { title: 'title1', dataIndex: 'a', key: 'a', width: 100, fixed: 'left' }, + { title: 'title2', dataIndex: 'b', key: 'b', width: 100, fixed: 'left' }, + { title: 'title3', dataIndex: 'c', key: 'c', width: 150 }, + { title: 'title4', dataIndex: 'c', key: 'd', width: 150 }, + { title: 'title5', dataIndex: 'c', key: 'e', width: 150 }, + { title: 'title6', dataIndex: 'c', key: 'f', width: 150 }, + { title: 'title7', dataIndex: 'c', key: 'g', width: 150 }, + { title: 'title8', dataIndex: 'c', key: 'h', width: 150 }, + { title: 'title9', dataIndex: 'b', key: 'i', width: 150 }, + { title: 'title10', dataIndex: 'b', key: 'j', width: 150 }, + { title: 'title11', dataIndex: 'b', key: 'k', width: 150 }, + { title: 'title12', dataIndex: 'b', key: 'l', width: 100, fixed: 'right' }, +]; + +const data = [ + { a: 'aaa', b: 'bbb', c: '内容内容内容内容内容', d: 3, key: '1' }, + { a: 'aaa', b: 'bbb', c: '内容内容内容内容内容', d: 3, key: '2' }, + { a: 'aaa', c: '内容内容内容内容内容', d: 2, key: '3' }, + { a: 'aaa', c: '内容内容内容内容内容', d: 2, key: '4' }, + { a: 'aaa', c: '内容内容内容内容内容', d: 2, key: '5' }, + { a: 'aaa', c: '内容内容内容内容内容', d: 2, key: '6' }, + { a: 'aaa', c: '内容内容内容内容内容', d: 2, key: '7' }, + { a: 'aaa', c: '内容内容内容内容内容', d: 2, key: '8' }, + { a: 'aaa', c: '内容内容内容内容内容', d: 2, key: '9' }, +]; + +ReactDOM.render( +
    +

    Fixed columns and header

    +
    + +, document.getElementById('__react-content')); diff --git a/components/vc-table/demo/fixedColumnsAndHeaderSyncRowHeight.js b/components/vc-table/demo/fixedColumnsAndHeaderSyncRowHeight.js new file mode 100644 index 000000000..932bdfa0f --- /dev/null +++ b/components/vc-table/demo/fixedColumnsAndHeaderSyncRowHeight.js @@ -0,0 +1,39 @@ +/* eslint-disable no-console,func-names,react/no-multi-comp */ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Table from 'rc-table'; +import 'rc-table/assets/index.less'; + +const columns = [ + { title: 'title1', dataIndex: 'a', key: 'a', width: 100, fixed: 'left' }, + { title: 'title2', dataIndex: 'b', key: 'b', width: 100, fixed: 'left' }, + { title: 'titletitle3', dataIndex: 'c', key: 'c' }, + { title: 'title4', dataIndex: 'c', key: 'd', width: 150 }, + { title: 'title5', dataIndex: 'c', key: 'e', width: 150 }, + { title: 'title6', dataIndex: 'c', key: 'f', width: 150 }, + { title: 'title7', dataIndex: 'c', key: 'g', width: 150 }, + { title: 'title8', dataIndex: 'c', key: 'h', width: 150 }, + { title: 'title9', dataIndex: 'b', key: 'i', width: 150 }, + { title: 'title10', dataIndex: 'b', key: 'j', width: 150 }, + { title: 'title11', dataIndex: 'b', key: 'k', width: 150 }, + { title: 'title12', dataIndex: 'b', key: 'l', width: 100, fixed: 'right' }, +]; + +const data = [ + { a: 'aaa', b: 'bbb', c: '内容内容内容内容内容', d: 3, key: '1' }, + { a: 'aaa', b: 'bbb', c: '内容内容内容内容内容', d: 3, key: '2' }, + { a: 'aaa', c: '内容内容内容内容内容', d: 2, key: '3' }, + { a: 'aaa', c: '内容内容内容内容内容', d: 2, key: '4' }, + { a: 'aaa', c: '内容内容内容内容内容', d: 2, key: '5' }, + { a: 'aaa', c: '内容内容内容内容内容', d: 2, key: '6' }, + { a: 'aaa', c: '内容内容内容内容内容', d: 2, key: '7' }, + { a: 'aaa', c: '内容内容内容内容内容', d: 2, key: '8' }, + { a: 'aaa', c: '内容内容内容内容内容', d: 2, key: '9' }, +]; + +ReactDOM.render( +
    +

    Fixed columns and header, resize window for test

    +
    + +, document.getElementById('__react-content')); diff --git a/components/vc-table/demo/fixedColumnsWhenResize.js b/components/vc-table/demo/fixedColumnsWhenResize.js new file mode 100644 index 000000000..a59b2c2c0 --- /dev/null +++ b/components/vc-table/demo/fixedColumnsWhenResize.js @@ -0,0 +1,45 @@ +/* eslint-disable no-console,func-names,react/no-multi-comp */ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Table from 'rc-table'; +import 'rc-table/assets/index.less'; + +const columns = [ + { title: 'title1', dataIndex: 'a', key: 'a', width: 100, fixed: 'left' }, + { title: 'title2', dataIndex: 'b', key: 'b', width: 100, fixed: 'left' }, + { title: 'title3', dataIndex: 'c', key: 'c' }, + { title: 'title4', dataIndex: 'b', key: 'd' }, + { title: 'title5', dataIndex: 'b', key: 'e' }, + { title: 'title6', dataIndex: 'b', key: 'f' }, + { title:
    title7


    Hello world!
    , dataIndex: 'b', key: 'g' }, + { title: 'title8', dataIndex: 'b', key: 'h' }, + { title: 'title9', dataIndex: 'b', key: 'i' }, + { title: 'title10', dataIndex: 'b', key: 'j' }, + { title: 'title11', dataIndex: 'b', key: 'k' }, + { title: 'title12', dataIndex: 'b', key: 'l', width: 100, fixed: 'right' }, +]; + +const data = [ + { a: '123', b: 'xxxxxxxx', d: 3, key: '1' }, + { a: 'cdd', b: 'edd12221', d: 3, key: '2' }, + { a: '133', c: 'edd12221', d: 2, key: '3' }, + { a: '133', c: 'edd12221', d: 2, key: '4' }, + { a: '133', c: 'edd12221', d: 2, key: '5' }, + { a: '133', c: 'edd12221', d: 2, key: '6' }, + { a: '133', c: 'edd12221', d: 2, key: '7' }, + { a: '133', c: 'edd12221', d: 2, key: '8' }, + { a: '133', c: 'edd12221', d: 2, key: '9' }, +]; + +ReactDOM.render( +
    +

    See fixed columns when you resize window

    +
    record.title} + expandIconAsCell + scroll={{ x: 800 }} + data={data} + /> + +, document.getElementById('__react-content')); diff --git a/components/vc-table/demo/grouping-columns.js b/components/vc-table/demo/grouping-columns.js new file mode 100644 index 000000000..699a5ce0f --- /dev/null +++ b/components/vc-table/demo/grouping-columns.js @@ -0,0 +1,100 @@ +/* eslint-disable no-console,func-names,react/no-multi-comp */ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Table from 'rc-table'; +import 'rc-table/assets/index.less'; +import 'rc-table/assets/bordered.less'; + +const columns = [ + { + title: '姓名', + dataIndex: 'name', + key: 'name', + }, + { + title: '其它', + children: [ + { + title: '年龄', + dataIndex: 'age', + key: 'age', + }, + { + title: '住址', + children: [ + { + title: '街道', + dataIndex: 'street', + key: 'street', + }, + { + title: '小区', + children: [ + { + title: '单元', + dataIndex: 'building', + key: 'building', + }, + { + title: '门牌', + dataIndex: 'number', + key: 'number', + }, + ], + }, + ], + }, + ], + }, + { + title: '公司', + children: [ + { + title: '地址', + dataIndex: 'companyAddress', + key: 'companyAddress', + }, + { + title: '名称', + dataIndex: 'companyName', + key: 'companyName', + }, + ], + }, + { + title: '性别', + dataIndex: 'gender', + key: 'gender', + }, +]; + + +const data = [{ + key: '1', + name: '胡彦斌', + age: 32, + street: '拱墅区和睦街道', + building: 1, + number: 2033, + companyAddress: '西湖区湖底公园', + companyName: '湖底有限公司', + gender: '男', +}, { + key: '2', + name: '胡彦祖', + age: 42, + street: '拱墅区和睦街道', + building: 3, + number: 2035, + companyAddress: '西湖区湖底公园', + companyName: '湖底有限公司', + gender: '男', +}]; + +ReactDOM.render( +
    +

    grouping columns

    +
    + , + document.getElementById('__react-content') +); diff --git a/components/vc-table/demo/hide-header.js b/components/vc-table/demo/hide-header.js new file mode 100644 index 000000000..ca1db1c6a --- /dev/null +++ b/components/vc-table/demo/hide-header.js @@ -0,0 +1,34 @@ +/* eslint-disable no-console,func-names,react/no-multi-comp */ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Table from 'rc-table'; +import 'rc-table/assets/index.less'; + +const columns = [ + { title: 'title1', dataIndex: 'a', key: 'a', width: 100 }, + { id: '123', title: 'title2', dataIndex: 'b', key: 'b', width: 100 }, + { title: 'title3', dataIndex: 'c', key: 'c', width: 200 }, + { + title: 'Operations', dataIndex: '', key: 'd', render() { + return Operations; + }, + }, +]; + +const data = [ + { a: '123', key: '1' }, + { a: 'cdd', b: 'edd', key: '2' }, + { a: '1333', c: 'eee', d: 2, key: '3' }, +]; + +ReactDOM.render( +
    +

    hide table head

    +
    + , + document.getElementById('__react-content') +); diff --git a/components/vc-table/demo/jsx.js b/components/vc-table/demo/jsx.js new file mode 100644 index 000000000..d39118b4a --- /dev/null +++ b/components/vc-table/demo/jsx.js @@ -0,0 +1,49 @@ +/* eslint-disable no-console,func-names,react/no-multi-comp */ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Table from 'rc-table'; +import 'rc-table/assets/index.less'; + +const { ColumnGroup, Column } = Table; + +const data = [ + { a: '123', key: '1' }, + { a: 'cdd', b: 'edd', key: '2' }, + { a: '1333', c: 'eee', d: 2, key: '3' }, +]; + +ReactDOM.render( +
    +

    JSX table

    +
    + + + + + + Operations} + /> +
    +
    , + document.getElementById('__react-content') +); diff --git a/components/vc-table/demo/key.js b/components/vc-table/demo/key.js new file mode 100644 index 000000000..d468ba201 --- /dev/null +++ b/components/vc-table/demo/key.js @@ -0,0 +1,70 @@ +/* eslint-disable no-console,func-names,react/no-multi-comp */ +import React from 'react'; +import ReactDOM from 'react-dom'; +import PropTypes from 'prop-types'; +import Table from 'rc-table'; +import 'rc-table/assets/index.less'; + +const CheckBox = ({ id }) => ( + +); + +class Demo extends React.Component { + static propTypes = { + data: PropTypes.array.isRequired, + } + + constructor(props) { + super(props); + + this.state = { + data: props.data, + }; + } + + remove(index) { + const rows = this.state.data; + rows.splice(index, 1); + this.setState({ + data: rows, + }); + } + + handleClick = (index) => () => { + this.remove(index); + } + + checkbox(a) { + return ; + } + + renderAction = (o, row, index) => { + return Delete; + } + + render() { + const state = this.state; + const columns = [ + { title: 'title1', dataIndex: 'a', key: 'a', width: 100, render: this.checkbox }, + { title: 'title2', dataIndex: 'b', key: 'b', width: 100 }, + { title: 'title3', dataIndex: 'c', key: 'c', width: 200 }, + { title: 'Operations', dataIndex: '', key: 'x', render: this.renderAction }, + ]; + return ( + record.a} /> + ); + } +} + +const data = [{ a: '123' }, { a: 'cdd', b: 'edd' }, { a: '1333', c: 'eee', d: 2 }]; + +ReactDOM.render( +
    +

    specify key

    + +
    , + document.getElementById('__react-content') +); diff --git a/components/vc-table/demo/nested.js b/components/vc-table/demo/nested.js new file mode 100644 index 000000000..860ebb0b5 --- /dev/null +++ b/components/vc-table/demo/nested.js @@ -0,0 +1,45 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Table from 'rc-table'; +import 'rc-table/assets/index.less'; + +const columns = [ + { title: 'First Name', dataIndex: 'names.first', key: 'a', width: 100 }, + { title: 'Last Name', dataIndex: 'names.last', key: 'b', width: 100 }, + { title: 'Age', dataIndex: 'age', key: 'c', width: 100 }, +]; + +const data = [{ + age: '23', + names: { + first: 'John', + last: 'Doe', + }, + key: '1', +}, { + age: '36', + names: { + first: 'Terry', + last: 'Garner', + }, + key: '2', +}, { + age: '52', + names: { + first: 'Thomas', + last: 'Goodwin', + }, + key: '3', +}]; + +ReactDOM.render( +
    +

    Nested data table

    +
    + , + document.getElementById('__react-content') +); diff --git a/components/vc-table/demo/no-data.js b/components/vc-table/demo/no-data.js new file mode 100644 index 000000000..670384753 --- /dev/null +++ b/components/vc-table/demo/no-data.js @@ -0,0 +1,26 @@ +/* eslint-disable no-console,func-names,react/no-multi-comp */ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Table from 'rc-table'; +import 'rc-table/assets/index.less'; + +const columns = [ + { title: 'title1', dataIndex: 'a', key: 'a', width: 100 }, + { id: '123', title: 'title2', dataIndex: 'b', key: 'b', width: 100 }, + { title: 'title3', dataIndex: 'c', key: 'c', width: 200 }, + { + title: 'Operations', dataIndex: '', key: 'd', render() { + return Operations; + }, + }, +]; + +const data = []; + +ReactDOM.render( +
    +

    simple table

    +
    + , + document.getElementById('__react-content') +); diff --git a/components/vc-table/demo/react-dnd.js b/components/vc-table/demo/react-dnd.js new file mode 100644 index 000000000..886b6f0b0 --- /dev/null +++ b/components/vc-table/demo/react-dnd.js @@ -0,0 +1,187 @@ +/* eslint-disable no-unused-expressions,new-cap */ +import React from 'react'; +import ReactDOM from 'react-dom'; +import { injectGlobal } from 'styled-components'; +import update from 'immutability-helper'; +import { DragDropContext, DragSource, DropTarget } from 'react-dnd'; +import HTML5Backend from 'react-dnd-html5-backend'; +import Table from 'rc-table'; +import 'rc-table/assets/index.less'; + +injectGlobal` + tr.drop-over-downward td { + border-bottom: 2px dashed red; + } + + tr.drop-over-upward td { + border-top: 2px dashed red; + } +`; + +function dragDirection( + dragIndex, + hoverIndex, + initialClientOffset, + clientOffset, + sourceClientOffset, +) { + const hoverMiddleY = (initialClientOffset.y - sourceClientOffset.y) / 2; + const hoverClientY = clientOffset.y - sourceClientOffset.y; + if (dragIndex < hoverIndex && hoverClientY > hoverMiddleY) { + return 'downward'; + } + if (dragIndex > hoverIndex && hoverClientY < hoverMiddleY) { + return 'upward'; + } +} + +let BodyRow = (props) => { + const { + isOver, + connectDragSource, + connectDropTarget, + moveRow, + dragRow, + clientOffset, + sourceClientOffset, + initialClientOffset, + ...restProps, + } = props; + const style = { cursor: 'move' }; + + let className = restProps.className; + if (isOver && initialClientOffset) { + const direction = dragDirection( + dragRow.index, + restProps.index, + initialClientOffset, + clientOffset, + sourceClientOffset + ); + if (direction === 'downward') { + className += ' drop-over-downward'; + } + if (direction === 'upward') { + className += ' drop-over-upward'; + } + } + + return connectDragSource( + connectDropTarget( + + ) + ); +}; + +const rowSource = { + beginDrag(props) { + return { + index: props.index, + }; + }, +}; + +const rowTarget = { + drop(props, monitor) { + const dragIndex = monitor.getItem().index; + const hoverIndex = props.index; + + // Don't replace items with themselves + if (dragIndex === hoverIndex) { + return; + } + + // Time to actually perform the action + props.moveRow(dragIndex, hoverIndex); + + // Note: we're mutating the monitor item here! + // Generally it's better to avoid mutations, + // but it's good here for the sake of performance + // to avoid expensive index searches. + monitor.getItem().index = hoverIndex; + }, +}; + +BodyRow = DropTarget('row', rowTarget, (connect, monitor) => ({ + connectDropTarget: connect.dropTarget(), + isOver: monitor.isOver(), + sourceClientOffset: monitor.getSourceClientOffset(), +}))( + DragSource('row', rowSource, (connect, monitor) => ({ + connectDragSource: connect.dragSource(), + dragRow: monitor.getItem(), + clientOffset: monitor.getClientOffset(), + initialClientOffset: monitor.getInitialClientOffset(), + }))(BodyRow) +); + +const columns = [ + { title: 'title1', dataIndex: 'a', key: 'a', width: 100 }, + { id: '123', title: 'title2', dataIndex: 'b', key: 'b', width: 100 }, + { title: 'title3', dataIndex: 'c', key: 'c', width: 200 }, + { + title: 'Operations', + dataIndex: '', + key: 'd', + render() { + return Operations; + }, + }, +]; + +class Demo extends React.Component { + state = { + data: [ + { a: '123', key: '1' }, + { a: 'cdd', b: 'edd', key: '2' }, + { a: '1333', c: 'eee', d: 2, key: '3' }, + ], + } + + components = { + body: { + row: BodyRow, + }, + } + + moveRow = (dragIndex, hoverIndex) => { + const { data } = this.state; + const dragRow = data[dragIndex]; + + this.setState( + update(this.state, { + data: { + $splice: [[dragIndex, 1], [hoverIndex, 0, dragRow]], + }, + }), + ); + } + + render() { + return ( +
    ({ + index, + moveRow: this.moveRow, + })} + /> + ); + } +} + +Demo = DragDropContext(HTML5Backend)(Demo); + +ReactDOM.render( +
    +

    Integrate with react-dnd

    + +
    , + document.getElementById('__react-content') +); diff --git a/components/vc-table/demo/rowAndCellClick.js b/components/vc-table/demo/rowAndCellClick.js new file mode 100644 index 000000000..e8dd239bc --- /dev/null +++ b/components/vc-table/demo/rowAndCellClick.js @@ -0,0 +1,105 @@ +/* eslint-disable no-console,func-names,react/no-multi-comp */ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Table from 'rc-table'; +import 'rc-table/assets/index.less'; + +const onRowClick = (record, index, event) => { + console.log(`Click nth(${index}) row of parent, record.name: ${record.name}`); + // See https://facebook.github.io/react/docs/events.html for original click event details. + if (event.shiftKey) { + console.log('Shift + mouse click triggered.'); + } +}; + +const onRowDoubleClick = (record, index) => { + console.log(`Double click nth(${index}) row of parent, record.name: ${record.name}`); +}; + +const columns = [{ + title: 'Name', + dataIndex: 'name', + key: 'name', + width: 400, +}, { + title: 'Age', + dataIndex: 'age', + key: 'age', + width: 100, + render: (text) => ( + {text} (Trigger Cell Click) + ), + onCell: (record) => ({ + onClick(e) { + console.log('Click cell', record, e.target); + }, + }), +}, { + title: 'Address', + dataIndex: 'address', + key: 'address', + width: 200, +}]; + +const data = [{ + key: 1, + name: 'a', + age: 32, + address: 'I am a', + children: [{ + key: 11, + name: 'aa', + age: 33, + address: 'I am aa', + }, { + key: 12, + name: 'ab', + age: 33, + address: 'I am ab', + children: [{ + key: 121, + name: 'aba', + age: 33, + address: 'I am aba', + }], + }, { + key: 13, + name: 'ac', + age: 33, + address: 'I am ac', + children: [{ + key: 131, + name: 'aca', + age: 33, + address: 'I am aca', + children: [{ + key: 1311, + name: 'acaa', + age: 33, + address: 'I am acaa', + }, { + key: 1312, + name: 'acab', + age: 33, + address: 'I am acab', + }], + }], + }], +}, { + key: 2, + name: 'b', + age: 32, + address: 'I am b', +}]; + +ReactDOM.render( +
    ({ + onClick: onRowClick.bind(null, record, index), + onDoubleClick: onRowDoubleClick.bind(null, record, index), + })} + />, + document.getElementById('__react-content') +); diff --git a/components/vc-table/demo/scrollX.js b/components/vc-table/demo/scrollX.js new file mode 100644 index 000000000..4659bcea2 --- /dev/null +++ b/components/vc-table/demo/scrollX.js @@ -0,0 +1,34 @@ +/* eslint-disable no-console,func-names,react/no-multi-comp */ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Table from 'rc-table'; +import 'rc-table/assets/index.less'; + +const columns = [ + { title: 'title1', dataIndex: 'a', key: 'a', width: 100 }, + { title: 'title2', dataIndex: 'b', key: 'b', width: 100 }, + { title: 'title3', dataIndex: 'c', key: 'c', width: 100 }, + { title: 'title4', dataIndex: 'b', key: 'd', width: 100 }, + { title: 'title5', dataIndex: 'b', key: 'e', width: 100 }, + { title: 'title6', dataIndex: 'b', key: 'f', width: 100 }, + { title: 'title7', dataIndex: 'b', key: 'g', width: 100 }, + { title: 'title8', dataIndex: 'b', key: 'h', width: 100 }, + { title: 'title9', dataIndex: 'b', key: 'i', width: 100 }, + { title: 'title10', dataIndex: 'b', key: 'j', width: 100 }, + { title: 'title11', dataIndex: 'b', key: 'k', width: 100 }, + { title: 'title12', dataIndex: 'b', key: 'l', width: 100 }, +]; + +const data = [ + { a: '123', b: 'xxxxxxxx xxxxxxxx', d: 3, key: '1' }, + { a: 'cdd', b: 'edd12221 edd12221', d: 3, key: '2' }, + { a: '133', c: 'edd12221 edd12221', d: 2, key: '3' }, + { a: '133', c: 'edd12221 edd12221', d: 2, key: '4' }, +]; + +ReactDOM.render( +
    +

    Scroll X

    +
    + +, document.getElementById('__react-content')); diff --git a/components/vc-table/demo/scrollXY.js b/components/vc-table/demo/scrollXY.js new file mode 100644 index 000000000..6da56a772 --- /dev/null +++ b/components/vc-table/demo/scrollXY.js @@ -0,0 +1,45 @@ +/* eslint-disable no-console,func-names,react/no-multi-comp */ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Table from 'rc-table'; +import 'rc-table/assets/index.less'; + +const columns = [ + { title: 'title1', dataIndex: 'a', key: 'a', width: 100 }, + { title: 'title2', dataIndex: 'b', key: 'b', width: 100 }, + { title: 'title3', dataIndex: 'c', key: 'c', width: 100 }, + { title: 'title4', dataIndex: 'b', key: 'd', width: 100 }, + { title: 'title5', dataIndex: 'b', key: 'e', width: 100 }, + { title: 'title6', dataIndex: 'b', key: 'f', width: 100 }, + { title: 'title7', dataIndex: 'b', key: 'g', width: 100 }, + { title: 'title8', dataIndex: 'b', key: 'h', width: 100 }, + { title: 'title9', dataIndex: 'b', key: 'i', width: 100 }, + { title: 'title10', dataIndex: 'b', key: 'j', width: 100 }, + { title: 'title11', dataIndex: 'b', key: 'k', width: 100 }, + { title: 'title12', dataIndex: 'b', key: 'l', width: 100 }, +]; + +const data = [ + { a: '123', b: 'xxxxxxxx xxxxxxxx', d: 3, key: '1' }, + { a: 'cdd', b: 'edd12221 edd12221', d: 3, key: '2' }, + { a: '133', c: 'edd12221 edd12221', d: 2, key: '3' }, + { a: '133', c: 'edd12221 edd12221', d: 2, key: '4' }, + { a: '133', c: 'edd12221 edd12221', d: 2, key: '5' }, + { a: '133', c: 'edd12221 edd12221', d: 2, key: '6' }, + { a: '133', c: 'edd12221 edd12221', d: 2, key: '7' }, + { a: '133', c: 'edd12221 edd12221', d: 2, key: '8' }, + { a: '133', c: 'edd12221 edd12221', d: 2, key: '9' }, +]; + +ReactDOM.render( +
    +

    Scroll X/Y

    +
    + +, document.getElementById('__react-content')); diff --git a/components/vc-table/demo/scrollY.js b/components/vc-table/demo/scrollY.js new file mode 100644 index 000000000..ae286c5ed --- /dev/null +++ b/components/vc-table/demo/scrollY.js @@ -0,0 +1,62 @@ +/* eslint-disable no-console,func-names,react/no-multi-comp */ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Table from 'rc-table'; +import 'rc-table/assets/index.less'; + +const data = []; +for (let i = 0; i < 10; i++) { + data.push({ + key: i, + a: `a${i}`, + b: `b${i}`, + c: `c${i}`, + }); +} + +class Demo extends React.Component { + state = { + showBody: true, + } + + toggleBody = () => { + this.setState({ + showBody: !this.state.showBody, + }); + } + + render() { + const columns = [ + { title: 'title1', key: 'a', dataIndex: 'a', width: 100 }, + { id: '123', title: 'title2', dataIndex: 'b', key: 'b', width: 100 }, + { title: 'title3', key: 'c', dataIndex: 'c', width: 200 }, + { + title: {this.state.showBody ? '隐藏' : '显示'}体, + key: 'x', + width: 200, + render() { + return Operations; + }, + }, + ]; + return ( +
    record.key} + bodyStyle={{ + display: this.state.showBody ? '' : 'none', + }} + /> + ); + } +} + +ReactDOM.render( +
    +

    scroll body table

    + +
    , + document.getElementById('__react-content') +); diff --git a/components/vc-table/demo/simple.js b/components/vc-table/demo/simple.js new file mode 100644 index 000000000..c07339aaf --- /dev/null +++ b/components/vc-table/demo/simple.js @@ -0,0 +1,30 @@ +/* eslint-disable no-console,func-names,react/no-multi-comp */ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Table from 'rc-table'; +import 'rc-table/assets/index.less'; + +const columns = [ + { title: 'title1', dataIndex: 'a', key: 'a', width: 100 }, + { id: '123', title: 'title2', dataIndex: 'b', key: 'b', width: 100 }, + { title: 'title3', dataIndex: 'c', key: 'c', width: 200 }, + { + title: 'Operations', dataIndex: '', key: 'd', render() { + return Operations; + }, + }, +]; + +const data = [ + { a: '123', key: '1' }, + { a: 'cdd', b: 'edd', key: '2' }, + { a: '1333', c: 'eee', d: 2, key: '3' }, +]; + +ReactDOM.render( +
    +

    simple table

    +
    + , + document.getElementById('__react-content') +); diff --git a/components/vc-table/demo/styled-components.js b/components/vc-table/demo/styled-components.js new file mode 100644 index 000000000..fa453e009 --- /dev/null +++ b/components/vc-table/demo/styled-components.js @@ -0,0 +1,45 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import styled from 'styled-components'; +import Table from 'rc-table'; +import 'rc-table/assets/index.less'; + +const columns = [ + { title: 'title1', dataIndex: 'a', key: 'a', width: 100 }, + { id: '123', title: 'title2', dataIndex: 'b', key: 'b', width: 100 }, + { title: 'title3', dataIndex: 'c', key: 'c', width: 200 }, + { + title: 'Operations', + dataIndex: '', + key: 'd', + render() { + return Operations; + }, + }, +]; + +const data = [ + { a: '123', key: '1' }, + { a: 'cdd', b: 'edd', key: '2' }, + { a: '1333', c: 'eee', d: 2, key: '3' }, +]; + +const BodyRow = styled.tr` + &:hover { + background: palevioletred !important; + } +`; + +const components = { + body: { + row: BodyRow, + }, +}; + +ReactDOM.render( +
    +

    Integrate with styled-components

    +
    + , + document.getElementById('__react-content') +); diff --git a/components/vc-table/demo/subTable.js b/components/vc-table/demo/subTable.js new file mode 100644 index 000000000..459428e36 --- /dev/null +++ b/components/vc-table/demo/subTable.js @@ -0,0 +1,63 @@ +/* eslint-disable no-console,func-names,react/no-multi-comp */ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Table from 'rc-table'; +import 'rc-table/assets/index.less'; + +const data = [ + { + a: 'a1', + }, + { + a: 'a2', + b: 'b2', + children: [ + { + a: 'a2-1', + b: 'b2-1', + }, + { + a: 'a2-2', + b: 'b2-2', + }, + ], + }, + { + a: 'a3', + c: 'c3', + d: 'd3', + }, +]; + +class Demo extends React.Component { + handleClick = (record, e) => { + e.preventDefault(); + console.log(record.a); + } + + render() { + const columns = [ + { title: 'title1', dataIndex: 'a', key: 'a', width: 100 }, + { title: 'title2', dataIndex: 'b', key: 'b', width: 100 }, + { title: 'title3', dataIndex: 'c', key: 'c', width: 200 }, + { + title: 'Operations', dataIndex: '', key: 'x', render: (text, record) => { + return this.handleClick(record, e)}>click {record.a}; + }, + }, + ]; + return ( +
    +

    sub table

    +
    record.a} + /> + + ); + } +} + +ReactDOM.render(, document.getElementById('__react-content')); diff --git a/components/vc-table/demo/title-and-footer.js b/components/vc-table/demo/title-and-footer.js new file mode 100644 index 000000000..92fb0fc8c --- /dev/null +++ b/components/vc-table/demo/title-and-footer.js @@ -0,0 +1,35 @@ +/* eslint-disable no-console,func-names,react/no-multi-comp */ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Table from 'rc-table'; +import 'rc-table/assets/index.less'; + +const columns = [ + { title: 'title1', dataIndex: 'a', key: 'a', width: 100 }, + { id: '123', title: 'title2', dataIndex: 'b', key: 'b', width: 100 }, + { title: 'title3', dataIndex: 'c', key: 'c', width: 200 }, + { + title: 'Operations', dataIndex: '', key: 'd', render() { + return Operations; + }, + }, +]; + +const data = [ + { a: '123', key: '1' }, + { a: 'cdd', b: 'edd', key: '2' }, + { a: '1333', c: 'eee', d: 2, key: '3' }, +]; + +ReactDOM.render( +
    +

    title and footer

    +
    Title: {currentData.length} items
    } + footer={currentData =>
    Footer: {currentData.length} items
    } + /> + , + document.getElementById('__react-content') +); diff --git a/components/vc-table/index.js b/components/vc-table/index.js new file mode 100644 index 000000000..c4a842cdc --- /dev/null +++ b/components/vc-table/index.js @@ -0,0 +1,9 @@ +import Table from './src/Table' +import Column from './src/Column' +import ColumnGroup from './src/ColumnGroup' + +Table.Column = Column +Table.ColumnGroup = ColumnGroup + +export default Table +export { Column, ColumnGroup } diff --git a/components/vc-table/src/BaseTable.jsx b/components/vc-table/src/BaseTable.jsx new file mode 100644 index 000000000..c2d0811eb --- /dev/null +++ b/components/vc-table/src/BaseTable.jsx @@ -0,0 +1,189 @@ + +import PropTypes from '../../_util/vue-types' +import ColGroup from './ColGroup' +import TableHeader from './TableHeader' +import TableRow from './TableRow' +import ExpandableRow from './ExpandableRow' +import { mergeProps } from '../../_util/props-util' + +const BaseTable = { + name: 'BaseTable', + props: { + fixed: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.bool, + ]), + columns: PropTypes.array.isRequired, + tableClassName: PropTypes.string.isRequired, + hasHead: PropTypes.bool.isRequired, + hasBody: PropTypes.bool.isRequired, + store: PropTypes.object.isRequired, + expander: PropTypes.object.isRequired, + getRowKey: PropTypes.func, + isAnyColumnsFixed: PropTypes.bool, + }, + inject: { + table: { default: {}}, + }, + methods: { + handleRowHover (isHover, key) { + this.props.store.setState({ + currentHoverKey: isHover ? key : null, + }) + }, + + renderRows (renderData, indent, ancestorKeys = []) { + const { + columnManager, components, + prefixCls, + childrenColumnName, + rowClassName, + // rowRef, + $listeners: { + rowClick: onRowClick, + rowDoubleclick: onRowDoubleClick, + rowContextmenu: onRowContextMenu, + rowMouseenter: onRowMouseEnter, + rowMouseleave: onRowMouseLeave, + row: onRow, + }, + } = this.table + const { getRowKey, fixed, expander, isAnyColumnsFixed } = this + + const rows = [] + + for (let i = 0; i < renderData.length; i++) { + const record = renderData[i] + const key = getRowKey(record, i) + const className = typeof rowClassName === 'string' + ? rowClassName + : rowClassName(record, i, indent) + + const onHoverProps = {} + if (columnManager.isAnyColumnsFixed()) { + onHoverProps.hover = this.handleRowHover + } + + let leafColumns + if (fixed === 'left') { + leafColumns = columnManager.leftLeafColumns() + } else if (fixed === 'right') { + leafColumns = columnManager.rightLeafColumns() + } else { + leafColumns = columnManager.leafColumns() + } + + const rowPrefixCls = `${prefixCls}-row` + const expandableRowProps = { + props: { + ...expander.props, + fixed, + index: i, + prefixCls: rowPrefixCls, + record, + rowKey: key, + needIndentSpaced: expander.needIndentSpaced, + }, + key, + on: { + rowClick: onRowClick, + expandedChange: expander.handleExpandChange, + }, + scopedSlots: { + default: (expandableRow) => { + const tableRowProps = mergeProps({ + props: { + fixed, + indent, + record, + index: i, + prefixCls: rowPrefixCls, + childrenColumnName: childrenColumnName, + columns: leafColumns, + rowKey: key, + ancestorKeys, + components, + isAnyColumnsFixed, + }, + on: { + row: onRow, + rowDoubleclick: onRowDoubleClick, + rowContextmenu: onRowContextMenu, + rowMouseenter: onRowMouseEnter, + rowMouseleave: onRowMouseLeave, + ...onHoverProps, + }, + class: className, + ref: `row_${i}_${indent}`, + }, expandableRow) + return ( + + ) + }, + }, + } + const row = ( + + ) + + rows.push(row) + + expander.renderRows( + this.renderRows, + rows, + record, + i, + indent, + fixed, + key, + ancestorKeys + ) + } + return rows + }, + }, + + render () { + const { components, prefixCls, scroll, data, getBodyWrapper } = this.table + const { expander, tableClassName, hasHead, hasBody, fixed, columns } = this + const tableStyle = {} + + if (!fixed && scroll.x) { + // not set width, then use content fixed width + if (scroll.x === true) { + tableStyle.tableLayout = 'fixed' + } else { + tableStyle.width = scroll.x + } + } + + const Table = hasBody ? components.table : 'table' + const BodyWrapper = components.body.wrapper + + let body + if (hasBody) { + body = ( + + {this.renderRows(data, 0)} + + ) + if (getBodyWrapper) { + body = getBodyWrapper(body) + } + } + + return ( +
    + + {hasHead && } + {body} +
    + ) + }, +} + +export default BaseTable diff --git a/components/vc-table/src/BodyTable.jsx b/components/vc-table/src/BodyTable.jsx new file mode 100644 index 000000000..6cb8d69be --- /dev/null +++ b/components/vc-table/src/BodyTable.jsx @@ -0,0 +1,117 @@ +import PropTypes from '../../_util/vue-types' +import { measureScrollbar } from './utils' +import BaseTable from './BaseTable' + +export default { + name: 'BodyTable', + props: { + fixed: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.bool, + ]), + columns: PropTypes.array.isRequired, + tableClassName: PropTypes.string.isRequired, + handleBodyScroll: PropTypes.func.isRequired, + getRowKey: PropTypes.func.isRequired, + expander: PropTypes.object.isRequired, + isAnyColumnsFixed: PropTypes.bool, + }, + inject: { + table: { default: {}}, + }, + render () { + const { prefixCls, scroll } = this.table + const { + columns, + fixed, + tableClassName, + getRowKey, + handleBodyScroll, + expander, + isAnyColumnsFixed, + } = this + let { useFixedHeader } = this.table + const bodyStyle = { ...this.table.bodyStyle } + const innerBodyStyle = {} + + if (scroll.x || fixed) { + bodyStyle.overflowX = bodyStyle.overflowX || 'auto' + // Fix weired webkit render bug + // https://github.com/ant-design/ant-design/issues/7783 + bodyStyle.WebkitTransform = 'translate3d (0, 0, 0)' + } + + if (scroll.y) { + // maxHeight will make fixed-Table scrolling not working + // so we only set maxHeight to body-Table here + if (fixed) { + innerBodyStyle.maxHeight = bodyStyle.maxHeight || scroll.y + innerBodyStyle.overflowY = bodyStyle.overflowY || 'scroll' + } else { + bodyStyle.maxHeight = bodyStyle.maxHeight || scroll.y + } + bodyStyle.overflowY = bodyStyle.overflowY || 'scroll' + useFixedHeader = true + + // Add negative margin bottom for scroll bar overflow bug + const scrollbarWidth = measureScrollbar() + if (scrollbarWidth > 0 && fixed) { + bodyStyle.marginBottom = `-${scrollbarWidth}px` + bodyStyle.paddingBottom = '0px' + } + } + + const baseTable = ( + + ) + + if (fixed && columns.length) { + let refName + if (columns[0].fixed === 'left' || columns[0].fixed === true) { + refName = 'fixedColumnsBodyLeft' + } else if (columns[0].fixed === 'right') { + refName = 'fixedColumnsBodyRight' + } + delete bodyStyle.overflowX + delete bodyStyle.overflowY + return ( +
    +
    + {baseTable} +
    +
    + ) + } + return ( +
    + {baseTable} +
    + ) + }, + +} + diff --git a/components/vc-table/src/ColGroup.jsx b/components/vc-table/src/ColGroup.jsx new file mode 100644 index 000000000..0df1afe27 --- /dev/null +++ b/components/vc-table/src/ColGroup.jsx @@ -0,0 +1,53 @@ +import PropTypes from '../../_util/vue-types' + +export default { + name: 'ColGroup', + props: { + fixed: PropTypes.string, + }, + inject: { + table: { default: {}}, + }, + render () { + const { fixed, table } = this + const { prefixCls, expandIconAsCell } = table + + let cols = [] + + if (expandIconAsCell && fixed !== 'right') { + cols.push( +
    + {this.renderExpandIcon()} +
    - - ); - } -} -ReactDOM.render( - , - document.getElementById('__react-content') -); diff --git a/components/vc-table/demo/childrenIndent.js b/components/vc-table/demo/childrenIndent.js index 4b226a887..f436835d7 100644 --- a/components/vc-table/demo/childrenIndent.js +++ b/components/vc-table/demo/childrenIndent.js @@ -1,8 +1,6 @@ /* eslint-disable no-console,func-names,react/no-multi-comp */ -import React from 'react'; -import ReactDOM from 'react-dom'; -import Table from 'rc-table'; -import 'rc-table/assets/index.less'; +import Table from '../index' +import '../assets/index.less' const columns = [{ title: 'Name', @@ -24,7 +22,7 @@ const columns = [{ dataIndex: 'operation', key: 'x', width: 150, -}]; +}] const data = [{ key: 1, @@ -75,13 +73,21 @@ const data = [{ name: 'b', age: 32, address: 'I am b', -}]; +}] -function onExpand(expanded, record) { - console.log('onExpand', expanded, record); +function onExpand (expanded, record) { + console.log('onExpand', expanded, record) +} +export default { + render () { + return ( +
    + ) + }, } -ReactDOM.render( -
    , - document.getElementById('__react-content') -); diff --git a/components/vc-table/demo/className.js b/components/vc-table/demo/className.js deleted file mode 100644 index 77f631174..000000000 --- a/components/vc-table/demo/className.js +++ /dev/null @@ -1,45 +0,0 @@ -/* eslint-disable no-console,func-names,react/no-multi-comp */ -import React from 'react'; -import ReactDOM from 'react-dom'; -import Table from 'rc-table'; -import 'rc-table/assets/index.less'; - -const columns = [ - { title: 'title1', dataIndex: 'a', - className: 'a', - key: 'a', width: 100 }, - { id: '123', title: 'title2', dataIndex: 'b', - className: 'b', - key: 'b', width: 100 }, - { title: 'title3', dataIndex: 'c', - className: 'c', - key: 'c', width: 200 }, - { - title: 'Operations', dataIndex: '', - className: 'd', - key: 'd', render() { - return Operations; - }, - }, -]; - -const data = [ - { a: '123', key: '1' }, - { a: 'cdd', b: 'edd', key: '2' }, - { a: '1333', c: 'eee', d: 2, key: '3' }, -]; - -ReactDOM.render( -
    -

    rowClassName and className

    -
    `row-${i}`} - expandedRowRender={record =>

    extra: {record.a}

    } - expandedRowClassName={(record, i) => `ex-row-${i}`} - data={data} - className="table" - /> - , - document.getElementById('__react-content') -); diff --git a/components/vc-table/demo/colspan-rowspan.js b/components/vc-table/demo/colspan-rowspan.js deleted file mode 100644 index c0e9b9187..000000000 --- a/components/vc-table/demo/colspan-rowspan.js +++ /dev/null @@ -1,107 +0,0 @@ -/* eslint-disable no-console,func-names,react/no-multi-comp */ -import React from 'react'; -import ReactDOM from 'react-dom'; -import Table from 'rc-table'; -import 'rc-table/assets/index.less'; - -const columns = [ - { title: '手机号', dataIndex: 'a', colSpan: 2, width: 100, key: 'a', render(o, row, index) { - const obj = { - children: o, - props: {}, - }; - // 设置第一行为链接 - if (index === 0) { - obj.children = {o}; - } - // 第5行合并两列 - if (index === 4) { - obj.props.colSpan = 2; - } - - if (index === 5) { - obj.props.colSpan = 6; - } - return obj; - } }, - { title: '电话', dataIndex: 'b', colSpan: 0, width: 100, key: 'b', render(o, row, index) { - const obj = { - children: o, - props: {}, - }; - // 列合并掉的表格设置colSpan=0,不会去渲染 - if (index === 4 || index === 5) { - obj.props.colSpan = 0; - } - return obj; - } }, - { title: 'Name', dataIndex: 'c', width: 100, key: 'c', render(o, row, index) { - const obj = { - children: o, - props: {}, - }; - - if (index === 5) { - obj.props.colSpan = 0; - } - return obj; - } }, - { title: 'Address', dataIndex: 'd', width: 200, key: 'd', render(o, row, index) { - const obj = { - children: o, - props: {}, - }; - if (index === 0) { - obj.props.rowSpan = 2; - } - if (index === 1 || index === 5) { - obj.props.rowSpan = 0; - } - - return obj; - } }, - { title: 'Gender', dataIndex: 'e', width: 200, key: 'e', render(o, row, index) { - const obj = { - children: o, - props: {}, - }; - if (index === 5) { - obj.props.colSpan = 0; - } - return obj; - } }, - { - title: 'Operations', dataIndex: '', key: 'f', - render(o, row, index) { - if (index === 5) { - return { - props: { - colSpan: 0, - }, - }; - } - return Operations; - }, - }, -]; - -const data = [ - { a: '13812340987', b: '0571-12345678', c: '张三', d: '文一西路', e: 'Male', key: '1' }, - { a: '13812340986', b: '0571-98787658', c: '张夫人', d: '文一西路', e: 'Female', key: '2' }, - { a: '13812988888', b: '0571-099877', c: '李四', d: '文二西路', e: 'Male', key: '3' }, - { a: '1381200008888', b: '0571-099877', c: '王五', d: '文二西路', e: 'Male', key: '4' }, - { a: '0571-88888110', c: '李警官', d: '武林门', e: 'Male', key: '5' }, - { a: '资料统计完毕于xxxx年xxx月xxx日', key: '6' }, -]; - -ReactDOM.render( -
    -

    colSpan & rowSpan

    -
    - , - document.getElementById('__react-content') -); diff --git a/components/vc-table/demo/column-resize.js b/components/vc-table/demo/column-resize.js deleted file mode 100644 index 7a56e580d..000000000 --- a/components/vc-table/demo/column-resize.js +++ /dev/null @@ -1,84 +0,0 @@ -/* eslint-disable no-console,func-names,react/no-multi-comp */ -import React from 'react'; -import ReactDOM from 'react-dom'; -import PropTypes from 'prop-types'; -import Table from 'rc-table'; -import 'rc-table/assets/index.less'; -import { Resizable } from 'react-resizable'; -import 'react-resizable/css/styles.css'; - -const ResizeableTitle = (props) => { - const { onResize, width, ...restProps } = props; - - if (!width) { - return
    ; - } - - return ( - - - - ); -}; - -ResizeableTitle.propTypes = { - onResize: PropTypes.func.isRequired, - width: PropTypes.number, -}; - -class Demo extends React.Component { - state = { - columns: [ - { title: 'title1', dataIndex: 'a', key: 'a', width: 100 }, - { id: '123', title: 'title2', dataIndex: 'b', key: 'b', width: 100 }, - { title: 'title3', dataIndex: 'c', key: 'c', width: 200 }, - { - title: 'Operations', dataIndex: '', key: 'd', render() { - return Operations; - }, - }, - ], - } - - components = { - header: { - cell: ResizeableTitle, - }, - } - - data = [ - { a: '123', key: '1' }, - { a: 'cdd', b: 'edd', key: '2' }, - { a: '1333', c: 'eee', d: 2, key: '3' }, - ] - - handleResize = index => (e, { size }) => { - this.setState(({ columns }) => { - const nextColumns = [...columns]; - nextColumns[index] = { - ...nextColumns[index], - width: size.width, - }; - return { columns: nextColumns }; - }); - } - - render() { - const columns = this.state.columns.map((col, index) => ({ - ...col, - onHeaderCell: (column) => ({ - width: column.width, - onResize: this.handleResize(index), - }), - })); - - return ( -
    -

    Integrate with react-resizable

    - - - ); - } -} - -ReactDOM.render(, document.getElementById('__react-content')); diff --git a/components/vc-table/demo/dropdown.js b/components/vc-table/demo/dropdown.js deleted file mode 100644 index 38c2f5e2c..000000000 --- a/components/vc-table/demo/dropdown.js +++ /dev/null @@ -1,111 +0,0 @@ -/* eslint-disable no-console,func-names,react/no-multi-comp */ -import React from 'react'; -import ReactDOM from 'react-dom'; -import Table from 'rc-table'; -import Menu, { Item, Divider } from 'rc-menu'; -import DropDown from 'rc-dropdown'; -import 'rc-table/assets/index.less'; -import 'rc-dropdown/assets/index.css'; -import 'rc-menu/assets/index.css'; - -const data = []; -for (let i = 0; i < 10; i++) { - data.push({ - key: i, - a: `a${i}`, - b: `b${i}`, - c: `c${i}`, - }); -} - -class Demo extends React.Component { - state = { - visible: false, - } - - filters = [] - - handleVisibleChange = (visible) => { - this.setState({ visible }); - } - - handleSelect = (selected) => { - this.filters.push(selected); - } - - handleDeselect = (key) => { - const index = this.filters.indexOf(key); - if (index !== -1) { - this.filters.splice(index, 1); - } - } - - confirmFilter = () => { - console.log(this.filters.join(',')); - this.setState({ - visible: false, - }); - } - - render() { - const menu = ( - - one - two - three - - - - - - ); - - const columns = [ - { - title: ( -
    - title1 - - filter - -
    - ), key: 'a', dataIndex: 'a', width: 100, - }, - { title: 'title2', key: 'b', dataIndex: 'b', width: 100 }, - { title: 'title3', key: 'c', dataIndex: 'c', width: 200 }, - ]; - - return ( -
    record.key} - /> - ); - } -} - -ReactDOM.render( -
    -

    use dropdown

    - -
    , - document.getElementById('__react-content') -); diff --git a/components/vc-table/demo/expandedRowRender.js b/components/vc-table/demo/expandedRowRender.js deleted file mode 100644 index 6e04bcc12..000000000 --- a/components/vc-table/demo/expandedRowRender.js +++ /dev/null @@ -1,114 +0,0 @@ -/* eslint-disable no-console,func-names,react/no-multi-comp */ -import React from 'react'; -import ReactDOM from 'react-dom'; -import Table from 'rc-table'; -import 'rc-table/assets/index.less'; - -const tableData = [ - { key: 0, a: '123' }, - { key: 1, a: 'cdd', b: 'edd' }, - { key: 2, a: '1333', c: 'eee', d: 2 }, -]; - -class Demo extends React.Component { - state = { - data: tableData, - expandedRowKeys: [], - expandIconAsCell: true, - expandRowByClick: false, - } - - onExpand = (expanded, record) => { - console.log('onExpand', expanded, record); - } - - onExpandedRowsChange = (rows) => { - this.setState({ - expandedRowKeys: rows, - }); - } - - onExpandIconAsCellChange = (e) => { - this.setState({ - expandIconAsCell: e.target.checked, - }); - } - - onExpandRowByClickChange = (e) => { - this.setState({ - expandRowByClick: e.target.checked, - }); - } - - columns = [ - { title: 'title 1', dataIndex: 'a', key: 'a', width: 100 }, - { title: 'title 2', dataIndex: 'b', key: 'b', width: 100 }, - { title: 'title 3', dataIndex: 'c', key: 'c', width: 200 }, - { title: 'Operation', dataIndex: '', key: 'x', render: this.renderAction }, - ] - - toggleButton() { - if (this.state.expandedRowKeys.length) { - const closeAll = () => this.setState({ expandedRowKeys: [] }); - return ; - } - const openAll = () => this.setState({ expandedRowKeys: [0, 1, 2] }); - return ; - } - - remove(index) { - const data = this.state.data; - data.splice(index, 1); - this.setState({ data }); - } - - expandedRowRender(record) { - // console.log(record); - return

    extra: {record.a}

    ; - } - - renderAction(o, row, index) { - return this.remove(index)}>Delete; - } - - render() { - const { expandIconAsCell, expandRowByClick, expandedRowKeys, data } = this.state; - return ( -
    - {this.toggleButton()} - - - expandIconAsCell - - - expandRowByClick -
    - - ); - } -} - -ReactDOM.render( -
    -

    expandedRowRender

    - -
    , - document.getElementById('__react-content') -); diff --git a/components/vc-table/demo/fixedColumns-auto-height.js b/components/vc-table/demo/fixedColumns-auto-height.js deleted file mode 100644 index 5a2973333..000000000 --- a/components/vc-table/demo/fixedColumns-auto-height.js +++ /dev/null @@ -1,46 +0,0 @@ -/* eslint-disable no-console,func-names,react/no-multi-comp */ -import React from 'react'; -import ReactDOM from 'react-dom'; -import Table from 'rc-table'; -import 'rc-table/assets/index.less'; - -const columns = [ - { title: 'title1', dataIndex: 'a', key: 'a', width: 100, fixed: 'left' }, - { title: 'title2', dataIndex: 'b', key: 'b', width: 100, fixed: 'left' }, - { title: 'title3', dataIndex: 'c', key: 'c' }, - { title: 'title4', dataIndex: 'b', key: 'd' }, - { title: 'title5', dataIndex: 'b', key: 'e' }, - { title: 'title6', dataIndex: 'b', key: 'f', - render: () =>
    我很高
    }, - { title: 'title7', dataIndex: 'b', key: 'g' }, - { title: 'title8', dataIndex: 'b', key: 'h' }, - { title: 'title9', dataIndex: 'b', key: 'i' }, - { title: 'title10', dataIndex: 'b', key: 'j' }, - { title: 'title11', dataIndex: 'b', key: 'k' }, - { title: 'title12', dataIndex: 'b', key: 'l', width: 100, fixed: 'right' }, -]; - -const data = [ - { a: '123', b: 'xxxxxxxx', d: 3, key: '1', title: 'hello' }, - { a: 'cdd', b: 'edd12221', d: 3, key: '2', title: 'hello' }, - { a: '133', c: 'edd12221', d: 2, key: '3', title: 'hello' }, - { a: '133', c: 'edd12221', d: 2, key: '4', title: 'hello' }, - { a: '133', c: 'edd12221', d: 2, key: '5', title: 'hello' }, - { a: '133', c: 'edd12221', d: 2, key: '6', title: 'hello' }, - { a: '133', c: 'edd12221', d: 2, key: '7', title: 'hello' }, - { a: '133', c: 'edd12221', d: 2, key: '8', title: 'hello' }, - { a: '133', c: 'edd12221', d: 2, key: '9', title: 'hello' }, -]; - -ReactDOM.render( -
    -

    Fixed columns

    -
    record.title} - expandIconAsCell - scroll={{ x: 1200 }} - data={data} - /> - -, document.getElementById('__react-content')); diff --git a/components/vc-table/demo/fixedColumns.js b/components/vc-table/demo/fixedColumns.js deleted file mode 100644 index ed05451dc..000000000 --- a/components/vc-table/demo/fixedColumns.js +++ /dev/null @@ -1,45 +0,0 @@ -/* eslint-disable no-console,func-names,react/no-multi-comp */ -import React from 'react'; -import ReactDOM from 'react-dom'; -import Table from 'rc-table'; -import 'rc-table/assets/index.less'; - -const columns = [ - { title: 'title1', dataIndex: 'a', key: 'a', width: 100, fixed: 'left' }, - { title: 'title2', dataIndex: 'b', key: 'b', width: 100, fixed: 'left' }, - { title: 'title3', dataIndex: 'c', key: 'c' }, - { title: 'title4', dataIndex: 'b', key: 'd' }, - { title: 'title5', dataIndex: 'b', key: 'e' }, - { title: 'title6', dataIndex: 'b', key: 'f' }, - { title:
    title7


    Hello world!
    , dataIndex: 'b', key: 'g' }, - { title: 'title8', dataIndex: 'b', key: 'h' }, - { title: 'title9', dataIndex: 'b', key: 'i' }, - { title: 'title10', dataIndex: 'b', key: 'j' }, - { title: 'title11', dataIndex: 'b', key: 'k' }, - { title: 'title12', dataIndex: 'b', key: 'l', width: 100, fixed: 'right' }, -]; - -const data = [ - { a: '123', b: 'xxxxxxxx', d: 3, key: '1' }, - { a: 'cdd', b: 'edd12221', d: 3, key: '2' }, - { a: '133', c: 'edd12221', d: 2, key: '3' }, - { a: '133', c: 'edd12221', d: 2, key: '4' }, - { a: '133', c: 'edd12221', d: 2, key: '5' }, - { a: '133', c: 'edd12221', d: 2, key: '6' }, - { a: '133', c: 'edd12221', d: 2, key: '7' }, - { a: '133', c: 'edd12221', d: 2, key: '8' }, - { a: '133', c: 'edd12221', d: 2, key: '9' }, -]; - -ReactDOM.render( -
    -

    Fixed columns

    -
    record.title} - expandIconAsCell - scroll={{ x: 1200 }} - data={data} - /> - -, document.getElementById('__react-content')); diff --git a/components/vc-table/demo/fixedColumnsAndHeader.js b/components/vc-table/demo/fixedColumnsAndHeader.js deleted file mode 100644 index e62776a59..000000000 --- a/components/vc-table/demo/fixedColumnsAndHeader.js +++ /dev/null @@ -1,39 +0,0 @@ -/* eslint-disable no-console,func-names,react/no-multi-comp */ -import React from 'react'; -import ReactDOM from 'react-dom'; -import Table from 'rc-table'; -import 'rc-table/assets/index.less'; - -const columns = [ - { title: 'title1', dataIndex: 'a', key: 'a', width: 100, fixed: 'left' }, - { title: 'title2', dataIndex: 'b', key: 'b', width: 100, fixed: 'left' }, - { title: 'title3', dataIndex: 'c', key: 'c', width: 150 }, - { title: 'title4', dataIndex: 'c', key: 'd', width: 150 }, - { title: 'title5', dataIndex: 'c', key: 'e', width: 150 }, - { title: 'title6', dataIndex: 'c', key: 'f', width: 150 }, - { title: 'title7', dataIndex: 'c', key: 'g', width: 150 }, - { title: 'title8', dataIndex: 'c', key: 'h', width: 150 }, - { title: 'title9', dataIndex: 'b', key: 'i', width: 150 }, - { title: 'title10', dataIndex: 'b', key: 'j', width: 150 }, - { title: 'title11', dataIndex: 'b', key: 'k', width: 150 }, - { title: 'title12', dataIndex: 'b', key: 'l', width: 100, fixed: 'right' }, -]; - -const data = [ - { a: 'aaa', b: 'bbb', c: '内容内容内容内容内容', d: 3, key: '1' }, - { a: 'aaa', b: 'bbb', c: '内容内容内容内容内容', d: 3, key: '2' }, - { a: 'aaa', c: '内容内容内容内容内容', d: 2, key: '3' }, - { a: 'aaa', c: '内容内容内容内容内容', d: 2, key: '4' }, - { a: 'aaa', c: '内容内容内容内容内容', d: 2, key: '5' }, - { a: 'aaa', c: '内容内容内容内容内容', d: 2, key: '6' }, - { a: 'aaa', c: '内容内容内容内容内容', d: 2, key: '7' }, - { a: 'aaa', c: '内容内容内容内容内容', d: 2, key: '8' }, - { a: 'aaa', c: '内容内容内容内容内容', d: 2, key: '9' }, -]; - -ReactDOM.render( -
    -

    Fixed columns and header

    -
    - -, document.getElementById('__react-content')); diff --git a/components/vc-table/demo/fixedColumnsAndHeaderSyncRowHeight.js b/components/vc-table/demo/fixedColumnsAndHeaderSyncRowHeight.js deleted file mode 100644 index 932bdfa0f..000000000 --- a/components/vc-table/demo/fixedColumnsAndHeaderSyncRowHeight.js +++ /dev/null @@ -1,39 +0,0 @@ -/* eslint-disable no-console,func-names,react/no-multi-comp */ -import React from 'react'; -import ReactDOM from 'react-dom'; -import Table from 'rc-table'; -import 'rc-table/assets/index.less'; - -const columns = [ - { title: 'title1', dataIndex: 'a', key: 'a', width: 100, fixed: 'left' }, - { title: 'title2', dataIndex: 'b', key: 'b', width: 100, fixed: 'left' }, - { title: 'titletitle3', dataIndex: 'c', key: 'c' }, - { title: 'title4', dataIndex: 'c', key: 'd', width: 150 }, - { title: 'title5', dataIndex: 'c', key: 'e', width: 150 }, - { title: 'title6', dataIndex: 'c', key: 'f', width: 150 }, - { title: 'title7', dataIndex: 'c', key: 'g', width: 150 }, - { title: 'title8', dataIndex: 'c', key: 'h', width: 150 }, - { title: 'title9', dataIndex: 'b', key: 'i', width: 150 }, - { title: 'title10', dataIndex: 'b', key: 'j', width: 150 }, - { title: 'title11', dataIndex: 'b', key: 'k', width: 150 }, - { title: 'title12', dataIndex: 'b', key: 'l', width: 100, fixed: 'right' }, -]; - -const data = [ - { a: 'aaa', b: 'bbb', c: '内容内容内容内容内容', d: 3, key: '1' }, - { a: 'aaa', b: 'bbb', c: '内容内容内容内容内容', d: 3, key: '2' }, - { a: 'aaa', c: '内容内容内容内容内容', d: 2, key: '3' }, - { a: 'aaa', c: '内容内容内容内容内容', d: 2, key: '4' }, - { a: 'aaa', c: '内容内容内容内容内容', d: 2, key: '5' }, - { a: 'aaa', c: '内容内容内容内容内容', d: 2, key: '6' }, - { a: 'aaa', c: '内容内容内容内容内容', d: 2, key: '7' }, - { a: 'aaa', c: '内容内容内容内容内容', d: 2, key: '8' }, - { a: 'aaa', c: '内容内容内容内容内容', d: 2, key: '9' }, -]; - -ReactDOM.render( -
    -

    Fixed columns and header, resize window for test

    -
    - -, document.getElementById('__react-content')); diff --git a/components/vc-table/demo/fixedColumnsWhenResize.js b/components/vc-table/demo/fixedColumnsWhenResize.js deleted file mode 100644 index a59b2c2c0..000000000 --- a/components/vc-table/demo/fixedColumnsWhenResize.js +++ /dev/null @@ -1,45 +0,0 @@ -/* eslint-disable no-console,func-names,react/no-multi-comp */ -import React from 'react'; -import ReactDOM from 'react-dom'; -import Table from 'rc-table'; -import 'rc-table/assets/index.less'; - -const columns = [ - { title: 'title1', dataIndex: 'a', key: 'a', width: 100, fixed: 'left' }, - { title: 'title2', dataIndex: 'b', key: 'b', width: 100, fixed: 'left' }, - { title: 'title3', dataIndex: 'c', key: 'c' }, - { title: 'title4', dataIndex: 'b', key: 'd' }, - { title: 'title5', dataIndex: 'b', key: 'e' }, - { title: 'title6', dataIndex: 'b', key: 'f' }, - { title:
    title7


    Hello world!
    , dataIndex: 'b', key: 'g' }, - { title: 'title8', dataIndex: 'b', key: 'h' }, - { title: 'title9', dataIndex: 'b', key: 'i' }, - { title: 'title10', dataIndex: 'b', key: 'j' }, - { title: 'title11', dataIndex: 'b', key: 'k' }, - { title: 'title12', dataIndex: 'b', key: 'l', width: 100, fixed: 'right' }, -]; - -const data = [ - { a: '123', b: 'xxxxxxxx', d: 3, key: '1' }, - { a: 'cdd', b: 'edd12221', d: 3, key: '2' }, - { a: '133', c: 'edd12221', d: 2, key: '3' }, - { a: '133', c: 'edd12221', d: 2, key: '4' }, - { a: '133', c: 'edd12221', d: 2, key: '5' }, - { a: '133', c: 'edd12221', d: 2, key: '6' }, - { a: '133', c: 'edd12221', d: 2, key: '7' }, - { a: '133', c: 'edd12221', d: 2, key: '8' }, - { a: '133', c: 'edd12221', d: 2, key: '9' }, -]; - -ReactDOM.render( -
    -

    See fixed columns when you resize window

    -
    record.title} - expandIconAsCell - scroll={{ x: 800 }} - data={data} - /> - -, document.getElementById('__react-content')); diff --git a/components/vc-table/demo/grouping-columns.js b/components/vc-table/demo/grouping-columns.js deleted file mode 100644 index 699a5ce0f..000000000 --- a/components/vc-table/demo/grouping-columns.js +++ /dev/null @@ -1,100 +0,0 @@ -/* eslint-disable no-console,func-names,react/no-multi-comp */ -import React from 'react'; -import ReactDOM from 'react-dom'; -import Table from 'rc-table'; -import 'rc-table/assets/index.less'; -import 'rc-table/assets/bordered.less'; - -const columns = [ - { - title: '姓名', - dataIndex: 'name', - key: 'name', - }, - { - title: '其它', - children: [ - { - title: '年龄', - dataIndex: 'age', - key: 'age', - }, - { - title: '住址', - children: [ - { - title: '街道', - dataIndex: 'street', - key: 'street', - }, - { - title: '小区', - children: [ - { - title: '单元', - dataIndex: 'building', - key: 'building', - }, - { - title: '门牌', - dataIndex: 'number', - key: 'number', - }, - ], - }, - ], - }, - ], - }, - { - title: '公司', - children: [ - { - title: '地址', - dataIndex: 'companyAddress', - key: 'companyAddress', - }, - { - title: '名称', - dataIndex: 'companyName', - key: 'companyName', - }, - ], - }, - { - title: '性别', - dataIndex: 'gender', - key: 'gender', - }, -]; - - -const data = [{ - key: '1', - name: '胡彦斌', - age: 32, - street: '拱墅区和睦街道', - building: 1, - number: 2033, - companyAddress: '西湖区湖底公园', - companyName: '湖底有限公司', - gender: '男', -}, { - key: '2', - name: '胡彦祖', - age: 42, - street: '拱墅区和睦街道', - building: 3, - number: 2035, - companyAddress: '西湖区湖底公园', - companyName: '湖底有限公司', - gender: '男', -}]; - -ReactDOM.render( -
    -

    grouping columns

    -
    - , - document.getElementById('__react-content') -); diff --git a/components/vc-table/demo/hide-header.js b/components/vc-table/demo/hide-header.js deleted file mode 100644 index ca1db1c6a..000000000 --- a/components/vc-table/demo/hide-header.js +++ /dev/null @@ -1,34 +0,0 @@ -/* eslint-disable no-console,func-names,react/no-multi-comp */ -import React from 'react'; -import ReactDOM from 'react-dom'; -import Table from 'rc-table'; -import 'rc-table/assets/index.less'; - -const columns = [ - { title: 'title1', dataIndex: 'a', key: 'a', width: 100 }, - { id: '123', title: 'title2', dataIndex: 'b', key: 'b', width: 100 }, - { title: 'title3', dataIndex: 'c', key: 'c', width: 200 }, - { - title: 'Operations', dataIndex: '', key: 'd', render() { - return Operations; - }, - }, -]; - -const data = [ - { a: '123', key: '1' }, - { a: 'cdd', b: 'edd', key: '2' }, - { a: '1333', c: 'eee', d: 2, key: '3' }, -]; - -ReactDOM.render( -
    -

    hide table head

    -
    - , - document.getElementById('__react-content') -); diff --git a/components/vc-table/demo/jsx.js b/components/vc-table/demo/jsx.js deleted file mode 100644 index d39118b4a..000000000 --- a/components/vc-table/demo/jsx.js +++ /dev/null @@ -1,49 +0,0 @@ -/* eslint-disable no-console,func-names,react/no-multi-comp */ -import React from 'react'; -import ReactDOM from 'react-dom'; -import Table from 'rc-table'; -import 'rc-table/assets/index.less'; - -const { ColumnGroup, Column } = Table; - -const data = [ - { a: '123', key: '1' }, - { a: 'cdd', b: 'edd', key: '2' }, - { a: '1333', c: 'eee', d: 2, key: '3' }, -]; - -ReactDOM.render( -
    -

    JSX table

    -
    - - - - - - Operations} - /> -
    -
    , - document.getElementById('__react-content') -); diff --git a/components/vc-table/demo/key.js b/components/vc-table/demo/key.js deleted file mode 100644 index d468ba201..000000000 --- a/components/vc-table/demo/key.js +++ /dev/null @@ -1,70 +0,0 @@ -/* eslint-disable no-console,func-names,react/no-multi-comp */ -import React from 'react'; -import ReactDOM from 'react-dom'; -import PropTypes from 'prop-types'; -import Table from 'rc-table'; -import 'rc-table/assets/index.less'; - -const CheckBox = ({ id }) => ( - -); - -class Demo extends React.Component { - static propTypes = { - data: PropTypes.array.isRequired, - } - - constructor(props) { - super(props); - - this.state = { - data: props.data, - }; - } - - remove(index) { - const rows = this.state.data; - rows.splice(index, 1); - this.setState({ - data: rows, - }); - } - - handleClick = (index) => () => { - this.remove(index); - } - - checkbox(a) { - return ; - } - - renderAction = (o, row, index) => { - return Delete; - } - - render() { - const state = this.state; - const columns = [ - { title: 'title1', dataIndex: 'a', key: 'a', width: 100, render: this.checkbox }, - { title: 'title2', dataIndex: 'b', key: 'b', width: 100 }, - { title: 'title3', dataIndex: 'c', key: 'c', width: 200 }, - { title: 'Operations', dataIndex: '', key: 'x', render: this.renderAction }, - ]; - return ( - record.a} /> - ); - } -} - -const data = [{ a: '123' }, { a: 'cdd', b: 'edd' }, { a: '1333', c: 'eee', d: 2 }]; - -ReactDOM.render( -
    -

    specify key

    - -
    , - document.getElementById('__react-content') -); diff --git a/components/vc-table/demo/nested.js b/components/vc-table/demo/nested.js deleted file mode 100644 index 860ebb0b5..000000000 --- a/components/vc-table/demo/nested.js +++ /dev/null @@ -1,45 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import Table from 'rc-table'; -import 'rc-table/assets/index.less'; - -const columns = [ - { title: 'First Name', dataIndex: 'names.first', key: 'a', width: 100 }, - { title: 'Last Name', dataIndex: 'names.last', key: 'b', width: 100 }, - { title: 'Age', dataIndex: 'age', key: 'c', width: 100 }, -]; - -const data = [{ - age: '23', - names: { - first: 'John', - last: 'Doe', - }, - key: '1', -}, { - age: '36', - names: { - first: 'Terry', - last: 'Garner', - }, - key: '2', -}, { - age: '52', - names: { - first: 'Thomas', - last: 'Goodwin', - }, - key: '3', -}]; - -ReactDOM.render( -
    -

    Nested data table

    -
    - , - document.getElementById('__react-content') -); diff --git a/components/vc-table/demo/no-data.js b/components/vc-table/demo/no-data.js deleted file mode 100644 index 670384753..000000000 --- a/components/vc-table/demo/no-data.js +++ /dev/null @@ -1,26 +0,0 @@ -/* eslint-disable no-console,func-names,react/no-multi-comp */ -import React from 'react'; -import ReactDOM from 'react-dom'; -import Table from 'rc-table'; -import 'rc-table/assets/index.less'; - -const columns = [ - { title: 'title1', dataIndex: 'a', key: 'a', width: 100 }, - { id: '123', title: 'title2', dataIndex: 'b', key: 'b', width: 100 }, - { title: 'title3', dataIndex: 'c', key: 'c', width: 200 }, - { - title: 'Operations', dataIndex: '', key: 'd', render() { - return Operations; - }, - }, -]; - -const data = []; - -ReactDOM.render( -
    -

    simple table

    -
    - , - document.getElementById('__react-content') -); diff --git a/components/vc-table/demo/react-dnd.js b/components/vc-table/demo/react-dnd.js deleted file mode 100644 index 886b6f0b0..000000000 --- a/components/vc-table/demo/react-dnd.js +++ /dev/null @@ -1,187 +0,0 @@ -/* eslint-disable no-unused-expressions,new-cap */ -import React from 'react'; -import ReactDOM from 'react-dom'; -import { injectGlobal } from 'styled-components'; -import update from 'immutability-helper'; -import { DragDropContext, DragSource, DropTarget } from 'react-dnd'; -import HTML5Backend from 'react-dnd-html5-backend'; -import Table from 'rc-table'; -import 'rc-table/assets/index.less'; - -injectGlobal` - tr.drop-over-downward td { - border-bottom: 2px dashed red; - } - - tr.drop-over-upward td { - border-top: 2px dashed red; - } -`; - -function dragDirection( - dragIndex, - hoverIndex, - initialClientOffset, - clientOffset, - sourceClientOffset, -) { - const hoverMiddleY = (initialClientOffset.y - sourceClientOffset.y) / 2; - const hoverClientY = clientOffset.y - sourceClientOffset.y; - if (dragIndex < hoverIndex && hoverClientY > hoverMiddleY) { - return 'downward'; - } - if (dragIndex > hoverIndex && hoverClientY < hoverMiddleY) { - return 'upward'; - } -} - -let BodyRow = (props) => { - const { - isOver, - connectDragSource, - connectDropTarget, - moveRow, - dragRow, - clientOffset, - sourceClientOffset, - initialClientOffset, - ...restProps, - } = props; - const style = { cursor: 'move' }; - - let className = restProps.className; - if (isOver && initialClientOffset) { - const direction = dragDirection( - dragRow.index, - restProps.index, - initialClientOffset, - clientOffset, - sourceClientOffset - ); - if (direction === 'downward') { - className += ' drop-over-downward'; - } - if (direction === 'upward') { - className += ' drop-over-upward'; - } - } - - return connectDragSource( - connectDropTarget( - - ) - ); -}; - -const rowSource = { - beginDrag(props) { - return { - index: props.index, - }; - }, -}; - -const rowTarget = { - drop(props, monitor) { - const dragIndex = monitor.getItem().index; - const hoverIndex = props.index; - - // Don't replace items with themselves - if (dragIndex === hoverIndex) { - return; - } - - // Time to actually perform the action - props.moveRow(dragIndex, hoverIndex); - - // Note: we're mutating the monitor item here! - // Generally it's better to avoid mutations, - // but it's good here for the sake of performance - // to avoid expensive index searches. - monitor.getItem().index = hoverIndex; - }, -}; - -BodyRow = DropTarget('row', rowTarget, (connect, monitor) => ({ - connectDropTarget: connect.dropTarget(), - isOver: monitor.isOver(), - sourceClientOffset: monitor.getSourceClientOffset(), -}))( - DragSource('row', rowSource, (connect, monitor) => ({ - connectDragSource: connect.dragSource(), - dragRow: monitor.getItem(), - clientOffset: monitor.getClientOffset(), - initialClientOffset: monitor.getInitialClientOffset(), - }))(BodyRow) -); - -const columns = [ - { title: 'title1', dataIndex: 'a', key: 'a', width: 100 }, - { id: '123', title: 'title2', dataIndex: 'b', key: 'b', width: 100 }, - { title: 'title3', dataIndex: 'c', key: 'c', width: 200 }, - { - title: 'Operations', - dataIndex: '', - key: 'd', - render() { - return Operations; - }, - }, -]; - -class Demo extends React.Component { - state = { - data: [ - { a: '123', key: '1' }, - { a: 'cdd', b: 'edd', key: '2' }, - { a: '1333', c: 'eee', d: 2, key: '3' }, - ], - } - - components = { - body: { - row: BodyRow, - }, - } - - moveRow = (dragIndex, hoverIndex) => { - const { data } = this.state; - const dragRow = data[dragIndex]; - - this.setState( - update(this.state, { - data: { - $splice: [[dragIndex, 1], [hoverIndex, 0, dragRow]], - }, - }), - ); - } - - render() { - return ( -
    ({ - index, - moveRow: this.moveRow, - })} - /> - ); - } -} - -Demo = DragDropContext(HTML5Backend)(Demo); - -ReactDOM.render( -
    -

    Integrate with react-dnd

    - -
    , - document.getElementById('__react-content') -); diff --git a/components/vc-table/demo/rowAndCellClick.js b/components/vc-table/demo/rowAndCellClick.js deleted file mode 100644 index e8dd239bc..000000000 --- a/components/vc-table/demo/rowAndCellClick.js +++ /dev/null @@ -1,105 +0,0 @@ -/* eslint-disable no-console,func-names,react/no-multi-comp */ -import React from 'react'; -import ReactDOM from 'react-dom'; -import Table from 'rc-table'; -import 'rc-table/assets/index.less'; - -const onRowClick = (record, index, event) => { - console.log(`Click nth(${index}) row of parent, record.name: ${record.name}`); - // See https://facebook.github.io/react/docs/events.html for original click event details. - if (event.shiftKey) { - console.log('Shift + mouse click triggered.'); - } -}; - -const onRowDoubleClick = (record, index) => { - console.log(`Double click nth(${index}) row of parent, record.name: ${record.name}`); -}; - -const columns = [{ - title: 'Name', - dataIndex: 'name', - key: 'name', - width: 400, -}, { - title: 'Age', - dataIndex: 'age', - key: 'age', - width: 100, - render: (text) => ( - {text} (Trigger Cell Click) - ), - onCell: (record) => ({ - onClick(e) { - console.log('Click cell', record, e.target); - }, - }), -}, { - title: 'Address', - dataIndex: 'address', - key: 'address', - width: 200, -}]; - -const data = [{ - key: 1, - name: 'a', - age: 32, - address: 'I am a', - children: [{ - key: 11, - name: 'aa', - age: 33, - address: 'I am aa', - }, { - key: 12, - name: 'ab', - age: 33, - address: 'I am ab', - children: [{ - key: 121, - name: 'aba', - age: 33, - address: 'I am aba', - }], - }, { - key: 13, - name: 'ac', - age: 33, - address: 'I am ac', - children: [{ - key: 131, - name: 'aca', - age: 33, - address: 'I am aca', - children: [{ - key: 1311, - name: 'acaa', - age: 33, - address: 'I am acaa', - }, { - key: 1312, - name: 'acab', - age: 33, - address: 'I am acab', - }], - }], - }], -}, { - key: 2, - name: 'b', - age: 32, - address: 'I am b', -}]; - -ReactDOM.render( -
    ({ - onClick: onRowClick.bind(null, record, index), - onDoubleClick: onRowDoubleClick.bind(null, record, index), - })} - />, - document.getElementById('__react-content') -); diff --git a/components/vc-table/demo/scrollX.js b/components/vc-table/demo/scrollX.js deleted file mode 100644 index 4659bcea2..000000000 --- a/components/vc-table/demo/scrollX.js +++ /dev/null @@ -1,34 +0,0 @@ -/* eslint-disable no-console,func-names,react/no-multi-comp */ -import React from 'react'; -import ReactDOM from 'react-dom'; -import Table from 'rc-table'; -import 'rc-table/assets/index.less'; - -const columns = [ - { title: 'title1', dataIndex: 'a', key: 'a', width: 100 }, - { title: 'title2', dataIndex: 'b', key: 'b', width: 100 }, - { title: 'title3', dataIndex: 'c', key: 'c', width: 100 }, - { title: 'title4', dataIndex: 'b', key: 'd', width: 100 }, - { title: 'title5', dataIndex: 'b', key: 'e', width: 100 }, - { title: 'title6', dataIndex: 'b', key: 'f', width: 100 }, - { title: 'title7', dataIndex: 'b', key: 'g', width: 100 }, - { title: 'title8', dataIndex: 'b', key: 'h', width: 100 }, - { title: 'title9', dataIndex: 'b', key: 'i', width: 100 }, - { title: 'title10', dataIndex: 'b', key: 'j', width: 100 }, - { title: 'title11', dataIndex: 'b', key: 'k', width: 100 }, - { title: 'title12', dataIndex: 'b', key: 'l', width: 100 }, -]; - -const data = [ - { a: '123', b: 'xxxxxxxx xxxxxxxx', d: 3, key: '1' }, - { a: 'cdd', b: 'edd12221 edd12221', d: 3, key: '2' }, - { a: '133', c: 'edd12221 edd12221', d: 2, key: '3' }, - { a: '133', c: 'edd12221 edd12221', d: 2, key: '4' }, -]; - -ReactDOM.render( -
    -

    Scroll X

    -
    - -, document.getElementById('__react-content')); diff --git a/components/vc-table/demo/scrollXY.js b/components/vc-table/demo/scrollXY.js deleted file mode 100644 index 6da56a772..000000000 --- a/components/vc-table/demo/scrollXY.js +++ /dev/null @@ -1,45 +0,0 @@ -/* eslint-disable no-console,func-names,react/no-multi-comp */ -import React from 'react'; -import ReactDOM from 'react-dom'; -import Table from 'rc-table'; -import 'rc-table/assets/index.less'; - -const columns = [ - { title: 'title1', dataIndex: 'a', key: 'a', width: 100 }, - { title: 'title2', dataIndex: 'b', key: 'b', width: 100 }, - { title: 'title3', dataIndex: 'c', key: 'c', width: 100 }, - { title: 'title4', dataIndex: 'b', key: 'd', width: 100 }, - { title: 'title5', dataIndex: 'b', key: 'e', width: 100 }, - { title: 'title6', dataIndex: 'b', key: 'f', width: 100 }, - { title: 'title7', dataIndex: 'b', key: 'g', width: 100 }, - { title: 'title8', dataIndex: 'b', key: 'h', width: 100 }, - { title: 'title9', dataIndex: 'b', key: 'i', width: 100 }, - { title: 'title10', dataIndex: 'b', key: 'j', width: 100 }, - { title: 'title11', dataIndex: 'b', key: 'k', width: 100 }, - { title: 'title12', dataIndex: 'b', key: 'l', width: 100 }, -]; - -const data = [ - { a: '123', b: 'xxxxxxxx xxxxxxxx', d: 3, key: '1' }, - { a: 'cdd', b: 'edd12221 edd12221', d: 3, key: '2' }, - { a: '133', c: 'edd12221 edd12221', d: 2, key: '3' }, - { a: '133', c: 'edd12221 edd12221', d: 2, key: '4' }, - { a: '133', c: 'edd12221 edd12221', d: 2, key: '5' }, - { a: '133', c: 'edd12221 edd12221', d: 2, key: '6' }, - { a: '133', c: 'edd12221 edd12221', d: 2, key: '7' }, - { a: '133', c: 'edd12221 edd12221', d: 2, key: '8' }, - { a: '133', c: 'edd12221 edd12221', d: 2, key: '9' }, -]; - -ReactDOM.render( -
    -

    Scroll X/Y

    -
    - -, document.getElementById('__react-content')); diff --git a/components/vc-table/demo/scrollY.js b/components/vc-table/demo/scrollY.js deleted file mode 100644 index ae286c5ed..000000000 --- a/components/vc-table/demo/scrollY.js +++ /dev/null @@ -1,62 +0,0 @@ -/* eslint-disable no-console,func-names,react/no-multi-comp */ -import React from 'react'; -import ReactDOM from 'react-dom'; -import Table from 'rc-table'; -import 'rc-table/assets/index.less'; - -const data = []; -for (let i = 0; i < 10; i++) { - data.push({ - key: i, - a: `a${i}`, - b: `b${i}`, - c: `c${i}`, - }); -} - -class Demo extends React.Component { - state = { - showBody: true, - } - - toggleBody = () => { - this.setState({ - showBody: !this.state.showBody, - }); - } - - render() { - const columns = [ - { title: 'title1', key: 'a', dataIndex: 'a', width: 100 }, - { id: '123', title: 'title2', dataIndex: 'b', key: 'b', width: 100 }, - { title: 'title3', key: 'c', dataIndex: 'c', width: 200 }, - { - title: {this.state.showBody ? '隐藏' : '显示'}体, - key: 'x', - width: 200, - render() { - return Operations; - }, - }, - ]; - return ( -
    record.key} - bodyStyle={{ - display: this.state.showBody ? '' : 'none', - }} - /> - ); - } -} - -ReactDOM.render( -
    -

    scroll body table

    - -
    , - document.getElementById('__react-content') -); diff --git a/components/vc-table/demo/simple.js b/components/vc-table/demo/simple.js deleted file mode 100644 index c07339aaf..000000000 --- a/components/vc-table/demo/simple.js +++ /dev/null @@ -1,30 +0,0 @@ -/* eslint-disable no-console,func-names,react/no-multi-comp */ -import React from 'react'; -import ReactDOM from 'react-dom'; -import Table from 'rc-table'; -import 'rc-table/assets/index.less'; - -const columns = [ - { title: 'title1', dataIndex: 'a', key: 'a', width: 100 }, - { id: '123', title: 'title2', dataIndex: 'b', key: 'b', width: 100 }, - { title: 'title3', dataIndex: 'c', key: 'c', width: 200 }, - { - title: 'Operations', dataIndex: '', key: 'd', render() { - return Operations; - }, - }, -]; - -const data = [ - { a: '123', key: '1' }, - { a: 'cdd', b: 'edd', key: '2' }, - { a: '1333', c: 'eee', d: 2, key: '3' }, -]; - -ReactDOM.render( -
    -

    simple table

    -
    - , - document.getElementById('__react-content') -); diff --git a/components/vc-table/demo/styled-components.js b/components/vc-table/demo/styled-components.js deleted file mode 100644 index fa453e009..000000000 --- a/components/vc-table/demo/styled-components.js +++ /dev/null @@ -1,45 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import styled from 'styled-components'; -import Table from 'rc-table'; -import 'rc-table/assets/index.less'; - -const columns = [ - { title: 'title1', dataIndex: 'a', key: 'a', width: 100 }, - { id: '123', title: 'title2', dataIndex: 'b', key: 'b', width: 100 }, - { title: 'title3', dataIndex: 'c', key: 'c', width: 200 }, - { - title: 'Operations', - dataIndex: '', - key: 'd', - render() { - return Operations; - }, - }, -]; - -const data = [ - { a: '123', key: '1' }, - { a: 'cdd', b: 'edd', key: '2' }, - { a: '1333', c: 'eee', d: 2, key: '3' }, -]; - -const BodyRow = styled.tr` - &:hover { - background: palevioletred !important; - } -`; - -const components = { - body: { - row: BodyRow, - }, -}; - -ReactDOM.render( -
    -

    Integrate with styled-components

    -
    - , - document.getElementById('__react-content') -); diff --git a/components/vc-table/demo/subTable.js b/components/vc-table/demo/subTable.js deleted file mode 100644 index 459428e36..000000000 --- a/components/vc-table/demo/subTable.js +++ /dev/null @@ -1,63 +0,0 @@ -/* eslint-disable no-console,func-names,react/no-multi-comp */ -import React from 'react'; -import ReactDOM from 'react-dom'; -import Table from 'rc-table'; -import 'rc-table/assets/index.less'; - -const data = [ - { - a: 'a1', - }, - { - a: 'a2', - b: 'b2', - children: [ - { - a: 'a2-1', - b: 'b2-1', - }, - { - a: 'a2-2', - b: 'b2-2', - }, - ], - }, - { - a: 'a3', - c: 'c3', - d: 'd3', - }, -]; - -class Demo extends React.Component { - handleClick = (record, e) => { - e.preventDefault(); - console.log(record.a); - } - - render() { - const columns = [ - { title: 'title1', dataIndex: 'a', key: 'a', width: 100 }, - { title: 'title2', dataIndex: 'b', key: 'b', width: 100 }, - { title: 'title3', dataIndex: 'c', key: 'c', width: 200 }, - { - title: 'Operations', dataIndex: '', key: 'x', render: (text, record) => { - return this.handleClick(record, e)}>click {record.a}; - }, - }, - ]; - return ( -
    -

    sub table

    -
    record.a} - /> - - ); - } -} - -ReactDOM.render(, document.getElementById('__react-content')); diff --git a/components/vc-table/demo/title-and-footer.js b/components/vc-table/demo/title-and-footer.js deleted file mode 100644 index 92fb0fc8c..000000000 --- a/components/vc-table/demo/title-and-footer.js +++ /dev/null @@ -1,35 +0,0 @@ -/* eslint-disable no-console,func-names,react/no-multi-comp */ -import React from 'react'; -import ReactDOM from 'react-dom'; -import Table from 'rc-table'; -import 'rc-table/assets/index.less'; - -const columns = [ - { title: 'title1', dataIndex: 'a', key: 'a', width: 100 }, - { id: '123', title: 'title2', dataIndex: 'b', key: 'b', width: 100 }, - { title: 'title3', dataIndex: 'c', key: 'c', width: 200 }, - { - title: 'Operations', dataIndex: '', key: 'd', render() { - return Operations; - }, - }, -]; - -const data = [ - { a: '123', key: '1' }, - { a: 'cdd', b: 'edd', key: '2' }, - { a: '1333', c: 'eee', d: 2, key: '3' }, -]; - -ReactDOM.render( -
    -

    title and footer

    -
    Title: {currentData.length} items
    } - footer={currentData =>
    Footer: {currentData.length} items
    } - /> - , - document.getElementById('__react-content') -); diff --git a/components/vc-table/src/BaseTable.jsx b/components/vc-table/src/BaseTable.jsx index c2d0811eb..93a624224 100644 --- a/components/vc-table/src/BaseTable.jsx +++ b/components/vc-table/src/BaseTable.jsx @@ -5,7 +5,8 @@ import TableHeader from './TableHeader' import TableRow from './TableRow' import ExpandableRow from './ExpandableRow' import { mergeProps } from '../../_util/props-util' - +import { connect } from '../../_util/store' +function noop () {} const BaseTable = { name: 'BaseTable', props: { @@ -34,18 +35,18 @@ const BaseTable = { renderRows (renderData, indent, ancestorKeys = []) { const { - columnManager, components, + columnManager, sComponents: components, prefixCls, childrenColumnName, rowClassName, // rowRef, $listeners: { - rowClick: onRowClick, - rowDoubleclick: onRowDoubleClick, - rowContextmenu: onRowContextMenu, - rowMouseenter: onRowMouseEnter, - rowMouseleave: onRowMouseLeave, - row: onRow, + rowClick: onRowClick = noop, + rowDoubleclick: onRowDoubleClick = noop, + rowContextmenu: onRowContextMenu = noop, + rowMouseenter: onRowMouseEnter = noop, + rowMouseleave: onRowMouseLeave = noop, + row: onRow = noop, }, } = this.table const { getRowKey, fixed, expander, isAnyColumnsFixed } = this @@ -86,6 +87,7 @@ const BaseTable = { }, key, on: { + // ...expander.on, rowClick: onRowClick, expandedChange: expander.handleExpandChange, }, @@ -148,7 +150,7 @@ const BaseTable = { }, render () { - const { components, prefixCls, scroll, data, getBodyWrapper } = this.table + const { sComponents: components, prefixCls, scroll, data, getBodyWrapper } = this.table const { expander, tableClassName, hasHead, hasBody, fixed, columns } = this const tableStyle = {} @@ -186,4 +188,4 @@ const BaseTable = { }, } -export default BaseTable +export default connect()(BaseTable) diff --git a/components/vc-table/src/ColGroup.jsx b/components/vc-table/src/ColGroup.jsx index 0df1afe27..9f169e57a 100644 --- a/components/vc-table/src/ColGroup.jsx +++ b/components/vc-table/src/ColGroup.jsx @@ -4,13 +4,14 @@ export default { name: 'ColGroup', props: { fixed: PropTypes.string, + columns: PropTypes.array, }, inject: { table: { default: {}}, }, render () { const { fixed, table } = this - const { prefixCls, expandIconAsCell } = table + const { prefixCls, expandIconAsCell, columnManager } = table let cols = [] @@ -26,18 +27,19 @@ export default { let leafColumns if (fixed === 'left') { - leafColumns = table.columnManager.leftLeafColumns() + leafColumns = columnManager.leftLeafColumns() } else if (fixed === 'right') { - leafColumns = table.columnManager.rightLeafColumns() + leafColumns = columnManager.rightLeafColumns() } else { - leafColumns = table.columnManager.leafColumns() + leafColumns = columnManager.leafColumns() } cols = cols.concat( leafColumns.map(c => { + const width = typeof c.width === 'number' ? `${c.width}px` : c.width return ( ) }) diff --git a/components/vc-table/src/ColumnManager.js b/components/vc-table/src/ColumnManager.jsx similarity index 99% rename from components/vc-table/src/ColumnManager.js rename to components/vc-table/src/ColumnManager.jsx index a90756c14..b0696baaf 100644 --- a/components/vc-table/src/ColumnManager.js +++ b/components/vc-table/src/ColumnManager.jsx @@ -1,8 +1,7 @@ export default class ColumnManager { - _cached = {} - constructor (columns, elements) { this.columns = columns || this.normalize(elements) + this._cached = {} } isAnyColumnsFixed () { diff --git a/components/vc-table/src/ExpandableRow.js b/components/vc-table/src/ExpandableRow.jsx similarity index 56% rename from components/vc-table/src/ExpandableRow.js rename to components/vc-table/src/ExpandableRow.jsx index 895cd886c..77d40a0b9 100644 --- a/components/vc-table/src/ExpandableRow.js +++ b/components/vc-table/src/ExpandableRow.jsx @@ -1,6 +1,7 @@ import PropTypes from '../../_util/vue-types' import ExpandIcon from './ExpandIcon' import BaseMixin from '../../_util/BaseMixin' +import { connect } from '../../_util/store' const ExpandableRow = { mixins: [BaseMixin], @@ -32,61 +33,62 @@ const ExpandableRow = { beforeDestroy () { this.handleDestroy() }, + methods: { + hasExpandIcon (columnIndex) { + const { expandRowByClick } = this + return !this.expandIconAsCell && + !expandRowByClick && + columnIndex === this.expandIconColumnIndex + }, - hasExpandIcon (columnIndex) { - const { expandRowByClick } = this - return !this.expandIconAsCell && - !expandRowByClick && - columnIndex === this.expandIconColumnIndex - }, + handleExpandChange (record, event) { + const { expanded, rowKey } = this + this.__emit('expandedChange', !expanded, record, event, rowKey) + }, - handleExpandChange (record, event) { - const { expanded, rowKey } = this - this.__emit('expandedChange', !expanded, record, event, rowKey) - }, + handleDestroy () { + const { rowKey, record } = this + this.__emit('expandedChange', false, record, null, rowKey, true) + }, - handleDestroy () { - const { rowKey, record } = this - this.__emit('expandedChange', false, record, null, rowKey, true) - }, + handleRowClick (record, index, event) { + const { expandRowByClick } = this + if (expandRowByClick) { + this.handleExpandChange(record, event) + } + this.__emit('rowClick', record, index, event) + }, - handleRowClick (record, index, event) { - const { expandRowByClick } = this - if (expandRowByClick) { - this.handleExpandChange(record, event) - } - this.__emit('rowClick', record, index, event) - }, + renderExpandIcon () { + const { prefixCls, expanded, record, needIndentSpaced } = this - renderExpandIcon () { - const { prefixCls, expanded, record, needIndentSpaced } = this + return ( + + ) + }, - return ( - - ) - }, + renderExpandIconCell (cells) { + if (!this.expandIconAsCell) { + return + } + const { prefixCls } = this - renderExpandIconCell (cells) { - if (!this.expandIconAsCell) { - return - } - const { prefixCls } = this - - cells.push( - - ) + cells.push( + + ) + }, }, render () { @@ -103,7 +105,6 @@ const ExpandableRow = { this.expandIconColumnIndex = fixed !== 'right' ? this.expandIconColumnIndex : -1 const childrenData = record[childrenColumnName] this.expandable = !!(childrenData || expandedRowRender) - const expandableRowProps = { props: { indentSize, diff --git a/components/vc-table/src/ExpandableTable.js b/components/vc-table/src/ExpandableTable.js deleted file mode 100644 index 025a59872..000000000 --- a/components/vc-table/src/ExpandableTable.js +++ /dev/null @@ -1,223 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import { connect } from 'mini-store' -import TableRow from './TableRow' -import { remove } from './utils' - -class ExpandableTable extends React.Component { - static propTypes = { - expandIconAsCell: PropTypes.bool, - expandedRowKeys: PropTypes.array, - expandedRowClassName: PropTypes.func, - defaultExpandAllRows: PropTypes.bool, - defaultExpandedRowKeys: PropTypes.array, - expandIconColumnIndex: PropTypes.number, - expandedRowRender: PropTypes.func, - childrenColumnName: PropTypes.string, - indentSize: PropTypes.number, - onExpand: PropTypes.func, - onExpandedRowsChange: PropTypes.func, - columnManager: PropTypes.object.isRequired, - store: PropTypes.object.isRequired, - prefixCls: PropTypes.string.isRequired, - data: PropTypes.array, - children: PropTypes.func.isRequired, - } - - static defaultProps = { - expandIconAsCell: false, - expandedRowClassName: () => '', - expandIconColumnIndex: 0, - defaultExpandAllRows: false, - defaultExpandedRowKeys: [], - childrenColumnName: 'children', - indentSize: 15, - onExpand () {}, - onExpandedRowsChange () {}, - } - - constructor (props) { - super(props) - - const { - data, - childrenColumnName, - defaultExpandAllRows, - expandedRowKeys, - defaultExpandedRowKeys, - getRowKey, - } = props - - let finnalExpandedRowKeys = [] - let rows = [...data] - - if (defaultExpandAllRows) { - for (let i = 0; i < rows.length; i++) { - const row = rows[i] - finnalExpandedRowKeys.push(getRowKey(row, i)) - rows = rows.concat(row[childrenColumnName] || []) - } - } else { - finnalExpandedRowKeys = expandedRowKeys || defaultExpandedRowKeys - } - - this.columnManager = props.columnManager - this.store = props.store - - this.store.setState({ - expandedRowsHeight: {}, - expandedRowKeys: finnalExpandedRowKeys, - }) - } - - componentWillReceiveProps (nextProps) { - if ('expandedRowKeys' in nextProps) { - this.store.setState({ - expandedRowKeys: nextProps.expandedRowKeys, - }) - } - } - - handleExpandChange = (expanded, record, event, rowKey, destroy = false) => { - if (event) { - event.preventDefault() - event.stopPropagation() - } - - const { onExpandedRowsChange, onExpand } = this.props - let { expandedRowKeys } = this.store.getState() - - if (expanded) { - // row was expaned - expandedRowKeys = [...expandedRowKeys, rowKey] - } else { - // row was collapse - const expandedRowIndex = expandedRowKeys.indexOf(rowKey) - if (expandedRowIndex !== -1) { - expandedRowKeys = remove(expandedRowKeys, rowKey) - } - } - - if (!this.props.expandedRowKeys) { - this.store.setState({ expandedRowKeys }) - } - - onExpandedRowsChange(expandedRowKeys) - if (!destroy) { - onExpand(expanded, record) - } - } - - renderExpandIndentCell = (rows, fixed) => { - const { prefixCls, expandIconAsCell } = this.props - if (!expandIconAsCell || fixed === 'right' || !rows.length) { - return - } - - const iconColumn = { - key: 'rc-table-expand-icon-cell', - className: `${prefixCls}-expand-icon-th`, - title: '', - rowSpan: rows.length, - } - - rows[0].unshift({ ...iconColumn, column: iconColumn }) - } - - renderExpandedRow (record, index, render, className, ancestorKeys, indent, fixed) { - const { prefixCls, expandIconAsCell, indentSize } = this.props - let colCount - if (fixed === 'left') { - colCount = this.columnManager.leftLeafColumns().length - } else if (fixed === 'right') { - colCount = this.columnManager.rightLeafColumns().length - } else { - colCount = this.columnManager.leafColumns().length - } - const columns = [{ - key: 'extra-row', - render: () => ({ - props: { - colSpan: colCount, - }, - children: fixed !== 'right' ? render(record, index, indent) : ' ', - }), - }] - if (expandIconAsCell && fixed !== 'right') { - columns.unshift({ - key: 'expand-icon-placeholder', - render: () => null, - }) - } - const parentKey = ancestorKeys[ancestorKeys.length - 1] - const rowKey = `${parentKey}-extra-row` - const components = { - body: { - row: 'tr', - cell: 'td', - }, - } - - return ( - - ) - } - - renderRows = (renderRows, rows, record, index, indent, fixed, parentKey, ancestorKeys) => { - const { expandedRowClassName, expandedRowRender, childrenColumnName } = this.props - const childrenData = record[childrenColumnName] - const nextAncestorKeys = [...ancestorKeys, parentKey] - const nextIndent = indent + 1 - - if (expandedRowRender) { - rows.push( - this.renderExpandedRow( - record, - index, - expandedRowRender, - expandedRowClassName(record, index, indent), - nextAncestorKeys, - nextIndent, - fixed, - ), - ) - } - - if (childrenData) { - rows.push( - ...renderRows( - childrenData, - nextIndent, - nextAncestorKeys, - ) - ) - } - } - - render () { - const { data, childrenColumnName, children } = this.props - const needIndentSpaced = data.some(record => record[childrenColumnName]) - - return children({ - props: this.props, - needIndentSpaced, - renderRows: this.renderRows, - handleExpandChange: this.handleExpandChange, - renderExpandIndentCell: this.renderExpandIndentCell, - }) - } -} - -export default connect()(ExpandableTable) diff --git a/components/vc-table/src/ExpandableTable.jsx b/components/vc-table/src/ExpandableTable.jsx new file mode 100644 index 000000000..8308699a8 --- /dev/null +++ b/components/vc-table/src/ExpandableTable.jsx @@ -0,0 +1,223 @@ +import PropTypes from '../../_util/vue-types' +import BaseMixin from '../../_util/BaseMixin' +import { connect } from '../../_util/store' +import TableRow from './TableRow' +import { remove } from './utils' +import { initDefaultProps, getOptionProps } from '../../_util/props-util' + +export const ExpandableTableProps = () => ({ + expandIconAsCell: PropTypes.bool, + expandedRowKeys: PropTypes.array, + expandedRowClassName: PropTypes.func, + defaultExpandAllRows: PropTypes.bool, + defaultExpandedRowKeys: PropTypes.array, + expandIconColumnIndex: PropTypes.number, + expandedRowRender: PropTypes.func, + childrenColumnName: PropTypes.string, + indentSize: PropTypes.number, + // onExpand: PropTypes.func, + // onExpandedRowsChange: PropTypes.func, + columnManager: PropTypes.object.isRequired, + store: PropTypes.object.isRequired, + prefixCls: PropTypes.string.isRequired, + data: PropTypes.array, + getRowKey: PropTypes.func, +}) + +const ExpandableTable = { + name: 'ExpandableTable', + mixins: [BaseMixin], + props: initDefaultProps(ExpandableTableProps(), { + expandIconAsCell: false, + expandedRowClassName: () => '', + expandIconColumnIndex: 0, + defaultExpandAllRows: false, + defaultExpandedRowKeys: [], + childrenColumnName: 'children', + indentSize: 15, + }), + + data () { + const { + data, + childrenColumnName, + defaultExpandAllRows, + expandedRowKeys, + defaultExpandedRowKeys, + getRowKey, + } = this + + let finnalExpandedRowKeys = [] + let rows = [...data] + + if (defaultExpandAllRows) { + for (let i = 0; i < rows.length; i++) { + const row = rows[i] + finnalExpandedRowKeys.push(getRowKey(row, i)) + rows = rows.concat(row[childrenColumnName] || []) + } + } else { + finnalExpandedRowKeys = expandedRowKeys || defaultExpandedRowKeys + } + + // this.columnManager = props.columnManager + // this.store = props.store + + this.store.setState({ + expandedRowsHeight: {}, + expandedRowKeys: finnalExpandedRowKeys, + }) + return {} + }, + watch: { + expandedRowKeys (val) { + this.store.setState({ + expandedRowKeys: val, + }) + }, + }, + methods: { + handleExpandChange (expanded, record, event, rowKey, destroy = false) { + if (event) { + event.preventDefault() + event.stopPropagation() + } + + let { expandedRowKeys } = this.store.getState() + + if (expanded) { + // row was expaned + expandedRowKeys = [...expandedRowKeys, rowKey] + } else { + // row was collapse + const expandedRowIndex = expandedRowKeys.indexOf(rowKey) + if (expandedRowIndex !== -1) { + expandedRowKeys = remove(expandedRowKeys, rowKey) + } + } + + if (!this.expandedRowKeys) { + this.store.setState({ expandedRowKeys }) + } + this.__emit('expandedRowsChange', expandedRowKeys) + if (!destroy) { + this.__emit('expand', expanded, record) + } + }, + + renderExpandIndentCell (rows, fixed) { + const { prefixCls, expandIconAsCell } = this + if (!expandIconAsCell || fixed === 'right' || !rows.length) { + return + } + + const iconColumn = { + key: 'rc-table-expand-icon-cell', + className: `${prefixCls}-expand-icon-th`, + title: '', + rowSpan: rows.length, + } + + rows[0].unshift({ ...iconColumn, column: iconColumn }) + }, + + renderExpandedRow (record, index, render, className, ancestorKeys, indent, fixed) { + const { prefixCls, expandIconAsCell, indentSize } = this + let colCount + if (fixed === 'left') { + colCount = this.columnManager.leftLeafColumns().length + } else if (fixed === 'right') { + colCount = this.columnManager.rightLeafColumns().length + } else { + colCount = this.columnManager.leafColumns().length + } + const columns = [{ + key: 'extra-row', + render: () => ({ + props: { + colSpan: colCount, + }, + children: fixed !== 'right' ? render(record, index, indent) : ' ', + }), + }] + if (expandIconAsCell && fixed !== 'right') { + columns.unshift({ + key: 'expand-icon-placeholder', + render: () => null, + }) + } + const parentKey = ancestorKeys[ancestorKeys.length - 1] + const rowKey = `${parentKey}-extra-row` + const components = { + body: { + row: 'tr', + cell: 'td', + }, + } + + return ( + + ) + }, + + renderRows (renderRows, rows, record, index, indent, fixed, parentKey, ancestorKeys) { + const { expandedRowClassName, expandedRowRender, childrenColumnName } = this + const childrenData = record[childrenColumnName] + const nextAncestorKeys = [...ancestorKeys, parentKey] + const nextIndent = indent + 1 + + if (expandedRowRender) { + rows.push( + this.renderExpandedRow( + record, + index, + expandedRowRender, + expandedRowClassName(record, index, indent), + nextAncestorKeys, + nextIndent, + fixed, + ), + ) + } + + if (childrenData) { + rows.push( + ...renderRows( + childrenData, + nextIndent, + nextAncestorKeys, + ) + ) + } + }, + }, + + render () { + const { data, childrenColumnName, $scopedSlots, $listeners } = this + const props = getOptionProps(this) + const needIndentSpaced = data.some(record => record[childrenColumnName]) + + return $scopedSlots.default && $scopedSlots.default({ + props, + on: $listeners, + needIndentSpaced, + renderRows: this.renderRows, + handleExpandChange: this.handleExpandChange, + renderExpandIndentCell: this.renderExpandIndentCell, + }) + }, +} + +export default connect()(ExpandableTable) diff --git a/components/vc-table/src/HeadTable.jsx b/components/vc-table/src/HeadTable.jsx index e2095b364..99c6468f0 100644 --- a/components/vc-table/src/HeadTable.jsx +++ b/components/vc-table/src/HeadTable.jsx @@ -14,6 +14,9 @@ export default { handleBodyScrollLeft: PropTypes.func.isRequired, expander: PropTypes.object.isRequired, }, + inject: { + table: { default: {}}, + }, render () { const { columns, fixed, tableClassName, handleBodyScrollLeft, expander, table } = this const { prefixCls, scroll, showHeader } = table diff --git a/components/vc-table/src/Table.js b/components/vc-table/src/Table.js deleted file mode 100644 index 566e522de..000000000 --- a/components/vc-table/src/Table.js +++ /dev/null @@ -1,475 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import { debounce, warningOnce } from './utils' -import shallowequal from 'shallowequal' -import addEventListener from 'rc-util/lib/Dom/addEventListener' -import { Provider, create } from 'mini-store' -import merge from 'lodash/merge' -import ColumnManager from './ColumnManager' -import classes from 'component-classes' -import HeadTable from './HeadTable' -import BodyTable from './BodyTable' -import ExpandableTable from './ExpandableTable' - -export default class Table extends React.Component { - static propTypes = { - data: PropTypes.array, - useFixedHeader: PropTypes.bool, - columns: PropTypes.array, - prefixCls: PropTypes.string, - bodyStyle: PropTypes.object, - style: PropTypes.object, - rowKey: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), - rowClassName: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), - onRow: PropTypes.func, - onHeaderRow: PropTypes.func, - onRowClick: PropTypes.func, - onRowDoubleClick: PropTypes.func, - onRowContextMenu: PropTypes.func, - onRowMouseEnter: PropTypes.func, - onRowMouseLeave: PropTypes.func, - showHeader: PropTypes.bool, - title: PropTypes.func, - id: PropTypes.string, - footer: PropTypes.func, - emptyText: PropTypes.oneOfType([PropTypes.node, PropTypes.func]), - scroll: PropTypes.object, - rowRef: PropTypes.func, - getBodyWrapper: PropTypes.func, - children: PropTypes.node, - components: PropTypes.shape({ - table: PropTypes.any, - header: PropTypes.shape({ - wrapper: PropTypes.any, - row: PropTypes.any, - cell: PropTypes.any, - }), - body: PropTypes.shape({ - wrapper: PropTypes.any, - row: PropTypes.any, - cell: PropTypes.any, - }), - }), - ...ExpandableTable.PropTypes, - } - - static childContextTypes = { - table: PropTypes.any, - components: PropTypes.any, - } - - static defaultProps = { - data: [], - useFixedHeader: false, - rowKey: 'key', - rowClassName: () => '', - onRow () {}, - onHeaderRow () {}, - prefixCls: 'rc-table', - bodyStyle: {}, - style: {}, - showHeader: true, - scroll: {}, - rowRef: () => null, - emptyText: () => 'No Data', - } - - constructor (props) { - super(props); - - [ - 'onRowClick', - 'onRowDoubleClick', - 'onRowContextMenu', - 'onRowMouseEnter', - 'onRowMouseLeave', - ].forEach(name => { - warningOnce( - props[name] === undefined, - `${name} is deprecated, please use onRow instead.`, - ) - }) - - warningOnce( - props.getBodyWrapper === undefined, - 'getBodyWrapper is deprecated, please use custom components instead.', - ) - - this.columnManager = new ColumnManager(props.columns, props.children) - - this.store = create({ - currentHoverKey: null, - fixedColumnsHeadRowsHeight: [], - fixedColumnsBodyRowsHeight: [], - }) - - this.setScrollPosition('left') - - this.debouncedWindowResize = debounce(this.handleWindowResize, 150) - } - - getChildContext () { - return { - table: { - props: this.props, - columnManager: this.columnManager, - saveRef: this.saveRef, - components: merge({ - table: 'table', - header: { - wrapper: 'thead', - row: 'tr', - cell: 'th', - }, - body: { - wrapper: 'tbody', - row: 'tr', - cell: 'td', - }, - }, this.props.components), - }, - } - } - - componentDidMount () { - if (this.columnManager.isAnyColumnsFixed()) { - this.handleWindowResize() - this.resizeEvent = addEventListener( - window, 'resize', this.debouncedWindowResize - ) - } - } - - componentWillReceiveProps (nextProps) { - if (nextProps.columns && nextProps.columns !== this.props.columns) { - this.columnManager.reset(nextProps.columns) - } else if (nextProps.children !== this.props.children) { - this.columnManager.reset(null, nextProps.children) - } - } - - componentDidUpdate (prevProps) { - if (this.columnManager.isAnyColumnsFixed()) { - this.handleWindowResize() - if (!this.resizeEvent) { - this.resizeEvent = addEventListener( - window, 'resize', this.debouncedWindowResize - ) - } - } - // when table changes to empty, reset scrollLeft - if (prevProps.data.length > 0 && this.props.data.length === 0 && this.hasScrollX()) { - this.resetScrollX() - } - } - - componentWillUnmount () { - if (this.resizeEvent) { - this.resizeEvent.remove() - } - if (this.debouncedWindowResize) { - this.debouncedWindowResize.cancel() - } - } - - getRowKey = (record, index) => { - const rowKey = this.props.rowKey - const key = (typeof rowKey === 'function') - ? rowKey(record, index) : record[rowKey] - warningOnce( - key !== undefined, - 'Each record in table should have a unique `key` prop,' + - 'or set `rowKey` to an unique primary key.' - ) - return key === undefined ? index : key - } - - setScrollPosition (position) { - this.scrollPosition = position - if (this.tableNode) { - const { prefixCls } = this.props - if (position === 'both') { - classes(this.tableNode) - .remove(new RegExp(`^${prefixCls}-scroll-position-.+$`)) - .add(`${prefixCls}-scroll-position-left`) - .add(`${prefixCls}-scroll-position-right`) - } else { - classes(this.tableNode) - .remove(new RegExp(`^${prefixCls}-scroll-position-.+$`)) - .add(`${prefixCls}-scroll-position-${position}`) - } - } - } - - setScrollPositionClassName () { - const node = this.bodyTable - const scrollToLeft = node.scrollLeft === 0 - const scrollToRight = node.scrollLeft + 1 >= - node.children[0].getBoundingClientRect().width - - node.getBoundingClientRect().width - if (scrollToLeft && scrollToRight) { - this.setScrollPosition('both') - } else if (scrollToLeft) { - this.setScrollPosition('left') - } else if (scrollToRight) { - this.setScrollPosition('right') - } else if (this.scrollPosition !== 'middle') { - this.setScrollPosition('middle') - } - } - - handleWindowResize = () => { - this.syncFixedTableRowHeight() - this.setScrollPositionClassName() - } - - syncFixedTableRowHeight = () => { - const tableRect = this.tableNode.getBoundingClientRect() - // If tableNode's height less than 0, suppose it is hidden and don't recalculate rowHeight. - // see: https://github.com/ant-design/ant-design/issues/4836 - if (tableRect.height !== undefined && tableRect.height <= 0) { - return - } - const { prefixCls } = this.props - const headRows = this.headTable - ? this.headTable.querySelectorAll('thead') - : this.bodyTable.querySelectorAll('thead') - const bodyRows = this.bodyTable.querySelectorAll(`.${prefixCls}-row`) || [] - const fixedColumnsHeadRowsHeight = [].map.call( - headRows, row => row.getBoundingClientRect().height || 'auto' - ) - const fixedColumnsBodyRowsHeight = [].map.call( - bodyRows, row => row.getBoundingClientRect().height || 'auto' - ) - const state = this.store.getState() - if (shallowequal(state.fixedColumnsHeadRowsHeight, fixedColumnsHeadRowsHeight) && - shallowequal(state.fixedColumnsBodyRowsHeight, fixedColumnsBodyRowsHeight)) { - return - } - - this.store.setState({ - fixedColumnsHeadRowsHeight, - fixedColumnsBodyRowsHeight, - }) - } - - resetScrollX () { - if (this.headTable) { - this.headTable.scrollLeft = 0 - } - if (this.bodyTable) { - this.bodyTable.scrollLeft = 0 - } - } - - hasScrollX () { - const { scroll = {}} = this.props - return 'x' in scroll - } - - handleBodyScrollLeft = (e) => { - // Fix https://github.com/ant-design/ant-design/issues/7635 - if (e.currentTarget !== e.target) { - return - } - const target = e.target - const { scroll = {}} = this.props - const { headTable, bodyTable } = this - if (target.scrollLeft !== this.lastScrollLeft && scroll.x) { - if (target === bodyTable && headTable) { - headTable.scrollLeft = target.scrollLeft - } else if (target === headTable && bodyTable) { - bodyTable.scrollLeft = target.scrollLeft - } - this.setScrollPositionClassName() - } - // Remember last scrollLeft for scroll direction detecting. - this.lastScrollLeft = target.scrollLeft - } - - handleBodyScrollTop = (e) => { - const target = e.target - const { scroll = {}} = this.props - const { headTable, bodyTable, fixedColumnsBodyLeft, fixedColumnsBodyRight } = this - if (target.scrollTop !== this.lastScrollTop && scroll.y && target !== headTable) { - const scrollTop = target.scrollTop - if (fixedColumnsBodyLeft && target !== fixedColumnsBodyLeft) { - fixedColumnsBodyLeft.scrollTop = scrollTop - } - if (fixedColumnsBodyRight && target !== fixedColumnsBodyRight) { - fixedColumnsBodyRight.scrollTop = scrollTop - } - if (bodyTable && target !== bodyTable) { - bodyTable.scrollTop = scrollTop - } - } - // Remember last scrollTop for scroll direction detecting. - this.lastScrollTop = target.scrollTop - } - - handleBodyScroll = (e) => { - this.handleBodyScrollLeft(e) - this.handleBodyScrollTop(e) - } - - saveRef = (name) => (node) => { - this[name] = node - } - - renderMainTable () { - const { scroll, prefixCls } = this.props - const isAnyColumnsFixed = this.columnManager.isAnyColumnsFixed() - const scrollable = isAnyColumnsFixed || scroll.x || scroll.y - - const table = [ - this.renderTable({ - columns: this.columnManager.groupedColumns(), - isAnyColumnsFixed, - }), - this.renderEmptyText(), - this.renderFooter(), - ] - - return scrollable ? ( -
    {table}
    - ) : table - } - - renderLeftFixedTable () { - const { prefixCls } = this.props - - return ( -
    - {this.renderTable({ - columns: this.columnManager.leftColumns(), - fixed: 'left', - })} -
    - ) - } - - renderRightFixedTable () { - const { prefixCls } = this.props - - return ( -
    - {this.renderTable({ - columns: this.columnManager.rightColumns(), - fixed: 'right', - })} -
    - ) - } - - renderTable (options) { - const { columns, fixed, isAnyColumnsFixed } = options - const { prefixCls, scroll = {}} = this.props - const tableClassName = (scroll.x || fixed) ? `${prefixCls}-fixed` : '' - - const headTable = ( - - ) - - const bodyTable = ( - - ) - - return [headTable, bodyTable] - } - - renderTitle () { - const { title, prefixCls } = this.props - return title ? ( -
    - {title(this.props.data)} -
    - ) : null - } - - renderFooter () { - const { footer, prefixCls } = this.props - return footer ? ( -
    - {footer(this.props.data)} -
    - ) : null - } - - renderEmptyText () { - const { emptyText, prefixCls, data } = this.props - if (data.length) { - return null - } - const emptyClassName = `${prefixCls}-placeholder` - return ( -
    - {(typeof emptyText === 'function') ? emptyText() : emptyText} -
    - ) - } - - render () { - const props = this.props - const prefixCls = props.prefixCls - - let className = props.prefixCls - if (props.className) { - className += ` ${props.className}` - } - if (props.useFixedHeader || (props.scroll && props.scroll.y)) { - className += ` ${prefixCls}-fixed-header` - } - if (this.scrollPosition === 'both') { - className += ` ${prefixCls}-scroll-position-left ${prefixCls}-scroll-position-right` - } else { - className += ` ${prefixCls}-scroll-position-${this.scrollPosition}` - } - const hasLeftFixed = this.columnManager.isAnyColumnsLeftFixed() - const hasRightFixed = this.columnManager.isAnyColumnsRightFixed() - - return ( - - - {(expander) => { - this.expander = expander - return ( -
    - {this.renderTitle()} -
    - {this.renderMainTable()} - {hasLeftFixed && this.renderLeftFixedTable()} - {hasRightFixed && this.renderRightFixedTable()} -
    -
    - ) - }} -
    -
    - ) - } -} diff --git a/components/vc-table/src/Table.jsx b/components/vc-table/src/Table.jsx new file mode 100644 index 000000000..84217873a --- /dev/null +++ b/components/vc-table/src/Table.jsx @@ -0,0 +1,507 @@ + +import PropTypes from '../../_util/vue-types' +import { debounce, warningOnce } from './utils' +import shallowequal from 'shallowequal' +import addEventListener from '../../_util/Dom/addEventListener' +import { Provider, create } from '../../_util/store' +import merge from 'lodash/merge' +import ColumnManager from './ColumnManager' +import classes from 'component-classes' +import HeadTable from './HeadTable' +import BodyTable from './BodyTable' +import ExpandableTable from './ExpandableTable' +import { initDefaultProps, getOptionProps } from '../../_util/props-util' +import BaseMixin from '../../_util/BaseMixin' + +export default { + name: 'Table', + mixins: [BaseMixin], + props: initDefaultProps({ + data: PropTypes.array, + useFixedHeader: PropTypes.bool, + columns: PropTypes.array, + prefixCls: PropTypes.string, + bodyStyle: PropTypes.object, + rowKey: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), + rowClassName: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), + // onRow: PropTypes.func, + // onHeaderRow: PropTypes.func, + // onRowClick: PropTypes.func, + // onRowDoubleClick: PropTypes.func, + // onRowContextMenu: PropTypes.func, + // onRowMouseEnter: PropTypes.func, + // onRowMouseLeave: PropTypes.func, + showHeader: PropTypes.bool, + title: PropTypes.func, + id: PropTypes.string, + footer: PropTypes.func, + emptyText: PropTypes.any, + scroll: PropTypes.object, + rowRef: PropTypes.func, + getBodyWrapper: PropTypes.func, + components: PropTypes.shape({ + table: PropTypes.any, + header: PropTypes.shape({ + wrapper: PropTypes.any, + row: PropTypes.any, + cell: PropTypes.any, + }), + body: PropTypes.shape({ + wrapper: PropTypes.any, + row: PropTypes.any, + cell: PropTypes.any, + }), + }), + expandIconAsCell: PropTypes.bool, + expandedRowKeys: PropTypes.array, + expandedRowClassName: PropTypes.func, + defaultExpandAllRows: PropTypes.bool, + defaultExpandedRowKeys: PropTypes.array, + expandIconColumnIndex: PropTypes.number, + expandedRowRender: PropTypes.func, + childrenColumnName: PropTypes.string, + indentSize: PropTypes.number, + }, { + data: [], + useFixedHeader: false, + rowKey: 'key', + rowClassName: () => '', + prefixCls: 'rc-table', + bodyStyle: {}, + showHeader: true, + scroll: {}, + rowRef: () => null, + emptyText: () => 'No Data', + }), + + // static childContextTypes = { + // table: PropTypes.any, + // components: PropTypes.any, + // }, + + created () { + [ + 'rowClick', + 'rowDoubleclick', + 'rowContextmenu', + 'rowMouseenter', + 'rowMouseleave', + ].forEach(name => { + warningOnce( + this.$listeners[name] === undefined, + `${name} is deprecated, please use onRow instead.`, + ) + }) + + warningOnce( + this.getBodyWrapper === undefined, + 'getBodyWrapper is deprecated, please use custom components instead.', + ) + + // this.columnManager = new ColumnManager(this.columns, this.$slots.default) + + this.store = create({ + currentHoverKey: null, + fixedColumnsHeadRowsHeight: [], + fixedColumnsBodyRowsHeight: [], + }) + + this.setScrollPosition('left') + + this.debouncedWindowResize = debounce(this.handleWindowResize, 150) + }, + data () { + this.preData = [...this.data] + return { + columnManager: new ColumnManager(this.columns, this.$slots.default), + sComponents: merge({ + table: 'table', + header: { + wrapper: 'thead', + row: 'tr', + cell: 'th', + }, + body: { + wrapper: 'tbody', + row: 'tr', + cell: 'td', + }, + }, this.components), + } + }, + provide () { + return { + table: this, + } + }, + watch: { + components (val) { + this._components = merge({ + table: 'table', + header: { + wrapper: 'thead', + row: 'tr', + cell: 'th', + }, + body: { + wrapper: 'tbody', + row: 'tr', + cell: 'td', + }, + }, this.components) + }, + columns (val) { + if (val) { + this.columnManager.reset(val) + } + }, + data (val) { + if (val.length === 0 && this.hasScrollX()) { + this.$nextTick(() => { + this.resetScrollX() + }) + } + }, + }, + + mounted () { + this.$nextTick(() => { + if (this.columnManager.isAnyColumnsFixed()) { + this.handleWindowResize() + this.resizeEvent = addEventListener( + window, 'resize', this.debouncedWindowResize + ) + } + }) + }, + + componentWillReceiveProps (nextProps) { + if (nextProps.columns && nextProps.columns !== this.props.columns) { + this.columnManager.reset(nextProps.columns) + } else if (nextProps.children !== this.props.children) { + this.columnManager.reset(null, nextProps.children) + } + }, + + updated (prevProps) { + if (this.columnManager.isAnyColumnsFixed()) { + this.handleWindowResize() + if (!this.resizeEvent) { + this.resizeEvent = addEventListener( + window, 'resize', this.debouncedWindowResize + ) + } + } + }, + + beforeDestroy () { + if (this.resizeEvent) { + this.resizeEvent.remove() + } + if (this.debouncedWindowResize) { + this.debouncedWindowResize.cancel() + } + }, + methods: { + getRowKey (record, index) { + const rowKey = this.rowKey + const key = (typeof rowKey === 'function') + ? rowKey(record, index) : record[rowKey] + warningOnce( + key !== undefined, + 'Each record in table should have a unique `key` prop,' + + 'or set `rowKey` to an unique primary key.' + ) + return key === undefined ? index : key + }, + + setScrollPosition (position) { + this.scrollPosition = position + if (this.tableNode) { + const { prefixCls } = this + if (position === 'both') { + classes(this.tableNode) + .remove(new RegExp(`^${prefixCls}-scroll-position-.+$`)) + .add(`${prefixCls}-scroll-position-left`) + .add(`${prefixCls}-scroll-position-right`) + } else { + classes(this.tableNode) + .remove(new RegExp(`^${prefixCls}-scroll-position-.+$`)) + .add(`${prefixCls}-scroll-position-${position}`) + } + } + }, + + setScrollPositionClassName () { + const node = this.bodyTable + const scrollToLeft = node.scrollLeft === 0 + const scrollToRight = node.scrollLeft + 1 >= + node.children[0].getBoundingClientRect().width - + node.getBoundingClientRect().width + if (scrollToLeft && scrollToRight) { + this.setScrollPosition('both') + } else if (scrollToLeft) { + this.setScrollPosition('left') + } else if (scrollToRight) { + this.setScrollPosition('right') + } else if (this.scrollPosition !== 'middle') { + this.setScrollPosition('middle') + } + }, + + handleWindowResize () { + this.syncFixedTableRowHeight() + this.setScrollPositionClassName() + }, + + syncFixedTableRowHeight () { + const tableRect = this.tableNode.getBoundingClientRect() + // If tableNode's height less than 0, suppose it is hidden and don't recalculate rowHeight. + // see: https://github.com/ant-design/ant-design/issues/4836 + if (tableRect.height !== undefined && tableRect.height <= 0) { + return + } + const { prefixCls } = this.props + const headRows = this.headTable + ? this.headTable.querySelectorAll('thead') + : this.bodyTable.querySelectorAll('thead') + const bodyRows = this.bodyTable.querySelectorAll(`.${prefixCls}-row`) || [] + const fixedColumnsHeadRowsHeight = [].map.call( + headRows, row => row.getBoundingClientRect().height || 'auto' + ) + const fixedColumnsBodyRowsHeight = [].map.call( + bodyRows, row => row.getBoundingClientRect().height || 'auto' + ) + const state = this.store.getState() + if (shallowequal(state.fixedColumnsHeadRowsHeight, fixedColumnsHeadRowsHeight) && + shallowequal(state.fixedColumnsBodyRowsHeight, fixedColumnsBodyRowsHeight)) { + return + } + + this.store.setState({ + fixedColumnsHeadRowsHeight, + fixedColumnsBodyRowsHeight, + }) + }, + + resetScrollX () { + if (this.headTable) { + this.headTable.scrollLeft = 0 + } + if (this.bodyTable) { + this.bodyTable.scrollLeft = 0 + } + }, + + hasScrollX () { + const { scroll = {}} = this + return 'x' in scroll + }, + + handleBodyScrollLeft (e) { + // Fix https://github.com/ant-design/ant-design/issues/7635 + if (e.currentTarget !== e.target) { + return + } + const target = e.target + const { scroll = {}} = this + const { headTable, bodyTable } = this + if (target.scrollLeft !== this.lastScrollLeft && scroll.x) { + if (target === bodyTable && headTable) { + headTable.scrollLeft = target.scrollLeft + } else if (target === headTable && bodyTable) { + bodyTable.scrollLeft = target.scrollLeft + } + this.setScrollPositionClassName() + } + // Remember last scrollLeft for scroll direction detecting. + this.lastScrollLeft = target.scrollLeft + }, + + handleBodyScrollTop (e) { + const target = e.target + const { scroll = {}} = this + const { headTable, bodyTable, fixedColumnsBodyLeft, fixedColumnsBodyRight } = this + if (target.scrollTop !== this.lastScrollTop && scroll.y && target !== headTable) { + const scrollTop = target.scrollTop + if (fixedColumnsBodyLeft && target !== fixedColumnsBodyLeft) { + fixedColumnsBodyLeft.scrollTop = scrollTop + } + if (fixedColumnsBodyRight && target !== fixedColumnsBodyRight) { + fixedColumnsBodyRight.scrollTop = scrollTop + } + if (bodyTable && target !== bodyTable) { + bodyTable.scrollTop = scrollTop + } + } + // Remember last scrollTop for scroll direction detecting. + this.lastScrollTop = target.scrollTop + }, + + handleBodyScroll (e) { + this.handleBodyScrollLeft(e) + this.handleBodyScrollTop(e) + }, + + renderMainTable () { + const { scroll, prefixCls } = this + const isAnyColumnsFixed = this.columnManager.isAnyColumnsFixed() + const scrollable = isAnyColumnsFixed || scroll.x || scroll.y + + const table = [ + this.renderTable({ + columns: this.columnManager.groupedColumns(), + isAnyColumnsFixed, + }), + this.renderEmptyText(), + this.renderFooter(), + ] + + return scrollable ? ( +
    {table}
    + ) : table + }, + + renderLeftFixedTable () { + const { prefixCls } = this + + return ( +
    + {this.renderTable({ + columns: this.columnManager.leftColumns(), + fixed: 'left', + })} +
    + ) + }, + renderRightFixedTable () { + const { prefixCls } = this + + return ( +
    + {this.renderTable({ + columns: this.columnManager.rightColumns(), + fixed: 'right', + })} +
    + ) + }, + + renderTable (options) { + const { columns, fixed, isAnyColumnsFixed } = options + const { prefixCls, scroll = {}} = this + const tableClassName = (scroll.x || fixed) ? `${prefixCls}-fixed` : '' + + const headTable = ( + + ) + + const bodyTable = ( + + ) + + return [headTable, bodyTable] + }, + + renderTitle () { + const { title, prefixCls } = this + return title ? ( +
    + {title(this.props.data)} +
    + ) : null + }, + + renderFooter () { + const { footer, prefixCls } = this + return footer ? ( +
    + {footer(this.props.data)} +
    + ) : null + }, + + renderEmptyText () { + const { emptyText, prefixCls, data } = this + if (data.length) { + return null + } + const emptyClassName = `${prefixCls}-placeholder` + return ( +
    + {(typeof emptyText === 'function') ? emptyText() : emptyText} +
    + ) + }, + }, + + render () { + const props = getOptionProps(this) + const { $listeners, columnManager, getRowKey } = this + const prefixCls = props.prefixCls + + let className = props.prefixCls + if (props.useFixedHeader || (props.scroll && props.scroll.y)) { + className += ` ${prefixCls}-fixed-header` + } + if (this.scrollPosition === 'both') { + className += ` ${prefixCls}-scroll-position-left ${prefixCls}-scroll-position-right` + } else { + className += ` ${prefixCls}-scroll-position-${this.scrollPosition}` + } + const hasLeftFixed = columnManager.isAnyColumnsLeftFixed() + const hasRightFixed = columnManager.isAnyColumnsRightFixed() + + const expandableTableProps = { + props: { + ...props, + columnManager, + getRowKey, + }, + on: { ...$listeners }, + scopedSlots: { + default: (expander) => { + this.expander = expander + return ( +
    + {this.renderTitle()} +
    + {this.renderMainTable()} + {hasLeftFixed && this.renderLeftFixedTable()} + {hasRightFixed && this.renderRightFixedTable()} +
    +
    + ) + }, + }, + } + return ( + + + + ) + }, +} diff --git a/components/vc-table/src/TableCell.jsx b/components/vc-table/src/TableCell.jsx index 41bccae5e..526f40ce9 100644 --- a/components/vc-table/src/TableCell.jsx +++ b/components/vc-table/src/TableCell.jsx @@ -10,12 +10,12 @@ export default { indent: PropTypes.number, indentSize: PropTypes.number, column: PropTypes.object, - expandIcon: PropTypes.node, + expandIcon: PropTypes.any, component: PropTypes.any, }, methods: { isInvalidRenderCellText (text) { - debugger + // debugger return text && Object.prototype.toString.call(text) === '[object Object]' }, diff --git a/components/vc-table/src/TableHeader.jsx b/components/vc-table/src/TableHeader.jsx index b0a1be40f..9e6271b8e 100644 --- a/components/vc-table/src/TableHeader.jsx +++ b/components/vc-table/src/TableHeader.jsx @@ -41,6 +41,9 @@ export default { expander: PropTypes.object.isRequired, }, + inject: { + table: { default: {}}, + }, methods: { onHeaderRow () { this.table.__emit('headerRow', ...arguments) @@ -48,7 +51,7 @@ export default { }, render () { - const { components, prefixCls, showHeader } = this.table + const { sComponents: components, prefixCls, showHeader } = this.table const { expander, columns, fixed, onHeaderRow } = this if (!showHeader) { diff --git a/components/vc-table/src/TableHeaderRow.js b/components/vc-table/src/TableHeaderRow.js deleted file mode 100644 index d801ba46d..000000000 --- a/components/vc-table/src/TableHeaderRow.js +++ /dev/null @@ -1,53 +0,0 @@ -import React from 'react' -import { connect } from 'mini-store' - -function TableHeaderRow ({ row, index, height, components, onHeaderRow }) { - const HeaderRow = components.header.row - const HeaderCell = components.header.cell - const rowProps = onHeaderRow(row.map(cell => cell.column), index) - const customStyle = rowProps ? rowProps.style : {} - const style = { height, ...customStyle } - - return ( - - {row.map((cell, i) => { - const { column, ...cellProps } = cell - const customProps = column.onHeaderCell ? column.onHeaderCell(column) : {} - if (column.align) { - cellProps.style = { textAlign: column.align } - } - return ( - - ) - })} - - ) -} - -function getRowHeight (state, props) { - const { fixedColumnsHeadRowsHeight } = state - const { columns, rows, fixed } = props - const headerHeight = fixedColumnsHeadRowsHeight[0] - - if (!fixed) { - return null - } - - if (headerHeight && columns) { - if (headerHeight === 'auto') { - return 'auto' - } - return headerHeight / rows.length - } - return null -} - -export default connect((state, props) => { - return { - height: getRowHeight(state, props), - } -})(TableHeaderRow) diff --git a/components/vc-table/src/TableHeaderRow.jsx b/components/vc-table/src/TableHeaderRow.jsx new file mode 100644 index 000000000..6e23182a1 --- /dev/null +++ b/components/vc-table/src/TableHeaderRow.jsx @@ -0,0 +1,71 @@ +import PropTypes from '../../_util/vue-types' +import { connect } from '../../_util/store' + +const TableHeaderRow = { + props: { + index: PropTypes.number, + fixed: PropTypes.string, + columns: PropTypes.array, + rows: PropTypes.array, + row: PropTypes.array, + components: PropTypes.object, + height: PropTypes.any, + }, + name: 'TableHeaderRow', + render () { + const { row, index, height, components, $listeners = {}} = this + const onHeaderRow = $listeners.headerRow + const HeaderRow = components.header.row + const HeaderCell = components.header.cell + const rowProps = onHeaderRow(row.map(cell => cell.column), index) + const customStyle = rowProps ? rowProps.style : {} + const style = { height, ...customStyle } + + return ( + + {row.map((cell, i) => { + const { column, children, className, ...cellProps } = cell + const customProps = column.onHeaderCell ? column.onHeaderCell(column) : {} + if (column.align) { + cellProps.style = { textAlign: column.align } + } + + return ( + + {children} + + ) + })} + + ) + }, +} + +function getRowHeight (state, props) { + const { fixedColumnsHeadRowsHeight } = state + const { columns, rows, fixed } = props + const headerHeight = fixedColumnsHeadRowsHeight[0] + + if (!fixed) { + return null + } + + if (headerHeight && columns) { + if (headerHeight === 'auto') { + return 'auto' + } + return `${headerHeight / rows.length}px` + } + return null +} + +export default connect((state, props) => { + return { + height: getRowHeight(state, props), + } +})(TableHeaderRow) diff --git a/components/vc-table/src/TableRow.js b/components/vc-table/src/TableRow.jsx similarity index 52% rename from components/vc-table/src/TableRow.js rename to components/vc-table/src/TableRow.jsx index 3348bb559..1ea693648 100644 --- a/components/vc-table/src/TableRow.js +++ b/components/vc-table/src/TableRow.jsx @@ -1,21 +1,23 @@ -import React from 'react' -import ReactDOM from 'react-dom' -import PropTypes from 'prop-types' -import { connect } from 'mini-store' +import PropTypes from '../../_util/vue-types' +import { connect } from '../../_util/store' import TableCell from './TableCell' import { warningOnce } from './utils' +import { initDefaultProps } from '../../_util/props-util' +import BaseMixin from '../../_util/BaseMixin' -class TableRow extends React.Component { - static propTypes = { - onRow: PropTypes.func, - onRowClick: PropTypes.func, - onRowDoubleClick: PropTypes.func, - onRowContextMenu: PropTypes.func, - onRowMouseEnter: PropTypes.func, - onRowMouseLeave: PropTypes.func, +const TableRow = { + name: 'TableRow', + mixins: [BaseMixin], + props: initDefaultProps({ + // onRow: PropTypes.func, + // onRowClick: PropTypes.func, + // onRowDoubleClick: PropTypes.func, + // onRowContextMenu: PropTypes.func, + // onRowMouseEnter: PropTypes.func, + // onRowMouseLeave: PropTypes.func, record: PropTypes.object, prefixCls: PropTypes.string, - onHover: PropTypes.func, + // onHover: PropTypes.func, columns: PropTypes.array, height: PropTypes.oneOfType([ PropTypes.string, @@ -43,133 +45,131 @@ class TableRow extends React.Component { expandedRow: PropTypes.bool, isAnyColumnsFixed: PropTypes.bool, ancestorKeys: PropTypes.array.isRequired, - } - - static defaultProps = { - onRow () {}, + expandIconColumnIndex: PropTypes.number, + expandRowByClick: PropTypes.bool, + // visible: PropTypes.bool, + // hovered: PropTypes.bool, + // height: PropTypes.any, + }, { expandIconColumnIndex: 0, expandRowByClick: false, - onHover () {}, hasExpandIcon () {}, renderExpandIcon () {}, renderExpandIconCell () {}, - } + }), - constructor (props) { - super(props) + data () { + this.shouldRender = this.visible + return {} + }, - this.shouldRender = props.visible - } - - componentDidMount () { + mounted () { if (this.shouldRender) { - this.saveRowRef() + this.$nextTick(() => { + this.saveRowRef() + }) } - } + }, + watch: { + + }, componentWillReceiveProps (nextProps) { if (this.props.visible || (!this.props.visible && nextProps.visible)) { this.shouldRender = true } - } + }, shouldComponentUpdate (nextProps) { return !!(this.props.visible || nextProps.visible) - } + }, - componentDidUpdate () { + updated () { if (this.shouldRender && !this.rowRef) { - this.saveRowRef() + this.$nextTick(() => { + this.saveRowRef() + }) } - } + }, + methods: { + onRowClick (event) { + const { record, index } = this + this.__emit('rowClick', record, index, event) + }, - onRowClick = (event) => { - const { record, index, onRowClick } = this.props - if (onRowClick) { - onRowClick(record, index, event) - } - } + onRowDoubleClick (event) { + const { record, index } = this + this.__emit('rowDoubleClick', record, index, event) + }, - onRowDoubleClick = (event) => { - const { record, index, onRowDoubleClick } = this.props - if (onRowDoubleClick) { - onRowDoubleClick(record, index, event) - } - } + onContextMenu (event) { + const { record, index } = this + this.__emit('rowContextmenu', record, index, event) + }, - onContextMenu = (event) => { - const { record, index, onRowContextMenu } = this.props - if (onRowContextMenu) { - onRowContextMenu(record, index, event) - } - } + onMouseEnter (event) { + const { record, index, rowKey } = this + this.__emit('hover', true, rowKey) + this.__emit('rowMouseenter', record, index, event) + }, - onMouseEnter = (event) => { - const { record, index, onRowMouseEnter, onHover, rowKey } = this.props - onHover(true, rowKey) - if (onRowMouseEnter) { - onRowMouseEnter(record, index, event) - } - } + onMouseLeave (event) { + const { record, index, rowKey } = this + this.__emit('hover', false, rowKey) + this.__emit('rowMouseleave', record, index, event) + }, - onMouseLeave = (event) => { - const { record, index, onRowMouseLeave, onHover, rowKey } = this.props - onHover(false, rowKey) - if (onRowMouseLeave) { - onRowMouseLeave(record, index, event) - } - } + setExpanedRowHeight () { + const { store, rowKey } = this + let { expandedRowsHeight } = store.getState() + const height = this.rowRef.getBoundingClientRect().height + expandedRowsHeight = { + ...expandedRowsHeight, + [rowKey]: height, + } + store.setState({ expandedRowsHeight }) + }, - setExpanedRowHeight () { - const { store, rowKey } = this.props - let { expandedRowsHeight } = store.getState() - const height = this.rowRef.getBoundingClientRect().height - expandedRowsHeight = { - ...expandedRowsHeight, - [rowKey]: height, - } - store.setState({ expandedRowsHeight }) - } + setRowHeight () { + const { store, index } = this + const fixedColumnsBodyRowsHeight = store.getState().fixedColumnsBodyRowsHeight.slice() + const height = this.rowRef.getBoundingClientRect().height + fixedColumnsBodyRowsHeight[index] = height + store.setState({ fixedColumnsBodyRowsHeight }) + }, - setRowHeight () { - const { store, index } = this.props - const fixedColumnsBodyRowsHeight = store.getState().fixedColumnsBodyRowsHeight.slice() - const height = this.rowRef.getBoundingClientRect().height - fixedColumnsBodyRowsHeight[index] = height - store.setState({ fixedColumnsBodyRowsHeight }) - } + getStyle () { + const { height, visible } = this - getStyle () { - const { height, visible } = this.props + if (height && height !== this.style.height) { + this.style = { ...this.style, height } + } - if (height && height !== this.style.height) { - this.style = { ...this.style, height } - } + if (!visible && !this.style.display) { + this.style = { ...this.style, display: 'none' } + } - if (!visible && !this.style.display) { - this.style = { ...this.style, display: 'none' } - } + return this.style + }, - return this.style - } + saveRowRef () { + this.rowRef = this.$el - saveRowRef () { - this.rowRef = ReactDOM.findDOMNode(this) + const { isAnyColumnsFixed, fixed, expandedRow, ancestorKeys } = this - const { isAnyColumnsFixed, fixed, expandedRow, ancestorKeys } = this.props + if (!isAnyColumnsFixed) { + return + } - if (!isAnyColumnsFixed) { - return - } + if (!fixed && expandedRow) { + this.setExpanedRowHeight() + } - if (!fixed && expandedRow) { - this.setExpanedRowHeight() - } - - if (!fixed && ancestorKeys.length >= 0) { - this.setRowHeight() - } - } + if (!fixed && ancestorKeys.length >= 0) { + this.setRowHeight() + } + }, + }, render () { if (!this.shouldRender) { @@ -181,7 +181,7 @@ class TableRow extends React.Component { columns, record, index, - onRow, + // onRow, indent, indentSize, hovered, @@ -191,12 +191,13 @@ class TableRow extends React.Component { hasExpandIcon, renderExpandIcon, renderExpandIconCell, - } = this.props - + $listeners, + } = this + const { row: onRow } = $listeners const BodyRow = components.body.row const BodyCell = components.body.cell - let { className } = this.props + let className = '' if (hovered) { className += ` ${prefixCls}-hover` @@ -241,22 +242,22 @@ class TableRow extends React.Component { } style = { ...style, ...customStyle } - + console.log('rowProps', rowProps) return ( {cells} ) - } + }, } function getRowHeight (state, props) { diff --git a/examples/routes.js b/examples/routes.js index 41b6a33fc..9ea148250 100644 --- a/examples/routes.js +++ b/examples/routes.js @@ -3,7 +3,7 @@ const AsyncComp = () => { const hashs = window.location.hash.split('/') const d = hashs[hashs.length - 1] return { - component: import(`../components/vc-tree/demo/${d}`), + component: import(`../components/vc-table/demo/${d}`), } } export default [ diff --git a/package-lock.json b/package-lock.json index be35897c4..b2cdd9e77 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "vue-antd-ui", - "version": "0.1.0", + "version": "0.1.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -206,7 +206,7 @@ }, "abab": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/abab/-/abab-1.0.4.tgz", + "resolved": "http://registry.npm.taobao.org/abab/download/abab-1.0.4.tgz", "integrity": "sha1-X6rZwsB/YN12dw9xzwJbYqY8/U4=", "dev": true, "optional": true @@ -251,7 +251,7 @@ }, "acorn-globals": { "version": "1.0.9", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-1.0.9.tgz", + "resolved": "http://registry.npm.taobao.org/acorn-globals/download/acorn-globals-1.0.9.tgz", "integrity": "sha1-VbtemGkVB7dFedBRNBMhfDgMVM8=", "dev": true, "optional": true, @@ -261,7 +261,7 @@ "dependencies": { "acorn": { "version": "2.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-2.7.0.tgz", + "resolved": "http://registry.npm.taobao.org/acorn/download/acorn-2.7.0.tgz", "integrity": "sha1-q259nYhqrKiwhbwzEreaGYQz8Oc=", "dev": true, "optional": true @@ -2491,7 +2491,7 @@ }, "component-classes": { "version": "1.2.6", - "resolved": "http://registry.npm.taobao.org/component-classes/download/component-classes-1.2.6.tgz", + "resolved": "https://registry.npmjs.org/component-classes/-/component-classes-1.2.6.tgz", "integrity": "sha1-xkI5TDYYpNiwuJGe/Mu9kw5c1pE=", "requires": { "component-indexof": "0.0.3" @@ -3086,13 +3086,13 @@ }, "cssom": { "version": "0.3.2", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.2.tgz", + "resolved": "http://registry.npm.taobao.org/cssom/download/cssom-0.3.2.tgz", "integrity": "sha1-uANhcMefB6kP8vFuIihAJ6JDhIs=", "dev": true }, "cssstyle": { "version": "0.2.37", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-0.2.37.tgz", + "resolved": "http://registry.npm.taobao.org/cssstyle/download/cssstyle-0.2.37.tgz", "integrity": "sha1-VBCXI0yyUTyDzu06zdwn/yeYfVQ=", "dev": true, "optional": true, @@ -8076,7 +8076,7 @@ }, "jsdom": { "version": "7.2.2", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-7.2.2.tgz", + "resolved": "http://registry.npm.taobao.org/jsdom/download/jsdom-7.2.2.tgz", "integrity": "sha1-QLQCdwwr2iNGkJa+6Rq2deOx/G4=", "dev": true, "optional": true, @@ -8100,14 +8100,14 @@ "dependencies": { "acorn": { "version": "2.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-2.7.0.tgz", + "resolved": "http://registry.npm.taobao.org/acorn/download/acorn-2.7.0.tgz", "integrity": "sha1-q259nYhqrKiwhbwzEreaGYQz8Oc=", "dev": true, "optional": true }, "parse5": { "version": "1.5.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-1.5.1.tgz", + "resolved": "http://registry.npm.taobao.org/parse5/download/parse5-1.5.1.tgz", "integrity": "sha1-m387DeMr543CQBsXVzzK8Pb1nZQ=", "dev": true, "optional": true @@ -8897,11 +8897,6 @@ "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", "dev": true }, - "lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" - }, "lodash.create": { "version": "3.1.1", "resolved": "https://registry.npm.taobao.org/lodash.create/download/lodash.create-3.1.1.tgz", @@ -8913,11 +8908,6 @@ "lodash._isiterateecall": "3.0.9" } }, - "lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npm.taobao.org/lodash.debounce/download/lodash.debounce-4.0.8.tgz", - "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" - }, "lodash.escape": { "version": "3.2.0", "resolved": "http://registry.npm.taobao.org/lodash.escape/download/lodash.escape-3.2.0.tgz", @@ -8945,11 +8935,6 @@ "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", "dev": true }, - "lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npm.taobao.org/lodash.isequal/download/lodash.isequal-4.5.0.tgz", - "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" - }, "lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", @@ -15976,7 +15961,7 @@ }, "symbol-tree": { "version": "3.2.2", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz", + "resolved": "http://registry.npm.taobao.org/symbol-tree/download/symbol-tree-3.2.2.tgz", "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=", "dev": true, "optional": true @@ -16267,7 +16252,7 @@ }, "tr46": { "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "resolved": "http://registry.npm.taobao.org/tr46/download/tr46-0.0.3.tgz", "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", "dev": true, "optional": true @@ -17031,7 +17016,7 @@ "dependencies": { "cheerio": { "version": "0.20.0", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.20.0.tgz", + "resolved": "http://registry.npm.taobao.org/cheerio/download/cheerio-0.20.0.tgz", "integrity": "sha1-XHEPK6uVZTJyhCugHG6mGzVF7DU=", "dev": true, "requires": { @@ -17045,7 +17030,7 @@ }, "domhandler": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz", + "resolved": "http://registry.npm.taobao.org/domhandler/download/domhandler-2.3.0.tgz", "integrity": "sha1-LeWaCCLVAn+r/28DLCsloqir5zg=", "dev": true, "requires": { @@ -17054,7 +17039,7 @@ }, "domutils": { "version": "1.5.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "resolved": "http://registry.npm.taobao.org/domutils/download/domutils-1.5.1.tgz", "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", "dev": true, "requires": { @@ -17064,7 +17049,7 @@ }, "htmlparser2": { "version": "3.8.3", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", + "resolved": "http://registry.npm.taobao.org/htmlparser2/download/htmlparser2-3.8.3.tgz", "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=", "dev": true, "requires": { @@ -17077,7 +17062,7 @@ "dependencies": { "entities": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", + "resolved": "http://registry.npm.taobao.org/entities/download/entities-1.0.0.tgz", "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=", "dev": true } @@ -17085,13 +17070,13 @@ }, "isarray": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "resolved": "http://registry.npm.taobao.org/isarray/download/isarray-0.0.1.tgz", "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", "dev": true }, "loader-utils": { "version": "0.2.17", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", + "resolved": "http://registry.npm.taobao.org/loader-utils/download/loader-utils-0.2.17.tgz", "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", "dev": true, "requires": { @@ -17103,7 +17088,7 @@ }, "readable-stream": { "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "resolved": "http://registry.npm.taobao.org/readable-stream/download/readable-stream-1.1.14.tgz", "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "dev": true, "requires": { @@ -17115,7 +17100,7 @@ }, "string_decoder": { "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "resolved": "http://registry.npm.taobao.org/string_decoder/download/string_decoder-0.10.31.tgz", "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", "dev": true } @@ -17319,7 +17304,7 @@ }, "webidl-conversions": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-2.0.1.tgz", + "resolved": "http://registry.npm.taobao.org/webidl-conversions/download/webidl-conversions-2.0.1.tgz", "integrity": "sha1-O/glj30xjHRDw28uFpQCoaZwNQY=", "dev": true, "optional": true @@ -18038,7 +18023,7 @@ }, "whatwg-url-compat": { "version": "0.6.5", - "resolved": "https://registry.npmjs.org/whatwg-url-compat/-/whatwg-url-compat-0.6.5.tgz", + "resolved": "http://registry.npm.taobao.org/whatwg-url-compat/download/whatwg-url-compat-0.6.5.tgz", "integrity": "sha1-AImBEa9om7CXVBzVpFymyHmERb8=", "dev": true, "optional": true, @@ -18168,7 +18153,7 @@ }, "xml-name-validator": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-2.0.1.tgz", + "resolved": "http://registry.npm.taobao.org/xml-name-validator/download/xml-name-validator-2.0.1.tgz", "integrity": "sha1-TYuPHszTQZqjYgYb7O9RXh5VljU=", "dev": true, "optional": true @@ -18250,4 +18235,4 @@ "dev": true } } -} \ No newline at end of file +} From 0c2ff441a72f18476a03582e6beb9017b5f028b9 Mon Sep 17 00:00:00 2001 From: tjz <415800467@qq.com> Date: Sun, 25 Mar 2018 22:23:04 +0800 Subject: [PATCH 7/8] fix vc-table --- components/_util/props-util.js | 10 +++-- components/vc-table/demo/className.js | 45 +++++++++++++++++++++ components/vc-table/src/ExpandableRow.jsx | 1 + components/vc-table/src/ExpandableTable.jsx | 4 +- components/vc-table/src/TableCell.jsx | 30 ++++++++------ components/vc-table/src/TableHeaderRow.jsx | 17 +++++--- components/vc-table/src/TableRow.jsx | 32 +++++++++------ 7 files changed, 103 insertions(+), 36 deletions(-) create mode 100644 components/vc-table/demo/className.js diff --git a/components/_util/props-util.js b/components/_util/props-util.js index d483cbd80..8aec27a94 100644 --- a/components/_util/props-util.js +++ b/components/_util/props-util.js @@ -166,10 +166,6 @@ export function getComponentName (opts) { return opts && (opts.Ctor.options.name || opts.tag) } -export function isValidElement (ele) { - return !!ele.tag -} - export function isEmptyElement (ele) { return !(ele.tag || ele.text.trim() !== '') } @@ -206,6 +202,11 @@ export function mergeProps () { return props } +function isValidElement (element) { + const name = element.constructor.name + return element.tag && (name === 'VNode' || name === 'VueComponent') +} + export { hasProp, filterProps, @@ -219,5 +220,6 @@ export { getValueByProp, parseStyleText, initDefaultProps, + isValidElement, } export default hasProp diff --git a/components/vc-table/demo/className.js b/components/vc-table/demo/className.js new file mode 100644 index 000000000..9413ea545 --- /dev/null +++ b/components/vc-table/demo/className.js @@ -0,0 +1,45 @@ +/* eslint-disable no-console,func-names,react/no-multi-comp */ +import Table from '../index' +import '../assets/index.less' + +const data = [ + { a: '123', key: '1' }, + { a: 'cdd', b: 'edd', key: '2' }, + { a: '1333', c: 'eee', d: 2, key: '3' }, +] +export default { + render () { + const columns = [ + { title: 'title1', dataIndex: 'a', + className: 'a', + key: 'a', width: 100 }, + { id: '123', title: 'title2', dataIndex: 'b', + className: 'b', + key: 'b', width: 100 }, + { title: 'title3', dataIndex: 'c', + className: 'c', + key: 'c', width: 200 }, + { + title: 'Operations', dataIndex: '', + className: 'd', + key: 'd', render (h) { + return Operations + }, + }, + ] + return ( +
    +

    rowClassName and className

    +
    - {this.renderExpandIcon()} - + {this.renderExpandIcon()} +
    `row-${i}`} + expandedRowRender={record =>

    extra: {record.a}

    } + expandedRowClassName={(record, i) => `ex-row-${i}`} + data={data} + class='table' + /> + + ) + }, +} + diff --git a/components/vc-table/src/ExpandableRow.jsx b/components/vc-table/src/ExpandableRow.jsx index 77d40a0b9..cfd431197 100644 --- a/components/vc-table/src/ExpandableRow.jsx +++ b/components/vc-table/src/ExpandableRow.jsx @@ -112,6 +112,7 @@ const ExpandableRow = { renderExpandIcon: this.renderExpandIcon, renderExpandIconCell: this.renderExpandIconCell, }, + on: { rowClick: this.handleRowClick, }, diff --git a/components/vc-table/src/ExpandableTable.jsx b/components/vc-table/src/ExpandableTable.jsx index 8308699a8..fc8402b3c 100644 --- a/components/vc-table/src/ExpandableTable.jsx +++ b/components/vc-table/src/ExpandableTable.jsx @@ -154,12 +154,11 @@ const ExpandableTable = { cell: 'td', }, } - return ( {}} /> ) }, diff --git a/components/vc-table/src/TableCell.jsx b/components/vc-table/src/TableCell.jsx index 526f40ce9..8ae309633 100644 --- a/components/vc-table/src/TableCell.jsx +++ b/components/vc-table/src/TableCell.jsx @@ -1,5 +1,6 @@ import PropTypes from '../../_util/vue-types' import get from 'lodash/get' +import { isValidElement } from '../../_util/props-util' export default { name: 'TableCell', @@ -16,7 +17,7 @@ export default { methods: { isInvalidRenderCellText (text) { // debugger - return text && + return text && !isValidElement(text) && Object.prototype.toString.call(text) === '[object Object]' }, @@ -28,7 +29,7 @@ export default { }, }, - render () { + render (h) { const { record, indentSize, @@ -39,8 +40,8 @@ export default { column, component: BodyCell, } = this - const { dataIndex, render } = column - + const { dataIndex, render, className = '' } = column + const cls = column.class || className // We should return undefined if no dataIndex is specified, but in order to // be compatible with object-path's behavior, we return the record object instead. let text @@ -51,22 +52,29 @@ export default { } else { text = get(record, dataIndex) } - let tdProps = {} + const tdProps = { + props: {}, + attrs: {}, + class: cls, + on: { + click: this.handleClick, + }, + } let colSpan let rowSpan if (render) { - text = render(text, record, index) + text = render(h, text, record, index) if (this.isInvalidRenderCellText(text)) { - tdProps = text.props || tdProps - colSpan = tdProps.colSpan - rowSpan = tdProps.rowSpan + tdProps.attrs = text.attrs || text.props || {} + colSpan = tdProps.attrs.colSpan + rowSpan = tdProps.attrs.rowSpan text = text.children } } if (column.onCell) { - tdProps = { ...tdProps, ...column.onCell(record) } + tdProps.attrs = { ...tdProps.attrs, ...column.onCell(record) } } // Fix https://github.com/ant-design/ant-design/issues/1202 @@ -88,11 +96,9 @@ export default { if (column.align) { tdProps.style = { textAlign: column.align } } - console.log('tdProps', tdProps) return ( {indentText} diff --git a/components/vc-table/src/TableHeaderRow.jsx b/components/vc-table/src/TableHeaderRow.jsx index 6e23182a1..e87532b51 100644 --- a/components/vc-table/src/TableHeaderRow.jsx +++ b/components/vc-table/src/TableHeaderRow.jsx @@ -1,5 +1,6 @@ import PropTypes from '../../_util/vue-types' import { connect } from '../../_util/store' +import { mergeProps } from '../../_util/props-util' const TableHeaderRow = { props: { @@ -25,17 +26,23 @@ const TableHeaderRow = { {row.map((cell, i) => { const { column, children, className, ...cellProps } = cell + const cls = cell.class || className const customProps = column.onHeaderCell ? column.onHeaderCell(column) : {} if (column.align) { cellProps.style = { textAlign: column.align } } - + const headerCellProps = mergeProps({ + attrs: { + ...cellProps, + }, + class: cls, + }, { + ...customProps, + key: column.key || column.dataIndex || i, + }) return ( {children} diff --git a/components/vc-table/src/TableRow.jsx b/components/vc-table/src/TableRow.jsx index 1ea693648..a64570267 100644 --- a/components/vc-table/src/TableRow.jsx +++ b/components/vc-table/src/TableRow.jsx @@ -2,9 +2,9 @@ import PropTypes from '../../_util/vue-types' import { connect } from '../../_util/store' import TableCell from './TableCell' import { warningOnce } from './utils' -import { initDefaultProps } from '../../_util/props-util' +import { initDefaultProps, mergeProps } from '../../_util/props-util' import BaseMixin from '../../_util/BaseMixin' - +function noop () {} const TableRow = { name: 'TableRow', mixins: [BaseMixin], @@ -71,7 +71,11 @@ const TableRow = { } }, watch: { - + visible (val) { + if (val) { + this.shouldRender = true + } + }, }, componentWillReceiveProps (nextProps) { @@ -193,7 +197,7 @@ const TableRow = { renderExpandIconCell, $listeners, } = this - const { row: onRow } = $listeners + const { row: onRow = noop } = $listeners const BodyRow = components.body.row const BodyCell = components.body.cell @@ -242,17 +246,19 @@ const TableRow = { } style = { ...style, ...customStyle } - console.log('rowProps', rowProps) + const bodyRowProps = mergeProps({ + on: { + click: this.onRowClick, + dblclick: this.onRowDoubleClick, + mouseenter: this.onMouseEnter, + mouseleave: this.onMouseLeave, + contextmenu: this.onContextMenu, + }, + class: rowClassName, + }, { ...rowProps, style }) return ( {cells} From 0cdf29011d2548f886eac9eaba6d321b1cb7a783 Mon Sep 17 00:00:00 2001 From: tjz <415800467@qq.com> Date: Sun, 25 Mar 2018 22:24:57 +0800 Subject: [PATCH 8/8] update tooltip demo --- components/tooltip/demo/placement.md | 48 ++++++++++++++-------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/components/tooltip/demo/placement.md b/components/tooltip/demo/placement.md index 52e346684..dd248b3a6 100644 --- a/components/tooltip/demo/placement.md +++ b/components/tooltip/demo/placement.md @@ -12,84 +12,84 @@ The ToolTip has 12 placements choice.