|
1 |
| -<svelte:options runes={false} /> |
| 1 | +<svelte:options runes={true} /> |
2 | 2 |
|
3 | 3 | <svelte:window onscroll={handleTargetScroll} onresize={handleWindowResize} />
|
4 | 4 |
|
|
15 | 15 | .map(([name, value]) => `${name}: ${value};`)
|
16 | 16 | .concat([style])
|
17 | 17 | .join(' ')}
|
18 |
| - {...$$restProps} |
| 18 | + {...restProps} |
19 | 19 | >
|
20 |
| - <slot /> |
| 20 | + {#if children}{@render children()}{/if} |
21 | 21 | </div>
|
22 | 22 |
|
23 | 23 | <script lang="ts">
|
24 |
| - import { afterUpdate, setContext } from 'svelte'; |
| 24 | + import type { Snippet } from 'svelte'; |
| 25 | + import { onMount, setContext } from 'svelte'; |
25 | 26 | import type { Subscriber } from 'svelte/store';
|
26 | 27 | import { readable, writable } from 'svelte/store';
|
27 | 28 | import type { SmuiAttrs } from '@smui/common';
|
28 | 29 | import type { ActionArray } from '@smui/common/internal';
|
29 | 30 | import { classMap, useActions } from '@smui/common/internal';
|
30 | 31 |
|
31 | 32 | type OwnProps = {
|
| 33 | + /** |
| 34 | + * An array of Action or [Action, ActionProps] to be applied to the element. |
| 35 | + */ |
32 | 36 | use?: ActionArray;
|
| 37 | + /** |
| 38 | + * A space separated list of CSS classes. |
| 39 | + */ |
33 | 40 | class?: string;
|
| 41 | + /** |
| 42 | + * A list of CSS styles. |
| 43 | + */ |
34 | 44 | style?: string;
|
35 |
| - color?: 'default' | 'primary' | 'secondary' | string; |
| 45 | + /** |
| 46 | + * The type of app bar to display. |
| 47 | + */ |
36 | 48 | variant?: 'fixed' | 'static' | 'standard';
|
37 |
| - }; |
38 |
| - type $$Props = OwnProps & SmuiAttrs<'div', keyof OwnProps>; |
| 49 | + /** |
| 50 | + * The color of the app bar. |
| 51 | + */ |
| 52 | + color?: 'default' | 'primary' | 'secondary' | string; |
39 | 53 |
|
40 |
| - // Remember to update $$Props if you add/remove/rename props. |
41 |
| - export let use: ActionArray = []; |
42 |
| - let className = ''; |
43 |
| - export { className as class }; |
44 |
| - export let style = ''; |
45 |
| - export let color: 'default' | 'primary' | 'secondary' | string = 'primary'; |
46 |
| - export let variant: 'fixed' | 'static' | 'standard' = 'standard'; |
| 54 | + children?: Snippet; |
| 55 | + }; |
| 56 | + let { |
| 57 | + use = $bindable([]), |
| 58 | + class: className = $bindable(''), |
| 59 | + style = $bindable(''), |
| 60 | + variant = $bindable('standard'), |
| 61 | + color = $bindable('primary'), |
| 62 | + children, |
| 63 | + ...restProps |
| 64 | + }: OwnProps & SmuiAttrs<'div', keyof OwnProps> = $props(); |
47 | 65 |
|
48 | 66 | let element: HTMLDivElement;
|
49 | 67 |
|
50 |
| - let internalStyles: { [k: string]: string } = {}; |
| 68 | + let internalStyles: { [k: string]: string } = $state({}); |
51 | 69 | const colorStore = writable(color);
|
52 |
| - let withFab = false; |
53 |
| - let adjustOffset = 0; |
54 |
| - $: $colorStore = color; |
| 70 | + let withFab = $state(false); |
| 71 | + let adjustOffset = $state(0); |
| 72 | + $effect(() => { |
| 73 | + $colorStore = color; |
| 74 | + }); |
55 | 75 | setContext('SMUI:bottom-app-bar:color', colorStore);
|
56 |
| - let propStoreSet: Subscriber<{ |
57 |
| - withFab: boolean; |
58 |
| - adjustOffset: number; |
59 |
| - variant: 'fixed' | 'static' | 'standard'; |
60 |
| - }>; |
| 76 | + let propStoreSet: |
| 77 | + | Subscriber<{ |
| 78 | + withFab: boolean; |
| 79 | + adjustOffset: number; |
| 80 | + variant: 'fixed' | 'static' | 'standard'; |
| 81 | + }> |
| 82 | + | undefined = $state(); |
61 | 83 | let propStore = readable(
|
62 | 84 | {
|
63 | 85 | withFab,
|
|
68 | 90 | propStoreSet = set;
|
69 | 91 | },
|
70 | 92 | );
|
71 |
| - $: if (propStoreSet) { |
72 |
| - propStoreSet({ |
73 |
| - withFab, |
74 |
| - adjustOffset, |
75 |
| - variant, |
| 93 | + $effect(() => { |
| 94 | + if (propStoreSet) { |
| 95 | + propStoreSet({ |
| 96 | + withFab, |
| 97 | + adjustOffset, |
| 98 | + variant, |
| 99 | + }); |
| 100 | + } |
| 101 | + }); |
| 102 | +
|
| 103 | + onMount(() => { |
| 104 | + const observer = new MutationObserver(() => { |
| 105 | + if (variant === 'standard' || variant === 'fixed') { |
| 106 | + withFab = |
| 107 | + getElement()?.querySelector<HTMLDivElement>('.mdc-fab') != null; |
| 108 | + } else { |
| 109 | + withFab = false; |
| 110 | + } |
76 | 111 | });
|
77 |
| - } |
78 | 112 |
|
79 |
| - afterUpdate(() => { |
80 |
| - if (variant === 'standard' || variant === 'fixed') { |
81 |
| - withFab = getElement().querySelector<HTMLDivElement>('.mdc-fab') != null; |
82 |
| - } |
| 113 | + observer.observe(getElement(), { childList: true, subtree: true }); |
| 114 | +
|
| 115 | + return () => { |
| 116 | + observer.disconnect(); |
| 117 | + }; |
83 | 118 | });
|
84 | 119 |
|
85 | 120 | function addStyle(name: string, value: string) {
|
|
142 | 177 | }
|
143 | 178 |
|
144 | 179 | let oldVariant: 'fixed' | 'static' | 'standard' | null = null;
|
145 |
| - $: if (element && variant !== oldVariant) { |
146 |
| - if (variant === 'standard') { |
147 |
| - lastScrollPosition = getViewportScrollY(); |
148 |
| - bottomAppBarHeight = getTopAppBarHeight(); |
149 |
| - } else if (oldVariant === 'standard') { |
150 |
| - addStyle('bottom', ''); |
151 |
| - addStyle('--smui-bottom-app-bar--fab-offset', '0px'); |
152 |
| - adjustOffset = 0; |
| 180 | + $effect(() => { |
| 181 | + if (element && variant !== oldVariant) { |
| 182 | + if (variant === 'standard') { |
| 183 | + lastScrollPosition = getViewportScrollY(); |
| 184 | + bottomAppBarHeight = getTopAppBarHeight(); |
| 185 | + } else if (oldVariant === 'standard') { |
| 186 | + addStyle('bottom', ''); |
| 187 | + addStyle('--smui-bottom-app-bar--fab-offset', '0px'); |
| 188 | + adjustOffset = 0; |
| 189 | + } |
| 190 | + oldVariant = variant; |
153 | 191 | }
|
154 |
| - oldVariant = variant; |
155 |
| - } |
| 192 | + }); |
156 | 193 |
|
157 | 194 | /**
|
158 | 195 | * Scroll handler for the default scroll behavior of the bottom app bar.
|
|
0 commit comments