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
da7339035f
commit
0af15bd7d8
|
@ -40,6 +40,8 @@
|
|||
"license": "MIT",
|
||||
"gitHead": "115189f72a850bfb52b65ec61d932531bf327072",
|
||||
"dependencies": {
|
||||
"@material/checkbox": "^4.0.0",
|
||||
"@material/form-field": "^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 {
|
||||
constructor(block, customDefaults) {
|
||||
this.block = `mdc-${block}`
|
||||
this.customDefaults = customDefaults //will be ignored when building custom classes
|
||||
constructor(block, defaultIgnoreList) {
|
||||
this.block = `mdc-${block}`;
|
||||
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 });
|
||||
}
|
||||
|
||||
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}`
|
||||
//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 } = 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}--${this._convertCamel(property)}-${value}`
|
||||
: ` ${classBase}--${value}`;
|
||||
} else if (valueType == "boolean") {
|
||||
return ` ${classBase}--${this._convertCamel(property)}`;
|
||||
}
|
||||
})
|
||||
.join("")
|
||||
if (extras) cls += ` ${extras.join(" ")}`
|
||||
return cls.trim()
|
||||
}
|
||||
})
|
||||
.join("");
|
||||
}
|
||||
|
||||
_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>
|
||||
import createApp from "./createApp"
|
||||
import { props } from "./props"
|
||||
|
||||
let _bb
|
||||
|
||||
const _appPromise = createApp()
|
||||
_appPromise.then(a => (_bb = a))
|
||||
|
||||
const { h1, overline, button, textfield } = props
|
||||
|
||||
const { h1, overline, button, textfield, checkbox } = props
|
||||
let currentComponent
|
||||
|
||||
let _appPromise
|
||||
$: {
|
||||
if (_bb && currentComponent) {
|
||||
_bb.hydrateChildren([h1, overline, button, textfield], currentComponent)
|
||||
if (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>
|
||||
|
@ -21,9 +24,7 @@
|
|||
{#await _appPromise}
|
||||
loading
|
||||
{:then _bb}
|
||||
|
||||
<div id="current_component" bind:this={currentComponent} />
|
||||
|
||||
{/await}
|
||||
|
||||
<style>
|
||||
|
|
|
@ -1,17 +1,24 @@
|
|||
import { createApp } from "@budibase/client/src/createApp"
|
||||
import components from "./testComponents"
|
||||
import packageJson from "../../package.json"
|
||||
|
||||
export default async () => {
|
||||
import { rootComponent } from "./rootComponent"
|
||||
export default async props => {
|
||||
delete components._lib
|
||||
|
||||
const componentLibraries = {}
|
||||
componentLibraries[packageJson.name] = components
|
||||
|
||||
componentLibraries["testcomponents"] = {
|
||||
rootComponent: rootComponent(window)
|
||||
}
|
||||
const appDef = { hierarchy: {}, actions: {} }
|
||||
const user = { name: "yeo", permissions: [] }
|
||||
|
||||
var app = createApp(window.document, componentLibraries, appDef, user, {})
|
||||
|
||||
return app
|
||||
const { initialisePage } = createApp(
|
||||
window.document,
|
||||
componentLibraries,
|
||||
{ appRootPath: "" },
|
||||
appDef,
|
||||
user,
|
||||
{},
|
||||
[]
|
||||
)
|
||||
return initialisePage
|
||||
}
|
|
@ -1,5 +1,3 @@
|
|||
const getComponent = comp => `@budibase/materialdesign-components/${comp}`;
|
||||
|
||||
|
||||
export const props = {
|
||||
h1: {
|
||||
|
@ -8,9 +6,9 @@ export const props = {
|
|||
text: "Im a big header",
|
||||
},
|
||||
overline: {
|
||||
_component: getComponent`Overline`,
|
||||
_component: "@budibase/materialdesign-components/Overline",
|
||||
_children: [],
|
||||
text: "A wee Overline",
|
||||
text: "Im a wee overline",
|
||||
},
|
||||
button: {
|
||||
_component: "@budibase/materialdesign-components/button",
|
||||
|
@ -36,7 +34,14 @@ export const props = {
|
|||
label: "First",
|
||||
colour: "secondary",
|
||||
textarea: true,
|
||||
fullwidth:true,
|
||||
fullwidth: true,
|
||||
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 { button, icon } from "@BBMD"
|
||||
import { H1, Overline, button, icon, checkbox, textfield } 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"
|
||||
export { default as icon } from "./Icon.svelte"
|
||||
import "@material/theme/mdc-theme.scss";
|
||||
|
||||
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