diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d3e9e593..9f0f5db93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # 更新日志 +## 2.2.0 + +- 新增 `` 走马灯组件 [#115](https://github.com/XiaoMi/hiui/issues/115) +- 新增 `` clearable 属性控制是否可清空 [#590](https://github.com/XiaoMi/hiui/issues/590) +- 新增 `` visible 属性手动控制显示或隐藏 [#589](https://github.com/XiaoMi/hiui/issues/589) +- 新增 `` visible 属性手动控制显示或隐藏 [#588](https://github.com/XiaoMi/hiui/issues/588) +- 修复 `` clearable 不生效的问题 [#580](https://github.com/XiaoMi/hiui/issues/580) diff --git a/components/button/index.js b/components/button/index.js index 90d973197..4023ddb59 100644 --- a/components/button/index.js +++ b/components/button/index.js @@ -3,4 +3,5 @@ import ButtonGroup from './ButtonGroup' import './style/index' Button.Group = ButtonGroup +Button.IS_HI_COMPONENT = true export default Button diff --git a/components/carousel/index.js b/components/carousel/index.js new file mode 100644 index 000000000..c35fd31e5 --- /dev/null +++ b/components/carousel/index.js @@ -0,0 +1,177 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import Icon from '../icon' +import classNames from 'classnames' +import './style/index' + +// const url = 'http://i1.mifile.cn/f/i/hiui/docs/' +class Carousel extends Component { + constructor (props) { + super(props) + this.rootRef = React.createRef() + const defaultActive = props.defaultActive + this.state = { + rootWidth: 0, + showArrow: false, + active: (defaultActive >= props.children.length || defaultActive < 0) ? 0 : defaultActive + } + this.timer = null + } + + componentDidMount () { + this.setState({ + rootWidth: this.rootRef.current.clientWidth + }, () => { + if (this.props.duration) { + this.autoPage() + } + }) + } + + goTo (page) { + if (page > this.props.children.length || page < 0) { + return + } + this.setState({ + active: page + }) + } + componentWillUnmount () { + this.timer && window.clearInterval(this.timer) + } + + autoPage () { + this.timer = window.setInterval(() => { + this.preNextEvent(1) + }, this.props.duration) + } + + pageEvent (active) { + this.setState({ + active + }) + } + + preNextEvent (val) { + let active = this.state.active + val + if (active >= this.props.children.length) { + active = 0 + } + this.setState({ + active + }) + } + + renderDot (type, key, index, active) { + if (type === 'sign') { + return
  • + } else { + const cls = classNames( + 'hi-carousel__dot', + active === index && 'hi-carousel__dot--active' + ) + return ( +
  • + ) + } + } + + mouseEvent (type) { + let showArrow = true + if (type === 'over') { + this.timer && window.clearInterval(this.timer) + } else { + showArrow = false + this.props.duration && this.autoPage() + } + this.setState({showArrow}) + } + render () { + const { rootWidth, active, showArrow } = this.state + const { showDots, showArrows } = this.props + const children = React.Children.toArray(this.props.children) + const arrowCls = classNames( + 'hi-carousel__arrows', + showArrow && 'hi-carousel__arrows--show' + ) + return
    +
    + { + children.map((child, index) => { + return React.cloneElement(child, { + key: index, + style: { + position: 'relative', + opacity: active === index ? 1 : 0, + transition: 'opacity 300ms ease 0s', + left: -(rootWidth * index), + width: rootWidth, + display: 'inline-block', + fontSize: 24, + ...child.props.style + } + }) + }) + } +
    + { + showArrows &&
      +
    • + +
    • +
    • + +
    • +
    + } + { + showDots &&
      + { + children.map((_, index) => { + const cls = classNames('hi-carousel__dot', active === index && 'hi-carousel__dot--active') + return
    • + }) + } +
    + } +
    + } +} + +Carousel.propTypes = { + duration: PropTypes.number, + onClick: PropTypes.func, + beforeChange: PropTypes.func, + afterChange: PropTypes.func, + showDots: PropTypes.bool, + showArrows: PropTypes.bool, + defaultActive: PropTypes.number +} +Carousel.defaultProps = { + duration: 0, + onClick: () => {}, + beforeChange: () => {}, + afterChange: () => {}, + showDots: true, + showArrows: true, + defaultActive: 0 +} + +export default Carousel diff --git a/components/carousel/style/index.js b/components/carousel/style/index.js new file mode 100644 index 000000000..63810a681 --- /dev/null +++ b/components/carousel/style/index.js @@ -0,0 +1 @@ +import './index.scss' diff --git a/components/carousel/style/index.scss b/components/carousel/style/index.scss new file mode 100644 index 000000000..cf049545b --- /dev/null +++ b/components/carousel/style/index.scss @@ -0,0 +1,74 @@ +.hi-carousel { + width: 100%; + color: rgba(153, 153, 153, 1); + overflow: hidden; + position: relative; + + &__container { + height: 100%; + transition: all 500ms; + } + + &__dots { + position: absolute; + bottom: 24px; + margin: 0; + padding: 0; + left: 50%; + transform: translateX(-50%); + } + + &__dot { + width: 24px; + height: 4px; + background: rgba(255, 255, 255, 0.4); + border-radius: 2px; + display: inline-block; + margin-left: 4px; + cursor: pointer; + transition: all 1s; + + &--active:not(.hi-carousel__dot--sign) { + background: rgba(255, 255, 255, 1); + width: 48px; + } + + &--sign { + width: 24px; + } + } + + &__arrows { + position: absolute; + width: 100%; + list-style: none; + margin: 0; + padding: 0; + top: 50%; + transform: translateY(-28px); + display: flex; + justify-content: space-between; + visibility: hidden; + + &--show { + visibility: visible; + } + } + + &__arrow { + width: 56px; + height: 56px; + background: rgba(0, 0, 0, 0.25); + border-radius: 50%; + text-align: center; + line-height: 56px; + color: #fff; + font-size: 32px; + margin-left: 24px; + cursor: pointer; + + &:last-child { + margin-right: 24px; + } + } +} diff --git a/components/date-picker/BasePicker.js b/components/date-picker/BasePicker.js index a2019459e..ae183cb54 100644 --- a/components/date-picker/BasePicker.js +++ b/components/date-picker/BasePicker.js @@ -264,12 +264,13 @@ class BasePicker extends Component { } _icon () { const {isFocus} = this.state + const { clearable } = this.props const iconCls = classNames( 'hi-datepicker__input-icon', 'hi-icon', - isFocus ? 'icon-close-circle clear' : 'icon-date' + (isFocus && clearable) ? 'icon-close-circle clear' : 'icon-date' ) - return isFocus + return (isFocus && clearable) ? : { if (this.props.disabled) return @@ -341,6 +342,9 @@ BasePicker.propTypes = { if (val === undefined || val === null) { return null } + if (!val) { + return null + } if (val.start && val.end) { const _start = dateFormat(val.start) const _end = dateFormat(val.end) @@ -364,13 +368,15 @@ BasePicker.propTypes = { if (val < 5 || val > 480 || (val > 60 && val % 60 !== 0) || (val < 60 && 60 % val !== 0)) { return new Error(`Invalid prop ${propName} supplied to ${componentName}. This value must be greater than 5 and less than 480 and is a multiple of 60.`) } - } + }, + clearable: PropTypes.bool } BasePicker.defaultProps = { type: 'date', disabled: false, showWeekNumber: true, weekOffset: 0, - timeInterval: 240 + timeInterval: 240, + clearable: true } export default BasePicker diff --git a/components/index.js b/components/index.js index 823896e3c..51ee73cd2 100755 --- a/components/index.js +++ b/components/index.js @@ -42,4 +42,5 @@ export { default as Rate } from './rate' export { default as Message } from './message' export { default as Tag } from './tag' export { default as Breadcrumb } from './breadcrumb' +export { default as Carousel } from './carousel' export { ThemeContext, LocaleContext } from './context' diff --git a/components/popover/index.js b/components/popover/index.js index e242cf4d0..78a1131ef 100644 --- a/components/popover/index.js +++ b/components/popover/index.js @@ -65,7 +65,6 @@ export default class Popover extends Component { this.element = ReactDOM.findDOMNode(this) const referenceRef = ReactDOM.findDOMNode(this.referenceRef) - // this.reference = ReactDOM.findDOMNode(this.refs.reference) if (referenceRef === null) return if (trigger === 'click') { @@ -105,27 +104,28 @@ export default class Popover extends Component { this.unbindHover = false popper.current.addEventListener('mouseenter', e => { this.eventTarget = e.target - // this.showPopper() }) popper.current.addEventListener('mouseleave', e => { - this.delayHidePopper(e) + const poperPosition = popper.current.getBoundingClientRect() + if (e.clientY > poperPosition.y + poperPosition.height - 1 || e.clientY < poperPosition.y || e.clientX < poperPosition.x || e.clientX > poperPosition.x + poperPosition.width - 1) { + this.delayHidePopper(e) + } }) } } render () { - const { style, className, title, content, placement, width } = this.props + const { style, className, title, content, placement, width, visible } = this.props const { showPopper } = this.state - return (
    { this.popoverContainer = node }}> { React.cloneElement(React.Children.only(this.props.children), { ref: (el) => { this.referenceRef = el }, tabIndex: '0' }) } { - if (!queryParams) { - return '' - } - if (typeof params === 'string') { - return params - } - if (Object.prototype.toString.call(params) === '[object Object]') { - return Object.keys(params) - .map((key) => `&${key}=${params[key]}`) - .join('') - } - })() - + const queryParams = qs.stringify(Object.assign({}, params, key && {[key]: keyword})) url = - url.indexOf('?') === -1 - ? `${url}?${[key]}=${keyword}${queryParams}` - : `${url}&${[key]}=${keyword}${queryParams}` + url.includes('?') + ? `${url}&${queryParams}` + : `${url}?${queryParams}` if (type.toUpperCase() === 'POST') { options.body = JSON.stringify(data) @@ -443,7 +432,7 @@ class Select extends Component { () => this.resetFocusedIndex() ) - if (this.props.dataSource) { + if (this.props.dataSource && this.props.dataSource.key) { if ( this.props.autoload || keyword.toString().length >= this.state.queryLength diff --git a/components/tooltip/index.js b/components/tooltip/index.js index 7519c2e84..035145ff8 100644 --- a/components/tooltip/index.js +++ b/components/tooltip/index.js @@ -25,9 +25,21 @@ class Tooltip extends Component { state = { tooltipShow: this.props.defaultVisible } - + // 兼容处理 button disabled tooltip 不消失的问题 + compatDisabledBtn = el => { + if (el.type.IS_HI_COMPONENT && el.props.disabled) { + return React.cloneElement(el, { + style: { + ...el.props.style, + pointerEvents: 'none' + } + }) + } else { + return el + } + } render () { - const { placement, style, className, onClick, title, children } = this.props + const { placement, style, className, onClick, title, children, visible } = this.props const eleClass = classNames(`${prefixCls}-base`, placement && `${prefixCls}-${placement}`) const { tooltipShow } = this.state return ( @@ -49,7 +61,7 @@ class Tooltip extends Component { >
    {title}
    - {children} + {this.compatDisabledBtn(children)}
    ) } diff --git a/components/tree/tree-legacy/util.js b/components/tree/tree-legacy/util.js index 674717088..d57019592 100644 --- a/components/tree/tree-legacy/util.js +++ b/components/tree/tree-legacy/util.js @@ -293,7 +293,7 @@ export const getAncestorIds = (id, data, arr = []) => { // 收集所有需要展开的节点 id export const collectExpandId = (data, searchValue, collection = [], allData) => { data.forEach(item => { - if (item.title.includes(searchValue)) { + if (searchValue && item.title.includes(searchValue)) { const parentIds = getAncestorIds(item.id, allData, []) collection.splice(collection.length - 1, 0, ...parentIds) } diff --git a/components/tree/util.js b/components/tree/util.js index 674717088..d57019592 100644 --- a/components/tree/util.js +++ b/components/tree/util.js @@ -293,7 +293,7 @@ export const getAncestorIds = (id, data, arr = []) => { // 收集所有需要展开的节点 id export const collectExpandId = (data, searchValue, collection = [], allData) => { data.forEach(item => { - if (item.title.includes(searchValue)) { + if (searchValue && item.title.includes(searchValue)) { const parentIds = getAncestorIds(item.id, allData, []) collection.splice(collection.length - 1, 0, ...parentIds) } diff --git a/docs/demo/breadcrumb/section-base.jsx b/docs/demo/breadcrumb/section-base.jsx index 4a7f39e5f..a8dc191ef 100644 --- a/docs/demo/breadcrumb/section-base.jsx +++ b/docs/demo/breadcrumb/section-base.jsx @@ -3,7 +3,7 @@ import DocViewer from '../../../libs/doc-viewer' import Breadcrumb from '../../../components/breadcrumb' const prefix = 'alert-base' const code = `import React from 'react' -import Alert from '@hi-ui/hiui/es/alert'\n +import Breadcrumb from '@hi-ui/hiui/es/breadcrumb'\n class Demo extends React.Component { render () { const data = [{ diff --git a/docs/demo/carousel/section-base.jsx b/docs/demo/carousel/section-base.jsx new file mode 100644 index 000000000..f0470c742 --- /dev/null +++ b/docs/demo/carousel/section-base.jsx @@ -0,0 +1,31 @@ +import React from 'react' +import DocViewer from '../../../libs/doc-viewer' +import Carousel from '../../../components/carousel' +const prefix = 'carousel-base' +const code = `import React from 'react' +import Carousel from '@hi-ui/hiui/es/carousel'\n +class Demo extends React.Component { + constructor (props) { + super(props) + } + + render () { + const data = [1, 2, 3, 4, 5, 6, 7, 8, 9] + return ( +
    + + { + data.map((item) => { + return
    {item}
    + }) + } +
    +
    + ) + } +}` + +const DemoBase = () => +export default DemoBase diff --git a/docs/demo/carousel/section-icon.jsx b/docs/demo/carousel/section-icon.jsx new file mode 100644 index 000000000..5ab0544a3 --- /dev/null +++ b/docs/demo/carousel/section-icon.jsx @@ -0,0 +1,27 @@ +import React from 'react' +import DocViewer from '../../../libs/doc-viewer' +import Carousel from '../../../components/carousel' +const prefix = 'carousel-base' +const code = `import React from 'react' +import Carousel from '@hi-ui/hiui/es/carousel'\n +class Demo extends React.Component { + render () { + const data = [1, 2, 3, 4, 5, 6, 7, 8] + return ( +
    + + { + data.map((item, index) => { + return + }) + } + +
    + ) + } +}` + +const DemoBase = () => +export default DemoBase diff --git a/docs/demo/date-picker/section-ban-date.jsx b/docs/demo/date-picker/section-ban-date.jsx index fa296ca8c..254d93345 100644 --- a/docs/demo/date-picker/section-ban-date.jsx +++ b/docs/demo/date-picker/section-ban-date.jsx @@ -18,6 +18,7 @@ class Demo extends React.Component { min={new Date()} max={new Date().getTime() + 30 * 24 * 60 * 60 * 1000} onChange={(date) => { + console.log(date) this.setState({date}) }} /> diff --git a/docs/zh-CN/components/carousel.mdx b/docs/zh-CN/components/carousel.mdx new file mode 100644 index 000000000..baae2b0b7 --- /dev/null +++ b/docs/zh-CN/components/carousel.mdx @@ -0,0 +1,22 @@ +# Carousel 走马灯 + +## 基础用法 + +import DemoBase from '../../demo/carousel/section-base.jsx' + + + +## 图片展示 + +import DemoImage from '../../demo/carousel/section-icon.jsx' + + + +## Props + +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +| -------------- | ------------------------------------------------- | --------------------------- | ------------- | ------------------------------------------------------- | +| duration | 自动切换间隔,默认不自动切换 | number | - | 0 | +| showDots | 是否显示分页指示器 | boolean | true \| false | false | +| showArrows | 是否显示箭头指示器 | boolean | true \| false | false | +| defaultActive | 默认激活索引,从0开始 | number | - | 0 | diff --git a/docs/zh-CN/components/changelog.mdx b/docs/zh-CN/components/changelog.mdx index 6748c294a..91b88af8e 100644 --- a/docs/zh-CN/components/changelog.mdx +++ b/docs/zh-CN/components/changelog.mdx @@ -1,13 +1,22 @@ # 更新日志 +## 2.2.0 + +- 新增 `` 走马灯组件 [#115](https://github.com/XiaoMi/hiui/issues/115) +- 新增 `` clearable 属性控制是否可清空 [#590](https://github.com/XiaoMi/hiui/issues/590) +- 新增 `` visible 属性手动控制显示或隐藏 [#589](https://github.com/XiaoMi/hiui/issues/589) +- 新增 `` visible 属性手动控制显示或隐藏 [#588](https://github.com/XiaoMi/hiui/issues/588) +- 修复 `` clearable 不生效的问题 [#580](https://github.com/XiaoMi/hiui/issues/580) - 修复 `` 中的自定义上传结合 maxCount 不能上传的问题 [#582](https://github.com/XiaoMi/hiui/issues/582) - 修复 `` type 为 textarea 时的底部的样式问题 [#584](https://github.com/XiaoMi/hiui/issues/584) - - ## 2.1.0 - 新增 `` 面包屑组件 [#573](https://github.com/XiaoMi/hiui/issues/573) diff --git a/docs/zh-CN/components/date-picker.mdx b/docs/zh-CN/components/date-picker.mdx index 0b93cb340..c4a0c94ae 100755 --- a/docs/zh-CN/components/date-picker.mdx +++ b/docs/zh-CN/components/date-picker.mdx @@ -95,6 +95,7 @@ import DemoModal from '../../demo/date-picker/section-modal.jsx' | minDate | 最小日期 | Date | null | null | | maxDate | 最大日期 | Date | null | null | | disabled | 是否禁用输入框 | boolean | true \| false | false | +| clearable | 是否可以清空 | boolean | true \| false | true | | showTime | 是否在日期选择器中显示时间选择器 | boolean | true \| false | false | | shortcuts | 快捷面板 | string[] | 近一周, 近一月, 近三月, 近一年 | null | | weekOffset | 周起始
    默认周日做为第一列 | number | 0/1 | 0 | diff --git a/docs/zh-CN/components/popover.mdx b/docs/zh-CN/components/popover.mdx index 7e326a90e..675eb8ec5 100755 --- a/docs/zh-CN/components/popover.mdx +++ b/docs/zh-CN/components/popover.mdx @@ -16,3 +16,4 @@ import DemoBase from '../../demo/popover/section-base.jsx' | content | 气泡卡片内容 | string \| ReactNode | - | - | | placement | 气泡卡片显示的位置 | string | 'top' \| 'right' \| 'bottom' \| 'left' | 'top' | | trigger | 气泡卡片触发方式 | string | 'click' \| 'focus' \| 'hover' | 'click' | +| visible | 控制气泡卡片的显示和隐藏(需要组件完全受控时使用) | boolean |true \| false | - | diff --git a/docs/zh-CN/components/tooltip.mdx b/docs/zh-CN/components/tooltip.mdx index 45b9ef7e7..e82b9835d 100755 --- a/docs/zh-CN/components/tooltip.mdx +++ b/docs/zh-CN/components/tooltip.mdx @@ -20,6 +20,7 @@ import DemoApi from '../../demo/tooltip/section-api.jsx' | --------- | ------------------ | ------ | -------------------------------------- | ------ | | title | 提示文字内容 | string | 字符串 | -- | | placement | tooltip 显示的位置 | string | 'top' \| 'right' \| 'bottom' \| 'left' | 'top' | +| visible | 控制 tooltip 的显示和隐藏(需要组件完全受控时使用) | boolean | true \| false | - | ## Methods diff --git a/package.json b/package.json index 6200e1546..eb81a22ac 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@hi-ui/hiui", - "version": "2.1.2", + "version": "2.2.0", "description": "HIUI for React", "scripts": { "test": "node_modules/.bin/standard && node_modules/.bin/stylelint --config .stylelintrc 'components/**/*.scss'", @@ -52,6 +52,7 @@ "github-markdown-css": "^3.0.1", "hoist-non-react-statics": "^2.5.0", "lodash": "^4.17.11", + "qs": "^6.8.0", "react": "^16.8.6", "react-addons-css-transition-group": "^15.6.2", "react-click-outside": "^3.0.1", diff --git a/site/locales/zh-CN.js b/site/locales/zh-CN.js index 2ba8da091..b2e923332 100755 --- a/site/locales/zh-CN.js +++ b/site/locales/zh-CN.js @@ -51,7 +51,8 @@ module.exports = { transfer: 'Transfer 穿梭框', switch: 'Switch 开关', rate: 'Rate 评分', - breadcrumb: 'Breadcrumb 面包屑' + breadcrumb: 'Breadcrumb 面包屑', + carousel: 'Carousel 走马灯' }, designs: { summarize: '概述', diff --git a/site/pages/components/index.js b/site/pages/components/index.js index 1581ea15a..cbcaf03a3 100755 --- a/site/pages/components/index.js +++ b/site/pages/components/index.js @@ -51,7 +51,8 @@ export default { popover: components['popover'], progress: components['progress'], card: components['card'], - timeline: components['timeline'] + timeline: components['timeline'], + carousel: components['carousel'] }, 'group-tips': { modal: components['modal'],