UNPKG

@douyinfe/semi-ui

Version:

A modern, comprehensive, flexible design system and UI library. Connect DesignOps & DevOps. Quickly build beautiful React apps. Maintained by Douyin-fe team.

708 lines (707 loc) 26.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _noop2 = _interopRequireDefault(require("lodash/noop")); var _isEqual2 = _interopRequireDefault(require("lodash/isEqual")); var _react = _interopRequireDefault(require("react")); var _reactDom = _interopRequireDefault(require("react-dom")); var _propTypes = _interopRequireDefault(require("prop-types")); var _classnames = _interopRequireDefault(require("classnames")); var _constants = require("@douyinfe/semi-foundation/lib/cjs/slider/constants"); var _baseComponent = _interopRequireDefault(require("../_base/baseComponent")); var _foundation = _interopRequireDefault(require("@douyinfe/semi-foundation/lib/cjs/slider/foundation")); var _index = _interopRequireDefault(require("../tooltip/index")); require("@douyinfe/semi-foundation/lib/cjs/slider/slider.css"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } var __rest = void 0 && (void 0).__rest || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; const prefixCls = _constants.cssClasses.PREFIX; function domIsInRenderTree(e) { if (!e) { return false; } return Boolean(e.offsetWidth || e.offsetHeight || e.getClientRects().length); } class Slider extends _baseComponent.default { constructor(props) { super(props); this.renderHandle = () => { var _a, _b, _c, _d, _e; const { vertical, range, tooltipVisible, tipFormatter, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledby, 'aria-valuetext': ariaValueText, getAriaValueText, disabled } = this.props; const { chooseMovePos, isDrag, isInRenderTree, firstDotFocusVisible, secondDotFocusVisible } = this.state; const stylePos = vertical ? 'top' : 'left'; const percentInfo = this.foundation.getMinAndMaxPercent(this.state.currentValue); const minPercent = percentInfo.min; const maxPercent = percentInfo.max; const { tipVisible, tipChildren } = this.foundation.computeHandleVisibleVal(tooltipVisible && isInRenderTree, tipFormatter, range); const minClass = (0, _classnames.default)(_constants.cssClasses.HANDLE, { [`${_constants.cssClasses.HANDLE}-clicked`]: chooseMovePos === 'min' && isDrag }); const maxClass = (0, _classnames.default)(_constants.cssClasses.HANDLE, { [`${_constants.cssClasses.HANDLE}-clicked`]: chooseMovePos === 'max' && isDrag }); const { min, max, currentValue } = this.state; const commonAria = { 'aria-label': ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : disabled ? 'Disabled Slider' : undefined, 'aria-labelledby': ariaLabelledby, 'aria-disabled': disabled }; vertical && Object.assign(commonAria, { 'aria-orientation': 'vertical' }); const handleDot = this.props.handleDot; const handleContents = !range ? (/*#__PURE__*/_react.default.createElement(_index.default, { content: tipChildren.min, showArrow: this.props.showArrow, position: "top", trigger: "custom", rePosKey: minPercent, visible: isInRenderTree && (tipVisible.min || firstDotFocusVisible), className: `${_constants.cssClasses.HANDLE}-tooltip` }, /*#__PURE__*/_react.default.createElement("span", Object.assign({ onMouseOver: this.foundation.checkAndUpdateIsInRenderTreeState, ref: this.minHanleEl, className: minClass, style: { [stylePos]: `${minPercent * 100}%`, zIndex: chooseMovePos === 'min' && isDrag ? 2 : 1 }, onMouseDown: e => { this.foundation.onHandleDown(e, 'min'); }, onMouseEnter: () => { this.foundation.onHandleEnter('min'); }, onTouchStart: e => { this.foundation.onHandleTouchStart(e, 'min'); }, onMouseLeave: () => { this.foundation.onHandleLeave(); }, onKeyUp: e => { this.foundation.onHandleUp(e); }, onTouchEnd: e => { this.foundation.onHandleUp(e); }, onKeyDown: e => { this.foundation.handleKeyDown(e, 'min'); }, onFocus: e => { this.foundation.onFocus(e, 'min'); }, onBlur: e => { this.foundation.onBlur(e, 'min'); }, role: "slider", "aria-valuetext": getAriaValueText ? getAriaValueText(currentValue, 0) : ariaValueText, tabIndex: disabled ? -1 : 0 }, commonAria, { "aria-valuenow": currentValue, "aria-valuemax": max, "aria-valuemin": min }), handleDot && /*#__PURE__*/_react.default.createElement("div", { className: _constants.cssClasses.HANDLE_DOT, style: Object.assign(Object.assign({}, (handleDot === null || handleDot === void 0 ? void 0 : handleDot.size) ? { width: handleDot.size, height: handleDot.size } : {}), (handleDot === null || handleDot === void 0 ? void 0 : handleDot.color) ? { backgroundColor: handleDot.color } : {}) })))) : (/*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_index.default, { content: tipChildren.min, position: "top", trigger: "custom", rePosKey: minPercent, visible: isInRenderTree && (tipVisible.min || firstDotFocusVisible), className: `${_constants.cssClasses.HANDLE}-tooltip` }, /*#__PURE__*/_react.default.createElement("span", Object.assign({ ref: this.minHanleEl, className: minClass, style: { [stylePos]: `${minPercent * 100}%`, zIndex: chooseMovePos === 'min' ? 2 : 1 }, onMouseDown: e => { this.foundation.onHandleDown(e, 'min'); }, onMouseEnter: () => { this.foundation.onHandleEnter('min'); }, onTouchStart: e => { this.foundation.onHandleTouchStart(e, 'min'); }, onMouseLeave: () => { this.foundation.onHandleLeave(); }, onKeyUp: e => { this.foundation.onHandleUp(e); }, onTouchEnd: e => { this.foundation.onHandleUp(e); }, onKeyDown: e => { this.foundation.handleKeyDown(e, 'min'); }, onFocus: e => { this.foundation.onFocus(e, 'min'); }, onBlur: e => { this.foundation.onBlur(e, 'min'); }, role: "slider", tabIndex: disabled ? -1 : 0 }, commonAria, { "aria-valuetext": getAriaValueText ? getAriaValueText(currentValue[0], 0) : ariaValueText, "aria-valuenow": currentValue[0], "aria-valuemax": currentValue[1], "aria-valuemin": min }), (handleDot === null || handleDot === void 0 ? void 0 : handleDot[0]) && /*#__PURE__*/_react.default.createElement("div", { className: _constants.cssClasses.HANDLE_DOT, style: Object.assign(Object.assign({}, ((_a = handleDot[0]) === null || _a === void 0 ? void 0 : _a.size) ? { width: handleDot[0].size, height: handleDot[0].size } : {}), ((_b = handleDot[0]) === null || _b === void 0 ? void 0 : _b.color) ? { backgroundColor: handleDot[0].color } : {}) }))), /*#__PURE__*/_react.default.createElement(_index.default, { content: tipChildren.max, position: "top", trigger: "custom", rePosKey: maxPercent, visible: isInRenderTree && (tipVisible.max || secondDotFocusVisible), className: `${_constants.cssClasses.HANDLE}-tooltip` }, /*#__PURE__*/_react.default.createElement("span", Object.assign({ ref: this.maxHanleEl, className: maxClass, style: { [stylePos]: `${maxPercent * 100}%`, zIndex: chooseMovePos === 'max' ? 2 : 1 }, onMouseDown: e => { this.foundation.onHandleDown(e, 'max'); }, onMouseEnter: () => { this.foundation.onHandleEnter('max'); }, onMouseLeave: () => { this.foundation.onHandleLeave(); }, onKeyUp: e => { this.foundation.onHandleUp(e); }, onTouchStart: e => { this.foundation.onHandleTouchStart(e, 'max'); }, onTouchEnd: e => { this.foundation.onHandleUp(e); }, onKeyDown: e => { this.foundation.handleKeyDown(e, 'max'); }, onFocus: e => { this.foundation.onFocus(e, 'max'); }, onBlur: e => { this.foundation.onBlur(e, 'max'); }, role: "slider", tabIndex: disabled ? -1 : 0 }, commonAria, { "aria-valuetext": getAriaValueText ? getAriaValueText(currentValue[1], 1) : ariaValueText, "aria-valuenow": currentValue[1], "aria-valuemax": max, "aria-valuemin": currentValue[0] }), ((_c = this.props.handleDot) === null || _c === void 0 ? void 0 : _c[1]) && /*#__PURE__*/_react.default.createElement("div", { className: _constants.cssClasses.HANDLE_DOT, style: Object.assign(Object.assign({}, ((_d = this.props.handleDot[1]) === null || _d === void 0 ? void 0 : _d.size) ? { width: this.props.handleDot[1].size, height: this.props.handleDot[1].size } : {}), ((_e = this.props.handleDot[1]) === null || _e === void 0 ? void 0 : _e.color) ? { backgroundColor: this.props.handleDot[1].color } : {}) }))))); return handleContents; }; this.renderTrack = () => { const { range, included, vertical } = this.props; const percentInfo = this.foundation.getMinAndMaxPercent(this.state.currentValue); const minPercent = percentInfo.min; const maxPercent = percentInfo.max; let trackStyle = !vertical ? { width: range ? `${Math.abs(maxPercent - minPercent) * 100}%` : `${minPercent * 100}%`, left: range ? `${Math.min(minPercent, maxPercent) * 100}%` : 0 } : { height: range ? `${Math.abs(maxPercent - minPercent) * 100}%` : `${minPercent * 100}%`, top: range ? `${Math.min(minPercent, maxPercent) * 100}%` : 0 }; trackStyle = included ? trackStyle : {}; return ( /*#__PURE__*/ // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions _react.default.createElement("div", { className: _constants.cssClasses.TRACK, style: trackStyle, onClick: this.foundation.handleWrapClick }) ); }; this.renderStepDot = () => { const { min, max, vertical, marks } = this.props; const stylePos = vertical ? 'top' : 'left'; const labelContent = marks && Object.keys(marks).length > 0 ? (/*#__PURE__*/_react.default.createElement("div", { className: _constants.cssClasses.DOTS }, Object.keys(marks).map(mark => { const activeResult = this.foundation.isMarkActive(Number(mark)); const markClass = (0, _classnames.default)(`${prefixCls}-dot`, { [`${prefixCls}-dot-active`]: this.foundation.isMarkActive(Number(mark)) === 'active' }); const markPercent = (Number(mark) - min) / (max - min); const dotDOM = /*#__PURE__*/ // eslint-disable-next-line jsx-a11y/no-static-element-interactions _react.default.createElement("span", { key: mark, onClick: this.foundation.handleWrapClick, className: markClass, style: { [stylePos]: `calc(${markPercent * 100}% - 2px)` } }); return activeResult ? this.props.tooltipOnMark ? /*#__PURE__*/_react.default.createElement(_index.default, { content: marks[mark] }, dotDOM) : dotDOM : null; }))) : null; return labelContent; }; this.renderLabel = () => { if (!this.props.showMarkLabel) { return null; } const { min, max, vertical, marks, verticalReverse } = this.props; const stylePos = vertical ? 'top' : 'left'; const labelContent = marks && Object.keys(marks).length > 0 ? (/*#__PURE__*/_react.default.createElement("div", { className: _constants.cssClasses.MARKS + (vertical && verticalReverse ? '-reverse' : '') }, Object.keys(marks).map(mark => { const activeResult = this.foundation.isMarkActive(Number(mark)); const markPercent = (Number(mark) - min) / (max - min); return activeResult ? ( /*#__PURE__*/ // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions _react.default.createElement("span", { key: mark, className: (0, _classnames.default)(`${prefixCls}-mark${vertical && verticalReverse ? '-reverse' : ''}`), style: { [stylePos]: `${markPercent * 100}%` }, onClick: this.foundation.handleWrapClick }, marks[mark])) : null; }))) : null; return labelContent; }; this._getAriaValueText = (value, index) => { const { getAriaValueText } = this.props; return getAriaValueText ? getAriaValueText(value, index) : value; }; let { value } = this.props; if (!value) { value = this.props.defaultValue; } this.state = { currentValue: value ? value : this.props.range ? [0, 0] : 0, min: this.props.min || 0, max: this.props.max || 0, focusPos: '', onChange: this.props.onChange, disabled: this.props.disabled || false, chooseMovePos: '', isDrag: false, clickValue: 0, showBoundary: false, isInRenderTree: true, firstDotFocusVisible: false, secondDotFocusVisible: false }; this.sliderEl = /*#__PURE__*/_react.default.createRef(); this.minHanleEl = /*#__PURE__*/_react.default.createRef(); this.maxHanleEl = /*#__PURE__*/_react.default.createRef(); this.dragging = [false, false]; this.foundation = new _foundation.default(this.adapter); this.eventListenerSet = new Set(); this.handleDownEventListenerSet = new Set(); } get adapter() { var _this = this; return Object.assign(Object.assign({}, super.adapter), { getSliderLengths: () => { var _a; if (this.sliderEl && this.sliderEl.current) { const rect = this.sliderEl.current.getBoundingClientRect(); const offsetParentRect = (_a = this.sliderEl.current.offsetParent) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect(); const offset = { x: offsetParentRect ? rect.left - offsetParentRect.left : this.sliderEl.current.offsetLeft, y: offsetParentRect ? rect.top - offsetParentRect.top : this.sliderEl.current.offsetTop }; return { sliderX: offset.x, sliderY: offset.y, sliderWidth: rect.width, sliderHeight: rect.height }; } return { sliderX: 0, sliderY: 0, sliderWidth: 0, sliderHeight: 0 }; }, getParentRect: () => { const parentObj = this.sliderEl && this.sliderEl.current && this.sliderEl.current.offsetParent; if (!parentObj) { return undefined; } return parentObj.getBoundingClientRect(); }, getScrollParentVal: () => { const scrollParent = this.foundation.getScrollParent(this.sliderEl.current); return { scrollTop: scrollParent.scrollTop, scrollLeft: scrollParent.scrollLeft }; }, isEventFromHandle: e => { const handles = [this.minHanleEl, this.maxHanleEl]; let flag = false; handles.forEach(handle => { if (!handle) { return; } const handleInstance = handle && handle.current; const handleDom = _reactDom.default.findDOMNode(handleInstance); if (handleDom && handleDom.contains(e.target)) { flag = true; } }); return flag; }, getOverallVars: () => ({ dragging: this.dragging }), updateDisabled: disabled => { this.setState({ disabled }); }, transNewPropsToState(stateObj) { let callback = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _noop2.default; this.setState(stateObj, callback); }, notifyChange: cbValue => { this.props.onChange(Array.isArray(cbValue) ? [...cbValue].sort((a, b) => a - b) : cbValue); }, setDragging: value => { this.dragging = value; }, updateCurrentValue: value => { const { currentValue } = this.state; if (value !== currentValue) { this.setState({ currentValue: value }); } }, setOverallVars: (key, value) => { this[key] = value; }, getMinHandleEl: () => this.minHanleEl.current, getMaxHandleEl: () => this.maxHanleEl.current, onHandleDown: e => { this.handleDownEventListenerSet.add(this._addEventListener(document.body, 'mousemove', this.foundation.onHandleMove, false)); this.handleDownEventListenerSet.add(this._addEventListener(window, 'mouseup', this.foundation.onHandleUp, false)); this.handleDownEventListenerSet.add(this._addEventListener(document.body, 'touchmove', this.foundation.onHandleTouchMove, false)); }, onHandleMove: function (mousePos, isMin) { let stateChangeCallback = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _noop2.default; let clickTrack = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; let outPutValue = arguments.length > 4 ? arguments[4] : undefined; const sliderDOMIsInRenderTree = _this.foundation.checkAndUpdateIsInRenderTreeState(); if (!sliderDOMIsInRenderTree) { return; } const { value } = _this.props; let finalOutPutValue = outPutValue; if (finalOutPutValue === undefined) { const moveValue = _this.foundation.transPosToValue(mousePos, isMin); if (moveValue === false) { return; } finalOutPutValue = _this.foundation.outPutValue(moveValue); } const { currentValue } = _this.state; if (!(0, _isEqual2.default)(_this.foundation.outPutValue(currentValue), finalOutPutValue)) { if (!clickTrack && _this.foundation.valueFormatIsCorrect(value)) { // still require afterChangeCallback when click on the track directly, need skip here return false; } _this.setState({ currentValue: finalOutPutValue }, stateChangeCallback); } }, setEventDefault: e => { e.stopPropagation(); e.preventDefault(); }, setStateVal: (name, val) => { this.setState({ [name]: val }); }, checkAndUpdateIsInRenderTreeState: () => { const sliderDOMIsInRenderTree = domIsInRenderTree(this.sliderEl.current); if (sliderDOMIsInRenderTree !== this.state.isInRenderTree) { this.setState({ isInRenderTree: sliderDOMIsInRenderTree }); } return sliderDOMIsInRenderTree; }, onHandleEnter: pos => { this.setState({ focusPos: pos }); }, onHandleLeave: () => { this.setState({ focusPos: '' }); }, onHandleUpBefore: e => { var _a, _b; (_b = (_a = this.props).onMouseUp) === null || _b === void 0 ? void 0 : _b.call(_a, e); e.stopPropagation(); e.preventDefault(); Array.from(this.handleDownEventListenerSet).forEach(clear => clear()); this.handleDownEventListenerSet.clear(); }, onHandleUpAfter: () => { const { currentValue } = this.state; const value = this.foundation.outPutValue(currentValue); this.props.onAfterChange(value); }, unSubscribeEventListener: () => { Array.from(this.eventListenerSet).forEach(clear => clear()); } }); } componentDidMount() { this.foundation.init(); } componentDidUpdate(prevProps, prevState) { const hasPropValueChange = !(0, _isEqual2.default)(this.props.value, prevProps.value); const hasPropDisabledChange = this.props.disabled !== prevProps.disabled; if (hasPropDisabledChange) { this.foundation.handleDisabledChange(this.props.disabled); } if (hasPropValueChange) { const nextValue = this.props.value; const prevValue = this.state.currentValue; this.foundation.handleValueChange(prevValue, nextValue); // trigger onAfterChange when value is controlled and changed this.props.onAfterChange(this.props.value); } } componentWillUnmount() { this.foundation.destroy(); } render() { const { disabled, currentValue, min, max } = this.state; const _a = this.props, { vertical, verticalReverse, style, railStyle, range, className } = _a, rest = __rest(_a, ["vertical", "verticalReverse", "style", "railStyle", "range", "className"]); const wrapperClass = (0, _classnames.default)(`${prefixCls}-wrapper`, { [`${prefixCls}-disabled`]: disabled, [`${_constants.cssClasses.VERTICAL}-wrapper`]: vertical, [`${prefixCls}-reverse`]: vertical && verticalReverse }, className); const boundaryClass = (0, _classnames.default)(`${prefixCls}-boundary`, { [`${prefixCls}-boundary-show`]: this.props.showBoundary && this.state.showBoundary }); const sliderCls = (0, _classnames.default)({ [`${prefixCls}`]: !vertical, [_constants.cssClasses.VERTICAL]: vertical }); const fixedCurrentValue = Array.isArray(currentValue) ? [...currentValue].sort() : currentValue; const ariaLabel = range ? `Range: ${this._getAriaValueText(fixedCurrentValue[0], 0)} to ${this._getAriaValueText(fixedCurrentValue[1], 1)}` : undefined; const slider = /*#__PURE__*/_react.default.createElement("div", Object.assign({ className: wrapperClass, style: style, ref: this.sliderEl, "aria-label": ariaLabel, onMouseEnter: () => this.foundation.handleWrapperEnter(), onMouseLeave: () => this.foundation.handleWrapperLeave() }, this.getDataAttr(rest)), /*#__PURE__*/_react.default.createElement("div", { className: `${prefixCls}-rail`, onClick: this.foundation.handleWrapClick, style: railStyle }), this.renderTrack(), this.renderStepDot(), /*#__PURE__*/_react.default.createElement("div", null, this.renderHandle()), this.renderLabel(), /*#__PURE__*/_react.default.createElement("div", { className: boundaryClass }, /*#__PURE__*/_react.default.createElement("span", { className: `${prefixCls}-boundary-min` }, min), /*#__PURE__*/_react.default.createElement("span", { className: `${prefixCls}-boundary-max` }, max))); if (!vertical) { return /*#__PURE__*/_react.default.createElement("div", { className: sliderCls }, slider); } return slider; } _addEventListener(target, eventName, callback) { if (target.addEventListener) { for (var _len = arguments.length, rests = new Array(_len > 3 ? _len - 3 : 0), _key = 3; _key < _len; _key++) { rests[_key - 3] = arguments[_key]; } target.addEventListener(eventName, callback, ...rests); const clearSelf = () => { target === null || target === void 0 ? void 0 : target.removeEventListener(eventName, callback); Promise.resolve().then(() => { this.eventListenerSet.delete(clearSelf); }); }; this.eventListenerSet.add(clearSelf); return clearSelf; } else { return _noop2.default; } } } exports.default = Slider; Slider.propTypes = { // allowClear: PropTypes.bool, defaultValue: _propTypes.default.oneOfType([_propTypes.default.number, _propTypes.default.array]), disabled: _propTypes.default.bool, showMarkLabel: _propTypes.default.bool, included: _propTypes.default.bool, marks: _propTypes.default.object, max: _propTypes.default.number, min: _propTypes.default.number, range: _propTypes.default.bool, step: _propTypes.default.number, tipFormatter: _propTypes.default.func, value: _propTypes.default.oneOfType([_propTypes.default.number, _propTypes.default.array]), vertical: _propTypes.default.bool, onAfterChange: _propTypes.default.func, onChange: _propTypes.default.func, onMouseUp: _propTypes.default.func, tooltipOnMark: _propTypes.default.bool, tooltipVisible: _propTypes.default.bool, showArrow: _propTypes.default.bool, style: _propTypes.default.object, className: _propTypes.default.string, showBoundary: _propTypes.default.bool, railStyle: _propTypes.default.object, verticalReverse: _propTypes.default.bool, getAriaValueText: _propTypes.default.func, handleDot: _propTypes.default.oneOfType([_propTypes.default.shape({ size: _propTypes.default.string, color: _propTypes.default.string }), _propTypes.default.arrayOf(_propTypes.default.shape({ size: _propTypes.default.string, color: _propTypes.default.string }))]) }; Slider.defaultProps = { // allowClear: false, disabled: false, showMarkLabel: true, tooltipOnMark: false, included: true, max: 100, min: 0, range: false, showArrow: true, step: 1, tipFormatter: value => value, vertical: false, showBoundary: false, onAfterChange: value => { // console.log(value); }, onChange: value => { // console.log(value); }, verticalReverse: false };