@proyecto-viviana/solid-stately 0.2.3 → 0.2.7
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 +21 -0
- package/dist/autocomplete/createAutocompleteState.d.ts +2 -1
- package/dist/checkbox/createCheckboxGroupState.d.ts +10 -1
- package/dist/collections/types.d.ts +11 -0
- package/dist/color/getColorChannels.d.ts +20 -0
- package/dist/data/createAsyncList.d.ts +111 -0
- package/dist/data/createListData.d.ts +65 -0
- package/dist/data/createTreeData.d.ts +61 -0
- package/dist/data/index.d.ts +3 -0
- package/dist/datepicker/index.d.ts +10 -0
- package/dist/grid/types.d.ts +5 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +3737 -2697
- package/dist/index.js.map +1 -7
- package/dist/menu/index.d.ts +8 -0
- package/dist/radio/createRadioGroupState.d.ts +10 -1
- package/dist/select/createSelectState.d.ts +17 -0
- package/dist/selection/index.d.ts +11 -0
- package/dist/toast/createToastState.d.ts +7 -1
- package/dist/toggle/createToggleGroupState.d.ts +45 -0
- package/dist/toggle/index.d.ts +1 -0
- package/dist/tree/TreeCollection.d.ts +3 -2
- package/package.json +6 -5
- package/src/autocomplete/createAutocompleteState.ts +10 -11
- package/src/calendar/createDateFieldState.ts +24 -1
- package/src/checkbox/createCheckboxGroupState.ts +42 -6
- package/src/collections/ListCollection.ts +152 -146
- package/src/collections/createListState.ts +266 -264
- package/src/collections/createMenuState.ts +106 -106
- package/src/collections/createSelectionState.ts +336 -336
- package/src/collections/index.ts +46 -46
- package/src/collections/types.ts +181 -169
- package/src/color/Color.ts +951 -951
- package/src/color/createColorAreaState.ts +293 -293
- package/src/color/createColorFieldState.ts +292 -292
- package/src/color/createColorSliderState.ts +241 -241
- package/src/color/createColorWheelState.ts +211 -211
- package/src/color/getColorChannels.ts +34 -0
- package/src/color/index.ts +47 -47
- package/src/color/types.ts +127 -127
- package/src/combobox/createComboBoxState.ts +703 -703
- package/src/combobox/index.ts +13 -13
- package/src/data/createAsyncList.ts +377 -0
- package/src/data/createListData.ts +298 -0
- package/src/data/createTreeData.ts +433 -0
- package/src/data/index.ts +25 -0
- package/src/datepicker/index.ts +36 -0
- package/src/disclosure/createDisclosureState.ts +4 -4
- package/src/dnd/createDragState.ts +153 -153
- package/src/dnd/createDraggableCollectionState.ts +165 -165
- package/src/dnd/createDropState.ts +212 -212
- package/src/dnd/createDroppableCollectionState.ts +357 -357
- package/src/dnd/index.ts +76 -76
- package/src/dnd/types.ts +317 -317
- package/src/form/createFormValidationState.ts +389 -389
- package/src/form/index.ts +15 -15
- package/src/grid/types.ts +5 -0
- package/src/index.ts +49 -0
- package/src/menu/index.ts +19 -0
- package/src/numberfield/createNumberFieldState.ts +427 -383
- package/src/numberfield/index.ts +5 -5
- package/src/overlays/createOverlayTriggerState.ts +67 -67
- package/src/overlays/index.ts +5 -5
- package/src/radio/createRadioGroupState.ts +44 -6
- package/src/searchfield/createSearchFieldState.ts +62 -62
- package/src/searchfield/index.ts +5 -5
- package/src/select/createSelectState.ts +290 -181
- package/src/select/index.ts +5 -5
- package/src/selection/index.ts +28 -0
- package/src/slider/createSliderState.ts +211 -211
- package/src/slider/index.ts +6 -6
- package/src/tabs/createTabListState.ts +37 -11
- package/src/toast/createToastState.d.ts +6 -1
- package/src/toast/createToastState.ts +8 -1
- package/src/toggle/createToggleGroupState.ts +127 -0
- package/src/toggle/index.ts +6 -0
- package/src/tooltip/createTooltipTriggerState.ts +183 -183
- package/src/tooltip/index.ts +6 -6
- package/src/tree/TreeCollection.ts +208 -175
- package/src/tree/createTreeState.ts +392 -392
- package/src/tree/index.ts +13 -13
- package/src/tree/types.ts +174 -174
|
@@ -1,211 +1,211 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Creates state for a slider component.
|
|
3
|
-
* Based on @react-stately/slider useSliderState.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { type Accessor, createSignal, createMemo } from 'solid-js';
|
|
7
|
-
import { access, type MaybeAccessor } from '../utils';
|
|
8
|
-
|
|
9
|
-
export type SliderOrientation = 'horizontal' | 'vertical';
|
|
10
|
-
|
|
11
|
-
export interface SliderStateProps {
|
|
12
|
-
/** The current value (controlled). */
|
|
13
|
-
value?: number;
|
|
14
|
-
/** The default value (uncontrolled). */
|
|
15
|
-
defaultValue?: number;
|
|
16
|
-
/** Handler called when the value changes. */
|
|
17
|
-
onChange?: (value: number) => void;
|
|
18
|
-
/** Handler called when the user stops dragging. */
|
|
19
|
-
onChangeEnd?: (value: number) => void;
|
|
20
|
-
/** The minimum value. */
|
|
21
|
-
minValue?: number;
|
|
22
|
-
/** The maximum value. */
|
|
23
|
-
maxValue?: number;
|
|
24
|
-
/** The step value. */
|
|
25
|
-
step?: number;
|
|
26
|
-
/** The orientation of the slider. */
|
|
27
|
-
orientation?: SliderOrientation;
|
|
28
|
-
/** Whether the slider is disabled. */
|
|
29
|
-
isDisabled?: boolean;
|
|
30
|
-
/** The locale for number formatting. */
|
|
31
|
-
locale?: string;
|
|
32
|
-
/** Number format options. */
|
|
33
|
-
formatOptions?: Intl.NumberFormatOptions;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export interface SliderState {
|
|
37
|
-
/** The current value. */
|
|
38
|
-
value: Accessor<number>;
|
|
39
|
-
/** Sets the value. */
|
|
40
|
-
setValue: (value: number) => void;
|
|
41
|
-
/** Sets the value by percent (0-1). */
|
|
42
|
-
setValuePercent: (percent: number) => void;
|
|
43
|
-
/** Gets the value as a percent (0-1). */
|
|
44
|
-
getValuePercent: Accessor<number>;
|
|
45
|
-
/** Gets the formatted value string. */
|
|
46
|
-
getFormattedValue: Accessor<string>;
|
|
47
|
-
/** Whether the thumb is being dragged. */
|
|
48
|
-
isDragging: Accessor<boolean>;
|
|
49
|
-
/** Sets the dragging state. */
|
|
50
|
-
setDragging: (dragging: boolean) => void;
|
|
51
|
-
/** Whether the slider is focused. */
|
|
52
|
-
isFocused: Accessor<boolean>;
|
|
53
|
-
/** Sets the focused state. */
|
|
54
|
-
setFocused: (focused: boolean) => void;
|
|
55
|
-
/** Increments the value by step. */
|
|
56
|
-
increment: (stepMultiplier?: number) => void;
|
|
57
|
-
/** Decrements the value by step. */
|
|
58
|
-
decrement: (stepMultiplier?: number) => void;
|
|
59
|
-
/** The minimum value. */
|
|
60
|
-
minValue: number;
|
|
61
|
-
/** The maximum value. */
|
|
62
|
-
maxValue: number;
|
|
63
|
-
/** The step value. */
|
|
64
|
-
step: number;
|
|
65
|
-
/** The page step (larger step for Page Up/Down). */
|
|
66
|
-
pageStep: number;
|
|
67
|
-
/** The orientation. */
|
|
68
|
-
orientation: SliderOrientation;
|
|
69
|
-
/** Whether the slider is disabled. */
|
|
70
|
-
isDisabled: boolean;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const DEFAULT_MIN = 0;
|
|
74
|
-
const DEFAULT_MAX = 100;
|
|
75
|
-
const DEFAULT_STEP = 1;
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Clamps a value between min and max.
|
|
79
|
-
*/
|
|
80
|
-
function clamp(value: number, min: number, max: number): number {
|
|
81
|
-
return Math.min(Math.max(value, min), max);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Snaps a value to the nearest step.
|
|
86
|
-
*/
|
|
87
|
-
function snapToStep(value: number, min: number, max: number, step: number): number {
|
|
88
|
-
const snapped = Math.round((value - min) / step) * step + min;
|
|
89
|
-
// Handle floating point precision issues
|
|
90
|
-
const decimalPlaces = (step.toString().split('.')[1] || '').length;
|
|
91
|
-
const rounded = parseFloat(snapped.toFixed(decimalPlaces));
|
|
92
|
-
return clamp(rounded, min, max);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Provides state management for a slider component.
|
|
97
|
-
*/
|
|
98
|
-
export function createSliderState(
|
|
99
|
-
props: MaybeAccessor<SliderStateProps>
|
|
100
|
-
): SliderState {
|
|
101
|
-
const getProps = () => access(props);
|
|
102
|
-
|
|
103
|
-
// Get static values with defaults
|
|
104
|
-
const minValue = getProps().minValue ?? DEFAULT_MIN;
|
|
105
|
-
const maxValue = getProps().maxValue ?? DEFAULT_MAX;
|
|
106
|
-
const step = getProps().step ?? DEFAULT_STEP;
|
|
107
|
-
const orientation = getProps().orientation ?? 'horizontal';
|
|
108
|
-
const isDisabled = getProps().isDisabled ?? false;
|
|
109
|
-
|
|
110
|
-
// Calculate page step (10% of range, snapped to step)
|
|
111
|
-
const pageStep = Math.max(step, snapToStep((maxValue - minValue) / 10, 0, maxValue - minValue, step));
|
|
112
|
-
|
|
113
|
-
// Controlled vs uncontrolled
|
|
114
|
-
const isControlled = () => getProps().value !== undefined;
|
|
115
|
-
|
|
116
|
-
// Internal signal for uncontrolled mode
|
|
117
|
-
const [internalValue, setInternalValue] = createSignal(
|
|
118
|
-
snapToStep(getProps().defaultValue ?? minValue, minValue, maxValue, step)
|
|
119
|
-
);
|
|
120
|
-
|
|
121
|
-
// Dragging and focus state
|
|
122
|
-
const [isDragging, setIsDragging] = createSignal(false);
|
|
123
|
-
const [isFocused, setIsFocused] = createSignal(false);
|
|
124
|
-
|
|
125
|
-
// Current value accessor
|
|
126
|
-
const value = createMemo(() => {
|
|
127
|
-
const p = getProps();
|
|
128
|
-
const rawValue = isControlled() ? (p.value ?? minValue) : internalValue();
|
|
129
|
-
return snapToStep(rawValue, minValue, maxValue, step);
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
// Value as percent (0-1)
|
|
133
|
-
const getValuePercent = createMemo(() => {
|
|
134
|
-
return (value() - minValue) / (maxValue - minValue);
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
// Formatted value
|
|
138
|
-
const getFormattedValue = createMemo(() => {
|
|
139
|
-
const p = getProps();
|
|
140
|
-
const formatter = new Intl.NumberFormat(p.locale, p.formatOptions);
|
|
141
|
-
return formatter.format(value());
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
// Set value function
|
|
145
|
-
const setValue = (newValue: number) => {
|
|
146
|
-
if (isDisabled) return;
|
|
147
|
-
|
|
148
|
-
const p = getProps();
|
|
149
|
-
const snappedValue = snapToStep(newValue, minValue, maxValue, step);
|
|
150
|
-
|
|
151
|
-
if (!isControlled()) {
|
|
152
|
-
setInternalValue(snappedValue);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
p.onChange?.(snappedValue);
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
// Set value by percent
|
|
159
|
-
const setValuePercent = (percent: number) => {
|
|
160
|
-
const clampedPercent = clamp(percent, 0, 1);
|
|
161
|
-
const newValue = clampedPercent * (maxValue - minValue) + minValue;
|
|
162
|
-
setValue(newValue);
|
|
163
|
-
};
|
|
164
|
-
|
|
165
|
-
// Dragging state management
|
|
166
|
-
const setDragging = (dragging: boolean) => {
|
|
167
|
-
const wasDragging = isDragging();
|
|
168
|
-
setIsDragging(dragging);
|
|
169
|
-
|
|
170
|
-
// Call onChangeEnd when dragging stops
|
|
171
|
-
if (wasDragging && !dragging) {
|
|
172
|
-
getProps().onChangeEnd?.(value());
|
|
173
|
-
}
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
// Increment/decrement
|
|
177
|
-
const increment = (stepMultiplier = 1) => {
|
|
178
|
-
if (isDisabled) return;
|
|
179
|
-
setValue(value() + step * stepMultiplier);
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
const decrement = (stepMultiplier = 1) => {
|
|
183
|
-
if (isDisabled) return;
|
|
184
|
-
setValue(value() - step * stepMultiplier);
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
// Set focused state
|
|
188
|
-
const setFocused = (focused: boolean) => {
|
|
189
|
-
setIsFocused(focused);
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
return {
|
|
193
|
-
value,
|
|
194
|
-
setValue,
|
|
195
|
-
setValuePercent,
|
|
196
|
-
getValuePercent,
|
|
197
|
-
getFormattedValue,
|
|
198
|
-
isDragging,
|
|
199
|
-
setDragging,
|
|
200
|
-
isFocused,
|
|
201
|
-
setFocused,
|
|
202
|
-
increment,
|
|
203
|
-
decrement,
|
|
204
|
-
minValue,
|
|
205
|
-
maxValue,
|
|
206
|
-
step,
|
|
207
|
-
pageStep,
|
|
208
|
-
orientation,
|
|
209
|
-
isDisabled,
|
|
210
|
-
};
|
|
211
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Creates state for a slider component.
|
|
3
|
+
* Based on @react-stately/slider useSliderState.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { type Accessor, createSignal, createMemo } from 'solid-js';
|
|
7
|
+
import { access, type MaybeAccessor } from '../utils';
|
|
8
|
+
|
|
9
|
+
export type SliderOrientation = 'horizontal' | 'vertical';
|
|
10
|
+
|
|
11
|
+
export interface SliderStateProps {
|
|
12
|
+
/** The current value (controlled). */
|
|
13
|
+
value?: number;
|
|
14
|
+
/** The default value (uncontrolled). */
|
|
15
|
+
defaultValue?: number;
|
|
16
|
+
/** Handler called when the value changes. */
|
|
17
|
+
onChange?: (value: number) => void;
|
|
18
|
+
/** Handler called when the user stops dragging. */
|
|
19
|
+
onChangeEnd?: (value: number) => void;
|
|
20
|
+
/** The minimum value. */
|
|
21
|
+
minValue?: number;
|
|
22
|
+
/** The maximum value. */
|
|
23
|
+
maxValue?: number;
|
|
24
|
+
/** The step value. */
|
|
25
|
+
step?: number;
|
|
26
|
+
/** The orientation of the slider. */
|
|
27
|
+
orientation?: SliderOrientation;
|
|
28
|
+
/** Whether the slider is disabled. */
|
|
29
|
+
isDisabled?: boolean;
|
|
30
|
+
/** The locale for number formatting. */
|
|
31
|
+
locale?: string;
|
|
32
|
+
/** Number format options. */
|
|
33
|
+
formatOptions?: Intl.NumberFormatOptions;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface SliderState {
|
|
37
|
+
/** The current value. */
|
|
38
|
+
value: Accessor<number>;
|
|
39
|
+
/** Sets the value. */
|
|
40
|
+
setValue: (value: number) => void;
|
|
41
|
+
/** Sets the value by percent (0-1). */
|
|
42
|
+
setValuePercent: (percent: number) => void;
|
|
43
|
+
/** Gets the value as a percent (0-1). */
|
|
44
|
+
getValuePercent: Accessor<number>;
|
|
45
|
+
/** Gets the formatted value string. */
|
|
46
|
+
getFormattedValue: Accessor<string>;
|
|
47
|
+
/** Whether the thumb is being dragged. */
|
|
48
|
+
isDragging: Accessor<boolean>;
|
|
49
|
+
/** Sets the dragging state. */
|
|
50
|
+
setDragging: (dragging: boolean) => void;
|
|
51
|
+
/** Whether the slider is focused. */
|
|
52
|
+
isFocused: Accessor<boolean>;
|
|
53
|
+
/** Sets the focused state. */
|
|
54
|
+
setFocused: (focused: boolean) => void;
|
|
55
|
+
/** Increments the value by step. */
|
|
56
|
+
increment: (stepMultiplier?: number) => void;
|
|
57
|
+
/** Decrements the value by step. */
|
|
58
|
+
decrement: (stepMultiplier?: number) => void;
|
|
59
|
+
/** The minimum value. */
|
|
60
|
+
minValue: number;
|
|
61
|
+
/** The maximum value. */
|
|
62
|
+
maxValue: number;
|
|
63
|
+
/** The step value. */
|
|
64
|
+
step: number;
|
|
65
|
+
/** The page step (larger step for Page Up/Down). */
|
|
66
|
+
pageStep: number;
|
|
67
|
+
/** The orientation. */
|
|
68
|
+
orientation: SliderOrientation;
|
|
69
|
+
/** Whether the slider is disabled. */
|
|
70
|
+
isDisabled: boolean;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const DEFAULT_MIN = 0;
|
|
74
|
+
const DEFAULT_MAX = 100;
|
|
75
|
+
const DEFAULT_STEP = 1;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Clamps a value between min and max.
|
|
79
|
+
*/
|
|
80
|
+
function clamp(value: number, min: number, max: number): number {
|
|
81
|
+
return Math.min(Math.max(value, min), max);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Snaps a value to the nearest step.
|
|
86
|
+
*/
|
|
87
|
+
function snapToStep(value: number, min: number, max: number, step: number): number {
|
|
88
|
+
const snapped = Math.round((value - min) / step) * step + min;
|
|
89
|
+
// Handle floating point precision issues
|
|
90
|
+
const decimalPlaces = (step.toString().split('.')[1] || '').length;
|
|
91
|
+
const rounded = parseFloat(snapped.toFixed(decimalPlaces));
|
|
92
|
+
return clamp(rounded, min, max);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Provides state management for a slider component.
|
|
97
|
+
*/
|
|
98
|
+
export function createSliderState(
|
|
99
|
+
props: MaybeAccessor<SliderStateProps>
|
|
100
|
+
): SliderState {
|
|
101
|
+
const getProps = () => access(props);
|
|
102
|
+
|
|
103
|
+
// Get static values with defaults
|
|
104
|
+
const minValue = getProps().minValue ?? DEFAULT_MIN;
|
|
105
|
+
const maxValue = getProps().maxValue ?? DEFAULT_MAX;
|
|
106
|
+
const step = getProps().step ?? DEFAULT_STEP;
|
|
107
|
+
const orientation = getProps().orientation ?? 'horizontal';
|
|
108
|
+
const isDisabled = getProps().isDisabled ?? false;
|
|
109
|
+
|
|
110
|
+
// Calculate page step (10% of range, snapped to step)
|
|
111
|
+
const pageStep = Math.max(step, snapToStep((maxValue - minValue) / 10, 0, maxValue - minValue, step));
|
|
112
|
+
|
|
113
|
+
// Controlled vs uncontrolled
|
|
114
|
+
const isControlled = () => getProps().value !== undefined;
|
|
115
|
+
|
|
116
|
+
// Internal signal for uncontrolled mode
|
|
117
|
+
const [internalValue, setInternalValue] = createSignal(
|
|
118
|
+
snapToStep(getProps().defaultValue ?? minValue, minValue, maxValue, step)
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
// Dragging and focus state
|
|
122
|
+
const [isDragging, setIsDragging] = createSignal(false);
|
|
123
|
+
const [isFocused, setIsFocused] = createSignal(false);
|
|
124
|
+
|
|
125
|
+
// Current value accessor
|
|
126
|
+
const value = createMemo(() => {
|
|
127
|
+
const p = getProps();
|
|
128
|
+
const rawValue = isControlled() ? (p.value ?? minValue) : internalValue();
|
|
129
|
+
return snapToStep(rawValue, minValue, maxValue, step);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// Value as percent (0-1)
|
|
133
|
+
const getValuePercent = createMemo(() => {
|
|
134
|
+
return (value() - minValue) / (maxValue - minValue);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// Formatted value
|
|
138
|
+
const getFormattedValue = createMemo(() => {
|
|
139
|
+
const p = getProps();
|
|
140
|
+
const formatter = new Intl.NumberFormat(p.locale, p.formatOptions);
|
|
141
|
+
return formatter.format(value());
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// Set value function
|
|
145
|
+
const setValue = (newValue: number) => {
|
|
146
|
+
if (isDisabled) return;
|
|
147
|
+
|
|
148
|
+
const p = getProps();
|
|
149
|
+
const snappedValue = snapToStep(newValue, minValue, maxValue, step);
|
|
150
|
+
|
|
151
|
+
if (!isControlled()) {
|
|
152
|
+
setInternalValue(snappedValue);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
p.onChange?.(snappedValue);
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
// Set value by percent
|
|
159
|
+
const setValuePercent = (percent: number) => {
|
|
160
|
+
const clampedPercent = clamp(percent, 0, 1);
|
|
161
|
+
const newValue = clampedPercent * (maxValue - minValue) + minValue;
|
|
162
|
+
setValue(newValue);
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
// Dragging state management
|
|
166
|
+
const setDragging = (dragging: boolean) => {
|
|
167
|
+
const wasDragging = isDragging();
|
|
168
|
+
setIsDragging(dragging);
|
|
169
|
+
|
|
170
|
+
// Call onChangeEnd when dragging stops
|
|
171
|
+
if (wasDragging && !dragging) {
|
|
172
|
+
getProps().onChangeEnd?.(value());
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
// Increment/decrement
|
|
177
|
+
const increment = (stepMultiplier = 1) => {
|
|
178
|
+
if (isDisabled) return;
|
|
179
|
+
setValue(value() + step * stepMultiplier);
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const decrement = (stepMultiplier = 1) => {
|
|
183
|
+
if (isDisabled) return;
|
|
184
|
+
setValue(value() - step * stepMultiplier);
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
// Set focused state
|
|
188
|
+
const setFocused = (focused: boolean) => {
|
|
189
|
+
setIsFocused(focused);
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
value,
|
|
194
|
+
setValue,
|
|
195
|
+
setValuePercent,
|
|
196
|
+
getValuePercent,
|
|
197
|
+
getFormattedValue,
|
|
198
|
+
isDragging,
|
|
199
|
+
setDragging,
|
|
200
|
+
isFocused,
|
|
201
|
+
setFocused,
|
|
202
|
+
increment,
|
|
203
|
+
decrement,
|
|
204
|
+
minValue,
|
|
205
|
+
maxValue,
|
|
206
|
+
step,
|
|
207
|
+
pageStep,
|
|
208
|
+
orientation,
|
|
209
|
+
isDisabled,
|
|
210
|
+
};
|
|
211
|
+
}
|
package/src/slider/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export { createSliderState } from './createSliderState';
|
|
2
|
-
export type {
|
|
3
|
-
SliderState,
|
|
4
|
-
SliderStateProps,
|
|
5
|
-
SliderOrientation,
|
|
6
|
-
} from './createSliderState';
|
|
1
|
+
export { createSliderState } from './createSliderState';
|
|
2
|
+
export type {
|
|
3
|
+
SliderState,
|
|
4
|
+
SliderStateProps,
|
|
5
|
+
SliderOrientation,
|
|
6
|
+
} from './createSliderState';
|
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
* Based on @react-stately/tabs.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { createSignal, type Accessor } from 'solid-js';
|
|
6
|
+
import { createComputed, createMemo, createSignal, type Accessor } from 'solid-js';
|
|
7
7
|
import { access, type MaybeAccessor } from '../utils';
|
|
8
8
|
import { ListCollection } from '../collections/ListCollection';
|
|
9
9
|
import type {
|
|
10
10
|
Collection,
|
|
11
|
+
CollectionItemLike,
|
|
11
12
|
CollectionNode,
|
|
12
13
|
Key,
|
|
13
14
|
FocusStrategy,
|
|
@@ -81,22 +82,23 @@ export function createTabListState<T = unknown>(
|
|
|
81
82
|
const getProps = () => access(props);
|
|
82
83
|
|
|
83
84
|
// Build collection from items
|
|
84
|
-
const collection: Accessor<Collection<T>> = () => {
|
|
85
|
+
const collection: Accessor<Collection<T>> = createMemo(() => {
|
|
85
86
|
const p = getProps();
|
|
86
87
|
const items = p.items ?? [];
|
|
87
88
|
|
|
88
89
|
const nodes: CollectionNode<T>[] = items.map((item, index) => {
|
|
89
|
-
const
|
|
90
|
+
const o = item as CollectionItemLike;
|
|
91
|
+
const key = p.getKey?.(item) ?? o.key ?? o.id ?? index;
|
|
90
92
|
const textValue =
|
|
91
|
-
p.getTextValue?.(item) ??
|
|
92
|
-
const isDisabled = p.getDisabled?.(item) ??
|
|
93
|
+
p.getTextValue?.(item) ?? o.textValue ?? o.label ?? String(item);
|
|
94
|
+
const isDisabled = p.getDisabled?.(item) ?? o.isDisabled ?? false;
|
|
93
95
|
|
|
94
96
|
return {
|
|
95
97
|
type: 'item' as const,
|
|
96
98
|
key,
|
|
97
99
|
value: item,
|
|
98
100
|
textValue,
|
|
99
|
-
rendered: null
|
|
101
|
+
rendered: null!,
|
|
100
102
|
level: 0,
|
|
101
103
|
index,
|
|
102
104
|
parentKey: null,
|
|
@@ -107,10 +109,10 @@ export function createTabListState<T = unknown>(
|
|
|
107
109
|
});
|
|
108
110
|
|
|
109
111
|
return new ListCollection(nodes);
|
|
110
|
-
};
|
|
112
|
+
});
|
|
111
113
|
|
|
112
|
-
// Compute disabled keys
|
|
113
|
-
const disabledKeys: Accessor<Set<Key>> = () => {
|
|
114
|
+
// Compute disabled keys (memoized to avoid rebuilding Set per access)
|
|
115
|
+
const disabledKeys: Accessor<Set<Key>> = createMemo(() => {
|
|
114
116
|
const p = getProps();
|
|
115
117
|
const result = new Set<Key>(p.disabledKeys ?? []);
|
|
116
118
|
|
|
@@ -122,7 +124,7 @@ export function createTabListState<T = unknown>(
|
|
|
122
124
|
}
|
|
123
125
|
|
|
124
126
|
return result;
|
|
125
|
-
};
|
|
127
|
+
});
|
|
126
128
|
|
|
127
129
|
// Check if a key is disabled
|
|
128
130
|
const isKeyDisabled = (key: Key): boolean => {
|
|
@@ -177,6 +179,7 @@ export function createTabListState<T = unknown>(
|
|
|
177
179
|
const setSelectedKey = (key: Key) => {
|
|
178
180
|
// Don't select disabled keys
|
|
179
181
|
if (isKeyDisabled(key)) return;
|
|
182
|
+
if (selectedKey() === key) return;
|
|
180
183
|
|
|
181
184
|
const p = getProps();
|
|
182
185
|
// For uncontrolled mode, update internal state
|
|
@@ -216,11 +219,34 @@ export function createTabListState<T = unknown>(
|
|
|
216
219
|
setChildFocusStrategy(childStrategy ?? null);
|
|
217
220
|
|
|
218
221
|
// In automatic mode, selecting follows focus
|
|
219
|
-
if (
|
|
222
|
+
if (
|
|
223
|
+
keyboardActivation() === 'automatic' &&
|
|
224
|
+
key !== null &&
|
|
225
|
+
key !== selectedKey() &&
|
|
226
|
+
!isKeyDisabled(key)
|
|
227
|
+
) {
|
|
220
228
|
setSelectedKey(key);
|
|
221
229
|
}
|
|
222
230
|
};
|
|
223
231
|
|
|
232
|
+
// Keep uncontrolled selection valid as items/disabled keys change.
|
|
233
|
+
createComputed(() => {
|
|
234
|
+
const p = getProps();
|
|
235
|
+
if (p.selectedKey !== undefined) return;
|
|
236
|
+
|
|
237
|
+
const coll = collection();
|
|
238
|
+
const current = selectedKeyInternal();
|
|
239
|
+
const currentExists = current !== null && coll.getItem(current) !== null;
|
|
240
|
+
const currentEnabled = current !== null && !isKeyDisabled(current);
|
|
241
|
+
|
|
242
|
+
if (currentExists && currentEnabled) return;
|
|
243
|
+
|
|
244
|
+
const nextKey = findFirstNonDisabledKey();
|
|
245
|
+
if (nextKey !== current) {
|
|
246
|
+
setSelectedKeyInternal(nextKey);
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
|
|
224
250
|
return {
|
|
225
251
|
collection,
|
|
226
252
|
selectedKey,
|
|
@@ -42,8 +42,13 @@ export interface ToastState<T> {
|
|
|
42
42
|
visibleToasts: Accessor<QueuedToast<T>[]>;
|
|
43
43
|
/** Adds a toast to the queue. */
|
|
44
44
|
add: (content: T, options?: ToastOptions) => string;
|
|
45
|
-
/** Closes a toast by key. */
|
|
45
|
+
/** Closes a toast by key. Starts exit animation if hasExitAnimation is enabled. */
|
|
46
46
|
close: (key: string) => void;
|
|
47
|
+
/**
|
|
48
|
+
* Removes a toast after exit animation completes.
|
|
49
|
+
* Call this from the component layer when the CSS exit animation finishes.
|
|
50
|
+
*/
|
|
51
|
+
remove: (key: string) => void;
|
|
47
52
|
/** Pauses all toast timers. */
|
|
48
53
|
pauseAll: () => void;
|
|
49
54
|
/** Resumes all toast timers. */
|
|
@@ -52,8 +52,14 @@ export interface ToastState<T> {
|
|
|
52
52
|
visibleToasts: Accessor<QueuedToast<T>[]>;
|
|
53
53
|
/** Adds a toast to the queue. */
|
|
54
54
|
add: (content: T, options?: ToastOptions) => string;
|
|
55
|
-
/** Closes a toast by key. */
|
|
55
|
+
/** Closes a toast by key. Starts exit animation if hasExitAnimation is enabled. */
|
|
56
56
|
close: (key: string) => void;
|
|
57
|
+
/**
|
|
58
|
+
* Removes a toast after exit animation completes.
|
|
59
|
+
* Call this from the component layer when the CSS exit animation finishes.
|
|
60
|
+
* If hasExitAnimation is false, close() removes immediately and this is not needed.
|
|
61
|
+
*/
|
|
62
|
+
remove: (key: string) => void;
|
|
57
63
|
/** Pauses all toast timers. */
|
|
58
64
|
pauseAll: () => void;
|
|
59
65
|
/** Resumes all toast timers. */
|
|
@@ -296,6 +302,7 @@ export function createToastState<T>(props: ToastStateProps<T>): ToastState<T> {
|
|
|
296
302
|
visibleToasts,
|
|
297
303
|
add: (content, options) => props.queue.add(content, options),
|
|
298
304
|
close: (key) => props.queue.close(key),
|
|
305
|
+
remove: (key) => props.queue.remove(key),
|
|
299
306
|
pauseAll: () => props.queue.pauseAll(),
|
|
300
307
|
resumeAll: () => props.queue.resumeAll(),
|
|
301
308
|
};
|