@arun-dev/headless 4.7.0 → 4.9.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/combobox/index.cjs +1109 -0
- package/dist/combobox/index.d.cts +322 -0
- package/dist/combobox/index.d.ts +322 -0
- package/dist/combobox/index.js +925 -0
- package/package.json +11 -1
|
@@ -0,0 +1,1109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/combobox/index.ts
|
|
21
|
+
var combobox_exports = {};
|
|
22
|
+
__export(combobox_exports, {
|
|
23
|
+
Combobox: () => index_parts_exports,
|
|
24
|
+
defaultFilter: () => defaultFilter
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(combobox_exports);
|
|
27
|
+
|
|
28
|
+
// src/combobox/index.parts.ts
|
|
29
|
+
var index_parts_exports = {};
|
|
30
|
+
__export(index_parts_exports, {
|
|
31
|
+
Chip: () => ComboboxChip,
|
|
32
|
+
ChipRemove: () => ComboboxChipRemove,
|
|
33
|
+
Clear: () => ComboboxClear,
|
|
34
|
+
Empty: () => ComboboxEmpty,
|
|
35
|
+
Input: () => ComboboxInput,
|
|
36
|
+
InputGroup: () => ComboboxInputGroup,
|
|
37
|
+
Item: () => ComboboxItem,
|
|
38
|
+
List: () => ComboboxList,
|
|
39
|
+
Popup: () => ComboboxPopup,
|
|
40
|
+
Root: () => ComboboxRoot,
|
|
41
|
+
Status: () => ComboboxStatus,
|
|
42
|
+
Trigger: () => ComboboxTrigger,
|
|
43
|
+
Value: () => ComboboxValue
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// src/combobox/ComboboxRoot.tsx
|
|
47
|
+
var import_react3 = require("react");
|
|
48
|
+
|
|
49
|
+
// src/core/anchoring.ts
|
|
50
|
+
function anchorNameFor(id) {
|
|
51
|
+
return `--hl-anchor-${id.replace(/[^a-zA-Z0-9_-]/g, "")}`;
|
|
52
|
+
}
|
|
53
|
+
var POSITION_AREA = {
|
|
54
|
+
bottom: { start: "bottom span-right", center: "bottom", end: "bottom span-left" },
|
|
55
|
+
top: { start: "top span-right", center: "top", end: "top span-left" },
|
|
56
|
+
right: { start: "right span-bottom", center: "right", end: "right span-top" },
|
|
57
|
+
left: { start: "left span-bottom", center: "left", end: "left span-top" }
|
|
58
|
+
};
|
|
59
|
+
function anchoredPopupStyle(anchorName, side, align) {
|
|
60
|
+
return {
|
|
61
|
+
positionAnchor: anchorName,
|
|
62
|
+
positionArea: POSITION_AREA[side][align],
|
|
63
|
+
// Flip across the axis it opens on; the other axis is left alone.
|
|
64
|
+
positionTryFallbacks: side === "top" || side === "bottom" ? "flip-block" : "flip-inline"
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function anchoredDataAttributes(side, align) {
|
|
68
|
+
return { "data-side": side, "data-align": align };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// src/core/rovingFocus.ts
|
|
72
|
+
function byDocumentOrder(a, b) {
|
|
73
|
+
const x = a.ref.current;
|
|
74
|
+
const y = b.ref.current;
|
|
75
|
+
if (!x || !y) return 0;
|
|
76
|
+
return x.compareDocumentPosition(y) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1;
|
|
77
|
+
}
|
|
78
|
+
function rovingIndex(key, {
|
|
79
|
+
count,
|
|
80
|
+
current,
|
|
81
|
+
orientation,
|
|
82
|
+
rtl
|
|
83
|
+
}) {
|
|
84
|
+
if (count === 0) return null;
|
|
85
|
+
const [prevKey, nextKey] = orientation === "vertical" ? ["ArrowUp", "ArrowDown"] : rtl ? ["ArrowRight", "ArrowLeft"] : ["ArrowLeft", "ArrowRight"];
|
|
86
|
+
switch (key) {
|
|
87
|
+
case nextKey:
|
|
88
|
+
return current < 0 ? 0 : (current + 1) % count;
|
|
89
|
+
case prevKey:
|
|
90
|
+
return current < 0 ? count - 1 : (current - 1 + count) % count;
|
|
91
|
+
case "Home":
|
|
92
|
+
return 0;
|
|
93
|
+
case "End":
|
|
94
|
+
return count - 1;
|
|
95
|
+
default:
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// src/core/useControlled.ts
|
|
101
|
+
var import_react = require("react");
|
|
102
|
+
function useControlled({
|
|
103
|
+
controlled,
|
|
104
|
+
default: defaultValue,
|
|
105
|
+
name = "Component",
|
|
106
|
+
state = "value"
|
|
107
|
+
}) {
|
|
108
|
+
const { current: isControlled } = (0, import_react.useRef)(controlled !== void 0);
|
|
109
|
+
const [uncontrolled, setUncontrolled] = (0, import_react.useState)(defaultValue);
|
|
110
|
+
const defaultRef = (0, import_react.useRef)(defaultValue);
|
|
111
|
+
const value = isControlled ? controlled : uncontrolled;
|
|
112
|
+
if (process.env.NODE_ENV !== "production") {
|
|
113
|
+
if (isControlled !== (controlled !== void 0)) {
|
|
114
|
+
console.error(
|
|
115
|
+
`${name}: cannot switch between controlled and uncontrolled \`${state}\`. Decide which one this component is for the whole of its life.`
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
if (!isControlled && defaultRef.current !== defaultValue) {
|
|
119
|
+
console.error(
|
|
120
|
+
`${name}: cannot change the default \`${state}\` after mount. It is only read once, so later changes are silently ignored.`
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const setValue = (0, import_react.useCallback)(
|
|
125
|
+
(next) => {
|
|
126
|
+
if (!isControlled) setUncontrolled(next);
|
|
127
|
+
},
|
|
128
|
+
[isControlled]
|
|
129
|
+
);
|
|
130
|
+
return [value, setValue];
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// src/combobox/defaultFilter.ts
|
|
134
|
+
function fold(text) {
|
|
135
|
+
return text.normalize("NFD").replace(/\p{M}/gu, "").toLocaleLowerCase();
|
|
136
|
+
}
|
|
137
|
+
function defaultFilter(items, query, itemToString) {
|
|
138
|
+
const needle = fold(query.trim());
|
|
139
|
+
if (needle === "") return [...items];
|
|
140
|
+
return items.filter((item) => fold(itemToString(item)).includes(needle));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// src/combobox/ComboboxRootContext.ts
|
|
144
|
+
var import_react2 = require("react");
|
|
145
|
+
function createHighlightStore() {
|
|
146
|
+
let current = { index: -1, reason: "none" };
|
|
147
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
148
|
+
return {
|
|
149
|
+
get: () => current,
|
|
150
|
+
set(next) {
|
|
151
|
+
if (next.index === current.index && next.reason === current.reason) return;
|
|
152
|
+
current = next;
|
|
153
|
+
for (const listener of listeners) listener();
|
|
154
|
+
},
|
|
155
|
+
subscribe(listener) {
|
|
156
|
+
listeners.add(listener);
|
|
157
|
+
return () => listeners.delete(listener);
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
var ComboboxRootContext = (0, import_react2.createContext)(null);
|
|
162
|
+
function useComboboxRootContext(part) {
|
|
163
|
+
const context = (0, import_react2.useContext)(ComboboxRootContext);
|
|
164
|
+
if (context === null) {
|
|
165
|
+
throw new Error(`<Combobox.${part}> must be rendered inside <Combobox.Root>.`);
|
|
166
|
+
}
|
|
167
|
+
return context;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// src/combobox/ComboboxRoot.tsx
|
|
171
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
172
|
+
function defaultItemToString(item) {
|
|
173
|
+
if (typeof item === "string") return item;
|
|
174
|
+
if (item !== null && typeof item === "object" && "label" in item) return String(item.label);
|
|
175
|
+
return String(item);
|
|
176
|
+
}
|
|
177
|
+
function ComboboxRoot({
|
|
178
|
+
items,
|
|
179
|
+
itemToString: itemToStringProp,
|
|
180
|
+
itemToKey: itemToKeyProp,
|
|
181
|
+
filter,
|
|
182
|
+
multiple: multipleProp,
|
|
183
|
+
value: valueProp,
|
|
184
|
+
defaultValue,
|
|
185
|
+
onValueChange,
|
|
186
|
+
inputValue: inputValueProp,
|
|
187
|
+
defaultInputValue,
|
|
188
|
+
onInputValueChange,
|
|
189
|
+
open: openProp,
|
|
190
|
+
defaultOpen,
|
|
191
|
+
onOpenChange,
|
|
192
|
+
onItemHighlighted,
|
|
193
|
+
onLoadMore,
|
|
194
|
+
loading = false,
|
|
195
|
+
disabled = false,
|
|
196
|
+
name,
|
|
197
|
+
form,
|
|
198
|
+
children
|
|
199
|
+
}) {
|
|
200
|
+
const multiple = multipleProp === true;
|
|
201
|
+
const itemToString = itemToStringProp ?? defaultItemToString;
|
|
202
|
+
const itemToKey = itemToKeyProp ?? itemToString;
|
|
203
|
+
const [value, setValueState] = useControlled({
|
|
204
|
+
controlled: valueProp,
|
|
205
|
+
default: defaultValue ?? (multiple ? [] : null),
|
|
206
|
+
name: "Combobox.Root",
|
|
207
|
+
state: "value"
|
|
208
|
+
});
|
|
209
|
+
const selectedItems = (0, import_react3.useMemo)(
|
|
210
|
+
() => multiple ? value : value == null ? [] : [value],
|
|
211
|
+
[multiple, value]
|
|
212
|
+
);
|
|
213
|
+
const selectedKeys = (0, import_react3.useMemo)(
|
|
214
|
+
() => new Set(selectedItems.map((item) => itemToKey(item))),
|
|
215
|
+
[selectedItems, itemToKey]
|
|
216
|
+
);
|
|
217
|
+
const selectedLabel = !multiple && selectedItems[0] != null ? itemToString(selectedItems[0]) : "";
|
|
218
|
+
const [inputValue, setInputValueState] = useControlled({
|
|
219
|
+
controlled: inputValueProp,
|
|
220
|
+
default: defaultInputValue ?? selectedLabel,
|
|
221
|
+
name: "Combobox.Root",
|
|
222
|
+
state: "inputValue"
|
|
223
|
+
});
|
|
224
|
+
const [open, setOpenState] = useControlled({
|
|
225
|
+
controlled: openProp,
|
|
226
|
+
default: defaultOpen ?? false,
|
|
227
|
+
name: "Combobox.Root",
|
|
228
|
+
state: "open"
|
|
229
|
+
});
|
|
230
|
+
const query = !multiple && inputValue === selectedLabel ? "" : inputValue;
|
|
231
|
+
const deferredQuery = (0, import_react3.useDeferredValue)(query);
|
|
232
|
+
const filteredItems = (0, import_react3.useMemo)(() => {
|
|
233
|
+
if (filter === null) return items;
|
|
234
|
+
const run = filter ?? defaultFilter;
|
|
235
|
+
return run(items, deferredQuery, itemToString);
|
|
236
|
+
}, [items, filter, deferredQuery, itemToString]);
|
|
237
|
+
const indexByKey = (0, import_react3.useMemo)(
|
|
238
|
+
() => new Map(filteredItems.map((item, index) => [itemToKey(item), index])),
|
|
239
|
+
[filteredItems, itemToKey]
|
|
240
|
+
);
|
|
241
|
+
const [highlight] = (0, import_react3.useState)(createHighlightStore);
|
|
242
|
+
const setHighlight = (0, import_react3.useCallback)(
|
|
243
|
+
(index, reason) => {
|
|
244
|
+
if (highlight.get().index === index && highlight.get().reason === reason) return;
|
|
245
|
+
highlight.set({ index, reason });
|
|
246
|
+
onItemHighlighted?.(filteredItems[index], { index, reason });
|
|
247
|
+
},
|
|
248
|
+
[highlight, filteredItems, onItemHighlighted]
|
|
249
|
+
);
|
|
250
|
+
(0, import_react3.useEffect)(() => {
|
|
251
|
+
if (highlight.get().index !== -1) setHighlight(-1, "none");
|
|
252
|
+
}, [filteredItems]);
|
|
253
|
+
const setValue = (0, import_react3.useCallback)(
|
|
254
|
+
(next, reason) => {
|
|
255
|
+
setValueState(next);
|
|
256
|
+
onValueChange?.(next, {
|
|
257
|
+
reason
|
|
258
|
+
});
|
|
259
|
+
},
|
|
260
|
+
[setValueState, onValueChange]
|
|
261
|
+
);
|
|
262
|
+
const setInputValue = (0, import_react3.useCallback)(
|
|
263
|
+
(next, reason) => {
|
|
264
|
+
if (next === inputValue) return;
|
|
265
|
+
setInputValueState(next);
|
|
266
|
+
onInputValueChange?.(next, { reason });
|
|
267
|
+
},
|
|
268
|
+
[inputValue, setInputValueState, onInputValueChange]
|
|
269
|
+
);
|
|
270
|
+
const setOpen = (0, import_react3.useCallback)(
|
|
271
|
+
(next, reason) => {
|
|
272
|
+
if (next === open) return;
|
|
273
|
+
setOpenState(next);
|
|
274
|
+
onOpenChange?.(next, { reason });
|
|
275
|
+
if (!next) setHighlight(-1, "none");
|
|
276
|
+
},
|
|
277
|
+
[open, setOpenState, onOpenChange, setHighlight]
|
|
278
|
+
);
|
|
279
|
+
const close = (0, import_react3.useCallback)(
|
|
280
|
+
(reason) => {
|
|
281
|
+
setOpen(false, reason);
|
|
282
|
+
setInputValue(selectedLabel, reason);
|
|
283
|
+
},
|
|
284
|
+
[setOpen, setInputValue, selectedLabel]
|
|
285
|
+
);
|
|
286
|
+
const select = (0, import_react3.useCallback)(
|
|
287
|
+
(item) => {
|
|
288
|
+
const key = itemToKey(item);
|
|
289
|
+
if (multiple) {
|
|
290
|
+
const current = value;
|
|
291
|
+
setValue(
|
|
292
|
+
selectedKeys.has(key) ? current.filter((i) => itemToKey(i) !== key) : [...current, item],
|
|
293
|
+
"item-press"
|
|
294
|
+
);
|
|
295
|
+
setInputValue("", "item-press");
|
|
296
|
+
} else {
|
|
297
|
+
if (!selectedKeys.has(key)) setValue(item, "item-press");
|
|
298
|
+
setInputValue(itemToString(item), "item-press");
|
|
299
|
+
}
|
|
300
|
+
setOpen(false, "item-press");
|
|
301
|
+
},
|
|
302
|
+
[multiple, value, selectedKeys, itemToKey, itemToString, setValue, setInputValue, setOpen]
|
|
303
|
+
);
|
|
304
|
+
const remove = (0, import_react3.useCallback)(
|
|
305
|
+
(item, reason) => {
|
|
306
|
+
if (!multiple) return;
|
|
307
|
+
const key = itemToKey(item);
|
|
308
|
+
setValue(
|
|
309
|
+
value.filter((i) => itemToKey(i) !== key),
|
|
310
|
+
reason
|
|
311
|
+
);
|
|
312
|
+
},
|
|
313
|
+
[multiple, value, itemToKey, setValue]
|
|
314
|
+
);
|
|
315
|
+
const clearSelection = (0, import_react3.useCallback)(
|
|
316
|
+
(reason) => setValue(multiple ? [] : null, reason),
|
|
317
|
+
[multiple, setValue]
|
|
318
|
+
);
|
|
319
|
+
const clear = (0, import_react3.useCallback)(() => {
|
|
320
|
+
setInputValue("", "clear");
|
|
321
|
+
clearSelection("clear");
|
|
322
|
+
setHighlight(-1, "none");
|
|
323
|
+
}, [setInputValue, clearSelection, setHighlight]);
|
|
324
|
+
const selectedKey = !multiple && selectedItems[0] != null ? itemToKey(selectedItems[0]) : null;
|
|
325
|
+
const lastSelectedKeyRef = (0, import_react3.useRef)(selectedKey);
|
|
326
|
+
(0, import_react3.useEffect)(() => {
|
|
327
|
+
if (lastSelectedKeyRef.current === selectedKey) return;
|
|
328
|
+
lastSelectedKeyRef.current = selectedKey;
|
|
329
|
+
setInputValueState(selectedLabel);
|
|
330
|
+
}, [selectedKey]);
|
|
331
|
+
const [chips, setChips] = (0, import_react3.useState)([]);
|
|
332
|
+
const registerChip = (0, import_react3.useCallback)((entry) => {
|
|
333
|
+
setChips((current) => [...current, entry].sort(byDocumentOrder));
|
|
334
|
+
return () => setChips((current) => current.filter((c) => c !== entry));
|
|
335
|
+
}, []);
|
|
336
|
+
const [disabledKeys] = (0, import_react3.useState)(() => /* @__PURE__ */ new Set());
|
|
337
|
+
const [insideRefs] = (0, import_react3.useState)(() => /* @__PURE__ */ new Set());
|
|
338
|
+
const registerInside = (0, import_react3.useCallback)(
|
|
339
|
+
(ref) => {
|
|
340
|
+
insideRefs.add(ref);
|
|
341
|
+
return () => {
|
|
342
|
+
insideRefs.delete(ref);
|
|
343
|
+
};
|
|
344
|
+
},
|
|
345
|
+
[insideRefs]
|
|
346
|
+
);
|
|
347
|
+
const [hasGroup, setHasGroup] = (0, import_react3.useState)(false);
|
|
348
|
+
const registerGroup = (0, import_react3.useCallback)(() => {
|
|
349
|
+
setHasGroup(true);
|
|
350
|
+
return () => setHasGroup(false);
|
|
351
|
+
}, []);
|
|
352
|
+
const inputRef = (0, import_react3.useRef)(null);
|
|
353
|
+
const popupRef = (0, import_react3.useRef)(null);
|
|
354
|
+
const { current: initial } = (0, import_react3.useRef)({ value, inputValue });
|
|
355
|
+
(0, import_react3.useEffect)(() => {
|
|
356
|
+
const owner = inputRef.current?.form;
|
|
357
|
+
if (!owner) return;
|
|
358
|
+
function onReset(event) {
|
|
359
|
+
queueMicrotask(() => {
|
|
360
|
+
if (event.defaultPrevented) return;
|
|
361
|
+
if (value !== initial.value) setValue(initial.value, "form-reset");
|
|
362
|
+
setInputValue(initial.inputValue, "form-reset");
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
owner.addEventListener("reset", onReset);
|
|
366
|
+
return () => owner.removeEventListener("reset", onReset);
|
|
367
|
+
}, [value, initial, setValue, setInputValue]);
|
|
368
|
+
const isInside = (0, import_react3.useCallback)(
|
|
369
|
+
(node) => [inputRef, popupRef, ...insideRefs, ...chips.map((chip) => chip.ref)].some(
|
|
370
|
+
(ref) => ref.current?.contains(node) ?? false
|
|
371
|
+
),
|
|
372
|
+
[insideRefs, chips]
|
|
373
|
+
);
|
|
374
|
+
const id = (0, import_react3.useId)();
|
|
375
|
+
const context = (0, import_react3.useMemo)(
|
|
376
|
+
() => ({
|
|
377
|
+
open,
|
|
378
|
+
multiple,
|
|
379
|
+
disabled,
|
|
380
|
+
inputValue,
|
|
381
|
+
loading,
|
|
382
|
+
filteredItems,
|
|
383
|
+
itemToString,
|
|
384
|
+
itemToKey,
|
|
385
|
+
indexByKey,
|
|
386
|
+
selectedKeys,
|
|
387
|
+
selectedItems,
|
|
388
|
+
disabledKeys,
|
|
389
|
+
highlight,
|
|
390
|
+
setHighlight,
|
|
391
|
+
setOpen,
|
|
392
|
+
setInputValue,
|
|
393
|
+
select,
|
|
394
|
+
remove,
|
|
395
|
+
clearSelection,
|
|
396
|
+
clear,
|
|
397
|
+
close,
|
|
398
|
+
registerChip,
|
|
399
|
+
chips,
|
|
400
|
+
registerInside,
|
|
401
|
+
isInside,
|
|
402
|
+
hasGroup,
|
|
403
|
+
registerGroup,
|
|
404
|
+
onLoadMore,
|
|
405
|
+
inputId: `${id}-input`,
|
|
406
|
+
listId: `${id}-list`,
|
|
407
|
+
optionId: (index) => `${id}-option-${index}`,
|
|
408
|
+
anchorName: anchorNameFor(id),
|
|
409
|
+
inputRef,
|
|
410
|
+
popupRef
|
|
411
|
+
}),
|
|
412
|
+
[
|
|
413
|
+
open,
|
|
414
|
+
multiple,
|
|
415
|
+
disabled,
|
|
416
|
+
inputValue,
|
|
417
|
+
loading,
|
|
418
|
+
filteredItems,
|
|
419
|
+
itemToString,
|
|
420
|
+
itemToKey,
|
|
421
|
+
indexByKey,
|
|
422
|
+
selectedKeys,
|
|
423
|
+
selectedItems,
|
|
424
|
+
disabledKeys,
|
|
425
|
+
highlight,
|
|
426
|
+
setHighlight,
|
|
427
|
+
setOpen,
|
|
428
|
+
setInputValue,
|
|
429
|
+
select,
|
|
430
|
+
remove,
|
|
431
|
+
clearSelection,
|
|
432
|
+
clear,
|
|
433
|
+
close,
|
|
434
|
+
registerChip,
|
|
435
|
+
chips,
|
|
436
|
+
registerInside,
|
|
437
|
+
isInside,
|
|
438
|
+
hasGroup,
|
|
439
|
+
registerGroup,
|
|
440
|
+
onLoadMore,
|
|
441
|
+
id
|
|
442
|
+
]
|
|
443
|
+
);
|
|
444
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ComboboxRootContext.Provider, { value: context, children: [
|
|
445
|
+
children,
|
|
446
|
+
name !== void 0 && (multiple ? selectedItems : [selectedItems[0] ?? null]).map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
447
|
+
"input",
|
|
448
|
+
{
|
|
449
|
+
type: "hidden",
|
|
450
|
+
name,
|
|
451
|
+
form,
|
|
452
|
+
disabled,
|
|
453
|
+
value: item === null ? "" : itemToKey(item)
|
|
454
|
+
},
|
|
455
|
+
item === null ? index : itemToKey(item)
|
|
456
|
+
))
|
|
457
|
+
] });
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// src/combobox/ComboboxInput.tsx
|
|
461
|
+
var import_react5 = require("react");
|
|
462
|
+
|
|
463
|
+
// src/core/useRender.ts
|
|
464
|
+
var import_react4 = require("react");
|
|
465
|
+
|
|
466
|
+
// src/core/mergeProps.ts
|
|
467
|
+
function isEventHandler(key) {
|
|
468
|
+
return key.length > 2 && key.startsWith("on") && key[2] === key[2]?.toUpperCase();
|
|
469
|
+
}
|
|
470
|
+
function mergeRefs(...refs) {
|
|
471
|
+
return (node) => {
|
|
472
|
+
const cleanups = refs.map((ref) => {
|
|
473
|
+
if (typeof ref === "function") {
|
|
474
|
+
const result = ref(node);
|
|
475
|
+
return typeof result === "function" ? result : () => ref(null);
|
|
476
|
+
}
|
|
477
|
+
if (ref && typeof ref === "object") {
|
|
478
|
+
ref.current = node;
|
|
479
|
+
return () => {
|
|
480
|
+
ref.current = null;
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
return void 0;
|
|
484
|
+
});
|
|
485
|
+
return () => {
|
|
486
|
+
cleanups.forEach((cleanup) => {
|
|
487
|
+
if (typeof cleanup === "function") cleanup();
|
|
488
|
+
});
|
|
489
|
+
};
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
function isSyntheticEvent(event) {
|
|
493
|
+
return typeof event === "object" && event !== null && "nativeEvent" in event;
|
|
494
|
+
}
|
|
495
|
+
function makeEventPreventable(event) {
|
|
496
|
+
if (Object.prototype.hasOwnProperty.call(event, "preventComponentHandler")) return;
|
|
497
|
+
const target = event;
|
|
498
|
+
target.preventComponentHandler = () => {
|
|
499
|
+
target.componentHandlerPrevented = true;
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
function isComponentHandlerPrevented(event) {
|
|
503
|
+
return typeof event === "object" && event !== null && event.componentHandlerPrevented === true;
|
|
504
|
+
}
|
|
505
|
+
function chainHandlers(earlier, value) {
|
|
506
|
+
return (...args) => {
|
|
507
|
+
const event = args[0];
|
|
508
|
+
if (isSyntheticEvent(event)) makeEventPreventable(event);
|
|
509
|
+
value(...args);
|
|
510
|
+
if (isComponentHandlerPrevented(event)) return;
|
|
511
|
+
earlier(...args);
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
function mergeProps(...objects) {
|
|
515
|
+
const merged = {};
|
|
516
|
+
for (const props of objects) {
|
|
517
|
+
if (!props) continue;
|
|
518
|
+
for (const key of Object.keys(props)) {
|
|
519
|
+
const value = props[key];
|
|
520
|
+
if (value === void 0) continue;
|
|
521
|
+
if (isEventHandler(key) && typeof value === "function") {
|
|
522
|
+
const earlier = merged[key];
|
|
523
|
+
merged[key] = typeof earlier === "function" ? chainHandlers(earlier, value) : value;
|
|
524
|
+
} else if (key === "className") {
|
|
525
|
+
merged.className = merged.className ? `${String(merged.className)} ${String(value)}` : value;
|
|
526
|
+
} else if (key === "style") {
|
|
527
|
+
merged.style = { ...merged.style, ...value };
|
|
528
|
+
} else if (key === "ref") {
|
|
529
|
+
merged.ref = merged.ref ? mergeRefs(merged.ref, value) : value;
|
|
530
|
+
} else {
|
|
531
|
+
merged[key] = value;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
return merged;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// src/core/useRender.ts
|
|
539
|
+
function useRender({
|
|
540
|
+
render,
|
|
541
|
+
defaultTagName,
|
|
542
|
+
props,
|
|
543
|
+
consumerProps
|
|
544
|
+
}) {
|
|
545
|
+
const merged = mergeProps(props, consumerProps);
|
|
546
|
+
if ((0, import_react4.isValidElement)(render)) {
|
|
547
|
+
return (0, import_react4.cloneElement)(render, mergeProps(merged, render.props));
|
|
548
|
+
}
|
|
549
|
+
return (0, import_react4.createElement)(defaultTagName, merged);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// src/combobox/comboboxDataAttributes.ts
|
|
553
|
+
function comboboxDataAttributes({ open, disabled }) {
|
|
554
|
+
return {
|
|
555
|
+
"data-open": open ? "" : void 0,
|
|
556
|
+
"data-closed": open ? void 0 : "",
|
|
557
|
+
"data-disabled": disabled ? "" : void 0
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
function comboboxItemDataAttributes({
|
|
561
|
+
selected,
|
|
562
|
+
highlighted,
|
|
563
|
+
disabled
|
|
564
|
+
}) {
|
|
565
|
+
return {
|
|
566
|
+
"data-selected": selected ? "" : void 0,
|
|
567
|
+
"data-highlighted": highlighted ? "" : void 0,
|
|
568
|
+
"data-disabled": disabled ? "" : void 0
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
function comboboxClearDataAttributes(visible) {
|
|
572
|
+
return { "data-visible": visible ? "" : void 0 };
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// src/combobox/ComboboxInput.tsx
|
|
576
|
+
function ComboboxInput({ className, render, ...rest }) {
|
|
577
|
+
const {
|
|
578
|
+
open,
|
|
579
|
+
multiple,
|
|
580
|
+
disabled,
|
|
581
|
+
inputValue,
|
|
582
|
+
filteredItems,
|
|
583
|
+
itemToKey,
|
|
584
|
+
selectedItems,
|
|
585
|
+
selectedKeys,
|
|
586
|
+
disabledKeys,
|
|
587
|
+
highlight,
|
|
588
|
+
setHighlight,
|
|
589
|
+
setOpen,
|
|
590
|
+
setInputValue,
|
|
591
|
+
select,
|
|
592
|
+
remove,
|
|
593
|
+
clearSelection,
|
|
594
|
+
close,
|
|
595
|
+
chips,
|
|
596
|
+
inputId,
|
|
597
|
+
listId,
|
|
598
|
+
optionId,
|
|
599
|
+
anchorName,
|
|
600
|
+
hasGroup,
|
|
601
|
+
inputRef,
|
|
602
|
+
popupRef
|
|
603
|
+
} = useComboboxRootContext("Input");
|
|
604
|
+
const highlighted = (0, import_react5.useSyncExternalStore)(
|
|
605
|
+
highlight.subscribe,
|
|
606
|
+
() => highlight.get().index,
|
|
607
|
+
() => -1
|
|
608
|
+
);
|
|
609
|
+
function enabledIndices() {
|
|
610
|
+
const indices = [];
|
|
611
|
+
filteredItems.forEach((item, index) => {
|
|
612
|
+
if (!disabledKeys.has(itemToKey(item))) indices.push(index);
|
|
613
|
+
});
|
|
614
|
+
return indices;
|
|
615
|
+
}
|
|
616
|
+
function openingIndex(key, enabled) {
|
|
617
|
+
if (!multiple && selectedItems[0] != null) {
|
|
618
|
+
const selected = enabled.find((i) => selectedKeys.has(itemToKey(filteredItems[i])));
|
|
619
|
+
if (selected !== void 0) return selected;
|
|
620
|
+
}
|
|
621
|
+
return (key === "ArrowDown" ? enabled[0] : enabled.at(-1)) ?? -1;
|
|
622
|
+
}
|
|
623
|
+
function moveHighlight(key) {
|
|
624
|
+
const enabled = enabledIndices();
|
|
625
|
+
const next = rovingIndex(key, {
|
|
626
|
+
count: enabled.length,
|
|
627
|
+
current: enabled.indexOf(highlighted),
|
|
628
|
+
orientation: "vertical",
|
|
629
|
+
rtl: false
|
|
630
|
+
});
|
|
631
|
+
if (next !== null) setHighlight(enabled[next] ?? -1, "keyboard");
|
|
632
|
+
}
|
|
633
|
+
return useRender({
|
|
634
|
+
render,
|
|
635
|
+
defaultTagName: "input",
|
|
636
|
+
props: {
|
|
637
|
+
id: inputId,
|
|
638
|
+
type: "text",
|
|
639
|
+
role: "combobox",
|
|
640
|
+
autoComplete: "off",
|
|
641
|
+
"aria-autocomplete": "list",
|
|
642
|
+
"aria-expanded": open,
|
|
643
|
+
"aria-controls": listId,
|
|
644
|
+
"aria-activedescendant": open && highlighted >= 0 ? optionId(highlighted) : void 0,
|
|
645
|
+
disabled: disabled || void 0,
|
|
646
|
+
value: inputValue,
|
|
647
|
+
...comboboxDataAttributes({ open, multiple, disabled }),
|
|
648
|
+
// The anchor, unless an InputGroup around it is.
|
|
649
|
+
style: hasGroup ? void 0 : { anchorName },
|
|
650
|
+
className,
|
|
651
|
+
ref: inputRef,
|
|
652
|
+
onChange(event) {
|
|
653
|
+
const text = event.target.value;
|
|
654
|
+
setInputValue(text, "input");
|
|
655
|
+
if (!multiple && text === "" && selectedItems.length > 0) clearSelection("input");
|
|
656
|
+
setOpen(true, "input");
|
|
657
|
+
},
|
|
658
|
+
onClick() {
|
|
659
|
+
if (!disabled) setOpen(true, "input-press");
|
|
660
|
+
},
|
|
661
|
+
onKeyDown(event) {
|
|
662
|
+
const { key } = event;
|
|
663
|
+
if (key === "ArrowDown" || key === "ArrowUp") {
|
|
664
|
+
event.preventDefault();
|
|
665
|
+
if (!open) {
|
|
666
|
+
setOpen(true, "keyboard");
|
|
667
|
+
if (!event.altKey) setHighlight(openingIndex(key, enabledIndices()), "keyboard");
|
|
668
|
+
return;
|
|
669
|
+
}
|
|
670
|
+
moveHighlight(key);
|
|
671
|
+
return;
|
|
672
|
+
}
|
|
673
|
+
if ((key === "Home" || key === "End") && open) {
|
|
674
|
+
event.preventDefault();
|
|
675
|
+
moveHighlight(key);
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
678
|
+
if (key === "Enter") {
|
|
679
|
+
if (!open || highlighted < 0) return;
|
|
680
|
+
event.preventDefault();
|
|
681
|
+
const item = filteredItems[highlighted];
|
|
682
|
+
if (!disabledKeys.has(itemToKey(item))) select(item);
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
685
|
+
if (key === "Escape") {
|
|
686
|
+
if (!open) return;
|
|
687
|
+
event.preventDefault();
|
|
688
|
+
close("escape");
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
if (!multiple) return;
|
|
692
|
+
if (key === "Backspace" && inputValue === "" && selectedItems.length > 0) {
|
|
693
|
+
remove(selectedItems.at(-1), "chip-remove");
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
const input = event.currentTarget;
|
|
697
|
+
if (key === "ArrowLeft" && chips.length > 0 && input.selectionStart === 0 && input.selectionEnd === 0) {
|
|
698
|
+
event.preventDefault();
|
|
699
|
+
chips.at(-1)?.ref.current?.focus();
|
|
700
|
+
}
|
|
701
|
+
},
|
|
702
|
+
onBlur(event) {
|
|
703
|
+
const next = event.relatedTarget;
|
|
704
|
+
if (next && popupRef.current?.contains(next)) return;
|
|
705
|
+
if (next && chips.some((chip) => chip.ref.current === next)) {
|
|
706
|
+
setOpen(false, "blur");
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
close("blur");
|
|
710
|
+
}
|
|
711
|
+
},
|
|
712
|
+
consumerProps: rest
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
// src/combobox/ComboboxInputGroup.tsx
|
|
717
|
+
var import_react6 = require("react");
|
|
718
|
+
function ComboboxInputGroup({
|
|
719
|
+
className,
|
|
720
|
+
children,
|
|
721
|
+
render,
|
|
722
|
+
...rest
|
|
723
|
+
}) {
|
|
724
|
+
const { open, multiple, disabled, anchorName, registerGroup, registerInside, inputRef } = useComboboxRootContext("InputGroup");
|
|
725
|
+
const elementRef = (0, import_react6.useRef)(null);
|
|
726
|
+
(0, import_react6.useLayoutEffect)(() => registerGroup(), [registerGroup]);
|
|
727
|
+
(0, import_react6.useLayoutEffect)(() => registerInside(elementRef), [registerInside]);
|
|
728
|
+
return useRender({
|
|
729
|
+
render,
|
|
730
|
+
defaultTagName: "div",
|
|
731
|
+
props: {
|
|
732
|
+
...comboboxDataAttributes({ open, multiple, disabled }),
|
|
733
|
+
style: { anchorName },
|
|
734
|
+
className,
|
|
735
|
+
children,
|
|
736
|
+
ref: elementRef,
|
|
737
|
+
onMouseDown(event) {
|
|
738
|
+
if (event.target !== event.currentTarget || disabled) return;
|
|
739
|
+
event.preventDefault();
|
|
740
|
+
inputRef.current?.focus();
|
|
741
|
+
}
|
|
742
|
+
},
|
|
743
|
+
consumerProps: rest
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
// src/combobox/ComboboxTrigger.tsx
|
|
748
|
+
var import_react7 = require("react");
|
|
749
|
+
function ComboboxTrigger({ className, children, render, ...rest }) {
|
|
750
|
+
const { open, multiple, disabled, setOpen, close, listId, inputRef, registerInside } = useComboboxRootContext("Trigger");
|
|
751
|
+
const elementRef = (0, import_react7.useRef)(null);
|
|
752
|
+
(0, import_react7.useLayoutEffect)(() => registerInside(elementRef), [registerInside]);
|
|
753
|
+
return useRender({
|
|
754
|
+
render,
|
|
755
|
+
defaultTagName: "button",
|
|
756
|
+
props: {
|
|
757
|
+
type: "button",
|
|
758
|
+
tabIndex: -1,
|
|
759
|
+
"aria-expanded": open,
|
|
760
|
+
"aria-controls": listId,
|
|
761
|
+
disabled: disabled || void 0,
|
|
762
|
+
...comboboxDataAttributes({ open, multiple, disabled }),
|
|
763
|
+
className,
|
|
764
|
+
children,
|
|
765
|
+
ref: elementRef,
|
|
766
|
+
onMouseDown(event) {
|
|
767
|
+
event.preventDefault();
|
|
768
|
+
},
|
|
769
|
+
onClick() {
|
|
770
|
+
if (open) close("trigger-press");
|
|
771
|
+
else setOpen(true, "trigger-press");
|
|
772
|
+
inputRef.current?.focus();
|
|
773
|
+
}
|
|
774
|
+
},
|
|
775
|
+
consumerProps: rest
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
// src/combobox/ComboboxClear.tsx
|
|
780
|
+
var import_react8 = require("react");
|
|
781
|
+
function ComboboxClear({ className, children, render, ...rest }) {
|
|
782
|
+
const { disabled, selectedItems, clear, inputRef, registerInside } = useComboboxRootContext("Clear");
|
|
783
|
+
const elementRef = (0, import_react8.useRef)(null);
|
|
784
|
+
(0, import_react8.useLayoutEffect)(() => registerInside(elementRef), [registerInside]);
|
|
785
|
+
return useRender({
|
|
786
|
+
render,
|
|
787
|
+
defaultTagName: "button",
|
|
788
|
+
props: {
|
|
789
|
+
type: "button",
|
|
790
|
+
tabIndex: -1,
|
|
791
|
+
disabled: disabled || void 0,
|
|
792
|
+
...comboboxClearDataAttributes(selectedItems.length > 0),
|
|
793
|
+
className,
|
|
794
|
+
children,
|
|
795
|
+
ref: elementRef,
|
|
796
|
+
onMouseDown(event) {
|
|
797
|
+
event.preventDefault();
|
|
798
|
+
},
|
|
799
|
+
onClick() {
|
|
800
|
+
if (disabled) return;
|
|
801
|
+
clear();
|
|
802
|
+
inputRef.current?.focus();
|
|
803
|
+
}
|
|
804
|
+
},
|
|
805
|
+
consumerProps: rest
|
|
806
|
+
});
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
// src/combobox/ComboboxPopup.tsx
|
|
810
|
+
var import_react9 = require("react");
|
|
811
|
+
function ComboboxPopup({
|
|
812
|
+
side = "bottom",
|
|
813
|
+
align = "start",
|
|
814
|
+
className,
|
|
815
|
+
children,
|
|
816
|
+
render,
|
|
817
|
+
...rest
|
|
818
|
+
}) {
|
|
819
|
+
const { open, multiple, disabled, setOpen, close, isInside, anchorName, inputRef, popupRef } = useComboboxRootContext("Popup");
|
|
820
|
+
(0, import_react9.useEffect)(() => {
|
|
821
|
+
if (!open) return;
|
|
822
|
+
function onPointerDown(event) {
|
|
823
|
+
if (event.target instanceof Node && isInside(event.target)) return;
|
|
824
|
+
close("outside-press");
|
|
825
|
+
}
|
|
826
|
+
document.addEventListener("pointerdown", onPointerDown, true);
|
|
827
|
+
return () => document.removeEventListener("pointerdown", onPointerDown, true);
|
|
828
|
+
}, [open, isInside, close]);
|
|
829
|
+
const [, resync] = (0, import_react9.useReducer)((n) => n + 1, 0);
|
|
830
|
+
(0, import_react9.useEffect)(() => {
|
|
831
|
+
const popup = popupRef.current;
|
|
832
|
+
if (!popup || typeof popup.showPopover !== "function") return;
|
|
833
|
+
const shown = popup.matches(":popover-open");
|
|
834
|
+
if (open && !shown) {
|
|
835
|
+
popup.showPopover({ source: inputRef.current ?? void 0 });
|
|
836
|
+
if (!popup.matches(":popover-open")) setOpen(false, "outside-press");
|
|
837
|
+
} else if (!open && shown) {
|
|
838
|
+
popup.hidePopover();
|
|
839
|
+
}
|
|
840
|
+
});
|
|
841
|
+
return useRender({
|
|
842
|
+
render,
|
|
843
|
+
defaultTagName: "div",
|
|
844
|
+
props: {
|
|
845
|
+
popover: "manual",
|
|
846
|
+
...anchoredDataAttributes(side, align),
|
|
847
|
+
...comboboxDataAttributes({ open, multiple, disabled }),
|
|
848
|
+
style: anchoredPopupStyle(anchorName, side, align),
|
|
849
|
+
className,
|
|
850
|
+
children,
|
|
851
|
+
ref: popupRef,
|
|
852
|
+
onMouseDown(event) {
|
|
853
|
+
event.preventDefault();
|
|
854
|
+
},
|
|
855
|
+
onToggle(event) {
|
|
856
|
+
if (event.target !== event.currentTarget) return;
|
|
857
|
+
if (event.nativeEvent.newState !== "closed") return;
|
|
858
|
+
close("outside-press");
|
|
859
|
+
resync();
|
|
860
|
+
}
|
|
861
|
+
},
|
|
862
|
+
consumerProps: rest
|
|
863
|
+
});
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
// src/combobox/ComboboxList.tsx
|
|
867
|
+
var import_react10 = require("react");
|
|
868
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
869
|
+
function ComboboxList({
|
|
870
|
+
className,
|
|
871
|
+
children,
|
|
872
|
+
render,
|
|
873
|
+
...rest
|
|
874
|
+
}) {
|
|
875
|
+
const { multiple, loading, filteredItems, onLoadMore, listId } = useComboboxRootContext("List");
|
|
876
|
+
const content = typeof children === "function" ? filteredItems.map((item, index) => children(item, index)) : children;
|
|
877
|
+
return useRender({
|
|
878
|
+
render,
|
|
879
|
+
defaultTagName: "div",
|
|
880
|
+
props: {
|
|
881
|
+
id: listId,
|
|
882
|
+
role: "listbox",
|
|
883
|
+
"aria-multiselectable": multiple || void 0,
|
|
884
|
+
"aria-busy": loading || void 0,
|
|
885
|
+
className,
|
|
886
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
|
|
887
|
+
content,
|
|
888
|
+
onLoadMore && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(LoadMoreSentinel, {})
|
|
889
|
+
] })
|
|
890
|
+
},
|
|
891
|
+
consumerProps: rest
|
|
892
|
+
});
|
|
893
|
+
}
|
|
894
|
+
function LoadMoreSentinel() {
|
|
895
|
+
const { open, loading, filteredItems, onLoadMore } = useComboboxRootContext("List");
|
|
896
|
+
const ref = (0, import_react10.useRef)(null);
|
|
897
|
+
const onLoadMoreRef = (0, import_react10.useRef)(onLoadMore);
|
|
898
|
+
onLoadMoreRef.current = onLoadMore;
|
|
899
|
+
(0, import_react10.useEffect)(() => {
|
|
900
|
+
const element = ref.current;
|
|
901
|
+
if (!element || !open || loading || typeof IntersectionObserver === "undefined") return;
|
|
902
|
+
const observer = new IntersectionObserver((entries) => {
|
|
903
|
+
if (entries.some((entry) => entry.isIntersecting)) onLoadMoreRef.current?.();
|
|
904
|
+
});
|
|
905
|
+
observer.observe(element);
|
|
906
|
+
return () => observer.disconnect();
|
|
907
|
+
}, [open, loading, filteredItems.length]);
|
|
908
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { ref, "aria-hidden": "true", "data-load-more": "", style: { blockSize: 1 } });
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
// src/combobox/ComboboxItem.tsx
|
|
912
|
+
var import_react11 = require("react");
|
|
913
|
+
function ComboboxItem({
|
|
914
|
+
value,
|
|
915
|
+
disabled = false,
|
|
916
|
+
className,
|
|
917
|
+
children,
|
|
918
|
+
render,
|
|
919
|
+
...rest
|
|
920
|
+
}) {
|
|
921
|
+
const {
|
|
922
|
+
itemToKey,
|
|
923
|
+
indexByKey,
|
|
924
|
+
selectedKeys,
|
|
925
|
+
disabledKeys,
|
|
926
|
+
highlight,
|
|
927
|
+
setHighlight,
|
|
928
|
+
select,
|
|
929
|
+
optionId
|
|
930
|
+
} = useComboboxRootContext("Item");
|
|
931
|
+
const key = itemToKey(value);
|
|
932
|
+
const index = indexByKey.get(key) ?? -1;
|
|
933
|
+
const selected = selectedKeys.has(key);
|
|
934
|
+
const highlighted = (0, import_react11.useSyncExternalStore)(
|
|
935
|
+
highlight.subscribe,
|
|
936
|
+
() => index >= 0 && highlight.get().index === index,
|
|
937
|
+
() => false
|
|
938
|
+
);
|
|
939
|
+
const elementRef = (0, import_react11.useRef)(null);
|
|
940
|
+
(0, import_react11.useLayoutEffect)(() => {
|
|
941
|
+
if (!disabled) return;
|
|
942
|
+
disabledKeys.add(key);
|
|
943
|
+
return () => {
|
|
944
|
+
disabledKeys.delete(key);
|
|
945
|
+
};
|
|
946
|
+
}, [disabled, disabledKeys, key]);
|
|
947
|
+
(0, import_react11.useEffect)(() => {
|
|
948
|
+
if (highlighted && highlight.get().reason === "keyboard") {
|
|
949
|
+
elementRef.current?.scrollIntoView?.({ block: "nearest" });
|
|
950
|
+
}
|
|
951
|
+
}, [highlighted, highlight]);
|
|
952
|
+
return useRender({
|
|
953
|
+
render,
|
|
954
|
+
defaultTagName: "div",
|
|
955
|
+
props: {
|
|
956
|
+
id: index >= 0 ? optionId(index) : void 0,
|
|
957
|
+
role: "option",
|
|
958
|
+
"aria-selected": selected,
|
|
959
|
+
"aria-disabled": disabled || void 0,
|
|
960
|
+
...comboboxItemDataAttributes({ selected, highlighted, disabled }),
|
|
961
|
+
className,
|
|
962
|
+
children,
|
|
963
|
+
ref: elementRef,
|
|
964
|
+
onMouseDown(event) {
|
|
965
|
+
event.preventDefault();
|
|
966
|
+
},
|
|
967
|
+
onPointerMove() {
|
|
968
|
+
if (!disabled && !highlighted && index >= 0) setHighlight(index, "pointer");
|
|
969
|
+
},
|
|
970
|
+
onClick() {
|
|
971
|
+
if (!disabled) select(value);
|
|
972
|
+
}
|
|
973
|
+
},
|
|
974
|
+
consumerProps: rest
|
|
975
|
+
});
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
// src/combobox/ComboboxEmpty.tsx
|
|
979
|
+
function ComboboxEmpty({ className, children, render, ...rest }) {
|
|
980
|
+
const { filteredItems, loading } = useComboboxRootContext("Empty");
|
|
981
|
+
const element = useRender({
|
|
982
|
+
render,
|
|
983
|
+
defaultTagName: "div",
|
|
984
|
+
props: { className, children },
|
|
985
|
+
consumerProps: rest
|
|
986
|
+
});
|
|
987
|
+
return filteredItems.length === 0 && !loading ? element : null;
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
// src/combobox/ComboboxStatus.tsx
|
|
991
|
+
function ComboboxStatus({ className, children, render, ...rest }) {
|
|
992
|
+
useComboboxRootContext("Status");
|
|
993
|
+
return useRender({
|
|
994
|
+
render,
|
|
995
|
+
defaultTagName: "div",
|
|
996
|
+
props: { role: "status", "aria-live": "polite", "aria-atomic": true, className, children },
|
|
997
|
+
consumerProps: rest
|
|
998
|
+
});
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
// src/combobox/ComboboxChip.tsx
|
|
1002
|
+
var import_react12 = require("react");
|
|
1003
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
1004
|
+
var ComboboxChipContext = (0, import_react12.createContext)(null);
|
|
1005
|
+
function useComboboxChipContext() {
|
|
1006
|
+
const context = (0, import_react12.useContext)(ComboboxChipContext);
|
|
1007
|
+
if (context === null) {
|
|
1008
|
+
throw new Error("<Combobox.ChipRemove> must be rendered inside <Combobox.Chip>.");
|
|
1009
|
+
}
|
|
1010
|
+
return context;
|
|
1011
|
+
}
|
|
1012
|
+
function ComboboxChip({
|
|
1013
|
+
value,
|
|
1014
|
+
className,
|
|
1015
|
+
children,
|
|
1016
|
+
render,
|
|
1017
|
+
...rest
|
|
1018
|
+
}) {
|
|
1019
|
+
const { disabled, itemToKey, remove, registerChip, chips, inputRef } = useComboboxRootContext("Chip");
|
|
1020
|
+
const key = itemToKey(value);
|
|
1021
|
+
const elementRef = (0, import_react12.useRef)(null);
|
|
1022
|
+
(0, import_react12.useLayoutEffect)(() => registerChip({ key, ref: elementRef }), [registerChip, key]);
|
|
1023
|
+
const chipContext = (0, import_react12.useMemo)(() => ({ value }), [value]);
|
|
1024
|
+
function focusAt(index) {
|
|
1025
|
+
const chip = chips[index];
|
|
1026
|
+
if (chip) chip.ref.current?.focus();
|
|
1027
|
+
else inputRef.current?.focus();
|
|
1028
|
+
}
|
|
1029
|
+
const element = useRender({
|
|
1030
|
+
render,
|
|
1031
|
+
defaultTagName: "div",
|
|
1032
|
+
props: {
|
|
1033
|
+
tabIndex: -1,
|
|
1034
|
+
"data-disabled": disabled ? "" : void 0,
|
|
1035
|
+
className,
|
|
1036
|
+
children,
|
|
1037
|
+
ref: elementRef,
|
|
1038
|
+
onKeyDown(event) {
|
|
1039
|
+
const index = chips.findIndex((chip) => chip.ref.current === elementRef.current);
|
|
1040
|
+
switch (event.key) {
|
|
1041
|
+
case "ArrowLeft":
|
|
1042
|
+
event.preventDefault();
|
|
1043
|
+
if (index > 0) focusAt(index - 1);
|
|
1044
|
+
return;
|
|
1045
|
+
case "ArrowRight":
|
|
1046
|
+
event.preventDefault();
|
|
1047
|
+
focusAt(index + 1);
|
|
1048
|
+
return;
|
|
1049
|
+
case "Backspace":
|
|
1050
|
+
case "Delete":
|
|
1051
|
+
event.preventDefault();
|
|
1052
|
+
if (disabled) return;
|
|
1053
|
+
remove(value, "chip-remove");
|
|
1054
|
+
focusAt(index + 1 < chips.length ? index + 1 : chips.length);
|
|
1055
|
+
return;
|
|
1056
|
+
case "Escape":
|
|
1057
|
+
inputRef.current?.focus();
|
|
1058
|
+
return;
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
},
|
|
1062
|
+
consumerProps: rest
|
|
1063
|
+
});
|
|
1064
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ComboboxChipContext.Provider, { value: chipContext, children: element });
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
// src/combobox/ComboboxChipRemove.tsx
|
|
1068
|
+
function ComboboxChipRemove({
|
|
1069
|
+
className,
|
|
1070
|
+
children,
|
|
1071
|
+
render,
|
|
1072
|
+
...rest
|
|
1073
|
+
}) {
|
|
1074
|
+
const { disabled, remove, inputRef } = useComboboxRootContext("ChipRemove");
|
|
1075
|
+
const { value } = useComboboxChipContext();
|
|
1076
|
+
return useRender({
|
|
1077
|
+
render,
|
|
1078
|
+
defaultTagName: "button",
|
|
1079
|
+
props: {
|
|
1080
|
+
type: "button",
|
|
1081
|
+
tabIndex: -1,
|
|
1082
|
+
disabled: disabled || void 0,
|
|
1083
|
+
className,
|
|
1084
|
+
children,
|
|
1085
|
+
onMouseDown(event) {
|
|
1086
|
+
event.preventDefault();
|
|
1087
|
+
},
|
|
1088
|
+
onClick() {
|
|
1089
|
+
if (disabled) return;
|
|
1090
|
+
remove(value, "chip-remove");
|
|
1091
|
+
inputRef.current?.focus();
|
|
1092
|
+
}
|
|
1093
|
+
},
|
|
1094
|
+
consumerProps: rest
|
|
1095
|
+
});
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
// src/combobox/ComboboxValue.tsx
|
|
1099
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
1100
|
+
function ComboboxValue({ children }) {
|
|
1101
|
+
const { multiple, selectedItems, itemToString } = useComboboxRootContext("Value");
|
|
1102
|
+
const value = multiple ? selectedItems : selectedItems[0] ?? null;
|
|
1103
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: children(value, { itemToString }) });
|
|
1104
|
+
}
|
|
1105
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1106
|
+
0 && (module.exports = {
|
|
1107
|
+
Combobox,
|
|
1108
|
+
defaultFilter
|
|
1109
|
+
});
|