feat(frontend): Fork + Fix table (#1037)

* feat(frontend): WIP

* feat(frontend): WIP

* feat(frontend): add support unused inline scripts

* feat(frontend): fix all interaction

* feat(frontend): Fix pick

* feat(frontend): add ability to fork

* feat(frontend): fix mobile preview

* feat(frontend): fix table

* feat(frontend): fix table actions

* feat(frontend): Fix build error
This commit is contained in:
Faton Ramadani
2022-12-21 08:22:30 +01:00
committed by GitHub
parent 8fe77cf15a
commit 767058dcd0
19 changed files with 436 additions and 219 deletions
@@ -23,7 +23,8 @@
async function createApp() {
const appJson: App = {
grid: [],
title: 'New app'
title: 'New app',
unusedInlineScripts: []
}
const policy = {
@@ -1,5 +1,6 @@
<script lang="ts">
import type { AppInput } from '../../inputType'
import { isScriptByNameDefined, isScriptByPathDefined } from '../../utils'
import NonRunnableComponent from './NonRunnableComponent.svelte'
import RunnableComponent from './RunnableComponent.svelte'
@@ -13,18 +14,7 @@
export let forceSchemaDisplay: boolean = false
function isRunnableDefined() {
if (!componentInput) return false
if (componentInput.type !== 'runnable') return false
if (
(componentInput.runnable?.type === 'runnableByName' &&
componentInput.runnable.inlineScript) ||
(componentInput.runnable?.type === 'runnableByPath' && componentInput.runnable.path)
) {
return true
}
return false
return isScriptByNameDefined(componentInput) || isScriptByPathDefined(componentInput)
}
</script>
@@ -32,12 +32,12 @@
let page = 1
const options = writable<TableOptions<T>>({
data: result,
data: [],
columns: [],
...tableOptions
})
const table = createSvelteTable(options)
let table = createSvelteTable(options)
const { worldStore, staticOutputs: staticOutputsStore } =
getContext<AppEditorContext>('AppEditorContext')
@@ -91,12 +91,18 @@
$: outputs = $worldStore?.outputsById[id] as {
selectedRow: Output<any>
}
function rerender() {
table = createSvelteTable(options)
}
$: result && rerender()
</script>
<InputValue input={configuration.search} bind:value={search} />
<InputValue input={configuration.pagination} bind:value={pagination} />
<RunnableWrapper bind:componentInput {id} bind:result {extraQueryParams}>
<RunnableWrapper bind:componentInput {id} bind:result {extraQueryParams} autoRefresh={false}>
<div class="border border-gray-300 shadow-sm divide-y divide-gray-300 flex flex-col h-full">
<div class="py-2 px-4">
<div class="flex justify-between items-center">
@@ -129,7 +135,7 @@
{/each}
</thead>
<tbody class="divide-y divide-gray-200 bg-white ">
{#each $table.getRowModel().rows as row, rowIndex}
{#each $table.getRowModel().rows as row, rowIndex (row.id)}
<tr
class={classNames(
selectedRowIndex === rowIndex
@@ -143,7 +149,7 @@
)}
on:click={() => toggleRow(row, rowIndex)}
>
{#each row.getVisibleCells() as cell}
{#each row.getVisibleCells() as cell, index (index)}
<td class="p-4 whitespace-nowrap text-xs text-gray-900">
<svelte:component
this={flexRender(cell.column.columnDef.cell, cell.getContext())}
@@ -153,7 +159,7 @@
{#if actionButtons.length > 0}
<td class="flex flex-row gap-2 p-4">
{#each actionButtons as props}
{#each actionButtons as props, actionIndex (actionIndex)}
<AppButton
{...props}
extraQueryParams={{ row }}
@@ -66,9 +66,15 @@
})
$: mounted && ($worldStore = buildWorld($staticOutputs))
$: selectedTab = $selectedComponent ? 'settings' : 'insert'
$: previewing = $mode === 'preview'
$: width = $breakpoint === 'sm' ? 'w-[640px]' : 'min-w-[900px] w-full'
$: width = $breakpoint === 'sm' ? 'w-[640px]' : 'min-w-[1080px] w-full'
let selectedTab: 'insert' | 'settings' = 'insert'
$: if ($selectedComponent) {
selectedTab = 'settings'
} else {
selectedTab = 'insert'
}
</script>
{#if !$userStore?.operator}
@@ -77,7 +83,7 @@
{/if}
{#if previewing}
<AppPreview app={$appStore} appPath={path} />
<AppPreview app={$appStore} appPath={path} {breakpoint} />
{:else}
<SplitPanesWrapper class="max-w-full overflow-hidden">
<Pane size={20}>
@@ -85,10 +91,10 @@
</Pane>
<Pane size={60}>
<SplitPanesWrapper horizontal>
<Pane size={75}>
<div class="bg-gray-100 p-4 relative min-h-full w-full">
<Pane size={70}>
<div class={classNames('bg-gray-100 mx-auto relative min-h-full', width)}>
{#if $appStore.grid}
<div class={classNames('mx-auto h-full bg-gray-100', width)}>
<div class={classNames('mx-auto p-4 w-full h-full bg-gray-100', width)}>
<GridEditor />
</div>
{/if}
@@ -99,7 +105,7 @@
{/if}
</div>
</Pane>
<Pane size={25}>
<Pane size={30}>
<InlineScriptsPanel />
</Pane>
</SplitPanesWrapper>
@@ -1,7 +1,7 @@
<script lang="ts">
import { onMount, setContext } from 'svelte'
import { writable } from 'svelte/store'
import { writable, type Writable } from 'svelte/store'
import { buildWorld, type World } from '../rx'
import type {
App,
@@ -16,13 +16,13 @@
export let app: App
export let appPath: string
export let breakpoint: Writable<EditorBreakpoint>
const appStore = writable<App>(app)
const worldStore = writable<World | undefined>(undefined)
const staticOutputs = writable<Record<string, string[]>>({})
const selectedComponent = writable<string | undefined>(undefined)
const mode = writable<EditorMode>('preview')
const breakpoint = writable<EditorBreakpoint>('lg')
const connectingInput = writable<ConnectingInput>({
opened: false,
@@ -1,7 +1,7 @@
<script lang="ts">
import { classNames } from '$lib/utils'
import type { AppComponent } from '../types'
import { Lock, X } from 'lucide-svelte'
import { Anchor, Lock, X } from 'lucide-svelte'
import { createEventDispatcher } from 'svelte'
export let component: AppComponent
@@ -30,9 +30,9 @@
}}
>
{#if locked}
<Lock size={16} class="text-red-500" />
<Anchor size={16} class="text-orange-500" />
{:else}
<Lock size={16} />
<Anchor size={16} />
{/if}
</button>
@@ -1,6 +1,6 @@
<script lang="ts">
import { getContext } from 'svelte'
import type { AppEditorContext } from '../types'
import type { AppEditorContext, InlineScript } from '../types'
import Grid from 'svelte-grid'
import ComponentEditor from './ComponentEditor.svelte'
import { classNames } from '$lib/utils'
@@ -29,12 +29,30 @@
$app.grid.map((gridItem) => enableDrag(gridItem))
}
function deleteComponent(component) {
function removeGridElement(component) {
if (component) {
$app.grid = $app.grid.filter((gridComponent) => gridComponent.data.id !== component?.id)
$app.grid = $app.grid.filter((gridComponent) => {
if (gridComponent.data.id === component.id) {
if (
gridComponent.data.componentInput?.runnable?.type === 'runnableByName' &&
gridComponent.data.componentInput?.runnable.inlineScript
) {
const { name, inlineScript } = gridComponent.data.componentInput?.runnable
gridColumns.forEach((colIndex) => {
$app.grid = gridHelp.adjust($app.grid, colIndex)
if (!$app.unusedInlineScripts) {
$app.unusedInlineScripts = []
}
$app.unusedInlineScripts.push({
name,
inlineScript
})
$app = $app
}
}
return gridComponent.data.id !== component?.id
})
// Delete static inputs
@@ -43,6 +61,8 @@
delete $runnableComponents[component.id]
$runnableComponents = $runnableComponents
$selectedComponent = undefined
}
}
</script>
@@ -55,7 +75,6 @@
rowHeight={64}
cols={columnConfiguration}
fastStart={true}
throttleUpdate={50}
on:pointerup={({ detail }) => {
if (!$connectingInput.opened) {
$selectedComponent = detail.id
@@ -73,7 +92,7 @@
<ComponentEditor
bind:component={gridComponent.data}
selected={$selectedComponent === dataItem.data.id}
on:delete={() => deleteComponent(gridComponent.data)}
on:delete={() => removeGridElement(gridComponent.data)}
on:lock={() => {
gridComponent = toggleFixed(gridComponent)
}}
@@ -0,0 +1,80 @@
<script lang="ts">
import type { Schema } from '$lib/common'
import FlowScriptPicker from '$lib/components/flows/pickers/FlowScriptPicker.svelte'
import { Script, type Preview } from '$lib/gen'
import { inferArgs } from '$lib/infer'
import { initialCode } from '$lib/script_helpers'
import { emptySchema } from '$lib/utils'
import { createEventDispatcher, getContext } from 'svelte'
import { fly } from 'svelte/transition'
import type { AppEditorContext } from '../../types'
export let name: string
const { appPath } = getContext<AppEditorContext>('AppEditorContext')
const dispatch = createEventDispatcher()
async function inferInlineScriptSchema(
language: Preview.language,
content: string,
schema: Schema
): Promise<Schema> {
try {
await inferArgs(language, content, schema)
} catch (e) {
console.error("Couldn't infer args", e)
}
return schema
}
async function createInlineScriptByLanguage(
language: Preview.language,
path: string,
subkind: 'pgsql' | 'mysql' | undefined = undefined
) {
const fullPath = `${appPath}/inline-script/${path}`
const content = initialCode(language, Script.kind.SCRIPT, subkind)
let schema: Schema = emptySchema()
schema = await inferInlineScriptSchema(language, content, schema)
const newInlineScript = {
content,
language,
path: fullPath,
schema
}
dispatch('new', newInlineScript)
}
</script>
<div class="flex flex-col p-4 gap-2 text-sm" in:fly={{ duration: 50 }}>
Please choose a language:
<div class="flex gap-2 flex-row flex-wrap">
{#each Object.values(Script.language) as lang}
<FlowScriptPicker
label={lang}
{lang}
on:click={() => {
createInlineScriptByLanguage(lang, name)
}}
/>
{/each}
<FlowScriptPicker
label={`PostgreSQL`}
lang="pgsql"
on:click={() => {
createInlineScriptByLanguage(Script.language.DENO, name, 'pgsql')
}}
/>
<FlowScriptPicker
label={`MySQL`}
lang="mysql"
on:click={() => {
createInlineScriptByLanguage(Script.language.DENO, name, 'mysql')
}}
/>
</div>
</div>
@@ -1,15 +1,11 @@
<script lang="ts">
import Button from '$lib/components/common/button/Button.svelte'
import { Preview, Script } from '$lib/gen'
import { initialCode } from '$lib/script_helpers'
import { emptySchema } from '$lib/utils'
import type { Preview } from '$lib/gen'
import { faTrash } from '@fortawesome/free-solid-svg-icons'
import { getContext, onMount } from 'svelte'
import type { AppEditorContext } from '../../types'
import { createEventDispatcher, onMount } from 'svelte'
import type { InlineScript } from '../../types'
import SimpleEditor from '$lib/components/SimpleEditor.svelte'
import { CheckCircle, Code2, X } from 'lucide-svelte'
import FlowScriptPicker from '$lib/components/flows/pickers/FlowScriptPicker.svelte'
import type { ResultAppInput } from '../../inputType'
import InlineScriptEditorDrawer from './InlineScriptEditorDrawer.svelte'
import { inferArgs } from '$lib/infer'
import type { Schema } from '$lib/common'
@@ -17,13 +13,9 @@
import { fly } from 'svelte/transition'
let inlineScriptEditorDrawer: InlineScriptEditorDrawer
export let componentInput: ResultAppInput
const name =
componentInput.runnable?.type === 'runnableByName' ? componentInput.runnable?.name : ''
$: shouldDisplay = componentInput.runnable?.type === 'runnableByName'
const { appPath } = getContext<AppEditorContext>('AppEditorContext')
export let inlineScript: InlineScript
export let name: string | undefined = undefined
let validCode = false
@@ -43,152 +35,76 @@
return schema
}
async function createInlineScriptByLanguage(
language: Preview.language,
path: string,
subkind: 'pgsql' | 'mysql' | undefined = undefined
) {
const fullPath = `${appPath}/inline-script/${path}`
const content = initialCode(language, Script.kind.SCRIPT, subkind)
let schema: Schema = emptySchema()
schema = await inferInlineScriptSchema(language, content, schema)
const inlineScript = {
content,
language,
path: fullPath,
schema
}
if (componentInput?.runnable?.type === 'runnableByName') {
componentInput.runnable.inlineScript = inlineScript
}
}
onMount(async () => {
if (
componentInput.runnable?.type === 'runnableByName' &&
componentInput.runnable.inlineScript
) {
componentInput.runnable.inlineScript.schema = await inferInlineScriptSchema(
componentInput.runnable.inlineScript?.language,
componentInput.runnable.inlineScript?.content,
componentInput.runnable.inlineScript?.schema
if (inlineScript) {
inlineScript.schema = await inferInlineScriptSchema(
inlineScript?.language,
inlineScript?.content,
inlineScript?.schema
)
}
})
const dispatch = createEventDispatcher()
</script>
{#if componentInput.runnable && componentInput.runnable.type === 'runnableByName' && componentInput.runnable.inlineScript}
<InlineScriptEditorDrawer
bind:this={inlineScriptEditorDrawer}
bind:inlineScript={componentInput.runnable.inlineScript}
/>
{/if}
<InlineScriptEditorDrawer bind:this={inlineScriptEditorDrawer} bind:inlineScript />
{#if shouldDisplay}
{#if componentInput?.runnable?.type === 'runnableByName' && componentInput?.runnable?.inlineScript}
<div class="h-full p-4 flex flex-col gap-2" transition:fly={{ duration: 50 }}>
<div class="flex justify-between w-full flex-row items-center">
{#if componentInput?.runnable?.name !== undefined}
<input bind:value={componentInput.runnable.name} placeholder="Inline script name" />
{/if}
<div class="flex w-full flex-row gap-2 items-center justify-end">
{#if validCode}
<Badge color="green">
<CheckCircle size={16} />
</Badge>
{:else}
<Badge color="red">
<X size={16} />
</Badge>
{/if}
<Button
size="xs"
color="light"
variant="border"
iconOnly
startIcon={{ icon: faTrash }}
on:click={() => {
if (componentInput?.runnable?.type === 'runnableByName') {
componentInput.runnable = undefined
componentInput.fields = {}
}
}}
/>
<Button
size="xs"
color="light"
variant="border"
on:click={() => {
inlineScriptEditorDrawer?.openDrawer()
}}
>
<div class="flex gap-1 items-center">
<Code2 size={16} />
Open full editor
</div>
</Button>
</div>
</div>
{#if componentInput?.runnable?.type === 'runnableByName' && componentInput?.runnable?.inlineScript}
<div class="border h-full">
<SimpleEditor
class="flex flex-1 grow h-full"
lang="typescript"
bind:code={componentInput.runnable.inlineScript.content}
fixedOverflowWidgets={false}
on:change={async () => {
if (
componentInput?.runnable?.type === 'runnableByName' &&
componentInput?.runnable?.inlineScript
) {
let schema = await inferInlineScriptSchema(
componentInput?.runnable?.inlineScript?.language,
componentInput.runnable.inlineScript.content,
componentInput.runnable.inlineScript.schema
)
componentInput.runnable.inlineScript.schema = schema
componentInput = componentInput
}
}}
/>
</div>
<div class="h-full p-4 flex flex-col gap-2" transition:fly={{ duration: 50 }}>
<div class="flex justify-between w-full flex-row items-center">
{#if name !== undefined}
<input bind:value={name} placeholder="Inline script name" />
{/if}
<div class="flex w-full flex-row gap-2 items-center justify-end">
{#if validCode}
<Badge color="green">
<CheckCircle size={16} />
</Badge>
{:else}
<Badge color="red">
<X size={16} />
</Badge>
{/if}
</div>
{:else}
<div class="flex flex-col p-4 gap-2 text-sm" in:fly={{ duration: 50 }}>
Please choose a language:
<div class="flex gap-2 flex-row flex-wrap">
{#each Object.values(Script.language) as lang}
<FlowScriptPicker
label={lang}
{lang}
on:click={() => {
createInlineScriptByLanguage(lang, name)
}}
/>
{/each}
<FlowScriptPicker
label={`PostgreSQL`}
lang="pgsql"
on:click={() => {
createInlineScriptByLanguage(Script.language.DENO, name, 'pgsql')
}}
/>
<FlowScriptPicker
label={`MySQL`}
lang="mysql"
on:click={() => {
createInlineScriptByLanguage(Script.language.DENO, name, 'mysql')
}}
/>
</div>
<Button
size="xs"
color="light"
variant="border"
iconOnly
startIcon={{ icon: faTrash }}
on:click={() => dispatch('delete')}
/>
<Button
size="xs"
color="blue"
on:click={() => {
inlineScriptEditorDrawer?.openDrawer()
}}
>
<div class="flex gap-1 items-center">
<Code2 size={16} />
Open full editor
</div>
</Button>
</div>
{/if}
{/if}
</div>
<div class="border h-full">
<SimpleEditor
class="flex flex-1 grow h-full"
lang="typescript"
bind:code={inlineScript.content}
fixedOverflowWidgets={false}
on:change={async () => {
if (inlineScript) {
let schema = await inferInlineScriptSchema(
inlineScript?.language,
inlineScript.content,
inlineScript.schema
)
inlineScript.schema = schema
inlineScript = inlineScript
}
}}
/>
</div>
</div>
@@ -0,0 +1,67 @@
<script lang="ts">
import Button from '$lib/components/common/button/Button.svelte'
import FlowModuleScript from '$lib/components/flows/content/FlowModuleScript.svelte'
import { getScriptByPath } from '$lib/utils'
import { faCodeBranch } from '@fortawesome/free-solid-svg-icons'
import { r } from 'svelte-highlight/languages'
import type { ResultAppInput } from '../../inputType'
import { clearResultAppInput } from '../../utils'
import EmptyInlineScript from './EmptyInlineScript.svelte'
import InlineScriptEditor from './InlineScriptEditor.svelte'
export let componentInput: ResultAppInput
async function fork(path: string) {
const { content, language, schema } = await getScriptByPath(path)
componentInput.runnable = {
type: 'runnableByName',
name: path,
inlineScript: {
content,
language,
schema,
path
}
}
}
</script>
{#if componentInput?.runnable?.type === 'runnableByName' && componentInput?.runnable?.name !== undefined}
{#if componentInput.runnable.inlineScript}
<InlineScriptEditor
bind:inlineScript={componentInput.runnable.inlineScript}
bind:name={componentInput.runnable.name}
on:delete={() => {
componentInput = clearResultAppInput(componentInput)
}}
/>
{:else}
<EmptyInlineScript
name={componentInput.runnable.name}
on:new={(e) => {
if (componentInput?.runnable?.type === 'runnableByName') {
componentInput.runnable.inlineScript = e.detail
}
}}
/>
{/if}
{:else if componentInput?.runnable?.type === 'runnableByPath' && componentInput?.runnable?.path}
<div class="p-2 h-full flex flex-col gap-2 ">
<div>
<Button
size="xs"
startIcon={{ icon: faCodeBranch }}
on:click={() => {
if (componentInput.runnable?.type === 'runnableByPath') {
fork(componentInput.runnable.path)
}
}}
>
Fork
</Button>
</div>
<div class="border w-full">
<FlowModuleScript path={componentInput.runnable.path} />
</div>
</div>
{/if}
@@ -4,6 +4,7 @@
import SplitPanesWrapper from '$lib/components/splitPanes/SplitPanesWrapper.svelte'
import { Pane } from 'svelte-splitpanes'
import InlineScriptsPanelList from './InlineScriptsPanelList.svelte'
import InlineScriptEditorPanel from './InlineScriptEditorPanel.svelte'
import InlineScriptEditor from './InlineScriptEditor.svelte'
const { app } = getContext<AppEditorContext>('AppEditorContext')
@@ -18,8 +19,30 @@
<Pane size={75}>
{#each $app.grid as gridComponent, index (index)}
{#if gridComponent.data.id === selectedScriptComponentId}
<InlineScriptEditor bind:componentInput={gridComponent.data.componentInput} />
<InlineScriptEditorPanel bind:componentInput={gridComponent.data.componentInput} />
{/if}
{#if gridComponent.data.type === 'tablecomponent'}
{#each gridComponent.data.actionButtons as actionButton, index (index)}
{#if actionButton.id === selectedScriptComponentId}
<InlineScriptEditorPanel bind:componentInput={actionButton.componentInput} />
{/if}
{/each}
{/if}
{/each}
{#if Array.isArray($app.unusedInlineScripts)}
{#each $app.unusedInlineScripts as unusedInlineScript, index (index)}
{#if unusedInlineScript.name === selectedScriptComponentId}
<InlineScriptEditor
bind:inlineScript={unusedInlineScript.inlineScript}
on:delete={() => {
// remove the script from the array at the index
$app.unusedInlineScripts.splice(index, 1)
$app.unusedInlineScripts = [...$app.unusedInlineScripts]
}}
/>
{/if}
{/each}
{/if}
</Pane>
</SplitPanesWrapper>
@@ -3,19 +3,36 @@
import { classNames } from '$lib/utils'
import { getContext } from 'svelte'
import type { AppInput } from '../../inputType'
import type { AppEditorContext } from '../../types'
import type { AppComponent, AppEditorContext } from '../../types'
import PanelSection from '../settingsPanel/common/PanelSection.svelte'
export let selectedScriptComponentId: string | undefined = undefined
const { app } = getContext<AppEditorContext>('AppEditorContext')
const { app, selectedComponent } = getContext<AppEditorContext>('AppEditorContext')
function selectInlineScript(id: string) {
selectedScriptComponentId = id
function selectInlineScript(id: string, subId?: string) {
selectedScriptComponentId = subId ? subId : id
$selectedComponent = id
}
$: componentInlineScripts = $app.grid.reduce((acc, gridComponent) => {
const componentInput: AppInput = gridComponent.data.componentInput
$: runnablesByName = $app.grid.reduce((acc, gridComponent) => {
const component: AppComponent = gridComponent.data
if (component.type === 'tablecomponent') {
component.actionButtons.forEach((actionButton) => {
if (actionButton.componentInput?.type === 'runnable') {
if (actionButton.componentInput.runnable?.type === 'runnableByName') {
acc.push({
name: actionButton.componentInput.runnable.name,
id: gridComponent.id,
subId: actionButton.id
})
}
}
})
}
const componentInput = component.componentInput
if (componentInput?.type === 'runnable') {
if (componentInput.runnable?.type === 'runnableByName') {
@@ -27,13 +44,81 @@
}
return acc
}, [])
$: runnablesByPath = $app.grid.reduce((acc, gridComponent) => {
const componentInput: AppInput = gridComponent.data.componentInput
if (componentInput?.type === 'runnable') {
if (componentInput.runnable?.type === 'runnableByPath') {
acc.push({
name: componentInput.runnable.path,
id: gridComponent.id
})
}
}
return acc
}, [])
// When seleced component changes, update selectedScriptComponentId
$: {
if (selectedComponent) {
selectedScriptComponentId = $selectedComponent
}
}
</script>
<PanelSection title="Inline scripts">
<PanelSection title="Inline scripts" smallPadding>
<div class="flex flex-col gap-2 w-full">
{#if componentInlineScripts.length > 0}
{#if runnablesByName.length > 0}
<div class="flex gap-2 flex-col ">
{#each componentInlineScripts as { name, id }, index (index)}
{#each runnablesByName as { name, id, subId }, index (index)}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
class="{classNames(
'border flex justify-between flex-row w-full items-center p-2 rounded-sm cursor-pointer hover:bg-blue-50 hover:text-blue-400',
selectedScriptComponentId === (subId ? subId : id) ? 'border-blue-500 border' : ''
)},"
on:click={() => selectInlineScript(id, subId)}
>
<span class="text-xs">{name}</span>
<div>
<Badge color="dark-indigo">{id}</Badge>
{#if subId}
<Badge color="dark-indigo">{subId}</Badge>
{/if}
</div>
</div>
{/each}
</div>
{:else}
<div class="text-sm text-gray-500">No inline scripts</div>
{/if}
{#if Array.isArray($app.unusedInlineScripts) && $app.unusedInlineScripts.length > 0}
<div class="flex gap-2 flex-col ">
{#each $app.unusedInlineScripts as unusedInlineScript, index (index)}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
class="{classNames(
'border flex justify-between flex-row w-full items-center p-2 rounded-md cursor-pointer hover:bg-blue-50 hover:text-blue-400',
selectedScriptComponentId === '' ? 'bg-blue-100 text-blue-600' : ''
)},"
on:click={() => selectInlineScript(unusedInlineScript.name)}
>
<span class="text-xs">{unusedInlineScript.name}</span>
<Badge color="red">Unused</Badge>
</div>
{/each}
</div>
{/if}
</div>
</PanelSection>
<PanelSection title="Runnable by path" smallPadding>
<div class="flex flex-col gap-2 w-full">
{#if runnablesByPath.length > 0}
<div class="flex gap-2 flex-col ">
{#each runnablesByPath as { name, id }, index (index)}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
class="{classNames(
@@ -39,10 +39,6 @@
if (component) {
$app.grid = $app.grid.filter((gridComponent) => gridComponent.data.id !== component?.id)
gridColumns.forEach((colIndex) => {
$app.grid = gridHelp.adjust($app.grid, colIndex)
})
// Delete static inputs
delete $staticOutputs[component.id]
$staticOutputs = $staticOutputs
@@ -2,6 +2,7 @@
import Button from '$lib/components/common/button/Button.svelte'
import { faClose, faEdit } from '@fortawesome/free-solid-svg-icons'
import type { ResultAppInput } from '../../inputType'
import { clearResultAppInput } from '../../utils'
import InlineScriptEditorDrawer from '../inlineScriptsPanel/InlineScriptEditorDrawer.svelte'
export let appInput: ResultAppInput
@@ -14,11 +15,7 @@
}
function clear() {
if (appInput) {
appInput.runnable = undefined
appInput.fields = {}
appInput = appInput
}
appInput = clearResultAppInput(appInput)
}
</script>
@@ -26,9 +26,12 @@
/>
</div>
{#if inlineScripts.length == 0}
Should create
{:else if filteredItems.length == 0}
{#if inlineScripts.length === 0}
<div class="flex flex-col w-full h-full">
<div class="text-md ">No inline scripts</div>
<div class="text-sm">Add inline scripts to your app</div>
</div>
{:else if filteredItems.length === 0}
<NoItemFound />
{:else}
<ul class="divide-y divide-gray-200 border rounded-md">
@@ -40,13 +40,19 @@
}
}
function pickInlineScript(name: string, inlineScript: InlineScript) {
if (appInput.type === 'runnable') {
function pickInlineScript(name: string) {
const unusedInlineScriptIndex = $app.unusedInlineScripts?.findIndex(
(script) => script.name === name
)
const unusedInlineScript = $app.unusedInlineScripts?.[unusedInlineScriptIndex]
if (appInput.type === 'runnable' && unusedInlineScript?.inlineScript) {
appInput.runnable = {
type: 'runnableByName',
name,
inlineScript
inlineScript: unusedInlineScript.inlineScript
}
$app.unusedInlineScripts.splice(unusedInlineScriptIndex, 1)
}
}
@@ -67,7 +73,10 @@
return acc
}, [] as string[])
while (names.includes(newScriptPath)) {
const unusedNames = Object.keys($app.unusedInlineScripts ?? {})
// Find a name that is not used by any other inline script
while (names.includes(newScriptPath) || unusedNames.includes(newScriptPath)) {
newScriptPath = `inline_script_${++index}`
}
@@ -117,7 +126,12 @@
<div class="flex flex-col gap-y-16">
<div class="flex flex-col">
{#if tab == 'inlinescripts'}
<InlineScriptList on:pick={(e) => pickInlineScript('', e.detail)} />
<InlineScriptList
on:pick={(e) => pickInlineScript(e.detail)}
inlineScripts={$app.unusedInlineScripts
? $app.unusedInlineScripts.map((uis) => uis.name)
: []}
/>
{:else if tab == 'workspacescripts'}
<WorkspaceScriptList on:pick={(e) => pickScript(e.detail)} />
{:else if tab == 'workspaceflows'}
@@ -113,6 +113,10 @@ export type InlineScript = {
export type App = {
grid: GridItem[]
title: string
unusedInlineScripts: Array<{
name: string
inlineScript: InlineScript
}>
}
export type ConnectingInput = {
+7 -1
View File
@@ -17,7 +17,7 @@ import {
Calendar,
ToggleLeft
} from 'lucide-svelte'
import type { AppInput, AppInputs, InputType } from './inputType'
import type { AppInput, AppInputs, InputType, ResultAppInput } from './inputType'
import type { AppComponent } from './types'
export async function loadSchema(
@@ -194,3 +194,9 @@ export function isScriptByPathDefined(appInput: AppInput | undefined): boolean {
return false
}
export function clearResultAppInput(appInput: ResultAppInput): ResultAppInput {
appInput.runnable = undefined
appInput.fields = {}
return appInput
}
@@ -9,6 +9,7 @@
<script lang="ts">
import { page } from '$app/stores'
import AppPreview from '$lib/components/apps/editor/AppPreview.svelte'
import type { EditorBreakpoint } from '$lib/components/apps/types'
import CenteredPage from '$lib/components/CenteredPage.svelte'
import { Skeleton } from '$lib/components/common'
@@ -16,6 +17,7 @@
import { AppService, AppWithLastVersion } from '$lib/gen'
import { workspaceStore } from '$lib/stores'
import { faPen } from '@fortawesome/free-solid-svg-icons'
import { writable } from 'svelte/store'
let app: AppWithLastVersion | undefined = undefined
@@ -26,6 +28,8 @@
$: if ($workspaceStore) {
loadApp()
}
const breakpoint = writable<EditorBreakpoint>('lg')
</script>
<Skeleton loading={app == undefined} layout={[10]} />
@@ -41,7 +45,7 @@
</div>
<div class="border rounded-md p-2">
<AppPreview app={app.value} appPath={app.path} />
<AppPreview app={app.value} appPath={app.path} {breakpoint} />
</div>
{/if}
</CenteredPage>