Skip to content

Commit 8448267

Browse files
committed
feat: new bottom app bar component
1 parent d97dc46 commit 8448267

29 files changed

+983
-13
lines changed

MIGRATING.md

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ https://github.com/material-components/material-components-web/blob/master/CHANG
2222
- Banner
2323
- New `autoClose` prop.
2424
- New `SMUIBanner:actionClicked` event. It is fired when an action is clicked and `autoClose` is `false`.
25+
- Bottom App Bar
26+
- A new component!
2527
- Dialog
2628
- New `sheet` prop.
2729
- New `noContentPadding` prop.

README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ Click a component/package below to go to the documentation. (Note that this docu
8282
- [Accordion](packages/accordion/README.md)
8383
- [Badge](packages/badge/README.md)
8484
- [Banner](packages/banner/README.md)
85+
- [Bottom App Bar](packages/bottom-app-bar/README.md)
8586
- Action Buttons
8687
- [Button](packages/button/README.md)
8788
- [Floating Action Button](packages/fab/README.md)
@@ -133,7 +134,7 @@ Click a component/package below to go to the documentation. (Note that this docu
133134

134135
<sub>† This is Sass based, and therefore doesn't require Svelte components. I've included a demo showing how you can use it.</sub>
135136

136-
<sub>‡ This is not an MDC Web component. It is an addition that SMUI provides.</sub>
137+
<sub>‡ This is not an MDC-Web component (upstream library). It is an addition that SMUI provides.</sub>
137138

138139
- Labels and icons are named exports in the components that use them, or you can use the 'Label' and 'Icon' exports from '@smui/common'. (Except for chips labels and icons, textfield icons, and select icons, because they are special snowflakes.)
139140

packages/bottom-app-bar/README.md

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Svelte Material UI - Bottom App Bar
2+
3+
Displays navigation and key actions at the bottom of mobile screens.
4+
5+
# Installation
6+
7+
```sh
8+
npm install --save-dev @smui-extra/bottom-app-bar
9+
```
10+
11+
# Examples and Usage Information
12+
13+
https://sveltematerialui.com/demo/bottom-app-bar
14+
15+
# More Information
16+
17+
See [App bars: bottom](https://material.io/components/app-bars-bottom) in the Material design spec.

packages/bottom-app-bar/_index.scss

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
@use 'smui-theme';
2+
@use './style';
3+
4+
@forward './mixins' show core-styles;

packages/bottom-app-bar/_mixins.scss

+147
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
@use 'sass:math';
2+
@use '@material/theme/index' as theme;
3+
@use '@material/feature-targeting/index' as feature-targeting;
4+
5+
// Default styles
6+
$section-height: 56px !default;
7+
$section-padding-vertical: 16px !default;
8+
$section-padding-horizontal: 16px !default;
9+
$fab-diameter: 56px !default;
10+
$inset-fab-padding: 8px !default;
11+
$acceleration-curve-timing-function: cubic-bezier(0.4, 0, 1, 1) !default;
12+
13+
@mixin core-styles($query: feature-targeting.all()) {
14+
$feat-color: feature-targeting.create-target($query, color);
15+
$feat-structure: feature-targeting.create-target($query, structure);
16+
17+
.smui-bottom-app-bar {
18+
display: flex;
19+
flex-direction: row;
20+
width: 100%;
21+
22+
&.smui-bottom-app-bar--fixed {
23+
position: fixed;
24+
bottom: 0;
25+
left: 0;
26+
right: 0;
27+
}
28+
29+
& > .smui-bottom-app-bar__section {
30+
display: flex;
31+
flex-direction: row;
32+
justify-content: center;
33+
padding: $section-padding-vertical $section-padding-horizontal;
34+
height: $section-height;
35+
box-sizing: border-box;
36+
flex-grow: 1;
37+
flex-basis: 0;
38+
overflow: visible;
39+
40+
.mdc-icon-button {
41+
margin-top: -12px;
42+
}
43+
44+
.mdc-fab {
45+
margin-top: -1 *
46+
($section-padding-vertical + math.div($fab-diameter, 2));
47+
// transform: translateY(-50%);
48+
}
49+
50+
&:first-child {
51+
justify-content: start;
52+
53+
.mdc-icon-button:first-child {
54+
margin-inline-start: -12px;
55+
}
56+
}
57+
58+
&:last-child {
59+
justify-content: end;
60+
61+
.mdc-icon-button:last-child {
62+
margin-inline-end: -12px;
63+
}
64+
}
65+
66+
&.smui-paper.smui-bottom-app-bar__section--fab-inset {
67+
@include inset-color(theme.$primary, 50%, $query);
68+
69+
&:first-child {
70+
@include inset-color(
71+
theme.$primary,
72+
$section-padding-horizontal + $inset-fab-padding +
73+
math.div($fab-diameter, 2),
74+
$query
75+
);
76+
77+
.mdc-fab {
78+
margin-inline-start: $inset-fab-padding;
79+
}
80+
}
81+
82+
&:last-child {
83+
@include inset-color(
84+
theme.$primary,
85+
calc(
86+
100% -
87+
(
88+
$section-padding-horizontal + $inset-fab-padding +
89+
math.div($fab-diameter, 2)
90+
)
91+
),
92+
$query
93+
);
94+
95+
.mdc-fab {
96+
margin-inline-end: $inset-fab-padding;
97+
}
98+
}
99+
100+
&.smui-paper--color-secondary {
101+
@include inset-color(theme.$secondary, 50%, $query);
102+
103+
&:first-child {
104+
@include inset-color(
105+
theme.$secondary,
106+
$section-padding-horizontal + $inset-fab-padding +
107+
math.div($fab-diameter, 2),
108+
$query
109+
);
110+
}
111+
112+
&:last-child {
113+
@include inset-color(
114+
theme.$secondary,
115+
calc(
116+
100% -
117+
(
118+
$section-padding-horizontal + $inset-fab-padding +
119+
math.div($fab-diameter, 2)
120+
)
121+
),
122+
$query
123+
);
124+
}
125+
}
126+
}
127+
}
128+
}
129+
130+
.smui-bottom-app-bar--fixed-adjust {
131+
padding-bottom: $section-height;
132+
}
133+
}
134+
135+
@mixin inset-color($color, $xpos, $query: feature-targeting.all()) {
136+
$feat-color: feature-targeting.create-target($query, color);
137+
138+
@include feature-targeting.targets($feat-color) {
139+
background: radial-gradient(
140+
($fab-diameter + ($inset-fab-padding * 2))
141+
($fab-diameter + ($inset-fab-padding * 2)) at $xpos 0,
142+
rgba(0, 0, 0, 0)
143+
((math.div($fab-diameter, 2) + $inset-fab-padding) * 0.99),
144+
$color ((math.div($fab-diameter, 2) + $inset-fab-padding) * 1.01)
145+
);
146+
}
147+
}

packages/bottom-app-bar/_style.scss

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
@use '@smui/paper/style' as paper-style;
2+
@use './mixins';
3+
4+
@include mixins.core-styles;

packages/bottom-app-bar/package.json

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
{
2+
"name": "@smui-extra/bottom-app-bar",
3+
"version": "6.1.4",
4+
"description": "Svelte Material UI - Bottom App Bar",
5+
"type": "module",
6+
"main": "dist/index.js",
7+
"module": "dist/index.js",
8+
"svelte": "dist/index.js",
9+
"types": "src/index.ts",
10+
"keywords": [
11+
"svelte",
12+
"svelte3",
13+
"material-ui",
14+
"material-design",
15+
"material",
16+
"svelte-components",
17+
"sveltejs"
18+
],
19+
"scripts": {
20+
"build": "npm run build:js && npm run build:svelte && npm run build:sass",
21+
"build:js": "tsc -p . --outDir dist/ --rootDir src/",
22+
"build:svelte": "svelte-strip strip src/ dist",
23+
"build:sass": "sass --no-source-map -I node_modules -I ../smui-theme/node_modules -I ../../node_modules _style.scss bare.css",
24+
"clean": "git status --ignored -u --porcelain . | grep -v node_modules/ | grep -e '^!! ' | sed 's/^!! packages\\/[a-z-]*\\///g' | tr '\\n' ' ' | xargs rm",
25+
"check": "npx svelte-check --ignore dist",
26+
"prepare": "npm run build",
27+
"test": "echo \"Error: no test specified\" && exit 1"
28+
},
29+
"publishConfig": {
30+
"access": "public"
31+
},
32+
"repository": {
33+
"type": "git",
34+
"url": "git+https://github.com/hperrin/svelte-material-ui.git"
35+
},
36+
"author": "Hunter Perrin <hperrin@gmail.com>",
37+
"bugs": {
38+
"url": "https://github.com/hperrin/svelte-material-ui/issues"
39+
},
40+
"license": "Apache-2.0",
41+
"dependencies": {
42+
"@material/feature-targeting": "^14.0.0",
43+
"@material/theme": "^14.0.0",
44+
"@smui/common": "^6.1.4",
45+
"@smui/paper": "^6.1.4",
46+
"svelte2tsx": "^0.5.12"
47+
},
48+
"devDependencies": {
49+
"@tsconfig/svelte": "^3.0.0",
50+
"sass": "^1.54.0",
51+
"svelte-check": "^2.8.1",
52+
"svelte-strip": "^1.0.2",
53+
"tslib": "^2.4.0",
54+
"typescript": "^4.7.4"
55+
}
56+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<svelte:component
2+
this={component}
3+
{tag}
4+
bind:this={element}
5+
use={[forwardEvents, ...use]}
6+
class={classMap({
7+
[className]: true,
8+
[adjustClass]: true,
9+
})}
10+
{...$$restProps}
11+
>
12+
<slot />
13+
</svelte:component>
14+
15+
<script lang="ts">
16+
import type { ComponentType, SvelteComponent } from 'svelte';
17+
import { get_current_component } from 'svelte/internal';
18+
import type { ActionArray } from '@smui/common/internal';
19+
import { forwardEventsBuilder, classMap } from '@smui/common/internal';
20+
import type { SmuiElementMap, SmuiAttrs, SmuiSvgAttrs } from '@smui/common';
21+
import { SmuiElement } from '@smui/common';
22+
23+
type TagName = $$Generic<keyof SmuiElementMap>;
24+
type Component = $$Generic<ComponentType<SvelteComponent>>;
25+
type OwnProps = {
26+
use?: ActionArray;
27+
class?: string;
28+
bottomAppBar: BottomAppBar;
29+
component?: Component;
30+
tag?: TagName;
31+
};
32+
type $$Props = OwnProps &
33+
(SmuiAttrs<keyof SmuiElementMap, OwnProps> | SmuiSvgAttrs<OwnProps>);
34+
35+
import type BottomAppBar from './BottomAppBar.svelte';
36+
37+
const forwardEvents = forwardEventsBuilder(get_current_component());
38+
39+
// Remember to update $$Props if you add/remove/rename props.
40+
export let use: ActionArray = [];
41+
let className = '';
42+
export { className as class };
43+
export let bottomAppBar: BottomAppBar;
44+
45+
let element: SvelteComponent;
46+
47+
export let component: Component = SmuiElement as unknown as Component;
48+
export let tag: TagName | undefined = (
49+
component === (SmuiElement as unknown as Component) ? 'main' : undefined
50+
) as TagName | undefined;
51+
52+
$: propStore = bottomAppBar && bottomAppBar.getPropStore();
53+
$: adjustClass = (() => {
54+
if (!propStore || $propStore.variant === 'static') {
55+
return '';
56+
}
57+
58+
return 'smui-bottom-app-bar--fixed-adjust';
59+
})();
60+
61+
export function getElement(): HTMLElement {
62+
return element.getElement();
63+
}
64+
</script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<div
2+
bind:this={element}
3+
use:useActions={use}
4+
use:forwardEvents
5+
class={classMap({
6+
[className]: true,
7+
'smui-bottom-app-bar': true,
8+
'smui-bottom-app-bar--fixed': variant === 'fixed',
9+
})}
10+
{...$$restProps}
11+
>
12+
<slot />
13+
</div>
14+
15+
<script lang="ts">
16+
import { setContext } from 'svelte';
17+
import { get_current_component } from 'svelte/internal';
18+
import type { Subscriber } from 'svelte/store';
19+
import { readable, writable } from 'svelte/store';
20+
import type { SmuiAttrs } from '@smui/common';
21+
import type { ActionArray } from '@smui/common/internal';
22+
import {
23+
forwardEventsBuilder,
24+
classMap,
25+
useActions,
26+
} from '@smui/common/internal';
27+
28+
type OwnProps = {
29+
use?: ActionArray;
30+
class?: string;
31+
color?: 'default' | 'primary' | 'secondary' | string;
32+
variant?: 'fixed' | 'static';
33+
};
34+
type $$Props = OwnProps & SmuiAttrs<'div', OwnProps>;
35+
36+
const forwardEvents = forwardEventsBuilder(get_current_component());
37+
38+
// Remember to update $$Props if you add/remove/rename props.
39+
export let use: ActionArray = [];
40+
let className = '';
41+
export { className as class };
42+
export let color: 'default' | 'primary' | 'secondary' | string = 'primary';
43+
export let variant: 'fixed' | 'static' = 'fixed';
44+
45+
let element: HTMLDivElement;
46+
47+
const colorStore = writable(color);
48+
$: $colorStore = color;
49+
setContext('SMUI:bottom-app-bar:color', colorStore);
50+
51+
let propStoreSet: Subscriber<{
52+
variant: 'fixed' | 'static';
53+
}>;
54+
let propStore = readable({ variant }, (set) => {
55+
propStoreSet = set;
56+
});
57+
$: if (propStoreSet) {
58+
propStoreSet({
59+
variant,
60+
});
61+
}
62+
63+
export function getPropStore() {
64+
return propStore;
65+
}
66+
67+
export function getElement() {
68+
return element;
69+
}
70+
</script>

0 commit comments

Comments
 (0)