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/dist/index.mjs ADDED
@@ -0,0 +1,273 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
+
5
+ // src/index.ts
6
+ var DEFAULT_Z_INDEX = "10";
7
+ var ACTIVE_Z_INDEX = "11";
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"];
11
+ var DomNodeRenderer = class {
12
+ constructor(cy, options = {}) {
13
+ __publicField(this, "cy");
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");
19
+ __publicField(this, "resizeObserver");
20
+ __publicField(this, "container");
21
+ __publicField(this, "handleNodeAdd", (event) => {
22
+ this.addNode(event.target);
23
+ });
24
+ __publicField(this, "handleNodeRemove", (event) => {
25
+ this.removeNode(event.target);
26
+ });
27
+ __publicField(this, "handleNodePosition", (event) => {
28
+ this.syncNodePosition(event.target);
29
+ });
30
+ __publicField(this, "handleNodeState", (event) => {
31
+ this.syncNodeState(event.target);
32
+ });
33
+ __publicField(this, "handleViewport", () => {
34
+ this.syncViewport();
35
+ });
36
+ __publicField(this, "stopInteractiveEvent", (event) => {
37
+ event.stopPropagation();
38
+ });
39
+ var _a, _b;
40
+ this.cy = cy;
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;
44
+ this.resizeObserver = new (resolveResizeObserver(options))((entries) => {
45
+ for (const entry of entries) {
46
+ this.syncNodeSize(entry.target);
47
+ }
48
+ });
49
+ this.bindEvents();
50
+ cy.nodes().forEach((node) => {
51
+ this.addNode(node);
52
+ });
53
+ this.syncViewport();
54
+ }
55
+ /**
56
+ * Return the DOM element that backs a Cytoscape node id.
57
+ */
58
+ nodeDom(id) {
59
+ return this.nodeElements.get(id);
60
+ }
61
+ /**
62
+ * @deprecated Use {@link nodeDom}.
63
+ */
64
+ node_dom(id) {
65
+ return this.nodeDom(id);
66
+ }
67
+ /**
68
+ * Remove event handlers and disconnect resize observation.
69
+ */
70
+ destroy() {
71
+ this.cy.off("add", "node", this.handleNodeAdd);
72
+ this.cy.off("remove", "node", this.handleNodeRemove);
73
+ this.cy.off("pan zoom", this.handleViewport);
74
+ this.cy.off("position bounds", "node", this.handleNodePosition);
75
+ this.cy.off("select unselect grab free", "node", this.handleNodeState);
76
+ this.resizeObserver.disconnect();
77
+ this.nodeElements.clear();
78
+ this.appendedNodeIds.clear();
79
+ this.clearInteractiveElements();
80
+ }
81
+ bindEvents() {
82
+ this.cy.on("add", "node", this.handleNodeAdd);
83
+ this.cy.on("remove", "node", this.handleNodeRemove);
84
+ this.cy.on("pan zoom", this.handleViewport);
85
+ this.cy.on("position bounds", "node", this.handleNodePosition);
86
+ this.cy.on("select unselect grab free", "node", this.handleNodeState);
87
+ }
88
+ addNode(node) {
89
+ const data = node.data();
90
+ const element = data.dom;
91
+ if (!element) {
92
+ return;
93
+ }
94
+ const nodeId = node.id();
95
+ const shouldAppend = data.skipNodeAppend !== true && data.skip_node_append !== true;
96
+ if (shouldAppend && element.parentNode !== this.container) {
97
+ this.container.appendChild(element);
98
+ }
99
+ element.__cy_id = nodeId;
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);
107
+ this.resizeObserver.observe(element);
108
+ this.syncNodeSize(element);
109
+ this.syncNodePosition(node);
110
+ this.syncNodeState(node);
111
+ }
112
+ removeNode(node) {
113
+ var _a;
114
+ const nodeId = node.id();
115
+ const element = this.nodeElements.get(nodeId);
116
+ if (!element) {
117
+ return;
118
+ }
119
+ this.resizeObserver.unobserve(element);
120
+ delete element.__cy_id;
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
+ }
126
+ }
127
+ syncViewport() {
128
+ const pan = this.cy.pan();
129
+ const zoom = this.cy.zoom();
130
+ const transform = `translate(${String(pan.x)}px, ${String(
131
+ pan.y
132
+ )}px) scale(${String(zoom)})`;
133
+ setMsTransform(this.container, transform);
134
+ this.container.style.transform = transform;
135
+ }
136
+ syncNodeSize(element) {
137
+ if (!element.__cy_id) {
138
+ return;
139
+ }
140
+ const node = this.cy.getElementById(element.__cy_id);
141
+ if (node.empty()) {
142
+ return;
143
+ }
144
+ node.style({
145
+ height: element.offsetHeight,
146
+ shape: "rectangle",
147
+ width: element.offsetWidth
148
+ });
149
+ }
150
+ syncNodePosition(node) {
151
+ const element = this.nodeElements.get(node.id());
152
+ if (!element) {
153
+ return;
154
+ }
155
+ const position = node.position();
156
+ const transform = `translate(-50%, -50%) translate(${position.x.toFixed(
157
+ 2
158
+ )}px, ${position.y.toFixed(2)}px)`;
159
+ element.style.webkitTransform = transform;
160
+ setMsTransform(element, transform);
161
+ element.style.transform = transform;
162
+ element.style.display = "inline";
163
+ element.style.position = "absolute";
164
+ }
165
+ syncNodeState(node) {
166
+ const element = this.nodeElements.get(node.id());
167
+ if (!element) {
168
+ return;
169
+ }
170
+ const isSelected = node.selected();
171
+ const isActive = isSelected || node.grabbed();
172
+ element.classList.toggle(SELECTED_CLASS_NAME, isSelected);
173
+ element.style.zIndex = isActive ? ACTIVE_Z_INDEX : DEFAULT_Z_INDEX;
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
+ }
218
+ };
219
+ function register(cytoscapeInstance) {
220
+ if (!cytoscapeInstance) {
221
+ return;
222
+ }
223
+ cytoscapeInstance(
224
+ "core",
225
+ "domNode",
226
+ function domNode(options) {
227
+ return new DomNodeRenderer(this, options);
228
+ }
229
+ );
230
+ }
231
+ function resolveDomContainer(cy, options) {
232
+ var _a;
233
+ const providedContainer = (_a = options.domContainer) != null ? _a : options.dom_container;
234
+ if (providedContainer) {
235
+ return providedContainer;
236
+ }
237
+ const cyContainer = cy.container();
238
+ const canvas = cyContainer == null ? void 0 : cyContainer.querySelector("canvas");
239
+ if (!(canvas == null ? void 0 : canvas.parentNode)) {
240
+ throw new Error(
241
+ "cytoscape-dom-node requires a Cytoscape container with a canvas, or a domContainer option."
242
+ );
243
+ }
244
+ const container = document.createElement("div");
245
+ container.style.position = "absolute";
246
+ container.style.transformOrigin = "0 0";
247
+ container.style.zIndex = DEFAULT_Z_INDEX;
248
+ canvas.parentNode.appendChild(container);
249
+ return container;
250
+ }
251
+ function resolveResizeObserver(options) {
252
+ var _a;
253
+ const ResizeObserverClass = (_a = options.resizeObserver) != null ? _a : globalThis.ResizeObserver;
254
+ if (!ResizeObserverClass) {
255
+ throw new Error(
256
+ "cytoscape-dom-node requires ResizeObserver. Provide a polyfill or pass resizeObserver."
257
+ );
258
+ }
259
+ return ResizeObserverClass;
260
+ }
261
+ function setMsTransform(element, transform) {
262
+ element.style.msTransform = transform;
263
+ }
264
+ var globalCytoscape = globalThis;
265
+ if (globalCytoscape.cytoscape) {
266
+ register(globalCytoscape.cytoscape);
267
+ }
268
+ export {
269
+ DomNodeRenderer as CytoscapeDomNode,
270
+ DomNodeRenderer,
271
+ register as default
272
+ };
273
+ //# sourceMappingURL=index.mjs.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":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cytoscape-dom-node",
3
- "version": "1.3.0",
3
+ "version": "2.1.0",
4
4
  "description": "Cytoscape extension for making nodes into DOM elements",
