cytoscape-dom-node 2.0.1 → 2.1.0

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