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:
parent
3fd14da711
commit
8c9618b33b
|
@ -38,5 +38,8 @@
|
||||||
],
|
],
|
||||||
"version": "0.0.15",
|
"version": "0.0.15",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"gitHead": "115189f72a850bfb52b65ec61d932531bf327072"
|
"gitHead": "115189f72a850bfb52b65ec61d932531bf327072",
|
||||||
|
"dependencies": {
|
||||||
|
"@material/textfield": "^4.0.0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,8 @@
|
||||||
<meta charset='utf8'>
|
<meta charset='utf8'>
|
||||||
<meta name='viewport' content='width=device-width'>
|
<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"
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons"
|
||||||
rel="stylesheet">
|
rel="stylesheet">
|
||||||
<link rel='icon' type='image/png' href='/favicon.png'>
|
<link rel='icon' type='image/png' href='/favicon.png'>
|
||||||
|
|
|
@ -5,15 +5,31 @@ import livereload from "rollup-plugin-livereload"
|
||||||
import { terser } from "rollup-plugin-terser"
|
import { terser } from "rollup-plugin-terser"
|
||||||
import json from "rollup-plugin-json"
|
import json from "rollup-plugin-json"
|
||||||
import alias from "rollup-plugin-alias"
|
import alias from "rollup-plugin-alias"
|
||||||
|
import postcss from "rollup-plugin-postcss";
|
||||||
import path from "path"
|
import path from "path"
|
||||||
|
|
||||||
const aliases = {
|
const aliases = {
|
||||||
resolve: [".js", ".svelte"],
|
resolve: [".js", ".svelte"],
|
||||||
entries: [
|
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 production = !process.env.ROLLUP_WATCH
|
||||||
|
|
||||||
const lodash_fp_exports = [
|
const lodash_fp_exports = [
|
||||||
|
@ -142,6 +158,7 @@ export default {
|
||||||
// If we're building for production (npm run build
|
// If we're building for production (npm run build
|
||||||
// instead of npm run dev), minify
|
// instead of npm run dev), minify
|
||||||
production && terser(),
|
production && terser(),
|
||||||
|
postcss(postcssOptions()),
|
||||||
],
|
],
|
||||||
watch: {
|
watch: {
|
||||||
clearScreen: false,
|
clearScreen: false,
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
export let href = ""
|
export let href = ""
|
||||||
export let icon = ""
|
export let icon = ""
|
||||||
export let trailingIcon = false
|
export let trailingIcon = false
|
||||||
export let fullBleed = false
|
export let fullwidth = false
|
||||||
|
|
||||||
export let text = ""
|
export let text = ""
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
|
@ -40,7 +40,7 @@
|
||||||
<button
|
<button
|
||||||
use:ripple={{ colour }}
|
use:ripple={{ colour }}
|
||||||
class={blockClasses}
|
class={blockClasses}
|
||||||
class:fullBleed
|
class:fullwidth
|
||||||
{disabled}
|
{disabled}
|
||||||
on:click>
|
on:click>
|
||||||
{#if renderLeadingIcon}
|
{#if renderLeadingIcon}
|
||||||
|
@ -54,7 +54,7 @@
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.fullBleed {
|
.fullwidth {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -18,15 +18,20 @@ export default class ClassBuilder {
|
||||||
return this.buildClass(base, classParams);
|
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) {
|
buildClass(base, classParams) {
|
||||||
let cls = base;
|
let cls = base;
|
||||||
const { modifiers, customs, extras } = classParams;
|
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)
|
if (!!customs)
|
||||||
cls += Object.entries(customs)
|
cls += Object.entries(customs)
|
||||||
.map(([property, value]) => {
|
.map(([property, value]) => {
|
||||||
//disregard falsy and values set by customDefaults constructor param
|
//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]
|
//custom scss name convention = bbmd-[block | element]--[property]-[value]
|
||||||
return ` bbmd-${base}--${property}-${value}`;
|
return ` bbmd-${base}--${property}-${value}`;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -4,10 +4,9 @@
|
||||||
export let icon = "";
|
export let icon = "";
|
||||||
|
|
||||||
let iconContext = getContext("BBMD:icon:context");
|
let iconContext = getContext("BBMD:icon:context");
|
||||||
let cls =
|
let cls = iconContext
|
||||||
iconContext == "button"
|
? `material-icons mdc-${iconContext}__icon`
|
||||||
? "material-icons mdc-button__icon"
|
: "material-icons";
|
||||||
: "material-icons";
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<i class={cls}>{icon}</i>
|
<i class={cls}>{icon}</i>
|
||||||
|
|
|
@ -9,12 +9,13 @@
|
||||||
|
|
||||||
const testProps = props.justAnH1
|
const testProps = props.justAnH1
|
||||||
const button = props.button
|
const button = props.button
|
||||||
|
const textfield = props.textfield
|
||||||
|
|
||||||
let currentComponent
|
let currentComponent
|
||||||
|
|
||||||
$: {
|
$: {
|
||||||
if (_bb && currentComponent) {
|
if (_bb && currentComponent) {
|
||||||
_bb.hydrateChildren([testProps, button], currentComponent)
|
_bb.hydrateChildren([testProps, button, textfield], currentComponent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -11,6 +11,7 @@ export default async () => {
|
||||||
const appDef = { hierarchy: {}, actions: {} }
|
const appDef = { hierarchy: {}, actions: {} }
|
||||||
const user = { name: "yeo", permissions: [] }
|
const user = { name: "yeo", permissions: [] }
|
||||||
|
|
||||||
var app = createApp(componentLibraries, appDef, user)
|
var app = createApp(window.document, componentLibraries, appDef, user, {})
|
||||||
|
|
||||||
return app
|
return app
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
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",
|
||||||
|
@ -13,7 +15,7 @@ export const props = {
|
||||||
href: "",
|
href: "",
|
||||||
icon: "alarm_on",
|
icon: "alarm_on",
|
||||||
trailingIcon: true,
|
trailingIcon: true,
|
||||||
fullBleed: false,
|
fullwidth: false,
|
||||||
text: "I am button",
|
text: "I am button",
|
||||||
disabled: false
|
disabled: false
|
||||||
},
|
},
|
||||||
|
@ -21,6 +23,16 @@ export const props = {
|
||||||
_component: "@budibase/materialdesign-components/icon",
|
_component: "@budibase/materialdesign-components/icon",
|
||||||
_children: [],
|
_children: [],
|
||||||
icon: ""
|
icon: ""
|
||||||
|
},
|
||||||
|
textfield: {
|
||||||
|
_component: "@budibase/materialdesign-components/textfield",
|
||||||
|
_children: [],
|
||||||
|
label: "Surname",
|
||||||
|
icon: "alarm_on",
|
||||||
|
variant: "outlined",
|
||||||
|
helperText: "Add Surname",
|
||||||
|
textarea: true,
|
||||||
|
useCharCounter: true
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import h1 from "../H1.svelte";
|
import h1 from "../H1.svelte";
|
||||||
import { button, icon } from "@BBMD";
|
import { button, icon, textfield } from "@BBMD";
|
||||||
|
export default { h1, button, icon, textfield };
|
||||||
export default { h1, button, icon };
|
|
||||||
|
|
||||||
|
|
|
@ -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>
|
|
@ -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();
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
import "./_index.scss"
|
||||||
|
export { default as textfield } from "./Textfield.svelte"
|
|
@ -1,4 +1,5 @@
|
||||||
export { default as h1 } from "./H1.svelte";
|
export { default as h1 } from "./H1.svelte";
|
||||||
|
|
||||||
export { default as icon } from "./Icon.svelte";
|
export { default as icon } from "./Icon.svelte";
|
||||||
export { button } from "./Button";
|
export { button } from "./Button";
|
||||||
|
export { textfield } from "./Textfield";
|
||||||
|
|
Loading…
Reference in New Issue