UNPKG

react95

Version:

Refreshed Windows95 UI components for modern web apps - React95

394 lines (381 loc) 14.8 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var React = require('react'); var styled = require('styled-components'); var index = require('../common/index.js'); var useControlledOrUncontrolled = require('../common/hooks/useControlledOrUncontrolled.js'); var useEventCallback = require('../common/hooks/useEventCallback.js'); var useForkRef = require('../common/hooks/useForkRef.js'); var useIsFocusVisible = require('../common/hooks/useIsFocusVisible.js'); var index$1 = require('../common/utils/index.js'); var ScrollView = require('../ScrollView/ScrollView.js'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var React__default = /*#__PURE__*/_interopDefaultLegacy(React); var styled__default = /*#__PURE__*/_interopDefaultLegacy(styled); function percentToValue(percent, min, max) { return (max - min) * percent + min; } function trackFinger(event, touchId) { if (touchId !== void 0 && "changedTouches" in event) { for (let i = 0; i < event.changedTouches.length; i += 1) { const touch = event.changedTouches[i]; if (touch.identifier === touchId) { return { x: touch.clientX, y: touch.clientY }; } } return false; } if ("clientX" in event) { return { x: event.clientX, y: event.clientY }; } return false; } function ownerDocument(node) { return node && node.ownerDocument || document; } function findClosest(values, currentValue) { var _a; const { index: closestIndex } = (_a = values.reduce((acc, value, index) => { const distance = Math.abs(currentValue - value); if (acc === null || distance < acc.distance || distance === acc.distance) { return { distance, index }; } return acc; }, null)) !== null && _a !== void 0 ? _a : {}; return closestIndex !== null && closestIndex !== void 0 ? closestIndex : -1; } const Wrapper = styled__default["default"].div` display: inline-block; position: relative; touch-action: none; &:before { content: ''; display: inline-block; position: absolute; top: -2px; left: -15px; width: calc(100% + 30px); height: ${({ hasMarks }) => hasMarks ? "41px" : "39px"}; ${({ isFocused, theme }) => isFocused && ` outline: 2px dotted ${theme.materialText}; `} } ${({ orientation, size }) => orientation === "vertical" ? styled.css` height: ${size}; margin-right: 1.5rem; &:before { left: -6px; top: -15px; height: calc(100% + 30px); width: ${({ hasMarks }) => hasMarks ? "41px" : "39px"}; } ` : styled.css` width: ${size}; margin-bottom: 1.5rem; &:before { top: -2px; left: -15px; width: calc(100% + 30px); height: ${({ hasMarks }) => hasMarks ? "41px" : "39px"}; } `} pointer-events: ${({ $disabled }) => $disabled ? "none" : "auto"}; `; const sharedGrooveStyles = () => styled.css` position: absolute; ${({ orientation }) => orientation === "vertical" ? styled.css` bottom: 0; left: 50%; transform: translateX(-50%); height: 100%; width: 8px; ` : styled.css` left: 0; top: 50%; transform: translateY(-50%); height: 8px; width: 100%; `} `; const StyledGroove = styled__default["default"](ScrollView.StyledScrollView)` ${sharedGrooveStyles()} `; const StyledFlatGroove = styled__default["default"](ScrollView.StyledScrollView)` ${sharedGrooveStyles()} border-left-color: ${({ theme }) => theme.flatLight}; border-top-color: ${({ theme }) => theme.flatLight}; border-right-color: ${({ theme }) => theme.canvas}; border-bottom-color: ${({ theme }) => theme.canvas}; &:before { border-left-color: ${({ theme }) => theme.flatDark}; border-top-color: ${({ theme }) => theme.flatDark}; border-right-color: ${({ theme }) => theme.flatLight}; border-bottom-color: ${({ theme }) => theme.flatLight}; } `; const Thumb = styled__default["default"].span` position: relative; ${({ orientation }) => orientation === "vertical" ? styled.css` width: 32px; height: 18px; right: 2px; transform: translateY(-50%); ` : styled.css` height: 32px; width: 18px; top: 2px; transform: translateX(-50%); `} ${({ variant }) => variant === "flat" ? styled.css` ${index.createFlatBoxStyles()} outline: 2px solid ${({ theme }) => theme.flatDark}; background: ${({ theme }) => theme.flatLight}; ` : styled.css` ${index.createBoxStyles()} ${index.createBorderStyles()} &:focus { outline: none; } `} ${({ $disabled, theme }) => $disabled && index.createHatchedBackground({ mainColor: theme.material, secondaryColor: theme.borderLightest })} `; const tickHeight = 6; const Tick = styled__default["default"].span` display: inline-block; position: absolute; ${({ orientation }) => orientation === "vertical" ? styled.css` right: ${-tickHeight - 2}px; bottom: 0px; transform: translateY(1px); width: ${tickHeight}px; border-bottom: 2px solid ${({ theme }) => theme.materialText}; ` : styled.css` bottom: ${-tickHeight}px; height: ${tickHeight}px; transform: translateX(-1px); border-left: 1px solid ${({ theme }) => theme.materialText}; border-right: 1px solid ${({ theme }) => theme.materialText}; `} color: ${({ theme }) => theme.materialText}; ${({ $disabled, theme }) => $disabled && styled.css` ${index.createDisabledTextStyles()} box-shadow: 1px 1px 0px ${theme.materialTextDisabledShadow}; border-color: ${theme.materialTextDisabled}; `} `; const Mark = styled__default["default"].div` position: absolute; bottom: 0; left: 0; line-height: 1; font-size: 0.875rem; ${({ orientation }) => orientation === "vertical" ? styled.css` transform: translate(${tickHeight + 2}px, ${tickHeight + 1}px); ` : styled.css` transform: translate(-0.5ch, calc(100% + 2px)); `} `; const Slider = React.forwardRef(({ defaultValue, disabled = false, marks: marksProp = false, max = 100, min = 0, name, onChange, onChangeCommitted, onMouseDown, orientation = "horizontal", size = "100%", step = 1, value, variant = "default", ...otherProps }, ref) => { const Groove = variant === "flat" ? StyledFlatGroove : StyledGroove; const vertical = orientation === "vertical"; const [valueDerived = min, setValueState] = useControlledOrUncontrolled({ defaultValue, onChange: onChange !== null && onChange !== void 0 ? onChange : onChangeCommitted, value }); const { isFocusVisible, onBlurVisible, ref: focusVisibleRef } = useIsFocusVisible.useIsFocusVisible(); const [focusVisible, setFocusVisible] = React.useState(false); const sliderRef = React.useRef(); const thumbRef = React.useRef(null); const handleFocusRef = useForkRef(focusVisibleRef, sliderRef); const handleRef = useForkRef(ref, handleFocusRef); const handleFocus = useEventCallback((event) => { if (isFocusVisible(event)) { setFocusVisible(true); } }); const handleBlur = useEventCallback(() => { if (focusVisible !== false) { setFocusVisible(false); onBlurVisible(); } }); const touchId = React.useRef(); const marks = React.useMemo(() => marksProp === true && Number.isFinite(step) ? [...Array(Math.round((max - min) / step) + 1)].map((_, index) => ({ label: void 0, value: min + step * index })) : Array.isArray(marksProp) ? marksProp : [], [marksProp, max, min, step]); const handleKeyDown = useEventCallback((event) => { const tenPercents = (max - min) / 10; const marksValues = marks.map((mark) => mark.value); const marksIndex = marksValues.indexOf(valueDerived); let newValue = 0; switch (event.key) { case "Home": newValue = min; break; case "End": newValue = max; break; case "PageUp": if (step) { newValue = valueDerived + tenPercents; } break; case "PageDown": if (step) { newValue = valueDerived - tenPercents; } break; case "ArrowRight": case "ArrowUp": if (step) { newValue = valueDerived + step; } else { newValue = marksValues[marksIndex + 1] || marksValues[marksValues.length - 1]; } break; case "ArrowLeft": case "ArrowDown": if (step) { newValue = valueDerived - step; } else { newValue = marksValues[marksIndex - 1] || marksValues[0]; } break; default: return; } event.preventDefault(); if (step) { newValue = index$1.roundValueToStep(newValue, step, min); } newValue = index$1.clamp(newValue, min, max); setValueState(newValue); setFocusVisible(true); onChange === null || onChange === void 0 ? void 0 : onChange(newValue); onChangeCommitted === null || onChangeCommitted === void 0 ? void 0 : onChangeCommitted(newValue); }); const getNewValue = React.useCallback((finger) => { if (!sliderRef.current) { return 0; } const rect = sliderRef.current.getBoundingClientRect(); let percent; if (vertical) { percent = (rect.bottom - finger.y) / rect.height; } else { percent = (finger.x - rect.left) / rect.width; } let newValue; newValue = percentToValue(percent, min, max); if (step) { newValue = index$1.roundValueToStep(newValue, step, min); } else { const marksValues = marks.map((mark) => mark.value); const closestIndex = findClosest(marksValues, newValue); newValue = marksValues[closestIndex]; } newValue = index$1.clamp(newValue, min, max); return newValue; }, [marks, max, min, step, vertical]); const handleTouchMove = useEventCallback((event) => { var _a; const finger = trackFinger(event, touchId.current); if (!finger) { return; } const newValue = getNewValue(finger); (_a = thumbRef.current) === null || _a === void 0 ? void 0 : _a.focus(); setValueState(newValue); setFocusVisible(true); onChange === null || onChange === void 0 ? void 0 : onChange(newValue); }); const handleTouchEnd = useEventCallback((event) => { const finger = trackFinger(event, touchId.current); if (!finger) { return; } const newValue = getNewValue(finger); onChangeCommitted === null || onChangeCommitted === void 0 ? void 0 : onChangeCommitted(newValue); touchId.current = void 0; const doc = ownerDocument(sliderRef.current); doc.removeEventListener("mousemove", handleTouchMove); doc.removeEventListener("mouseup", handleTouchEnd); doc.removeEventListener("touchmove", handleTouchMove); doc.removeEventListener("touchend", handleTouchEnd); }); const handleMouseDown = useEventCallback((event) => { var _a; onMouseDown === null || onMouseDown === void 0 ? void 0 : onMouseDown(event); event.preventDefault(); (_a = thumbRef.current) === null || _a === void 0 ? void 0 : _a.focus(); setFocusVisible(true); const finger = trackFinger(event, touchId.current); if (finger) { const newValue = getNewValue(finger); setValueState(newValue); onChange === null || onChange === void 0 ? void 0 : onChange(newValue); } const doc = ownerDocument(sliderRef.current); doc.addEventListener("mousemove", handleTouchMove); doc.addEventListener("mouseup", handleTouchEnd); }); const handleTouchStart = useEventCallback((event) => { var _a; event.preventDefault(); const touch = event.changedTouches[0]; if (touch != null) { touchId.current = touch.identifier; } (_a = thumbRef.current) === null || _a === void 0 ? void 0 : _a.focus(); setFocusVisible(true); const finger = trackFinger(event, touchId.current); if (finger) { const newValue = getNewValue(finger); setValueState(newValue); onChange === null || onChange === void 0 ? void 0 : onChange(newValue); } const doc = ownerDocument(sliderRef.current); doc.addEventListener("touchmove", handleTouchMove); doc.addEventListener("touchend", handleTouchEnd); }); React.useEffect(() => { const { current: slider } = sliderRef; slider === null || slider === void 0 ? void 0 : slider.addEventListener("touchstart", handleTouchStart); const doc = ownerDocument(slider); return () => { slider === null || slider === void 0 ? void 0 : slider.removeEventListener("touchstart", handleTouchStart); doc.removeEventListener("mousemove", handleTouchMove); doc.removeEventListener("mouseup", handleTouchEnd); doc.removeEventListener("touchmove", handleTouchMove); doc.removeEventListener("touchend", handleTouchEnd); }; }, [handleTouchEnd, handleTouchMove, handleTouchStart]); return React__default["default"].createElement( Wrapper, { "$disabled": disabled, hasMarks: Boolean(marks.length), isFocused: focusVisible, onMouseDown: handleMouseDown, orientation, ref: handleRef, size: index$1.getSize(size), ...otherProps }, React__default["default"].createElement("input", { disabled, name, type: "hidden", value: valueDerived !== null && valueDerived !== void 0 ? valueDerived : 0 }), marks && marks.map((m) => React__default["default"].createElement(Tick, { "$disabled": disabled, "data-testid": "tick", key: m.value / (max - min) * 100, orientation, style: { [vertical ? "bottom" : "left"]: `${(m.value - min) / (max - min) * 100}%` } }, m.label && React__default["default"].createElement(Mark, { "aria-hidden": true, "data-testid": "mark", orientation }, m.label))), React__default["default"].createElement(Groove, { orientation, variant }), React__default["default"].createElement(Thumb, { "$disabled": disabled, "aria-disabled": disabled ? true : void 0, "aria-orientation": orientation, "aria-valuemax": max, "aria-valuemin": min, "aria-valuenow": valueDerived, onBlur: handleBlur, onFocus: handleFocus, onKeyDown: handleKeyDown, orientation, ref: thumbRef, role: "slider", style: { [vertical ? "bottom" : "left"]: `${(vertical ? -100 : 0) + 100 * (valueDerived - min) / (max - min)}%` }, tabIndex: disabled ? void 0 : 0, variant }) ); }); Slider.displayName = "Slider"; exports.Slider = Slider;