UNPKG

vcc-ui

Version:

A React library for building user interfaces at Volvo Cars

380 lines (376 loc) 13 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.Slider = void 0; var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _react = _interopRequireWildcard(require("react")); var _reactFela = require("react-fela"); var _block = require("../block"); var _click = require("../click"); var _icon = require("../icon"); function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-nocheck const KEY_LEFT = 37; const KEY_UP = 38; const KEY_RIGHT = 39; const KEY_DOWN = 40; const THUMB_WIDTH = 40; const THUMB_HEIGHT = 40; const thumbStyle = _ref => { let { left, isDragging, theme, disabled } = _ref; return { position: 'absolute', top: 0, left: left, width: THUMB_WIDTH, height: THUMB_HEIGHT, borderWidth: 1, borderStyle: 'solid', borderColor: theme?.tokens?.inputBorder, background: theme?.tokens?.inputBackground, cursor: disabled ? 'not-allowed' : 'ew-resize', display: 'flex', alignItems: 'center', justifyContent: 'center', outlineWidth: 0, ':focus-visible': { ...(theme !== undefined ? theme.states.focus : undefined) }, extend: { condition: !isDragging, style: { transition: 'left 200ms ease-out' } } }; }; class SliderComponent extends _react.Component { constructor(props) { super(props); (0, _defineProperty2.default)(this, "resizeEventHandler", void 0); (0, _defineProperty2.default)(this, "trackRef", void 0); (0, _defineProperty2.default)(this, "thumbRef", void 0); (0, _defineProperty2.default)(this, "state", { isDragging: false, lastStep: 0, currentLeft: 0 }); (0, _defineProperty2.default)(this, "componentDidUpdate", prevProps => { const { initialValue } = this.props; const { initialValue: prevInitialValue } = prevProps; if (initialValue !== prevInitialValue) { this.setState(() => ({ currentStep: this.getStepForValue(initialValue) })); } }); (0, _defineProperty2.default)(this, "handleChange", () => { const { onChange = () => {} } = this.props; const { lastStep, currentStep } = this.state; const currentValue = this.getCurrentValue(); if (lastStep !== currentStep && typeof onChange === 'function') { onChange(currentValue || 0); } }); (0, _defineProperty2.default)(this, "getElementProperty", (element, property) => { return element && element.getBoundingClientRect ? element.getBoundingClientRect()[property] : 0; }); (0, _defineProperty2.default)(this, "getElementWidth", element => { return element ? this.getElementProperty(element, 'width') : 0; }); (0, _defineProperty2.default)(this, "getElementLeft", element => { return element ? this.getElementProperty(element, 'left') : 0; }); (0, _defineProperty2.default)(this, "getNumberOfSteps", () => { const { step, minValue, maxValue, valueList = [] } = this.props; if (valueList.length > 0) { return valueList.length; } else { return (maxValue || 0) - (minValue || 0) / (step || 0) + 1; } }); (0, _defineProperty2.default)(this, "getStepAtPercentagePosition", percentagePosition => { const steps = this.getNumberOfSteps(); return Math.round(percentagePosition * (steps - 1)); }); (0, _defineProperty2.default)(this, "getStepForValue", value => { const { minValue, maxValue, valueList = [] } = this.props; const steps = this.getNumberOfSteps(); const thisValue = value - (minValue || 0); const range = (maxValue || 0) - (minValue || 0); if (valueList.length > 0) { return valueList.indexOf(value); } else { return Math.round((steps - 1) * thisValue / range); } }); (0, _defineProperty2.default)(this, "getValueAtStepPosition", step => { const { minValue, maxValue, valueList = [] } = this.props; const steps = this.getNumberOfSteps(); if (valueList.length > 0) { return valueList.find((value, index) => index === step); } else { return (minValue || 0) + (step || 0) * (((maxValue || 0) - (minValue || 0)) / (steps - 1)); } }); (0, _defineProperty2.default)(this, "getCurrentValue", () => { const { currentStep } = this.state; return this.getValueAtStepPosition(currentStep); }); (0, _defineProperty2.default)(this, "updateCurrentStep", position => { const trackWidth = this.getElementWidth(this.trackRef.current); const trackLeft = this.getElementLeft(this.trackRef.current); const thumbWidth = THUMB_WIDTH; // we invert the leftOffset value on rtl layout const leftOffset = position - trackLeft - thumbWidth / 2; const newPositionLeft = this.props?.theme?.direction === 'ltr' ? leftOffset : trackWidth - leftOffset; const isMax = newPositionLeft >= trackWidth - thumbWidth; const isMin = newPositionLeft <= 0; const currentPositionLeft = isMax ? trackWidth - thumbWidth : isMin ? 0 : newPositionLeft; const currentPositionPercent = currentPositionLeft / (trackWidth - thumbWidth); if (typeof position === 'number') { this.setState({ currentStep: this.getStepAtPercentagePosition(currentPositionPercent), currentLeft: currentPositionLeft }); } }); (0, _defineProperty2.default)(this, "getLeftPositionFromCurrentStep", () => { if (!this.trackRef || !this.thumbRef) { return 0; } const { currentStep = 0, currentLeft = 0 } = this.state; const steps = this.getNumberOfSteps(); const trackWidth = this.getElementWidth(this.trackRef.current); const leftPos = currentStep * (trackWidth - THUMB_WIDTH) / (steps - 1); return currentLeft ? currentLeft : Math.round(leftPos); }); (0, _defineProperty2.default)(this, "handleMouseDown", e => { this.updateCurrentStep(e.clientX); this.setState({ lastStep: this.state.currentStep }); }); (0, _defineProperty2.default)(this, "handleDragStart", () => { this.setState({ isDragging: true, lastStep: this.state.currentStep }); document.addEventListener('mousemove', this.handleDrag); document.addEventListener('mouseup', this.handleDragEnd); }); (0, _defineProperty2.default)(this, "handleDrag", e => { const { onMoveStart } = this.props; const currentValue = this.getCurrentValue(); e.preventDefault(); e.stopPropagation(); if (this.state.isDragging) { this.updateCurrentStep('touches' in e ? e.touches[0].clientX : e.clientX); if (typeof onMoveStart === 'function') { onMoveStart(currentValue || 0); } } }); (0, _defineProperty2.default)(this, "handleDragEnd", () => { const { onMoveEnd } = this.props; this.setState({ isDragging: false }); document.removeEventListener('mousemove', this.handleDrag); document.removeEventListener('mouseup', this.handleDragEnd); this.thumbRef?.current?.blur(); this.resetCurrentLeft(); this.handleChange(); if (typeof onMoveEnd === 'function') { onMoveEnd(); } }); (0, _defineProperty2.default)(this, "adjustCurrentStepBy", diff => { const { currentStep } = this.state; const steps = this.getNumberOfSteps(); const nextStep = (currentStep || 0) + diff; this.setState({ currentStep: nextStep <= steps - 1 && nextStep >= 0 ? nextStep : currentStep }); }); (0, _defineProperty2.default)(this, "handleKeyDown", e => { const key = e.which; if (![KEY_DOWN, KEY_UP, KEY_RIGHT, KEY_LEFT].includes(key)) return; e.preventDefault(); e.stopPropagation(); if (key === KEY_UP || key === KEY_RIGHT) { this.adjustCurrentStepBy(1); } else if (key === KEY_DOWN || key === KEY_LEFT) { this.adjustCurrentStepBy(-1); } }); this.trackRef = /*#__PURE__*/(0, _react.createRef)(); this.thumbRef = /*#__PURE__*/(0, _react.createRef)(); this.resizeEventHandler = () => {}; } componentDidMount() { const { initialValue } = this.props; this.setState({ currentStep: this.getStepForValue(initialValue) }); let resizeTimer; this.resizeEventHandler = () => { clearTimeout(resizeTimer); resizeTimer = setTimeout(() => { this.forceUpdate(); }, 1); }; window.addEventListener('resize', this.resizeEventHandler); } componentWillUnmount() { window.removeEventListener('resize', this.resizeEventHandler); } resetCurrentLeft() { this.setState({ currentLeft: 0 }); } handleMouseUp() { this.resetCurrentLeft(); this.handleChange(); } handleKeyUp() { this.setState({ lastStep: this.state.currentStep }); this.handleChange(); } renderThumb() { const { minValue, maxValue, theme, disabled } = this.props; const value = this.getCurrentValue(); const left = this.getLeftPositionFromCurrentStep(); const styleProps = { isDragging: this.state.isDragging, left, theme, disabled }; const color = disabled ? 'secondary' : 'action'; return /*#__PURE__*/_react.default.createElement(_click.Click, { ref: this.thumbRef, extend: thumbStyle(styleProps), role: "slider", "aria-valuemin": minValue, "aria-valuemax": maxValue, "aria-valuenow": value, "aria-orientation": "horizontal", disabled: disabled, onMouseDown: () => this.handleDragStart(), onMouseMove: e => this.handleDrag(e), onMouseUp: () => this.handleDragEnd(), onContextMenu: () => this.handleDragEnd(), onTouchStart: () => this.handleDragStart(), onTouchMove: e => this.handleDrag(e), onTouchEnd: () => this.handleDragEnd(), onKeyDown: e => this.handleKeyDown(e), onKeyUp: () => this.handleKeyUp() }, /*#__PURE__*/_react.default.createElement(_icon.Icon, { color: color, type: "navigation-chevronback-12" }), /*#__PURE__*/_react.default.createElement(_icon.Icon, { color: color, type: "navigation-chevronforward-12" })); } render() { const { theme } = this.props; return /*#__PURE__*/_react.default.createElement(_block.Block, { extend: { position: 'relative', height: 42 } }, /*#__PURE__*/_react.default.createElement(_block.Block, { extend: [{ position: 'absolute', top: 9, left: 0, right: 0, width: '100%', boxSizing: 'border-box', height: 21, borderWidth: 1, borderStyle: 'solid', borderColor: theme?.tokens?.inputBorder, background: theme?.tokens?.inputBackground, cursor: this.props.disabled ? 'not-allwoed' : 'pointer' }], ref: this.trackRef, onMouseDown: e => { if (!this.props.disabled) { this.handleMouseDown(e); } }, onMouseUp: () => { if (!this.props.disabled) { this.handleMouseUp(); } } }), typeof this.state.currentStep !== 'undefined' && this.renderThumb()); } } (0, _defineProperty2.default)(SliderComponent, "displayName", 'Slider'); SliderComponent.displayName = 'Slider'; /** * @deprecated Use `import { RangeSlider } from '@volvo-cars/react-forms'` instead. See [RangeSlider](https://developer.volvocars.com/design-system/web/?path=/docs/components-forms-rangeslider--docs) */ const Slider = props => { const ComponentWithTheme = (0, _reactFela.withTheme)(SliderComponent); return /*#__PURE__*/_react.default.createElement(ComponentWithTheme, props); }; exports.Slider = Slider;