Skip to content

Commit 9b2a0cd

Browse files
committed
feat(Modal,Slideover): implement programmatic close method and update documentation
1 parent ca507c6 commit 9b2a0cd

File tree

8 files changed

+118
-22
lines changed

8 files changed

+118
-22
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<template>
2+
<UModal
3+
title="Modal with close method"
4+
description="This is useful when you want to close the modal from within the content without creating a new ref."
5+
>
6+
<UButton label="Open" color="neutral" variant="subtle" />
7+
8+
<template #body="{ close }">
9+
<div class="space-y-4">
10+
<p>You can close this modal using the close method exposed in the slots.</p>
11+
<UButton label="Close Modal" color="primary" @click="close" />
12+
</div>
13+
</template>
14+
15+
<template #footer="{ close }">
16+
<UButton label="Cancel" color="neutral" variant="outline" @click="close" />
17+
<UButton label="Submit" color="neutral" />
18+
</template>
19+
</UModal>
20+
</template>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<template>
2+
<USlideover
3+
title="Sliderover with close method"
4+
description="This is useful when you want to close the slider over from within the content without creating a new ref."
5+
>
6+
<UButton label="Open" color="neutral" variant="subtle" />
7+
8+
<template #body="{ close }">
9+
<div class="space-y-4">
10+
<p>You can close this slider over using the close method exposed in the slots.</p>
11+
<UButton label="Close Slideover" color="primary" @click="close" />
12+
</div>
13+
</template>
14+
15+
<template #footer="{ close }">
16+
<UButton label="Cancel" color="neutral" variant="outline" @click="close" />
17+
<UButton label="Submit" color="neutral" />
18+
</template>
19+
</USlideover>
20+
</template>

docs/content/3.components/modal.md

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -305,13 +305,13 @@ slots:
305305

306306
### Programmatic usage
307307

308-
You can use the [`useOverlay`](/composables/use-overlay) composable to open a Modal programatically.
308+
You can use the [`useOverlay`](/composables/use-overlay) composable to open a Modal programmatically.
309309

