@teammodlr/ui-kit 2.3.23 → 3.0.0-beta.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/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 +13 -5
- 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 +20 -11
- 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 +15444 -10914
- 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 +183 -37
- package/src/components/dialog/style/Dialog.css.ts +15 -1
- 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/modal/style/Modal.css.ts +26 -2
- 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 +301 -153
- package/src/components/select/examples/basic.vue +1 -1
- 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,65 +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>
|
|
35
|
-
<span :class="selectComputedArrowClasses">
|
|
36
|
-
<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>
|
|
37
|
-
</span>
|
|
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 }}
|
|
38
9
|
</div>
|
|
39
10
|
|
|
40
|
-
<
|
|
41
|
-
<
|
|
42
|
-
:
|
|
43
|
-
|
|
44
|
-
|
|
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
|
+
>
|
|
45
60
|
<div v-if="searchable || (multiple && selectAll)" :class="style.search">
|
|
46
61
|
<div v-if="multiple && selectAll" :class="style.checkAll">
|
|
47
62
|
<button role="button" type="button" :class="{
|
|
48
63
|
[style.checkAllButton]: true,
|
|
49
|
-
[style.checkAllButtonActive]:
|
|
50
|
-
computedOptions.length > 0 &&
|
|
51
|
-
computedOptions.length ===
|
|
52
|
-
selected.length
|
|
64
|
+
[style.checkAllButtonActive]: computedOptions.length > 0 && computedOptions.length === selected.length
|
|
53
65
|
}" @click.prevent="toggleAllItems" aria-description="Select all items">
|
|
54
|
-
<svg v-if="
|
|
55
|
-
|
|
56
|
-
computedOptions.length ===
|
|
57
|
-
selected.length
|
|
58
|
-
" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"
|
|
59
|
-
aria-hidden="true">
|
|
60
|
-
<path fill-rule="evenodd"
|
|
61
|
-
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"
|
|
62
|
-
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>
|
|
63
68
|
</svg>
|
|
64
69
|
</button>
|
|
65
70
|
</div>
|
|
@@ -70,42 +75,36 @@
|
|
|
70
75
|
<div v-for="(option, index) in computedOptions" :key="index" ref="optionsRef" :class="{
|
|
71
76
|
[style.option]: true,
|
|
72
77
|
[style.optionSelected]: option.active,
|
|
73
|
-
[style.optionFocused]:
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
:data-element="`select-menu-option-${id}-${option.value}`"
|
|
77
|
-
>
|
|
78
|
+
[style.optionFocused]: selectedIndex === index
|
|
79
|
+
}" @click.prevent="option.onClick" @mouseenter="selectedIndex = index"
|
|
80
|
+
:data-element="`select-menu-option-${id}-${option.value}`">
|
|
78
81
|
<span :class="style.optionIcon">
|
|
79
|
-
<svg v-if="option.active" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
|
|
80
|
-
fill="
|
|
81
|
-
<path fill-rule="evenodd"
|
|
82
|
-
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"
|
|
83
|
-
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>
|
|
84
84
|
</svg>
|
|
85
85
|
</span>
|
|
86
|
-
<span :class="style.optionLabel">{{
|
|
87
|
-
option.label
|
|
88
|
-
}}</span>
|
|
86
|
+
<span :class="style.optionLabel">{{ option.label }}</span>
|
|
89
87
|
</div>
|
|
90
88
|
<template v-if="computedOptions.length === 0">
|
|
91
89
|
<div :class="style.noResults">No results</div>
|
|
92
90
|
</template>
|
|
93
91
|
</div>
|
|
94
|
-
</
|
|
92
|
+
</PopoverContent>
|
|
95
93
|
</Transition>
|
|
96
|
-
</
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
94
|
+
</PopoverPortal>
|
|
95
|
+
|
|
96
|
+
<div v-if="inputError" :class="style.errorMessage">
|
|
97
|
+
{{ inputError }}
|
|
98
|
+
</div>
|
|
100
99
|
</div>
|
|
101
|
-
</
|
|
100
|
+
</PopoverRoot>
|
|
102
101
|
</template>
|
|
103
102
|
|
|
104
103
|
<script lang="ts">
|
|
105
104
|
import { useInput } from '../../composables/useInput';
|
|
106
|
-
import { usePopper, type Placement } from '../../composables/usePopper';
|
|
107
105
|
import type { MaybeArray, Size } from '../../types/props';
|
|
108
|
-
import {
|
|
106
|
+
import { PopoverContent, PopoverPortal, PopoverRoot, PopoverTrigger } from 'reka-ui';
|
|
107
|
+
import { useEventListener } from '@vueuse/core';
|
|
109
108
|
import {
|
|
110
109
|
computed,
|
|
111
110
|
defineComponent,
|
|
@@ -113,16 +112,18 @@ import {
|
|
|
113
112
|
ref,
|
|
114
113
|
watch,
|
|
115
114
|
watchEffect,
|
|
116
|
-
|
|
115
|
+
type PropType
|
|
117
116
|
} from 'vue';
|
|
118
117
|
import MTag from '../tag/Tag.vue';
|
|
119
118
|
import * as style from './style';
|
|
120
|
-
import {
|
|
119
|
+
import { fade } from '../../styles/transitions.css';
|
|
121
120
|
import { useInteractive } from '../../composables/useInteractive';
|
|
122
121
|
import type { OnUpdateValue, OnUpdateValueImpl } from './types';
|
|
123
122
|
import { call } from "../../utils/call";
|
|
124
123
|
import {useZIndex} from "../../composables/useZIndex";
|
|
125
124
|
|
|
125
|
+
let shouldOpenNextFocusedSelect = false;
|
|
126
|
+
|
|
126
127
|
export default defineComponent({
|
|
127
128
|
name: 'MSelect',
|
|
128
129
|
emits: {
|
|
@@ -197,6 +198,10 @@ export default defineComponent({
|
|
|
197
198
|
type: Number,
|
|
198
199
|
default: 5
|
|
199
200
|
},
|
|
201
|
+
portal: {
|
|
202
|
+
type: Boolean,
|
|
203
|
+
default: false
|
|
204
|
+
},
|
|
200
205
|
zIndex: {
|
|
201
206
|
type: Number
|
|
202
207
|
},
|
|
@@ -208,22 +213,28 @@ export default defineComponent({
|
|
|
208
213
|
onUpdateValue: [Function, Array] as PropType<MaybeArray<OnUpdateValue> | undefined>,
|
|
209
214
|
},
|
|
210
215
|
components: {
|
|
211
|
-
MTag
|
|
216
|
+
MTag,
|
|
217
|
+
PopoverContent,
|
|
218
|
+
PopoverPortal,
|
|
219
|
+
PopoverRoot,
|
|
220
|
+
PopoverTrigger
|
|
212
221
|
},
|
|
213
222
|
setup(props, { emit, expose }) {
|
|
214
223
|
const container = ref<HTMLElement | null>(null);
|
|
215
|
-
const
|
|
216
|
-
const
|
|
217
|
-
const searchRef = ref<HTMLElement>();
|
|
224
|
+
const optionsRef = ref<HTMLElement[]>([]);
|
|
225
|
+
const searchRef = ref<HTMLInputElement>();
|
|
218
226
|
const selectRef = ref<HTMLElement>();
|
|
227
|
+
const popper = ref<HTMLElement>();
|
|
219
228
|
const selectedIndex = ref<number | undefined>(0);
|
|
220
229
|
const open = ref<boolean>(false);
|
|
221
230
|
const focused = ref<boolean>(false);
|
|
222
231
|
const search = ref<string>('');
|
|
223
|
-
const placement = ref<Placement>('bottom-start');
|
|
224
|
-
const transfer = ref(false);
|
|
225
232
|
const width = ref<number>(0);
|
|
226
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);
|
|
227
238
|
const {nextZIndex} = useZIndex();
|
|
228
239
|
function doUpdateValue(
|
|
229
240
|
value: string | number | Array<string | number> | null,
|
|
@@ -254,14 +265,6 @@ export default defineComponent({
|
|
|
254
265
|
formLoading
|
|
255
266
|
} = useInput(props, { focus, emit, withListeners: false });
|
|
256
267
|
|
|
257
|
-
const { popper, updatePopper } = usePopper({
|
|
258
|
-
placement,
|
|
259
|
-
transfer,
|
|
260
|
-
reference: container,
|
|
261
|
-
wrapper,
|
|
262
|
-
offset: [0, 5]
|
|
263
|
-
});
|
|
264
|
-
|
|
265
268
|
const disabled = computed(() => formDisabled || props.disabled);
|
|
266
269
|
const loading = computed(() => formLoading || props.loading);
|
|
267
270
|
|
|
@@ -324,7 +327,7 @@ export default defineComponent({
|
|
|
324
327
|
|
|
325
328
|
return {
|
|
326
329
|
...styles,
|
|
327
|
-
zIndex: zIndex.value
|
|
330
|
+
zIndex: props.portal ? zIndex.value : 1
|
|
328
331
|
};
|
|
329
332
|
});
|
|
330
333
|
|
|
@@ -437,11 +440,16 @@ export default defineComponent({
|
|
|
437
440
|
}
|
|
438
441
|
}
|
|
439
442
|
|
|
440
|
-
function openSelect() {
|
|
443
|
+
function openSelect(forceOpen = false) {
|
|
441
444
|
if (disabled.value || loading.value) {
|
|
442
445
|
return;
|
|
443
446
|
}
|
|
444
447
|
|
|
448
|
+
if (forceOpen) {
|
|
449
|
+
open.value = true;
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
|
|
445
453
|
if (open.value) {
|
|
446
454
|
open.value = false;
|
|
447
455
|
return;
|
|
@@ -476,6 +484,88 @@ export default defineComponent({
|
|
|
476
484
|
}
|
|
477
485
|
}
|
|
478
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
|
+
|
|
479
569
|
function handleOptionClick(value: string | number) {
|
|
480
570
|
const option = computedOptions.value.find(
|
|
481
571
|
item => item.value === value
|
|
@@ -489,17 +579,15 @@ export default defineComponent({
|
|
|
489
579
|
const index = selected.value.indexOf(value);
|
|
490
580
|
|
|
491
581
|
if (index !== -1) {
|
|
492
|
-
selected.value.
|
|
582
|
+
selected.value = selected.value.filter(item => item !== value);
|
|
493
583
|
} else {
|
|
494
|
-
selected.value.
|
|
495
|
-
emit('update:modelValue', selected.value);
|
|
584
|
+
selected.value = [...selected.value, value];
|
|
496
585
|
}
|
|
497
586
|
} else {
|
|
498
587
|
selected.value = [value];
|
|
499
588
|
}
|
|
500
|
-
|
|
501
|
-
updatePopper();
|
|
502
589
|
} else {
|
|
590
|
+
restoreTriggerFocusOnClose.value = true;
|
|
503
591
|
selected.value = value;
|
|
504
592
|
open.value = false;
|
|
505
593
|
}
|
|
@@ -513,13 +601,7 @@ export default defineComponent({
|
|
|
513
601
|
}
|
|
514
602
|
|
|
515
603
|
if (Array.isArray(selected.value)) {
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
if (index !== -1) {
|
|
519
|
-
selected.value.splice(index, 1);
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
updatePopper();
|
|
604
|
+
selected.value = selected.value.filter(item => item !== value);
|
|
523
605
|
}
|
|
524
606
|
}
|
|
525
607
|
|
|
@@ -527,26 +609,58 @@ export default defineComponent({
|
|
|
527
609
|
open.value = false;
|
|
528
610
|
});
|
|
529
611
|
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
612
|
+
useEventListener('keydown', (event: KeyboardEvent) => {
|
|
613
|
+
focusOpenedByKeyboard.value = event.key === 'Tab';
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
useEventListener('pointerdown', () => {
|
|
617
|
+
focusOpenedByKeyboard.value = false;
|
|
618
|
+
});
|
|
534
619
|
|
|
620
|
+
function handleKeyDown(e: Event) {
|
|
535
621
|
if (props.disabled) {
|
|
536
622
|
return;
|
|
537
623
|
}
|
|
538
624
|
|
|
539
|
-
keyboardNavigation.value = true;
|
|
540
|
-
|
|
541
625
|
const event: KeyboardEvent = e as KeyboardEvent;
|
|
542
626
|
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
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;
|
|
546
651
|
open.value = false;
|
|
547
652
|
}
|
|
548
|
-
|
|
653
|
+
}
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
keyboardNavigation.value = true;
|
|
658
|
+
|
|
659
|
+
switch (event.key) {
|
|
549
660
|
case 'ArrowDown':
|
|
661
|
+
if (computedOptions.value.length === 0) {
|
|
662
|
+
return;
|
|
663
|
+
}
|
|
550
664
|
e.preventDefault();
|
|
551
665
|
if (!open.value) {
|
|
552
666
|
open.value = true;
|
|
@@ -556,6 +670,9 @@ export default defineComponent({
|
|
|
556
670
|
getSelectedIndex(selectedIndex.value, 'down');
|
|
557
671
|
break;
|
|
558
672
|
case 'ArrowUp':
|
|
673
|
+
if (computedOptions.value.length === 0) {
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
559
676
|
e.preventDefault();
|
|
560
677
|
if (!open.value) {
|
|
561
678
|
return;
|
|
@@ -564,6 +681,9 @@ export default defineComponent({
|
|
|
564
681
|
getSelectedIndex(selectedIndex.value, 'up');
|
|
565
682
|
break;
|
|
566
683
|
case 'Enter':
|
|
684
|
+
if (computedOptions.value.length === 0) {
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
567
687
|
e.preventDefault();
|
|
568
688
|
e.stopPropagation();
|
|
569
689
|
|
|
@@ -572,9 +692,13 @@ export default defineComponent({
|
|
|
572
692
|
return;
|
|
573
693
|
}
|
|
574
694
|
|
|
575
|
-
|
|
695
|
+
const activeIndex = typeof selectedIndex.value === 'undefined'
|
|
696
|
+
? 0
|
|
697
|
+
: selectedIndex.value;
|
|
698
|
+
|
|
699
|
+
if (computedOptions.value[activeIndex]) {
|
|
576
700
|
handleOptionClick(
|
|
577
|
-
computedOptions.value[
|
|
701
|
+
computedOptions.value[activeIndex].value
|
|
578
702
|
);
|
|
579
703
|
if (!props.multiple) {
|
|
580
704
|
open.value = false;
|
|
@@ -582,13 +706,14 @@ export default defineComponent({
|
|
|
582
706
|
}
|
|
583
707
|
break;
|
|
584
708
|
case 'Escape':
|
|
585
|
-
e.preventDefault();
|
|
586
|
-
e.stopPropagation();
|
|
587
|
-
|
|
588
709
|
if (!open.value) {
|
|
589
710
|
return;
|
|
590
711
|
}
|
|
591
712
|
|
|
713
|
+
e.preventDefault();
|
|
714
|
+
e.stopPropagation();
|
|
715
|
+
|
|
716
|
+
restoreTriggerFocusOnClose.value = true;
|
|
592
717
|
open.value = false;
|
|
593
718
|
break;
|
|
594
719
|
}
|
|
@@ -617,12 +742,7 @@ export default defineComponent({
|
|
|
617
742
|
})
|
|
618
743
|
|
|
619
744
|
const computedWrapperClasses = computed(() => {
|
|
620
|
-
return [
|
|
621
|
-
style.wrapper,
|
|
622
|
-
{
|
|
623
|
-
[style.focused]: focused.value
|
|
624
|
-
}
|
|
625
|
-
];
|
|
745
|
+
return [style.wrapper];
|
|
626
746
|
});
|
|
627
747
|
|
|
628
748
|
const selectComputedClasses = computed(() => {
|
|
@@ -633,10 +753,10 @@ export default defineComponent({
|
|
|
633
753
|
props.multiple &&
|
|
634
754
|
Array.isArray(selected.value) &&
|
|
635
755
|
selected.value.length > 0,
|
|
636
|
-
[style.focused]: open.value,
|
|
756
|
+
[style.focused]: open.value || focused.value,
|
|
637
757
|
// [`m-select--${props.size}`]: props.size,
|
|
638
758
|
'm-select--disabled': disabled.value || loading.value,
|
|
639
|
-
'm-select--focused': open.value,
|
|
759
|
+
'm-select--focused': open.value || focused.value,
|
|
640
760
|
'm-select--multiple': props.multiple,
|
|
641
761
|
'm-select--error': formError.value
|
|
642
762
|
}
|
|
@@ -654,17 +774,6 @@ export default defineComponent({
|
|
|
654
774
|
return option[props.optionLabel];
|
|
655
775
|
}
|
|
656
776
|
|
|
657
|
-
useEventListener(selectRef, 'keydown', handleKeyDown);
|
|
658
|
-
useEventListener(searchRef, 'keydown', handleKeyDown);
|
|
659
|
-
|
|
660
|
-
onClickOutside(
|
|
661
|
-
container,
|
|
662
|
-
() => {
|
|
663
|
-
open.value = false;
|
|
664
|
-
},
|
|
665
|
-
{ ignore: [searchRef, popper] }
|
|
666
|
-
);
|
|
667
|
-
|
|
668
777
|
watchEffect(() => {
|
|
669
778
|
if (container.value && open.value) {
|
|
670
779
|
width.value = container.value.offsetWidth;
|
|
@@ -693,22 +802,46 @@ export default defineComponent({
|
|
|
693
802
|
zIndex.value = nextZIndex();
|
|
694
803
|
}
|
|
695
804
|
|
|
696
|
-
if (props.searchable && searchRef.value) {
|
|
805
|
+
if (value && props.searchable && searchRef.value) {
|
|
697
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
|
+
});
|
|
698
817
|
}
|
|
818
|
+
|
|
699
819
|
if (value && optionsRef.value) {
|
|
820
|
+
const selectedValue = Array.isArray(selected.value)
|
|
821
|
+
? selected.value[0]
|
|
822
|
+
: selected.value;
|
|
700
823
|
const index = computedOptions.value.findIndex(
|
|
701
|
-
i => i.value ===
|
|
824
|
+
i => i.value === selectedValue
|
|
702
825
|
);
|
|
703
826
|
if (index !== -1) {
|
|
704
827
|
optionsRef.value[index].scrollIntoView({
|
|
705
828
|
block: 'nearest'
|
|
706
829
|
});
|
|
707
830
|
selectedIndex.value = index;
|
|
831
|
+
} else if (computedOptions.value.length > 0) {
|
|
832
|
+
selectedIndex.value = 0;
|
|
708
833
|
}
|
|
709
834
|
}
|
|
710
835
|
|
|
711
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
|
+
}
|
|
712
845
|
});
|
|
713
846
|
});
|
|
714
847
|
|
|
@@ -727,20 +860,33 @@ export default defineComponent({
|
|
|
727
860
|
return open.value;
|
|
728
861
|
});
|
|
729
862
|
|
|
730
|
-
const
|
|
731
|
-
|
|
732
|
-
if (selectRef.value && !open.value) {
|
|
733
|
-
openSelect();
|
|
734
|
-
}
|
|
735
|
-
}, 10);
|
|
863
|
+
const handleOpenChange = (value: boolean) => {
|
|
864
|
+
open.value = value;
|
|
736
865
|
};
|
|
737
866
|
|
|
738
|
-
const
|
|
739
|
-
|
|
740
|
-
|
|
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
|
+
|
|
741
883
|
openSelect();
|
|
742
884
|
};
|
|
743
885
|
|
|
886
|
+
const handleTriggerBlur = () => {
|
|
887
|
+
focused.value = false;
|
|
888
|
+
};
|
|
889
|
+
|
|
744
890
|
expose({
|
|
745
891
|
focus,
|
|
746
892
|
blur
|
|
@@ -752,8 +898,10 @@ export default defineComponent({
|
|
|
752
898
|
searchRef,
|
|
753
899
|
selectRef,
|
|
754
900
|
popper,
|
|
755
|
-
|
|
756
|
-
|
|
901
|
+
handleTriggerFocus,
|
|
902
|
+
handleTriggerBlur,
|
|
903
|
+
handleKeyDown,
|
|
904
|
+
handleOpenChange,
|
|
757
905
|
|
|
758
906
|
id,
|
|
759
907
|
isOpen,
|
|
@@ -779,7 +927,7 @@ export default defineComponent({
|
|
|
779
927
|
inputListeners,
|
|
780
928
|
style,
|
|
781
929
|
focused,
|
|
782
|
-
|
|
930
|
+
fade,
|
|
783
931
|
toggleAllItems
|
|
784
932
|
};
|
|
785
933
|
}
|