127 lines
4.0 KiB
TypeScript
127 lines
4.0 KiB
TypeScript
// This program has been developed by students from the bachelor Computer Science at Utrecht
|
|
// University within the Software Project course.
|
|
// © Copyright Utrecht University (Department of Information and Computing Sciences)
|
|
import {NodeToolbar, useReactFlow} from '@xyflow/react';
|
|
import '@xyflow/react/dist/style.css';
|
|
import {type JSX, useState} from "react";
|
|
import {createPortal} from "react-dom";
|
|
import styles from "../../VisProg.module.css";
|
|
import {NodeTooltips} from "../NodeRegistry.ts";
|
|
import useFlowStore from "../VisProgStores.tsx";
|
|
|
|
|
|
/**
|
|
* Props for the Toolbar component.
|
|
*
|
|
* @property nodeId - The ID of the node this toolbar is attached to.
|
|
* @property allowDelete - If `true`, the delete button is enabled; otherwise disabled.
|
|
*/
|
|
type ToolbarProps = {
|
|
nodeId: string;
|
|
allowDelete: boolean;
|
|
};
|
|
|
|
/**
|
|
* Node Toolbar definition:
|
|
* Handles: node deleting functionality
|
|
* Can be integrated to any custom node component as a React component
|
|
*
|
|
* @param {string} nodeId - The ID of the node for which the toolbar is rendered.
|
|
* @param {boolean} allowDelete - Enables or disables the delete functionality.
|
|
* @returns {React.JSX.Element} A JSX element representing the toolbar.
|
|
* @constructor
|
|
*/
|
|
export function Toolbar({nodeId, allowDelete}: ToolbarProps) {
|
|
const {nodes, deleteNode} = useFlowStore();
|
|
const { deleteElements } = useReactFlow();
|
|
|
|
const deleteParentNode = () => {
|
|
|
|
deleteNode(nodeId, deleteElements);
|
|
};
|
|
|
|
const nodeType = nodes.find((node) => node.id === nodeId)?.type as keyof typeof NodeTooltips;
|
|
return (
|
|
<NodeToolbar className={"flex-row align-center"}>
|
|
<button className="Node-toolbar__deletebutton" onClick={deleteParentNode} disabled={!allowDelete}>delete</button>
|
|
<Tooltip nodeType={nodeType}>
|
|
<div className={styles.nodeToolbarTooltip}>i</div>
|
|
</Tooltip>
|
|
</NodeToolbar>);
|
|
}
|
|
|
|
|
|
type TooltipProps = {
|
|
nodeType?: keyof typeof NodeTooltips;
|
|
children: JSX.Element;
|
|
};
|
|
|
|
|
|
/**
|
|
* A general tooltip component, that can be used as a wrapper for any component
|
|
* that has a nodeType and a corresponding nodeTooltip.
|
|
*
|
|
* currently used to show tooltips for draggable-nodes and nodes inside the editor
|
|
*
|
|
* @param {"start" | "end" | "phase" | "norm" | "goal" | "trigger" | "basic_belief" | undefined} nodeType
|
|
* @param {React.JSX.Element} children
|
|
* @returns {React.JSX.Element}
|
|
* @constructor
|
|
*/
|
|
export function Tooltip({ nodeType, children }: TooltipProps) {
|
|
const [showTooltip, setShowTooltip] = useState(false);
|
|
const [disabled , setDisabled] = useState(false);
|
|
const [coords, setCoords] = useState({ top: 0, left: 0 });
|
|
|
|
const updateTooltipPos = () => {
|
|
const rect = document.getElementById("draggable-sidebar")!.getBoundingClientRect();
|
|
setCoords({
|
|
// Position exactly below the bottom edge of the draggable sidebar (plus a small gap)
|
|
top: rect.bottom + 10,
|
|
left: rect.left + rect.width / 2, // Keep it horizontally centered
|
|
});
|
|
};
|
|
|
|
return nodeType ?
|
|
(<div>
|
|
<div
|
|
onMouseDown={() => {
|
|
updateTooltipPos();
|
|
setShowTooltip(false);
|
|
setDisabled(true);
|
|
}}
|
|
onMouseUp={() => {
|
|
setDisabled(false);
|
|
}}
|
|
onMouseOver={() => {
|
|
if (!disabled) {
|
|
updateTooltipPos();
|
|
setShowTooltip(true);
|
|
}
|
|
}}
|
|
onMouseLeave={ () => setShowTooltip(false)}
|
|
>
|
|
{children}
|
|
</div>
|
|
{showTooltip && createPortal(
|
|
<div
|
|
className={"flex-row"}
|
|
style={{
|
|
pointerEvents: 'none',
|
|
position: 'fixed',
|
|
top: `${coords.top}px`,
|
|
left: `${coords.left}px`,
|
|
transform: 'translateX(-50%)', // Center based on the midpoint
|
|
}}
|
|
>
|
|
<span className={styles.customTooltipHeader}>{nodeType}</span>
|
|
<span className={styles.customTooltip}>
|
|
{NodeTooltips[nodeType] || "Available for drag"}
|
|
</span>
|
|
</div>,
|
|
document.body
|
|
)}
|
|
</div>
|
|
) : children
|
|
}
|