cytoscape-dom-node 1.3.0 → 2.0.1

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,141 @@ 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.0.0 - v2.0.1 is a typescript conversion, and SHOULD be backwards compatible with v1.
14
+ v1.3.0 is the latest pre typescript incarnation.
15
+
16
+ ## Dependencies
17
+
18
+ - cytoscape ^3.19.0
13
19
 
14
20
  ## Extension registration
15
21
 
16
- Either import/require `cytoscape-dom-node`, and register it as an extension with Cytoscape:
22
+ Import `cytoscape-dom-node`, and register it as an extension with Cytoscape:
23
+
24
+ ```ts
25
+ import cytoscape from "cytoscape";
26
+ import cytoscapeDomNode from "cytoscape-dom-node";
27
+
28
+ cytoscape.use(cytoscapeDomNode);
29
+ ```
30
+
31
+ CommonJS is still supported:
17
32
 
18
33
  ```js
19
- const cytoscape = require('cytoscape');
20
- cytoscape.use(require('cytoscape-dom-node'));
34
+ const cytoscape = require("cytoscape");
35
+ const cytoscapeDomNode = require("cytoscape-dom-node");
36
+
37
+ cytoscape.use(cytoscapeDomNode);
21
38
  ```
22
39
 
23
40
  Or it can be included via a `<script>` tag after `cytoscape`, and will register itself:
24
41
 
25
42
  ```html
26
43
  <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>
44
+ <script
45
+ type="text/javascript"
46
+ charset="utf8"
47
+ src="path/to/cytoscape-dom-node.global.js"
48
+ ></script>
28
49
  ```
29
50
 
30
51
  ## Usage instructions
31
52
 
32
53
  Create a `cytoscape` instance and call `domNode` on it:
33
54
 
34
- ```js
35
- let cy = cytoscape({
36
- 'container': document.getElementById('id-of-my-cytoscape-container'),
37
- 'elements': [],
55
+ ```ts
56
+ const cy = cytoscape({
57
+ container: document.getElementById("id-of-my-cytoscape-container"),
58
+ elements: [],
38
59
  });
39
60
 
40
- cy.domNode();
61
+ const domNodeRenderer = cy.domNode();
41
62
  ```
42
63
 
43
64
  Now add a node with `dom` in the data, set to a DOM element:
44
65
 
45
- ```js
46
- let div = document.createElement("div");
66
+ ```ts
67
+ const div = document.createElement("div");
47
68
  div.innerHTML = `node ${id}`;
48
69
 
49
70
  cy.add({
50
- 'data': {
51
- 'id': id,
52
- 'dom': div,
53
- },
71
+ data: {
72
+ dom: div,
73
+ id,
74
+ },
54
75
  });
55
76
  ```
56
77
 
57
78
  The `div` you created will be shown as the node now.
58
79
 
80
+ You can retrieve the DOM element for a Cytoscape node id:
81
+
82
+ ```ts
83
+ const nodeElement = domNodeRenderer.nodeDom(id);
84
+ ```
85
+
86
+ The previous `node_dom(id)` method is kept as a backwards compatible alias.
87
+
59
88
  See [codepen abWdVOG](https://codepen.io/mwri/pen/abWdVOG) for a working
60
89
  example.
61
90
 
62
91
  ### 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
92
 
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.
93
+ The `skipNodeAppend` node data option controls whether `cytoscape-dom-node`
94
+ appends the provided DOM node to the configured DOM container. By default,
95
+ `cytoscape-dom-node` appends the node to the container.
68
96
 
69
- ```js
70
- let div = document.querySelector("#alreadyRenderedNodeId");
97
+ In certain scenarios, such as when using EmberJS or another front-end framework,
98
+ you might have already rendered the nodes to the DOM. In these cases, set
99
+ `skipNodeAppend` to `true` to keep control over rendering.
100
+
101
+ ```ts
102
+ const div = document.querySelector("#alreadyRenderedNodeId");
71
103
  cy.add({
72
- 'data': {
73
- 'id': id,
74
- 'dom': div,
75
- 'skip_node_append': true,
76
- }
77
- })
104
+ data: {
105
+ dom: div,
106
+ id,
107
+ skipNodeAppend: true,
108
+ },
109
+ });
78
110
  ```
79
111
 
112
+ The legacy `skip_node_append` name remains supported.
113
+
80
114
  ## Options
81
115
 
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:
116
+ `domContainer` allows a container element to be specified. It will be used for
117
+ nodes instead of the element `cytoscape-dom-node` would otherwise create. It is
118
+ the caller's responsibility to style the given element appropriately:
85
119
 
86
- ```js
87
- cy.domNode({'dom_container': some_element});
120
+ ```ts
121
+ cy.domNode({ domContainer: someElement });
122
+ ```
123
+
124
+ The legacy `dom_container` name remains supported.
125
+
126
+ ## Interaction State
127
+
128
+ DOM nodes mirror Cytoscape selection state with the `selected` class. Selected
129
+ or grabbed nodes receive z-index `11`; other DOM nodes receive z-index `10`.
130
+
131
+ ## Development
132
+
133
+ This package is authored in TypeScript and emits CommonJS, ESM, browser global,
134
+ and declaration outputs into `dist`.
135
+
136
+ Useful commands:
137
+
138
+ ```sh
139
+ npm run lint
140
+ npm run typecheck
141
+ npm run test
142
+ npm run test:coverage
143
+ npm run build
144
+ npm run docs
145
+ npm run check
88
146
  ```
