Merge pull request #81 from Conor-Mack/feature/md-button-ripple-icon-classbuilder

Functional MD Button and Icon with Ripple Effect and ClassBuilder Helper
This commit is contained in:
Martin McKeaveney 2020-02-04 17:42:04 +00:00 committed by GitHub
commit 0228a78d67
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 230 additions and 10 deletions

View File

@ -3,4 +3,4 @@ node_modules
yarn.lock yarn.lock
package-lock.json package-lock.json
dist/index.js dist/index.js
public/* public/build

View File

@ -0,0 +1,18 @@
<!doctype html>
<html>
<head>
<meta charset='utf8'>
<meta name='viewport' content='width=device-width'>
<title>Svelte app</title>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons"
rel="stylesheet">
<link rel='icon' type='image/png' href='/favicon.png'>
<link rel='stylesheet' href='/build/bundle.css'>
</head>
<body>
<script src='/build/bundle.js'></script>
</body>
</html>

View File

@ -91,7 +91,7 @@ export default {
sourcemap: true, sourcemap: true,
format: "iife", format: "iife",
name: "app", name: "app",
file: "public/bundle.js", file: "public/build/bundle.js",
globals: { globals: {
crypto: "crypto", crypto: "crypto",
}, },
@ -104,7 +104,7 @@ export default {
// we'll extract any component CSS out into // we'll extract any component CSS out into
// a separate file — better for performance // a separate file — better for performance
css: css => { css: css => {
css.write("public/bundle.css") css.write("public/build/bundle.css");
}, },
hydratable: true, hydratable: true,

View File

@ -0,0 +1,60 @@
<script>
import { setContext, getContext } from "svelte"
import Icon from "../Icon.svelte"
import ripple from "../Ripple.js"
import ClassBuilder from "../ClassBuilder.js"
const cb = new ClassBuilder("button", ["primary", "medium"])
export let variant = "raised"
export let colour = "primary"
export let size = "medium"
export let href = ""
export let icon = ""
export let trailingIcon = false
export let fullBleed = false
export let text = ""
export let disabled = false
$: if (icon) {
setContext("BBMD:icon:context", "button")
}
$: renderLeadingIcon = !!icon && !trailingIcon
$: renderTrailingIcon = !!icon && trailingIcon
let blockClasses = cb.blocks({
modifiers: !href ? [variant] : null,
customs: { size, colour },
})
</script>
<!-- TODO: Activated colour on primary elevated buttons seems to be rendering weird -->
{#if href}
<a class={blockClasses} {href} on:click>
<span class={cb.elements('label')}>{text}</span>
</a>
{:else}
<button
use:ripple={{ colour }}
class={blockClasses}
class:fullBleed
{disabled}
on:click>
{#if renderLeadingIcon}
<Icon {icon} />
{/if}
<span class={cb.elements('label')}>{text}</span>
{#if renderTrailingIcon}
<Icon {icon} />
{/if}
</button>
{/if}
<style>
.fullBleed {
width: 100%;
}
</style>

View File

@ -0,0 +1,5 @@
@import "@material/button/mdc-button.scss";
@import "@material/ripple/mdc-ripple.scss";
@import "./mixins.scss";
@include bbmd-button-styles();

View File

@ -0,0 +1,39 @@
@import "@material/feature-targeting/functions";
@import "@material/feature-targeting/mixins";
@mixin bbmd-button-styles($query: mdc-feature-all()) {
$feat-structure: mdc-feature-create-target($query, structure);
.bbmd-mdc-button--size-large {
@include button-sizing($feat-structure, 21px, 40px, 15px);
}
.bbmd-mdc-button--size-small {
@include button-sizing($feat-structure, 9px, 32px, 13px);
}
.bbmd-mdc-button--colour-secondary {
//no feature target as relying on theme variable custom property
@include mdc-button-ink-color(secondary);
&.mdc-button--raised,
&.mdc-button--unelevated {
@include mdc-button-container-fill-color(secondary);
@include mdc-button-ink-color(on-secondary);
}
&.mdc-button--outlined {
@include mdc-button-outline-color(secondary);
}
}
}
@mixin button-sizing($feat, $padding, $height, $fontSize) {
@include mdc-feature-targets($feat) {
padding: 0px $padding;
height: $height;
}
> .mdc-button__label {
font-size: $fontSize;
}
}

View File

@ -0,0 +1,2 @@
import "./_index.scss";
export { default as button } from "./Button.svelte";

View File

@ -0,0 +1,38 @@
export default class ClassBuilder {
constructor(block, customDefaults) {
this.block = `mdc-${block}`;
this.customDefaults = customDefaults; //will be ignored when building custom classes
}
// classParams: {modifiers:[] (mdc), custom:[] (bbmd), extra:[] (any)}
blocks(classParams) {
let base = this.block;
if (classParams == undefined) return base;
return this.buildClass(base, classParams);
}
//elementName: string, classParams: {}
elements(elementName, classParams) {
let base = `${this.block}__${elementName}`;
if (classParams == undefined) return base;
return this.buildClass(base, classParams);
}
buildClass(base, classParams) {
let cls = base;
const { modifiers, customs, extras } = classParams;
if (!!modifiers) cls += modifiers.map(m => ` ${base}--${m}`).join(" ");
if (!!customs)
cls += Object.entries(customs)
.map(([property, value]) => {
//disregard falsy and values set by customDefaults constructor param
if (!!value && !this.customDefaults.includes(value)) {
//custom scss name convention = bbmd-[block | element]--[property]-[value]
return ` bbmd-${base}--${property}-${value}`;
}
})
.join("");
if (!!extras) cls += ` ${extras.join(" ")}`;
return cls.trim();
}
}

View File

@ -0,0 +1,13 @@
<script>
import { getContext } from "svelte";
export let icon = "";
let iconContext = getContext("BBMD:icon:context");
let cls =
iconContext == "button"
? "material-icons mdc-button__icon"
: "material-icons";
</script>
<i class={cls}>{icon}</i>

View File

@ -0,0 +1,28 @@
import { MDCRipple } from "@material/ripple";
export default function ripple(
node,
props = { colour: "primary", unbounded: false }
) {
node.classList.add("mdc-ripple-surface");
const component = new MDCRipple(node);
component.unbounded = props.unbounded;
if (props.colour === "secondary") {
node.classList.remove("mdc-ripple-surface--primary");
node.classList.add("mdc-ripple-surface--accent");
} else {
node.classList.add("mdc-ripple-surface--primary");
node.classList.remove("mdc-ripple-surface--accent");
}
return {
destroy() {
component.destroy();
node.classList.remove("mdc-ripple-surface");
node.classList.remove("mdc-ripple-surface--primary");
node.classList.remove("mdc-ripple-surface--accent");
component = null;
}
};
}

View File

@ -7,6 +7,20 @@ export const props = {
button: { button: {
_component: "@budibase/materialdesign-components/button", _component: "@budibase/materialdesign-components/button",
_children: [], _children: [],
raised: true, variant: "raised",
colour: "secondary",
size: "large",
href: "",
icon: "alarm_on",
trailingIcon: true,
fullBleed: false,
text: "I am button",
disabled: false
}, },
} icon: {
_component: "@budibase/materialdesign-components/icon",
_children: [],
icon: ""
}
};

View File

@ -1,4 +1,5 @@
import h1 from "../H1.svelte" import h1 from "../H1.svelte";
import { button } from "@BBMD" import { button, icon } from "@BBMD";
export default { h1, button, icon };
export default { h1, button }

View File

@ -1,2 +1,4 @@
export { default as h1 } from "./H1.svelte" export { default as h1 } from "./H1.svelte";
export { default as button } from "./Button.svelte" export { default as icon } from "./Icon.svelte";
export { button } from "./Button";