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:
commit
0228a78d67
|
@ -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
|
|
@ -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>
|
|
@ -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,
|
||||||
|
|
|
@ -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>
|
|
@ -0,0 +1,5 @@
|
||||||
|
@import "@material/button/mdc-button.scss";
|
||||||
|
@import "@material/ripple/mdc-ripple.scss";
|
||||||
|
@import "./mixins.scss";
|
||||||
|
|
||||||
|
@include bbmd-button-styles();
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
import "./_index.scss";
|
||||||
|
export { default as button } from "./Button.svelte";
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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>
|
|
@ -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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -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: ""
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
|
@ -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 }
|
|
||||||
|
|
|
@ -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";
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue