sprintify-ui 0.0.106 → 0.0.108
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 +7534 -7472
- package/dist/types/src/components/BaseAutocomplete.vue.d.ts +12 -1
- package/dist/types/src/components/{BaseAutocompleteDropdown.vue.d.ts → BaseAutocompleteDrawer.vue.d.ts} +3 -3
- package/dist/types/src/components/BaseAutocompleteFetch.vue.d.ts +10 -1
- package/dist/types/src/components/BaseBelongsTo.vue.d.ts +10 -1
- package/dist/types/src/types/index.d.ts +6 -0
- package/package.json +1 -1
- package/src/components/BaseAutocomplete.stories.js +50 -0
- package/src/components/BaseAutocomplete.vue +105 -40
- package/src/components/{BaseAutocompleteDropdown.vue → BaseAutocompleteDrawer.vue} +11 -11
- package/src/components/BaseAutocompleteFetch.stories.js +53 -0
- package/src/components/BaseAutocompleteFetch.vue +15 -1
- package/src/components/BaseBelongsTo.stories.js +53 -0
- package/src/components/BaseBelongsTo.vue +6 -1
- package/src/components/BaseDataIterator.vue +1 -0
- package/src/components/BaseDropdown.stories.js +6 -1
- package/src/components/BaseDropdown.vue +15 -4
- package/src/components/BaseTagAutocomplete.vue +8 -8
- package/src/types/index.ts +7 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { PropType, ComputedRef } from 'vue';
|
|
2
2
|
import { NormalizedOption, Option } from '@/types';
|
|
3
|
+
import { SelectConfiguration } from '@/types';
|
|
3
4
|
declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<{
|
|
4
5
|
modelValue: {
|
|
5
6
|
default: undefined;
|
|
@@ -78,13 +79,17 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<{
|
|
|
78
79
|
default(): string;
|
|
79
80
|
type: StringConstructor;
|
|
80
81
|
};
|
|
82
|
+
select: {
|
|
83
|
+
default: undefined;
|
|
84
|
+
type: PropType<SelectConfiguration | undefined>;
|
|
85
|
+
};
|
|
81
86
|
}, {
|
|
82
87
|
focus: () => void;
|
|
83
88
|
blur: () => void;
|
|
84
89
|
close: () => void;
|
|
85
90
|
open: () => void;
|
|
86
91
|
setKeywords: (input: string) => void;
|
|
87
|
-
}, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, ("clear" | "update:modelValue" | "close" | "scrollBottom" | "typing" | "open")[], "clear" | "update:modelValue" | "close" | "scrollBottom" | "typing" | "open", import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{
|
|
92
|
+
}, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, ("select" | "clear" | "update:modelValue" | "close" | "scrollBottom" | "typing" | "open")[], "select" | "clear" | "update:modelValue" | "close" | "scrollBottom" | "typing" | "open", import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{
|
|
88
93
|
modelValue: {
|
|
89
94
|
default: undefined;
|
|
90
95
|
type: PropType<Option | null | undefined>;
|
|
@@ -162,7 +167,12 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<{
|
|
|
162
167
|
default(): string;
|
|
163
168
|
type: StringConstructor;
|
|
164
169
|
};
|
|
170
|
+
select: {
|
|
171
|
+
default: undefined;
|
|
172
|
+
type: PropType<SelectConfiguration | undefined>;
|
|
173
|
+
};
|
|
165
174
|
}>> & {
|
|
175
|
+
onSelect?: ((...args: any[]) => any) | undefined;
|
|
166
176
|
onScrollBottom?: ((...args: any[]) => any) | undefined;
|
|
167
177
|
onClear?: ((...args: any[]) => any) | undefined;
|
|
168
178
|
"onUpdate:modelValue"?: ((...args: any[]) => any) | undefined;
|
|
@@ -173,6 +183,7 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<{
|
|
|
173
183
|
filter: (option: NormalizedOption) => boolean;
|
|
174
184
|
required: boolean;
|
|
175
185
|
name: string;
|
|
186
|
+
select: SelectConfiguration | undefined;
|
|
176
187
|
loading: boolean;
|
|
177
188
|
loadingBottom: boolean;
|
|
178
189
|
size: "base" | "xs" | "sm";
|
|
@@ -25,7 +25,7 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<{
|
|
|
25
25
|
type: PropType<"base" | "xs" | "sm">;
|
|
26
26
|
default: string;
|
|
27
27
|
};
|
|
28
|
-
|
|
28
|
+
drawerClass: {
|
|
29
29
|
type: StringConstructor;
|
|
30
30
|
default: string;
|
|
31
31
|
};
|
|
@@ -56,7 +56,7 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<{
|
|
|
56
56
|
type: PropType<"base" | "xs" | "sm">;
|
|
57
57
|
default: string;
|
|
58
58
|
};
|
|
59
|
-
|
|
59
|
+
drawerClass: {
|
|
60
60
|
type: StringConstructor;
|
|
61
61
|
default: string;
|
|
62
62
|
};
|
|
@@ -70,7 +70,7 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<{
|
|
|
70
70
|
loading: boolean;
|
|
71
71
|
loadingBottom: boolean;
|
|
72
72
|
size: "base" | "xs" | "sm";
|
|
73
|
-
|
|
73
|
+
drawerClass: string;
|
|
74
74
|
}>, {
|
|
75
75
|
empty: (_: {}) => any;
|
|
76
76
|
option: (_: {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PropType } from 'vue';
|
|
2
|
-
import { Option } from '@/types';
|
|
2
|
+
import { Option, SelectConfiguration } from '@/types';
|
|
3
3
|
declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<{
|
|
4
4
|
modelValue: {
|
|
5
5
|
default: undefined;
|
|
@@ -69,6 +69,10 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<{
|
|
|
69
69
|
default: undefined;
|
|
70
70
|
type: StringConstructor;
|
|
71
71
|
};
|
|
72
|
+
select: {
|
|
73
|
+
default: undefined;
|
|
74
|
+
type: PropType<SelectConfiguration | undefined>;
|
|
75
|
+
};
|
|
72
76
|
}, {
|
|
73
77
|
focus: () => void | undefined;
|
|
74
78
|
blur: () => void | undefined;
|
|
@@ -144,6 +148,10 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<{
|
|
|
144
148
|
default: undefined;
|
|
145
149
|
type: StringConstructor;
|
|
146
150
|
};
|
|
151
|
+
select: {
|
|
152
|
+
default: undefined;
|
|
153
|
+
type: PropType<SelectConfiguration | undefined>;
|
|
154
|
+
};
|
|
147
155
|
}>> & {
|
|
148
156
|
onFocus?: ((...args: any[]) => any) | undefined;
|
|
149
157
|
onScrollBottom?: ((...args: any[]) => any) | undefined;
|
|
@@ -153,6 +161,7 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<{
|
|
|
153
161
|
}, {
|
|
154
162
|
required: boolean;
|
|
155
163
|
name: string;
|
|
164
|
+
select: SelectConfiguration | undefined;
|
|
156
165
|
size: "base" | "xs" | "sm";
|
|
157
166
|
disabled: boolean;
|
|
158
167
|
placeholder: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PropType } from 'vue';
|
|
2
|
-
import { Option } from '@/types';
|
|
2
|
+
import { Option, SelectConfiguration } from '@/types';
|
|
3
3
|
declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<{
|
|
4
4
|
modelValue: {
|
|
5
5
|
default: undefined;
|
|
@@ -73,6 +73,10 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<{
|
|
|
73
73
|
default: undefined;
|
|
74
74
|
type: StringConstructor;
|
|
75
75
|
};
|
|
76
|
+
select: {
|
|
77
|
+
default: undefined;
|
|
78
|
+
type: PropType<SelectConfiguration | undefined>;
|
|
79
|
+
};
|
|
76
80
|
}, {
|
|
77
81
|
focus: () => void | undefined;
|
|
78
82
|
blur: () => void | undefined;
|
|
@@ -152,11 +156,16 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<{
|
|
|
152
156
|
default: undefined;
|
|
153
157
|
type: StringConstructor;
|
|
154
158
|
};
|
|
159
|
+
select: {
|
|
160
|
+
default: undefined;
|
|
161
|
+
type: PropType<SelectConfiguration | undefined>;
|
|
162
|
+
};
|
|
155
163
|
}>> & {
|
|
156
164
|
"onUpdate:modelValue"?: ((...args: any[]) => any) | undefined;
|
|
157
165
|
}, {
|
|
158
166
|
required: boolean;
|
|
159
167
|
name: string;
|
|
168
|
+
select: SelectConfiguration | undefined;
|
|
160
169
|
size: "base" | "xs" | "sm";
|
|
161
170
|
disabled: boolean;
|
|
162
171
|
placeholder: string;
|
|
@@ -163,3 +163,9 @@ export interface NotificationsConfig {
|
|
|
163
163
|
footerLabel?: string;
|
|
164
164
|
footerTo?: RouteLocationRaw;
|
|
165
165
|
}
|
|
166
|
+
export interface SelectConfiguration {
|
|
167
|
+
options: Option[];
|
|
168
|
+
valueKey: string;
|
|
169
|
+
labelKey: string;
|
|
170
|
+
onChange: (value: OptionValue) => void;
|
|
171
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createFieldStory, options } from '../../.storybook/utils';
|
|
2
2
|
import BaseAutocomplete from './BaseAutocomplete.vue';
|
|
3
3
|
import ShowValue from '@/../.storybook/components/ShowValue.vue';
|
|
4
|
+
import { computed } from 'vue';
|
|
4
5
|
|
|
5
6
|
const sizes = ['xs', 'sm', 'base'];
|
|
6
7
|
|
|
@@ -179,6 +180,55 @@ export const SlotEmpty = (args) => {
|
|
|
179
180
|
};
|
|
180
181
|
};
|
|
181
182
|
|
|
183
|
+
export const WithSelect = (args) => {
|
|
184
|
+
return {
|
|
185
|
+
components: { BaseAutocomplete, ShowValue },
|
|
186
|
+
setup() {
|
|
187
|
+
const value = ref(options[0]);
|
|
188
|
+
const selected = ref(null);
|
|
189
|
+
|
|
190
|
+
const select = {
|
|
191
|
+
options: [
|
|
192
|
+
{ label: 'All', value: 'all' },
|
|
193
|
+
{ label: 'Jedi', value: 'jedi' },
|
|
194
|
+
{ label: 'Sith', value: 'sith' },
|
|
195
|
+
],
|
|
196
|
+
labelKey: 'label',
|
|
197
|
+
valueKey: 'value',
|
|
198
|
+
onChange: (option) => {
|
|
199
|
+
selected.value = option;
|
|
200
|
+
},
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
const options2 = computed(() => {
|
|
204
|
+
if (selected.value == 'all' || !selected.value) {
|
|
205
|
+
return options;
|
|
206
|
+
}
|
|
207
|
+
return options.filter((option) => option.type === selected.value);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
return { args, value, options2, selected, select };
|
|
211
|
+
},
|
|
212
|
+
template: `
|
|
213
|
+
<BaseAutocomplete
|
|
214
|
+
v-model="value"
|
|
215
|
+
v-bind="args"
|
|
216
|
+
:options="options2"
|
|
217
|
+
:select="select"
|
|
218
|
+
>
|
|
219
|
+
</BaseAutocomplete>
|
|
220
|
+
|
|
221
|
+
<div class="mb-2"></div>
|
|
222
|
+
|
|
223
|
+
<p class="text-xs mb-0">Selection</p>
|
|
224
|
+
<ShowValue class="mt-0 mb-2" :value="selected" />
|
|
225
|
+
|
|
226
|
+
<p class="text-xs mb-0">Model Value</p>
|
|
227
|
+
<ShowValue class="mt-0" :value="value" />
|
|
228
|
+
`,
|
|
229
|
+
};
|
|
230
|
+
};
|
|
231
|
+
|
|
182
232
|
export const Field = createFieldStory({
|
|
183
233
|
component: BaseAutocomplete,
|
|
184
234
|
componentName: 'BaseAutocomplete',
|
|
@@ -1,42 +1,64 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div ref="autocomplete">
|
|
3
3
|
<div class="relative z-[1]">
|
|
4
|
-
<div class="
|
|
5
|
-
<
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
placeholder ? placeholder : $t('sui.autocomplete_placeholder')
|
|
11
|
-
"
|
|
12
|
-
class="w-full rounded pr-9 disabled:cursor-not-allowed disabled:text-slate-300"
|
|
4
|
+
<div class="flex">
|
|
5
|
+
<select
|
|
6
|
+
v-if="select"
|
|
7
|
+
v-model="selection"
|
|
8
|
+
:disabled="disabled"
|
|
9
|
+
class="rounded-l border-r-transparent focus:z-[1] focus:border-r-blue-500 disabled:cursor-not-allowed disabled:text-slate-300 disabled:opacity-100"
|
|
13
10
|
:class="[
|
|
14
11
|
hasErrorInternal ? 'border-red-600' : 'border-slate-300',
|
|
15
|
-
|
|
16
|
-
!visibleFocus
|
|
17
|
-
? [
|
|
18
|
-
'focus:ring-0',
|
|
19
|
-
hasErrorInternal
|
|
20
|
-
? 'focus:border-red-600'
|
|
21
|
-
: 'focus:border-slate-300',
|
|
22
|
-
]
|
|
23
|
-
: '',
|
|
12
|
+
inputSelectClass,
|
|
24
13
|
]"
|
|
25
|
-
|
|
26
|
-
:disabled="disabled"
|
|
27
|
-
@click="open"
|
|
28
|
-
@input="onTextInput"
|
|
29
|
-
@keydown="onTextKeydown"
|
|
30
|
-
/>
|
|
31
|
-
<div
|
|
32
|
-
class="pointer-events-none absolute top-0 left-0 flex h-full items-center justify-center"
|
|
33
|
-
:class="[iconWrapClass]"
|
|
14
|
+
@change="onSelectChange"
|
|
34
15
|
>
|
|
35
|
-
<
|
|
36
|
-
|
|
37
|
-
:
|
|
38
|
-
|
|
16
|
+
<option
|
|
17
|
+
v-for="option in select.options"
|
|
18
|
+
:key="option[select.valueKey]"
|
|
19
|
+
:value="option[select.valueKey]"
|
|
20
|
+
>
|
|
21
|
+
{{ option[select.labelKey] }}
|
|
22
|
+
</option>
|
|
23
|
+
</select>
|
|
24
|
+
<div class="relative grow">
|
|
25
|
+
<input
|
|
26
|
+
ref="inputElement"
|
|
27
|
+
:value="keywords"
|
|
28
|
+
type="text"
|
|
29
|
+
:placeholder="
|
|
30
|
+
placeholder ? placeholder : $t('sui.autocomplete_placeholder')
|
|
31
|
+
"
|
|
32
|
+
class="relative w-full pr-9 disabled:cursor-not-allowed disabled:text-slate-300"
|
|
33
|
+
:class="[
|
|
34
|
+
select ? '-left-px rounded-r' : 'rounded',
|
|
35
|
+
hasErrorInternal ? 'border-red-600' : 'border-slate-300',
|
|
36
|
+
inputSizeClass,
|
|
37
|
+
!visibleFocus
|
|
38
|
+
? [
|
|
39
|
+
'focus:ring-0',
|
|
40
|
+
hasErrorInternal
|
|
41
|
+
? 'focus:border-red-600'
|
|
42
|
+
: 'focus:border-slate-300',
|
|
43
|
+
]
|
|
44
|
+
: '',
|
|
45
|
+
]"
|
|
46
|
+
autocomplete="off"
|
|
47
|
+
:disabled="disabled"
|
|
48
|
+
@click="open"
|
|
49
|
+
@input="onTextInput"
|
|
50
|
+
@keydown="onTextKeydown"
|
|
39
51
|
/>
|
|
52
|
+
<div
|
|
53
|
+
class="pointer-events-none absolute top-0 left-0 flex h-full items-center justify-center"
|
|
54
|
+
:class="[iconWrapClass]"
|
|
55
|
+
>
|
|
56
|
+
<BaseIcon
|
|
57
|
+
class="text-slate-400"
|
|
58
|
+
:class="[iconClass]"
|
|
59
|
+
icon="heroicons:magnifying-glass-solid"
|
|
60
|
+
/>
|
|
61
|
+
</div>
|
|
40
62
|
</div>
|
|
41
63
|
</div>
|
|
42
64
|
|
|
@@ -68,14 +90,14 @@
|
|
|
68
90
|
: 'absolute top-1 z-menu min-h-[110px] w-full overflow-hidden rounded border border-slate-300 bg-white shadow-md',
|
|
69
91
|
]"
|
|
70
92
|
>
|
|
71
|
-
<
|
|
72
|
-
ref="
|
|
93
|
+
<BaseAutocompleteDrawer
|
|
94
|
+
ref="drawer"
|
|
73
95
|
:selected="normalizedModelValue"
|
|
74
96
|
:options="filteredNormalizedOptions"
|
|
75
97
|
:size="size"
|
|
76
98
|
:loading="loading"
|
|
77
99
|
:loading-bottom="loadingBottom"
|
|
78
|
-
:
|
|
100
|
+
:drawer-class="inline ? 'pt-1' : 'p-1'"
|
|
79
101
|
:keywords="keywords"
|
|
80
102
|
@select="onSelect"
|
|
81
103
|
@scroll-bottom="emit('scrollBottom')"
|
|
@@ -89,7 +111,7 @@
|
|
|
89
111
|
<template #footer="footerProps">
|
|
90
112
|
<slot name="footer" v-bind="{ ...footerProps, ...slotProps }" />
|
|
91
113
|
</template>
|
|
92
|
-
</
|
|
114
|
+
</BaseAutocompleteDrawer>
|
|
93
115
|
</div>
|
|
94
116
|
</div>
|
|
95
117
|
</div>
|
|
@@ -103,7 +125,8 @@ import { useHasOptions } from '@/composables/hasOptions';
|
|
|
103
125
|
import { useField } from '@/composables/field';
|
|
104
126
|
import { BaseIcon } from './index';
|
|
105
127
|
import { useClickOutside } from '@/composables/clickOutside';
|
|
106
|
-
import
|
|
128
|
+
import BaseAutocompleteDrawer from './BaseAutocompleteDrawer.vue';
|
|
129
|
+
import { SelectConfiguration } from '@/types';
|
|
107
130
|
|
|
108
131
|
const props = defineProps({
|
|
109
132
|
modelValue: {
|
|
@@ -186,6 +209,10 @@ const props = defineProps({
|
|
|
186
209
|
},
|
|
187
210
|
type: String,
|
|
188
211
|
},
|
|
212
|
+
select: {
|
|
213
|
+
default: undefined,
|
|
214
|
+
type: Object as PropType<SelectConfiguration | undefined>,
|
|
215
|
+
},
|
|
189
216
|
});
|
|
190
217
|
|
|
191
218
|
const emit = defineEmits([
|
|
@@ -195,6 +222,7 @@ const emit = defineEmits([
|
|
|
195
222
|
'open',
|
|
196
223
|
'close',
|
|
197
224
|
'scrollBottom',
|
|
225
|
+
'select',
|
|
198
226
|
]);
|
|
199
227
|
|
|
200
228
|
const { hasErrorInternal, emitUpdate } = useField({
|
|
@@ -212,8 +240,8 @@ const hasOptions = useHasOptions(
|
|
|
212
240
|
computed(() => false)
|
|
213
241
|
);
|
|
214
242
|
|
|
215
|
-
const
|
|
216
|
-
typeof
|
|
243
|
+
const drawer = ref(null) as Ref<InstanceType<
|
|
244
|
+
typeof BaseAutocompleteDrawer
|
|
217
245
|
> | null>;
|
|
218
246
|
|
|
219
247
|
let timerId = 0;
|
|
@@ -330,7 +358,7 @@ const onTextInput = (event: Event) => {
|
|
|
330
358
|
const onTextKeydown = (event: KeyboardEvent) => {
|
|
331
359
|
const key = event.key;
|
|
332
360
|
|
|
333
|
-
|
|
361
|
+
drawer.value?.onKeydown(event);
|
|
334
362
|
|
|
335
363
|
// Prevent default behavior for up/down arrows
|
|
336
364
|
|
|
@@ -388,6 +416,33 @@ function blur() {
|
|
|
388
416
|
inputElement.value?.blur();
|
|
389
417
|
}
|
|
390
418
|
|
|
419
|
+
function highlight() {
|
|
420
|
+
inputElement.value?.select();
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
const selection = ref(null) as Ref<string | null>;
|
|
424
|
+
|
|
425
|
+
watch(
|
|
426
|
+
() => props.select,
|
|
427
|
+
(select) => {
|
|
428
|
+
if (select && select.options.length > 0) {
|
|
429
|
+
selection.value = select.options[0][select.valueKey];
|
|
430
|
+
}
|
|
431
|
+
},
|
|
432
|
+
{ immediate: true }
|
|
433
|
+
);
|
|
434
|
+
|
|
435
|
+
function onSelectChange(event: Event) {
|
|
436
|
+
focus();
|
|
437
|
+
open();
|
|
438
|
+
highlight();
|
|
439
|
+
const value = get(event, 'target.value', null);
|
|
440
|
+
if (props.select && props.select.onChange) {
|
|
441
|
+
props.select.onChange(value);
|
|
442
|
+
}
|
|
443
|
+
emit('select', value);
|
|
444
|
+
}
|
|
445
|
+
|
|
391
446
|
const slotProps = {
|
|
392
447
|
focus,
|
|
393
448
|
blur,
|
|
@@ -398,7 +453,7 @@ const slotProps = {
|
|
|
398
453
|
|
|
399
454
|
// Element Classes
|
|
400
455
|
|
|
401
|
-
const
|
|
456
|
+
const inputSizeClass = computed(() => {
|
|
402
457
|
if (props.size == 'xs') {
|
|
403
458
|
return 'xs:text-xs xs:pl-7 text-base pl-9';
|
|
404
459
|
}
|
|
@@ -408,6 +463,16 @@ const inputClass = computed(() => {
|
|
|
408
463
|
return 'text-base pl-9';
|
|
409
464
|
});
|
|
410
465
|
|
|
466
|
+
const inputSelectClass = computed(() => {
|
|
467
|
+
if (props.size == 'xs') {
|
|
468
|
+
return 'xs:text-xs text-base';
|
|
469
|
+
}
|
|
470
|
+
if (props.size == 'sm') {
|
|
471
|
+
return 'xs:text-sm text-base';
|
|
472
|
+
}
|
|
473
|
+
return 'text-base';
|
|
474
|
+
});
|
|
475
|
+
|
|
411
476
|
const iconClass = computed(() => {
|
|
412
477
|
if (props.size == 'xs') {
|
|
413
478
|
return 'xs:h-4 xs:w-4 h-5 w-5';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="relative w-full overflow-hidden">
|
|
3
3
|
<div
|
|
4
|
-
ref="
|
|
4
|
+
ref="drawer"
|
|
5
5
|
data-scroll-lock-scrollable
|
|
6
6
|
class="max-h-[214px] w-full overflow-y-auto"
|
|
7
7
|
>
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
</slot>
|
|
16
16
|
|
|
17
17
|
<!-- Option list -->
|
|
18
|
-
<ul v-else :class="
|
|
18
|
+
<ul v-else :class="drawerClass">
|
|
19
19
|
<li
|
|
20
20
|
v-for="(option, index) in options"
|
|
21
21
|
:key="option.value ? option.value : 'null'"
|
|
@@ -132,7 +132,7 @@ const props = defineProps({
|
|
|
132
132
|
type: String as PropType<'xs' | 'sm' | 'base'>,
|
|
133
133
|
default: 'base',
|
|
134
134
|
},
|
|
135
|
-
|
|
135
|
+
drawerClass: {
|
|
136
136
|
type: String,
|
|
137
137
|
default: '',
|
|
138
138
|
},
|
|
@@ -140,7 +140,7 @@ const props = defineProps({
|
|
|
140
140
|
|
|
141
141
|
const emit = defineEmits(['scrollBottom', 'select']);
|
|
142
142
|
|
|
143
|
-
const
|
|
143
|
+
const drawer = ref(null) as Ref<HTMLDivElement | null>;
|
|
144
144
|
|
|
145
145
|
let mouseIsMoving = false;
|
|
146
146
|
|
|
@@ -218,7 +218,7 @@ function onKeydown(event: KeyboardEvent) {
|
|
|
218
218
|
|
|
219
219
|
onMounted(() => {
|
|
220
220
|
useInfiniteScroll(
|
|
221
|
-
|
|
221
|
+
drawer.value,
|
|
222
222
|
() => {
|
|
223
223
|
emit('scrollBottom');
|
|
224
224
|
},
|
|
@@ -227,11 +227,11 @@ onMounted(() => {
|
|
|
227
227
|
});
|
|
228
228
|
|
|
229
229
|
function scrollToFocus() {
|
|
230
|
-
if (!
|
|
230
|
+
if (!drawer.value) {
|
|
231
231
|
return;
|
|
232
232
|
}
|
|
233
233
|
|
|
234
|
-
const option =
|
|
234
|
+
const option = drawer.value.querySelector(
|
|
235
235
|
`[data-index="${focusIndex.value}"]`
|
|
236
236
|
) as HTMLElement | null;
|
|
237
237
|
|
|
@@ -239,11 +239,11 @@ function scrollToFocus() {
|
|
|
239
239
|
return;
|
|
240
240
|
}
|
|
241
241
|
|
|
242
|
-
const dropdownHeight =
|
|
242
|
+
const dropdownHeight = drawer.value.clientHeight;
|
|
243
243
|
const offsetTop = option.offsetTop;
|
|
244
244
|
const optionHeight = option.clientHeight;
|
|
245
245
|
|
|
246
|
-
|
|
246
|
+
drawer.value.scrollTop = offsetTop - dropdownHeight / 2 + optionHeight / 2;
|
|
247
247
|
}
|
|
248
248
|
|
|
249
249
|
// Validate focus index
|
|
@@ -290,8 +290,8 @@ watch(
|
|
|
290
290
|
watch(
|
|
291
291
|
() => props.keywords,
|
|
292
292
|
() => {
|
|
293
|
-
if (
|
|
294
|
-
|
|
293
|
+
if (drawer.value) {
|
|
294
|
+
drawer.value.scrollTop = 0;
|
|
295
295
|
}
|
|
296
296
|
// Reset the focusIndex
|
|
297
297
|
updateFocusIndex(0);
|
|
@@ -164,6 +164,59 @@ export const SlotEmpty = (args) => {
|
|
|
164
164
|
};
|
|
165
165
|
};
|
|
166
166
|
|
|
167
|
+
export const WithSelect = (args) => {
|
|
168
|
+
return {
|
|
169
|
+
components: { BaseAutocompleteFetch, ShowValue },
|
|
170
|
+
setup() {
|
|
171
|
+
const value = ref(options[0]);
|
|
172
|
+
const selected = ref(null);
|
|
173
|
+
|
|
174
|
+
const select = {
|
|
175
|
+
options: [
|
|
176
|
+
{ label: 'All', value: 'all' },
|
|
177
|
+
{ label: 'Video', value: 'video' },
|
|
178
|
+
{ label: 'Article', value: 'article' },
|
|
179
|
+
],
|
|
180
|
+
labelKey: 'label',
|
|
181
|
+
valueKey: 'value',
|
|
182
|
+
onChange: (option) => {
|
|
183
|
+
selected.value = option;
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const url = computed(() => {
|
|
188
|
+
if (selected.value == 'all' || !selected.value) {
|
|
189
|
+
return 'https://effettandem.com/api/content/articles';
|
|
190
|
+
}
|
|
191
|
+
return (
|
|
192
|
+
'https://effettandem.com/api/content/articles' +
|
|
193
|
+
'?type=' +
|
|
194
|
+
selected.value
|
|
195
|
+
);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
return { args, value, url, selected, select };
|
|
199
|
+
},
|
|
200
|
+
template: `
|
|
201
|
+
<BaseAutocompleteFetch
|
|
202
|
+
v-model="value"
|
|
203
|
+
v-bind="args"
|
|
204
|
+
:url="url"
|
|
205
|
+
:select="select"
|
|
206
|
+
>
|
|
207
|
+
</BaseAutocompleteFetch>
|
|
208
|
+
|
|
209
|
+
<div class="mb-2"></div>
|
|
210
|
+
|
|
211
|
+
<p class="text-xs mb-0">Selection</p>
|
|
212
|
+
<ShowValue class="mt-0 mb-2" :value="selected" />
|
|
213
|
+
|
|
214
|
+
<p class="text-xs mb-0">Model Value</p>
|
|
215
|
+
<ShowValue class="mt-0" :value="value" />
|
|
216
|
+
`,
|
|
217
|
+
};
|
|
218
|
+
};
|
|
219
|
+
|
|
167
220
|
export const Field = createFieldStory({
|
|
168
221
|
component: BaseAutocompleteFetch,
|
|
169
222
|
componentName: 'BaseAutocompleteFetch',
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
:visible-focus="visibleFocus"
|
|
20
20
|
:show-empty-option="showEmptyOption"
|
|
21
21
|
:empty-option-label="emptyOptionLabel"
|
|
22
|
+
:select="select"
|
|
22
23
|
:filter="filterOptions"
|
|
23
24
|
@clear="onClear"
|
|
24
25
|
@open="onOpen"
|
|
@@ -51,7 +52,7 @@
|
|
|
51
52
|
import { config } from '@/index';
|
|
52
53
|
import { debounce, throttle } from 'lodash';
|
|
53
54
|
import { PropType, Ref } from 'vue';
|
|
54
|
-
import { Option } from '@/types';
|
|
55
|
+
import { Option, SelectConfiguration } from '@/types';
|
|
55
56
|
import BaseAutocomplete from './BaseAutocomplete.vue';
|
|
56
57
|
|
|
57
58
|
/**
|
|
@@ -132,6 +133,10 @@ const props = defineProps({
|
|
|
132
133
|
default: undefined,
|
|
133
134
|
type: String,
|
|
134
135
|
},
|
|
136
|
+
select: {
|
|
137
|
+
default: undefined,
|
|
138
|
+
type: Object as PropType<SelectConfiguration | undefined>,
|
|
139
|
+
},
|
|
135
140
|
});
|
|
136
141
|
|
|
137
142
|
const emit = defineEmits([
|
|
@@ -187,6 +192,15 @@ const scrollBottom = throttle(() => {
|
|
|
187
192
|
}
|
|
188
193
|
}, 500);
|
|
189
194
|
|
|
195
|
+
watch(
|
|
196
|
+
() => props.url,
|
|
197
|
+
() => {
|
|
198
|
+
page.value = 1;
|
|
199
|
+
reachedEnd.value = false;
|
|
200
|
+
search();
|
|
201
|
+
}
|
|
202
|
+
);
|
|
203
|
+
|
|
190
204
|
function search() {
|
|
191
205
|
if (fetching.value) {
|
|
192
206
|
return;
|