edvoyui-component-library-test-flight 0.0.245 → 0.0.246
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/components/checkbox/EUICheckbox.vue +22 -13
- package/dist/components/searchTagSelect/EUISearchTagSelect.d.ts +6 -1
- package/dist/components/searchTagSelect/EUISearchTagSelect.vue +66 -47
- package/dist/edvoy-ui.cjs.js +16 -16
- package/dist/edvoy-ui.css +1 -1
- package/dist/edvoy-ui.es.js +6249 -6229
- package/dist/edvoy-ui.umd.js +29 -29
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div>
|
|
3
|
-
<label class="flex flex-row items-center scale-100 translate-x-0 translate-y-0 eui-checkbox">
|
|
3
|
+
<label class="flex flex-row items-center scale-100 translate-x-0 translate-y-0 eui-checkbox" :class="{'select-none pointer-events-none opacity-50':disabled}">
|
|
4
4
|
<input
|
|
5
5
|
v-model="isChecked"
|
|
6
6
|
type="checkbox"
|
|
@@ -79,46 +79,55 @@ watch(props, (newValue) => {
|
|
|
79
79
|
<style lang="scss">
|
|
80
80
|
.eui-checkbox .cbx-icon {
|
|
81
81
|
transition: all 0.2s ease;
|
|
82
|
-
@apply select-none cursor-pointer p-px rounded-md overflow-hidden inline-block scale-
|
|
82
|
+
@apply select-none cursor-pointer p-px rounded-md overflow-hidden inline-block scale-100;
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
.eui-checkbox .cbx-icon span {
|
|
86
86
|
transform: translate3d(0, 0, 0);
|
|
87
87
|
transition: all 0.2s ease;
|
|
88
|
-
@apply float-left align-middle relative size-5
|
|
88
|
+
@apply float-left align-middle relative size-5 -outline-offset-1 outline outline-1 outline-gray-400 rounded scale-100;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
.eui-checkbox .cbx-icon span > svg {
|
|
92
|
-
top:
|
|
93
|
-
left:
|
|
92
|
+
top: 5px;
|
|
93
|
+
left: 4px;
|
|
94
94
|
stroke-width: 1.75;
|
|
95
95
|
stroke-linecap: round;
|
|
96
96
|
stroke-linejoin: round;
|
|
97
97
|
stroke-dasharray: 18px;
|
|
98
98
|
stroke-dashoffset: 18px;
|
|
99
|
-
transition:
|
|
100
|
-
transition-delay: 0.1s;
|
|
99
|
+
transition: stroke-dashoffset 0.16s ease-out;
|
|
101
100
|
transform: translate3d(0, 0, 0);
|
|
102
101
|
@apply absolute fill-none stroke-white;
|
|
103
102
|
}
|
|
104
103
|
|
|
105
|
-
.eui-checkbox .input-checkbox:checked + .cbx-icon > span {
|
|
106
|
-
animation: anim 0.
|
|
107
|
-
@apply bg-
|
|
104
|
+
.eui-checkbox .input-checkbox:checked:not(:indeterminate) + .cbx-icon > span {
|
|
105
|
+
animation: anim 0.18s ease-out;
|
|
106
|
+
@apply bg-violet-700 outline-violet-700;
|
|
108
107
|
}
|
|
109
108
|
|
|
110
109
|
.eui-checkbox .input-checkbox:indeterminate + .cbx-icon > span {
|
|
111
|
-
@apply bg-
|
|
110
|
+
@apply bg-violet-700 outline-violet-700;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.eui-checkbox .input-checkbox:indeterminate + .cbx-icon span > svg {
|
|
114
|
+
stroke-dashoffset: 18px;
|
|
115
|
+
transition: none;
|
|
112
116
|
}
|
|
113
117
|
|
|
114
118
|
.eui-checkbox .input-checkbox:indeterminate + .cbx-icon > span::after {
|
|
115
119
|
content: '';
|
|
120
|
+
animation: indeterminate-in 0.14s ease-out both;
|
|
116
121
|
@apply absolute left-1/2 top-1/2 h-0.5 w-2.5 -translate-x-1/2 -translate-y-1/2 rounded bg-white;
|
|
117
122
|
}
|
|
118
123
|
|
|
119
|
-
.eui-checkbox .input-checkbox:checked + .cbx-icon span > svg {
|
|
124
|
+
.eui-checkbox .input-checkbox:checked:not(:indeterminate) + .cbx-icon span > svg {
|
|
120
125
|
stroke-dashoffset: 0;
|
|
121
|
-
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
@keyframes indeterminate-in {
|
|
129
|
+
from { opacity: 0; scale: 0.5 1; }
|
|
130
|
+
to { opacity: 1; scale: 1 1; }
|
|
122
131
|
}
|
|
123
132
|
|
|
124
133
|
@keyframes anim {
|
|
@@ -15,6 +15,8 @@ interface Props {
|
|
|
15
15
|
searchable?: boolean;
|
|
16
16
|
backendPagination?: boolean;
|
|
17
17
|
hideNoData?: boolean;
|
|
18
|
+
/** Show a loading state while options are being fetched. */
|
|
19
|
+
loading?: boolean;
|
|
18
20
|
disabled?: boolean;
|
|
19
21
|
tag?: string;
|
|
20
22
|
tagColor?: "None" | "Success" | "Error";
|
|
@@ -26,12 +28,13 @@ interface Props {
|
|
|
26
28
|
ignoreDeselect?: boolean;
|
|
27
29
|
popperClasses?: string;
|
|
28
30
|
showCount?: boolean;
|
|
29
|
-
/**
|
|
31
|
+
/** Show an All row when search has results; it selects every item in `items`. Enabled by default in multiple mode. */
|
|
30
32
|
showSelectAll?: boolean;
|
|
31
33
|
selectAllLabel?: string;
|
|
32
34
|
/** Maximum selected tags shown before the More/Less control; 0 shows all. */
|
|
33
35
|
maxVisibleTags?: number;
|
|
34
36
|
tagSize?: "sm" | "md" | "lg";
|
|
37
|
+
dropdownWidth?: string;
|
|
35
38
|
}
|
|
36
39
|
export type SelectItemKey = string | (string | number)[] | ((item: object, fallback?: any) => any);
|
|
37
40
|
declare function __VLS_template(): {
|
|
@@ -182,6 +185,7 @@ declare const __VLS_component: import('../../../node_modules/vue').DefineCompone
|
|
|
182
185
|
onCreate?: ((value: string) => any) | undefined;
|
|
183
186
|
"onOn-deselect"?: ((value: any) => any) | undefined;
|
|
184
187
|
}>, {
|
|
188
|
+
loading: boolean;
|
|
185
189
|
disabled: boolean;
|
|
186
190
|
name: string;
|
|
187
191
|
items: any[];
|
|
@@ -209,6 +213,7 @@ declare const __VLS_component: import('../../../node_modules/vue').DefineCompone
|
|
|
209
213
|
selectAllLabel: string;
|
|
210
214
|
maxVisibleTags: number;
|
|
211
215
|
tagSize: "sm" | "md" | "lg";
|
|
216
|
+
dropdownWidth: string;
|
|
212
217
|
}, {}, {}, {}, string, import('../../../node_modules/vue').ComponentProvideOptions, false, {
|
|
213
218
|
componentRef: HTMLDivElement;
|
|
214
219
|
searchInputRef: import('../../../node_modules/vue').CreateComponentPublicInstanceWithMixins<Readonly<import('../../../node_modules/vue').ExtractPropTypes<{
|
|
@@ -86,10 +86,10 @@
|
|
|
86
86
|
<template #content>
|
|
87
87
|
<div
|
|
88
88
|
v-if="showDropDown"
|
|
89
|
-
class="w-full p-4 overflow-y-auto text-sm bg-white border-2 border-gray-100 border-solid shadow-lg rounded-3xl max-h-72 scrollbar--hide"
|
|
89
|
+
:class="['w-full p-4 overflow-y-auto text-sm bg-white border-2 border-gray-100 border-solid shadow-lg rounded-3xl max-h-72 scrollbar--hide min-w-28', dropdownWidth]"
|
|
90
90
|
>
|
|
91
91
|
<h3
|
|
92
|
-
class="sticky z-10 p-2 mb-2 -mx-px -mt-3 font-
|
|
92
|
+
class="sticky z-10 p-2 pb-0 mb-2 -mx-px -mt-3 text-xs font-bold tracking-wide text-gray-500 uppercase bg-white -top-4"
|
|
93
93
|
>
|
|
94
94
|
Search Results
|
|
95
95
|
</h3>
|
|
@@ -102,10 +102,10 @@
|
|
|
102
102
|
class="space-y-0.5"
|
|
103
103
|
>
|
|
104
104
|
<li
|
|
105
|
-
v-if="selectType === 'multiple' && showSelectAll &&
|
|
105
|
+
v-if="selectType === 'multiple' && showSelectAll && !loading && filteredItems.length"
|
|
106
106
|
role="option"
|
|
107
107
|
:aria-selected="allItemsSelected"
|
|
108
|
-
class="
|
|
108
|
+
class="pb-0.5 border-b border-gray-100"
|
|
109
109
|
@click.stop
|
|
110
110
|
>
|
|
111
111
|
<EUICheckbox
|
|
@@ -113,9 +113,9 @@
|
|
|
113
113
|
:indeterminate="someItemsSelected && !allItemsSelected"
|
|
114
114
|
:disabled="disabled"
|
|
115
115
|
:class="[
|
|
116
|
-
'relative flex items-center text-sm transition-colors duration-75 w-full multiple-select rounded-md font-medium [&_label]:w-full [&_label]:
|
|
116
|
+
'relative flex items-center text-sm transition-colors duration-75 w-full multiple-select rounded-md font-medium [&_label]:w-full [&_label]:h-9 [&_label]:px-2.5',
|
|
117
117
|
allItemsSelected || someItemsSelected
|
|
118
|
-
? 'text-gray-800 hover:bg-
|
|
118
|
+
? 'text-gray-800 hover:bg-violet-50 hover:text-violet-600'
|
|
119
119
|
: 'text-gray-600 hover:bg-gray-50 hover:text-gray-800',
|
|
120
120
|
]"
|
|
121
121
|
@toggle="handleSelectAll"
|
|
@@ -125,14 +125,24 @@
|
|
|
125
125
|
</template>
|
|
126
126
|
</EUICheckbox>
|
|
127
127
|
</li>
|
|
128
|
-
<li v-if="
|
|
128
|
+
<li v-if="loading" role="status" aria-live="polite" class="px-2 py-4 text-sm text-gray-500">
|
|
129
|
+
<span class="inline-flex items-center gap-2">
|
|
130
|
+
<span class="inline-block border-2 border-gray-200 rounded-full size-4 border-t-violet-600 animate-spin" aria-hidden="true"></span>
|
|
131
|
+
Loading options...
|
|
132
|
+
</span>
|
|
133
|
+
</li>
|
|
134
|
+
<li v-else-if="filteredItems.length === 0 && !hideNoData">
|
|
129
135
|
<slot name="no-items">
|
|
130
136
|
<div
|
|
131
|
-
|
|
132
|
-
class="px-2 py-4 mb-6 text-sm font-medium text-center text-gray-500 min-h-32"
|
|
137
|
+
class="px-2 py-4 text-sm font-medium text-center text-gray-500"
|
|
133
138
|
>
|
|
134
|
-
|
|
135
|
-
|
|
139
|
+
<template v-if="internalSearch.trim()">
|
|
140
|
+
No results found for "<strong>{{ internalSearch }}</strong>"
|
|
141
|
+
</template>
|
|
142
|
+
<template v-else-if="backendPagination">
|
|
143
|
+
Type in the search box to see results
|
|
144
|
+
</template>
|
|
145
|
+
<template v-else>No options available</template>
|
|
136
146
|
</div>
|
|
137
147
|
</slot>
|
|
138
148
|
</li>
|
|
@@ -160,7 +170,7 @@
|
|
|
160
170
|
<div
|
|
161
171
|
:class="[
|
|
162
172
|
selected(item)
|
|
163
|
-
? 'bg-
|
|
173
|
+
? 'bg-violet-50 text-violet-600'
|
|
164
174
|
: 'text-gray-600 hover:bg-gray-50 hover:text-gray-800',
|
|
165
175
|
'relative py-2 px-4 flex items-center capitalize text-sm transition-colors duration-75 rounded',
|
|
166
176
|
]"
|
|
@@ -194,9 +204,9 @@
|
|
|
194
204
|
name="checkbox"
|
|
195
205
|
:class="[
|
|
196
206
|
selected(item)
|
|
197
|
-
? 'text-gray-800 hover:bg-
|
|
207
|
+
? 'text-gray-800 hover:bg-violet-50 hover:text-violet-600'
|
|
198
208
|
: 'text-gray-600 hover:bg-gray-50 hover:text-gray-800',
|
|
199
|
-
'relative flex items-center capitalize text-sm transition-colors duration-75 w-full multiple-select rounded-md font-medium [&_label]:w-full [&_label]:
|
|
209
|
+
'relative flex items-center capitalize text-sm transition-colors duration-75 w-full multiple-select rounded-md font-medium [&_label]:w-full [&_label]:h-9 [&_label]:px-2.5',
|
|
200
210
|
]"
|
|
201
211
|
@toggle="
|
|
202
212
|
(checked) => handleCheckboxChange(item, checked)
|
|
@@ -225,10 +235,10 @@
|
|
|
225
235
|
class="my-2 space-y-2"
|
|
226
236
|
>
|
|
227
237
|
<div
|
|
228
|
-
class="px-0.5 gap-
|
|
238
|
+
class="px-0.5 gap-2 items-center flex-wrap"
|
|
229
239
|
:class="[
|
|
230
240
|
effectiveTagLimit ? 'flex w-full' : 'inline-flex',
|
|
231
|
-
tagsExpanded && effectiveTagLimit ? 'max-h-32 overflow-y-auto' : '',
|
|
241
|
+
tagsExpanded && effectiveTagLimit ? 'max-h-32 overflow-y-auto scrollbar--thin' : '',
|
|
232
242
|
]"
|
|
233
243
|
>
|
|
234
244
|
<slot
|
|
@@ -261,7 +271,7 @@
|
|
|
261
271
|
<button
|
|
262
272
|
v-if="canExpandTags && tagsExpanded"
|
|
263
273
|
type="button"
|
|
264
|
-
class="text-xs font-medium text-
|
|
274
|
+
class="text-xs font-medium text-gray-600 hover:text-gray-700"
|
|
265
275
|
@click="tagsExpanded = false"
|
|
266
276
|
>
|
|
267
277
|
Show less
|
|
@@ -327,6 +337,8 @@ interface Props {
|
|
|
327
337
|
searchable?: boolean;
|
|
328
338
|
backendPagination?: boolean;
|
|
329
339
|
hideNoData?: boolean;
|
|
340
|
+
/** Show a loading state while options are being fetched. */
|
|
341
|
+
loading?: boolean;
|
|
330
342
|
disabled?: boolean;
|
|
331
343
|
tag?: string;
|
|
332
344
|
tagColor?: "None" | "Success" | "Error";
|
|
@@ -338,12 +350,13 @@ interface Props {
|
|
|
338
350
|
ignoreDeselect?: boolean;
|
|
339
351
|
popperClasses?: string;
|
|
340
352
|
showCount?: boolean;
|
|
341
|
-
/**
|
|
353
|
+
/** Show an All row when search has results; it selects every item in `items`. Enabled by default in multiple mode. */
|
|
342
354
|
showSelectAll?: boolean;
|
|
343
355
|
selectAllLabel?: string;
|
|
344
356
|
/** Maximum selected tags shown before the More/Less control; 0 shows all. */
|
|
345
357
|
maxVisibleTags?: number;
|
|
346
358
|
tagSize?: "sm" | "md" | "lg";
|
|
359
|
+
dropdownWidth?: string,
|
|
347
360
|
}
|
|
348
361
|
const props = withDefaults(defineProps<Props>(), {
|
|
349
362
|
items: () => [],
|
|
@@ -357,6 +370,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
357
370
|
searchable: true,
|
|
358
371
|
backendPagination: false,
|
|
359
372
|
hideNoData: false,
|
|
373
|
+
loading: false,
|
|
360
374
|
disabled: false,
|
|
361
375
|
tag: "",
|
|
362
376
|
tagColor: "None",
|
|
@@ -369,10 +383,11 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
369
383
|
ignoreDeselect: false,
|
|
370
384
|
popperClasses: "",
|
|
371
385
|
showCount: false,
|
|
372
|
-
showSelectAll:
|
|
386
|
+
showSelectAll: true,
|
|
373
387
|
selectAllLabel: "All",
|
|
374
388
|
maxVisibleTags: 0,
|
|
375
389
|
tagSize: "md",
|
|
390
|
+
dropdownWidth: ''
|
|
376
391
|
});
|
|
377
392
|
|
|
378
393
|
|
|
@@ -395,8 +410,8 @@ const showDropDown = ref(false);
|
|
|
395
410
|
const options = ref<HTMLElement>();
|
|
396
411
|
const searchInputRef = ref<any>();
|
|
397
412
|
const componentRef = ref<HTMLElement>();
|
|
398
|
-
const isOpening = ref(false);
|
|
399
413
|
const tagsExpanded = ref(false);
|
|
414
|
+
let focusTimer: ReturnType<typeof setTimeout> | undefined;
|
|
400
415
|
|
|
401
416
|
// Computed properties
|
|
402
417
|
const selectedValues = computed({
|
|
@@ -644,26 +659,28 @@ const handleSelect = (item: any) => {
|
|
|
644
659
|
};
|
|
645
660
|
|
|
646
661
|
const handleOpen = () => {
|
|
647
|
-
if (props.disabled) return;
|
|
648
|
-
isOpening.value = true;
|
|
662
|
+
if (props.disabled || showDropDown.value) return;
|
|
649
663
|
|
|
650
664
|
// Only clear search for single select, preserve it for multiple select
|
|
651
665
|
if (props.selectType === "single") {
|
|
652
666
|
internalSearch.value = "";
|
|
653
667
|
}
|
|
654
668
|
showDropDown.value = true;
|
|
669
|
+
window.addEventListener("scroll", handleScroll, true);
|
|
655
670
|
emit("open");
|
|
656
671
|
|
|
657
672
|
nextTick(() => {
|
|
658
|
-
|
|
673
|
+
if (!showDropDown.value) return;
|
|
674
|
+
focusTimer = setTimeout(() => {
|
|
675
|
+
focusTimer = undefined;
|
|
676
|
+
if (!showDropDown.value) return;
|
|
659
677
|
internalSearch.value = "";
|
|
660
|
-
isOpening.value = false;
|
|
661
678
|
if (searchInputRef.value?.searchInput) {
|
|
662
|
-
searchInputRef.value.searchInput.focus();
|
|
679
|
+
searchInputRef.value.searchInput.focus({ preventScroll: true });
|
|
663
680
|
} else if (searchInputRef.value?.$el) {
|
|
664
681
|
const inputElement = searchInputRef.value.$el.querySelector("input");
|
|
665
682
|
if (inputElement) {
|
|
666
|
-
inputElement.focus();
|
|
683
|
+
inputElement.focus({ preventScroll: true });
|
|
667
684
|
}
|
|
668
685
|
}
|
|
669
686
|
}, 50);
|
|
@@ -671,6 +688,11 @@ const handleOpen = () => {
|
|
|
671
688
|
};
|
|
672
689
|
|
|
673
690
|
const handleClose = () => {
|
|
691
|
+
window.removeEventListener("scroll", handleScroll, true);
|
|
692
|
+
if (focusTimer) {
|
|
693
|
+
clearTimeout(focusTimer);
|
|
694
|
+
focusTimer = undefined;
|
|
695
|
+
}
|
|
674
696
|
if (showDropDown.value) {
|
|
675
697
|
emit("blur");
|
|
676
698
|
emit("close");
|
|
@@ -689,30 +711,25 @@ const handleSearchFocus = () => {
|
|
|
689
711
|
|
|
690
712
|
// Document click handler for outside clicks
|
|
691
713
|
const handleDocumentClick = (event: MouseEvent) => {
|
|
692
|
-
//
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
const isOutsideComponent = !componentRef.value.contains(target);
|
|
701
|
-
|
|
702
|
-
// Check if click is outside any popper content
|
|
703
|
-
const popperElements = document.querySelectorAll(
|
|
704
|
-
'[data-popper-placement], .popper-defaultstyle, [role="tooltip"]'
|
|
705
|
-
);
|
|
706
|
-
const isOutsidePopper = Array.from(popperElements).every(
|
|
707
|
-
(popper) => !popper.contains(target)
|
|
708
|
-
);
|
|
709
|
-
|
|
710
|
-
if (isOutsideComponent && isOutsidePopper) {
|
|
711
|
-
handleClose();
|
|
712
|
-
}
|
|
714
|
+
// The trigger can be replaced during this click. composedPath retains its
|
|
715
|
+
// original ancestors, so an inside click stays inside after that DOM update.
|
|
716
|
+
if (
|
|
717
|
+
showDropDown.value &&
|
|
718
|
+
componentRef.value &&
|
|
719
|
+
!event.composedPath().includes(componentRef.value)
|
|
720
|
+
) {
|
|
721
|
+
handleClose();
|
|
713
722
|
}
|
|
714
723
|
};
|
|
715
724
|
|
|
725
|
+
const handleScroll = (event: Event) => {
|
|
726
|
+
if (!showDropDown.value) return;
|
|
727
|
+
const target = event.target;
|
|
728
|
+
// Keep the options usable; scrolling any ancestor or the page closes them.
|
|
729
|
+
if (target instanceof Node && componentRef.value?.contains(target)) return;
|
|
730
|
+
handleClose();
|
|
731
|
+
};
|
|
732
|
+
|
|
716
733
|
// Lifecycle hooks
|
|
717
734
|
onMounted(() => {
|
|
718
735
|
document.addEventListener("click", handleDocumentClick);
|
|
@@ -720,6 +737,8 @@ onMounted(() => {
|
|
|
720
737
|
|
|
721
738
|
onUnmounted(() => {
|
|
722
739
|
document.removeEventListener("click", handleDocumentClick);
|
|
740
|
+
window.removeEventListener("scroll", handleScroll, true);
|
|
741
|
+
if (focusTimer) clearTimeout(focusTimer);
|
|
723
742
|
});
|
|
724
743
|
|
|
725
744
|
// Watchers
|