Files
windmill/frontend/src/lib/components/flows/map/FlowModuleSchemaMap.svelte
2023-01-26 01:06:53 +01:00

173 lines
4.8 KiB
Svelte

<script lang="ts">
import type { FlowEditorContext } from '../types'
import { getContext } from 'svelte'
import {
createBranchAll,
createBranches,
createLoop,
deleteFlowStateById,
emptyModule,
idMutex
} from '$lib/components/flows/flowStateUtils'
import { flowStateStore, type FlowModuleState } from '../flowState'
import type { FlowModule } from '$lib/gen'
import FlowErrorHandlerItem from './FlowErrorHandlerItem.svelte'
import RemoveStepConfirmationModal from '../content/RemoveStepConfirmationModal.svelte'
import { emptyFlowModuleState } from '../utils'
import MapItem from './MapItem.svelte'
import FlowSettingsItem from './FlowSettingsItem.svelte'
import FlowInputsItem from './FlowInputsItem.svelte'
import InsertModuleButton from './InsertModuleButton.svelte'
import { slide } from 'svelte/transition'
import FlowConstantsItem from './FlowConstantsItem.svelte'
export let root: boolean = false
export let modules: FlowModule[] | undefined
export let parentType: 'flow' | 'forloop' | 'branchall' | 'branchone' | undefined = undefined
let indexToRemove: number | undefined = undefined
const { select, selectedId } = getContext<FlowEditorContext>('FlowEditorContext')
async function insertNewModuleAtIndex(
index: number,
kind: 'script' | 'forloop' | 'branchone' | 'branchall' | 'flow' | 'trigger' | 'approval' | 'end'
): Promise<void> {
await idMutex.runExclusive(async () => {
var module = emptyModule(kind == 'flow')
var state = emptyFlowModuleState()
if (kind == 'forloop') {
;[module, state] = await createLoop(module.id)
} else if (kind == 'branchone') {
;[module, state] = await createBranches(module.id)
} else if (kind == 'branchall') {
;[module, state] = await createBranchAll(module.id)
}
const flowModule = module
if (!modules) return
modules.splice(index, 0, flowModule)
modules = modules
$flowStateStore[flowModule.id] = state
if (kind == 'trigger') {
flowModule.summary = 'Trigger'
} else if (kind == 'approval') {
flowModule.summary = 'Approval'
} else if (kind == 'end') {
flowModule.summary = 'Terminate flow'
flowModule.stop_after_if = { skip_if_stopped: false, expr: 'true' }
}
select(flowModule.id)
})
}
function removeAtIndex(index: number): void {
if (!modules) return
const [removedModule] = modules.splice(index, 1)
modules = modules
const leaves = findLeaves(removedModule)
leaves.forEach((leafId: string) => deleteFlowStateById(leafId))
select('settings-graph')
}
function findLeaves(flowModule: FlowModule): string[] {
const id = flowModule.id
if (flowModule.value.type === 'forloopflow') {
return [id, ...flowModule.value.modules.map((fm) => findLeaves(fm)).flat()]
}
if (flowModule.value.type === 'branchall') {
return [
id,
...flowModule.value.branches
.map((branch) => branch.modules.map((mod) => findLeaves(mod)).flat())
.flat()
]
}
if (flowModule.value.type === 'branchone') {
return [
id,
...flowModule.value.branches
.map((branch) => {
return branch.modules.map((mod) => findLeaves(mod)).flat()
})
.flat(),
...flowModule.value.default.map((mod) => findLeaves(mod)).flat()
]
}
return [id]
}
$: confirmationModalOpen = indexToRemove !== undefined
</script>
<div class="flex flex-col h-full relative">
{#if root}
<div
class="z-10 sticky inline-flex flex-col gap-2 top-0 bg-gray-50 flex-initial p-2 items-center border-b border-gray-300"
>
<FlowSettingsItem />
<FlowConstantsItem />
</div>
{/if}
<ul class="w-full flex-auto {root ? 'px-2 pb-2 pt-3' : ''} py-1">
{#if root}
<li>
<FlowInputsItem />
</li>
{/if}
{#if modules}
{#each modules as mod, index (mod.id ?? index)}
<div transition:slide|local>
<MapItem
{index}
bind:mod
on:delete={(event) => {
if (event.detail.detail.shiftKey || mod.value.type === 'identity') {
removeAtIndex(index)
} else {
indexToRemove = index
}
}}
on:insert={(e) => {
insertNewModuleAtIndex(index, e.detail)
}}
/>
</div>
{/each}
{/if}
{#if !modules || modules[modules?.length - 1]?.summary != 'Terminate flow'}
<InsertModuleButton
stop={!root && parentType == 'branchone'}
trigger={root && modules?.length == 0}
on:new={(e) => insertNewModuleAtIndex(modules?.length ?? 0, e.detail)}
/>
{:else}
<div class="my-2" />
{/if}
</ul>
{#if root}
<div class="sticky bottom-0 bg-gray-50 flex-none px-4 py-1 pb-2 border-t">
<FlowErrorHandlerItem />
</div>
{/if}
</div>
<RemoveStepConfirmationModal
bind:open={confirmationModalOpen}
on:canceled={() => {
indexToRemove = undefined
}}
on:confirmed={() => {
if (indexToRemove !== undefined) {
removeAtIndex(indexToRemove)
indexToRemove = undefined
}
$selectedId = 'settings-graph'
}}
/>