feat : réécriture du squelette MalioDrawer (slots, side, contrôlé/non-contrôlé)

This commit is contained in:
2026-05-21 16:33:28 +02:00
parent 906320080d
commit 31f0cb38cd
2 changed files with 72 additions and 119 deletions

View File

@@ -1,57 +1,36 @@
<template>
<Teleport to="body">
<Transition
name="drawer"
:name="`drawer-${side}`"
appear
@after-leave="isRendered = false"
>
<div
v-if="isRendered && isOpen"
:id="componentId"
class="fixed inset-0 z-50 flex justify-end"
class="fixed inset-0 z-50 flex"
:class="side === 'right' ? 'justify-end' : 'justify-start'"
v-bind="attrs"
>
<div
class="absolute inset-0 bg-black/40"
:class="twMerge('absolute inset-0 bg-black/40', overlayClass)"
data-test="backdrop"
@click="close"
/>
<div
ref="panelRef"
:class="twMerge(
'relative z-50 flex h-full w-full max-w-md flex-col bg-white shadow-xl',
'relative z-50 flex h-full w-full max-w-md flex-col bg-white',
drawerClass,
)"
role="dialog"
:aria-modal="true"
:aria-labelledby="titleId"
aria-modal="true"
tabindex="-1"
data-test="panel"
>
<div class="flex items-center justify-between px-5 pb-8 pt-8">
<h2
:id="titleId"
class="text-[32px] font-semibold text-m-primary"
>
{{ title }}
</h2>
<button
v-if="showClose"
type="button"
aria-label="Fermer"
class="flex h-8 w-8 cursor-pointer items-center justify-center rounded-full transition-colors hover:bg-m-surface"
data-test="close-button"
@click="close"
>
<IconifyIcon
icon="mdi:close"
:width="24"
:height="24"
/>
</button>
</div>
<div
class="flex-1 overflow-y-auto px-5"
:class="twMerge('flex-1 overflow-y-auto px-5', bodyClass)"
data-test="body"
>
<slot />
</div>
@@ -62,8 +41,7 @@
</template>
<script setup lang="ts">
import { computed, ref, useAttrs, useId, watch } from 'vue'
import { Icon as IconifyIcon } from '@iconify/vue'
import { computed, ref, useAttrs, useId } from 'vue'
import { twMerge } from 'tailwind-merge'
defineOptions({ name: 'MalioDrawer', inheritAttrs: false })
@@ -72,68 +50,88 @@ const props = withDefaults(
defineProps<{
id?: string
modelValue?: boolean
title?: string
side?: 'right' | 'left'
showClose?: boolean
dismissable?: boolean
closeOnEscape?: boolean
ariaLabel?: string
drawerClass?: string
overlayClass?: string
headerClass?: string
bodyClass?: string
footerClass?: string
}>(),
{
id: '',
modelValue: undefined,
title: '',
side: 'right',
showClose: true,
dismissable: true,
closeOnEscape: true,
ariaLabel: '',
drawerClass: '',
overlayClass: '',
headerClass: '',
bodyClass: '',
footerClass: '',
},
)
const emit = defineEmits<{
(e: 'update:modelValue', value: boolean): void
(e: 'close'): void
}>()
const attrs = useAttrs()
const generatedId = useId()
const componentId = computed(() => props.id || `malio-drawer-${generatedId}`)
const titleId = computed(() => `${componentId.value}-title`)
const isControlled = computed(() => props.modelValue !== undefined)
const localValue = ref(false)
const isOpen = computed(() =>
isControlled.value ? props.modelValue! : localValue.value,
)
const isRendered = ref(isOpen.value)
watch(isOpen, (val) => {
if (val) isRendered.value = true
})
const panelRef = ref<HTMLElement | null>(null)
function close() {
if (!isControlled.value) {
localValue.value = false
}
if (!isControlled.value) localValue.value = false
emit('update:modelValue', false)
emit('close')
}
</script>
<style scoped>
.drawer-enter-active,
.drawer-leave-active {
.drawer-right-enter-active,
.drawer-right-leave-active,
.drawer-left-enter-active,
.drawer-left-leave-active {
transition: opacity 0.2s ease;
}
.drawer-enter-active > div:last-child,
.drawer-leave-active > div:last-child {
.drawer-right-enter-active > div:last-child,
.drawer-right-leave-active > div:last-child,
.drawer-left-enter-active > div:last-child,
.drawer-left-leave-active > div:last-child {
transition: transform 0.3s ease;
}
.drawer-enter-from,
.drawer-leave-to {
.drawer-right-enter-from,
.drawer-right-leave-to,
.drawer-left-enter-from,
.drawer-left-leave-to {
opacity: 0;
}
.drawer-enter-from > div:last-child,
.drawer-leave-to > div:last-child {
.drawer-right-enter-from > div:last-child,
.drawer-right-leave-to > div:last-child {
transform: translateX(100%);
}
.drawer-left-enter-from > div:last-child,
.drawer-left-leave-to > div:last-child {
transform: translateX(-100%);
}
</style>