UNPKG

wix-style-react

Version:
252 lines (213 loc) • 6.76 kB
import React, { Component } from 'react'; import PropTypes from 'prop-types'; import Slide from 'rc-slider'; import { dataHooks } from './constants'; import { generateID } from '../utils/generateId'; import SliderHandle from './SliderHandle'; import Text from '../Text'; import { st, classes } from './Slider.st.css'; const range = ({ min, max, step }) => { const arr = []; for (let i = min; i <= max; i += step) { arr.push(i); } return arr; }; /** * A slider component with multi-range support */ export default class Slider extends Component { _getMarks() { const marksLabels = {}; if (this._isCustomMarks()) { const { marks } = this.props; Object.entries(marks).forEach(([key, value]) => { marksLabels[key] = { label: this._createMarkNode(value, true), }; }); } else { const { min, max, step, startPoint } = this.props; range({ min, max, step }).forEach(entry => { const shouldRenderMarkLabel = entry === min || entry === max || entry === startPoint; marksLabels[entry] = { label: this._createMarkNode(entry, shouldRenderMarkLabel), }; }); } return marksLabels; } _isCustomMarks() { const { marks } = this.props; return Object.entries(marks).length > 0; } _createMarkNode(value, shouldRenderMarkLabel) { return ( <div className={st(classes.mark, { direction: this.props.direction })}> <div className={classes.markLine} /> <div className={classes.markValue}> {shouldRenderMarkLabel && ( <Text className={classes.markLabel} dataHook={dataHooks.sliderMarkLabel} weight="thin" size="small" > {value} </Text> )} </div> </div> ); } _renderHandle = handleProps => { const { displayTooltip, disabled, marks, dataHook, direction } = this.props; const { index, value, min, max, offset, ref, ariaLabel, reverse } = handleProps; let tooltipValue; if (this._isCustomMarks()) { if (marks.hasOwnProperty(value)) { tooltipValue = marks[value].toString(); } } else { tooltipValue = value.toString(); } return ( <SliderHandle key={index} disabled={disabled} displayTooltip={displayTooltip} tooltipValue={tooltipValue} handleProps={{ ref, offset, min, max, value, reverse, ariaLabel }} index={index} dataHook={dataHook} direction={direction} /> ); }; _renderSlider = () => { const { pushable, allowCross, value, displayMarks, direction, startPoint, dataHook, id, ariaLabelForHandle, rtl, className, ...rest } = this.props; return Array.isArray(value) && value.length > 1 ? ( <Slide.Range {...rest} vertical={direction === 'vertical'} prefixCls="wsr-slider" handle={this._renderHandle} marks={displayMarks ? this._getMarks() : {}} value={value} pushable={pushable} allowCros={allowCross} reverse={rtl} ariaLabelGroupForHandles={ariaLabelForHandle} /> ) : ( <Slide {...rest} vertical={direction === 'vertical'} prefixCls="wsr-slider" startPoint={startPoint} handle={this._renderHandle} marks={displayMarks ? this._getMarks() : {}} value={Array.isArray(value) ? value[0] : value} reverse={rtl} ariaLabelForHandle={ Array.isArray(ariaLabelForHandle) ? ariaLabelForHandle[0] : ariaLabelForHandle } /> ); }; render() { const { dataHook, id, direction, className } = this.props; return ( <div className={st(classes.root, className)} id={id} data-hook={dataHook} data-direction={direction} > {this._renderSlider()} </div> ); } } Slider.displayName = 'Slider'; Slider.propTypes = { /** Allows sliders handles to cross. */ allowCross: PropTypes.bool, /** Applies a data-hook HTML attribute that can be used in the tests. */ dataHook: PropTypes.string, /** Specifies a CSS class name to be appended to the component’s root element. */ className: PropTypes.string, /** Controls the visibility of the marks. */ displayMarks: PropTypes.bool, /** Controls visibility of slide handle tooltip */ displayTooltip: PropTypes.bool, /** Assigns an unique identifier for the root element. */ id: PropTypes.string, /** Sets the absolute maximum value of sliders range. */ max: PropTypes.number, /** Sets the absolute minimum value of the sliders range. */ min: PropTypes.number, /** Specifies slider marks. The key determines the position, and the value determines what will show. The object structure should be either * ```{ number : number}``` / ```{ number : string }``` * */ marks: PropTypes.object, /** Defines a callback function which will be called when ontouchend or onmouseup is triggered. */ onAfterChange: PropTypes.func, /** Defines a callback function which will be called when ontouchstart or onmousedown is triggered. */ onBeforeChange: PropTypes.func, /** Defines a callback function which is called upon every value change. */ onChange: PropTypes.func.isRequired, /** Adjust component for RTL. */ rtl: PropTypes.bool, /** Specifies the interval between range values. */ step: PropTypes.number, /** Push surrounding handles when moving a handle (relevant for multi value sliders only). Number specifies a minimum distance between handles. */ pushable: PropTypes.oneOfType([PropTypes.bool, PropTypes.number]), /** Specifies the selected value. */ value: PropTypes.oneOfType([ PropTypes.arrayOf(PropTypes.number), PropTypes.number, ]), /** Specifies whether interactions are disabled. */ disabled: PropTypes.bool, /** Specifies the starting value of a track. If undefined, the minimum value of a range is used as a starting point. */ startPoint: PropTypes.number, /** Sets slider direction in either horizontal way or vertical */ direction: PropTypes.oneOf(['vertical', 'horizontal']), /** Set the aria-label attributes for slider handles. */ ariaLabelForHandle: PropTypes.oneOfType([ PropTypes.arrayOf(PropTypes.string), PropTypes.string, ]), }; Slider.defaultProps = { min: 1, max: 20, step: 1, value: 3, allowCross: true, id: generateID(), displayTooltip: true, displayMarks: true, marks: {}, rtl: false, disabled: false, startPoint: undefined, };