209 lines
5.2 KiB
Svelte
209 lines
5.2 KiB
Svelte
<script>
|
|
import Placeholder from "../Placeholder.svelte"
|
|
import { getContext, onDestroy } from "svelte"
|
|
import { Icon } from "@budibase/bbui"
|
|
import InnerForm from "./InnerForm.svelte"
|
|
import { writable } from "svelte/store"
|
|
import Provider from "components/context/Provider.svelte"
|
|
|
|
export let label
|
|
export let field
|
|
export let fieldState
|
|
export let fieldApi
|
|
export let fieldSchema
|
|
export let defaultValue
|
|
export let type
|
|
export let disabled = false
|
|
export let readonly = false
|
|
export let validation
|
|
export let span = 6
|
|
export let helpText = null
|
|
|
|
// Get contexts
|
|
const formContext = getContext("form")
|
|
const formStepContext = getContext("form-step")
|
|
const fieldGroupContext = getContext("field-group")
|
|
const { styleable, builderStore } = getContext("sdk")
|
|
const component = getContext("component")
|
|
|
|
// Register field with form
|
|
const formApi = formContext?.formApi
|
|
const labelPos = fieldGroupContext?.labelPosition || "above"
|
|
|
|
let touched = false
|
|
let labelNode
|
|
|
|
$: formStep = formStepContext ? $formStepContext || 1 : 1
|
|
$: formField = formApi?.registerField(
|
|
field,
|
|
type,
|
|
defaultValue,
|
|
disabled,
|
|
readonly,
|
|
validation,
|
|
formStep
|
|
)
|
|
$: schemaType =
|
|
fieldSchema?.type !== "formula" && fieldSchema?.type !== "bigint"
|
|
? fieldSchema?.type
|
|
: "string"
|
|
|
|
// Focus label when editing
|
|
$: $component.editing && labelNode?.focus()
|
|
|
|
// Update form properties in parent component on every store change
|
|
$: unsubscribe = formField?.subscribe(value => {
|
|
fieldState = value?.fieldState
|
|
fieldApi = value?.fieldApi
|
|
fieldSchema = value?.fieldSchema
|
|
})
|
|
|
|
// Determine label class from position
|
|
$: labelClass = labelPos === "above" ? "" : `spectrum-FieldLabel--${labelPos}`
|
|
|
|
const updateLabel = e => {
|
|
if (touched) {
|
|
builderStore.actions.updateProp("label", e.target.textContent)
|
|
}
|
|
touched = false
|
|
}
|
|
|
|
onDestroy(() => {
|
|
fieldApi?.deregister()
|
|
unsubscribe?.()
|
|
})
|
|
</script>
|
|
|
|
{#if !formContext}
|
|
<!-- Cant support attachments -->
|
|
<Provider data={{ value: fieldState?.value }}>
|
|
<InnerForm
|
|
{disabled}
|
|
{readonly}
|
|
currentStep={writable(1)}
|
|
provideContext={false}
|
|
>
|
|
<svelte:self {...$$props} bind:fieldState bind:fieldApi bind:fieldSchema>
|
|
<slot />
|
|
</svelte:self>
|
|
</InnerForm>
|
|
</Provider>
|
|
{:else}
|
|
<div
|
|
class="spectrum-Form-item"
|
|
class:span-2={span === 2}
|
|
class:span-3={span === 3}
|
|
class:span-6={span === 6 || !span}
|
|
use:styleable={$component.styles}
|
|
class:above={labelPos === "above"}
|
|
>
|
|
{#key $component.editing}
|
|
<label
|
|
bind:this={labelNode}
|
|
contenteditable={$component.editing}
|
|
on:blur={$component.editing ? updateLabel : null}
|
|
on:input={() => (touched = true)}
|
|
class:hidden={!label}
|
|
class:readonly
|
|
for={fieldState?.fieldId}
|
|
class={`spectrum-FieldLabel spectrum-FieldLabel--sizeM spectrum-Form-itemLabel ${labelClass}`}
|
|
>
|
|
{label || " "}
|
|
</label>
|
|
{/key}
|
|
<div class="spectrum-Form-itemField">
|
|
{#if !fieldState}
|
|
<Placeholder />
|
|
{:else if schemaType && schemaType !== type && !["options", "longform"].includes(type)}
|
|
<Placeholder
|
|
text="This Field setting is the wrong data type for this component"
|
|
/>
|
|
{:else}
|
|
<slot />
|
|
{#if fieldState.error}
|
|
<div class="error">
|
|
<Icon name="Alert" />
|
|
<span>{fieldState.error}</span>
|
|
</div>
|
|
{:else if helpText}
|
|
<div class="helpText">
|
|
<Icon name="HelpOutline" /> <span>{helpText}</span>
|
|
</div>
|
|
{/if}
|
|
{/if}
|
|
</div>
|
|
</div>
|
|
{/if}
|
|
|
|
<style>
|
|
:global(.form-block .spectrum-Form-item.span-2) {
|
|
grid-column: span 2;
|
|
}
|
|
:global(.form-block .spectrum-Form-item.span-3) {
|
|
grid-column: span 3;
|
|
}
|
|
:global(.form-block .spectrum-Form-item.span-6) {
|
|
grid-column: span 6;
|
|
}
|
|
.spectrum-Form-item.above {
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
label {
|
|
word-wrap: break-word;
|
|
}
|
|
label.hidden {
|
|
padding: 0;
|
|
}
|
|
.spectrum-Form-itemField {
|
|
position: relative;
|
|
width: 100%;
|
|
}
|
|
|
|
.error :global(svg),
|
|
.helpText :global(svg) {
|
|
width: 13px;
|
|
margin-right: 6px;
|
|
}
|
|
|
|
.error {
|
|
display: flex;
|
|
margin-top: var(--spectrum-global-dimension-size-75);
|
|
align-items: center;
|
|
}
|
|
.error :global(svg) {
|
|
color: var(
|
|
--spectrum-semantic-negative-color-default,
|
|
var(--spectrum-global-color-red-500)
|
|
);
|
|
}
|
|
.error span {
|
|
color: var(
|
|
--spectrum-semantic-negative-color-default,
|
|
var(--spectrum-global-color-red-500)
|
|
);
|
|
font-size: var(--spectrum-global-dimension-font-size-75);
|
|
}
|
|
|
|
.helpText {
|
|
display: flex;
|
|
margin-top: var(--spectrum-global-dimension-size-75);
|
|
align-items: center;
|
|
}
|
|
.helpText :global(svg) {
|
|
color: var(--spectrum-global-color-gray-600);
|
|
}
|
|
.helpText span {
|
|
color: var(--spectrum-global-color-gray-800);
|
|
font-size: var(--spectrum-global-dimension-font-size-75);
|
|
}
|
|
|
|
.spectrum-FieldLabel--right,
|
|
.spectrum-FieldLabel--left {
|
|
padding-right: var(--spectrum-global-dimension-size-200);
|
|
}
|
|
.readonly {
|
|
pointer-events: none;
|
|
}
|
|
</style>
|