@volverjs/ui-vue 0.0.9-beta.8 → 0.0.9
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 +2 -2
- package/dist/components/VvAccordion/VvAccordion.es.js +8 -7
- package/dist/components/VvAccordionGroup/VvAccordionGroup.es.js +8 -7
- package/dist/components/VvAction/VvAction.es.js +8 -7
- package/dist/components/VvAlert/VvAlert.es.js +8 -7
- package/dist/components/VvAlertGroup/VvAlertGroup.es.js +8 -7
- package/dist/components/VvAlertGroup/VvAlertGroup.vue.d.ts +7 -7
- package/dist/components/VvAlertGroup/index.d.ts +2 -2
- package/dist/components/VvAvatar/VvAvatar.es.js +8 -7
- package/dist/components/VvAvatarGroup/VvAvatarGroup.es.js +8 -7
- package/dist/components/VvBadge/VvBadge.es.js +8 -7
- package/dist/components/VvBreadcrumb/VvBreadcrumb.es.js +8 -7
- package/dist/components/VvButton/VvButton.es.js +8 -7
- package/dist/components/VvButtonGroup/VvButtonGroup.es.js +8 -7
- package/dist/components/VvCard/VvCard.es.js +8 -7
- package/dist/components/VvCheckbox/VvCheckbox.es.js +28 -20
- package/dist/components/VvCheckbox/VvCheckbox.umd.js +1 -1
- package/dist/components/VvCheckboxGroup/VvCheckboxGroup.es.js +28 -20
- package/dist/components/VvCheckboxGroup/VvCheckboxGroup.umd.js +1 -1
- package/dist/components/VvCombobox/VvCombobox.es.js +165 -129
- package/dist/components/VvCombobox/VvCombobox.umd.js +1 -1
- package/dist/components/VvCombobox/VvCombobox.vue.d.ts +306 -90
- package/dist/components/VvCombobox/index.d.ts +108 -31
- package/dist/components/VvDialog/VvDialog.es.js +37 -31
- package/dist/components/VvDialog/VvDialog.umd.js +1 -1
- package/dist/components/VvDropdown/VvDropdown.es.js +19 -16
- package/dist/components/VvDropdown/VvDropdown.umd.js +1 -1
- package/dist/components/VvDropdown/VvDropdown.vue.d.ts +301 -93
- package/dist/components/VvDropdown/index.d.ts +99 -30
- package/dist/components/VvDropdownAction/VvDropdownAction.es.js +8 -7
- package/dist/components/VvDropdownOptgroup/VvDropdownOptgroup.es.js +8 -7
- package/dist/components/VvDropdownOption/VvDropdownOption.es.js +8 -7
- package/dist/components/VvInputText/VvInputText.es.js +212 -115
- package/dist/components/VvInputText/VvInputText.umd.js +1 -1
- package/dist/components/VvInputText/VvInputText.vue.d.ts +19 -37
- package/dist/components/VvInputText/index.d.ts +15 -33
- package/dist/components/VvNav/VvNav.es.js +8 -7
- package/dist/components/VvProgress/VvProgress.es.js +8 -7
- package/dist/components/VvRadio/VvRadio.es.js +28 -20
- package/dist/components/VvRadio/VvRadio.umd.js +1 -1
- package/dist/components/VvRadioGroup/VvRadioGroup.es.js +28 -20
- package/dist/components/VvRadioGroup/VvRadioGroup.umd.js +1 -1
- package/dist/components/VvSelect/VvSelect.es.js +28 -20
- package/dist/components/VvSelect/VvSelect.umd.js +1 -1
- package/dist/components/VvSelect/VvSelect.vue.d.ts +1 -1
- package/dist/components/VvTab/VvTab.es.js +8 -7
- package/dist/components/VvTextarea/VvTextarea.es.js +36 -25
- package/dist/components/VvTextarea/VvTextarea.umd.js +1 -1
- package/dist/components/VvTooltip/VvTooltip.es.js +8 -7
- package/dist/components/index.es.js +390 -257
- package/dist/components/index.umd.js +1 -1
- package/dist/constants.d.ts +4 -0
- package/dist/directives/index.es.js +8 -7
- package/dist/directives/v-tooltip.es.js +8 -7
- package/dist/icons.es.js +3 -3
- package/dist/icons.umd.js +1 -1
- package/dist/props/index.d.ts +100 -31
- package/dist/resolvers/unplugin.es.js +3 -0
- package/dist/resolvers/unplugin.umd.js +1 -1
- package/dist/stories/AccordionGroup/AccordionGroup.stories.d.ts +1 -1
- package/dist/stories/AccordionGroup/AccordionGroupSlots.stories.d.ts +11 -11
- package/dist/stories/Combobox/Combobox.settings.d.ts +8 -0
- package/dist/stories/InputText/InputText.settings.d.ts +31 -9
- package/dist/stories/InputText/InputText.stories.d.ts +0 -1
- package/dist/stories/InputText/InputTextMask.stories.d.ts +12 -0
- package/package.json +63 -62
- 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/VvAlertGroup/VvAlertGroup.vue +2 -0
- package/src/components/VvCombobox/VvCombobox.vue +40 -19
- package/src/components/VvCombobox/index.ts +13 -0
- package/src/components/VvDialog/VvDialog.vue +16 -13
- package/src/components/VvDropdown/VvDropdown.vue +18 -16
- package/src/components/VvInputText/VvInputText.vue +170 -55
- package/src/components/VvInputText/index.ts +32 -34
- package/src/components/VvTextarea/VvTextarea.vue +8 -5
- package/src/components/common/HintSlot.ts +20 -12
- package/src/constants.ts +5 -0
- package/src/props/index.ts +7 -11
- package/src/resolvers/unplugin.ts +3 -0
- package/src/stories/Combobox/Combobox.settings.ts +8 -0
- package/src/stories/Combobox/Combobox.test.ts +1 -1
- package/src/stories/InputText/InputText.settings.ts +36 -15
- package/src/stories/InputText/InputText.stories.ts +4 -12
- package/src/stories/InputText/InputText.test.ts +31 -15
- package/src/stories/InputText/InputTextMask.stories.ts +122 -0
|
@@ -8,8 +8,10 @@
|
|
|
8
8
|
import { useVvAlertGroup, VvAlertGroupEvents, VvAlertGroupProps } from '.'
|
|
9
9
|
import VvAlert from '../VvAlert/VvAlert.vue'
|
|
10
10
|
|
|
11
|
+
// props and emit
|
|
11
12
|
const props = defineProps(VvAlertGroupProps)
|
|
12
13
|
const emit = defineEmits(VvAlertGroupEvents)
|
|
14
|
+
|
|
13
15
|
const { hasProps, hasTransition } = useVvAlertGroup(props, emit)
|
|
14
16
|
|
|
15
17
|
const alertGroupTransitionHandlers = {
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
<script setup lang="ts">
|
|
14
14
|
import type { Ref } from 'vue'
|
|
15
|
+
import { toRefs } from 'vue'
|
|
15
16
|
import { VvComboboxProps, VvComboboxEvents } from '.'
|
|
16
17
|
import VvIcon from '../VvIcon/VvIcon.vue'
|
|
17
18
|
import VvDropdown from '../VvDropdown/VvDropdown.vue'
|
|
@@ -84,7 +85,7 @@
|
|
|
84
85
|
const searchText = ref('')
|
|
85
86
|
const debouncedSearchText = refDebounced(
|
|
86
87
|
searchText,
|
|
87
|
-
Number(props.debounceSearch),
|
|
88
|
+
computed(() => Number(props.debounceSearch)),
|
|
88
89
|
)
|
|
89
90
|
watch(debouncedSearchText, () =>
|
|
90
91
|
emit('change:search', debouncedSearchText.value),
|
|
@@ -105,7 +106,7 @@
|
|
|
105
106
|
expanded.value = false
|
|
106
107
|
}
|
|
107
108
|
const onAfterExpand = () => {
|
|
108
|
-
if (
|
|
109
|
+
if (propsDefaults.value.searchable) {
|
|
109
110
|
if (inputSearchEl.value) {
|
|
110
111
|
inputSearchEl.value.focus({
|
|
111
112
|
preventScroll: true,
|
|
@@ -114,7 +115,7 @@
|
|
|
114
115
|
}
|
|
115
116
|
}
|
|
116
117
|
const onAfterCollapse = () => {
|
|
117
|
-
if (
|
|
118
|
+
if (propsDefaults.value.searchable) {
|
|
118
119
|
searchText.value = ''
|
|
119
120
|
}
|
|
120
121
|
}
|
|
@@ -131,7 +132,6 @@
|
|
|
131
132
|
valid,
|
|
132
133
|
invalid,
|
|
133
134
|
floating,
|
|
134
|
-
searchable,
|
|
135
135
|
} = toRefs(props)
|
|
136
136
|
const hasId = useUniqueId(id)
|
|
137
137
|
const hasHintId = computed(() => `${hasId.value}-hint`)
|
|
@@ -139,6 +139,10 @@
|
|
|
139
139
|
const hasSearchId = computed(() => `${hasId.value}-search`)
|
|
140
140
|
const hasLabelId = computed(() => `${hasId.value}-label`)
|
|
141
141
|
|
|
142
|
+
// loading
|
|
143
|
+
const localLoading = ref(false)
|
|
144
|
+
const isLoading = computed(() => localLoading.value || loading.value)
|
|
145
|
+
|
|
142
146
|
// ref
|
|
143
147
|
const dropdownEl = ref()
|
|
144
148
|
|
|
@@ -162,7 +166,7 @@
|
|
|
162
166
|
modifiers,
|
|
163
167
|
computed(() => ({
|
|
164
168
|
disabled: disabled.value,
|
|
165
|
-
loading:
|
|
169
|
+
loading: isLoading.value,
|
|
166
170
|
readonly: readonly.value,
|
|
167
171
|
'icon-before': Boolean(hasIconBefore.value),
|
|
168
172
|
'icon-after': Boolean(hasIconAfter.value),
|
|
@@ -183,7 +187,18 @@
|
|
|
183
187
|
} = useOptions(props)
|
|
184
188
|
|
|
185
189
|
// options filtered by search text
|
|
186
|
-
const filteredOptions =
|
|
190
|
+
const filteredOptions = computedAsync(async () => {
|
|
191
|
+
if (propsDefaults.value.searchFunction) {
|
|
192
|
+
localLoading.value = true
|
|
193
|
+
const toReturn = await Promise.resolve(
|
|
194
|
+
propsDefaults.value.searchFunction(
|
|
195
|
+
debouncedSearchText.value,
|
|
196
|
+
props.options,
|
|
197
|
+
),
|
|
198
|
+
)
|
|
199
|
+
localLoading.value = false
|
|
200
|
+
return toReturn
|
|
201
|
+
}
|
|
187
202
|
return props.options?.filter((option) => {
|
|
188
203
|
return getOptionLabel(option)
|
|
189
204
|
.toLowerCase()
|
|
@@ -221,12 +236,15 @@
|
|
|
221
236
|
} else if (props.modelValue) {
|
|
222
237
|
selectedValues = [props.modelValue]
|
|
223
238
|
}
|
|
224
|
-
const options = props.options.reduce(
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
239
|
+
const options = props.options.reduce(
|
|
240
|
+
(acc, value) => {
|
|
241
|
+
if (isGroup(value)) {
|
|
242
|
+
return [...acc, ...getOptionGrouped(value)]
|
|
243
|
+
}
|
|
244
|
+
return [...acc, value]
|
|
245
|
+
},
|
|
246
|
+
[] as Array<Option | string>,
|
|
247
|
+
)
|
|
230
248
|
|
|
231
249
|
return options.filter((option) => {
|
|
232
250
|
if (isGroup(option)) {
|
|
@@ -304,7 +322,7 @@
|
|
|
304
322
|
invalid: invalid.value,
|
|
305
323
|
invalidLabel: propsDefaults.value.invalidLabel,
|
|
306
324
|
hintLabel: propsDefaults.value.hintLabel,
|
|
307
|
-
loading:
|
|
325
|
+
loading: isLoading.value,
|
|
308
326
|
loadingLabel: propsDefaults.value.loadingLabel,
|
|
309
327
|
disabled: disabled.value,
|
|
310
328
|
readonly: readonly.value,
|
|
@@ -333,7 +351,7 @@
|
|
|
333
351
|
flip: propsDefaults.value.flip,
|
|
334
352
|
autoPlacement: propsDefaults.value.autoPlacement,
|
|
335
353
|
arrow: propsDefaults.value.arrow,
|
|
336
|
-
autofocusFirst:
|
|
354
|
+
autofocusFirst: propsDefaults.value.searchable
|
|
337
355
|
? true
|
|
338
356
|
: propsDefaults.value.autofocusFirst,
|
|
339
357
|
triggerWidth: propsDefaults.value.triggerWidth,
|
|
@@ -369,7 +387,7 @@
|
|
|
369
387
|
<label
|
|
370
388
|
v-if="label"
|
|
371
389
|
:id="hasLabelId"
|
|
372
|
-
:for="searchable ? hasSearchId : undefined"
|
|
390
|
+
:for="propsDefaults.searchable ? hasSearchId : undefined"
|
|
373
391
|
>
|
|
374
392
|
{{ label }}
|
|
375
393
|
</label>
|
|
@@ -383,13 +401,15 @@
|
|
|
383
401
|
@after-collapse="onAfterCollapse"
|
|
384
402
|
>
|
|
385
403
|
<template
|
|
386
|
-
v-if="
|
|
404
|
+
v-if="
|
|
405
|
+
propsDefaults.searchable || $slots['dropdown::before']
|
|
406
|
+
"
|
|
387
407
|
#before
|
|
388
408
|
>
|
|
389
409
|
<!-- @slot Slot before dropdown items -->
|
|
390
410
|
<slot name="dropdown::before" />
|
|
391
411
|
<input
|
|
392
|
-
v-if="searchable"
|
|
412
|
+
v-if="propsDefaults.searchable && !disabled"
|
|
393
413
|
:id="hasSearchId"
|
|
394
414
|
ref="inputSearchEl"
|
|
395
415
|
v-model="searchText"
|
|
@@ -460,6 +480,7 @@
|
|
|
460
480
|
:aria-label="
|
|
461
481
|
propsDefaults.deselectActionLabel
|
|
462
482
|
"
|
|
483
|
+
type="button"
|
|
463
484
|
@click.stop="onInput(option)"
|
|
464
485
|
>
|
|
465
486
|
<VvIcon name="close" />
|
|
@@ -483,7 +504,7 @@
|
|
|
483
504
|
</div>
|
|
484
505
|
</template>
|
|
485
506
|
<template #items>
|
|
486
|
-
<template v-if="filteredOptions
|
|
507
|
+
<template v-if="!disabled && filteredOptions?.length">
|
|
487
508
|
<template
|
|
488
509
|
v-for="(option, index) in filteredOptions"
|
|
489
510
|
:key="index"
|
|
@@ -565,7 +586,7 @@
|
|
|
565
586
|
{{ propsDefaults.noOptionsLabel }}
|
|
566
587
|
</slot>
|
|
567
588
|
</VvDropdownOption>
|
|
568
|
-
<VvDropdownOption v-else modifiers="inert">
|
|
589
|
+
<VvDropdownOption v-else-if="!disabled" modifiers="inert">
|
|
569
590
|
<!-- @slot Slot for no results available -->
|
|
570
591
|
<slot name="no-results">
|
|
571
592
|
{{ propsDefaults.noResultsLabel }}
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
DropdownProps,
|
|
17
17
|
LabelProps,
|
|
18
18
|
} from '../../props'
|
|
19
|
+
import type { Option } from '../../types/generic'
|
|
19
20
|
|
|
20
21
|
export const VvComboboxEvents = [
|
|
21
22
|
'update:modelValue',
|
|
@@ -90,6 +91,18 @@ export const VvComboboxProps = {
|
|
|
90
91
|
* Use input text to search on options
|
|
91
92
|
*/
|
|
92
93
|
searchable: Boolean,
|
|
94
|
+
/**
|
|
95
|
+
* Search function to filter options
|
|
96
|
+
*/
|
|
97
|
+
searchFunction: {
|
|
98
|
+
type: Function as PropType<
|
|
99
|
+
(
|
|
100
|
+
search: string,
|
|
101
|
+
options: (Option | string)[],
|
|
102
|
+
) => (Option | string)[] | Promise<(Option | string)[]>
|
|
103
|
+
>,
|
|
104
|
+
default: undefined,
|
|
105
|
+
},
|
|
93
106
|
/**
|
|
94
107
|
* On searchable select is the input search placeholder
|
|
95
108
|
*/
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
// data
|
|
18
18
|
const modelValue = useVModel(props, 'modelValue', emit)
|
|
19
19
|
const localModelValue = ref(false)
|
|
20
|
-
const
|
|
20
|
+
const isOpened = computed({
|
|
21
21
|
get: () => modelValue.value ?? localModelValue.value,
|
|
22
22
|
set: (newValue) => {
|
|
23
23
|
if (modelValue.value === undefined) {
|
|
@@ -49,12 +49,16 @@
|
|
|
49
49
|
const transitioName = computed(() => `vv-dialog--${props.transition}`)
|
|
50
50
|
const dialogTransitionHandlers = {
|
|
51
51
|
'before-enter': () => {
|
|
52
|
-
dialogEl.value?.
|
|
52
|
+
if (!dialogEl.value?.open) {
|
|
53
|
+
dialogEl.value?.showModal()
|
|
54
|
+
}
|
|
53
55
|
emit('open')
|
|
54
56
|
emit('before-enter')
|
|
55
57
|
},
|
|
56
58
|
'after-leave': () => {
|
|
57
|
-
dialogEl.value?.
|
|
59
|
+
if (dialogEl.value?.open) {
|
|
60
|
+
dialogEl.value?.close()
|
|
61
|
+
}
|
|
58
62
|
emit('close')
|
|
59
63
|
emit('after-leave')
|
|
60
64
|
},
|
|
@@ -80,37 +84,36 @@
|
|
|
80
84
|
|
|
81
85
|
// methods
|
|
82
86
|
onClickOutside(modalWrapper, () => {
|
|
83
|
-
if (!props.keepOpen
|
|
84
|
-
|
|
87
|
+
if (!props.keepOpen) {
|
|
88
|
+
close()
|
|
85
89
|
}
|
|
86
90
|
})
|
|
87
91
|
|
|
88
92
|
function close() {
|
|
89
|
-
|
|
93
|
+
isOpened.value = false
|
|
90
94
|
}
|
|
91
95
|
|
|
92
96
|
function open() {
|
|
93
|
-
|
|
97
|
+
isOpened.value = true
|
|
94
98
|
}
|
|
95
99
|
|
|
96
100
|
defineExpose({ close, open })
|
|
97
101
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (opened.value) {
|
|
101
|
-
e.preventDefault()
|
|
102
|
+
const onCancel = () => {
|
|
103
|
+
if (!props.keepOpen) {
|
|
102
104
|
close()
|
|
103
105
|
}
|
|
104
|
-
}
|
|
106
|
+
}
|
|
105
107
|
</script>
|
|
106
108
|
|
|
107
109
|
<template>
|
|
108
110
|
<Transition :name="transitioName" v-on="dialogTransitionHandlers">
|
|
109
111
|
<dialog
|
|
110
|
-
v-show="
|
|
112
|
+
v-show="isOpened"
|
|
111
113
|
v-bind="dialogAttrs"
|
|
112
114
|
ref="dialogEl"
|
|
113
115
|
:class="dialogClass"
|
|
116
|
+
@cancel.stop.prevent="onCancel"
|
|
114
117
|
>
|
|
115
118
|
<article ref="modalWrapper" class="vv-dialog__wrapper">
|
|
116
119
|
<header v-if="$slots.header || title" class="vv-dialog__header">
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
ShiftOptions,
|
|
26
26
|
SizeOptions,
|
|
27
27
|
} from '../../types/floating-ui'
|
|
28
|
+
import { Side, Strategy } from '../../constants'
|
|
28
29
|
|
|
29
30
|
// props, emit and attrs
|
|
30
31
|
const props = defineProps(VvDropdownProps)
|
|
@@ -96,7 +97,7 @@
|
|
|
96
97
|
}
|
|
97
98
|
} else if (props.flip) {
|
|
98
99
|
if (typeof props.flip === 'boolean') {
|
|
99
|
-
toReturn.push(flip())
|
|
100
|
+
toReturn.push(flip({ fallbackStrategy: 'initialPlacement' }))
|
|
100
101
|
} else {
|
|
101
102
|
toReturn.push(flip(props.flip as FlipOptions))
|
|
102
103
|
}
|
|
@@ -157,13 +158,13 @@
|
|
|
157
158
|
|
|
158
159
|
return toReturn
|
|
159
160
|
})
|
|
160
|
-
const { x, y,
|
|
161
|
+
const { x, y, middlewareData, placement, strategy } = useFloating(
|
|
161
162
|
referenceEl,
|
|
162
163
|
floatingEl,
|
|
163
164
|
{
|
|
164
165
|
whileElementsMounted: (...args) => {
|
|
165
166
|
return autoUpdate(...args, {
|
|
166
|
-
animationFrame: props.strategy ===
|
|
167
|
+
animationFrame: props.strategy === Strategy.fixed,
|
|
167
168
|
})
|
|
168
169
|
},
|
|
169
170
|
placement: computed(() => props.placement),
|
|
@@ -175,16 +176,17 @@
|
|
|
175
176
|
if (hasCustomPosition.value) {
|
|
176
177
|
return undefined
|
|
177
178
|
}
|
|
179
|
+
const width =
|
|
180
|
+
props.triggerWidth && referenceEl.value
|
|
181
|
+
? `${referenceEl.value?.offsetWidth}px`
|
|
182
|
+
: undefined
|
|
178
183
|
return {
|
|
179
184
|
position: strategy.value,
|
|
180
185
|
top: `${y.value ?? 0}px`,
|
|
181
186
|
left: `${x.value ?? 0}px`,
|
|
182
|
-
maxWidth: maxWidth.value,
|
|
187
|
+
maxWidth: width ? undefined : maxWidth.value,
|
|
183
188
|
maxHeight: maxHeight.value,
|
|
184
|
-
width
|
|
185
|
-
props.triggerWidth && referenceEl.value
|
|
186
|
-
? `${referenceEl.value.offsetWidth}px`
|
|
187
|
-
: undefined,
|
|
189
|
+
width,
|
|
188
190
|
}
|
|
189
191
|
})
|
|
190
192
|
|
|
@@ -192,20 +194,20 @@
|
|
|
192
194
|
const side = computed(
|
|
193
195
|
() =>
|
|
194
196
|
placement.value.split('-')[0] as
|
|
195
|
-
|
|
|
196
|
-
|
|
|
197
|
-
|
|
|
198
|
-
|
|
|
197
|
+
| Side.top
|
|
198
|
+
| Side.right
|
|
199
|
+
| Side.bottom
|
|
200
|
+
| Side.left,
|
|
199
201
|
)
|
|
200
202
|
const arrowPlacement = computed(() => {
|
|
201
203
|
if (hasCustomPosition.value) {
|
|
202
204
|
return undefined
|
|
203
205
|
}
|
|
204
206
|
const staticSide = {
|
|
205
|
-
top:
|
|
206
|
-
right:
|
|
207
|
-
bottom:
|
|
208
|
-
left:
|
|
207
|
+
[Side.top]: Side.bottom,
|
|
208
|
+
[Side.right]: Side.left,
|
|
209
|
+
[Side.bottom]: Side.top,
|
|
210
|
+
[Side.left]: Side.right,
|
|
209
211
|
}[side.value]
|
|
210
212
|
return {
|
|
211
213
|
left:
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
<script setup lang="ts">
|
|
8
8
|
import type { InputHTMLAttributes } from 'vue'
|
|
9
|
-
import {
|
|
9
|
+
import { useIMask } from 'vue-imask'
|
|
10
10
|
import HintSlotFactory from '../common/HintSlot'
|
|
11
11
|
import VvIcon from '../VvIcon/VvIcon.vue'
|
|
12
12
|
import VvInputTextActionsFactory from '../VvInputText/VvInputTextActions'
|
|
@@ -29,12 +29,6 @@
|
|
|
29
29
|
props,
|
|
30
30
|
)
|
|
31
31
|
|
|
32
|
-
// template refs
|
|
33
|
-
const inputEl = ref()
|
|
34
|
-
const innerEl = ref()
|
|
35
|
-
|
|
36
|
-
defineExpose({ $inner: innerEl })
|
|
37
|
-
|
|
38
32
|
// data
|
|
39
33
|
const {
|
|
40
34
|
id,
|
|
@@ -46,6 +40,12 @@
|
|
|
46
40
|
valid,
|
|
47
41
|
invalid,
|
|
48
42
|
loading,
|
|
43
|
+
debounce,
|
|
44
|
+
maxlength,
|
|
45
|
+
minlength,
|
|
46
|
+
type,
|
|
47
|
+
iMask,
|
|
48
|
+
step,
|
|
49
49
|
} = toRefs(props)
|
|
50
50
|
const hasId = useUniqueId(id)
|
|
51
51
|
const hasHintId = computed(() => `${hasId.value}-hint`)
|
|
@@ -54,35 +54,144 @@
|
|
|
54
54
|
props.floating && isEmpty(props.placeholder) ? ' ' : props.placeholder,
|
|
55
55
|
)
|
|
56
56
|
|
|
57
|
-
//
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
57
|
+
// template refs
|
|
58
|
+
const maskReady = ref(false)
|
|
59
|
+
const { el, mask, typed, masked, unmasked } = useIMask(
|
|
60
|
+
computed(
|
|
61
|
+
() =>
|
|
62
|
+
iMask?.value ?? {
|
|
63
|
+
mask: /./,
|
|
64
|
+
},
|
|
65
|
+
),
|
|
62
66
|
{
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
67
|
+
emit,
|
|
68
|
+
onAccept: () => {
|
|
69
|
+
if (!maskReady.value) {
|
|
70
|
+
return
|
|
66
71
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
+
emit('update:masked', masked.value)
|
|
73
|
+
if (type.value === INPUT_TYPES.NUMBER) {
|
|
74
|
+
if (masked.value === '') {
|
|
75
|
+
if (
|
|
76
|
+
localModelValue.value === null ||
|
|
77
|
+
localModelValue.value === undefined
|
|
78
|
+
) {
|
|
79
|
+
return
|
|
80
|
+
}
|
|
81
|
+
localModelValue.value = undefined
|
|
82
|
+
return
|
|
83
|
+
}
|
|
84
|
+
if (typeof typed.value !== 'number') {
|
|
85
|
+
localModelValue.value = Number(typed.value)
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
localModelValue.value = typed.value
|
|
89
|
+
return
|
|
90
|
+
}
|
|
91
|
+
if (type.value === INPUT_TYPES.DATE) {
|
|
92
|
+
if (
|
|
93
|
+
el.value instanceof HTMLInputElement &&
|
|
94
|
+
el.value.type === 'date'
|
|
95
|
+
) {
|
|
96
|
+
localModelValue.value = el.value.value
|
|
97
|
+
return
|
|
98
|
+
}
|
|
99
|
+
let date = typed.value
|
|
100
|
+
if (date === null || date === '') {
|
|
101
|
+
if (!localModelValue.value) {
|
|
102
|
+
return
|
|
103
|
+
}
|
|
104
|
+
localModelValue.value = ''
|
|
105
|
+
return
|
|
106
|
+
}
|
|
107
|
+
if (!(date instanceof Date)) {
|
|
108
|
+
date = new Date(date)
|
|
109
|
+
}
|
|
110
|
+
localModelValue.value = `${date.getFullYear()}-${(
|
|
111
|
+
'0' +
|
|
112
|
+
(date.getMonth() + 1)
|
|
113
|
+
).slice(-2)}-${('0' + date.getDate()).slice(-2)}`
|
|
114
|
+
return
|
|
115
|
+
}
|
|
116
|
+
if (type.value === INPUT_TYPES.DATETIME_LOCAL) {
|
|
117
|
+
if (
|
|
118
|
+
el.value instanceof HTMLInputElement &&
|
|
119
|
+
el.value.type === 'datetime-local'
|
|
120
|
+
) {
|
|
121
|
+
localModelValue.value = el.value.value
|
|
122
|
+
return
|
|
123
|
+
}
|
|
124
|
+
let date = typed.value
|
|
125
|
+
if (date === null || date === '') {
|
|
126
|
+
if (!localModelValue.value) {
|
|
127
|
+
return
|
|
128
|
+
}
|
|
129
|
+
localModelValue.value = ''
|
|
130
|
+
return
|
|
131
|
+
}
|
|
132
|
+
if (!(typed.value instanceof Date)) {
|
|
133
|
+
date = new Date(date)
|
|
134
|
+
}
|
|
135
|
+
localModelValue.value = `${date.getFullYear()}-${(
|
|
136
|
+
'0' +
|
|
137
|
+
(date.getMonth() + 1)
|
|
138
|
+
).slice(-2)}-${('0' + date.getDate()).slice(-2)}T${(
|
|
139
|
+
'0' + date.getHours()
|
|
140
|
+
).slice(-2)}:${('0' + date.getMinutes()).slice(-2)}`
|
|
141
|
+
return
|
|
72
142
|
}
|
|
73
|
-
if (
|
|
74
|
-
return
|
|
143
|
+
if (!localModelValue.value && !unmasked.value) {
|
|
144
|
+
return
|
|
75
145
|
}
|
|
76
|
-
|
|
146
|
+
localModelValue.value = unmasked.value
|
|
77
147
|
},
|
|
78
148
|
},
|
|
79
149
|
)
|
|
150
|
+
onMounted(() => {
|
|
151
|
+
if (mask.value) {
|
|
152
|
+
maskReady.value = true
|
|
153
|
+
typed.value = localModelValue.value ?? ''
|
|
154
|
+
}
|
|
155
|
+
})
|
|
156
|
+
watch(
|
|
157
|
+
() => props.modelValue,
|
|
158
|
+
(newValue) => {
|
|
159
|
+
if (mask.value) {
|
|
160
|
+
typed.value =
|
|
161
|
+
newValue && iMask?.value?.mask === Date
|
|
162
|
+
? new Date(newValue)
|
|
163
|
+
: newValue ?? ''
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
)
|
|
167
|
+
watch(
|
|
168
|
+
() => props.masked,
|
|
169
|
+
(newValue) => {
|
|
170
|
+
masked.value = newValue ?? ''
|
|
171
|
+
},
|
|
172
|
+
)
|
|
173
|
+
const inputEl = el as Ref<HTMLInputElement>
|
|
174
|
+
const innerEl = ref()
|
|
175
|
+
|
|
176
|
+
defineExpose({ $inner: innerEl })
|
|
177
|
+
|
|
178
|
+
// debounce
|
|
179
|
+
const localModelValue = useDebouncedInput(
|
|
180
|
+
modelValue,
|
|
181
|
+
emit,
|
|
182
|
+
debounce?.value ?? 0,
|
|
183
|
+
)
|
|
80
184
|
|
|
81
185
|
// focus
|
|
82
186
|
const { focused } = useComponentFocus(inputEl, emit)
|
|
83
187
|
const isFocused = computed(
|
|
84
188
|
() => focused.value && !props.disabled && !props.readonly,
|
|
85
189
|
)
|
|
190
|
+
watch(isFocused, (newValue) => {
|
|
191
|
+
if (newValue && propsDefaults.value.selectOnFocus && inputEl.value) {
|
|
192
|
+
inputEl.value.select()
|
|
193
|
+
}
|
|
194
|
+
})
|
|
86
195
|
|
|
87
196
|
// visibility
|
|
88
197
|
const isVisible = useElementVisibility(inputEl)
|
|
@@ -113,12 +222,21 @@
|
|
|
113
222
|
const isNumber = computed(() => props.type === INPUT_TYPES.NUMBER)
|
|
114
223
|
const onStepUp = () => {
|
|
115
224
|
if (isClickable.value) {
|
|
225
|
+
if (iMask?.value) {
|
|
226
|
+
typed.value = typed.value + Number(step?.value ?? 1)
|
|
227
|
+
return
|
|
228
|
+
}
|
|
116
229
|
inputEl.value.stepUp()
|
|
117
230
|
localModelValue.value = unref(inputEl).value
|
|
118
231
|
}
|
|
119
232
|
}
|
|
120
233
|
const onStepDown = () => {
|
|
121
234
|
if (isClickable.value) {
|
|
235
|
+
if (iMask?.value) {
|
|
236
|
+
typed.value = typed.value - Number(step?.value ?? 1)
|
|
237
|
+
|
|
238
|
+
return
|
|
239
|
+
}
|
|
122
240
|
inputEl.value.stepDown()
|
|
123
241
|
localModelValue.value = unref(inputEl).value
|
|
124
242
|
}
|
|
@@ -127,7 +245,7 @@
|
|
|
127
245
|
// search
|
|
128
246
|
const isSearch = computed(() => props.type === INPUT_TYPES.SEARCH)
|
|
129
247
|
const onClear = () => {
|
|
130
|
-
localModelValue.value =
|
|
248
|
+
localModelValue.value = ''
|
|
131
249
|
}
|
|
132
250
|
|
|
133
251
|
// icons
|
|
@@ -153,9 +271,9 @@
|
|
|
153
271
|
|
|
154
272
|
// count
|
|
155
273
|
const { formatted: countFormatted } = useTextCount(localModelValue, {
|
|
156
|
-
mode:
|
|
157
|
-
upperLimit: Number(
|
|
158
|
-
lowerLimit: Number(
|
|
274
|
+
mode: count.value,
|
|
275
|
+
upperLimit: Number(maxlength?.value),
|
|
276
|
+
lowerLimit: Number(minlength?.value),
|
|
159
277
|
})
|
|
160
278
|
|
|
161
279
|
// tabindex
|
|
@@ -207,6 +325,9 @@
|
|
|
207
325
|
if (isDateTime.value && !isDirty.value && !focused.value) {
|
|
208
326
|
return INPUT_TYPES.TEXT
|
|
209
327
|
}
|
|
328
|
+
if (iMask?.value) {
|
|
329
|
+
return INPUT_TYPES.TEXT
|
|
330
|
+
}
|
|
210
331
|
return props.type
|
|
211
332
|
})()
|
|
212
333
|
const toReturn: InputHTMLAttributes = {
|
|
@@ -299,33 +420,6 @@
|
|
|
299
420
|
props,
|
|
300
421
|
)
|
|
301
422
|
|
|
302
|
-
// mask
|
|
303
|
-
const mask = ref()
|
|
304
|
-
watch(
|
|
305
|
-
[
|
|
306
|
-
() => props.mask,
|
|
307
|
-
() => props.type,
|
|
308
|
-
() => props.maskEager,
|
|
309
|
-
() => props.maskReversed,
|
|
310
|
-
() => props.maskTokens,
|
|
311
|
-
() => props.maskTokensReplace,
|
|
312
|
-
],
|
|
313
|
-
([newMask, newType, eager, reversed, tokens, tokensReplace]) => {
|
|
314
|
-
if (newMask && newType === INPUT_TYPES.TEXT) {
|
|
315
|
-
mask.value = new Mask({
|
|
316
|
-
mask: newMask,
|
|
317
|
-
eager,
|
|
318
|
-
reversed,
|
|
319
|
-
tokens,
|
|
320
|
-
tokensReplace,
|
|
321
|
-
})
|
|
322
|
-
return
|
|
323
|
-
}
|
|
324
|
-
mask.value = undefined
|
|
325
|
-
},
|
|
326
|
-
{ immediate: true },
|
|
327
|
-
)
|
|
328
|
-
|
|
329
423
|
// auto-width
|
|
330
424
|
const onClickInner = () => {
|
|
331
425
|
if (isClickable.value) {
|
|
@@ -343,6 +437,26 @@
|
|
|
343
437
|
: undefined,
|
|
344
438
|
}
|
|
345
439
|
})
|
|
440
|
+
|
|
441
|
+
// keydown
|
|
442
|
+
const onKeyDown = (event: KeyboardEvent) => {
|
|
443
|
+
switch (event.code) {
|
|
444
|
+
case 'ArrowUp':
|
|
445
|
+
if (isNumber.value) {
|
|
446
|
+
onStepUp()
|
|
447
|
+
event.preventDefault()
|
|
448
|
+
}
|
|
449
|
+
break
|
|
450
|
+
|
|
451
|
+
case 'ArrowDown':
|
|
452
|
+
if (isNumber.value) {
|
|
453
|
+
onStepDown()
|
|
454
|
+
event.preventDefault()
|
|
455
|
+
}
|
|
456
|
+
break
|
|
457
|
+
}
|
|
458
|
+
emit('keydown', event)
|
|
459
|
+
}
|
|
346
460
|
</script>
|
|
347
461
|
|
|
348
462
|
<template>
|
|
@@ -368,10 +482,11 @@
|
|
|
368
482
|
<input
|
|
369
483
|
:id="hasId"
|
|
370
484
|
ref="inputEl"
|
|
371
|
-
v-model="localModelValue"
|
|
372
485
|
v-bind="hasAttrs"
|
|
373
486
|
:style="hasStyle"
|
|
374
487
|
@keyup="emit('keyup', $event)"
|
|
488
|
+
@keydown="onKeyDown"
|
|
489
|
+
@keypress="emit('keypress', $event)"
|
|
375
490
|
/>
|
|
376
491
|
<div
|
|
377
492
|
v-if="(unit || $slots.unit) && isDirty"
|