Skip to content

Commit 9c936ab

Browse files
committed
feat: migrate components in dialog to runes
1 parent f1783d9 commit 9c936ab

14 files changed

+192
-153
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ Svelte 5 Runes mode is being migrated to slowly. This is the todo list of compon
161161
- [x] Card
162162
- [x] Common
163163
- [ ] Data Table
164-
- [ ] Dialog
164+
- [x] Dialog
165165
- [x] Drawer
166166
- [ ] Image List
167167
- Inputs and Controls

packages/dialog/src/Dialog.svelte

Lines changed: 125 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<svelte:options runes={false} />
1+
<svelte:options runes />
22

33
<svelte:window
44
onresize={() => open && instance && instance.layout()}
@@ -21,38 +21,38 @@
2121
})}
2222
role="alertdialog"
2323
aria-modal="true"
24-
{...exclude($$restProps, ['container$', 'surface$'])}
24+
{...exclude(restProps, ['container$', 'surface$'])}
2525
onSMUIDialogOpening={(e) => {
2626
handleDialogOpening();
27-
$$restProps.onSMUIDialogOpening?.(e);
27+
restProps.onSMUIDialogOpening?.(e);
2828
}}
2929
onSMUIDialogOpened={(e) => {
3030
handleDialogOpened();
31-
$$restProps.onSMUIDialogOpened?.(e);
31+
restProps.onSMUIDialogOpened?.(e);
3232
}}
3333
onSMUIDialogClosed={(e) => {
3434
handleDialogClosed();
35-
$$restProps.onSMUIDialogClosed?.(e);
35+
restProps.onSMUIDialogClosed?.(e);
3636
}}
3737
onclick={(e) => {
3838
if (instance) {
3939
instance.handleClick(e);
4040
}
41-
$$restProps.onclick?.(e);
41+
restProps.onclick?.(e);
4242
}}
4343
onkeydown={(e) => {
4444
if (instance) {
4545
instance.handleKeydown(e);
4646
}
47-
$$restProps.onkeydown?.(e);
47+
restProps.onkeydown?.(e);
4848
}}
4949
>
5050
<div
5151
class={classMap({
5252
[container$class]: true,
5353
'mdc-dialog__container': true,
5454
})}
55-
{...prefixFilter($$restProps, 'container$')}
55+
{...prefixFilter(restProps, 'container$')}
5656
>
5757
<div
5858
class={classMap({
@@ -61,9 +61,9 @@
6161
})}
6262
role="alertdialog"
6363
aria-modal="true"
64-
{...prefixFilter($$restProps, 'surface$')}
64+
{...prefixFilter(restProps, 'surface$')}
6565
>
66-
<slot />
66+
{@render children?.()}
6767
{#if fullscreen}
6868
<div
6969
class="mdc-dialog__surface-scrim"
@@ -76,11 +76,12 @@
7676
<div class="mdc-dialog__scrim"></div>
7777
</div>
7878

79-
<slot name="over" />
79+
{@render over?.()}
8080

8181
<script lang="ts">
8282
import { MDCDialogFoundation, util } from '@material/dialog';
8383
import { focusTrap as domFocusTrap, ponyfill } from '@material/dom';
84+
import type { Snippet } from 'svelte';
8485
import { onMount, onDestroy, getContext, setContext } from 'svelte';
8586
import type { Writable } from 'svelte/store';
8687
import { writable } from 'svelte/store';
@@ -103,50 +104,99 @@
103104
const { closest, matches } = ponyfill;
104105
105106
type OwnProps = {
107+
/**
108+
* An array of Action or [Action, ActionProps] to be applied to the element.
109+
*/
106110
use?: ActionArray;
111+
/**
112+
* A space separated list of CSS classes.
113+
*/
107114
class?: string;
115+
/**
116+
* Whether the dialog is open.
117+
*/
108118
open?: boolean;
119+
/**
120+
* Whether this is a selection dialog.
121+
*/
109122
selection?: boolean;
123+
/**
124+
* What action the escape key should trigger.
125+
*
126+
* Set to an empty string to not trigger an action or close the dialog.
127+
*/
110128
escapeKeyAction?: string;
129+
/**
130+
* What action clicking the scrim should trigger.
131+
*
132+
* Set to an empty string to not trigger an action or close the dialog.
133+
*/
111134
scrimClickAction?: string;
135+
/**
136+
* Automatically stack buttons that are too wide on mobile screens.
137+
*/
112138
autoStackButtons?: boolean;
139+
/**
140+
* Style as a full screen dialog on mobile screens.
141+
*/
113142
fullscreen?: boolean;
114143
/**
144+
* Style as a floating sheet.
145+
*
115146
* Floating sheets are dialogs with a close icon button. Clicking the close
116147
* icon button closes the sheet. Having the close icon button is mutually
117148
* exclusive with having action bar buttons (e.g. cancel and OK buttons).
118149
* The icon button is absolutely positioned.
119150
*/
120151
sheet?: boolean;
152+
/**
153+
* Don't pad the content.
154+
*/
121155
noContentPadding?: boolean;
156+
/**
157+
* A space separated list of CSS classes.
158+
*/
122159
container$class?: string;
160+
/**
161+
* A space separated list of CSS classes.
162+
*/
123163
surface$class?: string;
164+
165+
children?: Snippet;
166+
/**
167+
* A spot to render another dialog over this one.
168+
*
169+
* According to the Material spec, you should only use this to put a choice
170+
* dialog over a fullscreen dialog.
171+
*/
172+
over?: Snippet;
124173
};
125-
type $$Props = OwnProps &
174+
let {
175+
use = [],
176+
class: className = '',
177+
open = $bindable(false),
178+
selection = false,
179+
escapeKeyAction = 'close',
180+
scrimClickAction = 'close',
181+
autoStackButtons = true,
182+
fullscreen = false,
183+
sheet = false,
184+
noContentPadding = false,
185+
container$class = '',
186+
surface$class = '',
187+
children,
188+
over,
189+
...restProps
190+
}: OwnProps &
126191
SmuiAttrs<'div', keyof OwnProps> & {
127192
[k in keyof SmuiElementPropMap['div'] as `container\$${k}`]?: SmuiElementPropMap['div'][k];
128193
} & {
129194
[k in keyof SmuiElementPropMap['div'] as `surface\$${k}`]?: SmuiElementPropMap['div'][k];
130-
};
131-
132-
// Remember to update $$Props if you add/remove/rename props.
133-
export let use: ActionArray = [];
134-
let className = '';
135-
export { className as class };
136-
export let open = false;
137-
export let selection = false;
138-
export let escapeKeyAction = 'close';
139-
export let scrimClickAction = 'close';
140-
export let autoStackButtons = true;
141-
export let fullscreen = false;
142-
export let sheet = false;
143-
export let noContentPadding = false;
144-
export let container$class = '';
145-
export let surface$class = '';
195+
} = $props();
146196
147197
let element: HTMLDivElement;
148-
let instance: MDCDialogFoundation;
149-
let internalClasses: { [k: string]: boolean } = {};
198+
let instance: MDCDialogFoundation | undefined = $state();
199+
let internalClasses: { [k: string]: boolean } = $state({});
150200
let focusTrap: domFocusTrap.FocusTrap;
151201
let actionButtonsReversed = writable(false);
152202
let aboveFullscreen = getContext<boolean | undefined>(
@@ -181,49 +231,61 @@
181231
setContext('SMUI:icon-button:context', 'dialog:sheet');
182232
}
183233
184-
$: if (instance && instance.getEscapeKeyAction() !== escapeKeyAction) {
185-
instance.setEscapeKeyAction(escapeKeyAction);
186-
}
234+
$effect(() => {
235+
if (instance && instance.getEscapeKeyAction() !== escapeKeyAction) {
236+
instance.setEscapeKeyAction(escapeKeyAction);
237+
}
238+
});
187239
188-
$: if (instance && instance.getScrimClickAction() !== scrimClickAction) {
189-
instance.setScrimClickAction(scrimClickAction);
190-
}
240+
$effect(() => {
241+
if (instance && instance.getScrimClickAction() !== scrimClickAction) {
242+
instance.setScrimClickAction(scrimClickAction);
243+
}
244+
});
191245
192-
$: if (instance && instance.getAutoStackButtons() !== autoStackButtons) {
193-
instance.setAutoStackButtons(autoStackButtons);
194-
}
246+
$effect(() => {
247+
if (instance && instance.getAutoStackButtons() !== autoStackButtons) {
248+
instance.setAutoStackButtons(autoStackButtons);
249+
}
250+
});
195251
196-
$: if (!autoStackButtons) {
197-
$actionButtonsReversed = true;
198-
}
252+
$effect(() => {
253+
if (!autoStackButtons) {
254+
$actionButtonsReversed = true;
255+
}
256+
});
199257
200258
if (addLayoutListener) {
201259
removeLayoutListener = addLayoutListener(layout);
202260
}
203261
204-
$: if (instance && instance.isOpen() !== open) {
205-
if (open) {
206-
instance.open({
207-
isAboveFullscreenDialog: !!aboveFullscreen,
208-
});
209-
} else {
210-
instance.close();
262+
$effect(() => {
263+
if (instance && instance.isOpen() !== open) {
264+
if (open) {
265+
instance.open({
266+
isAboveFullscreenDialog: !!aboveFullscreen,
267+
});
268+
} else {
269+
instance.close();
270+
}
211271
}
212-
}
272+
});
213273
214274
let previousAboveFullscreenShown = $aboveFullscreenShown;
215-
$: if (
216-
fullscreen &&
217-
instance &&
218-
previousAboveFullscreenShown !== $aboveFullscreenShown
219-
) {
220-
previousAboveFullscreenShown = $aboveFullscreenShown;
221-
if ($aboveFullscreenShown) {
222-
instance.showSurfaceScrim();
223-
} else {
224-
instance.hideSurfaceScrim();
275+
$effect(() => {
276+
if (
277+
fullscreen &&
278+
instance &&
279+
previousAboveFullscreenShown !== $aboveFullscreenShown
280+
) {
281+
previousAboveFullscreenShown = $aboveFullscreenShown;
282+
if ($aboveFullscreenShown) {
283+
instance.showSurfaceScrim();
284+
} else {
285+
instance.hideSurfaceScrim();
286+
}
225287
}
226-
}
288+
});
227289
228290
onMount(() => {
229291
focusTrap = new FocusTrap(element, {
@@ -299,7 +361,7 @@
299361
instance.init();
300362
301363
return () => {
302-
instance.destroy();
364+
instance?.destroy();
303365
};
304366
});
305367
@@ -377,7 +439,7 @@
377439
}
378440
379441
export function layout() {
380-
return instance.layout();
442+
return instance?.layout();
381443
}
382444
383445
export function getElement() {

packages/site/src/routes/demo/dialog/_DefaultFocus.svelte

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
<svelte:options runes={false} />
2-
31
<Dialog
42
bind:open
53
aria-labelledby="default-focus-title"
@@ -33,6 +31,6 @@
3331
import Dialog, { Title, Content, Actions, InitialFocus } from '@smui/dialog';
3432
import Button, { Label } from '@smui/button';
3533
36-
let open = false;
37-
let response = 'Nothing yet.';
34+
let open = $state(false);
35+
let response = $state('Nothing yet.');
3836
</script>

packages/site/src/routes/demo/dialog/_Event.svelte

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
<svelte:options runes={false} />
2-
31
<Dialog
42
bind:open
53
aria-labelledby="event-title"
@@ -30,8 +28,8 @@
3028
import Dialog, { Title, Content, Actions } from '@smui/dialog';
3129
import Button, { Label } from '@smui/button';
3230
33-
let open = false;
34-
let response = 'Nothing yet.';
31+
let open = $state(false);
32+
let response = $state('Nothing yet.');
3533
3634
function closeHandler(e: CustomEvent<{ action: string }>) {
3735
switch (e.detail.action) {

packages/site/src/routes/demo/dialog/_Fullscreen.svelte

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
<svelte:options runes={false} />
2-
31
<Dialog
42
bind:open
53
fullscreen
@@ -38,8 +36,8 @@
3836
import Button, { Label } from '@smui/button';
3937
import LoremIpsum from '$lib/LoremIpsum.svelte';
4038
41-
let open = false;
42-
let response = 'Nothing yet.';
39+
let open = $state(false);
40+
let response = $state('Nothing yet.');
4341
4442
function closeHandler(e: CustomEvent<{ action: string }>) {
4543
switch (e.detail.action) {

packages/site/src/routes/demo/dialog/_LargeScroll.svelte

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
<svelte:options runes={false} />
2-
31
<Dialog
42
bind:open
53
aria-labelledby="large-scroll-title"
@@ -28,5 +26,5 @@
2826
import Button, { Label } from '@smui/button';
2927
import LoremIpsum from '$lib/LoremIpsum.svelte';
3028
31-
let open = false;
29+
let open = $state(false);
3230
</script>

packages/site/src/routes/demo/dialog/_List.svelte

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
<svelte:options runes={false} />
2-
31
<Dialog
42
bind:open
53
selection
@@ -36,6 +34,6 @@
3634
import Button, { Label } from '@smui/button';
3735
import List, { Item, Text } from '@smui/list';
3836
39-
let open = false;
40-
let clicked: string | number = 'Nothing yet.';
37+
let open = $state(false);
38+
let clicked: string | number = $state('Nothing yet.');
4139
</script>

0 commit comments

Comments
 (0)