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 7fe09780a4
commit 52c0e3b066
5 changed files with 111 additions and 82 deletions

View File

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

View File

@ -1,43 +1,70 @@
export default class ClassBuilder {
constructor(block, customDefaults) {
constructor(block, defaultIgnoreList) {
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) {
let base = this.block;
if (classParams == undefined) return base;
return this.buildClass(base, classParams);
/*
handles both blocks and elementss (BEM MD Notation)
params = {elementName: string, props: {modifiers{}, customs:{}, extras: []}}
All are optional
*/
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: {}
elements(elementName, classParams) {
let base = `${this.block}__${elementName}`;
if (classParams == undefined) return base;
return this.buildClass(base, classParams);
//Easily grab a simple element class
elem(elementName) {
return this.build({ elementName });
}
//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
buildClass(base, classParams) {
//use if a different base is needed than whats defined by this.block
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;
const { modifiers, customs, extras } = classParams;
if (!!modifiers)
cls += modifiers.map(m => (!!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 || !this.customDefaults.includes(value))
) {
//custom scss name convention = bbmd-[block | element]--[property]-[value]
return ` bbmd-${base}--${property}-${value}`;
}
})
.join("");
const { modifiers, customs, extras } = elementProps;
if (!!modifiers) cls += this._bindProps(modifiers, base);
if (!!customs) cls += this._bindProps(customs, base, true);
if (!!extras) cls += ` ${extras.join(" ")}`;
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}`;
export const props = {
justAnH1: {
_component: "@budibase/materialdesign-components/h1",
@ -27,11 +28,11 @@ export const props = {
textfield: {
_component: "@budibase/materialdesign-components/textfield",
_children: [],
label: "Surname",
icon: "alarm_on",
variant: "outlined",
helperText: "Add Surname",
label: "First",
colour: "secondary",
textarea: true,
fullwidth:true,
helperText: "Add Surname",
useCharCounter: true
}
};

View File

@ -44,15 +44,14 @@
export let persistent = false
let id = `${label}-${variant}`
let helperClasses = `${cb.block}-helper-text`
let modifiers = []
let modifiers = { fullwidth, disabled, textarea }
let customs = { colour }
if (variant == "standard" || fullwidth) {
customs = { ...customs, variant }
} else {
modifiers.push(variant)
modifiers = { ...modifiers, variant }
}
if (!textarea && size !== "medium") {
@ -60,15 +59,12 @@
}
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
if (fullwidth) modifiers.push("fullwidth")
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 helperClasses = cb.debase(`${cb.block}-helper-text`, {
modifiers: { persistent, validation },
})
let useLabel = !!label && (!fullwidth || (fullwidth && textarea))
let useIcon = !!icon && (!textarea && !fullwidth)
@ -77,16 +73,16 @@
if (useIcon) {
setContext("BBMD:icon:context", "text-field")
trailingIcon
? modifiers.push("with-trailing-icon")
: modifiers.push("with-leading-icon")
let iconClass = trailingIcon ? "with-trailing-icon" : "with-leading-icon"
modifiers = { ...modifiers, iconClass }
}
$: renderLeadingIcon = useIcon && !trailingIcon
$: renderTrailingIcon = useIcon && trailingIcon
const blockClasses = cb.blocks({ modifiers, customs })
const inputClasses = cb.elements("input")
let props = { modifiers, customs }
const blockClasses = cb.build({ props })
const inputClasses = cb.elem("input")
let renderMaxLength = !!maxLength ? `0 / ${maxLength}` : "0"

View File

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