dsh-xray 0.7.0 → 0.7.2
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/lib/client.js +29 -11
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -17,19 +17,31 @@ window.__ModuleLoader__.load({
|
|
|
17
17
|
|
|
18
18
|
//#region styles (host design tokens; auto-claimed by client-modules)
|
|
19
19
|
const css = [
|
|
20
|
-
|
|
20
|
+
// The panel owns its scrolling: the tab fills the host view area
|
|
21
|
+
// (flex column, overflow on .xray-body) instead of growing inside the
|
|
22
|
+
// host scrollport. View switches then change only the inner scroll
|
|
23
|
+
// height — the page-level scroll position never jumps, however tall
|
|
24
|
+
// deps/cost render. .xray-stale dims outgoing content while the next
|
|
25
|
+
// payload is in flight.
|
|
26
|
+
'.xray-panel{flex:1;min-height:0;display:flex;flex-direction:column;max-width:920px;width:100%;margin:0 auto;padding:16px 20px;font-size:13px;color:var(--dsw-alias-label-primary)}',
|
|
27
|
+
'.xray-body{flex:1;min-height:0;overflow-y:auto}',
|
|
28
|
+
'.xray-stale{opacity:.45;transition:opacity .15s;pointer-events:none}',
|
|
21
29
|
'.xray-sub{color:var(--dsw-alias-label-tertiary);margin:0 0 14px;font-size:12px}',
|
|
22
30
|
'.xray-nav{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:12px}',
|
|
23
31
|
'.xray-nav button{font:inherit;font-size:12px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-3);border:1px solid var(--dsw-alias-border-l2);border-radius:6px;padding:4px 10px;cursor:pointer}',
|
|
24
|
-
'.xray-nav button
|
|
32
|
+
'.xray-nav button:hover{background:var(--dsw-alias-interactive-bg-hover)}',
|
|
33
|
+
// active view: the host's own tab-active family (state-business-*) —
|
|
34
|
+
// visible in both themes, unlike brand-primary which resolves to
|
|
35
|
+
// near-white in dark mode (white-on-white active buttons).
|
|
36
|
+
'.xray-nav button.active{color:var(--dsw-alias-state-business-primary);background:var(--dsw-alias-state-business-tertiary);border-color:var(--dsw-alias-state-business-primary);font-weight:600}',
|
|
25
37
|
'.xray-table{border-collapse:collapse;width:100%;margin-top:6px}',
|
|
26
38
|
'.xray-table th,.xray-table td{text-align:left;padding:4px 10px;border-bottom:1px solid var(--dsw-alias-border-l2);vertical-align:top}',
|
|
27
39
|
'.xray-table th{color:var(--dsw-alias-label-tertiary);font-weight:normal}',
|
|
28
40
|
'.xray-num{text-align:right}',
|
|
29
|
-
'.xray-warn{color:var(--dsw-alias-
|
|
30
|
-
'.xray-ok{color:var(--dsw-alias-
|
|
41
|
+
'.xray-warn{color:var(--dsw-alias-state-error-primary,#f85149)}',
|
|
42
|
+
'.xray-ok{color:var(--dsw-alias-state-success-primary,#3fb950)}',
|
|
31
43
|
'.xray-muted{color:var(--dsw-alias-label-tertiary)}',
|
|
32
|
-
'.xray-bar{background:var(--dsw-alias-
|
|
44
|
+
'.xray-bar{background:var(--dsw-alias-state-business-primary);height:10px;border-radius:2px;display:inline-block}',
|
|
33
45
|
'.xray-h3{font-size:13px;font-weight:600;margin:14px 0 4px}',
|
|
34
46
|
].join('\n');
|
|
35
47
|
if (
|
|
@@ -260,11 +272,13 @@ window.__ModuleLoader__.load({
|
|
|
260
272
|
// `for` stamps which view the payload belongs to: a click flips `view`
|
|
261
273
|
// synchronously while `state` still carries the previous view's data —
|
|
262
274
|
// rendering that mismatch with the new renderer throws and unmounts
|
|
263
|
-
// the whole tab.
|
|
275
|
+
// the whole tab. During the switch the previous view's content stays
|
|
276
|
+
// up (dimmed) and is replaced in one paint when the payload lands, so
|
|
277
|
+
// the layout changes once per click instead of collapsing to a
|
|
278
|
+
// one-line loading row and re-expanding.
|
|
264
279
|
const [state, setState] = react.useState({ phase: 'loading', for: 'summary' });
|
|
265
280
|
react.useEffect(() => {
|
|
266
281
|
let alive = true;
|
|
267
|
-
setState({ phase: 'loading', for: view });
|
|
268
282
|
fetch(`/xray/api/${view}`)
|
|
269
283
|
.then(async (res) => {
|
|
270
284
|
if (!res.ok) throw new Error(await res.text());
|
|
@@ -285,15 +299,18 @@ window.__ModuleLoader__.load({
|
|
|
285
299
|
alive = false;
|
|
286
300
|
};
|
|
287
301
|
}, [view]);
|
|
302
|
+
// Render whatever payload we HAVE (state.for), not the view the user
|
|
303
|
+
// just requested — the stale content keeps the scaffold stable while
|
|
304
|
+
// the fresh payload is in flight.
|
|
305
|
+
const settled = state.phase !== 'loading';
|
|
288
306
|
let body;
|
|
289
|
-
if (
|
|
290
|
-
body = h('p', { className: 'xray-muted' }, `loading ${view}…`);
|
|
307
|
+
if (!settled) body = h('p', { className: 'xray-muted' }, `loading ${view}…`);
|
|
291
308
|
else if (state.phase === 'error') body = h('p', { className: 'xray-warn' }, state.message);
|
|
292
309
|
else {
|
|
293
310
|
// A diagnostic surface must never take itself down on one bad
|
|
294
311
|
// payload: render the failure, keep the tab and its nav alive.
|
|
295
312
|
try {
|
|
296
|
-
body = renderers[
|
|
313
|
+
body = renderers[state.for](state.data);
|
|
297
314
|
} catch (err) {
|
|
298
315
|
body = h(
|
|
299
316
|
'p',
|
|
@@ -302,6 +319,7 @@ window.__ModuleLoader__.load({
|
|
|
302
319
|
);
|
|
303
320
|
}
|
|
304
321
|
}
|
|
322
|
+
const stale = settled && state.for !== view;
|
|
305
323
|
return h(
|
|
306
324
|
'div',
|
|
307
325
|
{ className: 'xray-panel' },
|
|
@@ -317,7 +335,7 @@ window.__ModuleLoader__.load({
|
|
|
317
335
|
),
|
|
318
336
|
),
|
|
319
337
|
),
|
|
320
|
-
body,
|
|
338
|
+
h('div', { className: stale ? 'xray-body xray-stale' : 'xray-body' }, body),
|
|
321
339
|
);
|
|
322
340
|
}
|
|
323
341
|
//#endregion
|