import React, { useState } from "react"; import { Node } from "../../common/graph/node"; import { NodeType } from "../../common/graph/nodetype"; import "./nodedetails.css"; import { NodeDataChangeRequest } from "../editor"; import ReferencesEditor from "./referenceseditor"; import DateVisibilityInput from "./datevisibilityinput"; type NodeDetailsProps = { selectedNodes: Node[]; idToObjectType: Map<number, NodeType>; onNodeDataChange: ( requests: NodeDataChangeRequest[], createCheckpoint?: boolean ) => void; createCheckpoint: (description: string) => void; }; function NodeDetails({ selectedNodes, idToObjectType, onNodeDataChange, createCheckpoint, }: NodeDetailsProps) { const [changed, setChanged] = useState(false); if (selectedNodes.length == 0) { return <div id="nodedetails">No node selected.</div>; } const getCollectiveValue = function <ValueType>( getter: (n: Node) => ValueType, defaultValue: ValueType ) { const referenceValue = getter(selectedNodes[0]); const differentValueFound = selectedNodes.some( (n: Node) => getter(n) !== referenceValue ); return differentValueFound ? defaultValue : referenceValue; }; const referenceData: NodeDataChangeRequest = { id: -1, name: getCollectiveValue((n) => n.name, undefined), description: getCollectiveValue((n) => n.description, undefined), video: getCollectiveValue((n) => n.video, undefined), icon: getCollectiveValue((n) => n.icon, undefined), banner: getCollectiveValue((n) => n.banner, undefined), references: getCollectiveValue((n) => n.references, undefined), type: getCollectiveValue((n) => n.type, undefined), visibleAfter: getCollectiveValue((n) => n.visibleAfter, undefined), }; const handleDataChange = function <ValueType>( key: keyof NodeDataChangeRequest, value: ValueType ) { if (!changed) { setChanged(true); } Object.assign(referenceData, { [key]: value }); onNodeDataChange( // Generate changes for individual nodes if multiple nodes are selected selectedNodes.map((node) => { const update = Object.fromEntries( Object.entries(referenceData).filter( ([k, v]) => v !== undefined ) ); const defaults = { name: node.name, description: node.description, video: node.video, icon: node.icon, banner: node.banner, references: node.references, type: node.type, }; return Object.assign({}, defaults, update, { id: node.id, }); }), false ); }; const handleBlur = () => { if (changed) { createCheckpoint(`Modified ${selectedNodes.length} node(s) data.`); setChanged(false); } }; return ( <div id="nodedetails" onBlur={handleBlur}> {selectedNodes.length === 1 ? ( <div> <input type="text" id="node-name" name="node-name" placeholder="Enter name" className="bottom-space" value={referenceData.name ?? ""} onChange={(event) => handleDataChange("name", event.target.value) } ></input> </div> ) : ( <h3>{selectedNodes.length} nodes selected</h3> )} {selectedNodes.length === 1 && ( <div> <h4>Description</h4> <textarea id="node-description" name="node-description" className="bottom-space" placeholder={"Enter description"} value={referenceData.description ?? ""} onChange={(event) => handleDataChange("description", event.target.value) } ></textarea> </div> )} <div> <h4>Icon Image</h4> {referenceData.icon && ( <div> <img id="node-image-preview" className="preview-image" src={referenceData.icon} /> <br /> </div> )} <input type="text" id="node-image" name="node-image" placeholder="Image URL" className="bottom-space" value={referenceData.icon ?? ""} onChange={(event) => handleDataChange("icon", event.target.value) } /> </div> <div> <h4>Banner Image</h4> {referenceData.banner && ( <div> <img id="node-image-preview" className="preview-image" src={referenceData.banner} /> <br /> </div> )} <input type="text" id="node-detail-image" name="node-detail-image" placeholder="Image URL" className="bottom-space" value={referenceData.banner ?? ""} onChange={(event) => handleDataChange("banner", event.target.value) } /> </div> <div> <h4>Type</h4> <select id="node-type" name="node-type" className="bottom-space" value={referenceData.type ? referenceData.type.id : ""} onChange={(event) => handleDataChange( "type", idToObjectType.get(Number(event.target.value)) ) } > <option className="empty-select-option" disabled></option> {[...idToObjectType.values()].map((type) => ( <option key={type.id} value={type.id}> {type.name} </option> ))} </select> </div> <div> <h4>Video</h4> <input type="text" placeholder="Video URL" id="node-video" name="node-video" value={referenceData.video ?? ""} onChange={(event) => handleDataChange("video", event.target.value) } ></input> </div> <div> <h4>Visible after date</h4> <DateVisibilityInput date={referenceData.visibleAfter} onDateChange={(date) => handleDataChange("visibleAfter", date) } /> </div> {selectedNodes.length === 1 && ( <div> <h4>References</h4> <ReferencesEditor references={referenceData.references ?? []} onReferencesChange={(references) => handleDataChange("references", references) } /> </div> )} </div> ); } export default NodeDetails;