fix(frontend): fix Decision Tree + fix Infinite list default ts code … (#3993)

* fix(frontend): fix Decision Tree + fix Infinite list default ts code indentation + add missing reset column defs button

* fix(frontend): improves how columnDefs are computed when we change the table

* fix(frontend): improved code quality

* fix(frontend): fix current node id

* fix(frontend): wip

* fix(frontend): wip

* fix(frontend): done

* fix(frontend): done

* fix(frontend): fix node deletion

* fix(frontend): fix node deletion

* fix(frontend): simplify node deletion
This commit is contained in:
Faton Ramadani
2024-06-28 17:24:45 +02:00
committed by GitHub
parent ab95a06637
commit 49b6cf1f37
9 changed files with 200 additions and 100 deletions
@@ -64,6 +64,10 @@
lastTable = undefined
}
clearColumnDefs()
}
function clearColumnDefs() {
const gridItem = findGridItem($app, id)
if (!gridItem) {
@@ -445,6 +449,11 @@
state = undefined
// If in the mean time the table has changed, we don't want to update the columnDefs
if (lastTable !== table) {
return
}
//@ts-ignore
gridItem.data.configuration.columnDefs = { value: ncols, type: 'static', loading: false }
gridItem.data = gridItem.data
@@ -669,6 +678,11 @@
on:update={onUpdate}
on:delete={onDelete}
allowColumnDefsActions={false}
on:recompute={() => {
lastTable = undefined
clearColumnDefs()
listColumnsIfAvailable()
}}
{actions}
/>
{/key}
@@ -301,6 +301,9 @@
agGrid: { api: e.api, columnApi: e.columnApi },
setSelectedIndex: (index) => {
e.api.getRowNode(index.toString())?.setSelected(true)
},
recompute: () => {
dispatch('recompute')
}
}
api = e.api
@@ -17,95 +17,127 @@
let currentNodeId: string = $worldStore.outputsById[id]?.currentNodeId?.peak() ?? 'a'
$worldStore.outputsById[id]?.currentNodeId?.subscribe(
{
id: id,
next: (value) => {
currentNodeId = value
}
},
currentNodeId
)
$: if (nodes[$debuggingComponents[id] ?? 0]?.id === undefined) {
currentNodeId = ''
$componentControl?.[id]?.setTab?.(0)
$debuggingComponents = Object.fromEntries(
Object.entries($debuggingComponents).filter(([key]) => key !== id)
function subscribeToCurrentNode(id: string) {
return $worldStore.outputsById[id]?.currentNodeId?.subscribe(
{
id: `id-${id}-${currentNodeId}`,
next: (value) => {
currentNodeId = value
}
},
currentNodeId
)
}
let subscription = subscribeToCurrentNode(id)
function onDebugNode(debuggedNodeIndex: number | undefined) {
if (debuggedNodeIndex === undefined) {
return
}
if (debuggedNodeIndex !== undefined && nodes[debuggedNodeIndex]?.id === undefined) {
currentNodeId = nodes[0]?.id ?? ''
$componentControl?.[id]?.setTab?.(0)
$debuggingComponents = Object.fromEntries(
Object.entries($debuggingComponents).filter(([key]) => key !== id)
)
}
}
$: onDebugNode($debuggingComponents[id])
let renderCount: number = 0
let lastNodes: DecisionTreeNode[] = nodes
function onNodesChange(newNodes: DecisionTreeNode[]) {
if (JSON.stringify(newNodes) !== JSON.stringify(lastNodes)) {
lastNodes = newNodes
if (subscription) {
subscription?.()
}
subscription = subscribeToCurrentNode(id)
renderCount++
}
}
$: onNodesChange(nodes)
</script>
<button
title={'Debug tabs'}
class={classNames(
'text-2xs py-0.5 font-bold w-fit border cursor-pointer rounded-sm',
isDebugging($debuggingComponents, id)
? 'bg-red-100 text-red-600 border-red-500 hover:bg-red-200 hover:text-red-800'
: 'bg-indigo-100 text-indigo-600 border-indigo-500 hover:bg-indigo-200 hover:text-indigo-800'
)}
on:click={() => dispatch('triggerInlineEditor')}
on:pointerdown|stopPropagation
>
<ButtonDropdown hasPadding={false}>
<svelte:fragment slot="buttonReplacement">
<div class="px-1">
{#if isDebugging($debuggingComponents, id)}
<div class="flex flex-row items-center gap-2">
{`Debugging node ${nodes[$debuggingComponents[id] ?? 0]?.id}`}
<button
on:click={() => {
$componentControl?.[id]?.setTab?.(0)
{#key renderCount}
<button
title={'Debug tabs'}
class={classNames(
'text-2xs py-0.5 font-bold w-fit border cursor-pointer rounded-sm',
isDebugging($debuggingComponents, id)
? 'bg-red-100 text-red-600 border-red-500 hover:bg-red-200 hover:text-red-800'
: 'bg-indigo-100 text-indigo-600 border-indigo-500 hover:bg-indigo-200 hover:text-indigo-800'
)}
on:click={() => dispatch('triggerInlineEditor')}
on:pointerdown|stopPropagation
>
<ButtonDropdown hasPadding={false}>
<svelte:fragment slot="buttonReplacement">
<div class="px-1">
{#if isDebugging($debuggingComponents, id)}
<div class="flex flex-row items-center gap-2">
{`Debugging node ${nodes[$debuggingComponents[id] ?? 0]?.id}`}
<button
on:click={() => {
$componentControl?.[id]?.setTab?.(0)
$debuggingComponents = Object.fromEntries(
Object.entries($debuggingComponents).filter(([key]) => key !== id)
)
}}
$debuggingComponents = Object.fromEntries(
Object.entries($debuggingComponents).filter(([key]) => key !== id)
)
}}
>
<X size={14} />
</button>
</div>
{:else}
{`Debug nodes (current node: ${currentNodeId})`}
{/if}
</div>
</svelte:fragment>
<svelte:fragment slot="items">
{#each nodes ?? [] as node, index}
<MenuItem
on:click={() => {
$componentControl?.[id]?.setTab?.(index)
$debuggingComponents[id] = index
}}
>
<div
class={classNames(
'!text-tertiary text-left px-4 py-2 gap-2 cursor-pointer hover:bg-gray-100 !text-xs font-semibold'
)}
>
<X size={14} />
</button>
</div>
{:else}
{`Debug nodes (current node: ${currentNodeId})`}
{/if}
</div>
</svelte:fragment>
<svelte:fragment slot="items">
{#each nodes ?? [] as node, index}
{`Debug node ${node.label}`}
</div>
</MenuItem>
{/each}
<MenuItem
on:click={() => {
$componentControl?.[id]?.setTab?.(index)
$componentControl?.[id]?.setTab?.(0)
$debuggingComponents[id] = index
$debuggingComponents = Object.fromEntries(
Object.entries($debuggingComponents).filter(([key]) => key !== id)
)
}}
>
<div
class={classNames(
'!text-tertiary text-left px-4 py-2 gap-2 cursor-pointer hover:bg-gray-100 !text-xs font-semibold'
'!text-red-600 text-left px-4 py-2 gap-2 cursor-pointer hover:bg-gray-100 !text-xs font-semibold'
)}
>
{`Debug node ${node.label}`}
{`Reset debug mode`}
</div>
</MenuItem>
{/each}
<MenuItem
on:click={() => {
$componentControl?.[id]?.setTab?.(0)
$debuggingComponents = Object.fromEntries(
Object.entries($debuggingComponents).filter(([key]) => key !== id)
)
}}
>
<div
class={classNames(
'!text-red-600 text-left px-4 py-2 gap-2 cursor-pointer hover:bg-gray-100 !text-xs font-semibold'
)}
>
{`Reset debug mode`}
</div>
</MenuItem>
</svelte:fragment>
</ButtonDropdown>
</button>
</svelte:fragment>
</ButtonDropdown>
</button>
{/key}
@@ -115,9 +115,9 @@ export async function main(db: Postgresql) {
email: string;
created_at: string;
};
const GLOBAL_COUNT = 25000;
const GLOBAL_COUNT = 25000;
function getUsers(limit: number, offset: number): User[] {
function getUsers(limit: number, offset: number): User[] {
if (offset > GLOBAL_COUNT) {
return []
@@ -130,28 +130,28 @@ export async function main(db: Postgresql) {
email: \`user\${offset + index}@example.com\`,
created_at: new Date().toISOString(),
}));
}
}
export async function main(offset: number, limit: number, orderBy: string, isDesc: boolean, search: string): Promise<User[]> {
export async function main(offset: number, limit: number, orderBy: string, isDesc: boolean, search: string): Promise<User[]> {
let users = getUsers(limit, offset);
if (orderBy && Object.keys(users[0]).includes(orderBy)) {
users.sort((a, b) => {
const aValue = a[orderBy as keyof User];
const bValue = b[orderBy as keyof User];
const aValue = a[orderBy as keyof User];
const bValue = b[orderBy as keyof User];
if (typeof aValue === 'string' && typeof bValue === 'string') {
return isDesc ? bValue.localeCompare(aValue) : aValue.localeCompare(bValue);
}
return 0;
if (typeof aValue === 'string' && typeof bValue === 'string') {
return isDesc ? bValue.localeCompare(aValue) : aValue.localeCompare(bValue);
}
return 0;
});
}
if (search) {
return users.filter(user =>
Object.values(user).some(value =>
typeof value === 'string' && value.toLowerCase().includes(search.toLowerCase())
)
Object.values(user).some(value =>
typeof value === 'string' && value.toLowerCase().includes(search.toLowerCase())
)
);
}
@@ -8,6 +8,7 @@
import { generateRandomString, pluralize } from '$lib/utils'
import Toggle from '$lib/components/Toggle.svelte'
import QuickAddColumn from './QuickAddColumn.svelte'
import RefreshDatabaseStudioTable from './RefreshDatabaseStudioTable.svelte'
export let componentInput: StaticInput<any[]> & { loading?: boolean }
export let subFieldType: InputType | undefined = undefined
@@ -192,11 +193,23 @@
componentInput.value = reorderedValues
}
let items = (Array.isArray(componentInput.value) ? componentInput.value : [])
.filter((x) => x != undefined)
.map((item, index) => {
return { value: item, id: generateRandomString() }
})
let items = getItems(componentInput)
function getItems(componentInput: StaticInput<any[]> & { loading?: boolean }) {
return (Array.isArray(componentInput.value) ? componentInput.value : [])
.filter((x) => x != undefined)
.map((item) => {
return { value: item, id: generateRandomString() }
})
}
function clearTableOnComponentReset(value: any[] | undefined) {
if (Array.isArray(value) && value.length === 0 && items.length > 0) {
items = []
}
}
$: subFieldType === 'db-explorer' && clearTableOnComponentReset(componentInput?.value)
$: items != undefined && handleItemsChange()
@@ -287,11 +300,14 @@
{/if}
{#if subFieldType === 'db-explorer'}
{#if componentInput.loading}
<div class="flex flex-row gap-2 w-full items-center text-xs">
<Loader2 class="animate-spin" size={14} />
Loading columns defintions...
<div class="flex flex-row gap-2 w-full items-center">
<div class="flex flex-row gap-2 w-full items-center text-xs">
<Loader2 class="animate-spin" size={14} />
Loading columns defintions...
</div>
</div>
{/if}
<RefreshDatabaseStudioTable {id} />
{/if}
{#if subFieldType !== 'db-explorer'}
<Button size="xs" color="light" startIcon={{ icon: Plus }} on:click={() => addElementByType()}>
@@ -3,7 +3,7 @@
import { Network, Plus, Trash } from 'lucide-svelte'
import type { AppComponent, DecisionTreeNode } from '../component'
import { Pane, Splitpanes } from 'svelte-splitpanes'
import { setContext } from 'svelte'
import { getContext, setContext } from 'svelte'
import InputsSpecEditor from './InputsSpecEditor.svelte'
import Section from '$lib/components/Section.svelte'
import { writable } from 'svelte/store'
@@ -11,6 +11,7 @@
import { addNewBranch, removeNode } from './decisionTree/utils'
import Label from '$lib/components/Label.svelte'
import { debounce } from '$lib/utils'
import type { AppViewerContext } from '../../types'
export let component: AppComponent
export let nodes: DecisionTreeNode[]
@@ -21,6 +22,8 @@
let paneHeight = 0
let renderCount = 0
const { debuggingComponents } = getContext<AppViewerContext>('AppViewerContext')
const selectedNodeId = writable<string | undefined>(undefined)
$: selectedNode = nodes?.find((node) => node.id == $selectedNodeId)
@@ -64,6 +67,11 @@
variant="border"
on:click={() => {
nodes = removeNode(nodes, selectedNode)
$debuggingComponents = Object.fromEntries(
Object.entries($debuggingComponents).filter(([key]) => key !== component.id)
)
renderCount++
}}
disabled={selectedNode?.next?.length > 1 || nodes.length == 1}
@@ -0,0 +1,21 @@
<script lang="ts">
import { Button } from '$lib/components/common'
import { RotateCcw } from 'lucide-svelte'
import { getContext } from 'svelte'
import type { AppViewerContext } from '../../types'
export let id: string | undefined = undefined
const { componentControl } = getContext<AppViewerContext>('AppViewerContext')
function onRefresh() {
if (!id) return
$componentControl[id]?.recompute?.()
}
</script>
{#if id}
<Button size="xs2" color="light" startIcon={{ icon: RotateCcw }} on:click={() => onRefresh()}>
Force refresh
</Button>
{/if}
@@ -222,13 +222,18 @@
}
case 'delete': {
const graphhNodeIndex = nodes.findIndex((node) => node.id == graphNode?.id)
const graphNodeIndex = nodes.findIndex((node) => node.id == graphNode?.id)
if (graphhNodeIndex > -1) {
deleteSubgrid(graphhNodeIndex)
if (graphNodeIndex > -1) {
deleteSubgrid(graphNodeIndex)
}
nodes = removeNode(nodes, graphNode)
$debuggingComponents = Object.fromEntries(
Object.entries($debuggingComponents).filter(([key]) => key !== component.id)
)
break
}
case 'addBranch': {
@@ -259,6 +259,7 @@ export type AppViewerContext = {
validateAll?: () => void
clearFiles?: () => void
showToast?: (message: string, error?: boolean) => void
recompute?: () => void
}
>
>