@proyecto-viviana/solid-stately 0.6.0-rc.0 → 0.6.0-rc.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 (127) hide show
  1. package/README.md +59 -29
  2. package/dist/calendar/createCalendarState.d.ts +3 -1
  3. package/dist/calendar/createCalendarState.d.ts.map +1 -1
  4. package/dist/calendar/createDatePickerState.d.ts +2 -0
  5. package/dist/calendar/createDatePickerState.d.ts.map +1 -1
  6. package/dist/calendar/createRangeCalendarState.d.ts +2 -0
  7. package/dist/calendar/createRangeCalendarState.d.ts.map +1 -1
  8. package/dist/calendar/createTimeFieldState.d.ts +1 -1
  9. package/dist/calendar/createTimeFieldState.d.ts.map +1 -1
  10. package/dist/checkbox/createCheckboxGroupState.d.ts +2 -0
  11. package/dist/checkbox/createCheckboxGroupState.d.ts.map +1 -1
  12. package/dist/collections/createListState.d.ts.map +1 -1
  13. package/dist/collections/createMenuState.d.ts +46 -2
  14. package/dist/collections/createMenuState.d.ts.map +1 -1
  15. package/dist/collections/createSelectionState.d.ts.map +1 -1
  16. package/dist/collections/index.d.ts +1 -1
  17. package/dist/collections/index.d.ts.map +1 -1
  18. package/dist/color/createColorAreaState.d.ts +4 -0
  19. package/dist/color/createColorAreaState.d.ts.map +1 -1
  20. package/dist/color/createColorFieldState.d.ts +2 -0
  21. package/dist/color/createColorFieldState.d.ts.map +1 -1
  22. package/dist/color/createColorSliderState.d.ts +2 -0
  23. package/dist/color/createColorSliderState.d.ts.map +1 -1
  24. package/dist/color/createColorWheelState.d.ts.map +1 -1
  25. package/dist/combobox/createComboBoxState.d.ts +90 -23
  26. package/dist/combobox/createComboBoxState.d.ts.map +1 -1
  27. package/dist/combobox/index.d.ts +1 -1
  28. package/dist/combobox/index.d.ts.map +1 -1
  29. package/dist/data/createAsyncList.d.ts.map +1 -1
  30. package/dist/data/createListData.d.ts.map +1 -1
  31. package/dist/data/createTreeData.d.ts.map +1 -1
  32. package/dist/dnd/createDraggableCollectionState.d.ts +5 -1
  33. package/dist/dnd/createDraggableCollectionState.d.ts.map +1 -1
  34. package/dist/dnd/createDroppableCollectionState.d.ts +6 -0
  35. package/dist/dnd/createDroppableCollectionState.d.ts.map +1 -1
  36. package/dist/form/createFormValidationState.d.ts.map +1 -1
  37. package/dist/grid/createGridState.d.ts +2 -2
  38. package/dist/grid/createGridState.d.ts.map +1 -1
  39. package/dist/index.d.ts +5 -5
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +1012 -471
  42. package/dist/index.js.map +1 -1
  43. package/dist/menu/index.d.ts +2 -1
  44. package/dist/menu/index.d.ts.map +1 -1
  45. package/dist/numberfield/createNumberFieldState.d.ts +6 -3
  46. package/dist/numberfield/createNumberFieldState.d.ts.map +1 -1
  47. package/dist/private/flags/flags.d.ts.map +1 -0
  48. package/dist/private/flags/flags.js +1 -1
  49. package/dist/private/flags/flags.js.map +1 -1
  50. package/dist/radio/createRadioGroupState.d.ts.map +1 -1
  51. package/dist/select/createSelectState.d.ts +85 -29
  52. package/dist/select/createSelectState.d.ts.map +1 -1
  53. package/dist/select/index.d.ts +1 -1
  54. package/dist/select/index.d.ts.map +1 -1
  55. package/dist/selection/SelectionManager.d.ts.map +1 -1
  56. package/dist/selection/createMultipleSelectionState.d.ts.map +1 -1
  57. package/dist/slider/createSliderState.d.ts +35 -9
  58. package/dist/slider/createSliderState.d.ts.map +1 -1
  59. package/dist/ssr/index.d.ts +0 -6
  60. package/dist/ssr/index.d.ts.map +1 -1
  61. package/dist/steplist/createStepListState.d.ts +8 -13
  62. package/dist/steplist/createStepListState.d.ts.map +1 -1
  63. package/dist/table/createTableColumnResizeState.d.ts.map +1 -1
  64. package/dist/table/createTableState.d.ts +2 -2
  65. package/dist/table/createTableState.d.ts.map +1 -1
  66. package/dist/table/createTreeGridState.d.ts +2 -2
  67. package/dist/table/createTreeGridState.d.ts.map +1 -1
  68. package/dist/table/types.d.ts +2 -2
  69. package/dist/table/types.d.ts.map +1 -1
  70. package/dist/tabs/createTabListState.d.ts.map +1 -1
  71. package/dist/tokenfield/TokenFieldValue.d.ts +28 -4
  72. package/dist/tokenfield/TokenFieldValue.d.ts.map +1 -1
  73. package/dist/tokenfield/index.d.ts +1 -1
  74. package/dist/tokenfield/index.d.ts.map +1 -1
  75. package/dist/tree/createTreeState.d.ts +2 -2
  76. package/dist/tree/createTreeState.d.ts.map +1 -1
  77. package/dist/utils/index.d.ts +1 -1
  78. package/dist/utils/index.d.ts.map +1 -1
  79. package/dist/utils/reactivity.d.ts +5 -0
  80. package/dist/utils/reactivity.d.ts.map +1 -1
  81. package/package.json +2 -2
  82. package/src/calendar/createCalendarState.ts +139 -58
  83. package/src/calendar/createDatePickerState.ts +14 -11
  84. package/src/calendar/createRangeCalendarState.ts +43 -17
  85. package/src/calendar/createTimeFieldState.ts +1 -1
  86. package/src/checkbox/createCheckboxGroupState.ts +16 -0
  87. package/src/collections/createListState.ts +4 -4
  88. package/src/collections/createMenuState.ts +134 -4
  89. package/src/collections/createSelectionState.ts +2 -2
  90. package/src/collections/index.ts +4 -0
  91. package/src/color/createColorAreaState.ts +19 -0
  92. package/src/color/createColorFieldState.ts +76 -26
  93. package/src/color/createColorSliderState.ts +25 -2
  94. package/src/color/createColorWheelState.ts +9 -5
  95. package/src/combobox/createComboBoxState.ts +371 -121
  96. package/src/combobox/index.ts +4 -0
  97. package/src/data/createAsyncList.ts +19 -4
  98. package/src/data/createListData.ts +8 -2
  99. package/src/data/createTreeData.ts +11 -3
  100. package/src/dnd/createDraggableCollectionState.ts +8 -1
  101. package/src/dnd/createDroppableCollectionState.ts +10 -0
  102. package/src/form/createFormValidationState.ts +17 -3
  103. package/src/grid/createGridState.ts +15 -8
  104. package/src/index.ts +18 -2
  105. package/src/menu/index.ts +5 -0
  106. package/src/numberfield/createNumberFieldState.ts +97 -35
  107. package/src/radio/createRadioGroupState.ts +4 -1
  108. package/src/select/createSelectState.ts +253 -131
  109. package/src/select/index.ts +8 -1
  110. package/src/selection/SelectionManager.ts +3 -1
  111. package/src/selection/createMultipleSelectionState.ts +2 -2
  112. package/src/slider/createSliderState.ts +227 -103
  113. package/src/ssr/index.ts +11 -4
  114. package/src/steplist/createStepListState.ts +156 -123
  115. package/src/table/createTableColumnResizeState.ts +51 -11
  116. package/src/table/createTableState.ts +5 -4
  117. package/src/table/createTreeGridState.ts +3 -4
  118. package/src/table/types.ts +2 -2
  119. package/src/tabs/createTabListState.ts +8 -31
  120. package/src/tokenfield/TokenFieldValue.ts +75 -16
  121. package/src/tokenfield/index.ts +1 -0
  122. package/src/tree/createTreeState.ts +15 -8
  123. package/src/utils/index.ts +1 -0
  124. package/src/utils/reactivity.ts +8 -0
  125. package/dist/flags/flags.d.ts.map +0 -1
  126. /package/dist/{flags → private/flags}/flags.d.ts +0 -0
  127. /package/src/{flags → private/flags}/flags.ts +0 -0
package/dist/index.js CHANGED
@@ -145,7 +145,19 @@
145
145
  * OF ANY KIND, either express or implied. See the License for the specific language
146
146
  * governing permissions and limitations under the License.
147
147
  */
148
+ /*
149
+ * Copyright 2023 Adobe. All rights reserved.
150
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
151
+ * you may not use this file except in compliance with the License. You may obtain a copy
152
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
153
+ *
154
+ * Unless required by applicable law or agreed to in writing, software distributed under
155
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
156
+ * OF ANY KIND, either express or implied. See the License for the specific language
157
+ * governing permissions and limitations under the License.
158
+ */
148
159
  // Ported to SolidJS for Proyecto Viviana; based on packages/react-stately/src/menu/useMenuTriggerState.ts
