@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.
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
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Menu compatibility surface.
3
+ *
4
+ * Exposes React Stately-like menu hook names while using existing
5
+ * Solid menu state primitives.
6
+ */
7
+ export { createMenuState, createMenuTriggerState, type MenuStateProps, type MenuState, type MenuTriggerStateProps, type MenuTriggerState, } from '../collections/createMenuState';
8
+ export { createMenuTriggerState as useMenuTriggerState, } from '../collections/createMenuState';
@@ -8,6 +8,7 @@
8
8
  */
9
9
  import { Accessor } from 'solid-js';
10
10
  import { type MaybeAccessor } from '../utils';
11
+ import { type FormValidationState, type ValidationFunction, type ValidationResult } from '../form';
11
12
  export interface RadioGroupProps {
12
13
  /** The current selected value (controlled). */
13
14
  value?: string | null;
@@ -37,8 +38,14 @@ export interface RadioGroupProps {
37
38
  onBlur?: (e: FocusEvent) => void;
38
39
  /** Handler that is called when the radio group's focus status changes. */
39
40
  onFocusChange?: (isFocused: boolean) => void;
41
+ /** Backward-compatible controlled validation state. */
42
+ validationState?: 'valid' | 'invalid';
43
+ /** Custom validation function. */
44
+ validate?: ValidationFunction<string | null>;
45
+ /** Validation behavior for the radio group. */
46
+ validationBehavior?: 'aria' | 'native';
40
47
  }
41
- export interface RadioGroupState {
48
+ export interface RadioGroupState extends Pick<FormValidationState, 'realtimeValidation' | 'displayValidation' | 'updateValidation' | 'resetValidation' | 'commitValidation'> {
42
49
  /** The name for the group, used for native form submission. */
43
50
  readonly name: string;
44
51
  /** Whether the radio group is disabled. */
@@ -59,6 +66,8 @@ export interface RadioGroupState {
59
66
  readonly lastFocusedValue: Accessor<string | null>;
60
67
  /** Sets the last focused value. */
61
68
  setLastFocusedValue(value: string | null): void;
69
+ /** Current display validation result for the group. */
70
+ readonly displayValidation: Accessor<ValidationResult>;
62
71
  }
63
72
  /**
64
73
  * Internal WeakMap to store sync version accessors for each radio group state.
@@ -5,6 +5,7 @@
5
5
  import { type Accessor } from 'solid-js';
6
6
  import { type MaybeAccessor } from '../utils';
7
7
  import type { Key, CollectionNode, Collection } from '../collections/types';
8
+ import type { SelectionMode, Selection } from '../collections/types';
8
9
  export interface SelectStateProps<T = unknown> {
9
10
  /** The items to display in the select. */
10
11
  items: T[];
@@ -20,8 +21,16 @@ export interface SelectStateProps<T = unknown> {
20
21
  selectedKey?: Key | null;
21
22
  /** The default selected key (uncontrolled). */
22
23
  defaultSelectedKey?: Key | null;
24
+ /** The selected keys (controlled, for multiple selection mode). */
25
+ selectedKeys?: 'all' | Iterable<Key>;
26
+ /** Default selected keys (uncontrolled, for multiple selection mode). */
27
+ defaultSelectedKeys?: 'all' | Iterable<Key>;
23
28
  /** Handler called when the selection changes. */
24
29
  onSelectionChange?: (key: Key | null) => void;
30
+ /** Handler called when selected keys change. */
31
+ onSelectionChangeKeys?: (keys: Selection) => void;
32
+ /** Selection mode for the select. */
33
+ selectionMode?: Extract<SelectionMode, 'single' | 'multiple'>;
25
34
  /** Whether the select is open (controlled). */
26
35
  isOpen?: boolean;
27
36
  /** Whether the select is open by default (uncontrolled). */
@@ -46,10 +55,16 @@ export interface SelectState<T = unknown> {
46
55
  toggle(): void;
47
56
  /** The currently selected key. */
48
57
  readonly selectedKey: Accessor<Key | null>;
58
+ /** The selected keys. */
59
+ readonly selectedKeys: Accessor<Selection>;
49
60
  /** The currently selected item. */
50
61
  readonly selectedItem: Accessor<CollectionNode<T> | null>;
62
+ /** The currently selected items. */
63
+ readonly selectedItems: Accessor<CollectionNode<T>[]>;
51
64
  /** Set the selected key. */
52
65
  setSelectedKey(key: Key | null): void;
66
+ /** Set selected keys. */
67
+ setSelectedKeys(keys: Iterable<Key>): void;
53
68
  /** The currently focused key. */
54
69
  readonly focusedKey: Accessor<Key | null>;
55
70
  /** Set the focused key. */
@@ -64,6 +79,8 @@ export interface SelectState<T = unknown> {
64
79
  readonly isDisabled: boolean;
65
80
  /** Whether the select is required. */
66
81
  readonly isRequired: boolean;
82
+ /** The selection mode. */
83
+ readonly selectionMode: Accessor<'single' | 'multiple'>;
67
84
  }
68
85
  /**
69
86
  * Creates state for a select component.
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Selection compatibility surface.
3
+ *
4
+ * Provides a React Stately-like module entrypoint for selection state while
5
+ * delegating to the existing Solid collection selection primitives.
6
+ */
7
+ export { createSelectionState, type SelectionStateProps, type SelectionState, } from '../collections/createSelectionState';
8
+ export type { Key, Selection, SelectionMode, SelectionBehavior, DisabledBehavior, FocusStrategy, } from '../collections/types';
9
+ export type MultipleSelectionStateProps = import('../collections/createSelectionState').SelectionStateProps;
10
+ export type MultipleSelectionState = import('../collections/createSelectionState').SelectionState;
11
+ export { createSelectionState as useMultipleSelectionState, } from '../collections/createSelectionState';
@@ -42,8 +42,14 @@ 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
+ * If hasExitAnimation is false, close() removes immediately and this is not needed.
51
+ */
52
+ remove: (key: string) => void;
47
53
  /** Pauses all toast timers. */
48
54
  pauseAll: () => void;
49
55
  /** Resumes all toast timers. */
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Toggle group state for Solid Stately
3
+ *
4
+ * Provides state management for groups of toggle buttons with
5
+ * single or multiple selection.
6
+ *
7
+ * This is a port of @react-stately/toggle's useToggleGroupState.
8
+ */
9
+ import type { Key } from '../collections';
10
+ import { type MaybeAccessor } from '../utils';
11
+ export interface ToggleGroupProps {
12
+ /**
13
+ * Whether single or multiple selection is enabled.
14
+ * @default 'single'
15
+ */
16
+ selectionMode?: 'single' | 'multiple';
17
+ /** Whether empty selection is disallowed. */
18
+ disallowEmptySelection?: boolean;
19
+ /** Controlled selected keys. */
20
+ selectedKeys?: Iterable<Key>;
21
+ /** Uncontrolled initial selected keys. */
22
+ defaultSelectedKeys?: Iterable<Key>;
23
+ /** Called when selected keys change. */
24
+ onSelectionChange?: (keys: Set<Key>) => void;
25
+ /** Whether all items are disabled. */
26
+ isDisabled?: boolean;
27
+ }
28
+ export interface ToggleGroupState {
29
+ /** Whether single or multiple selection is enabled. */
30
+ readonly selectionMode: 'single' | 'multiple';
31
+ /** Whether all items are disabled. */
32
+ readonly isDisabled: boolean;
33
+ /** Current selected keys. */
34
+ readonly selectedKeys: Set<Key>;
35
+ /** Toggles selected state for a key. */
36
+ toggleKey(key: Key): void;
37
+ /** Sets selected state for a key. */
38
+ setSelected(key: Key, isSelected: boolean): void;
39
+ /** Replaces selected keys. */
40
+ setSelectedKeys(keys: Set<Key>): void;
41
+ }
42
+ /**
43
+ * Manages state for a group of toggle buttons.
44
+ */
45
+ export declare function createToggleGroupState(props?: MaybeAccessor<ToggleGroupProps>): ToggleGroupState;
@@ -1 +1,2 @@
1
1
  export { createToggleState, type ToggleStateOptions, type ToggleState, } from './createToggleState';
2
+ export { createToggleGroupState, type ToggleGroupProps, type ToggleGroupState, } from './createToggleGroupState';
@@ -13,8 +13,9 @@ import type { TreeCollection as ITreeCollection, TreeNode, TreeItemData } from '
13
13
  */
14
14
  export declare class TreeCollection<T> implements ITreeCollection<T> {
15
15
  private keyMap;
16
- private visibleKeys;
17
- private _rows;
16
+ private visibleRows;
17
+ private firstKey;
18
+ private lastKey;
18
19
  constructor(items: TreeItemData<T>[], expandedKeys: Set<Key>);
19
20
  private buildCollection;
20
21
  get size(): number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proyecto-viviana/solid-stately",
3
- "version": "0.2.3",
3
+ "version": "0.2.7",
4
4
  "description": "A 1-1 SolidJS port of React Stately - headless state management for UI components",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -20,9 +20,9 @@
20
20
  ],
21
21
  "sideEffects": false,
22
22
  "scripts": {
23
- "build": "tsup",
23
+ "build": "tsup && rm -f tsconfig.build.tsbuildinfo && tsc -p tsconfig.build.json",
24
24
  "dev": "tsup --watch",
25
- "prepublishOnly": "echo 'Use deno task release from root'"
25
+ "prepublishOnly": "echo 'Use the root Changesets npm release workflow'"
26
26
  },
27
27
  "peerDependencies": {
28
28
  "solid-js": "^1.9.0"
@@ -31,7 +31,7 @@
31
31
  "@internationalized/date": "^3.8.0"
32
32
  },
33
33
  "devDependencies": {
34
- "solid-js": "^1.9.10"
34
+ "solid-js": "^1.9.11"
35
35
  },
36
36
  "keywords": [
37
37
  "solid",
@@ -44,7 +44,8 @@
44
44
  "license": "MIT",
45
45
  "repository": {
46
46
  "type": "git",
47
- "url": "https://github.com/proyecto-viviana/proyecto-viviana"
47
+ "url": "git+https://github.com/proyecto-viviana/proyecto-viviana.git",
48
+ "directory": "packages/solid-stately"
48
49
  },
49
50
  "publishConfig": {
50
51
  "access": "public"
@@ -5,6 +5,7 @@
5
5
  */
6
6
 
7
7
  import { createSignal, type Accessor } from 'solid-js'
8
+ import { access, type MaybeAccessor } from '../utils'
8
9
 
9
10
  // ============================================
10
11
  // TYPES
@@ -55,30 +56,28 @@ export interface AutocompleteStateOptions {
55
56
  * ```
56
57
  */
57
58
  export function createAutocompleteState(
58
- props: AutocompleteStateOptions = {}
59
+ props: MaybeAccessor<AutocompleteStateOptions> = {}
59
60
  ): AutocompleteState {
60
- const {
61
- inputValue: controlledInputValue,
62
- defaultInputValue = '',
63
- onInputChange,
64
- } = props
61
+ const getProps = () => access(props)
65
62
 
66
63
  // Track focused node ID for aria-activedescendant
67
64
  const [focusedNodeId, setFocusedNodeId] = createSignal<string | null>(null)
68
65
 
69
66
  // Handle controlled vs uncontrolled input value
70
- const isControlled = controlledInputValue !== undefined
71
- const [uncontrolledValue, setUncontrolledValue] = createSignal(defaultInputValue)
67
+ const isControlled = () => getProps().inputValue !== undefined
68
+ const [uncontrolledValue, setUncontrolledValue] = createSignal(getProps().defaultInputValue ?? '')
72
69
 
73
70
  const inputValue: Accessor<string> = () => {
74
- return isControlled ? controlledInputValue : uncontrolledValue()
71
+ const p = getProps()
72
+ return isControlled() ? (p.inputValue ?? '') : uncontrolledValue()
75
73
  }
76
74
 
77
75
  const setInputValue = (value: string) => {
78
- if (!isControlled) {
76
+ const p = getProps()
77
+ if (!isControlled()) {
79
78
  setUncontrolledValue(value)
80
79
  }
81
- onInputChange?.(value)
80
+ p.onInputChange?.(value)
82
81
  }
83
82
 
84
83
  return {
@@ -364,11 +364,34 @@ export function createDateFieldState<T extends DateValue = CalendarDate>(
364
364
 
365
365
  // Confirm placeholder value
366
366
  const confirmPlaceholder = () => {
367
+ if (isDisabled() || isReadOnly()) return;
368
+
369
+ const minValue = access(props.minValue);
370
+ const maxValue = access(props.maxValue);
371
+ const constrain = (candidate: DateValue): DateValue => {
372
+ if (minValue && candidate.compare(minValue) < 0) {
373
+ return minValue;
374
+ }
375
+ if (maxValue && candidate.compare(maxValue) > 0) {
376
+ return maxValue;
377
+ }
378
+ return candidate;
379
+ };
380
+
367
381
  const parts = placeholderDate();
368
382
  if (Object.keys(parts).length > 0) {
369
383
  const dv = dateValue();
370
384
  if (dv) {
371
- setValue(dv as T);
385
+ setValue(constrain(dv) as T);
386
+ }
387
+ return;
388
+ }
389
+
390
+ const current = value();
391
+ if (current) {
392
+ const constrained = constrain(current);
393
+ if (constrained.compare(current) !== 0) {
394
+ setValue(constrained as T);
372
395
  }
373
396
  }
374
397
  };
@@ -9,6 +9,12 @@
9
9
 
10
10
  import { createSignal, Accessor } from 'solid-js';
11
11
  import { type MaybeAccessor, access } from '../utils';
12
+ import {
13
+ createFormValidationState,
14
+ type FormValidationState,
15
+ type ValidationFunction,
16
+ type ValidationResult,
17
+ } from '../form';
12
18
 
13
19
  // ============================================
14
20
  // TYPES
@@ -41,9 +47,15 @@ export interface CheckboxGroupProps {
41
47
  onBlur?: (e: FocusEvent) => void;
42
48
  /** Handler that is called when the checkbox group's focus status changes. */
43
49
  onFocusChange?: (isFocused: boolean) => void;
50
+ /** Backward-compatible controlled validation state. */
51
+ validationState?: 'valid' | 'invalid';
52
+ /** Custom validation function. */
53
+ validate?: ValidationFunction<readonly string[]>;
54
+ /** Validation behavior for the checkbox group. */
55
+ validationBehavior?: 'aria' | 'native';
44
56
  }
45
57
 
46
- export interface CheckboxGroupState {
58
+ export interface CheckboxGroupState extends Pick<FormValidationState, 'realtimeValidation' | 'displayValidation' | 'updateValidation' | 'resetValidation' | 'commitValidation'> {
47
59
  /** Current selected values. */
48
60
  readonly value: Accessor<readonly string[]>;
49
61
  /** Default selected values. */
@@ -69,6 +81,9 @@ export interface CheckboxGroupState {
69
81
  removeValue(value: string): void;
70
82
  /** Toggles a value in the set of selected values. */
71
83
  toggleValue(value: string): void;
84
+
85
+ /** Current display validation result for the group. */
86
+ readonly displayValidation: Accessor<ValidationResult>;
72
87
  }
73
88
 
74
89
  // ============================================
@@ -106,10 +121,26 @@ export function createCheckboxGroupState(
106
121
  return !!p.isRequired && value().length === 0;
107
122
  };
108
123
 
109
- // Check if invalid
110
- const isInvalid = () => {
111
- return getProps().isInvalid ?? false;
112
- };
124
+ const validation = createFormValidationState<readonly string[]>({
125
+ get value() {
126
+ return value();
127
+ },
128
+ get isInvalid() {
129
+ return getProps().isInvalid;
130
+ },
131
+ get validationState() {
132
+ return getProps().validationState;
133
+ },
134
+ get validate() {
135
+ return getProps().validate;
136
+ },
137
+ get validationBehavior() {
138
+ return getProps().validationBehavior ?? 'aria';
139
+ },
140
+ get name() {
141
+ return getProps().name;
142
+ },
143
+ });
113
144
 
114
145
  // Set value
115
146
  function setValue(newValue: string[]): void {
@@ -181,7 +212,7 @@ export function createCheckboxGroupState(
181
212
  return getProps().isReadOnly ?? false;
182
213
  },
183
214
  get isInvalid() {
184
- return isInvalid();
215
+ return validation.displayValidation().isInvalid;
185
216
  },
186
217
  isRequired,
187
218
  isSelected,
@@ -189,5 +220,10 @@ export function createCheckboxGroupState(
189
220
  addValue,
190
221
  removeValue,
191
222
  toggleValue,
223
+ realtimeValidation: validation.realtimeValidation,
224
+ displayValidation: validation.displayValidation,
225
+ updateValidation: validation.updateValidation,
226
+ resetValidation: validation.resetValidation,
227
+ commitValidation: validation.commitValidation,
192
228
  };
193
229
  }