/**
* @file Example demonstrating type-safe node definitions using generics
*/
import React from "react";
import classes from "./TypedNodesExample.module.css";
import { NodeRendererProps, InspectorRenderProps, createNodeDefinition } from "../../../../types/NodeDefinition";
// For this example, we'll define the types directly
type CounterNodeData = {
label: string;
count: number;
step: number;
};
type TextDisplayData = {
title: string;
content: string;
fontSize: number;
};
// Step 2: Create type-safe node renderers (without module augmentation for this example)
const CounterNodeRenderer = ({ node, onUpdateNode }: NodeRendererProps): React.ReactElement => {
// For the example, we'll use type assertions
const { label, count, step } = node.data as CounterNodeData;
const handleIncrement = () => {
onUpdateNode({ data: { ...node.data, count: count + step } });
};
const handleDecrement = () => {
onUpdateNode({ data: { ...node.data, count: count - step } });
};
return (
{label}
{count}
);
};
const CounterNodeInspector = ({ node, onUpdateNode }: InspectorRenderProps): React.ReactElement => {
const { label, count, step } = node.data as CounterNodeData;
return (
);
};
// Step 3: Create the node definition
export const CounterNodeDefinition = createNodeDefinition({
type: "counter-node",
displayName: "Counter",
description: "A node that maintains a counter",
category: "Interactive",
defaultData: {
label: "Counter",
count: 0,
step: 1,
},
defaultSize: { width: 200, height: 150 },
renderNode: CounterNodeRenderer,
renderInspector: CounterNodeInspector,
ports: [
{
id: "value",
type: "output",
label: "Value",
position: "right",
},
],
});
// Text Display Node
const TextDisplayRenderer = ({ node }: NodeRendererProps): React.ReactElement => {
const { title, content, fontSize } = node.data as TextDisplayData;
return (
);
};
const TextDisplayInspector = ({ node, onUpdateNode }: InspectorRenderProps): React.ReactElement => {
const { title, content, fontSize } = node.data as TextDisplayData;
return (
);
};
export const TextDisplayDefinition = createNodeDefinition({
type: "text-display",
displayName: "Text Display",
description: "Displays formatted text",
category: "Display",
defaultData: {
title: "Text Display",
content: "Enter your text here...",
fontSize: 14,
},
defaultSize: { width: 250, height: 120 },
renderNode: TextDisplayRenderer,
renderInspector: TextDisplayInspector,
ports: [
{
id: "text-input",
type: "input",
label: "Text In",
position: "left",
},
],
});
// Example of using with existing non-typed nodes (backward compatibility)
const LegacyNodeRenderer = ({ node }: NodeRendererProps): React.ReactElement => {
// For non-typed nodes, node.data is just NodeData (Record)
const title = (node.data.title as string) || "Legacy Node";
return (
{title}
This is a legacy node without type safety
);
};
export const LegacyNodeDefinition = createNodeDefinition({
type: "legacy-node", // Works without type registration
displayName: "Legacy Node",
description: "A node without type definitions",
category: "Legacy",
defaultData: {
title: "Legacy Node",
},
renderNode: LegacyNodeRenderer,
});