Files
windmill/frontend/src/lib/components/tutorials/utils.ts

337 lines
6.9 KiB
TypeScript

import type { FlowModule, OpenFlow } from '$lib/gen'
import { deepEqual } from 'fast-equals'
import { findGridItem } from '../apps/editor/appUtils'
import type { App } from '../apps/types'
export function setInputBySelector(selector: string, value: string) {
const input = document.querySelector(selector) as HTMLInputElement
if (input) {
input.value = value
input.dispatchEvent(new Event('input', { bubbles: true }))
}
}
export function clickButtonBySelector(selector: string) {
const button = document.querySelector(selector) as HTMLButtonElement
if (button) {
button.click()
}
}
export function clickFirstButtonBySelector(selector: string) {
const buttons = document.querySelector(selector)
const button = buttons?.childNodes[0] as HTMLButtonElement
if (button) {
button.click()
}
}
export function triggerAddFlowStep(index: number) {
const button = document.querySelector(`#flow-editor-add-step-${index}`) as HTMLButtonElement
if (button) {
button.parentElement?.dispatchEvent(new PointerEvent('pointerdown', { bubbles: true }))
}
}
export function selectFlowStepKind(index: number) {
const button = document.querySelector(
`#flow-editor-insert-module > div > button:nth-child(${index})`
) as HTMLButtonElement
if (button) {
button?.dispatchEvent(new PointerEvent('pointerdown', { bubbles: true }))
}
}
export function selectOptionsBySelector(selector: string, value: string) {
const select = document.querySelector(selector) as HTMLSelectElement
if (select) {
select.value = value
select.dispatchEvent(new Event('change', { bubbles: true }))
}
}
export function isFlowTainted(flow: OpenFlow) {
return (
flow.value.modules.length > 0 || Object.keys((flow?.schema?.properties as any) ?? {}).length > 0
)
}
const emptyApp = {
grid: [
{
'3': {
fixed: false,
x: 0,
y: 0,
fullHeight: false,
w: 6,
h: 2
},
'12': {
fixed: false,
x: 0,
y: 0,
fullHeight: false,
w: 12,
h: 2
},
data: {
type: 'containercomponent',
configuration: {},
customCss: {
container: {
class: '!p-0',
style: ''
}
},
actions: [],
numberOfSubgrids: 1,
id: 'a'
},
id: 'a'
}
],
fullscreen: false,
unusedInlineScripts: [],
hiddenInlineScripts: [],
theme: {
type: 'path',
path: 'f/app_themes/theme_0'
},
subgrids: {
'a-0': [
{
'3': {
fixed: false,
x: 0,
y: 0,
fullHeight: false,
w: 6,
h: 1
},
'12': {
fixed: false,
x: 0,
y: 0,
fullHeight: false,
w: 6,
h: 1
},
data: {
type: 'textcomponent',
configuration: {
style: {
type: 'static',
value: 'Body'
},
copyButton: {
type: 'static',
value: false
},
tooltip: {
type: 'evalv2',
value: '',
fieldType: 'text',
expr: '`Author: ${ctx.author}`',
connections: [
{
componentId: 'ctx',
id: 'author'
}
]
},
disableNoText: {
type: 'static',
value: true,
fieldType: 'boolean'
}
},
componentInput: {
type: 'templatev2',
fieldType: 'template',
eval: '${ctx.summary}',
connections: [
{
id: 'summary',
componentId: 'ctx'
}
]
},
customCss: {
text: {
class: 'text-xl font-semibold whitespace-nowrap truncate',
style: ''
},
container: {
class: '',
style: ''
}
},
actions: [],
horizontalAlignment: 'left',
verticalAlignment: 'center',
id: 'b'
},
id: 'b'
},
{
'3': {
fixed: false,
x: 0,
y: 1,
fullHeight: false,
w: 3,
h: 1
},
'12': {
fixed: false,
x: 6,
y: 0,
fullHeight: false,
w: 6,
h: 1
},
data: {
type: 'recomputeallcomponent',
configuration: {},
actions: [],
menuItems: [],
horizontalAlignment: 'right',
verticalAlignment: 'center',
id: 'c'
},
id: 'c'
}
]
},
hideLegacyTopBar: true,
norefreshbar: false
}
export function isAppTainted(app: App) {
if (app.hideLegacyTopBar === true) {
// An empty app should have only have a topbar and no hidden inline scripts
if (Array.isArray(app.hiddenInlineScripts) && app.hiddenInlineScripts?.length > 0) {
return true
}
// New apps have only a single component which is the topbar
if (Array.isArray(app.grid) && app.grid.length > 1) {
return true
}
// Check if the current app is different from an empty app
return !deepEqual(app, emptyApp)
} else {
// For older apps,
return !(app.grid.length === 0 && app.hiddenInlineScripts?.length === 0)
}
}
export function updateFlowModuleById(
flow: OpenFlow,
id: string,
callback: (module: FlowModule) => void
) {
const dfs = (modules: FlowModule[]) => {
for (const module of modules) {
if (module.id === id) {
callback(module)
return
}
if (module.value.type === 'forloopflow') {
dfs(module.value.modules)
} else if (module.value.type === 'branchone') {
module.value.branches.forEach((branch) => dfs(branch.modules))
} else if (module.value.type === 'branchall') {
module.value.branches.forEach((branch) => dfs(branch.modules))
}
}
}
dfs(flow.value.modules)
flow = flow
}
export function updateBackgroundRunnableCode(app: App, index: number, newCode: string) {
const script = app.hiddenInlineScripts[index]
if (script.type === 'runnableByName' && script.inlineScript) {
script.inlineScript.content = newCode
}
app = app
}
export function updateInlineRunnableCode(app: App, componentId: string, newCode: string) {
const gridItem = findGridItem(app, componentId)
if (gridItem?.data.componentInput?.type === 'runnable') {
if (
gridItem.data.componentInput.runnable?.type === 'runnableByName' &&
gridItem.data.componentInput.runnable.inlineScript
) {
gridItem.data.componentInput.runnable.inlineScript.content = newCode
}
}
app = app
}
export function connectComponentSourceToOutput(app: App, componentId: string, targetId: string) {
const gridItem = findGridItem(app, componentId)
if (gridItem) {
gridItem.data.componentInput = {
type: 'evalv2',
fieldType: 'object',
expr: `${targetId}.result`,
connections: [
{
componentId: targetId,
id: 'result'
}
]
}
}
app = app
}
export function connectInlineRunnableInputToComponentOutput(
app: App,
sourceComponentId: string,
sourceField: string,
targetComponentId: string,
targetField: string,
fieldType: string = 'text'
) {
const gridItem = findGridItem(app, sourceComponentId)
if (gridItem?.data.componentInput?.type === 'runnable') {
// @ts-ignore
gridItem.data.componentInput.fields = {
[sourceField]: {
type: 'evalv2',
expr: `${targetComponentId}.${targetField}`,
fieldType: fieldType,
connections: [
{
componentId: targetComponentId,
id: targetField
}
]
}
}
}
}