@a-vision-software/vue-input-components 1.3.2 → 1.3.4
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/vue-input-components.cjs.js +1 -1
- package/dist/vue-input-components.css +1 -1
- package/dist/vue-input-components.es.js +3818 -3769
- package/dist/vue-input-components.umd.js +1 -1
- package/package.json +1 -1
- package/src/components/Dropdown.vue +130 -3
- package/src/components/TextInput.vue +12 -11
- package/src/types/dropdown.ts +3 -0
- package/src/views/DropdownTestView.vue +2 -2
package/package.json
CHANGED
|
@@ -4,13 +4,14 @@
|
|
|
4
4
|
'dropdown--disabled': disabled,
|
|
5
5
|
'dropdown--multiple': multiple,
|
|
6
6
|
'dropdown--large-icon': iconSize === 'large',
|
|
7
|
+
'dropdown--has-error': error,
|
|
7
8
|
}" :style="{
|
|
8
|
-
'--dropdown-color': color,
|
|
9
|
+
'--dropdown-color': error ? 'var(--danger-color)' : color,
|
|
9
10
|
'--dropdown-hover-color': hoverColor,
|
|
10
11
|
'--dropdown-active-color': activeColor,
|
|
11
12
|
'--dropdown-disabled-color': disabledColor,
|
|
12
13
|
'--dropdown-background-color': backgroundColor,
|
|
13
|
-
'--dropdown-border-radius': borderRadius,
|
|
14
|
+
'--dropdown-border-radius': error ? `${borderRadius} ${borderRadius} 0 0` : borderRadius,
|
|
14
15
|
'--dropdown-padding': padding,
|
|
15
16
|
'--dropdown-max-height': maxHeight,
|
|
16
17
|
'--dropdown-width': width,
|
|
@@ -41,6 +42,14 @@
|
|
|
41
42
|
@click.stop="clearSelection" />
|
|
42
43
|
<font-awesome-icon icon="chevron-down" class="dropdown__arrow" :class="{ 'dropdown__arrow--open': isOpen }" />
|
|
43
44
|
</div>
|
|
45
|
+
<span v-if="required && !showSaved && !showChanged" class="status-indicator required-indicator">required</span>
|
|
46
|
+
<transition name="fade">
|
|
47
|
+
<span v-if="showSaved && !error" class="status-indicator saved-indicator">saved</span>
|
|
48
|
+
</transition>
|
|
49
|
+
<transition name="fade">
|
|
50
|
+
<span v-if="showChanged && !error" class="status-indicator changed-indicator">changed</span>
|
|
51
|
+
</transition>
|
|
52
|
+
<div v-if="error" class="error-message">{{ error }}</div>
|
|
44
53
|
</div>
|
|
45
54
|
|
|
46
55
|
<div v-if="isOpen" class="dropdown__content">
|
|
@@ -62,7 +71,7 @@
|
|
|
62
71
|
</template>
|
|
63
72
|
|
|
64
73
|
<script setup lang="ts">
|
|
65
|
-
import { ref, computed, watch, nextTick } from 'vue'
|
|
74
|
+
import { ref, computed, watch, nextTick, onUnmounted } from 'vue'
|
|
66
75
|
import type { DropdownProps, DropdownOption } from '../types/dropdown'
|
|
67
76
|
|
|
68
77
|
const props = withDefaults(defineProps<DropdownProps>(), {
|
|
@@ -81,10 +90,14 @@ const props = withDefaults(defineProps<DropdownProps>(), {
|
|
|
81
90
|
padding: '0.5rem',
|
|
82
91
|
icon: '',
|
|
83
92
|
iconSize: 'normal',
|
|
93
|
+
required: false,
|
|
94
|
+
error: '',
|
|
84
95
|
})
|
|
85
96
|
|
|
86
97
|
const emit = defineEmits<{
|
|
87
98
|
(e: 'update:modelValue', value: string | string[]): void
|
|
99
|
+
(e: 'changed'): void
|
|
100
|
+
(e: 'saved'): void
|
|
88
101
|
}>()
|
|
89
102
|
|
|
90
103
|
const isOpen = ref(false)
|
|
@@ -144,8 +157,10 @@ const toggleOption = (option: DropdownOption) => {
|
|
|
144
157
|
? currentValue.filter((id) => id !== option.id)
|
|
145
158
|
: [...currentValue, option.id]
|
|
146
159
|
emit('update:modelValue', newValue)
|
|
160
|
+
debounceAutosave(newValue)
|
|
147
161
|
} else {
|
|
148
162
|
emit('update:modelValue', option.id)
|
|
163
|
+
debounceAutosave(option.id)
|
|
149
164
|
closeDropdown()
|
|
150
165
|
}
|
|
151
166
|
}
|
|
@@ -179,6 +194,66 @@ watch(isOpen, (newValue) => {
|
|
|
179
194
|
document.removeEventListener('click', handleClickOutside)
|
|
180
195
|
}
|
|
181
196
|
})
|
|
197
|
+
|
|
198
|
+
const showSaved = ref(false)
|
|
199
|
+
const showChanged = ref(false)
|
|
200
|
+
const isChanged = ref(false)
|
|
201
|
+
const debounceTimer = ref<number | null>(null)
|
|
202
|
+
const changedTimer = ref<number | null>(null)
|
|
203
|
+
|
|
204
|
+
const handleAutosave = async (value: string | string[]) => {
|
|
205
|
+
if (props.autosave) {
|
|
206
|
+
try {
|
|
207
|
+
await props.autosave(value)
|
|
208
|
+
if (!props.error) {
|
|
209
|
+
emit('saved')
|
|
210
|
+
showSaved.value = true
|
|
211
|
+
showChanged.value = false
|
|
212
|
+
setTimeout(() => {
|
|
213
|
+
showSaved.value = false
|
|
214
|
+
}, 3000)
|
|
215
|
+
}
|
|
216
|
+
} catch (error) {
|
|
217
|
+
console.error('Autosave failed:', error)
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const debounceAutosave = (value: string | string[]) => {
|
|
223
|
+
// Clear existing timers
|
|
224
|
+
if (debounceTimer.value) {
|
|
225
|
+
clearTimeout(debounceTimer.value)
|
|
226
|
+
}
|
|
227
|
+
if (changedTimer.value) {
|
|
228
|
+
clearTimeout(changedTimer.value)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Show changed indicator immediately
|
|
232
|
+
if (!props.error) {
|
|
233
|
+
showChanged.value = true
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Trigger changed event after 500ms
|
|
237
|
+
changedTimer.value = window.setTimeout(() => {
|
|
238
|
+
emit('changed')
|
|
239
|
+
isChanged.value = true
|
|
240
|
+
}, 500)
|
|
241
|
+
|
|
242
|
+
// Trigger autosave after 1500ms
|
|
243
|
+
debounceTimer.value = window.setTimeout(() => {
|
|
244
|
+
handleAutosave(value)
|
|
245
|
+
}, 1500)
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Cleanup timers on unmount
|
|
249
|
+
onUnmounted(() => {
|
|
250
|
+
if (debounceTimer.value) {
|
|
251
|
+
clearTimeout(debounceTimer.value)
|
|
252
|
+
}
|
|
253
|
+
if (changedTimer.value) {
|
|
254
|
+
clearTimeout(changedTimer.value)
|
|
255
|
+
}
|
|
256
|
+
})
|
|
182
257
|
</script>
|
|
183
258
|
|
|
184
259
|
<style scoped>
|
|
@@ -197,6 +272,7 @@ watch(isOpen, (newValue) => {
|
|
|
197
272
|
}
|
|
198
273
|
|
|
199
274
|
.dropdown__selected {
|
|
275
|
+
position: relative;
|
|
200
276
|
display: grid;
|
|
201
277
|
grid-template-columns: auto 1fr auto;
|
|
202
278
|
align-items: center;
|
|
@@ -397,4 +473,55 @@ watch(isOpen, (newValue) => {
|
|
|
397
473
|
justify-content: center;
|
|
398
474
|
padding: 0.75rem 0;
|
|
399
475
|
}
|
|
476
|
+
|
|
477
|
+
.dropdown__selected.has-error {
|
|
478
|
+
border-color: var(--danger-color);
|
|
479
|
+
border-bottom-left-radius: 0;
|
|
480
|
+
border-bottom-right-radius: 0;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
.status-indicator {
|
|
484
|
+
position: absolute;
|
|
485
|
+
top: -1px;
|
|
486
|
+
line-height: 1px;
|
|
487
|
+
right: 0.5rem;
|
|
488
|
+
font-size: 0.75rem;
|
|
489
|
+
color: var(--text-muted);
|
|
490
|
+
background-color: var(--dropdown-background-color);
|
|
491
|
+
padding: 0 0.25rem;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
.saved-indicator {
|
|
495
|
+
color: var(--success-color);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
.changed-indicator {
|
|
499
|
+
color: var(--warning-color);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
.error-message {
|
|
503
|
+
position: absolute;
|
|
504
|
+
bottom: 0;
|
|
505
|
+
left: 0;
|
|
506
|
+
right: 0;
|
|
507
|
+
padding: 0.25rem 0.75rem;
|
|
508
|
+
background-color: var(--danger-color);
|
|
509
|
+
color: white;
|
|
510
|
+
font-size: 0.75rem;
|
|
511
|
+
border-radius: 0 0 0.5rem 0.5rem;
|
|
512
|
+
transform: translateY(100%);
|
|
513
|
+
transition: transform 0.2s ease;
|
|
514
|
+
line-height: var(--line-height);
|
|
515
|
+
z-index: 1;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
.fade-enter-active,
|
|
519
|
+
.fade-leave-active {
|
|
520
|
+
transition: opacity 0.2s ease;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
.fade-enter-from,
|
|
524
|
+
.fade-leave-to {
|
|
525
|
+
opacity: 0;
|
|
526
|
+
}
|
|
400
527
|
</style>
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
'has-error': error,
|
|
18
18
|
'has-icon': icon,
|
|
19
19
|
}">
|
|
20
|
-
<div v-if="icon" class="icon-wrapper"
|
|
20
|
+
<div v-if="icon" class="icon-wrapper" @click="focusInput">
|
|
21
21
|
<font-awesome-icon :icon="icon" class="icon" />
|
|
22
22
|
</div>
|
|
23
23
|
<Datepicker v-if="type === 'date'" :id="id" v-model="dateValue" :placeholder="placeholder" :disabled="disabled"
|
|
@@ -279,22 +279,23 @@ defineExpose({
|
|
|
279
279
|
border-color: var(--danger-color);
|
|
280
280
|
border-bottom-left-radius: 0;
|
|
281
281
|
border-bottom-right-radius: 0;
|
|
282
|
+
|
|
283
|
+
.icon {
|
|
284
|
+
color: var(--danger-color);
|
|
285
|
+
}
|
|
282
286
|
}
|
|
283
287
|
|
|
284
288
|
.icon-wrapper {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
display: flex;
|
|
290
|
-
align-items: center;
|
|
291
|
-
justify-content: center;
|
|
292
|
-
color: var(--text-color);
|
|
289
|
+
display: grid;
|
|
290
|
+
place-items: start;
|
|
291
|
+
padding: 1rem;
|
|
292
|
+
border-right: 1px solid rgb(from var(--border-color) r g b / 20%);
|
|
293
293
|
cursor: pointer;
|
|
294
|
+
overflow: hidden;
|
|
294
295
|
}
|
|
295
296
|
|
|
296
|
-
.icon-wrapper
|
|
297
|
-
color: var(--
|
|
297
|
+
.icon-wrapper:hover {
|
|
298
|
+
background-color: var(--input-bg-hover);
|
|
298
299
|
}
|
|
299
300
|
|
|
300
301
|
.icon {
|
package/src/types/dropdown.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
<div class="dropdown-test__section">
|
|
8
8
|
<h2>Single Select Dropdown</h2>
|
|
9
|
-
<Dropdown v-model="selectedSingle" :options="options" placeholder="Select a color" filterable
|
|
9
|
+
<Dropdown v-model="selectedSingle" :options="options" placeholder="Select a color" filterable required
|
|
10
10
|
@update:modelValue="handleSingleChange" />
|
|
11
11
|
<div v-if="selectedSingle" class="selection-info">
|
|
12
12
|
Selected: {{ getOptionLabel(selectedSingle) }}
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
<div class="dropdown-test__section">
|
|
26
26
|
<h2>Dropdown with Icon</h2>
|
|
27
27
|
<Dropdown v-model="selectedWithIcon" :options="options" placeholder="Select with icon" icon="house"
|
|
28
|
-
@update:modelValue="handleIconChange" />
|
|
28
|
+
@update:modelValue="handleIconChange" error="This is an error" />
|
|
29
29
|
<div v-if="selectedWithIcon" class="selection-info">
|
|
30
30
|
Selected: {{ getOptionLabel(selectedWithIcon) }}
|
|
31
31
|
</div>
|