fix tab selection on app builder

This commit is contained in:
Ruben Fiszel
2025-06-16 14:36:12 +02:00
parent eb0eb80fc2
commit eeb049c0d0
9 changed files with 467 additions and 324 deletions

View File

@@ -343,7 +343,6 @@
on:resultSet={(e) => {
const res = e.detail
if ($initialized?.runnableInitialized?.[fullId] === undefined) {
console.log('resultSet', id)
$initialized.runnableInitialized = {
...($initialized.runnableInitialized ?? {}),
[fullId]: res

View File

@@ -18,6 +18,7 @@
import InitializeComponent from '../helpers/InitializeComponent.svelte'
import { twMerge } from 'tailwind-merge'
import ResolveStyle from '../helpers/ResolveStyle.svelte'
import { deepEqual } from 'fast-equals'
interface Props {
id: string
@@ -121,8 +122,24 @@
}
}
let lastTabs: string[] | undefined = tabs
$effect.pre(() => {
if (!untrack(() => selected)) {
return
}
if (
!deepEqual(
untrack(() => lastTabs),
tabs
)
) {
lastTabs = structuredClone($state.snapshot(tabs))
} else {
return
}
untrack(() => handleTabSelection())
})
$effect.pre(() => {
tabs
selected != undefined && untrack(() => handleTabSelection())
})

View File

@@ -847,6 +847,7 @@
}}
/>
<!-- {$focusedGrid?.parentComponentId} -->
{#if !$userStore?.operator}
{#if $appStore}
<AppEditorHeader

View File

@@ -1,7 +1,8 @@
<script context="module" lang="ts">
<script module lang="ts">
import { writable } from 'svelte/store'
import Popover from '../../Popover.svelte'
import { handleCut, handleCopy } from './component/componentCallbacks.svelte'
interface ContextMenuRegistry {
id: string
close: () => void
@@ -24,7 +25,6 @@
Scissors,
Trash
} from 'lucide-svelte'
import ComponentCallbacks from './component/ComponentCallbacks.svelte'
import { createEventDispatcher, getContext } from 'svelte'
import type { AppEditorContext, AppViewerContext } from '../types'
import DeleteComponent from './settingsPanel/DeleteComponent.svelte'
@@ -34,12 +34,9 @@
import { twMerge } from 'tailwind-merge'
let contextMenuVisible = false
let menuX = 0
let menuY = 0
export let locked: boolean = false
export let fullHeight: boolean = false
let contextMenuVisible = $state(false)
let menuX = $state(0)
let menuY = $state(0)
function handleRightClick(event: MouseEvent) {
event.preventDefault()
@@ -69,21 +66,39 @@
}
}
export let id: string
let componentCallbacks: ComponentCallbacks | undefined = undefined
interface Props {
locked?: boolean
fullHeight?: boolean
id: string
children?: import('svelte').Snippet
}
const { selectedComponent } = getContext<AppViewerContext>('AppViewerContext')
const { movingcomponents, stylePanel } = getContext<AppEditorContext>('AppEditorContext')
let { locked = false, fullHeight = false, id, children }: Props = $props()
const { selectedComponent, focusedGrid, componentControl, app } = getContext<AppViewerContext>(
'AppViewerContext'
) as AppViewerContext
const { movingcomponents, stylePanel, jobsDrawerOpen, history } = getContext<AppEditorContext>(
'AppEditorContext'
) as AppEditorContext
const ctx = {
history,
app,
selectedComponent,
focusedGrid,
componentControl,
movingcomponents,
jobsDrawerOpen
}
const dispatch = createEventDispatcher()
let deleteComponent: DeleteComponent | undefined = undefined
let deleteComponent: DeleteComponent | undefined = $state(undefined)
const menuItems = [
{
label: () => 'Cut',
onClick: () => {
componentCallbacks?.handleCut(new KeyboardEvent('keydown'))
handleCut(new KeyboardEvent('keydown'), ctx)
},
icon: Scissors,
shortcut: `${getModifierKey()}X`,
@@ -92,7 +107,7 @@
{
label: () => 'Copy',
onClick: () => {
componentCallbacks?.handleCopy(new KeyboardEvent('keydown'))
handleCopy(new KeyboardEvent('keydown'), ctx)
},
icon: Copy,
shortcut: `${getModifierKey()}C`
@@ -155,26 +170,25 @@
]
</script>
<ComponentCallbacks bind:this={componentCallbacks} />
<DeleteComponent bind:this={deleteComponent} />
<svelte:window on:click={handleClickOutside} />
<svelte:window onclick={handleClickOutside} />
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div on:click={closeContextMenu} on:contextmenu={handleRightClick} class="h-full w-full">
<!-- svelte-ignore a11y-no-static-element-interactions -->
<slot />
<!-- svelte-ignore a11y_click_events_have_key_events -->
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div onclick={closeContextMenu} oncontextmenu={handleRightClick} class="h-full w-full">
<!-- svelte-ignore a11y_no_static_element_interactions -->
{@render children?.()}
{#if contextMenuVisible}
<Portal name="grid-editor">
<div style="position: fixed; top: {menuY}px; left: {menuX}px; z-index:6000;">
<div class="rounded-md bg-surface border shadow-md divide-y w-64">
<div class="p-1" use:clickOutside={false}>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y_click_events_have_key_events -->
{#each menuItems as item}
<!-- svelte-ignore a11y-no-static-element-interactions -->
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y_no_static_element_interactions -->
<!-- svelte-ignore a11y_click_events_have_key_events -->
<Popover
notClickable
@@ -183,8 +197,8 @@
disablePopup={!item.tooltip}
appearTimeout={800}
>
<svelte:fragment slot="text"
>{item.tooltip?.text}
{#snippet text()}
{item.tooltip?.text}
{#if item.tooltip?.link}
<a href={item.tooltip.link} target="_blank" class="text-blue-300 text-xs">
<div class="flex flex-row gap-2 mt-4">
@@ -192,8 +206,8 @@
<ExternalLink size="16" />
</div>
</a>
{/if}</svelte:fragment
>
{/if}
{/snippet}
<button
class={twMerge(
@@ -203,14 +217,13 @@
item.color === 'blue' && 'text-blue-500',
item.disabled && 'opacity-50 cursor-not-allowed'
)}
on:click={() => {
onclick={() => {
item.onClick()
closeContextMenu()
}}
disabled={item.disabled}
>
<!-- svelte-ignore missing-declaration -->
<svelte:component this={item.icon} class="w-4 h-4" />
<item.icon class="w-4 h-4" />
<span class="ml-2 text-xs">{item.label()}</span>
{#if item.shortcut}

View File

@@ -1,269 +0,0 @@
<script lang="ts">
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'
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(): GridItem[] {
if (!$focusedGrid) {
return $app.grid
}
if (!$app.subgrids) {
return []
}
return $app.subgrids[`${$focusedGrid.parentComponentId}-${$focusedGrid.subGridIndex}`] ?? []
}
function getGridItems(): GridItem[] {
if ($app.grid.find((item) => item.id === $selectedComponent?.[0])) {
return $app.grid
}
if (!$app.subgrids) {
return []
}
return (
Object.values($app.subgrids ?? {}).find((grid) =>
grid.find((item) => item.id === $selectedComponent?.[0])
) ?? []
)
}
export function left(event: KeyboardEvent) {
if (!$componentControl[$selectedComponent?.[0] ?? '']?.left?.()) {
const sortedGridItems = getGridItems()
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) {
$selectedComponent = [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
) {
$selectedComponent = [left.data.actions[left.data.actions.length - 1].id]
} else {
$selectedComponent = [left.id]
}
}
}
event.preventDefault()
}
export function right(event: KeyboardEvent) {
let r = $componentControl[$selectedComponent?.[0] ?? '']?.right?.()
if (typeof r === 'string') {
$selectedComponent = [r]
r = $componentControl[r]?.right?.(true)
}
if (!r) {
const sortedGridItems = getGridItems()
const currentIndex = sortedGridItems.findIndex(
(item) => item.id === ($selectedComponent?.[0] ?? '')
)
if (currentIndex !== -1 && currentIndex < sortedGridItems.length - 1) {
$selectedComponent = [sortedGridItems[currentIndex + 1].id]
}
}
event.preventDefault()
}
export function down(event: KeyboardEvent) {
if (!$focusedGrid) {
$selectedComponent = [getSortedGridItemsOfChildren()[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) {
$selectedComponent = [subgrid[0].id]
}
event.preventDefault()
}
}
export function handleEscape(event: KeyboardEvent) {
$selectedComponent = undefined
$focusedGrid = undefined
event.preventDefault()
}
export function handleArrowUp(event: KeyboardEvent) {
if (!$selectedComponent) return
let parentId = findGridItemParentGrid($app, $selectedComponent?.[0])?.split('-')[0]
if (parentId) {
$selectedComponent = [parentId]
} else {
$selectedComponent = undefined
$focusedGrid = undefined
}
}
export async function handleCopy(event: KeyboardEvent) {
if (!$selectedComponent || $jobsDrawerOpen || window.getSelection()?.toString() != '') {
return
}
tempGridItems = undefined
const copiedGridItems = $selectedComponent
.map((x) => findGridItem($app, x))
.filter((x) => x != undefined) as GridItem[]
copyGridItemsToClipboard(copiedGridItems, 'copy')
}
async function copyGridItemsToClipboard(
items: GridItem[],
type: 'copy' | 'cut' | undefined = undefined
) {
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) {
if (!$selectedComponent) {
return
}
$movingcomponents = JSON.parse(JSON.stringify($selectedComponent))
push(history, $app)
let gridItems = $selectedComponent
.map((x) => findGridItem($app, x))
.filter((x) => x != undefined) as GridItem[]
copyGridItemsToClipboard(gridItems, '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) {
let classes = event.target?.['className']
if (
(typeof classes === 'string' && classes.includes('inputarea')) ||
['INPUT', 'TEXTAREA'].includes(document.activeElement?.tagName!)
) {
return
}
event.preventDefault()
push(history, $app)
$movingcomponents = 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)
$selectedComponent = 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)
}
$selectedComponent = nitems.map((x) => x)
}
$app = $app
}
</script>

View File

@@ -1,9 +1,34 @@
<script lang="ts">
import ComponentCallbacks from './ComponentCallbacks.svelte'
let componentCallbacks: ComponentCallbacks | undefined = undefined
import { getContext } from 'svelte'
import {
handleEscape,
handlePaste,
handleArrowUp,
handleCut,
handleCopy,
down,
right,
left
} from './componentCallbacks.svelte'
import type { AppEditorContext, AppViewerContext } from '../../types'
const { history, movingcomponents, jobsDrawerOpen } = getContext<AppEditorContext>(
'AppEditorContext'
) as AppEditorContext
const { app, selectedComponent, focusedGrid, componentControl } = getContext<AppViewerContext>(
'AppViewerContext'
) as AppViewerContext
const ctx = {
history,
app,
selectedComponent,
focusedGrid,
componentControl,
movingcomponents,
jobsDrawerOpen
}
function keydown(event: KeyboardEvent) {
// console.log('keydown', $focusedGrid, $selectedComponent)
// Ignore keydown events if the user is typing in monaco
let classes = event.target?.['className']
if (
@@ -14,38 +39,38 @@
}
switch (event.key) {
case 'Escape':
componentCallbacks?.handleEscape(event)
handleEscape(event, ctx)
break
case 'ArrowUp': {
componentCallbacks?.handleArrowUp(event)
handleArrowUp(event, ctx)
break
}
case 'ArrowDown': {
componentCallbacks?.down(event)
down(event, ctx)
break
}
case 'ArrowRight': {
componentCallbacks?.right(event)
right(event, ctx)
break
}
case 'ArrowLeft': {
componentCallbacks?.left(event)
left(event, ctx)
break
}
case 'c':
if (event.ctrlKey || event.metaKey) {
componentCallbacks?.handleCopy(event)
handleCopy(event, ctx)
}
break
case 'x':
if (event.ctrlKey || event.metaKey) {
componentCallbacks?.handleCut(event)
handleCut(event, ctx)
}
break
@@ -55,6 +80,4 @@
}
</script>
<ComponentCallbacks bind:this={componentCallbacks} />
<svelte:window on:keydown={keydown} on:paste={componentCallbacks?.handlePaste} />
<svelte:window onkeydown={keydown} onpaste={(e) => handlePaste(e, ctx)} />

View File

@@ -0,0 +1,357 @@
// 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 get(ctx.app).grid
}
let subgrids = get(ctx.app).subgrids
if (!subgrids) {
return []
}
return subgrids[`${focusedGrid.parentComponentId}-${focusedGrid.subGridIndex}`] ?? []
}
function getGridItems(ctx: {
app: AppViewerContext['app']
selectedComponent: AppViewerContext['selectedComponent']
}): GridItem[] {
const app = get(ctx.app)
const selectedComponent = get(ctx.selectedComponent)
if (app.grid.find((item) => item.id === selectedComponent?.[0])) {
return get(ctx.app).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 = get(ctx.app)
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(get(ctx.app), 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(get(ctx.app), 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 = get(ctx.app)
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 = get(ctx.app)
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 = get(ctx.app)
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.update((x) => x)
}

View File

@@ -23,14 +23,17 @@
}
let {
tabs = $bindable([]),
disabledTabs = $bindable([]),
tabs = $bindable(undefined),
disabledTabs = $bindable(undefined),
canDisableTabs = false,
word = 'Tab',
component = $bindable()
}: Props = $props()
$effect.pre(() => {
if (tabs == undefined) {
tabs = []
}
if (disabledTabs == undefined) {
disabledTabs = [
{ type: 'static', value: false, fieldType: 'boolean' },
@@ -40,7 +43,7 @@
})
let items = $state.raw(
tabs.map((tab, index) => {
(tabs ?? []).map((tab, index) => {
return { value: tab, id: generateRandomString(), originalIndex: index }
})
)
@@ -134,7 +137,7 @@
const newDisabledTabs: RichConfiguration[] = []
for (let i = 0; i < items.length; i++) {
newDisabledTabs.push(disabledTabs[items[i].originalIndex])
disabledTabs && newDisabledTabs.push(disabledTabs[items[i].originalIndex])
}
disabledTabs = newDisabledTabs

View File

@@ -185,9 +185,8 @@
if (detail.id && !selectedIds?.includes(detail.id)) {
nselectedIds = [detail.id, ...(selectedIds ?? [])]
}
sortedItems = citems
for (let id of nselectedIds) {
let activeItem = getItemById(id, sortedItems)
let activeItem = getItemById(id, citems)
if (activeItem && getComputedCols) {
activeItem = {
@@ -200,7 +199,7 @@
if ($isCtrlOrMetaPressedStore) {
if ($componentDraggedParentIdStore === $overlappedStore) {
const fixedContainer = sortedItems.map((item) => {
const fixedContainer = citems.map((item) => {
if (isContainer(item.data['type'])) {
initialFixedStates.set(item.id, {
item3Fixed: item[3].fixed,
@@ -231,7 +230,7 @@
sortedItems = nitems
}
} else {
let { items: nitems } = moveItem(activeItem, sortedItems, getComputedCols)
let { items: nitems } = moveItem(activeItem, citems, getComputedCols)
sortedItems = nitems
}