-
Matthias Konitzny authoredMatthias Konitzny authored
nodedetails.tsx 8.38 KiB
import React, { useState } from "react";
import { Node } from "../../common/graph/node";
import { NodeType } from "../../common/graph/nodetype";
import "./nodedetails.css";
import { NodeDataChangeRequest } from "../editor";
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),
};
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>
<label htmlFor="node-name" hidden>
Name
</label>
<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>
<label htmlFor="node-description">Description</label>
<br />
<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>
<label htmlFor="node-image">Icon Image</label>
<br />
{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>
<label htmlFor="node-detail-image">Banner Image</label>
<br />
{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>
<label htmlFor="node-type">Type</label>
<br />
<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>
<label htmlFor="node-video">Video</label>
<br />
<input
type="text"
placeholder="Video URL"
id="node-video"
name="node-video"
value={referenceData.video ?? ""}
onChange={(event) =>
handleDataChange("video", event.target.value)
}
></input>
</div>
{selectedNodes.length === 1 ? (
<div>
<label htmlFor="node-references">References</label>{" "}
<small>One URL per line</small>
<br />
<textarea
id="node-references"
name="node-references"
className="bottom-space"
value={referenceData.references ?? ""}
onChange={(event) =>
handleDataChange("references", event.target.value)
}
></textarea>
</div>
) : (
""
)}
</div>
);
}
export default NodeDetails;