Skip to content
This repository was archived by the owner on Dec 17, 2024. It is now read-only.

Commit d2a4807

Browse files
committed
refine grid filtering and zooming
Fixes #829
1 parent d5e01c2 commit d2a4807

File tree

6 files changed

+169
-52
lines changed

6 files changed

+169
-52
lines changed

app/content/css/ui.css

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1369,6 +1369,16 @@ body.subwindow.sidecar-full-screen #sidecar .sidecar-bottom-stripe .sidecar-bott
13691369
/* these two rules are an attempt to push the tooltips just above the bottom stripe */
13701370
height: 2rem;
13711371
align-items: center;
1372+
1373+
transition: opacity 150ms ease-in-out;
1374+
}
1375+
.graphical-icon.disabled {
1376+
opacity: 0.3;
1377+
}
1378+
.graphical-icon.disabled:hover {
1379+
opacity: 0.3 !important;
1380+
color: inherit !important;
1381+
cursor: default !important;
13721382
}
13731383
.graphical-icon[data-balloon]:after, .graphical-icon[data-balloon]:before {
13741384
text-transform: none;

app/content/js/bottom-stripe.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,17 @@ const addModeButton = (bottomStripe, opts, entity, show) => {
164164
const view = direct(entity)
165165
if (view && view.then && !actAsButton) {
166166
view.then(custom => ui.showCustom(custom, { leaveBottomStripeAlonex: true }))
167+
} else if (actAsButton && view && view.toggle) {
168+
view.toggle.forEach(({mode, disabled}) => {
169+
const button = bottomStripe.querySelector(`.sidecar-bottom-stripe-button[data-mode="${mode}"]`)
170+
if (button) {
171+
if (disabled) {
172+
button.classList.add('disabled')
173+
} else {
174+
button.classList.remove('disabled')
175+
}
176+
}
177+
})
167178
}
168179
} else {
169180
repl.pexec(command(entity), { leaveBottomStripeAlonex: true, echo, noHistory })

app/plugins/modules/activation-visualizations/lib/cell.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ const prettyPrintDuration = require('pretty-ms'),
2222
* Draw the given activation in the given cell (a dom)
2323
*
2424
*/
25-
exports.renderCell = (returnTo, cell, activation, isFailure=!activation.response.success, duration=activation.end-activation.start, latBucket=latencyBucket(duration), options) => {
25+
exports.renderCell = (returnTo, cell, activation, isFailure=!activation.response.success, duration=activation.end-activation.start, latBucket=isFailure ? -1 : latencyBucket(duration), options) => {
2626
let returnValue = cell
2727
if (!cell) {
2828
// then the caller asked us to make the container
@@ -44,11 +44,13 @@ exports.renderCell = (returnTo, cell, activation, isFailure=!activation.response
4444
cell = inner2
4545
}
4646

47+
cell.classList.add(`latency-${latBucket}`)
48+
4749
const container = document.createElement('div')
4850
cell.appendChild(container)
4951

5052
cell.className = `${cell.className} is-failure-${isFailure}`
51-
container.className = `grid-cell-content latency-${latBucket}`
53+
container.className = `grid-cell-content`
5254

5355
// any extra info to display in the tooltip?
5456
let extraTooltip = ''

app/plugins/modules/activation-visualizations/lib/grid.js

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -156,11 +156,66 @@ const drawGrid = (options, header) => activations => {
156156

157157
//injectHTML(content, 'grid/bottom-bar.html', 'bottom-bar')
158158

159+
/** zoom update button click handler */
160+
const rezoom = change => () => {
161+
const gridGrid = content.querySelector(`.${css.gridGrid}`),
162+
currentZoom = parseInt(gridGrid.getAttribute('data-zoom-level')),
163+
newZoom = change(currentZoom),
164+
zoomMin = -2,
165+
zoomMax = 2
166+
167+
if (newZoom !== currentZoom) {
168+
gridGrid.setAttribute('data-zoom-level', newZoom)
169+
gridGrid.classList.remove(`zoom_${currentZoom}`)
170+
gridGrid.classList.add(`zoom_${newZoom}`)
171+
172+
// and try to make the gridDom mostly squarish
173+
const grids = gridGrid.querySelectorAll('.grid')
174+
for (let idx = 0; idx < grids.length; idx++) {
175+
const gridDom = grids[idx],
176+
gridLabel = gridDom.querySelector('.grid-label'),
177+
gridRow = gridDom.querySelector('.grid-row'),
178+
width = gridDom.getAttribute('data-width'),
179+
vws = newZoom === 0 ? 2.75 : newZoom === 1 ? 3 : newZoom === 2 ? 4 : 0.75
180+
181+
gridLabel.style.maxWidth = `${Math.max(8, width * vws * 1.1)}vw`
182+
gridRow.style.maxWidth = `${Math.max(8, width * vws)}vw`
183+
}
184+
185+
if (newZoom === zoomMax) {
186+
return { toggle: [{ mode: 'zoom-in', disabled: true }, // can't zoom in any further
187+
{ mode: 'zoom-out', disabled: false }] }
188+
} else if (newZoom == zoomMin) {
189+
return { toggle: [{ mode: 'zoom-out', disabled: true }, // can't zoom out any further
190+
{ mode: 'zoom-in', disabled: false }] }
191+
} else {
192+
return { toggle: [{ mode: 'zoom-out', disabled: false },
193+
{ mode: 'zoom-in', disabled: false }] }
194+
}
195+
}
196+
}
197+
const zoomIn = { mode: 'zoom-in',
198+
fontawesome: 'fas fa-search-plus',
199+
balloon: 'Use larger grid cells',
200+
flush: 'right',
201+
actAsButton: true,
202+
direct: rezoom(_ => Math.min(2, _ + 1))
203+
},
204+
zoomOut = { mode: 'zoom-out',
205+
fontawesome: 'fas fa-search-minus',
206+
balloon: 'Use smaller grid cells',
207+
flush: 'right',
208+
actAsButton: true,
209+
direct: rezoom(_ => Math.max(-2, _ - 1))
210+
}
211+
159212
return {
160213
type: 'custom',
161214
content,
162215
controlHeaders: true,
163-
modes: modes('grid', options)
216+
217+
// add zoom buttons to the mode button model
218+
modes: modes('grid', options).concat([zoomIn, zoomOut])
164219
}
165220
}
166221

@@ -199,6 +254,7 @@ const _drawGrid = (options, {sidecar, leftHeader, rightHeader}, content, groupDa
199254
zoomLevelForDisplay = totalCount > 1000 ? -2 : totalCount <= 100 ? zoomLevel : 0 // don't zoom in too far, if there are many cells to display
200255

201256
gridGrid.className = `${css.gridGrid} cell-container zoom_${zoomLevelForDisplay}`
257+
gridGrid.setAttribute('data-zoom-level', zoomLevelForDisplay)
202258
colorBy('duration', gridGrid)
203259

204260
if (!redraw) {
@@ -214,7 +270,7 @@ const _drawGrid = (options, {sidecar, leftHeader, rightHeader}, content, groupDa
214270
const onclick = drilldownWith(viewName, `action get "${group.path}"`)
215271
ui.addNameToSidecarHeader(sidecar, group.name, packageName, onclick)
216272

217-
drawLegend(viewName, rightHeader, group, options)
273+
drawLegend(viewName, rightHeader, group, gridGrid, options)
218274
} else {
219275
const onclick = options.appName ? drilldownWith(viewName, `app get "${options.appName}"`) : undefined,
220276
pathComponents = (options.appName||'').split('/'),
@@ -224,7 +280,7 @@ const _drawGrid = (options, {sidecar, leftHeader, rightHeader}, content, groupDa
224280
ui.addNameToSidecarHeader(sidecar, name, packageName, onclick)
225281

226282
if (groups.length > 0) {
227-
drawLegend(viewName, rightHeader, summary, options)
283+
drawLegend(viewName, rightHeader, summary, gridGrid, options)
228284
}
229285
}
230286

@@ -281,13 +337,15 @@ const _drawGrid = (options, {sidecar, leftHeader, rightHeader}, content, groupDa
281337
group.height = L
282338
cells = grid.reserve(group)
283339

340+
gridDom.setAttribute('data-width', width)
341+
284342
// now that we know the width of the grid, adjust the width of the label
285343
if (zoomLevel === 0) {
286344
gridLabel.style.maxWidth = `${width * 2.75 * 1.1}vw` // 2.75vw is the width in table.css; 1.1x to give a bit of overflow
287345
}
288346

289347
// and try to make the gridDom mostly squarish
290-
gridDom.querySelector('.grid-row').style.maxWidth = `${width * (zoomLevelForDisplay === 0 ? 2.75 : zoomLevelForDisplay === 1 ? 3 : zoomLevelForDisplay === 2 ? 4 : 0.75)}vw`
348+
gridDom.querySelector('.grid-row').style.maxWidth = `${width * (zoomLevelForDisplay === 0 ? 2.75 : zoomLevelForDisplay === 1 ? 3 : zoomLevelForDisplay === 2 ? 4 : 0.75) * 1.1}vw`
291349

292350
let idx = 0
293351
group.activations.forEach(activation => {

app/plugins/modules/activation-visualizations/lib/legend.js

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,29 @@ const prettyPrintDuration = require('pretty-ms'),
3131
* @param options user options from the CLI
3232
*
3333
*/
34-
exports.drawLegend = (viewName, rightHeader, {statData, errorRate, nFailures}, options) => {
34+
exports.drawLegend = (viewName, rightHeader, {statData, errorRate, nFailures}, gridContainer, options) => {
3535
const existing = rightHeader.querySelector('.grid-header-key'),
3636
wrapper = existing || document.createElement('div'),
3737
existing2 = wrapper.querySelector('.cell-container'),
3838
wrapper2 = existing2 || document.createElement('div')
3939

40+
/** user asked to toggle the latency bucket filter */
41+
const toggleFilter = idx => () => {
42+
// isRemove: user deselected current filter
43+
const isRemove = gridContainer.getAttribute('data-latency-filter') == idx,
44+
containers = [wrapper2, gridContainer] // legend and main grid
45+
46+
containers.forEach(container => {
47+
if (isRemove) {
48+
container.classList.remove('has-latency-filter')
49+
container.removeAttribute('data-latency-filter')
50+
} else {
51+
container.classList.add('has-latency-filter')
52+
container.setAttribute('data-latency-filter', idx)
53+
}
54+
})
55+
}
56+
4057
if (!existing) {
4158
rightHeader.appendChild(wrapper)
4259
wrapper.appendChild(wrapper2)
@@ -79,6 +96,8 @@ exports.drawLegend = (viewName, rightHeader, {statData, errorRate, nFailures}, o
7996

8097
if (onclick) {
8198
cell.onclick = onclick
99+
} else {
100+
cell.classList.add('grid-no-hover')
82101
}
83102

84103
if (labelAsTooltip) {
@@ -109,27 +128,13 @@ exports.drawLegend = (viewName, rightHeader, {statData, errorRate, nFailures}, o
109128
valueDom.innerText = labelValue
110129
}
111130

112-
//
113-
// find the index of the first and last non-zero legend entries
114-
// for the performance squares (latency buckets)
115-
const firstNonZero = statData.latBuckets.findIndex(_ => _ > 0)
116-
let lastNonZero = -1
117-
for (let idx = statData.latBuckets.length - 1; idx >= 0; idx--) {
118-
if (statData.latBuckets[idx] > 0) {
119-
lastNonZero = idx
120-
break
121-
}
122-
}
123-
124131
//
125132
// if we have at least one non-zero performance bucket, then
126133
// render the buckets up to that last non-zero bucket
127134
//
128-
if (lastNonZero >= 0) {
135+
{
129136
latencyBuckets.forEach((latencyRange, idx, A) => {
130-
const isFirstNonZero = idx === firstNonZero,
131-
last = idx === A.length - 1,
132-
isLastNonZero = idx === lastNonZero,
137+
const last = idx === A.length - 1,
133138
lower = idx === 0 ? 0 : A[idx - 1],
134139
upper = latencyRange,
135140
roughlySame = upper - lower < 1000 && (lower < 1000 && upper < 1000 || lower > 1000 && upper > 1000)
@@ -146,14 +151,16 @@ exports.drawLegend = (viewName, rightHeader, {statData, errorRate, nFailures}, o
146151
// number of cells with this coloration
147152
const count = statData.latBuckets[idx]
148153

154+
const opts = { zoom: -1,
155+
useThisLabelInstead: (idx === A.length - 1 ? '>' : '') + (upper >= 500 && upper < 1000 ? `${(upper/1000).toLocaleString()}s` : prettyPrintDuration(upper)),
156+
}
157+
149158
if (count > 0) {
150-
entry(labelText, count, false, idx, // false means not a failure
151-
{ zoom: -1, labelAsTooltip: true,
152-
balloonPos: lastNonZero >= 2 && idx < ~~(nLatencyBuckets / 2) ? 'right' : 'left',
153-
useThisLabelInstead: isFirstNonZero ? `fast` : isLastNonZero ? `slow` : nbsp,
154-
onclick: drilldownWith(viewName, () => repl.pexec(`grid ${optionsToString(options)} --success --latency-bucket ${idx}`))
155-
})
159+
// only add an onclick handler if there is something to filter by
160+
opts.onclick = toggleFilter(idx)
156161
}
162+
163+
entry(labelText, count, false, idx, opts) // false means not a failure
157164
})
158165
}
159166

@@ -162,8 +169,9 @@ exports.drawLegend = (viewName, rightHeader, {statData, errorRate, nFailures}, o
162169
//
163170
entry('these cells represent activation failures',
164171
nFailures,
165-
true, 0, // true means render as failure
172+
true, -1, // true means render as failure
166173
{ zoom: -1, labelAsTooltip: true, useThisLabelInstead: 'fail', balloonPos: 'left', balloonLength: 'medium',
167-
onclick: drilldownWith(viewName, () => repl.pexec(`grid ${optionsToString(options)} --failure`))
174+
//onclick: drilldownWith(viewName, () => repl.pexec(`grid ${optionsToString(options)} --failure`))
175+
onclick: toggleFilter(-1)
168176
})
169177
}

0 commit comments

Comments
 (0)