@proyecto-viviana/solid-stately 0.3.0 → 0.4.1

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 (77) hide show
  1. package/dist/calendar/IncompleteDate.d.ts +61 -0
  2. package/dist/calendar/createCalendarState.d.ts +20 -10
  3. package/dist/calendar/createDateFieldState.d.ts +65 -20
  4. package/dist/calendar/createRangeCalendarState.d.ts +20 -2
  5. package/dist/calendar/createTimeFieldState.d.ts +38 -67
  6. package/dist/calendar/index.d.ts +1 -1
  7. package/dist/calendar/intl/index.d.ts +10 -0
  8. package/dist/collections/ListCollection.d.ts +8 -0
  9. package/dist/collections/createListState.d.ts +16 -1
  10. package/dist/collections/createSelectionState.d.ts +17 -5
  11. package/dist/collections/getChildNodes.d.ts +19 -0
  12. package/dist/collections/index.d.ts +3 -2
  13. package/dist/collections/types.d.ts +9 -0
  14. package/dist/color/intl/index.d.ts +14 -0
  15. package/dist/combobox/createComboBoxState.d.ts +7 -0
  16. package/dist/dnd/createDroppableCollectionState.d.ts +17 -2
  17. package/dist/dnd/index.d.ts +1 -1
  18. package/dist/dnd/types.d.ts +3 -1
  19. package/dist/grid/types.d.ts +17 -3
  20. package/dist/index.d.ts +6 -6
  21. package/dist/index.js +4372 -1087
  22. package/dist/index.js.map +1 -1
  23. package/dist/select/createSelectState.d.ts +6 -0
  24. package/dist/selection/Selection.d.ts +16 -0
  25. package/dist/selection/SelectionManager.d.ts +119 -0
  26. package/dist/selection/createMultipleSelectionState.d.ts +59 -0
  27. package/dist/selection/index.d.ts +1 -0
  28. package/dist/table/TableCollection.d.ts +30 -0
  29. package/dist/table/createTreeGridState.d.ts +15 -0
  30. package/dist/table/index.d.ts +2 -1
  31. package/dist/table/types.d.ts +58 -0
  32. package/dist/toast/createToastState.d.ts +16 -0
  33. package/dist/toast/index.d.ts +1 -1
  34. package/dist/tree/types.d.ts +14 -6
  35. package/package.json +1 -1
  36. package/src/calendar/IncompleteDate.ts +430 -0
  37. package/src/calendar/createCalendarState.ts +129 -40
  38. package/src/calendar/createDateFieldState.ts +633 -477
  39. package/src/calendar/createRangeCalendarState.ts +343 -30
  40. package/src/calendar/createTimeFieldState.ts +179 -467
  41. package/src/calendar/index.ts +2 -0
  42. package/src/calendar/intl/index.ts +364 -0
  43. package/src/collections/ListCollection.ts +31 -0
  44. package/src/collections/createListState.ts +139 -17
  45. package/src/collections/createMenuState.ts +3 -0
  46. package/src/collections/createSelectionState.ts +38 -28
  47. package/src/collections/getChildNodes.ts +129 -0
  48. package/src/collections/index.ts +12 -0
  49. package/src/collections/types.ts +10 -0
  50. package/src/color/Color.ts +104 -133
  51. package/src/color/intl/index.ts +1409 -0
  52. package/src/combobox/createComboBoxState.ts +38 -13
  53. package/src/dnd/createDroppableCollectionState.ts +125 -6
  54. package/src/dnd/index.d.ts +1 -0
  55. package/src/dnd/index.ts +1 -0
  56. package/src/dnd/types.d.ts +3 -1
  57. package/src/dnd/types.ts +4 -1
  58. package/src/grid/createGridState.ts +36 -2
  59. package/src/grid/types.ts +17 -3
  60. package/src/index.d.ts +1 -0
  61. package/src/index.ts +14 -0
  62. package/src/select/createSelectState.ts +15 -2
  63. package/src/selection/Selection.ts +28 -0
  64. package/src/selection/SelectionManager.ts +541 -0
  65. package/src/selection/createMultipleSelectionState.ts +247 -0
  66. package/src/selection/index.ts +6 -0
  67. package/src/steplist/createStepListState.ts +27 -4
  68. package/src/table/TableCollection.ts +213 -43
  69. package/src/table/createTableState.ts +20 -0
  70. package/src/table/createTreeGridState.ts +146 -0
  71. package/src/table/index.ts +3 -0
  72. package/src/table/types.ts +66 -0
  73. package/src/tabs/createTabListState.ts +11 -9
  74. package/src/toast/createToastState.ts +43 -10
  75. package/src/toast/index.ts +1 -0
  76. package/src/tree/createTreeState.ts +37 -6
  77. package/src/tree/types.ts +14 -6
