cytoscape-dom-node 1.3.0 → 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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (C) 2021 Michael Wright <mjw@methodanalysis.com>
3
+ Copyright (C) 2021-2026 Michael Wright <mjw@methodanalysis.com>
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -6,83 +6,170 @@ rendered on top of the node, and the node will be set to the size of the DOM
6
6
  element.
7
7
 
8
8
  For a full working demo, see [codepen abWdVOG](https://codepen.io/mwri/pen/abWdVOG).
9
+ There are also more demos in [./demos](./demos/README.md).
9
10
 
10
- ## Depenedencies
11
+ ## History
11
12
 
12
- * cytoscape ^3.19.0
13
+ v2.1.0 has bug fixes / improvements outstanding since v1.
14
+ v2.0.0 - v2.0.1 is a typescript conversion, and SHOULD be backwards compatible with v1.
15
+ v1.3.0 is the latest pre typescript incarnation.
16
+
17
+ ## Dependencies
18
+
19
+ - cytoscape ^3.19.0
13
20
 
14
21
  ## Extension registration
15
22
 
16
- Either import/require `cytoscape-dom-node`, and register it as an extension with Cytoscape:
23
+ Import `cytoscape-dom-node`, and register it as an extension with Cytoscape:
24
+
25
+ ```ts
26
+ import cytoscape from "cytoscape";
27
+ import cytoscapeDomNode from "cytoscape-dom-node";
28
+
29
+ cytoscape.use(cytoscapeDomNode);
30
+ ```
31
+
32
+ CommonJS is still supported:
17
33
 
18
34
  ```js
19
- const cytoscape = require('cytoscape');
20
- cytoscape.use(require('cytoscape-dom-node'));
35
+ const cytoscape = require("cytoscape");
36
+ const cytoscapeDomNode = require("cytoscape-dom-node");
37
+
38
+ cytoscape.use(cytoscapeDomNode);
21
39
  ```
22
40
 
23
41
  Or it can be included via a `<script>` tag after `cytoscape`, and will register itself:
24
42
 
25
43
  ```html
26
44
  <script type="text/javascript" charset="utf8" src="path/to/cytoscape.js"></script>
27
- <script type="text/javascript" charset="utf8" src="path/to/cytoscape-dom-node.js"></script>
45
+ <script
46
+ type="text/javascript"
47
+ charset="utf8"
48
+ src="path/to/cytoscape-dom-node.global.js"
49
+ ></script>
28
50
  ```
29
51
 
30
52
  ## Usage instructions
31
53
 
32
54
  Create a `cytoscape` instance and call `domNode` on it:
33
55
 
34
- ```js
35
- let cy = cytoscape({
36
- 'container': document.getElementById('id-of-my-cytoscape-container'),
37
- 'elements': [],
56
+ ```ts
57
+ const cy = cytoscape({
58
+ container: document.getElementById("id-of-my-cytoscape-container"),
59
+ elements: [],
38
60
  });
39
61
 
40
- cy.domNode();
62
+ const domNodeRenderer = cy.domNode();
41
63
  ```
42
64
 
43
65
  Now add a node with `dom` in the data, set to a DOM element:
44
66
 
45
- ```js
46
- let div = document.createElement("div");
67
+ ```ts
68
+ const div = document.createElement("div");
47
69
  div.innerHTML = `node ${id}`;
48
70
 
49
71
  cy.add({
50
- 'data': {
51
- 'id': id,
52
- 'dom': div,
53
- },
72
+ data: {
73
+ dom: div,
74
+ id,
75
+ },
54
76
  });
55
77
  ```
56
78
 
57
79
  The `div` you created will be shown as the node now.
58
80
 
81
+ You can retrieve the DOM element for a Cytoscape node id:
82
+
83
+ ```ts
84
+ const nodeElement = domNodeRenderer.nodeDom(id);
85
+ ```
86
+
87
+ The previous `node_dom(id)` method is kept as a backwards compatible alias.
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
+
59
93
  See [codepen abWdVOG](https://codepen.io/mwri/pen/abWdVOG) for a working
60
94
  example.
61
95
 
62
96
  ### Skip Node Append
63
- The `skip_node_append` option is a boolean flag (passed with node data) that controls whether the `cytoscape-dom-node` appends the provided node to the provided DOM container.
64
- By default, this option is set to false, meaning the `cytoscape-dom-node` will append the node to the container.
65
97
 
66
- However, in certain scenarios, such as when using EmberJS or another front-end framework, you might have already rendered the nodes to the DOM.
67
- In these cases, you can set `skip_node_append` to true to prevent the library from appending the node, allowing you to maintain control over the node's rendering process.
98
+ The `skipNodeAppend` node data option controls whether `cytoscape-dom-node`
99
+ appends the provided DOM node to the configured DOM container. By default,
100
+ `cytoscape-dom-node` appends the node to the container.
68
101
 
69
- ```js
70
- let div = document.querySelector("#alreadyRenderedNodeId");
102
+ In certain scenarios, such as when using EmberJS or another front-end framework,
103
+ you might have already rendered the nodes to the DOM. In these cases, set
104
+ `skipNodeAppend` to `true` to keep control over rendering.
105
+
106
+ ```ts
107
+ const div = document.querySelector("#alreadyRenderedNodeId");
71
108
  cy.add({
72
- 'data': {
73
- 'id': id,
74
- 'dom': div,
75
- 'skip_node_append': true,
76
- }
77
- })
109
+ data: {
110
+ dom: div,
111
+ id,
112
+ skipNodeAppend: true,
113
+ },
114
+ });
78
115
  ```
79
116
 
117
+ The legacy `skip_node_append` name remains supported.
118
+
80
119
  ## Options
81
120
 
82
- One option is supported, `dom_container` allows an container element to be specified which
83
- will be used for nodes instead of the element it would otherwise create and use. It is the
84
- callers responsibility to style the given element appropriately, for example:
121
+ `domContainer` allows a container element to be specified. It will be used for
122
+ nodes instead of the element `cytoscape-dom-node` would otherwise create. It is
123
+ the caller's responsibility to style the given element appropriately:
85
124
 
86
- ```js
87
- cy.domNode({'dom_container': some_element});
125
+ ```ts
126
+ cy.domNode({ domContainer: someElement });
127
+ ```
128
+
129
+ The legacy `dom_container` name remains supported.
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
+
155
+ ## Interaction State
156
+
157
+ DOM nodes mirror Cytoscape selection state with the `selected` class. Selected
158
+ or grabbed nodes receive z-index `11`; other DOM nodes receive z-index `10`.
159
+
160
+ ## Development
161
+
162
+ This package is authored in TypeScript and emits CommonJS, ESM, browser global,
163
+ and declaration outputs into `dist`.
164
+
165
+ Useful commands:
166
+
167
+ ```sh
168
+ npm run lint
169
+ npm run typecheck
170
+ npm run test
171
+ npm run test:coverage
172
+ npm run build
173
+ npm run docs
174
+ npm run check
88
175
  ```
