App editor improvements (#2328)

* feat(frontend): add zoomable app editor

* feat(frontend): add viewport zoom

* fix(frontend): Fix build

* improvements

---------

Co-authored-by: Ruben Fiszel <ruben@rubenfiszel.com>
This commit is contained in:
Faton Ramadani
2023-09-26 02:08:08 +02:00
committed by GitHub
parent 5bd3f9fe8a
commit 47730131be
14 changed files with 120 additions and 79 deletions
@@ -62,7 +62,11 @@
let resourceTypes: string[] | undefined = undefined
getResourceTypes()
async function loadResourceTypes() {
resourceTypes = await getResourceTypes()
}
loadResourceTypes()
</script>
<div class="w-full pt-2">
+27 -26
View File
@@ -18,7 +18,7 @@
export let size: 'sm' | 'xs' = 'sm'
const id = (Math.random() + 1).toString(36).substring(10)
const dispatch = createEventDispatcher()
const bothOptions = Boolean(options.left) && Boolean(options.right);
const bothOptions = Boolean(options.left) && Boolean(options.right)
</script>
<span class="{$$props.class} z-auto">
@@ -29,20 +29,21 @@
: 'cursor-pointer'}"
>
{#if Boolean(options?.left)}
<span
class={twMerge(
'mr-2 font-medium duration-50',
bothOptions ? (checked ? 'text-disabled' : 'text-primary') : 'text-primary',
size === 'xs' ? 'text-xs' : 'text-sm',
textClass
)}
style={textStyle}
>
{options?.left}
</span>
<span
class={twMerge(
'mr-2 font-medium duration-50',
bothOptions ? (checked ? 'text-disabled' : 'text-primary') : 'text-primary',
size === 'xs' ? 'text-xs' : 'text-sm',
textClass
)}
style={textStyle}
>
{options?.left}
</span>
{/if}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div class="relative" on:click|stopPropagation>
<input
on:focus
@@ -67,20 +68,20 @@
/>
</div>
{#if Boolean(options?.right)}
<span
class={twMerge(
'ml-2 font-medium duration-50',
bothOptions ? (checked ? 'text-primary' : 'text-disabled') : 'text-primary',
size === 'xs' ? 'text-xs' : 'text-sm',
textClass
)}
style={textStyle}
>
{options?.right}
{#if options?.rightTooltip}
<Tooltip>{options?.rightTooltip}</Tooltip>
{/if}
</span>
<span
class={twMerge(
'ml-2 font-medium duration-50',
bothOptions ? (checked ? 'text-primary' : 'text-disabled') : 'text-primary',
size === 'xs' ? 'text-xs' : 'text-sm',
textClass
)}
style={textStyle}
>
{options?.right}
{#if options?.rightTooltip}
<Tooltip>{options?.rightTooltip}</Tooltip>
{/if}
</span>
{/if}
</label>
</span>
@@ -106,7 +106,7 @@
<InputValue key="disable" {id} input={disableTab} bind:value={resolvedDisabledTabs[index]} />
{/each}
<div class={resolvedConfig.tabsKind == 'sidebar' ? 'flex gap-4 w-full' : 'w-full'}>
<div class={resolvedConfig.tabsKind == 'sidebar' ? 'flex gap-4 w-full h-full' : 'w-full'}>
{#if !resolvedConfig.tabsKind || resolvedConfig.tabsKind == 'tabs' || (resolvedConfig.tabsKind == 'invisibleOnView' && $mode == 'dnd')}
<div bind:clientHeight={tabHeight}>
<Tabs
@@ -36,7 +36,7 @@
import VariableEditor from '$lib/components/VariableEditor.svelte'
import { VariableService, type Policy } from '$lib/gen'
import { initHistory } from '$lib/history'
import { Component, Paintbrush, Plus } from 'lucide-svelte'
import { Component, Minus, Paintbrush, Plus } from 'lucide-svelte'
import { findGridItem, findGridItemParentGrid } from './appUtils'
import ComponentNavigation from './component/ComponentNavigation.svelte'
import CssSettings from './componentsPanel/CssSettings.svelte'
@@ -130,12 +130,15 @@
previewTheme
})
let scale = writable(100)
setContext<AppEditorContext>('AppEditorContext', {
history,
pickVariableCallback,
movingcomponents: writable(undefined),
selectedComponentInEditor: writable(undefined),
jobsDrawerOpen: writable(false)
jobsDrawerOpen: writable(false),
scale
})
let timeout: NodeJS.Timeout | undefined = undefined
@@ -443,17 +446,48 @@
$focusedGrid = undefined
}}
class={twMerge(
'bg-surface h-full w-full relative',
'bg-surface-secondary h-full w-full relative',
$appStore.css?.['app']?.['viewer']?.class,
'wm-app-viewer'
)}
style={$appStore.css?.['app']?.['viewer']?.style}
>
<div class="absolute bottom-4 left-4 z-50">
<div class="flex flex-row gap-2 text-xs items-center">
<Button
color="light"
variant="border"
size="xs2"
disabled={$scale <= 30}
on:click={() => {
$scale -= 10
}}
>
<Minus size={14} />
</Button>
{$scale}
<Button
color="light"
variant="border"
size="xs2"
disabled={$scale >= 100}
on:click={() => {
$scale += 10
}}
>
<Plus size={14} />
</Button>
</div>
</div>
<div id="app-editor-top-level-drawer" />
<div
class="absolute inset-0 h-full w-full surface-secondary bg-[radial-gradient(#dbdbdb_1px,transparent_1px)] dark:bg-[radial-gradient(#666666_1px,transparent_1px)] [background-size:16px_16px]"
/>
<div
class={classNames(
'bg-surface-secondary/80 relative mx-auto w-full h-full overflow-auto',
app.fullscreen ? '' : 'max-w-6xl'
'relative mx-auto w-full h-full overflow-auto',
$appStore.fullscreen ? '' : 'max-w-6xl border-x'
)}
>
{#if $appStore.grid}
@@ -485,7 +519,6 @@
size="xs"
class="h-full"
on:pointerdown={() => {
console.log('click', $cssEditorOpen)
if ($cssEditorOpen) {
$cssEditorOpen = false
selectedTab = 'insert'
@@ -189,9 +189,9 @@
<div
style={app.css?.['app']?.['grid']?.style}
class={twMerge(
'px-4 pt-4 pb-2 overflow-visible',
'p-2 overflow-visible',
app.css?.['app']?.['grid']?.class ?? '',
'wm-app-grid'
'wm-app-grid subgrid'
)}
bind:clientWidth={$parentWidth}
>
@@ -32,7 +32,7 @@
allIdsInPath
} = getContext<AppViewerContext>('AppViewerContext')
const { history } = getContext<AppEditorContext>('AppEditorContext')
const { history, scale } = getContext<AppEditorContext>('AppEditorContext')
let previousSelectedIds: string[] | undefined = undefined
$: if (!deepEqual(previousSelectedIds, $selectedComponent)) {
@@ -73,9 +73,9 @@
<div
style={$app.css?.['app']?.['grid']?.style}
class={twMerge(
'px-4 pt-4 pb-2 overflow-visible',
'p-2 overflow-visible',
$app.css?.['app']?.['grid']?.class ?? '',
'wm-app-grid'
'wm-app-grid border'
)}
on:pointerdown={() => {
$selectedComponent = undefined
@@ -83,7 +83,14 @@
}}
bind:clientWidth={$parentWidth}
>
<div class={!$focusedGrid && $mode !== 'preview' ? ' border border-dashed' : ''}>
<div
class={twMerge(
!$focusedGrid && $mode !== 'preview' ? 'border-dashed' : '',
'subgrid',
'border-[#999999] dark:border-[#aaaaaa] border '
)}
style={`transform: scale(${$scale / 100})`}
>
<Grid
allIdsInPath={$allIdsInPath}
selectedIds={$selectedComponent}
@@ -72,7 +72,7 @@ export function getComponentControl(type: keyof typeof components): Array<Compon
case 'modalcomponent':
return [openModal, closeModal]
case 'aggridcomponent':
return [getAgGrid]
return [getAgGrid, setSelectedIndex]
case 'displaycomponent':
case 'dateinputcomponent':
case 'textinputcomponent':
@@ -90,7 +90,6 @@ export function getComponentControl(type: keyof typeof components): Array<Compon
case 'textareainputcomponent':
return [setValue]
case 'tablecomponent':
case 'aggridcomponent':
return [setSelectedIndex]
default:
if (components[type].initialData['componentInput']) {
@@ -27,7 +27,7 @@
)}
>
<div class="min-w-[150px]">
<div class="sticky z-10 top-0 left-0 w-full p-1.5">
<div class="sticky z-10 top-0 left-0 w-full p-1.5 bg-surface">
<ClearableInput bind:value={$search} placeholder="Search outputs..." />
</div>
@@ -6,7 +6,6 @@
import PanelSection from './common/PanelSection.svelte'
import Toggle from '$lib/components/Toggle.svelte'
import InputsSpecsEditor from './InputsSpecsEditor.svelte'
import Tooltip from '$lib/components/Tooltip.svelte'
export let groupFields: RichConfigurations | undefined
@@ -40,13 +39,13 @@
}
console.log(groupFields)
}}
options={{ right: 'container is a component group' }}
/>
<Tooltip
>Group fields allow inner components to depend on the group fields which make the container a
options={{
right: 'container is a component group',
rightTooltip: `Group fields allow inner components to depend on the group fields which make the container a
group of component that is encapsulated. Inside the group, it is possible to retrieve the values
using `group.<x />` where x is the group field name</Tooltip
>
using \`group.<x />\` where x is the group field name`
}}
/>
</div>
{#if groupFields != undefined}
<PanelSection
@@ -17,7 +17,7 @@
)}
>
<div class="flex justify-between flex-wrap items-center w-full gap-1">
<div class="text-sm inline-flex items-center font-semibold text-primary {titlePadding} gap-1">
<div class="text-xs inline-flex items-center font-semibold text-primary {titlePadding} gap-1">
<span class="truncate">
{title}
</span>
@@ -1,5 +1,7 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte'
import { createEventDispatcher, getContext } from 'svelte'
import type { AppEditorContext } from '../types'
import { writable } from 'svelte/store'
const dispatch = createEventDispatcher()
@@ -25,6 +27,9 @@
export let onTop
export let shadow: { x: number; y: number; w: number; h: number } | undefined = undefined
const ctx = getContext<AppEditorContext>('AppEditorContext')
const scale = ctx ? ctx.scale : writable(100)
const divId = `component-${id}`
let shadowElement
@@ -50,19 +55,14 @@
let irect = shadowElement.getBoundingClientRect()
let shadowBound
const factor = $scale / 100
if (subgrid) {
let subGridParent = subgrid.parentElement
let subGridParentRect = subGridParent.getBoundingClientRect()
let prect = subgrid.getBoundingClientRect()
const subGridOffset = {
x: subGridParentRect.x - prect.x,
y: subGridParentRect.y - prect.y
}
shadowBound = {
x: irect.x - prect.left - subGridOffset.x,
y: irect.y - prect.top - subGridOffset.y
x: irect.x - prect.left,
y: irect.y - prect.top
}
} else {
shadowBound = irect
@@ -71,8 +71,8 @@
const xdragBound = rect.left + cordDiff.x
const ydragBound = rect.top + cordDiff.y
cordDiff.x = shadow.x * xPerPx + gapX - (shadowBound.x - xdragBound)
cordDiff.y = shadow.y * yPerPx + gapY - (shadowBound.y - ydragBound)
cordDiff.x = shadow.x * xPerPx + gapX - (shadowBound.x - xdragBound * factor) * factor
cordDiff.y = shadow.y * yPerPx + gapY - (shadowBound.y - ydragBound * factor) * factor
active = false
trans = true
@@ -121,19 +121,13 @@
let irect = gridItem.getBoundingClientRect()
if (subgrid && subgrid.parentElement) {
let subGridParent = subgrid.parentElement
let subGridParentRect = subGridParent.getBoundingClientRect()
let prect = subgrid.getBoundingClientRect()
const subGridOffset = {
x: subGridParentRect.x - prect.x,
y: subGridParentRect.y - prect.y
}
const factor = $scale / 100
rect = {
top: irect.top - prect.top - subGridOffset.y,
left: irect.left - prect.left - subGridOffset.x
top: (irect.top - prect.top) / factor,
left: (irect.left - prect.left) / factor
}
} else {
rect = irect
@@ -145,8 +139,8 @@
dragClosure = () => {
dragClosure = undefined
initX = clientX
initY = clientY
initX = (clientX / $scale) * 100
initY = (clientY / $scale) * 100
dispatch('initmove')
}
window.addEventListener('pointermove', pointermove)
@@ -200,7 +194,7 @@
event.stopImmediatePropagation()
const { clientX, clientY } = event
const cordDiff = { x: clientX - initX, y: clientY - initY }
const cordDiff = { x: (clientX / $scale) * 100 - initX, y: (clientY / $scale) * 100 - initY }
dispatch('move', { cordDiff, clientY })
}
@@ -286,8 +280,8 @@
const resizePointerMove = ({ pageX, pageY }) => {
if (shadow) {
newSize.width = initSize.width + pageX - resizeInitPos.x
newSize.height = initSize.height + pageY - resizeInitPos.y
newSize.width = initSize.width + ((pageX - resizeInitPos.x) / $scale) * 100
newSize.height = initSize.height + ((pageY - resizeInitPos.y) / $scale) * 100
// Get max col number
let maxWidth = cols - shadow.x
@@ -314,6 +308,7 @@
</script>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
draggable="false"
on:pointerdown|stopPropagation|preventDefault={pointerdown}
+2 -1
View File
@@ -249,7 +249,8 @@ export type AppEditorContext = {
pickVariableCallback: Writable<((path: string) => void) | undefined>
selectedComponentInEditor: Writable<string | undefined>
movingcomponents: Writable<string[] | undefined>
jobsDrawerOpen: Writable<boolean>
jobsDrawerOpen: Writable<boolean>,
scale: Writable<number>
}
export type FocusedGrid = { parentComponentId: string; subGridIndex: number }
@@ -14,3 +14,5 @@ export async function getResourceTypes() {
return nrts
}
}
+1 -1
View File
@@ -789,7 +789,7 @@ const config = {
overflow: 'auto !important'
},
'.splitpanes__splitter': {
backgroundColor: lightTheme.border + ' !important',
backgroundColor: 'rgb(243 244 246) !important',
margin: '0 !important',
border: 'none !important',
'&::after': {