Completed textfield, minor bug fixes and config changes to reference uncompiled components (#83)

* Beginnings of text field

* Completed textfield component and its dependents

* Minor bug fixes
This commit is contained in:
Conor_Mack 2020-02-07 20:56:00 +00:00 committed by GitHub
parent 3fd14da711
commit 8c9618b33b
17 changed files with 304 additions and 19 deletions

View File

@ -38,5 +38,8 @@
],
"version": "0.0.15",
"license": "MIT",
"gitHead": "115189f72a850bfb52b65ec61d932531bf327072"
"gitHead": "115189f72a850bfb52b65ec61d932531bf327072",
"dependencies": {
"@material/textfield": "^4.0.0"
}
}

View File

@ -4,7 +4,8 @@
<meta charset='utf8'>
<meta name='viewport' content='width=device-width'>
<title>Svelte app</title>
<title>Budibase-Material Design</title>
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons"
rel="stylesheet">
<link rel='icon' type='image/png' href='/favicon.png'>

View File

@ -5,15 +5,31 @@ import livereload from "rollup-plugin-livereload"
import { terser } from "rollup-plugin-terser"
import json from "rollup-plugin-json"
import alias from "rollup-plugin-alias"
import postcss from "rollup-plugin-postcss";
import path from "path"
const aliases = {
resolve: [".js", ".svelte"],
entries: [
{ find: "@BBMD", replacement: path.resolve(__dirname, "dist/index.js") },
// { find: "@BBMD", replacement: path.resolve(__dirname, "dist/index.js") },
{ find: "@BBMD", replacement: path.resolve(__dirname, "src/index.js") },
],
}
const postcssOptions = () => ({
extensions: [".scss", ".sass"],
extract: false,
minimize: true,
use: [
[
"sass",
{
includePaths: ["./node_modules"],
},
],
],
})
const production = !process.env.ROLLUP_WATCH
const lodash_fp_exports = [
@ -142,6 +158,7 @@ export default {
// If we're building for production (npm run build
// instead of npm run dev), minify
production && terser(),
postcss(postcssOptions()),
],
watch: {
clearScreen: false,

View File

@ -13,7 +13,7 @@
export let href = ""
export let icon = ""
export let trailingIcon = false
export let fullBleed = false
export let fullwidth = false
export let text = ""
export let disabled = false
@ -40,7 +40,7 @@
<button
use:ripple={{ colour }}
class={blockClasses}
class:fullBleed
class:fullwidth
{disabled}
on:click>
{#if renderLeadingIcon}
@ -54,7 +54,7 @@
{/if}
<style>
.fullBleed {
.fullwidth {
width: 100%;
}
</style>

View File

@ -18,15 +18,20 @@ export default class ClassBuilder {
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
buildClass(base, classParams) {
let cls = base;
const { modifiers, customs, extras } = classParams;
if (!!modifiers) cls += modifiers.map(m => ` ${base}--${m}`).join(" ");
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.includes(value)) {
if (
!!value &&
(!this.customDefaults || !this.customDefaults.includes(value))
) {
//custom scss name convention = bbmd-[block | element]--[property]-[value]
return ` bbmd-${base}--${property}-${value}`;
}

View File

@ -0,0 +1,7 @@
<script>
import "@material/floating-label/mdc-floating-label.scss"
export let forInput = ""
export let text = ""
</script>
<label for={forInput} class="mdc-floating-label">{text}</label>

View File

@ -0,0 +1,14 @@
<script>
import "@material/notched-outline/mdc-notched-outline.scss"
export let useLabel = true
</script>
<div class="mdc-notched-outline">
<div class="mdc-notched-outline__leading" />
{#if useLabel}
<div class="mdc-notched-outline__notch">
<slot />
</div>
{/if}
<div class="mdc-notched-outline__trailing" />
</div>

View File

@ -4,10 +4,9 @@
export let icon = "";
let iconContext = getContext("BBMD:icon:context");
let cls =
iconContext == "button"
? "material-icons mdc-button__icon"
: "material-icons";
let cls = iconContext
? `material-icons mdc-${iconContext}__icon`
: "material-icons";
</script>
<i class={cls}>{icon}</i>

View File

@ -9,12 +9,13 @@
const testProps = props.justAnH1
const button = props.button
const textfield = props.textfield
let currentComponent
$: {
if (_bb && currentComponent) {
_bb.hydrateChildren([testProps, button], currentComponent)
_bb.hydrateChildren([testProps, button, textfield], currentComponent)
}
}
</script>

View File

@ -11,6 +11,7 @@ export default async () => {
const appDef = { hierarchy: {}, actions: {} }
const user = { name: "yeo", permissions: [] }
var app = createApp(componentLibraries, appDef, user)
var app = createApp(window.document, componentLibraries, appDef, user, {})
return app
}

View File

@ -1,3 +1,5 @@
const getComponent = comp => `@budibase//materialdesign-components/${comp}`;
export const props = {
justAnH1: {
_component: "@budibase/materialdesign-components/h1",
@ -13,7 +15,7 @@ export const props = {
href: "",
icon: "alarm_on",
trailingIcon: true,
fullBleed: false,
fullwidth: false,
text: "I am button",
disabled: false
},
@ -21,6 +23,16 @@ export const props = {
_component: "@budibase/materialdesign-components/icon",
_children: [],
icon: ""
},
textfield: {
_component: "@budibase/materialdesign-components/textfield",
_children: [],
label: "Surname",
icon: "alarm_on",
variant: "outlined",
helperText: "Add Surname",
textarea: true,
useCharCounter: true
}
};

View File

@ -1,5 +1,4 @@
import h1 from "../H1.svelte";
import { button, icon } from "@BBMD";
export default { h1, button, icon };
import { button, icon, textfield } from "@BBMD";
export default { h1, button, icon, textfield };

View File

@ -0,0 +1,173 @@
<script>
import { setContext, onMount } from "svelte"
import { MDCTextField } from "@material/textfield"
import { MDCLineRipple } from "@material/line-ripple"
import ClassBuilder from "../ClassBuilder.js"
import NotchedOutline from "../Common/NotchedOutline.svelte"
import FloatingLabel from "../Common/FloatingLabel.svelte"
import Icon from "../Icon.svelte"
const cb = new ClassBuilder("text-field", ["primary", "medium"])
let tf = null
let tfInstance = null
onMount(() => {
if (!!tf) tfInstance = new MDCTextField(tf)
return () => {
!!tfInstance && tf.tfInstance.destroy()
tf = null
}
})
export let label = ""
export let variant = "standard" //outlined | filled | standard
export let disabled = false
export let fullwidth = false
export let colour = "primary"
export let size = "medium"
export let type = "text" //text or password
export let required = false
export let minLength = 0
export let maxLength = 100
export let useCharCounter = false
export let helperText = ""
export let errorText = ""
export let placeholder = ""
export let icon = ""
export let trailingIcon = false
export let textarea = false
export let rows = 4
export let cols = 40
export let validation = false
export let persistent = false
let id = `${label}-${variant}`
let helperClasses = `${cb.block}-helper-text`
let modifiers = []
let customs = { colour }
if (variant == "standard" || fullwidth) {
customs = { ...customs, variant }
} else {
modifiers.push(variant)
}
if (!textarea && size !== "medium") {
customs = { ...customs, size }
}
if (!label || fullwidth) {
modifiers.push("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 useLabel = !!label && (!fullwidth || (fullwidth && textarea))
let useIcon = !!icon && (!textarea && !fullwidth)
$: useNotchedOutline = variant == "outlined" || textarea
if (useIcon) {
setContext("BBMD:icon:context", "text-field")
trailingIcon
? modifiers.push("with-trailing-icon")
: modifiers.push("with-leading-icon")
}
$: renderLeadingIcon = useIcon && !trailingIcon
$: renderTrailingIcon = useIcon && trailingIcon
const blockClasses = cb.blocks({ modifiers, customs })
const inputClasses = cb.elements("input")
let renderMaxLength = !!maxLength ? `0 / ${maxLength}` : "0"
function focus(event) {
tfInstance.focus()
}
</script>
<!--
TODO:Needs error handling - this will depend on how Budibase handles errors
-->
<div class="textfield-container" class:fullwidth>
<div bind:this={tf} class={blockClasses}>
{#if textarea}
{#if useCharCounter}
<div class="mdc-text-field-character-counter">{renderMaxLength}</div>
{/if}
<textarea
{id}
class={inputClasses}
class:fullwidth
{disabled}
{rows}
{cols}
{required}
{placeholder}
{minLength}
{maxLength}
on:change />
{:else}
{#if renderLeadingIcon}
<Icon {icon} />
{/if}
<input
{id}
{disabled}
on:focus={focus}
class={inputClasses}
{type}
{required}
placeholder={!!label && fullwidth ? label : placeholder}
{minLength}
{maxLength}
aria-label={`Textfield ${variant}`}
on:change />
{#if renderTrailingIcon}
<Icon {icon} />
{/if}
{#if variant !== 'outlined'}
<div class="mdc-line-ripple" />
{/if}
{/if}
{#if useNotchedOutline}
<NotchedOutline {useLabel}>
{#if useLabel}
<FloatingLabel forInput={id} text={label} />
{/if}
</NotchedOutline>
{:else if useLabel}
<FloatingLabel forInput={id} text={label} />
{/if}
</div>
<!-- TODO: Split to own component? -->
<div class="mdc-text-field-helper-line">
<div class={helperClasses}>{!!errorText ? errorText : helperText}</div>
{#if useCharCounter && !textarea}
<div class="mdc-text-field-character-counter">{renderMaxLength}</div>
{/if}
</div>
</div>
<style>
.textfield-container {
padding: 8px;
display: flex;
flex-direction: column;
width: 227px;
}
.fullwidth {
width: 100%;
}
</style>

View File

@ -0,0 +1,5 @@
@import "@material/textfield/mdc-text-field.scss";
@import "@material/line-ripple/mdc-line-ripple.scss";
@import "./mixins.scss";
@include bbmd-textfield-styles();

View File

@ -0,0 +1,45 @@
@import "@material/feature-targeting/_functions.scss";
@import "@material/feature-targeting/_mixins.scss";
@mixin bbmd-textfield-styles($query: mdc-feature-all()) {
$feat-structure: mdc-feature-create-target($query, structure);
.mdc-text-field {
&.bbmd-mdc-text-field--size-small {
@include mdc-text-field-height(48px);
}
&.bbmd-mdc-text-field--size-large {
@include mdc-text-field-height(64px);
}
&.bbmd-mdc-text-field--colour-secondary {
&.mdc-text-field--focused {
.mdc-floating-label--float-above {
color: var(--mdc-theme-secondary);
}
}
.mdc-line-ripple--active {
background-color: var(--mdc-theme-secondary);
}
&.mdc-text-field--outlined,
&.mdc-text-field--textarea {
@include mdc-text-field-focused-outline-color(secondary);
}
}
&.bbmd-mdc-text-field--variant-standard {
@include mdc-text-field-fill-color(transparent);
&:before {
background-color: transparent;
}
}
+ .mdc-text-field-helper-line {
@include mdc-feature-targets($feat-structure) {
padding-right: 0px;
padding-left: 0px;
}
}
}
}

View File

@ -0,0 +1,2 @@
import "./_index.scss"
export { default as textfield } from "./Textfield.svelte"

View File

@ -1,4 +1,5 @@
export { default as h1 } from "./H1.svelte";
export { default as icon } from "./Icon.svelte";
export { button } from "./Button";
export { textfield } from "./Textfield";