@proyecto-viviana/solid-stately 0.3.0 → 0.4.0

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 (63) hide show
  1. package/dist/calendar/IncompleteDate.d.ts +61 -0
  2. package/dist/calendar/createCalendarState.d.ts +20 -10
  3. package/dist/calendar/createDateFieldState.d.ts +10 -3
  4. package/dist/calendar/createRangeCalendarState.d.ts +10 -2
  5. package/dist/calendar/index.d.ts +1 -1
  6. package/dist/collections/createListState.d.ts +4 -1
  7. package/dist/collections/createSelectionState.d.ts +17 -5
  8. package/dist/collections/getChildNodes.d.ts +11 -0
  9. package/dist/collections/index.d.ts +2 -1
  10. package/dist/combobox/createComboBoxState.d.ts +7 -0
  11. package/dist/dnd/index.d.ts +1 -1
  12. package/dist/dnd/types.d.ts +3 -1
  13. package/dist/grid/types.d.ts +17 -3
  14. package/dist/index.d.ts +6 -6
  15. package/dist/index.js +1570 -425
  16. package/dist/index.js.map +1 -1
  17. package/dist/select/createSelectState.d.ts +6 -0
  18. package/dist/selection/Selection.d.ts +16 -0
  19. package/dist/selection/SelectionManager.d.ts +110 -0
  20. package/dist/selection/createMultipleSelectionState.d.ts +59 -0
  21. package/dist/selection/index.d.ts +1 -0
  22. package/dist/table/TableCollection.d.ts +30 -0
  23. package/dist/table/createTreeGridState.d.ts +15 -0
  24. package/dist/table/index.d.ts +2 -1
  25. package/dist/table/types.d.ts +58 -0
  26. package/dist/toast/createToastState.d.ts +16 -0
  27. package/dist/toast/index.d.ts +1 -1
  28. package/dist/tree/types.d.ts +14 -6
  29. package/package.json +1 -1
  30. package/src/calendar/IncompleteDate.ts +430 -0
  31. package/src/calendar/createCalendarState.ts +104 -37
  32. package/src/calendar/createDateFieldState.ts +262 -261
  33. package/src/calendar/createRangeCalendarState.ts +131 -19
  34. package/src/calendar/index.ts +2 -0
  35. package/src/collections/createListState.ts +50 -19
  36. package/src/collections/createMenuState.ts +3 -0
  37. package/src/collections/createSelectionState.ts +38 -28
  38. package/src/collections/getChildNodes.ts +98 -0
  39. package/src/collections/index.ts +9 -0
  40. package/src/combobox/createComboBoxState.ts +8 -0
  41. package/src/dnd/index.d.ts +1 -0
  42. package/src/dnd/index.ts +1 -0
  43. package/src/dnd/types.d.ts +3 -1
  44. package/src/dnd/types.ts +4 -1
  45. package/src/grid/createGridState.ts +36 -2
  46. package/src/grid/types.ts +17 -3
  47. package/src/index.d.ts +1 -0
  48. package/src/index.ts +11 -0
  49. package/src/select/createSelectState.ts +15 -2
  50. package/src/selection/Selection.ts +28 -0
  51. package/src/selection/SelectionManager.ts +526 -0
  52. package/src/selection/createMultipleSelectionState.ts +247 -0
  53. package/src/selection/index.ts +6 -0
  54. package/src/table/TableCollection.ts +213 -43
  55. package/src/table/createTableState.ts +20 -0
  56. package/src/table/createTreeGridState.ts +146 -0
  57. package/src/table/index.ts +3 -0
  58. package/src/table/types.ts +66 -0
  59. package/src/tabs/createTabListState.ts +11 -9
  60. package/src/toast/createToastState.ts +43 -10
  61. package/src/toast/index.ts +1 -0
  62. package/src/tree/createTreeState.ts +37 -6
  63. package/src/tree/types.ts +14 -6
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { createComputed, createContext, createEffect, createMemo, createSignal, createUniqueId, on, onCleanup, untrack, useContext } from "solid-js";
2
2
  import { isServer as isServer$1 } from "solid-js/web";
3
- import { CalendarDate as CalendarDateClass, CalendarDateTime as CalendarDateTimeClass, DateFormatter, DateFormatter as DateFormatter$1, GregorianCalendar, GregorianCalendar as GregorianCalendar$1, Time, Time as TimeClass, ZonedDateTime as ZonedDateTimeClass, createCalendar, createCalendar as createCalendar$1, endOfMonth, endOfMonth as endOfMonth$1, endOfWeek, endOfYear, getDayOfWeek, getLocalTimeZone, getLocalTimeZone as getLocalTimeZone$1, getWeeksInMonth, getWeeksInMonth as getWeeksInMonth$1, isEqualCalendar, isSameDay, isSameDay as isSameDay$1, isSameMonth, isSameMonth as isSameMonth$1, isSameYear, isToday, isWeekend, maxDate, minDate, now, parseAbsolute, parseAbsoluteToLocal, parseDate, parseDateTime, parseTime, parseZonedDateTime, startOfMonth, startOfMonth as startOfMonth$1, startOfWeek, startOfWeek as startOfWeek$1, startOfYear, toCalendar, toCalendar as toCalendar$1, toCalendarDate, toCalendarDate as toCalendarDate$1, toCalendarDateTime, toCalendarDateTime as toCalendarDateTime$1, toTime, toZoned, toZoned as toZoned$1, today, today as today$1 } from "@internationalized/date";
3
+ import { CalendarDate, CalendarDate as CalendarDateClass, CalendarDateTime as CalendarDateTimeClass, DateFormatter, DateFormatter as DateFormatter$1, GregorianCalendar, GregorianCalendar as GregorianCalendar$1, Time, Time as TimeClass, ZonedDateTime as ZonedDateTimeClass, createCalendar, createCalendar as createCalendar$1, endOfMonth, endOfMonth as endOfMonth$1, endOfWeek, endOfYear, getDayOfWeek, getLocalTimeZone, getLocalTimeZone as getLocalTimeZone$1, getWeeksInMonth, getWeeksInMonth as getWeeksInMonth$1, isEqualCalendar, isSameDay, isSameDay as isSameDay$1, isSameMonth, isSameMonth as isSameMonth$1, isSameYear, isToday, isWeekend, maxDate, maxDate as maxDate$1, minDate, minDate as minDate$1, now, parseAbsolute, parseAbsoluteToLocal, parseDate, parseDateTime, parseTime, parseZonedDateTime, startOfMonth, startOfMonth as startOfMonth$1, startOfWeek, startOfWeek as startOfWeek$1, startOfYear, toCalendar, toCalendar as toCalendar$1, toCalendarDate, toCalendarDate as toCalendarDate$1, toCalendarDateTime, toCalendarDateTime as toCalendarDateTime$1, toTime, toZoned, toZoned as toZoned$1, today, today as today$1 } from "@internationalized/date";
4
4
  //#region src/utils/reactivity.ts
