mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-17 00:02:31 +00:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9ef31f314 | ||
|
|
355a92a3df |
@@ -4,9 +4,9 @@
|
||||
import BackgroundRunnablesTutorial from './tutorials/app/BackgroundRunnablesTutorial.svelte'
|
||||
import ConnectionTutorial from './tutorials/app/ConnectionTutorial.svelte'
|
||||
|
||||
let backgroundRunnablesTutorial: BackgroundRunnablesTutorial | undefined = undefined
|
||||
let connectionTutorial: ConnectionTutorial | undefined = undefined
|
||||
let appTutorial: AppTutorial | undefined = undefined
|
||||
let backgroundRunnablesTutorial: BackgroundRunnablesTutorial | undefined = $state(undefined)
|
||||
let connectionTutorial: ConnectionTutorial | undefined = $state(undefined)
|
||||
let appTutorial: AppTutorial | undefined = $state(undefined)
|
||||
|
||||
export function runTutorialById(id: string, options?: { skipStepsCount?: number }) {
|
||||
if (id === 'backgroundrunnables') {
|
||||
@@ -23,6 +23,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
P
|
||||
<AppTutorial
|
||||
bind:this={appTutorial}
|
||||
on:error
|
||||
|
||||
@@ -3,10 +3,15 @@
|
||||
import type { Writable } from 'svelte/store'
|
||||
import type { GroupContext } from '../types'
|
||||
|
||||
export let context: Writable<Record<string, any>>
|
||||
export let id: string
|
||||
interface Props {
|
||||
context: Writable<Record<string, any>>
|
||||
id: string
|
||||
children?: import('svelte').Snippet
|
||||
}
|
||||
|
||||
let { context, id, children }: Props = $props()
|
||||
|
||||
setContext<GroupContext>('GroupContext', { id, context })
|
||||
</script>
|
||||
|
||||
<slot />
|
||||
{@render children?.()}
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
let runnableComponent: RunnableComponent | undefined = $state()
|
||||
let loading = $state(false)
|
||||
|
||||
let css = $state(initCss($app.css?.formcomponent, customCss))
|
||||
let css = $state(initCss(app.css?.formcomponent, customCss))
|
||||
|
||||
let wrapper: RunnableWrapper | undefined = $state()
|
||||
$effect(() => {
|
||||
@@ -98,7 +98,7 @@
|
||||
{customCss}
|
||||
{key}
|
||||
bind:css={css[key]}
|
||||
componentStyle={$app.css?.formcomponent}
|
||||
componentStyle={app.css?.formcomponent}
|
||||
/>
|
||||
{/each}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
|
||||
let errors: Record<string, string> = {}
|
||||
|
||||
let css = $state(initCss($app?.css?.formbuttoncomponent, customCss))
|
||||
let css = $state(initCss(app?.css?.formbuttoncomponent, customCss))
|
||||
let runnableWrapper: RunnableWrapper | undefined = $state()
|
||||
let loading = $state(false)
|
||||
let modal: AlwaysMountedModal | undefined = $state()
|
||||
@@ -93,7 +93,7 @@
|
||||
{customCss}
|
||||
{key}
|
||||
bind:css={css[key]}
|
||||
componentStyle={$app.css?.formbuttoncomponent}
|
||||
componentStyle={app.css?.formbuttoncomponent}
|
||||
/>
|
||||
{/each}
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
let css = $state(initCss($app.css?.schemaformcomponent, customCss))
|
||||
let css = $state(initCss(app.css?.schemaformcomponent, customCss))
|
||||
|
||||
const resolvedConfig = $state(
|
||||
initConfig(components['schemaformcomponent'].initialData.configuration, configuration)
|
||||
@@ -125,7 +125,15 @@
|
||||
return policy
|
||||
}
|
||||
$effect(() => {
|
||||
args && untrack(() => handleArgsChange())
|
||||
if (!args) return
|
||||
{
|
||||
if (args && typeof args === 'object') {
|
||||
for (const key in args) {
|
||||
args[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
untrack(() => handleArgsChange())
|
||||
})
|
||||
$effect(() => {
|
||||
outputs.valid.set(valid)
|
||||
@@ -152,7 +160,7 @@
|
||||
{customCss}
|
||||
{key}
|
||||
bind:css={css[key]}
|
||||
componentStyle={$app.css?.schemaformcomponent}
|
||||
componentStyle={app.css?.schemaformcomponent}
|
||||
/>
|
||||
{/each}
|
||||
|
||||
@@ -164,8 +172,7 @@
|
||||
>
|
||||
<div
|
||||
onpointerdown={stopPropagation(
|
||||
(e) =>
|
||||
!$connectingInput.opened && selectId(e as PointerEvent, id, selectedComponent, $app)
|
||||
(e) => !$connectingInput.opened && selectId(e as PointerEvent, id, selectedComponent, app)
|
||||
)}
|
||||
>
|
||||
<SchemaForm
|
||||
|
||||
+31
-20
@@ -18,18 +18,27 @@
|
||||
import DisplayResult from '$lib/components/DisplayResult.svelte'
|
||||
import { userStore } from '$lib/stores'
|
||||
|
||||
export let id: string
|
||||
export let initializing: boolean | undefined = false
|
||||
export let customCss: ComponentCustomCSS<'jobiddisplaycomponent'> | undefined = undefined
|
||||
export let configuration: RichConfigurations
|
||||
export let render: boolean
|
||||
interface Props {
|
||||
id: string
|
||||
initializing?: boolean | undefined
|
||||
customCss?: ComponentCustomCSS<'jobiddisplaycomponent'> | undefined
|
||||
configuration: RichConfigurations
|
||||
render: boolean
|
||||
}
|
||||
|
||||
let {
|
||||
id,
|
||||
initializing = $bindable(false),
|
||||
customCss = undefined,
|
||||
configuration,
|
||||
render
|
||||
}: Props = $props()
|
||||
|
||||
const { app, worldStore, workspace, appPath } = getContext<AppViewerContext>('AppViewerContext')
|
||||
const requireHtmlApproval = getContext<boolean | undefined>(IS_APP_PUBLIC_CONTEXT_KEY)
|
||||
|
||||
let resolvedConfig = initConfig(
|
||||
components['jobiddisplaycomponent'].initialData.configuration,
|
||||
configuration
|
||||
let resolvedConfig = $state(
|
||||
initConfig(components['jobiddisplaycomponent'].initialData.configuration, configuration)
|
||||
)
|
||||
|
||||
const outputs = initOutput($worldStore, id, {
|
||||
@@ -40,18 +49,20 @@
|
||||
|
||||
initializing = false
|
||||
|
||||
let css = initCss($app.css?.jobiddisplaycomponent, customCss)
|
||||
let css = $state(initCss(app.css?.jobiddisplaycomponent, customCss))
|
||||
|
||||
let testJobLoader: TestJobLoader | undefined = undefined
|
||||
let testIsLoading: boolean = false
|
||||
let testJob: Job | undefined = undefined
|
||||
let testJobLoader: TestJobLoader | undefined = $state(undefined)
|
||||
let testIsLoading: boolean = $state(false)
|
||||
let testJob: Job | undefined = $state(undefined)
|
||||
|
||||
$: if (resolvedConfig.jobId) {
|
||||
outputs.loading.set(true)
|
||||
testJobLoader?.watchJob(resolvedConfig?.['jobId'])
|
||||
}
|
||||
$effect(() => {
|
||||
if (resolvedConfig.jobId) {
|
||||
outputs.loading.set(true)
|
||||
testJobLoader?.watchJob(resolvedConfig?.['jobId'])
|
||||
}
|
||||
})
|
||||
|
||||
let result: any = undefined
|
||||
let result: any = $state(undefined)
|
||||
</script>
|
||||
|
||||
{#each Object.keys(components['jobiddisplaycomponent'].initialData.configuration) as key (key)}
|
||||
@@ -69,7 +80,7 @@
|
||||
{customCss}
|
||||
{key}
|
||||
bind:css={css[key]}
|
||||
componentStyle={$app.css?.jobiddisplaycomponent}
|
||||
componentStyle={app.css?.jobiddisplaycomponent}
|
||||
/>
|
||||
{/each}
|
||||
|
||||
@@ -101,13 +112,13 @@
|
||||
</div>
|
||||
<div
|
||||
style={twMerge(
|
||||
$app.css?.['displaycomponent']?.['container']?.style,
|
||||
app.css?.['displaycomponent']?.['container']?.style,
|
||||
customCss?.container?.style,
|
||||
'wm-rich-result-container'
|
||||
)}
|
||||
class={twMerge(
|
||||
'p-2 grow overflow-auto',
|
||||
$app.css?.['displaycomponent']?.['container']?.class,
|
||||
app.css?.['displaycomponent']?.['container']?.class,
|
||||
customCss?.container?.class
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -224,9 +224,7 @@
|
||||
//@ts-ignore
|
||||
gridItem.data.configuration.longitude.value = center[0]
|
||||
//@ts-ignore
|
||||
gridItem.data.configuration.latitude.value = center[1]
|
||||
|
||||
$app = $app
|
||||
gridItem.data.configuration.latitude.value = center[1] // $app = $app
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
initializing = false
|
||||
|
||||
let css = initCss($app.css?.recomputeallcomponent, customCss)
|
||||
let css = initCss(app.css?.recomputeallcomponent, customCss)
|
||||
|
||||
$: resolvedConfig.defaultRefreshInterval && handleRefreshInterval()
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
{customCss}
|
||||
{key}
|
||||
bind:css={css[key]}
|
||||
componentStyle={$app.css?.recomputeallcomponent}
|
||||
componentStyle={app.css?.recomputeallcomponent}
|
||||
/>
|
||||
{/each}
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { run, createBubbler, stopPropagation } from 'svelte/legacy'
|
||||
|
||||
const bubble = createBubbler()
|
||||
import Tooltip from '$lib/components/Tooltip.svelte'
|
||||
import { Clipboard } from 'lucide-svelte'
|
||||
import { getContext } from 'svelte'
|
||||
@@ -15,23 +18,34 @@
|
||||
import { initCss } from '../../utils'
|
||||
import ResolveStyle from '../helpers/ResolveStyle.svelte'
|
||||
|
||||
export let id: string
|
||||
export let componentInput: AppInput | undefined
|
||||
export let horizontalAlignment: 'left' | 'center' | 'right' | undefined = 'left'
|
||||
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
|
||||
export let configuration: RichConfigurations
|
||||
export let initializing: boolean | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'textcomponent'> | undefined = undefined
|
||||
export let render: boolean
|
||||
export let editorMode: boolean = false
|
||||
interface Props {
|
||||
id: string
|
||||
componentInput: AppInput | undefined
|
||||
horizontalAlignment?: 'left' | 'center' | 'right' | undefined
|
||||
verticalAlignment?: 'top' | 'center' | 'bottom' | undefined
|
||||
configuration: RichConfigurations
|
||||
initializing?: boolean | undefined
|
||||
customCss?: ComponentCustomCSS<'textcomponent'> | undefined
|
||||
render: boolean
|
||||
editorMode?: boolean
|
||||
}
|
||||
|
||||
let resolvedConfig = initConfig(
|
||||
components['textcomponent'].initialData.configuration,
|
||||
configuration
|
||||
let {
|
||||
id,
|
||||
componentInput,
|
||||
horizontalAlignment = 'left',
|
||||
verticalAlignment = undefined,
|
||||
configuration,
|
||||
initializing = $bindable(undefined),
|
||||
customCss = undefined,
|
||||
render,
|
||||
editorMode = $bindable(false)
|
||||
}: Props = $props()
|
||||
|
||||
let resolvedConfig = $state(
|
||||
initConfig(components['textcomponent'].initialData.configuration, configuration)
|
||||
)
|
||||
|
||||
$: editorMode && onEditorMode()
|
||||
|
||||
function onEditorMode() {
|
||||
autosize()
|
||||
setTimeout(() => autosize(), 50)
|
||||
@@ -39,9 +53,9 @@
|
||||
const { app, worldStore, mode, componentControl } =
|
||||
getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let css = initCss($app.css?.textcomponent, customCss)
|
||||
let css = $state(initCss(app.css?.textcomponent, customCss))
|
||||
|
||||
let result: string | undefined = undefined
|
||||
let result: string | undefined = $state(undefined)
|
||||
|
||||
if (
|
||||
componentInput?.type == 'template' ||
|
||||
@@ -108,16 +122,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
let component = 'p'
|
||||
let classes = ''
|
||||
|
||||
$: resolvedConfig.style && (component = getComponent())
|
||||
$: resolvedConfig.style && (classes = getClasses())
|
||||
$: initialValue =
|
||||
componentInput?.type == 'template' || componentInput?.type == 'templatev2'
|
||||
? componentInput.eval
|
||||
: ''
|
||||
$: editableValue = initialValue ?? ''
|
||||
let component = $state('p')
|
||||
let classes = $state('')
|
||||
|
||||
let rows = 1
|
||||
|
||||
@@ -140,6 +146,21 @@
|
||||
// console.log(el, el?.scrollHeight)
|
||||
}, 0)
|
||||
}
|
||||
run(() => {
|
||||
editorMode && onEditorMode()
|
||||
})
|
||||
run(() => {
|
||||
resolvedConfig.style && (component = getComponent())
|
||||
})
|
||||
run(() => {
|
||||
resolvedConfig.style && (classes = getClasses())
|
||||
})
|
||||
let initialValue = $derived(
|
||||
componentInput?.type == 'template' || componentInput?.type == 'templatev2'
|
||||
? componentInput.eval
|
||||
: ''
|
||||
)
|
||||
let editableValue = $derived(initialValue ?? '')
|
||||
</script>
|
||||
|
||||
{#each Object.keys(components['textcomponent'].initialData.configuration) as key (key)}
|
||||
@@ -157,22 +178,22 @@
|
||||
{customCss}
|
||||
{key}
|
||||
bind:css={css[key]}
|
||||
componentStyle={$app.css?.textcomponent}
|
||||
componentStyle={app.css?.textcomponent}
|
||||
/>
|
||||
{/each}
|
||||
|
||||
<RunnableWrapper {outputs} {render} {componentInput} {id} bind:initializing bind:result>
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<div
|
||||
class={twMerge('h-full w-full overflow-hidden', css.container?.class, 'wm-text-container')}
|
||||
style={css?.container?.style}
|
||||
on:dblclick={() => {
|
||||
ondblclick={() => {
|
||||
if (!editorMode) {
|
||||
editorMode = true
|
||||
document.getElementById(`text-${id}`)?.focus()
|
||||
}
|
||||
}}
|
||||
on:keydown|stopPropagation
|
||||
onkeydown={stopPropagation(bubble('keydown'))}
|
||||
>
|
||||
{#if $mode == 'dnd' && editorMode && (componentInput?.type == 'template' || componentInput?.type == 'templatev2')}
|
||||
<AlignWrapper {verticalAlignment}>
|
||||
@@ -187,20 +208,20 @@
|
||||
horizontalAlignment === 'center'
|
||||
? 'text-center'
|
||||
: horizontalAlignment === 'right'
|
||||
? 'text-right'
|
||||
: 'text-left'
|
||||
? 'text-right'
|
||||
: 'text-left'
|
||||
)}
|
||||
on:pointerdown|stopPropagation
|
||||
onpointerdown={stopPropagation(bubble('pointerdown'))}
|
||||
style={css?.text?.style}
|
||||
id={`text-${id}`}
|
||||
on:pointerenter={() => {
|
||||
onpointerenter={() => {
|
||||
const elem = document.getElementById(`text-${id}`)
|
||||
if (elem) {
|
||||
elem.focus()
|
||||
}
|
||||
}}
|
||||
{rows}
|
||||
on:input={onInput}
|
||||
oninput={onInput}
|
||||
value={editableValue}
|
||||
></textarea>
|
||||
</AlignWrapper>
|
||||
@@ -215,7 +236,7 @@
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
||||
<div
|
||||
class="flex flex-wrap gap-0.5 pb-0.5 w-full {$mode === 'dnd' &&
|
||||
(componentInput?.type == 'template' || componentInput?.type == 'templatev2')
|
||||
@@ -232,8 +253,8 @@
|
||||
horizontalAlignment === 'center'
|
||||
? 'text-center'
|
||||
: horizontalAlignment === 'right'
|
||||
? 'text-right'
|
||||
: 'text-left'
|
||||
? 'text-right'
|
||||
: 'text-left'
|
||||
)}
|
||||
style={css?.text?.style}
|
||||
>
|
||||
|
||||
@@ -380,8 +380,7 @@
|
||||
|
||||
//@ts-ignore
|
||||
gridItem.data.configuration.columnDefs.loading = true
|
||||
gridItem.data = gridItem.data
|
||||
$app = $app
|
||||
gridItem.data = gridItem.data // $app = $app
|
||||
|
||||
let tableMetadata = await loadTableMetaData(
|
||||
resolvedConfig.type.configuration[selected].resource,
|
||||
@@ -404,9 +403,7 @@
|
||||
if (shouldReturnEarly(newMap, oldMap)) {
|
||||
//@ts-ignore
|
||||
gridItem.data.configuration.columnDefs.loading = false
|
||||
gridItem.data = gridItem.data
|
||||
|
||||
$app = $app
|
||||
gridItem.data = gridItem.data // $app = $app
|
||||
return
|
||||
}
|
||||
|
||||
@@ -457,9 +454,7 @@
|
||||
|
||||
//@ts-ignore
|
||||
gridItem.data.configuration.columnDefs = { value: ncols, type: 'static', loading: false }
|
||||
gridItem.data = gridItem.data
|
||||
|
||||
$app = $app
|
||||
gridItem.data = gridItem.data // $app = $app
|
||||
let oldS = $selectedComponent
|
||||
$selectedComponent = []
|
||||
await tick()
|
||||
|
||||
@@ -369,7 +369,7 @@
|
||||
const uuid = await executeRunnable(
|
||||
runnable,
|
||||
workspace,
|
||||
$app.version,
|
||||
app.version,
|
||||
$userStore?.username,
|
||||
$appPath,
|
||||
id,
|
||||
@@ -436,7 +436,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
const oneOfRunnableInputs = isEditor ? collectOneOfFields(fields, $app) : {}
|
||||
const oneOfRunnableInputs = isEditor ? collectOneOfFields(fields, app) : {}
|
||||
|
||||
const requestBody: ExecuteComponentData['requestBody'] = {
|
||||
args: nonStaticRunnableInputs,
|
||||
@@ -608,7 +608,7 @@
|
||||
|
||||
function handleInputClick(e: CustomEvent) {
|
||||
const event = e as unknown as PointerEvent
|
||||
!$connectingInput.opened && selectId(event, id, selectedComponent, $app)
|
||||
!$connectingInput.opened && selectId(event, id, selectedComponent, app)
|
||||
}
|
||||
|
||||
let cancellableRun: ((inlineScript?: InlineScript) => CancelablePromise<void>) | undefined =
|
||||
|
||||
@@ -12,23 +12,37 @@
|
||||
import GroupWrapper from '../GroupWrapper.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
|
||||
export let id: string
|
||||
export let componentContainerHeight: number
|
||||
export let customCss: ComponentCustomCSS<'containercomponent'> | undefined = undefined
|
||||
export let render: boolean
|
||||
export let groupFields: RichConfigurations | undefined = undefined
|
||||
interface Props {
|
||||
id: string
|
||||
componentContainerHeight: number
|
||||
customCss?: ComponentCustomCSS<'containercomponent'> | undefined
|
||||
render: boolean
|
||||
groupFields?: RichConfigurations | undefined
|
||||
}
|
||||
|
||||
let {
|
||||
id,
|
||||
componentContainerHeight,
|
||||
customCss = undefined,
|
||||
render,
|
||||
groupFields = undefined
|
||||
}: Props = $props()
|
||||
|
||||
const { app, focusedGrid, selectedComponent, worldStore, connectingInput, componentControl } =
|
||||
getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let everRender = render
|
||||
$: render && !everRender && (everRender = true)
|
||||
let everRender = $state(render)
|
||||
$effect(() => {
|
||||
render && !everRender && (everRender = true)
|
||||
})
|
||||
|
||||
let groupContext = writable({})
|
||||
|
||||
let outputs = initOutput($worldStore, id, { group: $groupContext })
|
||||
|
||||
$: outputs.group.set($groupContext, true)
|
||||
$effect(() => {
|
||||
outputs.group.set($groupContext, true)
|
||||
})
|
||||
|
||||
function onFocus() {
|
||||
$focusedGrid = {
|
||||
@@ -48,7 +62,7 @@
|
||||
}
|
||||
})
|
||||
|
||||
let css = initCss($app.css?.containercomponent, customCss)
|
||||
let css = $state(initCss(app.css?.containercomponent, customCss))
|
||||
</script>
|
||||
|
||||
<InitializeComponent {id} />
|
||||
@@ -59,7 +73,7 @@
|
||||
{customCss}
|
||||
{key}
|
||||
bind:css={css[key]}
|
||||
componentStyle={$app.css?.containercomponent}
|
||||
componentStyle={app.css?.containercomponent}
|
||||
/>
|
||||
{/each}
|
||||
|
||||
@@ -71,7 +85,7 @@
|
||||
|
||||
{#if everRender}
|
||||
<div class="w-full h-full">
|
||||
{#if $app.subgrids?.[`${id}-0`]}
|
||||
{#if app.subgrids?.[`${id}-0`]}
|
||||
<GroupWrapper {id} context={groupContext}>
|
||||
<SubGridEditor
|
||||
visible={render}
|
||||
@@ -90,7 +104,7 @@
|
||||
</GroupWrapper>
|
||||
{/if}
|
||||
</div>
|
||||
{:else if $app.subgrids?.[`${id}-0`]}
|
||||
{:else if app.subgrids?.[`${id}-0`]}
|
||||
<GroupWrapper {id} context={groupContext}>
|
||||
<SubGridEditor visible={false} {id} subGridId={`${id}-0`} />
|
||||
</GroupWrapper>
|
||||
|
||||
@@ -100,7 +100,7 @@
|
||||
|
||||
migrateApp(app)
|
||||
|
||||
const appStore = writable<App>(app)
|
||||
const appStore = $state(app)
|
||||
const selectedComponent = writable<string[] | undefined>(undefined)
|
||||
|
||||
// $: selectedComponent.subscribe((s) => {
|
||||
@@ -242,7 +242,7 @@
|
||||
timeout && clearTimeout(timeout)
|
||||
timeout = setTimeout(() => {
|
||||
try {
|
||||
localStorage.setItem(path != '' ? `app-${path}` : 'app', encodeState($appStore))
|
||||
localStorage.setItem(path != '' ? `app-${path}` : 'app', encodeState(appStore))
|
||||
} catch (err) {
|
||||
console.error('Error storing frontend draft in localStorage', err)
|
||||
}
|
||||
@@ -262,7 +262,7 @@
|
||||
selectedTab = 'settings'
|
||||
if (befSelected) {
|
||||
if (!['ctx', 'state'].includes(befSelected) && !befSelected?.startsWith(BG_PREFIX)) {
|
||||
let item = findGridItem($appStore, befSelected)
|
||||
let item = findGridItem(appStore, befSelected)
|
||||
if (item?.data.type === 'containercomponent' || item?.data.type === 'listcomponent') {
|
||||
$focusedGrid = {
|
||||
parentComponentId: befSelected,
|
||||
@@ -284,7 +284,7 @@
|
||||
($worldStore.outputsById?.[befSelected]?.selectedTabIndex?.peak() as number) ?? 0
|
||||
}
|
||||
} else {
|
||||
let subgrid = findGridItemParentGrid($appStore, befSelected)
|
||||
let subgrid = findGridItemParentGrid(appStore, befSelected)
|
||||
if (subgrid) {
|
||||
try {
|
||||
$focusedGrid = {
|
||||
@@ -388,22 +388,25 @@
|
||||
let css: string | undefined = $state(undefined)
|
||||
|
||||
let lastTheme: string | undefined = undefined
|
||||
appStore.subscribe(async (currentAppStore) => {
|
||||
if (!currentAppStore.theme) {
|
||||
return
|
||||
}
|
||||
|
||||
if (JSON.stringify(currentAppStore.theme) != lastTheme) {
|
||||
if (currentAppStore.theme.type === 'inlined') {
|
||||
css = currentAppStore.theme.css
|
||||
} else if (currentAppStore.theme.type === 'path' && currentAppStore.theme?.path) {
|
||||
let loadedCss = await getTheme($workspaceStore!, currentAppStore.theme.path)
|
||||
if (loadedCss) {
|
||||
css = loadedCss.value
|
||||
}
|
||||
$effect(() => {
|
||||
appStore.theme
|
||||
untrack(async () => {
|
||||
if (!appStore.theme) {
|
||||
return
|
||||
}
|
||||
lastTheme = JSON.stringify(currentAppStore.theme)
|
||||
}
|
||||
|
||||
if (JSON.stringify(appStore.theme) != lastTheme) {
|
||||
if (appStore.theme.type === 'inlined') {
|
||||
css = appStore.theme.css
|
||||
} else if (appStore.theme.type === 'path' && appStore.theme?.path) {
|
||||
let loadedCss = await getTheme($workspaceStore!, appStore.theme.path)
|
||||
if (loadedCss) {
|
||||
css = loadedCss.value
|
||||
}
|
||||
}
|
||||
lastTheme = JSON.stringify(appStore.theme)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
function addOrRemoveCss(isPremium: boolean, isPreview: boolean = false) {
|
||||
@@ -760,15 +763,15 @@
|
||||
path && untrack(() => onPathChange())
|
||||
})
|
||||
$effect(() => {
|
||||
$appStore && untrack(() => saveFrontendDraft())
|
||||
appStore && untrack(() => saveFrontendDraft())
|
||||
})
|
||||
$effect(() => {
|
||||
context.mode = $mode == 'dnd' ? 'editor' : 'viewer'
|
||||
})
|
||||
let width = $derived(
|
||||
$breakpoint === 'sm' && $appStore?.mobileViewOnSmallerScreens !== false
|
||||
$breakpoint === 'sm' && appStore?.mobileViewOnSmallerScreens !== false
|
||||
? 'min-w-[400px] max-w-[656px]'
|
||||
: `min-w-[710px] ${$appStore.fullscreen ? 'w-full' : 'max-w-7xl'}`
|
||||
: `min-w-[710px] ${appStore.fullscreen ? 'w-full' : 'max-w-7xl'}`
|
||||
)
|
||||
$effect(() => {
|
||||
if ($selectedComponent?.[0] != befSelected) {
|
||||
@@ -846,7 +849,7 @@
|
||||
/>
|
||||
|
||||
{#if !$userStore?.operator}
|
||||
{#if $appStore}
|
||||
{#if appStore}
|
||||
<AppEditorHeader
|
||||
{newPath}
|
||||
{newApp}
|
||||
@@ -885,15 +888,15 @@
|
||||
<div
|
||||
class={twMerge(
|
||||
'h-full w-full relative',
|
||||
$appStore.css?.['app']?.['viewer']?.class,
|
||||
appStore.css?.['app']?.['viewer']?.class,
|
||||
'wm-app-viewer'
|
||||
)}
|
||||
style={$appStore.css?.['app']?.['viewer']?.style}
|
||||
style={appStore.css?.['app']?.['viewer']?.style}
|
||||
>
|
||||
<AppPreview
|
||||
workspace={$workspaceStore ?? ''}
|
||||
summary={$summaryStore}
|
||||
app={$appStore}
|
||||
app={appStore}
|
||||
appPath={path}
|
||||
{breakpoint}
|
||||
{policy}
|
||||
@@ -951,11 +954,11 @@
|
||||
}}
|
||||
class={twMerge(
|
||||
'bg-surface-secondary h-full w-full relative',
|
||||
$appStore.css?.['app']?.['viewer']?.class,
|
||||
appStore.css?.['app']?.['viewer']?.class,
|
||||
'wm-app-viewer h-full overflow-visible',
|
||||
$panzoomActive ? 'cursor-grab' : ''
|
||||
)}
|
||||
style={$appStore.css?.['app']?.['viewer']?.style}
|
||||
style={appStore.css?.['app']?.['viewer']?.style}
|
||||
bind:clientWidth={centerPanelWidth}
|
||||
>
|
||||
{#if leftPanelSize === 0}
|
||||
@@ -1085,7 +1088,7 @@
|
||||
)}
|
||||
style={$componentActive ? `top: -${$yTop}px;` : ''}
|
||||
>
|
||||
{#if $appStore.grid}
|
||||
{#if appStore.grid}
|
||||
{#if !$connectingInput?.opened}
|
||||
<ComponentNavigation />
|
||||
{/if}
|
||||
@@ -1102,7 +1105,7 @@
|
||||
<GridEditor {policy} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if !$appStore?.mobileViewOnSmallerScreens && $breakpoint === 'sm'}
|
||||
{#if !appStore?.mobileViewOnSmallerScreens && $breakpoint === 'sm'}
|
||||
<div
|
||||
class="absolute inset-0 flex bg-surface center-center z-10000 bg-opacity-60"
|
||||
>
|
||||
@@ -1119,7 +1122,7 @@
|
||||
variant="border"
|
||||
size="xs"
|
||||
on:click={() => {
|
||||
$appStore.mobileViewOnSmallerScreens = true
|
||||
appStore.mobileViewOnSmallerScreens = true
|
||||
}}
|
||||
startIcon={{
|
||||
icon: Smartphone
|
||||
|
||||
@@ -187,7 +187,7 @@
|
||||
}
|
||||
|
||||
async function computeTriggerables() {
|
||||
const items = allItems($app.grid, $app.subgrids)
|
||||
const items = allItems(app.grid, app.subgrids)
|
||||
|
||||
console.debug('items', items)
|
||||
|
||||
@@ -277,7 +277,7 @@
|
||||
return processed as Promise<[string, TriggerableV2] | undefined>[]
|
||||
})
|
||||
.concat(
|
||||
Object.values($app.hiddenInlineScripts ?? {}).map(async (v, i) => {
|
||||
Object.values(app.hiddenInlineScripts ?? {}).map(async (v, i) => {
|
||||
return await processRunnable(BG_PREFIX + i, v, v.fields)
|
||||
}) as Promise<[string, TriggerableV2] | undefined>[]
|
||||
)
|
||||
@@ -294,7 +294,7 @@
|
||||
.map((x) => {
|
||||
const c = x.data as AppComponent
|
||||
const config = c.configuration as any
|
||||
return computeS3FileInputPolicy(config?.type?.configuration?.s3, $app)
|
||||
return computeS3FileInputPolicy(config?.type?.configuration?.s3, app)
|
||||
})
|
||||
.filter(Boolean) as {
|
||||
allowed_resources: string[]
|
||||
@@ -345,7 +345,7 @@
|
||||
fields: Record<string, any>
|
||||
): Promise<[string, TriggerableV2] | undefined> {
|
||||
const staticInputs = collectStaticFields(fields)
|
||||
const oneOfInputs = collectOneOfFields(fields, $app)
|
||||
const oneOfInputs = collectOneOfFields(fields, app)
|
||||
const allowUserResources: string[] = Object.entries(fields)
|
||||
.map(([k, v]) => {
|
||||
return v['allowUserResources'] ? k : undefined
|
||||
@@ -382,7 +382,7 @@
|
||||
await AppService.createApp({
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: {
|
||||
value: $app,
|
||||
value: app,
|
||||
path,
|
||||
summary: $summary,
|
||||
policy,
|
||||
@@ -392,7 +392,7 @@
|
||||
})
|
||||
savedApp = {
|
||||
summary: $summary,
|
||||
value: structuredClone($app),
|
||||
value: structuredClone($state.snapshot(app)),
|
||||
path: path,
|
||||
policy: policy,
|
||||
custom_path: customPath
|
||||
@@ -425,12 +425,12 @@
|
||||
if (
|
||||
deployedValue &&
|
||||
savedApp &&
|
||||
$app &&
|
||||
app &&
|
||||
orderedJsonStringify(deployedValue) ===
|
||||
orderedJsonStringify(
|
||||
replaceFalseWithUndefined({
|
||||
summary: $summary,
|
||||
value: $app,
|
||||
value: app,
|
||||
path: newEditedPath || savedApp.draft?.path || savedApp.path,
|
||||
policy,
|
||||
custom_path: customPath
|
||||
@@ -475,7 +475,7 @@
|
||||
workspace: $workspaceStore!,
|
||||
path: $appPath!,
|
||||
requestBody: {
|
||||
value: $app!,
|
||||
value: app!,
|
||||
summary: $summary,
|
||||
policy,
|
||||
path: npath,
|
||||
@@ -488,7 +488,7 @@
|
||||
})
|
||||
savedApp = {
|
||||
summary: $summary,
|
||||
value: structuredClone($app),
|
||||
value: structuredClone($state.snapshot(app)),
|
||||
path: npath,
|
||||
policy,
|
||||
custom_path: customPath
|
||||
@@ -548,7 +548,7 @@
|
||||
await AppService.createApp({
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: {
|
||||
value: $app,
|
||||
value: app,
|
||||
path: newEditedPath,
|
||||
summary: $summary,
|
||||
policy,
|
||||
@@ -562,7 +562,7 @@
|
||||
path: newEditedPath,
|
||||
typ: 'app',
|
||||
value: {
|
||||
value: $app,
|
||||
value: app,
|
||||
path: newEditedPath,
|
||||
summary: $summary,
|
||||
policy,
|
||||
@@ -572,13 +572,13 @@
|
||||
})
|
||||
savedApp = {
|
||||
summary: $summary,
|
||||
value: structuredClone($app),
|
||||
value: structuredClone($state.snapshot(app)),
|
||||
path: newEditedPath,
|
||||
policy,
|
||||
draft_only: true,
|
||||
draft: {
|
||||
summary: $summary,
|
||||
value: structuredClone($app),
|
||||
value: structuredClone($state.snapshot(app)),
|
||||
path: newEditedPath,
|
||||
policy,
|
||||
custom_path: customPath
|
||||
@@ -606,7 +606,7 @@
|
||||
const draftOrDeployed = cleanValueProperties(savedApp.draft || savedApp)
|
||||
const current = cleanValueProperties({
|
||||
summary: $summary,
|
||||
value: $app,
|
||||
value: app,
|
||||
path: newEditedPath || savedApp.draft?.path || savedApp.path,
|
||||
policy
|
||||
})
|
||||
@@ -633,7 +633,7 @@
|
||||
await AppService.createApp({
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: {
|
||||
value: $app!,
|
||||
value: app!,
|
||||
summary: $summary,
|
||||
policy,
|
||||
path: newEditedPath || path,
|
||||
@@ -648,7 +648,7 @@
|
||||
path: savedApp.draft_only ? newEditedPath || path : path,
|
||||
typ: 'app',
|
||||
value: {
|
||||
value: $app!,
|
||||
value: app!,
|
||||
summary: $summary,
|
||||
policy,
|
||||
path: newEditedPath || path
|
||||
@@ -660,7 +660,7 @@
|
||||
...(savedApp?.draft_only
|
||||
? {
|
||||
summary: $summary,
|
||||
value: structuredClone($app),
|
||||
value: structuredClone($state.snapshot(app)),
|
||||
path: savedApp.draft_only ? newEditedPath || path : path,
|
||||
policy,
|
||||
draft_only: true,
|
||||
@@ -669,7 +669,7 @@
|
||||
: savedApp),
|
||||
draft: {
|
||||
summary: $summary,
|
||||
value: structuredClone($app),
|
||||
value: structuredClone($state.snapshot(app)),
|
||||
path: newEditedPath || path,
|
||||
policy,
|
||||
custom_path: customPath
|
||||
@@ -726,13 +726,13 @@
|
||||
switch (event.key) {
|
||||
case 'Z':
|
||||
if (event.ctrlKey || event.metaKey) {
|
||||
$app = redo(history)
|
||||
// app = redo(history)
|
||||
event.preventDefault()
|
||||
}
|
||||
break
|
||||
case 'z':
|
||||
if (event.ctrlKey || event.metaKey) {
|
||||
$app = undo(history, $app)
|
||||
// app = undo(history, app)
|
||||
|
||||
event.preventDefault()
|
||||
}
|
||||
@@ -781,7 +781,7 @@
|
||||
displayName: 'Export',
|
||||
icon: FileJson,
|
||||
action: () => {
|
||||
appExport?.open($app)
|
||||
appExport?.open(app)
|
||||
}
|
||||
},
|
||||
// {
|
||||
@@ -796,7 +796,7 @@
|
||||
displayName: 'Hub compatible JSON',
|
||||
icon: FileUp,
|
||||
action: () => {
|
||||
appExport?.open(toStatic($app, $staticExporter, $summary).app)
|
||||
appExport?.open(toStatic(app, $staticExporter, $summary).app)
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -832,7 +832,7 @@
|
||||
draft: savedApp.draft,
|
||||
current: {
|
||||
summary: $summary,
|
||||
value: $app,
|
||||
value: app,
|
||||
path: newEditedPath || savedApp.draft?.path || savedApp.path,
|
||||
policy,
|
||||
custom_path: customPath
|
||||
@@ -885,7 +885,7 @@
|
||||
}
|
||||
|
||||
let priorDarkMode = document.documentElement.classList.contains('dark')
|
||||
setTheme($app?.darkMode)
|
||||
setTheme(app?.darkMode)
|
||||
|
||||
let customPath = $state(savedApp?.custom_path)
|
||||
let dirtyCustomPath = $state(false)
|
||||
@@ -951,7 +951,7 @@
|
||||
savedValue: savedApp,
|
||||
modifiedValue: {
|
||||
summary: $summary,
|
||||
value: $app,
|
||||
value: app,
|
||||
path: newEditedPath || savedApp?.draft?.path || savedApp?.path,
|
||||
policy,
|
||||
custom_path: customPath
|
||||
@@ -967,7 +967,7 @@
|
||||
bind:deployedValue
|
||||
currentValue={{
|
||||
summary: $summary,
|
||||
value: $app,
|
||||
value: app,
|
||||
path: newEditedPath || savedApp?.draft?.path || savedApp?.path,
|
||||
policy,
|
||||
custom_path: customPath
|
||||
@@ -1123,7 +1123,7 @@
|
||||
draft: savedApp.draft,
|
||||
current: {
|
||||
summary: $summary,
|
||||
value: $app,
|
||||
value: app,
|
||||
path: newEditedPath || savedApp.draft?.path || savedApp.path,
|
||||
policy,
|
||||
custom_path: customPath
|
||||
@@ -1313,19 +1313,19 @@
|
||||
undoProps={{ disabled: $history?.index === 0 }}
|
||||
redoProps={{ disabled: $history && $history?.index === $history.history.length - 1 }}
|
||||
on:undo={() => {
|
||||
$app = undo(history, $app)
|
||||
// app = undo(history, app)
|
||||
}}
|
||||
on:redo={() => {
|
||||
$app = redo(history)
|
||||
// app = redo(history)
|
||||
}}
|
||||
/>
|
||||
|
||||
{#if $app}
|
||||
{#if app}
|
||||
<ToggleButtonGroup
|
||||
class="h-[30px]"
|
||||
selected={$app.fullscreen ? 'true' : 'false'}
|
||||
selected={app.fullscreen ? 'true' : 'false'}
|
||||
on:selected={({ detail }) => {
|
||||
$app.fullscreen = detail === 'true'
|
||||
app.fullscreen = detail === 'true'
|
||||
}}
|
||||
>
|
||||
{#snippet children({ item })}
|
||||
@@ -1346,14 +1346,14 @@
|
||||
{/snippet}
|
||||
</ToggleButtonGroup>
|
||||
{/if}
|
||||
{#if $app}
|
||||
{#if app}
|
||||
<ToggleButtonGroup
|
||||
class="h-[30px]"
|
||||
on:selected={({ detail }) => {
|
||||
const theme = detail === 'dark' ? true : detail === 'sun' ? false : undefined
|
||||
setTheme(theme)
|
||||
}}
|
||||
selected={$app.darkMode === undefined ? 'auto' : $app.darkMode ? 'dark' : 'sun'}
|
||||
selected={app.darkMode === undefined ? 'auto' : app.darkMode ? 'dark' : 'sun'}
|
||||
>
|
||||
{#snippet children({ item })}
|
||||
<ToggleButton
|
||||
@@ -1406,7 +1406,7 @@
|
||||
'Desktop view is enabled by default. Enable this to customize the layout of the components for the mobile view'
|
||||
}}
|
||||
textClass="text-2xs whitespace-nowrap white !w-full"
|
||||
bind:checked={$app.mobileViewOnSmallerScreens}
|
||||
bind:checked={app.mobileViewOnSmallerScreens}
|
||||
class="flex flex-row px-2 items-center"
|
||||
/>
|
||||
{/if}
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
import ConfirmationModal from '$lib/components/common/confirmationModal/ConfirmationModal.svelte'
|
||||
import { isAppTainted } from '$lib/components/tutorials/utils'
|
||||
|
||||
let appTutorials: AppTutorials | undefined = undefined
|
||||
let targetTutorial: string | undefined = undefined
|
||||
let appTutorials: AppTutorials | undefined = $state(undefined)
|
||||
let targetTutorial: string | undefined = $state(undefined)
|
||||
|
||||
const { app } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
const forkedFromTemplate = urlParams.get('template')
|
||||
|
||||
if (
|
||||
!isAppTainted($app) &&
|
||||
!isAppTainted(app) &&
|
||||
!$ignoredTutorials.includes(7) &&
|
||||
$tutorialsToDo.includes(7) &&
|
||||
!forkedFromTheHub &&
|
||||
@@ -81,7 +81,7 @@
|
||||
|
||||
{#key $tutorialsToDo}
|
||||
<Dropdown items={getTutorialItems}>
|
||||
<svelte:fragment slot="buttonReplacement">
|
||||
{#snippet buttonReplacement()}
|
||||
<Button
|
||||
nonCaptureEvent
|
||||
size="xs"
|
||||
@@ -92,7 +92,7 @@
|
||||
icon: BookOpen
|
||||
}}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
{/snippet}
|
||||
</Dropdown>
|
||||
{/key}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
const { app } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let resourceOnly: boolean = true
|
||||
let resourceOnly: boolean = $state(true)
|
||||
</script>
|
||||
|
||||
<Alert type="info" title="Configurations">
|
||||
@@ -19,7 +19,7 @@
|
||||
<Toggle bind:checked={resourceOnly} options={{ right: 'Resource only' }} />
|
||||
</div>
|
||||
<div class="gap-4 flex flex-col pt-4">
|
||||
{#each allItems($app.grid, $app.subgrids) as gridItem (gridItem.data.id)}
|
||||
{#each allItems(app.grid, app.subgrids) as gridItem (gridItem.data.id)}
|
||||
{#if gridItem?.data?.type === 'tablecomponent'}
|
||||
<div>
|
||||
<AppComponentInput bind:component={gridItem.data} {resourceOnly} />
|
||||
@@ -52,10 +52,10 @@
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
{#if $app?.hiddenInlineScripts?.length > 0}
|
||||
{#if app?.hiddenInlineScripts?.length > 0}
|
||||
<div class="font-bold text-lg">Background runnable inputs</div>
|
||||
<div class="gap-4 flex flex-col pt-4">
|
||||
{#each $app?.hiddenInlineScripts ?? [] as script, index (script.name)}
|
||||
{#each app?.hiddenInlineScripts ?? [] as script, index (script.name)}
|
||||
<div class="border p-2">
|
||||
<div class="text-sm font-bold">{script.name}</div>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { getContext, onDestroy, setContext } from 'svelte'
|
||||
import { getContext, onDestroy, setContext, untrack } from 'svelte'
|
||||
import { get, writable, type Writable } from 'svelte/store'
|
||||
import { buildWorld } from '../rx'
|
||||
import type {
|
||||
@@ -24,28 +24,43 @@
|
||||
import HiddenComponent from '../components/helpers/HiddenComponent.svelte'
|
||||
import RecomputeAllComponents from './RecomputeAllComponents.svelte'
|
||||
|
||||
export let app: App
|
||||
export let appPath: string = ''
|
||||
export let breakpoint: Writable<EditorBreakpoint> = writable('lg')
|
||||
export let policy: Policy = {}
|
||||
export let summary: string = ''
|
||||
export let workspace: string = $workspaceStore!
|
||||
export let isEditor: boolean = false
|
||||
export let context: Record<string, any>
|
||||
export let noBackend: boolean = false
|
||||
export let isLocked = false
|
||||
export let hideRefreshBar = false
|
||||
interface Props {
|
||||
app: App
|
||||
appPath?: string
|
||||
breakpoint?: Writable<EditorBreakpoint>
|
||||
policy?: Policy
|
||||
summary?: string
|
||||
workspace?: string
|
||||
isEditor?: boolean
|
||||
context: Record<string, any>
|
||||
noBackend?: boolean
|
||||
isLocked?: boolean
|
||||
hideRefreshBar?: boolean
|
||||
className?: string
|
||||
replaceStateFn?: (path: string) => void
|
||||
gotoFn?: (path: string, opt?: Record<string, any> | undefined) => void
|
||||
}
|
||||
|
||||
export let replaceStateFn: (path: string) => void = (path: string) =>
|
||||
window.history.replaceState(null, '', path)
|
||||
export let gotoFn: (path: string, opt?: Record<string, any> | undefined) => void = (
|
||||
path: string,
|
||||
opt?: Record<string, any>
|
||||
) => window.history.pushState(null, '', path)
|
||||
let {
|
||||
app,
|
||||
appPath = '',
|
||||
breakpoint = writable('lg'),
|
||||
policy = {},
|
||||
summary = '',
|
||||
workspace = $workspaceStore!,
|
||||
isEditor = false,
|
||||
context,
|
||||
noBackend = false,
|
||||
isLocked = $bindable(false),
|
||||
hideRefreshBar = false,
|
||||
className = '',
|
||||
replaceStateFn = (path: string) => window.history.replaceState(null, '', path),
|
||||
gotoFn = (path: string, opt?: Record<string, any>) => window.history.pushState(null, '', path)
|
||||
}: Props = $props()
|
||||
|
||||
migrateApp(app)
|
||||
|
||||
const appStore = writable<App>(app)
|
||||
const appStore = $state(app)
|
||||
const selectedComponent = writable<string[] | undefined>(undefined)
|
||||
const mode = writable<EditorMode>('preview')
|
||||
|
||||
@@ -93,12 +108,11 @@
|
||||
|
||||
setTheme($darkMode)
|
||||
|
||||
const state = writable({})
|
||||
const appState = writable({})
|
||||
|
||||
let parentContext = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let worldStore = buildWorld(ncontext)
|
||||
$: onContextChange(context)
|
||||
|
||||
function onContextChange(context: any) {
|
||||
Object.assign(ncontext, context)
|
||||
@@ -118,7 +132,6 @@
|
||||
}
|
||||
|
||||
let writablePath = writable(appPath)
|
||||
$: appPath && onPathChange()
|
||||
|
||||
function onPathChange() {
|
||||
writablePath.set(appPath)
|
||||
@@ -152,7 +165,7 @@
|
||||
focusedGrid: writable(undefined),
|
||||
stateId: writable(0),
|
||||
parentWidth,
|
||||
state: state,
|
||||
state: appState,
|
||||
componentControl: writable({}),
|
||||
hoverStore: writable(undefined),
|
||||
allIdsInPath,
|
||||
@@ -172,19 +185,7 @@
|
||||
panzoomActive: writable(false)
|
||||
})
|
||||
|
||||
let previousSelectedIds: string[] | undefined = undefined
|
||||
$: if (!deepEqual(previousSelectedIds, $selectedComponent)) {
|
||||
previousSelectedIds = $selectedComponent
|
||||
$allIdsInPath = ($selectedComponent ?? [])
|
||||
.flatMap((id) => dfs(app.grid, id, app.subgrids ?? {}))
|
||||
.filter((x) => x != undefined) as string[]
|
||||
}
|
||||
|
||||
$: width =
|
||||
$breakpoint === 'sm' && $appStore?.mobileViewOnSmallerScreens !== false
|
||||
? 'max-w-[640px]'
|
||||
: 'w-full min-w-[768px]'
|
||||
$: lockedClasses = isLocked ? '!max-h-[400px] overflow-hidden pointer-events-none' : ''
|
||||
let previousSelectedIds: string[] | undefined = $state(undefined)
|
||||
|
||||
function onThemeChange() {
|
||||
$darkMode = app?.darkMode ?? document.documentElement.classList.contains('dark')
|
||||
@@ -192,9 +193,14 @@
|
||||
|
||||
const cssId = 'wm-global-style'
|
||||
|
||||
let css: string | undefined = undefined
|
||||
let css: string | undefined = $state(undefined)
|
||||
|
||||
appStore.subscribe(loadTheme)
|
||||
$effect(() => {
|
||||
appStore.theme
|
||||
untrack(() => {
|
||||
loadTheme(appStore)
|
||||
})
|
||||
})
|
||||
|
||||
async function loadTheme(currentAppStore: App) {
|
||||
if (!currentAppStore.theme) {
|
||||
@@ -211,8 +217,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
$: addOrRemoveCss($enterpriseLicense !== undefined || isEditor, css)
|
||||
|
||||
function addOrRemoveCss(isPremium: boolean, cssString: string | undefined) {
|
||||
const existingElement = document.getElementById(cssId)
|
||||
|
||||
@@ -237,33 +241,63 @@
|
||||
}
|
||||
}
|
||||
|
||||
let appHeight: number = 0
|
||||
let appHeight: number = $state(0)
|
||||
|
||||
$: maxRow = maxHeight($appStore.grid, appHeight, $breakpoint)
|
||||
$effect(() => {
|
||||
context
|
||||
untrack(() => {
|
||||
onContextChange(context)
|
||||
})
|
||||
})
|
||||
$effect(() => {
|
||||
appPath && onPathChange()
|
||||
})
|
||||
$effect(() => {
|
||||
if (!deepEqual(previousSelectedIds, $selectedComponent)) {
|
||||
untrack(() => {
|
||||
previousSelectedIds = $selectedComponent
|
||||
$allIdsInPath = ($selectedComponent ?? [])
|
||||
.flatMap((id) => dfs(app.grid, id, app.subgrids ?? {}))
|
||||
.filter((x) => x != undefined) as string[]
|
||||
})
|
||||
}
|
||||
})
|
||||
let width = $derived(
|
||||
$breakpoint === 'sm' && appStore?.mobileViewOnSmallerScreens !== false
|
||||
? 'max-w-[640px]'
|
||||
: 'w-full min-w-[768px]'
|
||||
)
|
||||
let lockedClasses = $derived(isLocked ? '!max-h-[400px] overflow-hidden pointer-events-none' : '')
|
||||
$effect(() => {
|
||||
;[$enterpriseLicense, isEditor, css]
|
||||
untrack(() => {
|
||||
addOrRemoveCss($enterpriseLicense !== undefined || isEditor, css)
|
||||
})
|
||||
})
|
||||
let maxRow = $derived(maxHeight(appStore.grid, appHeight, $breakpoint))
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
</svelte:head>
|
||||
<svelte:head></svelte:head>
|
||||
|
||||
<DarkModeObserver on:change={onThemeChange} />
|
||||
|
||||
<svelte:window on:hashchange={hashchange} on:resize={resizeWindow} />
|
||||
<svelte:window onhashchange={hashchange} onresize={resizeWindow} />
|
||||
|
||||
<div class="relative min-h-full grow" bind:clientHeight={appHeight}>
|
||||
<div id="app-editor-top-level-drawer"></div>
|
||||
<div id="app-editor-select"></div>
|
||||
|
||||
<div
|
||||
class="{$$props.class} {lockedClasses} {width} h-full bg-surface {app.fullscreen
|
||||
class="{className} {lockedClasses} {width} h-full bg-surface {app.fullscreen
|
||||
? ''
|
||||
: 'max-w-7xl'} mx-auto"
|
||||
id="app-content"
|
||||
>
|
||||
{#if $appStore.grid}
|
||||
{#if appStore.grid}
|
||||
<div
|
||||
class={twMerge(
|
||||
'mx-auto',
|
||||
hideRefreshBar || $appStore?.norefreshbar || $appStore.hideLegacyTopBar === true
|
||||
hideRefreshBar || appStore?.norefreshbar || appStore.hideLegacyTopBar === true
|
||||
? 'invisible h-0 overflow-hidden'
|
||||
: ''
|
||||
)}
|
||||
@@ -290,36 +324,32 @@
|
||||
bind:clientWidth={$parentWidth}
|
||||
>
|
||||
<div>
|
||||
<GridViewer
|
||||
allIdsInPath={$allIdsInPath}
|
||||
items={app.grid}
|
||||
let:dataItem
|
||||
{maxRow}
|
||||
breakpoint={$breakpoint}
|
||||
>
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
class={'h-full w-full center-center'}
|
||||
on:pointerdown={() => ($selectedComponent = [dataItem.id])}
|
||||
>
|
||||
<Component
|
||||
render={true}
|
||||
component={dataItem.data}
|
||||
selected={false}
|
||||
locked={true}
|
||||
fullHeight={dataItem?.[$breakpoint === 'sm' ? 3 : 12]?.fullHeight}
|
||||
/>
|
||||
</div>
|
||||
<GridViewer allIdsInPath={$allIdsInPath} items={app.grid} {maxRow} breakpoint={$breakpoint}>
|
||||
{#snippet children({ dataItem })}
|
||||
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
||||
<div
|
||||
class={'h-full w-full center-center'}
|
||||
onpointerdown={() => ($selectedComponent = [dataItem.id])}
|
||||
>
|
||||
<Component
|
||||
render={true}
|
||||
component={dataItem.data}
|
||||
selected={false}
|
||||
locked={true}
|
||||
fullHeight={dataItem?.[$breakpoint === 'sm' ? 3 : 12]?.fullHeight}
|
||||
/>
|
||||
</div>
|
||||
{/snippet}
|
||||
</GridViewer>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if isLocked}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<div
|
||||
on:click={() => (isLocked = false)}
|
||||
onclick={() => (isLocked = false)}
|
||||
class="absolute inset-0 center-center bg-black/20 z-50 backdrop-blur-[1px] cursor-pointer"
|
||||
>
|
||||
<Button on:click={() => (isLocked = false)}>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte'
|
||||
import { getContext, untrack } from 'svelte'
|
||||
import type { AppEditorContext, AppViewerContext } from '../types'
|
||||
import { gridColumns, isFixed, toggleFixed } from '../gridUtils'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
@@ -29,7 +29,11 @@
|
||||
import Popover from '$lib/components/Popover.svelte'
|
||||
import type { Policy } from '$lib/gen'
|
||||
|
||||
export let policy: Policy
|
||||
interface Props {
|
||||
policy: Policy
|
||||
}
|
||||
|
||||
let { policy }: Props = $props()
|
||||
|
||||
const {
|
||||
selectedComponent,
|
||||
@@ -42,33 +46,37 @@
|
||||
allIdsInPath,
|
||||
bgRuns,
|
||||
worldStore
|
||||
} = getContext<AppViewerContext>('AppViewerContext')
|
||||
} = $state(getContext<AppViewerContext>('AppViewerContext'))
|
||||
|
||||
const { history, componentActive } = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
let previousSelectedIds: string[] | undefined = undefined
|
||||
$: if (!deepEqual(previousSelectedIds, $selectedComponent)) {
|
||||
previousSelectedIds = $selectedComponent
|
||||
$allIdsInPath = ($selectedComponent ?? [])
|
||||
.flatMap((id) => dfs($app.grid, id, $app.subgrids ?? {}))
|
||||
.filter((x) => x != undefined) as string[]
|
||||
}
|
||||
let previousSelectedIds: string[] | undefined = $state(undefined)
|
||||
$effect(() => {
|
||||
if (!deepEqual(previousSelectedIds, $selectedComponent)) {
|
||||
untrack(() => {
|
||||
previousSelectedIds = $selectedComponent
|
||||
$allIdsInPath = ($selectedComponent ?? [])
|
||||
.flatMap((id) => dfs(app.grid, id, app.subgrids ?? {}))
|
||||
.filter((x) => x != undefined) as string[]
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
function handleLock(id: string) {
|
||||
const gridItem = findGridItem($app, id)
|
||||
const gridItem = findGridItem(app, id)
|
||||
if (gridItem) {
|
||||
toggleFixed(gridItem)
|
||||
}
|
||||
$app = $app
|
||||
// app = app
|
||||
}
|
||||
|
||||
function handleFillHeight(id: string) {
|
||||
const gridItem = findGridItem($app, id)
|
||||
const gridItem = findGridItem(app, id)
|
||||
const b = $breakpoint === 'sm' ? 3 : 12
|
||||
if (gridItem?.[b]) {
|
||||
gridItem[b].fullHeight = !gridItem[b].fullHeight
|
||||
}
|
||||
$app = $app
|
||||
// app = app
|
||||
}
|
||||
|
||||
export function moveComponentBetweenSubgrids(
|
||||
@@ -78,25 +86,25 @@
|
||||
position?: { x: number; y: number }
|
||||
) {
|
||||
// Find the component in the source subgrid
|
||||
const component = findGridItem($app, componentId)
|
||||
const component = findGridItem(app, componentId)
|
||||
|
||||
if (!component) {
|
||||
return
|
||||
}
|
||||
|
||||
let parentGrid = findGridItemParentGrid($app, component.id)
|
||||
let parentGrid = findGridItemParentGrid(app, component.id)
|
||||
if (parentGrid) {
|
||||
$app.subgrids &&
|
||||
($app.subgrids[parentGrid] = $app.subgrids[parentGrid].filter(
|
||||
app.subgrids &&
|
||||
(app.subgrids[parentGrid] = app.subgrids[parentGrid].filter(
|
||||
(item) => item.id !== component?.id
|
||||
))
|
||||
} else {
|
||||
$app.grid = $app.grid.filter((item) => item.id !== component?.id)
|
||||
app.grid = app.grid.filter((item) => item.id !== component?.id)
|
||||
}
|
||||
|
||||
const gridItem = component
|
||||
insertNewGridItem(
|
||||
$app,
|
||||
app,
|
||||
(id) => ({ ...gridItem.data, id }),
|
||||
{ parentComponentId: parentComponentId, subGridIndex: subGridIndex },
|
||||
Object.fromEntries(gridColumns.map((column) => [column, gridItem[column]])),
|
||||
@@ -108,9 +116,6 @@
|
||||
true
|
||||
)
|
||||
|
||||
// Update the app state
|
||||
$app = { ...$app }
|
||||
|
||||
$selectedComponent = [parentComponentId]
|
||||
$focusedGrid = {
|
||||
parentComponentId,
|
||||
@@ -120,7 +125,7 @@
|
||||
</script>
|
||||
|
||||
<div class="w-full z-[1000] overflow-visible h-full">
|
||||
<div class={$app.hideLegacyTopBar ? 'hidden' : ''}>
|
||||
<div class={app.hideLegacyTopBar ? 'hidden' : ''}>
|
||||
<div
|
||||
class="w-full sticky top-0 flex justify-between border-b {$componentActive
|
||||
? 'invisible'
|
||||
@@ -138,15 +143,17 @@
|
||||
<span class="!text-2xs text-tertiary inline-flex gap-1 items-center"
|
||||
><Loader2 size={10} class="animate-spin" /> {$bgRuns.length}
|
||||
</span>
|
||||
<span slot="text"
|
||||
><div class="flex flex-col">
|
||||
{#each $bgRuns as bgRun}
|
||||
<div class="flex gap-2 items-center">
|
||||
<div class="text-2xs text-tertiary">{bgRun}</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div></span
|
||||
>
|
||||
{#snippet text()}
|
||||
<span
|
||||
><div class="flex flex-col">
|
||||
{#each $bgRuns as bgRun}
|
||||
<div class="flex gap-2 items-center">
|
||||
<div class="text-2xs text-tertiary">{bgRun}</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div></span
|
||||
>
|
||||
{/snippet}
|
||||
</Popover>
|
||||
{:else}
|
||||
<span class="w-9"></span>
|
||||
@@ -155,7 +162,7 @@
|
||||
<div class="flex text-2xs gap-8 items-center">
|
||||
<div class="py-2 pr-2 text-secondary flex gap-1 items-center">
|
||||
Hide bar on view
|
||||
<Toggle size="xs" bind:checked={$app.norefreshbar} />
|
||||
<Toggle size="xs" bind:checked={app.norefreshbar} />
|
||||
</div>
|
||||
<div>
|
||||
{policy.on_behalf_of ? `Author ${policy.on_behalf_of_email}` : ''}
|
||||
@@ -167,15 +174,15 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
||||
<div
|
||||
style={$app.css?.['app']?.['grid']?.style}
|
||||
style={app.css?.['app']?.['grid']?.style}
|
||||
class={twMerge(
|
||||
'p-2 overflow-visible z-50',
|
||||
$app.css?.['app']?.['grid']?.class ?? '',
|
||||
app.css?.['app']?.['grid']?.class ?? '',
|
||||
'wm-app-grid !static h-full w-full'
|
||||
)}
|
||||
on:pointerdown={() => {
|
||||
onpointerdown={() => {
|
||||
$selectedComponent = undefined
|
||||
$focusedGrid = undefined
|
||||
}}
|
||||
@@ -188,20 +195,16 @@
|
||||
<Grid
|
||||
allIdsInPath={$allIdsInPath}
|
||||
selectedIds={$selectedComponent}
|
||||
items={$app.grid}
|
||||
items={app.grid}
|
||||
on:redraw={(e) => {
|
||||
push(history, $app)
|
||||
$app.grid = e.detail
|
||||
push(history, app)
|
||||
app.grid = e.detail
|
||||
}}
|
||||
root
|
||||
let:dataItem
|
||||
let:overlapped
|
||||
let:moveMode
|
||||
let:componentDraggedId
|
||||
on:dropped={(e) => {
|
||||
const { id, overlapped, x, y } = e.detail
|
||||
|
||||
const overlappedComponent = findGridItem($app, overlapped)
|
||||
const overlappedComponent = findGridItem(app, overlapped)
|
||||
|
||||
if (overlappedComponent && !isContainer(overlappedComponent.data.type)) {
|
||||
return
|
||||
@@ -224,62 +227,62 @@
|
||||
}}
|
||||
disableMove={!!$connectingInput.opened}
|
||||
>
|
||||
<ComponentWrapper
|
||||
id={dataItem.id}
|
||||
type={dataItem.data.type}
|
||||
class={classNames(
|
||||
'h-full w-full center-center outline outline-surface-secondary',
|
||||
Boolean($selectedComponent?.includes(dataItem.id)) ? 'active-grid-item' : ''
|
||||
)}
|
||||
>
|
||||
<GridEditorMenu
|
||||
{#snippet children({ dataItem, overlapped, moveMode, componentDraggedId })}
|
||||
<ComponentWrapper
|
||||
id={dataItem.id}
|
||||
on:expand={() => {
|
||||
push(history, $app)
|
||||
$selectedComponent = [dataItem.id]
|
||||
expandGriditem($app.grid, dataItem.id, $breakpoint)
|
||||
$app = $app
|
||||
}}
|
||||
on:lock={() => {
|
||||
handleLock(dataItem.id)
|
||||
}}
|
||||
on:fillHeight={() => {
|
||||
handleFillHeight(dataItem.id)
|
||||
}}
|
||||
locked={isFixed(dataItem)}
|
||||
fullHeight={dataItem?.[$breakpoint === 'sm' ? 3 : 12]?.fullHeight}
|
||||
type={dataItem.data.type}
|
||||
class={classNames(
|
||||
'h-full w-full center-center outline outline-surface-secondary',
|
||||
Boolean($selectedComponent?.includes(dataItem.id)) ? 'active-grid-item' : ''
|
||||
)}
|
||||
>
|
||||
<Component
|
||||
render={true}
|
||||
component={dataItem.data}
|
||||
selected={Boolean($selectedComponent?.includes(dataItem.id))}
|
||||
locked={isFixed(dataItem)}
|
||||
fullHeight={dataItem?.[$breakpoint === 'sm' ? 3 : 12]?.fullHeight}
|
||||
<GridEditorMenu
|
||||
id={dataItem.id}
|
||||
on:expand={() => {
|
||||
push(history, app)
|
||||
$selectedComponent = [dataItem.id]
|
||||
expandGriditem(app.grid, dataItem.id, $breakpoint) // app = app
|
||||
}}
|
||||
on:lock={() => {
|
||||
handleLock(dataItem.id)
|
||||
}}
|
||||
on:fillHeight={() => {
|
||||
handleFillHeight(dataItem.id)
|
||||
}}
|
||||
on:expand={() => {
|
||||
push(history, $app)
|
||||
$selectedComponent = [dataItem.id]
|
||||
expandGriditem($app.grid, dataItem.id, $breakpoint)
|
||||
$app = $app
|
||||
}}
|
||||
{overlapped}
|
||||
{moveMode}
|
||||
{componentDraggedId}
|
||||
/>
|
||||
</GridEditorMenu>
|
||||
</ComponentWrapper>
|
||||
locked={isFixed(dataItem)}
|
||||
fullHeight={dataItem?.[$breakpoint === 'sm' ? 3 : 12]?.fullHeight}
|
||||
>
|
||||
<Component
|
||||
render={true}
|
||||
component={dataItem.data}
|
||||
selected={Boolean($selectedComponent?.includes(dataItem.id))}
|
||||
locked={isFixed(dataItem)}
|
||||
fullHeight={dataItem?.[$breakpoint === 'sm' ? 3 : 12]?.fullHeight}
|
||||
on:lock={() => {
|
||||
handleLock(dataItem.id)
|
||||
}}
|
||||
on:fillHeight={() => {
|
||||
handleFillHeight(dataItem.id)
|
||||
}}
|
||||
on:expand={() => {
|
||||
push(history, app)
|
||||
$selectedComponent = [dataItem.id]
|
||||
expandGriditem(app.grid, dataItem.id, $breakpoint) // app = app
|
||||
}}
|
||||
{overlapped}
|
||||
{moveMode}
|
||||
{componentDraggedId}
|
||||
/>
|
||||
</GridEditorMenu>
|
||||
</ComponentWrapper>
|
||||
{/snippet}
|
||||
</Grid>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if $app.hiddenInlineScripts}
|
||||
{#each $app.hiddenInlineScripts as runnable, index}
|
||||
{#if app.hiddenInlineScripts}
|
||||
{#each app.hiddenInlineScripts as runnable, index}
|
||||
{#if runnable}
|
||||
<HiddenComponent id={BG_PREFIX + index} {runnable} />
|
||||
{/if}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { getContext, onMount } from 'svelte'
|
||||
import { getContext, onMount, untrack } from 'svelte'
|
||||
import type { App, AppEditorContext, AppViewerContext } from '../types'
|
||||
import { allItems, BG_PREFIX } from '../utils'
|
||||
import RecomputeAllButton from './RecomputeAllButton.svelte'
|
||||
@@ -10,13 +10,9 @@
|
||||
|
||||
let timeout: NodeJS.Timeout | undefined = undefined
|
||||
let shouldRefresh = false
|
||||
let firstLoad = false
|
||||
let firstLoad = $state(false)
|
||||
let progressTimer: NodeJS.Timeout | undefined = undefined
|
||||
|
||||
$: !firstLoad &&
|
||||
canInitializeAll($initialized?.initializedComponents, $app) &&
|
||||
refresh('all initialized')
|
||||
|
||||
// $: console.log('canInitializeAll', firstLoad, $initialized?.initializedComponents)
|
||||
function canInitializeAll(initialized: string[] | undefined, app: App) {
|
||||
// console.log(
|
||||
@@ -53,9 +49,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
$: $recomputeAllContext.componentNumber =
|
||||
Object.values($runnableComponents).filter((x) => x.autoRefresh).length ?? 0
|
||||
|
||||
onMount(() => {
|
||||
if (appEditorContext) {
|
||||
appEditorContext.refreshComponents.set(() => refresh('onClick global'))
|
||||
@@ -112,7 +105,7 @@
|
||||
onRefresh(!inter, 'setInter ' + source)
|
||||
}
|
||||
|
||||
let refreshing: string[] = []
|
||||
let refreshing: string[] = $state([])
|
||||
function refresh(reason: string, excludeId: string | undefined = undefined) {
|
||||
let isFirstLoad = false
|
||||
if (!firstLoad && reason == 'all initialized') {
|
||||
@@ -182,6 +175,19 @@
|
||||
setInter: (n) => setInter(n, 'all context')
|
||||
}
|
||||
})
|
||||
$effect(() => {
|
||||
;[$initialized?.initializedComponents]
|
||||
|
||||
if (!firstLoad) {
|
||||
untrack(() => {
|
||||
canInitializeAll($initialized?.initializedComponents, app) && refresh('all initialized')
|
||||
})
|
||||
}
|
||||
})
|
||||
$effect(() => {
|
||||
$recomputeAllContext.componentNumber =
|
||||
Object.values($runnableComponents).filter((x) => x.autoRefresh).length ?? 0
|
||||
})
|
||||
</script>
|
||||
|
||||
<RecomputeAllButton
|
||||
|
||||
@@ -10,20 +10,11 @@
|
||||
import EventHandlerItem from './settingsPanel/EventHandlerItem.svelte'
|
||||
import type { TableAction } from './component'
|
||||
|
||||
const { selectedComponent, app, stateId, runnableComponents } =
|
||||
const { selectedComponent, app, stateId, runnableComponents } = $state(
|
||||
getContext<AppViewerContext>('AppViewerContext')
|
||||
)
|
||||
|
||||
let firstComponent = $selectedComponent?.[0]
|
||||
|
||||
$: $selectedComponent?.[0] != firstComponent && (firstComponent = $selectedComponent?.[0])
|
||||
|
||||
$: hiddenInlineScript = $app?.hiddenInlineScripts
|
||||
?.map((x, i) => ({ script: x, index: i }))
|
||||
.find(({ script, index }) => $selectedComponent?.includes(BG_PREFIX + index))
|
||||
|
||||
$: gridItemWithLocation = findGridItemWithLocation($app, firstComponent)
|
||||
$: tableActionSettings = findTableActionSettings($app, firstComponent)
|
||||
$: menuItemsSettings = findMenuItemsSettings($app, firstComponent)
|
||||
let firstComponent = $state($selectedComponent?.[0])
|
||||
|
||||
function findTableActionSettings(app: App, id: string | undefined) {
|
||||
return allItemsWithLocation(app.grid, app.subgrids)
|
||||
@@ -107,6 +98,17 @@
|
||||
}
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
$effect(() => {
|
||||
$selectedComponent?.[0] != firstComponent && (firstComponent = $selectedComponent?.[0])
|
||||
})
|
||||
let hiddenInlineScript = $derived(
|
||||
app?.hiddenInlineScripts
|
||||
?.map((x, i) => ({ script: x, index: i }))
|
||||
.find(({ script, index }) => $selectedComponent?.includes(BG_PREFIX + index))
|
||||
)
|
||||
let gridItemWithLocation = $derived(findGridItemWithLocation(app, firstComponent))
|
||||
let tableActionSettings = $derived(findTableActionSettings(app, firstComponent))
|
||||
let menuItemsSettings = $derived(findMenuItemsSettings(app, firstComponent))
|
||||
</script>
|
||||
|
||||
{#if gridItemWithLocation}
|
||||
@@ -116,17 +118,17 @@
|
||||
() => gridItemWithLocation,
|
||||
(cs) => {
|
||||
if (gridItemWithLocation?.location.type === 'grid') {
|
||||
$app.grid[gridItemWithLocation.location.gridItemIndex] = cs.item
|
||||
app.grid[gridItemWithLocation.location.gridItemIndex] = cs.item
|
||||
} else if (
|
||||
gridItemWithLocation?.location.type === 'subgrid' &&
|
||||
Array.isArray($app.subgrids?.[gridItemWithLocation.location.subgridKey])
|
||||
Array.isArray(app.subgrids?.[gridItemWithLocation.location.subgridKey])
|
||||
) {
|
||||
if (
|
||||
$app.subgrids[gridItemWithLocation.location.subgridKey][
|
||||
app.subgrids[gridItemWithLocation.location.subgridKey][
|
||||
gridItemWithLocation.location.subgridItemIndex
|
||||
]
|
||||
) {
|
||||
$app.subgrids[gridItemWithLocation.location.subgridKey][
|
||||
app.subgrids[gridItemWithLocation.location.subgridKey][
|
||||
gridItemWithLocation.location.subgridItemIndex
|
||||
] = cs.item
|
||||
}
|
||||
@@ -149,14 +151,14 @@
|
||||
if (tableActionSettings.gridItemLocation.type === 'grid') {
|
||||
const { gridItemIndex } = tableActionSettings.gridItemLocation
|
||||
const { key, index } = tableActionSettings.location
|
||||
if ($app.grid[gridItemIndex]?.data?.[key]) {
|
||||
$app.grid[gridItemIndex].data[key][index] = cs.item.data
|
||||
if (app.grid[gridItemIndex]?.data?.[key]) {
|
||||
app.grid[gridItemIndex].data[key][index] = cs.item.data
|
||||
}
|
||||
} else if (tableActionSettings.gridItemLocation.type === 'subgrid') {
|
||||
const { subgridKey, subgridItemIndex } = tableActionSettings.gridItemLocation
|
||||
const { key, index } = tableActionSettings.location
|
||||
if ($app.subgrids?.[subgridKey]?.[subgridItemIndex]?.data?.[key]) {
|
||||
$app.subgrids[subgridKey][subgridItemIndex].data[key][index] = cs.item.data
|
||||
if (app.subgrids?.[subgridKey]?.[subgridItemIndex]?.data?.[key]) {
|
||||
app.subgrids[subgridKey][subgridItemIndex].data[key][index] = cs.item.data
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -165,7 +167,7 @@
|
||||
duplicateMoveAllowed={false}
|
||||
onDelete={() => {
|
||||
if (tableActionSettings) {
|
||||
const item = findGridItemWithLocation($app, tableActionSettings.parent)
|
||||
const item = findGridItemWithLocation(app, tableActionSettings.parent)
|
||||
if (!item) return
|
||||
const { item: parent, location } = item
|
||||
if (parent.data.type === 'tablecomponent') {
|
||||
@@ -174,15 +176,13 @@
|
||||
)
|
||||
if (location.type === 'grid') {
|
||||
const { gridItemIndex } = location
|
||||
if ($app.grid[gridItemIndex]?.data?.type === 'tablecomponent') {
|
||||
$app.grid[gridItemIndex].data.actionButtons = newActionButtons
|
||||
if (app.grid[gridItemIndex]?.data?.type === 'tablecomponent') {
|
||||
app.grid[gridItemIndex].data.actionButtons = newActionButtons
|
||||
}
|
||||
} else if (location.type === 'subgrid') {
|
||||
const { subgridKey, subgridItemIndex } = location
|
||||
if (
|
||||
$app.subgrids?.[subgridKey]?.[subgridItemIndex]?.data?.type === 'tablecomponent'
|
||||
) {
|
||||
$app.subgrids[subgridKey][subgridItemIndex].data.actionButtons = newActionButtons
|
||||
if (app.subgrids?.[subgridKey]?.[subgridItemIndex]?.data?.type === 'tablecomponent') {
|
||||
app.subgrids[subgridKey][subgridItemIndex].data.actionButtons = newActionButtons
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -192,13 +192,13 @@
|
||||
)
|
||||
if (location.type === 'grid') {
|
||||
const { gridItemIndex } = location
|
||||
if (itemHasActions($app.grid[gridItemIndex])) {
|
||||
$app.grid[gridItemIndex].data.actions = newActions
|
||||
if (itemHasActions(app.grid[gridItemIndex])) {
|
||||
app.grid[gridItemIndex].data.actions = newActions
|
||||
}
|
||||
} else {
|
||||
const { subgridKey, subgridItemIndex } = location
|
||||
if (itemHasActions($app.subgrids?.[subgridKey]?.[subgridItemIndex])) {
|
||||
$app.subgrids[subgridKey][subgridItemIndex].data.actions = newActions
|
||||
if (itemHasActions(app.subgrids?.[subgridKey]?.[subgridItemIndex])) {
|
||||
app.subgrids[subgridKey][subgridItemIndex].data.actions = newActions
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -216,13 +216,13 @@
|
||||
if (menuItemsSettings) {
|
||||
if (menuItemsSettings.gridItemLocation.type === 'grid') {
|
||||
const { gridItemIndex } = menuItemsSettings.gridItemLocation
|
||||
if ($app.grid[gridItemIndex]?.data?.type === 'menucomponent') {
|
||||
$app.grid[gridItemIndex].data.menuItems[cs.index] = cs.item.data
|
||||
if (app.grid[gridItemIndex]?.data?.type === 'menucomponent') {
|
||||
app.grid[gridItemIndex].data.menuItems[cs.index] = cs.item.data
|
||||
}
|
||||
} else if (menuItemsSettings.gridItemLocation.type === 'subgrid') {
|
||||
const { subgridKey, subgridItemIndex } = menuItemsSettings.gridItemLocation
|
||||
if ($app.subgrids?.[subgridKey]?.[subgridItemIndex]?.data?.type === 'menucomponent') {
|
||||
$app.subgrids[subgridKey][subgridItemIndex].data.menuItems[cs.index] = cs.item.data
|
||||
if (app.subgrids?.[subgridKey]?.[subgridItemIndex]?.data?.type === 'menucomponent') {
|
||||
app.subgrids[subgridKey][subgridItemIndex].data.menuItems[cs.index] = cs.item.data
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -230,7 +230,7 @@
|
||||
}
|
||||
onDelete={() => {
|
||||
if (menuItemsSettings) {
|
||||
const item = findGridItemWithLocation($app, menuItemsSettings.parent)
|
||||
const item = findGridItemWithLocation(app, menuItemsSettings.parent)
|
||||
if (!item) return
|
||||
const { item: parent, location } = item
|
||||
if (parent.data.type === 'menucomponent') {
|
||||
@@ -239,13 +239,13 @@
|
||||
)
|
||||
if (location.type === 'grid') {
|
||||
const { gridItemIndex } = location
|
||||
if ($app.grid[gridItemIndex]?.data?.type === 'menucomponent') {
|
||||
$app.grid[gridItemIndex].data.menuItems = newItems
|
||||
if (app.grid[gridItemIndex]?.data?.type === 'menucomponent') {
|
||||
app.grid[gridItemIndex].data.menuItems = newItems
|
||||
}
|
||||
} else if (location.type === 'subgrid') {
|
||||
const { subgridKey, subgridItemIndex } = location
|
||||
if ($app.subgrids?.[subgridKey]?.[subgridItemIndex]?.data?.type === 'menucomponent') {
|
||||
$app.subgrids[subgridKey][subgridItemIndex].data.menuItems = newItems
|
||||
if (app.subgrids?.[subgridKey]?.[subgridItemIndex]?.data?.type === 'menucomponent') {
|
||||
app.subgrids[subgridKey][subgridItemIndex].data.menuItems = newItems
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -260,7 +260,7 @@
|
||||
bind:runnable={
|
||||
() => hiddenInlineScript.script,
|
||||
(r) => {
|
||||
$app.hiddenInlineScripts[hiddenInlineScript.index] = r
|
||||
app.hiddenInlineScripts[hiddenInlineScript.index] = r
|
||||
}
|
||||
}
|
||||
{id}
|
||||
@@ -276,8 +276,8 @@
|
||||
bind:inputSpecs={
|
||||
() => hiddenInlineScript.script.fields,
|
||||
(is) => {
|
||||
if ($app.hiddenInlineScripts[hiddenInlineScript.index]) {
|
||||
$app.hiddenInlineScripts[hiddenInlineScript.index].fields = is
|
||||
if (app.hiddenInlineScripts[hiddenInlineScript.index]) {
|
||||
app.hiddenInlineScripts[hiddenInlineScript.index].fields = is
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -301,8 +301,8 @@
|
||||
bind:value={
|
||||
() => hiddenInlineScript.script.recomputeIds ?? [],
|
||||
(v) => {
|
||||
if ($app.hiddenInlineScripts[hiddenInlineScript.index]) {
|
||||
$app.hiddenInlineScripts[hiddenInlineScript.index].recomputeIds = v
|
||||
if (app.hiddenInlineScripts[hiddenInlineScript.index]) {
|
||||
app.hiddenInlineScripts[hiddenInlineScript.index].recomputeIds = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { run } from 'svelte/legacy'
|
||||
|
||||
import { push } from '$lib/history'
|
||||
import { classNames } from '$lib/utils'
|
||||
import { createEventDispatcher, getContext, onDestroy } from 'svelte'
|
||||
@@ -21,18 +23,31 @@
|
||||
import GridViewer from './GridViewer.svelte'
|
||||
import GridEditorMenu from './GridEditorMenu.svelte'
|
||||
|
||||
export let containerHeight: number | undefined = undefined
|
||||
export let containerWidth: number | undefined = undefined
|
||||
let classes = ''
|
||||
interface Props {
|
||||
containerHeight?: number | undefined
|
||||
containerWidth?: number | undefined
|
||||
class?: string
|
||||
style?: string
|
||||
noPadding?: boolean
|
||||
noYPadding?: boolean
|
||||
subGridId: string
|
||||
visible?: boolean
|
||||
id: string
|
||||
shouldHighlight?: boolean
|
||||
}
|
||||
|
||||
export { classes as class }
|
||||
export let style = ''
|
||||
export let noPadding = false
|
||||
export let noYPadding = false
|
||||
export let subGridId: string
|
||||
export let visible: boolean = true
|
||||
export let id: string
|
||||
export let shouldHighlight: boolean = true
|
||||
let {
|
||||
containerHeight = undefined,
|
||||
containerWidth = undefined,
|
||||
class: classes = '',
|
||||
style = '',
|
||||
noPadding = false,
|
||||
noYPadding = false,
|
||||
subGridId,
|
||||
visible = true,
|
||||
id,
|
||||
shouldHighlight = true
|
||||
}: Props = $props()
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@@ -50,17 +65,19 @@
|
||||
|
||||
const editorContext = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
let isActive = false
|
||||
let isActive = $state(false)
|
||||
let sber = editorContext?.componentActive?.subscribe((x) => (isActive = x))
|
||||
|
||||
let everVisible = visible
|
||||
let everVisible = $state(visible)
|
||||
|
||||
$: visible && !everVisible && (everVisible = true)
|
||||
run(() => {
|
||||
visible && !everVisible && (everVisible = true)
|
||||
})
|
||||
|
||||
onDestroy(() => {
|
||||
sber?.()
|
||||
})
|
||||
$: highlight = id === $focusedGrid?.parentComponentId && shouldHighlight
|
||||
let highlight = $derived(id === $focusedGrid?.parentComponentId && shouldHighlight)
|
||||
|
||||
const onpointerdown = (e) => {
|
||||
dispatch('focus')
|
||||
@@ -68,21 +85,23 @@
|
||||
|
||||
function selectComponent(e: PointerEvent, id: string) {
|
||||
if (!$connectingInput.opened) {
|
||||
selectId(e, id, selectedComponent, $app)
|
||||
selectId(e, id, selectedComponent, app)
|
||||
}
|
||||
}
|
||||
|
||||
function lock(dataItem: GridItem) {
|
||||
let fComponent = findGridItem($app, dataItem.id)
|
||||
let fComponent = findGridItem(app, dataItem.id)
|
||||
if (fComponent) {
|
||||
fComponent = toggleFixed(fComponent)
|
||||
}
|
||||
$app = $app
|
||||
// $app = $app
|
||||
}
|
||||
|
||||
let container: HTMLElement | undefined = undefined
|
||||
let container: HTMLElement | undefined = $state(undefined)
|
||||
|
||||
$: maxRow = maxHeight($app.subgrids?.[subGridId] ?? [], containerHeight ?? 0, $breakpoint)
|
||||
let maxRow = $derived(
|
||||
maxHeight(app.subgrids?.[subGridId] ?? [], containerHeight ?? 0, $breakpoint)
|
||||
)
|
||||
|
||||
export function moveComponentBetweenSubgrids(
|
||||
componentId: string,
|
||||
@@ -91,27 +110,27 @@
|
||||
position?: { x: number; y: number }
|
||||
) {
|
||||
// Find the component in the source subgrid
|
||||
const component = findGridItem($app, componentId)
|
||||
const component = findGridItem(app, componentId)
|
||||
|
||||
if (!component) {
|
||||
return
|
||||
}
|
||||
|
||||
let parentGrid = findGridItemParentGrid($app, component.id)
|
||||
let parentGrid = findGridItemParentGrid(app, component.id)
|
||||
|
||||
if (parentGrid) {
|
||||
$app.subgrids &&
|
||||
($app.subgrids[parentGrid] = $app.subgrids[parentGrid].filter(
|
||||
app.subgrids &&
|
||||
(app.subgrids[parentGrid] = app.subgrids[parentGrid].filter(
|
||||
(item) => item.id !== component?.id
|
||||
))
|
||||
} else {
|
||||
$app.grid = $app.grid.filter((item) => item.id !== component?.id)
|
||||
app.grid = app.grid.filter((item) => item.id !== component?.id)
|
||||
}
|
||||
|
||||
const gridItem = component
|
||||
|
||||
insertNewGridItem(
|
||||
$app,
|
||||
app,
|
||||
(id) => ({ ...gridItem.data, id }),
|
||||
{ parentComponentId: parentComponentId, subGridIndex: subGridIndex },
|
||||
Object.fromEntries(gridColumns.map((column) => [column, gridItem[column]])),
|
||||
@@ -123,8 +142,8 @@
|
||||
true
|
||||
)
|
||||
|
||||
// Update the app state
|
||||
$app = { ...$app }
|
||||
// // Update the app state
|
||||
// $app = { ...$app }
|
||||
|
||||
if (parentGrid) {
|
||||
$focusedGrid = {
|
||||
@@ -140,27 +159,27 @@
|
||||
|
||||
export function moveToRoot(componentId: string, position?: { x: number; y: number }) {
|
||||
// Find the component in the source subgrid
|
||||
const component = findGridItem($app, componentId)
|
||||
const component = findGridItem(app, componentId)
|
||||
|
||||
if (!component) {
|
||||
return
|
||||
}
|
||||
|
||||
let parentGrid = findGridItemParentGrid($app, component.id)
|
||||
let parentGrid = findGridItemParentGrid(app, component.id)
|
||||
|
||||
if (parentGrid) {
|
||||
$app.subgrids &&
|
||||
($app.subgrids[parentGrid] = $app.subgrids[parentGrid].filter(
|
||||
app.subgrids &&
|
||||
(app.subgrids[parentGrid] = app.subgrids[parentGrid].filter(
|
||||
(item) => item.id !== component?.id
|
||||
))
|
||||
} else {
|
||||
$app.grid = $app.grid.filter((item) => item.id !== component?.id)
|
||||
app.grid = app.grid.filter((item) => item.id !== component?.id)
|
||||
}
|
||||
|
||||
const gridItem = component
|
||||
|
||||
insertNewGridItem(
|
||||
$app,
|
||||
app,
|
||||
(id) => ({ ...gridItem.data, id }),
|
||||
undefined,
|
||||
Object.fromEntries(gridColumns.map((column) => [column, gridItem[column]])),
|
||||
@@ -173,17 +192,17 @@
|
||||
)
|
||||
|
||||
// Update the app state
|
||||
$app = { ...$app }
|
||||
// $app = { ...$app }
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if everVisible || $app.eagerRendering}
|
||||
{#if everVisible || app.eagerRendering}
|
||||
<div
|
||||
class="translate-x-0 translate-y-0 w-full subgrid {visible
|
||||
? 'visible'
|
||||
: 'invisible h-0 overflow-hidden'}"
|
||||
bind:this={container}
|
||||
on:pointerdown={onpointerdown}
|
||||
{onpointerdown}
|
||||
>
|
||||
<div
|
||||
class={twMerge(
|
||||
@@ -201,23 +220,19 @@
|
||||
isActive && !$selectedComponent?.includes(id)
|
||||
? 'outline-orange-600'
|
||||
: 'outline-gray-400 dark:outline-gray-600'
|
||||
}`
|
||||
}`
|
||||
: ''}
|
||||
>
|
||||
<Grid
|
||||
allIdsInPath={$allIdsInPath}
|
||||
items={$app.subgrids?.[subGridId] ?? []}
|
||||
items={app.subgrids?.[subGridId] ?? []}
|
||||
on:redraw={(e) => {
|
||||
push(editorContext?.history, $app)
|
||||
if ($app.subgrids) {
|
||||
$app.subgrids[subGridId] = e.detail
|
||||
push(editorContext?.history, app)
|
||||
if (app.subgrids) {
|
||||
app.subgrids[subGridId] = e.detail
|
||||
}
|
||||
}}
|
||||
selectedIds={$selectedComponent}
|
||||
let:dataItem
|
||||
let:overlapped
|
||||
let:moveMode
|
||||
let:componentDraggedId
|
||||
scroller={container}
|
||||
parentWidth={$parentWidth - 17}
|
||||
{containerWidth}
|
||||
@@ -227,7 +242,7 @@
|
||||
if (!overlapped) {
|
||||
moveToRoot(id, { x, y })
|
||||
} else {
|
||||
const overlappedComponent = findGridItem($app, overlapped)
|
||||
const overlappedComponent = findGridItem(app, overlapped)
|
||||
|
||||
if (overlappedComponent && !isContainer(overlappedComponent.data.type)) {
|
||||
return
|
||||
@@ -250,87 +265,93 @@
|
||||
}}
|
||||
disableMove={!!$connectingInput.opened}
|
||||
>
|
||||
<ComponentWrapper
|
||||
id={dataItem.id}
|
||||
type={dataItem.data.type}
|
||||
class={classNames(
|
||||
'h-full w-full center-center',
|
||||
$selectedComponent?.includes(dataItem.id) ? 'active-grid-item' : '',
|
||||
'top-0'
|
||||
)}
|
||||
>
|
||||
<GridEditorMenu id={dataItem.id}>
|
||||
<Component
|
||||
{overlapped}
|
||||
fullHeight={dataItem?.[$breakpoint === 'sm' ? 3 : 12]?.fullHeight}
|
||||
render={visible}
|
||||
component={dataItem.data}
|
||||
selected={Boolean($selectedComponent?.includes(dataItem.id))}
|
||||
locked={isFixed(dataItem)}
|
||||
on:lock={() => lock(dataItem)}
|
||||
on:expand={() => {
|
||||
const parentGridItem = findGridItem($app, id)
|
||||
{#snippet children({ dataItem, overlapped, moveMode, componentDraggedId })}
|
||||
<ComponentWrapper
|
||||
id={dataItem.id}
|
||||
type={dataItem.data.type}
|
||||
class={classNames(
|
||||
'h-full w-full center-center',
|
||||
$selectedComponent?.includes(dataItem.id) ? 'active-grid-item' : '',
|
||||
'top-0'
|
||||
)}
|
||||
>
|
||||
<GridEditorMenu id={dataItem.id}>
|
||||
<Component
|
||||
{overlapped}
|
||||
fullHeight={dataItem?.[$breakpoint === 'sm' ? 3 : 12]?.fullHeight}
|
||||
render={visible}
|
||||
component={dataItem.data}
|
||||
selected={Boolean($selectedComponent?.includes(dataItem.id))}
|
||||
locked={isFixed(dataItem)}
|
||||
on:lock={() => lock(dataItem)}
|
||||
on:expand={() => {
|
||||
const parentGridItem = findGridItem(app, id)
|
||||
|
||||
if (!parentGridItem) {
|
||||
return
|
||||
}
|
||||
if (!parentGridItem) {
|
||||
return
|
||||
}
|
||||
|
||||
$selectedComponent = [dataItem.id]
|
||||
push(editorContext?.history, $app)
|
||||
$selectedComponent = [dataItem.id]
|
||||
push(editorContext?.history, app)
|
||||
|
||||
expandGriditem(
|
||||
$app.subgrids?.[subGridId] ?? [],
|
||||
dataItem.id,
|
||||
$breakpoint,
|
||||
parentGridItem
|
||||
)
|
||||
$app = $app
|
||||
}}
|
||||
on:fillHeight={() => {
|
||||
const gridItem = findGridItem($app, dataItem.id)
|
||||
const b = $breakpoint === 'sm' ? 3 : 12
|
||||
expandGriditem(
|
||||
app.subgrids?.[subGridId] ?? [],
|
||||
dataItem.id,
|
||||
$breakpoint,
|
||||
parentGridItem
|
||||
)
|
||||
// $app = $app
|
||||
}}
|
||||
on:fillHeight={() => {
|
||||
const gridItem = findGridItem(app, dataItem.id)
|
||||
const b = $breakpoint === 'sm' ? 3 : 12
|
||||
|
||||
if (gridItem?.[b]) {
|
||||
gridItem[b].fullHeight = !gridItem[b].fullHeight
|
||||
}
|
||||
$app = $app
|
||||
}}
|
||||
{moveMode}
|
||||
{componentDraggedId}
|
||||
/>
|
||||
</GridEditorMenu>
|
||||
</ComponentWrapper>
|
||||
if (gridItem?.[b]) {
|
||||
gridItem[b].fullHeight = !gridItem[b].fullHeight
|
||||
}
|
||||
// $app = $app
|
||||
}}
|
||||
{moveMode}
|
||||
{componentDraggedId}
|
||||
/>
|
||||
</GridEditorMenu>
|
||||
</ComponentWrapper>
|
||||
{/snippet}
|
||||
</Grid>
|
||||
</div>
|
||||
{:else}
|
||||
<GridViewer
|
||||
allIdsInPath={$allIdsInPath}
|
||||
items={$app.subgrids?.[subGridId] ?? []}
|
||||
let:dataItem
|
||||
items={app.subgrids?.[subGridId] ?? []}
|
||||
breakpoint={$breakpoint}
|
||||
parentWidth={$parentWidth - 17}
|
||||
{containerWidth}
|
||||
{maxRow}
|
||||
>
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
on:pointerdown|stopPropagation={(e) => selectComponent(e, dataItem.id)}
|
||||
class={classNames('h-full w-full center-center', 'top-0')}
|
||||
>
|
||||
<Component
|
||||
fullHeight={dataItem?.[$breakpoint === 'sm' ? 3 : 12]?.fullHeight}
|
||||
render={visible}
|
||||
component={dataItem.data}
|
||||
selected={Boolean($selectedComponent?.includes(dataItem.id))}
|
||||
locked={isFixed(dataItem)}
|
||||
/>
|
||||
</div>
|
||||
{#snippet children({ dataItem })}
|
||||
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
||||
<div
|
||||
onpointerdown={(e) => {
|
||||
e.stopPropagation()
|
||||
selectComponent(e, dataItem.id)
|
||||
}}
|
||||
class={classNames('h-full w-full center-center', 'top-0')}
|
||||
>
|
||||
<Component
|
||||
fullHeight={dataItem?.[$breakpoint === 'sm' ? 3 : 12]?.fullHeight}
|
||||
render={visible}
|
||||
component={dataItem.data}
|
||||
selected={Boolean($selectedComponent?.includes(dataItem.id))}
|
||||
locked={isFixed(dataItem)}
|
||||
/>
|
||||
</div>
|
||||
{/snippet}
|
||||
</GridViewer>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{:else if $app.lazyInitRequire == undefined}
|
||||
{#each $app?.subgrids?.[subGridId] ?? [] as item}
|
||||
{:else if app.lazyInitRequire == undefined}
|
||||
{#each app?.subgrids?.[subGridId] ?? [] as item}
|
||||
<Component selected={false} fullHeight={false} render={false} component={item.data} />
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import { parseOutputs } from '$lib/infer'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
import type { EvalV2AppInput, TemplateV2Input } from '../inputType'
|
||||
import type { Writable } from 'svelte/store'
|
||||
import type { App } from '../types'
|
||||
|
||||
export async function inferDeps(
|
||||
code: string,
|
||||
worldOutputs: Record<string, any>,
|
||||
componentInput: EvalV2AppInput | TemplateV2Input,
|
||||
app: Writable<App>
|
||||
app: App
|
||||
) {
|
||||
const outputs = await parseOutputs(code, true)
|
||||
if (outputs && componentInput) {
|
||||
@@ -28,7 +27,7 @@ export async function inferDeps(
|
||||
}))
|
||||
if (!deepEqual(noutputs, componentInput.connections)) {
|
||||
componentInput.connections = noutputs
|
||||
app.update((old) => old)
|
||||
// app.update((old) => old)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,27 +24,27 @@
|
||||
const ITEM_TYPE = 'wm-grid-items'
|
||||
function getSortedGridItemsOfChildren(): GridItem[] {
|
||||
if (!$focusedGrid) {
|
||||
return $app.grid
|
||||
return app.grid
|
||||
}
|
||||
|
||||
if (!$app.subgrids) {
|
||||
if (!app.subgrids) {
|
||||
return []
|
||||
}
|
||||
|
||||
return $app.subgrids[`${$focusedGrid.parentComponentId}-${$focusedGrid.subGridIndex}`] ?? []
|
||||
return app.subgrids[`${$focusedGrid.parentComponentId}-${$focusedGrid.subGridIndex}`] ?? []
|
||||
}
|
||||
|
||||
function getGridItems(): GridItem[] {
|
||||
if ($app.grid.find((item) => item.id === $selectedComponent?.[0])) {
|
||||
return $app.grid
|
||||
if (app.grid.find((item) => item.id === $selectedComponent?.[0])) {
|
||||
return app.grid
|
||||
}
|
||||
|
||||
if (!$app.subgrids) {
|
||||
if (!app.subgrids) {
|
||||
return []
|
||||
}
|
||||
|
||||
return (
|
||||
Object.values($app.subgrids ?? {}).find((grid) =>
|
||||
Object.values(app.subgrids ?? {}).find((grid) =>
|
||||
grid.find((item) => item.id === $selectedComponent?.[0])
|
||||
) ?? []
|
||||
)
|
||||
@@ -105,9 +105,9 @@
|
||||
if (!$focusedGrid) {
|
||||
$selectedComponent = [getSortedGridItemsOfChildren()[0]?.id]
|
||||
event.preventDefault()
|
||||
} else if ($app.subgrids) {
|
||||
} else if (app.subgrids) {
|
||||
const index = $focusedGrid?.subGridIndex ?? 0
|
||||
const subgrid = $app.subgrids[`${$selectedComponent}-${index}`]
|
||||
const subgrid = app.subgrids[`${$selectedComponent}-${index}`]
|
||||
|
||||
if (!subgrid || subgrid.length === 0) {
|
||||
return
|
||||
@@ -128,7 +128,7 @@
|
||||
|
||||
export function handleArrowUp(event: KeyboardEvent) {
|
||||
if (!$selectedComponent) return
|
||||
let parentId = findGridItemParentGrid($app, $selectedComponent?.[0])?.split('-')[0]
|
||||
let parentId = findGridItemParentGrid(app, $selectedComponent?.[0])?.split('-')[0]
|
||||
|
||||
if (parentId) {
|
||||
$selectedComponent = [parentId]
|
||||
@@ -144,7 +144,7 @@
|
||||
}
|
||||
tempGridItems = undefined
|
||||
const copiedGridItems = $selectedComponent
|
||||
.map((x) => findGridItem($app, x))
|
||||
.map((x) => findGridItem(app, x))
|
||||
.filter((x) => x != undefined) as GridItem[]
|
||||
|
||||
copyGridItemsToClipboard(copiedGridItems, 'copy')
|
||||
@@ -156,9 +156,9 @@
|
||||
) {
|
||||
let allSubgrids = {}
|
||||
for (let item of items) {
|
||||
let subgrids = getAllSubgridsAndComponentIds($app, item.data)[0]
|
||||
let subgrids = getAllSubgridsAndComponentIds(app, item.data)[0]
|
||||
for (let key of subgrids) {
|
||||
allSubgrids[key] = $app.subgrids?.[key]
|
||||
allSubgrids[key] = app.subgrids?.[key]
|
||||
}
|
||||
}
|
||||
let success = await copyToClipboard(
|
||||
@@ -179,10 +179,10 @@
|
||||
return
|
||||
}
|
||||
$movingcomponents = JSON.parse(JSON.stringify($selectedComponent))
|
||||
push(history, $app)
|
||||
push(history, app)
|
||||
|
||||
let gridItems = $selectedComponent
|
||||
.map((x) => findGridItem($app, x))
|
||||
.map((x) => findGridItem(app, x))
|
||||
.filter((x) => x != undefined) as GridItem[]
|
||||
copyGridItemsToClipboard(gridItems, 'cut')
|
||||
|
||||
@@ -204,10 +204,10 @@
|
||||
}
|
||||
event.preventDefault()
|
||||
|
||||
push(history, $app)
|
||||
push(history, app)
|
||||
$movingcomponents = undefined
|
||||
let copiedGridItems: GridItem[] | undefined = undefined
|
||||
let subgrids = $app.subgrids ?? {}
|
||||
let subgrids = app.subgrids ?? {}
|
||||
const txt = event?.clipboardData?.getData('text')
|
||||
if (txt) {
|
||||
try {
|
||||
@@ -225,26 +225,26 @@
|
||||
for (let tempGridItem of tempGridItems) {
|
||||
if (
|
||||
$focusedGrid &&
|
||||
getAllSubgridsAndComponentIds($app, tempGridItem.data)[0].includes(
|
||||
getAllSubgridsAndComponentIds(app, tempGridItem.data)[0].includes(
|
||||
`${$focusedGrid.parentComponentId}-${$focusedGrid.subGridIndex}`
|
||||
)
|
||||
) {
|
||||
sendUserToast('Cannot paste a component into itself', true)
|
||||
return
|
||||
}
|
||||
let parentGrid = findGridItemParentGrid($app, tempGridItem.id)
|
||||
let parentGrid = findGridItemParentGrid(app, tempGridItem.id)
|
||||
if (parentGrid) {
|
||||
$app.subgrids &&
|
||||
($app.subgrids[parentGrid] = $app.subgrids[parentGrid].filter(
|
||||
app.subgrids &&
|
||||
(app.subgrids[parentGrid] = app.subgrids[parentGrid].filter(
|
||||
(item) => item.id !== tempGridItem?.id
|
||||
))
|
||||
} else {
|
||||
$app.grid = $app.grid.filter((item) => item.id !== tempGridItem?.id)
|
||||
app.grid = app.grid.filter((item) => item.id !== tempGridItem?.id)
|
||||
}
|
||||
|
||||
const gridItem = tempGridItem
|
||||
insertNewGridItem(
|
||||
$app,
|
||||
app,
|
||||
(id) => ({ ...gridItem.data, id }),
|
||||
$focusedGrid,
|
||||
Object.fromEntries(gridColumns.map((column) => [column, gridItem[column]])),
|
||||
@@ -258,12 +258,12 @@
|
||||
} else if (copiedGridItems) {
|
||||
let nitems: string[] = []
|
||||
for (let copiedGridItem of copiedGridItems) {
|
||||
let newItem = copyComponent($app, copiedGridItem, $focusedGrid, subgrids, [])
|
||||
let newItem = copyComponent(app, copiedGridItem, $focusedGrid, subgrids, [])
|
||||
newItem && nitems.push(newItem)
|
||||
}
|
||||
$selectedComponent = nitems.map((x) => x)
|
||||
}
|
||||
|
||||
$app = $app
|
||||
// app = app
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -75,15 +75,30 @@
|
||||
import type { AppComponent } from './components'
|
||||
import { Button } from '$lib/components/common'
|
||||
|
||||
export let component: AppComponent
|
||||
export let render: boolean
|
||||
export let componentContainerHeight: number
|
||||
export let errorHandledByComponent: boolean
|
||||
export let inlineEditorOpened: boolean
|
||||
export let initializing: boolean | undefined = undefined
|
||||
interface Props {
|
||||
component: AppComponent
|
||||
render: boolean
|
||||
componentContainerHeight: number
|
||||
errorHandledByComponent: boolean
|
||||
inlineEditorOpened: boolean
|
||||
initializing?: boolean | undefined
|
||||
}
|
||||
|
||||
let {
|
||||
component,
|
||||
render,
|
||||
componentContainerHeight,
|
||||
errorHandledByComponent = $bindable(),
|
||||
inlineEditorOpened = $bindable(),
|
||||
initializing = $bindable(undefined)
|
||||
}: Props = $props()
|
||||
</script>
|
||||
|
||||
<svelte:boundary>
|
||||
<svelte:boundary
|
||||
onerror={(e) => {
|
||||
console.log(`error in rendering component ${component.id}`, e)
|
||||
}}
|
||||
>
|
||||
{#if component.type === 'displaycomponent'}
|
||||
<AppDisplayComponent
|
||||
id={component.id}
|
||||
@@ -797,7 +812,9 @@
|
||||
>{error}</pre
|
||||
>
|
||||
<div class="flex mt-4">
|
||||
<Button wrapperClasses="border rounded !border-gray-400" color="dark" on:click={reset}>reset</Button>
|
||||
<Button wrapperClasses="border rounded !border-gray-400" color="dark" on:click={reset}
|
||||
>reset</Button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
{/snippet}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
<script lang="ts" context="module">
|
||||
<script lang="ts" module>
|
||||
let outTimeout: NodeJS.Timeout | undefined = undefined
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { run, stopPropagation } from 'svelte/legacy'
|
||||
|
||||
import { getContext, onMount } from 'svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import type { AppEditorContext, AppViewerContext } from '../../types'
|
||||
@@ -13,16 +15,29 @@
|
||||
import { findGridItemParentGrid, isContainer } from '../appUtils'
|
||||
import ComponentInner from './ComponentInner.svelte'
|
||||
|
||||
export let component: AppComponent
|
||||
export let selected: boolean
|
||||
export let locked: boolean = false
|
||||
export let fullHeight: boolean
|
||||
export let overlapped: string | undefined = undefined
|
||||
export let moveMode: string | undefined = undefined
|
||||
export let componentDraggedId: string | undefined = undefined
|
||||
export let render: boolean = false
|
||||
interface Props {
|
||||
component: AppComponent
|
||||
selected: boolean
|
||||
locked?: boolean
|
||||
fullHeight: boolean
|
||||
overlapped?: string | undefined
|
||||
moveMode?: string | undefined
|
||||
componentDraggedId?: string | undefined
|
||||
render?: boolean
|
||||
}
|
||||
|
||||
let initializing: boolean | undefined
|
||||
let {
|
||||
component,
|
||||
selected,
|
||||
locked = false,
|
||||
fullHeight,
|
||||
overlapped = undefined,
|
||||
moveMode = undefined,
|
||||
componentDraggedId = undefined,
|
||||
render = false
|
||||
}: Props = $props()
|
||||
|
||||
let initializing: boolean | undefined = $state()
|
||||
|
||||
const { mode, app, hoverStore, connectingInput } =
|
||||
getContext<AppViewerContext>('AppViewerContext')
|
||||
@@ -31,15 +46,16 @@
|
||||
const componentActive = editorContext?.componentActive
|
||||
|
||||
const movingcomponents = editorContext?.movingcomponents
|
||||
$: ismoving =
|
||||
let ismoving = $derived(
|
||||
movingcomponents != undefined && $mode == 'dnd' && $movingcomponents?.includes(component.id)
|
||||
)
|
||||
|
||||
let errorHandledByComponent: boolean = false
|
||||
let componentContainerHeight: number = 0
|
||||
let componentContainerWidth: number = 0
|
||||
let errorHandledByComponent: boolean = $state(false)
|
||||
let componentContainerHeight: number = $state(0)
|
||||
let componentContainerWidth: number = $state(0)
|
||||
|
||||
let inlineEditorOpened: boolean = false
|
||||
let showSkeleton = false
|
||||
let inlineEditorOpened: boolean = $state(false)
|
||||
let showSkeleton = $state(false)
|
||||
|
||||
onMount(() => {
|
||||
setTimeout(() => {
|
||||
@@ -63,19 +79,19 @@
|
||||
}
|
||||
|
||||
function componentDraggedIsNotChild(componentDraggedId: string, componentId: string) {
|
||||
let parentGrid = findGridItemParentGrid($app, componentDraggedId)
|
||||
let parentGrid = findGridItemParentGrid(app, componentDraggedId)
|
||||
|
||||
return !parentGrid?.startsWith(`${componentId}-`)
|
||||
}
|
||||
|
||||
function areOnTheSameSubgrid(componentDraggedId: string, componentId: string) {
|
||||
return (
|
||||
findGridItemParentGrid($app, componentDraggedId) === findGridItemParentGrid($app, componentId)
|
||||
findGridItemParentGrid(app, componentDraggedId) === findGridItemParentGrid(app, componentId)
|
||||
)
|
||||
}
|
||||
|
||||
let cachedComponentDraggedIsNotChild: boolean | undefined
|
||||
let cachedAreOnTheSameSubgrid: boolean | undefined
|
||||
let cachedComponentDraggedIsNotChild: boolean | undefined = $state()
|
||||
let cachedAreOnTheSameSubgrid: boolean | undefined = $state()
|
||||
|
||||
function updateCache(componentDraggedId: string | undefined) {
|
||||
if (componentDraggedId) {
|
||||
@@ -90,19 +106,21 @@
|
||||
}
|
||||
}
|
||||
|
||||
$: updateCache(componentDraggedId)
|
||||
run(() => {
|
||||
updateCache(componentDraggedId)
|
||||
})
|
||||
</script>
|
||||
|
||||
<!-- svelte-ignore a11y-mouse-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<!-- svelte-ignore a11y_mouse_events_have_key_events -->
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<div
|
||||
on:mouseover|stopPropagation={() => {
|
||||
onmouseover={stopPropagation(() => {
|
||||
outTimeout && clearTimeout(outTimeout)
|
||||
if (component.id !== $hoverStore) {
|
||||
$hoverStore = component.id
|
||||
}
|
||||
}}
|
||||
on:mouseout|stopPropagation={mouseOut}
|
||||
})}
|
||||
onmouseout={stopPropagation(mouseOut)}
|
||||
class={twMerge(
|
||||
'h-full flex flex-col w-full component relative',
|
||||
initializing ? 'overflow-hidden h-0' : ''
|
||||
@@ -162,7 +180,7 @@
|
||||
<div class="absolute -top-8 w-40">
|
||||
<button
|
||||
class="border p-0.5 text-xs"
|
||||
on:click={() => {
|
||||
onclick={() => {
|
||||
$movingcomponents = undefined
|
||||
}}
|
||||
>
|
||||
@@ -183,11 +201,11 @@
|
||||
selected && $mode !== 'preview' ? 'outline outline-blue-600' : '',
|
||||
$mode != 'preview' ? 'cursor-pointer' : '',
|
||||
'relative z-auto',
|
||||
$app.css?.['app']?.['component']?.class,
|
||||
app.css?.['app']?.['component']?.class,
|
||||
'wm-app-component',
|
||||
ismoving ? 'animate-pulse' : ''
|
||||
)}
|
||||
style={$app.css?.['app']?.['component']?.style}
|
||||
style={app.css?.['app']?.['component']?.style}
|
||||
bind:clientHeight={componentContainerHeight}
|
||||
bind:clientWidth={componentContainerWidth}
|
||||
>
|
||||
@@ -202,19 +220,19 @@
|
||||
</div>
|
||||
</div>
|
||||
{#if initializing && render && showSkeleton}
|
||||
<!-- svelte-ignore a11y-mouse-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<!-- svelte-ignore a11y_mouse_events_have_key_events -->
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<div
|
||||
on:mouseover|stopPropagation={() => {
|
||||
onmouseover={stopPropagation(() => {
|
||||
if (component.id !== $hoverStore) {
|
||||
$hoverStore = component.id
|
||||
}
|
||||
}}
|
||||
on:mouseout|stopPropagation={() => {
|
||||
})}
|
||||
onmouseout={stopPropagation(() => {
|
||||
if ($hoverStore !== undefined) {
|
||||
$hoverStore = undefined
|
||||
}
|
||||
}}
|
||||
})}
|
||||
class="absolute inset-0 center-center flex-col border animate-skeleton dark:bg-frost-900/50 [animation-delay:1000ms]"
|
||||
></div>
|
||||
{/if}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
function selectComponent(e: PointerEvent, id: string) {
|
||||
if (!$connectingInput.opened) {
|
||||
selectId(e, id, selectedComponent, $app)
|
||||
selectId(e, id, selectedComponent, app)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
if (!$connectingInput.opened) {
|
||||
selectComponent(e, id)
|
||||
} else {
|
||||
const allIdsInPath = dfs($app.grid, id, $app.subgrids ?? {}) ?? []
|
||||
const allIdsInPath = dfs(app.grid, id, app.subgrids ?? {}) ?? []
|
||||
|
||||
allIdsInPath.forEach((id) => {
|
||||
$manuallyOpened[id] = true
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import type { AppEditorContext, AppViewerContext } from '../../types'
|
||||
import { getContext, tick } from 'svelte'
|
||||
import { getContext, tick, untrack } from 'svelte'
|
||||
import {
|
||||
components as componentsRecord,
|
||||
presets as presetsRecord,
|
||||
@@ -34,12 +34,12 @@
|
||||
let groups: Array<{
|
||||
name: string
|
||||
path: string
|
||||
}> = []
|
||||
}> = $state([])
|
||||
|
||||
let customComponents: Array<{
|
||||
name: string
|
||||
path: string
|
||||
}> = []
|
||||
}> = $state([])
|
||||
|
||||
async function fetchGroups() {
|
||||
groups = await listGroups($workspaceStore ?? '')
|
||||
@@ -53,16 +53,16 @@
|
||||
}
|
||||
|
||||
function addComponent(appComponentType: TypedComponent['type']): string {
|
||||
push(history, $app)
|
||||
push(history, app)
|
||||
|
||||
const id = insertNewGridItem(
|
||||
$app,
|
||||
app,
|
||||
appComponentFromType(appComponentType) as (id: string) => AppComponent,
|
||||
$focusedGrid
|
||||
)
|
||||
|
||||
$selectedComponent = [id]
|
||||
$app = $app
|
||||
// app = app
|
||||
return id
|
||||
}
|
||||
|
||||
@@ -72,21 +72,21 @@
|
||||
|
||||
if (!res) return
|
||||
|
||||
push(history, $app)
|
||||
push(history, app)
|
||||
|
||||
const id = copyComponent($app, res.value.item, $focusedGrid, res.value.subgrids, [])
|
||||
const id = copyComponent(app, res.value.item, $focusedGrid, res.value.subgrids, [])
|
||||
|
||||
if (id) {
|
||||
$selectedComponent = [id]
|
||||
$app = $app
|
||||
// app = app
|
||||
}
|
||||
}
|
||||
|
||||
async function addNewGroup() {
|
||||
push(history, $app)
|
||||
push(history, app)
|
||||
|
||||
const id = insertNewGridItem(
|
||||
$app,
|
||||
app,
|
||||
appComponentFromType('containercomponent', undefined, { groupFields: {} }) as (
|
||||
id: string
|
||||
) => AppComponent,
|
||||
@@ -95,7 +95,7 @@
|
||||
|
||||
if (id) {
|
||||
$selectedComponent = [id]
|
||||
$app = $app
|
||||
// app = app
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,10 +114,10 @@
|
||||
|
||||
if (!res) return
|
||||
|
||||
push(history, $app)
|
||||
push(history, app)
|
||||
|
||||
const id = insertNewGridItem(
|
||||
$app,
|
||||
app,
|
||||
appComponentFromType('customcomponent', undefined, {
|
||||
customComponent: {
|
||||
name: cc.name.replace(/-/g, '_').replace(/\s/g, '_'),
|
||||
@@ -131,17 +131,17 @@
|
||||
|
||||
if (id) {
|
||||
$selectedComponent = [id]
|
||||
$app = $app
|
||||
// app = app
|
||||
}
|
||||
}
|
||||
|
||||
function addPresetComponent(appComponentType: string): void {
|
||||
const preset = presetsRecord[appComponentType]
|
||||
|
||||
push(history, $app)
|
||||
push(history, app)
|
||||
|
||||
const id = insertNewGridItem(
|
||||
$app,
|
||||
app,
|
||||
appComponentFromType(preset.targetComponent, preset.configuration, undefined) as (
|
||||
id: string
|
||||
) => AppComponent,
|
||||
@@ -158,36 +158,40 @@
|
||||
$selectedComponent = [id]
|
||||
|
||||
if (appComponentType === 'topbarcomponent') {
|
||||
setUpTopBarComponentContent(id, $app)
|
||||
setUpTopBarComponentContent(id, app)
|
||||
}
|
||||
|
||||
$app = $app
|
||||
// app = app
|
||||
}
|
||||
|
||||
let search = ''
|
||||
let search = $state('')
|
||||
|
||||
$: componentsFiltered = COMPONENT_SETS.map((set) => ({
|
||||
...set,
|
||||
components: set.components?.filter((component) => {
|
||||
const name = componentsRecord[component].name.toLowerCase()
|
||||
return name.includes(search.toLowerCase().trim())
|
||||
}),
|
||||
presets: set.presets?.filter((preset) => {
|
||||
const presetName = presetsRecord[preset].name.toLowerCase()
|
||||
return presetName.includes(search.toLowerCase().trim())
|
||||
})
|
||||
}))
|
||||
let componentsFiltered = $derived(
|
||||
COMPONENT_SETS.map((set) => ({
|
||||
...set,
|
||||
components: set.components?.filter((component) => {
|
||||
const name = componentsRecord[component].name.toLowerCase()
|
||||
return name.includes(search.toLowerCase().trim())
|
||||
}),
|
||||
presets: set.presets?.filter((preset) => {
|
||||
const presetName = presetsRecord[preset].name.toLowerCase()
|
||||
return presetName.includes(search.toLowerCase().trim())
|
||||
})
|
||||
}))
|
||||
)
|
||||
|
||||
$: {
|
||||
$effect(() => {
|
||||
if ($workspaceStore) {
|
||||
fetchGroups()
|
||||
fetchCustomComponents()
|
||||
untrack(() => {
|
||||
fetchGroups()
|
||||
fetchCustomComponents()
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
let dndTimeout: NodeJS.Timeout | undefined = undefined
|
||||
let dndTimeout: NodeJS.Timeout | undefined = $state(undefined)
|
||||
|
||||
let ccDrawer: Drawer
|
||||
let ccDrawer: Drawer | undefined = $state()
|
||||
</script>
|
||||
|
||||
<Drawer bind:this={ccDrawer}>
|
||||
@@ -217,22 +221,25 @@
|
||||
<ListItem title={`${title}`} subtitle={`(${components.length})`}>
|
||||
<div class="flex flex-wrap gap-3 py-2">
|
||||
{#each components as item (item)}
|
||||
{@const SvelteComponent = componentsRecord[item].icon}
|
||||
<div class="w-[64px] relative">
|
||||
{#if DEPRECATED_COMPONENTS[item]}
|
||||
<div
|
||||
class="absolute -top-2 -right-2 bg-gray-100 text-gray-900 dark:bg-gray-800 dark:text-gray-100 rounded-md py-0.5 px-1 flex flex-row gap-1 items-center"
|
||||
>
|
||||
<Popover>
|
||||
<div slot="text">
|
||||
{DEPRECATED_COMPONENTS[item]}
|
||||
</div>
|
||||
{#snippet text()}
|
||||
<div>
|
||||
{DEPRECATED_COMPONENTS[item]}
|
||||
</div>
|
||||
{/snippet}
|
||||
<div class="font-normal text-2xs"> Deprecated </div>
|
||||
</Popover>
|
||||
</div>
|
||||
{/if}
|
||||
<button
|
||||
id={item}
|
||||
on:pointerdown={async (e) => {
|
||||
onpointerdown={async (e) => {
|
||||
const id = addComponent(item)
|
||||
dndTimeout && clearTimeout(dndTimeout)
|
||||
dndTimeout = setTimeout(async () => {
|
||||
@@ -248,7 +255,7 @@
|
||||
class="cursor-move transition-all border w-[64px] shadow-sm h-16 p-2 flex flex-col gap-2 items-center
|
||||
justify-center bg-surface rounded-md hover:bg-blue-50 dark:hover:bg-blue-900 duration-200 hover:border-blue-500"
|
||||
>
|
||||
<svelte:component this={componentsRecord[item].icon} class="text-primary" />
|
||||
<SvelteComponent class="text-primary" />
|
||||
</button>
|
||||
<div class="text-xs text-center flex-wrap text-secondary mt-1">
|
||||
{componentsRecord[item].name}
|
||||
@@ -257,17 +264,15 @@
|
||||
{/each}
|
||||
{#if presets}
|
||||
{#each presets as presetItem (presetItem)}
|
||||
{@const SvelteComponent_1 = presetsRecord[presetItem].icon}
|
||||
<div class="w-[64px]">
|
||||
<button
|
||||
on:click={() => addPresetComponent(presetItem)}
|
||||
onclick={() => addPresetComponent(presetItem)}
|
||||
title={presetsRecord[presetItem].name}
|
||||
class="transition-all border w-[64px] shadow-sm h-16 p-2 flex flex-col gap-2 items-center
|
||||
justify-center bg-surface rounded-md hover:bg-blue-50 dark:hover:bg-blue-900 duration-200 hover:border-blue-500"
|
||||
>
|
||||
<svelte:component
|
||||
this={presetsRecord[presetItem].icon}
|
||||
class="text-secondary"
|
||||
/>
|
||||
<SvelteComponent_1 class="text-secondary" />
|
||||
</button>
|
||||
<div class="text-xs text-center flex-wrap text-secondary mt-1">
|
||||
{presetsRecord[presetItem].name}
|
||||
@@ -286,7 +291,7 @@
|
||||
{#each groups as group (group.path)}
|
||||
<div class="w-[64px]">
|
||||
<button
|
||||
on:click={() => {
|
||||
onclick={() => {
|
||||
addGroup(group)
|
||||
}}
|
||||
title={group.name}
|
||||
@@ -303,7 +308,7 @@
|
||||
{/if}
|
||||
<div class="w-[64px]">
|
||||
<button
|
||||
on:click={() => {
|
||||
onclick={() => {
|
||||
addNewGroup()
|
||||
}}
|
||||
title=""
|
||||
@@ -322,7 +327,7 @@
|
||||
{#each customComponents as cc (cc.path)}
|
||||
<div class="w-[64px]">
|
||||
<button
|
||||
on:click={() => {
|
||||
onclick={() => {
|
||||
addCustomComponent(cc)
|
||||
}}
|
||||
title={cc.name}
|
||||
@@ -339,11 +344,11 @@
|
||||
{/if}
|
||||
<div class="w-[64px]">
|
||||
<button
|
||||
on:click={() => {
|
||||
onclick={() => {
|
||||
if (!$enterpriseLicense) {
|
||||
sendUserToast('Custom components are only available on the EE', true)
|
||||
} else {
|
||||
ccDrawer.openDrawer()
|
||||
ccDrawer?.openDrawer()
|
||||
}
|
||||
}}
|
||||
title=""
|
||||
|
||||
@@ -91,9 +91,9 @@
|
||||
title={name}
|
||||
prefix={TITLE_PREFIX}
|
||||
on:open={(e) => {
|
||||
if ($app.css != undefined) {
|
||||
if (type && e.detail && $app.css[type] == undefined) {
|
||||
$app.css[type] = Object.fromEntries((ids ?? []).map(({ id }) => [id, {}]))
|
||||
if (app.css != undefined) {
|
||||
if (type && e.detail && app.css[type] == undefined) {
|
||||
app.css[type] = Object.fromEntries((ids ?? []).map(({ id }) => [id, {}]))
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
||||
@@ -16,13 +16,13 @@
|
||||
|
||||
const { app } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let cssEditor: SimpleEditor | undefined = undefined
|
||||
let alertHeight: number | undefined = undefined
|
||||
let themeViewer: any = undefined
|
||||
let selectedTab: 'css' | 'theme' = 'css'
|
||||
let cssEditor: SimpleEditor | undefined = $state(undefined)
|
||||
let alertHeight: number | undefined = $state(undefined)
|
||||
let themeViewer: any = $state(undefined)
|
||||
let selectedTab: 'css' | 'theme' = $state('css')
|
||||
|
||||
function insertSelector(selector: string) {
|
||||
if ($app?.theme?.type === 'path') {
|
||||
if (app?.theme?.type === 'path') {
|
||||
sendUserToast(
|
||||
'You cannot edit the theme because it is a path theme. Fork the theme to edit it.',
|
||||
true
|
||||
@@ -31,8 +31,7 @@
|
||||
}
|
||||
|
||||
const code = cssEditor?.getCode()
|
||||
cssEditor?.setCode(code + '\n' + selector)
|
||||
$app = $app
|
||||
cssEditor?.setCode(code + '\n' + selector) // $app = $app
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -71,25 +70,25 @@
|
||||
</div>
|
||||
{/if}
|
||||
<div style="height: calc(100% - {alertHeight || 0}px);">
|
||||
{#if $app.theme?.type === 'inlined'}
|
||||
{#if app.theme?.type === 'inlined'}
|
||||
<SimpleEditor
|
||||
class="h-full"
|
||||
lang="css"
|
||||
bind:code={$app.theme.css}
|
||||
bind:code={app.theme.css}
|
||||
fixedOverflowWidgets={true}
|
||||
small
|
||||
automaticLayout
|
||||
bind:this={cssEditor}
|
||||
/>
|
||||
{:else}
|
||||
<ThemeCodePreview theme={$app.theme}>
|
||||
<ThemeCodePreview theme={app.theme}>
|
||||
<div class="p-2 w-min">
|
||||
<Button
|
||||
size="xs"
|
||||
color="dark"
|
||||
on:click={async () => {
|
||||
const theme = await resolveTheme($app.theme, $workspaceStore)
|
||||
$app.theme = {
|
||||
const theme = await resolveTheme(app.theme, $workspaceStore)
|
||||
app.theme = {
|
||||
type: 'inlined',
|
||||
css: theme
|
||||
}
|
||||
|
||||
@@ -32,9 +32,9 @@
|
||||
|
||||
function getSubgrids(item: GridItem) {
|
||||
let allSubgrids = {}
|
||||
let subgrids = getAllSubgridsAndComponentIds($app, item.data)[0]
|
||||
let subgrids = getAllSubgridsAndComponentIds(app, item.data)[0]
|
||||
for (let key of subgrids) {
|
||||
allSubgrids[key] = $app.subgrids?.[key]
|
||||
allSubgrids[key] = app.subgrids?.[key]
|
||||
}
|
||||
return allSubgrids
|
||||
}
|
||||
|
||||
@@ -116,17 +116,17 @@
|
||||
function collectStyles() {
|
||||
const styles: string[] = []
|
||||
// Getting global app styles
|
||||
Object.values($app.css || {}).forEach((element) => {
|
||||
Object.values(app.css || {}).forEach((element) => {
|
||||
Object.values(element).filter(({ style }) => style && styles.push(style))
|
||||
})
|
||||
// Getting styles from individual components
|
||||
$app.grid.map((component) => {
|
||||
app.grid.map((component) => {
|
||||
Object.values(component.data.customCss || {}).forEach(({ style }) => {
|
||||
style && styles.push(style)
|
||||
})
|
||||
})
|
||||
// Getting style from subgrids
|
||||
Object.values($app.subgrids || {}).forEach((grid) => {
|
||||
Object.values(app.subgrids || {}).forEach((grid) => {
|
||||
grid.map((component) => {
|
||||
Object.values(component.data.customCss || {}).forEach(({ style }) => {
|
||||
style && styles.push(style)
|
||||
@@ -193,7 +193,7 @@
|
||||
mounted && (!value || value) && untrack(() => parseStyle())
|
||||
})
|
||||
$effect(() => {
|
||||
$app && untrack(() => setTopColors())
|
||||
app && untrack(() => setTopColors())
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -18,15 +18,15 @@
|
||||
|
||||
const { previewTheme, app } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let cssString: string | undefined = $app?.theme?.type === 'inlined' ? $app.theme.css : undefined
|
||||
$: type = $app?.theme?.type
|
||||
let cssString: string | undefined = app?.theme?.type === 'inlined' ? app.theme.css : undefined
|
||||
let type = $derived(app?.theme?.type)
|
||||
|
||||
let themes: Array<{
|
||||
name: string
|
||||
path: string
|
||||
}> = []
|
||||
}> = $state([])
|
||||
|
||||
let loading: boolean = false
|
||||
let loading: boolean = $state(false)
|
||||
|
||||
async function getThemes() {
|
||||
loading = true
|
||||
@@ -56,14 +56,14 @@
|
||||
|
||||
sendUserToast('Theme created:' + message)
|
||||
|
||||
$app.theme = {
|
||||
app.theme = {
|
||||
type: 'path',
|
||||
path: theme.path
|
||||
}
|
||||
}
|
||||
|
||||
let nameField: string = ''
|
||||
let previewThemePath: string | undefined = undefined
|
||||
let nameField: string = $state('')
|
||||
let previewThemePath: string | undefined = $state(undefined)
|
||||
|
||||
onMount(() => {
|
||||
getThemes()
|
||||
|
||||
@@ -16,17 +16,20 @@
|
||||
import ThemeDrawer from './ThemeDrawer.svelte'
|
||||
import Dropdown from '$lib/components/DropdownV2.svelte'
|
||||
|
||||
export let previewThemePath: string | undefined = undefined
|
||||
|
||||
export let row: {
|
||||
name: string
|
||||
path: string
|
||||
interface Props {
|
||||
previewThemePath?: string | undefined
|
||||
row: {
|
||||
name: string
|
||||
path: string
|
||||
}
|
||||
}
|
||||
|
||||
const { previewTheme, app } = getContext<AppViewerContext>('AppViewerContext')
|
||||
let { previewThemePath = $bindable(undefined), row }: Props = $props()
|
||||
|
||||
let cssString: string | undefined = $app?.theme?.type === 'inlined' ? $app.theme.css : undefined
|
||||
$: type = $app?.theme?.type
|
||||
const { previewTheme, app } = $state(getContext<AppViewerContext>('AppViewerContext'))
|
||||
|
||||
let cssString: string | undefined = app?.theme?.type === 'inlined' ? app.theme.css : undefined
|
||||
let type = $derived(app?.theme?.type)
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@@ -41,7 +44,7 @@
|
||||
}
|
||||
})
|
||||
|
||||
$app.theme = {
|
||||
app.theme = {
|
||||
type: 'path',
|
||||
path: row.path
|
||||
}
|
||||
@@ -71,7 +74,7 @@
|
||||
})
|
||||
|
||||
stopPreview()
|
||||
$app.theme = {
|
||||
app.theme = {
|
||||
type: 'path',
|
||||
path: DEFAULT_THEME
|
||||
}
|
||||
@@ -111,7 +114,7 @@
|
||||
|
||||
const resolvedTheme = await resolveTheme(theme, $workspaceStore)
|
||||
|
||||
$app.theme = {
|
||||
app.theme = {
|
||||
type: 'inlined',
|
||||
css: resolvedTheme
|
||||
}
|
||||
@@ -126,7 +129,7 @@
|
||||
|
||||
function apply() {
|
||||
stopPreview()
|
||||
$app.theme = {
|
||||
app.theme = {
|
||||
type: 'path',
|
||||
path: row.path ?? ''
|
||||
}
|
||||
@@ -162,7 +165,7 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
let themeDrawer: ThemeDrawer
|
||||
let themeDrawer: ThemeDrawer | undefined = $state(undefined)
|
||||
</script>
|
||||
|
||||
<tr class={twMerge(previewThemePath === row.path ? 'bg-blue-200' : '', 'transition-all')}>
|
||||
@@ -179,7 +182,7 @@
|
||||
<Badge color="blue" small>Default</Badge>
|
||||
{/if}
|
||||
|
||||
{#if $app?.theme?.type === 'path' && $app.theme.path === row.path}
|
||||
{#if app?.theme?.type === 'path' && app.theme.path === row.path}
|
||||
<Badge color="green" small>Active</Badge>
|
||||
{/if}
|
||||
|
||||
@@ -193,7 +196,7 @@
|
||||
Update
|
||||
</Button>
|
||||
{/if}
|
||||
{#if $app?.theme?.type !== 'path' || $app.theme.path !== row.path}
|
||||
{#if app?.theme?.type !== 'path' || app.theme.path !== row.path}
|
||||
<Button color="light" size="xs" on:click={preview} startIcon={{ icon: Eye }}>
|
||||
Preview
|
||||
</Button>
|
||||
|
||||
@@ -11,18 +11,29 @@
|
||||
// @ts-ignore
|
||||
import MenuItemsOutput from './components/MenuItemsOutput.svelte'
|
||||
|
||||
export let gridItem: GridItem
|
||||
export let first: boolean = false
|
||||
export let nested: boolean = false
|
||||
export let expanded: boolean = false
|
||||
export let renderRec: boolean = true
|
||||
interface Props {
|
||||
gridItem: GridItem
|
||||
first?: boolean
|
||||
nested?: boolean
|
||||
expanded?: boolean
|
||||
renderRec?: boolean
|
||||
}
|
||||
|
||||
let {
|
||||
gridItem,
|
||||
first = false,
|
||||
nested = false,
|
||||
expanded = false,
|
||||
renderRec = true
|
||||
}: Props = $props()
|
||||
const { connectingInput } = getContext<AppViewerContext>('AppViewerContext')
|
||||
const name = getComponentNameById(gridItem.id)
|
||||
|
||||
$: nameOverrides =
|
||||
let nameOverrides = $derived(
|
||||
gridItem?.data?.type === 'decisiontreecomponent'
|
||||
? gridItem.data.nodes.map((n, i) => `${n.label} (Tab index ${i})`)
|
||||
: undefined
|
||||
)
|
||||
|
||||
function getComponentNameById(componentId: string) {
|
||||
if (gridItem?.data?.type) {
|
||||
@@ -36,27 +47,30 @@
|
||||
}
|
||||
}
|
||||
|
||||
$: subGrids = Array.from({ length: gridItem.data?.numberOfSubgrids ?? 0 }).map(
|
||||
(_, i) => `${gridItem.id}-${i}`
|
||||
let subGrids = $derived(
|
||||
Array.from({ length: gridItem.data?.numberOfSubgrids ?? 0 }).map(
|
||||
(_, i) => `${gridItem.id}-${i}`
|
||||
)
|
||||
)
|
||||
</script>
|
||||
|
||||
<OutputHeader
|
||||
render={renderRec}
|
||||
let:render
|
||||
id={gridItem.id}
|
||||
name={getComponentNameById(gridItem.id)}
|
||||
{first}
|
||||
{nested}
|
||||
>
|
||||
<ComponentOutputViewer
|
||||
{render}
|
||||
componentId={gridItem.id}
|
||||
on:select={({ detail }) => {
|
||||
connectOutput(connectingInput, gridItem?.data?.type, gridItem.data.id, detail)
|
||||
}}
|
||||
/>
|
||||
<SubGridOutput {render} {name} {nameOverrides} {expanded} {subGrids} parentId={gridItem.id} />
|
||||
<TableActionsOutput {render} {gridItem} />
|
||||
<MenuItemsOutput {render} {gridItem} />
|
||||
{#snippet children({ render })}
|
||||
<ComponentOutputViewer
|
||||
{render}
|
||||
componentId={gridItem.id}
|
||||
on:select={({ detail }) => {
|
||||
connectOutput(connectingInput, gridItem?.data?.type, gridItem.data.id, detail)
|
||||
}}
|
||||
/>
|
||||
<SubGridOutput {render} {name} {nameOverrides} {expanded} {subGrids} parentId={gridItem.id} />
|
||||
<TableActionsOutput {render} {gridItem} />
|
||||
<MenuItemsOutput {render} {gridItem} />
|
||||
{/snippet}
|
||||
</OutputHeader>
|
||||
|
||||
@@ -19,11 +19,11 @@
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
let hasState: boolean = false
|
||||
let hasState: boolean = $state(false)
|
||||
</script>
|
||||
|
||||
<PanelSection noPadding titlePadding="px-2 pt-2" title="Outputs">
|
||||
<svelte:fragment slot="action">
|
||||
{#snippet action()}
|
||||
<div class="p-0.5">
|
||||
<HideButton
|
||||
on:click={() => {
|
||||
@@ -33,7 +33,7 @@
|
||||
/>
|
||||
<DocLink docLink="https://www.windmill.dev/docs/apps/outputs" />
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
{/snippet}
|
||||
<AnimatedButton
|
||||
animate={$connectingInput.opened}
|
||||
baseRadius="0px"
|
||||
@@ -57,45 +57,41 @@
|
||||
<div>
|
||||
<span class="text-xs font-semibold text-secondary p-2">State & Context</span>
|
||||
|
||||
<OutputHeader
|
||||
let:render
|
||||
selectable={false}
|
||||
id={'ctx'}
|
||||
name={'App Context'}
|
||||
first
|
||||
color="blue"
|
||||
>
|
||||
<ComponentOutputViewer
|
||||
{render}
|
||||
componentId={'ctx'}
|
||||
on:select={({ detail }) => {
|
||||
$connectingInput = connectInput($connectingInput, 'ctx', detail)
|
||||
}}
|
||||
/>
|
||||
<OutputHeader selectable={false} id={'ctx'} name={'App Context'} first color="blue">
|
||||
{#snippet children({ render })}
|
||||
<ComponentOutputViewer
|
||||
{render}
|
||||
componentId={'ctx'}
|
||||
on:select={({ detail }) => {
|
||||
$connectingInput = connectInput($connectingInput, 'ctx', detail)
|
||||
}}
|
||||
/>
|
||||
{/snippet}
|
||||
</OutputHeader>
|
||||
|
||||
<OutputHeader
|
||||
let:render
|
||||
selectable={false}
|
||||
id={'state'}
|
||||
name={'State'}
|
||||
color="blue"
|
||||
disabled={!hasState}
|
||||
>
|
||||
<ComponentOutputViewer
|
||||
{render}
|
||||
bind:hasContent={hasState}
|
||||
componentId={'state'}
|
||||
on:select={({ detail }) => {
|
||||
$connectingInput = connectInput($connectingInput, 'state', detail)
|
||||
}}
|
||||
/>
|
||||
{#snippet children({ render })}
|
||||
<ComponentOutputViewer
|
||||
{render}
|
||||
bind:hasContent={hasState}
|
||||
componentId={'state'}
|
||||
on:select={({ detail }) => {
|
||||
$connectingInput = connectInput($connectingInput, 'state', detail)
|
||||
}}
|
||||
/>
|
||||
{/snippet}
|
||||
</OutputHeader>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<span class="text-xs font-semibold text-secondary p-2">Components</span>
|
||||
{#each $app.grid as gridItem, index (gridItem.id)}
|
||||
{#each app.grid as gridItem, index (gridItem.id)}
|
||||
<ComponentOutput {gridItem} first={index === 0} />
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
@@ -12,34 +12,38 @@
|
||||
|
||||
const { app, initialized } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
$: unintitializedComponents = allItems($app.grid, $app.subgrids)
|
||||
.map((x) => x.id)
|
||||
.filter((x) => !$initialized.initializedComponents?.includes(x))
|
||||
.sort()
|
||||
let unintitializedComponents = $derived(
|
||||
allItems(app.grid, app.subgrids)
|
||||
.map((x) => x.id)
|
||||
.filter((x) => !$initialized.initializedComponents?.includes(x))
|
||||
.sort()
|
||||
)
|
||||
|
||||
$: subgridsErrors = Object.keys($app.subgrids ?? {})
|
||||
.map((x) => {
|
||||
const parentId = x.split('-')[0]
|
||||
const parent = findGridItem($app, parentId)
|
||||
const subgrid = x.replace(`${parentId}-`, '')
|
||||
if (subgrid == '-1') {
|
||||
return {
|
||||
subGridId: x,
|
||||
error: 'Invalid subgrid index -1 '
|
||||
let subgridsErrors = $derived(
|
||||
Object.keys(app.subgrids ?? {})
|
||||
.map((x) => {
|
||||
const parentId = x.split('-')[0]
|
||||
const parent = findGridItem(app, parentId)
|
||||
const subgrid = x.replace(`${parentId}-`, '')
|
||||
if (subgrid == '-1') {
|
||||
return {
|
||||
subGridId: x,
|
||||
error: 'Invalid subgrid index -1 '
|
||||
}
|
||||
} else if (parent === undefined) {
|
||||
return {
|
||||
subGridId: x,
|
||||
error: 'Parent not found'
|
||||
}
|
||||
} else if (parent?.data?.numberOfSubgrids === undefined) {
|
||||
return {
|
||||
subGridId: x,
|
||||
error: 'Parent is not a container'
|
||||
}
|
||||
}
|
||||
} else if (parent === undefined) {
|
||||
return {
|
||||
subGridId: x,
|
||||
error: 'Parent not found'
|
||||
}
|
||||
} else if (parent?.data?.numberOfSubgrids === undefined) {
|
||||
return {
|
||||
subGridId: x,
|
||||
error: 'Parent is not a container'
|
||||
}
|
||||
}
|
||||
})
|
||||
.filter(Boolean)
|
||||
})
|
||||
.filter(Boolean)
|
||||
)
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col gap-8" style="all:none;">
|
||||
@@ -74,49 +78,47 @@
|
||||
>Status</div
|
||||
>
|
||||
<div class="font-semibold bg-gray-100 dark:bg-gray-900 px-2 py-1 text-xs border-b"
|
||||
>Action</div>
|
||||
>Action</div
|
||||
>
|
||||
|
||||
<!-- Iterate over uninitializedComponents to display each component in the grid -->
|
||||
{#each unintitializedComponents as c}
|
||||
{@const item = findGridItem($app, c)}
|
||||
{@const item = findGridItem(app, c)}
|
||||
{#if !item}
|
||||
<div>Item {c} not found</div>
|
||||
{:else}
|
||||
<!-- Component Id -->
|
||||
<div class="text-xs flex items-center px-2 py-2">
|
||||
<Badge>
|
||||
{c}
|
||||
</Badge>
|
||||
</div>
|
||||
<!-- Component Id -->
|
||||
<div class="text-xs flex items-center px-2 py-2">
|
||||
<Badge>
|
||||
{c}
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
<div class="text-xs flex items-center px-2 py-2">
|
||||
<Badge color="blue">
|
||||
{item?.data?.type || 'Unknown'}
|
||||
</Badge>
|
||||
</div>
|
||||
<div class="text-xs flex items-center px-2 py-2">
|
||||
<Badge color="blue">
|
||||
{item?.data?.type || 'Unknown'}
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
<div class="text-xs flex items-center px-2 py-2">
|
||||
<Badge color="red">Uninitialized</Badge>
|
||||
</div>
|
||||
<div class="text-xs flex items-center px-2 py-2">
|
||||
|
||||
<Button
|
||||
color="light"
|
||||
startIcon={{
|
||||
icon: Trash
|
||||
}}
|
||||
size="xs2"
|
||||
on:click={() => {
|
||||
let parent = findGridItemParentGrid($app, c)
|
||||
deleteGridItem($app, item.data, parent)
|
||||
$app = $app
|
||||
}}
|
||||
>
|
||||
Remove
|
||||
</Button>
|
||||
</div>
|
||||
<div class="text-xs flex items-center px-2 py-2">
|
||||
<Badge color="red">Uninitialized</Badge>
|
||||
</div>
|
||||
<div class="text-xs flex items-center px-2 py-2">
|
||||
<Button
|
||||
color="light"
|
||||
startIcon={{
|
||||
icon: Trash
|
||||
}}
|
||||
size="xs2"
|
||||
on:click={() => {
|
||||
let parent = findGridItemParentGrid(app, c)
|
||||
deleteGridItem(app, item.data, parent) // $app = $app
|
||||
}}
|
||||
>
|
||||
Remove
|
||||
</Button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
@@ -164,9 +166,8 @@
|
||||
}}
|
||||
size="xs2"
|
||||
on:click={() => {
|
||||
if ($app.subgrids && s) {
|
||||
delete $app.subgrids[s.subGridId]
|
||||
$app = { ...$app }
|
||||
if (app.subgrids && s) {
|
||||
delete app.subgrids[s.subGridId]
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
import ToggleButton from '$lib/components/common/toggleButton-v2/ToggleButton.svelte'
|
||||
|
||||
const { app } = getContext<AppViewerContext>('AppViewerContext')
|
||||
let code = $state(JSON.stringify($app.lazyInitRequire))
|
||||
let code = $state(JSON.stringify(app.lazyInitRequire))
|
||||
|
||||
let selectedRendering = $state(
|
||||
$app.eagerRendering ? 'eager' : $app.lazyInitRequire ? 'lazy' : 'semi-lazy'
|
||||
app.eagerRendering ? 'eager' : app.lazyInitRequire ? 'lazy' : 'semi-lazy'
|
||||
)
|
||||
</script>
|
||||
|
||||
@@ -32,15 +32,15 @@
|
||||
bind:selected={selectedRendering}
|
||||
on:selected={(e) => {
|
||||
if (e.detail == 'eager') {
|
||||
$app.eagerRendering = true
|
||||
$app.lazyInitRequire = undefined
|
||||
app.eagerRendering = true
|
||||
app.lazyInitRequire = undefined
|
||||
} else if (e.detail == 'semi-lazy') {
|
||||
$app.eagerRendering = undefined
|
||||
$app.lazyInitRequire = undefined
|
||||
app.eagerRendering = undefined
|
||||
app.lazyInitRequire = undefined
|
||||
} else {
|
||||
$app.eagerRendering = undefined
|
||||
$app.lazyInitRequire = []
|
||||
code = JSON.stringify($app.lazyInitRequire)
|
||||
app.eagerRendering = undefined
|
||||
app.lazyInitRequire = []
|
||||
code = JSON.stringify(app.lazyInitRequire)
|
||||
}
|
||||
}}
|
||||
>
|
||||
@@ -65,7 +65,7 @@
|
||||
</Section>
|
||||
{:else if selectedRendering == 'lazy'}
|
||||
<Section label="Component ids to wait the initialization of before the initial refresh">
|
||||
<JsonEditor bind:value={$app.lazyInitRequire} {code} />
|
||||
<JsonEditor bind:value={app.lazyInitRequire} {code} />
|
||||
<span class="text-tertiary text-xs">
|
||||
{'e.g: ["a", "b"]'}, no need to put background runnables ids
|
||||
</span>
|
||||
|
||||
@@ -1,33 +1,31 @@
|
||||
<script lang="ts">
|
||||
import { classNames } from '$lib/utils'
|
||||
import { getContext } from 'svelte'
|
||||
import { getContext, untrack } from 'svelte'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppViewerContext } from '../../types'
|
||||
import { connectInput } from '../appUtils'
|
||||
import ComponentOutput from './ComponentOutput.svelte'
|
||||
|
||||
export let name: string | undefined = undefined
|
||||
export let parentId: string
|
||||
export let expanded: boolean = false
|
||||
export let subGrids: string[]
|
||||
export let nameOverrides: string[] | undefined = undefined
|
||||
export let render: boolean
|
||||
interface Props {
|
||||
name?: string | undefined
|
||||
parentId: string
|
||||
expanded?: boolean
|
||||
subGrids: string[]
|
||||
nameOverrides?: string[] | undefined
|
||||
render: boolean
|
||||
}
|
||||
|
||||
let {
|
||||
name = undefined,
|
||||
parentId,
|
||||
expanded = false,
|
||||
subGrids,
|
||||
nameOverrides = undefined,
|
||||
render
|
||||
}: Props = $props()
|
||||
const { app, connectingInput, worldStore } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let selected = 0
|
||||
|
||||
$: outputs = $worldStore?.outputsById[parentId] as {
|
||||
selectedTabIndex: Output<number>
|
||||
}
|
||||
|
||||
$: subgridItems = subGrids.map((k) => ({
|
||||
k,
|
||||
items: $app.subgrids?.[k] ?? []
|
||||
}))
|
||||
|
||||
$: if (outputs?.selectedTabIndex) {
|
||||
subscribeToOutput()
|
||||
}
|
||||
let selected = $state(0)
|
||||
|
||||
function subscribeToOutput() {
|
||||
outputs.selectedTabIndex.subscribe(
|
||||
@@ -40,19 +38,37 @@
|
||||
selected
|
||||
)
|
||||
}
|
||||
let outputs = $derived(
|
||||
$worldStore?.outputsById[parentId] as {
|
||||
selectedTabIndex: Output<number>
|
||||
}
|
||||
)
|
||||
let subgridItems = $derived(
|
||||
subGrids.map((k) => ({
|
||||
k,
|
||||
items: app.subgrids?.[k] ?? []
|
||||
}))
|
||||
)
|
||||
$effect(() => {
|
||||
if (outputs?.selectedTabIndex) {
|
||||
untrack(() => {
|
||||
subscribeToOutput()
|
||||
})
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
{#each subgridItems as { k, items }, index (k)}
|
||||
<div class="ml-2 my-2">
|
||||
{#if subGrids.length > 1 && render}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<div
|
||||
class={classNames(
|
||||
'px-1 py-0.5 flex justify-between items-center font-semibold text-xs border-l border-y w-full cursor-pointer',
|
||||
selected === index ? 'bg-surface-selected' : 'bg-surface'
|
||||
)}
|
||||
on:click={() => {
|
||||
onclick={() => {
|
||||
selected = index
|
||||
}}
|
||||
>
|
||||
@@ -78,9 +94,6 @@
|
||||
gridItem={subGridItem}
|
||||
first={index === 0}
|
||||
{expanded}
|
||||
on:select={({ detail }) => {
|
||||
$connectingInput = connectInput($connectingInput, subGridItem.id, detail)
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
{:else}
|
||||
|
||||
+17
-11
@@ -7,17 +7,23 @@
|
||||
|
||||
const { connectingInput } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
export let id: string
|
||||
export let name: string
|
||||
export let first: boolean = false
|
||||
interface Props {
|
||||
id: string
|
||||
name: string
|
||||
first?: boolean
|
||||
}
|
||||
|
||||
let { id, name, first = false }: Props = $props()
|
||||
</script>
|
||||
|
||||
<OutputHeader let:render renamable={false} selectable={true} {id} {name} color="blue" {first}>
|
||||
<ComponentOutputViewer
|
||||
{render}
|
||||
componentId={id}
|
||||
on:select={({ detail }) => {
|
||||
$connectingInput = connectInput($connectingInput, id, detail)
|
||||
}}
|
||||
/>
|
||||
<OutputHeader renamable={false} selectable={true} {id} {name} color="blue" {first}>
|
||||
{#snippet children({ render })}
|
||||
<ComponentOutputViewer
|
||||
{render}
|
||||
componentId={id}
|
||||
on:select={({ detail }) => {
|
||||
$connectingInput = connectInput($connectingInput, id, detail)
|
||||
}}
|
||||
/>
|
||||
{/snippet}
|
||||
</OutputHeader>
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@
|
||||
const { app } = getContext<AppViewerContext>('AppViewerContext')
|
||||
</script>
|
||||
|
||||
{#each $app.hiddenInlineScripts ?? [] as action, index}
|
||||
{#each app.hiddenInlineScripts ?? [] as action, index}
|
||||
{#if !action.hidden}
|
||||
<BackgroundScriptOutput id={BG_PREFIX + index} name={action.name} first={index === 0} />
|
||||
{/if}
|
||||
|
||||
@@ -8,11 +8,15 @@
|
||||
|
||||
const { app, selectedComponent } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
export let id: string
|
||||
interface Props {
|
||||
id: string
|
||||
}
|
||||
|
||||
let { id }: Props = $props()
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
$: reservedIds = allItems($app.grid, $app.subgrids).map((item) => item.id)
|
||||
let reservedIds = $derived(allItems(app.grid, app.subgrids).map((item) => item.id))
|
||||
</script>
|
||||
|
||||
<Popover
|
||||
@@ -20,9 +24,9 @@
|
||||
closeOnOtherPopoverOpen
|
||||
contentClasses="p-4"
|
||||
>
|
||||
<svelte:fragment slot="trigger">
|
||||
{#snippet trigger()}
|
||||
<button
|
||||
on:click={() => {
|
||||
onclick={() => {
|
||||
$selectedComponent = [id]
|
||||
}}
|
||||
title="Edit ID"
|
||||
@@ -31,8 +35,8 @@
|
||||
>
|
||||
<Pencil size={14} />
|
||||
</button>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="content" let:close>
|
||||
{/snippet}
|
||||
{#snippet content({ close })}
|
||||
<IdEditorInput
|
||||
initialId={id}
|
||||
on:close={() => close()}
|
||||
@@ -42,5 +46,5 @@
|
||||
}}
|
||||
{reservedIds}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
{/snippet}
|
||||
</Popover>
|
||||
|
||||
@@ -7,18 +7,24 @@
|
||||
|
||||
const { connectingInput } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
export let id: string
|
||||
export let first: boolean = false
|
||||
export let label: string
|
||||
export let renderRec: boolean
|
||||
interface Props {
|
||||
id: string
|
||||
first?: boolean
|
||||
label: string
|
||||
renderRec: boolean
|
||||
}
|
||||
|
||||
let { id, first = false, label, renderRec }: Props = $props()
|
||||
</script>
|
||||
|
||||
<OutputHeader render={renderRec} let:render renamable={false} {id} name={label} {first}>
|
||||
<ComponentOutputViewer
|
||||
{render}
|
||||
componentId={id}
|
||||
on:select={({ detail }) => {
|
||||
$connectingInput = connectInput($connectingInput, id, detail)
|
||||
}}
|
||||
/>
|
||||
<OutputHeader render={renderRec} renamable={false} {id} name={label} {first}>
|
||||
{#snippet children({ render })}
|
||||
<ComponentOutputViewer
|
||||
{render}
|
||||
componentId={id}
|
||||
on:select={({ detail }) => {
|
||||
$connectingInput = connectInput($connectingInput, id, detail)
|
||||
}}
|
||||
/>
|
||||
{/snippet}
|
||||
</OutputHeader>
|
||||
|
||||
+56
-36
@@ -1,4 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { stopPropagation } from 'svelte/legacy'
|
||||
|
||||
import type { AppViewerContext, ContextPanelContext } from '$lib/components/apps/types'
|
||||
import { allItems } from '$lib/components/apps/utils'
|
||||
import { classNames } from '$lib/utils'
|
||||
@@ -10,28 +12,46 @@
|
||||
import type { Runnable } from '$lib/components/apps/inputType'
|
||||
import DocLink from '../../settingsPanel/DocLink.svelte'
|
||||
|
||||
export let id: string
|
||||
export let name: string
|
||||
export let first: boolean = false
|
||||
export let nested: boolean = false
|
||||
export let color: 'blue' | 'indigo' = 'indigo'
|
||||
export let selectable: boolean = true
|
||||
export let renamable: boolean = true
|
||||
export let disabled: boolean = false
|
||||
export let render: boolean = true
|
||||
interface Props {
|
||||
id: string
|
||||
name: string
|
||||
first?: boolean
|
||||
nested?: boolean
|
||||
color?: 'blue' | 'indigo'
|
||||
selectable?: boolean
|
||||
renamable?: boolean
|
||||
disabled?: boolean
|
||||
render?: boolean
|
||||
children?: import('svelte').Snippet<[any]>
|
||||
}
|
||||
|
||||
let {
|
||||
id,
|
||||
name,
|
||||
first = false,
|
||||
nested = false,
|
||||
color = 'indigo',
|
||||
selectable = true,
|
||||
renamable = true,
|
||||
disabled = false,
|
||||
render = true,
|
||||
children
|
||||
}: Props = $props()
|
||||
|
||||
const { manuallyOpened, search, hasResult } = getContext<ContextPanelContext>('ContextPanel')
|
||||
|
||||
const { selectedComponent, app, hoverStore, allIdsInPath, connectingInput, worldStore } =
|
||||
getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
$: subids = $search != '' ? allsubIds($app, id) : []
|
||||
$: inSearch =
|
||||
let subids = $derived($search != '' ? allsubIds(app, id) : [])
|
||||
let inSearch = $derived(
|
||||
$search != '' &&
|
||||
($hasResult[id] ||
|
||||
Object.entries($hasResult).some(([key, value]) => value && subids.includes(key)))
|
||||
$: open =
|
||||
($hasResult[id] ||
|
||||
Object.entries($hasResult).some(([key, value]) => value && subids.includes(key)))
|
||||
)
|
||||
let open = $derived(
|
||||
$allIdsInPath.includes(id) || id == $selectedComponent?.[0] || $manuallyOpened[id] || inSearch
|
||||
)
|
||||
|
||||
const hoverColor = {
|
||||
blue: 'hover:bg-blue-100 hover:text-blue-500 dark:hover:bg-frost-900 dark:hover:text-frost-100',
|
||||
@@ -55,7 +75,7 @@
|
||||
}
|
||||
|
||||
function renameId(newId: string): void {
|
||||
const item = findGridItem($app, id)
|
||||
const item = findGridItem(app, id)
|
||||
|
||||
if (!item) {
|
||||
return
|
||||
@@ -63,23 +83,23 @@
|
||||
item.data.id = newId
|
||||
item.id = newId
|
||||
|
||||
const oldSubgrids = Object.keys($app.subgrids ?? {}).filter((subgrid) =>
|
||||
const oldSubgrids = Object.keys(app.subgrids ?? {}).filter((subgrid) =>
|
||||
subgrid.startsWith(id + '-')
|
||||
)
|
||||
|
||||
oldSubgrids.forEach((subgrid) => {
|
||||
if ($app.subgrids) {
|
||||
$app.subgrids[subgrid.replace(id, newId)] = $app.subgrids[subgrid]
|
||||
delete $app.subgrids[subgrid]
|
||||
if (app.subgrids) {
|
||||
app.subgrids[subgrid.replace(id, newId)] = app.subgrids[subgrid]
|
||||
delete app.subgrids[subgrid]
|
||||
}
|
||||
})
|
||||
|
||||
function propagateRename(from: string, to: string) {
|
||||
allItems($app.grid, $app.subgrids).forEach((item) => {
|
||||
allItems(app.grid, app.subgrids).forEach((item) => {
|
||||
renameComponent(from, to, item.data)
|
||||
})
|
||||
|
||||
$app.hiddenInlineScripts?.forEach((x) => {
|
||||
app.hiddenInlineScripts?.forEach((x) => {
|
||||
processRunnable(from, to, x)
|
||||
})
|
||||
}
|
||||
@@ -114,7 +134,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
$app = $app
|
||||
// $app = $app
|
||||
$selectedComponent = [newId]
|
||||
|
||||
delete $worldStore.outputsById[id]
|
||||
@@ -199,35 +219,35 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
||||
<div class={render && ($search == '' || inSearch) ? '' : 'invisible h-0 overflow-hidden'}>
|
||||
{#if render && ($search == '' || inSearch)}
|
||||
<!-- svelte-ignore a11y-mouse-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<!-- svelte-ignore a11y_mouse_events_have_key_events -->
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<div
|
||||
on:mouseenter|stopPropagation={() => {
|
||||
onmouseenter={stopPropagation(() => {
|
||||
if (id !== $hoverStore) {
|
||||
$hoverStore = id
|
||||
}
|
||||
}}
|
||||
on:mouseleave|stopPropagation={() => {
|
||||
})}
|
||||
onmouseleave={stopPropagation(() => {
|
||||
if ($hoverStore !== undefined) {
|
||||
$hoverStore = undefined
|
||||
}
|
||||
}}
|
||||
})}
|
||||
class={classNames(
|
||||
'flex items-center justify-between p-1 cursor-pointer gap-1 truncate',
|
||||
hoverColor[color],
|
||||
$selectedComponent?.includes(id)
|
||||
? openBackground[color]
|
||||
: $connectingInput.hoveredComponent === id
|
||||
? 'bg-[#fab157]'
|
||||
: 'bg-surface-secondary',
|
||||
? 'bg-[#fab157]'
|
||||
: 'bg-surface-secondary',
|
||||
first ? 'border-t' : '',
|
||||
nested ? 'border-l' : '',
|
||||
'transition-all'
|
||||
)}
|
||||
on:click={() => {
|
||||
onclick={() => {
|
||||
if (!disabled) {
|
||||
$manuallyOpened[id] = $manuallyOpened[id] != undefined ? !$manuallyOpened[id] : true
|
||||
}
|
||||
@@ -238,7 +258,7 @@
|
||||
<button
|
||||
disabled={!(selectable && !$selectedComponent?.includes(id)) || $connectingInput?.opened}
|
||||
title="Select component"
|
||||
on:click|stopPropagation={() => ($selectedComponent = [id])}
|
||||
onclick={stopPropagation(() => ($selectedComponent = [id]))}
|
||||
class="flex items-center ml-0.5 rounded-sm bg-surface-selected hover:text-primary text-tertiary"
|
||||
>
|
||||
<div
|
||||
@@ -271,8 +291,8 @@
|
||||
docLink={id === 'state'
|
||||
? 'https://www.windmill.dev/docs/apps/outputs#state'
|
||||
: id === 'ctx'
|
||||
? 'https://www.windmill.dev/docs/apps/outputs#app-context'
|
||||
: ''}
|
||||
? 'https://www.windmill.dev/docs/apps/outputs#app-context'
|
||||
: ''}
|
||||
size="xs2"
|
||||
/>
|
||||
{/if}
|
||||
@@ -299,7 +319,7 @@
|
||||
: ''}"
|
||||
>
|
||||
<div class={classNames(nested ? 'border-l ml-2' : '', open ? 'border-t' : '')}>
|
||||
<slot render={open && render} />
|
||||
{@render children?.({ render: open && render })}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+25
-14
@@ -20,14 +20,23 @@
|
||||
import type { Preview } from '$lib/gen'
|
||||
import type { InlineScript } from '../../types'
|
||||
|
||||
export let componentType: string | undefined = undefined
|
||||
export let showScriptPicker = false
|
||||
export let rawApps = false
|
||||
export let unusedInlineScripts: { name: string; inlineScript: InlineScript }[]
|
||||
interface Props {
|
||||
componentType?: string | undefined
|
||||
showScriptPicker?: boolean
|
||||
rawApps?: boolean
|
||||
unusedInlineScripts: { name: string; inlineScript: InlineScript }[]
|
||||
}
|
||||
|
||||
let tab = 'workspacescripts'
|
||||
let filter: string = ''
|
||||
let picker: Drawer
|
||||
let {
|
||||
componentType = undefined,
|
||||
showScriptPicker = false,
|
||||
rawApps = false,
|
||||
unusedInlineScripts
|
||||
}: Props = $props()
|
||||
|
||||
let tab = $state('workspacescripts')
|
||||
let filter: string = $state('')
|
||||
let picker: Drawer | undefined = $state()
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@@ -78,13 +87,15 @@
|
||||
newInlineScript(script.content, script.language)
|
||||
}
|
||||
|
||||
$: langs = processLangs(undefined, $defaultScripts?.order ?? Object.keys(defaultScriptLanguages))
|
||||
.map((l) => [defaultScriptLanguages[l], l])
|
||||
.filter(
|
||||
(x) =>
|
||||
x[1] != 'docker' &&
|
||||
($defaultScripts?.hidden == undefined || !$defaultScripts.hidden.includes(x[1]))
|
||||
) as [string, Preview['language']][]
|
||||
let langs = $derived(
|
||||
processLangs(undefined, $defaultScripts?.order ?? Object.keys(defaultScriptLanguages))
|
||||
.map((l) => [defaultScriptLanguages[l], l])
|
||||
.filter(
|
||||
(x) =>
|
||||
x[1] != 'docker' &&
|
||||
($defaultScripts?.hidden == undefined || !$defaultScripts.hidden.includes(x[1]))
|
||||
) as [string, Preview['language']][]
|
||||
)
|
||||
</script>
|
||||
|
||||
<Drawer bind:this={picker} size="1000px">
|
||||
|
||||
+5
-6
@@ -30,8 +30,7 @@
|
||||
stateId,
|
||||
worldStore,
|
||||
state: stateStore,
|
||||
appPath,
|
||||
app
|
||||
appPath
|
||||
} = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
interface Props {
|
||||
@@ -196,7 +195,7 @@
|
||||
$stateId++
|
||||
}
|
||||
}
|
||||
$app = $app
|
||||
// $app = $app
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,7 +270,7 @@
|
||||
placeholder="Inline script name"
|
||||
class="!text-xs !rounded-sm !shadow-none"
|
||||
onkeyup={() => {
|
||||
$app = $app
|
||||
// $app = $app
|
||||
if (stateId) {
|
||||
$stateId++
|
||||
}
|
||||
@@ -385,7 +384,7 @@
|
||||
loadSchemaAndInputsByName()
|
||||
}
|
||||
}
|
||||
$app = $app
|
||||
// $app = $app
|
||||
}}
|
||||
args={Object.entries(fields).reduce((acc, [key, obj]) => {
|
||||
acc[key] = obj.type === 'static' ? obj.value : undefined
|
||||
@@ -413,7 +412,7 @@
|
||||
}}
|
||||
on:change={async (e) => {
|
||||
inferSuggestions(e.detail.code)
|
||||
$app = $app
|
||||
// $app = $app
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
+3
-5
@@ -18,8 +18,7 @@
|
||||
|
||||
function clear() {
|
||||
if (componentInput && componentInput.type == 'runnable') {
|
||||
componentInput = clearResultAppInput(componentInput)
|
||||
$app = $app
|
||||
componentInput = clearResultAppInput(componentInput) // $app = $app
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,7 +75,7 @@
|
||||
/>
|
||||
{:else}
|
||||
<EmptyInlineScript
|
||||
unusedInlineScripts={$app?.unusedInlineScripts}
|
||||
unusedInlineScripts={app?.unusedInlineScripts}
|
||||
{componentType}
|
||||
on:delete={clear}
|
||||
on:new={(e) => {
|
||||
@@ -87,8 +86,7 @@
|
||||
) {
|
||||
componentInput.runnable.inlineScript = e.detail
|
||||
componentInput.autoRefresh = true
|
||||
componentInput.recomputeOnInputChanged = true
|
||||
$app = $app
|
||||
componentInput.recomputeOnInputChanged = true // $app = $app
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
+8
-4
@@ -6,9 +6,13 @@
|
||||
import type { Runnable, StaticAppInput } from '../../inputType'
|
||||
import { createEventDispatcher, getContext } from 'svelte'
|
||||
|
||||
export let runnable: HiddenRunnable
|
||||
export let id: string
|
||||
export let transformer: boolean
|
||||
interface Props {
|
||||
runnable: HiddenRunnable
|
||||
id: string
|
||||
transformer: boolean
|
||||
}
|
||||
|
||||
let { runnable = $bindable(), id, transformer }: Props = $props()
|
||||
|
||||
const { runnableComponents, app } = getContext<AppViewerContext>('AppViewerContext')
|
||||
async function fork(nrunnable: Runnable) {
|
||||
@@ -69,7 +73,7 @@
|
||||
/>
|
||||
{:else}
|
||||
<EmptyInlineScript
|
||||
unusedInlineScripts={$app?.unusedInlineScripts}
|
||||
unusedInlineScripts={app?.unusedInlineScripts}
|
||||
on:pick={(e) => onPick(e.detail)}
|
||||
on:delete
|
||||
showScriptPicker
|
||||
|
||||
+36
-28
@@ -18,11 +18,11 @@
|
||||
|
||||
function deleteBackgroundScript(index: number) {
|
||||
// remove the script from the array at the index
|
||||
if ($app.hiddenInlineScripts.length - 1 == index) {
|
||||
$app.hiddenInlineScripts.splice(index, 1)
|
||||
$app.hiddenInlineScripts = [...$app.hiddenInlineScripts]
|
||||
if (app.hiddenInlineScripts.length - 1 == index) {
|
||||
app.hiddenInlineScripts.splice(index, 1)
|
||||
app.hiddenInlineScripts = [...app.hiddenInlineScripts]
|
||||
} else {
|
||||
$app.hiddenInlineScripts[index] = {
|
||||
app.hiddenInlineScripts[index] = {
|
||||
hidden: true,
|
||||
inlineScript: undefined,
|
||||
name: `Background Runnable ${index}`,
|
||||
@@ -30,7 +30,7 @@
|
||||
type: 'runnableByName',
|
||||
recomputeIds: undefined
|
||||
}
|
||||
$app.hiddenInlineScripts = $app.hiddenInlineScripts
|
||||
app.hiddenInlineScripts = app.hiddenInlineScripts
|
||||
}
|
||||
|
||||
$selectedComponentInEditor = undefined
|
||||
@@ -40,24 +40,33 @@
|
||||
}
|
||||
}
|
||||
|
||||
$: gridItem =
|
||||
let gridItem = $derived(
|
||||
$selectedComponentInEditor && !$selectedComponentInEditor.startsWith(BG_PREFIX)
|
||||
? findGridItem($app, $selectedComponentInEditor?.split('_')?.[0])
|
||||
? findGridItem(app, $selectedComponentInEditor?.split('_')?.[0])
|
||||
: undefined
|
||||
|
||||
$: hiddenInlineScript = $app?.hiddenInlineScripts?.findIndex((k_, index) => {
|
||||
const [prefix, id] = $selectedComponentInEditor?.split('_') || []
|
||||
|
||||
if (prefix !== 'bg') return false
|
||||
|
||||
return Number(id) === index
|
||||
})
|
||||
|
||||
$: unusedInlineScript = $app?.unusedInlineScripts?.findIndex(
|
||||
(k_, index) => `unused-${index}` === $selectedComponentInEditor
|
||||
)
|
||||
|
||||
export let width: number | undefined = undefined
|
||||
let hiddenInlineScript = $derived(
|
||||
app?.hiddenInlineScripts?.findIndex((k_, index) => {
|
||||
const [prefix, id] = $selectedComponentInEditor?.split('_') || []
|
||||
|
||||
if (prefix !== 'bg') return false
|
||||
|
||||
return Number(id) === index
|
||||
})
|
||||
)
|
||||
|
||||
let unusedInlineScript = $derived(
|
||||
app?.unusedInlineScripts?.findIndex(
|
||||
(k_, index) => `unused-${index}` === $selectedComponentInEditor
|
||||
)
|
||||
)
|
||||
|
||||
interface Props {
|
||||
width?: number | undefined
|
||||
}
|
||||
|
||||
let { width = undefined }: Props = $props()
|
||||
</script>
|
||||
|
||||
<Splitpanes
|
||||
@@ -86,24 +95,24 @@
|
||||
bind:gridItem
|
||||
/>
|
||||
{/key}
|
||||
{:else if unusedInlineScript > -1 && $app.unusedInlineScripts?.[unusedInlineScript]}
|
||||
{:else if unusedInlineScript > -1 && app.unusedInlineScripts?.[unusedInlineScript]}
|
||||
{#key unusedInlineScript}
|
||||
<InlineScriptEditor
|
||||
on:createScriptFromInlineScript={() =>
|
||||
sendUserToast('Cannot save to workspace unused scripts', true)}
|
||||
id={`unused-${unusedInlineScript}`}
|
||||
bind:name={$app.unusedInlineScripts[unusedInlineScript].name}
|
||||
bind:inlineScript={$app.unusedInlineScripts[unusedInlineScript].inlineScript}
|
||||
bind:name={app.unusedInlineScripts[unusedInlineScript].name}
|
||||
bind:inlineScript={app.unusedInlineScripts[unusedInlineScript].inlineScript}
|
||||
on:delete={() => {
|
||||
// remove the script from the array at the index
|
||||
$app.unusedInlineScripts.splice(unusedInlineScript, 1)
|
||||
$app.unusedInlineScripts = [...$app.unusedInlineScripts]
|
||||
app.unusedInlineScripts.splice(unusedInlineScript, 1)
|
||||
app.unusedInlineScripts = [...app.unusedInlineScripts]
|
||||
}}
|
||||
/>
|
||||
{/key}
|
||||
{:else if hiddenInlineScript > -1}
|
||||
{#key hiddenInlineScript}
|
||||
{#if $app.hiddenInlineScripts?.[hiddenInlineScript]}
|
||||
{#if app.hiddenInlineScripts?.[hiddenInlineScript]}
|
||||
<InlineScriptHiddenRunnable
|
||||
on:createScriptFromInlineScript={(e) => {
|
||||
createScriptFromInlineScript(
|
||||
@@ -111,13 +120,12 @@
|
||||
e.detail,
|
||||
$workspaceStore ?? '',
|
||||
$appPath
|
||||
)
|
||||
$app = $app
|
||||
) // app = app
|
||||
}}
|
||||
transformer={$selectedComponentInEditor?.endsWith('_transformer')}
|
||||
on:delete={() => deleteBackgroundScript(hiddenInlineScript)}
|
||||
id={BG_PREFIX + hiddenInlineScript}
|
||||
bind:runnable={$app.hiddenInlineScripts[hiddenInlineScript]}
|
||||
bind:runnable={app.hiddenInlineScripts[hiddenInlineScript]}
|
||||
/>{/if}{/key}
|
||||
{:else}
|
||||
<div class="text-sm text-tertiary text-center py-8 px-2">
|
||||
|
||||
+30
-30
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { Badge, Button } from '$lib/components/common'
|
||||
import { Plus } from 'lucide-svelte'
|
||||
import { createEventDispatcher, getContext } from 'svelte'
|
||||
import { createEventDispatcher, getContext, untrack } from 'svelte'
|
||||
import Tooltip from '../../../Tooltip.svelte'
|
||||
import type { AppEditorContext, AppViewerContext } from '../../types'
|
||||
import { BG_PREFIX, getAllScriptNames } from '../../utils'
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
const PREFIX = 'script-selector-' as const
|
||||
|
||||
const { app, selectedComponent } = getContext<AppViewerContext>('AppViewerContext')
|
||||
const { app, selectedComponent } = $state(getContext<AppViewerContext>('AppViewerContext'))
|
||||
const { selectedComponentInEditor } = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
function selectScript(id: string) {
|
||||
@@ -26,11 +26,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
$: runnables = getAppScripts($app.grid, $app.subgrids)
|
||||
|
||||
// When selected component changes, update selectedScriptComponentId
|
||||
$: selectedComponent && handleSelectedComponent($selectedComponent)
|
||||
|
||||
function handleSelectedComponent(selectedComponent: string[] | undefined) {
|
||||
if (
|
||||
selectedComponent != $selectedComponentInEditor &&
|
||||
@@ -45,10 +40,10 @@
|
||||
appTutorials?.runTutorialById('backgroundrunnables', { skipStepsCount: 2 })
|
||||
}
|
||||
|
||||
for (const [index, script] of $app.hiddenInlineScripts.entries()) {
|
||||
for (const [index, script] of app.hiddenInlineScripts.entries()) {
|
||||
if (script.hidden) {
|
||||
delete script.hidden
|
||||
$app.hiddenInlineScripts = $app.hiddenInlineScripts
|
||||
app.hiddenInlineScripts = app.hiddenInlineScripts
|
||||
selectScript(BG_PREFIX + index)
|
||||
return
|
||||
}
|
||||
@@ -56,18 +51,18 @@
|
||||
let index = 0
|
||||
let newScriptPath = `Background Runnable ${index}`
|
||||
|
||||
const names = getAllScriptNames($app)
|
||||
const names = getAllScriptNames(app)
|
||||
|
||||
// Find a name that is not used by any other inline script
|
||||
while (names.includes(newScriptPath)) {
|
||||
newScriptPath = `Background Runnable ${++index}`
|
||||
}
|
||||
|
||||
if (!$app.hiddenInlineScripts) {
|
||||
$app.hiddenInlineScripts = []
|
||||
if (!app.hiddenInlineScripts) {
|
||||
app.hiddenInlineScripts = []
|
||||
}
|
||||
|
||||
$app.hiddenInlineScripts.push({
|
||||
app.hiddenInlineScripts.push({
|
||||
name: newScriptPath,
|
||||
inlineScript: undefined,
|
||||
autoRefresh: true,
|
||||
@@ -75,16 +70,21 @@
|
||||
fields: {},
|
||||
recomputeIds: undefined
|
||||
})
|
||||
$app.hiddenInlineScripts = $app.hiddenInlineScripts
|
||||
selectScript(`${BG_PREFIX}${$app.hiddenInlineScripts.length - 1}`)
|
||||
app.hiddenInlineScripts = app.hiddenInlineScripts
|
||||
selectScript(`${BG_PREFIX}${app.hiddenInlineScripts.length - 1}`)
|
||||
}
|
||||
|
||||
let appTutorials: AppTutorials | undefined = undefined
|
||||
let appTutorials: AppTutorials | undefined = $state(undefined)
|
||||
const dispatch = createEventDispatcher()
|
||||
let runnables = $derived(getAppScripts(app.grid, app.subgrids))
|
||||
// When selected component changes, update selectedScriptComponentId
|
||||
$effect(() => {
|
||||
selectedComponent && untrack(() => handleSelectedComponent($selectedComponent))
|
||||
})
|
||||
</script>
|
||||
|
||||
<PanelSection title="Runnables" id="app-editor-runnable-panel">
|
||||
<svelte:fragment slot="action">
|
||||
{#snippet action()}
|
||||
<div class="flex flex-row gap-1">
|
||||
<HideButton
|
||||
direction="bottom"
|
||||
@@ -96,7 +96,7 @@
|
||||
docLink="https://www.windmill.dev/docs/apps/app-runnable-panel#creating-a-runnable"
|
||||
/>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
{/snippet}
|
||||
<div class="w-full flex flex-col gap-6 py-1">
|
||||
<div>
|
||||
<div class="flex flex-col gap-2 w-full">
|
||||
@@ -109,7 +109,7 @@
|
||||
{$selectedComponentInEditor === id
|
||||
? 'border-blue-500 bg-blue-100 dark:bg-frost-900/50'
|
||||
: 'hover:bg-blue-50 dark:hover:bg-frost-900/50'}"
|
||||
on:click={() => selectScript(id)}
|
||||
onclick={() => selectScript(id)}
|
||||
>
|
||||
<span class="text-2xs truncate">{name}</span>
|
||||
<div>
|
||||
@@ -124,7 +124,7 @@
|
||||
{$selectedComponentInEditor === id + '_transformer'
|
||||
? 'border-blue-500 bg-blue-100 dark:bg-frost-900/50'
|
||||
: 'hover:bg-blue-50 dark:hover:bg-frost-900/50'}"
|
||||
on:click={() => selectScript(id + '_transformer')}
|
||||
onclick={() => selectScript(id + '_transformer')}
|
||||
>
|
||||
<span class="text-2xs truncate">Transformer</span>
|
||||
</button>
|
||||
@@ -140,7 +140,7 @@
|
||||
{$selectedComponentInEditor === id
|
||||
? 'border-blue-500 bg-blue-100 dark:bg-frost-900/50'
|
||||
: 'hover:bg-blue-50'}"
|
||||
on:click={() => selectScript(id)}
|
||||
onclick={() => selectScript(id)}
|
||||
>
|
||||
<span class="text-2xs truncate">{name}</span>
|
||||
<Badge color="indigo">{id}</Badge>
|
||||
@@ -153,7 +153,7 @@
|
||||
{$selectedComponentInEditor === id + '_transformer'
|
||||
? 'border-blue-500 bg-blue-100 dark:bg-frost-900/50'
|
||||
: 'hover:bg-blue-50 dark:hover:bg-frost-900/50'}"
|
||||
on:click={() => selectScript(id + '_transformer')}
|
||||
onclick={() => selectScript(id + '_transformer')}
|
||||
>
|
||||
<span class="text-2xs truncate">Transformer</span>
|
||||
</button>
|
||||
@@ -161,9 +161,9 @@
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
{#if $app.unusedInlineScripts?.length > 0}
|
||||
{#if app.unusedInlineScripts?.length > 0}
|
||||
<div class="flex gap-1 flex-col">
|
||||
{#each $app.unusedInlineScripts as unusedInlineScript, index (index)}
|
||||
{#each app.unusedInlineScripts as unusedInlineScript, index (index)}
|
||||
{@const id = `unused-${index}`}
|
||||
<button
|
||||
id={PREFIX + id}
|
||||
@@ -171,7 +171,7 @@
|
||||
{$selectedComponentInEditor === id
|
||||
? 'border-blue-500 bg-blue-100 dark:bg-frost-900/50'
|
||||
: 'hover:bg-blue-50 dark:hover:bg-frost-900/50'}"
|
||||
on:click={() => selectScript(id)}
|
||||
onclick={() => selectScript(id)}
|
||||
>
|
||||
<span class="text-2xs truncate">{unusedInlineScript.name}</span>
|
||||
<Badge color="red">Detached</Badge>
|
||||
@@ -179,7 +179,7 @@
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{#if runnables.inline.length == 0 && $app.unusedInlineScripts?.length == 0 && runnables.imported.length == 0}
|
||||
{#if runnables.inline.length == 0 && app.unusedInlineScripts?.length == 0 && runnables.imported.length == 0}
|
||||
<div class="text-xs text-tertiary">No scripts/flows</div>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -210,8 +210,8 @@
|
||||
</Button>
|
||||
</div>
|
||||
<div class="flex flex-col gap-1 w-full">
|
||||
{#if $app.hiddenInlineScripts?.length > 0}
|
||||
{#each $app.hiddenInlineScripts as { name, hidden, transformer }, index (index)}
|
||||
{#if app.hiddenInlineScripts?.length > 0}
|
||||
{#each app.hiddenInlineScripts as { name, hidden, transformer }, index (index)}
|
||||
{#if !hidden}
|
||||
{@const id = BG_PREFIX + index}
|
||||
<button
|
||||
@@ -220,7 +220,7 @@
|
||||
{$selectedComponentInEditor === id
|
||||
? 'border-blue-500 bg-blue-100 dark:bg-frost-900/50'
|
||||
: 'hover:bg-blue-50 dark:hover:bg-frost-900/50'}"
|
||||
on:click={() => selectScript(id)}
|
||||
onclick={() => selectScript(id)}
|
||||
>
|
||||
<span class="text-2xs truncate">{name}</span>
|
||||
<Badge color="indigo">{id}</Badge>
|
||||
@@ -233,7 +233,7 @@
|
||||
{$selectedComponentInEditor === id + '_transformer'
|
||||
? 'border-blue-500 bg-blue-100 dark:bg-frost-900/50'
|
||||
: 'hover:bg-blue-50 dark:hover:bg-frost-900/50'}"
|
||||
on:click={() => selectScript(id + '_transformer')}
|
||||
onclick={() => selectScript(id + '_transformer')}
|
||||
>
|
||||
<span class="text-2xs truncate">Transformer</span>
|
||||
</button>
|
||||
|
||||
@@ -7,14 +7,10 @@
|
||||
AlignStartHorizontal,
|
||||
AlignStartVertical
|
||||
} from 'lucide-svelte'
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppViewerContext } from '../../types'
|
||||
import type { AppComponent } from '../component'
|
||||
import ToggleButtonGroup from '$lib/components/common/toggleButton-v2/ToggleButtonGroup.svelte'
|
||||
import ToggleButton from '$lib/components/common/toggleButton-v2/ToggleButton.svelte'
|
||||
|
||||
const { app } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
interface Props {
|
||||
component: AppComponent
|
||||
}
|
||||
@@ -26,11 +22,7 @@
|
||||
<div class="flex flex-wrap gap-x-4 gap-y-1 w-full justify-end items-center">
|
||||
<div class="text-tertiary text-xs">Alignment</div>
|
||||
{#if component.horizontalAlignment}
|
||||
<ToggleButtonGroup
|
||||
noWFull
|
||||
on:selected={() => ($app = $app)}
|
||||
bind:selected={component.horizontalAlignment}
|
||||
>
|
||||
<ToggleButtonGroup noWFull bind:selected={component.horizontalAlignment}>
|
||||
{#snippet children({ item })}
|
||||
<ToggleButton value="left" icon={AlignStartVertical} {item} />
|
||||
<ToggleButton value="center" icon={AlignCenterVertical} {item} />
|
||||
@@ -39,11 +31,7 @@
|
||||
</ToggleButtonGroup>
|
||||
{/if}
|
||||
{#if component.type !== 'formcomponent' && component.verticalAlignment}
|
||||
<ToggleButtonGroup
|
||||
noWFull
|
||||
on:selected={() => ($app = $app)}
|
||||
bind:selected={component.verticalAlignment}
|
||||
>
|
||||
<ToggleButtonGroup noWFull bind:selected={component.verticalAlignment}>
|
||||
{#snippet children({ item })}
|
||||
<ToggleButton value="top" icon={AlignStartHorizontal} {item} />
|
||||
<ToggleButton value="center" icon={AlignCenterHorizontal} {item} />
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
}
|
||||
|
||||
function appendMigrationsToCss(migrations: Map<string, string[]>) {
|
||||
const theme = $app.theme
|
||||
const theme = app.theme
|
||||
|
||||
if (theme?.type === 'path') {
|
||||
sendUserToast(
|
||||
@@ -111,7 +111,7 @@
|
||||
|
||||
theme.css = cssString
|
||||
|
||||
$app.theme = theme
|
||||
app.theme = theme
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@
|
||||
</Badge>
|
||||
</div>
|
||||
{/if}
|
||||
{#if component?.type && $app.css}
|
||||
{#if component?.type && app.css}
|
||||
{#each Object.keys(component.customCss ?? {}) as cssKey}
|
||||
{#if component.customCss?.[cssKey].style != undefined && component.customCss[cssKey].style !== ''}
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
@@ -215,15 +215,15 @@
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
{#if hasStyles(component?.type ? $app.css?.[component?.type] : undefined)}
|
||||
{#if hasStyles(component?.type ? app.css?.[component?.type] : undefined)}
|
||||
<div class="leading-6 text-xs font-semibold">
|
||||
Global: {component?.type ? ccomponents[component.type]?.name : ''}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if component?.type && $app.css}
|
||||
{#each Object.keys($app.css[component?.type] ?? {}) as cssKey}
|
||||
{#if type && $app.css?.[type]?.[cssKey].style != undefined && $app.css[type]?.[cssKey].style !== ''}
|
||||
{#if component?.type && app.css}
|
||||
{#each Object.keys(app.css[component?.type] ?? {}) as cssKey}
|
||||
{#if type && app.css?.[type]?.[cssKey].style != undefined && app.css[type]?.[cssKey].style !== ''}
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<div>
|
||||
<div class="flex flex-row justify-between items-center py-0.5">
|
||||
@@ -236,9 +236,9 @@
|
||||
color="dark"
|
||||
size="xs"
|
||||
on:click={() => {
|
||||
if (type && $app.css?.[type]) {
|
||||
setOrUpdateMigration(cssKey, $app.css[type][cssKey].style)
|
||||
$app.css[type][cssKey].style = ''
|
||||
if (type && app.css?.[type]) {
|
||||
setOrUpdateMigration(cssKey, app.css[type][cssKey].style)
|
||||
app.css[type][cssKey].style = ''
|
||||
}
|
||||
}}
|
||||
endIcon={{ icon: MoveRight }}
|
||||
@@ -247,14 +247,14 @@
|
||||
</Button>
|
||||
</div>
|
||||
<div class="border p-2 rounded-md">
|
||||
<Highlight code={$app.css[type][cssKey].style} language={css} />
|
||||
<Highlight code={app.css[type][cssKey].style} language={css} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="">
|
||||
<div class="leading-6 text-xs font-semibold my-1">Preview</div>
|
||||
<div class="border rounded-md p-2">
|
||||
<Highlight
|
||||
code={`${getSelector(cssKey)} {\n\t${$app.css[type][cssKey].style}\n}`}
|
||||
code={`${getSelector(cssKey)} {\n\t${app.css[type][cssKey].style}\n}`}
|
||||
language={css}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -7,29 +7,35 @@
|
||||
import typescript from 'svelte-highlight/languages/typescript'
|
||||
import { Button } from '$lib/components/common'
|
||||
import HighlightTheme from '$lib/components/HighlightTheme.svelte'
|
||||
export let type: keyof typeof components
|
||||
interface Props {
|
||||
type: keyof typeof components
|
||||
}
|
||||
|
||||
let { type }: Props = $props()
|
||||
|
||||
const componentControls = getComponentControl(type)
|
||||
|
||||
let collapsed: boolean = true
|
||||
let collapsed: boolean = $state(true)
|
||||
</script>
|
||||
|
||||
<HighlightTheme />
|
||||
|
||||
{#if componentControls?.length > 0}
|
||||
<PanelSection title="Controls">
|
||||
<div slot="action" class="flex justify-end flex-wrap gap-1">
|
||||
<Button
|
||||
color="light"
|
||||
size="xs2"
|
||||
btnClasses="text-2xs font-normal"
|
||||
on:click={() => {
|
||||
collapsed = !collapsed
|
||||
}}
|
||||
>
|
||||
{collapsed ? 'Show' : 'Hide'} details
|
||||
</Button>
|
||||
</div>
|
||||
{#snippet action()}
|
||||
<div class="flex justify-end flex-wrap gap-1">
|
||||
<Button
|
||||
color="light"
|
||||
size="xs2"
|
||||
btnClasses="text-2xs font-normal"
|
||||
on:click={() => {
|
||||
collapsed = !collapsed
|
||||
}}
|
||||
>
|
||||
{collapsed ? 'Show' : 'Hide'} details
|
||||
</Button>
|
||||
</div>
|
||||
{/snippet}
|
||||
|
||||
{#if collapsed}
|
||||
<div class="flex flex-row gap-1 flex-wrap">
|
||||
|
||||
+2
-3
@@ -17,7 +17,7 @@
|
||||
|
||||
let { componentInput = $bindable(), disableStatic = false, evalV2editor }: Props = $props()
|
||||
|
||||
const { onchange, connectingInput, app } = getContext<AppViewerContext>('AppViewerContext')
|
||||
const { onchange, connectingInput } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@@ -45,8 +45,7 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
evalV2editor?.setCode(expr)
|
||||
$app = $app
|
||||
evalV2editor?.setCode(expr) // $app = $app
|
||||
}
|
||||
|
||||
let clientWidth: number = $state(0)
|
||||
|
||||
@@ -46,11 +46,19 @@
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import Popover from '$lib/components/Popover.svelte'
|
||||
|
||||
export let componentSettings: { item: GridItem; parent: string | undefined } | undefined =
|
||||
undefined
|
||||
export let onDelete: (() => void) | undefined = undefined
|
||||
export let noGrid = false
|
||||
export let duplicateMoveAllowed = true
|
||||
interface Props {
|
||||
componentSettings?: { item: GridItem; parent: string | undefined } | undefined
|
||||
onDelete?: (() => void) | undefined
|
||||
noGrid?: boolean
|
||||
duplicateMoveAllowed?: boolean
|
||||
}
|
||||
|
||||
let {
|
||||
componentSettings = $bindable(undefined),
|
||||
onDelete = undefined,
|
||||
noGrid = false,
|
||||
duplicateMoveAllowed = true
|
||||
}: Props = $props()
|
||||
|
||||
const {
|
||||
app,
|
||||
@@ -67,7 +75,7 @@
|
||||
const { history, movingcomponents } = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
function removeGridElement() {
|
||||
push(history, $app)
|
||||
push(history, app)
|
||||
|
||||
const id = componentSettings?.item?.id
|
||||
const onDeleteComponentControl = id ? $componentControl[id]?.onDelete : undefined
|
||||
@@ -91,7 +99,7 @@
|
||||
$selectedComponent = undefined
|
||||
$focusedGrid = undefined
|
||||
if (componentSettings?.item && !noGrid) {
|
||||
let ids = deleteGridItem($app, componentSettings?.item.data, componentSettings?.parent)
|
||||
let ids = deleteGridItem(app, componentSettings?.item.data, componentSettings?.parent)
|
||||
for (const key of ids) {
|
||||
delete $runnableComponents[key]
|
||||
}
|
||||
@@ -100,7 +108,7 @@
|
||||
if (componentSettings?.item?.data?.id) {
|
||||
delete $runnableComponents[componentSettings?.item?.data?.id]
|
||||
}
|
||||
$app = $app
|
||||
// $app = $app
|
||||
$runnableComponents = $runnableComponents
|
||||
|
||||
onDelete?.()
|
||||
@@ -108,10 +116,10 @@
|
||||
|
||||
let viewCssOptions = false
|
||||
|
||||
$: extraLib =
|
||||
let extraLib = $derived(
|
||||
(componentSettings?.item?.data?.componentInput?.type === 'template' ||
|
||||
componentSettings?.item?.data?.componentInput?.type === 'templatev2') &&
|
||||
$worldStore
|
||||
$worldStore
|
||||
? buildExtraLib(
|
||||
$worldStore?.outputsById ?? {},
|
||||
componentSettings?.item?.data?.id,
|
||||
@@ -119,6 +127,7 @@
|
||||
false
|
||||
)
|
||||
: undefined
|
||||
)
|
||||
|
||||
// `
|
||||
// /** The current's app state */
|
||||
@@ -144,7 +153,7 @@
|
||||
? isTriggerable(componentSettings?.item.data.type)
|
||||
: false
|
||||
|
||||
let evalV2editor: EvalV2InputEditor | undefined = undefined
|
||||
let evalV2editor: EvalV2InputEditor | undefined = $state(undefined)
|
||||
|
||||
function transformToFrontend() {
|
||||
if (componentSettings?.item.data.componentInput) {
|
||||
@@ -173,17 +182,17 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={keydown} />
|
||||
<svelte:window onkeydown={keydown} />
|
||||
|
||||
{#if componentSettings?.item?.id && isTableAction(componentSettings?.item?.id, $app)}
|
||||
{#if componentSettings?.item?.id && isTableAction(componentSettings?.item?.id, app)}
|
||||
<div
|
||||
class="flex items-center px-3 py-2 bg-surface border-b text-xs font-semibold gap-2 justify-between"
|
||||
>
|
||||
<div class="flex flex-row items-center gap-2">
|
||||
<Popover>
|
||||
<svelte:fragment slot="text">
|
||||
{#snippet text()}
|
||||
<div class="flex flex-row gap-1"> Back to table component </div>
|
||||
</svelte:fragment>
|
||||
{/snippet}
|
||||
<Button
|
||||
iconOnly
|
||||
startIcon={{
|
||||
@@ -238,7 +247,7 @@
|
||||
: 'Data source'}
|
||||
id={'component-input'}
|
||||
>
|
||||
<svelte:fragment slot="action">
|
||||
{#snippet action()}
|
||||
<div class="flex flex-row gap-1 justify-center items-center">
|
||||
<DocLink
|
||||
docLink={'https://www.windmill.dev/docs/apps/app-runnable-panel#creating-a-runnable'}
|
||||
@@ -252,7 +261,7 @@
|
||||
{`${component.id}`}
|
||||
</div>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
{/snippet}
|
||||
|
||||
{#if componentSettings.item.data.componentInput}
|
||||
<ComponentInputTypeEditor
|
||||
@@ -314,7 +323,7 @@
|
||||
id={component.id}
|
||||
bind:componentInput={componentSettings.item.data.componentInput}
|
||||
/>
|
||||
<a class="text-2xs" on:click={transformToFrontend} href={undefined}>
|
||||
<a class="text-2xs" onclick={transformToFrontend} href={undefined}>
|
||||
transform to a frontend script
|
||||
</a>
|
||||
{:else if componentSettings.item.data.componentInput?.type === 'runnable' && component.componentInput !== undefined}
|
||||
@@ -470,17 +479,19 @@
|
||||
|
||||
{#if Object.keys(ccomponents[component.type]?.customCss ?? {}).length > 0}
|
||||
<PanelSection title="Styling">
|
||||
<div slot="action" class="flex justify-end flex-wrap gap-1">
|
||||
<Button
|
||||
color="light"
|
||||
size="xs"
|
||||
variant="border"
|
||||
startIcon={{ icon: ChevronLeft }}
|
||||
on:click={() => secondaryMenuLeft.toggle(StylePanel, { type: 'style' })}
|
||||
>
|
||||
Show
|
||||
</Button>
|
||||
</div>
|
||||
{#snippet action()}
|
||||
<div class="flex justify-end flex-wrap gap-1">
|
||||
<Button
|
||||
color="light"
|
||||
size="xs"
|
||||
variant="border"
|
||||
startIcon={{ icon: ChevronLeft }}
|
||||
on:click={() => secondaryMenuLeft.toggle(StylePanel, { type: 'style' })}
|
||||
>
|
||||
Show
|
||||
</Button>
|
||||
</div>
|
||||
{/snippet}
|
||||
<div class="flex gap-2 items-center flex-wrap">
|
||||
<div class="!text-2xs">Full height</div>
|
||||
{#if componentSettings?.item?.[12]?.fullHeight !== undefined}
|
||||
@@ -525,20 +536,22 @@
|
||||
|
||||
{#if duplicateMoveAllowed}
|
||||
<PanelSection title="Copy/Move">
|
||||
<div slot="action">
|
||||
<Button
|
||||
size="xs"
|
||||
color="red"
|
||||
variant="border"
|
||||
on:click={removeGridElement}
|
||||
shortCut={{
|
||||
key: isMac() ? getModifierKey() + 'Del' : 'Del',
|
||||
withoutModifier: true
|
||||
}}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
</div>
|
||||
{#snippet action()}
|
||||
<div>
|
||||
<Button
|
||||
size="xs"
|
||||
color="red"
|
||||
variant="border"
|
||||
on:click={removeGridElement}
|
||||
shortCut={{
|
||||
key: isMac() ? getModifierKey() + 'Del' : 'Del',
|
||||
withoutModifier: true
|
||||
}}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
</div>
|
||||
{/snippet}
|
||||
|
||||
<div class="overflow-auto grid grid-cols-2 gap-1 text-tertiary">
|
||||
<div>
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
|
||||
function addGridItemContext(parentId: string) {
|
||||
if (!parentId) return
|
||||
const gridItem = findGridItem($app, parentId)
|
||||
const gridItem = findGridItem(app, parentId)
|
||||
addParentContextVariable(gridItem)
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
|
||||
function findParentsContextVariables(id: string): void {
|
||||
if (!id) return
|
||||
const allParents = dfs($app.grid, id, $app.subgrids ?? {})
|
||||
const allParents = dfs(app.grid, id, app.subgrids ?? {})
|
||||
|
||||
if (!allParents) return
|
||||
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
|
||||
export function removeGridElement() {
|
||||
const id = $selectedComponent?.[0]
|
||||
const componentSetting = findComponentSettings($app, id)
|
||||
push(history, $app)
|
||||
const componentSetting = findComponentSettings(app, id)
|
||||
push(history, app)
|
||||
|
||||
const onDeleteComponentControl = id ? $componentControl[id]?.onDelete : undefined
|
||||
if (onDeleteComponentControl) {
|
||||
@@ -44,7 +44,7 @@
|
||||
$selectedComponent = undefined
|
||||
$focusedGrid = undefined
|
||||
if (componentSetting?.item && !noGrid) {
|
||||
let ids = deleteGridItem($app, componentSetting?.item.data, componentSetting?.parent)
|
||||
let ids = deleteGridItem(app, componentSetting?.item.data, componentSetting?.parent)
|
||||
for (const key of ids) {
|
||||
delete $runnableComponents[key]
|
||||
}
|
||||
@@ -53,7 +53,7 @@
|
||||
if (componentSetting?.item?.data?.id) {
|
||||
delete $runnableComponents[componentSetting?.item?.data?.id]
|
||||
}
|
||||
$app = $app
|
||||
// $app = $app
|
||||
$runnableComponents = $runnableComponents
|
||||
|
||||
onDelete?.()
|
||||
|
||||
@@ -13,24 +13,32 @@
|
||||
import { deleteGridItem } from '../appUtils'
|
||||
import type { AppComponent } from '../component'
|
||||
|
||||
export let conditions: RichConfiguration[] = []
|
||||
export let component: AppComponent
|
||||
interface Props {
|
||||
conditions?: RichConfiguration[]
|
||||
component: AppComponent
|
||||
}
|
||||
|
||||
let items = conditions.slice(0, -1).map((condition, index) => {
|
||||
return { value: condition, id: generateRandomString(), originalIndex: index }
|
||||
let { conditions = $bindable([]), component = $bindable() }: Props = $props()
|
||||
|
||||
let items = $state(
|
||||
conditions.slice(0, -1).map((condition, index) => {
|
||||
return { value: condition, id: generateRandomString(), originalIndex: index }
|
||||
})
|
||||
)
|
||||
|
||||
$effect.pre(() => {
|
||||
conditions = items
|
||||
.map((item) => item.value)
|
||||
.concat([
|
||||
{
|
||||
type: 'evalv2',
|
||||
expr: 'true',
|
||||
fieldType: 'boolean',
|
||||
connections: []
|
||||
}
|
||||
])
|
||||
})
|
||||
|
||||
$: conditions = items
|
||||
.map((item) => item.value)
|
||||
.concat([
|
||||
{
|
||||
type: 'evalv2',
|
||||
expr: 'true',
|
||||
fieldType: 'boolean',
|
||||
connections: []
|
||||
}
|
||||
])
|
||||
|
||||
const { app, runnableComponents, componentControl } =
|
||||
getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
@@ -49,7 +57,7 @@
|
||||
const newSubgrids = {}
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
newSubgrids[`${component.id}-${i}`] =
|
||||
$app!.subgrids![`${component.id}-${items[i].originalIndex}`] ?? []
|
||||
app!.subgrids![`${component.id}-${items[i].originalIndex}`] ?? []
|
||||
}
|
||||
|
||||
// update originalIndex
|
||||
@@ -57,11 +65,10 @@
|
||||
item.originalIndex = i
|
||||
})
|
||||
|
||||
$app!.subgrids = {
|
||||
...$app!.subgrids,
|
||||
app!.subgrids = {
|
||||
...app!.subgrids,
|
||||
...newSubgrids
|
||||
}
|
||||
$app = $app
|
||||
} // $app = $app
|
||||
|
||||
tick().then(() => {
|
||||
const targetIndex = items.findIndex((i) => i.id === e.detail.info.id)
|
||||
@@ -72,8 +79,8 @@
|
||||
|
||||
function deleteSubgrid(index: number) {
|
||||
let subgrid = `${component.id}-${index}`
|
||||
for (const item of $app!.subgrids![subgrid]) {
|
||||
const components = deleteGridItem($app, item.data, subgrid)
|
||||
for (const item of app!.subgrids![subgrid]) {
|
||||
const components = deleteGridItem(app, item.data, subgrid)
|
||||
for (const key in components) {
|
||||
delete $runnableComponents[key]
|
||||
}
|
||||
@@ -81,7 +88,7 @@
|
||||
|
||||
$runnableComponents = $runnableComponents
|
||||
for (let i = index; i < items.length; i++) {
|
||||
$app!.subgrids![`${component.id}-${i}`] = $app!.subgrids![`${component.id}-${i + 1}`]
|
||||
app!.subgrids![`${component.id}-${i}`] = app!.subgrids![`${component.id}-${i + 1}`]
|
||||
}
|
||||
|
||||
// Remove the corresponding item from the items array
|
||||
@@ -94,21 +101,20 @@
|
||||
})
|
||||
items = nitems
|
||||
|
||||
delete $app!.subgrids![`${component.id}-${items.length + 1}`]
|
||||
$app = $app
|
||||
delete app!.subgrids![`${component.id}-${items.length + 1}`] // $app = $app
|
||||
}
|
||||
|
||||
function addCondition(): void {
|
||||
const numberOfConditions = conditions.length
|
||||
|
||||
if (!$app.subgrids) {
|
||||
$app.subgrids = {}
|
||||
if (!app.subgrids) {
|
||||
app.subgrids = {}
|
||||
}
|
||||
|
||||
$app.subgrids[`${component.id}-${numberOfConditions}`] =
|
||||
$app.subgrids[`${component.id}-${numberOfConditions - 1}`]
|
||||
app.subgrids[`${component.id}-${numberOfConditions}`] =
|
||||
app.subgrids[`${component.id}-${numberOfConditions - 1}`]
|
||||
|
||||
$app.subgrids[`${component.id}-${numberOfConditions - 1}`] = []
|
||||
app.subgrids[`${component.id}-${numberOfConditions - 1}`] = []
|
||||
|
||||
const newCondition: AppInputSpec<'boolean', boolean> = {
|
||||
type: 'evalv2',
|
||||
@@ -141,8 +147,8 @@
|
||||
flipDurationMs: 200,
|
||||
dropTargetStyle: {}
|
||||
}}
|
||||
on:consider={handleConsider}
|
||||
on:finalize={handleFinalize}
|
||||
onconsider={handleConsider}
|
||||
onfinalize={handleFinalize}
|
||||
>
|
||||
{#each items as item, index (item.id)}
|
||||
{@const condition = item.value}
|
||||
@@ -168,14 +174,14 @@
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col justify-center gap-2">
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div on:click={() => deleteSubgrid(index)}>
|
||||
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<div onclick={() => deleteSubgrid(index)}>
|
||||
<X size={16} />
|
||||
</div>
|
||||
|
||||
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<div use:dragHandle class="w-4 h-4 handle" aria-label="drag-handle">
|
||||
<GripVertical size={16} />
|
||||
</div>
|
||||
|
||||
@@ -41,8 +41,7 @@
|
||||
delete $app!.subgrids![`${component.id}-${panes.length}`]
|
||||
|
||||
panes = panes
|
||||
component.numberOfSubgrids = panes.length
|
||||
$app = $app
|
||||
component.numberOfSubgrids = panes.length // $app = $app
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
<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'
|
||||
@@ -11,27 +14,40 @@
|
||||
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(
|
||||
tabs.map((tab, index) => {
|
||||
return { value: tab, id: generateRandomString(), originalIndex: index }
|
||||
})
|
||||
)
|
||||
|
||||
$effect.pre(() => {
|
||||
tabs = items.map((item) => item.value)
|
||||
})
|
||||
|
||||
const { app, runnableComponents, componentControl } =
|
||||
getContext<AppViewerContext>('AppViewerContext')
|
||||
@@ -39,11 +55,11 @@
|
||||
function addTab() {
|
||||
const numberOfTabs = items.length
|
||||
|
||||
if (!$app.subgrids) {
|
||||
$app.subgrids = {}
|
||||
if (!app.subgrids) {
|
||||
app.subgrids = {}
|
||||
}
|
||||
|
||||
$app.subgrids[`${component.id}-${numberOfTabs}`] = []
|
||||
app.subgrids[`${component.id}-${numberOfTabs}`] = []
|
||||
items = [
|
||||
...items,
|
||||
{
|
||||
@@ -59,8 +75,8 @@
|
||||
|
||||
function deleteSubgrid(index: number) {
|
||||
let subgrid = `${component.id}-${index}`
|
||||
for (const item of $app!.subgrids![subgrid]) {
|
||||
const components = deleteGridItem($app, item.data, subgrid)
|
||||
for (const item of app!.subgrids![subgrid]) {
|
||||
const components = deleteGridItem(app, item.data, subgrid)
|
||||
for (const key in components) {
|
||||
delete $runnableComponents[key]
|
||||
}
|
||||
@@ -68,7 +84,7 @@
|
||||
$runnableComponents = $runnableComponents
|
||||
|
||||
for (let i = index; i < items.length - 1; i++) {
|
||||
$app!.subgrids![`${component.id}-${i}`] = $app!.subgrids![`${component.id}-${i + 1}`]
|
||||
app!.subgrids![`${component.id}-${i}`] = app!.subgrids![`${component.id}-${i + 1}`]
|
||||
}
|
||||
|
||||
// Remove the corresponding item from the items array
|
||||
@@ -84,8 +100,7 @@
|
||||
})
|
||||
items = items
|
||||
|
||||
delete $app!.subgrids![`${component.id}-${items.length}`]
|
||||
$app = $app
|
||||
delete app!.subgrids![`${component.id}-${items.length}`] // $app = $app
|
||||
}
|
||||
|
||||
function handleConsider(e: CustomEvent): void {
|
||||
@@ -110,7 +125,7 @@
|
||||
const newSubgrids = {}
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
newSubgrids[`${component.id}-${i}`] =
|
||||
$app!.subgrids![`${component.id}-${items[i].originalIndex}`] ?? []
|
||||
app!.subgrids![`${component.id}-${items[i].originalIndex}`] ?? []
|
||||
}
|
||||
|
||||
const newDisabledTabs: RichConfiguration[] = []
|
||||
@@ -124,11 +139,10 @@
|
||||
item.originalIndex = i
|
||||
})
|
||||
|
||||
$app!.subgrids = {
|
||||
...$app!.subgrids,
|
||||
app!.subgrids = {
|
||||
...app!.subgrids,
|
||||
...newSubgrids
|
||||
}
|
||||
$app = $app
|
||||
} // $app = $app
|
||||
|
||||
tick().then(() => {
|
||||
const targetIndex = items.findIndex((i) => i.id === e.detail.info.id)
|
||||
@@ -149,15 +163,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 +180,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>
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
}
|
||||
})
|
||||
|
||||
const { connectingInput, app, workspace } = getContext<AppViewerContext>('AppViewerContext')
|
||||
const { connectingInput, workspace } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@@ -129,8 +129,7 @@
|
||||
{ componentId: connection.componentId, id: connection.path.split('.')[0].split('[')[0] }
|
||||
]
|
||||
}
|
||||
evalV2editor?.setCode(expr)
|
||||
$app = $app
|
||||
evalV2editor?.setCode(expr) // $app = $app
|
||||
}
|
||||
|
||||
function closeConnection() {
|
||||
|
||||
@@ -13,8 +13,7 @@
|
||||
export let components: (BaseAppComponent & ButtonComponent)[]
|
||||
export let id: string
|
||||
|
||||
const { selectedComponent, app, errorByComponent } =
|
||||
getContext<AppViewerContext>('AppViewerContext')
|
||||
const { selectedComponent, errorByComponent } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
function addComponent() {
|
||||
const actionId = getNextId(components.map((x) => x.id.split('_')[1]))
|
||||
@@ -23,8 +22,7 @@
|
||||
...appComponentFromType('buttoncomponent')(`${id}_${actionId}`),
|
||||
recomputeIds: []
|
||||
}
|
||||
components = [...components, newComponent]
|
||||
$app = $app
|
||||
components = [...components, newComponent] // $app = $app
|
||||
}
|
||||
|
||||
function deleteComponent(cid: string) {
|
||||
@@ -32,8 +30,7 @@
|
||||
|
||||
delete $errorByComponent[cid]
|
||||
|
||||
$selectedComponent = [id]
|
||||
$app = $app
|
||||
$selectedComponent = [id] // $app = $app
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -9,20 +9,25 @@
|
||||
|
||||
const { app } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
export let appInput: ResultAppInput
|
||||
export let appComponent: AppComponent
|
||||
|
||||
$: if (appInput.autoRefresh === undefined) {
|
||||
appInput.autoRefresh = true
|
||||
interface Props {
|
||||
appInput: ResultAppInput
|
||||
appComponent: AppComponent
|
||||
}
|
||||
|
||||
let { appInput = $bindable(), appComponent = $bindable() }: Props = $props()
|
||||
|
||||
$effect.pre(() => {
|
||||
if (appInput.autoRefresh === undefined) {
|
||||
appInput.autoRefresh = true
|
||||
}
|
||||
})
|
||||
|
||||
function detach() {
|
||||
if (appInput.runnable?.type === 'runnableByName' && appInput.runnable.inlineScript) {
|
||||
$app.unusedInlineScripts.push({
|
||||
app.unusedInlineScripts.push({
|
||||
name: appInput.runnable.name,
|
||||
inlineScript: appInput.runnable.inlineScript
|
||||
})
|
||||
$app = $app
|
||||
}) // $app = $app
|
||||
appInput = clearResultAppInput(appInput)
|
||||
}
|
||||
}
|
||||
@@ -31,7 +36,7 @@
|
||||
appInput = clearResultAppInput(appInput)
|
||||
}
|
||||
|
||||
$: {
|
||||
$effect.pre(() => {
|
||||
if (appInput.recomputeOnInputChanged === undefined) {
|
||||
appInput.recomputeOnInputChanged = true
|
||||
}
|
||||
@@ -40,11 +45,13 @@
|
||||
appInput.recomputeOnInputChanged = !appInput.doNotRecomputeOnInputChanged
|
||||
appInput.doNotRecomputeOnInputChanged = undefined
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
$: hasScript =
|
||||
let hasScript = $derived(
|
||||
appInput?.runnable?.type === 'runnableByPath' ||
|
||||
(appInput?.runnable?.type === 'runnableByName' && appInput.runnable?.inlineScript !== undefined)
|
||||
(appInput?.runnable?.type === 'runnableByName' &&
|
||||
appInput.runnable?.inlineScript !== undefined)
|
||||
)
|
||||
|
||||
function getActions(_hasScript: boolean): ActionType[] {
|
||||
return [
|
||||
@@ -56,7 +63,7 @@
|
||||
color: 'light',
|
||||
callback: detach
|
||||
}
|
||||
] as const)
|
||||
] as const)
|
||||
: []),
|
||||
{
|
||||
label: 'Clear',
|
||||
@@ -67,7 +74,7 @@
|
||||
]
|
||||
}
|
||||
|
||||
$: actions = getActions(hasScript)
|
||||
let actions = $derived(getActions(hasScript))
|
||||
</script>
|
||||
|
||||
<ComponentScriptSettings bind:appInput bind:appComponent {hasScript} {actions} />
|
||||
|
||||
@@ -20,28 +20,29 @@
|
||||
|
||||
const { app, cssEditorOpen, selectedComponent } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let component: AppComponent | undefined
|
||||
$: {
|
||||
const newComponent = findComponentSettings($app, $selectedComponent?.[0])?.item?.data
|
||||
let component: AppComponent | undefined = $state()
|
||||
$effect.pre(() => {
|
||||
const newComponent = findComponentSettings(app, $selectedComponent?.[0])?.item?.data
|
||||
if (component != newComponent) {
|
||||
component = newComponent
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
let tab: 'local' | 'global' = 'local'
|
||||
let overrideGlobalCSS: (() => void) | undefined = undefined
|
||||
let overrideLocalCSS: (() => void) | undefined = undefined
|
||||
$: type = component?.type
|
||||
let migrationModal: CssMigrationModal | undefined = undefined
|
||||
let tab: 'local' | 'global' = $state('local')
|
||||
let overrideGlobalCSS: (() => void) | undefined = $state(undefined)
|
||||
let overrideLocalCSS: (() => void) | undefined = $state(undefined)
|
||||
let type = $derived(component?.type)
|
||||
let migrationModal: CssMigrationModal | undefined = $state(undefined)
|
||||
|
||||
$: customCssByComponentType =
|
||||
component?.type && $app.css
|
||||
? Object.entries($app.css[component.type] || {}).map(([id, v]) => ({
|
||||
let customCssByComponentType = $derived(
|
||||
component?.type && app.css
|
||||
? Object.entries(app.css[component.type] || {}).map(([id, v]) => ({
|
||||
id,
|
||||
forceStyle: v?.style != undefined,
|
||||
forceClass: v?.['class'] != undefined
|
||||
}))
|
||||
: undefined
|
||||
)
|
||||
|
||||
function copyLocalToGlobal(name: string, value: ComponentCssProperty | undefined) {
|
||||
if (!value) {
|
||||
@@ -51,18 +52,16 @@
|
||||
|
||||
if (!type) return
|
||||
|
||||
if (hasStyleValue($app.css?.[type]?.[name])) {
|
||||
if (hasStyleValue(app.css?.[type]?.[name])) {
|
||||
overrideGlobalCSS = () => {
|
||||
$app.css![type]![name] = JSON.parse(JSON.stringify(value))
|
||||
app.set($app)
|
||||
app.css![type]![name] = JSON.parse(JSON.stringify(value))
|
||||
}
|
||||
} else {
|
||||
if (!$app.css![type]) {
|
||||
if (!app.css![type]) {
|
||||
initGlobalCss()
|
||||
}
|
||||
|
||||
$app.css![type]![name] = JSON.parse(JSON.stringify(value))
|
||||
app.set($app)
|
||||
app.css![type]![name] = JSON.parse(JSON.stringify(value))
|
||||
sendUserToast('Global CSS copied')
|
||||
}
|
||||
}
|
||||
@@ -75,11 +74,9 @@
|
||||
if (hasStyleValue(value)) {
|
||||
overrideLocalCSS = () => {
|
||||
component!.customCss![id] = JSON.parse(JSON.stringify(value))
|
||||
app.set($app)
|
||||
}
|
||||
} else {
|
||||
component!.customCss![id] = JSON.parse(JSON.stringify(value))
|
||||
app.set($app)
|
||||
sendUserToast('Local CSS copied')
|
||||
}
|
||||
}
|
||||
@@ -88,21 +85,20 @@
|
||||
function initGlobalCss() {
|
||||
// If the global css is not initialised, we initialise it.
|
||||
// Should only happen once per app
|
||||
if (!$app.css) {
|
||||
$app.css = {}
|
||||
if (!app.css) {
|
||||
app.css = {}
|
||||
}
|
||||
|
||||
// If the global css for this component type is not initialised, we initialise it.
|
||||
// Should only happen once per component type
|
||||
if (
|
||||
$app.css &&
|
||||
app.css &&
|
||||
component &&
|
||||
!$app.css[component.type]?.style &&
|
||||
!app.css[component.type]?.style &&
|
||||
components[component.type] &&
|
||||
$app.css[component.type] === undefined
|
||||
app.css[component.type] === undefined
|
||||
) {
|
||||
$app.css[component.type] = JSON.parse(JSON.stringify(components[component.type].customCss))
|
||||
app.set($app)
|
||||
app.css[component.type] = JSON.parse(JSON.stringify(components[component.type].customCss))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,13 +192,12 @@
|
||||
wmClass={getSelector(name)}
|
||||
componentType={component.type}
|
||||
bind:value={component.customCss[name]}
|
||||
on:change={() => app.set($app)}
|
||||
shouldDisplayRight={hasStyleValue(component.customCss[name])}
|
||||
on:right={() => {
|
||||
copyLocalToGlobal(name, component?.customCss?.[name])
|
||||
tab = 'global'
|
||||
}}
|
||||
overridding={hasStyleValue($app.css?.[component.type]?.[name]) &&
|
||||
overridding={hasStyleValue(app.css?.[component.type]?.[name]) &&
|
||||
hasStyleValue(component.customCss[name])}
|
||||
/>
|
||||
</div>
|
||||
@@ -214,16 +209,16 @@
|
||||
{#if type}
|
||||
{#each customCssByComponentType ?? [] as { id, forceStyle, forceClass }}
|
||||
<div class="w-full">
|
||||
{#if $app.css && type && $app.css[type] && component?.customCss}
|
||||
{#if app.css && type && app.css[type] && component?.customCss}
|
||||
<CssPropertyWrapper
|
||||
{forceStyle}
|
||||
{forceClass}
|
||||
{id}
|
||||
bind:property={$app.css[type]}
|
||||
bind:property={app.css[type]}
|
||||
on:left={() => {
|
||||
copyGlobalToLocal(
|
||||
id,
|
||||
component?.type ? $app?.css?.[component?.type]?.[id] : undefined
|
||||
component?.type ? app?.css?.[component?.type]?.[id] : undefined
|
||||
)
|
||||
tab = 'local'
|
||||
}}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { createBubbler } from 'svelte/legacy'
|
||||
|
||||
const bubble = createBubbler()
|
||||
import { Badge } from '$lib/components/common'
|
||||
import Button from '$lib/components/common/button/Button.svelte'
|
||||
import { getNextId } from '$lib/components/flows/idUtils'
|
||||
@@ -15,11 +18,15 @@
|
||||
import TableActionsWizard from '$lib/components/wizards/TableActionsWizard.svelte'
|
||||
import Alert from '$lib/components/common/alert/Alert.svelte'
|
||||
|
||||
export let components:
|
||||
| (BaseAppComponent & (ButtonComponent | CheckboxComponent | SelectComponent))[]
|
||||
| undefined
|
||||
interface Props {
|
||||
components:
|
||||
| (BaseAppComponent & (ButtonComponent | CheckboxComponent | SelectComponent))[]
|
||||
| undefined
|
||||
actionsOrder?: RichConfiguration | undefined
|
||||
id: string
|
||||
}
|
||||
|
||||
export let actionsOrder: RichConfiguration | undefined = undefined
|
||||
let { components = $bindable(), actionsOrder = $bindable(undefined), id }: Props = $props()
|
||||
|
||||
// Migration code:
|
||||
onMount(() => {
|
||||
@@ -28,16 +35,17 @@
|
||||
}
|
||||
})
|
||||
|
||||
let items =
|
||||
let items = $state(
|
||||
components?.map((tab, index) => {
|
||||
return { value: tab, id: generateRandomString(), originalIndex: index }
|
||||
}) ?? []
|
||||
)
|
||||
|
||||
$: components = items.map((item) => item.value)
|
||||
$effect.pre(() => {
|
||||
components = items.map((item) => item.value)
|
||||
})
|
||||
|
||||
export let id: string
|
||||
|
||||
const { selectedComponent, app, errorByComponent, hoverStore } =
|
||||
const { selectedComponent, errorByComponent, hoverStore } =
|
||||
getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
function addComponent(typ: 'buttoncomponent' | 'checkboxcomponent' | 'selectcomponent') {
|
||||
@@ -68,8 +76,7 @@
|
||||
}
|
||||
]
|
||||
|
||||
components = [...components, newComponent]
|
||||
$app = $app
|
||||
components = [...components, newComponent] // $app = $app
|
||||
}
|
||||
|
||||
function deleteComponent(cid: string, index: number) {
|
||||
@@ -79,8 +86,7 @@
|
||||
components = components.filter((x) => x.id !== cid)
|
||||
delete $errorByComponent[cid]
|
||||
|
||||
$selectedComponent = [id]
|
||||
$app = $app
|
||||
$selectedComponent = [id] // $app = $app
|
||||
// Remove the corresponding item from the items array
|
||||
items = items.filter((item) => item.originalIndex !== index)
|
||||
}
|
||||
@@ -99,9 +105,9 @@
|
||||
|
||||
{#if components}
|
||||
<PanelSection title={`Table Actions`}>
|
||||
<svelte:fragment slot="action">
|
||||
{#snippet action()}
|
||||
<TableActionsWizard bind:actionsOrder selectedId={$selectedComponent?.[0] ?? ''} {components}>
|
||||
<svelte:fragment slot="trigger">
|
||||
{#snippet trigger()}
|
||||
<Button
|
||||
color="light"
|
||||
size="xs2"
|
||||
@@ -113,9 +119,9 @@
|
||||
<ListOrdered size={16} />
|
||||
</div>
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
{/snippet}
|
||||
</TableActionsWizard>
|
||||
</svelte:fragment>
|
||||
{/snippet}
|
||||
{#if components.length == 0}
|
||||
<span class="text-xs text-tertiary">No action buttons</span>
|
||||
{/if}
|
||||
@@ -126,27 +132,27 @@
|
||||
flipDurationMs: 200,
|
||||
dropTargetStyle: {}
|
||||
}}
|
||||
on:consider={handleConsider}
|
||||
on:finalize={handleFinalize}
|
||||
onconsider={handleConsider}
|
||||
onfinalize={handleFinalize}
|
||||
>
|
||||
{#each items as item, index (item.id)}
|
||||
{@const component = items[index].value}
|
||||
|
||||
<div animate:flip={{ duration: 200 }} class="flex flex-row gap-2 items-center mb-2">
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<!-- svelte-ignore a11y-mouse-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<!-- svelte-ignore a11y_mouse_events_have_key_events -->
|
||||
<div
|
||||
class={classNames(
|
||||
'w-full text-xs text-semibold truncate py-1.5 px-2 cursor-pointer justify-between flex items-center border rounded-md',
|
||||
'bg-surface hover:bg-surface-hover focus:border-primary text-secondary'
|
||||
)}
|
||||
on:click={() => {
|
||||
onclick={() => {
|
||||
$selectedComponent = [component.id]
|
||||
}}
|
||||
on:mouseover={() => {
|
||||
onmouseover={() => {
|
||||
$hoverStore = component.id
|
||||
}}
|
||||
on:keypress
|
||||
onkeypress={bubble('keypress')}
|
||||
>
|
||||
<div class="flex flex-row gap-2 items-center">
|
||||
<Badge color="dark-indigo">
|
||||
|
||||
@@ -2,18 +2,36 @@
|
||||
import { classNames } from '$lib/utils'
|
||||
import Tooltip from '../../../../Tooltip.svelte'
|
||||
|
||||
export let title: string
|
||||
export let noPadding: boolean = false
|
||||
export let fullHeight: boolean = true
|
||||
export let titlePadding: string = ''
|
||||
export let tooltip = ''
|
||||
export let documentationLink: string | undefined = undefined
|
||||
export let id: string | undefined = undefined
|
||||
interface Props {
|
||||
title: string
|
||||
noPadding?: boolean
|
||||
fullHeight?: boolean
|
||||
titlePadding?: string
|
||||
tooltip?: string
|
||||
documentationLink?: string | undefined
|
||||
id?: string | undefined
|
||||
class?: string
|
||||
action?: import('svelte').Snippet
|
||||
children?: import('svelte').Snippet
|
||||
}
|
||||
|
||||
let {
|
||||
title,
|
||||
noPadding = false,
|
||||
fullHeight = true,
|
||||
titlePadding = '',
|
||||
tooltip = '',
|
||||
documentationLink = undefined,
|
||||
id = undefined,
|
||||
class: className = '',
|
||||
action,
|
||||
children
|
||||
}: Props = $props()
|
||||
</script>
|
||||
|
||||
<div
|
||||
class={classNames(
|
||||
$$props.class,
|
||||
className,
|
||||
'flex flex-col gap-2 items-start',
|
||||
noPadding ? '' : 'p-3',
|
||||
fullHeight ? 'h-full' : ''
|
||||
@@ -31,7 +49,7 @@
|
||||
</Tooltip>
|
||||
{/if}
|
||||
</div>
|
||||
<slot name="action" />
|
||||
{@render action?.()}
|
||||
</div>
|
||||
<slot />
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
||||
+9
-10
@@ -57,10 +57,10 @@
|
||||
|
||||
function addSubGrid() {
|
||||
const numberOfPanes = nodes.length
|
||||
if (!$app.subgrids) {
|
||||
$app.subgrids = {}
|
||||
if (!app.subgrids) {
|
||||
app.subgrids = {}
|
||||
}
|
||||
$app.subgrids[`${component.id}-${numberOfPanes}`] = []
|
||||
app.subgrids[`${component.id}-${numberOfPanes}`] = []
|
||||
|
||||
component.numberOfSubgrids = nodes.length + 1
|
||||
}
|
||||
@@ -68,26 +68,25 @@
|
||||
function deleteSubgrid(index: number) {
|
||||
let subgrid = `${component.id}-${index}`
|
||||
|
||||
if (!$app.subgrids![subgrid]) {
|
||||
if (!app.subgrids![subgrid]) {
|
||||
return
|
||||
}
|
||||
|
||||
for (const item of $app!.subgrids![subgrid]) {
|
||||
const components = deleteGridItem($app, item.data, subgrid)
|
||||
for (const item of app!.subgrids![subgrid]) {
|
||||
const components = deleteGridItem(app, item.data, subgrid)
|
||||
for (const key in components) {
|
||||
delete $runnableComponents[key]
|
||||
}
|
||||
}
|
||||
$runnableComponents = $runnableComponents
|
||||
for (let i = index; i < nodes.length - 1; i++) {
|
||||
$app!.subgrids![`${component.id}-${i}`] = $app!.subgrids![`${component.id}-${i + 1}`]
|
||||
app!.subgrids![`${component.id}-${i}`] = app!.subgrids![`${component.id}-${i + 1}`]
|
||||
}
|
||||
nodes.splice(index, 1)
|
||||
delete $app!.subgrids![`${component.id}-${nodes.length}`]
|
||||
delete app!.subgrids![`${component.id}-${nodes.length}`]
|
||||
|
||||
nodes = nodes
|
||||
component.numberOfSubgrids = nodes.length
|
||||
$app = $app
|
||||
component.numberOfSubgrids = nodes.length // $app = $app
|
||||
}
|
||||
|
||||
function nodeCallbackHandler(
|
||||
|
||||
+2
-2
@@ -11,7 +11,7 @@
|
||||
|
||||
function applyConnection(connection: InputConnection) {
|
||||
componentInput.connection = connection
|
||||
$app = $app
|
||||
// $app = $app
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
if (componentInput.type === 'connected') {
|
||||
componentInput.connection = undefined
|
||||
}
|
||||
$app = $app
|
||||
// $app = $app
|
||||
}}
|
||||
>
|
||||
Disconnect
|
||||
|
||||
+2
-2
@@ -126,9 +126,9 @@
|
||||
bind:this={editor}
|
||||
lang="javascript"
|
||||
bind:code={
|
||||
() => componentInput.expr ?? '',
|
||||
() => componentInput?.expr ?? '',
|
||||
(e) => {
|
||||
if (componentInput.expr != e) {
|
||||
if (componentInput?.expr != e) {
|
||||
componentInput.expr = e
|
||||
}
|
||||
}
|
||||
|
||||
+8
-4
@@ -7,9 +7,13 @@
|
||||
import SelectedRunnable from '../SelectedRunnable.svelte'
|
||||
import type { AppEditorContext, AppViewerContext } from '$lib/components/apps/types'
|
||||
|
||||
export let appInput: ResultAppInput
|
||||
export let defaultUserInput = false
|
||||
export let appComponent: AppComponent
|
||||
interface Props {
|
||||
appInput: ResultAppInput
|
||||
defaultUserInput?: boolean
|
||||
appComponent: AppComponent
|
||||
}
|
||||
|
||||
let { appInput = $bindable(), defaultUserInput = false, appComponent }: Props = $props()
|
||||
|
||||
const { selectedComponentInEditor } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { app } = getContext<AppViewerContext>('AppViewerContext')
|
||||
@@ -34,7 +38,7 @@
|
||||
<SelectedRunnable {appComponent} bind:appInput />
|
||||
{:else if appInput !== undefined}
|
||||
<RunnableSelector
|
||||
unusedInlineScripts={$app.unusedInlineScripts}
|
||||
unusedInlineScripts={app.unusedInlineScripts}
|
||||
hideCreateScript={appComponent.type === 'flowstatuscomponent'}
|
||||
onlyFlow={appComponent.type === 'flowstatuscomponent'}
|
||||
{defaultUserInput}
|
||||
|
||||
+88
-59
@@ -1,4 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { run, createBubbler, stopPropagation } from 'svelte/legacy';
|
||||
|
||||
const bubble = createBubbler();
|
||||
import type { InputType, StaticInput, StaticOptions } from '../../../inputType'
|
||||
import ArrayStaticInputEditor from '../ArrayStaticInputEditor.svelte'
|
||||
import ResourcePicker from '$lib/components/ResourcePicker.svelte'
|
||||
@@ -26,32 +29,46 @@
|
||||
import S3FilePicker from '$lib/components/S3FilePicker.svelte'
|
||||
import FileUpload from '$lib/components/common/fileUpload/FileUpload.svelte'
|
||||
|
||||
export let componentInput: StaticInput<any> | 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
|
||||
interface Props {
|
||||
componentInput: StaticInput<any> | undefined;
|
||||
fieldType?: InputType | undefined;
|
||||
subFieldType?: InputType | undefined;
|
||||
selectOptions?: StaticOptions['selectOptions'] | undefined;
|
||||
placeholder?: string | undefined;
|
||||
format?: string | undefined;
|
||||
id: string | undefined;
|
||||
}
|
||||
|
||||
let {
|
||||
componentInput = $bindable(),
|
||||
fieldType = undefined,
|
||||
subFieldType = undefined,
|
||||
selectOptions = undefined,
|
||||
placeholder = undefined,
|
||||
format = undefined,
|
||||
id
|
||||
}: Props = $props();
|
||||
|
||||
const appContext = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
$: componentInput && appContext?.onchange?.()
|
||||
let s3FileUploadRawMode = false
|
||||
let s3FilePicker: S3FilePicker | undefined = undefined
|
||||
run(() => {
|
||||
componentInput && appContext?.onchange?.()
|
||||
});
|
||||
let s3FileUploadRawMode = $state(false)
|
||||
let s3FilePicker: S3FilePicker | undefined = $state(undefined)
|
||||
</script>
|
||||
|
||||
{#key subFieldType}
|
||||
{#if componentInput?.type === 'static'}
|
||||
{#if fieldType === 'number' || fieldType === 'integer'}
|
||||
<input on:keydown|stopPropagation type="number" bind:value={componentInput.value} />
|
||||
<input onkeydown={stopPropagation(bubble('keydown'))} type="number" bind:value={componentInput.value} />
|
||||
{:else if fieldType === 'textarea'}
|
||||
<textarea use:autosize on:keydown|stopPropagation bind:value={componentInput.value}
|
||||
<textarea use:autosize onkeydown={stopPropagation(bubble('keydown'))} bind:value={componentInput.value}
|
||||
></textarea>
|
||||
{:else if fieldType === 'date'}
|
||||
<input on:keydown|stopPropagation type="date" bind:value={componentInput.value} />
|
||||
<input onkeydown={stopPropagation(bubble('keydown'))} type="date" bind:value={componentInput.value} />
|
||||
{:else if fieldType === 'time'}
|
||||
<input on:keydown|stopPropagation type="time" bind:value={componentInput.value} />
|
||||
<input onkeydown={stopPropagation(bubble('keydown'))} type="time" bind:value={componentInput.value} />
|
||||
{:else if fieldType === 'datetime'}
|
||||
<DateTimeInput bind:value={componentInput.value} />
|
||||
{:else if fieldType === 'boolean'}
|
||||
@@ -60,7 +77,7 @@
|
||||
{#if subFieldType === 'db-table'}
|
||||
<DBTableSelect bind:componentInput {selectOptions} {id} />
|
||||
{:else}
|
||||
<select on:keydown|stopPropagation bind:value={componentInput.value}>
|
||||
<select onkeydown={stopPropagation(bubble('keydown'))} bind:value={componentInput.value}>
|
||||
{#each selectOptions ?? [] as option}
|
||||
{#if typeof option == 'string'}
|
||||
<option value={option}>
|
||||
@@ -114,7 +131,7 @@
|
||||
{#if componentInput?.value && typeof componentInput?.value == 'object' && 'label' in componentInput?.value && (componentInput.value?.['value'] == undefined || typeof componentInput.value?.['value'] == 'string')}
|
||||
<div class="flex flex-col gap-1 w-full">
|
||||
<input
|
||||
on:keydown|stopPropagation
|
||||
onkeydown={stopPropagation(bubble('keydown'))}
|
||||
placeholder="Label"
|
||||
type="text"
|
||||
bind:value={componentInput.value['label']}
|
||||
@@ -257,13 +274,15 @@
|
||||
/>
|
||||
<div class="absolute top-1 right-1">
|
||||
<AgGridWizard bind:value={componentInput.value}>
|
||||
<svelte:fragment slot="trigger">
|
||||
<Button color="light" size="xs2" nonCaptureEvent={true}>
|
||||
<div class="flex flex-row items-center gap-2 text-xs font-normal">
|
||||
<Settings size={16} />
|
||||
</div>
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
{#snippet trigger()}
|
||||
|
||||
<Button color="light" size="xs2" nonCaptureEvent={true}>
|
||||
<div class="flex flex-row items-center gap-2 text-xs font-normal">
|
||||
<Settings size={16} />
|
||||
</div>
|
||||
</Button>
|
||||
|
||||
{/snippet}
|
||||
</AgGridWizard>
|
||||
</div>
|
||||
</div>
|
||||
@@ -279,13 +298,15 @@
|
||||
/>
|
||||
<div class="absolute top-1 right-1">
|
||||
<DBExplorerWizard bind:value={componentInput.value}>
|
||||
<svelte:fragment slot="trigger">
|
||||
<Button color="light" size="xs2" nonCaptureEvent={true}>
|
||||
<div class="flex flex-row items-center gap-2 text-xs font-normal">
|
||||
<Settings size={16} />
|
||||
</div>
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
{#snippet trigger()}
|
||||
|
||||
<Button color="light" size="xs2" nonCaptureEvent={true}>
|
||||
<div class="flex flex-row items-center gap-2 text-xs font-normal">
|
||||
<Settings size={16} />
|
||||
</div>
|
||||
</Button>
|
||||
|
||||
{/snippet}
|
||||
</DBExplorerWizard>
|
||||
</div>
|
||||
</div>
|
||||
@@ -300,13 +321,15 @@
|
||||
/>
|
||||
<div class="absolute top-1 right-1">
|
||||
<TableColumnWizard bind:column={componentInput.value}>
|
||||
<svelte:fragment slot="trigger">
|
||||
<Button color="light" size="xs2" nonCaptureEvent={true}>
|
||||
<div class="flex flex-row items-center gap-2 text-xs font-normal">
|
||||
<Settings size={16} />
|
||||
</div>
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
{#snippet trigger()}
|
||||
|
||||
<Button color="light" size="xs2" nonCaptureEvent={true}>
|
||||
<div class="flex flex-row items-center gap-2 text-xs font-normal">
|
||||
<Settings size={16} />
|
||||
</div>
|
||||
</Button>
|
||||
|
||||
{/snippet}
|
||||
</TableColumnWizard>
|
||||
</div>
|
||||
</div>
|
||||
@@ -321,13 +344,15 @@
|
||||
/>
|
||||
<div class="absolute top-1 right-1">
|
||||
<PlotlyWizard bind:value={componentInput.value} on:remove>
|
||||
<svelte:fragment slot="trigger">
|
||||
<Button color="light" size="xs2" nonCaptureEvent={true}>
|
||||
<div class="flex flex-row items-center gap-2 text-xs font-normal">
|
||||
<Settings size={16} />
|
||||
</div>
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
{#snippet trigger()}
|
||||
|
||||
<Button color="light" size="xs2" nonCaptureEvent={true}>
|
||||
<div class="flex flex-row items-center gap-2 text-xs font-normal">
|
||||
<Settings size={16} />
|
||||
</div>
|
||||
</Button>
|
||||
|
||||
{/snippet}
|
||||
</PlotlyWizard>
|
||||
</div>
|
||||
</div>
|
||||
@@ -342,13 +367,15 @@
|
||||
/>
|
||||
<div class="absolute top-1 right-1">
|
||||
<ChartJSWizard bind:value={componentInput.value} on:remove>
|
||||
<svelte:fragment slot="trigger">
|
||||
<Button color="light" size="xs2" nonCaptureEvent={true}>
|
||||
<div class="flex flex-row items-center gap-2 text-xs font-normal">
|
||||
<Settings size={16} />
|
||||
</div>
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
{#snippet trigger()}
|
||||
|
||||
<Button color="light" size="xs2" nonCaptureEvent={true}>
|
||||
<div class="flex flex-row items-center gap-2 text-xs font-normal">
|
||||
<Settings size={16} />
|
||||
</div>
|
||||
</Button>
|
||||
|
||||
{/snippet}
|
||||
</ChartJSWizard>
|
||||
</div>
|
||||
</div>
|
||||
@@ -364,13 +391,15 @@
|
||||
|
||||
<div class="absolute top-1 right-1">
|
||||
<AgChartWizard bind:value={componentInput.value} on:remove>
|
||||
<svelte:fragment slot="trigger">
|
||||
<Button color="light" size="xs2" nonCaptureEvent={true}>
|
||||
<div class="flex flex-row items-center gap-2 text-xs font-normal">
|
||||
<Settings size={16} />
|
||||
</div>
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
{#snippet trigger()}
|
||||
|
||||
<Button color="light" size="xs2" nonCaptureEvent={true}>
|
||||
<div class="flex flex-row items-center gap-2 text-xs font-normal">
|
||||
<Settings size={16} />
|
||||
</div>
|
||||
</Button>
|
||||
|
||||
{/snippet}
|
||||
</AgChartWizard>
|
||||
</div>
|
||||
</div>
|
||||
@@ -401,7 +430,7 @@
|
||||
<textarea
|
||||
rows="1"
|
||||
use:autosize
|
||||
on:keydown|stopPropagation
|
||||
onkeydown={stopPropagation(bubble('keydown'))}
|
||||
placeholder={placeholder ?? 'Static value'}
|
||||
bind:value={componentInput.value}
|
||||
class="!pr-12"
|
||||
|
||||
+6
-2
@@ -4,11 +4,15 @@
|
||||
import { allItems } from '$lib/components/apps/utils'
|
||||
import { getContext } from 'svelte'
|
||||
|
||||
export let componentInput: StaticInput<{ id: string; index: number }>
|
||||
interface Props {
|
||||
componentInput: StaticInput<{ id: string; index: number }>
|
||||
}
|
||||
|
||||
let { componentInput = $bindable() }: Props = $props()
|
||||
|
||||
const { app } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
const tabComponents = allItems($app.grid, $app.subgrids).filter(
|
||||
const tabComponents = allItems(app.grid, app.subgrids).filter(
|
||||
(component) =>
|
||||
component.data.type === 'tabscomponent' ||
|
||||
component.data.type === 'conditionalwrapper' ||
|
||||
|
||||
+40
-24
@@ -4,24 +4,33 @@
|
||||
import { classNames, itemsExists } from '$lib/utils'
|
||||
import { Plus, X } from 'lucide-svelte'
|
||||
import { Button } from '$lib/components/common'
|
||||
import { getContext } from 'svelte'
|
||||
import { getContext, untrack } from 'svelte'
|
||||
import type { App, AppViewerContext, InlineScript } from '$lib/components/apps/types'
|
||||
import Tooltip from '$lib/components/Tooltip.svelte'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
import { getAllGridItems } from '../../../appUtils'
|
||||
|
||||
export let triggerEvents: string[] = []
|
||||
export let inlineScript: InlineScript | undefined = undefined
|
||||
export let isFrontend: boolean = false
|
||||
export let dependencies: string[] = []
|
||||
export let shoudlDisplayChangeEvents: boolean = false
|
||||
export let id: string
|
||||
interface Props {
|
||||
triggerEvents?: string[]
|
||||
inlineScript?: InlineScript | undefined
|
||||
isFrontend?: boolean
|
||||
dependencies?: string[]
|
||||
shoudlDisplayChangeEvents?: boolean
|
||||
id: string
|
||||
}
|
||||
|
||||
let {
|
||||
triggerEvents = [],
|
||||
inlineScript = $bindable(undefined),
|
||||
isFrontend = false,
|
||||
dependencies = [],
|
||||
shoudlDisplayChangeEvents = false,
|
||||
id
|
||||
}: Props = $props()
|
||||
|
||||
const { connectingInput, app, stateId } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let onSuccessEvents: string[] = []
|
||||
|
||||
$: computeOnSuccessEvents($app, id)
|
||||
let onSuccessEvents: string[] = $state([])
|
||||
|
||||
function computeOnSuccessEvents(app: App, _id: string) {
|
||||
const nr: string[] = []
|
||||
@@ -47,16 +56,6 @@
|
||||
})
|
||||
onSuccessEvents = nr
|
||||
}
|
||||
$: changeEvents = isFrontend
|
||||
? inlineScript?.refreshOn
|
||||
? inlineScript.refreshOn.map((x) => `${x.id}.${x.key}`)
|
||||
: []
|
||||
: dependencies
|
||||
|
||||
$: hasNoTriggers =
|
||||
triggerEvents.length === 0 &&
|
||||
(changeEvents.length === 0 || !shoudlDisplayChangeEvents) &&
|
||||
onSuccessEvents.length == 0
|
||||
|
||||
const badgeClass = 'inline-flex items-center rounded-md px-2 py-0.5 text-xs font-medium border'
|
||||
const colors = {
|
||||
@@ -84,9 +83,26 @@
|
||||
} else if (!itemsExists(inlineScript.refreshOn, refresh)) {
|
||||
inlineScript.refreshOn = [...inlineScript.refreshOn, refresh]
|
||||
}
|
||||
inlineScript = inlineScript
|
||||
$app = $app
|
||||
inlineScript = inlineScript // $app = $app
|
||||
}
|
||||
$effect.pre(() => {
|
||||
;[app, id]
|
||||
untrack(() => {
|
||||
computeOnSuccessEvents(app, id)
|
||||
})
|
||||
})
|
||||
let changeEvents = $derived(
|
||||
isFrontend
|
||||
? inlineScript?.refreshOn
|
||||
? inlineScript.refreshOn.map((x) => `${x.id}.${x.key}`)
|
||||
: []
|
||||
: dependencies
|
||||
)
|
||||
let hasNoTriggers = $derived(
|
||||
triggerEvents.length === 0 &&
|
||||
(changeEvents.length === 0 || !shoudlDisplayChangeEvents) &&
|
||||
onSuccessEvents.length == 0
|
||||
)
|
||||
</script>
|
||||
|
||||
{#if hasNoTriggers}
|
||||
@@ -118,7 +134,7 @@
|
||||
{#if isFrontend}
|
||||
<button
|
||||
class="bg-blue-300 ml-2 p-0.5 rounded-md hover:bg-blue-400 cursor-pointer"
|
||||
on:click={() => {
|
||||
onclick={() => {
|
||||
if (inlineScript?.refreshOn) {
|
||||
inlineScript.refreshOn = inlineScript.refreshOn.filter(
|
||||
(x) => `${x.id}.${x.key}` !== changeEvent
|
||||
@@ -173,7 +189,7 @@
|
||||
'p-0.5 rounded-md hover:bg-blue-400 cursor-pointer !text-2xs text-secondary',
|
||||
badgeClass
|
||||
)}
|
||||
on:click={() => {
|
||||
onclick={() => {
|
||||
if (inlineScript) {
|
||||
if (!itemsExists(inlineScript.refreshOn, suggestion)) {
|
||||
inlineScript.refreshOn = [...(inlineScript.refreshOn ?? []), suggestion]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<script lang="ts" context="module">
|
||||
<script lang="ts" module>
|
||||
import { writable } from 'svelte/store'
|
||||
|
||||
const componentDraggedIdStore = writable<string | undefined>(undefined)
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
import { getContainerHeight } from './utils/container'
|
||||
import { moveItem, getItemById, specifyUndefinedColumns } from './utils/item'
|
||||
import { onMount, createEventDispatcher, getContext } from 'svelte'
|
||||
import { onMount, createEventDispatcher, getContext, untrack } from 'svelte'
|
||||
import { getColumn, throttle } from './utils/other'
|
||||
import MoveResize from './MoveResize.svelte'
|
||||
import type { FilledItem } from './types'
|
||||
@@ -41,33 +41,49 @@
|
||||
|
||||
const { app, worldStore } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
export let items: FilledItem<T>[]
|
||||
export let rowHeight: number = ROW_HEIGHT
|
||||
interface Props {
|
||||
items: FilledItem<T>[]
|
||||
rowHeight?: number
|
||||
gap?: any
|
||||
throttleUpdate?: number
|
||||
throttleResize?: number
|
||||
selectedIds: string[] | undefined
|
||||
allIdsInPath: string[] | undefined
|
||||
containerWidth?: number | undefined
|
||||
scroller?: HTMLElement | undefined
|
||||
sensor?: number
|
||||
root?: boolean
|
||||
parentWidth?: number | undefined
|
||||
disableMove?: boolean
|
||||
children?: import('svelte').Snippet<[any]>
|
||||
}
|
||||
|
||||
export let gap = [ROW_GAP_X, ROW_GAP_Y]
|
||||
export let throttleUpdate = 100
|
||||
export let throttleResize = 100
|
||||
export let selectedIds: string[] | undefined
|
||||
export let allIdsInPath: string[] | undefined
|
||||
export let containerWidth: number | undefined = undefined
|
||||
export let scroller: HTMLElement | undefined = undefined
|
||||
export let sensor = 20
|
||||
export let root: boolean = false
|
||||
export let parentWidth: number | undefined = undefined
|
||||
export let disableMove: boolean = false
|
||||
let {
|
||||
items = $bindable(),
|
||||
rowHeight = ROW_HEIGHT,
|
||||
gap = [ROW_GAP_X, ROW_GAP_Y],
|
||||
throttleUpdate = 100,
|
||||
throttleResize = 100,
|
||||
selectedIds,
|
||||
allIdsInPath,
|
||||
containerWidth = $bindable(undefined),
|
||||
scroller = undefined,
|
||||
sensor = 20,
|
||||
root = false,
|
||||
parentWidth = undefined,
|
||||
disableMove = false,
|
||||
children
|
||||
}: Props = $props()
|
||||
const cols = columnConfiguration
|
||||
|
||||
let getComputedCols: 3 | 12 | undefined =
|
||||
$app.mobileViewOnSmallerScreens == false ? WIDE_GRID_COLUMNS : undefined
|
||||
let container
|
||||
let getComputedCols: 3 | 12 | undefined = $state(
|
||||
app.mobileViewOnSmallerScreens == false ? WIDE_GRID_COLUMNS : undefined
|
||||
)
|
||||
let container = $state() as Element | undefined
|
||||
|
||||
$: [gapX, gapY] = gap
|
||||
|
||||
let xPerPx = 0
|
||||
let xPerPx = $state(0)
|
||||
let yPerPx = rowHeight
|
||||
|
||||
$: containerHeight = getContainerHeight(items, yPerPx, getComputedCols)
|
||||
|
||||
const onResize = throttle(() => {
|
||||
items = specifyUndefinedColumns(items, getComputedCols, cols)
|
||||
dispatch('resize', {
|
||||
@@ -78,7 +94,7 @@
|
||||
})
|
||||
}, throttleUpdate)
|
||||
|
||||
let mounted = false
|
||||
let mounted = $state(false)
|
||||
|
||||
onMount(() => {
|
||||
const sizeObserver = new ResizeObserver((entries) => {
|
||||
@@ -92,7 +108,7 @@
|
||||
return
|
||||
}
|
||||
|
||||
if ($app.mobileViewOnSmallerScreens != false || !getComputedCols) {
|
||||
if (app.mobileViewOnSmallerScreens != false || !getComputedCols) {
|
||||
getComputedCols = getColumn(parentWidth ?? width, cols)
|
||||
}
|
||||
xPerPx = width / getComputedCols!
|
||||
@@ -119,10 +135,9 @@
|
||||
return () => sizeObserver.disconnect()
|
||||
})
|
||||
|
||||
let sortedItems: FilledItem<T>[] = []
|
||||
$: sortedItems = smartCopy(items).sort((a, b) => a.id.localeCompare(b.id))
|
||||
let sortedItems: FilledItem<T>[] = $state([])
|
||||
|
||||
let resizing: boolean = false
|
||||
let resizing: boolean = $state(false)
|
||||
|
||||
function handleKeyUp(event) {
|
||||
if ((event.key === 'Control' || event.key === 'Meta') && $isCtrlOrMetaPressedStore) {
|
||||
@@ -142,9 +157,11 @@
|
||||
? items.map((item) => {
|
||||
return {
|
||||
...item,
|
||||
[getComputedCols as number]: structuredClone(item[getComputedCols as number])
|
||||
[getComputedCols as number]: structuredClone(
|
||||
$state.snapshot(item[getComputedCols as number])
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
: []
|
||||
}
|
||||
const updateMatrix = ({ detail }) => {
|
||||
@@ -236,7 +253,7 @@
|
||||
//let hiddenComponents = writable({})
|
||||
|
||||
let lastDetail: { isPointerUp: false; activate: false; id: string | undefined } | undefined =
|
||||
undefined
|
||||
$state(undefined)
|
||||
const handleRepaint = ({ detail }) => {
|
||||
if (!detail.isPointerUp) {
|
||||
throttleMatrix({ detail })
|
||||
@@ -269,13 +286,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
let moveResizes: Record<string, MoveResize> = {}
|
||||
let shadows: Record<string, { x: number; y: number; w: number; h: number } | undefined> = {}
|
||||
let moveResizes: Record<string, MoveResize> = $state({})
|
||||
let shadows: Record<string, { x: number; y: number; w: number; h: number } | undefined> = $state(
|
||||
{}
|
||||
)
|
||||
|
||||
export function handleMove({ detail }) {
|
||||
Object.entries(moveResizes).forEach(([id, moveResize]) => {
|
||||
if (selectedIds?.includes(id)) {
|
||||
moveResize?.updateMove(structuredClone(detail.cordDiff), detail.eventY)
|
||||
moveResize?.updateMove(structuredClone($state.snapshot(detail.cordDiff)), detail.eventY)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -307,15 +326,15 @@
|
||||
const div = document.getElementById(`component-${$overlappedStore}`)
|
||||
const type = div?.getAttribute('data-componenttype')
|
||||
|
||||
if (!$app.subgrids) {
|
||||
if (!app.subgrids) {
|
||||
return
|
||||
}
|
||||
|
||||
const index = type ? subGridIndexKey(type, $overlappedStore, $worldStore) : 0
|
||||
|
||||
items = $app.subgrids[`${$overlappedStore}-${index}`] ?? []
|
||||
items = app.subgrids[`${$overlappedStore}-${index}`] ?? []
|
||||
} else {
|
||||
items = $app.grid ?? []
|
||||
items = app.grid ?? []
|
||||
}
|
||||
|
||||
if (!draggedItem) {
|
||||
@@ -346,7 +365,7 @@
|
||||
|
||||
export function handleInitMove(id: string) {
|
||||
$componentDraggedIdStore = id
|
||||
$componentDraggedParentIdStore = findGridItemParentGrid($app, id)?.split('-')[0] ?? undefined
|
||||
$componentDraggedParentIdStore = findGridItemParentGrid(app, id)?.split('-')[0] ?? undefined
|
||||
|
||||
Object.entries(moveResizes).forEach(([id, moveResize]) => {
|
||||
if (selectedIds?.includes(id)) {
|
||||
@@ -354,16 +373,24 @@
|
||||
}
|
||||
})
|
||||
}
|
||||
let [gapX, gapY] = $derived(gap)
|
||||
let containerHeight = $derived(getContainerHeight(items, yPerPx, getComputedCols))
|
||||
$effect(() => {
|
||||
items
|
||||
untrack(() => {
|
||||
sortedItems = smartCopy(items).sort((a, b) => a.id.localeCompare(b.id))
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<svelte:window
|
||||
on:focus={() => {
|
||||
onfocus={() => {
|
||||
if ($isCtrlOrMetaPressedStore) {
|
||||
$isCtrlOrMetaPressedStore = false
|
||||
}
|
||||
}}
|
||||
on:keydown={handleKeyDown}
|
||||
on:keyup={handleKeyUp}
|
||||
onkeydown={handleKeyDown}
|
||||
onkeyup={handleKeyUp}
|
||||
/>
|
||||
|
||||
<div
|
||||
@@ -482,7 +509,7 @@
|
||||
width={xPerPx == 0
|
||||
? 0
|
||||
: Math.min(getComputedCols, item[getComputedCols] && item[getComputedCols].w) * xPerPx -
|
||||
gapX * 2}
|
||||
gapX * 2}
|
||||
height={(item[getComputedCols] && item[getComputedCols].h) * yPerPx - gapY * 2}
|
||||
top={(item[getComputedCols] && item[getComputedCols].y) * yPerPx + gapY}
|
||||
left={(item[getComputedCols] && item[getComputedCols].x) * xPerPx + gapX}
|
||||
@@ -499,13 +526,13 @@
|
||||
{disableMove}
|
||||
>
|
||||
{#if item[getComputedCols]}
|
||||
<slot
|
||||
dataItem={item}
|
||||
hidden={false}
|
||||
overlapped={$overlappedStore}
|
||||
moveMode={$isCtrlOrMetaPressedStore ? 'insert' : 'move'}
|
||||
componentDraggedId={$componentDraggedIdStore}
|
||||
/>
|
||||
{@render children?.({
|
||||
dataItem: item,
|
||||
hidden: false,
|
||||
overlapped: $overlappedStore,
|
||||
moveMode: $isCtrlOrMetaPressedStore ? 'insert' : 'move',
|
||||
componentDraggedId: $componentDraggedIdStore
|
||||
})}
|
||||
{/if}
|
||||
</MoveResize>
|
||||
{/if}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { preventDefault, stopPropagation } from 'svelte/legacy'
|
||||
|
||||
import { createEventDispatcher, getContext, onMount } from 'svelte'
|
||||
import type { AppEditorContext, AppViewerContext } from '../types'
|
||||
import { writable } from 'svelte/store'
|
||||
@@ -13,33 +15,57 @@
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
export let sensor
|
||||
export let width
|
||||
export let height
|
||||
export let left
|
||||
export let top
|
||||
interface Props {
|
||||
sensor: any
|
||||
width: any
|
||||
height: any
|
||||
left: any
|
||||
top: any
|
||||
id: any
|
||||
container: any
|
||||
xPerPx: any
|
||||
yPerPx: any
|
||||
gapX: any
|
||||
gapY: any
|
||||
item: any
|
||||
cols: any
|
||||
nativeContainer: any
|
||||
onTop: any
|
||||
shadow?: { x: number; y: number; w: number; h: number } | undefined
|
||||
overlapped?: string | undefined
|
||||
moveMode?: 'move' | 'insert'
|
||||
type?: string | undefined
|
||||
fakeShadow?: GridShadow | undefined
|
||||
disableMove?: boolean
|
||||
mounted?: boolean
|
||||
children?: import('svelte').Snippet
|
||||
}
|
||||
|
||||
export let id
|
||||
export let container
|
||||
|
||||
export let xPerPx
|
||||
export let yPerPx
|
||||
|
||||
export let gapX
|
||||
export let gapY
|
||||
export let item
|
||||
|
||||
export let cols
|
||||
|
||||
export let nativeContainer
|
||||
export let onTop
|
||||
export let shadow: { x: number; y: number; w: number; h: number } | undefined = undefined
|
||||
export let overlapped: string | undefined = undefined
|
||||
export let moveMode: 'move' | 'insert' = 'move'
|
||||
export let type: string | undefined = undefined
|
||||
export let fakeShadow: GridShadow | undefined = undefined
|
||||
export let disableMove: boolean = true
|
||||
export let mounted: boolean = false
|
||||
let {
|
||||
sensor,
|
||||
width,
|
||||
height,
|
||||
left,
|
||||
top,
|
||||
id,
|
||||
container,
|
||||
xPerPx,
|
||||
yPerPx,
|
||||
gapX,
|
||||
gapY,
|
||||
item,
|
||||
cols,
|
||||
nativeContainer,
|
||||
onTop,
|
||||
shadow = $bindable(undefined),
|
||||
overlapped = undefined,
|
||||
moveMode = 'move',
|
||||
type = undefined,
|
||||
fakeShadow = undefined,
|
||||
disableMove = true,
|
||||
mounted = false,
|
||||
children
|
||||
}: Props = $props()
|
||||
|
||||
const ctx = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { mode, app } = getContext<AppViewerContext>('AppViewerContext')
|
||||
@@ -47,9 +73,9 @@
|
||||
const scale = ctx ? ctx.scale : writable(100)
|
||||
|
||||
const divId = `component-${id}`
|
||||
let shadowElement
|
||||
let shadowElement = $state(undefined) as HTMLElement | undefined
|
||||
|
||||
let active = false
|
||||
let active = $state(false)
|
||||
|
||||
let initX, initY
|
||||
|
||||
@@ -58,10 +84,10 @@
|
||||
y: 0
|
||||
}
|
||||
|
||||
let cordDiff = { x: 0, y: 0 }
|
||||
let cordDiff = $state({ x: 0, y: 0 })
|
||||
|
||||
let newSize = { width, height }
|
||||
let trans = false
|
||||
let newSize = $state({ width, height })
|
||||
let trans = $state(false)
|
||||
|
||||
let anima
|
||||
|
||||
@@ -114,6 +140,7 @@
|
||||
shadowBound = irect
|
||||
}
|
||||
|
||||
if (!rect) return
|
||||
const xdragBound = rect.left + cordDiff.x
|
||||
const ydragBound = rect.top + cordDiff.y
|
||||
|
||||
@@ -142,7 +169,7 @@
|
||||
// Autoscroll
|
||||
let _scrollTop = 0
|
||||
let containerFrame
|
||||
let rect
|
||||
let rect = $state() as { top: number; left: number } | undefined
|
||||
let scrollElement
|
||||
|
||||
const getContainerFrame = (element) => {
|
||||
@@ -317,7 +344,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
let element: HTMLElement | undefined = undefined
|
||||
let element: HTMLElement | undefined = $state(undefined)
|
||||
|
||||
function computeShadow(clientX: number, clientY: number) {
|
||||
const elementsAtPoint = document.elementsFromPoint(clientX, clientY)
|
||||
@@ -384,7 +411,7 @@
|
||||
return
|
||||
}
|
||||
|
||||
const parent = findGridItemParentGrid($app, id)
|
||||
const parent = findGridItemParentGrid(app, id)
|
||||
|
||||
if (overlapped && (overlapped === parent || parent?.startsWith(overlapped))) {
|
||||
return
|
||||
@@ -469,7 +496,7 @@
|
||||
return true
|
||||
}
|
||||
|
||||
const parent = findGridItemParentGrid($app, id)
|
||||
const parent = findGridItemParentGrid(app, id)
|
||||
|
||||
if (parent === undefined) {
|
||||
return overlapped === undefined
|
||||
@@ -481,12 +508,12 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<div
|
||||
bind:this={element}
|
||||
draggable="false"
|
||||
on:pointerdown|stopPropagation|preventDefault={pointerdown}
|
||||
onpointerdown={(e) => stopPropagation(preventDefault((_) => pointerdown(e)))}
|
||||
id={divId}
|
||||
class="svlt-grid-item"
|
||||
data-iscontainer={type ? isContainer(type) : false}
|
||||
@@ -495,30 +522,26 @@
|
||||
style="width: {xPerPx == 0 ? 0 : active ? newSize.width : width}px; height:{xPerPx == 0
|
||||
? 0
|
||||
: active
|
||||
? newSize.height
|
||||
: height}px;
|
||||
? newSize.height
|
||||
: height}px;
|
||||
{xPerPx == 0 ? 'overflow: hidden;' : ''}
|
||||
{onTop ? 'z-index: 1000;' : ''}
|
||||
|
||||
{active && rect
|
||||
? `transform: translate(${cordDiff.x}px, ${cordDiff.y}px);top:${rect.top}px;left:${rect.left}px;z-index:10000;`
|
||||
: trans
|
||||
? `transform: translate(${cordDiff.x}px, ${cordDiff.y}px); position:absolute; transition: width 0.2s, height 0.2s;`
|
||||
: `${
|
||||
xPerPx > 0 && mounted ? 'transition: transform 0.1s, opacity 0.1s;' : ''
|
||||
} transform: translate(${left}px, ${top}px); `} "
|
||||
? `transform: translate(${cordDiff.x}px, ${cordDiff.y}px); position:absolute; transition: width 0.2s, height 0.2s;`
|
||||
: `${
|
||||
xPerPx > 0 && mounted ? 'transition: transform 0.1s, opacity 0.1s;' : ''
|
||||
} transform: translate(${left}px, ${top}px); `} "
|
||||
>
|
||||
<slot />
|
||||
{@render children?.()}
|
||||
{#if moveMode === 'move' && !disableMove}
|
||||
<div
|
||||
class="svlt-grid-resizer-bottom"
|
||||
on:pointerdown={(e) => resizePointerDown(e, 'vertical')}
|
||||
<div class="svlt-grid-resizer-bottom" onpointerdown={(e) => resizePointerDown(e, 'vertical')}
|
||||
></div>
|
||||
<div
|
||||
class="svlt-grid-resizer-side"
|
||||
on:pointerdown={(e) => resizePointerDown(e, 'horizontal')}
|
||||
<div class="svlt-grid-resizer-side" onpointerdown={(e) => resizePointerDown(e, 'horizontal')}
|
||||
></div>
|
||||
<div class="svlt-grid-resizer" on:pointerdown={(e) => resizePointerDown(e, 'both')}></div>
|
||||
<div class="svlt-grid-resizer" onpointerdown={(e) => resizePointerDown(e, 'both')}></div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ import type {
|
||||
TemplateV2AppInput,
|
||||
UploadAppInput,
|
||||
UploadS3AppInput,
|
||||
UserAppInput,
|
||||
UserAppInput
|
||||
} from './inputType'
|
||||
import type { World } from './rx'
|
||||
import type { FilledItem } from './svelte-grid/types'
|
||||
@@ -141,13 +141,13 @@ export type HiddenRunnable = {
|
||||
|
||||
export type AppTheme =
|
||||
| {
|
||||
type: 'path'
|
||||
path: string
|
||||
}
|
||||
type: 'path'
|
||||
path: string
|
||||
}
|
||||
| {
|
||||
type: 'inlined'
|
||||
css: string
|
||||
}
|
||||
type: 'inlined'
|
||||
css: string
|
||||
}
|
||||
|
||||
export type App = {
|
||||
grid: GridItem[]
|
||||
@@ -209,7 +209,7 @@ export type JobById = {
|
||||
|
||||
export type AppViewerContext = {
|
||||
worldStore: Writable<World>
|
||||
app: Writable<App>
|
||||
app: App
|
||||
summary: Writable<string>
|
||||
initialized: Writable<{
|
||||
initializedComponents: string[]
|
||||
|
||||
@@ -1,12 +1,26 @@
|
||||
<script lang="ts">
|
||||
import AnimatedButtonInner from './AnimatedButtonInner.svelte'
|
||||
export let marginWidth = '2px'
|
||||
export let animationDuration = '2s'
|
||||
export let baseRadius = '4px'
|
||||
export let animate = true
|
||||
export let wrapperClasses = ''
|
||||
export let ringColor = 'transparent'
|
||||
export let darkMode = false
|
||||
interface Props {
|
||||
marginWidth?: string
|
||||
animationDuration?: string
|
||||
baseRadius?: string
|
||||
animate?: boolean
|
||||
wrapperClasses?: string
|
||||
ringColor?: string
|
||||
darkMode?: boolean
|
||||
children?: import('svelte').Snippet
|
||||
}
|
||||
|
||||
let {
|
||||
marginWidth = '2px',
|
||||
animationDuration = '2s',
|
||||
baseRadius = '4px',
|
||||
animate = true,
|
||||
wrapperClasses = '',
|
||||
ringColor = 'transparent',
|
||||
darkMode = false,
|
||||
children
|
||||
}: Props = $props()
|
||||
</script>
|
||||
|
||||
{#if animate}
|
||||
@@ -19,10 +33,10 @@
|
||||
{ringColor}
|
||||
{darkMode}
|
||||
>
|
||||
<slot />
|
||||
{@render children?.()}
|
||||
</AnimatedButtonInner>
|
||||
{:else}
|
||||
<div class={wrapperClasses}>
|
||||
<slot />
|
||||
{@render children?.()}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -100,7 +100,7 @@
|
||||
placeholder="Inline script name"
|
||||
class="!text-xs !rounded-sm !shadow-none"
|
||||
on:keyup={() => {
|
||||
// $app = $app
|
||||
// // $app = $app
|
||||
// if (stateId) {
|
||||
// $stateId++
|
||||
// }
|
||||
@@ -197,7 +197,7 @@
|
||||
syncFields()
|
||||
}
|
||||
}
|
||||
// $app = $app
|
||||
// // $app = $app
|
||||
}}
|
||||
args={Object.entries(fields ?? {}).reduce((acc, [key, obj]) => {
|
||||
acc[key] = obj.type === 'static' ? obj.value : undefined
|
||||
|
||||
@@ -9,8 +9,12 @@
|
||||
import type { Runnable } from '../apps/inputType'
|
||||
import { getNextId } from '$lib/components/flows/idUtils'
|
||||
|
||||
export let selectedRunnable: string | undefined
|
||||
export let runnables: Writable<Record<string, Runnable>>
|
||||
interface Props {
|
||||
selectedRunnable: string | undefined
|
||||
runnables: Writable<Record<string, Runnable>>
|
||||
}
|
||||
|
||||
let { selectedRunnable = $bindable(), runnables }: Props = $props()
|
||||
|
||||
function createBackgroundScript() {
|
||||
const nid = getNextId(Object.keys($runnables ?? {}))
|
||||
@@ -23,7 +27,6 @@
|
||||
}
|
||||
return r
|
||||
})
|
||||
console.log('BAR 2')
|
||||
selectedRunnable = nid
|
||||
}
|
||||
|
||||
@@ -31,7 +34,7 @@
|
||||
</script>
|
||||
|
||||
<PanelSection title="Backend Runnables" id="app-editor-runnable-panel">
|
||||
<svelte:fragment slot="action">
|
||||
{#snippet action()}
|
||||
<div class="flex flex-row gap-1">
|
||||
<HideButton
|
||||
direction="bottom"
|
||||
@@ -55,7 +58,7 @@
|
||||
<Plus size={14} class="!text-primary" />
|
||||
</Button>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
{/snippet}
|
||||
<div class="w-full flex flex-col gap-6 py-1">
|
||||
<div>
|
||||
<div class="flex flex-col gap-1 w-full">
|
||||
@@ -68,7 +71,7 @@
|
||||
{selectedRunnable === id
|
||||
? 'border-blue-500 bg-blue-100 dark:bg-frost-900/50'
|
||||
: 'hover:bg-blue-50 dark:hover:bg-frost-900/50'}"
|
||||
on:click={() => (selectedRunnable = id)}
|
||||
onclick={() => (selectedRunnable = id)}
|
||||
>
|
||||
<span class="text-2xs truncate">{runnable?.name}</span>
|
||||
<Badge color="indigo">{id}</Badge>
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
<script lang="ts">
|
||||
import { run } from 'svelte/legacy'
|
||||
|
||||
import { addWhitespaceBeforeCapitals, capitalize, classNames } from '$lib/utils'
|
||||
import Tooltip from '$lib/components/Tooltip.svelte'
|
||||
|
||||
@@ -54,7 +52,7 @@
|
||||
markdownTooltip = undefined
|
||||
}: Props = $props()
|
||||
|
||||
run(() => {
|
||||
$effect(() => {
|
||||
if (componentInput == undefined) {
|
||||
//@ts-ignore
|
||||
componentInput = {
|
||||
|
||||
@@ -14,10 +14,14 @@
|
||||
} from '../utils'
|
||||
import { updateProgress } from '$lib/tutorialUtils'
|
||||
|
||||
export let name: string
|
||||
export let index: number
|
||||
interface Props {
|
||||
name: string
|
||||
index: number
|
||||
}
|
||||
|
||||
let tutorial: Tutorial | undefined = undefined
|
||||
let { name, index }: Props = $props()
|
||||
|
||||
let tutorial: Tutorial | undefined = $state(undefined)
|
||||
|
||||
const { app, selectedComponent, focusedGrid, connectingInput } =
|
||||
getContext<AppViewerContext>('AppViewerContext')
|
||||
@@ -28,16 +32,15 @@
|
||||
}
|
||||
|
||||
function addComponent(appComponentType: TypedComponent['type']): void {
|
||||
push(history, $app)
|
||||
push(history, app)
|
||||
|
||||
const id = insertNewGridItem(
|
||||
$app,
|
||||
app,
|
||||
appComponentFromType(appComponentType) as (id: string) => AppComponent,
|
||||
$focusedGrid
|
||||
)
|
||||
|
||||
$selectedComponent = [id]
|
||||
$app = $app
|
||||
$selectedComponent = [id] // $app = $app
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -47,7 +50,7 @@
|
||||
{name}
|
||||
on:error
|
||||
on:skipAll
|
||||
tainted={isAppTainted($app)}
|
||||
tainted={isAppTainted(app)}
|
||||
getSteps={(driver) => {
|
||||
const steps = [
|
||||
{
|
||||
@@ -186,7 +189,7 @@
|
||||
setTimeout(() => {
|
||||
if ($selectedComponent?.[0]) {
|
||||
updateInlineRunnableCode(
|
||||
$app,
|
||||
app,
|
||||
$selectedComponent[0],
|
||||
`export async function main(x: string) {
|
||||
return x?.toLocaleUpperCase();
|
||||
@@ -265,9 +268,7 @@
|
||||
description:
|
||||
'We can now type in the text input and see the result in the display component',
|
||||
onNextClick: () => {
|
||||
connectInlineRunnableInputToComponentOutput($app, 'e', 'x', 'd', 'result', 'integer')
|
||||
|
||||
$app = $app
|
||||
connectInlineRunnableInputToComponentOutput(app, 'e', 'x', 'd', 'result', 'integer') // $app = $app
|
||||
|
||||
updateProgress(7)
|
||||
|
||||
|
||||
@@ -8,10 +8,14 @@
|
||||
import { clickButtonBySelector } from '../utils'
|
||||
import { updateProgress } from '$lib/tutorialUtils'
|
||||
|
||||
export let name: string
|
||||
export let index: number
|
||||
interface Props {
|
||||
name: string
|
||||
index: number
|
||||
}
|
||||
|
||||
let tutorial: Tutorial | undefined = undefined
|
||||
let { name, index }: Props = $props()
|
||||
|
||||
let tutorial: Tutorial | undefined = $state(undefined)
|
||||
|
||||
const { app, selectedComponent, focusedGrid } = getContext<AppViewerContext>('AppViewerContext')
|
||||
const { history } = getContext<AppEditorContext>('AppEditorContext')
|
||||
@@ -21,16 +25,15 @@
|
||||
}
|
||||
|
||||
function addComponent(): void {
|
||||
push(history, $app)
|
||||
push(history, app)
|
||||
|
||||
const id = insertNewGridItem(
|
||||
$app,
|
||||
app,
|
||||
appComponentFromType('textcomponent') as (id: string) => AppComponent,
|
||||
$focusedGrid
|
||||
)
|
||||
|
||||
$selectedComponent = [id]
|
||||
$app = $app
|
||||
$selectedComponent = [id] // $app = $app
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher, getContext } from 'svelte'
|
||||
import { createEventDispatcher, getContext, untrack } from 'svelte'
|
||||
import Popover from '$lib/components/meltComponents/Popover.svelte'
|
||||
import Label from '../Label.svelte'
|
||||
import { offset, flip, shift } from 'svelte-floating-ui/dom'
|
||||
@@ -16,21 +16,32 @@
|
||||
type: 'bar' | 'scatter' | 'line' | 'area' | 'range-bar' | 'range-area'
|
||||
}
|
||||
|
||||
let component: GridItem | undefined = undefined
|
||||
let component = $state(undefined) as GridItem | undefined
|
||||
|
||||
$: if (component === undefined && $selectedComponent && $app) {
|
||||
component = findGridItem($app, $selectedComponent[0])
|
||||
$effect.pre(() => {
|
||||
if (component === undefined && $selectedComponent && untrack(() => app)) {
|
||||
untrack(() => {
|
||||
component = findGridItem(app, $selectedComponent[0])
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
let isEE = $derived(component?.data.type === 'agchartscomponentee')
|
||||
|
||||
interface Props {
|
||||
value?: Dataset | undefined
|
||||
trigger?: import('svelte').Snippet
|
||||
}
|
||||
|
||||
$: isEE = component?.data.type === 'agchartscomponentee'
|
||||
|
||||
export let value: Dataset | undefined = undefined
|
||||
let { value = $bindable(undefined), trigger }: Props = $props()
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
function removeDataset() {
|
||||
dispatch('remove')
|
||||
}
|
||||
|
||||
const trigger_render = $derived(trigger)
|
||||
</script>
|
||||
|
||||
<Popover
|
||||
@@ -41,10 +52,10 @@
|
||||
}}
|
||||
closeOnOtherPopoverOpen
|
||||
>
|
||||
<svelte:fragment slot="trigger">
|
||||
<slot name="trigger" />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="content">
|
||||
{#snippet trigger()}
|
||||
{@render trigger_render?.()}
|
||||
{/snippet}
|
||||
{#snippet content()}
|
||||
{#if value}
|
||||
<div class="flex flex-col w-96 gap-4 p-4 max-h-[70vh] overflow-y-auto">
|
||||
<Label label="Name">
|
||||
@@ -66,5 +77,5 @@
|
||||
<Button color="red" size="xs" on:click={removeDataset}>Remove dataset</Button>
|
||||
</div>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
{/snippet}
|
||||
</Popover>
|
||||
|
||||
@@ -29,7 +29,12 @@
|
||||
cellRendererType: 'text' | 'badge' | 'link'
|
||||
}
|
||||
|
||||
export let value: Column | undefined
|
||||
interface Props {
|
||||
value: Column | undefined
|
||||
trigger?: import('svelte').Snippet
|
||||
}
|
||||
|
||||
let { value = $bindable(), trigger }: Props = $props()
|
||||
|
||||
const presets = [
|
||||
{
|
||||
@@ -82,11 +87,15 @@
|
||||
}
|
||||
]
|
||||
|
||||
let renderCount = 0
|
||||
let renderCount = $state(0)
|
||||
|
||||
$: if (value && value.cellRendererType === null) {
|
||||
value.cellRendererType = 'text'
|
||||
}
|
||||
$effect.pre(() => {
|
||||
if (value && value.cellRendererType === null) {
|
||||
value.cellRendererType = 'text'
|
||||
}
|
||||
})
|
||||
|
||||
const trigger_render = $derived(trigger)
|
||||
</script>
|
||||
|
||||
<Popover
|
||||
@@ -97,10 +106,10 @@
|
||||
}}
|
||||
closeOnOtherPopoverOpen
|
||||
>
|
||||
<svelte:fragment slot="trigger">
|
||||
<slot name="trigger" />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="content">
|
||||
{#snippet trigger()}
|
||||
{@render trigger_render?.()}
|
||||
{/snippet}
|
||||
{#snippet content()}
|
||||
{#if value}
|
||||
<div class="flex flex-col w-96 p-4 gap-4 max-h-[70vh] overflow-y-auto">
|
||||
<span class="text-sm mb-2 leading-6 font-semibold">
|
||||
@@ -132,7 +141,7 @@
|
||||
</Label>
|
||||
|
||||
<Label label="Flex">
|
||||
<svelte:fragment slot="header">
|
||||
{#snippet header()}
|
||||
<Tooltip
|
||||
documentationLink="https://www.ag-grid.com/javascript-data-grid/column-sizing/#column-flex"
|
||||
>
|
||||
@@ -146,7 +155,7 @@
|
||||
The column with flex: 2 has twice the size with flex: 1. So final sizes will be:
|
||||
150px, 100px, 200px.
|
||||
</Tooltip>
|
||||
</svelte:fragment>
|
||||
{/snippet}
|
||||
|
||||
<input type="range" step="1" bind:value={value.flex} min={1} max={12} />
|
||||
<div class="text-xs">{value.flex}</div>
|
||||
@@ -164,7 +173,7 @@
|
||||
</Label>
|
||||
|
||||
<Label label="Value formatter">
|
||||
<svelte:fragment slot="header">
|
||||
{#snippet header()}
|
||||
<Tooltip
|
||||
documentationLink="https://www.ag-grid.com/javascript-data-grid/value-formatters/"
|
||||
>
|
||||
@@ -172,8 +181,8 @@
|
||||
one type (e.g. numeric) but needs to be converted for human reading (e.g. putting in
|
||||
currency symbols and number formatting).
|
||||
</Tooltip>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="action">
|
||||
{/snippet}
|
||||
{#snippet action()}
|
||||
<Button
|
||||
size="xs"
|
||||
color="light"
|
||||
@@ -186,21 +195,20 @@
|
||||
>
|
||||
Clear
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
{/snippet}
|
||||
</Label>
|
||||
<div>
|
||||
{#key renderCount}
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="relative">
|
||||
{#if !presets.find((preset) => preset.value === value?.valueFormatter)}
|
||||
<div
|
||||
class="z-50 absolute bg-opacity-50 bg-surface top-0 left-0 bottom-0 right-0"
|
||||
<div class="z-50 absolute bg-opacity-50 bg-surface top-0 left-0 bottom-0 right-0"
|
||||
></div>
|
||||
{/if}
|
||||
<div class="text-xs font-semibold">Presets</div>
|
||||
<select
|
||||
bind:value={value.valueFormatter}
|
||||
on:change={() => {
|
||||
onchange={() => {
|
||||
renderCount++
|
||||
}}
|
||||
placeholder="Code"
|
||||
@@ -231,12 +239,12 @@
|
||||
</Label>
|
||||
|
||||
<Label label="Filter">
|
||||
<svelte:fragment slot="header">
|
||||
{#snippet header()}
|
||||
<Tooltip documentationLink="https://www.ag-grid.com/javascript-data-grid/filtering/">
|
||||
Filtering allows you to limit the rows displayed in your grid to those that match
|
||||
criteria you specify.
|
||||
</Tooltip>
|
||||
</svelte:fragment>
|
||||
{/snippet}
|
||||
<Toggle
|
||||
on:pointerdown={(e) => {
|
||||
e?.stopPropagation()
|
||||
@@ -248,36 +256,36 @@
|
||||
</Label>
|
||||
|
||||
<!--
|
||||
EE only
|
||||
EE only
|
||||
|
||||
<Label label="Aggregation function">
|
||||
<SimpleEditor autoHeight lang="javascript" bind:code={value.aggFunc} />
|
||||
</Label>
|
||||
<Label label="Aggregation function">
|
||||
<SimpleEditor autoHeight lang="javascript" bind:code={value.aggFunc} />
|
||||
</Label>
|
||||
|
||||
<Label label="Pivot">
|
||||
<Toggle bind:checked={value.pivot} size="xs" />
|
||||
</Label>
|
||||
<Label label="Pivot">
|
||||
<Toggle bind:checked={value.pivot} size="xs" />
|
||||
</Label>
|
||||
|
||||
<Label label="Pivot index">
|
||||
<input type="number" placeholder="pivot index" bind:value={value.pivotIndex} />
|
||||
</Label>
|
||||
<Label label="Pivot index">
|
||||
<input type="number" placeholder="pivot index" bind:value={value.pivotIndex} />
|
||||
</Label>
|
||||
|
||||
<Label label="Pinned">
|
||||
<select bind:value={value.pinned}>
|
||||
<option value={null}>None</option>
|
||||
<option value="left">Left</option>
|
||||
<option value="right">Right</option>
|
||||
</select>
|
||||
</Label>
|
||||
<Label label="Pinned">
|
||||
<select bind:value={value.pinned}>
|
||||
<option value={null}>None</option>
|
||||
<option value="left">Left</option>
|
||||
<option value="right">Right</option>
|
||||
</select>
|
||||
</Label>
|
||||
|
||||
<Label label="Row group">
|
||||
<Toggle bind:checked={value.rowGroup} size="xs" />
|
||||
</Label>
|
||||
<Label label="Row group">
|
||||
<Toggle bind:checked={value.rowGroup} size="xs" />
|
||||
</Label>
|
||||
|
||||
<Label label="Row group index">
|
||||
<input type="number" placeholder="row group index" bind:value={value.rowGroupIndex} />
|
||||
</Label>
|
||||
-->
|
||||
<Label label="Row group index">
|
||||
<input type="number" placeholder="row group index" bind:value={value.rowGroupIndex} />
|
||||
</Label>
|
||||
-->
|
||||
|
||||
<Label label="Type">
|
||||
<select bind:value={value.cellRendererType}>
|
||||
@@ -302,5 +310,5 @@
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
{/snippet}
|
||||
</Popover>
|
||||
|
||||
@@ -13,7 +13,12 @@
|
||||
name: string
|
||||
}
|
||||
|
||||
export let value: Dataset | undefined = undefined
|
||||
interface Props {
|
||||
value?: Dataset | undefined
|
||||
trigger?: import('svelte').Snippet
|
||||
}
|
||||
|
||||
let { value = $bindable(undefined), trigger: trigger_render }: Props = $props()
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@@ -30,10 +35,10 @@
|
||||
}}
|
||||
closeOnOtherPopoverOpen
|
||||
>
|
||||
<svelte:fragment slot="trigger">
|
||||
<slot name="trigger" />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="content">
|
||||
{#snippet trigger()}
|
||||
{@render trigger_render?.()}
|
||||
{/snippet}
|
||||
{#snippet content()}
|
||||
{#if value}
|
||||
<div class="flex flex-col w-96 p-4 gap-4 max-h-[70vh] overflow-y-auto">
|
||||
<Label label="Name">
|
||||
@@ -61,5 +66,5 @@
|
||||
<Button color="red" size="xs" on:click={removeDataset}>Remove dataset</Button>
|
||||
</div>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
{/snippet}
|
||||
</Popover>
|
||||
|
||||
@@ -11,9 +11,13 @@
|
||||
import { ColumnIdentity, type ColumnDef } from '../apps/components/display/dbtable/utils'
|
||||
import { offset, flip, shift } from 'svelte-floating-ui/dom'
|
||||
|
||||
export let value: ColumnDef | undefined
|
||||
|
||||
import Alert from '../common/alert/Alert.svelte'
|
||||
interface Props {
|
||||
value: ColumnDef | undefined
|
||||
trigger?: import('svelte').Snippet
|
||||
}
|
||||
|
||||
let { value = $bindable(), trigger: trigger_render }: Props = $props()
|
||||
|
||||
const presets = [
|
||||
{
|
||||
@@ -71,7 +75,7 @@
|
||||
}
|
||||
]
|
||||
|
||||
let renderCount = 0
|
||||
let renderCount = $state(0)
|
||||
|
||||
function computeWarning(columnMetadata, value) {
|
||||
if (columnMetadata?.isnullable === 'NO' && !columnMetadata?.defaultvalue) {
|
||||
@@ -118,7 +122,7 @@
|
||||
return null
|
||||
}
|
||||
|
||||
$: warning = computeWarning(value, value)
|
||||
let warning = $derived(computeWarning(value, value))
|
||||
</script>
|
||||
|
||||
<Popover
|
||||
@@ -130,27 +134,27 @@
|
||||
contentClasses="max-h-[70vh] overflow-y-auto p-4 flex flex-col gap-4 w-96"
|
||||
closeOnOtherPopoverOpen
|
||||
>
|
||||
<svelte:fragment slot="trigger">
|
||||
<slot name="trigger" />
|
||||
</svelte:fragment>
|
||||
{#snippet trigger()}
|
||||
{@render trigger_render?.()}
|
||||
{/snippet}
|
||||
|
||||
<svelte:fragment slot="content">
|
||||
{#snippet content()}
|
||||
{#if value}
|
||||
<Section label="Column settings">
|
||||
<svelte:fragment slot="header">
|
||||
{#snippet header()}
|
||||
<Badge color="blue">
|
||||
{value.field}
|
||||
</Badge>
|
||||
</svelte:fragment>
|
||||
{/snippet}
|
||||
<Label label="Skip for select and update">
|
||||
<svelte:fragment slot="header">
|
||||
{#snippet header()}
|
||||
<Tooltip>
|
||||
By default, all columns are included in the select and update queries. If you want to
|
||||
exclude a column from the select and update queries, you can set this property to
|
||||
true.
|
||||
</Tooltip>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="action">
|
||||
{/snippet}
|
||||
{#snippet action()}
|
||||
<Toggle
|
||||
on:pointerdown={(e) => {
|
||||
e?.stopPropagation()
|
||||
@@ -159,7 +163,7 @@
|
||||
size="xs"
|
||||
disabled={value?.isprimarykey}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
{/snippet}
|
||||
{#if value?.isprimarykey}
|
||||
<Alert type="warning" size="xs" title="Primary key" class="my-1">
|
||||
You cannot skip a primary key.
|
||||
@@ -168,14 +172,14 @@
|
||||
</Label>
|
||||
|
||||
<Label label="Hide from insert">
|
||||
<svelte:fragment slot="header">
|
||||
{#snippet header()}
|
||||
<Tooltip>
|
||||
By default, all columns are used to generate the submit form. If you want to exclude a
|
||||
column from the submit form, you can set this property to true. If the column is not
|
||||
nullable or doesn't have a default value, a default value will be required.
|
||||
</Tooltip>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="action">
|
||||
{/snippet}
|
||||
{#snippet action()}
|
||||
<Toggle
|
||||
disabled={value?.isidentity === ColumnIdentity.Always}
|
||||
on:pointerdown={(e) => {
|
||||
@@ -184,7 +188,7 @@
|
||||
bind:checked={value.hideInsert}
|
||||
size="xs"
|
||||
/>
|
||||
</svelte:fragment>
|
||||
{/snippet}
|
||||
</Label>
|
||||
{#if value?.isidentity === ColumnIdentity.Always}
|
||||
<Alert type="warning" size="xs" title="Identity column" class="my-1">
|
||||
@@ -216,13 +220,13 @@
|
||||
/>
|
||||
{/if}
|
||||
<Label label="Default input">
|
||||
<svelte:fragment slot="header">
|
||||
{#snippet header()}
|
||||
<Tooltip>
|
||||
By default, all columns are used to generate the submit form. If you want to exclude a
|
||||
column from the submit form, you can set this property to true. If the column is not
|
||||
nullable or doesn't have a default value, a default value will be required.
|
||||
</Tooltip>
|
||||
</svelte:fragment>
|
||||
{/snippet}
|
||||
{#if value?.datatype}
|
||||
{@const type = value?.datatype}
|
||||
|
||||
@@ -266,7 +270,7 @@
|
||||
<Section label="AG Grid configuration">
|
||||
<div
|
||||
class={twMerge('flex flex-col gap-4', value.ignored ? 'opacity-50 cursor-none ' : '')}
|
||||
on:pointerdown={(e) => {
|
||||
onpointerdown={(e) => {
|
||||
if (value?.ignored) {
|
||||
e?.stopPropagation()
|
||||
}
|
||||
@@ -292,7 +296,7 @@
|
||||
</Label>
|
||||
|
||||
<Label label="Flex">
|
||||
<svelte:fragment slot="header">
|
||||
{#snippet header()}
|
||||
<Tooltip
|
||||
documentationLink="https://www.ag-grid.com/javascript-data-grid/column-sizing/#column-flex"
|
||||
>
|
||||
@@ -306,7 +310,7 @@
|
||||
remaining. The column with flex: 2 has twice the size with flex: 1. So final sizes
|
||||
will be: 150px, 100px, 200px.
|
||||
</Tooltip>
|
||||
</svelte:fragment>
|
||||
{/snippet}
|
||||
|
||||
<input type="range" step="1" bind:value={value.flex} min={1} max={12} />
|
||||
<div class="text-xs">{value.flex}</div>
|
||||
@@ -324,7 +328,7 @@
|
||||
</Label>
|
||||
|
||||
<Label label="Value formatter">
|
||||
<svelte:fragment slot="header">
|
||||
{#snippet header()}
|
||||
<Tooltip
|
||||
documentationLink="https://www.ag-grid.com/javascript-data-grid/value-formatters/"
|
||||
>
|
||||
@@ -332,8 +336,8 @@
|
||||
one type (e.g. numeric) but needs to be converted for human reading (e.g. putting in
|
||||
currency symbols and number formatting).
|
||||
</Tooltip>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="action">
|
||||
{/snippet}
|
||||
{#snippet action()}
|
||||
<Button
|
||||
size="xs"
|
||||
color="light"
|
||||
@@ -346,7 +350,7 @@
|
||||
>
|
||||
Clear
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
{/snippet}
|
||||
</Label>
|
||||
<div>
|
||||
{#key renderCount}
|
||||
@@ -360,7 +364,7 @@
|
||||
<div class="text-xs font-semibold">Presets</div>
|
||||
<select
|
||||
bind:value={value.valueFormatter}
|
||||
on:change={() => {
|
||||
onchange={() => {
|
||||
renderCount++
|
||||
}}
|
||||
placeholder="Code"
|
||||
@@ -392,5 +396,5 @@
|
||||
</div>
|
||||
</Section>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
{/snippet}
|
||||
</Popover>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
const { selectedComponent } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let closeOnOutsideClick = true
|
||||
let closeOnOutsideClick = $state(true)
|
||||
|
||||
type Dataset = {
|
||||
value: RichConfiguration
|
||||
@@ -23,13 +23,20 @@
|
||||
extraOptions?: { mode: 'markers' | 'lines' | 'lines+markers' } | undefined
|
||||
}
|
||||
|
||||
export let value: Dataset | undefined = undefined
|
||||
interface Props {
|
||||
value?: Dataset | undefined
|
||||
trigger?: import('svelte').Snippet
|
||||
}
|
||||
|
||||
let { value = $bindable(undefined), trigger }: Props = $props()
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
function removeDataset() {
|
||||
dispatch('remove')
|
||||
}
|
||||
|
||||
const trigger_render = $derived(trigger)
|
||||
</script>
|
||||
|
||||
<Popover
|
||||
@@ -40,10 +47,10 @@
|
||||
}}
|
||||
{closeOnOutsideClick}
|
||||
>
|
||||
<svelte:fragment slot="trigger">
|
||||
<slot name="trigger" />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="content">
|
||||
{#snippet trigger()}
|
||||
{@render trigger_render?.()}
|
||||
{/snippet}
|
||||
{#snippet content()}
|
||||
{#if value}
|
||||
<div class="flex flex-col w-96 p-4 gap-4 max-h-[70vh] overflow-y-auto">
|
||||
<Label label="Name">
|
||||
@@ -55,7 +62,7 @@
|
||||
<option value="bar">Bar</option>
|
||||
<option
|
||||
value="scatter"
|
||||
on:click={() => {
|
||||
onclick={() => {
|
||||
if (value && value?.extraOptions === undefined) {
|
||||
value.extraOptions = { mode: 'markers' }
|
||||
}
|
||||
@@ -77,13 +84,13 @@
|
||||
{/if}
|
||||
|
||||
<Label label="Aggregation method">
|
||||
<svelte:fragment slot="header">
|
||||
{#snippet header()}
|
||||
<Tooltip>
|
||||
A method to aggregate the data. For example, if you have multiple x data points with
|
||||
the same value, you can choose to sum them up or take the mean. If you don't have
|
||||
multiple x data points with the same value, this option will have no effect.
|
||||
</Tooltip>
|
||||
</svelte:fragment>
|
||||
{/snippet}
|
||||
<select bind:value={value.aggregation_method}>
|
||||
<option value="sum">Sum</option>
|
||||
<option value="mean">Mean</option>
|
||||
@@ -128,5 +135,5 @@
|
||||
<Button color="red" size="xs" on:click={removeDataset}>Remove dataset</Button>
|
||||
</div>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
{/snippet}
|
||||
</Popover>
|
||||
|
||||
@@ -12,11 +12,23 @@
|
||||
CheckboxComponent,
|
||||
SelectComponent
|
||||
} from '../apps/editor/component'
|
||||
export let actionsOrder: RichConfiguration | undefined = undefined
|
||||
export let selectedId: string | undefined = undefined
|
||||
export let components:
|
||||
| (BaseAppComponent & (ButtonComponent | CheckboxComponent | SelectComponent))[]
|
||||
| undefined
|
||||
interface Props {
|
||||
actionsOrder?: RichConfiguration | undefined
|
||||
selectedId?: string | undefined
|
||||
components:
|
||||
| (BaseAppComponent & (ButtonComponent | CheckboxComponent | SelectComponent))[]
|
||||
| undefined
|
||||
trigger?: import('svelte').Snippet
|
||||
}
|
||||
|
||||
let {
|
||||
actionsOrder = $bindable(undefined),
|
||||
selectedId = undefined,
|
||||
components,
|
||||
trigger
|
||||
}: Props = $props()
|
||||
|
||||
const trigger_render = $derived(trigger)
|
||||
</script>
|
||||
|
||||
<Popover
|
||||
@@ -27,15 +39,15 @@
|
||||
}}
|
||||
closeButton
|
||||
>
|
||||
<svelte:fragment slot="trigger">
|
||||
<slot name="trigger" />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="content">
|
||||
{#snippet trigger()}
|
||||
{@render trigger_render?.()}
|
||||
{/snippet}
|
||||
{#snippet content()}
|
||||
<div class="w-96">
|
||||
<PanelSection
|
||||
title={`Manage actions programmatically`}
|
||||
tooltip="
|
||||
You can manage the order of the actions programmatically: You need to return an array of action ids in the order you want them to appear in the table. You can also hide actions by not including them in the array."
|
||||
You can manage the order of the actions programmatically: You need to return an array of action ids in the order you want them to appear in the table. You can also hide actions by not including them in the array."
|
||||
>
|
||||
<div class="w-full flex gap-2 flex-col mt-2">
|
||||
{#if actionsOrder}
|
||||
@@ -93,5 +105,5 @@
|
||||
</div>
|
||||
</PanelSection>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
{/snippet}
|
||||
</Popover>
|
||||
|
||||
@@ -5,11 +5,18 @@
|
||||
import Toggle from '../Toggle.svelte'
|
||||
import Tooltip from '../Tooltip.svelte'
|
||||
import { offset, flip, shift } from 'svelte-floating-ui/dom'
|
||||
export let column: {
|
||||
headerName: string
|
||||
hideColumn: boolean
|
||||
type: 'text' | 'badge' | 'link'
|
||||
interface Props {
|
||||
column: {
|
||||
headerName: string
|
||||
hideColumn: boolean
|
||||
type: 'text' | 'badge' | 'link'
|
||||
}
|
||||
trigger?: import('svelte').Snippet
|
||||
}
|
||||
|
||||
let { column = $bindable(), trigger }: Props = $props()
|
||||
|
||||
const trigger_render = $derived(trigger)
|
||||
</script>
|
||||
|
||||
<Popover
|
||||
@@ -21,10 +28,10 @@
|
||||
closeButton
|
||||
closeOnOtherPopoverOpen
|
||||
>
|
||||
<svelte:fragment slot="trigger">
|
||||
<slot name="trigger" />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="content">
|
||||
{#snippet trigger()}
|
||||
{@render trigger_render?.()}
|
||||
{/snippet}
|
||||
{#snippet content()}
|
||||
<div class="flex flex-col w-96 p-4 gap-4">
|
||||
<span class="text-sm mb-2 leading-6 font-semibold">
|
||||
Table Column
|
||||
@@ -73,5 +80,5 @@
|
||||
</Alert>
|
||||
{/if}
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
{/snippet}
|
||||
</Popover>
|
||||
|
||||
Reference in New Issue
Block a user