budibase/packages/materialdesign-components/src/Textfield/Textfield.svelte

196 lines
5.0 KiB
Svelte

<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 HelperText from "./HelperText.svelte"
import CharacterCounter from "./CharacterCounter.svelte"
import Icon from "../Common/Icon.svelte"
import { IconButton } from "../IconButton"
const cb = new ClassBuilder("text-field", ["primary", "medium"])
let tf = null
export let tfHeight = null
let tfInstance = null
onMount(() => {
if (!!tf) tfInstance = new MDCTextField(tf)
return () => {
!!tfInstance && tf.tfInstance && tf.tfInstance.destroy()
tf = null
}
})
export let onChange = text => {}
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 = null
export let maxLength = null
export let helperText = ""
export let errorText = ""
export let placeholder = ""
export let icon = ""
export let trailingIcon = false
export let useIconButton = false
export let iconButtonClick = () => {}
export let textarea = false
export let validation = false
export let persistent = false
export let value
export let _bb
let id = `${label}-${variant}`
let modifiers = { fullwidth, disabled, textarea }
let customs = { colour }
if (variant == "standard" || fullwidth) {
customs = { ...customs, variant }
} else {
modifiers = { ...modifiers, variant }
}
if (!textarea && size !== "medium") {
customs = { ...customs, size }
}
if (!label || fullwidth) {
modifiers = { ...modifiers, noLabel: "no-label" }
}
let useLabel = !!label && (!fullwidth || (fullwidth && textarea))
let useIcon = !!icon && !textarea && !fullwidth
if (useIcon) {
let iconClass = trailingIcon ? "with-trailing-icon" : "with-leading-icon"
modifiers = { ...modifiers, iconClass }
}
$: useNotchedOutline = variant == "outlined" || textarea
$: renderLeadingIcon = useIcon && !trailingIcon
$: renderTrailingIcon = useIcon && trailingIcon
$: safeMaxLength = maxLength <= 0 ? undefined : maxLength
let props = { modifiers, customs }
const blockClasses = cb.build({ props })
const inputClasses = cb.elem("input")
function focus(event) {
tfInstance.focus()
}
function changed(e) {
const val = e.target.value
value = val
if (_bb.isBound(_bb.props.value)) {
_bb.setStateFromBinding(_bb.props.value, val)
}
_bb.call(onChange, val)
}
</script>
<!--
TODO:Needs error handling - this will depend on how Budibase handles errors
-->
<div class="textfield-container" class:fullwidth>
<div bind:this={tf} bind:clientHeight={tfHeight} class={blockClasses}>
{#if textarea}
{#if maxLength}
<CharacterCounter />
{/if}
<textarea
{id}
class={inputClasses}
class:fullwidth
{disabled}
rows="5"
cols="70"
{required}
{placeholder}
{value}
{minLength}
maxlength={safeMaxLength}
on:change={changed} />
{:else}
{#if renderLeadingIcon}
{#if useIconButton}
<IconButton
{_bb}
{icon}
context="mdc-text-field__icon mdc-text-field__icon--leading"
onClick={iconButtonClick} />
{:else}
<Icon context="text-field__icon" {icon} />
{/if}
{/if}
<input
{id}
{disabled}
class={inputClasses}
{type}
{required}
{minLength}
maxLength={safeMaxLength}
placeholder={!!label && fullwidth ? label : placeholder}
{value}
aria-label={`Textfield ${variant}`}
on:focus={focus}
on:input={changed} />
{#if renderTrailingIcon}
{#if useIconButton}
<IconButton
{_bb}
{icon}
context="mdc-text-field__icon mdc-text-field__icon--trailing"
onClick={iconButtonClick} />
{:else}
<Icon context="text-field__icon" {icon} />
{/if}
{/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>
<HelperText
{persistent}
{validation}
{errorText}
{helperText}
useCharCounter={!!maxLength && !textarea} />
</div>
<style>
.textfield-container {
padding: 8px;
display: flex;
flex-direction: column;
width: 227px;
}
.fullwidth {
width: 100%;
}
</style>