polly-graph 0.1.10 → 0.1.11
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 +4 -0
- package/dist/index.cjs +287 -46
- package/dist/index.js +287 -46
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# polly-graph
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/polly-graph)
|
|
4
|
+
[](http://www.typescriptlang.org/)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
3
7
|
A framework-independent TypeScript-based D3 graph visualization SDK that provides a comprehensive, reusable solution for creating interactive network graphs. Designed to work seamlessly across React, Angular, Vue, Svelte, or vanilla JavaScript applications.
|
|
4
8
|
|
|
5
9
|
## Features
|
package/dist/index.cjs
CHANGED
|
@@ -5107,6 +5107,258 @@ function createDragBehavior(simulation, onDragStart, canvasBounds) {
|
|
|
5107
5107
|
});
|
|
5108
5108
|
}
|
|
5109
5109
|
|
|
5110
|
+
// src/utils/node-style-manager.ts
|
|
5111
|
+
var NodeStyleManager = class {
|
|
5112
|
+
styleStates = /* @__PURE__ */ new Map();
|
|
5113
|
+
/**
|
|
5114
|
+
* Initialize a node's style state by capturing its original styles
|
|
5115
|
+
*/
|
|
5116
|
+
initializeNode(element, node) {
|
|
5117
|
+
if (this.styleStates.has(element)) {
|
|
5118
|
+
return;
|
|
5119
|
+
}
|
|
5120
|
+
const domBackup = {
|
|
5121
|
+
stroke: element.getAttribute("stroke"),
|
|
5122
|
+
strokeWidth: element.getAttribute("stroke-width"),
|
|
5123
|
+
opacity: element.getAttribute("opacity"),
|
|
5124
|
+
fill: element.getAttribute("fill")
|
|
5125
|
+
};
|
|
5126
|
+
const originalStyle = {
|
|
5127
|
+
stroke: node.style?.stroke || void 0,
|
|
5128
|
+
strokeWidth: node.style?.strokeWidth || void 0,
|
|
5129
|
+
opacity: node.style?.opacity || void 0,
|
|
5130
|
+
fill: node.style?.fill || void 0
|
|
5131
|
+
};
|
|
5132
|
+
this.styleStates.set(element, {
|
|
5133
|
+
original: originalStyle,
|
|
5134
|
+
current: { ...originalStyle },
|
|
5135
|
+
domBackup
|
|
5136
|
+
});
|
|
5137
|
+
}
|
|
5138
|
+
/**
|
|
5139
|
+
* Apply temporary styles (e.g., hover effects) that will be reset later
|
|
5140
|
+
*/
|
|
5141
|
+
applyTemporaryStyles(element, styles) {
|
|
5142
|
+
this.applyStylesToDOM(element, styles);
|
|
5143
|
+
}
|
|
5144
|
+
/**
|
|
5145
|
+
* Apply permanent styles that become the new base styles
|
|
5146
|
+
*/
|
|
5147
|
+
applyPermanentStyles(element, styles) {
|
|
5148
|
+
const state = this.styleStates.get(element);
|
|
5149
|
+
if (!state) {
|
|
5150
|
+
console.warn("[NodeStyleManager] Node not initialized, cannot apply permanent styles");
|
|
5151
|
+
return;
|
|
5152
|
+
}
|
|
5153
|
+
this.applyStylesToDOM(element, styles);
|
|
5154
|
+
Object.assign(state.current, styles);
|
|
5155
|
+
}
|
|
5156
|
+
/**
|
|
5157
|
+
* Reset node to its current base styles (removes temporary styles)
|
|
5158
|
+
*/
|
|
5159
|
+
resetToBase(element) {
|
|
5160
|
+
const state = this.styleStates.get(element);
|
|
5161
|
+
if (!state) {
|
|
5162
|
+
this.clearAllStyles(element);
|
|
5163
|
+
return;
|
|
5164
|
+
}
|
|
5165
|
+
this.clearAllStyles(element);
|
|
5166
|
+
this.applyStylesToDOM(element, state.current);
|
|
5167
|
+
}
|
|
5168
|
+
/**
|
|
5169
|
+
* Reset node to its original styles (as captured during initialization)
|
|
5170
|
+
*/
|
|
5171
|
+
resetToOriginal(element) {
|
|
5172
|
+
const state = this.styleStates.get(element);
|
|
5173
|
+
if (!state) {
|
|
5174
|
+
this.clearAllStyles(element);
|
|
5175
|
+
return;
|
|
5176
|
+
}
|
|
5177
|
+
this.clearAllStyles(element);
|
|
5178
|
+
this.restoreOriginalDOM(element, state.domBackup);
|
|
5179
|
+
state.current = { ...state.original };
|
|
5180
|
+
}
|
|
5181
|
+
/**
|
|
5182
|
+
* Check if node is in a specific state (selected, hovered, etc.)
|
|
5183
|
+
*/
|
|
5184
|
+
hasState(element, stateName) {
|
|
5185
|
+
return element.dataset[stateName] === "true";
|
|
5186
|
+
}
|
|
5187
|
+
/**
|
|
5188
|
+
* Set state marker on node
|
|
5189
|
+
*/
|
|
5190
|
+
setState(element, stateName, value) {
|
|
5191
|
+
if (value) {
|
|
5192
|
+
element.dataset[stateName] = "true";
|
|
5193
|
+
} else {
|
|
5194
|
+
delete element.dataset[stateName];
|
|
5195
|
+
}
|
|
5196
|
+
}
|
|
5197
|
+
/**
|
|
5198
|
+
* Get the original styles for a node
|
|
5199
|
+
*/
|
|
5200
|
+
getOriginalStyles(element) {
|
|
5201
|
+
const state = this.styleStates.get(element);
|
|
5202
|
+
return state ? { ...state.original } : null;
|
|
5203
|
+
}
|
|
5204
|
+
/**
|
|
5205
|
+
* Get the current base styles for a node
|
|
5206
|
+
*/
|
|
5207
|
+
getCurrentStyles(element) {
|
|
5208
|
+
const state = this.styleStates.get(element);
|
|
5209
|
+
return state ? { ...state.current } : null;
|
|
5210
|
+
}
|
|
5211
|
+
/**
|
|
5212
|
+
* Remove a node from management (cleanup)
|
|
5213
|
+
*/
|
|
5214
|
+
removeNode(element) {
|
|
5215
|
+
this.styleStates.delete(element);
|
|
5216
|
+
}
|
|
5217
|
+
/**
|
|
5218
|
+
* Clear all managed nodes (for cleanup)
|
|
5219
|
+
*/
|
|
5220
|
+
clear() {
|
|
5221
|
+
this.styleStates.clear();
|
|
5222
|
+
}
|
|
5223
|
+
/**
|
|
5224
|
+
* Private: Apply styles to DOM element
|
|
5225
|
+
*/
|
|
5226
|
+
applyStylesToDOM(element, styles) {
|
|
5227
|
+
if (styles.stroke !== void 0) {
|
|
5228
|
+
if (styles.stroke === null || styles.stroke === "") {
|
|
5229
|
+
element.removeAttribute("stroke");
|
|
5230
|
+
element.style.stroke = "";
|
|
5231
|
+
} else {
|
|
5232
|
+
element.style.stroke = styles.stroke;
|
|
5233
|
+
}
|
|
5234
|
+
}
|
|
5235
|
+
if (styles.strokeWidth !== void 0) {
|
|
5236
|
+
if (styles.strokeWidth === null || styles.strokeWidth === 0) {
|
|
5237
|
+
element.removeAttribute("stroke-width");
|
|
5238
|
+
element.style.strokeWidth = "";
|
|
5239
|
+
} else {
|
|
5240
|
+
element.style.strokeWidth = String(styles.strokeWidth);
|
|
5241
|
+
}
|
|
5242
|
+
}
|
|
5243
|
+
if (styles.opacity !== void 0) {
|
|
5244
|
+
if (styles.opacity === null || styles.opacity === 1) {
|
|
5245
|
+
element.removeAttribute("opacity");
|
|
5246
|
+
element.style.opacity = "";
|
|
5247
|
+
} else {
|
|
5248
|
+
element.style.opacity = String(styles.opacity);
|
|
5249
|
+
}
|
|
5250
|
+
}
|
|
5251
|
+
if (styles.fill !== void 0) {
|
|
5252
|
+
if (styles.fill === null || styles.fill === "") {
|
|
5253
|
+
element.removeAttribute("fill");
|
|
5254
|
+
element.style.fill = "";
|
|
5255
|
+
} else {
|
|
5256
|
+
element.style.fill = styles.fill;
|
|
5257
|
+
}
|
|
5258
|
+
}
|
|
5259
|
+
if (styles.radius !== void 0) {
|
|
5260
|
+
if (styles.radius === null || styles.radius === 0) {
|
|
5261
|
+
element.removeAttribute("r");
|
|
5262
|
+
element.style.removeProperty("r");
|
|
5263
|
+
} else {
|
|
5264
|
+
element.setAttribute("r", String(styles.radius));
|
|
5265
|
+
}
|
|
5266
|
+
}
|
|
5267
|
+
}
|
|
5268
|
+
/**
|
|
5269
|
+
* Private: Clear all inline styles and remove hover-related attributes
|
|
5270
|
+
*/
|
|
5271
|
+
clearAllStyles(element) {
|
|
5272
|
+
element.style.stroke = "";
|
|
5273
|
+
element.style.strokeWidth = "";
|
|
5274
|
+
element.style.opacity = "";
|
|
5275
|
+
element.style.fill = "";
|
|
5276
|
+
element.style.removeProperty("r");
|
|
5277
|
+
this.removeIfHoverAttribute(element, "stroke");
|
|
5278
|
+
this.removeIfHoverAttribute(element, "stroke-width");
|
|
5279
|
+
this.removeIfHoverAttribute(element, "opacity");
|
|
5280
|
+
}
|
|
5281
|
+
/**
|
|
5282
|
+
* Private: Restore original DOM attributes
|
|
5283
|
+
*/
|
|
5284
|
+
restoreOriginalDOM(element, domBackup) {
|
|
5285
|
+
if (domBackup.stroke !== void 0) {
|
|
5286
|
+
if (domBackup.stroke === null) {
|
|
5287
|
+
element.removeAttribute("stroke");
|
|
5288
|
+
} else {
|
|
5289
|
+
element.setAttribute("stroke", domBackup.stroke);
|
|
5290
|
+
}
|
|
5291
|
+
}
|
|
5292
|
+
if (domBackup.strokeWidth !== void 0) {
|
|
5293
|
+
if (domBackup.strokeWidth === null) {
|
|
5294
|
+
element.removeAttribute("stroke-width");
|
|
5295
|
+
} else {
|
|
5296
|
+
element.setAttribute("stroke-width", domBackup.strokeWidth);
|
|
5297
|
+
}
|
|
5298
|
+
}
|
|
5299
|
+
if (domBackup.opacity !== void 0) {
|
|
5300
|
+
if (domBackup.opacity === null) {
|
|
5301
|
+
element.removeAttribute("opacity");
|
|
5302
|
+
} else {
|
|
5303
|
+
element.setAttribute("opacity", domBackup.opacity);
|
|
5304
|
+
}
|
|
5305
|
+
}
|
|
5306
|
+
if (domBackup.fill !== void 0) {
|
|
5307
|
+
if (domBackup.fill === null) {
|
|
5308
|
+
element.removeAttribute("fill");
|
|
5309
|
+
} else {
|
|
5310
|
+
element.setAttribute("fill", domBackup.fill);
|
|
5311
|
+
}
|
|
5312
|
+
}
|
|
5313
|
+
}
|
|
5314
|
+
/**
|
|
5315
|
+
* Private: Remove attribute only if it looks like it was set by hover/interaction
|
|
5316
|
+
*/
|
|
5317
|
+
removeIfHoverAttribute(element, attr) {
|
|
5318
|
+
const value = element.getAttribute(attr);
|
|
5319
|
+
if (!value) return;
|
|
5320
|
+
const hoverPatterns = {
|
|
5321
|
+
"stroke": ["#6366f1", "#8b5cf6", "#3b82f6", "#ffffff", "#fff", "white"],
|
|
5322
|
+
// Common hover stroke colors
|
|
5323
|
+
"stroke-width": ["2", "2.5", "3", "4"],
|
|
5324
|
+
// Common hover stroke widths
|
|
5325
|
+
"opacity": ["0.8", "0.9", "0.7"]
|
|
5326
|
+
// Common hover opacity values
|
|
5327
|
+
};
|
|
5328
|
+
const patterns = hoverPatterns[attr];
|
|
5329
|
+
if (patterns && patterns.includes(value)) {
|
|
5330
|
+
element.removeAttribute(attr);
|
|
5331
|
+
}
|
|
5332
|
+
}
|
|
5333
|
+
};
|
|
5334
|
+
var nodeStyleManager = new NodeStyleManager();
|
|
5335
|
+
function applyHoverStyles(element, node, hoverStyle) {
|
|
5336
|
+
if (nodeStyleManager.hasState(element, "selected")) {
|
|
5337
|
+
return;
|
|
5338
|
+
}
|
|
5339
|
+
nodeStyleManager.initializeNode(element, node);
|
|
5340
|
+
nodeStyleManager.applyTemporaryStyles(element, hoverStyle);
|
|
5341
|
+
nodeStyleManager.setState(element, "hovered", true);
|
|
5342
|
+
}
|
|
5343
|
+
function removeHoverStyles(element, node) {
|
|
5344
|
+
if (nodeStyleManager.hasState(element, "selected")) {
|
|
5345
|
+
return;
|
|
5346
|
+
}
|
|
5347
|
+
nodeStyleManager.initializeNode(element, node);
|
|
5348
|
+
nodeStyleManager.resetToBase(element);
|
|
5349
|
+
nodeStyleManager.setState(element, "hovered", false);
|
|
5350
|
+
}
|
|
5351
|
+
function applySelectionStyles(element, node, selectionStyle) {
|
|
5352
|
+
nodeStyleManager.initializeNode(element, node);
|
|
5353
|
+
nodeStyleManager.applyPermanentStyles(element, selectionStyle);
|
|
5354
|
+
nodeStyleManager.setState(element, "selected", true);
|
|
5355
|
+
}
|
|
5356
|
+
function removeSelectionStyles(element, node) {
|
|
5357
|
+
nodeStyleManager.initializeNode(element, node);
|
|
5358
|
+
nodeStyleManager.resetToOriginal(element);
|
|
5359
|
+
nodeStyleManager.setState(element, "selected", false);
|
|
5360
|
+
}
|
|
5361
|
+
|
|
5110
5362
|
// src/interactions/create-node-hover.ts
|
|
5111
5363
|
function createNodeHover(nodeSelection, hoverStyle) {
|
|
5112
5364
|
const firstNode = nodeSelection.node();
|
|
@@ -5114,35 +5366,10 @@ function createNodeHover(nodeSelection, hoverStyle) {
|
|
|
5114
5366
|
if (hoverStyle) {
|
|
5115
5367
|
nodeSelection.on("mouseenter.hover", function(_event, node) {
|
|
5116
5368
|
const circle = this;
|
|
5117
|
-
|
|
5118
|
-
return;
|
|
5119
|
-
}
|
|
5120
|
-
circle.setAttribute("stroke", hoverStyle.stroke ?? node.style?.stroke ?? "#ffffff");
|
|
5121
|
-
circle.setAttribute("stroke-width", String(hoverStyle.strokeWidth ?? node.style?.strokeWidth ?? 1.5));
|
|
5122
|
-
circle.setAttribute("opacity", String(hoverStyle.opacity ?? node.style?.opacity ?? 1));
|
|
5369
|
+
applyHoverStyles(circle, node, hoverStyle);
|
|
5123
5370
|
}).on("mouseleave.hover", function(_event, node) {
|
|
5124
5371
|
const circle = this;
|
|
5125
|
-
|
|
5126
|
-
return;
|
|
5127
|
-
}
|
|
5128
|
-
circle.style.stroke = "";
|
|
5129
|
-
circle.style.strokeWidth = "";
|
|
5130
|
-
circle.style.opacity = "";
|
|
5131
|
-
if (!node.style?.stroke) {
|
|
5132
|
-
circle.setAttribute("stroke", "#ffffff");
|
|
5133
|
-
} else {
|
|
5134
|
-
circle.setAttribute("stroke", node.style.stroke);
|
|
5135
|
-
}
|
|
5136
|
-
if (!node.style?.strokeWidth) {
|
|
5137
|
-
circle.setAttribute("stroke-width", "1.5");
|
|
5138
|
-
} else {
|
|
5139
|
-
circle.setAttribute("stroke-width", String(node.style.strokeWidth));
|
|
5140
|
-
}
|
|
5141
|
-
if (!node.style?.opacity) {
|
|
5142
|
-
circle.setAttribute("opacity", "1");
|
|
5143
|
-
} else {
|
|
5144
|
-
circle.setAttribute("opacity", String(node.style.opacity));
|
|
5145
|
-
}
|
|
5372
|
+
removeHoverStyles(circle, node);
|
|
5146
5373
|
});
|
|
5147
5374
|
}
|
|
5148
5375
|
const svgElement = firstNode.ownerSVGElement;
|
|
@@ -5842,12 +6069,7 @@ var SelectionManager = class {
|
|
|
5842
6069
|
this.clearSelection();
|
|
5843
6070
|
this.bringNodeToFront(nodeElement, nodeData);
|
|
5844
6071
|
if (this.config.nodeStyle) {
|
|
5845
|
-
|
|
5846
|
-
if (style.fill !== void 0) nodeElement.style.fill = style.fill;
|
|
5847
|
-
if (style.stroke !== void 0) nodeElement.style.stroke = style.stroke;
|
|
5848
|
-
if (style.strokeWidth !== void 0) nodeElement.style.strokeWidth = String(style.strokeWidth);
|
|
5849
|
-
if (style.opacity !== void 0) nodeElement.style.opacity = String(style.opacity);
|
|
5850
|
-
if (style.radius !== void 0) nodeElement.style.setProperty("r", String(style.radius));
|
|
6072
|
+
applySelectionStyles(nodeElement, nodeData, this.config.nodeStyle);
|
|
5851
6073
|
}
|
|
5852
6074
|
this.root.selectAll(".link-label").filter((item) => {
|
|
5853
6075
|
if (item.style.label.visibility !== "hover") return false;
|
|
@@ -5898,12 +6120,7 @@ var SelectionManager = class {
|
|
|
5898
6120
|
if (!this.state.selectedNode) return;
|
|
5899
6121
|
const { element, data } = this.state.selectedNode;
|
|
5900
6122
|
this.restoreSelectedElements(data);
|
|
5901
|
-
element
|
|
5902
|
-
element.style.stroke = "";
|
|
5903
|
-
element.style.strokeWidth = "";
|
|
5904
|
-
element.style.opacity = "";
|
|
5905
|
-
element.style.removeProperty("r");
|
|
5906
|
-
delete element.dataset.selected;
|
|
6123
|
+
removeSelectionStyles(element, data);
|
|
5907
6124
|
this.root.selectAll(".link-label.label-selection-pinned").classed("label-selection-pinned", false).interrupt().transition().duration(200).style("opacity", 0).style("pointer-events", "none");
|
|
5908
6125
|
this.state.selectedNode = null;
|
|
5909
6126
|
this.eventEmitter.emit("nodeDeselect", { node: data, element });
|
|
@@ -6128,18 +6345,42 @@ var DEFAULT_NODE_HOVER_STYLE = {
|
|
|
6128
6345
|
};
|
|
6129
6346
|
function resolveNodeStyle(params) {
|
|
6130
6347
|
if (params.isSelected) {
|
|
6131
|
-
return
|
|
6348
|
+
return mergeNodeStyleSmart(DEFAULT_NODE_HOVER_STYLE, params.interaction?.selection?.nodeStyle);
|
|
6132
6349
|
}
|
|
6133
6350
|
if (params.isHovered) {
|
|
6134
|
-
return
|
|
6351
|
+
return mergeNodeStyleSmart(DEFAULT_NODE_HOVER_STYLE, params.interaction?.hover?.nodeStyle);
|
|
6135
6352
|
}
|
|
6136
6353
|
return void 0;
|
|
6137
6354
|
}
|
|
6138
|
-
function
|
|
6139
|
-
return
|
|
6140
|
-
|
|
6141
|
-
|
|
6142
|
-
|
|
6355
|
+
function mergeNodeStyleSmart(base, override) {
|
|
6356
|
+
if (!override) return base;
|
|
6357
|
+
const result = { ...override };
|
|
6358
|
+
if (override.strokeWidth !== void 0 && override.stroke === void 0 && base.stroke !== void 0) {
|
|
6359
|
+
result.stroke = base.stroke;
|
|
6360
|
+
}
|
|
6361
|
+
const baseKeys = Object.keys(base);
|
|
6362
|
+
baseKeys.forEach((key) => {
|
|
6363
|
+
if (key !== "stroke" && override[key] === void 0 && base[key] !== void 0) {
|
|
6364
|
+
switch (key) {
|
|
6365
|
+
case "fill":
|
|
6366
|
+
result.fill = base.fill;
|
|
6367
|
+
break;
|
|
6368
|
+
case "strokeWidth":
|
|
6369
|
+
result.strokeWidth = base.strokeWidth;
|
|
6370
|
+
break;
|
|
6371
|
+
case "opacity":
|
|
6372
|
+
result.opacity = base.opacity;
|
|
6373
|
+
break;
|
|
6374
|
+
case "radius":
|
|
6375
|
+
result.radius = base.radius;
|
|
6376
|
+
break;
|
|
6377
|
+
case "textColor":
|
|
6378
|
+
result.textColor = base.textColor;
|
|
6379
|
+
break;
|
|
6380
|
+
}
|
|
6381
|
+
}
|
|
6382
|
+
});
|
|
6383
|
+
return result;
|
|
6143
6384
|
}
|
|
6144
6385
|
|
|
6145
6386
|
// src/core/interaction-manager.ts
|
package/dist/index.js
CHANGED
|
@@ -5075,6 +5075,258 @@ function createDragBehavior(simulation, onDragStart, canvasBounds) {
|
|
|
5075
5075
|
});
|
|
5076
5076
|
}
|
|
5077
5077
|
|
|
5078
|
+
// src/utils/node-style-manager.ts
|
|
5079
|
+
var NodeStyleManager = class {
|
|
5080
|
+
styleStates = /* @__PURE__ */ new Map();
|
|
5081
|
+
/**
|
|
5082
|
+
* Initialize a node's style state by capturing its original styles
|
|
5083
|
+
*/
|
|
5084
|
+
initializeNode(element, node) {
|
|
5085
|
+
if (this.styleStates.has(element)) {
|
|
5086
|
+
return;
|
|
5087
|
+
}
|
|
5088
|
+
const domBackup = {
|
|
5089
|
+
stroke: element.getAttribute("stroke"),
|
|
5090
|
+
strokeWidth: element.getAttribute("stroke-width"),
|
|
5091
|
+
opacity: element.getAttribute("opacity"),
|
|
5092
|
+
fill: element.getAttribute("fill")
|
|
5093
|
+
};
|
|
5094
|
+
const originalStyle = {
|
|
5095
|
+
stroke: node.style?.stroke || void 0,
|
|
5096
|
+
strokeWidth: node.style?.strokeWidth || void 0,
|
|
5097
|
+
opacity: node.style?.opacity || void 0,
|
|
5098
|
+
fill: node.style?.fill || void 0
|
|
5099
|
+
};
|
|
5100
|
+
this.styleStates.set(element, {
|
|
5101
|
+
original: originalStyle,
|
|
5102
|
+
current: { ...originalStyle },
|
|
5103
|
+
domBackup
|
|
5104
|
+
});
|
|
5105
|
+
}
|
|
5106
|
+
/**
|
|
5107
|
+
* Apply temporary styles (e.g., hover effects) that will be reset later
|
|
5108
|
+
*/
|
|
5109
|
+
applyTemporaryStyles(element, styles) {
|
|
5110
|
+
this.applyStylesToDOM(element, styles);
|
|
5111
|
+
}
|
|
5112
|
+
/**
|
|
5113
|
+
* Apply permanent styles that become the new base styles
|
|
5114
|
+
*/
|
|
5115
|
+
applyPermanentStyles(element, styles) {
|
|
5116
|
+
const state = this.styleStates.get(element);
|
|
5117
|
+
if (!state) {
|
|
5118
|
+
console.warn("[NodeStyleManager] Node not initialized, cannot apply permanent styles");
|
|
5119
|
+
return;
|
|
5120
|
+
}
|
|
5121
|
+
this.applyStylesToDOM(element, styles);
|
|
5122
|
+
Object.assign(state.current, styles);
|
|
5123
|
+
}
|
|
5124
|
+
/**
|
|
5125
|
+
* Reset node to its current base styles (removes temporary styles)
|
|
5126
|
+
*/
|
|
5127
|
+
resetToBase(element) {
|
|
5128
|
+
const state = this.styleStates.get(element);
|
|
5129
|
+
if (!state) {
|
|
5130
|
+
this.clearAllStyles(element);
|
|
5131
|
+
return;
|
|
5132
|
+
}
|
|
5133
|
+
this.clearAllStyles(element);
|
|
5134
|
+
this.applyStylesToDOM(element, state.current);
|
|
5135
|
+
}
|
|
5136
|
+
/**
|
|
5137
|
+
* Reset node to its original styles (as captured during initialization)
|
|
5138
|
+
*/
|
|
5139
|
+
resetToOriginal(element) {
|
|
5140
|
+
const state = this.styleStates.get(element);
|
|
5141
|
+
if (!state) {
|
|
5142
|
+
this.clearAllStyles(element);
|
|
5143
|
+
return;
|
|
5144
|
+
}
|
|
5145
|
+
this.clearAllStyles(element);
|
|
5146
|
+
this.restoreOriginalDOM(element, state.domBackup);
|
|
5147
|
+
state.current = { ...state.original };
|
|
5148
|
+
}
|
|
5149
|
+
/**
|
|
5150
|
+
* Check if node is in a specific state (selected, hovered, etc.)
|
|
5151
|
+
*/
|
|
5152
|
+
hasState(element, stateName) {
|
|
5153
|
+
return element.dataset[stateName] === "true";
|
|
5154
|
+
}
|
|
5155
|
+
/**
|
|
5156
|
+
* Set state marker on node
|
|
5157
|
+
*/
|
|
5158
|
+
setState(element, stateName, value) {
|
|
5159
|
+
if (value) {
|
|
5160
|
+
element.dataset[stateName] = "true";
|
|
5161
|
+
} else {
|
|
5162
|
+
delete element.dataset[stateName];
|
|
5163
|
+
}
|
|
5164
|
+
}
|
|
5165
|
+
/**
|
|
5166
|
+
* Get the original styles for a node
|
|
5167
|
+
*/
|
|
5168
|
+
getOriginalStyles(element) {
|
|
5169
|
+
const state = this.styleStates.get(element);
|
|
5170
|
+
return state ? { ...state.original } : null;
|
|
5171
|
+
}
|
|
5172
|
+
/**
|
|
5173
|
+
* Get the current base styles for a node
|
|
5174
|
+
*/
|
|
5175
|
+
getCurrentStyles(element) {
|
|
5176
|
+
const state = this.styleStates.get(element);
|
|
5177
|
+
return state ? { ...state.current } : null;
|
|
5178
|
+
}
|
|
5179
|
+
/**
|
|
5180
|
+
* Remove a node from management (cleanup)
|
|
5181
|
+
*/
|
|
5182
|
+
removeNode(element) {
|
|
5183
|
+
this.styleStates.delete(element);
|
|
5184
|
+
}
|
|
5185
|
+
/**
|
|
5186
|
+
* Clear all managed nodes (for cleanup)
|
|
5187
|
+
*/
|
|
5188
|
+
clear() {
|
|
5189
|
+
this.styleStates.clear();
|
|
5190
|
+
}
|
|
5191
|
+
/**
|
|
5192
|
+
* Private: Apply styles to DOM element
|
|
5193
|
+
*/
|
|
5194
|
+
applyStylesToDOM(element, styles) {
|
|
5195
|
+
if (styles.stroke !== void 0) {
|
|
5196
|
+
if (styles.stroke === null || styles.stroke === "") {
|
|
5197
|
+
element.removeAttribute("stroke");
|
|
5198
|
+
element.style.stroke = "";
|
|
5199
|
+
} else {
|
|
5200
|
+
element.style.stroke = styles.stroke;
|
|
5201
|
+
}
|
|
5202
|
+
}
|
|
5203
|
+
if (styles.strokeWidth !== void 0) {
|
|
5204
|
+
if (styles.strokeWidth === null || styles.strokeWidth === 0) {
|
|
5205
|
+
element.removeAttribute("stroke-width");
|
|
5206
|
+
element.style.strokeWidth = "";
|
|
5207
|
+
} else {
|
|
5208
|
+
element.style.strokeWidth = String(styles.strokeWidth);
|
|
5209
|
+
}
|
|
5210
|
+
}
|
|
5211
|
+
if (styles.opacity !== void 0) {
|
|
5212
|
+
if (styles.opacity === null || styles.opacity === 1) {
|
|
5213
|
+
element.removeAttribute("opacity");
|
|
5214
|
+
element.style.opacity = "";
|
|
5215
|
+
} else {
|
|
5216
|
+
element.style.opacity = String(styles.opacity);
|
|
5217
|
+
}
|
|
5218
|
+
}
|
|
5219
|
+
if (styles.fill !== void 0) {
|
|
5220
|
+
if (styles.fill === null || styles.fill === "") {
|
|
5221
|
+
element.removeAttribute("fill");
|
|
5222
|
+
element.style.fill = "";
|
|
5223
|
+
} else {
|
|
5224
|
+
element.style.fill = styles.fill;
|
|
5225
|
+
}
|
|
5226
|
+
}
|
|
5227
|
+
if (styles.radius !== void 0) {
|
|
5228
|
+
if (styles.radius === null || styles.radius === 0) {
|
|
5229
|
+
element.removeAttribute("r");
|
|
5230
|
+
element.style.removeProperty("r");
|
|
5231
|
+
} else {
|
|
5232
|
+
element.setAttribute("r", String(styles.radius));
|
|
5233
|
+
}
|
|
5234
|
+
}
|
|
5235
|
+
}
|
|
5236
|
+
/**
|
|
5237
|
+
* Private: Clear all inline styles and remove hover-related attributes
|
|
5238
|
+
*/
|
|
5239
|
+
clearAllStyles(element) {
|
|
5240
|
+
element.style.stroke = "";
|
|
5241
|
+
element.style.strokeWidth = "";
|
|
5242
|
+
element.style.opacity = "";
|
|
5243
|
+
element.style.fill = "";
|
|
5244
|
+
element.style.removeProperty("r");
|
|
5245
|
+
this.removeIfHoverAttribute(element, "stroke");
|
|
5246
|
+
this.removeIfHoverAttribute(element, "stroke-width");
|
|
5247
|
+
this.removeIfHoverAttribute(element, "opacity");
|
|
5248
|
+
}
|
|
5249
|
+
/**
|
|
5250
|
+
* Private: Restore original DOM attributes
|
|
5251
|
+
*/
|
|
5252
|
+
restoreOriginalDOM(element, domBackup) {
|
|
5253
|
+
if (domBackup.stroke !== void 0) {
|
|
5254
|
+
if (domBackup.stroke === null) {
|
|
5255
|
+
element.removeAttribute("stroke");
|
|
5256
|
+
} else {
|
|
5257
|
+
element.setAttribute("stroke", domBackup.stroke);
|
|
5258
|
+
}
|
|
5259
|
+
}
|
|
5260
|
+
if (domBackup.strokeWidth !== void 0) {
|
|
5261
|
+
if (domBackup.strokeWidth === null) {
|
|
5262
|
+
element.removeAttribute("stroke-width");
|
|
5263
|
+
} else {
|
|
5264
|
+
element.setAttribute("stroke-width", domBackup.strokeWidth);
|
|
5265
|
+
}
|
|
5266
|
+
}
|
|
5267
|
+
if (domBackup.opacity !== void 0) {
|
|
5268
|
+
if (domBackup.opacity === null) {
|
|
5269
|
+
element.removeAttribute("opacity");
|
|
5270
|
+
} else {
|
|
5271
|
+
element.setAttribute("opacity", domBackup.opacity);
|
|
5272
|
+
}
|
|
5273
|
+
}
|
|
5274
|
+
if (domBackup.fill !== void 0) {
|
|
5275
|
+
if (domBackup.fill === null) {
|
|
5276
|
+
element.removeAttribute("fill");
|
|
5277
|
+
} else {
|
|
5278
|
+
element.setAttribute("fill", domBackup.fill);
|
|
5279
|
+
}
|
|
5280
|
+
}
|
|
5281
|
+
}
|
|
5282
|
+
/**
|
|
5283
|
+
* Private: Remove attribute only if it looks like it was set by hover/interaction
|
|
5284
|
+
*/
|
|
5285
|
+
removeIfHoverAttribute(element, attr) {
|
|
5286
|
+
const value = element.getAttribute(attr);
|
|
5287
|
+
if (!value) return;
|
|
5288
|
+
const hoverPatterns = {
|
|
5289
|
+
"stroke": ["#6366f1", "#8b5cf6", "#3b82f6", "#ffffff", "#fff", "white"],
|
|
5290
|
+
// Common hover stroke colors
|
|
5291
|
+
"stroke-width": ["2", "2.5", "3", "4"],
|
|
5292
|
+
// Common hover stroke widths
|
|
5293
|
+
"opacity": ["0.8", "0.9", "0.7"]
|
|
5294
|
+
// Common hover opacity values
|
|
5295
|
+
};
|
|
5296
|
+
const patterns = hoverPatterns[attr];
|
|
5297
|
+
if (patterns && patterns.includes(value)) {
|
|
5298
|
+
element.removeAttribute(attr);
|
|
5299
|
+
}
|
|
5300
|
+
}
|
|
5301
|
+
};
|
|
5302
|
+
var nodeStyleManager = new NodeStyleManager();
|
|
5303
|
+
function applyHoverStyles(element, node, hoverStyle) {
|
|
5304
|
+
if (nodeStyleManager.hasState(element, "selected")) {
|
|
5305
|
+
return;
|
|
5306
|
+
}
|
|
5307
|
+
nodeStyleManager.initializeNode(element, node);
|
|
5308
|
+
nodeStyleManager.applyTemporaryStyles(element, hoverStyle);
|
|
5309
|
+
nodeStyleManager.setState(element, "hovered", true);
|
|
5310
|
+
}
|
|
5311
|
+
function removeHoverStyles(element, node) {
|
|
5312
|
+
if (nodeStyleManager.hasState(element, "selected")) {
|
|
5313
|
+
return;
|
|
5314
|
+
}
|
|
5315
|
+
nodeStyleManager.initializeNode(element, node);
|
|
5316
|
+
nodeStyleManager.resetToBase(element);
|
|
5317
|
+
nodeStyleManager.setState(element, "hovered", false);
|
|
5318
|
+
}
|
|
5319
|
+
function applySelectionStyles(element, node, selectionStyle) {
|
|
5320
|
+
nodeStyleManager.initializeNode(element, node);
|
|
5321
|
+
nodeStyleManager.applyPermanentStyles(element, selectionStyle);
|
|
5322
|
+
nodeStyleManager.setState(element, "selected", true);
|
|
5323
|
+
}
|
|
5324
|
+
function removeSelectionStyles(element, node) {
|
|
5325
|
+
nodeStyleManager.initializeNode(element, node);
|
|
5326
|
+
nodeStyleManager.resetToOriginal(element);
|
|
5327
|
+
nodeStyleManager.setState(element, "selected", false);
|
|
5328
|
+
}
|
|
5329
|
+
|
|
5078
5330
|
// src/interactions/create-node-hover.ts
|
|
5079
5331
|
function createNodeHover(nodeSelection, hoverStyle) {
|
|
5080
5332
|
const firstNode = nodeSelection.node();
|
|
@@ -5082,35 +5334,10 @@ function createNodeHover(nodeSelection, hoverStyle) {
|
|
|
5082
5334
|
if (hoverStyle) {
|
|
5083
5335
|
nodeSelection.on("mouseenter.hover", function(_event, node) {
|
|
5084
5336
|
const circle = this;
|
|
5085
|
-
|
|
5086
|
-
return;
|
|
5087
|
-
}
|
|
5088
|
-
circle.setAttribute("stroke", hoverStyle.stroke ?? node.style?.stroke ?? "#ffffff");
|
|
5089
|
-
circle.setAttribute("stroke-width", String(hoverStyle.strokeWidth ?? node.style?.strokeWidth ?? 1.5));
|
|
5090
|
-
circle.setAttribute("opacity", String(hoverStyle.opacity ?? node.style?.opacity ?? 1));
|
|
5337
|
+
applyHoverStyles(circle, node, hoverStyle);
|
|
5091
5338
|
}).on("mouseleave.hover", function(_event, node) {
|
|
5092
5339
|
const circle = this;
|
|
5093
|
-
|
|
5094
|
-
return;
|
|
5095
|
-
}
|
|
5096
|
-
circle.style.stroke = "";
|
|
5097
|
-
circle.style.strokeWidth = "";
|
|
5098
|
-
circle.style.opacity = "";
|
|
5099
|
-
if (!node.style?.stroke) {
|
|
5100
|
-
circle.setAttribute("stroke", "#ffffff");
|
|
5101
|
-
} else {
|
|
5102
|
-
circle.setAttribute("stroke", node.style.stroke);
|
|
5103
|
-
}
|
|
5104
|
-
if (!node.style?.strokeWidth) {
|
|
5105
|
-
circle.setAttribute("stroke-width", "1.5");
|
|
5106
|
-
} else {
|
|
5107
|
-
circle.setAttribute("stroke-width", String(node.style.strokeWidth));
|
|
5108
|
-
}
|
|
5109
|
-
if (!node.style?.opacity) {
|
|
5110
|
-
circle.setAttribute("opacity", "1");
|
|
5111
|
-
} else {
|
|
5112
|
-
circle.setAttribute("opacity", String(node.style.opacity));
|
|
5113
|
-
}
|
|
5340
|
+
removeHoverStyles(circle, node);
|
|
5114
5341
|
});
|
|
5115
5342
|
}
|
|
5116
5343
|
const svgElement = firstNode.ownerSVGElement;
|
|
@@ -5810,12 +6037,7 @@ var SelectionManager = class {
|
|
|
5810
6037
|
this.clearSelection();
|
|
5811
6038
|
this.bringNodeToFront(nodeElement, nodeData);
|
|
5812
6039
|
if (this.config.nodeStyle) {
|
|
5813
|
-
|
|
5814
|
-
if (style.fill !== void 0) nodeElement.style.fill = style.fill;
|
|
5815
|
-
if (style.stroke !== void 0) nodeElement.style.stroke = style.stroke;
|
|
5816
|
-
if (style.strokeWidth !== void 0) nodeElement.style.strokeWidth = String(style.strokeWidth);
|
|
5817
|
-
if (style.opacity !== void 0) nodeElement.style.opacity = String(style.opacity);
|
|
5818
|
-
if (style.radius !== void 0) nodeElement.style.setProperty("r", String(style.radius));
|
|
6040
|
+
applySelectionStyles(nodeElement, nodeData, this.config.nodeStyle);
|
|
5819
6041
|
}
|
|
5820
6042
|
this.root.selectAll(".link-label").filter((item) => {
|
|
5821
6043
|
if (item.style.label.visibility !== "hover") return false;
|
|
@@ -5866,12 +6088,7 @@ var SelectionManager = class {
|
|
|
5866
6088
|
if (!this.state.selectedNode) return;
|
|
5867
6089
|
const { element, data } = this.state.selectedNode;
|
|
5868
6090
|
this.restoreSelectedElements(data);
|
|
5869
|
-
element
|
|
5870
|
-
element.style.stroke = "";
|
|
5871
|
-
element.style.strokeWidth = "";
|
|
5872
|
-
element.style.opacity = "";
|
|
5873
|
-
element.style.removeProperty("r");
|
|
5874
|
-
delete element.dataset.selected;
|
|
6091
|
+
removeSelectionStyles(element, data);
|
|
5875
6092
|
this.root.selectAll(".link-label.label-selection-pinned").classed("label-selection-pinned", false).interrupt().transition().duration(200).style("opacity", 0).style("pointer-events", "none");
|
|
5876
6093
|
this.state.selectedNode = null;
|
|
5877
6094
|
this.eventEmitter.emit("nodeDeselect", { node: data, element });
|
|
@@ -6096,18 +6313,42 @@ var DEFAULT_NODE_HOVER_STYLE = {
|
|
|
6096
6313
|
};
|
|
6097
6314
|
function resolveNodeStyle(params) {
|
|
6098
6315
|
if (params.isSelected) {
|
|
6099
|
-
return
|
|
6316
|
+
return mergeNodeStyleSmart(DEFAULT_NODE_HOVER_STYLE, params.interaction?.selection?.nodeStyle);
|
|
6100
6317
|
}
|
|
6101
6318
|
if (params.isHovered) {
|
|
6102
|
-
return
|
|
6319
|
+
return mergeNodeStyleSmart(DEFAULT_NODE_HOVER_STYLE, params.interaction?.hover?.nodeStyle);
|
|
6103
6320
|
}
|
|
6104
6321
|
return void 0;
|
|
6105
6322
|
}
|
|
6106
|
-
function
|
|
6107
|
-
return
|
|
6108
|
-
|
|
6109
|
-
|
|
6110
|
-
|
|
6323
|
+
function mergeNodeStyleSmart(base, override) {
|
|
6324
|
+
if (!override) return base;
|
|
6325
|
+
const result = { ...override };
|
|
6326
|
+
if (override.strokeWidth !== void 0 && override.stroke === void 0 && base.stroke !== void 0) {
|
|
6327
|
+
result.stroke = base.stroke;
|
|
6328
|
+
}
|
|
6329
|
+
const baseKeys = Object.keys(base);
|
|
6330
|
+
baseKeys.forEach((key) => {
|
|
6331
|
+
if (key !== "stroke" && override[key] === void 0 && base[key] !== void 0) {
|
|
6332
|
+
switch (key) {
|
|
6333
|
+
case "fill":
|
|
6334
|
+
result.fill = base.fill;
|
|
6335
|
+
break;
|
|
6336
|
+
case "strokeWidth":
|
|
6337
|
+
result.strokeWidth = base.strokeWidth;
|
|
6338
|
+
break;
|
|
6339
|
+
case "opacity":
|
|
6340
|
+
result.opacity = base.opacity;
|
|
6341
|
+
break;
|
|
6342
|
+
case "radius":
|
|
6343
|
+
result.radius = base.radius;
|
|
6344
|
+
break;
|
|
6345
|
+
case "textColor":
|
|
6346
|
+
result.textColor = base.textColor;
|
|
6347
|
+
break;
|
|
6348
|
+
}
|
|
6349
|
+
}
|
|
6350
|
+
});
|
|
6351
|
+
return result;
|
|
6111
6352
|
}
|
|
6112
6353
|
|
|
6113
6354
|
// src/core/interaction-manager.ts
|
package/package.json
CHANGED