diff --git a/components/_util/SwitchVersion.js b/components/_util/SwitchVersion.js index 783cf6d8c..d11777061 100644 --- a/components/_util/SwitchVersion.js +++ b/components/_util/SwitchVersion.js @@ -1,16 +1,14 @@ import React, { forwardRef } from 'react' function SwitchVersion (component = {}, componentLegacy = {}) { - const WrapperComponent = ({ legacy, innerRef, ...props }) => { - const innerComponent = legacy === true ? componentLegacy : component - return React.createElement( - innerComponent, - Object.assign({}, props, { ref: innerRef }) - ) - } - return forwardRef((props, ref) => { - return + const WrapperComponent = forwardRef(({ legacy, ...props }, ref) => { + const InnerComponent = legacy === true ? componentLegacy : component + for (const staticProp in InnerComponent) { + WrapperComponent[staticProp] = InnerComponent[staticProp] + } + return }) + return WrapperComponent } export default SwitchVersion diff --git a/components/_util/depreactedPropsCompat.js b/components/_util/depreactedPropsCompat.js index e3d44e436..10d8e8adb 100644 --- a/components/_util/depreactedPropsCompat.js +++ b/components/_util/depreactedPropsCompat.js @@ -1,4 +1,4 @@ -import React from 'react' +import React, { forwardRef } from 'react' const isDevelopment = /development/gi.test(process.env.NODE_ENV) @@ -10,26 +10,28 @@ const isDevelopment = /development/gi.test(process.env.NODE_ENV) * @param {[[string, string, Function], [string, string, Function]]} compatPair * @returns */ -export const depreactedPropsCompat = (compatPair) => { - return (WrappedComponent) => { - return (props) => { - const compatProps = { ...props } - const componentName = - WrappedComponent.displayName || - WrappedComponent.name || - 'unknown component' - compatPair.forEach(([newProp, oldProp, convert]) => { - if (props[oldProp] !== undefined && props[newProp] === undefined) { - isDevelopment && - console.warn( - `${componentName}'s prop "${oldProp}" will be depreacted in next version! use ${newProp} instead.` - ) - compatProps[newProp] = convert - ? convert(props[oldProp]) - : props[oldProp] - } - }) - return React.createElement(WrappedComponent, compatProps) - } +export const depreactedPropsCompat = (compatPair) => (WrappedComponent) => { + const WrapperComponent = forwardRef((props, ref) => { + const compatProps = { ...props } + const componentName = + WrappedComponent.displayName || + WrappedComponent.name || + 'unknown component' + compatPair.forEach(([newProp, oldProp, convert]) => { + if (props[oldProp] !== undefined && props[newProp] === undefined) { + isDevelopment && + console.warn( + `${componentName}'s prop "${oldProp}" will be depreacted in next version! use ${newProp} instead.` + ) + compatProps[newProp] = convert + ? convert(props[oldProp]) + : props[oldProp] + } + }) + return + }) + for (const staticProp in WrappedComponent) { + WrapperComponent[staticProp] = WrappedComponent[staticProp] } + return WrapperComponent } diff --git a/components/input/Input.js b/components/input/Input.js index 8ff062437..01747fffb 100644 --- a/components/input/Input.js +++ b/components/input/Input.js @@ -70,6 +70,10 @@ class Input extends Component { } } + blur = () => { + this._Input.blur() + } + /** * 渲染 text 输入框 */ diff --git a/components/loading/Loading.js b/components/loading/Loading.js new file mode 100755 index 000000000..7609259ea --- /dev/null +++ b/components/loading/Loading.js @@ -0,0 +1,114 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import ReactDOM from 'react-dom' +import classNames from 'classnames' + +const loadingInstance = {} + +const prefixCls = 'hi-loading' +class Loading extends Component { + render () { + const { size, full, content, children, target, visible } = this.props + const mountNode = target || (full ? document.body : '') + const iconCls = classNames( + `${prefixCls}__icon`, + `${prefixCls}__icon--${size}` + ) + const maskCls = classNames(`${prefixCls}__mask`, { + [`${prefixCls}__mask--global`]: full, + [`${prefixCls}__mask--part`]: !full, + [`${prefixCls}__mask--hide`]: visible === false + }) + return ( + + {children} +
+
+
+
+
+
+
{content}
+
+
+ + ) + } +} + +Loading.propTypes = { + size: PropTypes.oneOf(['large', 'default', 'small']), + full: PropTypes.bool, + visible: PropTypes.bool, + content: PropTypes.oneOfType([PropTypes.string, PropTypes.element]), + target: PropTypes.any, + duration: PropTypes.number +} + +Loading.defaultProps = { + size: 'default' +} + +function PortalWrapper ({ mountNode, children }) { + return mountNode ? ( + ReactDOM.createPortal(children, mountNode) + ) : ( +
{children}
+ ) +} + +function open (target, { content, key, duration, size } = {}) { + let renderNode = document.createElement('div') + const mountNode = target || document.body + window.getComputedStyle(mountNode).position === 'absolute' || + mountNode.style.setProperty('position', 'relative') + const full = !target + ReactDOM.render( + , + renderNode + ) + loadingInstance[key] = renderNode + if (!isNaN(duration) && duration > 0) { + setTimeout(() => { + ReactDOM.unmountComponentAtNode(renderNode) + renderNode = undefined + }, duration) + } +} + +function deprecatedOpen ({ target, tip } = {}) { + let renderNode = document.createElement('div') + const mountNode = target || document.body + window.getComputedStyle(mountNode).position === 'absolute' || + mountNode.style.setProperty('position', 'relative') + const full = !target + ReactDOM.render( + , + renderNode + ) + function close () { + renderNode && ReactDOM.unmountComponentAtNode(renderNode) + renderNode = undefined + } + return { close } +} + +function openWrapper (target, options) { + if (arguments.length >= 2) { + open(target, options) + } else { + return deprecatedOpen(target) + } +} +function close (key) { + if (loadingInstance[key]) { + ReactDOM.unmountComponentAtNode(loadingInstance[key]) + loadingInstance[key].parentNode && + loadingInstance[key].parentNode.removeChild(loadingInstance[key]) + } +} + +Loading.open = openWrapper +Loading.close = close + +export default Loading diff --git a/components/loading/index.js b/components/loading/index.js old mode 100755 new mode 100644 index 56a890c4e..09fc64cfc --- a/components/loading/index.js +++ b/components/loading/index.js @@ -1,97 +1,5 @@ -import React, { Component } from 'react' -import PropTypes from 'prop-types' -import ReactDOM from 'react-dom' -import classNames from 'classnames' +import Loading from './Loading' +import { depreactedPropsCompat } from '../_util' import './style/index' -const loadingInstance = {} - -const prefixCls = 'hi-loading' -class Loading extends Component { - static propTypes = { - size: PropTypes.oneOf(['large', 'default', 'small']), - full: PropTypes.bool, - visible: PropTypes.bool, - content: PropTypes.oneOfType([PropTypes.string, PropTypes.element]), - target: PropTypes.any, - duration: PropTypes.number - } - static defaultProps = { - size: 'default' - } - render () { - const { size, full, content, visible, children, target, show } = this.props - const mountNode = target || (full ? document.body : '') - const iconCls = classNames(`${prefixCls}__icon`, `${prefixCls}__icon--${size}`) - const maskCls = classNames(`${prefixCls}__mask`, { - [`${prefixCls}__mask--global`]: full, - [`${prefixCls}__mask--part`]: !full, - [`${prefixCls}__mask--hide`]: visible === false || show === false - }) - return ( - - {children} -
-
-
-
-
-
-
{content}
-
-
- - ) - } -} - -function PortalWrapper ({ mountNode, children }) { - return mountNode ? ( - ReactDOM.createPortal(children, mountNode) - ) : ( -
{children}
- ) -} - -function open (target, { content, key, duration, size } = {}) { - let renderNode = document.createElement('div') - const mountNode = target || document.body - window.getComputedStyle(mountNode).position === 'absolute' || - mountNode.style.setProperty('position', 'relative') - const full = !target - ReactDOM.render(, renderNode) - - loadingInstance[key] = renderNode -} -function deprecatedOpen ({ target, tip } = {}) { - let renderNode = document.createElement('div') - const mountNode = target || document.body - window.getComputedStyle(mountNode).position === 'absolute' || - mountNode.style.setProperty('position', 'relative') - const full = !target - ReactDOM.render(, renderNode) - function close () { - renderNode && ReactDOM.unmountComponentAtNode(renderNode) - renderNode = undefined - } - return { close } -} - -function openWrapper (target, options) { - if (arguments.length >= 2) { - open(target, options) - } else { - return deprecatedOpen(target) - } -} -function close (key) { - if (loadingInstance[key]) { - ReactDOM.unmountComponentAtNode(loadingInstance[key]) - loadingInstance[key].parentNode && loadingInstance[key].parentNode.removeChild(loadingInstance[key]) - } -} - -Loading.open = openWrapper -Loading.close = close - -export default Loading +export default depreactedPropsCompat(['visible', 'show'])(Loading) diff --git a/components/pagination/Pagination.js b/components/pagination/Pagination.js index 642e39062..c6baafdc4 100644 --- a/components/pagination/Pagination.js +++ b/components/pagination/Pagination.js @@ -1,7 +1,7 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import Pager from './Pager' -import Dropdown from '../dropdown/index' +import Select from '../select' import Input from '../input' import Provider from '../context' @@ -18,35 +18,6 @@ function breakItemRender (page, element) { function noop () {} class Pagination extends Component { - static propTypes = { - defaultCurrent: PropTypes.number, - pageSize: PropTypes.number, - max: PropTypes.number, - showJumper: PropTypes.bool, - autoHide: PropTypes.bool, - total: PropTypes.number, - onChange: PropTypes.func, - itemRender: PropTypes.func, - onPageSizeChange: PropTypes.func, - onJump: PropTypes.func, - pageSizeOptions: PropTypes.array, - type: PropTypes.oneOf(['simple', 'default', 'shrink']) - } - - static defaultProps = { - pageSizeOptions: [], - showJumper: false, - autoHide: false, - type: 'default', - defaultCurrent: 1, - pageSize: 10, - max: 2, - total: 0, - onChange: noop, - className: '', - prefixCls: 'hi-pagination' - } - constructor (props) { super(props) @@ -158,17 +129,18 @@ class Pagination extends Component { return (
- ({ - value: typeof n === 'object' ? n.value : n, + id: n, title: `${n} ${i18nItem}/${i18nItemPerPage}` }))} - width={100} - trigger='click' - onClick={(val) => { - this.onPageSizeChange(val) - }} - /> + value={pageSize} + onChange={ids => { + this.onPageSizeChange(ids[0]) + }} />
) @@ -197,6 +169,12 @@ class Pagination extends Component {
{ const val = e.target.value + if (!val) { + this.setState({ + jumpTo: val + }) + return + } if (/^\d+$/.test(val)) { const maxPage = this.calculatePage(total) const jumpTo = val < 1 ? 1 : (val > maxPage ? maxPage : val) @@ -226,7 +204,7 @@ class Pagination extends Component { } else if (e.type === 'keypress') { if (e.charCode === 13) { setPageNum(pageNum) - this.jumper.current._Input.blur() + this.jumper.current.blur() } } } @@ -422,4 +400,33 @@ class Pagination extends Component { } } +Pagination.propTypes = { + defaultCurrent: PropTypes.number, + pageSize: PropTypes.number, + max: PropTypes.number, + showJumper: PropTypes.bool, + autoHide: PropTypes.bool, + total: PropTypes.number, + onChange: PropTypes.func, + itemRender: PropTypes.func, + onPageSizeChange: PropTypes.func, + onJump: PropTypes.func, + pageSizeOptions: PropTypes.array, + type: PropTypes.oneOf(['simple', 'default', 'shrink']) +} + +Pagination.defaultProps = { + pageSizeOptions: [], + showJumper: false, + autoHide: false, + type: 'default', + defaultCurrent: 1, + pageSize: 10, + max: 2, + total: 0, + onChange: noop, + className: '', + prefixCls: 'hi-pagination' +} + export default Provider(Pagination) diff --git a/components/select/Select.js b/components/select/Select.js index 03f49f11d..28f8df47a 100644 --- a/components/select/Select.js +++ b/components/select/Select.js @@ -35,6 +35,7 @@ class Select extends Component { showCheckAll: PropTypes.bool, autoload: PropTypes.bool, searchable: PropTypes.bool, + filterOption: PropTypes.func, clearable: PropTypes.bool, disabled: PropTypes.bool, placeholder: PropTypes.string, @@ -149,7 +150,7 @@ class Select extends Component { if (this.isRemote()) { return true } - return !!searchable + return searchable } parseValue (value = this.props.value) { @@ -170,7 +171,7 @@ class Select extends Component { resetSelectedItems (value, dropdownItems = [], listChanged = false) { const values = this.parseValue(value) let selectedItems = [] - dropdownItems.forEach(item => { + dropdownItems.forEach((item) => { if (values.includes(item.id)) { selectedItems.push(item) } @@ -247,11 +248,9 @@ class Select extends Component { } this.onChange(selectedItems, item, () => { - this.setState( - { - focusedIndex - } - ) + this.setState({ + focusedIndex + }) }) if (this.props.type !== 'multiple') { this.hideDropdown() @@ -350,11 +349,10 @@ class Select extends Component { : keyword this.autoloadFlag = false // 第一次自动加载数据后,输入的关键词即使为空也不再使用默认关键词 - const queryParams = qs.stringify(Object.assign({}, params, key && {[key]: keyword})) - url = - url.includes('?') - ? `${url}&${queryParams}` - : `${url}?${queryParams}` + const queryParams = qs.stringify( + Object.assign({}, params, key && { [key]: keyword }) + ) + url = url.includes('?') ? `${url}&${queryParams}` : `${url}?${queryParams}` if (type.toUpperCase() === 'POST') { options.body = JSON.stringify(data) @@ -418,7 +416,7 @@ class Select extends Component { onFilterItems(keyword) { this.setState( { - keyword + keyword: keyword }, () => this.resetFocusedIndex() ) @@ -434,13 +432,18 @@ class Select extends Component { } matchFilter(item) { + const { filterOption } = this.props const { searchable, keyword } = this.state - return ( - this.isRemote() || - (!searchable || !keyword) || - (String(item.id).includes(keyword) || - String(item.title).includes(keyword)) - ) + + const shouldMatch = this.isRemote() || + (!searchable || !keyword) + + if (typeof filterOption === 'function') { + return shouldMatch || filterOption(keyword, item) + } + + return shouldMatch || (String(item.id).includes(keyword) || + String(item.title).includes(keyword)) } resetFocusedIndex(setState = true) { @@ -539,7 +542,7 @@ class Select extends Component { style={style} >
{ this.selectInputContainer = node }} @@ -579,8 +582,8 @@ class Select extends Component { attachEle={this.selectInputContainer} zIndex={1050} topGap={5} - className="hi-select__popper" - placement="top-bottom-start" + className='hi-select__popper' + placement='top-bottom-start' > )} {mode === 'multiple' && showCheckAll && ( -
+
全选
)} @@ -127,3 +129,5 @@ export default class SelectDropdown extends Component { ) } } + +export default Provider(SelectDropdown) diff --git a/components/select/SelectInput.js b/components/select/SelectInput.js index 2b66b4677..02a37cdee 100644 --- a/components/select/SelectInput.js +++ b/components/select/SelectInput.js @@ -3,7 +3,9 @@ import React, { Component } from 'react' import classNames from 'classnames' import { getTextWidth } from './common.js' -export default class SelectInput extends Component { +import Proivder from '../context' + +class SelectInput extends Component { constructor (props) { super(props) @@ -103,6 +105,7 @@ export default class SelectInput extends Component { clearable, multipleMode, onFocus, + theme, onBlur } = this.props let icon = dropdownShow ? 'up' : 'down' @@ -118,7 +121,7 @@ export default class SelectInput extends Component { return (
{ - Loading.close(123) - }, 3000) + Loading.open(null, { duration: 3000 }) } demoEvent2 () { - Loading.open(this.el, - { + Loading.open(this.el, { content: '加载中', key: 666 }) diff --git a/docs/demo/loading/section-localControl.jsx b/docs/demo/loading/section-localControl.jsx index 2a68c8499..e97ccd875 100755 --- a/docs/demo/loading/section-localControl.jsx +++ b/docs/demo/loading/section-localControl.jsx @@ -22,7 +22,7 @@ class Demo extends React.Component { dataIndex: 'name', key: 'name', render: (text, row, index) => { - return {text} + return {text} } }, { title: 'Age', @@ -37,7 +37,7 @@ class Demo extends React.Component { key: 'action', render: (text, record) => ( - Action 一 {record.name} + Action 一 {record.name} ), }]; diff --git a/docs/demo/select/section-search.jsx b/docs/demo/select/section-search.jsx new file mode 100644 index 000000000..de71a85a7 --- /dev/null +++ b/docs/demo/select/section-search.jsx @@ -0,0 +1,51 @@ +import React from 'react' +import DocViewer from '../../../libs/doc-viewer' +import Select from '../../../components/select' +const prefix = 'select-search' +const code = `import React from 'react' +import Select from '@hi-ui/hiui/es/select'\n +class Demo extends React.Component { + constructor () { + super() + this.state = { + singleList: [ + { title: '小米1', id: 1 }, + { title: '小米2', id: 2, disabled: true }, + { title: '小米3', id: 3, disabled: true }, + { title: '小米4', id: 4 }, + { title: '小米5', id: 5 }, + { title: '小米6', id: 6 }, + { title: '小米8', id: 8 }, + { title: '小米9', id: 9 }, + ] + } + } + + render () { + return ( +