@proyecto-viviana/solid-stately 0.6.0-rc.0 → 0.6.0-rc.2
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/README.md +59 -29
- package/dist/calendar/createCalendarState.d.ts +3 -1
- package/dist/calendar/createCalendarState.d.ts.map +1 -1
- package/dist/calendar/createDatePickerState.d.ts +2 -0
- package/dist/calendar/createDatePickerState.d.ts.map +1 -1
- package/dist/calendar/createRangeCalendarState.d.ts +2 -0
- package/dist/calendar/createRangeCalendarState.d.ts.map +1 -1
- package/dist/calendar/createTimeFieldState.d.ts +1 -1
- package/dist/calendar/createTimeFieldState.d.ts.map +1 -1
- package/dist/checkbox/createCheckboxGroupState.d.ts +2 -0
- package/dist/checkbox/createCheckboxGroupState.d.ts.map +1 -1
- package/dist/collections/createListState.d.ts.map +1 -1
- package/dist/collections/createMenuState.d.ts +46 -2
- package/dist/collections/createMenuState.d.ts.map +1 -1
- package/dist/collections/createSelectionState.d.ts.map +1 -1
- package/dist/collections/index.d.ts +1 -1
- package/dist/collections/index.d.ts.map +1 -1
- package/dist/color/createColorAreaState.d.ts +4 -0
- package/dist/color/createColorAreaState.d.ts.map +1 -1
- package/dist/color/createColorFieldState.d.ts +2 -0
- package/dist/color/createColorFieldState.d.ts.map +1 -1
- package/dist/color/createColorSliderState.d.ts +2 -0
- package/dist/color/createColorSliderState.d.ts.map +1 -1
- package/dist/color/createColorWheelState.d.ts.map +1 -1
- package/dist/combobox/createComboBoxState.d.ts +90 -23
- package/dist/combobox/createComboBoxState.d.ts.map +1 -1
- package/dist/combobox/index.d.ts +1 -1
- package/dist/combobox/index.d.ts.map +1 -1
- package/dist/data/createAsyncList.d.ts.map +1 -1
- package/dist/data/createListData.d.ts.map +1 -1
- package/dist/data/createTreeData.d.ts.map +1 -1
- package/dist/dnd/createDraggableCollectionState.d.ts +5 -1
- package/dist/dnd/createDraggableCollectionState.d.ts.map +1 -1
- package/dist/dnd/createDroppableCollectionState.d.ts +6 -0
- package/dist/dnd/createDroppableCollectionState.d.ts.map +1 -1
- package/dist/form/createFormValidationState.d.ts.map +1 -1
- package/dist/grid/createGridState.d.ts +2 -2
- package/dist/grid/createGridState.d.ts.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1044 -478
- package/dist/index.js.map +1 -1
- package/dist/menu/index.d.ts +2 -1
- package/dist/menu/index.d.ts.map +1 -1
- package/dist/numberfield/createNumberFieldState.d.ts +6 -3
- package/dist/numberfield/createNumberFieldState.d.ts.map +1 -1
- package/dist/private/flags/flags.d.ts.map +1 -0
- package/dist/private/flags/flags.js +1 -1
- package/dist/private/flags/flags.js.map +1 -1
- package/dist/radio/createRadioGroupState.d.ts.map +1 -1
- package/dist/select/createSelectState.d.ts +85 -29
- package/dist/select/createSelectState.d.ts.map +1 -1
- package/dist/select/index.d.ts +1 -1
- package/dist/select/index.d.ts.map +1 -1
- package/dist/selection/SelectionManager.d.ts.map +1 -1
- package/dist/selection/createMultipleSelectionState.d.ts.map +1 -1
- package/dist/slider/createSliderState.d.ts +35 -9
- package/dist/slider/createSliderState.d.ts.map +1 -1
- package/dist/ssr/index.d.ts +0 -6
- package/dist/ssr/index.d.ts.map +1 -1
- package/dist/steplist/createStepListState.d.ts +8 -13
- package/dist/steplist/createStepListState.d.ts.map +1 -1
- package/dist/table/createTableColumnResizeState.d.ts.map +1 -1
- package/dist/table/createTableState.d.ts +2 -2
- package/dist/table/createTableState.d.ts.map +1 -1
- package/dist/table/createTreeGridState.d.ts +2 -2
- package/dist/table/createTreeGridState.d.ts.map +1 -1
- package/dist/table/types.d.ts +2 -2
- package/dist/table/types.d.ts.map +1 -1
- package/dist/tabs/createTabListState.d.ts.map +1 -1
- package/dist/textfield/createTextFieldState.d.ts.map +1 -1
- package/dist/toggle/createToggleGroupState.d.ts +0 -8
- package/dist/toggle/createToggleGroupState.d.ts.map +1 -1
- package/dist/toggle/createToggleState.d.ts.map +1 -1
- package/dist/tokenfield/TokenFieldValue.d.ts +28 -4
- package/dist/tokenfield/TokenFieldValue.d.ts.map +1 -1
- package/dist/tokenfield/index.d.ts +1 -1
- package/dist/tokenfield/index.d.ts.map +1 -1
- package/dist/tree/createTreeState.d.ts +2 -2
- package/dist/tree/createTreeState.d.ts.map +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/reactivity.d.ts +5 -0
- package/dist/utils/reactivity.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/calendar/createCalendarState.ts +139 -58
- package/src/calendar/createDatePickerState.ts +14 -11
- package/src/calendar/createRangeCalendarState.ts +43 -17
- package/src/calendar/createTimeFieldState.ts +1 -1
- package/src/checkbox/createCheckboxGroupState.ts +16 -0
- package/src/collections/createListState.ts +4 -4
- package/src/collections/createMenuState.ts +134 -4
- package/src/collections/createSelectionState.ts +2 -2
- package/src/collections/index.ts +4 -0
- package/src/color/createColorAreaState.ts +19 -0
- package/src/color/createColorFieldState.ts +76 -26
- package/src/color/createColorSliderState.ts +25 -2
- package/src/color/createColorWheelState.ts +9 -5
- package/src/combobox/createComboBoxState.ts +371 -121
- package/src/combobox/index.ts +4 -0
- package/src/data/createAsyncList.ts +19 -4
- package/src/data/createListData.ts +8 -2
- package/src/data/createTreeData.ts +11 -3
- package/src/dnd/createDraggableCollectionState.ts +8 -1
- package/src/dnd/createDroppableCollectionState.ts +10 -0
- package/src/form/createFormValidationState.ts +23 -5
- package/src/grid/createGridState.ts +15 -8
- package/src/index.ts +18 -2
- package/src/menu/index.ts +5 -0
- package/src/numberfield/createNumberFieldState.ts +97 -35
- package/src/radio/createRadioGroupState.ts +4 -1
- package/src/select/createSelectState.ts +253 -131
- package/src/select/index.ts +8 -1
- package/src/selection/SelectionManager.ts +3 -1
- package/src/selection/createMultipleSelectionState.ts +2 -2
- package/src/slider/createSliderState.ts +227 -103
- package/src/ssr/index.ts +11 -4
- package/src/steplist/createStepListState.ts +156 -123
- package/src/table/createTableColumnResizeState.ts +51 -11
- package/src/table/createTableState.ts +5 -4
- package/src/table/createTreeGridState.ts +3 -4
- package/src/table/types.ts +2 -2
- package/src/tabs/createTabListState.ts +8 -31
- package/src/textfield/createTextFieldState.ts +6 -3
- package/src/toggle/createToggleGroupState.ts +4 -2
- package/src/toggle/createToggleState.ts +5 -4
- package/src/tokenfield/TokenFieldValue.ts +75 -16
- package/src/tokenfield/index.ts +1 -0
- package/src/tree/createTreeState.ts +15 -8
- package/src/utils/index.ts +1 -0
- package/src/utils/reactivity.ts +8 -0
- package/dist/flags/flags.d.ts.map +0 -1
- /package/dist/{flags → private/flags}/flags.d.ts +0 -0
- /package/src/{flags → private/flags}/flags.ts +0 -0
|
@@ -17,21 +17,21 @@
|
|
|
17
17
|
* Based on @react-stately/slider useSliderState.
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
import { createMemo } from "solid-js";
|
|
20
|
+
import { createMemo, createEffect } from "solid-js";
|
|
21
21
|
import type { Accessor } from "solid-js";
|
|
22
22
|
import { createInternalSignal, access, type MaybeAccessor } from "../utils";
|
|
23
23
|
|
|
24
24
|
export type SliderOrientation = "horizontal" | "vertical";
|
|
25
25
|
|
|
26
|
-
export interface SliderStateProps {
|
|
26
|
+
export interface SliderStateProps<T extends number | number[] = number> {
|
|
27
27
|
/** The current value (controlled). */
|
|
28
|
-
value?:
|
|
28
|
+
value?: T;
|
|
29
29
|
/** The default value (uncontrolled). */
|
|
30
|
-
defaultValue?:
|
|
30
|
+
defaultValue?: T;
|
|
31
31
|
/** Handler called when the value changes. */
|
|
32
|
-
onChange?: (value:
|
|
32
|
+
onChange?: (value: T) => void;
|
|
33
33
|
/** Handler called when the user stops dragging. */
|
|
34
|
-
onChangeEnd?: (value:
|
|
34
|
+
onChangeEnd?: (value: T) => void;
|
|
35
35
|
/** The minimum value. */
|
|
36
36
|
minValue?: number;
|
|
37
37
|
/** The maximum value. */
|
|
@@ -58,14 +58,35 @@ export interface SliderState {
|
|
|
58
58
|
/** Gets the value as a percent (0-1). */
|
|
59
59
|
getValuePercent: Accessor<number>;
|
|
60
60
|
/** Gets the formatted value string. */
|
|
61
|
-
getFormattedValue:
|
|
62
|
-
|
|
61
|
+
getFormattedValue: {
|
|
62
|
+
(): string;
|
|
63
|
+
(value: number | readonly number[]): string;
|
|
64
|
+
};
|
|
65
|
+
/** Canonical values; legacy numeric members address thumb zero. */
|
|
66
|
+
values: Accessor<readonly number[]>;
|
|
67
|
+
/** Current reset defaults, normalized to the domain. */
|
|
68
|
+
defaultValues: Accessor<readonly number[]>;
|
|
69
|
+
getThumbValue: (index: number) => number;
|
|
70
|
+
setThumbValue: (index: number, value: number) => void;
|
|
71
|
+
setThumbPercent: (index: number, percent: number) => void;
|
|
72
|
+
getThumbPercent: (index: number) => number;
|
|
73
|
+
getThumbMinValue: (index: number) => number;
|
|
74
|
+
getThumbMaxValue: (index: number) => number;
|
|
75
|
+
getThumbValueLabel: (index: number) => string;
|
|
76
|
+
isThumbDragging: (index: number) => boolean;
|
|
77
|
+
setThumbDragging: (index: number, dragging: boolean) => void;
|
|
78
|
+
focusedThumb: Accessor<number | undefined>;
|
|
79
|
+
setFocusedThumb: (index: number | undefined) => void;
|
|
80
|
+
/** Absolute value distance, at least state.step. */
|
|
81
|
+
incrementThumb: (index: number, stepSize?: number) => void;
|
|
82
|
+
decrementThumb: (index: number, stepSize?: number) => void;
|
|
83
|
+
/** Whether any thumb is being dragged. */
|
|
63
84
|
isDragging: Accessor<boolean>;
|
|
64
|
-
/** Sets
|
|
85
|
+
/** Sets thumb zero dragging state. */
|
|
65
86
|
setDragging: (dragging: boolean) => void;
|
|
66
87
|
/** Whether the slider is focused. */
|
|
67
88
|
isFocused: Accessor<boolean>;
|
|
68
|
-
/**
|
|
89
|
+
/** Focuses thumb zero; false clears only thumb zero. */
|
|
69
90
|
setFocused: (focused: boolean) => void;
|
|
70
91
|
/** Increments the value by step. */
|
|
71
92
|
increment: (stepMultiplier?: number) => void;
|
|
@@ -110,17 +131,40 @@ function snapToStep(value: number, min: number, max: number, step: number): numb
|
|
|
110
131
|
/**
|
|
111
132
|
* Provides state management for a slider component.
|
|
112
133
|
*/
|
|
113
|
-
export function createSliderState(
|
|
134
|
+
export function createSliderState(
|
|
135
|
+
props: MaybeAccessor<
|
|
136
|
+
SliderStateProps<number[]> & ({ value: number[] } | { defaultValue: number[] })
|
|
137
|
+
>,
|
|
138
|
+
): SliderState;
|
|
139
|
+
export function createSliderState(props: MaybeAccessor<SliderStateProps>): SliderState;
|
|
140
|
+
export function createSliderState(
|
|
141
|
+
props: MaybeAccessor<SliderStateProps | SliderStateProps<number[]>>,
|
|
142
|
+
): SliderState {
|
|
114
143
|
const getProps = () => access(props);
|
|
115
|
-
|
|
116
144
|
const initialProps = getProps();
|
|
117
|
-
|
|
118
|
-
const
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
const
|
|
123
|
-
const
|
|
145
|
+
// Mode is established by explicit input shape, never by callback arity.
|
|
146
|
+
const arrayMode = Array.isArray(initialProps.value) || Array.isArray(initialProps.defaultValue);
|
|
147
|
+
const initialArray = Array.isArray(initialProps.value)
|
|
148
|
+
? initialProps.value
|
|
149
|
+
: initialProps.defaultValue;
|
|
150
|
+
const initialCount = Array.isArray(initialArray) ? initialArray.length : 1;
|
|
151
|
+
const finite = (value: number | undefined, fallback: number) =>
|
|
152
|
+
typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
153
|
+
const minValue = createMemo(() =>
|
|
154
|
+
arrayMode ? finite(getProps().minValue, DEFAULT_MIN) : (getProps().minValue ?? DEFAULT_MIN),
|
|
155
|
+
);
|
|
156
|
+
const step = createMemo(() => {
|
|
157
|
+
const next = arrayMode
|
|
158
|
+
? finite(getProps().step, DEFAULT_STEP)
|
|
159
|
+
: (getProps().step ?? DEFAULT_STEP);
|
|
160
|
+
return arrayMode && next <= 0 ? DEFAULT_STEP : next;
|
|
161
|
+
});
|
|
162
|
+
const maxValue = createMemo(() => {
|
|
163
|
+
const next = arrayMode
|
|
164
|
+
? finite(getProps().maxValue, DEFAULT_MAX)
|
|
165
|
+
: (getProps().maxValue ?? DEFAULT_MAX);
|
|
166
|
+
return arrayMode && next <= minValue() ? minValue() + step() : next;
|
|
167
|
+
});
|
|
124
168
|
const orientation = createMemo(() => getProps().orientation ?? "horizontal");
|
|
125
169
|
const isDisabled = createMemo(() => getProps().isDisabled ?? false);
|
|
126
170
|
const pageStep = createMemo(() =>
|
|
@@ -129,103 +173,183 @@ export function createSliderState(props: MaybeAccessor<SliderStateProps>): Slide
|
|
|
129
173
|
snapToStep((maxValue() - minValue()) / 10, 0, maxValue() - minValue(), step()),
|
|
130
174
|
),
|
|
131
175
|
);
|
|
132
|
-
|
|
133
|
-
// Controlled vs uncontrolled
|
|
134
176
|
const isControlled = () => getProps().value !== undefined;
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
const [
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
177
|
+
const fallbackValues = () =>
|
|
178
|
+
Array.from({ length: initialCount }, (_, index) => (index === 0 ? minValue() : maxValue()));
|
|
179
|
+
const normalize = (raw: number | readonly number[] | undefined): number[] => {
|
|
180
|
+
const items = raw === undefined ? fallbackValues() : typeof raw === "number" ? [raw] : raw;
|
|
181
|
+
const snapped = items.map((item, index) =>
|
|
182
|
+
snapToStep(
|
|
183
|
+
arrayMode ? finite(item, index === 0 ? minValue() : maxValue()) : item,
|
|
184
|
+
minValue(),
|
|
185
|
+
maxValue(),
|
|
186
|
+
step(),
|
|
187
|
+
),
|
|
188
|
+
);
|
|
189
|
+
return arrayMode ? snapped.sort((a, b) => a - b) : snapped;
|
|
190
|
+
};
|
|
191
|
+
const [internalValues, setInternalValues] = createInternalSignal(
|
|
192
|
+
normalize(initialProps.defaultValue),
|
|
144
193
|
);
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
const
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
194
|
+
const values: Accessor<readonly number[]> = () =>
|
|
195
|
+
normalize(isControlled() ? getProps().value : internalValues());
|
|
196
|
+
const initialValues = [...values()];
|
|
197
|
+
const initialDefaultValues = normalize(initialProps.defaultValue ?? initialValues);
|
|
198
|
+
const defaultValues: Accessor<readonly number[]> = () =>
|
|
199
|
+
// Scalar defaults are captured at mount; RangeSlider resets to current domain defaults.
|
|
200
|
+
normalize(arrayMode ? getProps().defaultValue : initialDefaultValues);
|
|
201
|
+
if (arrayMode) {
|
|
202
|
+
// Preserve RangeSlider's reactive uncontrolled default/domain reset policy.
|
|
203
|
+
// The compute phase reads only props/bounds, never the mutable current values.
|
|
204
|
+
const resetSnapshot = () =>
|
|
205
|
+
isControlled()
|
|
206
|
+
? undefined
|
|
207
|
+
: {
|
|
208
|
+
defaults: normalize(getProps().defaultValue),
|
|
209
|
+
min: minValue(),
|
|
210
|
+
max: maxValue(),
|
|
211
|
+
step: step(),
|
|
212
|
+
};
|
|
213
|
+
// Seed before effects drain: an initial equal default must not erase setters
|
|
214
|
+
// called during setup. Only a changed default/domain/control mode resets.
|
|
215
|
+
let previousReset = resetSnapshot();
|
|
216
|
+
createEffect(resetSnapshot, (next) => {
|
|
217
|
+
const previous = previousReset;
|
|
218
|
+
previousReset = next;
|
|
219
|
+
if (
|
|
220
|
+
next &&
|
|
221
|
+
(!previous ||
|
|
222
|
+
next.min !== previous.min ||
|
|
223
|
+
next.max !== previous.max ||
|
|
224
|
+
next.step !== previous.step ||
|
|
225
|
+
next.defaults.length !== previous.defaults.length ||
|
|
226
|
+
next.defaults.some((value, index) => value !== previous.defaults[index]))
|
|
227
|
+
) {
|
|
228
|
+
setInternalValues(next.defaults);
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
const [draggings, setDraggings] = createInternalSignal<boolean[]>(initialValues.map(() => false));
|
|
233
|
+
const [focusedThumb, setFocusedThumbValue] = createInternalSignal<number | undefined>(undefined);
|
|
234
|
+
const isDragging = () => draggings().some(Boolean);
|
|
235
|
+
let pendingValues = [...initialValues];
|
|
236
|
+
let hasPendingUpdate = false;
|
|
237
|
+
// Upstream retains each setter proposal outside drag too. Rebase when rendered values change.
|
|
238
|
+
let nonDraggingRenderedSnapshot: readonly number[] | undefined;
|
|
239
|
+
const validIndex = (index: number) =>
|
|
240
|
+
Number.isInteger(index) && index >= 0 && index < values().length;
|
|
241
|
+
const getThumbValue = (index: number) => values()[index];
|
|
242
|
+
const getThumbMinValue = (index: number) => (index === 0 ? minValue() : values()[index - 1]);
|
|
243
|
+
const getThumbMaxValue = (index: number) =>
|
|
244
|
+
index === values().length - 1 ? maxValue() : values()[index + 1];
|
|
245
|
+
const emit = (kind: "onChange" | "onChangeEnd", next: readonly number[]) => {
|
|
173
246
|
const p = getProps();
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
setInternalValue(snappedValue);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
p.onChange?.(snappedValue);
|
|
247
|
+
// The public overloads guarantee this callback shape for the established mode.
|
|
248
|
+
if (arrayMode) (p as SliderStateProps<number[]>)[kind]?.([...next]);
|
|
249
|
+
else (p as SliderStateProps)[kind]?.(next[0]);
|
|
181
250
|
};
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
|
|
251
|
+
const setThumbValue = (index: number, nextValue: number) => {
|
|
252
|
+
if (isDisabled() || !validIndex(index)) return;
|
|
253
|
+
const rendered = values();
|
|
254
|
+
const retainNonDraggingProposal =
|
|
255
|
+
arrayMode &&
|
|
256
|
+
isControlled() &&
|
|
257
|
+
nonDraggingRenderedSnapshot !== undefined &&
|
|
258
|
+
rendered.length === nonDraggingRenderedSnapshot.length &&
|
|
259
|
+
rendered.every((item, i) => item === nonDraggingRenderedSnapshot![i]);
|
|
260
|
+
const base = normalize(isDragging() || retainNonDraggingProposal ? pendingValues : rendered);
|
|
261
|
+
const lower = index === 0 ? minValue() : base[index - 1];
|
|
262
|
+
const upper = index === base.length - 1 ? maxValue() : base[index + 1];
|
|
263
|
+
// Current RangeSlider anchors snapping to the whole domain, then clamps to neighbors.
|
|
264
|
+
const snapped = clamp(snapToStep(nextValue, minValue(), maxValue(), step()), lower, upper);
|
|
265
|
+
const next = [...base];
|
|
266
|
+
next[index] = snapped;
|
|
267
|
+
pendingValues = next;
|
|
268
|
+
hasPendingUpdate = true;
|
|
269
|
+
nonDraggingRenderedSnapshot =
|
|
270
|
+
!isDragging() && arrayMode && isControlled() ? [...rendered] : undefined;
|
|
271
|
+
const changed = next.some((item, i) => item !== rendered[i]);
|
|
272
|
+
if (!isControlled() && (!arrayMode || changed)) setInternalValues(next);
|
|
273
|
+
// Scalar's duplicate callbacks and RangeSlider's equal-pair suppression are intentional.
|
|
274
|
+
if (!arrayMode || changed) emit("onChange", next);
|
|
188
275
|
};
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
276
|
+
const setThumbPercent = (index: number, percent: number) =>
|
|
277
|
+
setThumbValue(index, clamp(percent, 0, 1) * (maxValue() - minValue()) + minValue());
|
|
278
|
+
const getThumbPercent = (index: number) =>
|
|
279
|
+
(getThumbValue(index) - minValue()) / (maxValue() - minValue());
|
|
280
|
+
const isThumbDragging = (index: number) => draggings()[index] ?? false;
|
|
281
|
+
const setThumbDragging = (index: number, dragging: boolean) => {
|
|
282
|
+
if (dragging && (!validIndex(index) || (arrayMode && isDisabled()))) return;
|
|
283
|
+
const wasDragging = isThumbDragging(index);
|
|
284
|
+
if (wasDragging === dragging) return;
|
|
285
|
+
// One snapshot per drag session: a second handle cannot discard unechoed updates.
|
|
286
|
+
if (dragging && !isDragging()) {
|
|
287
|
+
pendingValues = [...values()];
|
|
288
|
+
hasPendingUpdate = false;
|
|
289
|
+
nonDraggingRenderedSnapshot = undefined;
|
|
290
|
+
}
|
|
291
|
+
const next = [...draggings()];
|
|
292
|
+
next[index] = dragging;
|
|
293
|
+
setDraggings(next);
|
|
294
|
+
// Allow disabled endings to release ownership; completion occurs only at the final end.
|
|
295
|
+
if (wasDragging && !isDragging()) {
|
|
296
|
+
emit("onChangeEnd", normalize(hasPendingUpdate ? pendingValues : values()));
|
|
198
297
|
}
|
|
199
298
|
};
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
const increment = (stepMultiplier = 1) => {
|
|
203
|
-
if (isDisabled()) return;
|
|
204
|
-
setValue(value() + step() * stepMultiplier);
|
|
299
|
+
const setFocusedThumb = (index: number | undefined) => {
|
|
300
|
+
if (index === undefined || validIndex(index)) setFocusedThumbValue(index);
|
|
205
301
|
};
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
302
|
+
const incrementThumb = (index: number, stepSize = 1) =>
|
|
303
|
+
setThumbValue(index, getThumbValue(index) + Math.max(stepSize, step()));
|
|
304
|
+
const decrementThumb = (index: number, stepSize = 1) =>
|
|
305
|
+
setThumbValue(index, getThumbValue(index) - Math.max(stepSize, step()));
|
|
306
|
+
const formatter = createMemo(
|
|
307
|
+
() => new Intl.NumberFormat(getProps().locale, getProps().formatOptions),
|
|
308
|
+
);
|
|
309
|
+
function getFormattedValue(): string;
|
|
310
|
+
function getFormattedValue(value: number | readonly number[]): string;
|
|
311
|
+
function getFormattedValue(value: number | readonly number[] = values()): string {
|
|
312
|
+
const items = typeof value === "number" ? [value] : value;
|
|
313
|
+
if (items.length === 0) return "";
|
|
314
|
+
if (items.length === 1) return formatter().format(items[0]);
|
|
315
|
+
if (items.length === 2) return formatter().formatRange(items[0], items[1]);
|
|
316
|
+
return new Intl.ListFormat(formatter().resolvedOptions().locale, { type: "unit" }).format(
|
|
317
|
+
items.map((item) => formatter().format(item)),
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
const value = () => getThumbValue(0) ?? minValue();
|
|
321
|
+
const setValue = (next: number) => setThumbValue(0, next);
|
|
217
322
|
return {
|
|
323
|
+
values,
|
|
324
|
+
defaultValues,
|
|
325
|
+
getThumbValue,
|
|
326
|
+
setThumbValue,
|
|
327
|
+
setThumbPercent,
|
|
328
|
+
getThumbPercent,
|
|
329
|
+
getThumbMinValue,
|
|
330
|
+
getThumbMaxValue,
|
|
331
|
+
getThumbValueLabel: (index) => getFormattedValue(getThumbValue(index)),
|
|
332
|
+
isThumbDragging,
|
|
333
|
+
setThumbDragging,
|
|
334
|
+
focusedThumb,
|
|
335
|
+
setFocusedThumb,
|
|
336
|
+
incrementThumb,
|
|
337
|
+
decrementThumb,
|
|
218
338
|
value,
|
|
219
339
|
setValue,
|
|
220
|
-
setValuePercent,
|
|
221
|
-
getValuePercent,
|
|
340
|
+
setValuePercent: (percent) => setThumbPercent(0, percent),
|
|
341
|
+
getValuePercent: () => getThumbPercent(0),
|
|
222
342
|
getFormattedValue,
|
|
223
343
|
isDragging,
|
|
224
|
-
setDragging,
|
|
225
|
-
isFocused,
|
|
226
|
-
setFocused
|
|
227
|
-
|
|
228
|
-
|
|
344
|
+
setDragging: (dragging) => setThumbDragging(0, dragging),
|
|
345
|
+
isFocused: () => focusedThumb() !== undefined,
|
|
346
|
+
setFocused: (focused) => {
|
|
347
|
+
if (focused) setFocusedThumb(0);
|
|
348
|
+
else if (focusedThumb() === 0) setFocusedThumb(undefined);
|
|
349
|
+
},
|
|
350
|
+
// Preserve even zero/negative scalar multiplier semantics, rather than using indexed delta.
|
|
351
|
+
increment: (multiplier = 1) => setValue(value() + step() * multiplier),
|
|
352
|
+
decrement: (multiplier = 1) => setValue(value() - step() * multiplier),
|
|
229
353
|
get minValue() {
|
|
230
354
|
return minValue();
|
|
231
355
|
},
|
package/src/ssr/index.ts
CHANGED
|
@@ -46,14 +46,21 @@ export function createIsSSR(): boolean {
|
|
|
46
46
|
*
|
|
47
47
|
* @param defaultId - Optional default ID to use instead of generating one.
|
|
48
48
|
*/
|
|
49
|
+
let fallbackIdCounter = 0;
|
|
50
|
+
|
|
49
51
|
export function createId(defaultId?: string): string {
|
|
50
|
-
// Generate first, choose second, like upstream `useId`: `createUniqueId` is
|
|
51
|
-
// order-dependent in both of its branches, so returning early on `defaultId`
|
|
52
|
-
// would shift every later id in the same render or hydration pass.
|
|
53
|
-
const uniqueId = createUniqueId();
|
|
54
52
|
if (defaultId) {
|
|
55
53
|
return defaultId;
|
|
56
54
|
}
|
|
55
|
+
let uniqueId: string | undefined;
|
|
56
|
+
try {
|
|
57
|
+
uniqueId = createUniqueId();
|
|
58
|
+
} catch {
|
|
59
|
+
// createUniqueId throws outside of a reactive context in server/hydration passes
|
|
60
|
+
}
|
|
61
|
+
if (!uniqueId) {
|
|
62
|
+
uniqueId = `nh-${++fallbackIdCounter}`;
|
|
63
|
+
}
|
|
57
64
|
return `solid-stately-${uniqueId}`;
|
|
58
65
|
}
|
|
59
66
|
|