@destyler/combobox 0.0.1-beta.20

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.
@@ -0,0 +1,1131 @@
1
+ import { defineComponent, h, mergeProps, toRefs, ref, computed, watch, nextTick, onMounted, withDirectives, withKeys } from 'vue';
2
+ import { DestylerPrimitive } from '@destyler/primitive';
3
+ import { useForwardExpose, useDirection, useVModel, useFormControl, useId, useBodyScrollLock, useHideOthers, useForwardProps, useForwardPropsEmits, useMutationObserver } from '@destyler/composition';
4
+ import { DestylerPopperAnchor, DestylerPopperRoot, DestylerPopperContent, DestylerPopperArrow } from '@destyler/popper';
5
+ import { createContext, computedWithControl, handleAndDispatchCustomEvent } from '@destyler/shared';
6
+ import isEqual from 'fast-deep-equal';
7
+ import { createCollection, DestylerCollectionSlot, DestylerCollectionItem } from '@destyler/collection';
8
+ import { DestylerVisuallyhiddenInput } from '@destyler/visually-hidden/dist/component';
9
+ import { DestylerDismissableLayer } from '@destyler/dismissable-layer';
10
+ import { BindOnceDirective } from '@destyler/directives';
11
+ import { DestylerPresence } from '@destyler/presence';
12
+ import { DestylerTeleport } from '@destyler/teleport';
13
+
14
+ const destylerComboboxAnchorProps = {
15
+ as: {
16
+ type: [String, Object],
17
+ required: false,
18
+ default: "div"
19
+ },
20
+ asChild: {
21
+ type: Boolean,
22
+ required: false,
23
+ default: false
24
+ }
25
+ };
26
+ const DestylerComboboxAnchor = defineComponent({
27
+ name: "DestylerComboboxAnchor",
28
+ props: destylerComboboxAnchorProps,
29
+ setup() {
30
+ const { forwardRef } = useForwardExpose();
31
+ return {
32
+ forwardRef
33
+ };
34
+ },
35
+ render() {
36
+ return h(DestylerPopperAnchor, {
37
+ asChild: true
38
+ }, {
39
+ default: () => {
40
+ return h(DestylerPrimitive, mergeProps(this.$attrs, {
41
+ ref: (el) => this.forwardRef(el),
42
+ as: this.$props.as,
43
+ asChild: this.$props.asChild
44
+ }), {
45
+ default: () => this.$slots.default?.()
46
+ });
47
+ }
48
+ });
49
+ }
50
+ });
51
+
52
+ const destylerComboboxRootProps = {
53
+ as: {
54
+ type: [String, Object],
55
+ required: false,
56
+ default: "div"
57
+ },
58
+ asChild: {
59
+ type: Boolean,
60
+ required: false,
61
+ default: false
62
+ },
63
+ modelValue: {
64
+ type: [String, Number, Boolean, Object],
65
+ required: false
66
+ },
67
+ defaultValue: {
68
+ type: [String, Number, Boolean, Object],
69
+ required: false
70
+ },
71
+ open: {
72
+ type: Boolean,
73
+ required: false,
74
+ default: void 0
75
+ },
76
+ defaultOpen: {
77
+ type: Boolean,
78
+ required: false
79
+ },
80
+ searchTerm: {
81
+ type: String,
82
+ required: false
83
+ },
84
+ multiple: {
85
+ type: Boolean,
86
+ required: false
87
+ },
88
+ disabled: {
89
+ type: Boolean,
90
+ required: false
91
+ },
92
+ name: {
93
+ type: String,
94
+ required: false
95
+ },
96
+ dir: {
97
+ type: String,
98
+ required: false
99
+ },
100
+ filterFunction: {
101
+ type: Function,
102
+ required: false
103
+ },
104
+ displayValue: {
105
+ type: Function,
106
+ required: false
107
+ }
108
+ };
109
+ const [injectComboboxRootContext, provideComboboxRootContext] = createContext("DestylerComboboxRoot");
110
+ const DestylerComboboxRoot = defineComponent({
111
+ name: "DestylerComboboxRoot",
112
+ props: destylerComboboxRootProps,
113
+ emits: ["update:modelValue", "update:open", "update:searchTerm"],
114
+ setup(props, { emit }) {
115
+ const { multiple, disabled, dir: propDir } = toRefs(props);
116
+ const dir = useDirection(propDir);
117
+ const searchTerm = useVModel(props, "searchTerm", emit, {
118
+ defaultValue: "",
119
+ passive: props.searchTerm === void 0
120
+ });
121
+ const modelValue = useVModel(props, "modelValue", emit, {
122
+ defaultValue: props.defaultValue ?? multiple.value ? [] : void 0,
123
+ passive: props.modelValue === void 0,
124
+ deep: true
125
+ });
126
+ const open = useVModel(props, "open", emit, {
127
+ defaultValue: props.defaultOpen,
128
+ passive: props.open === void 0
129
+ });
130
+ const selectedValue = ref();
131
+ const isUserInputted = ref(false);
132
+ const inputElement = ref();
133
+ const contentElement = ref();
134
+ const { forwardRef, currentElement: parentElement } = useForwardExpose();
135
+ const { getItems, reactiveItems, itemMapSize } = createCollection("data-destyler-combobox-item");
136
+ async function onOpenChange(val) {
137
+ open.value = val;
138
+ await nextTick();
139
+ if (val) {
140
+ if (modelValue.value) {
141
+ if (Array.isArray(modelValue.value) && multiple.value)
142
+ selectedValue.value = getItems().find((i) => i.ref?.dataset?.state === "checked")?.value;
143
+ else
144
+ selectedValue.value = modelValue.value;
145
+ }
146
+ inputElement.value?.focus();
147
+ scrollSelectedValueIntoView();
148
+ } else {
149
+ isUserInputted.value = false;
150
+ resetSearchTerm();
151
+ }
152
+ }
153
+ function onValueChange(val) {
154
+ if (Array.isArray(modelValue.value) && multiple.value) {
155
+ const index = modelValue.value.findIndex((i) => isEqual(i, val));
156
+ index === -1 ? modelValue.value.push(val) : modelValue.value.splice(index, 1);
157
+ } else {
158
+ modelValue.value = val;
159
+ onOpenChange(false);
160
+ }
161
+ }
162
+ const options = computedWithControl(() => itemMapSize.value, () => {
163
+ return getItems().map((i) => i.value);
164
+ });
165
+ const filteredOptions = computed(() => {
166
+ if (isUserInputted.value) {
167
+ if (props.filterFunction)
168
+ return props.filterFunction(options.value, searchTerm.value);
169
+ else if (typeof options.value[0] === "string")
170
+ return options.value.filter((i) => i.toLowerCase().includes(searchTerm.value?.toLowerCase()));
171
+ }
172
+ return options.value;
173
+ });
174
+ function resetSearchTerm() {
175
+ if (!multiple.value && modelValue.value) {
176
+ if (props.displayValue)
177
+ searchTerm.value = props.displayValue(modelValue.value);
178
+ else if (typeof modelValue.value !== "object")
179
+ searchTerm.value = modelValue.value.toString();
180
+ else
181
+ searchTerm.value = "";
182
+ } else {
183
+ searchTerm.value = "";
184
+ }
185
+ }
186
+ const activeIndex = computed(() => filteredOptions.value.findIndex((i) => isEqual(i, selectedValue.value)));
187
+ const selectedElement = computed(() => {
188
+ return reactiveItems.value.find((i) => i.value === selectedValue.value)?.ref;
189
+ });
190
+ watch(modelValue, async () => {
191
+ await nextTick();
192
+ resetSearchTerm();
193
+ }, { immediate: true });
194
+ watch(() => filteredOptions.value.length, async (length) => {
195
+ await nextTick();
196
+ await nextTick();
197
+ if (length && activeIndex.value === -1)
198
+ selectedValue.value = filteredOptions.value[0];
199
+ });
200
+ const isFormControl = useFormControl(parentElement);
201
+ function scrollSelectedValueIntoView() {
202
+ if (selectedElement.value instanceof Element)
203
+ selectedElement.value.scrollIntoView({ block: "nearest" });
204
+ }
205
+ provideComboboxRootContext({
206
+ searchTerm,
207
+ modelValue,
208
+ onValueChange,
209
+ isUserInputted,
210
+ multiple,
211
+ disabled,
212
+ open,
213
+ onOpenChange,
214
+ filteredOptions,
215
+ contentId: useId(),
216
+ inputElement,
217
+ onInputElementChange: (val) => inputElement.value = val,
218
+ onInputNavigation: async (val) => {
219
+ const index = activeIndex.value;
220
+ if (index === 0 && val === "up" || index === filteredOptions.value.length - 1 && val === "down")
221
+ return;
222
+ if (index === -1 && filteredOptions.value.length || val === "home")
223
+ selectedValue.value = filteredOptions.value[0];
224
+ else if (val === "end")
225
+ selectedValue.value = filteredOptions.value[filteredOptions.value.length - 1];
226
+ else
227
+ selectedValue.value = filteredOptions.value[val === "up" ? index - 1 : index + 1];
228
+ scrollSelectedValueIntoView();
229
+ },
230
+ onInputEnter: async () => {
231
+ if (filteredOptions.value.length && selectedValue.value && selectedElement.value instanceof Element)
232
+ selectedElement.value?.click();
233
+ },
234
+ selectedValue,
235
+ onSelectedValueChange: (val) => selectedValue.value = val,
236
+ parentElement,
237
+ contentElement,
238
+ onContentElementChange: (val) => contentElement.value = val
239
+ });
240
+ return {
241
+ forwardRef,
242
+ isFormControl,
243
+ open,
244
+ modelValue,
245
+ dir
246
+ };
247
+ },
248
+ render() {
249
+ return h(DestylerPopperRoot, {}, {
250
+ default: () => {
251
+ return h(DestylerPrimitive, mergeProps(this.$attrs, {
252
+ ref: (el) => this.forwardRef(el),
253
+ style: {
254
+ pointerEvents: this.open ? "auto" : void 0
255
+ },
256
+ as: this.$props.as,
257
+ asChild: this.$props.asChild,
258
+ dir: this.dir
259
+ }), {
260
+ default: () => {
261
+ return [
262
+ this.$slots.default?.({ open: this.open, modelValue: this.modelValue }),
263
+ this.isFormControl && this.$props.name ? h(DestylerVisuallyhiddenInput, {
264
+ name: this.$props.name,
265
+ value: this.modelValue
266
+ }) : null
267
+ ];
268
+ }
269
+ });
270
+ }
271
+ });
272
+ }
273
+ });
274
+
275
+ const destylerComboboxContentImplProps = {
276
+ as: {
277
+ type: [String, Object],
278
+ required: false,
279
+ default: "div"
280
+ },
281
+ asChild: {
282
+ type: Boolean,
283
+ required: false,
284
+ default: false
285
+ },
286
+ side: {
287
+ type: String,
288
+ required: false,
289
+ default: "bottom"
290
+ },
291
+ sideOffset: {
292
+ type: Number,
293
+ required: false,
294
+ default: 0
295
+ },
296
+ align: {
297
+ type: String,
298
+ required: false,
299
+ default: "center"
300
+ },
301
+ alignOffset: {
302
+ type: Number,
303
+ required: false,
304
+ default: 0
305
+ },
306
+ avoidCollisions: {
307
+ type: Boolean,
308
+ required: false,
309
+ default: true
310
+ },
311
+ collisionBoundary: {
312
+ type: [Object, Array, null],
313
+ required: false,
314
+ default: () => []
315
+ },
316
+ collisionPadding: {
317
+ type: [Number, Object],
318
+ required: false,
319
+ default: 0
320
+ },
321
+ arrowPadding: {
322
+ type: Number,
323
+ required: false,
324
+ default: 0
325
+ },
326
+ sticky: {
327
+ type: String,
328
+ required: false,
329
+ default: "partial"
330
+ },
331
+ hideWhenDetached: {
332
+ type: Boolean,
333
+ required: false,
334
+ default: false
335
+ },
336
+ updatePositionStrategy: {
337
+ type: String,
338
+ required: false,
339
+ default: "optimized"
340
+ },
341
+ onPlaced: {
342
+ type: Function,
343
+ required: false
344
+ },
345
+ prioritizePosition: {
346
+ type: Boolean,
347
+ required: false,
348
+ default: false
349
+ },
350
+ disableOutsidePointerEvents: {
351
+ type: Boolean,
352
+ required: false,
353
+ default: false
354
+ },
355
+ isDismissable: {
356
+ type: Boolean,
357
+ required: false,
358
+ default: true
359
+ },
360
+ position: {
361
+ type: String,
362
+ required: false,
363
+ default: "inline"
364
+ },
365
+ bodyLock: {
366
+ type: Boolean,
367
+ required: false
368
+ },
369
+ dismissable: {
370
+ type: Boolean,
371
+ required: false,
372
+ default: true
373
+ }
374
+ };
375
+ const [injectComboboxContentContext, provideComboboxContentContext] = createContext("DestylerComboboxContent");
376
+ const DestylerComboboxContentImpl = defineComponent({
377
+ name: "DestylerComboboxContentImpl",
378
+ props: destylerComboboxContentImplProps,
379
+ emits: ["escapeKeyDown", "pointerDownOutside", "focusOutside", "interactOutside", "dismiss"],
380
+ setup(props) {
381
+ const { position } = toRefs(props);
382
+ const rootContext = injectComboboxRootContext();
383
+ useBodyScrollLock(props.bodyLock);
384
+ const { forwardRef, currentElement } = useForwardExpose();
385
+ useHideOthers(currentElement);
386
+ const pickedProps = computed(() => {
387
+ if (props.position === "popper")
388
+ return props;
389
+ else
390
+ return {};
391
+ });
392
+ const forwardedProps = useForwardProps(pickedProps.value);
393
+ function handleLeave() {
394
+ rootContext.onSelectedValueChange("");
395
+ }
396
+ onMounted(() => {
397
+ rootContext.onContentElementChange(currentElement.value);
398
+ });
399
+ const popperStyle = {
400
+ "boxSizing": "border-box",
401
+ "--destyler_combobox_content_transform_origin": "var(--destyler_popper_transform_origin)",
402
+ "--destyler_combobox_content_available_width": "var(--destyler_popper_available_width)",
403
+ "--destyler_combobox_content_available_height": "var(--destyler_popper_available_height)",
404
+ "--destyler_combobox_trigger_width": "var(--destyler_popper_anchor_width)",
405
+ "--destyler_combobox_trigger_height": "var(--destyler_popper_anchor_height)"
406
+ };
407
+ provideComboboxContentContext({ position });
408
+ return {
409
+ rootContext,
410
+ popperStyle,
411
+ forwardedProps,
412
+ forwardRef,
413
+ handleLeave
414
+ };
415
+ },
416
+ render() {
417
+ return h(DestylerCollectionSlot, null, {
418
+ default: () => {
419
+ return [
420
+ this.$props.dismissable ? h(DestylerDismissableLayer, mergeProps(this.$attrs, this.forwardedProps, {
421
+ asChild: true,
422
+ disableOutsidePointerEvents: this.$props.disableOutsidePointerEvents,
423
+ onDismiss: () => {
424
+ this.rootContext.onOpenChange(false);
425
+ },
426
+ onFocusOutside: (event) => {
427
+ if (this.rootContext.parentElement.value?.contains(event.target))
428
+ event.preventDefault();
429
+ this.$emit("focusOutside", event);
430
+ },
431
+ onInteractOutside: (event) => {
432
+ this.$emit("interactOutside", event);
433
+ },
434
+ onEscapeKeyDown: (event) => {
435
+ this.$emit("escapeKeyDown", event);
436
+ },
437
+ onPointerDownOutside: (event) => {
438
+ if (this.rootContext.parentElement.value?.contains(event.target))
439
+ event.preventDefault();
440
+ this.$emit("pointerDownOutside", event);
441
+ }
442
+ }), {
443
+ default: () => {
444
+ return withDirectives(h(this.$props.position === "popper" ? DestylerPopperContent : DestylerPrimitive, {
445
+ "ref": (el) => this.forwardRef(el),
446
+ "role": "listbox",
447
+ "data-state": this.rootContext.open.value ? "open" : "closed",
448
+ "style": {
449
+ display: "flex",
450
+ flexDirection: "column",
451
+ outline: "none",
452
+ ...this.$props.position === "popper" ? this.popperStyle : {}
453
+ },
454
+ "onPointerleave": () => {
455
+ this.handleLeave();
456
+ }
457
+ }, {
458
+ default: () => this.$slots.default?.()
459
+ }), [
460
+ [BindOnceDirective, { id: this.rootContext.contentId }]
461
+ ]);
462
+ }
463
+ }) : withDirectives(h(this.$props.position === "popper" ? DestylerPopperContent : DestylerPrimitive, {
464
+ "ref": (el) => this.forwardRef(el),
465
+ "role": "listbox",
466
+ "data-state": this.rootContext.open.value ? "open" : "closed",
467
+ "style": {
468
+ display: "flex",
469
+ flexDirection: "column",
470
+ outline: "none",
471
+ ...this.$props.position === "popper" ? this.popperStyle : {}
472
+ },
473
+ "onPointerleave": () => {
474
+ this.handleLeave();
475
+ }
476
+ }, {
477
+ default: () => this.$slots.default?.()
478
+ }), [
479
+ [BindOnceDirective, { id: this.rootContext.contentId }]
480
+ ])
481
+ ];
482
+ }
483
+ });
484
+ }
485
+ });
486
+
487
+ const destylerComboboxArrowProps = {
488
+ asChild: {
489
+ type: Boolean,
490
+ required: false,
491
+ default: false
492
+ },
493
+ width: {
494
+ type: Number,
495
+ required: false,
496
+ default: 10
497
+ },
498
+ height: {
499
+ type: Number,
500
+ required: false,
501
+ default: 5
502
+ },
503
+ as: {
504
+ type: [String, Object],
505
+ required: false,
506
+ default: "svg"
507
+ }
508
+ };
509
+ const DestylerComboboxArrow = defineComponent({
510
+ name: "DestylerComboboxArrow",
511
+ props: destylerComboboxArrowProps,
512
+ setup() {
513
+ useForwardExpose();
514
+ const rootContext = injectComboboxRootContext();
515
+ const contentContext = injectComboboxContentContext();
516
+ return {
517
+ rootContext,
518
+ contentContext
519
+ };
520
+ },
521
+ render() {
522
+ if (this.rootContext.open.value && this.contentContext.position.value === "popper") {
523
+ return h(DestylerPopperArrow, this.$props, {
524
+ default: () => this.$slots.default?.()
525
+ });
526
+ }
527
+ }
528
+ });
529
+
530
+ const destylerComboboxCancelProps = {
531
+ as: {
532
+ type: [String, Object],
533
+ required: false,
534
+ default: "button"
535
+ },
536
+ asChild: {
537
+ type: Boolean,
538
+ required: false,
539
+ default: false
540
+ }
541
+ };
542
+ const DestylerComboboxCancel = defineComponent({
543
+ name: "DestylerComboboxCancel",
544
+ props: destylerComboboxCancelProps,
545
+ setup() {
546
+ useForwardExpose();
547
+ const rootContext = injectComboboxRootContext();
548
+ function handleClick() {
549
+ rootContext.searchTerm.value = "";
550
+ rootContext.inputElement.value?.focus();
551
+ }
552
+ return {
553
+ rootContext,
554
+ handleClick
555
+ };
556
+ },
557
+ render() {
558
+ return h(DestylerPrimitive, mergeProps(this.$props, {
559
+ type: this.$props.as === "button" ? "button" : void 0,
560
+ tabindex: "-1",
561
+ onClick: () => {
562
+ this.handleClick();
563
+ }
564
+ }), {
565
+ default: () => this.$slots.default?.()
566
+ });
567
+ }
568
+ });
569
+
570
+ const destylerComboboxContentProps = {
571
+ ...destylerComboboxContentImplProps,
572
+ forceMount: {
573
+ type: Boolean,
574
+ required: false
575
+ }
576
+ };
577
+ const DestylerComboboxContent = defineComponent({
578
+ name: "DestylerComboboxContent",
579
+ props: destylerComboboxContentProps,
580
+ emits: ["escapeKeyDown", "pointerDownOutside", "focusOutside", "interactOutside", "dismiss"],
581
+ setup(props, { emit }) {
582
+ const forwarded = useForwardPropsEmits(props, emit);
583
+ const { forwardRef } = useForwardExpose();
584
+ const rootContext = injectComboboxRootContext();
585
+ return {
586
+ forwardRef,
587
+ forwarded,
588
+ rootContext
589
+ };
590
+ },
591
+ render() {
592
+ return h(DestylerPresence, {
593
+ present: this.$props.forceMount || this.rootContext.open.value
594
+ }, {
595
+ default: () => {
596
+ return h(DestylerComboboxContentImpl, mergeProps(this.$attrs, this.forwarded, {
597
+ ref: (el) => this.forwardRef(el)
598
+ }), {
599
+ default: () => this.$slots.default?.()
600
+ });
601
+ }
602
+ });
603
+ }
604
+ });
605
+
606
+ const destylerComboboxEmptyProps = {
607
+ as: {
608
+ type: [String, Object],
609
+ required: false,
610
+ default: "button"
611
+ },
612
+ asChild: {
613
+ type: Boolean,
614
+ required: false,
615
+ default: false
616
+ }
617
+ };
618
+ const DestylerComboboxEmpty = defineComponent({
619
+ name: "DestylerComboboxEmpty",
620
+ props: destylerComboboxEmptyProps,
621
+ setup() {
622
+ useForwardExpose();
623
+ const rootContext = injectComboboxRootContext();
624
+ const isEmpty = computed(() => rootContext.filteredOptions.value.length === 0);
625
+ return {
626
+ rootContext,
627
+ isEmpty
628
+ };
629
+ },
630
+ render() {
631
+ if (this.isEmpty) {
632
+ return h(DestylerPrimitive, this.$props, {
633
+ default: () => this.$slots.default ? this.$slots.default?.() : "No options"
634
+ });
635
+ }
636
+ }
637
+ });
638
+
639
+ const destylerComboboxGroupProps = {
640
+ as: {
641
+ type: [String, Object],
642
+ required: false,
643
+ default: "div"
644
+ },
645
+ asChild: {
646
+ type: Boolean,
647
+ required: false,
648
+ default: false
649
+ }
650
+ };
651
+ const [injectComboboxGroupContext, provideComboboxGroupContext] = createContext("DestylerComboboxGroup");
652
+ const DestylerComboboxGroup = defineComponent({
653
+ name: "DestylerComboboxGroup",
654
+ props: destylerComboboxGroupProps,
655
+ setup() {
656
+ const { currentRef, currentElement } = useForwardExpose();
657
+ const id = useId();
658
+ const rootContext = injectComboboxRootContext();
659
+ const hasOptions = ref(false);
660
+ function checkCollectionItem() {
661
+ if (!currentElement.value)
662
+ return;
663
+ const collectionItem = currentElement.value.querySelectorAll("[data-destyler-combobox-item]:not([data-hidden])");
664
+ hasOptions.value = !!collectionItem.length;
665
+ }
666
+ useMutationObserver(currentElement, () => {
667
+ checkCollectionItem();
668
+ }, { childList: true });
669
+ watch(() => rootContext.searchTerm.value, () => {
670
+ nextTick(() => {
671
+ checkCollectionItem();
672
+ });
673
+ }, { immediate: true });
674
+ provideComboboxGroupContext({
675
+ id
676
+ });
677
+ return {
678
+ currentRef,
679
+ hasOptions,
680
+ id
681
+ };
682
+ },
683
+ render() {
684
+ return h(DestylerPrimitive, mergeProps(this.$props, {
685
+ "ref": "currentRef",
686
+ "role": "group",
687
+ "aria-labelledby": this.id,
688
+ ...this.hasOptions ? {} : { style: { display: "none" } }
689
+ }), {
690
+ default: () => this.$slots.default?.()
691
+ });
692
+ }
693
+ });
694
+
695
+ const destylerComboboxInputProps = {
696
+ as: {
697
+ type: [String, Object],
698
+ required: false,
699
+ default: "input"
700
+ },
701
+ asChild: {
702
+ type: Boolean,
703
+ required: false,
704
+ default: false
705
+ },
706
+ type: {
707
+ type: String,
708
+ required: false,
709
+ default: "text"
710
+ },
711
+ disabled: {
712
+ type: Boolean,
713
+ required: false
714
+ },
715
+ autoFocus: {
716
+ type: Boolean,
717
+ required: false
718
+ }
719
+ };
720
+ const DestylerComboboxInput = defineComponent({
721
+ name: "DestylerComboboxInput",
722
+ props: destylerComboboxInputProps,
723
+ setup(props) {
724
+ const rootContext = injectComboboxRootContext();
725
+ const { forwardRef, currentElement } = useForwardExpose();
726
+ onMounted(() => {
727
+ const inputEl = currentElement.value.nodeName === "INPUT" ? currentElement.value : currentElement.value.querySelector("input");
728
+ if (!inputEl)
729
+ return;
730
+ rootContext.onInputElementChange(inputEl);
731
+ setTimeout(() => {
732
+ if (props.autoFocus)
733
+ inputEl?.focus();
734
+ }, 1);
735
+ });
736
+ const disabled = computed(() => props.disabled || rootContext.disabled.value || false);
737
+ function handleKeyDown(ev) {
738
+ if (!rootContext.open.value)
739
+ rootContext.onOpenChange(true);
740
+ else
741
+ rootContext.onInputNavigation(ev.key === "ArrowUp" ? "up" : "down");
742
+ }
743
+ function handleHomeEnd(ev) {
744
+ if (!rootContext.open.value)
745
+ return;
746
+ rootContext.onInputNavigation(ev.key === "Home" ? "home" : "end");
747
+ }
748
+ function handleInput(event) {
749
+ rootContext.searchTerm.value = event.target?.value;
750
+ if (!rootContext.open.value)
751
+ rootContext.onOpenChange(true);
752
+ rootContext.isUserInputted.value = true;
753
+ }
754
+ return {
755
+ disabled,
756
+ rootContext,
757
+ forwardRef,
758
+ handleInput,
759
+ handleKeyDown,
760
+ handleHomeEnd
761
+ };
762
+ },
763
+ render() {
764
+ return h(DestylerPrimitive, {
765
+ "ref": (el) => this.forwardRef(el),
766
+ "as": this.$props.as,
767
+ "asChild": this.$props.asChild,
768
+ "type": this.$props.type,
769
+ "disabled": this.disabled,
770
+ "value": this.rootContext.searchTerm.value,
771
+ "aria-expanded": this.rootContext.open.value,
772
+ "aria-controls": this.rootContext.contentId,
773
+ "aria-disabled": this.disabled ?? void 0,
774
+ "aria-autocomplete": "list",
775
+ "role": "combobox",
776
+ "autocomplete": "false",
777
+ "onInput": (event) => {
778
+ this.handleInput(event);
779
+ },
780
+ "onKeydown": withKeys((event) => {
781
+ switch (event.key) {
782
+ case "ArrowUp":
783
+ case "ArrowDown":
784
+ event.preventDefault();
785
+ this.handleKeyDown(event);
786
+ break;
787
+ case "Home":
788
+ case "End":
789
+ event.preventDefault();
790
+ this.handleHomeEnd(event);
791
+ break;
792
+ case "Enter":
793
+ this.rootContext.onInputEnter();
794
+ break;
795
+ }
796
+ }, ["down", "up", "enter", "home", "end"])
797
+ }, {
798
+ default: () => this.$slots.default?.()
799
+ });
800
+ }
801
+ });
802
+
803
+ const COMBOBOX_SELECT = "combobox.select";
804
+ const destylerComboboxItemProps = {
805
+ as: {
806
+ type: [String, Object],
807
+ required: false,
808
+ default: "div"
809
+ },
810
+ asChild: {
811
+ type: Boolean,
812
+ required: false,
813
+ default: false
814
+ },
815
+ value: {
816
+ type: [String, Number, Boolean, Object],
817
+ required: true
818
+ },
819
+ disabled: {
820
+ type: Boolean,
821
+ required: false
822
+ }
823
+ };
824
+ const [injectComboboxItemContext, provideComboboxItemContext] = createContext("DestylerComboboxItem");
825
+ const DestylerComboboxItem = defineComponent({
826
+ name: "DestylerComboboxItem",
827
+ props: destylerComboboxItemProps,
828
+ emits: ["select"],
829
+ setup(props, { emit }) {
830
+ const { disabled } = toRefs(props);
831
+ const rootContext = injectComboboxRootContext();
832
+ injectComboboxGroupContext({ id: "", options: ref([]) });
833
+ const { forwardRef } = useForwardExpose();
834
+ const isSelected = computed(
835
+ () => rootContext.multiple.value && Array.isArray(rootContext.modelValue.value) ? rootContext.modelValue.value?.includes(props.value) : isEqual(rootContext.modelValue?.value, props.value)
836
+ );
837
+ const isFocused = computed(() => isEqual(rootContext.selectedValue.value, props.value));
838
+ const textId = useId();
839
+ const isInOption = computed(() => rootContext.isUserInputted.value ? rootContext.searchTerm.value === "" || !!rootContext.filteredOptions.value.find((i) => isEqual(i, props.value)) : true);
840
+ async function handleSelect(ev) {
841
+ emit("select", ev);
842
+ if (ev?.defaultPrevented)
843
+ return;
844
+ if (!disabled.value && ev)
845
+ rootContext.onValueChange(props.value);
846
+ }
847
+ function handleSelectCustomEvent(ev) {
848
+ if (!ev)
849
+ return;
850
+ const eventDetail = { originalEvent: ev, value: props.value };
851
+ handleAndDispatchCustomEvent(COMBOBOX_SELECT, handleSelect, eventDetail);
852
+ }
853
+ async function handlePointerMove(event) {
854
+ await nextTick();
855
+ if (event.defaultPrevented)
856
+ return;
857
+ rootContext.onSelectedValueChange(props.value);
858
+ }
859
+ if (props.value === "") {
860
+ throw new Error(
861
+ "A <DestylerSelectItem /> must have a value prop that is not an empty string. This is because the Select value can be set to an empty string to clear the selection and show the placeholder."
862
+ );
863
+ }
864
+ provideComboboxItemContext({
865
+ isSelected
866
+ });
867
+ return {
868
+ forwardRef,
869
+ textId,
870
+ isFocused,
871
+ isSelected,
872
+ isInOption,
873
+ handleSelectCustomEvent,
874
+ handlePointerMove
875
+ };
876
+ },
877
+ render() {
878
+ return h(DestylerCollectionItem, null, {
879
+ default: () => {
880
+ return h(DestylerPrimitive, {
881
+ "ref": (el) => this.forwardRef(el),
882
+ "role": "option",
883
+ "tabindex": "-1",
884
+ "aria-labelledby": this.textId,
885
+ "data-highlighted": this.isFocused ? "" : void 0,
886
+ "aria-selected": this.isSelected,
887
+ "data-state": this.isSelected ? "checked" : "unchecked",
888
+ "aria-disabled": this.$props.disabled || void 0,
889
+ "data-disabled": this.$props.disabled ? "" : void 0,
890
+ "as": this.$props.as,
891
+ "asChild": this.$props.asChild,
892
+ "data-hidden": !this.isInOption ? true : void 0,
893
+ "onClick": (event) => {
894
+ this.handleSelectCustomEvent(event);
895
+ },
896
+ "onPointermove": (event) => {
897
+ this.handlePointerMove(event);
898
+ },
899
+ ...this.isInOption ? {} : { style: { display: "none" } }
900
+ }, {
901
+ default: () => this.$slots.default ? this.$slots.default?.() : this.$props.value
902
+ });
903
+ }
904
+ });
905
+ }
906
+ });
907
+
908
+ const destylerComboboxItemIndicatorProps = {
909
+ as: {
910
+ type: [String, Object],
911
+ required: false,
912
+ default: "span"
913
+ },
914
+ asChild: {
915
+ type: Boolean,
916
+ required: false,
917
+ default: false
918
+ }
919
+ };
920
+ const DestylerComboboxItemIndicator = defineComponent({
921
+ name: "DestylerComboboxItemIndicator",
922
+ props: destylerComboboxItemIndicatorProps,
923
+ setup() {
924
+ useForwardExpose();
925
+ const itemContext = injectComboboxItemContext();
926
+ return {
927
+ itemContext
928
+ };
929
+ },
930
+ render() {
931
+ if (this.itemContext.isSelected.value) {
932
+ return h(DestylerPrimitive, mergeProps(this.$props, {
933
+ "aria-hidden": ""
934
+ }), {
935
+ default: () => this.$slots.default?.()
936
+ });
937
+ }
938
+ }
939
+ });
940
+
941
+ const destylerComboboxLabelProps = {
942
+ as: {
943
+ type: [String, Object],
944
+ required: false,
945
+ default: "div"
946
+ },
947
+ asChild: {
948
+ type: Boolean,
949
+ required: false,
950
+ default: false
951
+ },
952
+ for: {
953
+ type: String,
954
+ required: false
955
+ }
956
+ };
957
+ const DestylerComboboxLabel = defineComponent({
958
+ name: "DestylerComboboxLabel",
959
+ props: destylerComboboxLabelProps,
960
+ setup() {
961
+ useForwardExpose();
962
+ const groupContext = injectComboboxGroupContext({ id: "" });
963
+ return {
964
+ groupContext
965
+ };
966
+ },
967
+ render() {
968
+ return withDirectives(h(DestylerPrimitive, this.$props, {
969
+ default: () => this.$slots.default?.()
970
+ }), [
971
+ [BindOnceDirective, { id: this.groupContext.id }]
972
+ ]);
973
+ }
974
+ });
975
+
976
+ const destylerComboboxPortalProps = {
977
+ to: {
978
+ type: [String, Object],
979
+ required: false,
980
+ default: "body"
981
+ },
982
+ disabled: {
983
+ type: Boolean,
984
+ required: false,
985
+ default: false
986
+ },
987
+ forceMount: {
988
+ type: Boolean,
989
+ required: false,
990
+ default: false
991
+ }
992
+ };
993
+ const DestylerComboboxPortal = defineComponent({
994
+ name: "DestylerComboboxPortal",
995
+ props: destylerComboboxPortalProps,
996
+ render() {
997
+ return h(DestylerTeleport, this.$props, {
998
+ default: () => this.$slots.default?.()
999
+ });
1000
+ }
1001
+ });
1002
+
1003
+ const destylerComboboxSeparatorProps = {
1004
+ as: {
1005
+ type: [String, Object],
1006
+ required: false,
1007
+ default: "div"
1008
+ },
1009
+ asChild: {
1010
+ type: Boolean,
1011
+ required: false,
1012
+ default: false
1013
+ }
1014
+ };
1015
+ const DestylerComboboxSeparator = defineComponent({
1016
+ name: "DestylerComboboxSeparator",
1017
+ props: destylerComboboxSeparatorProps,
1018
+ setup() {
1019
+ useForwardExpose();
1020
+ },
1021
+ render() {
1022
+ return h(DestylerPrimitive, mergeProps(this.$props, {
1023
+ "aria-hidden": ""
1024
+ }));
1025
+ }
1026
+ });
1027
+
1028
+ const destylerComboboxTriggerProps = {
1029
+ as: {
1030
+ type: [String, Object],
1031
+ required: false,
1032
+ default: "button"
1033
+ },
1034
+ asChild: {
1035
+ type: Boolean,
1036
+ required: false,
1037
+ default: false
1038
+ },
1039
+ disabled: {
1040
+ type: Boolean,
1041
+ required: false
1042
+ }
1043
+ };
1044
+ const DestylerComboboxTrigger = defineComponent({
1045
+ name: "DestylerComboboxTrigger",
1046
+ props: destylerComboboxTriggerProps,
1047
+ setup(props) {
1048
+ useForwardExpose();
1049
+ const rootContext = injectComboboxRootContext();
1050
+ const disabled = computed(() => props.disabled || rootContext.disabled.value || false);
1051
+ return {
1052
+ rootContext,
1053
+ disabled
1054
+ };
1055
+ },
1056
+ render() {
1057
+ return h(DestylerPrimitive, mergeProps(this.$props, {
1058
+ "type": this.$props.as === "button" ? "button" : void 0,
1059
+ "tabindex": "-1",
1060
+ "aria-label": "Show popup",
1061
+ "aria-haspopup": "listbox",
1062
+ "aria-expanded": this.rootContext.open.value,
1063
+ "aria-controls": this.rootContext.contentId,
1064
+ "data-state": this.rootContext.open.value ? "open" : "closed",
1065
+ "disabled": this.disabled,
1066
+ "data-disabled": this.disabled ? "" : void 0,
1067
+ "aria-disabled": this.disabled ?? void 0,
1068
+ "onClick": () => {
1069
+ this.rootContext.onOpenChange(!this.rootContext.open.value);
1070
+ }
1071
+ }), {
1072
+ default: () => this.$slots.default?.()
1073
+ });
1074
+ }
1075
+ });
1076
+
1077
+ const destylerComboboxViewportProps = {
1078
+ as: {
1079
+ type: [String, Object],
1080
+ required: false,
1081
+ default: "div"
1082
+ },
1083
+ asChild: {
1084
+ type: Boolean,
1085
+ required: false,
1086
+ default: false
1087
+ }
1088
+ };
1089
+ const DestylerComboboxViewport = defineComponent({
1090
+ name: "DestylerComboboxViewport",
1091
+ props: destylerComboboxViewportProps,
1092
+ setup() {
1093
+ const { forwardRef } = useForwardExpose();
1094
+ return {
1095
+ forwardRef
1096
+ };
1097
+ },
1098
+ render() {
1099
+ return [
1100
+ h(DestylerPrimitive, {
1101
+ "ref": (el) => this.forwardRef(el),
1102
+ "data-destyler-combobox-viewport": "",
1103
+ "role": "presentation",
1104
+ "style": {
1105
+ position: "relative",
1106
+ flex: 1,
1107
+ overflow: "auto"
1108
+ }
1109
+ }, {
1110
+ default: () => this.$slots.default?.()
1111
+ }),
1112
+ h(DestylerPrimitive, {
1113
+ as: "style"
1114
+ }, {
1115
+ default: () => `
1116
+ /* Hide scrollbars cross-browser and enable momentum scroll for touch devices */
1117
+ [data-destyler-combobox-viewport] {
1118
+ scrollbar-width:none;
1119
+ -ms-overflow-style: none;
1120
+ -webkit-overflow-scrolling: touch;
1121
+ }
1122
+ [data-destyler-combobox-viewport]::-webkit-scrollbar {
1123
+ display: none;
1124
+ }
1125
+ `
1126
+ })
1127
+ ];
1128
+ }
1129
+ });
1130
+
1131
+ export { DestylerComboboxAnchor, DestylerComboboxArrow, DestylerComboboxCancel, DestylerComboboxContent, DestylerComboboxContentImpl, DestylerComboboxEmpty, DestylerComboboxGroup, DestylerComboboxInput, DestylerComboboxItem, DestylerComboboxItemIndicator, DestylerComboboxLabel, DestylerComboboxPortal, DestylerComboboxRoot, DestylerComboboxSeparator, DestylerComboboxTrigger, DestylerComboboxViewport };