5
5
  /**
6
6
  * Unwraps a MaybeAccessor to get the underlying value.
@@ -863,21 +863,11 @@ function createSelectionState(props = {}) {
863
863
  const rangeKeys = keys.slice(start, end + 1).filter((k) => !isDisabled(k));
864
864
  updateSelection(new Set(rangeKeys));
865
865
  };
866
- const select = (key, e, collection) => {
867
- if (isDisabled(key)) return;
866
+ const select = (key, e) => {
868
867
  if (selectionMode() === "none") return;
869
- const mode = selectionMode();
870
- const behavior = selectionBehavior();
871
- if (mode === "single") {
872
- if (behavior === "replace" || !isSelected(key)) replaceSelection(key);
873
- else if (!disallowEmptySelection()) clearSelection();
874
- return;
875
- }
876
- if (e?.shiftKey && collection) {
877
- extendSelection(key, collection);
878
- return;
879
- }
880
- if (e?.ctrlKey || e?.metaKey || behavior === "toggle") toggleSelection(key);
868
+ if (selectionMode() === "single") if (isSelected(key) && !disallowEmptySelection()) toggleSelection(key);
869
+ else replaceSelection(key);
870
+ else if (selectionBehavior() === "toggle" || e != null && (e.pointerType === "touch" || e.pointerType === "virtual")) toggleSelection(key);
881
871
  else replaceSelection(key);
882
872
  };
883
873
  const setSelectionBehavior = (behavior) => {
@@ -914,6 +904,477 @@ function normalizeSelection$2(keys) {
914
904
  return new Set(keys);
915
905
  }
916
906
  //#endregion
907
+ //#region src/selection/Selection.ts
908
+ /**
909
+ * A Selection is a special Set containing Keys, which also has an anchor
910
+ * and current selected key for use when range selecting.
911
+ */
912
+ var Selection = class Selection extends Set {
913
+ anchorKey;
914
+ currentKey;
915
+ constructor(keys, anchorKey, currentKey) {
916
+ super(keys);
917
+ if (keys instanceof Selection) {
918
+ this.anchorKey = anchorKey ?? keys.anchorKey;
919
+ this.currentKey = currentKey ?? keys.currentKey;
920
+ } else {
921
+ this.anchorKey = anchorKey ?? null;
922
+ this.currentKey = currentKey ?? null;
923
+ }
924
+ }
925
+ };
926
+ //#endregion
927
+ //#region src/selection/createMultipleSelectionState.ts
928
+ /**
929
+ * Raw multiple-selection + focus state for a collection.
930
+ *
931
+ * The lower of the two selection layers ported from upstream
932
+ * (`@react-stately/selection`'s `useMultipleSelectionState`): it owns the
933
+ * selected keys, focus, selection behavior, and disabled keys, but is NOT
934
+ * collection-aware. The collection-aware operations (toggle, extend, select
935
+ * all, range, etc.) live in {@link SelectionManager}, which is built on top of
936
+ * this state.
937
+ *
938
+ * Based on @react-stately/selection.
939
+ */
940
+ function equalSets(setA, setB) {
941
+ if (setA.size !== setB.size) return false;
942
+ for (const item of setA) if (!setB.has(item)) return false;
943
+ return true;
944
+ }
945
+ function convertSelection(selection, defaultValue) {
946
+ if (!selection) return defaultValue;
947
+ return selection === "all" ? "all" : new Selection(selection);
948
+ }
949
+ /**
950
+ * Manages raw state for multiple selection and focus in a collection.
951
+ */
952
+ function createMultipleSelectionState(props = {}) {
953
+ const getProps = () => access(props);
954
+ const [isFocused, setIsFocused] = createSignal(false);
955
+ const [focusedKey, setFocusedKeySignal] = createSignal(null);
956
+ const [childFocusStrategy, setChildFocusStrategy] = createSignal(null);
957
+ const [uncontrolledSelectedKeys, setUncontrolledSelectedKeys] = createSignal(convertSelection(getProps().defaultSelectedKeys, new Selection()));
958
+ const selectedKeys = createMemo(() => {
959
+ const p = getProps();
960
+ if (p.selectedKeys !== void 0) return convertSelection(p.selectedKeys, new Selection());
961
+ return uncontrolledSelectedKeys();
962
+ });
963
+ const selectionBehaviorProp = () => getProps().selectionBehavior ?? "toggle";
964
+ const [selectionBehavior, setSelectionBehaviorState] = createSignal(selectionBehaviorProp());
965
+ createComputed(() => {
966
+ const keys = selectedKeys();
967
+ if (selectionBehaviorProp() === "replace" && untrack(selectionBehavior) === "toggle" && typeof keys === "object" && keys.size === 0) setSelectionBehaviorState("replace");
968
+ });
969
+ let lastSelectionBehavior = selectionBehaviorProp();
970
+ createComputed(() => {
971
+ const prop = selectionBehaviorProp();
972
+ if (prop !== lastSelectionBehavior) {
973
+ setSelectionBehaviorState(prop);
974
+ lastSelectionBehavior = prop;
975
+ }
976
+ });
977
+ const disabledKeys = createMemo(() => {
978
+ const keys = getProps().disabledKeys;
979
+ return keys ? new Set(keys) : /* @__PURE__ */ new Set();
980
+ });
981
+ let lastSelectionEvent = null;
982
+ const emitSelectionEvent = (keys) => {
983
+ lastSelectionEvent = keys;
984
+ getProps().onSelectionChange?.(keys);
985
+ };
986
+ const setSelectedKeys = (keys) => {
987
+ const p = getProps();
988
+ const current = selectedKeys();
989
+ if (!(p.allowDuplicateSelectionEvents || keys === "all" || current === "all" || !equalSets(keys, current))) return;
990
+ if (p.selectedKeys === void 0) setUncontrolledSelectedKeys(keys);
991
+ emitSelectionEvent(keys);
992
+ };
993
+ const emitDuplicateSelectionEvent = (keys) => {
994
+ const eventKeys = keys ?? lastSelectionEvent;
995
+ if (eventKeys == null) return;
996
+ emitSelectionEvent(eventKeys);
997
+ };
998
+ return {
999
+ get selectionMode() {
1000
+ return getProps().selectionMode ?? "none";
1001
+ },
1002
+ get selectionBehavior() {
1003
+ return selectionBehavior();
1004
+ },
1005
+ setSelectionBehavior(behavior) {
1006
+ setSelectionBehaviorState(behavior);
1007
+ },
1008
+ get disallowEmptySelection() {
1009
+ return getProps().disallowEmptySelection ?? false;
1010
+ },
1011
+ get isFocused() {
1012
+ return isFocused();
1013
+ },
1014
+ setFocused(f) {
1015
+ setIsFocused(f);
1016
+ },
1017
+ get focusedKey() {
1018
+ return focusedKey();
1019
+ },
1020
+ get childFocusStrategy() {
1021
+ return childFocusStrategy();
1022
+ },
1023
+ setFocusedKey(key, strategy = "first") {
1024
+ setFocusedKeySignal(key);
1025
+ setChildFocusStrategy(strategy);
1026
+ },
1027
+ get selectedKeys() {
1028
+ return selectedKeys();
1029
+ },
1030
+ get lastSelectionEvent() {
1031
+ return lastSelectionEvent;
1032
+ },
1033
+ setSelectedKeys,
1034
+ emitDuplicateSelectionEvent,
1035
+ get disabledKeys() {
1036
+ return disabledKeys();
1037
+ },
1038
+ get disabledBehavior() {
1039
+ return getProps().disabledBehavior ?? "all";
1040
+ }
1041
+ };
1042
+ }
1043
+ //#endregion
1044
+ //#region src/collections/getChildNodes.ts
1045
+ function getChildNodes(node, collection) {
1046
+ if (typeof collection.getChildren === "function") return collection.getChildren(node.key);
1047
+ return node.childNodes;
1048
+ }
1049
+ function getFirstItem(iterable) {
1050
+ return getNthItem(iterable, 0);
1051
+ }
1052
+ function getNthItem(iterable, index) {
1053
+ if (index < 0) return;
1054
+ let i = 0;
1055
+ for (const item of iterable) {
1056
+ if (i === index) return item;
1057
+ i++;
1058
+ }
1059
+ }
1060
+ function compareNodeOrder(collection, a, b) {
1061
+ if (a.parentKey === b.parentKey) return a.index - b.index;
1062
+ const aAncestors = [...getAncestors(collection, a), a];
1063
+ const bAncestors = [...getAncestors(collection, b), b];
1064
+ const firstNonMatchingAncestor = aAncestors.slice(0, bAncestors.length).findIndex((a, i) => a !== bAncestors[i]);
1065
+ if (firstNonMatchingAncestor !== -1) {
1066
+ a = aAncestors[firstNonMatchingAncestor];
1067
+ b = bAncestors[firstNonMatchingAncestor];
1068
+ return a.index - b.index;
1069
+ }
1070
+ if (aAncestors.findIndex((node) => node === b) >= 0) return 1;
1071
+ else if (bAncestors.findIndex((node) => node === a) >= 0) return -1;
1072
+ return -1;
1073
+ }
1074
+ function getAncestors(collection, node) {
1075
+ const parents = [];
1076
+ let currNode = node;
1077
+ while (currNode?.parentKey != null) {
1078
+ currNode = collection.getItem(currNode.parentKey);
1079
+ if (currNode) parents.unshift(currNode);
1080
+ }
1081
+ return parents;
1082
+ }
1083
+ //#endregion
1084
+ //#region src/selection/SelectionManager.ts
1085
+ /**
1086
+ * An interface for reading and updating multiple selection state.
1087
+ *
1088
+ * The upper of the two selection layers ported from upstream
1089
+ * (`@react-stately/selection`'s `SelectionManager`): the collection-aware
1090
+ * operations built on top of the raw {@link MultipleSelectionState}.
1091
+ *
1092
+ * React → Solid adaptations, all faithful to upstream's observable contract:
1093
+ * - Upstream re-instantiates `SelectionManager` every render to capture a
1094
+ * fresh `collection`. Here the manager is long-lived, so it captures a
1095
+ * reactive `collection` accessor and reads it live via `get collection()`.
1096
+ * - Upstream caches `_isSelectAll` for the lifetime of one render. A
1097
+ * long-lived manager would let that cache go stale, so it is dropped and
1098
+ * `isSelectAll` recomputes (it is consulted rarely — only by select-all UI).
1099
+ * - Our `CollectionNode` has no `'cell'` type (grids/trees use a separate
1100
+ * state engine), so the cell-selection branches are omitted.
1101
+ *
1102
+ * Based on @react-stately/selection.
1103
+ */
1104
+ /**
1105
+ * An interface for reading and updating multiple selection state.
1106
+ */
1107
+ var SelectionManager = class SelectionManager {
1108
+ collectionAccessor;
1109
+ state;
1110
+ layoutDelegate;
1111
+ fullCollection;
1112
+ constructor(collection, state, options) {
1113
+ this.collectionAccessor = collection;
1114
+ this.state = state;
1115
+ this.layoutDelegate = options?.layoutDelegate ?? null;
1116
+ this.fullCollection = options?.fullCollection ?? null;
1117
+ }
1118
+ /** The collection the manager operates over (read live so it stays reactive). */
1119
+ get collection() {
1120
+ return this.collectionAccessor();
1121
+ }
1122
+ /** The type of selection that is allowed in the collection. */
1123
+ get selectionMode() {
1124
+ return this.state.selectionMode;
1125
+ }
1126
+ /** Whether the collection allows empty selection. */
1127
+ get disallowEmptySelection() {
1128
+ return this.state.disallowEmptySelection;
1129
+ }
1130
+ /** The selection behavior for the collection. */
1131
+ get selectionBehavior() {
1132
+ return this.state.selectionBehavior;
1133
+ }
1134
+ /** Sets the selection behavior for the collection. */
1135
+ setSelectionBehavior(selectionBehavior) {
1136
+ this.state.setSelectionBehavior(selectionBehavior);
1137
+ }
1138
+ /** Whether the collection is currently focused. */
1139
+ get isFocused() {
1140
+ return this.state.isFocused;
1141
+ }
1142
+ /** Sets whether the collection is focused. */
1143
+ setFocused(isFocused) {
1144
+ this.state.setFocused(isFocused);
1145
+ }
1146
+ /** The current focused key in the collection. */
1147
+ get focusedKey() {
1148
+ return this.state.focusedKey;
1149
+ }
1150
+ /** Whether the first or last child of the focused key should receive focus. */
1151
+ get childFocusStrategy() {
1152
+ return this.state.childFocusStrategy;
1153
+ }
1154
+ /** Sets the focused key. */
1155
+ setFocusedKey(key, childFocusStrategy) {
1156
+ if (key == null || this.collection.getItem(key)) this.state.setFocusedKey(key, childFocusStrategy);
1157
+ }
1158
+ /** The currently selected keys in the collection (materialized — 'all' is expanded). */
1159
+ get selectedKeys() {
1160
+ const keys = this.state.selectedKeys;
1161
+ return keys === "all" ? new Set(this.getSelectAllKeys()) : keys;
1162
+ }
1163
+ /** The raw selection value for the collection. Either 'all' for select all, or a set of keys. */
1164
+ get rawSelection() {
1165
+ return this.state.selectedKeys;
1166
+ }
1167
+ /** Last selection payload emitted through onSelectionChange. @internal */
1168
+ get lastSelectionEvent() {
1169
+ return this.state.lastSelectionEvent;
1170
+ }
1171
+ /** Re-emits a selection payload without recomputing selection. @internal */
1172
+ emitDuplicateSelectionEvent(keys = this.state.lastSelectionEvent) {
1173
+ if (keys == null || this.selectionMode === "none") return;
1174
+ this.state.emitDuplicateSelectionEvent(keys);
1175
+ }
1176
+ /** Returns whether a key is selected. */
1177
+ isSelected(key) {
1178
+ if (this.state.selectionMode === "none") return false;
1179
+ const mappedKey = this.getKey(key);
1180
+ if (mappedKey == null) return false;
1181
+ const selectedKeys = this.state.selectedKeys;
1182
+ return selectedKeys === "all" ? this.canSelectItem(mappedKey) : selectedKeys.has(mappedKey);
1183
+ }
1184
+ /** Whether the selection is empty. */
1185
+ get isEmpty() {
1186
+ return this.state.selectedKeys !== "all" && this.state.selectedKeys.size === 0;
1187
+ }
1188
+ /** Whether all items in the collection are selected. */
1189
+ get isSelectAll() {
1190
+ if (this.isEmpty) return false;
1191
+ const selectedKeys = this.state.selectedKeys;
1192
+ if (selectedKeys === "all") return true;
1193
+ return this.getSelectAllKeys().every((k) => selectedKeys.has(k));
1194
+ }
1195
+ get firstSelectedKey() {
1196
+ let first = null;
1197
+ for (const key of this.state.selectedKeys) {
1198
+ const item = this.collection.getItem(key);
1199
+ if (!first || item && compareNodeOrder(this.collection, item, first) < 0) first = item;
1200
+ }
1201
+ return first?.key ?? null;
1202
+ }
1203
+ get lastSelectedKey() {
1204
+ let last = null;
1205
+ for (const key of this.state.selectedKeys) {
1206
+ const item = this.collection.getItem(key);
1207
+ if (!last || item && compareNodeOrder(this.collection, item, last) > 0) last = item;
1208
+ }
1209
+ return last?.key ?? null;
1210
+ }
1211
+ get disabledKeys() {
1212
+ return this.state.disabledKeys;
1213
+ }
1214
+ get disabledBehavior() {
1215
+ return this.state.disabledBehavior;
1216
+ }
1217
+ /** Extends the selection to the given key. */
1218
+ extendSelection(toKey) {
1219
+ if (this.selectionMode === "none") return;
1220
+ if (this.selectionMode === "single") {
1221
+ this.replaceSelection(toKey);
1222
+ return;
1223
+ }
1224
+ const mappedToKey = this.getKey(toKey);
1225
+ if (mappedToKey == null) return;
1226
+ let selection;
1227
+ const currentSelection = this.state.selectedKeys;
1228
+ if (currentSelection === "all") selection = new Selection([mappedToKey], mappedToKey, mappedToKey);
1229
+ else {
1230
+ const selectedKeys = currentSelection;
1231
+ const anchorKey = selectedKeys.anchorKey ?? mappedToKey;
1232
+ selection = new Selection(selectedKeys, anchorKey, mappedToKey);
1233
+ for (const key of this.getKeyRange(anchorKey, selectedKeys.currentKey ?? mappedToKey)) selection.delete(key);
1234
+ for (const key of this.getKeyRange(mappedToKey, anchorKey)) if (this.canSelectItem(key)) selection.add(key);
1235
+ }
1236
+ this.state.setSelectedKeys(selection);
1237
+ }
1238
+ getKeyRange(from, to) {
1239
+ const fromItem = this.collection.getItem(from);
1240
+ const toItem = this.collection.getItem(to);
1241
+ if (fromItem && toItem) {
1242
+ if (compareNodeOrder(this.collection, fromItem, toItem) <= 0) return this.getKeyRangeInternal(from, to);
1243
+ return this.getKeyRangeInternal(to, from);
1244
+ }
1245
+ return [];
1246
+ }
1247
+ getKeyRangeInternal(from, to) {
1248
+ if (this.layoutDelegate?.getKeyRange) return this.layoutDelegate.getKeyRange(from, to);
1249
+ const keys = [];
1250
+ let key = from;
1251
+ while (key != null) {
1252
+ const item = this.collection.getItem(key);
1253
+ if (item && item.type === "item") keys.push(key);
1254
+ if (key === to) return keys;
1255
+ key = this.collection.getKeyAfter(key);
1256
+ }
1257
+ return [];
1258
+ }
1259
+ getKey(key) {
1260
+ let item = this.collection.getItem(key);
1261
+ if (!item) return key;
1262
+ while (item && item.type !== "item" && item.parentKey != null) item = this.collection.getItem(item.parentKey);
1263
+ if (!item || item.type !== "item") return null;
1264
+ return item.key;
1265
+ }
1266
+ /** Toggles whether the given key is selected. */
1267
+ toggleSelection(key) {
1268
+ if (this.selectionMode === "none") return;
1269
+ if (this.selectionMode === "single" && !this.isSelected(key)) {
1270
+ this.replaceSelection(key);
1271
+ return;
1272
+ }
1273
+ const mappedKey = this.getKey(key);
1274
+ if (mappedKey == null) return;
1275
+ const keys = new Selection(this.state.selectedKeys === "all" ? this.getSelectAllKeys() : this.state.selectedKeys);
1276
+ if (keys.has(mappedKey)) keys.delete(mappedKey);
1277
+ else if (this.canSelectItem(mappedKey)) {
1278
+ keys.add(mappedKey);
1279
+ keys.anchorKey = mappedKey;
1280
+ keys.currentKey = mappedKey;
1281
+ }
1282
+ if (this.disallowEmptySelection && keys.size === 0) return;
1283
+ this.state.setSelectedKeys(keys);
1284
+ }
1285
+ /** Replaces the selection with only the given key. */
1286
+ replaceSelection(key) {
1287
+ if (this.selectionMode === "none") return;
1288
+ const mappedKey = this.getKey(key);
1289
+ if (mappedKey == null) return;
1290
+ const selection = this.canSelectItem(mappedKey) ? new Selection([mappedKey], mappedKey, mappedKey) : new Selection();
1291
+ this.state.setSelectedKeys(selection);
1292
+ }
1293
+ /** Replaces the selection with the given keys. */
1294
+ setSelectedKeys(keys) {
1295
+ if (this.selectionMode === "none") return;
1296
+ const selection = new Selection();
1297
+ for (const key of keys) {
1298
+ const mappedKey = this.getKey(key);
1299
+ if (mappedKey != null) {
1300
+ selection.add(mappedKey);
1301
+ if (this.selectionMode === "single") break;
1302
+ }
1303
+ }
1304
+ this.state.setSelectedKeys(selection);
1305
+ }
1306
+ getSelectAllKeys() {
1307
+ const collection = this.fullCollection ?? this.collection;
1308
+ const keys = [];
1309
+ const addKeys = (key) => {
1310
+ while (key != null) {
1311
+ if (this.canSelectItemIn(key, collection)) {
1312
+ const item = collection.getItem(key);
1313
+ if (item?.type === "item") keys.push(key);
1314
+ if (item?.hasChildNodes && item.type !== "item") addKeys(getFirstItem(getChildNodes(item, collection))?.key ?? null);
1315
+ }
1316
+ key = collection.getKeyAfter(key);
1317
+ }
1318
+ };
1319
+ addKeys(collection.getFirstKey());
1320
+ return keys;
1321
+ }
1322
+ /** Selects all items in the collection. */
1323
+ selectAll() {
1324
+ if (!this.isSelectAll && this.selectionMode === "multiple") this.state.setSelectedKeys("all");
1325
+ }
1326
+ /** Removes all keys from the selection. */
1327
+ clearSelection() {
1328
+ if (!this.disallowEmptySelection && (this.state.selectedKeys === "all" || this.state.selectedKeys.size > 0)) this.state.setSelectedKeys(new Selection());
1329
+ }
1330
+ /** Toggles between select all and an empty selection. */
1331
+ toggleSelectAll() {
1332
+ if (this.isSelectAll) this.clearSelection();
1333
+ else this.selectAll();
1334
+ }
1335
+ select(key, e) {
1336
+ if (this.selectionMode === "none") return;
1337
+ if (this.selectionMode === "single") if (this.isSelected(key) && !this.disallowEmptySelection) this.toggleSelection(key);
1338
+ else this.replaceSelection(key);
1339
+ else if (this.selectionBehavior === "toggle" || e && (e.pointerType === "touch" || e.pointerType === "virtual")) this.toggleSelection(key);
1340
+ else this.replaceSelection(key);
1341
+ }
1342
+ /** Returns whether the current selection is equal to the given selection. */
1343
+ isSelectionEqual(selection) {
1344
+ if (selection === this.state.selectedKeys) return true;
1345
+ const selectedKeys = this.selectedKeys;
1346
+ if (selection.size !== selectedKeys.size) return false;
1347
+ for (const key of selection) if (!selectedKeys.has(key)) return false;
1348
+ for (const key of selectedKeys) if (!selection.has(key)) return false;
1349
+ return true;
1350
+ }
1351
+ canSelectItem(key) {
1352
+ return this.canSelectItemIn(key, this.collection);
1353
+ }
1354
+ canSelectItemIn(key, collection) {
1355
+ if (this.state.selectionMode === "none" || this.state.disabledKeys.has(key)) return false;
1356
+ const item = collection.getItem(key);
1357
+ if (!item || item.isDisabled || item.props?.isDisabled) return false;
1358
+ return true;
1359
+ }
1360
+ isDisabled(key) {
1361
+ const item = this.collection.getItem(key);
1362
+ return this.state.disabledBehavior === "all" && (this.state.disabledKeys.has(key) || !!item?.props?.isDisabled) && item?.props?.disabledBehavior !== "selection";
1363
+ }
1364
+ isLink(key) {
1365
+ return !!this.collection.getItem(key)?.props?.href;
1366
+ }
1367
+ getItemProps(key) {
1368
+ return this.collection.getItem(key)?.props;
1369
+ }
1370
+ withCollection(collection) {
1371
+ return new SelectionManager(() => collection, this.state, {
1372
+ layoutDelegate: this.layoutDelegate ?? void 0,
1373
+ fullCollection: this.fullCollection ?? this.collection
1374
+ });
1375
+ }
1376
+ };
1377
+ //#endregion
917
1378
  //#region src/collections/createListState.ts