5
5
  "keywords": [
6
6
  "cytoscape",
@@ -13,10 +13,40 @@
13
13
  "email": "mjw@methodanalysis.com"
14
14
  },
15
15
  "license": "MIT",
16
- "main": "src/index.js",
16
+ "main": "./dist/index.cjs",
17
+ "module": "./dist/index.mjs",
18
+ "types": "./dist/index.d.ts",
19
+ "browser": "./dist/index.global.js",
20
+ "unpkg": "./dist/index.global.js",
21
+ "jsdelivr": "./dist/index.global.js",
22
+ "exports": {
23
+ ".": {
24
+ "types": "./dist/index.d.ts",
25
+ "import": "./dist/index.mjs",
26
+ "require": "./dist/index.cjs"
27
+ },
28
+ "./src/index.js": "./src/index.js",
29
+ "./package.json": "./package.json"
30
+ },
31
+ "files": [
32
+ "dist",
33
+ "src/index.js",
34
+ "LICENSE",
35
+ "README.md"
36
+ ],
17
37
  "url": "https://github.com/mwri/cytoscape-dom-node",
18
38
  "scripts": {
19
- "prepublishOnly": "./node_modules/grunt-cli/bin/grunt prepublish"
39
+ "build": "tsup",
40
+ "build:demo:modern": "tsc -p demos/modern/tsconfig.json",
41
+ "check": "npm run lint && npm run typecheck && npm run test:coverage && npm run build && npm run build:demo:modern && npm run docs",
42
+ "docs": "typedoc",
43
+ "format": "prettier --write .",
44
+ "format:check": "prettier --check .",
45
+ "lint": "eslint .",
46
+ "prepublishOnly": "npm run check",
47
+ "test": "vitest run",
48
+ "test:coverage": "vitest run --coverage",
49
+ "typecheck": "tsc --noEmit"
20
50
  },
