Skip to content

Commit 30ad599

Browse files
committed
feat: migrate components in snackbar to runes
1 parent 67646a4 commit 30ad599

File tree

11 files changed

+176
-110
lines changed

11 files changed

+176
-110
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,8 +192,8 @@ Svelte 5 Runes mode is being migrated to slowly. This is the todo list of compon
192192
- Progress Indicators
193193
- [x] Circular Progress
194194
- [x] Linear Progress
195-
- [ ] Snackbar
196-
- [ ] Kitchen
195+
- [x] Snackbar
196+
- [x] Kitchen
197197
- Tabs
198198
- [x] Tab
199199
- [x] Tab Bar

packages/site/src/routes/demo/snackbar/+page.svelte

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@
2323

2424
<Demo component={DynamicText} file="snackbar/_DynamicText.svelte">
2525
Using dynamic text
26+
{#snippet subtitle()}
27+
This demo is set to not close automatically, so you can more easily test
28+
it.
29+
{/snippet}
2630
</Demo>
2731

2832
<Demo

packages/site/src/routes/demo/snackbar/_Colors.svelte

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
<svelte:options runes={false} />
2-
31
<Snackbar bind:this={snackbarSuccess} class="demo-success">
42
<Label
53
>That thing you tried to do actually worked, if you can believe it!</Label

packages/site/src/routes/demo/snackbar/_DynamicText.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
<Snackbar bind:this={snackbar} labelText={text} timeoutMs={-1}>
42
<Label />
53
<Actions>
@@ -20,5 +18,5 @@
2018
import Textfield from '@smui/textfield';
2119
2220
let snackbar: Snackbar;
23-
let text = 'This is a snackbar with dynamic text.';
21+
let text = $state('This is a snackbar with dynamic text.');
2422
</script>

packages/site/src/routes/demo/snackbar/_Kitchen.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
<Kitchen bind:this={kitchen} dismiss$class="material-icons" />
42

53
<Button onclick={pushToKitchen}><Label>Push a New Snackbar</Label></Button>
@@ -16,8 +14,8 @@
1614
import Button, { Label } from '@smui/button';
1715
1816
let kitchen: Kitchen;
19-
let reason = 'nothing yet';
20-
let action = 'nothing yet';
17+
let reason = $state('nothing yet');
18+
let action = $state('nothing yet');
2119
2220
function pushToKitchen() {
2321
kitchen.push({

packages/site/src/routes/demo/snackbar/_KitchenSvg.svelte

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
<svelte:options runes={false} />
2-
31
<Kitchen bind:this={kitchen} dismiss$tag="svg" dismiss$viewBox="0 0 24 24">
4-
<path slot="dismiss" fill="currentColor" d={mdiCloseCircle} />
2+
{#snippet dismiss()}
3+
<path fill="currentColor" d={mdiCloseCircle} />
4+
{/snippet}
55
</Kitchen>
66

77
<Button onclick={pushToKitchen}><Label>Push a New Snackbar</Label></Button>
@@ -19,8 +19,8 @@
1919
import Button, { Label } from '@smui/button';
2020
2121
let kitchen: Kitchen<undefined, 'svg'>;
22-
let reason = 'nothing yet';
23-
let action = 'nothing yet';
22+
let reason = $state('nothing yet');
23+
let action = $state('nothing yet');
2424
2525
function pushToKitchen() {
2626
kitchen.push({

packages/site/src/routes/demo/snackbar/_LeadingWithAction.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
<Snackbar leading bind:this={snackbar} onSMUISnackbarClosed={handleClosed}>
42
<Label>This is a leading snackbar.</Label>
53
<Actions>
@@ -18,7 +16,7 @@
1816
import Button from '@smui/button';
1917
2018
let snackbar: Snackbar;
21-
let reason = 'nothing yet';
19+
let reason = $state('nothing yet');
2220
2321
function handleClosed(e: CustomEvent<{ reason: string | undefined }>) {
2422
reason = e.detail.reason ?? 'Undefined.';

packages/site/src/routes/demo/snackbar/_Simple.svelte

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
<svelte:options runes={false} />
2-
31
<Snackbar bind:this={snackbarWithClose}>
42
<Label>This is a snackbar.</Label>
53
<Actions>

packages/site/src/routes/demo/snackbar/_StackedWithAction.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
<Snackbar
42
variant="stacked"
53
bind:this={snackbar}
@@ -32,8 +30,8 @@
3230
import IconButton from '@smui/icon-button';
3331
3432
let snackbar: Snackbar;
35-
let reason = 'nothing yet';
36-
let action = 'nothing yet';
33+
let reason = $state('nothing yet');
34+
let action = $state('nothing yet');
3735
3836
function handleClosedStacked(e: CustomEvent<{ reason: string | undefined }>) {
3937
reason = e.detail.reason ?? 'Undefined.';

packages/snackbar/src/Snackbar.svelte

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

33
<aside
44
bind:this={element}
@@ -10,16 +10,16 @@
1010
'mdc-snackbar--leading': leading,
1111
...internalClasses,
1212
})}
13-
{...exclude($$restProps, ['surface$'])}
13+
{...exclude(restProps, ['surface$'])}
1414
onkeydown={(e) => {
1515
if (instance) {
1616
instance.handleKeyDown(e);
1717
}
18-
$$restProps.onkeydown?.(e);
18+
restProps.onkeydown?.(e);
1919
}}
2020
onSMUISnackbarClosed={(e) => {
2121
handleClosed();
22-
$$restProps.onSMUISnackbarClosed?.(e);
22+
restProps.onSMUISnackbarClosed?.(e);
2323
}}
2424
>
2525
<div
@@ -30,13 +30,13 @@
3030
})}
3131
role="status"
3232
aria-relevant="additions"
33-
{...prefixFilter($$restProps, 'surface$')}
33+
{...prefixFilter(restProps, 'surface$')}
3434
onclick={(e) => {
3535
handleSurfaceClick(e);
36-
$$restProps.surface$onclick?.(e);
36+
restProps.surface$onclick?.(e);
3737
}}
3838
>
39-
<slot />
39+
{@render children?.()}
4040
</div>
4141
</aside>
4242

@@ -47,6 +47,7 @@
4747
<script lang="ts">
4848
import { MDCSnackbarFoundation, util } from '@material/snackbar';
4949
import { ponyfill } from '@material/dom';
50+
import type { Snippet } from 'svelte';
5051
import { onMount, setContext } from 'svelte';
5152
import type { SmuiAttrs, SmuiElementPropMap } from '@smui/common';
5253
import type { ActionArray } from '@smui/common/internal';
@@ -60,73 +61,118 @@
6061
6162
const { closest } = ponyfill;
6263
64+
interface UninitializedValue extends Function {}
65+
let uninitializedValue: UninitializedValue = () => {};
66+
function isUninitializedValue(value: any): value is UninitializedValue {
67+
return value === uninitializedValue;
68+
}
69+
6370
type OwnProps = {
71+
/**
72+
* An array of Action or [Action, ActionProps] to be applied to the element.
73+
*/
6474
use?: ActionArray;
75+
/**
76+
* A space separated list of CSS classes.
77+
*/
6578
class?: string;
66-
variant?: string;
79+
/**
80+
* The styling variant of the snackbar.
81+
*
82+
* Undefined is the default variant. Stacked means the text goes above the
83+
* actions and icons.
84+
*/
85+
variant?: 'stacked' | undefined;
86+
/**
87+
* Whether to position the snackbar in the leading portion of the screen.
88+
*/
6789
leading?: boolean;
90+
/**
91+
* How many milliseconds to wait before automatically closing.
92+
*/
6893
timeoutMs?: number;
94+
/**
95+
* Whether to close the snackbar when the escape key is pressed.
96+
*
97+
* This only works when an element inside the snackbar has focus.
98+
*/
6999
closeOnEscape?: boolean;
100+
/**
101+
* Text content to place in the label.
102+
*/
70103
labelText?: string;
104+
/**
105+
* Text content to place in the action button.
106+
*/
71107
actionButtonText?: string;
72-
surface$class?: string;
108+
/**
109+
* An array of Action or [Action, ActionProps] to be applied to the element.
110+
*/
73111
surface$use?: ActionArray;
112+
/**
113+
* A space separated list of CSS classes.
114+
*/
115+
surface$class?: string;
116+
117+
children?: Snippet;
74118
};
75-
type $$Props = OwnProps &
119+
let {
120+
use = [],
121+
class: className = '',
122+
variant,
123+
leading = false,
124+
timeoutMs = 5000,
125+
closeOnEscape = true,
126+
labelText = uninitializedValue as unknown as string,
127+
actionButtonText = uninitializedValue as unknown as string,
128+
surface$use = [],
129+
surface$class = '',
130+
children,
131+
...restProps
132+
}: OwnProps &
76133
SmuiAttrs<'aside', keyof OwnProps> & {
77134
[k in keyof SmuiElementPropMap['div'] as `surface\$${k}`]?: SmuiElementPropMap['div'][k];
78-
};
79-
80-
interface UninitializedValue extends Function {}
81-
let uninitializedValue: UninitializedValue = () => {};
82-
function isUninitializedValue(value: any): value is UninitializedValue {
83-
return value === uninitializedValue;
84-
}
85-
86-
// Remember to update $$Props if you add/remove/rename props.
87-
export let use: ActionArray = [];
88-
let className = '';
89-
export { className as class };
90-
export let variant = '';
91-
export let leading = false;
92-
export let timeoutMs = 5000;
93-
export let closeOnEscape = true;
94-
export let labelText: UninitializedValue | string = uninitializedValue;
95-
export let actionButtonText: UninitializedValue | string = uninitializedValue;
96-
export let surface$class = '';
97-
export let surface$use: ActionArray = [];
135+
} = $props();
98136
99137
let element: HTMLElement;
100-
let instance: MDCSnackbarFoundation;
101-
let internalClasses: { [k: string]: boolean } = {};
138+
let instance: MDCSnackbarFoundation | undefined = $state();
139+
let internalClasses: { [k: string]: boolean } = $state({});
102140
let closeResolve: (value: void) => void;
103141
let closePromise = new Promise<void>((resolve) => (closeResolve = resolve));
104142
105143
setContext('SMUI:label:context', 'snackbar');
106144
107-
$: if (instance && instance.getTimeoutMs() !== timeoutMs) {
108-
instance.setTimeoutMs(timeoutMs);
109-
}
145+
$effect(() => {
146+
if (instance && instance.getTimeoutMs() !== timeoutMs) {
147+
instance.setTimeoutMs(timeoutMs);
148+
}
149+
});
110150
111-
$: if (instance && instance.getCloseOnEscape() !== closeOnEscape) {
112-
instance.setCloseOnEscape(closeOnEscape);
113-
}
151+
$effect(() => {
152+
if (instance && instance.getCloseOnEscape() !== closeOnEscape) {
153+
instance.setCloseOnEscape(closeOnEscape);
154+
}
155+
});
114156
115-
$: if (
116-
instance &&
117-
!isUninitializedValue(labelText) &&
118-
getLabelElement().textContent !== labelText
119-
) {
120-
getLabelElement().textContent = labelText;
121-
}
157+
$effect(() => {
158+
if (
159+
instance &&
160+
!isUninitializedValue(labelText) &&
161+
getLabelElement().textContent !== labelText
162+
) {
163+
getLabelElement().textContent = labelText;
164+
}
165+
});
122166
123-
$: if (
124-
instance &&
125-
!isUninitializedValue(actionButtonText) &&
126-
getActionButtonElement().textContent !== actionButtonText
127-
) {
128-
getActionButtonElement().textContent = actionButtonText;
129-
}
167+
$effect(() => {
168+
if (
169+
instance &&
170+
!isUninitializedValue(actionButtonText) &&
171+
getActionButtonElement().textContent !== actionButtonText
172+
) {
173+
getActionButtonElement().textContent = actionButtonText;
174+
}
175+
});
130176
131177
onMount(() => {
132178
instance = new MDCSnackbarFoundation({
@@ -144,7 +190,7 @@
144190
instance.init();
145191
146192
return () => {
147-
instance.destroy();
193+
instance?.destroy();
148194
};
149195
});
150196
@@ -178,20 +224,23 @@
178224
179225
export function open() {
180226
waiting = waiting.then(() => {
181-
instance.open();
227+
instance?.open();
182228
return closePromise;
183229
});
184230
}
185231
186232
export function forceOpen() {
187-
return instance.open();
233+
return instance?.open();
188234
}
189235
190236
export function close(reason?: string) {
191-
return instance.close(reason);
237+
return instance?.close(reason);
192238
}
193239
194240
export function isOpen() {
241+
if (instance == null) {
242+
throw new Error('Instance is undefined.');
243+
}
195244
return instance.isOpen();
196245
}
197246

0 commit comments

Comments
 (0)