context panel refactor

This commit is contained in:
Ruben Fiszel
2023-03-15 22:24:13 +01:00
parent 2c6673ae1d
commit dab7b68e40
27 changed files with 436 additions and 275 deletions

View File

@@ -171,6 +171,7 @@
updateRegionOutput()
if (z) {
//@ts-ignore
gridItem.data.configuration.zoom.value = z
}
@@ -180,7 +181,9 @@
}
if (gridItem) {
//@ts-ignore
gridItem.data.configuration.longitude.value = center[0]
//@ts-ignore
gridItem.data.configuration.latitude.value = center[1]
}
}

View File

@@ -155,7 +155,9 @@
function syncZoomValue() {
const gridItem = findGridItem($app, id)
//@ts-ignore
if (gridItem && gridItem.data.configuration.zoom.value !== zoom) {
//@ts-ignore
gridItem.data.configuration.zoom.value = zoom
}

View File

@@ -351,6 +351,4 @@
</div>
{/if}
</div>
{:else}
<div class="w-full h-full" />
{/if}

View File

@@ -1,7 +1,7 @@
<script lang="ts">
import { onMount, setContext } from 'svelte'
import { setContext } from 'svelte'
import { writable, type Writable } from 'svelte/store'
import { buildWorld, type World } from '../rx'
import { buildWorld } from '../rx'
import type {
App,
AppEditorContext,
@@ -10,11 +10,16 @@
EditorBreakpoint,
EditorMode
} from '../types'
import GridEditor from './GridEditor.svelte'
import { classNames } from '$lib/utils'
import type { Policy } from '$lib/gen'
import Button from '../../common/button/Button.svelte'
import { Unlock } from 'lucide-svelte'
import RecomputeAllComponents from './RecomputeAllComponents.svelte'
import GridViewer from './GridViewer.svelte'
import { Component } from './component'
import { twMerge } from 'tailwind-merge'
import { columnConfiguration } from '../gridUtils'
import { HiddenComponent } from '../components'
export let app: App
export let appPath: string
@@ -28,7 +33,6 @@
export let isLocked = false
const appStore = writable<App>(app)
const worldStore = writable<World | undefined>(undefined)
const selectedComponent = writable<string | undefined>(undefined)
const mode = writable<EditorMode>('preview')
@@ -40,6 +44,7 @@
const runnableComponents = writable<Record<string, () => Promise<void>>>({})
const parentWidth = writable(0)
setContext<AppViewerContext>('AppViewerContext', {
worldStore: buildWorld(context),
app: appStore,
@@ -60,7 +65,7 @@
openDebugRun: writable(undefined),
focusedGrid: writable(undefined),
stateId: writable(0),
parentWidth: writable(0),
parentWidth,
state: writable({}),
componentControl: writable({})
})
@@ -70,12 +75,6 @@
pickVariableCallback: writable(undefined)
})
let mounted = false
onMount(() => {
mounted = true
})
let ncontext = context
function hashchange(e: HashChangeEvent) {
@@ -98,9 +97,48 @@
>
{#if $appStore.grid}
<div class={classNames('mx-auto', width)}>
<GridEditor {policy} />
<div
class="w-full sticky top-0 flex justify-between border-b bg-gray-50 px-4 py-1 items-center gap-4"
style="z-index: 1000;"
>
<h2 class="truncate">{summary}</h2>
<RecomputeAllComponents />
<div class="text-2xs text-gray-600">
{policy.on_behalf_of ? `on behalf of ${policy.on_behalf_of_email}` : ''}
</div>
</div>
</div>
{/if}
<div
style={app.css?.['app']?.['grid']?.style}
class={twMerge('px-4 pt-4 pb-2 overflow-visible', app.css?.['app']?.['grid']?.class ?? '')}
bind:clientWidth={$parentWidth}
>
<div>
<GridViewer
onTopId={$selectedComponent}
items={app.grid}
let:dataItem
rowHeight={36}
cols={columnConfiguration}
gap={[4, 2]}
>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
class={'h-full w-full center-center'}
on:pointerdown={() => ($selectedComponent = dataItem.data.id)}
>
<Component
render={true}
pointerdown={false}
component={dataItem.data}
selected={false}
locked={true}
/>
</div>
</GridViewer>
</div>
</div>
</div>
{#if isLocked}
@@ -116,3 +154,17 @@
</div>
{/if}
</div>
{#if app.hiddenInlineScripts}
{#each app.hiddenInlineScripts as script, index}
{#if script}
<HiddenComponent
id={`bg_${index}`}
inlineScript={script.inlineScript}
name={script.name}
fields={script.fields}
autoRefresh={script.autoRefresh ?? false}
/>
{/if}
{/each}
{/if}

View File

@@ -1,8 +1,8 @@
<script lang="ts">
import { getContext, afterUpdate } from 'svelte'
import type { App, AppEditorContext, AppViewerContext, GridItem } from '../types'
import type { App, AppEditorContext, AppViewerContext } from '../types'
import { classNames } from '$lib/utils'
import { columnConfiguration, disableDrag, enableDrag, isFixed, toggleFixed } from '../gridUtils'
import { columnConfiguration, isFixed, toggleFixed } from '../gridUtils'
import { twMerge } from 'tailwind-merge'
import RecomputeAllComponents from './RecomputeAllComponents.svelte'
@@ -11,7 +11,7 @@
import Component from './component/Component.svelte'
import { deepEqual } from 'fast-equals'
import { push } from '$lib/history'
import { expandGriditem, findGridItem, sortGridItemsPosition } from './appUtils'
import { expandGriditem, findGridItem } from './appUtils'
import Grid from '../svelte-grid/Grid.svelte'
export let policy: Policy
@@ -30,31 +30,6 @@
const { history } = getContext<AppEditorContext>('AppEditorContext')
// The drag is disabled when the user is connecting an input
// or when the user is previewing the app
// or when the focused grid is a subgrid
$: setAllDrags($mode === 'preview' || $connectingInput.opened)
function setAllDrags(enable: boolean) {
const fct = enable ? disableDrag : enableDrag
$app.grid.map((gridItem) => {
const disabledGridItem = fct(gridItem)
if (disabledGridItem?.data?.subGrids) {
disabledGridItem.data.subGrids = disabledGridItem.data.subGrids.map(
(subgrid: GridItem[]) => subgrid?.map((subgridItem: GridItem) => fct(subgridItem)) ?? []
)
}
return disabledGridItem
})
Object.values($app.subgrids ?? {}).map(
(subgrid: GridItem[]) => subgrid?.map((subgridItem: GridItem) => fct(subgridItem)) ?? []
)
}
function removeGridElement(component) {
if (component) {
$app.grid = $app.grid.filter((gridComponent) => {

View File

@@ -23,6 +23,7 @@
bind:component={actionButton}
duplicateMoveAllowed={false}
onDelete={() => {
//@ts-ignore
gridItem.data.actionButtons = gridItem.data.actionButtons.filter(
(c) => c.id !== actionButton.id
)

View File

@@ -0,0 +1,104 @@
<script lang="ts">
import { onMount, createEventDispatcher } from 'svelte'
import type { FilledItem } from '../svelte-grid/types'
import GridViewerComponent from './GridViewerComponent.svelte'
import { getColumn, throttle } from '../svelte-grid/utils/other'
import { getContainerHeight } from '../svelte-grid/utils/container'
import { specifyUndefinedColumns } from '../svelte-grid/utils/item'
const dispatch = createEventDispatcher()
type T = $$Generic
export let items: FilledItem<T>[]
export let rowHeight: number
export let cols: [number, number][]
export let gap = [10, 10]
export let throttleUpdate = 100
export let onTopId: string | undefined = undefined
export let containerWidth: number | undefined = undefined
export let parentWidth: number | undefined = undefined
let getComputedCols
let container
$: [gapX, gapY] = gap
let xPerPx = 0
let yPerPx = rowHeight
$: containerHeight = getContainerHeight(items, yPerPx, getComputedCols)
const onResize = throttle(() => {
items = specifyUndefinedColumns(items, getComputedCols, cols)
dispatch('resize', {
cols: getComputedCols,
xPerPx,
yPerPx,
width: containerWidth
})
}, throttleUpdate)
onMount(() => {
const sizeObserver = new ResizeObserver((entries) => {
requestAnimationFrame(() => {
let width = entries[0].contentRect.width
if (width === containerWidth) return
getComputedCols = getColumn(parentWidth ?? width, cols)
xPerPx = width / getComputedCols
if (!containerWidth) {
items = specifyUndefinedColumns(items, getComputedCols, cols)
dispatch('mount', {
cols: getComputedCols,
xPerPx,
yPerPx // same as rowHeight
})
} else {
onResize()
}
containerWidth = width
})
})
sizeObserver.observe(container)
return () => sizeObserver.disconnect()
})
</script>
<div class="svlt-grid-container" style="height: {containerHeight}px" bind:this={container}>
{#if xPerPx}
{#each items as item (item.id)}
<GridViewerComponent
onTop={item.id == onTopId}
width={Math.min(getComputedCols, item[getComputedCols] && item[getComputedCols].w) *
xPerPx -
gapX * 2}
height={(item[getComputedCols] && item[getComputedCols].h) * yPerPx - gapY * 2}
top={(item[getComputedCols] && item[getComputedCols].y) * yPerPx + gapY}
left={(item[getComputedCols] && item[getComputedCols].x) * xPerPx + gapX}
>
{#if item[getComputedCols]}
<slot dataItem={item} item={item[getComputedCols]} />
{/if}
</GridViewerComponent>
{/each}
{/if}
</div>
<style>
.svlt-grid-container {
position: relative;
width: 100%;
}
</style>

View File

@@ -0,0 +1,26 @@
<script lang="ts">
export let width: number
export let height: number
export let left: number
export let top: number
export let onTop: boolean
</script>
<div
class="svlt-grid-item"
style="width: {width}px; height:{height}px; {onTop ? 'z-index: 100;' : ''}
{`transition: transform 0.1s, opacity 0.1s; transform: translate(${left}px, ${top}px); `}"
>
<slot />
</div>
<style>
.svlt-grid-item {
touch-action: none;
position: absolute;
will-change: auto;
backface-visibility: hidden;
-webkit-backface-visibility: hidden;
}
</style>

View File

@@ -8,6 +8,7 @@
import { expandGriditem, findGridItem, sortGridItemsPosition } from './appUtils'
import { push } from '$lib/history'
import Grid from '../svelte-grid/Grid.svelte'
import GridViewer from './GridViewer.svelte'
export let containerHeight: number
export let containerWidth: number | undefined = undefined
@@ -89,46 +90,85 @@
on:pointerup={onpointerup}
style="height: {containerHeight}px; {style ?? ''}"
>
<div class={highlight && $mode !== 'preview' ? 'border-gray-400 border border-dashed' : ''}>
<Grid
{#if $mode !== 'preview'}
<div class={highlight ? 'border-gray-400 border border-dashed' : ''}>
<Grid
items={subGrid}
on:redraw={(e) => {
push(history, $app)
subGrid = e.detail
}}
let:dataItem
rowHeight={36}
cols={columnConfiguration}
fastStart={true}
gap={[4, 2]}
scroller={container}
parentWidth={$parentWidth - 17}
{containerWidth}
>
<!-- svelte-ignore a11y-click-events-have-key-events -->
{#if $connectingInput.opened}
<div
on:pointerenter={() => ($connectingInput.hoveredComponent = dataItem.data.id)}
on:pointerleave={() => ($connectingInput.hoveredComponent = undefined)}
class="absolute w-full h-full bg-black border-2 bg-opacity-25 z-20 flex justify-center items-center"
/>
<div
style="transform: translate(-50%, -50%);"
class="absolute w-fit justify-center bg-indigo-500/90 left-[50%] top-[50%] z-50 px-6 rounded border text-white py-2 text-5xl center-center"
>
{dataItem.data.id}
</div>
{/if}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
on:pointerdown={() => selectComponent(dataItem.data.id)}
class={classNames(
'h-full w-full center-center',
$selectedComponent === dataItem.data.id ? 'active-grid-item' : '',
dataItem.data.card ? 'border border-gray-100' : '',
'top-0'
)}
>
<Component
render={visible}
{pointerdown}
component={dataItem.data}
selected={$selectedComponent === dataItem.data.id}
locked={isFixed(dataItem)}
on:lock={() => lock(dataItem)}
on:expand={() => {
const parentGridItem = findGridItem($app, id)
if (!parentGridItem) {
return
}
$selectedComponent = dataItem.data.id
push(history, $app)
expandGriditem(subGrid, dataItem, $breakpoint, parentGridItem)
$app = $app
}}
/>
</div>
</Grid>
</div>
{:else}
<GridViewer
items={subGrid}
on:redraw={(e) => {
push(history, $app)
subGrid = e.detail
}}
let:dataItem
rowHeight={36}
cols={columnConfiguration}
fastStart={true}
gap={[4, 2]}
scroller={container}
parentWidth={$parentWidth - 17}
{containerWidth}
>
<!-- svelte-ignore a11y-click-events-have-key-events -->
{#if $connectingInput.opened}
<div
on:pointerenter={() => ($connectingInput.hoveredComponent = dataItem.data.id)}
on:pointerleave={() => ($connectingInput.hoveredComponent = undefined)}
class="absolute w-full h-full bg-black border-2 bg-opacity-25 z-20 flex justify-center items-center"
/>
<div
style="transform: translate(-50%, -50%);"
class="absolute w-fit justify-center bg-indigo-500/90 left-[50%] top-[50%] z-50 px-6 rounded border text-white py-2 text-5xl center-center"
>
{dataItem.data.id}
</div>
{/if}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
on:pointerdown={() => selectComponent(dataItem.data.id)}
class={classNames(
'h-full w-full center-center',
$selectedComponent === dataItem.data.id ? 'active-grid-item' : '',
dataItem.data.card ? 'border border-gray-100' : '',
'top-0'
)}
class={classNames('h-full w-full center-center', 'top-0')}
>
<Component
render={visible}
@@ -136,22 +176,9 @@
component={dataItem.data}
selected={$selectedComponent === dataItem.data.id}
locked={isFixed(dataItem)}
on:lock={() => lock(dataItem)}
on:expand={() => {
const parentGridItem = findGridItem($app, id)
if (!parentGridItem) {
return
}
$selectedComponent = dataItem.data.id
push(history, $app)
expandGriditem(subGrid, dataItem, $breakpoint, parentGridItem)
$app = $app
}}
/>
</div>
</Grid>
</div>
</GridViewer>
{/if}
</div>
</div>

View File

@@ -35,15 +35,12 @@ export function findGridItemParentGrid(app: App, id: string): string | undefined
}
}
export function isIdInsideGriditem(app: App, gridItem: GridItem, id: string | undefined): boolean {
const path: string[] = []
let currentId = id
while (currentId) {
path.push(currentId)
currentId = findGridItemParentGrid(app, currentId)?.split('-')[0]
export function allsubIds(app: App, parentId: string): string[] {
let item = findGridItem(app, parentId)
if (!item?.data.numberOfSubgrids) {
return [parentId]
}
return path.includes(gridItem.id)
return getAllSubgridsAndComponentIds(app, item?.data)[1]
}
export function findGridItem(app: App, id: string): GridItem | undefined {
@@ -336,7 +333,7 @@ export function sortGridItemsPosition<T>(
gridItems: FilledItem<T>[],
width: number
): FilledItem<T>[] {
return gridItems.sort((a: GridItem, b: GridItem) => {
return gridItems.sort((a: FilledItem<T>, b: FilledItem<T>) => {
const aX = a[width].x
const aY = a[width].y
const bX = b[width].x
@@ -392,7 +389,10 @@ export function recursivelyFilterKeyInJSON(
Object.keys(json).forEach((key) => {
if (
key.toLowerCase().includes(search.toLowerCase()) ||
extraSearch?.toLowerCase().includes(search.toLowerCase())
extraSearch?.toLowerCase().includes(search.toLowerCase()) ||
(typeof json[key] === 'string' && json[key].toLowerCase().includes(search.toLowerCase())) ||
(typeof json[key] === 'number' &&
json[key].toString().toLowerCase().includes(search.toLowerCase()))
) {
filteredJSON[key] = json[key]
} else if (typeof json[key] === 'object') {

View File

@@ -34,7 +34,7 @@ import {
FileText,
AtSignIcon
} from 'lucide-svelte'
import type { BaseAppComponent } from '../../types'
import type { BaseAppComponent, GridItem } from '../../types'
import type { Size } from '../../svelte-grid/types'
type BaseComponent<T extends string> = {
@@ -68,7 +68,7 @@ export type BarChartComponent = BaseComponent<'barchartcomponent'>
export type PieChartComponent = BaseComponent<'piechartcomponent'>
export type ScatterChartComponent = BaseComponent<'scatterchartcomponent'>
export type TableComponent = BaseComponent<'tablecomponent'> & {
actionButtons: (BaseAppComponent & ButtonComponent)[]
actionButtons: (BaseAppComponent & ButtonComponent & GridItem)[]
}
export type AggridComponent = BaseComponent<'aggridcomponent'>
export type DisplayComponent = BaseComponent<'displaycomponent'>
@@ -769,7 +769,7 @@ Hello \${ctx.username}
fieldType: 'boolean',
value: false,
onlyStatic: true,
tooltip: "Configure all columns as Editable by users"
tooltip: 'Configure all columns as Editable by users'
},
pagination: {
type: 'static',
@@ -782,7 +782,7 @@ Hello \${ctx.username}
fieldType: 'number',
value: 10,
onlyStatic: true,
tooltip: "Number of rows per page"
tooltip: 'Number of rows per page'
}
},
componentInput: {
@@ -887,7 +887,7 @@ Hello \${ctx.username}
fieldType: 'boolean',
value: false,
onlyStatic: true,
tooltip: "Allows user to manually add new value"
tooltip: 'Allows user to manually add new value'
},
placeholder: {
type: 'static',
@@ -1004,7 +1004,7 @@ Hello \${ctx.username}
type: 'static',
value: 1,
fieldType: 'number',
tooltip: "Spread between each number suggestion"
tooltip: 'Spread between each number suggestion'
}
},
customCss: {
@@ -1048,7 +1048,7 @@ Hello \${ctx.username}
fieldType: 'select',
onlyStatic: true,
optionValuesKey: 'localeOptions',
tooltip: "Currency format"
tooltip: 'Currency format'
}
},
customCss: {
@@ -1088,7 +1088,7 @@ Hello \${ctx.username}
type: 'static',
value: 1,
fieldType: 'number',
tooltip: "Spread between each number suggestion"
tooltip: 'Spread between each number suggestion'
}
},
customCss: {
@@ -1135,7 +1135,7 @@ Hello \${ctx.username}
type: 'static',
value: 1,
fieldType: 'number',
tooltip: "Spread between each number suggestion"
tooltip: 'Spread between each number suggestion'
}
},
customCss: {

View File

@@ -3,7 +3,7 @@
import { getContext } from 'svelte'
import type { AppViewerContext, GridItem } from '../../types'
import ComponentOutputViewer from './ComponentOutputViewer.svelte'
import { connectInput, isIdInsideGriditem } from '../appUtils'
import { connectInput } from '../appUtils'
import SubGridOutput from './SubGridOutput.svelte'
import OutputHeader from './components/OutputHeader.svelte'
import TableActionsOutput from './components/TableActionsOutput.svelte'
@@ -14,8 +14,7 @@
export let parentId: string | undefined = undefined
export let expanded: boolean = false
const { app, selectedComponent, connectingInput } =
getContext<AppViewerContext>('AppViewerContext')
const { selectedComponent, connectingInput } = getContext<AppViewerContext>('AppViewerContext')
const name = getComponentNameById(gridItem.id)
function getComponentNameById(componentId: string) {
@@ -30,14 +29,10 @@
}
}
$: subGrids = Array.from({ length: gridItem.data.numberOfSubgrids }).map(
$: subGrids = Array.from({ length: gridItem.data.numberOfSubgrids ?? 0 }).map(
(_, i) => `${gridItem.id}-${i}`
)
$: insideGrid = isIdInsideGriditem($app, gridItem, $selectedComponent)
$: isSelected = $selectedComponent === gridItem.id
$: shouldOpen = insideGrid || isSelected
function onHeaderClick(manuallyOpen: boolean) {
if (manuallyOpen) {
if (parentId) {
@@ -52,7 +47,6 @@
</script>
<OutputHeader
{shouldOpen}
on:handleClick={(e) => {
if (!$connectingInput.opened) {
onHeaderClick(e.detail.manuallyOpen)
@@ -62,19 +56,16 @@
name={getComponentNameById(gridItem.id)}
{first}
{nested}
{expanded}
>
<div class="py-1">
<ComponentOutputViewer
componentId={gridItem.id}
on:select={({ detail }) => {
if ($connectingInput.opened) {
$connectingInput = connectInput($connectingInput, gridItem.id, detail)
}
}}
/>
</div>
<ComponentOutputViewer
componentId={gridItem.id}
on:select={({ detail }) => {
if ($connectingInput.opened) {
$connectingInput = connectInput($connectingInput, gridItem.id, detail)
}
}}
/>
<SubGridOutput {name} {expanded} {subGrids} parentId={gridItem.id} />
<TableActionsOutput {gridItem} {expanded} />
<TableActionsOutput {gridItem} />
</OutputHeader>

View File

@@ -3,13 +3,13 @@
import { getContext } from 'svelte'
import type { Writable } from 'svelte/store'
import type { Output } from '../../rx'
import type { AppViewerContext } from '../../types'
import { recursivelyFilterKeyInJSON } from '../appUtils'
import type { AppViewerContext, ContextPanelContext } from '../../types'
import { recursivelyFilterKeyInJSON as recursivelyFilterInJSON } from '../appUtils'
export let componentId: string
const { worldStore } = getContext<AppViewerContext>('AppViewerContext')
const { search } = getContext<{ search: Writable<string> }>('searchCtx')
const { search, hasResult } = getContext<ContextPanelContext>('ContextPanel')
let object = {}
@@ -29,11 +29,15 @@
$: subscribeToAllOutputs($worldStore?.outputsById?.[componentId])
$: filtered = recursivelyFilterKeyInJSON(object, $search, componentId)
$: filtered = recursivelyFilterInJSON(object, $search, componentId)
$: $hasResult[componentId] = Object.keys(filtered).length > 0
</script>
{#if Object.keys(filtered).length > 0}
{#if $hasResult[componentId] || $search == ''}
<ObjectViewer json={filtered} on:select topBrackets={false} />
{:else if $search.length > 0}
<div class="text-xs pl-2">No results</div>
<div class="text-xs pl-2 text-gray-600">No results</div>
{:else}
<div class="text-xs pl-2 text-gray-600">No outputs</div>
{/if}

View File

@@ -4,7 +4,7 @@
import { getContext, setContext } from 'svelte'
import { writable } from 'svelte/store'
import type { AppViewerContext } from '../../types'
import type { AppViewerContext, ContextPanelContext } from '../../types'
import { connectInput } from '../appUtils'
import PanelSection from '../settingsPanel/common/PanelSection.svelte'
import ComponentOutput from './ComponentOutput.svelte'
@@ -13,19 +13,17 @@
import MinMaxButton from './components/MinMaxButton.svelte'
import OutputHeader from './components/OutputHeader.svelte'
const { connectingInput, app, worldStore } = getContext<AppViewerContext>('AppViewerContext')
const { connectingInput, app } = getContext<AppViewerContext>('AppViewerContext')
let search = writable<string>('')
let expanded = false
let ctxOpened = true
let stateOpened = true
let expanded = writable(false)
setContext('searchCtx', {
search
setContext<ContextPanelContext>('ContextPanel', {
search,
manuallyOpened: writable<Record<string, boolean>>({}),
hasResult: writable<Record<string, boolean>>({}),
expanded
})
$: expanded && !ctxOpened && (ctxOpened = true)
$: expanded && !stateOpened && (stateOpened = true)
</script>
<PanelSection noPadding titlePadding="px-4 pt-2 pb-0.5" title="Outputs">
@@ -50,46 +48,44 @@
</div>
<div class="p-1 ">
<MinMaxButton bind:expanded />
<MinMaxButton bind:expanded={$expanded} />
</div>
<div class="flex flex-col gap-4">
{#key $worldStore?.outputsById}
<div>
<span class="text-sm font-bold p-2">State & Context</span>
<div>
<span class="text-sm font-bold p-2">State & Context</span>
<OutputHeader id={'ctx'} name={'App Context'} first color="blue" {expanded}>
<ComponentOutputViewer
componentId={'ctx'}
on:select={({ detail }) => {
$connectingInput = connectInput($connectingInput, 'ctx', detail)
}}
/>
</OutputHeader>
<OutputHeader id={'ctx'} name={'App Context'} first color="blue">
<ComponentOutputViewer
componentId={'ctx'}
on:select={({ detail }) => {
$connectingInput = connectInput($connectingInput, 'ctx', detail)
}}
/>
</OutputHeader>
<OutputHeader id={'state'} name={'State'} color="blue" {expanded}>
<ComponentOutputViewer
componentId={'state'}
on:select={({ detail }) => {
$connectingInput = connectInput($connectingInput, 'state', detail)
}}
/>
</OutputHeader>
</div>
<OutputHeader id={'state'} name={'State'} color="blue">
<ComponentOutputViewer
componentId={'state'}
on:select={({ detail }) => {
$connectingInput = connectInput($connectingInput, 'state', detail)
}}
/>
</OutputHeader>
</div>
<div>
<span class="text-sm font-bold p-2">Components</span>
{#each $app.grid as gridItem, index (gridItem.id)}
<ComponentOutput {gridItem} first={index === 0} {expanded} />
{/each}
<div>
<span class="text-sm font-bold p-2">Components</span>
{#each $app.grid as gridItem, index (gridItem.id)}
<ComponentOutput {gridItem} first={index === 0} />
{/each}
</div>
<div>
<span class="text-sm font-bold p-2">Background scripts</span>
<div class="border-t">
<BackgroundScriptsOutput />
</div>
<div>
<span class="text-sm font-bold p-2">Background scripts</span>
<div class="border-t">
<BackgroundScriptsOutput {expanded} />
</div>
</div>
{/key}
</div>
</div>
</div>
</div>

View File

@@ -12,8 +12,7 @@
export let expanded: boolean = false
export let subGrids: string[]
const { app, connectingInput, breakpoint, worldStore } =
getContext<AppViewerContext>('AppViewerContext')
const { app, connectingInput, worldStore } = getContext<AppViewerContext>('AppViewerContext')
let selected = 0

View File

@@ -9,7 +9,6 @@
export let id: string
export let name: string
export let expanded: boolean = false
export let first: boolean = false
function onHeaderClick(manuallyOpen: boolean) {
@@ -30,13 +29,11 @@
{name}
color="blue"
{first}
{expanded}
on:handleClick={(e) => {
if (!$connectingInput.opened) {
onHeaderClick(e.detail.manuallyOpen)
}
}}
shouldOpen={$selectedComponent === id}
>
<ComponentOutputViewer
componentId={id}

View File

@@ -1,7 +1,6 @@
<script lang="ts">
import type { AppViewerContext } from '$lib/components/apps/types'
import { getContext } from 'svelte'
import OutputHeader from './OutputHeader.svelte'
import BackgroundScriptOutput from './BackgroundScriptOutput.svelte'
const { app } = getContext<AppViewerContext>('AppViewerContext')
@@ -10,5 +9,5 @@
</script>
{#each $app.hiddenInlineScripts as action, index}
<BackgroundScriptOutput id={`bg_${index}`} name={action.name} {expanded} />
<BackgroundScriptOutput id={`bg_${index}`} name={action.name} />
{/each}

View File

@@ -1,28 +1,32 @@
<script lang="ts">
import type { AppViewerContext, ContextPanelContext } from '$lib/components/apps/types'
import { classNames } from '$lib/utils'
import { ChevronDown, ChevronUp } from 'lucide-svelte'
import { createEventDispatcher } from 'svelte'
import { createEventDispatcher, getContext } from 'svelte'
import { slide } from 'svelte/transition'
import { allsubIds } from '../../appUtils'
export let id: string
export let name: string
export let first: boolean = false
export let nested: boolean = false
export let color: 'blue' | 'indigo' = 'indigo'
export let expanded: boolean = false
export let shouldOpen: boolean = false
$: open = shouldOpen
let manuallyOpen = false
const { expanded, manuallyOpened, search, hasResult } =
getContext<ContextPanelContext>('ContextPanel')
const { selectedComponent, app } = getContext<AppViewerContext>('AppViewerContext')
$: subids = allsubIds($app, id)
$: inSearch =
$search != '' &&
($hasResult[id] ||
Object.entries($hasResult).some(([key, value]) => value && subids.includes(key)))
$: open =
$expanded || subids.includes($selectedComponent ?? '') || $manuallyOpened[id] || inSearch
const dispatch = createEventDispatcher()
$: if (expanded) {
manuallyOpen = true
} else {
manuallyOpen = false
}
const hoverColor = {
blue: 'hover:bg-blue-300 hover:text-blue-600',
indigo: 'hover:bg-indigo-300 hover:text-indigo-600'
@@ -45,42 +49,47 @@
</script>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
class={classNames(
'flex items-center justify-between p-1 cursor-pointer border-b gap-1 truncate',
hoverColor[color],
open && !manuallyOpen ? openBackground[color] : 'bg-white',
first ? 'border-t' : '',
nested ? 'border-l' : ''
)}
on:click={() => {
dispatch('handleClick', { manuallyOpen })
manuallyOpen = !manuallyOpen
}}
>
<div class={$search == '' || inSearch ? '' : 'invisible h-0 overflow-hidden'}>
<div
class={classNames(
'text-2xs ml-0.5 font-bold px-2 py-0.5 rounded-sm',
open ? idClass[color] : ' bg-gray-100'
'flex items-center justify-between p-1 cursor-pointer border-b gap-1 truncate',
hoverColor[color],
$selectedComponent == id ? openBackground[color] : 'bg-white',
first ? 'border-t' : '',
nested ? 'border-l' : ''
)}
on:click={() => {
dispatch('handleClick', { manuallyOpen: $manuallyOpened[id] })
$manuallyOpened[id] = $manuallyOpened[id] != undefined ? !$manuallyOpened[id] : true
}}
>
{id}
<div
class={classNames(
'text-2xs ml-0.5 font-bold px-2 py-0.5 rounded-sm',
$selectedComponent == id ? idClass[color] : ' bg-gray-100'
)}
>
{id}
</div>
<div
on:click|stopPropagation={() => {
$manuallyOpened[id] = $manuallyOpened[id] != undefined ? !$manuallyOpened[id] : false
}}
class="text-2xs font-bold flex flex-row gap-2 items-center truncate"
>
{name}
{#if !open}
<ChevronDown size={14} />
{:else if $manuallyOpened[id]}
<ChevronUp size={14} class={manuallyOpenColor[color]} strokeWidth={4} />
{:else}
<ChevronUp size={14} />
{/if}
</div>
</div>
<div class="text-2xs font-bold flex flex-row gap-2 items-center truncate">
{name}
{#if !open && !manuallyOpen}
<ChevronDown size={14} />
{:else if manuallyOpen}
<ChevronUp size={14} class={manuallyOpenColor[color]} strokeWidth={4} />
{:else}
<ChevronUp size={14} />
{/if}
</div>
</div>
{#if open || manuallyOpen}
<div class="py-1 border-b" transition:slide|local>
<div class="py-1 border-b {open ? '' : 'invisible h-0 overflow-hidden'} ">
<div class={classNames(nested ? 'border-l ml-2' : '')}>
<slot />
</div>
</div>
{/if}
</div>

View File

@@ -8,11 +8,10 @@
const { connectingInput } = getContext<AppViewerContext>('AppViewerContext')
export let id: string
export let expanded: boolean = false
export let first: boolean = false
</script>
<OutputHeader {id} name={'Table action'} {first} {expanded}>
<OutputHeader {id} name={'Table action'} {first}>
<ComponentOutputViewer
componentId={id}
on:select={({ detail }) => {

View File

@@ -3,14 +3,13 @@
import TableActionOutput from './TableActionOutput.svelte'
export let gridItem: GridItem
export let expanded: boolean = false
</script>
<div class="my-1">
{#if gridItem.data.type === 'tablecomponent' && gridItem.data.actionButtons.length > 0}
<div class="ml-2 border-l">
{#each gridItem.data.actionButtons as action, index}
<TableActionOutput id={action.id} {expanded} first={index === 0} />
<TableActionOutput id={action.id} first={index === 0} />
{/each}
</div>
{/if}

View File

@@ -49,7 +49,7 @@
const componentType = $app.grid.find((c) => c.data.id === id)?.data?.type
const content =
defaultCode(componentType, language) ??
defaultCode(componentType ?? '', language) ??
initialCode(language, Script.kind.SCRIPT, subkind ?? 'flow')
return newInlineScript(content, language, path)

View File

@@ -36,8 +36,7 @@
{#each $app.grid as gridItem (gridItem?.data?.id)}
{#if gridItem?.data?.id && gridItem.data.id === selectedScriptComponentId}
<InlineScriptEditorPanel
defaultUserInput={gridItem?.data?.type == 'formcomponent' ||
gridItem?.data?.type == 'buttonformcomponent'}
defaultUserInput={gridItem?.data?.type == 'formcomponent'}
id={gridItem.data.id}
bind:componentInput={gridItem.data.componentInput}
/>
@@ -60,8 +59,7 @@
{#each $app.subgrids[key] as gridItem (gridItem?.data?.id)}
{#if gridItem?.data?.id && gridItem.data.id === selectedScriptComponentId}
<InlineScriptEditorPanel
defaultUserInput={gridItem.data?.type == 'formcomponent' ||
gridItem.data?.type == 'buttonformcomponent'}
defaultUserInput={gridItem.data?.type == 'formcomponent'}
id={gridItem.data.id}
bind:componentInput={gridItem.data.componentInput}
/>

View File

@@ -7,8 +7,8 @@ const Breakpoints = {
lg: 1024
}
const WIDE_GRID_COLUMNS = 12 as const;
const NARROW_GRID_COLUMNS = 3 as const;
const WIDE_GRID_COLUMNS = 12 as const
const NARROW_GRID_COLUMNS = 3 as const
const columnConfiguration: ColumnConfiguration = [
[Breakpoints.lg, WIDE_GRID_COLUMNS],
@@ -17,14 +17,6 @@ const columnConfiguration: ColumnConfiguration = [
const gridColumns = columnConfiguration.map((value) => value[1])
function disableDrag(component: GridItem): GridItem {
gridColumns.forEach((column: number) => {
component[column].customDragger = true
component[column].customResizer = true
})
return component
}
function toggleFixed(component: GridItem): GridItem {
const nValue = !component[gridColumns[0]].fixed
gridColumns.forEach((column: number) => {
@@ -44,21 +36,11 @@ function isFixed(component: GridItem): boolean {
return fixed
}
function enableDrag(component: GridItem): GridItem {
gridColumns.forEach((column: number) => {
component[column].customDragger = false
component[column].customResizer = false
})
return component
}
export {
gridColumns,
WIDE_GRID_COLUMNS,
NARROW_GRID_COLUMNS,
columnConfiguration,
disableDrag,
enableDrag,
Breakpoints,
toggleFixed,
isFixed

View File

@@ -1,25 +1,21 @@
export interface Size {
w: number
h: number
w: number
h: number
}
export interface Positon {
x: number
y: number
x: number
y: number
}
interface ItemLayout extends Size, Positon {
fixed?: boolean
resizable?: boolean
draggable?: boolean
customDragger?: boolean
customResizer?: boolean
min?: Size
max?: Size
fixed?: boolean
resizable?: boolean
draggable?: boolean
customDragger?: boolean
customResizer?: boolean
min?: Size
max?: Size
}
export type FilledItem<T> = T & { [width: number]: Required<ItemLayout>; data: any, id: string }
export type FilledItem<T> = { [width: number]: Required<ItemLayout>; data: T; id: string }

View File

@@ -77,10 +77,7 @@ export type AppSection = {
id: SectionID
}
export type GridItem = FilledItem<{
data: AppComponent
id: string
}>
export type GridItem = FilledItem<AppComponent>
export type InlineScript = {
content: string
@@ -155,3 +152,10 @@ export type EditorBreakpoint = 'sm' | 'lg'
export const IS_APP_PUBLIC_CONTEXT_KEY = 'isAppPublicContext' as const
type ComponentID = string
export type ContextPanelContext = {
search: Writable<string>
manuallyOpened: Writable<Record<string, boolean>>
expanded: Writable<boolean>
hasResult: Writable<Record<string, boolean>>
}

View File

@@ -199,8 +199,8 @@ export function getAllScriptNames(app: App): string[] {
acc.push(componentInput.runnable.name)
}
if (componentInput?.type === 'tablecomponent') {
componentInput.actionButtons.forEach((actionButton) => {
if (gridItem.data.type === 'tablecomponent') {
gridItem.data.actionButtons.forEach((actionButton) => {
if (actionButton.componentInput?.type === 'runnable') {
if (actionButton.componentInput.runnable?.type === 'runnableByName') {
acc.push(actionButton.componentInput.runnable.name)

View File

@@ -132,7 +132,7 @@
{:else if topBrackets}
<span class="text-black">{openBracket}{closeBracket}</span>
{:else}
<span class="text-gray-600 text-xs">No items</span>
<span class="text-gray-600 text-xs ml-2">No items</span>
{/if}
<style>