310310
::warning
311311
Make sure to wrap your app with the [`App`](/components/app) component which uses the [`OverlayProvider`](https://github.com/nuxt/ui/blob/v3/src/runtime/components/OverlayProvider.vue) component.
312312
::
313313

314-
First, create a modal component that will be opened programatically:
314+
First, create a modal component that will be opened programmatically:
315315

316316
::component-example
317317
---
@@ -347,6 +347,22 @@ name: 'modal-nested-example'
347347
---
348348
::
349349

350+
### Using the close method
351+
352+
All slots in the Modal component receive a `close` method that can be used to programmatically close the modal from within the content.
353+
354+
::component-example
355+
---
356+
name: 'modal-programmatic-close'
357+
props:
358+
class: 'px-4'
359+
---
360+
::
361+
362+
::tip
363+
The `close` method is available in all slots: `content`, `header`, `body`, and `footer`.
364+
::
365+
350366
### With footer slot
351367

352368
Use the `#footer` slot to add content after the Modal's body.

docs/content/3.components/slideover.md

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -304,13 +304,13 @@ slots:
304304

305305
### Programmatic usage
306306

307-
You can use the [`useOverlay`](/composables/use-overlay) composable to open a Slideover programatically.
307+
You can use the [`useOverlay`](/composables/use-overlay) composable to open a Slideover programmatically.
308308

309309
::warning
310310
Make sure to wrap your app with the [`App`](/components/app) component which uses the [`OverlayProvider`](https://github.com/nuxt/ui/blob/v3/src/runtime/components/OverlayProvider.vue) component.
311311
::
312312

313-
First, create a slideover component that will be opened programatically:
313+
First, create a slideover component that will be opened programmatically:
314314

315315
::component-example
316316
---
@@ -346,6 +346,22 @@ name: 'slideover-nested-example'
346346
---
347347
::
348348

349+
### Using the close method
350+
351+
All slots in the SlideOver component receive a `close` method that can be used to programmatically close the modal from within the content.
352+
353+
::component-example
354+
---
355+
name: 'modal-programmatic-close'
356+
props:
357+
class: 'px-4'
358+
---
359+
::
360+
361+
::tip
362+
The `close` method is available in all slots: `content`, `header`, `body`, and `footer`.
363+
::
364+
349365
### With footer slot
350366

351367
Use the `#footer` slot to add content after the Slideover's body.

playground/app/pages/components/modal.vue

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,5 +69,13 @@ function openModal() {
6969
</UModal>
7070

7171
<UButton label="Open programmatically" color="neutral" variant="outline" @click="openModal" />
72+
73+
<UModal title="First modal">
74+
<UButton color="neutral" variant="outline" label="Close with scoped slot close" />
75+
76+
<template #footer="{ close }">
77+
<UButton label="Close with scoped slot close" @click="close" />
78+
</template>
79+
</UModal>
7280
</div>
7381
</template>

playground/app/pages/components/slideover.vue

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,5 +125,21 @@ function openSlideover() {
125125
</USlideover>
126126

127127
<UButton label="Open programmatically" color="neutral" variant="outline" @click="openSlideover" />
128+
129+
<USlideover title="Slideover with scoped slot close" description="This slideover has a scoped slot close that can be used to close the slideover from within the content.">
130+
<UButton color="neutral" variant="subtle" label="Open with scoped slot close" />
131+
132+
<template #header="{ close }">
133+
<UButton label="Close with scoped slot close" @click="close" />
134+
</template>
135+
136+
<template #body="{ close }">
137+
<UButton label="Close with scoped slot close" @click="close" />
138+
</template>
139+
140+
<template #footer="{ close }">
141+
<UButton label="Close with scoped slot close" @click="close" />
142+
</template>
143+
</USlideover>
128144
</div>
129145
</template>

src/runtime/components/Modal.vue

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,13 @@ export interface ModalEmits extends DialogRootEmits {
6161
6262
export interface ModalSlots {
6363
default(props: { open: boolean }): any
64-
content(props?: {}): any
65-
header(props?: {}): any
64+
content(props: { close: () => void }): any
65+
header(props: { close: () => void }): any
6666
title(props?: {}): any
6767
description(props?: {}): any
6868
close(props: { ui: { [K in keyof Required<Modal['slots']>]: (props?: Record<string, any>) => string } }): any
69-
body(props?: {}): any
70-
footer(props?: {}): any
69+
body(props: { close: () => void }): any
70+
footer(props: { close: () => void }): any
7171
}
7272
</script>
7373

@@ -125,7 +125,7 @@ const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.modal || {})
125125
</script>
126126

127127
<template>
128-
<DialogRoot v-slot="{ open }" v-bind="rootProps">
128+
<DialogRoot v-slot="{ open, close: closeDialog }" v-bind="rootProps">
129129
<DialogTrigger v-if="!!slots.default" as-child :class="props.class">
130130
<slot :open="open" />
131131
</DialogTrigger>
@@ -148,9 +148,9 @@ const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.modal || {})
148148
</DialogDescription>
149149
</VisuallyHidden>
150150

151-
<slot name="content">
151+
<slot name="content" :close="closeDialog">
152152
<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">
153+
<slot name="header" :close="closeDialog">
154154
<div :class="ui.wrapper({ class: props.ui?.wrapper })">
155155
<DialogTitle v-if="title || !!slots.title" :class="ui.title({ class: props.ui?.title })">
156156
<slot name="title">
@@ -183,11 +183,11 @@ const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.modal || {})
183183
</div>
184184

185185
<div v-if="!!slots.body" :class="ui.body({ class: props.ui?.body })">
186-
<slot name="body" />
186+
<slot name="body" :close="closeDialog" />
187187
</div>
188188

189189
<div v-if="!!slots.footer" :class="ui.footer({ class: props.ui?.footer })">
190-
<slot name="footer" />
190+
<slot name="footer" :close="closeDialog" />
191191
</div>
192192
</slot>
193193
</DialogContent>

src/runtime/components/Slideover.vue

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,13 @@ export interface SlideoverEmits extends DialogRootEmits {
6161
6262
export interface SlideoverSlots {
6363
default(props: { open: boolean }): any
64-
content(props?: {}): any
65-
header(props?: {}): any
64+
content(props: { close: () => void }): any
65+
header(props: { close: () => void }): any
6666
title(props?: {}): any
6767
description(props?: {}): any
6868
close(props: { ui: { [K in keyof Required<Slideover['slots']>]: (props?: Record<string, any>) => string } }): any
69-
body(props?: {}): any
70-
footer(props?: {}): any
69+
body(props: { close: () => void }): any
70+
footer(props: { close: () => void }): any
7171
}
7272
</script>
7373

@@ -125,7 +125,7 @@ const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.slideover ||
125125
</script>
126126

127127
<template>
128-
<DialogRoot v-slot="{ open }" v-bind="rootProps">
128+
<DialogRoot v-slot="{ open, close: closeDialog }" v-bind="rootProps">
129129
<DialogTrigger v-if="!!slots.default" as-child :class="props.class">
130130
<slot :open="open" />
131131
</DialogTrigger>
@@ -155,9 +155,9 @@ const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.slideover ||
155155
</DialogDescription>
156156
</VisuallyHidden>
157157

158-
<slot name="content">
158+
<slot name="content" :close="closeDialog">
159159
<div v-if="!!slots.header || (title || !!slots.title) || (description || !!slots.description) || (close || !!slots.close)" :class="ui.header({ class: props.ui?.header })">
160-
<slot name="header">
160+
<slot name="header" :close="closeDialog">
161161
<div :class="ui.wrapper({ class: props.ui?.wrapper })">
162162
<DialogTitle v-if="title || !!slots.title" :class="ui.title({ class: props.ui?.title })">
163163
<slot name="title">
@@ -190,11 +190,11 @@ const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.slideover ||
190190
</div>
191191

192192
<div :class="ui.body({ class: props.ui?.body })">
193-
<slot name="body" />
193+
<slot name="body" :close="closeDialog" />
194194
</div>
195195

196196
<div v-if="!!slots.footer" :class="ui.footer({ class: props.ui?.footer })">
197-
<slot name="footer" />
197+
<slot name="footer" :close="closeDialog" />
198198
</div>
199199
</slot>
200200
</DialogContent>

0 commit comments

Comments
 (0)