Skip to content

Commit c37a269

Browse files
committed
tooltip for current element
1 parent 917e7ea commit c37a269

File tree

3 files changed

+68
-27
lines changed

3 files changed

+68
-27
lines changed

NetworkDynamicsInspector/assets/app.css

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,17 @@ html, body {
7171
flex-grow: 1;
7272
overflow:hidden;
7373
}
74+
75+
.tooltip {
76+
position: absolute;
77+
background-color: black;
78+
color: white;
79+
padding: 5px 10px;
80+
border-radius: 5px;
81+
font-size: 12px;
82+
font-family: monospace;
83+
pointer-events: none;
84+
white-space: nowrap;
85+
display: none; /* Initially hidden */
86+
transform: translate(12px, 12px); /* Offset from cursor */
87+
}

NetworkDynamicsInspector/src/NetworkDynamicsInspector.jl

Lines changed: 52 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,6 @@ function graphplot_card(app, session)
146146
Stress(; pin)
147147
end
148148

149-
small_hover_text = Observable{String}("")
150149
fig, ax = with_theme(apptheme()) do
151150
fig = Figure(; figure_padding=0)
152151
ax = Axis(fig[1,1])
@@ -156,11 +155,6 @@ function graphplot_card(app, session)
156155

157156
hidespines!(ax)
158157
hidedecorations!(ax)
159-
160-
fig[1,1] = Label(fig, small_hover_text,
161-
tellwidth=false, tellheight=false,
162-
justification=:left, halign=:left, valign=:bottom)
163-
164158
fig, ax
165159
end
166160
xratio = Ref{Float64}(1.0)
@@ -174,33 +168,65 @@ function graphplot_card(app, session)
174168
####
175169
#### Interactions
176170
####
177-
hoverstate = Observable(false)
171+
tooltip_text = Observable{String}("")
178172

179173
js = js"""
180174
const gpcard = document.querySelector(".graphplot-card");
181-
$(hoverstate).on((state) => {
182-
if (state) {
175+
176+
// Create tooltip element
177+
const tooltip = document.createElement("div");
178+
tooltip.classList.add("tooltip"); // Apply styles from CSS
179+
document.body.appendChild(tooltip);
180+
181+
// let tooltipTimeout; // Store timeout reference
182+
let latestX = 0, latestY = 0; // Store latest cursor position
183+
let isUpdating = false;
184+
185+
gpcard.addEventListener("mousemove", (event) => {
186+
latestX = event.clientX;
187+
latestY = event.clientY;
188+
189+
if (!isUpdating && tooltip.style.display === "block") {
190+
isUpdating = true;
191+
requestAnimationFrame(() => {
192+
tooltip.style.left = `${latestX}px`;
193+
tooltip.style.top = `${latestY}px`;
194+
isUpdating = false;
195+
});
196+
}
197+
});
198+
199+
// Show tooltip after delay when idx > 0
200+
$(tooltip_text).on((text) => {
201+
// clearTimeout(tooltipTimeout); // Prevent multiple pending tooltips
202+
203+
if (text.length > 0) {
183204
gpcard.style.cursor = "pointer";
205+
206+
// tooltipTimeout = setTimeout(() => { // Delay tooltip display
207+
tooltip.textContent = text;
208+
tooltip.style.display = "block";
209+
tooltip.style.left = `${latestX}px`;
210+
tooltip.style.top = `${latestY}px`;
211+
// }, 300); // 0.3s delay
184212
} else {
185213
gpcard.style.cursor = "default";
214+
tooltip.style.display = "none"; // Hide tooltip immediately
215+
// clearTimeout(tooltipTimeout); // Cancel any pending tooltip show
186216
}
187217
});
188218
"""
189219
evaljs(session, js)
190220