package/dist/index.cjs ADDED
@@ -0,0 +1,229 @@
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 DomNodeRenderer = class {
34
+ constructor(cy, options = {}) {
35
+ __publicField(this, "cy");
36
+ __publicField(this, "nodeElements", /* @__PURE__ */ new Map());
37
+ __publicField(this, "resizeObserver");
38
+ __publicField(this, "container");
39
+ __publicField(this, "handleNodeAdd", (event) => {
40
+ this.addNode(event.target);
41
+ });
42
+ __publicField(this, "handleNodeRemove", (event) => {
43
+ this.removeNode(event.target);
44
+ });
45
+ __publicField(this, "handleNodePosition", (event) => {
46
+ this.syncNodePosition(event.target);
47
+ });
48
+ __publicField(this, "handleNodeState", (event) => {
49
+ this.syncNodeState(event.target);
50
+ });
51
+ __publicField(this, "handleViewport", () => {
52
+ this.syncViewport();
53
+ });
54
+ this.cy = cy;
55
+ this.container = resolveDomContainer(cy, options);
56
+ this.resizeObserver = new (resolveResizeObserver(options))((entries) => {
57
+ for (const entry of entries) {
58
+ this.syncNodeSize(entry.target);
59
+ }
60
+ });
61
+ this.bindEvents();
62
+ cy.nodes().forEach((node) => {
63
+ this.addNode(node);
64
+ });
65
+ this.syncViewport();
66
+ }
67
+ /**
68
+ * Return the DOM element that backs a Cytoscape node id.
69
+ */
70
+ nodeDom(id) {
71
+ return this.nodeElements.get(id);
72
+ }
73
+ /**
74
+ * @deprecated Use {@link nodeDom}.
75
+ */
76
+ node_dom(id) {
77
+ return this.nodeDom(id);
78
+ }
79
+ /**
80
+ * Remove event handlers and disconnect resize observation.
81
+ */
82
+ destroy() {
83
+ this.cy.off("add", "node", this.handleNodeAdd);
84
+ this.cy.off("remove", "node", this.handleNodeRemove);
85
+ this.cy.off("pan zoom", this.handleViewport);
86
+ this.cy.off("position bounds", "node", this.handleNodePosition);
87
+ this.cy.off("select unselect grab free", "node", this.handleNodeState);
88
+ this.resizeObserver.disconnect();
89
+ this.nodeElements.clear();
90
+ }
91
+ bindEvents() {
92
+ this.cy.on("add", "node", this.handleNodeAdd);
93
+ this.cy.on("remove", "node", this.handleNodeRemove);
94
+ this.cy.on("pan zoom", this.handleViewport);
95
+ this.cy.on("position bounds", "node", this.handleNodePosition);
96
+ this.cy.on("select unselect grab free", "node", this.handleNodeState);
97
+ }
98
+ addNode(node) {
99
+ const data = node.data();
100
+ const element = data.dom;
101
+ if (!element) {
102
+ return;
103
+ }
104
+ const nodeId = node.id();
105
+ const shouldAppend = data.skipNodeAppend !== true && data.skip_node_append !== true;
106
+ if (shouldAppend && element.parentNode !== this.container) {
107
+ this.container.appendChild(element);
108
+ }
109
+ element.__cy_id = nodeId;
110
+ this.nodeElements.set(nodeId, element);
111
+ this.resizeObserver.observe(element);
112
+ this.syncNodeSize(element);
113
+ this.syncNodePosition(node);
114
+ this.syncNodeState(node);
115
+ }
116
+ removeNode(node) {
117
+ const element = this.nodeElements.get(node.id());
118
+ if (!element) {
119
+ return;
120
+ }
121
+ this.resizeObserver.unobserve(element);
122
+ delete element.__cy_id;
123
+ this.nodeElements.delete(node.id());
124
+ }
125
+ syncViewport() {
126
+ const pan = this.cy.pan();
127
+ const zoom = this.cy.zoom();
128
+ const transform = `translate(${String(pan.x)}px, ${String(
129
+ pan.y
130
+ )}px) scale(${String(zoom)})`;
131
+ setMsTransform(this.container, transform);
132
+ this.container.style.transform = transform;
133
+ }
134
+ syncNodeSize(element) {
135
+ if (!element.__cy_id) {
136
+ return;
137
+ }
138
+ const node = this.cy.getElementById(element.__cy_id);
139
+ if (node.empty()) {
140
+ return;
141
+ }
142
+ node.style({
143
+ height: element.offsetHeight,
144
+ shape: "rectangle",
145
+ width: element.offsetWidth
146
+ });
147
+ }
148
+ syncNodePosition(node) {
149
+ const element = this.nodeElements.get(node.id());
150
+ if (!element) {
151
+ return;
152
+ }
153
+ const position = node.position();
154
+ const transform = `translate(-50%, -50%) translate(${position.x.toFixed(
155
+ 2
156
+ )}px, ${position.y.toFixed(2)}px)`;
157
+ element.style.webkitTransform = transform;
158
+ setMsTransform(element, transform);
159
+ element.style.transform = transform;
160
+ element.style.display = "inline";
161
+ element.style.position = "absolute";
162
+ }
163
+ syncNodeState(node) {
164
+ const element = this.nodeElements.get(node.id());
165
+ if (!element) {
166
+ return;
167
+ }
168
+ const isSelected = node.selected();
169
+ const isActive = isSelected || node.grabbed();
170
+ element.classList.toggle(SELECTED_CLASS_NAME, isSelected);
171
+ element.style.zIndex = isActive ? ACTIVE_Z_INDEX : DEFAULT_Z_INDEX;
172
+ }
173
+ };
174
+ function register(cytoscapeInstance) {
175
+ if (!cytoscapeInstance) {
176
+ return;
177
+ }
178
+ cytoscapeInstance(
179
+ "core",
180
+ "domNode",
181
+ function domNode(options) {
182
+ return new DomNodeRenderer(this, options);
183
+ }
184
+ );
185
+ }
186
+ function resolveDomContainer(cy, options) {
187
+ var _a;
188
+ const providedContainer = (_a = options.domContainer) != null ? _a : options.dom_container;
189
+ if (providedContainer) {
190
+ return providedContainer;
191
+ }
192
+ const cyContainer = cy.container();
193
+ const canvas = cyContainer == null ? void 0 : cyContainer.querySelector("canvas");
194
+ if (!(canvas == null ? void 0 : canvas.parentNode)) {
195
+ throw new Error(
196
+ "cytoscape-dom-node requires a Cytoscape container with a canvas, or a domContainer option."
197
+ );
198
+ }
199
+ const container = document.createElement("div");
200
+ container.style.position = "absolute";
201
+ container.style.transformOrigin = "0 0";
202
+ container.style.zIndex = DEFAULT_Z_INDEX;
203
+ canvas.parentNode.appendChild(container);
204
+ return container;
205
+ }
206
+ function resolveResizeObserver(options) {
207
+ var _a;
208
+ const ResizeObserverClass = (_a = options.resizeObserver) != null ? _a : globalThis.ResizeObserver;
209
+ if (!ResizeObserverClass) {
210
+ throw new Error(
211
+ "cytoscape-dom-node requires ResizeObserver. Provide a polyfill or pass resizeObserver."
212
+ );
213
+ }
214
+ return ResizeObserverClass;
215
+ }
216
+ function setMsTransform(element, transform) {
217
+ element.style.msTransform = transform;
218
+ }
219
+ var globalCytoscape = globalThis;
220
+ if (globalCytoscape.cytoscape) {
221
+ register(globalCytoscape.cytoscape);
222
+ }
223
+ // Annotate the CommonJS export names for ESM import in node:
224
+ 0 && (module.exports = {
225
+ CytoscapeDomNode,
226
+ DomNodeRenderer
227
+ });
228
+ module.exports = Object.assign(module.exports.default, module.exports);
229
+ //# 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\";\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":[]}
@@ -0,0 +1,94 @@
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
+ declare global {
34
+ namespace cytoscape {
35
+ interface Core {
36
+ /**
37
+ * Enable DOM-backed Cytoscape nodes for this core instance.
38
+ */
39
+ domNode(options?: DomNodeOptions): DomNodeRenderer;
40
+ }
41
+ }
42
+ }
43
+ declare module "cytoscape" {
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
+ /**
54
+ * Keeps Cytoscape node positions, dimensions, and interaction state mirrored
55
+ * onto caller-supplied DOM elements.
56
+ */
57
+ declare class DomNodeRenderer {
58
+ private readonly cy;
59
+ private readonly nodeElements;
60
+ private readonly resizeObserver;
61
+ private readonly container;
62
+ private readonly handleNodeAdd;
63
+ private readonly handleNodeRemove;
64
+ private readonly handleNodePosition;
65
+ private readonly handleNodeState;
66
+ private readonly handleViewport;
67
+ constructor(cy: cytoscape.Core, options?: DomNodeOptions);
68
+ /**
69
+ * Return the DOM element that backs a Cytoscape node id.
70
+ */
71
+ nodeDom(id: string): HTMLElement | undefined;
72
+ /**
73
+ * @deprecated Use {@link nodeDom}.
74
+ */
75
+ node_dom(id: string): HTMLElement | undefined;
76
+ /**
77
+ * Remove event handlers and disconnect resize observation.
78
+ */
79
+ destroy(): void;
80
+ private bindEvents;
81
+ private addNode;
82
+ private removeNode;
83
+ private syncViewport;
84
+ private syncNodeSize;
85
+ private syncNodePosition;
86
+ private syncNodeState;
87
+ }
88
+
89
+ /**
90
+ * Register the extension with Cytoscape.
91
+ */
92
+ declare function register(cytoscapeInstance?: CytoscapeRegistry): void;
93
+
94
+ export { DomNodeRenderer as CytoscapeDomNode, type DomNodeOptions, DomNodeRenderer, type ResizeObserverConstructor, register as default };
@@ -0,0 +1,94 @@
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
+ declare global {
34
+ namespace cytoscape {
35
+ interface Core {
36
+ /**
37
+ * Enable DOM-backed Cytoscape nodes for this core instance.
38
+ */
39
+ domNode(options?: DomNodeOptions): DomNodeRenderer;
40
+ }
41
+ }
42
+ }
43
+ declare module "cytoscape" {
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
+ /**
54
+ * Keeps Cytoscape node positions, dimensions, and interaction state mirrored
55
+ * onto caller-supplied DOM elements.
56
+ */
57
+ declare class DomNodeRenderer {
58
+ private readonly cy;
59
+ private readonly nodeElements;
60
+ private readonly resizeObserver;
61
+ private readonly container;
62
+ private readonly handleNodeAdd;
63
+ private readonly handleNodeRemove;
64
+ private readonly handleNodePosition;
65
+ private readonly handleNodeState;
66
+ private readonly handleViewport;
67
+ constructor(cy: cytoscape.Core, options?: DomNodeOptions);
68
+ /**
69
+ * Return the DOM element that backs a Cytoscape node id.
70
+ */
71
+ nodeDom(id: string): HTMLElement | undefined;
72
+ /**
73
+ * @deprecated Use {@link nodeDom}.
74
+ */
75
+ node_dom(id: string): HTMLElement | undefined;
76
+ /**
77
+ * Remove event handlers and disconnect resize observation.
78
+ */
79
+ destroy(): void;
80
+ private bindEvents;
81
+ private addNode;
82
+ private removeNode;
83
+ private syncViewport;
84
+ private syncNodeSize;
85
+ private syncNodePosition;
86
+ private syncNodeState;
87
+ }
88
+
89
+ /**
90
+ * Register the extension with Cytoscape.
91
+ */
92
+ declare function register(cytoscapeInstance?: CytoscapeRegistry): void;
93
+
94
+ export { DomNodeRenderer as CytoscapeDomNode, type DomNodeOptions, DomNodeRenderer, type ResizeObserverConstructor, register as default };