918
1379
  /**
919
1380
  * State management for list-like components.
@@ -952,7 +1413,7 @@ function createListState(props) {
952
1413
  for (const node of coll) if (node.isDisabled) itemDisabled.push(node.key);
953
1414
  return [...new Set([...propsDisabled, ...itemDisabled])];
954
1415
  });
955
- const selectionState = createSelectionState({
1416
+ const state = createMultipleSelectionState({
956
1417
  get selectionMode() {
957
1418
  return getProps().selectionMode;
958
1419
  },
@@ -981,21 +1442,34 @@ function createListState(props) {
981
1442
  return getProps().allowDuplicateSelectionEvents;
982
1443
  }
983
1444
  });
984
- const [isFocused, setIsFocused] = createSignal(false);
985
- const [focusedKey, setFocusedKeyInternal] = createSignal(null);
986
- const [childFocusStrategy, setChildFocusStrategy] = createSignal(null);
987
- const setFocusedKey = (key, childStrategy) => {
988
- setFocusedKeyInternal(key);
989
- setChildFocusStrategy(childStrategy ?? null);
990
- };
1445
+ const selectionManager = new SelectionManager(() => collection(), state);
991
1446
  return {
992
1447
  collection,
993
- isFocused,
994
- setFocused: setIsFocused,
995
- focusedKey,
996
- setFocusedKey,
997
- childFocusStrategy,
998
- ...selectionState
1448
+ selectionManager,
1449
+ isFocused: () => state.isFocused,
1450
+ setFocused: (isFocused) => state.setFocused(isFocused),
1451
+ focusedKey: () => state.focusedKey,
1452
+ setFocusedKey: (key, childStrategy) => selectionManager.setFocusedKey(key, childStrategy),
1453
+ childFocusStrategy: () => state.childFocusStrategy,
1454
+ selectionMode: () => selectionManager.selectionMode,
1455
+ selectionBehavior: () => selectionManager.selectionBehavior,
1456
+ disallowEmptySelection: () => selectionManager.disallowEmptySelection,
1457
+ selectedKeys: () => state.selectedKeys,
1458
+ disabledKeys: () => selectionManager.disabledKeys,
1459
+ disabledBehavior: () => selectionManager.disabledBehavior,
1460
+ isEmpty: () => selectionManager.isEmpty,
1461
+ isSelectAll: () => selectionManager.isSelectAll,
1462
+ isSelected: (key) => selectionManager.isSelected(key),
1463
+ isDisabled: (key) => selectionManager.isDisabled(key),
1464
+ setSelectionBehavior: (behavior) => selectionManager.setSelectionBehavior(behavior),
1465
+ toggleSelection: (key) => selectionManager.toggleSelection(key),
1466
+ replaceSelection: (key) => selectionManager.replaceSelection(key),
1467
+ setSelectedKeys: (keys) => selectionManager.setSelectedKeys(keys),
1468
+ selectAll: () => selectionManager.selectAll(),
1469
+ clearSelection: () => selectionManager.clearSelection(),
1470
+ toggleSelectAll: () => selectionManager.toggleSelectAll(),
1471
+ extendSelection: (toKey, _collection) => selectionManager.extendSelection(toKey),
1472
+ select: (key, e) => selectionManager.select(key, e)
999
1473
  };
1000
1474
  }
1001
1475
  /**
@@ -1111,6 +1585,9 @@ function createMenuState(props) {
1111
1585
  },
1112
1586
  get selectionBehavior() {
1113
1587
  return getProps().selectionBehavior;
1588
+ },
1589
+ get allowDuplicateSelectionEvents() {
1590
+ return getProps().allowDuplicateSelectionEvents;
1114
1591
  }
1115
1592
  });
1116
1593
  const close = () => {
@@ -1144,6 +1621,9 @@ const dayOfWeekNames$1 = [
1144
1621
  function createCalendarState(props = {}) {
1145
1622
  const timeZone = getLocalTimeZone$1();
1146
1623
  const locale = createMemo(() => access(props.locale) ?? "en-US");
1624
+ const minValueState = createMemo(() => access(props.minValue) ?? null);
1625
+ const maxValueState = createMemo(() => access(props.maxValue) ?? null);
1626
+ const selectionMode = createMemo(() => access(props.selectionMode) ?? "single");
1147
1627
  const resolvedOptions = createMemo(() => new DateFormatter$1(locale()).resolvedOptions());
1148
1628
  const calendar = createMemo(() => (props.createCalendar ?? createCalendar$1)(resolvedOptions().calendar));
1149
1629
  const visibleMonths = props.visibleMonths ?? 1;
@@ -1151,13 +1631,13 @@ function createCalendarState(props = {}) {
1151
1631
  const toDisplayCalendarDate = (date) => toCalendar$1(toCalendarDate$3(date), calendar());
1152
1632
  const selectionAlignmentOffset = () => {
1153
1633
  switch (props.selectionAlignment) {
1634
+ case "start": return 0;
1154
1635
  case "end": return Math.max(visibleMonths - 1, 0);
1155
- case "center": {
1636
+ default: {
1156
1637
  let halfDuration = Math.floor(visibleMonths / 2);
1157
1638
  if (halfDuration > 0 && visibleMonths % 2 === 0) halfDuration--;
1158
1639
  return halfDuration;
1159
1640
  }
1160
- default: return 0;
1161
1641
  }
1162
1642
  };
1163
1643
  const alignVisibleRangeStart = (date) => {
@@ -1182,8 +1662,10 @@ function createCalendarState(props = {}) {
1182
1662
  if (controlledFocused) return toDisplayCalendarDate(controlledFocused);
1183
1663
  if (props.defaultFocusedValue) return toDisplayCalendarDate(props.defaultFocusedValue);
1184
1664
  const controlledValue = access(props.value);
1185
- if (controlledValue) return toDisplayCalendarDate(controlledValue);
1186
- if (props.defaultValue) return toDisplayCalendarDate(props.defaultValue);
1665
+ const controlledFirst = Array.isArray(controlledValue) ? controlledValue[0] : controlledValue;
1666
+ if (controlledFirst) return toDisplayCalendarDate(controlledFirst);
1667
+ const defaultFirst = Array.isArray(props.defaultValue) ? props.defaultValue[0] : props.defaultValue;
1668
+ if (defaultFirst) return toDisplayCalendarDate(defaultFirst);
1187
1669
  return toCalendar$1(today$1(timeZone), calendar());
1188
1670
  };
1189
1671
  const initialFocusedDate = constrainDate(getInitialFocusedDate());
@@ -1198,6 +1680,7 @@ function createCalendarState(props = {}) {
1198
1680
  });
1199
1681
  const value = createMemo(() => {
1200
1682
  const currentValue = sourceValue();
1683
+ if (Array.isArray(currentValue)) return currentValue.map((v) => toDisplayCalendarDate(v));
1201
1684
  return currentValue ? toDisplayCalendarDate(currentValue) : null;
1202
1685
  });
1203
1686
  const isDisabled = createMemo(() => access(props.isDisabled) ?? false);
@@ -1263,7 +1746,11 @@ function createCalendarState(props = {}) {
1263
1746
  const setValue = (newValue) => {
1264
1747
  if (isDisabled() || isReadOnly()) return;
1265
1748
  const oldValue = sourceValue();
1266
- const nextValue = newValue ? convertValue$1(constrainDate(toDisplayCalendarDate(newValue)), oldValue) : null;
1749
+ const referenceValue = Array.isArray(oldValue) ? oldValue[0] ?? null : oldValue;
1750
+ let nextValue;
1751
+ if (newValue == null) nextValue = selectionMode() === "multiple" ? [] : null;
1752
+ else if (Array.isArray(newValue)) nextValue = newValue.map((v) => convertValue$1(constrainDate(toDisplayCalendarDate(v)), referenceValue));
1753
+ else nextValue = convertValue$1(constrainDate(toDisplayCalendarDate(newValue)), referenceValue);
1267
1754
  if (access(props.value) === void 0) setInternalValue(() => nextValue);
1268
1755
  if (!Object.is(oldValue, nextValue) && props.onChange) props.onChange(nextValue);
1269
1756
  };
@@ -1276,8 +1763,10 @@ function createCalendarState(props = {}) {
1276
1763
  };
1277
1764
  const isSelected = (date) => {
1278
1765
  const v = value();
1279
- if (!v) return false;
1280
- return isSameDay$1(toDisplayCalendarDate(date), toDisplayCalendarDate(v));
1766
+ if (v == null) return false;
1767
+ const target = toDisplayCalendarDate(date);
1768
+ if (Array.isArray(v)) return v.some((d) => isSameDay$1(target, toDisplayCalendarDate(d)));
1769
+ return isSameDay$1(target, toDisplayCalendarDate(v));
1281
1770
  };
1282
1771
  const isCellFocused = (date) => {
1283
1772
  return isSameDay$1(toDisplayCalendarDate(date), focusedDate());
@@ -1348,14 +1837,19 @@ function createCalendarState(props = {}) {
1348
1837
  const selectFocusedDate = () => {
1349
1838
  if (isReadOnly() || isDisabled()) return;
1350
1839
  const date = focusedDate();
1351
- if (!isCellDisabled(date) && !isCellUnavailable(date)) setValue(date);
1840
+ if (!isCellUnavailable(date)) selectDate(date);
1352
1841
  };
1353
1842
  const selectDate = (date) => {
1354
1843
  if (isReadOnly() || isDisabled()) return;
1355
- if (!isCellDisabled(date) && !isCellUnavailable(date)) {
1356
- setValue(date);
1357
- setFocusedDate(date);
1358
- }
1844
+ if (isCellDisabled(date) || isCellUnavailable(date)) return;
1845
+ if (selectionMode() === "multiple") {
1846
+ const current = value();
1847
+ const base = Array.isArray(current) ? current.map((v) => toDisplayCalendarDate(v)) : current != null ? [toDisplayCalendarDate(current)] : [];
1848
+ const target = toDisplayCalendarDate(date);
1849
+ const index = base.findIndex((d) => isSameDay$1(d, target));
1850
+ setValue(index >= 0 ? [...base.slice(0, index), ...base.slice(index + 1)] : [...base, target]);
1851
+ } else setValue(date);
1852
+ setFocusedDate(date);
1359
1853
  };
1360
1854
  const getDatesInWeek = (weekIndex, monthStartDate) => {
1361
1855
  const startDate = monthStartDate ?? visibleRange().start;
@@ -1373,6 +1867,7 @@ function createCalendarState(props = {}) {
1373
1867
  return getWeeksInMonth$1(date ?? focusedDate(), locale(), firstDayOfWeekName());
1374
1868
  };
1375
1869
  return {
1870
+ selectionMode,
1376
1871
  value,
1377
1872
  setValue,
1378
1873
  focusedDate,
@@ -1382,6 +1877,8 @@ function createCalendarState(props = {}) {
1382
1877
  visibleRange,
1383
1878
  timeZone,
1384
1879
  locale,
1880
+ minValue: minValueState,
1881
+ maxValue: maxValueState,
1385
1882
  firstDayOfWeek: firstDayOfWeekName,
1386
1883
  validationState,
1387
1884
  isValueInvalid,
@@ -1447,17 +1944,39 @@ const dayOfWeekNames = [
1447
1944
  function createRangeCalendarState(props = {}) {
1448
1945
  const timeZone = getLocalTimeZone$1();
1449
1946
  const locale = createMemo(() => access(props.locale) ?? "en-US");
1947
+ const minValueState = createMemo(() => access(props.minValue) ?? null);
1948
+ const maxValueState = createMemo(() => access(props.maxValue) ?? null);
1450
1949
  const resolvedOptions = createMemo(() => new DateFormatter$1(locale()).resolvedOptions());
1451
1950
  const calendar = createMemo(() => (props.createCalendar ?? createCalendar$1)(resolvedOptions().calendar));
1452
1951
  const visibleMonths = Math.max(1, Number(access(props.visibleMonths) ?? 1));
1453
1952
  const firstDayOfWeekName = () => props.firstDayOfWeek == null ? void 0 : dayOfWeekNames[props.firstDayOfWeek];
1454
1953
  const toDisplayCalendarDate = (date) => toCalendar$1(toCalendarDate$2(date), calendar());
1954
+ const [anchorDate, setAnchorDate] = createSignal(null);
1955
+ const getAvailableRange = (anchor) => {
1956
+ if (anchor && props.isDateUnavailable && !props.allowsNonContiguousRanges) {
1957
+ const predicate = (date) => props.isDateUnavailable(date, anchor);
1958
+ return {
1959
+ start: nextUnavailableDate(anchor, predicate, visibleMonths, -1),
1960
+ end: nextUnavailableDate(anchor, predicate, visibleMonths, 1)
1961
+ };
1962
+ }
1963
+ return null;
1964
+ };
1965
+ const availableRange = createMemo(() => getAvailableRange(anchorDate()));
1966
+ const effectiveMinValue = createMemo(() => {
1967
+ const min = access(props.minValue);
1968
+ return maxDate$1(min != null ? toDisplayCalendarDate(min) : void 0, availableRange()?.start) ?? null;
1969
+ });
1970
+ const effectiveMaxValue = createMemo(() => {
1971
+ const max = access(props.maxValue);
1972
+ return minDate$1(max != null ? toDisplayCalendarDate(max) : void 0, availableRange()?.end) ?? null;
1973
+ });
1455
1974
  const constrainDate = (date) => {
1456
- const minValue = access(props.minValue);
1457
- const maxValue = access(props.maxValue);
1975
+ const min = effectiveMinValue();
1976
+ const max = effectiveMaxValue();
1458
1977
  let constrained = date;
1459
- if (minValue && constrained.compare(toDisplayCalendarDate(minValue)) < 0) constrained = toDisplayCalendarDate(minValue);
1460
- if (maxValue && constrained.compare(toDisplayCalendarDate(maxValue)) > 0) constrained = toDisplayCalendarDate(maxValue);
1978
+ if (min && constrained.compare(min) < 0) constrained = min;
1979
+ if (max && constrained.compare(max) > 0) constrained = max;
1461
1980
  return constrained;
1462
1981
  };
1463
1982
  const visibleRangeEndFromStart = (start) => {
@@ -1514,7 +2033,6 @@ function createRangeCalendarState(props = {}) {
1514
2033
  const initialFocusedDate = constrainDate(getInitialFocusedDate());
1515
2034
  const [focusedDate, setFocusedDateInternal] = createSignal(initialFocusedDate);
1516
2035
  const [visibleRangeStart, setVisibleRangeStart] = createSignal(alignVisibleRangeStart(initialFocusedDate));
1517
- const [anchorDate, setAnchorDate] = createSignal(null);
1518
2036
  const [isFocused, setFocused] = createSignal(false);
1519
2037
  const [isDragging, setDragging] = createSignal(false);
1520
2038
  const sourceValue = createMemo(() => {
@@ -1532,7 +2050,17 @@ function createRangeCalendarState(props = {}) {
1532
2050
  const isDisabled = createMemo(() => access(props.isDisabled) ?? false);
1533
2051
  const isReadOnly = createMemo(() => access(props.isReadOnly) ?? false);
1534
2052
  const validationState = createMemo(() => access(props.validationState));
1535
- const isValueInvalid = createMemo(() => validationState() === "invalid");
2053
+ const isInvalidSelection = createMemo(() => {
2054
+ const v = sourceValue();
2055
+ if (!v || anchorDate()) return false;
2056
+ if (props.isDateUnavailable?.(v.start, null) || props.isDateUnavailable?.(v.end, null)) return true;
2057
+ const min = access(props.minValue);
2058
+ const max = access(props.maxValue);
2059
+ const minCal = min != null ? toDisplayCalendarDate(min) : null;
2060
+ const maxCal = max != null ? toDisplayCalendarDate(max) : null;
2061
+ return isDateOutsideRange(toDisplayCalendarDate(v.start), minCal, maxCal) || isDateOutsideRange(toDisplayCalendarDate(v.end), minCal, maxCal);
2062
+ });
2063
+ const isValueInvalid = createMemo(() => validationState() === "invalid" || isInvalidSelection());
1536
2064
  const highlightedRange = createMemo(() => {
1537
2065
  const anchor = anchorDate();
1538
2066
  if (!anchor) {
@@ -1643,24 +2171,24 @@ function createRangeCalendarState(props = {}) {
1643
2171
  return isSameDay$1(toDisplayCalendarDate(date), focusedDate());
1644
2172
  };
1645
2173
  const isCellUnavailable = (date) => {
1646
- return props.isDateUnavailable?.(date) ?? false;
2174
+ return props.isDateUnavailable?.(date, anchorDate()) ?? false;
1647
2175
  };
1648
2176
  const isCellDisabled = (date) => {
1649
2177
  if (isDisabled()) return true;
1650
2178
  if (props.isDateDisabled?.(date)) return true;
1651
- const minValue = access(props.minValue);
1652
- const maxValue = access(props.maxValue);
2179
+ const min = effectiveMinValue();
2180
+ const max = effectiveMaxValue();
1653
2181
  const calDate = toDisplayCalendarDate(date);
1654
- if (minValue && calDate.compare(toDisplayCalendarDate(minValue)) < 0) return true;
1655
- if (maxValue && calDate.compare(toDisplayCalendarDate(maxValue)) > 0) return true;
2182
+ if (min && calDate.compare(min) < 0) return true;
2183
+ if (max && calDate.compare(max) > 0) return true;
1656
2184
  return false;
1657
2185
  };
1658
2186
  const isDateOutsideAllowedRange = (date) => {
1659
- const minValue = access(props.minValue);
1660
- const maxValue = access(props.maxValue);
2187
+ const min = effectiveMinValue();
2188
+ const max = effectiveMaxValue();
1661
2189
  const calDate = toDisplayCalendarDate(date);
1662
- if (minValue && calDate.compare(toDisplayCalendarDate(minValue)) < 0) return true;
1663
- if (maxValue && calDate.compare(toDisplayCalendarDate(maxValue)) > 0) return true;
2190
+ if (min && calDate.compare(min) < 0) return true;
2191
+ if (max && calDate.compare(max) > 0) return true;
1664
2192
  return false;
1665
2193
  };
1666
2194
  const isOutsideVisibleRange = (date) => {
@@ -1769,6 +2297,8 @@ function createRangeCalendarState(props = {}) {
1769
2297
  visibleRange,
1770
2298
  timeZone,
1771
2299
  locale,
2300
+ minValue: minValueState,
2301
+ maxValue: maxValueState,
1772
2302
  firstDayOfWeek: firstDayOfWeekName,
1773
2303
  validationState,
1774
2304
  isValueInvalid,
@@ -1816,6 +2346,331 @@ function convertValue(newValue, oldValue) {
1816
2346
  if (oldValue && "hour" in oldValue) return oldValue.set(localValue);
1817
2347
  return localValue;
1818
2348
  }
2349
+ /** Whether a date falls outside the closed [minValue, maxValue] interval. */
2350
+ function isDateOutsideRange(date, minValue, maxValue) {
2351
+ return minValue != null && date.compare(minValue) < 0 || maxValue != null && date.compare(maxValue) > 0;
2352
+ }
2353
+ /**
2354
+ * Walks outward from the anchor one day at a time in `dir` until it reaches an
2355
+ * unavailable date, then returns the last available date before it — or
2356
+ * undefined when no unavailable date is found within `visibleMonths` of the
2357
+ * anchor (no narrowing needed on that side). Mirrors @react-stately/calendar
2358
+ * nextUnavailableDate.
2359
+ */
2360
+ function nextUnavailableDate(anchorDate, isDateUnavailable, visibleMonths, dir) {
2361
+ let nextDate = anchorDate.add({ days: dir });
2362
+ const minBound = anchorDate.subtract({ months: visibleMonths });
2363
+ const maxBound = anchorDate.add({ months: visibleMonths });
2364
+ while ((dir < 0 ? nextDate.compare(minBound) >= 0 : nextDate.compare(maxBound) <= 0) && !isDateUnavailable(nextDate)) nextDate = nextDate.add({ days: dir });
2365
+ if (isDateUnavailable(nextDate)) return nextDate.add({ days: -dir });
2366
+ }
2367
+ //#endregion
2368
+ //#region src/calendar/IncompleteDate.ts
2369
+ /**
2370
+ * IncompleteDate for Solid-Stately
2371
+ *
2372
+ * Represents a date that is incomplete or otherwise invalid as a result of user
2373
+ * editing (for example February 31st, if the day is edited before the month).
2374
+ * This lets a date field hold an invalid intermediate value while typing and
2375
+ * only constrain it on blur, matching upstream behavior.
2376
+ *
2377
+ * Based on @react-stately/datepicker IncompleteDate
2378
+ */
2379
+ /**
2380
+ * This class represents a date that is incomplete or otherwise invalid as a result of user editing.
2381
+ * For example, it can represent temporary dates such as February 31st if the user edits the day
2382
+ * before the month. Times are represented according to an hour cycle rather than always in 24 hour
2383
+ * time. This enables the user to adjust the day period (e.g. am/pm) independently from the hour.
2384
+ */
2385
+ var IncompleteDate = class IncompleteDate {
2386
+ calendar;
2387
+ era;
2388
+ year;
2389
+ month;
2390
+ day;
2391
+ hour;
2392
+ hourCycle;
2393
+ dayPeriod;
2394
+ minute;
2395
+ second;
2396
+ millisecond;
2397
+ offset;
2398
+ constructor(calendar, hourCycle, dateValue) {
2399
+ this.era = dateValue?.era ?? null;
2400
+ this.calendar = calendar;
2401
+ this.year = dateValue?.year ?? null;
2402
+ this.month = dateValue?.month ?? null;
2403
+ this.day = dateValue?.day ?? null;
2404
+ this.hour = dateValue?.hour ?? null;
2405
+ this.hourCycle = hourCycle;
2406
+ this.dayPeriod = null;
2407
+ this.minute = dateValue?.minute ?? null;
2408
+ this.second = dateValue?.second ?? null;
2409
+ this.millisecond = dateValue?.millisecond ?? null;
2410
+ this.offset = "offset" in (dateValue ?? {}) ? dateValue.offset : null;
2411
+ if (this.hour != null) {
2412
+ const [dayPeriod, hour] = toHourCycle(this.hour, hourCycle);
2413
+ this.dayPeriod = dayPeriod;
2414
+ this.hour = hour;
2415
+ }
2416
+ }
2417
+ copy() {
2418
+ const res = new IncompleteDate(this.calendar, this.hourCycle);
2419
+ res.era = this.era;
2420
+ res.year = this.year;
2421
+ res.month = this.month;
2422
+ res.day = this.day;
2423
+ res.hour = this.hour;
2424
+ res.dayPeriod = this.dayPeriod;
2425
+ res.minute = this.minute;
2426
+ res.second = this.second;
2427
+ res.millisecond = this.millisecond;
2428
+ res.offset = this.offset;
2429
+ return res;
2430
+ }
2431
+ /** Checks whether all the specified segments have a value. */
2432
+ isComplete(segments) {
2433
+ return segments.every((segment) => this[segment] != null);
2434
+ }
2435
+ /** Checks whether the given date value matches this value for the specified segments. */
2436
+ validate(dt, segments) {
2437
+ return segments.every((segment) => {
2438
+ if ((segment === "hour" || segment === "dayPeriod") && "hour" in dt) {
2439
+ const [dayPeriod, hour] = toHourCycle(dt.hour, this.hourCycle);
2440
+ return this.dayPeriod === dayPeriod && this.hour === hour;
2441
+ }
2442
+ return this[segment] === dt[segment];
2443
+ });
2444
+ }
2445
+ /** Checks if the date is empty (i.e. all specified segments are null). */
2446
+ isCleared(segments) {
2447
+ return segments.every((segment) => this[segment] === null);
2448
+ }
2449
+ /** Sets the given field. */
2450
+ set(field, value, placeholder) {
2451
+ const result = this.copy();
2452
+ result[field] = value;
2453
+ if (field === "hour" && result.dayPeriod == null && "hour" in placeholder) result.dayPeriod = toHourCycle(placeholder.hour, this.hourCycle)[0];
2454
+ if (field === "year" && result.era == null) result.era = placeholder.era;
2455
+ if (field !== "second" && field !== "literal" && field !== "timeZoneName") result.offset = null;
2456
+ return result;
2457
+ }
2458
+ /** Sets the given field to null. */
2459
+ clear(field) {
2460
+ const result = this.copy();
2461
+ result[field] = null;
2462
+ if (field === "year") result.era = null;
2463
+ result.offset = null;
2464
+ return result;
2465
+ }
2466
+ /**
2467
+ * Increments or decrements the given field. If it is null, then it is set to the placeholder
2468
+ * value.
2469
+ */
2470
+ cycle(field, amount, placeholder, displaySegments) {
2471
+ const res = this.copy();
2472
+ if (res[field] == null && field !== "dayPeriod" && field !== "era") {
2473
+ if (field === "hour" && "hour" in placeholder) {
2474
+ const [dayPeriod, hour] = toHourCycle(placeholder.hour, this.hourCycle);
2475
+ res.dayPeriod = dayPeriod;
2476
+ res.hour = hour;
2477
+ } else res[field] = placeholder[field];
2478
+ if (field === "year" && res.era == null) res.era = placeholder.era;
2479
+ return res;
2480
+ }
2481
+ switch (field) {
2482
+ case "era": {
2483
+ const eras = this.calendar.getEras();
2484
+ let index = eras.indexOf(res.era);
2485
+ index = cycleValue(index, amount, 0, eras.length - 1);
2486
+ res.era = eras[index];
2487
+ break;
2488
+ }
2489
+ case "year": {
2490
+ let date = new CalendarDate(this.calendar, this.era ?? placeholder.era, this.year ?? placeholder.year, this.month ?? 1, this.day ?? 1);
2491
+ date = date.cycle(field, amount, { round: field === "year" });
2492
+ res.era = date.era;
2493
+ res.year = date.year;
2494
+ break;
2495
+ }
2496
+ case "month":
2497
+ res.month = cycleValue(res.month ?? 1, amount, 1, this.calendar.getMaximumMonthsInYear());
2498
+ break;
2499
+ case "day":
2500
+ res.day = cycleValue(res.day ?? 1, amount, 1, this.calendar.getMaximumDaysInMonth());
2501
+ break;
2502
+ case "hour": {
2503
+ const hasDateSegements = displaySegments.some((s) => [
2504
+ "year",
2505
+ "month",
2506
+ "day"
2507
+ ].includes(s));
2508
+ if ("timeZone" in placeholder && (!hasDateSegements || res.year != null && res.month != null && res.day != null)) {
2509
+ let date = this.toValue(placeholder);
2510
+ date = date.cycle("hour", amount, {
2511
+ hourCycle: this.hourCycle === "h12" ? 12 : 24,
2512
+ round: false
2513
+ });
2514
+ const [dayPeriod, adjustedHour] = toHourCycle(date.hour, this.hourCycle);
2515
+ res.hour = adjustedHour;
2516
+ res.dayPeriod = dayPeriod;
2517
+ res.offset = date.offset;
2518
+ } else {
2519
+ const hours = res.hour ?? 0;
2520
+ const limits = this.getSegmentLimits("hour");
2521
+ res.hour = cycleValue(hours, amount, limits.minValue, limits.maxValue);
2522
+ if (res.dayPeriod == null && "hour" in placeholder) res.dayPeriod = toHourCycle(placeholder.hour, this.hourCycle)[0];
2523
+ }
2524
+ break;
2525
+ }
2526
+ case "dayPeriod":
2527
+ res.dayPeriod = cycleValue(res.dayPeriod ?? 0, amount, 0, 1);
2528
+ break;
2529
+ case "minute":
2530
+ res.minute = cycleValue(res.minute ?? 0, amount, 0, 59, true);
2531
+ break;
2532
+ case "second":
2533
+ res.second = cycleValue(res.second ?? 0, amount, 0, 59, true);
2534
+ break;
2535
+ }
2536
+ return res;
2537
+ }
2538
+ /**
2539
+ * Converts the incomplete date to a full date value, using the provided value for any unset
2540
+ * fields.
2541
+ */
2542
+ toValue(value) {
2543
+ if ("hour" in value) {
2544
+ let hour = this.hour;
2545
+ if (hour != null) hour = fromHourCycle(hour, this.dayPeriod ?? 0, this.hourCycle);
2546
+ else if (this.hourCycle === "h12" || this.hourCycle === "h11") hour = this.dayPeriod === 1 ? 12 : 0;
2547
+ let res = value.set({
2548
+ era: this.era ?? value.era,
2549
+ year: this.year ?? value.year,
2550
+ month: this.month ?? value.month,
2551
+ day: this.day ?? value.day,
2552
+ hour: hour ?? value.hour,
2553
+ minute: this.minute ?? value.minute,
2554
+ second: this.second ?? value.second,
2555
+ millisecond: this.millisecond ?? value.millisecond
2556
+ });
2557
+ if ("offset" in res && this.offset != null && res.offset !== this.offset) res = res.add({ milliseconds: res.offset - this.offset });
2558
+ return res;
2559
+ } else return value.set({
2560
+ era: this.era ?? value.era,
2561
+ year: this.year ?? value.year,
2562
+ month: this.month ?? value.month,
2563
+ day: this.day ?? value.day
2564
+ });
2565
+ }
2566
+ getSegmentLimits(type) {
2567
+ switch (type) {
2568
+ case "era": {
2569
+ const eras = this.calendar.getEras();
2570
+ return {
2571
+ value: this.era != null ? eras.indexOf(this.era) : eras.length - 1,
2572
+ minValue: 0,
2573
+ maxValue: eras.length - 1
2574
+ };
2575
+ }
2576
+ case "year": return {
2577
+ value: this.year,
2578
+ minValue: 1,
2579
+ maxValue: 9999
2580
+ };
2581
+ case "month": return {
2582
+ value: this.month,
2583
+ minValue: 1,
2584
+ maxValue: this.calendar.getMaximumMonthsInYear()
2585
+ };
2586
+ case "day": return {
2587
+ value: this.day,
2588
+ minValue: 1,
2589
+ maxValue: this.calendar.getMaximumDaysInMonth()
2590
+ };
2591
+ case "dayPeriod": return {
2592
+ value: this.dayPeriod,
2593
+ minValue: 0,
2594
+ maxValue: 1
2595
+ };
2596
+ case "hour": {
2597
+ let minValue = 0;
2598
+ let maxValue = 23;
2599
+ if (this.hourCycle === "h12") {
2600
+ minValue = 1;
2601
+ maxValue = 12;
2602
+ } else if (this.hourCycle === "h11") {
2603
+ minValue = 0;
2604
+ maxValue = 11;
2605
+ }
2606
+ return {
2607
+ value: this.hour,
2608
+ minValue,
2609
+ maxValue
2610
+ };
2611
+ }
2612
+ case "minute": return {
2613
+ value: this.minute,
2614
+ minValue: 0,
2615
+ maxValue: 59
2616
+ };
2617
+ case "second": return {
2618
+ value: this.second,
2619
+ minValue: 0,
2620
+ maxValue: 59
2621
+ };
2622
+ }
2623
+ }
2624
+ };
2625
+ function cycleValue(value, amount, min, max, round = false) {
2626
+ if (round) {
2627
+ value += Math.sign(amount);
2628
+ if (value < min) value = max;
2629
+ const div = Math.abs(amount);
2630
+ if (amount > 0) value = Math.ceil(value / div) * div;
2631
+ else value = Math.floor(value / div) * div;
2632
+ if (value > max) value = min;
2633
+ } else {
2634
+ value += amount;
2635
+ if (value < min) value = max - (min - value - 1);
2636
+ else if (value > max) value = min + (value - max - 1);
2637
+ }
2638
+ return value;
2639
+ }
2640
+ function toHourCycle(hour, hourCycle) {
2641
+ let dayPeriod = hour >= 12 ? 1 : 0;
2642
+ switch (hourCycle) {
2643
+ case "h11":
2644
+ if (hour >= 12) hour -= 12;
2645
+ break;
2646
+ case "h12":
2647
+ if (hour === 0) hour = 12;
2648
+ else if (hour > 12) hour -= 12;
2649
+ break;
2650
+ case "h23":
2651
+ dayPeriod = null;
2652
+ break;
2653
+ case "h24":
2654
+ hour += 1;
2655
+ dayPeriod = null;
2656
+ }
2657
+ return [dayPeriod, hour];
2658
+ }
2659
+ function fromHourCycle(hour, dayPeriod, hourCycle) {
2660
+ switch (hourCycle) {
2661
+ case "h11":
2662
+ if (dayPeriod === 1) hour += 12;
2663
+ break;
2664
+ case "h12":
2665
+ if (hour === 12) hour = 0;
2666
+ if (dayPeriod === 1) hour += 12;
2667
+ break;
2668
+ case "h24":
2669
+ hour -= 1;
2670
+ break;
2671
+ }
2672
+ return hour;
2673
+ }
1819
2674
  //#endregion
