@a-vision-software/vue-input-components 1.3.2 → 1.3.3
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 +3817 -3769
- package/dist/vue-input-components.umd.js +1 -1
- package/package.json +1 -1
- package/src/components/Dropdown.vue +127 -1
- package/src/components/TextInput.vue +12 -11
- package/src/types/dropdown.ts +3 -0
- package/src/views/DropdownTestView.vue +1 -1
package/package.json
CHANGED
|
@@ -41,6 +41,14 @@
|
|
|
41
41
|
@click.stop="clearSelection" />
|
|
42
42
|
<font-awesome-icon icon="chevron-down" class="dropdown__arrow" :class="{ 'dropdown__arrow--open': isOpen }" />
|
|
43
43
|
</div>
|
|
44
|
+
<span v-if="required && !showSaved && !showChanged" class="status-indicator required-indicator">required</span>
|
|
45
|
+
<transition name="fade">
|
|
46
|
+
<span v-if="showSaved && !error" class="status-indicator saved-indicator">saved</span>
|
|
47
|
+
</transition>
|
|
48
|
+
<transition name="fade">
|
|
49
|
+
<span v-if="showChanged && !error" class="status-indicator changed-indicator">changed</span>
|
|
50
|
+
</transition>
|
|
51
|
+
<div v-if="error" class="error-message">{{ error }}</div>
|
|
44
52
|
</div>
|
|
45
53
|
|
|
46
54
|
<div v-if="isOpen" class="dropdown__content">
|
|
@@ -62,7 +70,7 @@
|
|
|
62
70
|
</template>
|
|
63
71
|
|
|
64
72
|
<script setup lang="ts">
|
|
65
|
-
import { ref, computed, watch, nextTick } from 'vue'
|
|
73
|
+
import { ref, computed, watch, nextTick, onUnmounted } from 'vue'
|
|
66
74
|
import type { DropdownProps, DropdownOption } from '../types/dropdown'
|
|
67
75
|
|
|
68
76
|
const props = withDefaults(defineProps<DropdownProps>(), {
|
|
@@ -81,10 +89,14 @@ const props = withDefaults(defineProps<DropdownProps>(), {
|
|
|
81
89
|
padding: '0.5rem',
|
|
82
90
|
icon: '',
|
|
83
91
|
iconSize: 'normal',
|
|
92
|
+
required: false,
|
|
93
|
+
error: '',
|
|
84
94
|
})
|
|
85
95
|
|
|
86
96
|
const emit = defineEmits<{
|
|
87
97
|
(e: 'update:modelValue', value: string | string[]): void
|
|
98
|
+
(e: 'changed'): void
|
|
99
|
+
(e: 'saved'): void
|
|
88
100
|
}>()
|
|
89
101
|
|
|
90
102
|
const isOpen = ref(false)
|
|
@@ -144,8 +156,10 @@ const toggleOption = (option: DropdownOption) => {
|
|
|
144
156
|
? currentValue.filter((id) => id !== option.id)
|
|
145
157
|
: [...currentValue, option.id]
|
|
146
158
|
emit('update:modelValue', newValue)
|
|
159
|
+
debounceAutosave(newValue)
|
|
147
160
|
} else {
|
|
148
161
|
emit('update:modelValue', option.id)
|
|
162
|
+
debounceAutosave(option.id)
|
|
149
163
|
closeDropdown()
|
|
150
164
|
}
|
|
151
165
|
}
|
|
@@ -179,6 +193,66 @@ watch(isOpen, (newValue) => {
|
|
|
179
193
|
document.removeEventListener('click', handleClickOutside)
|
|
180
194
|
}
|
|
181
195
|
})
|
|
196
|
+
|
|
197
|
+
const showSaved = ref(false)
|
|
198
|
+
const showChanged = ref(false)
|
|
199
|
+
const isChanged = ref(false)
|
|
200
|
+
const debounceTimer = ref<number | null>(null)
|
|
201
|
+
const changedTimer = ref<number | null>(null)
|
|
202
|
+
|
|
203
|
+
const handleAutosave = async (value: string | string[]) => {
|
|
204
|
+
if (props.autosave) {
|
|
205
|
+
try {
|
|
206
|
+
await props.autosave(value)
|
|
207
|
+
if (!props.error) {
|
|
208
|
+
emit('saved')
|
|
209
|
+
showSaved.value = true
|
|
210
|
+
showChanged.value = false
|
|
211
|
+
setTimeout(() => {
|
|
212
|
+
showSaved.value = false
|
|
213
|
+
}, 3000)
|
|
214
|
+
}
|
|
215
|
+
} catch (error) {
|
|
216
|
+
console.error('Autosave failed:', error)
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const debounceAutosave = (value: string | string[]) => {
|
|
222
|
+
// Clear existing timers
|
|
223
|
+
if (debounceTimer.value) {
|
|
224
|
+
clearTimeout(debounceTimer.value)
|
|
225
|
+
}
|
|
226
|
+
if (changedTimer.value) {
|
|
227
|
+
clearTimeout(changedTimer.value)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Show changed indicator immediately
|
|
231
|
+
if (!props.error) {
|
|
232
|
+
showChanged.value = true
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Trigger changed event after 500ms
|
|
236
|
+
changedTimer.value = window.setTimeout(() => {
|
|
237
|
+
emit('changed')
|
|
238
|
+
isChanged.value = true
|
|
239
|
+
}, 500)
|
|
240
|
+
|
|
241
|
+
// Trigger autosave after 1500ms
|
|
242
|
+
debounceTimer.value = window.setTimeout(() => {
|
|
243
|
+
handleAutosave(value)
|
|
244
|
+
}, 1500)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Cleanup timers on unmount
|
|
248
|
+
onUnmounted(() => {
|
|
249
|
+
if (debounceTimer.value) {
|
|
250
|
+
clearTimeout(debounceTimer.value)
|
|
251
|
+
}
|
|
252
|
+
if (changedTimer.value) {
|
|
253
|
+
clearTimeout(changedTimer.value)
|
|
254
|
+
}
|
|
255
|
+
})
|
|
182
256
|
</script>
|
|
183
257
|
|
|
184
258
|
<style scoped>
|
|
@@ -197,6 +271,7 @@ watch(isOpen, (newValue) => {
|
|
|
197
271
|
}
|
|
198
272
|
|
|
199
273
|
.dropdown__selected {
|
|
274
|
+
position: relative;
|
|
200
275
|
display: grid;
|
|
201
276
|
grid-template-columns: auto 1fr auto;
|
|
202
277
|
align-items: center;
|
|
@@ -397,4 +472,55 @@ watch(isOpen, (newValue) => {
|
|
|
397
472
|
justify-content: center;
|
|
398
473
|
padding: 0.75rem 0;
|
|
399
474
|
}
|
|
475
|
+
|
|
476
|
+
.dropdown__selected.has-error {
|
|
477
|
+
border-color: var(--danger-color);
|
|
478
|
+
border-bottom-left-radius: 0;
|
|
479
|
+
border-bottom-right-radius: 0;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
.status-indicator {
|
|
483
|
+
position: absolute;
|
|
484
|
+
top: -1px;
|
|
485
|
+
line-height: 1px;
|
|
486
|
+
right: 0.5rem;
|
|
487
|
+
font-size: 0.75rem;
|
|
488
|
+
color: var(--text-muted);
|
|
489
|
+
background-color: var(--dropdown-background-color);
|
|
490
|
+
padding: 0 0.25rem;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
.saved-indicator {
|
|
494
|
+
color: var(--success-color);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
.changed-indicator {
|
|
498
|
+
color: var(--warning-color);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
.error-message {
|
|
502
|
+
position: absolute;
|
|
503
|
+
bottom: 0;
|
|
504
|
+
left: 0;
|
|
505
|
+
right: 0;
|
|
506
|
+
padding: 0.25rem 0.75rem;
|
|
507
|
+
background-color: var(--danger-color);
|
|
508
|
+
color: white;
|
|
509
|
+
font-size: 0.75rem;
|
|
510
|
+
border-radius: 0 0 0.5rem 0.5rem;
|
|
511
|
+
transform: translateY(100%);
|
|
512
|
+
transition: transform 0.2s ease;
|
|
513
|
+
line-height: var(--line-height);
|
|
514
|
+
z-index: 1;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
.fade-enter-active,
|
|
518
|
+
.fade-leave-active {
|
|
519
|
+
transition: opacity 0.2s ease;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
.fade-enter-from,
|
|
523
|
+
.fade-leave-to {
|
|
524
|
+
opacity: 0;
|
|
525
|
+
}
|
|
400
526
|
</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) }}
|