114 lines
4.6 KiB
JavaScript
114 lines
4.6 KiB
JavaScript
"use strict";
|
|
|
|
/** Render references from the active diagram to concepts linked from other CMaps. */
|
|
export class CmapBoundaryReferenceView {
|
|
constructor({ canvas, zoomFactor, mapTitle, surfaceElement, itemCenter, onOpenReference }) {
|
|
this.canvas = canvas;
|
|
this.zoomFactor = zoomFactor;
|
|
this.mapTitle = mapTitle || "";
|
|
this.surfaceElement = surfaceElement;
|
|
this.itemCenter = itemCenter;
|
|
this.onOpenReference = onOpenReference;
|
|
this.layer = null;
|
|
}
|
|
|
|
clear() {
|
|
if (this.layer) this.layer.remove();
|
|
this.layer = null;
|
|
}
|
|
|
|
render(references = []) {
|
|
this.clear();
|
|
if (!references.length) return;
|
|
const surface = this.surfaceElement();
|
|
if (!surface) return;
|
|
|
|
const layer = document.createElement("div");
|
|
layer.className = "rw-cmap-boundary-layer";
|
|
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
svg.classList.add("rw-cmap-boundary-lines");
|
|
const definitions = document.createElementNS("http://www.w3.org/2000/svg", "defs");
|
|
const marker = document.createElementNS("http://www.w3.org/2000/svg", "marker");
|
|
marker.setAttribute("id", "rw-cmap-boundary-arrow");
|
|
marker.setAttribute("viewBox", "0 0 10 10");
|
|
marker.setAttribute("refX", "9");
|
|
marker.setAttribute("refY", "5");
|
|
marker.setAttribute("markerWidth", "7");
|
|
marker.setAttribute("markerHeight", "7");
|
|
marker.setAttribute("orient", "auto-start-reverse");
|
|
const arrow = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
arrow.setAttribute("d", "M 0 0 L 10 5 L 0 10 z");
|
|
arrow.setAttribute("fill", "#808080");
|
|
marker.append(arrow);
|
|
definitions.append(marker);
|
|
svg.append(definitions);
|
|
layer.append(svg);
|
|
surface.append(layer);
|
|
this.layer = layer;
|
|
|
|
const factor = this.zoomFactor();
|
|
const viewLeft = this.canvas.scrollLeft / factor;
|
|
const viewTop = this.canvas.scrollTop / factor;
|
|
const viewWidth = this.canvas.clientWidth / factor;
|
|
const viewHeight = this.canvas.clientHeight / factor;
|
|
const buttonWidth = 190;
|
|
const occupied = { left: [], right: [] };
|
|
const reserveY = (side, desired) => {
|
|
let y = Math.max(viewTop + 12, Math.min(desired, viewTop + viewHeight - 40));
|
|
while (occupied[side].some((used) => Math.abs(used - y) < 34)) y += 34;
|
|
if (y > viewTop + viewHeight - 40) y = viewTop + 12;
|
|
occupied[side].push(y);
|
|
return y;
|
|
};
|
|
|
|
for (const reference of references) {
|
|
const inside = this.itemCenter(reference.insideRecord);
|
|
const side = inside.x < viewLeft + (viewWidth / 2) ? "left" : "right";
|
|
const x = side === "left" ? viewLeft + 12 : viewLeft + viewWidth - buttonWidth - 12;
|
|
const y = reserveY(side, inside.y - 15);
|
|
const button = document.createElement("button");
|
|
button.type = "button";
|
|
button.className = `rw-cmap-boundary-reference rw-cmap-boundary-reference-${side}`;
|
|
button.style.transform = `translate(${x}px, ${y}px)`;
|
|
button.style.width = `${buttonWidth}px`;
|
|
if (reference.linkingPhraseLabel) {
|
|
button.append(document.createTextNode(reference.linkingPhraseLabel));
|
|
button.append(document.createTextNode(" →"));
|
|
button.append(document.createElement("br"));
|
|
}
|
|
button.append(document.createTextNode(reference.label));
|
|
if (this.mapTitle) {
|
|
button.append(document.createElement("br"));
|
|
const mapLabel = document.createElement("small");
|
|
const mapName = document.createElement("em");
|
|
mapName.textContent = `(${this.mapTitle})`;
|
|
mapLabel.append(mapName);
|
|
button.append(mapLabel);
|
|
}
|
|
button.title = "Open the concept map containing this connection";
|
|
button.addEventListener("click", () => {
|
|
if (this.onOpenReference) this.onOpenReference(reference);
|
|
});
|
|
layer.append(button);
|
|
|
|
const boundaryX = side === "left" ? x + buttonWidth : x;
|
|
const boundaryY = y + 15;
|
|
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
const startX = reference.sourceInside ? inside.x : boundaryX;
|
|
const startY = reference.sourceInside ? inside.y : boundaryY;
|
|
const endX = reference.sourceInside ? boundaryX : inside.x;
|
|
const endY = reference.sourceInside ? boundaryY : inside.y;
|
|
path.setAttribute("d", `M ${startX} ${startY} L ${endX} ${endY}`);
|
|
path.setAttribute("fill", "none");
|
|
path.setAttribute("stroke", "#808080");
|
|
path.setAttribute("stroke-width", String(reference.connector.lineWidth || 2));
|
|
if (reference.connector.hasArrow) path.setAttribute("marker-end", "url(#rw-cmap-boundary-arrow)");
|
|
svg.append(path);
|
|
}
|
|
}
|
|
|
|
destroy() {
|
|
this.clear();
|
|
}
|
|
}
|