mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-04 00:03:02 +00:00
358 lines
9.7 KiB
TypeScript
358 lines
9.7 KiB
TypeScript
// import { getContext } from 'svelte'
|
|
import {
|
|
copyComponent,
|
|
findGridItem,
|
|
findGridItemParentGrid,
|
|
getAllSubgridsAndComponentIds,
|
|
insertNewGridItem
|
|
} from '../appUtils'
|
|
import type { AppEditorContext, AppViewerContext, GridItem } from '../../types'
|
|
import { push } from '$lib/history'
|
|
import { sendUserToast } from '$lib/toast'
|
|
import { gridColumns } from '../../gridUtils'
|
|
import { copyToClipboard } from '$lib/utils'
|
|
import { get } from 'svelte/store'
|
|
|
|
// const { app, selectedComponent, focusedGrid, componentControl } =
|
|
// getContext<AppViewerContext>('AppViewerContext')
|
|
|
|
// const { history, movingcomponents, jobsDrawerOpen } =
|
|
// getContext<AppEditorContext>('AppEditorContext')
|
|
|
|
let tempGridItems: GridItem[] | undefined = undefined
|
|
|
|
const ITEM_TYPE = 'wm-grid-items'
|
|
|
|
function getSortedGridItemsOfChildren(ctx: {
|
|
app: AppViewerContext['app']
|
|
focusedGrid: AppViewerContext['focusedGrid']
|
|
}): GridItem[] {
|
|
const focusedGrid = get(ctx.focusedGrid)
|
|
if (!focusedGrid) {
|
|
return ctx.app.val.grid
|
|
}
|
|
|
|
let subgrids = ctx.app.val.subgrids
|
|
if (!subgrids) {
|
|
return []
|
|
}
|
|
|
|
return subgrids[`${focusedGrid.parentComponentId}-${focusedGrid.subGridIndex}`] ?? []
|
|
}
|
|
|
|
function getGridItems(ctx: {
|
|
app: AppViewerContext['app']
|
|
selectedComponent: AppViewerContext['selectedComponent']
|
|
}): GridItem[] {
|
|
const app = ctx.app.val
|
|
const selectedComponent = get(ctx.selectedComponent)
|
|
if (app.grid.find((item) => item.id === selectedComponent?.[0])) {
|
|
return ctx.app.val.grid
|
|
}
|
|
|
|
if (!app.subgrids) {
|
|
return []
|
|
}
|
|
|
|
return (
|
|
Object.values(app.subgrids ?? {}).find((grid) =>
|
|
grid.find((item) => item.id === selectedComponent?.[0])
|
|
) ?? []
|
|
)
|
|
}
|
|
|
|
export function left(
|
|
event: KeyboardEvent,
|
|
ctx: {
|
|
app: AppViewerContext['app']
|
|
componentControl: AppViewerContext['componentControl']
|
|
selectedComponent: AppViewerContext['selectedComponent']
|
|
}
|
|
) {
|
|
const componentControl = get(ctx.componentControl)
|
|
const selectedComponent = get(ctx.selectedComponent)
|
|
if (!componentControl[selectedComponent?.[0] ?? '']?.left?.()) {
|
|
const sortedGridItems = getGridItems(ctx)
|
|
const currentIndex = sortedGridItems.findIndex(
|
|
(item) => item.id === (selectedComponent?.[0] ?? '')
|
|
)
|
|
|
|
if (currentIndex !== -1 && currentIndex > 0) {
|
|
const left = sortedGridItems[currentIndex - 1]
|
|
|
|
if (left.data.type === 'tablecomponent' && left.data.actionButtons.length >= 1) {
|
|
ctx.selectedComponent.set([left.data.actionButtons[left.data.actionButtons.length - 1].id])
|
|
} else if (
|
|
(left.data.type === 'aggridcomponent' ||
|
|
left.data.type === 'aggridcomponentee' ||
|
|
left.data.type === 'dbexplorercomponent') &&
|
|
Array.isArray(left.data.actions) &&
|
|
left.data.actions.length >= 1
|
|
) {
|
|
ctx.selectedComponent.set([left.data.actions[left.data.actions.length - 1].id])
|
|
} else {
|
|
ctx.selectedComponent.set([left.id])
|
|
}
|
|
}
|
|
}
|
|
|
|
event.preventDefault()
|
|
}
|
|
|
|
export function right(
|
|
event: KeyboardEvent,
|
|
ctx: {
|
|
app: AppViewerContext['app']
|
|
componentControl: AppViewerContext['componentControl']
|
|
selectedComponent: AppViewerContext['selectedComponent']
|
|
}
|
|
) {
|
|
const componentControl = get(ctx.componentControl)
|
|
const selectedComponent = get(ctx.selectedComponent)
|
|
let r = componentControl[selectedComponent?.[0] ?? '']?.right?.()
|
|
|
|
if (typeof r === 'string') {
|
|
ctx.selectedComponent.set([r])
|
|
r = componentControl[r]?.right?.(true)
|
|
}
|
|
|
|
if (!r) {
|
|
const sortedGridItems = getGridItems(ctx)
|
|
const currentIndex = sortedGridItems.findIndex(
|
|
(item) => item.id === (selectedComponent?.[0] ?? '')
|
|
)
|
|
|
|
if (currentIndex !== -1 && currentIndex < sortedGridItems.length - 1) {
|
|
ctx.selectedComponent.set([sortedGridItems[currentIndex + 1].id])
|
|
}
|
|
}
|
|
|
|
event.preventDefault()
|
|
}
|
|
|
|
export function down(
|
|
event: KeyboardEvent,
|
|
ctx: {
|
|
app: AppViewerContext['app']
|
|
componentControl: AppViewerContext['componentControl']
|
|
selectedComponent: AppViewerContext['selectedComponent']
|
|
focusedGrid: AppViewerContext['focusedGrid']
|
|
}
|
|
) {
|
|
const focusedGrid = get(ctx.focusedGrid)
|
|
const selectedComponent = get(ctx.selectedComponent)
|
|
const app = ctx.app.val
|
|
if (!focusedGrid) {
|
|
ctx.selectedComponent.set([getSortedGridItemsOfChildren(ctx)[0]?.id])
|
|
event.preventDefault()
|
|
} else if (app.subgrids) {
|
|
const index = focusedGrid?.subGridIndex ?? 0
|
|
const subgrid = app.subgrids[`${selectedComponent}-${index}`]
|
|
|
|
if (!subgrid || subgrid.length === 0) {
|
|
return
|
|
}
|
|
|
|
if (subgrid) {
|
|
ctx.selectedComponent.set([subgrid[0].id])
|
|
}
|
|
event.preventDefault()
|
|
}
|
|
}
|
|
|
|
export function handleEscape(
|
|
event: KeyboardEvent,
|
|
ctx: {
|
|
selectedComponent: AppViewerContext['selectedComponent']
|
|
focusedGrid: AppViewerContext['focusedGrid']
|
|
}
|
|
) {
|
|
ctx.selectedComponent.set(undefined)
|
|
ctx.focusedGrid.set(undefined)
|
|
event.preventDefault()
|
|
}
|
|
|
|
export function handleArrowUp(
|
|
event: KeyboardEvent,
|
|
ctx: {
|
|
app: AppViewerContext['app']
|
|
selectedComponent: AppViewerContext['selectedComponent']
|
|
focusedGrid: AppViewerContext['focusedGrid']
|
|
}
|
|
) {
|
|
const selectedComponent = get(ctx.selectedComponent)
|
|
if (!selectedComponent) return
|
|
let parentId = findGridItemParentGrid(ctx.app.val, selectedComponent?.[0])?.split('-')[0]
|
|
|
|
if (parentId) {
|
|
ctx.selectedComponent.set([parentId])
|
|
} else {
|
|
ctx.selectedComponent.set(undefined)
|
|
ctx.focusedGrid.set(undefined)
|
|
}
|
|
}
|
|
|
|
export async function handleCopy(
|
|
event: KeyboardEvent,
|
|
ctx: {
|
|
app: AppViewerContext['app']
|
|
selectedComponent: AppViewerContext['selectedComponent']
|
|
jobsDrawerOpen: AppEditorContext['jobsDrawerOpen']
|
|
}
|
|
) {
|
|
const selectedComponent = get(ctx.selectedComponent)
|
|
if (!selectedComponent || get(ctx.jobsDrawerOpen) || window.getSelection()?.toString() != '') {
|
|
return
|
|
}
|
|
tempGridItems = undefined
|
|
const copiedGridItems = selectedComponent
|
|
.map((x) => findGridItem(ctx.app.val, x))
|
|
.filter((x) => x != undefined) as GridItem[]
|
|
|
|
copyGridItemsToClipboard(copiedGridItems, ctx, 'copy')
|
|
}
|
|
|
|
async function copyGridItemsToClipboard(
|
|
items: GridItem[],
|
|
ctx: {
|
|
app: AppViewerContext['app']
|
|
},
|
|
type: 'copy' | 'cut' | undefined = undefined
|
|
) {
|
|
const app = ctx.app.val
|
|
let allSubgrids = {}
|
|
for (let item of items) {
|
|
let subgrids = getAllSubgridsAndComponentIds(app, item.data)[0]
|
|
for (let key of subgrids) {
|
|
allSubgrids[key] = app.subgrids?.[key]
|
|
}
|
|
}
|
|
let success = await copyToClipboard(
|
|
JSON.stringify({ type: ITEM_TYPE, items, subgrids: allSubgrids }),
|
|
false
|
|
)
|
|
|
|
if (!success) {
|
|
sendUserToast('Could not copy to clipboard. Are you in an unsecure context?', true)
|
|
} else if (type) {
|
|
const text = type == 'copy' ? 'Copied' : 'Cut'
|
|
sendUserToast(`${text} ${items.length} component${items.length > 1 ? 's' : ''}`)
|
|
}
|
|
}
|
|
|
|
export function handleCut(
|
|
event: KeyboardEvent,
|
|
ctx: {
|
|
selectedComponent: AppViewerContext['selectedComponent']
|
|
history: AppEditorContext['history']
|
|
app: AppViewerContext['app']
|
|
movingcomponents: AppEditorContext['movingcomponents']
|
|
}
|
|
) {
|
|
const selectedComponent = get(ctx.selectedComponent)
|
|
const app = ctx.app.val
|
|
if (!selectedComponent) {
|
|
return
|
|
}
|
|
ctx.movingcomponents.set(JSON.parse(JSON.stringify(selectedComponent)))
|
|
push(ctx.history, app)
|
|
|
|
let gridItems = selectedComponent
|
|
.map((x) => findGridItem(app, x))
|
|
.filter((x) => x != undefined) as GridItem[]
|
|
copyGridItemsToClipboard(gridItems, ctx, 'cut')
|
|
|
|
if (!gridItems) {
|
|
return
|
|
}
|
|
|
|
// Store the grid item in a temp variable so we can paste it later
|
|
tempGridItems = gridItems
|
|
}
|
|
|
|
export async function handlePaste(
|
|
event: ClipboardEvent,
|
|
ctx: {
|
|
app: AppViewerContext['app']
|
|
selectedComponent: AppViewerContext['selectedComponent']
|
|
focusedGrid: AppViewerContext['focusedGrid']
|
|
componentControl: AppViewerContext['componentControl']
|
|
history: AppEditorContext['history']
|
|
jobsDrawerOpen: AppEditorContext['jobsDrawerOpen']
|
|
movingcomponents: AppEditorContext['movingcomponents']
|
|
}
|
|
) {
|
|
let classes = event.target?.['className']
|
|
if (
|
|
(typeof classes === 'string' && classes.includes('inputarea')) ||
|
|
['INPUT', 'TEXTAREA'].includes(document.activeElement?.tagName!)
|
|
) {
|
|
return
|
|
}
|
|
event.preventDefault()
|
|
const app = ctx.app.val
|
|
const focusedGrid = get(ctx.focusedGrid)
|
|
|
|
push(ctx.history, app)
|
|
ctx.movingcomponents.set(undefined)
|
|
let copiedGridItems: GridItem[] | undefined = undefined
|
|
let subgrids = app.subgrids ?? {}
|
|
const txt = event?.clipboardData?.getData('text')
|
|
if (txt) {
|
|
try {
|
|
const parsed = JSON.parse(txt)
|
|
if (parsed.type == ITEM_TYPE) {
|
|
copiedGridItems = parsed.items
|
|
subgrids = parsed.subgrids ?? subgrids
|
|
} else {
|
|
copiedGridItems = undefined
|
|
}
|
|
} catch {}
|
|
}
|
|
|
|
if (tempGridItems != undefined) {
|
|
for (let tempGridItem of tempGridItems) {
|
|
if (
|
|
focusedGrid &&
|
|
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)
|
|
if (parentGrid) {
|
|
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)
|
|
}
|
|
|
|
const gridItem = tempGridItem
|
|
insertNewGridItem(
|
|
app,
|
|
(id) => ({ ...gridItem.data, id }),
|
|
focusedGrid,
|
|
Object.fromEntries(gridColumns.map((column) => [column, gridItem[column]])),
|
|
tempGridItem.id
|
|
)
|
|
}
|
|
copyGridItemsToClipboard(tempGridItems, ctx)
|
|
ctx.selectedComponent.set(tempGridItems.map((x) => x.id))
|
|
|
|
tempGridItems = undefined
|
|
} else if (copiedGridItems) {
|
|
let nitems: string[] = []
|
|
for (let copiedGridItem of copiedGridItems) {
|
|
let newItem = copyComponent(app, copiedGridItem, focusedGrid, subgrids, [])
|
|
newItem && nitems.push(newItem)
|
|
}
|
|
ctx.selectedComponent.set(nitems.map((x) => x))
|
|
}
|
|
|
|
ctx.app.val = ctx.app.val
|
|
}
|