chore(react): add back note map link configuration

This commit is contained in:
Elian Doran 2025-10-04 13:04:40 +03:00
parent ad5ff6e41a
commit 2d29d1b41f
No known key found for this signature in database
5 changed files with 57 additions and 57 deletions

View file

@ -169,7 +169,7 @@ const entityMap: Record<string, string> = {
"=": "&#x3D;"
};
function escapeHtml(str: string) {
export function escapeHtml(str: string) {
return str.replace(/[&<>"'`=\/]/g, (s) => entityMap[s]);
}

View file

@ -34,14 +34,10 @@ export default class NoteMapWidget extends NoteContextAwareWidget {
private fixNodes: boolean;
private widgetMode: WidgetMode;
private themeStyle!: string;
private $container!: JQuery<HTMLElement>;
private $styleResolver!: JQuery<HTMLElement>;
private $fixNodesButton!: JQuery<HTMLElement>;
graph!: ForceGraph;
private noteIdToSizeMap!: Record<string, number>;
private zoomLevel!: number;
private nodes!: Node[];
constructor(widgetMode: WidgetMode) {
super();
@ -117,13 +113,6 @@ export default class NoteMapWidget extends NoteContextAwareWidget {
}
});
if (this.mapType === "link") {
this.graph
.linkLabel((l) => `${esc((l as Link).source.name)} - <strong>${esc((l as Link).name)}</strong> - ${esc((l as Link).target.name)}`)
.linkCanvasObject((link, ctx) => this.paintLink(link as Link, ctx))
.linkCanvasObjectMode(() => "after");
}
const nodeLinkRatio = data.nodes.length / data.links.length;
const magnifiedRatio = Math.pow(nodeLinkRatio, 1.5);
const charge = -20 / magnifiedRatio;
@ -148,45 +137,6 @@ export default class NoteMapWidget extends NoteContextAwareWidget {
this.zoomLevel = level;
}
paintLink(link: Link, ctx: CanvasRenderingContext2D) {
if (this.zoomLevel < 5) {
return;
}
ctx.font = `3px ${this.cssData.fontFamily}`;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillStyle = this.cssData.mutedTextColor;
const { source, target } = link;
if (typeof source !== "object" || typeof target !== "object") {
return;
}
if (source.x && source.y && target.x && target.y) {
const x = (source.x + target.x) / 2;
const y = (source.y + target.y) / 2;
ctx.save();
ctx.translate(x, y);
const deltaY = source.y - target.y;
const deltaX = source.x - target.x;
let angle = Math.atan2(deltaY, deltaX);
let moveY = 2;
if (angle < -Math.PI / 2 || angle > Math.PI / 2) {
angle += Math.PI;
moveY = -2;
}
ctx.rotate(angle);
ctx.fillText(link.name, 0, moveY);
}
ctx.restore();
}
renderData(data: Data) {
if (this.widgetMode === "ribbon" && this.note?.type !== "search") {
setTimeout(() => {

View file

@ -1,6 +1,6 @@
import { useEffect, useRef, useState } from "preact/hooks";
import "./NoteMap.css";
import { getMapRootNoteId, getThemeStyle, NoteMapWidgetMode, rgb2hex } from "./utils";
import { getMapRootNoteId, getThemeStyle, MapType, NoteMapWidgetMode, rgb2hex } from "./utils";
import { RefObject } from "preact";
import FNote from "../../entities/fnote";
import { useElementSize, useNoteContext, useNoteLabel } from "../react/hooks";
@ -16,8 +16,6 @@ interface NoteMapProps {
parentRef: RefObject<HTMLElement>;
}
type MapType = "tree" | "link";
export default function NoteMap({ note, widgetMode, parentRef }: NoteMapProps) {
const containerRef = useRef<HTMLDivElement>(null);
const styleResolverRef = useRef<HTMLDivElement>(null);
@ -50,7 +48,8 @@ export default function NoteMap({ note, widgetMode, parentRef }: NoteMapProps) {
noteIdToSizeMap: notesAndRelations.noteIdToSizeMap,
notesAndRelations,
themeStyle: getThemeStyle(),
widgetMode
widgetMode,
mapType
});
graph.graphData(notesAndRelations);
});

View file

@ -1,7 +1,8 @@
import type ForceGraph from "force-graph";
import { Link, Node, NotesAndRelationsData } from "./data";
import { NodeObject } from "force-graph";
import { getColorForNode, NoteMapWidgetMode } from "./utils";
import { getColorForNode, MapType, NoteMapWidgetMode } from "./utils";
import { escapeHtml } from "../../services/utils";
export interface CssData {
fontFamily: string;
@ -16,9 +17,10 @@ interface RenderData {
themeStyle: "light" | "dark";
widgetMode: NoteMapWidgetMode;
notesAndRelations: NotesAndRelationsData;
mapType: MapType;
}
export function setupRendering(graph: ForceGraph, { noteId, themeStyle, widgetMode, noteIdToSizeMap, notesAndRelations, cssData }: RenderData) {
export function setupRendering(graph: ForceGraph, { 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
const neighbours = new Set();
const highlightLinks = new Set();
@ -57,6 +59,46 @@ export function setupRendering(graph: ForceGraph, { noteId, themeStyle, widgetMo
ctx.fillText(title, x, y + Math.round(size * 1.5));
}
function paintLink(link: Link, ctx: CanvasRenderingContext2D) {
if (zoomLevel < 5) {
return;
}
ctx.font = `3px ${cssData.fontFamily}`;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillStyle = cssData.mutedTextColor;
const { source, target } = link;
if (typeof source !== "object" || typeof target !== "object") {
return;
}
if (source.x && source.y && target.x && target.y) {
const x = (source.x + target.x) / 2;
const y = (source.y + target.y) / 2;
ctx.save();
ctx.translate(x, y);
const deltaY = source.y - target.y;
const deltaX = source.x - target.x;
let angle = Math.atan2(deltaY, deltaX);
let moveY = 2;
if (angle < -Math.PI / 2 || angle > Math.PI / 2) {
angle += Math.PI;
moveY = -2;
}
ctx.rotate(angle);
ctx.fillText(link.name, 0, moveY);
}
ctx.restore();
}
// main code for highlighting hovered nodes and neighbours. here we "style" the nodes. the nodes are rendered several hundred times per second.
graph
.d3AlphaDecay(0.01)
@ -92,5 +134,13 @@ export function setupRendering(graph: ForceGraph, { noteId, themeStyle, widgetMo
highlightLinks.clear();
})
.onZoom((zoom) => zoomLevel = zoom.k);
// Link-specific config
if (mapType) {
graph
.linkLabel((l) => `${escapeHtml((l as Link).source.name)} - <strong>${escapeHtml((l as Link).name)}</strong> - ${escapeHtml((l as Link).target.name)}`)
.linkCanvasObject((link, ctx) => paintLink(link as Link, ctx))
.linkCanvasObjectMode(() => "after");
}
}

View file

@ -4,6 +4,7 @@ import hoisted_note from "../../services/hoisted_note";
import { Node } from "./data";
export type NoteMapWidgetMode = "ribbon" | "hoisted";
export type MapType = "tree" | "link";
export function rgb2hex(rgb: string) {
return `#${(rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/) || [])