Skip to content

Commit 1f92d28

Browse files
committed
feat: migrate components in bottom app bar to runes
1 parent b6dff77 commit 1f92d28

File tree

14 files changed

+171
-115
lines changed

14 files changed

+171
-115
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ Svelte 5 Runes mode is being migrated to slowly. This is the todo list of compon
154154
- [x] Floating Action Button
155155
- [x] Icon Button
156156
- App Bars
157-
- [ ] Bottom App Bar
157+
- [x] Bottom App Bar
158158
- [x] Top App Bar
159159
- [x] Badge
160160
- [ ] Banner

packages/bottom-app-bar/src/AutoAdjust.svelte

+57-30
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
<svelte:options runes={false} />
1+
<svelte:options runes={true} />
22

3-
<svelte:component
4-
this={component}
3+
<MyComponent
54
{tag}
65
bind:this={element}
76
{use}
@@ -13,12 +12,13 @@
1312
.map(([name, value]) => `${name}: ${value};`)
1413
.concat([style])
1514
.join(' ')}
16-
{...$$restProps}
15+
{...restProps}
1716
>
18-
<slot />
19-
</svelte:component>
17+
{#if children}{@render children()}{/if}
18+
</MyComponent>
2019

2120
<script lang="ts" generics="TagName extends SmuiEveryElement = 'main'">
21+
import type { Snippet } from 'svelte';
2222
import type { ActionArray } from '@smui/common/internal';
2323
import { classMap } from '@smui/common/internal';
2424
import type {
@@ -29,47 +29,74 @@
2929
} from '@smui/common';
3030
import { SmuiElement } from '@smui/common';
3131
32+
import type BottomAppBar from './BottomAppBar.svelte';
33+
3234
type OwnProps = {
35+
/**
36+
* An array of Action or [Action, ActionProps] to be applied to the element.
37+
*/
3338
use?: ActionArray;
39+
/**
40+
* A space separated list of CSS classes.
41+
*/
3442
class?: string;
43+
/**
44+
* A list of CSS styles.
45+
*/
3546
style?: string;
36-
bottomAppBar: BottomAppBar;
47+
/**
48+
* The Bottom App Bar that this auto adjuster is for.
49+
*
50+
* This is REQUIRED! The reason you can provide `null` is so that the
51+
* Bottom App Bar has a chance to mount, then you must provide it.
52+
*/
53+
bottomAppBar: BottomAppBar | null;
54+
/**
55+
* The component to use to render the element.
56+
*/
3757
component?: SmuiComponent<SmuiElementMap[TagName]>;
58+
/**
59+
* The tag name of the element to create.
60+
*/
3861
tag?: TagName;
39-
};
40-
type $$Props = OwnProps & SmuiAttrs<TagName, keyof OwnProps>;
41-
42-
import type BottomAppBar from './BottomAppBar.svelte';
4362
44-
// Remember to update $$Props if you add/remove/rename props.
45-
export let use: ActionArray = [];
46-
let className = '';
47-
export { className as class };
48-
export let style = '';
49-
export let bottomAppBar: BottomAppBar;
63+
children?: Snippet;
64+
};
65+
let {
66+
use = $bindable([]),
67+
class: className = $bindable(''),
68+
style = $bindable(''),
69+
bottomAppBar = $bindable(),
70+
component: MyComponent = $bindable(SmuiElement),
71+
tag = $bindable('main' as TagName),
72+
children,
73+
...restProps
74+
}: OwnProps & SmuiAttrs<TagName, keyof OwnProps> = $props();
5075
5176
let element: ReturnType<SmuiComponent<SmuiElementMap[TagName]>>;
5277
53-
export let component: SmuiComponent<SmuiElementMap[TagName]> = SmuiElement;
54-
export let tag: SmuiEveryElement | undefined =
55-
component === SmuiElement ? 'main' : undefined;
56-
57-
let internalStyles: { [k: string]: string } = {};
58-
$: propStore = bottomAppBar && bottomAppBar.getPropStore();
59-
$: adjustClass = (() => {
60-
if (!propStore || $propStore.variant === 'static') {
78+
let internalStyles: { [k: string]: string } = $state({});
79+
const propStore = $derived(bottomAppBar && bottomAppBar.getPropStore());
80+
const adjustClass = $derived.by(() => {
81+
if (!propStore || !$propStore || $propStore.variant === 'static') {
6182
return '';
6283
}
6384
85+
return `smui-bottom-app-bar--${$propStore.variant}-adjust ${
86+
$propStore.withFab ? 'smui-bottom-app-bar--with-fab' : ''
87+
}`;
88+
});
89+
$effect(() => {
90+
if (!propStore || !$propStore || $propStore.variant === 'static') {
91+
addStyle('--smui-bottom-app-bar--fab-offset', '0px');
92+
return;
93+
}
94+
6495
addStyle(
6596
'--smui-bottom-app-bar--fab-offset',
6697
$propStore.adjustOffset + 'px',
6798
);
68-
69-
return `smui-bottom-app-bar--${$propStore.variant}-adjust ${
70-
$propStore.withFab ? 'smui-bottom-app-bar--with-fab' : ''
71-
}`;
72-
})();
99+
});
73100
74101
function addStyle(name: string, value: string) {
75102
if (internalStyles[name] != value) {

packages/bottom-app-bar/src/BottomAppBar.svelte

+80-43
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<svelte:options runes={false} />
1+
<svelte:options runes={true} />
22

33
<svelte:window onscroll={handleTargetScroll} onresize={handleWindowResize} />
44

@@ -15,49 +15,71 @@
1515
.map(([name, value]) => `${name}: ${value};`)
1616
.concat([style])
1717
.join(' ')}
18-
{...$$restProps}
18+
{...restProps}
1919
>
20-
<slot />
20+
{#if children}{@render children()}{/if}
2121
</div>
2222

2323
<script lang="ts">
24-
import { afterUpdate, setContext } from 'svelte';
24+
import type { Snippet } from 'svelte';
25+
import { onMount, setContext } from 'svelte';
2526
import type { Subscriber } from 'svelte/store';
2627
import { readable, writable } from 'svelte/store';
2728
import type { SmuiAttrs } from '@smui/common';
2829
import type { ActionArray } from '@smui/common/internal';
2930
import { classMap, useActions } from '@smui/common/internal';
3031
3132
type OwnProps = {
33+
/**
34+
* An array of Action or [Action, ActionProps] to be applied to the element.
35+
*/
3236
use?: ActionArray;
37+
/**
38+
* A space separated list of CSS classes.
39+
*/
3340
class?: string;
41+
/**
42+
* A list of CSS styles.
43+
*/
3444
style?: string;
35-
color?: 'default' | 'primary' | 'secondary' | string;
45+
/**
46+
* The type of app bar to display.
47+
*/
3648
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;
3953
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();
4765
4866
let element: HTMLDivElement;
4967
50-
let internalStyles: { [k: string]: string } = {};
68+
let internalStyles: { [k: string]: string } = $state({});
5169
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+
});
5575
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();
6183
let propStore = readable(
6284
{
6385
withFab,
@@ -68,18 +90,31 @@
6890
propStoreSet = set;
6991
},
7092
);
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+
}
76111
});
77-
}
78112
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+
};
83118
});
84119
85120
function addStyle(name: string, value: string) {
@@ -142,17 +177,19 @@
142177
}
143178
144179
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;
153191
}
154-
oldVariant = variant;
155-
}
192+
});
156193
157194
/**
158195
* Scroll handler for the default scroll behavior of the bottom app bar.

packages/bottom-app-bar/src/Section.svelte

+23-11
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<svelte:options runes={false} />
1+
<svelte:options runes={true} />
22

33
<Paper
44
bind:this={element}
@@ -11,33 +11,45 @@
1111
color={$color}
1212
variant="unelevated"
1313
square
14-
{...$$restProps}><slot /></Paper
14+
{...restProps}
15+
>{#if children}{@render children()}{/if}</Paper
1516
>
1617

1718
<script lang="ts">
18-
import type { ComponentProps } from 'svelte';
19+
import type { ComponentProps, Snippet } from 'svelte';
1920
import { getContext } from 'svelte';
2021
import type { ActionArray } from '@smui/common/internal';
2122
import { classMap } from '@smui/common/internal';
2223
import Paper from '@smui/paper';
2324
2425
type OwnProps = {
26+
/**
27+
* An array of Action or [Action, ActionProps] to be applied to the element.
28+
*/
2529
use?: ActionArray;
30+
/**
31+
* A space separated list of CSS classes.
32+
*/
2633
class?: string;
34+
/**
35+
* Use an inset cutout styling for the FAB.
36+
*/
2737
fabInset?: boolean;
38+
39+
children?: Snippet;
2840
};
29-
type $$Props = Omit<ComponentProps<typeof Paper>, keyof OwnProps> &
41+
let {
42+
use = $bindable([]),
43+
class: className = $bindable(''),
44+
fabInset = $bindable(false),
45+
children,
46+
...restProps
47+
}: Omit<ComponentProps<typeof Paper>, keyof OwnProps> &
3048
OwnProps & {
3149
color?: never;
3250
variant?: never;
3351
square?: never;
34-
};
35-
36-
// Remember to update $$Props if you add/remove/rename props.
37-
export let use: ActionArray = [];
38-
let className = '';
39-
export { className as class };
40-
export let fabInset = false;
52+
} = $props();
4153
4254
let element: Paper;
4355

packages/site/src/routes/demo/bottom-app-bar/_Fab.svelte

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
<svelte:options runes={false} />
2-
31
<div>
42
<FormField>
53
<Checkbox bind:checked={secondaryColor} />
@@ -93,7 +91,7 @@
9391
import FormField from '@smui/form-field';
9492
import LoremIpsum from '$lib/LoremIpsum.svelte';
9593
96-
let secondaryColor = false;
94+
let secondaryColor = $state(false);
9795
</script>
9896

9997
<style>

packages/site/src/routes/demo/bottom-app-bar/_InsetFab.svelte

-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
<svelte:options runes={false} />
2-
31
<!-- Check out iframe/*.svelte too see how these work. -->
42
<iframe
53
class="bottom-app-bar-iframe"

packages/site/src/routes/demo/bottom-app-bar/_Snackbar.svelte

-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
<svelte:options runes={false} />
2-
31
<!-- Check out iframe/*.svelte too see how these work. -->
42
<iframe
53
class="bottom-app-bar-iframe"

0 commit comments

Comments
 (0)