Files
windmill/frontend/src/lib/components/InputTransformForm.svelte
T
2022-10-25 00:27:06 +02:00

217 lines
5.9 KiB
Svelte

<script lang="ts">
import type { Schema } from '$lib/common'
import type { InputTransform } from '$lib/gen'
import type { InputCat } from '$lib/utils'
import { getContext } from 'svelte'
import ArgInput from './ArgInput.svelte'
import FieldHeader from './FieldHeader.svelte'
import DynamicInputHelpBox from './flows/content/DynamicInputHelpBox.svelte'
import type { PropPickerWrapperContext } from './flows/propPicker/PropPickerWrapper.svelte'
import { codeToStaticTemplate, getDefaultExpr, isCodeInjection } from './flows/utils'
import SimpleEditor from './SimpleEditor.svelte'
import Toggle from './Toggle.svelte'
import { Button } from './common'
import Icon from 'svelte-awesome'
import { faChain } from '@fortawesome/free-solid-svg-icons'
export let schema: Schema
export let arg: InputTransform | any
export let argName: string
export let extraLib: string = 'missing extraLib'
export let inputCheck: boolean = true
export let importPath: string | undefined = undefined
export let monaco: SimpleEditor | undefined = undefined
let argInput: ArgInput | undefined = undefined
let inputCat: InputCat = 'object'
let propertyType = getPropertyType(arg)
function getPropertyType(arg: InputTransform | any): 'static' | 'javascript' {
let type: 'static' | 'javascript' = arg?.type ?? 'static'
if (type == 'javascript') {
if (codeToStaticTemplate(arg.expr)) {
type = 'static'
}
}
return type
}
function setPropertyType(rawValue: string) {
if (!arg) {
return
}
if (isCodeInjection(rawValue)) {
arg.expr = getDefaultExpr(importPath, argName, `\`${rawValue}\``)
arg.type = 'javascript'
propertyType = 'static'
} else {
if (arg.type === 'javascript' && propertyType === 'static') {
arg.type = 'static'
}
if (arg.type) {
propertyType = arg.type
}
}
}
function isStaticTemplate(inputCat: InputCat) {
return inputCat === 'string' || inputCat === 'sql'
}
function connectProperty(rawValue: string) {
if (isStaticTemplate(inputCat)) {
arg.value = `\$\{${rawValue}}`
setPropertyType(arg.value)
} else {
arg.expr = getDefaultExpr(importPath, undefined, rawValue)
arg.type = 'javascript'
propertyType = 'javascript'
}
}
$: checked = propertyType == 'javascript'
function onFocus() {
{
if (isStaticTemplate(inputCat)) {
focusProp(argName, 'append', (path) => {
const toAppend = `\$\{${path}}`
arg.value = `${arg.value ?? ''}${toAppend}`
setPropertyType(arg.value)
argInput?.focus()
return false
})
} else {
focusProp(argName, 'insert', (path) => {
arg.expr = path
arg.type = 'javascript'
propertyType = 'javascript'
return true
})
}
argInput?.recomputeSize()
}
}
const { focusProp } = getContext<PropPickerWrapperContext>('PropPickerWrapper')
</script>
{#if arg != undefined}
<div class="flex justify-between items-center mb-2">
<div class="flex items-center">
<FieldHeader
label={argName}
format={schema.properties[argName].format}
contentEncoding={schema.properties[argName].contentEncoding}
required={schema.required.includes(argName)}
type={schema.properties[argName].type}
itemsType={schema.properties[argName].items}
/>
{#if !checked && arg.type === 'javascript'}
<span class="bg-blue-100 text-blue-800 text-sm font-medium mr-2 px-2.5 py-0.5 rounded ml-2">
{'${...}'}
</span>
{/if}
</div>
<div class="flex flex-row space-x-2 items-center">
<Toggle
bind:checked
options={{
right: 'Raw Javascript Editor'
}}
on:change={(e) => {
const type = e.detail ? 'javascript' : 'static'
const staticTemplate = isStaticTemplate(inputCat)
if (type === 'javascript') {
arg.expr = getDefaultExpr(
importPath,
argName,
staticTemplate ? `\`${arg.value ?? ''}\`` : arg.value
)
arg.value = undefined
propertyType = 'javascript'
} else {
arg.value = staticTemplate ? codeToStaticTemplate(arg.expr) : undefined
arg.expr = undefined
propertyType = 'static'
}
arg.type = type
}}
/>
<div
on:click={() => {
focusProp(argName, 'connect', (path) => {
connectProperty(path)
return false
})
}}
>
<Button variant="contained" color="blue" size="md">
<Icon data={faChain} />
</Button>
</div>
</div>
</div>
<div class="max-w-xs" />
{#if propertyType === undefined || !checked}
<ArgInput
bind:this={argInput}
on:focus={() => onFocus()}
label={argName}
bind:editor={monaco}
bind:description={schema.properties[argName].description}
bind:value={arg.value}
type={schema.properties[argName].type}
required={schema.required.includes(argName)}
bind:pattern={schema.properties[argName].pattern}
bind:valid={inputCheck}
defaultValue={schema.properties[argName].default}
bind:enum_={schema.properties[argName].enum}
bind:format={schema.properties[argName].format}
contentEncoding={schema.properties[argName].contentEncoding}
bind:itemsType={schema.properties[argName].items}
properties={schema.properties[argName].properties}
displayHeader={false}
bind:inputCat
on:input={(e) => {
if (isStaticTemplate(inputCat)) {
setPropertyType(e.detail.rawValue)
}
}}
/>
{:else if checked}
{#if arg.expr != undefined}
<div class="border rounded p-2 mt-2 border-gray-300">
<SimpleEditor
bind:this={monaco}
on:focus={() => {
focusProp(argName, 'insert', (path) => {
monaco?.insertAtCursor(path)
return false
})
}}
bind:code={arg.expr}
lang="javascript"
class="few-lines-editor"
{extraLib}
extraLibPath="file:///node_modules/@types/windmill@{importPath}/index.d.ts"
shouldBindKey={false}
/>
</div>
<DynamicInputHelpBox {importPath} />
<div class="mb-2" />
{/if}
{:else}
<p>Not recognized arg type {arg.type}</p>
{/if}
{:else}
<p>Arg at {argName} is undefined</p>
{/if}