cytoscape-dom-node 2.0.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -0
- package/dist/index.cjs +71 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +18 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.global.js +71 -2
- package/dist/index.global.js.map +1 -1
- package/dist/index.mjs +71 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@ There are also more demos in [./demos](./demos/README.md).
|
|
|
10
10
|
|
|
11
11
|
## History
|
|
12
12
|
|
|
13
|
+
v2.1.0 has bug fixes / improvements outstanding since v1.
|
|
13
14
|
v2.0.0 - v2.0.1 is a typescript conversion, and SHOULD be backwards compatible with v1.
|
|
14
15
|
v1.3.0 is the latest pre typescript incarnation.
|
|
15
16
|
|
|
@@ -85,6 +86,10 @@ const nodeElement = domNodeRenderer.nodeDom(id);
|
|
|
85
86
|
|
|
86
87
|
The previous `node_dom(id)` method is kept as a backwards compatible alias.
|
|
87
88
|
|
|
89
|
+
When a Cytoscape node is removed, `cytoscape-dom-node` removes the DOM element
|
|
90
|
+
it appended for that node. Nodes using `skipNodeAppend` remain caller-owned and
|
|
91
|
+
are not removed from the DOM.
|
|
92
|
+
|
|
88
93
|
See [codepen abWdVOG](https://codepen.io/mwri/pen/abWdVOG) for a working
|
|
89
94
|
example.
|
|
90
95
|
|
|
@@ -123,6 +128,30 @@ cy.domNode({ domContainer: someElement });
|
|
|
123
128
|
|
|
124
129
|
The legacy `dom_container` name remains supported.
|
|
125
130
|
|
|
131
|
+
`interactiveSelector` sets which child elements should receive native DOM
|
|
132
|
+
interaction instead of becoming Cytoscape gestures. Matching controls get
|
|
133
|
+
`pointer-events: auto`, and `pointerdown`, `mousedown`, and `touchstart` are
|
|
134
|
+
stopped during capture so inputs and buttons do not start graph drags. It
|
|
135
|
+
defaults to common controls:
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
cy.domNode({
|
|
139
|
+
interactiveSelector:
|
|
140
|
+
"input, button, select, textarea, a[href], [contenteditable]:not([contenteditable='false']), [data-cy-dom-node-interactive]",
|
|
141
|
+
});
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
For draggable DOM-backed nodes, make the node shell pass pointer events through
|
|
145
|
+
to Cytoscape and let the renderer re-enable matching controls:
|
|
146
|
+
|
|
147
|
+
```css
|
|
148
|
+
.dom-node {
|
|
149
|
+
pointer-events: none;
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Set `interactiveSelector` to `false` to disable this control isolation.
|
|
154
|
+
|
|
126
155
|
## Interaction State
|
|
127
156
|
|
|
128
157
|
DOM nodes mirror Cytoscape selection state with the `selected` class. Selected
|
package/dist/index.cjs
CHANGED
|
@@ -30,10 +30,16 @@ module.exports = __toCommonJS(index_exports);
|
|
|
30
30
|
var DEFAULT_Z_INDEX = "10";
|
|
31
31
|
var ACTIVE_Z_INDEX = "11";
|
|
32
32
|
var SELECTED_CLASS_NAME = "selected";
|
|
33
|
+
var DEFAULT_INTERACTIVE_SELECTOR = "input, button, select, textarea, a[href], [contenteditable]:not([contenteditable='false']), [data-cy-dom-node-interactive]";
|
|
34
|
+
var DEFAULT_INTERACTIVE_EVENTS = ["pointerdown", "mousedown", "touchstart"];
|
|
33
35
|
var DomNodeRenderer = class {
|
|
34
36
|
constructor(cy, options = {}) {
|
|
35
37
|
__publicField(this, "cy");
|
|
36
38
|
__publicField(this, "nodeElements", /* @__PURE__ */ new Map());
|
|
39
|
+
__publicField(this, "appendedNodeIds", /* @__PURE__ */ new Set());
|
|
40
|
+
__publicField(this, "interactiveElementBindings", /* @__PURE__ */ new Map());
|
|
41
|
+
__publicField(this, "interactiveEvents");
|
|
42
|
+
__publicField(this, "interactiveSelector");
|
|
37
43
|
__publicField(this, "resizeObserver");
|
|
38
44
|
__publicField(this, "container");
|
|
39
45
|
__publicField(this, "handleNodeAdd", (event) => {
|
|
@@ -51,8 +57,14 @@ var DomNodeRenderer = class {
|
|
|
51
57
|
__publicField(this, "handleViewport", () => {
|
|
52
58
|
this.syncViewport();
|
|
53
59
|
});
|
|
60
|
+
__publicField(this, "stopInteractiveEvent", (event) => {
|
|
61
|
+
event.stopPropagation();
|
|
62
|
+
});
|
|
63
|
+
var _a, _b;
|
|
54
64
|
this.cy = cy;
|
|
55
65
|
this.container = resolveDomContainer(cy, options);
|
|
66
|
+
this.interactiveEvents = (_a = options.interactiveEvents) != null ? _a : DEFAULT_INTERACTIVE_EVENTS;
|
|
67
|
+
this.interactiveSelector = (_b = options.interactiveSelector) != null ? _b : DEFAULT_INTERACTIVE_SELECTOR;
|
|
56
68
|
this.resizeObserver = new (resolveResizeObserver(options))((entries) => {
|
|
57
69
|
for (const entry of entries) {
|
|
58
70
|
this.syncNodeSize(entry.target);
|
|
@@ -87,6 +99,8 @@ var DomNodeRenderer = class {
|
|
|
87
99
|
this.cy.off("select unselect grab free", "node", this.handleNodeState);
|
|
88
100
|
this.resizeObserver.disconnect();
|
|
89
101
|
this.nodeElements.clear();
|
|
102
|
+
this.appendedNodeIds.clear();
|
|
103
|
+
this.clearInteractiveElements();
|
|
90
104
|
}
|
|
91
105
|
bindEvents() {
|
|
92
106
|
this.cy.on("add", "node", this.handleNodeAdd);
|
|
@@ -108,19 +122,31 @@ var DomNodeRenderer = class {
|
|
|
108
122
|
}
|
|
109
123
|
element.__cy_id = nodeId;
|
|
110
124
|
this.nodeElements.set(nodeId, element);
|
|
125
|
+
if (shouldAppend) {
|
|
126
|
+
this.appendedNodeIds.add(nodeId);
|
|
127
|
+
} else {
|
|
128
|
+
this.appendedNodeIds.delete(nodeId);
|
|
129
|
+
}
|
|
130
|
+
this.bindInteractiveElements(nodeId, element);
|
|
111
131
|
this.resizeObserver.observe(element);
|
|
112
132
|
this.syncNodeSize(element);
|
|
113
133
|
this.syncNodePosition(node);
|
|
114
134
|
this.syncNodeState(node);
|
|
115
135
|
}
|
|
116
136
|
removeNode(node) {
|
|
117
|
-
|
|
137
|
+
var _a;
|
|
138
|
+
const nodeId = node.id();
|
|
139
|
+
const element = this.nodeElements.get(nodeId);
|
|
118
140
|
if (!element) {
|
|
119
141
|
return;
|
|
120
142
|
}
|
|
121
143
|
this.resizeObserver.unobserve(element);
|
|
122
144
|
delete element.__cy_id;
|
|
123
|
-
this.nodeElements.delete(
|
|
145
|
+
this.nodeElements.delete(nodeId);
|
|
146
|
+
this.clearInteractiveElements(nodeId);
|
|
147
|
+
if (this.appendedNodeIds.delete(nodeId)) {
|
|
148
|
+
(_a = element.parentNode) == null ? void 0 : _a.removeChild(element);
|
|
149
|
+
}
|
|
124
150
|
}
|
|
125
151
|
syncViewport() {
|
|
126
152
|
const pan = this.cy.pan();
|
|
@@ -170,6 +196,49 @@ var DomNodeRenderer = class {
|
|
|
170
196
|
element.classList.toggle(SELECTED_CLASS_NAME, isSelected);
|
|
171
197
|
element.style.zIndex = isActive ? ACTIVE_Z_INDEX : DEFAULT_Z_INDEX;
|
|
172
198
|
}
|
|
199
|
+
bindInteractiveElements(nodeId, root) {
|
|
200
|
+
this.clearInteractiveElements(nodeId);
|
|
201
|
+
if (!this.interactiveSelector) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
const elements = root.querySelectorAll(this.interactiveSelector);
|
|
205
|
+
const bindings = [];
|
|
206
|
+
for (const element of elements) {
|
|
207
|
+
bindings.push({
|
|
208
|
+
element,
|
|
209
|
+
previousPointerEvents: element.style.pointerEvents
|
|
210
|
+
});
|
|
211
|
+
element.style.pointerEvents = "auto";
|
|
212
|
+
for (const eventName of this.interactiveEvents) {
|
|
213
|
+
element.addEventListener(eventName, this.stopInteractiveEvent, true);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (bindings.length > 0) {
|
|
217
|
+
this.interactiveElementBindings.set(nodeId, bindings);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
clearInteractiveElements(nodeId) {
|
|
221
|
+
if (nodeId) {
|
|
222
|
+
this.clearInteractiveElementBindings(nodeId);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
for (const id of this.interactiveElementBindings.keys()) {
|
|
226
|
+
this.clearInteractiveElementBindings(id);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
clearInteractiveElementBindings(nodeId) {
|
|
230
|
+
const bindings = this.interactiveElementBindings.get(nodeId);
|
|
231
|
+
if (!bindings) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
for (const binding of bindings) {
|
|
235
|
+
binding.element.style.pointerEvents = binding.previousPointerEvents;
|
|
236
|
+
for (const eventName of this.interactiveEvents) {
|
|
237
|
+
binding.element.removeEventListener(eventName, this.stopInteractiveEvent, true);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
this.interactiveElementBindings.delete(nodeId);
|
|
241
|
+
}
|
|
173
242
|
};
|
|
174
243
|
function register(cytoscapeInstance) {
|
|
175
244
|
if (!cytoscapeInstance) {
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Index.\n *\n * @packageDocumentation\n */\n\nimport type cytoscape from \"cytoscape\";\n\nconst DEFAULT_Z_INDEX = \"10\";\nconst ACTIVE_Z_INDEX = \"11\";\nconst SELECTED_CLASS_NAME = \"selected\";\n\ntype CytoscapeRegistry = typeof cytoscape;\n\n/**\n * Constructor shape for ResizeObserver implementations or polyfills.\n */\nexport type ResizeObserverConstructor = new (\n callback: ResizeObserverCallback,\n) => ResizeObserver;\n\ntype DomNodeElement = HTMLElement & {\n __cy_id?: string;\n};\n\ninterface DomNodeData {\n dom?: HTMLElement;\n skipNodeAppend?: boolean;\n skip_node_append?: boolean;\n}\n\n/**\n * Options for {@link DomNodeRenderer}.\n */\nexport interface DomNodeOptions {\n /**\n * Container that receives node DOM elements. When omitted, the extension\n * creates an absolutely positioned overlay next to Cytoscape's canvas.\n */\n domContainer?: HTMLElement;\n\n /**\n * @deprecated Use `domContainer`.\n */\n dom_container?: HTMLElement;\n\n /**\n * ResizeObserver implementation. This is mainly useful for tests or\n * browser environments that provide a polyfill.\n */\n resizeObserver?: ResizeObserverConstructor;\n}\n\ndeclare global {\n namespace cytoscape {\n interface Core {\n /**\n * Enable DOM-backed Cytoscape nodes for this core instance.\n */\n domNode(options?: DomNodeOptions): DomNodeRenderer;\n }\n }\n}\n\ndeclare module \"cytoscape\" {\n namespace cytoscape {\n interface Core {\n /**\n * Enable DOM-backed Cytoscape nodes for this core instance.\n */\n domNode(options?: DomNodeOptions): DomNodeRenderer;\n }\n }\n}\n\n/**\n * Keeps Cytoscape node positions, dimensions, and interaction state mirrored\n * onto caller-supplied DOM elements.\n */\nexport class DomNodeRenderer {\n private readonly cy: cytoscape.Core;\n private readonly nodeElements = new Map<string, DomNodeElement>();\n private readonly resizeObserver: ResizeObserver;\n private readonly container: HTMLElement;\n\n private readonly handleNodeAdd = (event: cytoscape.EventObject): void => {\n this.addNode(event.target as cytoscape.NodeSingular);\n };\n\n private readonly handleNodeRemove = (event: cytoscape.EventObject): void => {\n this.removeNode(event.target as cytoscape.NodeSingular);\n };\n\n private readonly handleNodePosition = (event: cytoscape.EventObject): void => {\n this.syncNodePosition(event.target as cytoscape.NodeSingular);\n };\n\n private readonly handleNodeState = (event: cytoscape.EventObject): void => {\n this.syncNodeState(event.target as cytoscape.NodeSingular);\n };\n\n private readonly handleViewport = (): void => {\n this.syncViewport();\n };\n\n public constructor(cy: cytoscape.Core, options: DomNodeOptions = {}) {\n this.cy = cy;\n this.container = resolveDomContainer(cy, options);\n this.resizeObserver = new (resolveResizeObserver(options))((entries) => {\n for (const entry of entries) {\n this.syncNodeSize(entry.target as DomNodeElement);\n }\n });\n\n this.bindEvents();\n cy.nodes().forEach((node) => {\n this.addNode(node);\n });\n this.syncViewport();\n }\n\n /**\n * Return the DOM element that backs a Cytoscape node id.\n */\n public nodeDom(id: string): HTMLElement | undefined {\n return this.nodeElements.get(id);\n }\n\n /**\n * @deprecated Use {@link nodeDom}.\n */\n public node_dom(id: string): HTMLElement | undefined {\n return this.nodeDom(id);\n }\n\n /**\n * Remove event handlers and disconnect resize observation.\n */\n public destroy(): void {\n this.cy.off(\"add\", \"node\", this.handleNodeAdd);\n this.cy.off(\"remove\", \"node\", this.handleNodeRemove);\n this.cy.off(\"pan zoom\", this.handleViewport);\n this.cy.off(\"position bounds\", \"node\", this.handleNodePosition);\n this.cy.off(\"select unselect grab free\", \"node\", this.handleNodeState);\n this.resizeObserver.disconnect();\n this.nodeElements.clear();\n }\n\n private bindEvents(): void {\n this.cy.on(\"add\", \"node\", this.handleNodeAdd);\n this.cy.on(\"remove\", \"node\", this.handleNodeRemove);\n this.cy.on(\"pan zoom\", this.handleViewport);\n this.cy.on(\"position bounds\", \"node\", this.handleNodePosition);\n this.cy.on(\"select unselect grab free\", \"node\", this.handleNodeState);\n }\n\n private addNode(node: cytoscape.NodeSingular): void {\n const data = node.data() as DomNodeData;\n const element = data.dom as DomNodeElement | undefined;\n\n if (!element) {\n return;\n }\n\n const nodeId = node.id();\n const shouldAppend = data.skipNodeAppend !== true && data.skip_node_append !== true;\n\n if (shouldAppend && element.parentNode !== this.container) {\n this.container.appendChild(element);\n }\n\n element.__cy_id = nodeId;\n this.nodeElements.set(nodeId, element);\n this.resizeObserver.observe(element);\n\n this.syncNodeSize(element);\n this.syncNodePosition(node);\n this.syncNodeState(node);\n }\n\n private removeNode(node: cytoscape.NodeSingular): void {\n const element = this.nodeElements.get(node.id());\n\n if (!element) {\n return;\n }\n\n this.resizeObserver.unobserve(element);\n delete element.__cy_id;\n this.nodeElements.delete(node.id());\n }\n\n private syncViewport(): void {\n const pan = this.cy.pan();\n const zoom = this.cy.zoom();\n const transform = `translate(${String(pan.x)}px, ${String(\n pan.y,\n )}px) scale(${String(zoom)})`;\n\n setMsTransform(this.container, transform);\n this.container.style.transform = transform;\n }\n\n private syncNodeSize(element: DomNodeElement): void {\n if (!element.__cy_id) {\n return;\n }\n\n const node = this.cy.getElementById(element.__cy_id);\n\n if (node.empty()) {\n return;\n }\n\n node.style({\n height: element.offsetHeight,\n shape: \"rectangle\",\n width: element.offsetWidth,\n });\n }\n\n private syncNodePosition(node: cytoscape.NodeSingular): void {\n const element = this.nodeElements.get(node.id());\n\n if (!element) {\n return;\n }\n\n const position = node.position();\n const transform = `translate(-50%, -50%) translate(${position.x.toFixed(\n 2,\n )}px, ${position.y.toFixed(2)}px)`;\n\n element.style.webkitTransform = transform;\n setMsTransform(element, transform);\n element.style.transform = transform;\n element.style.display = \"inline\";\n element.style.position = \"absolute\";\n }\n\n private syncNodeState(node: cytoscape.NodeSingular): void {\n const element = this.nodeElements.get(node.id());\n\n if (!element) {\n return;\n }\n\n const isSelected = node.selected();\n const isActive = isSelected || node.grabbed();\n\n element.classList.toggle(SELECTED_CLASS_NAME, isSelected);\n element.style.zIndex = isActive ? ACTIVE_Z_INDEX : DEFAULT_Z_INDEX;\n }\n}\n\n/**\n * Backwards-compatible class export for consumers that referenced the previous\n * implementation name from bundled output.\n */\nexport { DomNodeRenderer as CytoscapeDomNode };\n\n/**\n * Register the extension with Cytoscape.\n */\nexport default function register(cytoscapeInstance?: CytoscapeRegistry): void {\n if (!cytoscapeInstance) {\n return;\n }\n\n cytoscapeInstance(\n \"core\",\n \"domNode\",\n function domNode(this: cytoscape.Core, options?: DomNodeOptions): DomNodeRenderer {\n return new DomNodeRenderer(this, options);\n },\n );\n}\n\nfunction resolveDomContainer(cy: cytoscape.Core, options: DomNodeOptions): HTMLElement {\n const providedContainer = options.domContainer ?? options.dom_container;\n\n if (providedContainer) {\n return providedContainer;\n }\n\n const cyContainer = cy.container();\n const canvas = cyContainer?.querySelector(\"canvas\");\n\n if (!canvas?.parentNode) {\n throw new Error(\n \"cytoscape-dom-node requires a Cytoscape container with a canvas, or a domContainer option.\",\n );\n }\n\n const container = document.createElement(\"div\");\n container.style.position = \"absolute\";\n container.style.transformOrigin = \"0 0\";\n container.style.zIndex = DEFAULT_Z_INDEX;\n\n canvas.parentNode.appendChild(container);\n\n return container;\n}\n\nfunction resolveResizeObserver(options: DomNodeOptions): ResizeObserverConstructor {\n const ResizeObserverClass = options.resizeObserver ?? globalThis.ResizeObserver;\n\n if (!ResizeObserverClass) {\n throw new Error(\n \"cytoscape-dom-node requires ResizeObserver. Provide a polyfill or pass resizeObserver.\",\n );\n }\n\n return ResizeObserverClass;\n}\n\nfunction setMsTransform(element: HTMLElement, transform: string): void {\n (element.style as CSSStyleDeclaration & { msTransform?: string }).msTransform =\n transform;\n}\n\nconst globalCytoscape = globalThis as typeof globalThis & {\n cytoscape?: CytoscapeRegistry;\n};\n\n/* v8 ignore next 3 */\nif (globalCytoscape.cytoscape) {\n register(globalCytoscape.cytoscape);\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Index.\n *\n * @packageDocumentation\n */\n\nimport type cytoscape from \"cytoscape\";\n\nconst DEFAULT_Z_INDEX = \"10\";\nconst ACTIVE_Z_INDEX = \"11\";\nconst SELECTED_CLASS_NAME = \"selected\";\nconst DEFAULT_INTERACTIVE_SELECTOR =\n \"input, button, select, textarea, a[href], [contenteditable]:not([contenteditable='false']), [data-cy-dom-node-interactive]\";\nconst DEFAULT_INTERACTIVE_EVENTS = [\"pointerdown\", \"mousedown\", \"touchstart\"] as const;\n\ntype CytoscapeRegistry = typeof cytoscape;\n\n/**\n * Constructor shape for ResizeObserver implementations or polyfills.\n */\nexport type ResizeObserverConstructor = new (\n callback: ResizeObserverCallback,\n) => ResizeObserver;\n\ntype DomNodeElement = HTMLElement & {\n __cy_id?: string;\n};\n\ninterface InteractiveElementBinding {\n element: HTMLElement;\n previousPointerEvents: string;\n}\n\ninterface DomNodeData {\n dom?: HTMLElement;\n skipNodeAppend?: boolean;\n skip_node_append?: boolean;\n}\n\n/**\n * Options for {@link DomNodeRenderer}.\n */\nexport interface DomNodeOptions {\n /**\n * Container that receives node DOM elements. When omitted, the extension\n * creates an absolutely positioned overlay next to Cytoscape's canvas.\n */\n domContainer?: HTMLElement;\n\n /**\n * @deprecated Use `domContainer`.\n */\n dom_container?: HTMLElement;\n\n /**\n * ResizeObserver implementation. This is mainly useful for tests or\n * browser environments that provide a polyfill.\n */\n resizeObserver?: ResizeObserverConstructor;\n\n /**\n * Selector for child controls that should receive native DOM interaction\n * instead of starting Cytoscape gestures. Set to `false` to disable this\n * automatic event isolation.\n */\n interactiveSelector?: string | false;\n\n /**\n * Event names stopped on interactive child controls during the capture phase.\n */\n interactiveEvents?: readonly string[];\n}\n\ndeclare global {\n namespace cytoscape {\n interface Core {\n /**\n * Enable DOM-backed Cytoscape nodes for this core instance.\n */\n domNode(options?: DomNodeOptions): DomNodeRenderer;\n }\n }\n}\n\ndeclare module \"cytoscape\" {\n namespace cytoscape {\n interface Core {\n /**\n * Enable DOM-backed Cytoscape nodes for this core instance.\n */\n domNode(options?: DomNodeOptions): DomNodeRenderer;\n }\n }\n}\n\n/**\n * Keeps Cytoscape node positions, dimensions, and interaction state mirrored\n * onto caller-supplied DOM elements.\n */\nexport class DomNodeRenderer {\n private readonly cy: cytoscape.Core;\n private readonly nodeElements = new Map<string, DomNodeElement>();\n private readonly appendedNodeIds = new Set<string>();\n private readonly interactiveElementBindings = new Map<\n string,\n InteractiveElementBinding[]\n >();\n private readonly interactiveEvents: readonly string[];\n private readonly interactiveSelector: string | false;\n private readonly resizeObserver: ResizeObserver;\n private readonly container: HTMLElement;\n\n private readonly handleNodeAdd = (event: cytoscape.EventObject): void => {\n this.addNode(event.target as cytoscape.NodeSingular);\n };\n\n private readonly handleNodeRemove = (event: cytoscape.EventObject): void => {\n this.removeNode(event.target as cytoscape.NodeSingular);\n };\n\n private readonly handleNodePosition = (event: cytoscape.EventObject): void => {\n this.syncNodePosition(event.target as cytoscape.NodeSingular);\n };\n\n private readonly handleNodeState = (event: cytoscape.EventObject): void => {\n this.syncNodeState(event.target as cytoscape.NodeSingular);\n };\n\n private readonly handleViewport = (): void => {\n this.syncViewport();\n };\n\n private readonly stopInteractiveEvent = (event: Event): void => {\n event.stopPropagation();\n };\n\n public constructor(cy: cytoscape.Core, options: DomNodeOptions = {}) {\n this.cy = cy;\n this.container = resolveDomContainer(cy, options);\n this.interactiveEvents = options.interactiveEvents ?? DEFAULT_INTERACTIVE_EVENTS;\n this.interactiveSelector =\n options.interactiveSelector ?? DEFAULT_INTERACTIVE_SELECTOR;\n this.resizeObserver = new (resolveResizeObserver(options))((entries) => {\n for (const entry of entries) {\n this.syncNodeSize(entry.target as DomNodeElement);\n }\n });\n\n this.bindEvents();\n cy.nodes().forEach((node) => {\n this.addNode(node);\n });\n this.syncViewport();\n }\n\n /**\n * Return the DOM element that backs a Cytoscape node id.\n */\n public nodeDom(id: string): HTMLElement | undefined {\n return this.nodeElements.get(id);\n }\n\n /**\n * @deprecated Use {@link nodeDom}.\n */\n public node_dom(id: string): HTMLElement | undefined {\n return this.nodeDom(id);\n }\n\n /**\n * Remove event handlers and disconnect resize observation.\n */\n public destroy(): void {\n this.cy.off(\"add\", \"node\", this.handleNodeAdd);\n this.cy.off(\"remove\", \"node\", this.handleNodeRemove);\n this.cy.off(\"pan zoom\", this.handleViewport);\n this.cy.off(\"position bounds\", \"node\", this.handleNodePosition);\n this.cy.off(\"select unselect grab free\", \"node\", this.handleNodeState);\n this.resizeObserver.disconnect();\n this.nodeElements.clear();\n this.appendedNodeIds.clear();\n this.clearInteractiveElements();\n }\n\n private bindEvents(): void {\n this.cy.on(\"add\", \"node\", this.handleNodeAdd);\n this.cy.on(\"remove\", \"node\", this.handleNodeRemove);\n this.cy.on(\"pan zoom\", this.handleViewport);\n this.cy.on(\"position bounds\", \"node\", this.handleNodePosition);\n this.cy.on(\"select unselect grab free\", \"node\", this.handleNodeState);\n }\n\n private addNode(node: cytoscape.NodeSingular): void {\n const data = node.data() as DomNodeData;\n const element = data.dom as DomNodeElement | undefined;\n\n if (!element) {\n return;\n }\n\n const nodeId = node.id();\n const shouldAppend = data.skipNodeAppend !== true && data.skip_node_append !== true;\n\n if (shouldAppend && element.parentNode !== this.container) {\n this.container.appendChild(element);\n }\n\n element.__cy_id = nodeId;\n this.nodeElements.set(nodeId, element);\n if (shouldAppend) {\n this.appendedNodeIds.add(nodeId);\n } else {\n this.appendedNodeIds.delete(nodeId);\n }\n this.bindInteractiveElements(nodeId, element);\n this.resizeObserver.observe(element);\n\n this.syncNodeSize(element);\n this.syncNodePosition(node);\n this.syncNodeState(node);\n }\n\n private removeNode(node: cytoscape.NodeSingular): void {\n const nodeId = node.id();\n const element = this.nodeElements.get(nodeId);\n\n if (!element) {\n return;\n }\n\n this.resizeObserver.unobserve(element);\n delete element.__cy_id;\n this.nodeElements.delete(nodeId);\n this.clearInteractiveElements(nodeId);\n\n if (this.appendedNodeIds.delete(nodeId)) {\n element.parentNode?.removeChild(element);\n }\n }\n\n private syncViewport(): void {\n const pan = this.cy.pan();\n const zoom = this.cy.zoom();\n const transform = `translate(${String(pan.x)}px, ${String(\n pan.y,\n )}px) scale(${String(zoom)})`;\n\n setMsTransform(this.container, transform);\n this.container.style.transform = transform;\n }\n\n private syncNodeSize(element: DomNodeElement): void {\n if (!element.__cy_id) {\n return;\n }\n\n const node = this.cy.getElementById(element.__cy_id);\n\n if (node.empty()) {\n return;\n }\n\n node.style({\n height: element.offsetHeight,\n shape: \"rectangle\",\n width: element.offsetWidth,\n });\n }\n\n private syncNodePosition(node: cytoscape.NodeSingular): void {\n const element = this.nodeElements.get(node.id());\n\n if (!element) {\n return;\n }\n\n const position = node.position();\n const transform = `translate(-50%, -50%) translate(${position.x.toFixed(\n 2,\n )}px, ${position.y.toFixed(2)}px)`;\n\n element.style.webkitTransform = transform;\n setMsTransform(element, transform);\n element.style.transform = transform;\n element.style.display = \"inline\";\n element.style.position = \"absolute\";\n }\n\n private syncNodeState(node: cytoscape.NodeSingular): void {\n const element = this.nodeElements.get(node.id());\n\n if (!element) {\n return;\n }\n\n const isSelected = node.selected();\n const isActive = isSelected || node.grabbed();\n\n element.classList.toggle(SELECTED_CLASS_NAME, isSelected);\n element.style.zIndex = isActive ? ACTIVE_Z_INDEX : DEFAULT_Z_INDEX;\n }\n\n private bindInteractiveElements(nodeId: string, root: DomNodeElement): void {\n this.clearInteractiveElements(nodeId);\n\n if (!this.interactiveSelector) {\n return;\n }\n\n const elements = root.querySelectorAll<HTMLElement>(this.interactiveSelector);\n const bindings: InteractiveElementBinding[] = [];\n\n for (const element of elements) {\n bindings.push({\n element,\n previousPointerEvents: element.style.pointerEvents,\n });\n element.style.pointerEvents = \"auto\";\n\n for (const eventName of this.interactiveEvents) {\n element.addEventListener(eventName, this.stopInteractiveEvent, true);\n }\n }\n\n if (bindings.length > 0) {\n this.interactiveElementBindings.set(nodeId, bindings);\n }\n }\n\n private clearInteractiveElements(nodeId?: string): void {\n if (nodeId) {\n this.clearInteractiveElementBindings(nodeId);\n return;\n }\n\n for (const id of this.interactiveElementBindings.keys()) {\n this.clearInteractiveElementBindings(id);\n }\n }\n\n private clearInteractiveElementBindings(nodeId: string): void {\n const bindings = this.interactiveElementBindings.get(nodeId);\n\n if (!bindings) {\n return;\n }\n\n for (const binding of bindings) {\n binding.element.style.pointerEvents = binding.previousPointerEvents;\n\n for (const eventName of this.interactiveEvents) {\n binding.element.removeEventListener(eventName, this.stopInteractiveEvent, true);\n }\n }\n\n this.interactiveElementBindings.delete(nodeId);\n }\n}\n\n/**\n * Backwards-compatible class export for consumers that referenced the previous\n * implementation name from bundled output.\n */\nexport { DomNodeRenderer as CytoscapeDomNode };\n\n/**\n * Register the extension with Cytoscape.\n */\nexport default function register(cytoscapeInstance?: CytoscapeRegistry): void {\n if (!cytoscapeInstance) {\n return;\n }\n\n cytoscapeInstance(\n \"core\",\n \"domNode\",\n function domNode(this: cytoscape.Core, options?: DomNodeOptions): DomNodeRenderer {\n return new DomNodeRenderer(this, options);\n },\n );\n}\n\nfunction resolveDomContainer(cy: cytoscape.Core, options: DomNodeOptions): HTMLElement {\n const providedContainer = options.domContainer ?? options.dom_container;\n\n if (providedContainer) {\n return providedContainer;\n }\n\n const cyContainer = cy.container();\n const canvas = cyContainer?.querySelector(\"canvas\");\n\n if (!canvas?.parentNode) {\n throw new Error(\n \"cytoscape-dom-node requires a Cytoscape container with a canvas, or a domContainer option.\",\n );\n }\n\n const container = document.createElement(\"div\");\n container.style.position = \"absolute\";\n container.style.transformOrigin = \"0 0\";\n container.style.zIndex = DEFAULT_Z_INDEX;\n\n canvas.parentNode.appendChild(container);\n\n return container;\n}\n\nfunction resolveResizeObserver(options: DomNodeOptions): ResizeObserverConstructor {\n const ResizeObserverClass = options.resizeObserver ?? globalThis.ResizeObserver;\n\n if (!ResizeObserverClass) {\n throw new Error(\n \"cytoscape-dom-node requires ResizeObserver. Provide a polyfill or pass resizeObserver.\",\n );\n }\n\n return ResizeObserverClass;\n}\n\nfunction setMsTransform(element: HTMLElement, transform: string): void {\n (element.style as CSSStyleDeclaration & { msTransform?: string }).msTransform =\n transform;\n}\n\nconst globalCytoscape = globalThis as typeof globalThis & {\n cytoscape?: CytoscapeRegistry;\n};\n\n/* v8 ignore next 3 */\nif (globalCytoscape.cytoscape) {\n register(globalCytoscape.cytoscape);\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.mts
CHANGED
|
@@ -29,6 +29,16 @@ interface DomNodeOptions {
|
|
|
29
29
|
* browser environments that provide a polyfill.
|
|
30
30
|
*/
|
|
31
31
|
resizeObserver?: ResizeObserverConstructor;
|
|
32
|
+
/**
|
|
33
|
+
* Selector for child controls that should receive native DOM interaction
|
|
34
|
+
* instead of starting Cytoscape gestures. Set to `false` to disable this
|
|
35
|
+
* automatic event isolation.
|
|
36
|
+
*/
|
|
37
|
+
interactiveSelector?: string | false;
|
|
38
|
+
/**
|
|
39
|
+
* Event names stopped on interactive child controls during the capture phase.
|
|
40
|
+
*/
|
|
41
|
+
interactiveEvents?: readonly string[];
|
|
32
42
|
}
|
|
33
43
|
declare global {
|
|
34
44
|
namespace cytoscape {
|
|
@@ -57,6 +67,10 @@ declare module "cytoscape" {
|
|
|
57
67
|
declare class DomNodeRenderer {
|
|
58
68
|
private readonly cy;
|
|
59
69
|
private readonly nodeElements;
|
|
70
|
+
private readonly appendedNodeIds;
|
|
71
|
+
private readonly interactiveElementBindings;
|
|
72
|
+
private readonly interactiveEvents;
|
|
73
|
+
private readonly interactiveSelector;
|
|
60
74
|
private readonly resizeObserver;
|
|
61
75
|
private readonly container;
|
|
62
76
|
private readonly handleNodeAdd;
|
|
@@ -64,6 +78,7 @@ declare class DomNodeRenderer {
|
|
|
64
78
|
private readonly handleNodePosition;
|
|
65
79
|
private readonly handleNodeState;
|
|
66
80
|
private readonly handleViewport;
|
|
81
|
+
private readonly stopInteractiveEvent;
|
|
67
82
|
constructor(cy: cytoscape.Core, options?: DomNodeOptions);
|
|
68
83
|
/**
|
|
69
84
|
* Return the DOM element that backs a Cytoscape node id.
|
|
@@ -84,6 +99,9 @@ declare class DomNodeRenderer {
|
|
|
84
99
|
private syncNodeSize;
|
|
85
100
|
private syncNodePosition;
|
|
86
101
|
private syncNodeState;
|
|
102
|
+
private bindInteractiveElements;
|
|
103
|
+
private clearInteractiveElements;
|
|
104
|
+
private clearInteractiveElementBindings;
|
|
87
105
|
}
|
|
88
106
|
|
|
89
107
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -29,6 +29,16 @@ interface DomNodeOptions {
|
|
|
29
29
|
* browser environments that provide a polyfill.
|
|
30
30
|
*/
|
|
31
31
|
resizeObserver?: ResizeObserverConstructor;
|
|
32
|
+
/**
|
|
33
|
+
* Selector for child controls that should receive native DOM interaction
|
|
34
|
+
* instead of starting Cytoscape gestures. Set to `false` to disable this
|
|
35
|
+
* automatic event isolation.
|
|
36
|
+
*/
|
|
37
|
+
interactiveSelector?: string | false;
|
|
38
|
+
/**
|
|
39
|
+
* Event names stopped on interactive child controls during the capture phase.
|
|
40
|
+
*/
|
|
41
|
+
interactiveEvents?: readonly string[];
|
|
32
42
|
}
|
|
33
43
|
declare global {
|
|
34
44
|
namespace cytoscape {
|
|
@@ -57,6 +67,10 @@ declare module "cytoscape" {
|
|
|
57
67
|
declare class DomNodeRenderer {
|
|
58
68
|
private readonly cy;
|
|
59
69
|
private readonly nodeElements;
|
|
70
|
+
private readonly appendedNodeIds;
|
|
71
|
+
private readonly interactiveElementBindings;
|
|
72
|
+
private readonly interactiveEvents;
|
|
73
|
+
private readonly interactiveSelector;
|
|
60
74
|
private readonly resizeObserver;
|
|
61
75
|
private readonly container;
|
|
62
76
|
private readonly handleNodeAdd;
|
|
@@ -64,6 +78,7 @@ declare class DomNodeRenderer {
|
|
|
64
78
|
private readonly handleNodePosition;
|
|
65
79
|
private readonly handleNodeState;
|
|
66
80
|
private readonly handleViewport;
|
|
81
|
+
private readonly stopInteractiveEvent;
|
|
67
82
|
constructor(cy: cytoscape.Core, options?: DomNodeOptions);
|
|
68
83
|
/**
|
|
69
84
|
* Return the DOM element that backs a Cytoscape node id.
|
|
@@ -84,6 +99,9 @@ declare class DomNodeRenderer {
|
|
|
84
99
|
private syncNodeSize;
|
|
85
100
|
private syncNodePosition;
|
|
86
101
|
private syncNodeState;
|
|
102
|
+
private bindInteractiveElements;
|
|
103
|
+
private clearInteractiveElements;
|
|
104
|
+
private clearInteractiveElementBindings;
|
|
87
105
|
}
|
|
88
106
|
|
|
89
107
|
/**
|
package/dist/index.global.js
CHANGED
|
@@ -30,10 +30,16 @@ var cytoscapeDomNode = (() => {
|
|
|
30
30
|
var DEFAULT_Z_INDEX = "10";
|
|
31
31
|
var ACTIVE_Z_INDEX = "11";
|
|
32
32
|
var SELECTED_CLASS_NAME = "selected";
|
|
33
|
+
var DEFAULT_INTERACTIVE_SELECTOR = "input, button, select, textarea, a[href], [contenteditable]:not([contenteditable='false']), [data-cy-dom-node-interactive]";
|
|
34
|
+
var DEFAULT_INTERACTIVE_EVENTS = ["pointerdown", "mousedown", "touchstart"];
|
|
33
35
|
var DomNodeRenderer = class {
|
|
34
36
|
constructor(cy, options = {}) {
|
|
35
37
|
__publicField(this, "cy");
|
|
36
38
|
__publicField(this, "nodeElements", /* @__PURE__ */ new Map());
|
|
39
|
+
__publicField(this, "appendedNodeIds", /* @__PURE__ */ new Set());
|
|
40
|
+
__publicField(this, "interactiveElementBindings", /* @__PURE__ */ new Map());
|
|
41
|
+
__publicField(this, "interactiveEvents");
|
|
42
|
+
__publicField(this, "interactiveSelector");
|
|
37
43
|
__publicField(this, "resizeObserver");
|
|
38
44
|
__publicField(this, "container");
|
|
39
45
|
__publicField(this, "handleNodeAdd", (event) => {
|
|
@@ -51,8 +57,14 @@ var cytoscapeDomNode = (() => {
|
|
|
51
57
|
__publicField(this, "handleViewport", () => {
|
|
52
58
|
this.syncViewport();
|
|
53
59
|
});
|
|
60
|
+
__publicField(this, "stopInteractiveEvent", (event) => {
|
|
61
|
+
event.stopPropagation();
|
|
62
|
+
});
|
|
63
|
+
var _a, _b;
|
|
54
64
|
this.cy = cy;
|
|
55
65
|
this.container = resolveDomContainer(cy, options);
|
|
66
|
+
this.interactiveEvents = (_a = options.interactiveEvents) != null ? _a : DEFAULT_INTERACTIVE_EVENTS;
|
|
67
|
+
this.interactiveSelector = (_b = options.interactiveSelector) != null ? _b : DEFAULT_INTERACTIVE_SELECTOR;
|
|
56
68
|
this.resizeObserver = new (resolveResizeObserver(options))((entries) => {
|
|
57
69
|
for (const entry of entries) {
|
|
58
70
|
this.syncNodeSize(entry.target);
|
|
@@ -87,6 +99,8 @@ var cytoscapeDomNode = (() => {
|
|
|
87
99
|
this.cy.off("select unselect grab free", "node", this.handleNodeState);
|
|
88
100
|
this.resizeObserver.disconnect();
|
|
89
101
|
this.nodeElements.clear();
|
|
102
|
+
this.appendedNodeIds.clear();
|
|
103
|
+
this.clearInteractiveElements();
|
|
90
104
|
}
|
|
91
105
|
bindEvents() {
|
|
92
106
|
this.cy.on("add", "node", this.handleNodeAdd);
|
|
@@ -108,19 +122,31 @@ var cytoscapeDomNode = (() => {
|
|
|
108
122
|
}
|
|
109
123
|
element.__cy_id = nodeId;
|
|
110
124
|
this.nodeElements.set(nodeId, element);
|
|
125
|
+
if (shouldAppend) {
|
|
126
|
+
this.appendedNodeIds.add(nodeId);
|
|
127
|
+
} else {
|
|
128
|
+
this.appendedNodeIds.delete(nodeId);
|
|
129
|
+
}
|
|
130
|
+
this.bindInteractiveElements(nodeId, element);
|
|
111
131
|
this.resizeObserver.observe(element);
|
|
112
132
|
this.syncNodeSize(element);
|
|
113
133
|
this.syncNodePosition(node);
|
|
114
134
|
this.syncNodeState(node);
|
|
115
135
|
}
|
|
116
136
|
removeNode(node) {
|
|
117
|
-
|
|
137
|
+
var _a;
|
|
138
|
+
const nodeId = node.id();
|
|
139
|
+
const element = this.nodeElements.get(nodeId);
|
|
118
140
|
if (!element) {
|
|
119
141
|
return;
|
|
120
142
|
}
|
|
121
143
|
this.resizeObserver.unobserve(element);
|
|
122
144
|
delete element.__cy_id;
|
|
123
|
-
this.nodeElements.delete(
|
|
145
|
+
this.nodeElements.delete(nodeId);
|
|
146
|
+
this.clearInteractiveElements(nodeId);
|
|
147
|
+
if (this.appendedNodeIds.delete(nodeId)) {
|
|
148
|
+
(_a = element.parentNode) == null ? void 0 : _a.removeChild(element);
|
|
149
|
+
}
|
|
124
150
|
}
|
|
125
151
|
syncViewport() {
|
|
126
152
|
const pan = this.cy.pan();
|
|
@@ -170,6 +196,49 @@ var cytoscapeDomNode = (() => {
|
|
|
170
196
|
element.classList.toggle(SELECTED_CLASS_NAME, isSelected);
|
|
171
197
|
element.style.zIndex = isActive ? ACTIVE_Z_INDEX : DEFAULT_Z_INDEX;
|
|
172
198
|
}
|
|
199
|
+
bindInteractiveElements(nodeId, root) {
|
|
200
|
+
this.clearInteractiveElements(nodeId);
|
|
201
|
+
if (!this.interactiveSelector) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
const elements = root.querySelectorAll(this.interactiveSelector);
|
|
205
|
+
const bindings = [];
|
|
206
|
+
for (const element of elements) {
|
|
207
|
+
bindings.push({
|
|
208
|
+
element,
|
|
209
|
+
previousPointerEvents: element.style.pointerEvents
|
|
210
|
+
});
|
|
211
|
+
element.style.pointerEvents = "auto";
|
|
212
|
+
for (const eventName of this.interactiveEvents) {
|
|
213
|
+
element.addEventListener(eventName, this.stopInteractiveEvent, true);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (bindings.length > 0) {
|
|
217
|
+
this.interactiveElementBindings.set(nodeId, bindings);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
clearInteractiveElements(nodeId) {
|
|
221
|
+
if (nodeId) {
|
|
222
|
+
this.clearInteractiveElementBindings(nodeId);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
for (const id of this.interactiveElementBindings.keys()) {
|
|
226
|
+
this.clearInteractiveElementBindings(id);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
clearInteractiveElementBindings(nodeId) {
|
|
230
|
+
const bindings = this.interactiveElementBindings.get(nodeId);
|
|
231
|
+
if (!bindings) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
for (const binding of bindings) {
|
|
235
|
+
binding.element.style.pointerEvents = binding.previousPointerEvents;
|
|
236
|
+
for (const eventName of this.interactiveEvents) {
|
|
237
|
+
binding.element.removeEventListener(eventName, this.stopInteractiveEvent, true);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
this.interactiveElementBindings.delete(nodeId);
|
|
241
|
+
}
|
|
173
242
|
};
|
|
174
243
|
function register(cytoscapeInstance) {
|
|
175
244
|
if (!cytoscapeInstance) {
|
package/dist/index.global.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Index.\n *\n * @packageDocumentation\n */\n\nimport type cytoscape from \"cytoscape\";\n\nconst DEFAULT_Z_INDEX = \"10\";\nconst ACTIVE_Z_INDEX = \"11\";\nconst SELECTED_CLASS_NAME = \"selected\";\n\ntype CytoscapeRegistry = typeof cytoscape;\n\n/**\n * Constructor shape for ResizeObserver implementations or polyfills.\n */\nexport type ResizeObserverConstructor = new (\n callback: ResizeObserverCallback,\n) => ResizeObserver;\n\ntype DomNodeElement = HTMLElement & {\n __cy_id?: string;\n};\n\ninterface DomNodeData {\n dom?: HTMLElement;\n skipNodeAppend?: boolean;\n skip_node_append?: boolean;\n}\n\n/**\n * Options for {@link DomNodeRenderer}.\n */\nexport interface DomNodeOptions {\n /**\n * Container that receives node DOM elements. When omitted, the extension\n * creates an absolutely positioned overlay next to Cytoscape's canvas.\n */\n domContainer?: HTMLElement;\n\n /**\n * @deprecated Use `domContainer`.\n */\n dom_container?: HTMLElement;\n\n /**\n * ResizeObserver implementation. This is mainly useful for tests or\n * browser environments that provide a polyfill.\n */\n resizeObserver?: ResizeObserverConstructor;\n}\n\ndeclare global {\n namespace cytoscape {\n interface Core {\n /**\n * Enable DOM-backed Cytoscape nodes for this core instance.\n */\n domNode(options?: DomNodeOptions): DomNodeRenderer;\n }\n }\n}\n\ndeclare module \"cytoscape\" {\n namespace cytoscape {\n interface Core {\n /**\n * Enable DOM-backed Cytoscape nodes for this core instance.\n */\n domNode(options?: DomNodeOptions): DomNodeRenderer;\n }\n }\n}\n\n/**\n * Keeps Cytoscape node positions, dimensions, and interaction state mirrored\n * onto caller-supplied DOM elements.\n */\nexport class DomNodeRenderer {\n private readonly cy: cytoscape.Core;\n private readonly nodeElements = new Map<string, DomNodeElement>();\n private readonly resizeObserver: ResizeObserver;\n private readonly container: HTMLElement;\n\n private readonly handleNodeAdd = (event: cytoscape.EventObject): void => {\n this.addNode(event.target as cytoscape.NodeSingular);\n };\n\n private readonly handleNodeRemove = (event: cytoscape.EventObject): void => {\n this.removeNode(event.target as cytoscape.NodeSingular);\n };\n\n private readonly handleNodePosition = (event: cytoscape.EventObject): void => {\n this.syncNodePosition(event.target as cytoscape.NodeSingular);\n };\n\n private readonly handleNodeState = (event: cytoscape.EventObject): void => {\n this.syncNodeState(event.target as cytoscape.NodeSingular);\n };\n\n private readonly handleViewport = (): void => {\n this.syncViewport();\n };\n\n public constructor(cy: cytoscape.Core, options: DomNodeOptions = {}) {\n this.cy = cy;\n this.container = resolveDomContainer(cy, options);\n this.resizeObserver = new (resolveResizeObserver(options))((entries) => {\n for (const entry of entries) {\n this.syncNodeSize(entry.target as DomNodeElement);\n }\n });\n\n this.bindEvents();\n cy.nodes().forEach((node) => {\n this.addNode(node);\n });\n this.syncViewport();\n }\n\n /**\n * Return the DOM element that backs a Cytoscape node id.\n */\n public nodeDom(id: string): HTMLElement | undefined {\n return this.nodeElements.get(id);\n }\n\n /**\n * @deprecated Use {@link nodeDom}.\n */\n public node_dom(id: string): HTMLElement | undefined {\n return this.nodeDom(id);\n }\n\n /**\n * Remove event handlers and disconnect resize observation.\n */\n public destroy(): void {\n this.cy.off(\"add\", \"node\", this.handleNodeAdd);\n this.cy.off(\"remove\", \"node\", this.handleNodeRemove);\n this.cy.off(\"pan zoom\", this.handleViewport);\n this.cy.off(\"position bounds\", \"node\", this.handleNodePosition);\n this.cy.off(\"select unselect grab free\", \"node\", this.handleNodeState);\n this.resizeObserver.disconnect();\n this.nodeElements.clear();\n }\n\n private bindEvents(): void {\n this.cy.on(\"add\", \"node\", this.handleNodeAdd);\n this.cy.on(\"remove\", \"node\", this.handleNodeRemove);\n this.cy.on(\"pan zoom\", this.handleViewport);\n this.cy.on(\"position bounds\", \"node\", this.handleNodePosition);\n this.cy.on(\"select unselect grab free\", \"node\", this.handleNodeState);\n }\n\n private addNode(node: cytoscape.NodeSingular): void {\n const data = node.data() as DomNodeData;\n const element = data.dom as DomNodeElement | undefined;\n\n if (!element) {\n return;\n }\n\n const nodeId = node.id();\n const shouldAppend = data.skipNodeAppend !== true && data.skip_node_append !== true;\n\n if (shouldAppend && element.parentNode !== this.container) {\n this.container.appendChild(element);\n }\n\n element.__cy_id = nodeId;\n this.nodeElements.set(nodeId, element);\n this.resizeObserver.observe(element);\n\n this.syncNodeSize(element);\n this.syncNodePosition(node);\n this.syncNodeState(node);\n }\n\n private removeNode(node: cytoscape.NodeSingular): void {\n const element = this.nodeElements.get(node.id());\n\n if (!element) {\n return;\n }\n\n this.resizeObserver.unobserve(element);\n delete element.__cy_id;\n this.nodeElements.delete(node.id());\n }\n\n private syncViewport(): void {\n const pan = this.cy.pan();\n const zoom = this.cy.zoom();\n const transform = `translate(${String(pan.x)}px, ${String(\n pan.y,\n )}px) scale(${String(zoom)})`;\n\n setMsTransform(this.container, transform);\n this.container.style.transform = transform;\n }\n\n private syncNodeSize(element: DomNodeElement): void {\n if (!element.__cy_id) {\n return;\n }\n\n const node = this.cy.getElementById(element.__cy_id);\n\n if (node.empty()) {\n return;\n }\n\n node.style({\n height: element.offsetHeight,\n shape: \"rectangle\",\n width: element.offsetWidth,\n });\n }\n\n private syncNodePosition(node: cytoscape.NodeSingular): void {\n const element = this.nodeElements.get(node.id());\n\n if (!element) {\n return;\n }\n\n const position = node.position();\n const transform = `translate(-50%, -50%) translate(${position.x.toFixed(\n 2,\n )}px, ${position.y.toFixed(2)}px)`;\n\n element.style.webkitTransform = transform;\n setMsTransform(element, transform);\n element.style.transform = transform;\n element.style.display = \"inline\";\n element.style.position = \"absolute\";\n }\n\n private syncNodeState(node: cytoscape.NodeSingular): void {\n const element = this.nodeElements.get(node.id());\n\n if (!element) {\n return;\n }\n\n const isSelected = node.selected();\n const isActive = isSelected || node.grabbed();\n\n element.classList.toggle(SELECTED_CLASS_NAME, isSelected);\n element.style.zIndex = isActive ? ACTIVE_Z_INDEX : DEFAULT_Z_INDEX;\n }\n}\n\n/**\n * Backwards-compatible class export for consumers that referenced the previous\n * implementation name from bundled output.\n */\nexport { DomNodeRenderer as CytoscapeDomNode };\n\n/**\n * Register the extension with Cytoscape.\n */\nexport default function register(cytoscapeInstance?: CytoscapeRegistry): void {\n if (!cytoscapeInstance) {\n return;\n }\n\n cytoscapeInstance(\n \"core\",\n \"domNode\",\n function domNode(this: cytoscape.Core, options?: DomNodeOptions): DomNodeRenderer {\n return new DomNodeRenderer(this, options);\n },\n );\n}\n\nfunction resolveDomContainer(cy: cytoscape.Core, options: DomNodeOptions): HTMLElement {\n const providedContainer = options.domContainer ?? options.dom_container;\n\n if (providedContainer) {\n return providedContainer;\n }\n\n const cyContainer = cy.container();\n const canvas = cyContainer?.querySelector(\"canvas\");\n\n if (!canvas?.parentNode) {\n throw new Error(\n \"cytoscape-dom-node requires a Cytoscape container with a canvas, or a domContainer option.\",\n );\n }\n\n const container = document.createElement(\"div\");\n container.style.position = \"absolute\";\n container.style.transformOrigin = \"0 0\";\n container.style.zIndex = DEFAULT_Z_INDEX;\n\n canvas.parentNode.appendChild(container);\n\n return container;\n}\n\nfunction resolveResizeObserver(options: DomNodeOptions): ResizeObserverConstructor {\n const ResizeObserverClass = options.resizeObserver ?? globalThis.ResizeObserver;\n\n if (!ResizeObserverClass) {\n throw new Error(\n \"cytoscape-dom-node requires ResizeObserver. Provide a polyfill or pass resizeObserver.\",\n );\n }\n\n return ResizeObserverClass;\n}\n\nfunction setMsTransform(element: HTMLElement, transform: string): void {\n (element.style as CSSStyleDeclaration & { msTransform?: string }).msTransform =\n transform;\n}\n\nconst globalCytoscape = globalThis as typeof globalThis & {\n cytoscape?: CytoscapeRegistry;\n};\n\n/* v8 ignore next 3 */\nif (globalCytoscape.cytoscape) {\n register(globalCytoscape.cytoscape);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQA,MAAM,kBAAkB;AACxB,MAAM,iBAAiB;AACvB,MAAM,sBAAsB;AAqErB,MAAM,kBAAN,MAAsB;AAAA,IA0BpB,YAAY,IAAoB,UAA0B,CAAC,GAAG;AAzBrE,0BAAiB;AACjB,0BAAiB,gBAAe,oBAAI,IAA4B;AAChE,0BAAiB;AACjB,0BAAiB;AAEjB,0BAAiB,iBAAgB,CAAC,UAAuC;AACvE,aAAK,QAAQ,MAAM,MAAgC;AAAA,MACrD;AAEA,0BAAiB,oBAAmB,CAAC,UAAuC;AAC1E,aAAK,WAAW,MAAM,MAAgC;AAAA,MACxD;AAEA,0BAAiB,sBAAqB,CAAC,UAAuC;AAC5E,aAAK,iBAAiB,MAAM,MAAgC;AAAA,MAC9D;AAEA,0BAAiB,mBAAkB,CAAC,UAAuC;AACzE,aAAK,cAAc,MAAM,MAAgC;AAAA,MAC3D;AAEA,0BAAiB,kBAAiB,MAAY;AAC5C,aAAK,aAAa;AAAA,MACpB;AAGE,WAAK,KAAK;AACV,WAAK,YAAY,oBAAoB,IAAI,OAAO;AAChD,WAAK,iBAAiB,KAAK,sBAAsB,OAAO,GAAG,CAAC,YAAY;AACtE,mBAAW,SAAS,SAAS;AAC3B,eAAK,aAAa,MAAM,MAAwB;AAAA,QAClD;AAAA,MACF,CAAC;AAED,WAAK,WAAW;AAChB,SAAG,MAAM,EAAE,QAAQ,CAAC,SAAS;AAC3B,aAAK,QAAQ,IAAI;AAAA,MACnB,CAAC;AACD,WAAK,aAAa;AAAA,IACpB;AAAA;AAAA;AAAA;AAAA,IAKO,QAAQ,IAAqC;AAClD,aAAO,KAAK,aAAa,IAAI,EAAE;AAAA,IACjC;AAAA;AAAA;AAAA;AAAA,IAKO,SAAS,IAAqC;AACnD,aAAO,KAAK,QAAQ,EAAE;AAAA,IACxB;AAAA;AAAA;AAAA;AAAA,IAKO,UAAgB;AACrB,WAAK,GAAG,IAAI,OAAO,QAAQ,KAAK,aAAa;AAC7C,WAAK,GAAG,IAAI,UAAU,QAAQ,KAAK,gBAAgB;AACnD,WAAK,GAAG,IAAI,YAAY,KAAK,cAAc;AAC3C,WAAK,GAAG,IAAI,mBAAmB,QAAQ,KAAK,kBAAkB;AAC9D,WAAK,GAAG,IAAI,6BAA6B,QAAQ,KAAK,eAAe;AACrE,WAAK,eAAe,WAAW;AAC/B,WAAK,aAAa,MAAM;AAAA,IAC1B;AAAA,IAEQ,aAAmB;AACzB,WAAK,GAAG,GAAG,OAAO,QAAQ,KAAK,aAAa;AAC5C,WAAK,GAAG,GAAG,UAAU,QAAQ,KAAK,gBAAgB;AAClD,WAAK,GAAG,GAAG,YAAY,KAAK,cAAc;AAC1C,WAAK,GAAG,GAAG,mBAAmB,QAAQ,KAAK,kBAAkB;AAC7D,WAAK,GAAG,GAAG,6BAA6B,QAAQ,KAAK,eAAe;AAAA,IACtE;AAAA,IAEQ,QAAQ,MAAoC;AAClD,YAAM,OAAO,KAAK,KAAK;AACvB,YAAM,UAAU,KAAK;AAErB,UAAI,CAAC,SAAS;AACZ;AAAA,MACF;AAEA,YAAM,SAAS,KAAK,GAAG;AACvB,YAAM,eAAe,KAAK,mBAAmB,QAAQ,KAAK,qBAAqB;AAE/E,UAAI,gBAAgB,QAAQ,eAAe,KAAK,WAAW;AACzD,aAAK,UAAU,YAAY,OAAO;AAAA,MACpC;AAEA,cAAQ,UAAU;AAClB,WAAK,aAAa,IAAI,QAAQ,OAAO;AACrC,WAAK,eAAe,QAAQ,OAAO;AAEnC,WAAK,aAAa,OAAO;AACzB,WAAK,iBAAiB,IAAI;AAC1B,WAAK,cAAc,IAAI;AAAA,IACzB;AAAA,IAEQ,WAAW,MAAoC;AACrD,YAAM,UAAU,KAAK,aAAa,IAAI,KAAK,GAAG,CAAC;AAE/C,UAAI,CAAC,SAAS;AACZ;AAAA,MACF;AAEA,WAAK,eAAe,UAAU,OAAO;AACrC,aAAO,QAAQ;AACf,WAAK,aAAa,OAAO,KAAK,GAAG,CAAC;AAAA,IACpC;AAAA,IAEQ,eAAqB;AAC3B,YAAM,MAAM,KAAK,GAAG,IAAI;AACxB,YAAM,OAAO,KAAK,GAAG,KAAK;AAC1B,YAAM,YAAY,aAAa,OAAO,IAAI,CAAC,CAAC,OAAO;AAAA,QACjD,IAAI;AAAA,MACN,CAAC,aAAa,OAAO,IAAI,CAAC;AAE1B,qBAAe,KAAK,WAAW,SAAS;AACxC,WAAK,UAAU,MAAM,YAAY;AAAA,IACnC;AAAA,IAEQ,aAAa,SAA+B;AAClD,UAAI,CAAC,QAAQ,SAAS;AACpB;AAAA,MACF;AAEA,YAAM,OAAO,KAAK,GAAG,eAAe,QAAQ,OAAO;AAEnD,UAAI,KAAK,MAAM,GAAG;AAChB;AAAA,MACF;AAEA,WAAK,MAAM;AAAA,QACT,QAAQ,QAAQ;AAAA,QAChB,OAAO;AAAA,QACP,OAAO,QAAQ;AAAA,MACjB,CAAC;AAAA,IACH;AAAA,IAEQ,iBAAiB,MAAoC;AAC3D,YAAM,UAAU,KAAK,aAAa,IAAI,KAAK,GAAG,CAAC;AAE/C,UAAI,CAAC,SAAS;AACZ;AAAA,MACF;AAEA,YAAM,WAAW,KAAK,SAAS;AAC/B,YAAM,YAAY,mCAAmC,SAAS,EAAE;AAAA,QAC9D;AAAA,MACF,CAAC,OAAO,SAAS,EAAE,QAAQ,CAAC,CAAC;AAE7B,cAAQ,MAAM,kBAAkB;AAChC,qBAAe,SAAS,SAAS;AACjC,cAAQ,MAAM,YAAY;AAC1B,cAAQ,MAAM,UAAU;AACxB,cAAQ,MAAM,WAAW;AAAA,IAC3B;AAAA,IAEQ,cAAc,MAAoC;AACxD,YAAM,UAAU,KAAK,aAAa,IAAI,KAAK,GAAG,CAAC;AAE/C,UAAI,CAAC,SAAS;AACZ;AAAA,MACF;AAEA,YAAM,aAAa,KAAK,SAAS;AACjC,YAAM,WAAW,cAAc,KAAK,QAAQ;AAE5C,cAAQ,UAAU,OAAO,qBAAqB,UAAU;AACxD,cAAQ,MAAM,SAAS,WAAW,iBAAiB;AAAA,IACrD;AAAA,EACF;AAWe,WAAR,SAA0B,mBAA6C;AAC5E,QAAI,CAAC,mBAAmB;AACtB;AAAA,IACF;AAEA;AAAA,MACE;AAAA,MACA;AAAA,MACA,SAAS,QAA8B,SAA2C;AAChF,eAAO,IAAI,gBAAgB,MAAM,OAAO;AAAA,MAC1C;AAAA,IACF;AAAA,EACF;AAEA,WAAS,oBAAoB,IAAoB,SAAsC;AAtRvF;AAuRE,UAAM,qBAAoB,aAAQ,iBAAR,YAAwB,QAAQ;AAE1D,QAAI,mBAAmB;AACrB,aAAO;AAAA,IACT;AAEA,UAAM,cAAc,GAAG,UAAU;AACjC,UAAM,SAAS,2CAAa,cAAc;AAE1C,QAAI,EAAC,iCAAQ,aAAY;AACvB,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,UAAM,YAAY,SAAS,cAAc,KAAK;AAC9C,cAAU,MAAM,WAAW;AAC3B,cAAU,MAAM,kBAAkB;AAClC,cAAU,MAAM,SAAS;AAEzB,WAAO,WAAW,YAAY,SAAS;AAEvC,WAAO;AAAA,EACT;AAEA,WAAS,sBAAsB,SAAoD;AAhTnF;AAiTE,UAAM,uBAAsB,aAAQ,mBAAR,YAA0B,WAAW;AAEjE,QAAI,CAAC,qBAAqB;AACxB,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAEA,WAAS,eAAe,SAAsB,WAAyB;AACrE,IAAC,QAAQ,MAAyD,cAChE;AAAA,EACJ;AAEA,MAAM,kBAAkB;AAKxB,MAAI,gBAAgB,WAAW;AAC7B,aAAS,gBAAgB,SAAS;AAAA,EACpC;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Index.\n *\n * @packageDocumentation\n */\n\nimport type cytoscape from \"cytoscape\";\n\nconst DEFAULT_Z_INDEX = \"10\";\nconst ACTIVE_Z_INDEX = \"11\";\nconst SELECTED_CLASS_NAME = \"selected\";\nconst DEFAULT_INTERACTIVE_SELECTOR =\n \"input, button, select, textarea, a[href], [contenteditable]:not([contenteditable='false']), [data-cy-dom-node-interactive]\";\nconst DEFAULT_INTERACTIVE_EVENTS = [\"pointerdown\", \"mousedown\", \"touchstart\"] as const;\n\ntype CytoscapeRegistry = typeof cytoscape;\n\n/**\n * Constructor shape for ResizeObserver implementations or polyfills.\n */\nexport type ResizeObserverConstructor = new (\n callback: ResizeObserverCallback,\n) => ResizeObserver;\n\ntype DomNodeElement = HTMLElement & {\n __cy_id?: string;\n};\n\ninterface InteractiveElementBinding {\n element: HTMLElement;\n previousPointerEvents: string;\n}\n\ninterface DomNodeData {\n dom?: HTMLElement;\n skipNodeAppend?: boolean;\n skip_node_append?: boolean;\n}\n\n/**\n * Options for {@link DomNodeRenderer}.\n */\nexport interface DomNodeOptions {\n /**\n * Container that receives node DOM elements. When omitted, the extension\n * creates an absolutely positioned overlay next to Cytoscape's canvas.\n */\n domContainer?: HTMLElement;\n\n /**\n * @deprecated Use `domContainer`.\n */\n dom_container?: HTMLElement;\n\n /**\n * ResizeObserver implementation. This is mainly useful for tests or\n * browser environments that provide a polyfill.\n */\n resizeObserver?: ResizeObserverConstructor;\n\n /**\n * Selector for child controls that should receive native DOM interaction\n * instead of starting Cytoscape gestures. Set to `false` to disable this\n * automatic event isolation.\n */\n interactiveSelector?: string | false;\n\n /**\n * Event names stopped on interactive child controls during the capture phase.\n */\n interactiveEvents?: readonly string[];\n}\n\ndeclare global {\n namespace cytoscape {\n interface Core {\n /**\n * Enable DOM-backed Cytoscape nodes for this core instance.\n */\n domNode(options?: DomNodeOptions): DomNodeRenderer;\n }\n }\n}\n\ndeclare module \"cytoscape\" {\n namespace cytoscape {\n interface Core {\n /**\n * Enable DOM-backed Cytoscape nodes for this core instance.\n */\n domNode(options?: DomNodeOptions): DomNodeRenderer;\n }\n }\n}\n\n/**\n * Keeps Cytoscape node positions, dimensions, and interaction state mirrored\n * onto caller-supplied DOM elements.\n */\nexport class DomNodeRenderer {\n private readonly cy: cytoscape.Core;\n private readonly nodeElements = new Map<string, DomNodeElement>();\n private readonly appendedNodeIds = new Set<string>();\n private readonly interactiveElementBindings = new Map<\n string,\n InteractiveElementBinding[]\n >();\n private readonly interactiveEvents: readonly string[];\n private readonly interactiveSelector: string | false;\n private readonly resizeObserver: ResizeObserver;\n private readonly container: HTMLElement;\n\n private readonly handleNodeAdd = (event: cytoscape.EventObject): void => {\n this.addNode(event.target as cytoscape.NodeSingular);\n };\n\n private readonly handleNodeRemove = (event: cytoscape.EventObject): void => {\n this.removeNode(event.target as cytoscape.NodeSingular);\n };\n\n private readonly handleNodePosition = (event: cytoscape.EventObject): void => {\n this.syncNodePosition(event.target as cytoscape.NodeSingular);\n };\n\n private readonly handleNodeState = (event: cytoscape.EventObject): void => {\n this.syncNodeState(event.target as cytoscape.NodeSingular);\n };\n\n private readonly handleViewport = (): void => {\n this.syncViewport();\n };\n\n private readonly stopInteractiveEvent = (event: Event): void => {\n event.stopPropagation();\n };\n\n public constructor(cy: cytoscape.Core, options: DomNodeOptions = {}) {\n this.cy = cy;\n this.container = resolveDomContainer(cy, options);\n this.interactiveEvents = options.interactiveEvents ?? DEFAULT_INTERACTIVE_EVENTS;\n this.interactiveSelector =\n options.interactiveSelector ?? DEFAULT_INTERACTIVE_SELECTOR;\n this.resizeObserver = new (resolveResizeObserver(options))((entries) => {\n for (const entry of entries) {\n this.syncNodeSize(entry.target as DomNodeElement);\n }\n });\n\n this.bindEvents();\n cy.nodes().forEach((node) => {\n this.addNode(node);\n });\n this.syncViewport();\n }\n\n /**\n * Return the DOM element that backs a Cytoscape node id.\n */\n public nodeDom(id: string): HTMLElement | undefined {\n return this.nodeElements.get(id);\n }\n\n /**\n * @deprecated Use {@link nodeDom}.\n */\n public node_dom(id: string): HTMLElement | undefined {\n return this.nodeDom(id);\n }\n\n /**\n * Remove event handlers and disconnect resize observation.\n */\n public destroy(): void {\n this.cy.off(\"add\", \"node\", this.handleNodeAdd);\n this.cy.off(\"remove\", \"node\", this.handleNodeRemove);\n this.cy.off(\"pan zoom\", this.handleViewport);\n this.cy.off(\"position bounds\", \"node\", this.handleNodePosition);\n this.cy.off(\"select unselect grab free\", \"node\", this.handleNodeState);\n this.resizeObserver.disconnect();\n this.nodeElements.clear();\n this.appendedNodeIds.clear();\n this.clearInteractiveElements();\n }\n\n private bindEvents(): void {\n this.cy.on(\"add\", \"node\", this.handleNodeAdd);\n this.cy.on(\"remove\", \"node\", this.handleNodeRemove);\n this.cy.on(\"pan zoom\", this.handleViewport);\n this.cy.on(\"position bounds\", \"node\", this.handleNodePosition);\n this.cy.on(\"select unselect grab free\", \"node\", this.handleNodeState);\n }\n\n private addNode(node: cytoscape.NodeSingular): void {\n const data = node.data() as DomNodeData;\n const element = data.dom as DomNodeElement | undefined;\n\n if (!element) {\n return;\n }\n\n const nodeId = node.id();\n const shouldAppend = data.skipNodeAppend !== true && data.skip_node_append !== true;\n\n if (shouldAppend && element.parentNode !== this.container) {\n this.container.appendChild(element);\n }\n\n element.__cy_id = nodeId;\n this.nodeElements.set(nodeId, element);\n if (shouldAppend) {\n this.appendedNodeIds.add(nodeId);\n } else {\n this.appendedNodeIds.delete(nodeId);\n }\n this.bindInteractiveElements(nodeId, element);\n this.resizeObserver.observe(element);\n\n this.syncNodeSize(element);\n this.syncNodePosition(node);\n this.syncNodeState(node);\n }\n\n private removeNode(node: cytoscape.NodeSingular): void {\n const nodeId = node.id();\n const element = this.nodeElements.get(nodeId);\n\n if (!element) {\n return;\n }\n\n this.resizeObserver.unobserve(element);\n delete element.__cy_id;\n this.nodeElements.delete(nodeId);\n this.clearInteractiveElements(nodeId);\n\n if (this.appendedNodeIds.delete(nodeId)) {\n element.parentNode?.removeChild(element);\n }\n }\n\n private syncViewport(): void {\n const pan = this.cy.pan();\n const zoom = this.cy.zoom();\n const transform = `translate(${String(pan.x)}px, ${String(\n pan.y,\n )}px) scale(${String(zoom)})`;\n\n setMsTransform(this.container, transform);\n this.container.style.transform = transform;\n }\n\n private syncNodeSize(element: DomNodeElement): void {\n if (!element.__cy_id) {\n return;\n }\n\n const node = this.cy.getElementById(element.__cy_id);\n\n if (node.empty()) {\n return;\n }\n\n node.style({\n height: element.offsetHeight,\n shape: \"rectangle\",\n width: element.offsetWidth,\n });\n }\n\n private syncNodePosition(node: cytoscape.NodeSingular): void {\n const element = this.nodeElements.get(node.id());\n\n if (!element) {\n return;\n }\n\n const position = node.position();\n const transform = `translate(-50%, -50%) translate(${position.x.toFixed(\n 2,\n )}px, ${position.y.toFixed(2)}px)`;\n\n element.style.webkitTransform = transform;\n setMsTransform(element, transform);\n element.style.transform = transform;\n element.style.display = \"inline\";\n element.style.position = \"absolute\";\n }\n\n private syncNodeState(node: cytoscape.NodeSingular): void {\n const element = this.nodeElements.get(node.id());\n\n if (!element) {\n return;\n }\n\n const isSelected = node.selected();\n const isActive = isSelected || node.grabbed();\n\n element.classList.toggle(SELECTED_CLASS_NAME, isSelected);\n element.style.zIndex = isActive ? ACTIVE_Z_INDEX : DEFAULT_Z_INDEX;\n }\n\n private bindInteractiveElements(nodeId: string, root: DomNodeElement): void {\n this.clearInteractiveElements(nodeId);\n\n if (!this.interactiveSelector) {\n return;\n }\n\n const elements = root.querySelectorAll<HTMLElement>(this.interactiveSelector);\n const bindings: InteractiveElementBinding[] = [];\n\n for (const element of elements) {\n bindings.push({\n element,\n previousPointerEvents: element.style.pointerEvents,\n });\n element.style.pointerEvents = \"auto\";\n\n for (const eventName of this.interactiveEvents) {\n element.addEventListener(eventName, this.stopInteractiveEvent, true);\n }\n }\n\n if (bindings.length > 0) {\n this.interactiveElementBindings.set(nodeId, bindings);\n }\n }\n\n private clearInteractiveElements(nodeId?: string): void {\n if (nodeId) {\n this.clearInteractiveElementBindings(nodeId);\n return;\n }\n\n for (const id of this.interactiveElementBindings.keys()) {\n this.clearInteractiveElementBindings(id);\n }\n }\n\n private clearInteractiveElementBindings(nodeId: string): void {\n const bindings = this.interactiveElementBindings.get(nodeId);\n\n if (!bindings) {\n return;\n }\n\n for (const binding of bindings) {\n binding.element.style.pointerEvents = binding.previousPointerEvents;\n\n for (const eventName of this.interactiveEvents) {\n binding.element.removeEventListener(eventName, this.stopInteractiveEvent, true);\n }\n }\n\n this.interactiveElementBindings.delete(nodeId);\n }\n}\n\n/**\n * Backwards-compatible class export for consumers that referenced the previous\n * implementation name from bundled output.\n */\nexport { DomNodeRenderer as CytoscapeDomNode };\n\n/**\n * Register the extension with Cytoscape.\n */\nexport default function register(cytoscapeInstance?: CytoscapeRegistry): void {\n if (!cytoscapeInstance) {\n return;\n }\n\n cytoscapeInstance(\n \"core\",\n \"domNode\",\n function domNode(this: cytoscape.Core, options?: DomNodeOptions): DomNodeRenderer {\n return new DomNodeRenderer(this, options);\n },\n );\n}\n\nfunction resolveDomContainer(cy: cytoscape.Core, options: DomNodeOptions): HTMLElement {\n const providedContainer = options.domContainer ?? options.dom_container;\n\n if (providedContainer) {\n return providedContainer;\n }\n\n const cyContainer = cy.container();\n const canvas = cyContainer?.querySelector(\"canvas\");\n\n if (!canvas?.parentNode) {\n throw new Error(\n \"cytoscape-dom-node requires a Cytoscape container with a canvas, or a domContainer option.\",\n );\n }\n\n const container = document.createElement(\"div\");\n container.style.position = \"absolute\";\n container.style.transformOrigin = \"0 0\";\n container.style.zIndex = DEFAULT_Z_INDEX;\n\n canvas.parentNode.appendChild(container);\n\n return container;\n}\n\nfunction resolveResizeObserver(options: DomNodeOptions): ResizeObserverConstructor {\n const ResizeObserverClass = options.resizeObserver ?? globalThis.ResizeObserver;\n\n if (!ResizeObserverClass) {\n throw new Error(\n \"cytoscape-dom-node requires ResizeObserver. Provide a polyfill or pass resizeObserver.\",\n );\n }\n\n return ResizeObserverClass;\n}\n\nfunction setMsTransform(element: HTMLElement, transform: string): void {\n (element.style as CSSStyleDeclaration & { msTransform?: string }).msTransform =\n transform;\n}\n\nconst globalCytoscape = globalThis as typeof globalThis & {\n cytoscape?: CytoscapeRegistry;\n};\n\n/* v8 ignore next 3 */\nif (globalCytoscape.cytoscape) {\n register(globalCytoscape.cytoscape);\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.mjs
CHANGED
|
@@ -6,10 +6,16 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
6
6
|
var DEFAULT_Z_INDEX = "10";
|
|
7
7
|
var ACTIVE_Z_INDEX = "11";
|
|
8
8
|
var SELECTED_CLASS_NAME = "selected";
|
|
9
|
+
var DEFAULT_INTERACTIVE_SELECTOR = "input, button, select, textarea, a[href], [contenteditable]:not([contenteditable='false']), [data-cy-dom-node-interactive]";
|
|
10
|
+
var DEFAULT_INTERACTIVE_EVENTS = ["pointerdown", "mousedown", "touchstart"];
|
|
9
11
|
var DomNodeRenderer = class {
|
|
10
12
|
constructor(cy, options = {}) {
|
|
11
13
|
__publicField(this, "cy");
|
|
12
14
|
__publicField(this, "nodeElements", /* @__PURE__ */ new Map());
|
|
15
|
+
__publicField(this, "appendedNodeIds", /* @__PURE__ */ new Set());
|
|
16
|
+
__publicField(this, "interactiveElementBindings", /* @__PURE__ */ new Map());
|
|
17
|
+
__publicField(this, "interactiveEvents");
|
|
18
|
+
__publicField(this, "interactiveSelector");
|
|
13
19
|
__publicField(this, "resizeObserver");
|
|
14
20
|
__publicField(this, "container");
|
|
15
21
|
__publicField(this, "handleNodeAdd", (event) => {
|
|
@@ -27,8 +33,14 @@ var DomNodeRenderer = class {
|
|
|
27
33
|
__publicField(this, "handleViewport", () => {
|
|
28
34
|
this.syncViewport();
|
|
29
35
|
});
|
|
36
|
+
__publicField(this, "stopInteractiveEvent", (event) => {
|
|
37
|
+
event.stopPropagation();
|
|
38
|
+
});
|
|
39
|
+
var _a, _b;
|
|
30
40
|
this.cy = cy;
|
|
31
41
|
this.container = resolveDomContainer(cy, options);
|
|
42
|
+
this.interactiveEvents = (_a = options.interactiveEvents) != null ? _a : DEFAULT_INTERACTIVE_EVENTS;
|
|
43
|
+
this.interactiveSelector = (_b = options.interactiveSelector) != null ? _b : DEFAULT_INTERACTIVE_SELECTOR;
|
|
32
44
|
this.resizeObserver = new (resolveResizeObserver(options))((entries) => {
|
|
33
45
|
for (const entry of entries) {
|
|
34
46
|
this.syncNodeSize(entry.target);
|
|
@@ -63,6 +75,8 @@ var DomNodeRenderer = class {
|
|
|
63
75
|
this.cy.off("select unselect grab free", "node", this.handleNodeState);
|
|
64
76
|
this.resizeObserver.disconnect();
|
|
65
77
|
this.nodeElements.clear();
|
|
78
|
+
this.appendedNodeIds.clear();
|
|
79
|
+
this.clearInteractiveElements();
|
|
66
80
|
}
|
|
67
81
|
bindEvents() {
|
|
68
82
|
this.cy.on("add", "node", this.handleNodeAdd);
|
|
@@ -84,19 +98,31 @@ var DomNodeRenderer = class {
|
|
|
84
98
|
}
|
|
85
99
|
element.__cy_id = nodeId;
|
|
86
100
|
this.nodeElements.set(nodeId, element);
|
|
101
|
+
if (shouldAppend) {
|
|
102
|
+
this.appendedNodeIds.add(nodeId);
|
|
103
|
+
} else {
|
|
104
|
+
this.appendedNodeIds.delete(nodeId);
|
|
105
|
+
}
|
|
106
|
+
this.bindInteractiveElements(nodeId, element);
|
|
87
107
|
this.resizeObserver.observe(element);
|
|
88
108
|
this.syncNodeSize(element);
|
|
89
109
|
this.syncNodePosition(node);
|
|
90
110
|
this.syncNodeState(node);
|
|
91
111
|
}
|
|
92
112
|
removeNode(node) {
|
|
93
|
-
|
|
113
|
+
var _a;
|
|
114
|
+
const nodeId = node.id();
|
|
115
|
+
const element = this.nodeElements.get(nodeId);
|
|
94
116
|
if (!element) {
|
|
95
117
|
return;
|
|
96
118
|
}
|
|
97
119
|
this.resizeObserver.unobserve(element);
|
|
98
120
|
delete element.__cy_id;
|
|
99
|
-
this.nodeElements.delete(
|
|
121
|
+
this.nodeElements.delete(nodeId);
|
|
122
|
+
this.clearInteractiveElements(nodeId);
|
|
123
|
+
if (this.appendedNodeIds.delete(nodeId)) {
|
|
124
|
+
(_a = element.parentNode) == null ? void 0 : _a.removeChild(element);
|
|
125
|
+
}
|
|
100
126
|
}
|
|
101
127
|
syncViewport() {
|
|
102
128
|
const pan = this.cy.pan();
|
|
@@ -146,6 +172,49 @@ var DomNodeRenderer = class {
|
|
|
146
172
|
element.classList.toggle(SELECTED_CLASS_NAME, isSelected);
|
|
147
173
|
element.style.zIndex = isActive ? ACTIVE_Z_INDEX : DEFAULT_Z_INDEX;
|
|
148
174
|
}
|
|
175
|
+
bindInteractiveElements(nodeId, root) {
|
|
176
|
+
this.clearInteractiveElements(nodeId);
|
|
177
|
+
if (!this.interactiveSelector) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
const elements = root.querySelectorAll(this.interactiveSelector);
|
|
181
|
+
const bindings = [];
|
|
182
|
+
for (const element of elements) {
|
|
183
|
+
bindings.push({
|
|
184
|
+
element,
|
|
185
|
+
previousPointerEvents: element.style.pointerEvents
|
|
186
|
+
});
|
|
187
|
+
element.style.pointerEvents = "auto";
|
|
188
|
+
for (const eventName of this.interactiveEvents) {
|
|
189
|
+
element.addEventListener(eventName, this.stopInteractiveEvent, true);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (bindings.length > 0) {
|
|
193
|
+
this.interactiveElementBindings.set(nodeId, bindings);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
clearInteractiveElements(nodeId) {
|
|
197
|
+
if (nodeId) {
|
|
198
|
+
this.clearInteractiveElementBindings(nodeId);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
for (const id of this.interactiveElementBindings.keys()) {
|
|
202
|
+
this.clearInteractiveElementBindings(id);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
clearInteractiveElementBindings(nodeId) {
|
|
206
|
+
const bindings = this.interactiveElementBindings.get(nodeId);
|
|
207
|
+
if (!bindings) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
for (const binding of bindings) {
|
|
211
|
+
binding.element.style.pointerEvents = binding.previousPointerEvents;
|
|
212
|
+
for (const eventName of this.interactiveEvents) {
|
|
213
|
+
binding.element.removeEventListener(eventName, this.stopInteractiveEvent, true);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
this.interactiveElementBindings.delete(nodeId);
|
|
217
|
+
}
|
|
149
218
|
};
|
|
150
219
|
function register(cytoscapeInstance) {
|
|
151
220
|
if (!cytoscapeInstance) {
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Index.\n *\n * @packageDocumentation\n */\n\nimport type cytoscape from \"cytoscape\";\n\nconst DEFAULT_Z_INDEX = \"10\";\nconst ACTIVE_Z_INDEX = \"11\";\nconst SELECTED_CLASS_NAME = \"selected\";\n\ntype CytoscapeRegistry = typeof cytoscape;\n\n/**\n * Constructor shape for ResizeObserver implementations or polyfills.\n */\nexport type ResizeObserverConstructor = new (\n callback: ResizeObserverCallback,\n) => ResizeObserver;\n\ntype DomNodeElement = HTMLElement & {\n __cy_id?: string;\n};\n\ninterface DomNodeData {\n dom?: HTMLElement;\n skipNodeAppend?: boolean;\n skip_node_append?: boolean;\n}\n\n/**\n * Options for {@link DomNodeRenderer}.\n */\nexport interface DomNodeOptions {\n /**\n * Container that receives node DOM elements. When omitted, the extension\n * creates an absolutely positioned overlay next to Cytoscape's canvas.\n */\n domContainer?: HTMLElement;\n\n /**\n * @deprecated Use `domContainer`.\n */\n dom_container?: HTMLElement;\n\n /**\n * ResizeObserver implementation. This is mainly useful for tests or\n * browser environments that provide a polyfill.\n */\n resizeObserver?: ResizeObserverConstructor;\n}\n\ndeclare global {\n namespace cytoscape {\n interface Core {\n /**\n * Enable DOM-backed Cytoscape nodes for this core instance.\n */\n domNode(options?: DomNodeOptions): DomNodeRenderer;\n }\n }\n}\n\ndeclare module \"cytoscape\" {\n namespace cytoscape {\n interface Core {\n /**\n * Enable DOM-backed Cytoscape nodes for this core instance.\n */\n domNode(options?: DomNodeOptions): DomNodeRenderer;\n }\n }\n}\n\n/**\n * Keeps Cytoscape node positions, dimensions, and interaction state mirrored\n * onto caller-supplied DOM elements.\n */\nexport class DomNodeRenderer {\n private readonly cy: cytoscape.Core;\n private readonly nodeElements = new Map<string, DomNodeElement>();\n private readonly resizeObserver: ResizeObserver;\n private readonly container: HTMLElement;\n\n private readonly handleNodeAdd = (event: cytoscape.EventObject): void => {\n this.addNode(event.target as cytoscape.NodeSingular);\n };\n\n private readonly handleNodeRemove = (event: cytoscape.EventObject): void => {\n this.removeNode(event.target as cytoscape.NodeSingular);\n };\n\n private readonly handleNodePosition = (event: cytoscape.EventObject): void => {\n this.syncNodePosition(event.target as cytoscape.NodeSingular);\n };\n\n private readonly handleNodeState = (event: cytoscape.EventObject): void => {\n this.syncNodeState(event.target as cytoscape.NodeSingular);\n };\n\n private readonly handleViewport = (): void => {\n this.syncViewport();\n };\n\n public constructor(cy: cytoscape.Core, options: DomNodeOptions = {}) {\n this.cy = cy;\n this.container = resolveDomContainer(cy, options);\n this.resizeObserver = new (resolveResizeObserver(options))((entries) => {\n for (const entry of entries) {\n this.syncNodeSize(entry.target as DomNodeElement);\n }\n });\n\n this.bindEvents();\n cy.nodes().forEach((node) => {\n this.addNode(node);\n });\n this.syncViewport();\n }\n\n /**\n * Return the DOM element that backs a Cytoscape node id.\n */\n public nodeDom(id: string): HTMLElement | undefined {\n return this.nodeElements.get(id);\n }\n\n /**\n * @deprecated Use {@link nodeDom}.\n */\n public node_dom(id: string): HTMLElement | undefined {\n return this.nodeDom(id);\n }\n\n /**\n * Remove event handlers and disconnect resize observation.\n */\n public destroy(): void {\n this.cy.off(\"add\", \"node\", this.handleNodeAdd);\n this.cy.off(\"remove\", \"node\", this.handleNodeRemove);\n this.cy.off(\"pan zoom\", this.handleViewport);\n this.cy.off(\"position bounds\", \"node\", this.handleNodePosition);\n this.cy.off(\"select unselect grab free\", \"node\", this.handleNodeState);\n this.resizeObserver.disconnect();\n this.nodeElements.clear();\n }\n\n private bindEvents(): void {\n this.cy.on(\"add\", \"node\", this.handleNodeAdd);\n this.cy.on(\"remove\", \"node\", this.handleNodeRemove);\n this.cy.on(\"pan zoom\", this.handleViewport);\n this.cy.on(\"position bounds\", \"node\", this.handleNodePosition);\n this.cy.on(\"select unselect grab free\", \"node\", this.handleNodeState);\n }\n\n private addNode(node: cytoscape.NodeSingular): void {\n const data = node.data() as DomNodeData;\n const element = data.dom as DomNodeElement | undefined;\n\n if (!element) {\n return;\n }\n\n const nodeId = node.id();\n const shouldAppend = data.skipNodeAppend !== true && data.skip_node_append !== true;\n\n if (shouldAppend && element.parentNode !== this.container) {\n this.container.appendChild(element);\n }\n\n element.__cy_id = nodeId;\n this.nodeElements.set(nodeId, element);\n this.resizeObserver.observe(element);\n\n this.syncNodeSize(element);\n this.syncNodePosition(node);\n this.syncNodeState(node);\n }\n\n private removeNode(node: cytoscape.NodeSingular): void {\n const element = this.nodeElements.get(node.id());\n\n if (!element) {\n return;\n }\n\n this.resizeObserver.unobserve(element);\n delete element.__cy_id;\n this.nodeElements.delete(node.id());\n }\n\n private syncViewport(): void {\n const pan = this.cy.pan();\n const zoom = this.cy.zoom();\n const transform = `translate(${String(pan.x)}px, ${String(\n pan.y,\n )}px) scale(${String(zoom)})`;\n\n setMsTransform(this.container, transform);\n this.container.style.transform = transform;\n }\n\n private syncNodeSize(element: DomNodeElement): void {\n if (!element.__cy_id) {\n return;\n }\n\n const node = this.cy.getElementById(element.__cy_id);\n\n if (node.empty()) {\n return;\n }\n\n node.style({\n height: element.offsetHeight,\n shape: \"rectangle\",\n width: element.offsetWidth,\n });\n }\n\n private syncNodePosition(node: cytoscape.NodeSingular): void {\n const element = this.nodeElements.get(node.id());\n\n if (!element) {\n return;\n }\n\n const position = node.position();\n const transform = `translate(-50%, -50%) translate(${position.x.toFixed(\n 2,\n )}px, ${position.y.toFixed(2)}px)`;\n\n element.style.webkitTransform = transform;\n setMsTransform(element, transform);\n element.style.transform = transform;\n element.style.display = \"inline\";\n element.style.position = \"absolute\";\n }\n\n private syncNodeState(node: cytoscape.NodeSingular): void {\n const element = this.nodeElements.get(node.id());\n\n if (!element) {\n return;\n }\n\n const isSelected = node.selected();\n const isActive = isSelected || node.grabbed();\n\n element.classList.toggle(SELECTED_CLASS_NAME, isSelected);\n element.style.zIndex = isActive ? ACTIVE_Z_INDEX : DEFAULT_Z_INDEX;\n }\n}\n\n/**\n * Backwards-compatible class export for consumers that referenced the previous\n * implementation name from bundled output.\n */\nexport { DomNodeRenderer as CytoscapeDomNode };\n\n/**\n * Register the extension with Cytoscape.\n */\nexport default function register(cytoscapeInstance?: CytoscapeRegistry): void {\n if (!cytoscapeInstance) {\n return;\n }\n\n cytoscapeInstance(\n \"core\",\n \"domNode\",\n function domNode(this: cytoscape.Core, options?: DomNodeOptions): DomNodeRenderer {\n return new DomNodeRenderer(this, options);\n },\n );\n}\n\nfunction resolveDomContainer(cy: cytoscape.Core, options: DomNodeOptions): HTMLElement {\n const providedContainer = options.domContainer ?? options.dom_container;\n\n if (providedContainer) {\n return providedContainer;\n }\n\n const cyContainer = cy.container();\n const canvas = cyContainer?.querySelector(\"canvas\");\n\n if (!canvas?.parentNode) {\n throw new Error(\n \"cytoscape-dom-node requires a Cytoscape container with a canvas, or a domContainer option.\",\n );\n }\n\n const container = document.createElement(\"div\");\n container.style.position = \"absolute\";\n container.style.transformOrigin = \"0 0\";\n container.style.zIndex = DEFAULT_Z_INDEX;\n\n canvas.parentNode.appendChild(container);\n\n return container;\n}\n\nfunction resolveResizeObserver(options: DomNodeOptions): ResizeObserverConstructor {\n const ResizeObserverClass = options.resizeObserver ?? globalThis.ResizeObserver;\n\n if (!ResizeObserverClass) {\n throw new Error(\n \"cytoscape-dom-node requires ResizeObserver. Provide a polyfill or pass resizeObserver.\",\n );\n }\n\n return ResizeObserverClass;\n}\n\nfunction setMsTransform(element: HTMLElement, transform: string): void {\n (element.style as CSSStyleDeclaration & { msTransform?: string }).msTransform =\n transform;\n}\n\nconst globalCytoscape = globalThis as typeof globalThis & {\n cytoscape?: CytoscapeRegistry;\n};\n\n/* v8 ignore next 3 */\nif (globalCytoscape.cytoscape) {\n register(globalCytoscape.cytoscape);\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Index.\n *\n * @packageDocumentation\n */\n\nimport type cytoscape from \"cytoscape\";\n\nconst DEFAULT_Z_INDEX = \"10\";\nconst ACTIVE_Z_INDEX = \"11\";\nconst SELECTED_CLASS_NAME = \"selected\";\nconst DEFAULT_INTERACTIVE_SELECTOR =\n \"input, button, select, textarea, a[href], [contenteditable]:not([contenteditable='false']), [data-cy-dom-node-interactive]\";\nconst DEFAULT_INTERACTIVE_EVENTS = [\"pointerdown\", \"mousedown\", \"touchstart\"] as const;\n\ntype CytoscapeRegistry = typeof cytoscape;\n\n/**\n * Constructor shape for ResizeObserver implementations or polyfills.\n */\nexport type ResizeObserverConstructor = new (\n callback: ResizeObserverCallback,\n) => ResizeObserver;\n\ntype DomNodeElement = HTMLElement & {\n __cy_id?: string;\n};\n\ninterface InteractiveElementBinding {\n element: HTMLElement;\n previousPointerEvents: string;\n}\n\ninterface DomNodeData {\n dom?: HTMLElement;\n skipNodeAppend?: boolean;\n skip_node_append?: boolean;\n}\n\n/**\n * Options for {@link DomNodeRenderer}.\n */\nexport interface DomNodeOptions {\n /**\n * Container that receives node DOM elements. When omitted, the extension\n * creates an absolutely positioned overlay next to Cytoscape's canvas.\n */\n domContainer?: HTMLElement;\n\n /**\n * @deprecated Use `domContainer`.\n */\n dom_container?: HTMLElement;\n\n /**\n * ResizeObserver implementation. This is mainly useful for tests or\n * browser environments that provide a polyfill.\n */\n resizeObserver?: ResizeObserverConstructor;\n\n /**\n * Selector for child controls that should receive native DOM interaction\n * instead of starting Cytoscape gestures. Set to `false` to disable this\n * automatic event isolation.\n */\n interactiveSelector?: string | false;\n\n /**\n * Event names stopped on interactive child controls during the capture phase.\n */\n interactiveEvents?: readonly string[];\n}\n\ndeclare global {\n namespace cytoscape {\n interface Core {\n /**\n * Enable DOM-backed Cytoscape nodes for this core instance.\n */\n domNode(options?: DomNodeOptions): DomNodeRenderer;\n }\n }\n}\n\ndeclare module \"cytoscape\" {\n namespace cytoscape {\n interface Core {\n /**\n * Enable DOM-backed Cytoscape nodes for this core instance.\n */\n domNode(options?: DomNodeOptions): DomNodeRenderer;\n }\n }\n}\n\n/**\n * Keeps Cytoscape node positions, dimensions, and interaction state mirrored\n * onto caller-supplied DOM elements.\n */\nexport class DomNodeRenderer {\n private readonly cy: cytoscape.Core;\n private readonly nodeElements = new Map<string, DomNodeElement>();\n private readonly appendedNodeIds = new Set<string>();\n private readonly interactiveElementBindings = new Map<\n string,\n InteractiveElementBinding[]\n >();\n private readonly interactiveEvents: readonly string[];\n private readonly interactiveSelector: string | false;\n private readonly resizeObserver: ResizeObserver;\n private readonly container: HTMLElement;\n\n private readonly handleNodeAdd = (event: cytoscape.EventObject): void => {\n this.addNode(event.target as cytoscape.NodeSingular);\n };\n\n private readonly handleNodeRemove = (event: cytoscape.EventObject): void => {\n this.removeNode(event.target as cytoscape.NodeSingular);\n };\n\n private readonly handleNodePosition = (event: cytoscape.EventObject): void => {\n this.syncNodePosition(event.target as cytoscape.NodeSingular);\n };\n\n private readonly handleNodeState = (event: cytoscape.EventObject): void => {\n this.syncNodeState(event.target as cytoscape.NodeSingular);\n };\n\n private readonly handleViewport = (): void => {\n this.syncViewport();\n };\n\n private readonly stopInteractiveEvent = (event: Event): void => {\n event.stopPropagation();\n };\n\n public constructor(cy: cytoscape.Core, options: DomNodeOptions = {}) {\n this.cy = cy;\n this.container = resolveDomContainer(cy, options);\n this.interactiveEvents = options.interactiveEvents ?? DEFAULT_INTERACTIVE_EVENTS;\n this.interactiveSelector =\n options.interactiveSelector ?? DEFAULT_INTERACTIVE_SELECTOR;\n this.resizeObserver = new (resolveResizeObserver(options))((entries) => {\n for (const entry of entries) {\n this.syncNodeSize(entry.target as DomNodeElement);\n }\n });\n\n this.bindEvents();\n cy.nodes().forEach((node) => {\n this.addNode(node);\n });\n this.syncViewport();\n }\n\n /**\n * Return the DOM element that backs a Cytoscape node id.\n */\n public nodeDom(id: string): HTMLElement | undefined {\n return this.nodeElements.get(id);\n }\n\n /**\n * @deprecated Use {@link nodeDom}.\n */\n public node_dom(id: string): HTMLElement | undefined {\n return this.nodeDom(id);\n }\n\n /**\n * Remove event handlers and disconnect resize observation.\n */\n public destroy(): void {\n this.cy.off(\"add\", \"node\", this.handleNodeAdd);\n this.cy.off(\"remove\", \"node\", this.handleNodeRemove);\n this.cy.off(\"pan zoom\", this.handleViewport);\n this.cy.off(\"position bounds\", \"node\", this.handleNodePosition);\n this.cy.off(\"select unselect grab free\", \"node\", this.handleNodeState);\n this.resizeObserver.disconnect();\n this.nodeElements.clear();\n this.appendedNodeIds.clear();\n this.clearInteractiveElements();\n }\n\n private bindEvents(): void {\n this.cy.on(\"add\", \"node\", this.handleNodeAdd);\n this.cy.on(\"remove\", \"node\", this.handleNodeRemove);\n this.cy.on(\"pan zoom\", this.handleViewport);\n this.cy.on(\"position bounds\", \"node\", this.handleNodePosition);\n this.cy.on(\"select unselect grab free\", \"node\", this.handleNodeState);\n }\n\n private addNode(node: cytoscape.NodeSingular): void {\n const data = node.data() as DomNodeData;\n const element = data.dom as DomNodeElement | undefined;\n\n if (!element) {\n return;\n }\n\n const nodeId = node.id();\n const shouldAppend = data.skipNodeAppend !== true && data.skip_node_append !== true;\n\n if (shouldAppend && element.parentNode !== this.container) {\n this.container.appendChild(element);\n }\n\n element.__cy_id = nodeId;\n this.nodeElements.set(nodeId, element);\n if (shouldAppend) {\n this.appendedNodeIds.add(nodeId);\n } else {\n this.appendedNodeIds.delete(nodeId);\n }\n this.bindInteractiveElements(nodeId, element);\n this.resizeObserver.observe(element);\n\n this.syncNodeSize(element);\n this.syncNodePosition(node);\n this.syncNodeState(node);\n }\n\n private removeNode(node: cytoscape.NodeSingular): void {\n const nodeId = node.id();\n const element = this.nodeElements.get(nodeId);\n\n if (!element) {\n return;\n }\n\n this.resizeObserver.unobserve(element);\n delete element.__cy_id;\n this.nodeElements.delete(nodeId);\n this.clearInteractiveElements(nodeId);\n\n if (this.appendedNodeIds.delete(nodeId)) {\n element.parentNode?.removeChild(element);\n }\n }\n\n private syncViewport(): void {\n const pan = this.cy.pan();\n const zoom = this.cy.zoom();\n const transform = `translate(${String(pan.x)}px, ${String(\n pan.y,\n )}px) scale(${String(zoom)})`;\n\n setMsTransform(this.container, transform);\n this.container.style.transform = transform;\n }\n\n private syncNodeSize(element: DomNodeElement): void {\n if (!element.__cy_id) {\n return;\n }\n\n const node = this.cy.getElementById(element.__cy_id);\n\n if (node.empty()) {\n return;\n }\n\n node.style({\n height: element.offsetHeight,\n shape: \"rectangle\",\n width: element.offsetWidth,\n });\n }\n\n private syncNodePosition(node: cytoscape.NodeSingular): void {\n const element = this.nodeElements.get(node.id());\n\n if (!element) {\n return;\n }\n\n const position = node.position();\n const transform = `translate(-50%, -50%) translate(${position.x.toFixed(\n 2,\n )}px, ${position.y.toFixed(2)}px)`;\n\n element.style.webkitTransform = transform;\n setMsTransform(element, transform);\n element.style.transform = transform;\n element.style.display = \"inline\";\n element.style.position = \"absolute\";\n }\n\n private syncNodeState(node: cytoscape.NodeSingular): void {\n const element = this.nodeElements.get(node.id());\n\n if (!element) {\n return;\n }\n\n const isSelected = node.selected();\n const isActive = isSelected || node.grabbed();\n\n element.classList.toggle(SELECTED_CLASS_NAME, isSelected);\n element.style.zIndex = isActive ? ACTIVE_Z_INDEX : DEFAULT_Z_INDEX;\n }\n\n private bindInteractiveElements(nodeId: string, root: DomNodeElement): void {\n this.clearInteractiveElements(nodeId);\n\n if (!this.interactiveSelector) {\n return;\n }\n\n const elements = root.querySelectorAll<HTMLElement>(this.interactiveSelector);\n const bindings: InteractiveElementBinding[] = [];\n\n for (const element of elements) {\n bindings.push({\n element,\n previousPointerEvents: element.style.pointerEvents,\n });\n element.style.pointerEvents = \"auto\";\n\n for (const eventName of this.interactiveEvents) {\n element.addEventListener(eventName, this.stopInteractiveEvent, true);\n }\n }\n\n if (bindings.length > 0) {\n this.interactiveElementBindings.set(nodeId, bindings);\n }\n }\n\n private clearInteractiveElements(nodeId?: string): void {\n if (nodeId) {\n this.clearInteractiveElementBindings(nodeId);\n return;\n }\n\n for (const id of this.interactiveElementBindings.keys()) {\n this.clearInteractiveElementBindings(id);\n }\n }\n\n private clearInteractiveElementBindings(nodeId: string): void {\n const bindings = this.interactiveElementBindings.get(nodeId);\n\n if (!bindings) {\n return;\n }\n\n for (const binding of bindings) {\n binding.element.style.pointerEvents = binding.previousPointerEvents;\n\n for (const eventName of this.interactiveEvents) {\n binding.element.removeEventListener(eventName, this.stopInteractiveEvent, true);\n }\n }\n\n this.interactiveElementBindings.delete(nodeId);\n }\n}\n\n/**\n * Backwards-compatible class export for consumers that referenced the previous\n * implementation name from bundled output.\n */\nexport { DomNodeRenderer as CytoscapeDomNode };\n\n/**\n * Register the extension with Cytoscape.\n */\nexport default function register(cytoscapeInstance?: CytoscapeRegistry): void {\n if (!cytoscapeInstance) {\n return;\n }\n\n cytoscapeInstance(\n \"core\",\n \"domNode\",\n function domNode(this: cytoscape.Core, options?: DomNodeOptions): DomNodeRenderer {\n return new DomNodeRenderer(this, options);\n },\n );\n}\n\nfunction resolveDomContainer(cy: cytoscape.Core, options: DomNodeOptions): HTMLElement {\n const providedContainer = options.domContainer ?? options.dom_container;\n\n if (providedContainer) {\n return providedContainer;\n }\n\n const cyContainer = cy.container();\n const canvas = cyContainer?.querySelector(\"canvas\");\n\n if (!canvas?.parentNode) {\n throw new Error(\n \"cytoscape-dom-node requires a Cytoscape container with a canvas, or a domContainer option.\",\n );\n }\n\n const container = document.createElement(\"div\");\n container.style.position = \"absolute\";\n container.style.transformOrigin = \"0 0\";\n container.style.zIndex = DEFAULT_Z_INDEX;\n\n canvas.parentNode.appendChild(container);\n\n return container;\n}\n\nfunction resolveResizeObserver(options: DomNodeOptions): ResizeObserverConstructor {\n const ResizeObserverClass = options.resizeObserver ?? globalThis.ResizeObserver;\n\n if (!ResizeObserverClass) {\n throw new Error(\n \"cytoscape-dom-node requires ResizeObserver. Provide a polyfill or pass resizeObserver.\",\n );\n }\n\n return ResizeObserverClass;\n}\n\nfunction setMsTransform(element: HTMLElement, transform: string): void {\n (element.style as CSSStyleDeclaration & { msTransform?: string }).msTransform =\n transform;\n}\n\nconst globalCytoscape = globalThis as typeof globalThis & {\n cytoscape?: CytoscapeRegistry;\n};\n\n/* v8 ignore next 3 */\nif (globalCytoscape.cytoscape) {\n register(globalCytoscape.cytoscape);\n}\n"],"mappings":"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","names":[]}
|