UNPKG

@vimeo/iris

Version:
212 lines (209 loc) 10.9 kB
import { b as __rest, c as __assign, _ as __read } from '../../../tslib.es6-7f0e734f.js'; import React__default, { forwardRef, useRef, useReducer, useCallback, useLayoutEffect, useEffect } from 'react'; import { reducer, initialState } from './Slider.state.esm.js'; import { Background, ActiveRange, SliderContainer, Label } from './Slider.style.esm.js'; import { Handle } from './Handle.esm.js'; import { end, home, arrowRight, arrowUp, arrowLeft, arrowDown } from '../../../utils/events/KeyCodes.esm.js'; import { SliderEditableInput } from './SliderEditableInput.esm.js'; import { useOutsideClick } from '../../../utils/hooks/useOutsideClick.esm.js'; import { geometry } from '../../../utils/DOM/geometry.esm.js'; import 'styled-components'; import 'polished'; import './SliderInputArrow.esm.js'; import '../../../color/colors.esm.js'; import '../../../tokens/core.esm.js'; import '../../../tokens/color/index.esm.js'; import '../../../tokens/color/background/background.esm.js'; import '../../../tokens/util/readToken.esm.js'; import '../../../tokens/util/clamp.esm.js'; import '../../../tokens/color/format/format.esm.js'; import '../../../tokens/color/format/primary.esm.js'; import '../../../tokens/color/format/secondary.esm.js'; import '../../../tokens/color/format/tertiary.esm.js'; import '../../../tokens/color/rainbow/rainbow.esm.js'; import '../../../tokens/color/rainbow/conic/index.esm.js'; import '../../../tokens/color/rainbow/conic/sm.esm.js'; import '../../../tokens/color/rainbow/conic/xl.esm.js'; import '../../../tokens/color/rainbow/linear/index.esm.js'; import '../../../tokens/color/rainbow/linear/sm.esm.js'; import '../../../tokens/color/rainbow/linear/xl.esm.js'; import '../../../tokens/color/livestream/livestream.esm.js'; import '../../../tokens/color/status/status.esm.js'; import '../../../tokens/color/status/caution.esm.js'; import '../../../tokens/color/status/negative.esm.js'; import '../../../tokens/color/status/positive.esm.js'; import '../../../tokens/color/stroke/stroke.esm.js'; import '../../../tokens/color/surface/surface.esm.js'; import '../../../tokens/color/text/text.esm.js'; import '../../../tokens/util/round.esm.js'; import '../../../tokens/color/upsell/upsell.esm.js'; import '../../../tokens/color/upsell/sm.esm.js'; import '../../../tokens/color/upsell/xl.esm.js'; import '../../../tokens/color/upsell/new.esm.js'; import '../../../tokens/edge/edge.esm.js'; import '../../../tokens/space/space.esm.js'; import '../../../tokens/typography/index.esm.js'; import '../../../tokens/typography/size/size.esm.js'; import '../../../utils/css.esm.js'; function Slider(_a) { var disabled = _a.disabled, editableLabel = _a.editableLabel, _b = _a.formatter, formatter = _b === void 0 ? function (value) { return value; } : _b, _c = _a.initialValues, initialValues = _c === void 0 ? [0, 100] : _c, _d = _a.max, max = _d === void 0 ? 100 : _d, _e = _a.min, min = _e === void 0 ? 0 : _e, onChange = _a.onChange, onDragEnd = _a.onDragEnd, range = _a.range, props = __rest(_a, ["disabled", "editableLabel", "formatter", "initialValues", "max", "min", "onChange", "onDragEnd", "range"]); var isFirstRender = useRef(true); var trackRef = useRef(null); var startHandleRef = useRef(null); var endHandleRef = useRef(null); useOutsideClick([trackRef], function () { setFocus(null); }); var _f = __read(useReducer(reducer, initialState(initialValues)), 2), state = _f[0], dispatch = _f[1]; var values = state.values, trackRect = state.trackRect, focused = state.focused, dragging = state.dragging; var dispatchChangeEvent = useCallback(function (callback) { var _a; var currentInput = focused === 'startHandle' || focused === 'startInput' ? startHandleRef : endHandleRef; if (!currentInput.current) currentInput = startHandleRef; var event = new Event('change', { bubbles: true }); (_a = currentInput.current) === null || _a === void 0 ? void 0 : _a.dispatchEvent(event); callback && callback(event); }, [focused]); function setDragging(payload) { if (payload) setFocus(payload); return dispatch({ type: 'SET_DRAGGING', payload: payload }); } function setValue(payload) { return dispatch({ type: 'SET_VALUES', payload: payload }); } function setFocus(payload) { return dispatch({ type: 'SET_FOCUS', payload: payload }); } function setStartValue(value) { var newValue = constrain(min, range ? values[1] - 1 : max)(value); setValue([newValue, values[1]]); } function setEndValue(value) { var newValue = constrain(values[0] + 1, max)(value); setValue([values[0], newValue]); } function handleArrowClick(direction, handle) { var action = handle === 'startInput' ? setStartValue : setEndValue; var currentValue = handle === 'startInput' ? values[0] : values[1]; if (direction === 'up') action(currentValue + 1); else action(currentValue - 1); } useLayoutEffect(function () { var _a = geometry(trackRef.current), left = _a.left, width = _a.width; dispatch({ type: 'SET_TRACK_RECT', payload: { left: left, width: width } }); }, [dragging]); useEffect(function () { if (!isFirstRender.current) { dispatchChangeEvent(onChange); } else { isFirstRender.current = false; } // dispatchChangeEvent is updated on focus change and we want this flow to fire only on values / onChange updates // eslint-disable-next-line react-hooks/exhaustive-deps }, [onChange, values]); useEffect(function () { var mouseup = function () { if (dragging) { setDragging(false); dispatchChangeEvent(onDragEnd); } }; var keydown = function (event) { if (!focused || !isHandleFocused(focused)) return; var action = focused === 'startHandle' ? setStartValue : setEndValue; var currentValue = focused === 'startHandle' ? values[0] : values[1]; switch (event.keyCode) { case arrowDown: case arrowLeft: action(currentValue - 1); break; case arrowUp: case arrowRight: action(currentValue + 1); break; case home: action(min); break; case end: action(max); break; } }; document && document.addEventListener('mouseup', mouseup); document && document.addEventListener('keydown', keydown); return function () { document && document.removeEventListener('mouseup', mouseup); document && document.removeEventListener('keydown', keydown); }; }); useEffect(function () { function mousemove(event) { if (dragging) { var pos = constrainedPosition(event, trackRect, min, max); if (dragging === 'startHandle') setStartValue(pos); if (dragging === 'endHandle') setEndValue(pos); } } document && document.addEventListener('mousemove', mousemove); return function () { return document && document.removeEventListener('mousemove', mousemove); }; }); return (React__default.createElement(SliderContainer, __assign({}, props, { range: range, "aria-label": "slider", role: "slider", "aria-valuemin": min, "aria-valuemax": max, "aria-valuenow": dragging === 'startHandle' ? values[0] : values[1] }), range && (React__default.createElement(Label, { focused: focused === 'startInput' }, editableLabel ? (React__default.createElement(SliderEditableInput, { value: values[0], disabled: disabled, onFocus: function () { return setFocus('startInput'); }, onChange: function (e) { return !e.button && setStartValue(parseInt(e.target.value)); }, role: "start-input", onArrowUpClick: function () { return handleArrowClick('up', range ? 'endInput' : 'startInput'); }, onArrowDownClick: function () { return handleArrowClick('down', range ? 'endInput' : 'startInput'); } })) : (formatter(values[0])))), React__default.createElement(Track, { ref: trackRef, values: values, id: "track-1", max: max, range: range, "aria-label": "track" }, React__default.createElement(Handle, { disabled: disabled, handle: "startHandle", min: min, max: max, setFocus: setFocus, setDragging: setDragging, value: values[0], ref: startHandleRef }), range && (React__default.createElement(Handle, { disabled: disabled, handle: "endHandle", min: min, max: max, setFocus: setFocus, setDragging: setDragging, value: values[1], ref: endHandleRef }))), React__default.createElement(Label, { focused: focused === (range ? 'endInput' : 'startInput') }, editableLabel ? (React__default.createElement(SliderEditableInput, { disabled: disabled, value: range ? values[1] : values[0], onFocus: function () { return setFocus(range ? 'endInput' : 'startInput'); }, onChange: function (e) { range ? setEndValue(parseInt(e.target.value)) : setStartValue(parseInt(e.target.value)); }, role: range ? 'end-input' : 'start-input', onArrowUpClick: function () { return handleArrowClick('up', range ? 'endInput' : 'startInput'); }, onArrowDownClick: function () { return handleArrowClick('down', range ? 'endInput' : 'startInput'); } })) : (formatter(range ? values[1] : values[0]))))); } var Track = forwardRef(function (_a, ref) { var children = _a.children, values = _a.values, max = _a.max, range = _a.range, props = __rest(_a, ["children", "values", "max", "range"]); return (React__default.createElement(Background, __assign({ ref: ref }, props), children, React__default.createElement(ActiveRange, { values: values, max: max, range: range }))); }); function isHandleFocused(focusedElement) { return focusedElement.includes('Handle'); } function constrainedPosition(event, element, min, max) { var left = event.clientX - element.left; var pos = Math.round((left / element.width) * max); return constrain(min, max)(pos); } function constrain(min, max) { return function (val) { if (val > max) return max; if (val < min) return min; return val; }; } export { Slider };