mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-11 16:09:39 +00:00
feat(apps): add action on form/button/formbutton
This commit is contained in:
@@ -4,13 +4,13 @@
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { AppViewerContext, ComponentCustomCSS, RichConfigurations } from '../../types'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import type RunnableComponent from '../helpers/RunnableComponent.svelte'
|
||||
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
|
||||
import { loadIcon } from '../icon'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { initConfig, initOutput } from '../../editor/appUtils'
|
||||
import { components, configurationKeys } from '../../editor/component'
|
||||
import { components } from '../../editor/component'
|
||||
import ResolveConfig from '../helpers/ResolveConfig.svelte'
|
||||
|
||||
export let id: string
|
||||
export let componentInput: AppInput | undefined
|
||||
@@ -111,26 +111,27 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
{#each configurationKeys['buttoncomponent'] as key (key)}
|
||||
<InputValue
|
||||
key={key + extraKey}
|
||||
{#each Object.keys(components['buttoncomponent'].initialData.configuration) as key (key)}
|
||||
<ResolveConfig
|
||||
{id}
|
||||
input={configuration[key]}
|
||||
bind:value={resolvedConfig[key]}
|
||||
{extraKey}
|
||||
{key}
|
||||
bind:resolvedConfig={resolvedConfig[key]}
|
||||
configuration={configuration[key]}
|
||||
/>
|
||||
{/each}
|
||||
|
||||
<!-- gotoNewTab={resolvedConfig.onSuccess.selected == 'goto'} -->
|
||||
|
||||
<RunnableWrapper
|
||||
bind:this={runnableWrapper}
|
||||
{recomputeIds}
|
||||
bind:runnableComponent
|
||||
{componentInput}
|
||||
doOnSuccess={resolvedConfig.onSuccess}
|
||||
{id}
|
||||
{extraQueryParams}
|
||||
autoRefresh={false}
|
||||
gotoUrl={resolvedConfig.goto}
|
||||
gotoNewTab={resolvedConfig.gotoNewTab}
|
||||
setTab={resolvedConfig.setTab}
|
||||
{render}
|
||||
{outputs}
|
||||
{extraKey}
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
import { faUser } from '@fortawesome/free-solid-svg-icons'
|
||||
import { getContext } from 'svelte'
|
||||
import { Icon } from 'svelte-awesome'
|
||||
import { initOutput } from '../../editor/appUtils'
|
||||
import { initConfig, initOutput } from '../../editor/appUtils'
|
||||
import { components } from '../../editor/component'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppViewerContext, ComponentCustomCSS, RichConfigurations } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import ResolveConfig from '../helpers/ResolveConfig.svelte'
|
||||
import type RunnableComponent from '../helpers/RunnableComponent.svelte'
|
||||
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
|
||||
|
||||
@@ -31,12 +31,8 @@
|
||||
loading: false
|
||||
})
|
||||
|
||||
let labelValue: string = ''
|
||||
let color: ButtonType.Color
|
||||
let size: ButtonType.Size
|
||||
let resolvedConfig = initConfig(components['formcomponent'].initialData.configuration)
|
||||
let runnableComponent: RunnableComponent
|
||||
let goto: string | undefined = undefined
|
||||
let setTab: Array<{ id: string; index: number }> | undefined = undefined
|
||||
|
||||
let isLoading: boolean = false
|
||||
|
||||
@@ -58,11 +54,14 @@
|
||||
$: css = concatCustomCss($app.css?.formcomponent, customCss)
|
||||
</script>
|
||||
|
||||
<InputValue {id} input={configuration.goto} bind:value={goto} />
|
||||
<InputValue {id} input={configuration.label} bind:value={labelValue} />
|
||||
<InputValue {id} input={configuration.color} bind:value={color} />
|
||||
<InputValue {id} input={configuration.size} bind:value={size} />
|
||||
<InputValue {id} input={configuration.setTab} bind:value={setTab} />
|
||||
{#each Object.keys(components['formcomponent'].initialData.configuration) as key (key)}
|
||||
<ResolveConfig
|
||||
{id}
|
||||
{key}
|
||||
bind:resolvedConfig={resolvedConfig[key]}
|
||||
configuration={configuration[key]}
|
||||
/>
|
||||
{/each}
|
||||
|
||||
<RunnableWrapper
|
||||
{recomputeIds}
|
||||
@@ -70,13 +69,12 @@
|
||||
bind:runnableComponent
|
||||
{componentInput}
|
||||
{id}
|
||||
gotoUrl={goto}
|
||||
doOnSuccess={resolvedConfig.onSuccess}
|
||||
{extraQueryParams}
|
||||
autoRefresh={false}
|
||||
forceSchemaDisplay={true}
|
||||
runnableClass="!block"
|
||||
runnableStyle={css?.container?.style}
|
||||
{setTab}
|
||||
{outputs}
|
||||
>
|
||||
<AlignWrapper {horizontalAlignment}>
|
||||
@@ -110,10 +108,10 @@
|
||||
on:click={() => {
|
||||
runnableComponent?.runComponent()
|
||||
}}
|
||||
{size}
|
||||
{color}
|
||||
size={resolvedConfig.size}
|
||||
color={resolvedConfig.color}
|
||||
>
|
||||
{labelValue}
|
||||
{resolvedConfig.label}
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -13,7 +13,9 @@
|
||||
import Portal from 'svelte-portal'
|
||||
import Modal from '$lib/components/common/modal/Modal.svelte'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import { initOutput } from '../../editor/appUtils'
|
||||
import { initConfig, initOutput } from '../../editor/appUtils'
|
||||
import { components } from '../../editor/component'
|
||||
import ResolveConfig from '../helpers/ResolveConfig.svelte'
|
||||
|
||||
export let id: string
|
||||
export let componentInput: AppInput | undefined
|
||||
@@ -32,12 +34,8 @@
|
||||
loading: false
|
||||
})
|
||||
|
||||
let labelValue: string = ''
|
||||
let color: ButtonType.Color
|
||||
let size: ButtonType.Size
|
||||
let resolvedConfig = initConfig(components['formbuttoncomponent'].initialData.configuration)
|
||||
let runnableComponent: RunnableComponent
|
||||
let disabled: boolean | undefined = undefined
|
||||
let setTab: Array<{ id: string; index: number }> | undefined = undefined
|
||||
|
||||
let isLoading: boolean = false
|
||||
let ownClick: boolean = false
|
||||
@@ -71,21 +69,19 @@
|
||||
$: css = concatCustomCss($app?.css?.formbuttoncomponent, customCss)
|
||||
</script>
|
||||
|
||||
<InputValue {id} input={configuration.label} bind:value={labelValue} />
|
||||
<InputValue {id} input={configuration.color} bind:value={color} />
|
||||
<InputValue {id} input={configuration.size} bind:value={size} />
|
||||
<InputValue
|
||||
{id}
|
||||
input={configuration.disabled}
|
||||
bind:value={disabled}
|
||||
bind:error={errors.disabled}
|
||||
/>
|
||||
<InputValue {id} input={configuration.setTab} bind:value={setTab} />
|
||||
{#each Object.keys(components['formbuttoncomponent'].initialData.configuration) as key (key)}
|
||||
<ResolveConfig
|
||||
{id}
|
||||
{key}
|
||||
bind:resolvedConfig={resolvedConfig[key]}
|
||||
configuration={configuration[key]}
|
||||
/>
|
||||
{/each}
|
||||
|
||||
<Portal>
|
||||
<Modal
|
||||
{open}
|
||||
title={labelValue}
|
||||
title={resolvedConfig.label ?? ''}
|
||||
class={css?.popup?.class}
|
||||
style={css?.popup?.style}
|
||||
on:canceled={() => {
|
||||
@@ -106,7 +102,7 @@
|
||||
forceSchemaDisplay={true}
|
||||
runnableClass="!block"
|
||||
{outputs}
|
||||
{setTab}
|
||||
doOnSuccess={resolvedConfig.onSuccess}
|
||||
>
|
||||
<div class="flex flex-col gap-2 px-4 w-full">
|
||||
<div>
|
||||
@@ -151,15 +147,15 @@
|
||||
<div class="text-red-500 text-xs">{errorsMessage}</div>
|
||||
{/if}
|
||||
<Button
|
||||
{disabled}
|
||||
{size}
|
||||
{color}
|
||||
disabled={resolvedConfig.disabled ?? false}
|
||||
size={resolvedConfig.size ?? 'md'}
|
||||
color={resolvedConfig.color}
|
||||
btnClasses={css?.button?.class ?? ''}
|
||||
style={css?.button?.style ?? ''}
|
||||
on:click={(e) => {
|
||||
open = true
|
||||
}}
|
||||
>
|
||||
{labelValue}
|
||||
{resolvedConfig.label}
|
||||
</Button>
|
||||
</AlignWrapper>
|
||||
|
||||
@@ -2,14 +2,11 @@
|
||||
import { getContext } from 'svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { initConfig, initOutput } from '../../editor/appUtils'
|
||||
import { components, configurationKeys } from '../../editor/component'
|
||||
import type { staticValues } from '../../editor/componentsPanel/componentStaticValues'
|
||||
import { components, selectOptions } from '../../editor/component'
|
||||
import type { AppViewerContext, ComponentCustomCSS, RichConfigurations } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import Loader from '../helpers/Loader.svelte'
|
||||
|
||||
type FitOption = (typeof staticValues)['objectFitOptions'][number]
|
||||
import ResolveConfig from '../helpers/ResolveConfig.svelte'
|
||||
|
||||
export let id: string
|
||||
export let configuration: RichConfigurations
|
||||
@@ -17,7 +14,7 @@
|
||||
export let render: boolean
|
||||
|
||||
const { app, worldStore } = getContext<AppViewerContext>('AppViewerContext')
|
||||
const fit: Record<FitOption, string> = {
|
||||
const fit: Record<string, string> = {
|
||||
cover: 'object-cover',
|
||||
contain: 'object-contain',
|
||||
fill: 'object-fill'
|
||||
@@ -31,8 +28,13 @@
|
||||
$: css = concatCustomCss($app.css?.imagecomponent, customCss)
|
||||
</script>
|
||||
|
||||
{#each configurationKeys['imagecomponent'] as key (key)}
|
||||
<InputValue {key} {id} input={configuration[key]} bind:value={resolvedConfig[key]} />
|
||||
{#each Object.keys(components['imagecomponent'].initialData.configuration) as key (key)}
|
||||
<ResolveConfig
|
||||
{id}
|
||||
{key}
|
||||
bind:resolvedConfig={resolvedConfig[key]}
|
||||
configuration={configuration[key]}
|
||||
/>
|
||||
{/each}
|
||||
|
||||
{#if render}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<script lang="ts">
|
||||
import { InputValue } from '.'
|
||||
import type { RichConfiguration } from '../../types'
|
||||
|
||||
export let id: string
|
||||
export let extraKey: string = ''
|
||||
export let key: string
|
||||
export let resolvedConfig: any | { type: 'oneOf'; configuration: any; selected: string }
|
||||
export let configuration: RichConfiguration
|
||||
|
||||
$: configuration?.type == 'oneOf' && handleSelected(configuration.selected)
|
||||
|
||||
function handleSelected(selected: string) {
|
||||
if (resolvedConfig?.selected != undefined && resolvedConfig?.selected != selected) {
|
||||
resolvedConfig.selected = selected
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if configuration?.type == 'oneOf' && resolvedConfig?.type == 'oneOf'}
|
||||
{@const choice = resolvedConfig.selected}
|
||||
{#each Object.keys(configuration.configuration?.[choice] ?? {}) as nestedKey (nestedKey)}
|
||||
{#if resolvedConfig.configuration?.[choice] != undefined}
|
||||
<InputValue
|
||||
key={key + choice + nestedKey + extraKey}
|
||||
{id}
|
||||
input={configuration?.configuration?.[choice]?.[nestedKey]}
|
||||
bind:value={resolvedConfig.configuration[choice][nestedKey]}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
{:else}
|
||||
<InputValue key={key + extraKey} {id} input={configuration} bind:value={resolvedConfig} />
|
||||
{/if}
|
||||
@@ -7,6 +7,7 @@
|
||||
import NonRunnableComponent from './NonRunnableComponent.svelte'
|
||||
import RunnableComponent from './RunnableComponent.svelte'
|
||||
import { goto } from '$app/navigation'
|
||||
import { sendUserToast } from '$lib/utils'
|
||||
|
||||
export let componentInput: AppInput | undefined
|
||||
export let id: string
|
||||
@@ -19,9 +20,21 @@
|
||||
export let forceSchemaDisplay: boolean = false
|
||||
export let runnableClass = ''
|
||||
export let runnableStyle = ''
|
||||
export let gotoUrl: string | undefined = undefined
|
||||
export let gotoNewTab: boolean | undefined = undefined
|
||||
export let setTab: Array<{ id: string; index: number }> | undefined = undefined
|
||||
export let doOnSuccess:
|
||||
| {
|
||||
selected: 'gotoUrl' | 'none' | 'setTab' | 'sendToast'
|
||||
configuration: {
|
||||
gotoUrl: { url: string | undefined; newTab: boolean | undefined }
|
||||
setTab: {
|
||||
setTab: { id: string; index: number }[] | undefined
|
||||
}
|
||||
sendToast: {
|
||||
message: string | undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
| undefined = undefined
|
||||
|
||||
export let render: boolean
|
||||
export let recomputeIds: string[] = []
|
||||
export let outputs: { result: Output<any>; loading: Output<boolean> }
|
||||
@@ -46,19 +59,35 @@
|
||||
}
|
||||
|
||||
export function onSuccess() {
|
||||
if (Array.isArray(setTab)) {
|
||||
setTab.forEach((tab) => {
|
||||
const { id, index } = tab
|
||||
$componentControl[id].setTab?.(index)
|
||||
})
|
||||
}
|
||||
if (!doOnSuccess) return
|
||||
|
||||
if (gotoUrl && gotoUrl != '' && result?.error == undefined) {
|
||||
if (gotoNewTab) {
|
||||
window.open(gotoUrl, '_blank')
|
||||
} else {
|
||||
goto(gotoUrl)
|
||||
if (doOnSuccess.selected == 'none') return
|
||||
|
||||
if (doOnSuccess.selected == 'setTab') {
|
||||
if (Array.isArray(doOnSuccess.configuration.setTab.setTab)) {
|
||||
doOnSuccess.configuration.setTab?.setTab?.forEach((tab) => {
|
||||
if (tab) {
|
||||
const { id, index } = tab
|
||||
$componentControl[id].setTab?.(index)
|
||||
}
|
||||
})
|
||||
}
|
||||
} else if (
|
||||
doOnSuccess.selected == 'gotoUrl' &&
|
||||
doOnSuccess.configuration.gotoUrl.url &&
|
||||
doOnSuccess.configuration.gotoUrl.url != ''
|
||||
) {
|
||||
if (doOnSuccess.configuration.gotoUrl.newTab) {
|
||||
window.open(doOnSuccess.configuration.gotoUrl.url, '_blank')
|
||||
} else {
|
||||
goto(doOnSuccess.configuration.gotoUrl.url)
|
||||
}
|
||||
} else if (
|
||||
doOnSuccess.selected == 'sendToast' &&
|
||||
doOnSuccess.configuration.sendToast.message &&
|
||||
doOnSuccess.configuration.sendToast.message != ''
|
||||
) {
|
||||
sendUserToast(doOnSuccess.configuration.sendToast.message)
|
||||
}
|
||||
|
||||
if (recomputeIds) {
|
||||
|
||||
@@ -5,8 +5,7 @@ import type {
|
||||
ConnectingInput,
|
||||
EditorBreakpoint,
|
||||
FocusedGrid,
|
||||
GridItem,
|
||||
StaticRichConfigurations
|
||||
GridItem
|
||||
} from '../types'
|
||||
import {
|
||||
ccomponents,
|
||||
@@ -14,7 +13,7 @@ import {
|
||||
getRecommendedDimensionsByComponent,
|
||||
type AppComponent,
|
||||
type BaseComponent,
|
||||
type TypedComponent
|
||||
type InitialAppComponent
|
||||
} from './component'
|
||||
import { gridColumns } from '../gridUtils'
|
||||
import { allItems } from '../utils'
|
||||
@@ -94,8 +93,6 @@ export function getAllRecomputeIdsForComponent(app: App, id: string) {
|
||||
}
|
||||
|
||||
export function createNewGridItem(grid: GridItem[], id: string, data: AppComponent): GridItem {
|
||||
console.log('INSERT X')
|
||||
|
||||
const newComponent = {
|
||||
fixed: false,
|
||||
x: 0,
|
||||
@@ -135,24 +132,45 @@ export function getGridItems(app: App, focusedGrid: FocusedGrid | undefined): Gr
|
||||
}
|
||||
}
|
||||
|
||||
function cleanseValue(key: string, value: { type: 'eval' | 'static'; value?: any; expr?: string }) {
|
||||
if (!value) {
|
||||
return [key, undefined]
|
||||
}
|
||||
if (value.type === 'static') {
|
||||
return [key, { type: value.type, value: value.value }]
|
||||
} else {
|
||||
return [key, { type: value.type, expr: value.expr }]
|
||||
}
|
||||
}
|
||||
export function appComponentFromType<T extends keyof typeof components>(
|
||||
type: T
|
||||
): (id: string) => BaseAppComponent & BaseComponent<T> {
|
||||
return (id: string) => {
|
||||
const init = ccomponents[type].initialData
|
||||
const init = JSON.parse(JSON.stringify(ccomponents[type].initialData)) as InitialAppComponent
|
||||
return {
|
||||
type,
|
||||
//TODO remove tooltip and onlyStatic from there
|
||||
configuration: Object.fromEntries(
|
||||
Object.entries(init.configuration).map(([key, value]) => {
|
||||
if (value.ctype === undefined) {
|
||||
if (value.type === 'static') {
|
||||
return [key, { type: value.type, value: value.value }]
|
||||
} else if (value.type === 'eval') {
|
||||
return [key, { type: value.type, expr: value.expr }]
|
||||
}
|
||||
if (value.type != 'oneOf') {
|
||||
return cleanseValue(key, value)
|
||||
} else {
|
||||
return [
|
||||
key,
|
||||
{
|
||||
type: value.type,
|
||||
selected: value.selected,
|
||||
configuration: Object.fromEntries(
|
||||
Object.entries(value.configuration).map(([key, val]) => [
|
||||
key,
|
||||
Object.fromEntries(
|
||||
Object.entries(val).map(([key, val]) => cleanseValue(key, val))
|
||||
)
|
||||
])
|
||||
)
|
||||
}
|
||||
]
|
||||
}
|
||||
return [key, value]
|
||||
})
|
||||
),
|
||||
componentInput: init.componentInput,
|
||||
@@ -370,16 +388,61 @@ export function initOutput<I extends Record<string, any>>(
|
||||
) as Outputtable<I>
|
||||
}
|
||||
|
||||
export function initConfig<T extends Record<string, StaticAppInput | EvalAppInput>>(
|
||||
export function initConfig<
|
||||
T extends Record<
|
||||
string,
|
||||
| StaticAppInput
|
||||
| EvalAppInput
|
||||
| {
|
||||
type: 'oneOf'
|
||||
selected: string
|
||||
configuration: Record<string, Record<string, StaticAppInput | EvalAppInput>>
|
||||
}
|
||||
>
|
||||
>(
|
||||
r: T
|
||||
): {
|
||||
[Property in keyof T]: T[Property] extends StaticAppInput ? T[Property]['value'] | undefined : any
|
||||
[Property in keyof T]: T[Property] extends StaticAppInput
|
||||
? T[Property]['value'] | undefined
|
||||
: T[Property] extends { type: 'oneOf' }
|
||||
? {
|
||||
type: 'oneOf'
|
||||
selected: keyof T[Property]['configuration']
|
||||
configuration: {
|
||||
[Choice in keyof T[Property]['configuration']]: {
|
||||
[IT in keyof T[Property]['configuration'][Choice]]: T[Property]['configuration'][Choice][IT] extends StaticAppInput
|
||||
? T[Property]['configuration'][Choice][IT]['value'] | undefined
|
||||
: undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
: undefined
|
||||
} {
|
||||
return Object.fromEntries(
|
||||
Object.entries(r).map(([key, value]) =>
|
||||
value.type == 'static' ? [key, undefined] : [key, undefined]
|
||||
return JSON.parse(
|
||||
JSON.stringify(
|
||||
Object.fromEntries(
|
||||
Object.entries(r).map(([key, value]) =>
|
||||
value.type == 'static'
|
||||
? [key, undefined]
|
||||
: value.type == 'oneOf'
|
||||
? [
|
||||
key,
|
||||
{
|
||||
selected: value.selected,
|
||||
type: 'oneOf',
|
||||
configuration: Object.fromEntries(
|
||||
Object.entries(value.configuration).map(([choice, config]) => [
|
||||
choice,
|
||||
initConfig(config)
|
||||
])
|
||||
)
|
||||
}
|
||||
]
|
||||
: [key, undefined]
|
||||
)
|
||||
) as any
|
||||
)
|
||||
) as any
|
||||
)
|
||||
}
|
||||
|
||||
export function expandGriditem(
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import type { IntRange } from '../../../../common'
|
||||
import type { NARROW_GRID_COLUMNS, WIDE_GRID_COLUMNS } from '../../gridUtils'
|
||||
import { BUTTON_COLORS } from '../../../common'
|
||||
|
||||
import { defaultAlignement } from '../componentsPanel/componentDefaultProps'
|
||||
import {
|
||||
BarChart4,
|
||||
@@ -187,6 +189,80 @@ export interface InitialAppComponent extends Partial<Aligned> {
|
||||
panes?: number[]
|
||||
}
|
||||
|
||||
const buttonColorOptions = [...BUTTON_COLORS]
|
||||
|
||||
export const selectOptions = {
|
||||
buttonColorOptions,
|
||||
tabsKindOptions: ['tabs', 'sidebar', 'invisibleOnView'],
|
||||
buttonSizeOptions: ['xs', 'sm', 'md', 'lg', 'xl'],
|
||||
tableSearchOptions: ['By Component', 'By Runnable', 'Disabled'],
|
||||
chartThemeOptions: ['theme1', 'theme2', 'theme3'],
|
||||
textStyleOptions: ['Title', 'Subtitle', 'Body', 'Label', 'Caption'],
|
||||
currencyOptions: ['USD', 'EUR', 'GBP', 'CAD', 'AUD', 'JPY', 'CNY', 'INR', 'BRL'],
|
||||
localeOptions: [
|
||||
'en-US',
|
||||
'en-GB',
|
||||
'en-IE',
|
||||
'de-DE',
|
||||
'fr-FR',
|
||||
'br-FR',
|
||||
'ja-JP',
|
||||
'pt-TL',
|
||||
'fr-CA',
|
||||
'en-CA'
|
||||
],
|
||||
objectFitOptions: ['contain', 'cover', 'fill'],
|
||||
splitPanelOptions: ['2', '3', '4']
|
||||
}
|
||||
|
||||
const onSuccessClick = {
|
||||
type: 'oneOf',
|
||||
tooltip: 'Action to perform on success',
|
||||
selected: 'none',
|
||||
labels: {
|
||||
none: 'Do nothing',
|
||||
gotoUrl: 'Go to an url',
|
||||
setTab: 'Set the tab of a tabs component',
|
||||
sendToast: 'Display a toast notification'
|
||||
},
|
||||
configuration: {
|
||||
none: {},
|
||||
gotoUrl: {
|
||||
url: {
|
||||
tooltip: 'Go to the given url, absolute or relative',
|
||||
fieldType: 'text',
|
||||
type: 'static',
|
||||
value: '',
|
||||
placeholder: '/apps/get/foo'
|
||||
},
|
||||
newTab: {
|
||||
tooltip: 'Open the url in a new tab',
|
||||
fieldType: 'boolean',
|
||||
type: 'static',
|
||||
value: true
|
||||
}
|
||||
},
|
||||
setTab: {
|
||||
setTab: {
|
||||
type: 'static',
|
||||
value: [] as Array<{ id: string; index: number }>,
|
||||
fieldType: 'array',
|
||||
subFieldType: 'tab-select',
|
||||
tooltip: 'Set the tabs id and index to go to on success'
|
||||
}
|
||||
},
|
||||
sendToast: {
|
||||
message: {
|
||||
tooltip: 'The message of the toast to diplay',
|
||||
fieldType: 'text',
|
||||
type: 'static',
|
||||
value: '',
|
||||
placeholder: 'Hello there'
|
||||
}
|
||||
}
|
||||
}
|
||||
} as const
|
||||
|
||||
export const components = {
|
||||
displaycomponent: {
|
||||
name: 'Rich Result',
|
||||
@@ -242,7 +318,7 @@ export const components = {
|
||||
fieldType: 'select',
|
||||
type: 'static',
|
||||
onlyStatic: true,
|
||||
optionValuesKey: 'textStyleOptions',
|
||||
selectOptions: selectOptions.textStyleOptions,
|
||||
value: 'Body'
|
||||
},
|
||||
copyButton: {
|
||||
@@ -288,14 +364,14 @@ export const components = {
|
||||
fieldType: 'select',
|
||||
type: 'static',
|
||||
onlyStatic: true,
|
||||
optionValuesKey: 'buttonColorOptions',
|
||||
selectOptions: selectOptions.buttonColorOptions,
|
||||
value: 'blue'
|
||||
},
|
||||
size: {
|
||||
fieldType: 'select',
|
||||
type: 'static',
|
||||
onlyStatic: true,
|
||||
optionValuesKey: 'buttonSizeOptions',
|
||||
selectOptions: selectOptions.buttonSizeOptions,
|
||||
value: 'xs'
|
||||
},
|
||||
fillContainer: {
|
||||
@@ -309,18 +385,7 @@ export const components = {
|
||||
type: 'eval',
|
||||
expr: 'false'
|
||||
},
|
||||
goto: {
|
||||
tooltip: 'Go to an url on success if not empty',
|
||||
fieldType: 'text',
|
||||
type: 'static',
|
||||
value: ''
|
||||
},
|
||||
gotoNewTab: {
|
||||
tooltip: 'Go to an url on a new tab',
|
||||
fieldType: 'boolean',
|
||||
type: 'static',
|
||||
value: true
|
||||
},
|
||||
onSuccess: onSuccessClick,
|
||||
beforeIcon: {
|
||||
type: 'static',
|
||||
value: undefined,
|
||||
@@ -338,13 +403,6 @@ export const components = {
|
||||
value: false,
|
||||
fieldType: 'boolean',
|
||||
onlyStatic: true
|
||||
},
|
||||
setTab: {
|
||||
type: 'static',
|
||||
value: [] as Array<{ id: string; index: number }>,
|
||||
fieldType: 'array',
|
||||
subFieldType: 'tab-select',
|
||||
tooltip: 'Set the tabs id and index to go to on success'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -367,13 +425,6 @@ export const components = {
|
||||
},
|
||||
recomputeIds: undefined,
|
||||
configuration: {
|
||||
setTab: {
|
||||
type: 'static',
|
||||
value: [] as Array<{ id: string; index: number }>,
|
||||
fieldType: 'array',
|
||||
subFieldType: 'tab-select',
|
||||
tooltip: 'Set the tabs id and index to go to on success'
|
||||
},
|
||||
label: {
|
||||
type: 'static',
|
||||
value: 'Submit',
|
||||
@@ -384,21 +435,16 @@ export const components = {
|
||||
type: 'static',
|
||||
onlyStatic: true,
|
||||
value: 'dark',
|
||||
optionValuesKey: 'buttonColorOptions'
|
||||
selectOptions: selectOptions.buttonColorOptions
|
||||
},
|
||||
size: {
|
||||
fieldType: 'select',
|
||||
type: 'static',
|
||||
value: 'xs',
|
||||
onlyStatic: true,
|
||||
optionValuesKey: 'buttonSizeOptions'
|
||||
selectOptions: selectOptions.buttonSizeOptions
|
||||
},
|
||||
goto: {
|
||||
tooltip: 'Go to an url on success if not empty',
|
||||
fieldType: 'text',
|
||||
type: 'static',
|
||||
value: ''
|
||||
}
|
||||
onSuccess: onSuccessClick
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -431,21 +477,20 @@ export const components = {
|
||||
type: 'static',
|
||||
onlyStatic: true,
|
||||
value: 'dark',
|
||||
optionValuesKey: 'buttonColorOptions'
|
||||
selectOptions: buttonColorOptions
|
||||
},
|
||||
size: {
|
||||
fieldType: 'select',
|
||||
type: 'static',
|
||||
value: 'xs',
|
||||
onlyStatic: true,
|
||||
optionValuesKey: 'buttonSizeOptions'
|
||||
selectOptions: selectOptions.buttonSizeOptions
|
||||
},
|
||||
setTab: {
|
||||
type: 'static',
|
||||
value: [] as Array<{ id: string; index: number }>,
|
||||
fieldType: 'array',
|
||||
subFieldType: 'tab-select',
|
||||
tooltip: 'Set the tabs id and index to go to on success'
|
||||
onSuccess: onSuccessClick,
|
||||
disabled: {
|
||||
fieldType: 'boolean',
|
||||
type: 'eval',
|
||||
expr: 'false'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -463,7 +508,7 @@ export const components = {
|
||||
type: 'static',
|
||||
onlyStatic: true,
|
||||
fieldType: 'select',
|
||||
optionValuesKey: 'chartThemeOptions',
|
||||
selectOptions: selectOptions.chartThemeOptions,
|
||||
value: 'theme1'
|
||||
},
|
||||
doughnutStyle: {
|
||||
@@ -493,7 +538,7 @@ export const components = {
|
||||
type: 'static',
|
||||
onlyStatic: true,
|
||||
fieldType: 'select',
|
||||
optionValuesKey: 'chartThemeOptions',
|
||||
selectOptions: selectOptions.chartThemeOptions,
|
||||
value: 'theme1'
|
||||
},
|
||||
line: {
|
||||
@@ -732,7 +777,7 @@ Hello \${ctx.username}
|
||||
fieldType: 'select',
|
||||
type: 'static',
|
||||
onlyStatic: true,
|
||||
optionValuesKey: 'tableSearchOptions',
|
||||
selectOptions: selectOptions.tableSearchOptions,
|
||||
value: 'Disabled'
|
||||
}
|
||||
},
|
||||
@@ -1026,14 +1071,14 @@ Hello \${ctx.username}
|
||||
value: 'USD',
|
||||
fieldType: 'select',
|
||||
onlyStatic: true,
|
||||
optionValuesKey: 'currencyOptions'
|
||||
selectOptions: selectOptions.currencyOptions
|
||||
},
|
||||
locale: {
|
||||
type: 'static',
|
||||
value: 'en-US',
|
||||
fieldType: 'select',
|
||||
onlyStatic: true,
|
||||
optionValuesKey: 'localeOptions',
|
||||
selectOptions: selectOptions.localeOptions,
|
||||
tooltip: 'Currency format'
|
||||
}
|
||||
}
|
||||
@@ -1217,7 +1262,7 @@ Hello \${ctx.username}
|
||||
fieldType: 'select',
|
||||
type: 'static',
|
||||
onlyStatic: true,
|
||||
optionValuesKey: 'tabsKindOptions',
|
||||
selectOptions: selectOptions.tabsKindOptions,
|
||||
value: 'tabs'
|
||||
}
|
||||
},
|
||||
@@ -1375,7 +1420,7 @@ Hello \${ctx.username}
|
||||
fieldType: 'select',
|
||||
type: 'static',
|
||||
onlyStatic: true,
|
||||
optionValuesKey: 'objectFitOptions',
|
||||
selectOptions: selectOptions.objectFitOptions,
|
||||
value: 'contain'
|
||||
},
|
||||
altText: {
|
||||
@@ -1415,14 +1460,14 @@ Hello \${ctx.username}
|
||||
fieldType: 'select',
|
||||
type: 'static',
|
||||
onlyStatic: true,
|
||||
optionValuesKey: 'buttonColorOptions',
|
||||
selectOptions: buttonColorOptions,
|
||||
value: 'blue'
|
||||
},
|
||||
size: {
|
||||
fieldType: 'select',
|
||||
type: 'static',
|
||||
onlyStatic: true,
|
||||
optionValuesKey: 'buttonSizeOptions',
|
||||
selectOptions: selectOptions.buttonSizeOptions,
|
||||
value: 'xs'
|
||||
},
|
||||
fillContainer: {
|
||||
@@ -1555,30 +1600,6 @@ Hello \${ctx.username}
|
||||
}
|
||||
} as const
|
||||
|
||||
export const configurationKeys: Record<AppComponent['type'], string[]> = Object.fromEntries(
|
||||
Object.entries(components)
|
||||
.map(([k, v]) => [
|
||||
k,
|
||||
[
|
||||
...new Set(
|
||||
Object.entries(v.initialData.configuration).flatMap(([k, v]) => {
|
||||
if (!v.ctype) {
|
||||
return [k]
|
||||
} else if (v.ctype == 'oneOf') {
|
||||
return [
|
||||
k,
|
||||
...Object.values(v.configuration ?? {}).flatMap((v) => Object.keys(v ?? {}))
|
||||
]
|
||||
} else if (v.ctype == 'group') {
|
||||
return Object.keys(v.configuration ?? {})
|
||||
}
|
||||
})
|
||||
)
|
||||
]
|
||||
])
|
||||
.filter(([k, v]) => v != undefined)
|
||||
)
|
||||
|
||||
export const ccomponents: {
|
||||
[Property in keyof typeof components]: AppComponentConfig<Property>
|
||||
} = components
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
import { X } from 'lucide-svelte'
|
||||
import { push } from '$lib/history'
|
||||
import { flip } from 'svelte/animate'
|
||||
import { Badge } from '$lib/components/common'
|
||||
|
||||
const { app, selectedComponent, focusedGrid, worldStore } =
|
||||
getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
import { BUTTON_COLORS } from '../../../common'
|
||||
|
||||
const buttonColorOptions = [...BUTTON_COLORS]
|
||||
|
||||
export const staticValues = {
|
||||
buttonColorOptions,
|
||||
tabsKindOptions: ['tabs', 'sidebar', 'invisibleOnView'],
|
||||
buttonSizeOptions: ['xs', 'sm', 'md', 'lg', 'xl'],
|
||||
tableSearchOptions: ['By Component', 'By Runnable', 'Disabled'],
|
||||
chartThemeOptions: ['theme1', 'theme2', 'theme3'],
|
||||
textStyleOptions: ['Title', 'Subtitle', 'Body', 'Label', 'Caption'],
|
||||
currencyOptions: ['USD', 'EUR', 'GBP', 'CAD', 'AUD', 'JPY', 'CNY', 'INR', 'BRL'],
|
||||
localeOptions: [
|
||||
'en-US',
|
||||
'en-GB',
|
||||
'en-IE',
|
||||
'de-DE',
|
||||
'fr-FR',
|
||||
'br-FR',
|
||||
'ja-JP',
|
||||
'pt-TL',
|
||||
'fr-CA',
|
||||
'en-CA'
|
||||
],
|
||||
objectFitOptions: ['contain', 'cover', 'fill'],
|
||||
splitPanelOptions: ['2', '3', '4']
|
||||
} as const
|
||||
+16
-20
@@ -1,19 +1,19 @@
|
||||
<script lang="ts">
|
||||
import { Button } from '$lib/components/common'
|
||||
import { faPlus, faTrashAlt } from '@fortawesome/free-solid-svg-icons'
|
||||
import { X } from 'lucide-svelte'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import type { InputType, StaticAppInput, StaticInput } from '../../inputType'
|
||||
import { staticValues } from '../componentsPanel/componentStaticValues'
|
||||
import { fade } from 'svelte/transition'
|
||||
import type { InputType, StaticAppInput, StaticInput, StaticOptions } from '../../inputType'
|
||||
import SubTypeEditor from './SubTypeEditor.svelte'
|
||||
|
||||
export let componentInput: StaticInput<any[]>
|
||||
export let subFieldType: InputType | undefined = undefined
|
||||
export let optionValuesKey: keyof typeof staticValues | undefined = undefined
|
||||
export let selectOptions: StaticOptions['selectOptions'] | undefined = undefined
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
function addElementByType() {
|
||||
console.log('ADD')
|
||||
if (!Array.isArray(componentInput.value)) {
|
||||
componentInput.value = []
|
||||
}
|
||||
@@ -38,14 +38,12 @@
|
||||
subFieldType === 'datetime'
|
||||
) {
|
||||
value.push('')
|
||||
} else if (subFieldType === 'select' && optionValuesKey) {
|
||||
value.push(staticValues[optionValuesKey][0])
|
||||
} else if (subFieldType === 'select' && selectOptions) {
|
||||
value.push(selectOptions[0])
|
||||
}
|
||||
} else {
|
||||
value.push('')
|
||||
}
|
||||
|
||||
console.log(value, componentInput.value)
|
||||
componentInput = componentInput
|
||||
}
|
||||
|
||||
@@ -64,19 +62,17 @@
|
||||
{#if Array.isArray(componentInput.value)}
|
||||
{#each componentInput.value as value, index (index)}
|
||||
<div class="flex flex-row gap-2 items-center relative">
|
||||
<SubTypeEditor {subFieldType} bind:componentInput bind:value />
|
||||
|
||||
<div class="absolute top-1 right-1">
|
||||
<Button
|
||||
size="xs"
|
||||
color="light"
|
||||
variant="border"
|
||||
on:click={() => deleteElementByType(index)}
|
||||
iconOnly
|
||||
btnClasses="!text-red-500"
|
||||
startIcon={{ icon: faTrashAlt }}
|
||||
/>
|
||||
<div>
|
||||
<SubTypeEditor {subFieldType} bind:componentInput bind:value />
|
||||
</div>
|
||||
<button
|
||||
transition:fade|local={{ duration: 100 }}
|
||||
class="z-10 rounded-full p-1 duration-200 hover:bg-gray-200"
|
||||
aria-label="Remove item"
|
||||
on:click|preventDefault|stopPropagation={() => deleteElementByType(index)}
|
||||
>
|
||||
<X size={14} />
|
||||
</button>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppViewerContext, RichConfiguration } from '../../types'
|
||||
import type { InputType } from '../../inputType'
|
||||
import type { staticValues } from '../componentsPanel/componentStaticValues'
|
||||
|
||||
export let id: string
|
||||
export let componentInput: RichConfiguration
|
||||
@@ -28,112 +27,120 @@
|
||||
export let fieldType: InputType
|
||||
export let subFieldType: InputType | undefined
|
||||
export let format: string | undefined
|
||||
export let optionValuesKey: keyof typeof staticValues | undefined
|
||||
export let selectOptions: string[] | undefined
|
||||
export let placeholder: string | undefined
|
||||
|
||||
const { connectingInput } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
$: if (componentInput == undefined) {
|
||||
//@ts-ignore
|
||||
componentInput = {
|
||||
type: 'static',
|
||||
value: undefined
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if componentInput?.ctype == undefined}
|
||||
{#if !(resourceOnly && (fieldType !== 'object' || !format?.startsWith('resource-')))}
|
||||
<div class="flex flex-col gap-1">
|
||||
<div class="flex justify-between items-end gap-1">
|
||||
<span class="text-sm font-semibold truncate">
|
||||
{shouldCapitalize ? capitalize(addWhitespaceBeforeCapitals(key)) : key}
|
||||
{#if tooltip}
|
||||
<Tooltip>
|
||||
{tooltip}
|
||||
</Tooltip>
|
||||
{/if}
|
||||
</span>
|
||||
{#if !(resourceOnly && (fieldType !== 'object' || !format?.startsWith('resource-')))}
|
||||
<div class="flex flex-col gap-1">
|
||||
<div class="flex justify-between items-end gap-1">
|
||||
<span class="text-sm font-semibold truncate">
|
||||
{shouldCapitalize ? capitalize(addWhitespaceBeforeCapitals(key)) : key}
|
||||
{#if tooltip}
|
||||
<Tooltip>
|
||||
{tooltip}
|
||||
</Tooltip>
|
||||
{/if}
|
||||
</span>
|
||||
|
||||
<div class="flex gap-x-2 gap-y-1 flex-wrap justify-end items-center">
|
||||
<Badge color="blue">
|
||||
{fieldType === 'array' && subFieldType
|
||||
? `${capitalize(fieldTypeToTsType(subFieldType))}[]`
|
||||
: capitalize(fieldTypeToTsType(fieldType))}
|
||||
</Badge>
|
||||
<div class="flex gap-x-2 gap-y-1 flex-wrap justify-end items-center">
|
||||
<Badge color="blue">
|
||||
{fieldType === 'array' && subFieldType
|
||||
? `${capitalize(fieldTypeToTsType(subFieldType))}[]`
|
||||
: capitalize(fieldTypeToTsType(fieldType))}
|
||||
</Badge>
|
||||
|
||||
{#if !onlyStatic && componentInput?.type && componentInput.type != 'eval'}
|
||||
<ToggleButtonGroup
|
||||
bind:selected={componentInput.type}
|
||||
on:selected={(e) => {
|
||||
if (e.detail == 'connected' && !componentInput['connection']) {
|
||||
$connectingInput = {
|
||||
opened: true,
|
||||
input: undefined,
|
||||
hoveredComponent: undefined
|
||||
}
|
||||
{#if !onlyStatic && componentInput?.type && componentInput.type != 'eval'}
|
||||
<ToggleButtonGroup
|
||||
bind:selected={componentInput.type}
|
||||
on:selected={(e) => {
|
||||
if (e.detail == 'connected' && !componentInput['connection']) {
|
||||
$connectingInput = {
|
||||
opened: true,
|
||||
input: undefined,
|
||||
hoveredComponent: undefined
|
||||
}
|
||||
}}
|
||||
>
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Popover placement="bottom" notClickable disapperTimoout={0}>
|
||||
<ToggleButton
|
||||
position="left"
|
||||
value="static"
|
||||
startIcon={{ icon: faPen }}
|
||||
size="xs"
|
||||
iconOnly
|
||||
/>
|
||||
<svelte:fragment slot="text">Static</svelte:fragment>
|
||||
</Popover>
|
||||
{#if userInputEnabled && !format?.startsWith('resource-')}
|
||||
<Popover placement="bottom" notClickable disapperTimoout={0}>
|
||||
<ToggleButton
|
||||
position="left"
|
||||
value="static"
|
||||
startIcon={{ icon: faPen }}
|
||||
position="center"
|
||||
value="user"
|
||||
startIcon={{ icon: faUser }}
|
||||
size="xs"
|
||||
iconOnly
|
||||
/>
|
||||
<svelte:fragment slot="text">Static</svelte:fragment>
|
||||
<svelte:fragment slot="text">User Input</svelte:fragment>
|
||||
</Popover>
|
||||
{#if userInputEnabled && !format?.startsWith('resource-')}
|
||||
<Popover placement="bottom" notClickable disapperTimoout={0}>
|
||||
<ToggleButton
|
||||
position="center"
|
||||
value="user"
|
||||
startIcon={{ icon: faUser }}
|
||||
size="xs"
|
||||
iconOnly
|
||||
/>
|
||||
<svelte:fragment slot="text">User Input</svelte:fragment>
|
||||
</Popover>
|
||||
{/if}
|
||||
{#if 'fileUpload' in componentInput}
|
||||
<Popover placement="bottom" notClickable disapperTimoout={0}>
|
||||
<ToggleButton
|
||||
position="center"
|
||||
value="upload"
|
||||
startIcon={{ icon: faUpload }}
|
||||
size="xs"
|
||||
iconOnly
|
||||
/>
|
||||
<svelte:fragment slot="text">Upload</svelte:fragment>
|
||||
</Popover>
|
||||
{/if}
|
||||
{/if}
|
||||
{#if 'fileUpload' in componentInput}
|
||||
<Popover placement="bottom" notClickable disapperTimoout={0}>
|
||||
<ToggleButton
|
||||
position="right"
|
||||
value="connected"
|
||||
startIcon={{ icon: faArrowRight }}
|
||||
position="center"
|
||||
value="upload"
|
||||
startIcon={{ icon: faUpload }}
|
||||
size="xs"
|
||||
iconOnly
|
||||
/>
|
||||
<svelte:fragment slot="text">Connect</svelte:fragment>
|
||||
<svelte:fragment slot="text">Upload</svelte:fragment>
|
||||
</Popover>
|
||||
</ToggleButtonGroup>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
<Popover placement="bottom" notClickable disapperTimoout={0}>
|
||||
<ToggleButton
|
||||
position="right"
|
||||
value="connected"
|
||||
startIcon={{ icon: faArrowRight }}
|
||||
size="xs"
|
||||
iconOnly
|
||||
/>
|
||||
<svelte:fragment slot="text">Connect</svelte:fragment>
|
||||
</Popover>
|
||||
</ToggleButtonGroup>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if componentInput?.type === 'connected'}
|
||||
<ConnectedInputEditor bind:componentInput />
|
||||
{:else if componentInput?.type === 'row'}
|
||||
<RowInputEditor bind:componentInput />
|
||||
{:else if componentInput?.type === 'static'}
|
||||
<StaticInputEditor
|
||||
{fieldType}
|
||||
{subFieldType}
|
||||
{optionValuesKey}
|
||||
{format}
|
||||
bind:componentInput
|
||||
/>
|
||||
{:else if componentInput?.type === 'eval'}
|
||||
<EvalInputEditor {hasRows} {id} bind:componentInput />
|
||||
{:else if componentInput?.type === 'upload'}
|
||||
<UploadInputEditor bind:componentInput />
|
||||
{:else if componentInput?.type === 'user'}
|
||||
<span class="text-2xs italic text-gray-600">Field's value is set by the user</span>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if componentInput?.type === 'connected'}
|
||||
<ConnectedInputEditor bind:componentInput />
|
||||
{:else if componentInput?.type === 'row'}
|
||||
<RowInputEditor bind:componentInput />
|
||||
{:else if componentInput?.type === 'static'}
|
||||
<StaticInputEditor
|
||||
{fieldType}
|
||||
{subFieldType}
|
||||
{selectOptions}
|
||||
{format}
|
||||
{placeholder}
|
||||
bind:componentInput
|
||||
/>
|
||||
{:else if componentInput?.type === 'eval'}
|
||||
<EvalInputEditor {hasRows} {id} bind:componentInput />
|
||||
{:else if componentInput?.type === 'upload'}
|
||||
<UploadInputEditor bind:componentInput />
|
||||
{:else if componentInput?.type === 'user'}
|
||||
<span class="text-2xs italic text-gray-600">Field's value is set by the user</span>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import type { RichConfigurations } from '../../types'
|
||||
|
||||
import InputsSpecEditor from './InputsSpecEditor.svelte'
|
||||
import OneOfInputSpecsEditor from './OneOfInputSpecsEditor.svelte'
|
||||
|
||||
export let id: string
|
||||
export let inputSpecs: RichConfigurations
|
||||
@@ -15,7 +16,19 @@
|
||||
{#if inputSpecs}
|
||||
<div class="w-full flex flex-col gap-4">
|
||||
{#each Object.keys(inputSpecsConfiguration) as k}
|
||||
{#if inputSpecs[k] && inputSpecs[k]?.ctype == undefined}
|
||||
{#if inputSpecs[k] && inputSpecs[k]?.type == 'oneOf'}
|
||||
<OneOfInputSpecsEditor
|
||||
key={k}
|
||||
bind:oneOf={inputSpecs[k]}
|
||||
{id}
|
||||
{shouldCapitalize}
|
||||
{resourceOnly}
|
||||
{rowColumns}
|
||||
inputSpecsConfiguration={inputSpecsConfiguration?.[k]?.['configuration']}
|
||||
labels={inputSpecsConfiguration?.[k]?.['labels']}
|
||||
tooltip={inputSpecsConfiguration?.[k]?.['tooltip']}
|
||||
/>
|
||||
{:else}
|
||||
{@const meta = inputSpecsConfiguration?.[k]}
|
||||
<!-- {JSON.stringify(meta)} -->
|
||||
<InputsSpecEditor
|
||||
@@ -29,9 +42,10 @@
|
||||
fieldType={meta?.['fieldType']}
|
||||
subFieldType={meta?.['subFieldType']}
|
||||
format={meta?.['format']}
|
||||
optionValuesKey={meta?.['optionValuesKey']}
|
||||
selectOptions={meta?.['selectOptions']}
|
||||
tooltip={meta?.['tooltip']}
|
||||
onlyStatic={meta?.['onlyStatic']}
|
||||
placeholder={meta?.['placeholder']}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
<script lang="ts">
|
||||
import Tooltip from '$lib/components/Tooltip.svelte'
|
||||
import type { RichConfiguration } from '../../types'
|
||||
import InputsSpecEditor from './InputsSpecEditor.svelte'
|
||||
|
||||
export let key: string
|
||||
export let oneOf: { selected: string; configuration: RichConfiguration } | any
|
||||
export let inputSpecsConfiguration: RichConfiguration | any
|
||||
export let labels: Record<string, string> | undefined
|
||||
export let shouldCapitalize: boolean
|
||||
export let id: string
|
||||
export let resourceOnly: boolean
|
||||
export let rowColumns: boolean
|
||||
export let tooltip: string | undefined
|
||||
|
||||
$: if (oneOf.configuration[oneOf.selected] == undefined) {
|
||||
oneOf.configuration[oneOf.selected] = {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="p-2 border">
|
||||
<h4 class="mb-2">{key} <Tooltip>{tooltip}</Tooltip></h4>
|
||||
<select
|
||||
class="w-full border border-gray-300 rounded-md p-2"
|
||||
value={oneOf.selected}
|
||||
on:change={(e) => {
|
||||
oneOf = { ...oneOf, selected: e?.target?.['value'] }
|
||||
}}
|
||||
>
|
||||
{#each Object.keys(inputSpecsConfiguration ?? {}) as choice}
|
||||
<option value={choice}>{labels?.[choice] ?? choice}</option>
|
||||
{/each}
|
||||
</select>
|
||||
<div class="mb-4" />
|
||||
<div class="flex flex-col gap-4">
|
||||
{#each Object.keys(inputSpecsConfiguration?.[oneOf.selected] ?? {}) as nestedKey}
|
||||
{@const config = inputSpecsConfiguration?.[oneOf.selected]?.[nestedKey]}
|
||||
{#if config && oneOf.configuration[oneOf.selected]}
|
||||
<InputsSpecEditor
|
||||
key={nestedKey}
|
||||
bind:componentInput={oneOf.configuration[oneOf.selected][nestedKey]}
|
||||
{id}
|
||||
userInputEnabled={false}
|
||||
{shouldCapitalize}
|
||||
{resourceOnly}
|
||||
hasRows={rowColumns}
|
||||
fieldType={config?.['fieldType']}
|
||||
subFieldType={config?.['subFieldType']}
|
||||
format={config?.['format']}
|
||||
selectOptions={config?.['selectOptions']}
|
||||
placeholder={config?.['placeholder']}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
+16
-9
@@ -1,7 +1,6 @@
|
||||
<script lang="ts">
|
||||
import Toggle from '$lib/components/Toggle.svelte'
|
||||
import { staticValues } from '../../componentsPanel/componentStaticValues'
|
||||
import type { InputType, StaticInput } from '../../../inputType'
|
||||
import type { InputType, StaticInput, StaticOptions } from '../../../inputType'
|
||||
import ArrayStaticInputEditor from '../ArrayStaticInputEditor.svelte'
|
||||
import ResourcePicker from '$lib/components/ResourcePicker.svelte'
|
||||
import JsonEditor from './JsonEditor.svelte'
|
||||
@@ -16,7 +15,9 @@
|
||||
export let componentInput: StaticInput<any> | undefined
|
||||
export let fieldType: InputType | undefined = undefined
|
||||
export let subFieldType: InputType | undefined = undefined
|
||||
export let optionValuesKey: keyof typeof staticValues | undefined = undefined
|
||||
export let selectOptions: StaticOptions['selectOptions'] | undefined = undefined
|
||||
export let placeholder: string | undefined = undefined
|
||||
|
||||
export let format: string | undefined = undefined
|
||||
export let noVariablePicker: boolean = false
|
||||
|
||||
@@ -35,12 +36,18 @@
|
||||
<input on:keydown|stopPropagation type="date" bind:value={componentInput.value} />
|
||||
{:else if fieldType === 'boolean'}
|
||||
<Toggle bind:checked={componentInput.value} />
|
||||
{:else if fieldType === 'select' && optionValuesKey}
|
||||
{:else if fieldType === 'select' && selectOptions}
|
||||
<select on:keydown|stopPropagation on:keydown|stopPropagation bind:value={componentInput.value}>
|
||||
{#each staticValues[optionValuesKey] || [] as option}
|
||||
<option value={option}>
|
||||
{option}
|
||||
</option>
|
||||
{#each selectOptions ?? [] as option}
|
||||
{#if typeof option == 'string'}
|
||||
<option value={option}>
|
||||
{option}
|
||||
</option>
|
||||
{:else}
|
||||
<option value={option.value}>
|
||||
{option.label}
|
||||
</option>
|
||||
{/if}
|
||||
{/each}
|
||||
</select>
|
||||
{:else if fieldType === 'icon-select'}
|
||||
@@ -108,7 +115,7 @@
|
||||
<input
|
||||
on:keydown|stopPropagation
|
||||
type="text"
|
||||
placeholder="Static value"
|
||||
placeholder={placeholder ?? 'Static value'}
|
||||
bind:value={componentInput.value}
|
||||
/>
|
||||
{#if !noVariablePicker}
|
||||
|
||||
-1
@@ -24,7 +24,6 @@
|
||||
$: onLoad =
|
||||
!onClick ||
|
||||
(appComponent?.configuration?.triggerOnAppLoad != undefined &&
|
||||
appComponent?.configuration?.triggerOnAppLoad?.ctype === undefined &&
|
||||
appComponent.configuration.triggerOnAppLoad.type == 'static' &&
|
||||
appComponent.configuration.triggerOnAppLoad.value)
|
||||
</script>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { ReadFileAs } from '../common/fileInput/model'
|
||||
import type { staticValues } from './editor/componentsPanel/componentStaticValues'
|
||||
import type { InlineScript } from './types'
|
||||
|
||||
export type InputType =
|
||||
@@ -122,11 +121,8 @@ type InputConfiguration<T extends InputType, V extends InputType> = {
|
||||
}
|
||||
}
|
||||
|
||||
type StaticOptions = {
|
||||
/**
|
||||
* One of the keys of `staticValues` from `lib/components/apps/editor/componentsPanel/componentStaticValues`
|
||||
*/
|
||||
optionValuesKey: keyof typeof staticValues
|
||||
export type StaticOptions = {
|
||||
selectOptions: string[] | { value: string; label: string }[]
|
||||
}
|
||||
|
||||
export type AppInput =
|
||||
@@ -141,12 +137,6 @@ export type AppInput =
|
||||
| AppInputSpec<'any', any>
|
||||
| AppInputSpec<'object', Record<string | number, any>>
|
||||
| AppInputSpec<'object', string>
|
||||
| (AppInputSpec<'select', string> & {
|
||||
/**
|
||||
* One of the keys of `staticValues` from `lib/components/apps/editor/componentsPanel/componentStaticValues`
|
||||
*/
|
||||
optionValuesKey: keyof typeof staticValues
|
||||
})
|
||||
| (AppInputSpec<'select', string> & StaticOptions)
|
||||
| AppInputSpec<'icon-select', string>
|
||||
| AppInputSpec<'color', string>
|
||||
@@ -158,9 +148,6 @@ export type AppInput =
|
||||
| AppInputSpec<'array', string[], 'time'>
|
||||
| AppInputSpec<'array', string[], 'datetime'>
|
||||
| AppInputSpec<'array', object[], 'object'>
|
||||
| (AppInputSpec<'array', string[], 'select'> & {
|
||||
optionValuesKey: keyof typeof staticValues
|
||||
})
|
||||
| (AppInputSpec<'array', string[], 'select'> & StaticOptions)
|
||||
| AppInputSpec<'array', object[], 'labeledresource'>
|
||||
| AppInputSpec<'labeledresource', object>
|
||||
|
||||
@@ -29,6 +29,7 @@ export type Aligned = {
|
||||
export interface GeneralAppInput {
|
||||
onlyStatic?: boolean
|
||||
tooltip?: string
|
||||
placeholder?: string
|
||||
}
|
||||
|
||||
export type ComponentCssProperty = {
|
||||
@@ -51,14 +52,14 @@ export type Configuration =
|
||||
|
||||
export type StaticConfiguration = GeneralAppInput & StaticAppInput
|
||||
export type RichConfigurationT<T> =
|
||||
| (T & { ctype?: undefined })
|
||||
| (T & { type: AppInput['type'] })
|
||||
| {
|
||||
ctype: 'oneOf'
|
||||
type: 'oneOf'
|
||||
selected: string
|
||||
tooltip?: string
|
||||
labels?: Record<string, string>
|
||||
configuration: Record<string, Record<string, T>>
|
||||
}
|
||||
| { ctype: 'group'; title: string; configuration: Record<string, T> }
|
||||
|
||||
export type RichConfiguration = RichConfigurationT<Configuration>
|
||||
export type RichConfigurations = Record<string, RichConfiguration>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user