forked from
npmx.dev/npmx.dev
[READ-ONLY]
a fast, modern browser for the npm registry
1<script setup lang="ts">
2const props = defineProps<{
3 modalTitle: string
4}>()
5
6const dialogRef = useTemplateRef('dialogRef')
7
8const emit = defineEmits<{
9 (e: 'transitioned'): void
10}>()
11
12const modalTitleId = computed(() => {
13 const id = getCurrentInstance()?.attrs.id
14 return id ? `${id}-title` : undefined
15})
16
17function handleModalClose() {
18 dialogRef.value?.close()
19}
20
21/**
22 * Emits `transitioned` once the dialog has finished its open opacity transition.
23 * This is used by consumers that need to run layout-sensitive logic (for example
24 * dispatching a resize) only after the modal is fully displayed.
25 */
26function onDialogTransitionEnd(event: TransitionEvent) {
27 const el = dialogRef.value
28 if (!el) return
29 if (!el.open) return
30 if (event.target !== el) return
31 if (event.propertyName !== 'opacity') return
32 emit('transitioned')
33}
34
35defineExpose({
36 showModal: () => dialogRef.value?.showModal(),
37 close: () => dialogRef.value?.close(),
38})
39</script>
40
41<template>
42 <Teleport to="body">
43 <dialog
44 ref="dialogRef"
45 closedby="any"
46 class="w-[calc(100%-2rem)] bg-bg border border-border rounded-lg shadow-xl max-h-[90vh] overflow-y-auto overscroll-contain m-0 m-auto p-6 text-fg focus-visible:outline focus-visible:outline-accent/70"
47 :aria-labelledby="modalTitleId"
48 v-bind="$attrs"
49 @transitionend="onDialogTransitionEnd"
50 >
51 <!-- Modal top header section -->
52 <div class="flex items-center justify-between mb-6">
53 <h2 :id="modalTitleId" class="font-mono text-lg font-medium">
54 {{ modalTitle }}
55 </h2>
56 <ButtonBase
57 type="button"
58 :aria-label="$t('common.close')"
59 @click="handleModalClose"
60 classicon="i-lucide:x"
61 />
62 </div>
63 <!-- Modal body content -->
64 <slot />
65 </dialog>
66 </Teleport>
67</template>
68
69<style scoped>
70/* Backdrop styling when any of the modals are open */
71dialog:modal::backdrop {
72 @apply bg-bg-elevated/70;
73}
74
75dialog::backdrop {
76 pointer-events: none;
77}
78
79/* Modal transition styles */
80dialog {
81 opacity: 0;
82 transition: opacity 200ms ease;
83 transition-behavior: allow-discrete;
84}
85
86dialog:modal {
87 opacity: 1;
88 transition: opacity 200ms ease;
89 transition-behavior: allow-discrete;
90}
91
92@starting-style {
93 dialog:modal {
94 opacity: 0;
95 }
96}
97</style>