feat : réécriture du squelette MalioDrawer (slots, side, contrôlé/non-contrôlé)
This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user