// The React state and reducer of a D3Graph component showing an onion graph state. import {PrimitiveValue} from "onion/types"; import {INodeState, IValueState, GraphStateListener} from "onion/graph_state"; import {D3GraphData, D3NodeData, D3LinkData} from "../d3graph"; export type D3OnionNodeData = D3NodeData; export type D3OnionLinkData = D3LinkData; export type D3OnionGraphData = D3GraphData; interface AddNode {type:'addNode', ns: INodeState, x: number, y: number} interface RemoveNode {type:'removeNode', id: PrimitiveValue} interface AddValue {type:'addValue', vs: IValueState, x: number, y: number} interface RemoveValue {type:'removeValue', value: PrimitiveValue} interface AddLinkToNode {type:'addLinkToNode', sourceId: PrimitiveValue, label: string, targetId: PrimitiveValue} interface AddLinkToValue {type:'addLinkToValue', sourceId: PrimitiveValue, label: string, targetValue: PrimitiveValue} interface RemoveLink {type:'removeLink', sourceId: PrimitiveValue, label: string} export type D3OnionGraphAction = Readonly | Readonly | Readonly | Readonly | Readonly | Readonly | Readonly; export function onionGraphReducer(prevState: D3OnionGraphData, action: D3OnionGraphAction): D3OnionGraphData { switch (action.type) { case 'addNode': { return { nodes: [...prevState.nodes, { id: nodeNodeId(action.ns.creation.id.value), label: JSON.stringify(action.ns.creation.id.value), x: action.x, y: action.y, color: "darkturquoise", obj: action.ns, bold: false, }], links: prevState.links, }; } case 'removeNode': { return { nodes: prevState.nodes.filter(n => !n.obj.isNode(action.id)), links: prevState.links, }; } case 'addValue': { return { nodes: [...prevState.nodes, { id: valueNodeId(action.vs.value), label: JSON.stringify(action.vs.value), x: action.x, y: action.y, color: "darkorange", obj: action.vs, bold: false, }], links: prevState.links, }; } case 'removeValue': { return { nodes: prevState.nodes.filter(n => !n.obj.isValue(action.value)), links: prevState.links, }; } case 'addLinkToNode': { return { nodes: prevState.nodes, links: [...prevState.links, { source: prevState.nodes.find(n => n.obj.isNode(action.sourceId)), // AR: here is the problem! target: prevState.nodes.find(n => n.obj.isNode(action.targetId)), label: action.label, color: 'black', obj: null, }], }; } case 'addLinkToValue': { return { nodes: prevState.nodes, links: [...prevState.links, { source: prevState.nodes.find(n => n.obj.isNode(action.sourceId)), target: prevState.nodes.find(n => n.obj.isValue(action.targetValue)), label: action.label, color: 'black', obj: null, }], }; } case 'removeLink': { return { nodes: prevState.nodes, links: prevState.links.filter(l => l.source.obj.creation.id.value !== action.sourceId || l.label !== action.label), }; } } } function nodeNodeId(nodeId: PrimitiveValue) { return "N"+JSON.stringify(nodeId); } function valueNodeId(value: PrimitiveValue) { return "V"+JSON.stringify(value); } // Responds to changes to a GraphState object by updating the React state of a d3Graph component. export class D3GraphUpdater implements GraphStateListener { readonly setGraph: (cb: (prevGraph: D3OnionGraphData) => D3OnionGraphData) => void; // SVG coordinates for newly created nodes // This information cannot be part of our NodeCreation deltas, but it must come from somewhere... x: number; y: number; constructor(setGraph: (cb: (prevGraph: D3OnionGraphData) => D3OnionGraphData) => void, x, y) { this.setGraph = setGraph; this.x = x; this.y = y; } createNode(ns: INodeState) { this.setGraph(prevGraph => onionGraphReducer(prevGraph, {type: 'addNode', ns, x: this.x, y: this.y})); } createValue(vs: IValueState) { this.setGraph(prevGraph => onionGraphReducer(prevGraph, {type: 'addValue', vs, x: this.x, y: this.y})); } deleteNode(id: PrimitiveValue) { this.setGraph(prevGraph => onionGraphReducer(prevGraph, {type: 'removeNode', id})); } deleteValue(value: PrimitiveValue) { this.setGraph(prevGraph => onionGraphReducer(prevGraph, {type: 'removeValue', value})); } createLinkToNode(sourceId: PrimitiveValue, label: string, targetId: PrimitiveValue) { this.setGraph(prevGraph => onionGraphReducer(prevGraph, {type: 'addLinkToNode', sourceId, label, targetId})); } createLinkToValue(sourceId: PrimitiveValue, label: string, targetValue: PrimitiveValue) { this.setGraph(prevGraph => onionGraphReducer(prevGraph, {type: 'addLinkToValue', sourceId, label, targetValue})); } deleteLink(sourceId: PrimitiveValue, label: string) { this.setGraph(prevGraph => onionGraphReducer(prevGraph, {type: 'removeLink', sourceId, label})); } }