@proyecto-viviana/solid-stately 0.6.0-rc.0 → 0.6.0-rc.2

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 (134) 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 +1044 -478
  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/textfield/createTextFieldState.d.ts.map +1 -1
  72. package/dist/toggle/createToggleGroupState.d.ts +0 -8
  73. package/dist/toggle/createToggleGroupState.d.ts.map +1 -1
  74. package/dist/toggle/createToggleState.d.ts.map +1 -1
  75. package/dist/tokenfield/TokenFieldValue.d.ts +28 -4
  76. package/dist/tokenfield/TokenFieldValue.d.ts.map +1 -1
  77. package/dist/tokenfield/index.d.ts +1 -1
  78. package/dist/tokenfield/index.d.ts.map +1 -1
  79. package/dist/tree/createTreeState.d.ts +2 -2
  80. package/dist/tree/createTreeState.d.ts.map +1 -1
  81. package/dist/utils/index.d.ts +1 -1
  82. package/dist/utils/index.d.ts.map +1 -1
  83. package/dist/utils/reactivity.d.ts +5 -0
  84. package/dist/utils/reactivity.d.ts.map +1 -1
  85. package/package.json +2 -2
  86. package/src/calendar/createCalendarState.ts +139 -58
  87. package/src/calendar/createDatePickerState.ts +14 -11
  88. package/src/calendar/createRangeCalendarState.ts +43 -17
  89. package/src/calendar/createTimeFieldState.ts +1 -1
  90. package/src/checkbox/createCheckboxGroupState.ts +16 -0
  91. package/src/collections/createListState.ts +4 -4
  92. package/src/collections/createMenuState.ts +134 -4
  93. package/src/collections/createSelectionState.ts +2 -2
  94. package/src/collections/index.ts +4 -0
  95. package/src/color/createColorAreaState.ts +19 -0
  96. package/src/color/createColorFieldState.ts +76 -26
  97. package/src/color/createColorSliderState.ts +25 -2
  98. package/src/color/createColorWheelState.ts +9 -5
  99. package/src/combobox/createComboBoxState.ts +371 -121
  100. package/src/combobox/index.ts +4 -0
  101. package/src/data/createAsyncList.ts +19 -4
  102. package/src/data/createListData.ts +8 -2
  103. package/src/data/createTreeData.ts +11 -3
  104. package/src/dnd/createDraggableCollectionState.ts +8 -1
  105. package/src/dnd/createDroppableCollectionState.ts +10 -0
  106. package/src/form/createFormValidationState.ts +23 -5
  107. package/src/grid/createGridState.ts +15 -8
  108. package/src/index.ts +18 -2
  109. package/src/menu/index.ts +5 -0
  110. package/src/numberfield/createNumberFieldState.ts +97 -35
  111. package/src/radio/createRadioGroupState.ts +4 -1
  112. package/src/select/createSelectState.ts +253 -131
  113. package/src/select/index.ts +8 -1
  114. package/src/selection/SelectionManager.ts +3 -1
  115. package/src/selection/createMultipleSelectionState.ts +2 -2
  116. package/src/slider/createSliderState.ts +227 -103
  117. package/src/ssr/index.ts +11 -4
  118. package/src/steplist/createStepListState.ts +156 -123
  119. package/src/table/createTableColumnResizeState.ts +51 -11
  120. package/src/table/createTableState.ts +5 -4
  121. package/src/table/createTreeGridState.ts +3 -4
  122. package/src/table/types.ts +2 -2
  123. package/src/tabs/createTabListState.ts +8 -31
  124. package/src/textfield/createTextFieldState.ts +6 -3
  125. package/src/toggle/createToggleGroupState.ts +4 -2
  126. package/src/toggle/createToggleState.ts +5 -4
  127. package/src/tokenfield/TokenFieldValue.ts +75 -16
  128. package/src/tokenfield/index.ts +1 -0
  129. package/src/tree/createTreeState.ts +15 -8
  130. package/src/utils/index.ts +1 -0
  131. package/src/utils/reactivity.ts +8 -0
  132. package/dist/flags/flags.d.ts.map +0 -1
  133. /package/dist/{flags → private/flags}/flags.d.ts +0 -0
  134. /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`.
@@ -737,12 +756,19 @@ function useContextOptional(context) {
737
756
  //#endregion
738
757
  //#region src/toggle/createToggleState.ts
739
758
  /**
759
+ * Toggle state for Solid Stately
760
+ *
761
+ * Provides state management for toggle components like checkboxes and switches.
762
+ *
763
+ * This is a 1:1 port of @react-stately/toggle's useToggleState.
764
+ */
765
+ /**
740
766
  * Provides state management for toggle components like checkboxes and switches.
741
767
  */
742
768
  function createToggleState(props = {}) {
743
769
  const getProps = () => access(props);
744
- const initialProps = getProps();
745
- const initialSelected = initialProps.isSelected ?? initialProps.defaultSelected ?? false;
770
+ const initialDefaultSelected = untrack(() => getProps().defaultSelected);
771
+ const initialSelected = untrack(() => getProps().isSelected) ?? initialDefaultSelected ?? false;
746
772
  const [internalSelected, setInternalSelected] = createInternalSignal(initialSelected);
747
773
  const isControlled = () => getProps().isSelected !== void 0;
748
774
  const isSelected = () => {
@@ -762,13 +788,21 @@ function createToggleState(props = {}) {
762
788
  }
763
789
  return {
764
790
  isSelected,
765
- defaultSelected: initialProps.defaultSelected ?? initialSelected,
791
+ defaultSelected: initialDefaultSelected ?? initialSelected,
766
792
  setSelected,
767
793
  toggle
768
794
  };
769
795
  }
770
796
  //#endregion
771
797
  //#region src/toggle/createToggleGroupState.ts
798
+ /**
799
+ * Toggle group state for Solid Stately
800
+ *
801
+ * Provides state management for groups of toggle buttons with
802
+ * single or multiple selection.
803
+ *
804
+ * This is a port of @react-stately/toggle's useToggleGroupState.
805
+ */
772
806
  function toKeySet(keys) {
773
807
  return new Set(keys ? Array.from(keys) : []);
774
808
  }
@@ -777,7 +811,7 @@ function toKeySet(keys) {
777
811
  */
778
812
  function createToggleGroupState(props = {}) {
779
813
  const getProps = () => access(props);
780
- const [internalSelectedKeys, setInternalSelectedKeys] = createInternalSignal(toKeySet(getProps().defaultSelectedKeys));
814
+ const [internalSelectedKeys, setInternalSelectedKeys] = createInternalSignal(toKeySet(untrack(() => getProps().defaultSelectedKeys)));
781
815
  const isControlled = () => getProps().selectedKeys !== void 0;
782
816
  const selectedKeys = () => isControlled() ? toKeySet(getProps().selectedKeys) : internalSelectedKeys();
783
817
  function setSelectedKeys(keys) {
@@ -862,13 +896,23 @@ function createAutocompleteState(props = {}) {
862
896
  //#endregion
863
897
  //#region src/textfield/createTextFieldState.ts
864
898
  /**
899
+ * TextField state for Solid Stately
900
+ *
901
+ * Provides state management for text input components.
902
+ *
903
+ * This is a port of @react-stately/utils's useControlledState pattern
904
+ * as used by @react-aria/textfield.
905
+ */
906
+ /**
865
907
  * Provides state management for text input components.
866
908
  * Supports both controlled and uncontrolled modes.
867
909
  */
868
910
  function createTextFieldState(props = {}) {
869
911
  const getProps = () => access(props);
870
- const initialProps = getProps();
871
- const [internalValue, setInternalValue] = createInternalSignal(initialProps.value ?? initialProps.defaultValue ?? "");
912
+ const [internalValue, setInternalValue] = createInternalSignal(untrack(() => {
913
+ const initialProps = getProps();
914
+ return initialProps.value ?? initialProps.defaultValue ?? "";
915
+ }));
872
916
  const isControlled = () => getProps().value !== void 0;
873
917
  const value = () => {
874
918
  const p = getProps();
@@ -886,6 +930,42 @@ function createTextFieldState(props = {}) {
886
930
  }
887
931
  //#endregion
888
932
  //#region src/tokenfield/TokenFieldValue.ts
933
+ /** Represents a text selection in a TokenField. */
934
+ var SelectedRange = class {
935
+ /** The anchor position. */
936
+ anchor;
937
+ /** The current (i.e. caret) position. */
938
+ current;
939
+ /**
940
+ * Creates a new selection range. If only a single position is provided, the selection is
941
+ * collapsed.
942
+ */
943
+ constructor(anchor, current = anchor) {
944
+ this.anchor = anchor;
945
+ this.current = current;
946
+ }
947
+ /** Whether the selection is collapsed to a single caret position. */
948
+ get isCollapsed() {
949
+ return this.anchor.index === this.current.index && this.anchor.offset === this.current.offset;
950
+ }
951
+ /** The side of the selection closest to the start of the value. */
952
+ get start() {
953
+ return compare(this.anchor, this.current) < 0 ? this.anchor : this.current;
954
+ }
955
+ /** The side of the selection closest to the end of the value. */
956
+ get end() {
957
+ return compare(this.anchor, this.current) < 0 ? this.current : this.anchor;
958
+ }
959
+ /** Returns whether this selection is equal to another. */
960
+ isEqual(other) {
961
+ if (this === other) return true;
962
+ 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;
963
+ }
964
+ };
965
+ function compare(a, b) {
966
+ if (a.index === b.index) return a.offset - b.offset;
967
+ return a.index - b.index;
968
+ }
889
969
  var Direction = /* @__PURE__ */ function(Direction) {
890
970
  Direction[Direction["Forward"] = 1] = "Forward";
891
971
  Direction[Direction["Backward"] = -1] = "Backward";
@@ -896,38 +976,43 @@ var Direction = /* @__PURE__ */ function(Direction) {
896
976
  */
897
977
  var TokenFieldValue = class {
898
978
  static Direction = Direction;
979
+ static SelectedRange = SelectedRange;
899
980
  /** The text and token segments in the list. */
900
981
  segments;
901
- /** The caret position. */
902
- caretPosition = {
903
- index: 0,
904
- offset: 0
905
- };
982
+ /** The selected range. */
983
+ selectedRange;
906
984
  previous = null;
907
985
  next = null;
908
986
  isCoalescing = true;
909
987
  /** Create a new list with the given segments. */
910
988
  constructor(tokens, options) {
911
989
  this.segments = tokens;
912
- this.caretPosition = options?.caretPosition ?? {
990
+ this.selectedRange = options?.selectedRange ?? new SelectedRange({
913
991
  index: 0,
914
992
  offset: 0
915
- };
993
+ });
994
+ }
995
+ get caretPosition() {
996
+ return this.selectedRange.current;
916
997
  }
917
998
  createFieldValue(segments) {
918
999
  const Constructor = this.constructor;
919
1000
  return new Constructor(segments);
920
1001
  }
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;
1002
+ /** Create a new list with the selected range set to the given range. */
1003
+ withSelectedRange(selectedRange) {
1004
+ if (this.selectedRange.isEqual(selectedRange)) return this;
924
1005
  let result = this.createFieldValue(this.segments);
925
- result.caretPosition = caretPosition;
1006
+ result.selectedRange = selectedRange;
926
1007
  result.previous = this.previous;
927
1008
  result.next = this.next;
928
1009
  result.isCoalescing = this.isCoalescing;
929
1010
  return result;
930
1011
  }
1012
+ /** Create a new list with the caret position set to the given position. */
1013
+ withCaretPosition(position) {
1014
+ return this.withSelectedRange(new SelectedRange(position));
1015
+ }
931
1016
  splitSegment(segment, offset) {
932
1017
  if (!segment) {
933
1018
  let empty = this.createTextSegment("");
@@ -983,14 +1068,14 @@ var TokenFieldValue = class {
983
1068
  if (endSplit) appendSegments(newSegments, [endSplit]);
984
1069
  appendSegments(newSegments, this.segments.slice(end.index + 1));
985
1070
  let segments = this.createFieldValue(newSegments);
986
- segments.caretPosition = caret;
1071
+ segments.selectedRange = new SelectedRange(caret);
987
1072
  segments.isCoalescing = coalesce;
988
1073
  if (this.isCoalescing && coalesce && this.previous) {
989
1074
  segments.previous = this.previous;
990
1075
  segments.previous.next = segments;
991
1076
  } else {
992
1077
  segments.previous = this;
993
- this.caretPosition = end;
1078
+ this.selectedRange = new SelectedRange(start, end);
994
1079
  this.next = segments;
995
1080
  }
996
1081
  return segments;
@@ -1060,15 +1145,14 @@ var TokenFieldValue = class {
1060
1145
  delete(position, segmenter, direction, coalesce = true) {
1061
1146
  let boundary = this.findBoundaryWithSegmenter(position, segmenter, direction);
1062
1147
  if (boundary) return this.replaceRange(direction === -1 ? boundary : position, direction === -1 ? position : boundary, "", coalesce);
1063
- this.caretPosition = position;
1064
- return this;
1148
+ return this.withSelectedRange(new SelectedRange(position));
1065
1149
  }
1066
1150
  /** Delete text to the next or previous line break. */
1067
1151
  deleteLine(position, direction, coalesce = true) {
1068
1152
  if (this.segments.length === 0) return this;
1069
1153
  let boundary = this.findLineBoundary(position, direction);
1070
1154
  if (boundary) return this.replaceRange(direction === -1 ? boundary : position, direction === -1 ? position : boundary, "", coalesce);
1071
- return this;
1155
+ return this.withSelectedRange(new SelectedRange(position));
1072
1156
  }
1073
1157
  /** Create a new list containing a subset of the segments. */
1074
1158
  slice(start, end) {
@@ -1213,6 +1297,10 @@ const DEFAULT_VALIDATION_RESULT = {
1213
1297
  };
1214
1298
  /** Context for server-side validation errors. */
1215
1299
  const FormValidationContext = createContext({});
1300
+ const validationErrorsSource = Symbol.for("proyecto-viviana.validationErrorsSource");
1301
+ function validationErrorsIdentity(errors) {
1302
+ return errors[validationErrorsSource] ?? errors;
1303
+ }
1216
1304
  /** Private prop key for passing validation state to children. */
1217
1305
  const privateValidationStateProp = "__formValidationState" + Date.now();
1218
1306
  function asArray(v) {
@@ -1282,10 +1370,10 @@ function createFormValidationState(props) {
1282
1370
  if (name) return Array.isArray(name) ? name.flatMap((n) => asArray(serverErrors[n])) : asArray(serverErrors[name]);
1283
1371
  return [];
1284
1372
  });
1285
- const [lastServerErrors, setLastServerErrors] = createInternalSignal(serverErrors);
1373
+ const [lastServerErrors, setLastServerErrors] = createInternalSignal(validationErrorsIdentity(serverErrors));
1286
1374
  const [isServerErrorCleared, setServerErrorCleared] = createInternalSignal(false);
1287
1375
  createEffect(() => ({
1288
- errors: serverErrors,
1376
+ errors: validationErrorsIdentity(serverErrors),
1289
1377
  last: lastServerErrors()
1290
1378
  }), ({ errors, last }) => {
1291
1379
  if (errors !== last) {
@@ -1316,7 +1404,7 @@ function createFormValidationState(props) {
1316
1404
  return controlledError() || serverError() || clientError() || builtinValidation() || currentValidity();
1317
1405
  }),
1318
1406
  updateValidation(value) {
1319
- if (validationBehavior() === "aria" && !isEqualValidation(currentValidity(), value)) setCurrentValidity(value);
1407
+ if (untrack(validationBehavior) === "aria" && !isEqualValidation(untrack(currentValidity), value)) setCurrentValidity(value);
1320
1408
  else nextValidation = value;
1321
1409
  },
1322
1410
  resetValidation() {
@@ -1433,6 +1521,12 @@ function createCheckboxGroupState(props = {}) {
1433
1521
  const snapshot = liveValue();
1434
1522
  if (snapshot.includes(removeVal)) setValue(snapshot.filter((v) => v !== removeVal));
1435
1523
  }
1524
+ const invalidValues = /* @__PURE__ */ new Map();
1525
+ function setInvalid(itemValue, result) {
1526
+ if (result.isInvalid) invalidValues.set(itemValue, result);
1527
+ else invalidValues.delete(itemValue);
1528
+ validation.updateValidation(mergeValidation(...invalidValues.values()));
1529
+ }
1436
1530
  function toggleValue(toggleVal) {
1437
1531
  const p = getProps();
1438
1532
  if (p.isReadOnly || p.isDisabled) return;
@@ -1458,6 +1552,7 @@ function createCheckboxGroupState(props = {}) {
1458
1552
  addValue,
1459
1553
  removeValue,
1460
1554
  toggleValue,
1555
+ setInvalid,
1461
1556
  realtimeValidation: validation.realtimeValidation,
1462
1557
  displayValidation: validation.displayValidation,
1463
1558
  updateValidation: validation.updateValidation,
@@ -1494,9 +1589,14 @@ function createIsSSR() {
1494
1589
  *
1495
1590
  * @param defaultId - Optional default ID to use instead of generating one.
1496
1591
  */
1592
+ let fallbackIdCounter = 0;
1497
1593
  function createId(defaultId) {
1498
- const uniqueId = createUniqueId();
1499
1594
  if (defaultId) return defaultId;
1595
+ let uniqueId;
1596
+ try {
1597
+ uniqueId = createUniqueId();
1598
+ } catch {}
1599
+ if (!uniqueId) uniqueId = `nh-${++fallbackIdCounter}`;
1500
1600
  return `solid-stately-${uniqueId}`;
1501
1601
  }
1502
1602
  /**
@@ -1578,7 +1678,7 @@ function createRadioGroupState(props = {}) {
1578
1678
  const state = {
1579
1679
  name,
1580
1680
  selectedValue,
1581
- defaultSelectedValue: initialProps.defaultValue ?? null,
1681
+ defaultSelectedValue: initialProps.value !== void 0 ? initialProps.value : initialProps.defaultValue ?? null,
1582
1682
  setSelectedValue,
1583
1683
  lastFocusedValue,
1584
1684
  setLastFocusedValue,
@@ -1831,7 +1931,7 @@ function getAncestors(collection, node) {
1831
1931
  * Creates selection state for a collection.
1832
1932
  */
1833
1933
  function createSelectionState(props = {}) {
1834
- const getProps = () => access(props);
1934
+ const getProps = propsAccessor(props);
1835
1935
  const [internalBehavior, setInternalBehavior] = createInternalSignal("toggle");
1836
1936
  const [internalSelectedKeys, setInternalSelectedKeys] = createInternalSignal(getInitialSelection$2(getProps().defaultSelectedKeys));
1837
1937
  const [anchorKey, setAnchorKey] = createInternalSignal(null);
@@ -2031,7 +2131,7 @@ function convertSelection(selection, defaultValue) {
2031
2131
  * Manages raw state for multiple selection and focus in a collection.
2032
2132
  */
2033
2133
  function createMultipleSelectionState(props = {}) {
2034
- const getProps = () => access(props);
2134
+ const getProps = propsAccessor(props);
2035
2135
  const [isFocused, setIsFocused] = createInternalSignal(false);
2036
2136
  const [focusedKey, setFocusedKeySignal] = createInternalSignal(null);
2037
2137
  const [childFocusStrategy, setChildFocusStrategy] = createInternalSignal(null);
@@ -2403,7 +2503,7 @@ var SelectionManager = class SelectionManager {
2403
2503
  }
2404
2504
  isDisabled(key) {
2405
2505
  const item = this.collection.getItem(key);
2406
- return this.state.disabledBehavior === "all" && (this.state.disabledKeys.has(key) || !!item?.props?.isDisabled) && item?.props?.disabledBehavior !== "selection";
2506
+ return this.state.disabledBehavior === "all" && (this.state.disabledKeys.has(key) || !!item?.props?.isDisabled || item?.isDisabled === true) && item?.props?.disabledBehavior !== "selection";
2407
2507
  }
2408
2508
  isLink(key) {
2409
2509
  return !!this.collection.getItem(key)?.props?.href;
@@ -2445,7 +2545,7 @@ var SelectionManager = class SelectionManager {
2445
2545
  * Creates state for a list component with selection.
2446
2546
  */
2447
2547
  function createListState(props) {
2448
- const getProps = () => access(props);
2548
+ const getProps = propsAccessor(props);
2449
2549
  const collection = createMemo(() => {
2450
2550
  const p = getProps();
2451
2551
  return new ListCollection((p.items ?? []).map((item, index) => {
@@ -2606,7 +2706,7 @@ function createFilteredListState(state, filterFn) {
2606
2706
  * Creates state for a single-select list component.
2607
2707
  */
2608
2708
  function createSingleSelectListState(props) {
2609
- const getProps = () => access(props);
2709
+ const getProps = propsAccessor(props);
2610
2710
  const listState = createListState({
2611
2711
  get items() {
2612
2712
  return getProps().items;
@@ -2631,7 +2731,7 @@ function createSingleSelectListState(props) {
2631
2731
  allowDuplicateSelectionEvents: true,
2632
2732
  get selectedKeys() {
2633
2733
  const key = getProps().selectedKey;
2634
- return key != null ? [key] : [];
2734
+ return key !== void 0 ? key != null ? [key] : [] : void 0;
2635
2735
  },
2636
2736
  get defaultSelectedKeys() {
2637
2737
  const key = getProps().defaultSelectedKey;
@@ -2671,6 +2771,8 @@ function createSingleSelectListState(props) {
2671
2771
  *
2672
2772
  * createMenuTriggerState is ported from
2673
2773
  * packages/react-stately/src/menu/useMenuTriggerState.ts.
2774
+ * createSubmenuTriggerState is ported from
2775
+ * packages/react-stately/src/menu/useSubmenuTriggerState.ts.
2674
2776
  * createMenuState is a local composition of the separately attributed list state.
2675
2777
  */
2676
2778
  /**
@@ -2678,7 +2780,7 @@ function createSingleSelectListState(props) {
2678
2780
  * Menus are single-select lists that support actions.
2679
2781
  */
2680
2782
  function createMenuState(props) {
2681
- const getProps = () => access(props);
2783
+ const getProps = propsAccessor(props);
2682
2784
  const listState = createListState({
2683
2785
  get items() {
2684
2786
  return getProps().items;
@@ -2733,11 +2835,29 @@ function createMenuState(props) {
2733
2835
  }
2734
2836
  /**
2735
2837
  * Creates state for a menu trigger. Mirrors `@react-stately/menu` `useMenuTriggerState`:
2736
- * overlay open state plus the focus strategy passed through to `createMenu`.
2838
+ * overlay open state, the focus strategy passed through to `createMenu`, and the
2839
+ * expanded submenu stack.
2737
2840
  */
2738
2841
  function createMenuTriggerState(props = {}) {
2739
2842
  const overlay = createOverlayTriggerState(props);
2740
2843
  const [focusStrategy, setFocusStrategy] = createInternalSignal(null);
2844
+ const [expandedKeysStack, setExpandedKeysStack] = createInternalSignal([]);
2845
+ const openSubmenu = (triggerKey, level) => {
2846
+ setExpandedKeysStack((oldStack) => {
2847
+ if (level > oldStack.length) return oldStack;
2848
+ return [...oldStack.slice(0, level), triggerKey];
2849
+ });
2850
+ };
2851
+ const closeSubmenu = (triggerKey, level) => {
2852
+ setExpandedKeysStack((oldStack) => {
2853
+ if (oldStack[level] !== triggerKey) return oldStack;
2854
+ return oldStack.slice(0, level);
2855
+ });
2856
+ };
2857
+ const close = () => {
2858
+ setExpandedKeysStack([]);
2859
+ overlay.close();
2860
+ };
2741
2861
  return {
2742
2862
  ...overlay,
2743
2863
  focusStrategy,
@@ -2749,7 +2869,46 @@ function createMenuTriggerState(props = {}) {
2749
2869
  toggle(strategy = null) {
2750
2870
  setFocusStrategy(strategy);
2751
2871
  overlay.toggle();
2752
- }
2872
+ },
2873
+ close,
2874
+ expandedKeysStack,
2875
+ openSubmenu,
2876
+ closeSubmenu
2877
+ };
2878
+ }
2879
+ /**
2880
+ * Creates state for a submenu trigger. Mirrors `@react-stately/menu`
2881
+ * `useSubmenuTriggerState`: open state is the root stack entry at this level.
2882
+ */
2883
+ function createSubmenuTriggerState(props, state) {
2884
+ const getProps = propsAccessor(props);
2885
+ const submenuLevel = state.expandedKeysStack().length;
2886
+ const [focusStrategy, setFocusStrategy] = createInternalSignal(null);
2887
+ const isOpen = () => state.expandedKeysStack()[submenuLevel] === getProps().triggerKey;
2888
+ const open = (strategy) => {
2889
+ setFocusStrategy(strategy ?? null);
2890
+ state.openSubmenu(getProps().triggerKey, submenuLevel);
2891
+ };
2892
+ const close = () => {
2893
+ setFocusStrategy(null);
2894
+ state.closeSubmenu(getProps().triggerKey, submenuLevel);
2895
+ };
2896
+ const toggle = (strategy) => {
2897
+ setFocusStrategy(strategy ?? null);
2898
+ if (isOpen()) close();
2899
+ else open(strategy);
2900
+ };
2901
+ return {
2902
+ focusStrategy,
2903
+ isOpen,
2904
+ open,
2905
+ close,
2906
+ closeAll: () => state.close(),
2907
+ submenuLevel,
2908
+ setOpen() {},
2909
+ toggle,
2910
+ point: null,
2911
+ setPoint() {}
2753
2912
  };
2754
2913
  }
2755
2914
  //#endregion
@@ -2773,18 +2932,22 @@ const dayOfWeekNames$1 = [
2773
2932
  * Provides state management for a calendar component.
2774
2933
  */
2775
2934
  function createCalendarState(props = {}) {
2935
+ const getProps = propsAccessor(props);
2776
2936
  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");
2937
+ const locale = createMemo(() => access(getProps().locale) ?? "en-US");
2938
+ const minValueState = createMemo(() => access(getProps().minValue) ?? null);
2939
+ const maxValueState = createMemo(() => access(getProps().maxValue) ?? null);
2940
+ const selectionMode = createMemo(() => access(getProps().selectionMode) ?? "single");
2781
2941
  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];
2942
+ const calendar = createMemo(() => (getProps().createCalendar ?? createCalendar$1)(resolvedOptions().calendar));
2943
+ const visibleMonths = createMemo(() => Math.max(1, Number(access(getProps().visibleMonths) ?? 1)));
2944
+ const firstDayOfWeekName = () => {
2945
+ const firstDayOfWeek = getProps().firstDayOfWeek;
2946
+ return firstDayOfWeek == null ? void 0 : dayOfWeekNames$1[firstDayOfWeek];
2947
+ };
2785
2948
  const toDisplayCalendarDate = (date) => toCalendar$1(toCalendarDate$3(date), calendar());
2786
2949
  const selectionAlignmentOffset = () => {
2787
- switch (props.selectionAlignment) {
2950
+ switch (getProps().selectionAlignment) {
2788
2951
  case "start": return 0;
2789
2952
  case "end": return Math.max(visibleMonths() - 1, 0);
2790
2953
  default: {
@@ -2794,9 +2957,26 @@ function createCalendarState(props = {}) {
2794
2957
  }
2795
2958
  }
2796
2959
  };
2960
+ const rawAlignStart = (date) => startOfMonth$1(date);
2961
+ const rawAlignEnd = (date, months) => startOfMonth$1(date).subtract({ months: Math.max(months - 1, 0) });
2962
+ const constrainVisibleRangeStart = (date, aligned, months) => {
2963
+ const minValue = access(getProps().minValue);
2964
+ const maxValue = access(getProps().maxValue);
2965
+ let constrained = aligned;
2966
+ if (minValue && date.compare(toDisplayCalendarDate(minValue)) >= 0) {
2967
+ const minStart = rawAlignStart(toDisplayCalendarDate(minValue));
2968
+ if (minStart.compare(constrained) > 0) constrained = minStart;
2969
+ }
2970
+ if (maxValue && date.compare(toDisplayCalendarDate(maxValue)) <= 0) {
2971
+ const maxStart = rawAlignEnd(toDisplayCalendarDate(maxValue), months);
2972
+ if (maxStart.compare(constrained) < 0) constrained = maxStart;
2973
+ }
2974
+ return constrained;
2975
+ };
2797
2976
  const alignVisibleRangeStart = (date) => {
2798
2977
  const offset = selectionAlignmentOffset();
2799
- return startOfMonth$1(offset > 0 ? date.subtract({ months: offset }) : date);
2978
+ const aligned = rawAlignStart(offset > 0 ? date.subtract({ months: offset }) : date);
2979
+ return constrainVisibleRangeStart(date, aligned, visibleMonths());
2800
2980
  };
2801
2981
  const visibleRangeEnd = (start) => {
2802
2982
  let end = endOfMonth$1(start);
@@ -2804,32 +2984,37 @@ function createCalendarState(props = {}) {
2804
2984
  return end;
2805
2985
  };
2806
2986
  const constrainDate = (date) => {
2807
- const minValue = access(props.minValue);
2808
- const maxValue = access(props.maxValue);
2987
+ const minValue = access(getProps().minValue);
2988
+ const maxValue = access(getProps().maxValue);
2809
2989
  let constrained = date;
2810
2990
  if (minValue && constrained.compare(toDisplayCalendarDate(minValue)) < 0) constrained = toDisplayCalendarDate(minValue);
2811
2991
  if (maxValue && constrained.compare(toDisplayCalendarDate(maxValue)) > 0) constrained = toDisplayCalendarDate(maxValue);
2812
2992
  return constrained;
2813
2993
  };
2814
2994
  const getInitialFocusedDate = () => {
2815
- const controlledFocused = access(props.focusedValue);
2995
+ const controlledFocused = access(getProps().focusedValue);
2816
2996
  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);
2997
+ const defaultFocusedValue = getProps().defaultFocusedValue;
2998
+ if (defaultFocusedValue) return toDisplayCalendarDate(defaultFocusedValue);
2999
+ const controlledValue = access(getProps().value);
3000
+ if (controlledValue !== void 0) {
3001
+ const controlledFirst = Array.isArray(controlledValue) ? controlledValue[0] : controlledValue;
3002
+ if (controlledFirst) return toDisplayCalendarDate(controlledFirst);
3003
+ } else {
3004
+ const defaultValue = getProps().defaultValue;
3005
+ const defaultFirst = Array.isArray(defaultValue) ? defaultValue[0] : defaultValue;
3006
+ if (defaultFirst) return toDisplayCalendarDate(defaultFirst);
3007
+ }
2823
3008
  return toCalendar$1(today$1(timeZone), calendar());
2824
3009
  };
2825
3010
  const initialFocusedDate = constrainDate(getInitialFocusedDate());
2826
- const [internalValue, setInternalValue] = createInternalSignal(props.defaultValue ?? null);
3011
+ const [internalValue, setInternalValue] = createInternalSignal(getProps().defaultValue ?? null);
2827
3012
  const [focusedDate, setFocusedDateInternal] = createInternalSignal(initialFocusedDate);
2828
3013
  const [visibleRangeStart, setVisibleRangeStart] = createInternalSignal(alignVisibleRangeStart(initialFocusedDate));
2829
- const [isFocused, setFocused] = createInternalSignal(props.autoFocus ?? false);
3014
+ const [isFocused, setFocused] = createInternalSignal(getProps().autoFocus ?? false);
2830
3015
  const [isPaginating, setIsPaginating] = createInternalSignal(false);
2831
3016
  const sourceValue = createMemo(() => {
2832
- const controlled = access(props.value);
3017
+ const controlled = access(getProps().value);
2833
3018
  return controlled !== void 0 ? controlled : internalValue();
2834
3019
  });
2835
3020
  const value = createMemo(() => {
@@ -2837,10 +3022,23 @@ function createCalendarState(props = {}) {
2837
3022
  if (Array.isArray(currentValue)) return currentValue.map((v) => toDisplayCalendarDate(v));
2838
3023
  return currentValue ? toDisplayCalendarDate(currentValue) : null;
2839
3024
  });
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");
3025
+ const isDisabled = createMemo(() => access(getProps().isDisabled) ?? false);
3026
+ const isReadOnly = createMemo(() => access(getProps().isReadOnly) ?? false);
3027
+ const validationState = createMemo(() => access(getProps().validationState));
3028
+ const isInvalidSelection = createMemo(() => {
3029
+ const current = value();
3030
+ if (current == null) return false;
3031
+ const dates = Array.isArray(current) ? current : [current];
3032
+ const min = access(getProps().minValue);
3033
+ const max = access(getProps().maxValue);
3034
+ const minCal = min != null ? toDisplayCalendarDate(min) : null;
3035
+ const maxCal = max != null ? toDisplayCalendarDate(max) : null;
3036
+ return dates.some((date) => {
3037
+ const cal = toDisplayCalendarDate(date);
3038
+ return Boolean(getProps().isDateUnavailable?.(cal)) || minCal != null && cal.compare(minCal) < 0 || maxCal != null && cal.compare(maxCal) > 0;
3039
+ });
3040
+ });
3041
+ const isValueInvalid = createMemo(() => Boolean(access(getProps().isInvalid)) || validationState() === "invalid" || isInvalidSelection());
2844
3042
  const visibleRange = () => {
2845
3043
  const start = visibleRangeStart();
2846
3044
  return {
@@ -2849,11 +3047,11 @@ function createCalendarState(props = {}) {
2849
3047
  };
2850
3048
  };
2851
3049
  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));
3050
+ if (nextFocusedDate.compare(range.start) < 0) setVisibleRangeStart(constrainVisibleRangeStart(nextFocusedDate, rawAlignEnd(nextFocusedDate, months), months));
3051
+ else if (nextFocusedDate.compare(range.end) > 0) setVisibleRangeStart(constrainVisibleRangeStart(nextFocusedDate, rawAlignStart(nextFocusedDate), months));
2854
3052
  };
2855
3053
  createEffect(() => {
2856
- const controlledFocused = access(props.focusedValue);
3054
+ const controlledFocused = access(getProps().focusedValue);
2857
3055
  if (!controlledFocused) return null;
2858
3056
  return {
2859
3057
  nextFocusedDate: constrainDate(toDisplayCalendarDate(controlledFocused)),
@@ -2868,7 +3066,7 @@ function createCalendarState(props = {}) {
2868
3066
  syncVisibleRangeForFocusedDate(nextFocusedDate, range, months);
2869
3067
  });
2870
3068
  createEffect(() => {
2871
- if (access(props.focusedValue)) return null;
3069
+ if (access(getProps().focusedValue)) return null;
2872
3070
  const currentFocusedDate = focusedDate();
2873
3071
  const nextFocusedDate = constrainDate(currentFocusedDate);
2874
3072
  if (nextFocusedDate.compare(currentFocusedDate) === 0 && isEqualCalendar(nextFocusedDate.calendar, currentFocusedDate.calendar)) return null;
@@ -2882,7 +3080,7 @@ function createCalendarState(props = {}) {
2882
3080
  if (!data) return;
2883
3081
  syncVisibleRangeForFocusedDate(data.nextFocusedDate, data.range, data.months);
2884
3082
  setFocusedDateInternal(data.nextFocusedDate);
2885
- props.onFocusChange?.(data.nextFocusedDate);
3083
+ getProps().onFocusChange?.(data.nextFocusedDate);
2886
3084
  });
2887
3085
  createEffect(() => {
2888
3086
  const currentFocusedDate = focusedDate();
@@ -2919,11 +3117,11 @@ function createCalendarState(props = {}) {
2919
3117
  });
2920
3118
  const normalizeValue = (newValue) => {
2921
3119
  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);
3120
+ const minBound = access(getProps().minValue);
3121
+ const prev = previousAvailableDate$1(constrained, minBound != null ? toDisplayCalendarDate(minBound) : minDate$1(constrained, visibleRange().start) ?? constrained, getProps().isDateUnavailable);
2924
3122
  if (!prev) return null;
2925
3123
  const oldValue = sourceValue();
2926
- return convertValue$3(prev, Array.isArray(oldValue) ? oldValue[0] ?? null : oldValue);
3124
+ return convertValue$5(prev, Array.isArray(oldValue) ? oldValue[0] ?? null : oldValue);
2927
3125
  };
2928
3126
  const setValue = (newValue) => {
2929
3127
  if (isDisabled() || isReadOnly()) return;
@@ -2936,15 +3134,15 @@ function createCalendarState(props = {}) {
2936
3134
  if (!localValue) return;
2937
3135
  nextValue = localValue;
2938
3136
  }
2939
- if (access(props.value) === void 0) setInternalValue(() => nextValue);
2940
- if (!Object.is(oldValue, nextValue) && props.onChange) props.onChange(nextValue);
3137
+ if (access(getProps().value) === void 0) setInternalValue(() => nextValue);
3138
+ if (!Object.is(oldValue, nextValue)) getProps().onChange?.(nextValue);
2941
3139
  };
2942
3140
  const setFocusedDate = (date) => {
2943
3141
  const constrained = constrainDate(date);
2944
3142
  if (Object.is(constrained, focusedDate())) return;
2945
3143
  syncVisibleRangeForFocusedDate(constrained, visibleRange(), visibleMonths());
2946
3144
  setFocusedDateInternal(constrained);
2947
- props.onFocusChange?.(constrained);
3145
+ getProps().onFocusChange?.(constrained);
2948
3146
  };
2949
3147
  const isSelected = (date) => {
2950
3148
  const v = value();
@@ -2957,13 +3155,13 @@ function createCalendarState(props = {}) {
2957
3155
  return isFocused() && isSameDay$1(toDisplayCalendarDate(date), focusedDate());
2958
3156
  };
2959
3157
  const isCellUnavailable = (date) => {
2960
- return props.isDateUnavailable?.(date) ?? false;
3158
+ return getProps().isDateUnavailable?.(date) ?? false;
2961
3159
  };
2962
3160
  const isCellDisabled = (date) => {
2963
3161
  if (isDisabled()) return true;
2964
- if (props.isDateDisabled?.(date)) return true;
2965
- const minValue = access(props.minValue);
2966
- const maxValue = access(props.maxValue);
3162
+ if (getProps().isDateDisabled?.(date)) return true;
3163
+ const minValue = access(getProps().minValue);
3164
+ const maxValue = access(getProps().maxValue);
2967
3165
  const calDate = toDisplayCalendarDate(date);
2968
3166
  const range = visibleRange();
2969
3167
  if (calDate.compare(toDisplayCalendarDate(range.start)) < 0) return true;
@@ -3000,20 +3198,24 @@ function createCalendarState(props = {}) {
3000
3198
  };
3001
3199
  const focusPreviousPage = () => {
3002
3200
  setIsPaginating(true);
3003
- const pageMonths = props.pageBehavior === "single" ? 1 : visibleMonths();
3004
- const nextFocusedDate = constrainDate(focusedDate().subtract({ months: pageMonths }));
3201
+ const pageMonths = getProps().pageBehavior === "single" ? 1 : visibleMonths();
3202
+ const currentFocused = focusedDate();
3203
+ const nextFocusedDate = constrainDate(currentFocused.subtract({ months: pageMonths }));
3204
+ const slidStart = startOfMonth$1(visibleRangeStart().subtract({ months: pageMonths }));
3005
3205
  setFocusedDateInternal(nextFocusedDate);
3006
- setVisibleRangeStart(startOfMonth$1(visibleRangeStart().subtract({ months: pageMonths })));
3007
- props.onFocusChange?.(nextFocusedDate);
3206
+ setVisibleRangeStart(constrainVisibleRangeStart(currentFocused, slidStart, pageMonths));
3207
+ getProps().onFocusChange?.(nextFocusedDate);
3008
3208
  setIsPaginating(false);
3009
3209
  };
3010
3210
  const focusNextPage = () => {
3011
3211
  setIsPaginating(true);
3012
- const pageMonths = props.pageBehavior === "single" ? 1 : visibleMonths();
3013
- const nextFocusedDate = constrainDate(focusedDate().add({ months: pageMonths }));
3212
+ const pageMonths = getProps().pageBehavior === "single" ? 1 : visibleMonths();
3213
+ const currentFocused = focusedDate();
3214
+ const nextFocusedDate = constrainDate(currentFocused.add({ months: pageMonths }));
3215
+ const slidStart = startOfMonth$1(visibleRangeStart().add({ months: pageMonths }));
3014
3216
  setFocusedDateInternal(nextFocusedDate);
3015
- setVisibleRangeStart(startOfMonth$1(visibleRangeStart().add({ months: pageMonths })));
3016
- props.onFocusChange?.(nextFocusedDate);
3217
+ setVisibleRangeStart(constrainVisibleRangeStart(currentFocused, slidStart, pageMonths));
3218
+ getProps().onFocusChange?.(nextFocusedDate);
3017
3219
  setIsPaginating(false);
3018
3220
  };
3019
3221
  const focusPreviousSection = () => {
@@ -3048,7 +3250,7 @@ function createCalendarState(props = {}) {
3048
3250
  const selectDate = (date) => {
3049
3251
  if (isReadOnly() || isDisabled()) return;
3050
3252
  if (selectionMode() === "multiple") {
3051
- const current = access(props.value) !== void 0 ? value() : readNow(internalValue);
3253
+ const current = access(getProps().value) !== void 0 ? value() : readNow(internalValue);
3052
3254
  const base = Array.isArray(current) ? current.map((v) => toDisplayCalendarDate(v)) : current != null ? [toDisplayCalendarDate(current)] : [];
3053
3255
  const target = toDisplayCalendarDate(date);
3054
3256
  const index = base.findIndex((d) => isSameDay$1(d, target));
@@ -3128,7 +3330,7 @@ function createCalendarState(props = {}) {
3128
3330
  function toCalendarDate$3(date) {
3129
3331
  return toCalendarDate$1(date);
3130
3332
  }
3131
- function convertValue$3(newValue, oldValue) {
3333
+ function convertValue$5(newValue, oldValue) {
3132
3334
  const localValue = toCalendar$1(newValue, oldValue?.calendar ?? new GregorianCalendar$1());
3133
3335
  if (oldValue && "hour" in oldValue) return oldValue.set(localValue);
3134
3336
  return localValue;
@@ -4175,7 +4377,7 @@ function createDateFieldState(props = {}) {
4175
4377
  if (access(props.value) === void 0) setInternalValue(() => v);
4176
4378
  props.onChange?.(v);
4177
4379
  };
4178
- const calendarValue = createMemo(() => convertValue$2(rawValue(), calendar()) ?? null);
4380
+ const calendarValue = createMemo(() => convertValue$4(rawValue(), calendar()) ?? null);
4179
4381
  const initialValue = untrack(calendarValue);
4180
4382
  const [displayValue, setDisplayValue] = createInternalSignal(untrack(() => new IncompleteDate(calendar(), hourCycle(), untrack(calendarValue))));
4181
4383
  let lastValue = untrack(calendarValue);
@@ -4477,13 +4679,13 @@ function getFormatOptions(fieldOptions, options) {
4477
4679
  if (options.showEra && startIdx === 0) opts.era = "short";
4478
4680
  return opts;
4479
4681
  }
4480
- function convertValue$2(value, calendar) {
4682
+ function convertValue$4(value, calendar) {
4481
4683
  if (value === null) return null;
4482
4684
  if (!value) return void 0;
4483
4685
  return toCalendar$1(value, calendar);
4484
4686
  }
4485
4687
  function createPlaceholderDate(placeholderValue, granularity, calendar, timeZone) {
4486
- if (placeholderValue) return convertValue$2(placeholderValue, calendar);
4688
+ if (placeholderValue) return convertValue$4(placeholderValue, calendar);
4487
4689
  const date = toCalendar$1(now$1(timeZone ?? getLocalTimeZone$1()).set({
4488
4690
  hour: 0,
4489
4691
  minute: 0,
@@ -4601,8 +4803,8 @@ function createRangeCalendarState(props = {}) {
4601
4803
  return end;
4602
4804
  };
4603
4805
  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) => {
4806
+ const rawAlignEnd = (date, months = visibleMonths()) => startOfMonth$1(date).subtract({ months: Math.max(months - 1, 0) });
4807
+ const constrainVisibleRangeStart = (date, aligned, months = visibleMonths()) => {
4606
4808
  const minValue = access(props.minValue);
4607
4809
  const maxValue = access(props.maxValue);
4608
4810
  let constrained = aligned;
@@ -4611,7 +4813,7 @@ function createRangeCalendarState(props = {}) {
4611
4813
  if (minStart.compare(constrained) > 0) constrained = minStart;
4612
4814
  }
4613
4815
  if (maxValue && date.compare(toDisplayCalendarDate(maxValue)) <= 0) {
4614
- const maxStart = rawAlignEnd(toDisplayCalendarDate(maxValue));
4816
+ const maxStart = rawAlignEnd(toDisplayCalendarDate(maxValue), months);
4615
4817
  if (maxStart.compare(constrained) < 0) constrained = maxStart;
4616
4818
  }
4617
4819
  return constrained;
@@ -4624,7 +4826,8 @@ function createRangeCalendarState(props = {}) {
4624
4826
  return constrainVisibleRangeStart(date, rawAlignStart(date).subtract({ months: offset }));
4625
4827
  };
4626
4828
  const defaultSelectionAlignment = () => {
4627
- const currentValue = access(props.value) ?? props.defaultValue;
4829
+ const controlled = access(props.value);
4830
+ const currentValue = controlled !== void 0 ? controlled : props.defaultValue;
4628
4831
  if (!currentValue?.start || !currentValue?.end) return "center";
4629
4832
  const start = alignCenter(toDisplayCalendarDate(currentValue.start));
4630
4833
  const end = visibleRangeEndFromStart(start);
@@ -4642,8 +4845,9 @@ function createRangeCalendarState(props = {}) {
4642
4845
  if (controlledFocused) return toDisplayCalendarDate(controlledFocused);
4643
4846
  if (props.defaultFocusedValue) return toDisplayCalendarDate(props.defaultFocusedValue);
4644
4847
  const controlledValue = access(props.value);
4645
- if (controlledValue?.start) return toDisplayCalendarDate(controlledValue.start);
4646
- if (props.defaultValue?.start) return toDisplayCalendarDate(props.defaultValue.start);
4848
+ if (controlledValue !== void 0) {
4849
+ if (controlledValue?.start) return toDisplayCalendarDate(controlledValue.start);
4850
+ } else if (props.defaultValue?.start) return toDisplayCalendarDate(props.defaultValue.start);
4647
4851
  return toCalendar$1(today$1(timeZone), calendar());
4648
4852
  };
4649
4853
  const [internalValue, setInternalValue] = createInternalSignal(props.defaultValue ?? null);
@@ -4677,7 +4881,7 @@ function createRangeCalendarState(props = {}) {
4677
4881
  const maxCal = max != null ? toDisplayCalendarDate(max) : null;
4678
4882
  return isDateOutsideRange(toDisplayCalendarDate(v.start), minCal, maxCal) || isDateOutsideRange(toDisplayCalendarDate(v.end), minCal, maxCal);
4679
4883
  });
4680
- const isValueInvalid = createMemo(() => validationState() === "invalid" || isInvalidSelection());
4884
+ const isValueInvalid = createMemo(() => Boolean(access(props.isInvalid)) || validationState() === "invalid" || isInvalidSelection());
4681
4885
  const highlightedRange = createMemo(() => {
4682
4886
  const anchor = anchorDate();
4683
4887
  if (!anchor) {
@@ -4778,8 +4982,8 @@ function createRangeCalendarState(props = {}) {
4778
4982
  if (isDisabled() || isReadOnly()) return;
4779
4983
  const oldValue = sourceValue();
4780
4984
  const nextValue = newValue ? {
4781
- start: convertValue$1(constrainDate(toDisplayCalendarDate(newValue.start)), oldValue?.start),
4782
- end: convertValue$1(constrainDate(toDisplayCalendarDate(newValue.end)), oldValue?.end)
4985
+ start: convertValue$3(constrainDate(toDisplayCalendarDate(newValue.start)), oldValue?.start),
4986
+ end: convertValue$3(constrainDate(toDisplayCalendarDate(newValue.end)), oldValue?.end)
4783
4987
  } : null;
4784
4988
  if (access(props.value) === void 0) setInternalValue(() => nextValue);
4785
4989
  if (!Object.is(oldValue, nextValue) && props.onChange) props.onChange(nextValue);
@@ -4844,16 +5048,20 @@ function createRangeCalendarState(props = {}) {
4844
5048
  };
4845
5049
  const focusPreviousPage = () => {
4846
5050
  const pageMonths = access(props.pageBehavior) === "single" ? 1 : visibleMonths();
4847
- const nextFocusedDate = constrainDate(focusedDate().subtract({ months: pageMonths }));
5051
+ const currentFocused = focusedDate();
5052
+ const nextFocusedDate = constrainDate(currentFocused.subtract({ months: pageMonths }));
5053
+ const slidStart = startOfMonth$1(visibleRangeStart().subtract({ months: pageMonths }));
4848
5054
  setFocusedDateInternal(nextFocusedDate);
4849
- setVisibleRangeStart(startOfMonth$1(visibleRangeStart().subtract({ months: pageMonths })));
5055
+ setVisibleRangeStart(constrainVisibleRangeStart(currentFocused, slidStart, pageMonths));
4850
5056
  props.onFocusChange?.(nextFocusedDate);
4851
5057
  };
4852
5058
  const focusNextPage = () => {
4853
5059
  const pageMonths = access(props.pageBehavior) === "single" ? 1 : visibleMonths();
4854
- const nextFocusedDate = constrainDate(focusedDate().add({ months: pageMonths }));
5060
+ const currentFocused = focusedDate();
5061
+ const nextFocusedDate = constrainDate(currentFocused.add({ months: pageMonths }));
5062
+ const slidStart = startOfMonth$1(visibleRangeStart().add({ months: pageMonths }));
4855
5063
  setFocusedDateInternal(nextFocusedDate);
4856
- setVisibleRangeStart(startOfMonth$1(visibleRangeStart().add({ months: pageMonths })));
5064
+ setVisibleRangeStart(constrainVisibleRangeStart(currentFocused, slidStart, pageMonths));
4857
5065
  props.onFocusChange?.(nextFocusedDate);
4858
5066
  };
4859
5067
  const focusPreviousSection = () => {
@@ -4936,7 +5144,14 @@ function createRangeCalendarState(props = {}) {
4936
5144
  return week;
4937
5145
  };
4938
5146
  const focusNearestAvailableDate = (anchor) => {
4939
- const isDateInvalid = (candidate) => isInvalid(candidate) || isDateOutsideAllowedRange(candidate);
5147
+ const passedRange = getAvailableRange(anchor);
5148
+ const isDateInvalid = (candidate) => {
5149
+ if (isDateOutsideAllowedRange(candidate)) return true;
5150
+ const calDate = toDisplayCalendarDate(candidate);
5151
+ if (passedRange?.start && calDate.compare(passedRange.start) < 0) return true;
5152
+ if (passedRange?.end && calDate.compare(passedRange.end) > 0) return true;
5153
+ return false;
5154
+ };
4940
5155
  let nextDay = anchor.add({ days: 1 });
4941
5156
  if (isDateInvalid(nextDay)) nextDay = anchor.subtract({ days: 1 });
4942
5157
  if (!isDateInvalid(nextDay)) {
@@ -5058,7 +5273,7 @@ function createRangeCalendarState(props = {}) {
5058
5273
  function toCalendarDate$2(date) {
5059
5274
  return toCalendarDate$1(date);
5060
5275
  }
5061
- function convertValue$1(newValue, oldValue) {
5276
+ function convertValue$3(newValue, oldValue) {
5062
5277
  const localValue = toCalendar$1(newValue, oldValue?.calendar ?? new GregorianCalendar$1());
5063
5278
  if (oldValue && "hour" in oldValue) return oldValue.set(localValue);
5064
5279
  return localValue;
@@ -5122,7 +5337,7 @@ function nextUnavailableDate(anchorDate, isDateUnavailable, visibleMonths, dir)
5122
5337
  * that already carry a date (`CalendarDateTime`/`ZonedDateTime`) pass through.
5123
5338
  * Mirrors `@react-stately/datepicker`'s `convertValue`.
5124
5339
  */
5125
- function convertValue(value, date = today$1(getLocalTimeZone$1())) {
5340
+ function convertValue$2(value, date = today$1(getLocalTimeZone$1())) {
5126
5341
  if (!value) return null;
5127
5342
  if ("day" in value) return value;
5128
5343
  return toCalendarDateTime$1(date, value);
@@ -5146,20 +5361,20 @@ function createTimeFieldState(props = {}) {
5146
5361
  const placeholderDate = createMemo(() => {
5147
5362
  const current = v();
5148
5363
  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);
5364
+ if (zone && placeholderValue) return toZoned$1(convertValue$2(placeholderValue), zone);
5365
+ return convertValue$2(placeholderValue);
5151
5366
  });
5152
- const minDate = createMemo(() => convertValue(access(props.minValue), day()) ?? void 0);
5153
- const maxDate = createMemo(() => convertValue(access(props.maxValue), day()) ?? void 0);
5367
+ const minDate = createMemo(() => convertValue$2(access(props.minValue), day()) ?? void 0);
5368
+ const maxDate = createMemo(() => convertValue$2(access(props.maxValue), day()) ?? void 0);
5154
5369
  const timeValue = createMemo(() => {
5155
5370
  const current = value();
5156
5371
  return current && "day" in current ? toTime$1(current) : current;
5157
5372
  });
5158
5373
  const dateTime = createMemo(() => {
5159
5374
  const current = value();
5160
- return current == null ? null : convertValue(current);
5375
+ return current == null ? null : convertValue$2(current);
5161
5376
  });
5162
- const defaultDateTime = props.defaultValue == null ? null : convertValue(props.defaultValue);
5377
+ const defaultDateTime = props.defaultValue == null ? null : convertValue$2(props.defaultValue);
5163
5378
  const onChange = (newValue) => {
5164
5379
  const converted = day() || defaultValueTimeZone ? newValue : newValue && toTime$1(newValue);
5165
5380
  if (access(props.value) === void 0) setInternalValue(() => converted);
@@ -5274,16 +5489,16 @@ function createDatePickerState(props = {}) {
5274
5489
  const isReadOnly = createMemo(() => access(props.isReadOnly) ?? false);
5275
5490
  const validationState = createMemo(() => access(props.validationState));
5276
5491
  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
- });
5492
+ const dateValue = () => {
5493
+ const committed = value();
5494
+ if (committed) return committed;
5495
+ return selectedDate();
5496
+ };
5497
+ const timeValue = () => {
5498
+ const committed = value();
5499
+ if (committed && "hour" in committed) return committed;
5500
+ return selectedTime();
5501
+ };
5287
5502
  const getPlaceholderTime = () => {
5288
5503
  const placeholder = props.placeholderValue;
5289
5504
  if (placeholder && "hour" in placeholder) return placeholder;
@@ -5409,12 +5624,17 @@ function createDatePickerState(props = {}) {
5409
5624
  * State management for select components.
5410
5625
  * Based on @react-stately/select useSelectState.
5411
5626
  */
5627
+ function convertValue$1(value) {
5628
+ if (value === void 0) return void 0;
5629
+ if (value === null || typeof value === "string" || typeof value === "number") return value == null ? [] : [value];
5630
+ return value;
5631
+ }
5412
5632
  /**
5413
5633
  * Creates state for a select component.
5414
5634
  * Combines list state with overlay trigger state for dropdown behavior.
5415
5635
  */
5416
5636
  function createSelectState(props) {
5417
- const getProps = () => access(props);
5637
+ const getProps = propsAccessor(props);
5418
5638
  const selectionMode = () => getProps().selectionMode ?? "single";
5419
5639
  const overlayState = createOverlayTriggerState({
5420
5640
  get isOpen() {
@@ -5427,61 +5647,85 @@ function createSelectState(props) {
5427
5647
  return getProps().onOpenChange;
5428
5648
  }
5429
5649
  });
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();
5650
+ const resolvedDefault = untrack(() => {
5651
+ const current = getProps();
5652
+ if (current.defaultValue !== void 0) return current.defaultValue;
5653
+ return (current.selectionMode ?? "single") === "single" ? current.defaultSelectedKey ?? null : [];
5654
+ });
5655
+ const controlledProp = () => {
5656
+ const current = getProps();
5657
+ if (current.value !== void 0) return current.value;
5658
+ if (selectionMode() === "single" && current.selectedKey !== void 0) return current.selectedKey ?? null;
5441
5659
  };
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);
5660
+ const [internalValue, setInternalValue] = createInternalSignal(resolvedDefault);
5661
+ const readControlled = () => {
5662
+ const controlled = controlledProp();
5663
+ return controlled === void 0 ? void 0 : controlled;
5664
+ };
5665
+ let valueRef = untrack(() => {
5666
+ const controlled = readControlled();
5667
+ return controlled !== void 0 ? controlled : internalValue();
5668
+ });
5669
+ let wroteThisTurn = false;
5670
+ const syncValueRef = () => {
5671
+ if (wroteThisTurn) return;
5672
+ const controlled = readControlled();
5673
+ valueRef = controlled !== void 0 ? controlled : untrack(internalValue);
5674
+ };
5675
+ const displayValue = () => {
5676
+ const controlled = readControlled();
5677
+ const raw = controlled !== void 0 ? controlled : internalValue();
5678
+ if (selectionMode() === "single" && raw !== null && typeof raw !== "string" && typeof raw !== "number") return raw[0] ?? null;
5679
+ return raw;
5680
+ };
5681
+ const setValue = (next) => {
5682
+ syncValueRef();
5683
+ if (selectionMode() === "single") {
5684
+ const key = typeof next === "string" || typeof next === "number" ? next : next === null ? null : next[0] ?? null;
5685
+ if (!Object.is(valueRef, key)) {
5686
+ valueRef = key;
5687
+ wroteThisTurn = true;
5688
+ queueMicrotask(() => {
5689
+ wroteThisTurn = false;
5690
+ });
5691
+ if (controlledProp() === void 0) setInternalValue(key);
5692
+ getProps().onChange?.(key);
5452
5693
  getProps().onSelectionChange?.(key);
5453
5694
  }
5454
5695
  return;
5455
5696
  }
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();
5697
+ const keys = typeof next === "string" || typeof next === "number" ? [next] : next === null ? [] : next;
5698
+ if (!Object.is(valueRef, keys)) {
5699
+ valueRef = keys;
5700
+ wroteThisTurn = true;
5701
+ queueMicrotask(() => {
5702
+ wroteThisTurn = false;
5703
+ });
5704
+ if (controlledProp() === void 0) setInternalValue(keys);
5705
+ getProps().onChange?.([...keys]);
5467
5706
  }
5468
- const key = selectedKey();
5469
- return key != null ? /* @__PURE__ */ new Set([key]) : /* @__PURE__ */ new Set();
5470
- });
5707
+ };
5471
5708
  const setSelectedKeys = (keys) => {
5472
- const next = new Set(keys);
5473
5709
  if (selectionMode() === "multiple") {
5474
- if (!isControlledMultiple()) setInternalSelectedKeys(next);
5475
- getProps().onSelectionChangeKeys?.(next);
5476
- getProps().onSelectionChange?.(next.size > 0 ? Array.from(next)[0] : null);
5710
+ setValue([...keys]);
5477
5711
  return;
5478
5712
  }
5479
- const key = next.size > 0 ? Array.from(next)[0] : null;
5480
- setSelectedKey(key);
5713
+ let key = null;
5714
+ for (const item of keys) {
5715
+ key = item;
5716
+ break;
5717
+ }
5718
+ setValue(key);
5481
5719
  };
5720
+ const initialDisplay = untrack(displayValue);
5721
+ const stateDefaultValue = resolvedDefault ?? initialDisplay;
5722
+ const defaultSelectedKey = untrack(() => getProps().defaultSelectedKey) ?? (untrack(() => getProps().selectionMode) === "single" ? initialDisplay : null);
5723
+ const shouldCloseOnSelect = () => getProps().shouldCloseOnSelect ?? selectionMode() === "single";
5482
5724
  const validation = createFormValidationState({
5483
5725
  get value() {
5484
- return selectionMode() === "multiple" ? selectedKeys() : selectedKey();
5726
+ const display = displayValue();
5727
+ if (display !== null && typeof display !== "string" && typeof display !== "number" && display.length === 0) return null;
5728
+ return display;
5485
5729
  },
5486
5730
  get isInvalid() {
5487
5731
  return getProps().isInvalid;
@@ -5523,41 +5767,34 @@ function createSelectState(props) {
5523
5767
  },
5524
5768
  allowDuplicateSelectionEvents: true,
5525
5769
  get selectedKeys() {
5526
- const keys = selectedKeys();
5527
- if (keys === "all") return "all";
5528
- return keys;
5770
+ return convertValue$1(displayValue()) ?? [];
5529
5771
  },
5530
5772
  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
5773
  if (keys === "all") return;
5539
- const key = keys.size > 0 ? Array.from(keys)[0] : null;
5540
- setSelectedKey(key);
5541
- overlayState.close();
5774
+ if (selectionMode() === "single") setValue(keys.values().next().value ?? null);
5775
+ else setValue([...keys]);
5776
+ if (shouldCloseOnSelect()) overlayState.close();
5542
5777
  validation.commitValidation();
5543
5778
  }
5544
5779
  });
5780
+ const [focusStrategy, setFocusStrategy] = createInternalSignal(null);
5545
5781
  const [isFocused, setFocused] = createInternalSignal(false);
5782
+ const selectedKey = () => listState.selectionManager.firstSelectedKey;
5783
+ const selectedKeys = () => new Set(convertValue$1(displayValue()) ?? []);
5546
5784
  const selectedItem = createMemo(() => {
5547
5785
  const key = selectedKey();
5548
5786
  if (key == null) return null;
5549
5787
  return listState.collection().getItem(key);
5550
5788
  });
5551
5789
  const selectedItems = createMemo(() => {
5552
- const keys = selectedKeys();
5553
- if (keys === "all") return Array.from(listState.collection());
5554
5790
  const items = [];
5555
- for (const key of keys) {
5791
+ for (const key of listState.selectionManager.selectedKeys) {
5556
5792
  const item = listState.collection().getItem(key);
5557
5793
  if (item) items.push(item);
5558
5794
  }
5559
5795
  return items;
5560
5796
  });
5797
+ const canOpenMenu = () => listState.collection().size !== 0 || getProps().allowsEmptyCollection === true;
5561
5798
  return {
5562
5799
  realtimeValidation: validation.realtimeValidation,
5563
5800
  displayValidation: validation.displayValidation,
@@ -5571,15 +5808,29 @@ function createSelectState(props) {
5571
5808
  isFocused,
5572
5809
  setFocused,
5573
5810
  isOpen: overlayState.isOpen,
5574
- open: overlayState.open,
5811
+ open(strategy = null) {
5812
+ if (!canOpenMenu()) return;
5813
+ setFocusStrategy(strategy);
5814
+ overlayState.open();
5815
+ },
5575
5816
  close: overlayState.close,
5576
- toggle: overlayState.toggle,
5817
+ toggle(strategy = null) {
5818
+ if (!canOpenMenu()) return;
5819
+ setFocusStrategy(strategy);
5820
+ overlayState.toggle();
5821
+ },
5822
+ focusStrategy,
5577
5823
  selectionMode,
5824
+ shouldCloseOnSelect,
5825
+ value: () => displayValue(),
5826
+ defaultValue: stateDefaultValue,
5827
+ setValue,
5578
5828
  selectedKey,
5829
+ defaultSelectedKey,
5830
+ setSelectedKey: setValue,
5579
5831
  selectedKeys,
5580
5832
  selectedItem,
5581
5833
  selectedItems,
5582
- setSelectedKey,
5583
5834
  setSelectedKeys,
5584
5835
  isKeyDisabled: (key) => listState.isDisabled(key),
5585
5836
  get isDisabled() {
@@ -5599,6 +5850,7 @@ function createSelectState(props) {
5599
5850
  * ComboBox combines a text input with a dropdown list, allowing users to
5600
5851
  * either type to filter options or select from a list.
5601
5852
  */
5853
+ const EMPTY_COMBOBOX_KEYS = [];
5602
5854
  /**
5603
5855
  * Default filter function that does case-insensitive "contains" matching.
5604
5856
  */
@@ -5610,26 +5862,28 @@ const defaultContainsFilter = (textValue, inputValue) => {
5610
5862
  * Combines list state with input value management and filtering.
5611
5863
  */
5612
5864
  function createComboBoxState(props) {
5613
- const getProps = () => access(props);
5865
+ const getProps = propsAccessor(props);
5614
5866
  const menuTrigger = () => getProps().menuTrigger ?? "input";
5615
5867
  const allowsEmptyCollection = () => getProps().allowsEmptyCollection ?? false;
5616
5868
  const allowsCustomValue = () => getProps().allowsCustomValue ?? false;
5617
5869
  const shouldCloseOnBlur = () => getProps().shouldCloseOnBlur ?? true;
5618
- const isMultiple = () => getProps().selectionMode === "multiple";
5870
+ const isMultiple = () => (getProps().selectionMode ?? "single") === "multiple";
5619
5871
  const [focusStrategy, setFocusStrategy] = createInternalSignal(null);
5620
5872
  const [showAllItems, setShowAllItems] = createInternalSignal(false);
5621
5873
  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);
5874
+ const controlledPropValue = () => {
5875
+ const propsNow = getProps();
5876
+ if (propsNow.value !== void 0) return propsNow.value;
5877
+ if (!isMultiple() && propsNow.selectedKey !== void 0) return propsNow.selectedKey;
5878
+ };
5879
+ const isValueControlled = () => controlledPropValue() !== void 0;
5880
+ const initialStoredValue = () => {
5881
+ const propsNow = getProps();
5882
+ if (propsNow.defaultValue !== void 0) return propsNow.defaultValue;
5883
+ if ((propsNow.selectionMode ?? "single") === "multiple") return [];
5884
+ return propsNow.defaultSelectedKey ?? null;
5885
+ };
5886
+ const [internalValue, setInternalValue] = createInternalSignal(initialStoredValue());
5633
5887
  const isInputControlled = () => getProps().inputValue !== void 0;
5634
5888
  const [internalInputValue, setInternalInputValue] = createInternalSignal(getProps().defaultInputValue ?? "");
5635
5889
  let inputInitialized = false;
@@ -5641,13 +5895,77 @@ function createComboBoxState(props) {
5641
5895
  getProps().onInputChange?.(value);
5642
5896
  };
5643
5897
  const [lastValue, setLastValue] = createInternalSignal(inputValue());
5898
+ const storedValue = () => {
5899
+ const controlled = controlledPropValue();
5900
+ return controlled !== void 0 ? controlled : internalValue();
5901
+ };
5902
+ const displayValue = () => {
5903
+ const current = storedValue();
5904
+ if (!isMultiple()) return isKeyList(current) ? current[0] ?? null : current;
5905
+ if (isKeyList(current)) return current;
5906
+ if (current == null) return EMPTY_COMBOBOX_KEYS;
5907
+ return [current];
5908
+ };
5644
5909
  const selectedKey = () => {
5645
- return isSelectionControlled() ? getProps().selectedKey ?? null : internalSelectedKey();
5910
+ if (isMultiple()) return null;
5911
+ const current = displayValue();
5912
+ return isKeyList(current) ? current[0] ?? null : current;
5646
5913
  };
5647
- const setSelectedKey = (key) => {
5648
- if (!isSelectionControlled()) setInternalSelectedKey(key);
5649
- getProps().onSelectionChange?.(key);
5914
+ const selectedKeys = () => {
5915
+ const current = displayValue();
5916
+ if (isKeyList(current)) return new Set(current);
5917
+ return current == null ? /* @__PURE__ */ new Set() : /* @__PURE__ */ new Set([current]);
5918
+ };
5919
+ const setValue = (next) => {
5920
+ if (!isMultiple()) {
5921
+ const key = typeof next === "string" || typeof next === "number" ? next : next === null ? null : next[0] ?? null;
5922
+ const previous = untrack(selectedKey);
5923
+ if (!isValueControlled()) setInternalValue(key);
5924
+ getProps().onChange?.(key);
5925
+ if (key !== previous) getProps().onSelectionChange?.(key);
5926
+ return;
5927
+ }
5928
+ const keys = typeof next === "string" || typeof next === "number" ? [next] : next === null ? [] : [...next];
5929
+ if (!isValueControlled()) setInternalValue(keys);
5930
+ getProps().onChange?.([...keys]);
5650
5931
  };
5932
+ const setSelectedKey = (key) => {
5933
+ setValue(key);
5934
+ };
5935
+ const defaultValue = (() => {
5936
+ const propsNow = getProps();
5937
+ return (propsNow.defaultValue !== void 0 ? propsNow.defaultValue : isMultiple() ? EMPTY_COMBOBOX_KEYS : propsNow.defaultSelectedKey ?? null) ?? untrack(displayValue);
5938
+ })();
5939
+ const defaultSelectedKey = getProps().defaultSelectedKey ?? (isMultiple() ? null : untrack(displayValue));
5940
+ const validationValue = createMemo(() => {
5941
+ const dVal = displayValue();
5942
+ if (isKeyList(dVal) && dVal.length === 0) return null;
5943
+ return {
5944
+ inputValue: inputValue(),
5945
+ value: isKeyList(dVal) ? [...dVal] : dVal,
5946
+ selectedKey: selectedKey()
5947
+ };
5948
+ });
5949
+ const validation = createFormValidationState({
5950
+ get value() {
5951
+ return validationValue();
5952
+ },
5953
+ get isInvalid() {
5954
+ return getProps().isInvalid;
5955
+ },
5956
+ get validationState() {
5957
+ return getProps().validationState;
5958
+ },
5959
+ get validate() {
5960
+ return getProps().validate;
5961
+ },
5962
+ get name() {
5963
+ return getProps().name;
5964
+ },
5965
+ get validationBehavior() {
5966
+ return getProps().validationBehavior ?? "native";
5967
+ }
5968
+ });
5651
5969
  let listState;
5652
5970
  const overlayState = createOverlayTriggerState({
5653
5971
  get isOpen() {
@@ -5681,26 +5999,27 @@ function createComboBoxState(props) {
5681
5999
  get selectionMode() {
5682
6000
  return isMultiple() ? "multiple" : "single";
5683
6001
  },
5684
- disallowEmptySelection: false,
6002
+ get disallowEmptySelection() {
6003
+ return !isMultiple();
6004
+ },
6005
+ allowDuplicateSelectionEvents: true,
5685
6006
  get selectedKeys() {
5686
- if (isMultiple()) return Array.from(selectedKeys());
5687
- const key = selectedKey();
5688
- return key != null ? [key] : [];
6007
+ return convertValue(displayValue());
5689
6008
  },
5690
6009
  onSelectionChange(keys) {
5691
6010
  if (keys === "all") return;
5692
6011
  if (isMultiple()) {
5693
- const newKeys = new Set(keys);
5694
- setSelectedKeys(newKeys);
6012
+ setValue([...keys]);
5695
6013
  return;
5696
6014
  }
5697
6015
  const key = keys.size > 0 ? Array.from(keys)[0] : null;
5698
6016
  if (key === selectedKey()) {
6017
+ getProps().onSelectionChange?.(key);
5699
6018
  resetInputValue();
5700
6019
  closeMenu();
5701
6020
  return;
5702
6021
  }
5703
- setSelectedKey(key);
6022
+ setValue(key);
5704
6023
  }
5705
6024
  });
5706
6025
  const originalCollection = listState.collection;
@@ -5729,18 +6048,21 @@ function createComboBoxState(props) {
5729
6048
  return items;
5730
6049
  });
5731
6050
  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);
6051
+ if (getProps().defaultInputValue == null && !isMultiple()) {
6052
+ const key = selectedKey();
6053
+ if (key != null) {
6054
+ const item = originalCollection().getItem(key);
6055
+ if (item) {
6056
+ setInternalInputValue(item.textValue);
6057
+ setLastValue(item.textValue);
6058
+ }
5738
6059
  }
5739
6060
  }
5740
6061
  inputInitialized = true;
5741
6062
  }
5742
6063
  const resetInputValue = () => {
5743
- const textValue = selectedItem()?.textValue ?? "";
6064
+ const key = selectedKey();
6065
+ const textValue = key != null ? originalCollection().getItem(key)?.textValue ?? "" : "";
5744
6066
  setLastValue(textValue);
5745
6067
  setInputValue(textValue);
5746
6068
  };
@@ -5779,14 +6101,25 @@ function createComboBoxState(props) {
5779
6101
  overlayState.toggle();
5780
6102
  };
5781
6103
  const commitCustomValue = () => {
5782
- setSelectedKey(null);
6104
+ if (isMultiple()) {
6105
+ setLastValue(inputValue());
6106
+ closeMenu();
6107
+ return;
6108
+ }
6109
+ setValue(null);
5783
6110
  closeMenu();
5784
6111
  };
5785
- const commitSelection = () => {
5786
- if (isSelectionControlled() && isInputControlled()) {
5787
- getProps().onSelectionChange?.(selectedKey());
5788
- const item = selectedItem();
5789
- setLastValue(item?.textValue ?? "");
6112
+ const commitSelection = (shouldForceSelectionChange = false) => {
6113
+ if (isValueControlled() && isInputControlled()) {
6114
+ const key = selectedKey();
6115
+ const itemText = key != null ? originalCollection().getItem(key)?.textValue ?? "" : "";
6116
+ if (shouldForceSelectionChange || isMultiple() || inputValue() !== itemText) {
6117
+ getProps().onSelectionChange?.(key);
6118
+ const current = displayValue();
6119
+ const changeValue = isKeyList(current) ? [...current] : current;
6120
+ getProps().onChange?.(changeValue);
6121
+ }
6122
+ setLastValue(itemText);
5790
6123
  closeMenu();
5791
6124
  } else {
5792
6125
  resetInputValue();
@@ -5802,12 +6135,9 @@ function createComboBoxState(props) {
5802
6135
  };
5803
6136
  const commit = () => {
5804
6137
  const focusedKey = listState.focusedKey();
5805
- if (isMultiple()) {
5806
- if (overlayState.isOpen() && focusedKey != null) select(focusedKey);
5807
- return;
5808
- }
5809
6138
  if (overlayState.isOpen() && focusedKey != null) {
5810
- if (selectedKey() === focusedKey) commitSelection();
6139
+ if (!isMultiple() && selectedKey() === focusedKey) commitSelection(true);
6140
+ else if (isMultiple()) select(focusedKey);
5811
6141
  else listState.selectionManager.select(focusedKey);
5812
6142
  } else commitValue();
5813
6143
  };
@@ -5816,10 +6146,24 @@ function createComboBoxState(props) {
5816
6146
  else commitSelection();
5817
6147
  };
5818
6148
  const [isFocused, setIsFocused] = createInternalSignal(false);
6149
+ let valueOnFocus = [untrack(inputValue), untrack(displayValue)];
6150
+ const hasMoved = (a, b) => {
6151
+ if (a === b) return false;
6152
+ if (Array.isArray(a) && Array.isArray(b)) {
6153
+ if (a.length !== b.length) return true;
6154
+ for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return true;
6155
+ return false;
6156
+ }
6157
+ return true;
6158
+ };
5819
6159
  const setFocused = (focused) => {
5820
6160
  if (focused) {
6161
+ valueOnFocus = [inputValue(), displayValue()];
5821
6162
  if (menuTrigger() === "focus" && !getProps().isReadOnly) open(null, "focus");
5822
- } else if (shouldCloseOnBlur()) commitValue();
6163
+ } else {
6164
+ if (shouldCloseOnBlur()) commitValue();
6165
+ if (inputValue() !== valueOnFocus[0] || hasMoved(displayValue(), valueOnFocus[1])) validation.commitValidation();
6166
+ }
5823
6167
  setIsFocused(focused);
5824
6168
  };
5825
6169
  let lastDisplayValue = untrack(selectedKey);
@@ -5834,15 +6178,16 @@ function createComboBoxState(props) {
5834
6178
  trigger: menuTrigger(),
5835
6179
  showingAll: showAllItems(),
5836
6180
  inputControlled: isInputControlled(),
5837
- selectionControlled: isSelectionControlled()
5838
- }), ({ input, filtered, isOpen, last, focused, key, allowsEmpty, trigger, showingAll, inputControlled, selectionControlled }) => {
6181
+ selectionControlled: isValueControlled(),
6182
+ multiple: isMultiple()
6183
+ }), ({ input, filtered, isOpen, last, focused, key, allowsEmpty, trigger, showingAll, inputControlled, selectionControlled, multiple }) => {
5839
6184
  if (focused && (filtered.size > 0 || allowsEmpty) && !isOpen && input !== last && trigger !== "manual") open(null, "input");
5840
6185
  if (!showingAll && !allowsEmpty && isOpen && filtered.size === 0) closeMenu();
5841
6186
  if (key != null && key !== lastDisplayValue) closeMenu();
5842
6187
  if (input !== last) {
5843
6188
  listState.setFocusedKey(null);
5844
6189
  setShowAllItems(false);
5845
- if (input === "" && (!inputControlled || !selectionControlled)) setSelectedKey(null);
6190
+ if (!multiple && input === "" && (!inputControlled || !selectionControlled)) setValue(null);
5846
6191
  setLastValue(input);
5847
6192
  }
5848
6193
  lastDisplayValue = key;
@@ -5854,7 +6199,7 @@ function createComboBoxState(props) {
5854
6199
  key,
5855
6200
  textValue: (key != null ? originalCollection().getItem(key) : null)?.textValue ?? "",
5856
6201
  inputControlled: isInputControlled(),
5857
- selectionControlled: isSelectionControlled()
6202
+ selectionControlled: isValueControlled()
5858
6203
  };
5859
6204
  }, (data) => {
5860
6205
  if (!data) return;
@@ -5871,34 +6216,39 @@ function createComboBoxState(props) {
5871
6216
  const current = new Set(selectedKeys());
5872
6217
  if (current.has(key)) current.delete(key);
5873
6218
  else current.add(key);
5874
- setSelectedKeys(current);
6219
+ setValue([...current]);
5875
6220
  } else {
5876
- setSelectedKey(key);
6221
+ setValue(key);
5877
6222
  closeMenu();
5878
6223
  }
5879
6224
  };
6225
+ const setSelectedKeys = (keys) => {
6226
+ setValue([...keys]);
6227
+ };
5880
6228
  const removeSelectedKey = (key) => {
5881
- if (isMultiple()) {
5882
- const current = new Set(selectedKeys());
5883
- current.delete(key);
5884
- setSelectedKeys(current);
5885
- }
6229
+ if (!isMultiple()) return;
6230
+ setValue([...selectedKeys()].filter((selected) => selected !== key));
5886
6231
  };
5887
6232
  const selectionMode = () => getProps().selectionMode ?? "single";
5888
- const isSelected = (key) => {
5889
- if (isMultiple()) return selectedKeys().has(key);
5890
- return selectedKey() === key;
5891
- };
6233
+ const isSelected = (key) => selectedKeys().has(key);
5892
6234
  return {
6235
+ realtimeValidation: validation.realtimeValidation,
6236
+ displayValidation: validation.displayValidation,
6237
+ updateValidation: validation.updateValidation,
6238
+ resetValidation: validation.resetValidation,
6239
+ commitValidation: validation.commitValidation,
5893
6240
  collection: displayedCollection,
5894
6241
  isOpen: overlayState.isOpen,
5895
6242
  open,
5896
6243
  close: commitValue,
5897
6244
  toggle,
5898
6245
  selectedKey,
5899
- defaultSelectedKey: getProps().defaultSelectedKey ?? null,
6246
+ defaultSelectedKey,
5900
6247
  selectedItem,
5901
6248
  setSelectedKey,
6249
+ value: () => displayValue(),
6250
+ defaultValue,
6251
+ setValue,
5902
6252
  selectedKeys,
5903
6253
  setSelectedKeys,
5904
6254
  selectedItems,
@@ -5929,6 +6279,14 @@ function createComboBoxState(props) {
5929
6279
  }
5930
6280
  };
5931
6281
  }
6282
+ function isKeyList(value) {
6283
+ return value !== null && typeof value !== "string" && typeof value !== "number";
6284
+ }
6285
+ function convertValue(value) {
6286
+ if (value == null) return [];
6287
+ if (typeof value === "string" || typeof value === "number") return [value];
6288
+ return value;
6289
+ }
5932
6290
  /**
5933
6291
  * Filter a collection based on input value.
5934
6292
  * RAC `useComboBoxState.ts:644-672` copies nodes into a new `ListCollection`,
@@ -5969,7 +6327,7 @@ function filterCollection(collection, inputValue, filter) {
5969
6327
  * Creates state for a tab list component.
5970
6328
  */
5971
6329
  function createTabListState(props) {
5972
- const getProps = () => access(props);
6330
+ const getProps = propsAccessor(props);
5973
6331
  const collection = createMemo(() => {
5974
6332
  const p = getProps();
5975
6333
  return new ListCollection((p.items ?? []).map((item, index) => {
@@ -6041,32 +6399,13 @@ function createTabListState(props) {
6041
6399
  setChildFocusStrategy(childStrategy ?? null);
6042
6400
  };
6043
6401
  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
6402
  createEffect(() => ({
6052
6403
  sel: selectedKey(),
6053
6404
  foc: focusedKey(),
6054
6405
  focused: isFocused()
6055
6406
  }), ({ 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;
6407
+ if (sel !== null && foc === null || !focused && sel !== lastSelectedKey) setFocusedKey(sel);
6408
+ lastSelectedKey = sel;
6070
6409
  });
6071
6410
  createEffect(() => {
6072
6411
  const p = getProps();
@@ -6131,12 +6470,38 @@ function clamp$2(value, min, max) {
6131
6470
  return result;
6132
6471
  }
6133
6472
  /**
6134
- * Snaps a value to the nearest step.
6473
+ * Rounds to the decimal precision implied by a step, including exponential steps.
6135
6474
  */
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);
6475
+ function roundToStepPrecision(value, step) {
6476
+ let roundedValue = value;
6477
+ let precision = 0;
6478
+ const stepString = step.toString();
6479
+ const eIndex = stepString.toLowerCase().indexOf("e-");
6480
+ if (eIndex > 0) precision = Math.abs(Math.floor(Math.log10(Math.abs(step)))) + eIndex;
6481
+ else {
6482
+ const pointIndex = stepString.indexOf(".");
6483
+ if (pointIndex >= 0) precision = stepString.length - pointIndex;
6484
+ }
6485
+ if (precision > 0) {
6486
+ const pow = Math.pow(10, precision);
6487
+ roundedValue = Math.round(roundedValue * pow) / pow;
6488
+ }
6489
+ return roundedValue;
6490
+ }
6491
+ /**
6492
+ * Snaps to the nearest step, then to the last in-range step when that exceeds max.
6493
+ * A negative halfway rounds away from the step base.
6494
+ */
6495
+ function snapValueToStep(value, min, max, step) {
6496
+ const minNumber = Number(min);
6497
+ const maxNumber = Number(max);
6498
+ const remainder = (value - (isNaN(minNumber) ? 0 : minNumber)) % step;
6499
+ let snappedValue = roundToStepPrecision(Math.abs(remainder) * 2 >= step ? value + Math.sign(remainder) * (step - Math.abs(remainder)) : value - remainder, step);
6500
+ if (!isNaN(minNumber)) {
6501
+ if (snappedValue < minNumber) snappedValue = minNumber;
6502
+ else if (!isNaN(maxNumber) && snappedValue > maxNumber) snappedValue = minNumber + Math.floor(roundToStepPrecision((maxNumber - minNumber) / step, step)) * step;
6503
+ } else if (!isNaN(maxNumber) && snappedValue > maxNumber) snappedValue = Math.floor(roundToStepPrecision(maxNumber / step, step)) * step;
6504
+ return roundToStepPrecision(snappedValue, step);
6140
6505
  }
6141
6506
  function isValidStep(step) {
6142
6507
  return step != null && !isNaN(step) && step > 0;
@@ -6180,7 +6545,7 @@ function createNumberFieldState(props) {
6180
6545
  const applyConstraints = (value) => {
6181
6546
  const p = getProps();
6182
6547
  if (isNaN(value)) return NaN;
6183
- if (hasCustomStep()) return clamp$2(snapToStep$1(value, step(), p.minValue), p.minValue, p.maxValue);
6548
+ if (hasCustomStep()) return snapValueToStep(value, p.minValue, p.maxValue, step());
6184
6549
  return clamp$2(value, p.minValue, p.maxValue);
6185
6550
  };
6186
6551
  let initialNumberValue = NaN;
@@ -6262,11 +6627,11 @@ function createNumberFieldState(props) {
6262
6627
  syncControlledValue();
6263
6628
  setInputValueInternal(value);
6264
6629
  };
6265
- const commit = () => {
6630
+ const commit = (override) => {
6266
6631
  ensureInitialized();
6267
6632
  const p = getProps();
6268
- const input = readNow(inputValue);
6269
- if (input === "" || input === "-") {
6633
+ const input = override === void 0 ? readNow(inputValue) : override;
6634
+ if (input === "") {
6270
6635
  setNumberValue(NaN);
6271
6636
  setInputValueInternal(p.value === void 0 ? "" : formatNumber(readNow(actualNumberValue)));
6272
6637
  p.onChange?.(NaN);
@@ -6282,7 +6647,7 @@ function createNumberFieldState(props) {
6282
6647
  parsed = constrainForCommit(parsed);
6283
6648
  parsed = numberParser().parse(formatNumber(parsed));
6284
6649
  setNumberValue(parsed);
6285
- setInputValueInternal(formatNumber(parsed));
6650
+ setInputValueInternal(formatNumber(p.value === void 0 ? parsed : previous));
6286
6651
  p.onChange?.(parsed);
6287
6652
  if (parsed !== previous) validation.commitValidation();
6288
6653
  };
@@ -6293,7 +6658,7 @@ function createNumberFieldState(props) {
6293
6658
  const current = parsedInputValue();
6294
6659
  if (isNaN(current)) return true;
6295
6660
  if (p.maxValue == null) return true;
6296
- return snapToStep$1(current, step(), p.minValue) > current || handleDecimalOperation("+", current, step()) <= p.maxValue;
6661
+ return snapValueToStep(current, p.minValue, p.maxValue, step()) > current || handleDecimalOperation("+", current, step()) <= p.maxValue;
6297
6662
  });
6298
6663
  const canDecrement = createMemo(() => {
6299
6664
  ensureInitialized();
@@ -6302,15 +6667,20 @@ function createNumberFieldState(props) {
6302
6667
  const current = parsedInputValue();
6303
6668
  if (isNaN(current)) return true;
6304
6669
  if (p.minValue == null) return true;
6305
- return snapToStep$1(current, step(), p.minValue) < current || handleDecimalOperation("-", current, step()) >= p.minValue;
6670
+ return snapValueToStep(current, p.minValue, p.maxValue, step()) < current || handleDecimalOperation("-", current, step()) >= p.minValue;
6306
6671
  });
6307
6672
  const safeNextStep = (operation, minOrMaxValue = 0) => {
6308
6673
  const p = getProps();
6309
6674
  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);
6675
+ if (isNaN(parsed)) return snapValueToStep(isNaN(minOrMaxValue) ? 0 : minOrMaxValue, p.minValue, p.maxValue, step());
6676
+ const snapped = snapValueToStep(parsed, p.minValue, p.maxValue, step());
6677
+ if (operation === "+" && snapped > parsed || operation === "-" && snapped < parsed) return snapped;
6678
+ return snapValueToStep(handleDecimalOperation(operation, parsed, step()), p.minValue, p.maxValue, step());
6679
+ };
6680
+ const commitSteppedNumber = (next, refreshWhenUnchanged) => {
6681
+ const committed = readNow(actualNumberValue);
6682
+ setNumberValue(next);
6683
+ if (getProps().value === void 0 || refreshWhenUnchanged && next === committed) setInputValueInternal(formatNumber(next));
6314
6684
  };
6315
6685
  const increment = () => {
6316
6686
  ensureInitialized();
@@ -6318,8 +6688,7 @@ function createNumberFieldState(props) {
6318
6688
  const p = getProps();
6319
6689
  if (p.isDisabled || p.isReadOnly) return;
6320
6690
  const current = safeNextStep("+", p.minValue);
6321
- setNumberValue(current);
6322
- setInputValueInternal(formatNumber(current));
6691
+ commitSteppedNumber(current, true);
6323
6692
  p.onChange?.(current);
6324
6693
  validation.commitValidation();
6325
6694
  };
@@ -6329,8 +6698,7 @@ function createNumberFieldState(props) {
6329
6698
  const p = getProps();
6330
6699
  if (p.isDisabled || p.isReadOnly) return;
6331
6700
  const current = safeNextStep("-", p.maxValue);
6332
- setNumberValue(current);
6333
- setInputValueInternal(formatNumber(current));
6701
+ commitSteppedNumber(current, true);
6334
6702
  p.onChange?.(current);
6335
6703
  validation.commitValidation();
6336
6704
  };
@@ -6340,9 +6708,8 @@ function createNumberFieldState(props) {
6340
6708
  const p = getProps();
6341
6709
  if (p.isDisabled || p.isReadOnly) return;
6342
6710
  if (p.maxValue == null) return;
6343
- const snapped = snapToStep$1(p.maxValue, step(), p.minValue);
6344
- setNumberValue(snapped);
6345
- setInputValueInternal(formatNumber(snapped));
6711
+ const snapped = snapValueToStep(p.maxValue, p.minValue, p.maxValue, step());
6712
+ commitSteppedNumber(snapped, false);
6346
6713
  p.onChange?.(snapped);
6347
6714
  validation.commitValidation();
6348
6715
  };
@@ -6352,15 +6719,13 @@ function createNumberFieldState(props) {
6352
6719
  const p = getProps();
6353
6720
  if (p.isDisabled || p.isReadOnly) return;
6354
6721
  if (p.minValue == null) return;
6355
- setNumberValue(p.minValue);
6356
- setInputValueInternal(formatNumber(p.minValue));
6722
+ commitSteppedNumber(p.minValue, false);
6357
6723
  p.onChange?.(p.minValue);
6358
6724
  validation.commitValidation();
6359
6725
  };
6360
6726
  const setNumberValuePublic = (value) => {
6361
6727
  ensureInitialized();
6362
- setNumberValue(value);
6363
- setInputValueInternal(formatNumber(value));
6728
+ commitSteppedNumber(value, false);
6364
6729
  getProps().onChange?.(value);
6365
6730
  };
6366
6731
  return {
@@ -6375,7 +6740,7 @@ function createNumberFieldState(props) {
6375
6740
  return inputValue;
6376
6741
  },
6377
6742
  get numberValue() {
6378
- return actualNumberValue;
6743
+ return parsedInputValue;
6379
6744
  },
6380
6745
  canIncrement,
6381
6746
  canDecrement,
@@ -6448,75 +6813,144 @@ function snapToStep(value, min, max, step) {
6448
6813
  const decimalPlaces = (step.toString().split(".")[1] || "").length;
6449
6814
  return clamp$1(parseFloat(snapped.toFixed(decimalPlaces)), min, max);
6450
6815
  }
6451
- /**
6452
- * Provides state management for a slider component.
6453
- */
6454
6816
  function createSliderState(props) {
6455
6817
  const getProps = () => access(props);
6456
6818
  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);
6819
+ const arrayMode = Array.isArray(initialProps.value) || Array.isArray(initialProps.defaultValue);
6820
+ const initialArray = Array.isArray(initialProps.value) ? initialProps.value : initialProps.defaultValue;
6821
+ const initialCount = Array.isArray(initialArray) ? initialArray.length : 1;
6822
+ const finite = (value, fallback) => typeof value === "number" && Number.isFinite(value) ? value : fallback;
6823
+ const minValue = createMemo(() => arrayMode ? finite(getProps().minValue, DEFAULT_MIN) : getProps().minValue ?? DEFAULT_MIN);
6824
+ const step = createMemo(() => {
6825
+ const next = arrayMode ? finite(getProps().step, DEFAULT_STEP) : getProps().step ?? DEFAULT_STEP;
6826
+ return arrayMode && next <= 0 ? DEFAULT_STEP : next;
6827
+ });
6828
+ const maxValue = createMemo(() => {
6829
+ const next = arrayMode ? finite(getProps().maxValue, DEFAULT_MAX) : getProps().maxValue ?? DEFAULT_MAX;
6830
+ return arrayMode && next <= minValue() ? minValue() + step() : next;
6831
+ });
6463
6832
  const orientation = createMemo(() => getProps().orientation ?? "horizontal");
6464
6833
  const isDisabled = createMemo(() => getProps().isDisabled ?? false);
6465
6834
  const pageStep = createMemo(() => Math.max(step(), snapToStep((maxValue() - minValue()) / 10, 0, maxValue() - minValue(), step())));
6466
6835
  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;
6836
+ const fallbackValues = () => Array.from({ length: initialCount }, (_, index) => index === 0 ? minValue() : maxValue());
6837
+ const normalize = (raw) => {
6838
+ 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()));
6839
+ return arrayMode ? snapped.sort((a, b) => a - b) : snapped;
6840
+ };
6841
+ const [internalValues, setInternalValues] = createInternalSignal(normalize(initialProps.defaultValue));
6842
+ const values = () => normalize(isControlled() ? getProps().value : internalValues());
6843
+ const initialValues = [...values()];
6844
+ const initialDefaultValues = normalize(initialProps.defaultValue ?? initialValues);
6845
+ const defaultValues = () => normalize(arrayMode ? getProps().defaultValue : initialDefaultValues);
6846
+ if (arrayMode) {
6847
+ const resetSnapshot = () => isControlled() ? void 0 : {
6848
+ defaults: normalize(getProps().defaultValue),
6849
+ min: minValue(),
6850
+ max: maxValue(),
6851
+ step: step()
6852
+ };
6853
+ let previousReset = resetSnapshot();
6854
+ createEffect(resetSnapshot, (next) => {
6855
+ const previous = previousReset;
6856
+ previousReset = next;
6857
+ 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);
6858
+ });
6859
+ }
6860
+ const [draggings, setDraggings] = createInternalSignal(initialValues.map(() => false));
6861
+ const [focusedThumb, setFocusedThumbValue] = createInternalSignal(void 0);
6862
+ const isDragging = () => draggings().some(Boolean);
6863
+ let pendingValues = [...initialValues];
6864
+ let hasPendingUpdate = false;
6865
+ let nonDraggingRenderedSnapshot;
6866
+ const validIndex = (index) => Number.isInteger(index) && index >= 0 && index < values().length;
6867
+ const getThumbValue = (index) => values()[index];
6868
+ const getThumbMinValue = (index) => index === 0 ? minValue() : values()[index - 1];
6869
+ const getThumbMaxValue = (index) => index === values().length - 1 ? maxValue() : values()[index + 1];
6870
+ const emit = (kind, next) => {
6483
6871
  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
- };
6872
+ if (arrayMode) p[kind]?.([...next]);
6873
+ else p[kind]?.(next[0]);
6874
+ };
6875
+ const setThumbValue = (index, nextValue) => {
6876
+ if (isDisabled() || !validIndex(index)) return;
6877
+ const rendered = values();
6878
+ const retainNonDraggingProposal = arrayMode && isControlled() && nonDraggingRenderedSnapshot !== void 0 && rendered.length === nonDraggingRenderedSnapshot.length && rendered.every((item, i) => item === nonDraggingRenderedSnapshot[i]);
6879
+ const base = normalize(isDragging() || retainNonDraggingProposal ? pendingValues : rendered);
6880
+ const lower = index === 0 ? minValue() : base[index - 1];
6881
+ const upper = index === base.length - 1 ? maxValue() : base[index + 1];
6882
+ const snapped = clamp$1(snapToStep(nextValue, minValue(), maxValue(), step()), lower, upper);
6883
+ const next = [...base];
6884
+ next[index] = snapped;
6885
+ pendingValues = next;
6886
+ hasPendingUpdate = true;
6887
+ nonDraggingRenderedSnapshot = !isDragging() && arrayMode && isControlled() ? [...rendered] : void 0;
6888
+ const changed = next.some((item, i) => item !== rendered[i]);
6889
+ if (!isControlled() && (!arrayMode || changed)) setInternalValues(next);
6890
+ if (!arrayMode || changed) emit("onChange", next);
6891
+ };
6892
+ const setThumbPercent = (index, percent) => setThumbValue(index, clamp$1(percent, 0, 1) * (maxValue() - minValue()) + minValue());
6893
+ const getThumbPercent = (index) => (getThumbValue(index) - minValue()) / (maxValue() - minValue());
6894
+ const isThumbDragging = (index) => draggings()[index] ?? false;
6895
+ const setThumbDragging = (index, dragging) => {
6896
+ if (dragging && (!validIndex(index) || arrayMode && isDisabled())) return;
6897
+ const wasDragging = isThumbDragging(index);
6898
+ if (wasDragging === dragging) return;
6899
+ if (dragging && !isDragging()) {
6900
+ pendingValues = [...values()];
6901
+ hasPendingUpdate = false;
6902
+ nonDraggingRenderedSnapshot = void 0;
6903
+ }
6904
+ const next = [...draggings()];
6905
+ next[index] = dragging;
6906
+ setDraggings(next);
6907
+ if (wasDragging && !isDragging()) emit("onChangeEnd", normalize(hasPendingUpdate ? pendingValues : values()));
6908
+ };
6909
+ const setFocusedThumb = (index) => {
6910
+ if (index === void 0 || validIndex(index)) setFocusedThumbValue(index);
6911
+ };
6912
+ const incrementThumb = (index, stepSize = 1) => setThumbValue(index, getThumbValue(index) + Math.max(stepSize, step()));
6913
+ const decrementThumb = (index, stepSize = 1) => setThumbValue(index, getThumbValue(index) - Math.max(stepSize, step()));
6914
+ const formatter = createMemo(() => new Intl.NumberFormat(getProps().locale, getProps().formatOptions));
6915
+ function getFormattedValue(value = values()) {
6916
+ const items = typeof value === "number" ? [value] : value;
6917
+ if (items.length === 0) return "";
6918
+ if (items.length === 1) return formatter().format(items[0]);
6919
+ if (items.length === 2) return formatter().formatRange(items[0], items[1]);
6920
+ return new Intl.ListFormat(formatter().resolvedOptions().locale, { type: "unit" }).format(items.map((item) => formatter().format(item)));
6921
+ }
6922
+ const value = () => getThumbValue(0) ?? minValue();
6923
+ const setValue = (next) => setThumbValue(0, next);
6508
6924
  return {
6925
+ values,
6926
+ defaultValues,
6927
+ getThumbValue,
6928
+ setThumbValue,
6929
+ setThumbPercent,
6930
+ getThumbPercent,
6931
+ getThumbMinValue,
6932
+ getThumbMaxValue,
6933
+ getThumbValueLabel: (index) => getFormattedValue(getThumbValue(index)),
6934
+ isThumbDragging,
6935
+ setThumbDragging,
6936
+ focusedThumb,
6937
+ setFocusedThumb,
6938
+ incrementThumb,
6939
+ decrementThumb,
6509
6940
  value,
6510
6941
  setValue,
6511
- setValuePercent,
6512
- getValuePercent,
6942
+ setValuePercent: (percent) => setThumbPercent(0, percent),
6943
+ getValuePercent: () => getThumbPercent(0),
6513
6944
  getFormattedValue,
6514
6945
  isDragging,
6515
- setDragging,
6516
- isFocused,
6517
- setFocused,
6518
- increment,
6519
- decrement,
6946
+ setDragging: (dragging) => setThumbDragging(0, dragging),
6947
+ isFocused: () => focusedThumb() !== void 0,
6948
+ setFocused: (focused) => {
6949
+ if (focused) setFocusedThumb(0);
6950
+ else if (focusedThumb() === 0) setFocusedThumb(void 0);
6951
+ },
6952
+ increment: (multiplier = 1) => setValue(value() + step() * multiplier),
6953
+ decrement: (multiplier = 1) => setValue(value() - step() * multiplier),
6520
6954
  get minValue() {
6521
6955
  return minValue();
6522
6956
  },
@@ -6991,7 +7425,7 @@ function createDisclosureGroupState(props = {}) {
6991
7425
  * Handles row selection, focus management, and keyboard navigation state.
6992
7426
  */
6993
7427
  function createGridState(options) {
6994
- const getOptions = () => options();
7428
+ const getOptions = propsAccessor(options);
6995
7429
  const disabledKeys = createMemo(() => {
6996
7430
  const keys = getOptions().disabledKeys;
6997
7431
  return keys ? new Set(keys) : /* @__PURE__ */ new Set();
@@ -7014,15 +7448,18 @@ function createGridState(options) {
7014
7448
  const disallowEmptySelection = createMemo(() => getOptions().disallowEmptySelection ?? false);
7015
7449
  const disabledBehavior = createMemo(() => getOptions().disabledBehavior ?? "all");
7016
7450
  const focusMode = createMemo(() => getOptions().focusMode ?? "row");
7451
+ let lastSelectionBehavior = selectionBehaviorProp();
7017
7452
  createEffect(selectionBehaviorProp, (behavior) => {
7018
- setSelectionBehaviorState(behavior);
7453
+ if (behavior !== lastSelectionBehavior) {
7454
+ setSelectionBehaviorState(behavior);
7455
+ lastSelectionBehavior = behavior;
7456
+ }
7019
7457
  });
7020
7458
  createEffect(() => ({
7021
7459
  prop: selectionBehaviorProp(),
7022
- state: selectionBehaviorState(),
7023
7460
  keys: selectedKeys()
7024
- }), ({ prop, state, keys }) => {
7025
- if (prop === "replace" && state === "toggle" && keys !== "all" && keys.size === 0) setSelectionBehaviorState("replace");
7461
+ }), ({ prop, keys }) => {
7462
+ if (prop === "replace" && selectionBehaviorState() === "toggle" && keys !== "all" && keys.size === 0) setSelectionBehaviorState("replace");
7026
7463
  });
7027
7464
  const setFocusedKey = (key, strategy = "first") => {
7028
7465
  const collection = getOptions().collection;
@@ -7279,7 +7716,7 @@ const OPPOSITE_SORT_DIRECTION = {
7279
7716
  * Extends grid state with sorting and table-specific features.
7280
7717
  */
7281
7718
  function createTableState(options) {
7282
- const getOptions = () => options();
7719
+ const getOptions = propsAccessor(options);
7283
7720
  const gridState = createGridState(() => ({
7284
7721
  collection: getOptions().collection,
7285
7722
  disabledKeys: getOptions().disabledKeys,
@@ -7321,7 +7758,7 @@ function createTableState(options) {
7321
7758
  return gridState.disabledBehavior;
7322
7759
  },
7323
7760
  get isKeyboardNavigationDisabled() {
7324
- return gridState.isKeyboardNavigationDisabled;
7761
+ return getOptions().collection.size === 0 || gridState.isKeyboardNavigationDisabled;
7325
7762
  },
7326
7763
  get focusedKey() {
7327
7764
  return gridState.focusedKey;
@@ -7759,7 +8196,7 @@ function createTableCollection(options) {
7759
8196
  * expanded rows in addition to the selection, sort and focus handled by the table state.
7760
8197
  */
7761
8198
  function createTreeGridState(options) {
7762
- const getOptions = () => options();
8199
+ const getOptions = propsAccessor(options);
7763
8200
  const [internalExpandedKeys, setInternalExpandedKeys] = createInternalSignal(getInitialExpanded(getOptions().UNSTABLE_defaultExpandedKeys));
7764
8201
  const expandedKeys = () => {
7765
8202
  const controlled = getOptions().UNSTABLE_expandedKeys;
@@ -7902,13 +8339,28 @@ function parseColumnDef(col, tableWidth) {
7902
8339
  maxWidth
7903
8340
  };
7904
8341
  }
8342
+ function applyFractionalRemainder(widths, columns, originalWidth) {
8343
+ if (!(originalWidth - Math.floor(originalWidth) > 0) || columns.length === 0) return;
8344
+ const lastKey = columns[columns.length - 1].key;
8345
+ if (widths.has(lastKey)) {
8346
+ const tableFractionalWidth = originalWidth.toString().split(".")[1];
8347
+ if (tableFractionalWidth) {
8348
+ const columnWidth = Math.floor(widths.get(lastKey)).toString();
8349
+ widths.set(lastKey, Number(columnWidth + "." + tableFractionalWidth));
8350
+ }
8351
+ }
8352
+ }
7905
8353
  /**
7906
8354
  * 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.
8355
+ * 1. Floor tableWidth to integer before column sizing, matching upstream TableUtils.calculateColumnSizes.
8356
+ * 2. Fixed-width columns get their declared width (clamped to min/max).
8357
+ * 3. Remaining space is distributed among fractional columns proportionally with cascading rounding.
8358
+ * 4. Leftover sub-pixel width is added to the last column so widths sum exactly to tableWidth.
7909
8359
  */
7910
8360
  function distributeWidths(columns, tableWidth) {
7911
- const parsed = columns.map((c) => parseColumnDef(c, tableWidth));
8361
+ const originalWidth = tableWidth;
8362
+ const availableWidth = Math.max(0, Math.floor(tableWidth));
8363
+ const parsed = columns.map((c) => parseColumnDef(c, availableWidth));
7912
8364
  let usedSpace = 0;
7913
8365
  let totalFr = 0;
7914
8366
  for (const p of parsed) if (p.fr > 0) totalFr += p.fr;
@@ -7916,16 +8368,22 @@ function distributeWidths(columns, tableWidth) {
7916
8368
  const w = Math.max(p.minWidth, Math.min(p.maxWidth, p.fixedPx));
7917
8369
  usedSpace += w;
7918
8370
  }
7919
- const remainingSpace = Math.max(0, tableWidth - usedSpace);
8371
+ const remainingSpace = Math.max(0, availableWidth - usedSpace);
7920
8372
  const perFr = totalFr > 0 ? remainingSpace / totalFr : 0;
7921
8373
  const widths = /* @__PURE__ */ new Map();
8374
+ let fpTotal = 0;
8375
+ let intTotal = 0;
7922
8376
  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
- }
8377
+ let targetSize;
8378
+ if (p.fr > 0) targetSize = p.fr * perFr;
8379
+ else targetSize = p.fixedPx;
8380
+ targetSize = Math.max(p.minWidth, Math.min(p.maxWidth, targetSize));
8381
+ const rounded = Math.round(targetSize + fpTotal) - intTotal;
8382
+ fpTotal += targetSize;
8383
+ intTotal += rounded;
8384
+ widths.set(p.key, rounded);
8385
+ }
8386
+ applyFractionalRemainder(widths, columns, originalWidth);
7929
8387
  return widths;
7930
8388
  }
7931
8389
  /**
@@ -7946,6 +8404,7 @@ function createTableColumnResizeState(props) {
7946
8404
  if (over.size === 0) return base;
7947
8405
  const merged = new Map(base);
7948
8406
  for (const [key, width] of over) if (merged.has(key)) merged.set(key, width);
8407
+ applyFractionalRemainder(merged, getProps().columns, getProps().tableWidth);
7949
8408
  return merged;
7950
8409
  });
7951
8410
  const minWidthMap = createMemo(() => {
@@ -7976,13 +8435,14 @@ function createTableColumnResizeState(props) {
7976
8435
  const updateResizedColumns = (key, width) => {
7977
8436
  const minW = getColumnMinWidth(key);
7978
8437
  const maxW = getColumnMaxWidth(key);
7979
- const clampedWidth = Math.round(Math.max(minW, Math.min(maxW, width)));
8438
+ const clampedWidth = Math.max(minW, Math.min(maxW, Math.floor(width)));
7980
8439
  const newOverrides = new Map(overrides());
7981
8440
  newOverrides.set(key, clampedWidth);
7982
8441
  setOverrides(newOverrides);
7983
8442
  const base = initialWidths();
7984
8443
  const merged = new Map(base);
7985
8444
  for (const [k, w] of newOverrides) if (merged.has(k)) merged.set(k, w);
8445
+ applyFractionalRemainder(merged, getProps().columns, getProps().tableWidth);
7986
8446
  return merged;
7987
8447
  };
7988
8448
  return {
@@ -8009,7 +8469,7 @@ function createTableColumnResizeState(props) {
8009
8469
  * Handles expansion, selection, focus management, and keyboard navigation state.
8010
8470
  */
8011
8471
  function createTreeState(options) {
8012
- const getOptions = () => options();
8472
+ const getOptions = propsAccessor(options);
8013
8473
  const disabledKeys = createMemo(() => {
8014
8474
  const keys = getOptions().disabledKeys;
8015
8475
  return keys ? new Set(keys) : /* @__PURE__ */ new Set();
@@ -8040,15 +8500,18 @@ function createTreeState(options) {
8040
8500
  const selectionBehavior = createMemo(() => selectionBehaviorState());
8041
8501
  const disallowEmptySelection = createMemo(() => getOptions().disallowEmptySelection ?? false);
8042
8502
  const disabledBehavior = createMemo(() => getOptions().disabledBehavior ?? "all");
8503
+ let lastSelectionBehavior = selectionBehaviorProp();
8043
8504
  createEffect(selectionBehaviorProp, (behavior) => {
8044
- setSelectionBehaviorState(behavior);
8505
+ if (behavior !== lastSelectionBehavior) {
8506
+ setSelectionBehaviorState(behavior);
8507
+ lastSelectionBehavior = behavior;
8508
+ }
8045
8509
  });
8046
8510
  createEffect(() => ({
8047
8511
  prop: selectionBehaviorProp(),
8048
- state: selectionBehaviorState(),
8049
8512
  keys: selectedKeys()
8050
- }), ({ prop, state, keys }) => {
8051
- if (prop === "replace" && state === "toggle" && keys !== "all" && keys.size === 0) setSelectionBehaviorState("replace");
8513
+ }), ({ prop, keys }) => {
8514
+ if (prop === "replace" && selectionBehaviorState() === "toggle" && keys !== "all" && keys.size === 0) setSelectionBehaviorState("replace");
8052
8515
  });
8053
8516
  const setFocusedKey = (key, strategy = "first") => {
8054
8517
  setFocusedKeyInternal(key);
@@ -10558,6 +11021,14 @@ function createColorSliderState(options) {
10558
11021
  const init = initValue();
10559
11022
  if (init) setInternalValue(init);
10560
11023
  }
11024
+ const initialOptions = getOptions();
11025
+ const resetColor = (color) => {
11026
+ const normalized = normalizeColor(color);
11027
+ return initialOptions.colorSpace ? normalized.toFormat(initialOptions.colorSpace) : normalized;
11028
+ };
11029
+ const initialDefault = initialOptions.defaultValue ? resetColor(initialOptions.defaultValue) : void 0;
11030
+ const firstColor = initialOptions.value !== void 0 ? resetColor(initialOptions.value) : void 0;
11031
+ const defaultThumbValue = (initialDefault ?? firstColor ?? resetColor("#ff0000")).getChannelValue(initialOptions.channel);
10561
11032
  const value = createMemo(() => {
10562
11033
  const opts = getOptions();
10563
11034
  if (opts.value !== void 0) return normalizeValue(opts.value);
@@ -10631,7 +11102,9 @@ function createColorSliderState(options) {
10631
11102
  };
10632
11103
  const getDisplayColor = () => {
10633
11104
  const v = value();
10634
- if (channel() === "hue") return normalizeColor(`hsl(${v.getChannelValue("hue")}, 100%, 50%)`);
11105
+ const current = channel();
11106
+ if (current === "hue") return normalizeColor(`hsl(${v.getChannelValue("hue")}, 100%, 50%)`);
11107
+ if (current === "alpha") return v;
10635
11108
  return v.withChannelValue("alpha", 1);
10636
11109
  };
10637
11110
  const getThumbValueLabel = () => {
@@ -10668,6 +11141,7 @@ function createColorSliderState(options) {
10668
11141
  get isDisabled() {
10669
11142
  return isDisabled();
10670
11143
  },
11144
+ defaultThumbValue,
10671
11145
  getThumbValue,
10672
11146
  getThumbMinValue,
10673
11147
  getThumbMaxValue,
@@ -10698,6 +11172,9 @@ function createColorAreaState(options) {
10698
11172
  return colorSpace ? color.toFormat(colorSpace) : color;
10699
11173
  };
10700
11174
  const [internalValue, setInternalValue] = createInternalSignal(null);
11175
+ const firstOptions = getOptions();
11176
+ const controlledInitial = firstOptions.value !== void 0 ? normalizeValue(firstOptions.value) : void 0;
11177
+ const initialValue = normalizeValue(firstOptions.defaultValue ?? "#ff0000");
10701
11178
  const [isDragging, setIsDragging] = createInternalSignal(false);
10702
11179
  const initValue = () => {
10703
11180
  const opts = getOptions();
@@ -10745,6 +11222,9 @@ function createColorAreaState(options) {
10745
11222
  };
10746
11223
  const getXValue = () => value().getChannelValue(xChannel());
10747
11224
  const getYValue = () => value().getChannelValue(yChannel());
11225
+ const setValue = (newValue) => {
11226
+ updateValue(normalizeValue(newValue));
11227
+ };
10748
11228
  const setXValue = (newValue) => {
10749
11229
  const range = xRange();
10750
11230
  const clamped = Math.max(range.minValue, Math.min(range.maxValue, newValue));
@@ -10813,6 +11293,9 @@ function createColorAreaState(options) {
10813
11293
  get value() {
10814
11294
  return value();
10815
11295
  },
11296
+ get defaultValue() {
11297
+ return controlledInitial ?? initialValue;
11298
+ },
10816
11299
  get xChannel() {
10817
11300
  return xChannel();
10818
11301
  },
@@ -10844,6 +11327,7 @@ function createColorAreaState(options) {
10844
11327
  getYValue,
10845
11328
  setXValue,
10846
11329
  setYValue,
11330
+ setValue,
10847
11331
  setColorFromPoint,
10848
11332
  getThumbPosition,
10849
11333
  incrementX,
@@ -10870,7 +11354,9 @@ function createColorWheelState(options) {
10870
11354
  const value = normalizeColor(color);
10871
11355
  return value.toFormat(value.getColorSpace() === "hsb" ? "hsb" : "hsl");
10872
11356
  };
10873
- const initialValue = normalizeWheelValue(getOptions().defaultValue ?? defaultColor);
11357
+ const firstOptions = getOptions();
11358
+ const controlledInitial = firstOptions.value !== void 0 ? normalizeWheelValue(firstOptions.value) : void 0;
11359
+ const initialValue = normalizeWheelValue(firstOptions.defaultValue ?? defaultColor);
10874
11360
  const [internalValue, setInternalValue] = createInternalSignal(initialValue);
10875
11361
  const [isDragging, setIsDragging] = createInternalSignal(false);
10876
11362
  let valueRef = initialValue;
@@ -10912,9 +11398,7 @@ function createColorWheelState(options) {
10912
11398
  const radToDeg = (rad) => rad * 180 / Math.PI;
10913
11399
  const degToRad = (deg) => deg * Math.PI / 180;
10914
11400
  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);
11401
+ return mod(radToDeg(Math.atan2(y / radius, x / radius)), 360);
10918
11402
  };
10919
11403
  const angleToCartesian = (angle, radius) => ({
10920
11404
  x: radius * Math.cos(degToRad(angle)),
@@ -10973,7 +11457,7 @@ function createColorWheelState(options) {
10973
11457
  return value();
10974
11458
  },
10975
11459
  get defaultValue() {
10976
- return initialValue;
11460
+ return controlledInitial ?? initialValue;
10977
11461
  },
10978
11462
  get isDragging() {
10979
11463
  return isDragging();
@@ -11075,9 +11559,13 @@ function createColorFieldState(options) {
11075
11559
  const getOptions = () => options();
11076
11560
  const initialOptions = getOptions();
11077
11561
  const initialValue = getDefaultValue(initialOptions);
11562
+ const initialDefault = initialOptions.defaultValue !== void 0 ? normalizeNullableColor(initialOptions.defaultValue) : void 0;
11563
+ const firstColor = initialOptions.value !== void 0 ? normalizeNullableColor(initialOptions.value) : void 0;
11564
+ const defaultColorValue = initialDefault ?? firstColor ?? null;
11078
11565
  const [internalValue, setInternalValue] = createInternalSignal(initialValue);
11079
11566
  const [inputValue, setInputValueInternal] = createInternalSignal(formatColorValue(initialValue, initialOptions.channel, initialOptions.colorSpace, initialOptions.colorFormat, initialOptions.locale ?? "en-US"));
11080
11567
  const [invalidInput, setInvalidInput] = createInternalSignal(false);
11568
+ let isChannelInputEdited = false;
11081
11569
  const channel = createMemo(() => getOptions().channel);
11082
11570
  const colorSpace = createMemo(() => getOptions().colorSpace);
11083
11571
  const locale = createMemo(() => getOptions().locale ?? "en-US");
@@ -11093,6 +11581,7 @@ function createColorFieldState(options) {
11093
11581
  const isInvalid = createMemo(() => (getOptions().isInvalid ?? false) || invalidInput());
11094
11582
  const formatCurrentValue = (color = value()) => formatColorValue(color, channel(), colorSpace(), getOptions().colorFormat, locale());
11095
11583
  createEffect(() => formatCurrentValue(), (formatted) => {
11584
+ isChannelInputEdited = false;
11096
11585
  setInputValueInternal(formatted);
11097
11586
  setInvalidInput(false);
11098
11587
  });
@@ -11100,56 +11589,69 @@ function createColorFieldState(options) {
11100
11589
  const opts = getOptions();
11101
11590
  if (opts.value === void 0) setInternalValue(newColor);
11102
11591
  opts.onChange?.(newColor);
11592
+ isChannelInputEdited = false;
11103
11593
  setInputValueInternal(formatCurrentValue(newColor));
11104
11594
  setInvalidInput(false);
11105
11595
  };
11106
11596
  const setInputValue = (text) => {
11597
+ isChannelInputEdited = true;
11107
11598
  setInputValueInternal(text);
11108
11599
  setInvalidInput(false);
11109
11600
  };
11110
11601
  const setColorValue = (newColor) => {
11111
11602
  updateValue(newColor);
11112
11603
  };
11604
+ const currentValue = () => {
11605
+ const opts = getOptions();
11606
+ return opts.value !== void 0 ? normalizeNullableColor(opts.value) : readNow(internalValue);
11607
+ };
11113
11608
  const commit = () => {
11114
11609
  const text = readNow(inputValue).trim();
11115
11610
  const opts = getOptions();
11116
11611
  const chan = channel();
11612
+ const current = currentValue();
11117
11613
  if (!text) {
11118
- updateValue(null);
11119
- return;
11120
- }
11121
- if (chan) {
11122
- const displayColor = colorValue();
11614
+ if (current !== null) updateValue(null);
11615
+ } else if (chan) {
11616
+ const displayColor = getDisplayColor(current, colorSpace());
11123
11617
  const rawValue = parseChannelValue(text, displayColor, chan);
11124
- if (rawValue == null) {
11125
- setInputValueInternal(formatCurrentValue());
11126
- setInvalidInput(false);
11127
- return;
11618
+ if (isChannelInputEdited && rawValue !== null) {
11619
+ const next = displayColor.withChannelValue(chan, rawValue);
11620
+ if (current === null || next.getChannelValue(chan) !== displayColor.getChannelValue(chan)) updateValue(next);
11128
11621
  }
11129
- updateValue(displayColor.withChannelValue(chan, rawValue));
11130
- return;
11131
- }
11622
+ } else try {
11623
+ const next = parseHexValue(text, opts.colorFormat);
11624
+ if (current === null || next.toHexInt() !== current.toHexInt() || opts.colorFormat === "hexa" && next.getChannelValue("alpha") !== current.getChannelValue("alpha")) updateValue(next);
11625
+ } catch {}
11626
+ isChannelInputEdited = false;
11627
+ setInputValueInternal(formatCurrentValue(currentValue()));
11628
+ setInvalidInput(false);
11629
+ };
11630
+ const incrementHex = (amount) => {
11631
+ const text = readNow(inputValue);
11632
+ let parsed = null;
11132
11633
  try {
11133
- updateValue(parseHexValue(text, opts.colorFormat));
11634
+ parsed = parseColor(text.startsWith("#") ? text : `#${text}`);
11134
11635
  } catch {
11135
- setInputValueInternal(formatCurrentValue());
11136
- setInvalidInput(false);
11636
+ parsed = null;
11137
11637
  }
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);
11638
+ const nextColor = hexIntToColor((parsed ?? hexIntToColor(0)).toFormat("rgb").toHexInt() + amount);
11639
+ const current = value();
11640
+ if (current && current.toFormat("rgb").toHexInt() === nextColor.toHexInt()) return;
11641
+ const format = getOptions().colorFormat;
11642
+ updateValue(format ? nextColor.toFormat(format) : nextColor);
11144
11643
  };
