@kenos-ui/react-combobox 0.1.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/index.cjs ADDED
@@ -0,0 +1,758 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var utils = require('@kenos-ui/utils');
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+
7
+ var __defProp = Object.defineProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+
13
+ // src/index.parts.ts
14
+ var index_parts_exports = {};
15
+ __export(index_parts_exports, {
16
+ Clear: () => Clear,
17
+ Content: () => Content,
18
+ Empty: () => Empty,
19
+ Input: () => Input,
20
+ Item: () => Item,
21
+ ItemText: () => ItemText,
22
+ Label: () => Label,
23
+ List: () => List,
24
+ Root: () => Root,
25
+ Trigger: () => Trigger
26
+ });
27
+ var ComboboxStore = class {
28
+ state;
29
+ listeners = /* @__PURE__ */ new Set();
30
+ constructor(initial = {}) {
31
+ this.state = {
32
+ open: false,
33
+ openSource: null,
34
+ value: null,
35
+ inputValue: "",
36
+ highlightedValue: null,
37
+ items: /* @__PURE__ */ new Map(),
38
+ ...initial
39
+ };
40
+ }
41
+ getState() {
42
+ return this.state;
43
+ }
44
+ subscribe(listener) {
45
+ this.listeners.add(listener);
46
+ return () => this.listeners.delete(listener);
47
+ }
48
+ notify() {
49
+ for (const listener of this.listeners) {
50
+ listener();
51
+ }
52
+ }
53
+ // ── Mutations ────────────────────────────────────────────────────────────
54
+ setOpen(open, source = null) {
55
+ if (this.state.open === open) return;
56
+ this.state = {
57
+ ...this.state,
58
+ open,
59
+ openSource: open ? source : null,
60
+ highlightedValue: open ? this.state.highlightedValue : null
61
+ };
62
+ this.notify();
63
+ }
64
+ setValue(value) {
65
+ if (this.state.value === value) return;
66
+ this.state = { ...this.state, value };
67
+ this.notify();
68
+ }
69
+ setInputValue(inputValue) {
70
+ if (this.state.inputValue === inputValue) return;
71
+ this.state = { ...this.state, inputValue };
72
+ this.notify();
73
+ }
74
+ clearValue() {
75
+ if (this.state.value === null && this.state.inputValue === "") return;
76
+ this.state = { ...this.state, value: null, inputValue: "" };
77
+ this.notify();
78
+ }
79
+ isSelected(value, comparator) {
80
+ const current = this.state.value;
81
+ return typeof current === "string" && comparator(current, value);
82
+ }
83
+ setHighlightedValue(value) {
84
+ if (this.state.highlightedValue === value) return;
85
+ this.state = { ...this.state, highlightedValue: value };
86
+ this.notify();
87
+ }
88
+ registerItem(record) {
89
+ const next = new Map(this.state.items);
90
+ next.set(record.value, record);
91
+ this.state = { ...this.state, items: next };
92
+ this.notify();
93
+ }
94
+ unregisterItem(value) {
95
+ if (!this.state.items.has(value)) return;
96
+ const next = new Map(this.state.items);
97
+ next.delete(value);
98
+ this.state = { ...this.state, items: next };
99
+ this.notify();
100
+ }
101
+ updateItemRef(value, ref) {
102
+ const item = this.state.items.get(value);
103
+ if (!item || item.ref === ref) return;
104
+ const next = new Map(this.state.items);
105
+ next.set(value, { ...item, ref });
106
+ this.state = { ...this.state, items: next };
107
+ }
108
+ };
109
+ function useComboboxStore(store, selector) {
110
+ return react.useSyncExternalStore(
111
+ react.useCallback((cb) => store.subscribe(cb), [store]),
112
+ () => selector(store.getState()),
113
+ () => selector(store.getState())
114
+ );
115
+ }
116
+ var ComboboxContext = react.createContext(null);
117
+ function useComboboxContext() {
118
+ const ctx = react.useContext(ComboboxContext);
119
+ if (!ctx) {
120
+ throw new Error("Combobox compound components must be rendered inside <Combobox.Root>.");
121
+ }
122
+ return ctx;
123
+ }
124
+ var defaultIsItemEqualToValue = (a, b) => a === b;
125
+ function resolveLabel(value, items, staticItems) {
126
+ if (value == null) return "";
127
+ return items.get(value)?.label ?? staticItems[value] ?? value;
128
+ }
129
+ function Root(props) {
130
+ const {
131
+ children,
132
+ value,
133
+ defaultValue,
134
+ onValueChange,
135
+ inputValue,
136
+ defaultInputValue,
137
+ onInputValueChange,
138
+ open,
139
+ defaultOpen,
140
+ onOpenChange,
141
+ onOpenChangeComplete,
142
+ disabled = false,
143
+ required = false,
144
+ readOnly = false,
145
+ modal = false,
146
+ id,
147
+ items = {},
148
+ isItemEqualToValue = defaultIsItemEqualToValue,
149
+ filter
150
+ } = props;
151
+ const uid = react.useId();
152
+ const prefix = id ?? `cbx-${uid.replace(/:/g, "")}`;
153
+ const ids = react.useMemo(
154
+ () => ({
155
+ root: prefix,
156
+ label: `${prefix}-label`,
157
+ input: `${prefix}-input`,
158
+ trigger: `${prefix}-trigger`,
159
+ content: `${prefix}-content`
160
+ }),
161
+ [prefix]
162
+ );
163
+ const inputRef = react.useRef(null);
164
+ const triggerRef = react.useRef(null);
165
+ const contentRef = react.useRef(null);
166
+ const listRef = react.useRef(null);
167
+ const isControlledValue = value !== void 0;
168
+ const isControlledOpen = open !== void 0;
169
+ const isControlledInputValue = inputValue !== void 0;
170
+ const initialValue = isControlledValue ? value ?? null : defaultValue ?? null;
171
+ const initialInputValue = isControlledInputValue ? inputValue ?? "" : defaultInputValue ?? resolveLabel(initialValue, /* @__PURE__ */ new Map(), items);
172
+ const storeRef = react.useRef(null);
173
+ if (!storeRef.current) {
174
+ storeRef.current = new ComboboxStore({
175
+ value: initialValue,
176
+ inputValue: initialInputValue,
177
+ open: isControlledOpen ? open ?? false : defaultOpen ?? false
178
+ });
179
+ }
180
+ const store = storeRef.current;
181
+ const prevControlledValue = react.useRef(value);
182
+ react.useEffect(() => {
183
+ if (!isControlledValue) return;
184
+ if (value === prevControlledValue.current) return;
185
+ prevControlledValue.current = value;
186
+ store.setValue(typeof value === "string" ? value : null);
187
+ }, [isControlledValue, value, store]);
188
+ const prevControlledInputValue = react.useRef(inputValue);
189
+ react.useEffect(() => {
190
+ if (!isControlledInputValue) return;
191
+ if (inputValue === prevControlledInputValue.current) return;
192
+ prevControlledInputValue.current = inputValue;
193
+ store.setInputValue(inputValue ?? "");
194
+ }, [isControlledInputValue, inputValue, store]);
195
+ const prevControlledOpen = react.useRef(open);
196
+ react.useEffect(() => {
197
+ if (!isControlledOpen) return;
198
+ if (open === prevControlledOpen.current) return;
199
+ prevControlledOpen.current = open;
200
+ store.setOpen(open ?? false);
201
+ }, [isControlledOpen, open, store]);
202
+ const onValueChangeRef = react.useRef(onValueChange);
203
+ onValueChangeRef.current = onValueChange;
204
+ const prevStoreValue = react.useRef(store.getState().value);
205
+ react.useEffect(() => {
206
+ return store.subscribe(() => {
207
+ const state = store.getState();
208
+ if (state.value !== prevStoreValue.current) {
209
+ prevStoreValue.current = state.value;
210
+ onValueChangeRef.current?.(
211
+ typeof state.value === "string" ? state.value : null
212
+ );
213
+ }
214
+ });
215
+ }, [store]);
216
+ const onInputValueChangeRef = react.useRef(onInputValueChange);
217
+ onInputValueChangeRef.current = onInputValueChange;
218
+ const prevStoreInputValue = react.useRef(store.getState().inputValue);
219
+ react.useEffect(() => {
220
+ return store.subscribe(() => {
221
+ const state = store.getState();
222
+ if (state.inputValue !== prevStoreInputValue.current) {
223
+ prevStoreInputValue.current = state.inputValue;
224
+ onInputValueChangeRef.current?.(state.inputValue);
225
+ }
226
+ });
227
+ }, [store]);
228
+ const onOpenChangeRef = react.useRef(onOpenChange);
229
+ onOpenChangeRef.current = onOpenChange;
230
+ const prevStoreOpen = react.useRef(store.getState().open);
231
+ react.useEffect(() => {
232
+ return store.subscribe(() => {
233
+ const state = store.getState();
234
+ if (state.open !== prevStoreOpen.current) {
235
+ prevStoreOpen.current = state.open;
236
+ onOpenChangeRef.current?.(state.open);
237
+ }
238
+ });
239
+ }, [store]);
240
+ const close = react.useCallback(() => {
241
+ const state = store.getState();
242
+ if (!state.open) return;
243
+ store.setOpen(false);
244
+ utils.restoreFocus({
245
+ openSource: state.openSource === "trigger" ? "trigger" : "input",
246
+ trigger: inputRef.current ?? triggerRef.current
247
+ });
248
+ }, [store]);
249
+ const selectValue = react.useCallback(
250
+ (itemValue) => {
251
+ const item = store.getState().items.get(itemValue);
252
+ const label = item?.label ?? items[itemValue] ?? itemValue;
253
+ store.setValue(itemValue);
254
+ store.setInputValue(label);
255
+ close();
256
+ },
257
+ [store, close, items]
258
+ );
259
+ const clearValue = react.useCallback(() => {
260
+ store.clearValue();
261
+ }, [store]);
262
+ const config = react.useMemo(
263
+ () => ({
264
+ disabled,
265
+ required,
266
+ readOnly,
267
+ modal,
268
+ items,
269
+ isItemEqualToValue,
270
+ filter: filter ?? ((item, query) => {
271
+ const normalized = query.trim().toLowerCase();
272
+ if (!normalized) return true;
273
+ return item.textValue.toLowerCase().includes(normalized) || item.label.toLowerCase().includes(normalized);
274
+ })
275
+ }),
276
+ [disabled, required, readOnly, modal, items, isItemEqualToValue, filter]
277
+ );
278
+ const ctx = react.useMemo(
279
+ () => ({
280
+ store,
281
+ ids,
282
+ refs: { inputRef, triggerRef, contentRef, listRef },
283
+ config,
284
+ isControlledValue,
285
+ isControlledOpen,
286
+ isControlledInputValue,
287
+ onOpenChangeComplete,
288
+ close,
289
+ selectValue,
290
+ clearValue
291
+ }),
292
+ [
293
+ store,
294
+ ids,
295
+ config,
296
+ isControlledValue,
297
+ isControlledOpen,
298
+ isControlledInputValue,
299
+ onOpenChangeComplete,
300
+ close,
301
+ selectValue,
302
+ clearValue
303
+ ]
304
+ );
305
+ return /* @__PURE__ */ jsxRuntime.jsx(ComboboxContext.Provider, { value: ctx, children });
306
+ }
307
+ function Label({ children, ...props }) {
308
+ const { ids } = useComboboxContext();
309
+ return /* @__PURE__ */ jsxRuntime.jsx("label", { id: ids.label, htmlFor: ids.input, ...props, children });
310
+ }
311
+ function Input({
312
+ onChange,
313
+ onFocus,
314
+ onKeyDown,
315
+ disabled,
316
+ readOnly,
317
+ ...props
318
+ }) {
319
+ const { store, ids, refs, config, close, selectValue } = useComboboxContext();
320
+ const open = useComboboxStore(store, (s) => s.open);
321
+ const inputValue = useComboboxStore(store, (s) => s.inputValue);
322
+ const highlightedValue = useComboboxStore(store, (s) => s.highlightedValue);
323
+ const items = useComboboxStore(store, (s) => s.items);
324
+ const isDisabled = disabled ?? config.disabled;
325
+ const isReadOnly = readOnly ?? config.readOnly;
326
+ const filteredItems = Array.from(items.values()).filter((item) => config.filter(item, inputValue)).map((item) => ({
327
+ value: item.value,
328
+ disabled: item.disabled
329
+ }));
330
+ const { onKeyDown: onNavKeyDown } = utils.useListNavigation({
331
+ enabled: open && !isDisabled && !isReadOnly,
332
+ items: filteredItems,
333
+ highlightedValue,
334
+ onHighlight: (v) => store.setHighlightedValue(v),
335
+ loop: true
336
+ });
337
+ const activeDescendantId = open && highlightedValue != null ? `${ids.content}-opt-${highlightedValue}` : void 0;
338
+ const handleKeyDown = react.useCallback(
339
+ (e) => {
340
+ if (e.key === "ArrowDown" || e.key === "ArrowUp") {
341
+ if (!open) {
342
+ store.setOpen(true, "input");
343
+ }
344
+ onNavKeyDown(e);
345
+ onKeyDown?.(e);
346
+ return;
347
+ }
348
+ if (e.key === "Enter" && open && highlightedValue != null) {
349
+ const item = items.get(highlightedValue);
350
+ if (item && !item.disabled) {
351
+ e.preventDefault();
352
+ selectValue(highlightedValue);
353
+ }
354
+ onKeyDown?.(e);
355
+ return;
356
+ }
357
+ if (e.key === "Escape" && open) {
358
+ e.preventDefault();
359
+ e.stopPropagation();
360
+ close();
361
+ onKeyDown?.(e);
362
+ return;
363
+ }
364
+ onKeyDown?.(e);
365
+ },
366
+ [
367
+ open,
368
+ highlightedValue,
369
+ items,
370
+ store,
371
+ onNavKeyDown,
372
+ selectValue,
373
+ close,
374
+ onKeyDown
375
+ ]
376
+ );
377
+ return /* @__PURE__ */ jsxRuntime.jsx(
378
+ "input",
379
+ {
380
+ ref: refs.inputRef,
381
+ type: "text",
382
+ id: ids.input,
383
+ role: "combobox",
384
+ "aria-haspopup": "listbox",
385
+ "aria-expanded": open,
386
+ "aria-controls": open ? ids.content : void 0,
387
+ "aria-autocomplete": "list",
388
+ "aria-labelledby": ids.label ? `${ids.label} ${ids.input}` : void 0,
389
+ "aria-activedescendant": activeDescendantId,
390
+ "aria-disabled": isDisabled || isReadOnly || void 0,
391
+ autoComplete: "off",
392
+ "data-disabled": isDisabled || isReadOnly ? "true" : void 0,
393
+ "data-open": open ? "true" : void 0,
394
+ "data-state": open ? "open" : "closed",
395
+ disabled: isDisabled,
396
+ readOnly: isReadOnly,
397
+ value: inputValue,
398
+ onChange: (e) => {
399
+ if (isDisabled || isReadOnly) return;
400
+ store.setInputValue(e.target.value);
401
+ store.setOpen(true, "input");
402
+ onChange?.(e);
403
+ },
404
+ onFocus: (e) => {
405
+ if (!isDisabled && !isReadOnly) {
406
+ store.setOpen(true, "input");
407
+ }
408
+ onFocus?.(e);
409
+ },
410
+ onKeyDown: handleKeyDown,
411
+ ...props
412
+ }
413
+ );
414
+ }
415
+ function Trigger({ children, onClick, disabled, ...props }) {
416
+ const { store, ids, refs, config } = useComboboxContext();
417
+ const open = useComboboxStore(store, (s) => s.open);
418
+ const isDisabled = disabled ?? config.disabled;
419
+ const isReadOnly = config.readOnly;
420
+ return /* @__PURE__ */ jsxRuntime.jsx(
421
+ "button",
422
+ {
423
+ ref: refs.triggerRef,
424
+ type: "button",
425
+ id: ids.trigger,
426
+ "aria-haspopup": "listbox",
427
+ "aria-expanded": open,
428
+ "aria-controls": open ? ids.content : void 0,
429
+ "aria-label": "Toggle suggestions",
430
+ "aria-disabled": isDisabled || isReadOnly || void 0,
431
+ "data-disabled": isDisabled || isReadOnly ? "true" : void 0,
432
+ "data-open": open ? "true" : void 0,
433
+ "data-state": open ? "open" : "closed",
434
+ disabled: isDisabled || isReadOnly,
435
+ tabIndex: -1,
436
+ onClick: (e) => {
437
+ if (isDisabled || isReadOnly) return;
438
+ store.setOpen(!open, "trigger");
439
+ refs.inputRef.current?.focus();
440
+ onClick?.(e);
441
+ },
442
+ ...props,
443
+ children
444
+ }
445
+ );
446
+ }
447
+ var ComboboxCollectionContext = react.createContext(
448
+ null
449
+ );
450
+ function useComboboxCollection() {
451
+ const ctx = react.useContext(ComboboxCollectionContext);
452
+ if (!ctx) {
453
+ throw new Error(
454
+ "Combobox collection context is missing. Render inside <Combobox.Content>."
455
+ );
456
+ }
457
+ return ctx;
458
+ }
459
+ function Content({
460
+ children,
461
+ forceMount,
462
+ side = "bottom",
463
+ align = "start",
464
+ sideOffset = 4,
465
+ alignOffset = 0,
466
+ avoidCollisions = true,
467
+ collisionPadding = 8,
468
+ sameWidth = false,
469
+ lazyMount = true,
470
+ unmountOnExit = false,
471
+ onOpenChangeComplete: onOpenChangeCompleteProp,
472
+ style,
473
+ onKeyDown,
474
+ ...props
475
+ }) {
476
+ const {
477
+ store,
478
+ ids,
479
+ refs,
480
+ config,
481
+ close,
482
+ selectValue,
483
+ onOpenChangeComplete: onOpenChangeCompleteRoot
484
+ } = useComboboxContext();
485
+ const open = useComboboxStore(store, (s) => s.open);
486
+ const highlightedValue = useComboboxStore(store, (s) => s.highlightedValue);
487
+ const allItems = useComboboxStore(store, (s) => s.items);
488
+ const inputValue = useComboboxStore(store, (s) => s.inputValue);
489
+ const onOpenChangeComplete = onOpenChangeCompleteProp ?? onOpenChangeCompleteRoot;
490
+ const collection = utils.useSelectCollection({
491
+ items: allItems,
492
+ inputValue,
493
+ filter: config.filter
494
+ });
495
+ const collectionValue = react.useMemo(
496
+ () => ({
497
+ filteredItems: collection.items,
498
+ enabledItems: collection.enabledItems,
499
+ isEmpty: collection.isEmpty
500
+ }),
501
+ [collection.items, collection.enabledItems, collection.isEmpty]
502
+ );
503
+ const navItems = collection.enabledItems.map((item) => ({
504
+ value: item.value,
505
+ disabled: item.disabled
506
+ }));
507
+ const { setReference, setFloating, floatingStyles, isPositioned } = utils.useFloating({
508
+ open,
509
+ side,
510
+ align,
511
+ sideOffset,
512
+ alignOffset,
513
+ avoidCollisions,
514
+ collisionPadding,
515
+ sameWidth
516
+ });
517
+ react.useLayoutEffect(() => {
518
+ if (!open) return;
519
+ setReference(refs.inputRef.current);
520
+ }, [open, refs.inputRef, setReference]);
521
+ const mergedRef = react.useCallback(
522
+ (node) => {
523
+ refs.contentRef.current = node;
524
+ setFloating(node);
525
+ },
526
+ [refs.contentRef, setFloating]
527
+ );
528
+ const { present } = utils.usePresence({
529
+ open,
530
+ lazyMount,
531
+ unmountOnExit,
532
+ onOpenChangeComplete
533
+ });
534
+ utils.useClickOutside([refs.contentRef, refs.inputRef, refs.triggerRef], close, open);
535
+ utils.useEscapeKey({
536
+ enabled: open,
537
+ stopPropagation: true,
538
+ onEscape: close
539
+ });
540
+ utils.useFocusTrap(refs.contentRef, open && config.modal);
541
+ const { onKeyDown: onNavKeyDown } = utils.useListNavigation({
542
+ enabled: open && !config.disabled && !config.readOnly,
543
+ items: navItems,
544
+ highlightedValue,
545
+ onHighlight: (v) => store.setHighlightedValue(v),
546
+ loop: true
547
+ });
548
+ const handleKeyDown = react.useCallback(
549
+ (e) => {
550
+ if (e.key === "Enter" || e.key === " ") {
551
+ if (highlightedValue != null) {
552
+ e.preventDefault();
553
+ const item = allItems.get(highlightedValue);
554
+ if (item && !item.disabled) {
555
+ selectValue(highlightedValue);
556
+ }
557
+ }
558
+ return;
559
+ }
560
+ onNavKeyDown(e);
561
+ onKeyDown?.(e);
562
+ },
563
+ [highlightedValue, allItems, selectValue, onNavKeyDown, onKeyDown]
564
+ );
565
+ const [transitionsReady, setTransitionsReady] = react.useState(false);
566
+ react.useEffect(() => {
567
+ if (!open || !isPositioned) {
568
+ setTransitionsReady(false);
569
+ return;
570
+ }
571
+ const raf = requestAnimationFrame(() => setTransitionsReady(true));
572
+ return () => cancelAnimationFrame(raf);
573
+ }, [open, isPositioned]);
574
+ react.useEffect(() => {
575
+ if (!open) return;
576
+ const state = store.getState();
577
+ if (state.highlightedValue == null) {
578
+ const first = navItems.find((i) => !i.disabled);
579
+ if (first) store.setHighlightedValue(first.value);
580
+ }
581
+ }, [open, inputValue, navItems.length]);
582
+ react.useEffect(() => {
583
+ if (!open || !highlightedValue) return;
584
+ const item = allItems.get(highlightedValue);
585
+ if (typeof item?.ref?.scrollIntoView === "function") {
586
+ item.ref.scrollIntoView({ block: "nearest" });
587
+ }
588
+ }, [highlightedValue, allItems, open]);
589
+ if (!present && !forceMount) return null;
590
+ return /* @__PURE__ */ jsxRuntime.jsx(ComboboxCollectionContext.Provider, { value: collectionValue, children: /* @__PURE__ */ jsxRuntime.jsx(
591
+ "div",
592
+ {
593
+ ref: mergedRef,
594
+ id: ids.content,
595
+ "data-state": open ? "open" : "closed",
596
+ "data-open": open ? "true" : void 0,
597
+ "data-empty": collection.isEmpty ? "true" : void 0,
598
+ "aria-modal": config.modal ? "true" : void 0,
599
+ tabIndex: -1,
600
+ style: {
601
+ ...floatingStyles,
602
+ ...!open ? { display: "none" } : void 0,
603
+ ...open && !isPositioned ? { opacity: 0, pointerEvents: "none" } : void 0,
604
+ ...open && !transitionsReady ? { transition: "none" } : void 0,
605
+ ...style
606
+ },
607
+ onKeyDown: handleKeyDown,
608
+ ...props,
609
+ children
610
+ }
611
+ ) });
612
+ }
613
+ function List({ children, ...props }) {
614
+ const { ids, refs } = useComboboxContext();
615
+ return /* @__PURE__ */ jsxRuntime.jsx(
616
+ "ul",
617
+ {
618
+ ref: refs.listRef,
619
+ role: "listbox",
620
+ id: `${ids.content}-list`,
621
+ "aria-labelledby": ids.label,
622
+ ...props,
623
+ children
624
+ }
625
+ );
626
+ }
627
+ Item.displayName = "Combobox.Item";
628
+ function Item({
629
+ value,
630
+ disabled = false,
631
+ textValue,
632
+ children,
633
+ onClick,
634
+ onPointerMove,
635
+ style,
636
+ ...props
637
+ }) {
638
+ const { store, ids, config, selectValue } = useComboboxContext();
639
+ const selectedValue = useComboboxStore(store, (s) => s.value);
640
+ const highlightedValue = useComboboxStore(store, (s) => s.highlightedValue);
641
+ const { filteredItems } = useComboboxCollection();
642
+ const liRef = react.useRef(null);
643
+ const isVisible = filteredItems.some((item) => item.value === value);
644
+ const isSelected = typeof selectedValue === "string" && config.isItemEqualToValue(selectedValue, value);
645
+ const isHighlighted = highlightedValue === value;
646
+ const isDisabled = disabled || config.disabled || config.readOnly;
647
+ react.useLayoutEffect(() => {
648
+ const el = liRef.current;
649
+ const label = textValue ?? (el?.textContent ?? value);
650
+ store.registerItem({
651
+ value,
652
+ label,
653
+ textValue: textValue ?? label,
654
+ disabled: isDisabled,
655
+ ref: el
656
+ });
657
+ return () => store.unregisterItem(value);
658
+ }, [value, isDisabled, store, textValue, children]);
659
+ react.useLayoutEffect(() => {
660
+ store.updateItemRef(value, liRef.current);
661
+ });
662
+ const handleClick = react.useCallback(
663
+ (e) => {
664
+ if (isDisabled) return;
665
+ selectValue(value);
666
+ onClick?.(e);
667
+ },
668
+ [isDisabled, selectValue, value, onClick]
669
+ );
670
+ const handlePointerMove = react.useCallback(
671
+ (e) => {
672
+ if (!isDisabled) store.setHighlightedValue(value);
673
+ onPointerMove?.(e);
674
+ },
675
+ [isDisabled, store, value, onPointerMove]
676
+ );
677
+ return /* @__PURE__ */ jsxRuntime.jsx(
678
+ "li",
679
+ {
680
+ ref: liRef,
681
+ id: `${ids.content}-opt-${value}`,
682
+ role: "option",
683
+ "aria-selected": isSelected,
684
+ "aria-disabled": isDisabled || void 0,
685
+ "aria-hidden": !isVisible ? true : void 0,
686
+ "data-highlighted": isHighlighted ? "true" : void 0,
687
+ "data-selected": isSelected ? "true" : void 0,
688
+ "data-disabled": isDisabled ? "true" : void 0,
689
+ "data-hidden": !isVisible ? "true" : void 0,
690
+ tabIndex: -1,
691
+ style: {
692
+ ...style,
693
+ display: isVisible ? style?.display : "none"
694
+ },
695
+ onClick: handleClick,
696
+ onPointerMove: handlePointerMove,
697
+ ...props,
698
+ children
699
+ }
700
+ );
701
+ }
702
+ function ItemText({ children, ...props }) {
703
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { ...props, children });
704
+ }
705
+ function Empty({ children = "No results found", ...props }) {
706
+ const { store } = useComboboxContext();
707
+ const open = useComboboxStore(store, (s) => s.open);
708
+ const { isEmpty } = useComboboxCollection();
709
+ if (!open || !isEmpty) {
710
+ return null;
711
+ }
712
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "status", "aria-live": "polite", "data-empty": "true", ...props, children });
713
+ }
714
+ function Clear({ onClick, ...props }) {
715
+ const { store, config, clearValue, refs } = useComboboxContext();
716
+ const value = useComboboxStore(store, (s) => s.value);
717
+ const inputValue = useComboboxStore(store, (s) => s.inputValue);
718
+ const hasValue = value != null || inputValue.length > 0;
719
+ const handleActivate = react.useCallback(
720
+ (e) => {
721
+ e.preventDefault();
722
+ e.stopPropagation();
723
+ clearValue();
724
+ refs.inputRef.current?.focus();
725
+ onClick?.(e);
726
+ },
727
+ [clearValue, onClick, refs.inputRef]
728
+ );
729
+ const handleKeyDown = react.useCallback(
730
+ (e) => {
731
+ if (e.key === "Enter" || e.key === " ") {
732
+ handleActivate(e);
733
+ }
734
+ },
735
+ [handleActivate]
736
+ );
737
+ if (!hasValue || config.disabled || config.readOnly) {
738
+ return null;
739
+ }
740
+ return /* @__PURE__ */ jsxRuntime.jsx(
741
+ "span",
742
+ {
743
+ role: "button",
744
+ tabIndex: 0,
745
+ "aria-label": "Clear",
746
+ onClick: handleActivate,
747
+ onKeyDown: handleKeyDown,
748
+ ...props
749
+ }
750
+ );
751
+ }
752
+
753
+ exports.Combobox = index_parts_exports;
754
+ exports.ComboboxStore = ComboboxStore;
755
+ exports.useComboboxContext = useComboboxContext;
756
+ exports.useComboboxStore = useComboboxStore;
757
+ //# sourceMappingURL=index.cjs.map
758
+ //# sourceMappingURL=index.cjs.map