mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-04 00:03:02 +00:00
appSvelte5
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
if (fComponent) {
|
||||
fComponent = toggleFixed(fComponent)
|
||||
}
|
||||
$app = $app
|
||||
// $app = $app
|
||||
}
|
||||
|
||||
let container: HTMLElement | undefined = undefined
|
||||
@@ -201,7 +201,7 @@
|
||||
isActive && !$selectedComponent?.includes(id)
|
||||
? 'outline-orange-600'
|
||||
: 'outline-gray-400 dark:outline-gray-600'
|
||||
}`
|
||||
}`
|
||||
: ''}
|
||||
>
|
||||
<Grid
|
||||
@@ -284,7 +284,7 @@
|
||||
$breakpoint,
|
||||
parentGridItem
|
||||
)
|
||||
$app = $app
|
||||
// $app = $app
|
||||
}}
|
||||
on:fillHeight={() => {
|
||||
const gridItem = findGridItem($app, dataItem.id)
|
||||
@@ -293,7 +293,7 @@
|
||||
if (gridItem?.[b]) {
|
||||
gridItem[b].fullHeight = !gridItem[b].fullHeight
|
||||
}
|
||||
$app = $app
|
||||
// $app = $app
|
||||
}}
|
||||
{moveMode}
|
||||
{componentDraggedId}
|
||||
|
||||
@@ -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,12 +75,23 @@
|
||||
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>
|
||||
@@ -797,7 +808,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=""
|
||||
|
||||
@@ -31,8 +31,7 @@
|
||||
}
|
||||
|
||||
const code = cssEditor?.getCode()
|
||||
cssEditor?.setCode(code + '\n' + selector)
|
||||
$app = $app
|
||||
cssEditor?.setCode(code + '\n' + selector) // $app = $app
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -74,7 +74,8 @@
|
||||
>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}
|
||||
@@ -82,41 +83,38 @@
|
||||
{#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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
+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">
|
||||
|
||||
+4
-4
@@ -196,7 +196,7 @@
|
||||
$stateId++
|
||||
}
|
||||
}
|
||||
$app = $app
|
||||
// $app = $app
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,7 +271,7 @@
|
||||
placeholder="Inline script name"
|
||||
class="!text-xs !rounded-sm !shadow-none"
|
||||
onkeyup={() => {
|
||||
$app = $app
|
||||
// $app = $app
|
||||
if (stateId) {
|
||||
$stateId++
|
||||
}
|
||||
@@ -385,7 +385,7 @@
|
||||
loadSchemaAndInputsByName()
|
||||
}
|
||||
}
|
||||
$app = $app
|
||||
// $app = $app
|
||||
}}
|
||||
args={Object.entries(fields).reduce((acc, [key, obj]) => {
|
||||
acc[key] = obj.type === 'static' ? obj.value : undefined
|
||||
@@ -413,7 +413,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
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
+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} />
|
||||
|
||||
+1
-2
@@ -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>
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
if (componentSetting?.item?.data?.id) {
|
||||
delete $runnableComponents[componentSetting?.item?.data?.id]
|
||||
}
|
||||
$app = $app
|
||||
// $app = $app
|
||||
$runnableComponents = $runnableComponents
|
||||
|
||||
onDelete?.()
|
||||
|
||||
@@ -60,8 +60,7 @@
|
||||
$app!.subgrids = {
|
||||
...$app!.subgrids,
|
||||
...newSubgrids
|
||||
}
|
||||
$app = $app
|
||||
} // $app = $app
|
||||
|
||||
tick().then(() => {
|
||||
const targetIndex = items.findIndex((i) => i.id === e.detail.info.id)
|
||||
@@ -94,8 +93,7 @@
|
||||
})
|
||||
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 {
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -84,8 +84,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 {
|
||||
@@ -127,8 +126,7 @@
|
||||
$app!.subgrids = {
|
||||
...$app!.subgrids,
|
||||
...newSubgrids
|
||||
}
|
||||
$app = $app
|
||||
} // $app = $app
|
||||
|
||||
tick().then(() => {
|
||||
const targetIndex = items.findIndex((i) => i.id === e.detail.info.id)
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -23,8 +23,7 @@
|
||||
...appComponentFromType('buttoncomponent')(`${id}_${actionId}`),
|
||||
recomputeIds: []
|
||||
}
|
||||
components = [...components, newComponent]
|
||||
$app = $app
|
||||
components = [...components, newComponent] // $app = $app
|
||||
}
|
||||
|
||||
function deleteComponent(cid: string) {
|
||||
@@ -32,8 +31,7 @@
|
||||
|
||||
delete $errorByComponent[cid]
|
||||
|
||||
$selectedComponent = [id]
|
||||
$app = $app
|
||||
$selectedComponent = [id] // $app = $app
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -21,8 +21,7 @@
|
||||
$app.unusedInlineScripts.push({
|
||||
name: appInput.runnable.name,
|
||||
inlineScript: appInput.runnable.inlineScript
|
||||
})
|
||||
$app = $app
|
||||
}) // $app = $app
|
||||
appInput = clearResultAppInput(appInput)
|
||||
}
|
||||
}
|
||||
@@ -56,7 +55,7 @@
|
||||
color: 'light',
|
||||
callback: detach
|
||||
}
|
||||
] as const)
|
||||
] as const)
|
||||
: []),
|
||||
{
|
||||
label: 'Clear',
|
||||
|
||||
@@ -68,8 +68,7 @@
|
||||
}
|
||||
]
|
||||
|
||||
components = [...components, newComponent]
|
||||
$app = $app
|
||||
components = [...components, newComponent] // $app = $app
|
||||
}
|
||||
|
||||
function deleteComponent(cid: string, index: number) {
|
||||
@@ -79,8 +78,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)
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
+1
-2
@@ -86,8 +86,7 @@
|
||||
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
|
||||
|
||||
+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"
|
||||
|
||||
+1
-2
@@ -84,8 +84,7 @@
|
||||
} else if (!itemsExists(inlineScript.refreshOn, refresh)) {
|
||||
inlineScript.refreshOn = [...inlineScript.refreshOn, refresh]
|
||||
}
|
||||
inlineScript = inlineScript
|
||||
$app = $app
|
||||
inlineScript = inlineScript // $app = $app
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -29,8 +29,7 @@
|
||||
$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>
|
||||
|
||||
@@ -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