11145
11644
  const updateChannel = (amount) => {
11146
11645
  const chan = channel();
11147
11646
  if (!chan) return;
11148
- const displayColor = colorValue();
11647
+ const current = currentValue();
11648
+ const displayColor = getDisplayColor(current, colorSpace());
11149
11649
  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));
11650
+ const currentChannel = displayColor.getChannelValue(chan);
11651
+ const nextValue = amount === "min" ? range.minValue : amount === "max" ? range.maxValue : Math.min(range.maxValue, Math.max(range.minValue, currentChannel + amount));
11652
+ const next = displayColor.withChannelValue(chan, nextValue);
11653
+ if (current !== null && next.getChannelValue(chan) === currentChannel) return;
11654
+ updateValue(next);
11153
11655
  };
11154
11656
  const increment = () => {
11155
11657
  const chan = channel();
@@ -11195,6 +11697,7 @@ function createColorFieldState(options) {
11195
11697
  get colorValue() {
11196
11698
  return colorValue();
11197
11699
  },
11700
+ defaultColorValue,
11198
11701
  get inputValue() {
11199
11702
  return inputValue();
11200
11703
  },
@@ -11572,6 +12075,9 @@ function createDraggableCollectionState(props) {
11572
12075
  if (p.isSelected) return p.isSelected(key);
11573
12076
  const selected = selectedKeySet(p.selectedKeys, p.collection);
11574
12077
  return selected === "all" ? true : selected.has(key);
12078
+ },
12079
+ get collection() {
12080
+ return getProps().collection;
11575
12081
  }
11576
12082
  };
11577
12083
  }