package/dist/index.cjs ADDED
@@ -0,0 +1,298 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
21
+
22
+ // src/index.ts
23
+ var index_exports = {};
24
+ __export(index_exports, {
25
+ CytoscapeDomNode: () => DomNodeRenderer,
26
+ DomNodeRenderer: () => DomNodeRenderer,
27
+ default: () => register
28
+ });
29
+ module.exports = __toCommonJS(index_exports);
30
+ var DEFAULT_Z_INDEX = "10";
31
+ var ACTIVE_Z_INDEX = "11";
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"];
35
+ var DomNodeRenderer = class {
36
+ constructor(cy, options = {}) {
37
+ __publicField(this, "cy");
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");
43
+ __publicField(this, "resizeObserver");
44
+ __publicField(this, "container");
45
+ __publicField(this, "handleNodeAdd", (event) => {
46
+ this.addNode(event.target);
47
+ });
48
+ __publicField(this, "handleNodeRemove", (event) => {
49
+ this.removeNode(event.target);
50
+ });
51
+ __publicField(this, "handleNodePosition", (event) => {
52
+ this.syncNodePosition(event.target);
53
+ });
54
+ __publicField(this, "handleNodeState", (event) => {
55
+ this.syncNodeState(event.target);
56
+ });
57
+ __publicField(this, "handleViewport", () => {
58
+ this.syncViewport();
59
+ });
60
+ __publicField(this, "stopInteractiveEvent", (event) => {
61
+ event.stopPropagation();
62
+ });
63
+ var _a, _b;
64
+ this.cy = cy;
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;
68
+ this.resizeObserver = new (resolveResizeObserver(options))((entries) => {
69
+ for (const entry of entries) {
70
+ this.syncNodeSize(entry.target);
71
+ }
72
+ });
73
+ this.bindEvents();
74
+ cy.nodes().forEach((node) => {
75
+ this.addNode(node);
76
+ });
77
+ this.syncViewport();
78
+ }
79
+ /**
80
+ * Return the DOM element that backs a Cytoscape node id.
81
+ */
82
+ nodeDom(id) {
83
+ return this.nodeElements.get(id);
84
+ }
85
+ /**
86
+ * @deprecated Use {@link nodeDom}.
87
+ */
88
+ node_dom(id) {
89
+ return this.nodeDom(id);
90
+ }
91
+ /**
92
+ * Remove event handlers and disconnect resize observation.
93
+ */
94
+ destroy() {
95
+ this.cy.off("add", "node", this.handleNodeAdd);
96
+ this.cy.off("remove", "node", this.handleNodeRemove);
97
+ this.cy.off("pan zoom", this.handleViewport);
98
+ this.cy.off("position bounds", "node", this.handleNodePosition);
99
+ this.cy.off("select unselect grab free", "node", this.handleNodeState);
100
+ this.resizeObserver.disconnect();
101
+ this.nodeElements.clear();
102
+ this.appendedNodeIds.clear();
103
+ this.clearInteractiveElements();
104
+ }
105
+ bindEvents() {
106
+ this.cy.on("add", "node", this.handleNodeAdd);
107
+ this.cy.on("remove", "node", this.handleNodeRemove);
108
+ this.cy.on("pan zoom", this.handleViewport);
109
+ this.cy.on("position bounds", "node", this.handleNodePosition);
110
+ this.cy.on("select unselect grab free", "node", this.handleNodeState);
111
+ }
112
+ addNode(node) {
113
+ const data = node.data();
114
+ const element = data.dom;
115
+ if (!element) {
116
+ return;
117
+ }
118
+ const nodeId = node.id();
119
+ const shouldAppend = data.skipNodeAppend !== true && data.skip_node_append !== true;
120
+ if (shouldAppend && element.parentNode !== this.container) {
121
+ this.container.appendChild(element);
122
+ }
123
+ element.__cy_id = nodeId;
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);
131
+ this.resizeObserver.observe(element);
132
+ this.syncNodeSize(element);
133
+ this.syncNodePosition(node);
134
+ this.syncNodeState(node);
135
+ }
136
+ removeNode(node) {
137
+ var _a;
138
+ const nodeId = node.id();
139
+ const element = this.nodeElements.get(nodeId);
140
+ if (!element) {
141
+ return;
142
+ }
143
+ this.resizeObserver.unobserve(element);
144
+ delete element.__cy_id;
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
+ }
150
+ }
151
+ syncViewport() {
152
+ const pan = this.cy.pan();
153
+ const zoom = this.cy.zoom();
154
+ const transform = `translate(${String(pan.x)}px, ${String(
155
+ pan.y
156
+ )}px) scale(${String(zoom)})`;
157
+ setMsTransform(this.container, transform);
158
+ this.container.style.transform = transform;
159
+ }
160
+ syncNodeSize(element) {
161
+ if (!element.__cy_id) {
162
+ return;
163
+ }
164
+ const node = this.cy.getElementById(element.__cy_id);
165
+ if (node.empty()) {
166
+ return;
167
+ }
168
+ node.style({
169
+ height: element.offsetHeight,
170
+ shape: "rectangle",
171
+ width: element.offsetWidth
172
+ });
173
+ }
174
+ syncNodePosition(node) {
175
+ const element = this.nodeElements.get(node.id());
176
+ if (!element) {
177
+ return;
178
+ }
179
+ const position = node.position();
180
+ const transform = `translate(-50%, -50%) translate(${position.x.toFixed(
181
+ 2
182
+ )}px, ${position.y.toFixed(2)}px)`;
183
+ element.style.webkitTransform = transform;
184
+ setMsTransform(element, transform);
185
+ element.style.transform = transform;
186
+ element.style.display = "inline";
187
+ element.style.position = "absolute";
188
+ }
189
+ syncNodeState(node) {
190
+ const element = this.nodeElements.get(node.id());
191
+ if (!element) {
192
+ return;
193
+ }
194
+ const isSelected = node.selected();
195
+ const isActive = isSelected || node.grabbed();
196
+ element.classList.toggle(SELECTED_CLASS_NAME, isSelected);
197
+ element.style.zIndex = isActive ? ACTIVE_Z_INDEX : DEFAULT_Z_INDEX;
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
+ }
242
+ };
243
+ function register(cytoscapeInstance) {
244
+ if (!cytoscapeInstance) {
245
+ return;
246
+ }
247
+ cytoscapeInstance(
248
+ "core",
249
+ "domNode",
250
+ function domNode(options) {
251
+ return new DomNodeRenderer(this, options);
252
+ }
253
+ );
254
+ }
255
+ function resolveDomContainer(cy, options) {
256
+ var _a;
257
+ const providedContainer = (_a = options.domContainer) != null ? _a : options.dom_container;
258
+ if (providedContainer) {
259
+ return providedContainer;
260
+ }
261
+ const cyContainer = cy.container();
262
+ const canvas = cyContainer == null ? void 0 : cyContainer.querySelector("canvas");
263
+ if (!(canvas == null ? void 0 : canvas.parentNode)) {
264
+ throw new Error(
265
+ "cytoscape-dom-node requires a Cytoscape container with a canvas, or a domContainer option."
266
+ );
267
+ }
268
+ const container = document.createElement("div");
269
+ container.style.position = "absolute";
270
+ container.style.transformOrigin = "0 0";
271
+ container.style.zIndex = DEFAULT_Z_INDEX;
272
+ canvas.parentNode.appendChild(container);
273
+ return container;
274
+ }
275
+ function resolveResizeObserver(options) {
276
+ var _a;
277
+ const ResizeObserverClass = (_a = options.resizeObserver) != null ? _a : globalThis.ResizeObserver;
278
+ if (!ResizeObserverClass) {
279
+ throw new Error(
280
+ "cytoscape-dom-node requires ResizeObserver. Provide a polyfill or pass resizeObserver."
281
+ );
282
+ }
283
+ return ResizeObserverClass;
284
+ }
285
+ function setMsTransform(element, transform) {
286
+ element.style.msTransform = transform;
287
+ }
288
+ var globalCytoscape = globalThis;
289
+ if (globalCytoscape.cytoscape) {
290
+ register(globalCytoscape.cytoscape);
291
+ }
292
+ // Annotate the CommonJS export names for ESM import in node:
293
+ 0 && (module.exports = {
294
+ CytoscapeDomNode,
295
+ DomNodeRenderer
296
+ });
297
+ module.exports = Object.assign(module.exports.default, module.exports);
298
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +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\";\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":[]}
@@ -0,0 +1,112 @@
1
+ import cytoscape from 'cytoscape';
2
+
3
+ /**
4
+ * Index.
5
+ *
6
+ * @packageDocumentation
7
+ */
8
+
9
+ type CytoscapeRegistry = typeof cytoscape;
10
+ /**
11
+ * Constructor shape for ResizeObserver implementations or polyfills.
12
+ */
13
+ type ResizeObserverConstructor = new (callback: ResizeObserverCallback) => ResizeObserver;
14
+ /**
15
+ * Options for {@link DomNodeRenderer}.
16
+ */
17
+ interface DomNodeOptions {
18
+ /**
19
+ * Container that receives node DOM elements. When omitted, the extension
20
+ * creates an absolutely positioned overlay next to Cytoscape's canvas.
21
+ */
22
+ domContainer?: HTMLElement;
23
+ /**
24
+ * @deprecated Use `domContainer`.
25
+ */
26
+ dom_container?: HTMLElement;
27
+ /**
28
+ * ResizeObserver implementation. This is mainly useful for tests or
29
+ * browser environments that provide a polyfill.
30
+ */
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[];
42
+ }
43
+ declare global {
44
+ namespace cytoscape {
45
+ interface Core {
46
+ /**
47
+ * Enable DOM-backed Cytoscape nodes for this core instance.
48
+ */
49
+ domNode(options?: DomNodeOptions): DomNodeRenderer;
50
+ }
51
+ }
52
+ }
53
+ declare module "cytoscape" {
54
+ namespace cytoscape {
55
+ interface Core {
56
+ /**
57
+ * Enable DOM-backed Cytoscape nodes for this core instance.
58
+ */
59
+ domNode(options?: DomNodeOptions): DomNodeRenderer;
60
+ }
61
+ }
62
+ }
63
+ /**
64
+ * Keeps Cytoscape node positions, dimensions, and interaction state mirrored
65
+ * onto caller-supplied DOM elements.
66
+ */
67
+ declare class DomNodeRenderer {
68
+ private readonly cy;
69
+ private readonly nodeElements;
70
+ private readonly appendedNodeIds;
71
+ private readonly interactiveElementBindings;
72
+ private readonly interactiveEvents;
73
+ private readonly interactiveSelector;
74
+ private readonly resizeObserver;
75
+ private readonly container;
76
+ private readonly handleNodeAdd;
77
+ private readonly handleNodeRemove;
78
+ private readonly handleNodePosition;
79
+ private readonly handleNodeState;
80
+ private readonly handleViewport;
81
+ private readonly stopInteractiveEvent;
82
+ constructor(cy: cytoscape.Core, options?: DomNodeOptions);
83
+ /**
84
+ * Return the DOM element that backs a Cytoscape node id.
85
+ */
86
+ nodeDom(id: string): HTMLElement | undefined;
87
+ /**
88
+ * @deprecated Use {@link nodeDom}.
89
+ */
90
+ node_dom(id: string): HTMLElement | undefined;
91
+ /**
92
+ * Remove event handlers and disconnect resize observation.
93
+ */
94
+ destroy(): void;
95
+ private bindEvents;
96
+ private addNode;
97
+ private removeNode;
98
+ private syncViewport;
99
+ private syncNodeSize;
100
+ private syncNodePosition;
101
+ private syncNodeState;
102
+ private bindInteractiveElements;
103
+ private clearInteractiveElements;
104
+ private clearInteractiveElementBindings;
105
+ }
106
+
107
+ /**
108
+ * Register the extension with Cytoscape.
109
+ */
110
+ declare function register(cytoscapeInstance?: CytoscapeRegistry): void;
111
+
112
+ export { DomNodeRenderer as CytoscapeDomNode, type DomNodeOptions, DomNodeRenderer, type ResizeObserverConstructor, register as default };