feat: added node-tooltips to the editor
This commit is contained in:
committed by
Björn Otgaar
parent
5385bd72b1
commit
566c4c18cc
@@ -3,7 +3,8 @@ import { useReactFlow, type XYPosition } from '@xyflow/react';
|
||||
import { type ReactNode, useCallback, useRef, useState } from 'react';
|
||||
import useFlowStore from '../VisProgStores';
|
||||
import styles from '../../VisProg.module.css';
|
||||
import { NodeDefaults, type NodeTypes } from '../NodeRegistry'
|
||||
import { NodeDefaults, type NodeTypes} from '../NodeRegistry'
|
||||
import {Tooltip} from "./NodeComponents.tsx";
|
||||
|
||||
/**
|
||||
* Props for a draggable node within the drag-and-drop toolbar.
|
||||
@@ -47,14 +48,17 @@ function DraggableNode({ className, children, nodeType, onDrop }: DraggableNodeP
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={className}
|
||||
ref={draggableRef}
|
||||
id={`draggable-${nodeType}`}
|
||||
data-testid={`draggable-${nodeType}`}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
<Tooltip nodeType={nodeType}>
|
||||
<div>
|
||||
<div className={className}
|
||||
ref={draggableRef}
|
||||
id={`draggable-${nodeType}`}
|
||||
data-testid={`draggable-${nodeType}`}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</Tooltip>)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -133,7 +137,7 @@ export function DndToolbar() {
|
||||
}));
|
||||
|
||||
return (
|
||||
<div className={`flex-col gap-lg padding-md ${styles.innerDndPanel}`}>
|
||||
<div className={`flex-col gap-lg padding-md ${styles.innerDndPanel}`} id={"draggable-sidebar"}>
|
||||
<div className="description">
|
||||
You can drag these nodes to the pane to create new nodes.
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import { NodeToolbar } from '@xyflow/react';
|
||||
import {NodeToolbar} 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.
|
||||
*
|
||||
@@ -24,14 +29,94 @@ type ToolbarProps = {
|
||||
* @constructor
|
||||
*/
|
||||
export function Toolbar({nodeId, allowDelete}: ToolbarProps) {
|
||||
const {deleteNode} = useFlowStore();
|
||||
const {nodes, deleteNode} = useFlowStore();
|
||||
|
||||
const deleteParentNode = ()=> {
|
||||
|
||||
const deleteParentNode = () => {
|
||||
deleteNode(nodeId);
|
||||
}
|
||||
};
|
||||
|
||||
const nodeType = nodes.find((node) => node.id === nodeId)?.type as keyof typeof NodeTooltips;
|
||||
return (
|
||||
<NodeToolbar>
|
||||
<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
|
||||
}
|
||||
|
||||
@@ -31,4 +31,13 @@
|
||||
filter: drop-shadow(0 0 0.25rem green);
|
||||
}
|
||||
|
||||
:global(.react-flow__handle) {
|
||||
width: calc(8px / var(--flow-zoom, 1));
|
||||
height: calc(8px / var(--flow-zoom, 1));
|
||||
transition: width 0.1s ease, height 0.1s ease;
|
||||
min-width: 8px;
|
||||
min-height: 8px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user