import { useReactFlow } from "@xyflow/react"; import { useState } from "react"; import React from "react"; import Markdown from "react-markdown"; // @ts-ignore import Trash from "~icons/tabler/trash"; import LynxKiteNode from "./LynxKiteNode"; import Table from "./Table"; function toMD(v: any): string { if (typeof v === "string") { return v; } if (Array.isArray(v)) { return v.map(toMD).join("\n\n"); } return JSON.stringify(v); } function displayTable(name: string, df: any) { if (df.data.length > 1) { return ( ); } if (df.data.length) { return (
{df.columns.map((c: string, i: number) => (
{c}
{toMD(df.data[0][i])}
))}
); } return JSON.stringify(df.data); } function relationsToDict(relations: any[]) { if (!relations) { return {}; } return Object.assign({}, ...relations.map((r: any) => ({ [r.name]: r }))); } export type UpdateOptions = { delay?: number }; export default function NodeWithGraphCreationView(props: any) { const reactFlow = useReactFlow(); const [open, setOpen] = useState({} as { [name: string]: boolean }); const display = props.data.display?.value; const tables = display?.dataframes || {}; const singleTable = tables && Object.keys(tables).length === 1; const [relations, setRelations] = useState( relationsToDict(display?.relations) || {}, ); const singleRelation = relations && Object.keys(relations).length === 1; function setParam(name: string, newValue: any, opts: UpdateOptions) { reactFlow.updateNodeData(props.id, { params: { ...props.data.params, [name]: newValue }, __execution_delay: opts.delay || 0, }); } function updateRelation(event: any, relation: any) { event.preventDefault(); const updatedRelation = { ...relation, ...Object.fromEntries(new FormData(event.target).entries()), }; // Avoid mutating React state directly const newRelations = { ...relations }; if (relation.name !== updatedRelation.name) { delete newRelations[relation.name]; } newRelations[updatedRelation.name] = updatedRelation; setRelations(newRelations); // There is some issue with how Yjs handles complex objects (maps, arrays) // so we need to serialize the relations object to a string setParam("relations", JSON.stringify(newRelations), {}); } const addRelation = () => { const new_relation = { name: "new_relation", df: "", source_column: "", target_column: "", source_table: "", target_table: "", source_key: "", target_key: "", }; setRelations({ ...relations, [new_relation.name]: new_relation, }); setOpen({ ...open, [new_relation.name]: true }); }; const deleteRelation = (relation: any) => { const newOpen = { ...open }; delete newOpen[relation.name]; setOpen(newOpen); const newRelations = { ...relations }; delete newRelations[relation.name]; setRelations(newRelations); // There is some issue with how Yjs handles complex objects (maps, arrays) // so we need to serialize the relations object to a string setParam("relations", JSON.stringify(newRelations), {}); }; function displayRelation(relation: any) { // TODO: Dynamic autocomplete return ( { updateRelation(e, relation); }} > {Object.keys(tables).map((name) => ( {tables[relation.source_table] && tables[relation.df].columns.map((name: string) => ( {tables[relation.source_table] && tables[relation.source_table].columns.map((name: string) => ( {tables[relation.source_table] && tables[relation.target_table].columns.map((name: string) => ( ); } return (
Node Tables
{display && [ Object.entries(tables).map(([name, df]: [string, any]) => ( {!singleTable && (
setOpen({ ...open, [name]: !open[name] })} > {name}
)} {(singleTable || open[name]) && displayTable(name, df)}
)), Object.entries(display.others || {}).map(([name, o]) => ( <>
setOpen({ ...open, [name]: !open[name] })} > {name}
{open[name] &&
{(o as any).toString()}
} )), ]}
Relationships
{relations && Object.entries(relations).map(([name, relation]: [string, any]) => (
setOpen({ ...open, [name]: !open[name] })} > {name}
{(singleRelation || open[name]) && displayRelation(relation)}
))}
); }