1820
2675
  //#region src/calendar/createDateFieldState.ts
1821
2676
  /**
@@ -1823,6 +2678,12 @@ function convertValue(newValue, oldValue) {
1823
2678
  *
1824
2679
  * Provides state management for date field components with segment-based editing.
1825
2680
  * Based on @react-stately/datepicker useDateFieldState
2681
+ *
2682
+ * Editing is modeled with an {@link IncompleteDate} display value so that invalid
2683
+ * intermediate dates (e.g. February 31st, produced by editing the day before the
2684
+ * month) are held as-typed while the field is focused and only constrained on blur
2685
+ * via `confirmPlaceholder`, matching upstream behavior. Out-of-range values are
2686
+ * never snapped to `minValue`/`maxValue`; they are kept and reported as invalid.
1826
2687
  */
1827
2688
  /**
1828
2689
  * Provides state management for a date field component.
@@ -1831,38 +2692,28 @@ function createDateFieldState(props = {}) {
1831
2692
  const timeZone = getLocalTimeZone$1();
1832
2693
  const locale = props.locale ?? "en-US";
1833
2694
  const granularity = props.granularity ?? "day";
2695
+ const calendar = new GregorianCalendar$1();
2696
+ const hourCycle = resolveHourCycle(locale, props.hourCycle);
2697
+ const displaySegments = computeDisplaySegments(granularity, hourCycle);
2698
+ const getPlaceholderValue = () => {
2699
+ const base = props.placeholderValue ?? today$1(timeZone);
2700
+ return granularity === "day" ? toCalendarDate$1(base) : toCalendarDateTime$1(base);
2701
+ };
2702
+ const toSource = (v) => v ?? null;
1834
2703
  const initialValue = props.defaultValue ?? null;
1835
2704
  const [internalValue, setInternalValue] = createSignal(initialValue);
1836
- const [placeholderDate, setPlaceholderDate] = createSignal({});
1837
2705
  const value = createMemo(() => {
1838
2706
  const controlled = access(props.value);
1839
2707
  return controlled !== void 0 ? controlled : internalValue();
1840
2708
  });
1841
- const dateValue = createMemo(() => {
2709
+ const [displayValue, setDisplayValue] = createSignal(new IncompleteDate(calendar, hourCycle, toSource(value())));
2710
+ let lastSyncedValue = value();
2711
+ createEffect(() => {
1842
2712
  const v = value();
1843
- if (v) return v;
1844
- const parts = placeholderDate();
1845
- const placeholder = props.placeholderValue ?? today$1(timeZone);
1846
- if (Object.keys(parts).length === 0) return null;
1847
- const year = parts.year ?? placeholder.year;
1848
- const month = parts.month ?? placeholder.month;
1849
- const day = parts.day ?? placeholder.day;
1850
- if (granularity === "day") return toCalendarDate$1(placeholder).set({
1851
- year,
1852
- month,
1853
- day
1854
- });
1855
- const hour = parts.hour ?? 0;
1856
- const minute = parts.minute ?? 0;
1857
- const second = parts.second ?? 0;
1858
- return toCalendarDateTime$1(placeholder).set({
1859
- year,
1860
- month,
1861
- day,
1862
- hour,
1863
- minute,
1864
- second
1865
- });
2713
+ if (v !== lastSyncedValue) {
2714
+ lastSyncedValue = v;
2715
+ setDisplayValue(new IncompleteDate(calendar, hourCycle, toSource(v)));
2716
+ }
1866
2717
  });
1867
2718
  const isDisabled = createMemo(() => access(props.isDisabled) ?? false);
1868
2719
  const isReadOnly = createMemo(() => access(props.isReadOnly) ?? false);
@@ -1918,117 +2769,120 @@ function createDateFieldState(props = {}) {
1918
2769
  });
1919
2770
  const isInvalid = createMemo(() => validation.displayValidation().isInvalid);
1920
2771
  const validationState = createMemo(() => explicitValidationState() ?? (isInvalid() ? "invalid" : void 0));
1921
- const segments = createMemo(() => {
2772
+ const dateValue = createMemo(() => {
1922
2773
  const v = value();
1923
- const placeholder = props.placeholderValue ?? today$1(timeZone);
1924
- const parts = placeholderDate();
1925
- const segs = [];
2774
+ if (v) return v;
2775
+ const dv = displayValue();
2776
+ if (dv.isCleared(displaySegments)) return null;
2777
+ return dv.toValue(getPlaceholderValue());
2778
+ });
2779
+ const segments = createMemo(() => {
2780
+ const dv = displayValue();
2781
+ const base = value() ?? getPlaceholderValue();
2782
+ const jsDate = dv.toValue(base).toDate(timeZone);
1926
2783
  const shouldForceLeadingZeros = props.shouldForceLeadingZeros === true;
1927
- const formatOptions = {
1928
- year: "numeric",
1929
- month: shouldForceLeadingZeros ? "2-digit" : "numeric",
1930
- day: shouldForceLeadingZeros ? "2-digit" : "numeric"
1931
- };
1932
- if (granularity !== "day") {
1933
- formatOptions.hour = "2-digit";
1934
- formatOptions.minute = "2-digit";
1935
- if (granularity === "second") formatOptions.second = "2-digit";
1936
- if (props.hourCycle) formatOptions.hourCycle = props.hourCycle === 12 ? "h12" : "h23";
1937
- }
1938
- const formatter = new DateFormatter$1(locale, formatOptions);
1939
- const dateToFormat = v ?? placeholder;
1940
- const formattedParts = formatter.formatToParts(dateToFormat.toDate(timeZone));
1941
- for (const part of formattedParts) {
2784
+ const formatter = new DateFormatter$1(locale, buildFormatOptions(granularity, shouldForceLeadingZeros, props.hourCycle));
2785
+ const resolvedOptions = formatter.resolvedOptions();
2786
+ const parts = formatter.formatToParts(jsDate);
2787
+ const numberFormatter = new Intl.NumberFormat(locale, { useGrouping: false });
2788
+ const twoDigitFormatter = new Intl.NumberFormat(locale, {
2789
+ useGrouping: false,
2790
+ minimumIntegerDigits: 2
2791
+ });
2792
+ const result = [];
2793
+ for (const part of parts) {
1942
2794
  const type = mapPartType(part.type);
1943
- if (type === "literal") segs.push({
1944
- type: "literal",
1945
- text: part.value,
1946
- isEditable: false,
1947
- isPlaceholder: false,
1948
- placeholder: part.value
1949
- });
1950
- else if (type) {
1951
- const segValue = getSegmentValue(v, type);
1952
- const partValue = parts[type];
1953
- const hasValue = v !== null || partValue !== void 0;
1954
- segs.push({
1955
- type,
1956
- text: hasValue ? part.value : getPlaceholderText(type, locale, granularity, shouldForceLeadingZeros),
1957
- value: segValue ?? partValue,
1958
- minValue: getMinValue(type),
1959
- maxValue: getMaxValue(type, v ?? placeholder),
1960
- isEditable: !isDisabled() && !isReadOnly(),
1961
- isPlaceholder: !hasValue,
1962
- placeholder: getPlaceholderText(type, locale, granularity, shouldForceLeadingZeros)
2795
+ if (type === "literal") {
2796
+ result.push({
2797
+ type: "literal",
2798
+ text: part.value,
2799
+ isEditable: false,
2800
+ isPlaceholder: false,
2801
+ placeholder: part.value
1963
2802
  });
2803
+ continue;
2804
+ }
2805
+ if (!type) continue;
2806
+ const editable = isEditableSegment(type);
2807
+ const rawValue = rawSegmentValue(dv, type);
2808
+ const isPlaceholder = editable && rawValue == null;
2809
+ const limits = dv.getSegmentLimits(type);
2810
+ const placeholderText = getPlaceholderText(type, locale, granularity, shouldForceLeadingZeros);
2811
+ let text = part.value;
2812
+ if ((type === "year" || type === "month" || type === "day" || type === "hour") && typeof rawValue === "number") {
2813
+ const numeric = rawValue ?? 0;
2814
+ text = resolvedOptions[type] === "2-digit" ? twoDigitFormatter.format(numeric) : numberFormatter.format(numeric);
1964
2815
  }
2816
+ result.push({
2817
+ type,
2818
+ text: isPlaceholder ? placeholderText : text,
2819
+ value: limits?.value,
2820
+ minValue: limits?.minValue,
2821
+ maxValue: limits?.maxValue,
2822
+ isEditable: editable && !isDisabled() && !isReadOnly(),
2823
+ isPlaceholder,
2824
+ placeholder: placeholderText
2825
+ });
1965
2826
  }
1966
- return segs;
2827
+ return result;
1967
2828
  });
1968
- const setValue = (newValue) => {
1969
- if (isDisabled() || isReadOnly()) return;
1970
- if (access(props.value) === void 0) setInternalValue(() => newValue);
1971
- if (props.onChange) props.onChange(newValue);
1972
- setPlaceholderDate({});
2829
+ const commitDate = (newValue) => {
2830
+ if (access(props.value) === void 0) {
2831
+ lastSyncedValue = newValue;
2832
+ setInternalValue(() => newValue);
2833
+ }
2834
+ props.onChange?.(newValue);
1973
2835
  };
1974
- const setSegment = (type, newValue) => {
2836
+ const setValue = (newValue) => {
1975
2837
  if (isDisabled() || isReadOnly()) return;
1976
- const v = value();
1977
- if (v) setValue(updateDatePart(v, type, newValue));
1978
- else setPlaceholderDate((prev) => ({
1979
- ...prev,
1980
- [type]: newValue
1981
- }));
2838
+ if (newValue == null || newValue instanceof IncompleteDate && newValue.isCleared(displaySegments)) {
2839
+ commitDate(null);
2840
+ setDisplayValue(new IncompleteDate(calendar, hourCycle, null));
2841
+ return;
2842
+ }
2843
+ if (!(newValue instanceof IncompleteDate)) {
2844
+ commitDate(newValue);
2845
+ setDisplayValue(new IncompleteDate(calendar, hourCycle, toSource(newValue)));
2846
+ return;
2847
+ }
2848
+ if (newValue.isComplete(displaySegments)) {
2849
+ const resolved = newValue.toValue(value() ?? getPlaceholderValue());
2850
+ if (newValue.validate(resolved, displaySegments)) {
2851
+ const committed = value();
2852
+ if (!committed || resolved.compare(committed) !== 0) {
2853
+ setDisplayValue(new IncompleteDate(calendar, hourCycle, toSource(resolved)));
2854
+ commitDate(resolved);
2855
+ return;
2856
+ }
2857
+ }
2858
+ }
2859
+ setDisplayValue(newValue);
1982
2860
  };
1983
- const incrementSegment = (type) => {
2861
+ const adjustSegment = (type, amount) => {
1984
2862
  if (isDisabled() || isReadOnly()) return;
1985
- const v = value();
1986
- const current = v ? getSegmentValue(v, type) : placeholderDate()[type];
1987
- const max = getMaxValue(type, v ?? props.placeholderValue ?? today$1(timeZone));
1988
- const min = getMinValue(type);
1989
- const newValue = current !== void 0 ? current + 1 : min;
1990
- setSegment(type, newValue > max ? min : newValue);
2863
+ setValue(displayValue().cycle(type, amount, getPlaceholderValue(), displaySegments));
1991
2864
  };
1992
- const decrementSegment = (type) => {
2865
+ const setSegment = (type, newValue) => {
1993
2866
  if (isDisabled() || isReadOnly()) return;
1994
- const v = value();
1995
- const current = v ? getSegmentValue(v, type) : placeholderDate()[type];
1996
- const max = getMaxValue(type, v ?? props.placeholderValue ?? today$1(timeZone));
1997
- const min = getMinValue(type);
1998
- const newValue = current !== void 0 ? current - 1 : max;
1999
- setSegment(type, newValue < min ? max : newValue);
2867
+ setValue(displayValue().set(type, newValue, getPlaceholderValue()));
2000
2868
  };
2869
+ const incrementSegment = (type) => adjustSegment(type, 1);
2870
+ const decrementSegment = (type) => adjustSegment(type, -1);
2001
2871
  const clearSegment = (type) => {
2002
2872
  if (isDisabled() || isReadOnly()) return;
2003
- if (type === "literal") return;
2004
- const v = value();
2005
- if (v) setValue(updateDatePart(v, type, getMinValue(type)));
2006
- else setPlaceholderDate((prev) => {
2007
- const next = { ...prev };
2008
- delete next[type];
2009
- return next;
2010
- });
2873
+ let dv = displayValue();
2874
+ if (type !== "timeZoneName" && type !== "literal") dv = dv.clear(type);
2875
+ setValue(dv);
2011
2876
  };
2012
2877
  const confirmPlaceholder = () => {
2013
2878
  if (isDisabled() || isReadOnly()) return;
2014
- const minValue = access(props.minValue);
2015
- const maxValue = access(props.maxValue);
2016
- const constrain = (candidate) => {
2017
- if (minValue && candidate.compare(minValue) < 0) return minValue;
2018
- if (maxValue && candidate.compare(maxValue) > 0) return maxValue;
2019
- return candidate;
2020
- };
2021
- const parts = placeholderDate();
2022
- if (Object.keys(parts).length > 0) {
2023
- const dv = dateValue();
2024
- if (dv) setValue(constrain(dv));
2025
- return;
2026
- }
2027
- const current = value();
2028
- if (current) {
2029
- const constrained = constrain(current);
2030
- if (constrained.compare(current) !== 0) setValue(constrained);
2031
- }
2879
+ const dv = displayValue();
2880
+ if (!dv.isComplete(displaySegments)) return;
2881
+ const resolved = dv.toValue(value() ?? getPlaceholderValue());
2882
+ const committed = value();
2883
+ const needsCommit = !committed || resolved.compare(committed) !== 0;
2884
+ if (needsCommit) commitDate(resolved);
2885
+ if (needsCommit || !dv.validate(resolved, displaySegments)) setDisplayValue(new IncompleteDate(calendar, hourCycle, toSource(value() ?? resolved)));
2032
2886
  };
2033
2887
  const formatValue = (fieldOptions) => {
2034
2888
  const v = value();
@@ -2072,6 +2926,68 @@ function createDateFieldState(props = {}) {
2072
2926
  timeZone
2073
2927
  };
2074
2928
  }
2929
+ const EDITABLE_SEGMENTS = {
2930
+ year: true,
2931
+ month: true,
2932
+ day: true,
2933
+ hour: true,
2934
+ minute: true,
2935
+ second: true,
2936
+ dayPeriod: true,
2937
+ era: true
2938
+ };
2939
+ function isEditableSegment(type) {
2940
+ return EDITABLE_SEGMENTS[type] === true;
2941
+ }
2942
+ function rawSegmentValue(dv, type) {
2943
+ switch (type) {
2944
+ case "era": return dv.era;
2945
+ case "year": return dv.year;
2946
+ case "month": return dv.month;
2947
+ case "day": return dv.day;
2948
+ case "hour": return dv.hour;
2949
+ case "minute": return dv.minute;
2950
+ case "second": return dv.second;
2951
+ case "dayPeriod": return dv.dayPeriod;
2952
+ default: return null;
2953
+ }
2954
+ }
2955
+ function resolveHourCycle(locale, hourCycle) {
2956
+ return new DateFormatter$1(locale, {
2957
+ hour: "numeric",
2958
+ hour12: hourCycle != null ? hourCycle === 12 : void 0
2959
+ }).resolvedOptions().hourCycle ?? "h23";
2960
+ }
2961
+ function computeDisplaySegments(granularity, hourCycle) {
2962
+ const is12HourClock = hourCycle === "h11" || hourCycle === "h12";
2963
+ const segments = [
2964
+ "era",
2965
+ "year",
2966
+ "month",
2967
+ "day",
2968
+ "hour",
2969
+ ...is12HourClock ? ["dayPeriod"] : [],
2970
+ "minute",
2971
+ "second"
2972
+ ];
2973
+ const minIndex = segments.indexOf("era");
2974
+ const maxIndex = segments.indexOf(granularity === "hour" && is12HourClock ? "dayPeriod" : granularity);
2975
+ return segments.slice(minIndex, maxIndex + 1);
2976
+ }
2977
+ function buildFormatOptions(granularity, shouldForceLeadingZeros, hourCycle) {
2978
+ const formatOptions = {
2979
+ year: "numeric",
2980
+ month: shouldForceLeadingZeros ? "2-digit" : "numeric",
2981
+ day: shouldForceLeadingZeros ? "2-digit" : "numeric"
2982
+ };
2983
+ if (granularity !== "day") {
2984
+ formatOptions.hour = "2-digit";
2985
+ formatOptions.minute = "2-digit";
2986
+ if (granularity === "second") formatOptions.second = "2-digit";
2987
+ if (hourCycle) formatOptions.hourCycle = hourCycle === 12 ? "h12" : "h23";
2988
+ }
2989
+ return formatOptions;
2990
+ }
2075
2991
  function mapPartType(type) {
2076
2992
  switch (type) {
2077
2993
  case "year": return "year";
@@ -2087,40 +3003,6 @@ function mapPartType(type) {
2087
3003
  default: return null;
2088
3004
  }
2089
3005
  }
2090
- function getSegmentValue(date, type) {
2091
- if (!date) return void 0;
2092
- switch (type) {
2093
- case "year": return date.year;
2094
- case "month": return date.month;
2095
- case "day": return date.day;
2096
- case "hour": return "hour" in date ? date.hour : void 0;
2097
- case "minute": return "minute" in date ? date.minute : void 0;
2098
- case "second": return "second" in date ? date.second : void 0;
2099
- default: return;
2100
- }
2101
- }
2102
- function getMinValue(type) {
2103
- switch (type) {
2104
- case "year": return 1;
2105
- case "month": return 1;
2106
- case "day": return 1;
2107
- case "hour": return 0;
2108
- case "minute": return 0;
2109
- case "second": return 0;
2110
- default: return 0;
2111
- }
2112
- }
2113
- function getMaxValue(type, date) {
2114
- switch (type) {
2115
- case "year": return 9999;
2116
- case "month": return 12;
2117
- case "day": return endOfMonth$1(date).day;
2118
- case "hour": return 23;
2119
- case "minute": return 59;
2120
- case "second": return 59;
2121
- default: return 0;
2122
- }
2123
- }
2124
3006
  function getPlaceholderText(type, locale, granularity, shouldForceLeadingZeros = false) {
2125
3007
  if (type === "literal") return "";
2126
3008
  if (type === "dayPeriod") return "AM";
@@ -2137,7 +3019,7 @@ function getPlaceholderText(type, locale, granularity, shouldForceLeadingZeros =
2137
3019
  const formatter = new DateFormatter$1(locale, formatOptions);
2138
3020
  const refDate = new Date(2222, 10, 22, 22, 22, 22);
2139
3021
  const parts = formatter.formatToParts(refDate);
2140
- for (const part of parts) if (mapPartType(part.type) === type) return part.value.replace(/\d/g, (d) => {
3022
+ for (const part of parts) if (mapPartType(part.type) === type) return part.value.replace(/\d/g, (_d) => {
2141
3023
  if (type === "year") return "y";
2142
3024
  if (type === "month") return "m";
2143
3025
  if (type === "day") return "d";
@@ -2153,18 +3035,6 @@ function getPlaceholderText(type, locale, granularity, shouldForceLeadingZeros =
2153
3035
  default: return "";
2154
3036
  }
2155
3037
  }
2156
- function updateDatePart(date, type, value) {
2157
- if (typeof date.set !== "function") return date;
2158
- switch (type) {
2159
- case "year": return date.set({ year: value });
2160
- case "month": return date.set({ month: value });
2161
- case "day": return date.set({ day: value });
2162
- case "hour": return date.set({ hour: value });
2163
- case "minute": return date.set({ minute: value });
2164
- case "second": return date.set({ second: value });
2165
- default: return date;
2166
- }
2167
- }
2168
3038
  //#endregion
2169
3039
  //#region src/calendar/createTimeFieldState.ts
2170
3040
  /**
@@ -2720,6 +3590,7 @@ function createSelectState(props) {
2720
3590
  overlayState.close();
2721
3591
  }
2722
3592
  });
3593
+ const [isFocused, setFocused] = createSignal(false);
2723
3594
  const selectedItem = createMemo(() => {
2724
3595
  const key = selectedKey();
2725
3596
  if (key == null) return null;
@@ -2737,10 +3608,11 @@ function createSelectState(props) {
2737
3608
  });
2738
3609
  return {
2739
3610
  collection: listState.collection,
3611
+ selectionManager: listState.selectionManager,
2740
3612
  focusedKey: listState.focusedKey,
2741
3613
  setFocusedKey: listState.setFocusedKey,
2742
- isFocused: listState.isFocused,
2743
- setFocused: listState.setFocused,
3614
+ isFocused,
3615
+ setFocused,
2744
3616
  isOpen: overlayState.isOpen,
2745
3617
  open: overlayState.open,
2746
3618
  close: overlayState.close,
@@ -3057,6 +3929,7 @@ function createComboBoxState(props) {
3057
3929
  inputValue,
3058
3930
  defaultInputValue: getProps().defaultInputValue ?? "",
3059
3931
  setInputValue,
3932
+ selectionManager: listState.selectionManager,
3060
3933
  focusedKey: listState.focusedKey,
3061
3934
  setFocusedKey: listState.setFocusedKey,
3062
3935
  isFocused,
@@ -3250,8 +4123,13 @@ function createTabListState(props) {
3250
4123
  const setFocusedKey = (key, childStrategy) => {
3251
4124
  setFocusedKeyInternal(key);
3252
4125
  setChildFocusStrategy(childStrategy ?? null);
3253
- if (keyboardActivation() === "automatic" && key !== null && key !== selectedKey() && !isKeyDisabled(key)) setSelectedKey(key);
3254
4126
  };
4127
+ let lastSelectedKey = selectedKey();
4128
+ createComputed(() => {
4129
+ const sel = selectedKey();
4130
+ if (sel !== null && focusedKey() === null || !isFocused() && sel !== lastSelectedKey) setFocusedKey(sel);
4131
+ lastSelectedKey = sel;
4132
+ });
3255
4133
  createComputed(() => {
3256
4134
  const p = getProps();
3257
4135
  if (p.selectedKey !== void 0) return;
@@ -3843,9 +4721,23 @@ var ToastQueue = class {
3843
4721
  maxVisibleToasts;
3844
4722
  hasExitAnimation;
3845
4723
  keyCounter = 0;
4724
+ wrapUpdate;
3846
4725
  constructor(options = {}) {
3847
4726
  this.maxVisibleToasts = options.maxVisibleToasts ?? 5;
3848
4727
  this.hasExitAnimation = options.hasExitAnimation ?? false;
4728
+ this.wrapUpdate = options.wrapUpdate;
4729
+ }
4730
+ /**
4731
+ * Installs (or clears) the function that wraps visible-toast updates, so the
4732
+ * resulting re-render can be animated. Used to attach `document.startViewTransition`
4733
+ * to a shared queue (e.g. the global queue) after construction.
4734
+ */
4735
+ setWrapUpdate(wrapUpdate) {
4736
+ this.wrapUpdate = wrapUpdate;
4737
+ }
4738
+ runWithWrapUpdate(fn, action) {
4739
+ if (this.wrapUpdate) this.wrapUpdate(fn, action);
4740
+ else fn();
3849
4741
  }
