canvas/dist/utils/index.mjs

545 lines
12 KiB
JavaScript
Raw Permalink Normal View History

2026-03-11 18:42:08 -07:00
// src/utils/debug.ts
import debugFactory from "debug";
var NAMESPACE = "canvas";
function createDebug(module) {
const base = debugFactory(`${NAMESPACE}:${module}`);
const warn = debugFactory(`${NAMESPACE}:${module}:warn`);
const error = debugFactory(`${NAMESPACE}:${module}:error`);
warn.enabled = true;
error.enabled = true;
warn.log = console.warn.bind(console);
error.log = console.error.bind(console);
const debugFn = Object.assign(base, {
warn,
error
});
return debugFn;
}
var debug = {
graph: {
node: createDebug("graph:node"),
edge: createDebug("graph:edge"),
sync: createDebug("graph:sync")
},
ui: {
selection: createDebug("ui:selection"),
drag: createDebug("ui:drag"),
resize: createDebug("ui:resize")
},
sync: {
status: createDebug("sync:status"),
mutations: createDebug("sync:mutations"),
queue: createDebug("sync:queue")
},
viewport: createDebug("viewport")
};
// src/utils/gesture-configs.ts
var fingerGestureConfig = {
eventOptions: {
passive: false,
capture: false
},
drag: {
pointer: {
touch: true,
keys: false,
capture: false,
buttons: -1
},
filterTaps: true,
tapsThreshold: 10,
// Was 3 — too strict for fingers
threshold: 10
// Was 3 — needs larger dead zone
}
};
var pencilGestureConfig = {
eventOptions: {
passive: false,
capture: false
},
drag: {
pointer: {
touch: true,
keys: false,
capture: false,
buttons: -1
},
filterTaps: true,
tapsThreshold: 3,
threshold: 2
// Very precise — small dead zone
}
};
var mouseGestureConfig = {
eventOptions: {
passive: false,
capture: false
},
drag: {
pointer: {
touch: true,
keys: false,
capture: false,
buttons: -1
},
filterTaps: true,
tapsThreshold: 5,
// Was 3
threshold: 3
}
};
function getNodeGestureConfig(source) {
switch (source) {
case "finger":
return fingerGestureConfig;
case "pencil":
return pencilGestureConfig;
case "mouse":
return mouseGestureConfig;
}
}
function getViewportGestureConfig(source) {
const base = getNodeGestureConfig(source);
return {
...base,
eventOptions: {
passive: false
},
pinch: {
pointer: {
touch: true
}
},
wheel: {
eventOptions: {
passive: false
}
}
};
}
// src/utils/mutation-queue.ts
var pendingNodeMutations = /* @__PURE__ */ new Map();
function getPendingState(nodeId) {
let state = pendingNodeMutations.get(nodeId);
if (!state) {
state = {
inFlight: false,
queuedPosition: null,
queuedUiProperties: null,
graphId: null
};
pendingNodeMutations.set(nodeId, state);
}
return state;
}
function clearPendingState(nodeId) {
pendingNodeMutations.delete(nodeId);
}
function hasPendingMutations() {
for (const state of pendingNodeMutations.values()) {
if (state.inFlight || state.queuedPosition !== null || state.queuedUiProperties !== null) {
return true;
}
}
return false;
}
// src/utils/component-registry.tsx
import { c as _c } from "react/compiler-runtime";
import React from "react";
import { jsx as _jsx } from "react/jsx-runtime";
function createComponentRegistry({
registry,
fallback,
getDisplayName = (type) => `Memoized${type}Component`
}) {
const memoizedCache = /* @__PURE__ */ new Map();
const getComponent = (type) => {
if (memoizedCache.has(type)) {
return memoizedCache.get(type);
}
const LazyComponent = registry[type];
if (!LazyComponent) {
return fallback || null;
}
function RegistryComponent(props) {
const $ = _c(2);
let t0;
if ($[0] !== props) {
t0 = /* @__PURE__ */ _jsx(LazyComponent, {
...props
});
$[0] = props;
$[1] = t0;
} else {
t0 = $[1];
}
return t0;
}
RegistryComponent.displayName = getDisplayName(type);
memoizedCache.set(type, RegistryComponent);
return RegistryComponent;
};
const getTypes = () => Object.keys(registry);
const hasType = (type) => type in registry;
return {
getComponent,
getTypes,
hasType,
Fallback: fallback || null
};
}
function createFallbackComponent(getMessage) {
return function FallbackComponent(props) {
const message = getMessage ? getMessage(props) : `Unsupported type: "${props.nodeData?.dbData?.node_type || "Unknown"}"`;
return /* @__PURE__ */ _jsx("div", {
style: {
padding: "10px",
color: "orange"
},
children: /* @__PURE__ */ _jsx("p", {
children: message
})
});
};
}
// src/utils/edge-path-calculators.ts
var bezierHorizontal = ({
x1,
y1,
x2,
y2
}) => {
const dist = Math.abs(x2 - x1);
const offset = Math.max(dist * 0.5, 50);
const cp1x = x1 + offset;
const cp1y = y1;
const cp2x = x2 - offset;
const cp2y = y2;
const path = `M ${x1} ${y1} C ${cp1x} ${cp1y}, ${cp2x} ${cp2y}, ${x2} ${y2}`;
const labelX = (x1 + x2) / 2;
const labelY = (y1 + y2) / 2;
return {
path,
labelX,
labelY
};
};
var bezierVertical = ({
x1,
y1,
x2,
y2
}) => {
const dist = Math.abs(y2 - y1);
const offset = Math.max(dist * 0.5, 50);
const cp1x = x1;
const cp1y = y1 + offset;
const cp2x = x2;
const cp2y = y2 - offset;
const path = `M ${x1} ${y1} C ${cp1x} ${cp1y}, ${cp2x} ${cp2y}, ${x2} ${y2}`;
const labelX = (x1 + x2) / 2;
const labelY = (y1 + y2) / 2;
return {
path,
labelX,
labelY
};
};
var bezierSmart = (input) => {
const {
x1,
y1,
x2,
y2
} = input;
const dx = Math.abs(x2 - x1);
const dy = Math.abs(y2 - y1);
return dx > dy ? bezierHorizontal(input) : bezierVertical(input);
};
var straight = ({
x1,
y1,
x2,
y2
}) => {
const path = `M ${x1} ${y1} L ${x2} ${y2}`;
const labelX = (x1 + x2) / 2;
const labelY = (y1 + y2) / 2;
return {
path,
labelX,
labelY
};
};
var stepHorizontal = ({
x1,
y1,
x2,
y2
}) => {
const midX = (x1 + x2) / 2;
const path = `M ${x1} ${y1} L ${midX} ${y1} L ${midX} ${y2} L ${x2} ${y2}`;
const labelX = midX;
const labelY = (y1 + y2) / 2;
return {
path,
labelX,
labelY
};
};
var stepVertical = ({
x1,
y1,
x2,
y2
}) => {
const midY = (y1 + y2) / 2;
const path = `M ${x1} ${y1} L ${x1} ${midY} L ${x2} ${midY} L ${x2} ${y2}`;
const labelX = (x1 + x2) / 2;
const labelY = midY;
return {
path,
labelX,
labelY
};
};
var stepSmart = (input) => {
const {
x1,
y1,
x2,
y2
} = input;
const dx = Math.abs(x2 - x1);
const dy = Math.abs(y2 - y1);
return dx > dy ? stepHorizontal(input) : stepVertical(input);
};
var smoothStep = ({
x1,
y1,
x2,
y2
}) => {
const midX = (x1 + x2) / 2;
const radius = Math.min(20, Math.abs(x2 - x1) / 4, Math.abs(y2 - y1) / 2);
if (radius < 5 || Math.abs(y2 - y1) < radius * 2) {
return stepHorizontal({
x1,
y1,
x2,
y2,
sourceWidth: 0,
sourceHeight: 0,
targetWidth: 0,
targetHeight: 0
});
}
const yDir = y2 > y1 ? 1 : -1;
const path = `
M ${x1} ${y1}
L ${midX - radius} ${y1}
Q ${midX} ${y1}, ${midX} ${y1 + radius * yDir}
L ${midX} ${y2 - radius * yDir}
Q ${midX} ${y2}, ${midX + radius} ${y2}
L ${x2} ${y2}
`.replace(/\s+/g, " ").trim();
const labelX = midX;
const labelY = (y1 + y2) / 2;
return {
path,
labelX,
labelY
};
};
function getEdgePathCalculator(type) {
switch (type) {
case "bezier":
return bezierHorizontal;
case "bezier-vertical":
return bezierVertical;
case "bezier-smart":
return bezierSmart;
case "straight":
return straight;
case "step":
return stepHorizontal;
case "step-vertical":
return stepVertical;
case "step-smart":
return stepSmart;
case "smooth-step":
return smoothStep;
default:
return bezierHorizontal;
}
}
var defaultEdgePathCalculator = bezierHorizontal;
// src/utils/layout.ts
var FitToBoundsMode = /* @__PURE__ */ (function(FitToBoundsMode2) {
FitToBoundsMode2["Graph"] = "graph";
FitToBoundsMode2["Selection"] = "selection";
return FitToBoundsMode2;
})({});
var calculateBounds = (nodes) => {
if (nodes.length === 0) {
return {
x: 0,
y: 0,
width: 0,
height: 0
};
}
const minX = Math.min(...nodes.map((node) => node.x));
const minY = Math.min(...nodes.map((node) => node.y));
const maxX = Math.max(...nodes.map((node) => node.x + node.width));
const maxY = Math.max(...nodes.map((node) => node.y + node.height));
return {
x: minX,
y: minY,
width: maxX - minX,
height: maxY - minY
};
};
function getNodeCenter(node) {
return {
x: node.x + node.width / 2,
y: node.y + node.height / 2
};
}
function setNodeCenter(node, centerX, centerY) {
return {
...node,
x: centerX - node.width / 2,
y: centerY - node.height / 2
};
}
function checkNodesOverlap(node1, node2) {
const center1 = getNodeCenter(node1);
const center2 = getNodeCenter(node2);
const dx = Math.abs(center1.x - center2.x);
const dy = Math.abs(center1.y - center2.y);
const minDistanceX = (node1.width + node2.width) / 2;
const minDistanceY = (node1.height + node2.height) / 2;
return dx < minDistanceX && dy < minDistanceY;
}
function getNodeBounds(node) {
return {
x: node.x,
y: node.y,
width: node.width,
height: node.height,
left: node.x,
right: node.x + node.width,
top: node.y,
bottom: node.y + node.height
};
}
function areNodesClose(node1, node2, threshold = 200) {
const center1 = getNodeCenter(node1);
const center2 = getNodeCenter(node2);
const distance = Math.sqrt(Math.pow(center1.x - center2.x, 2) + Math.pow(center1.y - center2.y, 2));
return distance <= threshold;
}
// src/utils/hit-test.ts
var defaultProvider = {
elementFromPoint: (x, y) => document.elementFromPoint(x, y),
elementsFromPoint: (x, y) => document.elementsFromPoint(x, y)
};
var _provider = defaultProvider;
function setHitTestProvider(provider) {
_provider = provider ?? defaultProvider;
}
function hitTestNode(screenX, screenY) {
const element = _provider.elementFromPoint(screenX, screenY);
const nodeElement = element?.closest("[data-node-id]") ?? null;
const nodeId = nodeElement?.getAttribute("data-node-id") ?? null;
return {
nodeId,
element: nodeElement
};
}
function hitTestPort(screenX, screenY) {
const elements = _provider.elementsFromPoint(screenX, screenY);
for (const el of elements) {
const portElement = el.closest("[data-drag-port-id]");
if (portElement) {
const portId = portElement.dataset.dragPortId;
const portBar = portElement.closest("[data-port-bar]");
const nodeId = portBar?.dataset.nodeId;
if (portId && nodeId) {
return {
nodeId,
portId
};
}
}
}
return null;
}
// src/utils/edge-path-registry.ts
var customCalculators = /* @__PURE__ */ new Map();
function registerEdgePathCalculator(name, calculator) {
customCalculators.set(name, calculator);
}
function unregisterEdgePathCalculator(name) {
return customCalculators.delete(name);
}
function resolveEdgePathCalculator(name) {
return customCalculators.get(name) ?? getEdgePathCalculator(name);
}
function hasCustomEdgePathCalculator(name) {
return customCalculators.has(name);
}
function getCustomEdgePathCalculatorNames() {
return Array.from(customCalculators.keys());
}
function clearCustomEdgePathCalculators() {
customCalculators.clear();
}
export {
FitToBoundsMode,
areNodesClose,
bezierHorizontal,
bezierSmart,
bezierVertical,
calculateBounds,
checkNodesOverlap,
clearCustomEdgePathCalculators,
clearPendingState,
createComponentRegistry,
createDebug,
createFallbackComponent,
debug,
defaultEdgePathCalculator,
getCustomEdgePathCalculatorNames,
getEdgePathCalculator,
getNodeBounds,
getNodeCenter,
getNodeGestureConfig,
getPendingState,
getViewportGestureConfig,
hasCustomEdgePathCalculator,
hasPendingMutations,
hitTestNode,
hitTestPort,
pendingNodeMutations,
registerEdgePathCalculator,
resolveEdgePathCalculator,
setHitTestProvider,
setNodeCenter,
smoothStep,
stepHorizontal,
stepSmart,
stepVertical,
straight,
unregisterEdgePathCalculator
};
//# sourceMappingURL=index.mjs.map