Skip to content

Commit

Permalink
fix(frontend): Fix context panel + delete component (#937)
Browse files Browse the repository at this point in the history
* fix(frontend): Fix context panel + delete component

* fix(frontend): Handle nested paths
  • Loading branch information
fatonramadani committed Nov 23, 2022
1 parent 4ddb3ec commit ab481b3
Show file tree
Hide file tree
Showing 10 changed files with 110 additions and 64 deletions.
Expand Up @@ -14,7 +14,6 @@
export const staticOutputs: string[] = ['loading', 'result']
let labelValue: string = 'Default label'
let runnableComponent: RunnableComponent
</script>

Expand Down
@@ -1,10 +1,11 @@
<script lang="ts">
import { getContext } from 'svelte'
import type { StaticInput, DynamicInput, AppEditorContext, UserInput } from '../../types'
import { accessPropertyByPath } from '../../utils'
type T = $$Generic
export let input: DynamicInput | StaticInput | UserInput
export let value: T
export let value: T | undefined = undefined
const { worldStore } = getContext<AppEditorContext>('AppEditorContext')
Expand All @@ -26,7 +27,13 @@
function onValueChange(newValue: T): void {
if (input.type === 'output') {
value = newValue
if (input.name?.includes('.')) {
const path = input.name.split('.').slice(1).join('.')
value = accessPropertyByPath(newValue, path)
} else {
value = newValue
}
} else {
// TODO: handle disconnect
}
Expand Down
68 changes: 36 additions & 32 deletions frontend/src/lib/components/apps/editor/AppEditor.svelte
Expand Up @@ -69,7 +69,9 @@
<AppEditorHeader bind:title={$appStore.title} bind:mode={$mode} />
<SplitPanesWrapper>
<Pane size={20} minSize={20} maxSize={40}>
<ContextPanel appPath={path} />
{#if $mode === 'dnd'}
<ContextPanel appPath={path} />
{/if}
</Pane>
<Pane size={60} maxSize={100}>
<div class="p-4 bg-gray-100 h-full" id="faton">
Expand All @@ -79,36 +81,38 @@
</div>
</Pane>
<Pane size={20} minSize={20} maxSize={40}>
<Tabs bind:selected={selectedTab}>
<Tab value="insert" size="xs">
<div class="m-1 flex flex-row gap-2">
<Icon data={faPlus} />
<span>Insert</span>
</div>
</Tab>
<Tab value="settings" size="xs">
<div class="m-1 flex flex-row gap-2">
<Icon data={faSliders} />
<span>Settings</span>
</div>
</Tab>
<svelte:fragment slot="content">
<TabContent value="settings">
{#if $selectedComponent !== undefined}
{#each $appStore.grid as gridItem (gridItem.id)}
{#if gridItem.data.id === $selectedComponent}
<ComponentPanel bind:component={gridItem.data} />
{/if}
{/each}
{/if}
{#if $selectedComponent === undefined}
<div class="p-4 text-sm">No component selected.</div>
{/if}
</TabContent>
<TabContent value="insert">
<ComponentList />
</TabContent>
</svelte:fragment>
</Tabs>
{#if $mode === 'dnd'}
<Tabs bind:selected={selectedTab}>
<Tab value="insert" size="xs">
<div class="m-1 flex flex-row gap-2">
<Icon data={faPlus} />
<span>Insert</span>
</div>
</Tab>
<Tab value="settings" size="xs">
<div class="m-1 flex flex-row gap-2">
<Icon data={faSliders} />
<span>Settings</span>
</div>
</Tab>
<svelte:fragment slot="content">
<TabContent value="settings">
{#if $selectedComponent !== undefined}
{#each $appStore.grid as gridItem (gridItem.id)}
{#if gridItem.data.id === $selectedComponent}
<ComponentPanel bind:component={gridItem.data} />
{/if}
{/each}
{/if}
{#if $selectedComponent === undefined}
<div class="p-4 text-sm">No component selected.</div>
{/if}
</TabContent>
<TabContent value="insert">
<ComponentList />
</TabContent>
</svelte:fragment>
</Tabs>
{/if}
</Pane>
</SplitPanesWrapper>
32 changes: 19 additions & 13 deletions frontend/src/lib/components/apps/editor/GridEditor.svelte
Expand Up @@ -27,19 +27,25 @@

<div class="bg-white h-full">
<Grid bind:items={$app.grid} rowHeight={32} let:dataItem {cols}>
{@const index = $app.grid.findIndex((c) => c.data.id === dataItem.data.id)}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
class={classNames('h-full w-full flex justify-center align-center border border-gray-100')}
on:click={() => {
$selectedComponent = dataItem.data.id
}}
>
<ComponentEditor
bind:component={$app.grid[index].data}
selected={$selectedComponent === dataItem.data.id}
/>
</div>
{#each $app.grid as gridComponent (gridComponent.id)}
{#if gridComponent.data.id === dataItem.data.id}
<!-- svelte-ignore a11y-click-events-have-key-events -->

<div
class={classNames(
'h-full w-full flex justify-center align-center border border-gray-100'
)}
on:click={() => {
$selectedComponent = dataItem.data.id
}}
>
<ComponentEditor
bind:component={gridComponent.data}
selected={$selectedComponent === dataItem.data.id}
/>
</div>
{/if}
{/each}
</Grid>
</div>

Expand Down
@@ -1,6 +1,7 @@
<script lang="ts">
import ObjectViewer from '$lib/components/propertyPicker/ObjectViewer.svelte'
import { getContext } from 'svelte'
import type { Output } from '../../rx'
import type { AppEditorContext } from '../../types'
export let outputs: string[] = []
Expand All @@ -10,13 +11,20 @@
let object = {}
outputs.forEach((output: string) => {
$worldStore?.outputsById[componentId][output].subscribe({
next: (value) => {
object[output] = value
}
})
})
function subscribeToAllOutputs(observableOutputs: Record<string, Output<any>>) {
if (observableOutputs) {
outputs.forEach((output) => {
observableOutputs[output].subscribe({
next: (value) => {
object[output] = value
}
})
})
}
}
$: $worldStore?.outputsById[componentId] &&
subscribeToAllOutputs($worldStore.outputsById[componentId])
</script>

<ObjectViewer json={object} on:select topBrackets={true} />
Expand Up @@ -16,7 +16,8 @@
export let appPath: string
const { connectingInput, staticOutputs, app } = getContext<AppEditorContext>('AppEditorContext')
const { connectingInput, staticOutputs, app, worldStore } =
getContext<AppEditorContext>('AppEditorContext')
function connectInput(id: string, name: string) {
if ($connectingInput) {
Expand Down Expand Up @@ -152,16 +153,15 @@

<PanelSection title="Outputs">
{#each Object.entries($staticOutputs) as [componentId, outputs], index}
{#if outputs.length > 0}
{#if outputs.length > 0 && $worldStore?.outputsById[componentId]}
<Badge color="blue">Component: {componentId}</Badge>

<div class="w-full p-2 rounded-xs border">
<ComponentOutputViewer
{outputs}
{componentId}
on:select={({ detail }) => {
const [output] = detail.split('.')
connectInput(componentId, output)
connectInput(componentId, detail)
}}
/>
</div>
Expand Down
Expand Up @@ -26,8 +26,7 @@
function removeGridElement() {
if (component) {
const COLS = 6
const index = $app.grid.findIndex((gridComponent) => gridComponent.data.id === component?.id)
$app.grid.splice(index, 1)
$app.grid = $app.grid.filter((gridComponent) => gridComponent.data.id !== component?.id)
$app.grid = gridHelp.adjust($app.grid, COLS)
// Delete static inputs
Expand Down
Expand Up @@ -8,7 +8,7 @@
export let inputSpecs: InputsSpec
const userTypeKeys = ['schemaProperty', 'defaultValue', 'value']
const staticTypeKeys = ['visible', 'value', 'fieldType']
const staticTypeKeys = ['value']
const dynamicTypeKeys = ['id', 'name', 'defaultValue']
function sanitizeInputSpec(type: 'user' | 'static' | 'output', inputSpecKey: string) {
Expand Down
7 changes: 5 additions & 2 deletions frontend/src/lib/components/apps/rx.ts
Expand Up @@ -45,10 +45,13 @@ export function buildObservableWorld() {
}
} else if (inputSpec.type === 'output') {
const input = cachedInput(next)
let obs = observables[`${inputSpec.id}.${inputSpec.name}`]

const [name] = inputSpec.name ? inputSpec.name.split('.') : [undefined]

let obs = observables[`${inputSpec.id}.${name}`]

if (!obs) {
console.warn('Observable at ' + inputSpec.id + '.' + inputSpec.name + ' not found')
console.warn('Observable at ' + inputSpec.id + '.' + name + ' not found')
return {
peak: () => undefined,
next: () => {}
Expand Down
20 changes: 20 additions & 0 deletions frontend/src/lib/components/apps/utils.ts
Expand Up @@ -73,3 +73,23 @@ export const displayData = {
icon: faBarChart
}
}

export function accessPropertyByPath<T>(object: T, path: string): T | undefined {
// convert indexes to properties
path = path.replace(/\[(\w+)\]/g, '.$1')
// strip a leading dot
path = path.replace(/^\./, '')

let a = path.split('.')

for (let i = 0, depth = a.length; i < depth; ++i) {
let key = a[i]
if (object[key]) {
object = object[key]
} else {
// Value not found
return
}
}
return object
}

0 comments on commit ab481b3

Please sign in to comment.