3850
4742
  /**
3851
4743
  * Subscribe to queue changes.
@@ -3882,7 +4774,7 @@ var ToastQueue = class {
3882
4774
  toast,
3883
4775
  ...this.queue.slice(low)
3884
4776
  ];
3885
- this.updateVisibility();
4777
+ this.updateVisibility("add");
3886
4778
  return key;
3887
4779
  }
3888
4780
  /**
@@ -3898,7 +4790,7 @@ var ToastQueue = class {
3898
4790
  timer: null,
3899
4791
  animation: "exiting"
3900
4792
  } : t);
3901
- this.notify();
4793
+ this.notify("remove");
3902
4794
  } else this.remove(key);
3903
4795
  }
3904
4796
  /**
@@ -3911,7 +4803,7 @@ var ToastQueue = class {
3911
4803
  toast.timer?.cancel();
3912
4804
  }
3913
4805
  this.queue = this.queue.filter((t) => t.key !== key);
3914
- this.updateVisibility();
4806
+ this.updateVisibility("remove");
3915
4807
  }
3916
4808
  /**
3917
4809
  * Clear all queued and visible toasts.
@@ -3919,7 +4811,7 @@ var ToastQueue = class {
3919
4811
  clear() {
3920
4812
  for (const toast of this.queue) toast.timer?.cancel();
3921
4813
  this.queue = [];
3922
- this.notify();
4814
+ this.notify("clear");
3923
4815
  }
3924
4816
  /**
3925
4817
  * Pause all toast timers.
@@ -3933,7 +4825,7 @@ var ToastQueue = class {
3933
4825
  resumeAll() {
3934
4826
  for (const toast of this.queue) toast.timer?.resume();
3935
4827
  }
3936
- updateVisibility() {
4828
+ updateVisibility(action) {
3937
4829
  const visibleCount = this.queue.filter((t) => t.animation !== "queued" && t.animation !== "exiting").length;
3938
4830
  let promoted = 0;
3939
4831
  this.queue = this.queue.map((toast) => {
@@ -3954,11 +4846,13 @@ var ToastQueue = class {
3954
4846
  };
3955
4847
  return nextToast;
3956
4848
  });
3957
- this.notify();
4849
+ this.notify(action);
3958
4850
  }
3959
- notify() {
4851
+ notify(action = "add") {
3960
4852
  const toasts = [...this.queue];
3961
- for (const callback of this.subscriptions) callback(toasts);
4853
+ this.runWithWrapUpdate(() => {
4854
+ for (const callback of this.subscriptions) callback(toasts);
4855
+ }, action);
3962
4856
  }
3963
4857
  };
3964
4858
  /**
@@ -4108,9 +5002,19 @@ function createGridState(options) {
4108
5002
  return internalSelectedKeys();
4109
5003
  });
4110
5004
  const selectionMode = createMemo(() => getOptions().selectionMode ?? "none");
4111
- const selectionBehavior = createMemo(() => getOptions().selectionBehavior ?? "toggle");
5005
+ const selectionBehaviorProp = () => getOptions().selectionBehavior ?? "toggle";
5006
+ const [selectionBehaviorState, setSelectionBehaviorState] = createSignal(selectionBehaviorProp());
5007
+ const selectionBehavior = createMemo(() => selectionBehaviorState());
4112
5008
  const disallowEmptySelection = createMemo(() => getOptions().disallowEmptySelection ?? false);
5009
+ const disabledBehavior = createMemo(() => getOptions().disabledBehavior ?? "all");
4113
5010
  const focusMode = createMemo(() => getOptions().focusMode ?? "row");
5011
+ createEffect(on(selectionBehaviorProp, (behavior) => {
5012
+ setSelectionBehaviorState(behavior);
5013
+ }));
5014
+ createEffect(() => {
5015
+ const keys = selectedKeys();
5016
+ if (selectionBehaviorProp() === "replace" && selectionBehaviorState() === "toggle" && keys !== "all" && keys.size === 0) setSelectionBehaviorState("replace");
5017
+ });
4114
5018
  const setFocusedKey = (key, strategy = "first") => {
4115
5019
  const collection = getOptions().collection;
4116
5020
  if (focusMode() === "cell" && key != null) {
@@ -4201,6 +5105,9 @@ function createGridState(options) {
4201
5105
  updateSelection(new Set([key]));
4202
5106
  setAnchorKey(key);
4203
5107
  };
5108
+ const setSelectedKeys = (keys) => {
5109
+ updateSelection(normalizeSelection$1(keys));
5110
+ };
4204
5111
  const extendSelection = (toKey) => {
4205
5112
  if (isDisabled(toKey)) return;
4206
5113
  if (selectionMode() !== "multiple") {
@@ -4244,6 +5151,9 @@ function createGridState(options) {
4244
5151
  get disabledKeys() {
4245
5152
  return disabledKeys();
4246
5153
  },
5154
+ get disabledBehavior() {
5155
+ return disabledBehavior();
5156
+ },
4247
5157
  get isKeyboardNavigationDisabled() {
4248
5158
  return isKeyboardNavigationDisabled();
4249
5159
  },
@@ -4262,6 +5172,9 @@ function createGridState(options) {
4262
5172
  get selectionBehavior() {
4263
5173
  return selectionBehavior();
4264
5174
  },
5175
+ get disallowEmptySelection() {
5176
+ return disallowEmptySelection();
5177
+ },
4265
5178
  get selectedKeys() {
4266
5179
  return selectedKeys();
4267
5180
  },
@@ -4269,8 +5182,10 @@ function createGridState(options) {
4269
5182
  isDisabled,
4270
5183
  setFocusedKey,
4271
5184
  setFocused: setIsFocused,
5185
+ setSelectionBehavior: setSelectionBehaviorState,
4272
5186
  toggleSelection,
4273
5187
  replaceSelection,
5188
+ setSelectedKeys,
4274
5189
  extendSelection,
4275
5190
  selectAll,
4276
5191
  clearSelection,
@@ -4305,6 +5220,7 @@ function createTableState(options) {
4305
5220
  const gridState = createGridState(() => ({
4306
5221
  collection: getOptions().collection,
4307
5222
  disabledKeys: getOptions().disabledKeys,
5223
+ disabledBehavior: getOptions().disabledBehavior,
4308
5224
  focusMode: getOptions().focusMode,
4309
5225
  selectionMode: getOptions().selectionMode,
4310
5226
  selectionBehavior: getOptions().selectionBehavior,
@@ -4338,6 +5254,9 @@ function createTableState(options) {
4338
5254
  get disabledKeys() {
4339
5255
  return gridState.disabledKeys;
4340
5256
  },
5257
+ get disabledBehavior() {
5258
+ return gridState.disabledBehavior;
5259
+ },
4341
5260
  get isKeyboardNavigationDisabled() {
4342
5261
  return gridState.isKeyboardNavigationDisabled;
4343
5262
  },
@@ -4356,6 +5275,9 @@ function createTableState(options) {
4356
5275
  get selectionBehavior() {
4357
5276
  return gridState.selectionBehavior;
4358
5277
  },
5278
+ get disallowEmptySelection() {
5279
+ return gridState.disallowEmptySelection;
5280
+ },
4359
5281
  get selectedKeys() {
4360
5282
  return gridState.selectedKeys;
4361
5283
  },
@@ -4365,6 +5287,8 @@ function createTableState(options) {
4365
5287
  setFocused: gridState.setFocused,
4366
5288
  toggleSelection: gridState.toggleSelection,
4367
5289
  replaceSelection: gridState.replaceSelection,
5290
+ setSelectionBehavior: gridState.setSelectionBehavior,
5291
+ setSelectedKeys: gridState.setSelectedKeys,
4368
5292
  extendSelection: gridState.extendSelection,
4369
5293
  selectAll: gridState.selectAll,
4370
5294
  clearSelection: gridState.clearSelection,
@@ -4376,163 +5300,17 @@ function createTableState(options) {
4376
5300
  get sortDescriptor() {
4377
5301
  return sortDescriptor();
4378
5302
  },
4379
- sort
4380
- };
4381
- }
4382
- //#endregion
4383
- //#region src/table/createTableColumnResizeState.ts
4384
- /**
4385
- * Table column resize state management.
4386
- * Based on @react-stately/table/useTableColumnResizeState.
4387
- *
4388
- * Provides column width state management for a table with column resizing.
4389
- * Tracks column widths, the currently resizing column, and handles
4390
- * width distribution and clamping.
4391
- */
4392
- const DEFAULT_MIN_WIDTH = 75;
4393
- const DEFAULT_MAX_WIDTH = Infinity;
4394
- function parseColumnDef(col, tableWidth) {
4395
- const minWidth = col.minWidth ?? DEFAULT_MIN_WIDTH;
4396
- const maxWidth = col.maxWidth ?? DEFAULT_MAX_WIDTH;
4397
- if (col.width == null) return {
4398
- key: col.key,
4399
- fr: 1,
4400
- fixedPx: 0,
4401
- minWidth,
4402
- maxWidth
4403
- };
4404
- if (typeof col.width === "number") return {
4405
- key: col.key,
4406
- fr: 0,
4407
- fixedPx: col.width,
4408
- minWidth,
4409
- maxWidth
4410
- };
4411
- const str = col.width.trim();
4412
- if (str.endsWith("fr")) {
4413
- const val = parseFloat(str);
4414
- return {
4415
- key: col.key,
4416
- fr: isNaN(val) ? 1 : val,
4417
- fixedPx: 0,
4418
- minWidth,
4419
- maxWidth
4420
- };
4421
- }
4422
- if (str.endsWith("%")) {
4423
- const pct = parseFloat(str);
4424
- const px = isNaN(pct) ? 0 : pct / 100 * tableWidth;
4425
- return {
4426
- key: col.key,
4427
- fr: 0,
4428
- fixedPx: px,
4429
- minWidth,
4430
- maxWidth
4431
- };
4432
- }
4433
- const px = parseFloat(str);
4434
- return {
4435
- key: col.key,
4436
- fr: 0,
4437
- fixedPx: isNaN(px) ? 0 : px,
4438
- minWidth,
4439
- maxWidth
4440
- };
4441
- }
4442
- /**
4443
- * Distribute `tableWidth` among columns:
4444
- * 1. Fixed-width columns get their declared width (clamped to min/max).
4445
- * 2. Remaining space is distributed among fractional columns proportionally.
4446
- */
4447
- function distributeWidths(columns, tableWidth) {
4448
- const parsed = columns.map((c) => parseColumnDef(c, tableWidth));
4449
- let usedSpace = 0;
4450
- let totalFr = 0;
4451
- for (const p of parsed) if (p.fr > 0) totalFr += p.fr;
4452
- else {
4453
- const w = Math.max(p.minWidth, Math.min(p.maxWidth, p.fixedPx));
4454
- usedSpace += w;
4455
- }
4456
- const remainingSpace = Math.max(0, tableWidth - usedSpace);
4457
- const perFr = totalFr > 0 ? remainingSpace / totalFr : 0;
4458
- const widths = /* @__PURE__ */ new Map();
4459
- for (const p of parsed) {
4460
- let w;
4461
- if (p.fr > 0) w = p.fr * perFr;
4462
- else w = p.fixedPx;
4463
- w = Math.max(p.minWidth, Math.min(p.maxWidth, w));
4464
- widths.set(p.key, Math.round(w));
4465
- }
4466
- return widths;
4467
- }
4468
- /**
4469
- * Creates column resize state for a table component.
4470
- *
4471
- * Builds an initial column width map from column definitions + table width,
4472
- * then tracks resize operations that clamp to min/max and redistribute
4473
- * remaining space among flex columns.
4474
- */
4475
- function createTableColumnResizeState(props) {
4476
- const getProps = () => props();
4477
- const initialWidths = createMemo(() => distributeWidths(getProps().columns, getProps().tableWidth));
4478
- const [overrides, setOverrides] = createSignal(/* @__PURE__ */ new Map());
4479
- const [resizingColumn, setResizingColumn] = createSignal(null);
4480
- const columnWidths = createMemo(() => {
4481
- const base = initialWidths();
4482
- const over = overrides();
4483
- if (over.size === 0) return base;
4484
- const merged = new Map(base);
4485
- for (const [key, width] of over) if (merged.has(key)) merged.set(key, width);
4486
- return merged;
4487
- });
4488
- const minWidthMap = createMemo(() => {
4489
- const map = /* @__PURE__ */ new Map();
4490
- for (const col of getProps().columns) map.set(col.key, col.minWidth ?? DEFAULT_MIN_WIDTH);
4491
- return map;
4492
- });
4493
- const maxWidthMap = createMemo(() => {
4494
- const map = /* @__PURE__ */ new Map();
4495
- for (const col of getProps().columns) map.set(col.key, col.maxWidth ?? DEFAULT_MAX_WIDTH);
4496
- return map;
4497
- });
4498
- const getColumnWidth = (key) => {
4499
- return columnWidths().get(key) ?? 0;
4500
- };
4501
- const getColumnMinWidth = (key) => {
4502
- return minWidthMap().get(key) ?? DEFAULT_MIN_WIDTH;
4503
- };
4504
- const getColumnMaxWidth = (key) => {
4505
- return maxWidthMap().get(key) ?? DEFAULT_MAX_WIDTH;
4506
- };
4507
- const startResize = (key) => {
4508
- setResizingColumn(key);
4509
- };
4510
- const endResize = () => {
4511
- setResizingColumn(null);
4512
- };
4513
- const updateResizedColumns = (key, width) => {
4514
- const minW = getColumnMinWidth(key);
4515
- const maxW = getColumnMaxWidth(key);
4516
- const clampedWidth = Math.round(Math.max(minW, Math.min(maxW, width)));
4517
- const newOverrides = new Map(overrides());
4518
- newOverrides.set(key, clampedWidth);
4519
- setOverrides(newOverrides);
4520
- const base = initialWidths();
4521
- const merged = new Map(base);
4522
- for (const [k, w] of newOverrides) if (merged.has(k)) merged.set(k, w);
4523
- return merged;
4524
- };
4525
- return {
4526
- resizingColumn,
4527
- columnWidths,
4528
- startResize,
4529
- endResize,
4530
- updateResizedColumns,
4531
- getColumnWidth,
4532
- getColumnMinWidth,
4533
- getColumnMaxWidth
5303
+ sort,
5304
+ get expandedKeys() {
5305
+ return EMPTY_EXPANDED_KEYS;
5306
+ },
5307
+ get treeColumn() {
5308
+ return getOptions().collection.treeColumn ?? null;
5309
+ },
5310
+ toggleKey: () => {}
4534
5311
  };
4535
5312
  }
