Completed checkbox and form field. Changes to test app (#91)
* Completed checkbox and form field component with changes around how test app renders components * Tidyup
This commit is contained in:
parent
34b957f331
commit
74bc97f3d4
|
@ -40,6 +40,8 @@
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"gitHead": "115189f72a850bfb52b65ec61d932531bf327072",
|
"gitHead": "115189f72a850bfb52b65ec61d932531bf327072",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@material/checkbox": "^4.0.0",
|
||||||
|
"@material/form-field": "^4.0.0",
|
||||||
"@material/textfield": "^4.0.0"
|
"@material/textfield": "^4.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
<script>
|
||||||
|
import { onMount, onDestroy, getContext } from "svelte";
|
||||||
|
import Formfield from "../Common/Formfield.svelte";
|
||||||
|
import { fieldStore } from "../Common/FormfieldStore.js";
|
||||||
|
import ClassBuilder from "../ClassBuilder.js";
|
||||||
|
import { MDCCheckbox } from "@material/checkbox";
|
||||||
|
|
||||||
|
export let id = "";
|
||||||
|
export let label = "";
|
||||||
|
export let disabled = false;
|
||||||
|
export let alignEnd = false;
|
||||||
|
export let indeterminate = false;
|
||||||
|
|
||||||
|
let instance = null;
|
||||||
|
let checkbox = null;
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
if (!!checkbox) {
|
||||||
|
instance = new MDCCheckbox(checkbox);
|
||||||
|
let fieldStore = getContext("BBMD:field-element");
|
||||||
|
fieldStore.setInput(instance);
|
||||||
|
instance.indeterminate = indeterminate;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const cb = new ClassBuilder("checkbox");
|
||||||
|
let modifiers = { disabled };
|
||||||
|
let props = { modifiers };
|
||||||
|
|
||||||
|
const blockClass = cb.build({ props });
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- TODO: Customizing Colour and Density - What level of customization for these things does Budibase need here? -->
|
||||||
|
|
||||||
|
<Formfield {label} {id} {alignEnd}>
|
||||||
|
<div bind:this={checkbox} class={blockClass}>
|
||||||
|
<input type="checkbox" class={cb.elem`native-control`} {id} {disabled} />
|
||||||
|
<div class={cb.elem`background`}>
|
||||||
|
<svg class={cb.elem`checkmark`} viewBox="0 0 24 24">
|
||||||
|
<path
|
||||||
|
class={cb.elem`checkmark-path`}
|
||||||
|
fill="none"
|
||||||
|
d="M1.73,12.91 8.1,19.28 22.79,4.59" />
|
||||||
|
</svg>
|
||||||
|
<div class={cb.elem`mixedmark`} />
|
||||||
|
</div>
|
||||||
|
<div class={cb.elem`ripple`} />
|
||||||
|
</div>
|
||||||
|
</Formfield>
|
|
@ -0,0 +1,2 @@
|
||||||
|
@import "@material/form-field/mdc-form-field";
|
||||||
|
@import "@material/checkbox/mdc-checkbox.scss";
|
|
@ -0,0 +1,2 @@
|
||||||
|
import "./_style.scss";
|
||||||
|
export { default as checkbox } from "./Checkbox.svelte";
|
|
@ -1,38 +1,74 @@
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
buildClass(base, classParams) {
|
//use if a different base is needed than whats defined by this.block
|
||||||
let cls = base
|
debase(base, elementProps) {
|
||||||
const { modifiers, customs, extras } = classParams
|
if (!elementProps) return base;
|
||||||
if (modifiers) cls += modifiers.map(m => ` ${base}--${m}`).join(" ")
|
return this._handleProps(base, elementProps);
|
||||||
if (customs)
|
}
|
||||||
cls += Object.entries(customs)
|
|
||||||
|
//proxies bindProps and checks for which elementProps exist before binding
|
||||||
|
_handleProps(base, elementProps) {
|
||||||
|
let cls = base;
|
||||||
|
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]) => {
|
.map(([property, value]) => {
|
||||||
//disregard falsy and values set by customDefaults constructor param
|
//disregard falsy and values set by defaultIgnoreList constructor param
|
||||||
if (!!value && !this.customDefaults.includes(value)) {
|
if (
|
||||||
//custom scss name convention = bbmd-[block | element]--[property]-[value]
|
!!value &&
|
||||||
return ` bbmd-${base}--${property}-${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}--${this._convertCamel(property)}-${value}`
|
||||||
|
: ` ${classBase}--${value}`;
|
||||||
|
} else if (valueType == "boolean") {
|
||||||
|
return ` ${classBase}--${this._convertCamel(property)}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.join("")
|
.join("");
|
||||||
if (extras) cls += ` ${extras.join(" ")}`
|
}
|
||||||
return cls.trim()
|
|
||||||
|
_convertCamel(str) {
|
||||||
|
return str.replace(/[A-Z]/g, match => `-${match.toLowerCase()}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
<script>
|
||||||
|
import "@material/form-field/mdc-form-field.scss";
|
||||||
|
import ClassBuilder from "../ClassBuilder.js";
|
||||||
|
import { fieldStore } from "./FormfieldStore.js";
|
||||||
|
import { MDCFormField } from "@material/form-field";
|
||||||
|
import { onMount, onDestroy, setContext } from "svelte";
|
||||||
|
|
||||||
|
const cb = new ClassBuilder("form-field");
|
||||||
|
|
||||||
|
let store;
|
||||||
|
const unsubscribe = fieldStore.subscribe(s => (store = s));
|
||||||
|
|
||||||
|
export let id = "";
|
||||||
|
export let label = "";
|
||||||
|
export let alignEnd = false;
|
||||||
|
|
||||||
|
let formField = null;
|
||||||
|
|
||||||
|
let modifiers = { alignEnd };
|
||||||
|
let props = { modifiers };
|
||||||
|
|
||||||
|
let blockClasses = cb.build({ props });
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
if (!!formField) fieldStore.set(new MDCFormField(formField));
|
||||||
|
setContext("BBMD:field-element", fieldStore);
|
||||||
|
});
|
||||||
|
|
||||||
|
onDestroy(unsubscribe);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div bind:this={formField} class={blockClasses}>
|
||||||
|
<slot />
|
||||||
|
<label for={id}>{label}</label>
|
||||||
|
</div>
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { writable } from "svelte/store";
|
||||||
|
|
||||||
|
function store() {
|
||||||
|
const { set, update, subscribe } = writable({});
|
||||||
|
|
||||||
|
function setInput(inp) {
|
||||||
|
update(n => {
|
||||||
|
n.input = inp;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
subscribe,
|
||||||
|
set,
|
||||||
|
setInput
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const fieldStore = store();
|
|
@ -1,19 +1,22 @@
|
||||||
<script>
|
<script>
|
||||||
import createApp from "./createApp"
|
import createApp from "./createApp"
|
||||||
import { props } from "./props"
|
import { props } from "./props"
|
||||||
|
|
||||||
let _bb
|
let _bb
|
||||||
|
const { h1, overline, button, textfield, checkbox } = props
|
||||||
const _appPromise = createApp()
|
|
||||||
_appPromise.then(a => (_bb = a))
|
|
||||||
|
|
||||||
const { h1, overline, button, textfield } = props
|
|
||||||
|
|
||||||
let currentComponent
|
let currentComponent
|
||||||
|
let _appPromise
|
||||||
$: {
|
$: {
|
||||||
if (_bb && currentComponent) {
|
if (currentComponent) {
|
||||||
_bb.hydrateChildren([h1, overline, button, textfield], currentComponent)
|
const _appPromise = createApp()
|
||||||
|
const page = {
|
||||||
|
props: {
|
||||||
|
_component: "testcomponents/rootComponent",
|
||||||
|
_children: [h1, overline, button, textfield, checkbox],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_appPromise.then(initialise => {
|
||||||
|
initialise(page, window.document.body, "")
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -21,9 +24,7 @@
|
||||||
{#await _appPromise}
|
{#await _appPromise}
|
||||||
loading
|
loading
|
||||||
{:then _bb}
|
{:then _bb}
|
||||||
|
|
||||||
<div id="current_component" bind:this={currentComponent} />
|
<div id="current_component" bind:this={currentComponent} />
|
||||||
|
|
||||||
{/await}
|
{/await}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
|
@ -1,17 +1,24 @@
|
||||||
import { createApp } from "@budibase/client/src/createApp"
|
import { createApp } from "@budibase/client/src/createApp"
|
||||||
import components from "./testComponents"
|
import components from "./testComponents"
|
||||||
import packageJson from "../../package.json"
|
import packageJson from "../../package.json"
|
||||||
|
import { rootComponent } from "./rootComponent"
|
||||||
export default async () => {
|
export default async props => {
|
||||||
delete components._lib
|
delete components._lib
|
||||||
|
|
||||||
const componentLibraries = {}
|
const componentLibraries = {}
|
||||||
componentLibraries[packageJson.name] = components
|
componentLibraries[packageJson.name] = components
|
||||||
|
componentLibraries["testcomponents"] = {
|
||||||
|
rootComponent: rootComponent(window)
|
||||||
|
}
|
||||||
const appDef = { hierarchy: {}, actions: {} }
|
const appDef = { hierarchy: {}, actions: {} }
|
||||||
const user = { name: "yeo", permissions: [] }
|
const user = { name: "yeo", permissions: [] }
|
||||||
|
const { initialisePage } = createApp(
|
||||||
var app = createApp(window.document, componentLibraries, appDef, user, {})
|
window.document,
|
||||||
|
componentLibraries,
|
||||||
return app
|
{ appRootPath: "" },
|
||||||
|
appDef,
|
||||||
|
user,
|
||||||
|
{},
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
return initialisePage
|
||||||
}
|
}
|
|
@ -1,5 +1,3 @@
|
||||||
const getComponent = comp => `@budibase/materialdesign-components/${comp}`;
|
|
||||||
|
|
||||||
|
|
||||||
export const props = {
|
export const props = {
|
||||||
h1: {
|
h1: {
|
||||||
|
@ -8,9 +6,9 @@ export const props = {
|
||||||
text: "Im a big header",
|
text: "Im a big header",
|
||||||
},
|
},
|
||||||
overline: {
|
overline: {
|
||||||
_component: getComponent`Overline`,
|
_component: "@budibase/materialdesign-components/Overline",
|
||||||
_children: [],
|
_children: [],
|
||||||
text: "A wee Overline",
|
text: "Im a wee overline",
|
||||||
},
|
},
|
||||||
button: {
|
button: {
|
||||||
_component: "@budibase/materialdesign-components/button",
|
_component: "@budibase/materialdesign-components/button",
|
||||||
|
@ -38,5 +36,12 @@ export const props = {
|
||||||
textarea: true,
|
textarea: true,
|
||||||
fullwidth: true,
|
fullwidth: true,
|
||||||
helperText: "Add Surname",
|
helperText: "Add Surname",
|
||||||
useCharCounter: true
|
useCharCounter: true,
|
||||||
|
},
|
||||||
|
checkbox: {
|
||||||
|
_component: "@budibase/materialdesign-components/checkbox",
|
||||||
|
_children: [],
|
||||||
|
id: "test-check",
|
||||||
|
label: "Check Yo Self",
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
export const rootComponent = window => {
|
||||||
|
return function(opts) {
|
||||||
|
const node = window.document.createElement("DIV")
|
||||||
|
const $set = props => {
|
||||||
|
props._bb.hydrateChildren(props._children, node)
|
||||||
|
}
|
||||||
|
const $destroy = () => {
|
||||||
|
if (opts.target && node) opts.target.removeChild(node)
|
||||||
|
}
|
||||||
|
this.$set = $set
|
||||||
|
this.$set(opts.props)
|
||||||
|
this.$destroy = $destroy
|
||||||
|
opts.target.appendChild(node)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,3 @@
|
||||||
import h1 from "../H1.svelte"
|
import { H1, Overline, button, icon, checkbox, textfield } from "@BBMD"
|
||||||
import { button, icon } from "@BBMD"
|
|
||||||
|
|
||||||
export default { h1, button, icon }
|
export default {H1, Overline, button, icon, checkbox, textfield }
|
||||||
|
|
|
@ -1,3 +1,14 @@
|
||||||
export { default as h1 } from "./H1.svelte"
|
import "@material/theme/mdc-theme.scss";
|
||||||
export { default as icon } from "./Icon.svelte"
|
|
||||||
export { button } from "./Button"
|
export { button } from "./Button"
|
||||||
|
export { default as icon } from "./Icon.svelte"
|
||||||
|
export { textfield } from "./Textfield"
|
||||||
|
export * from "./Typography"
|
||||||
|
export { checkbox } from "./Checkbox"
|
||||||
|
|
||||||
|
// import { Button } from "./Button";
|
||||||
|
// import Icon from "./Icon.svelte";
|
||||||
|
// import { Textfield } from "./Textfield";
|
||||||
|
// export { Button, Icon, Textfield };
|
||||||
|
// export * from "./Typography";
|
||||||
|
// export { Checkbox } from "./Checkbox";
|
||||||
|
|
Loading…
Reference in New Issue