| Numéro du ticket | Titre du ticket | |------------------|-----------------| | | | ## Description de la PR ## Modification du .env ## Check list - [ ] Pas de régression - [ ] TU/TI/TF rédigée - [ ] TU/TI/TF OK - [ ] CHANGELOG modifié --------- Co-authored-by: admin malio <malio@yuno.malio.fr> Co-authored-by: THOLOT DECHENE Matthieu <matthieu@yuno.malio.fr> Co-authored-by: matthieu <matthieu@yuno.malio.fr> Reviewed-on: #70 Co-authored-by: tristan <tristan@yuno.malio.fr> Co-committed-by: tristan <tristan@yuno.malio.fr>
This commit was merged in pull request #70.
This commit is contained in:
@@ -37,15 +37,24 @@
|
||||
twMerge(label ? 'min-h-[40px]' : 'h-[40px] py-0', fieldClass),
|
||||
rounded,
|
||||
textField,
|
||||
keyboardFocused
|
||||
? (isOpen
|
||||
? (openDirection === 'down' ? 'm-combo-ring-top' : 'm-combo-ring-bottom')
|
||||
: 'm-focus-ring-kbd')
|
||||
: '',
|
||||
]"
|
||||
:aria-expanded="isOpen"
|
||||
:aria-controls="listboxId"
|
||||
:aria-activedescendant="isOpen && activeIndex >= 0 ? optionId(activeIndex) : undefined"
|
||||
:aria-invalid="hasError"
|
||||
:aria-describedby="describedBy"
|
||||
:aria-required="required || undefined"
|
||||
:aria-readonly="isReadonly || undefined"
|
||||
:disabled="disabled"
|
||||
@click="toggle"
|
||||
@keydown="onKeydown"
|
||||
@focus="onKbdFocus"
|
||||
@blur="onKbdBlur"
|
||||
>
|
||||
<label
|
||||
v-if="label"
|
||||
@@ -134,7 +143,10 @@
|
||||
? 'border-m-danger'
|
||||
: hasSuccess
|
||||
? 'border-m-success'
|
||||
: 'border-m-primary'
|
||||
: 'border-m-primary',
|
||||
keyboardFocused
|
||||
? (openDirection === 'down' ? 'm-combo-ring-bottom' : 'm-combo-ring-top')
|
||||
: '',
|
||||
]"
|
||||
>
|
||||
<li
|
||||
@@ -184,13 +196,16 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {computed, onBeforeUnmount, onMounted, ref, useId, nextTick} from 'vue'
|
||||
import {computed, onBeforeUnmount, onMounted, ref, useId, nextTick, watch} from 'vue'
|
||||
import {Icon as IconifyIcon} from '@iconify/vue'
|
||||
import {twMerge} from 'tailwind-merge'
|
||||
import MalioRequiredMark from '../shared/RequiredMark.vue'
|
||||
import {useKbdFocusRing} from '../shared/useKbdFocusRing'
|
||||
|
||||
defineOptions({name: 'MalioSelect', inheritAttrs: false})
|
||||
|
||||
const {keyboardFocused, onFocus: onKbdFocus, onBlur: onKbdBlur} = useKbdFocusRing()
|
||||
|
||||
type Option = {
|
||||
label: string;
|
||||
value: string | number | null
|
||||
@@ -344,7 +359,68 @@ function toggle() {
|
||||
function select(value: string | number | null) {
|
||||
emit('update:modelValue', value)
|
||||
close()
|
||||
buttonRef.value?.blur()
|
||||
// On garde le focus sur le bouton après sélection (APG) : le focus ne doit pas
|
||||
// retomber sur le body. La souris le conserve déjà via @mousedown.prevent sur
|
||||
// les options ; au clavier le focus n'a jamais quitté le bouton.
|
||||
buttonRef.value?.focus()
|
||||
}
|
||||
|
||||
// Garde l'option active visible quand on navigue au clavier
|
||||
watch(activeIndex, async (index) => {
|
||||
if (index < 0 || !isOpen.value) return
|
||||
await nextTick()
|
||||
document.getElementById(optionId(index))?.scrollIntoView({block: 'nearest'})
|
||||
})
|
||||
|
||||
function onKeydown(e: KeyboardEvent) {
|
||||
if (props.disabled || props.readonly) return
|
||||
|
||||
// Tab : laisse le focus partir mais ferme la liste
|
||||
if (e.key === 'Tab') {
|
||||
if (isOpen.value) close()
|
||||
return
|
||||
}
|
||||
|
||||
// Liste fermée : ouverture au clavier
|
||||
if (!isOpen.value) {
|
||||
if (e.key === 'ArrowDown' || e.key === 'ArrowUp' || e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault()
|
||||
open()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Liste ouverte
|
||||
if (e.key === 'Escape') {
|
||||
e.preventDefault()
|
||||
close()
|
||||
return
|
||||
}
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault()
|
||||
const opt = normalizedOptions.value[activeIndex.value]
|
||||
if (opt) select(opt.value)
|
||||
return
|
||||
}
|
||||
if (e.key === 'ArrowDown') {
|
||||
e.preventDefault()
|
||||
activeIndex.value = Math.min(activeIndex.value + 1, normalizedOptions.value.length - 1)
|
||||
return
|
||||
}
|
||||
if (e.key === 'ArrowUp') {
|
||||
e.preventDefault()
|
||||
activeIndex.value = Math.max(activeIndex.value - 1, 0)
|
||||
return
|
||||
}
|
||||
if (e.key === 'Home') {
|
||||
e.preventDefault()
|
||||
activeIndex.value = 0
|
||||
return
|
||||
}
|
||||
if (e.key === 'End') {
|
||||
e.preventDefault()
|
||||
activeIndex.value = normalizedOptions.value.length - 1
|
||||
}
|
||||
}
|
||||
|
||||
function onClickOutside(e: MouseEvent) {
|
||||
|
||||
Reference in New Issue
Block a user