From fd3de1751eb85956f93395a748e9f218236b4fc4 Mon Sep 17 00:00:00 2001 From: solarjoker Date: Thu, 12 Nov 2020 15:39:42 +0800 Subject: [PATCH 001/136] feat: handle focus --- components/modal/index.jsx | 22 +++++++++++++++++++--- components/modal/style/index.scss | 1 + components/switch/switch.js | 1 + 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/components/modal/index.jsx b/components/modal/index.jsx index 9af9aabe0..d12bcf6bc 100644 --- a/components/modal/index.jsx +++ b/components/modal/index.jsx @@ -42,9 +42,15 @@ const InternalModalComp = ({ defaultContainer.current = getDefaultContainer() } + const focusedElementBeforeOpenModal = useRef(null) + const modalRef = useRef(null) + const [vi, setVi] = useState(false) useEffect(() => { - visible && setVi(true) + if (visible === true) { + setVi(true) + focusedElementBeforeOpenModal.current = document.activeElement + } }, [visible]) const destroy = useCallback(() => { @@ -64,6 +70,7 @@ const InternalModalComp = ({ destroy() } } + modalRef.current.focus() }, [vi]) return createPortal( @@ -82,13 +89,22 @@ const InternalModalComp = ({ } }} /> -
+
{}} + > { - setTimeout(() => setVi(false), 300) + setTimeout(() => { + setVi(false) + focusedElementBeforeOpenModal.current.focus() + }, 300) }} >
{ if ([13, 32].includes(e.keyCode)) { e.stopPropagation() + e.preventDefault() clickSwitch(e) } }} From b05d196eb0be025f4100ab9cd9580e8f2e8dbaec Mon Sep 17 00:00:00 2001 From: solarjoker Date: Fri, 13 Nov 2020 00:51:41 +0800 Subject: [PATCH 002/136] feat: finish modal keyEvents --- components/modal/index.jsx | 86 +++++++++++++++----------------------- 1 file changed, 33 insertions(+), 53 deletions(-) diff --git a/components/modal/index.jsx b/components/modal/index.jsx index d12bcf6bc..f5ddbace0 100644 --- a/components/modal/index.jsx +++ b/components/modal/index.jsx @@ -1,5 +1,5 @@ import React, { useEffect, useRef, useState, useCallback } from 'react' -import { render, unmountComponentAtNode, createPortal } from 'react-dom' +import { unmountComponentAtNode, createPortal } from 'react-dom' import { CSSTransition } from 'react-transition-group' import Classnames from 'classnames' import Provider from '../context/index' @@ -8,6 +8,8 @@ import Icon from '../icon' import './style/index' const PREFIX = 'hi-modal' +const focusableElementsString = + 'a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, [tabindex="0"], [contenteditable]' const getDefaultContainer = () => { const defaultContainer = document.createElement('div') @@ -45,6 +47,35 @@ const InternalModalComp = ({ const focusedElementBeforeOpenModal = useRef(null) const modalRef = useRef(null) + const trapTabKey = useCallback((e) => { + // Find all focusable children + let focusableElements = modalRef.current.querySelectorAll(focusableElementsString) + // Convert NodeList to Array + focusableElements = Array.prototype.slice.call(focusableElements) + const firstTabStop = focusableElements[0] + const lastTabStop = focusableElements[focusableElements.length - 1] + // Check for TAB key press + if (e.keyCode === 9) { + // SHIFT + TAB + if (e.shiftKey) { + if (document.activeElement === firstTabStop) { + e.preventDefault() + lastTabStop.focus() + } + // TAB + } else { + if (document.activeElement === lastTabStop) { + e.preventDefault() + firstTabStop.focus() + } + } + } + // ESCAPE + if (e.keyCode === 27) { + onCancel() + } + }, []) + const [vi, setVi] = useState(false) useEffect(() => { if (visible === true) { @@ -94,7 +125,7 @@ const InternalModalComp = ({ style={{ display: vi === false && 'none' }} tabIndex={-1} ref={modalRef} - onKeyDown={(e) => {}} + onKeyDown={trapTabKey} > { - const confirmContainer = document.createElement('div') - - document.body.appendChild(confirmContainer) - const modal = React.createElement(ModalComp, { - container: confirmContainer, - title, - width: 480, - height: 240, - visible: true, - confirmText, - cancelText, - onConfirm: () => { - onConfirm && onConfirm() - confirmContainer.parentNode.style.removeProperty('overflow') - unmountComponentAtNode(confirmContainer) - confirmContainer.parentNode.removeChild(confirmContainer) - }, - showFooterDivider: false, - children: ( -
- {type !== 'default' && ( - - )} - {content} -
- ), - onCancel: () => { - onCancel && onCancel() - confirmContainer.parentNode.style.removeProperty('overflow') - unmountComponentAtNode(confirmContainer) - confirmContainer.parentNode.removeChild(confirmContainer) - } - }) - render(modal, confirmContainer) -} - const ModalComp = Provider(InternalModalComp) ModalComp.confirm = confirm export default ModalComp From 3e211bf99a66b4cb7fc8dd2448ed7761edab4ea7 Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Fri, 13 Nov 2020 09:56:31 +0800 Subject: [PATCH 003/136] fix: #1314 --- components/index.js | 1 + components/list/index.js | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/components/index.js b/components/index.js index f0f79b065..cd987b36b 100755 --- a/components/index.js +++ b/components/index.js @@ -17,6 +17,7 @@ export { default as Tooltip } from './tooltip' export { default as Popover } from './popover' export { default as Popper } from './popper' export { default as Loading } from './loading' +export { default as List } from './list' export { default as Upload } from './upload' export { default as Tree } from './tree' export { default as Input } from './input' diff --git a/components/list/index.js b/components/list/index.js index fe2c94647..2e7842a61 100644 --- a/components/list/index.js +++ b/components/list/index.js @@ -8,7 +8,7 @@ import './style' const prefixCls = 'hi-list' -const getPagePosition = pagination => { +const getPagePosition = (pagination) => { let pagePosition = 'flex-end' switch (pagination.position) { case 'left': @@ -26,7 +26,7 @@ const getPagePosition = pagination => { } return pagePosition } -const getActionPosition = actionPosition => { +const getActionPosition = (actionPosition) => { let _actionPosition = 'flex-end' switch (actionPosition) { case 'top': @@ -75,9 +75,9 @@ const List = ({ })} key={index} > -
+
{avatar && ( -
+
)} @@ -87,7 +87,7 @@ const List = ({ {action && (
{action(item)} From db9ee46e2907fb3f1a097842ec8f48896dd46175 Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Fri, 13 Nov 2020 09:58:41 +0800 Subject: [PATCH 004/136] fix: #1314 --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c5155c906..658b3206c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # 更新日志 +## 3.1.1 + +- 修复 `List` 组件导出问题 [#1314](https://github.com/XiaoMi/hiui/issues/1314) + ## 3.1.0 - 新增 `Card` bordered 属性配置是否显示边框 [#1296](https://github.com/XiaoMi/hiui/issues/1296) @@ -29,7 +33,6 @@ - 优化 `Breadcrumb` 渲染方式 [#1303](https://github.com/XiaoMi/hiui/issues/1303) - 优化 `Form` 组件 Form.Item 宽度设置问题 [#1295](https://github.com/XiaoMi/hiui/issues/1295) - ## 3.0.0 - 新增:`Slider` 滑块组件 [#1225](https://github.com/XiaoMi/hiui/issues/1225) From e862c6e0877848faeda73fdf4f260da0adb4268a Mon Sep 17 00:00:00 2001 From: solarjoker Date: Fri, 13 Nov 2020 10:39:21 +0800 Subject: [PATCH 005/136] feat: finish drawer keyEvents --- components/drawer/index.jsx | 42 +++++++++++++++++++++++++++++- components/drawer/style/index.scss | 4 +++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/components/drawer/index.jsx b/components/drawer/index.jsx index d310d16db..f604b0709 100644 --- a/components/drawer/index.jsx +++ b/components/drawer/index.jsx @@ -1,10 +1,12 @@ -import React, { useRef, useEffect } from 'react' +import React, { useRef, useEffect, useCallback } from 'react' import ReactDOM from 'react-dom' import Icon from '../icon' import Classnames from 'classnames' import './style/index' const PREFIX = 'hi-drawer' +const focusableElementsString = + 'a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, [tabindex="0"], [contenteditable]' const getDefaultContainer = () => { const defaultContainer = document.createElement('div') @@ -31,13 +33,48 @@ const DrawerComp = ({ defaultContainer.current = getDefaultContainer() } + const focusedElementBeforeOpenModal = useRef(null) + const drawerRef = useRef(null) + + const trapTabKey = useCallback((e) => { + // Find all focusable children + let focusableElements = drawerRef.current.querySelectorAll(focusableElementsString) + // Convert NodeList to Array + focusableElements = Array.prototype.slice.call(focusableElements) + const firstTabStop = focusableElements[0] + const lastTabStop = focusableElements[focusableElements.length - 1] + // Check for TAB key press + if (e.keyCode === 9) { + // SHIFT + TAB + if (e.shiftKey) { + if (document.activeElement === firstTabStop) { + e.preventDefault() + lastTabStop.focus() + } + // TAB + } else { + if (document.activeElement === lastTabStop) { + e.preventDefault() + firstTabStop.focus() + } + } + } + // ESCAPE + if (e.keyCode === 27) { + onClose() + } + }, []) + useEffect(() => { const parent = (container || defaultContainer.current).parentNode // 屏蔽滚动条 if (visible) { parent.style.setProperty('overflow', 'hidden') + focusedElementBeforeOpenModal.current = document.activeElement + drawerRef.current.focus() } else { parent.style.removeProperty('overflow') + focusedElementBeforeOpenModal.current && focusedElementBeforeOpenModal.current.focus() } }, [visible, container]) @@ -54,6 +91,9 @@ const DrawerComp = ({ /> )}
Date: Fri, 13 Nov 2020 14:46:19 +0800 Subject: [PATCH 006/136] feat: finish slider keyEvents --- components/slider/index.js | 47 +++++++++++++++--------------- components/slider/style/index.scss | 3 +- 2 files changed, 24 insertions(+), 26 deletions(-) diff --git a/components/slider/index.js b/components/slider/index.js index 6495d498e..6d2357fd5 100644 --- a/components/slider/index.js +++ b/components/slider/index.js @@ -36,7 +36,6 @@ const Slider = memo( const [value, setValue] = useState(initValue !== undefined ? getValue(initValue) : getValue(defaultValue)) // 是否可拖动 const [canMove, setCanMove] = useState(false) - const [canKeyDown, setCanKeyDown] = useState(false) const [startX, setStartX] = useState() const [startY, setStartY] = useState() @@ -48,7 +47,6 @@ const Slider = memo( const [firstTime, setFirstTime] = useState(0) const [lastTime, setLastTime] = useState(0) const [isMove, setIsMove] = useState(false) - const [isClick, setIsClick] = useState(false) const [max, setMax] = useState(initMax) const [min, setMin] = useState(initMin) const [isInitPage, setIsInitPage] = useState(true) @@ -57,8 +55,6 @@ const Slider = memo( useClickOutside((e) => { setShowTooltip(false) - setCanKeyDown(false) - setIsClick(false) }, document.querySelector(`#${prefixCls}`)) useEffect(() => { @@ -95,8 +91,23 @@ const Slider = memo( // <- -> 键盘事件 const onKeyDown = useCallback( (e) => { - if (e.keyCode === 37 || e.keyCode === 39) { - let _value = e.keyCode === 37 ? value - step : value + step + // home: 36 end: 35 + if (e.keyCode === 36) { + e.preventDefault() + setStartPosition(0) + setValue(min || 0) + onChange(min || 0) + } + if (e.keyCode === 35) { + e.preventDefault() + setStartPosition(100) + setValue(max || 100) + onChange(max || 100) + } + // 方向键 + if ([37, 38, 39, 40].includes(e.keyCode)) { + e.preventDefault() + let _value = e.keyCode === 37 || e.keyCode === 38 ? value - step : value + step if (_value < (min || 0)) { _value = min || 0 } else if (_value > (max || 100)) { @@ -112,14 +123,6 @@ const Slider = memo( [value] ) - useEffect(() => { - if (canKeyDown) { - window.onkeydown = onKeyDown - } else { - window.onkeydown = null - } - }, [canKeyDown, onKeyDown]) - useEffect(() => { if (initValue !== undefined) { const _value = getValue(initValue) @@ -195,9 +198,7 @@ const Slider = memo( e.stopPropagation() setLastTime(new Date().getTime()) setStartPosition(newRightPosition) - // setShowTooltip(false) setCanMove(false) - // setIsClick(false) setIsMove(false) }, [newRightPosition] @@ -233,7 +234,6 @@ const Slider = memo( const { clientX, clientY } = e setCanMove(true) setIsMove(true) - setIsClick(false) setStartPosition(getTrackWidth(value)) if (vertical) { setStartY(clientY) @@ -254,9 +254,7 @@ const Slider = memo( (e) => { if (lastTime - firstTime < 200) { e.stopPropagation() - setIsClick(true) setShowTooltip(true) - setCanKeyDown(true) } }, [lastTime, firstTime] @@ -293,7 +291,6 @@ const Slider = memo( if (initValue === undefined) { setValue(value) } - setIsClick(true) setStartPosition(position) onChange(value) }, @@ -326,13 +323,11 @@ const Slider = memo( onClick={railClick} />
{ - if (!isMove && !isClick) { + if (!isMove) { setShowTooltip(false) } }} @@ -344,6 +339,10 @@ const Slider = memo( }%` }} tabIndex="0" + onKeyDown={onKeyDown} + onBlur={(e) => { + setShowTooltip(false) + }} > Date: Fri, 13 Nov 2020 17:41:05 +0800 Subject: [PATCH 007/136] feat: collapse keyEvents init --- components/collapse/Panel.jsx | 41 ++++++++++++++++++++++++ components/collapse/index.js | 47 ++-------------------------- components/collapse/style/index.scss | 4 +++ 3 files changed, 47 insertions(+), 45 deletions(-) create mode 100644 components/collapse/Panel.jsx diff --git a/components/collapse/Panel.jsx b/components/collapse/Panel.jsx new file mode 100644 index 000000000..7f526889d --- /dev/null +++ b/components/collapse/Panel.jsx @@ -0,0 +1,41 @@ +import React, { useCallback } from 'react' +import classNames from 'classnames' +import Icon from '../icon' + +const Panel = ({ key, arrow, header, disabled = false, isActive, children, onClickPanel, showArrow }) => { + const classnames = classNames('collapse-item', { + 'collapse-item--show': isActive, + 'collapse-item--disabled': disabled + }) + const onKeyDown = useCallback((e) => { + if ([13, 32].includes(e.keyCode)) { + e.preventDefault() + onClickPanel(key) + } + // // home: 36 end: 35 + // if (e.keyCode === 36) { + // } + // if (e.keyCode === 35) { + // } + // 方向键 + // if ([37, 38, 39, 40].includes(e.keyCode)) { + // } + }, []) + return ( +
+
onClickPanel(key)} + tabIndex={!disabled && 0} + onKeyDown={onKeyDown} + > + {showArrow && arrow === 'left' && } +
{header}
+ {showArrow && arrow === 'right' && } +
+
{children}
+
+ ) +} + +export default Panel diff --git a/components/collapse/index.js b/components/collapse/index.js index d033b6059..dbe3977ab 100755 --- a/components/collapse/index.js +++ b/components/collapse/index.js @@ -2,7 +2,7 @@ import React, { Component, Children } from 'react' import PropTypes from 'prop-types' import classNames from 'classnames' import _ from 'lodash' -import Icon from '../icon' +import Panel from './Panel' import './style/index' const noop = () => {} @@ -98,50 +98,7 @@ class Collapse extends Component { } } -class CollapsePanel extends Component { - static propTypes = { - header: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), - disabled: PropTypes.bool, - isActive: PropTypes.bool, - arrow: PropTypes.string, - showArrow: PropTypes.bool, - onClickPanel: PropTypes.func - } - static defaultProps = { - disabled: false - } - render () { - const { - key, - arrow, - header, - disabled, - isActive, - children, - onClickPanel, - showArrow - } = this.props - let classnames = classNames('collapse-item', { - 'collapse-item--show': isActive, - 'collapse-item--disabled': disabled - }) - return ( -
-
onClickPanel(key)}> - {showArrow && arrow === 'left' && ( - - )} -
{header}
- {showArrow && arrow === 'right' && ( - - )} -
-
{children}
-
- ) - } -} -Collapse.Panel = CollapsePanel +Collapse.Panel = Panel export default Collapse diff --git a/components/collapse/style/index.scss b/components/collapse/style/index.scss index d28b2f64e..b112e8ef7 100755 --- a/components/collapse/style/index.scss +++ b/components/collapse/style/index.scss @@ -16,6 +16,10 @@ color: rgba(0, 0, 0, 0.85); cursor: pointer; + &:focus { + outline: none; + } + .collapse-item__title { flex: auto; line-height: 1; From 7cc41c7c09796db3202f9aa2b97b8c6bbed932ee Mon Sep 17 00:00:00 2001 From: solarjoker Date: Fri, 13 Nov 2020 18:08:05 +0800 Subject: [PATCH 008/136] feat: update collapse keyEvents --- components/collapse/Panel.jsx | 17 +++++++++-------- components/collapse/index.js | 10 +++++++++- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/components/collapse/Panel.jsx b/components/collapse/Panel.jsx index 7f526889d..2f13f037e 100644 --- a/components/collapse/Panel.jsx +++ b/components/collapse/Panel.jsx @@ -2,7 +2,7 @@ import React, { useCallback } from 'react' import classNames from 'classnames' import Icon from '../icon' -const Panel = ({ key, arrow, header, disabled = false, isActive, children, onClickPanel, showArrow }) => { +const Panel = ({ key, arrow, header, disabled = false, isActive, children, onClickPanel, showArrow, panelKeys }) => { const classnames = classNames('collapse-item', { 'collapse-item--show': isActive, 'collapse-item--disabled': disabled @@ -12,21 +12,22 @@ const Panel = ({ key, arrow, header, disabled = false, isActive, children, onCli e.preventDefault() onClickPanel(key) } + // // home: 36 end: 35 - // if (e.keyCode === 36) { - // } - // if (e.keyCode === 35) { - // } + if (e.keyCode === 36) { + } + if (e.keyCode === 35) { + } // 方向键 - // if ([37, 38, 39, 40].includes(e.keyCode)) { - // } + if ([37, 38, 39, 40].includes(e.keyCode)) { + } }, []) return (
onClickPanel(key)} - tabIndex={!disabled && 0} + tabIndex={disabled ? -1 : 0} onKeyDown={onKeyDown} > {showArrow && arrow === 'left' && } diff --git a/components/collapse/index.js b/components/collapse/index.js index dbe3977ab..9b666c60b 100755 --- a/components/collapse/index.js +++ b/components/collapse/index.js @@ -34,7 +34,10 @@ class Collapse extends Component { this.state = { activeId: Array.isArray(_activeId) ? _activeId : [_activeId] } + this.panelKeys = [] } + + static getDerivedStateFromProps (nextProps, prevState) { if ( !_.isEqual(nextProps.activeId !== prevState.activeId) && @@ -76,6 +79,10 @@ class Collapse extends Component { if (!child) return const key = child.props.id || child.key || String(index) const { header, disabled, title } = child.props + if(!this.panelKeys.map(p => p.key).includes(key)) { + this.panelKeys.push({key, disabled}) + } + let isActive = accordion ? activeKey[0] === key : activeKey.includes(key) const props = { key, @@ -85,7 +92,8 @@ class Collapse extends Component { arrow: arrowPlacement !== 'left' ? arrowPlacement : arrow, showArrow, children: child.props.children, - onClickPanel: disabled ? noop : () => this.onClickPanel(key) + onClickPanel: disabled ? noop : () => this.onClickPanel(key), + panelKeys: this.panelKeys } newChildren.push(React.cloneElement(child, props)) }) From 2a542add9026d41ab3118d3109c602c9da438632 Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Mon, 16 Nov 2020 09:39:56 +0800 Subject: [PATCH 009/136] fix: #1320 --- CHANGELOG.md | 1 + components/tabs/TabItem.js | 10 +++++----- components/tabs/TabPane.js | 18 ++++-------------- components/tabs/Tabs.js | 9 +++++---- docs/demo/tabs/section-card.jsx | 12 ++---------- 5 files changed, 17 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a41565ff2..69fc0014c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## 3.1.1 - 修复 `List` 组件导出问题 [#1314](https://github.com/XiaoMi/hiui/issues/1314) +- 修复 `Tabs` Tabs.Pane 组件中 tabTitle 属性支持 ReactNode [#1320](https://github.com/XiaoMi/hiui/issues/1320) ## 3.1.0 diff --git a/components/tabs/TabItem.js b/components/tabs/TabItem.js index a06c9ab73..ceb6c287f 100644 --- a/components/tabs/TabItem.js +++ b/components/tabs/TabItem.js @@ -1,7 +1,9 @@ import React from 'react' import classNames from 'classnames' +import _ from 'lodash' import Icon from '../icon' import Tooltip from '../tooltip' + const TabItem = ({ index, prefixCls, @@ -47,16 +49,14 @@ const TabItem = ({ onClick={(e) => handleClick(item, e)} className={itemClasses} draggable={draggable} - data-item={JSON.stringify({ ...item, newIndex: index })} + data-item={JSON.stringify({ ..._.omit(item, 'tabTitle'), newIndex: index })} onDragEnd={(e) => dragEnd(e, item)} onDragStart={(e) => dragStart(e, item)} onMouseEnter={(e) => toggleTooltip(e, item)} onMouseLeave={(e) => toggleTooltip(e, item)} > {tabTitle} - {type === 'desc' && ( - {tabDesc} - )} + {type === 'desc' && {tabDesc}} {editable && closeable && ( )} diff --git a/components/tabs/TabPane.js b/components/tabs/TabPane.js index e26f122ee..2ca75c9b5 100644 --- a/components/tabs/TabPane.js +++ b/components/tabs/TabPane.js @@ -17,31 +17,21 @@ const TabPane = ({ if (activeIdIndex === index) { if (placement === 'horizontal') { animateClass = - `${prefixCls}__` + - (Number(latestActiveIdIndex) > Number(activeIdIndex) - ? 'slide-right' - : 'slide-left') + `${prefixCls}__` + (Number(latestActiveIdIndex) > Number(activeIdIndex) ? 'slide-right' : 'slide-left') } else { animateClass = - `${prefixCls}__` + - (Number(latestActiveIdIndex) > Number(activeIdIndex) - ? 'slide-bottom' - : 'slide-top') + `${prefixCls}__` + (Number(latestActiveIdIndex) > Number(activeIdIndex) ? 'slide-bottom' : 'slide-top') } } if (latestActiveIdIndex === index) { if (placement === 'horizontal') { animateClass = `${prefixCls}__` + - (Number(latestActiveIdIndex) > Number(activeIdIndex) - ? 'slide-right-leave' - : 'slide-left-leave') + (Number(latestActiveIdIndex) > Number(activeIdIndex) ? 'slide-right-leave' : 'slide-left-leave') } else { animateClass = `${prefixCls}__` + - (Number(latestActiveIdIndex) > Number(activeIdIndex) - ? 'slide-bottom-leave' - : 'slide-top-leave') + (Number(latestActiveIdIndex) > Number(activeIdIndex) ? 'slide-bottom-leave' : 'slide-top-leave') } } return animateClass diff --git a/components/tabs/Tabs.js b/components/tabs/Tabs.js index 61d1b2401..edc6f459f 100644 --- a/components/tabs/Tabs.js +++ b/components/tabs/Tabs.js @@ -38,6 +38,7 @@ const Tabs = ({ React.Children.map(children, (child) => { if (child) { const { tabTitle, tabId, tabDesc, disabled, closeable = true, animation } = child.props + // console.log(child.props) const item = { tabTitle, tabId, @@ -117,10 +118,10 @@ const Tabs = ({ }, []) useEffect(() => { - const tabItems = getTabItems() + const { showTabItems, hiddenTabItems } = getTabItems() - setShowTabItems(tabItems.showTabItems) - setHiddentab(tabItems.hiddenTabItems) + setShowTabItems(showTabItems) + setHiddentab(hiddenTabItems) if (canScroll && children.length > childRef.current) { const contain = containRef.current setTimeout(() => { @@ -154,7 +155,7 @@ const Tabs = ({ setTimeout(() => { const { width } = child.getBoundingClientRect() const ink = inkRef.current - if (placement === 'horizontal') { + if (placement === 'horizontal' && ink) { const offsetLeft = child.offsetLeft if (index === 0) { ink.style.width = `${width - 17}px` diff --git a/docs/demo/tabs/section-card.jsx b/docs/demo/tabs/section-card.jsx index 3065802ce..6bdb87e58 100644 --- a/docs/demo/tabs/section-card.jsx +++ b/docs/demo/tabs/section-card.jsx @@ -98,7 +98,7 @@ class Demo extends React.Component { closeable={closeable} key={index} > -
{pane.tabTitle}
+
{paneItem.tabTitle}
) }) @@ -111,13 +111,5 @@ class Demo extends React.Component { } ] -const Demo = () => ( - -) +const Demo = () => export default Demo From 54a45c15a1125135e7d3626ecf85a2473a56ec49 Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Mon, 16 Nov 2020 09:44:30 +0800 Subject: [PATCH 010/136] fix: #1320 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69fc0014c..79a2cb991 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ## 3.1.1 - 修复 `List` 组件导出问题 [#1314](https://github.com/XiaoMi/hiui/issues/1314) -- 修复 `Tabs` Tabs.Pane 组件中 tabTitle 属性支持 ReactNode [#1320](https://github.com/XiaoMi/hiui/issues/1320) +- 修复 `Tabs` Tabs.Pane 组件中 tabTitle 属性传入 ReactNode 报错问题 [#1320](https://github.com/XiaoMi/hiui/issues/1320) ## 3.1.0 From 486c3074eeb408c5f8a4694632001a3c152dc4ff Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Mon, 16 Nov 2020 09:45:20 +0800 Subject: [PATCH 011/136] fix: #1320 --- components/tabs/Tabs.js | 1 - 1 file changed, 1 deletion(-) diff --git a/components/tabs/Tabs.js b/components/tabs/Tabs.js index edc6f459f..475606b5d 100644 --- a/components/tabs/Tabs.js +++ b/components/tabs/Tabs.js @@ -38,7 +38,6 @@ const Tabs = ({ React.Children.map(children, (child) => { if (child) { const { tabTitle, tabId, tabDesc, disabled, closeable = true, animation } = child.props - // console.log(child.props) const item = { tabTitle, tabId, From 2fe3a2d70298a8d76ae8375817548d41c9083e63 Mon Sep 17 00:00:00 2001 From: solarjoker Date: Mon, 16 Nov 2020 12:46:35 +0800 Subject: [PATCH 012/136] fix: #1322 --- components/upload/hooks/useUpload.js | 9 ++++----- docs/zh-CN/components/upload.mdx | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/components/upload/hooks/useUpload.js b/components/upload/hooks/useUpload.js index be1c129ff..6f4f149c4 100644 --- a/components/upload/hooks/useUpload.js +++ b/components/upload/hooks/useUpload.js @@ -25,6 +25,7 @@ const useUpload = ({ useEffect(() => { if (fileList) { updateFileList(fileList) + fileListRef.current = fileList } }, [fileList]) @@ -34,19 +35,17 @@ const useUpload = ({ } let result = true if (onRemove) { - result = onRemove(file) + result = onRemove(file, [...fileListRef.current], index) } if (!fileList) { + const newFileList = [...fileListRef.current] + newFileList.splice(index, 1) if (result === true) { - const newFileList = [...fileListRef.current] - newFileList.splice(index, 1) fileListRef.current = newFileList updateFileList(fileListRef.current) } else if (result && typeof result.then === 'function') { result.then((res) => { if (res === true) { - const newFileList = [...fileListRef.current] - newFileList.splice(index, 1) fileListRef.current = newFileList updateFileList(fileListRef.current) } diff --git a/docs/zh-CN/components/upload.mdx b/docs/zh-CN/components/upload.mdx index 46ce22e12..8f4e2f45d 100755 --- a/docs/zh-CN/components/upload.mdx +++ b/docs/zh-CN/components/upload.mdx @@ -73,7 +73,7 @@ import DemoCustom from '../../demo/upload/section-custom.jsx' | 名称 | 说明 | 类型 | 参数 | 返回值 | | ---------- | ------------------------ | ----------------------------------------------------------- | ------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------- | | onChange | 上传回调 | (file: File, fileList: File[], response: object) => boolean | file: 上传的文件对象
fileList: 当前已上传文件列表集合
response: 响应对象 | boolean \| `Promise`: 返回 false 则已上传文件列表不展示该文件 | -| onRemove | 删除上传的文件 | (file: File) => boolean | file: 移除的文件对象 | boolean \| `Promise`: 返回 false 则不可删除,返回 true 时在前端删除文件 | +| onRemove | 删除上传的文件 | (file: File, fileList: File[], index: number) => boolean | file: 移除的文件对象, fileList: 当前已上传文件列表集合, index 索引 | boolean \| `Promise`: 返回 false 则不可删除,返回 true 时在前端删除文件 | | onDownload | 点击已上传的文件时的回调 | (file: File) => void | file: 点击的文件对象 | - | ## Type From 14a22d33b9810ad8b01f36a630b362724b194188 Mon Sep 17 00:00:00 2001 From: solarjoker Date: Mon, 16 Nov 2020 14:14:02 +0800 Subject: [PATCH 013/136] chore: update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a41565ff2..4dcc6137b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## 3.1.1 - 修复 `List` 组件导出问题 [#1314](https://github.com/XiaoMi/hiui/issues/1314) +- 修复 `Upload` onRemove 的问题 [#1322](https://github.com/XiaoMi/hiui/issues/1322) ## 3.1.0 From 32d5aa5f0a50e0105d84f11b4b5b0171f0b3f4a8 Mon Sep 17 00:00:00 2001 From: solarjoker Date: Mon, 16 Nov 2020 16:42:18 +0800 Subject: [PATCH 014/136] fix: #1325 --- components/table/BodyTable.jsx | 8 ++++++-- components/table/HeaderTable.jsx | 4 +++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/components/table/BodyTable.jsx b/components/table/BodyTable.jsx index 93f1323d0..bea4893f2 100644 --- a/components/table/BodyTable.jsx +++ b/components/table/BodyTable.jsx @@ -24,13 +24,17 @@ const BodyTable = ({ fatherRef, emptyContent }) => { realColumnsWidth, resizable, scrollWidth, - setEachRowHeight + setEachRowHeight, + expandedRender, + rowSelection } = useContext(TableContext) // **************** 获取colgroup const _columns = _.cloneDeep(columns) const depthArray = [] setDepth(_columns, 0, depthArray) - const columnsgroup = flatTreeData(_columns).filter((col) => col.isLast) + const columnsgroup = [rowSelection && 'checkbox', expandedRender && 'expandedButton'] + .concat(flatTreeData(_columns).filter((col) => col.isLast)) + .filter((column) => !!column) // **************** // **************** 同步滚动位置 diff --git a/components/table/HeaderTable.jsx b/components/table/HeaderTable.jsx index 867874eed..d6e29c96f 100644 --- a/components/table/HeaderTable.jsx +++ b/components/table/HeaderTable.jsx @@ -68,7 +68,9 @@ const HeaderTable = ({ isFixed, bodyWidth, rightFixedIndex }) => { setDepth(_columns, 0, depthArray) const maxDepth = depthArray.length > 0 ? Math.max.apply(null, depthArray) : 0 - const columnsgroup = flatTreeData(_columns).filter((col) => col.isLast) + const columnsgroup = [rowSelection && 'checkbox', expandedRender && 'expandedButton'] + .concat(flatTreeData(_columns).filter((col) => col.isLast)) + .filter((column) => !!column) // TODO: 这里是考虑了多级表头的冻结,待优化 // *********全量 col group const allColumns = _.cloneDeep(columns) From e104315a9daf55f402c09c6926b233cbc3d7918f Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Mon, 16 Nov 2020 18:11:22 +0800 Subject: [PATCH 015/136] fix: #1319 --- components/select/Select.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/components/select/Select.js b/components/select/Select.js index 08b85677c..ea2a4f746 100644 --- a/components/select/Select.js +++ b/components/select/Select.js @@ -172,6 +172,7 @@ const InternalSelect = (props) => { const moveFocusedIndex = useCallback( (direction) => { let _focusedIndex = focusedIndex + console.log('focusedIndex', focusedIndex) if (direction === 'up') { dropdownItems .slice(0, _focusedIndex) @@ -205,6 +206,7 @@ const InternalSelect = (props) => { // 按键操作 const handleKeyDown = useCallback( (evt) => { + console.log(evt.keyCode) if (evt.keyCode === 13) { onEnterSelect() } @@ -217,6 +219,10 @@ const InternalSelect = (props) => { evt.preventDefault() moveFocusedIndex('down') } + if (evt.keyCode === 32) { + evt.preventDefault() + setDropdownShow(!dropdownShow) + } }, [onEnterSelect, moveFocusedIndex, moveFocusedIndex] ) From cdc410eac105971ed6af88df7f26efa81cb5378b Mon Sep 17 00:00:00 2001 From: solarjoker Date: Mon, 16 Nov 2020 19:48:07 +0800 Subject: [PATCH 016/136] feat: collpase keyEvents update --- components/collapse/Panel.jsx | 69 +++++++++++++++++++++++++++-------- components/collapse/index.js | 11 +++--- 2 files changed, 59 insertions(+), 21 deletions(-) diff --git a/components/collapse/Panel.jsx b/components/collapse/Panel.jsx index 2f13f037e..ccecd8b04 100644 --- a/components/collapse/Panel.jsx +++ b/components/collapse/Panel.jsx @@ -1,27 +1,66 @@ import React, { useCallback } from 'react' import classNames from 'classnames' import Icon from '../icon' +import _ from 'lodash' -const Panel = ({ key, arrow, header, disabled = false, isActive, children, onClickPanel, showArrow, panelKeys }) => { +const Panel = ({ + key, + arrow, + header, + disabled = false, + isActive, + children, + onClickPanel, + showArrow, + panels, + panelContainer, + idx: panelIndex +}) => { + const _panels = React.Children.toArray(panels) const classnames = classNames('collapse-item', { 'collapse-item--show': isActive, 'collapse-item--disabled': disabled }) - const onKeyDown = useCallback((e) => { - if ([13, 32].includes(e.keyCode)) { - e.preventDefault() - onClickPanel(key) - } + const onKeyDown = useCallback( + (e) => { + if ([13, 32].includes(e.keyCode)) { + e.preventDefault() + onClickPanel(key) + } - // // home: 36 end: 35 - if (e.keyCode === 36) { - } - if (e.keyCode === 35) { - } - // 方向键 - if ([37, 38, 39, 40].includes(e.keyCode)) { - } - }, []) + // home: 36 end: 35 + if (e.keyCode === 36) { + e.preventDefault() + const idx = _.findIndex(_panels, (p) => { + return !p.props.disabled + }) + panelContainer.current && panelContainer.current.querySelectorAll('.collapse-item__head')[idx].focus() + } + if (e.keyCode === 35) { + e.preventDefault() + const idx = _.findLastIndex(_panels, (p) => { + return !p.props.disabled + }) + panelContainer.current && panelContainer.current.querySelectorAll('.collapse-item__head')[idx].focus() + } + // 方向键 + if ([38, 40].includes(e.keyCode)) { + e.preventDefault() + const enablePanels = _panels.filter((p) => !p.props.disabled) + const prevArr = [] + const nextArr = [] + enablePanels.forEach((p, idx) => { + if (idx < panelIndex) { + prevArr.push(idx) + } + if (idx > panelIndex) { + nextArr.push(idx) + } + }) + } + }, + [panels, panelContainer] + ) return (
p.key).includes(key)) { - this.panelKeys.push({key, disabled}) - } let isActive = accordion ? activeKey[0] === key : activeKey.includes(key) const props = { @@ -93,7 +90,9 @@ class Collapse extends Component { showArrow, children: child.props.children, onClickPanel: disabled ? noop : () => this.onClickPanel(key), - panelKeys: this.panelKeys + panels: children, + panelContainer: this.panelContainer, + idx } newChildren.push(React.cloneElement(child, props)) }) @@ -102,7 +101,7 @@ class Collapse extends Component { render () { const { prefixCls, type } = this.props let classnames = classNames(prefixCls, type && `${prefixCls}__${type}`) - return
{this.renderPanels()}
+ return
{this.renderPanels()}
} } From abbc71075b93bced0ee661a9d4bf1d8a6d8302b4 Mon Sep 17 00:00:00 2001 From: wugaoliang Date: Mon, 16 Nov 2020 22:02:38 +0800 Subject: [PATCH 017/136] fix: #1326 --- CHANGELOG.md | 1 + components/date-picker/BasePicker.jsx | 17 +++++--- components/date-picker/components/Root.jsx | 27 ++++++++++--- components/date-picker/components/Time.jsx | 45 +++++++++++---------- components/date-picker/hooks/useDate.js | 18 +-------- components/date-picker/utils.js | 22 ++++++++++ docs/demo/date-picker/section-date-time.jsx | 13 +++++- docs/demo/date-picker/section-normal.jsx | 26 +++++++++++- docs/demo/date-picker/section-scope.jsx | 7 +++- 9 files changed, 123 insertions(+), 53 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe72754bd..235fb1d5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - 修复 `List` 组件导出问题 [#1314](https://github.com/XiaoMi/hiui/issues/1314) - 修复 `Tabs` Tabs.Pane 组件中 tabTitle 属性传入 ReactNode 报错问题 [#1320](https://github.com/XiaoMi/hiui/issues/1320) - 修复 `Upload` onRemove 的问题 [#1322](https://github.com/XiaoMi/hiui/issues/1322) +- 修复 `DatePicker` 受控无法选中值问题 [#1326](https://github.com/XiaoMi/hiui/issues/1326) ## 3.1.0 diff --git a/components/date-picker/BasePicker.jsx b/components/date-picker/BasePicker.jsx index 9f46916c7..f608c54d6 100644 --- a/components/date-picker/BasePicker.jsx +++ b/components/date-picker/BasePicker.jsx @@ -1,9 +1,9 @@ -import React, { useState, useRef, useCallback } from 'react' +import React, { useState, useRef, useCallback, useEffect } from 'react' import moment from 'moment' import DPContext from './context' import Provider from '../context/index' import { useDate, useFormat, useAltData } from './hooks' -import { getInRangeDate } from './utils' +import { getInRangeDate, parseValue } from './utils' import _ from 'lodash' import classNames from 'classnames' import Popper from '../popper/index' @@ -96,7 +96,6 @@ const BasePicker = ({ onChange(returnDate, returnDateStr) } const onPick = (dates, isShowPanel) => { - !value && changeOutDate([].concat(dates)) setTimeout(() => { setShowPanel(isShowPanel) }, 0) @@ -104,6 +103,7 @@ const BasePicker = ({ setInputFocus(false) callback(dates) } + changeOutDate([].concat(dates)) } const clickOutsideEvent = useCallback(() => { @@ -111,13 +111,13 @@ const BasePicker = ({ const isValid = moment(outDateValue).isValid() const { startDate, endDate } = isValid && getInRangeDate(outDate[0], outDate[1], max, min) const _outDate = isValid ? [moment(startDate), moment(endDate)] : [null] - changeOutDate(_outDate) resetStatus() _outDate.forEach((od, index) => { if (od && !od.isSame(cacheDate.current[index])) { callback(_outDate) } }) + changeOutDate(_outDate) }, [outDate]) const onClear = () => { resetStatus() @@ -128,6 +128,12 @@ const BasePicker = ({ setShowPanel(false) setInputFocus(false) }, []) + useEffect(() => { + if (!showPanel && value) { + const d = parseValue(value, type, format) + changeOutDate(d) + } + }, [showPanel, value]) const popperCls = classNames( 'hi-datepicker__popper', type === 'date' && showTime && 'hi-datepicker__popper--time', @@ -167,7 +173,8 @@ const BasePicker = ({ hourStep, minuteStep, secondStep, - inputReadOnly + inputReadOnly, + value }} > { - const { localeDatas, type, outDate, placeholder, showTime, disabled, clearable, theme, width } = useContext(DPContext) + const { + localeDatas, + type, + outDate, + placeholder, + showTime, + disabled, + clearable, + theme, + width, + value, + format + } = useContext(DPContext) + const [inputData, setInputData] = useState(outDate) const inputRef = useRef(null) const [placeholders] = usePlaceholder({ type, @@ -22,7 +37,9 @@ const Root = ({ placeholder, localeDatas }) - + useEffect(() => { + setInputData(value ? parseValue(value, type, format) : outDate) + }, [outDate, value]) const onPickerClickEvent = () => { onTrigger() } @@ -55,7 +72,7 @@ const Root = ({ >
{localeDatas.datePicker.to} { disabledSeconds = () => [], hourStep = 1, minuteStep = 1, - secondStep = 1 + secondStep = 1, + type: PropsType } = useContext(DPContext) const isShowHMS = () => { return { @@ -27,34 +28,36 @@ const Time = ({ date, onChange, timeRangePanelType, startDate }) => { // 设置Date的选中状态 const setDisableTime = (type, i, disabledTime = []) => { let isDisabled = disabledTime.includes(i) + if (PropsType === 'timerange' || PropsType === 'time' || PropsType === 'default') { + if (timeRangePanelType === 'right') { + const { hour, minute, second } = deconstructDate(startDate) + const { hour: endHour, minute: endMinute } = date ? deconstructDate(date) : deconstructDate(new Date()) - if (timeRangePanelType === 'right') { - const { hour, minute, second } = deconstructDate(startDate) - const { hour: endHour, minute: endMinute } = date ? deconstructDate(date) : deconstructDate(new Date()) - - isDisabled = type === 'hour' && hour > i - if (type === 'minute') { - if (endHour === hour) { - isDisabled = minute > i - } - if (endHour < hour) { - isDisabled = true + isDisabled = type === 'hour' && hour > i + if (type === 'minute') { + if (endHour === hour) { + isDisabled = minute > i + } + if (endHour < hour) { + isDisabled = true + } } - } - if (type === 'second') { - if (endHour === hour) { - isDisabled = endMinute === minute && second > i - if (endMinute < minute) { + if (type === 'second') { + if (endHour === hour) { + isDisabled = endMinute === minute && second > i + if (endMinute < minute) { + isDisabled = true + } + } + if (endHour < hour) { isDisabled = true } } - if (endHour < hour) { - isDisabled = true - } } + + return isDisabled } - return isDisabled } const getStep = (type) => { let step = 1 diff --git a/components/date-picker/hooks/useDate.js b/components/date-picker/hooks/useDate.js index 5294e4e65..b02429048 100644 --- a/components/date-picker/hooks/useDate.js +++ b/components/date-picker/hooks/useDate.js @@ -1,22 +1,6 @@ import { useState, useEffect } from 'react' -import moment from 'moment' import _ from 'lodash' - -const parseValue = (value, type, format) => { - if (!value) return [null] - const _value = moment(value) - const isValid = moment(value).isValid() - if (value && typeof value === 'object' && type.includes('range')) { - if (type === 'weekrange') { - return [ - value.start ? moment(value.start).startOf('week') : null, - value.end ? moment(value.end).endOf('week') : null - ] - } - return [value.start ? moment(value.start, format) : null, value.end ? moment(value.end, format) : null] - } - return [isValid ? _value : null] -} +import { parseValue } from '../utils' const useDate = ({ value, defaultValue, cacheDate, type, format }) => { const [outDate, setOutDate] = useState([]) diff --git a/components/date-picker/utils.js b/components/date-picker/utils.js index 32becc485..3e1047734 100644 --- a/components/date-picker/utils.js +++ b/components/date-picker/utils.js @@ -217,3 +217,25 @@ export const getInRangeDate = (momentstartDate, momentendDate, max, min) => { } return { startDate: _startDate, endDate: _endDate } } + +/** + * 格式化value + * @param {Any} value 格式化的值 + * @param {String} type 类型 + * @param {String} format 日期格式 + */ +export const parseValue = (value, type, format) => { + if (!value) return [null] + const _value = moment(value) + const isValid = moment(value).isValid() + if (value && typeof value === 'object' && type.includes('range')) { + if (type === 'weekrange') { + return [ + value.start ? moment(value.start).startOf('week') : null, + value.end ? moment(value.end).endOf('week') : null + ] + } + return [value.start ? moment(value.start, format) : null, value.end ? moment(value.end, format) : null] + } + return [isValid ? _value : null] +} diff --git a/docs/demo/date-picker/section-date-time.jsx b/docs/demo/date-picker/section-date-time.jsx index 5785b1437..11f0c27e7 100644 --- a/docs/demo/date-picker/section-date-time.jsx +++ b/docs/demo/date-picker/section-date-time.jsx @@ -6,13 +6,22 @@ const desc = '以时间点为粒度,展示“YYYY-MM-DD HH:mm:ss”' const code = `import React from 'react' import DatePicker from '@hi-ui/hiui/es/date-picker'\n class Demo extends React.Component { + constructor() { + super() + this.state={ + value: new Date() + } + } render () { return ( { console.log('onChange', date, dateStr) }} + onChange={(date, dateStr) => { + console.log('onChange', date, dateStr) + this.setState({value: dateStr}) + }} /> ) } diff --git a/docs/demo/date-picker/section-normal.jsx b/docs/demo/date-picker/section-normal.jsx index a97dd9ae4..6a0dbd553 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' @@ -37,6 +37,30 @@ class Demo extends React.Component { { code: `import React from 'react' import DatePicker from '@hi-ui/hiui/es/date-picker'\n +class Demo extends React.Component { + constructor() { + super() + this.state={ + value: new Date() + } + } + render () { + return ( + { + console.log('onChange', date, dateStr) + this.setState({value: dateStr}) + }} + /> + ) + } +}`, + opt: ['受控'] + }, + { + code: `import React from 'react' +import DatePicker from '@hi-ui/hiui/es/date-picker'\n class Demo extends React.Component { render () { return ( diff --git a/docs/demo/date-picker/section-scope.jsx b/docs/demo/date-picker/section-scope.jsx index 433c3c696..03c1ea488 100644 --- a/docs/demo/date-picker/section-scope.jsx +++ b/docs/demo/date-picker/section-scope.jsx @@ -21,8 +21,11 @@ class Demo extends React.Component { {console.log('onChange', date, dateStr)}} + value={this.state.rangeDate} + onChange={(date, dateStr) => { + console.log('onChange', date, dateStr) + this.setState({rangeDate: dateStr}) + }} />
) From 27218ae73125374bc2d4d9ad049fd7a56d9ae75a Mon Sep 17 00:00:00 2001 From: wugaoliang Date: Mon, 16 Nov 2020 22:10:38 +0800 Subject: [PATCH 018/136] fix: #1326 --- components/date-picker/BasePicker.jsx | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/components/date-picker/BasePicker.jsx b/components/date-picker/BasePicker.jsx index f608c54d6..4e95edc0a 100644 --- a/components/date-picker/BasePicker.jsx +++ b/components/date-picker/BasePicker.jsx @@ -1,9 +1,9 @@ -import React, { useState, useRef, useCallback, useEffect } from 'react' +import React, { useState, useRef, useCallback } from 'react' import moment from 'moment' import DPContext from './context' import Provider from '../context/index' import { useDate, useFormat, useAltData } from './hooks' -import { getInRangeDate, parseValue } from './utils' +import { getInRangeDate } from './utils' import _ from 'lodash' import classNames from 'classnames' import Popper from '../popper/index' @@ -128,12 +128,6 @@ const BasePicker = ({ setShowPanel(false) setInputFocus(false) }, []) - useEffect(() => { - if (!showPanel && value) { - const d = parseValue(value, type, format) - changeOutDate(d) - } - }, [showPanel, value]) const popperCls = classNames( 'hi-datepicker__popper', type === 'date' && showTime && 'hi-datepicker__popper--time', From 10fa66462258e79d5d4c32690319e44ac4d4634d Mon Sep 17 00:00:00 2001 From: solarjoker Date: Tue, 17 Nov 2020 00:39:14 +0800 Subject: [PATCH 019/136] feat: finshi collapse keyEvents --- components/collapse/Panel.jsx | 36 +++++++++++++++++++++++++++-------- components/collapse/index.js | 2 +- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/components/collapse/Panel.jsx b/components/collapse/Panel.jsx index ccecd8b04..df0820be7 100644 --- a/components/collapse/Panel.jsx +++ b/components/collapse/Panel.jsx @@ -46,20 +46,40 @@ const Panel = ({ // 方向键 if ([38, 40].includes(e.keyCode)) { e.preventDefault() - const enablePanels = _panels.filter((p) => !p.props.disabled) const prevArr = [] const nextArr = [] - enablePanels.forEach((p, idx) => { - if (idx < panelIndex) { - prevArr.push(idx) - } - if (idx > panelIndex) { - nextArr.push(idx) + _panels.forEach((p, idx) => { + if (!p.props.disabled) { + if (idx < panelIndex) { + prevArr.push(idx) + } + if (idx > panelIndex) { + nextArr.push(idx) + } } }) + + let prev + let next + if (prevArr.length > 0) { + prev = prevArr[prevArr.length - 1] + } else if (prevArr.length === 0 && nextArr.length > 0) { + prev = nextArr[nextArr.length - 1] + } + if (nextArr.length > 0) { + next = nextArr[0] + } else if (nextArr.length === 0 && prevArr.length > 0) { + next = prevArr[0] + } + if (e.keyCode === 38 && prev !== undefined) { + panelContainer.current && panelContainer.current.querySelectorAll('.collapse-item__head')[prev].focus() + } + if (e.keyCode === 40 && next !== undefined) { + panelContainer.current && panelContainer.current.querySelectorAll('.collapse-item__head')[next].focus() + } } }, - [panels, panelContainer] + [panels, panelContainer, panelIndex] ) return (
diff --git a/components/collapse/index.js b/components/collapse/index.js index 9407004c1..57fa3918d 100755 --- a/components/collapse/index.js +++ b/components/collapse/index.js @@ -92,7 +92,7 @@ class Collapse extends Component { onClickPanel: disabled ? noop : () => this.onClickPanel(key), panels: children, panelContainer: this.panelContainer, - idx + idx: index } newChildren.push(React.cloneElement(child, props)) }) From 9bb6d8bc1dbd844640a85b05d5f4388e905235f5 Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Tue, 17 Nov 2020 10:13:42 +0800 Subject: [PATCH 020/136] feat: select key --- components/popper/Overlay.js | 5 ++++- components/popper/style/index.scss | 3 ++- components/select/MultipleInput.js | 3 +-- components/select/Select.js | 13 +++++++++---- components/select/SelectDropdown.js | 2 -- components/select/SelectInput.js | 8 ++------ components/select/SingleInput.js | 2 -- components/select/style/index.scss | 1 + 8 files changed, 19 insertions(+), 18 deletions(-) diff --git a/components/popper/Overlay.js b/components/popper/Overlay.js index f05eb680b..ed4103ff6 100644 --- a/components/popper/Overlay.js +++ b/components/popper/Overlay.js @@ -29,7 +29,8 @@ const Overlay = (props) => { onMouseEnter, onMouseLeave, onClickOutside, - overlayClassName + overlayClassName, + onKeyDown } = props const [isAddevent, setIsAddevent] = useState(false) const [state, setState] = useState({ @@ -154,6 +155,8 @@ const Overlay = (props) => { className={classNames(overlayClassName, 'hi-popper__container', { 'hi-popper__container--hide': !show })} + tabIndex="-1" + onKeyDown={onKeyDown} style={{ left, top, zIndex }} >
{ @@ -112,7 +111,7 @@ const MultipleInput = ({ )} {searchable && !disabled && (
- +
)}
diff --git a/components/select/Select.js b/components/select/Select.js index ea2a4f746..87e0d9387 100644 --- a/components/select/Select.js +++ b/components/select/Select.js @@ -206,7 +206,7 @@ const InternalSelect = (props) => { // 按键操作 const handleKeyDown = useCallback( (evt) => { - console.log(evt.keyCode) + console.log(_.cloneDeep(evt), document.activeElement) if (evt.keyCode === 13) { onEnterSelect() } @@ -406,10 +406,14 @@ const InternalSelect = (props) => { ? selectInputContainer.current.getBoundingClientRect().width : null return ( -
+
{ // 自定义options的方向 placement={placement || 'top-bottom-start'} className="hi-select__popper" + onKeyDown={handleKeyDown} + tabIndex="-1" width={optionWidth} onClickOutside={() => { hideDropdown() @@ -472,7 +478,6 @@ const InternalSelect = (props) => { filterOption={filterOption} matchFilter={matchFilter} show={dropdownShow} - handleKeyDown={handleKeyDown} optionWidth={optionWidth} selectInputWidth={selectInputWidth} dropdownItems={dropdownItems} diff --git a/components/select/SelectDropdown.js b/components/select/SelectDropdown.js index 10d205c18..1ce35b824 100644 --- a/components/select/SelectDropdown.js +++ b/components/select/SelectDropdown.js @@ -21,7 +21,6 @@ const SelectDropdown = ({ searchPlaceholder, dropdownItems, localeMap, - handleKeyDown, onSearch, isOnSearch, onClickOption, @@ -259,7 +258,6 @@ const SelectDropdown = ({ onFocus={onFocus} onBlur={onBlur} clearabletrigger="always" - onKeyDown={handleKeyDown} onChange={searchEvent} /> {searchbarValue.length > 0 ? ( diff --git a/components/select/SelectInput.js b/components/select/SelectInput.js index 9b0bf2f0e..7306f215c 100644 --- a/components/select/SelectInput.js +++ b/components/select/SelectInput.js @@ -3,13 +3,9 @@ import SingleInput from './SingleInput' import MultipleInput from './MultipleInput' const InternalSelectInput = (props) => { - const { mode, handleKeyDown } = props + const { mode } = props - return mode === 'multiple' ? ( - - ) : ( - - ) + return mode === 'multiple' ? : } const SelectInput = forwardRef((props, ref) => { return diff --git a/components/select/SingleInput.js b/components/select/SingleInput.js index 0083c4965..51f2a72b9 100644 --- a/components/select/SingleInput.js +++ b/components/select/SingleInput.js @@ -14,7 +14,6 @@ const SingleInput = (props) => { onBlur, onClick, selectedItems: propsSelectItem, - handleKeyDown, onClear, fieldNames, isFocus @@ -69,7 +68,6 @@ const SingleInput = (props) => { type="text" value={selectedItems.length > 0 ? placeholder : ''} placeholder={placeholder} - onKeyDown={handleKeyDown} onFocus={onFocus} onBlur={onBlur} readOnly diff --git a/components/select/style/index.scss b/components/select/style/index.scss index 3539e61c4..c96678826 100644 --- a/components/select/style/index.scss +++ b/components/select/style/index.scss @@ -4,6 +4,7 @@ font-size: 14px; overflow: hidden; vertical-align: middle; + outline: none; &.is-disabled { opacity: 0.4; From 2b9607bb8e7453bf18d9b8cc449f5944c901d5f8 Mon Sep 17 00:00:00 2001 From: Wugaoliang Date: Tue, 17 Nov 2020 10:31:35 +0800 Subject: [PATCH 021/136] fix: #1326 --- components/date-picker/BasePicker.jsx | 5 ++- components/date-picker/hooks/useAltData.js | 52 +++++++++------------- 2 files changed, 23 insertions(+), 34 deletions(-) diff --git a/components/date-picker/BasePicker.jsx b/components/date-picker/BasePicker.jsx index 4e95edc0a..0c6914875 100644 --- a/components/date-picker/BasePicker.jsx +++ b/components/date-picker/BasePicker.jsx @@ -60,13 +60,14 @@ const BasePicker = ({ locale }) const isLarge = altCalendar || altCalendarPreset || dateMarkRender || dateMarkPreset + const [showPanel, setShowPanel] = useState(false) const [altCalendarPresetData, dateMarkPresetData] = useAltData({ altCalendar, altCalendarPreset, dateMarkRender, - dateMarkPreset + dateMarkPreset, + showPanel }) - const [showPanel, setShowPanel] = useState(false) const inputChangeEvent = (val, dir) => { if (val.isValid()) { const oData = _.cloneDeep(outDate) diff --git a/components/date-picker/hooks/useAltData.js b/components/date-picker/hooks/useAltData.js index 2a8f55da8..784f85be3 100644 --- a/components/date-picker/hooks/useAltData.js +++ b/components/date-picker/hooks/useAltData.js @@ -1,12 +1,7 @@ import React, { useState, useEffect } from 'react' import { getPRCDate, deconstructDate } from '../utils' -const useAltData = ({ - altCalendarPreset, - altCalendar, - dateMarkPreset, - dateMarkRender -}) => { +const useAltData = ({ altCalendarPreset, altCalendar, dateMarkPreset, showPanel }) => { const [altCalendarPresetData, setAltCalendarPresetData] = useState({}) const [dateMarkPresetData, setDateMarkPresetData] = useState({}) @@ -14,12 +9,11 @@ const useAltData = ({ const getLunarPresetData = () => { const allPRCDate = {} if (['zh-CN', 'id-ID'].includes(altCalendarPreset)) { - const _urlKey = - altCalendarPreset === 'zh-CN' ? 'PRCLunar' : 'IndiaHoliday' - getPRCDate(_urlKey).then(res => { - Object.keys(res.data).forEach(key => { - let oneYear = {} - res.data[key][_urlKey].forEach(item => { + const _urlKey = altCalendarPreset === 'zh-CN' ? 'PRCLunar' : 'IndiaHoliday' + getPRCDate(_urlKey).then((res) => { + Object.keys(res.data).forEach((key) => { + const oneYear = {} + res.data[key][_urlKey].forEach((item) => { Object.assign(oneYear, { [item.date.replace(/-/g, '/')]: { ...item, @@ -29,14 +23,10 @@ const useAltData = ({ }) Object.assign(allPRCDate, oneYear) }) - setAltCalendarPresetData( - altCalendar ? getAltCalendarData(allPRCDate) : allPRCDate - ) + setAltCalendarPresetData(altCalendar ? getAltCalendarData(allPRCDate) : allPRCDate) }) } else { - setAltCalendarPresetData( - altCalendar ? getAltCalendarData(allPRCDate) : {} - ) + setAltCalendarPresetData(altCalendar ? getAltCalendarData(allPRCDate) : {}) } } // 获取预置数据 @@ -45,19 +35,15 @@ const useAltData = ({ return } if (dateMarkPreset === 'zh-CN') { - getPRCDate('PRCHoliday').then(res => { + getPRCDate('PRCHoliday').then((res) => { const allPRCDate = {} - Object.keys(res.data).forEach(key => { - Object.keys(res.data[key].PRCHoliday).forEach(elkey => { + Object.keys(res.data).forEach((key) => { + Object.keys(res.data[key].PRCHoliday).forEach((elkey) => { allPRCDate[elkey.replace(/-/g, '/')] = res.data[key].PRCHoliday[elkey] === '1' ? ( - - 休 - + ) : ( - - 班 - + ) }) }) @@ -66,10 +52,10 @@ const useAltData = ({ } } // 合并用户自定义的日期信息作为presetData - const getAltCalendarData = allPRCDate => { + const getAltCalendarData = (allPRCDate) => { const allData = {} altCalendar.length > 0 && - altCalendar.forEach(item => { + altCalendar.forEach((item) => { const dateInfo = deconstructDate(item.date) if (!Number.isNaN(dateInfo.year)) { Object.assign(allData, { @@ -81,9 +67,11 @@ const useAltData = ({ } useEffect(() => { - altCalendarPreset && getLunarPresetData() - dateMarkPreset && getMarkPresetData() - }, []) + if (showPanel) { + altCalendarPreset && getLunarPresetData() + dateMarkPreset && getMarkPresetData() + } + }, [showPanel]) return [altCalendarPresetData, dateMarkPresetData] } From 91c86e938d682d3fdd369d14fc0f6f4f9b2a849d Mon Sep 17 00:00:00 2001 From: solarjoker Date: Tue, 17 Nov 2020 10:40:36 +0800 Subject: [PATCH 022/136] feat: radio-group keyEvents init --- components/radio/Group.js | 1 + components/radio/Radio.js | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/components/radio/Group.js b/components/radio/Group.js index 8be7b8ab4..c845e5b74 100644 --- a/components/radio/Group.js +++ b/components/radio/Group.js @@ -50,6 +50,7 @@ class Group extends React.Component { -
diff --git a/components/select/Select.js b/components/select/Select.js index 87e0d9387..75ef530a0 100644 --- a/components/select/Select.js +++ b/components/select/Select.js @@ -44,6 +44,7 @@ const InternalSelect = (props) => { const [dropdownItems, setDropdownItems] = useState(data) const [focusedIndex, setFocusedIndex] = useState(0) const [isFocus, setIsFouces] = useState(false) + const SelectWrapper = useRef() // 存储问题 const [cacheSelectItem, setCacheSelectItem] = useState([]) @@ -76,6 +77,10 @@ const InternalSelect = (props) => { useEffect(() => { setIsFouces(dropdownShow) + !dropdownShow && + setTimeout(() => { + SelectWrapper.current.focus() + }, 0) }, [dropdownShow]) useEffect(() => { @@ -172,7 +177,6 @@ const InternalSelect = (props) => { const moveFocusedIndex = useCallback( (direction) => { let _focusedIndex = focusedIndex - console.log('focusedIndex', focusedIndex) if (direction === 'up') { dropdownItems .slice(0, _focusedIndex) @@ -206,7 +210,7 @@ const InternalSelect = (props) => { // 按键操作 const handleKeyDown = useCallback( (evt) => { - console.log(_.cloneDeep(evt), document.activeElement) + evt.stopPropagation() if (evt.keyCode === 13) { onEnterSelect() } @@ -219,12 +223,15 @@ const InternalSelect = (props) => { evt.preventDefault() moveFocusedIndex('down') } - if (evt.keyCode === 32) { + if ( + evt.keyCode === 32 && + !document.activeElement.classList.value.includes('hi-select__dropdown__searchbar--input') + ) { evt.preventDefault() setDropdownShow(!dropdownShow) } }, - [onEnterSelect, moveFocusedIndex, moveFocusedIndex] + [onEnterSelect, moveFocusedIndex] ) // 对关键字的校验 对数据的过滤 const matchFilter = useCallback( @@ -409,8 +416,9 @@ const InternalSelect = (props) => {
{ 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(() => { + dropdownWrapper.current.scrollTop = (focusedIndex - 6) * 36 + }, [focusedIndex]) // 监控全选功能 useEffect(() => { setIscheckAll(selectedItems.length > 0 && selectedItems.length === filterItems.length) @@ -45,6 +49,9 @@ const SelectDropdown = ({ // 让搜索框获取焦点 useEffect(() => { searchable && setTimeout(() => searchbar.current && searchbar.current.focus(), 0) + return () => { + searchbar.current && searchbar.current.blur() + } }, []) // 仅看已选 const showSelected = useCallback( @@ -209,6 +216,7 @@ const SelectDropdown = ({ className={classNames('hi-select__dropdown--item', `theme__${theme}`, { 'is-active': isSelected, 'is-disabled': isDisabled, + 'is-focus': filterItemsIndex === focusedIndex, 'hi-select__dropdown--item-default': !item[transKeys(fieldNames, 'children')] && !dropdownRender })} onClick={(e) => onClickOptionIntal(e, item, filterItemsIndex)} @@ -221,7 +229,7 @@ const SelectDropdown = ({ } const renderItems = () => { return ( -
    +
      {filterItems && filterItems.map((item, filterItemsIndex) => { if (matchFilter(item)) { diff --git a/components/select/SingleInput.js b/components/select/SingleInput.js index 51f2a72b9..63253b6ae 100644 --- a/components/select/SingleInput.js +++ b/components/select/SingleInput.js @@ -66,6 +66,7 @@ const SingleInput = (props) => { > 0 ? placeholder : ''} placeholder={placeholder} onFocus={onFocus} diff --git a/components/select/style/select-dropdown.scss b/components/select/style/select-dropdown.scss index 6c3ab645c..3cd29fa64 100644 --- a/components/select/style/select-dropdown.scss +++ b/components/select/style/select-dropdown.scss @@ -106,6 +106,10 @@ background-color: use-color('primary-20'); } + &.is-focus { + background-color: use-color('primary-20'); + } + &.disabled { opacity: 0.4; cursor: not-allowed; From 6dc9820152a00782d9a13485ce9c9ac2536ab477 Mon Sep 17 00:00:00 2001 From: solarjoker Date: Tue, 17 Nov 2020 17:04:18 +0800 Subject: [PATCH 026/136] feat: init radio keyEvents --- components/radio/Group.js | 70 ++++++++++++++++++++++++++++--- components/radio/Radio.js | 14 +++++-- components/radio/style/index.scss | 10 +++++ 3 files changed, 85 insertions(+), 9 deletions(-) diff --git a/components/radio/Group.js b/components/radio/Group.js index c845e5b74..98145b439 100644 --- a/components/radio/Group.js +++ b/components/radio/Group.js @@ -12,6 +12,7 @@ class Group extends React.Component { constructor (props) { super(props) this.state = { data: getData(props), originValue: props.value, originData: props.data } + this.groupRef = React.createRef(null) } static getDerivedStateFromProps (nextProps, state) { if (!isEqual(nextProps.value, state.originValue)) { @@ -40,21 +41,80 @@ class Group extends React.Component { onChange && onChange(callbackValue) hasValue(this.props) || this.setState({ data: newData }) } + onKeyDown = (radioRef, radioIdx) => e => { + if (e.keyCode === 32) { + e.preventDefault() + radioRef.current.click() + } + const { data } = this.state + const prevArr = [] + const nextArr = [] + data.forEach((item, idx) => { + if (!item.disabled) { + if (idx < radioIdx) { + prevArr.push(idx) + } + if (idx > radioIdx) { + nextArr.push(idx) + } + } + }) + + let prev + let next + if (prevArr.length > 0) { + prev = prevArr[prevArr.length - 1] + } else if (prevArr.length === 0 && nextArr.length > 0) { + prev = nextArr[nextArr.length - 1] + } + if (nextArr.length > 0) { + next = nextArr[0] + } else if (nextArr.length === 0 && prevArr.length > 0) { + next = prevArr[0] + } + if([37, 38].includes(e.keyCode)) { + e.preventDefault() + this.groupRef.current && this.groupRef.current.querySelectorAll('.hi-radio')[prev].focus() + this.groupRef.current && this.groupRef.current.querySelectorAll('.hi-radio')[prev].querySelector('input').click() + } + if([39, 40].includes(e.keyCode)) { + e.preventDefault() + this.groupRef.current && this.groupRef.current.querySelectorAll('.hi-radio')[next].focus() + this.groupRef.current && this.groupRef.current.querySelectorAll('.hi-radio')[next].querySelector('input').click() + } + } + + // getDefaultFocus = data => { + // let idx, checkedIdx + // const _data = data.map((d, index)=> { + // // if () + // return d.checked }) + + // if(_data.length === 0) { + // idx = data.findIndex(d => !d.disabled) + // } + // // if (_data.length === 1 && ) + // return idx + // } render () { const { className, style, disabled, type, placement } = this.props const { data } = this.state const groupCls = classNames(className, prefixCls, { vertical: placement === 'vertical' }) + // const defaultFocus = originValue === undefined && !disabled && data.findIndex(d=> !d.disabled) + // 如果单选组没有选中项或选中项刚好也是禁用项,需要提供一个默认的可以获取焦点的项 return ( - + {data.map(({ label, value, checked, disabled: itemDisabled }, idx) => ( {label} @@ -64,14 +124,14 @@ class Group extends React.Component { } } -function GroupWrapper ({ children, type, ...restProps }) { +const GroupWrapper = React.forwardRef(({ children, type, ...restProps }, ref) => { const shouldUseButton = type === 'button' return shouldUseButton ? ( {children} ) : ( -
      {children}
      +
      {children}
      ) -} +}) function hasValue (props) { return props.value !== undefined diff --git a/components/radio/Radio.js b/components/radio/Radio.js index 843c46b5a..15ecf103a 100644 --- a/components/radio/Radio.js +++ b/components/radio/Radio.js @@ -26,6 +26,7 @@ class Radio extends React.Component { checked: event.target.checked }) } + render () { const { autoFocus, @@ -36,7 +37,14 @@ class Radio extends React.Component { theme, type, value, - tabIdx + tabIdx, + index, + onKeyDown = (inputRef) => (e) => { + if (e.keyCode === 32) { + e.preventDefault() + inputRef && inputRef.current.click() + } + } } = this.props const shouldUseButton = type === 'button' const { checked } = this.state @@ -57,9 +65,7 @@ class Radio extends React.Component { ) return ( -