- Implemented a help system for different node types (config, render, variable, function) with detailed usage instructions. - Created a node registry to manage node types, including registration, retrieval, and validation of connections between nodes. - Defined central node and edge types for the application to streamline state management. - Added Nunjucks autocomplete functionality to enhance user experience in template editing. - Developed a syntax highlighting parser for PlantUML and Nunjucks within the CodeMirror editor. - Registered built-in node types at application startup, including their default configurations and help entries. - Introduced a theme context provider to manage light/dark mode preferences across the application. - Created utility functions for class name management using clsx and tailwind-merge. - Set up Tailwind CSS for styling with custom themes and responsive design. - Configured Vite for development with proxy settings for backend API calls and Kroki diagram service.
98 lines
3.6 KiB
TypeScript
98 lines
3.6 KiB
TypeScript
/**
|
|
* Use as second argument to React.memo() for node components.
|
|
* Skips re-render when only position (or other unrelated props) changed,
|
|
* so dragging one node doesn't force other nodes to re-render.
|
|
*/
|
|
|
|
import {
|
|
getIdPrefix,
|
|
getDefaultDataForType as getDefaultDataFromRegistry,
|
|
getResetDataForType as getResetDataFromRegistry,
|
|
getDefaultStyle,
|
|
} from './nodeRegistry'
|
|
|
|
export function nodePropsAreEqual<P extends { id?: string; data?: any; width?: number; height?: number; selected?: boolean }>(
|
|
prev: P,
|
|
next: P
|
|
): boolean {
|
|
return (
|
|
prev.id === next.id &&
|
|
prev.data === next.data &&
|
|
prev.width === next.width &&
|
|
prev.height === next.height &&
|
|
prev.selected === next.selected
|
|
)
|
|
}
|
|
|
|
/** @deprecated Use getIdPrefix from nodeRegistry for new code. Kept for compatibility. */
|
|
export const PREFIX_BY_TYPE: Record<string, string> = {
|
|
config: 'cfg_',
|
|
render: 'rnd_',
|
|
variable: 'var_',
|
|
function: 'fn_',
|
|
}
|
|
|
|
/** Next node id for type: prefix + 3-digit increasing number (001, 002, …). Uses nodeRegistry for prefix when available. */
|
|
export function getNextNodeId(type: string, existingIds: string[]): string {
|
|
const prefix = getIdPrefix(type)
|
|
const re = new RegExp(`^${prefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}(\\d+)$`)
|
|
let max = 0
|
|
for (const id of existingIds) {
|
|
const m = id.match(re)
|
|
if (m) max = Math.max(max, parseInt(m[1], 10))
|
|
}
|
|
return `${prefix}${String(max + 1).padStart(3, '0')}`
|
|
}
|
|
|
|
/** Recursively replace oldId with newId in string values (for rename propagation) */
|
|
function replaceInData(value: unknown, oldId: string, newId: string): unknown {
|
|
if (typeof value === 'string') return value.split(oldId).join(newId)
|
|
if (value === null || typeof value !== 'object') return value
|
|
if (Array.isArray(value)) return value.map((v) => replaceInData(v, oldId, newId))
|
|
const out: Record<string, unknown> = {}
|
|
for (const [k, v] of Object.entries(value)) out[k] = replaceInData(v, oldId, newId)
|
|
return out
|
|
}
|
|
|
|
/** Update graph after renaming a node: change node id and all references in data and edges */
|
|
export function replaceNodeIdInGraph(
|
|
nodes: Array<{ id: string; data?: any; [k: string]: any }>,
|
|
edges: Array<{ id: string; source: string; target: string; [k: string]: any }>,
|
|
oldId: string,
|
|
newId: string
|
|
): { nodes: typeof nodes; edges: typeof edges } {
|
|
const newNodes = nodes.map((n) =>
|
|
n.id === oldId ? { ...n, id: newId } : { ...n, data: replaceInData(n.data, oldId, newId) as any }
|
|
)
|
|
const newEdges = edges.map((e) => ({
|
|
...e,
|
|
id: e.id.includes(oldId) ? e.id.split(oldId).join(newId) : e.id,
|
|
source: e.source === oldId ? newId : e.source,
|
|
target: e.target === oldId ? newId : e.target,
|
|
}))
|
|
return { nodes: newNodes, edges: newEdges }
|
|
}
|
|
|
|
/** @deprecated Use getDefaultStyle from nodeRegistry. Kept for compatibility. */
|
|
export const DEFAULT_NODE_STYLE: Record<string, { width: number; height: number }> = {
|
|
config: { width: 320, height: 320 },
|
|
render: { width: 384, height: 320 },
|
|
variable: { width: 224, height: 180 },
|
|
function: { width: 288, height: 260 },
|
|
}
|
|
|
|
/** Default data for a new node. Uses nodeRegistry when type is registered. */
|
|
export function getDefaultDataForType(type: string, newId?: string): any {
|
|
return getDefaultDataFromRegistry(type, newId)
|
|
}
|
|
|
|
/** Data for Reset action. Uses nodeRegistry when type is registered. */
|
|
export function getResetDataForType(type: string, nodeId?: string): any {
|
|
return getResetDataFromRegistry(type, nodeId)
|
|
}
|
|
|
|
/** Default style for a type. Uses nodeRegistry when type is registered. */
|
|
export function getDefaultStyleForType(type: string): { width: number; height: number } {
|
|
return getDefaultStyle(type)
|
|
}
|