From f71ac4b8f4be199aa3e4f770b7dedf2774e7bc1d Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Mon, 5 Jun 2023 03:18:46 +0200 Subject: [PATCH] feat(apps): copy paste across apps --- .../apps/components/display/AppText.svelte | 20 +--- .../apps/components/helpers/InputValue.svelte | 1 - .../helpers/NonRunnableComponent.svelte | 2 +- .../components/apps/editor/AppEditor.svelte | 1 - .../component/ComponentNavigation.svelte | 94 ++++++++++++++----- .../settingsPanel/ComponentPanel.svelte | 8 +- frontend/src/lib/components/apps/types.ts | 1 - 7 files changed, 73 insertions(+), 54 deletions(-) diff --git a/frontend/src/lib/components/apps/components/display/AppText.svelte b/frontend/src/lib/components/apps/components/display/AppText.svelte index 6ac162861c..00f630132e 100644 --- a/frontend/src/lib/components/apps/components/display/AppText.svelte +++ b/frontend/src/lib/components/apps/components/display/AppText.svelte @@ -3,7 +3,6 @@ import Tooltip from '$lib/components/Tooltip.svelte' import { Clipboard } from 'lucide-svelte' import { getContext } from 'svelte' - import { get } from 'svelte/store' import { twMerge } from 'tailwind-merge' import { copyToClipboard } from '../../../../utils' import Button from '../../../common/button/Button.svelte' @@ -11,12 +10,7 @@ import { initConfig, initOutput } from '../../editor/appUtils' import { components } from '../../editor/component' import type { AppInput } from '../../inputType' - import type { - AppEditorContext, - AppViewerContext, - ComponentCustomCSS, - RichConfigurations - } from '../../types' + import type { AppViewerContext, ComponentCustomCSS, RichConfigurations } from '../../types' import AlignWrapper from '../helpers/AlignWrapper.svelte' import ResolveConfig from '../helpers/ResolveConfig.svelte' import RunnableWrapper from '../helpers/RunnableWrapper.svelte' @@ -39,8 +33,6 @@ const { app, worldStore, mode, componentControl } = getContext('AppViewerContext') - const editorcontext = getContext('AppEditorContext') - let result: string | undefined = undefined if (componentInput?.type == 'template' && !isCodeInjection(componentInput.eval)) { @@ -133,7 +125,7 @@ function onInput(e: Event) { const target = e.target as HTMLTextAreaElement - if (target.value) { + if (target.value != undefined) { $componentControl[id]?.setCode?.(target.value) autosize() } @@ -210,14 +202,6 @@ componentInput?.type == 'template' ? 'cursor-text' : ''}" - on:click={() => { - if ($mode === 'dnd' && componentInput?.type == 'template') { - let ontextfocus = editorcontext?.ontextfocus - if (ontextfocus) { - get(ontextfocus)?.() - } - } - }} > { - console.log('template') let nvalue = await getValue(lastInput) if (!deepEqual(nvalue, value)) { value = nvalue diff --git a/frontend/src/lib/components/apps/components/helpers/NonRunnableComponent.svelte b/frontend/src/lib/components/apps/components/helpers/NonRunnableComponent.svelte index 17cd9c51ce..200e3e203f 100644 --- a/frontend/src/lib/components/apps/components/helpers/NonRunnableComponent.svelte +++ b/frontend/src/lib/components/apps/components/helpers/NonRunnableComponent.svelte @@ -23,7 +23,7 @@ outputs?.result?.set(v, true) } - $: result && outputs && setOutput(result) + $: result != undefined && outputs && setOutput(result) diff --git a/frontend/src/lib/components/apps/editor/AppEditor.svelte b/frontend/src/lib/components/apps/editor/AppEditor.svelte index ef4b74e765..cf2f858b5b 100644 --- a/frontend/src/lib/components/apps/editor/AppEditor.svelte +++ b/frontend/src/lib/components/apps/editor/AppEditor.svelte @@ -111,7 +111,6 @@ setContext('AppEditorContext', { history, pickVariableCallback, - ontextfocus: writable(undefined), movingcomponents: writable(undefined), selectedComponentInEditor: writable(undefined) }) diff --git a/frontend/src/lib/components/apps/editor/component/ComponentNavigation.svelte b/frontend/src/lib/components/apps/editor/component/ComponentNavigation.svelte index 233c1f8e34..e2020b8292 100644 --- a/frontend/src/lib/components/apps/editor/component/ComponentNavigation.svelte +++ b/frontend/src/lib/components/apps/editor/component/ComponentNavigation.svelte @@ -10,6 +10,7 @@ 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') @@ -17,8 +18,8 @@ const { history, movingcomponents } = getContext('AppEditorContext') let tempGridItems: GridItem[] | undefined = undefined - let copiedGridItems: GridItem[] | undefined = undefined + const ITEM_TYPE = 'wm-grid-items' function getSortedGridItemsOfChildren(): GridItem[] { if (!$focusedGrid) { return $app.grid @@ -127,14 +128,33 @@ } } - function handleCopy(event: KeyboardEvent) { + async function handleCopy(event: KeyboardEvent) { if (!$selectedComponent) { return } tempGridItems = undefined - copiedGridItems = $selectedComponent + const copiedGridItems = $selectedComponent .map((x) => findGridItem($app, x)) .filter((x) => x != undefined) as GridItem[] + + copyGridItemsToClipboard(copiedGridItems) + } + + function copyGridItemsToClipboard(items: GridItem[]) { + 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 = 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) + } } function handleCut(event: KeyboardEvent) { @@ -147,7 +167,7 @@ let gridItems = $selectedComponent .map((x) => findGridItem($app, x)) .filter((x) => x != undefined) as GridItem[] - copiedGridItems = gridItems + copyGridItemsToClipboard(gridItems) if (!gridItems) { return @@ -157,9 +177,34 @@ tempGridItems = gridItems } - function handlePaste(event: KeyboardEvent) { + 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 + console.log('subgrids', subgrids) + } else { + copiedGridItems = undefined + } + } catch {} + } + if (tempGridItems != undefined) { for (let tempGridItem of tempGridItems) { if ( @@ -190,14 +235,14 @@ tempGridItem.id ) } - copiedGridItems = tempGridItems + copyGridItemsToClipboard(tempGridItems) $selectedComponent = tempGridItems.map((x) => x.id) tempGridItems = undefined } else if (copiedGridItems) { let nitems: string[] = [] for (let copiedGridItem of copiedGridItems) { - let newItem = copyComponent(copiedGridItem, $focusedGrid) + let newItem = copyComponent(copiedGridItem, $focusedGrid, subgrids) newItem && nitems.push(newItem) } $selectedComponent = nitems.map((x) => x) @@ -206,7 +251,12 @@ $app = $app } - function copyComponent(item: GridItem, parentGrid: FocusedGrid | undefined, doNotVisit?: string) { + function copyComponent( + item: GridItem, + parentGrid: FocusedGrid | undefined, + subgrids: Record, + doNotVisit?: string + ) { if (item.id === doNotVisit) { return } @@ -217,17 +267,17 @@ Object.fromEntries(gridColumns.map((column) => [column, item[column]])) ) - if ($app.subgrids && item.data.numberOfSubgrids) { - for (let i = 0; i < item.data.numberOfSubgrids; i++) { - $app.subgrids[`${item.id}-${i}`].forEach((subgridItem) => { - copyComponent( - subgridItem, - { parentComponentId: newItem, subGridIndex: i }, - doNotVisit ?? newItem - ) - }) - } + for (let i = 0; i < (item?.data?.numberOfSubgrids ?? 0); i++) { + subgrids[`${item.id}-${i}`].forEach((subgridItem) => { + copyComponent( + subgridItem, + { parentComponentId: newItem, subGridIndex: i }, + subgrids, + doNotVisit ?? newItem + ) + }) } + return newItem } @@ -271,12 +321,6 @@ } break - case 'v': - if (event.ctrlKey || event.metaKey) { - handlePaste(event) - } - break - case 'x': if (event.ctrlKey || event.metaKey) { handleCut(event) @@ -289,4 +333,4 @@ } - + diff --git a/frontend/src/lib/components/apps/editor/settingsPanel/ComponentPanel.svelte b/frontend/src/lib/components/apps/editor/settingsPanel/ComponentPanel.svelte index 4f3a8a989e..4d3dfea1a1 100644 --- a/frontend/src/lib/components/apps/editor/settingsPanel/ComponentPanel.svelte +++ b/frontend/src/lib/components/apps/editor/settingsPanel/ComponentPanel.svelte @@ -37,8 +37,6 @@ export let noGrid = false export let duplicateMoveAllowed = true - let editor: TemplateEditor | undefined = undefined - const { app, runnableComponents, @@ -52,10 +50,7 @@ componentControl } = getContext('AppViewerContext') - const { history, ontextfocus, movingcomponents } = - getContext('AppEditorContext') - - $: editor && ($ontextfocus = () => editor?.focus()) + const { history, movingcomponents } = getContext('AppEditorContext') function removeGridElement() { push(history, $app) @@ -174,7 +169,6 @@ {:else if componentSettings.item.data.componentInput.type === 'template' && componentSettings.item.data.componentInput !== undefined}
| undefined pickVariableCallback: Writable<((path: string) => void) | undefined> - ontextfocus: Writable<(() => void) | undefined> selectedComponentInEditor: Writable movingcomponents: Writable }