191221
nhh = NodeHoverHandler() do hstate, idx, event, axis
192-
hoverstate[] = hstate
222+
tooltip_text[] = hstate ? _sidx_to_str(VIndex(idx), app) : ""
193223
app.graphplot._hoverel[] = hstate ? VIndex(idx) : nothing
194224
end
195225
ehh = EdgeHoverHandler() do hstate, idx, event, axis
196-
hoverstate[] = hstate
226+
tooltip_text[] = hstate ? _sidx_to_str(EIndex(idx), app) : ""
197227
app.graphplot._hoverel[] = hstate ? EIndex(idx) : nothing
198228
end
199229

200-
on(app.graphplot._hoverel) do el
201-
small_hover_text[] = isnothing(el) ? "" : repr(el)
202-
end
203-
204230
# interactions
205231
clickaction = (i, type) -> begin
206232
idx = type == :vertex ? VIndex(i) : EIndex(i)
@@ -259,7 +285,6 @@ function adapt_xy_scaling!(xratio, yratio, ax)
259285
resize = potential_x_factor > potential_y_factor ? :x : :y
260286
@warn "No valid scaling factor found, chose $resize"
261287
end
262-
@info "Resizing $resize"
263288

264289
if resize == :x
265290
xratio[] = potential_x_factor
@@ -475,7 +500,7 @@ function timeseries_card(app, key, session)
475500
comp_sel = MultiSelect(comp_options, tsplot.selcomp;
476501
placeholder="Select components",
477502
multi=true,
478-
option_to_string=_sidx_to_str,
503+
option_to_string=s -> _sidx_to_str(s, app),
479504
T=SymbolicIndex,
480505
id=gendomid("compsel"))
481506
# comp_sel_dom = Grid(DOM.span("Components"), comp_sel; columns = "70px 1fr", align_items = "center")
@@ -531,7 +556,7 @@ function timeseries_card(app, key, session)
531556
i = _smallest_free(color_cache)
532557
color_cache[new] = i
533558
end
534-
colorpairs[] = [(; title=_sidx_to_str(k),
559+
colorpairs[] = [(; title=_sidx_to_str(k, app),
535560
color="#"*Colors.hex(getcycled(COLORS, v)))
536561
for (k,v) in color_cache]
537562
nothing
@@ -766,8 +791,15 @@ function timeseries_card(app, key, session)
766791

767792
return card
768793
end
769-
function _sidx_to_str(s)
770-
(s isa VIndex ? "v" : "e") * string(s.compidx)
794+
795+
function _sidx_to_str(s, app)
796+
if s isa VIndex
797+
"v$(s.compidx)"
798+
else
799+
edge = extract_nw(app.sol[]).im.edgevec[s.compidx]
800+
src, dst = edge.src, edge.dst
801+
"e$(s.compidx): $src$dst"
802+
end
771803
end
772804
function _smallest_free(d::Dict)
773805
vals = values(d)

test/Inspector_test.jl

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -74,14 +74,9 @@ app = (;
7474
ecolorrange = Observable{Tuple{Float32,Float32}}((-1.0, 1.0)),
7575
ecolorscheme = Observable{ColorScheme}(ColorSchemes.coolwarm),
7676
_selcomp = Observable{Vector{SymbolicIndex}}(SymbolicIndex[]),
77-
_hoverel = Observable{Union{EIndex{Int,Nothing},VIndex{Int,Nothing},Nothing}}(nothing)
78-
_lastclickel = Observable{Union{EIndex{Int,Nothing},VIndex{Int,Nothing},Nothing}}(nothing)
77+
_hoverel = Observable{Union{EIndex{Int,Nothing},VIndex{Int,Nothing},Nothing}}(nothing),
78+
_lastclickel = Observable{Union{EIndex{Int,Nothing},VIndex{Int,Nothing},Nothing}}(nothing),
7979
),
80-
# tsplot = (;
81-
# selcomp = Observable{Vector{SymbolicIndex}}(SymbolicIndex[]),
82-
# states = Observable{Vector{Symbol}}(Symbol[]),
83-
# rel = Observable{Bool}(false),
84-
# ),
8580
tsplots = Observable{Any}(OrderedDict(
8681
"a" => (;
8782
selcomp = Observable{Vector{SymbolicIndex}}(SymbolicIndex[]),

0 commit comments

Comments
 (0)