New feature and refactor of Classbuilder along with some bug fixes (#85)

This commit is contained in:
Conor_Mack 2020-02-10 10:04:20 +00:00 committed by GitHub
parent 8c9618b33b
commit 97da31fb63
5 changed files with 111 additions and 82 deletions

View File

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

View File

@ -1,43 +1,70 @@
export default class ClassBuilder { export default class ClassBuilder {
constructor(block, customDefaults) { constructor(block, defaultIgnoreList) {
this.block = `mdc-${block}`; this.block = `mdc-${block}`;
this.customDefaults = customDefaults; //will be ignored when building custom classes this.defaultIgnoreList = defaultIgnoreList; //will be ignored when building custom classes
} }
// classParams: {modifiers:[] (mdc), custom:[] (bbmd), extra:[] (any)} /*
blocks(classParams) { handles both blocks and elementss (BEM MD Notation)
let base = this.block; params = {elementName: string, props: {modifiers{}, customs:{}, extras: []}}
if (classParams == undefined) return base; All are optional
return this.buildClass(base, classParams); */
build(params) {
if (!params) return this.block; //return block if nothing passed
const { props, elementName } = params;
let base = !!elementName ? `${this.block}__${elementName}` : this.block;
if (!props) return base;
return this._handleProps(base, props);
} }
//elementName: string, classParams: {} //Easily grab a simple element class
elements(elementName, classParams) { elem(elementName) {
let base = `${this.block}__${elementName}`; return this.build({ elementName });
if (classParams == undefined) return base;
return this.buildClass(base, classParams);
} }
//TODO: Classparams modifier and custom could take an object. Booleans or numbers use key value for classes, strings use property value for classes. Extras to stay as is //use if a different base is needed than whats defined by this.block
buildClass(base, classParams) { debase(base, elementProps) {
if (!elementProps) return base;
return this._handleProps(base, elementProps);
}
//proxies bindProps and checks for which elementProps exist before binding
_handleProps(base, elementProps) {
let cls = base; let cls = base;
const { modifiers, customs, extras } = classParams; const { modifiers, customs, extras } = elementProps;
if (!!modifiers) if (!!modifiers) cls += this._bindProps(modifiers, base);
cls += modifiers.map(m => (!!m ? ` ${base}--${m}` : "")).join(" "); if (!!customs) cls += this._bindProps(customs, base, true);
if (!!customs)
cls += Object.entries(customs)
.map(([property, value]) => {
//disregard falsy and values set by customDefaults constructor param
if (
!!value &&
(!this.customDefaults || !this.customDefaults.includes(value))
) {
//custom scss name convention = bbmd-[block | element]--[property]-[value]
return ` bbmd-${base}--${property}-${value}`;
}
})
.join("");
if (!!extras) cls += ` ${extras.join(" ")}`; if (!!extras) cls += ` ${extras.join(" ")}`;
return cls.trim(); return cls.trim();
} }
/*
Handles both modifiers and customs. Use property, value or both depending
on whether it is passsed props for custom or modifiers
if custom uses the following convention for scss mixins:
bbmd-{this.block}--{property}-{value}
bbmd-mdc-button--size-large
*/
_bindProps(elementProps, base, isCustom = false) {
return Object.entries(elementProps)
.map(([property, value]) => {
//disregard falsy and values set by defaultIgnoreList constructor param
if (
!!value &&
(!this.defaultIgnoreList || !this.defaultIgnoreList.includes(value))
) {
let classBase = isCustom ? `bbmd-${base}` : `${base}`;
let valueType = typeof value;
if (valueType == "string" || valueType == "number") {
return isCustom
? ` ${classBase}--${property}-${value}`
: ` ${classBase}--${value}`;
} else if (valueType == "boolean") {
return ` ${classBase}--${property}`;
}
}
})
.join("");
}
} }

View File

@ -1,5 +1,6 @@
const getComponent = comp => `@budibase//materialdesign-components/${comp}`; const getComponent = comp => `@budibase//materialdesign-components/${comp}`;
export const props = { export const props = {
justAnH1: { justAnH1: {
_component: "@budibase/materialdesign-components/h1", _component: "@budibase/materialdesign-components/h1",
@ -27,11 +28,11 @@ export const props = {
textfield: { textfield: {
_component: "@budibase/materialdesign-components/textfield", _component: "@budibase/materialdesign-components/textfield",
_children: [], _children: [],
label: "Surname", label: "First",
icon: "alarm_on", colour: "secondary",
variant: "outlined",
helperText: "Add Surname",
textarea: true, textarea: true,
fullwidth:true,
helperText: "Add Surname",
useCharCounter: true useCharCounter: true
} }
}; };

View File

@ -44,15 +44,14 @@
export let persistent = false export let persistent = false
let id = `${label}-${variant}` let id = `${label}-${variant}`
let helperClasses = `${cb.block}-helper-text`
let modifiers = [] let modifiers = { fullwidth, disabled, textarea }
let customs = { colour } let customs = { colour }
if (variant == "standard" || fullwidth) { if (variant == "standard" || fullwidth) {
customs = { ...customs, variant } customs = { ...customs, variant }
} else { } else {
modifiers.push(variant) modifiers = { ...modifiers, variant }
} }
if (!textarea && size !== "medium") { if (!textarea && size !== "medium") {
@ -60,15 +59,12 @@
} }
if (!label || fullwidth) { if (!label || fullwidth) {
modifiers.push("no-label") modifiers = { ...modifiers, noLabel: "no-label" }
} }
//TODO: Refactor - this could be handled better using an object as modifier instead of an array let helperClasses = cb.debase(`${cb.block}-helper-text`, {
if (fullwidth) modifiers.push("fullwidth") modifiers: { persistent, validation },
if (disabled) modifiers.push("disabled") })
if (textarea) modifiers.push("textarea")
if (persistent) helperClasses += ` ${cb.block}-helper-text--persistent`
if (validation) helperClasses += ` ${cb.block}-helper-text--validation`
let useLabel = !!label && (!fullwidth || (fullwidth && textarea)) let useLabel = !!label && (!fullwidth || (fullwidth && textarea))
let useIcon = !!icon && (!textarea && !fullwidth) let useIcon = !!icon && (!textarea && !fullwidth)
@ -77,16 +73,16 @@
if (useIcon) { if (useIcon) {
setContext("BBMD:icon:context", "text-field") setContext("BBMD:icon:context", "text-field")
trailingIcon let iconClass = trailingIcon ? "with-trailing-icon" : "with-leading-icon"
? modifiers.push("with-trailing-icon") modifiers = { ...modifiers, iconClass }
: modifiers.push("with-leading-icon")
} }
$: renderLeadingIcon = useIcon && !trailingIcon $: renderLeadingIcon = useIcon && !trailingIcon
$: renderTrailingIcon = useIcon && trailingIcon $: renderTrailingIcon = useIcon && trailingIcon
const blockClasses = cb.blocks({ modifiers, customs }) let props = { modifiers, customs }
const inputClasses = cb.elements("input") const blockClasses = cb.build({ props })
const inputClasses = cb.elem("input")
let renderMaxLength = !!maxLength ? `0 / ${maxLength}` : "0" let renderMaxLength = !!maxLength ? `0 / ${maxLength}` : "0"

View File

@ -16,11 +16,12 @@
&.bbmd-mdc-text-field--colour-secondary { &.bbmd-mdc-text-field--colour-secondary {
&.mdc-text-field--focused { &.mdc-text-field--focused {
.mdc-floating-label--float-above { .mdc-floating-label--float-above {
color: var(--mdc-theme-secondary); @include mdc-floating-label-ink-color(secondary)
} }
} }
.mdc-line-ripple--active { .mdc-line-ripple--active {
background-color: var(--mdc-theme-secondary); @include mdc-line-ripple-color(secondary);
} }
&.mdc-text-field--outlined, &.mdc-text-field--outlined,