@deephaven/components 1.19.1-console-types-fixes.0 → 1.21.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/shortcuts/GlobalShortcuts.js +2 -2
- package/dist/shortcuts/GlobalShortcuts.js.map +1 -1
- package/dist/spectrum/comboBox/ComboBox.d.ts +1 -1
- package/dist/spectrum/index.d.ts +1 -0
- package/dist/spectrum/index.d.ts.map +1 -1
- package/dist/spectrum/index.js +1 -0
- package/dist/spectrum/index.js.map +1 -1
- package/dist/spectrum/listView/ListView.d.ts +1 -1
- package/dist/spectrum/listView/ListView.d.ts.map +1 -1
- package/dist/spectrum/listView/ListView.js.map +1 -1
- package/dist/spectrum/multiSelect/MultiSelect.css +326 -0
- package/dist/spectrum/multiSelect/MultiSelect.css.map +1 -0
- package/dist/spectrum/multiSelect/MultiSelect.d.ts +7 -0
- package/dist/spectrum/multiSelect/MultiSelect.d.ts.map +1 -0
- package/dist/spectrum/multiSelect/MultiSelect.js +445 -0
- package/dist/spectrum/multiSelect/MultiSelect.js.map +1 -0
- package/dist/spectrum/multiSelect/MultiSelectListBox.d.ts +29 -0
- package/dist/spectrum/multiSelect/MultiSelectListBox.d.ts.map +1 -0
- package/dist/spectrum/multiSelect/MultiSelectListBox.js +41 -0
- package/dist/spectrum/multiSelect/MultiSelectListBox.js.map +1 -0
- package/dist/spectrum/multiSelect/MultiSelectNormalized.d.ts +8 -0
- package/dist/spectrum/multiSelect/MultiSelectNormalized.d.ts.map +1 -0
- package/dist/spectrum/multiSelect/MultiSelectNormalized.js +37 -0
- package/dist/spectrum/multiSelect/MultiSelectNormalized.js.map +1 -0
- package/dist/spectrum/multiSelect/MultiSelectProps.d.ts +121 -0
- package/dist/spectrum/multiSelect/MultiSelectProps.d.ts.map +1 -0
- package/dist/spectrum/multiSelect/MultiSelectProps.js +2 -0
- package/dist/spectrum/multiSelect/MultiSelectProps.js.map +1 -0
- package/dist/spectrum/multiSelect/MultiSelectTag.d.ts +14 -0
- package/dist/spectrum/multiSelect/MultiSelectTag.d.ts.map +1 -0
- package/dist/spectrum/multiSelect/MultiSelectTag.js +36 -0
- package/dist/spectrum/multiSelect/MultiSelectTag.js.map +1 -0
- package/dist/spectrum/multiSelect/index.d.ts +4 -0
- package/dist/spectrum/multiSelect/index.d.ts.map +1 -0
- package/dist/spectrum/multiSelect/index.js +4 -0
- package/dist/spectrum/multiSelect/index.js.map +1 -0
- package/dist/spectrum/multiSelect/multiSelectUtils.d.ts +53 -0
- package/dist/spectrum/multiSelect/multiSelectUtils.d.ts.map +1 -0
- package/dist/spectrum/multiSelect/multiSelectUtils.js +166 -0
- package/dist/spectrum/multiSelect/multiSelectUtils.js.map +1 -0
- package/dist/spectrum/multiSelect/useMultiSelectFilter.d.ts +27 -0
- package/dist/spectrum/multiSelect/useMultiSelectFilter.d.ts.map +1 -0
- package/dist/spectrum/multiSelect/useMultiSelectFilter.js +51 -0
- package/dist/spectrum/multiSelect/useMultiSelectFilter.js.map +1 -0
- package/dist/spectrum/multiSelect/useMultiSelectKeyboard.d.ts +40 -0
- package/dist/spectrum/multiSelect/useMultiSelectKeyboard.d.ts.map +1 -0
- package/dist/spectrum/multiSelect/useMultiSelectKeyboard.js +200 -0
- package/dist/spectrum/multiSelect/useMultiSelectKeyboard.js.map +1 -0
- package/dist/spectrum/multiSelect/useMultiSelectLoadingSpinner.d.ts +11 -0
- package/dist/spectrum/multiSelect/useMultiSelectLoadingSpinner.d.ts.map +1 -0
- package/dist/spectrum/multiSelect/useMultiSelectLoadingSpinner.js +44 -0
- package/dist/spectrum/multiSelect/useMultiSelectLoadingSpinner.js.map +1 -0
- package/dist/spectrum/multiSelect/useMultiSelectNormalizedProps.d.ts +21 -0
- package/dist/spectrum/multiSelect/useMultiSelectNormalizedProps.d.ts.map +1 -0
- package/dist/spectrum/multiSelect/useMultiSelectNormalizedProps.js +83 -0
- package/dist/spectrum/multiSelect/useMultiSelectNormalizedProps.js.map +1 -0
- package/dist/spectrum/multiSelect/useMultiSelectScrollListener.d.ts +17 -0
- package/dist/spectrum/multiSelect/useMultiSelectScrollListener.d.ts.map +1 -0
- package/dist/spectrum/multiSelect/useMultiSelectScrollListener.js +55 -0
- package/dist/spectrum/multiSelect/useMultiSelectScrollListener.js.map +1 -0
- package/dist/spectrum/multiSelect/useMultiSelectState.d.ts +26 -0
- package/dist/spectrum/multiSelect/useMultiSelectState.d.ts.map +1 -0
- package/dist/spectrum/multiSelect/useMultiSelectState.js +67 -0
- package/dist/spectrum/multiSelect/useMultiSelectState.js.map +1 -0
- package/dist/spectrum/picker/Picker.d.ts +1 -1
- package/dist/spectrum/picker/PickerProps.d.ts +1 -1
- package/dist/spectrum/picker/PickerProps.d.ts.map +1 -1
- package/dist/spectrum/picker/PickerProps.js.map +1 -1
- package/dist/spectrum/picker/usePickerProps.js +1 -1
- package/dist/spectrum/picker/usePickerProps.js.map +1 -1
- package/dist/spectrum/utils/itemWrapperUtils.d.ts +1 -1
- package/dist/spectrum/utils/itemWrapperUtils.d.ts.map +1 -1
- package/dist/spectrum/utils/itemWrapperUtils.js +3 -0
- package/dist/spectrum/utils/itemWrapperUtils.js.map +1 -1
- package/dist/spectrum/utils/useStringifiedMultiSelection.d.ts.map +1 -1
- package/dist/spectrum/utils/useStringifiedMultiSelection.js +12 -1
- package/dist/spectrum/utils/useStringifiedMultiSelection.js.map +1 -1
- package/package.json +15 -8
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { type KeyboardEvent, type RefObject } from 'react';
|
|
2
|
+
import type { OverlayTriggerState } from '@react-stately/overlays';
|
|
3
|
+
import type { MenuTriggerAction } from '../comboBox';
|
|
4
|
+
import { type MultiSelectFlatItem } from './multiSelectUtils';
|
|
5
|
+
export interface UseMultiSelectKeyboardOptions {
|
|
6
|
+
filteredItems: MultiSelectFlatItem[];
|
|
7
|
+
allItems: MultiSelectFlatItem[];
|
|
8
|
+
shouldFocusWrap: boolean;
|
|
9
|
+
overlayState: OverlayTriggerState;
|
|
10
|
+
openOverlay: (reason: MenuTriggerAction) => void;
|
|
11
|
+
closeOverlay: () => void;
|
|
12
|
+
isReadOnly: boolean;
|
|
13
|
+
isDisabled: boolean;
|
|
14
|
+
searchText: string;
|
|
15
|
+
setSearchText: (value: string) => void;
|
|
16
|
+
selectedKeys: Set<string>;
|
|
17
|
+
toggleKey: (key: string) => void;
|
|
18
|
+
allowsCustomValue: boolean;
|
|
19
|
+
menuTrigger: 'focus' | 'input' | 'manual';
|
|
20
|
+
onKeyDown: ((e: KeyboardEvent) => void) | undefined;
|
|
21
|
+
listBoxContainerRef: RefObject<HTMLElement | null>;
|
|
22
|
+
inputRef: RefObject<HTMLInputElement | null>;
|
|
23
|
+
}
|
|
24
|
+
export interface UseMultiSelectKeyboardResult {
|
|
25
|
+
/** `onKeyDown` handler for the inline input. */
|
|
26
|
+
handleInputKeyDown: (e: KeyboardEvent) => void;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Owns virtual-focus tracking, keyboard handling, and the DOM/scroll
|
|
30
|
+
* side-effects for option highlighting in MultiSelect. Keyboard navigation
|
|
31
|
+
* is handled outside Spectrum's ListBox because the input retains real
|
|
32
|
+
* focus while options are visually highlighted via `data-dh-focused`.
|
|
33
|
+
*
|
|
34
|
+
* Focus is tracked by item key, so it survives filtering, virtualization, etc
|
|
35
|
+
* where the underlying filteredItems array shifts independently of the user's
|
|
36
|
+
* intended focus target.
|
|
37
|
+
*/
|
|
38
|
+
export declare function useMultiSelectKeyboard({ filteredItems, allItems, shouldFocusWrap, overlayState, openOverlay, closeOverlay, isReadOnly, isDisabled, searchText, setSearchText, selectedKeys, toggleKey, allowsCustomValue, menuTrigger, onKeyDown, listBoxContainerRef, inputRef, }: UseMultiSelectKeyboardOptions): UseMultiSelectKeyboardResult;
|
|
39
|
+
export default useMultiSelectKeyboard;
|
|
40
|
+
//# sourceMappingURL=useMultiSelectKeyboard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useMultiSelectKeyboard.d.ts","sourceRoot":"","sources":["../../../src/spectrum/multiSelect/useMultiSelectKeyboard.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,SAAS,EAKf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAE9D,MAAM,WAAW,6BAA6B;IAC5C,aAAa,EAAE,mBAAmB,EAAE,CAAC;IACrC,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC,eAAe,EAAE,OAAO,CAAC;IACzB,YAAY,EAAE,mBAAmB,CAAC;IAClC,WAAW,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACjD,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC1B,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,iBAAiB,EAAE,OAAO,CAAC;IAC3B,WAAW,EAAE,OAAO,GAAG,OAAO,GAAG,QAAQ,CAAC;IAC1C,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,aAAa,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACpD,mBAAmB,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACnD,QAAQ,EAAE,SAAS,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;CAC9C;AAED,MAAM,WAAW,4BAA4B;IAC3C,gDAAgD;IAChD,kBAAkB,EAAE,CAAC,CAAC,EAAE,aAAa,KAAK,IAAI,CAAC;CAChD;AAWD;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,aAAa,EACb,QAAQ,EACR,eAAe,EACf,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,UAAU,EACV,UAAU,EACV,UAAU,EACV,aAAa,EACb,YAAY,EACZ,SAAS,EACT,iBAAiB,EACjB,WAAW,EACX,SAAS,EACT,mBAAmB,EACnB,QAAQ,GACT,EAAE,6BAA6B,GAAG,4BAA4B,CAwM9D;AAED,eAAe,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Replicates the key-normalization Spectrum applies to listbox option ids
|
|
4
|
+
* (see https://github.com/adobe/react-spectrum/blob/9508b15bf8c0e968c56220548207cc57c7e4f57c/packages/react-aria/src/listbox/utils.ts#L31). Whitespace is stripped so that
|
|
5
|
+
* `<listId>-option-<normalizedKey>` matches the actual rendered DOM `id`.
|
|
6
|
+
*/
|
|
7
|
+
function normalizeKey(key) {
|
|
8
|
+
return key.replace(/\s+/g, '');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Owns virtual-focus tracking, keyboard handling, and the DOM/scroll
|
|
13
|
+
* side-effects for option highlighting in MultiSelect. Keyboard navigation
|
|
14
|
+
* is handled outside Spectrum's ListBox because the input retains real
|
|
15
|
+
* focus while options are visually highlighted via `data-dh-focused`.
|
|
16
|
+
*
|
|
17
|
+
* Focus is tracked by item key, so it survives filtering, virtualization, etc
|
|
18
|
+
* where the underlying filteredItems array shifts independently of the user's
|
|
19
|
+
* intended focus target.
|
|
20
|
+
*/
|
|
21
|
+
export function useMultiSelectKeyboard(_ref) {
|
|
22
|
+
var {
|
|
23
|
+
filteredItems,
|
|
24
|
+
allItems,
|
|
25
|
+
shouldFocusWrap,
|
|
26
|
+
overlayState,
|
|
27
|
+
openOverlay,
|
|
28
|
+
closeOverlay,
|
|
29
|
+
isReadOnly,
|
|
30
|
+
isDisabled,
|
|
31
|
+
searchText,
|
|
32
|
+
setSearchText,
|
|
33
|
+
selectedKeys,
|
|
34
|
+
toggleKey,
|
|
35
|
+
allowsCustomValue,
|
|
36
|
+
menuTrigger,
|
|
37
|
+
onKeyDown,
|
|
38
|
+
listBoxContainerRef,
|
|
39
|
+
inputRef
|
|
40
|
+
} = _ref;
|
|
41
|
+
var [focusedKey, setFocusedKey] = useState(null);
|
|
42
|
+
var moveFocus = useCallback(dir => {
|
|
43
|
+
var len = filteredItems.length;
|
|
44
|
+
if (len === 0) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
setFocusedKey(prev => {
|
|
48
|
+
var currentIdx = prev == null ? -1 : filteredItems.findIndex(i => i.key === prev);
|
|
49
|
+
var nextIdx;
|
|
50
|
+
if (dir === 'down') {
|
|
51
|
+
if (currentIdx === -1) {
|
|
52
|
+
nextIdx = 0;
|
|
53
|
+
} else if (shouldFocusWrap) {
|
|
54
|
+
nextIdx = (currentIdx + 1) % len;
|
|
55
|
+
} else {
|
|
56
|
+
nextIdx = Math.min(currentIdx + 1, len - 1);
|
|
57
|
+
}
|
|
58
|
+
} else if (currentIdx === -1) {
|
|
59
|
+
nextIdx = len - 1;
|
|
60
|
+
} else if (shouldFocusWrap) {
|
|
61
|
+
nextIdx = (currentIdx - 1 + len) % len;
|
|
62
|
+
} else {
|
|
63
|
+
nextIdx = Math.max(currentIdx - 1, 0);
|
|
64
|
+
}
|
|
65
|
+
return filteredItems[nextIdx].key;
|
|
66
|
+
});
|
|
67
|
+
}, [filteredItems, shouldFocusWrap]);
|
|
68
|
+
var handleInputKeyDown = useCallback(e => {
|
|
69
|
+
if (isDisabled) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
switch (e.key) {
|
|
73
|
+
case 'ArrowDown':
|
|
74
|
+
e.preventDefault();
|
|
75
|
+
if (!overlayState.isOpen) {
|
|
76
|
+
openOverlay('manual');
|
|
77
|
+
} else {
|
|
78
|
+
moveFocus('down');
|
|
79
|
+
}
|
|
80
|
+
break;
|
|
81
|
+
case 'ArrowUp':
|
|
82
|
+
e.preventDefault();
|
|
83
|
+
if (overlayState.isOpen) {
|
|
84
|
+
moveFocus('up');
|
|
85
|
+
}
|
|
86
|
+
break;
|
|
87
|
+
case 'Enter':
|
|
88
|
+
{
|
|
89
|
+
if (!overlayState.isOpen) {
|
|
90
|
+
e.preventDefault();
|
|
91
|
+
openOverlay('manual');
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
var focusedItemStillExists = focusedKey != null && filteredItems.some(i => i.key === focusedKey);
|
|
95
|
+
if (!isReadOnly && focusedItemStillExists) {
|
|
96
|
+
e.preventDefault();
|
|
97
|
+
toggleKey(focusedKey);
|
|
98
|
+
} else if (!isReadOnly && allowsCustomValue && searchText.trim() !== '') {
|
|
99
|
+
e.preventDefault();
|
|
100
|
+
var trimmed = searchText.trim();
|
|
101
|
+
// Check if typed text exactly matches an existing item label
|
|
102
|
+
var matchingItem = allItems.find(item => item.label.toLowerCase() === trimmed.toLowerCase());
|
|
103
|
+
toggleKey(matchingItem != null ? matchingItem.key : trimmed);
|
|
104
|
+
setSearchText('');
|
|
105
|
+
} else {
|
|
106
|
+
// No focused item, no custom value — clear search and close
|
|
107
|
+
setSearchText('');
|
|
108
|
+
closeOverlay();
|
|
109
|
+
}
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
case 'Escape':
|
|
113
|
+
if (overlayState.isOpen) {
|
|
114
|
+
e.preventDefault();
|
|
115
|
+
closeOverlay();
|
|
116
|
+
}
|
|
117
|
+
break;
|
|
118
|
+
case 'Tab':
|
|
119
|
+
if (overlayState.isOpen) {
|
|
120
|
+
closeOverlay();
|
|
121
|
+
}
|
|
122
|
+
break;
|
|
123
|
+
case 'Backspace':
|
|
124
|
+
if (searchText === '' && !isReadOnly && selectedKeys.size > 0) {
|
|
125
|
+
var keys = [...selectedKeys];
|
|
126
|
+
var lastKey = keys[keys.length - 1];
|
|
127
|
+
if (lastKey != null) {
|
|
128
|
+
toggleKey(lastKey);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
break;
|
|
132
|
+
default:
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
onKeyDown === null || onKeyDown === void 0 || onKeyDown(e);
|
|
136
|
+
}, [isDisabled, overlayState, openOverlay, closeOverlay, moveFocus, isReadOnly, focusedKey, filteredItems, allItems, allowsCustomValue, toggleKey, searchText, setSearchText, selectedKeys, onKeyDown]);
|
|
137
|
+
|
|
138
|
+
// Reset state only when the dropdown closes (not every render). This avoids clearing the input
|
|
139
|
+
// on each keystroke, especially in menuTrigger='manual' mode.
|
|
140
|
+
var wasOpenRef = useRef(false);
|
|
141
|
+
useEffect(() => {
|
|
142
|
+
if (wasOpenRef.current && !overlayState.isOpen) {
|
|
143
|
+
if (!allowsCustomValue) {
|
|
144
|
+
setSearchText('');
|
|
145
|
+
}
|
|
146
|
+
setFocusedKey(null);
|
|
147
|
+
}
|
|
148
|
+
wasOpenRef.current = overlayState.isOpen;
|
|
149
|
+
}, [overlayState.isOpen, setSearchText, allowsCustomValue]);
|
|
150
|
+
|
|
151
|
+
// Open dropdown when user starts typing (unless menuTrigger is 'manual').
|
|
152
|
+
// Intentionally watches only searchText: including overlayState.isOpen would
|
|
153
|
+
// re-fire on close and auto-reopen if the input still has text.
|
|
154
|
+
useEffect(() => {
|
|
155
|
+
if (menuTrigger !== 'manual' && searchText !== '' && !overlayState.isOpen && !isDisabled) {
|
|
156
|
+
openOverlay('input');
|
|
157
|
+
}
|
|
158
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
159
|
+
}, [searchText]);
|
|
160
|
+
|
|
161
|
+
// Spectrum's <ListBox> doesn't expose its own focus management, so we mark the focused option
|
|
162
|
+
// with data-dh-focused for SCSS styling and copy the option's id onto
|
|
163
|
+
// the input's aria-activedescendant.
|
|
164
|
+
useEffect(() => {
|
|
165
|
+
var container = listBoxContainerRef.current;
|
|
166
|
+
if (container == null) {
|
|
167
|
+
var _inputRef$current;
|
|
168
|
+
(_inputRef$current = inputRef.current) === null || _inputRef$current === void 0 || _inputRef$current.removeAttribute('aria-activedescendant');
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
var options = container.querySelectorAll('[role="option"]');
|
|
172
|
+
var focusedSuffix = focusedKey != null ? "-option-".concat(normalizeKey(focusedKey)) : null;
|
|
173
|
+
var focusedOptionId;
|
|
174
|
+
options.forEach(el => {
|
|
175
|
+
var matches = focusedSuffix != null && el.id.endsWith(focusedSuffix);
|
|
176
|
+
if (matches) {
|
|
177
|
+
el.setAttribute('data-dh-focused', 'true');
|
|
178
|
+
el.scrollIntoView({
|
|
179
|
+
block: 'nearest'
|
|
180
|
+
});
|
|
181
|
+
focusedOptionId = el.id;
|
|
182
|
+
} else {
|
|
183
|
+
el.removeAttribute('data-dh-focused');
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
var input = inputRef.current;
|
|
187
|
+
if (input != null) {
|
|
188
|
+
if (focusedOptionId != null) {
|
|
189
|
+
input.setAttribute('aria-activedescendant', focusedOptionId);
|
|
190
|
+
} else {
|
|
191
|
+
input.removeAttribute('aria-activedescendant');
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}, [focusedKey, filteredItems, inputRef, listBoxContainerRef]);
|
|
195
|
+
return {
|
|
196
|
+
handleInputKeyDown
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
export default useMultiSelectKeyboard;
|
|
200
|
+
//# sourceMappingURL=useMultiSelectKeyboard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useMultiSelectKeyboard.js","names":["useCallback","useEffect","useRef","useState","normalizeKey","key","replace","useMultiSelectKeyboard","_ref","filteredItems","allItems","shouldFocusWrap","overlayState","openOverlay","closeOverlay","isReadOnly","isDisabled","searchText","setSearchText","selectedKeys","toggleKey","allowsCustomValue","menuTrigger","onKeyDown","listBoxContainerRef","inputRef","focusedKey","setFocusedKey","moveFocus","dir","len","length","prev","currentIdx","findIndex","i","nextIdx","Math","min","max","handleInputKeyDown","e","preventDefault","isOpen","focusedItemStillExists","some","trim","trimmed","matchingItem","find","item","label","toLowerCase","size","keys","lastKey","wasOpenRef","current","container","_inputRef$current","removeAttribute","options","querySelectorAll","focusedSuffix","concat","focusedOptionId","forEach","el","matches","id","endsWith","setAttribute","scrollIntoView","block","input"],"sources":["../../../src/spectrum/multiSelect/useMultiSelectKeyboard.ts"],"sourcesContent":["import {\n type KeyboardEvent,\n type RefObject,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from 'react';\nimport type { OverlayTriggerState } from '@react-stately/overlays';\nimport type { MenuTriggerAction } from '../comboBox';\nimport { type MultiSelectFlatItem } from './multiSelectUtils';\n\nexport interface UseMultiSelectKeyboardOptions {\n filteredItems: MultiSelectFlatItem[];\n allItems: MultiSelectFlatItem[];\n shouldFocusWrap: boolean;\n overlayState: OverlayTriggerState;\n openOverlay: (reason: MenuTriggerAction) => void;\n closeOverlay: () => void;\n isReadOnly: boolean;\n isDisabled: boolean;\n searchText: string;\n setSearchText: (value: string) => void;\n selectedKeys: Set<string>;\n toggleKey: (key: string) => void;\n allowsCustomValue: boolean;\n menuTrigger: 'focus' | 'input' | 'manual';\n onKeyDown: ((e: KeyboardEvent) => void) | undefined;\n listBoxContainerRef: RefObject<HTMLElement | null>;\n inputRef: RefObject<HTMLInputElement | null>;\n}\n\nexport interface UseMultiSelectKeyboardResult {\n /** `onKeyDown` handler for the inline input. */\n handleInputKeyDown: (e: KeyboardEvent) => void;\n}\n\n/**\n * Replicates the key-normalization Spectrum applies to listbox option ids\n * (see https://github.com/adobe/react-spectrum/blob/9508b15bf8c0e968c56220548207cc57c7e4f57c/packages/react-aria/src/listbox/utils.ts#L31). Whitespace is stripped so that\n * `<listId>-option-<normalizedKey>` matches the actual rendered DOM `id`.\n */\nfunction normalizeKey(key: string): string {\n return key.replace(/\\s+/g, '');\n}\n\n/**\n * Owns virtual-focus tracking, keyboard handling, and the DOM/scroll\n * side-effects for option highlighting in MultiSelect. Keyboard navigation\n * is handled outside Spectrum's ListBox because the input retains real\n * focus while options are visually highlighted via `data-dh-focused`.\n *\n * Focus is tracked by item key, so it survives filtering, virtualization, etc\n * where the underlying filteredItems array shifts independently of the user's\n * intended focus target.\n */\nexport function useMultiSelectKeyboard({\n filteredItems,\n allItems,\n shouldFocusWrap,\n overlayState,\n openOverlay,\n closeOverlay,\n isReadOnly,\n isDisabled,\n searchText,\n setSearchText,\n selectedKeys,\n toggleKey,\n allowsCustomValue,\n menuTrigger,\n onKeyDown,\n listBoxContainerRef,\n inputRef,\n}: UseMultiSelectKeyboardOptions): UseMultiSelectKeyboardResult {\n const [focusedKey, setFocusedKey] = useState<string | null>(null);\n\n const moveFocus = useCallback(\n (dir: 'down' | 'up') => {\n const len = filteredItems.length;\n if (len === 0) {\n return;\n }\n setFocusedKey(prev => {\n const currentIdx =\n prev == null ? -1 : filteredItems.findIndex(i => i.key === prev);\n let nextIdx: number;\n if (dir === 'down') {\n if (currentIdx === -1) {\n nextIdx = 0;\n } else if (shouldFocusWrap) {\n nextIdx = (currentIdx + 1) % len;\n } else {\n nextIdx = Math.min(currentIdx + 1, len - 1);\n }\n } else if (currentIdx === -1) {\n nextIdx = len - 1;\n } else if (shouldFocusWrap) {\n nextIdx = (currentIdx - 1 + len) % len;\n } else {\n nextIdx = Math.max(currentIdx - 1, 0);\n }\n return filteredItems[nextIdx].key;\n });\n },\n [filteredItems, shouldFocusWrap]\n );\n\n const handleInputKeyDown = useCallback(\n (e: KeyboardEvent) => {\n if (isDisabled) {\n return;\n }\n\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault();\n if (!overlayState.isOpen) {\n openOverlay('manual');\n } else {\n moveFocus('down');\n }\n break;\n\n case 'ArrowUp':\n e.preventDefault();\n if (overlayState.isOpen) {\n moveFocus('up');\n }\n break;\n\n case 'Enter': {\n if (!overlayState.isOpen) {\n e.preventDefault();\n openOverlay('manual');\n break;\n }\n const focusedItemStillExists =\n focusedKey != null && filteredItems.some(i => i.key === focusedKey);\n if (!isReadOnly && focusedItemStillExists) {\n e.preventDefault();\n toggleKey(focusedKey);\n } else if (\n !isReadOnly &&\n allowsCustomValue &&\n searchText.trim() !== ''\n ) {\n e.preventDefault();\n const trimmed = searchText.trim();\n // Check if typed text exactly matches an existing item label\n const matchingItem = allItems.find(\n item => item.label.toLowerCase() === trimmed.toLowerCase()\n );\n toggleKey(matchingItem != null ? matchingItem.key : trimmed);\n setSearchText('');\n } else {\n // No focused item, no custom value — clear search and close\n setSearchText('');\n closeOverlay();\n }\n break;\n }\n\n case 'Escape':\n if (overlayState.isOpen) {\n e.preventDefault();\n closeOverlay();\n }\n break;\n\n case 'Tab':\n if (overlayState.isOpen) {\n closeOverlay();\n }\n break;\n\n case 'Backspace':\n if (searchText === '' && !isReadOnly && selectedKeys.size > 0) {\n const keys = [...selectedKeys];\n const lastKey = keys[keys.length - 1];\n if (lastKey != null) {\n toggleKey(lastKey);\n }\n }\n break;\n\n default:\n break;\n }\n\n onKeyDown?.(e);\n },\n [\n isDisabled,\n overlayState,\n openOverlay,\n closeOverlay,\n moveFocus,\n isReadOnly,\n focusedKey,\n filteredItems,\n allItems,\n allowsCustomValue,\n toggleKey,\n searchText,\n setSearchText,\n selectedKeys,\n onKeyDown,\n ]\n );\n\n // Reset state only when the dropdown closes (not every render). This avoids clearing the input\n // on each keystroke, especially in menuTrigger='manual' mode.\n const wasOpenRef = useRef(false);\n useEffect(() => {\n if (wasOpenRef.current && !overlayState.isOpen) {\n if (!allowsCustomValue) {\n setSearchText('');\n }\n setFocusedKey(null);\n }\n wasOpenRef.current = overlayState.isOpen;\n }, [overlayState.isOpen, setSearchText, allowsCustomValue]);\n\n // Open dropdown when user starts typing (unless menuTrigger is 'manual').\n // Intentionally watches only searchText: including overlayState.isOpen would\n // re-fire on close and auto-reopen if the input still has text.\n useEffect(() => {\n if (\n menuTrigger !== 'manual' &&\n searchText !== '' &&\n !overlayState.isOpen &&\n !isDisabled\n ) {\n openOverlay('input');\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [searchText]);\n\n // Spectrum's <ListBox> doesn't expose its own focus management, so we mark the focused option\n // with data-dh-focused for SCSS styling and copy the option's id onto\n // the input's aria-activedescendant.\n useEffect(() => {\n const container = listBoxContainerRef.current;\n if (container == null) {\n inputRef.current?.removeAttribute('aria-activedescendant');\n return;\n }\n const options = container.querySelectorAll<HTMLElement>('[role=\"option\"]');\n const focusedSuffix =\n focusedKey != null ? `-option-${normalizeKey(focusedKey)}` : null;\n let focusedOptionId: string | undefined;\n options.forEach(el => {\n const matches = focusedSuffix != null && el.id.endsWith(focusedSuffix);\n if (matches) {\n el.setAttribute('data-dh-focused', 'true');\n el.scrollIntoView({ block: 'nearest' });\n focusedOptionId = el.id;\n } else {\n el.removeAttribute('data-dh-focused');\n }\n });\n\n const input = inputRef.current;\n if (input != null) {\n if (focusedOptionId != null) {\n input.setAttribute('aria-activedescendant', focusedOptionId);\n } else {\n input.removeAttribute('aria-activedescendant');\n }\n }\n }, [focusedKey, filteredItems, inputRef, listBoxContainerRef]);\n\n return { handleInputKeyDown };\n}\n\nexport default useMultiSelectKeyboard;\n"],"mappings":"AAAA,SAGEA,WAAW,EACXC,SAAS,EACTC,MAAM,EACNC,QAAQ,QACH,OAAO;AA8Bd;AACA;AACA;AACA;AACA;AACA,SAASC,YAAYA,CAACC,GAAW,EAAU;EACzC,OAAOA,GAAG,CAACC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;AAChC;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,sBAAsBA,CAAAC,IAAA,EAkB0B;EAAA,IAlBzB;IACrCC,aAAa;IACbC,QAAQ;IACRC,eAAe;IACfC,YAAY;IACZC,WAAW;IACXC,YAAY;IACZC,UAAU;IACVC,UAAU;IACVC,UAAU;IACVC,aAAa;IACbC,YAAY;IACZC,SAAS;IACTC,iBAAiB;IACjBC,WAAW;IACXC,SAAS;IACTC,mBAAmB;IACnBC;EAC6B,CAAC,GAAAjB,IAAA;EAC9B,IAAM,CAACkB,UAAU,EAAEC,aAAa,CAAC,GAAGxB,QAAQ,CAAgB,IAAI,CAAC;EAEjE,IAAMyB,SAAS,GAAG5B,WAAW,CAC1B6B,GAAkB,IAAK;IACtB,IAAMC,GAAG,GAAGrB,aAAa,CAACsB,MAAM;IAChC,IAAID,GAAG,KAAK,CAAC,EAAE;MACb;IACF;IACAH,aAAa,CAACK,IAAI,IAAI;MACpB,IAAMC,UAAU,GACdD,IAAI,IAAI,IAAI,GAAG,CAAC,CAAC,GAAGvB,aAAa,CAACyB,SAAS,CAACC,CAAC,IAAIA,CAAC,CAAC9B,GAAG,KAAK2B,IAAI,CAAC;MAClE,IAAII,OAAe;MACnB,IAAIP,GAAG,KAAK,MAAM,EAAE;QAClB,IAAII,UAAU,KAAK,CAAC,CAAC,EAAE;UACrBG,OAAO,GAAG,CAAC;QACb,CAAC,MAAM,IAAIzB,eAAe,EAAE;UAC1ByB,OAAO,GAAG,CAACH,UAAU,GAAG,CAAC,IAAIH,GAAG;QAClC,CAAC,MAAM;UACLM,OAAO,GAAGC,IAAI,CAACC,GAAG,CAACL,UAAU,GAAG,CAAC,EAAEH,GAAG,GAAG,CAAC,CAAC;QAC7C;MACF,CAAC,MAAM,IAAIG,UAAU,KAAK,CAAC,CAAC,EAAE;QAC5BG,OAAO,GAAGN,GAAG,GAAG,CAAC;MACnB,CAAC,MAAM,IAAInB,eAAe,EAAE;QAC1ByB,OAAO,GAAG,CAACH,UAAU,GAAG,CAAC,GAAGH,GAAG,IAAIA,GAAG;MACxC,CAAC,MAAM;QACLM,OAAO,GAAGC,IAAI,CAACE,GAAG,CAACN,UAAU,GAAG,CAAC,EAAE,CAAC,CAAC;MACvC;MACA,OAAOxB,aAAa,CAAC2B,OAAO,CAAC,CAAC/B,GAAG;IACnC,CAAC,CAAC;EACJ,CAAC,EACD,CAACI,aAAa,EAAEE,eAAe,CACjC,CAAC;EAED,IAAM6B,kBAAkB,GAAGxC,WAAW,CACnCyC,CAAgB,IAAK;IACpB,IAAIzB,UAAU,EAAE;MACd;IACF;IAEA,QAAQyB,CAAC,CAACpC,GAAG;MACX,KAAK,WAAW;QACdoC,CAAC,CAACC,cAAc,CAAC,CAAC;QAClB,IAAI,CAAC9B,YAAY,CAAC+B,MAAM,EAAE;UACxB9B,WAAW,CAAC,QAAQ,CAAC;QACvB,CAAC,MAAM;UACLe,SAAS,CAAC,MAAM,CAAC;QACnB;QACA;MAEF,KAAK,SAAS;QACZa,CAAC,CAACC,cAAc,CAAC,CAAC;QAClB,IAAI9B,YAAY,CAAC+B,MAAM,EAAE;UACvBf,SAAS,CAAC,IAAI,CAAC;QACjB;QACA;MAEF,KAAK,OAAO;QAAE;UACZ,IAAI,CAAChB,YAAY,CAAC+B,MAAM,EAAE;YACxBF,CAAC,CAACC,cAAc,CAAC,CAAC;YAClB7B,WAAW,CAAC,QAAQ,CAAC;YACrB;UACF;UACA,IAAM+B,sBAAsB,GAC1BlB,UAAU,IAAI,IAAI,IAAIjB,aAAa,CAACoC,IAAI,CAACV,CAAC,IAAIA,CAAC,CAAC9B,GAAG,KAAKqB,UAAU,CAAC;UACrE,IAAI,CAACX,UAAU,IAAI6B,sBAAsB,EAAE;YACzCH,CAAC,CAACC,cAAc,CAAC,CAAC;YAClBtB,SAAS,CAACM,UAAU,CAAC;UACvB,CAAC,MAAM,IACL,CAACX,UAAU,IACXM,iBAAiB,IACjBJ,UAAU,CAAC6B,IAAI,CAAC,CAAC,KAAK,EAAE,EACxB;YACAL,CAAC,CAACC,cAAc,CAAC,CAAC;YAClB,IAAMK,OAAO,GAAG9B,UAAU,CAAC6B,IAAI,CAAC,CAAC;YACjC;YACA,IAAME,YAAY,GAAGtC,QAAQ,CAACuC,IAAI,CAChCC,IAAI,IAAIA,IAAI,CAACC,KAAK,CAACC,WAAW,CAAC,CAAC,KAAKL,OAAO,CAACK,WAAW,CAAC,CAC3D,CAAC;YACDhC,SAAS,CAAC4B,YAAY,IAAI,IAAI,GAAGA,YAAY,CAAC3C,GAAG,GAAG0C,OAAO,CAAC;YAC5D7B,aAAa,CAAC,EAAE,CAAC;UACnB,CAAC,MAAM;YACL;YACAA,aAAa,CAAC,EAAE,CAAC;YACjBJ,YAAY,CAAC,CAAC;UAChB;UACA;QACF;MAEA,KAAK,QAAQ;QACX,IAAIF,YAAY,CAAC+B,MAAM,EAAE;UACvBF,CAAC,CAACC,cAAc,CAAC,CAAC;UAClB5B,YAAY,CAAC,CAAC;QAChB;QACA;MAEF,KAAK,KAAK;QACR,IAAIF,YAAY,CAAC+B,MAAM,EAAE;UACvB7B,YAAY,CAAC,CAAC;QAChB;QACA;MAEF,KAAK,WAAW;QACd,IAAIG,UAAU,KAAK,EAAE,IAAI,CAACF,UAAU,IAAII,YAAY,CAACkC,IAAI,GAAG,CAAC,EAAE;UAC7D,IAAMC,IAAI,GAAG,CAAC,GAAGnC,YAAY,CAAC;UAC9B,IAAMoC,OAAO,GAAGD,IAAI,CAACA,IAAI,CAACvB,MAAM,GAAG,CAAC,CAAC;UACrC,IAAIwB,OAAO,IAAI,IAAI,EAAE;YACnBnC,SAAS,CAACmC,OAAO,CAAC;UACpB;QACF;QACA;MAEF;QACE;IACJ;IAEAhC,SAAS,aAATA,SAAS,eAATA,SAAS,CAAGkB,CAAC,CAAC;EAChB,CAAC,EACD,CACEzB,UAAU,EACVJ,YAAY,EACZC,WAAW,EACXC,YAAY,EACZc,SAAS,EACTb,UAAU,EACVW,UAAU,EACVjB,aAAa,EACbC,QAAQ,EACRW,iBAAiB,EACjBD,SAAS,EACTH,UAAU,EACVC,aAAa,EACbC,YAAY,EACZI,SAAS,CAEb,CAAC;;EAED;EACA;EACA,IAAMiC,UAAU,GAAGtD,MAAM,CAAC,KAAK,CAAC;EAChCD,SAAS,CAAC,MAAM;IACd,IAAIuD,UAAU,CAACC,OAAO,IAAI,CAAC7C,YAAY,CAAC+B,MAAM,EAAE;MAC9C,IAAI,CAACtB,iBAAiB,EAAE;QACtBH,aAAa,CAAC,EAAE,CAAC;MACnB;MACAS,aAAa,CAAC,IAAI,CAAC;IACrB;IACA6B,UAAU,CAACC,OAAO,GAAG7C,YAAY,CAAC+B,MAAM;EAC1C,CAAC,EAAE,CAAC/B,YAAY,CAAC+B,MAAM,EAAEzB,aAAa,EAAEG,iBAAiB,CAAC,CAAC;;EAE3D;EACA;EACA;EACApB,SAAS,CAAC,MAAM;IACd,IACEqB,WAAW,KAAK,QAAQ,IACxBL,UAAU,KAAK,EAAE,IACjB,CAACL,YAAY,CAAC+B,MAAM,IACpB,CAAC3B,UAAU,EACX;MACAH,WAAW,CAAC,OAAO,CAAC;IACtB;IACA;EACF,CAAC,EAAE,CAACI,UAAU,CAAC,CAAC;;EAEhB;EACA;EACA;EACAhB,SAAS,CAAC,MAAM;IACd,IAAMyD,SAAS,GAAGlC,mBAAmB,CAACiC,OAAO;IAC7C,IAAIC,SAAS,IAAI,IAAI,EAAE;MAAA,IAAAC,iBAAA;MACrB,CAAAA,iBAAA,GAAAlC,QAAQ,CAACgC,OAAO,cAAAE,iBAAA,eAAhBA,iBAAA,CAAkBC,eAAe,CAAC,uBAAuB,CAAC;MAC1D;IACF;IACA,IAAMC,OAAO,GAAGH,SAAS,CAACI,gBAAgB,CAAc,iBAAiB,CAAC;IAC1E,IAAMC,aAAa,GACjBrC,UAAU,IAAI,IAAI,cAAAsC,MAAA,CAAc5D,YAAY,CAACsB,UAAU,CAAC,IAAK,IAAI;IACnE,IAAIuC,eAAmC;IACvCJ,OAAO,CAACK,OAAO,CAACC,EAAE,IAAI;MACpB,IAAMC,OAAO,GAAGL,aAAa,IAAI,IAAI,IAAII,EAAE,CAACE,EAAE,CAACC,QAAQ,CAACP,aAAa,CAAC;MACtE,IAAIK,OAAO,EAAE;QACXD,EAAE,CAACI,YAAY,CAAC,iBAAiB,EAAE,MAAM,CAAC;QAC1CJ,EAAE,CAACK,cAAc,CAAC;UAAEC,KAAK,EAAE;QAAU,CAAC,CAAC;QACvCR,eAAe,GAAGE,EAAE,CAACE,EAAE;MACzB,CAAC,MAAM;QACLF,EAAE,CAACP,eAAe,CAAC,iBAAiB,CAAC;MACvC;IACF,CAAC,CAAC;IAEF,IAAMc,KAAK,GAAGjD,QAAQ,CAACgC,OAAO;IAC9B,IAAIiB,KAAK,IAAI,IAAI,EAAE;MACjB,IAAIT,eAAe,IAAI,IAAI,EAAE;QAC3BS,KAAK,CAACH,YAAY,CAAC,uBAAuB,EAAEN,eAAe,CAAC;MAC9D,CAAC,MAAM;QACLS,KAAK,CAACd,eAAe,CAAC,uBAAuB,CAAC;MAChD;IACF;EACF,CAAC,EAAE,CAAClC,UAAU,EAAEjB,aAAa,EAAEgB,QAAQ,EAAED,mBAAmB,CAAC,CAAC;EAE9D,OAAO;IAAEgB;EAAmB,CAAC;AAC/B;AAEA,eAAejC,sBAAsB","ignoreList":[]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type LoadingState } from '@react-types/shared';
|
|
2
|
+
import { type MenuTriggerAction } from '../comboBox/ComboBox';
|
|
3
|
+
export interface UseMultiSelectLoadingSpinnerOptions {
|
|
4
|
+
loadingState: LoadingState | undefined;
|
|
5
|
+
searchText: string;
|
|
6
|
+
isOpen: boolean;
|
|
7
|
+
menuTrigger: MenuTriggerAction;
|
|
8
|
+
}
|
|
9
|
+
export declare function useMultiSelectLoadingSpinner({ loadingState, searchText, isOpen, menuTrigger, }: UseMultiSelectLoadingSpinnerOptions): boolean;
|
|
10
|
+
export default useMultiSelectLoadingSpinner;
|
|
11
|
+
//# sourceMappingURL=useMultiSelectLoadingSpinner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useMultiSelectLoadingSpinner.d.ts","sourceRoot":"","sources":["../../../src/spectrum/multiSelect/useMultiSelectLoadingSpinner.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAI9D,MAAM,WAAW,mCAAmC;IAClD,YAAY,EAAE,YAAY,GAAG,SAAS,CAAC;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,iBAAiB,CAAC;CAChC;AAED,wBAAgB,4BAA4B,CAAC,EAC3C,YAAY,EACZ,UAAU,EACV,MAAM,EACN,WAAW,GACZ,EAAE,mCAAmC,GAAG,OAAO,CA2C/C;AAED,eAAe,4BAA4B,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from 'react';
|
|
2
|
+
var LOADING_DEBOUNCE_MS = 500;
|
|
3
|
+
export function useMultiSelectLoadingSpinner(_ref) {
|
|
4
|
+
var {
|
|
5
|
+
loadingState,
|
|
6
|
+
searchText,
|
|
7
|
+
isOpen,
|
|
8
|
+
menuTrigger
|
|
9
|
+
} = _ref;
|
|
10
|
+
var [showLoading, setShowLoading] = useState(false);
|
|
11
|
+
var loadingTimeoutRef = useRef(null);
|
|
12
|
+
var isLoadingForSpinner = loadingState === 'loading' || loadingState === 'filtering';
|
|
13
|
+
var lastSearchTextRef = useRef(searchText);
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
if (isLoadingForSpinner && !showLoading) {
|
|
16
|
+
var searchChanged = searchText !== lastSearchTextRef.current;
|
|
17
|
+
if (loadingTimeoutRef.current !== null && searchChanged) {
|
|
18
|
+
clearTimeout(loadingTimeoutRef.current);
|
|
19
|
+
loadingTimeoutRef.current = null;
|
|
20
|
+
}
|
|
21
|
+
if (loadingTimeoutRef.current === null) {
|
|
22
|
+
loadingTimeoutRef.current = setTimeout(() => {
|
|
23
|
+
setShowLoading(true);
|
|
24
|
+
}, LOADING_DEBOUNCE_MS);
|
|
25
|
+
}
|
|
26
|
+
} else if (!isLoadingForSpinner) {
|
|
27
|
+
setShowLoading(false);
|
|
28
|
+
if (loadingTimeoutRef.current != null) {
|
|
29
|
+
clearTimeout(loadingTimeoutRef.current);
|
|
30
|
+
loadingTimeoutRef.current = null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
lastSearchTextRef.current = searchText;
|
|
34
|
+
}, [isLoadingForSpinner, showLoading, searchText]);
|
|
35
|
+
useEffect(() => () => {
|
|
36
|
+
if (loadingTimeoutRef.current != null) {
|
|
37
|
+
clearTimeout(loadingTimeoutRef.current);
|
|
38
|
+
loadingTimeoutRef.current = null;
|
|
39
|
+
}
|
|
40
|
+
}, []);
|
|
41
|
+
return showLoading && (isOpen || menuTrigger === 'manual' || loadingState === 'loading');
|
|
42
|
+
}
|
|
43
|
+
export default useMultiSelectLoadingSpinner;
|
|
44
|
+
//# sourceMappingURL=useMultiSelectLoadingSpinner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useMultiSelectLoadingSpinner.js","names":["useEffect","useRef","useState","LOADING_DEBOUNCE_MS","useMultiSelectLoadingSpinner","_ref","loadingState","searchText","isOpen","menuTrigger","showLoading","setShowLoading","loadingTimeoutRef","isLoadingForSpinner","lastSearchTextRef","searchChanged","current","clearTimeout","setTimeout"],"sources":["../../../src/spectrum/multiSelect/useMultiSelectLoadingSpinner.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react';\nimport { type LoadingState } from '@react-types/shared';\nimport { type MenuTriggerAction } from '../comboBox/ComboBox';\n\nconst LOADING_DEBOUNCE_MS = 500;\n\nexport interface UseMultiSelectLoadingSpinnerOptions {\n loadingState: LoadingState | undefined;\n searchText: string;\n isOpen: boolean;\n menuTrigger: MenuTriggerAction;\n}\n\nexport function useMultiSelectLoadingSpinner({\n loadingState,\n searchText,\n isOpen,\n menuTrigger,\n}: UseMultiSelectLoadingSpinnerOptions): boolean {\n const [showLoading, setShowLoading] = useState(false);\n const loadingTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const isLoadingForSpinner =\n loadingState === 'loading' || loadingState === 'filtering';\n const lastSearchTextRef = useRef(searchText);\n\n useEffect(() => {\n if (isLoadingForSpinner && !showLoading) {\n const searchChanged = searchText !== lastSearchTextRef.current;\n if (loadingTimeoutRef.current !== null && searchChanged) {\n clearTimeout(loadingTimeoutRef.current);\n loadingTimeoutRef.current = null;\n }\n if (loadingTimeoutRef.current === null) {\n loadingTimeoutRef.current = setTimeout(() => {\n setShowLoading(true);\n }, LOADING_DEBOUNCE_MS);\n }\n } else if (!isLoadingForSpinner) {\n setShowLoading(false);\n if (loadingTimeoutRef.current != null) {\n clearTimeout(loadingTimeoutRef.current);\n loadingTimeoutRef.current = null;\n }\n }\n lastSearchTextRef.current = searchText;\n }, [isLoadingForSpinner, showLoading, searchText]);\n\n useEffect(\n () => () => {\n if (loadingTimeoutRef.current != null) {\n clearTimeout(loadingTimeoutRef.current);\n loadingTimeoutRef.current = null;\n }\n },\n []\n );\n\n return (\n showLoading &&\n (isOpen || menuTrigger === 'manual' || loadingState === 'loading')\n );\n}\n\nexport default useMultiSelectLoadingSpinner;\n"],"mappings":"AAAA,SAASA,SAAS,EAAEC,MAAM,EAAEC,QAAQ,QAAQ,OAAO;AAInD,IAAMC,mBAAmB,GAAG,GAAG;AAS/B,OAAO,SAASC,4BAA4BA,CAAAC,IAAA,EAKK;EAAA,IALJ;IAC3CC,YAAY;IACZC,UAAU;IACVC,MAAM;IACNC;EACmC,CAAC,GAAAJ,IAAA;EACpC,IAAM,CAACK,WAAW,EAAEC,cAAc,CAAC,GAAGT,QAAQ,CAAC,KAAK,CAAC;EACrD,IAAMU,iBAAiB,GAAGX,MAAM,CAAuC,IAAI,CAAC;EAC5E,IAAMY,mBAAmB,GACvBP,YAAY,KAAK,SAAS,IAAIA,YAAY,KAAK,WAAW;EAC5D,IAAMQ,iBAAiB,GAAGb,MAAM,CAACM,UAAU,CAAC;EAE5CP,SAAS,CAAC,MAAM;IACd,IAAIa,mBAAmB,IAAI,CAACH,WAAW,EAAE;MACvC,IAAMK,aAAa,GAAGR,UAAU,KAAKO,iBAAiB,CAACE,OAAO;MAC9D,IAAIJ,iBAAiB,CAACI,OAAO,KAAK,IAAI,IAAID,aAAa,EAAE;QACvDE,YAAY,CAACL,iBAAiB,CAACI,OAAO,CAAC;QACvCJ,iBAAiB,CAACI,OAAO,GAAG,IAAI;MAClC;MACA,IAAIJ,iBAAiB,CAACI,OAAO,KAAK,IAAI,EAAE;QACtCJ,iBAAiB,CAACI,OAAO,GAAGE,UAAU,CAAC,MAAM;UAC3CP,cAAc,CAAC,IAAI,CAAC;QACtB,CAAC,EAAER,mBAAmB,CAAC;MACzB;IACF,CAAC,MAAM,IAAI,CAACU,mBAAmB,EAAE;MAC/BF,cAAc,CAAC,KAAK,CAAC;MACrB,IAAIC,iBAAiB,CAACI,OAAO,IAAI,IAAI,EAAE;QACrCC,YAAY,CAACL,iBAAiB,CAACI,OAAO,CAAC;QACvCJ,iBAAiB,CAACI,OAAO,GAAG,IAAI;MAClC;IACF;IACAF,iBAAiB,CAACE,OAAO,GAAGT,UAAU;EACxC,CAAC,EAAE,CAACM,mBAAmB,EAAEH,WAAW,EAAEH,UAAU,CAAC,CAAC;EAElDP,SAAS,CACP,MAAM,MAAM;IACV,IAAIY,iBAAiB,CAACI,OAAO,IAAI,IAAI,EAAE;MACrCC,YAAY,CAACL,iBAAiB,CAACI,OAAO,CAAC;MACvCJ,iBAAiB,CAACI,OAAO,GAAG,IAAI;IAClC;EACF,CAAC,EACD,EACF,CAAC;EAED,OACEN,WAAW,KACVF,MAAM,IAAIC,WAAW,KAAK,QAAQ,IAAIH,YAAY,KAAK,SAAS,CAAC;AAEtE;AAEA,eAAeF,4BAA4B","ignoreList":[]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type MultiSelectNormalizedProps, type MultiSelectProps } from './MultiSelectProps';
|
|
2
|
+
/** Props that are derived by `useMultiSelectNormalizedProps` */
|
|
3
|
+
export type UseMultiSelectNormalizedDerivedProps = {
|
|
4
|
+
children: JSX.Element[];
|
|
5
|
+
forceRerenderKey: string;
|
|
6
|
+
selectedKeys: MultiSelectProps['selectedKeys'];
|
|
7
|
+
defaultSelectedKeys: MultiSelectProps['defaultSelectedKeys'];
|
|
8
|
+
disabledKeys: MultiSelectProps['disabledKeys'];
|
|
9
|
+
onChange: MultiSelectProps['onChange'];
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Props that are passed through untouched. Should exclude all of the
|
|
13
|
+
* destructured props passed into `useMultiSelectNormalizedProps` that are not
|
|
14
|
+
* in the spread `...props`.
|
|
15
|
+
*/
|
|
16
|
+
export type UseMultiSelectNormalizedPassthroughProps = Omit<MultiSelectNormalizedProps, 'normalizedItems' | 'showItemIcons' | 'tooltip' | 'selectedKeys' | 'defaultSelectedKeys' | 'disabledKeys' | 'onChange' | 'onSelectionChange'>;
|
|
17
|
+
/** Props returned from `useMultiSelectNormalizedProps` hook. */
|
|
18
|
+
export type UseMultiSelectNormalizedResult = UseMultiSelectNormalizedDerivedProps & UseMultiSelectNormalizedPassthroughProps;
|
|
19
|
+
export declare function useMultiSelectNormalizedProps({ normalizedItems, showItemIcons, tooltip, selectedKeys, defaultSelectedKeys, disabledKeys, onChange, onSelectionChange, ...props }: MultiSelectNormalizedProps): UseMultiSelectNormalizedResult;
|
|
20
|
+
export default useMultiSelectNormalizedProps;
|
|
21
|
+
//# sourceMappingURL=useMultiSelectNormalizedProps.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useMultiSelectNormalizedProps.d.ts","sourceRoot":"","sources":["../../../src/spectrum/multiSelect/useMultiSelectNormalizedProps.tsx"],"names":[],"mappings":"AASA,OAAO,EACL,KAAK,0BAA0B,EAC/B,KAAK,gBAAgB,EACtB,MAAM,oBAAoB,CAAC;AAE5B,gEAAgE;AAChE,MAAM,MAAM,oCAAoC,GAAG;IACjD,QAAQ,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,gBAAgB,CAAC,cAAc,CAAC,CAAC;IAC/C,mBAAmB,EAAE,gBAAgB,CAAC,qBAAqB,CAAC,CAAC;IAC7D,YAAY,EAAE,gBAAgB,CAAC,cAAc,CAAC,CAAC;IAC/C,QAAQ,EAAE,gBAAgB,CAAC,UAAU,CAAC,CAAC;CACxC,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,wCAAwC,GAAG,IAAI,CACzD,0BAA0B,EACxB,iBAAiB,GACjB,eAAe,GACf,SAAS,GACT,cAAc,GACd,qBAAqB,GACrB,cAAc,GACd,UAAU,GACV,mBAAmB,CACtB,CAAC;AAEF,gEAAgE;AAChE,MAAM,MAAM,8BAA8B,GACxC,oCAAoC,GAClC,wCAAwC,CAAC;AAE7C,wBAAgB,6BAA6B,CAAC,EAC5C,eAAe,EACf,aAAa,EACb,OAAc,EACd,YAAY,EACZ,mBAAmB,EACnB,YAAY,EACZ,QAAQ,EACR,iBAAiB,EACjB,GAAG,KAAK,EACT,EAAE,0BAA0B,GAAG,8BAA8B,CAqE7D;AAED,eAAe,6BAA6B,CAAC"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
var _excluded = ["normalizedItems", "showItemIcons", "tooltip", "selectedKeys", "defaultSelectedKeys", "disabledKeys", "onChange", "onSelectionChange"];
|
|
2
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
3
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
4
|
+
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
|
|
5
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
|
|
6
|
+
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
7
|
+
function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }
|
|
8
|
+
function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
|
|
9
|
+
import { useMemo } from 'react';
|
|
10
|
+
import { Section } from '@adobe/react-spectrum';
|
|
11
|
+
import { getItemKey, isNormalizedSection, normalizeTooltipOptions, useRenderNormalizedItem, useStringifiedMultiSelection } from "../utils/index.js";
|
|
12
|
+
/** Props that are derived by `useMultiSelectNormalizedProps` */
|
|
13
|
+
/**
|
|
14
|
+
* Props that are passed through untouched. Should exclude all of the
|
|
15
|
+
* destructured props passed into `useMultiSelectNormalizedProps` that are not
|
|
16
|
+
* in the spread `...props`.
|
|
17
|
+
*/
|
|
18
|
+
/** Props returned from `useMultiSelectNormalizedProps` hook. */
|
|
19
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
20
|
+
export function useMultiSelectNormalizedProps(_ref) {
|
|
21
|
+
var {
|
|
22
|
+
normalizedItems,
|
|
23
|
+
showItemIcons,
|
|
24
|
+
tooltip = true,
|
|
25
|
+
selectedKeys,
|
|
26
|
+
defaultSelectedKeys,
|
|
27
|
+
disabledKeys,
|
|
28
|
+
onChange,
|
|
29
|
+
onSelectionChange
|
|
30
|
+
} = _ref,
|
|
31
|
+
props = _objectWithoutProperties(_ref, _excluded);
|
|
32
|
+
var tooltipOptions = useMemo(() => normalizeTooltipOptions(tooltip), [tooltip]);
|
|
33
|
+
var renderNormalizedItem = useRenderNormalizedItem({
|
|
34
|
+
itemIconSlot: 'icon',
|
|
35
|
+
showItemDescriptions: false,
|
|
36
|
+
showItemIcons,
|
|
37
|
+
tooltipOptions
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Spectrum doesn't re-render if only the render function identity changes,
|
|
41
|
+
// so we expose a key that the parent can use to force a re-render.
|
|
42
|
+
var forceRerenderKey = "".concat(showItemIcons, "-").concat(tooltipOptions === null || tooltipOptions === void 0 ? void 0 : tooltipOptions.placement);
|
|
43
|
+
|
|
44
|
+
// Stringification operates on the flat item list so selection works for
|
|
45
|
+
// items inside sections too.
|
|
46
|
+
var flatItems = useMemo(() => normalizedItems.flatMap(item => {
|
|
47
|
+
var _item$item$items, _item$item;
|
|
48
|
+
return isNormalizedSection(item) ? (_item$item$items = (_item$item = item.item) === null || _item$item === void 0 ? void 0 : _item$item.items) !== null && _item$item$items !== void 0 ? _item$item$items : [] : [item];
|
|
49
|
+
}), [normalizedItems]);
|
|
50
|
+
var {
|
|
51
|
+
selectedStringKeys,
|
|
52
|
+
defaultSelectedStringKeys,
|
|
53
|
+
disabledStringKeys,
|
|
54
|
+
onStringSelectionChange
|
|
55
|
+
} = useStringifiedMultiSelection({
|
|
56
|
+
normalizedItems: flatItems,
|
|
57
|
+
selectedKeys,
|
|
58
|
+
defaultSelectedKeys,
|
|
59
|
+
disabledKeys,
|
|
60
|
+
onChange: onChange !== null && onChange !== void 0 ? onChange : onSelectionChange
|
|
61
|
+
});
|
|
62
|
+
var children = useMemo(() => normalizedItems.map(itemOrSection => {
|
|
63
|
+
if (isNormalizedSection(itemOrSection)) {
|
|
64
|
+
var _itemOrSection$item, _itemOrSection$item2;
|
|
65
|
+
return /*#__PURE__*/_jsx(Section, {
|
|
66
|
+
title: (_itemOrSection$item = itemOrSection.item) === null || _itemOrSection$item === void 0 ? void 0 : _itemOrSection$item.title,
|
|
67
|
+
items: (_itemOrSection$item2 = itemOrSection.item) === null || _itemOrSection$item2 === void 0 ? void 0 : _itemOrSection$item2.items,
|
|
68
|
+
children: renderNormalizedItem
|
|
69
|
+
}, getItemKey(itemOrSection));
|
|
70
|
+
}
|
|
71
|
+
return renderNormalizedItem(itemOrSection);
|
|
72
|
+
}), [normalizedItems, renderNormalizedItem]);
|
|
73
|
+
return _objectSpread(_objectSpread({}, props), {}, {
|
|
74
|
+
children,
|
|
75
|
+
forceRerenderKey,
|
|
76
|
+
selectedKeys: selectedStringKeys,
|
|
77
|
+
defaultSelectedKeys: defaultSelectedStringKeys,
|
|
78
|
+
disabledKeys: disabledStringKeys,
|
|
79
|
+
onChange: onStringSelectionChange
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
export default useMultiSelectNormalizedProps;
|
|
83
|
+
//# sourceMappingURL=useMultiSelectNormalizedProps.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useMultiSelectNormalizedProps.js","names":["useMemo","Section","getItemKey","isNormalizedSection","normalizeTooltipOptions","useRenderNormalizedItem","useStringifiedMultiSelection","jsx","_jsx","useMultiSelectNormalizedProps","_ref","normalizedItems","showItemIcons","tooltip","selectedKeys","defaultSelectedKeys","disabledKeys","onChange","onSelectionChange","props","_objectWithoutProperties","_excluded","tooltipOptions","renderNormalizedItem","itemIconSlot","showItemDescriptions","forceRerenderKey","concat","placement","flatItems","flatMap","item","_item$item$items","_item$item","items","selectedStringKeys","defaultSelectedStringKeys","disabledStringKeys","onStringSelectionChange","children","map","itemOrSection","_itemOrSection$item","_itemOrSection$item2","title","_objectSpread"],"sources":["../../../src/spectrum/multiSelect/useMultiSelectNormalizedProps.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport { Section } from '@adobe/react-spectrum';\nimport {\n getItemKey,\n isNormalizedSection,\n normalizeTooltipOptions,\n useRenderNormalizedItem,\n useStringifiedMultiSelection,\n} from '../utils';\nimport {\n type MultiSelectNormalizedProps,\n type MultiSelectProps,\n} from './MultiSelectProps';\n\n/** Props that are derived by `useMultiSelectNormalizedProps` */\nexport type UseMultiSelectNormalizedDerivedProps = {\n children: JSX.Element[];\n forceRerenderKey: string;\n selectedKeys: MultiSelectProps['selectedKeys'];\n defaultSelectedKeys: MultiSelectProps['defaultSelectedKeys'];\n disabledKeys: MultiSelectProps['disabledKeys'];\n onChange: MultiSelectProps['onChange'];\n};\n\n/**\n * Props that are passed through untouched. Should exclude all of the\n * destructured props passed into `useMultiSelectNormalizedProps` that are not\n * in the spread `...props`.\n */\nexport type UseMultiSelectNormalizedPassthroughProps = Omit<\n MultiSelectNormalizedProps,\n | 'normalizedItems'\n | 'showItemIcons'\n | 'tooltip'\n | 'selectedKeys'\n | 'defaultSelectedKeys'\n | 'disabledKeys'\n | 'onChange'\n | 'onSelectionChange'\n>;\n\n/** Props returned from `useMultiSelectNormalizedProps` hook. */\nexport type UseMultiSelectNormalizedResult =\n UseMultiSelectNormalizedDerivedProps &\n UseMultiSelectNormalizedPassthroughProps;\n\nexport function useMultiSelectNormalizedProps({\n normalizedItems,\n showItemIcons,\n tooltip = true,\n selectedKeys,\n defaultSelectedKeys,\n disabledKeys,\n onChange,\n onSelectionChange,\n ...props\n}: MultiSelectNormalizedProps): UseMultiSelectNormalizedResult {\n const tooltipOptions = useMemo(\n () => normalizeTooltipOptions(tooltip),\n [tooltip]\n );\n\n const renderNormalizedItem = useRenderNormalizedItem({\n itemIconSlot: 'icon',\n showItemDescriptions: false,\n showItemIcons,\n tooltipOptions,\n });\n\n // Spectrum doesn't re-render if only the render function identity changes,\n // so we expose a key that the parent can use to force a re-render.\n const forceRerenderKey = `${showItemIcons}-${tooltipOptions?.placement}`;\n\n // Stringification operates on the flat item list so selection works for\n // items inside sections too.\n const flatItems = useMemo(\n () =>\n normalizedItems.flatMap(item =>\n isNormalizedSection(item) ? item.item?.items ?? [] : [item]\n ),\n [normalizedItems]\n );\n\n const {\n selectedStringKeys,\n defaultSelectedStringKeys,\n disabledStringKeys,\n onStringSelectionChange,\n } = useStringifiedMultiSelection({\n normalizedItems: flatItems,\n selectedKeys,\n defaultSelectedKeys,\n disabledKeys,\n onChange: onChange ?? onSelectionChange,\n });\n\n const children = useMemo(\n () =>\n normalizedItems.map(itemOrSection => {\n if (isNormalizedSection(itemOrSection)) {\n return (\n <Section\n key={getItemKey(itemOrSection)}\n title={itemOrSection.item?.title}\n items={itemOrSection.item?.items}\n >\n {renderNormalizedItem}\n </Section>\n );\n }\n return renderNormalizedItem(itemOrSection);\n }),\n [normalizedItems, renderNormalizedItem]\n );\n\n return {\n ...props,\n children,\n forceRerenderKey,\n selectedKeys: selectedStringKeys as MultiSelectProps['selectedKeys'],\n defaultSelectedKeys:\n defaultSelectedStringKeys as MultiSelectProps['defaultSelectedKeys'],\n disabledKeys: disabledStringKeys as MultiSelectProps['disabledKeys'],\n onChange: onStringSelectionChange as MultiSelectProps['onChange'],\n };\n}\n\nexport default useMultiSelectNormalizedProps;\n"],"mappings":";;;;;;;;AAAA,SAASA,OAAO,QAAQ,OAAO;AAC/B,SAASC,OAAO,QAAQ,uBAAuB;AAAC,SAE9CC,UAAU,EACVC,mBAAmB,EACnBC,uBAAuB,EACvBC,uBAAuB,EACvBC,4BAA4B;AAO9B;AAUA;AACA;AACA;AACA;AACA;AAaA;AAAA,SAAAC,GAAA,IAAAC,IAAA;AAKA,OAAO,SAASC,6BAA6BA,CAAAC,IAAA,EAUkB;EAAA,IAVjB;MAC5CC,eAAe;MACfC,aAAa;MACbC,OAAO,GAAG,IAAI;MACdC,YAAY;MACZC,mBAAmB;MACnBC,YAAY;MACZC,QAAQ;MACRC;IAE0B,CAAC,GAAAR,IAAA;IADxBS,KAAK,GAAAC,wBAAA,CAAAV,IAAA,EAAAW,SAAA;EAER,IAAMC,cAAc,GAAGtB,OAAO,CAC5B,MAAMI,uBAAuB,CAACS,OAAO,CAAC,EACtC,CAACA,OAAO,CACV,CAAC;EAED,IAAMU,oBAAoB,GAAGlB,uBAAuB,CAAC;IACnDmB,YAAY,EAAE,MAAM;IACpBC,oBAAoB,EAAE,KAAK;IAC3Bb,aAAa;IACbU;EACF,CAAC,CAAC;;EAEF;EACA;EACA,IAAMI,gBAAgB,MAAAC,MAAA,CAAMf,aAAa,OAAAe,MAAA,CAAIL,cAAc,aAAdA,cAAc,uBAAdA,cAAc,CAAEM,SAAS,CAAE;;EAExE;EACA;EACA,IAAMC,SAAS,GAAG7B,OAAO,CACvB,MACEW,eAAe,CAACmB,OAAO,CAACC,IAAI;IAAA,IAAAC,gBAAA,EAAAC,UAAA;IAAA,OAC1B9B,mBAAmB,CAAC4B,IAAI,CAAC,IAAAC,gBAAA,IAAAC,UAAA,GAAGF,IAAI,CAACA,IAAI,cAAAE,UAAA,uBAATA,UAAA,CAAWC,KAAK,cAAAF,gBAAA,cAAAA,gBAAA,GAAI,EAAE,GAAG,CAACD,IAAI,CAAC;EAAA,CAC7D,CAAC,EACH,CAACpB,eAAe,CAClB,CAAC;EAED,IAAM;IACJwB,kBAAkB;IAClBC,yBAAyB;IACzBC,kBAAkB;IAClBC;EACF,CAAC,GAAGhC,4BAA4B,CAAC;IAC/BK,eAAe,EAAEkB,SAAS;IAC1Bf,YAAY;IACZC,mBAAmB;IACnBC,YAAY;IACZC,QAAQ,EAAEA,QAAQ,aAARA,QAAQ,cAARA,QAAQ,GAAIC;EACxB,CAAC,CAAC;EAEF,IAAMqB,QAAQ,GAAGvC,OAAO,CACtB,MACEW,eAAe,CAAC6B,GAAG,CAACC,aAAa,IAAI;IACnC,IAAItC,mBAAmB,CAACsC,aAAa,CAAC,EAAE;MAAA,IAAAC,mBAAA,EAAAC,oBAAA;MACtC,oBACEnC,IAAA,CAACP,OAAO;QAEN2C,KAAK,GAAAF,mBAAA,GAAED,aAAa,CAACV,IAAI,cAAAW,mBAAA,uBAAlBA,mBAAA,CAAoBE,KAAM;QACjCV,KAAK,GAAAS,oBAAA,GAAEF,aAAa,CAACV,IAAI,cAAAY,oBAAA,uBAAlBA,oBAAA,CAAoBT,KAAM;QAAAK,QAAA,EAEhChB;MAAoB,GAJhBrB,UAAU,CAACuC,aAAa,CAKtB,CAAC;IAEd;IACA,OAAOlB,oBAAoB,CAACkB,aAAa,CAAC;EAC5C,CAAC,CAAC,EACJ,CAAC9B,eAAe,EAAEY,oBAAoB,CACxC,CAAC;EAED,OAAAsB,aAAA,CAAAA,aAAA,KACK1B,KAAK;IACRoB,QAAQ;IACRb,gBAAgB;IAChBZ,YAAY,EAAEqB,kBAAsD;IACpEpB,mBAAmB,EACjBqB,yBAAoE;IACtEpB,YAAY,EAAEqB,kBAAsD;IACpEpB,QAAQ,EAAEqB;EAAuD;AAErE;AAEA,eAAe7B,6BAA6B","ignoreList":[]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type RefObject } from 'react';
|
|
2
|
+
export interface UseMultiSelectScrollListenerOptions {
|
|
3
|
+
/** Ref to the DOM container that wraps the popover's `<ListBox>`. */
|
|
4
|
+
containerRef: RefObject<HTMLElement | null>;
|
|
5
|
+
/** Whether the popover is currently open. */
|
|
6
|
+
isOpen: boolean;
|
|
7
|
+
/** Scroll event listener attached to the inner scroll area. */
|
|
8
|
+
onScroll: (event: Event) => void;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Resolves the scrollable element inside the popover (the listbox, or the
|
|
12
|
+
* container if the listbox isn't present) and attaches a scroll listener
|
|
13
|
+
* to it. The listener is detached when the popover closes.
|
|
14
|
+
*/
|
|
15
|
+
export declare function useMultiSelectScrollListener({ containerRef, isOpen, onScroll, }: UseMultiSelectScrollListenerOptions): void;
|
|
16
|
+
export default useMultiSelectScrollListener;
|
|
17
|
+
//# sourceMappingURL=useMultiSelectScrollListener.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useMultiSelectScrollListener.d.ts","sourceRoot":"","sources":["../../../src/spectrum/multiSelect/useMultiSelectScrollListener.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAA+B,MAAM,OAAO,CAAC;AAEpE,MAAM,WAAW,mCAAmC;IAClD,qEAAqE;IACrE,YAAY,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAC5C,6CAA6C;IAC7C,MAAM,EAAE,OAAO,CAAC;IAChB,+DAA+D;IAC/D,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAED;;;;GAIG;AACH,wBAAgB,4BAA4B,CAAC,EAC3C,YAAY,EACZ,MAAM,EACN,QAAQ,GACT,EAAE,mCAAmC,GAAG,IAAI,CA8C5C;AAED,eAAe,4BAA4B,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Resolves the scrollable element inside the popover (the listbox, or the
|
|
4
|
+
* container if the listbox isn't present) and attaches a scroll listener
|
|
5
|
+
* to it. The listener is detached when the popover closes.
|
|
6
|
+
*/
|
|
7
|
+
export function useMultiSelectScrollListener(_ref) {
|
|
8
|
+
var {
|
|
9
|
+
containerRef,
|
|
10
|
+
isOpen,
|
|
11
|
+
onScroll
|
|
12
|
+
} = _ref;
|
|
13
|
+
var [scrollAreaEl, setScrollAreaEl] = useState(null);
|
|
14
|
+
|
|
15
|
+
// Mirror onScroll into a ref so the listener is attached once per
|
|
16
|
+
// scrollAreaEl lifetime, regardless of caller memoization.
|
|
17
|
+
var onScrollRef = useRef(onScroll);
|
|
18
|
+
onScrollRef.current = onScroll;
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
if (!isOpen) {
|
|
21
|
+
setScrollAreaEl(null);
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// The ListBox mounts asynchronously inside the popover, so defer one
|
|
26
|
+
// animation frame to give Spectrum time to attach.
|
|
27
|
+
var handle = window.requestAnimationFrame(() => {
|
|
28
|
+
var container = containerRef.current;
|
|
29
|
+
if (container == null) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
var listBox = container.querySelector('[role="listbox"]');
|
|
33
|
+
setScrollAreaEl(listBox !== null && listBox !== void 0 ? listBox : container);
|
|
34
|
+
});
|
|
35
|
+
return () => {
|
|
36
|
+
window.cancelAnimationFrame(handle);
|
|
37
|
+
};
|
|
38
|
+
}, [isOpen, containerRef]);
|
|
39
|
+
|
|
40
|
+
// Attach scroll listener when the scroll area becomes available.
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (scrollAreaEl == null) {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
var handler = event => {
|
|
46
|
+
onScrollRef.current(event);
|
|
47
|
+
};
|
|
48
|
+
scrollAreaEl.addEventListener('scroll', handler);
|
|
49
|
+
return () => {
|
|
50
|
+
scrollAreaEl.removeEventListener('scroll', handler);
|
|
51
|
+
};
|
|
52
|
+
}, [scrollAreaEl]);
|
|
53
|
+
}
|
|
54
|
+
export default useMultiSelectScrollListener;
|
|
55
|
+
//# sourceMappingURL=useMultiSelectScrollListener.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useMultiSelectScrollListener.js","names":["useEffect","useRef","useState","useMultiSelectScrollListener","_ref","containerRef","isOpen","onScroll","scrollAreaEl","setScrollAreaEl","onScrollRef","current","undefined","handle","window","requestAnimationFrame","container","listBox","querySelector","cancelAnimationFrame","handler","event","addEventListener","removeEventListener"],"sources":["../../../src/spectrum/multiSelect/useMultiSelectScrollListener.ts"],"sourcesContent":["import { type RefObject, useEffect, useRef, useState } from 'react';\n\nexport interface UseMultiSelectScrollListenerOptions {\n /** Ref to the DOM container that wraps the popover's `<ListBox>`. */\n containerRef: RefObject<HTMLElement | null>;\n /** Whether the popover is currently open. */\n isOpen: boolean;\n /** Scroll event listener attached to the inner scroll area. */\n onScroll: (event: Event) => void;\n}\n\n/**\n * Resolves the scrollable element inside the popover (the listbox, or the\n * container if the listbox isn't present) and attaches a scroll listener\n * to it. The listener is detached when the popover closes.\n */\nexport function useMultiSelectScrollListener({\n containerRef,\n isOpen,\n onScroll,\n}: UseMultiSelectScrollListenerOptions): void {\n const [scrollAreaEl, setScrollAreaEl] = useState<HTMLElement | null>(null);\n\n // Mirror onScroll into a ref so the listener is attached once per\n // scrollAreaEl lifetime, regardless of caller memoization.\n const onScrollRef = useRef(onScroll);\n onScrollRef.current = onScroll;\n\n useEffect(() => {\n if (!isOpen) {\n setScrollAreaEl(null);\n return undefined;\n }\n\n // The ListBox mounts asynchronously inside the popover, so defer one\n // animation frame to give Spectrum time to attach.\n const handle = window.requestAnimationFrame(() => {\n const container = containerRef.current;\n if (container == null) {\n return;\n }\n const listBox = container.querySelector<HTMLElement>('[role=\"listbox\"]');\n setScrollAreaEl(listBox ?? container);\n });\n\n return () => {\n window.cancelAnimationFrame(handle);\n };\n }, [isOpen, containerRef]);\n\n // Attach scroll listener when the scroll area becomes available.\n useEffect(() => {\n if (scrollAreaEl == null) {\n return undefined;\n }\n\n const handler = (event: Event): void => {\n onScrollRef.current(event);\n };\n\n scrollAreaEl.addEventListener('scroll', handler);\n\n return () => {\n scrollAreaEl.removeEventListener('scroll', handler);\n };\n }, [scrollAreaEl]);\n}\n\nexport default useMultiSelectScrollListener;\n"],"mappings":"AAAA,SAAyBA,SAAS,EAAEC,MAAM,EAAEC,QAAQ,QAAQ,OAAO;AAWnE;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,4BAA4BA,CAAAC,IAAA,EAIE;EAAA,IAJD;IAC3CC,YAAY;IACZC,MAAM;IACNC;EACmC,CAAC,GAAAH,IAAA;EACpC,IAAM,CAACI,YAAY,EAAEC,eAAe,CAAC,GAAGP,QAAQ,CAAqB,IAAI,CAAC;;EAE1E;EACA;EACA,IAAMQ,WAAW,GAAGT,MAAM,CAACM,QAAQ,CAAC;EACpCG,WAAW,CAACC,OAAO,GAAGJ,QAAQ;EAE9BP,SAAS,CAAC,MAAM;IACd,IAAI,CAACM,MAAM,EAAE;MACXG,eAAe,CAAC,IAAI,CAAC;MACrB,OAAOG,SAAS;IAClB;;IAEA;IACA;IACA,IAAMC,MAAM,GAAGC,MAAM,CAACC,qBAAqB,CAAC,MAAM;MAChD,IAAMC,SAAS,GAAGX,YAAY,CAACM,OAAO;MACtC,IAAIK,SAAS,IAAI,IAAI,EAAE;QACrB;MACF;MACA,IAAMC,OAAO,GAAGD,SAAS,CAACE,aAAa,CAAc,kBAAkB,CAAC;MACxET,eAAe,CAACQ,OAAO,aAAPA,OAAO,cAAPA,OAAO,GAAID,SAAS,CAAC;IACvC,CAAC,CAAC;IAEF,OAAO,MAAM;MACXF,MAAM,CAACK,oBAAoB,CAACN,MAAM,CAAC;IACrC,CAAC;EACH,CAAC,EAAE,CAACP,MAAM,EAAED,YAAY,CAAC,CAAC;;EAE1B;EACAL,SAAS,CAAC,MAAM;IACd,IAAIQ,YAAY,IAAI,IAAI,EAAE;MACxB,OAAOI,SAAS;IAClB;IAEA,IAAMQ,OAAO,GAAIC,KAAY,IAAW;MACtCX,WAAW,CAACC,OAAO,CAACU,KAAK,CAAC;IAC5B,CAAC;IAEDb,YAAY,CAACc,gBAAgB,CAAC,QAAQ,EAAEF,OAAO,CAAC;IAEhD,OAAO,MAAM;MACXZ,YAAY,CAACe,mBAAmB,CAAC,QAAQ,EAAEH,OAAO,CAAC;IACrD,CAAC;EACH,CAAC,EAAE,CAACZ,YAAY,CAAC,CAAC;AACpB;AAEA,eAAeL,4BAA4B","ignoreList":[]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Selection } from '@react-types/shared';
|
|
2
|
+
import { type ItemKey, type ItemSelection } from '../utils';
|
|
3
|
+
import { type MultiSelectFlatItem } from './multiSelectUtils';
|
|
4
|
+
export interface UseMultiSelectStateOptions {
|
|
5
|
+
selectedKeys: 'all' | Iterable<ItemKey> | undefined;
|
|
6
|
+
defaultSelectedKeys: 'all' | Iterable<ItemKey> | undefined;
|
|
7
|
+
disabledKeys: Iterable<ItemKey> | undefined;
|
|
8
|
+
onChange: ((keys: ItemSelection) => void) | undefined;
|
|
9
|
+
onSelectionChange: ((keys: ItemSelection) => void) | undefined;
|
|
10
|
+
allKeys: string[];
|
|
11
|
+
}
|
|
12
|
+
export interface UseMultiSelectStateResult {
|
|
13
|
+
/** Resolved selection set (controlled or uncontrolled). */
|
|
14
|
+
selectedKeys: Set<string>;
|
|
15
|
+
/** Selected keys as an array (memoized for stable rendering). */
|
|
16
|
+
selectedKeyArray: string[];
|
|
17
|
+
/** Disabled keys, ready to pass to `<ListBox disabledKeys>`. */
|
|
18
|
+
listBoxDisabledKeys: Iterable<string> | undefined;
|
|
19
|
+
/** Toggle a single key in the selection. */
|
|
20
|
+
toggleKey: (key: string) => void;
|
|
21
|
+
/** Apply a `Selection` from the underlying `<ListBox>`. */
|
|
22
|
+
applyListBoxSelection: (selection: Selection, filteredItems: MultiSelectFlatItem[]) => void;
|
|
23
|
+
}
|
|
24
|
+
export declare function useMultiSelectState({ selectedKeys: propSelectedKeys, defaultSelectedKeys, disabledKeys: propDisabledKeys, onChange: propOnChange, onSelectionChange: propOnSelectionChange, allKeys, }: UseMultiSelectStateOptions): UseMultiSelectStateResult;
|
|
25
|
+
export default useMultiSelectState;
|
|
26
|
+
//# sourceMappingURL=useMultiSelectState.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useMultiSelectState.d.ts","sourceRoot":"","sources":["../../../src/spectrum/multiSelect/useMultiSelectState.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAO,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,aAAa,EAEnB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAoB,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEhF,MAAM,WAAW,0BAA0B;IACzC,YAAY,EAAE,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,SAAS,CAAC;IACpD,mBAAmB,EAAE,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,SAAS,CAAC;IAC3D,YAAY,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,SAAS,CAAC;IAC5C,QAAQ,EAAE,CAAC,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACtD,iBAAiB,EAAE,CAAC,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC/D,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,yBAAyB;IACxC,2DAA2D;IAC3D,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC1B,iEAAiE;IACjE,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,gEAAgE;IAChE,mBAAmB,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;IAClD,4CAA4C;IAC5C,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,2DAA2D;IAC3D,qBAAqB,EAAE,CACrB,SAAS,EAAE,SAAS,EACpB,aAAa,EAAE,mBAAmB,EAAE,KACjC,IAAI,CAAC;CACX;AAED,wBAAgB,mBAAmB,CAAC,EAClC,YAAY,EAAE,gBAAgB,EAC9B,mBAAmB,EACnB,YAAY,EAAE,gBAAgB,EAC9B,QAAQ,EAAE,YAAY,EACtB,iBAAiB,EAAE,qBAAqB,EACxC,OAAO,GACR,EAAE,0BAA0B,GAAG,yBAAyB,CAmFxD;AAED,eAAe,mBAAmB,CAAC"}
|