UNPKG

vcc-ui

Version:

A React library for building user interfaces at Volvo Cars

370 lines (367 loc) 11.5 kB
import _defineProperty from "@babel/runtime/helpers/defineProperty"; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-nocheck import React, { Component, createRef } from 'react'; import { withTheme } from 'react-fela'; import { Block } from '../block'; import { Click } from '../click'; import { Icon } from '../icon'; 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 Component { constructor(props) { super(props); _defineProperty(this, "resizeEventHandler", void 0); _defineProperty(this, "trackRef", void 0); _defineProperty(this, "thumbRef", void 0); _defineProperty(this, "state", { isDragging: false, lastStep: 0, currentLeft: 0 }); _defineProperty(this, "componentDidUpdate", prevProps => { const { initialValue } = this.props; const { initialValue: prevInitialValue } = prevProps; if (initialValue !== prevInitialValue) { this.setState(() => ({ currentStep: this.getStepForValue(initialValue) })); } }); _defineProperty(this, "handleChange", () => { const { onChange = () => {} } = this.props; const { lastStep, currentStep } = this.state; const currentValue = this.getCurrentValue(); if (lastStep !== currentStep && typeof onChange === 'function') { onChange(currentValue || 0); } }); _defineProperty(this, "getElementProperty", (element, property) => { return element && element.getBoundingClientRect ? element.getBoundingClientRect()[property] : 0; }); _defineProperty(this, "getElementWidth", element => { return element ? this.getElementProperty(element, 'width') : 0; }); _defineProperty(this, "getElementLeft", element => { return element ? this.getElementProperty(element, 'left') : 0; }); _defineProperty(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; } }); _defineProperty(this, "getStepAtPercentagePosition", percentagePosition => { const steps = this.getNumberOfSteps(); return Math.round(percentagePosition * (steps - 1)); }); _defineProperty(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); } }); _defineProperty(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)); } }); _defineProperty(this, "getCurrentValue", () => { const { currentStep } = this.state; return this.getValueAtStepPosition(currentStep); }); _defineProperty(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 }); } }); _defineProperty(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); }); _defineProperty(this, "handleMouseDown", e => { this.updateCurrentStep(e.clientX); this.setState({ lastStep: this.state.currentStep }); }); _defineProperty(this, "handleDragStart", () => { this.setState({ isDragging: true, lastStep: this.state.currentStep }); document.addEventListener('mousemove', this.handleDrag); document.addEventListener('mouseup', this.handleDragEnd); }); _defineProperty(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); } } }); _defineProperty(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(); } }); _defineProperty(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 }); }); _defineProperty(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__*/createRef(); this.thumbRef = /*#__PURE__*/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.createElement(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.createElement(Icon, { color: color, type: "navigation-chevronback-12" }), /*#__PURE__*/React.createElement(Icon, { color: color, type: "navigation-chevronforward-12" })); } render() { const { theme } = this.props; return /*#__PURE__*/React.createElement(Block, { extend: { position: 'relative', height: 42 } }, /*#__PURE__*/React.createElement(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()); } } _defineProperty(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) */ export const Slider = props => { const ComponentWithTheme = withTheme(SliderComponent); return /*#__PURE__*/React.createElement(ComponentWithTheme, props); };