21
51
  "repository": {
22
52
  "type": "git",
@@ -30,9 +60,17 @@
30
60
  "cytoscape": "^3.19.0"
31
61
  },
32
62
  "devDependencies": {
33
- "grunt": "^1.5.3",
34
- "grunt-cli": "^1.4.3",
35
- "grunt-git": "^1.1.1",
36
- "load-grunt-tasks": "^5.1.0"
63
+ "@eslint/js": "^10.0.1",
64
+ "@types/node": "^25.6.2",
65
+ "@vitest/coverage-v8": "^4.1.5",
66
+ "cytoscape": "^3.33.3",
67
+ "eslint": "^10.3.0",
68
+ "jsdom": "^29.1.1",
69
+ "prettier": "^3.8.3",
70
+ "tsup": "^8.5.1",
71
+ "typedoc": "^0.28.19",
72
+ "typescript": "^6.0.3",
73
+ "typescript-eslint": "^8.59.2",
74
+ "vitest": "^4.1.5"
37
75
  }
38
76
  }
package/src/index.js CHANGED
@@ -1,150 +1 @@
1
- class CytoscapeDomNode {
2
- constructor (cy, params = {}) {
3
- this._cy = cy;
4
- this._params = params;
5
- this._node_dom = {};
6
-
7
- let cy_container = cy.container();
8
-
9
- if (params.dom_container) {
10
- this._nodes_dom_container = params.dom_container;
11
- } else {
12
- let nodes_dom_container = document.createElement("div");
13
- nodes_dom_container.style.position = 'absolute';
14
- nodes_dom_container.style.zIndex = 10;
15
-
16
- let cy_canvas = cy_container.querySelector("canvas");
17
- cy_canvas.parentNode.appendChild(nodes_dom_container);
18
-
19
- this._nodes_dom_container = nodes_dom_container;
20
- }
21
-
22
- this._resize_observer = new ResizeObserver((entries) => {
23
- for (let e of entries) {
24
- let node_div = e.target;
25
- let id = node_div.__cy_id;
26
- let n = cy.getElementById(id);
27
- n.style({'width': node_div.offsetWidth, 'height': node_div.offsetHeight, shape: 'rectangle'});
28
- }
29
- });
30
-
31
- cy.on('add', 'node', (ev) => {
32
- this._add_node(ev.target);
33
- });
34
-
35
- for (let n of cy.nodes())
36
- this._add_node(n);
37
-
38
- cy.on("pan zoom", (ev) => {
39
- let pan = cy.pan();
40
- let zoom = cy.zoom();
41
-
42
- let transform = "translate(" + pan.x + "px," + pan.y + "px) scale(" + zoom + ")";
43
- this._nodes_dom_container.style.msTransform = transform;
44
- this._nodes_dom_container.style.transform = transform;
45
- });
46
-
47
- cy.on('position bounds', 'node', (ev) => {
48
- let cy_node = ev.target;
49
- let id = cy_node.id();
50
-
51
- if (!this._node_dom[id])
52
- return;
53
-
54
- let dom = this._node_dom[id];
55
-
56
- let style_transform = `translate(-50%, -50%) translate(${cy_node.position('x').toFixed(2)}px, ${cy_node.position('y').toFixed(2)}px)`;
57
- dom.style.webkitTransform = style_transform;
58
- dom.style.msTransform = style_transform;
59
- dom.style.transform = style_transform;
60
-
61
- dom.style.display = 'inline';
62
- dom.style.position = 'absolute';
63
- });
64
-
65
- cy.on('select', 'node', (ev) => {
66
- let cy_node = ev.target;
67
- let id = cy_node.id();
68
-
69
- if (!this._node_dom[id])
70
- return;
71
-
72
- let dom = this._node_dom[id];
73
- dom.classList.add("selected");
74
- dom.style['z-index'] = 11;
75
- });
76
-
77
- cy.on('unselect', 'node', (ev) => {
78
- let cy_node = ev.target;
79
- let id = cy_node.id();
80
-
81
- if (!this._node_dom[id])
82
- return;
83
-
84
- let dom = this._node_dom[id];
85
- dom.classList.remove("selected");
86
- dom.style['z-index'] = 10;
87
- });
88
-
89
- cy.on('grab', 'node', (ev) => {
90
- let cy_node = ev.target;
91
- let id = cy_node.id();
92
-
93
- if (!this._node_dom[id])
94
- return;
95
-
96
- let dom = this._node_dom[id];
97
- dom.style['z-index'] = 11;
98
- });
99
-
100
- cy.on('free', 'node', (ev) => {
101
- let cy_node = ev.target;
102
- let id = cy_node.id();
103
-
104
- if (!this._node_dom[id])
105
- return;
106
-
107
- let dom = this._node_dom[id];
108
- if(!dom.classList.contains("selected"))
109
- dom.style['z-index'] = 10;
110
- });
111
- }
112
-
113
- _add_node (n) {
114
- let data = n.data();
115
-
116
- if (!data.dom)
117
- return;
118
-
119
- if (data.skip_node_append !== true) {
120
- this._nodes_dom_container.appendChild(data.dom);
121
- }
122
- data.dom.__cy_id = n.id();
123
-
124
- this._node_dom[n.id()] = data.dom;
125
-
126
- this._resize_observer.observe(data.dom);
127
- }
128
-
129
- node_dom (id) {
130
- return this._node_dom[id];
131
- }
132
- }
133
-
134
-
135
- function register (cy) {
136
- if (!cy)
137
- return;
138
-
139
- cy('core', 'domNode', function (params, opts) {
140
- return new CytoscapeDomNode(this, params, opts);
141
- });
142
- }
143
-
144
-
145
- if (typeof(cytoscape) !== 'undefined') {
146
- register(cytoscape);
147
- }
148
-
149
-
150
- module.exports = register;
1
+ module.exports = require("../dist/index.cjs");
package/demo/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- The MIT License (MIT)
2
-
3
- Copyright (C) 2021 Michael Wright <mjw@methodanalysis.com>
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
package/demo/Makefile DELETED
@@ -1,18 +0,0 @@
1
- all: bundle
2
-
3
- .PHONY: bundle
4
- bundle: node_modules/.bin/webpack-dev-server
5
- nodejs ./node_modules/.bin/webpack
6
-
7
- .PHONY: webpack-dev-server
8
- webpack-dev-server: node_modules/.bin/webpack-dev-server
9
- NODE_ENV=development nodejs ./node_modules/.bin/webpack-dev-server --debug
10
-
11
- node_modules/.bin/webpack-dev-server: node_modules
12
-
13
- package-lock.json: package.json
14
- npm install
15
-
16
- .PHONY: node_modules
17
- node_modules: package.json package-lock.json
18
- npm install