@@ -11781,7 +12287,10 @@ function createDroppableCollectionState(props) {
11781
12287
  drop,
11782
12288
  getDropOperation,
11783
12289
  shouldAcceptItemDrop,
11784
- isDropTargetFor
12290
+ isDropTargetFor,
12291
+ get collection() {
12292
+ return getProps().collection;
12293
+ }
11785
12294
  };
11786
12295
  }
11787
12296
  /**
@@ -11910,8 +12419,11 @@ function createListData(options) {
11910
12419
  },
11911
12420
  insertBefore(key, ...values) {
11912
12421
  setState((s) => {
11913
- const index = s.items.findIndex((item) => getKey(item) === key);
11914
- if (index === -1) return s;
12422
+ let index = s.items.findIndex((item) => getKey(item) === key);
12423
+ if (index === -1) {
12424
+ if (s.items.length === 0) index = 0;
12425
+ else return s;
12426
+ }
11915
12427
  return insertItems(s, index, ...values);
11916
12428
  });
11917
12429
  },
@@ -12238,6 +12750,8 @@ function createTreeData(options) {
12238
12750
  });
12239
12751
  },
12240
12752
  remove(...keys) {
12753
+ if (keys.length === 0) return;
12754
+ let nextMap;
12241
12755
  setTreeState((state) => {
12242
12756
  let { items, nodeMap } = state;
12243
12757
  let newMap = new Map(nodeMap);
@@ -12247,15 +12761,16 @@ function createTreeData(options) {
12247
12761
  newItems = result.items;
12248
12762
  newMap = result.nodeMap;
12249
12763
  }
12764
+ nextMap = newMap;
12250
12765
  return {
12251
12766
  items: newItems,
12252
12767
  nodeMap: newMap
12253
12768
  };
12254
12769
  });
12255
12770
  setSelectedKeys((sel) => {
12256
- if (sel === "all") return sel;
12771
+ if (sel === "all" || !nextMap) return sel;
12257
12772
  const newSel = new Set(sel);
12258
- for (const key of keys) newSel.delete(key);
12773
+ for (const key of sel) if (!nextMap.has(key)) newSel.delete(key);
12259
12774
  return newSel;
12260
12775
  });
12261
12776
  },
@@ -12611,8 +13126,11 @@ function createAsyncList(options) {
12611
13126
  },
12612
13127
  insertBefore(key, ...values) {
12613
13128
  setState((s) => {
12614
- const index = s.items.findIndex((item) => getKey(item) === key);
12615
- if (index === -1) return s;
13129
+ let index = s.items.findIndex((item) => getKey(item) === key);
13130
+ if (index === -1) {
13131
+ if (s.items.length === 0) index = 0;
13132
+ else return s;
13133
+ }
12616
13134
  return {
12617
13135
  ...s,
12618
13136
  items: [
@@ -12625,8 +13143,11 @@ function createAsyncList(options) {
12625
13143
  },
12626
13144
  insertAfter(key, ...values) {
12627
13145
  setState((s) => {
12628
- const index = s.items.findIndex((item) => getKey(item) === key);
12629
- if (index === -1) return s;
13146
+ let index = s.items.findIndex((item) => getKey(item) === key);
13147
+ if (index === -1) {
13148
+ if (s.items.length === 0) index = 0;
13149
+ else return s;
13150
+ }
12630
13151
  return {
12631
13152
  ...s,
12632
13153
  items: [
@@ -12659,6 +13180,7 @@ function createAsyncList(options) {
12659
13180
  for (const key of keys) newSelection.delete(key);
12660
13181
  selectedKeys = newSelection;
12661
13182
  }
13183
+ if (s.cursor == null && items.length === 0) selectedKeys = /* @__PURE__ */ new Set();
12662
13184
  return {
12663
13185
  ...s,
12664
13186
  items,
@@ -12759,100 +13281,144 @@ function getColorChannels(colorSpace) {
12759
13281
  * Creates state for a step list component.
12760
13282
  */
12761
13283
  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;
13284
+ const isListDisabled = () => props.isDisabled ?? false;
12771
13285
  const isReadOnly = () => props.isReadOnly ?? false;
13286
+ const items = () => props.items ?? [];
12772
13287
  const [lastCompletedStepInternal, setLastCompletedStepInternal] = createInternalSignal(props.defaultLastCompletedStep ?? null);
12773
13288
  const lastCompletedStep = () => {
12774
13289
  if (props.lastCompletedStep !== void 0) return props.lastCompletedStep;
12775
13290
  return lastCompletedStepInternal();
12776
13291
  };
13292
+ const isCompleted = (step) => {
13293
+ if (step == null) return false;
13294
+ const completed = lastCompletedStep();
13295
+ if (completed == null) return false;
13296
+ const { indexMap } = keyMaps();
13297
+ const stepIdx = indexMap.get(step);
13298
+ const completedIdx = indexMap.get(completed);
13299
+ if (stepIdx === void 0 || completedIdx === void 0) return false;
13300
+ return stepIdx <= completedIdx;
13301
+ };
13302
+ const isSelectable = (step) => {
13303
+ if (isListDisabled() || singleSelectListState.disabledKeys().has(step) || isReadOnly()) return false;
13304
+ if (isCompleted(step)) return true;
13305
+ const { keysLinkedList } = keyMaps();
13306
+ const prevStep = keysLinkedList.get(step);
13307
+ return prevStep !== void 0 && isCompleted(prevStep) || step === singleSelectListState.collection().getFirstKey();
13308
+ };
13309
+ const findInitialSelectedKey = () => {
13310
+ if (props.defaultSelectedKey !== void 0) return props.defaultSelectedKey;
13311
+ const currentItems = props.items ?? [];
13312
+ const disabled = new Set(props.disabledKeys ?? []);
13313
+ for (const item of currentItems) if (!disabled.has(item.key)) return item.key;
13314
+ return currentItems[0]?.key;
13315
+ };
13316
+ const singleSelectListState = createSingleSelectListState({
13317
+ get items() {
13318
+ return props.items ?? [];
13319
+ },
13320
+ getKey(item) {
13321
+ return item.key;
13322
+ },
13323
+ getTextValue(item) {
13324
+ return item.label ?? item.textValue ?? (item.key != null ? String(item.key) : "");
13325
+ },
13326
+ get disabledKeys() {
13327
+ return props.disabledKeys;
13328
+ },
13329
+ get selectedKey() {
13330
+ return props.selectedKey;
13331
+ },
13332
+ get defaultSelectedKey() {
13333
+ return findInitialSelectedKey();
13334
+ },
13335
+ onSelectionChange(key) {
13336
+ if (key != null) props.onSelectionChange?.(key);
13337
+ }
13338
+ });
13339
+ const keyMaps = createMemo(() => {
13340
+ const coll = singleSelectListState.collection();
13341
+ const indexMap = /* @__PURE__ */ new Map();
13342
+ const keysLinkedList = /* @__PURE__ */ new Map();
13343
+ let i = 0;
13344
+ let prev;
13345
+ for (const item of coll) {
13346
+ indexMap.set(item.key, i);
13347
+ keysLinkedList.set(item.key, prev?.key);
13348
+ prev = item;
13349
+ i++;
13350
+ }
13351
+ return {
13352
+ indexMap,
13353
+ keysLinkedList
13354
+ };
13355
+ });
12777
13356
  const setLastCompletedStep = (key) => {
12778
- const currentIndex = indexMap().get(key);
12779
- const prevIndex = lastCompletedStep() !== null ? indexMap().get(lastCompletedStep()) : -1;
13357
+ if (key == null) {
13358
+ if (props.lastCompletedStep === void 0) setLastCompletedStepInternal(null);
13359
+ props.onLastCompletedStepChange?.(null);
13360
+ return;
13361
+ }
13362
+ const { indexMap } = keyMaps();
13363
+ const currentIndex = indexMap.get(key);
13364
+ const completed = lastCompletedStep();
13365
+ const prevIndex = completed !== null ? indexMap.get(completed) : -1;
12780
13366
  if (currentIndex !== void 0 && (prevIndex === void 0 || currentIndex > (prevIndex ?? -1))) {
12781
13367
  if (props.lastCompletedStep === void 0) setLastCompletedStepInternal(key);
12782
13368
  props.onLastCompletedStepChange?.(key);
12783
13369
  }
12784
13370
  };
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
13371
  createEffect(() => {
12833
- const selKey = selectedKey();
13372
+ const selKey = singleSelectListState.selectedKey();
12834
13373
  if (selKey === null) return null;
12835
- const selIdx = indexMap().get(selKey);
13374
+ const { indexMap, keysLinkedList } = keyMaps();
13375
+ const selIdx = indexMap.get(selKey);
12836
13376
  if (selIdx === void 0 || selIdx <= 0) return null;
12837
13377
  const completed = lastCompletedStep();
12838
- if (selIdx <= (completed !== null ? indexMap().get(completed) ?? -1 : -1) + 1) return null;
12839
- return items()[selIdx - 1]?.key;
13378
+ if (selIdx <= (completed !== null ? indexMap.get(completed) ?? -1 : -1) + 1) return null;
13379
+ return keysLinkedList.get(selKey) ?? null;
12840
13380
  }, (prevKey) => {
12841
- if (prevKey !== void 0 && prevKey !== null) setLastCompletedStep(prevKey);
13381
+ if (prevKey != null) setLastCompletedStep(prevKey);
13382
+ });
13383
+ createEffect(() => singleSelectListState.selectedKey(), (selKey) => {
13384
+ if (singleSelectListState.focusedKey() == null && selKey != null) singleSelectListState.setFocusedKey(selKey);
13385
+ });
13386
+ const setSelectedKey = (key) => {
13387
+ if (isListDisabled() || isReadOnly()) return;
13388
+ if (!isSelectable(key)) return;
13389
+ const { keysLinkedList } = keyMaps();
13390
+ const prevKey = keysLinkedList.get(key);
13391
+ if (prevKey && !isCompleted(prevKey)) setLastCompletedStep(prevKey);
13392
+ singleSelectListState.setSelectedKey(key);
13393
+ };
13394
+ singleSelectListState.selectionManager.canSelectItem = (key) => isSelectable(key);
13395
+ const origReplaceSelection = singleSelectListState.selectionManager.replaceSelection.bind(singleSelectListState.selectionManager);
13396
+ singleSelectListState.selectionManager.replaceSelection = (key) => {
13397
+ if (isListDisabled() || isReadOnly()) return;
13398
+ if (!isSelectable(key)) return;
13399
+ const { keysLinkedList } = keyMaps();
13400
+ const prevKey = keysLinkedList.get(key);
13401
+ if (prevKey && !isCompleted(prevKey)) setLastCompletedStep(prevKey);
13402
+ origReplaceSelection(key);
13403
+ };
13404
+ const isDisabledFn = ((key) => {
13405
+ if (key === void 0) return isListDisabled();
13406
+ return !isSelectable(key);
12842
13407
  });
12843
13408
  return {
12844
- selectedKey,
13409
+ ...singleSelectListState,
13410
+ selectedKey: singleSelectListState.selectedKey,
12845
13411
  lastCompletedStep,
12846
13412
  items,
12847
13413
  setSelectedKey,
12848
13414
  setLastCompletedStep,
12849
13415
  isCompleted,
12850
13416
  isSelectable,
12851
- isDisabled,
13417
+ isDisabled: isDisabledFn,
12852
13418
  isReadOnly
12853
13419
  };
12854
13420
  }
12855
13421
  //#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 };
13422
+ 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
13423
 
12858
13424
  //# sourceMappingURL=index.js.map