ivt
Version:
Ivt Components Library
595 lines (591 loc) • 23.3 kB
JavaScript
import * as React from 'react';
import { c as clamp } from '../chunks/index-GgS4otoq.mjs';
import { c as composeEventHandlers } from '../chunks/index-Bl-WJHvp.mjs';
import { u as useComposedRefs } from '../chunks/index-1tQVI0Jh.mjs';
import { c as createContextScope } from '../chunks/index-DT8WgpCS.mjs';
import { u as useControllableState } from '../chunks/index-D4FMFHi9.mjs';
import { u as useDirection } from '../chunks/index-CGLjQEjG.mjs';
import { u as usePrevious } from '../chunks/index-FL3PKmOS.mjs';
import { u as useSize } from '../chunks/index-DmY774z-.mjs';
import { P as Primitive } from '../chunks/index-DgKlJYZP.mjs';
import { createCollection } from '@radix-ui/react-collection';
import { jsx, jsxs } from 'react/jsx-runtime';
import { c as cn } from '../chunks/utils-05LlW3Cl.mjs';
import '../chunks/index-DKOlG3mh.mjs';
import 'react-dom';
import '@radix-ui/react-slot';
import '../chunks/bundle-mjs-BYcyWisL.mjs';
var PAGE_KEYS = [
"PageUp",
"PageDown"
];
var ARROW_KEYS = [
"ArrowUp",
"ArrowDown",
"ArrowLeft",
"ArrowRight"
];
var BACK_KEYS = {
"from-left": [
"Home",
"PageDown",
"ArrowDown",
"ArrowLeft"
],
"from-right": [
"Home",
"PageDown",
"ArrowDown",
"ArrowRight"
],
"from-bottom": [
"Home",
"PageDown",
"ArrowDown",
"ArrowLeft"
],
"from-top": [
"Home",
"PageDown",
"ArrowUp",
"ArrowLeft"
]
};
var SLIDER_NAME = "Slider";
var [Collection, useCollection, createCollectionScope] = createCollection(SLIDER_NAME);
var [createSliderContext] = createContextScope(SLIDER_NAME, [
createCollectionScope
]);
var [SliderProvider, useSliderContext] = createSliderContext(SLIDER_NAME);
var Slider$1 = React.forwardRef((props, forwardedRef)=>{
const { name, min = 0, max = 100, step = 1, orientation = "horizontal", disabled = false, minStepsBetweenThumbs = 0, defaultValue = [
min
], value, onValueChange = ()=>{}, onValueCommit = ()=>{}, inverted = false, form, ...sliderProps } = props;
const thumbRefs = React.useRef(/* @__PURE__ */ new Set());
const valueIndexToChangeRef = React.useRef(0);
const isHorizontal = orientation === "horizontal";
const SliderOrientation = isHorizontal ? SliderHorizontal : SliderVertical;
const [values = [], setValues] = useControllableState({
prop: value,
defaultProp: defaultValue,
onChange: (value2)=>{
const thumbs = [
...thumbRefs.current
];
thumbs[valueIndexToChangeRef.current]?.focus();
onValueChange(value2);
}
});
const valuesBeforeSlideStartRef = React.useRef(values);
function handleSlideStart(value2) {
const closestIndex = getClosestValueIndex(values, value2);
updateValues(value2, closestIndex);
}
function handleSlideMove(value2) {
updateValues(value2, valueIndexToChangeRef.current);
}
function handleSlideEnd() {
const prevValue = valuesBeforeSlideStartRef.current[valueIndexToChangeRef.current];
const nextValue = values[valueIndexToChangeRef.current];
const hasChanged = nextValue !== prevValue;
if (hasChanged) onValueCommit(values);
}
function updateValues(value2, atIndex, { commit } = {
commit: false
}) {
const decimalCount = getDecimalCount(step);
const snapToStep = roundValue(Math.round((value2 - min) / step) * step + min, decimalCount);
const nextValue = clamp(snapToStep, [
min,
max
]);
setValues((prevValues = [])=>{
const nextValues = getNextSortedValues(prevValues, nextValue, atIndex);
if (hasMinStepsBetweenValues(nextValues, minStepsBetweenThumbs * step)) {
valueIndexToChangeRef.current = nextValues.indexOf(nextValue);
const hasChanged = String(nextValues) !== String(prevValues);
if (hasChanged && commit) onValueCommit(nextValues);
return hasChanged ? nextValues : prevValues;
} else {
return prevValues;
}
});
}
return /* @__PURE__ */ jsx(SliderProvider, {
scope: props.__scopeSlider,
name,
disabled,
min,
max,
valueIndexToChangeRef,
thumbs: thumbRefs.current,
values,
orientation,
form,
children: /* @__PURE__ */ jsx(Collection.Provider, {
scope: props.__scopeSlider,
children: /* @__PURE__ */ jsx(Collection.Slot, {
scope: props.__scopeSlider,
children: /* @__PURE__ */ jsx(SliderOrientation, {
"aria-disabled": disabled,
"data-disabled": disabled ? "" : void 0,
...sliderProps,
ref: forwardedRef,
onPointerDown: composeEventHandlers(sliderProps.onPointerDown, ()=>{
if (!disabled) valuesBeforeSlideStartRef.current = values;
}),
min,
max,
inverted,
onSlideStart: disabled ? void 0 : handleSlideStart,
onSlideMove: disabled ? void 0 : handleSlideMove,
onSlideEnd: disabled ? void 0 : handleSlideEnd,
onHomeKeyDown: ()=>!disabled && updateValues(min, 0, {
commit: true
}),
onEndKeyDown: ()=>!disabled && updateValues(max, values.length - 1, {
commit: true
}),
onStepKeyDown: ({ event, direction: stepDirection })=>{
if (!disabled) {
const isPageKey = PAGE_KEYS.includes(event.key);
const isSkipKey = isPageKey || event.shiftKey && ARROW_KEYS.includes(event.key);
const multiplier = isSkipKey ? 10 : 1;
const atIndex = valueIndexToChangeRef.current;
const value2 = values[atIndex];
const stepInDirection = step * multiplier * stepDirection;
updateValues(value2 + stepInDirection, atIndex, {
commit: true
});
}
}
})
})
})
});
});
Slider$1.displayName = SLIDER_NAME;
var [SliderOrientationProvider, useSliderOrientationContext] = createSliderContext(SLIDER_NAME, {
startEdge: "left",
endEdge: "right",
size: "width",
direction: 1
});
var SliderHorizontal = React.forwardRef((props, forwardedRef)=>{
const { min, max, dir, inverted, onSlideStart, onSlideMove, onSlideEnd, onStepKeyDown, ...sliderProps } = props;
const [slider, setSlider] = React.useState(null);
const composedRefs = useComposedRefs(forwardedRef, (node)=>setSlider(node));
const rectRef = React.useRef(void 0);
const direction = useDirection(dir);
const isDirectionLTR = direction === "ltr";
const isSlidingFromLeft = isDirectionLTR && !inverted || !isDirectionLTR && inverted;
function getValueFromPointer(pointerPosition) {
const rect = rectRef.current || slider.getBoundingClientRect();
const input = [
0,
rect.width
];
const output = isSlidingFromLeft ? [
min,
max
] : [
max,
min
];
const value = linearScale(input, output);
rectRef.current = rect;
return value(pointerPosition - rect.left);
}
return /* @__PURE__ */ jsx(SliderOrientationProvider, {
scope: props.__scopeSlider,
startEdge: isSlidingFromLeft ? "left" : "right",
endEdge: isSlidingFromLeft ? "right" : "left",
direction: isSlidingFromLeft ? 1 : -1,
size: "width",
children: /* @__PURE__ */ jsx(SliderImpl, {
dir: direction,
"data-orientation": "horizontal",
...sliderProps,
ref: composedRefs,
style: {
...sliderProps.style,
["--radix-slider-thumb-transform"]: "translateX(-50%)"
},
onSlideStart: (event)=>{
const value = getValueFromPointer(event.clientX);
onSlideStart?.(value);
},
onSlideMove: (event)=>{
const value = getValueFromPointer(event.clientX);
onSlideMove?.(value);
},
onSlideEnd: ()=>{
rectRef.current = void 0;
onSlideEnd?.();
},
onStepKeyDown: (event)=>{
const slideDirection = isSlidingFromLeft ? "from-left" : "from-right";
const isBackKey = BACK_KEYS[slideDirection].includes(event.key);
onStepKeyDown?.({
event,
direction: isBackKey ? -1 : 1
});
}
})
});
});
var SliderVertical = React.forwardRef((props, forwardedRef)=>{
const { min, max, inverted, onSlideStart, onSlideMove, onSlideEnd, onStepKeyDown, ...sliderProps } = props;
const sliderRef = React.useRef(null);
const ref = useComposedRefs(forwardedRef, sliderRef);
const rectRef = React.useRef(void 0);
const isSlidingFromBottom = !inverted;
function getValueFromPointer(pointerPosition) {
const rect = rectRef.current || sliderRef.current.getBoundingClientRect();
const input = [
0,
rect.height
];
const output = isSlidingFromBottom ? [
max,
min
] : [
min,
max
];
const value = linearScale(input, output);
rectRef.current = rect;
return value(pointerPosition - rect.top);
}
return /* @__PURE__ */ jsx(SliderOrientationProvider, {
scope: props.__scopeSlider,
startEdge: isSlidingFromBottom ? "bottom" : "top",
endEdge: isSlidingFromBottom ? "top" : "bottom",
size: "height",
direction: isSlidingFromBottom ? 1 : -1,
children: /* @__PURE__ */ jsx(SliderImpl, {
"data-orientation": "vertical",
...sliderProps,
ref,
style: {
...sliderProps.style,
["--radix-slider-thumb-transform"]: "translateY(50%)"
},
onSlideStart: (event)=>{
const value = getValueFromPointer(event.clientY);
onSlideStart?.(value);
},
onSlideMove: (event)=>{
const value = getValueFromPointer(event.clientY);
onSlideMove?.(value);
},
onSlideEnd: ()=>{
rectRef.current = void 0;
onSlideEnd?.();
},
onStepKeyDown: (event)=>{
const slideDirection = isSlidingFromBottom ? "from-bottom" : "from-top";
const isBackKey = BACK_KEYS[slideDirection].includes(event.key);
onStepKeyDown?.({
event,
direction: isBackKey ? -1 : 1
});
}
})
});
});
var SliderImpl = React.forwardRef((props, forwardedRef)=>{
const { __scopeSlider, onSlideStart, onSlideMove, onSlideEnd, onHomeKeyDown, onEndKeyDown, onStepKeyDown, ...sliderProps } = props;
const context = useSliderContext(SLIDER_NAME, __scopeSlider);
return /* @__PURE__ */ jsx(Primitive.span, {
...sliderProps,
ref: forwardedRef,
onKeyDown: composeEventHandlers(props.onKeyDown, (event)=>{
if (event.key === "Home") {
onHomeKeyDown(event);
event.preventDefault();
} else if (event.key === "End") {
onEndKeyDown(event);
event.preventDefault();
} else if (PAGE_KEYS.concat(ARROW_KEYS).includes(event.key)) {
onStepKeyDown(event);
event.preventDefault();
}
}),
onPointerDown: composeEventHandlers(props.onPointerDown, (event)=>{
const target = event.target;
target.setPointerCapture(event.pointerId);
event.preventDefault();
if (context.thumbs.has(target)) {
target.focus();
} else {
onSlideStart(event);
}
}),
onPointerMove: composeEventHandlers(props.onPointerMove, (event)=>{
const target = event.target;
if (target.hasPointerCapture(event.pointerId)) onSlideMove(event);
}),
onPointerUp: composeEventHandlers(props.onPointerUp, (event)=>{
const target = event.target;
if (target.hasPointerCapture(event.pointerId)) {
target.releasePointerCapture(event.pointerId);
onSlideEnd(event);
}
})
});
});
var TRACK_NAME = "SliderTrack";
var SliderTrack = React.forwardRef((props, forwardedRef)=>{
const { __scopeSlider, ...trackProps } = props;
const context = useSliderContext(TRACK_NAME, __scopeSlider);
return /* @__PURE__ */ jsx(Primitive.span, {
"data-disabled": context.disabled ? "" : void 0,
"data-orientation": context.orientation,
...trackProps,
ref: forwardedRef
});
});
SliderTrack.displayName = TRACK_NAME;
var RANGE_NAME = "SliderRange";
var SliderRange = React.forwardRef((props, forwardedRef)=>{
const { __scopeSlider, ...rangeProps } = props;
const context = useSliderContext(RANGE_NAME, __scopeSlider);
const orientation = useSliderOrientationContext(RANGE_NAME, __scopeSlider);
const ref = React.useRef(null);
const composedRefs = useComposedRefs(forwardedRef, ref);
const valuesCount = context.values.length;
const percentages = context.values.map((value)=>convertValueToPercentage(value, context.min, context.max));
const offsetStart = valuesCount > 1 ? Math.min(...percentages) : 0;
const offsetEnd = 100 - Math.max(...percentages);
return /* @__PURE__ */ jsx(Primitive.span, {
"data-orientation": context.orientation,
"data-disabled": context.disabled ? "" : void 0,
...rangeProps,
ref: composedRefs,
style: {
...props.style,
[orientation.startEdge]: offsetStart + "%",
[orientation.endEdge]: offsetEnd + "%"
}
});
});
SliderRange.displayName = RANGE_NAME;
var THUMB_NAME = "SliderThumb";
var SliderThumb = React.forwardRef((props, forwardedRef)=>{
const getItems = useCollection(props.__scopeSlider);
const [thumb, setThumb] = React.useState(null);
const composedRefs = useComposedRefs(forwardedRef, (node)=>setThumb(node));
const index = React.useMemo(()=>thumb ? getItems().findIndex((item)=>item.ref.current === thumb) : -1, [
getItems,
thumb
]);
return /* @__PURE__ */ jsx(SliderThumbImpl, {
...props,
ref: composedRefs,
index
});
});
var SliderThumbImpl = React.forwardRef((props, forwardedRef)=>{
const { __scopeSlider, index, name, ...thumbProps } = props;
const context = useSliderContext(THUMB_NAME, __scopeSlider);
const orientation = useSliderOrientationContext(THUMB_NAME, __scopeSlider);
const [thumb, setThumb] = React.useState(null);
const composedRefs = useComposedRefs(forwardedRef, (node)=>setThumb(node));
const isFormControl = thumb ? context.form || !!thumb.closest("form") : true;
const size = useSize(thumb);
const value = context.values[index];
const percent = value === void 0 ? 0 : convertValueToPercentage(value, context.min, context.max);
const label = getLabel(index, context.values.length);
const orientationSize = size?.[orientation.size];
const thumbInBoundsOffset = orientationSize ? getThumbInBoundsOffset(orientationSize, percent, orientation.direction) : 0;
React.useEffect(()=>{
if (thumb) {
context.thumbs.add(thumb);
return ()=>{
context.thumbs.delete(thumb);
};
}
}, [
thumb,
context.thumbs
]);
return /* @__PURE__ */ jsxs("span", {
style: {
transform: "var(--radix-slider-thumb-transform)",
position: "absolute",
[orientation.startEdge]: `calc(${percent}% + ${thumbInBoundsOffset}px)`
},
children: [
/* @__PURE__ */ jsx(Collection.ItemSlot, {
scope: props.__scopeSlider,
children: /* @__PURE__ */ jsx(Primitive.span, {
role: "slider",
"aria-label": props["aria-label"] || label,
"aria-valuemin": context.min,
"aria-valuenow": value,
"aria-valuemax": context.max,
"aria-orientation": context.orientation,
"data-orientation": context.orientation,
"data-disabled": context.disabled ? "" : void 0,
tabIndex: context.disabled ? void 0 : 0,
...thumbProps,
ref: composedRefs,
style: value === void 0 ? {
display: "none"
} : props.style,
onFocus: composeEventHandlers(props.onFocus, ()=>{
context.valueIndexToChangeRef.current = index;
})
})
}),
isFormControl && /* @__PURE__ */ jsx(SliderBubbleInput, {
name: name ?? (context.name ? context.name + (context.values.length > 1 ? "[]" : "") : void 0),
form: context.form,
value
}, index)
]
});
});
SliderThumb.displayName = THUMB_NAME;
var BUBBLE_INPUT_NAME = "RadioBubbleInput";
var SliderBubbleInput = React.forwardRef(({ __scopeSlider, value, ...props }, forwardedRef)=>{
const ref = React.useRef(null);
const composedRefs = useComposedRefs(ref, forwardedRef);
const prevValue = usePrevious(value);
React.useEffect(()=>{
const input = ref.current;
if (!input) return;
const inputProto = window.HTMLInputElement.prototype;
const descriptor = Object.getOwnPropertyDescriptor(inputProto, "value");
const setValue = descriptor.set;
if (prevValue !== value && setValue) {
const event = new Event("input", {
bubbles: true
});
setValue.call(input, value);
input.dispatchEvent(event);
}
}, [
prevValue,
value
]);
return /* @__PURE__ */ jsx(Primitive.input, {
style: {
display: "none"
},
...props,
ref: composedRefs,
defaultValue: value
});
});
SliderBubbleInput.displayName = BUBBLE_INPUT_NAME;
function getNextSortedValues(prevValues = [], nextValue, atIndex) {
const nextValues = [
...prevValues
];
nextValues[atIndex] = nextValue;
return nextValues.sort((a, b)=>a - b);
}
function convertValueToPercentage(value, min, max) {
const maxSteps = max - min;
const percentPerStep = 100 / maxSteps;
const percentage = percentPerStep * (value - min);
return clamp(percentage, [
0,
100
]);
}
function getLabel(index, totalValues) {
if (totalValues > 2) {
return `Value ${index + 1} of ${totalValues}`;
} else if (totalValues === 2) {
return [
"Minimum",
"Maximum"
][index];
} else {
return void 0;
}
}
function getClosestValueIndex(values, nextValue) {
if (values.length === 1) return 0;
const distances = values.map((value)=>Math.abs(value - nextValue));
const closestDistance = Math.min(...distances);
return distances.indexOf(closestDistance);
}
function getThumbInBoundsOffset(width, left, direction) {
const halfWidth = width / 2;
const halfPercent = 50;
const offset = linearScale([
0,
halfPercent
], [
0,
halfWidth
]);
return (halfWidth - offset(left) * direction) * direction;
}
function getStepsBetweenValues(values) {
return values.slice(0, -1).map((value, index)=>values[index + 1] - value);
}
function hasMinStepsBetweenValues(values, minStepsBetweenValues) {
if (minStepsBetweenValues > 0) {
const stepsBetweenValues = getStepsBetweenValues(values);
const actualMinStepsBetweenValues = Math.min(...stepsBetweenValues);
return actualMinStepsBetweenValues >= minStepsBetweenValues;
}
return true;
}
function linearScale(input, output) {
return (value)=>{
if (input[0] === input[1] || output[0] === output[1]) return output[0];
const ratio = (output[1] - output[0]) / (input[1] - input[0]);
return output[0] + ratio * (value - input[0]);
};
}
function getDecimalCount(value) {
return (String(value).split(".")[1] || "").length;
}
function roundValue(value, decimalCount) {
const rounder = Math.pow(10, decimalCount);
return Math.round(value * rounder) / rounder;
}
var Root = Slider$1;
var Track = SliderTrack;
var Range = SliderRange;
var Thumb = SliderThumb;
function Slider({ className, defaultValue, value, min = 0, max = 100, ...props }) {
const _values = React.useMemo(()=>Array.isArray(value) ? value : Array.isArray(defaultValue) ? defaultValue : [
min,
max
], [
value,
defaultValue,
min,
max
]);
return /*#__PURE__*/ React.createElement(Root, {
"data-slot": "slider",
defaultValue: defaultValue,
value: value,
min: min,
max: max,
className: cn("relative flex w-full touch-none items-center select-none data-[disabled]:opacity-50 data-[orientation=vertical]:h-full data-[orientation=vertical]:min-h-44 data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col", className),
...props
}, /*#__PURE__*/ React.createElement(Track, {
"data-slot": "slider-track",
className: cn("bg-muted relative grow overflow-hidden rounded-full data-[orientation=horizontal]:h-1.5 data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-1.5")
}, /*#__PURE__*/ React.createElement(Range, {
"data-slot": "slider-range",
className: cn("bg-primary absolute data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full")
})), Array.from({
length: _values.length
}, (_, index)=>/*#__PURE__*/ React.createElement(Thumb, {
"data-slot": "slider-thumb",
key: index,
className: "border-primary ring-ring/50 block size-4 shrink-0 rounded-full border bg-white shadow-sm transition-[color,box-shadow] hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50"
})));
}
export { Slider };
//# sourceMappingURL=index.mjs.map