Skip to content

Commit 5835eb5

Browse files
feat(Modal/Slideover): add close method in slots (#4219)
Co-authored-by: Benjamin Canac <[email protected]>
1 parent ca507c6 commit 5835eb5

File tree

8 files changed

+64
-38
lines changed

8 files changed

+64
-38
lines changed

docs/app/components/content/examples/modal/ModalFooterSlotExample.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ const open = ref(false)
1010
<Placeholder class="h-48" />
1111
</template>
1212

13-
<template #footer>
14-
<UButton label="Cancel" color="neutral" variant="outline" @click="open = false" />
13+
<template #footer="{ close }">
14+
<UButton label="Cancel" color="neutral" variant="outline" @click="close" />
1515
<UButton label="Submit" color="neutral" />
1616
</template>
1717
</UModal>

docs/app/components/content/examples/slideover/SlideoverFooterSlotExample.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ const open = ref(false)
1010
<Placeholder class="h-full" />
1111
</template>
1212

13-
<template #footer>
14-
<UButton label="Cancel" color="neutral" variant="outline" @click="open = false" />
13+
<template #footer="{ close }">
14+
<UButton label="Cancel" color="neutral" variant="outline" @click="close" />
1515
<UButton label="Submit" color="neutral" />
1616
</template>
1717
</USlideover>

docs/content/3.components/modal.md

Lines changed: 2 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
---

docs/content/3.components/slideover.md

Lines changed: 2 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
---

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: 16 additions & 15 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
68-
close(props: { ui: { [K in keyof Required<Modal['slots']>]: (props?: Record<string, any>) => string } }): any
69-
body(props?: {}): any
70-
footer(props?: {}): any
68+
close(props: { close: () => void, ui: { [K in keyof Required<Modal['slots']>]: (props?: Record<string, any>) => string } }): any
69+
body(props: { close: () => void }): any
70+
footer(props: { close: () => void }): any
7171
}
7272
</script>
7373

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

127+
<!-- eslint-disable vue/no-template-shadow -->
127128
<template>
128-
<DialogRoot v-slot="{ open }" v-bind="rootProps">
129+
<DialogRoot v-slot="{ open, close }" v-bind="rootProps">
129130
<DialogTrigger v-if="!!slots.default" as-child :class="props.class">
130131
<slot :open="open" />
131132
</DialogTrigger>
@@ -148,9 +149,9 @@ const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.modal || {})
148149
</DialogDescription>
149150
</VisuallyHidden>
150151

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">
152+
<slot name="content" :close="close">
153+
<div v-if="!!slots.header || (title || !!slots.title) || (description || !!slots.description) || (props.close || !!slots.close)" :class="ui.header({ class: props.ui?.header })">
154+
<slot name="header" :close="close">
154155
<div :class="ui.wrapper({ class: props.ui?.wrapper })">
155156
<DialogTitle v-if="title || !!slots.title" :class="ui.title({ class: props.ui?.title })">
156157
<slot name="title">
@@ -165,16 +166,16 @@ const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.modal || {})
165166
</DialogDescription>
166167
</div>
167168

168-
<DialogClose v-if="close || !!slots.close" as-child>
169-
<slot name="close" :ui="ui">
169+
<DialogClose v-if="props.close || !!slots.close" as-child>
170+
<slot name="close" :close="close" :ui="ui">
170171
<UButton
171-
v-if="close"
172+
v-if="props.close"
172173
:icon="closeIcon || appConfig.ui.icons.close"
173174
size="md"
174175
color="neutral"
175176
variant="ghost"
176177
:aria-label="t('modal.close')"
177-
v-bind="(typeof close === 'object' ? close as Partial<ButtonProps> : {})"
178+
v-bind="(typeof props.close === 'object' ? props.close as Partial<ButtonProps> : {})"
178179
:class="ui.close({ class: props.ui?.close })"
179180
/>
180181
</slot>
@@ -183,11 +184,11 @@ const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.modal || {})
183184
</div>
184185

185186
<div v-if="!!slots.body" :class="ui.body({ class: props.ui?.body })">
186-
<slot name="body" />
187+
<slot name="body" :close="close" />
187188
</div>
188189

189190
<div v-if="!!slots.footer" :class="ui.footer({ class: props.ui?.footer })">
190-
<slot name="footer" />
191+
<slot name="footer" :close="close" />
191192
</div>
192193
</slot>
193194
</DialogContent>

src/runtime/components/Slideover.vue

Lines changed: 16 additions & 15 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
68-
close(props: { ui: { [K in keyof Required<Slideover['slots']>]: (props?: Record<string, any>) => string } }): any
69-
body(props?: {}): any
70-
footer(props?: {}): any
68+
close(props: { close: () => void, ui: { [K in keyof Required<Slideover['slots']>]: (props?: Record<string, any>) => string } }): any
69+
body(props: { close: () => void }): any
70+
footer(props: { close: () => void }): any
7171
}
7272
</script>
7373

@@ -124,8 +124,9 @@ const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.slideover ||
124124
}))
125125
</script>
126126

127+
<!-- eslint-disable vue/no-template-shadow -->
127128
<template>
128-
<DialogRoot v-slot="{ open }" v-bind="rootProps">
129+
<DialogRoot v-slot="{ open, close }" v-bind="rootProps">
129130
<DialogTrigger v-if="!!slots.default" as-child :class="props.class">
130131
<slot :open="open" />
131132
</DialogTrigger>
@@ -155,9 +156,9 @@ const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.slideover ||
155156
</DialogDescription>
156157
</VisuallyHidden>
157158

158-
<slot name="content">
159-
<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">
159+
<slot name="content" :close="close">
160+
<div v-if="!!slots.header || (title || !!slots.title) || (description || !!slots.description) || (props.close || !!slots.close)" :class="ui.header({ class: props.ui?.header })">
161+
<slot name="header" :close="close">
161162
<div :class="ui.wrapper({ class: props.ui?.wrapper })">
162163
<DialogTitle v-if="title || !!slots.title" :class="ui.title({ class: props.ui?.title })">
163164
<slot name="title">
@@ -172,16 +173,16 @@ const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.slideover ||
172173
</DialogDescription>
173174
</div>
174175

175-
<DialogClose v-if="close || !!slots.close" as-child>
176-
<slot name="close" :ui="ui">
176+
<DialogClose v-if="props.close || !!slots.close" as-child>
177+
<slot name="close" :close="close" :ui="ui">
177178
<UButton
178-
v-if="close"
179+
v-if="props.close"
179180
:icon="closeIcon || appConfig.ui.icons.close"
180181
size="md"
181182
color="neutral"
182183
variant="ghost"
183184
:aria-label="t('slideover.close')"
184-
v-bind="(typeof close === 'object' ? close as Partial<ButtonProps> : {})"
185+
v-bind="(typeof props.close === 'object' ? props.close as Partial<ButtonProps> : {})"
185186
:class="ui.close({ class: props.ui?.close })"
186187
/>
187188
</slot>
@@ -190,11 +191,11 @@ const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.slideover ||
190191
</div>
191192

192193
<div :class="ui.body({ class: props.ui?.body })">
193-
<slot name="body" />
194+
<slot name="body" :close="close" />
194195
</div>
195196

196197
<div v-if="!!slots.footer" :class="ui.footer({ class: props.ui?.footer })">
197-
<slot name="footer" />
198+
<slot name="footer" :close="close" />
198199
</div>
199200
</slot>
200201
</DialogContent>

0 commit comments

Comments
 (0)