5313
+ const EMPTY_EXPANDED_KEYS = /* @__PURE__ */ new Set();
4536
5314
  //#endregion
4537
5315
  //#region src/table/TableCollection.ts
4538
5316
  /**
@@ -4547,8 +5325,13 @@ var TableCollection = class {
4547
5325
  _keyMap = /* @__PURE__ */ new Map();
4548
5326
  _rowHeaderColumnKeys;
4549
5327
  _size = 0;
5328
+ /** Visible rows in DFS order. Drives size, navigation, iteration and the `rows` list. */
5329
+ _flattenedRows = [];
5330
+ _treeColumn = null;
5331
+ _userColumnCount = 0;
4550
5332
  constructor(options) {
4551
- const { columns, rows, getKey, getTextValue, showSelectionCheckboxes = false, rowHeaderColumnKeys } = options;
5333
+ const { columns, rows, getKey, getTextValue, showSelectionCheckboxes = false, rowHeaderColumnKeys, expandedKeys } = options;
5334
+ const isTreeGrid = expandedKeys !== void 0;
4552
5335
  this._columns = this.buildColumns(columns, showSelectionCheckboxes);
4553
5336
  this._rowHeaderColumnKeys = rowHeaderColumnKeys ?? this.getDefaultRowHeaderColumnKeys();
4554
5337
  this._headerRows = this.buildHeaderRows();
@@ -4565,20 +5348,33 @@ var TableCollection = class {
4565
5348
  };
4566
5349
  this._keyMap.set("header", this._head);
4567
5350
  }
