sprintify-ui 0.10.25 → 0.10.27
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/sprintify-ui.es.js +7752 -7719
- package/dist/types/components/BaseAutocomplete.vue.d.ts +26 -6
- package/dist/types/components/BaseTagAutocomplete.vue.d.ts +26 -6
- package/package.json +1 -1
- package/src/components/BaseAutocomplete.stories.js +8 -2
- package/src/components/BaseAutocomplete.vue +33 -9
- package/src/components/BaseBelongsTo.vue +4 -3
- package/src/components/BaseDataIterator.vue +10 -1
- package/src/components/BaseTagAutocomplete.stories.js +10 -4
- package/src/components/BaseTagAutocomplete.vue +33 -7
|
@@ -9,6 +9,8 @@ import BaseInput from './BaseInput.vue';
|
|
|
9
9
|
import BaseSelect from './BaseSelect.vue';
|
|
10
10
|
declare const emit: (event: "close" | "select" | "open" | "clear" | "update:modelValue" | "scrollBottom" | "typing", ...args: any[]) => void;
|
|
11
11
|
declare const hasErrorInternal: ComputedRef<boolean>, sizeInternal: ComputedRef<"xs" | "sm" | "md" | "lg" | "xl">;
|
|
12
|
+
declare const inputRef: import("vue").Ref<HTMLElement | null, HTMLElement | null>;
|
|
13
|
+
declare const dropdownRef: import("vue").Ref<HTMLElement | null, HTMLElement | null>;
|
|
12
14
|
declare const drawer: import("vue").Ref<({
|
|
13
15
|
$: import("vue").ComponentInternalInstance;
|
|
14
16
|
$data: {};
|
|
@@ -349,18 +351,32 @@ declare const slotProps: {
|
|
|
349
351
|
close: typeof close;
|
|
350
352
|
keywords: ComputedRef<string>;
|
|
351
353
|
};
|
|
354
|
+
declare const inputRefWidth: ComputedRef<number>;
|
|
355
|
+
declare const floatingStyles: Readonly<import("vue").Ref<{
|
|
356
|
+
position: import("@floating-ui/utils").Strategy;
|
|
357
|
+
top: string;
|
|
358
|
+
left: string;
|
|
359
|
+
transform?: string;
|
|
360
|
+
willChange?: string;
|
|
361
|
+
}, {
|
|
362
|
+
position: import("@floating-ui/utils").Strategy;
|
|
363
|
+
top: string;
|
|
364
|
+
left: string;
|
|
365
|
+
transform?: string;
|
|
366
|
+
willChange?: string;
|
|
367
|
+
}>>;
|
|
352
368
|
declare const deleteButtonIconClasses: ComputedRef<string[]>;
|
|
353
369
|
declare const inputClasses: ComputedRef<string | string[]>;
|
|
354
370
|
declare const selectClasses: ComputedRef<string | string[]>;
|
|
355
371
|
declare const showRemoveButtonInternal: ComputedRef<boolean | null>;
|
|
356
372
|
declare const __VLS_ctx: InstanceType<__VLS_PickNotAny<typeof __VLS_self, new () => {}>>;
|
|
357
|
-
declare var
|
|
373
|
+
declare var __VLS_37: {
|
|
358
374
|
focus: typeof focus;
|
|
359
375
|
blur: typeof blur;
|
|
360
376
|
open: typeof open;
|
|
361
377
|
close: typeof close;
|
|
362
378
|
keywords: ComputedRef<string>;
|
|
363
|
-
},
|
|
379
|
+
}, __VLS_39: {
|
|
364
380
|
focus: typeof focus;
|
|
365
381
|
blur: typeof blur;
|
|
366
382
|
open: typeof open;
|
|
@@ -371,7 +387,7 @@ declare var __VLS_33: {
|
|
|
371
387
|
label: string;
|
|
372
388
|
selected: boolean;
|
|
373
389
|
active: boolean;
|
|
374
|
-
},
|
|
390
|
+
}, __VLS_41: {
|
|
375
391
|
focus: typeof focus;
|
|
376
392
|
blur: typeof blur;
|
|
377
393
|
open: typeof open;
|
|
@@ -380,11 +396,11 @@ declare var __VLS_33: {
|
|
|
380
396
|
options: NormalizedOption[];
|
|
381
397
|
};
|
|
382
398
|
type __VLS_Slots = __VLS_PrettifyGlobal<__VLS_OmitStringIndex<typeof __VLS_ctx.$slots> & {
|
|
383
|
-
empty?: (props: typeof
|
|
399
|
+
empty?: (props: typeof __VLS_37) => any;
|
|
384
400
|
} & {
|
|
385
|
-
option?: (props: typeof
|
|
401
|
+
option?: (props: typeof __VLS_39) => any;
|
|
386
402
|
} & {
|
|
387
|
-
footer?: (props: typeof
|
|
403
|
+
footer?: (props: typeof __VLS_41) => any;
|
|
388
404
|
}>;
|
|
389
405
|
declare const __VLS_self: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
390
406
|
modelValue: {
|
|
@@ -494,6 +510,8 @@ declare const __VLS_self: import("vue").DefineComponent<import("vue").ExtractPro
|
|
|
494
510
|
emit: typeof emit;
|
|
495
511
|
hasErrorInternal: typeof hasErrorInternal;
|
|
496
512
|
sizeInternal: typeof sizeInternal;
|
|
513
|
+
inputRef: typeof inputRef;
|
|
514
|
+
dropdownRef: typeof dropdownRef;
|
|
497
515
|
drawer: typeof drawer;
|
|
498
516
|
keywords: typeof keywords;
|
|
499
517
|
autocomplete: typeof autocomplete;
|
|
@@ -510,6 +528,8 @@ declare const __VLS_self: import("vue").DefineComponent<import("vue").ExtractPro
|
|
|
510
528
|
selection: typeof selection;
|
|
511
529
|
onSelectChange: typeof onSelectChange;
|
|
512
530
|
slotProps: typeof slotProps;
|
|
531
|
+
inputRefWidth: typeof inputRefWidth;
|
|
532
|
+
floatingStyles: typeof floatingStyles;
|
|
513
533
|
deleteButtonIconClasses: typeof deleteButtonIconClasses;
|
|
514
534
|
inputClasses: typeof inputClasses;
|
|
515
535
|
selectClasses: typeof selectClasses;
|
|
@@ -4,6 +4,8 @@ import BaseAutocompleteDrawer from './BaseAutocompleteDrawer.vue';
|
|
|
4
4
|
import { t } from '@/i18n';
|
|
5
5
|
import { Size } from '@/utils/sizes';
|
|
6
6
|
declare const emit: (event: "close" | "open" | "update:modelValue" | "scrollBottom" | "typing", ...args: any[]) => void;
|
|
7
|
+
declare const inputRef: import("vue").Ref<HTMLElement | null, HTMLElement | null>;
|
|
8
|
+
declare const dropdownRef: import("vue").Ref<HTMLElement | null, HTMLElement | null>;
|
|
7
9
|
declare const drawer: import("vue").Ref<({
|
|
8
10
|
$: import("vue").ComponentInternalInstance;
|
|
9
11
|
$data: {};
|
|
@@ -341,6 +343,20 @@ declare const slotProps: {
|
|
|
341
343
|
keywords: ComputedRef<string>;
|
|
342
344
|
};
|
|
343
345
|
declare const removeOption: (option: NormalizedOption) => void;
|
|
346
|
+
declare const inputRefWidth: ComputedRef<number>;
|
|
347
|
+
declare const floatingStyles: Readonly<import("vue").Ref<{
|
|
348
|
+
position: import("@floating-ui/utils").Strategy;
|
|
349
|
+
top: string;
|
|
350
|
+
left: string;
|
|
351
|
+
transform?: string;
|
|
352
|
+
willChange?: string;
|
|
353
|
+
}, {
|
|
354
|
+
position: import("@floating-ui/utils").Strategy;
|
|
355
|
+
top: string;
|
|
356
|
+
left: string;
|
|
357
|
+
transform?: string;
|
|
358
|
+
willChange?: string;
|
|
359
|
+
}>>;
|
|
344
360
|
declare const wrapperClass: ComputedRef<string | string[]>;
|
|
345
361
|
declare const inputClasses: ComputedRef<string[]>;
|
|
346
362
|
declare const selectionClass: (selection: NormalizedOption) => string;
|
|
@@ -350,13 +366,13 @@ declare var __VLS_1: {
|
|
|
350
366
|
items: NormalizedOption[];
|
|
351
367
|
removeOption: (option: NormalizedOption) => void;
|
|
352
368
|
disabled: boolean;
|
|
353
|
-
},
|
|
369
|
+
}, __VLS_17: {
|
|
354
370
|
focus: typeof focus;
|
|
355
371
|
blur: typeof blur;
|
|
356
372
|
open: typeof open;
|
|
357
373
|
close: typeof close;
|
|
358
374
|
keywords: ComputedRef<string>;
|
|
359
|
-
},
|
|
375
|
+
}, __VLS_19: {
|
|
360
376
|
focus: typeof focus;
|
|
361
377
|
blur: typeof blur;
|
|
362
378
|
open: typeof open;
|
|
@@ -367,7 +383,7 @@ declare var __VLS_1: {
|
|
|
367
383
|
label: string;
|
|
368
384
|
selected: boolean;
|
|
369
385
|
active: boolean;
|
|
370
|
-
},
|
|
386
|
+
}, __VLS_21: {
|
|
371
387
|
focus: typeof focus;
|
|
372
388
|
blur: typeof blur;
|
|
373
389
|
open: typeof open;
|
|
@@ -378,11 +394,11 @@ declare var __VLS_1: {
|
|
|
378
394
|
type __VLS_Slots = __VLS_PrettifyGlobal<__VLS_OmitStringIndex<typeof __VLS_ctx.$slots> & {
|
|
379
395
|
items?: (props: typeof __VLS_1) => any;
|
|
380
396
|
} & {
|
|
381
|
-
empty?: (props: typeof
|
|
397
|
+
empty?: (props: typeof __VLS_17) => any;
|
|
382
398
|
} & {
|
|
383
|
-
option?: (props: typeof
|
|
399
|
+
option?: (props: typeof __VLS_19) => any;
|
|
384
400
|
} & {
|
|
385
|
-
footer?: (props: typeof
|
|
401
|
+
footer?: (props: typeof __VLS_21) => any;
|
|
386
402
|
}>;
|
|
387
403
|
declare const __VLS_self: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
388
404
|
modelValue: {
|
|
@@ -465,6 +481,8 @@ declare const __VLS_self: import("vue").DefineComponent<import("vue").ExtractPro
|
|
|
465
481
|
BaseAutocompleteDrawer: typeof BaseAutocompleteDrawer;
|
|
466
482
|
t: typeof t;
|
|
467
483
|
emit: typeof emit;
|
|
484
|
+
inputRef: typeof inputRef;
|
|
485
|
+
dropdownRef: typeof dropdownRef;
|
|
468
486
|
drawer: typeof drawer;
|
|
469
487
|
keywords: typeof keywords;
|
|
470
488
|
autocomplete: typeof autocomplete;
|
|
@@ -478,6 +496,8 @@ declare const __VLS_self: import("vue").DefineComponent<import("vue").ExtractPro
|
|
|
478
496
|
onSelect: typeof onSelect;
|
|
479
497
|
slotProps: typeof slotProps;
|
|
480
498
|
removeOption: typeof removeOption;
|
|
499
|
+
inputRefWidth: typeof inputRefWidth;
|
|
500
|
+
floatingStyles: typeof floatingStyles;
|
|
481
501
|
wrapperClass: typeof wrapperClass;
|
|
482
502
|
inputClasses: typeof inputClasses;
|
|
483
503
|
selectionClass: typeof selectionClass;
|
package/package.json
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { createFieldStory, options, sizes } from "../../.storybook/utils";
|
|
2
2
|
import BaseAutocomplete from "./BaseAutocomplete.vue";
|
|
3
|
+
import BaseCard from "./BaseCard.vue";
|
|
4
|
+
import BaseCardRow from "./BaseCardRow.vue";
|
|
3
5
|
import ShowValue from "@/../.storybook/components/ShowValue.vue";
|
|
4
6
|
import { computed } from "vue";
|
|
5
7
|
|
|
@@ -25,13 +27,17 @@ export default {
|
|
|
25
27
|
};
|
|
26
28
|
|
|
27
29
|
const Template = (args) => ({
|
|
28
|
-
components: { BaseAutocomplete, ShowValue },
|
|
30
|
+
components: { BaseAutocomplete, BaseCard, BaseCardRow, ShowValue },
|
|
29
31
|
setup() {
|
|
30
32
|
const value = ref(options[2]);
|
|
31
33
|
return { args, value };
|
|
32
34
|
},
|
|
33
35
|
template: `
|
|
34
|
-
<
|
|
36
|
+
<BaseCard clipped>
|
|
37
|
+
<BaseCardRow>
|
|
38
|
+
<BaseAutocomplete v-model="value" v-bind="args"></BaseAutocomplete>
|
|
39
|
+
</BaseCardRow>
|
|
40
|
+
</BaseCard>
|
|
35
41
|
<ShowValue :value="value" />
|
|
36
42
|
`,
|
|
37
43
|
});
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div ref="autocomplete">
|
|
3
|
-
<div
|
|
3
|
+
<div
|
|
4
|
+
ref="inputRef"
|
|
5
|
+
class="relative z-[1]"
|
|
6
|
+
>
|
|
4
7
|
<div
|
|
5
8
|
class="flex"
|
|
6
9
|
:class="[
|
|
@@ -61,15 +64,19 @@
|
|
|
61
64
|
</button>
|
|
62
65
|
</div>
|
|
63
66
|
</div>
|
|
64
|
-
|
|
65
|
-
|
|
67
|
+
<Teleport
|
|
68
|
+
to="body"
|
|
69
|
+
:disabled="inline"
|
|
70
|
+
>
|
|
66
71
|
<div
|
|
67
72
|
v-show="dropdownOpened"
|
|
68
|
-
|
|
73
|
+
ref="dropdownRef"
|
|
74
|
+
:style="!inline ? { ...floatingStyles, width: inputRefWidth + 'px' } : {}"
|
|
75
|
+
class="min-h-[110px] overflow-hidden bg-white border-slate-300 input-rounded"
|
|
69
76
|
:class="[
|
|
70
77
|
inline
|
|
71
78
|
? 'relative border-b border-x rounded-t-none'
|
|
72
|
-
: '
|
|
79
|
+
: 'fixed border shadow-2xl z-menu',
|
|
73
80
|
]"
|
|
74
81
|
>
|
|
75
82
|
<BaseAutocompleteDrawer
|
|
@@ -104,7 +111,7 @@
|
|
|
104
111
|
</template>
|
|
105
112
|
</BaseAutocompleteDrawer>
|
|
106
113
|
</div>
|
|
107
|
-
</
|
|
114
|
+
</Teleport>
|
|
108
115
|
</div>
|
|
109
116
|
</template>
|
|
110
117
|
|
|
@@ -122,6 +129,8 @@ import { Size, sizes } from '@/utils/sizes';
|
|
|
122
129
|
import BaseInput from './BaseInput.vue';
|
|
123
130
|
import BaseSelect from './BaseSelect.vue';
|
|
124
131
|
import { twMerge } from 'tailwind-merge';
|
|
132
|
+
import { autoUpdate, flip, offset, useFloating } from '@floating-ui/vue';
|
|
133
|
+
import { useElementBounding } from '@vueuse/core';
|
|
125
134
|
|
|
126
135
|
const props = defineProps({
|
|
127
136
|
modelValue: {
|
|
@@ -266,6 +275,8 @@ onBeforeUnmount(() => {
|
|
|
266
275
|
clearTimeout(openOfFocusTimeout);
|
|
267
276
|
});
|
|
268
277
|
|
|
278
|
+
const inputRef = ref<HTMLElement | null>(null);
|
|
279
|
+
const dropdownRef = ref<HTMLElement | null>(null);
|
|
269
280
|
const drawer = ref<InstanceType<typeof BaseAutocompleteDrawer> | null>(null);
|
|
270
281
|
|
|
271
282
|
let timerId = 0;
|
|
@@ -279,8 +290,7 @@ const opened = ref(false);
|
|
|
279
290
|
const dropdownOpened = computed(() => opened.value || props.dropdownShow == 'always');
|
|
280
291
|
|
|
281
292
|
const normalizedOptions = hasOptions.normalizedOptions;
|
|
282
|
-
const normalizedModelValue =
|
|
283
|
-
hasOptions.normalizedModelValue as ComputedRef<NormalizedOption | null>;
|
|
293
|
+
const normalizedModelValue = hasOptions.normalizedModelValue as ComputedRef<NormalizedOption | null>;
|
|
284
294
|
|
|
285
295
|
const filteredNormalizedOptions = computed((): NormalizedOption[] => {
|
|
286
296
|
let options = normalizedOptions.value;
|
|
@@ -330,7 +340,7 @@ watch(
|
|
|
330
340
|
|
|
331
341
|
useClickOutside(autocomplete, () => {
|
|
332
342
|
close();
|
|
333
|
-
});
|
|
343
|
+
}, { includes: () => [dropdownRef] });
|
|
334
344
|
|
|
335
345
|
onMounted(() => {
|
|
336
346
|
window.addEventListener('keydown', onWindowKeydown);
|
|
@@ -510,6 +520,20 @@ const slotProps = {
|
|
|
510
520
|
keywords: computed(() => keywords.value),
|
|
511
521
|
};
|
|
512
522
|
|
|
523
|
+
// Floating UI
|
|
524
|
+
|
|
525
|
+
const inputRefRect = useElementBounding(inputRef);
|
|
526
|
+
const inputRefWidth = computed(() => {
|
|
527
|
+
return inputRefRect.width.value;
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
const { floatingStyles } = useFloating(inputRef, dropdownRef, {
|
|
531
|
+
middleware: [offset(4), flip({
|
|
532
|
+
fallbackPlacements: ['right', 'bottom'],
|
|
533
|
+
})],
|
|
534
|
+
whileElementsMounted: autoUpdate,
|
|
535
|
+
});
|
|
536
|
+
|
|
513
537
|
// Element Classes
|
|
514
538
|
|
|
515
539
|
const deleteButtonIconClasses = computed(() => {
|
|
@@ -135,9 +135,10 @@ const emit = defineEmits(['update:modelValue']);
|
|
|
135
135
|
const autocomplete = ref<InstanceType<typeof BaseAutocomplete> | null>(null);
|
|
136
136
|
|
|
137
137
|
const model = computed(() => {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
138
|
+
if (!props.modelValue) {
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
return props.options.find((option) => (option[props.primaryKey] + '') === (props.modelValue + ''));
|
|
141
142
|
});
|
|
142
143
|
|
|
143
144
|
function onUpdate(newModel: RawOption | null) {
|
|
@@ -198,7 +198,7 @@ const DEFAULT_QUERY = {
|
|
|
198
198
|
</script>
|
|
199
199
|
|
|
200
200
|
<script lang="ts" setup>
|
|
201
|
-
import { cloneDeep, debounce, merge, set, sortBy } from 'lodash';
|
|
201
|
+
import { cloneDeep, debounce, merge, set, sortBy, uniqueId } from 'lodash';
|
|
202
202
|
import hash from 'object-hash';
|
|
203
203
|
import { PropType } from 'vue';
|
|
204
204
|
import {
|
|
@@ -661,6 +661,8 @@ function fetchWithoutLoading(force = false) {
|
|
|
661
661
|
fetch(force, false);
|
|
662
662
|
}
|
|
663
663
|
|
|
664
|
+
let requestId = '';
|
|
665
|
+
|
|
664
666
|
function fetch(force = false, showLoading = true) {
|
|
665
667
|
if (willUnmount) {
|
|
666
668
|
return;
|
|
@@ -682,6 +684,9 @@ function fetch(force = false, showLoading = true) {
|
|
|
682
684
|
return;
|
|
683
685
|
}
|
|
684
686
|
|
|
687
|
+
const requestIdInternal = uniqueId();
|
|
688
|
+
requestId = requestIdInternal;
|
|
689
|
+
|
|
685
690
|
if (showLoading) {
|
|
686
691
|
loading.value = true;
|
|
687
692
|
}
|
|
@@ -691,6 +696,10 @@ function fetch(force = false, showLoading = true) {
|
|
|
691
696
|
http
|
|
692
697
|
.get(fullUrl)
|
|
693
698
|
.then((response) => {
|
|
699
|
+
if (requestIdInternal !== requestId) {
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
|
|
694
703
|
data.value = response.data;
|
|
695
704
|
error.value = false;
|
|
696
705
|
firstLoad.value = true;
|
|
@@ -2,6 +2,8 @@ import { createFieldStory, options, sizes } from "../../.storybook/utils";
|
|
|
2
2
|
import BaseTagAutocomplete from "./BaseTagAutocomplete.vue";
|
|
3
3
|
import ShowValue from "@/../.storybook/components/ShowValue.vue";
|
|
4
4
|
import BaseAppSnackbars from "./BaseAppSnackbars.vue";
|
|
5
|
+
import BaseCard from "./BaseCard.vue";
|
|
6
|
+
import BaseCardRow from "./BaseCardRow.vue";
|
|
5
7
|
|
|
6
8
|
export default {
|
|
7
9
|
title: "Form/BaseTagAutocomplete",
|
|
@@ -25,15 +27,19 @@ export default {
|
|
|
25
27
|
};
|
|
26
28
|
|
|
27
29
|
const Template = (args) => ({
|
|
28
|
-
components: { BaseTagAutocomplete, ShowValue, BaseAppSnackbars },
|
|
30
|
+
components: { BaseTagAutocomplete, BaseCard, BaseCardRow, ShowValue, BaseAppSnackbars },
|
|
29
31
|
setup() {
|
|
30
32
|
const value = ref(null);
|
|
31
33
|
return { args, value };
|
|
32
34
|
},
|
|
33
35
|
template: `
|
|
34
|
-
|
|
35
|
-
<
|
|
36
|
-
|
|
36
|
+
<BaseCard clipped>
|
|
37
|
+
<BaseCardRow>
|
|
38
|
+
<BaseTagAutocomplete v-model="value" v-bind="args"></BaseTagAutocomplete>
|
|
39
|
+
</BaseCardRow>
|
|
40
|
+
</BaseCard>
|
|
41
|
+
<ShowValue :value="value" />
|
|
42
|
+
<BaseAppSnackbars />
|
|
37
43
|
`,
|
|
38
44
|
});
|
|
39
45
|
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div ref="autocomplete">
|
|
3
|
-
<div
|
|
3
|
+
<div
|
|
4
|
+
ref="inputRef"
|
|
5
|
+
:class="wrapperClass"
|
|
6
|
+
>
|
|
4
7
|
<slot
|
|
5
8
|
name="items"
|
|
6
9
|
:items="normalizedModelValue"
|
|
@@ -44,14 +47,19 @@
|
|
|
44
47
|
>
|
|
45
48
|
</div>
|
|
46
49
|
|
|
47
|
-
<
|
|
50
|
+
<Teleport
|
|
51
|
+
to="body"
|
|
52
|
+
:disabled="inline"
|
|
53
|
+
>
|
|
48
54
|
<div
|
|
49
|
-
v-
|
|
50
|
-
|
|
55
|
+
v-show="dropdownOpened"
|
|
56
|
+
ref="dropdownRef"
|
|
57
|
+
:style="!inline ? { ...floatingStyles, width: inputRefWidth + 'px' } : {}"
|
|
58
|
+
class="min-h-[110px] overflow-hidden bg-white border-slate-300 input-rounded"
|
|
51
59
|
:class="[
|
|
52
60
|
inline
|
|
53
61
|
? 'relative border-b border-x rounded-t-none'
|
|
54
|
-
: '
|
|
62
|
+
: 'fixed border shadow-2xl z-menu',
|
|
55
63
|
]"
|
|
56
64
|
>
|
|
57
65
|
<BaseAutocompleteDrawer
|
|
@@ -86,7 +94,7 @@
|
|
|
86
94
|
</template>
|
|
87
95
|
</BaseAutocompleteDrawer>
|
|
88
96
|
</div>
|
|
89
|
-
</
|
|
97
|
+
</Teleport>
|
|
90
98
|
</div>
|
|
91
99
|
</template>
|
|
92
100
|
|
|
@@ -102,6 +110,8 @@ import BaseAutocompleteDrawer from './BaseAutocompleteDrawer.vue';
|
|
|
102
110
|
import { twMerge } from 'tailwind-merge';
|
|
103
111
|
import { t } from '@/i18n';
|
|
104
112
|
import { Size, sizes } from '@/utils/sizes';
|
|
113
|
+
import { autoUpdate, flip, offset, useFloating } from '@floating-ui/vue';
|
|
114
|
+
import { useElementBounding } from '@vueuse/core';
|
|
105
115
|
|
|
106
116
|
const snackbars = useSnackbarsStore();
|
|
107
117
|
|
|
@@ -248,6 +258,8 @@ onBeforeUnmount(() => {
|
|
|
248
258
|
clearTimeout(openOfFocusTimeout);
|
|
249
259
|
});
|
|
250
260
|
|
|
261
|
+
const inputRef = ref<HTMLElement | null>(null);
|
|
262
|
+
const dropdownRef = ref<HTMLElement | null>(null);
|
|
251
263
|
const drawer = ref<InstanceType<typeof BaseAutocompleteDrawer> | null>(null);
|
|
252
264
|
|
|
253
265
|
const keywords = ref('');
|
|
@@ -286,7 +298,7 @@ const filteredNormalizedOptions = computed((): NormalizedOption[] => {
|
|
|
286
298
|
|
|
287
299
|
useClickOutside(autocomplete, () => {
|
|
288
300
|
close();
|
|
289
|
-
});
|
|
301
|
+
}, { includes: () => [dropdownRef] });
|
|
290
302
|
|
|
291
303
|
function open() {
|
|
292
304
|
// Always focus as a safety
|
|
@@ -422,6 +434,20 @@ const removeOption = (option: NormalizedOption) => {
|
|
|
422
434
|
update(newModelValue);
|
|
423
435
|
};
|
|
424
436
|
|
|
437
|
+
// Floating UI
|
|
438
|
+
|
|
439
|
+
const inputRefRect = useElementBounding(inputRef);
|
|
440
|
+
const inputRefWidth = computed(() => {
|
|
441
|
+
return inputRefRect.width.value;
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
const { floatingStyles } = useFloating(inputRef, dropdownRef, {
|
|
445
|
+
middleware: [offset(4), flip({
|
|
446
|
+
fallbackPlacements: ['right', 'bottom'],
|
|
447
|
+
})],
|
|
448
|
+
whileElementsMounted: autoUpdate,
|
|
449
|
+
});
|
|
450
|
+
|
|
425
451
|
// Element Classes
|
|
426
452
|
|
|
427
453
|
const wrapperClass = computed(() => {
|