@yuneta/gobj-ui 7.20.0 → 7.21.0
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 +51 -27
- package/dist/gobj-ui.cjs.js +230 -62
- package/dist/gobj-ui.es.js +230 -62
- package/package.json +1 -1
- package/src/c_yui_json.css +78 -1
- package/src/c_yui_json.js +264 -65
- 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,17 @@ const MAX_RENDER_ROWS = 5000;
|
|
|
103
125
|
*/
|
|
104
126
|
const MAX_TEXT_CHARS = 2000000;
|
|
105
127
|
|
|
128
|
+
/*
|
|
129
|
+
* The three views, in switch order. EV_SET_VIEW_MODE with no mode
|
|
130
|
+
* advances along this list, which is what the old two-view toggle did
|
|
131
|
+
* when the list was two long.
|
|
132
|
+
*/
|
|
133
|
+
const VIEWS = [
|
|
134
|
+
{mode: "tree", icon: "yi-sitemap", key: "tree view"},
|
|
135
|
+
{mode: "text", icon: "yi-code", key: "text view"},
|
|
136
|
+
{mode: "graph", icon: "yi-hexagon-nodes", key: "graph view"},
|
|
137
|
+
];
|
|
138
|
+
|
|
106
139
|
/***************************************************************
|
|
107
140
|
* Data
|
|
108
141
|
***************************************************************/
|
|
@@ -113,7 +146,7 @@ SDATA(data_type_t.DTP_POINTER, "subscriber", 0, null, "Subscriber of outpu
|
|
|
113
146
|
/*---------------- Config ----------------*/
|
|
114
147
|
SDATA(data_type_t.DTP_STRING, "title", 0, "", "Optional header title (i18n key)"),
|
|
115
148
|
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)
|
|
149
|
+
SDATA(data_type_t.DTP_STRING, "view_mode", 0, "tree", "View: 'tree' (lazy tree), 'text' (raw dump) or 'graph'"),
|
|
117
150
|
|
|
118
151
|
/*---------------- UI ----------------*/
|
|
119
152
|
SDATA(data_type_t.DTP_POINTER, "$container", 0, null, "HTMLElement root, mounted by the parent"),
|
|
@@ -129,11 +162,13 @@ let PRIVATE_DATA = {
|
|
|
129
162
|
$tree: null, // the scrollable tree body element
|
|
130
163
|
$text: null, // the scrollable text body element
|
|
131
164
|
$text_body: null, // the <pre> inside it
|
|
165
|
+
$graph: null, // the graph body element
|
|
166
|
+
graph_gobj: null, // hosted C_YUI_JSON_GRAPH child (built on first use)
|
|
132
167
|
$search: null, // the search input element
|
|
133
168
|
$search_ctl: null, // the search control (hidden in text view)
|
|
134
169
|
$expand_btn: null, // "expand loaded" button (hidden in text view)
|
|
135
170
|
$collapse_btn: null,// "collapse all" button (hidden in text view)
|
|
136
|
-
$
|
|
171
|
+
$mode_btns: null, // Map<mode, HTMLElement> of the view switch
|
|
137
172
|
};
|
|
138
173
|
|
|
139
174
|
let __gclass__ = null;
|
|
@@ -198,6 +233,7 @@ function mt_stop(gobj)
|
|
|
198
233
|
***************************************************************/
|
|
199
234
|
function mt_destroy(gobj)
|
|
200
235
|
{
|
|
236
|
+
teardown_graph_child(gobj);
|
|
201
237
|
destroy_ui(gobj);
|
|
202
238
|
}
|
|
203
239
|
|
|
@@ -227,7 +263,12 @@ function build_ui(gobj)
|
|
|
227
263
|
['div', {class: 'JSON_TREE is-flex-grow-1',
|
|
228
264
|
style: 'flex:1 1 auto; min-height:0; overflow:auto;'}, []],
|
|
229
265
|
['div', {class: 'JSON_TEXT is-flex-grow-1 is-hidden',
|
|
230
|
-
style: 'flex:1 1 auto; min-height:0; overflow:auto;'}, []]
|
|
266
|
+
style: 'flex:1 1 auto; min-height:0; overflow:auto;'}, []],
|
|
267
|
+
/* The graph body does NOT scroll: G6 owns its viewport and
|
|
268
|
+
* pans inside it. An `overflow:auto` here would give the
|
|
269
|
+
* canvas a second, competing scroller. */
|
|
270
|
+
['div', {class: 'JSON_GRAPH is-flex-grow-1 is-hidden',
|
|
271
|
+
style: 'flex:1 1 auto; overflow:hidden;'}, []]
|
|
231
272
|
]]
|
|
232
273
|
);
|
|
233
274
|
|
|
@@ -238,7 +279,14 @@ function build_ui(gobj)
|
|
|
238
279
|
priv.$search_ctl = $container.querySelector('.JSON_SEARCH_CONTROL');
|
|
239
280
|
priv.$expand_btn = $container.querySelector('.EV_EXPAND_ALL');
|
|
240
281
|
priv.$collapse_btn = $container.querySelector('.EV_COLLAPSE_ALL');
|
|
241
|
-
priv.$
|
|
282
|
+
priv.$graph = $container.querySelector('.JSON_GRAPH');
|
|
283
|
+
priv.$mode_btns = new Map();
|
|
284
|
+
VIEWS.forEach(function(v) {
|
|
285
|
+
let $btn = $container.querySelector('.JSON_VIEW_MODE_' + v.mode.toUpperCase());
|
|
286
|
+
if($btn) {
|
|
287
|
+
priv.$mode_btns.set(v.mode, $btn);
|
|
288
|
+
}
|
|
289
|
+
});
|
|
242
290
|
|
|
243
291
|
refresh_language($container, t);
|
|
244
292
|
}
|
|
@@ -259,11 +307,12 @@ function destroy_ui(gobj)
|
|
|
259
307
|
priv.$tree = null;
|
|
260
308
|
priv.$text = null;
|
|
261
309
|
priv.$text_body = null;
|
|
310
|
+
priv.$graph = null;
|
|
262
311
|
priv.$search = null;
|
|
263
312
|
priv.$search_ctl = null;
|
|
264
313
|
priv.$expand_btn = null;
|
|
265
314
|
priv.$collapse_btn = null;
|
|
266
|
-
priv.$
|
|
315
|
+
priv.$mode_btns = null;
|
|
267
316
|
}
|
|
268
317
|
|
|
269
318
|
/************************************************************
|
|
@@ -277,7 +326,11 @@ function make_toolbar(gobj)
|
|
|
277
326
|
let left_items = [];
|
|
278
327
|
if(title) {
|
|
279
328
|
left_items.push(
|
|
280
|
-
|
|
329
|
+
/* is-hidden-mobile: on a phone the toolbar cannot hold the
|
|
330
|
+
* title AND a usable search AND six buttons, and the title
|
|
331
|
+
* is the one thing the host usually repeats — a dialog
|
|
332
|
+
* header, a card heading — so it is the one that goes. */
|
|
333
|
+
['span', {class: 'JSON_TITLE is-flex is-align-items-center px-2 is-hidden-mobile',
|
|
281
334
|
style: 'font-weight:600;', 'data-i18n': title}, title]
|
|
282
335
|
);
|
|
283
336
|
}
|
|
@@ -301,7 +354,7 @@ function make_toolbar(gobj)
|
|
|
301
354
|
left_items.push($search_control);
|
|
302
355
|
|
|
303
356
|
let right_items = [
|
|
304
|
-
|
|
357
|
+
view_mode_switch(gobj),
|
|
305
358
|
icon_button(gobj, "yi-chevron-right", "EV_EXPAND_ALL", "expand loaded"),
|
|
306
359
|
icon_button(gobj, "yi-chevron-right", "EV_COLLAPSE_ALL", "collapse all"),
|
|
307
360
|
icon_button(gobj, "yi-copy", "EV_COPY_ALL", "copy json"),
|
|
@@ -337,34 +390,64 @@ function icon_button(gobj, icon, event_name, label_key)
|
|
|
337
390
|
}
|
|
338
391
|
|
|
339
392
|
/************************************************************
|
|
340
|
-
* The
|
|
393
|
+
* The view switch: one button per view, the current one marked.
|
|
341
394
|
*
|
|
342
|
-
*
|
|
343
|
-
*
|
|
344
|
-
*
|
|
345
|
-
*
|
|
346
|
-
*
|
|
395
|
+
* Three views do not fit a toggle. A button that CYCLES cannot be
|
|
396
|
+
* aimed — reaching the graph from the tree would mean passing
|
|
397
|
+
* through the text and rebuilding it on the way — so each view gets
|
|
398
|
+
* its own button and says where it goes. apply_view_mode() moves
|
|
399
|
+
* the `is-active` mark.
|
|
347
400
|
************************************************************/
|
|
348
|
-
function
|
|
401
|
+
function view_mode_switch(gobj)
|
|
349
402
|
{
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
403
|
+
let buttons = VIEWS.map(function(v) {
|
|
404
|
+
let label = view_label(v.mode);
|
|
405
|
+
return ['button', {class: `button JSON_VIEW_MODE JSON_VIEW_MODE_${v.mode.toUpperCase()}`,
|
|
406
|
+
type: 'button', style: 'width:2.5em;',
|
|
407
|
+
title: label, 'data-i18n-title': v.key,
|
|
408
|
+
'aria-label': label, 'data-i18n-aria-label': v.key}, [
|
|
409
|
+
['span', {class: 'icon'}, [['i', {class: v.icon}]]]
|
|
410
|
+
], {
|
|
411
|
+
click: function(evt) {
|
|
412
|
+
evt.stopPropagation();
|
|
413
|
+
gobj_send_event(gobj, "EV_SET_VIEW_MODE", {mode: v.mode}, gobj);
|
|
414
|
+
}
|
|
415
|
+
}];
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
return ['div', {class: 'buttons has-addons is-flex-wrap-nowrap mb-0 JSON_VIEW_SWITCH'},
|
|
419
|
+
buttons];
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/************************************************************
|
|
423
|
+
* The label of a view, with every key SPELLED OUT inside t().
|
|
424
|
+
*
|
|
425
|
+
* Never t(VIEWS[i].key): the apps' validate-locales scans for
|
|
426
|
+
* t("literal"), so a key reached through a variable is a key it
|
|
427
|
+
* cannot demand — and i18next answers an undefined key with the key
|
|
428
|
+
* ITSELF, which renders in lower-case English and never changes
|
|
429
|
+
* language. That shipped once already (7.20.0, `tree view`); this
|
|
430
|
+
* table would have hidden all three.
|
|
431
|
+
************************************************************/
|
|
432
|
+
function view_label(mode)
|
|
433
|
+
{
|
|
434
|
+
switch(mode) {
|
|
435
|
+
case "text":
|
|
436
|
+
return t("text view");
|
|
437
|
+
case "graph":
|
|
438
|
+
return t("graph view");
|
|
439
|
+
default:
|
|
440
|
+
return t("tree view");
|
|
441
|
+
}
|
|
360
442
|
}
|
|
361
443
|
|
|
362
444
|
/************************************************************
|
|
363
|
-
* The current view, normalised. Anything
|
|
445
|
+
* The current view, normalised. Anything unknown is the tree.
|
|
364
446
|
************************************************************/
|
|
365
447
|
function current_view_mode(gobj)
|
|
366
448
|
{
|
|
367
|
-
|
|
449
|
+
let mode = gobj_read_str_attr(gobj, "view_mode");
|
|
450
|
+
return VIEWS.some((v) => v.mode === mode)? mode: "tree";
|
|
368
451
|
}
|
|
369
452
|
|
|
370
453
|
/************************************************************
|
|
@@ -373,38 +456,32 @@ function current_view_mode(gobj)
|
|
|
373
456
|
function apply_view_mode(gobj)
|
|
374
457
|
{
|
|
375
458
|
let priv = gobj.priv;
|
|
376
|
-
let
|
|
459
|
+
let mode = current_view_mode(gobj);
|
|
377
460
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
461
|
+
let bodies = {tree: priv.$tree, text: priv.$text, graph: priv.$graph};
|
|
462
|
+
for(let [m, $el] of Object.entries(bodies)) {
|
|
463
|
+
if($el) {
|
|
464
|
+
$el.classList.toggle('is-hidden', m !== mode);
|
|
465
|
+
}
|
|
383
466
|
}
|
|
384
467
|
|
|
385
468
|
/*
|
|
386
|
-
* Search and expand/collapse act on TREE rows;
|
|
387
|
-
*
|
|
388
|
-
*
|
|
389
|
-
*
|
|
469
|
+
* Search and expand/collapse act on TREE rows; the other two views
|
|
470
|
+
* have no rows to act on, and a control that answers nothing is
|
|
471
|
+
* worse than an absent one. A <pre> is searched with the browser's
|
|
472
|
+
* own Ctrl+F, and the graph carries its own controls.
|
|
390
473
|
*/
|
|
391
474
|
[priv.$search_ctl, priv.$expand_btn, priv.$collapse_btn].forEach(function($el) {
|
|
392
475
|
if($el) {
|
|
393
|
-
$el.classList.toggle('is-hidden',
|
|
476
|
+
$el.classList.toggle('is-hidden', mode !== "tree");
|
|
394
477
|
}
|
|
395
478
|
});
|
|
396
479
|
|
|
397
|
-
if(priv.$
|
|
398
|
-
let
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
if($i) {
|
|
402
|
-
$i.className = icon;
|
|
480
|
+
if(priv.$mode_btns) {
|
|
481
|
+
for(let [m, $btn] of priv.$mode_btns) {
|
|
482
|
+
$btn.classList.toggle('is-active', m === mode);
|
|
483
|
+
$btn.setAttribute("aria-pressed", (m === mode)? "true": "false");
|
|
403
484
|
}
|
|
404
|
-
priv.$mode_btn.setAttribute("data-i18n-title", key);
|
|
405
|
-
priv.$mode_btn.setAttribute("data-i18n-aria-label", key);
|
|
406
|
-
priv.$mode_btn.setAttribute("title", t(key));
|
|
407
|
-
priv.$mode_btn.setAttribute("aria-label", t(key));
|
|
408
485
|
}
|
|
409
486
|
}
|
|
410
487
|
|
|
@@ -413,10 +490,105 @@ function apply_view_mode(gobj)
|
|
|
413
490
|
************************************************************/
|
|
414
491
|
function render_view(gobj)
|
|
415
492
|
{
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
493
|
+
switch(current_view_mode(gobj)) {
|
|
494
|
+
case "text":
|
|
495
|
+
render_text(gobj);
|
|
496
|
+
break;
|
|
497
|
+
case "graph":
|
|
498
|
+
render_graph(gobj);
|
|
499
|
+
break;
|
|
500
|
+
default:
|
|
501
|
+
render_tree(gobj);
|
|
502
|
+
break;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/************************************************************
|
|
507
|
+
* Render the graph view.
|
|
508
|
+
*
|
|
509
|
+
* The C_YUI_JSON_GRAPH child is built HERE, on first entry, and
|
|
510
|
+
* never in build_ui: G6 sizes itself from its container, and a
|
|
511
|
+
* container behind `is-hidden` measures 0x0. By the time this
|
|
512
|
+
* runs apply_view_mode() has already revealed the body.
|
|
513
|
+
************************************************************/
|
|
514
|
+
function render_graph(gobj)
|
|
515
|
+
{
|
|
516
|
+
let priv = gobj.priv;
|
|
517
|
+
let $graph = priv.$graph;
|
|
518
|
+
if(!$graph) {
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
if(priv.root === null || priv.root === undefined) {
|
|
523
|
+
teardown_graph_child(gobj);
|
|
524
|
+
$graph.textContent = "";
|
|
525
|
+
$graph.appendChild(createElement2(
|
|
526
|
+
['div', {class: 'JSON_EMPTY has-text-grey p-3', 'data-i18n': 'no data'}, 'no data']
|
|
527
|
+
));
|
|
528
|
+
refresh_language($graph, t);
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
if(!priv.graph_gobj) {
|
|
533
|
+
$graph.textContent = "";
|
|
534
|
+
if(!build_graph_child(gobj)) {
|
|
535
|
+
return; // Error already logged
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/* The document, and then the size: the container may have changed
|
|
540
|
+
* while this view was hidden, and G6 does not watch it. */
|
|
541
|
+
gobj_send_event(priv.graph_gobj, "EV_LOAD_DATA",
|
|
542
|
+
{data: priv.root, path: gobj_read_str_attr(gobj, "title") || ""}, gobj);
|
|
543
|
+
gobj_send_event(priv.graph_gobj, "EV_RESIZE", {}, gobj);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/************************************************************
|
|
547
|
+
* Build and mount the hosted graph child. Returns true on
|
|
548
|
+
* success; every failure path logs.
|
|
549
|
+
************************************************************/
|
|
550
|
+
function build_graph_child(gobj)
|
|
551
|
+
{
|
|
552
|
+
let priv = gobj.priv;
|
|
553
|
+
|
|
554
|
+
let graph = gobj_create_pure_child(
|
|
555
|
+
"graph_" + clean_name(gobj_name(gobj)),
|
|
556
|
+
"C_YUI_JSON_GRAPH",
|
|
557
|
+
{},
|
|
558
|
+
gobj
|
|
559
|
+
);
|
|
560
|
+
if(!graph) {
|
|
561
|
+
log_error(`${gobj_short_name(gobj)}: cannot create the graph viewer`);
|
|
562
|
+
return false;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
let $box = gobj_read_attr(graph, "$container");
|
|
566
|
+
if(!$box) {
|
|
567
|
+
log_error(`${gobj_short_name(gobj)}: the graph viewer built no $container`);
|
|
568
|
+
gobj_destroy(graph);
|
|
569
|
+
return false;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/* Mounted BEFORE gobj_start: mt_start builds the G6 graph and
|
|
573
|
+
* measures the canvas, which is 0x0 while it is still detached. */
|
|
574
|
+
priv.$graph.appendChild($box);
|
|
575
|
+
priv.graph_gobj = graph;
|
|
576
|
+
gobj_start(graph);
|
|
577
|
+
return true;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
/************************************************************
|
|
581
|
+
* Destroy the hosted graph child, if any.
|
|
582
|
+
************************************************************/
|
|
583
|
+
function teardown_graph_child(gobj)
|
|
584
|
+
{
|
|
585
|
+
let priv = gobj.priv;
|
|
586
|
+
if(priv.graph_gobj) {
|
|
587
|
+
if(gobj_is_running(priv.graph_gobj)) {
|
|
588
|
+
gobj_stop(priv.graph_gobj);
|
|
589
|
+
}
|
|
590
|
+
gobj_destroy(priv.graph_gobj);
|
|
591
|
+
priv.graph_gobj = null;
|
|
420
592
|
}
|
|
421
593
|
}
|
|
422
594
|
|
|
@@ -970,21 +1142,28 @@ function ac_copy_all(gobj, event, kw, src)
|
|
|
970
1142
|
}
|
|
971
1143
|
|
|
972
1144
|
/************************************************************
|
|
973
|
-
* EV_SET_VIEW_MODE { mode } — "tree" | "text".
|
|
1145
|
+
* EV_SET_VIEW_MODE { mode } — "tree" | "text" | "graph".
|
|
974
1146
|
*
|
|
975
|
-
* With no mode it
|
|
1147
|
+
* With no mode it ADVANCES to the next view in VIEWS order, which
|
|
1148
|
+
* is what the two-view toggle did when the list was two long. The
|
|
1149
|
+
* toolbar buttons always name their mode.
|
|
976
1150
|
************************************************************/
|
|
977
1151
|
function ac_set_view_mode(gobj, event, kw, src)
|
|
978
1152
|
{
|
|
979
1153
|
let mode = kw.mode;
|
|
980
1154
|
|
|
981
1155
|
if(mode === undefined || mode === null || mode === "") {
|
|
982
|
-
|
|
983
|
-
|
|
1156
|
+
let i = VIEWS.findIndex((v) => v.mode === current_view_mode(gobj));
|
|
1157
|
+
mode = VIEWS[(i + 1) % VIEWS.length].mode;
|
|
1158
|
+
} else if(!VIEWS.some((v) => v.mode === mode)) {
|
|
984
1159
|
log_error(`${GCLASS_NAME}: unknown view_mode '${mode}'`);
|
|
985
1160
|
return -1;
|
|
986
1161
|
}
|
|
987
1162
|
|
|
1163
|
+
if(mode === current_view_mode(gobj)) {
|
|
1164
|
+
return 0; // already there; re-rendering the graph would relayout it
|
|
1165
|
+
}
|
|
1166
|
+
|
|
988
1167
|
gobj_write_attr(gobj, "view_mode", mode);
|
|
989
1168
|
|
|
990
1169
|
apply_view_mode(gobj);
|
|
@@ -992,6 +1171,18 @@ function ac_set_view_mode(gobj, event, kw, src)
|
|
|
992
1171
|
return 0;
|
|
993
1172
|
}
|
|
994
1173
|
|
|
1174
|
+
/************************************************************
|
|
1175
|
+
* EV_JSON_ITEM_CLICKED — from the hosted graph child.
|
|
1176
|
+
*
|
|
1177
|
+
* Republished under this gclass's own name so the host has ONE
|
|
1178
|
+
* contract and never has to know the child exists.
|
|
1179
|
+
************************************************************/
|
|
1180
|
+
function ac_json_item_clicked(gobj, event, kw, src)
|
|
1181
|
+
{
|
|
1182
|
+
gobj_publish_event(gobj, "EV_JSON_ITEM_CLICKED", kw);
|
|
1183
|
+
return 0;
|
|
1184
|
+
}
|
|
1185
|
+
|
|
995
1186
|
/************************************************************
|
|
996
1187
|
* EV_LANGUAGE_CHANGED — re-translate chrome + re-render
|
|
997
1188
|
************************************************************/
|
|
@@ -1084,6 +1275,7 @@ function create_gclass(gclass_name)
|
|
|
1084
1275
|
]],
|
|
1085
1276
|
["ST_READY", [
|
|
1086
1277
|
["EV_SET_JSON", ac_set_json, null],
|
|
1278
|
+
["EV_JSON_ITEM_CLICKED", ac_json_item_clicked, null],
|
|
1087
1279
|
["EV_SUBTREE_LOADED", ac_subtree_loaded, null],
|
|
1088
1280
|
["EV_SUBTREE_ERROR", ac_subtree_error, null],
|
|
1089
1281
|
["EV_TOGGLE_NODE", ac_toggle_node, null],
|
|
@@ -1114,6 +1306,7 @@ function create_gclass(gclass_name)
|
|
|
1114
1306
|
["EV_COLLAPSE_ALL", 0],
|
|
1115
1307
|
["EV_COPY_ALL", 0],
|
|
1116
1308
|
["EV_SET_VIEW_MODE", 0],
|
|
1309
|
+
["EV_JSON_ITEM_CLICKED", event_flag_t.EVF_OUTPUT_EVENT|event_flag_t.EVF_NO_WARN_SUBS],
|
|
1117
1310
|
["EV_LANGUAGE_CHANGED", 0],
|
|
1118
1311
|
["EV_REFRESH", 0],
|
|
1119
1312
|
["EV_SHOW", 0],
|
|
@@ -1154,6 +1347,12 @@ function register_c_yui_json()
|
|
|
1154
1347
|
if(gclass_find_by_name(GCLASS_NAME, false)) {
|
|
1155
1348
|
return 0;
|
|
1156
1349
|
}
|
|
1350
|
+
/* The graph view hosts a C_YUI_JSON_GRAPH child: make sure its
|
|
1351
|
+
* gclass exists even if the app never registered it (same
|
|
1352
|
+
* arrangement C_YUI_TREEDB_TOPIC_WITH_FORM makes for this one). */
|
|
1353
|
+
if(!gclass_find_by_name("C_YUI_JSON_GRAPH", false)) {
|
|
1354
|
+
register_c_yui_json_graph();
|
|
1355
|
+
}
|
|
1157
1356
|
return create_gclass(GCLASS_NAME);
|
|
1158
1357
|
}
|
|
1159
1358
|
|
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
|
|