Skip to content

Commit 5de28c8

Browse files
committed
feat: migrate components in banner to runes
1 parent dbabb13 commit 5de28c8

File tree

7 files changed

+156
-92
lines changed

7 files changed

+156
-92
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ Svelte 5 Runes mode is being migrated to slowly. This is the todo list of compon
157157
- [x] Bottom App Bar
158158
- [x] Top App Bar
159159
- [x] Badge
160-
- [ ] Banner
160+
- [x] Banner
161161
- [x] Card
162162
- [x] Common
163163
- [ ] Data Table

packages/banner/src/Banner.svelte

Lines changed: 95 additions & 48 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 onresize={layout} />
44

@@ -17,17 +17,17 @@
1717
.concat([style])
1818
.join(' ')}
1919
role="banner"
20-
{...exclude($$restProps, ['content$', 'textWrapper$', 'graphic$'])}
20+
{...exclude(restProps, ['content$', 'textWrapper$', 'graphic$'])}
2121
onSMUIBannerButtonPrimaryActionClick={(e) => {
2222
handlePrimaryActionClick();
23-
$$restProps.onSMUIBannerButtonPrimaryActionClick?.(e);
23+
restProps.onSMUIBannerButtonPrimaryActionClick?.(e);
2424
}}
2525
onSMUIBannerButtonSecondaryActionClick={(e) => {
2626
handleSecondaryActionClick();
27-
$$restProps.onSMUIBannerButtonSecondaryActionClick?.(e);
27+
restProps.onSMUIBannerButtonSecondaryActionClick?.(e);
2828
}}
2929
>
30-
<Fixed bind:fixed {width}>
30+
<Fixed {fixed} {width}>
3131
<div
3232
bind:this={content}
3333
class={classMap({
@@ -36,35 +36,35 @@
3636
})}
3737
role="alertdialog"
3838
aria-live="assertive"
39-
{...prefixFilter($$restProps, 'content$')}
39+
{...prefixFilter(restProps, 'content$')}
4040
>
41-
{#if $$slots.icon || $$slots.label}
41+
{#if icon || label}
4242
<div
4343
class={classMap({
4444
[textWrapper$class]: true,
4545
'mdc-banner__graphic-text-wrapper': true,
4646
})}
47-
{...prefixFilter($$restProps, 'textWrapper$')}
47+
{...prefixFilter(restProps, 'textWrapper$')}
4848
>
49-
{#if $$slots.icon}
49+
{#if icon}
5050
<div
5151
class={classMap({
5252
[graphic$class]: true,
5353
'mdc-banner__graphic': true,
5454
})}
5555
role="img"
5656
{...altProp}
57-
{...prefixFilter($$restProps, 'graphic$')}
57+
{...prefixFilter(restProps, 'graphic$')}
5858
>
59-
<slot name="icon" />
59+
{@render icon?.()}
6060
</div>
6161
{/if}
62-
<slot name="label" />
62+
{@render label?.()}
6363
</div>
6464
{/if}
65-
{#if $$slots.actions}
65+
{#if actions}
6666
<div class="mdc-banner__actions">
67-
<slot name="actions" />
67+
{@render actions?.()}
6868
</div>
6969
{/if}
7070
</div>
@@ -74,6 +74,7 @@
7474
<script lang="ts">
7575
import { CloseReason, MDCBannerFoundation } from '@material/banner';
7676
import { focusTrap as domFocusTrap } from '@material/dom';
77+
import type { Snippet } from 'svelte';
7778
import { onMount, onDestroy, getContext, setContext, tick } from 'svelte';
7879
import type {
7980
AddLayoutListener,
@@ -95,52 +96,94 @@
9596
const { FocusTrap } = domFocusTrap;
9697
9798
type OwnProps = {
99+
/**
100+
* An array of Action or [Action, ActionProps] to be applied to the element.
101+
*/
98102
use?: ActionArray;
103+
/**
104+
* A space separated list of CSS classes.
105+
*/
99106
class?: string;
107+
/**
108+
* A list of CSS styles.
109+
*/
100110
style?: string;
111+
/**
112+
* Whether the banner is open.
113+
*/
101114
open?: boolean;
102-
autoClose?: false;
115+
/**
116+
* Whether the banner closes on button click.
117+
*/
118+
autoClose?: boolean;
119+
/**
120+
* Whether the banner contents are centered.
121+
*/
103122
centered?: boolean;
123+
/**
124+
* Fix the banner to the top of the container.
125+
*/
104126
fixed?: boolean;
127+
/**
128+
* Stack the buttons under the content on mobile displays.
129+
*/
105130
mobileStacked?: boolean;
131+
/**
132+
* A space separated list of CSS classes.
133+
*/
106134
content$class?: string;
135+
/**
136+
* A space separated list of CSS classes.
137+
*/
107138
textWrapper$class?: string;
139+
/**
140+
* A space separated list of CSS classes.
141+
*/
108142
graphic$class?: string;
143+
144+
children?: Snippet;
145+
146+
icon?: Snippet;
147+
label?: Snippet;
148+
actions?: Snippet;
109149
};
110-
type $$Props = OwnProps &
150+
let {
151+
use = [],
152+
class: className = '',
153+
style = '',
154+
open = $bindable(false),
155+
autoClose = true,
156+
centered = false,
157+
fixed = false,
158+
mobileStacked = false,
159+
content$class = '',
160+
textWrapper$class = '',
161+
graphic$class = '',
162+
children,
163+
icon,
164+
label,
165+
actions,
166+
...restProps
167+
}: OwnProps &
111168
SmuiAttrs<'div', keyof OwnProps> & {
112169
[k in keyof SmuiElementPropMap['div'] as `content\$${k}`]?: SmuiElementPropMap['div'][k];
113170
} & {
114171
[k in keyof SmuiElementPropMap['div'] as `textWrapper\$${k}`]?: SmuiElementPropMap['div'][k];
115172
} & {
116173
[k in keyof SmuiElementPropMap['div'] as `graphic\$${k}`]?: SmuiElementPropMap['div'][k];
117-
};
118-
119-
// Remember to update $$Props if you add/remove/rename props.
120-
export let use: ActionArray = [];
121-
let className = '';
122-
export { className as class };
123-
export let style = '';
124-
export let open = false;
125-
export let autoClose = true;
126-
export let centered = false;
127-
export let fixed = false;
128-
export let mobileStacked = false;
129-
export let content$class = '';
130-
export let textWrapper$class = '';
131-
export let graphic$class = '';
174+
} = $props();
132175
133176
let element: HTMLDivElement;
134-
let instance: MDCBannerFoundation;
135-
let internalClasses: { [k: string]: boolean } = {};
136-
let internalStyles: { [k: string]: string } = {};
177+
let instance: MDCBannerFoundation | undefined = $state();
178+
let internalClasses: { [k: string]: boolean } = $state({});
179+
let internalStyles: { [k: string]: string } = $state({});
137180
let content: HTMLDivElement;
138181
let focusTrap: domFocusTrap.FocusTrap | undefined;
139182
let addLayoutListener = getContext<AddLayoutListener | undefined>(
140183
'SMUI:addLayoutListener',
141184
);
142185
let removeLayoutListener: RemoveLayoutListener | undefined;
143-
let width: number | undefined = undefined;
186+
let width: number | undefined = $state();
144187
145188
// This is for a div that uses the role of "img". TS doesn't like it directly
146189
// on the element.
@@ -150,19 +193,23 @@
150193
setContext('SMUI:icon:context', 'banner');
151194
setContext('SMUI:button:context', 'banner');
152195
153-
$: if (instance && instance.isOpen() !== open) {
154-
if (open) {
155-
instance.open();
156-
} else {
157-
instance.close(CloseReason.UNSPECIFIED);
196+
$effect(() => {
197+
if (instance && instance.isOpen() !== open) {
198+
if (open) {
199+
instance.open();
200+
} else {
201+
instance.close(CloseReason.UNSPECIFIED);
202+
}
158203
}
159-
}
204+
});
160205
161206
let previousMobileStacked = mobileStacked;
162-
$: if (previousMobileStacked !== mobileStacked) {
163-
previousMobileStacked = mobileStacked;
164-
tick().then(layout);
165-
}
207+
$effect(() => {
208+
if (previousMobileStacked !== mobileStacked) {
209+
previousMobileStacked = mobileStacked;
210+
tick().then(layout);
211+
}
212+
});
166213
167214
if (addLayoutListener) {
168215
removeLayoutListener = addLayoutListener(layout);
@@ -214,7 +261,7 @@
214261
layout();
215262
216263
return () => {
217-
instance.destroy();
264+
instance?.destroy();
218265
};
219266
});
220267
@@ -255,11 +302,11 @@
255302
}
256303
257304
function handlePrimaryActionClick() {
258-
instance.handlePrimaryActionClick(!autoClose);
305+
instance?.handlePrimaryActionClick(!autoClose);
259306
}
260307
261308
function handleSecondaryActionClick() {
262-
instance.handleSecondaryActionClick(!autoClose);
309+
instance?.handleSecondaryActionClick(!autoClose);
263310
}
264311
265312
export function isOpen() {

packages/banner/src/Fixed.svelte

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,36 @@
1-
<svelte:options runes={false} />
1+
<svelte:options runes />
22

33
{#if fixed}
44
<div
55
bind:this={element}
66
class="mdc-banner__fixed"
77
style={width == null ? undefined : `width: ${width}px;`}
8-
{...$$restProps}
8+
{...restProps}
99
>
10-
<slot />
10+
{@render children?.()}
1111
</div>
1212
{:else}
13-
<slot />
13+
{@render children?.()}
1414
{/if}
1515

1616
<script lang="ts">
17-
export let fixed = false;
18-
export let width: number | undefined = undefined;
17+
import type { Snippet } from 'svelte';
18+
import type { SmuiAttrs } from '@smui/common';
1919
20-
let element: HTMLDivElement;
20+
type OwnProps = {
21+
fixed?: boolean;
22+
width?: number;
23+
24+
children?: Snippet;
25+
};
26+
let {
27+
fixed = false,
28+
width,
29+
children,
30+
...restProps
31+
}: OwnProps & SmuiAttrs<'div', keyof OwnProps> = $props();
32+
33+
let element: HTMLDivElement = $state() as HTMLDivElement;
2134
2235
export function getElement() {
2336
return element;

packages/site/src/routes/demo/banner/_DisableAutoClose.svelte

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
<svelte:options runes={false} />
2-
31
<pre class="status">Action Clicked: {action}</pre>
42

53
<div class="top-app-bar-container">
@@ -15,11 +13,13 @@
1513
autoClose={false}
1614
onSMUIBannerActionClicked={handleActionClicked}
1715
>
18-
<Label slot="label">This is a banner with actions to click.</Label>
19-
<svelte:fragment slot="actions">
16+
{#snippet label()}
17+
<Label>This is a banner with actions to click.</Label>
18+
{/snippet}
19+
{#snippet actions()}
2020
<Button secondary>Secondary</Button>
2121
<Button>Primary</Button>
22-
</svelte:fragment>
22+
{/snippet}
2323
</Banner>
2424
<div>
2525
<img
@@ -41,7 +41,7 @@
4141
[1]: 'Secondary',
4242
[2]: 'Unknown',
4343
};
44-
let action = 'None yet';
44+
let action = $state('None yet');
4545
4646
function handleActionClicked(event: CustomEvent<{ action: Action }>) {
4747
action = actions[event.detail.action];

packages/site/src/routes/demo/banner/_Fixed.svelte

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
<svelte:options runes={false} />
2-
31
<Banner open fixed mobileStacked content$style="max-width: max-content;">
4-
<Label slot="label">
5-
This is a fixed banner! It's here to let you know an important thing. Once
6-
you've successfully known the thing, you can dismiss it.
7-
</Label>
8-
<Button slot="actions">I Know It</Button>
2+
{#snippet label()}
3+
<Label>
4+
This is a fixed banner! It's here to let you know an important thing. Once
5+
you've successfully known the thing, you can dismiss it.
6+
</Label>
7+
{/snippet}
8+
{#snippet actions()}
9+
<Button>I Know It</Button>
10+
{/snippet}
911
</Banner>
1012

1113
<script lang="ts">

0 commit comments

Comments
 (0)