refactor(notemap): use proper types

This commit is contained in:
Elian Doran 2025-10-04 18:55:26 +03:00
parent d2dda95654
commit cf37549f19
No known key found for this signature in database
3 changed files with 33 additions and 30 deletions

View file

@ -3,9 +3,9 @@ import "./NoteMap.css";
import { getThemeStyle, MapType, NoteMapWidgetMode, rgb2hex } from "./utils"; import { getThemeStyle, MapType, NoteMapWidgetMode, rgb2hex } from "./utils";
import { RefObject } from "preact"; import { RefObject } from "preact";
import FNote from "../../entities/fnote"; import FNote from "../../entities/fnote";
import { useElementSize, useNoteContext, useNoteLabel } from "../react/hooks"; import { useElementSize, useNoteLabel } from "../react/hooks";
import ForceGraph, { LinkObject, NodeObject } from "force-graph"; import ForceGraph from "force-graph";
import { loadNotesAndRelations, Node, NotesAndRelationsData } from "./data"; import { loadNotesAndRelations, NoteMapLinkObject, NoteMapNodeObject, NotesAndRelationsData } from "./data";
import { CssData, setupRendering } from "./rendering"; import { CssData, setupRendering } from "./rendering";
import ActionButton from "../react/ActionButton"; import ActionButton from "../react/ActionButton";
import { t } from "../../services/i18n"; import { t } from "../../services/i18n";
@ -27,7 +27,7 @@ export default function NoteMap({ note, widgetMode, parentRef }: NoteMapProps) {
const [ mapRootIdLabel ] = useNoteLabel(note, "mapRootNoteId"); const [ mapRootIdLabel ] = useNoteLabel(note, "mapRootNoteId");
const mapType: MapType = mapTypeRaw === "tree" ? "tree" : "link"; const mapType: MapType = mapTypeRaw === "tree" ? "tree" : "link";
const graphRef = useRef<ForceGraph<NodeObject, LinkObject<NodeObject>>>(); const graphRef = useRef<ForceGraph<NoteMapNodeObject, NoteMapLinkObject>>();
const containerSize = useElementSize(parentRef); const containerSize = useElementSize(parentRef);
const [ fixNodes, setFixNodes ] = useState(false); const [ fixNodes, setFixNodes ] = useState(false);
const [ linkDistance, setLinkDistance ] = useState(40); const [ linkDistance, setLinkDistance ] = useState(40);
@ -49,7 +49,7 @@ export default function NoteMap({ note, widgetMode, parentRef }: NoteMapProps) {
useEffect(() => { useEffect(() => {
const container = containerRef.current; const container = containerRef.current;
if (!container || !mapRootId) return; if (!container || !mapRootId) return;
const graph = new ForceGraph(container); const graph = new ForceGraph<NoteMapNodeObject, NoteMapLinkObject>(container);
graphRef.current = graph; graphRef.current = graph;
@ -76,11 +76,11 @@ export default function NoteMap({ note, widgetMode, parentRef }: NoteMapProps) {
graph graph
.onNodeClick((node) => { .onNodeClick((node) => {
if (!node.id) return; if (!node.id) return;
appContext.tabManager.getActiveContext()?.setNote((node as Node).id); appContext.tabManager.getActiveContext()?.setNote(node.id);
}) })
.onNodeRightClick((node, e) => { .onNodeRightClick((node, e) => {
if (!node.id) return; if (!node.id) return;
link_context_menu.openContextMenu((node as Node).id, e); link_context_menu.openContextMenu(node.id, e);
}); });
// Set data // Set data

View file

@ -11,28 +11,26 @@ interface GroupedLink {
names: string[]; names: string[];
} }
export interface Node extends NodeObject { export interface NoteMapNodeObject extends NodeObject {
id: string; id: string;
name: string; name: string;
type: string; type: string;
color: string; color: string;
} }
export interface Link extends LinkObject<NodeObject> { export interface NoteMapLinkObject extends LinkObject<NoteMapNodeObject> {
id: string; id: string;
name: string; name: string;
x: number; x?: number;
y: number; y?: number;
source: Node;
target: Node;
} }
export interface NotesAndRelationsData { export interface NotesAndRelationsData {
nodes: Node[]; nodes: NoteMapNodeObject[];
links: { links: {
id: string; id: string;
source: string; source: string | NoteMapNodeObject;
target: string; target: string | NoteMapNodeObject;
name: string; name: string;
}[]; }[];
noteIdToSizeMap: Record<string, number>; noteIdToSizeMap: Record<string, number>;

View file

@ -1,5 +1,5 @@
import type ForceGraph from "force-graph"; import type ForceGraph from "force-graph";
import { Link, Node, NotesAndRelationsData } from "./data"; import { NoteMapLinkObject, NoteMapNodeObject, NotesAndRelationsData } from "./data";
import { LinkObject, NodeObject } from "force-graph"; import { LinkObject, NodeObject } from "force-graph";
import { generateColorFromString, MapType, NoteMapWidgetMode } from "./utils"; import { generateColorFromString, MapType, NoteMapWidgetMode } from "./utils";
import { escapeHtml } from "../../services/utils"; import { escapeHtml } from "../../services/utils";
@ -22,14 +22,14 @@ interface RenderData {
mapType: MapType; mapType: MapType;
} }
export function setupRendering(graph: ForceGraph, { note, noteId, themeStyle, widgetMode, noteIdToSizeMap, notesAndRelations, cssData, mapType }: RenderData) { export function setupRendering(graph: ForceGraph<NoteMapNodeObject, NoteMapLinkObject>, { note, noteId, themeStyle, widgetMode, noteIdToSizeMap, notesAndRelations, cssData, mapType }: RenderData) {
// variables for the hover effect. We have to save the neighbours of a hovered node in a set. Also we need to save the links as well as the hovered node itself // variables for the hover effect. We have to save the neighbours of a hovered node in a set. Also we need to save the links as well as the hovered node itself
const neighbours = new Set(); const neighbours = new Set();
const highlightLinks = new Set(); const highlightLinks = new Set();
let hoverNode: NodeObject | null = null; let hoverNode: NodeObject | null = null;
let zoomLevel: number; let zoomLevel: number;
function getColorForNode(node: Node) { function getColorForNode(node: NoteMapNodeObject) {
if (node.color) { if (node.color) {
return node.color; return node.color;
} else if (widgetMode === "ribbon" && node.id === noteId) { } else if (widgetMode === "ribbon" && node.id === noteId) {
@ -39,7 +39,7 @@ export function setupRendering(graph: ForceGraph, { note, noteId, themeStyle, wi
} }
} }
function paintNode(node: Node, color: string, ctx: CanvasRenderingContext2D) { function paintNode(node: NoteMapNodeObject, color: string, ctx: CanvasRenderingContext2D) {
const { x, y } = node; const { x, y } = node;
if (!x || !y) { if (!x || !y) {
return; return;
@ -72,7 +72,7 @@ export function setupRendering(graph: ForceGraph, { note, noteId, themeStyle, wi
} }
function paintLink(link: Link, ctx: CanvasRenderingContext2D) { function paintLink(link: NoteMapLinkObject, ctx: CanvasRenderingContext2D) {
if (zoomLevel < 5) { if (zoomLevel < 5) {
return; return;
} }
@ -117,16 +117,17 @@ export function setupRendering(graph: ForceGraph, { note, noteId, themeStyle, wi
.d3VelocityDecay(0.08) .d3VelocityDecay(0.08)
.maxZoom(7) .maxZoom(7)
.warmupTicks(30) .warmupTicks(30)
.nodeCanvasObject((_node, ctx) => { .nodeCanvasObject((node, ctx) => {
const node: Node = _node as Node;
if (hoverNode == node) { if (hoverNode == node) {
//paint only hovered node //paint only hovered node
paintNode(node, "#661822", ctx); paintNode(node, "#661822", ctx);
neighbours.clear(); //clearing neighbours or the effect would be maintained after hovering is over neighbours.clear(); //clearing neighbours or the effect would be maintained after hovering is over
for (const _link of notesAndRelations.links) { for (const link of notesAndRelations.links) {
const link = _link as unknown as Link; const { source, target } = link;
if (typeof source !== "object" || typeof target !== "object") continue;
//check if node is part of a link in the canvas, if so add it´s neighbours and related links to the previous defined variables to paint the nodes //check if node is part of a link in the canvas, if so add it´s neighbours and related links to the previous defined variables to paint the nodes
if (link.source.id == node.id || link.target.id == node.id) { if (source.id == node.id || target.id == node.id) {
neighbours.add(link.source); neighbours.add(link.source);
neighbours.add(link.target); neighbours.add(link.target);
highlightLinks.add(link); highlightLinks.add(link);
@ -145,7 +146,7 @@ export function setupRendering(graph: ForceGraph, { note, noteId, themeStyle, wi
hoverNode = node || null; hoverNode = node || null;
highlightLinks.clear(); highlightLinks.clear();
}) })
.nodePointerAreaPaint((node, _, ctx) => paintNode(node as Node, getColorForNode(node as Node), ctx)) .nodePointerAreaPaint((node, _, ctx) => paintNode(node, getColorForNode(node), ctx))
.nodePointerAreaPaint((node, color, ctx) => { .nodePointerAreaPaint((node, color, ctx) => {
if (!node.id) { if (!node.id) {
return; return;
@ -158,7 +159,7 @@ export function setupRendering(graph: ForceGraph, { note, noteId, themeStyle, wi
} }
ctx.fill(); ctx.fill();
}) })
.nodeLabel((node) => escapeHtml((node as Node).name)) .nodeLabel((node) => escapeHtml(node.name))
.onZoom((zoom) => zoomLevel = zoom.k); .onZoom((zoom) => zoomLevel = zoom.k);
// set link width to immitate a highlight effect. Checking the condition if any links are saved in the previous defined set highlightlinks // set link width to immitate a highlight effect. Checking the condition if any links are saved in the previous defined set highlightlinks
@ -171,8 +172,12 @@ export function setupRendering(graph: ForceGraph, { note, noteId, themeStyle, wi
// Link-specific config // Link-specific config
if (mapType) { if (mapType) {
graph graph
.linkLabel((l) => `${escapeHtml((l as Link).source.name)} - <strong>${escapeHtml((l as Link).name)}</strong> - ${escapeHtml((l as Link).target.name)}`) .linkLabel((link) => {
.linkCanvasObject((link, ctx) => paintLink(link as Link, ctx)) const { source, target } = link;
if (typeof source !== "object" || typeof target !== "object") return escapeHtml(link.name);
return `${escapeHtml(source.name)} - <strong>${escapeHtml(link.name)}</strong> - ${escapeHtml(target.name)}`;
})
.linkCanvasObject((link, ctx) => paintLink(link, ctx))
.linkCanvasObjectMode(() => "after"); .linkCanvasObjectMode(() => "after");
} }