@tamagui/slider 1.88.13 → 1.89.0-1706308641099

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020 Nate Wienert
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,439 @@
1
+ import { composeRefs, useComposedRefs } from "@tamagui/compose-refs";
2
+ import { isClient, isWeb } from "@tamagui/constants";
3
+ import { createShallowSetState, getTokens, getVariableValue, styled } from "@tamagui/core";
4
+ import { getSize } from "@tamagui/get-token";
5
+ import { withStaticProperties } from "@tamagui/helpers";
6
+ import { clamp, composeEventHandlers } from "@tamagui/helpers";
7
+ import { ThemeableStack } from "@tamagui/stacks";
8
+ import { useControllableState } from "@tamagui/use-controllable-state";
9
+ import { useDirection } from "@tamagui/use-direction";
10
+ import * as React from "react";
11
+ import { ARROW_KEYS, BACK_KEYS, PAGE_KEYS, SLIDER_NAME, SliderOrientationProvider, SliderProvider, useSliderContext, useSliderOrientationContext } from "./constants.mjs";
12
+ import { convertValueToPercentage, getClosestValueIndex, getDecimalCount, getLabel, getNextSortedValues, getThumbInBoundsOffset, hasMinStepsBetweenValues, linearScale, roundValue } from "./helpers.mjs";
13
+ import { SliderFrame, SliderImpl } from "./SliderImpl.mjs";
14
+ import { jsx } from "react/jsx-runtime";
15
+ const SliderHorizontal = React.forwardRef((props, forwardedRef) => {
16
+ const {
17
+ min,
18
+ max,
19
+ dir,
20
+ onSlideStart,
21
+ onSlideMove,
22
+ onStepKeyDown,
23
+ onSlideEnd,
24
+ ...sliderProps
25
+ } = props,
26
+ direction = useDirection(dir),
27
+ isDirectionLTR = direction === "ltr",
28
+ sliderRef = React.useRef(null),
29
+ [state, setState_] = React.useState(() => ({
30
+ size: 0,
31
+ offset: 0
32
+ })),
33
+ setState = createShallowSetState(setState_);
34
+ function getValueFromPointer(pointerPosition) {
35
+ const input = [0, state.size];
36
+ return linearScale(input, isDirectionLTR ? [min, max] : [max, min])(pointerPosition);
37
+ }
38
+ const measure = () => {
39
+ sliderRef.current?.measure((_x, _y, width, _height, pageX, _pageY) => {
40
+ setState({
41
+ size: width,
42
+ offset: pageX
43
+ });
44
+ });
45
+ };
46
+ return isClient && useOnDebouncedWindowResize(measure), /* @__PURE__ */jsx(SliderOrientationProvider, {
47
+ scope: props.__scopeSlider,
48
+ startEdge: isDirectionLTR ? "left" : "right",
49
+ endEdge: isDirectionLTR ? "right" : "left",
50
+ direction: isDirectionLTR ? 1 : -1,
51
+ sizeProp: "width",
52
+ size: state.size,
53
+ children: /* @__PURE__ */jsx(SliderImpl, {
54
+ ref: composeRefs(forwardedRef, sliderRef),
55
+ dir: direction,
56
+ ...sliderProps,
57
+ orientation: "horizontal",
58
+ onLayout: measure,
59
+ onSlideStart: (event, target) => {
60
+ const value = getValueFromPointer(event.nativeEvent.locationX);
61
+ value && onSlideStart?.(value, target, event);
62
+ },
63
+ onSlideMove: event => {
64
+ const value = getValueFromPointer(event.nativeEvent.pageX - state.offset);
65
+ value && onSlideMove?.(value, event);
66
+ },
67
+ onSlideEnd: event => {
68
+ const value = getValueFromPointer(event.nativeEvent.pageX - state.offset);
69
+ value && onSlideEnd?.(event, value);
70
+ },
71
+ onStepKeyDown: event => {
72
+ const isBackKey = BACK_KEYS[direction].includes(event.key);
73
+ onStepKeyDown?.({
74
+ event,
75
+ direction: isBackKey ? -1 : 1
76
+ });
77
+ }
78
+ })
79
+ });
80
+ });
81
+ function useOnDebouncedWindowResize(callback, amt = 200) {
82
+ React.useEffect(() => {
83
+ let last;
84
+ const onResize = () => {
85
+ clearTimeout(last), last = setTimeout(callback, amt);
86
+ };
87
+ return window.addEventListener("resize", onResize), () => {
88
+ clearTimeout(last), window.removeEventListener("resize", onResize);
89
+ };
90
+ }, []);
91
+ }
92
+ const SliderVertical = React.forwardRef((props, forwardedRef) => {
93
+ const {
94
+ min,
95
+ max,
96
+ onSlideStart,
97
+ onSlideMove,
98
+ onStepKeyDown,
99
+ onSlideEnd,
100
+ ...sliderProps
101
+ } = props,
102
+ [state, setState_] = React.useState(() => ({
103
+ size: 0,
104
+ offset: 0
105
+ })),
106
+ setState = createShallowSetState(setState_),
107
+ sliderRef = React.useRef(null);
108
+ function getValueFromPointer(pointerPosition) {
109
+ const input = [0, state.size];
110
+ return linearScale(input, [max, min])(pointerPosition);
111
+ }
112
+ const measure = () => {
113
+ sliderRef.current?.measure((_x, _y, _width, height, _pageX, pageY) => {
114
+ setState({
115
+ size: height,
116
+ offset: pageY
117
+ });
118
+ });
119
+ };
120
+ return isClient && useOnDebouncedWindowResize(measure), /* @__PURE__ */jsx(SliderOrientationProvider, {
121
+ scope: props.__scopeSlider,
122
+ startEdge: "bottom",
123
+ endEdge: "top",
124
+ sizeProp: "height",
125
+ size: state.size,
126
+ direction: 1,
127
+ children: /* @__PURE__ */jsx(SliderImpl, {
128
+ ref: composeRefs(forwardedRef, sliderRef),
129
+ ...sliderProps,
130
+ orientation: "vertical",
131
+ onLayout: measure,
132
+ onSlideStart: (event, target) => {
133
+ const value = getValueFromPointer(event.nativeEvent.locationY);
134
+ value && onSlideStart?.(value, target, event);
135
+ },
136
+ onSlideMove: event => {
137
+ const value = getValueFromPointer(event.nativeEvent.pageY - state.offset);
138
+ value && onSlideMove?.(value, event);
139
+ },
140
+ onSlideEnd: event => {
141
+ const value = getValueFromPointer(event.nativeEvent.pageY - state.offset);
142
+ onSlideEnd?.(event, value);
143
+ },
144
+ onStepKeyDown: event => {
145
+ const isBackKey = BACK_KEYS.ltr.includes(event.key);
146
+ onStepKeyDown?.({
147
+ event,
148
+ direction: isBackKey ? -1 : 1
149
+ });
150
+ }
151
+ })
152
+ });
153
+ }),
154
+ TRACK_NAME = "SliderTrack",
155
+ SliderTrackFrame = styled(SliderFrame, {
156
+ name: "SliderTrack",
157
+ variants: {
158
+ unstyled: {
159
+ false: {
160
+ height: "100%",
161
+ width: "100%",
162
+ backgroundColor: "$background",
163
+ position: "relative",
164
+ borderRadius: 1e5,
165
+ overflow: "hidden"
166
+ }
167
+ }
168
+ },
169
+ defaultVariants: {
170
+ unstyled: process.env.TAMAGUI_HEADLESS === "1"
171
+ }
172
+ }),
173
+ SliderTrack = React.forwardRef((props, forwardedRef) => {
174
+ const {
175
+ __scopeSlider,
176
+ ...trackProps
177
+ } = props,
178
+ context = useSliderContext(TRACK_NAME, __scopeSlider);
179
+ return /* @__PURE__ */jsx(SliderTrackFrame, {
180
+ "data-disabled": context.disabled ? "" : void 0,
181
+ "data-orientation": context.orientation,
182
+ orientation: context.orientation,
183
+ size: context.size,
184
+ ...trackProps,
185
+ ref: forwardedRef
186
+ });
187
+ });
188
+ SliderTrack.displayName = TRACK_NAME;
189
+ const RANGE_NAME = "SliderTrackActive",
190
+ SliderTrackActiveFrame = styled(SliderFrame, {
191
+ name: "SliderTrackActive",
192
+ backgroundColor: "$background",
193
+ position: "absolute"
194
+ }),
195
+ SliderTrackActive = React.forwardRef((props, forwardedRef) => {
196
+ const {
197
+ __scopeSlider,
198
+ ...rangeProps
199
+ } = props,
200
+ context = useSliderContext(RANGE_NAME, __scopeSlider),
201
+ orientation = useSliderOrientationContext(RANGE_NAME, __scopeSlider),
202
+ ref = React.useRef(null),
203
+ composedRefs = useComposedRefs(forwardedRef, ref),
204
+ valuesCount = context.values.length,
205
+ percentages = context.values.map(value => convertValueToPercentage(value, context.min, context.max)),
206
+ offsetStart = valuesCount > 1 ? Math.min(...percentages) : 0,
207
+ offsetEnd = 100 - Math.max(...percentages);
208
+ return /* @__PURE__ */jsx(SliderTrackActiveFrame, {
209
+ orientation: context.orientation,
210
+ "data-orientation": context.orientation,
211
+ "data-disabled": context.disabled ? "" : void 0,
212
+ size: context.size,
213
+ animateOnly: ["left", "top", "right", "bottom"],
214
+ ...rangeProps,
215
+ ref: composedRefs,
216
+ [orientation.startEdge]: `${offsetStart}%`,
217
+ [orientation.endEdge]: `${offsetEnd}%`,
218
+ ...(orientation.sizeProp === "width" ? {
219
+ height: "100%"
220
+ } : {
221
+ left: 0,
222
+ right: 0
223
+ })
224
+ });
225
+ });
226
+ SliderTrackActive.displayName = RANGE_NAME;
227
+ const THUMB_NAME = "SliderThumb",
228
+ getThumbSize = val => {
229
+ const tokens = getTokens(),
230
+ size = typeof val == "number" ? val : getSize(tokens.size[val], {
231
+ shift: -1
232
+ });
233
+ return {
234
+ width: size,
235
+ height: size,
236
+ minWidth: size,
237
+ minHeight: size
238
+ };
239
+ },
240
+ SliderThumbFrame = styled(ThemeableStack, {
241
+ name: "SliderThumb",
242
+ variants: {
243
+ size: {
244
+ "...size": getThumbSize
245
+ },
246
+ unstyled: {
247
+ false: {
248
+ position: "absolute",
249
+ bordered: 2,
250
+ borderWidth: 2,
251
+ backgrounded: !0,
252
+ pressTheme: isWeb,
253
+ focusTheme: isWeb,
254
+ hoverTheme: isWeb
255
+ }
256
+ }
257
+ },
258
+ defaultVariants: {
259
+ unstyled: process.env.TAMAGUI_HEADLESS === "1"
260
+ }
261
+ }),
262
+ SliderThumb = React.memo(SliderThumbFrame.styleable(function (props, forwardedRef) {
263
+ const {
264
+ __scopeSlider,
265
+ index,
266
+ size: sizeProp,
267
+ ...thumbProps
268
+ } = props,
269
+ context = useSliderContext(THUMB_NAME, __scopeSlider),
270
+ orientation = useSliderOrientationContext(THUMB_NAME, __scopeSlider),
271
+ [thumb, setThumb] = React.useState(null),
272
+ composedRefs = useComposedRefs(forwardedRef, node => setThumb(node)),
273
+ value = context.values[index],
274
+ percent = value === void 0 ? 0 : convertValueToPercentage(value, context.min, context.max),
275
+ label = getLabel(index, context.values.length),
276
+ sizeIn = sizeProp ?? context.size ?? "$true",
277
+ [size, setSize] = React.useState(() => getVariableValue(getThumbSize(sizeIn).width)),
278
+ thumbInBoundsOffset = size ? getThumbInBoundsOffset(size, percent, orientation.direction) : 0;
279
+ React.useEffect(() => {
280
+ if (thumb) return context.thumbs.set(thumb, index), () => {
281
+ context.thumbs.delete(thumb);
282
+ };
283
+ }, [thumb, context.thumbs, index]);
284
+ const positionalStyles = context.orientation === "horizontal" ? {
285
+ x: thumbInBoundsOffset - size / 2,
286
+ y: -size / 2,
287
+ top: "50%",
288
+ ...(size === 0 && {
289
+ top: "auto",
290
+ bottom: "auto"
291
+ })
292
+ } : {
293
+ x: -size / 2,
294
+ y: size / 2,
295
+ left: "50%",
296
+ ...(size === 0 && {
297
+ left: "auto",
298
+ right: "auto"
299
+ })
300
+ };
301
+ return /* @__PURE__ */jsx(SliderThumbFrame, {
302
+ ref: composedRefs,
303
+ role: "slider",
304
+ "aria-label": props["aria-label"] || label,
305
+ "aria-valuemin": context.min,
306
+ "aria-valuenow": value,
307
+ "aria-valuemax": context.max,
308
+ "aria-orientation": context.orientation,
309
+ "data-orientation": context.orientation,
310
+ "data-disabled": context.disabled ? "" : void 0,
311
+ tabIndex: context.disabled ? void 0 : 0,
312
+ animateOnly: ["transform", "left", "top", "right", "bottom"],
313
+ ...positionalStyles,
314
+ [orientation.startEdge]: `${percent}%`,
315
+ size: sizeIn,
316
+ ...thumbProps,
317
+ onLayout: e => {
318
+ setSize(e.nativeEvent.layout[orientation.sizeProp]);
319
+ },
320
+ onFocus: composeEventHandlers(props.onFocus, () => {
321
+ context.valueIndexToChangeRef.current = index;
322
+ })
323
+ });
324
+ })),
325
+ SliderComponent = React.forwardRef((props, forwardedRef) => {
326
+ const {
327
+ name,
328
+ min = 0,
329
+ max = 100,
330
+ step = 1,
331
+ orientation = "horizontal",
332
+ disabled = !1,
333
+ minStepsBetweenThumbs = 0,
334
+ defaultValue = [min],
335
+ value,
336
+ onValueChange = () => {},
337
+ size: sizeProp,
338
+ onSlideEnd,
339
+ onSlideMove,
340
+ onSlideStart,
341
+ ...sliderProps
342
+ } = props,
343
+ sliderRef = React.useRef(null),
344
+ composedRefs = useComposedRefs(sliderRef, forwardedRef),
345
+ thumbRefs = React.useRef( /* @__PURE__ */new Map()),
346
+ valueIndexToChangeRef = React.useRef(0),
347
+ isHorizontal = orientation === "horizontal",
348
+ [values = [], setValues] = useControllableState({
349
+ prop: value,
350
+ defaultProp: defaultValue,
351
+ transition: !0,
352
+ onChange: value2 => {
353
+ updateThumbFocus(valueIndexToChangeRef.current), onValueChange(value2);
354
+ }
355
+ });
356
+ isWeb && React.useEffect(() => {
357
+ const node = sliderRef.current;
358
+ if (!node) return;
359
+ const preventDefault = e => {
360
+ e.preventDefault();
361
+ };
362
+ return node.addEventListener("touchstart", preventDefault), () => {
363
+ node.removeEventListener("touchstart", preventDefault);
364
+ };
365
+ }, []);
366
+ function updateThumbFocus(focusIndex) {
367
+ if (isWeb) {
368
+ for (const [node, index] of thumbRefs.current.entries()) if (index === focusIndex) {
369
+ node.focus();
370
+ return;
371
+ }
372
+ }
373
+ }
374
+ function handleSlideMove(value2, event) {
375
+ updateValues(value2, valueIndexToChangeRef.current), onSlideMove?.(event, value2);
376
+ }
377
+ function updateValues(value2, atIndex) {
378
+ const decimalCount = getDecimalCount(step),
379
+ snapToStep = roundValue(Math.round((value2 - min) / step) * step + min, decimalCount),
380
+ nextValue = clamp(snapToStep, [min, max]);
381
+ setValues((prevValues = []) => {
382
+ const nextValues = getNextSortedValues(prevValues, nextValue, atIndex);
383
+ return hasMinStepsBetweenValues(nextValues, minStepsBetweenThumbs * step) ? (valueIndexToChangeRef.current = nextValues.indexOf(nextValue), String(nextValues) === String(prevValues) ? prevValues : nextValues) : prevValues;
384
+ });
385
+ }
386
+ const SliderOriented = isHorizontal ? SliderHorizontal : SliderVertical;
387
+ return /* @__PURE__ */jsx(SliderProvider, {
388
+ scope: props.__scopeSlider,
389
+ disabled,
390
+ min,
391
+ max,
392
+ valueIndexToChangeRef,
393
+ thumbs: thumbRefs.current,
394
+ values,
395
+ orientation,
396
+ size: sizeProp,
397
+ children: /* @__PURE__ */jsx(SliderOriented, {
398
+ "aria-disabled": disabled,
399
+ "data-disabled": disabled ? "" : void 0,
400
+ ...sliderProps,
401
+ ref: composedRefs,
402
+ min,
403
+ max,
404
+ onSlideEnd,
405
+ onSlideStart: disabled ? void 0 : (value2, target, event) => {
406
+ if (target !== "thumb") {
407
+ const closestIndex = getClosestValueIndex(values, value2);
408
+ updateValues(value2, closestIndex);
409
+ }
410
+ onSlideStart?.(event, value2, target);
411
+ },
412
+ onSlideMove: disabled ? void 0 : handleSlideMove,
413
+ onHomeKeyDown: () => !disabled && updateValues(min, 0),
414
+ onEndKeyDown: () => !disabled && updateValues(max, values.length - 1),
415
+ onStepKeyDown: ({
416
+ event,
417
+ direction: stepDirection
418
+ }) => {
419
+ if (!disabled) {
420
+ const multiplier = PAGE_KEYS.includes(event.key) || event.shiftKey && ARROW_KEYS.includes(event.key) ? 10 : 1,
421
+ atIndex = valueIndexToChangeRef.current,
422
+ value2 = values[atIndex],
423
+ stepInDirection = step * multiplier * stepDirection;
424
+ updateValues(value2 + stepInDirection, atIndex);
425
+ }
426
+ }
427
+ })
428
+ });
429
+ }),
430
+ Slider = withStaticProperties(SliderComponent, {
431
+ Track: SliderTrack,
432
+ TrackActive: SliderTrackActive,
433
+ Thumb: SliderThumb
434
+ });
435
+ Slider.displayName = SLIDER_NAME;
436
+ const Track = SliderTrack,
437
+ Range = SliderTrackActive,
438
+ Thumb = SliderThumb;
439
+ export { Range, Slider, SliderThumb, SliderThumbFrame, SliderTrack, SliderTrackActive, SliderTrackActiveFrame, SliderTrackFrame, Thumb, Track };
@@ -0,0 +1,74 @@
1
+ import { isWeb } from "@tamagui/constants";
2
+ import { getVariableValue, styled } from "@tamagui/core";
3
+ import { getSize } from "@tamagui/get-token";
4
+ import { composeEventHandlers } from "@tamagui/helpers";
5
+ import { YStack } from "@tamagui/stacks";
6
+ import * as React from "react";
7
+ import { ARROW_KEYS, PAGE_KEYS, SLIDER_NAME, useSliderContext } from "./constants.mjs";
8
+ import { jsx } from "react/jsx-runtime";
9
+ const SliderFrame = styled(YStack, {
10
+ position: "relative",
11
+ variants: {
12
+ orientation: {
13
+ horizontal: {},
14
+ vertical: {}
15
+ },
16
+ size: (val, extras) => {
17
+ if (!val) return;
18
+ const orientation = extras.props.orientation,
19
+ size = Math.round(getVariableValue(getSize(val)) / 6);
20
+ return orientation === "horizontal" ? {
21
+ height: size,
22
+ borderRadius: size,
23
+ justifyContent: "center"
24
+ } : {
25
+ width: size,
26
+ borderRadius: size,
27
+ alignItems: "center"
28
+ };
29
+ }
30
+ }
31
+ }),
32
+ SliderImpl = React.forwardRef((props, forwardedRef) => {
33
+ const {
34
+ __scopeSlider,
35
+ onSlideStart,
36
+ onSlideMove,
37
+ onSlideEnd,
38
+ onHomeKeyDown,
39
+ onEndKeyDown,
40
+ onStepKeyDown,
41
+ ...sliderProps
42
+ } = props,
43
+ context = useSliderContext(SLIDER_NAME, __scopeSlider);
44
+ return /* @__PURE__ */jsx(SliderFrame, {
45
+ size: "$4",
46
+ ...sliderProps,
47
+ "data-orientation": sliderProps.orientation,
48
+ ref: forwardedRef,
49
+ ...(isWeb && {
50
+ onKeyDown: event => {
51
+ event.key === "Home" ? (onHomeKeyDown(event), event.preventDefault()) : event.key === "End" ? (onEndKeyDown(event), event.preventDefault()) : PAGE_KEYS.concat(ARROW_KEYS).includes(event.key) && (onStepKeyDown(event), event.preventDefault());
52
+ }
53
+ }),
54
+ onMoveShouldSetResponderCapture: () => !0,
55
+ onScrollShouldSetResponder: () => !0,
56
+ onScrollShouldSetResponderCapture: () => !0,
57
+ onMoveShouldSetResponder: () => !0,
58
+ onStartShouldSetResponder: () => !0,
59
+ onResponderTerminationRequest: () => !1,
60
+ onResponderGrant: composeEventHandlers(props.onResponderGrant, event => {
61
+ const target = event.target,
62
+ thumbIndex = context.thumbs.get(target),
63
+ isStartingOnThumb = thumbIndex !== void 0;
64
+ isWeb && target instanceof HTMLElement && context.thumbs.has(target) && target.focus(), !isWeb && isStartingOnThumb && (context.valueIndexToChangeRef.current = thumbIndex), onSlideStart(event, isStartingOnThumb ? "thumb" : "track");
65
+ }),
66
+ onResponderMove: composeEventHandlers(props.onResponderMove, event => {
67
+ event.stopPropagation(), onSlideMove(event);
68
+ }),
69
+ onResponderRelease: composeEventHandlers(props.onResponderRelease, event => {
70
+ onSlideEnd(event);
71
+ })
72
+ });
73
+ });
74
+ export { SliderFrame, SliderImpl };
@@ -0,0 +1,18 @@
1
+ import { createContextScope } from "@tamagui/create-context";
2
+ const SLIDER_NAME = "Slider",
3
+ [createSliderContext, createSliderScope] = createContextScope(SLIDER_NAME),
4
+ [SliderProvider, useSliderContext] = createSliderContext(SLIDER_NAME),
5
+ [SliderOrientationProvider, useSliderOrientationContext] = createSliderContext(SLIDER_NAME, {
6
+ startEdge: "left",
7
+ endEdge: "right",
8
+ sizeProp: "width",
9
+ size: 0,
10
+ direction: 1
11
+ }),
12
+ PAGE_KEYS = ["PageUp", "PageDown"],
13
+ ARROW_KEYS = ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"],
14
+ BACK_KEYS = {
15
+ ltr: ["ArrowDown", "Home", "ArrowLeft", "PageDown"],
16
+ rtl: ["ArrowDown", "Home", "ArrowRight", "PageDown"]
17
+ };
18
+ export { ARROW_KEYS, BACK_KEYS, PAGE_KEYS, SLIDER_NAME, SliderOrientationProvider, SliderProvider, createSliderContext, createSliderScope, useSliderContext, useSliderOrientationContext };
@@ -0,0 +1,47 @@
1
+ function getNextSortedValues(prevValues = [], nextValue, atIndex) {
2
+ const nextValues = [...prevValues];
3
+ return nextValues[atIndex] = nextValue, nextValues.sort((a, b) => a - b);
4
+ }
5
+ function convertValueToPercentage(value, min, max) {
6
+ return 100 / (max - min) * (value - min);
7
+ }
8
+ function getLabel(index, totalValues) {
9
+ if (totalValues > 2) return `Value ${index + 1} of ${totalValues}`;
10
+ if (totalValues === 2) return ["Minimum", "Maximum"][index];
11
+ }
12
+ function getClosestValueIndex(values, nextValue) {
13
+ if (values.length === 1) return 0;
14
+ const distances = values.map(value => Math.abs(value - nextValue)),
15
+ closestDistance = Math.min(...distances);
16
+ return distances.indexOf(closestDistance);
17
+ }
18
+ function getThumbInBoundsOffset(width, left, direction) {
19
+ const quarterWidth = width / 4,
20
+ offset = linearScale([0, 50], [0, quarterWidth]);
21
+ return (quarterWidth - offset(left) * direction) * direction;
22
+ }
23
+ function getStepsBetweenValues(values) {
24
+ return values.slice(0, -1).map((value, index) => values[index + 1] - value);
25
+ }
26
+ function hasMinStepsBetweenValues(values, minStepsBetweenValues) {
27
+ if (minStepsBetweenValues > 0) {
28
+ const stepsBetweenValues = getStepsBetweenValues(values);
29
+ return Math.min(...stepsBetweenValues) >= minStepsBetweenValues;
30
+ }
31
+ return !0;
32
+ }
33
+ function linearScale(input, output) {
34
+ return value => {
35
+ if (input[0] === input[1] || output[0] === output[1]) return output[0];
36
+ const ratio = (output[1] - output[0]) / (input[1] - input[0]);
37
+ return output[0] + ratio * (value - input[0]);
38
+ };
39
+ }
40
+ function getDecimalCount(value) {
41
+ return (String(value).split(".")[1] || "").length;
42
+ }
43
+ function roundValue(value, decimalCount) {
44
+ const rounder = 10 ** decimalCount;
45
+ return Math.round(value * rounder) / rounder;
46
+ }
47
+ export { convertValueToPercentage, getClosestValueIndex, getDecimalCount, getLabel, getNextSortedValues, getThumbInBoundsOffset, hasMinStepsBetweenValues, linearScale, roundValue };
@@ -0,0 +1,3 @@
1
+ export * from "./Slider.mjs";
2
+ import { SliderFrame } from "./SliderImpl.mjs";
3
+ export { SliderFrame };
File without changes
@@ -0,0 +1,439 @@
1
+ import { composeRefs, useComposedRefs } from "@tamagui/compose-refs";
2
+ import { isClient, isWeb } from "@tamagui/constants";
3
+ import { createShallowSetState, getTokens, getVariableValue, styled } from "@tamagui/core";
4
+ import { getSize } from "@tamagui/get-token";
5
+ import { withStaticProperties } from "@tamagui/helpers";
6
+ import { clamp, composeEventHandlers } from "@tamagui/helpers";
7
+ import { ThemeableStack } from "@tamagui/stacks";
8
+ import { useControllableState } from "@tamagui/use-controllable-state";
9
+ import { useDirection } from "@tamagui/use-direction";
10
+ import * as React from "react";
11
+ import { ARROW_KEYS, BACK_KEYS, PAGE_KEYS, SLIDER_NAME, SliderOrientationProvider, SliderProvider, useSliderContext, useSliderOrientationContext } from "./constants.mjs";
12
+ import { convertValueToPercentage, getClosestValueIndex, getDecimalCount, getLabel, getNextSortedValues, getThumbInBoundsOffset, hasMinStepsBetweenValues, linearScale, roundValue } from "./helpers.mjs";
13
+ import { SliderFrame, SliderImpl } from "./SliderImpl.mjs";
14
+ import { jsx } from "react/jsx-runtime";
15
+ const SliderHorizontal = React.forwardRef((props, forwardedRef) => {
16
+ const {
17
+ min,
18
+ max,
19
+ dir,
20
+ onSlideStart,
21
+ onSlideMove,
22
+ onStepKeyDown,
23
+ onSlideEnd,
24
+ ...sliderProps
25
+ } = props,
26
+ direction = useDirection(dir),
27
+ isDirectionLTR = direction === "ltr",
28
+ sliderRef = React.useRef(null),
29
+ [state, setState_] = React.useState(() => ({
30
+ size: 0,
31
+ offset: 0
32
+ })),
33
+ setState = createShallowSetState(setState_);
34
+ function getValueFromPointer(pointerPosition) {
35
+ const input = [0, state.size];
36
+ return linearScale(input, isDirectionLTR ? [min, max] : [max, min])(pointerPosition);
37
+ }
38
+ const measure = () => {
39
+ sliderRef.current?.measure((_x, _y, width, _height, pageX, _pageY) => {
40
+ setState({
41
+ size: width,
42
+ offset: pageX
43
+ });
44
+ });
45
+ };
46
+ return isClient && useOnDebouncedWindowResize(measure), /* @__PURE__ */jsx(SliderOrientationProvider, {
47
+ scope: props.__scopeSlider,
48
+ startEdge: isDirectionLTR ? "left" : "right",
49
+ endEdge: isDirectionLTR ? "right" : "left",
50
+ direction: isDirectionLTR ? 1 : -1,
51
+ sizeProp: "width",
52
+ size: state.size,
53
+ children: /* @__PURE__ */jsx(SliderImpl, {
54
+ ref: composeRefs(forwardedRef, sliderRef),
55
+ dir: direction,
56
+ ...sliderProps,
57
+ orientation: "horizontal",
58
+ onLayout: measure,
59
+ onSlideStart: (event, target) => {
60
+ const value = getValueFromPointer(event.nativeEvent.locationX);
61
+ value && onSlideStart?.(value, target, event);
62
+ },
63
+ onSlideMove: event => {
64
+ const value = getValueFromPointer(event.nativeEvent.pageX - state.offset);
65
+ value && onSlideMove?.(value, event);
66
+ },
67
+ onSlideEnd: event => {
68
+ const value = getValueFromPointer(event.nativeEvent.pageX - state.offset);
69
+ value && onSlideEnd?.(event, value);
70
+ },
71
+ onStepKeyDown: event => {
72
+ const isBackKey = BACK_KEYS[direction].includes(event.key);
73
+ onStepKeyDown?.({
74
+ event,
75
+ direction: isBackKey ? -1 : 1
76
+ });
77
+ }
78
+ })
79
+ });
80
+ });
81
+ function useOnDebouncedWindowResize(callback, amt = 200) {
82
+ React.useEffect(() => {
83
+ let last;
84
+ const onResize = () => {
85
+ clearTimeout(last), last = setTimeout(callback, amt);
86
+ };
87
+ return window.addEventListener("resize", onResize), () => {
88
+ clearTimeout(last), window.removeEventListener("resize", onResize);
89
+ };
90
+ }, []);
91
+ }
92
+ const SliderVertical = React.forwardRef((props, forwardedRef) => {
93
+ const {
94
+ min,
95
+ max,
96
+ onSlideStart,
97
+ onSlideMove,
98
+ onStepKeyDown,
99
+ onSlideEnd,
100
+ ...sliderProps
101
+ } = props,
102
+ [state, setState_] = React.useState(() => ({
103
+ size: 0,
104
+ offset: 0
105
+ })),
106
+ setState = createShallowSetState(setState_),
107
+ sliderRef = React.useRef(null);
108
+ function getValueFromPointer(pointerPosition) {
109
+ const input = [0, state.size];
110
+ return linearScale(input, [max, min])(pointerPosition);
111
+ }
112
+ const measure = () => {
113
+ sliderRef.current?.measure((_x, _y, _width, height, _pageX, pageY) => {
114
+ setState({
115
+ size: height,
116
+ offset: pageY
117
+ });
118
+ });
119
+ };
120
+ return isClient && useOnDebouncedWindowResize(measure), /* @__PURE__ */jsx(SliderOrientationProvider, {
121
+ scope: props.__scopeSlider,
122
+ startEdge: "bottom",
123
+ endEdge: "top",
124
+ sizeProp: "height",
125
+ size: state.size,
126
+ direction: 1,
127
+ children: /* @__PURE__ */jsx(SliderImpl, {
128
+ ref: composeRefs(forwardedRef, sliderRef),
129
+ ...sliderProps,
130
+ orientation: "vertical",
131
+ onLayout: measure,
132
+ onSlideStart: (event, target) => {
133
+ const value = getValueFromPointer(event.nativeEvent.locationY);
134
+ value && onSlideStart?.(value, target, event);
135
+ },
136
+ onSlideMove: event => {
137
+ const value = getValueFromPointer(event.nativeEvent.pageY - state.offset);
138
+ value && onSlideMove?.(value, event);
139
+ },
140
+ onSlideEnd: event => {
141
+ const value = getValueFromPointer(event.nativeEvent.pageY - state.offset);
142
+ onSlideEnd?.(event, value);
143
+ },
144
+ onStepKeyDown: event => {
145
+ const isBackKey = BACK_KEYS.ltr.includes(event.key);
146
+ onStepKeyDown?.({
147
+ event,
148
+ direction: isBackKey ? -1 : 1
149
+ });
150
+ }
151
+ })
152
+ });
153
+ }),
154
+ TRACK_NAME = "SliderTrack",
155
+ SliderTrackFrame = styled(SliderFrame, {
156
+ name: "SliderTrack",
157
+ variants: {
158
+ unstyled: {
159
+ false: {
160
+ height: "100%",
161
+ width: "100%",
162
+ backgroundColor: "$background",
163
+ position: "relative",
164
+ borderRadius: 1e5,
165
+ overflow: "hidden"
166
+ }
167
+ }
168
+ },
169
+ defaultVariants: {
170
+ unstyled: process.env.TAMAGUI_HEADLESS === "1"
171
+ }
172
+ }),
173
+ SliderTrack = React.forwardRef((props, forwardedRef) => {
174
+ const {
175
+ __scopeSlider,
176
+ ...trackProps
177
+ } = props,
178
+ context = useSliderContext(TRACK_NAME, __scopeSlider);
179
+ return /* @__PURE__ */jsx(SliderTrackFrame, {
180
+ "data-disabled": context.disabled ? "" : void 0,
181
+ "data-orientation": context.orientation,
182
+ orientation: context.orientation,
183
+ size: context.size,
184
+ ...trackProps,
185
+ ref: forwardedRef
186
+ });
187
+ });
188
+ SliderTrack.displayName = TRACK_NAME;
189
+ const RANGE_NAME = "SliderTrackActive",
190
+ SliderTrackActiveFrame = styled(SliderFrame, {
191
+ name: "SliderTrackActive",
192
+ backgroundColor: "$background",
193
+ position: "absolute"
194
+ }),
195
+ SliderTrackActive = React.forwardRef((props, forwardedRef) => {
196
+ const {
197
+ __scopeSlider,
198
+ ...rangeProps
199
+ } = props,
200
+ context = useSliderContext(RANGE_NAME, __scopeSlider),
201
+ orientation = useSliderOrientationContext(RANGE_NAME, __scopeSlider),
202
+ ref = React.useRef(null),
203
+ composedRefs = useComposedRefs(forwardedRef, ref),
204
+ valuesCount = context.values.length,
205
+ percentages = context.values.map(value => convertValueToPercentage(value, context.min, context.max)),
206
+ offsetStart = valuesCount > 1 ? Math.min(...percentages) : 0,
207
+ offsetEnd = 100 - Math.max(...percentages);
208
+ return /* @__PURE__ */jsx(SliderTrackActiveFrame, {
209
+ orientation: context.orientation,
210
+ "data-orientation": context.orientation,
211
+ "data-disabled": context.disabled ? "" : void 0,
212
+ size: context.size,
213
+ animateOnly: ["left", "top", "right", "bottom"],
214
+ ...rangeProps,
215
+ ref: composedRefs,
216
+ [orientation.startEdge]: `${offsetStart}%`,
217
+ [orientation.endEdge]: `${offsetEnd}%`,
218
+ ...(orientation.sizeProp === "width" ? {
219
+ height: "100%"
220
+ } : {
221
+ left: 0,
222
+ right: 0
223
+ })
224
+ });
225
+ });
226
+ SliderTrackActive.displayName = RANGE_NAME;
227
+ const THUMB_NAME = "SliderThumb",
228
+ getThumbSize = val => {
229
+ const tokens = getTokens(),
230
+ size = typeof val == "number" ? val : getSize(tokens.size[val], {
231
+ shift: -1
232
+ });
233
+ return {
234
+ width: size,
235
+ height: size,
236
+ minWidth: size,
237
+ minHeight: size
238
+ };
239
+ },
240
+ SliderThumbFrame = styled(ThemeableStack, {
241
+ name: "SliderThumb",
242
+ variants: {
243
+ size: {
244
+ "...size": getThumbSize
245
+ },
246
+ unstyled: {
247
+ false: {
248
+ position: "absolute",
249
+ bordered: 2,
250
+ borderWidth: 2,
251
+ backgrounded: !0,
252
+ pressTheme: isWeb,
253
+ focusTheme: isWeb,
254
+ hoverTheme: isWeb
255
+ }
256
+ }
257
+ },
258
+ defaultVariants: {
259
+ unstyled: process.env.TAMAGUI_HEADLESS === "1"
260
+ }
261
+ }),
262
+ SliderThumb = React.memo(SliderThumbFrame.styleable(function (props, forwardedRef) {
263
+ const {
264
+ __scopeSlider,
265
+ index,
266
+ size: sizeProp,
267
+ ...thumbProps
268
+ } = props,
269
+ context = useSliderContext(THUMB_NAME, __scopeSlider),
270
+ orientation = useSliderOrientationContext(THUMB_NAME, __scopeSlider),
271
+ [thumb, setThumb] = React.useState(null),
272
+ composedRefs = useComposedRefs(forwardedRef, node => setThumb(node)),
273
+ value = context.values[index],
274
+ percent = value === void 0 ? 0 : convertValueToPercentage(value, context.min, context.max),
275
+ label = getLabel(index, context.values.length),
276
+ sizeIn = sizeProp ?? context.size ?? "$true",
277
+ [size, setSize] = React.useState(() => getVariableValue(getThumbSize(sizeIn).width)),
278
+ thumbInBoundsOffset = size ? getThumbInBoundsOffset(size, percent, orientation.direction) : 0;
279
+ React.useEffect(() => {
280
+ if (thumb) return context.thumbs.set(thumb, index), () => {
281
+ context.thumbs.delete(thumb);
282
+ };
283
+ }, [thumb, context.thumbs, index]);
284
+ const positionalStyles = context.orientation === "horizontal" ? {
285
+ x: thumbInBoundsOffset - size / 2,
286
+ y: -size / 2,
287
+ top: "50%",
288
+ ...(size === 0 && {
289
+ top: "auto",
290
+ bottom: "auto"
291
+ })
292
+ } : {
293
+ x: -size / 2,
294
+ y: size / 2,
295
+ left: "50%",
296
+ ...(size === 0 && {
297
+ left: "auto",
298
+ right: "auto"
299
+ })
300
+ };
301
+ return /* @__PURE__ */jsx(SliderThumbFrame, {
302
+ ref: composedRefs,
303
+ role: "slider",
304
+ "aria-label": props["aria-label"] || label,
305
+ "aria-valuemin": context.min,
306
+ "aria-valuenow": value,
307
+ "aria-valuemax": context.max,
308
+ "aria-orientation": context.orientation,
309
+ "data-orientation": context.orientation,
310
+ "data-disabled": context.disabled ? "" : void 0,
311
+ tabIndex: context.disabled ? void 0 : 0,
312
+ animateOnly: ["transform", "left", "top", "right", "bottom"],
313
+ ...positionalStyles,
314
+ [orientation.startEdge]: `${percent}%`,
315
+ size: sizeIn,
316
+ ...thumbProps,
317
+ onLayout: e => {
318
+ setSize(e.nativeEvent.layout[orientation.sizeProp]);
319
+ },
320
+ onFocus: composeEventHandlers(props.onFocus, () => {
321
+ context.valueIndexToChangeRef.current = index;
322
+ })
323
+ });
324
+ })),
325
+ SliderComponent = React.forwardRef((props, forwardedRef) => {
326
+ const {
327
+ name,
328
+ min = 0,
329
+ max = 100,
330
+ step = 1,
331
+ orientation = "horizontal",
332
+ disabled = !1,
333
+ minStepsBetweenThumbs = 0,
334
+ defaultValue = [min],
335
+ value,
336
+ onValueChange = () => {},
337
+ size: sizeProp,
338
+ onSlideEnd,
339
+ onSlideMove,
340
+ onSlideStart,
341
+ ...sliderProps
342
+ } = props,
343
+ sliderRef = React.useRef(null),
344
+ composedRefs = useComposedRefs(sliderRef, forwardedRef),
345
+ thumbRefs = React.useRef( /* @__PURE__ */new Map()),
346
+ valueIndexToChangeRef = React.useRef(0),
347
+ isHorizontal = orientation === "horizontal",
348
+ [values = [], setValues] = useControllableState({
349
+ prop: value,
350
+ defaultProp: defaultValue,
351
+ transition: !0,
352
+ onChange: value2 => {
353
+ updateThumbFocus(valueIndexToChangeRef.current), onValueChange(value2);
354
+ }
355
+ });
356
+ isWeb && React.useEffect(() => {
357
+ const node = sliderRef.current;
358
+ if (!node) return;
359
+ const preventDefault = e => {
360
+ e.preventDefault();
361
+ };
362
+ return node.addEventListener("touchstart", preventDefault), () => {
363
+ node.removeEventListener("touchstart", preventDefault);
364
+ };
365
+ }, []);
366
+ function updateThumbFocus(focusIndex) {
367
+ if (isWeb) {
368
+ for (const [node, index] of thumbRefs.current.entries()) if (index === focusIndex) {
369
+ node.focus();
370
+ return;
371
+ }
372
+ }
373
+ }
374
+ function handleSlideMove(value2, event) {
375
+ updateValues(value2, valueIndexToChangeRef.current), onSlideMove?.(event, value2);
376
+ }
377
+ function updateValues(value2, atIndex) {
378
+ const decimalCount = getDecimalCount(step),
379
+ snapToStep = roundValue(Math.round((value2 - min) / step) * step + min, decimalCount),
380
+ nextValue = clamp(snapToStep, [min, max]);
381
+ setValues((prevValues = []) => {
382
+ const nextValues = getNextSortedValues(prevValues, nextValue, atIndex);
383
+ return hasMinStepsBetweenValues(nextValues, minStepsBetweenThumbs * step) ? (valueIndexToChangeRef.current = nextValues.indexOf(nextValue), String(nextValues) === String(prevValues) ? prevValues : nextValues) : prevValues;
384
+ });
385
+ }
386
+ const SliderOriented = isHorizontal ? SliderHorizontal : SliderVertical;
387
+ return /* @__PURE__ */jsx(SliderProvider, {
388
+ scope: props.__scopeSlider,
389
+ disabled,
390
+ min,
391
+ max,
392
+ valueIndexToChangeRef,
393
+ thumbs: thumbRefs.current,
394
+ values,
395
+ orientation,
396
+ size: sizeProp,
397
+ children: /* @__PURE__ */jsx(SliderOriented, {
398
+ "aria-disabled": disabled,
399
+ "data-disabled": disabled ? "" : void 0,
400
+ ...sliderProps,
401
+ ref: composedRefs,
402
+ min,
403
+ max,
404
+ onSlideEnd,
405
+ onSlideStart: disabled ? void 0 : (value2, target, event) => {
406
+ if (target !== "thumb") {
407
+ const closestIndex = getClosestValueIndex(values, value2);
408
+ updateValues(value2, closestIndex);
409
+ }
410
+ onSlideStart?.(event, value2, target);
411
+ },
412
+ onSlideMove: disabled ? void 0 : handleSlideMove,
413
+ onHomeKeyDown: () => !disabled && updateValues(min, 0),
414
+ onEndKeyDown: () => !disabled && updateValues(max, values.length - 1),
415
+ onStepKeyDown: ({
416
+ event,
417
+ direction: stepDirection
418
+ }) => {
419
+ if (!disabled) {
420
+ const multiplier = PAGE_KEYS.includes(event.key) || event.shiftKey && ARROW_KEYS.includes(event.key) ? 10 : 1,
421
+ atIndex = valueIndexToChangeRef.current,
422
+ value2 = values[atIndex],
423
+ stepInDirection = step * multiplier * stepDirection;
424
+ updateValues(value2 + stepInDirection, atIndex);
425
+ }
426
+ }
427
+ })
428
+ });
429
+ }),
430
+ Slider = withStaticProperties(SliderComponent, {
431
+ Track: SliderTrack,
432
+ TrackActive: SliderTrackActive,
433
+ Thumb: SliderThumb
434
+ });
435
+ Slider.displayName = SLIDER_NAME;
436
+ const Track = SliderTrack,
437
+ Range = SliderTrackActive,
438
+ Thumb = SliderThumb;
439
+ export { Range, Slider, SliderThumb, SliderThumbFrame, SliderTrack, SliderTrackActive, SliderTrackActiveFrame, SliderTrackFrame, Thumb, Track };
@@ -0,0 +1,74 @@
1
+ import { isWeb } from "@tamagui/constants";
2
+ import { getVariableValue, styled } from "@tamagui/core";
3
+ import { getSize } from "@tamagui/get-token";
4
+ import { composeEventHandlers } from "@tamagui/helpers";
5
+ import { YStack } from "@tamagui/stacks";
6
+ import * as React from "react";
7
+ import { ARROW_KEYS, PAGE_KEYS, SLIDER_NAME, useSliderContext } from "./constants.mjs";
8
+ import { jsx } from "react/jsx-runtime";
9
+ const SliderFrame = styled(YStack, {
10
+ position: "relative",
11
+ variants: {
12
+ orientation: {
13
+ horizontal: {},
14
+ vertical: {}
15
+ },
16
+ size: (val, extras) => {
17
+ if (!val) return;
18
+ const orientation = extras.props.orientation,
19
+ size = Math.round(getVariableValue(getSize(val)) / 6);
20
+ return orientation === "horizontal" ? {
21
+ height: size,
22
+ borderRadius: size,
23
+ justifyContent: "center"
24
+ } : {
25
+ width: size,
26
+ borderRadius: size,
27
+ alignItems: "center"
28
+ };
29
+ }
30
+ }
31
+ }),
32
+ SliderImpl = React.forwardRef((props, forwardedRef) => {
33
+ const {
34
+ __scopeSlider,
35
+ onSlideStart,
36
+ onSlideMove,
37
+ onSlideEnd,
38
+ onHomeKeyDown,
39
+ onEndKeyDown,
40
+ onStepKeyDown,
41
+ ...sliderProps
42
+ } = props,
43
+ context = useSliderContext(SLIDER_NAME, __scopeSlider);
44
+ return /* @__PURE__ */jsx(SliderFrame, {
45
+ size: "$4",
46
+ ...sliderProps,
47
+ "data-orientation": sliderProps.orientation,
48
+ ref: forwardedRef,
49
+ ...(isWeb && {
50
+ onKeyDown: event => {
51
+ event.key === "Home" ? (onHomeKeyDown(event), event.preventDefault()) : event.key === "End" ? (onEndKeyDown(event), event.preventDefault()) : PAGE_KEYS.concat(ARROW_KEYS).includes(event.key) && (onStepKeyDown(event), event.preventDefault());
52
+ }
53
+ }),
54
+ onMoveShouldSetResponderCapture: () => !0,
55
+ onScrollShouldSetResponder: () => !0,
56
+ onScrollShouldSetResponderCapture: () => !0,
57
+ onMoveShouldSetResponder: () => !0,
58
+ onStartShouldSetResponder: () => !0,
59
+ onResponderTerminationRequest: () => !1,
60
+ onResponderGrant: composeEventHandlers(props.onResponderGrant, event => {
61
+ const target = event.target,
62
+ thumbIndex = context.thumbs.get(target),
63
+ isStartingOnThumb = thumbIndex !== void 0;
64
+ isWeb && target instanceof HTMLElement && context.thumbs.has(target) && target.focus(), !isWeb && isStartingOnThumb && (context.valueIndexToChangeRef.current = thumbIndex), onSlideStart(event, isStartingOnThumb ? "thumb" : "track");
65
+ }),
66
+ onResponderMove: composeEventHandlers(props.onResponderMove, event => {
67
+ event.stopPropagation(), onSlideMove(event);
68
+ }),
69
+ onResponderRelease: composeEventHandlers(props.onResponderRelease, event => {
70
+ onSlideEnd(event);
71
+ })
72
+ });
73
+ });
74
+ export { SliderFrame, SliderImpl };
@@ -0,0 +1,18 @@
1
+ import { createContextScope } from "@tamagui/create-context";
2
+ const SLIDER_NAME = "Slider",
3
+ [createSliderContext, createSliderScope] = createContextScope(SLIDER_NAME),
4
+ [SliderProvider, useSliderContext] = createSliderContext(SLIDER_NAME),
5
+ [SliderOrientationProvider, useSliderOrientationContext] = createSliderContext(SLIDER_NAME, {
6
+ startEdge: "left",
7
+ endEdge: "right",
8
+ sizeProp: "width",
9
+ size: 0,
10
+ direction: 1
11
+ }),
12
+ PAGE_KEYS = ["PageUp", "PageDown"],
13
+ ARROW_KEYS = ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"],
14
+ BACK_KEYS = {
15
+ ltr: ["ArrowDown", "Home", "ArrowLeft", "PageDown"],
16
+ rtl: ["ArrowDown", "Home", "ArrowRight", "PageDown"]
17
+ };
18
+ export { ARROW_KEYS, BACK_KEYS, PAGE_KEYS, SLIDER_NAME, SliderOrientationProvider, SliderProvider, createSliderContext, createSliderScope, useSliderContext, useSliderOrientationContext };
@@ -0,0 +1,47 @@
1
+ function getNextSortedValues(prevValues = [], nextValue, atIndex) {
2
+ const nextValues = [...prevValues];
3
+ return nextValues[atIndex] = nextValue, nextValues.sort((a, b) => a - b);
4
+ }
5
+ function convertValueToPercentage(value, min, max) {
6
+ return 100 / (max - min) * (value - min);
7
+ }
8
+ function getLabel(index, totalValues) {
9
+ if (totalValues > 2) return `Value ${index + 1} of ${totalValues}`;
10
+ if (totalValues === 2) return ["Minimum", "Maximum"][index];
11
+ }
12
+ function getClosestValueIndex(values, nextValue) {
13
+ if (values.length === 1) return 0;
14
+ const distances = values.map(value => Math.abs(value - nextValue)),
15
+ closestDistance = Math.min(...distances);
16
+ return distances.indexOf(closestDistance);
17
+ }
18
+ function getThumbInBoundsOffset(width, left, direction) {
19
+ const quarterWidth = width / 4,
20
+ offset = linearScale([0, 50], [0, quarterWidth]);
21
+ return (quarterWidth - offset(left) * direction) * direction;
22
+ }
23
+ function getStepsBetweenValues(values) {
24
+ return values.slice(0, -1).map((value, index) => values[index + 1] - value);
25
+ }
26
+ function hasMinStepsBetweenValues(values, minStepsBetweenValues) {
27
+ if (minStepsBetweenValues > 0) {
28
+ const stepsBetweenValues = getStepsBetweenValues(values);
29
+ return Math.min(...stepsBetweenValues) >= minStepsBetweenValues;
30
+ }
31
+ return !0;
32
+ }
33
+ function linearScale(input, output) {
34
+ return value => {
35
+ if (input[0] === input[1] || output[0] === output[1]) return output[0];
36
+ const ratio = (output[1] - output[0]) / (input[1] - input[0]);
37
+ return output[0] + ratio * (value - input[0]);
38
+ };
39
+ }
40
+ function getDecimalCount(value) {
41
+ return (String(value).split(".")[1] || "").length;
42
+ }
43
+ function roundValue(value, decimalCount) {
44
+ const rounder = 10 ** decimalCount;
45
+ return Math.round(value * rounder) / rounder;
46
+ }
47
+ export { convertValueToPercentage, getClosestValueIndex, getDecimalCount, getLabel, getNextSortedValues, getThumbInBoundsOffset, hasMinStepsBetweenValues, linearScale, roundValue };
@@ -0,0 +1,3 @@
1
+ export * from "./Slider.mjs";
2
+ import { SliderFrame } from "./SliderImpl.mjs";
3
+ export { SliderFrame };
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tamagui/slider",
3
- "version": "1.88.13",
3
+ "version": "1.89.0-1706308641099",
4
4
  "sideEffects": [
5
5
  "*.css"
6
6
  ],
