|
1 | 1 | <script lang="ts">
|
2 | 2 | import type { DialogRootProps, DialogRootEmits, DialogContentProps, DialogContentEmits } from 'reka-ui'
|
| 3 | +import { DialogRoot, DialogTrigger, DialogPortal, DialogOverlay, DialogContent, DialogTitle, DialogDescription, DialogClose, VisuallyHidden, useForwardPropsEmits } from 'reka-ui' |
3 | 4 | import type { AppConfig } from '@nuxt/schema'
|
4 | 5 | import theme from '#build/ui/modal'
|
5 | 6 | import type { ButtonProps } from '../types'
|
6 | 7 | import type { EmitsToProps, ComponentConfig } from '../types/utils'
|
| 8 | +import ModalContext from './ModalContext.vue' |
7 | 9 |
|
8 | 10 | type Modal = ComponentConfig<typeof theme, AppConfig, 'modal'>
|
9 | 11 |
|
@@ -61,19 +63,18 @@ export interface ModalEmits extends DialogRootEmits {
|
61 | 63 |
|
62 | 64 | export interface ModalSlots {
|
63 | 65 | default(props: { open: boolean }): any
|
64 |
| - content(props?: {}): any |
65 |
| - header(props?: {}): any |
66 |
| - title(props?: {}): any |
67 |
| - description(props?: {}): any |
| 66 | + content(props: { close: () => void }): any |
| 67 | + header(props: { close: () => void }): any |
| 68 | + title(props: { close: () => void }): any |
| 69 | + description(props: { close: () => void }): any |
68 | 70 | close(props: { ui: { [K in keyof Required<Modal['slots']>]: (props?: Record<string, any>) => string } }): any
|
69 |
| - body(props?: {}): any |
70 |
| - footer(props?: {}): any |
| 71 | + body(props: { close: () => void }): any |
| 72 | + footer(props: { close: () => void }): any |
71 | 73 | }
|
72 | 74 | </script>
|
73 | 75 |
|
74 | 76 | <script setup lang="ts">
|
75 | 77 | import { computed, toRef } from 'vue'
|
76 |
| -import { DialogRoot, DialogTrigger, DialogPortal, DialogOverlay, DialogContent, DialogTitle, DialogDescription, DialogClose, VisuallyHidden, useForwardPropsEmits } from 'reka-ui' |
77 | 78 | import { reactivePick } from '@vueuse/core'
|
78 | 79 | import { useAppConfig } from '#imports'
|
79 | 80 | import { useLocale } from '../composables/useLocale'
|
@@ -126,71 +127,73 @@ const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.modal || {})
|
126 | 127 |
|
127 | 128 | <template>
|
128 | 129 | <DialogRoot v-slot="{ open }" v-bind="rootProps">
|
129 |
| - <DialogTrigger v-if="!!slots.default" as-child :class="props.class"> |
130 |
| - <slot :open="open" /> |
131 |
| - </DialogTrigger> |
132 |
| - |
133 |
| - <DialogPortal v-bind="portalProps"> |
134 |
| - <DialogOverlay v-if="overlay" :class="ui.overlay({ class: props.ui?.overlay })" /> |
135 |
| - |
136 |
| - <DialogContent :class="ui.content({ class: [!slots.default && props.class, props.ui?.content] })" v-bind="contentProps" @after-enter="emits('after:enter')" @after-leave="emits('after:leave')" v-on="contentEvents"> |
137 |
| - <VisuallyHidden v-if="!!slots.content && ((title || !!slots.title) || (description || !!slots.description))"> |
138 |
| - <DialogTitle v-if="title || !!slots.title"> |
139 |
| - <slot name="title"> |
140 |
| - {{ title }} |
141 |
| - </slot> |
142 |
| - </DialogTitle> |
143 |
| - |
144 |
| - <DialogDescription v-if="description || !!slots.description"> |
145 |
| - <slot name="description"> |
146 |
| - {{ description }} |
147 |
| - </slot> |
148 |
| - </DialogDescription> |
149 |
| - </VisuallyHidden> |
150 |
| - |
151 |
| - <slot name="content"> |
152 |
| - <div v-if="!!slots.header || (title || !!slots.title) || (description || !!slots.description) || (close || !!slots.close)" :class="ui.header({ class: props.ui?.header })"> |
153 |
| - <slot name="header"> |
154 |
| - <div :class="ui.wrapper({ class: props.ui?.wrapper })"> |
155 |
| - <DialogTitle v-if="title || !!slots.title" :class="ui.title({ class: props.ui?.title })"> |
156 |
| - <slot name="title"> |
157 |
| - {{ title }} |
158 |
| - </slot> |
159 |
| - </DialogTitle> |
160 |
| - |
161 |
| - <DialogDescription v-if="description || !!slots.description" :class="ui.description({ class: props.ui?.description })"> |
162 |
| - <slot name="description"> |
163 |
| - {{ description }} |
| 130 | + <ModalContext v-slot="{ onOpenChange }"> |
| 131 | + <DialogTrigger v-if="!!slots.default" as-child :class="props.class"> |
| 132 | + <slot :open="open" :close="() => onOpenChange(false)" /> |
| 133 | + </DialogTrigger> |
| 134 | + |
| 135 | + <DialogPortal v-bind="portalProps"> |
| 136 | + <DialogOverlay v-if="overlay" :class="ui.overlay({ class: props.ui?.overlay })" /> |
| 137 | + |
| 138 | + <DialogContent :class="ui.content({ class: [!slots.default && props.class, props.ui?.content] })" v-bind="contentProps" @after-enter="emits('after:enter')" @after-leave="emits('after:leave')" v-on="contentEvents"> |
| 139 | + <VisuallyHidden v-if="!!slots.content && ((title || !!slots.title) || (description || !!slots.description))"> |
| 140 | + <DialogTitle v-if="title || !!slots.title"> |
| 141 | + <slot name="title" :close="() => onOpenChange(false)"> |
| 142 | + {{ title }} |
| 143 | + </slot> |
| 144 | + </DialogTitle> |
| 145 | + |
| 146 | + <DialogDescription v-if="description || !!slots.description"> |
| 147 | + <slot name="description" :close="() => onOpenChange(false)"> |
| 148 | + {{ description }} |
| 149 | + </slot> |
| 150 | + </DialogDescription> |
| 151 | + </VisuallyHidden> |
| 152 | + |
| 153 | + <slot name="content" :close="() => onOpenChange(false)"> |
| 154 | + <div v-if="!!slots.header || (title || !!slots.title) || (description || !!slots.description) || (close || !!slots.close)" :class="ui.header({ class: props.ui?.header })"> |
| 155 | + <slot name="header" :close="() => onOpenChange(false)"> |
| 156 | + <div :class="ui.wrapper({ class: props.ui?.wrapper })"> |
| 157 | + <DialogTitle v-if="title || !!slots.title" :class="ui.title({ class: props.ui?.title })"> |
| 158 | + <slot name="title" :close="() => onOpenChange(false)"> |
| 159 | + {{ title }} |
| 160 | + </slot> |
| 161 | + </DialogTitle> |
| 162 | + |
| 163 | + <DialogDescription v-if="description || !!slots.description" :class="ui.description({ class: props.ui?.description })"> |
| 164 | + <slot name="description" :close="() => onOpenChange(false)"> |
| 165 | + {{ description }} |
| 166 | + </slot> |
| 167 | + </DialogDescription> |
| 168 | + </div> |
| 169 | + |
| 170 | + <DialogClose v-if="close || !!slots.close" as-child> |
| 171 | + <slot name="close" :ui="ui"> |
| 172 | + <UButton |
| 173 | + v-if="close" |
| 174 | + :icon="closeIcon || appConfig.ui.icons.close" |
| 175 | + size="md" |
| 176 | + color="neutral" |
| 177 | + variant="ghost" |
| 178 | + :aria-label="t('modal.close')" |
| 179 | + v-bind="(typeof close === 'object' ? close as Partial<ButtonProps> : {})" |
| 180 | + :class="ui.close({ class: props.ui?.close })" |
| 181 | + /> |
164 | 182 | </slot>
|
165 |
| - </DialogDescription> |
166 |
| - </div> |
167 |
| - |
168 |
| - <DialogClose v-if="close || !!slots.close" as-child> |
169 |
| - <slot name="close" :ui="ui"> |
170 |
| - <UButton |
171 |
| - v-if="close" |
172 |
| - :icon="closeIcon || appConfig.ui.icons.close" |
173 |
| - size="md" |
174 |
| - color="neutral" |
175 |
| - variant="ghost" |
176 |
| - :aria-label="t('modal.close')" |
177 |
| - v-bind="(typeof close === 'object' ? close as Partial<ButtonProps> : {})" |
178 |
| - :class="ui.close({ class: props.ui?.close })" |
179 |
| - /> |
180 |
| - </slot> |
181 |
| - </DialogClose> |
182 |
| - </slot> |
183 |
| - </div> |
184 |
| - |
185 |
| - <div v-if="!!slots.body" :class="ui.body({ class: props.ui?.body })"> |
186 |
| - <slot name="body" /> |
187 |
| - </div> |
188 |
| - |
189 |
| - <div v-if="!!slots.footer" :class="ui.footer({ class: props.ui?.footer })"> |
190 |
| - <slot name="footer" /> |
191 |
| - </div> |
192 |
| - </slot> |
193 |
| - </DialogContent> |
194 |
| - </DialogPortal> |
| 183 | + </DialogClose> |
| 184 | + </slot> |
| 185 | + </div> |
| 186 | + |
| 187 | + <div v-if="!!slots.body" :class="ui.body({ class: props.ui?.body })"> |
| 188 | + <slot name="body" :close="() => onOpenChange(false)" /> |
| 189 | + </div> |
| 190 | + |
| 191 | + <div v-if="!!slots.footer" :class="ui.footer({ class: props.ui?.footer })"> |
| 192 | + <slot name="footer" :close="() => onOpenChange(false)" /> |
| 193 | + </div> |
| 194 | + </slot> |
| 195 | + </DialogContent> |
| 196 | + </DialogPortal> |
| 197 | + </ModalContext> |
195 | 198 | </DialogRoot>
|
196 | 199 | </template>
|
0 commit comments