feat(frontend): Added support for title and placeholder for ArgInputs (#3779)

* feat(frontend): Added support for customLabel and placeholder

* feat(frontend): correctly handle labels

* feat(frontend): rename customLabel into title

* feat(frontend): fix layout

* feat(frontend): properly implement placeholder

* feat(frontend): properly implement placeholder

* feat(frontend): add missing undefined check

* feat(frontend): remove code duplication
This commit is contained in:
Faton Ramadani
2024-05-21 21:58:48 +02:00
committed by GitHub
parent 36df8389cd
commit 59c0abae3d
13 changed files with 150 additions and 38 deletions
+7 -1
View File
@@ -41,6 +41,8 @@ export interface SchemaProperty {
order?: string[]
nullable?: boolean
dateFormat?: string
title?: string
placeholder?: string
}
export interface ModalSchemaProperty {
@@ -65,6 +67,8 @@ export interface ModalSchemaProperty {
password?: boolean
nullable?: boolean
dateFormat?: string
title?: string
placeholder?: string
}
export function modalToSchema(schema: ModalSchemaProperty): SchemaProperty {
@@ -88,7 +92,9 @@ export function modalToSchema(schema: ModalSchemaProperty): SchemaProperty {
showExpr: schema.showExpr,
password: schema.password,
nullable: schema.nullable,
dateFormat: schema.dateFormat
dateFormat: schema.dateFormat,
title: schema.title,
placeholder: schema.placeholder
}
}
export type Schema = {
+30 -8
View File
@@ -1,6 +1,6 @@
<script lang="ts">
import type { SchemaProperty } from '$lib/common'
import { setInputCat as computeInputCat, emptyString } from '$lib/utils'
import { setInputCat as computeInputCat, emptyString, shouldDisplayPlaceholder } from '$lib/utils'
import { ChevronDown, DollarSign, Pipette, Plus, X } from 'lucide-svelte'
import { createEventDispatcher } from 'svelte'
import Multiselect from 'svelte-multiselect'
@@ -30,6 +30,8 @@
import autosize from '$lib/autosize'
import PasswordArgInput from './PasswordArgInput.svelte'
import Password from './Password.svelte'
import Label from './Label.svelte'
import Tooltip from './Tooltip.svelte'
export let label: string = ''
export let value: any
@@ -74,6 +76,8 @@
export let customErrorMessage: string | undefined = undefined
export let onlyMaskPassword = false
export let nullable: boolean = false
export let title: string | undefined = undefined
export let placeholder: string | undefined = undefined
let seeEditable: boolean = enum_ != undefined || pattern != undefined
const dispatch = createEventDispatcher()
@@ -233,7 +237,7 @@
{#if displayHeader}
<FieldHeader
prettify={prettifyHeader}
{label}
label={title && !emptyString(title) ? title : label}
{disabled}
{required}
{type}
@@ -241,6 +245,7 @@
{format}
{simpleTooltip}
/>
{#if editableSchema}
<span class="mx-8" />
{#if editableSchema.i > 0}
@@ -260,7 +265,7 @@
{/if}
{/if}
{#if editableSchema}
<label class="text-secondary">
<Label label="Description">
<textarea
class="mb-1"
use:autosize
@@ -269,7 +274,15 @@
on:keydown={onKeyDown}
placeholder="Field description"
/>
</label>
</Label>
<div class="flex flex-row gap-2 w-full">
<Label label="Custom Title" class="w-full">
<svelte:fragment slot="header">
<Tooltip light>Will be displayed in the UI instead of the field name.</Tooltip>
</svelte:fragment>
<input class="mb-1" bind:value={title} on:keydown={onKeyDown} placeholder="Field title" />
</Label>
</div>
{#if type == 'array'}
<ArrayTypeNarrowing bind:itemsType />
@@ -317,6 +330,11 @@
<ObjectTypeNarrowing bind:format />
{/if}
</div>
{#if shouldDisplayPlaceholder(type, format, enum_, contentEncoding, pattern)}
<Label label="Placeholder" class="pt-2">
<textarea placeholder="Enter a placeholder" rows="1" bind:value={placeholder} />
</Label>
{/if}
{#if !required && type === 'string'}
<div class="mt-2 border-t pt-4">
<Toggle
@@ -381,7 +399,7 @@
class={valid
? ''
: 'border border-red-700 border-opacity-30 focus:border-red-700 focus:border-opacity-30 bg-red-100'}
placeholder={defaultValue ?? ''}
placeholder={placeholder ?? defaultValue ?? ''}
bind:value
min={extra['min']}
max={extra['max']}
@@ -680,7 +698,7 @@
class={valid
? ''
: 'border border-red-700 border-opacity-30 focus:border-red-700 focus:border-opacity-3'}
placeholder={defaultValue ?? ''}
placeholder={placeholder ?? defaultValue ?? ''}
bind:value
/>
{:else if inputCat == 'string'}
@@ -688,7 +706,11 @@
<div class="flex flex-row w-full items-center justify-between relative">
{#if password || extra?.['password'] == true}
{#if onlyMaskPassword}
<Password {disabled} bind:password={value} placeholder={defaultValue ?? ''} />
<Password
{disabled}
bind:password={value}
placeholder={placeholder ?? defaultValue ?? ''}
/>
{:else}
<PasswordArgInput {disabled} bind:value />
{/if}
@@ -713,7 +735,7 @@
? ''
: 'border border-red-700 border-opacity-30 focus:border-red-700 focus:border-opacity-3'
)}
placeholder={defaultValue ?? ''}
placeholder={placeholder ?? defaultValue ?? ''}
bind:value
/>
{/key}
@@ -152,6 +152,8 @@
bind:extra={schema.properties[argName]}
simpleTooltip={schemaFieldTooltip[argName]}
nullable={schema.properties[argName].nullable}
bind:title={schema.properties[argName].title}
bind:placeholder={schema.properties[argName].placeholder}
/>
{/if}
{/if}
@@ -407,6 +407,8 @@
bind:pickForField
showSchemaExplorer
nullable={schema.properties[argName].nullable}
bind:title={schema.properties[argName].title}
bind:placeholder={schema.properties[argName].placeholder}
/>
{:else if arg.expr != undefined}
<div class="border mt-2">
@@ -51,6 +51,8 @@
export let hideResourceInput: boolean = false
export let resourceInputUnsupported: boolean = false
export let render = true
export let title: string | undefined = undefined
export let placeholder: string | undefined = undefined
const dispatch = createEventDispatcher()
@@ -180,8 +182,8 @@
<div>
{#if displayHeader}
<FieldHeader
prettify
{label}
prettify={emptyString(title)}
label={title && !emptyString(title) ? title : label}
{required}
{type}
{contentEncoding}
@@ -231,7 +233,7 @@
? ''
: 'border !border-red-700 !border-opacity-70 focus:!border-red-700 focus:!border-opacity-30'
)}
placeholder={defaultValue ?? ''}
placeholder={placeholder ?? defaultValue ?? ''}
bind:value
min={extra['min']}
max={extra['max']}
@@ -430,7 +432,7 @@
class={valid
? ''
: 'border border-red-700 border-opacity-30 focus:border-red-700 focus:border-opacity-3'}
placeholder={defaultValue ?? ''}
placeholder={placeholder ?? defaultValue ?? ''}
bind:value
/>
{:else if inputCat == 'currency'}
@@ -439,7 +441,7 @@
class={valid
? ''
: 'border border-red-700 border-opacity-30 focus:border-red-700 focus:border-opacity-3'}
placeholder={defaultValue ?? ''}
placeholder={placeholder ?? defaultValue ?? ''}
bind:value
/>
{:else if inputCat == 'string'}
@@ -458,7 +460,7 @@
class="col-span-10 {valid && error == ''
? ''
: 'border !border-red-700 !border-opacity-70 focus:!border-red-700 focus:!border-opacity-30'}"
placeholder={defaultValue ?? ''}
placeholder={placeholder ?? defaultValue ?? ''}
bind:value
on:pointerdown|stopPropagation={(e) => {
dispatch('inputClicked', e)
@@ -86,6 +86,8 @@
nestedRequired={schema.properties[argName].required}
itemsType={schema.properties[argName].items}
extra={schema.properties[argName]}
title={schema.properties[argName].title}
placeholder={schema.properties[argName].placeholder}
on:inputClicked
{displayType}
{css}
@@ -96,6 +96,8 @@
itemsType={schema.properties[argName].items}
extra={schema.properties[argName]}
nullable={schema.properties[argName].nullable}
title={schema.properties[argName].title}
placeholder={schema.properties[argName].placeholder}
/>
{/if}
<div class="pt-6 mt-0.5">
@@ -159,6 +159,8 @@
simpleTooltip={schemaFieldTooltip[argName]}
{onlyMaskPassword}
nullable={schema.properties[argName].nullable}
title={schema.properties[argName].title}
placeholder={schema.properties[argName].placeholder}
>
<svelte:fragment slot="actions">
{#if linkedSecretCandidates?.includes(argName)}
+34 -1
View File
@@ -62,7 +62,9 @@
showExpr: schema.showExpr,
password: schema.password,
nullable: schema.nullable,
dateFormat: schema.format
dateFormat: schema.format,
title: schema.title,
placeholder: schema.placeholder
}
}
@@ -88,6 +90,8 @@
import LightweightSchemaForm from './LightweightSchemaForm.svelte'
import NumberTypeNarrowing from './NumberTypeNarrowing.svelte'
import SimpleEditor from './SimpleEditor.svelte'
import Label from './Label.svelte'
import { shouldDisplayPlaceholder } from '$lib/utils'
export let error = ''
export let editing = false
@@ -138,6 +142,8 @@
property.password = undefined
property.nullable = false
property.dateFormat = undefined
property.title = undefined
property.placeholder = undefined
drawer.closeDrawer()
}
@@ -157,6 +163,16 @@
return []
}
function shouldDisplayPlaceholderForProperty(property: ModalSchemaProperty): boolean {
return shouldDisplayPlaceholder(
property.selectedType,
property.format,
property.enum_,
property.contentEncoding,
property.pattern
)
}
</script>
<Drawer bind:this={drawer} placement="right">
@@ -192,6 +208,11 @@
</div>
<textarea placeholder="Enter a description" rows="3" bind:value={property.description} />
</label>
<label class="block">
<div class="mb-1 font-semibold text-secondary"> Custom Title </div>
<textarea placeholder="Enter a custom title" rows="1" bind:value={property.title} />
</label>
<div>
<div class="mb-1 font-semibold text-secondary">Type<Required required={true} /></div>
<div class="grid sm:grid-cols-3 md:grid-cols-4 gap-x-2 gap-y-1 items-center mb-2 w-full">
@@ -266,6 +287,8 @@
extra={property}
disabled={property.password}
nullable={property.nullable}
title={property.title}
placeholder={property.placeholder}
/>
<div>
<Toggle
@@ -341,6 +364,16 @@
</svelte:fragment>
</Tabs>
{/if}
{#if property && shouldDisplayPlaceholderForProperty(property)}
<Label label="Placeholder" class="pt-2">
<textarea
placeholder="Enter a placeholder"
rows="1"
bind:value={property.placeholder}
/>
</Label>
{/if}
<div class="pt-2">
<Toggle
options={{ right: 'Show this field only when conditions are met' }}
@@ -1,4 +1,5 @@
<script lang="ts">
import { computeKind } from '$lib/utils'
import Label from './Label.svelte'
import RadioButton from './RadioButton.svelte'
import ResourceTypePicker from './ResourceTypePicker.svelte'
@@ -21,7 +22,13 @@
export let noExtra = false
export let dateFormat: string | undefined
let kind: 'none' | 'pattern' | 'enum' | 'resource' | 'format' | 'base64' = computeKind()
let kind: 'none' | 'pattern' | 'enum' | 'resource' | 'format' | 'base64' = computeKind(
enum_,
contentEncoding,
pattern,
format
)
let patternStr: string = pattern ?? ''
let resource: string | undefined
@@ -64,24 +71,6 @@
}
}
function computeKind(): 'base64' | 'none' | 'pattern' | 'enum' | 'resource' | 'format' {
if (enum_ != undefined) {
return 'enum'
}
if (contentEncoding == 'base64') {
return 'base64'
}
if (pattern != undefined) {
return 'pattern'
}
if (format != undefined && format != '') {
if (format.startsWith('resource')) {
return 'resource'
}
return 'format'
}
return 'none'
}
const presetOptions = [
{ label: 'ISO Format', format: 'yyyy-MM-dd' },
{ label: 'US Format', format: 'MM/dd/yyyy' },
@@ -52,7 +52,9 @@
...componentInput,
type: 'evalv2',
expr: expr,
connections: [{ componentId: connection.componentId, id: connection.path.split('.')[0].split('[')[0] }]
connections: [
{ componentId: connection.componentId, id: connection.path.split('.')[0].split('[')[0] }
]
}
evalV2editor?.setCode(expr)
$app = $app
+46
View File
@@ -851,3 +851,49 @@ export function getLocalSetting(name: string) {
return undefined
}
}
export function computeKind(
enum_: string[] | undefined,
contentEncoding: 'base64' | 'binary' | undefined,
pattern: string | undefined,
format: string | undefined
): 'base64' | 'none' | 'pattern' | 'enum' | 'resource' | 'format' {
if (enum_ != undefined) {
return 'enum'
}
if (contentEncoding == 'base64') {
return 'base64'
}
if (pattern != undefined) {
return 'pattern'
}
if (format != undefined && format != '') {
if (format?.startsWith('resource')) {
return 'resource'
}
return 'format'
}
return 'none'
}
// Used to check whether a placeholder should be displayed in the input field, based on the schema
export function shouldDisplayPlaceholder(
type: string | undefined,
format: string | undefined,
enum_: string[] | undefined,
contentEncoding: 'base64' | 'binary' | undefined,
pattern: string | undefined
): boolean {
if (type === 'string') {
const kind = computeKind(enum_, contentEncoding, pattern, format)
if (kind === 'format' && format) {
const whiteList = ['email', 'hostname', 'ipv4', 'uri', 'uuid']
return whiteList.includes(format)
}
return kind === 'none' || kind === 'pattern'
}
return type === 'number' || type === 'integer' || type === undefined
}
@@ -5,7 +5,7 @@
import { defaultScripts, workspaceStore } from '$lib/stores'
import ScriptBuilder from '$lib/components/ScriptBuilder.svelte'
import type { Schema } from '$lib/common'
import { decodeState, emptySchema } from '$lib/utils'
import { decodeState, emptySchema, emptyString } from '$lib/utils'
import { goto } from '$app/navigation'
import UnsavedConfirmationModal from '$lib/components/common/confirmationModal/UnsavedConfirmationModal.svelte'
@@ -57,7 +57,9 @@
workspace: $workspaceStore!,
path: templatePath
})
script.summary = `Copy of ${template.summary}`
// Only copy the summary if it's not empty
script.summary = !emptyString(template.summary) ? `Copy of ${template.summary}` : ''
script.description = template.description
script.content = template.content
script.schema = template.schema