@@ -32,22 +32,22 @@
32
32
  }
33
33
  },
34
34
  "dependencies": {
35
- "@tamagui/compose-refs": "1.88.13",
36
- "@tamagui/constants": "1.88.13",
37
- "@tamagui/core": "1.88.13",
38
- "@tamagui/create-context": "1.88.13",
39
- "@tamagui/get-token": "1.88.13",
40
- "@tamagui/helpers": "1.88.13",
41
- "@tamagui/stacks": "1.88.13",
42
- "@tamagui/use-controllable-state": "1.88.13",
43
- "@tamagui/use-direction": "1.88.13"
35
+ "@tamagui/compose-refs": "1.89.0-1706308641099",
36
+ "@tamagui/constants": "1.89.0-1706308641099",
37
+ "@tamagui/core": "1.89.0-1706308641099",
38
+ "@tamagui/create-context": "1.89.0-1706308641099",
39
+ "@tamagui/get-token": "1.89.0-1706308641099",
40
+ "@tamagui/helpers": "1.89.0-1706308641099",
41
+ "@tamagui/stacks": "1.89.0-1706308641099",
42
+ "@tamagui/use-controllable-state": "1.89.0-1706308641099",
43
+ "@tamagui/use-direction": "1.89.0-1706308641099"
44
44
  },
45
45
  "peerDependencies": {
46
46
  "react": "*",
47
47
  "react-native": "*"
48
48
  },
49
49
  "devDependencies": {
50
- "@tamagui/build": "1.88.13",
50
+ "@tamagui/build": "1.89.0-1706308641099",
51
51
  "react": "^18.2.0",
52
52
  "react-native": "^0.72.6"
53
53
  },