@volverjs/ui-vue 0.0.10-beta.25 → 0.0.10-beta.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/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 +92 -38
- package/dist/components/VvAccordionGroup/VvAccordionGroup.umd.js +1 -1
- package/dist/components/VvAccordionGroup/VvAccordionGroup.vue.d.ts +13 -2
- package/dist/components/VvAccordionGroup/index.d.ts +6 -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 +358 -144
- 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 +27 -4
- package/dist/stories/AccordionGroup/AccordionGroupSlots.stories.d.ts +176 -29
- package/dist/stories/Button/Button.settings.d.ts +0 -1
- package/dist/types/nav.d.ts +1 -2
- package/package.json +8 -8
- 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 +14 -19
- package/src/components/VvAccordionGroup/index.ts +2 -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 +100 -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/Button/Button.settings.ts +1 -4
- package/src/types/nav.ts +1 -3
|
@@ -20,7 +20,14 @@
|
|
|
20
20
|
const emit = defineEmits(VvAccordionGroupEvents)
|
|
21
21
|
|
|
22
22
|
// data
|
|
23
|
-
const {
|
|
23
|
+
const {
|
|
24
|
+
disabled,
|
|
25
|
+
modifiers,
|
|
26
|
+
itemModifiers,
|
|
27
|
+
items,
|
|
28
|
+
storageKey,
|
|
29
|
+
storageType,
|
|
30
|
+
} = toRefs(props)
|
|
24
31
|
watchEffect(() => {
|
|
25
32
|
if (typeof props.modelValue === 'string' && props.collapse) {
|
|
26
33
|
// eslint-disable-next-line no-console
|
|
@@ -31,33 +38,21 @@
|
|
|
31
38
|
})
|
|
32
39
|
|
|
33
40
|
const accordionNames = reactive(new Set<string>())
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if (oldKey && oldKey !== newKey) {
|
|
39
|
-
localStorage.removeItem(oldKey)
|
|
40
|
-
}
|
|
41
|
-
if (newKey) {
|
|
42
|
-
storeModelValue = useLocalStorage(newKey, storeModelValue.value)
|
|
43
|
-
return
|
|
44
|
-
}
|
|
45
|
-
storeModelValue = ref(storeModelValue.value)
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
immediate: true,
|
|
49
|
-
},
|
|
41
|
+
const storageModelValue = usePersistence<string | string[] | undefined>(
|
|
42
|
+
storageKey,
|
|
43
|
+
storageType,
|
|
44
|
+
[],
|
|
50
45
|
)
|
|
51
46
|
const localModelValue = computed({
|
|
52
47
|
get: () => {
|
|
53
48
|
if (props.modelValue !== null && props.modelValue !== undefined) {
|
|
54
49
|
return props.modelValue
|
|
55
50
|
}
|
|
56
|
-
return
|
|
51
|
+
return storageModelValue.value
|
|
57
52
|
},
|
|
58
53
|
set: (newValue) => {
|
|
59
54
|
emit('update:modelValue', newValue)
|
|
60
|
-
|
|
55
|
+
storageModelValue.value = newValue
|
|
61
56
|
},
|
|
62
57
|
})
|
|
63
58
|
const expandedAccordions = computed<Set<string>>({
|
|
@@ -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,6 +10,7 @@ export interface VvAccordionGroupItem {
|
|
|
10
10
|
|
|
11
11
|
export const VvAccordionGroupProps = {
|
|
12
12
|
...ModifiersProps,
|
|
13
|
+
...StorageProps,
|
|
13
14
|
/**
|
|
14
15
|
* VModel
|
|
15
16
|
*/
|
|
@@ -44,10 +45,6 @@ export const VvAccordionGroupProps = {
|
|
|
44
45
|
* If true, the accordion items will be opened by default
|
|
45
46
|
*/
|
|
46
47
|
not: Boolean,
|
|
47
|
-
/**
|
|
48
|
-
* Enable local storage persistence
|
|
49
|
-
*/
|
|
50
|
-
storeKey: String,
|
|
51
48
|
}
|
|
52
49
|
|
|
53
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,41 @@
|
|
|
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]
|
|
340
|
+
.filter(
|
|
341
|
+
(suggestion) =>
|
|
342
|
+
isEmpty(localModelValue.value) ||
|
|
343
|
+
(`${suggestion}`
|
|
344
|
+
.toLowerCase()
|
|
345
|
+
.includes(`${localModelValue.value}`.toLowerCase()) &&
|
|
346
|
+
suggestion !== localModelValue.value),
|
|
347
|
+
)
|
|
348
|
+
.reverse()
|
|
349
|
+
})
|
|
350
|
+
const hasSuggestions = computed(
|
|
351
|
+
() =>
|
|
352
|
+
storageKey?.value &&
|
|
353
|
+
suggestions.value &&
|
|
354
|
+
suggestions.value.size > 0,
|
|
355
|
+
)
|
|
356
|
+
const onSuggestionSelect = (suggestion: string) => {
|
|
357
|
+
localModelValue.value = suggestion
|
|
358
|
+
dropdownEl.value?.hide()
|
|
359
|
+
}
|
|
360
|
+
const onSuggestionRemove = (suggestion: string) => {
|
|
361
|
+
suggestions.value?.delete(suggestion)
|
|
362
|
+
}
|
|
363
|
+
|
|
298
364
|
// styles
|
|
299
365
|
const { modifiers } = toRefs(props)
|
|
300
366
|
const bemCssClasses = useModifiers(
|
|
@@ -463,7 +529,7 @@
|
|
|
463
529
|
<label v-if="label" :for="hasId" class="vv-input-text__label">
|
|
464
530
|
{{ label }}
|
|
465
531
|
</label>
|
|
466
|
-
<div class="vv-input-text__wrapper">
|
|
532
|
+
<div ref="wrapperEl" class="vv-input-text__wrapper">
|
|
467
533
|
<div v-if="$slots.before" class="vv-input-text__input-before">
|
|
468
534
|
<!-- @slot Slot before input icon -->
|
|
469
535
|
<slot name="before" v-bind="slotProps" />
|
|
@@ -542,5 +608,36 @@
|
|
|
542
608
|
<slot name="invalid" v-bind="hintSlotScope" />
|
|
543
609
|
</template>
|
|
544
610
|
</HintSlot>
|
|
611
|
+
<VvDropdown
|
|
612
|
+
v-if="hasSuggestions"
|
|
613
|
+
ref="dropdownEl"
|
|
614
|
+
:reference="wrapperEl"
|
|
615
|
+
:autofocus-first="false"
|
|
616
|
+
:trigger-width="true"
|
|
617
|
+
>
|
|
618
|
+
<template #items>
|
|
619
|
+
<VvDropdownOption
|
|
620
|
+
v-for="value in filteredSuggestions"
|
|
621
|
+
:key="value"
|
|
622
|
+
@click.stop="onSuggestionSelect(value)"
|
|
623
|
+
>
|
|
624
|
+
<div class="flex-1">
|
|
625
|
+
<slot name="suggestion" v-bind="{ value }">
|
|
626
|
+
{{ value }}
|
|
627
|
+
</slot>
|
|
628
|
+
</div>
|
|
629
|
+
<button
|
|
630
|
+
v-if="suggestions && hasIconRemoveSuggestion"
|
|
631
|
+
type="button"
|
|
632
|
+
tabindex="-1"
|
|
633
|
+
class="cursor-pointer"
|
|
634
|
+
:title="labelRemoveSuggestion"
|
|
635
|
+
@click.stop="onSuggestionRemove(value)"
|
|
636
|
+
>
|
|
637
|
+
<VvIcon v-bind="hasIconRemoveSuggestion" />
|
|
638
|
+
</button>
|
|
639
|
+
</VvDropdownOption>
|
|
640
|
+
</template>
|
|
641
|
+
</VvDropdown>
|
|
545
642
|
</div>
|
|
546
643
|
</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
|
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { StorageType } from '@/constants'
|
|
2
|
+
|
|
3
|
+
export const usePersistence = <T>(
|
|
4
|
+
storageKey: Ref<string | undefined> | undefined,
|
|
5
|
+
storageType: Ref<`${StorageType}`> | `${StorageType}` = StorageType.local,
|
|
6
|
+
defaultValue?: T,
|
|
7
|
+
) => {
|
|
8
|
+
const localValue: Ref<T | undefined> = ref()
|
|
9
|
+
if (defaultValue) {
|
|
10
|
+
localValue.value = defaultValue
|
|
11
|
+
}
|
|
12
|
+
let storageValue: Ref<T | undefined> | undefined
|
|
13
|
+
if (storageKey) {
|
|
14
|
+
watch(
|
|
15
|
+
storageKey,
|
|
16
|
+
(newKey, oldKey) => {
|
|
17
|
+
const storage =
|
|
18
|
+
unref(storageType) === StorageType.session
|
|
19
|
+
? sessionStorage
|
|
20
|
+
: localStorage
|
|
21
|
+
if (oldKey && oldKey !== newKey) {
|
|
22
|
+
storage.removeItem(oldKey)
|
|
23
|
+
}
|
|
24
|
+
if (newKey) {
|
|
25
|
+
storageValue = useStorage(
|
|
26
|
+
newKey,
|
|
27
|
+
storageValue?.value ?? localValue.value,
|
|
28
|
+
storage,
|
|
29
|
+
)
|
|
30
|
+
return
|
|
31
|
+
}
|
|
32
|
+
storageValue = undefined
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
immediate: true,
|
|
36
|
+
},
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
if (isRef(storageType)) {
|
|
40
|
+
watch(storageType, (newType, oldType) => {
|
|
41
|
+
if (storageKey?.value) {
|
|
42
|
+
if (newType) {
|
|
43
|
+
const storage =
|
|
44
|
+
newType === StorageType.session
|
|
45
|
+
? sessionStorage
|
|
46
|
+
: localStorage
|
|
47
|
+
storageValue = useStorage(
|
|
48
|
+
storageKey.value,
|
|
49
|
+
storageValue?.value ?? localValue.value,
|
|
50
|
+
storage,
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
if (oldType && oldType !== newType) {
|
|
54
|
+
const oldStorage =
|
|
55
|
+
oldType === StorageType.session
|
|
56
|
+
? sessionStorage
|
|
57
|
+
: localStorage
|
|
58
|
+
oldStorage.removeItem(storageKey.value)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return computed<T | undefined>({
|
|
65
|
+
get: () => {
|
|
66
|
+
return storageValue?.value ?? localValue.value
|
|
67
|
+
},
|
|
68
|
+
set: (value) => {
|
|
69
|
+
if (storageValue) {
|
|
70
|
+
storageValue.value = value
|
|
71
|
+
return
|
|
72
|
+
}
|
|
73
|
+
localValue.value = value
|
|
74
|
+
},
|
|
75
|
+
})
|
|
76
|
+
}
|
package/src/constants.ts
CHANGED
|
@@ -11,6 +11,11 @@ import type {
|
|
|
11
11
|
|
|
12
12
|
export const DEFAULT_ICONIFY_PROVIDER = 'vv'
|
|
13
13
|
|
|
14
|
+
export enum StorageType {
|
|
15
|
+
local = 'local',
|
|
16
|
+
session = 'session',
|
|
17
|
+
}
|
|
18
|
+
|
|
14
19
|
export enum Strategy {
|
|
15
20
|
absolute = 'absolute',
|
|
16
21
|
fixed = 'fixed',
|
|
@@ -68,13 +73,6 @@ export enum DropdownItemRole {
|
|
|
68
73
|
presentation = 'presentation',
|
|
69
74
|
}
|
|
70
75
|
|
|
71
|
-
export enum AnchorTarget {
|
|
72
|
-
_blank = '_blank',
|
|
73
|
-
_self = '_self',
|
|
74
|
-
_parent = '_parent',
|
|
75
|
-
_top = '_top',
|
|
76
|
-
}
|
|
77
|
-
|
|
78
76
|
// volver
|
|
79
77
|
export const INJECTION_KEY_VOLVER = Symbol.for('volver') as InjectionKey<Volver>
|
|
80
78
|
|
|
@@ -93,9 +91,7 @@ export const INJECTION_KEY_ACCORDION_GROUP = Symbol.for(
|
|
|
93
91
|
) as InjectionKey<AccordionGroupState>
|
|
94
92
|
|
|
95
93
|
// dropdown
|
|
96
|
-
export
|
|
97
|
-
'dropdownTrigger',
|
|
98
|
-
) as InjectionKey<{
|
|
94
|
+
export type DropdownTriggerState = {
|
|
99
95
|
id?: Ref<string | number>
|
|
100
96
|
reference?: Ref<HTMLElement | null>
|
|
101
97
|
bus?: Emitter<{
|
|
@@ -109,19 +105,28 @@ export const INJECTION_KEY_DROPDOWN_TRIGGER = Symbol.for(
|
|
|
109
105
|
'aria-haspopup': boolean
|
|
110
106
|
'aria-expanded': boolean
|
|
111
107
|
}>
|
|
112
|
-
}
|
|
108
|
+
}
|
|
109
|
+
export const INJECTION_KEY_DROPDOWN_TRIGGER = Symbol.for(
|
|
110
|
+
'dropdownTrigger',
|
|
111
|
+
) as InjectionKey<DropdownTriggerState>
|
|
112
|
+
|
|
113
|
+
export type DropdownItemState = {
|
|
114
|
+
role?: Ref<`${DropdownItemRole}`>
|
|
115
|
+
expanded?: Ref<boolean>
|
|
116
|
+
focused?: Ref<boolean>
|
|
117
|
+
hovered?: Ref<boolean>
|
|
118
|
+
}
|
|
113
119
|
export const INJECTION_KEY_DROPDOWN_ITEM = Symbol.for(
|
|
114
120
|
'dropdownItem',
|
|
115
|
-
) as InjectionKey<
|
|
116
|
-
|
|
121
|
+
) as InjectionKey<DropdownItemState>
|
|
122
|
+
|
|
123
|
+
export type DropdownActionState = {
|
|
124
|
+
role?: Ref<`${ActionRoles}`>
|
|
117
125
|
expanded?: Ref<boolean>
|
|
118
|
-
}
|
|
126
|
+
}
|
|
119
127
|
export const INJECTION_KEY_DROPDOWN_ACTION = Symbol.for(
|
|
120
128
|
'dropdownAction',
|
|
121
|
-
) as InjectionKey<
|
|
122
|
-
role?: Ref<`${ActionRoles}`>
|
|
123
|
-
expanded?: Ref<boolean>
|
|
124
|
-
}>
|
|
129
|
+
) as InjectionKey<DropdownActionState>
|
|
125
130
|
|
|
126
131
|
// alert
|
|
127
132
|
export const INJECTION_KEY_ALERT_GROUP = Symbol.for(
|
package/src/props/index.ts
CHANGED
|
@@ -13,9 +13,9 @@ import {
|
|
|
13
13
|
Strategy,
|
|
14
14
|
Position,
|
|
15
15
|
Side,
|
|
16
|
-
AnchorTarget,
|
|
17
16
|
ButtonType,
|
|
18
17
|
ActionTag,
|
|
18
|
+
StorageType,
|
|
19
19
|
} from '@/constants'
|
|
20
20
|
import type { VvIconProps } from '@/components/VvIcon'
|
|
21
21
|
|
|
@@ -34,12 +34,7 @@ export const LinkProps = {
|
|
|
34
34
|
/**
|
|
35
35
|
* Anchor target
|
|
36
36
|
*/
|
|
37
|
-
target:
|
|
38
|
-
type: String as PropType<`${AnchorTarget}`>,
|
|
39
|
-
default: undefined,
|
|
40
|
-
validator: (value: AnchorTarget) =>
|
|
41
|
-
Object.values(AnchorTarget).includes(value),
|
|
42
|
-
},
|
|
37
|
+
target: String,
|
|
43
38
|
/**
|
|
44
39
|
* Anchor rel
|
|
45
40
|
*/
|
|
@@ -549,3 +544,13 @@ export const NavProps = {
|
|
|
549
544
|
default: () => [],
|
|
550
545
|
},
|
|
551
546
|
}
|
|
547
|
+
|
|
548
|
+
export const StorageProps = {
|
|
549
|
+
storageType: {
|
|
550
|
+
type: String as PropType<`${StorageType}`>,
|
|
551
|
+
default: StorageType.local,
|
|
552
|
+
validator: (value: StorageType) =>
|
|
553
|
+
Object.values(StorageType).includes(value),
|
|
554
|
+
},
|
|
555
|
+
storageKey: String,
|
|
556
|
+
}
|
|
@@ -121,11 +121,8 @@ export const argTypes = {
|
|
|
121
121
|
control: { type: 'text' },
|
|
122
122
|
},
|
|
123
123
|
target: {
|
|
124
|
-
options: ['', '_blank', '_self', '_parent', '_top'],
|
|
125
124
|
description: 'The target attribute',
|
|
126
|
-
control: {
|
|
127
|
-
type: 'select',
|
|
128
|
-
},
|
|
125
|
+
control: { type: 'text' },
|
|
129
126
|
},
|
|
130
127
|
to: {
|
|
131
128
|
description: 'The router link / nuxt link settings',
|
package/src/types/nav.ts
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import type { AnchorTarget } from '@/constants'
|
|
2
|
-
|
|
3
1
|
export type NavItem = {
|
|
4
2
|
label: string
|
|
5
3
|
ariaLabel?: string
|
|
6
4
|
title?: string
|
|
7
5
|
to?: string | Record<string, unknown>
|
|
8
6
|
href?: string
|
|
9
|
-
target?:
|
|
7
|
+
target?: string
|
|
10
8
|
rel?: string
|
|
11
9
|
disabled?: boolean
|
|
12
10
|
current?: boolean
|