Newer
Older
import React, { useState } from "react";
import { Node } from "../../common/graph/node";
import { NodeType } from "../../common/graph/nodetype";

Matthias Konitzny
committed
import { NodeDataChangeRequest } from "../editor";
import ReferencesEditor from "./referenceseditor";

Matthias Konitzny
committed
type NodeDetailsProps = {
selectedNodes: Node[];
idToObjectType: Map<number, NodeType>;
onNodeDataChange: (
requests: NodeDataChangeRequest[],
createCheckpoint?: boolean
) => void;
createCheckpoint: (description: string) => void;

Matthias Konitzny
committed
function NodeDetails({
selectedNodes,
idToObjectType,

Matthias Konitzny
committed
onNodeDataChange,

Matthias Konitzny
committed
}: NodeDetailsProps) {
const [changed, setChanged] = useState(false);

Matthias Konitzny
committed
if (selectedNodes.length == 0) {
return <div id="nodedetails">No node selected.</div>;

Matthias Konitzny
committed
const getCollectiveValue = function <ValueType>(
getter: (n: Node) => ValueType,
defaultValue: ValueType
) {
const referenceValue = getter(selectedNodes[0]);
const differentValueFound = selectedNodes.some(
(n: Node) => getter(n) !== referenceValue

Matthias Konitzny
committed
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),

Matthias Konitzny
committed
type: getCollectiveValue((n) => n.type, undefined),
visibleAfter: getCollectiveValue((n) => n.visibleAfter, undefined),

Matthias Konitzny
committed
};
const handleDataChange = function <ValueType>(
key: keyof NodeDataChangeRequest,
value: ValueType
) {
if (!changed) {
setChanged(true);
}

Matthias Konitzny
committed
Object.assign(referenceData, { [key]: value });

Matthias Konitzny
committed
onNodeDataChange(
// Generate changes for individual nodes if multiple nodes are selected

Matthias Konitzny
committed
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,
});

Matthias Konitzny
committed
};
const handleBlur = () => {
if (changed) {
createCheckpoint(`Modified ${selectedNodes.length} node(s) data.`);
setChanged(false);
}
};

Matthias Konitzny
committed
return (
<div id="nodedetails" onBlur={handleBlur}>

Matthias Konitzny
committed
{selectedNodes.length === 1 ? (

Matthias Konitzny
committed
id="node-name"
name="node-name"
placeholder="Enter name"
value={referenceData.name ?? ""}
onChange={(event) =>

Matthias Konitzny
committed
handleDataChange("name", event.target.value)

Matthias Konitzny
committed
></input>

Matthias Konitzny
committed
) : (
<h3>{selectedNodes.length} nodes selected</h3>
)}
{selectedNodes.length === 1 && (
<h4>Description</h4>

Matthias Konitzny
committed
<textarea
id="node-description"
name="node-description"
placeholder={"Enter description"}
value={referenceData.description ?? ""}
onChange={(event) =>

Matthias Konitzny
committed
handleDataChange("description", event.target.value)

Matthias Konitzny
committed
></textarea>

Matthias Konitzny
committed
)}
<div>
<h4>Icon Image</h4>

Matthias Konitzny
committed
{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>

Matthias Konitzny
committed
{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>

Matthias Konitzny
committed
<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))

Matthias Konitzny
committed
)
}
>
<option className="empty-select-option" disabled></option>
{[...idToObjectType.values()].map((type) => (

Matthias Konitzny
committed
<option key={type.id} value={type.id}>
{type.name}
</option>
))}
</select>
</div>
<div>

Matthias Konitzny
committed
<input
type="text"
placeholder="Video URL"
id="node-video"
name="node-video"
value={referenceData.video ?? ""}
onChange={(event) =>
handleDataChange("video", event.target.value)
}
></input>
</div>
<h4>Visible after date</h4>
<DateVisibilityInput
date={referenceData.visibleAfter}
onDateChange={(date) =>
handleDataChange("visibleAfter", date)
{selectedNodes.length === 1 && (
<div>
<h4>References</h4>
<ReferencesEditor
references={referenceData.references ?? []}
onReferencesChange={(references) =>
handleDataChange("references", references)
}
/>
</div>

Matthias Konitzny
committed
)}
</div>
);

Matthias Konitzny
committed
export default NodeDetails;