@strapi/ui-primitives 1.19.0-typescript.0 → 2.0.0-beta.1

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.mjs ADDED
@@ -0,0 +1,2172 @@
1
+ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { useComposedRefs } from "@radix-ui/react-compose-refs";
4
+ import { createContextScope, createContext } from "@radix-ui/react-context";
5
+ import { Slot } from "@radix-ui/react-slot";
6
+ import { composeEventHandlers } from "@radix-ui/primitive";
7
+ import { composeEventHandlers as composeEventHandlers2 } from "@radix-ui/primitive";
8
+ import { DismissableLayer } from "@radix-ui/react-dismissable-layer";
9
+ import { useFocusGuards } from "@radix-ui/react-focus-guards";
10
+ import { FocusScope } from "@radix-ui/react-focus-scope";
11
+ import { useId } from "@radix-ui/react-id";
12
+ import * as PopperPrimitive from "@radix-ui/react-popper";
13
+ import { createPopperScope } from "@radix-ui/react-popper";
14
+ import { Portal as Portal$2 } from "@radix-ui/react-portal";
15
+ import { Primitive } from "@radix-ui/react-primitive";
16
+ import { useControllableState } from "@radix-ui/react-use-controllable-state";
17
+ import { useLayoutEffect } from "@radix-ui/react-use-layout-effect";
18
+ import { hideOthers } from "aria-hidden";
19
+ import * as ReactDOM from "react-dom";
20
+ import { RemoveScroll } from "react-remove-scroll";
21
+ import { clamp } from "@radix-ui/number";
22
+ import { createCollection as createCollection$1 } from "@radix-ui/react-collection";
23
+ import { useDirection } from "@radix-ui/react-direction";
24
+ import { usePrevious } from "@radix-ui/react-use-previous";
25
+ import { VisuallyHidden } from "@radix-ui/react-visually-hidden";
26
+ import { useCallbackRef } from "@radix-ui/react-use-callback-ref";
27
+ import { useCallbackRef as useCallbackRef2 } from "@radix-ui/react-use-callback-ref";
28
+ function createCollection(name) {
29
+ const PROVIDER_NAME = `${name}CollectionProvider`;
30
+ const [createCollectionContext, createCollectionScope2] = createContextScope(PROVIDER_NAME);
31
+ const [CollectionProviderImpl, useCollectionContext] = createCollectionContext(PROVIDER_NAME, {
32
+ collectionRef: { current: null },
33
+ itemMap: /* @__PURE__ */ new Map(),
34
+ listeners: /* @__PURE__ */ new Set()
35
+ });
36
+ const CollectionProvider = (props) => {
37
+ const { scope, children } = props;
38
+ const ref = React.useRef(null);
39
+ const itemMap = React.useRef(/* @__PURE__ */ new Map()).current;
40
+ const listeners = React.useRef(/* @__PURE__ */ new Set()).current;
41
+ return /* @__PURE__ */ jsx(CollectionProviderImpl, { scope, itemMap, collectionRef: ref, listeners, children });
42
+ };
43
+ CollectionProvider.displayName = PROVIDER_NAME;
44
+ const COLLECTION_SLOT_NAME = `${name}CollectionSlot`;
45
+ const CollectionSlot = React.forwardRef((props, forwardedRef) => {
46
+ const { scope, children } = props;
47
+ const context = useCollectionContext(COLLECTION_SLOT_NAME, scope);
48
+ const composedRefs = useComposedRefs(forwardedRef, context.collectionRef);
49
+ return /* @__PURE__ */ jsx(Slot, { ref: composedRefs, children });
50
+ });
51
+ CollectionSlot.displayName = COLLECTION_SLOT_NAME;
52
+ const ITEM_SLOT_NAME = `${name}CollectionItemSlot`;
53
+ const ITEM_DATA_ATTR = "data-radix-collection-item";
54
+ const CollectionItemSlot = React.forwardRef((props, forwardedRef) => {
55
+ const { scope, children, ...itemData } = props;
56
+ const ref = React.useRef(null);
57
+ const composedRefs = useComposedRefs(forwardedRef, ref);
58
+ const context = useCollectionContext(ITEM_SLOT_NAME, scope);
59
+ React.useEffect(() => {
60
+ const previousMap = Array.from(context.itemMap.values());
61
+ context.itemMap.set(ref, { ref, ...itemData });
62
+ context.listeners.forEach((listener) => listener(Array.from(context.itemMap.values()), previousMap));
63
+ return () => {
64
+ const previousMap2 = Array.from(context.itemMap.values());
65
+ context.itemMap.delete(ref);
66
+ context.listeners.forEach((listener) => listener(Array.from(context.itemMap.values()), previousMap2));
67
+ };
68
+ });
69
+ return /* @__PURE__ */ jsx(Slot, { ...{ [ITEM_DATA_ATTR]: "" }, ref: composedRefs, children });
70
+ });
71
+ CollectionItemSlot.displayName = ITEM_SLOT_NAME;
72
+ function useCollection2(scope) {
73
+ const context = useCollectionContext(`${name}CollectionConsumer`, scope);
74
+ const getItems = React.useCallback(() => {
75
+ const collectionNode = context.collectionRef.current;
76
+ if (!collectionNode)
77
+ return [];
78
+ const orderedNodes = Array.from(collectionNode.querySelectorAll(`[${ITEM_DATA_ATTR}]`));
79
+ const items = Array.from(context.itemMap.values());
80
+ const orderedItems = items.sort(
81
+ (a, b) => orderedNodes.indexOf(a.ref.current) - orderedNodes.indexOf(b.ref.current)
82
+ );
83
+ return orderedItems;
84
+ }, [context.collectionRef, context.itemMap]);
85
+ const subscribe = React.useCallback(
86
+ (listener) => {
87
+ context.listeners.add(listener);
88
+ return () => context.listeners.delete(listener);
89
+ },
90
+ [context.listeners]
91
+ );
92
+ return { getItems, subscribe };
93
+ }
94
+ return [
95
+ { Provider: CollectionProvider, Slot: CollectionSlot, ItemSlot: CollectionItemSlot },
96
+ useCollection2,
97
+ createCollectionScope2
98
+ ];
99
+ }
100
+ const cache = /* @__PURE__ */ new Map();
101
+ function useCollator(locale, options) {
102
+ const cacheKey = locale + (options ? Object.entries(options).sort((a, b) => a[0] < b[0] ? -1 : 1).join() : "");
103
+ if (cache.has(cacheKey)) {
104
+ return cache.get(cacheKey);
105
+ }
106
+ const formatter = new Intl.Collator(locale, options);
107
+ cache.set(cacheKey, formatter);
108
+ return formatter;
109
+ }
110
+ function useFilter(locale, options) {
111
+ const collator = useCollator(locale, {
112
+ usage: "search",
113
+ ...options
114
+ });
115
+ return {
116
+ startsWith(string, substring) {
117
+ if (substring.length === 0) {
118
+ return true;
119
+ }
120
+ string = string.normalize("NFC");
121
+ substring = substring.normalize("NFC");
122
+ return collator.compare(string.slice(0, substring.length), substring) === 0;
123
+ },
124
+ endsWith(string, substring) {
125
+ if (substring.length === 0) {
126
+ return true;
127
+ }
128
+ string = string.normalize("NFC");
129
+ substring = substring.normalize("NFC");
130
+ return collator.compare(string.slice(-substring.length), substring) === 0;
131
+ },
132
+ contains(string, substring) {
133
+ if (substring.length === 0) {
134
+ return true;
135
+ }
136
+ string = string.normalize("NFC");
137
+ substring = substring.normalize("NFC");
138
+ let scan = 0;
139
+ const sliceLen = substring.length;
140
+ for (; scan + sliceLen <= string.length; scan++) {
141
+ const slice = string.slice(scan, scan + sliceLen);
142
+ if (collator.compare(substring, slice) === 0) {
143
+ return true;
144
+ }
145
+ }
146
+ return false;
147
+ }
148
+ };
149
+ }
150
+ const usePrev = (value) => {
151
+ const ref = React.useRef();
152
+ React.useEffect(() => {
153
+ ref.current = value;
154
+ });
155
+ return ref.current;
156
+ };
157
+ const OPEN_KEYS$1 = [" ", "Enter", "ArrowUp", "ArrowDown"];
158
+ const SELECTION_KEYS$1 = ["Enter"];
159
+ const defaultIsPrintableCharacter = (str) => {
160
+ return Boolean(str.length === 1 && str.match(/\S| /));
161
+ };
162
+ const COMBOBOX_NAME = "Combobox";
163
+ const [Collection$1, useCollection$1] = createCollection(COMBOBOX_NAME);
164
+ const [ComboboxProvider, useComboboxContext] = createContext(COMBOBOX_NAME);
165
+ const ComboboxProviders = ({ children }) => /* @__PURE__ */ jsx(PopperPrimitive.Root, { children: /* @__PURE__ */ jsx(Collection$1.Provider, { scope: void 0, children }) });
166
+ const formatAutocomplete = (autocomplete) => {
167
+ if (typeof autocomplete === "string") {
168
+ if (autocomplete === "none") {
169
+ return {
170
+ type: autocomplete,
171
+ filter: void 0
172
+ };
173
+ }
174
+ return {
175
+ type: autocomplete,
176
+ filter: "startsWith"
177
+ };
178
+ }
179
+ return autocomplete;
180
+ };
181
+ const Combobox = (props) => {
182
+ const {
183
+ allowCustomValue = false,
184
+ autocomplete = "none",
185
+ children,
186
+ open: openProp,
187
+ defaultOpen,
188
+ onOpenChange,
189
+ value: valueProp,
190
+ defaultValue,
191
+ onValueChange,
192
+ disabled,
193
+ required = false,
194
+ locale = "en-EN",
195
+ onTextValueChange,
196
+ textValue: textValueProp,
197
+ defaultTextValue,
198
+ filterValue: filterValueProp,
199
+ defaultFilterValue,
200
+ onFilterValueChange,
201
+ isPrintableCharacter = defaultIsPrintableCharacter
202
+ } = props;
203
+ const [trigger, setTrigger] = React.useState(null);
204
+ const [viewport, setViewport] = React.useState(null);
205
+ const [content, setContent] = React.useState(null);
206
+ const [visuallyFocussedItem, setVisuallyFocussedItem] = React.useState(null);
207
+ const [open = false, setOpen] = useControllableState({
208
+ prop: openProp,
209
+ defaultProp: defaultOpen,
210
+ onChange: onOpenChange
211
+ });
212
+ const [value, setValue] = useControllableState({
213
+ prop: valueProp,
214
+ defaultProp: defaultValue,
215
+ onChange: onValueChange
216
+ });
217
+ const [textValue, setTextValue] = useControllableState({
218
+ prop: textValueProp,
219
+ defaultProp: allowCustomValue && !defaultTextValue ? valueProp : defaultTextValue,
220
+ onChange: onTextValueChange
221
+ });
222
+ const [filterValue, setFilterValue] = useControllableState({
223
+ prop: filterValueProp,
224
+ defaultProp: defaultFilterValue,
225
+ onChange: onFilterValueChange
226
+ });
227
+ const id = useId();
228
+ const focusFirst = React.useCallback(
229
+ (candidates, items) => {
230
+ const allItems = items.map((item) => item.ref.current);
231
+ const [firstItem, ...restItems] = allItems;
232
+ const [lastItem] = restItems.slice(-1);
233
+ const PREVIOUSLY_FOCUSED_ELEMENT = visuallyFocussedItem ?? items.find((item) => item.value === value)?.ref.current;
234
+ for (const candidate of candidates) {
235
+ if (candidate === PREVIOUSLY_FOCUSED_ELEMENT)
236
+ return;
237
+ candidate?.scrollIntoView({ block: "nearest" });
238
+ if (candidate === firstItem && viewport)
239
+ viewport.scrollTop = 0;
240
+ if (candidate === lastItem && viewport)
241
+ viewport.scrollTop = viewport.scrollHeight;
242
+ setVisuallyFocussedItem(candidate);
243
+ if (autocomplete === "both") {
244
+ const item = items.find((item2) => item2.ref.current === candidate);
245
+ if (item) {
246
+ setTextValue(item.textValue);
247
+ }
248
+ }
249
+ if (candidate !== PREVIOUSLY_FOCUSED_ELEMENT)
250
+ return;
251
+ }
252
+ },
253
+ [autocomplete, setTextValue, viewport, visuallyFocussedItem, value]
254
+ );
255
+ const autocompleteObject = formatAutocomplete(autocomplete);
256
+ React.useEffect(() => {
257
+ if (autocomplete !== "both") {
258
+ setVisuallyFocussedItem(null);
259
+ }
260
+ }, [textValue, autocomplete]);
261
+ React.useEffect(() => {
262
+ if (content && trigger)
263
+ return hideOthers([content, trigger]);
264
+ }, [content, trigger]);
265
+ return /* @__PURE__ */ jsx(ComboboxProviders, { children: /* @__PURE__ */ jsx(
266
+ ComboboxProvider,
267
+ {
268
+ allowCustomValue,
269
+ autocomplete: autocompleteObject,
270
+ required,
271
+ trigger,
272
+ onTriggerChange: setTrigger,
273
+ contentId: id,
274
+ value,
275
+ onValueChange: setValue,
276
+ open,
277
+ onOpenChange: setOpen,
278
+ disabled,
279
+ locale,
280
+ focusFirst,
281
+ textValue,
282
+ onTextValueChange: setTextValue,
283
+ onViewportChange: setViewport,
284
+ onContentChange: setContent,
285
+ visuallyFocussedItem,
286
+ filterValue,
287
+ onFilterValueChange: setFilterValue,
288
+ onVisuallyFocussedItemChange: setVisuallyFocussedItem,
289
+ isPrintableCharacter,
290
+ children
291
+ }
292
+ ) });
293
+ };
294
+ const TRIGGER_NAME$1 = "ComboboxTrigger";
295
+ const ComboboxTrigger = React.forwardRef((props, forwardedRef) => {
296
+ const { ...triggerProps } = props;
297
+ const context = useComboboxContext(TRIGGER_NAME$1);
298
+ const handleOpen = () => {
299
+ if (!context.disabled) {
300
+ context.onOpenChange(true);
301
+ }
302
+ };
303
+ return /* @__PURE__ */ jsx(PopperPrimitive.Anchor, { asChild: true, children: /* @__PURE__ */ jsx(
304
+ FocusScope,
305
+ {
306
+ asChild: true,
307
+ trapped: context.open,
308
+ onMountAutoFocus: (event) => {
309
+ event.preventDefault();
310
+ },
311
+ onUnmountAutoFocus: (event) => {
312
+ context.trigger?.focus({ preventScroll: true });
313
+ document.getSelection()?.empty();
314
+ event.preventDefault();
315
+ },
316
+ children: /* @__PURE__ */ jsx(
317
+ "div",
318
+ {
319
+ ref: forwardedRef,
320
+ "data-disabled": context.disabled ? "" : void 0,
321
+ ...triggerProps,
322
+ onClick: composeEventHandlers(triggerProps.onClick, () => {
323
+ context.trigger?.focus();
324
+ }),
325
+ onPointerDown: composeEventHandlers(triggerProps.onPointerDown, (event) => {
326
+ const target = event.target;
327
+ if (target.hasPointerCapture(event.pointerId)) {
328
+ target.releasePointerCapture(event.pointerId);
329
+ }
330
+ const buttonTarg = target.closest("button") ?? target.closest("div");
331
+ if (buttonTarg !== event.currentTarget) {
332
+ return;
333
+ }
334
+ if (event.button === 0 && event.ctrlKey === false) {
335
+ handleOpen();
336
+ context.trigger?.focus();
337
+ }
338
+ })
339
+ }
340
+ )
341
+ }
342
+ ) });
343
+ });
344
+ ComboboxTrigger.displayName = TRIGGER_NAME$1;
345
+ const INPUT_NAME = "ComboboxInput";
346
+ const ComboxboxTextInput = React.forwardRef((props, forwardedRef) => {
347
+ const context = useComboboxContext(INPUT_NAME);
348
+ const inputRef = React.useRef(null);
349
+ const { getItems } = useCollection$1(void 0);
350
+ const { startsWith } = useFilter(context.locale, { sensitivity: "base" });
351
+ const isDisabled = context.disabled;
352
+ const composedRefs = useComposedRefs(inputRef, forwardedRef, context.onTriggerChange);
353
+ const handleOpen = () => {
354
+ if (!isDisabled) {
355
+ context.onOpenChange(true);
356
+ }
357
+ };
358
+ const previousFilter = usePrev(context.filterValue);
359
+ useLayoutEffect(() => {
360
+ const timeout = setTimeout(() => {
361
+ if (context.textValue === "" || context.textValue === void 0 || context.filterValue === "" || context.filterValue === void 0)
362
+ return;
363
+ const firstItem = getItems().find(
364
+ (item) => item.type === "option" && startsWith(item.textValue, context.textValue)
365
+ );
366
+ const characterChangedAtIndex = findChangedIndex(previousFilter ?? "", context.filterValue);
367
+ if (firstItem && !context.visuallyFocussedItem && characterChangedAtIndex === context.filterValue.length) {
368
+ inputRef.current?.setSelectionRange(context.filterValue.length, context.textValue.length);
369
+ }
370
+ });
371
+ return () => clearTimeout(timeout);
372
+ }, [context.textValue, context.filterValue, startsWith, context.visuallyFocussedItem, getItems, previousFilter]);
373
+ return /* @__PURE__ */ jsx(
374
+ "input",
375
+ {
376
+ type: "text",
377
+ role: "combobox",
378
+ "aria-controls": context.contentId,
379
+ "aria-expanded": context.open,
380
+ "aria-required": context.required,
381
+ "aria-autocomplete": context.autocomplete.type,
382
+ "data-state": context.open ? "open" : "closed",
383
+ "aria-disabled": isDisabled,
384
+ "aria-activedescendant": context.visuallyFocussedItem?.id,
385
+ disabled: isDisabled,
386
+ "data-disabled": isDisabled ? "" : void 0,
387
+ "data-placeholder": context.textValue === void 0 ? "" : void 0,
388
+ value: context.textValue ?? "",
389
+ ...props,
390
+ ref: composedRefs,
391
+ onKeyDown: composeEventHandlers(props.onKeyDown, (event) => {
392
+ if (["ArrowUp", "ArrowDown", "Home", "End"].includes(event.key)) {
393
+ if (!context.open) {
394
+ handleOpen();
395
+ }
396
+ setTimeout(() => {
397
+ const items = getItems().filter((item) => !item.disabled && item.isVisible);
398
+ let candidateNodes = items.map((item) => item.ref.current);
399
+ if (["ArrowUp", "End"].includes(event.key)) {
400
+ candidateNodes = candidateNodes.slice().reverse();
401
+ }
402
+ if (["ArrowUp", "ArrowDown"].includes(event.key)) {
403
+ const currentElement = context.visuallyFocussedItem ?? getItems().find((item) => item.value === context.value)?.ref.current;
404
+ if (currentElement) {
405
+ let currentIndex = candidateNodes.indexOf(currentElement);
406
+ if (currentIndex === candidateNodes.length - 1) {
407
+ currentIndex = -1;
408
+ }
409
+ candidateNodes = candidateNodes.slice(currentIndex + 1);
410
+ }
411
+ }
412
+ if (["ArrowDown"].includes(event.key) && context.autocomplete.type === "both" && candidateNodes.length > 1) {
413
+ const [firstItem, ...restItems] = candidateNodes;
414
+ const firstItemText = getItems().find((item) => item.ref.current === firstItem).textValue;
415
+ if (context.textValue === firstItemText) {
416
+ candidateNodes = restItems;
417
+ }
418
+ }
419
+ context.focusFirst(candidateNodes, getItems());
420
+ });
421
+ event.preventDefault();
422
+ } else if (["Tab"].includes(event.key) && context.open) {
423
+ event.preventDefault();
424
+ } else if (["Escape"].includes(event.key)) {
425
+ if (context.open) {
426
+ context.onOpenChange(false);
427
+ } else {
428
+ context.onValueChange(void 0);
429
+ context.onTextValueChange("");
430
+ }
431
+ event.preventDefault();
432
+ } else if (SELECTION_KEYS$1.includes(event.key)) {
433
+ if (context.visuallyFocussedItem) {
434
+ const focussedItem = getItems().find((item) => item.ref.current === context.visuallyFocussedItem);
435
+ if (focussedItem) {
436
+ context.onValueChange(focussedItem.value);
437
+ context.onTextValueChange(focussedItem.textValue);
438
+ if (context.autocomplete.type === "both") {
439
+ context.onFilterValueChange(focussedItem.textValue);
440
+ }
441
+ focussedItem.ref.current?.click();
442
+ }
443
+ } else {
444
+ const matchedItem = getItems().find(
445
+ (item) => item.type === "option" && !item.disabled && item.textValue === context.textValue
446
+ );
447
+ if (matchedItem) {
448
+ context.onValueChange(matchedItem.value);
449
+ context.onTextValueChange(matchedItem.textValue);
450
+ if (context.autocomplete.type === "both") {
451
+ context.onFilterValueChange(matchedItem.textValue);
452
+ }
453
+ matchedItem.ref.current?.click();
454
+ }
455
+ }
456
+ context.onOpenChange(false);
457
+ event.preventDefault();
458
+ } else {
459
+ context.onVisuallyFocussedItemChange(null);
460
+ }
461
+ }),
462
+ onChange: composeEventHandlers(props.onChange, (event) => {
463
+ context.onTextValueChange(event.currentTarget.value);
464
+ if (context.autocomplete.type === "both") {
465
+ context.onFilterValueChange(event.currentTarget.value);
466
+ }
467
+ }),
468
+ onKeyUp: composeEventHandlers(props.onKeyUp, (event) => {
469
+ if (!context.open && (context.isPrintableCharacter(event.key) || ["Backspace"].includes(event.key))) {
470
+ handleOpen();
471
+ }
472
+ setTimeout(() => {
473
+ if (context.autocomplete.type === "both" && context.isPrintableCharacter(event.key) && context.filterValue !== void 0) {
474
+ const value = context.filterValue;
475
+ const firstItem = getItems().find((item) => startsWith(item.textValue, value));
476
+ if (firstItem) {
477
+ context.onTextValueChange(firstItem.textValue);
478
+ }
479
+ }
480
+ });
481
+ if (context.autocomplete.type === "none" && context.isPrintableCharacter(event.key)) {
482
+ const value = context.textValue ?? "";
483
+ const nextItem = getItems().find((item) => startsWith(item.textValue, value));
484
+ if (nextItem) {
485
+ context.onVisuallyFocussedItemChange(nextItem.ref.current);
486
+ nextItem.ref.current?.scrollIntoView();
487
+ }
488
+ }
489
+ }),
490
+ onBlur: composeEventHandlers(props.onBlur, () => {
491
+ if (context.open) {
492
+ return;
493
+ }
494
+ context.onVisuallyFocussedItemChange(null);
495
+ const [activeItem] = getItems().filter(
496
+ (item) => item.textValue === context.textValue && item.type === "option"
497
+ );
498
+ if (activeItem) {
499
+ context.onValueChange(activeItem.value);
500
+ if (context.autocomplete.type === "both") {
501
+ context.onFilterValueChange(activeItem.textValue);
502
+ }
503
+ return;
504
+ }
505
+ if (context.allowCustomValue) {
506
+ context.onValueChange(context.textValue);
507
+ if (context.autocomplete.type === "both") {
508
+ context.onFilterValueChange(context.textValue);
509
+ }
510
+ return;
511
+ }
512
+ const [previousItem] = getItems().filter((item) => item.value === context.value && item.type === "option");
513
+ if (previousItem && context.textValue !== "") {
514
+ context.onTextValueChange(previousItem.textValue);
515
+ if (context.autocomplete.type === "both") {
516
+ context.onFilterValueChange(previousItem.textValue);
517
+ }
518
+ } else {
519
+ context.onValueChange(void 0);
520
+ context.onTextValueChange("");
521
+ }
522
+ })
523
+ }
524
+ );
525
+ });
526
+ ComboxboxTextInput.displayName = "ComboboxTextInput";
527
+ const ComboboxIcon = React.forwardRef((props, forwardedRef) => {
528
+ const { children, ...iconProps } = props;
529
+ const context = useComboboxContext(INPUT_NAME);
530
+ const isDisabled = context.disabled;
531
+ const handleOpen = () => {
532
+ if (!isDisabled) {
533
+ context.onOpenChange(true);
534
+ context.trigger?.focus();
535
+ }
536
+ };
537
+ return /* @__PURE__ */ jsx(
538
+ Primitive.button,
539
+ {
540
+ "aria-hidden": true,
541
+ type: "button",
542
+ "aria-disabled": isDisabled,
543
+ "aria-controls": context.contentId,
544
+ "aria-expanded": context.open,
545
+ disabled: isDisabled,
546
+ "data-disabled": isDisabled ? "" : void 0,
547
+ ...iconProps,
548
+ tabIndex: -1,
549
+ ref: forwardedRef,
550
+ onClick: composeEventHandlers(iconProps.onClick, () => {
551
+ context.trigger?.focus();
552
+ }),
553
+ onPointerDown: composeEventHandlers(iconProps.onPointerDown, (event) => {
554
+ if (event.button === 0 && event.ctrlKey === false) {
555
+ handleOpen();
556
+ event.preventDefault();
557
+ }
558
+ }),
559
+ onKeyDown: composeEventHandlers(iconProps.onKeyDown, (event) => {
560
+ if (OPEN_KEYS$1.includes(event.key)) {
561
+ handleOpen();
562
+ event.preventDefault();
563
+ }
564
+ }),
565
+ children: children || "▼"
566
+ }
567
+ );
568
+ });
569
+ ComboboxIcon.displayName = "ComboboxIcon";
570
+ const PORTAL_NAME$1 = "ComboboxPortal";
571
+ const ComboboxPortal = (props) => {
572
+ return /* @__PURE__ */ jsx(Portal$2, { asChild: true, ...props });
573
+ };
574
+ ComboboxPortal.displayName = PORTAL_NAME$1;
575
+ const CONTENT_NAME$1 = "ComboboxContent";
576
+ const ComboboxContent = React.forwardRef((props, forwardedRef) => {
577
+ const context = useComboboxContext(CONTENT_NAME$1);
578
+ const { getItems } = useCollection$1(void 0);
579
+ const [fragment, setFragment] = React.useState();
580
+ useLayoutEffect(() => {
581
+ setFragment(new DocumentFragment());
582
+ }, []);
583
+ useLayoutEffect(() => {
584
+ if (context.open && context.autocomplete.type === "none") {
585
+ setTimeout(() => {
586
+ const activeItem = getItems().find((item) => item.value === context.value);
587
+ activeItem?.ref.current?.scrollIntoView({ block: "nearest" });
588
+ });
589
+ }
590
+ }, [getItems, context.autocomplete, context.value, context.open]);
591
+ if (!context.open) {
592
+ const frag = fragment;
593
+ return frag ? ReactDOM.createPortal(
594
+ /* @__PURE__ */ jsx(Collection$1.Slot, { scope: void 0, children: /* @__PURE__ */ jsx("div", { children: props.children }) }),
595
+ frag
596
+ ) : null;
597
+ }
598
+ return /* @__PURE__ */ jsx(ComboboxContentImpl, { ...props, ref: forwardedRef });
599
+ });
600
+ ComboboxContent.displayName = CONTENT_NAME$1;
601
+ const CONTENT_MARGIN$1 = 10;
602
+ const ComboboxContentImpl = React.forwardRef(
603
+ (props, forwardedRef) => {
604
+ const { onEscapeKeyDown, onPointerDownOutside, ...contentProps } = props;
605
+ const context = useComboboxContext(CONTENT_NAME$1);
606
+ const composedRefs = useComposedRefs(forwardedRef, (node) => context.onContentChange(node));
607
+ const { onOpenChange } = context;
608
+ useFocusGuards();
609
+ React.useEffect(() => {
610
+ const close = () => {
611
+ onOpenChange(false);
612
+ };
613
+ window.addEventListener("blur", close);
614
+ window.addEventListener("resize", close);
615
+ return () => {
616
+ window.removeEventListener("blur", close);
617
+ window.removeEventListener("resize", close);
618
+ };
619
+ }, [onOpenChange]);
620
+ return /* @__PURE__ */ jsx(RemoveScroll, { allowPinchZoom: true, children: /* @__PURE__ */ jsx(
621
+ DismissableLayer,
622
+ {
623
+ asChild: true,
624
+ onEscapeKeyDown,
625
+ onPointerDownOutside,
626
+ onFocusOutside: (event) => {
627
+ event.preventDefault();
628
+ },
629
+ onDismiss: () => {
630
+ context.onOpenChange(false);
631
+ context.trigger?.focus({ preventScroll: true });
632
+ },
633
+ children: /* @__PURE__ */ jsx(
634
+ ComboboxPopperPosition,
635
+ {
636
+ role: "listbox",
637
+ id: context.contentId,
638
+ "data-state": context.open ? "open" : "closed",
639
+ onContextMenu: (event) => event.preventDefault(),
640
+ ...contentProps,
641
+ ref: composedRefs,
642
+ style: {
643
+ // flex layout so we can place the scroll buttons properly
644
+ display: "flex",
645
+ flexDirection: "column",
646
+ // reset the outline by default as the content MAY get focused
647
+ outline: "none",
648
+ ...contentProps.style
649
+ }
650
+ }
651
+ )
652
+ }
653
+ ) });
654
+ }
655
+ );
656
+ ComboboxContentImpl.displayName = "ComboboxContentImpl";
657
+ const ComboboxPopperPosition = React.forwardRef(
658
+ (props, forwardedRef) => {
659
+ const { align = "start", collisionPadding = CONTENT_MARGIN$1, ...popperProps } = props;
660
+ return /* @__PURE__ */ jsx(
661
+ PopperPrimitive.Content,
662
+ {
663
+ ...popperProps,
664
+ ref: forwardedRef,
665
+ align,
666
+ collisionPadding,
667
+ style: {
668
+ // Ensure border-box for floating-ui calculations
669
+ boxSizing: "border-box",
670
+ ...popperProps.style,
671
+ // re-namespace exposed content custom properties
672
+ ...{
673
+ "--radix-combobox-content-transform-origin": "var(--radix-popper-transform-origin)",
674
+ "--radix-combobox-content-available-width": "var(--radix-popper-available-width)",
675
+ "--radix-combobox-content-available-height": "var(--radix-popper-available-height)",
676
+ "--radix-combobox-trigger-width": "var(--radix-popper-anchor-width)",
677
+ "--radix-combobox-trigger-height": "var(--radix-popper-anchor-height)"
678
+ }
679
+ }
680
+ }
681
+ );
682
+ }
683
+ );
684
+ ComboboxPopperPosition.displayName = "ComboboxPopperPosition";
685
+ const VIEWPORT_NAME$1 = "ComboboxViewport";
686
+ const ComboboxViewport = React.forwardRef((props, forwardedRef) => {
687
+ const comboboxContext = useComboboxContext(VIEWPORT_NAME$1);
688
+ const composedRefs = useComposedRefs(forwardedRef, comboboxContext.onViewportChange);
689
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
690
+ /* @__PURE__ */ jsx(
691
+ "style",
692
+ {
693
+ dangerouslySetInnerHTML: {
694
+ __html: `[data-radix-combobox-viewport]{scrollbar-width:none;-ms-overflow-style:none;-webkit-overflow-scrolling:touch;}[data-radix-combobox-viewport]::-webkit-scrollbar{display:none}`
695
+ }
696
+ }
697
+ ),
698
+ /* @__PURE__ */ jsx(Collection$1.Slot, { scope: void 0, children: /* @__PURE__ */ jsx(
699
+ Primitive.div,
700
+ {
701
+ "data-radix-combobox-viewport": "",
702
+ role: "presentation",
703
+ ...props,
704
+ ref: composedRefs,
705
+ style: {
706
+ // we use position: 'relative' here on the `viewport` so that when we call
707
+ // `selectedItem.offsetTop` in calculations, the offset is relative to the viewport
708
+ // (independent of the scrollUpButton).
709
+ position: "relative",
710
+ flex: 1,
711
+ overflow: "auto",
712
+ ...props.style
713
+ }
714
+ }
715
+ ) })
716
+ ] });
717
+ });
718
+ ComboboxViewport.displayName = VIEWPORT_NAME$1;
719
+ const ITEM_NAME$1 = "ComboboxItem";
720
+ const [ComboboxItemProvider, useComboboxItemContext] = createContext(ITEM_NAME$1);
721
+ const ComboboxItem = React.forwardRef((props, forwardedRef) => {
722
+ const { value, disabled = false, textValue: textValueProp, ...restProps } = props;
723
+ const [fragment, setFragment] = React.useState();
724
+ useLayoutEffect(() => {
725
+ setFragment(new DocumentFragment());
726
+ }, []);
727
+ const { onTextValueChange, textValue: contextTextValue, ...context } = useComboboxContext(ITEM_NAME$1);
728
+ const textId = useId();
729
+ const [textValue, setTextValue] = React.useState(textValueProp ?? "");
730
+ const isSelected = context.value === value;
731
+ const { startsWith, contains } = useFilter(context.locale, { sensitivity: "base" });
732
+ const handleTextValueChange = React.useCallback((node) => {
733
+ setTextValue((prevTextValue) => {
734
+ return prevTextValue || (node?.textContent ?? "").trim();
735
+ });
736
+ }, []);
737
+ React.useEffect(() => {
738
+ if (isSelected && contextTextValue === void 0 && textValue !== "") {
739
+ onTextValueChange(textValue);
740
+ }
741
+ }, [textValue, isSelected, contextTextValue, onTextValueChange]);
742
+ if (context.autocomplete.type === "both" && textValue && context.filterValue && !startsWith(textValue, context.filterValue) || context.autocomplete.type === "list" && context.autocomplete.filter === "startsWith" && textValue && contextTextValue && !startsWith(textValue, contextTextValue) || context.autocomplete.type === "list" && context.autocomplete.filter === "contains" && textValue && contextTextValue && !contains(textValue, contextTextValue)) {
743
+ return fragment ? ReactDOM.createPortal(
744
+ /* @__PURE__ */ jsx(
745
+ ComboboxItemProvider,
746
+ {
747
+ textId,
748
+ onTextValueChange: handleTextValueChange,
749
+ isSelected,
750
+ textValue,
751
+ children: /* @__PURE__ */ jsx(
752
+ Collection$1.ItemSlot,
753
+ {
754
+ scope: void 0,
755
+ value,
756
+ textValue,
757
+ disabled,
758
+ type: "option",
759
+ isVisible: false,
760
+ children: /* @__PURE__ */ jsx(ComboboxItemImpl, { ref: forwardedRef, value, disabled, ...restProps })
761
+ }
762
+ )
763
+ }
764
+ ),
765
+ fragment
766
+ ) : null;
767
+ }
768
+ return /* @__PURE__ */ jsx(
769
+ ComboboxItemProvider,
770
+ {
771
+ textId,
772
+ onTextValueChange: handleTextValueChange,
773
+ isSelected,
774
+ textValue,
775
+ children: /* @__PURE__ */ jsx(
776
+ Collection$1.ItemSlot,
777
+ {
778
+ scope: void 0,
779
+ value,
780
+ textValue,
781
+ disabled,
782
+ type: "option",
783
+ isVisible: true,
784
+ children: /* @__PURE__ */ jsx(ComboboxItemImpl, { ref: forwardedRef, value, disabled, ...restProps })
785
+ }
786
+ )
787
+ }
788
+ );
789
+ });
790
+ ComboboxItem.displayName = ITEM_NAME$1;
791
+ const ITEM_IMPL_NAME = "ComboboxItemImpl";
792
+ const ComboboxItemImpl = React.forwardRef((props, forwardedRef) => {
793
+ const { value, disabled = false, ...restProps } = props;
794
+ const itemRef = React.useRef(null);
795
+ const composedRefs = useComposedRefs(forwardedRef, itemRef);
796
+ const { getItems } = useCollection$1(void 0);
797
+ const { onTextValueChange, visuallyFocussedItem, ...context } = useComboboxContext(ITEM_NAME$1);
798
+ const { isSelected, textValue, textId } = useComboboxItemContext(ITEM_IMPL_NAME);
799
+ const handleSelect = () => {
800
+ if (!disabled) {
801
+ context.onValueChange(value);
802
+ onTextValueChange(textValue);
803
+ context.onOpenChange(false);
804
+ if (context.autocomplete.type === "both") {
805
+ context.onFilterValueChange(textValue);
806
+ }
807
+ context.trigger?.focus({ preventScroll: true });
808
+ }
809
+ };
810
+ const isFocused = React.useMemo(() => {
811
+ return visuallyFocussedItem === getItems().find((item) => item.ref.current === itemRef.current)?.ref.current;
812
+ }, [getItems, visuallyFocussedItem]);
813
+ const id = useId();
814
+ return /* @__PURE__ */ jsx(
815
+ Primitive.div,
816
+ {
817
+ role: "option",
818
+ "aria-labelledby": textId,
819
+ "data-highlighted": isFocused ? "" : void 0,
820
+ "aria-selected": isSelected && isFocused,
821
+ "data-state": isSelected ? "checked" : "unchecked",
822
+ "aria-disabled": disabled || void 0,
823
+ "data-disabled": disabled ? "" : void 0,
824
+ tabIndex: disabled ? void 0 : -1,
825
+ ...restProps,
826
+ id,
827
+ ref: composedRefs,
828
+ onPointerUp: composeEventHandlers(restProps.onPointerUp, handleSelect)
829
+ }
830
+ );
831
+ });
832
+ ComboboxItemImpl.displayName = ITEM_IMPL_NAME;
833
+ const ITEM_TEXT_NAME$1 = "ComboboxItemText";
834
+ const ComboboxItemText = React.forwardRef((props, forwardedRef) => {
835
+ const { className: _unusedClassName, style: _unusedStyle, ...itemTextProps } = props;
836
+ const itemContext = useComboboxItemContext(ITEM_TEXT_NAME$1);
837
+ const composedRefs = useComposedRefs(forwardedRef, itemContext.onTextValueChange);
838
+ return /* @__PURE__ */ jsx(Primitive.span, { id: itemContext.textId, ...itemTextProps, ref: composedRefs });
839
+ });
840
+ ComboboxItemText.displayName = ITEM_TEXT_NAME$1;
841
+ const ITEM_INDICATOR_NAME$1 = "ComboboxItemIndicator";
842
+ const ComboboxItemIndicator = React.forwardRef((props, forwardedRef) => {
843
+ const { isSelected } = useComboboxItemContext(ITEM_INDICATOR_NAME$1);
844
+ return isSelected ? /* @__PURE__ */ jsx(Primitive.span, { "aria-hidden": true, ...props, ref: forwardedRef }) : null;
845
+ });
846
+ ComboboxItemIndicator.displayName = ITEM_INDICATOR_NAME$1;
847
+ const NO_VALUE_FOUND_NAME = "ComboboxNoValueFound";
848
+ const ComboboxNoValueFound = React.forwardRef((props, ref) => {
849
+ const { textValue = "", filterValue = "", locale, autocomplete } = useComboboxContext(NO_VALUE_FOUND_NAME);
850
+ const [items, setItems] = React.useState([]);
851
+ const { subscribe } = useCollection$1(void 0);
852
+ const { startsWith, contains } = useFilter(locale, { sensitivity: "base" });
853
+ React.useEffect(() => {
854
+ const unsub = subscribe((state) => {
855
+ setItems(state);
856
+ });
857
+ return () => {
858
+ unsub();
859
+ };
860
+ }, [subscribe]);
861
+ if (items.length === 0)
862
+ return null;
863
+ if (autocomplete.type === "none") {
864
+ return null;
865
+ }
866
+ if (autocomplete.type === "list" && autocomplete.filter === "startsWith" && items.some((item) => startsWith(item.textValue, textValue))) {
867
+ return null;
868
+ }
869
+ if (autocomplete.type === "both" && items.some((item) => startsWith(item.textValue, filterValue))) {
870
+ return null;
871
+ }
872
+ if (autocomplete.type === "list" && autocomplete.filter === "contains" && items.some((item) => contains(item.textValue, textValue))) {
873
+ return null;
874
+ }
875
+ return /* @__PURE__ */ jsx(Primitive.div, { ...props, ref });
876
+ });
877
+ ComboboxNoValueFound.displayName = NO_VALUE_FOUND_NAME;
878
+ const ComboboxCreateItem = React.forwardRef((props, ref) => {
879
+ const { disabled = false, ...restProps } = props;
880
+ const context = useComboboxContext(NO_VALUE_FOUND_NAME);
881
+ const { textValue, visuallyFocussedItem } = context;
882
+ const { getItems, subscribe } = useCollection$1(void 0);
883
+ const itemRef = React.useRef(null);
884
+ const [show, setShow] = React.useState(false);
885
+ const composedRefs = useComposedRefs(ref, itemRef);
886
+ const isFocused = React.useMemo(() => {
887
+ return visuallyFocussedItem === getItems().find((item) => item.ref.current === itemRef.current)?.ref.current;
888
+ }, [getItems, visuallyFocussedItem]);
889
+ const id = useId();
890
+ const handleSelect = () => {
891
+ if (!disabled && textValue) {
892
+ context.onValueChange(textValue);
893
+ context.onTextValueChange(textValue);
894
+ context.onOpenChange(false);
895
+ if (context.autocomplete.type === "both") {
896
+ context.onFilterValueChange(textValue);
897
+ }
898
+ context.trigger?.focus({ preventScroll: true });
899
+ }
900
+ };
901
+ useLayoutEffect(() => {
902
+ const unsub = subscribe((state) => {
903
+ setShow(!state.some((item) => item.textValue === textValue && item.type !== "create"));
904
+ });
905
+ if (getItems().length === 0) {
906
+ setShow(true);
907
+ }
908
+ return () => {
909
+ unsub();
910
+ };
911
+ }, [textValue, subscribe, getItems]);
912
+ if (!textValue || !show) {
913
+ return null;
914
+ }
915
+ return /* @__PURE__ */ jsx(
916
+ Collection$1.ItemSlot,
917
+ {
918
+ scope: void 0,
919
+ value: textValue ?? "",
920
+ textValue: textValue ?? "",
921
+ disabled,
922
+ isVisible: true,
923
+ type: "create",
924
+ children: /* @__PURE__ */ jsx(
925
+ Primitive.div,
926
+ {
927
+ role: "option",
928
+ tabIndex: disabled ? void 0 : -1,
929
+ "aria-disabled": disabled || void 0,
930
+ "data-disabled": disabled ? "" : void 0,
931
+ "data-highlighted": isFocused ? "" : void 0,
932
+ ...restProps,
933
+ id,
934
+ ref: composedRefs,
935
+ onPointerUp: composeEventHandlers(restProps.onPointerUp, handleSelect)
936
+ }
937
+ )
938
+ }
939
+ );
940
+ });
941
+ ComboboxCreateItem.displayName = "ComboboxCreateItem";
942
+ const Root$1 = Combobox;
943
+ const Trigger$1 = ComboboxTrigger;
944
+ const TextInput = ComboxboxTextInput;
945
+ const Icon$1 = ComboboxIcon;
946
+ const Portal$1 = ComboboxPortal;
947
+ const Content$1 = ComboboxContent;
948
+ const Viewport$1 = ComboboxViewport;
949
+ const Item$1 = ComboboxItem;
950
+ const ItemText$1 = ComboboxItemText;
951
+ const ItemIndicator$1 = ComboboxItemIndicator;
952
+ const NoValueFound = ComboboxNoValueFound;
953
+ const CreateItem = ComboboxCreateItem;
954
+ function findChangedIndex(a, b) {
955
+ const length = Math.min(a.length, b.length);
956
+ for (let i = 0; i < length; i++) {
957
+ if (a[i] !== b[i]) {
958
+ return i;
959
+ }
960
+ }
961
+ return length;
962
+ }
963
+ const Combobox$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
964
+ __proto__: null,
965
+ ComboboxItem,
966
+ Content: Content$1,
967
+ CreateItem,
968
+ Icon: Icon$1,
969
+ Item: Item$1,
970
+ ItemIndicator: ItemIndicator$1,
971
+ ItemText: ItemText$1,
972
+ NoValueFound,
973
+ Portal: Portal$1,
974
+ Root: Root$1,
975
+ TextInput,
976
+ Trigger: Trigger$1,
977
+ Viewport: Viewport$1
978
+ }, Symbol.toStringTag, { value: "Module" }));
979
+ const OPEN_KEYS = [" ", "Enter", "ArrowUp", "ArrowDown"];
980
+ const SELECTION_KEYS = [" ", "Enter"];
981
+ const SELECT_NAME = "Select";
982
+ const [Collection, useCollection, createCollectionScope] = createCollection$1(SELECT_NAME);
983
+ const [createSelectContext, createSelectScope] = createContextScope(SELECT_NAME, [
984
+ createCollectionScope,
985
+ createPopperScope
986
+ ]);
987
+ const usePopperScope = createPopperScope();
988
+ const [SelectProvider, useSelectContext] = createSelectContext(SELECT_NAME);
989
+ const [SelectNativeOptionsProvider, useSelectNativeOptionsContext] = createSelectContext(SELECT_NAME);
990
+ const Select = (props) => {
991
+ const {
992
+ __scopeSelect,
993
+ children,
994
+ open: openProp,
995
+ defaultOpen,
996
+ onOpenChange,
997
+ value: valueProp,
998
+ defaultValue,
999
+ onValueChange,
1000
+ dir,
1001
+ // name,
1002
+ // autoComplete,
1003
+ disabled,
1004
+ required,
1005
+ multi = false
1006
+ } = props;
1007
+ const popperScope = usePopperScope(__scopeSelect);
1008
+ const [trigger, setTrigger] = React.useState(null);
1009
+ const [valueNode, setValueNode] = React.useState(null);
1010
+ const [valueNodeHasChildren, setValueNodeHasChildren] = React.useState(false);
1011
+ const direction = useDirection(dir);
1012
+ const [open = false, setOpen] = useControllableState({
1013
+ prop: openProp,
1014
+ defaultProp: defaultOpen,
1015
+ onChange: onOpenChange
1016
+ });
1017
+ const [value, setValue] = useControllableState({
1018
+ prop: valueProp,
1019
+ defaultProp: defaultValue,
1020
+ onChange(value2) {
1021
+ if (onValueChange) {
1022
+ if (Array.isArray(value2)) {
1023
+ onValueChange(value2);
1024
+ } else {
1025
+ onValueChange(value2);
1026
+ }
1027
+ }
1028
+ }
1029
+ });
1030
+ const triggerPointerDownPosRef = React.useRef(null);
1031
+ const [_nativeOptionsSet, setNativeOptionsSet] = React.useState(/* @__PURE__ */ new Set());
1032
+ return /* @__PURE__ */ jsx(PopperPrimitive.Root, { ...popperScope, children: /* @__PURE__ */ jsx(
1033
+ SelectProvider,
1034
+ {
1035
+ required,
1036
+ scope: __scopeSelect,
1037
+ trigger,
1038
+ onTriggerChange: setTrigger,
1039
+ valueNode,
1040
+ onValueNodeChange: setValueNode,
1041
+ valueNodeHasChildren,
1042
+ onValueNodeHasChildrenChange: setValueNodeHasChildren,
1043
+ contentId: useId(),
1044
+ value,
1045
+ onValueChange: setValue,
1046
+ open,
1047
+ onOpenChange: setOpen,
1048
+ dir: direction,
1049
+ triggerPointerDownPosRef,
1050
+ disabled,
1051
+ multi,
1052
+ children: /* @__PURE__ */ jsx(Collection.Provider, { scope: __scopeSelect, children: /* @__PURE__ */ jsx(
1053
+ SelectNativeOptionsProvider,
1054
+ {
1055
+ scope: props.__scopeSelect,
1056
+ onNativeOptionAdd: React.useCallback((option) => {
1057
+ setNativeOptionsSet((prev) => new Set(prev).add(option));
1058
+ }, []),
1059
+ onNativeOptionRemove: React.useCallback((option) => {
1060
+ setNativeOptionsSet((prev) => {
1061
+ const optionsSet = new Set(prev);
1062
+ optionsSet.delete(option);
1063
+ return optionsSet;
1064
+ });
1065
+ }, []),
1066
+ children
1067
+ }
1068
+ ) })
1069
+ }
1070
+ ) });
1071
+ };
1072
+ Select.displayName = SELECT_NAME;
1073
+ const TRIGGER_NAME = "SelectTrigger";
1074
+ const SelectTrigger = React.forwardRef(
1075
+ (props, forwardedRef) => {
1076
+ const { __scopeSelect, ...triggerProps } = props;
1077
+ const popperScope = usePopperScope(__scopeSelect);
1078
+ const context = useSelectContext(TRIGGER_NAME, __scopeSelect);
1079
+ const isDisabled = context.disabled;
1080
+ const composedRefs = useComposedRefs(forwardedRef, context.onTriggerChange);
1081
+ const getItems = useCollection(__scopeSelect);
1082
+ const [searchRef, handleTypeaheadSearch, resetTypeahead] = useTypeaheadSearch((search) => {
1083
+ const enabledItems = getItems().filter((item) => !item.disabled);
1084
+ const currentItem = enabledItems.find((item) => item.value === context.value);
1085
+ const nextItem = findNextItem(enabledItems, search, currentItem);
1086
+ if (nextItem !== void 0 && !Array.isArray(nextItem.value)) {
1087
+ const newValue = context.multi ? [nextItem.value] : nextItem.value;
1088
+ context.onValueChange(newValue);
1089
+ }
1090
+ });
1091
+ const handleOpen = () => {
1092
+ if (!isDisabled) {
1093
+ context.onOpenChange(true);
1094
+ resetTypeahead();
1095
+ }
1096
+ };
1097
+ return /* @__PURE__ */ jsx(PopperPrimitive.Anchor, { asChild: true, ...popperScope, children: /* @__PURE__ */ jsx(
1098
+ Primitive.div,
1099
+ {
1100
+ role: "combobox",
1101
+ "aria-controls": context.contentId,
1102
+ "aria-expanded": context.open,
1103
+ "aria-required": context.required,
1104
+ "aria-autocomplete": "none",
1105
+ dir: context.dir,
1106
+ "data-state": context.open ? "open" : "closed",
1107
+ "data-disabled": isDisabled ? "" : void 0,
1108
+ "data-placeholder": context.value === void 0 ? "" : void 0,
1109
+ tabIndex: isDisabled ? void 0 : 0,
1110
+ ...triggerProps,
1111
+ ref: composedRefs,
1112
+ onClick: composeEventHandlers(triggerProps.onClick, (event) => {
1113
+ event.currentTarget.focus();
1114
+ }),
1115
+ onPointerDown: composeEventHandlers(triggerProps.onPointerDown, (event) => {
1116
+ const target = event.target;
1117
+ if (target.hasPointerCapture(event.pointerId)) {
1118
+ target.releasePointerCapture(event.pointerId);
1119
+ }
1120
+ const buttonTarg = target.closest("button") ?? target.closest("div");
1121
+ if (buttonTarg !== event.currentTarget) {
1122
+ return;
1123
+ }
1124
+ if (event.button === 0 && event.ctrlKey === false) {
1125
+ handleOpen();
1126
+ context.triggerPointerDownPosRef.current = {
1127
+ x: Math.round(event.pageX),
1128
+ y: Math.round(event.pageY)
1129
+ };
1130
+ event.preventDefault();
1131
+ }
1132
+ }),
1133
+ onKeyDown: composeEventHandlers(triggerProps.onKeyDown, (event) => {
1134
+ const isTypingAhead = searchRef.current !== "";
1135
+ const isModifierKey = event.ctrlKey || event.altKey || event.metaKey;
1136
+ const target = event.target;
1137
+ const buttonTarg = target.closest("button") ?? target.closest("div");
1138
+ if (buttonTarg !== event.currentTarget) {
1139
+ return;
1140
+ }
1141
+ if (!isModifierKey && event.key.length === 1)
1142
+ handleTypeaheadSearch(event.key);
1143
+ if (isTypingAhead && event.key === " ")
1144
+ return;
1145
+ if (OPEN_KEYS.includes(event.key)) {
1146
+ handleOpen();
1147
+ event.preventDefault();
1148
+ }
1149
+ })
1150
+ }
1151
+ ) });
1152
+ }
1153
+ );
1154
+ SelectTrigger.displayName = TRIGGER_NAME;
1155
+ const VALUE_NAME = "SelectValue";
1156
+ const SelectValue = React.forwardRef(
1157
+ (props, forwardedRef) => {
1158
+ const { __scopeSelect, children, placeholder, ...valueProps } = props;
1159
+ const context = useSelectContext(VALUE_NAME, __scopeSelect);
1160
+ const { onValueNodeHasChildrenChange } = context;
1161
+ const hasChildren = children !== void 0;
1162
+ const composedRefs = useComposedRefs(forwardedRef, context.onValueNodeChange);
1163
+ const [valuedItems, setValuedItems] = React.useState([]);
1164
+ const getItems = useCollection(__scopeSelect);
1165
+ useLayoutEffect(() => {
1166
+ onValueNodeHasChildrenChange(hasChildren);
1167
+ }, [onValueNodeHasChildrenChange, hasChildren]);
1168
+ React.useLayoutEffect(() => {
1169
+ if (Array.isArray(context.value) && valuedItems.length !== context.value.length) {
1170
+ const timeout = setTimeout(() => {
1171
+ const valuedItems2 = getItems().filter(
1172
+ (item) => !Array.isArray(item.value) ? context.value?.includes(item.value) : false
1173
+ );
1174
+ setValuedItems(valuedItems2);
1175
+ });
1176
+ return () => {
1177
+ clearTimeout(timeout);
1178
+ };
1179
+ }
1180
+ }, [context.value, getItems, valuedItems]);
1181
+ let renderValue;
1182
+ if ((context.value === void 0 || context.value.length === 0) && placeholder !== void 0) {
1183
+ renderValue = /* @__PURE__ */ jsx("span", { children: placeholder });
1184
+ } else if (typeof children === "function") {
1185
+ if (Array.isArray(context.value)) {
1186
+ const childrenArray = context.value.map((value) => {
1187
+ const valueItem = valuedItems.find((item) => item.value === value);
1188
+ if (!valueItem) {
1189
+ return null;
1190
+ }
1191
+ return children({ value, textValue: valueItem?.textValue });
1192
+ });
1193
+ renderValue = childrenArray.every((child) => child === null) ? placeholder : childrenArray;
1194
+ } else {
1195
+ renderValue = children(context.value);
1196
+ }
1197
+ } else {
1198
+ renderValue = children;
1199
+ }
1200
+ return /* @__PURE__ */ jsx(Primitive.span, { ...valueProps, ref: composedRefs, children: renderValue || null });
1201
+ }
1202
+ );
1203
+ SelectValue.displayName = VALUE_NAME;
1204
+ const ICON_NAME = "SelectIcon";
1205
+ const SelectIcon = React.forwardRef(
1206
+ (props, forwardedRef) => {
1207
+ const { __scopeSelect, children, ...iconProps } = props;
1208
+ return /* @__PURE__ */ jsx(Primitive.span, { "aria-hidden": true, ...iconProps, ref: forwardedRef, children: children || "▼" });
1209
+ }
1210
+ );
1211
+ SelectIcon.displayName = ICON_NAME;
1212
+ const PORTAL_NAME = "SelectPortal";
1213
+ const SelectPortal = (props) => {
1214
+ return /* @__PURE__ */ jsx(Portal$2, { asChild: true, ...props });
1215
+ };
1216
+ SelectPortal.displayName = PORTAL_NAME;
1217
+ const CONTENT_NAME = "SelectContent";
1218
+ const SelectContent = React.forwardRef(
1219
+ (props, forwardedRef) => {
1220
+ const context = useSelectContext(CONTENT_NAME, props.__scopeSelect);
1221
+ const [fragment, setFragment] = React.useState();
1222
+ useLayoutEffect(() => {
1223
+ setFragment(new DocumentFragment());
1224
+ }, []);
1225
+ if (!context.open) {
1226
+ const frag = fragment;
1227
+ return frag ? ReactDOM.createPortal(
1228
+ /* @__PURE__ */ jsx(SelectContentProvider, { scope: props.__scopeSelect, children: /* @__PURE__ */ jsx(Collection.Slot, { scope: props.__scopeSelect, children: /* @__PURE__ */ jsx("div", { children: props.children }) }) }),
1229
+ frag
1230
+ ) : null;
1231
+ }
1232
+ return /* @__PURE__ */ jsx(SelectContentImpl, { ...props, ref: forwardedRef });
1233
+ }
1234
+ );
1235
+ SelectContent.displayName = CONTENT_NAME;
1236
+ const CONTENT_MARGIN = 10;
1237
+ const [SelectContentProvider, useSelectContentContext] = createSelectContext(CONTENT_NAME);
1238
+ const CONTENT_IMPL_NAME = "SelectContentImpl";
1239
+ const SelectContentImpl = React.forwardRef(
1240
+ (props, forwardedRef) => {
1241
+ const {
1242
+ __scopeSelect,
1243
+ position = "item-aligned",
1244
+ onCloseAutoFocus,
1245
+ onEscapeKeyDown,
1246
+ onPointerDownOutside,
1247
+ //
1248
+ // PopperContent props
1249
+ side,
1250
+ sideOffset,
1251
+ align,
1252
+ alignOffset,
1253
+ arrowPadding,
1254
+ collisionBoundary,
1255
+ collisionPadding,
1256
+ sticky,
1257
+ hideWhenDetached,
1258
+ avoidCollisions,
1259
+ //
1260
+ ...contentProps
1261
+ } = props;
1262
+ const context = useSelectContext(CONTENT_NAME, __scopeSelect);
1263
+ const [content, setContent] = React.useState(null);
1264
+ const [viewport, setViewport] = React.useState(null);
1265
+ const composedRefs = useComposedRefs(forwardedRef, (node) => setContent(node));
1266
+ const [selectedItem, setSelectedItem] = React.useState(null);
1267
+ const [selectedItemText, setSelectedItemText] = React.useState(null);
1268
+ const getItems = useCollection(__scopeSelect);
1269
+ const [isPositioned, setIsPositioned] = React.useState(false);
1270
+ const firstValidItemFoundRef = React.useRef(false);
1271
+ React.useEffect(() => {
1272
+ if (content)
1273
+ return hideOthers(content);
1274
+ }, [content]);
1275
+ useFocusGuards();
1276
+ const focusFirst = React.useCallback(
1277
+ (candidates) => {
1278
+ const [firstItem, ...restItems] = getItems().map((item) => item.ref.current);
1279
+ const [lastItem] = restItems.slice(-1);
1280
+ const PREVIOUSLY_FOCUSED_ELEMENT = document.activeElement;
1281
+ for (const candidate of candidates) {
1282
+ if (candidate === PREVIOUSLY_FOCUSED_ELEMENT)
1283
+ return;
1284
+ candidate?.scrollIntoView({ block: "nearest" });
1285
+ if (candidate === firstItem && viewport)
1286
+ viewport.scrollTop = 0;
1287
+ if (candidate === lastItem && viewport)
1288
+ viewport.scrollTop = viewport.scrollHeight;
1289
+ candidate?.focus();
1290
+ if (document.activeElement !== PREVIOUSLY_FOCUSED_ELEMENT)
1291
+ return;
1292
+ }
1293
+ },
1294
+ [getItems, viewport]
1295
+ );
1296
+ const focusSelectedItem = React.useCallback(
1297
+ () => focusFirst([selectedItem, content]),
1298
+ [focusFirst, selectedItem, content]
1299
+ );
1300
+ React.useEffect(() => {
1301
+ if (isPositioned) {
1302
+ focusSelectedItem();
1303
+ }
1304
+ }, [isPositioned, focusSelectedItem]);
1305
+ const { onOpenChange, triggerPointerDownPosRef } = context;
1306
+ React.useEffect(() => {
1307
+ if (content) {
1308
+ let pointerMoveDelta = { x: 0, y: 0 };
1309
+ const handlePointerMove = (event) => {
1310
+ pointerMoveDelta = {
1311
+ x: Math.abs(Math.round(event.pageX) - (triggerPointerDownPosRef.current?.x ?? 0)),
1312
+ y: Math.abs(Math.round(event.pageY) - (triggerPointerDownPosRef.current?.y ?? 0))
1313
+ };
1314
+ };
1315
+ const handlePointerUp = (event) => {
1316
+ if (pointerMoveDelta.x <= 10 && pointerMoveDelta.y <= 10) {
1317
+ event.preventDefault();
1318
+ } else if (!content.contains(event.target)) {
1319
+ onOpenChange(false);
1320
+ }
1321
+ document.removeEventListener("pointermove", handlePointerMove);
1322
+ triggerPointerDownPosRef.current = null;
1323
+ };
1324
+ if (triggerPointerDownPosRef.current !== null) {
1325
+ document.addEventListener("pointermove", handlePointerMove);
1326
+ document.addEventListener("pointerup", handlePointerUp, { capture: true, once: true });
1327
+ }
1328
+ return () => {
1329
+ document.removeEventListener("pointermove", handlePointerMove);
1330
+ document.removeEventListener("pointerup", handlePointerUp, { capture: true });
1331
+ };
1332
+ }
1333
+ }, [content, onOpenChange, triggerPointerDownPosRef]);
1334
+ React.useEffect(() => {
1335
+ const close = () => onOpenChange(false);
1336
+ window.addEventListener("blur", close);
1337
+ window.addEventListener("resize", close);
1338
+ return () => {
1339
+ window.removeEventListener("blur", close);
1340
+ window.removeEventListener("resize", close);
1341
+ };
1342
+ }, [onOpenChange]);
1343
+ const [searchRef, handleTypeaheadSearch] = useTypeaheadSearch((search) => {
1344
+ const enabledItems = getItems().filter((item) => !item.disabled);
1345
+ const currentItem = enabledItems.find((item) => item.ref.current === document.activeElement);
1346
+ const nextItem = findNextItem(enabledItems, search, currentItem);
1347
+ if (nextItem) {
1348
+ setTimeout(() => nextItem.ref.current.focus());
1349
+ }
1350
+ });
1351
+ const itemRefCallback = React.useCallback(
1352
+ (node, value, disabled) => {
1353
+ const isFirstValidItem = !firstValidItemFoundRef.current && !disabled;
1354
+ const isSelectedItem = context.value !== void 0 && context.value === value;
1355
+ if (isSelectedItem || isFirstValidItem) {
1356
+ setSelectedItem(node);
1357
+ if (isFirstValidItem)
1358
+ firstValidItemFoundRef.current = true;
1359
+ }
1360
+ },
1361
+ [context.value]
1362
+ );
1363
+ const handleItemLeave = React.useCallback(() => content?.focus(), [content]);
1364
+ const itemTextRefCallback = React.useCallback(
1365
+ (node, value, disabled) => {
1366
+ const isFirstValidItem = !firstValidItemFoundRef.current && !disabled;
1367
+ const isSelectedItem = context.value !== void 0 && (Array.isArray(value) ? value.every((v) => context.value?.includes(v)) : context.value === value);
1368
+ if (isSelectedItem || isFirstValidItem) {
1369
+ setSelectedItemText(node);
1370
+ }
1371
+ },
1372
+ [context.value]
1373
+ );
1374
+ const SelectPosition = position === "popper" ? SelectPopperPosition : SelectItemAlignedPosition;
1375
+ const popperContentProps = SelectPosition === SelectPopperPosition ? {
1376
+ side,
1377
+ sideOffset,
1378
+ align,
1379
+ alignOffset,
1380
+ arrowPadding,
1381
+ collisionBoundary,
1382
+ collisionPadding,
1383
+ sticky,
1384
+ hideWhenDetached,
1385
+ avoidCollisions
1386
+ } : {};
1387
+ return /* @__PURE__ */ jsx(
1388
+ SelectContentProvider,
1389
+ {
1390
+ scope: __scopeSelect,
1391
+ content,
1392
+ viewport,
1393
+ onViewportChange: setViewport,
1394
+ itemRefCallback,
1395
+ selectedItem,
1396
+ onItemLeave: handleItemLeave,
1397
+ itemTextRefCallback,
1398
+ focusSelectedItem,
1399
+ selectedItemText,
1400
+ position,
1401
+ isPositioned,
1402
+ searchRef,
1403
+ children: /* @__PURE__ */ jsx(RemoveScroll, { as: Slot, allowPinchZoom: true, children: /* @__PURE__ */ jsx(
1404
+ FocusScope,
1405
+ {
1406
+ asChild: true,
1407
+ trapped: context.open,
1408
+ onMountAutoFocus: (event) => {
1409
+ event.preventDefault();
1410
+ },
1411
+ onUnmountAutoFocus: composeEventHandlers(onCloseAutoFocus, (event) => {
1412
+ context.trigger?.focus({ preventScroll: true });
1413
+ document.getSelection()?.empty();
1414
+ event.preventDefault();
1415
+ }),
1416
+ children: /* @__PURE__ */ jsx(
1417
+ DismissableLayer,
1418
+ {
1419
+ asChild: true,
1420
+ disableOutsidePointerEvents: true,
1421
+ onEscapeKeyDown,
1422
+ onPointerDownOutside,
1423
+ onFocusOutside: (event) => event.preventDefault(),
1424
+ onDismiss: () => context.onOpenChange(false),
1425
+ children: /* @__PURE__ */ jsx(
1426
+ SelectPosition,
1427
+ {
1428
+ role: "listbox",
1429
+ id: context.contentId,
1430
+ "data-state": context.open ? "open" : "closed",
1431
+ "aria-multiselectable": context.multi ? "true" : void 0,
1432
+ dir: context.dir,
1433
+ onContextMenu: (event) => event.preventDefault(),
1434
+ ...contentProps,
1435
+ ...popperContentProps,
1436
+ onPlaced: () => setIsPositioned(true),
1437
+ ref: composedRefs,
1438
+ style: {
1439
+ // flex layout so we can place the scroll buttons properly
1440
+ display: "flex",
1441
+ flexDirection: "column",
1442
+ // reset the outline by default as the content MAY get focused
1443
+ outline: "none",
1444
+ ...contentProps.style
1445
+ },
1446
+ onKeyDown: composeEventHandlers(contentProps.onKeyDown, (event) => {
1447
+ const isModifierKey = event.ctrlKey || event.altKey || event.metaKey;
1448
+ if (event.key === "Tab")
1449
+ event.preventDefault();
1450
+ if (!isModifierKey && event.key.length === 1)
1451
+ handleTypeaheadSearch(event.key);
1452
+ if (["ArrowUp", "ArrowDown", "Home", "End"].includes(event.key)) {
1453
+ const items = getItems().filter((item) => !item.disabled);
1454
+ let candidateNodes = items.map((item) => item.ref.current);
1455
+ if (["ArrowUp", "End"].includes(event.key)) {
1456
+ candidateNodes = candidateNodes.slice().reverse();
1457
+ }
1458
+ if (["ArrowUp", "ArrowDown"].includes(event.key)) {
1459
+ const currentElement = event.target;
1460
+ const currentIndex = candidateNodes.indexOf(currentElement);
1461
+ candidateNodes = candidateNodes.slice(currentIndex + 1);
1462
+ }
1463
+ setTimeout(() => focusFirst(candidateNodes));
1464
+ event.preventDefault();
1465
+ }
1466
+ })
1467
+ }
1468
+ )
1469
+ }
1470
+ )
1471
+ }
1472
+ ) })
1473
+ }
1474
+ );
1475
+ }
1476
+ );
1477
+ SelectContentImpl.displayName = CONTENT_IMPL_NAME;
1478
+ const ITEM_ALIGNED_POSITION_NAME = "SelectItemAlignedPosition";
1479
+ const SelectItemAlignedPosition = React.forwardRef(
1480
+ (props, forwardedRef) => {
1481
+ const { __scopeSelect, onPlaced, ...popperProps } = props;
1482
+ const context = useSelectContext(CONTENT_NAME, __scopeSelect);
1483
+ const contentContext = useSelectContentContext(CONTENT_NAME, __scopeSelect);
1484
+ const [contentWrapper, setContentWrapper] = React.useState(null);
1485
+ const [content, setContent] = React.useState(null);
1486
+ const composedRefs = useComposedRefs(forwardedRef, (node) => setContent(node));
1487
+ const getItems = useCollection(__scopeSelect);
1488
+ const shouldExpandOnScrollRef = React.useRef(false);
1489
+ const shouldRepositionRef = React.useRef(true);
1490
+ const { viewport, selectedItem, selectedItemText, focusSelectedItem } = contentContext;
1491
+ const position = React.useCallback(() => {
1492
+ if (context.trigger && context.valueNode && contentWrapper && content && viewport && selectedItem && selectedItemText) {
1493
+ const triggerRect = context.trigger.getBoundingClientRect();
1494
+ const contentRect = content.getBoundingClientRect();
1495
+ const valueNodeRect = context.valueNode.getBoundingClientRect();
1496
+ const itemTextRect = selectedItemText.getBoundingClientRect();
1497
+ if (context.dir !== "rtl") {
1498
+ const itemTextOffset = itemTextRect.left - contentRect.left;
1499
+ const left = valueNodeRect.left - itemTextOffset;
1500
+ const leftDelta = triggerRect.left - left;
1501
+ const minContentWidth = triggerRect.width + leftDelta;
1502
+ const contentWidth = Math.max(minContentWidth, contentRect.width);
1503
+ const rightEdge = window.innerWidth - CONTENT_MARGIN;
1504
+ const clampedLeft = clamp(left, [CONTENT_MARGIN, rightEdge - contentWidth]);
1505
+ contentWrapper.style.minWidth = `${minContentWidth}px`;
1506
+ contentWrapper.style.left = `${clampedLeft}px`;
1507
+ } else {
1508
+ const itemTextOffset = contentRect.right - itemTextRect.right;
1509
+ const right = window.innerWidth - valueNodeRect.right - itemTextOffset;
1510
+ const rightDelta = window.innerWidth - triggerRect.right - right;
1511
+ const minContentWidth = triggerRect.width + rightDelta;
1512
+ const contentWidth = Math.max(minContentWidth, contentRect.width);
1513
+ const leftEdge = window.innerWidth - CONTENT_MARGIN;
1514
+ const clampedRight = clamp(right, [CONTENT_MARGIN, leftEdge - contentWidth]);
1515
+ contentWrapper.style.minWidth = `${minContentWidth}px`;
1516
+ contentWrapper.style.right = `${clampedRight}px`;
1517
+ }
1518
+ const items = getItems();
1519
+ const availableHeight = window.innerHeight - CONTENT_MARGIN * 2;
1520
+ const itemsHeight = viewport.scrollHeight;
1521
+ const contentStyles = window.getComputedStyle(content);
1522
+ const contentBorderTopWidth = parseInt(contentStyles.borderTopWidth, 10);
1523
+ const contentPaddingTop = parseInt(contentStyles.paddingTop, 10);
1524
+ const contentBorderBottomWidth = parseInt(contentStyles.borderBottomWidth, 10);
1525
+ const contentPaddingBottom = parseInt(contentStyles.paddingBottom, 10);
1526
+ const fullContentHeight = contentBorderTopWidth + contentPaddingTop + itemsHeight + contentPaddingBottom + contentBorderBottomWidth;
1527
+ const minContentHeight = Math.min(selectedItem.offsetHeight * 5, fullContentHeight);
1528
+ const viewportStyles = window.getComputedStyle(viewport);
1529
+ const viewportPaddingTop = parseInt(viewportStyles.paddingTop, 10);
1530
+ const viewportPaddingBottom = parseInt(viewportStyles.paddingBottom, 10);
1531
+ const topEdgeToTriggerMiddle = triggerRect.top + triggerRect.height / 2 - CONTENT_MARGIN;
1532
+ const triggerMiddleToBottomEdge = availableHeight - topEdgeToTriggerMiddle;
1533
+ const selectedItemHalfHeight = selectedItem.offsetHeight / 2;
1534
+ const itemOffsetMiddle = selectedItem.offsetTop + selectedItemHalfHeight;
1535
+ const contentTopToItemMiddle = contentBorderTopWidth + contentPaddingTop + itemOffsetMiddle;
1536
+ const itemMiddleToContentBottom = fullContentHeight - contentTopToItemMiddle;
1537
+ const willAlignWithoutTopOverflow = contentTopToItemMiddle <= topEdgeToTriggerMiddle;
1538
+ if (willAlignWithoutTopOverflow) {
1539
+ const isLastItem = selectedItem === items[items.length - 1].ref.current;
1540
+ contentWrapper.style.bottom = `${0}px`;
1541
+ const viewportOffsetBottom = content.clientHeight - viewport.offsetTop - viewport.offsetHeight;
1542
+ const clampedTriggerMiddleToBottomEdge = Math.max(
1543
+ triggerMiddleToBottomEdge,
1544
+ selectedItemHalfHeight + // viewport might have padding bottom, include it to avoid a scrollable viewport
1545
+ (isLastItem ? viewportPaddingBottom : 0) + viewportOffsetBottom + contentBorderBottomWidth
1546
+ );
1547
+ const height = contentTopToItemMiddle + clampedTriggerMiddleToBottomEdge;
1548
+ contentWrapper.style.height = `${height}px`;
1549
+ } else {
1550
+ const isFirstItem = selectedItem === items[0].ref.current;
1551
+ contentWrapper.style.top = `${0}px`;
1552
+ const clampedTopEdgeToTriggerMiddle = Math.max(
1553
+ topEdgeToTriggerMiddle,
1554
+ contentBorderTopWidth + viewport.offsetTop + // viewport might have padding top, include it to avoid a scrollable viewport
1555
+ (isFirstItem ? viewportPaddingTop : 0) + selectedItemHalfHeight
1556
+ );
1557
+ const height = clampedTopEdgeToTriggerMiddle + itemMiddleToContentBottom;
1558
+ contentWrapper.style.height = `${height}px`;
1559
+ viewport.scrollTop = contentTopToItemMiddle - topEdgeToTriggerMiddle + viewport.offsetTop;
1560
+ }
1561
+ contentWrapper.style.margin = `${CONTENT_MARGIN}px 0`;
1562
+ contentWrapper.style.minHeight = `${minContentHeight}px`;
1563
+ contentWrapper.style.maxHeight = `${availableHeight}px`;
1564
+ onPlaced?.();
1565
+ requestAnimationFrame(() => shouldExpandOnScrollRef.current = true);
1566
+ }
1567
+ }, [
1568
+ getItems,
1569
+ context.trigger,
1570
+ context.valueNode,
1571
+ contentWrapper,
1572
+ content,
1573
+ viewport,
1574
+ selectedItem,
1575
+ selectedItemText,
1576
+ context.dir,
1577
+ onPlaced
1578
+ ]);
1579
+ useLayoutEffect(() => position(), [position]);
1580
+ const [contentZIndex, setContentZIndex] = React.useState();
1581
+ useLayoutEffect(() => {
1582
+ if (content)
1583
+ setContentZIndex(window.getComputedStyle(content).zIndex);
1584
+ }, [content]);
1585
+ const handleScrollButtonChange = React.useCallback(
1586
+ (node) => {
1587
+ if (node && shouldRepositionRef.current === true) {
1588
+ position();
1589
+ focusSelectedItem?.();
1590
+ shouldRepositionRef.current = false;
1591
+ }
1592
+ },
1593
+ [position, focusSelectedItem]
1594
+ );
1595
+ return /* @__PURE__ */ jsx(
1596
+ SelectViewportProvider,
1597
+ {
1598
+ scope: __scopeSelect,
1599
+ contentWrapper,
1600
+ shouldExpandOnScrollRef,
1601
+ onScrollButtonChange: handleScrollButtonChange,
1602
+ children: /* @__PURE__ */ jsx(
1603
+ "div",
1604
+ {
1605
+ ref: setContentWrapper,
1606
+ style: {
1607
+ display: "flex",
1608
+ flexDirection: "column",
1609
+ position: "fixed",
1610
+ zIndex: contentZIndex
1611
+ },
1612
+ children: /* @__PURE__ */ jsx(
1613
+ Primitive.div,
1614
+ {
1615
+ ...popperProps,
1616
+ ref: composedRefs,
1617
+ style: {
1618
+ // When we get the height of the content, it includes borders. If we were to set
1619
+ // the height without having `boxSizing: 'border-box'` it would be too big.
1620
+ boxSizing: "border-box",
1621
+ // We need to ensure the content doesn't get taller than the wrapper
1622
+ maxHeight: "100%",
1623
+ ...popperProps.style
1624
+ }
1625
+ }
1626
+ )
1627
+ }
1628
+ )
1629
+ }
1630
+ );
1631
+ }
1632
+ );
1633
+ SelectItemAlignedPosition.displayName = ITEM_ALIGNED_POSITION_NAME;
1634
+ const POPPER_POSITION_NAME = "SelectPopperPosition";
1635
+ const SelectPopperPosition = React.forwardRef(
1636
+ (props, forwardedRef) => {
1637
+ const { __scopeSelect, align = "start", collisionPadding = CONTENT_MARGIN, ...popperProps } = props;
1638
+ const popperScope = usePopperScope(__scopeSelect);
1639
+ return /* @__PURE__ */ jsx(
1640
+ PopperPrimitive.Content,
1641
+ {
1642
+ ...popperScope,
1643
+ ...popperProps,
1644
+ ref: forwardedRef,
1645
+ align,
1646
+ collisionPadding,
1647
+ style: {
1648
+ // Ensure border-box for floating-ui calculations
1649
+ boxSizing: "border-box",
1650
+ ...popperProps.style,
1651
+ // re-namespace exposed content custom properties
1652
+ ...{
1653
+ "--radix-select-content-transform-origin": "var(--radix-popper-transform-origin)",
1654
+ "--radix-select-content-available-width": "var(--radix-popper-available-width)",
1655
+ "--radix-select-content-available-height": "var(--radix-popper-available-height)",
1656
+ "--radix-select-trigger-width": "var(--radix-popper-anchor-width)",
1657
+ "--radix-select-trigger-height": "var(--radix-popper-anchor-height)"
1658
+ }
1659
+ }
1660
+ }
1661
+ );
1662
+ }
1663
+ );
1664
+ SelectPopperPosition.displayName = POPPER_POSITION_NAME;
1665
+ const [SelectViewportProvider, useSelectViewportContext] = createSelectContext(
1666
+ CONTENT_NAME,
1667
+ {}
1668
+ );
1669
+ const VIEWPORT_NAME = "SelectViewport";
1670
+ const SelectViewport = React.forwardRef(
1671
+ (props, forwardedRef) => {
1672
+ const { __scopeSelect, ...viewportProps } = props;
1673
+ const contentContext = useSelectContentContext(VIEWPORT_NAME, __scopeSelect);
1674
+ const viewportContext = useSelectViewportContext(VIEWPORT_NAME, __scopeSelect);
1675
+ const composedRefs = useComposedRefs(forwardedRef, contentContext.onViewportChange);
1676
+ const prevScrollTopRef = React.useRef(0);
1677
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1678
+ /* @__PURE__ */ jsx(
1679
+ "style",
1680
+ {
1681
+ dangerouslySetInnerHTML: {
1682
+ __html: `[data-radix-select-viewport]{scrollbar-width:none;-ms-overflow-style:none;-webkit-overflow-scrolling:touch;}[data-radix-select-viewport]::-webkit-scrollbar{display:none}`
1683
+ }
1684
+ }
1685
+ ),
1686
+ /* @__PURE__ */ jsx(Collection.Slot, { scope: __scopeSelect, children: /* @__PURE__ */ jsx(
1687
+ Primitive.div,
1688
+ {
1689
+ "data-radix-select-viewport": "",
1690
+ role: "presentation",
1691
+ ...viewportProps,
1692
+ ref: composedRefs,
1693
+ style: {
1694
+ // we use position: 'relative' here on the `viewport` so that when we call
1695
+ // `selectedItem.offsetTop` in calculations, the offset is relative to the viewport
1696
+ // (independent of the scrollUpButton).
1697
+ position: "relative",
1698
+ flex: 1,
1699
+ overflow: "auto",
1700
+ ...viewportProps.style
1701
+ },
1702
+ onScroll: composeEventHandlers(viewportProps.onScroll, (event) => {
1703
+ const viewport = event.currentTarget;
1704
+ const { contentWrapper, shouldExpandOnScrollRef } = viewportContext;
1705
+ if (shouldExpandOnScrollRef?.current && contentWrapper) {
1706
+ const scrolledBy = Math.abs(prevScrollTopRef.current - viewport.scrollTop);
1707
+ if (scrolledBy > 0) {
1708
+ const availableHeight = window.innerHeight - CONTENT_MARGIN * 2;
1709
+ const cssMinHeight = parseFloat(contentWrapper.style.minHeight);
1710
+ const cssHeight = parseFloat(contentWrapper.style.height);
1711
+ const prevHeight = Math.max(cssMinHeight, cssHeight);
1712
+ if (prevHeight < availableHeight) {
1713
+ const nextHeight = prevHeight + scrolledBy;
1714
+ const clampedNextHeight = Math.min(availableHeight, nextHeight);
1715
+ const heightDiff = nextHeight - clampedNextHeight;
1716
+ contentWrapper.style.height = `${clampedNextHeight}px`;
1717
+ if (contentWrapper.style.bottom === "0px") {
1718
+ viewport.scrollTop = heightDiff > 0 ? heightDiff : 0;
1719
+ contentWrapper.style.justifyContent = "flex-end";
1720
+ }
1721
+ }
1722
+ }
1723
+ }
1724
+ prevScrollTopRef.current = viewport.scrollTop;
1725
+ })
1726
+ }
1727
+ ) })
1728
+ ] });
1729
+ }
1730
+ );
1731
+ SelectViewport.displayName = VIEWPORT_NAME;
1732
+ const GROUP_NAME = "SelectGroup";
1733
+ const [SelectGroupContextProvider, useSelectGroupContext] = createSelectContext(GROUP_NAME);
1734
+ const SelectGroup = React.forwardRef(
1735
+ (props, forwardedRef) => {
1736
+ const { __scopeSelect, ...groupProps } = props;
1737
+ const groupId = useId();
1738
+ return /* @__PURE__ */ jsx(SelectGroupContextProvider, { scope: __scopeSelect, id: groupId, children: /* @__PURE__ */ jsx(Primitive.div, { role: "group", "aria-labelledby": groupId, ...groupProps, ref: forwardedRef }) });
1739
+ }
1740
+ );
1741
+ SelectGroup.displayName = GROUP_NAME;
1742
+ const LABEL_NAME = "SelectLabel";
1743
+ const SelectLabel = React.forwardRef(
1744
+ (props, forwardedRef) => {
1745
+ const { __scopeSelect, ...labelProps } = props;
1746
+ const groupContext = useSelectGroupContext(LABEL_NAME, __scopeSelect);
1747
+ return /* @__PURE__ */ jsx(Primitive.div, { id: groupContext.id, ...labelProps, ref: forwardedRef });
1748
+ }
1749
+ );
1750
+ SelectLabel.displayName = LABEL_NAME;
1751
+ const ITEM_NAME = "SelectItem";
1752
+ const [SelectItemContextProvider, useSelectItemContext] = createSelectContext(ITEM_NAME);
1753
+ const SelectItem = React.forwardRef(
1754
+ (props, forwardedRef) => {
1755
+ const { __scopeSelect, value, disabled = false, textValue: textValueProp, ...itemProps } = props;
1756
+ const context = useSelectContext(ITEM_NAME, __scopeSelect);
1757
+ const contentContext = useSelectContentContext(ITEM_NAME, __scopeSelect);
1758
+ const isSelected = typeof value === "string" ? Array.isArray(context.value) ? context.value.includes(value) : context.value === value : value.every((v) => context.value?.includes(v));
1759
+ const isIntermediate = Array.isArray(context.value) && Array.isArray(value) && value.some((v) => context.value?.includes(v));
1760
+ const [textValue, setTextValue] = React.useState(textValueProp ?? "");
1761
+ const [isFocused, setIsFocused] = React.useState(false);
1762
+ const composedRefs = useComposedRefs(
1763
+ forwardedRef,
1764
+ (node) => contentContext.itemRefCallback?.(node, value, disabled)
1765
+ );
1766
+ const textId = useId();
1767
+ const handleSelect = () => {
1768
+ if (!disabled) {
1769
+ let newValue = context.multi && typeof value === "string" ? [value] : value;
1770
+ if (isIntermediate && !isSelected) {
1771
+ context.onValueChange(newValue);
1772
+ } else if (Array.isArray(context.value)) {
1773
+ newValue = toggleArrayValue(value, context.value);
1774
+ }
1775
+ context.onValueChange(newValue);
1776
+ if (!context.multi) {
1777
+ context.onOpenChange(false);
1778
+ }
1779
+ }
1780
+ };
1781
+ if (!context.multi && Array.isArray(value)) {
1782
+ throw new Error("You can only pass an array of values in multi selects");
1783
+ }
1784
+ return /* @__PURE__ */ jsx(
1785
+ SelectItemContextProvider,
1786
+ {
1787
+ scope: __scopeSelect,
1788
+ value,
1789
+ disabled,
1790
+ textId,
1791
+ isSelected,
1792
+ isIntermediate,
1793
+ onItemTextChange: React.useCallback((node) => {
1794
+ setTextValue((prevTextValue) => prevTextValue || (node?.textContent ?? "").trim());
1795
+ }, []),
1796
+ children: /* @__PURE__ */ jsx(Collection.ItemSlot, { scope: __scopeSelect, value, disabled, textValue, children: /* @__PURE__ */ jsx(
1797
+ Primitive.div,
1798
+ {
1799
+ role: "option",
1800
+ "aria-labelledby": textId,
1801
+ "data-highlighted": isFocused ? "" : void 0,
1802
+ "aria-selected": !context.multi ? isSelected && isFocused : void 0,
1803
+ "aria-checked": context.multi ? isSelected : void 0,
1804
+ "data-state": isSelected ? "checked" : "unchecked",
1805
+ "aria-disabled": disabled || void 0,
1806
+ "data-disabled": disabled ? "" : void 0,
1807
+ tabIndex: disabled ? void 0 : -1,
1808
+ ...itemProps,
1809
+ ref: composedRefs,
1810
+ onFocus: composeEventHandlers(itemProps.onFocus, () => setIsFocused(true)),
1811
+ onBlur: composeEventHandlers(itemProps.onBlur, () => setIsFocused(false)),
1812
+ onPointerUp: composeEventHandlers(itemProps.onPointerUp, handleSelect),
1813
+ onPointerMove: composeEventHandlers(itemProps.onPointerMove, (event) => {
1814
+ if (disabled) {
1815
+ contentContext.onItemLeave?.();
1816
+ } else {
1817
+ event.currentTarget.focus({ preventScroll: true });
1818
+ }
1819
+ }),
1820
+ onPointerLeave: composeEventHandlers(itemProps.onPointerLeave, (event) => {
1821
+ if (event.currentTarget === document.activeElement) {
1822
+ contentContext.onItemLeave?.();
1823
+ }
1824
+ }),
1825
+ onKeyDown: composeEventHandlers(itemProps.onKeyDown, (event) => {
1826
+ const isTypingAhead = contentContext.searchRef?.current !== "";
1827
+ if (isTypingAhead && event.key === " ")
1828
+ return;
1829
+ if (SELECTION_KEYS.includes(event.key))
1830
+ handleSelect();
1831
+ if (event.key === " ")
1832
+ event.preventDefault();
1833
+ })
1834
+ }
1835
+ ) })
1836
+ }
1837
+ );
1838
+ }
1839
+ );
1840
+ SelectItem.displayName = ITEM_NAME;
1841
+ const ITEM_TEXT_NAME = "SelectItemText";
1842
+ const SelectItemText = React.forwardRef(
1843
+ (props, forwardedRef) => {
1844
+ const { __scopeSelect, className: _className, style: _style, ...itemTextProps } = props;
1845
+ const context = useSelectContext(ITEM_TEXT_NAME, __scopeSelect);
1846
+ const contentContext = useSelectContentContext(ITEM_TEXT_NAME, __scopeSelect);
1847
+ const itemContext = useSelectItemContext(ITEM_TEXT_NAME, __scopeSelect);
1848
+ const nativeOptionsContext = useSelectNativeOptionsContext(ITEM_TEXT_NAME, __scopeSelect);
1849
+ const [itemTextNode, setItemTextNode] = React.useState(null);
1850
+ const composedRefs = useComposedRefs(
1851
+ forwardedRef,
1852
+ (node) => setItemTextNode(node),
1853
+ itemContext.onItemTextChange,
1854
+ (node) => contentContext.itemTextRefCallback?.(node, itemContext.value, itemContext.disabled)
1855
+ );
1856
+ const textContent = itemTextNode?.textContent;
1857
+ const nativeOption = React.useMemo(
1858
+ () => /* @__PURE__ */ jsx(
1859
+ "option",
1860
+ {
1861
+ value: itemContext.value,
1862
+ disabled: itemContext.disabled,
1863
+ children: textContent
1864
+ },
1865
+ Array.isArray(itemContext.value) ? itemContext.value.join(";") : itemContext.value
1866
+ ),
1867
+ [itemContext.disabled, itemContext.value, textContent]
1868
+ );
1869
+ const { onNativeOptionAdd, onNativeOptionRemove } = nativeOptionsContext;
1870
+ useLayoutEffect(() => {
1871
+ onNativeOptionAdd(nativeOption);
1872
+ return () => onNativeOptionRemove(nativeOption);
1873
+ }, [onNativeOptionAdd, onNativeOptionRemove, nativeOption]);
1874
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1875
+ /* @__PURE__ */ jsx(Primitive.span, { id: itemContext.textId, ...itemTextProps, ref: composedRefs }),
1876
+ itemContext.isSelected && context.valueNode && !context.valueNodeHasChildren ? ReactDOM.createPortal(itemTextProps.children, context.valueNode) : null
1877
+ ] });
1878
+ }
1879
+ );
1880
+ SelectItemText.displayName = ITEM_TEXT_NAME;
1881
+ const ITEM_INDICATOR_NAME = "SelectItemIndicator";
1882
+ const SelectItemIndicator = React.forwardRef(
1883
+ (props, forwardedRef) => {
1884
+ const { __scopeSelect, children, ...itemIndicatorProps } = props;
1885
+ const itemContext = useSelectItemContext(ITEM_INDICATOR_NAME, __scopeSelect);
1886
+ if (typeof children === "function") {
1887
+ return /* @__PURE__ */ jsx(Primitive.span, { "aria-hidden": true, ...itemIndicatorProps, ref: forwardedRef, children: children({
1888
+ isSelected: itemContext.isSelected,
1889
+ isIntermediate: itemContext.isIntermediate
1890
+ }) });
1891
+ }
1892
+ return itemContext.isSelected ? /* @__PURE__ */ jsx(Primitive.span, { "aria-hidden": true, ...itemIndicatorProps, ref: forwardedRef, children }) : null;
1893
+ }
1894
+ );
1895
+ SelectItemIndicator.displayName = ITEM_INDICATOR_NAME;
1896
+ const SCROLL_UP_BUTTON_NAME = "SelectScrollUpButton";
1897
+ const SelectScrollUpButton = React.forwardRef(
1898
+ (props, forwardedRef) => {
1899
+ const contentContext = useSelectContentContext(SCROLL_UP_BUTTON_NAME, props.__scopeSelect);
1900
+ const viewportContext = useSelectViewportContext(SCROLL_UP_BUTTON_NAME, props.__scopeSelect);
1901
+ const [canScrollUp, setCanScrollUp] = React.useState(false);
1902
+ const composedRefs = useComposedRefs(forwardedRef, viewportContext.onScrollButtonChange);
1903
+ useLayoutEffect(() => {
1904
+ if (contentContext.viewport && contentContext.isPositioned) {
1905
+ const viewport = contentContext.viewport;
1906
+ const handleScroll = () => {
1907
+ const canScrollUp2 = viewport.scrollTop > 0;
1908
+ setCanScrollUp(canScrollUp2);
1909
+ };
1910
+ handleScroll();
1911
+ viewport.addEventListener("scroll", handleScroll);
1912
+ return () => viewport.removeEventListener("scroll", handleScroll);
1913
+ }
1914
+ }, [contentContext.viewport, contentContext.isPositioned]);
1915
+ return canScrollUp ? /* @__PURE__ */ jsx(
1916
+ SelectScrollButtonImpl,
1917
+ {
1918
+ ...props,
1919
+ ref: composedRefs,
1920
+ onAutoScroll: () => {
1921
+ const { viewport, selectedItem } = contentContext;
1922
+ if (viewport && selectedItem) {
1923
+ viewport.scrollTop -= selectedItem.offsetHeight;
1924
+ }
1925
+ }
1926
+ }
1927
+ ) : null;
1928
+ }
1929
+ );
1930
+ SelectScrollUpButton.displayName = SCROLL_UP_BUTTON_NAME;
1931
+ const SCROLL_DOWN_BUTTON_NAME = "SelectScrollDownButton";
1932
+ const SelectScrollDownButton = React.forwardRef(
1933
+ (props, forwardedRef) => {
1934
+ const contentContext = useSelectContentContext(SCROLL_DOWN_BUTTON_NAME, props.__scopeSelect);
1935
+ const viewportContext = useSelectViewportContext(SCROLL_DOWN_BUTTON_NAME, props.__scopeSelect);
1936
+ const [canScrollDown, setCanScrollDown] = React.useState(false);
1937
+ const composedRefs = useComposedRefs(forwardedRef, viewportContext.onScrollButtonChange);
1938
+ useLayoutEffect(() => {
1939
+ if (contentContext.viewport && contentContext.isPositioned) {
1940
+ const viewport = contentContext.viewport;
1941
+ const handleScroll = () => {
1942
+ const maxScroll = viewport.scrollHeight - viewport.clientHeight;
1943
+ const canScrollDown2 = Math.ceil(viewport.scrollTop) < maxScroll;
1944
+ setCanScrollDown(canScrollDown2);
1945
+ };
1946
+ handleScroll();
1947
+ viewport.addEventListener("scroll", handleScroll);
1948
+ return () => viewport.removeEventListener("scroll", handleScroll);
1949
+ }
1950
+ }, [contentContext.viewport, contentContext.isPositioned]);
1951
+ return canScrollDown ? /* @__PURE__ */ jsx(
1952
+ SelectScrollButtonImpl,
1953
+ {
1954
+ ...props,
1955
+ ref: composedRefs,
1956
+ onAutoScroll: () => {
1957
+ const { viewport, selectedItem } = contentContext;
1958
+ if (viewport && selectedItem) {
1959
+ viewport.scrollTop += selectedItem.offsetHeight;
1960
+ }
1961
+ }
1962
+ }
1963
+ ) : null;
1964
+ }
1965
+ );
1966
+ SelectScrollDownButton.displayName = SCROLL_DOWN_BUTTON_NAME;
1967
+ const SelectScrollButtonImpl = React.forwardRef(
1968
+ (props, forwardedRef) => {
1969
+ const { __scopeSelect, onAutoScroll, ...scrollIndicatorProps } = props;
1970
+ const contentContext = useSelectContentContext("SelectScrollButton", __scopeSelect);
1971
+ const autoScrollTimerRef = React.useRef(null);
1972
+ const getItems = useCollection(__scopeSelect);
1973
+ const clearAutoScrollTimer = React.useCallback(() => {
1974
+ if (autoScrollTimerRef.current !== null) {
1975
+ window.clearInterval(autoScrollTimerRef.current);
1976
+ autoScrollTimerRef.current = null;
1977
+ }
1978
+ }, []);
1979
+ React.useEffect(() => {
1980
+ return () => clearAutoScrollTimer();
1981
+ }, [clearAutoScrollTimer]);
1982
+ useLayoutEffect(() => {
1983
+ const activeItem = getItems().find((item) => item.ref.current === document.activeElement);
1984
+ activeItem?.ref.current?.scrollIntoView({ block: "nearest" });
1985
+ }, [getItems]);
1986
+ return /* @__PURE__ */ jsx(
1987
+ Primitive.div,
1988
+ {
1989
+ "aria-hidden": true,
1990
+ ...scrollIndicatorProps,
1991
+ ref: forwardedRef,
1992
+ style: { flexShrink: 0, ...scrollIndicatorProps.style },
1993
+ onPointerMove: composeEventHandlers(scrollIndicatorProps.onPointerMove, () => {
1994
+ contentContext.onItemLeave?.();
1995
+ if (autoScrollTimerRef.current === null) {
1996
+ autoScrollTimerRef.current = window.setInterval(onAutoScroll, 50);
1997
+ }
1998
+ }),
1999
+ onPointerLeave: composeEventHandlers(scrollIndicatorProps.onPointerLeave, () => {
2000
+ clearAutoScrollTimer();
2001
+ })
2002
+ }
2003
+ );
2004
+ }
2005
+ );
2006
+ SelectScrollButtonImpl.displayName = "SelectScrollButtonImpl";
2007
+ const SEPARATOR_NAME = "SelectSeparator";
2008
+ const SelectSeparator = React.forwardRef(
2009
+ (props, forwardedRef) => {
2010
+ const { __scopeSelect, ...separatorProps } = props;
2011
+ return /* @__PURE__ */ jsx(Primitive.div, { "aria-hidden": true, ...separatorProps, ref: forwardedRef });
2012
+ }
2013
+ );
2014
+ SelectSeparator.displayName = SEPARATOR_NAME;
2015
+ const ARROW_NAME = "SelectArrow";
2016
+ const SelectArrow = React.forwardRef(
2017
+ (props, forwardedRef) => {
2018
+ const { __scopeSelect, ...arrowProps } = props;
2019
+ const popperScope = usePopperScope(__scopeSelect);
2020
+ const context = useSelectContext(ARROW_NAME, __scopeSelect);
2021
+ const contentContext = useSelectContentContext(ARROW_NAME, __scopeSelect);
2022
+ return context.open && contentContext.position === "popper" ? /* @__PURE__ */ jsx(PopperPrimitive.Arrow, { ...popperScope, ...arrowProps, ref: forwardedRef }) : null;
2023
+ }
2024
+ );
2025
+ SelectArrow.displayName = ARROW_NAME;
2026
+ const BUBBLE_SELECT_NAME = "BubbleSelect";
2027
+ const BubbleSelect = React.forwardRef(
2028
+ (props, forwardedRef) => {
2029
+ const { value, ...selectProps } = props;
2030
+ const ref = React.useRef(null);
2031
+ const composedRefs = useComposedRefs(forwardedRef, ref);
2032
+ const prevValue = usePrevious(value);
2033
+ const context = useSelectContext(BUBBLE_SELECT_NAME, void 0);
2034
+ React.useEffect(() => {
2035
+ const select = ref.current;
2036
+ const selectProto = window.HTMLSelectElement.prototype;
2037
+ const descriptor = Object.getOwnPropertyDescriptor(selectProto, "value");
2038
+ const setValue = descriptor.set;
2039
+ if (prevValue !== value && setValue) {
2040
+ const event = new Event("change", { bubbles: true });
2041
+ setValue.call(select, value);
2042
+ select.dispatchEvent(event);
2043
+ }
2044
+ }, [prevValue, value]);
2045
+ let defaultValue = value;
2046
+ if (context.multi && !Array.isArray(value)) {
2047
+ defaultValue = [];
2048
+ }
2049
+ return /* @__PURE__ */ jsx(VisuallyHidden, { asChild: true, children: /* @__PURE__ */ jsx(
2050
+ "select",
2051
+ {
2052
+ ...selectProps,
2053
+ multiple: context.multi ? true : void 0,
2054
+ ref: composedRefs,
2055
+ defaultValue
2056
+ }
2057
+ ) });
2058
+ }
2059
+ );
2060
+ BubbleSelect.displayName = "BubbleSelect";
2061
+ function useTypeaheadSearch(onSearchChange) {
2062
+ const handleSearchChange = useCallbackRef(onSearchChange);
2063
+ const searchRef = React.useRef("");
2064
+ const timerRef = React.useRef(0);
2065
+ const handleTypeaheadSearch = React.useCallback(
2066
+ (key) => {
2067
+ const search = searchRef.current + key;
2068
+ handleSearchChange(search);
2069
+ (function updateSearch(value) {
2070
+ searchRef.current = value;
2071
+ window.clearTimeout(timerRef.current);
2072
+ if (value !== "")
2073
+ timerRef.current = window.setTimeout(() => updateSearch(""), 1e3);
2074
+ })(search);
2075
+ },
2076
+ [handleSearchChange]
2077
+ );
2078
+ const resetTypeahead = React.useCallback(() => {
2079
+ searchRef.current = "";
2080
+ window.clearTimeout(timerRef.current);
2081
+ }, []);
2082
+ React.useEffect(() => {
2083
+ return () => window.clearTimeout(timerRef.current);
2084
+ }, []);
2085
+ return [searchRef, handleTypeaheadSearch, resetTypeahead];
2086
+ }
2087
+ function findNextItem(items, search, currentItem) {
2088
+ const isRepeated = search.length > 1 && Array.from(search).every((char) => char === search[0]);
2089
+ const normalizedSearch = isRepeated ? search[0] : search;
2090
+ const currentItemIndex = currentItem ? items.indexOf(currentItem) : -1;
2091
+ let wrappedItems = wrapArray(items, Math.max(currentItemIndex, 0));
2092
+ const excludeCurrentItem = normalizedSearch.length === 1;
2093
+ if (excludeCurrentItem)
2094
+ wrappedItems = wrappedItems.filter((v) => v !== currentItem);
2095
+ const nextItem = wrappedItems.find((item) => item.textValue.toLowerCase().startsWith(normalizedSearch.toLowerCase()));
2096
+ return nextItem !== currentItem ? nextItem : void 0;
2097
+ }
2098
+ function wrapArray(array, startIndex) {
2099
+ return array.map((_, index) => array[(startIndex + index) % array.length]);
2100
+ }
2101
+ const toggleArrayValue = (value, array = []) => {
2102
+ if (Array.isArray(value)) {
2103
+ return value.reduce((acc, val) => toggleArrayValue(val, acc), array);
2104
+ }
2105
+ const index = array.indexOf(value);
2106
+ if (index === -1) {
2107
+ return [...array, value];
2108
+ }
2109
+ return [...array.slice(0, index), ...array.slice(index + 1)];
2110
+ };
2111
+ const Root = Select;
2112
+ const Trigger = SelectTrigger;
2113
+ const Value = SelectValue;
2114
+ const Icon = SelectIcon;
2115
+ const Portal = SelectPortal;
2116
+ const Content = SelectContent;
2117
+ const Viewport = SelectViewport;
2118
+ const Group = SelectGroup;
2119
+ const Label = SelectLabel;
2120
+ const Item = SelectItem;
2121
+ const ItemText = SelectItemText;
2122
+ const ItemIndicator = SelectItemIndicator;
2123
+ const ScrollUpButton = SelectScrollUpButton;
2124
+ const ScrollDownButton = SelectScrollDownButton;
2125
+ const Separator = SelectSeparator;
2126
+ const Arrow = SelectArrow;
2127
+ const Select$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
2128
+ __proto__: null,
2129
+ Arrow,
2130
+ Content,
2131
+ Group,
2132
+ Icon,
2133
+ Item,
2134
+ ItemIndicator,
2135
+ ItemText,
2136
+ Label,
2137
+ Portal,
2138
+ Root,
2139
+ ScrollDownButton,
2140
+ ScrollUpButton,
2141
+ Select,
2142
+ SelectArrow,
2143
+ SelectContent,
2144
+ SelectGroup,
2145
+ SelectIcon,
2146
+ SelectItem,
2147
+ SelectItemIndicator,
2148
+ SelectItemText,
2149
+ SelectLabel,
2150
+ SelectPortal,
2151
+ SelectScrollDownButton,
2152
+ SelectScrollUpButton,
2153
+ SelectSeparator,
2154
+ SelectTrigger,
2155
+ SelectValue,
2156
+ SelectViewport,
2157
+ Separator,
2158
+ Trigger,
2159
+ Value,
2160
+ Viewport,
2161
+ createSelectScope
2162
+ }, Symbol.toStringTag, { value: "Module" }));
2163
+ export {
2164
+ Combobox$1 as Combobox,
2165
+ Select$1 as Select,
2166
+ composeEventHandlers2 as composeEventHandlers,
2167
+ createCollection,
2168
+ useCallbackRef2 as useCallbackRef,
2169
+ useCollator,
2170
+ useFilter
2171
+ };
2172
+ //# sourceMappingURL=index.mjs.map