This commit is contained in:
Ruben Fiszel
2025-06-14 13:21:45 +02:00
parent 1cd253405d
commit 7d4ee825fa
7 changed files with 201 additions and 97 deletions
@@ -1,5 +1,5 @@
<script lang="ts">
import { getContext, onDestroy } from 'svelte'
import { getContext, onDestroy, untrack } from 'svelte'
import type {
AppViewerContext,
@@ -18,18 +18,28 @@
import { twMerge } from 'tailwind-merge'
import ResolveStyle from '../helpers/ResolveStyle.svelte'
export let id: string
export let configuration: RichConfigurations
export let horizontalAlignment: 'left' | 'center' | 'right' | undefined = undefined
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
export let customCss: ComponentCustomCSS<'selecttabcomponent'> | undefined = undefined
export let render: boolean
interface Props {
id: string
configuration: RichConfigurations
horizontalAlignment?: 'left' | 'center' | 'right' | undefined
verticalAlignment?: 'top' | 'center' | 'bottom' | undefined
customCss?: ComponentCustomCSS<'selecttabcomponent'> | undefined
render: boolean
}
let {
id,
configuration,
horizontalAlignment = undefined,
verticalAlignment = undefined,
customCss = undefined,
render
}: Props = $props()
const { app, worldStore, componentControl } = getContext<AppViewerContext>('AppViewerContext')
const resolvedConfig = initConfig(
components['selecttabcomponent'].initialData.configuration,
configuration
const resolvedConfig = $state(
initConfig(components['selecttabcomponent'].initialData.configuration, configuration)
)
const outputs = initOutput($worldStore, id, {
result: undefined as string | undefined
@@ -38,8 +48,7 @@
const iterContext = getContext<ListContext>('ListWrapperContext')
const listInputs: ListInputs | undefined = getContext<ListInputs>('ListInputs')
let selected: string = ''
$: selected === '' && resolvedConfig && setDefaultValue()
let selected: string = $state('')
onDestroy(() => {
listInputs?.remove(id)
@@ -51,6 +60,11 @@
},
setTab(index) {
selected = resolvedConfig.items?.[index]?.value
},
setSelectedIndex(index) {
if (index >= 0 && index < resolvedConfig.items?.length) {
selected = resolvedConfig.items?.[index]?.value
}
}
}
@@ -69,8 +83,13 @@
}
}
$: selected && handleSelection(selected)
let css = initCss($app.css?.selecttabcomponent, customCss)
let css = $state(initCss($app.css?.selecttabcomponent, customCss))
$effect.pre(() => {
selected === '' && resolvedConfig.defaultValue && untrack(() => setDefaultValue())
})
$effect.pre(() => {
selected && untrack(() => handleSelection(selected))
})
</script>
{#each Object.keys(components['selecttabcomponent'].initialData.configuration) as key (key)}
@@ -1,5 +1,5 @@
<script lang="ts">
import { getContext } from 'svelte'
import { getContext, untrack } from 'svelte'
import { initOutput } from '../../editor/appUtils'
import SubGridEditor from '../../editor/SubGridEditor.svelte'
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
@@ -14,16 +14,31 @@
import RunnableComponent from '../helpers/RunnableComponent.svelte'
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
export let id: string
export let componentContainerHeight: number
export let tabs: string[]
export let customCss: ComponentCustomCSS<'steppercomponent'> | undefined = undefined
export let render: boolean
export let recomputeIds: string[] | undefined = undefined
export let extraQueryParams: Record<string, any> = {}
export let componentInput: AppInput | undefined
export let onNext: string[] | undefined = undefined
export let onPrevious: string[] | undefined = undefined
interface Props {
id: string
componentContainerHeight: number
tabs: string[]
customCss?: ComponentCustomCSS<'steppercomponent'> | undefined
render: boolean
recomputeIds?: string[] | undefined
extraQueryParams?: Record<string, any>
componentInput: AppInput | undefined
onNext?: string[] | undefined
onPrevious?: string[] | undefined
}
let {
id,
componentContainerHeight,
tabs,
customCss = undefined,
render,
recomputeIds = undefined,
extraQueryParams = {},
componentInput,
onNext = undefined,
onPrevious = undefined
}: Props = $props()
const {
app,
@@ -36,16 +51,18 @@
runnableComponents
} = getContext<AppViewerContext>('AppViewerContext')
let everRender = render
$: render && !everRender && (everRender = true)
let everRender = $state(render)
$effect.pre(() => {
render && !everRender && (everRender = true)
})
let selected = tabs[0]
let tabHeight: number = 0
let footerHeight: number = 0
let runnableComponent: RunnableComponent
let selectedIndex = tabs?.indexOf(selected) ?? -1
let maxReachedIndex = -1
let statusByStep = [] as Array<'success' | 'error' | 'pending'>
let selected = $state(tabs[0])
let tabHeight: number = $state(0)
let footerHeight: number = $state(0)
let runnableComponent: RunnableComponent | undefined = $state()
let selectedIndex = $state(tabs?.indexOf(selected) ?? -1)
let maxReachedIndex = $state(-1)
let statusByStep = $state([] as Array<'success' | 'error' | 'pending'>)
let debugMode: boolean = false
let outputs = initOutput($worldStore, id, {
@@ -74,7 +91,8 @@
}
}
let result: { error: { name: string; message: string; stack: string } } | undefined = undefined
let result: { error: { name: string; message: string; stack: string } } | undefined =
$state(undefined)
async function runStep(targetIndex: number) {
statusByStep[selectedIndex] = 'pending'
@@ -122,14 +140,22 @@
}
handleTabSelection()
},
setSelectedIndex(index) {
if (index >= 0 && index < tabs.length) {
selected = tabs[index]
handleTabSelection()
}
}
}
$: selected != undefined && handleTabSelection()
let css = initCss($app.css?.steppercomponent, customCss)
$: lastStep = selectedIndex === tabs.length - 1
$effect.pre(() => {
selected != undefined && untrack(() => handleTabSelection())
})
let css = $state(initCss($app.css?.steppercomponent, customCss))
let lastStep = $derived(selectedIndex === tabs.length - 1)
let directionClicked: 'left' | 'right' | undefined = undefined
let directionClicked: 'left' | 'right' | undefined = $state(undefined)
</script>
{#each Object.keys(css ?? {}) as key (key)}
@@ -1,6 +1,9 @@
<script lang="ts">
import { createBubbler, stopPropagation } from 'svelte/legacy'
const bubble = createBubbler()
import { Tab, Tabs } from '$lib/components/common'
import { getContext } from 'svelte'
import { getContext, untrack } from 'svelte'
import { initConfig, initOutput } from '../../editor/appUtils'
import { components } from '../../editor/component'
import SubGridEditor from '../../editor/SubGridEditor.svelte'
@@ -16,21 +19,35 @@
import { twMerge } from 'tailwind-merge'
import ResolveStyle from '../helpers/ResolveStyle.svelte'
export let id: string
export let configuration: RichConfigurations
export let componentContainerHeight: number
export let tabs: string[]
export let customCss: ComponentCustomCSS<'tabscomponent'> | undefined = undefined
export let render: boolean
export let disabledTabs: RichConfiguration[]
export let onTabChange: string[] | undefined = undefined
interface Props {
id: string
configuration: RichConfigurations
componentContainerHeight: number
tabs: string[]
customCss?: ComponentCustomCSS<'tabscomponent'> | undefined
render: boolean
disabledTabs: RichConfiguration[]
onTabChange?: string[] | undefined
}
let resolvedConfig = initConfig(
components['tabscomponent'].initialData.configuration,
configuration
let {
id,
configuration,
componentContainerHeight,
tabs,
customCss = undefined,
render,
disabledTabs,
onTabChange = undefined
}: Props = $props()
let resolvedConfig = $state(
initConfig(components['tabscomponent'].initialData.configuration, configuration)
)
let everRender = render
$: render && !everRender && (everRender = true)
let everRender = $state(render)
$effect.pre(() => {
render && !everRender && (everRender = true)
})
const {
app,
@@ -43,8 +60,11 @@
runnableComponents
} = getContext<AppViewerContext>('AppViewerContext')
let selected: string = tabs[0]
let tabHeight: number = 0
let selected: string = $state(tabs[0])
let selectedIndex = $state(tabs?.indexOf(selected) ?? -1)
let css = $state(initCss($app.css?.tabscomponent, customCss))
let tabHeight: number = $state(0)
let outputs = initOutput($worldStore, id, {
selectedTabIndex: 0
@@ -53,7 +73,14 @@
const titleBarHeight = 40 // Example height of a single accordion title bar in pixels
function handleTabSelection() {
let oldSelectedIndex = selectedIndex
selectedIndex = tabs?.indexOf(selected)
if (selectedIndex == -1) {
selectedIndex = oldSelectedIndex
if (selectedIndex != -1 && tabs.length > 0) {
selected = tabs[selectedIndex]
}
}
outputs?.selectedTabIndex.set(selectedIndex)
onTabChange?.forEach((id) => $runnableComponents?.[id]?.cb?.forEach((cb) => cb?.()))
@@ -67,6 +94,11 @@
}
$componentControl[id] = {
setSelectedIndex(index) {
if (index >= 0 && index < tabs.length) {
selected = tabs[index]
}
},
left: () => {
const index = tabs.indexOf(selected)
if (index > 0) {
@@ -89,11 +121,12 @@
}
}
$: selected != undefined && handleTabSelection()
let selectedIndex = tabs?.indexOf(selected) ?? -1
let css = initCss($app.css?.tabscomponent, customCss)
$effect.pre(() => {
tabs
selected != undefined && untrack(() => handleTabSelection())
})
let resolvedDisabledTabs: boolean[] = []
let resolvedDisabledTabs: boolean[] = $state([])
</script>
<InputValue key="kind" {id} input={configuration.tabsKind} bind:value={resolvedConfig.tabsKind} />
@@ -153,8 +186,8 @@
>
{#each tabs ?? [] as res, index}
<button
on:pointerdown|stopPropagation
on:click={() => !resolvedDisabledTabs[index] && (selected = res)}
onpointerdown={stopPropagation(bubble('pointerdown'))}
onclick={() => !resolvedDisabledTabs[index] && (selected = res)}
disabled={resolvedDisabledTabs[index]}
class={twMerge(
'rounded-sm !truncate text-sm hover:text-primary px-1 py-2',
@@ -167,7 +200,9 @@
'wm-tabs-selectedTab'
)
: '',
resolvedDisabledTabs[index] ? 'opacity-50 cursor-not-allowed hover:text-secondary' : ''
resolvedDisabledTabs[index]
? 'opacity-50 cursor-not-allowed hover:text-secondary'
: ''
)}
style={selected == res
? [css?.allTabs?.style, css?.selectedTab?.style].filter(Boolean).join(';')
@@ -183,8 +218,8 @@
{#each tabs ?? [] as res, index}
<div class="border-b">
<button
on:pointerdown|stopPropagation
on:click={() => !resolvedDisabledTabs[index] && (selected = res)}
onpointerdown={stopPropagation(bubble('pointerdown'))}
onclick={() => !resolvedDisabledTabs[index] && (selected = res)}
disabled={resolvedDisabledTabs[index]}
class={twMerge(
'w-full text-left bg-surface !truncate text-sm hover:text-primary px-1 py-2',
@@ -197,7 +232,9 @@
'wm-tabs-selectedTab'
)
: 'text-secondary',
resolvedDisabledTabs[index] ? 'opacity-50 cursor-not-allowed hover:text-secondary' : ''
resolvedDisabledTabs[index]
? 'opacity-50 cursor-not-allowed hover:text-secondary'
: ''
)}
>
<span class="mr-2 w-8 font-mono">{selected == res ? '-' : '+'}</span>
@@ -85,11 +85,11 @@ const validateAll = {
export function getComponentControl(type: keyof typeof components): Array<ComponentFunction> {
switch (type) {
case 'tabscomponent':
return [setTab]
return [setTab, setSelectedIndex]
case 'selectstepcomponent':
return [setTab, setValue]
return [setTab, setValue, setSelectedIndex]
case 'selecttabcomponent':
return [setTab, setValue]
return [setTab, setValue, setSelectedIndex]
case 'conditionalwrapper':
case 'steppercomponent':
return [setTab]
@@ -1,7 +1,10 @@
<script lang="ts">
import { createBubbler, stopPropagation } from 'svelte/legacy'
const bubble = createBubbler()
import Button from '$lib/components/common/button/Button.svelte'
import CloseButton from '$lib/components/common/CloseButton.svelte'
import { getContext, tick } from 'svelte'
import { getContext, tick, untrack } from 'svelte'
import type { AppViewerContext, RichConfiguration } from '../../types'
import { deleteGridItem } from '../appUtils'
import type { AppComponent } from '../component'
@@ -11,27 +14,43 @@
import { GripVertical, Plus } from 'lucide-svelte'
import GridTabDisabled from './GridTabDisabled.svelte'
export let tabs: string[] = []
export let disabledTabs: RichConfiguration[] = []
export let canDisableTabs: boolean = false
export let word: string = 'Tab'
export let component: AppComponent
$: if (disabledTabs == undefined) {
disabledTabs = [
{ type: 'static', value: false, fieldType: 'boolean' },
{ type: 'static', value: false, fieldType: 'boolean' }
]
interface Props {
tabs?: string[]
disabledTabs?: RichConfiguration[]
canDisableTabs?: boolean
word?: string
component: AppComponent
}
let items = tabs.map((tab, index) => {
return { value: tab, id: generateRandomString(), originalIndex: index }
let {
tabs = $bindable([]),
disabledTabs = $bindable([]),
canDisableTabs = false,
word = 'Tab',
component = $bindable()
}: Props = $props()
$effect.pre(() => {
if (disabledTabs == undefined) {
disabledTabs = [
{ type: 'static', value: false, fieldType: 'boolean' },
{ type: 'static', value: false, fieldType: 'boolean' }
]
}
})
$: tabs = items.map((item) => item.value)
let items = $state.raw(
tabs.map((tab, index) => {
return { value: tab, id: generateRandomString(), originalIndex: index }
})
)
$effect.pre(() => {
items
untrack(() => {
tabs = items.map((item) => item.value)
})
})
const { app, runnableComponents, componentControl } =
getContext<AppViewerContext>('AppViewerContext')
@@ -149,15 +168,15 @@
flipDurationMs: 200,
dropTargetStyle: {}
}}
on:consider={handleConsider}
on:finalize={handleFinalize}
onconsider={handleConsider}
onfinalize={handleFinalize}
>
{#each items as item, index (item.id)}
<div class="border rounded-md p-2 mb-2 bg-surface">
<div class="w-full flex flex-row gap-2 items-center relative my-1">
<input
on:keydown|stopPropagation
on:input={(e) => updateItemValue(index, e)}
onkeydown={stopPropagation(bubble('keydown'))}
oninput={(e) => updateItemValue(index, e)}
type="text"
bind:value={items[index].value}
/>
@@ -166,9 +185,9 @@
</div>
<div class="flex flex-col justify-center gap-2">
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div use:dragHandle class="handle w-4 h-4" aria-label="drag-handle">
<GripVertical size={16} />
</div>
@@ -4,11 +4,15 @@
import InputsSpecEditor from './InputsSpecEditor.svelte'
import { slide } from 'svelte/transition'
export let id: string
export let field: RichConfiguration
export let index: number
interface Props {
id: string
field: RichConfiguration
index: number
}
let disablable = field && !(field?.type === 'static' && field?.value === false)
let { id, field = $bindable(), index }: Props = $props()
let disablable = $state(field && !(field?.type === 'static' && field?.value === false))
</script>
<Toggle
@@ -23,7 +23,6 @@
}
return r
})
console.log('BAR 2')
selectedRunnable = nid
}