Skip to content

Commit 6730499

Browse files
committed
Merge branch 'v1.12.0'
2 parents 2ca19c9 + cece7f9 commit 6730499

12 files changed

Lines changed: 491 additions & 95 deletions

File tree

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,20 @@
11
# Changelog
22

3+
## 1.12.0
4+
5+
### Features
6+
7+
* Added toggle button for hover highlight effect with hotkey (H) and auto-clear of highlight/dim states on disable
8+
* Added fine-grained bubble-set label controls with tabbed UI
9+
* Show data source label in header and improve export filenames
10+
11+
### Fixes
12+
13+
* Fixed edges rendering on top of nodes after dragging a node and then hovering — reset zIndex elevated by G6's `frontElement` after drag ends
14+
* Fixed group-contiguous column order in Excel export
15+
* Fixed new columns being inserted at end instead of next to their group
16+
* Fixed apply button not enabling after row deletion in data editor
17+
318
## 1.11.0
419

520
### Features

src/config.js

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
22
* Defaults for the graph, layouts and UI
33
*/
4-
const VERSION = "1.11.2";
4+
const VERSION = "1.12.0";
55

66
const DEFAULTS = {
77
NODE: {
@@ -48,51 +48,79 @@ const DEFAULTS = {
4848
stroke: '#C33D35',
4949
strokeOpacity: 1,
5050
virtualEdges: true,
51+
label: true,
52+
labelText: 'group one',
5153
labelFill: '#fff',
54+
labelFontSize: 12,
5255
labelPadding: 2,
56+
labelBackground: true,
5357
labelBackgroundFill: '#403C53',
5458
labelBackgroundRadius: 5,
55-
label: true,
56-
labelText: 'group one'
59+
labelCloseToPath: true,
60+
labelAutoRotate: true,
61+
labelOffsetX: 0,
62+
labelOffsetY: 0,
63+
labelPlacement: 'bottom',
5764
},
5865
"groupTwo": {
5966
fill: '#c33d35',
6067
fillOpacity: 0.25,
6168
stroke: '#403c53',
6269
strokeOpacity: 1,
6370
virtualEdges: true,
71+
label: true,
72+
labelText: 'group two',
6473
labelFill: '#fff',
74+
labelFontSize: 12,
6575
labelPadding: 2,
76+
labelBackground: true,
6677
labelBackgroundFill: '#c33d35',
6778
labelBackgroundRadius: 5,
68-
label: true,
69-
labelText: 'group two'
79+
labelCloseToPath: true,
80+
labelAutoRotate: true,
81+
labelOffsetX: 0,
82+
labelOffsetY: 0,
83+
labelPlacement: 'bottom',
7084
},
7185
"groupThree": {
7286
fill: '#EFB0AA',
7387
fillOpacity: 0.4,
7488
stroke: '#8CA6D9',
7589
strokeOpacity: 1,
7690
virtualEdges: true,
91+
label: true,
92+
labelText: 'group three',
7793
labelFill: '#fff',
94+
labelFontSize: 12,
7895
labelPadding: 2,
96+
labelBackground: true,
7997
labelBackgroundFill: '#EFB0AA',
8098
labelBackgroundRadius: 5,
81-
label: true,
82-
labelText: 'group three'
99+
labelCloseToPath: true,
100+
labelAutoRotate: true,
101+
labelOffsetX: 0,
102+
labelOffsetY: 0,
103+
labelPlacement: 'bottom',
83104
},
84105
"groupFour": {
85106
fill: '#8CA6D9',
86107
fillOpacity: 0.4,
87108
stroke: '#EFB0AA',
88109
strokeOpacity: 1,
89110
virtualEdges: true,
111+
label: true,
112+
labelText: 'group four',
90113
labelFill: '#fff',
114+
labelFontSize: 12,
91115
labelPadding: 2,
116+
labelBackground: true,
92117
labelBackgroundFill: '#8CA6D9',
93118
labelBackgroundRadius: 5,
94-
label: true,
95-
labelText: 'group four'
119+
labelCloseToPath: true,
120+
labelAutoRotate: true,
121+
labelOffsetX: 0,
122+
labelOffsetY: 0,
123+
labelPlacement: 'bottom',
96124
},
97125
},
98126
BUBBLE_GROUP_QUADRANT_POSITIONS: {
@@ -182,7 +210,8 @@ const CFG = {
182210
HIDE_LABELS: false,
183211

184212
// if network is greater than defined threshold, hover effects are disabled
185-
MAX_NODES_BEFORE_DISABLING_HOVER_EFFECT: 300,
213+
MAX_NODES_BEFORE_DISABLING_HOVER_EFFECT: 200,
214+
MAX_EDGES_BEFORE_DISABLING_HOVER_EFFECT: 500,
186215
DISABLE_HOVER_EFFECT: false,
187216

188217
// if network is greater than defined threshold, bubble groups may span across non-bubble group members

src/gll.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,7 @@ async function loadDemoData() {
392392
const data = await stringDemoDataLoader.loadNetwork();
393393

394394
if (data) {
395+
cache.ui.setDataSourceLabel(`STRING: ${genes.join(', ')}`);
395396

396397
await cache.gcm.destroyGraphAndRollBackUI();
397398
cache.gcm.resetEventLocks();
@@ -432,6 +433,7 @@ async function loadDemoData() {
432433

433434
async function startTour() {
434435
const data = generateTourData();
436+
cache.ui.setDataSourceLabel('Tour: Sample Gene Network');
435437

436438
await cache.gcm.destroyGraphAndRollBackUI();
437439
cache.gcm.resetEventLocks();

src/graph/bubble_sets.js

Lines changed: 73 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -62,20 +62,38 @@ class GraphBubbleSetManager {
6262
break;
6363
case "Label Background":
6464
bStyle.labelBackground = value;
65-
if (!value) {
66-
bStyle.labelFill = "#000000";
67-
} else {
68-
bStyle.labelFill = "#FFFFFF";
69-
}
65+
break;
66+
case "Label Fill Color":
67+
bStyle.labelFill = value;
68+
break;
69+
case "Label Font Size":
70+
bStyle.labelFontSize = value;
71+
break;
72+
case "Label Close To Path":
73+
bStyle.labelCloseToPath = value;
74+
break;
75+
case "Label Auto Rotate":
76+
bStyle.labelAutoRotate = value;
77+
break;
78+
case "Label Offset X":
79+
bStyle.labelOffsetX = value;
80+
break;
81+
case "Label Offset Y":
82+
bStyle.labelOffsetY = value;
83+
break;
84+
case "Label Placement":
85+
bStyle.labelPlacement = value;
7086
break;
7187
default:
7288
break;
7389
}
74-
await this.cache.INSTANCES.BUBBLE_GROUPS[group].update(bStyle);
90+
await this.cache.INSTANCES.BUBBLE_GROUPS[group].update({ ...bStyle });
7591
await this.cache.gcm.decideToRenderOrDraw(true);
92+
this.refreshBubbleStyleElements();
7693
}
7794

7895
refreshBubbleStyleElements() {
96+
let anyGroupActive = false;
7997
for (const group of this.traverseBubbleSets()) {
8098
const currentLayout = this.cache.data.layouts[this.cache.data.selectedLayout];
8199
const bubbleStyle = currentLayout.bubbleSetStyle[group];
@@ -105,6 +123,7 @@ class GraphBubbleSetManager {
105123
}
106124

107125
const hasActiveMembers = actualMembers.size > 0;
126+
if (hasActiveMembers) anyGroupActive = true;
108127
const labelConfigShouldBeEnabled = bubbleStyle.label;
109128

110129
// toggle entire cards based on bubble group members
@@ -117,25 +136,61 @@ class GraphBubbleSetManager {
117136
labelInput.value = bubbleStyle.labelText;
118137
}
119138

120-
// Update color pickers (fill, stroke, label background)
121-
const fillColorInput = document.querySelector(`input[data-property="Bubble Set ${group} Fill Color"]`);
122-
if (fillColorInput && bubbleStyle.fill) {
123-
fillColorInput.value = bubbleStyle.fill;
124-
}
125-
126-
const strokeColorInput = document.querySelector(`input[data-property="Bubble Set ${group} Stroke Color"]`);
127-
if (strokeColorInput && bubbleStyle.stroke) {
128-
strokeColorInput.value = bubbleStyle.stroke;
129-
}
139+
// Sync color inputs by data-property attribute
140+
const syncColorInput = (prop, val) => {
141+
const el = card.querySelector(`input[data-property="Bubble Set ${group} ${prop}"]`);
142+
if (el && val != null) el.value = val;
143+
};
144+
syncColorInput("Fill Color", bubbleStyle.fill);
145+
syncColorInput("Stroke Color", bubbleStyle.stroke);
146+
syncColorInput("Label Background Color", bubbleStyle.labelBackgroundFill);
147+
syncColorInput("Label Fill Color", bubbleStyle.labelFill);
148+
149+
// Sync slider inputs by data-property attribute
150+
const syncSliderInput = (prop, val) => {
151+
const container = card.querySelector(`[data-property="Bubble Set ${group} ${prop}"]`);
152+
if (!container || val === undefined) return;
153+
const slider = container.querySelector('input[type="range"]');
154+
const numInput = container.querySelector('input[type="number"]');
155+
if (slider) slider.value = val;
156+
if (numInput) numInput.value = val;
157+
};
158+
syncSliderInput("Label Font Size", bubbleStyle.labelFontSize);
159+
syncSliderInput("Label Offset X", bubbleStyle.labelOffsetX);
160+
syncSliderInput("Label Offset Y", bubbleStyle.labelOffsetY);
161+
162+
// Sync switch inputs by data-property attribute
163+
const syncSwitch = (prop, val) => {
164+
const el = card.querySelector(`[data-property="Bubble Set ${group} ${prop}"]`);
165+
if (el && el.setChecked) el.setChecked(!!val);
166+
};
167+
syncSwitch("Label Close To Path", bubbleStyle.labelCloseToPath);
168+
syncSwitch("Label Auto Rotate", bubbleStyle.labelAutoRotate);
169+
170+
// Sync dropdown by data-property attribute
171+
const placementDropdown = card.querySelector(`[data-property="Bubble Set ${group} Label Placement"]`);
172+
if (placementDropdown && bubbleStyle.labelPlacement) placementDropdown.value = bubbleStyle.labelPlacement;
130173

131174
// toggle label-related properties
132175
for (const elem of card.querySelectorAll(".bubbleSetOptionalLabelConfig")) {
133176
labelConfigShouldBeEnabled ? elem.classList.remove("disabled") : elem.classList.add("disabled");
134177
}
135178

136-
// override css properties to style round-button quadrants
179+
// override css properties to style round-button quadrants and tabs
137180
const fillColor = bubbleStyle.fill || this.cache.DEFAULTS.BUBBLE_GROUP_STYLE[group].fill;
138181
document.documentElement.style.setProperty(`--${group}-color`, fillColor);
182+
183+
const tab = document.querySelector(`.bubble-set-tab[data-group="${group}"]`);
184+
if (tab) {
185+
tab.style.setProperty("--tab-color", fillColor);
186+
tab.style.setProperty("--tab-text-color", StaticUtilities.getReadableForegroundColor(fillColor));
187+
}
188+
}
189+
190+
// Toggle the entire bubble sets card when no groups are active
191+
const outerCard = document.querySelector(".bubble-set-config-card-header");
192+
if (outerCard) {
193+
anyGroupActive ? outerCard.classList.remove("disabled") : outerCard.classList.add("disabled");
139194
}
140195
}
141196

@@ -198,18 +253,12 @@ class GraphBubbleSetManager {
198253
const bubbleStyle = this.cache.data.layouts[this.cache.data.selectedLayout].bubbleSetStyle[group];
199254

200255
await this.cache.INSTANCES.BUBBLE_GROUPS[group].update({
256+
...bubbleStyle,
201257
members: empty ? [] : membersAsArray,
202258
avoidMembers: avoidMembers,
203259
fillOpacity: empty ? 0 : bubbleStyle.fillOpacity,
204260
strokeOpacity: empty ? 0 : bubbleStyle.strokeOpacity,
205261
label: empty ? false : bubbleStyle.label,
206-
// Apply all style properties including label text
207-
labelText: bubbleStyle.labelText,
208-
fill: bubbleStyle.fill,
209-
stroke: bubbleStyle.stroke,
210-
labelBackgroundFill: bubbleStyle.labelBackgroundFill,
211-
labelFill: bubbleStyle.labelFill,
212-
labelBackground: bubbleStyle.labelBackground,
213262
});
214263
await this.cache.INSTANCES.BUBBLE_GROUPS[group].drawBubbleSets();
215264
}

src/graph/core.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,15 +249,22 @@ class GraphCoreManager {
249249
plugins: plugins,
250250
});
251251

252-
this.cache.graph.on("node:dragend", async () => {
252+
this.cache.graph.on("node:dragend", async (event) => {
253253
/**
254254
* Persist all positions on every drag event
255+
* and reset zIndex elevated by G6's frontElement during drag
255256
*/
256257
if (this.cache.EVENT_LOCKS.DRAG_END_RUNNING) return;
257258

258259
this.cache.ui.debug("DRAG END");
259260
this.cache.EVENT_LOCKS.DRAG_END_RUNNING = true;
260261
await this.cache.lm.persistNodePositions();
262+
263+
const draggedId = event?.target?.id;
264+
if (draggedId) {
265+
await this.cache.graph.setElementZIndex(draggedId, 0);
266+
}
267+
261268
this.cache.EVENT_LOCKS.DRAG_END_RUNNING = false;
262269
});
263270

@@ -1157,6 +1164,9 @@ class GraphCoreManager {
11571164
case "l":
11581165
await this.cache.ui.toggleLassoSelection();
11591166
break;
1167+
case "h":
1168+
await this.cache.ui.toggleHoverEffect(document.getElementById("hoverToggleBtn"));
1169+
break;
11601170
default:
11611171
break;
11621172
}

src/graph_lens_lite.html

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,13 @@ <h3 id="loadingHeader">Loading ..</h3>
7676
<div class="sidebar-resize-handle"></div>
7777
<div id="sidebarContentContainer">
7878
<div class="header-row">
79-
<h3 id="appHeader" class="disabled-header" onclick="cache.ui.reloadApp()">
80-
<img inline src="delta-4-icon.png" alt="Delta4 Icon" style="height: 1em; vertical-align: middle; margin-right: 0.3em;">
81-
Graph Lens Lite <span id="versionInfo" style="font-size: 0.6em; color: #666; font-weight: normal;"></span>
82-
</h3>
79+
<div class="header-title-group">
80+
<h3 id="appHeader" class="disabled-header" onclick="cache.ui.reloadApp()">
81+
<img inline src="delta-4-icon.png" alt="Delta4 Icon" style="height: 1em; vertical-align: middle; margin-right: 0.3em;">
82+
Graph Lens Lite <span id="versionInfo" style="font-size: 0.6em; color: #666; font-weight: normal;"></span>
83+
</h3>
84+
<span id="dataSourceLabel" class="data-source-label"></span>
85+
</div>
8386
<div class="showOnLoad button-group">
8487
<button id="metricsToggleBtn" class="showOnLoad medium-btn" title="Toggle network metrics panel (M)" onclick="cache.metrics.toggleUI()">📊</button>
8588
<button id="dataToggleBtn" class="showOnLoad medium-btn" title="Toggle data editor (D)" onclick="cache.ui.toggleDataEditor()">🔢</button>
@@ -110,6 +113,7 @@ <h3 id="appHeader" class="disabled-header" onclick="cache.ui.reloadApp()">
110113
<div class="button-separator"></div>
111114
<button class="small-btn" title="Fit graph to screen (F)" onclick="cache.gcm.fitViewToVisibleNodes()"></button>
112115
<button id="hideDisconnectedBtn" class="small-btn red" title="Hide all nodes and edges that are not connected to any other node or edge" onclick="cache.gcm.toggleCleanUpDanglingElements(this)">🚫</button>
116+
<button id="hoverToggleBtn" class="small-btn green highlight" title="Disable hover highlight effect (H)" onclick="cache.ui.toggleHoverEffect(this)"></button>
113117
<div id="debugDiv" class="inline"></div>
114118
<br>
115119
<h5 class="purple">Shown:

0 commit comments

Comments
 (0)