|
1 |
| -<svelte:options runes={false} /> |
| 1 | +<svelte:options runes /> |
2 | 2 |
|
3 | 3 | <svelte:window onresize={layout} />
|
4 | 4 |
|
|
17 | 17 | .concat([style])
|
18 | 18 | .join(' ')}
|
19 | 19 | role="banner"
|
20 |
| - {...exclude($$restProps, ['content$', 'textWrapper$', 'graphic$'])} |
| 20 | + {...exclude(restProps, ['content$', 'textWrapper$', 'graphic$'])} |
21 | 21 | onSMUIBannerButtonPrimaryActionClick={(e) => {
|
22 | 22 | handlePrimaryActionClick();
|
23 |
| - $$restProps.onSMUIBannerButtonPrimaryActionClick?.(e); |
| 23 | + restProps.onSMUIBannerButtonPrimaryActionClick?.(e); |
24 | 24 | }}
|
25 | 25 | onSMUIBannerButtonSecondaryActionClick={(e) => {
|
26 | 26 | handleSecondaryActionClick();
|
27 |
| - $$restProps.onSMUIBannerButtonSecondaryActionClick?.(e); |
| 27 | + restProps.onSMUIBannerButtonSecondaryActionClick?.(e); |
28 | 28 | }}
|
29 | 29 | >
|
30 |
| - <Fixed bind:fixed {width}> |
| 30 | + <Fixed {fixed} {width}> |
31 | 31 | <div
|
32 | 32 | bind:this={content}
|
33 | 33 | class={classMap({
|
|
36 | 36 | })}
|
37 | 37 | role="alertdialog"
|
38 | 38 | aria-live="assertive"
|
39 |
| - {...prefixFilter($$restProps, 'content$')} |
| 39 | + {...prefixFilter(restProps, 'content$')} |
40 | 40 | >
|
41 |
| - {#if $$slots.icon || $$slots.label} |
| 41 | + {#if icon || label} |
42 | 42 | <div
|
43 | 43 | class={classMap({
|
44 | 44 | [textWrapper$class]: true,
|
45 | 45 | 'mdc-banner__graphic-text-wrapper': true,
|
46 | 46 | })}
|
47 |
| - {...prefixFilter($$restProps, 'textWrapper$')} |
| 47 | + {...prefixFilter(restProps, 'textWrapper$')} |
48 | 48 | >
|
49 |
| - {#if $$slots.icon} |
| 49 | + {#if icon} |
50 | 50 | <div
|
51 | 51 | class={classMap({
|
52 | 52 | [graphic$class]: true,
|
53 | 53 | 'mdc-banner__graphic': true,
|
54 | 54 | })}
|
55 | 55 | role="img"
|
56 | 56 | {...altProp}
|
57 |
| - {...prefixFilter($$restProps, 'graphic$')} |
| 57 | + {...prefixFilter(restProps, 'graphic$')} |
58 | 58 | >
|
59 |
| - <slot name="icon" /> |
| 59 | + {@render icon?.()} |
60 | 60 | </div>
|
61 | 61 | {/if}
|
62 |
| - <slot name="label" /> |
| 62 | + {@render label?.()} |
63 | 63 | </div>
|
64 | 64 | {/if}
|
65 |
| - {#if $$slots.actions} |
| 65 | + {#if actions} |
66 | 66 | <div class="mdc-banner__actions">
|
67 |
| - <slot name="actions" /> |
| 67 | + {@render actions?.()} |
68 | 68 | </div>
|
69 | 69 | {/if}
|
70 | 70 | </div>
|
|
74 | 74 | <script lang="ts">
|
75 | 75 | import { CloseReason, MDCBannerFoundation } from '@material/banner';
|
76 | 76 | import { focusTrap as domFocusTrap } from '@material/dom';
|
| 77 | + import type { Snippet } from 'svelte'; |
77 | 78 | import { onMount, onDestroy, getContext, setContext, tick } from 'svelte';
|
78 | 79 | import type {
|
79 | 80 | AddLayoutListener,
|
|
95 | 96 | const { FocusTrap } = domFocusTrap;
|
96 | 97 |
|
97 | 98 | type OwnProps = {
|
| 99 | + /** |
| 100 | + * An array of Action or [Action, ActionProps] to be applied to the element. |
| 101 | + */ |
98 | 102 | use?: ActionArray;
|
| 103 | + /** |
| 104 | + * A space separated list of CSS classes. |
| 105 | + */ |
99 | 106 | class?: string;
|
| 107 | + /** |
| 108 | + * A list of CSS styles. |
| 109 | + */ |
100 | 110 | style?: string;
|
| 111 | + /** |
| 112 | + * Whether the banner is open. |
| 113 | + */ |
101 | 114 | 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 | + */ |
103 | 122 | centered?: boolean;
|
| 123 | + /** |
| 124 | + * Fix the banner to the top of the container. |
| 125 | + */ |
104 | 126 | fixed?: boolean;
|
| 127 | + /** |
| 128 | + * Stack the buttons under the content on mobile displays. |
| 129 | + */ |
105 | 130 | mobileStacked?: boolean;
|
| 131 | + /** |
| 132 | + * A space separated list of CSS classes. |
| 133 | + */ |
106 | 134 | content$class?: string;
|
| 135 | + /** |
| 136 | + * A space separated list of CSS classes. |
| 137 | + */ |
107 | 138 | textWrapper$class?: string;
|
| 139 | + /** |
| 140 | + * A space separated list of CSS classes. |
| 141 | + */ |
108 | 142 | graphic$class?: string;
|
| 143 | +
|
| 144 | + children?: Snippet; |
| 145 | +
|
| 146 | + icon?: Snippet; |
| 147 | + label?: Snippet; |
| 148 | + actions?: Snippet; |
109 | 149 | };
|
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 & |
111 | 168 | SmuiAttrs<'div', keyof OwnProps> & {
|
112 | 169 | [k in keyof SmuiElementPropMap['div'] as `content\$${k}`]?: SmuiElementPropMap['div'][k];
|
113 | 170 | } & {
|
114 | 171 | [k in keyof SmuiElementPropMap['div'] as `textWrapper\$${k}`]?: SmuiElementPropMap['div'][k];
|
115 | 172 | } & {
|
116 | 173 | [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(); |
132 | 175 |
|
133 | 176 | 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({}); |
137 | 180 | let content: HTMLDivElement;
|
138 | 181 | let focusTrap: domFocusTrap.FocusTrap | undefined;
|
139 | 182 | let addLayoutListener = getContext<AddLayoutListener | undefined>(
|
140 | 183 | 'SMUI:addLayoutListener',
|
141 | 184 | );
|
142 | 185 | let removeLayoutListener: RemoveLayoutListener | undefined;
|
143 |
| - let width: number | undefined = undefined; |
| 186 | + let width: number | undefined = $state(); |
144 | 187 |
|
145 | 188 | // This is for a div that uses the role of "img". TS doesn't like it directly
|
146 | 189 | // on the element.
|
|
150 | 193 | setContext('SMUI:icon:context', 'banner');
|
151 | 194 | setContext('SMUI:button:context', 'banner');
|
152 | 195 |
|
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 | + } |
158 | 203 | }
|
159 |
| - } |
| 204 | + }); |
160 | 205 |
|
161 | 206 | 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 | + }); |
166 | 213 |
|
167 | 214 | if (addLayoutListener) {
|
168 | 215 | removeLayoutListener = addLayoutListener(layout);
|
|
214 | 261 | layout();
|
215 | 262 |
|
216 | 263 | return () => {
|
217 |
| - instance.destroy(); |
| 264 | + instance?.destroy(); |
218 | 265 | };
|
219 | 266 | });
|
220 | 267 |
|
|
255 | 302 | }
|
256 | 303 |
|
257 | 304 | function handlePrimaryActionClick() {
|
258 |
| - instance.handlePrimaryActionClick(!autoClose); |
| 305 | + instance?.handlePrimaryActionClick(!autoClose); |
259 | 306 | }
|
260 | 307 |
|
261 | 308 | function handleSecondaryActionClick() {
|
262 |
| - instance.handleSecondaryActionClick(!autoClose); |
| 309 | + instance?.handleSecondaryActionClick(!autoClose); |
263 | 310 | }
|
264 | 311 |
|
265 | 312 | export function isOpen() {
|
|
0 commit comments