Compare commits
66 Commits
1.462.4
...
feat/apped
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
30ac3d3679 | ||
|
|
8fcf5de0b8 | ||
|
|
927dbc7b6d | ||
|
|
489d0f7926 | ||
|
|
82c2ac564a | ||
|
|
ce01d975b3 | ||
|
|
74250285fc | ||
|
|
3c50c0ffa3 | ||
|
|
e783f2487a | ||
|
|
985846f312 | ||
|
|
1a6f89df21 | ||
|
|
552a0f8ec8 | ||
|
|
5abac8bc2b | ||
|
|
200112cf1f | ||
|
|
4fc09da15d | ||
|
|
2df7c85e2d | ||
|
|
686dc177e8 | ||
|
|
88d75cc584 | ||
|
|
1367296de0 | ||
|
|
51d5987110 | ||
|
|
2fc808c065 | ||
|
|
940af69a6a | ||
|
|
59a9458ca8 | ||
|
|
756b07b16c | ||
|
|
e2ebad90ba | ||
|
|
c724982d33 | ||
|
|
70737c6642 | ||
|
|
b84c76ee9f | ||
|
|
663a193c2a | ||
|
|
70b4bff32c | ||
|
|
42bc803445 | ||
|
|
a1038c32b7 | ||
|
|
8e605a7f0f | ||
|
|
d6dc3be311 | ||
|
|
3207a98d1a | ||
|
|
629e1ec36b | ||
|
|
349be01861 | ||
|
|
d04620e86e | ||
|
|
d4a556d0f6 | ||
|
|
f350e3679b | ||
|
|
e338c28713 | ||
|
|
8549cb20b0 | ||
|
|
4b519a5dc8 | ||
|
|
6b60ccc7ad | ||
|
|
34249143e7 | ||
|
|
14ea9d6b55 | ||
|
|
eaa6e36f9d | ||
|
|
fa9e4ba328 | ||
|
|
322ed7dfd0 | ||
|
|
b91f4372e9 | ||
|
|
67e06d54f0 | ||
|
|
b5f3188239 | ||
|
|
6258c501a2 | ||
|
|
e94c1f9a9d | ||
|
|
d256f41793 | ||
|
|
7d52eecad8 | ||
|
|
d74e406a2d | ||
|
|
d9bd24df95 | ||
|
|
390e46245d | ||
|
|
e1ea5bd2e8 | ||
|
|
546da5b983 | ||
|
|
29c9ba874c | ||
|
|
32f8127e02 | ||
|
|
f875e74064 | ||
|
|
0ae0321e4b | ||
|
|
9aee6f78d0 |
@@ -16,10 +16,13 @@
|
||||
import { offset, flip, shift } from 'svelte-floating-ui/dom'
|
||||
import ResolveStyle from '../helpers/ResolveStyle.svelte'
|
||||
import MultiSelect from '$lib/components/multiselect/MultiSelect.svelte'
|
||||
import type { ObjectOption } from '../../../multiselect/types'
|
||||
import { parseConfigOptions } from './utils'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
|
||||
export let id: string
|
||||
export let configuration: RichConfigurations
|
||||
export let customCss: ComponentCustomCSS<'multiselectcomponent'> | undefined = undefined
|
||||
export let id: string
|
||||
export let render: boolean
|
||||
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
|
||||
|
||||
@@ -30,55 +33,33 @@
|
||||
|
||||
const { app, worldStore, selectedComponent, componentControl } =
|
||||
getContext<AppViewerContext>('AppViewerContext')
|
||||
let items: string[]
|
||||
|
||||
const resolvedConfig = initConfig(
|
||||
components['multiselectcomponent'].initialData.configuration,
|
||||
components['multiselectcomponentv2'].initialData.configuration,
|
||||
configuration
|
||||
)
|
||||
|
||||
const outputs = initOutput($worldStore, id, {
|
||||
result: [] as string[]
|
||||
})
|
||||
|
||||
let value: string[] | undefined = [...new Set(outputs?.result.peak())] as string[]
|
||||
|
||||
$componentControl[id] = {
|
||||
setValue(nvalue: string[]) {
|
||||
value = [...new Set(nvalue)]
|
||||
outputs?.result.set([...(value ?? [])])
|
||||
}
|
||||
}
|
||||
|
||||
$: resolvedConfig.items && handleItems()
|
||||
|
||||
function handleItems() {
|
||||
if (Array.isArray(resolvedConfig.items)) {
|
||||
items = resolvedConfig.items?.map((label) => {
|
||||
return typeof label === 'string' ? label : `NOT_STRING`
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
$: resolvedConfig.defaultItems && handleDefaultItems()
|
||||
|
||||
function handleDefaultItems() {
|
||||
if (Array.isArray(resolvedConfig.defaultItems)) {
|
||||
const nvalue = resolvedConfig.defaultItems?.map((label) => {
|
||||
return typeof label === 'string' ? label : `NOT_STRING`
|
||||
})
|
||||
value = [...new Set(nvalue)]
|
||||
outputs?.result.set([...(value ?? [])])
|
||||
}
|
||||
}
|
||||
const outputs = initOutput($worldStore, id, { result: [] as any[] })
|
||||
let options: ObjectOption[] = []
|
||||
let selectedOptions: ObjectOption[] = [...new Set(findOptionsByValue(outputs?.result.peak()))]
|
||||
|
||||
let css = initCss($app.css?.multiselectcomponent, customCss)
|
||||
let outerDiv: HTMLDivElement | undefined = undefined
|
||||
let portalRef: HTMLDivElement | undefined = undefined
|
||||
let w = 0
|
||||
let open: boolean = false
|
||||
|
||||
function setOuterDivStyle(outerDiv: HTMLDivElement, portalRef: HTMLDivElement, style: string) {
|
||||
outerDiv.setAttribute('style', style)
|
||||
// find ul in portalRef and set style
|
||||
const ul = portalRef.querySelector('ul')
|
||||
ul?.setAttribute('style', extractCustomProperties(style))
|
||||
$: if (resolvedConfig.items) {
|
||||
options = parseConfigOptions(resolvedConfig.items)
|
||||
}
|
||||
|
||||
$componentControl[id] = {
|
||||
setValue(newValues: any[]) {
|
||||
selectOptionsByValue(newValues)
|
||||
}
|
||||
}
|
||||
|
||||
$: if (resolvedConfig.defaultItems) {
|
||||
selectOptionsByValue(resolvedConfig.defaultItems)
|
||||
}
|
||||
|
||||
$: outerDiv &&
|
||||
@@ -86,8 +67,56 @@
|
||||
css?.multiselect?.style &&
|
||||
setOuterDivStyle(outerDiv, portalRef, css?.multiselect?.style)
|
||||
|
||||
let outerDiv: HTMLDivElement | undefined = undefined
|
||||
let portalRef: HTMLDivElement | undefined = undefined
|
||||
$: if (render && portalRef && outerDiv && options?.length > 0) {
|
||||
tick().then(() => {
|
||||
moveOptionsToPortal()
|
||||
})
|
||||
}
|
||||
|
||||
function findOptionsByValue(values: any[]) {
|
||||
if (!Array.isArray(values)) {
|
||||
return []
|
||||
}
|
||||
return options?.filter((item) => {
|
||||
let itemValue = item.value
|
||||
try {
|
||||
if (typeof itemValue == 'string') {
|
||||
itemValue = JSON.parse(itemValue)
|
||||
}
|
||||
} catch (_) {}
|
||||
return values.some((value) => deepEqual(itemValue, value))
|
||||
}) as ObjectOption[]
|
||||
}
|
||||
|
||||
function selectOptionsByValue(values: any[]) {
|
||||
if (!Array.isArray(values)) {
|
||||
outputs?.result.set([])
|
||||
}
|
||||
|
||||
selectedOptions = findOptionsByValue(values)
|
||||
outputs?.result.set([...toValues(selectedOptions)])
|
||||
}
|
||||
|
||||
function toValues(options: ObjectOption[] | undefined) {
|
||||
if (!options || !Array.isArray(options)) {
|
||||
return []
|
||||
}
|
||||
|
||||
return options.map((o) => {
|
||||
let val = o.value
|
||||
try {
|
||||
if (typeof val === 'string') val = JSON.parse(val)
|
||||
} catch (_) {}
|
||||
return val
|
||||
})
|
||||
}
|
||||
|
||||
function setOuterDivStyle(outerDiv: HTMLDivElement, portalRef: HTMLDivElement, style: string) {
|
||||
outerDiv.setAttribute('style', style)
|
||||
// find ul in portalRef and set style
|
||||
const ul = portalRef.querySelector('ul')
|
||||
ul?.setAttribute('style', extractCustomProperties(style))
|
||||
}
|
||||
|
||||
function moveOptionsToPortal() {
|
||||
// Find ul element with class 'options' within the outerDiv
|
||||
@@ -98,14 +127,6 @@
|
||||
portalRef?.appendChild(ul)
|
||||
}
|
||||
}
|
||||
|
||||
$: if (render && portalRef && outerDiv && items?.length > 0) {
|
||||
tick().then(() => {
|
||||
moveOptionsToPortal()
|
||||
})
|
||||
}
|
||||
let w = 0
|
||||
let open: boolean = false
|
||||
</script>
|
||||
|
||||
{#each Object.keys(components['multiselectcomponent'].initialData.configuration) as key (key)}
|
||||
@@ -128,7 +149,6 @@
|
||||
{/each}
|
||||
|
||||
<InitializeComponent {id} />
|
||||
|
||||
<AlignWrapper {render} hFull {verticalAlignment}>
|
||||
<div
|
||||
class="w-full app-editor-input"
|
||||
@@ -143,7 +163,7 @@
|
||||
use:floatingRef
|
||||
bind:clientWidth={w}
|
||||
>
|
||||
{#if !value || Array.isArray(value)}
|
||||
{#if !selectedOptions || Array.isArray(selectedOptions)}
|
||||
<MultiSelect
|
||||
bind:outerDiv
|
||||
outerDivClass={`${resolvedConfig.allowOverflow ? '' : 'h-full'}`}
|
||||
@@ -151,15 +171,19 @@
|
||||
--sms-border={'none'}
|
||||
--sms-min-height={'32px'}
|
||||
--sms-focus-border={'none'}
|
||||
bind:selected={value}
|
||||
options={items}
|
||||
bind:selected={selectedOptions}
|
||||
{options}
|
||||
placeholder={resolvedConfig.placeholder}
|
||||
allowUserOptions={resolvedConfig.create}
|
||||
key={(option) => {
|
||||
if (typeof option == 'string' || typeof option == 'number') return option
|
||||
return option.value
|
||||
}}
|
||||
on:change={(event) => {
|
||||
if (event?.detail?.type === 'removeAll') {
|
||||
outputs?.result.set([])
|
||||
} else {
|
||||
outputs?.result.set([...(value ?? [])])
|
||||
outputs?.result.set([...toValues(selectedOptions)])
|
||||
}
|
||||
}}
|
||||
on:open={() => {
|
||||
@@ -181,9 +205,10 @@
|
||||
e.target?.['parentElement']?.dispatchEvent(newe)
|
||||
}}
|
||||
>
|
||||
{option}
|
||||
{@html option.label === '' ? ` ` : option.label}
|
||||
</div>
|
||||
</MultiSelect>
|
||||
|
||||
<Portal name="app-multiselect-v2">
|
||||
<div use:floatingContent class="z5000" hidden={!open}>
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
@@ -197,7 +222,7 @@
|
||||
</div>
|
||||
</Portal>
|
||||
{:else}
|
||||
Value {value} is not an array
|
||||
Value {selectedOptions} is not an array
|
||||
{/if}
|
||||
</div>
|
||||
</AlignWrapper>
|
||||
|
||||
48
frontend/src/lib/components/apps/components/inputs/utils.ts
Normal file
48
frontend/src/lib/components/apps/components/inputs/utils.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import {
|
||||
type ObjectOption,
|
||||
isObjectOptionArray,
|
||||
isStringArray
|
||||
} from '$lib/components/multiselect/types'
|
||||
import type { StaticAppInput } from '../../inputType'
|
||||
|
||||
export function parseConfigOptions(resolvedConfigItems: StaticAppInput) {
|
||||
if (!resolvedConfigItems) {
|
||||
return []
|
||||
}
|
||||
if (isObjectOptionArray(resolvedConfigItems)) {
|
||||
return parseLabeledItems(resolvedConfigItems)
|
||||
} else if (isStringArray(resolvedConfigItems)) {
|
||||
return parseStringItems(resolvedConfigItems)
|
||||
}
|
||||
return []
|
||||
}
|
||||
export function parseLabeledItems(resolvedConfigItems: ObjectOption[]) {
|
||||
return resolvedConfigItems?.map((item: ObjectOption) => {
|
||||
if (!item || typeof item !== 'object') {
|
||||
console.error(
|
||||
'When labeled, MultiSelect component items should be an array of { label: string, value: string }.'
|
||||
)
|
||||
return {
|
||||
label: 'not object',
|
||||
value: 'not object'
|
||||
}
|
||||
}
|
||||
return {
|
||||
label: item?.label ?? 'undefined',
|
||||
value:
|
||||
typeof item?.value === 'object' ? JSON.stringify(item.value) : item?.value ?? 'undefined'
|
||||
}
|
||||
})
|
||||
}
|
||||
export function parseStringItems(resolvedConfigItems: string[]): ObjectOption[] {
|
||||
return resolvedConfigItems?.map((option: string) => {
|
||||
if (option === null || option === undefined || typeof option !== 'string') {
|
||||
console.error('When not labeled, MultiSelect component items should be an array of strings.')
|
||||
return { label: 'not string', value: 'not string' }
|
||||
}
|
||||
if (option === '') {
|
||||
return { label: 'empty string', value: 'empty string' }
|
||||
}
|
||||
return { label: option, value: option }
|
||||
})
|
||||
}
|
||||
@@ -2079,7 +2079,8 @@ This is a paragraph.
|
||||
value: [
|
||||
{ value: 'foo', label: 'Foo' },
|
||||
{ value: 'bar', label: 'Bar' }
|
||||
]
|
||||
],
|
||||
hasLabeledMode: true
|
||||
} as StaticAppInput,
|
||||
create: {
|
||||
type: 'static',
|
||||
@@ -2199,14 +2200,15 @@ This is a paragraph.
|
||||
items: {
|
||||
type: 'static',
|
||||
fieldType: 'array',
|
||||
subFieldType: 'text',
|
||||
value: ['Foo', 'Bar']
|
||||
subFieldType: 'simplestringselect',
|
||||
value: ['Foo', 'Bar'],
|
||||
hasLabeledMode: true
|
||||
} as StaticAppInput,
|
||||
defaultItems: {
|
||||
type: 'static',
|
||||
fieldType: 'array',
|
||||
subFieldType: 'text',
|
||||
value: []
|
||||
subFieldType: 'simplestringselect',
|
||||
value: undefined
|
||||
} as StaticAppInput,
|
||||
placeholder: {
|
||||
type: 'static',
|
||||
|
||||
@@ -9,15 +9,30 @@
|
||||
import Toggle from '$lib/components/Toggle.svelte'
|
||||
import QuickAddColumn from './QuickAddColumn.svelte'
|
||||
import RefreshDatabaseStudioTable from './RefreshDatabaseStudioTable.svelte'
|
||||
import { isObjectOptionArray, isStringArray } from '$lib/components/multiselect/types'
|
||||
|
||||
export let componentInput: StaticInput<any[]> & { loading?: boolean }
|
||||
export let componentInput: StaticInput<any[]> & {
|
||||
loading?: boolean
|
||||
expr?: string
|
||||
isLabeled?: boolean
|
||||
}
|
||||
export let subFieldType: InputType | undefined = undefined
|
||||
export let selectOptions: StaticOptions['selectOptions'] | undefined = undefined
|
||||
export let id: string | undefined
|
||||
export let hasLabeledMode: boolean = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const flipDurationMs = 200
|
||||
|
||||
let items = getItems(componentInput)
|
||||
let raw: boolean = false
|
||||
let labeled: boolean = subFieldType === 'labeledselect'
|
||||
|
||||
$: subFieldType === 'db-explorer' && clearTableOnComponentReset(componentInput?.value)
|
||||
|
||||
$: labeled !== undefined && handleLabeledChange()
|
||||
$: items !== undefined && handleItemsChange()
|
||||
|
||||
function addElementByType() {
|
||||
if (!Array.isArray(componentInput.value)) {
|
||||
componentInput.value = []
|
||||
@@ -32,12 +47,13 @@
|
||||
} else if (subFieldType === 'object') {
|
||||
value.push({})
|
||||
} else if (subFieldType === 'labeledresource' || subFieldType === 'labeledselect') {
|
||||
value.push({ value: 'value', label: 'label' })
|
||||
value.push({ label: 'label', value: 'value' })
|
||||
} else if (subFieldType === 'tab-select') {
|
||||
value.push({ id: '', index: 0 })
|
||||
} else if (
|
||||
subFieldType === 'text' ||
|
||||
subFieldType === 'textarea' ||
|
||||
subFieldType === 'simplestringselect' ||
|
||||
// TODO: Add support for these types
|
||||
subFieldType === 'date' ||
|
||||
subFieldType === 'time' ||
|
||||
@@ -193,8 +209,6 @@
|
||||
componentInput.value = reorderedValues
|
||||
}
|
||||
|
||||
let items = getItems(componentInput)
|
||||
|
||||
function getItems(componentInput: StaticInput<any[]> & { loading?: boolean }) {
|
||||
return (Array.isArray(componentInput.value) ? componentInput.value : [])
|
||||
.filter((x) => x != undefined)
|
||||
@@ -209,15 +223,30 @@
|
||||
}
|
||||
}
|
||||
|
||||
$: subFieldType === 'db-explorer' && clearTableOnComponentReset(componentInput?.value)
|
||||
|
||||
$: items != undefined && handleItemsChange()
|
||||
|
||||
function handleItemsChange() {
|
||||
componentInput.value = items.map((item) => item.value).filter((item) => item != undefined)
|
||||
const newComponentInput = { ...componentInput }
|
||||
newComponentInput.value = items.map((item) => item.value).filter((item) => item != undefined)
|
||||
newComponentInput.expr = JSON.stringify(newComponentInput.value)
|
||||
newComponentInput.isLabeled = labeled
|
||||
|
||||
dispatch('componentInputChange', newComponentInput)
|
||||
}
|
||||
|
||||
/** Transforms items string[] to ObjectOption] and vice versa. Mutating items will fire handleItemsChange. */
|
||||
function handleLabeledChange() {
|
||||
if (labeled && isStringArray(items.map((item) => item.value))) {
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const itemValue = items[i].value
|
||||
items[i].value = { label: itemValue, value: itemValue }
|
||||
}
|
||||
} else if (!labeled && isObjectOptionArray(items.map((item) => item.value))) {
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const itemValue = items[i].value
|
||||
items[i].value = itemValue.label
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let raw: boolean = false
|
||||
// let mounted = false
|
||||
|
||||
// $: if (componentInput.value && mounted) {
|
||||
@@ -243,7 +272,6 @@
|
||||
{#if Array.isArray(items) && componentInput.value}
|
||||
<div class="flex flex-row items-center justify-between">
|
||||
<div class="text-xs text-tertiary font-semibold">{pluralize(items.length, 'item')}</div>
|
||||
|
||||
{#if subFieldType === 'ag-grid' || subFieldType === 'table-column'}
|
||||
<Toggle
|
||||
options={{
|
||||
@@ -253,6 +281,16 @@
|
||||
bind:checked={raw}
|
||||
/>
|
||||
{/if}
|
||||
{#if hasLabeledMode}
|
||||
<Toggle
|
||||
options={{
|
||||
right: 'w/ label'
|
||||
}}
|
||||
size="xs"
|
||||
bind:checked={labeled}
|
||||
on:change={handleLabeledChange}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
<section
|
||||
use:dragHandleZone={{
|
||||
@@ -271,7 +309,7 @@
|
||||
<div class="grow min-w-0">
|
||||
<SubTypeEditor
|
||||
{id}
|
||||
subFieldType={raw ? 'object' : subFieldType}
|
||||
subFieldType={raw ? 'object' : labeled ? 'labeledselect' : subFieldType}
|
||||
bind:componentInput
|
||||
bind:value={item.value}
|
||||
on:remove={() => deleteElementByType(index)}
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
export let documentationLink: string | undefined = undefined
|
||||
export let markdownTooltip: string | undefined = undefined
|
||||
export let securedContext = false
|
||||
export let hasLabeledMode = false
|
||||
|
||||
const { connectingInput, app } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
@@ -171,6 +172,7 @@
|
||||
{selectOptions}
|
||||
{format}
|
||||
{placeholder}
|
||||
{hasLabeledMode}
|
||||
bind:componentInput
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -81,6 +81,7 @@
|
||||
{recomputeOnInputChanged}
|
||||
{showOnDemandOnlyToggle}
|
||||
{securedContext}
|
||||
hasLabeledMode={meta?.['hasLabeledMode']}
|
||||
/>
|
||||
{#if deletable}
|
||||
<div class="flex flex-row-reverse -mt-4">
|
||||
|
||||
@@ -30,6 +30,9 @@
|
||||
editor?.setCode(code)
|
||||
}
|
||||
|
||||
let fullscreen = false
|
||||
let focus = false
|
||||
|
||||
$: extraLib =
|
||||
componentInput?.expr && $worldStore
|
||||
? buildExtraLib($worldStore?.outputsById ?? {}, acceptSelf ? '' : id, $state, false)
|
||||
@@ -52,9 +55,6 @@
|
||||
inferDeps(code, $worldStore.outputsById, componentInput, app)
|
||||
}
|
||||
}
|
||||
|
||||
let fullscreen = false
|
||||
let focus = false
|
||||
</script>
|
||||
|
||||
{#if componentInput?.type === 'evalv2'}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import ArrayStaticInputEditor from '../ArrayStaticInputEditor.svelte'
|
||||
import ResourcePicker from '$lib/components/ResourcePicker.svelte'
|
||||
import JsonEditor from './JsonEditor.svelte'
|
||||
import { getContext } from 'svelte'
|
||||
import { getContext, onMount } from 'svelte'
|
||||
import type { AppViewerContext } from '$lib/components/apps/types'
|
||||
import IconSelectInput from './IconSelectInput.svelte'
|
||||
import ColorInput from './ColorInput.svelte'
|
||||
@@ -24,17 +24,49 @@
|
||||
import EditableSchemaDrawer from '$lib/components/schema/EditableSchemaDrawer.svelte'
|
||||
import AppPicker from '$lib/components/wizards/AppPicker.svelte'
|
||||
|
||||
export let componentInput: StaticInput<any> | undefined
|
||||
export let componentInput: (StaticInput<any> & { isLabeled?: boolean }) | undefined
|
||||
export let fieldType: InputType | undefined = undefined
|
||||
export let subFieldType: InputType | undefined = undefined
|
||||
export let selectOptions: StaticOptions['selectOptions'] | undefined = undefined
|
||||
export let placeholder: string | undefined = undefined
|
||||
export let format: string | undefined = undefined
|
||||
export let id: string | undefined
|
||||
export let hasLabeledMode: boolean = false
|
||||
|
||||
const { onchange } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
$: componentInput && onchange?.()
|
||||
|
||||
onMount(() => {
|
||||
/** preserve the `labeled` toggling when switching from eval to static */
|
||||
if (componentInput !== undefined) {
|
||||
handleComponentInputChange(componentInput)
|
||||
}
|
||||
})
|
||||
/**
|
||||
* Every components: switching from `static` to `eval` preserves the user data
|
||||
*
|
||||
* Multiselect: toggling `labeled` needs to mutate input value and input fieldType.
|
||||
* */
|
||||
function handleComponentInputChange(newComponentInput: StaticInput<any>) {
|
||||
componentInput = { ...componentInput, ...newComponentInput }
|
||||
|
||||
if (componentInput?.isLabeled) {
|
||||
if (fieldType === 'array' && subFieldType === 'simplestringselect') {
|
||||
subFieldType = 'labeledselect'
|
||||
}
|
||||
if (fieldType === 'simplestringselect') {
|
||||
fieldType = 'labeledselect'
|
||||
}
|
||||
} else if (componentInput?.isLabeled === false) {
|
||||
if (fieldType === 'array' && subFieldType === 'labeledselect') {
|
||||
subFieldType = 'simplestringselect'
|
||||
}
|
||||
if (fieldType === 'labeledselect') {
|
||||
fieldType = 'simplestringselect'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#key subFieldType}
|
||||
@@ -133,7 +165,7 @@
|
||||
{/if}
|
||||
{:else if fieldType === 'color'}
|
||||
<ColorInput bind:value={componentInput.value} />
|
||||
{:else if fieldType === 'object' || fieldType == 'labeledselect'}
|
||||
{:else if fieldType === 'object' || fieldType == 'labeledselect' || fieldType === 'simplestringselect'}
|
||||
{#if format?.startsWith('resource-') && (componentInput.value == undefined || typeof componentInput.value == 'string')}
|
||||
<ResourcePicker
|
||||
initialValue={componentInput.value?.split('$res:')?.[1] || ''}
|
||||
@@ -162,7 +194,14 @@
|
||||
</div>
|
||||
{/if}
|
||||
{:else if fieldType === 'array'}
|
||||
<ArrayStaticInputEditor {id} {subFieldType} bind:componentInput on:deleteArrayItem />
|
||||
<ArrayStaticInputEditor
|
||||
{id}
|
||||
{subFieldType}
|
||||
bind:componentInput
|
||||
on:deleteArrayItem
|
||||
on:componentInputChange={(e) => handleComponentInputChange(e.detail)}
|
||||
{hasLabeledMode}
|
||||
/>
|
||||
{:else if fieldType === 'schema'}
|
||||
<div class="w-full">
|
||||
<EditableSchemaDrawer bind:schema={componentInput.value} />
|
||||
|
||||
@@ -20,6 +20,7 @@ export type InputType =
|
||||
| 'any'
|
||||
| 'labeledresource'
|
||||
| 'labeledselect'
|
||||
| 'simplestringselect'
|
||||
| 'tab-select'
|
||||
| 'schema'
|
||||
| 'ag-grid'
|
||||
@@ -175,6 +176,7 @@ type InputConfiguration<T extends InputType, V extends InputType> = {
|
||||
noStatic?: boolean
|
||||
onDemandOnly?: boolean
|
||||
hideRefreshButton?: boolean
|
||||
isLabeled?: boolean
|
||||
}
|
||||
|
||||
export type StaticOptions = {
|
||||
@@ -207,6 +209,7 @@ export type AppInput =
|
||||
| (AppInputSpec<'array', string[], 'select'> & StaticOptions)
|
||||
| AppInputSpec<'array', object[], 'labeledresource'>
|
||||
| AppInputSpec<'array', object[], 'labeledselect'>
|
||||
| AppInputSpec<'array', string[], 'simplestringselect'>
|
||||
| AppInputSpec<'labeledselect', object>
|
||||
| AppInputSpec<'labeledresource', object>
|
||||
| AppInputSpec<'array', object[], 'tab-select'>
|
||||
|
||||
@@ -45,3 +45,30 @@ export type MultiSelectEvents = {
|
||||
touchmove: TouchEvent
|
||||
touchstart: TouchEvent
|
||||
}
|
||||
|
||||
export function isObjectOption(item: any): item is ObjectOption {
|
||||
if (!item || item !== Object(item)) {
|
||||
return false
|
||||
}
|
||||
if (item.label === undefined || item.label === null) {
|
||||
return false
|
||||
}
|
||||
if (typeof item.label !== 'string' && typeof item.label !== 'number') {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
export function isObjectOptionArray(arr: any): arr is ObjectOption[] {
|
||||
if (!arr || !Array.isArray(arr)) {
|
||||
return false
|
||||
}
|
||||
return arr.every((item) => isObjectOption(item))
|
||||
}
|
||||
|
||||
export function isStringArray(arr: any): arr is string[] {
|
||||
if (!arr || !Array.isArray(arr)) {
|
||||
return false
|
||||
}
|
||||
return arr.every((item) => typeof item === 'string')
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user