160
+ // Ported to SolidJS for Proyecto Viviana; based on packages/react-stately/src/menu/useSubmenuTriggerState.ts
149
161
  /*
150
162
  * Copyright 2020 Adobe. All rights reserved.
151
163
  * This file is licensed to you under the Apache License, Version 2.0 (the "License");
@@ -683,6 +695,13 @@ function isAccessor(value) {
683
695
  return typeof value === "function";
684
696
  }
685
697
  /**
698
+ * Resolve a props bag once. Later reads call the accessor directly, so a
699
+ * function-valued field such as `getKey` is never treated as the bag.
700
+ */
701
+ function propsAccessor(props) {
702
+ return typeof props === "function" ? props : () => props;
703
+ }
704
+ /**
686
705
  * Headless state is written from factory setup and from public setters that
687
706
  * tests (and some call sites) invoke inside `createRoot` / a component body.
688
707
  * Solid 2 throws on those writes unless the signal opts into `ownedWrite`.
@@ -886,6 +905,42 @@ function createTextFieldState(props = {}) {
886
905
  }
887
906
  //#endregion
888
907
  //#region src/tokenfield/TokenFieldValue.ts
908
+ /** Represents a text selection in a TokenField. */
909
+ var SelectedRange = class {
910
+ /** The anchor position. */
911
+ anchor;
912
+ /** The current (i.e. caret) position. */
913
+ current;
914
+ /**
915
+ * Creates a new selection range. If only a single position is provided, the selection is
916
+ * collapsed.
917
+ */
918
+ constructor(anchor, current = anchor) {
919
+ this.anchor = anchor;
920
+ this.current = current;
921
+ }
922
+ /** Whether the selection is collapsed to a single caret position. */
923
+ get isCollapsed() {
924
+ return this.anchor.index === this.current.index && this.anchor.offset === this.current.offset;
925
+ }
926
+ /** The side of the selection closest to the start of the value. */
927
+ get start() {
928
+ return compare(this.anchor, this.current) < 0 ? this.anchor : this.current;
929
+ }
930
+ /** The side of the selection closest to the end of the value. */
931
+ get end() {
932
+ return compare(this.anchor, this.current) < 0 ? this.current : this.anchor;
933
+ }
934
+ /** Returns whether this selection is equal to another. */
935
+ isEqual(other) {
936
+ if (this === other) return true;
937
+ return this.anchor.index === other.anchor.index && this.anchor.offset === other.anchor.offset && this.current.index === other.current.index && this.current.offset === other.current.offset;
938
+ }
939
+ };
940
+ function compare(a, b) {
941
+ if (a.index === b.index) return a.offset - b.offset;
942
+ return a.index - b.index;
943
+ }
889
944
  var Direction = /* @__PURE__ */ function(Direction) {
890
945
  Direction[Direction["Forward"] = 1] = "Forward";
891
946
  Direction[Direction["Backward"] = -1] = "Backward";
@@ -896,38 +951,43 @@ var Direction = /* @__PURE__ */ function(Direction) {
896
951
  */
897
952
  var TokenFieldValue = class {
898
953
  static Direction = Direction;
954
+ static SelectedRange = SelectedRange;
899
955
  /** The text and token segments in the list. */
900
956
  segments;
901
- /** The caret position. */
902
- caretPosition = {
903
- index: 0,
904
- offset: 0
905
- };
957
+ /** The selected range. */
958
+ selectedRange;
906
959
  previous = null;
907
960
  next = null;
908
961
  isCoalescing = true;
909
962
  /** Create a new list with the given segments. */
910
963
  constructor(tokens, options) {
911
964
  this.segments = tokens;
912
- this.caretPosition = options?.caretPosition ?? {
965
+ this.selectedRange = options?.selectedRange ?? new SelectedRange({
913
966
  index: 0,
914
967
  offset: 0
915
- };
968
+ });
969
+ }
970
+ get caretPosition() {
971
+ return this.selectedRange.current;
916
972
  }
917
973
  createFieldValue(segments) {
918
974
  const Constructor = this.constructor;
919
975
  return new Constructor(segments);
920
976
  }
921
- /** Create a new list with the caret position set to the given position. */
922
- withCaretPosition(caretPosition) {
923
- if (this.caretPosition.index === caretPosition.index && this.caretPosition.offset === caretPosition.offset) return this;
977
+ /** Create a new list with the selected range set to the given range. */
978
+ withSelectedRange(selectedRange) {
979
+ if (this.selectedRange.isEqual(selectedRange)) return this;
924
980
  let result = this.createFieldValue(this.segments);
925
- result.caretPosition = caretPosition;
981
+ result.selectedRange = selectedRange;
926
982
  result.previous = this.previous;
927
983
  result.next = this.next;
928
984
  result.isCoalescing = this.isCoalescing;
929
985
  return result;
930
986
  }
987
+ /** Create a new list with the caret position set to the given position. */
988
+ withCaretPosition(position) {
989
+ return this.withSelectedRange(new SelectedRange(position));
990
+ }
931
991
  splitSegment(segment, offset) {
932
992
  if (!segment) {
933
993
  let empty = this.createTextSegment("");
@@ -983,14 +1043,14 @@ var TokenFieldValue = class {
983
1043
  if (endSplit) appendSegments(newSegments, [endSplit]);
984
1044
  appendSegments(newSegments, this.segments.slice(end.index + 1));
985
1045
  let segments = this.createFieldValue(newSegments);
986
- segments.caretPosition = caret;
1046
+ segments.selectedRange = new SelectedRange(caret);
987
1047
  segments.isCoalescing = coalesce;
988
1048
  if (this.isCoalescing && coalesce && this.previous) {
989
1049
  segments.previous = this.previous;
990
1050
  segments.previous.next = segments;
991
1051
  } else {
992
1052
  segments.previous = this;
993
- this.caretPosition = end;
1053
+ this.selectedRange = new SelectedRange(start, end);
994
1054
  this.next = segments;
995
1055
  }
996
1056
  return segments;
@@ -1060,15 +1120,14 @@ var TokenFieldValue = class {
1060
1120
  delete(position, segmenter, direction, coalesce = true) {
1061
1121
  let boundary = this.findBoundaryWithSegmenter(position, segmenter, direction);
1062
1122
  if (boundary) return this.replaceRange(direction === -1 ? boundary : position, direction === -1 ? position : boundary, "", coalesce);
1063
- this.caretPosition = position;
1064
- return this;
1123
+ return this.withSelectedRange(new SelectedRange(position));
1065
1124
  }
1066
1125
  /** Delete text to the next or previous line break. */
1067
1126
  deleteLine(position, direction, coalesce = true) {
1068
1127
  if (this.segments.length === 0) return this;
1069
1128
  let boundary = this.findLineBoundary(position, direction);
1070
1129
  if (boundary) return this.replaceRange(direction === -1 ? boundary : position, direction === -1 ? position : boundary, "", coalesce);
1071
- return this;
1130
+ return this.withSelectedRange(new SelectedRange(position));
1072
1131
  }
1073
1132
  /** Create a new list containing a subset of the segments. */
1074
1133
  slice(start, end) {
@@ -1213,6 +1272,10 @@ const DEFAULT_VALIDATION_RESULT = {
1213
1272
  };
1214
1273
  /** Context for server-side validation errors. */
1215
1274
  const FormValidationContext = createContext({});
1275
+ const validationErrorsSource = Symbol.for("proyecto-viviana.validationErrorsSource");
1276
+ function validationErrorsIdentity(errors) {
1277
+ return errors[validationErrorsSource] ?? errors;
1278
+ }
1216
1279
  /** Private prop key for passing validation state to children. */
1217
1280
  const privateValidationStateProp = "__formValidationState" + Date.now();
1218
1281
  function asArray(v) {
@@ -1282,10 +1345,10 @@ function createFormValidationState(props) {
1282
1345
  if (name) return Array.isArray(name) ? name.flatMap((n) => asArray(serverErrors[n])) : asArray(serverErrors[name]);
1283
1346
  return [];
1284
1347
  });
1285
- const [lastServerErrors, setLastServerErrors] = createInternalSignal(serverErrors);
1348
+ const [lastServerErrors, setLastServerErrors] = createInternalSignal(validationErrorsIdentity(serverErrors));
1286
1349
  const [isServerErrorCleared, setServerErrorCleared] = createInternalSignal(false);
1287
1350
  createEffect(() => ({
1288
- errors: serverErrors,
1351
+ errors: validationErrorsIdentity(serverErrors),
1289
1352
  last: lastServerErrors()
1290
1353
  }), ({ errors, last }) => {
1291
1354
  if (errors !== last) {
@@ -1433,6 +1496,12 @@ function createCheckboxGroupState(props = {}) {
1433
1496
  const snapshot = liveValue();
1434
1497
  if (snapshot.includes(removeVal)) setValue(snapshot.filter((v) => v !== removeVal));
1435
1498
  }
1499
+ const invalidValues = /* @__PURE__ */ new Map();
1500
+ function setInvalid(itemValue, result) {
1501
+ if (result.isInvalid) invalidValues.set(itemValue, result);
1502
+ else invalidValues.delete(itemValue);
1503
+ validation.updateValidation(mergeValidation(...invalidValues.values()));
1504
+ }
1436
1505
  function toggleValue(toggleVal) {
1437
1506
  const p = getProps();
1438
1507
  if (p.isReadOnly || p.isDisabled) return;
@@ -1458,6 +1527,7 @@ function createCheckboxGroupState(props = {}) {
1458
1527
  addValue,
1459
1528
  removeValue,
1460
1529
  toggleValue,
1530
+ setInvalid,
1461
1531
  realtimeValidation: validation.realtimeValidation,
1462
1532
  displayValidation: validation.displayValidation,
1463
1533
  updateValidation: validation.updateValidation,
@@ -1494,9 +1564,14 @@ function createIsSSR() {
1494
1564
  *
1495
1565
  * @param defaultId - Optional default ID to use instead of generating one.
1496
1566
  */
1567
+ let fallbackIdCounter = 0;
1497
1568
  function createId(defaultId) {
1498
- const uniqueId = createUniqueId();
1499
1569
  if (defaultId) return defaultId;
1570
+ let uniqueId;
1571
+ try {
1572
+ uniqueId = createUniqueId();
1573
+ } catch {}
1574
+ if (!uniqueId) uniqueId = `nh-${++fallbackIdCounter}`;
1500
1575
  return `solid-stately-${uniqueId}`;
1501
1576
  }
1502
1577
  /**
@@ -1578,7 +1653,7 @@ function createRadioGroupState(props = {}) {
1578
1653
  const state = {
1579
1654
  name,
1580
1655
  selectedValue,
1581
- defaultSelectedValue: initialProps.defaultValue ?? null,
1656
+ defaultSelectedValue: initialProps.value !== void 0 ? initialProps.value : initialProps.defaultValue ?? null,
1582
1657
  setSelectedValue,
1583
1658
  lastFocusedValue,
1584
1659
  setLastFocusedValue,
@@ -1831,7 +1906,7 @@ function getAncestors(collection, node) {
1831
1906
  * Creates selection state for a collection.
1832
1907
  */
1833
1908
  function createSelectionState(props = {}) {
1834
- const getProps = () => access(props);
1909
+ const getProps = propsAccessor(props);
1835
1910
  const [internalBehavior, setInternalBehavior] = createInternalSignal("toggle");
1836
1911
  const [internalSelectedKeys, setInternalSelectedKeys] = createInternalSignal(getInitialSelection$2(getProps().defaultSelectedKeys));
1837
1912
  const [anchorKey, setAnchorKey] = createInternalSignal(null);
@@ -2031,7 +2106,7 @@ function convertSelection(selection, defaultValue) {
2031
2106
  * Manages raw state for multiple selection and focus in a collection.
2032
2107
  */
2033
2108
  function createMultipleSelectionState(props = {}) {
2034
- const getProps = () => access(props);
2109
+ const getProps = propsAccessor(props);
2035
2110
  const [isFocused, setIsFocused] = createInternalSignal(false);
2036
2111
  const [focusedKey, setFocusedKeySignal] = createInternalSignal(null);
2037
2112
  const [childFocusStrategy, setChildFocusStrategy] = createInternalSignal(null);
@@ -2403,7 +2478,7 @@ var SelectionManager = class SelectionManager {
2403
2478
  }
2404
2479
  isDisabled(key) {
2405
2480
  const item = this.collection.getItem(key);
2406
- return this.state.disabledBehavior === "all" && (this.state.disabledKeys.has(key) || !!item?.props?.isDisabled) && item?.props?.disabledBehavior !== "selection";
2481
+ return this.state.disabledBehavior === "all" && (this.state.disabledKeys.has(key) || !!item?.props?.isDisabled || item?.isDisabled === true) && item?.props?.disabledBehavior !== "selection";
2407
2482
  }
2408
2483
  isLink(key) {
2409
2484
  return !!this.collection.getItem(key)?.props?.href;
@@ -2445,7 +2520,7 @@ var SelectionManager = class SelectionManager {
2445
2520
  * Creates state for a list component with selection.
2446
2521
  */
2447
2522
  function createListState(props) {
2448
- const getProps = () => access(props);
2523
+ const getProps = propsAccessor(props);
2449
2524
  const collection = createMemo(() => {
2450
2525
  const p = getProps();
2451
2526
  return new ListCollection((p.items ?? []).map((item, index) => {
@@ -2606,7 +2681,7 @@ function createFilteredListState(state, filterFn) {
2606
2681
  * Creates state for a single-select list component.
2607
2682
  */
2608
2683
  function createSingleSelectListState(props) {
2609
- const getProps = () => access(props);
2684
+ const getProps = propsAccessor(props);
2610
2685
  const listState = createListState({
2611
2686
  get items() {
2612
2687
  return getProps().items;
@@ -2631,7 +2706,7 @@ function createSingleSelectListState(props) {
2631
2706
  allowDuplicateSelectionEvents: true,
2632
2707
  get selectedKeys() {
2633
2708
  const key = getProps().selectedKey;
2634
- return key != null ? [key] : [];
2709
+ return key !== void 0 ? key != null ? [key] : [] : void 0;
2635
2710
  },
2636
2711
  get defaultSelectedKeys() {
2637
2712
  const key = getProps().defaultSelectedKey;
@@ -2671,6 +2746,8 @@ function createSingleSelectListState(props) {
2671
2746
  *
2672
2747
  * createMenuTriggerState is ported from
2673
2748
  * packages/react-stately/src/menu/useMenuTriggerState.ts.
2749
+ * createSubmenuTriggerState is ported from
2750
+ * packages/react-stately/src/menu/useSubmenuTriggerState.ts.
2674
2751
  * createMenuState is a local composition of the separately attributed list state.
2675
2752
  */
2676
2753
  /**
@@ -2678,7 +2755,7 @@ function createSingleSelectListState(props) {
2678
2755
  * Menus are single-select lists that support actions.
2679
2756
  */
2680
2757
  function createMenuState(props) {
2681
- const getProps = () => access(props);
2758
+ const getProps = propsAccessor(props);
2682
2759
  const listState = createListState({
2683
2760
  get items() {
2684
2761
  return getProps().items;
@@ -2733,11 +2810,29 @@ function createMenuState(props) {
2733
2810
  }
2734
2811
  /**
2735
2812
  * Creates state for a menu trigger. Mirrors `@react-stately/menu` `useMenuTriggerState`:
2736
- * overlay open state plus the focus strategy passed through to `createMenu`.
2813
+ * overlay open state, the focus strategy passed through to `createMenu`, and the
2814
+ * expanded submenu stack.
2737
2815
  */
2738
2816
  function createMenuTriggerState(props = {}) {
2739
2817
  const overlay = createOverlayTriggerState(props);
2740
2818
  const [focusStrategy, setFocusStrategy] = createInternalSignal(null);
2819
+ const [expandedKeysStack, setExpandedKeysStack] = createInternalSignal([]);
2820
+ const openSubmenu = (triggerKey, level) => {
2821
+ setExpandedKeysStack((oldStack) => {
2822
+ if (level > oldStack.length) return oldStack;
2823
+ return [...oldStack.slice(0, level), triggerKey];
2824
+ });
2825
+ };
2826
+ const closeSubmenu = (triggerKey, level) => {
2827
+ setExpandedKeysStack((oldStack) => {
2828
+ if (oldStack[level] !== triggerKey) return oldStack;
2829
+ return oldStack.slice(0, level);
2830
+ });
2831
+ };
2832
+ const close = () => {
2833
+ setExpandedKeysStack([]);
2834
+ overlay.close();
2835
+ };
2741
2836
  return {
2742
2837
  ...overlay,
2743
2838
  focusStrategy,
@@ -2749,7 +2844,46 @@ function createMenuTriggerState(props = {}) {
2749
2844
  toggle(strategy = null) {
2750
2845
  setFocusStrategy(strategy);
2751
2846
  overlay.toggle();
2752
- }
2847
+ },
2848
+ close,
2849
+ expandedKeysStack,
2850
+ openSubmenu,
2851
+ closeSubmenu
2852
+ };
2853
+ }
2854
+ /**
2855
+ * Creates state for a submenu trigger. Mirrors `@react-stately/menu`
2856
+ * `useSubmenuTriggerState`: open state is the root stack entry at this level.
2857
+ */
2858
+ function createSubmenuTriggerState(props, state) {
2859
+ const getProps = propsAccessor(props);
2860
+ const submenuLevel = state.expandedKeysStack().length;
2861
+ const [focusStrategy, setFocusStrategy] = createInternalSignal(null);
2862
+ const isOpen = () => state.expandedKeysStack()[submenuLevel] === getProps().triggerKey;
2863
+ const open = (strategy) => {
2864
+ setFocusStrategy(strategy ?? null);
2865
+ state.openSubmenu(getProps().triggerKey, submenuLevel);
2866
+ };
2867
+ const close = () => {
2868
+ setFocusStrategy(null);
2869
+ state.closeSubmenu(getProps().triggerKey, submenuLevel);
2870
+ };
2871
+ const toggle = (strategy) => {
2872
+ setFocusStrategy(strategy ?? null);
2873
+ if (isOpen()) close();
2874
+ else open(strategy);
2875
+ };
2876
+ return {
2877
+ focusStrategy,
2878
+ isOpen,
2879
+ open,
2880
+ close,
2881
+ closeAll: () => state.close(),
2882
+ submenuLevel,
2883
+ setOpen() {},
2884
+ toggle,
2885
+ point: null,
2886
+ setPoint() {}
2753
2887
  };
2754
2888
  }
2755
2889
  //#endregion
@@ -2773,18 +2907,22 @@ const dayOfWeekNames$1 = [
2773
2907
  * Provides state management for a calendar component.
2774
2908
  */
2775
2909
  function createCalendarState(props = {}) {
2910
+ const getProps = propsAccessor(props);
2776
2911
  const timeZone = getLocalTimeZone$1();
2777
- const locale = createMemo(() => access(props.locale) ?? "en-US");
2778
- const minValueState = createMemo(() => access(props.minValue) ?? null);
2779
- const maxValueState = createMemo(() => access(props.maxValue) ?? null);
2780
- const selectionMode = createMemo(() => access(props.selectionMode) ?? "single");
2912
+ const locale = createMemo(() => access(getProps().locale) ?? "en-US");
2913
+ const minValueState = createMemo(() => access(getProps().minValue) ?? null);
2914
+ const maxValueState = createMemo(() => access(getProps().maxValue) ?? null);
2915
+ const selectionMode = createMemo(() => access(getProps().selectionMode) ?? "single");
2781
2916
  const resolvedOptions = createMemo(() => new DateFormatter$1(locale()).resolvedOptions());
2782
- const calendar = createMemo(() => (props.createCalendar ?? createCalendar$1)(resolvedOptions().calendar));
2783
- const visibleMonths = createMemo(() => Math.max(1, Number(access(props.visibleMonths) ?? 1)));
2784
- const firstDayOfWeekName = () => props.firstDayOfWeek == null ? void 0 : dayOfWeekNames$1[props.firstDayOfWeek];
2917
+ const calendar = createMemo(() => (getProps().createCalendar ?? createCalendar$1)(resolvedOptions().calendar));
2918
+ const visibleMonths = createMemo(() => Math.max(1, Number(access(getProps().visibleMonths) ?? 1)));
2919
+ const firstDayOfWeekName = () => {
2920
+ const firstDayOfWeek = getProps().firstDayOfWeek;
2921
+ return firstDayOfWeek == null ? void 0 : dayOfWeekNames$1[firstDayOfWeek];
2922
+ };
2785
2923
  const toDisplayCalendarDate = (date) => toCalendar$1(toCalendarDate$3(date), calendar());
2786
2924
  const selectionAlignmentOffset = () => {
2787
- switch (props.selectionAlignment) {
2925
+ switch (getProps().selectionAlignment) {
2788
2926
  case "start": return 0;
2789
2927
  case "end": return Math.max(visibleMonths() - 1, 0);
2790
2928
  default: {
@@ -2794,9 +2932,26 @@ function createCalendarState(props = {}) {
2794
2932
  }
2795
2933
  }
2796
2934
  };
2935
+ const rawAlignStart = (date) => startOfMonth$1(date);
2936
+ const rawAlignEnd = (date, months) => startOfMonth$1(date).subtract({ months: Math.max(months - 1, 0) });
2937
+ const constrainVisibleRangeStart = (date, aligned, months) => {
2938
+ const minValue = access(getProps().minValue);
2939
+ const maxValue = access(getProps().maxValue);
2940
+ let constrained = aligned;
2941
+ if (minValue && date.compare(toDisplayCalendarDate(minValue)) >= 0) {
2942
+ const minStart = rawAlignStart(toDisplayCalendarDate(minValue));
2943
+ if (minStart.compare(constrained) > 0) constrained = minStart;
2944
+ }
2945
+ if (maxValue && date.compare(toDisplayCalendarDate(maxValue)) <= 0) {
2946
+ const maxStart = rawAlignEnd(toDisplayCalendarDate(maxValue), months);
2947
+ if (maxStart.compare(constrained) < 0) constrained = maxStart;
2948
+ }
2949
+ return constrained;
2950
+ };
2797
2951
  const alignVisibleRangeStart = (date) => {
2798
2952
  const offset = selectionAlignmentOffset();
2799
- return startOfMonth$1(offset > 0 ? date.subtract({ months: offset }) : date);
2953
+ const aligned = rawAlignStart(offset > 0 ? date.subtract({ months: offset }) : date);
2954
+ return constrainVisibleRangeStart(date, aligned, visibleMonths());
2800
2955
  };
2801
2956
  const visibleRangeEnd = (start) => {
2802
2957
  let end = endOfMonth$1(start);
@@ -2804,32 +2959,37 @@ function createCalendarState(props = {}) {
2804
2959
  return end;
2805
2960
  };
2806
2961
  const constrainDate = (date) => {
2807
- const minValue = access(props.minValue);
2808
- const maxValue = access(props.maxValue);
2962
+ const minValue = access(getProps().minValue);
2963
+ const maxValue = access(getProps().maxValue);
2809
2964
  let constrained = date;
2810
2965
  if (minValue && constrained.compare(toDisplayCalendarDate(minValue)) < 0) constrained = toDisplayCalendarDate(minValue);
2811
2966
  if (maxValue && constrained.compare(toDisplayCalendarDate(maxValue)) > 0) constrained = toDisplayCalendarDate(maxValue);
2812
2967
  return constrained;
2813
2968
  };
2814
2969
  const getInitialFocusedDate = () => {
2815
- const controlledFocused = access(props.focusedValue);
2970
+ const controlledFocused = access(getProps().focusedValue);
2816
2971
  if (controlledFocused) return toDisplayCalendarDate(controlledFocused);
2817
- if (props.defaultFocusedValue) return toDisplayCalendarDate(props.defaultFocusedValue);
2818
- const controlledValue = access(props.value);
2819
- const controlledFirst = Array.isArray(controlledValue) ? controlledValue[0] : controlledValue;
2820
- if (controlledFirst) return toDisplayCalendarDate(controlledFirst);
2821
- const defaultFirst = Array.isArray(props.defaultValue) ? props.defaultValue[0] : props.defaultValue;
2822
- if (defaultFirst) return toDisplayCalendarDate(defaultFirst);
2972
+ const defaultFocusedValue = getProps().defaultFocusedValue;
2973
+ if (defaultFocusedValue) return toDisplayCalendarDate(defaultFocusedValue);
2974
+ const controlledValue = access(getProps().value);
2975
+ if (controlledValue !== void 0) {
2976
+ const controlledFirst = Array.isArray(controlledValue) ? controlledValue[0] : controlledValue;
2977
+ if (controlledFirst) return toDisplayCalendarDate(controlledFirst);
2978
+ } else {
2979
+ const defaultValue = getProps().defaultValue;
2980
+ const defaultFirst = Array.isArray(defaultValue) ? defaultValue[0] : defaultValue;
2981
+ if (defaultFirst) return toDisplayCalendarDate(defaultFirst);
2982
+ }
2823
2983
  return toCalendar$1(today$1(timeZone), calendar());
2824
2984
  };
2825
2985
  const initialFocusedDate = constrainDate(getInitialFocusedDate());
2826
- const [internalValue, setInternalValue] = createInternalSignal(props.defaultValue ?? null);
2986
+ const [internalValue, setInternalValue] = createInternalSignal(getProps().defaultValue ?? null);
2827
2987
  const [focusedDate, setFocusedDateInternal] = createInternalSignal(initialFocusedDate);
2828
2988
  const [visibleRangeStart, setVisibleRangeStart] = createInternalSignal(alignVisibleRangeStart(initialFocusedDate));
2829
- const [isFocused, setFocused] = createInternalSignal(props.autoFocus ?? false);
2989
+ const [isFocused, setFocused] = createInternalSignal(getProps().autoFocus ?? false);
2830
2990
  const [isPaginating, setIsPaginating] = createInternalSignal(false);
2831
2991
  const sourceValue = createMemo(() => {
2832
- const controlled = access(props.value);
2992
+ const controlled = access(getProps().value);
2833
2993
  return controlled !== void 0 ? controlled : internalValue();
2834
2994
  });
2835
2995
  const value = createMemo(() => {
@@ -2837,10 +2997,23 @@ function createCalendarState(props = {}) {
2837
2997
  if (Array.isArray(currentValue)) return currentValue.map((v) => toDisplayCalendarDate(v));
2838
2998
  return currentValue ? toDisplayCalendarDate(currentValue) : null;
2839
2999
  });
2840
- const isDisabled = createMemo(() => access(props.isDisabled) ?? false);
2841
- const isReadOnly = createMemo(() => access(props.isReadOnly) ?? false);
2842
- const validationState = createMemo(() => access(props.validationState));
2843
- const isValueInvalid = createMemo(() => validationState() === "invalid");
3000
+ const isDisabled = createMemo(() => access(getProps().isDisabled) ?? false);
3001
+ const isReadOnly = createMemo(() => access(getProps().isReadOnly) ?? false);
3002
+ const validationState = createMemo(() => access(getProps().validationState));
3003
+ const isInvalidSelection = createMemo(() => {
3004
+ const current = value();
3005
+ if (current == null) return false;
3006
+ const dates = Array.isArray(current) ? current : [current];
3007
+ const min = access(getProps().minValue);
3008
+ const max = access(getProps().maxValue);
3009
+ const minCal = min != null ? toDisplayCalendarDate(min) : null;
3010
+ const maxCal = max != null ? toDisplayCalendarDate(max) : null;
3011
+ return dates.some((date) => {
3012
+ const cal = toDisplayCalendarDate(date);
3013
+ return Boolean(getProps().isDateUnavailable?.(cal)) || minCal != null && cal.compare(minCal) < 0 || maxCal != null && cal.compare(maxCal) > 0;
3014
+ });
3015
+ });
3016
+ const isValueInvalid = createMemo(() => Boolean(access(getProps().isInvalid)) || validationState() === "invalid" || isInvalidSelection());
2844
3017
  const visibleRange = () => {
2845
3018
  const start = visibleRangeStart();
2846
3019
  return {
@@ -2849,11 +3022,11 @@ function createCalendarState(props = {}) {
2849
3022
  };
2850
3023
  };
2851
3024
  const syncVisibleRangeForFocusedDate = (nextFocusedDate, range, months) => {
2852
- if (nextFocusedDate.compare(range.start) < 0) setVisibleRangeStart(startOfMonth$1(nextFocusedDate.subtract({ months: months - 1 })));
2853
- else if (nextFocusedDate.compare(range.end) > 0) setVisibleRangeStart(startOfMonth$1(nextFocusedDate));
3025
+ if (nextFocusedDate.compare(range.start) < 0) setVisibleRangeStart(constrainVisibleRangeStart(nextFocusedDate, rawAlignEnd(nextFocusedDate, months), months));
3026
+ else if (nextFocusedDate.compare(range.end) > 0) setVisibleRangeStart(constrainVisibleRangeStart(nextFocusedDate, rawAlignStart(nextFocusedDate), months));
2854
3027
  };
2855
3028
  createEffect(() => {
2856
- const controlledFocused = access(props.focusedValue);
3029
+ const controlledFocused = access(getProps().focusedValue);
2857
3030
  if (!controlledFocused) return null;
2858
3031
  return {
2859
3032
  nextFocusedDate: constrainDate(toDisplayCalendarDate(controlledFocused)),
@@ -2868,7 +3041,7 @@ function createCalendarState(props = {}) {
2868
3041
  syncVisibleRangeForFocusedDate(nextFocusedDate, range, months);
2869
3042
  });
2870
3043
  createEffect(() => {
2871
- if (access(props.focusedValue)) return null;
3044
+ if (access(getProps().focusedValue)) return null;
2872
3045
  const currentFocusedDate = focusedDate();
2873
3046
  const nextFocusedDate = constrainDate(currentFocusedDate);
2874
3047
  if (nextFocusedDate.compare(currentFocusedDate) === 0 && isEqualCalendar(nextFocusedDate.calendar, currentFocusedDate.calendar)) return null;
@@ -2882,7 +3055,7 @@ function createCalendarState(props = {}) {
2882
3055
  if (!data) return;
2883
3056
  syncVisibleRangeForFocusedDate(data.nextFocusedDate, data.range, data.months);
2884
3057
  setFocusedDateInternal(data.nextFocusedDate);
2885
- props.onFocusChange?.(data.nextFocusedDate);
3058
+ getProps().onFocusChange?.(data.nextFocusedDate);
2886
3059
  });
2887
3060
  createEffect(() => {
2888
3061
  const currentFocusedDate = focusedDate();
@@ -2919,11 +3092,11 @@ function createCalendarState(props = {}) {
2919
3092
  });
2920
3093
  const normalizeValue = (newValue) => {
2921
3094
  const constrained = constrainDate(newValue);
2922
- const minBound = access(props.minValue);
2923
- const prev = previousAvailableDate$1(constrained, minBound != null ? toDisplayCalendarDate(minBound) : minDate$1(constrained, visibleRange().start) ?? constrained, props.isDateUnavailable);
3095
+ const minBound = access(getProps().minValue);
3096
+ const prev = previousAvailableDate$1(constrained, minBound != null ? toDisplayCalendarDate(minBound) : minDate$1(constrained, visibleRange().start) ?? constrained, getProps().isDateUnavailable);
2924
3097
  if (!prev) return null;
2925
3098
  const oldValue = sourceValue();
2926
- return convertValue$3(prev, Array.isArray(oldValue) ? oldValue[0] ?? null : oldValue);
3099
+ return convertValue$5(prev, Array.isArray(oldValue) ? oldValue[0] ?? null : oldValue);
2927
3100
  };
2928
3101
  const setValue = (newValue) => {
2929
3102
  if (isDisabled() || isReadOnly()) return;
@@ -2936,15 +3109,15 @@ function createCalendarState(props = {}) {
2936
3109
  if (!localValue) return;
2937
3110
  nextValue = localValue;
2938
3111
  }
2939
- if (access(props.value) === void 0) setInternalValue(() => nextValue);
2940
- if (!Object.is(oldValue, nextValue) && props.onChange) props.onChange(nextValue);
3112
+ if (access(getProps().value) === void 0) setInternalValue(() => nextValue);
3113
+ if (!Object.is(oldValue, nextValue)) getProps().onChange?.(nextValue);
2941
3114
  };
2942
3115
  const setFocusedDate = (date) => {
2943
3116
  const constrained = constrainDate(date);
2944
3117
  if (Object.is(constrained, focusedDate())) return;
2945
3118
  syncVisibleRangeForFocusedDate(constrained, visibleRange(), visibleMonths());
2946
3119
  setFocusedDateInternal(constrained);
2947
- props.onFocusChange?.(constrained);
3120
+ getProps().onFocusChange?.(constrained);
2948
3121
  };
2949
3122
  const isSelected = (date) => {
2950
3123
  const v = value();
@@ -2957,13 +3130,13 @@ function createCalendarState(props = {}) {
2957
3130
  return isFocused() && isSameDay$1(toDisplayCalendarDate(date), focusedDate());
2958
3131
  };
2959
3132
  const isCellUnavailable = (date) => {
2960
- return props.isDateUnavailable?.(date) ?? false;
3133
+ return getProps().isDateUnavailable?.(date) ?? false;
2961
3134
  };
2962
3135
  const isCellDisabled = (date) => {
2963
3136
  if (isDisabled()) return true;
2964
- if (props.isDateDisabled?.(date)) return true;
2965
- const minValue = access(props.minValue);
2966
- const maxValue = access(props.maxValue);
3137
+ if (getProps().isDateDisabled?.(date)) return true;
3138
+ const minValue = access(getProps().minValue);
3139
+ const maxValue = access(getProps().maxValue);
2967
3140
  const calDate = toDisplayCalendarDate(date);
2968
3141
  const range = visibleRange();
2969
3142
  if (calDate.compare(toDisplayCalendarDate(range.start)) < 0) return true;
@@ -3000,20 +3173,24 @@ function createCalendarState(props = {}) {
3000
3173
  };
3001
3174
  const focusPreviousPage = () => {
3002
3175
  setIsPaginating(true);
3003
- const pageMonths = props.pageBehavior === "single" ? 1 : visibleMonths();
3004
- const nextFocusedDate = constrainDate(focusedDate().subtract({ months: pageMonths }));
3176
+ const pageMonths = getProps().pageBehavior === "single" ? 1 : visibleMonths();
3177
+ const currentFocused = focusedDate();
3178
+ const nextFocusedDate = constrainDate(currentFocused.subtract({ months: pageMonths }));
3179
+ const slidStart = startOfMonth$1(visibleRangeStart().subtract({ months: pageMonths }));
3005
3180
  setFocusedDateInternal(nextFocusedDate);
3006
- setVisibleRangeStart(startOfMonth$1(visibleRangeStart().subtract({ months: pageMonths })));
3007
- props.onFocusChange?.(nextFocusedDate);
3181
+ setVisibleRangeStart(constrainVisibleRangeStart(currentFocused, slidStart, pageMonths));
3182
+ getProps().onFocusChange?.(nextFocusedDate);
3008
3183
  setIsPaginating(false);
3009
3184
  };
3010
3185
  const focusNextPage = () => {
3011
3186
  setIsPaginating(true);
3012
- const pageMonths = props.pageBehavior === "single" ? 1 : visibleMonths();
3013
- const nextFocusedDate = constrainDate(focusedDate().add({ months: pageMonths }));
3187
+ const pageMonths = getProps().pageBehavior === "single" ? 1 : visibleMonths();
3188
+ const currentFocused = focusedDate();
3189
+ const nextFocusedDate = constrainDate(currentFocused.add({ months: pageMonths }));
3190
+ const slidStart = startOfMonth$1(visibleRangeStart().add({ months: pageMonths }));
3014
3191
  setFocusedDateInternal(nextFocusedDate);
3015
- setVisibleRangeStart(startOfMonth$1(visibleRangeStart().add({ months: pageMonths })));
3016
- props.onFocusChange?.(nextFocusedDate);
3192
+ setVisibleRangeStart(constrainVisibleRangeStart(currentFocused, slidStart, pageMonths));
3193
+ getProps().onFocusChange?.(nextFocusedDate);
3017
3194
  setIsPaginating(false);
3018
3195
  };
3019
3196
  const focusPreviousSection = () => {
@@ -3048,7 +3225,7 @@ function createCalendarState(props = {}) {
3048
3225
  const selectDate = (date) => {
3049
3226
  if (isReadOnly() || isDisabled()) return;
3050
3227
  if (selectionMode() === "multiple") {
3051
- const current = access(props.value) !== void 0 ? value() : readNow(internalValue);
3228
+ const current = access(getProps().value) !== void 0 ? value() : readNow(internalValue);
3052
3229
  const base = Array.isArray(current) ? current.map((v) => toDisplayCalendarDate(v)) : current != null ? [toDisplayCalendarDate(current)] : [];
3053
3230
  const target = toDisplayCalendarDate(date);
3054
3231
  const index = base.findIndex((d) => isSameDay$1(d, target));
@@ -3128,7 +3305,7 @@ function createCalendarState(props = {}) {
3128
3305
  function toCalendarDate$3(date) {
3129
3306
  return toCalendarDate$1(date);
3130
3307
  }
3131
- function convertValue$3(newValue, oldValue) {
3308
+ function convertValue$5(newValue, oldValue) {
3132
3309
  const localValue = toCalendar$1(newValue, oldValue?.calendar ?? new GregorianCalendar$1());
3133
3310
  if (oldValue && "hour" in oldValue) return oldValue.set(localValue);
3134
3311
  return localValue;
@@ -4175,7 +4352,7 @@ function createDateFieldState(props = {}) {
4175
4352
  if (access(props.value) === void 0) setInternalValue(() => v);
4176
4353
  props.onChange?.(v);
4177
4354
  };
4178
- const calendarValue = createMemo(() => convertValue$2(rawValue(), calendar()) ?? null);
4355
+ const calendarValue = createMemo(() => convertValue$4(rawValue(), calendar()) ?? null);
4179
4356
  const initialValue = untrack(calendarValue);
4180
4357
  const [displayValue, setDisplayValue] = createInternalSignal(untrack(() => new IncompleteDate(calendar(), hourCycle(), untrack(calendarValue))));
4181
4358
  let lastValue = untrack(calendarValue);
@@ -4477,13 +4654,13 @@ function getFormatOptions(fieldOptions, options) {
4477
4654
  if (options.showEra && startIdx === 0) opts.era = "short";
4478
4655
  return opts;
4479
4656
  }
4480
- function convertValue$2(value, calendar) {
4657
+ function convertValue$4(value, calendar) {
4481
4658
  if (value === null) return null;
4482
4659
  if (!value) return void 0;
4483
4660
  return toCalendar$1(value, calendar);
4484
4661
  }
4485
4662
  function createPlaceholderDate(placeholderValue, granularity, calendar, timeZone) {
4486
- if (placeholderValue) return convertValue$2(placeholderValue, calendar);
4663
+ if (placeholderValue) return convertValue$4(placeholderValue, calendar);
4487
4664
  const date = toCalendar$1(now$1(timeZone ?? getLocalTimeZone$1()).set({
4488
4665
  hour: 0,
4489
4666
  minute: 0,
@@ -4601,8 +4778,8 @@ function createRangeCalendarState(props = {}) {
4601
4778
  return end;
4602
4779
  };
4603
4780
  const rawAlignStart = (date) => startOfMonth$1(date);
4604
- const rawAlignEnd = (date) => startOfMonth$1(date).subtract({ months: Math.max(visibleMonths() - 1, 0) });
4605
- const constrainVisibleRangeStart = (date, aligned) => {
4781
+ const rawAlignEnd = (date, months = visibleMonths()) => startOfMonth$1(date).subtract({ months: Math.max(months - 1, 0) });
4782
+ const constrainVisibleRangeStart = (date, aligned, months = visibleMonths()) => {
4606
4783
  const minValue = access(props.minValue);
4607
4784
  const maxValue = access(props.maxValue);
4608
4785
  let constrained = aligned;
@@ -4611,7 +4788,7 @@ function createRangeCalendarState(props = {}) {
4611
4788
  if (minStart.compare(constrained) > 0) constrained = minStart;
4612
4789
  }
4613
4790
  if (maxValue && date.compare(toDisplayCalendarDate(maxValue)) <= 0) {
4614
- const maxStart = rawAlignEnd(toDisplayCalendarDate(maxValue));
4791
+ const maxStart = rawAlignEnd(toDisplayCalendarDate(maxValue), months);
4615
4792
  if (maxStart.compare(constrained) < 0) constrained = maxStart;
4616
4793
  }
4617
4794
  return constrained;
@@ -4624,7 +4801,8 @@ function createRangeCalendarState(props = {}) {
4624
4801
  return constrainVisibleRangeStart(date, rawAlignStart(date).subtract({ months: offset }));
4625
4802
  };
4626
4803
  const defaultSelectionAlignment = () => {
4627
- const currentValue = access(props.value) ?? props.defaultValue;
4804
+ const controlled = access(props.value);
4805
+ const currentValue = controlled !== void 0 ? controlled : props.defaultValue;
4628
4806
  if (!currentValue?.start || !currentValue?.end) return "center";
4629
4807
  const start = alignCenter(toDisplayCalendarDate(currentValue.start));
4630
4808
  const end = visibleRangeEndFromStart(start);
@@ -4642,8 +4820,9 @@ function createRangeCalendarState(props = {}) {
4642
4820
  if (controlledFocused) return toDisplayCalendarDate(controlledFocused);
4643
4821
  if (props.defaultFocusedValue) return toDisplayCalendarDate(props.defaultFocusedValue);
4644
4822
  const controlledValue = access(props.value);
4645
- if (controlledValue?.start) return toDisplayCalendarDate(controlledValue.start);
4646
- if (props.defaultValue?.start) return toDisplayCalendarDate(props.defaultValue.start);
4823
+ if (controlledValue !== void 0) {
4824
+ if (controlledValue?.start) return toDisplayCalendarDate(controlledValue.start);
4825
+ } else if (props.defaultValue?.start) return toDisplayCalendarDate(props.defaultValue.start);
4647
4826
  return toCalendar$1(today$1(timeZone), calendar());
4648
4827
  };
4649
4828
  const [internalValue, setInternalValue] = createInternalSignal(props.defaultValue ?? null);
@@ -4677,7 +4856,7 @@ function createRangeCalendarState(props = {}) {
4677
4856
  const maxCal = max != null ? toDisplayCalendarDate(max) : null;
4678
4857
  return isDateOutsideRange(toDisplayCalendarDate(v.start), minCal, maxCal) || isDateOutsideRange(toDisplayCalendarDate(v.end), minCal, maxCal);
4679
4858
  });
4680
- const isValueInvalid = createMemo(() => validationState() === "invalid" || isInvalidSelection());
4859
+ const isValueInvalid = createMemo(() => Boolean(access(props.isInvalid)) || validationState() === "invalid" || isInvalidSelection());
4681
4860
  const highlightedRange = createMemo(() => {
4682
4861
  const anchor = anchorDate();
4683
4862
  if (!anchor) {
@@ -4778,8 +4957,8 @@ function createRangeCalendarState(props = {}) {
4778
4957
  if (isDisabled() || isReadOnly()) return;
4779
4958
  const oldValue = sourceValue();
4780
4959
  const nextValue = newValue ? {
4781
- start: convertValue$1(constrainDate(toDisplayCalendarDate(newValue.start)), oldValue?.start),
4782
- end: convertValue$1(constrainDate(toDisplayCalendarDate(newValue.end)), oldValue?.end)
4960
+ start: convertValue$3(constrainDate(toDisplayCalendarDate(newValue.start)), oldValue?.start),
4961
+ end: convertValue$3(constrainDate(toDisplayCalendarDate(newValue.end)), oldValue?.end)
4783
4962
  } : null;
4784
4963
  if (access(props.value) === void 0) setInternalValue(() => nextValue);
4785
4964
  if (!Object.is(oldValue, nextValue) && props.onChange) props.onChange(nextValue);
@@ -4844,16 +5023,20 @@ function createRangeCalendarState(props = {}) {
4844
5023
  };
4845
5024
  const focusPreviousPage = () => {
4846
5025
  const pageMonths = access(props.pageBehavior) === "single" ? 1 : visibleMonths();
4847
- const nextFocusedDate = constrainDate(focusedDate().subtract({ months: pageMonths }));
5026
+ const currentFocused = focusedDate();
5027
+ const nextFocusedDate = constrainDate(currentFocused.subtract({ months: pageMonths }));
5028
+ const slidStart = startOfMonth$1(visibleRangeStart().subtract({ months: pageMonths }));
4848
5029
  setFocusedDateInternal(nextFocusedDate);
4849
- setVisibleRangeStart(startOfMonth$1(visibleRangeStart().subtract({ months: pageMonths })));
5030
+ setVisibleRangeStart(constrainVisibleRangeStart(currentFocused, slidStart, pageMonths));
4850
5031
  props.onFocusChange?.(nextFocusedDate);
4851
5032
  };
4852
5033
  const focusNextPage = () => {
4853
5034
  const pageMonths = access(props.pageBehavior) === "single" ? 1 : visibleMonths();
4854
- const nextFocusedDate = constrainDate(focusedDate().add({ months: pageMonths }));
5035
+ const currentFocused = focusedDate();
5036
+ const nextFocusedDate = constrainDate(currentFocused.add({ months: pageMonths }));
5037
+ const slidStart = startOfMonth$1(visibleRangeStart().add({ months: pageMonths }));
4855
5038
  setFocusedDateInternal(nextFocusedDate);
4856
- setVisibleRangeStart(startOfMonth$1(visibleRangeStart().add({ months: pageMonths })));
5039
+ setVisibleRangeStart(constrainVisibleRangeStart(currentFocused, slidStart, pageMonths));
4857
5040
  props.onFocusChange?.(nextFocusedDate);
4858
5041
  };
4859
5042
  const focusPreviousSection = () => {
@@ -4936,7 +5119,14 @@ function createRangeCalendarState(props = {}) {
4936
5119
  return week;
4937
5120
  };
4938
5121
  const focusNearestAvailableDate = (anchor) => {
4939
- const isDateInvalid = (candidate) => isInvalid(candidate) || isDateOutsideAllowedRange(candidate);
5122
+ const passedRange = getAvailableRange(anchor);
5123
+ const isDateInvalid = (candidate) => {
5124
+ if (isDateOutsideAllowedRange(candidate)) return true;
5125
+ const calDate = toDisplayCalendarDate(candidate);
5126
+ if (passedRange?.start && calDate.compare(passedRange.start) < 0) return true;
5127
+ if (passedRange?.end && calDate.compare(passedRange.end) > 0) return true;
5128
+ return false;
5129
+ };
4940
5130
  let nextDay = anchor.add({ days: 1 });
4941
5131
  if (isDateInvalid(nextDay)) nextDay = anchor.subtract({ days: 1 });
4942
5132
  if (!isDateInvalid(nextDay)) {
@@ -5058,7 +5248,7 @@ function createRangeCalendarState(props = {}) {
5058
5248
  function toCalendarDate$2(date) {
5059
5249
  return toCalendarDate$1(date);
5060
5250
  }
5061
- function convertValue$1(newValue, oldValue) {
5251
+ function convertValue$3(newValue, oldValue) {
5062
5252
  const localValue = toCalendar$1(newValue, oldValue?.calendar ?? new GregorianCalendar$1());
5063
5253
  if (oldValue && "hour" in oldValue) return oldValue.set(localValue);
5064
5254
  return localValue;
@@ -5122,7 +5312,7 @@ function nextUnavailableDate(anchorDate, isDateUnavailable, visibleMonths, dir)
5122
5312
  * that already carry a date (`CalendarDateTime`/`ZonedDateTime`) pass through.
5123
5313
  * Mirrors `@react-stately/datepicker`'s `convertValue`.
5124
5314
  */
5125
- function convertValue(value, date = today$1(getLocalTimeZone$1())) {
5315
+ function convertValue$2(value, date = today$1(getLocalTimeZone$1())) {
5126
5316
  if (!value) return null;
5127
5317
  if ("day" in value) return value;
5128
5318
  return toCalendarDateTime$1(date, value);
@@ -5146,20 +5336,20 @@ function createTimeFieldState(props = {}) {
5146
5336
  const placeholderDate = createMemo(() => {
5147
5337
  const current = v();
5148
5338
  const zone = (current && "timeZone" in current ? current.timeZone : void 0) || defaultValueTimeZone;
5149
- if (zone && placeholderValue) return toZoned$1(convertValue(placeholderValue), zone);
5150
- return convertValue(placeholderValue);
5339
+ if (zone && placeholderValue) return toZoned$1(convertValue$2(placeholderValue), zone);
5340
+ return convertValue$2(placeholderValue);
5151
5341
  });
5152
- const minDate = createMemo(() => convertValue(access(props.minValue), day()) ?? void 0);
5153
- const maxDate = createMemo(() => convertValue(access(props.maxValue), day()) ?? void 0);
5342
+ const minDate = createMemo(() => convertValue$2(access(props.minValue), day()) ?? void 0);
5343
+ const maxDate = createMemo(() => convertValue$2(access(props.maxValue), day()) ?? void 0);
5154
5344
  const timeValue = createMemo(() => {
5155
5345
  const current = value();
5156
5346
  return current && "day" in current ? toTime$1(current) : current;
5157
5347
  });
5158
5348
  const dateTime = createMemo(() => {
5159
5349
  const current = value();
5160
- return current == null ? null : convertValue(current);
5350
+ return current == null ? null : convertValue$2(current);
5161
5351
  });
5162
- const defaultDateTime = props.defaultValue == null ? null : convertValue(props.defaultValue);
5352
+ const defaultDateTime = props.defaultValue == null ? null : convertValue$2(props.defaultValue);
5163
5353
  const onChange = (newValue) => {
5164
5354
  const converted = day() || defaultValueTimeZone ? newValue : newValue && toTime$1(newValue);
5165
5355
  if (access(props.value) === void 0) setInternalValue(() => converted);
@@ -5274,16 +5464,16 @@ function createDatePickerState(props = {}) {
5274
5464
  const isReadOnly = createMemo(() => access(props.isReadOnly) ?? false);
5275
5465
  const validationState = createMemo(() => access(props.validationState));
5276
5466
  const isOpen = () => access(props.isOpen) ?? readNow(internalOpen);
5277
- const dateValue = createMemo(() => {
5278
- const v = value();
5279
- if (!v) return null;
5280
- return toCalendarDate$1(v);
5281
- });
5282
- const timeValue = createMemo(() => {
5283
- const v = value();
5284
- if (!v || !("hour" in v)) return null;
5285
- return v;
5286
- });
5467
+ const dateValue = () => {
5468
+ const committed = value();
5469
+ if (committed) return committed;
5470
+ return selectedDate();
5471
+ };
5472
+ const timeValue = () => {
5473
+ const committed = value();
5474
+ if (committed && "hour" in committed) return committed;
5475
+ return selectedTime();
5476
+ };
5287
5477
  const getPlaceholderTime = () => {
5288
5478
  const placeholder = props.placeholderValue;
5289
5479
  if (placeholder && "hour" in placeholder) return placeholder;
@@ -5409,12 +5599,17 @@ function createDatePickerState(props = {}) {
5409
5599
  * State management for select components.
5410
5600
  * Based on @react-stately/select useSelectState.
5411
5601
  */
5602
+ function convertValue$1(value) {
5603
+ if (value === void 0) return void 0;
5604
+ if (value === null || typeof value === "string" || typeof value === "number") return value == null ? [] : [value];
5605
+ return value;
5606
+ }
5412
5607
  /**
5413
5608
  * Creates state for a select component.
5414
5609
  * Combines list state with overlay trigger state for dropdown behavior.
5415
5610
  */
5416
5611
  function createSelectState(props) {
5417
- const getProps = () => access(props);
5612
+ const getProps = propsAccessor(props);
5418
5613
  const selectionMode = () => getProps().selectionMode ?? "single";
5419
5614
  const overlayState = createOverlayTriggerState({
5420
5615
  get isOpen() {
@@ -5427,61 +5622,85 @@ function createSelectState(props) {
5427
5622
  return getProps().onOpenChange;
5428
5623
  }
5429
5624
  });
5430
- const isControlledSingle = () => getProps().selectedKey !== void 0;
5431
- const isControlledMultiple = () => getProps().selectedKeys !== void 0;
5432
- const [internalSelectedKey, setInternalSelectedKey] = createInternalSignal(getProps().defaultSelectedKey ?? null);
5433
- const [internalSelectedKeys, setInternalSelectedKeys] = createInternalSignal(getProps().defaultSelectedKeys === "all" ? "all" : new Set(getProps().defaultSelectedKeys ?? []));
5434
- const selectedKey = () => {
5435
- if (selectionMode() === "multiple") {
5436
- const keys = selectedKeys();
5437
- if (keys === "all") return null;
5438
- return keys.size > 0 ? Array.from(keys)[0] : null;
5439
- }
5440
- return isControlledSingle() ? getProps().selectedKey ?? null : internalSelectedKey();
5625
+ const resolvedDefault = untrack(() => {
5626
+ const current = getProps();
5627
+ if (current.defaultValue !== void 0) return current.defaultValue;
5628
+ return (current.selectionMode ?? "single") === "single" ? current.defaultSelectedKey ?? null : [];
5629
+ });
5630
+ const controlledProp = () => {
5631
+ const current = getProps();
5632
+ if (current.value !== void 0) return current.value;
5633
+ if (selectionMode() === "single" && current.selectedKey !== void 0) return current.selectedKey ?? null;
5441
5634
  };
5442
- const setSelectedKey = (key) => {
5443
- if (selectionMode() === "multiple") {
5444
- if (key == null) {
5445
- if (!isControlledMultiple()) setInternalSelectedKeys(/* @__PURE__ */ new Set());
5446
- getProps().onSelectionChangeKeys?.(/* @__PURE__ */ new Set());
5447
- getProps().onSelectionChange?.(null);
5448
- } else {
5449
- const next = /* @__PURE__ */ new Set([key]);
5450
- if (!isControlledMultiple()) setInternalSelectedKeys(next);
5451
- getProps().onSelectionChangeKeys?.(next);
5635
+ const [internalValue, setInternalValue] = createInternalSignal(resolvedDefault);
5636
+ const readControlled = () => {
5637
+ const controlled = controlledProp();
5638
+ return controlled === void 0 ? void 0 : controlled;
5639
+ };
5640
+ let valueRef = untrack(() => {
5641
+ const controlled = readControlled();
5642
+ return controlled !== void 0 ? controlled : internalValue();
5643
+ });
5644
+ let wroteThisTurn = false;
5645
+ const syncValueRef = () => {
5646
+ if (wroteThisTurn) return;
5647
+ const controlled = readControlled();
5648
+ valueRef = controlled !== void 0 ? controlled : untrack(internalValue);
5649
+ };
5650
+ const displayValue = () => {
5651
+ const controlled = readControlled();
5652
+ const raw = controlled !== void 0 ? controlled : internalValue();
5653
+ if (selectionMode() === "single" && raw !== null && typeof raw !== "string" && typeof raw !== "number") return raw[0] ?? null;
5654
+ return raw;
5655
+ };
5656
+ const setValue = (next) => {
5657
+ syncValueRef();
5658
+ if (selectionMode() === "single") {
5659
+ const key = typeof next === "string" || typeof next === "number" ? next : next === null ? null : next[0] ?? null;
5660
+ if (!Object.is(valueRef, key)) {
5661
+ valueRef = key;
5662
+ wroteThisTurn = true;
5663
+ queueMicrotask(() => {
5664
+ wroteThisTurn = false;
5665
+ });
5666
+ if (controlledProp() === void 0) setInternalValue(key);
5667
+ getProps().onChange?.(key);
5452
5668
  getProps().onSelectionChange?.(key);
5453
5669
  }
5454
5670
  return;
5455
5671
  }
5456
- if (!isControlledSingle()) setInternalSelectedKey(key);
5457
- getProps().onSelectionChange?.(key);
5458
- getProps().onSelectionChangeKeys?.(key != null ? /* @__PURE__ */ new Set([key]) : /* @__PURE__ */ new Set());
5459
- };
5460
- const selectedKeys = createMemo(() => {
5461
- if (selectionMode() === "multiple") {
5462
- if (isControlledMultiple()) {
5463
- const keys = getProps().selectedKeys;
5464
- return keys === "all" ? "all" : new Set(keys ?? []);
5465
- }
5466
- return internalSelectedKeys();
5672
+ const keys = typeof next === "string" || typeof next === "number" ? [next] : next === null ? [] : next;
5673
+ if (!Object.is(valueRef, keys)) {
5674
+ valueRef = keys;
5675
+ wroteThisTurn = true;
5676
+ queueMicrotask(() => {
5677
+ wroteThisTurn = false;
5678
+ });
5679
+ if (controlledProp() === void 0) setInternalValue(keys);
5680
+ getProps().onChange?.([...keys]);
5467
5681
  }
5468
- const key = selectedKey();
5469
- return key != null ? /* @__PURE__ */ new Set([key]) : /* @__PURE__ */ new Set();
5470
- });
5682
+ };
5471
5683
  const setSelectedKeys = (keys) => {
5472
- const next = new Set(keys);
5473
5684
  if (selectionMode() === "multiple") {
5474
- if (!isControlledMultiple()) setInternalSelectedKeys(next);
5475
- getProps().onSelectionChangeKeys?.(next);
5476
- getProps().onSelectionChange?.(next.size > 0 ? Array.from(next)[0] : null);
5685
+ setValue([...keys]);
5477
5686
  return;
5478
5687
  }
5479
- const key = next.size > 0 ? Array.from(next)[0] : null;
5480
- setSelectedKey(key);
5688
+ let key = null;
5689
+ for (const item of keys) {
5690
+ key = item;
5691
+ break;
5692
+ }
5693
+ setValue(key);
5481
5694
  };
5695
+ const initialDisplay = untrack(displayValue);
5696
+ const stateDefaultValue = resolvedDefault ?? initialDisplay;
5697
+ const defaultSelectedKey = untrack(() => getProps().defaultSelectedKey) ?? (untrack(() => getProps().selectionMode) === "single" ? initialDisplay : null);
5698
+ const shouldCloseOnSelect = () => getProps().shouldCloseOnSelect ?? selectionMode() === "single";
5482
5699
  const validation = createFormValidationState({
5483
5700
  get value() {
5484
- return selectionMode() === "multiple" ? selectedKeys() : selectedKey();
5701
+ const display = displayValue();
5702
+ if (display !== null && typeof display !== "string" && typeof display !== "number" && display.length === 0) return null;
5703
+ return display;
5485
5704
  },
5486
5705
  get isInvalid() {
5487
5706
  return getProps().isInvalid;
@@ -5523,41 +5742,34 @@ function createSelectState(props) {
5523
5742
  },
5524
5743
  allowDuplicateSelectionEvents: true,
5525
5744
  get selectedKeys() {
5526
- const keys = selectedKeys();
5527
- if (keys === "all") return "all";
5528
- return keys;
5745
+ return convertValue$1(displayValue()) ?? [];
5529
5746
  },
5530
5747
  onSelectionChange(keys) {
5531
- if (selectionMode() === "multiple") {
5532
- if (!isControlledMultiple()) setInternalSelectedKeys(keys);
5533
- getProps().onSelectionChangeKeys?.(keys);
5534
- if (keys !== "all") getProps().onSelectionChange?.(keys.size > 0 ? Array.from(keys)[0] : null);
5535
- validation.commitValidation();
5536
- return;
5537
- }
5538
5748
  if (keys === "all") return;
5539
- const key = keys.size > 0 ? Array.from(keys)[0] : null;
5540
- setSelectedKey(key);
5541
- overlayState.close();
5749
+ if (selectionMode() === "single") setValue(keys.values().next().value ?? null);
5750
+ else setValue([...keys]);
5751
+ if (shouldCloseOnSelect()) overlayState.close();
5542
5752
  validation.commitValidation();
5543
5753
  }
5544
5754
  });
5755
+ const [focusStrategy, setFocusStrategy] = createInternalSignal(null);
5545
5756
  const [isFocused, setFocused] = createInternalSignal(false);
5757
+ const selectedKey = () => listState.selectionManager.firstSelectedKey;
5758
+ const selectedKeys = () => new Set(convertValue$1(displayValue()) ?? []);
5546
5759
  const selectedItem = createMemo(() => {
5547
5760
  const key = selectedKey();
5548
5761
  if (key == null) return null;
5549
5762
  return listState.collection().getItem(key);
5550
5763
  });
5551
5764
  const selectedItems = createMemo(() => {
5552
- const keys = selectedKeys();
5553
- if (keys === "all") return Array.from(listState.collection());
5554
5765
  const items = [];
5555
- for (const key of keys) {
5766
+ for (const key of listState.selectionManager.selectedKeys) {
5556
5767
  const item = listState.collection().getItem(key);
5557
5768
  if (item) items.push(item);
5558
5769
  }
5559
5770
  return items;
5560
5771
  });
5772
+ const canOpenMenu = () => listState.collection().size !== 0 || getProps().allowsEmptyCollection === true;
5561
5773
  return {
5562
5774
  realtimeValidation: validation.realtimeValidation,
5563
5775
  displayValidation: validation.displayValidation,
@@ -5571,15 +5783,29 @@ function createSelectState(props) {
5571
5783
  isFocused,
5572
5784
  setFocused,
5573
5785
  isOpen: overlayState.isOpen,
5574
- open: overlayState.open,
5786
+ open(strategy = null) {
5787
+ if (!canOpenMenu()) return;
5788
+ setFocusStrategy(strategy);
5789
+ overlayState.open();
5790
+ },
5575
5791
  close: overlayState.close,
5576
- toggle: overlayState.toggle,
5792
+ toggle(strategy = null) {
5793
+ if (!canOpenMenu()) return;
5794
+ setFocusStrategy(strategy);
5795
+ overlayState.toggle();
5796
+ },
5797
+ focusStrategy,
5577
5798
  selectionMode,
5799
+ shouldCloseOnSelect,
5800
+ value: () => displayValue(),
5801
+ defaultValue: stateDefaultValue,
5802
+ setValue,
5578
5803
  selectedKey,
5804
+ defaultSelectedKey,
5805
+ setSelectedKey: setValue,
5579
5806
  selectedKeys,
5580
5807
  selectedItem,
5581
5808
  selectedItems,
5582
- setSelectedKey,
5583
5809
  setSelectedKeys,
5584
5810
  isKeyDisabled: (key) => listState.isDisabled(key),
5585
5811
  get isDisabled() {
@@ -5599,6 +5825,7 @@ function createSelectState(props) {
5599
5825
  * ComboBox combines a text input with a dropdown list, allowing users to
5600
5826
  * either type to filter options or select from a list.
5601
5827
  */
5828
+ const EMPTY_COMBOBOX_KEYS = [];
5602
5829
  /**
5603
5830
  * Default filter function that does case-insensitive "contains" matching.
5604
5831
  */
@@ -5610,26 +5837,28 @@ const defaultContainsFilter = (textValue, inputValue) => {
5610
5837
  * Combines list state with input value management and filtering.
5611
5838
  */
5612
5839
  function createComboBoxState(props) {
5613
- const getProps = () => access(props);
5840
+ const getProps = propsAccessor(props);
5614
5841
  const menuTrigger = () => getProps().menuTrigger ?? "input";
5615
5842
  const allowsEmptyCollection = () => getProps().allowsEmptyCollection ?? false;
5616
5843
  const allowsCustomValue = () => getProps().allowsCustomValue ?? false;
5617
5844
  const shouldCloseOnBlur = () => getProps().shouldCloseOnBlur ?? true;
5618
- const isMultiple = () => getProps().selectionMode === "multiple";
5845
+ const isMultiple = () => (getProps().selectionMode ?? "single") === "multiple";
5619
5846
  const [focusStrategy, setFocusStrategy] = createInternalSignal(null);
5620
5847
  const [showAllItems, setShowAllItems] = createInternalSignal(false);
5621
5848
  let menuOpenTrigger = "focus";
5622
- const isMultiSelectionControlled = () => getProps().selectedKeys !== void 0;
5623
- const [internalSelectedKeys, setInternalSelectedKeys] = createInternalSignal(new Set(getProps().defaultSelectedKeys ?? []));
5624
- const selectedKeys = () => {
5625
- return isMultiSelectionControlled() ? new Set(getProps().selectedKeys ?? []) : internalSelectedKeys();
5626
- };
5627
- const setSelectedKeys = (keys) => {
5628
- if (!isMultiSelectionControlled()) setInternalSelectedKeys(new Set(keys));
5629
- getProps().onSelectionChangeMultiple?.(keys);
5630
- };
5631
- const isSelectionControlled = () => getProps().selectedKey !== void 0;
5632
- const [internalSelectedKey, setInternalSelectedKey] = createInternalSignal(getProps().defaultSelectedKey ?? null);
5849
+ const controlledPropValue = () => {
5850
+ const propsNow = getProps();
5851
+ if (propsNow.value !== void 0) return propsNow.value;
5852
+ if (!isMultiple() && propsNow.selectedKey !== void 0) return propsNow.selectedKey;
5853
+ };
5854
+ const isValueControlled = () => controlledPropValue() !== void 0;
5855
+ const initialStoredValue = () => {
5856
+ const propsNow = getProps();
5857
+ if (propsNow.defaultValue !== void 0) return propsNow.defaultValue;
5858
+ if ((propsNow.selectionMode ?? "single") === "multiple") return [];
5859
+ return propsNow.defaultSelectedKey ?? null;
5860
+ };
5861
+ const [internalValue, setInternalValue] = createInternalSignal(initialStoredValue());
5633
5862
  const isInputControlled = () => getProps().inputValue !== void 0;
5634
5863
  const [internalInputValue, setInternalInputValue] = createInternalSignal(getProps().defaultInputValue ?? "");
5635
5864
  let inputInitialized = false;
@@ -5641,13 +5870,77 @@ function createComboBoxState(props) {
5641
5870
  getProps().onInputChange?.(value);
5642
5871
  };
5643
5872
  const [lastValue, setLastValue] = createInternalSignal(inputValue());
5873
+ const storedValue = () => {
5874
+ const controlled = controlledPropValue();
5875
+ return controlled !== void 0 ? controlled : internalValue();
5876
+ };
5877
+ const displayValue = () => {
5878
+ const current = storedValue();
5879
+ if (!isMultiple()) return isKeyList(current) ? current[0] ?? null : current;
5880
+ if (isKeyList(current)) return current;
5881
+ if (current == null) return EMPTY_COMBOBOX_KEYS;
5882
+ return [current];
5883
+ };
5644
5884
  const selectedKey = () => {
5645
- return isSelectionControlled() ? getProps().selectedKey ?? null : internalSelectedKey();
5885
+ if (isMultiple()) return null;
5886
+ const current = displayValue();
5887
+ return isKeyList(current) ? current[0] ?? null : current;
5646
5888
  };
5647
- const setSelectedKey = (key) => {
5648
- if (!isSelectionControlled()) setInternalSelectedKey(key);
5649
- getProps().onSelectionChange?.(key);
5889
+ const selectedKeys = () => {
5890
+ const current = displayValue();
5891
+ if (isKeyList(current)) return new Set(current);
5892
+ return current == null ? /* @__PURE__ */ new Set() : /* @__PURE__ */ new Set([current]);
5893
+ };
5894
+ const setValue = (next) => {
5895
+ if (!isMultiple()) {
5896
+ const key = typeof next === "string" || typeof next === "number" ? next : next === null ? null : next[0] ?? null;
5897
+ const previous = untrack(selectedKey);
5898
+ if (!isValueControlled()) setInternalValue(key);
5899
+ getProps().onChange?.(key);
5900
+ if (key !== previous) getProps().onSelectionChange?.(key);
5901
+ return;
5902
+ }
5903
+ const keys = typeof next === "string" || typeof next === "number" ? [next] : next === null ? [] : [...next];
5904
+ if (!isValueControlled()) setInternalValue(keys);
5905
+ getProps().onChange?.([...keys]);
5650
5906
  };
5907
+ const setSelectedKey = (key) => {
5908
+ setValue(key);
5909
+ };
5910
+ const defaultValue = (() => {
5911
+ const propsNow = getProps();
5912
+ return (propsNow.defaultValue !== void 0 ? propsNow.defaultValue : isMultiple() ? EMPTY_COMBOBOX_KEYS : propsNow.defaultSelectedKey ?? null) ?? untrack(displayValue);
5913
+ })();
5914
+ const defaultSelectedKey = getProps().defaultSelectedKey ?? (isMultiple() ? null : untrack(displayValue));
5915
+ const validationValue = createMemo(() => {
5916
+ const dVal = displayValue();
5917
+ if (isKeyList(dVal) && dVal.length === 0) return null;
5918
+ return {
5919
+ inputValue: inputValue(),
5920
+ value: isKeyList(dVal) ? [...dVal] : dVal,
5921
+ selectedKey: selectedKey()
5922
+ };
5923
+ });
5924
+ const validation = createFormValidationState({
5925
+ get value() {
5926
+ return validationValue();
5927
+ },
5928
+ get isInvalid() {
5929
+ return getProps().isInvalid;
5930
+ },
5931
+ get validationState() {
5932
+ return getProps().validationState;
5933
+ },
5934
+ get validate() {
5935
+ return getProps().validate;
5936
+ },
5937
+ get name() {
5938
+ return getProps().name;
5939
+ },
5940
+ get validationBehavior() {
5941
+ return getProps().validationBehavior ?? "native";
5942
+ }
5943
+ });
5651
5944
  let listState;
5652
5945
  const overlayState = createOverlayTriggerState({
5653
5946
  get isOpen() {
@@ -5681,26 +5974,27 @@ function createComboBoxState(props) {
5681
5974
  get selectionMode() {
5682
5975
  return isMultiple() ? "multiple" : "single";
5683
5976
  },
5684
- disallowEmptySelection: false,
5977
+ get disallowEmptySelection() {
5978
+ return !isMultiple();
5979
+ },
5980
+ allowDuplicateSelectionEvents: true,
5685
5981
  get selectedKeys() {
5686
- if (isMultiple()) return Array.from(selectedKeys());
5687
- const key = selectedKey();
5688
- return key != null ? [key] : [];
5982
+ return convertValue(displayValue());
5689
5983
  },
5690
5984
  onSelectionChange(keys) {
5691
5985
  if (keys === "all") return;
5692
5986
  if (isMultiple()) {
5693
- const newKeys = new Set(keys);
5694
- setSelectedKeys(newKeys);
5987
+ setValue([...keys]);
5695
5988
  return;
5696
5989
  }
5697
5990
  const key = keys.size > 0 ? Array.from(keys)[0] : null;
5698
5991
  if (key === selectedKey()) {
5992
+ getProps().onSelectionChange?.(key);
5699
5993
  resetInputValue();
5700
5994
  closeMenu();
5701
5995
  return;
5702
5996
  }
5703
- setSelectedKey(key);
5997
+ setValue(key);
5704
5998
  }
5705
5999
  });
5706
6000
  const originalCollection = listState.collection;
@@ -5729,18 +6023,21 @@ function createComboBoxState(props) {
5729
6023
  return items;
5730
6024
  });
5731
6025
  if (!inputInitialized && !isInputControlled()) {
5732
- const defaultKey = getProps().defaultSelectedKey;
5733
- if (defaultKey != null && !getProps().defaultInputValue) {
5734
- const item = originalCollection().getItem(defaultKey);
5735
- if (item) {
5736
- setInternalInputValue(item.textValue);
5737
- setLastValue(item.textValue);
6026
+ if (getProps().defaultInputValue == null && !isMultiple()) {
6027
+ const key = selectedKey();
6028
+ if (key != null) {
6029
+ const item = originalCollection().getItem(key);
6030
+ if (item) {
6031
+ setInternalInputValue(item.textValue);
6032
+ setLastValue(item.textValue);
6033
+ }
5738
6034
  }
5739
6035
  }
5740
6036
  inputInitialized = true;
5741
6037
  }
5742
6038
  const resetInputValue = () => {
5743
- const textValue = selectedItem()?.textValue ?? "";
6039
+ const key = selectedKey();
6040
+ const textValue = key != null ? originalCollection().getItem(key)?.textValue ?? "" : "";
5744
6041
  setLastValue(textValue);
5745
6042
  setInputValue(textValue);
5746
6043
  };
@@ -5779,14 +6076,25 @@ function createComboBoxState(props) {
5779
6076
  overlayState.toggle();
5780
6077
  };
5781
6078
  const commitCustomValue = () => {
5782
- setSelectedKey(null);
6079
+ if (isMultiple()) {
6080
+ setLastValue(inputValue());
6081
+ closeMenu();
6082
+ return;
6083
+ }
6084
+ setValue(null);
5783
6085
  closeMenu();
5784
6086
  };
5785
- const commitSelection = () => {
5786
- if (isSelectionControlled() && isInputControlled()) {
5787
- getProps().onSelectionChange?.(selectedKey());
5788
- const item = selectedItem();
5789
- setLastValue(item?.textValue ?? "");
6087
+ const commitSelection = (shouldForceSelectionChange = false) => {
6088
+ if (isValueControlled() && isInputControlled()) {
6089
+ const key = selectedKey();
6090
+ const itemText = key != null ? originalCollection().getItem(key)?.textValue ?? "" : "";
6091
+ if (shouldForceSelectionChange || isMultiple() || inputValue() !== itemText) {
6092
+ getProps().onSelectionChange?.(key);
6093
+ const current = displayValue();
6094
+ const changeValue = isKeyList(current) ? [...current] : current;
6095
+ getProps().onChange?.(changeValue);
6096
+ }
6097
+ setLastValue(itemText);
5790
6098
  closeMenu();
5791
6099
  } else {
5792
6100
  resetInputValue();
@@ -5802,12 +6110,9 @@ function createComboBoxState(props) {
5802
6110
  };
5803
6111
  const commit = () => {
5804
6112
  const focusedKey = listState.focusedKey();
5805
- if (isMultiple()) {
5806
- if (overlayState.isOpen() && focusedKey != null) select(focusedKey);
5807
- return;
5808
- }
5809
6113
  if (overlayState.isOpen() && focusedKey != null) {
5810
- if (selectedKey() === focusedKey) commitSelection();
6114
+ if (!isMultiple() && selectedKey() === focusedKey) commitSelection(true);
6115
+ else if (isMultiple()) select(focusedKey);
5811
6116
  else listState.selectionManager.select(focusedKey);
5812
6117
  } else commitValue();
5813
6118
  };
@@ -5816,10 +6121,24 @@ function createComboBoxState(props) {
5816
6121
  else commitSelection();
5817
6122
  };
5818
6123
  const [isFocused, setIsFocused] = createInternalSignal(false);
6124
+ let valueOnFocus = [untrack(inputValue), untrack(displayValue)];
6125
+ const hasMoved = (a, b) => {
6126
+ if (a === b) return false;
6127
+ if (Array.isArray(a) && Array.isArray(b)) {
6128
+ if (a.length !== b.length) return true;
6129
+ for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return true;
6130
+ return false;
6131
+ }
6132
+ return true;
6133
+ };
5819
6134
  const setFocused = (focused) => {
5820
6135
  if (focused) {
6136
+ valueOnFocus = [inputValue(), displayValue()];
5821
6137
  if (menuTrigger() === "focus" && !getProps().isReadOnly) open(null, "focus");
5822
- } else if (shouldCloseOnBlur()) commitValue();
6138
+ } else {
6139
+ if (shouldCloseOnBlur()) commitValue();
6140
+ if (inputValue() !== valueOnFocus[0] || hasMoved(displayValue(), valueOnFocus[1])) validation.commitValidation();
6141
+ }
5823
6142
  setIsFocused(focused);
5824
6143
  };
5825
6144
  let lastDisplayValue = untrack(selectedKey);
@@ -5834,15 +6153,16 @@ function createComboBoxState(props) {
5834
6153
  trigger: menuTrigger(),
5835
6154
  showingAll: showAllItems(),
5836
6155
  inputControlled: isInputControlled(),
5837
- selectionControlled: isSelectionControlled()
5838
- }), ({ input, filtered, isOpen, last, focused, key, allowsEmpty, trigger, showingAll, inputControlled, selectionControlled }) => {
6156
+ selectionControlled: isValueControlled(),
6157
+ multiple: isMultiple()
6158
+ }), ({ input, filtered, isOpen, last, focused, key, allowsEmpty, trigger, showingAll, inputControlled, selectionControlled, multiple }) => {
5839
6159
  if (focused && (filtered.size > 0 || allowsEmpty) && !isOpen && input !== last && trigger !== "manual") open(null, "input");
5840
6160
  if (!showingAll && !allowsEmpty && isOpen && filtered.size === 0) closeMenu();
5841
6161
  if (key != null && key !== lastDisplayValue) closeMenu();
5842
6162
  if (input !== last) {
5843
6163
  listState.setFocusedKey(null);
5844
6164
  setShowAllItems(false);
5845
- if (input === "" && (!inputControlled || !selectionControlled)) setSelectedKey(null);
6165
+ if (!multiple && input === "" && (!inputControlled || !selectionControlled)) setValue(null);
5846
6166
  setLastValue(input);
5847
6167
  }
5848
6168
  lastDisplayValue = key;
@@ -5854,7 +6174,7 @@ function createComboBoxState(props) {
5854
6174
  key,
5855
6175
  textValue: (key != null ? originalCollection().getItem(key) : null)?.textValue ?? "",
5856
6176
  inputControlled: isInputControlled(),
5857
- selectionControlled: isSelectionControlled()
6177
+ selectionControlled: isValueControlled()
5858
6178
  };
5859
6179
  }, (data) => {
5860
6180
  if (!data) return;
@@ -5871,34 +6191,39 @@ function createComboBoxState(props) {
5871
6191
  const current = new Set(selectedKeys());
5872
6192
  if (current.has(key)) current.delete(key);
5873
6193
  else current.add(key);
5874
- setSelectedKeys(current);
6194
+ setValue([...current]);
5875
6195
  } else {
5876
- setSelectedKey(key);
6196
+ setValue(key);
5877
6197
  closeMenu();
5878
6198
  }
5879
6199
  };
6200
+ const setSelectedKeys = (keys) => {
6201
+ setValue([...keys]);
6202
+ };
5880
6203
  const removeSelectedKey = (key) => {
5881
- if (isMultiple()) {
5882
- const current = new Set(selectedKeys());
5883
- current.delete(key);
5884
- setSelectedKeys(current);
5885
- }
6204
+ if (!isMultiple()) return;
6205
+ setValue([...selectedKeys()].filter((selected) => selected !== key));
5886
6206
  };
5887
6207
  const selectionMode = () => getProps().selectionMode ?? "single";
5888
- const isSelected = (key) => {
5889
- if (isMultiple()) return selectedKeys().has(key);
5890
- return selectedKey() === key;
5891
- };
6208
+ const isSelected = (key) => selectedKeys().has(key);
5892
6209
  return {
6210
+ realtimeValidation: validation.realtimeValidation,
6211
+ displayValidation: validation.displayValidation,
6212
+ updateValidation: validation.updateValidation,
6213
+ resetValidation: validation.resetValidation,
6214
+ commitValidation: validation.commitValidation,
5893
6215
  collection: displayedCollection,
5894
6216
  isOpen: overlayState.isOpen,
5895
6217
  open,
5896
6218
  close: commitValue,
5897
6219
  toggle,
5898
6220
  selectedKey,
5899
- defaultSelectedKey: getProps().defaultSelectedKey ?? null,
6221
+ defaultSelectedKey,
5900
6222
  selectedItem,
5901
6223
  setSelectedKey,
6224
+ value: () => displayValue(),
6225
+ defaultValue,
6226
+ setValue,
5902
6227
  selectedKeys,
5903
6228
  setSelectedKeys,
5904
6229
  selectedItems,
@@ -5929,6 +6254,14 @@ function createComboBoxState(props) {
5929
6254
  }
5930
6255
  };
5931
6256
  }
6257
+ function isKeyList(value) {
6258
+ return value !== null && typeof value !== "string" && typeof value !== "number";
6259
+ }
6260
+ function convertValue(value) {
6261
+ if (value == null) return [];
6262
+ if (typeof value === "string" || typeof value === "number") return [value];
6263
+ return value;
6264
+ }
5932
6265
  /**
5933
6266
  * Filter a collection based on input value.
5934
6267
  * RAC `useComboBoxState.ts:644-672` copies nodes into a new `ListCollection`,
@@ -5969,7 +6302,7 @@ function filterCollection(collection, inputValue, filter) {
5969
6302
  * Creates state for a tab list component.
5970
6303
  */
5971
6304
  function createTabListState(props) {
5972
- const getProps = () => access(props);
6305
+ const getProps = propsAccessor(props);
5973
6306
  const collection = createMemo(() => {
5974
6307
  const p = getProps();
5975
6308
  return new ListCollection((p.items ?? []).map((item, index) => {
@@ -6041,32 +6374,13 @@ function createTabListState(props) {
6041
6374
  setChildFocusStrategy(childStrategy ?? null);
6042
6375
  };
6043
6376
  let lastSelectedKey = selectedKey();
6044
- let selectedToFocusedFrame = null;
6045
- const copySelectedToFocusedKey = () => {
6046
- selectedToFocusedFrame = null;
6047
- const sel = selectedKey();
6048
- if (sel !== null && focusedKey() === null || !isFocused() && sel !== lastSelectedKey) setFocusedKey(sel);
6049
- lastSelectedKey = sel;
6050
- };
6051
6377
  createEffect(() => ({
6052
6378
  sel: selectedKey(),
6053
6379
  foc: focusedKey(),
6054
6380
  focused: isFocused()
6055
6381
  }), ({ sel, foc, focused }) => {
6056
- if (sel !== null && foc === null) {
6057
- setFocusedKey(sel);
6058
- lastSelectedKey = sel;
6059
- return;
6060
- }
6061
- if (!focused && sel !== lastSelectedKey) {
6062
- if (selectedToFocusedFrame != null) return;
6063
- selectedToFocusedFrame = requestAnimationFrame(copySelectedToFocusedKey);
6064
- }
6065
- });
6066
- onCleanup(() => {
6067
- if (selectedToFocusedFrame == null) return;
6068
- cancelAnimationFrame(selectedToFocusedFrame);
6069
- selectedToFocusedFrame = null;
6382
+ if (sel !== null && foc === null || !focused && sel !== lastSelectedKey) setFocusedKey(sel);
6383
+ lastSelectedKey = sel;
6070
6384
  });
6071
6385
  createEffect(() => {
6072
6386
  const p = getProps();
@@ -6131,12 +6445,38 @@ function clamp$2(value, min, max) {
6131
6445
  return result;
6132
6446
  }
6133
6447
  /**
6134
- * Snaps a value to the nearest step.
6448
+ * Rounds to the decimal precision implied by a step, including exponential steps.
6449
+ */
6450
+ function roundToStepPrecision(value, step) {
6451
+ let roundedValue = value;
6452
+ let precision = 0;
6453
+ const stepString = step.toString();
6454
+ const eIndex = stepString.toLowerCase().indexOf("e-");
6455
+ if (eIndex > 0) precision = Math.abs(Math.floor(Math.log10(Math.abs(step)))) + eIndex;
6456
+ else {
6457
+ const pointIndex = stepString.indexOf(".");
6458
+ if (pointIndex >= 0) precision = stepString.length - pointIndex;
6459
+ }
6460
+ if (precision > 0) {
6461
+ const pow = Math.pow(10, precision);
6462
+ roundedValue = Math.round(roundedValue * pow) / pow;
6463
+ }
6464
+ return roundedValue;
6465
+ }
6466
+ /**
6467
+ * Snaps to the nearest step, then to the last in-range step when that exceeds max.
6468
+ * A negative halfway rounds away from the step base.
6135
6469
  */
6136
- function snapToStep$1(value, step, min) {
6137
- const base = min ?? 0;
6138
- const diff = value - base;
6139
- return handleDecimalOperation("+", base, Math.round(diff / step) * step);
6470
+ function snapValueToStep(value, min, max, step) {
6471
+ const minNumber = Number(min);
6472
+ const maxNumber = Number(max);
6473
+ const remainder = (value - (isNaN(minNumber) ? 0 : minNumber)) % step;
6474
+ let snappedValue = roundToStepPrecision(Math.abs(remainder) * 2 >= step ? value + Math.sign(remainder) * (step - Math.abs(remainder)) : value - remainder, step);
6475
+ if (!isNaN(minNumber)) {
6476
+ if (snappedValue < minNumber) snappedValue = minNumber;
6477
+ else if (!isNaN(maxNumber) && snappedValue > maxNumber) snappedValue = minNumber + Math.floor(roundToStepPrecision((maxNumber - minNumber) / step, step)) * step;
6478
+ } else if (!isNaN(maxNumber) && snappedValue > maxNumber) snappedValue = Math.floor(roundToStepPrecision(maxNumber / step, step)) * step;
6479
+ return roundToStepPrecision(snappedValue, step);
6140
6480
  }
6141
6481
  function isValidStep(step) {
6142
6482
  return step != null && !isNaN(step) && step > 0;
@@ -6180,7 +6520,7 @@ function createNumberFieldState(props) {
6180
6520
  const applyConstraints = (value) => {
6181
6521
  const p = getProps();
6182
6522
  if (isNaN(value)) return NaN;
6183
- if (hasCustomStep()) return clamp$2(snapToStep$1(value, step(), p.minValue), p.minValue, p.maxValue);
6523
+ if (hasCustomStep()) return snapValueToStep(value, p.minValue, p.maxValue, step());
6184
6524
  return clamp$2(value, p.minValue, p.maxValue);
6185
6525
  };
6186
6526
  let initialNumberValue = NaN;
@@ -6262,11 +6602,11 @@ function createNumberFieldState(props) {
6262
6602
  syncControlledValue();
6263
6603
  setInputValueInternal(value);
6264
6604
  };
6265
- const commit = () => {
6605
+ const commit = (override) => {
6266
6606
  ensureInitialized();
6267
6607
  const p = getProps();
6268
- const input = readNow(inputValue);
6269
- if (input === "" || input === "-") {
6608
+ const input = override === void 0 ? readNow(inputValue) : override;
6609
+ if (input === "") {
6270
6610
  setNumberValue(NaN);
6271
6611
  setInputValueInternal(p.value === void 0 ? "" : formatNumber(readNow(actualNumberValue)));
6272
6612
  p.onChange?.(NaN);
@@ -6282,7 +6622,7 @@ function createNumberFieldState(props) {
6282
6622
  parsed = constrainForCommit(parsed);
6283
6623
  parsed = numberParser().parse(formatNumber(parsed));
6284
6624
  setNumberValue(parsed);
6285
- setInputValueInternal(formatNumber(parsed));
6625
+ setInputValueInternal(formatNumber(p.value === void 0 ? parsed : previous));
6286
6626
  p.onChange?.(parsed);
6287
6627
  if (parsed !== previous) validation.commitValidation();
6288
6628
  };
@@ -6293,7 +6633,7 @@ function createNumberFieldState(props) {
6293
6633
  const current = parsedInputValue();
6294
6634
  if (isNaN(current)) return true;
6295
6635
  if (p.maxValue == null) return true;
6296
- return snapToStep$1(current, step(), p.minValue) > current || handleDecimalOperation("+", current, step()) <= p.maxValue;
6636
+ return snapValueToStep(current, p.minValue, p.maxValue, step()) > current || handleDecimalOperation("+", current, step()) <= p.maxValue;
6297
6637
  });
6298
6638
  const canDecrement = createMemo(() => {
6299
6639
  ensureInitialized();
@@ -6302,15 +6642,20 @@ function createNumberFieldState(props) {
6302
6642
  const current = parsedInputValue();
6303
6643
  if (isNaN(current)) return true;
6304
6644
  if (p.minValue == null) return true;
6305
- return snapToStep$1(current, step(), p.minValue) < current || handleDecimalOperation("-", current, step()) >= p.minValue;
6645
+ return snapValueToStep(current, p.minValue, p.maxValue, step()) < current || handleDecimalOperation("-", current, step()) >= p.minValue;
6306
6646
  });
6307
6647
  const safeNextStep = (operation, minOrMaxValue = 0) => {
6308
6648
  const p = getProps();
6309
6649
  const parsed = parsedInputValue();
6310
- if (isNaN(parsed)) return clamp$2(snapToStep$1(isNaN(minOrMaxValue) ? 0 : minOrMaxValue, step(), p.minValue), p.minValue, p.maxValue);
6311
- const snapped = snapToStep$1(parsed, step(), p.minValue);
6312
- if (operation === "+" && snapped > parsed || operation === "-" && snapped < parsed) return clamp$2(snapped, p.minValue, p.maxValue);
6313
- return clamp$2(snapToStep$1(handleDecimalOperation(operation, parsed, step()), step(), p.minValue), p.minValue, p.maxValue);
6650
+ if (isNaN(parsed)) return snapValueToStep(isNaN(minOrMaxValue) ? 0 : minOrMaxValue, p.minValue, p.maxValue, step());
6651
+ const snapped = snapValueToStep(parsed, p.minValue, p.maxValue, step());
6652
+ if (operation === "+" && snapped > parsed || operation === "-" && snapped < parsed) return snapped;
6653
+ return snapValueToStep(handleDecimalOperation(operation, parsed, step()), p.minValue, p.maxValue, step());
6654
+ };
6655
+ const commitSteppedNumber = (next, refreshWhenUnchanged) => {
6656
+ const committed = readNow(actualNumberValue);
6657
+ setNumberValue(next);
6658
+ if (getProps().value === void 0 || refreshWhenUnchanged && next === committed) setInputValueInternal(formatNumber(next));
6314
6659
  };
6315
6660
  const increment = () => {
6316
6661
  ensureInitialized();
@@ -6318,8 +6663,7 @@ function createNumberFieldState(props) {
6318
6663
  const p = getProps();
6319
6664
  if (p.isDisabled || p.isReadOnly) return;
6320
6665
  const current = safeNextStep("+", p.minValue);
6321
- setNumberValue(current);
6322
- setInputValueInternal(formatNumber(current));
6666
+ commitSteppedNumber(current, true);
6323
6667
  p.onChange?.(current);
6324
6668
  validation.commitValidation();
6325
6669
  };
@@ -6329,8 +6673,7 @@ function createNumberFieldState(props) {
6329
6673
  const p = getProps();
6330
6674
  if (p.isDisabled || p.isReadOnly) return;
6331
6675
  const current = safeNextStep("-", p.maxValue);
6332
- setNumberValue(current);
6333
- setInputValueInternal(formatNumber(current));
6676
+ commitSteppedNumber(current, true);
6334
6677
  p.onChange?.(current);
6335
6678
  validation.commitValidation();
6336
6679
  };
@@ -6340,9 +6683,8 @@ function createNumberFieldState(props) {
6340
6683
  const p = getProps();
6341
6684
  if (p.isDisabled || p.isReadOnly) return;
6342
6685
  if (p.maxValue == null) return;
6343
- const snapped = snapToStep$1(p.maxValue, step(), p.minValue);
6344
- setNumberValue(snapped);
6345
- setInputValueInternal(formatNumber(snapped));
6686
+ const snapped = snapValueToStep(p.maxValue, p.minValue, p.maxValue, step());
6687
+ commitSteppedNumber(snapped, false);
6346
6688
  p.onChange?.(snapped);
6347
6689
  validation.commitValidation();
6348
6690
  };
@@ -6352,15 +6694,13 @@ function createNumberFieldState(props) {
6352
6694
  const p = getProps();
6353
6695
  if (p.isDisabled || p.isReadOnly) return;
6354
6696
  if (p.minValue == null) return;
6355
- setNumberValue(p.minValue);
6356
- setInputValueInternal(formatNumber(p.minValue));
6697
+ commitSteppedNumber(p.minValue, false);
6357
6698
  p.onChange?.(p.minValue);
6358
6699
  validation.commitValidation();
6359
6700
  };
6360
6701
  const setNumberValuePublic = (value) => {
6361
6702
  ensureInitialized();
6362
- setNumberValue(value);
6363
- setInputValueInternal(formatNumber(value));
6703
+ commitSteppedNumber(value, false);
6364
6704
  getProps().onChange?.(value);
6365
6705
  };
6366
6706
  return {
@@ -6375,7 +6715,7 @@ function createNumberFieldState(props) {
6375
6715
  return inputValue;
6376
6716
  },
6377
6717
  get numberValue() {
6378
- return actualNumberValue;
6718
+ return parsedInputValue;
6379
6719
  },
6380
6720
  canIncrement,
6381
6721
  canDecrement,
@@ -6448,75 +6788,144 @@ function snapToStep(value, min, max, step) {
6448
6788
  const decimalPlaces = (step.toString().split(".")[1] || "").length;
6449
6789
  return clamp$1(parseFloat(snapped.toFixed(decimalPlaces)), min, max);
6450
6790
  }
6451
- /**
6452
- * Provides state management for a slider component.
6453
- */
6454
6791
  function createSliderState(props) {
6455
6792
  const getProps = () => access(props);
6456
6793
  const initialProps = getProps();
6457
- const initialMinValue = initialProps.minValue ?? DEFAULT_MIN;
6458
- const initialMaxValue = initialProps.maxValue ?? DEFAULT_MAX;
6459
- const initialStep = initialProps.step ?? DEFAULT_STEP;
6460
- const minValue = createMemo(() => getProps().minValue ?? DEFAULT_MIN);
6461
- const maxValue = createMemo(() => getProps().maxValue ?? DEFAULT_MAX);
6462
- const step = createMemo(() => getProps().step ?? DEFAULT_STEP);
6794
+ const arrayMode = Array.isArray(initialProps.value) || Array.isArray(initialProps.defaultValue);
6795
+ const initialArray = Array.isArray(initialProps.value) ? initialProps.value : initialProps.defaultValue;
6796
+ const initialCount = Array.isArray(initialArray) ? initialArray.length : 1;
6797
+ const finite = (value, fallback) => typeof value === "number" && Number.isFinite(value) ? value : fallback;
6798
+ const minValue = createMemo(() => arrayMode ? finite(getProps().minValue, DEFAULT_MIN) : getProps().minValue ?? DEFAULT_MIN);
6799
+ const step = createMemo(() => {
6800
+ const next = arrayMode ? finite(getProps().step, DEFAULT_STEP) : getProps().step ?? DEFAULT_STEP;
6801
+ return arrayMode && next <= 0 ? DEFAULT_STEP : next;
6802
+ });
6803
+ const maxValue = createMemo(() => {
6804
+ const next = arrayMode ? finite(getProps().maxValue, DEFAULT_MAX) : getProps().maxValue ?? DEFAULT_MAX;
6805
+ return arrayMode && next <= minValue() ? minValue() + step() : next;
6806
+ });
6463
6807
  const orientation = createMemo(() => getProps().orientation ?? "horizontal");
6464
6808
  const isDisabled = createMemo(() => getProps().isDisabled ?? false);
6465
6809
  const pageStep = createMemo(() => Math.max(step(), snapToStep((maxValue() - minValue()) / 10, 0, maxValue() - minValue(), step())));
6466
6810
  const isControlled = () => getProps().value !== void 0;
6467
- const [internalValue, setInternalValue] = createInternalSignal(snapToStep(initialProps.defaultValue ?? initialMinValue, initialMinValue, initialMaxValue, initialStep));
6468
- const [isDragging, setIsDragging] = createInternalSignal(false);
6469
- const [isFocused, setIsFocused] = createInternalSignal(false);
6470
- const value = createMemo(() => {
6471
- const p = getProps();
6472
- return snapToStep(isControlled() ? p.value ?? minValue() : internalValue(), minValue(), maxValue(), step());
6473
- });
6474
- const getValuePercent = createMemo(() => {
6475
- return (value() - minValue()) / (maxValue() - minValue());
6476
- });
6477
- const getFormattedValue = createMemo(() => {
6478
- const p = getProps();
6479
- return new Intl.NumberFormat(p.locale, p.formatOptions).format(value());
6480
- });
6481
- const setValue = (newValue) => {
6482
- if (isDisabled()) return;
6811
+ const fallbackValues = () => Array.from({ length: initialCount }, (_, index) => index === 0 ? minValue() : maxValue());
6812
+ const normalize = (raw) => {
6813
+ const snapped = (raw === void 0 ? fallbackValues() : typeof raw === "number" ? [raw] : raw).map((item, index) => snapToStep(arrayMode ? finite(item, index === 0 ? minValue() : maxValue()) : item, minValue(), maxValue(), step()));
6814
+ return arrayMode ? snapped.sort((a, b) => a - b) : snapped;
6815
+ };
6816
+ const [internalValues, setInternalValues] = createInternalSignal(normalize(initialProps.defaultValue));
6817
+ const values = () => normalize(isControlled() ? getProps().value : internalValues());
6818
+ const initialValues = [...values()];
6819
+ const initialDefaultValues = normalize(initialProps.defaultValue ?? initialValues);
6820
+ const defaultValues = () => normalize(arrayMode ? getProps().defaultValue : initialDefaultValues);
6821
+ if (arrayMode) {
6822
+ const resetSnapshot = () => isControlled() ? void 0 : {
6823
+ defaults: normalize(getProps().defaultValue),
6824
+ min: minValue(),
6825
+ max: maxValue(),
6826
+ step: step()
6827
+ };
6828
+ let previousReset = resetSnapshot();
6829
+ createEffect(resetSnapshot, (next) => {
6830
+ const previous = previousReset;
6831
+ previousReset = next;
6832
+ if (next && (!previous || next.min !== previous.min || next.max !== previous.max || next.step !== previous.step || next.defaults.length !== previous.defaults.length || next.defaults.some((value, index) => value !== previous.defaults[index]))) setInternalValues(next.defaults);
6833
+ });
6834
+ }
6835
+ const [draggings, setDraggings] = createInternalSignal(initialValues.map(() => false));
6836
+ const [focusedThumb, setFocusedThumbValue] = createInternalSignal(void 0);
6837
+ const isDragging = () => draggings().some(Boolean);
6838
+ let pendingValues = [...initialValues];
6839
+ let hasPendingUpdate = false;
6840
+ let nonDraggingRenderedSnapshot;
6841
+ const validIndex = (index) => Number.isInteger(index) && index >= 0 && index < values().length;
6842
+ const getThumbValue = (index) => values()[index];
6843
+ const getThumbMinValue = (index) => index === 0 ? minValue() : values()[index - 1];
6844
+ const getThumbMaxValue = (index) => index === values().length - 1 ? maxValue() : values()[index + 1];
6845
+ const emit = (kind, next) => {
6483
6846
  const p = getProps();
6484
- const snappedValue = snapToStep(newValue, minValue(), maxValue(), step());
6485
- if (!isControlled()) setInternalValue(snappedValue);
6486
- p.onChange?.(snappedValue);
6487
- };
6488
- const setValuePercent = (percent) => {
6489
- const newValue = clamp$1(percent, 0, 1) * (maxValue() - minValue()) + minValue();
6490
- setValue(newValue);
6491
- };
6492
- const setDragging = (dragging) => {
6493
- const wasDragging = isDragging();
6494
- setIsDragging(dragging);
6495
- if (wasDragging && !dragging) getProps().onChangeEnd?.(value());
6496
- };
6497
- const increment = (stepMultiplier = 1) => {
6498
- if (isDisabled()) return;
6499
- setValue(value() + step() * stepMultiplier);
6500
- };
6501
- const decrement = (stepMultiplier = 1) => {
6502
- if (isDisabled()) return;
6503
- setValue(value() - step() * stepMultiplier);
6504
- };
6505
- const setFocused = (focused) => {
6506
- setIsFocused(focused);
6507
- };
6847
+ if (arrayMode) p[kind]?.([...next]);
6848
+ else p[kind]?.(next[0]);
6849
+ };
6850
+ const setThumbValue = (index, nextValue) => {
6851
+ if (isDisabled() || !validIndex(index)) return;
6852
+ const rendered = values();
6853
+ const retainNonDraggingProposal = arrayMode && isControlled() && nonDraggingRenderedSnapshot !== void 0 && rendered.length === nonDraggingRenderedSnapshot.length && rendered.every((item, i) => item === nonDraggingRenderedSnapshot[i]);
6854
+ const base = normalize(isDragging() || retainNonDraggingProposal ? pendingValues : rendered);
6855
+ const lower = index === 0 ? minValue() : base[index - 1];
6856
+ const upper = index === base.length - 1 ? maxValue() : base[index + 1];
6857
+ const snapped = clamp$1(snapToStep(nextValue, minValue(), maxValue(), step()), lower, upper);
6858
+ const next = [...base];
6859
+ next[index] = snapped;
6860
+ pendingValues = next;
6861
+ hasPendingUpdate = true;
6862
+ nonDraggingRenderedSnapshot = !isDragging() && arrayMode && isControlled() ? [...rendered] : void 0;
6863
+ const changed = next.some((item, i) => item !== rendered[i]);
6864
+ if (!isControlled() && (!arrayMode || changed)) setInternalValues(next);
6865
+ if (!arrayMode || changed) emit("onChange", next);
6866
+ };
6867
+ const setThumbPercent = (index, percent) => setThumbValue(index, clamp$1(percent, 0, 1) * (maxValue() - minValue()) + minValue());
6868
+ const getThumbPercent = (index) => (getThumbValue(index) - minValue()) / (maxValue() - minValue());
6869
+ const isThumbDragging = (index) => draggings()[index] ?? false;
6870
+ const setThumbDragging = (index, dragging) => {
6871
+ if (dragging && (!validIndex(index) || arrayMode && isDisabled())) return;
6872
+ const wasDragging = isThumbDragging(index);
6873
+ if (wasDragging === dragging) return;
6874
+ if (dragging && !isDragging()) {
6875
+ pendingValues = [...values()];
6876
+ hasPendingUpdate = false;
6877
+ nonDraggingRenderedSnapshot = void 0;
6878
+ }
6879
+ const next = [...draggings()];
6880
+ next[index] = dragging;
6881
+ setDraggings(next);
6882
+ if (wasDragging && !isDragging()) emit("onChangeEnd", normalize(hasPendingUpdate ? pendingValues : values()));
6883
+ };
6884
+ const setFocusedThumb = (index) => {
6885
+ if (index === void 0 || validIndex(index)) setFocusedThumbValue(index);
6886
+ };
6887
+ const incrementThumb = (index, stepSize = 1) => setThumbValue(index, getThumbValue(index) + Math.max(stepSize, step()));
6888
+ const decrementThumb = (index, stepSize = 1) => setThumbValue(index, getThumbValue(index) - Math.max(stepSize, step()));
6889
+ const formatter = createMemo(() => new Intl.NumberFormat(getProps().locale, getProps().formatOptions));
6890
+ function getFormattedValue(value = values()) {
6891
+ const items = typeof value === "number" ? [value] : value;
6892
+ if (items.length === 0) return "";
6893
+ if (items.length === 1) return formatter().format(items[0]);
6894
+ if (items.length === 2) return formatter().formatRange(items[0], items[1]);
6895
+ return new Intl.ListFormat(formatter().resolvedOptions().locale, { type: "unit" }).format(items.map((item) => formatter().format(item)));
6896
+ }
6897
+ const value = () => getThumbValue(0) ?? minValue();
6898
+ const setValue = (next) => setThumbValue(0, next);
6508
6899
  return {
6900
+ values,
6901
+ defaultValues,
6902
+ getThumbValue,
6903
+ setThumbValue,
6904
+ setThumbPercent,
6905
+ getThumbPercent,
6906
+ getThumbMinValue,
6907
+ getThumbMaxValue,
6908
+ getThumbValueLabel: (index) => getFormattedValue(getThumbValue(index)),
6909
+ isThumbDragging,
6910
+ setThumbDragging,
6911
+ focusedThumb,
6912
+ setFocusedThumb,
6913
+ incrementThumb,
6914
+ decrementThumb,
6509
6915
  value,
6510
6916
  setValue,
6511
- setValuePercent,
6512
- getValuePercent,
6917
+ setValuePercent: (percent) => setThumbPercent(0, percent),
6918
+ getValuePercent: () => getThumbPercent(0),
6513
6919
  getFormattedValue,
6514
6920
  isDragging,
6515
- setDragging,
6516
- isFocused,
6517
- setFocused,
6518
- increment,
6519
- decrement,
6921
+ setDragging: (dragging) => setThumbDragging(0, dragging),
6922
+ isFocused: () => focusedThumb() !== void 0,
6923
+ setFocused: (focused) => {
6924
+ if (focused) setFocusedThumb(0);
6925
+ else if (focusedThumb() === 0) setFocusedThumb(void 0);
6926
+ },
6927
+ increment: (multiplier = 1) => setValue(value() + step() * multiplier),
6928
+ decrement: (multiplier = 1) => setValue(value() - step() * multiplier),
6520
6929
  get minValue() {
6521
6930
  return minValue();
6522
6931
  },
@@ -6991,7 +7400,7 @@ function createDisclosureGroupState(props = {}) {
6991
7400
  * Handles row selection, focus management, and keyboard navigation state.
6992
7401
  */
6993
7402
  function createGridState(options) {
6994
- const getOptions = () => options();
7403
+ const getOptions = propsAccessor(options);
6995
7404
  const disabledKeys = createMemo(() => {
6996
7405
  const keys = getOptions().disabledKeys;
6997
7406
  return keys ? new Set(keys) : /* @__PURE__ */ new Set();
@@ -7014,15 +7423,18 @@ function createGridState(options) {
7014
7423
  const disallowEmptySelection = createMemo(() => getOptions().disallowEmptySelection ?? false);
7015
7424
  const disabledBehavior = createMemo(() => getOptions().disabledBehavior ?? "all");
7016
7425
  const focusMode = createMemo(() => getOptions().focusMode ?? "row");
7426
+ let lastSelectionBehavior = selectionBehaviorProp();
7017
7427
  createEffect(selectionBehaviorProp, (behavior) => {
7018
- setSelectionBehaviorState(behavior);
7428
+ if (behavior !== lastSelectionBehavior) {
7429
+ setSelectionBehaviorState(behavior);
7430
+ lastSelectionBehavior = behavior;
7431
+ }
7019
7432
  });
7020
7433
  createEffect(() => ({
7021
7434
  prop: selectionBehaviorProp(),
7022
- state: selectionBehaviorState(),
7023
7435
  keys: selectedKeys()
7024
- }), ({ prop, state, keys }) => {
7025
- if (prop === "replace" && state === "toggle" && keys !== "all" && keys.size === 0) setSelectionBehaviorState("replace");
7436
+ }), ({ prop, keys }) => {
7437
+ if (prop === "replace" && selectionBehaviorState() === "toggle" && keys !== "all" && keys.size === 0) setSelectionBehaviorState("replace");
7026
7438
  });
7027
7439
  const setFocusedKey = (key, strategy = "first") => {
7028
7440
  const collection = getOptions().collection;
@@ -7279,7 +7691,7 @@ const OPPOSITE_SORT_DIRECTION = {
7279
7691
  * Extends grid state with sorting and table-specific features.
7280
7692
  */
7281
7693
  function createTableState(options) {
7282
- const getOptions = () => options();
7694
+ const getOptions = propsAccessor(options);
7283
7695
  const gridState = createGridState(() => ({
7284
7696
  collection: getOptions().collection,
7285
7697
  disabledKeys: getOptions().disabledKeys,
@@ -7321,7 +7733,7 @@ function createTableState(options) {
7321
7733
  return gridState.disabledBehavior;
7322
7734
  },
7323
7735
  get isKeyboardNavigationDisabled() {
7324
- return gridState.isKeyboardNavigationDisabled;
7736
+ return getOptions().collection.size === 0 || gridState.isKeyboardNavigationDisabled;
7325
7737
  },
7326
7738
  get focusedKey() {
7327
7739
  return gridState.focusedKey;
@@ -7759,7 +8171,7 @@ function createTableCollection(options) {
7759
8171
  * expanded rows in addition to the selection, sort and focus handled by the table state.
7760
8172
  */
7761
8173
  function createTreeGridState(options) {
7762
- const getOptions = () => options();
8174
+ const getOptions = propsAccessor(options);
7763
8175
  const [internalExpandedKeys, setInternalExpandedKeys] = createInternalSignal(getInitialExpanded(getOptions().UNSTABLE_defaultExpandedKeys));
7764
8176
  const expandedKeys = () => {
7765
8177
  const controlled = getOptions().UNSTABLE_expandedKeys;
@@ -7902,13 +8314,28 @@ function parseColumnDef(col, tableWidth) {
7902
8314
  maxWidth
7903
8315
  };
7904
8316
  }
8317
+ function applyFractionalRemainder(widths, columns, originalWidth) {
8318
+ if (!(originalWidth - Math.floor(originalWidth) > 0) || columns.length === 0) return;
8319
+ const lastKey = columns[columns.length - 1].key;
8320
+ if (widths.has(lastKey)) {
8321
+ const tableFractionalWidth = originalWidth.toString().split(".")[1];
8322
+ if (tableFractionalWidth) {
8323
+ const columnWidth = Math.floor(widths.get(lastKey)).toString();
8324
+ widths.set(lastKey, Number(columnWidth + "." + tableFractionalWidth));
8325
+ }
8326
+ }
8327
+ }
7905
8328
  /**
7906
8329
  * Distribute `tableWidth` among columns:
7907
- * 1. Fixed-width columns get their declared width (clamped to min/max).
7908
- * 2. Remaining space is distributed among fractional columns proportionally.
8330
+ * 1. Floor tableWidth to integer before column sizing, matching upstream TableUtils.calculateColumnSizes.
8331
+ * 2. Fixed-width columns get their declared width (clamped to min/max).
8332
+ * 3. Remaining space is distributed among fractional columns proportionally with cascading rounding.
8333
+ * 4. Leftover sub-pixel width is added to the last column so widths sum exactly to tableWidth.
7909
8334
  */
7910
8335
  function distributeWidths(columns, tableWidth) {
7911
- const parsed = columns.map((c) => parseColumnDef(c, tableWidth));
8336
+ const originalWidth = tableWidth;
8337
+ const availableWidth = Math.max(0, Math.floor(tableWidth));
8338
+ const parsed = columns.map((c) => parseColumnDef(c, availableWidth));
7912
8339
  let usedSpace = 0;
7913
8340
  let totalFr = 0;
7914
8341
  for (const p of parsed) if (p.fr > 0) totalFr += p.fr;
@@ -7916,16 +8343,22 @@ function distributeWidths(columns, tableWidth) {
7916
8343
  const w = Math.max(p.minWidth, Math.min(p.maxWidth, p.fixedPx));
7917
8344
  usedSpace += w;
7918
8345
  }
7919
- const remainingSpace = Math.max(0, tableWidth - usedSpace);
8346
+ const remainingSpace = Math.max(0, availableWidth - usedSpace);
7920
8347
  const perFr = totalFr > 0 ? remainingSpace / totalFr : 0;
7921
8348
  const widths = /* @__PURE__ */ new Map();
8349
+ let fpTotal = 0;
8350
+ let intTotal = 0;
7922
8351
  for (const p of parsed) {
7923
- let w;
7924
- if (p.fr > 0) w = p.fr * perFr;
7925
- else w = p.fixedPx;
7926
- w = Math.max(p.minWidth, Math.min(p.maxWidth, w));
7927
- widths.set(p.key, Math.round(w));
7928
- }
8352
+ let targetSize;
8353
+ if (p.fr > 0) targetSize = p.fr * perFr;
8354
+ else targetSize = p.fixedPx;
8355
+ targetSize = Math.max(p.minWidth, Math.min(p.maxWidth, targetSize));
8356
+ const rounded = Math.round(targetSize + fpTotal) - intTotal;
8357
+ fpTotal += targetSize;
8358
+ intTotal += rounded;
8359
+ widths.set(p.key, rounded);
8360
+ }
8361
+ applyFractionalRemainder(widths, columns, originalWidth);
7929
8362
  return widths;
7930
8363
  }
7931
8364
  /**
@@ -7946,6 +8379,7 @@ function createTableColumnResizeState(props) {
7946
8379
  if (over.size === 0) return base;
7947
8380
  const merged = new Map(base);
7948
8381
  for (const [key, width] of over) if (merged.has(key)) merged.set(key, width);
8382
+ applyFractionalRemainder(merged, getProps().columns, getProps().tableWidth);
7949
8383
  return merged;
7950
8384
  });
7951
8385
  const minWidthMap = createMemo(() => {
@@ -7976,13 +8410,14 @@ function createTableColumnResizeState(props) {
7976
8410
  const updateResizedColumns = (key, width) => {
7977
8411
  const minW = getColumnMinWidth(key);
7978
8412
  const maxW = getColumnMaxWidth(key);
7979
- const clampedWidth = Math.round(Math.max(minW, Math.min(maxW, width)));
8413
+ const clampedWidth = Math.max(minW, Math.min(maxW, Math.floor(width)));
7980
8414
  const newOverrides = new Map(overrides());
7981
8415
  newOverrides.set(key, clampedWidth);
7982
8416
  setOverrides(newOverrides);
7983
8417
  const base = initialWidths();
7984
8418
  const merged = new Map(base);
7985
8419
  for (const [k, w] of newOverrides) if (merged.has(k)) merged.set(k, w);
8420
+ applyFractionalRemainder(merged, getProps().columns, getProps().tableWidth);
7986
8421
  return merged;
7987
8422
  };
7988
8423
  return {
@@ -8009,7 +8444,7 @@ function createTableColumnResizeState(props) {
8009
8444
  * Handles expansion, selection, focus management, and keyboard navigation state.
8010
8445
  */
8011
8446
  function createTreeState(options) {
8012
- const getOptions = () => options();
8447
+ const getOptions = propsAccessor(options);
8013
8448
  const disabledKeys = createMemo(() => {
8014
8449
  const keys = getOptions().disabledKeys;
8015
8450
  return keys ? new Set(keys) : /* @__PURE__ */ new Set();
@@ -8040,15 +8475,18 @@ function createTreeState(options) {
8040
8475
  const selectionBehavior = createMemo(() => selectionBehaviorState());
8041
8476
  const disallowEmptySelection = createMemo(() => getOptions().disallowEmptySelection ?? false);
8042
8477
  const disabledBehavior = createMemo(() => getOptions().disabledBehavior ?? "all");
8478
+ let lastSelectionBehavior = selectionBehaviorProp();
8043
8479
  createEffect(selectionBehaviorProp, (behavior) => {
8044
- setSelectionBehaviorState(behavior);
8480
+ if (behavior !== lastSelectionBehavior) {
8481
+ setSelectionBehaviorState(behavior);
8482
+ lastSelectionBehavior = behavior;
8483
+ }
8045
8484
  });
8046
8485
  createEffect(() => ({
8047
8486
  prop: selectionBehaviorProp(),
8048
- state: selectionBehaviorState(),
8049
8487
  keys: selectedKeys()
8050
- }), ({ prop, state, keys }) => {
8051
- if (prop === "replace" && state === "toggle" && keys !== "all" && keys.size === 0) setSelectionBehaviorState("replace");
8488
+ }), ({ prop, keys }) => {
8489
+ if (prop === "replace" && selectionBehaviorState() === "toggle" && keys !== "all" && keys.size === 0) setSelectionBehaviorState("replace");
8052
8490
  });
8053
8491
  const setFocusedKey = (key, strategy = "first") => {
8054
8492
  setFocusedKeyInternal(key);
@@ -10558,6 +10996,14 @@ function createColorSliderState(options) {
10558
10996
  const init = initValue();
10559
10997
  if (init) setInternalValue(init);
10560
10998
  }
10999
+ const initialOptions = getOptions();
11000
+ const resetColor = (color) => {
11001
+ const normalized = normalizeColor(color);
11002
+ return initialOptions.colorSpace ? normalized.toFormat(initialOptions.colorSpace) : normalized;
11003
+ };
11004
+ const initialDefault = initialOptions.defaultValue ? resetColor(initialOptions.defaultValue) : void 0;
11005
+ const firstColor = initialOptions.value !== void 0 ? resetColor(initialOptions.value) : void 0;
11006
+ const defaultThumbValue = (initialDefault ?? firstColor ?? resetColor("#ff0000")).getChannelValue(initialOptions.channel);
10561
11007
  const value = createMemo(() => {
10562
11008
  const opts = getOptions();
10563
11009
  if (opts.value !== void 0) return normalizeValue(opts.value);
@@ -10631,7 +11077,9 @@ function createColorSliderState(options) {
10631
11077
  };
10632
11078
  const getDisplayColor = () => {
10633
11079
  const v = value();
10634
- if (channel() === "hue") return normalizeColor(`hsl(${v.getChannelValue("hue")}, 100%, 50%)`);
11080
+ const current = channel();
11081
+ if (current === "hue") return normalizeColor(`hsl(${v.getChannelValue("hue")}, 100%, 50%)`);
11082
+ if (current === "alpha") return v;
10635
11083
  return v.withChannelValue("alpha", 1);
10636
11084
  };
10637
11085
  const getThumbValueLabel = () => {
@@ -10668,6 +11116,7 @@ function createColorSliderState(options) {
10668
11116
  get isDisabled() {
10669
11117
  return isDisabled();
10670
11118
  },
11119
+ defaultThumbValue,
10671
11120
  getThumbValue,
10672
11121
  getThumbMinValue,
10673
11122
  getThumbMaxValue,
@@ -10698,6 +11147,9 @@ function createColorAreaState(options) {
10698
11147
  return colorSpace ? color.toFormat(colorSpace) : color;
10699
11148
  };
10700
11149
  const [internalValue, setInternalValue] = createInternalSignal(null);
11150
+ const firstOptions = getOptions();
11151
+ const controlledInitial = firstOptions.value !== void 0 ? normalizeValue(firstOptions.value) : void 0;
11152
+ const initialValue = normalizeValue(firstOptions.defaultValue ?? "#ff0000");
10701
11153
  const [isDragging, setIsDragging] = createInternalSignal(false);
10702
11154
  const initValue = () => {
10703
11155
  const opts = getOptions();
@@ -10745,6 +11197,9 @@ function createColorAreaState(options) {
10745
11197
  };
10746
11198
  const getXValue = () => value().getChannelValue(xChannel());
10747
11199
  const getYValue = () => value().getChannelValue(yChannel());
11200
+ const setValue = (newValue) => {
11201
+ updateValue(normalizeValue(newValue));
11202
+ };
10748
11203
  const setXValue = (newValue) => {
10749
11204
  const range = xRange();
10750
11205
  const clamped = Math.max(range.minValue, Math.min(range.maxValue, newValue));
@@ -10813,6 +11268,9 @@ function createColorAreaState(options) {
10813
11268
  get value() {
10814
11269
  return value();
10815
11270
  },
11271
+ get defaultValue() {
11272
+ return controlledInitial ?? initialValue;
11273
+ },
10816
11274
  get xChannel() {
10817
11275
  return xChannel();
10818
11276
  },
@@ -10844,6 +11302,7 @@ function createColorAreaState(options) {
10844
11302
  getYValue,
10845
11303
  setXValue,
10846
11304
  setYValue,
11305
+ setValue,
10847
11306
  setColorFromPoint,
10848
11307
  getThumbPosition,
10849
11308
  incrementX,
@@ -10870,7 +11329,9 @@ function createColorWheelState(options) {
10870
11329
  const value = normalizeColor(color);
10871
11330
  return value.toFormat(value.getColorSpace() === "hsb" ? "hsb" : "hsl");
10872
11331
  };
10873
- const initialValue = normalizeWheelValue(getOptions().defaultValue ?? defaultColor);
11332
+ const firstOptions = getOptions();
11333
+ const controlledInitial = firstOptions.value !== void 0 ? normalizeWheelValue(firstOptions.value) : void 0;
11334
+ const initialValue = normalizeWheelValue(firstOptions.defaultValue ?? defaultColor);
10874
11335
  const [internalValue, setInternalValue] = createInternalSignal(initialValue);
10875
11336
  const [isDragging, setIsDragging] = createInternalSignal(false);
10876
11337
  let valueRef = initialValue;
@@ -10912,9 +11373,7 @@ function createColorWheelState(options) {
10912
11373
  const radToDeg = (rad) => rad * 180 / Math.PI;
10913
11374
  const degToRad = (deg) => deg * Math.PI / 180;
10914
11375
  const cartesianToAngle = (x, y, radius) => {
10915
- const clampedX = Math.max(-radius, Math.min(radius, x));
10916
- const clampedY = Math.max(-radius, Math.min(radius, y));
10917
- return mod(radToDeg(Math.atan2(clampedY, clampedX)), 360);
11376
+ return mod(radToDeg(Math.atan2(y / radius, x / radius)), 360);
10918
11377
  };
10919
11378
  const angleToCartesian = (angle, radius) => ({
10920
11379
  x: radius * Math.cos(degToRad(angle)),
@@ -10973,7 +11432,7 @@ function createColorWheelState(options) {
10973
11432
  return value();
10974
11433
  },
10975
11434
  get defaultValue() {
10976
- return initialValue;
11435
+ return controlledInitial ?? initialValue;
10977
11436
  },
10978
11437
  get isDragging() {
10979
11438
  return isDragging();
@@ -11075,9 +11534,13 @@ function createColorFieldState(options) {
11075
11534
  const getOptions = () => options();
11076
11535
  const initialOptions = getOptions();
11077
11536
  const initialValue = getDefaultValue(initialOptions);
11537
+ const initialDefault = initialOptions.defaultValue !== void 0 ? normalizeNullableColor(initialOptions.defaultValue) : void 0;
11538
+ const firstColor = initialOptions.value !== void 0 ? normalizeNullableColor(initialOptions.value) : void 0;
11539
+ const defaultColorValue = initialDefault ?? firstColor ?? null;
11078
11540
  const [internalValue, setInternalValue] = createInternalSignal(initialValue);
11079
11541
  const [inputValue, setInputValueInternal] = createInternalSignal(formatColorValue(initialValue, initialOptions.channel, initialOptions.colorSpace, initialOptions.colorFormat, initialOptions.locale ?? "en-US"));
11080
11542
  const [invalidInput, setInvalidInput] = createInternalSignal(false);
11543
+ let isChannelInputEdited = false;
11081
11544
  const channel = createMemo(() => getOptions().channel);
11082
11545
  const colorSpace = createMemo(() => getOptions().colorSpace);
11083
11546
  const locale = createMemo(() => getOptions().locale ?? "en-US");
@@ -11093,6 +11556,7 @@ function createColorFieldState(options) {
11093
11556
  const isInvalid = createMemo(() => (getOptions().isInvalid ?? false) || invalidInput());
11094
11557
  const formatCurrentValue = (color = value()) => formatColorValue(color, channel(), colorSpace(), getOptions().colorFormat, locale());
11095
11558
  createEffect(() => formatCurrentValue(), (formatted) => {
11559
+ isChannelInputEdited = false;
11096
11560
  setInputValueInternal(formatted);
11097
11561
  setInvalidInput(false);
11098
11562
  });
@@ -11100,56 +11564,69 @@ function createColorFieldState(options) {
11100
11564
  const opts = getOptions();
11101
11565
  if (opts.value === void 0) setInternalValue(newColor);
11102
11566
  opts.onChange?.(newColor);
11567
+ isChannelInputEdited = false;
11103
11568
  setInputValueInternal(formatCurrentValue(newColor));
11104
11569
  setInvalidInput(false);
11105
11570
  };
11106
11571
  const setInputValue = (text) => {
11572
+ isChannelInputEdited = true;
11107
11573
  setInputValueInternal(text);
11108
11574
  setInvalidInput(false);
11109
11575
  };
11110
11576
  const setColorValue = (newColor) => {
11111
11577
  updateValue(newColor);
11112
11578
  };
11579
+ const currentValue = () => {
11580
+ const opts = getOptions();
11581
+ return opts.value !== void 0 ? normalizeNullableColor(opts.value) : readNow(internalValue);
11582
+ };
11113
11583
  const commit = () => {
11114
11584
  const text = readNow(inputValue).trim();
11115
11585
  const opts = getOptions();
11116
11586
  const chan = channel();
11587
+ const current = currentValue();
11117
11588
  if (!text) {
11118
- updateValue(null);
11119
- return;
11120
- }
11121
- if (chan) {
11122
- const displayColor = colorValue();
11589
+ if (current !== null) updateValue(null);
11590
+ } else if (chan) {
11591
+ const displayColor = getDisplayColor(current, colorSpace());
11123
11592
  const rawValue = parseChannelValue(text, displayColor, chan);
11124
- if (rawValue == null) {
11125
- setInputValueInternal(formatCurrentValue());
11126
- setInvalidInput(false);
11127
- return;
11593
+ if (isChannelInputEdited && rawValue !== null) {
11594
+ const next = displayColor.withChannelValue(chan, rawValue);
11595
+ if (current === null || next.getChannelValue(chan) !== displayColor.getChannelValue(chan)) updateValue(next);
11128
11596
  }
11129
- updateValue(displayColor.withChannelValue(chan, rawValue));
11130
- return;
11131
- }
11597
+ } else try {
11598
+ const next = parseHexValue(text, opts.colorFormat);
11599
+ if (current === null || next.toHexInt() !== current.toHexInt() || opts.colorFormat === "hexa" && next.getChannelValue("alpha") !== current.getChannelValue("alpha")) updateValue(next);
11600
+ } catch {}
11601
+ isChannelInputEdited = false;
11602
+ setInputValueInternal(formatCurrentValue(currentValue()));
11603
+ setInvalidInput(false);
11604
+ };
11605
+ const incrementHex = (amount) => {
11606
+ const text = readNow(inputValue);
11607
+ let parsed = null;
11132
11608
  try {
11133
- updateValue(parseHexValue(text, opts.colorFormat));
11609
+ parsed = parseColor(text.startsWith("#") ? text : `#${text}`);
11134
11610
  } catch {
11135
- setInputValueInternal(formatCurrentValue());
11136
- setInvalidInput(false);
11611
+ parsed = null;
11137
11612
  }
11138
- };
11139
- const incrementHex = (amount) => {
11140
- const currentColor = value();
11141
- if (!currentColor) return;
11142
- const newColor = hexIntToColor(currentColor.toFormat("rgb").toHexInt() + amount);
11143
- updateValue(getOptions().colorFormat ? newColor.toFormat(getOptions().colorFormat) : newColor);
11613
+ const nextColor = hexIntToColor((parsed ?? hexIntToColor(0)).toFormat("rgb").toHexInt() + amount);
11614
+ const current = value();
11615
+ if (current && current.toFormat("rgb").toHexInt() === nextColor.toHexInt()) return;
11616
+ const format = getOptions().colorFormat;
11617
+ updateValue(format ? nextColor.toFormat(format) : nextColor);
11144
11618
  };
11145
11619
  const updateChannel = (amount) => {
11146
11620
  const chan = channel();
11147
11621
  if (!chan) return;
11148
- const displayColor = colorValue();
11622
+ const current = currentValue();
11623
+ const displayColor = getDisplayColor(current, colorSpace());
11149
11624
  const range = displayColor.getChannelRange(chan);
11150
- const currentValue = displayColor.getChannelValue(chan);
11151
- const nextValue = amount === "min" ? range.minValue : amount === "max" ? range.maxValue : Math.min(range.maxValue, Math.max(range.minValue, currentValue + amount));
11152
- updateValue(displayColor.withChannelValue(chan, nextValue));
11625
+ const currentChannel = displayColor.getChannelValue(chan);
11626
+ const nextValue = amount === "min" ? range.minValue : amount === "max" ? range.maxValue : Math.min(range.maxValue, Math.max(range.minValue, currentChannel + amount));
11627
+ const next = displayColor.withChannelValue(chan, nextValue);
11628
+ if (current !== null && next.getChannelValue(chan) === currentChannel) return;
11629
+ updateValue(next);
11153
11630
  };
11154
11631
  const increment = () => {
11155
11632
  const chan = channel();
@@ -11195,6 +11672,7 @@ function createColorFieldState(options) {
11195
11672
  get colorValue() {
11196
11673
  return colorValue();
11197
11674
  },
11675
+ defaultColorValue,
11198
11676
  get inputValue() {
11199
11677
  return inputValue();
11200
11678
  },
@@ -11572,6 +12050,9 @@ function createDraggableCollectionState(props) {
11572
12050
  if (p.isSelected) return p.isSelected(key);
11573
12051
  const selected = selectedKeySet(p.selectedKeys, p.collection);
11574
12052
  return selected === "all" ? true : selected.has(key);
12053
+ },
12054
+ get collection() {
12055
+ return getProps().collection;
11575
12056
  }
11576
12057
  };
11577
12058
  }
@@ -11781,7 +12262,10 @@ function createDroppableCollectionState(props) {
11781
12262
  drop,
11782
12263
  getDropOperation,
11783
12264
  shouldAcceptItemDrop,
11784
- isDropTargetFor
12265
+ isDropTargetFor,
12266
+ get collection() {
12267
+ return getProps().collection;
12268
+ }
11785
12269
  };
11786
12270
  }
11787
12271
  /**
@@ -11910,8 +12394,11 @@ function createListData(options) {
11910
12394
  },
11911
12395
  insertBefore(key, ...values) {
11912
12396
  setState((s) => {
11913
- const index = s.items.findIndex((item) => getKey(item) === key);
11914
- if (index === -1) return s;
12397
+ let index = s.items.findIndex((item) => getKey(item) === key);
12398
+ if (index === -1) {
12399
+ if (s.items.length === 0) index = 0;
12400
+ else return s;
12401
+ }
11915
12402
  return insertItems(s, index, ...values);
11916
12403
  });
11917
12404
  },
@@ -12238,6 +12725,8 @@ function createTreeData(options) {
12238
12725
  });
12239
12726
  },
12240
12727
  remove(...keys) {
12728
+ if (keys.length === 0) return;
12729
+ let nextMap;
12241
12730
  setTreeState((state) => {
12242
12731
  let { items, nodeMap } = state;
12243
12732
  let newMap = new Map(nodeMap);
@@ -12247,15 +12736,16 @@ function createTreeData(options) {
12247
12736
  newItems = result.items;
12248
12737
  newMap = result.nodeMap;
12249
12738
  }
12739
+ nextMap = newMap;
12250
12740
  return {
12251
12741
  items: newItems,
12252
12742
  nodeMap: newMap
12253
12743
  };
12254
12744
  });
12255
12745
  setSelectedKeys((sel) => {
12256
- if (sel === "all") return sel;
12746
+ if (sel === "all" || !nextMap) return sel;
12257
12747
  const newSel = new Set(sel);
12258
- for (const key of keys) newSel.delete(key);
12748
+ for (const key of sel) if (!nextMap.has(key)) newSel.delete(key);
12259
12749
  return newSel;
12260
12750
  });
12261
12751
  },
@@ -12611,8 +13101,11 @@ function createAsyncList(options) {
12611
13101
  },
12612
13102
  insertBefore(key, ...values) {
12613
13103
  setState((s) => {
12614
- const index = s.items.findIndex((item) => getKey(item) === key);
12615
- if (index === -1) return s;
13104
+ let index = s.items.findIndex((item) => getKey(item) === key);
13105
+ if (index === -1) {
13106
+ if (s.items.length === 0) index = 0;
13107
+ else return s;
13108
+ }
12616
13109
  return {
12617
13110
  ...s,
12618
13111
  items: [
@@ -12625,8 +13118,11 @@ function createAsyncList(options) {
12625
13118
  },
12626
13119
  insertAfter(key, ...values) {
12627
13120
  setState((s) => {
12628
- const index = s.items.findIndex((item) => getKey(item) === key);
12629
- if (index === -1) return s;
13121
+ let index = s.items.findIndex((item) => getKey(item) === key);
13122
+ if (index === -1) {
13123
+ if (s.items.length === 0) index = 0;
13124
+ else return s;
13125
+ }
12630
13126
  return {
12631
13127
  ...s,
12632
13128
  items: [
@@ -12659,6 +13155,7 @@ function createAsyncList(options) {
12659
13155
  for (const key of keys) newSelection.delete(key);
12660
13156
  selectedKeys = newSelection;
12661
13157
  }
13158
+ if (s.cursor == null && items.length === 0) selectedKeys = /* @__PURE__ */ new Set();
12662
13159
  return {
12663
13160
  ...s,
12664
13161
  items,
@@ -12759,100 +13256,144 @@ function getColorChannels(colorSpace) {
12759
13256
  * Creates state for a step list component.
12760
13257
  */
12761
13258
  function createStepListState(props) {
12762
- const items = () => props.items;
12763
- const indexMap = createMemo(() => {
12764
- const map = /* @__PURE__ */ new Map();
12765
- const currentItems = items();
12766
- for (let i = 0; i < currentItems.length; i++) map.set(currentItems[i].key, i);
12767
- return map;
12768
- });
12769
- const disabledKeysSet = createMemo(() => new Set(props.disabledKeys ?? []));
12770
- const isDisabled = () => props.isDisabled ?? false;
13259
+ const isListDisabled = () => props.isDisabled ?? false;
12771
13260
  const isReadOnly = () => props.isReadOnly ?? false;
13261
+ const items = () => props.items ?? [];
12772
13262
  const [lastCompletedStepInternal, setLastCompletedStepInternal] = createInternalSignal(props.defaultLastCompletedStep ?? null);
12773
13263
  const lastCompletedStep = () => {
12774
13264
  if (props.lastCompletedStep !== void 0) return props.lastCompletedStep;
12775
13265
  return lastCompletedStepInternal();
12776
13266
  };
13267
+ const isCompleted = (step) => {
13268
+ if (step == null) return false;
13269
+ const completed = lastCompletedStep();
13270
+ if (completed == null) return false;
13271
+ const { indexMap } = keyMaps();
13272
+ const stepIdx = indexMap.get(step);
13273
+ const completedIdx = indexMap.get(completed);
13274
+ if (stepIdx === void 0 || completedIdx === void 0) return false;
13275
+ return stepIdx <= completedIdx;
13276
+ };
13277
+ const isSelectable = (step) => {
13278
+ if (isListDisabled() || singleSelectListState.disabledKeys().has(step) || isReadOnly()) return false;
13279
+ if (isCompleted(step)) return true;
13280
+ const { keysLinkedList } = keyMaps();
13281
+ const prevStep = keysLinkedList.get(step);
13282
+ return prevStep !== void 0 && isCompleted(prevStep) || step === singleSelectListState.collection().getFirstKey();
13283
+ };
13284
+ const findInitialSelectedKey = () => {
13285
+ if (props.defaultSelectedKey !== void 0) return props.defaultSelectedKey;
13286
+ const currentItems = props.items ?? [];
13287
+ const disabled = new Set(props.disabledKeys ?? []);
13288
+ for (const item of currentItems) if (!disabled.has(item.key)) return item.key;
13289
+ return currentItems[0]?.key;
13290
+ };
13291
+ const singleSelectListState = createSingleSelectListState({
13292
+ get items() {
13293
+ return props.items ?? [];
13294
+ },
13295
+ getKey(item) {
13296
+ return item.key;
13297
+ },
13298
+ getTextValue(item) {
13299
+ return item.label ?? item.textValue ?? (item.key != null ? String(item.key) : "");
13300
+ },
13301
+ get disabledKeys() {
13302
+ return props.disabledKeys;
13303
+ },
13304
+ get selectedKey() {
13305
+ return props.selectedKey;
13306
+ },
13307
+ get defaultSelectedKey() {
13308
+ return findInitialSelectedKey();
13309
+ },
13310
+ onSelectionChange(key) {
13311
+ if (key != null) props.onSelectionChange?.(key);
13312
+ }
13313
+ });
13314
+ const keyMaps = createMemo(() => {
13315
+ const coll = singleSelectListState.collection();
13316
+ const indexMap = /* @__PURE__ */ new Map();
13317
+ const keysLinkedList = /* @__PURE__ */ new Map();
13318
+ let i = 0;
13319
+ let prev;
13320
+ for (const item of coll) {
13321
+ indexMap.set(item.key, i);
13322
+ keysLinkedList.set(item.key, prev?.key);
13323
+ prev = item;
13324
+ i++;
13325
+ }
13326
+ return {
13327
+ indexMap,
13328
+ keysLinkedList
13329
+ };
13330
+ });
12777
13331
  const setLastCompletedStep = (key) => {
12778
- const currentIndex = indexMap().get(key);
12779
- const prevIndex = lastCompletedStep() !== null ? indexMap().get(lastCompletedStep()) : -1;
13332
+ if (key == null) {
13333
+ if (props.lastCompletedStep === void 0) setLastCompletedStepInternal(null);
13334
+ props.onLastCompletedStepChange?.(null);
13335
+ return;
13336
+ }
13337
+ const { indexMap } = keyMaps();
13338
+ const currentIndex = indexMap.get(key);
13339
+ const completed = lastCompletedStep();
13340
+ const prevIndex = completed !== null ? indexMap.get(completed) : -1;
12780
13341
  if (currentIndex !== void 0 && (prevIndex === void 0 || currentIndex > (prevIndex ?? -1))) {
12781
13342
  if (props.lastCompletedStep === void 0) setLastCompletedStepInternal(key);
12782
13343
  props.onLastCompletedStepChange?.(key);
12783
13344
  }
12784
13345
  };
12785
- const isCompleted = (key) => {
12786
- const completed = lastCompletedStep();
12787
- if (completed === null) return false;
12788
- const keyIndex = indexMap().get(key);
12789
- const completedIndex = indexMap().get(completed);
12790
- if (keyIndex === void 0 || completedIndex === void 0) return false;
12791
- return keyIndex <= completedIndex;
12792
- };
12793
- const isStepDisabled = (key) => {
12794
- if (isDisabled()) return true;
12795
- return disabledKeysSet().has(key);
12796
- };
12797
- const isSelectable = (key) => {
12798
- if (isDisabled() || isReadOnly() || isStepDisabled(key)) return false;
12799
- if (isCompleted(key)) return true;
12800
- const keyIndex = indexMap().get(key);
12801
- if (keyIndex === 0) return true;
12802
- const currentItems = items();
12803
- if (keyIndex !== void 0 && keyIndex > 0) {
12804
- const prevKey = currentItems[keyIndex - 1].key;
12805
- if (isCompleted(prevKey)) return true;
12806
- }
12807
- return false;
12808
- };
12809
- const findDefaultSelectedKey = () => {
12810
- const currentItems = items();
12811
- for (const item of currentItems) if (!isCompleted(item.key) && !isStepDisabled(item.key)) return item.key;
12812
- for (const item of currentItems) if (!isStepDisabled(item.key)) return item.key;
12813
- return currentItems.length > 0 ? currentItems[0].key : null;
12814
- };
12815
- const [selectedKeyInternal, setSelectedKeyInternal] = createInternalSignal(props.defaultSelectedKey ?? findDefaultSelectedKey());
12816
- const selectedKey = () => {
12817
- if (props.selectedKey !== void 0) return props.selectedKey;
12818
- return selectedKeyInternal();
12819
- };
12820
- const setSelectedKey = (key) => {
12821
- if (isReadOnly() || isDisabled()) return;
12822
- if (!isSelectable(key)) return;
12823
- const currentSelected = selectedKey();
12824
- if (currentSelected !== null) {
12825
- const currentIndex = indexMap().get(currentSelected);
12826
- const newIndex = indexMap().get(key);
12827
- if (currentIndex !== void 0 && newIndex !== void 0 && newIndex > currentIndex && !isCompleted(currentSelected)) setLastCompletedStep(currentSelected);
12828
- }
12829
- if (props.selectedKey === void 0) setSelectedKeyInternal(key);
12830
- props.onSelectionChange?.(key);
12831
- };
12832
13346
  createEffect(() => {
12833
- const selKey = selectedKey();
13347
+ const selKey = singleSelectListState.selectedKey();
12834
13348
  if (selKey === null) return null;
12835
- const selIdx = indexMap().get(selKey);
13349
+ const { indexMap, keysLinkedList } = keyMaps();
13350
+ const selIdx = indexMap.get(selKey);
12836
13351
  if (selIdx === void 0 || selIdx <= 0) return null;
12837
13352
  const completed = lastCompletedStep();
12838
- if (selIdx <= (completed !== null ? indexMap().get(completed) ?? -1 : -1) + 1) return null;
12839
- return items()[selIdx - 1]?.key;
13353
+ if (selIdx <= (completed !== null ? indexMap.get(completed) ?? -1 : -1) + 1) return null;
13354
+ return keysLinkedList.get(selKey) ?? null;
12840
13355
  }, (prevKey) => {
12841
- if (prevKey !== void 0 && prevKey !== null) setLastCompletedStep(prevKey);
13356
+ if (prevKey != null) setLastCompletedStep(prevKey);
13357
+ });
13358
+ createEffect(() => singleSelectListState.selectedKey(), (selKey) => {
13359
+ if (singleSelectListState.focusedKey() == null && selKey != null) singleSelectListState.setFocusedKey(selKey);
13360
+ });
13361
+ const setSelectedKey = (key) => {
13362
+ if (isListDisabled() || isReadOnly()) return;
13363
+ if (!isSelectable(key)) return;
13364
+ const { keysLinkedList } = keyMaps();
13365
+ const prevKey = keysLinkedList.get(key);
13366
+ if (prevKey && !isCompleted(prevKey)) setLastCompletedStep(prevKey);
13367
+ singleSelectListState.setSelectedKey(key);
13368
+ };
13369
+ singleSelectListState.selectionManager.canSelectItem = (key) => isSelectable(key);
13370
+ const origReplaceSelection = singleSelectListState.selectionManager.replaceSelection.bind(singleSelectListState.selectionManager);
13371
+ singleSelectListState.selectionManager.replaceSelection = (key) => {
13372
+ if (isListDisabled() || isReadOnly()) return;
13373
+ if (!isSelectable(key)) return;
13374
+ const { keysLinkedList } = keyMaps();
13375
+ const prevKey = keysLinkedList.get(key);
13376
+ if (prevKey && !isCompleted(prevKey)) setLastCompletedStep(prevKey);
13377
+ origReplaceSelection(key);
13378
+ };
13379
+ const isDisabledFn = ((key) => {
13380
+ if (key === void 0) return isListDisabled();
13381
+ return !isSelectable(key);
12842
13382
  });
12843
13383
  return {
12844
- selectedKey,
13384
+ ...singleSelectListState,
13385
+ selectedKey: singleSelectListState.selectedKey,
12845
13386
  lastCompletedStep,
12846
13387
  items,
12847
13388
  setSelectedKey,
12848
13389
  setLastCompletedStep,
12849
13390
  isCompleted,
12850
13391
  isSelectable,
12851
- isDisabled,
13392
+ isDisabled: isDisabledFn,
12852
13393
  isReadOnly
12853
13394
  };
12854
13395
  }
12855
13396
  //#endregion
12856
- export { CalendarDateClass, CalendarDateTimeClass, DEFAULT_VALIDATION_RESULT, DIRECTORY_DRAG_TYPE, DateFormatter, FormValidationContext, GregorianCalendar, ListCollection, SelectionManager, TableCollection, TimeClass, Timer, ToastQueue, TokenFieldValue, TreeCollection, VALID_VALIDITY_STATE, ZonedDateTimeClass, access, canUseDOM, createAsyncList, createAutocompleteState, createCalendar, createCalendarState, createCheckboxGroupState, createColorAreaState, createColorFieldState, createColorSliderState, createColorWheelState, createComboBoxState, createDateFieldState, createDateFieldState as useDateFieldState, createDatePickerState, createDatePickerState as useDatePickerState, createDisclosureGroupState, createDisclosureState, createDragState, createDraggableCollectionState, createDropState, createDroppableCollectionState, createFilteredListState, createFormValidationState, createGridState, createHSBColor, createHSLColor, createId, createIsSSR, createListCollection, createListData, createListState, createMenuState, createMenuTriggerState, createMenuTriggerState as useMenuTriggerState, createNumberFieldState, createOverlayTriggerState, createRGBColor, createRadioGroupState, createRangeCalendarState, createRangeCalendarState as useDateRangePickerState, createSearchFieldState, createSelectState, createSelectionState, createSelectionState as useMultipleSelectionState, createSingleSelectListState, createSliderState, createStepListState, createTabListState, createTableCollection, createTableColumnResizeState, createTableState, createTextFieldState, createTimeFieldState, createTimeFieldState as useTimeFieldState, createToastQueue, createToastState, createToggleGroupState, createToggleState, createTokenFieldState, createTooltipTriggerState, createTreeCollection, createTreeData, createTreeGridState, createTreeState, defaultContainsFilter, endOfMonth, endOfWeek, endOfYear, getColorChannels, getDayOfWeek, getItemCount, getKeysForDrag, getLocalTimeZone, getWeeksInMonth, isAccessor, isSameDay, isSameMonth, isSameYear, isServer, isToday, isWeekend, maxDate, mergeValidation, minDate, normalizeColor, now, parseAbsolute, parseAbsoluteToLocal, parseColor, parseDate, parseDateTime, parseTime, parseZonedDateTime, privateValidationStateProp, radioGroupSyncVersion, resetTooltipState, startOfMonth, startOfWeek, startOfYear, toCalendar, toCalendarDate, toCalendarDateTime, toTime, toZoned, today };
13397
+ export { CalendarDateClass, CalendarDateTimeClass, DEFAULT_VALIDATION_RESULT, DIRECTORY_DRAG_TYPE, DateFormatter, FormValidationContext, GregorianCalendar, ListCollection, SelectionManager, TableCollection, TimeClass, Timer, ToastQueue, TokenFieldValue, TreeCollection, VALID_VALIDITY_STATE, ZonedDateTimeClass, access, canUseDOM, createAsyncList, createAutocompleteState, createCalendar, createCalendarState, createCheckboxGroupState, createColorAreaState, createColorFieldState, createColorSliderState, createColorWheelState, createComboBoxState, createDateFieldState, createDateFieldState as useDateFieldState, createDatePickerState, createDatePickerState as useDatePickerState, createDisclosureGroupState, createDisclosureState, createDragState, createDraggableCollectionState, createDropState, createDroppableCollectionState, createFilteredListState, createFormValidationState, createGridState, createHSBColor, createHSLColor, createId, createIsSSR, createListCollection, createListData, createListState, createMenuState, createMenuTriggerState, createMenuTriggerState as useMenuTriggerState, createNumberFieldState, createOverlayTriggerState, createRGBColor, createRadioGroupState, createRangeCalendarState, createRangeCalendarState as useDateRangePickerState, createSearchFieldState, createSelectState, createSelectionState, createSelectionState as useMultipleSelectionState, createSingleSelectListState, createSliderState, createStepListState, createSubmenuTriggerState, createSubmenuTriggerState as useSubmenuTriggerState, createTabListState, createTableCollection, createTableColumnResizeState, createTableState, createTextFieldState, createTimeFieldState, createTimeFieldState as useTimeFieldState, createToastQueue, createToastState, createToggleGroupState, createToggleState, createTokenFieldState, createTooltipTriggerState, createTreeCollection, createTreeData, createTreeGridState, createTreeState, defaultContainsFilter, endOfMonth, endOfWeek, endOfYear, getColorChannels, getDayOfWeek, getItemCount, getKeysForDrag, getLocalTimeZone, getWeeksInMonth, isAccessor, isSameDay, isSameMonth, isSameYear, isServer, isToday, isWeekend, maxDate, mergeValidation, minDate, normalizeColor, now, parseAbsolute, parseAbsoluteToLocal, parseColor, parseDate, parseDateTime, parseTime, parseZonedDateTime, privateValidationStateProp, radioGroupSyncVersion, resetTooltipState, startOfMonth, startOfWeek, startOfYear, toCalendar, toCalendarDate, toCalendarDateTime, toTime, toZoned, today };
12857
13398
 
12858
13399
  //# sourceMappingURL=index.js.map