@proyecto-viviana/solid-stately 0.2.4 → 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.
Files changed (82) hide show
  1. package/LICENSE +21 -0
  2. package/dist/autocomplete/createAutocompleteState.d.ts +2 -1
  3. package/dist/checkbox/createCheckboxGroupState.d.ts +10 -1
  4. package/dist/collections/types.d.ts +11 -0
  5. package/dist/color/getColorChannels.d.ts +20 -0
  6. package/dist/data/createAsyncList.d.ts +111 -0
  7. package/dist/data/createListData.d.ts +65 -0
  8. package/dist/data/createTreeData.d.ts +61 -0
  9. package/dist/data/index.d.ts +3 -0
  10. package/dist/datepicker/index.d.ts +10 -0
  11. package/dist/grid/types.d.ts +5 -1
  12. package/dist/index.d.ts +6 -1
  13. package/dist/index.js +3737 -2697
  14. package/dist/index.js.map +1 -7
  15. package/dist/menu/index.d.ts +8 -0
  16. package/dist/radio/createRadioGroupState.d.ts +10 -1
  17. package/dist/select/createSelectState.d.ts +17 -0
  18. package/dist/selection/index.d.ts +11 -0
  19. package/dist/toast/createToastState.d.ts +7 -1
  20. package/dist/toggle/createToggleGroupState.d.ts +45 -0
  21. package/dist/toggle/index.d.ts +1 -0
  22. package/dist/tree/TreeCollection.d.ts +3 -2
  23. package/package.json +6 -5
  24. package/src/autocomplete/createAutocompleteState.ts +10 -11
  25. package/src/calendar/createDateFieldState.ts +24 -1
  26. package/src/checkbox/createCheckboxGroupState.ts +42 -6
  27. package/src/collections/ListCollection.ts +152 -146
  28. package/src/collections/createListState.ts +266 -264
  29. package/src/collections/createMenuState.ts +106 -106
  30. package/src/collections/createSelectionState.ts +336 -336
  31. package/src/collections/index.ts +46 -46
  32. package/src/collections/types.ts +181 -169
  33. package/src/color/Color.ts +951 -951
  34. package/src/color/createColorAreaState.ts +293 -293
  35. package/src/color/createColorFieldState.ts +292 -292
  36. package/src/color/createColorSliderState.ts +241 -241
  37. package/src/color/createColorWheelState.ts +211 -211
  38. package/src/color/getColorChannels.ts +34 -0
  39. package/src/color/index.ts +47 -47
  40. package/src/color/types.ts +127 -127
  41. package/src/combobox/createComboBoxState.ts +703 -703
  42. package/src/combobox/index.ts +13 -13
  43. package/src/data/createAsyncList.ts +377 -0
  44. package/src/data/createListData.ts +298 -0
  45. package/src/data/createTreeData.ts +433 -0
  46. package/src/data/index.ts +25 -0
  47. package/src/datepicker/index.ts +36 -0
  48. package/src/disclosure/createDisclosureState.ts +4 -4
  49. package/src/dnd/createDragState.ts +153 -153
  50. package/src/dnd/createDraggableCollectionState.ts +165 -165
  51. package/src/dnd/createDropState.ts +212 -212
  52. package/src/dnd/createDroppableCollectionState.ts +357 -357
  53. package/src/dnd/index.ts +76 -76
  54. package/src/dnd/types.ts +317 -317
  55. package/src/form/createFormValidationState.ts +389 -389
  56. package/src/form/index.ts +15 -15
  57. package/src/grid/types.ts +5 -0
  58. package/src/index.ts +49 -0
  59. package/src/menu/index.ts +19 -0
  60. package/src/numberfield/createNumberFieldState.ts +427 -383
  61. package/src/numberfield/index.ts +5 -5
  62. package/src/overlays/createOverlayTriggerState.ts +67 -67
  63. package/src/overlays/index.ts +5 -5
  64. package/src/radio/createRadioGroupState.ts +44 -6
  65. package/src/searchfield/createSearchFieldState.ts +62 -62
  66. package/src/searchfield/index.ts +5 -5
  67. package/src/select/createSelectState.ts +290 -181
  68. package/src/select/index.ts +5 -5
  69. package/src/selection/index.ts +28 -0
  70. package/src/slider/createSliderState.ts +211 -211
  71. package/src/slider/index.ts +6 -6
  72. package/src/tabs/createTabListState.ts +37 -11
  73. package/src/toast/createToastState.d.ts +6 -1
  74. package/src/toast/createToastState.ts +8 -1
  75. package/src/toggle/createToggleGroupState.ts +127 -0
  76. package/src/toggle/index.ts +6 -0
  77. package/src/tooltip/createTooltipTriggerState.ts +183 -183
  78. package/src/tooltip/index.ts +6 -6
  79. package/src/tree/TreeCollection.ts +208 -175
  80. package/src/tree/createTreeState.ts +392 -392
  81. package/src/tree/index.ts +13 -13
  82. 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
+ }
@@ -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 key = p.getKey?.(item) ?? (item as any).key ?? (item as any).id ?? index;
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) ?? (item as any).textValue ?? (item as any).label ?? String(item);
92
- const isDisabled = p.getDisabled?.(item) ?? (item as any).isDisabled ?? false;
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 as any,
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 (keyboardActivation() === 'automatic' && key !== null && !isKeyDisabled(key)) {
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
  };