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",
|
||||
"license": "MIT",
|
||||
"gitHead": "115189f72a850bfb52b65ec61d932531bf327072"
|
||||
"gitHead": "115189f72a850bfb52b65ec61d932531bf327072",
|
||||
"dependencies": {
|
||||
"@material/textfield": "^4.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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'>
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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}`;
|
||||
}
|
||||
|
|
|
@ -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 = "";
|
||||
|
||||
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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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 };
|
||||
|
||||
|
|
|
@ -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 icon } from "./Icon.svelte";
|
||||
export { button } from "./Button";
|
||||
|
||||
export { textfield } from "./Textfield";
|
||||
|
|
Loading…
Reference in New Issue