@teammodlr/ui-kit 3.0.0-beta.24 → 3.0.0-beta.28
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/lib/components/select/Select.vue.d.ts +12 -4
- package/lib/components/select/style/Select.css.d.ts +1 -0
- package/lib/index.cjs.js +3 -3
- package/lib/index.es.js +5519 -4915
- package/lib/style.css +1 -1
- package/package.json +4 -3
- package/src/components/select/Select.vue +52 -33
- package/src/components/select/style/Select.css.ts +36 -14
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
<div :class="selectComputedClasses">
|
|
17
17
|
<template v-if="multiple && Array.isArray(selected) && selected.length > 0">
|
|
18
18
|
<MTag v-for="value in selectedMax" :key="value" :closeable="true"
|
|
19
|
-
@close="
|
|
19
|
+
@close="() => removeSelected(value)" :size="size">
|
|
20
20
|
{{ getSelectedLabel(value) }}
|
|
21
21
|
</MTag>
|
|
22
22
|
<template v-if="selectedCountNotShown > 0">
|
|
@@ -71,24 +71,30 @@
|
|
|
71
71
|
<input v-if="searchable" type="text" ref="searchRef" v-model="search" placeholder="Search"
|
|
72
72
|
:class="style.searchInput" :data-input="`select-menu-search-${id}`" />
|
|
73
73
|
</div>
|
|
74
|
-
<div :class="style.
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
74
|
+
<div v-if="computedOptions.length === 0" :class="style.noResults">No results</div>
|
|
75
|
+
<VList
|
|
76
|
+
v-else
|
|
77
|
+
ref="virtualListRef"
|
|
78
|
+
:data="computedOptions"
|
|
79
|
+
:item-size="optionItemHeight"
|
|
80
|
+
:style="{ height: optionsHeight + 'px', padding: '4px' }"
|
|
81
|
+
>
|
|
82
|
+
<template #default="{ item, index }">
|
|
83
|
+
<div :class="[optionSizeClass, {
|
|
84
|
+
[style.option]: true,
|
|
85
|
+
[style.optionSelected]: item.active,
|
|
86
|
+
[style.optionFocused]: selectedIndex === index
|
|
87
|
+
}]" @click.prevent="item.onClick" @mouseenter="selectedIndex = index"
|
|
88
|
+
:data-element="`select-menu-option-${id}-${item.value}`">
|
|
89
|
+
<span :class="style.optionIcon">
|
|
90
|
+
<svg v-if="item.active" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
|
91
|
+
<path fill-rule="evenodd" d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z" clip-rule="evenodd"></path>
|
|
92
|
+
</svg>
|
|
93
|
+
</span>
|
|
94
|
+
<span :class="style.optionLabel">{{ item.label }}</span>
|
|
95
|
+
</div>
|
|
90
96
|
</template>
|
|
91
|
-
</
|
|
97
|
+
</VList>
|
|
92
98
|
</PopoverContent>
|
|
93
99
|
</Transition>
|
|
94
100
|
</PopoverPortal>
|
|
@@ -115,7 +121,12 @@ import {
|
|
|
115
121
|
type PropType
|
|
116
122
|
} from 'vue';
|
|
117
123
|
import MTag from '../tag/Tag.vue';
|
|
124
|
+
import { VList } from 'virtua/vue';
|
|
118
125
|
import * as style from './style';
|
|
126
|
+
|
|
127
|
+
interface VListHandle {
|
|
128
|
+
scrollToIndex(index: number, opts?: { align?: 'start' | 'center' | 'end' | 'nearest' }): void;
|
|
129
|
+
}
|
|
119
130
|
import { fade } from '../../styles/transitions.css';
|
|
120
131
|
import { useInteractive } from '../../composables/useInteractive';
|
|
121
132
|
import type { OnUpdateValue, OnUpdateValueImpl } from './types';
|
|
@@ -127,7 +138,9 @@ let shouldOpenNextFocusedSelect = false;
|
|
|
127
138
|
export default defineComponent({
|
|
128
139
|
name: 'MSelect',
|
|
129
140
|
emits: {
|
|
130
|
-
...useInput.emits()
|
|
141
|
+
...useInput.emits(),
|
|
142
|
+
'on-open': () => true,
|
|
143
|
+
'on-close': () => true
|
|
131
144
|
},
|
|
132
145
|
props: {
|
|
133
146
|
...useInput.props(),
|
|
@@ -222,6 +235,7 @@ export default defineComponent({
|
|
|
222
235
|
},
|
|
223
236
|
components: {
|
|
224
237
|
MTag,
|
|
238
|
+
VList,
|
|
225
239
|
PopoverContent,
|
|
226
240
|
PopoverPortal,
|
|
227
241
|
PopoverRoot,
|
|
@@ -229,7 +243,7 @@ export default defineComponent({
|
|
|
229
243
|
},
|
|
230
244
|
setup(props, { emit, expose }) {
|
|
231
245
|
const container = ref<HTMLElement | null>(null);
|
|
232
|
-
const
|
|
246
|
+
const virtualListRef = ref<VListHandle | null>(null);
|
|
233
247
|
const searchRef = ref<HTMLInputElement>();
|
|
234
248
|
const selectRef = ref<HTMLElement>();
|
|
235
249
|
const popper = ref<HTMLElement>();
|
|
@@ -601,9 +615,7 @@ export default defineComponent({
|
|
|
601
615
|
}
|
|
602
616
|
}
|
|
603
617
|
|
|
604
|
-
function removeSelected(
|
|
605
|
-
event.preventDefault();
|
|
606
|
-
event.stopPropagation();
|
|
618
|
+
function removeSelected(value: string | number) {
|
|
607
619
|
if (!props.multiple) {
|
|
608
620
|
return;
|
|
609
621
|
}
|
|
@@ -749,6 +761,13 @@ export default defineComponent({
|
|
|
749
761
|
]
|
|
750
762
|
})
|
|
751
763
|
|
|
764
|
+
const optionItemHeightMap: Record<string, number> = { xs: 30, sm: 32, md: 38, lg: 42, xl: 46 };
|
|
765
|
+
const optionItemHeight = computed(() => optionItemHeightMap[props.size] ?? 38);
|
|
766
|
+
const optionsHeight = computed(() => {
|
|
767
|
+
const total = computedOptions.value.length * optionItemHeight.value + 8; // 8px = paddingTop+paddingBottom
|
|
768
|
+
return Math.min(total, 222);
|
|
769
|
+
});
|
|
770
|
+
const optionSizeClass = computed(() => style.optionSizes[props.size]);
|
|
752
771
|
const fillRootClass = computed(() => props.fill ? style.fillRoot : undefined);
|
|
753
772
|
const fillLabelClass = computed(() => props.fill ? style.fillLabel : undefined);
|
|
754
773
|
|
|
@@ -800,21 +819,20 @@ export default defineComponent({
|
|
|
800
819
|
if (!keyboardNavigation.value) {
|
|
801
820
|
return;
|
|
802
821
|
}
|
|
803
|
-
if (typeof index !== 'undefined'
|
|
804
|
-
|
|
805
|
-
block: 'nearest',
|
|
806
|
-
inline: 'nearest'
|
|
807
|
-
});
|
|
822
|
+
if (typeof index !== 'undefined') {
|
|
823
|
+
virtualListRef.value?.scrollToIndex(index, { align: 'nearest' });
|
|
808
824
|
}
|
|
809
825
|
});
|
|
810
826
|
|
|
811
827
|
watch(open, (value: boolean) => {
|
|
812
828
|
nextTick(() => {
|
|
813
829
|
if (!value) {
|
|
830
|
+
emit('on-close');
|
|
814
831
|
search.value = '';
|
|
815
832
|
}
|
|
816
833
|
|
|
817
834
|
if (value) {
|
|
835
|
+
emit('on-open');
|
|
818
836
|
zIndex.value = nextZIndex();
|
|
819
837
|
}
|
|
820
838
|
|
|
@@ -832,7 +850,7 @@ export default defineComponent({
|
|
|
832
850
|
});
|
|
833
851
|
}
|
|
834
852
|
|
|
835
|
-
if (value
|
|
853
|
+
if (value) {
|
|
836
854
|
const selectedValue = Array.isArray(selected.value)
|
|
837
855
|
? selected.value[0]
|
|
838
856
|
: selected.value;
|
|
@@ -840,9 +858,7 @@ export default defineComponent({
|
|
|
840
858
|
i => i.value === selectedValue
|
|
841
859
|
);
|
|
842
860
|
if (index !== -1) {
|
|
843
|
-
|
|
844
|
-
block: 'nearest'
|
|
845
|
-
});
|
|
861
|
+
nextTick(() => virtualListRef.value?.scrollToIndex(index, { align: 'nearest' }));
|
|
846
862
|
selectedIndex.value = index;
|
|
847
863
|
} else if (computedOptions.value.length > 0) {
|
|
848
864
|
selectedIndex.value = 0;
|
|
@@ -910,7 +926,7 @@ export default defineComponent({
|
|
|
910
926
|
|
|
911
927
|
return {
|
|
912
928
|
container,
|
|
913
|
-
|
|
929
|
+
virtualListRef,
|
|
914
930
|
searchRef,
|
|
915
931
|
selectRef,
|
|
916
932
|
popper,
|
|
@@ -945,6 +961,9 @@ export default defineComponent({
|
|
|
945
961
|
focused,
|
|
946
962
|
fade,
|
|
947
963
|
toggleAllItems,
|
|
964
|
+
optionItemHeight,
|
|
965
|
+
optionsHeight,
|
|
966
|
+
optionSizeClass,
|
|
948
967
|
fillRootClass,
|
|
949
968
|
fillLabelClass
|
|
950
969
|
};
|
|
@@ -189,8 +189,7 @@ export const menu = style({
|
|
|
189
189
|
borderRadius: '4px',
|
|
190
190
|
border: `1px solid ${themeVars.color.gray[200]}`,
|
|
191
191
|
boxShadow: themeVars.shadow.md,
|
|
192
|
-
|
|
193
|
-
overflowY: 'auto'
|
|
192
|
+
overflow: 'hidden'
|
|
194
193
|
});
|
|
195
194
|
|
|
196
195
|
export const placeholder = style({
|
|
@@ -199,11 +198,7 @@ export const placeholder = style({
|
|
|
199
198
|
});
|
|
200
199
|
|
|
201
200
|
export const search = style({
|
|
202
|
-
// padding: `${themeVars.space[1]} ${themeVars.space[1]} 0`,
|
|
203
201
|
backgroundColor: '#FFFFFF',
|
|
204
|
-
position: 'sticky',
|
|
205
|
-
top: 0,
|
|
206
|
-
zIndex: 1,
|
|
207
202
|
borderBottom: `1px solid ${themeVars.color.gray[200]}`,
|
|
208
203
|
display: 'flex'
|
|
209
204
|
});
|
|
@@ -219,10 +214,7 @@ export const searchInput = style({
|
|
|
219
214
|
});
|
|
220
215
|
|
|
221
216
|
export const options = style({
|
|
222
|
-
|
|
223
|
-
display: 'flex',
|
|
224
|
-
flexDirection: 'column',
|
|
225
|
-
gap: themeVars.space[0.5]
|
|
217
|
+
overflow: 'hidden'
|
|
226
218
|
});
|
|
227
219
|
|
|
228
220
|
export const option = style({
|
|
@@ -234,10 +226,7 @@ export const option = style({
|
|
|
234
226
|
display: 'flex',
|
|
235
227
|
alignItems: 'center',
|
|
236
228
|
cursor: 'pointer',
|
|
237
|
-
|
|
238
|
-
fontSize: themeVars.fontSize.base,
|
|
239
|
-
paddingBlock: themeVars.space['1.5'],
|
|
240
|
-
paddingInline: themeVars.space['3'],
|
|
229
|
+
marginBottom: '2px',
|
|
241
230
|
gap: themeVars.space[2],
|
|
242
231
|
color: vars.optionColor,
|
|
243
232
|
transition: 'background-color 0.05s ease-in-out',
|
|
@@ -280,6 +269,39 @@ export const optionSelected = style({
|
|
|
280
269
|
|
|
281
270
|
export const optionLabel = style({});
|
|
282
271
|
|
|
272
|
+
export const optionSizes = styleVariants({
|
|
273
|
+
xs: {
|
|
274
|
+
minHeight: '28px',
|
|
275
|
+
fontSize: themeVars.fontSize.small,
|
|
276
|
+
paddingBlock: themeVars.space['1'],
|
|
277
|
+
paddingInline: themeVars.space['2']
|
|
278
|
+
},
|
|
279
|
+
sm: {
|
|
280
|
+
minHeight: '30px',
|
|
281
|
+
fontSize: themeVars.fontSize.base,
|
|
282
|
+
paddingBlock: themeVars.space['1'],
|
|
283
|
+
paddingInline: themeVars.space['2.5']
|
|
284
|
+
},
|
|
285
|
+
md: {
|
|
286
|
+
minHeight: '36px',
|
|
287
|
+
fontSize: themeVars.fontSize.base,
|
|
288
|
+
paddingBlock: themeVars.space['1.5'],
|
|
289
|
+
paddingInline: themeVars.space['3']
|
|
290
|
+
},
|
|
291
|
+
lg: {
|
|
292
|
+
minHeight: '40px',
|
|
293
|
+
fontSize: themeVars.fontSize.large,
|
|
294
|
+
paddingBlock: themeVars.space['2'],
|
|
295
|
+
paddingInline: themeVars.space['3']
|
|
296
|
+
},
|
|
297
|
+
xl: {
|
|
298
|
+
minHeight: '44px',
|
|
299
|
+
fontSize: themeVars.fontSize.large,
|
|
300
|
+
paddingBlock: themeVars.space['2.5'],
|
|
301
|
+
paddingInline: themeVars.space['3']
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
|
|
283
305
|
export const checkAll = style({
|
|
284
306
|
display: 'flex',
|
|
285
307
|
alignItems: 'center',
|