Skip to content

Commit

Permalink
Expose close and attemptClose methods as slot props from Dialog (
Browse files Browse the repository at this point in the history
…#464)

* Expose `close` and `attemptClose` methods as slot props from `Dialog`

* Add example

* Prevent close from firing twice

* Run format

* Refactor `close` actions to remove `close-attempt` and expose options

* Remove `reason`, simplify `close` impl, dispatch `open` when opened

* Remove button type

* Adjust changeset(s)
  • Loading branch information
brandonmcconnell committed Aug 14, 2024
1 parent 3791b63 commit 32e3169
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 37 deletions.
5 changes: 5 additions & 0 deletions .changeset/fast-actors-tease-1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'svelte-ux': patch
---

Refactor `close` method in `Dialog` and `Drawer` and expose as a slot prop across all props
5 changes: 5 additions & 0 deletions .changeset/fast-actors-tease-2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'svelte-ux': patch
---

Remove `close-attempt` on `Dialog` and `Drawer`
46 changes: 26 additions & 20 deletions packages/svelte-ux/src/lib/components/Dialog.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@
import Overlay from './Overlay.svelte';
import { getComponentClasses } from './theme.js';
const dispatch = createEventDispatcher();
const dispatch = createEventDispatcher<{
close: { open: boolean };
open: null;
}>();
export let open = false;
export let portal: PortalOptions = true;
Expand Down Expand Up @@ -45,29 +48,37 @@
} else if (actionsEl.contains(e.target)) {
// Close if action button clicked on (but not container). Can be disabled with `e.stopPropagation()`
if (e.target != actionsEl && !e.target.hasAttribute('slot')) {
open = false;
dispatch('close-attempt');
close();
}
}
} catch (err) {
console.error(err);
}
}
$: if (open === false) {
function close(options?: { force?: boolean }) {
const force = options?.force ?? false;
if (open) {
if (!persistent || force) {
open = false;
} else {
// attempted close of persistent dialog
dispatch('close', { open });
}
}
}
$: if (open) {
dispatch('open');
} else {
dispatch('close', { open });
}
</script>

<!-- Separate `{#if}` block works around Svelte 5 regression: https://github.com/sveltejs/svelte/issues/12440 -->
{#if open}
<Backdrop
on:click={() => {
if (!persistent) {
open = false;
}
dispatch('close-attempt');
}}
on:click={() => close()}
on:mouseup={(e) => {
// Do not allow event to reach Popover's on:mouseup (clickOutside)
e.stopPropagation();
Expand Down Expand Up @@ -95,12 +106,7 @@
if (e.key === 'Escape') {
// Do not allow event to reach Popover's on:keydown
e.stopPropagation();

if (!persistent) {
open = false;
}

dispatch('close-attempt');
close();
}
}}
use:portalAction={portal}
Expand Down Expand Up @@ -130,17 +136,17 @@
</Overlay>
{/if}

<slot name="header">
<slot name="header" {open} {close}>
{#if $$slots.title}
<div
class={cls('text-xl font-bold pt-4 pb-2 px-6', settingsClasses.title, classes.title)}
>
<slot name="title" />
<slot name="title" {open} {close} />
</div>
{/if}
</slot>

<slot />
<slot {open} {close} />

{#if $$slots.actions}
<div
Expand All @@ -151,7 +157,7 @@
)}
bind:this={actionsEl}
>
<slot name="actions" />
<slot name="actions" {open} {close} />
</div>
{/if}
</div>
Expand Down
37 changes: 24 additions & 13 deletions packages/svelte-ux/src/lib/components/Drawer.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@
import { cls } from '../utils/styles.js';
import { getComponentClasses } from './theme.js';
const dispatch = createEventDispatcher();
const dispatch = createEventDispatcher<{
change: { open: boolean };
close: { open: boolean };
open: null;
}>();
export let open = true;
export let portal: PortalOptions = true;
Expand All @@ -28,7 +32,21 @@
$: dispatch('change', { open });
$: if (open === false) {
function close(options?: { force?: boolean }) {
const force = options?.force ?? false;
if (open) {
if (!persistent || force) {
open = false;
} else {
// attempted close of persistent dialog
dispatch('close', { open });
}
}
}
$: if (open) {
dispatch('open');
} else {
dispatch('close', { open });
}
</script>
Expand All @@ -37,10 +55,7 @@
{#if open}
<Backdrop
on:click={(e) => {
if (!persistent) {
open = false;
}
dispatch('close-attempt');
close();
}}
on:mouseup={(e) => {
// Do not allow event to reach Popover's on:mouseup (clickOutside)
Expand Down Expand Up @@ -87,11 +102,7 @@
// Do not allow event to reach Popover's on:keydown
e.stopPropagation();

if (!persistent) {
open = false;
}

dispatch('close-attempt');
close();
}
}}
on:mouseup={(e) => {
Expand All @@ -108,7 +119,7 @@
</Overlay>
{/if}

<slot {open} />
<slot {open} {close} />

{#if $$slots.actions}
<div
Expand All @@ -118,7 +129,7 @@
classes.actions
)}
>
<slot name="actions" />
<slot name="actions" {open} {close} />
</div>
{/if}
</div>
Expand Down
48 changes: 45 additions & 3 deletions packages/svelte-ux/src/routes/docs/components/Dialog/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -185,11 +185,53 @@
<Preview>
<Toggle let:on={open} let:toggle let:toggleOff>
<Button on:click={toggle}>Show Dialog</Button>
<Dialog {open} on:close={toggleOff} persistent>
<Dialog {open} persistent>
<div slot="title">Are you sure you want to do that?</div>
<div slot="actions">
<Button variant="fill" color="primary">Yes</Button>
<Button>No</Button>
<Button variant="fill" color="primary" on:click={toggleOff}>Yes, close this dialog</Button>
<Button>No, keep this dialog open</Button>
</div>
</Dialog>
</Toggle>
</Preview>

<h2>With close slot prop</h2>

<Preview>
<Toggle let:on={open} let:toggle let:toggleOff>
<Button on:click={toggle}>Show Dialog</Button>
<Dialog
{open}
persistent
let:close
on:close={({ detail }) => {
if (detail.open) {
alert(
"Attempted to close persistent Dialog without using 'force'\n\nUse 'close({ force: true })' instead of Use 'close()' to close.\n\nDialog will remain open."
);
} else {
alert(
"Persistent Dialog forced close via 'close({ force: true })'.\n\nDialog will close."
);
toggleOff();
}
}}
>
<div class="p-5">
<div class="mb-4">
The <span
class="font-mono bg-primary-700/20 text-primary-500 font-medium px-1 py-0.5 rounded"
>close</span
> method is available on every slot.
</div>
<div class="grid gap-2">
<Button variant="fill" color="primary" on:click={() => close()}
>Attempt close: <code>close()</code></Button
>
<Button variant="fill" color="primary" on:click={() => close({ force: true })}
>Force close: <code>close({'{ force: true }'})</code></Button
>
</div>
</div>
</Dialog>
</Toggle>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,14 @@
<Toggle let:on={showDrawer} let:toggleOn={openDrawer} let:toggleOff={closeDrawer}>
<Drawer
open={showDrawer}
on:close-attempt={isChanged ? openConfirmation : closeDrawer}
persistent={isChanged}
on:close={({ detail }) => {
if (detail.open) {
openConfirmation();
} else {
closeDrawer();
}
}}
class="w-[400px]"
>
<div class="p-4">
Expand Down

0 comments on commit 32e3169

Please sign in to comment.