From 8f7eb876eadcb0ef1aec97777d3daf8d6ac0f7d8 Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Mon, 4 Jan 2021 16:27:15 +0800 Subject: [PATCH 01/71] fix: #1491 --- CHANGELOG.md | 3 +++ components/select-tree/SelectTreeHook.js | 17 ++++++++++++++--- .../components/tree/style/index.scss | 1 - 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e71106fd3..85fbbc76b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # 更新日志 +## 3.3.1 + +- 修复 `SelectTree` 搜索输入框在输入值时候失焦问题 [#1491](https://github.com/XiaoMi/hiui/issues/1491) ## 3.3.0 - 新增 `Card` 模式模式下 loading 加载中状态 [#1454](https://github.com/XiaoMi/hiui/issues/1454) - 新增 `Table` loading 加载中状态 [#1466](https://github.com/XiaoMi/hiui/issues/1466) diff --git a/components/select-tree/SelectTreeHook.js b/components/select-tree/SelectTreeHook.js index 858cef036..d4ffcadb6 100644 --- a/components/select-tree/SelectTreeHook.js +++ b/components/select-tree/SelectTreeHook.js @@ -430,13 +430,12 @@ const SelectTree = ({ searchTreeNode(val) } } - const debouncedFilterItems = _.debounce(changeEvents, 100) + const debouncedFilterItems = _.debounce(changeEvents, 300) const searchable = searchMode === 'filter' || searchMode === 'highlight' // 按键操作 const handleKeyDown = useCallback( (evt) => { - evt.stopPropagation() // space if ( evt.keyCode === 32 && @@ -444,36 +443,48 @@ const SelectTree = ({ !show ) { evt.preventDefault() + evt.stopPropagation() setShow(true) } // esc if (evt.keyCode === 27) { + evt.stopPropagation() + setShow(false) } if (show) { // down if (evt.keyCode === 40) { + evt.stopPropagation() evt.preventDefault() setActiveId(moveFocusedIndex('down', activeId, selectTreeRoot)) } // up if (evt.keyCode === 38) { evt.preventDefault() + evt.stopPropagation() + setActiveId(moveFocusedIndex('up', activeId, selectTreeRoot)) } // right if (evt.keyCode === 39) { evt.preventDefault() + evt.stopPropagation() + rightHandle({ activeId, flattenData, expandIds, expandEvents, setActiveId, mode }) } // left if (evt.keyCode === 37) { evt.preventDefault() + evt.stopPropagation() + leftHandle({ activeId, flattenData, expandIds, expandEvents, setActiveId, mode }) } // space 选中 if (evt.keyCode === 32 && !document.activeElement.classList.value.includes('hi-selecttree__searchinput')) { evt.preventDefault() + evt.stopPropagation() + if (mode !== 'breadcrumb') { type === 'multiple' ? checkedEvents( @@ -545,8 +556,8 @@ const SelectTree = ({ className="hi-selecttree__searchinput" placeholder={localeDatas.selectTree.search} clearable="true" - value={searchValue} clearabletrigger="always" + defaultValue={searchValue} onKeyDown={(e) => { if (e.keyCode === '13') { searchTreeNode(e.target.value) diff --git a/components/select-tree/components/tree/style/index.scss b/components/select-tree/components/tree/style/index.scss index deb1926ae..00d8887dd 100644 --- a/components/select-tree/components/tree/style/index.scss +++ b/components/select-tree/components/tree/style/index.scss @@ -10,7 +10,6 @@ $tree: 'hi-select-tree' !default; &--empty { display: inline-block; - margin-bottom: 12px; } &__nodes { From 38fe9d75c9ff8ecb2354a998b5538913c6d5bd24 Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Mon, 4 Jan 2021 16:29:23 +0800 Subject: [PATCH 02/71] chore: changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 85fbbc76b..1442fbcd4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## 3.3.1 -- 修复 `SelectTree` 搜索输入框在输入值时候失焦问题 [#1491](https://github.com/XiaoMi/hiui/issues/1491) +- 修复 `SelectTree` 搜索输入框在输入值时失焦问题 [#1491](https://github.com/XiaoMi/hiui/issues/1491) ## 3.3.0 - 新增 `Card` 模式模式下 loading 加载中状态 [#1454](https://github.com/XiaoMi/hiui/issues/1454) - 新增 `Table` loading 加载中状态 [#1466](https://github.com/XiaoMi/hiui/issues/1466) From 1631093a8164668e18908bfdfbfb44fe152cc73c Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Mon, 4 Jan 2021 16:30:23 +0800 Subject: [PATCH 03/71] chore: changelog --- components/_util/EventEmitter.js | 1 - 1 file changed, 1 deletion(-) diff --git a/components/_util/EventEmitter.js b/components/_util/EventEmitter.js index a271531ea..e9eb39969 100644 --- a/components/_util/EventEmitter.js +++ b/components/_util/EventEmitter.js @@ -10,7 +10,6 @@ class EventEmitter { // 触发事件 emit(type, ...arg) { - console.log(this.event) this.event[type] && this.event[type](...arg) } From 507c2c7928de5f0fd590b1fe12f70f76c629ac84 Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Mon, 4 Jan 2021 16:34:17 +0800 Subject: [PATCH 04/71] fix: empty margin --- components/select-tree/components/tree/style/index.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/components/select-tree/components/tree/style/index.scss b/components/select-tree/components/tree/style/index.scss index 00d8887dd..b4faba1d3 100644 --- a/components/select-tree/components/tree/style/index.scss +++ b/components/select-tree/components/tree/style/index.scss @@ -99,6 +99,7 @@ $tree: 'hi-select-tree' !default; .hi-select-tree--empty { padding: 12px 0 0 12px; + margin-bottom: 12px; } } From 3bed3427d9ed89c076f63895a2d754894f4936c8 Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Tue, 5 Jan 2021 17:23:44 +0800 Subject: [PATCH 05/71] feat: popper auto end&start --- components/popper/utils/positionUtils.js | 117 +++++++++++++++-------- docs/demo/date-picker/section-normal.jsx | 1 + 2 files changed, 76 insertions(+), 42 deletions(-) diff --git a/components/popper/utils/positionUtils.js b/components/popper/utils/positionUtils.js index 59bf8a4ea..bd900ef06 100644 --- a/components/popper/utils/positionUtils.js +++ b/components/popper/utils/positionUtils.js @@ -60,57 +60,90 @@ const positionAuto = (attachEleRect, popperHeight, popperRef, height, containerH } return placement } -const getPlacement = (attachEleRect, container, props, state) => { - let { popperHeight, popperRef } = state +const caclLeftOrRightPlacement = (leftPlacement, RightPlacement, popperRef, widthConstant) => { + let { width, popperWidth, poperLeft, containerWidth } = widthConstant + // 计算popper在元素上面或下面 + let placement = RightPlacement + popperWidth === undefined && (popperWidth = 0) // 自动探测边界,第一次时需设置为不可见,否则会闪跳,用来设置class hi-popper__content--hide + if (popperRef || width) { + // 元素已挂载到dom且当前popper处于显示状态 + if (width) { + popperWidth = width + } else if (popperRef.clientWidth && popperWidth !== popperRef.clientWidth) { + popperWidth = popperRef.clientWidth + } + poperLeft += popperWidth + if (poperLeft >= containerWidth) { + placement = leftPlacement + } + } + return placement +} +const caclBottomOrTopPlacement = (bottomPlacement, topPlacement, popperRef, heightConstant, widthConstant) => { + let { popperHeight, height, poperTop, containerHeight } = heightConstant + // 计算popper在元素上面或下面 + let placement = bottomPlacement + popperHeight === undefined && (popperHeight = 0) // 自动探测边界,第一次时需设置为不可见,否则会闪跳,用来设置class hi-popper__content--hide + if (popperRef || height) { + // 元素已挂载到dom且当前popper处于显示状态 + if (height) { + popperHeight = height + } else if (popperRef.clientHeight && popperHeight !== popperRef.clientHeight) { + popperHeight = popperRef.clientHeight + } + poperTop += popperHeight + if (poperTop >= containerHeight) { + placement = topPlacement + } + } + const topOrbottom = placement === 'top-start' ? 'top' : 'bottom' + placement = topOrbottom + '-' + caclLeftOrRightPlacement('end', 'start', popperRef, widthConstant) + return placement +} +const getPlacement = (attachEleRect, container, props, state, attachEleWidth) => { + const { popperHeight, popperRef, popperWidth } = state let { attachEle, placement, height, width = 0, leftGap = 0 } = props if (!attachEle) return let containerHeight = document.documentElement.clientHeight || document.body.clientHeight + let containerWidth = document.documentElement.clientWidth || document.body.clientWidth if (isFixed(attachEle)) { containerHeight = container.clientHeight + containerWidth = container.clientWidth } if (isBody(container)) { containerHeight = document.documentElement.clientHeight || document.body.clientHeight + containerWidth = document.documentElement.clientWidth || document.body.clientWidth } - let poperTop = attachEleRect.top + attachEleRect.height - const caclBottomOrTopPlacement = (bottomPlacement, topPlacement) => { - // 计算popper在元素上面或下面 - placement = bottomPlacement - popperHeight === undefined && (popperHeight = 0) // 自动探测边界,第一次时需设置为不可见,否则会闪跳,用来设置class hi-popper__content--hide - if (popperRef || height) { - // 元素已挂载到dom且当前popper处于显示状态 - if (height) { - popperHeight = height - } else if (popperRef.clientHeight && popperHeight !== popperRef.clientHeight) { - popperHeight = popperRef.clientHeight - } - poperTop += popperHeight - if (poperTop >= containerHeight) { - placement = topPlacement - } - } - } - const caclLeftOrRightPlacement = (leftPlacement, RightPlacement) => { - // 计算popper在元素上面或下面 - placement = leftPlacement - const _width = popperRef ? popperRef.clientWidth : width - if (attachEleRect.right > _width + leftGap) { - placement = RightPlacement - } - if (attachEleRect.left > _width + leftGap) { - placement = leftPlacement - } + const poperTop = attachEleRect.top + attachEleRect.height + const poperLeft = attachEleRect.left + attachEleRect.width + + const widthConstant = { + width, + popperWidth, + poperLeft, + containerWidth, + attachEleWidth, + leftGap + } + const heightConstant = { + popperHeight, + height, + poperTop, + containerHeight, + leftGap } + if (placement === 'top-bottom-start') { - caclBottomOrTopPlacement('bottom-start', 'top-start') + placement = caclBottomOrTopPlacement('bottom-start', 'top-start', popperRef, heightConstant, widthConstant) } else if (placement === 'top-bottom') { - caclBottomOrTopPlacement('bottom', 'top') + placement = caclBottomOrTopPlacement('bottom', 'top', popperRef, heightConstant, widthConstant) } else if (placement === 'left-right-start') { - caclLeftOrRightPlacement('left-start', 'right-start') + placement = caclLeftOrRightPlacement('left-start', 'right-start', popperRef, widthConstant) } else if (placement === 'left-right') { - caclLeftOrRightPlacement('left', 'right') + placement = caclLeftOrRightPlacement('left', 'right', popperRef, width, widthConstant) } else if (placement === 'auto') { positionAuto(attachEleRect, popperHeight, popperRef, height, containerHeight) } @@ -120,7 +153,7 @@ export const getOffset = (props, state, status) => { let { attachEle, topGap, leftGap, width, container, preventOverflow } = props if (!attachEle) return - const { popperHeight } = state + const { popperHeight, popperWidth } = state let rect = attachEle.getBoundingClientRect() if (isFixed(attachEle) && !isBody(container)) { @@ -138,9 +171,9 @@ export const getOffset = (props, state, status) => { let top = rect.top + _scrollTop let left = rect.left + _scrollLeft - width = width === false ? '' : width === undefined ? rect.width : width - - let placement = getPlacement(rect, container, props, state) || 'bottom-start' + width = width === false ? popperWidth : width === undefined ? rect.width : width + const _width = width + let placement = getPlacement(rect, container, props, state, width) || 'bottom-start' const rectHeight = rect.height switch (placement) { case 'bottom': @@ -173,11 +206,11 @@ export const getOffset = (props, state, status) => { break case 'left-start': top = top + topGap - left = left - rect.width + left = left - width break case 'left-end': top = top + rect.height - topGap - popperHeight - left = left - rect.width + left = left - width break case 'right': @@ -190,7 +223,7 @@ export const getOffset = (props, state, status) => { break case 'right-end': top = top + rect.height - topGap - popperHeight - left = left + rect.width + leftGap + left = left + width + leftGap break } @@ -199,7 +232,7 @@ export const getOffset = (props, state, status) => { } return { - width, + width: _width, top, left, placement: placement diff --git a/docs/demo/date-picker/section-normal.jsx b/docs/demo/date-picker/section-normal.jsx index 6a0dbd553..292d513b9 100644 --- a/docs/demo/date-picker/section-normal.jsx +++ b/docs/demo/date-picker/section-normal.jsx @@ -12,6 +12,7 @@ class Demo extends React.Component { render () { return ( {console.log('onChange', date, dateStr)}} /> ) From 7bdcd26f23d8ded4f0642318bafda9a13948b05a Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Tue, 5 Jan 2021 18:01:10 +0800 Subject: [PATCH 06/71] feat: #1494 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1442fbcd4..ec6d44df0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## 3.3.1 - 修复 `SelectTree` 搜索输入框在输入值时失焦问题 [#1491](https://github.com/XiaoMi/hiui/issues/1491) +- 优化组件弹出层自动计算合适的左右位置 [#1494](https://github.com/XiaoMi/hiui/issues/1494) ## 3.3.0 - 新增 `Card` 模式模式下 loading 加载中状态 [#1454](https://github.com/XiaoMi/hiui/issues/1454) - 新增 `Table` loading 加载中状态 [#1466](https://github.com/XiaoMi/hiui/issues/1466) From d6c0933338c631fb8c462ddcfb65531f719b790c Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Tue, 5 Jan 2021 18:55:02 +0800 Subject: [PATCH 07/71] feat: #1494 --- components/popper/utils/positionUtils.js | 6 +++--- docs/demo/date-picker/section-normal.jsx | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/components/popper/utils/positionUtils.js b/components/popper/utils/positionUtils.js index bd900ef06..7c945be08 100644 --- a/components/popper/utils/positionUtils.js +++ b/components/popper/utils/positionUtils.js @@ -61,7 +61,7 @@ const positionAuto = (attachEleRect, popperHeight, popperRef, height, containerH return placement } const caclLeftOrRightPlacement = (leftPlacement, RightPlacement, popperRef, widthConstant) => { - let { width, popperWidth, poperLeft, containerWidth } = widthConstant + let { width, popperWidth, poperLeft, containerWidth, attachEleWidth } = widthConstant // 计算popper在元素上面或下面 let placement = RightPlacement popperWidth === undefined && (popperWidth = 0) // 自动探测边界,第一次时需设置为不可见,否则会闪跳,用来设置class hi-popper__content--hide @@ -72,7 +72,7 @@ const caclLeftOrRightPlacement = (leftPlacement, RightPlacement, popperRef, widt } else if (popperRef.clientWidth && popperWidth !== popperRef.clientWidth) { popperWidth = popperRef.clientWidth } - poperLeft += popperWidth + poperLeft = poperLeft + (popperWidth - (attachEleWidth || 0)) if (poperLeft >= containerWidth) { placement = leftPlacement } @@ -173,7 +173,7 @@ export const getOffset = (props, state, status) => { width = width === false ? popperWidth : width === undefined ? rect.width : width const _width = width - let placement = getPlacement(rect, container, props, state, width) || 'bottom-start' + let placement = getPlacement(rect, container, props, state, rect.width) || 'bottom-start' const rectHeight = rect.height switch (placement) { case 'bottom': diff --git a/docs/demo/date-picker/section-normal.jsx b/docs/demo/date-picker/section-normal.jsx index 292d513b9..6a0dbd553 100644 --- a/docs/demo/date-picker/section-normal.jsx +++ b/docs/demo/date-picker/section-normal.jsx @@ -12,7 +12,6 @@ class Demo extends React.Component { render () { return ( {console.log('onChange', date, dateStr)}} /> ) From 9f4a35786f00d4bbe6f4063014500b32e8c6a2c3 Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Tue, 5 Jan 2021 19:05:32 +0800 Subject: [PATCH 08/71] feat: #1494 --- components/popper/utils/positionUtils.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/components/popper/utils/positionUtils.js b/components/popper/utils/positionUtils.js index 7c945be08..98bf6b818 100644 --- a/components/popper/utils/positionUtils.js +++ b/components/popper/utils/positionUtils.js @@ -64,7 +64,10 @@ const caclLeftOrRightPlacement = (leftPlacement, RightPlacement, popperRef, widt let { width, popperWidth, poperLeft, containerWidth, attachEleWidth } = widthConstant // 计算popper在元素上面或下面 let placement = RightPlacement - popperWidth === undefined && (popperWidth = 0) // 自动探测边界,第一次时需设置为不可见,否则会闪跳,用来设置class hi-popper__content--hide + if (popperWidth === undefined) { + const clientWidth = popperRef || {} + popperWidth = clientWidth || 0 + } // 自动探测边界,第一次时需设置为不可见,否则会闪跳,用来设置class hi-popper__content--hide if (popperRef || width) { // 元素已挂载到dom且当前popper处于显示状态 if (width) { From 658777d77bd79b9f0bb0d3582a7410d3ec722267 Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Tue, 5 Jan 2021 19:20:10 +0800 Subject: [PATCH 09/71] fix: #1497 --- CHANGELOG.md | 1 + components/select/SelectDropdown.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1442fbcd4..1cc38a58c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## 3.3.1 - 修复 `SelectTree` 搜索输入框在输入值时失焦问题 [#1491](https://github.com/XiaoMi/hiui/issues/1491) +- 修复 `Select` 组件分组形态 filterOption 函数无法使用 [#1497](https://github.com/XiaoMi/hiui/issues/1497) ## 3.3.0 - 新增 `Card` 模式模式下 loading 加载中状态 [#1454](https://github.com/XiaoMi/hiui/issues/1454) - 新增 `Table` loading 加载中状态 [#1466](https://github.com/XiaoMi/hiui/issues/1466) diff --git a/components/select/SelectDropdown.js b/components/select/SelectDropdown.js index 541c6cb90..d8795a733 100644 --- a/components/select/SelectDropdown.js +++ b/components/select/SelectDropdown.js @@ -219,7 +219,7 @@ const SelectDropdown = ({ ) renderGroup.push(label) filterGroupItem[transKeys(fieldNames, 'children')].forEach((item, index) => { - renderGroup.push(normalItem(item, filterItemsIndex + '-' + index, true)) + matchFilter(item) && renderGroup.push(normalItem(item, filterItemsIndex + '-' + index, true)) }) return renderGroup } From 35f1fff34b308378a1addf1e625b51790b3d2c68 Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Tue, 5 Jan 2021 19:21:09 +0800 Subject: [PATCH 10/71] fix: #1497 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1cc38a58c..3dfef6175 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ## 3.3.1 - 修复 `SelectTree` 搜索输入框在输入值时失焦问题 [#1491](https://github.com/XiaoMi/hiui/issues/1491) -- 修复 `Select` 组件分组形态 filterOption 函数无法使用 [#1497](https://github.com/XiaoMi/hiui/issues/1497) +- 修复 `Select` 组件分组形态 filterOption 函数无法使用问题 [#1497](https://github.com/XiaoMi/hiui/issues/1497) ## 3.3.0 - 新增 `Card` 模式模式下 loading 加载中状态 [#1454](https://github.com/XiaoMi/hiui/issues/1454) - 新增 `Table` loading 加载中状态 [#1466](https://github.com/XiaoMi/hiui/issues/1466) From a0322e767405438e906d9a2d0e92ff5c1ebb592d Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Tue, 5 Jan 2021 19:35:22 +0800 Subject: [PATCH 11/71] fix: #1491 --- components/select-tree/SelectTreeHook.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/select-tree/SelectTreeHook.js b/components/select-tree/SelectTreeHook.js index d4ffcadb6..5c8ad17e4 100644 --- a/components/select-tree/SelectTreeHook.js +++ b/components/select-tree/SelectTreeHook.js @@ -422,7 +422,6 @@ const SelectTree = ({ } // 搜索框的值改变时的事件 const changeEvents = (val) => { - setSearchValue(val) if (dataSource && val.length) { setAutoload(true) onTrigger(val) @@ -557,13 +556,14 @@ const SelectTree = ({ placeholder={localeDatas.selectTree.search} clearable="true" clearabletrigger="always" - defaultValue={searchValue} + value={searchValue} onKeyDown={(e) => { if (e.keyCode === '13') { searchTreeNode(e.target.value) } }} onChange={(e) => { + setSearchValue(e.target.value) debouncedFilterItems(e.target.value) }} /> From 922acb3981740731fb662ac59d92d7d1c6248115 Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Tue, 5 Jan 2021 19:53:49 +0800 Subject: [PATCH 12/71] fix: #1493 --- CHANGELOG.md | 1 + components/tabs/style/index.scss | 11 +++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1442fbcd4..c9b3d091a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## 3.3.1 - 修复 `SelectTree` 搜索输入框在输入值时失焦问题 [#1491](https://github.com/XiaoMi/hiui/issues/1491) +- 修复 `Tabs` 组件垂直方向样式显示错误 [#1493](https://github.com/XiaoMi/hiui/issues/1493) ## 3.3.0 - 新增 `Card` 模式模式下 loading 加载中状态 [#1454](https://github.com/XiaoMi/hiui/issues/1454) - 新增 `Table` loading 加载中状态 [#1466](https://github.com/XiaoMi/hiui/issues/1466) diff --git a/components/tabs/style/index.scss b/components/tabs/style/index.scss index 6aa5eb597..afdf20ccd 100644 --- a/components/tabs/style/index.scss +++ b/components/tabs/style/index.scss @@ -15,6 +15,7 @@ $prefix: 'hi-tabs' !default; cursor: pointer; border: 1px solid transparent; transition: all 300ms; + &:not(&--disabled):focus { outline: none; color: use-color('primary'); @@ -112,6 +113,12 @@ $prefix: 'hi-tabs' !default; } } + &.hi-tabs--line:not(.hi-tabs--vertical) { + .hi-tabs__item:first-of-type { + padding-left: 0; + } + } + &--card.#{$prefix}--vertical { display: flex; flex-wrap: nowrap; @@ -290,10 +297,6 @@ $prefix: 'hi-tabs' !default; padding: 0 16px; box-sizing: border-box; - &:first-of-type { - padding-left: 0; - } - &:hover { color: use-color('primary'); } From a7a480bd545f4d6f8ff97d10a010643f63cacb66 Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Tue, 5 Jan 2021 20:00:35 +0800 Subject: [PATCH 13/71] fix: #1493 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c9b3d091a..72abb462b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ## 3.3.1 - 修复 `SelectTree` 搜索输入框在输入值时失焦问题 [#1491](https://github.com/XiaoMi/hiui/issues/1491) -- 修复 `Tabs` 组件垂直方向样式显示错误 [#1493](https://github.com/XiaoMi/hiui/issues/1493) +- 修复 `Tabs` 组件垂直方向样式显示异常问题 [#1493](https://github.com/XiaoMi/hiui/issues/1493) ## 3.3.0 - 新增 `Card` 模式模式下 loading 加载中状态 [#1454](https://github.com/XiaoMi/hiui/issues/1454) - 新增 `Table` loading 加载中状态 [#1466](https://github.com/XiaoMi/hiui/issues/1466) From bdd5b1c688a8087967c6e43f44f3851c4b648378 Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Tue, 5 Jan 2021 20:15:11 +0800 Subject: [PATCH 14/71] chore: #1494 --- components/popper/utils/positionUtils.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/popper/utils/positionUtils.js b/components/popper/utils/positionUtils.js index 98bf6b818..7bf47c067 100644 --- a/components/popper/utils/positionUtils.js +++ b/components/popper/utils/positionUtils.js @@ -60,10 +60,10 @@ const positionAuto = (attachEleRect, popperHeight, popperRef, height, containerH } return placement } -const caclLeftOrRightPlacement = (leftPlacement, RightPlacement, popperRef, widthConstant) => { + +const caclLeftOrRightPlacement = (leftPlacement, rightPlacement, popperRef, widthConstant) => { let { width, popperWidth, poperLeft, containerWidth, attachEleWidth } = widthConstant - // 计算popper在元素上面或下面 - let placement = RightPlacement + let placement = rightPlacement if (popperWidth === undefined) { const clientWidth = popperRef || {} popperWidth = clientWidth || 0 @@ -82,9 +82,9 @@ const caclLeftOrRightPlacement = (leftPlacement, RightPlacement, popperRef, widt } return placement } +// 计算popper在元素上面或下面 const caclBottomOrTopPlacement = (bottomPlacement, topPlacement, popperRef, heightConstant, widthConstant) => { let { popperHeight, height, poperTop, containerHeight } = heightConstant - // 计算popper在元素上面或下面 let placement = bottomPlacement popperHeight === undefined && (popperHeight = 0) // 自动探测边界,第一次时需设置为不可见,否则会闪跳,用来设置class hi-popper__content--hide if (popperRef || height) { From f50d209e6963dc538cef56106555a7683b34408a Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Tue, 5 Jan 2021 20:25:37 +0800 Subject: [PATCH 15/71] chore: #1494 --- components/popper/utils/positionUtils.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/components/popper/utils/positionUtils.js b/components/popper/utils/positionUtils.js index 7bf47c067..4d29ebf2a 100644 --- a/components/popper/utils/positionUtils.js +++ b/components/popper/utils/positionUtils.js @@ -173,9 +173,8 @@ export const getOffset = (props, state, status) => { let top = rect.top + _scrollTop let left = rect.left + _scrollLeft - + const _width = width === false ? '' : width === undefined ? rect.width : width width = width === false ? popperWidth : width === undefined ? rect.width : width - const _width = width let placement = getPlacement(rect, container, props, state, rect.width) || 'bottom-start' const rectHeight = rect.height switch (placement) { From 4a5f4d700c587ab86b82141bca9a217482b6e43f Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Wed, 6 Jan 2021 12:48:05 +0800 Subject: [PATCH 16/71] fix: #1501 --- docs/demo/select/select-group.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/demo/select/select-group.jsx b/docs/demo/select/select-group.jsx index 5948a45ee..d1ca8c34b 100644 --- a/docs/demo/select/select-group.jsx +++ b/docs/demo/select/select-group.jsx @@ -42,7 +42,6 @@ class Demo extends React.Component { { childrenKey={getChildrenKey()} onSelect={onChangeValue} onHover={onHover} + currentDeep={currentDeep} expandTrigger={expandTrigger} + focusOptionIndex={focusOptionIndex} emptyContent={emptyContent} localeDatasProps={localeDatasProps} /> diff --git a/components/cascader/Menu.js b/components/cascader/Menu.js index d18d4da5b..5de83dc21 100644 --- a/components/cascader/Menu.js +++ b/components/cascader/Menu.js @@ -19,7 +19,9 @@ const Menu = forwardRef( onHover, expandTrigger, localeDatasProps, - emptyContent + emptyContent, + focusOptionIndex, + currentDeep }, ref ) => { @@ -28,11 +30,13 @@ const Menu = forwardRef( let currentOptions = options.slice() let deep = 0 const menus = [] + const _focusOptionIndex = String(focusOptionIndex).split('-') while (currentOptions) { const currentValue = value[deep] - const _currentOptions = currentOptions.slice() currentOptions = false + console.log('focusOptionIndex', focusOptionIndex, deep) + currentDeep.current = deep if ((isFiltered && value.length > deep) || !isFiltered) { menus.push(
    { e.stopPropagation() !option.disabled && onSelect(optionValues, hasChildren) @@ -86,7 +93,7 @@ const Menu = forwardRef( } } return menus - }, [options, value, valueKey]) + }, [options, value, valueKey, focusOptionIndex]) const getOptionValues = useCallback((values, optionValue, index) => { if (index === 0) { @@ -109,7 +116,7 @@ const Menu = forwardRef( {value.length === 0 && isFiltered && ( <> -

    {emptyContent || localeDatasProps('emptyContent')}

    ' ' +

    {emptyContent || localeDatasProps('emptyContent')}

    )} diff --git a/components/cascader/style/cascader.scss b/components/cascader/style/cascader.scss index 2aa476777..456c4d68b 100644 --- a/components/cascader/style/cascader.scss +++ b/components/cascader/style/cascader.scss @@ -3,6 +3,8 @@ $cascader: 'hi-cascader' !default; .#{$cascader} { + outline: none; + &.hi-cascader--focused { .hi-cascader { &__input-container { diff --git a/components/cascader/style/menu.scss b/components/cascader/style/menu.scss index cddb7c11e..351ecf268 100644 --- a/components/cascader/style/menu.scss +++ b/components/cascader/style/menu.scss @@ -67,6 +67,10 @@ $AnimationClassName: 'hi-cascader_transition' !default; background-color: #f5f5f5; } + &-focus { + background-color: rgba(0, 0, 0, 0.1) !important; + } + &--icon { position: absolute; right: 12px; diff --git a/docs/demo/cascader/section-basic.jsx b/docs/demo/cascader/section-basic.jsx index 1c9fef2ca..f8778ed00 100644 --- a/docs/demo/cascader/section-basic.jsx +++ b/docs/demo/cascader/section-basic.jsx @@ -76,7 +76,6 @@ class Demo extends React.Component { }) }} - value={value} data={this.state.options} style={{ width: 240 }} /> @@ -310,12 +309,6 @@ class Demo extends React.Component { ] const DemoBasic = () => ( - + ) export default DemoBasic From 0eb413968ecb96ea240d05bb6075fed1112cf1b0 Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Mon, 11 Jan 2021 19:35:19 +0800 Subject: [PATCH 21/71] feat: #1505 --- components/cascader/Cascader.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/components/cascader/Cascader.js b/components/cascader/Cascader.js index 972e99a1e..eb5571f96 100644 --- a/components/cascader/Cascader.js +++ b/components/cascader/Cascader.js @@ -414,8 +414,14 @@ const Cascader = (props) => { optionIndexs.map((item, index) => { optionValues.push(getDeepData(index)[item].id) }) - const _data = getDeepData(l - 1)[optionIndexs[l - 1]] - onChangeValue(optionValues, !!_data.children) + const { children } = getDeepData(l - 1)[optionIndexs[l - 1]] + onChangeValue(optionValues, !!children) + console.log('children', children) + let index = 0 + while (children[index] && children[index].disabled) { + index++ + } + setFocusOptionIndex(focusOptionIndex + '-' + index) } // 按键操作 const handleKeyDown = (evt) => { From 809362228e879e32c0690a7a876d0eb3f4739a33 Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Tue, 12 Jan 2021 11:33:38 +0800 Subject: [PATCH 22/71] feat: #1505 --- components/cascader/Cascader.js | 48 ++++++++++++++++++----------- components/cascader/Menu.js | 6 ++-- components/cascader/style/menu.scss | 2 +- 3 files changed, 35 insertions(+), 21 deletions(-) diff --git a/components/cascader/Cascader.js b/components/cascader/Cascader.js index eb5571f96..c80332ef4 100644 --- a/components/cascader/Cascader.js +++ b/components/cascader/Cascader.js @@ -98,10 +98,11 @@ const Cascader = (props) => { setCacheValue(value) } }, [value]) - // 重置按键操作 - useEffect(() => { - !popperShow && setFocusOptionIndex(-1) - }, [popperShow]) + // useEffect(() => { + // setFocusOptionIndex(-1) + // currentDeep.current = 0 + // }, [popperShow]) + useEffect(() => { setCascaderLabel(getCascaderLabel(cacheValue)) setCascaderValue(cacheValue) @@ -362,33 +363,34 @@ const Cascader = (props) => { const parseFocusOptionIndex = (deep = currentDeep.current) => { const optionIndexs = focusOptionIndex < 0 ? [focusOptionIndex] : String(focusOptionIndex).split('-') return { - optionIndexs, + optionIndexs: optionIndexs.slice(0, deep + 1), focusOptionIndex: optionIndexs[deep] } } - // 获取不同深度的数据 + // 获取不同深度的数据, 该深度的全部数据而不是单条数据 const getDeepData = (deep = currentDeep.current) => { const { optionIndexs } = parseFocusOptionIndex(deep) let _data = data if (optionIndexs.length <= 1) { return _data } - _data = optionIndexs.reduce((deepData, current) => { + _data = optionIndexs.reduce((deepData, current, index) => { + if (index === 0) return deepData return deepData[current].children }, data) return _data } // 上下按键 const moveFocusedIndex = (direction) => { - const _data = currentDeep === 0 ? data : getDeepData() - const { focusOptionIndex } = parseFocusOptionIndex() + const _data = currentDeep.current === 0 ? data : getDeepData() + const { focusOptionIndex: _focusOptionIndex } = parseFocusOptionIndex() const isAllDisabled = _data.every((item) => { return item.disabled }) - let index = direction === 'down' ? focusOptionIndex / 1 + 1 : focusOptionIndex / 1 - 1 + let index = direction === 'down' ? _focusOptionIndex / 1 + 1 : _focusOptionIndex / 1 - 1 if (index < 0) { index = _data.length - 1 - } else if (index > _data.length) { + } else if (index >= _data.length) { index = 0 } if (!isAllDisabled) { @@ -405,23 +407,32 @@ const Cascader = (props) => { setFocusOptionIndex(-1) } } - // 右按键 + // 右方向按键 const rightHandle = () => { // onChangeValue const { optionIndexs } = parseFocusOptionIndex() const optionValues = [] const l = optionIndexs.length optionIndexs.map((item, index) => { - optionValues.push(getDeepData(index)[item].id) + const _data = getDeepData(index) + optionValues.push(_data[item].id) }) - const { children } = getDeepData(l - 1)[optionIndexs[l - 1]] - onChangeValue(optionValues, !!children) - console.log('children', children) + const { children = [] } = getDeepData(l - 1)[optionIndexs[l - 1]] + const hasChildren = !!children.length + onChangeValue(optionValues, hasChildren) let index = 0 while (children[index] && children[index].disabled) { index++ } - setFocusOptionIndex(focusOptionIndex + '-' + index) + index = hasChildren ? focusOptionIndex + '-' + index : focusOptionIndex + setFocusOptionIndex(index) + } + // 左方向按键 + const leftHandle = () => { + const optionsIndex = focusOptionIndex.split('-') + if (cascaderValue.length === 0) return + setFocusOptionIndex(optionsIndex.splice(0, optionsIndex.length - 1).join('-')) + onChangeValue(cascaderValue.splice(0, cascaderValue.length - 1), true) } // 按键操作 const handleKeyDown = (evt) => { @@ -459,13 +470,14 @@ const Cascader = (props) => { if (evt.keyCode === 37) { evt.preventDefault() evt.stopPropagation() + leftHandle() } } } const expandIcon = popperShow ? 'icon-up' : 'icon-down' const placeholder = cascaderLabel || localeDatasProps('placeholder') - + console.log('now focusOptionIndex:', focusOptionIndex) return (
    { e.stopPropagation() + console.log('optionValues', optionValues) !option.disabled && onSelect(optionValues, hasChildren) }} onMouseEnter={(e) => { diff --git a/components/cascader/style/menu.scss b/components/cascader/style/menu.scss index 351ecf268..03d69d3bb 100644 --- a/components/cascader/style/menu.scss +++ b/components/cascader/style/menu.scss @@ -54,7 +54,7 @@ $AnimationClassName: 'hi-cascader_transition' !default; padding-right: 25px; } - &.hi-cascader-menu__item-active { + &.hi-cascader-menu__item-active:not(.hi-cascader-menu__item-focus) { color: #4284f5; background-color: transparent !important; From 3fe761ee5b8d32bba6073b3e53e175420cb82ff4 Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Tue, 12 Jan 2021 17:16:09 +0800 Subject: [PATCH 23/71] feat: #1505 --- components/cascader/Cascader.js | 63 ++++++++++++++++++++++++--------- components/cascader/Menu.js | 30 ++++++++++------ 2 files changed, 66 insertions(+), 27 deletions(-) diff --git a/components/cascader/Cascader.js b/components/cascader/Cascader.js index c80332ef4..fe3542493 100644 --- a/components/cascader/Cascader.js +++ b/components/cascader/Cascader.js @@ -1,7 +1,7 @@ import React, { useRef, useState, useCallback, useEffect } from 'react' import classNames from 'classnames' - +import _ from 'lodash' import Popper from '../popper' import Menu from './Menu' @@ -27,10 +27,6 @@ const Cascader = (props) => { onChange = noop, onActiveItemChange = noop } = props - const data = props.data || props.options // 兼容1.x API 2.x 改为data - const emptyContent = props.emptyContent || props.noFoundTip // 兼容1.x API 2.x改为 emptyContent - const expandTrigger = props.expandTrigger || props.trigger || 'click' // 兼容2.x API 1.x改为 expandTrigger - const getLabelKey = useCallback(() => { return fieldNames.label || 'content' }, []) @@ -42,7 +38,25 @@ const Cascader = (props) => { const getChildrenKey = useCallback(() => { return fieldNames.children || 'children' }, []) - + const parseData = useCallback( + (data) => { + const _data = _.cloneDeep(data) + const setDataItemPath = (data = [], parent) => { + data.forEach((item, index) => { + item._path = parent ? parent._path + '-' + index : index + if (item[getChildrenKey()]) { + setDataItemPath(item[getChildrenKey()], item) + } + }) + } + setDataItemPath(_data) + return _data + }, + [props.data, props.options] + ) + const data = parseData(props.data || props.options) // 兼容1.x API 2.x 改为data + const emptyContent = props.emptyContent || props.noFoundTip // 兼容1.x API 2.x改为 emptyContent + const expandTrigger = props.expandTrigger || props.trigger || 'click' // 兼容2.x API 1.x改为 expandTrigger const getCascaderLabel = useCallback( (values, dataList) => { if (displayRender) { @@ -84,6 +98,7 @@ const Cascader = (props) => { const [cascaderValue, setCascaderValue] = useState(value || defaultValue || []) const [cascaderLabel, setCascaderLabel] = useState(getCascaderLabel(value || defaultValue || [])) const [focusOptionIndex, setFocusOptionIndex] = useState(-1) + const targetByKeyDown = useRef(false) const currentDeep = useRef(0) const [popperShow, setPopperShow] = useState(false) const [keyword, setKeyword] = useState('') @@ -98,10 +113,10 @@ const Cascader = (props) => { setCacheValue(value) } }, [value]) - // useEffect(() => { - // setFocusOptionIndex(-1) - // currentDeep.current = 0 - // }, [popperShow]) + useEffect(() => { + setFocusOptionIndex(-1) + currentDeep.current = 0 + }, [popperShow]) useEffect(() => { setCascaderLabel(getCascaderLabel(cacheValue)) @@ -364,7 +379,7 @@ const Cascader = (props) => { const optionIndexs = focusOptionIndex < 0 ? [focusOptionIndex] : String(focusOptionIndex).split('-') return { optionIndexs: optionIndexs.slice(0, deep + 1), - focusOptionIndex: optionIndexs[deep] + focusOptionIndex: optionIndexs[deep] || -1 } } // 获取不同深度的数据, 该深度的全部数据而不是单条数据 @@ -376,12 +391,13 @@ const Cascader = (props) => { } _data = optionIndexs.reduce((deepData, current, index) => { if (index === 0) return deepData - return deepData[current].children + return deepData[current][getChildrenKey()] }, data) - return _data + return _data || [] } // 上下按键 const moveFocusedIndex = (direction) => { + targetByKeyDown.current = true const _data = currentDeep.current === 0 ? data : getDeepData() const { focusOptionIndex: _focusOptionIndex } = parseFocusOptionIndex() const isAllDisabled = _data.every((item) => { @@ -401,6 +417,8 @@ const Cascader = (props) => { const _focusOptionIndex = String(focusOptionIndex).split('-') _focusOptionIndex[currentDeep.current] = index index = _focusOptionIndex.join('-') + } else { + index = String(index) } setFocusOptionIndex(index) } else { @@ -410,6 +428,8 @@ const Cascader = (props) => { // 右方向按键 const rightHandle = () => { // onChangeValue + targetByKeyDown.current = true + currentDeep.current++ const { optionIndexs } = parseFocusOptionIndex() const optionValues = [] const l = optionIndexs.length @@ -417,8 +437,10 @@ const Cascader = (props) => { const _data = getDeepData(index) optionValues.push(_data[item].id) }) - const { children = [] } = getDeepData(l - 1)[optionIndexs[l - 1]] + const currentItem = getDeepData(l - 1)[optionIndexs[l - 1]] || {} + const children = currentItem[getChildrenKey()] || [] const hasChildren = !!children.length + console.log('optionValues', optionValues, hasChildren) onChangeValue(optionValues, hasChildren) let index = 0 while (children[index] && children[index].disabled) { @@ -429,8 +451,14 @@ const Cascader = (props) => { } // 左方向按键 const leftHandle = () => { + targetByKeyDown.current = true + const optionsIndex = focusOptionIndex.split('-') - if (cascaderValue.length === 0) return + if (cascaderValue.length === 0) { + currentDeep.current = 0 + return + } + currentDeep.current-- setFocusOptionIndex(optionsIndex.splice(0, optionsIndex.length - 1).join('-')) onChangeValue(cascaderValue.splice(0, cascaderValue.length - 1), true) } @@ -461,7 +489,7 @@ const Cascader = (props) => { moveFocusedIndex('up') } // right - if (evt.keyCode === 39) { + if (evt.keyCode === 39 || evt.keyCode === 13) { evt.preventDefault() evt.stopPropagation() rightHandle() @@ -477,7 +505,6 @@ const Cascader = (props) => { const expandIcon = popperShow ? 'icon-up' : 'icon-down' const placeholder = cascaderLabel || localeDatasProps('placeholder') - console.log('now focusOptionIndex:', focusOptionIndex) return (
    { currentDeep={currentDeep} expandTrigger={expandTrigger} focusOptionIndex={focusOptionIndex} + setFocusOptionIndex={setFocusOptionIndex} + targetByKeyDown={targetByKeyDown} emptyContent={emptyContent} localeDatasProps={localeDatasProps} /> diff --git a/components/cascader/Menu.js b/components/cascader/Menu.js index 8e224e511..ad0eddb03 100644 --- a/components/cascader/Menu.js +++ b/components/cascader/Menu.js @@ -21,7 +21,9 @@ const Menu = forwardRef( localeDatasProps, emptyContent, focusOptionIndex, - currentDeep + currentDeep, + targetByKeyDown, + setFocusOptionIndex }, ref ) => { @@ -30,13 +32,11 @@ const Menu = forwardRef( let currentOptions = options.slice() let deep = 0 const menus = [] - const _focusOptionIndex = focusOptionIndex < 0 ? [focusOptionIndex] : String(focusOptionIndex).split('-') while (currentOptions) { const currentValue = value[deep] const _currentOptions = currentOptions.slice() currentOptions = false - console.log('focusOptionIndex', focusOptionIndex, deep) - currentDeep.current = deep + // currentDeep.current = deep if ((isFiltered && value.length > deep) || !isFiltered) { menus.push(
      { e.stopPropagation() - console.log('optionValues', optionValues) - !option.disabled && onSelect(optionValues, hasChildren) + if (!option.disabled) { + console.log('optionValues', optionValues, hasChildren) + onSelect(optionValues, hasChildren) + targetByKeyDown.current = false + setFocusOptionIndex(_path) + currentDeep.current = _deep + } }} onMouseEnter={(e) => { e.stopPropagation() - !option.disabled && expandTrigger === 'hover' && onHover(optionValues, hasChildren) + if (!option.disabled && expandTrigger === 'hover') { + onHover(optionValues, hasChildren) + targetByKeyDown.current = false + setFocusOptionIndex(_path) + currentDeep.current = _deep + } }} key={optionValue + index} > From 4dba403add4c4e0fd7542393af1b413cf6e8c615 Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Tue, 12 Jan 2021 18:44:00 +0800 Subject: [PATCH 24/71] feat: #1505 --- components/cascader/Cascader.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/cascader/Cascader.js b/components/cascader/Cascader.js index fe3542493..ff124f08a 100644 --- a/components/cascader/Cascader.js +++ b/components/cascader/Cascader.js @@ -391,7 +391,8 @@ const Cascader = (props) => { } _data = optionIndexs.reduce((deepData, current, index) => { if (index === 0) return deepData - return deepData[current][getChildrenKey()] + const _index = optionIndexs[index - 1] + return deepData[_index][getChildrenKey()] }, data) return _data || [] } @@ -440,7 +441,6 @@ const Cascader = (props) => { const currentItem = getDeepData(l - 1)[optionIndexs[l - 1]] || {} const children = currentItem[getChildrenKey()] || [] const hasChildren = !!children.length - console.log('optionValues', optionValues, hasChildren) onChangeValue(optionValues, hasChildren) let index = 0 while (children[index] && children[index].disabled) { From df776ef52ca6851cc9b92ed0e6513cfaed8d0edb Mon Sep 17 00:00:00 2001 From: wugaoliang Date: Tue, 12 Jan 2021 21:49:09 +0800 Subject: [PATCH 25/71] fix: #1482 --- CHANGELOG.md | 1 + components/checkbox/style/index.scss | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6395ad218..008826df0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - 修复 `Select` 组件分组形态 filterOption 函数无法使用问题 [#1497](https://github.com/XiaoMi/hiui/issues/1497) - 修复 `Select` 组件分组形态全选以及受控问题 [#1501](https://github.com/XiaoMi/hiui/issues/1501) - 修复 `Tabs` 组件垂直方向样式显示异常问题 [#1493](https://github.com/XiaoMi/hiui/issues/1493) +- 优化 `Checkbox` 样式相关内容 [#1482](https://github.com/XiaoMi/hiui/issues/1482) ## 3.3.0 - 新增 `Card` 模式模式下 loading 加载中状态 [#1454](https://github.com/XiaoMi/hiui/issues/1454) - 新增 `Table` loading 加载中状态 [#1466](https://github.com/XiaoMi/hiui/issues/1466) diff --git a/components/checkbox/style/index.scss b/components/checkbox/style/index.scss index 59fc1208b..3bbc2f9e4 100644 --- a/components/checkbox/style/index.scss +++ b/components/checkbox/style/index.scss @@ -44,9 +44,8 @@ $prefixCls: '.hi-checkbox' !default; @include component-reset(); input { - position: absolute; - width: 16px; - height: 16px; + width: 0; + height: 0; opacity: 0; } From 8ea322af3e235c8ad1e26f18718bcb56aa9e04bb Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Wed, 13 Jan 2021 09:18:57 +0800 Subject: [PATCH 26/71] feat: #1505 --- components/cascader/Cascader.js | 178 +++++++++++++++++--------------- 1 file changed, 95 insertions(+), 83 deletions(-) diff --git a/components/cascader/Cascader.js b/components/cascader/Cascader.js index ff124f08a..1fece92af 100644 --- a/components/cascader/Cascader.js +++ b/components/cascader/Cascader.js @@ -375,59 +375,68 @@ const Cascader = (props) => { [popperShow, cacheValue, cascaderValue, keyword] ) - const parseFocusOptionIndex = (deep = currentDeep.current) => { - const optionIndexs = focusOptionIndex < 0 ? [focusOptionIndex] : String(focusOptionIndex).split('-') - return { - optionIndexs: optionIndexs.slice(0, deep + 1), - focusOptionIndex: optionIndexs[deep] || -1 - } - } + const parseFocusOptionIndex = useCallback( + (deep = currentDeep.current) => { + const optionIndexs = focusOptionIndex < 0 ? [focusOptionIndex] : String(focusOptionIndex).split('-') + return { + optionIndexs: optionIndexs.slice(0, deep + 1), + focusOptionIndex: optionIndexs[deep] || -1 + } + }, + [focusOptionIndex, currentDeep.current] + ) // 获取不同深度的数据, 该深度的全部数据而不是单条数据 - const getDeepData = (deep = currentDeep.current) => { - const { optionIndexs } = parseFocusOptionIndex(deep) - let _data = data - if (optionIndexs.length <= 1) { - return _data - } - _data = optionIndexs.reduce((deepData, current, index) => { - if (index === 0) return deepData - const _index = optionIndexs[index - 1] - return deepData[_index][getChildrenKey()] - }, data) - return _data || [] - } + const getDeepData = useCallback( + (deep = currentDeep.current) => { + const { optionIndexs } = parseFocusOptionIndex(deep) + let _data = data + if (optionIndexs.length <= 1) { + return _data + } + _data = optionIndexs.reduce((deepData, current, index) => { + if (index === 0) return deepData + const _index = optionIndexs[index - 1] + return deepData[_index][getChildrenKey()] + }, data) + return _data || [] + }, + [parseFocusOptionIndex, data, currentDeep.current] + ) // 上下按键 - const moveFocusedIndex = (direction) => { - targetByKeyDown.current = true - const _data = currentDeep.current === 0 ? data : getDeepData() - const { focusOptionIndex: _focusOptionIndex } = parseFocusOptionIndex() - const isAllDisabled = _data.every((item) => { - return item.disabled - }) - let index = direction === 'down' ? _focusOptionIndex / 1 + 1 : _focusOptionIndex / 1 - 1 - if (index < 0) { - index = _data.length - 1 - } else if (index >= _data.length) { - index = 0 - } - if (!isAllDisabled) { - while (data[index] && data[index].disabled) { - index++ + const moveFocusedIndex = useCallback( + (direction) => { + targetByKeyDown.current = true + const _data = currentDeep.current === 0 ? data : getDeepData() + const { focusOptionIndex: _focusOptionIndex } = parseFocusOptionIndex() + const isAllDisabled = _data.every((item) => { + return item.disabled + }) + let index = direction === 'down' ? _focusOptionIndex / 1 + 1 : _focusOptionIndex / 1 - 1 + if (index < 0) { + index = _data.length - 1 + } else if (index >= _data.length) { + index = 0 } - if (currentDeep.current > 0) { - const _focusOptionIndex = String(focusOptionIndex).split('-') - _focusOptionIndex[currentDeep.current] = index - index = _focusOptionIndex.join('-') + if (!isAllDisabled) { + while (data[index] && data[index].disabled) { + index++ + } + if (currentDeep.current > 0) { + const _focusOptionIndex = String(focusOptionIndex).split('-') + _focusOptionIndex[currentDeep.current] = index + index = _focusOptionIndex.join('-') + } else { + index = String(index) + } + setFocusOptionIndex(index) } else { - index = String(index) + setFocusOptionIndex(-1) } - setFocusOptionIndex(index) - } else { - setFocusOptionIndex(-1) - } - } + }, + [targetByKeyDown.current, currentDeep.current, data, getDeepData, parseFocusOptionIndex, focusOptionIndex] + ) // 右方向按键 - const rightHandle = () => { + const rightHandle = useCallback(() => { // onChangeValue targetByKeyDown.current = true currentDeep.current++ @@ -448,9 +457,9 @@ const Cascader = (props) => { } index = hasChildren ? focusOptionIndex + '-' + index : focusOptionIndex setFocusOptionIndex(index) - } + }, [targetByKeyDown.current, parseFocusOptionIndex, focusOptionIndex, onChangeValue, currentDeep.current]) // 左方向按键 - const leftHandle = () => { + const leftHandle = useCallback(() => { targetByKeyDown.current = true const optionsIndex = focusOptionIndex.split('-') @@ -461,47 +470,50 @@ const Cascader = (props) => { currentDeep.current-- setFocusOptionIndex(optionsIndex.splice(0, optionsIndex.length - 1).join('-')) onChangeValue(cascaderValue.splice(0, cascaderValue.length - 1), true) - } + }, [focusOptionIndex, targetByKeyDown.current, onChangeValue, currentDeep.current, cascaderValue]) // 按键操作 - const handleKeyDown = (evt) => { - // space - if (evt.keyCode === 32) { - evt.preventDefault() - evt.stopPropagation() - setPopperShow(true) - } - // esc - if (evt.keyCode === 27) { - evt.stopPropagation() - setPopperShow(false) - } - if (popperShow) { - // down - if (evt.keyCode === 40) { - evt.stopPropagation() - evt.preventDefault() - moveFocusedIndex('down') - } - // up - if (evt.keyCode === 38) { + const handleKeyDown = useCallback( + (evt) => { + // space + if (evt.keyCode === 32) { evt.preventDefault() evt.stopPropagation() - moveFocusedIndex('up') + setPopperShow(true) } - // right - if (evt.keyCode === 39 || evt.keyCode === 13) { - evt.preventDefault() + // esc + if (evt.keyCode === 27) { evt.stopPropagation() - rightHandle() + setPopperShow(false) } - // left - if (evt.keyCode === 37) { - evt.preventDefault() - evt.stopPropagation() - leftHandle() + if (popperShow) { + // down + if (evt.keyCode === 40) { + evt.stopPropagation() + evt.preventDefault() + moveFocusedIndex('down') + } + // up + if (evt.keyCode === 38) { + evt.preventDefault() + evt.stopPropagation() + moveFocusedIndex('up') + } + // right + if (evt.keyCode === 39 || evt.keyCode === 13) { + evt.preventDefault() + evt.stopPropagation() + rightHandle() + } + // left + if (evt.keyCode === 37) { + evt.preventDefault() + evt.stopPropagation() + leftHandle() + } } - } - } + }, + [moveFocusedIndex, rightHandle, leftHandle] + ) const expandIcon = popperShow ? 'icon-up' : 'icon-down' const placeholder = cascaderLabel || localeDatasProps('placeholder') From c454848b6de33c891c8379d58776c78dcf20ed1e Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Wed, 13 Jan 2021 09:27:34 +0800 Subject: [PATCH 27/71] chore: remark --- components/cascader/Cascader.js | 3 +-- components/cascader/Menu.js | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/components/cascader/Cascader.js b/components/cascader/Cascader.js index 1fece92af..0b12672e8 100644 --- a/components/cascader/Cascader.js +++ b/components/cascader/Cascader.js @@ -437,7 +437,6 @@ const Cascader = (props) => { ) // 右方向按键 const rightHandle = useCallback(() => { - // onChangeValue targetByKeyDown.current = true currentDeep.current++ const { optionIndexs } = parseFocusOptionIndex() @@ -475,7 +474,7 @@ const Cascader = (props) => { const handleKeyDown = useCallback( (evt) => { // space - if (evt.keyCode === 32) { + if (evt.keyCode === 32 && !searchable) { evt.preventDefault() evt.stopPropagation() setPopperShow(true) diff --git a/components/cascader/Menu.js b/components/cascader/Menu.js index ad0eddb03..19c80d586 100644 --- a/components/cascader/Menu.js +++ b/components/cascader/Menu.js @@ -67,8 +67,6 @@ const Menu = forwardRef( 'hi-cascader-menu__item--isFiltered': isFiltered && !option.hightlight, 'hi-cascader-menu__item--path': isFiltered && value.includes(option.id) })} - data-casacder-id={option.id} - data-casacder-deep={deep} onClick={(e) => { e.stopPropagation() if (!option.disabled) { From 81b42fe6cc04ef97a9945f4aa20f4b124c7c8e84 Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Wed, 13 Jan 2021 09:30:41 +0800 Subject: [PATCH 28/71] chore: remark --- components/cascader/Cascader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/cascader/Cascader.js b/components/cascader/Cascader.js index 0b12672e8..442af934d 100644 --- a/components/cascader/Cascader.js +++ b/components/cascader/Cascader.js @@ -477,7 +477,7 @@ const Cascader = (props) => { if (evt.keyCode === 32 && !searchable) { evt.preventDefault() evt.stopPropagation() - setPopperShow(true) + setPopperShow(!popperShow) } // esc if (evt.keyCode === 27) { From b1997e5bf220293e48653741b23da696cb3631d1 Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Wed, 13 Jan 2021 09:47:11 +0800 Subject: [PATCH 29/71] feat: #1508 --- components/carousel/index.js | 17 +++++++++++++++++ components/carousel/style/index.scss | 2 ++ 2 files changed, 19 insertions(+) diff --git a/components/carousel/index.js b/components/carousel/index.js index a1c52e777..460f0dd62 100644 --- a/components/carousel/index.js +++ b/components/carousel/index.js @@ -71,6 +71,21 @@ class Carousel extends Component { this.setState({ showArrow }) } + handleKeyDown = (evt) => { + // right + if (evt.keyCode === 39) { + evt.preventDefault() + evt.stopPropagation() + this.preNextEvent(1) + } + // left + if (evt.keyCode === 37) { + evt.preventDefault() + evt.stopPropagation() + this.preNextEvent(-1) + } + } + render() { const { rootWidth, active, showArrow } = this.state const { showDots, showArrows, showPages } = this.props @@ -81,6 +96,8 @@ class Carousel extends Component {
      diff --git a/components/carousel/style/index.scss b/components/carousel/style/index.scss index 135e9e19a..dec306aad 100644 --- a/components/carousel/style/index.scss +++ b/components/carousel/style/index.scss @@ -3,6 +3,7 @@ color: #fff; overflow: hidden; position: relative; + outline: none; &__container { height: 100%; @@ -54,6 +55,7 @@ visibility: hidden; height: 0; z-index: 2; + &--show { visibility: visible; } From db6b7d08aa55c3f5b2841bcc1f45ea168fe4891f Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Wed, 13 Jan 2021 14:58:08 +0800 Subject: [PATCH 30/71] fix: #1510 --- CHANGELOG.md | 1 + components/select-tree/SelectTreeHook.js | 20 ++++++++++++++++++-- components/select/Select.js | 1 + components/select/SelectDropdown.js | 7 ++++++- 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6395ad218..c034e66b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - 修复 `Select` 组件分组形态 filterOption 函数无法使用问题 [#1497](https://github.com/XiaoMi/hiui/issues/1497) - 修复 `Select` 组件分组形态全选以及受控问题 [#1501](https://github.com/XiaoMi/hiui/issues/1501) - 修复 `Tabs` 组件垂直方向样式显示异常问题 [#1493](https://github.com/XiaoMi/hiui/issues/1493) +- 优化 `SelectTree` 异步受控数据返显问题 [#1510](https://github.com/XiaoMi/hiui/issues/1510) ## 3.3.0 - 新增 `Card` 模式模式下 loading 加载中状态 [#1454](https://github.com/XiaoMi/hiui/issues/1454) - 新增 `Table` loading 加载中状态 [#1466](https://github.com/XiaoMi/hiui/issues/1466) diff --git a/components/select-tree/SelectTreeHook.js b/components/select-tree/SelectTreeHook.js index 5c8ad17e4..1254913a6 100644 --- a/components/select-tree/SelectTreeHook.js +++ b/components/select-tree/SelectTreeHook.js @@ -114,7 +114,7 @@ const SelectTree = ({ // 依赖 flattenData & value 解析生成 checkedNodes 或 selectedItems useEffect(() => { - if (flattenData.length > 0) { + if (flattenData.length > 0 && value) { if (type === 'multiple') { const cstatus = parseCheckStatusData( value, @@ -130,7 +130,7 @@ const SelectTree = ({ } } } - }, [value]) + }, [value, flattenData]) // 依赖展开项生成展开节点数据 useEffect(() => { @@ -479,6 +479,22 @@ const SelectTree = ({ leftHandle({ activeId, flattenData, expandIds, expandEvents, setActiveId, mode }) } + // enter + if (evt.keyCode === 13) { + evt.preventDefault() + evt.stopPropagation() + if (mode !== 'breadcrumb') { + type === 'multiple' + ? checkedEvents( + !selectedItems.some((item) => { + return activeId === item.id + }), + getNodeByIdTitle(activeId, flattenData), + checkedNodes + ) + : selectedEvents(getNodeByIdTitle(activeId, flattenData)) + } + } // space 选中 if (evt.keyCode === 32 && !document.activeElement.classList.value.includes('hi-selecttree__searchinput')) { evt.preventDefault() diff --git a/components/select/Select.js b/components/select/Select.js index 11af32bbc..9ab22b3f0 100644 --- a/components/select/Select.js +++ b/components/select/Select.js @@ -627,6 +627,7 @@ const InternalSelect = (props) => { checkAll={checkAll} loading={loading} focusedIndex={focusedIndex} + setFocusedIndex={setFocusedIndex} showJustSelected={showJustSelected} filterOption={filterOption} matchFilter={matchFilter} diff --git a/components/select/SelectDropdown.js b/components/select/SelectDropdown.js index 67a4f105a..2ef7f470c 100644 --- a/components/select/SelectDropdown.js +++ b/components/select/SelectDropdown.js @@ -29,7 +29,9 @@ const SelectDropdown = ({ show, fieldNames, focusedIndex, - isGroup + isGroup, + tragetByKeyDown, + setFocusedIndex }) => { const [filterItems, setFilterItems] = useState(dropdownItems) const [searchbarValue, setSearchbarValue] = useState('') @@ -253,6 +255,9 @@ const SelectDropdown = ({ onClick={(e) => onClickOptionIntal(e, item, _filterItemsIndex)} key={item[transKeys(fieldNames, 'id')]} index={filterItemsIndex} + onMouseEnter={() => { + setFocusedIndex(_filterItemsIndex) + }} > {renderOption(isSelected, item, filterItemsIndex)} From 17cd02abcef76cde0fefd796decc6430dcda0c32 Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Wed, 13 Jan 2021 14:59:37 +0800 Subject: [PATCH 31/71] fix: #1510 --- components/select/SelectDropdown.js | 1 - 1 file changed, 1 deletion(-) diff --git a/components/select/SelectDropdown.js b/components/select/SelectDropdown.js index 2ef7f470c..42b4f5b2c 100644 --- a/components/select/SelectDropdown.js +++ b/components/select/SelectDropdown.js @@ -30,7 +30,6 @@ const SelectDropdown = ({ fieldNames, focusedIndex, isGroup, - tragetByKeyDown, setFocusedIndex }) => { const [filterItems, setFilterItems] = useState(dropdownItems) From c434a07090a2985ce27bf8dd2213d9961e9c1c71 Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Wed, 13 Jan 2021 15:16:43 +0800 Subject: [PATCH 32/71] feat: #1508 --- components/carousel/index.js | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/components/carousel/index.js b/components/carousel/index.js index 460f0dd62..e2257bbb2 100644 --- a/components/carousel/index.js +++ b/components/carousel/index.js @@ -72,17 +72,11 @@ class Carousel extends Component { } handleKeyDown = (evt) => { - // right - if (evt.keyCode === 39) { + const { keyCode } = evt + if ([39, 37].includes(keyCode)) { evt.preventDefault() evt.stopPropagation() - this.preNextEvent(1) - } - // left - if (evt.keyCode === 37) { - evt.preventDefault() - evt.stopPropagation() - this.preNextEvent(-1) + this.preNextEvent(keyCode === 37 ? -1 : 1) } } From 41320b7bca6cac1ec3bd2dca26e60476ca257b01 Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Wed, 13 Jan 2021 15:59:47 +0800 Subject: [PATCH 33/71] feat: #1505 --- components/cascader/Menu.js | 1 - components/cascader/style/menu.scss | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/components/cascader/Menu.js b/components/cascader/Menu.js index 19c80d586..207fe52c2 100644 --- a/components/cascader/Menu.js +++ b/components/cascader/Menu.js @@ -70,7 +70,6 @@ const Menu = forwardRef( onClick={(e) => { e.stopPropagation() if (!option.disabled) { - console.log('optionValues', optionValues, hasChildren) onSelect(optionValues, hasChildren) targetByKeyDown.current = false setFocusOptionIndex(_path) diff --git a/components/cascader/style/menu.scss b/components/cascader/style/menu.scss index 03d69d3bb..5e237959c 100644 --- a/components/cascader/style/menu.scss +++ b/components/cascader/style/menu.scss @@ -68,7 +68,7 @@ $AnimationClassName: 'hi-cascader_transition' !default; } &-focus { - background-color: rgba(0, 0, 0, 0.1) !important; + background-color: rgba(0, 0, 0, 0.1); } &--icon { From 180bc3ba60d273f7316640ef324c9466d44f1e3b Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Thu, 14 Jan 2021 19:14:51 +0800 Subject: [PATCH 34/71] fix: #1519 --- CHANGELOG.md | 1 + components/select-tree/SelectTreeHook.js | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1200c676a..9b3e7d5ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - 优化组件弹出层自动计算合适的左右位置 [#1494](https://github.com/XiaoMi/hiui/issues/1494) - 修复 `SelectTree` 搜索输入框在输入值时失焦问题 [#1491](https://github.com/XiaoMi/hiui/issues/1491) +- 修复 `SelectTree` 单选形态下受控问题 [#1519](https://github.com/XiaoMi/hiui/issues/1519) - 修复 `Select` 组件分组形态 filterOption 函数无法使用问题 [#1497](https://github.com/XiaoMi/hiui/issues/1497) - 修复 `Select` 组件分组形态全选以及受控问题 [#1501](https://github.com/XiaoMi/hiui/issues/1501) - 修复 `Tabs` 组件垂直方向样式显示异常问题 [#1493](https://github.com/XiaoMi/hiui/issues/1493) diff --git a/components/select-tree/SelectTreeHook.js b/components/select-tree/SelectTreeHook.js index 1254913a6..7d53794d0 100644 --- a/components/select-tree/SelectTreeHook.js +++ b/components/select-tree/SelectTreeHook.js @@ -384,13 +384,15 @@ const SelectTree = ({ */ const selectedEvents = useCallback( (node) => { - setSelectedItems([node]) + if (typeof value === 'undefined') { + setSelectedItems([node]) + } const n = clearReturnData(node) onChange(node.id, n, n) setShow(false) setActiveId(node.id) }, - [onChange, show, selectedItems] + [onChange, show, selectedItems, value] ) /** From 0d914af936638f3d1c5ca069080e90c4cf6b3ddf Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Thu, 14 Jan 2021 19:17:46 +0800 Subject: [PATCH 35/71] fix: #1519 --- components/select-tree/SelectTreeHook.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/components/select-tree/SelectTreeHook.js b/components/select-tree/SelectTreeHook.js index 7d53794d0..4f299ea1b 100644 --- a/components/select-tree/SelectTreeHook.js +++ b/components/select-tree/SelectTreeHook.js @@ -384,9 +384,7 @@ const SelectTree = ({ */ const selectedEvents = useCallback( (node) => { - if (typeof value === 'undefined') { - setSelectedItems([node]) - } + typeof value === 'undefined' && setSelectedItems([node]) const n = clearReturnData(node) onChange(node.id, n, n) setShow(false) From c6c4ce874f9c20990a72e7a19014d2e3cb491610 Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Thu, 14 Jan 2021 19:24:19 +0800 Subject: [PATCH 36/71] fix: #1519 --- components/select/style/select-dropdown.scss | 4 ---- site/main.js | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/components/select/style/select-dropdown.scss b/components/select/style/select-dropdown.scss index 409c45d13..4bf6147e6 100644 --- a/components/select/style/select-dropdown.scss +++ b/components/select/style/select-dropdown.scss @@ -99,10 +99,6 @@ background-color: use-color('primary-20'); } - &:hover { - background-color: use-color('primary-20'); - } - &.is-focus { background-color: use-color('primary-20'); } diff --git a/site/main.js b/site/main.js index e927e31c9..2f6649601 100644 --- a/site/main.js +++ b/site/main.js @@ -7,7 +7,7 @@ import './style/index.scss' import { ThemeContext } from '../components' render( - + , From 4f39ab318a3b76fb62623181e07b5a2b05df1562 Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Thu, 14 Jan 2021 19:25:13 +0800 Subject: [PATCH 37/71] fix: ThemeContext value --- site/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/main.js b/site/main.js index 2f6649601..e927e31c9 100644 --- a/site/main.js +++ b/site/main.js @@ -7,7 +7,7 @@ import './style/index.scss' import { ThemeContext } from '../components' render( - + , From 7e2b45fb2a251939bfed2dc70b6fa5db168e0802 Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Fri, 15 Jan 2021 20:04:33 +0800 Subject: [PATCH 38/71] feat: #1522 --- components/select/Select.js | 16 +++++++++++++--- components/select/SelectDropdown.js | 22 +++++++++++++++++----- components/select/index.d.ts | 2 ++ docs/zh-CN/components/select.mdx | 2 ++ 4 files changed, 34 insertions(+), 8 deletions(-) diff --git a/components/select/Select.js b/components/select/Select.js index 9ab22b3f0..38c59a34a 100644 --- a/components/select/Select.js +++ b/components/select/Select.js @@ -30,7 +30,9 @@ const InternalSelect = (props) => { localeDatas, preventOverflow, placement, + onSearch, onChange: propsonChange, + onOverlayScroll, value, defaultValue, autoload, @@ -46,6 +48,8 @@ const InternalSelect = (props) => { const [focusedIndex, setFocusedIndex] = useState(0) const [isFocus, setIsFouces] = useState(false) const SelectWrapper = useRef() + const targetByKeyDown = useRef(false) + // 存储问题 const [cacheSelectItem, setCacheSelectItem] = useState([]) @@ -222,6 +226,7 @@ const InternalSelect = (props) => { // 方向键的回调 const moveFocusedIndex = useCallback( (direction) => { + targetByKeyDown.current = true let _focusedIndex = focusedIndex let _dropdownItems = dropdownItems let group = 0 @@ -291,7 +296,7 @@ const InternalSelect = (props) => { } setFocusedIndex(isGroup ? group + '-' + _focusedIndex : _focusedIndex) }, - [focusedIndex, dropdownItems, fieldNames] + [focusedIndex, dropdownItems, fieldNames, targetByKeyDown.current] ) // 点击回车选中 const onEnterSelect = useCallback(() => { @@ -332,7 +337,7 @@ const InternalSelect = (props) => { } } }, - [onEnterSelect, moveFocusedIndex] + [onEnterSelect, moveFocusedIndex, targetByKeyDown.current] ) // 对关键字的校验 对数据的过滤 const matchFilter = useCallback( @@ -433,7 +438,10 @@ const InternalSelect = (props) => { // 过滤筛选项 const onFilterItems = (keyword) => { setKeyword(keyword) - + if (typeof onSearch === 'function') { + onSearch(keyword) + return + } if (dataSource && (autoload || keyword)) { remoteSearch(keyword) } @@ -617,6 +625,8 @@ const InternalSelect = (props) => { fieldNames={fieldNames} localeMap={localeDatas.select || {}} mode={type} + onOverlayScroll={onOverlayScroll} + targetByKeyDown={targetByKeyDown} searchPlaceholder={searchPlaceholder} theme={theme} onFocus={onFocus} diff --git a/components/select/SelectDropdown.js b/components/select/SelectDropdown.js index 42b4f5b2c..cd9fa8a27 100644 --- a/components/select/SelectDropdown.js +++ b/components/select/SelectDropdown.js @@ -30,18 +30,21 @@ const SelectDropdown = ({ fieldNames, focusedIndex, isGroup, - setFocusedIndex + onOverlayScroll, + setFocusedIndex, + targetByKeyDown }) => { const [filterItems, setFilterItems] = useState(dropdownItems) const [searchbarValue, setSearchbarValue] = useState('') const [isCheckAll, setIsCheckAll] = useState(false) const searchbar = useRef('') const dropdownWrapper = useRef('') + useEffect(() => { setFilterItems(dropdownItems) }, [dropdownItems]) useEffect(() => { - if (dropdownWrapper.current) { + if (dropdownWrapper.current && targetByKeyDown.current) { let _focusedIndex = focusedIndex if (isGroup) { const focusedGroup = _focusedIndex.split('-') @@ -58,7 +61,7 @@ const SelectDropdown = ({ dropdownWrapper.current.scrollTop = _scrollTop >= focusedIndexTop && focusedIndexTop > 0 ? _scrollTop : focusedIndexTop } - }, [focusedIndex]) + }, [focusedIndex, targetByKeyDown.current]) // 监控全选功能 useEffect(() => { @@ -255,7 +258,7 @@ const SelectDropdown = ({ key={item[transKeys(fieldNames, 'id')]} index={filterItemsIndex} onMouseEnter={() => { - setFocusedIndex(_filterItemsIndex) + !targetByKeyDown.current && setFocusedIndex(_filterItemsIndex) }} > {renderOption(isSelected, item, filterItemsIndex)} @@ -264,7 +267,16 @@ const SelectDropdown = ({ } const renderItems = () => { return ( -
        +
          { + targetByKeyDown.current = false + }} + onScroll={(e) => { + onOverlayScroll && onOverlayScroll(e) + }} + > {filterItems && filterItems.map((item, filterItemsIndex) => { if (matchFilter(item)) { diff --git a/components/select/index.d.ts b/components/select/index.d.ts index 23dd47a31..58d92e290 100644 --- a/components/select/index.d.ts +++ b/components/select/index.d.ts @@ -43,6 +43,8 @@ interface Props { style?: CSSProperties optionWidth?: number onChange?: (selectedIds: string[], changedItem: DataItem) => void + onSearch?: (keyword: string) => void + onOverlayScroll?: (e: Event) => void render?: (item: DataItem, selected: boolean) => JSX.Element overlayClassName?: string setOverlayContainer?: (triggerNode: any) => any diff --git a/docs/zh-CN/components/select.mdx b/docs/zh-CN/components/select.mdx index 25f6937b9..482db29d9 100755 --- a/docs/zh-CN/components/select.mdx +++ b/docs/zh-CN/components/select.mdx @@ -84,6 +84,8 @@ import DemoSelectsearchbypinyin from '../../demo/select/section-searchbypinyin' | optionWidth | 自定义下拉选项宽度 | number | - | | | render | 自定义下拉菜单渲染函数,回调值为选项数据和是否被选中 | (item: DataItem, selected: boolean) => ReactNode | - | 无内容 | | overlayClassName | 下拉根元素的类名称 (3.0 新增) | string | - | - | +| onOverlayScroll | 下拉列表滚动时的回调 | function | - | - | +| onSearch | 下拉弹层中文本框值变化时回调 | function(value: string) | - | - | | setOverlayContainer | 如遇到菜单滚动定位问题,试试修改为滚动的区域,并相对其定位 (3.0 新增) | function(triggerNode) | - | () => document.body | > 注意,如果发现下拉菜单跟随页面滚动,或者需要在其他弹层中触发 Select,请尝试使用 setOverlayContainer={triggerNode => triggerNode.parentElement} 将下拉弹层渲染节点固定在触发器的父元素中。 From f7b18c4346f34e0aae08a5dbf6ea6b8703e86f4d Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Fri, 15 Jan 2021 20:09:03 +0800 Subject: [PATCH 39/71] feat: #1522 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b3e7d5ed..2db2212e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## 3.3.1 - 优化组件弹出层自动计算合适的左右位置 [#1494](https://github.com/XiaoMi/hiui/issues/1494) +- 新增 `Select` 组件 onSearch、onOverlayScroll 方法 [#1522](https://github.com/XiaoMi/hiui/issues/1522) - 修复 `SelectTree` 搜索输入框在输入值时失焦问题 [#1491](https://github.com/XiaoMi/hiui/issues/1491) - 修复 `SelectTree` 单选形态下受控问题 [#1519](https://github.com/XiaoMi/hiui/issues/1519) - 修复 `Select` 组件分组形态 filterOption 函数无法使用问题 [#1497](https://github.com/XiaoMi/hiui/issues/1497) From 8f74d89323fb2fbef79b71162bcc96973e683114 Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Mon, 18 Jan 2021 10:22:02 +0800 Subject: [PATCH 40/71] feat: #1522 --- components/select/Select.js | 3 ++- components/select/SelectDropdown.js | 14 ++++++++------ components/select/style/select-dropdown.scss | 4 ++++ 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/components/select/Select.js b/components/select/Select.js index 38c59a34a..f19d0000f 100644 --- a/components/select/Select.js +++ b/components/select/Select.js @@ -630,7 +630,8 @@ const InternalSelect = (props) => { searchPlaceholder={searchPlaceholder} theme={theme} onFocus={onFocus} - isOnSearch={dataSource} + isByRemoteSearch={dataSource} + isByCustomSearch={onSearch} onSearch={debouncedFilterItems} searchable={searchable} showCheckAll={showCheckAll} diff --git a/components/select/SelectDropdown.js b/components/select/SelectDropdown.js index cd9fa8a27..d9977491f 100644 --- a/components/select/SelectDropdown.js +++ b/components/select/SelectDropdown.js @@ -21,7 +21,8 @@ const SelectDropdown = ({ dropdownItems, localeMap, onSearch, - isOnSearch, + isByRemoteSearch, + isByCustomSearch, onClickOption, checkAll, selectInputWidth, @@ -104,7 +105,7 @@ const SelectDropdown = ({ useEffect(() => { const _filterItems = dropdownItems setFilterItems(_filterItems) - }, [mode, isOnSearch, dropdownItems, show]) + }, [mode, isByRemoteSearch, dropdownItems, show]) let matched = 0 const style = optionWidth && { @@ -150,6 +151,7 @@ const SelectDropdown = ({ (e, item, index) => { e.stopPropagation() e.preventDefault() + setFocusedIndex(index) if (item[transKeys(fieldNames, 'disabled')]) { return } @@ -208,7 +210,7 @@ const SelectDropdown = ({ disabled={item[transKeys(fieldNames, 'disabled')]} >
          - {isOnSearch + {isByRemoteSearch ? item[transKeys(fieldNames, 'title')] : hightlightKeyword(item[transKeys(fieldNames, 'title')], item[transKeys(fieldNames, 'id')])}
          @@ -216,7 +218,7 @@ const SelectDropdown = ({ )} {mode === 'single' && (
          - {isOnSearch + {isByRemoteSearch ? item[transKeys(fieldNames, 'title')] : hightlightKeyword(item[transKeys(fieldNames, 'title')], item[transKeys(fieldNames, 'id')])}
          @@ -251,7 +253,7 @@ const SelectDropdown = ({ 'is-active': isSelected, 'is-disabled': isDisabled, 'hi-select__dropdown--item--child': isChildItem, - 'is-focus': filterItemsIndex === focusedIndex, + 'is-focus': targetByKeyDown.current && filterItemsIndex === focusedIndex, 'hi-select__dropdown--item-default': !item[transKeys(fieldNames, 'children')] && !dropdownRender })} onClick={(e) => onClickOptionIntal(e, item, _filterItemsIndex)} @@ -298,7 +300,7 @@ const SelectDropdown = ({ } return (
          - {searchable && ( + {(searchable || isByCustomSearch) && (
          diff --git a/components/select/style/select-dropdown.scss b/components/select/style/select-dropdown.scss index 4bf6147e6..e8d959c5c 100644 --- a/components/select/style/select-dropdown.scss +++ b/components/select/style/select-dropdown.scss @@ -100,6 +100,10 @@ } &.is-focus { + background-color: use-color('primary-10'); + } + + &:hover { background-color: use-color('primary-20'); } From 54a665733f8bfed880149a00cc11eb9bcc66d3ce Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Tue, 19 Jan 2021 18:08:31 +0800 Subject: [PATCH 41/71] feat: #1512 --- .../hi-base-table/hiui-basetable-theme.scss | 36 ++++ components/hi-base-table/index.js | 73 ++++++++ docs/demo/base-table/section-base.jsx | 156 +++++++++++++++++ docs/demo/base-table/section-bigdata.jsx | 158 ++++++++++++++++++ docs/zh-CN/docs/hi-basetable.mdx | 34 ++++ package.json | 1 + site/locales/zh-CN.js | 2 + site/pages/docs/index.js | 3 +- 8 files changed, 462 insertions(+), 1 deletion(-) create mode 100644 components/hi-base-table/hiui-basetable-theme.scss create mode 100644 components/hi-base-table/index.js create mode 100644 docs/demo/base-table/section-base.jsx create mode 100644 docs/demo/base-table/section-bigdata.jsx create mode 100644 docs/zh-CN/docs/hi-basetable.mdx diff --git a/components/hi-base-table/hiui-basetable-theme.scss b/components/hi-base-table/hiui-basetable-theme.scss new file mode 100644 index 000000000..1c262590d --- /dev/null +++ b/components/hi-base-table/hiui-basetable-theme.scss @@ -0,0 +1,36 @@ +$table-prefix: hiui-basetable !default; +$table-font-size: 14px !default; +$header-background-color: #fbfbfb !default; +$header-font-weight: 500 !default; +$border: 1px solid #e6e7e8 !default; +$row-hovered-background-color: var(--color-primary-20) !default; + +@import "~react-base-table/es/_BaseTable.scss"; + +.#{$table-prefix} { + &__table-main { + outline: none; + } + + &.bordered { + border: 1px solid var(--color-gray-20); + border-right: none; + + .hiui-basetable__row-cell { + border-right: 1px solid var(--color-gray-20); + } + } + + &__header-cell { + color: var(--color-gray-70); + + &-text { + cursor: text; + } + } +} + +.wrapper { + height: 100vh; + border: 1px solid red; +} diff --git a/components/hi-base-table/index.js b/components/hi-base-table/index.js new file mode 100644 index 000000000..e7e87906e --- /dev/null +++ b/components/hi-base-table/index.js @@ -0,0 +1,73 @@ +import React, { useEffect, useState } from 'react' +import BaseTable, { + Column, + SortOrder, + AutoResizer, + normalizeColumns, + callOrReturn, + unflatten, + TableHeader as BaseTableHeader, + TableRow as BaseTableRow +} from 'react-base-table' +import classNames from 'classnames' + +import './hiui-basetable-theme.scss' +const classPrefix = 'hiui-basetable' +// 格式化 Columns + +const generateColumns = (columns) => { + return columns.map((column, columnIndex) => { + return { + ...column, + key: columnIndex, + dataKey: column.dataKey, + title: column.title, + width: column.width || 150 + } + }) +} +const generateData = (data) => { + return data.map((item, itemIndex) => { + return { + ...item, + id: itemIndex + } + }) +} + +const HiBaseTable = React.forwardRef((props, ref) => { + let { scrollWidth, data, columns, bordered, className } = props + const [defaultHeight, setDefaultHeight] = useState(60 * (data.length % 10)) + columns = generateColumns(columns || []) + const _defaultWidth = columns.reduce((pre, now) => { + return pre + now.width + }, 0) + const [defaultWidth, setDefaultWidth] = useState(_defaultWidth) + useEffect(() => { + setDefaultHeight(60 * (data.length % 10)) + }, [data]) + + useEffect(() => { + const _defaultWidth = columns.reduce((pre, now) => { + return pre + now.width + }, 0) + setDefaultWidth(_defaultWidth) + }, [columns]) + return ( + + ) +}) +HiBaseTable.Column = Column +HiBaseTable.PlaceholderKey = BaseTable.PlaceholderKey + +export default HiBaseTable +export { AutoResizer } diff --git a/docs/demo/base-table/section-base.jsx b/docs/demo/base-table/section-base.jsx new file mode 100644 index 000000000..cfb16ec9c --- /dev/null +++ b/docs/demo/base-table/section-base.jsx @@ -0,0 +1,156 @@ +import React from 'react' +import DocViewer from '../../../libs/doc-viewer' +import HiBaseTable, { AutoResizer } from '../../../components/hi-base-table' +import Watermark from '../../../components/watermark' +const prefix = 'table-base' +const rightOptions = ['基础', '10000条 宽度自适应'] +const code = [ + { + code: `import React from 'react' + import HiBaseTable from '@hi-ui/hiui/es/table'\n + class Demo extends React.Component { + constructor(props){ + super(props) + this.columns = [ + { + title: '商品名', + dataKey: 'name' + }, + { + title: '品类', + dataKey: 'type' + }, + { + title: '规格', + dataKey: 'size' + }, + { + title: '单价', + dataKey: 'price' + }, + { + title: '门店', + dataKey: 'address' + }, + { + title: '库存', + dataKey: 'stock' + } + ] + + this.data = [ + { + name: '小米9', + type: '手机', + size: '6G+64G 全息幻彩蓝', + price: '3299.00', + address: '华润五彩城店', + stock: '29,000', + key: 1 + }, + { + name: '小米9 SE', + type: '手机', + size: '6G+64G 全息幻彩蓝', + price: '1999.00', + address: '清河店', + stock: '10,000', + key: 2 + }, + { + name: '小米8', + type: '手机', + size: '6G+64G 全息幻彩蓝', + price: '2599.00', + address: '双安店', + stock: '12,000', + key: 3 + }, + { + name: 'Redmi Note7', + type: '手机', + size: '6G+64G 全息幻彩蓝', + price: '999.00', + address: '华润五彩城店', + stock: '140,000', + key: 4 + }, + { + name: '小米8 SE', + type: '手机', + size: '6G+64G 全息幻彩蓝', + price: '699.00', + address: '双安店', + stock: '12,000', + key: 5 + } + ] + } + render() { + return + } + }`, + opt: ['基础'] + }, + { + code: `import React from 'react' + import HiBaseTable from '@hi-ui/hiui/es/table'\n + class Demo extends React.Component { + constructor(props){ + super(props) + this.columns = this.generateColumns(10) + + this.data = this.generateData(this.columns, 10000) + } + // 格式化 Columns + generateColumns(count = 10, prefix = 'column-', props){ + return new Array(count).fill(0).map((column, columnIndex) => ({ + ...props, + key: prefix + columnIndex, + dataKey: prefix+columnIndex, + title: "column" + columnIndex, + width: 150 + })) + } + + + generateData(columns, count = 200, prefix = 'row-'){ + return new Array(count).fill(0).map((row, rowIndex) => { + return columns.reduce( + (rowData, column, columnIndex) => { + rowData[column.dataKey] = "Row" + rowIndex +" - Col" + columnIndex + return rowData + }, + { + key: prefix + rowIndex, + } + ) + }) + } + + render() { + return
          + + {(size) => { + const { width, height } = size + return ( + + ) + }} + +
          + } + }`, + opt: ['10000条 宽度自适应'] + } +] + +const DemoBase = () => ( + +) +export default DemoBase diff --git a/docs/demo/base-table/section-bigdata.jsx b/docs/demo/base-table/section-bigdata.jsx new file mode 100644 index 000000000..e5639dd50 --- /dev/null +++ b/docs/demo/base-table/section-bigdata.jsx @@ -0,0 +1,158 @@ +import React from 'react' +import DocViewer from '../../../libs/doc-viewer' +import HiBaseTable, { AutoResizer } from '../../../components/hi-base-table' +import Watermark from '../../../components/watermark' +const prefix = 'table-bigdata' +const rightOptions = ['10000 条数据'] +// 格式化 Columns +const code = [ + { + code: `import React from 'react' + import HiBaseTable from '@hi-ui/hiui/es/hi-base-table'\n + class Demo extends React.Component { + constructor(props){ + super(props) + this.columns = this.generateColumns(10) + + this.data = this.generateData(this.columns, 10000) + } + generateColumns(count = 10, prefix = 'column-', props){ + return new Array(count).fill(0).map((column, columnIndex) => ({ + ...props, + key: prefix + columnIndex, + dataKey: prefix+columnIndex, + title: "column" + columnIndex, + width: 150 + })) + } + + + generateData(columns, count = 200, prefix = 'row-'){ + return new Array(count).fill(0).map((row, rowIndex) => { + return columns.reduce( + (rowData, column, columnIndex) => { + rowData[column.dataKey] = "Row" + rowIndex +" - Col" + columnIndex + return rowData + }, + { + key: prefix + rowIndex, + } + ) + }) + } + render() { + return
          + 123 + +
          + } + }`, + opt: ['10000 条数据'] + }, + { + code: `import React from 'react' + import HiBaseTable from '@hi-ui/hiui/es/hi-base-table'\n + class Demo extends React.Component { + constructor(props){ + super(props) + this.columns = [ + { + title: '商品名', + dataKey: 'name' + }, + { + title: '品类', + dataKey: 'type' + }, + { + title: '规格', + dataKey: 'size' + }, + { + title: '单价', + dataKey: 'price' + }, + { + title: '门店', + dataKey: 'address' + }, + { + title: '库存', + dataKey: 'stock' + } + ] + + this.data = [ + { + name: '小米9', + type: '手机', + size: '6G+64G 全息幻彩蓝', + price: '3299.00', + address: '华润五彩城店', + stock: '29,000', + key: 1 + }, + { + name: '小米9 SE', + type: '手机', + size: '6G+64G 全息幻彩蓝', + price: '1999.00', + address: '清河店', + stock: '10,000', + key: 2 + }, + { + name: '小米8', + type: '手机', + size: '6G+64G 全息幻彩蓝', + price: '2599.00', + address: '双安店', + stock: '12,000', + key: 3 + }, + { + name: 'Redmi Note7', + type: '手机', + size: '6G+64G 全息幻彩蓝', + price: '999.00', + address: '华润五彩城店', + stock: '140,000', + key: 4 + }, + { + name: '小米8 SE', + type: '手机', + size: '6G+64G 全息幻彩蓝', + price: '699.00', + address: '双安店', + stock: '12,000', + key: 5 + } + ] + } + render() { + return
          + + {(size) => { + const { width, height } = size + return ( + + ) + }} + +
          + } + }`, + opt: ['自适应'] + } +] + +const DemoBase = () => ( + +) +export default DemoBase diff --git a/docs/zh-CN/docs/hi-basetable.mdx b/docs/zh-CN/docs/hi-basetable.mdx new file mode 100644 index 000000000..a85bd573a --- /dev/null +++ b/docs/zh-CN/docs/hi-basetable.mdx @@ -0,0 +1,34 @@ +# HiBaseTable + +为满足开发对 table 的多样化需求,特基于[BaseTable](https://autodesk.github.io/react-base-table/docs/get-started)进行封装 + +## 以下情况下建议使用HIBaseTable + + - 数据量较多时候 + - table 自定义度较高时 + +## 使用方法 + + +1. 安装基础依赖 react-base-table + +```shell +# npm +npm install react-base-table --save +# yarn +yarn add react-base-table + +``` +## 基础用法 + +## 基础用法 + +import DemoBase from '../../demo/base-table/section-base.jsx' + + + +## 特色用法 + +import DemoDigData from '../../demo/base-table/section-bigdata.jsx' + + diff --git a/package.json b/package.json index 9238fc566..f1e547d2b 100644 --- a/package.json +++ b/package.json @@ -136,6 +136,7 @@ "prismjs": "^1.16.0", "raw-loader": "^0.5.1", "react": "^16.9.0", + "react-base-table": "^1.12.0", "react-codemirror2": "^7.2.1", "react-custom-scrollbars": "^4.2.1", "react-dnd-test-backend": "^7.7.0", diff --git a/site/locales/zh-CN.js b/site/locales/zh-CN.js index ea43e5cda..1ee8ad8de 100755 --- a/site/locales/zh-CN.js +++ b/site/locales/zh-CN.js @@ -7,6 +7,7 @@ module.exports = { palette: '配色主题', changelog: '更新日志', 'hi-request': 'HiRequest 请求工具', + 'hi-basetable': 'BaseTable', 'group-basic': '通用', 'community-recommended': '社区推荐', 'code-editor': 'CodeEditor 代码编辑器', @@ -75,6 +76,7 @@ module.exports = { palette: '配色主题', changelog: '更新日志', 'hi-request': 'HiRequest 请求工具', + 'hi-basetable': 'BaseTable', 'group-basic': '通用', new: '重点更新', 'group-navgation': '导航', diff --git a/site/pages/docs/index.js b/site/pages/docs/index.js index 8a4378810..981c0696f 100755 --- a/site/pages/docs/index.js +++ b/site/pages/docs/index.js @@ -7,7 +7,8 @@ files.keys().forEach((key) => { export default { components: { 'community-recommended': { - 'code-editor': docs['code-editor'] + 'code-editor': docs['code-editor'], + 'hi-basetable': docs['hi-basetable'] } }, documents: { From f3ccb67ef74471373f3b3745a06d07f6d14d6dec Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Tue, 19 Jan 2021 18:17:23 +0800 Subject: [PATCH 42/71] feat: #1512 --- docs/zh-CN/docs/hi-basetable.mdx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/zh-CN/docs/hi-basetable.mdx b/docs/zh-CN/docs/hi-basetable.mdx index a85bd573a..4f71e495e 100644 --- a/docs/zh-CN/docs/hi-basetable.mdx +++ b/docs/zh-CN/docs/hi-basetable.mdx @@ -32,3 +32,6 @@ import DemoBase from '../../demo/base-table/section-base.jsx' import DemoDigData from '../../demo/base-table/section-bigdata.jsx' + +## 其他 +- [更多示例](https://autodesk.github.io/react-base-table/examples/default) From 934961a3a275e3639bdc94e4617fcd8e1b57338f Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Wed, 20 Jan 2021 15:55:59 +0800 Subject: [PATCH 43/71] feat: #1512 --- .../hi-base-table/hiui-basetable-theme.scss | 13 ++ components/hi-base-table/index.js | 124 ++++++++----- docs/demo/base-table/section-base.jsx | 20 +-- docs/demo/base-table/section-tooltip.jsx | 163 ++++++++++++++++++ docs/zh-CN/docs/hi-basetable.mdx | 41 ++++- 5 files changed, 299 insertions(+), 62 deletions(-) create mode 100644 docs/demo/base-table/section-tooltip.jsx diff --git a/components/hi-base-table/hiui-basetable-theme.scss b/components/hi-base-table/hiui-basetable-theme.scss index 1c262590d..c26672d56 100644 --- a/components/hi-base-table/hiui-basetable-theme.scss +++ b/components/hi-base-table/hiui-basetable-theme.scss @@ -8,6 +8,8 @@ $row-hovered-background-color: var(--color-primary-20) !default; @import "~react-base-table/es/_BaseTable.scss"; .#{$table-prefix} { + box-shadow: none; + &__table-main { outline: none; } @@ -28,6 +30,17 @@ $row-hovered-background-color: var(--color-primary-20) !default; cursor: text; } } + + &.autoResize { + .#{$table-prefix}__header-cell { + flex: 1 1 !important; + } + + .#{$table-prefix}__row-cell { + flex: 1 1 !important; + } + } + } .wrapper { diff --git a/components/hi-base-table/index.js b/components/hi-base-table/index.js index e7e87906e..283f6bc8a 100644 --- a/components/hi-base-table/index.js +++ b/components/hi-base-table/index.js @@ -1,69 +1,107 @@ -import React, { useEffect, useState } from 'react' -import BaseTable, { - Column, - SortOrder, - AutoResizer, - normalizeColumns, - callOrReturn, - unflatten, - TableHeader as BaseTableHeader, - TableRow as BaseTableRow -} from 'react-base-table' +import React, { useCallback, useEffect, useRef, useState } from 'react' +import BaseTable, { Column, AutoResizer } from 'react-base-table' import classNames from 'classnames' - +import _ from 'lodash' import './hiui-basetable-theme.scss' const classPrefix = 'hiui-basetable' // 格式化 Columns - -const generateColumns = (columns) => { +// 冻结处理 +const fixedColumns = (columns, fixedToColumn) => { + let leftIndex = 0 + let rightIndex = columns.length + if (Object.prototype.toString.call(fixedToColumn) === '[object String]') { + leftIndex = columns.findIndex((item) => { + return item.dataKey === fixedToColumn + }) + } else { + const { left, right } = fixedToColumn + leftIndex = columns.findIndex((item) => { + console.log('left', item, left) + return item.dataKey === left + }) + rightIndex = columns.findIndex((item) => { + return item.dataKey === right + }) + } + console.log(leftIndex, rightIndex) return columns.map((column, columnIndex) => { + let frozen + if (columnIndex <= leftIndex) frozen = Column.FrozenDirection.LEFT + if (columnIndex >= rightIndex) frozen = Column.FrozenDirection.RIGHT + return { ...column, frozen } + }) +} +const generateColumns = (columns) => { + return columns.map((column) => { return { ...column, - key: columnIndex, + key: column.dataKey, dataKey: column.dataKey, title: column.title, - width: column.width || 150 + width: column.width || 100, + maxWidth: column.width || 100 } }) } const generateData = (data) => { - return data.map((item, itemIndex) => { - return { - ...item, - id: itemIndex - } - }) + const _data = _.cloneDeep(data) + const setDataId = (data, parentId = 0) => { + data.forEach((item, itemIndex) => { + const id = item.key || itemIndex + const { parentId: _parentId } = item + item.id = id + item.parentId = typeof _parentId !== 'undefined' ? _parentId : parentId + if (item.children) { + setDataId(item.children, id) + } + }) + } + setDataId(_data) + return _data } const HiBaseTable = React.forwardRef((props, ref) => { - let { scrollWidth, data, columns, bordered, className } = props - const [defaultHeight, setDefaultHeight] = useState(60 * (data.length % 10)) + const wrapperRef = useRef() + const timeId = useRef() + let { data, columns, bordered, className, height, width, autoResize = true, fixedToColumn } = props columns = generateColumns(columns || []) const _defaultWidth = columns.reduce((pre, now) => { return pre + now.width }, 0) const [defaultWidth, setDefaultWidth] = useState(_defaultWidth) + const getDefaultWidth = useCallback(() => { + const rect = wrapperRef.current.parentNode.getBoundingClientRect() + return rect.width || _defaultWidth + }, [wrapperRef]) + const resize = useCallback(() => { + clearTimeout(timeId.current) + timeId.current = setTimeout(() => { + setDefaultWidth(getDefaultWidth()) + }, [60]) + }, []) useEffect(() => { - setDefaultHeight(60 * (data.length % 10)) - }, [data]) - - useEffect(() => { - const _defaultWidth = columns.reduce((pre, now) => { - return pre + now.width - }, 0) - setDefaultWidth(_defaultWidth) - }, [columns]) + window.addEventListener('resize', resize) + setDefaultWidth(getDefaultWidth()) + return () => { + window.removeEventListener('resize', resize) + } + }, []) return ( - +
          + +
          ) }) HiBaseTable.Column = Column diff --git a/docs/demo/base-table/section-base.jsx b/docs/demo/base-table/section-base.jsx index cfb16ec9c..ea918f8d6 100644 --- a/docs/demo/base-table/section-base.jsx +++ b/docs/demo/base-table/section-base.jsx @@ -3,7 +3,7 @@ import DocViewer from '../../../libs/doc-viewer' import HiBaseTable, { AutoResizer } from '../../../components/hi-base-table' import Watermark from '../../../components/watermark' const prefix = 'table-base' -const rightOptions = ['基础', '10000条 宽度自适应'] +const rightOptions = ['基础', '10000条数据'] const code = [ { code: `import React from 'react' @@ -129,24 +129,10 @@ const code = [ } render() { - return
          - - {(size) => { - const { width, height } = size - return ( - - ) - }} - -
          + return } }`, - opt: ['10000条 宽度自适应'] + opt: ['10000条数据'] } ] diff --git a/docs/demo/base-table/section-tooltip.jsx b/docs/demo/base-table/section-tooltip.jsx new file mode 100644 index 000000000..f80077fee --- /dev/null +++ b/docs/demo/base-table/section-tooltip.jsx @@ -0,0 +1,163 @@ +import React from 'react' +import DocViewer from '../../../libs/doc-viewer' +import HiBaseTable, { AutoResizer } from '../../../components/hi-base-table' +import Watermark from '../../../components/watermark' +import Tooltip from '../../../components/tooltip' +import Text from 'react-texty' +const prefix = 'table-bigdata' +const rightOptions = ['结合Tooltip', '树形表格', '列冻结'] +// 格式化 Columns +const code = [ + { + code: `import React from 'react' + import HiBaseTable from '@hi-ui/hiui/es/table'\n + class Demo extends React.Component { + constructor(props){ + super(props) + this.columns = this.generateColumns(10) + + this.data = this.generateData(this.columns, 10) + } + // 格式化 Columns + generateColumns(count = 10, prefix = 'column-', props){ + return new Array(count).fill(0).map((column, columnIndex) => ({ + ...props, + key: prefix + columnIndex, + dataKey: prefix+columnIndex, + title: "column" + columnIndex, + width: 150 + })) + } + + generateData(columns, count = 200, prefix = 'row-'){ + return new Array(count).fill(0).map((row, rowIndex) => { + return columns.reduce( + (rowData, column, columnIndex) => { + rowData[column.dataKey] = "Row" + rowIndex +" - Col" + "Row" + '-' + rowIndex +" - Col" + return rowData + }, + { + key: prefix + rowIndex, + } + ) + }) + } + TableCell({ className, cellData }){ + return +

          + {cellData} +

          +
          + } + render() { + return + } +}`, + opt: ['结合Tooltip'] + }, + { + code: `import React from 'react' + import HiBaseTable from '@hi-ui/hiui/es/hi-base-table'\n + class Demo extends React.Component { + constructor(props){ + super(props) + } + + render() { + return ( + + ) + } + }`, + opt: ['树形表格'] + }, + { + code: `import React from 'react' + import HiBaseTable from '@hi-ui/hiui/es/table'\n + class Demo extends React.Component { + constructor(props){ + super(props) + this.columns = this.generateColumns(10) + + this.data = this.generateData(this.columns, 100) + } + // 格式化 Columns + generateColumns(count = 10, prefix = 'column-', props){ + return new Array(count).fill(0).map((column, columnIndex) => ({ + ...props, + key: prefix + columnIndex, + dataKey: prefix+columnIndex, + title: "column" + columnIndex, + width: 150 + })) + } + + + generateData(columns, count = 200, prefix = 'row-'){ + return new Array(count).fill(0).map((row, rowIndex) => { + return columns.reduce( + (rowData, column, columnIndex) => { + rowData[column.dataKey] = "Row" + rowIndex +" - Col" + columnIndex + return rowData + }, + { + key: prefix + rowIndex, + } + ) + }) + } + + render() { + return + } + }`, + opt: ['列冻结'] + } +] + +const DemoBase = () => ( + +) +export default DemoBase diff --git a/docs/zh-CN/docs/hi-basetable.mdx b/docs/zh-CN/docs/hi-basetable.mdx index 4f71e495e..1525553ec 100644 --- a/docs/zh-CN/docs/hi-basetable.mdx +++ b/docs/zh-CN/docs/hi-basetable.mdx @@ -29,9 +29,46 @@ import DemoBase from '../../demo/base-table/section-base.jsx' ## 特色用法 -import DemoDigData from '../../demo/base-table/section-bigdata.jsx' +import DemoTooltip from '../../demo/base-table/section-tooltip.jsx' - + + +## Props + +| 属性名 | 描述 | 类型 | 可选值 | 默认值 | +| ------------------ | ------------------------------------------------------------------------------------------------------- | ---------------------------------------------- | ----------------------------------------- | ---------- | +| data | 表格数据 | object[] | - | - | +| columns | 表格列配置信息 | ColumnItem[] | - | - | +| bordered | 是否显示边框(表头分组模式下,表格自带边框) | boolean | true \| false | false | +| autoResize | 是否自适应(设置为**false**后, 需要手动定义**column**的宽度) | boolean | true \| false | true | +| fixedToColumn | 表格列冻结设置,为 string 时仅支持从左侧冻结至某一列 | string \| FixedOption | columns 中对应的 dataKey | null | + +[更多Table设置](https://autodesk.github.io/react-base-table/api/basetable) + +### ColumnItem + +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +| -------- | ---------------------- | -------------------------------------------------------------------------------- | ----------------- | ------ | +| title | 列标题 | string | - | - | +| dataKey | 列对应数据项的唯一标识 | string \| number | - | - | +| align | 列对齐方式 | string | 'left' \|'center' \|'right' | 'left' | +| sorter | 列排序函数 | () => boolean | - | null | +| avg | 该列是否支持平均值 | boolean | - | false | +| total | 该列是否支持合计 | boolean | - | false | +| width | 该列宽度 | number | - | - | +| children | 多级表头 | ColumnItem[] | - | - | +| render | 控制单元格自定义渲染 | (text: DataItem[ColumnItem[dataKey]], row: DataItem, index: number) => ReactNode | - | - | + +[更多ColumnItem设置](https://autodesk.github.io/react-base-table/api/column) + + +### FixedOption + +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +| ----- | -------------------- | ------ | ------ | ------ | +| left | 表格从左侧冻结至某列 | string | columns 中对应的 dataKey | - | +| right | 表格从右侧冻结至某列 | string | columns 中对应的 dataKey | - | ## 其他 + - [更多示例](https://autodesk.github.io/react-base-table/examples/default) From 2b534ef28c07c5a639f55c940297318cb857f9f5 Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Wed, 20 Jan 2021 16:16:28 +0800 Subject: [PATCH 44/71] feat: #1512 --- components/hi-base-table/hiui-basetable-theme.scss | 6 +----- components/hi-base-table/index.js | 5 +---- docs/demo/base-table/section-tooltip.jsx | 2 +- docs/zh-CN/docs/hi-basetable.mdx | 12 ++++-------- 4 files changed, 7 insertions(+), 18 deletions(-) diff --git a/components/hi-base-table/hiui-basetable-theme.scss b/components/hi-base-table/hiui-basetable-theme.scss index c26672d56..162d8481f 100644 --- a/components/hi-base-table/hiui-basetable-theme.scss +++ b/components/hi-base-table/hiui-basetable-theme.scss @@ -40,10 +40,6 @@ $row-hovered-background-color: var(--color-primary-20) !default; flex: 1 1 !important; } } - } -.wrapper { - height: 100vh; - border: 1px solid red; -} + diff --git a/components/hi-base-table/index.js b/components/hi-base-table/index.js index 283f6bc8a..e9cfbc8fa 100644 --- a/components/hi-base-table/index.js +++ b/components/hi-base-table/index.js @@ -16,14 +16,12 @@ const fixedColumns = (columns, fixedToColumn) => { } else { const { left, right } = fixedToColumn leftIndex = columns.findIndex((item) => { - console.log('left', item, left) return item.dataKey === left }) rightIndex = columns.findIndex((item) => { return item.dataKey === right }) } - console.log(leftIndex, rightIndex) return columns.map((column, columnIndex) => { let frozen if (columnIndex <= leftIndex) frozen = Column.FrozenDirection.LEFT @@ -38,8 +36,7 @@ const generateColumns = (columns) => { key: column.dataKey, dataKey: column.dataKey, title: column.title, - width: column.width || 100, - maxWidth: column.width || 100 + width: column.width || 100 } }) } diff --git a/docs/demo/base-table/section-tooltip.jsx b/docs/demo/base-table/section-tooltip.jsx index f80077fee..fe8bc625b 100644 --- a/docs/demo/base-table/section-tooltip.jsx +++ b/docs/demo/base-table/section-tooltip.jsx @@ -97,7 +97,7 @@ const code = [ { a: 'a-4', b: 'b-4', c: 'c-4', d: 'd-4', key: 4 } ]} columns={[ - { title: 'A', dataKey: 'a' , width: 100}, + { title: 'A', dataKey: 'a' }, { title: 'B', dataKey: 'b' }, { title: 'C', dataKey: 'c' }, { title: 'D', dataKey: 'd' } diff --git a/docs/zh-CN/docs/hi-basetable.mdx b/docs/zh-CN/docs/hi-basetable.mdx index 1525553ec..65eaafe77 100644 --- a/docs/zh-CN/docs/hi-basetable.mdx +++ b/docs/zh-CN/docs/hi-basetable.mdx @@ -1,8 +1,9 @@ # HiBaseTable -为满足开发对 table 的多样化需求,特基于[BaseTable](https://autodesk.github.io/react-base-table/docs/get-started)进行封装 +为满足开发对 table 的多样化需求,特基于[BaseTable](https://autodesk.github.io/react-base-table/docs/get-started)进行封装,本次推荐仅仅做了部分功能封装;更多功能请参考[BaseTable文档](https://autodesk.github.io/react-base-table/docs/get-started)&&[更多示例](https://autodesk.github.io/react-base-table/examples/default) + +## 以下情况下建议使用HiBaseTable -## 以下情况下建议使用HIBaseTable - 数据量较多时候 - table 自定义度较高时 @@ -27,7 +28,7 @@ import DemoBase from '../../demo/base-table/section-base.jsx' -## 特色用法 +## 其他用法 import DemoTooltip from '../../demo/base-table/section-tooltip.jsx' @@ -52,12 +53,7 @@ import DemoTooltip from '../../demo/base-table/section-tooltip.jsx' | title | 列标题 | string | - | - | | dataKey | 列对应数据项的唯一标识 | string \| number | - | - | | align | 列对齐方式 | string | 'left' \|'center' \|'right' | 'left' | -| sorter | 列排序函数 | () => boolean | - | null | -| avg | 该列是否支持平均值 | boolean | - | false | -| total | 该列是否支持合计 | boolean | - | false | | width | 该列宽度 | number | - | - | -| children | 多级表头 | ColumnItem[] | - | - | -| render | 控制单元格自定义渲染 | (text: DataItem[ColumnItem[dataKey]], row: DataItem, index: number) => ReactNode | - | - | [更多ColumnItem设置](https://autodesk.github.io/react-base-table/api/column) From 23415e4a7ef7d4512a44b5ff255942e114c9ffb5 Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Wed, 20 Jan 2021 16:20:55 +0800 Subject: [PATCH 45/71] feat: #1512 --- docs/zh-CN/docs/hi-basetable.mdx | 3 +-- site/locales/zh-CN.js | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/zh-CN/docs/hi-basetable.mdx b/docs/zh-CN/docs/hi-basetable.mdx index 65eaafe77..343a73a85 100644 --- a/docs/zh-CN/docs/hi-basetable.mdx +++ b/docs/zh-CN/docs/hi-basetable.mdx @@ -4,9 +4,8 @@ ## 以下情况下建议使用HiBaseTable - - 数据量较多时候 - - table 自定义度较高时 + - 显示灵活度较高时 ## 使用方法 diff --git a/site/locales/zh-CN.js b/site/locales/zh-CN.js index 1ee8ad8de..afede6c1b 100755 --- a/site/locales/zh-CN.js +++ b/site/locales/zh-CN.js @@ -7,7 +7,7 @@ module.exports = { palette: '配色主题', changelog: '更新日志', 'hi-request': 'HiRequest 请求工具', - 'hi-basetable': 'BaseTable', + 'hi-basetable': 'BaseTable 表格', 'group-basic': '通用', 'community-recommended': '社区推荐', 'code-editor': 'CodeEditor 代码编辑器', From a77b959dcb823a2fdd023fb74f581aee8d9dcefe Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Wed, 20 Jan 2021 16:37:48 +0800 Subject: [PATCH 46/71] feat: #1512 --- components/hi-base-table/index.js | 2 +- .../hi-base-table/{ => style}/hiui-basetable-theme.scss | 4 +--- components/hi-base-table/style/index.js | 1 + docs/demo/base-table/section-base.jsx | 4 ++-- docs/demo/base-table/section-tooltip.jsx | 7 +++---- 5 files changed, 8 insertions(+), 10 deletions(-) rename components/hi-base-table/{ => style}/hiui-basetable-theme.scss (93%) create mode 100644 components/hi-base-table/style/index.js diff --git a/components/hi-base-table/index.js b/components/hi-base-table/index.js index e9cfbc8fa..78dd27672 100644 --- a/components/hi-base-table/index.js +++ b/components/hi-base-table/index.js @@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useRef, useState } from 'react' import BaseTable, { Column, AutoResizer } from 'react-base-table' import classNames from 'classnames' import _ from 'lodash' -import './hiui-basetable-theme.scss' +import './style/index' const classPrefix = 'hiui-basetable' // 格式化 Columns // 冻结处理 diff --git a/components/hi-base-table/hiui-basetable-theme.scss b/components/hi-base-table/style/hiui-basetable-theme.scss similarity index 93% rename from components/hi-base-table/hiui-basetable-theme.scss rename to components/hi-base-table/style/hiui-basetable-theme.scss index 162d8481f..256ae0c28 100644 --- a/components/hi-base-table/hiui-basetable-theme.scss +++ b/components/hi-base-table/style/hiui-basetable-theme.scss @@ -5,7 +5,7 @@ $header-font-weight: 500 !default; $border: 1px solid #e6e7e8 !default; $row-hovered-background-color: var(--color-primary-20) !default; -@import "~react-base-table/es/_BaseTable.scss"; +@import '~react-base-table/es/_BaseTable.scss'; .#{$table-prefix} { box-shadow: none; @@ -41,5 +41,3 @@ $row-hovered-background-color: var(--color-primary-20) !default; } } } - - diff --git a/components/hi-base-table/style/index.js b/components/hi-base-table/style/index.js new file mode 100644 index 000000000..ea3232ccb --- /dev/null +++ b/components/hi-base-table/style/index.js @@ -0,0 +1 @@ +import './hiui-basetable-theme.scss' diff --git a/docs/demo/base-table/section-base.jsx b/docs/demo/base-table/section-base.jsx index ea918f8d6..8f1ea1e2c 100644 --- a/docs/demo/base-table/section-base.jsx +++ b/docs/demo/base-table/section-base.jsx @@ -7,7 +7,7 @@ const rightOptions = ['基础', '10000条数据'] const code = [ { code: `import React from 'react' - import HiBaseTable from '@hi-ui/hiui/es/table'\n + import HiBaseTable from '@hi-ui/hiui/es/hi-base-table'\n class Demo extends React.Component { constructor(props){ super(props) @@ -94,7 +94,7 @@ const code = [ }, { code: `import React from 'react' - import HiBaseTable from '@hi-ui/hiui/es/table'\n + import HiBaseTable from '@hi-ui/hiui/es/hi-base-table'\n class Demo extends React.Component { constructor(props){ super(props) diff --git a/docs/demo/base-table/section-tooltip.jsx b/docs/demo/base-table/section-tooltip.jsx index fe8bc625b..561ede305 100644 --- a/docs/demo/base-table/section-tooltip.jsx +++ b/docs/demo/base-table/section-tooltip.jsx @@ -3,14 +3,13 @@ import DocViewer from '../../../libs/doc-viewer' import HiBaseTable, { AutoResizer } from '../../../components/hi-base-table' import Watermark from '../../../components/watermark' import Tooltip from '../../../components/tooltip' -import Text from 'react-texty' const prefix = 'table-bigdata' const rightOptions = ['结合Tooltip', '树形表格', '列冻结'] // 格式化 Columns const code = [ { code: `import React from 'react' - import HiBaseTable from '@hi-ui/hiui/es/table'\n + import HiBaseTable from '@hi-ui/hiui/es/hi-base-table'\n class Demo extends React.Component { constructor(props){ super(props) @@ -110,7 +109,7 @@ const code = [ }, { code: `import React from 'react' - import HiBaseTable from '@hi-ui/hiui/es/table'\n + import HiBaseTable from '@hi-ui/hiui/es/hi-base-table'\n class Demo extends React.Component { constructor(props){ super(props) @@ -155,7 +154,7 @@ const code = [ const DemoBase = () => ( From f3631a104f89bb724833f011de4ce7967d01d783 Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Wed, 20 Jan 2021 16:41:02 +0800 Subject: [PATCH 47/71] feat: #1512 --- site/locales/zh-CN.js | 1 - 1 file changed, 1 deletion(-) diff --git a/site/locales/zh-CN.js b/site/locales/zh-CN.js index afede6c1b..13c94f639 100755 --- a/site/locales/zh-CN.js +++ b/site/locales/zh-CN.js @@ -76,7 +76,6 @@ module.exports = { palette: '配色主题', changelog: '更新日志', 'hi-request': 'HiRequest 请求工具', - 'hi-basetable': 'BaseTable', 'group-basic': '通用', new: '重点更新', 'group-navgation': '导航', From 77c0a3a47bbd74ff9638695c08f8947a0eed976f Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Wed, 20 Jan 2021 16:50:43 +0800 Subject: [PATCH 48/71] feat: add index.d.ts --- components/hi-base-table/index.d.ts | 673 ++++++++++++++++++++++++++++ 1 file changed, 673 insertions(+) create mode 100644 components/hi-base-table/index.d.ts diff --git a/components/hi-base-table/index.d.ts b/components/hi-base-table/index.d.ts new file mode 100644 index 000000000..c91b72452 --- /dev/null +++ b/components/hi-base-table/index.d.ts @@ -0,0 +1,673 @@ +declare module 'react-base-table' { + export type SortOrder = 'asc' | 'desc'; + + export type Alignment = 'left' | 'right' | 'center'; + + export type FrozenDirection = 'left' | 'right' | true | false; + + export type RowKey = string | number; + + export type Size = { width: number; height: number }; + + export type CallOrReturn = T | (P extends any[] ? (...p: P) => T : (p: P) => T); + + export interface ColumnShape { + /** + * Unique key for each column + */ + key: React.Key; + /** + * Class name for the column cell + */ + className?: CallOrReturn< + string, + { + cellData: any; + columns: ColumnShape[]; + column: ColumnShape; + columnIndex: number; + rowData: T; + rowIndex: number; + } + >; + /** + * Class name for the column header + */ + headerClassName?: CallOrReturn< + string, + { + columns: ColumnShape[]; + column: ColumnShape; + columnIndex: number; + headerIndex: number; + } + >; + /** + * Custom style for the column cell, including the header cells + */ + style?: React.CSSProperties; + /** + * Title of the column header + */ + title?: string; + /** + * Data key for the cell value, could be "a.b.c" + */ + dataKey?: string; + /** + * Custom cell data getter + * The handler is of the shape of `({ columns, column, columnIndex, rowData, rowIndex }) => node` + */ + dataGetter?: CallOrReturn< + React.ReactNode, + { + columns: ColumnShape[]; + column: ColumnShape; + columnIndex: number; + rowData: T; + rowIndex: number; + } + >; + /** + * Alignment of the column cell + */ + align?: Alignment; + /** + * Flex grow style, defaults to 0 + */ + flexGrow?: number; + /** + * Flex shrink style, defaults to 1 for flexible table and 0 for fixed table + */ + flexShrink?: number; + /** + * The width of the column, gutter width is not included + */ + width: number; + /** + * Maximum width of the column, used if the column is resizable + */ + maxWidth?: number; + /** + * Minimum width of the column, used if the column is resizable + */ + minWidth?: number; + /** + * Whether the column is frozen and what's the frozen side + */ + frozen?: FrozenDirection; + /** + * Whether the column is hidden + */ + hidden?: boolean; + /** + * Whether the column is resizable, defaults to false + */ + resizable?: boolean; + /** + * Whether the column is sortable, defaults to false + */ + sortable?: boolean; + /** + * Custom column cell renderer + * The renderer receives props `{ cellData, columns, column, columnIndex, rowData, rowIndex, container, isScrolling }` + */ + cellRenderer?: CallOrReturn< + React.ReactNode, + { + cellData: any; + columns: ColumnShape[]; + column: ColumnShape; + columnIndex: number; + rowData: T; + rowIndex: number; + container: BaseTable; + isScrolling?: boolean; + } + >; + /** + * Custom column header renderer + * The renderer receives props `{ columns, column, columnIndex, headerIndex, container }` + */ + headerRenderer?: CallOrReturn< + React.ReactNode, + { + columns: ColumnShape[]; + column: ColumnShape; + columnIndex: number; + headerIndex: number; + container: BaseTable; + } + >; + [key: string]: any; + } + + export class Column extends React.Component> { + static readonly Alignment: { + readonly LEFT: 'left'; + readonly CENTER: 'center'; + readonly RIGHT: 'right'; + }; + static readonly FrozenDirection: { + readonly LEFT: 'left'; + readonly RIGHT: 'right'; + readonly DEFAULT: true; + readonly NONE: false; + }; + } + type FixedOption = { + left?: string + right?: string + } + export interface BaseTableProps { + bordered?: boolean, + autoResize?: boolean, + bordered?: boolean, + fixedToColumn?: string | FixedOption, + /** + * Prefix for table's inner className + */ + classPrefix?: string; + /** + * Class name for the table + */ + className?: string; + /** + * Custom style for the table + */ + style?: React.CSSProperties; + /** + * A collection of Column + */ + children?: React.ReactNode; + /** + * Columns for the table + */ + columns?: ColumnShape[]; + /** + * The data for the table + */ + data?: T[]; + /** + * The data to be frozen to top, `rowIndex` is negative and starts from `-1` + */ + frozenData?: T[]; + /** + * The key field of each data item + */ + rowKey?: RowKey; + /** + * The width of the table + */ + width: number; + /** + * The height of the table, will be ignored if `maxHeight` is set + */ + height?: number; + /** + * The max height of the table, the table's height will auto change when data changes, + * will turns to vertical scroll if reaches the max height + */ + maxHeight?: number; + /** + * The height of each table row, will be ignored if `estimatedRowHeight` is set + */ + rowHeight?: number; + /** + * Estimated row height, the real height will be measure dynamically according to the content + * The callback is of the shape of `({ rowData, rowIndex }) => number` + */ + estimatedRowHeight?: CallOrReturn< + number, + { + rowData: T; + rowIndex: number; + } + >; + /** + * The height of the table header, set to 0 to hide the header, could be an array to render multi headers. + */ + headerHeight?: number | number[]; + /** + * The height of the table footer + */ + footerHeight?: number; + /** + * Whether the width of the columns are fixed or flexible + */ + fixed?: boolean; + /** + * Whether the table is disabled + */ + disabled?: boolean; + /** + * Custom renderer on top of the table component + */ + overlayRenderer?: CallOrReturn; + /** + * Custom renderer when the length of data is 0 + */ + emptyRenderer?: CallOrReturn; + /** + * Custom footer renderer, available only if `footerHeight` is larger then 0 + */ + footerRenderer?: CallOrReturn; + /** + * Custom header renderer + * The renderer receives props `{ cells, columns, headerIndex }` + */ + headerRenderer?: CallOrReturn< + React.ReactNode, + { + cells: React.ReactNode[]; + columns: ColumnShape; + headerIndex: number; + } + >; + /** + * Custom row renderer + * The renderer receives props `{ isScrolling, cells, columns, rowData, rowIndex, depth }` + */ + rowRenderer?: CallOrReturn< + React.ReactNode, + { + isScrolling?: boolean; + cells: React.ReactNode[]; + columns: ColumnShape; + rowData: T; + rowIndex: number; + depth: number; + } + >; + /** + * Class name for the table header, could be a callback to return the class name + * The callback is of the shape of `({ columns, headerIndex }) => string` + */ + headerClassName?: CallOrReturn< + string, + { + columns: ColumnShape[]; + headerIndex: number; + } + >; + /** + * Class name for the table row, could be a callback to return the class name + * The callback is of the shape of `({ columns, rowData, rowIndex }) => string` + */ + rowClassName?: CallOrReturn< + string, + { + columns: ColumnShape[]; + rowData: T; + rowIndex: number; + } + >; + /** + * Extra props applied to header element + * The handler is of the shape of `({ columns, headerIndex }) object` + */ + headerProps?: CallOrReturn< + object, + { + columns: ColumnShape[]; + headerIndex: number; + } + >; + /** + * Extra props applied to header cell element + * The handler is of the shape of `({ columns, column, columnIndex, headerIndex }) => object` + */ + headerCellProps?: CallOrReturn< + object, + { + columns: ColumnShape[]; + column: ColumnShape; + columnIndex: number; + headerIndex: number; + } + >; + /** + * Extra props applied to row element + * The handler is of the shape of `({ columns, rowData, rowIndex }) => object` + */ + rowProps?: CallOrReturn< + object, + { + columns: ColumnShape[]; + rowData: T; + rowIndex: number; + } + >; + /** + * Extra props applied to row cell element + * The handler is of the shape of `({ columns, column, columnIndex, rowData, rowIndex }) => object` + */ + cellProps?: CallOrReturn< + object, + { + columns: ColumnShape[]; + column: ColumnShape; + columnIndex: number; + rowData: T; + rowIndex: number; + } + >; + /** + * Extra props applied to ExpandIcon component + * The handler is of the shape of `({ rowData, rowIndex, depth, expandable, expanded }) => object` + */ + expandIconProps?: CallOrReturn< + object, + { + rowData: T; + rowIndex: number; + depth: number; + expandable: boolean; + expanded: boolean; + } + >; + /** + * The key for the expand column which render the expand icon if the data is a tree + */ + expandColumnKey?: string; + /** + * Default expanded row keys when initialize the table + */ + defaultExpandedRowKeys?: RowKey[]; + /** + * Controlled expanded row keys + */ + expandedRowKeys?: RowKey[]; + /** + * A callback function when expand or collapse a tree node + * The handler is of the shape of `({ expanded, rowData, rowIndex, rowKey }) => *` + */ + onRowExpand?: (args: { expanded: boolean; rowData: T; rowIndex: number; rowKey: RowKey }) => void; + /** + * A callback function when the expanded row keys changed + * The handler is of the shape of `(expandedRowKeys) => *` + */ + onExpandedRowsChange?: (expandedRowKeys: RowKey[]) => void; + /** + * The sort state for the table, will be ignored if `sortState` is set + */ + sortBy?: { + key: React.Key; + order: SortOrder; + }; + /** + * Multiple columns sort state for the table + * + * example: + * ```js + * { + * 'column-0': SortOrder.ASC, + * 'column-1': SortOrder.DESC, + * } + * ``` + */ + sortState?: { + [key in React.Key]: SortOrder; + }; + /** + * A callback function for the header cell click event + * The handler is of the shape of `({ column, key, order }) => *` + */ + onColumnSort?: (args: { column: ColumnShape; key: React.Key; order: SortOrder }) => void; + /** + * A callback function when resizing the column width + * The handler is of the shape of `({ column, width }) => *` + */ + onColumnResize?: (args: { column: ColumnShape; width: number }) => void; + /** + * A callback function when resizing the column width ends + * The handler is of the shape of `({ column, width }) => *` + */ + onColumnResizeEnd?: (args: { column: ColumnShape; width: number }) => void; + /** + * Adds an additional isScrolling parameter to the row renderer. + * This parameter can be used to show a placeholder row while scrolling. + */ + useIsScrolling?: boolean; + /** + * Number of rows to render above/below the visible bounds of the list + */ + overscanRowCount?: number; + /** + * Custom scrollbar size measurement + */ + getScrollbarSize?: () => number; + /** + * A callback function when scrolling the table + * The handler is of the shape of `({ scrollLeft, scrollTop, horizontalScrollDirection, verticalScrollDirection, scrollUpdateWasRequested }) => *` + * + * `scrollLeft` and `scrollTop` are numbers. + * + * `horizontalDirection` and `verticalDirection` are either `forward` or `backward`. + * + * `scrollUpdateWasRequested` is a boolean. This value is true if the scroll was caused by `scrollTo*`, + * and false if it was the result of a user interaction in the browser. + */ + onScroll?: (args: { + scrollLeft: number; + scrollTop: number; + horizontalScrollDirection: 'forward' | 'backward'; + verticalScrollDirection: 'forward' | 'backward'; + scrollUpdateWasRequested: boolean; + }) => void; + /** + * A callback function when scrolling the table within `onEndReachedThreshold` of the bottom + * The handler is of the shape of `({ distanceFromEnd }) => *` + */ + onEndReached?: (args: { distanceFromEnd: number }) => void; + /** + * Threshold in pixels for calling `onEndReached`. + */ + onEndReachedThreshold?: number; + /** + * A callback function with information about the slice of rows that were just rendered + * The handler is of the shape of `({ overscanStartIndex, overscanStopIndex, startIndex, stopIndex }) => *` + */ + onRowsRendered?: (args: { + overscanStartIndex: number; + overscanStopIndex: number; + startIndex: number; + stopIndex: number; + }) => void; + /** + * A callback function when the scrollbar presence state changed + * The handler is of the shape of `({ size, vertical, horizontal }) => *` + */ + onScrollbarPresenceChange?: (args: { size: number; vertical: boolean; horizontal: boolean }) => void; + /** + * A object for the row event handlers + * Each of the keys is row event name, like `onClick`, `onDoubleClick` and etc. + * Each of the handlers is of the shape of `({ rowData, rowIndex, rowKey, event }) => *` + */ + rowEventHandlers?: { + [key: string]: (args: { rowData: T; rowIndex: number; rowKey: RowKey; event: React.SyntheticEvent }) => void; + }; + /** + * whether to ignore function properties while comparing column definition + */ + ignoreFunctionInColumnCompare?: boolean; + /** + * A object for the custom components, like `ExpandIcon` and `SortIndicator` + */ + components?: TableComponents; + [key: string]: any; + } + + export interface TableComponents { + TableCell?: React.ElementType<{ + className: string; + isScrolling?: boolean; + cellData: any; + columns: ColumnShape[]; + column: ColumnShape; + columnIndex: number; + rowData: T; + rowIndex: number; + container: BaseTable; + }>; + TableHeaderCell?: React.ElementType<{ + className: string; + columns: ColumnShape[]; + column: ColumnShape; + columnIndex: number; + headerIndex: number; + container: BaseTable; + }>; + ExpandIcon?: React.ElementType<{ + depth: number; + expandable: boolean; + expanded: boolean; + onExpand: (expanded: boolean) => void; + [key: string]: any; + }>; + SortIndicator?: React.ElementType<{ + sortOrder: SortOrder; + className: string; + }>; + } + + export default class BaseTable extends React.Component, any> { + static readonly Column: typeof Column; + static readonly PlaceholderKey = '__placeholder__'; + static defaultProps: Partial; + static propTypes: React.WeakValidationMap; + + /** + * Get the DOM node of the table + */ + getDOMNode(): HTMLDivElement | null; + /** + * Get the column manager + */ + getColumnManager(): any; + /** + * Get internal `expandedRowKeys` state + */ + getExpandedRowKeys(): RowKey[]; + /** + * Get the expanded state, fallback to normal state if not expandable. + */ + getExpandedState(): { + expandedData: T[]; + expandedRowKeys: RowKey[]; + expandedDepthMap: { [key in RowKey]: number }; + }; + /** + * Get the total height of all rows, including expanded rows. + */ + getTotalRowsHeight(): number; + /** + * Get the total width of all columns. + */ + getTotalColumnsWidth(): number; + /** + * Forcefully re-render the inner Grid component. + * + * Calling `forceUpdate` on `Table` may not re-render the inner Grid since it uses `shallowCompare` as a performance optimization. + * Use this method if you want to manually trigger a re-render. + * This may be appropriate if the underlying row data has changed but the row sizes themselves have not. + */ + forceUpdateTable(): void; + /** + * Reset cached offsets for positioning after a specific rowIndex, should be used only in dynamic mode(estimatedRowHeight is provided) + */ + resetAfterRowIndex(rowIndex?: number, shouldForceUpdate?: boolean): void; + /** + * Reset row height cache, useful if `data` changed entirely, should be used only in dynamic mode(estimatedRowHeight is provided) + */ + resetRowHeightCache(): void; + /** + * Scroll to the specified offset. + * Useful for animating position changes. + */ + scrollToPosition(offset: { scrollLeft: number; scrollTop: number }): void; + /** + * Scroll to the specified offset vertically. + */ + scrollToTop(scrollTop: number): void; + /** + * Scroll to the specified offset horizontally. + */ + scrollToLeft(scrollLeft: number): void; + /** + * Scroll to the specified row. + * By default, the table will scroll as little as possible to ensure the row is visible. + * You can control the alignment of the row though by specifying an align property. Acceptable values are: + * + * - `auto` (default) - Scroll as little as possible to ensure the row is visible. + * - `smart` - Same as `auto` if it is less than one viewport away, or it's the same as`center`. + * - `center` - Center align the row within the table. + * - `end` - Align the row to the bottom side of the table. + * - `start` - Align the row to the top side of the table. + */ + scrollToRow(rowIndex?: number, align?: 'auto' | 'smart' | 'center' | 'end' | 'start'): void; + /** + * Set `expandedRowKeys` manually. + * This method is available only if `expandedRowKeys` is uncontrolled. + */ + setExpandedRowKeys(expandedRowKeys: RowKey[]): void; + } + + export interface AutoResizerProps { + /** + * Class name for the component + */ + className?: string; + /** + * the width of the component, will be the container's width if not set + */ + width?: number; + /** + * the height of the component, will be the container's width if not set + */ + height?: number; + /** + * A callback function to render the children component + * The handler is of the shape of `({ width, height }) => node` + */ + children: (size: Size) => React.ReactNode; + /** + * A callback function when the size of the table container changed if the width and height are not set + * The handler is of the shape of `({ width, height }) => *` + */ + onResize?: (size: Size) => void; + } + + export const AutoResizer: React.FC; + + export function renderElement( + renderer: React.ReactElement | ((props: Partial) => React.ReactNode), + props?: T + ): React.ReactNode; + + export function normalizeColumns(elements: React.ReactNode[]): ColumnShape[]; + + export function isObjectEqual(objA: object, objB: object, ignoreFunction?: boolean): boolean; + + export function callOrReturn(funcOrValue: CallOrReturn, ...args: P): T; + + export function hasChildren(data: object): boolean; + + export function unflatten( + array: T[], + rootId?: any, + dataKey?: string, + parentKey?: string + ): (T & { children?: T[] })[]; + + export function flattenOnKeys( + tree: T[], + keys?: RowKey[], + depthMap?: { [key in RowKey]: number }, + dataKey?: string + ): T[]; + + export function getValue(object: any, path?: string, defaultValue?: any): any; + + export function getScrollbarSize(recalculate?: boolean): number; +} From 3f8acae2cf5729ffe863c833d666024f6d0d9df1 Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Thu, 21 Jan 2021 13:25:49 +0800 Subject: [PATCH 49/71] feat: sticky --- components/hi-base-table/index.js | 69 +++++- .../style/hiui-basetable-theme.scss | 4 + docs/demo/base-table/section-tooltip.jsx | 217 +++++++++++++++++- docs/zh-CN/docs/hi-basetable.mdx | 5 + 4 files changed, 283 insertions(+), 12 deletions(-) diff --git a/components/hi-base-table/index.js b/components/hi-base-table/index.js index 78dd27672..a0b431d47 100644 --- a/components/hi-base-table/index.js +++ b/components/hi-base-table/index.js @@ -1,5 +1,5 @@ import React, { useCallback, useEffect, useRef, useState } from 'react' -import BaseTable, { Column, AutoResizer } from 'react-base-table' +import BaseTable, { Column, AutoResizer, SortOrder } from 'react-base-table' import classNames from 'classnames' import _ from 'lodash' import './style/index' @@ -42,12 +42,12 @@ const generateColumns = (columns) => { } const generateData = (data) => { const _data = _.cloneDeep(data) - const setDataId = (data, parentId = 0) => { + const setDataId = (data, parentId) => { data.forEach((item, itemIndex) => { - const id = item.key || itemIndex - const { parentId: _parentId } = item + const id = typeof parentId !== 'undefined' ? parentId + '-' + itemIndex : itemIndex + console.log() item.id = id - item.parentId = typeof _parentId !== 'undefined' ? _parentId : parentId + item.parentId = parentId if (item.children) { setDataId(item.children, id) } @@ -60,7 +60,18 @@ const generateData = (data) => { const HiBaseTable = React.forwardRef((props, ref) => { const wrapperRef = useRef() const timeId = useRef() - let { data, columns, bordered, className, height, width, autoResize = true, fixedToColumn } = props + let { + data, + columns, + bordered, + className, + height, + width, + autoResize = true, + fixedToColumn, + sticky, + stickyTop = 0 + } = props columns = generateColumns(columns || []) const _defaultWidth = columns.reduce((pre, now) => { return pre + now.width @@ -76,11 +87,50 @@ const HiBaseTable = React.forwardRef((props, ref) => { setDefaultWidth(getDefaultWidth()) }, [60]) }, []) + const scroll = useCallback(() => { + if (wrapperRef.current) { + if (sticky) { + const { height: headerH } = wrapperRef.current.querySelector('.hiui-basetable__header').getBoundingClientRect() + const { top, height } = wrapperRef.current.getBoundingClientRect() + const header = wrapperRef.current.querySelectorAll('.hiui-basetable__header') + header.forEach((element) => { + if ( + element.parentNode.classList.contains('hiui-basetable__table-frozen-right') || + element.parentNode.classList.contains('hiui-basetable__table-frozen-left') + ) { + if (top <= stickyTop) { + element.classList.add('header__sticky-row') + element.style.top = stickyTop + 'px' + } else { + element.style.top = 0 + element.classList.remove('header__sticky-row') + } + } else { + element.style.position = 'sticky' + element.style.top = stickyTop + 'px' + } + }) + if (top + height - headerH < stickyTop) { + header.forEach((element) => { + if ( + element.parentNode.classList.contains('hiui-basetable__table-frozen-right') || + element.parentNode.classList.contains('hiui-basetable__table-frozen-left') + ) { + element.style.top = 0 + element.classList.remove('header__sticky-row') + } + }) + } + } + } + }, [stickyTop, wrapperRef, sticky]) useEffect(() => { window.addEventListener('resize', resize) + window.addEventListener('scroll', scroll) setDefaultWidth(getDefaultWidth()) return () => { window.removeEventListener('resize', resize) + window.removeEventListener('scroll', scroll) } }, []) return ( @@ -95,7 +145,10 @@ const HiBaseTable = React.forwardRef((props, ref) => { ref={ref} columns={fixedToColumn ? fixedColumns(columns, fixedToColumn) : columns} data={generateData(data)} - className={classNames({ bordered: bordered, autoResize: autoResize }, className)} + className={classNames( + { bordered: bordered, autoResize: autoResize, 'hiui-basetable__ticky-row': sticky }, + className + )} classPrefix={classPrefix} />
          @@ -105,4 +158,4 @@ HiBaseTable.Column = Column HiBaseTable.PlaceholderKey = BaseTable.PlaceholderKey export default HiBaseTable -export { AutoResizer } +export { AutoResizer, SortOrder } diff --git a/components/hi-base-table/style/hiui-basetable-theme.scss b/components/hi-base-table/style/hiui-basetable-theme.scss index 256ae0c28..3e211dff1 100644 --- a/components/hi-base-table/style/hiui-basetable-theme.scss +++ b/components/hi-base-table/style/hiui-basetable-theme.scss @@ -41,3 +41,7 @@ $row-hovered-background-color: var(--color-primary-20) !default; } } } + +.header__sticky-row { + position: fixed !important; +} diff --git a/docs/demo/base-table/section-tooltip.jsx b/docs/demo/base-table/section-tooltip.jsx index 561ede305..ec2b61405 100644 --- a/docs/demo/base-table/section-tooltip.jsx +++ b/docs/demo/base-table/section-tooltip.jsx @@ -1,10 +1,10 @@ import React from 'react' import DocViewer from '../../../libs/doc-viewer' -import HiBaseTable, { AutoResizer } from '../../../components/hi-base-table' +import HiBaseTable, { AutoResizer, SortOrder } from '../../../components/hi-base-table' import Watermark from '../../../components/watermark' import Tooltip from '../../../components/tooltip' const prefix = 'table-bigdata' -const rightOptions = ['结合Tooltip', '树形表格', '列冻结'] +const rightOptions = ['结合Tooltip', '树形表格', '列冻结', '树形 + 列冻结 + 吸顶 + 排序'] // 格式化 Columns const code = [ { @@ -109,7 +109,7 @@ const code = [ }, { code: `import React from 'react' - import HiBaseTable from '@hi-ui/hiui/es/hi-base-table'\n + import HiBaseTable , {SortOrder} from '@hi-ui/hiui/es/hi-base-table'\n class Demo extends React.Component { constructor(props){ super(props) @@ -148,13 +148,222 @@ const code = [ } }`, opt: ['列冻结'] + }, + { + code: `import React from 'react' + import HiBaseTable from '@hi-ui/hiui/es/hi-base-table'\n + class Demo extends React.Component { + constructor(props){ + super(props) + this.columns = [ + { + key: 'column-0', + dataKey: 'column-0', + title: 'Column 0', + width: 150, + sortable: true + }, + { key: 'column-1', dataKey: 'column-1', title: 'Column 1', width: 150, sortable: true }, + { key: 'column-2', dataKey: 'column-2', title: 'Column 2', width: 150 }, + { key: 'column-3', dataKey: 'column-3', title: 'Column 3', width: 150 }, + { key: 'column-4', dataKey: 'column-4', title: 'Column 4', width: 150 }, + { key: 'column-5', dataKey: 'column-5', title: 'Column 5', width: 150 }, + { key: 'column-6', dataKey: 'column-6', title: 'Column 6', width: 150 }, + { key: 'column-7', dataKey: 'column-7', title: 'Column 7', width: 150 }, + { key: 'column-8', dataKey: 'column-8', title: 'Column 8', width: 150 }, + { key: 'column-9', dataKey: 'column-9', title: 'Column 9', width: 150 }, + { key: 'column-10', dataKey: 'column-10', title: 'Column 10', width: 150 }, + { key: 'column-11', dataKey: 'column-11', title: 'Column 11', width: 150 }, + { key: 'column-12', dataKey: 'column-12', title: 'Column 12', width: 150 }, + { key: 'column-13', dataKey: 'column-13', title: 'Column 13', width: 150 }, + { + key: 'column-14', + dataKey: 'column-14', + title: 'Column 14', + width: 150, + } + ] + this.state = { + data:[ + { + 'column-0': 'Row 0 - Col 0', + 'column-1': 'Row 0 - Col 1', + 'column-2': 'Row 0 - Col 2', + 'column-3': 'Row 0 - Col 3', + 'column-4': 'Row 0 - Col 4', + 'column-5': 'Row 0 - Col 5', + 'column-6': 'Row 0 - Col 6', + 'column-7': 'Row 0 - Col 7', + 'column-8': 'Row 0 - Col 8', + 'column-9': 'Row 0 - Col 9', + 'column-10': 'Row 0 - Col 10', + 'column-11': 'Row 0 - Col 11', + 'column-12': 'Row 0 - Col 12', + 'column-13': 'Row 0 - Col 13', + 'column-14': 'Row 0 - Col 14', + children: [ + { + 'column-0': 'Sub 0', + 'column-1': 'Row 0 - Col 1', + 'column-2': 'Row 0 - Col 2', + 'column-3': 'Row 0 - Col 3', + 'column-4': 'Row 0 - Col 4', + 'column-5': 'Row 0 - Col 5', + 'column-6': 'Row 0 - Col 6', + 'column-7': 'Row 0 - Col 7', + 'column-8': 'Row 0 - Col 8', + 'column-9': 'Row 0 - Col 9', + 'column-10': 'Row 0 - Col 10', + 'column-11': 'Row 0 - Col 11', + 'column-12': 'Row 0 - Col 12', + 'column-13': 'Row 0 - Col 13', + 'column-14': 'Row 0 - Col 14', + children: [ + { + 'column-0': 'Sub 0', + 'column-1': 'Row 0 - Col 1', + 'column-2': 'Row 0 - Col 2', + 'column-3': 'Row 0 - Col 3', + 'column-4': 'Row 0 - Col 4', + 'column-5': 'Row 0 - Col 5', + 'column-6': 'Row 0 - Col 6', + 'column-7': 'Row 0 - Col 7', + 'column-8': 'Row 0 - Col 8', + 'column-9': 'Row 0 - Col 9', + 'column-10': 'Row 0 - Col 10', + 'column-11': 'Row 0 - Col 11', + 'column-12': 'Row 0 - Col 12', + 'column-13': 'Row 0 - Col 13', + 'column-14': 'Row 0 - Col 14', + children: [ + { + 'column-0': 'Sub-Sub 0', + 'column-1': 'Row 0 - Col 1', + 'column-2': 'Row 0 - Col 2', + 'column-3': 'Row 0 - Col 3', + 'column-4': 'Row 0 - Col 4', + 'column-5': 'Row 0 - Col 5', + 'column-6': 'Row 0 - Col 6', + 'column-7': 'Row 0 - Col 7', + 'column-8': 'Row 0 - Col 8', + 'column-9': 'Row 0 - Col 9', + 'column-10': 'Row 0 - Col 10', + 'column-11': 'Row 0 - Col 11', + 'column-12': 'Row 0 - Col 12', + 'column-13': 'Row 0 - Col 13', + 'column-14': 'Row 0 - Col 14', + children: [] + } + ] + }, + { + 'column-0': 'Sub 1', + 'column-1': 'Row 0 - Col 1', + 'column-2': 'Row 0 - Col 2', + 'column-3': 'Row 0 - Col 3', + 'column-4': 'Row 0 - Col 4', + 'column-5': 'Row 0 - Col 5', + 'column-6': 'Row 0 - Col 6', + 'column-7': 'Row 0 - Col 7', + 'column-8': 'Row 0 - Col 8', + 'column-9': 'Row 0 - Col 9', + 'column-10': 'Row 0 - Col 10', + 'column-11': 'Row 0 - Col 11', + 'column-12': 'Row 0 - Col 12', + 'column-13': 'Row 0 - Col 13', + 'column-14': 'Row 0 - Col 14', + children: [ + { + 'column-0': 'Sub-Sub 1', + 'column-1': 'Row 0 - Col 1', + 'column-2': 'Row 0 - Col 2', + 'column-3': 'Row 0 - Col 3', + 'column-4': 'Row 0 - Col 4', + 'column-5': 'Row 0 - Col 5', + 'column-6': 'Row 0 - Col 6', + 'column-7': 'Row 0 - Col 7', + 'column-8': 'Row 0 - Col 8', + 'column-9': 'Row 0 - Col 9', + 'column-10': 'Row 0 - Col 10', + 'column-11': 'Row 0 - Col 11', + 'column-12': 'Row 0 - Col 12', + 'column-13': 'Row 0 - Col 13', + 'column-14': 'Row 0 - Col 14', + children: [] + } + ] + } + ] + }, + { + 'column-0': 'Sub 1', + 'column-1': 'Row 0 - Col 1', + 'column-2': 'Row 0 - Col 2', + 'column-3': 'Row 0 - Col 3', + 'column-4': 'Row 0 - Col 4', + 'column-5': 'Row 0 - Col 5', + 'column-6': 'Row 0 - Col 6', + 'column-7': 'Row 0 - Col 7', + 'column-8': 'Row 0 - Col 8', + 'column-9': 'Row 0 - Col 9', + 'column-10': 'Row 0 - Col 10', + 'column-11': 'Row 0 - Col 11', + 'column-12': 'Row 0 - Col 12', + 'column-13': 'Row 0 - Col 13', + 'column-14': 'Row 0 - Col 14', + children: [] + } + ] + }, + { + 'column-0': 'Row 1 - Col 0', + 'column-1': 'Row 1 - Col 1', + 'column-2': 'Row 1 - Col 2', + 'column-3': 'Row 1 - Col 3', + 'column-4': 'Row 1 - Col 4', + 'column-5': 'Row 1 - Col 5', + 'column-6': 'Row 1 - Col 6', + 'column-7': 'Row 1 - Col 7', + 'column-8': 'Row 1 - Col 8', + 'column-9': 'Row 1 - Col 9', + 'column-10': 'Row 1 - Col 10', + 'column-11': 'Row 1 - Col 11', + 'column-12': 'Row 1 - Col 12', + 'column-13': 'Row 1 - Col 13', + 'column-14': 'Row 1 - Col 14', + children: [] + } + ] + } + } + + + render() { + return { + console.log('column, key, order', column, key, order) + this.setState({ + data: this.state.data.reverse() + }) + }} /> + } + }`, + opt: ['树形 + 列冻结 + 吸顶 + 排序'] } ] const DemoBase = () => ( diff --git a/docs/zh-CN/docs/hi-basetable.mdx b/docs/zh-CN/docs/hi-basetable.mdx index 343a73a85..4c82721ef 100644 --- a/docs/zh-CN/docs/hi-basetable.mdx +++ b/docs/zh-CN/docs/hi-basetable.mdx @@ -64,6 +64,11 @@ import DemoTooltip from '../../demo/base-table/section-tooltip.jsx' | left | 表格从左侧冻结至某列 | string | columns 中对应的 dataKey | - | | right | 表格从右侧冻结至某列 | string | columns 中对应的 dataKey | - | +### + + + ## 其他 - [更多示例](https://autodesk.github.io/react-base-table/examples/default) +- [SortOrder](https://github.com/Autodesk/react-base-table/blob/master/src/SortOrder.js) \ No newline at end of file From baca382d8b10ed40585841997c04c65189b1d903 Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Thu, 21 Jan 2021 14:21:00 +0800 Subject: [PATCH 50/71] feat: sticky --- components/hi-base-table/index.d.ts | 2 ++ components/hi-base-table/index.js | 11 ++++++++++- docs/zh-CN/docs/hi-basetable.mdx | 2 ++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/components/hi-base-table/index.d.ts b/components/hi-base-table/index.d.ts index c91b72452..bf5ed43d1 100644 --- a/components/hi-base-table/index.d.ts +++ b/components/hi-base-table/index.d.ts @@ -160,6 +160,8 @@ declare module 'react-base-table' { right?: string } export interface BaseTableProps { + sticky?: boolean + stickyTop?: number bordered?: boolean, autoResize?: boolean, bordered?: boolean, diff --git a/components/hi-base-table/index.js b/components/hi-base-table/index.js index a0b431d47..e19a06807 100644 --- a/components/hi-base-table/index.js +++ b/components/hi-base-table/index.js @@ -45,7 +45,6 @@ const generateData = (data) => { const setDataId = (data, parentId) => { data.forEach((item, itemIndex) => { const id = typeof parentId !== 'undefined' ? parentId + '-' + itemIndex : itemIndex - console.log() item.id = id item.parentId = parentId if (item.children) { @@ -101,9 +100,17 @@ const HiBaseTable = React.forwardRef((props, ref) => { if (top <= stickyTop) { element.classList.add('header__sticky-row') element.style.top = stickyTop + 'px' + let _w = element.getAttribute('cacheWidth') || parseInt(element.style.width) + element.setAttribute('cacheWidth', _w) + _w = _w - 20 + element.style.width = _w + 'px' } else { element.style.top = 0 element.classList.remove('header__sticky-row') + let _w = element.getAttribute('cacheWidth') || parseInt(element.style.width) + element.setAttribute('cacheWidth', _w) + _w = _w + 20 + element.style.width = _w + 'px' } } else { element.style.position = 'sticky' @@ -118,6 +125,8 @@ const HiBaseTable = React.forwardRef((props, ref) => { ) { element.style.top = 0 element.classList.remove('header__sticky-row') + } else { + element.style.position = 'static' } }) } diff --git a/docs/zh-CN/docs/hi-basetable.mdx b/docs/zh-CN/docs/hi-basetable.mdx index 4c82721ef..74f2b7d14 100644 --- a/docs/zh-CN/docs/hi-basetable.mdx +++ b/docs/zh-CN/docs/hi-basetable.mdx @@ -39,6 +39,8 @@ import DemoTooltip from '../../demo/base-table/section-tooltip.jsx' | ------------------ | ------------------------------------------------------------------------------------------------------- | ---------------------------------------------- | ----------------------------------------- | ---------- | | data | 表格数据 | object[] | - | - | | columns | 表格列配置信息 | ColumnItem[] | - | - | +| sticky | 是否支持表头吸顶 | boolean | true \| false | false | +| stickyTop | 表头吸顶距离视口顶部距离 | number | - | 0 | | bordered | 是否显示边框(表头分组模式下,表格自带边框) | boolean | true \| false | false | | autoResize | 是否自适应(设置为**false**后, 需要手动定义**column**的宽度) | boolean | true \| false | true | | fixedToColumn | 表格列冻结设置,为 string 时仅支持从左侧冻结至某一列 | string \| FixedOption | columns 中对应的 dataKey | null | From 862f3331ee52c3c063ede4f00c508366b2f4f216 Mon Sep 17 00:00:00 2001 From: wugaoliang Date: Thu, 21 Jan 2021 22:21:30 +0800 Subject: [PATCH 51/71] fix: #1524 --- CHANGELOG.md | 1 + components/form/Item.js | 8 ++++++-- docs/demo/form/section-dynamic.jsx | 7 ++++--- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2db2212e3..06907678d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - 修复 `Select` 组件分组形态 filterOption 函数无法使用问题 [#1497](https://github.com/XiaoMi/hiui/issues/1497) - 修复 `Select` 组件分组形态全选以及受控问题 [#1501](https://github.com/XiaoMi/hiui/issues/1501) - 修复 `Tabs` 组件垂直方向样式显示异常问题 [#1493](https://github.com/XiaoMi/hiui/issues/1493) +- 修复 `Form` DatePicker、SelectTree 在 Form.Item 中点击清空Icon 无效问题 [#1524](https://github.com/XiaoMi/hiui/issues/1524) - 优化 `Checkbox` 样式相关内容 [#1482](https://github.com/XiaoMi/hiui/issues/1482) - 优化 `SelectTree` 异步受控数据返显问题 [#1510](https://github.com/XiaoMi/hiui/issues/1510) ## 3.3.0 diff --git a/components/form/Item.js b/components/form/Item.js index c0613d854..42b144860 100644 --- a/components/form/Item.js +++ b/components/form/Item.js @@ -228,13 +228,17 @@ const FormItem = (props) => { }, [props.labelWidth, formProps.labelWidth]) const setEvent = (eventName, component, componentProps, e, ...args) => { - e.persist && e.persist() + const theEisObject = Object.prototype.toString.call(e) === '[object Object]' + theEisObject && Object.prototype.toString.call(e.persist) === '[object Function]' && e.persist() const displayName = component && component.type && component.type.displayName const _props = componentProps || children.props eventName === 'onChange' && _props.onChange && _props.onChange(e, ...args) eventName === 'onBlur' && _props.onBlur && _props.onBlur(e, ...args) - let value = e.target && Object.prototype.hasOwnProperty.call(e.target, valuePropName) ? e.target[valuePropName] : e + let value = + theEisObject && e.target && Object.prototype.hasOwnProperty.call(e.target, valuePropName) + ? e.target[valuePropName] + : e if (displayName === 'Counter') { value = args[0] } diff --git a/docs/demo/form/section-dynamic.jsx b/docs/demo/form/section-dynamic.jsx index 36dd6f2b0..a493ad2df 100644 --- a/docs/demo/form/section-dynamic.jsx +++ b/docs/demo/form/section-dynamic.jsx @@ -12,6 +12,7 @@ import Switch from '../../../components/switch' import DatePicker from '../../../components/date-picker' import Rate from '../../../components/rate' import Upload from '../../../components/upload' +import SelectTree from '../../../components/select-tree' import Grid from '../../../components/grid' const prefix = 'form-dynamic' @@ -175,7 +176,7 @@ const code = [ { controlCounter[0] === 'show' && - + } @@ -195,7 +196,6 @@ const code = [ {console.log('onChange DatePicker', date, dateStr)}} /> @@ -391,7 +391,8 @@ const DemoRow = () => ( DatePicker, Rate, Upload, - Grid + Grid, + SelectTree }} prefix={prefix} leftOptions={leftOptions} From dc17ade4685ff990fcd2b7a232ddd97d0b6f44a7 Mon Sep 17 00:00:00 2001 From: wugaoliang Date: Thu, 21 Jan 2021 22:28:59 +0800 Subject: [PATCH 52/71] fix: #1524 --- CHANGELOG.md | 1 + components/form/Item.js | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 06907678d..221494007 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - 修复 `Form` DatePicker、SelectTree 在 Form.Item 中点击清空Icon 无效问题 [#1524](https://github.com/XiaoMi/hiui/issues/1524) - 优化 `Checkbox` 样式相关内容 [#1482](https://github.com/XiaoMi/hiui/issues/1482) - 优化 `SelectTree` 异步受控数据返显问题 [#1510](https://github.com/XiaoMi/hiui/issues/1510) + ## 3.3.0 - 新增 `Card` 模式模式下 loading 加载中状态 [#1454](https://github.com/XiaoMi/hiui/issues/1454) - 新增 `Table` loading 加载中状态 [#1466](https://github.com/XiaoMi/hiui/issues/1466) diff --git a/components/form/Item.js b/components/form/Item.js index 42b144860..fe9612587 100644 --- a/components/form/Item.js +++ b/components/form/Item.js @@ -228,15 +228,15 @@ const FormItem = (props) => { }, [props.labelWidth, formProps.labelWidth]) const setEvent = (eventName, component, componentProps, e, ...args) => { - const theEisObject = Object.prototype.toString.call(e) === '[object Object]' - theEisObject && Object.prototype.toString.call(e.persist) === '[object Function]' && e.persist() + const beObject = Object.prototype.toString.call(e) === '[object Object]' + beObject && Object.prototype.toString.call(e.persist) === '[object Function]' && e.persist() const displayName = component && component.type && component.type.displayName const _props = componentProps || children.props eventName === 'onChange' && _props.onChange && _props.onChange(e, ...args) eventName === 'onBlur' && _props.onBlur && _props.onBlur(e, ...args) let value = - theEisObject && e.target && Object.prototype.hasOwnProperty.call(e.target, valuePropName) + beObject && e.target && Object.prototype.hasOwnProperty.call(e.target, valuePropName) ? e.target[valuePropName] : e if (displayName === 'Counter') { From 090fa764860dd1aa1b22d27b6e0c1ba3aa5820ee Mon Sep 17 00:00:00 2001 From: wugaoliang Date: Fri, 22 Jan 2021 07:01:09 +0800 Subject: [PATCH 53/71] fix: #1526 --- components/select-tree/components/Trigger.jsx | 6 +++--- components/select-tree/style/index.scss | 13 +++++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/components/select-tree/components/Trigger.jsx b/components/select-tree/components/Trigger.jsx index 5311ea0d9..c2c92a41b 100644 --- a/components/select-tree/components/Trigger.jsx +++ b/components/select-tree/components/Trigger.jsx @@ -38,7 +38,7 @@ const Trigger = ({ <>
          {selectedItems.map((node, index) => ( - {node && node.title || ''} + {(node && node.title) || ''} ))}
          {selectedItems.length === 0 && {placeholder}} @@ -46,7 +46,7 @@ const Trigger = ({ selectedItems.slice(0, showCount || 1).map((node, index) => { return (
          -
          {node && node.title || ''}
          +
          {(node && node.title) || ''}
          {type === 'multiple' && ( +
          +{selectedItems.length - showCount}
          )} diff --git a/components/select-tree/style/index.scss b/components/select-tree/style/index.scss index e46cbe722..feff025b5 100644 --- a/components/select-tree/style/index.scss +++ b/components/select-tree/style/index.scss @@ -17,6 +17,19 @@ display: flex; justify-content: space-between; + &-items--left { + display: flex; + align-items: center; + padding: 0 8px; + height: 22px; + line-height: 22px; + font-size: 12px; + color: var(--color-black); + background-color: var(--color-gray-10); + border-radius: 2px; + box-sizing: border-box; + } + &:hover { border: 1px solid use-color('primary'); From 742712e032556078d20229777bd312977138a550 Mon Sep 17 00:00:00 2001 From: wugaoliang Date: Fri, 22 Jan 2021 07:50:30 +0800 Subject: [PATCH 54/71] feat: #1527 --- components/search/index.d.ts | 2 +- components/select-tree/SelectTreeHook.js | 20 ++++++++++++++++++-- components/select/MultipleInput.js | 18 ++++++++++++++++-- 3 files changed, 35 insertions(+), 5 deletions(-) diff --git a/components/search/index.d.ts b/components/search/index.d.ts index 4303b0823..52d4169ae 100644 --- a/components/search/index.d.ts +++ b/components/search/index.d.ts @@ -13,7 +13,7 @@ interface Props { placeholder?: string data?: DataItem onSearch?: (inputVal: string, item?: DataItem) => void - onChange?: (e: React.ChangeEvent) => void + onChange?: (e: string) => void overlayClassName?: string } declare const Search: React.ComponentType diff --git a/components/select-tree/SelectTreeHook.js b/components/select-tree/SelectTreeHook.js index 4f299ea1b..d2af028a9 100644 --- a/components/select-tree/SelectTreeHook.js +++ b/components/select-tree/SelectTreeHook.js @@ -82,6 +82,7 @@ const SelectTree = ({ checked: [], semiChecked: [] }) + const resizeTimeId = useRef() // 拉平的数据 const [flattenData, setFlattenData] = useState([]) // 关键字搜索值 @@ -146,8 +147,7 @@ const SelectTree = ({ setActiveId(flattenData[0].id) } }, [expandIdsProps, flattenData]) - - useEffect(() => { + const getShowCount = useCallback(() => { if (selectedItemsRef.current) { const sref = selectedItemsRef.current // 多选超过一行时以数字显示 @@ -166,8 +166,21 @@ const SelectTree = ({ } setShowCount(num) } + }, [showCount, selectedItems]) + + useEffect(() => { + getShowCount() }, [selectedItems]) + + const resize = useCallback(() => { + clearTimeout(resizeTimeId.current) + resizeTimeId.current = setTimeout(() => { + getShowCount() + }, [60]) + }, [getShowCount]) + useEffect(() => { + window.addEventListener('resize', resize) if (data) { const { flattenData = [], nodeEntries } = flattenNodesData( data, @@ -196,6 +209,9 @@ const SelectTree = ({ } else if (Array.isArray(data) && data.length > 0) { setActiveId(data[0].id) } + return () => { + window.removeEventListener('resize', resize) + } }, []) // 过滤方法 const searchTreeNode = (val) => { diff --git a/components/select/MultipleInput.js b/components/select/MultipleInput.js index d4eafee4e..8314dcf17 100644 --- a/components/select/MultipleInput.js +++ b/components/select/MultipleInput.js @@ -1,4 +1,4 @@ -import React, { useState, useEffect, useRef } from 'react' +import React, { useState, useEffect, useRef, useCallback } from 'react' import classNames from 'classnames' import _ from 'lodash' import Icon from '../icon' @@ -27,6 +27,20 @@ const MultipleInput = ({ const tagWrapperRef = useRef('') const calShowCountFlag = useRef(true) // 在渲染完成进行测试是否展示 +1 const selectedItems = _.uniqBy(cacheSelectItem.concat(propsSelectItem), transKeys(fieldNames, 'id')) + const resizeTimeId = useRef() + const resize = useCallback(() => { + clearTimeout(resizeTimeId.current) + resizeTimeId.current = setTimeout(() => { + setShowCount(0) + }, [60]) + }, []) + + useEffect(() => { + window.addEventListener('resize', resize) + return () => { + window.removeEventListener('resize', resize) + } + }, []) useEffect(() => { if (multipleMode === 'nowrap' && calShowCountFlag.current && tagWrapperRef.current) { @@ -48,7 +62,7 @@ const MultipleInput = ({ } else { calShowCountFlag.current = true } - }) + }, [selectedItems]) const handleClear = (e) => { e.stopPropagation() From 8fd5b3916dab7768ed35f9ab84173acdefdcc771 Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Fri, 22 Jan 2021 10:17:36 +0800 Subject: [PATCH 55/71] feat: #1527 --- CHANGELOG.md | 1 + components/select/MultipleInput.js | 41 +++++++++++++++++------------- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2db2212e3..74b89c233 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - 修复 `Tabs` 组件垂直方向样式显示异常问题 [#1493](https://github.com/XiaoMi/hiui/issues/1493) - 优化 `Checkbox` 样式相关内容 [#1482](https://github.com/XiaoMi/hiui/issues/1482) - 优化 `SelectTree` 异步受控数据返显问题 [#1510](https://github.com/XiaoMi/hiui/issues/1510) +- 优化 `Select SelectTree` 计数根据窗口自动调整 [#1527](https://github.com/XiaoMi/hiui/issues/1527) ## 3.3.0 - 新增 `Card` 模式模式下 loading 加载中状态 [#1454](https://github.com/XiaoMi/hiui/issues/1454) - 新增 `Table` loading 加载中状态 [#1466](https://github.com/XiaoMi/hiui/issues/1466) diff --git a/components/select/MultipleInput.js b/components/select/MultipleInput.js index 8314dcf17..249aa4185 100644 --- a/components/select/MultipleInput.js +++ b/components/select/MultipleInput.js @@ -28,32 +28,19 @@ const MultipleInput = ({ const calShowCountFlag = useRef(true) // 在渲染完成进行测试是否展示 +1 const selectedItems = _.uniqBy(cacheSelectItem.concat(propsSelectItem), transKeys(fieldNames, 'id')) const resizeTimeId = useRef() - const resize = useCallback(() => { - clearTimeout(resizeTimeId.current) - resizeTimeId.current = setTimeout(() => { - setShowCount(0) - }, [60]) - }, []) - - useEffect(() => { - window.addEventListener('resize', resize) - return () => { - window.removeEventListener('resize', resize) - } - }, []) - - useEffect(() => { + const getShowCount = useCallback(() => { if (multipleMode === 'nowrap' && calShowCountFlag.current && tagWrapperRef.current) { // 多选超过一行时以数字显示 const tagWrapperRect = tagWrapperRef.current.getBoundingClientRect() + let width = 0 let showCountIndex = 0 // 在第几个开始显示折行 const tags = tagWrapperRef.current.querySelectorAll('.hi-select__input--item') tags.forEach((tag, index) => { const tagRect = tag.getBoundingClientRect() width += tagRect.width - if (width + 50 > tagWrapperRect.width && calShowCountFlag.current) { - // 50是留给显示剩余选项的空间 + if (width + 110 > tagWrapperRect.width && calShowCountFlag.current) { + // 110是留给显示剩余选项的空间 calShowCountFlag.current = false showCountIndex = index } @@ -62,6 +49,26 @@ const MultipleInput = ({ } else { calShowCountFlag.current = true } + }, [showCount, selectedItems]) + const resize = useCallback(() => { + clearTimeout(resizeTimeId.current) + resizeTimeId.current = setTimeout(() => { + calShowCountFlag.current = true + setShowCount(0) + getShowCount() + }, [60]) + }, [getShowCount, showCount]) + + useEffect(() => { + window.addEventListener('resize', resize) + return () => { + window.removeEventListener('resize', resize) + } + }, []) + + useEffect(() => { + calShowCountFlag.current = true + getShowCount() }, [selectedItems]) const handleClear = (e) => { From 46d8c72d0b255357afdbc69ff32fea174f145c33 Mon Sep 17 00:00:00 2001 From: wugaoliang Date: Sun, 24 Jan 2021 11:15:19 +0800 Subject: [PATCH 56/71] feat: popper docs --- components/popper/Overlay.js | 39 ++++++++++-------- components/popper/README.md | 79 ++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 17 deletions(-) create mode 100644 components/popper/README.md diff --git a/components/popper/Overlay.js b/components/popper/Overlay.js index 95807efc9..7348ceedf 100644 --- a/components/popper/Overlay.js +++ b/components/popper/Overlay.js @@ -1,7 +1,6 @@ import React, { useState, useRef, useEffect, useCallback } from 'react' import PropTypes from 'prop-types' import classNames from 'classnames' -import _ from 'lodash' import PopperJS from './utils/popper' import { getOffset } from './utils/positionUtils' import useClickOutside from './utils/useClickOutside' @@ -59,19 +58,22 @@ const Overlay = (props) => { } const scrollCallBack = useCallback(() => { - const offset = getOffset(props, state) - offsetData.current = offset - if (staticPopperRef) { - setState( - Object.assign({}, state, { - popperRef: staticPopperRef.current - }) - ) + if (props.attachEle) { + const offset = getOffset(props, state) + offsetData.current = offset + if (staticPopperRef) { + setState( + Object.assign({}, state, { + popperRef: staticPopperRef.current + }) + ) + } } }, [props, state]) useEffect(() => { const { attachEle, container, show } = props + if (attachEle) return const { cacheContainerPosition } = state const offset = getOffset(props, state) offsetData.current = offset @@ -142,13 +144,16 @@ const Overlay = (props) => { ) }, []) - if (!(attachEle && show && children)) return null - - const { offset = getOffset(props, state) } = state - const width = offset.width - const left = offset.left + 'px' - const top = offset.top + 'px' - + if (!(show && children)) return null + let { width, left = 0, top = 0 } = props + let { offset } = state + if (attachEle) { + offset = state.offset || getOffset(props, state) + width = offset.width + left = offset.left + 'px' + top = offset.top + 'px' + } + const placementClassName = offset ? `hi-popper__content--${offset.placement}` : '' return (
          { ref={(node) => { staticPopperRef.current = node }} - className={classNames(className, 'hi-popper__content', `hi-popper__content--${offset.placement}`, { + className={classNames(className, placementClassName, 'hi-popper__content', { 'hi-popper__content--hide': popperHeight === 0 || popperWidth === 0 })} style={{ width, height }} diff --git a/components/popper/README.md b/components/popper/README.md new file mode 100644 index 000000000..0cbdbb880 --- /dev/null +++ b/components/popper/README.md @@ -0,0 +1,79 @@ +# Popper + +基础弹层组件 + +## 快速使用 + +## 依附元素 + +```js +import Popper from "@hi-ui/hiui/es/popper" +import React, { useState, useRef } from "react" +const demo = () => { + const PopperAttachEle = useRef() + const [showPopper, setShowPopper] = useState(false) + return ( +
          +
          popper-attachEle
          + { + setShowPopper(false) + }} + > +
          Popper Content
          +
          +
          + ) +} +``` + +## 无依附元素 + +```js +import Popper from @hi-ui/hiui/es/popper +import React, { useState, useRef } from react +const demo = () => { + const [showPopper, setShowPopper] = useState(false) + return ( +
          + { + setShowPopper(false) + }} + > +
          Popper Content
          +
          +
          + ) +} +``` + +## Props + +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +| --------- | -------------------------------------------- | ----------- | ------------- | ------ | +| show | 弹出层显示隐藏 | boolean | - | false | +| attachEle | 依附元素,会自动显示到该元素下方,并跟随自动 | HTMLElement | - | - | +| container | 弹出层依赖定位的元素,也就是弹出层参考定位的元素 | HTMLElement | - | - | +| width | 弹层宽度,如果存在**attachEle**参数且宽度未传入的情况下,会根据**attachEle**的宽度进行计算,其他情况请传入宽度 | number \| string \| bool | - | - | +| topGap | 距离依附元素的上偏移量,存在 **attachEle** 属性时有效, | number | 0 | 0 | +| leftGap | 距离依附元素的左偏移量,存在 **attachEle** 属性时有效, | number | 0 | 0 | +| zIndex | 堆叠顺序 | number | - | 1060 | +| placement | 位于依附元素的方位 | string | bottom \| bottom-start \| bottom-end \| top \| top-start \| top-end \| left \| left-start \| left-end \| right \| right-start \| right-end \| top-bottom-start(使用该属性会自动根据依附元素距离可视区域自动翻转) \| top-bottom \| left-right \| left-right-start | bottom-start | + +## Events + +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +| --------- | -------------------------------------------- | ----------- | ------------- | ------ | +| onClickOutside | 点击该元素外的回调方法 | function | - |-| +| setOverlayContainer | 如遇到菜单滚动定位问题,试试修改为滚动的区域,并相对其定位 (3.0 新增) | function(triggerNode) | - | () => document.body | From 1f7b1526981b1942c23c6d53a3773ffef201c1b6 Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Mon, 25 Jan 2021 10:47:48 +0800 Subject: [PATCH 57/71] feat: #1536 --- CHANGELOG.md | 1 + components/drawer/index.jsx | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2db2212e3..40c4d3e11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - 修复 `Tabs` 组件垂直方向样式显示异常问题 [#1493](https://github.com/XiaoMi/hiui/issues/1493) - 优化 `Checkbox` 样式相关内容 [#1482](https://github.com/XiaoMi/hiui/issues/1482) - 优化 `SelectTree` 异步受控数据返显问题 [#1510](https://github.com/XiaoMi/hiui/issues/1510) +- 优化 `Drawer` 组件支持className属性 [#1510](https://github.com/XiaoMi/hiui/issues/1510) ## 3.3.0 - 新增 `Card` 模式模式下 loading 加载中状态 [#1454](https://github.com/XiaoMi/hiui/issues/1454) - 新增 `Table` loading 加载中状态 [#1466](https://github.com/XiaoMi/hiui/issues/1466) diff --git a/components/drawer/index.jsx b/components/drawer/index.jsx index f604b0709..0588037a0 100644 --- a/components/drawer/index.jsx +++ b/components/drawer/index.jsx @@ -25,7 +25,8 @@ const DrawerComp = ({ footer, width, showMask = true, - placement = 'right' + placement = 'right', + className }) => { // TODO: 整体可以抽成一个 hooks 供 modal 和 drawer 复用 const defaultContainer = useRef(false) @@ -79,7 +80,7 @@ const DrawerComp = ({ }, [visible, container]) return ReactDOM.createPortal( -
          +
          {showMask && (
          Date: Mon, 25 Jan 2021 16:06:38 +0800 Subject: [PATCH 58/71] fix: _BaseTable.scss path --- components/hi-base-table/style/hiui-basetable-theme.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/hi-base-table/style/hiui-basetable-theme.scss b/components/hi-base-table/style/hiui-basetable-theme.scss index 3e211dff1..4a0b33fcf 100644 --- a/components/hi-base-table/style/hiui-basetable-theme.scss +++ b/components/hi-base-table/style/hiui-basetable-theme.scss @@ -5,7 +5,7 @@ $header-font-weight: 500 !default; $border: 1px solid #e6e7e8 !default; $row-hovered-background-color: var(--color-primary-20) !default; -@import '~react-base-table/es/_BaseTable.scss'; +@import '../../../node_modules/react-base-table/es/_BaseTable.scss'; .#{$table-prefix} { box-shadow: none; From a49d8d98c1790af2dfe81d685c5de9c30997be7c Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Tue, 26 Jan 2021 10:33:02 +0800 Subject: [PATCH 59/71] fix: #1543 --- CHANGELOG.md | 1 + components/select/Select.js | 28 +++++++++++++++++++--------- docs/demo/select/section-async.jsx | 6 +++--- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4698236f7..bc8b108b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - 修复 `SelectTree` 单选形态下受控问题 [#1519](https://github.com/XiaoMi/hiui/issues/1519) - 修复 `Select` 组件分组形态 filterOption 函数无法使用问题 [#1497](https://github.com/XiaoMi/hiui/issues/1497) - 修复 `Select` 组件分组形态全选以及受控问题 [#1501](https://github.com/XiaoMi/hiui/issues/1501) +- 修复 `Select` 异步数据请求返回结果顺序异常 [#1543](https://github.com/XiaoMi/hiui/issues/1543) - 修复 `Tabs` 组件垂直方向样式显示异常问题 [#1493](https://github.com/XiaoMi/hiui/issues/1493) - 修复 `Form` DatePicker、SelectTree 在 Form.Item 中点击清空Icon 无效问题 [#1524](https://github.com/XiaoMi/hiui/issues/1524) - 优化 `Checkbox` 样式相关内容 [#1482](https://github.com/XiaoMi/hiui/issues/1482) diff --git a/components/select/Select.js b/components/select/Select.js index f19d0000f..cdfa97199 100644 --- a/components/select/Select.js +++ b/components/select/Select.js @@ -49,7 +49,8 @@ const InternalSelect = (props) => { const [isFocus, setIsFouces] = useState(false) const SelectWrapper = useRef() const targetByKeyDown = useRef(false) - + const CancelToken = HiRequest.CancelToken + let cancel // 存储问题 const [cacheSelectItem, setCacheSelectItem] = useState([]) @@ -403,11 +404,16 @@ const InternalSelect = (props) => { options.params = key ? { [key]: keyword, ...params } : params const _withCredentials = withCredentials || credentials === 'include' - + // 取消上一次的请求 + const CANCEL_STATE = 'Cancel' + typeof cancel === 'function' && cancel(CANCEL_STATE) HiRequest({ url, method, data: data, + cancelToken: new CancelToken((c) => { + cancel = c + }), withCredentials: _withCredentials, error, beforeRequest: (config) => { @@ -415,18 +421,22 @@ const InternalSelect = (props) => { return config }, errorCallback: (err) => { - setLoading(false) + const { message = 'normal' } = err + setLoading(message === CANCEL_STATE) error && error(err) }, ...options }).then( (response) => { - setLoading(false) - const dataItems = transformResponse && transformResponse(response.data, response) - if (Array.isArray(dataItems)) { - setDropdownItems(dataItems) - } else { - console.error('transformResponse return data is not array') + const { message = 'normal' } = response + if (message !== CANCEL_STATE) { + setLoading(false) + const dataItems = transformResponse && transformResponse(response.data, response) + if (Array.isArray(dataItems)) { + setDropdownItems(dataItems) + } else { + console.error('transformResponse return data is not array') + } } }, (error) => { diff --git a/docs/demo/select/section-async.jsx b/docs/demo/select/section-async.jsx index 6beee9f47..8166d9532 100644 --- a/docs/demo/select/section-async.jsx +++ b/docs/demo/select/section-async.jsx @@ -25,7 +25,7 @@ class Demo extends React.Component { key: 'id', url: 'https://mife-gallery.test.mi.com/hiui/stores', transformResponse: (res) => { - if(res.code === 200){ + if(res && res.code === 200){ return res.data } return [] @@ -68,7 +68,7 @@ class Demo extends React.Component { url: 'https://mife-gallery.test.mi.com/hiui/stores', params:{id: keyword}, transformResponse: (res) => { - if(res.code === 200){ + if(res && res.code === 200){ return res.data } return [] @@ -127,7 +127,7 @@ class Demo extends React.Component { url: 'https://mife-gallery.test.mi.com/hiui/stores', params:{id: keyword}, transformResponse: (res) => { - if(res.code === 200){ + if(res && res.code === 200){ return res.data } return [] From 7c3319a958e14e8ed1db144ca01c264087d5593d Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Thu, 28 Jan 2021 12:06:01 +0800 Subject: [PATCH 60/71] feat: #1546 --- CHANGELOG.md | 3 +- components/preview/index.d.ts | 11 + components/preview/index.js | 355 ++++++++++++++++++++++++++++ components/preview/style/index.js | 1 + components/preview/style/index.scss | 77 ++++++ docs/demo/preview/section-base.jsx | 59 +++++ docs/zh-CN/components/preview.mdx | 22 ++ site/locales/en-US.js | 3 +- site/locales/zh-CN.js | 3 +- site/pages/components/index.js | 1 + 10 files changed, 532 insertions(+), 3 deletions(-) create mode 100644 components/preview/index.d.ts create mode 100644 components/preview/index.js create mode 100644 components/preview/style/index.js create mode 100644 components/preview/style/index.scss create mode 100644 docs/demo/preview/section-base.jsx create mode 100644 docs/zh-CN/components/preview.mdx diff --git a/CHANGELOG.md b/CHANGELOG.md index bc8b108b3..f94a2bbfe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,9 @@ # 更新日志 -## 3.3.1 +## 3.4.0 - 优化组件弹出层自动计算合适的左右位置 [#1494](https://github.com/XiaoMi/hiui/issues/1494) +- 新增 `Preview` 预览组件 [#1546](https://github.com/XiaoMi/hiui/issues/1546) - 新增 `Select` 组件 onSearch、onOverlayScroll 方法 [#1522](https://github.com/XiaoMi/hiui/issues/1522) - 修复 `SelectTree` 搜索输入框在输入值时失焦问题 [#1491](https://github.com/XiaoMi/hiui/issues/1491) - 修复 `SelectTree` 单选形态下受控问题 [#1519](https://github.com/XiaoMi/hiui/issues/1519) diff --git a/components/preview/index.d.ts b/components/preview/index.d.ts new file mode 100644 index 000000000..a6ecc2c2a --- /dev/null +++ b/components/preview/index.d.ts @@ -0,0 +1,11 @@ +interface Props { + visible?: boolean + showBar?: boolean + showArrow?: boolean + showCount?: boolean + images?: string[] | object[] + simpleData?: boolean + activeIndex?: number +} +declare const Preview: React.ComponentType +export default Preview diff --git a/components/preview/index.js b/components/preview/index.js new file mode 100644 index 000000000..4ac1ba867 --- /dev/null +++ b/components/preview/index.js @@ -0,0 +1,355 @@ +import React, { useRef, useState, useEffect, useCallback } from 'react' +import { createPortal } from 'react-dom' +import ReactCSSTransitionGroup from 'react-addons-css-transition-group' +import classNames from 'classnames' +import _ from 'lodash' +import Icon from '../icon' +import Loading from '../loading' +import './style/index.js' +// unstable_batchedUpdates +const node = document.createElement('div') +document.body.appendChild(node) +const Preview = ({ + className, + visible: propsvisible, + simpleData, + images, + activeIndex: propsActiveIndex = 0, + duration, + showBar = true, + onClose: propsOnClose, + showArrow, + showCount +}) => { + if (images.length === 0 || !propsvisible) { + return null + } + const previewRef = useRef() + const imgRef = useRef() + const [isLoaded, setIsLoaded] = useState(false) + const [visible, setVisible] = useState(false) + const [isMouseDown, setIsMouseDown] = useState(false) + const [mouseCoord, setMouseCoord] = useState({ mouseX: 0, mouseY: 0 }) + const [activeIndex, setactiveIndex] = useState(propsActiveIndex) + const [style, setStyle] = useState({ + width: 0, + height: 0, + left: 0, + top: 0, + rotate: 0, + scaleX: 1, + scaleY: 1 + }) + const [compileStyle, setCompileStyle] = useState({}) + const autoPlayTimer = useRef(null) + + useEffect(() => { + setVisible(propsvisible) + }, [propsvisible]) + + useEffect(() => { + setactiveIndex(propsActiveIndex) + }, [propsActiveIndex]) + + /** + * 修改图片样式 + * @param {*} args 计算后的部分样式 + */ + const changeImageState = useCallback( + (args) => { + const _style = { ...Object.assign({}, style, { ...args }) } + setStyle(_style) + }, + [style] + ) + + useEffect(() => { + if (isLoaded) { + const _style = _.cloneDeep(style) + const compileStyle = { + width: _style.width, + height: _style.height, + transform: `translateX(${_style.left}px) translateY(${_style.top}px) rotate(${_style.rotate}deg) scaleX(${_style.scaleX}) scaleY(${_style.scaleY})` + } + setCompileStyle({ + ...compileStyle + }) + } + }, [style, isLoaded]) + + const handleMouseMove = useCallback( + (e) => { + if (isMouseDown) { + const diffX = e.clientX - mouseCoord.mouseX + const diffY = e.clientY - mouseCoord.mouseY + setMouseCoord({ + mouseX: e.clientX, + mouseY: e.clientY + }) + changeImageState({ + left: style.left + diffX, + top: style.top + diffY + }) + } + }, + [isMouseDown, mouseCoord, changeImageState] + ) + const getImageCenterXY = useCallback(() => { + const { left, top, width, height } = style + return { + x: left + width / 2, + y: top + height / 2 + } + }, [style]) + /** + * 缩放事件 + * @param {*} x 鼠标位置 x + * @param {*} y 鼠标位置 y + * @param {*} direct 方向 + */ + const handleZoom = useCallback( + (x, y, direct) => { + const speed = 0.05 + const imgCenterXY = getImageCenterXY() + const diffX = x - imgCenterXY.x + const diffY = y - imgCenterXY.y + const { left, top, scaleX, scaleY } = style + const directX = scaleX > 0 ? 1 : -1 + const directY = scaleY > 0 ? 1 : -1 + const _scaleX = scaleX + speed * direct * directX + const _scaleY = scaleY + speed * direct * directY + if (Math.abs(_scaleX) < 0.1 || Math.abs(_scaleY) < 0.1) { + return + } + changeImageState({ + scaleX: _scaleX, + scaleY: _scaleY, + top: top + ((-direct * diffY) / scaleX) * speed * directX, + left: left + ((-direct * diffX) / scaleY) * speed * directY + }) + }, + [getImageCenterXY, changeImageState] + ) + const handleMouseWheel = useCallback( + (e) => { + e.preventDefault() + const direct = e.deltaY + if (direct !== 0) { + const x = e.clientX + const y = e.clientY + handleZoom(x, y, direct) + } + }, + [handleZoom] + ) + const handleMouseDown = useCallback( + (e) => { + e.preventDefault() + e.stopPropagation() + setIsMouseDown(true) + setMouseCoord({ + mouseX: e.nativeEvent.clientX, + mouseY: e.nativeEvent.clientY + }) + }, + [isMouseDown, mouseCoord] + ) + const handleMouseUp = useCallback(() => { + setIsMouseDown(false) + }, [isMouseDown]) + const imageMouseOver = useCallback(() => { + clearInterval(autoPlayTimer.current) + }, []) + const getImgWidthHeight = useCallback( + (imgWidth, imgHeight) => { + let width = 0 + let height = 0 + const { innerHeight, innerWidth } = window + const maxWidth = innerWidth * 0.8 + const maxHeight = innerHeight * 0.8 + let style = {} + width = Math.min(maxWidth, imgWidth) + height = (width / imgWidth) * imgHeight + if (height > maxHeight) { + height = maxHeight + width = (height / imgHeight) * imgWidth + } + style = { + width, + height, + left: (innerWidth - width) / 2, + top: (innerHeight - height) / 2, + scaleX: 1, + scaleY: 1, + rotate: 0 + } + changeImageState(style) + }, + [style] + ) + /** + * 加载图片 加载完成后生成计算样式 + * @param {*} imageIndex 加载的图片索引 + */ + const loadImg = useCallback( + (imageIndex) => { + if (!images || images.length === 0) { + return + } + if (imageIndex >= images.length) { + imageIndex = 0 + } + if (imageIndex < 0) { + imageIndex = images.length - 1 + } + const currentImage = images[imageIndex] + const img = new window.Image() + img.onload = () => { + setactiveIndex(imageIndex) + setIsLoaded(true) + getImgWidthHeight(img.width, img.height) + } + img.src = simpleData ? currentImage : currentImage.url + }, + [images, activeIndex, isLoaded] + ) + /** + * 自动滚动函数,目前 API 未暴露 + */ + const addAutoPlayEvent = useCallback(() => { + if (duration && duration > 0) { + autoPlayTimer.current = setInterval(() => { + loadImg(activeIndex + 1) + }, duration) + } + }, [duration, loadImg]) + const imageMouseOut = useCallback(() => { + clearInterval(autoPlayTimer.current) + addAutoPlayEvent() + }, []) + const handleRotate = useCallback( + (isRight) => { + const { rotate } = style + changeImageState({ + rotate: rotate + 90 * (isRight ? 1 : -1) + }) + }, + [style, changeImageState] + ) + const onClose = useCallback( + (e) => { + setStyle({}) + propsOnClose && propsOnClose() + }, + [propsOnClose] + ) + const clickEvent = useCallback( + (type, event) => { + event.stopPropagation() + event.preventDefault() + const { x, y } = getImageCenterXY() + switch (type) { + case 'zoomIn': + handleZoom(x, y, 1) + break + case 'zoomOut': + handleZoom(x, y, -1) + break + case 'reset': + loadImg(activeIndex) + break + case 'leftRotate': + handleRotate() + break + case 'rightRotate': + handleRotate(true) + break + case 'prev': + loadImg(activeIndex - 1) + break + case 'next': + loadImg(activeIndex + 1) + break + case 'close': + onClose() + break + default: + break + } + }, + [activeIndex, getImageCenterXY] + ) + useEffect(() => { + loadImg(activeIndex) + addAutoPlayEvent() + return () => { + clearInterval(autoPlayTimer.current) + } + }, []) + return createPortal( + +
          + {isLoaded ? ( + + ) : ( + + )} + {showBar && ( +
          { + e.stopPropagation() + e.preventDefault() + }} + > + clickEvent('zoomOut', e)} /> + clickEvent('zoomIn', e)} /> + clickEvent('prev', e)} /> + clickEvent('reset', e)} /> + clickEvent('next', e)} /> + clickEvent('leftRotate', e)} /> + clickEvent('rightRotate', e)} /> +
          + )} +
          + +
          + {showArrow && ( +
          + clickEvent('prev', e)} /> + clickEvent('next', e)} /> +
          + )} + {showCount && ( +
          + {activeIndex + 1} +  /  + {images.length} +
          + )} +
          +
          , + node + ) +} +export default Preview diff --git a/components/preview/style/index.js b/components/preview/style/index.js new file mode 100644 index 000000000..63810a681 --- /dev/null +++ b/components/preview/style/index.js @@ -0,0 +1 @@ +import './index.scss' diff --git a/components/preview/style/index.scss b/components/preview/style/index.scss new file mode 100644 index 000000000..998806959 --- /dev/null +++ b/components/preview/style/index.scss @@ -0,0 +1,77 @@ +.hi-preview { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.54); + z-index: 9999; + user-select: none; + + &__image { + transform: all 0.3s; + + &:hover { + cursor: move; + } + } + + &-toolbar { + position: absolute; + bottom: 20px; + left: 50%; + transform: translate(-50%, -50%); + background: #fff; + padding: 0 32px; + height: 48px; + display: flex; + align-items: center; + border-radius: 28px; + + .hi-icon { + margin: 0 12px; + font-size: 20px; + cursor: pointer; + } + } + + &__close { + position: absolute; + right: 10px; + top: 10px; + width: 36px; + height: 36px; + line-height: 1; + border-radius: 50%; + text-align: center; + cursor: pointer; + } + + &__wrapper-arrow { + position: absolute; + font-size: 72px; + top: 50%; + transform: translateY(-50%); + display: flex; + justify-content: space-between; + color: #fff; + width: 100%; + z-index: -1; + + .hi-icon { + cursor: pointer; + } + } + + &__count { + position: absolute; + top: 12px; + left: 50%; + transform: translateX(-50%); + background: #fff; + padding: 4px 12px; + border-radius: 16px; + min-width: 48px; + text-align: center; + } +} diff --git a/docs/demo/preview/section-base.jsx b/docs/demo/preview/section-base.jsx new file mode 100644 index 000000000..ba1a08f83 --- /dev/null +++ b/docs/demo/preview/section-base.jsx @@ -0,0 +1,59 @@ +import React from 'react' +import DocViewer from '../../../libs/doc-viewer' +import Preview from '../../../components/preview/index.js' +import Grid from '../../../components/grid' +const prefix = 'Preview-base' +const desc = '用于大图预览' +const code = `import React from 'react' +import Button from '@hi-ui/hiui/es/button' +import Preview from '@hi-ui/hiui/es/preview'\n +import Grid from '@hi-ui/hiui/es/grid'\n +class Demo extends React.Component { + constructor() { + super() + this.state = { + previewShow: false, + activeIndex: 0, + images: [ + 'http://i1.mifile.cn/f/i/hiui/docs/card/pic_8.png', + 'http://i1.mifile.cn/f/i/hiui/docs/card/pic_7.png', + 'http://i1.mifile.cn/f/i/hiui/docs/card/pic_6.png', + 'http://i1.mifile.cn/f/i/hiui/docs/card/pic_5.png', + 'http://i1.mifile.cn/f/i/hiui/docs/card/pic_4.png', + 'http://i1.mifile.cn/f/i/hiui/docs/card/pic_3.png', + 'http://i1.mifile.cn/f/i/hiui/docs/card/pic_2.png', + 'http://i1.mifile.cn/f/i/hiui/docs/card/pic_1.png', + ] + } + } + render() { + const { images, previewShow, activeIndex } = this.state + const Row = Grid.Row + const Col = Grid.Col + return ( +
          + + { + this.state.images.slice(0, 4).map((url, index) => { + return + this.setState({previewShow: true,activeIndex: index })}/> + + }) + } + + this.setState({previewShow: false})} + /> +
          + ) + } +}` + +const DemoBase = () => +export default DemoBase diff --git a/docs/zh-CN/components/preview.mdx b/docs/zh-CN/components/preview.mdx new file mode 100644 index 000000000..bafb677f9 --- /dev/null +++ b/docs/zh-CN/components/preview.mdx @@ -0,0 +1,22 @@ +# Preview 图片预览 + +预览当前图片 + + +## 基础 + +import DemoBase from '../../demo/preview/section-base.jsx' + + + +## Props + +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +| --------- | -------------------------------------------------- | ------------------- | -------------------------------------- | ------- | +| visible | 是否显示预览窗体 | boolean | - | false | +| showBar | 是否显示预览窗体下方工具条 | boolean | true \| false | true | +| showArrow | 是否显示预览窗体两侧切换按钮 | boolean | true \| false | false | +| showCount | 是否显示预览窗体上方图片数量 | boolean | true \| false | false | +| images | 图片列表 | String[] \| Object[] | - | [] | +| simpleData| 是否为简单数据: | boolean | true \| false | false | +| activeIndex| 激活图片索引,从0开始 | number | true \| false | 0 | \ No newline at end of file diff --git a/site/locales/en-US.js b/site/locales/en-US.js index 232715096..5d7768fd5 100644 --- a/site/locales/en-US.js +++ b/site/locales/en-US.js @@ -49,7 +49,8 @@ module.exports = { timeline: 'Timeline', transfer: 'Transfer', switch: 'Switch', - rate: 'Rate' + rate: 'Rate', + preview: 'preview' }, designs: { 'design-patterns': '设计模式', diff --git a/site/locales/zh-CN.js b/site/locales/zh-CN.js index 13c94f639..a0f85a412 100755 --- a/site/locales/zh-CN.js +++ b/site/locales/zh-CN.js @@ -132,7 +132,8 @@ module.exports = { list: 'List 列表', filter: 'Filter 筛选', charts: 'Charts 图表', - 'rich-text-editor': 'RichTextEditor 富文本编辑' + 'rich-text-editor': 'RichTextEditor 富文本编辑', + preview: 'preview 预览' }, designs: { summarize: '概述', diff --git a/site/pages/components/index.js b/site/pages/components/index.js index 920dd9a95..7eb557fa9 100755 --- a/site/pages/components/index.js +++ b/site/pages/components/index.js @@ -58,6 +58,7 @@ export default { 'group-tips': { modal: components.modal, drawer: components.drawer, + preview: components.preview, notification: components.notification, message: components.message, alert: components.alert, From 2497f5ff57fed30cee7f594a0da02059b51bb785 Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Thu, 28 Jan 2021 14:53:18 +0800 Subject: [PATCH 61/71] fix: #1547 --- CHANGELOG.md | 1 + components/date-picker/BasePicker.jsx | 18 ++++- .../date-picker/components/Calender.jsx | 17 +++-- .../date-picker/hooks/useCalenderData.js | 73 +++++++++++++++++-- docs/zh-CN/components/date-picker.mdx | 2 +- 5 files changed, 91 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc8b108b3..70a2d6cc1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - 修复 `Select` 异步数据请求返回结果顺序异常 [#1543](https://github.com/XiaoMi/hiui/issues/1543) - 修复 `Tabs` 组件垂直方向样式显示异常问题 [#1493](https://github.com/XiaoMi/hiui/issues/1493) - 修复 `Form` DatePicker、SelectTree 在 Form.Item 中点击清空Icon 无效问题 [#1524](https://github.com/XiaoMi/hiui/issues/1524) +- 修复 `DatePicker` minDate、maxDate、disabledDate 在非 date 类型类型下不生效问题 [#1547](https://github.com/XiaoMi/hiui/issues/1547) - 优化 `Checkbox` 样式相关内容 [#1482](https://github.com/XiaoMi/hiui/issues/1482) - 优化 `SelectTree` 异步受控数据返显问题 [#1510](https://github.com/XiaoMi/hiui/issues/1510) - 优化 `Select SelectTree` 计数根据窗口自动调整 [#1527](https://github.com/XiaoMi/hiui/issues/1527) diff --git a/components/date-picker/BasePicker.jsx b/components/date-picker/BasePicker.jsx index b0843d0b8..1d32ce1ad 100644 --- a/components/date-picker/BasePicker.jsx +++ b/components/date-picker/BasePicker.jsx @@ -24,8 +24,6 @@ const BasePicker = ({ clearable = true, width = 'auto', weekOffset, - min = null, - max = null, hourStep, minuteStep, secondStep, @@ -42,8 +40,21 @@ const BasePicker = ({ placement = 'top-bottom-start', inputReadOnly, locale, + disabledDate, ...otherPorps }) => { + // 兼容2.x api -> max,min + const [max, setMax] = useState(otherPorps.max || otherPorps.maxDate || null) + const [min, setMin] = useState(otherPorps.min || otherPorps.minDate || null) + + useEffect(() => { + setMax(otherPorps.max || otherPorps.maxDate || null) + }, [otherPorps.max, otherPorps.maxDate]) + + useEffect(() => { + setMin(otherPorps.min || otherPorps.minDate || null) + }, [otherPorps.min, otherPorps.minDate]) + const cacheDate = useRef(null) const [inputFocus, setInputFocus] = useState(false) const [type, setType] = useState(propType) @@ -179,7 +190,8 @@ const BasePicker = ({ minuteStep, secondStep, inputReadOnly, - value + value, + disabledDate }} > { const ele = e.target - // let {year, month} = deconstructDate(date) const panelDate = _.cloneDeep(renderDate) if (type.includes('range')) { const val = ele.getAttribute('value') @@ -171,9 +172,9 @@ const Calender = ({ view = 'date', originDate, onPick, range, mouseMove, panelPo } return (
          - -
          -
          {holidayFullName}
          + +
          +
          {holidayFullName}
          @@ -203,12 +204,12 @@ const Calender = ({ view = 'date', originDate, onPick, range, mouseMove, panelPo // className='hi-datepicker__cell' className={getTDClass(cell, largeCell)} > -
          +
          {parseInt(cell.text || cell.value) < 10 ? '0' + (cell.text || cell.value) diff --git a/components/date-picker/hooks/useCalenderData.js b/components/date-picker/hooks/useCalenderData.js index c05a19d40..0ac99b2cb 100644 --- a/components/date-picker/hooks/useCalenderData.js +++ b/components/date-picker/hooks/useCalenderData.js @@ -3,7 +3,7 @@ import moment from 'moment' import { DAY_MILLISECONDS } from '../constants' import _ from 'lodash' -const getYearOrMonthRows = ({ originDate, renderDate, type, view, range, localeDatas }) => { +const getYearOrMonthRows = ({ originDate, renderDate, type, view, range, localeDatas, min, max, disabledDate }) => { const _date = renderDate ? moment(renderDate) : moment() const start = view === 'year' ? _date.year() - 4 : 0 const trs = [[], [], [], []] @@ -42,6 +42,44 @@ const getYearOrMonthRows = ({ originDate, renderDate, type, view, range, localeD if (originDate && (y === originDate.year() || y === originDate.month())) { col.type = 'selected' } + // 判断年月可选状态 + const _y = currentYM.year() + const _m = currentYM.month() + + if (disabledDate && view.includes('year')) { + col.type = disabledDate(_y) ? 'disabled' : col.type + } + if (disabledDate && view.includes('mouth')) { + col.type = disabledDate(_y + '-' + _m) ? 'disabled' : col.type + } + // 年的状态 + if (view.includes('year') && (min || max)) { + if (min) { + const minYear = moment(min).year() + col.type = _y < minYear ? 'disabled' : col.type + } + if (max) { + const maxYear = moment(max).year() + col.type = _y > maxYear ? 'disabled' : col.type + } + } + + if (view.includes('month') && (min || max)) { + if (min) { + const minMoment = moment(min) + const minYear = minMoment.year() + const minMonth = minMoment.month() + col.type = _y < minYear ? 'disabled' : col.type + col.type = _y === minYear && _m < minMonth ? 'disabled' : col.type + } + if (max) { + const maxMoment = moment(max) + const maxYear = maxMoment.year() + const maxMonth = maxMoment.month() + col.type = _y < maxYear ? 'disabled' : col.type + col.type = _y === maxYear && _m > maxMonth ? 'disabled' : col.type + } + } } } return trs @@ -51,7 +89,7 @@ const getTime = (week, y, m) => { const t = r.getTime() - week * DAY_MILLISECONDS return t } -const getDateRows = ({ originDate, range, type, weekOffset, min, max, renderDate, view }) => { +const getDateRows = ({ originDate, range, type, weekOffset, min, max, renderDate, view, disabledDate }) => { const rows = [[], [], [], [], [], []] const today = moment() const _date = moment(renderDate) @@ -82,7 +120,10 @@ const getDateRows = ({ originDate, range, type, weekOffset, min, max, renderDate }) const currentTime = moment(startTimeByCurrentPanel + DAY_MILLISECONDS * (i * 7 + j)) let isPN = false // is Prev Or Next Month - const isDisabled = currentTime.isBefore(moment(min)) || currentTime.isAfter(moment(max)) // isDisabled cell + const isDisabled = + currentTime.isBefore(moment(min)) || + currentTime.isAfter(moment(max)) || + (disabledDate && disabledDate(currentTime)) // isDisabled cell if (i === 0) { // 处理第一行的日期数据 if (j >= firstDayWeek) { @@ -110,7 +151,7 @@ const getDateRows = ({ originDate, range, type, weekOffset, min, max, renderDate if (isDisabled) { col.type = 'disabled' } - if (!isPN && currentTime.isSame(today, 'day')) { + if (!isPN && currentTime.isSame(today, 'day') && col.type !== 'disabled') { col.type = 'today' } if (type.includes('range') && !isPN) { @@ -154,7 +195,19 @@ const getDateRows = ({ originDate, range, type, weekOffset, min, max, renderDate } return rows } -const useDate = ({ view, date, originDate, weekOffset, localeDatas, range, type, min, max, renderDate }) => { +const useDate = ({ + view, + date, + originDate, + weekOffset, + localeDatas, + range, + type, + min, + max, + renderDate, + disabledDate +}) => { const [rows, setRows] = useState([]) useEffect(() => { const _rows = @@ -165,7 +218,10 @@ const useDate = ({ view, date, originDate, weekOffset, localeDatas, range, type, type, view, localeDatas, - range + range, + min, + max, + disabledDate }) : getDateRows({ originDate, @@ -175,10 +231,11 @@ const useDate = ({ view, date, originDate, weekOffset, localeDatas, range, type, min, max, renderDate, - view + view, + disabledDate }) setRows(_rows) - }, [renderDate, view, range, type]) + }, [renderDate, view, range, type, disabledDate]) return [rows] } diff --git a/docs/zh-CN/components/date-picker.mdx b/docs/zh-CN/components/date-picker.mdx index 774d1e468..a4d62699e 100755 --- a/docs/zh-CN/components/date-picker.mdx +++ b/docs/zh-CN/components/date-picker.mdx @@ -56,7 +56,7 @@ import DemoCalendar from '../../demo/date-picker/section-calendar.jsx' | maxDate | 最大日期 | Date | null | null | | disabled | 是否禁用输入框 | boolean | true \| false | false | | inputReadOnly | 设置输入框为只读 | boolean | true \| false | false | -| disabledDate | 不可选择的日期 | (currentDate: Date) => boolean | true \| false | false | +| disabledDate | 不可选择的日期(返回true为不可选) | (currentDate: Date) => boolean | true \| false | false | | clearable | 是否可以清空 | boolean | true \| false | true | | showTime | 是否在日期选择器中显示时间选择器,在使用时请注意 format 的设置 | boolean | true \| false | false | | format | 展示的日期格式,配置参考 [moment.js](http://momentjs.cn/docs/#/displaying/format/) | string | - | - | From 3ae266ab0a1f18973cc132573e328112c8a4bdb8 Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Thu, 28 Jan 2021 14:54:51 +0800 Subject: [PATCH 62/71] fix: #1547 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 70a2d6cc1..50c684545 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ - 修复 `Select` 异步数据请求返回结果顺序异常 [#1543](https://github.com/XiaoMi/hiui/issues/1543) - 修复 `Tabs` 组件垂直方向样式显示异常问题 [#1493](https://github.com/XiaoMi/hiui/issues/1493) - 修复 `Form` DatePicker、SelectTree 在 Form.Item 中点击清空Icon 无效问题 [#1524](https://github.com/XiaoMi/hiui/issues/1524) -- 修复 `DatePicker` minDate、maxDate、disabledDate 在非 date 类型类型下不生效问题 [#1547](https://github.com/XiaoMi/hiui/issues/1547) +- 修复 `DatePicker` minDate、maxDate、disabledDate 在非 date 类型下不生效问题 [#1547](https://github.com/XiaoMi/hiui/issues/1547) - 优化 `Checkbox` 样式相关内容 [#1482](https://github.com/XiaoMi/hiui/issues/1482) - 优化 `SelectTree` 异步受控数据返显问题 [#1510](https://github.com/XiaoMi/hiui/issues/1510) - 优化 `Select SelectTree` 计数根据窗口自动调整 [#1527](https://github.com/XiaoMi/hiui/issues/1527) From 47831d8f22bd7016b1c0d2910b32f0246316bcb3 Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Thu, 28 Jan 2021 16:31:20 +0800 Subject: [PATCH 63/71] feat: #1553 --- CHANGELOG.md | 1 + components/cascader/Cascader.js | 9 ++- components/cascader/index.d.ts | 1 + components/cascader/style/cascader.scss | 6 +- components/date-picker/BasePicker.jsx | 5 +- components/date-picker/components/Root.jsx | 6 +- components/date-picker/index.d.ts | 1 + components/date-picker/style/index.scss | 11 ++- components/input/Input.js | 8 +- components/input/index.d.ts | 1 + components/input/style/index.scss | 5 +- components/select-tree/SelectTreeHook.js | 4 +- components/select-tree/components/Trigger.jsx | 6 +- components/select-tree/index.d.ts | 1 + components/select-tree/style/index.scss | 13 +++- components/select/MultipleInput.js | 4 +- components/select/Select.js | 4 +- components/select/SingleInput.js | 5 +- components/select/index.d.ts | 1 + components/select/style/select-input.scss | 5 +- docs/demo/cascader/section-basic.jsx | 77 ++++++++++++++++++- docs/demo/date-picker/section-normal.jsx | 17 +++- docs/demo/input/section-state.jsx | 20 ++++- docs/demo/select-tree/section-single.jsx | 22 +++++- docs/demo/select/section-type.jsx | 36 ++++++++- docs/zh-CN/components/cascader.mdx | 1 + docs/zh-CN/components/date-picker.mdx | 1 + docs/zh-CN/components/input.mdx | 1 + docs/zh-CN/components/select-tree.mdx | 1 + docs/zh-CN/components/select.mdx | 1 + 30 files changed, 245 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc8b108b3..e78a21c84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## 3.3.1 - 优化组件弹出层自动计算合适的左右位置 [#1494](https://github.com/XiaoMi/hiui/issues/1494) +- 新增 `Select SelectTree Cascader DatePicker Input` 等组件无边框形态 [#1553](https://github.com/XiaoMi/hiui/issues/1553) - 新增 `Select` 组件 onSearch、onOverlayScroll 方法 [#1522](https://github.com/XiaoMi/hiui/issues/1522) - 修复 `SelectTree` 搜索输入框在输入值时失焦问题 [#1491](https://github.com/XiaoMi/hiui/issues/1491) - 修复 `SelectTree` 单选形态下受控问题 [#1519](https://github.com/XiaoMi/hiui/issues/1519) diff --git a/components/cascader/Cascader.js b/components/cascader/Cascader.js index 442af934d..66e4bdb6e 100644 --- a/components/cascader/Cascader.js +++ b/components/cascader/Cascader.js @@ -25,7 +25,8 @@ const Cascader = (props) => { changeOnSelect = false, localeDatas, onChange = noop, - onActiveItemChange = noop + onActiveItemChange = noop, + bordered = true } = props const getLabelKey = useCallback(() => { return fieldNames.label || 'content' @@ -524,7 +525,11 @@ const Cascader = (props) => { tabIndex="0" onKeyDown={handleKeyDown} > -
          +
          boolean clearable?: boolean disabled?: boolean diff --git a/components/cascader/style/cascader.scss b/components/cascader/style/cascader.scss index 456c4d68b..aef4c0a9b 100644 --- a/components/cascader/style/cascader.scss +++ b/components/cascader/style/cascader.scss @@ -43,7 +43,11 @@ $cascader: 'hi-cascader' !default; display: flex; align-items: center; padding: 5px 12px; - border: 1px solid #d8d8d8; + box-sizing: border-box; + + &.bordered { + border: 1px solid #d8d8d8; + } } &__input-keyword { diff --git a/components/date-picker/BasePicker.jsx b/components/date-picker/BasePicker.jsx index b0843d0b8..45d89226c 100644 --- a/components/date-picker/BasePicker.jsx +++ b/components/date-picker/BasePicker.jsx @@ -42,6 +42,7 @@ const BasePicker = ({ placement = 'top-bottom-start', inputReadOnly, locale, + bordered = true, ...otherPorps }) => { const cacheDate = useRef(null) @@ -179,13 +180,15 @@ const BasePicker = ({ minuteStep, secondStep, inputReadOnly, - value + value, + bordered }} > { setShowPanel(true) diff --git a/components/date-picker/components/Root.jsx b/components/date-picker/components/Root.jsx index 0f6ff8344..9cf5e2578 100644 --- a/components/date-picker/components/Root.jsx +++ b/components/date-picker/components/Root.jsx @@ -27,7 +27,8 @@ const Root = ({ theme, width, value, - format + format, + bordered } = useContext(DPContext) const [inputData, setInputData] = useState(outDate) const inputRef = useRef(null) @@ -58,7 +59,8 @@ const Root = ({ inputFocus && 'hi-datepicker__picker--focus', disabled && 'hi-datepicker__picker--disabled', showTime && 'hi-datepicker__picker--hastime', - rangeInputIsError && 'hi-datepicker__picker--error' + rangeInputIsError && 'hi-datepicker__picker--error', + { bordered } ) const renderRange = type.includes('range') || type === 'timeperiod' diff --git a/components/date-picker/index.d.ts b/components/date-picker/index.d.ts index 8288996ab..b82fd9269 100644 --- a/components/date-picker/index.d.ts +++ b/components/date-picker/index.d.ts @@ -32,6 +32,7 @@ interface DateProps extends CommonProps { minDate?: Date max?: Date maxDate?: Date + bordered?: boolean disabledDate?: (currentDate: Date) => boolean showTime?: boolean shortcuts?: string[] | Shortcuts[] diff --git a/components/date-picker/style/index.scss b/components/date-picker/style/index.scss index 40388cef0..0af30b132 100644 --- a/components/date-picker/style/index.scss +++ b/components/date-picker/style/index.scss @@ -45,7 +45,6 @@ $error-color: get-color($palette-secondary, 'danger') !default; &__picker { background: use-color('white'); - border: 1px solid use-color('gray-30'); border-radius: 2px; height: 32px; width: 180px; @@ -55,15 +54,19 @@ $error-color: get-color($palette-secondary, 'danger') !default; justify-content: space-around; box-sizing: border-box; - &:hover:not(.hi-datepicker__picker--disabled):not(.hi-datepicker__picker--error) { + &.bordered { + border: 1px solid use-color('gray-30'); + } + + &:hover:not(.hi-datepicker__picker--disabled):not(.hi-datepicker__picker--error).bordered { border: 1px solid use-color('primary'); } - &--focus { + &--focus.bordered { border: 1px solid use-color('primary'); } - &--error { + &--error.bordered { border: 1px solid $error-color; } diff --git a/components/input/Input.js b/components/input/Input.js index ad84333ee..5702cbfdc 100644 --- a/components/input/Input.js +++ b/components/input/Input.js @@ -93,7 +93,7 @@ class Input extends Component { renderText() { const { hover, active, value } = this.state // clearableTrigger 为内部预留,主要表示清除按钮的触发形态,类型分为 'hover' 和 ‘always’ - const { disabled, type, id, placeholder, clearable, clearableTrigger = 'hover' } = this.props + const { disabled, type, id, placeholder, clearable, clearableTrigger = 'hover', bordered = true } = this.props const { prefix, suffix, prepend, append } = this.state const noClear = ['textarea'] const prefixId = id ? id + '_prefix' : '' @@ -123,7 +123,11 @@ class Input extends Component { // 前置元素 prepend && {prepend} } -
          +
          { // 前缀 prefix && ( diff --git a/components/input/index.d.ts b/components/input/index.d.ts index 5bde14b7b..4fe9d1a6a 100644 --- a/components/input/index.d.ts +++ b/components/input/index.d.ts @@ -8,6 +8,7 @@ interface Props { clearable?: boolean placeholder?: string style?: CSSProperties + bordered?: boolean ref?: string | ((instance: HTMLInputElement | null) => void) | React.RefObject | null | undefined onFocus?: (e: React.FocusEvent) => void onBlur?: (e: React.FocusEvent) => void diff --git a/components/input/style/index.scss b/components/input/style/index.scss index aa337c67f..99b31126f 100644 --- a/components/input/style/index.scss +++ b/components/input/style/index.scss @@ -113,12 +113,15 @@ textarea.#{$input} { position: relative; display: flex; width: 100%; - border: 1px solid use-color('gray-30'); box-sizing: border-box; border-radius: 2px; background-color: use-color('white'); transition: border-color 0.3s; + &.bordered { + border: 1px solid use-color('gray-30'); + } + &:not(.disabled):hover { z-index: 1; border-color: use-color('primary'); diff --git a/components/select-tree/SelectTreeHook.js b/components/select-tree/SelectTreeHook.js index d2af028a9..7fc75d6dd 100644 --- a/components/select-tree/SelectTreeHook.js +++ b/components/select-tree/SelectTreeHook.js @@ -54,7 +54,8 @@ const SelectTree = ({ optionWidth, autoload: propsAutoload, placement = 'top-bottom-start', - emptyContent + emptyContent, + bordered = true }) => { const [isFocus, setIsFocus] = useState(false) const placeholder = propsPlaceholder || localeDatas.selectTree.placeholder @@ -557,6 +558,7 @@ const SelectTree = ({ placeholder={placeholder} checkedEvents={checkedEvents} onTrigger={onTrigger} + bordered={bordered} onClear={handleClear} /> { diff --git a/components/select-tree/components/Trigger.jsx b/components/select-tree/components/Trigger.jsx index c2c92a41b..1686d0c6d 100644 --- a/components/select-tree/components/Trigger.jsx +++ b/components/select-tree/components/Trigger.jsx @@ -14,7 +14,8 @@ const Trigger = ({ selectedItemsRef, placeholder, valueRender, - isFocus + isFocus, + bordered }) => { return (
          { const icon = dropdownShow ? 'up' : 'down' const [showCount, setShowCount] = useState(0) @@ -84,6 +85,7 @@ const MultipleInput = ({ 'multiple-values', `theme__${theme}`, { + bordered, disabled }, { diff --git a/components/select/Select.js b/components/select/Select.js index cdfa97199..9b36e1312 100644 --- a/components/select/Select.js +++ b/components/select/Select.js @@ -39,7 +39,8 @@ const InternalSelect = (props) => { searchable: propsSearchable, fieldNames, overlayClassName, - setOverlayContainer + setOverlayContainer, + bordered = true } = props const selectInputContainer = useRef() const historyData = useRef([]) @@ -604,6 +605,7 @@ const InternalSelect = (props) => { fieldNames={fieldNames} isFocus={isFocus} value={value} + bordered={bordered} onClick={() => { handleInputClick() }} diff --git a/components/select/SingleInput.js b/components/select/SingleInput.js index 09f2e48ed..e7eefa45e 100644 --- a/components/select/SingleInput.js +++ b/components/select/SingleInput.js @@ -16,7 +16,8 @@ const SingleInput = (props) => { selectedItems: propsSelectItem, onClear, fieldNames, - isFocus + isFocus, + bordered } = props const [cacheselectedItems, setCacheselectedItems] = useState(propsSelectItem || []) useEffect(() => { @@ -41,7 +42,7 @@ const SingleInput = (props) => { 'hi-select__input', 'single-value', `theme__${theme}`, - { disabled }, + { disabled, bordered }, { 'hi-select__input__focus': isFocus } diff --git a/components/select/index.d.ts b/components/select/index.d.ts index 58d92e290..b028ee6b1 100644 --- a/components/select/index.d.ts +++ b/components/select/index.d.ts @@ -42,6 +42,7 @@ interface Props { emptyContent?: string | JSX.Element style?: CSSProperties optionWidth?: number + bordered?: boolean onChange?: (selectedIds: string[], changedItem: DataItem) => void onSearch?: (keyword: string) => void onOverlayScroll?: (e: Event) => void diff --git a/components/select/style/select-input.scss b/components/select/style/select-input.scss index 60ba65157..3b6c795fd 100644 --- a/components/select/style/select-input.scss +++ b/components/select/style/select-input.scss @@ -8,7 +8,6 @@ color: use-color('black'); line-height: 1; border-radius: 2px; - border: 1px solid use-color('gray-30'); cursor: pointer; outline: none; user-select: none; @@ -24,6 +23,10 @@ transition: margin 0.2s cubic-bezier(0.645, 0.045, 0.355, 1); } + &.bordered { + border: 1px solid use-color('gray-30'); + } + &.disabled { cursor: not-allowed; background-color: use-color('gray-10'); diff --git a/docs/demo/cascader/section-basic.jsx b/docs/demo/cascader/section-basic.jsx index f8778ed00..c70c46992 100644 --- a/docs/demo/cascader/section-basic.jsx +++ b/docs/demo/cascader/section-basic.jsx @@ -3,7 +3,7 @@ import DocViewer from '../../../libs/doc-viewer' import Cascader from '../../../components/cascader' const prefix = 'section-basic' const desc = '展示从多个收起的备选项中选出的一个选项' -const rightOptions = ['基础', '带默认值', '可清空', '禁用'] +const rightOptions = ['基础', '带默认值', '可清空', '无边框', '禁用'] const code = [ { code: `import React from 'react' @@ -161,6 +161,81 @@ class Demo extends React.Component { { code: `import React from 'react' import Cascader from '@hi-ui/hiui/es/cascader'\n +class Demo extends React.Component { + constructor () { + super() + this.state = { + options: [ + { + id: '手机', + content: '手机', + children: [ + { + id: '小米', + content: '小米', + children: [ + { + id: '小米3', + content: '小米3' + }, + { + id: '小米4', + content: '小米4' + }, + ] + }, + { + id: '红米', + content: '红米', + children: [ + { + id: '红米3', + content: '红米3' + }, + { + id: '红米4', + content: '红米4' + } + ] + } + ] + }, + { + id: '电视', + content: '电视', + children: [ + { + id: '小米电视4A', + content: '小米电视4A' + }, + { + id: '小米电视4C', + content: '小米电视4C' + } + ] + } + ] + } + } + render(){ + return( + { + console.log('on change', id) + }} + defaultValue={['电视','小米电视4C']} + data={this.state.options} + bordered={false} + style={{ width: 200 }} + /> + ) + } +}`, + opt: ['无边框'] + }, + { + code: `import React from 'react' +import Cascader from '@hi-ui/hiui/es/cascader'\n class Demo extends React.Component { constructor () { super() diff --git a/docs/demo/date-picker/section-normal.jsx b/docs/demo/date-picker/section-normal.jsx index 6a0dbd553..6f266af0e 100644 --- a/docs/demo/date-picker/section-normal.jsx +++ b/docs/demo/date-picker/section-normal.jsx @@ -3,7 +3,7 @@ import DocViewer from '../../../libs/doc-viewer' import DatePicker from '../../../components/date-picker' const prefix = 'date-picker-normal' const desc = '以天为粒度,展示“YYYY-MM-DD”' -const rightOptions = ['基础', '带默认值', '受控', '禁用', '限制范围'] +const rightOptions = ['基础', '带默认值', '受控', '禁用', '限制范围', '无边框'] const code = [ { code: `import React from 'react' @@ -88,6 +88,21 @@ class Demo extends React.Component { } }`, opt: ['限制范围'] + }, + { + code: `import React from 'react' +import DatePicker from '@hi-ui/hiui/es/date-picker'\n +class Demo extends React.Component { + render () { + return ( + {console.log('onChange', date, dateStr)}} + /> + ) + } +}`, + opt: ['无边框'] } ] const DemoNormal = () => ( diff --git a/docs/demo/input/section-state.jsx b/docs/demo/input/section-state.jsx index 510972485..9d8f4ee06 100644 --- a/docs/demo/input/section-state.jsx +++ b/docs/demo/input/section-state.jsx @@ -4,7 +4,7 @@ import Grid from '../../../components/grid' import Input from '../../../components/input' import Radio from '../../../components/radio' import Button from '../../../components/button' -const leftOptions = ['基础', '受控', '默认值', '禁用', '可清除', '自动聚焦', '手动聚焦'] +const leftOptions = ['基础', '受控', '默认值', '禁用', '无边框', '可清除', '自动聚焦', '手动聚焦'] const prefix = 'input-state' const desc = '可获取有限长度的字符串,不折行显示' const code = [ @@ -77,6 +77,24 @@ class Demo extends React.Component { import Grid from '@hi-ui/hiui/es/grid' import Radio from '@hi-ui/hiui/es/radio' import Input from '@hi-ui/hiui/es/input'\n +class Demo extends React.Component { + render() { + return ( + + ) + } +}`, + opt: ['无边框'] + }, + { + code: `import React from 'react' +import Grid from '@hi-ui/hiui/es/grid' +import Radio from '@hi-ui/hiui/es/radio' +import Input from '@hi-ui/hiui/es/input'\n class Demo extends React.Component { render() { return ( diff --git a/docs/demo/select-tree/section-single.jsx b/docs/demo/select-tree/section-single.jsx index 18021350a..ef1d36d2b 100644 --- a/docs/demo/select-tree/section-single.jsx +++ b/docs/demo/select-tree/section-single.jsx @@ -3,7 +3,7 @@ import DocViewer from '../../../libs/doc-viewer' import SelectTree from '../../../components/select-tree' import Button from '../../../components/button' const prefix = 'tree-select-single' -const rightOptions = ['基础', '默认值', '默认展开'] +const rightOptions = ['基础', '默认值', '无边框', '默认展开'] const desc = '展示从多个收起的备选项中选出的一个选项' const defaultJson = `constructor () { super() @@ -126,6 +126,26 @@ const code = [ } }`, opt: ['默认展开'] + }, + { + code: `import React from 'react' + import SelectTree from '@hi-ui/hiui/es/select-tree'\n + class Demo extends React.Component { + ${defaultJson} + render () { + const { value, singleList } = this.state + return ( + + ) + } + }`, + opt: ['无边框'] } ] const DemoType = () => ( diff --git a/docs/demo/select/section-type.jsx b/docs/demo/select/section-type.jsx index 0d26fc904..9ecf27be4 100644 --- a/docs/demo/select/section-type.jsx +++ b/docs/demo/select/section-type.jsx @@ -2,7 +2,7 @@ import React from 'react' import DocViewer from '../../../libs/doc-viewer' import Select from '../../../components/select' const prefix = 'alert-autoClose' -const rightOptions = ['基础', '受控', '带默认值', '可清空', '禁用'] +const rightOptions = ['基础', '受控', '带默认值', '可清空', '无边框', '禁用'] const desc = '展示从多个收起的备选项中选出的一个选项' const code = [ { @@ -120,6 +120,40 @@ class Demo extends React.Component { } } + render () { + const { value, singleList } = this.state + return ( +