@@ -35,13 +35,23 @@ export type CalendarDayOfWeek = "sun" | "mon" | "tue" | "wed" | "thu" | "fri" |
35
35
 
36
36
  const dayOfWeekNames: CalendarDayOfWeek[] = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"];
37
37
 
38
- export interface CalendarStateProps<T extends DateValue = DateValue> {
38
+ export type CalendarSelectionMode = "single" | "multiple";
39
+ export type CalendarValueType<T, M extends CalendarSelectionMode> = M extends "single"
40
+ ? T
41
+ : readonly T[];
42
+
43
+ export interface CalendarStateProps<
44
+ T extends DateValue = DateValue,
45
+ M extends CalendarSelectionMode = "single",
46
+ > {
39
47
  /** The current value (controlled). */
40
- value?: MaybeAccessor<T | null>;
48
+ value?: MaybeAccessor<CalendarValueType<T | null, M> | undefined>;
41
49
  /** The default value (uncontrolled). */
42
- defaultValue?: T | null;
50
+ defaultValue?: CalendarValueType<T | null, M> | null;
43
51
  /** Handler called when the value changes. */
44
- onChange?: (value: T | null) => void;
52
+ onChange?: (value: CalendarValueType<T | null, M>) => void;
53
+ /** Whether single or multiple date selection is enabled. */
54
+ selectionMode?: MaybeAccessor<M>;
45
55
  /** The minimum allowed date. */
46
56
  minValue?: MaybeAccessor<DateValue | undefined>;
47
57
  /** The maximum allowed date. */
@@ -82,15 +92,24 @@ export interface CalendarStateProps<T extends DateValue = DateValue> {
82
92
  firstDayOfWeek?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
83
93
  }
84
94
 
85
- export interface CalendarState<T extends DateValue = DateValue> {
86
- /** The currently selected date. */
87
- value: Accessor<T | null>;
88
- /** Sets the selected date. */
89
- setValue: (value: T | null) => void;
95
+ export interface CalendarState<
96
+ T extends DateValue = DateValue,
97
+ M extends CalendarSelectionMode = "single",
98
+ > {
99
+ /** Whether single or multiple selection is enabled. */
100
+ selectionMode: Accessor<CalendarSelectionMode>;
101
+ /** The currently selected date(s). */
102
+ value: Accessor<CalendarValueType<T | null, M>>;
103
+ /** Sets the selected date(s). */
104
+ setValue: (value: CalendarValueType<T | null, M>) => void;
90
105
  /** The currently focused date. */
91
106
  focusedDate: Accessor<CalendarDate>;
92
107
  /** Sets the focused date. */
93
108
  setFocusedDate: (date: CalendarDate) => void;
109
+ /** The minimum allowed date that a user may select. */
110
+ minValue: Accessor<DateValue | null>;
111
+ /** The maximum allowed date that a user may select. */
112
+ maxValue: Accessor<DateValue | null>;
94
113
  /** Whether the calendar is disabled. */
95
114
  isDisabled: Accessor<boolean>;
96
115
  /** Whether the calendar is read-only. */
@@ -164,11 +183,17 @@ export interface CalendarState<T extends DateValue = DateValue> {
164
183
  /**
165
184
  * Provides state management for a calendar component.
166
185
  */
167
- export function createCalendarState<T extends DateValue = CalendarDate>(
168
- props: CalendarStateProps<T> = {},
169
- ): CalendarState<T> {
186
+ export function createCalendarState<
187
+ T extends DateValue = CalendarDate,
188
+ M extends CalendarSelectionMode = "single",
189
+ >(props: CalendarStateProps<T, M> = {}): CalendarState<T, M> {
170
190
  const timeZone = getLocalTimeZone();
171
191
  const locale = createMemo(() => access(props.locale) ?? "en-US");
192
+ const minValueState = createMemo(() => access(props.minValue) ?? null);
193
+ const maxValueState = createMemo(() => access(props.maxValue) ?? null);
194
+ const selectionMode = createMemo<CalendarSelectionMode>(
195
+ () => access(props.selectionMode) ?? "single",
196
+ );
172
197
  const resolvedOptions = createMemo(() => new DateFormatter(locale()).resolvedOptions());
173
198
  const calendar = createMemo(() =>
174
199
  (props.createCalendar ?? intlCreateCalendar)(resolvedOptions().calendar as CalendarIdentifier),
@@ -181,19 +206,21 @@ export function createCalendarState<T extends DateValue = CalendarDate>(
181
206
  intlToCalendar(toCalendarDate(date), calendar());
182
207
 
183
208
  const selectionAlignmentOffset = (): number => {
209
+ // Mirrors @react-stately/calendar useCalendarState, where selectionAlignment
210
+ // defaults to "center" (alignCenter) when not provided.
184
211
  switch (props.selectionAlignment) {
212
+ case "start":
213
+ return 0;
185
214
  case "end":
186
215
  return Math.max(visibleMonths - 1, 0);
187
- case "center": {
216
+ case "center":
217
+ default: {
188
218
  let halfDuration = Math.floor(visibleMonths / 2);
189
219
  if (halfDuration > 0 && visibleMonths % 2 === 0) {
190
220
  halfDuration--;
191
221
  }
192
222
  return halfDuration;
193
223
  }
194
- case "start":
195
- default:
196
- return 0;
197
224
  }
198
225
  };
199
226
 
@@ -238,32 +265,44 @@ export function createCalendarState<T extends DateValue = CalendarDate>(
238
265
  return toDisplayCalendarDate(props.defaultFocusedValue);
239
266
  }
240
267
  const controlledValue = access(props.value);
241
- if (controlledValue) {
242
- return toDisplayCalendarDate(controlledValue);
268
+ const controlledFirst = Array.isArray(controlledValue) ? controlledValue[0] : controlledValue;
269
+ if (controlledFirst) {
270
+ return toDisplayCalendarDate(controlledFirst);
243
271
  }
244
- if (props.defaultValue) {
245
- return toDisplayCalendarDate(props.defaultValue);
272
+ const defaultFirst = Array.isArray(props.defaultValue)
273
+ ? props.defaultValue[0]
274
+ : props.defaultValue;
275
+ if (defaultFirst) {
276
+ return toDisplayCalendarDate(defaultFirst);
246
277
  }
247
278
  return intlToCalendar(today(timeZone), calendar());
248
279
  };
249
280
 
250
281
  // State signals
251
282
  const initialFocusedDate = constrainDate(getInitialFocusedDate());
252
- const [internalValue, setInternalValue] = createSignal<T | null>(props.defaultValue ?? null);
283
+ const [internalValue, setInternalValue] = createSignal<T | T[] | null>(
284
+ (props.defaultValue as T | T[] | null) ?? null,
285
+ );
253
286
  const [focusedDate, setFocusedDateInternal] = createSignal<CalendarDate>(initialFocusedDate);
254
287
  const [visibleRangeStart, setVisibleRangeStart] = createSignal<CalendarDate>(
255
288
  alignVisibleRangeStart(initialFocusedDate),
256
289
  );
257
- const [isFocused, setFocused] = createSignal(false);
290
+ // Init from autoFocus, mirroring @react-stately useCalendarState
291
+ // (`useState(props.autoFocus || false)`). Without autoFocus the calendar
292
+ // mounts unfocused and never steals DOM focus.
293
+ const [isFocused, setFocused] = createSignal(props.autoFocus ?? false);
258
294
  const [isPaginating, setIsPaginating] = createSignal(false);
259
295
 
260
296
  // Controlled vs uncontrolled value
261
- const sourceValue = createMemo<T | null>(() => {
297
+ const sourceValue = createMemo<T | T[] | null>(() => {
262
298
  const controlled = access(props.value);
263
- return controlled !== undefined ? controlled : internalValue();
299
+ return controlled !== undefined ? (controlled as T | T[] | null) : internalValue();
264
300
  });
265
- const value = createMemo<T | null>(() => {
301
+ const value = createMemo<T | T[] | null>(() => {
266
302
  const currentValue = sourceValue();
303
+ if (Array.isArray(currentValue)) {
304
+ return currentValue.map((v) => toDisplayCalendarDate(v) as unknown as T);
305
+ }
267
306
  return currentValue ? (toDisplayCalendarDate(currentValue) as unknown as T) : null;
268
307
  });
269
308
 
@@ -370,13 +409,22 @@ export function createCalendarState<T extends DateValue = CalendarDate>(
370
409
  });
371
410
 
372
411
  // Set value with onChange callback
373
- const setValue = (newValue: T | null) => {
412
+ const setValue = (newValue: T | T[] | null) => {
374
413
  if (isDisabled() || isReadOnly()) return;
375
414
 
376
415
  const oldValue = sourceValue();
377
- const nextValue = newValue
378
- ? (convertValue(constrainDate(toDisplayCalendarDate(newValue)), oldValue) as T)
379
- : null;
416
+ const referenceValue = Array.isArray(oldValue) ? (oldValue[0] ?? null) : oldValue;
417
+
418
+ let nextValue: T | T[] | null;
419
+ if (newValue == null) {
420
+ nextValue = selectionMode() === "multiple" ? ([] as T[]) : null;
421
+ } else if (Array.isArray(newValue)) {
422
+ nextValue = newValue.map(
423
+ (v) => convertValue(constrainDate(toDisplayCalendarDate(v)), referenceValue) as T,
424
+ );
425
+ } else {
426
+ nextValue = convertValue(constrainDate(toDisplayCalendarDate(newValue)), referenceValue) as T;
427
+ }
380
428
 
381
429
  const controlled = access(props.value);
382
430
  if (controlled === undefined) {
@@ -384,7 +432,7 @@ export function createCalendarState<T extends DateValue = CalendarDate>(
384
432
  }
385
433
 
386
434
  if (!Object.is(oldValue, nextValue) && props.onChange) {
387
- props.onChange(nextValue);
435
+ props.onChange(nextValue as CalendarValueType<T | null, M>);
388
436
  }
389
437
  };
390
438
 
@@ -404,13 +452,21 @@ export function createCalendarState<T extends DateValue = CalendarDate>(
404
452
  // Check if a date is selected
405
453
  const isSelected = (date: DateValue): boolean => {
406
454
  const v = value();
407
- if (!v) return false;
408
- return isSameDay(toDisplayCalendarDate(date), toDisplayCalendarDate(v));
455
+ if (v == null) return false;
456
+ const target = toDisplayCalendarDate(date);
457
+ if (Array.isArray(v)) {
458
+ return v.some((d) => isSameDay(target, toDisplayCalendarDate(d)));
459
+ }
460
+ return isSameDay(target, toDisplayCalendarDate(v));
409
461
  };
410
462
 
411
- // Check if a date is focused
463
+ // Check if a date is focused. Mirrors @react-stately/calendar isCellFocused
464
+ // (`isFocused && focusedDate && isSameDay(date, focusedDate)`): the
465
+ // calendar-level `isFocused` flag gates cell focus so the calendar does NOT
466
+ // steal DOM focus on mount. The roving tabbable cell is tracked separately by
467
+ // the cell's `tabIndex` (isSameDay against focusedDate, ungated).
412
468
  const isCellFocused = (date: DateValue): boolean => {
413
- return isSameDay(toDisplayCalendarDate(date), focusedDate());
469
+ return isFocused() && isSameDay(toDisplayCalendarDate(date), focusedDate());
414
470
  };
415
471
 
416
472
  // Check if a date is unavailable
@@ -427,6 +483,15 @@ export function createCalendarState<T extends DateValue = CalendarDate>(
427
483
  const maxValue = access(props.maxValue);
428
484
  const calDate = toDisplayCalendarDate(date);
429
485
 
486
+ // Dates outside the visible range are disabled, mirroring
487
+ // @react-stately/calendar isCellDisabled (`date.compare(startDate) < 0 ||
488
+ // date.compare(endDate) > 0` over the visible range). This is what marks the
489
+ // leading/trailing padding cells (days from the adjacent months that fill out
490
+ // the first/last grid weeks) as aria-disabled.
491
+ const range = visibleRange();
492
+ if (calDate.compare(toDisplayCalendarDate(range.start)) < 0) return true;
493
+ if (calDate.compare(toDisplayCalendarDate(range.end)) > 0) return true;
494
+
430
495
  if (minValue && calDate.compare(toDisplayCalendarDate(minValue)) < 0) return true;
431
496
  if (maxValue && calDate.compare(toDisplayCalendarDate(maxValue)) > 0) return true;
432
497
 
@@ -502,17 +567,38 @@ export function createCalendarState<T extends DateValue = CalendarDate>(
502
567
  const selectFocusedDate = () => {
503
568
  if (isReadOnly() || isDisabled()) return;
504
569
  const date = focusedDate();
505
- if (!isCellDisabled(date) && !isCellUnavailable(date)) {
506
- setValue(date as unknown as T);
570
+ if (!isCellUnavailable(date)) {
571
+ selectDate(date);
507
572
  }
508
573
  };
509
574
 
510
575
  const selectDate = (date: CalendarDate) => {
576
+ // Mirror @react-stately/calendar `selectDate(date) { setValue(date); }`:
577
+ // no isCellDisabled/isCellUnavailable guard here. Upstream gates selection
578
+ // at the cell layer (createCalendarCell handlePointerDown/handleClick guard
579
+ // on isDisabled/isUnavailable before calling selectDate); the state method
580
+ // only refuses when the whole calendar is disabled/readOnly (setValue's
581
+ // guard). Double-guarding here also broke programmatic selection of dates
582
+ // outside the current visible range.
511
583
  if (isReadOnly() || isDisabled()) return;
512
- if (!isCellDisabled(date) && !isCellUnavailable(date)) {
584
+
585
+ if (selectionMode() === "multiple") {
586
+ const current = value();
587
+ const base: CalendarDate[] = Array.isArray(current)
588
+ ? current.map((v) => toDisplayCalendarDate(v))
589
+ : current != null
590
+ ? [toDisplayCalendarDate(current)]
591
+ : [];
592
+ const target = toDisplayCalendarDate(date);
593
+ const index = base.findIndex((d) => isSameDay(d, target));
594
+ const nextValue =
595
+ index >= 0 ? [...base.slice(0, index), ...base.slice(index + 1)] : [...base, target];
596
+ setValue(nextValue as unknown as T[]);
597
+ } else {
513
598
  setValue(date as unknown as T);
514
- setFocusedDate(date);
515
599
  }
600
+
601
+ setFocusedDate(date);
516
602
  };
517
603
 
518
604
  // Get dates in a specific week
@@ -548,8 +634,9 @@ export function createCalendarState<T extends DateValue = CalendarDate>(
548
634
  };
549
635
 
550
636
  return {
551
- value,
552
- setValue,
637
+ selectionMode,
638
+ value: value as unknown as Accessor<CalendarValueType<T | null, M>>,
639
+ setValue: setValue as unknown as (value: CalendarValueType<T | null, M>) => void,
553
640
  focusedDate,
554
641
  setFocusedDate,
555
642
  isDisabled,
@@ -557,6 +644,8 @@ export function createCalendarState<T extends DateValue = CalendarDate>(
557
644
  visibleRange,
558
645
  timeZone,
559
646
  locale,
647
+ minValue: minValueState,
648
+ maxValue: maxValueState,
560
649
  firstDayOfWeek: firstDayOfWeekName,
561
650
  validationState,
562
651
  isValueInvalid,