@funstack/ui 0.1.0-alpha.2 → 0.1.0-alpha.3
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/dist/accordion/accordion-context.mjs.map +1 -1
- package/dist/accordion/accordion-item.mjs +18 -4
- package/dist/accordion/accordion-item.mjs.map +1 -1
- package/dist/accordion/accordion-panel.mjs +2 -3
- package/dist/accordion/accordion-panel.mjs.map +1 -1
- package/dist/accordion/accordion-root.d.mts.map +1 -1
- package/dist/accordion/accordion-root.mjs +32 -14
- package/dist/accordion/accordion-root.mjs.map +1 -1
- package/dist/accordion/accordion-trigger.mjs +4 -5
- package/dist/accordion/accordion-trigger.mjs.map +1 -1
- package/dist/checkbox/checkbox.d.mts +5 -3
- package/dist/checkbox/checkbox.d.mts.map +1 -1
- package/dist/checkbox/checkbox.mjs +25 -9
- package/dist/checkbox/checkbox.mjs.map +1 -1
- package/dist/checkbox-group/checkbox-group-context.mjs.map +1 -1
- package/dist/checkbox-group/checkbox-group-item.d.mts +2 -1
- package/dist/checkbox-group/checkbox-group-item.d.mts.map +1 -1
- package/dist/checkbox-group/checkbox-group-item.mjs +3 -1
- package/dist/checkbox-group/checkbox-group-item.mjs.map +1 -1
- package/dist/checkbox-group/checkbox-group-root.d.mts +5 -2
- package/dist/checkbox-group/checkbox-group-root.d.mts.map +1 -1
- package/dist/checkbox-group/checkbox-group-root.mjs +24 -5
- package/dist/checkbox-group/checkbox-group-root.mjs.map +1 -1
- package/dist/combobox/combobox-context.mjs.map +1 -1
- package/dist/combobox/combobox-input.d.mts +3 -1
- package/dist/combobox/combobox-input.d.mts.map +1 -1
- package/dist/combobox/combobox-input.mjs +16 -2
- package/dist/combobox/combobox-input.mjs.map +1 -1
- package/dist/combobox/combobox-list.mjs +1 -1
- package/dist/combobox/combobox-list.mjs.map +1 -1
- package/dist/combobox/combobox-root.d.mts.map +1 -1
- package/dist/combobox/combobox-root.mjs +19 -4
- package/dist/combobox/combobox-root.mjs.map +1 -1
- package/dist/dialog/dialog-close.d.mts +17 -10
- package/dist/dialog/dialog-close.d.mts.map +1 -1
- package/dist/dialog/dialog-close.mjs +31 -20
- package/dist/dialog/dialog-close.mjs.map +1 -1
- package/dist/dialog/dialog-context.mjs.map +1 -1
- package/dist/dialog/dialog-form.d.mts +12 -5
- package/dist/dialog/dialog-form.d.mts.map +1 -1
- package/dist/dialog/dialog-form.mjs +16 -28
- package/dist/dialog/dialog-form.mjs.map +1 -1
- package/dist/dialog/dialog-popup.d.mts +10 -4
- package/dist/dialog/dialog-popup.d.mts.map +1 -1
- package/dist/dialog/dialog-popup.mjs +106 -41
- package/dist/dialog/dialog-popup.mjs.map +1 -1
- package/dist/dialog/dialog-root.d.mts +14 -8
- package/dist/dialog/dialog-root.d.mts.map +1 -1
- package/dist/dialog/dialog-root.mjs +66 -11
- package/dist/dialog/dialog-root.mjs.map +1 -1
- package/dist/dialog/dialog-trigger.d.mts +3 -2
- package/dist/dialog/dialog-trigger.d.mts.map +1 -1
- package/dist/dialog/dialog-trigger.mjs.map +1 -1
- package/dist/dialog/index.d.mts +4 -2
- package/dist/dialog/index.d.mts.map +1 -1
- package/dist/dialog/index.mjs +4 -2
- package/dist/dialog/index.mjs.map +1 -1
- package/dist/field/field-control.d.mts +33 -0
- package/dist/field/field-control.d.mts.map +1 -0
- package/dist/field/field-control.mjs +68 -0
- package/dist/field/field-control.mjs.map +1 -0
- package/dist/field/field-description.d.mts +24 -0
- package/dist/field/field-description.d.mts.map +1 -0
- package/dist/field/field-description.mjs +34 -0
- package/dist/field/field-description.mjs.map +1 -0
- package/dist/field/field-error.d.mts +30 -0
- package/dist/field/field-error.d.mts.map +1 -0
- package/dist/field/field-error.mjs +42 -0
- package/dist/field/field-error.mjs.map +1 -0
- package/dist/field/field-label.d.mts +25 -0
- package/dist/field/field-label.d.mts.map +1 -0
- package/dist/field/field-label.mjs +46 -0
- package/dist/field/field-label.mjs.map +1 -0
- package/dist/field/field-root.d.mts +53 -0
- package/dist/field/field-root.d.mts.map +1 -0
- package/dist/field/field-root.mjs +317 -0
- package/dist/field/field-root.mjs.map +1 -0
- package/dist/field/index.d.mts +18 -0
- package/dist/field/index.d.mts.map +1 -0
- package/dist/field/index.mjs +17 -0
- package/dist/field/index.mjs.map +1 -0
- package/dist/form/form-root.d.mts +58 -0
- package/dist/form/form-root.d.mts.map +1 -0
- package/dist/form/form-root.mjs +146 -0
- package/dist/form/form-root.mjs.map +1 -0
- package/dist/form/form-submit.d.mts +31 -0
- package/dist/form/form-submit.d.mts.map +1 -0
- package/dist/form/form-submit.mjs +52 -0
- package/dist/form/form-submit.mjs.map +1 -0
- package/dist/form/index.d.mts +11 -0
- package/dist/form/index.d.mts.map +1 -0
- package/dist/form/index.mjs +11 -0
- package/dist/form/index.mjs.map +1 -0
- package/dist/index.d.mts +27 -1
- package/dist/index.mjs +6 -1
- package/dist/internal/content-focus-probe.mjs +42 -0
- package/dist/internal/content-focus-probe.mjs.map +1 -0
- package/dist/internal/field-context.d.mts +19 -0
- package/dist/internal/field-context.d.mts.map +1 -0
- package/dist/internal/field-context.mjs +14 -0
- package/dist/internal/field-context.mjs.map +1 -0
- package/dist/internal/focus-first-invalid.mjs +67 -0
- package/dist/internal/focus-first-invalid.mjs.map +1 -0
- package/dist/internal/form-status-watcher.mjs +31 -0
- package/dist/internal/form-status-watcher.mjs.map +1 -0
- package/dist/internal/modal-registry.mjs +39 -0
- package/dist/internal/modal-registry.mjs.map +1 -0
- package/dist/internal/types.d.mts +3 -1
- package/dist/internal/types.d.mts.map +1 -1
- package/dist/internal/use-controllable-action.mjs +33 -15
- package/dist/internal/use-controllable-action.mjs.map +1 -1
- package/dist/internal/use-field-control.mjs +107 -0
- package/dist/internal/use-field-control.mjs.map +1 -0
- package/dist/internal/use-form-owner-event.mjs +44 -0
- package/dist/internal/use-form-owner-event.mjs.map +1 -0
- package/dist/internal/use-form-reset.mjs +10 -33
- package/dist/internal/use-form-reset.mjs.map +1 -1
- package/dist/internal/use-latest.mjs +32 -0
- package/dist/internal/use-latest.mjs.map +1 -0
- package/dist/internal/use-modal-registration.mjs +47 -0
- package/dist/internal/use-modal-registration.mjs.map +1 -0
- package/dist/internal/use-native-group-reconciliation.mjs +6 -5
- package/dist/internal/use-native-group-reconciliation.mjs.map +1 -1
- package/dist/internal/use-native-select-value.mjs +7 -5
- package/dist/internal/use-native-select-value.mjs.map +1 -1
- package/dist/internal/use-popover-popup.mjs +8 -22
- package/dist/internal/use-popover-popup.mjs.map +1 -1
- package/dist/internal/use-popover-root.mjs +58 -23
- package/dist/internal/use-popover-root.mjs.map +1 -1
- package/dist/internal/validity.d.mts +24 -0
- package/dist/internal/validity.d.mts.map +1 -0
- package/dist/internal/validity.mjs +108 -0
- package/dist/internal/validity.mjs.map +1 -0
- package/dist/menu/menu-group.d.mts.map +1 -1
- package/dist/menu/menu-group.mjs +3 -2
- package/dist/menu/menu-group.mjs.map +1 -1
- package/dist/menu/menu-radio-group.mjs +9 -4
- package/dist/menu/menu-radio-group.mjs.map +1 -1
- package/dist/menu/menu-root.mjs.map +1 -1
- package/dist/radio-group/radio-group-root.d.mts +4 -2
- package/dist/radio-group/radio-group-root.d.mts.map +1 -1
- package/dist/radio-group/radio-group-root.mjs +34 -8
- package/dist/radio-group/radio-group-root.mjs.map +1 -1
- package/dist/select/select-group.d.mts.map +1 -1
- package/dist/select/select-group.mjs +6 -4
- package/dist/select/select-group.mjs.map +1 -1
- package/dist/select/select-root.d.mts +4 -2
- package/dist/select/select-root.d.mts.map +1 -1
- package/dist/select/select-root.mjs +26 -6
- package/dist/select/select-root.mjs.map +1 -1
- package/dist/slider/index.d.mts +32 -0
- package/dist/slider/index.d.mts.map +1 -0
- package/dist/slider/index.mjs +27 -0
- package/dist/slider/index.mjs.map +1 -0
- package/dist/slider/slider-arithmetic.mjs +120 -0
- package/dist/slider/slider-arithmetic.mjs.map +1 -0
- package/dist/slider/slider-context.mjs +13 -0
- package/dist/slider/slider-context.mjs.map +1 -0
- package/dist/slider/slider-indicator.d.mts +22 -0
- package/dist/slider/slider-indicator.d.mts.map +1 -0
- package/dist/slider/slider-indicator.mjs +36 -0
- package/dist/slider/slider-indicator.mjs.map +1 -0
- package/dist/slider/slider-label.d.mts +23 -0
- package/dist/slider/slider-label.d.mts.map +1 -0
- package/dist/slider/slider-label.mjs +44 -0
- package/dist/slider/slider-label.mjs.map +1 -0
- package/dist/slider/slider-root.d.mts +111 -0
- package/dist/slider/slider-root.d.mts.map +1 -0
- package/dist/slider/slider-root.mjs +582 -0
- package/dist/slider/slider-root.mjs.map +1 -0
- package/dist/slider/slider-thumb.d.mts +58 -0
- package/dist/slider/slider-thumb.d.mts.map +1 -0
- package/dist/slider/slider-thumb.mjs +145 -0
- package/dist/slider/slider-thumb.mjs.map +1 -0
- package/dist/slider/slider-track.d.mts +29 -0
- package/dist/slider/slider-track.d.mts.map +1 -0
- package/dist/slider/slider-track.mjs +54 -0
- package/dist/slider/slider-track.mjs.map +1 -0
- package/dist/slider/slider-value.d.mts +34 -0
- package/dist/slider/slider-value.d.mts.map +1 -0
- package/dist/slider/slider-value.mjs +38 -0
- package/dist/slider/slider-value.mjs.map +1 -0
- package/dist/switch/switch.d.mts +5 -3
- package/dist/switch/switch.d.mts.map +1 -1
- package/dist/switch/switch.mjs +24 -8
- package/dist/switch/switch.mjs.map +1 -1
- package/dist/tabs/tabs-root.mjs +13 -3
- package/dist/tabs/tabs-root.mjs.map +1 -1
- package/dist/toast/index.d.mts +25 -0
- package/dist/toast/index.d.mts.map +1 -0
- package/dist/toast/index.mjs +26 -0
- package/dist/toast/index.mjs.map +1 -0
- package/dist/toast/toast-action.d.mts +39 -0
- package/dist/toast/toast-action.d.mts.map +1 -0
- package/dist/toast/toast-action.mjs +64 -0
- package/dist/toast/toast-action.mjs.map +1 -0
- package/dist/toast/toast-close.d.mts +25 -0
- package/dist/toast/toast-close.d.mts.map +1 -0
- package/dist/toast/toast-close.mjs +46 -0
- package/dist/toast/toast-close.mjs.map +1 -0
- package/dist/toast/toast-context.mjs +19 -0
- package/dist/toast/toast-context.mjs.map +1 -0
- package/dist/toast/toast-description.d.mts +24 -0
- package/dist/toast/toast-description.d.mts.map +1 -0
- package/dist/toast/toast-description.mjs +35 -0
- package/dist/toast/toast-description.mjs.map +1 -0
- package/dist/toast/toast-root.d.mts +43 -0
- package/dist/toast/toast-root.d.mts.map +1 -0
- package/dist/toast/toast-root.mjs +204 -0
- package/dist/toast/toast-root.mjs.map +1 -0
- package/dist/toast/toast-title.d.mts +24 -0
- package/dist/toast/toast-title.d.mts.map +1 -0
- package/dist/toast/toast-title.mjs +33 -0
- package/dist/toast/toast-title.mjs.map +1 -0
- package/dist/toast/toast-viewport.d.mts +52 -0
- package/dist/toast/toast-viewport.d.mts.map +1 -0
- package/dist/toast/toast-viewport.mjs +621 -0
- package/dist/toast/toast-viewport.mjs.map +1 -0
- package/dist/toast/toast.d.mts +119 -0
- package/dist/toast/toast.d.mts.map +1 -0
- package/dist/toast/toast.mjs +153 -0
- package/dist/toast/toast.mjs.map +1 -0
- package/package.json +5 -1
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
//#region src/slider/slider-arithmetic.ts
|
|
2
|
+
/** Number of decimal places of `n` as written (`0.1` → 1, `1e-7` → 7, `5` → 0). */
|
|
3
|
+
function decimalsOf(n) {
|
|
4
|
+
if (!Number.isFinite(n)) return 0;
|
|
5
|
+
const text = String(n);
|
|
6
|
+
const exponent = /e-(\d+)$/i.exec(text);
|
|
7
|
+
if (exponent !== null) {
|
|
8
|
+
const mantissa = text.slice(0, exponent.index);
|
|
9
|
+
const dot = mantissa.indexOf(".");
|
|
10
|
+
return Number(exponent[1]) + (dot === -1 ? 0 : mantissa.length - dot - 1);
|
|
11
|
+
}
|
|
12
|
+
const dot = text.indexOf(".");
|
|
13
|
+
return dot === -1 ? 0 : text.length - dot - 1;
|
|
14
|
+
}
|
|
15
|
+
function roundTo(value, decimals) {
|
|
16
|
+
if (decimals <= 0) return Math.round(value);
|
|
17
|
+
return Number(value.toFixed(Math.min(decimals, 100)));
|
|
18
|
+
}
|
|
19
|
+
/** Tolerance for grid computations on binary floating point (`0.3 / 0.1` is `2.9999999999999996`). */
|
|
20
|
+
const EPSILON = 1e-9;
|
|
21
|
+
/**
|
|
22
|
+
* The value a native range with these bounds would hold for `value`: snapped to
|
|
23
|
+
* `min + n × step` (ties toward +∞, the HTML step-mismatch rule), clamped to `[min, max]`, and,
|
|
24
|
+
* when the snapped value exceeds `max`, the last grid point below `max` (so `min 0, max 5,
|
|
25
|
+
* step 2` caps at `4`). Rounded to the decimal places of `step` and `min`. With `max < min` the
|
|
26
|
+
* platform's maximum is `min`.
|
|
27
|
+
*/
|
|
28
|
+
function snap(value, min, max, step) {
|
|
29
|
+
const decimals = Math.max(decimalsOf(step), decimalsOf(min));
|
|
30
|
+
if (!Number.isFinite(value)) return min;
|
|
31
|
+
if (!(max > min)) return roundTo(min, decimals);
|
|
32
|
+
const lastIndex = Math.floor((max - min) / step + EPSILON);
|
|
33
|
+
let index = Math.floor((value - min) / step + .5 + EPSILON);
|
|
34
|
+
if (index > lastIndex) index = lastIndex;
|
|
35
|
+
if (index < 0) index = 0;
|
|
36
|
+
return roundTo(min + index * step, decimals);
|
|
37
|
+
}
|
|
38
|
+
/** Whether `max` is reachable: `max - min` is a whole number of steps. */
|
|
39
|
+
function isMaxReachable(min, max, step) {
|
|
40
|
+
if (!(max > min)) return true;
|
|
41
|
+
const steps = (max - min) / step;
|
|
42
|
+
return Math.abs(steps - Math.round(steps)) < EPSILON * Math.max(1, steps);
|
|
43
|
+
}
|
|
44
|
+
/** Whether `value` is a value a native range with these bounds can hold unchanged. */
|
|
45
|
+
function isOnGrid(value, min, max, step) {
|
|
46
|
+
return Math.abs(snap(value, min, max, step) - value) < EPSILON * Math.max(1, Math.abs(value));
|
|
47
|
+
}
|
|
48
|
+
/** The native `min` / `max` of thumb `index`: the slider's bounds, narrowed by its neighbours. */
|
|
49
|
+
function thumbBounds(values, index, min, max, gap) {
|
|
50
|
+
const decimals = Math.max(decimalsOf(gap), decimalsOf(min));
|
|
51
|
+
const before = values[index - 1];
|
|
52
|
+
const after = values[index + 1];
|
|
53
|
+
return {
|
|
54
|
+
lower: before === void 0 ? min : roundTo(before + gap, decimals),
|
|
55
|
+
upper: after === void 0 ? max : roundTo(after - gap, decimals)
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* `next` for thumb `index`, snapped to the slider's grid and clamped between its neighbours
|
|
60
|
+
* (`values[i - 1] + gap`, `values[i + 1] - gap`; on the grid, because every thumb shares `step`
|
|
61
|
+
* and `min`). With no room between the neighbours the thumb keeps its value.
|
|
62
|
+
*/
|
|
63
|
+
function clampToNeighbours(values, index, next, min, max, step, gap) {
|
|
64
|
+
const { lower, upper } = thumbBounds(values, index, min, max, gap);
|
|
65
|
+
const current = values[index] ?? next;
|
|
66
|
+
if (lower > upper + EPSILON) return current;
|
|
67
|
+
return Math.min(Math.max(snap(next, min, max, step), lower), upper);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* The value a native input with these attributes would hold for `value` (each thumb's bounds come
|
|
71
|
+
* from `reference`, the values it is rendered against). Used to compare a DOM read with the shown
|
|
72
|
+
* value: jsdom does not sanitise a range's value, browsers do, and a consumer's off-grid value is
|
|
73
|
+
* rendered as given and sanitised by the DOM.
|
|
74
|
+
*/
|
|
75
|
+
function sanitize(values, reference, min, max, step, gap) {
|
|
76
|
+
return values.map((value, index) => {
|
|
77
|
+
const { lower, upper } = thumbBounds(reference, index, min, max, gap);
|
|
78
|
+
return snap(value, lower, Math.max(lower, upper), step);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
/** Position of `value` in `[min, max]`, in percent, clamped to `0`–`100`. */
|
|
82
|
+
function percent(value, min, max) {
|
|
83
|
+
if (!(max > min)) return 0;
|
|
84
|
+
const fraction = (value - min) / (max - min);
|
|
85
|
+
return Math.min(100, Math.max(0, fraction * 100));
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* The thumb a track press at `target` moves: the nearest by value. On a tie (two thumbs at one
|
|
89
|
+
* value, or equidistant thumbs) the thumb that can move toward the pointer: the later one when
|
|
90
|
+
* the pointer is above it, else the earlier one.
|
|
91
|
+
*/
|
|
92
|
+
function nearestThumb(values, target) {
|
|
93
|
+
let best = 0;
|
|
94
|
+
let bestDistance = Number.POSITIVE_INFINITY;
|
|
95
|
+
values.forEach((value, index) => {
|
|
96
|
+
const distance = Math.abs(value - target);
|
|
97
|
+
if (distance < bestDistance - EPSILON) {
|
|
98
|
+
best = index;
|
|
99
|
+
bestDistance = distance;
|
|
100
|
+
} else if (Math.abs(distance - bestDistance) <= EPSILON && target > value) best = index;
|
|
101
|
+
});
|
|
102
|
+
return best;
|
|
103
|
+
}
|
|
104
|
+
/** The platform's default value: the midpoint, snapped to the grid; `min` when `max < min`. */
|
|
105
|
+
function platformDefault(min, max, step) {
|
|
106
|
+
if (!(max > min)) return min;
|
|
107
|
+
return snap(min + (max - min) / 2, min, max, step);
|
|
108
|
+
}
|
|
109
|
+
/** Element-wise equality of two value arrays (within floating-point tolerance). */
|
|
110
|
+
function equalValues(a, b) {
|
|
111
|
+
if (a.length !== b.length) return false;
|
|
112
|
+
return a.every((value, index) => {
|
|
113
|
+
const other = b[index];
|
|
114
|
+
return value === other || Math.abs(value - other) < EPSILON * Math.max(1, Math.abs(value));
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
//#endregion
|
|
118
|
+
export { clampToNeighbours, decimalsOf, equalValues, isMaxReachable, isOnGrid, nearestThumb, percent, platformDefault, sanitize, snap, thumbBounds };
|
|
119
|
+
|
|
120
|
+
//# sourceMappingURL=slider-arithmetic.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slider-arithmetic.mjs","names":[],"sources":["../../src/slider/slider-arithmetic.ts"],"sourcesContent":["// Pure value arithmetic of the Slider (design/components/slider.md 5). No directive: no React,\n// no client-only API. The library computes exactly what the platform's range sanitisation would,\n// so the number it renders is the number the DOM holds and `FormData` posts.\n\n/** Number of decimal places of `n` as written (`0.1` → 1, `1e-7` → 7, `5` → 0). */\nexport function decimalsOf(n: number): number {\n if (!Number.isFinite(n)) return 0;\n const text = String(n);\n const exponent = /e-(\\d+)$/i.exec(text);\n if (exponent !== null) {\n const mantissa = text.slice(0, exponent.index);\n const dot = mantissa.indexOf(\".\");\n return Number(exponent[1]) + (dot === -1 ? 0 : mantissa.length - dot - 1);\n }\n const dot = text.indexOf(\".\");\n return dot === -1 ? 0 : text.length - dot - 1;\n}\n\nfunction roundTo(value: number, decimals: number): number {\n if (decimals <= 0) return Math.round(value);\n // `toFixed` takes at most 100 digits; anything near that is already exact.\n return Number(value.toFixed(Math.min(decimals, 100)));\n}\n\n/** Tolerance for grid computations on binary floating point (`0.3 / 0.1` is `2.9999999999999996`). */\nconst EPSILON = 1e-9;\n\n/**\n * The value a native range with these bounds would hold for `value`: snapped to\n * `min + n × step` (ties toward +∞, the HTML step-mismatch rule), clamped to `[min, max]`, and,\n * when the snapped value exceeds `max`, the last grid point below `max` (so `min 0, max 5,\n * step 2` caps at `4`). Rounded to the decimal places of `step` and `min`. With `max < min` the\n * platform's maximum is `min`.\n */\nexport function snap(value: number, min: number, max: number, step: number): number {\n const decimals = Math.max(decimalsOf(step), decimalsOf(min));\n if (!Number.isFinite(value)) return min;\n if (!(max > min)) return roundTo(min, decimals);\n const lastIndex = Math.floor((max - min) / step + EPSILON);\n let index = Math.floor((value - min) / step + 0.5 + EPSILON);\n if (index > lastIndex) index = lastIndex;\n if (index < 0) index = 0;\n return roundTo(min + index * step, decimals);\n}\n\n/** Whether `max` is reachable: `max - min` is a whole number of steps. */\nexport function isMaxReachable(min: number, max: number, step: number): boolean {\n if (!(max > min)) return true;\n const steps = (max - min) / step;\n return Math.abs(steps - Math.round(steps)) < EPSILON * Math.max(1, steps);\n}\n\n/** Whether `value` is a value a native range with these bounds can hold unchanged. */\nexport function isOnGrid(value: number, min: number, max: number, step: number): boolean {\n return Math.abs(snap(value, min, max, step) - value) < EPSILON * Math.max(1, Math.abs(value));\n}\n\n/** The native `min` / `max` of thumb `index`: the slider's bounds, narrowed by its neighbours. */\nexport function thumbBounds(\n values: readonly number[],\n index: number,\n min: number,\n max: number,\n gap: number,\n): { readonly lower: number; readonly upper: number } {\n const decimals = Math.max(decimalsOf(gap), decimalsOf(min));\n const before = values[index - 1];\n const after = values[index + 1];\n const lower = before === undefined ? min : roundTo(before + gap, decimals);\n const upper = after === undefined ? max : roundTo(after - gap, decimals);\n return { lower, upper };\n}\n\n/**\n * `next` for thumb `index`, snapped to the slider's grid and clamped between its neighbours\n * (`values[i - 1] + gap`, `values[i + 1] - gap`; on the grid, because every thumb shares `step`\n * and `min`). With no room between the neighbours the thumb keeps its value.\n */\nexport function clampToNeighbours(\n values: readonly number[],\n index: number,\n next: number,\n min: number,\n max: number,\n step: number,\n gap: number,\n): number {\n const { lower, upper } = thumbBounds(values, index, min, max, gap);\n const current = values[index] ?? next;\n if (lower > upper + EPSILON) return current;\n return Math.min(Math.max(snap(next, min, max, step), lower), upper);\n}\n\n/**\n * The value a native input with these attributes would hold for `value` (each thumb's bounds come\n * from `reference`, the values it is rendered against). Used to compare a DOM read with the shown\n * value: jsdom does not sanitise a range's value, browsers do, and a consumer's off-grid value is\n * rendered as given and sanitised by the DOM.\n */\nexport function sanitize(\n values: readonly number[],\n reference: readonly number[],\n min: number,\n max: number,\n step: number,\n gap: number,\n): readonly number[] {\n return values.map((value, index) => {\n const { lower, upper } = thumbBounds(reference, index, min, max, gap);\n // The native step base is the input's own `min` attribute (the neighbour-adjusted bound).\n return snap(value, lower, Math.max(lower, upper), step);\n });\n}\n\n/** Position of `value` in `[min, max]`, in percent, clamped to `0`–`100`. */\nexport function percent(value: number, min: number, max: number): number {\n if (!(max > min)) return 0;\n const fraction = (value - min) / (max - min);\n return Math.min(100, Math.max(0, fraction * 100));\n}\n\n/**\n * The thumb a track press at `target` moves: the nearest by value. On a tie (two thumbs at one\n * value, or equidistant thumbs) the thumb that can move toward the pointer: the later one when\n * the pointer is above it, else the earlier one.\n */\nexport function nearestThumb(values: readonly number[], target: number): number {\n let best = 0;\n let bestDistance = Number.POSITIVE_INFINITY;\n values.forEach((value, index) => {\n const distance = Math.abs(value - target);\n if (distance < bestDistance - EPSILON) {\n best = index;\n bestDistance = distance;\n } else if (Math.abs(distance - bestDistance) <= EPSILON && target > value) {\n best = index;\n }\n });\n return best;\n}\n\n/** The platform's default value: the midpoint, snapped to the grid; `min` when `max < min`. */\nexport function platformDefault(min: number, max: number, step: number): number {\n if (!(max > min)) return min;\n return snap(min + (max - min) / 2, min, max, step);\n}\n\n/** Element-wise equality of two value arrays (within floating-point tolerance). */\nexport function equalValues(a: readonly number[], b: readonly number[]): boolean {\n if (a.length !== b.length) return false;\n return a.every((value, index) => {\n const other = b[index] as number;\n return value === other || Math.abs(value - other) < EPSILON * Math.max(1, Math.abs(value));\n });\n}\n"],"mappings":";;AAKA,SAAgB,WAAW,GAAmB;CAC5C,IAAI,CAAC,OAAO,SAAS,CAAC,GAAG,OAAO;CAChC,MAAM,OAAO,OAAO,CAAC;CACrB,MAAM,WAAW,YAAY,KAAK,IAAI;CACtC,IAAI,aAAa,MAAM;EACrB,MAAM,WAAW,KAAK,MAAM,GAAG,SAAS,KAAK;EAC7C,MAAM,MAAM,SAAS,QAAQ,GAAG;EAChC,OAAO,OAAO,SAAS,EAAE,KAAK,QAAQ,KAAK,IAAI,SAAS,SAAS,MAAM;CACzE;CACA,MAAM,MAAM,KAAK,QAAQ,GAAG;CAC5B,OAAO,QAAQ,KAAK,IAAI,KAAK,SAAS,MAAM;AAC9C;AAEA,SAAS,QAAQ,OAAe,UAA0B;CACxD,IAAI,YAAY,GAAG,OAAO,KAAK,MAAM,KAAK;CAE1C,OAAO,OAAO,MAAM,QAAQ,KAAK,IAAI,UAAU,GAAG,CAAC,CAAC;AACtD;;AAGA,MAAM,UAAU;;;;;;;;AAShB,SAAgB,KAAK,OAAe,KAAa,KAAa,MAAsB;CAClF,MAAM,WAAW,KAAK,IAAI,WAAW,IAAI,GAAG,WAAW,GAAG,CAAC;CAC3D,IAAI,CAAC,OAAO,SAAS,KAAK,GAAG,OAAO;CACpC,IAAI,EAAE,MAAM,MAAM,OAAO,QAAQ,KAAK,QAAQ;CAC9C,MAAM,YAAY,KAAK,OAAO,MAAM,OAAO,OAAO,OAAO;CACzD,IAAI,QAAQ,KAAK,OAAO,QAAQ,OAAO,OAAO,KAAM,OAAO;CAC3D,IAAI,QAAQ,WAAW,QAAQ;CAC/B,IAAI,QAAQ,GAAG,QAAQ;CACvB,OAAO,QAAQ,MAAM,QAAQ,MAAM,QAAQ;AAC7C;;AAGA,SAAgB,eAAe,KAAa,KAAa,MAAuB;CAC9E,IAAI,EAAE,MAAM,MAAM,OAAO;CACzB,MAAM,SAAS,MAAM,OAAO;CAC5B,OAAO,KAAK,IAAI,QAAQ,KAAK,MAAM,KAAK,CAAC,IAAI,UAAU,KAAK,IAAI,GAAG,KAAK;AAC1E;;AAGA,SAAgB,SAAS,OAAe,KAAa,KAAa,MAAuB;CACvF,OAAO,KAAK,IAAI,KAAK,OAAO,KAAK,KAAK,IAAI,IAAI,KAAK,IAAI,UAAU,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,CAAC;AAC9F;;AAGA,SAAgB,YACd,QACA,OACA,KACA,KACA,KACoD;CACpD,MAAM,WAAW,KAAK,IAAI,WAAW,GAAG,GAAG,WAAW,GAAG,CAAC;CAC1D,MAAM,SAAS,OAAO,QAAQ;CAC9B,MAAM,QAAQ,OAAO,QAAQ;CAG7B,OAAO;EAAE,OAFK,WAAW,KAAA,IAAY,MAAM,QAAQ,SAAS,KAAK,QAAQ;EAEzD,OADF,UAAU,KAAA,IAAY,MAAM,QAAQ,QAAQ,KAAK,QAAQ;CACjD;AACxB;;;;;;AAOA,SAAgB,kBACd,QACA,OACA,MACA,KACA,KACA,MACA,KACQ;CACR,MAAM,EAAE,OAAO,UAAU,YAAY,QAAQ,OAAO,KAAK,KAAK,GAAG;CACjE,MAAM,UAAU,OAAO,UAAU;CACjC,IAAI,QAAQ,QAAQ,SAAS,OAAO;CACpC,OAAO,KAAK,IAAI,KAAK,IAAI,KAAK,MAAM,KAAK,KAAK,IAAI,GAAG,KAAK,GAAG,KAAK;AACpE;;;;;;;AAQA,SAAgB,SACd,QACA,WACA,KACA,KACA,MACA,KACmB;CACnB,OAAO,OAAO,KAAK,OAAO,UAAU;EAClC,MAAM,EAAE,OAAO,UAAU,YAAY,WAAW,OAAO,KAAK,KAAK,GAAG;EAEpE,OAAO,KAAK,OAAO,OAAO,KAAK,IAAI,OAAO,KAAK,GAAG,IAAI;CACxD,CAAC;AACH;;AAGA,SAAgB,QAAQ,OAAe,KAAa,KAAqB;CACvE,IAAI,EAAE,MAAM,MAAM,OAAO;CACzB,MAAM,YAAY,QAAQ,QAAQ,MAAM;CACxC,OAAO,KAAK,IAAI,KAAK,KAAK,IAAI,GAAG,WAAW,GAAG,CAAC;AAClD;;;;;;AAOA,SAAgB,aAAa,QAA2B,QAAwB;CAC9E,IAAI,OAAO;CACX,IAAI,eAAe,OAAO;CAC1B,OAAO,SAAS,OAAO,UAAU;EAC/B,MAAM,WAAW,KAAK,IAAI,QAAQ,MAAM;EACxC,IAAI,WAAW,eAAe,SAAS;GACrC,OAAO;GACP,eAAe;EACjB,OAAO,IAAI,KAAK,IAAI,WAAW,YAAY,KAAK,WAAW,SAAS,OAClE,OAAO;CAEX,CAAC;CACD,OAAO;AACT;;AAGA,SAAgB,gBAAgB,KAAa,KAAa,MAAsB;CAC9E,IAAI,EAAE,MAAM,MAAM,OAAO;CACzB,OAAO,KAAK,OAAO,MAAM,OAAO,GAAG,KAAK,KAAK,IAAI;AACnD;;AAGA,SAAgB,YAAY,GAAsB,GAA+B;CAC/E,IAAI,EAAE,WAAW,EAAE,QAAQ,OAAO;CAClC,OAAO,EAAE,OAAO,OAAO,UAAU;EAC/B,MAAM,QAAQ,EAAE;EAChB,OAAO,UAAU,SAAS,KAAK,IAAI,QAAQ,KAAK,IAAI,UAAU,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,CAAC;CAC3F,CAAC;AACH"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { createContext, use } from "react";
|
|
3
|
+
//#region src/slider/slider-context.ts
|
|
4
|
+
const SliderContext = createContext(null);
|
|
5
|
+
function useSliderContext(part) {
|
|
6
|
+
const context = use(SliderContext);
|
|
7
|
+
if (context === null) throw new Error(`${part} must be rendered inside a Slider.Root.`);
|
|
8
|
+
return context;
|
|
9
|
+
}
|
|
10
|
+
//#endregion
|
|
11
|
+
export { SliderContext, useSliderContext };
|
|
12
|
+
|
|
13
|
+
//# sourceMappingURL=slider-context.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slider-context.mjs","names":[],"sources":["../../src/slider/slider-context.ts"],"sourcesContent":["\"use client\";\nimport { createContext, use } from \"react\";\nimport type { RefCallback, RefObject } from \"react\";\nimport type { SliderOrientation, SliderRootState, SliderValue } from \"./slider-root.js\";\n\n/** What the Root routes from an enclosing `Field.Root` to its Thumbs (design 9.4.1). */\nexport interface SliderFieldWiring {\n /** One thumb only: Thumb `index` 0 merges it into its input ref (registers the input). */\n readonly inputRef: RefCallback<HTMLElement> | undefined;\n /** One thumb only: token-merged into Thumb 0's input `aria-describedby`. */\n readonly describedBy: string | undefined;\n /** On every Thumb's input (ARIA 1.2: never on the group element). */\n readonly invalid: \"true\" | \"false\" | undefined;\n /** On every Thumb's styled element. */\n readonly dataInvalid: \"\" | undefined;\n /** The Field's own `name`, for the Thumb's \"name differs from the Field's\" warning. */\n readonly name: string | undefined;\n}\n\nexport interface SliderContextValue {\n /** The Root's state, for `Slider.Label` and `Slider.Value`. */\n readonly state: SliderRootState<SliderValue>;\n /** The values the thumbs show (design 2.2): `live` while held, else optimistic. */\n readonly shown: readonly number[];\n /** `shown` in the prop's shape (`number` or array). */\n readonly shownValue: number | readonly number[];\n readonly min: number;\n readonly max: number;\n readonly step: number;\n /** `minStepsBetweenThumbs × step`. */\n readonly gap: number;\n readonly orientation: SliderOrientation;\n readonly disabled: boolean;\n /** A step or commit dispatched through this Slider is in flight. */\n readonly pending: boolean;\n /** The thumb a pointer is captured on, or `null`. */\n readonly draggingThumb: number | null;\n /** The thumb the latest step or commit moved, or `null` (platform dispatches). */\n readonly pendingThumb: number | null;\n readonly name: string | undefined;\n readonly form: string | undefined;\n readonly getAriaValueText: ((value: number, index: number) => string) | undefined;\n readonly labelId: string;\n readonly thumbId: (index: number) => string;\n /** The Track's element: the geometry reference for pointer math. */\n readonly trackRef: RefObject<HTMLElement | null>;\n\n /** The native `input` of thumb `index` (React `onChange`): one step. */\n readonly inputStep: (index: number, value: number, event: Event) => void;\n /** The native `change` of thumb `index`: the key press's commit. */\n readonly inputCommit: (index: number, event: Event) => void;\n /** `keydown` on thumb `index`: ArrowLeft / ArrowRight normalisation of a vertical slider. */\n readonly inputKeyDown: (index: number, event: KeyboardEvent) => void;\n /** `pointerdown` on a Thumb (`index`) or on the Track (`null`: the nearest thumb). */\n readonly beginGesture: (index: number | null, event: PointerEvent, element: HTMLElement) => void;\n /** Called by each Thumb from a layout effect; returns the unregistration. */\n readonly registerThumb: (index: number, input: HTMLInputElement) => () => void;\n /** Called by each Label from a layout effect; returns the unregistration. */\n readonly registerLabel: () => () => void;\n /** The enclosing Field's wiring (design 9.4), `null` outside a `Field.Root`. */\n readonly field: SliderFieldWiring | null;\n /** Late-mounting Thumbs report here (useNativeGroupReconciliation). */\n readonly onItemMounted: () => void;\n}\n\nexport const SliderContext = createContext<SliderContextValue | null>(null);\n\nexport function useSliderContext(part: string): SliderContextValue {\n const context = use(SliderContext);\n if (context === null) {\n throw new Error(`${part} must be rendered inside a Slider.Root.`);\n }\n return context;\n}\n"],"mappings":";;;AAiEA,MAAa,gBAAgB,cAAyC,IAAI;AAE1E,SAAgB,iBAAiB,MAAkC;CACjE,MAAM,UAAU,IAAI,aAAa;CACjC,IAAI,YAAY,MACd,MAAM,IAAI,MAAM,GAAG,KAAK,wCAAwC;CAElE,OAAO;AACT"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { RenderProp, StateValue } from "../internal/types.mjs";
|
|
2
|
+
import { SliderTrackState } from "./slider-track.mjs";
|
|
3
|
+
import { CSSProperties, ComponentPropsWithRef, ReactNode } from "react";
|
|
4
|
+
//#region src/slider/slider-indicator.d.ts
|
|
5
|
+
export type SliderIndicatorState = SliderTrackState;
|
|
6
|
+
export interface SliderIndicatorProps extends Omit<ComponentPropsWithRef<"span">, "className" | "style" | "children"> {
|
|
7
|
+
className?: StateValue<string | undefined, SliderIndicatorState>;
|
|
8
|
+
style?: StateValue<CSSProperties | undefined, SliderIndicatorState>;
|
|
9
|
+
children?: StateValue<ReactNode, SliderIndicatorState>;
|
|
10
|
+
render?: RenderProp<ComponentPropsWithRef<"span">, SliderIndicatorState>;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The fill between the first and last thumb (from `min` to the value for one thumb). Renders no
|
|
14
|
+
* inline position: the consumer's CSS places it from the Root's inherited `--slider-start` /
|
|
15
|
+
* `--slider-end`. Owned keys: `data-orientation`, `data-disabled`.
|
|
16
|
+
*/
|
|
17
|
+
export declare function SliderIndicator({ className, style, children, render, ...rest }: SliderIndicatorProps): ReactNode;
|
|
18
|
+
export declare namespace SliderIndicator {
|
|
19
|
+
var displayName: string;
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
22
|
+
//# sourceMappingURL=slider-indicator.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slider-indicator.d.mts","names":[],"sources":["../../src/slider/slider-indicator.tsx"],"mappings":";;;;YAQY,uBAAuB;iBAElB,6BAA6B,KAC5C;EAGA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;EAC9C,WAAW,WAAW,WAAW;EACjC,SAAS,WAAW,+BAA+B;;;;;;;wBAQrC,kBACd,WACA,OACA,UACA,WACG,QACF,uBAAoB;yBANP"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { dataAttr } from "../internal/data-attr.mjs";
|
|
3
|
+
import { useRender } from "../internal/use-render.mjs";
|
|
4
|
+
import { useSliderContext } from "./slider-context.mjs";
|
|
5
|
+
//#region src/slider/slider-indicator.tsx
|
|
6
|
+
/**
|
|
7
|
+
* The fill between the first and last thumb (from `min` to the value for one thumb). Renders no
|
|
8
|
+
* inline position: the consumer's CSS places it from the Root's inherited `--slider-start` /
|
|
9
|
+
* `--slider-end`. Owned keys: `data-orientation`, `data-disabled`.
|
|
10
|
+
*/
|
|
11
|
+
function SliderIndicator({ className, style, children, render, ...rest }) {
|
|
12
|
+
const ctx = useSliderContext("Slider.Indicator");
|
|
13
|
+
const state = {
|
|
14
|
+
orientation: ctx.orientation,
|
|
15
|
+
dragging: ctx.draggingThumb !== null,
|
|
16
|
+
disabled: ctx.disabled
|
|
17
|
+
};
|
|
18
|
+
return useRender({
|
|
19
|
+
defaultElement: "span",
|
|
20
|
+
render,
|
|
21
|
+
state,
|
|
22
|
+
className,
|
|
23
|
+
style,
|
|
24
|
+
children,
|
|
25
|
+
props: rest,
|
|
26
|
+
libraryProps: {
|
|
27
|
+
"data-orientation": ctx.orientation,
|
|
28
|
+
"data-disabled": dataAttr(ctx.disabled)
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
SliderIndicator.displayName = "SliderIndicator";
|
|
33
|
+
//#endregion
|
|
34
|
+
export { SliderIndicator };
|
|
35
|
+
|
|
36
|
+
//# sourceMappingURL=slider-indicator.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slider-indicator.mjs","names":[],"sources":["../../src/slider/slider-indicator.tsx"],"sourcesContent":["\"use client\";\nimport type { ComponentPropsWithRef, CSSProperties, ReactNode } from \"react\";\nimport { dataAttr } from \"../internal/data-attr.js\";\nimport type { RenderProp, StateValue } from \"../internal/types.js\";\nimport { useRender } from \"../internal/use-render.js\";\nimport { useSliderContext } from \"./slider-context.js\";\nimport type { SliderTrackState } from \"./slider-track.js\";\n\nexport type SliderIndicatorState = SliderTrackState;\n\nexport interface SliderIndicatorProps extends Omit<\n ComponentPropsWithRef<\"span\">,\n \"className\" | \"style\" | \"children\"\n> {\n className?: StateValue<string | undefined, SliderIndicatorState>;\n style?: StateValue<CSSProperties | undefined, SliderIndicatorState>;\n children?: StateValue<ReactNode, SliderIndicatorState>;\n render?: RenderProp<ComponentPropsWithRef<\"span\">, SliderIndicatorState>;\n}\n\n/**\n * The fill between the first and last thumb (from `min` to the value for one thumb). Renders no\n * inline position: the consumer's CSS places it from the Root's inherited `--slider-start` /\n * `--slider-end`. Owned keys: `data-orientation`, `data-disabled`.\n */\nexport function SliderIndicator({\n className,\n style,\n children,\n render,\n ...rest\n}: SliderIndicatorProps) {\n const ctx = useSliderContext(\"Slider.Indicator\");\n const state: SliderIndicatorState = {\n orientation: ctx.orientation,\n dragging: ctx.draggingThumb !== null,\n disabled: ctx.disabled,\n };\n return useRender<ComponentPropsWithRef<\"span\">, SliderIndicatorState>({\n defaultElement: \"span\",\n render,\n state,\n className,\n style,\n children,\n props: rest,\n libraryProps: {\n \"data-orientation\": ctx.orientation,\n \"data-disabled\": dataAttr(ctx.disabled),\n },\n });\n}\n\nSliderIndicator.displayName = \"SliderIndicator\";\n"],"mappings":";;;;;;;;;;AAyBA,SAAgB,gBAAgB,EAC9B,WACA,OACA,UACA,QACA,GAAG,QACoB;CACvB,MAAM,MAAM,iBAAiB,kBAAkB;CAC/C,MAAM,QAA8B;EAClC,aAAa,IAAI;EACjB,UAAU,IAAI,kBAAkB;EAChC,UAAU,IAAI;CAChB;CACA,OAAO,UAA+D;EACpE,gBAAgB;EAChB;EACA;EACA;EACA;EACA;EACA,OAAO;EACP,cAAc;GACZ,oBAAoB,IAAI;GACxB,iBAAiB,SAAS,IAAI,QAAQ;EACxC;CACF,CAAC;AACH;AAEA,gBAAgB,cAAc"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { RenderProp, StateValue } from "../internal/types.mjs";
|
|
2
|
+
import { SliderRootState, SliderValue } from "./slider-root.mjs";
|
|
3
|
+
import { CSSProperties, ComponentPropsWithRef, ReactNode } from "react";
|
|
4
|
+
//#region src/slider/slider-label.d.ts
|
|
5
|
+
/** `T` defaults to `SliderValue`; pass it to narrow the state (`<Slider.Label<number>>`). */
|
|
6
|
+
export interface SliderLabelProps<T extends SliderValue = SliderValue> extends Omit<ComponentPropsWithRef<"label">, "htmlFor" | "id" | "className" | "style" | "children"> {
|
|
7
|
+
className?: StateValue<string | undefined, SliderRootState<T>>;
|
|
8
|
+
style?: StateValue<CSSProperties | undefined, SliderRootState<T>>;
|
|
9
|
+
children?: StateValue<ReactNode, SliderRootState<T>>;
|
|
10
|
+
/** Must stay a `<label>` when the slider has one thumb (the `for` association). */
|
|
11
|
+
render?: RenderProp<ComponentPropsWithRef<"label">, SliderRootState<T>>;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* The visible name of the slider: `<label for>` naming the only thumb's input, or, with several
|
|
15
|
+
* thumbs, the element whose `id` the Root's `aria-labelledby` (`role="group"`) points at, emitted
|
|
16
|
+
* only while a Label is mounted. Owned keys: `id`, `htmlFor`.
|
|
17
|
+
*/
|
|
18
|
+
export declare function SliderLabel<T extends SliderValue = SliderValue>({ className, style, children, render, ...rest }: SliderLabelProps<T>): ReactNode;
|
|
19
|
+
export declare namespace SliderLabel {
|
|
20
|
+
var displayName: string;
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
//# sourceMappingURL=slider-label.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slider-label.d.mts","names":[],"sources":["../../src/slider/slider-label.tsx"],"mappings":";;;;;iBAWiB,iBAAiB,UAAU,cAAc,qBAAqB,KAC7E;EAGA,YAAY,+BAA+B,gBAAgB;EAC3D,QAAQ,WAAW,2BAA2B,gBAAgB;EAC9D,WAAW,WAAW,WAAW,gBAAgB;;EAEjD,SAAS,WAAW,gCAAgC,gBAAgB;;;;;;;wBAQtD,YAAY,UAAU,cAAc,eAClD,WACA,OACA,UACA,WACG,QACF,iBAAiB,KAAE;yBANN"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useRender } from "../internal/use-render.mjs";
|
|
3
|
+
import { useSliderContext } from "./slider-context.mjs";
|
|
4
|
+
import { useCallback, useLayoutEffect, useRef } from "react";
|
|
5
|
+
//#region src/slider/slider-label.tsx
|
|
6
|
+
const isDev = process.env.NODE_ENV !== "production";
|
|
7
|
+
/**
|
|
8
|
+
* The visible name of the slider: `<label for>` naming the only thumb's input, or, with several
|
|
9
|
+
* thumbs, the element whose `id` the Root's `aria-labelledby` (`role="group"`) points at, emitted
|
|
10
|
+
* only while a Label is mounted. Owned keys: `id`, `htmlFor`.
|
|
11
|
+
*/
|
|
12
|
+
function SliderLabel({ className, style, children, render, ...rest }) {
|
|
13
|
+
const ctx = useSliderContext("Slider.Label");
|
|
14
|
+
const { registerLabel } = ctx;
|
|
15
|
+
useLayoutEffect(registerLabel, [registerLabel]);
|
|
16
|
+
const single = ctx.shown.length === 1;
|
|
17
|
+
const warnedElement = useRef(false);
|
|
18
|
+
const libraryRef = useCallback((element) => {
|
|
19
|
+
if (isDev && single && !warnedElement.current && element.tagName !== "LABEL") {
|
|
20
|
+
warnedElement.current = true;
|
|
21
|
+
console.error(`Slider.Label: expected a <label> element but got <${element.tagName.toLowerCase()}>. With one thumb the Label names the input through \`for\`, which only a <label> has.`);
|
|
22
|
+
}
|
|
23
|
+
return () => {};
|
|
24
|
+
}, [single]);
|
|
25
|
+
return useRender({
|
|
26
|
+
defaultElement: "label",
|
|
27
|
+
render,
|
|
28
|
+
state: ctx.state,
|
|
29
|
+
className,
|
|
30
|
+
style,
|
|
31
|
+
children,
|
|
32
|
+
props: rest,
|
|
33
|
+
ref: libraryRef,
|
|
34
|
+
libraryProps: {
|
|
35
|
+
id: ctx.labelId,
|
|
36
|
+
htmlFor: single ? ctx.thumbId(0) : void 0
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
SliderLabel.displayName = "SliderLabel";
|
|
41
|
+
//#endregion
|
|
42
|
+
export { SliderLabel };
|
|
43
|
+
|
|
44
|
+
//# sourceMappingURL=slider-label.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slider-label.mjs","names":[],"sources":["../../src/slider/slider-label.tsx"],"sourcesContent":["\"use client\";\nimport { useCallback, useLayoutEffect, useRef } from \"react\";\nimport type { ComponentPropsWithRef, CSSProperties, ReactNode } from \"react\";\nimport type { RenderProp, StateValue } from \"../internal/types.js\";\nimport { useRender } from \"../internal/use-render.js\";\nimport { useSliderContext } from \"./slider-context.js\";\nimport type { SliderRootState, SliderValue } from \"./slider-root.js\";\n\nconst isDev = process.env.NODE_ENV !== \"production\";\n\n/** `T` defaults to `SliderValue`; pass it to narrow the state (`<Slider.Label<number>>`). */\nexport interface SliderLabelProps<T extends SliderValue = SliderValue> extends Omit<\n ComponentPropsWithRef<\"label\">,\n \"htmlFor\" | \"id\" | \"className\" | \"style\" | \"children\"\n> {\n className?: StateValue<string | undefined, SliderRootState<T>>;\n style?: StateValue<CSSProperties | undefined, SliderRootState<T>>;\n children?: StateValue<ReactNode, SliderRootState<T>>;\n /** Must stay a `<label>` when the slider has one thumb (the `for` association). */\n render?: RenderProp<ComponentPropsWithRef<\"label\">, SliderRootState<T>>;\n}\n\n/**\n * The visible name of the slider: `<label for>` naming the only thumb's input, or, with several\n * thumbs, the element whose `id` the Root's `aria-labelledby` (`role=\"group\"`) points at, emitted\n * only while a Label is mounted. Owned keys: `id`, `htmlFor`.\n */\nexport function SliderLabel<T extends SliderValue = SliderValue>({\n className,\n style,\n children,\n render,\n ...rest\n}: SliderLabelProps<T>) {\n const ctx = useSliderContext(\"Slider.Label\");\n const { registerLabel } = ctx;\n useLayoutEffect(registerLabel, [registerLabel]);\n\n const single = ctx.shown.length === 1;\n const warnedElement = useRef(false);\n const libraryRef = useCallback(\n (element: HTMLElement) => {\n if (isDev && single && !warnedElement.current && element.tagName !== \"LABEL\") {\n warnedElement.current = true;\n console.error(\n `Slider.Label: expected a <label> element but got <${element.tagName.toLowerCase()}>. ` +\n `With one thumb the Label names the input through \\`for\\`, which only a <label> has.`,\n );\n }\n // A cleanup, so the ref is never called with `null`.\n return () => {};\n },\n [single],\n );\n\n return useRender<ComponentPropsWithRef<\"label\">, SliderRootState<T>>({\n defaultElement: \"label\",\n render,\n state: ctx.state as SliderRootState<T>,\n className,\n style,\n children,\n props: rest,\n ref: libraryRef,\n libraryProps: {\n id: ctx.labelId,\n htmlFor: single ? ctx.thumbId(0) : undefined,\n },\n });\n}\n\nSliderLabel.displayName = \"SliderLabel\";\n"],"mappings":";;;;;AAQA,MAAM,QAAQ,QAAQ,IAAI,aAAa;;;;;;AAmBvC,SAAgB,YAAiD,EAC/D,WACA,OACA,UACA,QACA,GAAG,QACmB;CACtB,MAAM,MAAM,iBAAiB,cAAc;CAC3C,MAAM,EAAE,kBAAkB;CAC1B,gBAAgB,eAAe,CAAC,aAAa,CAAC;CAE9C,MAAM,SAAS,IAAI,MAAM,WAAW;CACpC,MAAM,gBAAgB,OAAO,KAAK;CAClC,MAAM,aAAa,aAChB,YAAyB;EACxB,IAAI,SAAS,UAAU,CAAC,cAAc,WAAW,QAAQ,YAAY,SAAS;GAC5E,cAAc,UAAU;GACxB,QAAQ,MACN,qDAAqD,QAAQ,QAAQ,YAAY,EAAE,uFAErF;EACF;EAEA,aAAa,CAAC;CAChB,GACA,CAAC,MAAM,CACT;CAEA,OAAO,UAA8D;EACnE,gBAAgB;EAChB;EACA,OAAO,IAAI;EACX;EACA;EACA;EACA,OAAO;EACP,KAAK;EACL,cAAc;GACZ,IAAI,IAAI;GACR,SAAS,SAAS,IAAI,QAAQ,CAAC,IAAI,KAAA;EACrC;CACF,CAAC;AACH;AAEA,YAAY,cAAc"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { ActionDetails, ChangeAction, ChangeReason, RenderProp, StateValue } from "../internal/types.mjs";
|
|
2
|
+
import { CSSProperties, ComponentPropsWithRef, ReactNode } from "react";
|
|
3
|
+
//#region src/slider/slider-root.d.ts
|
|
4
|
+
/** One number (one thumb) or one number per thumb, in ascending order. */
|
|
5
|
+
export type SliderValue = number | readonly number[];
|
|
6
|
+
export type SliderOrientation = "horizontal" | "vertical";
|
|
7
|
+
/** Details passed as the second argument of `Slider.Root`'s `valueChangeAction` (each step). */
|
|
8
|
+
export interface SliderValueChangeDetails extends ActionDetails {
|
|
9
|
+
/**
|
|
10
|
+
* `"activate"`: the user moved a thumb (`details.event` is the native `input` event for a key
|
|
11
|
+
* press, the `pointerdown` / `pointermove` / `pointerup` for a drag, the `keydown` for a
|
|
12
|
+
* JS-normalised key).
|
|
13
|
+
* `"platform"`: a form reset (the `reset` event) or a value found in the inputs at mount
|
|
14
|
+
* (`undefined`).
|
|
15
|
+
* `"dismiss"`: the commit action rejected and the slider reverted to the committed value
|
|
16
|
+
* (uncontrolled mode; `undefined`).
|
|
17
|
+
*/
|
|
18
|
+
readonly reason: ChangeReason;
|
|
19
|
+
/** Index of the thumb that moved; `null` on a platform path or a revert, where several may change. */
|
|
20
|
+
readonly thumb: number | null;
|
|
21
|
+
}
|
|
22
|
+
/** Details passed as the second argument of `Slider.Root`'s `valueCommitAction` (each gesture). */
|
|
23
|
+
export interface SliderValueCommitDetails extends ActionDetails {
|
|
24
|
+
/**
|
|
25
|
+
* `"activate"`: a gesture ended (`details.event` is the native `change` event for a key press,
|
|
26
|
+
* the `pointerup` / `pointercancel` / `lostpointercapture` for a drag, the `keydown` for a
|
|
27
|
+
* JS-normalised key). `"platform"`: a form reset (the `reset` event) or mount reconciliation
|
|
28
|
+
* (`undefined`). Never `"dismiss"`.
|
|
29
|
+
*/
|
|
30
|
+
readonly reason: ChangeReason;
|
|
31
|
+
/** Index of the thumb the gesture moved; `null` on a platform path. */
|
|
32
|
+
readonly thumb: number | null;
|
|
33
|
+
}
|
|
34
|
+
export interface SliderRootState<T extends SliderValue = number> {
|
|
35
|
+
/**
|
|
36
|
+
* The value the thumbs show: the value a gesture has reached while a pointer is captured or a
|
|
37
|
+
* step or commit is in flight, else the optimistic value. Content rendered from it must not
|
|
38
|
+
* suspend.
|
|
39
|
+
*/
|
|
40
|
+
readonly value: T;
|
|
41
|
+
/** `value` as an array, one entry per thumb, in thumb order. */
|
|
42
|
+
readonly values: readonly number[];
|
|
43
|
+
/** A step or commit dispatched through this Slider is in flight. */
|
|
44
|
+
readonly pending: boolean;
|
|
45
|
+
/** A pointer is captured on a thumb. */
|
|
46
|
+
readonly dragging: boolean;
|
|
47
|
+
readonly orientation: SliderOrientation;
|
|
48
|
+
readonly disabled: boolean;
|
|
49
|
+
readonly min: number;
|
|
50
|
+
readonly max: number;
|
|
51
|
+
readonly step: number;
|
|
52
|
+
}
|
|
53
|
+
export interface SliderRootProps<T extends SliderValue = number> extends Omit<ComponentPropsWithRef<"div">, "defaultValue" | "role" | "className" | "style" | "children" | "aria-valuemin" | "aria-valuemax" | "aria-valuenow" | "aria-valuetext" | "aria-orientation"> {
|
|
54
|
+
/** Controlled value: one number, or one per thumb in ascending order. `undefined`: uncontrolled. */
|
|
55
|
+
value?: T;
|
|
56
|
+
/**
|
|
57
|
+
* Initial value in uncontrolled mode; also the form-reset target (latest value). Default: the
|
|
58
|
+
* platform's midpoint, `min + (max - min) / 2`, snapped to `step`.
|
|
59
|
+
*/
|
|
60
|
+
defaultValue?: T;
|
|
61
|
+
/**
|
|
62
|
+
* Runs inside the Root's step transition on every step: each pointer move, each key press,
|
|
63
|
+
* each platform change, and the revert after a rejected commit (`reason: "dismiss"`,
|
|
64
|
+
* uncontrolled). Awaited; `data-pending` meanwhile (without this action a step starts no
|
|
65
|
+
* transition and is never pending). Adopt the value here in a controlled
|
|
66
|
+
* parent (`setValue(next)` in the sync prefix is a transition update). Not the place for a
|
|
67
|
+
* save: a pending async action entangles every transition in the app, and a drag is many
|
|
68
|
+
* steps. A `setState` after your own `await` needs `startTransition`.
|
|
69
|
+
*/
|
|
70
|
+
valueChangeAction?: ChangeAction<T, SliderValueChangeDetails>;
|
|
71
|
+
/**
|
|
72
|
+
* Runs inside the Root's commit transition once per gesture: pointer release, each key press,
|
|
73
|
+
* a form reset, a value found at mount. Awaited; the thumb keeps the gesture's value and shows
|
|
74
|
+
* `data-pending` until it settles; in uncontrolled mode the value is committed when it
|
|
75
|
+
* resolves and reverts to the previous value if it rejects. May be a Server Function.
|
|
76
|
+
*/
|
|
77
|
+
valueCommitAction?: ChangeAction<T, SliderValueCommitDetails>;
|
|
78
|
+
/** Default `0`. */
|
|
79
|
+
min?: number;
|
|
80
|
+
/**
|
|
81
|
+
* Default `100`. A `max` that is not on the step grid from `min` is unreachable, as on a
|
|
82
|
+
* native range (dev warning).
|
|
83
|
+
*/
|
|
84
|
+
max?: number;
|
|
85
|
+
/** Default `1`. Must be positive. Values snap to `min + n × step`. */
|
|
86
|
+
step?: number;
|
|
87
|
+
/** Minimum number of steps between two adjacent thumbs. Default `0` (thumbs may share a value). */
|
|
88
|
+
minStepsBetweenThumbs?: number;
|
|
89
|
+
/** Default `"horizontal"`. Vertical sliders have `min` at the bottom. */
|
|
90
|
+
orientation?: SliderOrientation;
|
|
91
|
+
/**
|
|
92
|
+
* `aria-valuetext` for a thumb, from the value it shows. Set it when the number alone is not
|
|
93
|
+
* meaningful ("3 of 5 stars", "Tuesday"). Default: none (the number is announced).
|
|
94
|
+
*/
|
|
95
|
+
getAriaValueText?: (value: number, index: number) => string;
|
|
96
|
+
/**
|
|
97
|
+
* Form field name for every thumb's input (a Thumb's own `name` wins). With several thumbs,
|
|
98
|
+
* read with `FormData.getAll()`.
|
|
99
|
+
*/
|
|
100
|
+
name?: string;
|
|
101
|
+
/** The `form` attribute for every thumb's input (a Thumb's own `form` wins). */
|
|
102
|
+
form?: string;
|
|
103
|
+
/** Disables every input (native `disabled`; not submitted) and the pointer. Never set while pending. */
|
|
104
|
+
disabled?: boolean;
|
|
105
|
+
className?: StateValue<string | undefined, SliderRootState<T>>;
|
|
106
|
+
style?: StateValue<CSSProperties | undefined, SliderRootState<T>>;
|
|
107
|
+
children?: StateValue<ReactNode, SliderRootState<T>>;
|
|
108
|
+
render?: RenderProp<ComponentPropsWithRef<"div">, SliderRootState<T>>;
|
|
109
|
+
}
|
|
110
|
+
//#endregion
|
|
111
|
+
//# sourceMappingURL=slider-root.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slider-root.d.mts","names":[],"sources":["../../src/slider/slider-root.tsx"],"mappings":";;;;YAmDY;YAEA;;iBAGK,iCAAiC;;;;;;;;;;WAUvC,QAAQ;;WAER;;;iBAIM,iCAAiC;;;;;;;WAOvC,QAAQ;;WAER;;iBAGM,gBAAgB,UAAU;;;;;;WAMhC,OAAO;;WAEP;;WAEA;;WAEA;WACA,aAAa;WACb;WACA;WACA;WACA;;iBAGM,gBAAgB,UAAU,8BAA8B,KACvE;;EAcA,QAAQ;;;;;EAKR,eAAe;;;;;;;;;;EAUf,oBAAoB,aAAa,GAAG;;;;;;;EAOpC,oBAAoB,aAAa,GAAG;;EAEpC;;;;;EAKA;;EAEA;;EAEA;;EAEA,cAAc;;;;;EAKd,oBAAoB,eAAe;;;;;EAKnC;;EAEA;;EAEA;EACA,YAAY,+BAA+B,gBAAgB;EAC3D,QAAQ,WAAW,2BAA2B,gBAAgB;EAC9D,WAAW,WAAW,WAAW,gBAAgB;EACjD,SAAS,WAAW,8BAA8B,gBAAgB"}
|