@yuneta/gobj-ui 7.20.1 → 7.21.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/README.md +54 -27
- package/dist/gobj-ui.cjs.js +230 -63
- package/dist/gobj-ui.es.js +230 -63
- package/package.json +1 -1
- package/src/c_yui_json.css +78 -1
- package/src/c_yui_json.js +265 -72
- package/src/c_yui_json_graph.js +8 -0
package/src/c_yui_json.js
CHANGED
|
@@ -24,19 +24,32 @@
|
|
|
24
24
|
* Only expanded containers are materialised in the DOM, so the tree
|
|
25
25
|
* stays bounded no matter how large the source document is.
|
|
26
26
|
*
|
|
27
|
-
*
|
|
28
|
-
* attr ("tree" | "text") and switched from the
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
27
|
+
* THREE VIEWS over the same working document, chosen by the
|
|
28
|
+
* `view_mode` attr ("tree" | "text" | "graph") and switched from the
|
|
29
|
+
* toolbar. They answer three different questions:
|
|
30
|
+
*
|
|
31
|
+
* tree where is this value, and what is around it — the lazy
|
|
32
|
+
* view above, the only one that can drill.
|
|
33
|
+
* text what does this document SAY, verbatim — for reading it
|
|
34
|
+
* as written, selecting a slab of it, or searching it with
|
|
35
|
+
* the browser's own Ctrl+F.
|
|
36
|
+
* graph what SHAPE is this — nesting as a hierarchy, drawn by a
|
|
37
|
+
* hosted C_YUI_JSON_GRAPH child (AntV/G6).
|
|
38
|
+
*
|
|
39
|
+
* Neither text nor graph is lazy: both show what the client currently
|
|
40
|
+
* holds, `__collapsed__` sentinels included, because that is honestly
|
|
41
|
+
* what it has. Drill in the tree and they grow with it. The
|
|
34
42
|
* tree-only controls (search, expand/collapse) hide with the tree.
|
|
35
43
|
*
|
|
44
|
+
* The graph child is built on FIRST entry into graph mode and not
|
|
45
|
+
* before: G6 measures its container, so a graph created behind
|
|
46
|
+
* `is-hidden` comes up 0x0, and a viewer nobody switches to graph
|
|
47
|
+
* should not pay for a canvas.
|
|
48
|
+
*
|
|
36
49
|
* DOM is self-describing (UPPER_SNAKE logical classes): JSON_VIEWER /
|
|
37
|
-
* JSON_TOOLBAR / JSON_SEARCH /
|
|
38
|
-
*
|
|
39
|
-
* JSON_COLLAPSED / JSON_TIME.
|
|
50
|
+
* JSON_TOOLBAR / JSON_SEARCH / JSON_VIEW_SWITCH / JSON_VIEW_MODE /
|
|
51
|
+
* JSON_TREE / JSON_TEXT / JSON_TEXT_BODY / JSON_GRAPH / JSON_ROW /
|
|
52
|
+
* JSON_KEY / JSON_VALUE / JSON_SUMMARY / JSON_COLLAPSED / JSON_TIME.
|
|
40
53
|
*
|
|
41
54
|
* Copyright (c) 2026, ArtGins.
|
|
42
55
|
* All Rights Reserved.
|
|
@@ -52,6 +65,14 @@ import {
|
|
|
52
65
|
gobj_read_pointer_attr,
|
|
53
66
|
gobj_parent,
|
|
54
67
|
gobj_subscribe_event,
|
|
68
|
+
gobj_create_pure_child,
|
|
69
|
+
gobj_start,
|
|
70
|
+
gobj_stop,
|
|
71
|
+
gobj_destroy,
|
|
72
|
+
gobj_is_running,
|
|
73
|
+
gobj_short_name,
|
|
74
|
+
clean_name,
|
|
75
|
+
gobj_name,
|
|
55
76
|
gobj_read_attr,
|
|
56
77
|
gobj_write_attr,
|
|
57
78
|
gobj_read_str_attr,
|
|
@@ -76,6 +97,7 @@ import {
|
|
|
76
97
|
} from "./json_view_helpers.js";
|
|
77
98
|
|
|
78
99
|
import {yui_toolbar} from "./yui_toolbar.js";
|
|
100
|
+
import {register_c_yui_json_graph} from "./c_yui_json_graph.js";
|
|
79
101
|
import {attach_clear} from "./yui_inputs.js";
|
|
80
102
|
|
|
81
103
|
import {t} from "i18next";
|
|
@@ -103,6 +125,18 @@ const MAX_RENDER_ROWS = 5000;
|
|
|
103
125
|
*/
|
|
104
126
|
const MAX_TEXT_CHARS = 2000000;
|
|
105
127
|
|
|
128
|
+
/*
|
|
129
|
+
* The three views, in switch order — this table IS the order of the
|
|
130
|
+
* buttons, and EV_SET_VIEW_MODE with no mode advances along it.
|
|
131
|
+
* Note the order is not the default: the viewer still OPENS on the
|
|
132
|
+
* tree (the `view_mode` attr default), it just sits second in the row.
|
|
133
|
+
*/
|
|
134
|
+
const VIEWS = [
|
|
135
|
+
{mode: "text", icon: "yi-code", key: "text view"},
|
|
136
|
+
{mode: "tree", icon: "yi-sitemap", key: "tree view"},
|
|
137
|
+
{mode: "graph", icon: "yi-hexagon-nodes", key: "graph view"},
|
|
138
|
+
];
|
|
139
|
+
|
|
106
140
|
/***************************************************************
|
|
107
141
|
* Data
|
|
108
142
|
***************************************************************/
|
|
@@ -113,7 +147,7 @@ SDATA(data_type_t.DTP_POINTER, "subscriber", 0, null, "Subscriber of outpu
|
|
|
113
147
|
/*---------------- Config ----------------*/
|
|
114
148
|
SDATA(data_type_t.DTP_STRING, "title", 0, "", "Optional header title (i18n key)"),
|
|
115
149
|
SDATA(data_type_t.DTP_JSON, "json_data", 0, null, "Initial JSON to render (usually already collapsed)"),
|
|
116
|
-
SDATA(data_type_t.DTP_STRING, "view_mode", 0, "tree", "View: 'tree' (lazy tree)
|
|
150
|
+
SDATA(data_type_t.DTP_STRING, "view_mode", 0, "tree", "View: 'tree' (lazy tree), 'text' (raw dump) or 'graph'"),
|
|
117
151
|
|
|
118
152
|
/*---------------- UI ----------------*/
|
|
119
153
|
SDATA(data_type_t.DTP_POINTER, "$container", 0, null, "HTMLElement root, mounted by the parent"),
|
|
@@ -129,11 +163,13 @@ let PRIVATE_DATA = {
|
|
|
129
163
|
$tree: null, // the scrollable tree body element
|
|
130
164
|
$text: null, // the scrollable text body element
|
|
131
165
|
$text_body: null, // the <pre> inside it
|
|
166
|
+
$graph: null, // the graph body element
|
|
167
|
+
graph_gobj: null, // hosted C_YUI_JSON_GRAPH child (built on first use)
|
|
132
168
|
$search: null, // the search input element
|
|
133
169
|
$search_ctl: null, // the search control (hidden in text view)
|
|
134
170
|
$expand_btn: null, // "expand loaded" button (hidden in text view)
|
|
135
171
|
$collapse_btn: null,// "collapse all" button (hidden in text view)
|
|
136
|
-
$
|
|
172
|
+
$mode_btns: null, // Map<mode, HTMLElement> of the view switch
|
|
137
173
|
};
|
|
138
174
|
|
|
139
175
|
let __gclass__ = null;
|
|
@@ -198,6 +234,7 @@ function mt_stop(gobj)
|
|
|
198
234
|
***************************************************************/
|
|
199
235
|
function mt_destroy(gobj)
|
|
200
236
|
{
|
|
237
|
+
teardown_graph_child(gobj);
|
|
201
238
|
destroy_ui(gobj);
|
|
202
239
|
}
|
|
203
240
|
|
|
@@ -227,7 +264,12 @@ function build_ui(gobj)
|
|
|
227
264
|
['div', {class: 'JSON_TREE is-flex-grow-1',
|
|
228
265
|
style: 'flex:1 1 auto; min-height:0; overflow:auto;'}, []],
|
|
229
266
|
['div', {class: 'JSON_TEXT is-flex-grow-1 is-hidden',
|
|
230
|
-
style: 'flex:1 1 auto; min-height:0; overflow:auto;'}, []]
|
|
267
|
+
style: 'flex:1 1 auto; min-height:0; overflow:auto;'}, []],
|
|
268
|
+
/* The graph body does NOT scroll: G6 owns its viewport and
|
|
269
|
+
* pans inside it. An `overflow:auto` here would give the
|
|
270
|
+
* canvas a second, competing scroller. */
|
|
271
|
+
['div', {class: 'JSON_GRAPH is-flex-grow-1 is-hidden',
|
|
272
|
+
style: 'flex:1 1 auto; overflow:hidden;'}, []]
|
|
231
273
|
]]
|
|
232
274
|
);
|
|
233
275
|
|
|
@@ -238,7 +280,14 @@ function build_ui(gobj)
|
|
|
238
280
|
priv.$search_ctl = $container.querySelector('.JSON_SEARCH_CONTROL');
|
|
239
281
|
priv.$expand_btn = $container.querySelector('.EV_EXPAND_ALL');
|
|
240
282
|
priv.$collapse_btn = $container.querySelector('.EV_COLLAPSE_ALL');
|
|
241
|
-
priv.$
|
|
283
|
+
priv.$graph = $container.querySelector('.JSON_GRAPH');
|
|
284
|
+
priv.$mode_btns = new Map();
|
|
285
|
+
VIEWS.forEach(function(v) {
|
|
286
|
+
let $btn = $container.querySelector('.JSON_VIEW_MODE_' + v.mode.toUpperCase());
|
|
287
|
+
if($btn) {
|
|
288
|
+
priv.$mode_btns.set(v.mode, $btn);
|
|
289
|
+
}
|
|
290
|
+
});
|
|
242
291
|
|
|
243
292
|
refresh_language($container, t);
|
|
244
293
|
}
|
|
@@ -259,11 +308,12 @@ function destroy_ui(gobj)
|
|
|
259
308
|
priv.$tree = null;
|
|
260
309
|
priv.$text = null;
|
|
261
310
|
priv.$text_body = null;
|
|
311
|
+
priv.$graph = null;
|
|
262
312
|
priv.$search = null;
|
|
263
313
|
priv.$search_ctl = null;
|
|
264
314
|
priv.$expand_btn = null;
|
|
265
315
|
priv.$collapse_btn = null;
|
|
266
|
-
priv.$
|
|
316
|
+
priv.$mode_btns = null;
|
|
267
317
|
}
|
|
268
318
|
|
|
269
319
|
/************************************************************
|
|
@@ -277,7 +327,11 @@ function make_toolbar(gobj)
|
|
|
277
327
|
let left_items = [];
|
|
278
328
|
if(title) {
|
|
279
329
|
left_items.push(
|
|
280
|
-
|
|
330
|
+
/* is-hidden-mobile: on a phone the toolbar cannot hold the
|
|
331
|
+
* title AND a usable search AND six buttons, and the title
|
|
332
|
+
* is the one thing the host usually repeats — a dialog
|
|
333
|
+
* header, a card heading — so it is the one that goes. */
|
|
334
|
+
['span', {class: 'JSON_TITLE is-flex is-align-items-center px-2 is-hidden-mobile',
|
|
281
335
|
style: 'font-weight:600;', 'data-i18n': title}, title]
|
|
282
336
|
);
|
|
283
337
|
}
|
|
@@ -301,7 +355,7 @@ function make_toolbar(gobj)
|
|
|
301
355
|
left_items.push($search_control);
|
|
302
356
|
|
|
303
357
|
let right_items = [
|
|
304
|
-
|
|
358
|
+
view_mode_switch(gobj),
|
|
305
359
|
icon_button(gobj, "yi-chevron-right", "EV_EXPAND_ALL", "expand loaded"),
|
|
306
360
|
icon_button(gobj, "yi-chevron-right", "EV_COLLAPSE_ALL", "collapse all"),
|
|
307
361
|
icon_button(gobj, "yi-copy", "EV_COPY_ALL", "copy json"),
|
|
@@ -337,34 +391,64 @@ function icon_button(gobj, icon, event_name, label_key)
|
|
|
337
391
|
}
|
|
338
392
|
|
|
339
393
|
/************************************************************
|
|
340
|
-
* The
|
|
394
|
+
* The view switch: one button per view, the current one marked.
|
|
341
395
|
*
|
|
342
|
-
*
|
|
343
|
-
*
|
|
344
|
-
*
|
|
345
|
-
*
|
|
346
|
-
*
|
|
396
|
+
* Three views do not fit a toggle. A button that CYCLES cannot be
|
|
397
|
+
* aimed — reaching the graph from the tree would mean passing
|
|
398
|
+
* through the text and rebuilding it on the way — so each view gets
|
|
399
|
+
* its own button and says where it goes. apply_view_mode() moves
|
|
400
|
+
* the `is-active` mark.
|
|
347
401
|
************************************************************/
|
|
348
|
-
function
|
|
402
|
+
function view_mode_switch(gobj)
|
|
349
403
|
{
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
404
|
+
let buttons = VIEWS.map(function(v) {
|
|
405
|
+
let label = view_label(v.mode);
|
|
406
|
+
return ['button', {class: `button JSON_VIEW_MODE JSON_VIEW_MODE_${v.mode.toUpperCase()}`,
|
|
407
|
+
type: 'button', style: 'width:2.5em;',
|
|
408
|
+
title: label, 'data-i18n-title': v.key,
|
|
409
|
+
'aria-label': label, 'data-i18n-aria-label': v.key}, [
|
|
410
|
+
['span', {class: 'icon'}, [['i', {class: v.icon}]]]
|
|
411
|
+
], {
|
|
412
|
+
click: function(evt) {
|
|
413
|
+
evt.stopPropagation();
|
|
414
|
+
gobj_send_event(gobj, "EV_SET_VIEW_MODE", {mode: v.mode}, gobj);
|
|
415
|
+
}
|
|
416
|
+
}];
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
return ['div', {class: 'buttons has-addons is-flex-wrap-nowrap mb-0 JSON_VIEW_SWITCH'},
|
|
420
|
+
buttons];
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/************************************************************
|
|
424
|
+
* The label of a view, with every key SPELLED OUT inside t().
|
|
425
|
+
*
|
|
426
|
+
* Never t(VIEWS[i].key): the apps' validate-locales scans for
|
|
427
|
+
* t("literal"), so a key reached through a variable is a key it
|
|
428
|
+
* cannot demand — and i18next answers an undefined key with the key
|
|
429
|
+
* ITSELF, which renders in lower-case English and never changes
|
|
430
|
+
* language. That shipped once already (7.20.0, `tree view`); this
|
|
431
|
+
* table would have hidden all three.
|
|
432
|
+
************************************************************/
|
|
433
|
+
function view_label(mode)
|
|
434
|
+
{
|
|
435
|
+
switch(mode) {
|
|
436
|
+
case "text":
|
|
437
|
+
return t("text view");
|
|
438
|
+
case "graph":
|
|
439
|
+
return t("graph view");
|
|
440
|
+
default:
|
|
441
|
+
return t("tree view");
|
|
442
|
+
}
|
|
360
443
|
}
|
|
361
444
|
|
|
362
445
|
/************************************************************
|
|
363
|
-
* The current view, normalised. Anything
|
|
446
|
+
* The current view, normalised. Anything unknown is the tree.
|
|
364
447
|
************************************************************/
|
|
365
448
|
function current_view_mode(gobj)
|
|
366
449
|
{
|
|
367
|
-
|
|
450
|
+
let mode = gobj_read_str_attr(gobj, "view_mode");
|
|
451
|
+
return VIEWS.some((v) => v.mode === mode)? mode: "tree";
|
|
368
452
|
}
|
|
369
453
|
|
|
370
454
|
/************************************************************
|
|
@@ -373,45 +457,32 @@ function current_view_mode(gobj)
|
|
|
373
457
|
function apply_view_mode(gobj)
|
|
374
458
|
{
|
|
375
459
|
let priv = gobj.priv;
|
|
376
|
-
let
|
|
460
|
+
let mode = current_view_mode(gobj);
|
|
377
461
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
462
|
+
let bodies = {tree: priv.$tree, text: priv.$text, graph: priv.$graph};
|
|
463
|
+
for(let [m, $el] of Object.entries(bodies)) {
|
|
464
|
+
if($el) {
|
|
465
|
+
$el.classList.toggle('is-hidden', m !== mode);
|
|
466
|
+
}
|
|
383
467
|
}
|
|
384
468
|
|
|
385
469
|
/*
|
|
386
|
-
* Search and expand/collapse act on TREE rows;
|
|
387
|
-
*
|
|
388
|
-
*
|
|
389
|
-
*
|
|
470
|
+
* Search and expand/collapse act on TREE rows; the other two views
|
|
471
|
+
* have no rows to act on, and a control that answers nothing is
|
|
472
|
+
* worse than an absent one. A <pre> is searched with the browser's
|
|
473
|
+
* own Ctrl+F, and the graph carries its own controls.
|
|
390
474
|
*/
|
|
391
475
|
[priv.$search_ctl, priv.$expand_btn, priv.$collapse_btn].forEach(function($el) {
|
|
392
476
|
if($el) {
|
|
393
|
-
$el.classList.toggle('is-hidden',
|
|
477
|
+
$el.classList.toggle('is-hidden', mode !== "tree");
|
|
394
478
|
}
|
|
395
479
|
});
|
|
396
480
|
|
|
397
|
-
if(priv.$
|
|
398
|
-
let
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
* see is a key it cannot demand — i18next then answers an
|
|
402
|
-
* undefined key with the key ITSELF, so the button renders in
|
|
403
|
-
* lower-case English and never changes language. `key` still
|
|
404
|
-
* carries the data-i18n-* attrs refresh_language() reads. */
|
|
405
|
-
let key = text_mode? "tree view": "text view";
|
|
406
|
-
let label = text_mode? t("tree view"): t("text view");
|
|
407
|
-
let $i = priv.$mode_btn.querySelector('i');
|
|
408
|
-
if($i) {
|
|
409
|
-
$i.className = icon;
|
|
481
|
+
if(priv.$mode_btns) {
|
|
482
|
+
for(let [m, $btn] of priv.$mode_btns) {
|
|
483
|
+
$btn.classList.toggle('is-active', m === mode);
|
|
484
|
+
$btn.setAttribute("aria-pressed", (m === mode)? "true": "false");
|
|
410
485
|
}
|
|
411
|
-
priv.$mode_btn.setAttribute("data-i18n-title", key);
|
|
412
|
-
priv.$mode_btn.setAttribute("data-i18n-aria-label", key);
|
|
413
|
-
priv.$mode_btn.setAttribute("title", label);
|
|
414
|
-
priv.$mode_btn.setAttribute("aria-label", label);
|
|
415
486
|
}
|
|
416
487
|
}
|
|
417
488
|
|
|
@@ -420,10 +491,105 @@ function apply_view_mode(gobj)
|
|
|
420
491
|
************************************************************/
|
|
421
492
|
function render_view(gobj)
|
|
422
493
|
{
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
494
|
+
switch(current_view_mode(gobj)) {
|
|
495
|
+
case "text":
|
|
496
|
+
render_text(gobj);
|
|
497
|
+
break;
|
|
498
|
+
case "graph":
|
|
499
|
+
render_graph(gobj);
|
|
500
|
+
break;
|
|
501
|
+
default:
|
|
502
|
+
render_tree(gobj);
|
|
503
|
+
break;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/************************************************************
|
|
508
|
+
* Render the graph view.
|
|
509
|
+
*
|
|
510
|
+
* The C_YUI_JSON_GRAPH child is built HERE, on first entry, and
|
|
511
|
+
* never in build_ui: G6 sizes itself from its container, and a
|
|
512
|
+
* container behind `is-hidden` measures 0x0. By the time this
|
|
513
|
+
* runs apply_view_mode() has already revealed the body.
|
|
514
|
+
************************************************************/
|
|
515
|
+
function render_graph(gobj)
|
|
516
|
+
{
|
|
517
|
+
let priv = gobj.priv;
|
|
518
|
+
let $graph = priv.$graph;
|
|
519
|
+
if(!$graph) {
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
if(priv.root === null || priv.root === undefined) {
|
|
524
|
+
teardown_graph_child(gobj);
|
|
525
|
+
$graph.textContent = "";
|
|
526
|
+
$graph.appendChild(createElement2(
|
|
527
|
+
['div', {class: 'JSON_EMPTY has-text-grey p-3', 'data-i18n': 'no data'}, 'no data']
|
|
528
|
+
));
|
|
529
|
+
refresh_language($graph, t);
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
if(!priv.graph_gobj) {
|
|
534
|
+
$graph.textContent = "";
|
|
535
|
+
if(!build_graph_child(gobj)) {
|
|
536
|
+
return; // Error already logged
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/* The document, and then the size: the container may have changed
|
|
541
|
+
* while this view was hidden, and G6 does not watch it. */
|
|
542
|
+
gobj_send_event(priv.graph_gobj, "EV_LOAD_DATA",
|
|
543
|
+
{data: priv.root, path: gobj_read_str_attr(gobj, "title") || ""}, gobj);
|
|
544
|
+
gobj_send_event(priv.graph_gobj, "EV_RESIZE", {}, gobj);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/************************************************************
|
|
548
|
+
* Build and mount the hosted graph child. Returns true on
|
|
549
|
+
* success; every failure path logs.
|
|
550
|
+
************************************************************/
|
|
551
|
+
function build_graph_child(gobj)
|
|
552
|
+
{
|
|
553
|
+
let priv = gobj.priv;
|
|
554
|
+
|
|
555
|
+
let graph = gobj_create_pure_child(
|
|
556
|
+
"graph_" + clean_name(gobj_name(gobj)),
|
|
557
|
+
"C_YUI_JSON_GRAPH",
|
|
558
|
+
{},
|
|
559
|
+
gobj
|
|
560
|
+
);
|
|
561
|
+
if(!graph) {
|
|
562
|
+
log_error(`${gobj_short_name(gobj)}: cannot create the graph viewer`);
|
|
563
|
+
return false;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
let $box = gobj_read_attr(graph, "$container");
|
|
567
|
+
if(!$box) {
|
|
568
|
+
log_error(`${gobj_short_name(gobj)}: the graph viewer built no $container`);
|
|
569
|
+
gobj_destroy(graph);
|
|
570
|
+
return false;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
/* Mounted BEFORE gobj_start: mt_start builds the G6 graph and
|
|
574
|
+
* measures the canvas, which is 0x0 while it is still detached. */
|
|
575
|
+
priv.$graph.appendChild($box);
|
|
576
|
+
priv.graph_gobj = graph;
|
|
577
|
+
gobj_start(graph);
|
|
578
|
+
return true;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
/************************************************************
|
|
582
|
+
* Destroy the hosted graph child, if any.
|
|
583
|
+
************************************************************/
|
|
584
|
+
function teardown_graph_child(gobj)
|
|
585
|
+
{
|
|
586
|
+
let priv = gobj.priv;
|
|
587
|
+
if(priv.graph_gobj) {
|
|
588
|
+
if(gobj_is_running(priv.graph_gobj)) {
|
|
589
|
+
gobj_stop(priv.graph_gobj);
|
|
590
|
+
}
|
|
591
|
+
gobj_destroy(priv.graph_gobj);
|
|
592
|
+
priv.graph_gobj = null;
|
|
427
593
|
}
|
|
428
594
|
}
|
|
429
595
|
|
|
@@ -977,21 +1143,28 @@ function ac_copy_all(gobj, event, kw, src)
|
|
|
977
1143
|
}
|
|
978
1144
|
|
|
979
1145
|
/************************************************************
|
|
980
|
-
* EV_SET_VIEW_MODE { mode } — "tree" | "text".
|
|
1146
|
+
* EV_SET_VIEW_MODE { mode } — "tree" | "text" | "graph".
|
|
981
1147
|
*
|
|
982
|
-
* With no mode it
|
|
1148
|
+
* With no mode it ADVANCES to the next view in VIEWS order, which
|
|
1149
|
+
* is what the two-view toggle did when the list was two long. The
|
|
1150
|
+
* toolbar buttons always name their mode.
|
|
983
1151
|
************************************************************/
|
|
984
1152
|
function ac_set_view_mode(gobj, event, kw, src)
|
|
985
1153
|
{
|
|
986
1154
|
let mode = kw.mode;
|
|
987
1155
|
|
|
988
1156
|
if(mode === undefined || mode === null || mode === "") {
|
|
989
|
-
|
|
990
|
-
|
|
1157
|
+
let i = VIEWS.findIndex((v) => v.mode === current_view_mode(gobj));
|
|
1158
|
+
mode = VIEWS[(i + 1) % VIEWS.length].mode;
|
|
1159
|
+
} else if(!VIEWS.some((v) => v.mode === mode)) {
|
|
991
1160
|
log_error(`${GCLASS_NAME}: unknown view_mode '${mode}'`);
|
|
992
1161
|
return -1;
|
|
993
1162
|
}
|
|
994
1163
|
|
|
1164
|
+
if(mode === current_view_mode(gobj)) {
|
|
1165
|
+
return 0; // already there; re-rendering the graph would relayout it
|
|
1166
|
+
}
|
|
1167
|
+
|
|
995
1168
|
gobj_write_attr(gobj, "view_mode", mode);
|
|
996
1169
|
|
|
997
1170
|
apply_view_mode(gobj);
|
|
@@ -999,6 +1172,18 @@ function ac_set_view_mode(gobj, event, kw, src)
|
|
|
999
1172
|
return 0;
|
|
1000
1173
|
}
|
|
1001
1174
|
|
|
1175
|
+
/************************************************************
|
|
1176
|
+
* EV_JSON_ITEM_CLICKED — from the hosted graph child.
|
|
1177
|
+
*
|
|
1178
|
+
* Republished under this gclass's own name so the host has ONE
|
|
1179
|
+
* contract and never has to know the child exists.
|
|
1180
|
+
************************************************************/
|
|
1181
|
+
function ac_json_item_clicked(gobj, event, kw, src)
|
|
1182
|
+
{
|
|
1183
|
+
gobj_publish_event(gobj, "EV_JSON_ITEM_CLICKED", kw);
|
|
1184
|
+
return 0;
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1002
1187
|
/************************************************************
|
|
1003
1188
|
* EV_LANGUAGE_CHANGED — re-translate chrome + re-render
|
|
1004
1189
|
************************************************************/
|
|
@@ -1091,6 +1276,7 @@ function create_gclass(gclass_name)
|
|
|
1091
1276
|
]],
|
|
1092
1277
|
["ST_READY", [
|
|
1093
1278
|
["EV_SET_JSON", ac_set_json, null],
|
|
1279
|
+
["EV_JSON_ITEM_CLICKED", ac_json_item_clicked, null],
|
|
1094
1280
|
["EV_SUBTREE_LOADED", ac_subtree_loaded, null],
|
|
1095
1281
|
["EV_SUBTREE_ERROR", ac_subtree_error, null],
|
|
1096
1282
|
["EV_TOGGLE_NODE", ac_toggle_node, null],
|
|
@@ -1121,6 +1307,7 @@ function create_gclass(gclass_name)
|
|
|
1121
1307
|
["EV_COLLAPSE_ALL", 0],
|
|
1122
1308
|
["EV_COPY_ALL", 0],
|
|
1123
1309
|
["EV_SET_VIEW_MODE", 0],
|
|
1310
|
+
["EV_JSON_ITEM_CLICKED", event_flag_t.EVF_OUTPUT_EVENT|event_flag_t.EVF_NO_WARN_SUBS],
|
|
1124
1311
|
["EV_LANGUAGE_CHANGED", 0],
|
|
1125
1312
|
["EV_REFRESH", 0],
|
|
1126
1313
|
["EV_SHOW", 0],
|
|
@@ -1161,6 +1348,12 @@ function register_c_yui_json()
|
|
|
1161
1348
|
if(gclass_find_by_name(GCLASS_NAME, false)) {
|
|
1162
1349
|
return 0;
|
|
1163
1350
|
}
|
|
1351
|
+
/* The graph view hosts a C_YUI_JSON_GRAPH child: make sure its
|
|
1352
|
+
* gclass exists even if the app never registered it (same
|
|
1353
|
+
* arrangement C_YUI_TREEDB_TOPIC_WITH_FORM makes for this one). */
|
|
1354
|
+
if(!gclass_find_by_name("C_YUI_JSON_GRAPH", false)) {
|
|
1355
|
+
register_c_yui_json_graph();
|
|
1356
|
+
}
|
|
1164
1357
|
return create_gclass(GCLASS_NAME);
|
|
1165
1358
|
}
|
|
1166
1359
|
|
package/src/c_yui_json_graph.js
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
sdata_flag_t,
|
|
16
16
|
event_flag_t,
|
|
17
17
|
gclass_create,
|
|
18
|
+
gclass_find_by_name,
|
|
18
19
|
log_error,
|
|
19
20
|
gobj_read_pointer_attr,
|
|
20
21
|
gobj_parent,
|
|
@@ -1109,6 +1110,13 @@ function create_gclass(gclass_name)
|
|
|
1109
1110
|
***************************************************************/
|
|
1110
1111
|
function register_c_yui_json_graph()
|
|
1111
1112
|
{
|
|
1113
|
+
/* Idempotent: C_YUI_JSON auto-registers this gclass for its graph
|
|
1114
|
+
* view, so an app that also registers it explicitly (in either
|
|
1115
|
+
* order) must not trip "GClass ALREADY created". Same arrangement
|
|
1116
|
+
* C_YUI_JSON itself makes for C_YUI_FORM's hosts. */
|
|
1117
|
+
if(gclass_find_by_name(GCLASS_NAME, false)) {
|
|
1118
|
+
return 0;
|
|
1119
|
+
}
|
|
1112
1120
|
return create_gclass(GCLASS_NAME);
|
|
1113
1121
|
}
|
|
1114
1122
|
|