@data-navigator/inspector 1.0.3 → 1.1.1
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/package.json +4 -2
- package/src/console-menu.js +79 -0
- package/src/force-graph.js +5 -3
- package/src/inspector.js +63 -18
- package/src/menu-events.js +28 -0
- package/src/menu-sections.js +938 -0
- package/src/menu-state.js +89 -0
- package/src/svg-highlight.js +479 -0
- package/src/tree-graph.js +60 -35
- package/style.css +295 -0
package/src/tree-graph.js
CHANGED
|
@@ -8,10 +8,7 @@ import { schemeTableau10 } from 'd3-scale-chromatic';
|
|
|
8
8
|
import { create } from 'd3-selection';
|
|
9
9
|
|
|
10
10
|
export function TreeGraph(
|
|
11
|
-
{
|
|
12
|
-
nodes,
|
|
13
|
-
links
|
|
14
|
-
},
|
|
11
|
+
{ nodes, links },
|
|
15
12
|
{
|
|
16
13
|
nodeId = d => d.id,
|
|
17
14
|
nodeGroup,
|
|
@@ -22,7 +19,8 @@ export function TreeGraph(
|
|
|
22
19
|
height = 400,
|
|
23
20
|
dimensions,
|
|
24
21
|
description,
|
|
25
|
-
hide
|
|
22
|
+
hide,
|
|
23
|
+
idPrefix = ''
|
|
26
24
|
} = {}
|
|
27
25
|
) {
|
|
28
26
|
// Compute values (same pattern as ForceGraph).
|
|
@@ -31,15 +29,19 @@ export function TreeGraph(
|
|
|
31
29
|
|
|
32
30
|
// Build a lookup from node id to original node data.
|
|
33
31
|
const nodeById = {};
|
|
34
|
-
nodes.forEach((n, i) => {
|
|
32
|
+
nodes.forEach((n, i) => {
|
|
33
|
+
nodeById[N[i]] = n;
|
|
34
|
+
});
|
|
35
35
|
|
|
36
36
|
// Replace input nodes with positioned objects.
|
|
37
37
|
nodes = map(nodes, (_, i) => ({ id: N[i], x: 0, y: 0 }));
|
|
38
38
|
const nodeObjById = {};
|
|
39
|
-
nodes.forEach(n => {
|
|
39
|
+
nodes.forEach(n => {
|
|
40
|
+
nodeObjById[n.id] = n;
|
|
41
|
+
});
|
|
40
42
|
|
|
41
43
|
// Map links to source/target ids.
|
|
42
|
-
const linkData = map(links,
|
|
44
|
+
const linkData = map(links, l => ({
|
|
43
45
|
source: intern(l.source),
|
|
44
46
|
target: intern(l.target),
|
|
45
47
|
navigationRules: l.navigationRules || []
|
|
@@ -68,7 +70,9 @@ export function TreeGraph(
|
|
|
68
70
|
// Map each sibling rule name to its pair for splitting merged edges.
|
|
69
71
|
const ruleToPair = {};
|
|
70
72
|
siblingRulePairs.forEach(pair => {
|
|
71
|
-
pair.forEach(r => {
|
|
73
|
+
pair.forEach(r => {
|
|
74
|
+
ruleToPair[r] = pair.join(',');
|
|
75
|
+
});
|
|
72
76
|
});
|
|
73
77
|
|
|
74
78
|
// Classify links. Sibling links with merged rules (multiple pairs)
|
|
@@ -105,7 +109,10 @@ export function TreeGraph(
|
|
|
105
109
|
|
|
106
110
|
nodes.forEach(n => {
|
|
107
111
|
const orig = nodeById[n.id];
|
|
108
|
-
if (!orig) {
|
|
112
|
+
if (!orig) {
|
|
113
|
+
otherNodes.push(n);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
109
116
|
const dl = orig.dimensionLevel;
|
|
110
117
|
const dn = orig.derivedNode;
|
|
111
118
|
if (dl === 0) level0.push(n);
|
|
@@ -139,7 +146,9 @@ export function TreeGraph(
|
|
|
139
146
|
|
|
140
147
|
// Organize level2 nodes by their parent dimension.
|
|
141
148
|
const divisionsByDim = {};
|
|
142
|
-
dimOrder.forEach(key => {
|
|
149
|
+
dimOrder.forEach(key => {
|
|
150
|
+
divisionsByDim[key] = [];
|
|
151
|
+
});
|
|
143
152
|
level2.forEach(n => {
|
|
144
153
|
const orig = nodeById[n.id];
|
|
145
154
|
if (orig && orig.derivedNode && divisionsByDim[orig.derivedNode]) {
|
|
@@ -160,9 +169,10 @@ export function TreeGraph(
|
|
|
160
169
|
const upperSpacing = upperRows > 1 ? upperHeight / (upperRows - 1) : upperHeight;
|
|
161
170
|
const leafGap = usableHeight * 0.05; // small gap between hierarchy and leaves
|
|
162
171
|
const leafYPos = padding.top + upperHeight + leafGap;
|
|
172
|
+
const leafAreaCenter = (leafYPos + height - padding.bottom) / 2;
|
|
163
173
|
|
|
164
174
|
let currentRow = 0;
|
|
165
|
-
const yFor =
|
|
175
|
+
const yFor = row => padding.top + row * upperSpacing;
|
|
166
176
|
|
|
167
177
|
// Level 0
|
|
168
178
|
if (hasLevel0) {
|
|
@@ -215,13 +225,13 @@ export function TreeGraph(
|
|
|
215
225
|
// Leaf nodes
|
|
216
226
|
const leafY = leafYPos;
|
|
217
227
|
if (dimOrder.length === 0 || !dimensions) {
|
|
218
|
-
// No dimensions — flat row
|
|
228
|
+
// No dimensions — flat row, centered in leaf area
|
|
219
229
|
leafNodes.forEach((n, i) => {
|
|
220
230
|
n.x = padding.left + (usableWidth / (leafNodes.length + 1)) * (i + 1);
|
|
221
|
-
n.y =
|
|
231
|
+
n.y = leafAreaCenter;
|
|
222
232
|
});
|
|
223
233
|
} else if (dimOrder.length === 1) {
|
|
224
|
-
// Single dimension — spread leaves under their division parent
|
|
234
|
+
// Single dimension — spread leaves under their division parent, centered in leaf area
|
|
225
235
|
const key = dimOrder[0];
|
|
226
236
|
const dim = dimensions[key];
|
|
227
237
|
const divIds = Object.keys(dim.divisions || {});
|
|
@@ -235,22 +245,30 @@ export function TreeGraph(
|
|
|
235
245
|
const n = nodeObjById[leafId];
|
|
236
246
|
if (n) {
|
|
237
247
|
n.x = parentX - spreadWidth / 2 + leafSpacing * (i + 1);
|
|
238
|
-
n.y =
|
|
248
|
+
n.y = leafAreaCenter;
|
|
239
249
|
}
|
|
240
250
|
});
|
|
241
251
|
});
|
|
242
252
|
} else {
|
|
243
253
|
// 2+ dimensions — grid layout
|
|
244
|
-
//
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
+
// Prefer horizontal spread: ensure cols >= rows by swapping dims if needed.
|
|
255
|
+
// This prevents single-column layouts (e.g. when dim1 is compressed to 1 division).
|
|
256
|
+
let dim1Key = dimOrder[0];
|
|
257
|
+
let dim2Key = dimOrder[1];
|
|
258
|
+
let dim1 = dimensions[dim1Key];
|
|
259
|
+
let dim2 = dimensions[dim2Key];
|
|
260
|
+
let div1Ids = Object.keys(dim1.divisions || {});
|
|
261
|
+
let div2Ids = Object.keys(dim2.divisions || {});
|
|
262
|
+
|
|
263
|
+
let cols = div1Ids.length || 1;
|
|
264
|
+
let rows = div2Ids.length || 1;
|
|
265
|
+
|
|
266
|
+
if (cols < rows) {
|
|
267
|
+
[dim1Key, dim2Key] = [dim2Key, dim1Key];
|
|
268
|
+
[dim1, dim2] = [dim2, dim1];
|
|
269
|
+
[div1Ids, div2Ids] = [div2Ids, div1Ids];
|
|
270
|
+
[cols, rows] = [rows, cols];
|
|
271
|
+
}
|
|
254
272
|
|
|
255
273
|
// Grid occupies the full leaf area
|
|
256
274
|
const gridTop = leafY;
|
|
@@ -298,7 +316,7 @@ export function TreeGraph(
|
|
|
298
316
|
|
|
299
317
|
// Other nodes (exit, etc.) — placed bottom-right
|
|
300
318
|
otherNodes.forEach((n, i) => {
|
|
301
|
-
n.x = width - padding.right -
|
|
319
|
+
n.x = width - padding.right - i * nodeRadius * 3;
|
|
302
320
|
n.y = height - padding.bottom;
|
|
303
321
|
});
|
|
304
322
|
|
|
@@ -316,17 +334,21 @@ export function TreeGraph(
|
|
|
316
334
|
// Wrap-around (circular) siblings arc below/right with a larger curve.
|
|
317
335
|
// When multiple edges share the same node pair, each gets a larger arc.
|
|
318
336
|
const edgePairCounts = {};
|
|
319
|
-
const siblingArc =
|
|
337
|
+
const siblingArc = d => {
|
|
320
338
|
const s = nodeObjById[d.source];
|
|
321
339
|
const t = nodeObjById[d.target];
|
|
322
340
|
if (!s || !t) return '';
|
|
323
|
-
const sx = s.x,
|
|
324
|
-
|
|
341
|
+
const sx = s.x,
|
|
342
|
+
sy = s.y,
|
|
343
|
+
tx = t.x,
|
|
344
|
+
ty = t.y;
|
|
345
|
+
const dx = tx - sx,
|
|
346
|
+
dy = ty - sy;
|
|
325
347
|
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
326
348
|
const isHorizontal = Math.abs(dx) > Math.abs(dy);
|
|
327
349
|
// Detect wrap-around: source is to the right of target (horizontal)
|
|
328
350
|
// or below target (vertical) — these are circular edges.
|
|
329
|
-
const isWrap = isHorizontal ?
|
|
351
|
+
const isWrap = isHorizontal ? sx > tx : sy > ty;
|
|
330
352
|
// Track regular and wrap arcs separately — they curve in opposite
|
|
331
353
|
// directions so only same-direction arcs need stacking.
|
|
332
354
|
const pairKey = [d.source, d.target].sort().join('::') + (isWrap ? '::wrap' : '::reg');
|
|
@@ -353,6 +375,7 @@ export function TreeGraph(
|
|
|
353
375
|
.selectAll('path')
|
|
354
376
|
.data(siblingLinks)
|
|
355
377
|
.join('path')
|
|
378
|
+
.attr('id', d => idPrefix + 'svgedge' + d.source + '-' + d.target + '-' + (d.navigationRules?.[0] || ''))
|
|
356
379
|
.attr('d', siblingArc)
|
|
357
380
|
.attr('fill', 'none')
|
|
358
381
|
.attr('stroke', '#888')
|
|
@@ -366,6 +389,7 @@ export function TreeGraph(
|
|
|
366
389
|
.selectAll('line')
|
|
367
390
|
.data(parentChildLinks)
|
|
368
391
|
.join('line')
|
|
392
|
+
.attr('id', d => idPrefix + 'svgedge' + d.source + '-' + d.target)
|
|
369
393
|
.attr('x1', d => nodeObjById[d.source]?.x || 0)
|
|
370
394
|
.attr('y1', d => nodeObjById[d.source]?.y || 0)
|
|
371
395
|
.attr('x2', d => nodeObjById[d.target]?.x || 0)
|
|
@@ -389,10 +413,11 @@ export function TreeGraph(
|
|
|
389
413
|
.attr('r', nodeRadius)
|
|
390
414
|
.attr('role', 'presentation');
|
|
391
415
|
|
|
392
|
-
if (G)
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
416
|
+
if (G)
|
|
417
|
+
node.attr('fill', ({ id }) => {
|
|
418
|
+
const idx = N.indexOf(id);
|
|
419
|
+
return idx >= 0 ? color(G[idx]) : '#999';
|
|
420
|
+
});
|
|
396
421
|
|
|
397
422
|
function intern(value) {
|
|
398
423
|
return value !== null && typeof value === 'object' ? value.valueOf() : value;
|
package/style.css
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
/* Inspector default styles */
|
|
2
|
+
|
|
3
|
+
.dn-inspector-wrapper {
|
|
4
|
+
position: relative;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.dn-inspector-hidden {
|
|
8
|
+
display: none;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.dn-inspector-tooltip {
|
|
12
|
+
position: absolute;
|
|
13
|
+
padding: 10px;
|
|
14
|
+
background-color: white;
|
|
15
|
+
border: 1px solid black;
|
|
16
|
+
width: 200px;
|
|
17
|
+
top: 0;
|
|
18
|
+
left: 0;
|
|
19
|
+
font-size: 0.85em;
|
|
20
|
+
pointer-events: none;
|
|
21
|
+
z-index: 10;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.dn-inspector-focus-indicator {
|
|
25
|
+
pointer-events: none;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.dn-inspector-node {
|
|
29
|
+
position: absolute;
|
|
30
|
+
padding: 0px;
|
|
31
|
+
margin: 0px;
|
|
32
|
+
overflow: visible;
|
|
33
|
+
pointer-events: none;
|
|
34
|
+
background: transparent;
|
|
35
|
+
border: none;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.dn-inspector-node:focus {
|
|
39
|
+
outline: 2px solid #1e3369;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* Dynamic edges drawn on-demand for virtual/excluded edges */
|
|
43
|
+
.dn-inspector-dynamic-edge {
|
|
44
|
+
pointer-events: none;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* ==== Console Menu ====
|
|
48
|
+
*
|
|
49
|
+
* All selectors below use `.dn-inspector-menu` as a prefix for two reasons:
|
|
50
|
+
* 1. Scoping — these styles only affect the inspector menu
|
|
51
|
+
* 2. Specificity — overrides global UI framework styles (e.g. VitePress sets
|
|
52
|
+
* `summary { min-height: 44px }` and `button { min-width: 44px }`)
|
|
53
|
+
*/
|
|
54
|
+
|
|
55
|
+
.dn-inspector-menu {
|
|
56
|
+
min-width: 500px;
|
|
57
|
+
max-height: 450px;
|
|
58
|
+
overflow-y: auto;
|
|
59
|
+
border: 1px solid #ccc;
|
|
60
|
+
font-size: 12px;
|
|
61
|
+
font-family: monospace;
|
|
62
|
+
line-height: 1.3;
|
|
63
|
+
padding: 2px;
|
|
64
|
+
margin-top: 4px;
|
|
65
|
+
background: #fff;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/* Hidden utility */
|
|
69
|
+
.dn-inspector-menu .dn-menu-hidden {
|
|
70
|
+
display: none !important;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/* ---- Caret summaries ---- */
|
|
74
|
+
|
|
75
|
+
.dn-inspector-menu summary {
|
|
76
|
+
cursor: pointer;
|
|
77
|
+
user-select: none;
|
|
78
|
+
list-style: none;
|
|
79
|
+
display: flex;
|
|
80
|
+
align-items: center;
|
|
81
|
+
min-height: 24px;
|
|
82
|
+
padding: 0 2px;
|
|
83
|
+
border: none;
|
|
84
|
+
border-radius: 0;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.dn-inspector-menu summary::-webkit-details-marker {
|
|
88
|
+
display: none;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.dn-inspector-menu summary:focus {
|
|
92
|
+
outline: 2px solid #1e3369;
|
|
93
|
+
outline-offset: 0;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* Group checkbox inside summary */
|
|
97
|
+
.dn-inspector-menu .dn-menu-group-checkbox {
|
|
98
|
+
margin: 0 2px 0 0;
|
|
99
|
+
flex-shrink: 0;
|
|
100
|
+
width: 13px;
|
|
101
|
+
height: 13px;
|
|
102
|
+
min-width: 13px;
|
|
103
|
+
min-height: 13px;
|
|
104
|
+
padding: 0;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/* Caret indicator */
|
|
108
|
+
.dn-inspector-menu summary::before {
|
|
109
|
+
content: '\25B8'; /* right-pointing triangle */
|
|
110
|
+
display: inline-flex;
|
|
111
|
+
align-items: center;
|
|
112
|
+
justify-content: center;
|
|
113
|
+
width: 24px;
|
|
114
|
+
min-width: 24px;
|
|
115
|
+
height: 24px;
|
|
116
|
+
font-size: 10px;
|
|
117
|
+
flex-shrink: 0;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.dn-inspector-menu details[open] > summary::before {
|
|
121
|
+
content: '\25BE'; /* down-pointing triangle */
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/* Top-level section summaries */
|
|
125
|
+
.dn-inspector-menu .dn-menu-summary-top {
|
|
126
|
+
font-weight: bold;
|
|
127
|
+
font-size: 12px;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/* Nested indentation: each details level indents its contents */
|
|
131
|
+
.dn-inspector-menu details details {
|
|
132
|
+
padding-left: 12px;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.dn-inspector-menu details details details {
|
|
136
|
+
padding-left: 12px;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/* ---- Menu items (checkbox + label + log btn) ---- */
|
|
140
|
+
|
|
141
|
+
.dn-inspector-menu .dn-menu-item {
|
|
142
|
+
display: flex;
|
|
143
|
+
align-items: center;
|
|
144
|
+
gap: 3px;
|
|
145
|
+
padding: 0 2px 0 6px;
|
|
146
|
+
min-height: 20px;
|
|
147
|
+
cursor: default;
|
|
148
|
+
margin-left: 32px;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.dn-inspector-menu .dn-menu-item:hover {
|
|
152
|
+
background: #f0f0f0;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.dn-inspector-menu .dn-menu-item input[type='checkbox'] {
|
|
156
|
+
margin: 0;
|
|
157
|
+
flex-shrink: 0;
|
|
158
|
+
width: 13px;
|
|
159
|
+
height: 13px;
|
|
160
|
+
min-width: 13px;
|
|
161
|
+
min-height: 13px;
|
|
162
|
+
padding: 0;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.dn-inspector-menu .dn-menu-item-label {
|
|
166
|
+
flex: 1;
|
|
167
|
+
overflow: hidden;
|
|
168
|
+
text-overflow: ellipsis;
|
|
169
|
+
white-space: nowrap;
|
|
170
|
+
font-size: 11px;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.dn-inspector-menu .dn-menu-log-btn {
|
|
174
|
+
font-size: 10px;
|
|
175
|
+
font-family: monospace;
|
|
176
|
+
padding: 0 4px;
|
|
177
|
+
line-height: 16px;
|
|
178
|
+
min-width: auto;
|
|
179
|
+
min-height: auto;
|
|
180
|
+
cursor: pointer;
|
|
181
|
+
border: 1px solid #bbb;
|
|
182
|
+
background: #f5f5f5;
|
|
183
|
+
border-radius: 2px;
|
|
184
|
+
flex-shrink: 0;
|
|
185
|
+
color: #555;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.dn-inspector-menu .dn-menu-log-btn:hover {
|
|
189
|
+
background: #e0e0e0;
|
|
190
|
+
color: #222;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.dn-inspector-menu .dn-menu-log-btn:focus {
|
|
194
|
+
outline: 2px solid #1e3369;
|
|
195
|
+
outline-offset: 0;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/* Console clear button */
|
|
199
|
+
.dn-inspector-menu .dn-menu-clear-btn {
|
|
200
|
+
margin-left: auto;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/* ---- Informational items (no checkbox) ---- */
|
|
204
|
+
|
|
205
|
+
.dn-inspector-menu .dn-menu-info {
|
|
206
|
+
padding: 0 2px 0 30px;
|
|
207
|
+
font-size: 11px;
|
|
208
|
+
min-height: 18px;
|
|
209
|
+
display: flex;
|
|
210
|
+
align-items: center;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.dn-inspector-menu .dn-menu-empty {
|
|
214
|
+
color: #999;
|
|
215
|
+
font-style: italic;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/* ---- Data / Props pre blocks ---- */
|
|
219
|
+
|
|
220
|
+
.dn-inspector-menu .dn-menu-pre {
|
|
221
|
+
max-height: 150px;
|
|
222
|
+
overflow: auto;
|
|
223
|
+
font-size: 10px;
|
|
224
|
+
margin: 2px 0 2px 30px;
|
|
225
|
+
padding: 3px;
|
|
226
|
+
background: #fafafa;
|
|
227
|
+
border: 1px solid #eee;
|
|
228
|
+
white-space: pre-wrap;
|
|
229
|
+
word-break: break-word;
|
|
230
|
+
line-height: 1.25;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/* ---- Console section ---- */
|
|
234
|
+
|
|
235
|
+
.dn-inspector-menu .dn-menu-console-list {
|
|
236
|
+
max-height: 180px;
|
|
237
|
+
overflow-y: auto;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.dn-inspector-menu .dn-menu-console-list:empty::after {
|
|
241
|
+
content: '(empty)';
|
|
242
|
+
color: #999;
|
|
243
|
+
padding: 2px 30px;
|
|
244
|
+
display: block;
|
|
245
|
+
font-style: italic;
|
|
246
|
+
font-size: 11px;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
.dn-inspector-menu .dn-menu-console-entry {
|
|
250
|
+
border-bottom: 1px solid #f0f0f0;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/* ---- Inline expandable arrays ---- */
|
|
254
|
+
|
|
255
|
+
.dn-inspector-menu .dn-menu-inline-array {
|
|
256
|
+
font-size: 11px;
|
|
257
|
+
padding: 0 2px;
|
|
258
|
+
min-height: 18px;
|
|
259
|
+
display: flex;
|
|
260
|
+
align-items: baseline;
|
|
261
|
+
flex-wrap: wrap;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
.dn-inspector-menu .dn-menu-array-toggle {
|
|
265
|
+
color: #666;
|
|
266
|
+
cursor: pointer;
|
|
267
|
+
flex-shrink: 0;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.dn-inspector-menu .dn-menu-array-collapsed {
|
|
271
|
+
color: #08c;
|
|
272
|
+
cursor: pointer;
|
|
273
|
+
text-decoration: underline;
|
|
274
|
+
text-decoration-style: dotted;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.dn-inspector-menu .dn-menu-array-collapsed:hover {
|
|
278
|
+
color: #005fa3;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.dn-inspector-menu .dn-menu-array-expanded {
|
|
282
|
+
color: #333;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.dn-inspector-menu .dn-menu-array-chip {
|
|
286
|
+
color: #08c;
|
|
287
|
+
cursor: default;
|
|
288
|
+
padding: 0 1px;
|
|
289
|
+
border-radius: 2px;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.dn-inspector-menu .dn-menu-array-chip:hover {
|
|
293
|
+
background: #e8f4fd;
|
|
294
|
+
color: #005fa3;
|
|
295
|
+
}
|