@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.
- package/dist/calendar/IncompleteDate.d.ts +61 -0
- package/dist/calendar/createCalendarState.d.ts +20 -10
- package/dist/calendar/createDateFieldState.d.ts +10 -3
- package/dist/calendar/createRangeCalendarState.d.ts +10 -2
- package/dist/calendar/index.d.ts +1 -1
- package/dist/collections/createListState.d.ts +4 -1
- package/dist/collections/createSelectionState.d.ts +17 -5
- package/dist/collections/getChildNodes.d.ts +11 -0
- package/dist/collections/index.d.ts +2 -1
- package/dist/combobox/createComboBoxState.d.ts +7 -0
- package/dist/dnd/index.d.ts +1 -1
- package/dist/dnd/types.d.ts +3 -1
- package/dist/grid/types.d.ts +17 -3
- package/dist/index.d.ts +6 -6
- package/dist/index.js +1570 -425
- package/dist/index.js.map +1 -1
- package/dist/select/createSelectState.d.ts +6 -0
- package/dist/selection/Selection.d.ts +16 -0
- package/dist/selection/SelectionManager.d.ts +110 -0
- package/dist/selection/createMultipleSelectionState.d.ts +59 -0
- package/dist/selection/index.d.ts +1 -0
- package/dist/table/TableCollection.d.ts +30 -0
- package/dist/table/createTreeGridState.d.ts +15 -0
- package/dist/table/index.d.ts +2 -1
- package/dist/table/types.d.ts +58 -0
- package/dist/toast/createToastState.d.ts +16 -0
- package/dist/toast/index.d.ts +1 -1
- package/dist/tree/types.d.ts +14 -6
- package/package.json +1 -1
- package/src/calendar/IncompleteDate.ts +430 -0
- package/src/calendar/createCalendarState.ts +104 -37
- package/src/calendar/createDateFieldState.ts +262 -261
- package/src/calendar/createRangeCalendarState.ts +131 -19
- package/src/calendar/index.ts +2 -0
- package/src/collections/createListState.ts +50 -19
- package/src/collections/createMenuState.ts +3 -0
- package/src/collections/createSelectionState.ts +38 -28
- package/src/collections/getChildNodes.ts +98 -0
- package/src/collections/index.ts +9 -0
- package/src/combobox/createComboBoxState.ts +8 -0
- package/src/dnd/index.d.ts +1 -0
- package/src/dnd/index.ts +1 -0
- package/src/dnd/types.d.ts +3 -1
- package/src/dnd/types.ts +4 -1
- package/src/grid/createGridState.ts +36 -2
- package/src/grid/types.ts +17 -3
- package/src/index.d.ts +1 -0
- package/src/index.ts +11 -0
- package/src/select/createSelectState.ts +15 -2
- package/src/selection/Selection.ts +28 -0
- package/src/selection/SelectionManager.ts +526 -0
- package/src/selection/createMultipleSelectionState.ts +247 -0
- package/src/selection/index.ts +6 -0
- package/src/table/TableCollection.ts +213 -43
- package/src/table/createTableState.ts +20 -0
- package/src/table/createTreeGridState.ts +146 -0
- package/src/table/index.ts +3 -0
- package/src/table/types.ts +66 -0
- package/src/tabs/createTabListState.ts +11 -9
- package/src/toast/createToastState.ts +43 -10
- package/src/toast/index.ts +1 -0
- package/src/tree/createTreeState.ts +37 -6
- 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
|
|
867
|
-
if (isDisabled(key)) return;
|
|
866
|
+
const select = (key, e) => {
|
|
868
867
|
if (selectionMode() === "none") return;
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
if (
|
|
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
|
|
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
|
|
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
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1186
|
-
if (
|
|
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
|
|
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 (
|
|
1280
|
-
|
|
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 (!
|
|
1840
|
+
if (!isCellUnavailable(date)) selectDate(date);
|
|
1352
1841
|
};
|
|
1353
1842
|
const selectDate = (date) => {
|
|
1354
1843
|
if (isReadOnly() || isDisabled()) return;
|
|
1355
|
-
if (
|
|
1356
|
-
|
|
1357
|
-
|
|
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
|
|
1457
|
-
const
|
|
1975
|
+
const min = effectiveMinValue();
|
|
1976
|
+
const max = effectiveMaxValue();
|
|
1458
1977
|
let constrained = date;
|
|
1459
|
-
if (
|
|
1460
|
-
if (
|
|
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
|
|
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
|
|
1652
|
-
const
|
|
2179
|
+
const min = effectiveMinValue();
|
|
2180
|
+
const max = effectiveMaxValue();
|
|
1653
2181
|
const calDate = toDisplayCalendarDate(date);
|
|
1654
|
-
if (
|
|
1655
|
-
if (
|
|
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
|
|
1660
|
-
const
|
|
2187
|
+
const min = effectiveMinValue();
|
|
2188
|
+
const max = effectiveMaxValue();
|
|
1661
2189
|
const calDate = toDisplayCalendarDate(date);
|
|
1662
|
-
if (
|
|
1663
|
-
if (
|
|
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
|
|
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)
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
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
|
|
2772
|
+
const dateValue = createMemo(() => {
|
|
1922
2773
|
const v = value();
|
|
1923
|
-
|
|
1924
|
-
const
|
|
1925
|
-
|
|
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
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
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")
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
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
|
|
2827
|
+
return result;
|
|
1967
2828
|
});
|
|
1968
|
-
const
|
|
1969
|
-
if (
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
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
|
|
2836
|
+
const setValue = (newValue) => {
|
|
1975
2837
|
if (isDisabled() || isReadOnly()) return;
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
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
|
|
2861
|
+
const adjustSegment = (type, amount) => {
|
|
1984
2862
|
if (isDisabled() || isReadOnly()) return;
|
|
1985
|
-
|
|
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
|
|
2865
|
+
const setSegment = (type, newValue) => {
|
|
1993
2866
|
if (isDisabled() || isReadOnly()) return;
|
|
1994
|
-
|
|
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
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
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
|
|
2015
|
-
|
|
2016
|
-
const
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
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, (
|
|
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
|
|
2743
|
-
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
|
-
|
|
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
|
|
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
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
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
|
-
|
|
4569
|
-
|
|
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:
|
|
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, ...
|
|
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.
|
|
4686
|
-
|
|
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
|
-
|
|
4758
|
-
|
|
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.
|
|
5648
|
+
const bodyRows = this._flattenedRows;
|
|
4773
5649
|
return bodyRows.length > 0 ? bodyRows[0].key : null;
|
|
4774
5650
|
}
|
|
4775
5651
|
getLastKey() {
|
|
4776
|
-
const bodyRows = this.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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)
|
|
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)
|
|
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)
|
|
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(-
|
|
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
|