mirror of https://github.com/Budibase/budibase.git
Browse Source
* Beginnings of text field * Completed textfield component and its dependents * Minor bug fixespull/4023/head
committed by
GitHub
17 changed files with 304 additions and 19 deletions
@ -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> |
|||
@ -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