@teammodlr/ui-kit 2.3.22 → 3.0.0-beta.2
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/avatar/Avatar.vue.d.ts +2 -2
- package/lib/components/badge/style/Badge.css.d.ts +2 -2
- package/lib/components/button/Button.vue.d.ts +7 -4
- package/lib/components/button/style/Button.css.d.ts +3 -3
- package/lib/components/cardcontextmenu/CardContextMenu.vue.d.ts +5 -8
- package/lib/components/cardcontextmenu/CardContextMenuItem.vue.d.ts +11 -2
- package/lib/components/cardcontextmenu/CardContextMenuTriggerProvider.vue.d.ts +2 -0
- package/lib/components/cardcontextmenu/style/Dropdown.css.d.ts +2 -1
- package/lib/components/cardcontextmenu/symbol.d.ts +1 -0
- package/lib/components/checkbox/Checkbox.vue.d.ts +15 -4
- package/lib/components/collapse/CollapseItem.vue.d.ts +3 -2
- package/lib/components/colorpicker/ColorPicker.vue.d.ts +8 -9
- package/lib/components/confirm/Confirm.vue.d.ts +9 -0
- package/lib/components/confirm/props.d.ts +4 -0
- package/lib/components/dialog/Dialog.vue.d.ts +9 -4
- package/lib/components/dialogprovider/DialogProvider.vue.d.ts +1 -0
- package/lib/components/dialogprovider/types.d.ts +1 -0
- package/lib/components/dropdown/Dropdown.vue.d.ts +3 -1
- package/lib/components/dropdown/DropdownItem.vue.d.ts +11 -1
- package/lib/components/dropdown/DropdownSeperator.vue.d.ts +1 -0
- package/lib/components/dropdown/DropdownTriggerProvider.vue.d.ts +2 -0
- package/lib/components/dropdown/style/Dropdown.css.d.ts +2 -1
- package/lib/components/dropdown/symbol.d.ts +2 -0
- package/lib/components/image/Image.vue.d.ts +1 -1
- package/lib/components/messageprovider/Message.vue.d.ts +1 -1
- package/lib/components/messageprovider/MessageProvider.vue.d.ts +22 -24
- package/lib/components/messageprovider/style/Message.css.d.ts +1 -1
- package/lib/components/messageprovider/types.d.ts +5 -0
- package/lib/components/modal/Modal.vue.d.ts +18 -6
- package/lib/components/radio/Radio.vue.d.ts +2 -0
- package/lib/components/radiobuttongroup/RadioButtonGroup.vue.d.ts +1 -0
- package/lib/components/select/Select.vue.d.ts +23 -11
- package/lib/components/select/style/Select.css.d.ts +2 -0
- package/lib/components/switch/Switch.vue.d.ts +2 -0
- package/lib/components/tab/Tab.vue.d.ts +5 -2
- package/lib/components/tab/TabGroup.vue.d.ts +8 -3
- package/lib/components/textarea/Textarea.vue.d.ts +2 -1
- package/lib/components/tooltip/Tooltip.vue.d.ts +5 -20
- package/lib/components/tooltip/TooltipTrigger.vue.d.ts +1 -10
- package/lib/components/tree/Tree.vue.d.ts +3 -3
- package/lib/components/tree/TreeNode.vue.d.ts +26 -12
- package/lib/components/tree/types.d.ts +5 -5
- package/lib/index.cjs.js +5 -14
- package/lib/index.es.js +14765 -10217
- package/lib/style.css +1 -1
- package/package.json +12 -6
- package/src/components/avatar/Avatar.vue +32 -24
- package/src/components/button/Button.vue +24 -5
- package/src/components/cardcontextmenu/CardContextMenu.vue +68 -95
- package/src/components/cardcontextmenu/CardContextMenuItem.vue +40 -50
- package/src/components/cardcontextmenu/CardContextMenuSeperator.vue +9 -7
- package/src/components/cardcontextmenu/CardContextMenuTriggerProvider.vue +15 -0
- package/src/components/cardcontextmenu/style/Dropdown.css.ts +155 -2
- package/src/components/cardcontextmenu/symbol.ts +3 -0
- package/src/components/checkbox/Checkbox.vue +37 -12
- package/src/components/checkbox/style/Checkbox.css.ts +6 -6
- package/src/components/collapse/CollapseItem.vue +46 -21
- package/src/components/collapse/style/Collapse.css.ts +5 -1
- package/src/components/colorpicker/ColorPicker.vue +76 -45
- package/src/components/confirm/Confirm.vue +15 -8
- package/src/components/confirm/props.ts +4 -0
- package/src/components/dialog/Dialog.vue +169 -36
- package/src/components/dialogprovider/types.ts +1 -0
- package/src/components/dropdown/Dropdown.vue +46 -3
- package/src/components/dropdown/DropdownItem.vue +55 -25
- package/src/components/dropdown/DropdownSeperator.vue +19 -11
- package/src/components/dropdown/DropdownTriggerProvider.vue +15 -0
- package/src/components/dropdown/examples/context.vue +2 -2
- package/src/components/dropdown/style/Dropdown.css.ts +157 -4
- package/src/components/dropdown/symbol.ts +4 -0
- package/src/components/form/examples/basic.vue +19 -8
- package/src/components/messageprovider/MessageProvider.vue +136 -90
- package/src/components/messageprovider/examples/basic.vue +3 -1
- package/src/components/messageprovider/examples/index.stories.ts +37 -1
- package/src/components/messageprovider/types.ts +10 -0
- package/src/components/modal/Modal.vue +142 -67
- package/src/components/radio/Radio.vue +31 -17
- package/src/components/radiobutton/RadioButton.vue +15 -2
- package/src/components/radiobuttongroup/RadioButtonGroup.vue +11 -4
- package/src/components/select/Select.vue +309 -148
- package/src/components/select/examples/basic.vue +1 -1
- package/src/components/select/style/Select.css.ts +15 -0
- package/src/components/slider/Slider.vue +1 -1
- package/src/components/switch/Switch.vue +30 -10
- package/src/components/switch/style/Switch.css.ts +3 -5
- package/src/components/tab/Tab.vue +29 -20
- package/src/components/tab/TabGroup.vue +29 -38
- package/src/components/textarea/Textarea.vue +8 -4
- package/src/components/tooltip/Tooltip.vue +52 -141
- package/src/components/tooltip/TooltipTrigger.vue +14 -17
- package/src/components/tooltip/style/Tooltip.css.ts +148 -29
- package/src/components/tree/Tree.vue +138 -100
- package/src/components/tree/TreeNode.vue +105 -98
- package/src/components/tree/types.ts +4 -4
- package/src/styles/transitions.css.ts +36 -16
|
@@ -1,62 +1,70 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
<
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
<div :class="computedWrapperClasses" v-on="inputListeners" :id="id" ref="container"
|
|
11
|
-
:aria-disabled="disabled ? 'true' : 'false'" :aria-labelledby="`${id}-label`" tabindex="0"
|
|
12
|
-
@mousedown.prevent="onSelectClick" @focusin.stop="focusSelect" :data-value="selected">
|
|
13
|
-
<div :class="selectComputedClasses">
|
|
14
|
-
<template v-if="
|
|
15
|
-
multiple &&
|
|
16
|
-
Array.isArray(selected) &&
|
|
17
|
-
selected.length > 0
|
|
18
|
-
">
|
|
19
|
-
<MTag v-for="value in selectedMax" :key="value" :closeable="true"
|
|
20
|
-
@close="event => removeSelected(event, value)" :size="size">
|
|
21
|
-
{{ getSelectedLabel(value) }}
|
|
22
|
-
</MTag>
|
|
23
|
-
<template v-if="selectedCountNotShown > 0">
|
|
24
|
-
<MTag :size="size">
|
|
25
|
-
+{{ selectedCountNotShown }} more
|
|
26
|
-
</MTag>
|
|
27
|
-
</template>
|
|
28
|
-
</template>
|
|
29
|
-
<template v-else-if="!multiple && selected">
|
|
30
|
-
{{ getSelectedLabel(selected) }}
|
|
31
|
-
</template>
|
|
32
|
-
<template v-else>
|
|
33
|
-
<div :class="style.placeholder">{{ placeholder }}</div>
|
|
34
|
-
</template>
|
|
2
|
+
<PopoverRoot :open="isOpen" @update:open="handleOpenChange">
|
|
3
|
+
<div ref="selectRef" tabindex="-1">
|
|
4
|
+
<label v-if="label" :for="id" :class="style.label" :id="`${id}-label`">
|
|
5
|
+
{{ label }}
|
|
6
|
+
</label>
|
|
7
|
+
<div v-if="description" :class="style.description">
|
|
8
|
+
{{ description }}
|
|
35
9
|
</div>
|
|
36
10
|
|
|
37
|
-
<
|
|
38
|
-
<
|
|
39
|
-
:
|
|
40
|
-
|
|
41
|
-
|
|
11
|
+
<PopoverTrigger as-child :disabled="disabled || loading">
|
|
12
|
+
<div :class="computedWrapperClasses" v-on="inputListeners" :id="id" ref="container"
|
|
13
|
+
:aria-disabled="disabled ? 'true' : 'false'" :aria-labelledby="`${id}-label`" tabindex="0"
|
|
14
|
+
data-m-select-trigger="true"
|
|
15
|
+
@focus="handleTriggerFocus" @blur="handleTriggerBlur" @keydown="handleKeyDown" :data-value="selected">
|
|
16
|
+
<div :class="selectComputedClasses">
|
|
17
|
+
<template v-if="multiple && Array.isArray(selected) && selected.length > 0">
|
|
18
|
+
<MTag v-for="value in selectedMax" :key="value" :closeable="true"
|
|
19
|
+
@close="event => removeSelected(event, value)" :size="size">
|
|
20
|
+
{{ getSelectedLabel(value) }}
|
|
21
|
+
</MTag>
|
|
22
|
+
<template v-if="selectedCountNotShown > 0">
|
|
23
|
+
<MTag :size="size">
|
|
24
|
+
+{{ selectedCountNotShown }} more
|
|
25
|
+
</MTag>
|
|
26
|
+
</template>
|
|
27
|
+
</template>
|
|
28
|
+
<template v-else-if="!multiple && selected">
|
|
29
|
+
{{ getSelectedLabel(selected) }}
|
|
30
|
+
</template>
|
|
31
|
+
<template v-else>
|
|
32
|
+
<div :class="style.placeholder">{{ placeholder }}</div>
|
|
33
|
+
</template>
|
|
34
|
+
<span :class="selectComputedArrowClasses">
|
|
35
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-down-icon lucide-chevron-down"><path d="m6 9 6 6 6-6"/></svg>
|
|
36
|
+
</span>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
</PopoverTrigger>
|
|
40
|
+
|
|
41
|
+
<PopoverPortal :disabled="!portal">
|
|
42
|
+
<Transition type="animation" :enter-active-class="fade.enterActive" :leave-active-class="fade.leaveActive"
|
|
43
|
+
:enter-from-class="fade.enterFrom" :leave-to-class="fade.leaveTo">
|
|
44
|
+
<PopoverContent
|
|
45
|
+
v-if="isOpen"
|
|
46
|
+
ref="popper"
|
|
47
|
+
:class="style.menu"
|
|
48
|
+
:style="computedConsistentWidth"
|
|
49
|
+
side="bottom"
|
|
50
|
+
align="start"
|
|
51
|
+
:side-offset="5"
|
|
52
|
+
:collision-padding="8"
|
|
53
|
+
:disable-outside-pointer-events="false"
|
|
54
|
+
:aria-hidden="isOpen ? 'false' : 'true'"
|
|
55
|
+
:data-element="`select-menu-overlay-${id}`"
|
|
56
|
+
@keydown.capture="handleKeyDown"
|
|
57
|
+
@open-auto-focus.prevent
|
|
58
|
+
@close-auto-focus.prevent
|
|
59
|
+
>
|
|
42
60
|
<div v-if="searchable || (multiple && selectAll)" :class="style.search">
|
|
43
61
|
<div v-if="multiple && selectAll" :class="style.checkAll">
|
|
44
62
|
<button role="button" type="button" :class="{
|
|
45
63
|
[style.checkAllButton]: true,
|
|
46
|
-
[style.checkAllButtonActive]:
|
|
47
|
-
computedOptions.length > 0 &&
|
|
48
|
-
computedOptions.length ===
|
|
49
|
-
selected.length
|
|
64
|
+
[style.checkAllButtonActive]: computedOptions.length > 0 && computedOptions.length === selected.length
|
|
50
65
|
}" @click.prevent="toggleAllItems" aria-description="Select all items">
|
|
51
|
-
<svg v-if="
|
|
52
|
-
|
|
53
|
-
computedOptions.length ===
|
|
54
|
-
selected.length
|
|
55
|
-
" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"
|
|
56
|
-
aria-hidden="true">
|
|
57
|
-
<path fill-rule="evenodd"
|
|
58
|
-
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"
|
|
59
|
-
clip-rule="evenodd"></path>
|
|
66
|
+
<svg v-if="computedOptions.length > 0 && computedOptions.length === selected.length" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
|
67
|
+
<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>
|
|
60
68
|
</svg>
|
|
61
69
|
</button>
|
|
62
70
|
</div>
|
|
@@ -67,42 +75,36 @@
|
|
|
67
75
|
<div v-for="(option, index) in computedOptions" :key="index" ref="optionsRef" :class="{
|
|
68
76
|
[style.option]: true,
|
|
69
77
|
[style.optionSelected]: option.active,
|
|
70
|
-
[style.optionFocused]:
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
:data-element="`select-menu-option-${id}-${option.value}`"
|
|
74
|
-
>
|
|
78
|
+
[style.optionFocused]: selectedIndex === index
|
|
79
|
+
}" @click.prevent="option.onClick" @mouseenter="selectedIndex = index"
|
|
80
|
+
:data-element="`select-menu-option-${id}-${option.value}`">
|
|
75
81
|
<span :class="style.optionIcon">
|
|
76
|
-
<svg v-if="option.active" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
|
|
77
|
-
fill="
|
|
78
|
-
<path fill-rule="evenodd"
|
|
79
|
-
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"
|
|
80
|
-
clip-rule="evenodd"></path>
|
|
82
|
+
<svg v-if="option.active" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
|
83
|
+
<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>
|
|
81
84
|
</svg>
|
|
82
85
|
</span>
|
|
83
|
-
<span :class="style.optionLabel">{{
|
|
84
|
-
option.label
|
|
85
|
-
}}</span>
|
|
86
|
+
<span :class="style.optionLabel">{{ option.label }}</span>
|
|
86
87
|
</div>
|
|
87
88
|
<template v-if="computedOptions.length === 0">
|
|
88
89
|
<div :class="style.noResults">No results</div>
|
|
89
90
|
</template>
|
|
90
91
|
</div>
|
|
91
|
-
</
|
|
92
|
+
</PopoverContent>
|
|
92
93
|
</Transition>
|
|
93
|
-
</
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
94
|
+
</PopoverPortal>
|
|
95
|
+
|
|
96
|
+
<div v-if="inputError" :class="style.errorMessage">
|
|
97
|
+
{{ inputError }}
|
|
98
|
+
</div>
|
|
97
99
|
</div>
|
|
98
|
-
</
|
|
100
|
+
</PopoverRoot>
|
|
99
101
|
</template>
|
|
100
102
|
|
|
101
103
|
<script lang="ts">
|
|
102
104
|
import { useInput } from '../../composables/useInput';
|
|
103
|
-
import { usePopper, type Placement } from '../../composables/usePopper';
|
|
104
105
|
import type { MaybeArray, Size } from '../../types/props';
|
|
105
|
-
import {
|
|
106
|
+
import { PopoverContent, PopoverPortal, PopoverRoot, PopoverTrigger } from 'reka-ui';
|
|
107
|
+
import { useEventListener } from '@vueuse/core';
|
|
106
108
|
import {
|
|
107
109
|
computed,
|
|
108
110
|
defineComponent,
|
|
@@ -110,16 +112,18 @@ import {
|
|
|
110
112
|
ref,
|
|
111
113
|
watch,
|
|
112
114
|
watchEffect,
|
|
113
|
-
|
|
115
|
+
type PropType
|
|
114
116
|
} from 'vue';
|
|
115
117
|
import MTag from '../tag/Tag.vue';
|
|
116
118
|
import * as style from './style';
|
|
117
|
-
import {
|
|
119
|
+
import { fade } from '../../styles/transitions.css';
|
|
118
120
|
import { useInteractive } from '../../composables/useInteractive';
|
|
119
121
|
import type { OnUpdateValue, OnUpdateValueImpl } from './types';
|
|
120
122
|
import { call } from "../../utils/call";
|
|
121
123
|
import {useZIndex} from "../../composables/useZIndex";
|
|
122
124
|
|
|
125
|
+
let shouldOpenNextFocusedSelect = false;
|
|
126
|
+
|
|
123
127
|
export default defineComponent({
|
|
124
128
|
name: 'MSelect',
|
|
125
129
|
emits: {
|
|
@@ -194,6 +198,10 @@ export default defineComponent({
|
|
|
194
198
|
type: Number,
|
|
195
199
|
default: 5
|
|
196
200
|
},
|
|
201
|
+
portal: {
|
|
202
|
+
type: Boolean,
|
|
203
|
+
default: false
|
|
204
|
+
},
|
|
197
205
|
zIndex: {
|
|
198
206
|
type: Number
|
|
199
207
|
},
|
|
@@ -205,22 +213,28 @@ export default defineComponent({
|
|
|
205
213
|
onUpdateValue: [Function, Array] as PropType<MaybeArray<OnUpdateValue> | undefined>,
|
|
206
214
|
},
|
|
207
215
|
components: {
|
|
208
|
-
MTag
|
|
216
|
+
MTag,
|
|
217
|
+
PopoverContent,
|
|
218
|
+
PopoverPortal,
|
|
219
|
+
PopoverRoot,
|
|
220
|
+
PopoverTrigger
|
|
209
221
|
},
|
|
210
222
|
setup(props, { emit, expose }) {
|
|
211
223
|
const container = ref<HTMLElement | null>(null);
|
|
212
|
-
const
|
|
213
|
-
const
|
|
214
|
-
const searchRef = ref<HTMLElement>();
|
|
224
|
+
const optionsRef = ref<HTMLElement[]>([]);
|
|
225
|
+
const searchRef = ref<HTMLInputElement>();
|
|
215
226
|
const selectRef = ref<HTMLElement>();
|
|
227
|
+
const popper = ref<HTMLElement>();
|
|
216
228
|
const selectedIndex = ref<number | undefined>(0);
|
|
217
229
|
const open = ref<boolean>(false);
|
|
218
230
|
const focused = ref<boolean>(false);
|
|
219
231
|
const search = ref<string>('');
|
|
220
|
-
const placement = ref<Placement>('bottom-start');
|
|
221
|
-
const transfer = ref(false);
|
|
222
232
|
const width = ref<number>(0);
|
|
223
233
|
const keyboardNavigation = ref<boolean>(false);
|
|
234
|
+
const focusOpenedByKeyboard = ref<boolean>(false);
|
|
235
|
+
const pendingFocusDirection = ref<boolean | null>(null);
|
|
236
|
+
const restoreTriggerFocusOnClose = ref<boolean>(false);
|
|
237
|
+
const suppressOpenOnFocusOnce = ref<boolean>(false);
|
|
224
238
|
const {nextZIndex} = useZIndex();
|
|
225
239
|
function doUpdateValue(
|
|
226
240
|
value: string | number | Array<string | number> | null,
|
|
@@ -251,14 +265,6 @@ export default defineComponent({
|
|
|
251
265
|
formLoading
|
|
252
266
|
} = useInput(props, { focus, emit, withListeners: false });
|
|
253
267
|
|
|
254
|
-
const { popper, updatePopper } = usePopper({
|
|
255
|
-
placement,
|
|
256
|
-
transfer,
|
|
257
|
-
reference: container,
|
|
258
|
-
wrapper,
|
|
259
|
-
offset: [0, 5]
|
|
260
|
-
});
|
|
261
|
-
|
|
262
268
|
const disabled = computed(() => formDisabled || props.disabled);
|
|
263
269
|
const loading = computed(() => formLoading || props.loading);
|
|
264
270
|
|
|
@@ -321,7 +327,7 @@ export default defineComponent({
|
|
|
321
327
|
|
|
322
328
|
return {
|
|
323
329
|
...styles,
|
|
324
|
-
zIndex: zIndex.value
|
|
330
|
+
zIndex: props.portal ? zIndex.value : 1
|
|
325
331
|
};
|
|
326
332
|
});
|
|
327
333
|
|
|
@@ -434,11 +440,16 @@ export default defineComponent({
|
|
|
434
440
|
}
|
|
435
441
|
}
|
|
436
442
|
|
|
437
|
-
function openSelect() {
|
|
443
|
+
function openSelect(forceOpen = false) {
|
|
438
444
|
if (disabled.value || loading.value) {
|
|
439
445
|
return;
|
|
440
446
|
}
|
|
441
447
|
|
|
448
|
+
if (forceOpen) {
|
|
449
|
+
open.value = true;
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
|
|
442
453
|
if (open.value) {
|
|
443
454
|
open.value = false;
|
|
444
455
|
return;
|
|
@@ -473,6 +484,88 @@ export default defineComponent({
|
|
|
473
484
|
}
|
|
474
485
|
}
|
|
475
486
|
|
|
487
|
+
function getFocusableElements() {
|
|
488
|
+
return Array.from(
|
|
489
|
+
document.querySelectorAll<HTMLElement>(
|
|
490
|
+
'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
|
|
491
|
+
)
|
|
492
|
+
).filter(element => {
|
|
493
|
+
return !element.matches(':disabled')
|
|
494
|
+
&& element.getAttribute('aria-disabled') !== 'true'
|
|
495
|
+
&& element.getAttribute('aria-hidden') !== 'true'
|
|
496
|
+
&& element.tabIndex >= 0
|
|
497
|
+
&& element.getClientRects().length > 0;
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
function getElementRef<T extends HTMLElement>(value: T | { $el?: T } | null | undefined) {
|
|
502
|
+
if (!value) {
|
|
503
|
+
return null;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
if (value instanceof HTMLElement) {
|
|
507
|
+
return value;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
if ('$el' in value && value.$el instanceof HTMLElement) {
|
|
511
|
+
return value.$el;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
return null;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
function getAdjacentFocusableElement(backwards = false) {
|
|
518
|
+
if (!container.value) {
|
|
519
|
+
return null;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
const popperElement = getElementRef(popper.value);
|
|
523
|
+
|
|
524
|
+
const focusableElements = getFocusableElements();
|
|
525
|
+
const currentIndex = focusableElements.indexOf(container.value);
|
|
526
|
+
|
|
527
|
+
if (currentIndex === -1) {
|
|
528
|
+
return null;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
const isInsideCurrentSelect = (element: HTMLElement) => {
|
|
532
|
+
return container.value?.contains(element)
|
|
533
|
+
|| popperElement?.contains(element)
|
|
534
|
+
|| selectRef.value?.contains(element);
|
|
535
|
+
};
|
|
536
|
+
|
|
537
|
+
let nextIndex = backwards ? currentIndex - 1 : currentIndex + 1;
|
|
538
|
+
|
|
539
|
+
while (nextIndex >= 0 && nextIndex < focusableElements.length) {
|
|
540
|
+
const nextElement = focusableElements[nextIndex];
|
|
541
|
+
|
|
542
|
+
if (!isInsideCurrentSelect(nextElement)) {
|
|
543
|
+
return nextElement;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
nextIndex += backwards ? -1 : 1;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
return null;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
function focusAdjacentElement(backwards = false) {
|
|
553
|
+
const nextElement = getAdjacentFocusableElement(backwards);
|
|
554
|
+
|
|
555
|
+
if (!nextElement) {
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
shouldOpenNextFocusedSelect = nextElement.getAttribute('data-m-select-trigger') === 'true';
|
|
560
|
+
|
|
561
|
+
nextElement.focus();
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
function closeAndMoveFocus(backwards = false) {
|
|
565
|
+
pendingFocusDirection.value = backwards;
|
|
566
|
+
open.value = false;
|
|
567
|
+
}
|
|
568
|
+
|
|
476
569
|
function handleOptionClick(value: string | number) {
|
|
477
570
|
const option = computedOptions.value.find(
|
|
478
571
|
item => item.value === value
|
|
@@ -486,17 +579,15 @@ export default defineComponent({
|
|
|
486
579
|
const index = selected.value.indexOf(value);
|
|
487
580
|
|
|
488
581
|
if (index !== -1) {
|
|
489
|
-
selected.value.
|
|
582
|
+
selected.value = selected.value.filter(item => item !== value);
|
|
490
583
|
} else {
|
|
491
|
-
selected.value.
|
|
492
|
-
emit('update:modelValue', selected.value);
|
|
584
|
+
selected.value = [...selected.value, value];
|
|
493
585
|
}
|
|
494
586
|
} else {
|
|
495
587
|
selected.value = [value];
|
|
496
588
|
}
|
|
497
|
-
|
|
498
|
-
updatePopper();
|
|
499
589
|
} else {
|
|
590
|
+
restoreTriggerFocusOnClose.value = true;
|
|
500
591
|
selected.value = value;
|
|
501
592
|
open.value = false;
|
|
502
593
|
}
|
|
@@ -510,13 +601,7 @@ export default defineComponent({
|
|
|
510
601
|
}
|
|
511
602
|
|
|
512
603
|
if (Array.isArray(selected.value)) {
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
if (index !== -1) {
|
|
516
|
-
selected.value.splice(index, 1);
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
updatePopper();
|
|
604
|
+
selected.value = selected.value.filter(item => item !== value);
|
|
520
605
|
}
|
|
521
606
|
}
|
|
522
607
|
|
|
@@ -524,26 +609,58 @@ export default defineComponent({
|
|
|
524
609
|
open.value = false;
|
|
525
610
|
});
|
|
526
611
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
612
|
+
useEventListener('keydown', (event: KeyboardEvent) => {
|
|
613
|
+
focusOpenedByKeyboard.value = event.key === 'Tab';
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
useEventListener('pointerdown', () => {
|
|
617
|
+
focusOpenedByKeyboard.value = false;
|
|
618
|
+
});
|
|
531
619
|
|
|
620
|
+
function handleKeyDown(e: Event) {
|
|
532
621
|
if (props.disabled) {
|
|
533
622
|
return;
|
|
534
623
|
}
|
|
535
624
|
|
|
536
|
-
keyboardNavigation.value = true;
|
|
537
|
-
|
|
538
625
|
const event: KeyboardEvent = e as KeyboardEvent;
|
|
539
626
|
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
627
|
+
if (event.key === 'Tab') {
|
|
628
|
+
if (open.value) {
|
|
629
|
+
const activeElement = document.activeElement as HTMLElement | null;
|
|
630
|
+
const popperElement = getElementRef(popper.value);
|
|
631
|
+
const isFocusInsidePopup = !!activeElement && !!popperElement?.contains(activeElement);
|
|
632
|
+
|
|
633
|
+
if (isFocusInsidePopup) {
|
|
634
|
+
event.preventDefault();
|
|
635
|
+
const nextElement = getAdjacentFocusableElement(event.shiftKey);
|
|
636
|
+
|
|
637
|
+
pendingFocusDirection.value = null;
|
|
638
|
+
restoreTriggerFocusOnClose.value = false;
|
|
639
|
+
open.value = false;
|
|
640
|
+
|
|
641
|
+
if (nextElement) {
|
|
642
|
+
requestAnimationFrame(() => {
|
|
643
|
+
shouldOpenNextFocusedSelect = nextElement.getAttribute('data-m-select-trigger') === 'true';
|
|
644
|
+
|
|
645
|
+
nextElement.focus();
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
} else {
|
|
649
|
+
pendingFocusDirection.value = null;
|
|
650
|
+
restoreTriggerFocusOnClose.value = false;
|
|
543
651
|
open.value = false;
|
|
544
652
|
}
|
|
545
|
-
|
|
653
|
+
}
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
keyboardNavigation.value = true;
|
|
658
|
+
|
|
659
|
+
switch (event.key) {
|
|
546
660
|
case 'ArrowDown':
|
|
661
|
+
if (computedOptions.value.length === 0) {
|
|
662
|
+
return;
|
|
663
|
+
}
|
|
547
664
|
e.preventDefault();
|
|
548
665
|
if (!open.value) {
|
|
549
666
|
open.value = true;
|
|
@@ -553,6 +670,9 @@ export default defineComponent({
|
|
|
553
670
|
getSelectedIndex(selectedIndex.value, 'down');
|
|
554
671
|
break;
|
|
555
672
|
case 'ArrowUp':
|
|
673
|
+
if (computedOptions.value.length === 0) {
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
556
676
|
e.preventDefault();
|
|
557
677
|
if (!open.value) {
|
|
558
678
|
return;
|
|
@@ -561,6 +681,9 @@ export default defineComponent({
|
|
|
561
681
|
getSelectedIndex(selectedIndex.value, 'up');
|
|
562
682
|
break;
|
|
563
683
|
case 'Enter':
|
|
684
|
+
if (computedOptions.value.length === 0) {
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
564
687
|
e.preventDefault();
|
|
565
688
|
e.stopPropagation();
|
|
566
689
|
|
|
@@ -569,9 +692,13 @@ export default defineComponent({
|
|
|
569
692
|
return;
|
|
570
693
|
}
|
|
571
694
|
|
|
572
|
-
|
|
695
|
+
const activeIndex = typeof selectedIndex.value === 'undefined'
|
|
696
|
+
? 0
|
|
697
|
+
: selectedIndex.value;
|
|
698
|
+
|
|
699
|
+
if (computedOptions.value[activeIndex]) {
|
|
573
700
|
handleOptionClick(
|
|
574
|
-
computedOptions.value[
|
|
701
|
+
computedOptions.value[activeIndex].value
|
|
575
702
|
);
|
|
576
703
|
if (!props.multiple) {
|
|
577
704
|
open.value = false;
|
|
@@ -579,13 +706,14 @@ export default defineComponent({
|
|
|
579
706
|
}
|
|
580
707
|
break;
|
|
581
708
|
case 'Escape':
|
|
582
|
-
e.preventDefault();
|
|
583
|
-
e.stopPropagation();
|
|
584
|
-
|
|
585
709
|
if (!open.value) {
|
|
586
710
|
return;
|
|
587
711
|
}
|
|
588
712
|
|
|
713
|
+
e.preventDefault();
|
|
714
|
+
e.stopPropagation();
|
|
715
|
+
|
|
716
|
+
restoreTriggerFocusOnClose.value = true;
|
|
589
717
|
open.value = false;
|
|
590
718
|
break;
|
|
591
719
|
}
|
|
@@ -604,13 +732,17 @@ export default defineComponent({
|
|
|
604
732
|
];
|
|
605
733
|
});
|
|
606
734
|
|
|
607
|
-
const
|
|
735
|
+
const selectComputedArrowClasses = computed(() => {
|
|
608
736
|
return [
|
|
609
|
-
style.
|
|
737
|
+
style.arrow,
|
|
610
738
|
{
|
|
611
|
-
[style.
|
|
739
|
+
[style.arrowOpen]: open.value
|
|
612
740
|
}
|
|
613
|
-
]
|
|
741
|
+
]
|
|
742
|
+
})
|
|
743
|
+
|
|
744
|
+
const computedWrapperClasses = computed(() => {
|
|
745
|
+
return [style.wrapper];
|
|
614
746
|
});
|
|
615
747
|
|
|
616
748
|
const selectComputedClasses = computed(() => {
|
|
@@ -621,10 +753,10 @@ export default defineComponent({
|
|
|
621
753
|
props.multiple &&
|
|
622
754
|
Array.isArray(selected.value) &&
|
|
623
755
|
selected.value.length > 0,
|
|
624
|
-
[style.focused]: open.value,
|
|
756
|
+
[style.focused]: open.value || focused.value,
|
|
625
757
|
// [`m-select--${props.size}`]: props.size,
|
|
626
758
|
'm-select--disabled': disabled.value || loading.value,
|
|
627
|
-
'm-select--focused': open.value,
|
|
759
|
+
'm-select--focused': open.value || focused.value,
|
|
628
760
|
'm-select--multiple': props.multiple,
|
|
629
761
|
'm-select--error': formError.value
|
|
630
762
|
}
|
|
@@ -642,17 +774,6 @@ export default defineComponent({
|
|
|
642
774
|
return option[props.optionLabel];
|
|
643
775
|
}
|
|
644
776
|
|
|
645
|
-
useEventListener(selectRef, 'keydown', handleKeyDown);
|
|
646
|
-
useEventListener(searchRef, 'keydown', handleKeyDown);
|
|
647
|
-
|
|
648
|
-
onClickOutside(
|
|
649
|
-
container,
|
|
650
|
-
() => {
|
|
651
|
-
open.value = false;
|
|
652
|
-
},
|
|
653
|
-
{ ignore: [searchRef, popper] }
|
|
654
|
-
);
|
|
655
|
-
|
|
656
777
|
watchEffect(() => {
|
|
657
778
|
if (container.value && open.value) {
|
|
658
779
|
width.value = container.value.offsetWidth;
|
|
@@ -681,22 +802,46 @@ export default defineComponent({
|
|
|
681
802
|
zIndex.value = nextZIndex();
|
|
682
803
|
}
|
|
683
804
|
|
|
684
|
-
if (props.searchable && searchRef.value) {
|
|
805
|
+
if (value && props.searchable && searchRef.value) {
|
|
685
806
|
searchRef.value.focus();
|
|
807
|
+
} else if (value && container.value) {
|
|
808
|
+
container.value.focus();
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
if (!value && restoreTriggerFocusOnClose.value && container.value) {
|
|
812
|
+
restoreTriggerFocusOnClose.value = false;
|
|
813
|
+
suppressOpenOnFocusOnce.value = true;
|
|
814
|
+
requestAnimationFrame(() => {
|
|
815
|
+
container.value?.focus();
|
|
816
|
+
});
|
|
686
817
|
}
|
|
818
|
+
|
|
687
819
|
if (value && optionsRef.value) {
|
|
820
|
+
const selectedValue = Array.isArray(selected.value)
|
|
821
|
+
? selected.value[0]
|
|
822
|
+
: selected.value;
|
|
688
823
|
const index = computedOptions.value.findIndex(
|
|
689
|
-
i => i.value ===
|
|
824
|
+
i => i.value === selectedValue
|
|
690
825
|
);
|
|
691
826
|
if (index !== -1) {
|
|
692
827
|
optionsRef.value[index].scrollIntoView({
|
|
693
828
|
block: 'nearest'
|
|
694
829
|
});
|
|
695
830
|
selectedIndex.value = index;
|
|
831
|
+
} else if (computedOptions.value.length > 0) {
|
|
832
|
+
selectedIndex.value = 0;
|
|
696
833
|
}
|
|
697
834
|
}
|
|
698
835
|
|
|
699
836
|
keyboardNavigation.value = false;
|
|
837
|
+
|
|
838
|
+
if (!value && pendingFocusDirection.value !== null) {
|
|
839
|
+
const backwards = pendingFocusDirection.value;
|
|
840
|
+
pendingFocusDirection.value = null;
|
|
841
|
+
requestAnimationFrame(() => {
|
|
842
|
+
focusAdjacentElement(backwards);
|
|
843
|
+
});
|
|
844
|
+
}
|
|
700
845
|
});
|
|
701
846
|
});
|
|
702
847
|
|
|
@@ -715,20 +860,33 @@ export default defineComponent({
|
|
|
715
860
|
return open.value;
|
|
716
861
|
});
|
|
717
862
|
|
|
718
|
-
const
|
|
719
|
-
|
|
720
|
-
if (selectRef.value && !open.value) {
|
|
721
|
-
openSelect();
|
|
722
|
-
}
|
|
723
|
-
}, 10);
|
|
863
|
+
const handleOpenChange = (value: boolean) => {
|
|
864
|
+
open.value = value;
|
|
724
865
|
};
|
|
725
866
|
|
|
726
|
-
const
|
|
727
|
-
|
|
728
|
-
|
|
867
|
+
const handleTriggerFocus = () => {
|
|
868
|
+
focused.value = true;
|
|
869
|
+
|
|
870
|
+
if (suppressOpenOnFocusOnce.value) {
|
|
871
|
+
suppressOpenOnFocusOnce.value = false;
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
const shouldOpenFromFocus = focusOpenedByKeyboard.value || shouldOpenNextFocusedSelect;
|
|
876
|
+
|
|
877
|
+
shouldOpenNextFocusedSelect = false;
|
|
878
|
+
|
|
879
|
+
if (!shouldOpenFromFocus || open.value) {
|
|
880
|
+
return;
|
|
881
|
+
}
|
|
882
|
+
|
|
729
883
|
openSelect();
|
|
730
884
|
};
|
|
731
885
|
|
|
886
|
+
const handleTriggerBlur = () => {
|
|
887
|
+
focused.value = false;
|
|
888
|
+
};
|
|
889
|
+
|
|
732
890
|
expose({
|
|
733
891
|
focus,
|
|
734
892
|
blur
|
|
@@ -740,8 +898,10 @@ export default defineComponent({
|
|
|
740
898
|
searchRef,
|
|
741
899
|
selectRef,
|
|
742
900
|
popper,
|
|
743
|
-
|
|
744
|
-
|
|
901
|
+
handleTriggerFocus,
|
|
902
|
+
handleTriggerBlur,
|
|
903
|
+
handleKeyDown,
|
|
904
|
+
handleOpenChange,
|
|
745
905
|
|
|
746
906
|
id,
|
|
747
907
|
isOpen,
|
|
@@ -754,6 +914,7 @@ export default defineComponent({
|
|
|
754
914
|
search,
|
|
755
915
|
|
|
756
916
|
containerComputedClasses,
|
|
917
|
+
selectComputedArrowClasses,
|
|
757
918
|
selectComputedClasses,
|
|
758
919
|
computedWrapperClasses,
|
|
759
920
|
computedConsistentWidth: computedStyle,
|
|
@@ -766,7 +927,7 @@ export default defineComponent({
|
|
|
766
927
|
inputListeners,
|
|
767
928
|
style,
|
|
768
929
|
focused,
|
|
769
|
-
|
|
930
|
+
fade,
|
|
770
931
|
toggleAllItems
|
|
771
932
|
};
|
|
772
933
|
}
|