vcc-ui
Version:
A React library for building user interfaces at Volvo Cars
380 lines (376 loc) • 13 kB
JavaScript
"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;