@volverjs/ui-vue 0.0.10-beta.24 → 0.0.10-beta.26
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/auto-imports.d.ts +1 -0
- package/dist/components/VvAccordion/VvAccordion.es.js +14 -12
- package/dist/components/VvAccordion/VvAccordion.umd.js +1 -1
- package/dist/components/VvAccordionGroup/VvAccordionGroup.es.js +169 -89
- package/dist/components/VvAccordionGroup/VvAccordionGroup.umd.js +1 -1
- package/dist/components/VvAccordionGroup/VvAccordionGroup.vue.d.ts +31 -5
- package/dist/components/VvAccordionGroup/index.d.ts +7 -4
- package/dist/components/VvAction/VvAction.es.js +14 -12
- package/dist/components/VvAction/VvAction.umd.js +1 -1
- package/dist/components/VvAction/VvAction.vue.d.ts +2 -11
- package/dist/components/VvAction/index.d.ts +1 -5
- package/dist/components/VvAlert/VvAlert.es.js +14 -12
- package/dist/components/VvAlert/VvAlert.umd.js +1 -1
- package/dist/components/VvAlertGroup/VvAlertGroup.es.js +14 -12
- package/dist/components/VvAlertGroup/VvAlertGroup.umd.js +1 -1
- package/dist/components/VvAvatar/VvAvatar.es.js +14 -12
- package/dist/components/VvAvatar/VvAvatar.umd.js +1 -1
- package/dist/components/VvAvatarGroup/VvAvatarGroup.es.js +14 -12
- package/dist/components/VvAvatarGroup/VvAvatarGroup.umd.js +1 -1
- package/dist/components/VvBadge/VvBadge.es.js +14 -12
- package/dist/components/VvBadge/VvBadge.umd.js +1 -1
- package/dist/components/VvBreadcrumb/VvBreadcrumb.es.js +14 -12
- package/dist/components/VvBreadcrumb/VvBreadcrumb.umd.js +1 -1
- package/dist/components/VvBreadcrumb/VvBreadcrumb.vue.d.ts +1 -1
- package/dist/components/VvButton/VvButton.es.js +14 -12
- package/dist/components/VvButton/VvButton.umd.js +1 -1
- package/dist/components/VvButton/VvButton.vue.d.ts +2 -19
- package/dist/components/VvButton/index.d.ts +1 -5
- package/dist/components/VvButtonGroup/VvButtonGroup.es.js +14 -12
- package/dist/components/VvButtonGroup/VvButtonGroup.umd.js +1 -1
- package/dist/components/VvCard/VvCard.es.js +14 -12
- package/dist/components/VvCard/VvCard.umd.js +1 -1
- package/dist/components/VvCheckbox/VvCheckbox.es.js +14 -12
- package/dist/components/VvCheckbox/VvCheckbox.umd.js +1 -1
- package/dist/components/VvCheckboxGroup/VvCheckboxGroup.es.js +14 -12
- package/dist/components/VvCheckboxGroup/VvCheckboxGroup.umd.js +1 -1
- package/dist/components/VvCombobox/VvCombobox.es.js +58 -34
- package/dist/components/VvCombobox/VvCombobox.umd.js +1 -1
- package/dist/components/VvCombobox/VvCombobox.vue.d.ts +2 -8
- package/dist/components/VvCombobox/index.d.ts +9 -2
- package/dist/components/VvDialog/VvDialog.es.js +14 -12
- package/dist/components/VvDialog/VvDialog.umd.js +1 -1
- package/dist/components/VvDropdown/VvDropdown.es.js +24 -16
- package/dist/components/VvDropdown/VvDropdown.umd.js +1 -1
- package/dist/components/VvDropdown/VvDropdownAction.vue.d.ts +2 -19
- package/dist/components/VvDropdown/VvDropdownItem.vue.d.ts +13 -1
- package/dist/components/VvDropdown/VvDropdownOption.vue.d.ts +9 -0
- package/dist/components/VvDropdown/index.d.ts +37 -0
- package/dist/components/VvDropdownAction/VvDropdownAction.es.js +64 -15
- package/dist/components/VvDropdownAction/VvDropdownAction.umd.js +1 -1
- package/dist/components/VvDropdownItem/VvDropdownItem.es.js +322 -1
- package/dist/components/VvDropdownItem/VvDropdownItem.umd.js +1 -1
- package/dist/components/VvDropdownOptgroup/VvDropdownOptgroup.es.js +14 -12
- package/dist/components/VvDropdownOptgroup/VvDropdownOptgroup.umd.js +1 -1
- package/dist/components/VvDropdownOption/VvDropdownOption.es.js +131 -76
- package/dist/components/VvDropdownOption/VvDropdownOption.umd.js +1 -1
- package/dist/components/VvInputFile/VvInputFile.es.js +14 -12
- package/dist/components/VvInputFile/VvInputFile.umd.js +1 -1
- package/dist/components/VvInputFile/index.d.ts +5 -5
- package/dist/components/VvInputText/VvInputText.es.js +979 -107
- package/dist/components/VvInputText/VvInputText.umd.js +1 -1
- package/dist/components/VvInputText/VvInputText.vue.d.ts +44 -1
- package/dist/components/VvInputText/index.d.ts +28 -0
- package/dist/components/VvNav/VvNav.es.js +14 -12
- package/dist/components/VvNav/VvNav.umd.js +1 -1
- package/dist/components/VvNav/VvNav.vue.d.ts +1 -1
- package/dist/components/VvNavItem/VvNavItem.es.js +14 -12
- package/dist/components/VvNavItem/VvNavItem.umd.js +1 -1
- package/dist/components/VvProgress/VvProgress.es.js +14 -12
- package/dist/components/VvProgress/VvProgress.umd.js +1 -1
- package/dist/components/VvRadio/VvRadio.es.js +14 -12
- package/dist/components/VvRadio/VvRadio.umd.js +1 -1
- package/dist/components/VvRadioGroup/VvRadioGroup.es.js +14 -12
- package/dist/components/VvRadioGroup/VvRadioGroup.umd.js +1 -1
- package/dist/components/VvSelect/VvSelect.es.js +14 -12
- package/dist/components/VvSelect/VvSelect.umd.js +1 -1
- package/dist/components/VvTab/VvTab.es.js +14 -12
- package/dist/components/VvTab/VvTab.umd.js +1 -1
- package/dist/components/VvTab/VvTab.vue.d.ts +1 -1
- package/dist/components/VvTextarea/VvTextarea.es.js +14 -12
- package/dist/components/VvTextarea/VvTextarea.umd.js +1 -1
- package/dist/components/VvTooltip/VvTooltip.es.js +14 -12
- package/dist/components/VvTooltip/VvTooltip.umd.js +1 -1
- package/dist/components/index.es.js +435 -195
- package/dist/components/index.umd.js +1 -1
- package/dist/composables/dropdown/useInjectDropdown.d.ts +3 -23
- package/dist/composables/dropdown/useProvideDropdown.d.ts +2 -3
- package/dist/composables/usePersistence.d.ts +3 -0
- package/dist/constants.d.ts +24 -21
- package/dist/directives/index.es.js +14 -12
- package/dist/directives/index.umd.js +1 -1
- package/dist/directives/v-tooltip.es.js +14 -12
- package/dist/directives/v-tooltip.umd.js +1 -1
- package/dist/icons.es.js +3 -3
- package/dist/icons.umd.js +1 -1
- package/dist/props/index.d.ts +11 -11
- package/dist/stories/AccordionGroup/AccordionGroup.stories.d.ts +51 -9
- package/dist/stories/AccordionGroup/AccordionGroupSlots.stories.d.ts +327 -64
- package/dist/stories/Button/Button.settings.d.ts +0 -1
- package/dist/types/nav.d.ts +1 -2
- package/package.json +1 -1
- package/src/assets/icons/detailed.json +1 -1
- package/src/assets/icons/normal.json +1 -1
- package/src/assets/icons/simple.json +1 -1
- package/src/components/VvAccordionGroup/VvAccordionGroup.vue +86 -69
- package/src/components/VvAccordionGroup/index.ts +3 -5
- package/src/components/VvCombobox/VvCombobox.vue +2 -0
- package/src/components/VvDropdown/VvDropdown.vue +11 -2
- package/src/components/VvDropdown/VvDropdownItem.vue +4 -1
- package/src/components/VvDropdown/VvDropdownOption.vue +3 -21
- package/src/components/VvDropdown/index.ts +35 -1
- package/src/components/VvInputText/VvInputText.vue +98 -3
- package/src/components/VvInputText/index.ts +24 -1
- package/src/composables/dropdown/useProvideDropdown.ts +4 -4
- package/src/composables/usePersistence.ts +76 -0
- package/src/constants.ts +23 -18
- package/src/props/index.ts +12 -7
- package/src/stories/AccordionGroup/AccordionGroup.settings.ts +2 -2
- package/src/stories/AccordionGroup/AccordionGroup.test.ts +5 -5
- package/src/stories/Button/Button.settings.ts +1 -4
- package/src/types/nav.ts +1 -3
|
@@ -16,10 +16,18 @@
|
|
|
16
16
|
|
|
17
17
|
// props and emit
|
|
18
18
|
const props = defineProps(VvAccordionGroupProps)
|
|
19
|
+
// eslint-disable-next-line
|
|
19
20
|
const emit = defineEmits(VvAccordionGroupEvents)
|
|
20
21
|
|
|
21
22
|
// data
|
|
22
|
-
const {
|
|
23
|
+
const {
|
|
24
|
+
disabled,
|
|
25
|
+
modifiers,
|
|
26
|
+
itemModifiers,
|
|
27
|
+
items,
|
|
28
|
+
storageKey,
|
|
29
|
+
storageType,
|
|
30
|
+
} = toRefs(props)
|
|
23
31
|
watchEffect(() => {
|
|
24
32
|
if (typeof props.modelValue === 'string' && props.collapse) {
|
|
25
33
|
// eslint-disable-next-line no-console
|
|
@@ -30,81 +38,81 @@
|
|
|
30
38
|
})
|
|
31
39
|
|
|
32
40
|
const accordionNames = reactive(new Set<string>())
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
(newKey, oldKey) => {
|
|
37
|
-
if (oldKey && oldKey !== newKey) {
|
|
38
|
-
localStorage.removeItem(oldKey)
|
|
39
|
-
}
|
|
40
|
-
if (newKey) {
|
|
41
|
-
modelValue = useLocalStorage(newKey, modelValue.value)
|
|
42
|
-
return
|
|
43
|
-
}
|
|
44
|
-
modelValue = ref(new Set<string>(modelValue.value))
|
|
45
|
-
},
|
|
46
|
-
{ immediate: true },
|
|
41
|
+
const storageModelValue = usePersistence<string | string[] | undefined>(
|
|
42
|
+
storageKey,
|
|
43
|
+
storageType,
|
|
47
44
|
)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
emit(
|
|
53
|
-
'update:modelValue',
|
|
54
|
-
[...accordionNames].filter(
|
|
55
|
-
(name) => !modelValue.value.has(name),
|
|
56
|
-
),
|
|
57
|
-
)
|
|
58
|
-
return
|
|
59
|
-
}
|
|
60
|
-
if (props.collapse) {
|
|
61
|
-
emit('update:modelValue', [...modelValue.value])
|
|
62
|
-
return
|
|
45
|
+
const localModelValue = computed({
|
|
46
|
+
get: () => {
|
|
47
|
+
if (props.modelValue !== null && props.modelValue !== undefined) {
|
|
48
|
+
return props.modelValue
|
|
63
49
|
}
|
|
64
|
-
|
|
50
|
+
return storageModelValue.value
|
|
65
51
|
},
|
|
66
|
-
{
|
|
67
|
-
|
|
68
|
-
|
|
52
|
+
set: (newValue) => {
|
|
53
|
+
emit('update:modelValue', newValue)
|
|
54
|
+
storageModelValue.value = newValue
|
|
69
55
|
},
|
|
70
|
-
)
|
|
71
|
-
|
|
72
|
-
() =>
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
newValue === undefined ||
|
|
76
|
-
newValue === null ||
|
|
77
|
-
JSON.stringify(newValue) === JSON.stringify(oldValue)
|
|
78
|
-
) {
|
|
79
|
-
return
|
|
56
|
+
})
|
|
57
|
+
const expandedAccordions = computed<Set<string>>({
|
|
58
|
+
get: () => {
|
|
59
|
+
if (localModelValue.value === undefined) {
|
|
60
|
+
return new Set<string>()
|
|
80
61
|
}
|
|
81
62
|
let toReturn = new Set<string>()
|
|
82
63
|
if (props.not) {
|
|
83
|
-
if (typeof
|
|
64
|
+
if (typeof localModelValue.value === 'string') {
|
|
84
65
|
toReturn = new Set<string>(
|
|
85
|
-
[...accordionNames].filter(
|
|
66
|
+
[...accordionNames].filter(
|
|
67
|
+
(name) => name !== localModelValue.value,
|
|
68
|
+
),
|
|
86
69
|
)
|
|
87
|
-
} else if (Array.isArray(
|
|
70
|
+
} else if (Array.isArray(localModelValue.value)) {
|
|
88
71
|
toReturn = new Set<string>(
|
|
89
72
|
[...accordionNames].filter(
|
|
90
|
-
(name) =>
|
|
73
|
+
(name) =>
|
|
74
|
+
!(localModelValue.value as string[]).includes(
|
|
75
|
+
name,
|
|
76
|
+
),
|
|
91
77
|
),
|
|
92
78
|
)
|
|
93
79
|
}
|
|
94
|
-
} else if (typeof
|
|
95
|
-
toReturn = new Set<string>([
|
|
96
|
-
} else if (Array.isArray(
|
|
97
|
-
toReturn = new Set<string>(
|
|
98
|
-
}
|
|
99
|
-
for (const name of accordionNames) {
|
|
100
|
-
bus.emit('toggle', { name, value: toReturn.has(name) })
|
|
80
|
+
} else if (typeof localModelValue.value === 'string') {
|
|
81
|
+
toReturn = new Set<string>([localModelValue.value])
|
|
82
|
+
} else if (Array.isArray(localModelValue.value)) {
|
|
83
|
+
toReturn = new Set<string>(localModelValue.value)
|
|
101
84
|
}
|
|
102
|
-
|
|
85
|
+
return toReturn
|
|
103
86
|
},
|
|
104
|
-
{
|
|
105
|
-
|
|
87
|
+
set: (newValue) => {
|
|
88
|
+
if (props.not) {
|
|
89
|
+
localModelValue.value = [...accordionNames].filter(
|
|
90
|
+
(name) => !newValue.has(name),
|
|
91
|
+
)
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
|
+
if (props.collapse) {
|
|
95
|
+
localModelValue.value = [...newValue]
|
|
96
|
+
return
|
|
97
|
+
}
|
|
98
|
+
localModelValue.value = newValue.values().next().value
|
|
106
99
|
},
|
|
107
|
-
)
|
|
100
|
+
})
|
|
101
|
+
onMounted(() => {
|
|
102
|
+
if (props.not && localModelValue.value === undefined) {
|
|
103
|
+
localModelValue.value = props.collapse
|
|
104
|
+
? []
|
|
105
|
+
: [...accordionNames.values()].splice(1, accordionNames.size)
|
|
106
|
+
}
|
|
107
|
+
nextTick(() => {
|
|
108
|
+
for (const name of accordionNames) {
|
|
109
|
+
bus.emit('toggle', {
|
|
110
|
+
name,
|
|
111
|
+
value: expandedAccordions.value.has(name),
|
|
112
|
+
})
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
})
|
|
108
116
|
|
|
109
117
|
// provide
|
|
110
118
|
const bus = mitt<AccordionGroupBusEvents>()
|
|
@@ -120,19 +128,22 @@
|
|
|
120
128
|
accordionNames.delete(name)
|
|
121
129
|
})
|
|
122
130
|
bus.on('toggle', ({ name, value }) => {
|
|
131
|
+
const newValue = new Set<string>(expandedAccordions.value)
|
|
123
132
|
if (value) {
|
|
124
133
|
if (!props.collapse) {
|
|
125
|
-
for (const item of
|
|
134
|
+
for (const item of newValue) {
|
|
126
135
|
if (item !== name) {
|
|
127
136
|
bus.emit('toggle', { name: item, value: false })
|
|
128
137
|
}
|
|
129
138
|
}
|
|
130
|
-
|
|
139
|
+
newValue.clear()
|
|
131
140
|
}
|
|
132
|
-
|
|
141
|
+
newValue.add(name)
|
|
142
|
+
expandedAccordions.value = newValue
|
|
133
143
|
return
|
|
134
144
|
}
|
|
135
|
-
|
|
145
|
+
newValue.delete(name)
|
|
146
|
+
expandedAccordions.value = newValue
|
|
136
147
|
})
|
|
137
148
|
const expand = (name?: string | string[]) => {
|
|
138
149
|
if (typeof name === 'string') {
|
|
@@ -169,7 +180,7 @@
|
|
|
169
180
|
bus.on('collapse', ({ name }) => collapse(name))
|
|
170
181
|
|
|
171
182
|
// expose
|
|
172
|
-
defineExpose({
|
|
183
|
+
defineExpose({ expandedAccordions, expand, collapse })
|
|
173
184
|
|
|
174
185
|
// styles
|
|
175
186
|
const bemCssClasses = useModifiers(
|
|
@@ -186,7 +197,7 @@
|
|
|
186
197
|
<!-- @slot Default slot -->
|
|
187
198
|
<slot
|
|
188
199
|
v-bind="{
|
|
189
|
-
|
|
200
|
+
expandedAccordions,
|
|
190
201
|
expand,
|
|
191
202
|
collapse,
|
|
192
203
|
}"
|
|
@@ -200,13 +211,19 @@
|
|
|
200
211
|
content: item.content,
|
|
201
212
|
}"
|
|
202
213
|
>
|
|
203
|
-
<template
|
|
214
|
+
<template
|
|
215
|
+
v-if="$slots[`summary::${item.name}`]"
|
|
216
|
+
#summary="data"
|
|
217
|
+
>
|
|
204
218
|
<!-- @slot Slot for accordion header -->
|
|
205
|
-
<slot v-bind="data" :name="`
|
|
219
|
+
<slot v-bind="data" :name="`summary::${item.name}`" />
|
|
206
220
|
</template>
|
|
207
|
-
<template
|
|
221
|
+
<template
|
|
222
|
+
v-if="$slots[`content::${item.name}`]"
|
|
223
|
+
#default="data"
|
|
224
|
+
>
|
|
208
225
|
<!-- @slot Slot for accordion details -->
|
|
209
|
-
<slot v-bind="data" :name="`
|
|
226
|
+
<slot v-bind="data" :name="`content::${item.name}`" />
|
|
210
227
|
</template>
|
|
211
228
|
</VvAccordion>
|
|
212
229
|
</slot>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ModifiersProps } from '@/props'
|
|
1
|
+
import { ModifiersProps, StorageProps } from '@/props'
|
|
2
2
|
|
|
3
3
|
export interface VvAccordionGroupItem {
|
|
4
4
|
title: string
|
|
@@ -10,11 +10,13 @@ export interface VvAccordionGroupItem {
|
|
|
10
10
|
|
|
11
11
|
export const VvAccordionGroupProps = {
|
|
12
12
|
...ModifiersProps,
|
|
13
|
+
...StorageProps,
|
|
13
14
|
/**
|
|
14
15
|
* VModel
|
|
15
16
|
*/
|
|
16
17
|
modelValue: {
|
|
17
18
|
type: [String, Array] as PropType<string | string[] | undefined>,
|
|
19
|
+
default: undefined,
|
|
18
20
|
},
|
|
19
21
|
/**
|
|
20
22
|
* Accordion items
|
|
@@ -43,10 +45,6 @@ export const VvAccordionGroupProps = {
|
|
|
43
45
|
* If true, the accordion items will be opened by default
|
|
44
46
|
*/
|
|
45
47
|
not: Boolean,
|
|
46
|
-
/**
|
|
47
|
-
* Enable local storage persistence
|
|
48
|
-
*/
|
|
49
|
-
storeKey: String,
|
|
50
48
|
}
|
|
51
49
|
|
|
52
50
|
export const VvAccordionGroupEvents = ['update:modelValue']
|
|
@@ -526,6 +526,7 @@
|
|
|
526
526
|
}"
|
|
527
527
|
:key="i"
|
|
528
528
|
class="vv-dropdown-option"
|
|
529
|
+
focus-on-hover
|
|
529
530
|
@click.passive="onInput(item)"
|
|
530
531
|
>
|
|
531
532
|
<!-- @slot Slot for option customization -->
|
|
@@ -556,6 +557,7 @@
|
|
|
556
557
|
propsDefaults.selectedHintLabel,
|
|
557
558
|
}"
|
|
558
559
|
class="vv-dropdown-option"
|
|
560
|
+
focus-on-hover
|
|
559
561
|
@click.passive="onInput(option)"
|
|
560
562
|
>
|
|
561
563
|
<!-- @slot Slot for option customization -->
|
|
@@ -299,9 +299,7 @@
|
|
|
299
299
|
})
|
|
300
300
|
bus.on('click', toggle)
|
|
301
301
|
|
|
302
|
-
// provide top dropdown item
|
|
303
302
|
const { role, modifiers } = toRefs(props)
|
|
304
|
-
const { itemRole } = useProvideDropdownItem({ role, expanded })
|
|
305
303
|
|
|
306
304
|
// styles
|
|
307
305
|
const bemCssClasses = useModifiers(
|
|
@@ -376,6 +374,17 @@
|
|
|
376
374
|
})
|
|
377
375
|
}
|
|
378
376
|
|
|
377
|
+
// hover
|
|
378
|
+
const hovered = useElementHover(floatingEl)
|
|
379
|
+
|
|
380
|
+
// provide top dropdown item
|
|
381
|
+
const { itemRole } = useProvideDropdownItem({
|
|
382
|
+
role,
|
|
383
|
+
expanded,
|
|
384
|
+
focused,
|
|
385
|
+
hovered,
|
|
386
|
+
})
|
|
387
|
+
|
|
379
388
|
// keyboard
|
|
380
389
|
onKeyStroke('Escape', (e) => {
|
|
381
390
|
if (expanded.value) {
|
|
@@ -5,6 +5,9 @@
|
|
|
5
5
|
</script>
|
|
6
6
|
|
|
7
7
|
<script setup lang="ts">
|
|
8
|
+
import { VvDropdownItemProps } from '.'
|
|
9
|
+
|
|
10
|
+
const props = defineProps(VvDropdownItemProps)
|
|
8
11
|
const { role, expanded } = useInjectedDropdownItem()
|
|
9
12
|
const element = ref(null)
|
|
10
13
|
useProvideDropdownAction({ expanded })
|
|
@@ -14,7 +17,7 @@
|
|
|
14
17
|
|
|
15
18
|
// focus item on hover
|
|
16
19
|
watch(hovered, (newValue) => {
|
|
17
|
-
if (newValue) {
|
|
20
|
+
if (newValue && props.focusOnHover) {
|
|
18
21
|
focused.value = true
|
|
19
22
|
}
|
|
20
23
|
})
|
|
@@ -5,30 +5,11 @@
|
|
|
5
5
|
</script>
|
|
6
6
|
|
|
7
7
|
<script setup lang="ts">
|
|
8
|
+
import { VvDropdownOptionProps } from '.'
|
|
8
9
|
import VvDropdownItem from './VvDropdownItem.vue'
|
|
9
|
-
import {
|
|
10
|
-
DisabledProps,
|
|
11
|
-
ModifiersProps,
|
|
12
|
-
SelectedProps,
|
|
13
|
-
UnselectableProps,
|
|
14
|
-
} from '../../props'
|
|
15
10
|
|
|
16
11
|
// props
|
|
17
|
-
const props = defineProps(
|
|
18
|
-
...DisabledProps,
|
|
19
|
-
...SelectedProps,
|
|
20
|
-
...UnselectableProps,
|
|
21
|
-
...ModifiersProps,
|
|
22
|
-
deselectHintLabel: {
|
|
23
|
-
type: String,
|
|
24
|
-
},
|
|
25
|
-
selectHintLabel: {
|
|
26
|
-
type: String,
|
|
27
|
-
},
|
|
28
|
-
selectedHintLabel: {
|
|
29
|
-
type: String,
|
|
30
|
-
},
|
|
31
|
-
})
|
|
12
|
+
const props = defineProps(VvDropdownOptionProps)
|
|
32
13
|
|
|
33
14
|
// style
|
|
34
15
|
const { modifiers } = toRefs(props)
|
|
@@ -61,6 +42,7 @@
|
|
|
61
42
|
:tabindex="disabled ? -1 : 0"
|
|
62
43
|
:aria-selected="selected"
|
|
63
44
|
:aria-disabled="disabled"
|
|
45
|
+
:focus-on-hover="focusOnHover"
|
|
64
46
|
>
|
|
65
47
|
<slot />
|
|
66
48
|
<span class="vv-dropdown-option__hint" :title="hintLabel">
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import type { PropType } from 'vue'
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
DropdownProps,
|
|
4
|
+
IdProps,
|
|
5
|
+
DisabledProps,
|
|
6
|
+
ModifiersProps,
|
|
7
|
+
SelectedProps,
|
|
8
|
+
UnselectableProps,
|
|
9
|
+
} from '../../props'
|
|
3
10
|
import { DropdownRole } from '../../constants'
|
|
4
11
|
|
|
5
12
|
export const VvDropdownProps = {
|
|
@@ -30,3 +37,30 @@ export const VvDropdownProps = {
|
|
|
30
37
|
Object.values(DropdownRole).includes(value),
|
|
31
38
|
},
|
|
32
39
|
}
|
|
40
|
+
|
|
41
|
+
export const VvDropdownItemProps = {
|
|
42
|
+
focusOnHover: {
|
|
43
|
+
type: Boolean,
|
|
44
|
+
default: false,
|
|
45
|
+
},
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const VvDropdownOptionProps = {
|
|
49
|
+
...DisabledProps,
|
|
50
|
+
...SelectedProps,
|
|
51
|
+
...UnselectableProps,
|
|
52
|
+
...ModifiersProps,
|
|
53
|
+
deselectHintLabel: {
|
|
54
|
+
type: String,
|
|
55
|
+
},
|
|
56
|
+
selectHintLabel: {
|
|
57
|
+
type: String,
|
|
58
|
+
},
|
|
59
|
+
selectedHintLabel: {
|
|
60
|
+
type: String,
|
|
61
|
+
},
|
|
62
|
+
focusOnHover: {
|
|
63
|
+
type: Boolean,
|
|
64
|
+
default: false,
|
|
65
|
+
},
|
|
66
|
+
}
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
import VvIcon from '../VvIcon/VvIcon.vue'
|
|
12
12
|
import { ACTION_ICONS } from '../VvIcon'
|
|
13
13
|
import VvInputTextActionsFactory from '../VvInputText/VvInputTextActions'
|
|
14
|
+
import VvDropdown from '../VvDropdown/VvDropdown.vue'
|
|
15
|
+
import VvDropdownOption from '../VvDropdown/VvDropdownOption.vue'
|
|
14
16
|
import {
|
|
15
17
|
VvInputTextEvents,
|
|
16
18
|
VvInputTextProps,
|
|
@@ -34,6 +36,7 @@
|
|
|
34
36
|
id,
|
|
35
37
|
icon,
|
|
36
38
|
iconPosition,
|
|
39
|
+
iconRemoveSuggestion,
|
|
37
40
|
label,
|
|
38
41
|
modelValue,
|
|
39
42
|
count,
|
|
@@ -46,6 +49,8 @@
|
|
|
46
49
|
type,
|
|
47
50
|
iMask,
|
|
48
51
|
step,
|
|
52
|
+
storageKey,
|
|
53
|
+
storageType,
|
|
49
54
|
} = toRefs(props)
|
|
50
55
|
const hasId = useUniqueId(id)
|
|
51
56
|
const hasHintId = computed(() => `${hasId.value}-hint`)
|
|
@@ -54,7 +59,7 @@
|
|
|
54
59
|
props.floating && isEmpty(props.placeholder) ? ' ' : props.placeholder,
|
|
55
60
|
)
|
|
56
61
|
|
|
57
|
-
//
|
|
62
|
+
// mask
|
|
58
63
|
const maskReady = ref(false)
|
|
59
64
|
const { el, mask, typed, masked, unmasked } = useIMask(
|
|
60
65
|
computed(
|
|
@@ -170,8 +175,12 @@
|
|
|
170
175
|
masked.value = newValue ?? ''
|
|
171
176
|
},
|
|
172
177
|
)
|
|
178
|
+
|
|
179
|
+
// template refs
|
|
173
180
|
const inputEl = el as Ref<HTMLInputElement>
|
|
174
|
-
const innerEl = ref()
|
|
181
|
+
const innerEl = ref<HTMLInputElement>()
|
|
182
|
+
const wrapperEl = ref<HTMLDivElement>()
|
|
183
|
+
const dropdownEl = ref<typeof VvDropdown>()
|
|
175
184
|
|
|
176
185
|
defineExpose({ $inner: innerEl })
|
|
177
186
|
|
|
@@ -191,6 +200,26 @@
|
|
|
191
200
|
if (newValue && propsDefaults.value.selectOnFocus && inputEl.value) {
|
|
192
201
|
inputEl.value.select()
|
|
193
202
|
}
|
|
203
|
+
if (newValue) {
|
|
204
|
+
dropdownEl.value?.show()
|
|
205
|
+
return
|
|
206
|
+
}
|
|
207
|
+
setTimeout(() => {
|
|
208
|
+
if (isDirty.value && suggestions.value) {
|
|
209
|
+
const suggestionsLimit = props.maxSuggestions - 1
|
|
210
|
+
if (
|
|
211
|
+
suggestions.value.size > suggestionsLimit &&
|
|
212
|
+
!suggestions.value.has(localModelValue.value)
|
|
213
|
+
) {
|
|
214
|
+
suggestions.value = new Set(
|
|
215
|
+
[...suggestions.value].slice(
|
|
216
|
+
suggestions.value.size - suggestionsLimit,
|
|
217
|
+
),
|
|
218
|
+
)
|
|
219
|
+
}
|
|
220
|
+
suggestions.value.add(localModelValue.value)
|
|
221
|
+
}
|
|
222
|
+
}, 300)
|
|
194
223
|
})
|
|
195
224
|
|
|
196
225
|
// visibility
|
|
@@ -267,6 +296,8 @@
|
|
|
267
296
|
}
|
|
268
297
|
return undefined
|
|
269
298
|
})
|
|
299
|
+
const { hasIcon: hasIconRemoveSuggestion } =
|
|
300
|
+
useComponentIcon(iconRemoveSuggestion)
|
|
270
301
|
|
|
271
302
|
// count
|
|
272
303
|
const { formatted: countFormatted } = useTextCount(localModelValue, {
|
|
@@ -295,6 +326,39 @@
|
|
|
295
326
|
return undefined
|
|
296
327
|
})
|
|
297
328
|
|
|
329
|
+
// suggestions
|
|
330
|
+
const suggestions = usePersistence<Set<string>>(
|
|
331
|
+
storageKey,
|
|
332
|
+
storageType,
|
|
333
|
+
new Set(),
|
|
334
|
+
)
|
|
335
|
+
const filteredSuggestions = computed(() => {
|
|
336
|
+
if (!suggestions.value) {
|
|
337
|
+
return []
|
|
338
|
+
}
|
|
339
|
+
return [...suggestions.value].filter(
|
|
340
|
+
(suggestion) =>
|
|
341
|
+
isEmpty(localModelValue.value) ||
|
|
342
|
+
(`${suggestion}`
|
|
343
|
+
.toLowerCase()
|
|
344
|
+
.includes(`${localModelValue.value}`.toLowerCase()) &&
|
|
345
|
+
suggestion !== localModelValue.value),
|
|
346
|
+
)
|
|
347
|
+
})
|
|
348
|
+
const hasSuggestions = computed(
|
|
349
|
+
() =>
|
|
350
|
+
storageKey?.value &&
|
|
351
|
+
suggestions.value &&
|
|
352
|
+
suggestions.value.size > 0,
|
|
353
|
+
)
|
|
354
|
+
const onSuggestionSelect = (suggestion: string) => {
|
|
355
|
+
localModelValue.value = suggestion
|
|
356
|
+
dropdownEl.value?.hide()
|
|
357
|
+
}
|
|
358
|
+
const onSuggestionRemove = (suggestion: string) => {
|
|
359
|
+
suggestions.value?.delete(suggestion)
|
|
360
|
+
}
|
|
361
|
+
|
|
298
362
|
// styles
|
|
299
363
|
const { modifiers } = toRefs(props)
|
|
300
364
|
const bemCssClasses = useModifiers(
|
|
@@ -463,7 +527,7 @@
|
|
|
463
527
|
<label v-if="label" :for="hasId" class="vv-input-text__label">
|
|
464
528
|
{{ label }}
|
|
465
529
|
</label>
|
|
466
|
-
<div class="vv-input-text__wrapper">
|
|
530
|
+
<div ref="wrapperEl" class="vv-input-text__wrapper">
|
|
467
531
|
<div v-if="$slots.before" class="vv-input-text__input-before">
|
|
468
532
|
<!-- @slot Slot before input icon -->
|
|
469
533
|
<slot name="before" v-bind="slotProps" />
|
|
@@ -542,5 +606,36 @@
|
|
|
542
606
|
<slot name="invalid" v-bind="hintSlotScope" />
|
|
543
607
|
</template>
|
|
544
608
|
</HintSlot>
|
|
609
|
+
<VvDropdown
|
|
610
|
+
v-if="hasSuggestions"
|
|
611
|
+
ref="dropdownEl"
|
|
612
|
+
:reference="wrapperEl"
|
|
613
|
+
:autofocus-first="false"
|
|
614
|
+
:trigger-width="true"
|
|
615
|
+
>
|
|
616
|
+
<template #items>
|
|
617
|
+
<VvDropdownOption
|
|
618
|
+
v-for="value in filteredSuggestions"
|
|
619
|
+
:key="value"
|
|
620
|
+
@click.stop="onSuggestionSelect(value)"
|
|
621
|
+
>
|
|
622
|
+
<div class="flex-1">
|
|
623
|
+
<slot name="suggestion" v-bind="{ value }">
|
|
624
|
+
{{ value }}
|
|
625
|
+
</slot>
|
|
626
|
+
</div>
|
|
627
|
+
<button
|
|
628
|
+
v-if="suggestions && hasIconRemoveSuggestion"
|
|
629
|
+
type="button"
|
|
630
|
+
tabindex="-1"
|
|
631
|
+
class="cursor-pointer"
|
|
632
|
+
:title="labelRemoveSuggestion"
|
|
633
|
+
@click.stop="onSuggestionRemove(value)"
|
|
634
|
+
>
|
|
635
|
+
<VvIcon v-bind="hasIconRemoveSuggestion" />
|
|
636
|
+
</button>
|
|
637
|
+
</VvDropdownOption>
|
|
638
|
+
</template>
|
|
639
|
+
</VvDropdown>
|
|
545
640
|
</div>
|
|
546
641
|
</template>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ExtractPropTypes, PropType } from 'vue'
|
|
2
2
|
import type { FactoryOpts } from 'imask'
|
|
3
|
-
import { InputTextareaProps } from '../../props'
|
|
3
|
+
import { InputTextareaProps, StorageProps } from '../../props'
|
|
4
4
|
import { type VvIconProps, ACTION_ICONS } from '../VvIcon'
|
|
5
5
|
|
|
6
6
|
export const INPUT_TYPES = {
|
|
@@ -40,6 +40,7 @@ export const VvInputTextEvents = [
|
|
|
40
40
|
|
|
41
41
|
export const VvInputTextProps = {
|
|
42
42
|
...InputTextareaProps,
|
|
43
|
+
...StorageProps,
|
|
43
44
|
/**
|
|
44
45
|
* Input value
|
|
45
46
|
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#value
|
|
@@ -109,6 +110,14 @@ export const VvInputTextProps = {
|
|
|
109
110
|
type: [String, Object] as PropType<string | VvIconProps>,
|
|
110
111
|
default: ACTION_ICONS.clear,
|
|
111
112
|
},
|
|
113
|
+
/**
|
|
114
|
+
* VvIcon name for remove suggestion button
|
|
115
|
+
* @see VVIcon
|
|
116
|
+
*/
|
|
117
|
+
iconRemoveSuggestion: {
|
|
118
|
+
type: [String, Object] as PropType<string | VvIconProps>,
|
|
119
|
+
default: ACTION_ICONS.remove,
|
|
120
|
+
},
|
|
112
121
|
/**
|
|
113
122
|
* Label for step up button
|
|
114
123
|
*/
|
|
@@ -144,6 +153,13 @@ export const VvInputTextProps = {
|
|
|
144
153
|
type: String,
|
|
145
154
|
default: 'Clear',
|
|
146
155
|
},
|
|
156
|
+
/**
|
|
157
|
+
* Label for remove suggestion button
|
|
158
|
+
*/
|
|
159
|
+
labelRemoveSuggestion: {
|
|
160
|
+
type: String,
|
|
161
|
+
default: 'Remove suggestion',
|
|
162
|
+
},
|
|
147
163
|
/**
|
|
148
164
|
* iMask options
|
|
149
165
|
* @see https://imask.js.org/guide.html
|
|
@@ -186,6 +202,13 @@ export const VvInputTextProps = {
|
|
|
186
202
|
type: Boolean,
|
|
187
203
|
default: false,
|
|
188
204
|
},
|
|
205
|
+
/**
|
|
206
|
+
* Maximum number of suggestions
|
|
207
|
+
*/
|
|
208
|
+
maxSuggestions: {
|
|
209
|
+
type: Number,
|
|
210
|
+
default: 5,
|
|
211
|
+
},
|
|
189
212
|
}
|
|
190
213
|
|
|
191
214
|
export type VvInputTextPropsTypes = ExtractPropTypes<typeof VvInputTextProps>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type Ref, Fragment } from 'vue'
|
|
2
2
|
import mitt from 'mitt'
|
|
3
3
|
import {
|
|
4
|
+
type DropdownItemState,
|
|
4
5
|
INJECTION_KEY_DROPDOWN_TRIGGER,
|
|
5
6
|
INJECTION_KEY_DROPDOWN_ACTION,
|
|
6
7
|
INJECTION_KEY_DROPDOWN_ITEM,
|
|
@@ -61,10 +62,9 @@ export function useProvideDropdownTrigger({
|
|
|
61
62
|
*/
|
|
62
63
|
export function useProvideDropdownItem({
|
|
63
64
|
role,
|
|
64
|
-
|
|
65
|
-
}: {
|
|
65
|
+
...others
|
|
66
|
+
}: Omit<DropdownItemState, 'role'> & {
|
|
66
67
|
role: Ref<`${DropdownRole}`>
|
|
67
|
-
expanded: Ref<boolean>
|
|
68
68
|
}) {
|
|
69
69
|
const itemRole = computed(() =>
|
|
70
70
|
role.value === DropdownRole.listbox
|
|
@@ -73,7 +73,7 @@ export function useProvideDropdownItem({
|
|
|
73
73
|
)
|
|
74
74
|
provide(INJECTION_KEY_DROPDOWN_ITEM, {
|
|
75
75
|
role: itemRole,
|
|
76
|
-
|
|
76
|
+
...others,
|
|
77
77
|
})
|
|
78
78
|
return { itemRole }
|
|
79
79
|
}
|