4568
- const bodyRows = this.buildRows(rows, getKey, getTextValue);
4569
- this._size = bodyRows.length;
5351
+ let bodyChildren;
5352
+ if (isTreeGrid) {
5353
+ const built = this.buildTreeRows(rows, expandedKeys, getKey, getTextValue);
5354
+ bodyChildren = built.topLevelRows;
5355
+ this._flattenedRows = built.flattenedRows;
5356
+ this._userColumnCount = this.countLeafColumns();
5357
+ this._treeColumn = options.treeColumn !== void 0 ? options.treeColumn : this._rowHeaderColumnKeys.values().next().value ?? null;
5358
+ } else {
5359
+ const bodyRows = this.buildRows(rows, getKey, getTextValue);
5360
+ bodyChildren = bodyRows;
5361
+ this._flattenedRows = bodyRows;
5362
+ this._userColumnCount = this.countLeafColumns();
5363
+ this._treeColumn = null;
5364
+ }
5365
+ this._size = this._flattenedRows.length;
4570
5366
  this._body = {
4571
5367
  type: "item",
4572
5368
  key: "body",
4573
5369
  index: this._headerRows.length,
4574
5370
  level: 0,
4575
5371
  hasChildNodes: true,
4576
- childNodes: bodyRows,
5372
+ childNodes: bodyChildren,
4577
5373
  value: null,
4578
5374
  textValue: ""
4579
5375
  };
4580
5376
  this._keyMap.set("body", this._body);
4581
- this._rows = [...this._headerRows, ...bodyRows];
5377
+ this._rows = [...this._headerRows, ...this._flattenedRows];
4582
5378
  }
