merge master
commit
6bf84b865c
|
@ -166,10 +166,6 @@ export function getComponentName (opts) {
|
||||||
return opts && (opts.Ctor.options.name || opts.tag)
|
return opts && (opts.Ctor.options.name || opts.tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isValidElement (ele) {
|
|
||||||
return !!ele.tag
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isEmptyElement (ele) {
|
export function isEmptyElement (ele) {
|
||||||
return !(ele.tag || ele.text.trim() !== '')
|
return !(ele.tag || ele.text.trim() !== '')
|
||||||
}
|
}
|
||||||
|
@ -206,6 +202,11 @@ export function mergeProps () {
|
||||||
return props
|
return props
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isValidElement (element) {
|
||||||
|
const name = element.constructor.name
|
||||||
|
return element.tag && (name === 'VNode' || name === 'VueComponent')
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
hasProp,
|
hasProp,
|
||||||
filterProps,
|
filterProps,
|
||||||
|
@ -219,5 +220,6 @@ export {
|
||||||
getValueByProp,
|
getValueByProp,
|
||||||
parseStyleText,
|
parseStyleText,
|
||||||
initDefaultProps,
|
initDefaultProps,
|
||||||
|
isValidElement,
|
||||||
}
|
}
|
||||||
export default hasProp
|
export default hasProp
|
||||||
|
|
|
@ -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,
|
||||||
|
})
|
|
@ -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]
|
||||||
|
},
|
||||||
|
}
|
|
@ -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 (
|
||||||
|
<WrappedComponent {...wrapProps}/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return Connect
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
export { default as Provider } from './Provider'
|
||||||
|
|
||||||
|
export { default as connect } from './connect'
|
||||||
|
|
||||||
|
export { default as create } from './create'
|
|
@ -20,7 +20,6 @@ export default {
|
||||||
},
|
},
|
||||||
inject: {
|
inject: {
|
||||||
checkboxGroupContext: { default: null },
|
checkboxGroupContext: { default: null },
|
||||||
test: { default: null },
|
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
const { checkboxGroupContext, checked, defaultChecked, value } = this
|
const { checkboxGroupContext, checked, defaultChecked, value } = this
|
||||||
|
|
|
@ -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` |
|
| 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) | - |
|
| overlay(slot) | the dropdown menu | [Menu](#/us/components/menu) | - |
|
||||||
| placement | placement of pop menu: `bottomLeft` `bottomCenter` `bottomRight` `topLeft` `topCenter` `topRight` | String | `bottomLeft` |
|
| 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 | - |
|
| visible(v-model) | whether the dropdown menu is visible | boolean | - |
|
||||||
|
|
||||||
### events
|
### 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) | - |
|
| overlay(slot) | the dropdown menu | [Menu](#/us/components/menu) | - |
|
||||||
| placement | placement of pop menu: `bottomLeft` `bottomCenter` `bottomRight` `topLeft` `topCenter` `topRight` | String | `bottomLeft` |
|
| 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` |
|
| 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` |
|
| type | type of the button, the same as [Button](#/us/components/button) | string | `default` |
|
||||||
| visible | whether the dropdown menu is visible | boolean | - |
|
| visible | whether the dropdown menu is visible | boolean | - |
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
| getPopupContainer | 菜单渲染父节点。默认渲染到 body 上,如果你遇到菜单滚动定位问题,试试修改为滚动的区域,并相对其定位。 | Function(triggerNode) | `() => document.body` |
|
| getPopupContainer | 菜单渲染父节点。默认渲染到 body 上,如果你遇到菜单滚动定位问题,试试修改为滚动的区域,并相对其定位。 | Function(triggerNode) | `() => document.body` |
|
||||||
| overlay(slot) | 菜单 | [Menu](#/cn/components/menu) | - |
|
| overlay(slot) | 菜单 | [Menu](#/cn/components/menu) | - |
|
||||||
| placement | 菜单弹出位置:`bottomLeft` `bottomCenter` `bottomRight` `topLeft` `topCenter` `topRight` | String | `bottomLeft` |
|
| 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 | - |
|
| visible(v-model) | 菜单是否显示 | boolean | - |
|
||||||
|
|
||||||
`overlay` 菜单使用 [Menu](#/cn/components/menu/),还包括菜单项 `Menu.Item`,分割线 `Menu.Divider`。
|
`overlay` 菜单使用 [Menu](#/cn/components/menu/),还包括菜单项 `Menu.Item`,分割线 `Menu.Divider`。
|
||||||
|
@ -31,7 +31,7 @@
|
||||||
| overlay(slot) | 菜单 | [Menu](#/cn/components/menu/) | - |
|
| overlay(slot) | 菜单 | [Menu](#/cn/components/menu/) | - |
|
||||||
| placement | 菜单弹出位置:`bottomLeft` `bottomCenter` `bottomRight` `topLeft` `topCenter` `topRight` | String | `bottomLeft` |
|
| placement | 菜单弹出位置:`bottomLeft` `bottomCenter` `bottomRight` `topLeft` `topCenter` `topRight` | String | `bottomLeft` |
|
||||||
| size | 按钮大小,和 [Button](#/cn/components/button/) 一致 | string | 'default' |
|
| 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' |
|
| type | 按钮类型,和 [Button](#/cn/components/button/) 一致 | string | 'default' |
|
||||||
| visible(v-model) | 菜单是否显示 | boolean | - |
|
| visible(v-model) | 菜单是否显示 | boolean | - |
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
<td>action</td>
|
<td>action</td>
|
||||||
<td>string[]</td>
|
<td>string[]</td>
|
||||||
<td>['hover']</td>
|
<td>['hover']</td>
|
||||||
<td>which actions cause popup shown. enum of 'hover','click','focus','contextMenu'</td>
|
<td>which actions cause popup shown. enum of 'hover','click','focus','contextmenu'</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>mouseEnterDelay</td>
|
<td>mouseEnterDelay</td>
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,227 @@
|
||||||
|
@import 'normalize.css';
|
||||||
|
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
/* eslint-disable no-console,func-names,react/no-multi-comp */
|
||||||
|
import Table from '../index'
|
||||||
|
import '../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)
|
||||||
|
}
|
||||||
|
export default {
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<Table
|
||||||
|
defaultExpandAllRows
|
||||||
|
columns={columns}
|
||||||
|
data={data}
|
||||||
|
indentSize={30}
|
||||||
|
onExpand={onExpand} />
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
|
@ -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 <a href='#'>Operations</a>
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h2>rowClassName and className</h2>
|
||||||
|
<Table
|
||||||
|
columns={columns}
|
||||||
|
rowClassName={(record, i) => `row-${i}`}
|
||||||
|
expandedRowRender={record => <p>extra: {record.a}</p>}
|
||||||
|
expandedRowClassName={(record, i) => `ex-row-${i}`}
|
||||||
|
data={data}
|
||||||
|
class='table'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
|
@ -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 }
|
|
@ -0,0 +1,191 @@
|
||||||
|
|
||||||
|
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'
|
||||||
|
import { connect } from '../../_util/store'
|
||||||
|
function noop () {}
|
||||||
|
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, sComponents: components,
|
||||||
|
prefixCls,
|
||||||
|
childrenColumnName,
|
||||||
|
rowClassName,
|
||||||
|
// rowRef,
|
||||||
|
$listeners: {
|
||||||
|
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
|
||||||
|
|
||||||
|
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: {
|
||||||
|
// ...expander.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 (
|
||||||
|
<TableRow
|
||||||
|
{...tableRowProps}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const row = (
|
||||||
|
<ExpandableRow
|
||||||
|
{...expandableRowProps}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
rows.push(row)
|
||||||
|
|
||||||
|
expander.renderRows(
|
||||||
|
this.renderRows,
|
||||||
|
rows,
|
||||||
|
record,
|
||||||
|
i,
|
||||||
|
indent,
|
||||||
|
fixed,
|
||||||
|
key,
|
||||||
|
ancestorKeys
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return rows
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { sComponents: 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 = (
|
||||||
|
<BodyWrapper class={`${prefixCls}-tbody`}>
|
||||||
|
{this.renderRows(data, 0)}
|
||||||
|
</BodyWrapper>
|
||||||
|
)
|
||||||
|
if (getBodyWrapper) {
|
||||||
|
body = getBodyWrapper(body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Table class={tableClassName} style={tableStyle} key='table'>
|
||||||
|
<ColGroup columns={columns} fixed={fixed} />
|
||||||
|
{hasHead && <TableHeader expander={expander} columns={columns} fixed={fixed} /> }
|
||||||
|
{body}
|
||||||
|
</Table>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect()(BaseTable)
|
|
@ -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 = (
|
||||||
|
<BaseTable
|
||||||
|
tableClassName={tableClassName}
|
||||||
|
hasHead={!useFixedHeader}
|
||||||
|
hasBody
|
||||||
|
fixed={fixed}
|
||||||
|
columns={columns}
|
||||||
|
expander={expander}
|
||||||
|
getRowKey={getRowKey}
|
||||||
|
isAnyColumnsFixed={isAnyColumnsFixed}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<div
|
||||||
|
key='bodyTable'
|
||||||
|
class={`${prefixCls}-body-outer`}
|
||||||
|
style={{ ...bodyStyle }}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class={`${prefixCls}-body-inner`}
|
||||||
|
style={innerBodyStyle}
|
||||||
|
ref={refName}
|
||||||
|
onScroll={handleBodyScroll}
|
||||||
|
>
|
||||||
|
{baseTable}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key='bodyTable'
|
||||||
|
class={`${prefixCls}-body`}
|
||||||
|
style={bodyStyle}
|
||||||
|
ref='bodyTable'
|
||||||
|
onScroll={handleBodyScroll}
|
||||||
|
>
|
||||||
|
{baseTable}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
import PropTypes from '../../_util/vue-types'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ColGroup',
|
||||||
|
props: {
|
||||||
|
fixed: PropTypes.string,
|
||||||
|
columns: PropTypes.array,
|
||||||
|
},
|
||||||
|
inject: {
|
||||||
|
table: { default: {}},
|
||||||
|
},
|
||||||
|
render () {
|
||||||
|
const { fixed, table } = this
|
||||||
|
const { prefixCls, expandIconAsCell, columnManager } = table
|
||||||
|
|
||||||
|
let cols = []
|
||||||
|
|
||||||
|
if (expandIconAsCell && fixed !== 'right') {
|
||||||
|
cols.push(
|
||||||
|
<col
|
||||||
|
class={`${prefixCls}-expand-icon-col`}
|
||||||
|
key='rc-table-expand-icon-col'
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let leafColumns
|
||||||
|
|
||||||
|
if (fixed === 'left') {
|
||||||
|
leafColumns = columnManager.leftLeafColumns()
|
||||||
|
} else if (fixed === 'right') {
|
||||||
|
leafColumns = columnManager.rightLeafColumns()
|
||||||
|
} else {
|
||||||
|
leafColumns = columnManager.leafColumns()
|
||||||
|
}
|
||||||
|
cols = cols.concat(
|
||||||
|
leafColumns.map(c => {
|
||||||
|
const width = typeof c.width === 'number' ? `${c.width}px` : c.width
|
||||||
|
return (
|
||||||
|
<col
|
||||||
|
key={c.key || c.dataIndex}
|
||||||
|
style={{ width, minWidth: width }}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
return (
|
||||||
|
<colgroup>
|
||||||
|
{cols}
|
||||||
|
</colgroup>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
},
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
import PropTypes from '../../_util/vue-types'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ColumnGroup',
|
||||||
|
props: {
|
||||||
|
title: PropTypes.any,
|
||||||
|
},
|
||||||
|
isTableColumnGroup: true,
|
||||||
|
}
|
|
@ -0,0 +1,149 @@
|
||||||
|
export default class ColumnManager {
|
||||||
|
constructor (columns, elements) {
|
||||||
|
this.columns = columns || this.normalize(elements)
|
||||||
|
this._cached = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 (
|
||||||
|
<span
|
||||||
|
class={`${prefixCls}-expand-icon ${prefixCls}-${expandClassName}`}
|
||||||
|
onClick={onExpand}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
} else if (needIndentSpaced) {
|
||||||
|
return <span class={`${prefixCls}-expand-icon ${prefixCls}-spaced`} />
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
}
|
|
@ -0,0 +1,128 @@
|
||||||
|
import PropTypes from '../../_util/vue-types'
|
||||||
|
import ExpandIcon from './ExpandIcon'
|
||||||
|
import BaseMixin from '../../_util/BaseMixin'
|
||||||
|
import { connect } from '../../_util/store'
|
||||||
|
|
||||||
|
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()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
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 (
|
||||||
|
<ExpandIcon
|
||||||
|
expandable={this.expandable}
|
||||||
|
prefixCls={prefixCls}
|
||||||
|
onExpand={this.handleExpandChange}
|
||||||
|
needIndentSpaced={needIndentSpaced}
|
||||||
|
expanded={expanded}
|
||||||
|
record={record}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
renderExpandIconCell (cells) {
|
||||||
|
if (!this.expandIconAsCell) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const { prefixCls } = this
|
||||||
|
|
||||||
|
cells.push(
|
||||||
|
<td
|
||||||
|
class={`${prefixCls}-expand-icon-cell`}
|
||||||
|
key='rc-table-expand-icon-cell'
|
||||||
|
>
|
||||||
|
{this.renderExpandIcon()}
|
||||||
|
</td>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
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)
|
|
@ -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 (
|
||||||
|
<TableRow
|
||||||
|
key={rowKey}
|
||||||
|
columns={columns}
|
||||||
|
class={className}
|
||||||
|
rowKey={rowKey}
|
||||||
|
ancestorKeys={ancestorKeys}
|
||||||
|
prefixCls={`${prefixCls}-expanded-row`}
|
||||||
|
indentSize={indentSize}
|
||||||
|
indent={indent}
|
||||||
|
fixed={fixed}
|
||||||
|
components={components}
|
||||||
|
expandedRow
|
||||||
|
hasExpandIcon={() => {}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
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)
|
|
@ -0,0 +1,59 @@
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
inject: {
|
||||||
|
table: { default: {}},
|
||||||
|
},
|
||||||
|
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 (
|
||||||
|
<div
|
||||||
|
key='headTable'
|
||||||
|
ref={fixed ? null : 'headTable'}
|
||||||
|
class={`${prefixCls}-header`}
|
||||||
|
style={headStyle}
|
||||||
|
onScroll={handleBodyScrollLeft}
|
||||||
|
>
|
||||||
|
<BaseTable
|
||||||
|
tableClassName={tableClassName}
|
||||||
|
hasHead
|
||||||
|
hasBody={false}
|
||||||
|
fixed={fixed}
|
||||||
|
columns={columns}
|
||||||
|
expander={expander}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
|
@ -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 ? (
|
||||||
|
<div class={`${prefixCls}-scroll`}>{table}</div>
|
||||||
|
) : table
|
||||||
|
},
|
||||||
|
|
||||||
|
renderLeftFixedTable () {
|
||||||
|
const { prefixCls } = this
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class={`${prefixCls}-fixed-left`}>
|
||||||
|
{this.renderTable({
|
||||||
|
columns: this.columnManager.leftColumns(),
|
||||||
|
fixed: 'left',
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
renderRightFixedTable () {
|
||||||
|
const { prefixCls } = this
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class={`${prefixCls}-fixed-right`}>
|
||||||
|
{this.renderTable({
|
||||||
|
columns: this.columnManager.rightColumns(),
|
||||||
|
fixed: 'right',
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
renderTable (options) {
|
||||||
|
const { columns, fixed, isAnyColumnsFixed } = options
|
||||||
|
const { prefixCls, scroll = {}} = this
|
||||||
|
const tableClassName = (scroll.x || fixed) ? `${prefixCls}-fixed` : ''
|
||||||
|
|
||||||
|
const headTable = (
|
||||||
|
<HeadTable
|
||||||
|
key='head'
|
||||||
|
columns={columns}
|
||||||
|
fixed={fixed}
|
||||||
|
tableClassName={tableClassName}
|
||||||
|
handleBodyScrollLeft={this.handleBodyScrollLeft}
|
||||||
|
expander={this.expander}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
const bodyTable = (
|
||||||
|
<BodyTable
|
||||||
|
key='body'
|
||||||
|
columns={columns}
|
||||||
|
fixed={fixed}
|
||||||
|
tableClassName={tableClassName}
|
||||||
|
getRowKey={this.getRowKey}
|
||||||
|
handleBodyScroll={this.handleBodyScroll}
|
||||||
|
expander={this.expander}
|
||||||
|
isAnyColumnsFixed={isAnyColumnsFixed}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
return [headTable, bodyTable]
|
||||||
|
},
|
||||||
|
|
||||||
|
renderTitle () {
|
||||||
|
const { title, prefixCls } = this
|
||||||
|
return title ? (
|
||||||
|
<div class={`${prefixCls}-title`} key='title'>
|
||||||
|
{title(this.props.data)}
|
||||||
|
</div>
|
||||||
|
) : null
|
||||||
|
},
|
||||||
|
|
||||||
|
renderFooter () {
|
||||||
|
const { footer, prefixCls } = this
|
||||||
|
return footer ? (
|
||||||
|
<div class={`${prefixCls}-footer`} key='footer'>
|
||||||
|
{footer(this.props.data)}
|
||||||
|
</div>
|
||||||
|
) : null
|
||||||
|
},
|
||||||
|
|
||||||
|
renderEmptyText () {
|
||||||
|
const { emptyText, prefixCls, data } = this
|
||||||
|
if (data.length) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const emptyClassName = `${prefixCls}-placeholder`
|
||||||
|
return (
|
||||||
|
<div class={emptyClassName} key='emptyText'>
|
||||||
|
{(typeof emptyText === 'function') ? emptyText() : emptyText}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<div
|
||||||
|
ref='tableNode'
|
||||||
|
class={className}
|
||||||
|
// style={props.style}
|
||||||
|
// id={props.id}
|
||||||
|
>
|
||||||
|
{this.renderTitle()}
|
||||||
|
<div class={`${prefixCls}-content`}>
|
||||||
|
{this.renderMainTable()}
|
||||||
|
{hasLeftFixed && this.renderLeftFixedTable()}
|
||||||
|
{hasRightFixed && this.renderRightFixedTable()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Provider store={this.store}>
|
||||||
|
<ExpandableTable
|
||||||
|
{...expandableTableProps}
|
||||||
|
/>
|
||||||
|
</Provider>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
|
@ -0,0 +1,110 @@
|
||||||
|
import PropTypes from '../../_util/vue-types'
|
||||||
|
import get from 'lodash/get'
|
||||||
|
import { isValidElement } from '../../_util/props-util'
|
||||||
|
|
||||||
|
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.any,
|
||||||
|
component: PropTypes.any,
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
isInvalidRenderCellText (text) {
|
||||||
|
// debugger
|
||||||
|
return text && !isValidElement(text) &&
|
||||||
|
Object.prototype.toString.call(text) === '[object Object]'
|
||||||
|
},
|
||||||
|
|
||||||
|
handleClick (e) {
|
||||||
|
const { record, column: { onCellClick }} = this
|
||||||
|
if (onCellClick) {
|
||||||
|
onCellClick(record, e)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
render (h) {
|
||||||
|
const {
|
||||||
|
record,
|
||||||
|
indentSize,
|
||||||
|
prefixCls,
|
||||||
|
indent,
|
||||||
|
index,
|
||||||
|
expandIcon,
|
||||||
|
column,
|
||||||
|
component: BodyCell,
|
||||||
|
} = this
|
||||||
|
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
|
||||||
|
if (typeof dataIndex === 'number') {
|
||||||
|
text = get(record, dataIndex)
|
||||||
|
} else if (!dataIndex || dataIndex.length === 0) {
|
||||||
|
text = record
|
||||||
|
} else {
|
||||||
|
text = get(record, dataIndex)
|
||||||
|
}
|
||||||
|
const tdProps = {
|
||||||
|
props: {},
|
||||||
|
attrs: {},
|
||||||
|
class: cls,
|
||||||
|
on: {
|
||||||
|
click: this.handleClick,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
let colSpan
|
||||||
|
let rowSpan
|
||||||
|
|
||||||
|
if (render) {
|
||||||
|
text = render(h, text, record, index)
|
||||||
|
if (this.isInvalidRenderCellText(text)) {
|
||||||
|
tdProps.attrs = text.attrs || text.props || {}
|
||||||
|
colSpan = tdProps.attrs.colSpan
|
||||||
|
rowSpan = tdProps.attrs.rowSpan
|
||||||
|
text = text.children
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (column.onCell) {
|
||||||
|
tdProps.attrs = { ...tdProps.attrs, ...column.onCell(record) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fix https://github.com/ant-design/ant-design/issues/1202
|
||||||
|
if (this.isInvalidRenderCellText(text)) {
|
||||||
|
text = null
|
||||||
|
}
|
||||||
|
|
||||||
|
const indentText = expandIcon ? (
|
||||||
|
<span
|
||||||
|
style={{ paddingLeft: `${indentSize * indent}px` }}
|
||||||
|
class={`${prefixCls}-indent indent-level-${indent}`}
|
||||||
|
/>
|
||||||
|
) : null
|
||||||
|
|
||||||
|
if (rowSpan === 0 || colSpan === 0) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (column.align) {
|
||||||
|
tdProps.style = { textAlign: column.align }
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BodyCell
|
||||||
|
{...tdProps}
|
||||||
|
>
|
||||||
|
{indentText}
|
||||||
|
{expandIcon}
|
||||||
|
{text}
|
||||||
|
</BodyCell>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
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,
|
||||||
|
|
||||||
|
},
|
||||||
|
inject: {
|
||||||
|
table: { default: {}},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onHeaderRow () {
|
||||||
|
this.table.__emit('headerRow', ...arguments)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { sComponents: 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 (
|
||||||
|
<HeaderWrapper class={`${prefixCls}-thead`}>
|
||||||
|
{
|
||||||
|
rows.map((row, index) => (
|
||||||
|
<TableHeaderRow
|
||||||
|
key={index}
|
||||||
|
index={index}
|
||||||
|
fixed={fixed}
|
||||||
|
columns={columns}
|
||||||
|
rows={rows}
|
||||||
|
row={row}
|
||||||
|
components={components}
|
||||||
|
onHeaderRow={onHeaderRow}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</HeaderWrapper>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
import PropTypes from '../../_util/vue-types'
|
||||||
|
import { connect } from '../../_util/store'
|
||||||
|
import { mergeProps } from '../../_util/props-util'
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<HeaderRow {...rowProps} style={style}>
|
||||||
|
{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 (
|
||||||
|
<HeaderCell
|
||||||
|
{...headerCellProps}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</HeaderCell>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</HeaderRow>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
|
@ -0,0 +1,298 @@
|
||||||
|
import PropTypes from '../../_util/vue-types'
|
||||||
|
import { connect } from '../../_util/store'
|
||||||
|
import TableCell from './TableCell'
|
||||||
|
import { warningOnce } from './utils'
|
||||||
|
import { initDefaultProps, mergeProps } from '../../_util/props-util'
|
||||||
|
import BaseMixin from '../../_util/BaseMixin'
|
||||||
|
function noop () {}
|
||||||
|
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,
|
||||||
|
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,
|
||||||
|
expandIconColumnIndex: PropTypes.number,
|
||||||
|
expandRowByClick: PropTypes.bool,
|
||||||
|
// visible: PropTypes.bool,
|
||||||
|
// hovered: PropTypes.bool,
|
||||||
|
// height: PropTypes.any,
|
||||||
|
}, {
|
||||||
|
expandIconColumnIndex: 0,
|
||||||
|
expandRowByClick: false,
|
||||||
|
hasExpandIcon () {},
|
||||||
|
renderExpandIcon () {},
|
||||||
|
renderExpandIconCell () {},
|
||||||
|
}),
|
||||||
|
|
||||||
|
data () {
|
||||||
|
this.shouldRender = this.visible
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted () {
|
||||||
|
if (this.shouldRender) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.saveRowRef()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
visible (val) {
|
||||||
|
if (val) {
|
||||||
|
this.shouldRender = true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillReceiveProps (nextProps) {
|
||||||
|
if (this.props.visible || (!this.props.visible && nextProps.visible)) {
|
||||||
|
this.shouldRender = true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
shouldComponentUpdate (nextProps) {
|
||||||
|
return !!(this.props.visible || nextProps.visible)
|
||||||
|
},
|
||||||
|
|
||||||
|
updated () {
|
||||||
|
if (this.shouldRender && !this.rowRef) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.saveRowRef()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onRowClick (event) {
|
||||||
|
const { record, index } = this
|
||||||
|
this.__emit('rowClick', record, index, event)
|
||||||
|
},
|
||||||
|
|
||||||
|
onRowDoubleClick (event) {
|
||||||
|
const { record, index } = this
|
||||||
|
this.__emit('rowDoubleClick', record, index, event)
|
||||||
|
},
|
||||||
|
|
||||||
|
onContextMenu (event) {
|
||||||
|
const { record, index } = this
|
||||||
|
this.__emit('rowContextmenu', record, index, event)
|
||||||
|
},
|
||||||
|
|
||||||
|
onMouseEnter (event) {
|
||||||
|
const { record, index, rowKey } = this
|
||||||
|
this.__emit('hover', true, rowKey)
|
||||||
|
this.__emit('rowMouseenter', record, index, event)
|
||||||
|
},
|
||||||
|
|
||||||
|
onMouseLeave (event) {
|
||||||
|
const { record, index, rowKey } = this
|
||||||
|
this.__emit('hover', false, rowKey)
|
||||||
|
this.__emit('rowMouseleave', 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 })
|
||||||
|
},
|
||||||
|
|
||||||
|
setRowHeight () {
|
||||||
|
const { store, index } = this
|
||||||
|
const fixedColumnsBodyRowsHeight = store.getState().fixedColumnsBodyRowsHeight.slice()
|
||||||
|
const height = this.rowRef.getBoundingClientRect().height
|
||||||
|
fixedColumnsBodyRowsHeight[index] = height
|
||||||
|
store.setState({ fixedColumnsBodyRowsHeight })
|
||||||
|
},
|
||||||
|
|
||||||
|
getStyle () {
|
||||||
|
const { height, visible } = this
|
||||||
|
|
||||||
|
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 = this.$el
|
||||||
|
|
||||||
|
const { isAnyColumnsFixed, fixed, expandedRow, ancestorKeys } = this
|
||||||
|
|
||||||
|
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,
|
||||||
|
$listeners,
|
||||||
|
} = this
|
||||||
|
const { row: onRow = noop } = $listeners
|
||||||
|
const BodyRow = components.body.row
|
||||||
|
const BodyCell = components.body.cell
|
||||||
|
|
||||||
|
let className = ''
|
||||||
|
|
||||||
|
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(
|
||||||
|
<TableCell
|
||||||
|
prefixCls={prefixCls}
|
||||||
|
record={record}
|
||||||
|
indentSize={indentSize}
|
||||||
|
indent={indent}
|
||||||
|
index={index}
|
||||||
|
column={column}
|
||||||
|
key={column.key || column.dataIndex}
|
||||||
|
expandIcon={hasExpandIcon(i) && renderExpandIcon()}
|
||||||
|
component={BodyCell}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 }
|
||||||
|
const bodyRowProps = mergeProps({
|
||||||
|
on: {
|
||||||
|
click: this.onRowClick,
|
||||||
|
dblclick: this.onRowDoubleClick,
|
||||||
|
mouseenter: this.onMouseEnter,
|
||||||
|
mouseleave: this.onMouseLeave,
|
||||||
|
contextmenu: this.onContextMenu,
|
||||||
|
},
|
||||||
|
class: rowClassName,
|
||||||
|
}, { ...rowProps, style })
|
||||||
|
return (
|
||||||
|
<BodyRow
|
||||||
|
{...bodyRowProps}
|
||||||
|
>
|
||||||
|
{cells}
|
||||||
|
</BodyRow>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
|
@ -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)
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
File diff suppressed because one or more lines are too long
Binary file not shown.
After Width: | Height: | Size: 45 B |
Binary file not shown.
After Width: | Height: | Size: 381 B |
|
@ -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 = (<span class='cus-label'>
|
||||||
|
<span>operations: </span>
|
||||||
|
<span style={{ color: 'blue' }} onClick={this.onEdit}>Edit</span>
|
||||||
|
<label onClick={(e) => e.stopPropagation()}><input type='checkbox' /> checked</label>
|
||||||
|
<span style={{ color: 'red' }} onClick={this.onDel}>Delete</span>
|
||||||
|
</span>)
|
||||||
|
return (<div style={{ margin: '0 20px' }}>
|
||||||
|
<h2>simple</h2>
|
||||||
|
{/* <Tree
|
||||||
|
class='myCls' showLine checkable defaultExpandAll
|
||||||
|
defaultExpandedKeys={this.defaultExpandedKeys}
|
||||||
|
onExpand={this.onExpand}
|
||||||
|
defaultSelectedKeys={this.defaultSelectedKeys}
|
||||||
|
defaultCheckedKeys={this.defaultCheckedKeys}
|
||||||
|
onSelect={this.onSelect} onCheck={this.onCheck}
|
||||||
|
>
|
||||||
|
<TreeNode title='parent 1' key='0-0'>
|
||||||
|
<TreeNode title={customLabel} key='0-0-0'>
|
||||||
|
<TreeNode title='leaf' key='0-0-0-0' />
|
||||||
|
<TreeNode title='leaf' key='0-0-0-1' />
|
||||||
|
</TreeNode>
|
||||||
|
<TreeNode title='parent 1-1' key='0-0-1'>
|
||||||
|
<TreeNode title='parent 1-1-0' key='0-0-1-0' disableCheckbox />
|
||||||
|
<TreeNode title='parent 1-1-1' key='0-0-1-1' />
|
||||||
|
</TreeNode>
|
||||||
|
<TreeNode title='parent 1-2' key='0-0-2' disabled>
|
||||||
|
<TreeNode title='parent 1-2-0' key='0-0-2-0' disabled />
|
||||||
|
<TreeNode title='parent 1-2-1' key='0-0-2-1' />
|
||||||
|
</TreeNode>
|
||||||
|
</TreeNode>
|
||||||
|
</Tree> */}
|
||||||
|
|
||||||
|
<h2>Check on Click TreeNode</h2>
|
||||||
|
<button onClick={this.toggleChildren}>toggle children</button>
|
||||||
|
<Tree
|
||||||
|
class='myCls'
|
||||||
|
showLine
|
||||||
|
checkable
|
||||||
|
selectable={ false }
|
||||||
|
defaultExpandAll
|
||||||
|
onExpand={this.onExpand}
|
||||||
|
defaultSelectedKeys={this.defaultSelectedKeys}
|
||||||
|
defaultCheckedKeys={this.defaultCheckedKeys}
|
||||||
|
onSelect={this.onSelect}
|
||||||
|
onCheck={this.onCheck}
|
||||||
|
>
|
||||||
|
<TreeNode title='parent 1' key='0-0'>
|
||||||
|
<TreeNode title='parent 1-1' key='0-0-1'>
|
||||||
|
<TreeNode title='parent 1-1-0' key='0-0-1-0' disableCheckbox />
|
||||||
|
<TreeNode title='parent 1-1-1' key='0-0-1-1' />
|
||||||
|
</TreeNode>
|
||||||
|
{this.showMore ? <TreeNode title='parent 2-1' key='0-0-2'>
|
||||||
|
<TreeNode title='parent 2-1-0' key='0-0-2-0' disableCheckbox />
|
||||||
|
<TreeNode title='parent 2-1-1' key='0-0-2-1' />
|
||||||
|
</TreeNode> : null}
|
||||||
|
</TreeNode>
|
||||||
|
</Tree>
|
||||||
|
</div>)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
.rc-tree li a.rc-tree-node-selected{
|
||||||
|
.cus-label {
|
||||||
|
background-color: white;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
module.exports = require('./src/')
|
|
@ -0,0 +1,602 @@
|
||||||
|
import PropTypes from '../../_util/vue-types'
|
||||||
|
import classNames from 'classnames'
|
||||||
|
import warning from 'warning'
|
||||||
|
import { initDefaultProps, getOptionProps } from '../../_util/props-util'
|
||||||
|
import { cloneElement } from '../../_util/vnode'
|
||||||
|
import BaseMixin from '../../_util/BaseMixin'
|
||||||
|
import {
|
||||||
|
traverseTreeNodes, getStrictlyValue,
|
||||||
|
getFullKeyList, getPosition, getDragNodesKeys,
|
||||||
|
calcExpandedKeys, calcSelectedKeys,
|
||||||
|
calcCheckedKeys, calcDropPosition,
|
||||||
|
arrAdd, arrDel, posToArr,
|
||||||
|
} from './util'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thought we still use `cloneElement` to pass `key`,
|
||||||
|
* other props can pass with context for future refactor.
|
||||||
|
*/
|
||||||
|
export const contextTypes = {
|
||||||
|
rcTree: PropTypes.shape({
|
||||||
|
root: PropTypes.object,
|
||||||
|
|
||||||
|
prefixCls: PropTypes.string,
|
||||||
|
selectable: PropTypes.bool,
|
||||||
|
showIcon: PropTypes.bool,
|
||||||
|
draggable: PropTypes.bool,
|
||||||
|
checkable: PropTypes.oneOfType([
|
||||||
|
PropTypes.bool,
|
||||||
|
PropTypes.object,
|
||||||
|
]),
|
||||||
|
checkStrictly: PropTypes.bool,
|
||||||
|
disabled: PropTypes.bool,
|
||||||
|
openTransitionName: PropTypes.string,
|
||||||
|
openAnimation: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
||||||
|
|
||||||
|
loadData: PropTypes.func,
|
||||||
|
filterTreeNode: PropTypes.func,
|
||||||
|
renderTreeNode: PropTypes.func,
|
||||||
|
|
||||||
|
isKeyChecked: PropTypes.func,
|
||||||
|
|
||||||
|
// onNodeExpand: PropTypes.func,
|
||||||
|
// onNodeSelect: PropTypes.func,
|
||||||
|
// onNodeMouseEnter: PropTypes.func,
|
||||||
|
// onNodeMouseLeave: PropTypes.func,
|
||||||
|
// onNodeContextMenu: PropTypes.func,
|
||||||
|
// onNodeDragStart: PropTypes.func,
|
||||||
|
// onNodeDragEnter: PropTypes.func,
|
||||||
|
// onNodeDragOver: PropTypes.func,
|
||||||
|
// onNodeDragLeave: PropTypes.func,
|
||||||
|
// onNodeDragEnd: PropTypes.func,
|
||||||
|
// onNodeDrop: PropTypes.func,
|
||||||
|
// onBatchNodeCheck: PropTypes.func,
|
||||||
|
// onCheckConductFinished: PropTypes.func,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
const Tree = {
|
||||||
|
name: 'Tree',
|
||||||
|
mixins: [BaseMixin],
|
||||||
|
props: initDefaultProps({
|
||||||
|
prefixCls: PropTypes.string,
|
||||||
|
showLine: PropTypes.bool,
|
||||||
|
showIcon: PropTypes.bool,
|
||||||
|
focusable: PropTypes.bool,
|
||||||
|
selectable: PropTypes.bool,
|
||||||
|
disabled: PropTypes.bool,
|
||||||
|
multiple: PropTypes.bool,
|
||||||
|
checkable: PropTypes.oneOfType([
|
||||||
|
PropTypes.bool,
|
||||||
|
PropTypes.node,
|
||||||
|
]),
|
||||||
|
checkStrictly: PropTypes.bool,
|
||||||
|
draggable: PropTypes.bool,
|
||||||
|
autoExpandParent: PropTypes.bool,
|
||||||
|
defaultExpandAll: PropTypes.bool,
|
||||||
|
defaultExpandedKeys: PropTypes.arrayOf(PropTypes.string),
|
||||||
|
expandedKeys: PropTypes.arrayOf(PropTypes.string),
|
||||||
|
defaultCheckedKeys: PropTypes.arrayOf(PropTypes.string),
|
||||||
|
checkedKeys: PropTypes.oneOfType([
|
||||||
|
PropTypes.arrayOf(PropTypes.string),
|
||||||
|
PropTypes.object,
|
||||||
|
]),
|
||||||
|
defaultSelectedKeys: PropTypes.arrayOf(PropTypes.string),
|
||||||
|
selectedKeys: PropTypes.arrayOf(PropTypes.string),
|
||||||
|
// onExpand: PropTypes.func,
|
||||||
|
// onCheck: PropTypes.func,
|
||||||
|
// onSelect: PropTypes.func,
|
||||||
|
loadData: PropTypes.func,
|
||||||
|
// onMouseEnter: PropTypes.func,
|
||||||
|
// onMouseLeave: PropTypes.func,
|
||||||
|
// onRightClick: PropTypes.func,
|
||||||
|
// onDragStart: PropTypes.func,
|
||||||
|
// onDragEnter: PropTypes.func,
|
||||||
|
// onDragOver: PropTypes.func,
|
||||||
|
// onDragLeave: PropTypes.func,
|
||||||
|
// onDragEnd: PropTypes.func,
|
||||||
|
// onDrop: PropTypes.func,
|
||||||
|
filterTreeNode: PropTypes.func,
|
||||||
|
openTransitionName: PropTypes.string,
|
||||||
|
openAnimation: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
||||||
|
children: PropTypes.any,
|
||||||
|
}, {
|
||||||
|
prefixCls: 'rc-tree',
|
||||||
|
showLine: false,
|
||||||
|
showIcon: true,
|
||||||
|
selectable: true,
|
||||||
|
multiple: false,
|
||||||
|
checkable: false,
|
||||||
|
disabled: false,
|
||||||
|
checkStrictly: false,
|
||||||
|
draggable: false,
|
||||||
|
autoExpandParent: true,
|
||||||
|
defaultExpandAll: false,
|
||||||
|
defaultExpandedKeys: [],
|
||||||
|
defaultCheckedKeys: [],
|
||||||
|
defaultSelectedKeys: [],
|
||||||
|
}),
|
||||||
|
|
||||||
|
// static childContextTypes = contextTypes;
|
||||||
|
|
||||||
|
data () {
|
||||||
|
const props = getOptionProps(this)
|
||||||
|
const {
|
||||||
|
defaultExpandAll,
|
||||||
|
defaultExpandedKeys,
|
||||||
|
defaultCheckedKeys,
|
||||||
|
defaultSelectedKeys,
|
||||||
|
} = props
|
||||||
|
const children = this.$slots.default
|
||||||
|
// Sync state with props
|
||||||
|
const { checkedKeys = [], halfCheckedKeys = [] } =
|
||||||
|
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(children)
|
||||||
|
: calcExpandedKeys(defaultExpandedKeys, props, children),
|
||||||
|
sSelectedKeys: calcSelectedKeys(defaultSelectedKeys, props, children),
|
||||||
|
sCheckedKeys: checkedKeys,
|
||||||
|
sHalfCheckedKeys: halfCheckedKeys,
|
||||||
|
|
||||||
|
...(this.getSyncProps(props) || {}),
|
||||||
|
dragOverNodeKey: '',
|
||||||
|
dropPosition: null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
provide () {
|
||||||
|
return {
|
||||||
|
vcTree: this,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
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
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
dragNodesKeys: getDragNodesKeys(children, node),
|
||||||
|
sExpandedKeys: arrDel(sExpandedKeys, eventKey),
|
||||||
|
})
|
||||||
|
this.__emit('dragstart', { event, node })
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [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
|
||||||
|
|
||||||
|
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(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
|
||||||
|
|
||||||
|
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 })
|
||||||
|
|
||||||
|
const eventObj = {
|
||||||
|
event: 'select',
|
||||||
|
selected: targetSelected,
|
||||||
|
node: treeNode,
|
||||||
|
selectedNodes,
|
||||||
|
}
|
||||||
|
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 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(
|
||||||
|
(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 })
|
||||||
|
this.__emit('expand', 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) {
|
||||||
|
this.__emit('mouseenter', { event, node })
|
||||||
|
},
|
||||||
|
|
||||||
|
onNodeMouseLeave (event, node) {
|
||||||
|
this.__emit('mouseleave', { event, node })
|
||||||
|
},
|
||||||
|
|
||||||
|
onNodeContextMenu (event, node) {
|
||||||
|
event.preventDefault()
|
||||||
|
this.__emit('rightClick', { event, node })
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkSync('expandedKeys')) {
|
||||||
|
newState.sExpandedKeys = calcExpandedKeys(props.expandedKeys, props, children)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkSync('selectedKeys')) {
|
||||||
|
newState.sSelectedKeys = calcSelectedKeys(props.selectedKeys, props, children)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkSync('checkedKeys')) {
|
||||||
|
const { checkedKeys = [], halfCheckedKeys = [] } =
|
||||||
|
calcCheckedKeys(props.checkedKeys, props, children) || {}
|
||||||
|
newState.sCheckedKeys = checkedKeys
|
||||||
|
newState.sHalfCheckedKeys = halfCheckedKeys
|
||||||
|
}
|
||||||
|
|
||||||
|
return needSync ? newState : null
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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, focusable,
|
||||||
|
showLine,
|
||||||
|
$slots: { default: children = [] },
|
||||||
|
} = this
|
||||||
|
const domProps = {}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ul
|
||||||
|
{...domProps}
|
||||||
|
class={classNames(prefixCls, {
|
||||||
|
[`${prefixCls}-show-line`]: showLine,
|
||||||
|
})}
|
||||||
|
role='tree-node'
|
||||||
|
unselectable='on'
|
||||||
|
tabIndex={focusable ? '0' : null}
|
||||||
|
onKeydown={focusable ? this.onKeydown : () => {}}
|
||||||
|
>
|
||||||
|
{children.map(this.renderTreeNode)}
|
||||||
|
</ul>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Tree
|
|
@ -0,0 +1,589 @@
|
||||||
|
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'
|
||||||
|
import BaseMixin from '../../_util/BaseMixin'
|
||||||
|
import getTransitionProps from '../../_util/getTransitionProps'
|
||||||
|
|
||||||
|
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,
|
||||||
|
vcTreeNode: PropTypes.shape({
|
||||||
|
onUpCheckConduct: PropTypes.func,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
const TreeNode = {
|
||||||
|
name: 'TreeNode',
|
||||||
|
mixins: [BaseMixin],
|
||||||
|
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.any,
|
||||||
|
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: {
|
||||||
|
vcTree: { default: {}},
|
||||||
|
},
|
||||||
|
provide () {
|
||||||
|
return {
|
||||||
|
vcTree: this.vcTree,
|
||||||
|
vcTreeNode: this,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Isomorphic needn't load data in server side
|
||||||
|
mounted () {
|
||||||
|
this.syncLoadData(this.$props)
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
expanded (val) {
|
||||||
|
this.syncLoadData({ expanded: val })
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
// 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 { vcTree: { checkStrictly, isKeyChecked, onBatchNodeCheck }} = this
|
||||||
|
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 { vcTree: { onNodeSelect }} = this
|
||||||
|
e.preventDefault()
|
||||||
|
onNodeSelect(e, this)
|
||||||
|
},
|
||||||
|
|
||||||
|
onCheck (e) {
|
||||||
|
if (this.isDisabled()) return
|
||||||
|
|
||||||
|
const { disableCheckbox, checked, eventKey } = this
|
||||||
|
const {
|
||||||
|
vcTree: { checkable, onBatchNodeCheck, onCheckConductFinished },
|
||||||
|
vcTreeNode: { onUpCheckConduct } = {},
|
||||||
|
} = this
|
||||||
|
|
||||||
|
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 { vcTree: { onNodeMouseEnter }} = this
|
||||||
|
onNodeMouseEnter(e, this)
|
||||||
|
},
|
||||||
|
|
||||||
|
onMouseLeave (e) {
|
||||||
|
const { vcTree: { onNodeMouseLeave }} = this
|
||||||
|
onNodeMouseLeave(e, this)
|
||||||
|
},
|
||||||
|
|
||||||
|
onContextMenu (e) {
|
||||||
|
const { vcTree: { onNodeContextMenu }} = this
|
||||||
|
onNodeContextMenu(e, this)
|
||||||
|
},
|
||||||
|
|
||||||
|
onDragStart (e) {
|
||||||
|
const { vcTree: { onNodeDragStart }} = this
|
||||||
|
|
||||||
|
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 { vcTree: { onNodeDragEnter }} = this
|
||||||
|
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
onNodeDragEnter(e, this)
|
||||||
|
},
|
||||||
|
|
||||||
|
onDragOver (e) {
|
||||||
|
const { vcTree: { onNodeDragOver }} = this
|
||||||
|
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
onNodeDragOver(e, this)
|
||||||
|
},
|
||||||
|
|
||||||
|
onDragLeave (e) {
|
||||||
|
const { vcTree: { onNodeDragLeave }} = this
|
||||||
|
|
||||||
|
e.stopPropagation()
|
||||||
|
onNodeDragLeave(e, this)
|
||||||
|
},
|
||||||
|
|
||||||
|
onDragEnd (e) {
|
||||||
|
const { vcTree: { onNodeDragEnd }} = this
|
||||||
|
|
||||||
|
e.stopPropagation()
|
||||||
|
this.setState({
|
||||||
|
dragNodeHighlight: false,
|
||||||
|
})
|
||||||
|
onNodeDragEnd(e, this)
|
||||||
|
},
|
||||||
|
|
||||||
|
onDrop (e) {
|
||||||
|
const { vcTree: { onNodeDrop }} = this
|
||||||
|
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
this.setState({
|
||||||
|
dragNodeHighlight: false,
|
||||||
|
})
|
||||||
|
onNodeDrop(e, this)
|
||||||
|
},
|
||||||
|
|
||||||
|
// Disabled item still can be switch
|
||||||
|
onExpand (e) {
|
||||||
|
const { vcTree: { onNodeExpand }} = this
|
||||||
|
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.isLeaf2()) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return expanded ? ICON_OPEN : ICON_CLOSE
|
||||||
|
},
|
||||||
|
|
||||||
|
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 <span class={`${prefixCls}-switcher ${prefixCls}-switcher-noop`} />
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
class={classNames(
|
||||||
|
`${prefixCls}-switcher`,
|
||||||
|
`${prefixCls}-switcher_${expanded ? ICON_OPEN : ICON_CLOSE}`,
|
||||||
|
)}
|
||||||
|
onClick={this.onExpand}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 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 (
|
||||||
|
<span
|
||||||
|
class={classNames(
|
||||||
|
`${prefixCls}-checkbox`,
|
||||||
|
checked && `${prefixCls}-checkbox-checked`,
|
||||||
|
!checked && halfChecked && `${prefixCls}-checkbox-indeterminate`,
|
||||||
|
(disabled || disableCheckbox) && `${prefixCls}-checkbox-disabled`,
|
||||||
|
)}
|
||||||
|
onClick={this.onCheck}
|
||||||
|
>
|
||||||
|
{$custom}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
renderIcon () {
|
||||||
|
const { loadStatus } = this
|
||||||
|
const { vcTree: { prefixCls }} = this
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
class={classNames(
|
||||||
|
`${prefixCls}-iconEle`,
|
||||||
|
`${prefixCls}-icon__${this.getNodeState() || 'docu'}`,
|
||||||
|
(loadStatus === LOAD_STATUS_LOADING) && `${prefixCls}-icon_loading`,
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 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 ? (
|
||||||
|
<span
|
||||||
|
class={classNames(
|
||||||
|
`${prefixCls}-iconEle`,
|
||||||
|
`${prefixCls}-icon__customize`,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{typeof icon === 'function'
|
||||||
|
? icon(this.$props) : icon}
|
||||||
|
</span>
|
||||||
|
) : this.renderIcon()
|
||||||
|
} else if (loadData && loadStatus === LOAD_STATUS_LOADING) {
|
||||||
|
$icon = this.renderIcon()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Title
|
||||||
|
const $title = <span class={`${prefixCls}-title`}>{title}</span>
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
ref='selectHandle'
|
||||||
|
title={typeof title === 'string' ? title : ''}
|
||||||
|
class={classNames(
|
||||||
|
`${wrapClass}`,
|
||||||
|
`${wrapClass}-${this.getNodeState() || 'normal'}`,
|
||||||
|
(!disabled && (selected || dragNodeHighlight)) && `${prefixCls}-node-selected`,
|
||||||
|
(!disabled && draggable) && 'draggable'
|
||||||
|
)}
|
||||||
|
draggable={(!disabled && draggable) || undefined}
|
||||||
|
aria-grabbed={(!disabled && draggable) || undefined}
|
||||||
|
|
||||||
|
onMouseenter={this.onMouseEnter}
|
||||||
|
onMouseleave={this.onMouseLeave}
|
||||||
|
onContexmenu={this.onContextMenu}
|
||||||
|
onClick={this.onSelectorClick}
|
||||||
|
onDragstart={this.onDragStart}
|
||||||
|
>
|
||||||
|
{$icon}{$title}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 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 = (
|
||||||
|
<ul
|
||||||
|
v-show={expanded}
|
||||||
|
class={classNames(
|
||||||
|
`${prefixCls}-child-tree`,
|
||||||
|
expanded && `${prefixCls}-child-tree-open`,
|
||||||
|
)}
|
||||||
|
data-expanded={expanded}
|
||||||
|
>
|
||||||
|
{nodeList.map((node, index) => (
|
||||||
|
renderTreeNode(node, index, pos)
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<transition
|
||||||
|
{...animProps}
|
||||||
|
>
|
||||||
|
{$children}
|
||||||
|
</transition>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const {
|
||||||
|
dragOver, dragOverGapTop, dragOverGapBottom,
|
||||||
|
} = this
|
||||||
|
const { vcTree: {
|
||||||
|
prefixCls,
|
||||||
|
filterTreeNode,
|
||||||
|
}} = this
|
||||||
|
const disabled = this.isDisabled()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li
|
||||||
|
class={{
|
||||||
|
[`${prefixCls}-treenode-disabled`]: disabled,
|
||||||
|
'drag-over': !disabled && dragOver,
|
||||||
|
'drag-over-gap-top': !disabled && dragOverGapTop,
|
||||||
|
'drag-over-gap-bottom': !disabled && dragOverGapBottom,
|
||||||
|
'filter-node': filterTreeNode && filterTreeNode(this),
|
||||||
|
}}
|
||||||
|
onDragenter={this.onDragEnter}
|
||||||
|
onDragover={this.onDragOver}
|
||||||
|
onDragleave={this.onDragLeave}
|
||||||
|
onDrop={this.onDrop}
|
||||||
|
onDragend={this.onDragEnd}
|
||||||
|
>
|
||||||
|
{this.renderSwitcher()}
|
||||||
|
{this.renderCheckbox()}
|
||||||
|
{this.renderSelector()}
|
||||||
|
{this.renderChildren()}
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
TreeNode.isTreeNode = 1
|
||||||
|
|
||||||
|
export default TreeNode
|
|
@ -0,0 +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 (
|
||||||
|
<Tree {...treeProps}>{$slots.default}</Tree>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
export { TreeNode }
|
||||||
|
export default NewTree
|
|
@ -0,0 +1,398 @@
|
||||||
|
/* eslint no-loop-func: 0*/
|
||||||
|
import warning from 'warning'
|
||||||
|
import { getSlotOptions, getOptionProps } from '../../_util/props-util'
|
||||||
|
|
||||||
|
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 = []) {
|
||||||
|
return children
|
||||||
|
.filter(child => getSlotOptions(child).isTreeNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isCheckDisabled (node) {
|
||||||
|
const { disabled, disableCheckbox } = getOptionProps(node) || {}
|
||||||
|
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.componentOptions.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 = []
|
||||||
|
childList.forEach((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
|
||||||
|
childList.forEach((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 } = getOptionProps(node)
|
||||||
|
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, children = []) {
|
||||||
|
if (!keyList) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
const { autoExpandParent } = 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, children = []) {
|
||||||
|
const { checkable, 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)
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "vue-antd-ui",
|
"name": "vue-antd-ui",
|
||||||
"version": "0.1.0",
|
"version": "0.1.1",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -206,7 +206,7 @@
|
||||||
},
|
},
|
||||||
"abab": {
|
"abab": {
|
||||||
"version": "1.0.4",
|
"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=",
|
"integrity": "sha1-X6rZwsB/YN12dw9xzwJbYqY8/U4=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true
|
"optional": true
|
||||||
|
@ -251,7 +251,7 @@
|
||||||
},
|
},
|
||||||
"acorn-globals": {
|
"acorn-globals": {
|
||||||
"version": "1.0.9",
|
"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=",
|
"integrity": "sha1-VbtemGkVB7dFedBRNBMhfDgMVM8=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true,
|
"optional": true,
|
||||||
|
@ -261,7 +261,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"acorn": {
|
"acorn": {
|
||||||
"version": "2.7.0",
|
"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=",
|
"integrity": "sha1-q259nYhqrKiwhbwzEreaGYQz8Oc=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true
|
"optional": true
|
||||||
|
@ -2491,7 +2491,7 @@
|
||||||
},
|
},
|
||||||
"component-classes": {
|
"component-classes": {
|
||||||
"version": "1.2.6",
|
"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=",
|
"integrity": "sha1-xkI5TDYYpNiwuJGe/Mu9kw5c1pE=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"component-indexof": "0.0.3"
|
"component-indexof": "0.0.3"
|
||||||
|
@ -3086,13 +3086,13 @@
|
||||||
},
|
},
|
||||||
"cssom": {
|
"cssom": {
|
||||||
"version": "0.3.2",
|
"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=",
|
"integrity": "sha1-uANhcMefB6kP8vFuIihAJ6JDhIs=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"cssstyle": {
|
"cssstyle": {
|
||||||
"version": "0.2.37",
|
"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=",
|
"integrity": "sha1-VBCXI0yyUTyDzu06zdwn/yeYfVQ=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true,
|
"optional": true,
|
||||||
|
@ -8076,7 +8076,7 @@
|
||||||
},
|
},
|
||||||
"jsdom": {
|
"jsdom": {
|
||||||
"version": "7.2.2",
|
"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=",
|
"integrity": "sha1-QLQCdwwr2iNGkJa+6Rq2deOx/G4=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true,
|
"optional": true,
|
||||||
|
@ -8100,14 +8100,14 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"acorn": {
|
"acorn": {
|
||||||
"version": "2.7.0",
|
"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=",
|
"integrity": "sha1-q259nYhqrKiwhbwzEreaGYQz8Oc=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
"parse5": {
|
"parse5": {
|
||||||
"version": "1.5.1",
|
"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=",
|
"integrity": "sha1-m387DeMr543CQBsXVzzK8Pb1nZQ=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true
|
"optional": true
|
||||||
|
@ -8897,11 +8897,6 @@
|
||||||
"integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=",
|
"integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=",
|
||||||
"dev": true
|
"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": {
|
"lodash.create": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npm.taobao.org/lodash.create/download/lodash.create-3.1.1.tgz",
|
"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._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": {
|
"lodash.escape": {
|
||||||
"version": "3.2.0",
|
"version": "3.2.0",
|
||||||
"resolved": "http://registry.npm.taobao.org/lodash.escape/download/lodash.escape-3.2.0.tgz",
|
"resolved": "http://registry.npm.taobao.org/lodash.escape/download/lodash.escape-3.2.0.tgz",
|
||||||
|
@ -8945,11 +8935,6 @@
|
||||||
"integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=",
|
"integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=",
|
||||||
"dev": true
|
"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": {
|
"lodash.isplainobject": {
|
||||||
"version": "4.0.6",
|
"version": "4.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
||||||
|
@ -15976,7 +15961,7 @@
|
||||||
},
|
},
|
||||||
"symbol-tree": {
|
"symbol-tree": {
|
||||||
"version": "3.2.2",
|
"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=",
|
"integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true
|
"optional": true
|
||||||
|
@ -16267,7 +16252,7 @@
|
||||||
},
|
},
|
||||||
"tr46": {
|
"tr46": {
|
||||||
"version": "0.0.3",
|
"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=",
|
"integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true
|
"optional": true
|
||||||
|
@ -17031,7 +17016,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cheerio": {
|
"cheerio": {
|
||||||
"version": "0.20.0",
|
"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=",
|
"integrity": "sha1-XHEPK6uVZTJyhCugHG6mGzVF7DU=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
|
@ -17045,7 +17030,7 @@
|
||||||
},
|
},
|
||||||
"domhandler": {
|
"domhandler": {
|
||||||
"version": "2.3.0",
|
"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=",
|
"integrity": "sha1-LeWaCCLVAn+r/28DLCsloqir5zg=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
|
@ -17054,7 +17039,7 @@
|
||||||
},
|
},
|
||||||
"domutils": {
|
"domutils": {
|
||||||
"version": "1.5.1",
|
"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=",
|
"integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
|
@ -17064,7 +17049,7 @@
|
||||||
},
|
},
|
||||||
"htmlparser2": {
|
"htmlparser2": {
|
||||||
"version": "3.8.3",
|
"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=",
|
"integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
|
@ -17077,7 +17062,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"entities": {
|
"entities": {
|
||||||
"version": "1.0.0",
|
"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=",
|
"integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=",
|
||||||
"dev": true
|
"dev": true
|
||||||
}
|
}
|
||||||
|
@ -17085,13 +17070,13 @@
|
||||||
},
|
},
|
||||||
"isarray": {
|
"isarray": {
|
||||||
"version": "0.0.1",
|
"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=",
|
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"loader-utils": {
|
"loader-utils": {
|
||||||
"version": "0.2.17",
|
"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=",
|
"integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
|
@ -17103,7 +17088,7 @@
|
||||||
},
|
},
|
||||||
"readable-stream": {
|
"readable-stream": {
|
||||||
"version": "1.1.14",
|
"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=",
|
"integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
|
@ -17115,7 +17100,7 @@
|
||||||
},
|
},
|
||||||
"string_decoder": {
|
"string_decoder": {
|
||||||
"version": "0.10.31",
|
"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=",
|
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
|
||||||
"dev": true
|
"dev": true
|
||||||
}
|
}
|
||||||
|
@ -17319,7 +17304,7 @@
|
||||||
},
|
},
|
||||||
"webidl-conversions": {
|
"webidl-conversions": {
|
||||||
"version": "2.0.1",
|
"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=",
|
"integrity": "sha1-O/glj30xjHRDw28uFpQCoaZwNQY=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true
|
"optional": true
|
||||||
|
@ -18038,7 +18023,7 @@
|
||||||
},
|
},
|
||||||
"whatwg-url-compat": {
|
"whatwg-url-compat": {
|
||||||
"version": "0.6.5",
|
"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=",
|
"integrity": "sha1-AImBEa9om7CXVBzVpFymyHmERb8=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true,
|
"optional": true,
|
||||||
|
@ -18168,7 +18153,7 @@
|
||||||
},
|
},
|
||||||
"xml-name-validator": {
|
"xml-name-validator": {
|
||||||
"version": "2.0.1",
|
"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=",
|
"integrity": "sha1-TYuPHszTQZqjYgYb7O9RXh5VljU=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true
|
"optional": true
|
||||||
|
|
Loading…
Reference in New Issue