Files
windmill/frontend/src/lib/components/FlowViewer.svelte
Faton Ramadani 2fb5f1a204 feat(frontend): Menu + Tab components (#517)
* feat(frontend): Migrate Tabs

* feat(frontend): Generalise Menu

* feat(frontend): Menu component done

* feat(frontend): Fix placements

* feat(frontend): Clean unused imports

* feat(frontend): Fix linting
2022-08-31 18:41:13 +02:00

208 lines
6.7 KiB
Svelte

<script lang="ts">
import { scriptPathToHref } from '$lib/utils'
import Highlight from 'svelte-highlight'
import json from 'svelte-highlight/languages/json'
import python from 'svelte-highlight/languages/python'
import typescript from 'svelte-highlight/languages/typescript'
import type { FlowValue } from '$lib/gen'
import { slide } from 'svelte/transition'
import Tabs from './common/tabs/Tabs.svelte'
import Tab from './common/tabs/Tab.svelte'
import TabContent from './common/tabs/TabContent.svelte'
import SchemaViewer from './SchemaViewer.svelte'
import FieldHeader from './FieldHeader.svelte'
import InputTransformsViewer from './InputTransformsViewer.svelte'
import SvelteMarkdown from 'svelte-markdown'
import IconedPath from './IconedPath.svelte'
export let flow: {
summary: string
description?: string
value: FlowValue
schema?: any
}
export let initialOpen: number | undefined = undefined
let flowFiltered = {
summary: flow.summary,
description: flow.description,
value: flow.value,
schema: flow.schema
}
export let embedded = false
export let tab: 'ui' | 'json' | 'schema' = 'ui'
let open: { [id: number]: boolean } = {}
if (initialOpen) {
open[initialOpen] = true
}
function toAny(x: unknown): any {
return x as any
}
</script>
{#if !embedded}
<Tabs bind:selected={tab}>
<Tab value="ui">Flow rendered</Tab>
<Tab value="json">Json</Tab>
<Tab value="schema">Input schema of the flow</Tab>
<svelte:fragment slot="content">
<TabContent value="ui">
<div class="flow-root w-full pb-4">
{#if !embedded}
<h2 class="my-4">{flow.summary}</h2>
<SvelteMarkdown source={flow.description ?? ''} />
<p class="font-black text-lg w-full my-4">
<span>Inputs</span>
</p>
{#if flow.schema && flow.schema.properties && Object.keys(flow.schema.properties).length > 0 && flow.schema}
<ul class="my-2">
{#each Object.entries(flow.schema.properties) as [inp, v]}
<li class="list-disc flex flex-row">
<FieldHeader
label={inp}
required={flow.schema.required?.includes(inp)}
type={toAny(v)?.type}
contentEncoding={toAny(v)?.contentEncoding}
format={toAny(v)?.format}
itemsType={toAny(v)?.itemsType}
/><span class="ml-4 mt-2 text-xs"
>{toAny(v)?.default != undefined
? 'default: ' + JSON.stringify(toAny(v)?.default)
: ''}</span
>
</li>
{/each}
</ul>
{:else}
<div class="text-gray-700 text-xs italic mb-4">No inputs</div>
{/if}
{/if}
<p class="font-black text-lg my-6 w-full">
<span
>{flow?.value?.modules?.length} Step{flow?.value?.modules?.length > 1 ? 's' : ''}
</span>
<span class="mt-4" />
</p>
<ul class="-mb-8 w-full">
{#each flow?.value?.modules ?? [] as mod, i}
<li class="w-full">
<div class="relative pb-8 w-full">
{#if i < (flow?.value?.modules ?? []).length - 1}
<span
class="absolute top-4 left-4 -ml-px h-full w-0.5 bg-gray-200"
aria-hidden="true"
/>
{/if}
<div class="relative flex space-x-3">
<div>
<span
class="h-8 w-8 rounded-full bg-blue-600 flex items-center justify-center ring-8 ring-white text-white"
>{i + 1}
</span>
</div>
<div class="min-w-0 flex-1 pt-1.5 flex justify-between space-x-4 w-full">
<div class="w-full">
<span class="text-black">{mod?.summary ?? ''}</span>
<p class="text-sm text-gray-500">
{#if mod?.value?.type == 'script'}
Script at path <a
target="_blank"
href={scriptPathToHref(mod?.value?.path ?? '')}
class="font-medium text-gray-900"
>
<IconedPath path={mod?.value?.path ?? ''} />
</a>
{#if mod?.value?.path?.startsWith('hub/')}
<div>
<button
on:click={async () => {
open[i] = !open[i]
}}
class="mb-2 underline text-black"
>
View code and inputs {open[i] ? '(-)' : '(+)'}</button
>
{#if open[i]}
<div class="border border-black p-2 bg-gray-50 divide-y">
<InputTransformsViewer inputTransforms={mod?.input_transform} />
<div class="w-full h-full mt-6">
<iframe
style="height: 400px;"
class="w-full h-full text-sm"
title="embedded script from hub"
frameborder="0"
src="https://hub.windmill.dev/embed/script/{mod?.value?.path?.substring(
4
)}"
/>
</div>
</div>
{/if}
</div>
{/if}
{:else if mod?.value?.type == 'rawscript'}
<button
on:click={() => (open[i] = !open[i])}
class="mb-2 underline text-black"
>
Raw {mod?.value?.language} script {open[i] ? '(-)' : '(+)'}</button
>
{#if open[i]}
<div
transition:slide
class="border border-black p-2 bg-gray-50 w-full"
>
<InputTransformsViewer inputTransforms={mod?.input_transform} />
<Highlight
language={mod?.value?.language == 'deno' ? typescript : python}
code={mod?.value?.content}
/>
</div>
{/if}
{:else if mod?.value?.type == 'flow'}
Flow at path {mod?.value?.path}
{:else if mod?.value?.type == 'forloopflow'}
For loop over all the elements of the list returned as a result of step {i}:
<svelte:self flow={mod?.value} embedded={true} />
{/if}
</p>
</div>
</div>
</div>
</div>
</li>
{/each}
</ul>
</div>
</TabContent>
<TabContent value="json">
<div class="relative">
<button
on:click={async () => {
await navigator.clipboard.writeText(JSON.stringify(flowFiltered, null, 4))
}}
class="absolute default-secondary-button-v2 bg-white/30 right-0 my-2 ml-4"
>copy content</button
>
<Highlight language={json} code={JSON.stringify(flowFiltered, null, 4)} />
</div>
</TabContent>
<TabContent value="schema">
<div class="my-4" />
<SchemaViewer schema={flow.schema} />
</TabContent>
</svelte:fragment>
</Tabs>
{/if}