4583
5379
  buildColumns(columns, showSelectionCheckboxes) {
4584
5380
  const result = [];
@@ -4644,6 +5440,17 @@ var TableCollection = class {
4644
5440
  for (const col of this._columns) if (col.key !== "__selection__") return new Set([col.key]);
4645
5441
  return /* @__PURE__ */ new Set();
4646
5442
  }
5443
+ /** Counts the leaf columns supplied by the user (excludes the selection column). */
5444
+ countLeafColumns() {
5445
+ let count = 0;
5446
+ const visit = (col) => {
5447
+ if (col.key === "__selection__") return;
5448
+ if (col.childNodes.length > 0) col.childNodes.forEach(visit);
5449
+ else count++;
5450
+ };
5451
+ this._columns.forEach(visit);
5452
+ return count;
5453
+ }
4647
5454
  buildHeaderRows() {
4648
5455
  if (this._columns.length === 0) return [];
4649
5456
  const headerCells = this._columns.map((col, index) => ({
@@ -4673,6 +5480,29 @@ var TableCollection = class {
4673
5480
  headerCells.forEach((cell) => this._keyMap.set(cell.key, cell));
4674
5481
  return [headerRow];
4675
5482
  }
5483
+ /**
5484
+ * Builds the cell nodes for one row. Does not register the cells in the key map;
5485
+ * the caller registers cells once the owning row is known to be present.
5486
+ */
5487
+ buildCells(rowKey, value, rowLevel, getTextValue) {
5488
+ return this._columns.map((col, colIndex) => {
5489
+ const cellKey = `${rowKey}-${col.key}`;
5490
+ const columnDefinition = col.props?.columnDefinition ?? { key: col.key };
5491
+ const cellTextValue = col.key === "__selection__" ? "Selection" : getTextValue ? getTextValue(value, columnDefinition) : String(value?.[String(col.key)] ?? "");
5492
+ return {
5493
+ type: this._rowHeaderColumnKeys.has(col.key) ? "rowheader" : "cell",
5494
+ key: cellKey,
5495
+ index: colIndex,
5496
+ column: colIndex,
5497
+ level: rowLevel + 1,
5498
+ hasChildNodes: false,
5499
+ childNodes: [],
5500
+ value,
5501
+ textValue: cellTextValue,
5502
+ parentKey: rowKey
5503
+ };
5504
+ });
5505
+ }
4676
5506
  buildRows(rows, getKey, getTextValue) {
4677
5507
  const result = [];
4678
5508
  const rowIndexOffset = this._headerRows.length;
@@ -4682,25 +5512,8 @@ var TableCollection = class {
4682
5512
  const key = isRowDef ? row.key : getKey?.(row) ?? i;
4683
5513
  const value = isRowDef ? row.value : row;
4684
5514
  const textValue = isRowDef ? row.textValue : typeof row?.textValue === "string" ? row.textValue : void 0;
4685
- const cells = this._columns.map((col, colIndex) => {
4686
- const cellKey = `${key}-${col.key}`;
4687
- const columnDefinition = col.props?.columnDefinition ?? { key: col.key };
4688
- const cellTextValue = col.key === "__selection__" ? "Selection" : getTextValue ? getTextValue(value, columnDefinition) : String(value?.[String(col.key)] ?? "");
4689
- const cell = {
4690
- type: this._rowHeaderColumnKeys.has(col.key) ? "rowheader" : "cell",
4691
- key: cellKey,
4692
- index: colIndex,
4693
- column: colIndex,
4694
- level: 1,
4695
- hasChildNodes: false,
4696
- childNodes: [],
4697
- value,
4698
- textValue: cellTextValue,
4699
- parentKey: key
4700
- };
4701
- this._keyMap.set(cellKey, cell);
4702
- return cell;
4703
- });
5515
+ const cells = this.buildCells(key, value, 0, getTextValue);
5516
+ cells.forEach((cell) => this._keyMap.set(cell.key, cell));
4704
5517
  const rowNode = {
4705
5518
  type: "item",
4706
5519
  key,
@@ -4717,6 +5530,58 @@ var TableCollection = class {
4717
5530
  }
4718
5531
  return result;
4719
5532
  }
5533
+ /**
5534
+ * Builds the tree-grid rows: every row keeps its full structure (cells then all child
5535
+ * rows, expanded or not) while only the rows whose ancestors are all expanded are
5536
+ * registered in the key map and pushed into the flattened body. Mirrors
5537
+ * `generateTreeGridCollection` upstream.
5538
+ */
5539
+ buildTreeRows(rows, expandedKeys, getKey, getTextValue) {
5540
+ const isExpanded = (key) => expandedKeys === "all" || expandedKeys.has(key);
5541
+ const buildRowNode = (row, level, parentKey, indexAmongSiblings) => {
5542
+ const isRowDef = this.isRowDefinition(row);
5543
+ const key = isRowDef ? row.key : getKey?.(row) ?? indexAmongSiblings;
5544
+ const value = isRowDef ? row.value : row;
5545
+ const textValue = isRowDef ? row.textValue : typeof row?.textValue === "string" ? row.textValue : void 0;
5546
+ const cells = this.buildCells(key, value, level, getTextValue);
5547
+ const childRowDefs = isRowDef ? row.childRows ?? [] : [];
5548
+ const hasChildRows = childRowDefs.length > 0 || isRowDef && !!row.hasChildRows;
5549
+ const childRowNodes = childRowDefs.map((child, childIdx) => buildRowNode(child, level + 1, key, childIdx));
5550
+ const childNodes = [...cells, ...childRowNodes];
5551
+ return {
5552
+ type: "item",
5553
+ key,
5554
+ index: indexAmongSiblings,
5555
+ level,
5556
+ parentKey,
5557
+ hasChildNodes: true,
5558
+ childNodes,
5559
+ value,
5560
+ textValue: textValue ?? cells.map((c) => c.textValue).join(" "),
5561
+ isExpandable: hasChildRows,
5562
+ isExpanded: hasChildRows && isExpanded(key),
5563
+ firstChildKey: childNodes[0]?.key ?? null,
5564
+ lastChildKey: childNodes[childNodes.length - 1]?.key ?? null
5565
+ };
5566
+ };
5567
+ const topLevelRows = rows.map((row, i) => buildRowNode(row, 0, "body", i));
5568
+ const flattenedRows = [];
5569
+ const rowIndexOffset = this._headerRows.length;
5570
+ const visit = (node) => {
5571
+ node.rowIndex = rowIndexOffset + flattenedRows.length;
5572
+ flattenedRows.push(node);
5573
+ this._keyMap.set(node.key, node);
5574
+ for (const child of node.childNodes) if (child.type !== "item") this._keyMap.set(child.key, child);
5575
+ if (isExpanded(node.key)) {
5576
+ for (const child of node.childNodes) if (child.type === "item") visit(child);
5577
+ }
5578
+ };
5579
+ for (const node of topLevelRows) visit(node);
5580
+ return {
5581
+ topLevelRows,
5582
+ flattenedRows
5583
+ };
5584
+ }
4720
5585
  isRowDefinition(row) {
4721
5586
  return typeof row === "object" && row !== null && "key" in row && "value" in row;
4722
5587
  }
@@ -4747,6 +5612,18 @@ var TableCollection = class {
4747
5612
  get rowHeaderColumnKeys() {
4748
5613
  return this._rowHeaderColumnKeys;
4749
5614
  }
5615
+ /** The key of the column that renders the tree-grid affordance, or `null` for a flat table. */
5616
+ get treeColumn() {
5617
+ return this._treeColumn;
5618
+ }
5619
+ /** The number of leaf columns provided by the user (excludes the selection column). */
5620
+ get userColumnCount() {
5621
+ return this._userColumnCount;
5622
+ }
5623
+ /** The key map of all present nodes (visible rows, their cells, columns and the body). */
5624
+ get keyMap() {
5625
+ return this._keyMap;
5626
+ }
4750
5627
  getKeys() {
4751
5628
  return this._keyMap.keys();
4752
5629
  }
@@ -4754,9 +5631,8 @@ var TableCollection = class {
4754
5631
  return this._keyMap.get(key) ?? null;
4755
5632
  }
4756
5633
  at(index) {
4757
- const bodyRows = this._body.childNodes;
4758
- if (index < 0 || index >= bodyRows.length) return null;
4759
- return bodyRows[index];
5634
+ if (index < 0 || index >= this._flattenedRows.length) return null;
5635
+ return this._flattenedRows[index];
4760
5636
  }
4761
5637
  getChildren(key) {
4762
5638
  return this._keyMap.get(key)?.childNodes ?? [];
@@ -4769,29 +5645,29 @@ var TableCollection = class {
4769
5645
  return this._keyMap.get(cellKey) ?? null;
4770
5646
  }
4771
5647
  getFirstKey() {
4772
- const bodyRows = this._body.childNodes;
5648
+ const bodyRows = this._flattenedRows;
4773
5649
  return bodyRows.length > 0 ? bodyRows[0].key : null;
4774
5650
  }
4775
5651
  getLastKey() {
4776
- const bodyRows = this._body.childNodes;
5652
+ const bodyRows = this._flattenedRows;
4777
5653
  return bodyRows.length > 0 ? bodyRows[bodyRows.length - 1].key : null;
4778
5654
  }
4779
5655
  getKeyBefore(key) {
4780
5656
  if (!this._keyMap.get(key)) return null;
4781
- const bodyRows = this._body.childNodes;
5657
+ const bodyRows = this._flattenedRows;
4782
5658
  const index = bodyRows.findIndex((r) => r.key === key);
4783
5659
  if (index > 0) return bodyRows[index - 1].key;
4784
5660
  return null;
4785
5661
  }
4786
5662
  getKeyAfter(key) {
4787
5663
  if (!this._keyMap.get(key)) return null;
4788
- const bodyRows = this._body.childNodes;
5664
+ const bodyRows = this._flattenedRows;
4789
5665
  const index = bodyRows.findIndex((r) => r.key === key);
4790
5666
  if (index >= 0 && index < bodyRows.length - 1) return bodyRows[index + 1].key;
4791
5667
  return null;
4792
5668
  }
4793
5669
  *[Symbol.iterator]() {
4794
- for (const row of this._body.childNodes) yield row;
5670
+ for (const row of this._flattenedRows) yield row;
4795
5671
  }
4796
5672
  };
4797
5673
  /**
@@ -4801,6 +5677,257 @@ function createTableCollection(options) {
4801
5677
  return new TableCollection(options);
4802
5678
  }
4803
5679
  //#endregion
5680
+ //#region src/table/createTreeGridState.ts
5681
+ /**
5682
+ * Tree grid state management for expandable Table rows.
5683
+ * Based on @react-stately/table/useTreeGridState (the `UNSTABLE_` tree-grid feature).
5684
+ *
5685
+ * Owns the expanded-keys signal (controlled or uncontrolled), rebuilds the table
5686
+ * collection from it, and layers the expansion API on top of {@link createTableState}.
5687
+ */
5688
+ /**
5689
+ * Creates state management for a tree grid (a table with expandable rows). Handles the
5690
+ * expanded rows in addition to the selection, sort and focus handled by the table state.
5691
+ */
5692
+ function createTreeGridState(options) {
5693
+ const getOptions = () => options();
5694
+ const [internalExpandedKeys, setInternalExpandedKeys] = createSignal(getInitialExpanded(getOptions().UNSTABLE_defaultExpandedKeys));
5695
+ const expandedKeys = createMemo(() => {
5696
+ const controlled = getOptions().UNSTABLE_expandedKeys;
5697
+ if (controlled !== void 0) return convertExpanded(controlled);
5698
+ return internalExpandedKeys();
5699
+ });
5700
+ const collection = createMemo(() => createTableCollection({
5701
+ columns: getOptions().columns,
5702
+ rows: getOptions().rows,
5703
+ getKey: getOptions().getKey,
5704
+ getTextValue: getOptions().getTextValue,
5705
+ showSelectionCheckboxes: getOptions().showSelectionCheckboxes ?? false,
5706
+ rowHeaderColumnKeys: getOptions().rowHeaderColumnKeys,
5707
+ expandedKeys: expandedKeys()
5708
+ }));
5709
+ const tableState = createTableState(() => ({
5710
+ collection: collection(),
5711
+ disabledKeys: getOptions().disabledKeys,
5712
+ focusMode: getOptions().focusMode,
5713
+ selectionMode: getOptions().selectionMode,
5714
+ selectionBehavior: getOptions().selectionBehavior,
5715
+ disallowEmptySelection: getOptions().disallowEmptySelection,
5716
+ selectedKeys: getOptions().selectedKeys,
5717
+ defaultSelectedKeys: getOptions().defaultSelectedKeys,
5718
+ onSelectionChange: getOptions().onSelectionChange,
5719
+ sortDescriptor: getOptions().sortDescriptor,
5720
+ onSortChange: getOptions().onSortChange,
5721
+ showSelectionCheckboxes: getOptions().showSelectionCheckboxes
5722
+ }));
5723
+ const toggleKey = (key) => {
5724
+ const updated = toggleExpandedKey(expandedKeys(), key, collection());
5725
+ if (getOptions().UNSTABLE_expandedKeys === void 0) setInternalExpandedKeys(updated);
5726
+ getOptions().UNSTABLE_onExpandedChange?.(updated);
5727
+ };
5728
+ const treeGridState = tableState;
5729
+ Object.defineProperty(treeGridState, "expandedKeys", {
5730
+ get: () => expandedKeys(),
5731
+ configurable: true,
5732
+ enumerable: true
5733
+ });
5734
+ Object.defineProperty(treeGridState, "treeColumn", {
5735
+ get: () => collection().treeColumn ?? null,
5736
+ configurable: true,
5737
+ enumerable: true
5738
+ });
5739
+ Object.defineProperty(treeGridState, "keyMap", {
5740
+ get: () => collection().keyMap,
5741
+ configurable: true,
5742
+ enumerable: true
5743
+ });
5744
+ Object.defineProperty(treeGridState, "userColumnCount", {
5745
+ get: () => collection().userColumnCount,
5746
+ configurable: true,
5747
+ enumerable: true
5748
+ });
5749
+ treeGridState.toggleKey = toggleKey;
5750
+ return treeGridState;
5751
+ }
5752
+ /**
5753
+ * Computes the next expanded-key set when a row is toggled. Mirrors the upstream
5754
+ * `toggleKey`: collapsing from `'all'` materialises every expandable row first.
5755
+ */
5756
+ function toggleExpandedKey(current, key, collection) {
5757
+ let updated;
5758
+ if (current === "all") {
5759
+ updated = new Set(collection.rows.filter((row) => row.type === "item" && row.isExpandable).map((row) => row.key));
5760
+ updated.delete(key);
5761
+ } else {
5762
+ updated = new Set(current);
5763
+ if (updated.has(key)) updated.delete(key);
5764
+ else updated.add(key);
5765
+ }
5766
+ return updated;
5767
+ }
5768
+ function getInitialExpanded(defaultExpanded) {
5769
+ if (defaultExpanded === void 0) return /* @__PURE__ */ new Set();
5770
+ return convertExpanded(defaultExpanded);
5771
+ }
5772
+ function convertExpanded(expanded) {
5773
+ if (!expanded) return /* @__PURE__ */ new Set();
5774
+ return expanded === "all" ? "all" : new Set(expanded);
5775
+ }
5776
+ //#endregion
5777
+ //#region src/table/createTableColumnResizeState.ts
5778
+ /**
5779
+ * Table column resize state management.
5780
+ * Based on @react-stately/table/useTableColumnResizeState.
5781
+ *
5782
+ * Provides column width state management for a table with column resizing.
5783
+ * Tracks column widths, the currently resizing column, and handles
5784
+ * width distribution and clamping.
5785
+ */
5786
+ const DEFAULT_MIN_WIDTH = 75;
5787
+ const DEFAULT_MAX_WIDTH = Infinity;
5788
+ function parseColumnDef(col, tableWidth) {
5789
+ const minWidth = col.minWidth ?? DEFAULT_MIN_WIDTH;
5790
+ const maxWidth = col.maxWidth ?? DEFAULT_MAX_WIDTH;
5791
+ if (col.width == null) return {
5792
+ key: col.key,
5793
+ fr: 1,
5794
+ fixedPx: 0,
5795
+ minWidth,
5796
+ maxWidth
5797
+ };
5798
+ if (typeof col.width === "number") return {
5799
+ key: col.key,
5800
+ fr: 0,
5801
+ fixedPx: col.width,
5802
+ minWidth,
5803
+ maxWidth
5804
+ };
5805
+ const str = col.width.trim();
5806
+ if (str.endsWith("fr")) {
5807
+ const val = parseFloat(str);
5808
+ return {
5809
+ key: col.key,
5810
+ fr: isNaN(val) ? 1 : val,
5811
+ fixedPx: 0,
5812
+ minWidth,
5813
+ maxWidth
5814
+ };
5815
+ }
5816
+ if (str.endsWith("%")) {
5817
+ const pct = parseFloat(str);
5818
+ const px = isNaN(pct) ? 0 : pct / 100 * tableWidth;
5819
+ return {
5820
+ key: col.key,
5821
+ fr: 0,
5822
+ fixedPx: px,
5823
+ minWidth,
5824
+ maxWidth
5825
+ };
5826
+ }
5827
+ const px = parseFloat(str);
5828
+ return {
5829
+ key: col.key,
5830
+ fr: 0,
5831
+ fixedPx: isNaN(px) ? 0 : px,
5832
+ minWidth,
5833
+ maxWidth
5834
+ };
5835
+ }
5836
+ /**
5837
+ * Distribute `tableWidth` among columns:
5838
+ * 1. Fixed-width columns get their declared width (clamped to min/max).
5839
+ * 2. Remaining space is distributed among fractional columns proportionally.
5840
+ */
5841
+ function distributeWidths(columns, tableWidth) {
5842
+ const parsed = columns.map((c) => parseColumnDef(c, tableWidth));
5843
+ let usedSpace = 0;
5844
+ let totalFr = 0;
5845
+ for (const p of parsed) if (p.fr > 0) totalFr += p.fr;
5846
+ else {
5847
+ const w = Math.max(p.minWidth, Math.min(p.maxWidth, p.fixedPx));
5848
+ usedSpace += w;
5849
+ }
5850
+ const remainingSpace = Math.max(0, tableWidth - usedSpace);
5851
+ const perFr = totalFr > 0 ? remainingSpace / totalFr : 0;
5852
+ const widths = /* @__PURE__ */ new Map();
5853
+ for (const p of parsed) {
5854
+ let w;
5855
+ if (p.fr > 0) w = p.fr * perFr;
5856
+ else w = p.fixedPx;
5857
+ w = Math.max(p.minWidth, Math.min(p.maxWidth, w));
5858
+ widths.set(p.key, Math.round(w));
5859
+ }
5860
+ return widths;
5861
+ }
5862
+ /**
5863
+ * Creates column resize state for a table component.
5864
+ *
5865
+ * Builds an initial column width map from column definitions + table width,
5866
+ * then tracks resize operations that clamp to min/max and redistribute
5867
+ * remaining space among flex columns.
5868
+ */
5869
+ function createTableColumnResizeState(props) {
5870
+ const getProps = () => props();
5871
+ const initialWidths = createMemo(() => distributeWidths(getProps().columns, getProps().tableWidth));
5872
+ const [overrides, setOverrides] = createSignal(/* @__PURE__ */ new Map());
5873
+ const [resizingColumn, setResizingColumn] = createSignal(null);
5874
+ const columnWidths = createMemo(() => {
5875
+ const base = initialWidths();
5876
+ const over = overrides();
5877
+ if (over.size === 0) return base;
5878
+ const merged = new Map(base);
5879
+ for (const [key, width] of over) if (merged.has(key)) merged.set(key, width);
5880
+ return merged;
5881
+ });
5882
+ const minWidthMap = createMemo(() => {
5883
+ const map = /* @__PURE__ */ new Map();
5884
+ for (const col of getProps().columns) map.set(col.key, col.minWidth ?? DEFAULT_MIN_WIDTH);
5885
+ return map;
5886
+ });
5887
+ const maxWidthMap = createMemo(() => {
5888
+ const map = /* @__PURE__ */ new Map();
5889
+ for (const col of getProps().columns) map.set(col.key, col.maxWidth ?? DEFAULT_MAX_WIDTH);
5890
+ return map;
5891
+ });
5892
+ const getColumnWidth = (key) => {
5893
+ return columnWidths().get(key) ?? 0;
5894
+ };
5895
+ const getColumnMinWidth = (key) => {
5896
+ return minWidthMap().get(key) ?? DEFAULT_MIN_WIDTH;
5897
+ };
5898
+ const getColumnMaxWidth = (key) => {
5899
+ return maxWidthMap().get(key) ?? DEFAULT_MAX_WIDTH;
5900
+ };
5901
+ const startResize = (key) => {
5902
+ setResizingColumn(key);
5903
+ };
5904
+ const endResize = () => {
5905
+ setResizingColumn(null);
5906
+ };
5907
+ const updateResizedColumns = (key, width) => {
5908
+ const minW = getColumnMinWidth(key);
5909
+ const maxW = getColumnMaxWidth(key);
5910
+ const clampedWidth = Math.round(Math.max(minW, Math.min(maxW, width)));
5911
+ const newOverrides = new Map(overrides());
5912
+ newOverrides.set(key, clampedWidth);
5913
+ setOverrides(newOverrides);
5914
+ const base = initialWidths();
5915
+ const merged = new Map(base);
5916
+ for (const [k, w] of newOverrides) if (merged.has(k)) merged.set(k, w);
5917
+ return merged;
5918
+ };
5919
+ return {
5920
+ resizingColumn,
5921
+ columnWidths,
5922
+ startResize,
5923
+ endResize,
5924
+ updateResizedColumns,
5925
+ getColumnWidth,
5926
+ getColumnMinWidth,
5927
+ getColumnMaxWidth
5928
+ };
5929
+ }
5930
+ //#endregion
4804
5931
  //#region src/tree/createTreeState.ts
4805
5932
  /**
4806
5933
  * Tree state management for Tree components.
@@ -4839,9 +5966,16 @@ function createTreeState(options) {
4839
5966
  return internalSelectedKeys();
4840
5967
  });
4841
5968
  const selectionMode = createMemo(() => getOptions().selectionMode ?? "none");
4842
- const selectionBehavior = createMemo(() => getOptions().selectionBehavior ?? "toggle");
5969
+ const selectionBehaviorProp = () => getOptions().selectionBehavior ?? "toggle";
5970
+ const [selectionBehaviorState, setSelectionBehaviorState] = createSignal(selectionBehaviorProp());
5971
+ const selectionBehavior = createMemo(() => selectionBehaviorState());
4843
5972
  const disallowEmptySelection = createMemo(() => getOptions().disallowEmptySelection ?? false);
4844
5973
  const disabledBehavior = createMemo(() => getOptions().disabledBehavior ?? "all");
5974
+ createEffect(on(selectionBehaviorProp, (behavior) => setSelectionBehaviorState(behavior)));
5975
+ createEffect(() => {
5976
+ const keys = selectedKeys();
5977
+ if (selectionBehaviorProp() === "replace" && selectionBehaviorState() === "toggle" && keys !== "all" && keys.size === 0) setSelectionBehaviorState("replace");
5978
+ });
4845
5979
  const setFocusedKey = (key, strategy = "first") => {
4846
5980
  setFocusedKeyInternal(key);
4847
5981
  setChildFocusStrategy(strategy);
@@ -4905,7 +6039,7 @@ function createTreeState(options) {
4905
6039
  }
4906
6040
  };
4907
6041
  const toggleSelection = (key) => {
4908
- if (isDisabled(key) && disabledBehavior() === "all") return;
6042
+ if (isDisabled(key)) return;
4909
6043
  if (selectionMode() === "none") return;
4910
6044
  const current = selectedKeys();
4911
6045
  if (selectionMode() === "single") {
@@ -4922,13 +6056,16 @@ function createTreeState(options) {
4922
6056
  setAnchorKey(key);
4923
6057
  };
4924
6058
  const replaceSelection = (key) => {
4925
- if (isDisabled(key) && disabledBehavior() === "all") return;
6059
+ if (isDisabled(key)) return;
4926
6060
  if (selectionMode() === "none") return;
4927
6061
  updateSelection(new Set([key]));
4928
6062
  setAnchorKey(key);
4929
6063
  };
6064
+ const setSelectedKeys = (keys) => {
6065
+ updateSelection(normalizeSelection(keys));
6066
+ };
4930
6067
  const extendSelection = (toKey) => {
4931
- if (isDisabled(toKey) && disabledBehavior() === "all") return;
6068
+ if (isDisabled(toKey)) return;
4932
6069
  if (selectionMode() !== "multiple") {
4933
6070
  replaceSelection(toKey);
4934
6071
  return;
@@ -5012,6 +6149,9 @@ function createTreeState(options) {
5012
6149
  get disabledKeys() {
5013
6150
  return disabledKeys();
5014
6151
  },
6152
+ get disabledBehavior() {
6153
+ return disabledBehavior();
6154
+ },
5015
6155
  get expandedKeys() {
5016
6156
  return expandedKeys();
5017
6157
  },
@@ -5033,6 +6173,9 @@ function createTreeState(options) {
5033
6173
  get selectionBehavior() {
5034
6174
  return selectionBehavior();
5035
6175
  },
6176
+ get disallowEmptySelection() {
6177
+ return disallowEmptySelection();
6178
+ },
5036
6179
  get selectedKeys() {
5037
6180
  return selectedKeys();
5038
6181
  },
@@ -5043,6 +6186,8 @@ function createTreeState(options) {
5043
6186
  setFocused: setIsFocused,
5044
6187
  toggleSelection,
5045
6188
  replaceSelection,
6189
+ setSelectedKeys,
6190
+ setSelectionBehavior: setSelectionBehaviorState,
5046
6191
  extendSelection,
5047
6192
  selectAll,
5048
6193
  clearSelection,
@@ -6615,7 +7760,7 @@ function createColorFieldState(options) {
6615
7760
  };
6616
7761
  const decrementToMin = () => {
6617
7762
  if (!channel()) {
6618
- incrementHex(-HEX_COLOR_MAX);
7763
+ incrementHex(-16777215);
6619
7764
  return;
6620
7765
  }
6621
7766
  updateChannel("min");
@@ -8129,6 +9274,6 @@ function createStepListState(props) {
8129
9274
  };
8130
9275
  }
8131
9276
  //#endregion
8132
- export { CalendarDateClass, CalendarDateTimeClass, DEFAULT_VALIDATION_RESULT, DIRECTORY_DRAG_TYPE, DateFormatter, FormValidationContext, GregorianCalendar, ListCollection, TableCollection, TimeClass, Timer, ToastQueue, 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, createFormValidationState, createGridState, createHSBColor, createHSLColor, createId, createIsSSR, createListCollection, createListData, createListState, createMenuState, createOverlayTriggerState as createMenuTriggerState, createOverlayTriggerState, createOverlayTriggerState as useMenuTriggerState, createNumberFieldState, 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, createTooltipTriggerState, createTreeCollection, createTreeData, createTreeState, defaultContainsFilter, endOfMonth, endOfWeek, endOfYear, getColorChannels, getDayOfWeek, 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 };
9277
+ export { CalendarDateClass, CalendarDateTimeClass, DEFAULT_VALIDATION_RESULT, DIRECTORY_DRAG_TYPE, DateFormatter, FormValidationContext, GregorianCalendar, ListCollection, SelectionManager, TableCollection, TimeClass, Timer, ToastQueue, 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, createFormValidationState, createGridState, createHSBColor, createHSLColor, createId, createIsSSR, createListCollection, createListData, createListState, createMenuState, createOverlayTriggerState as createMenuTriggerState, createOverlayTriggerState, createOverlayTriggerState as useMenuTriggerState, createNumberFieldState, 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, createTooltipTriggerState, createTreeCollection, createTreeData, createTreeGridState, createTreeState, defaultContainsFilter, endOfMonth, endOfWeek, endOfYear, getColorChannels, getDayOfWeek, 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 };
8133
9278
 
8134
9279
  //# sourceMappingURL=index.js.map