@yuneta/gobj-ui 7.23.140 → 7.23.142
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 +123 -37
- package/dist/gobj-ui.cjs.js +412 -140
- package/dist/gobj-ui.es.js +412 -140
- package/package.json +2 -2
- package/src/c_g6_nodes_tree.js +98 -59
- package/src/c_yui_schema_editor.js +16 -0
- package/src/c_yui_treedb_graph.js +153 -23
- package/src/schema_import.js +2 -1
- package/src/schema_model.js +2 -1
- package/src/schema_to_c.js +1 -1
- package/src/schema_validate.js +20 -0
- package/src/schema_validate.test.js +44 -0
- package/src/treedb_fold_model.js +185 -21
- package/src/treedb_fold_model.test.js +120 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yuneta/gobj-ui",
|
|
3
|
-
"version": "7.23.
|
|
3
|
+
"version": "7.23.142",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/gobj-ui.cjs.js",
|
|
6
6
|
"module": "dist/gobj-ui.es.js",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"src/",
|
|
21
21
|
"vite-plugin-yuneta-html.js"
|
|
22
22
|
],
|
|
23
|
-
"description": "Yuneta UI Library
|
|
23
|
+
"description": "Yuneta UI Library — the active line: declarative shell (C_YUI_SHELL+NAV+PAGER+WIZARD) + windows, TreeDB views, forms, charts and maps. Consumed by wattyzer and the yunetas JS yunos (gui_agent, gui_treedb). The frozen legacy GClass GUI stack (C_YUI_MAIN/TABS/ROUTING) lives on the v1 branch, npm dist-tag 'legacy' (consumed by estadodelaire and hidraulia).",
|
|
24
24
|
"scripts": {
|
|
25
25
|
"build": "vite build",
|
|
26
26
|
"test": "vitest run",
|
package/src/c_g6_nodes_tree.js
CHANGED
|
@@ -13,8 +13,9 @@
|
|
|
13
13
|
* The graph opens FOLDED, like a JSON viewer.
|
|
14
14
|
*
|
|
15
15
|
* Every record of every topic is fetched, but only the ones on
|
|
16
|
-
* screen become G6 nodes: the roots,
|
|
17
|
-
*
|
|
16
|
+
* screen become G6 nodes: the roots, and the MAIN tree open to
|
|
17
|
+
* the reader's level (`fold_level`, `expand_depth` until they
|
|
18
|
+
* step it), one page (`fold_page_size`) of children per hook. Each
|
|
18
19
|
* card carries a pill per hook that has children -- `▸ devices
|
|
19
20
|
* 142` -- which opens or folds that hook; an open hook with more
|
|
20
21
|
* children than its page ends in a `+N` chip that opens the next
|
|
@@ -117,9 +118,11 @@ import {
|
|
|
117
118
|
fold_build_model,
|
|
118
119
|
fold_new_state,
|
|
119
120
|
fold_visible_set,
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
121
|
+
fold_expand_to_level,
|
|
122
|
+
fold_open_levels,
|
|
123
|
+
fold_close_levels,
|
|
124
|
+
fold_level_clamp,
|
|
125
|
+
fold_is_hierarchical,
|
|
123
126
|
fold_toggle,
|
|
124
127
|
fold_show_more,
|
|
125
128
|
fold_reveal,
|
|
@@ -299,14 +302,14 @@ const PORT_LINE_WIDTH = 2;
|
|
|
299
302
|
const NODE_MODES = ['expanded', 'compact', 'shape'];
|
|
300
303
|
|
|
301
304
|
const PILL_SIZE = {
|
|
302
|
-
|
|
305
|
+
entity: [200, 30],
|
|
303
306
|
extended: [180, 28],
|
|
304
307
|
child: [160, 26],
|
|
305
308
|
};
|
|
306
309
|
const PORT_R_PILL = 6;
|
|
307
310
|
|
|
308
311
|
const SHAPE_SIZE = {
|
|
309
|
-
|
|
312
|
+
entity: 32,
|
|
310
313
|
extended: 28,
|
|
311
314
|
child: 22,
|
|
312
315
|
};
|
|
@@ -434,7 +437,8 @@ SDATA(data_type_t.DTP_STRING, "hook_port_position", 0, "bottom", "Hook po
|
|
|
434
437
|
SDATA(data_type_t.DTP_STRING, "fkey_port_position", 0, "top", "Fkey port position"),
|
|
435
438
|
|
|
436
439
|
/*---------------- Folding ----------------*/
|
|
437
|
-
SDATA(data_type_t.DTP_INTEGER, "expand_depth", 0,
|
|
440
|
+
SDATA(data_type_t.DTP_INTEGER, "expand_depth", 0, 1, "Levels open when a treedb loads, until the reader steps (`fold_level`): 1 = the roots alone (the default), 2 = the roots and their children. Every open hook shows its first page"),
|
|
441
|
+
SDATA(data_type_t.DTP_INTEGER, "fold_level", 0, 0, "The level of the MAIN tree the toolbar's stepper stands on: 1 = its roots, N = N-1 levels under them; 0 = `expand_depth`. The host persists it and sends EV_SET_FOLD_LEVEL; it is published back in EV_LEGEND_STATE (`fold`) with the levels the tree has"),
|
|
438
442
|
SDATA(data_type_t.DTP_INTEGER, "fold_page_size", 0, 24, "Children of one hook shown per page; the `+N` chip after the page opens the next one"),
|
|
439
443
|
SDATA(data_type_t.DTP_LIST, "hidden_topics", 0, "[]", "Topics left out of the tree: no card, no pill counts them, no edge reaches them"),
|
|
440
444
|
SDATA(data_type_t.DTP_STRING, "main_topic", 0, "", "The topic the tree hangs from; empty = deduced (the one whose hooks reach the most others). Its parentless records are the roots; a parentless record of a topic linked to it is LOOSE and shown only on request"),
|
|
@@ -542,7 +546,8 @@ let PRIVATE_DATA = {
|
|
|
542
546
|
_nodes_placed: 0, // ...of which carry a saved position
|
|
543
547
|
|
|
544
548
|
/*---------------- folding ----------------*/
|
|
545
|
-
expand_depth:
|
|
549
|
+
expand_depth: 1,
|
|
550
|
+
fold_level: 0,
|
|
546
551
|
fold_page_size: 24,
|
|
547
552
|
hidden_topics: null,
|
|
548
553
|
main_topic: "",
|
|
@@ -1855,7 +1860,12 @@ function configure_behaviour(gobj)
|
|
|
1855
1860
|
}
|
|
1856
1861
|
|
|
1857
1862
|
/************************************************************
|
|
1858
|
-
* Count hooks and fkeys in topic desc, classify node type
|
|
1863
|
+
* Count hooks and fkeys in topic desc, classify node type:
|
|
1864
|
+
* `child` (no hooks), `extended` (hooks, no fkeys), `entity`
|
|
1865
|
+
* (both). It sizes the card and nothing else. It used to be
|
|
1866
|
+
* called `hierarchical`, and that word means something else now:
|
|
1867
|
+
* a topic hooked to ITSELF, the only kind a tree can hang from
|
|
1868
|
+
* (treedb_fold_model).
|
|
1859
1869
|
************************************************************/
|
|
1860
1870
|
function calculate_hooks_fkeys_counter(desc)
|
|
1861
1871
|
{
|
|
@@ -1881,7 +1891,7 @@ function calculate_hooks_fkeys_counter(desc)
|
|
|
1881
1891
|
} else if(desc.fkeys_counter === 0) {
|
|
1882
1892
|
desc.node_treedb_type = 'extended';
|
|
1883
1893
|
} else {
|
|
1884
|
-
desc.node_treedb_type = '
|
|
1894
|
+
desc.node_treedb_type = 'entity';
|
|
1885
1895
|
}
|
|
1886
1896
|
}
|
|
1887
1897
|
|
|
@@ -2302,7 +2312,7 @@ function card_shape_of(gobj, desc, record, geometry, pills_html)
|
|
|
2302
2312
|
desc.topic_name, true, record.id, false, false, false, pills_html
|
|
2303
2313
|
);
|
|
2304
2314
|
} else {
|
|
2305
|
-
//
|
|
2315
|
+
// Entity, hooks and fkeys (ROOT / container): largest tier.
|
|
2306
2316
|
style.innerHTML = build_node_innerHTML(
|
|
2307
2317
|
paint.fill, priv.theme, record.icon, label,
|
|
2308
2318
|
desc.topic_name, false, record.id, false, false, false, pills_html
|
|
@@ -2405,7 +2415,7 @@ function pill_shape_of(gobj, desc, record, geometry)
|
|
|
2405
2415
|
{
|
|
2406
2416
|
let priv = gobj.priv;
|
|
2407
2417
|
let tier = desc.node_treedb_type;
|
|
2408
|
-
let size = PILL_SIZE[tier] || PILL_SIZE.
|
|
2418
|
+
let size = PILL_SIZE[tier] || PILL_SIZE.entity;
|
|
2409
2419
|
let ports = build_ports(gobj, desc);
|
|
2410
2420
|
let paint = node_paint_of(gobj, desc, geometry);
|
|
2411
2421
|
|
|
@@ -2468,7 +2478,7 @@ function figure_shape_of(gobj, desc, record, geometry, flags)
|
|
|
2468
2478
|
let dark = (priv.theme === "dark");
|
|
2469
2479
|
let f = flags || {};
|
|
2470
2480
|
let tier = desc.node_treedb_type;
|
|
2471
|
-
let side = SHAPE_SIZE[tier] || SHAPE_SIZE.
|
|
2481
|
+
let side = SHAPE_SIZE[tier] || SHAPE_SIZE.entity;
|
|
2472
2482
|
let labels = gobj_read_bool_attr(gobj, "node_labels");
|
|
2473
2483
|
let figure = node_figure_of(gobj, desc, geometry || {});
|
|
2474
2484
|
let paint = node_paint_of(gobj, desc, geometry);
|
|
@@ -7038,7 +7048,7 @@ function show_node_detail_popover(gobj, node_id)
|
|
|
7038
7048
|
}
|
|
7039
7049
|
|
|
7040
7050
|
/************************************************************
|
|
7041
|
-
* Re-render every HTML (
|
|
7051
|
+
* Re-render every HTML (entity) node so its card adopts
|
|
7042
7052
|
* the given theme: entity cards and leaf chips (baked innerHTML,
|
|
7043
7053
|
* not re-themed by setTheme()) are regenerated; structural
|
|
7044
7054
|
* junction diamonds get their neutral fill/stroke re-themed.
|
|
@@ -7254,7 +7264,7 @@ function refresh_minimap(gobj)
|
|
|
7254
7264
|
|
|
7255
7265
|
/************************************************************
|
|
7256
7266
|
* The innerHTML of ONE node, in the given theme and highlight state.
|
|
7257
|
-
* The three treedb tiers (
|
|
7267
|
+
* The three treedb tiers (entity / extended / child) each have
|
|
7258
7268
|
* their own card, and both the theme refresh and the highlight need the
|
|
7259
7269
|
* same three-way choice — it lived inline in the theme refresh and is
|
|
7260
7270
|
* shared now. Returns null for a node that carries no desc.
|
|
@@ -7291,7 +7301,7 @@ function node_innerHTML_of(nd, theme, highlight, selected, anchored, pills_html)
|
|
|
7291
7301
|
desc.topic_name, true, record.id, highlight, selected, anchored,
|
|
7292
7302
|
pills_html
|
|
7293
7303
|
);
|
|
7294
|
-
case '
|
|
7304
|
+
case 'entity':
|
|
7295
7305
|
return build_node_innerHTML(
|
|
7296
7306
|
fill, theme, record.icon, label,
|
|
7297
7307
|
desc.topic_name, false, record.id, highlight, selected, anchored,
|
|
@@ -7565,7 +7575,7 @@ function build_chip_innerHTML(color, theme, icon, label, key, highlight, selecte
|
|
|
7565
7575
|
}
|
|
7566
7576
|
|
|
7567
7577
|
/************************************************************
|
|
7568
|
-
* Build innerHTML for
|
|
7578
|
+
* Build innerHTML for entity (HTML) nodes.
|
|
7569
7579
|
*
|
|
7570
7580
|
* Card style modelled on the documentation schema graphs
|
|
7571
7581
|
* (docs gen_treedb_graphs.py): rounded card, soft tint of the
|
|
@@ -9534,13 +9544,24 @@ function publish_legend_state(gobj, visible)
|
|
|
9534
9544
|
loose_shown: model.loose_shown.has(topic_name),
|
|
9535
9545
|
hidden: model.hidden.has(topic_name),
|
|
9536
9546
|
linked: model.linked.has(topic_name),
|
|
9547
|
+
hierarchical: fold_is_hierarchical(priv.descs, topic_name),
|
|
9537
9548
|
});
|
|
9538
9549
|
}
|
|
9539
9550
|
|
|
9540
9551
|
gobj_publish_event(gobj, "EV_LEGEND_STATE", {
|
|
9541
9552
|
main_topic: model.main_topic,
|
|
9542
|
-
|
|
9553
|
+
/* CHOSEN only when the reader's pick is the one in force: a
|
|
9554
|
+
* pick the rule refuses (not hierarchical) is ignored. */
|
|
9555
|
+
main_chosen: !!model.main_topic &&
|
|
9556
|
+
gobj_read_str_attr(gobj, "main_topic") === model.main_topic,
|
|
9543
9557
|
topics: topics,
|
|
9558
|
+
/* The toolbar's stepper: the tree it walks, where it stands,
|
|
9559
|
+
* and how deep it goes. `levels` 0 = nothing to step through. */
|
|
9560
|
+
fold: {
|
|
9561
|
+
topic: model.main_topic,
|
|
9562
|
+
level: current_fold_level(gobj),
|
|
9563
|
+
levels: model.levels,
|
|
9564
|
+
},
|
|
9544
9565
|
});
|
|
9545
9566
|
}
|
|
9546
9567
|
|
|
@@ -10003,14 +10024,13 @@ function ac_load_data(gobj, event, kw, src)
|
|
|
10003
10024
|
}
|
|
10004
10025
|
|
|
10005
10026
|
if(all_loaded && priv.graph) {
|
|
10006
|
-
/* The tree of the records, opened
|
|
10007
|
-
* roots and their children by default. Only that
|
|
10008
|
-
* G6 nodes -- a treedb of a thousand records opens
|
|
10009
|
-
* handful of cards with a count on each cut. */
|
|
10027
|
+
/* The tree of the records, the main one opened to the reader's
|
|
10028
|
+
* level: the roots and their children by default. Only that
|
|
10029
|
+
* much becomes G6 nodes -- a treedb of a thousand records opens
|
|
10030
|
+
* as a handful of cards with a count on each cut. */
|
|
10010
10031
|
rebuild_fold_model(gobj);
|
|
10011
|
-
|
|
10012
|
-
priv._fold_model, priv._fold_state,
|
|
10013
|
-
gobj_read_integer_attr(gobj, "expand_depth")
|
|
10032
|
+
fold_expand_to_level(
|
|
10033
|
+
priv._fold_model, priv._fold_state, current_fold_level(gobj)
|
|
10014
10034
|
);
|
|
10015
10035
|
|
|
10016
10036
|
/* Before the layout runs, and only now: whether anybody has
|
|
@@ -10622,21 +10642,62 @@ function ac_show_more(gobj, event, kw, src)
|
|
|
10622
10642
|
}
|
|
10623
10643
|
|
|
10624
10644
|
/************************************************************
|
|
10625
|
-
* The
|
|
10626
|
-
*
|
|
10627
|
-
* the
|
|
10645
|
+
* The level of the main tree the toolbar's stepper stands on:
|
|
10646
|
+
* `fold_level` once the reader has stepped, `expand_depth`
|
|
10647
|
+
* until then, clamped to the levels the tree has.
|
|
10628
10648
|
************************************************************/
|
|
10629
|
-
function
|
|
10649
|
+
function current_fold_level(gobj)
|
|
10630
10650
|
{
|
|
10631
10651
|
let priv = gobj.priv;
|
|
10652
|
+
let level = gobj_read_integer_attr(gobj, "fold_level");
|
|
10632
10653
|
|
|
10654
|
+
if(!(level > 0)) {
|
|
10655
|
+
level = gobj_read_integer_attr(gobj, "expand_depth");
|
|
10656
|
+
}
|
|
10633
10657
|
if(!priv._fold_model) {
|
|
10634
|
-
|
|
10658
|
+
return level;
|
|
10659
|
+
}
|
|
10660
|
+
return fold_level_clamp(priv._fold_model, level);
|
|
10661
|
+
}
|
|
10662
|
+
|
|
10663
|
+
/************************************************************
|
|
10664
|
+
* The toolbar's stepper: one level more or one less of the
|
|
10665
|
+
* MAIN tree (the host owns the number and persists it; this
|
|
10666
|
+
* applies it). Deeper opens the hooks above the new level,
|
|
10667
|
+
* shallower folds from it down -- a level is a floor, not a
|
|
10668
|
+
* picture, so what the reader opened by its pill stays (see
|
|
10669
|
+
* treedb_fold_model). Replaces expand all / collapse all
|
|
10670
|
+
* (7.23.141): the whole treedb at once was the pile, and
|
|
10671
|
+
* closing threw away everything the reader had opened.
|
|
10672
|
+
************************************************************/
|
|
10673
|
+
function ac_set_fold_level(gobj, event, kw, src)
|
|
10674
|
+
{
|
|
10675
|
+
let priv = gobj.priv;
|
|
10676
|
+
let level = Number(kw && kw.level);
|
|
10677
|
+
|
|
10678
|
+
if(!(level > 0)) {
|
|
10679
|
+
log_error(`${gobj_short_name(gobj)}: fold level '${kw && kw.level}' is not a level`);
|
|
10635
10680
|
return -1;
|
|
10636
10681
|
}
|
|
10682
|
+
if(!priv._fold_model) {
|
|
10683
|
+
gobj_write_attr(gobj, "fold_level", level);
|
|
10684
|
+
return 0; /* applied when the records arrive */
|
|
10685
|
+
}
|
|
10686
|
+
|
|
10687
|
+
let from = current_fold_level(gobj);
|
|
10688
|
+
level = fold_level_clamp(priv._fold_model, level);
|
|
10689
|
+
gobj_write_attr(gobj, "fold_level", level);
|
|
10690
|
+
if(level === from) {
|
|
10691
|
+
return 0;
|
|
10692
|
+
}
|
|
10693
|
+
|
|
10637
10694
|
let keep = fold_keep_node(gobj);
|
|
10638
10695
|
let vp = yui_graph_viewport_of(priv.graph, keep);
|
|
10639
|
-
|
|
10696
|
+
if(level > from) {
|
|
10697
|
+
fold_open_levels(priv._fold_model, priv._fold_state, level);
|
|
10698
|
+
} else {
|
|
10699
|
+
fold_close_levels(priv._fold_model, priv._fold_state, level);
|
|
10700
|
+
}
|
|
10640
10701
|
reconcile_fold(gobj, {keep: keep, keep_vp: vp});
|
|
10641
10702
|
|
|
10642
10703
|
return 0;
|
|
@@ -10669,31 +10730,12 @@ function fold_keep_node(gobj)
|
|
|
10669
10730
|
return "";
|
|
10670
10731
|
}
|
|
10671
10732
|
|
|
10672
|
-
/************************************************************
|
|
10673
|
-
* The roots alone.
|
|
10674
|
-
************************************************************/
|
|
10675
|
-
function ac_collapse_all(gobj, event, kw, src)
|
|
10676
|
-
{
|
|
10677
|
-
let priv = gobj.priv;
|
|
10678
|
-
|
|
10679
|
-
if(!priv._fold_model) {
|
|
10680
|
-
log_error(`${gobj_short_name(gobj)}: collapse all with no tree loaded`);
|
|
10681
|
-
return -1;
|
|
10682
|
-
}
|
|
10683
|
-
let keep = fold_keep_node(gobj);
|
|
10684
|
-
let vp = yui_graph_viewport_of(priv.graph, keep);
|
|
10685
|
-
fold_collapse_all(priv._fold_model, priv._fold_state);
|
|
10686
|
-
reconcile_fold(gobj, {keep: keep, keep_vp: vp});
|
|
10687
|
-
|
|
10688
|
-
return 0;
|
|
10689
|
-
}
|
|
10690
|
-
|
|
10691
10733
|
/************************************************************
|
|
10692
10734
|
* The legend's three settings, from the host, which persists
|
|
10693
10735
|
* them. Each rebuilds the tree; the fold state is kept -- it is
|
|
10694
10736
|
* keyed by group, and a group that is gone is never read -- except
|
|
10695
10737
|
* for a new MAIN topic, whose roots are other roots: the tree
|
|
10696
|
-
* opens again at
|
|
10738
|
+
* opens again at the reader's level, now counted from them.
|
|
10697
10739
|
************************************************************/
|
|
10698
10740
|
function ac_set_hidden_topics(gobj, event, kw, src)
|
|
10699
10741
|
{
|
|
@@ -10724,9 +10766,8 @@ function ac_set_main_topic(gobj, event, kw, src)
|
|
|
10724
10766
|
let keep = fold_keep_node(gobj);
|
|
10725
10767
|
let vp = yui_graph_viewport_of(priv.graph, keep);
|
|
10726
10768
|
rebuild_fold_model(gobj);
|
|
10727
|
-
|
|
10728
|
-
priv._fold_model, priv._fold_state,
|
|
10729
|
-
gobj_read_integer_attr(gobj, "expand_depth")
|
|
10769
|
+
fold_expand_to_level(
|
|
10770
|
+
priv._fold_model, priv._fold_state, current_fold_level(gobj)
|
|
10730
10771
|
);
|
|
10731
10772
|
reconcile_fold(gobj, {relayout: true, keep: keep, keep_vp: vp});
|
|
10732
10773
|
return 0;
|
|
@@ -11404,8 +11445,7 @@ function create_gclass(gclass_name)
|
|
|
11404
11445
|
["EV_FIND_NODES", ac_find_nodes, null],
|
|
11405
11446
|
["EV_TOGGLE_FOLD", ac_toggle_fold, null],
|
|
11406
11447
|
["EV_SHOW_MORE", ac_show_more, null],
|
|
11407
|
-
["
|
|
11408
|
-
["EV_COLLAPSE_ALL", ac_collapse_all, null],
|
|
11448
|
+
["EV_SET_FOLD_LEVEL", ac_set_fold_level, null],
|
|
11409
11449
|
["EV_SET_HIDDEN_TOPICS", ac_set_hidden_topics, null],
|
|
11410
11450
|
["EV_SET_MAIN_TOPIC", ac_set_main_topic, null],
|
|
11411
11451
|
["EV_SET_LOOSE_TOPICS", ac_set_loose_topics, null],
|
|
@@ -11472,8 +11512,7 @@ function create_gclass(gclass_name)
|
|
|
11472
11512
|
["EV_CAMERA_CHANGED", event_flag_t.EVF_OUTPUT_EVENT|event_flag_t.EVF_NO_WARN_SUBS],
|
|
11473
11513
|
["EV_TOGGLE_FOLD", 0],
|
|
11474
11514
|
["EV_SHOW_MORE", 0],
|
|
11475
|
-
["
|
|
11476
|
-
["EV_COLLAPSE_ALL", 0],
|
|
11515
|
+
["EV_SET_FOLD_LEVEL", 0],
|
|
11477
11516
|
["EV_SET_HIDDEN_TOPICS", 0],
|
|
11478
11517
|
["EV_SET_MAIN_TOPIC", 0],
|
|
11479
11518
|
["EV_SET_LOOSE_TOPICS", 0],
|
|
@@ -1061,6 +1061,13 @@ function render_topics(gobj, $body)
|
|
|
1061
1061
|
? ["span", {class: "SCHEMA_TOPIC_SYSTEM tag ml-2",
|
|
1062
1062
|
i18n: "system"}, t("system")]
|
|
1063
1063
|
: ["span", {}, ""],
|
|
1064
|
+
/* The schema's mark of the topic the tree hangs from: the
|
|
1065
|
+
* same gold star the treedb graph's legend wears. */
|
|
1066
|
+
topic.main_topic
|
|
1067
|
+
? ["span", {class: "SCHEMA_TOPIC_MAIN ml-2",
|
|
1068
|
+
style: "color:#d4a017;",
|
|
1069
|
+
title: t("main topic"), "data-i18n-title": "main topic"}, "★"]
|
|
1070
|
+
: ["span", {}, ""],
|
|
1064
1071
|
ambiguous
|
|
1065
1072
|
? ["div", {class: "SCHEMA_TOPIC_ID is-size-7 yui-text-quiet"},
|
|
1066
1073
|
[["code", {}, `${topic.id}`]]]
|
|
@@ -1898,6 +1905,14 @@ function open_topic_form(gobj, treedb, topic)
|
|
|
1898
1905
|
["span", {class: "ml-1", i18n: "system topic"}, t("system topic")]
|
|
1899
1906
|
]]
|
|
1900
1907
|
]],
|
|
1908
|
+
/* A LABEL that wraps its control names it: no aria-label. */
|
|
1909
|
+
["div", {class: "SCHEMA_TOPIC_FORM_MAIN field mb-3"}, [
|
|
1910
|
+
["label", {class: "checkbox"}, [
|
|
1911
|
+
["input", el_attrs({type: "checkbox", "data-name": "main_topic",
|
|
1912
|
+
checked: record.main_topic ? "checked" : undefined})],
|
|
1913
|
+
["span", {class: "ml-1", i18n: "main topic"}, t("main topic")]
|
|
1914
|
+
]]
|
|
1915
|
+
]],
|
|
1901
1916
|
["div", {class: "SCHEMA_TOPIC_FORM_ACTIONS is-flex mt-4",
|
|
1902
1917
|
style: "gap:.5rem; justify-content:flex-end;"}, [
|
|
1903
1918
|
["button", {class: "SCHEMA_TOPIC_FORM_CANCEL button", type: "button"},
|
|
@@ -2501,6 +2516,7 @@ function topic_record(gobj, treedb, topic, values)
|
|
|
2501
2516
|
record.tkey = values.tkey || "";
|
|
2502
2517
|
record.topic_version = values.topic_version === "" ? 1 : Number(values.topic_version);
|
|
2503
2518
|
record.system_topic = !!values.system_topic;
|
|
2519
|
+
record.main_topic = !!values.main_topic;
|
|
2504
2520
|
|
|
2505
2521
|
return record;
|
|
2506
2522
|
}
|
|
@@ -82,7 +82,6 @@ import {
|
|
|
82
82
|
|
|
83
83
|
import {attach_clear} from "./yui_inputs.js";
|
|
84
84
|
import {register_c_g6_nodes_tree} from "./c_g6_nodes_tree.js";
|
|
85
|
-
import {yui_graph_fold_items} from "./yui_graph_camera.js";
|
|
86
85
|
import {yui_toolbar, yui_toolbar_icon, YUI_TOOLBAR_ICON_SIZE} from "./yui_toolbar.js";
|
|
87
86
|
import {
|
|
88
87
|
removeChildElements,
|
|
@@ -112,7 +111,7 @@ SDATA(data_type_t.DTP_LIST, "operation_modes", 0,
|
|
|
112
111
|
'["reading", "operation", "writing", "edition"]',
|
|
113
112
|
"Available **permission** or behaviour modes. These operation modes are required to be accomplish by the graph handler (G6 child). TODO permissions must match treedb permissions."),
|
|
114
113
|
SDATA(data_type_t.DTP_BOOLEAN, "with_treedb_tables",0, false, "Include treedb tables"),
|
|
115
|
-
SDATA(data_type_t.DTP_INTEGER, "expand_depth", 0,
|
|
114
|
+
SDATA(data_type_t.DTP_INTEGER, "expand_depth", 0, 1, "Levels of the treedb open when it loads, until the reader steps (`fold_level`): 1 = the roots alone (the default), 2 = the roots and their children. Forwarded to the G6 child"),
|
|
116
115
|
SDATA(data_type_t.DTP_INTEGER, "fold_page_size", 0, 24, "Children of one hook shown per page in the graph; a `+N` chip opens the next page. Forwarded to the G6 child"),
|
|
117
116
|
|
|
118
117
|
/*---------------- Forwarded to the G6 child (its own defaults) ----------------*/
|
|
@@ -137,6 +136,7 @@ SDATA(data_type_t.DTP_LIST, "loose_topics", sdata_flag_t.SDF_PERSIST, "[
|
|
|
137
136
|
SDATA(data_type_t.DTP_STRING, "node_mode", sdata_flag_t.SDF_PERSIST, "expanded", "How the records are drawn: `expanded` = the cards with their pills and ports, `compact` = one-line pills with the name, `shape` = coloured figures, the topology alone. User preference"),
|
|
138
137
|
SDATA(data_type_t.DTP_BOOLEAN, "node_labels", sdata_flag_t.SDF_PERSIST, true, "A `shape` node says its name under its figure. User preference"),
|
|
139
138
|
SDATA(data_type_t.DTP_DICT, "camera", sdata_flag_t.SDF_PERSIST, "{}", "Where the reader left the viewport: {zoom, x, y}. Handed to the engine on creation and written back when a move settles, so the graph opens where it was left. User preference, per treedb"),
|
|
139
|
+
SDATA(data_type_t.DTP_INTEGER, "fold_level", sdata_flag_t.SDF_PERSIST, 0, "The level of the MAIN tree the toolbar's stepper stands on: 1 = the main topic's roots, N = N-1 levels under them; 0 = never stepped, `expand_depth`. Handed to the engine on creation, so the graph opens at the level it was left. User preference, per treedb"),
|
|
140
140
|
|
|
141
141
|
/*---------------- Remote Connection ----------------*/
|
|
142
142
|
SDATA(data_type_t.DTP_POINTER, "gobj_remote_yuno", 0, null, "Remote Yuno to request data"),
|
|
@@ -269,6 +269,7 @@ function mt_create(gobj)
|
|
|
269
269
|
hook_port_position: "bottom",
|
|
270
270
|
fkey_port_position: "top",
|
|
271
271
|
expand_depth: gobj_read_integer_attr(gobj, "expand_depth"),
|
|
272
|
+
fold_level: gobj_read_integer_attr(gobj, "fold_level"),
|
|
272
273
|
fold_page_size: gobj_read_integer_attr(gobj, "fold_page_size"),
|
|
273
274
|
minimap_min_nodes: gobj_read_integer_attr(gobj, "minimap_min_nodes"),
|
|
274
275
|
with_gridline: gobj_read_bool_attr(gobj, "with_gridline"),
|
|
@@ -671,6 +672,117 @@ function refresh_node_shape_buttons(gobj, $root)
|
|
|
671
672
|
}
|
|
672
673
|
}
|
|
673
674
|
|
|
675
|
+
/************************************************************
|
|
676
|
+
* The fold STEPPER: one level less, where the tree stands, one
|
|
677
|
+
* level more.
|
|
678
|
+
*
|
|
679
|
+
* It replaces the fold pair (7.23.141). `expand all` opened every
|
|
680
|
+
* hook of the treedb -- the pile, on purpose, which on a treedb
|
|
681
|
+
* of thousands is a pile nobody reads -- and `collapse all` threw
|
|
682
|
+
* away everything the reader had opened. The stepper walks the
|
|
683
|
+
* MAIN tree a level at a time, the way the frontend view opens
|
|
684
|
+
* one level per press: `+` opens the next level under the main
|
|
685
|
+
* topic's roots, through every topic that is not hidden; `-`
|
|
686
|
+
* folds the deepest one. Pages and pills the reader opened stay.
|
|
687
|
+
*
|
|
688
|
+
* The readout says what the two buttons act on, which no chevron
|
|
689
|
+
* can: the main topic -- its swatch and its name, which is DATA
|
|
690
|
+
* and never translated -- and `level/levels`. On a phone the name
|
|
691
|
+
* goes and the numbers stay. The level is the reader's, per
|
|
692
|
+
* treedb: persisted (`fold_level`) and handed to the engine on
|
|
693
|
+
* creation, so a reload opens where it was left.
|
|
694
|
+
*
|
|
695
|
+
* Both buttons are born disabled: nothing can be stepped until
|
|
696
|
+
* the engine has said how deep the tree goes (EV_LEGEND_STATE).
|
|
697
|
+
************************************************************/
|
|
698
|
+
function fold_level_items(gobj, wide)
|
|
699
|
+
{
|
|
700
|
+
let step_button = (cls, icon, key, event_name) => {
|
|
701
|
+
return ['button', {class: `${cls} button`, type: 'button',
|
|
702
|
+
style: {height: wide, width: '2.5em'},
|
|
703
|
+
disabled: true,
|
|
704
|
+
title: t(key), 'data-i18n-title': key,
|
|
705
|
+
'aria-label': t(key), 'data-i18n-aria-label': key},
|
|
706
|
+
yui_toolbar_icon(icon),
|
|
707
|
+
{
|
|
708
|
+
click: (evt) => {
|
|
709
|
+
evt.stopPropagation();
|
|
710
|
+
gobj_send_event(gobj, event_name, {}, gobj);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
];
|
|
714
|
+
};
|
|
715
|
+
|
|
716
|
+
return [
|
|
717
|
+
['div', {class: 'GRAPH_FOLD_STEPPER buttons has-addons',
|
|
718
|
+
style: 'margin:0; flex:0 0 auto; flex-wrap:nowrap; align-items:stretch;'}, [
|
|
719
|
+
/* The same two chevrons the pills speak: `▸` folded, `▾` open. */
|
|
720
|
+
step_button('GRAPH_FOLD_COLLAPSE', 'yi-chevron-right',
|
|
721
|
+
'collapse one level', 'EV_COLLAPSE_LEVEL'),
|
|
722
|
+
/* A `div` with the button skin, like the legend's main
|
|
723
|
+
* chip: a readout between two buttons of one group, not
|
|
724
|
+
* a third button, and at full strength -- `is-static`
|
|
725
|
+
* is Bulma's grey of something switched off. */
|
|
726
|
+
['div', {class: 'GRAPH_FOLD_LEVEL button',
|
|
727
|
+
style: {height: wide, cursor: 'default', gap: '.35rem'},
|
|
728
|
+
role: 'status',
|
|
729
|
+
title: t('fold level'), 'data-i18n-title': 'fold level'}, [
|
|
730
|
+
['span', {class: 'GRAPH_FOLD_SWATCH',
|
|
731
|
+
style: 'display:none; width:.8rem; height:.8rem; border-radius:3px; ' +
|
|
732
|
+
'flex:0 0 auto; border:1px solid rgba(0,0,0,.25);'}],
|
|
733
|
+
['span', {class: 'GRAPH_FOLD_TOPIC is-hidden-mobile',
|
|
734
|
+
style: 'max-width:10em; overflow:hidden; text-overflow:ellipsis; ' +
|
|
735
|
+
'white-space:nowrap; font-weight:600;'}],
|
|
736
|
+
/* The WORD, not just the numbers: the legend chip under
|
|
737
|
+
* it says `departments 2/3` too, and there it means
|
|
738
|
+
* records on screen. */
|
|
739
|
+
['span', {class: 'GRAPH_FOLD_WORD', i18n: 'level'}, 'level'],
|
|
740
|
+
['span', {class: 'GRAPH_FOLD_COUNT',
|
|
741
|
+
style: 'font-variant-numeric:tabular-nums;'}, '–'],
|
|
742
|
+
]],
|
|
743
|
+
step_button('GRAPH_FOLD_EXPAND', 'yi-chevron-down',
|
|
744
|
+
'expand one level', 'EV_EXPAND_LEVEL'),
|
|
745
|
+
]],
|
|
746
|
+
];
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
/* Paint the stepper from the engine's last word (EV_LEGEND_STATE). */
|
|
750
|
+
function refresh_fold_level(gobj)
|
|
751
|
+
{
|
|
752
|
+
let priv = gobj.priv;
|
|
753
|
+
let $container = gobj_read_attr(gobj, "$container");
|
|
754
|
+
if(!$container) {
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
let $readout = $container.querySelector('.GRAPH_FOLD_LEVEL');
|
|
758
|
+
if(!$readout) {
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
let state = priv._legend_state || {};
|
|
763
|
+
let fold = state.fold || {};
|
|
764
|
+
let levels = (fold.levels > 0)? fold.levels : 0;
|
|
765
|
+
let level = (fold.level > 0)? fold.level : 0;
|
|
766
|
+
let topic = fold.topic || "";
|
|
767
|
+
let entry = (is_array(state.topics)? state.topics : []).find((e) => e.topic === topic);
|
|
768
|
+
|
|
769
|
+
let $swatch = $readout.querySelector('.GRAPH_FOLD_SWATCH');
|
|
770
|
+
$swatch.style.display = topic? 'inline-block' : 'none';
|
|
771
|
+
$swatch.style.background = (entry && entry.color) || '#94a3b8';
|
|
772
|
+
$readout.querySelector('.GRAPH_FOLD_TOPIC').textContent = topic;
|
|
773
|
+
$readout.querySelector('.GRAPH_FOLD_COUNT').textContent =
|
|
774
|
+
levels? `${level}/${levels}` : '–';
|
|
775
|
+
|
|
776
|
+
let $collapse = $container.querySelector('.GRAPH_FOLD_COLLAPSE');
|
|
777
|
+
let $expand = $container.querySelector('.GRAPH_FOLD_EXPAND');
|
|
778
|
+
if($collapse) {
|
|
779
|
+
$collapse.disabled = !(levels && level > 1);
|
|
780
|
+
}
|
|
781
|
+
if($expand) {
|
|
782
|
+
$expand.disabled = !(levels && level < levels);
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
|
|
674
786
|
function make_toolbar(gobj)
|
|
675
787
|
{
|
|
676
788
|
let priv = gobj.priv;
|
|
@@ -741,11 +853,9 @@ function make_toolbar(gobj)
|
|
|
741
853
|
* Center: how the graph is SHOWN.
|
|
742
854
|
*/
|
|
743
855
|
let center_items = [
|
|
744
|
-
/* The fold
|
|
745
|
-
*
|
|
746
|
-
|
|
747
|
-
* way back to the default depth. */
|
|
748
|
-
...yui_graph_fold_items(gobj, gobj_read_str_attr(gobj, "wide")),
|
|
856
|
+
/* The fold stepper: one level less or more of the main tree,
|
|
857
|
+
* and between the two, which tree and which level. */
|
|
858
|
+
...fold_level_items(gobj, gobj_read_str_attr(gobj, "wide")),
|
|
749
859
|
|
|
750
860
|
/* The view of the nodes -- full cards, one-line pills or
|
|
751
861
|
* figures -- and the name under a figure. */
|
|
@@ -2589,6 +2699,11 @@ function refresh_legend(gobj)
|
|
|
2589
2699
|
return String(a.topic).localeCompare(String(b.topic));
|
|
2590
2700
|
});
|
|
2591
2701
|
|
|
2702
|
+
/* Only a topic hooked to ITSELF can be the main one, so only those
|
|
2703
|
+
* offer the star -- and only when there is more than one to pick
|
|
2704
|
+
* from: with a single hierarchical topic the choice is made. */
|
|
2705
|
+
let hierarchical_count = topics.filter((e) => e.hierarchical && !e.hidden).length;
|
|
2706
|
+
|
|
2592
2707
|
for(let entry of topics) {
|
|
2593
2708
|
let topic_name = entry.topic;
|
|
2594
2709
|
let is_main = (topic_name === state.main_topic);
|
|
@@ -2703,7 +2818,7 @@ function refresh_legend(gobj)
|
|
|
2703
2818
|
style: 'padding:0 .5rem; cursor:default;',
|
|
2704
2819
|
title: t('main topic'), 'data-i18n-title': 'main topic'},
|
|
2705
2820
|
[['span', {style: LEGEND_STAR_MAIN_STYLE}, '★']]]);
|
|
2706
|
-
} else if(!hidden) {
|
|
2821
|
+
} else if(!hidden && entry.hierarchical && hierarchical_count > 1) {
|
|
2707
2822
|
extras.push(['button', {class: 'GRAPH_LEGEND_STAR button',
|
|
2708
2823
|
type: 'button', style: 'padding:0 .5rem;',
|
|
2709
2824
|
title: t('main topic'), 'data-i18n-title': 'main topic',
|
|
@@ -2795,6 +2910,7 @@ function ac_legend_state(gobj, event, kw, src)
|
|
|
2795
2910
|
let priv = gobj.priv;
|
|
2796
2911
|
priv._legend_state = kw || null;
|
|
2797
2912
|
refresh_legend(gobj);
|
|
2913
|
+
refresh_fold_level(gobj);
|
|
2798
2914
|
return 0;
|
|
2799
2915
|
}
|
|
2800
2916
|
|
|
@@ -2964,23 +3080,37 @@ function ac_find_nodes(gobj, event, kw, src)
|
|
|
2964
3080
|
}
|
|
2965
3081
|
|
|
2966
3082
|
/************************************************************
|
|
2967
|
-
*
|
|
2968
|
-
*
|
|
3083
|
+
* One press of the fold stepper (EV_EXPAND_LEVEL /
|
|
3084
|
+
* EV_COLLAPSE_LEVEL): the level moves by one, is SAVED (the
|
|
3085
|
+
* reader's, per treedb) and reaches the G6 child, which owns
|
|
3086
|
+
* the tree.
|
|
3087
|
+
*
|
|
3088
|
+
* The readout moves at once, not when the engine speaks again:
|
|
3089
|
+
* a reconcile is asynchronous, and a second press that came
|
|
3090
|
+
* before it would step from the level already left -- two
|
|
3091
|
+
* presses, one level.
|
|
2969
3092
|
************************************************************/
|
|
2970
|
-
function
|
|
3093
|
+
function ac_fold_level_step(gobj, event, kw, src)
|
|
2971
3094
|
{
|
|
2972
3095
|
let priv = gobj.priv;
|
|
2973
|
-
|
|
2974
|
-
|
|
3096
|
+
let fold = priv._legend_state && priv._legend_state.fold;
|
|
3097
|
+
|
|
3098
|
+
if(!fold || !(fold.levels > 0)) {
|
|
3099
|
+
log_error(`${gobj_short_name(gobj)}: ${event} with no tree to step through`);
|
|
3100
|
+
return -1;
|
|
3101
|
+
}
|
|
3102
|
+
let level = fold.level + ((event === "EV_EXPAND_LEVEL")? 1 : -1);
|
|
3103
|
+
if(level < 1 || level > fold.levels) {
|
|
3104
|
+
log_error(`${gobj_short_name(gobj)}: ${event}: level ${level} out of 1..${fold.levels}`);
|
|
3105
|
+
return -1;
|
|
2975
3106
|
}
|
|
2976
|
-
return 0;
|
|
2977
|
-
}
|
|
2978
3107
|
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
3108
|
+
gobj_write_attr(gobj, "fold_level", level);
|
|
3109
|
+
gobj_save_persistent_attrs(gobj, "fold_level");
|
|
3110
|
+
fold.level = level;
|
|
3111
|
+
refresh_fold_level(gobj);
|
|
2982
3112
|
if(priv.gobj_nodes_tree) {
|
|
2983
|
-
gobj_send_event(priv.gobj_nodes_tree, "
|
|
3113
|
+
gobj_send_event(priv.gobj_nodes_tree, "EV_SET_FOLD_LEVEL", {level: level}, gobj);
|
|
2984
3114
|
}
|
|
2985
3115
|
return 0;
|
|
2986
3116
|
}
|
|
@@ -3181,8 +3311,8 @@ function create_gclass(gclass_name)
|
|
|
3181
3311
|
["EV_SET_OPERATION_MODE", ac_set_operation_mode, null],
|
|
3182
3312
|
["EV_SET_FOCUS_TOPIC", ac_set_focus_topic, null],
|
|
3183
3313
|
["EV_FIND_NODES", ac_find_nodes, null],
|
|
3184
|
-
["
|
|
3185
|
-
["
|
|
3314
|
+
["EV_EXPAND_LEVEL", ac_fold_level_step, null],
|
|
3315
|
+
["EV_COLLAPSE_LEVEL", ac_fold_level_step, null],
|
|
3186
3316
|
["EV_SET_NODE_MODE", ac_set_node_mode, null],
|
|
3187
3317
|
["EV_TOGGLE_NODE_LABELS", ac_toggle_node_labels, null],
|
|
3188
3318
|
["EV_LAYOUT_AUTOSET", ac_layout_autoset, null],
|
|
@@ -3226,8 +3356,8 @@ function create_gclass(gclass_name)
|
|
|
3226
3356
|
["EV_SET_OPERATION_MODE", 0],
|
|
3227
3357
|
["EV_SET_FOCUS_TOPIC", 0],
|
|
3228
3358
|
["EV_FIND_NODES", 0],
|
|
3229
|
-
["
|
|
3230
|
-
["
|
|
3359
|
+
["EV_EXPAND_LEVEL", 0],
|
|
3360
|
+
["EV_COLLAPSE_LEVEL", 0],
|
|
3231
3361
|
["EV_SET_NODE_MODE", 0],
|
|
3232
3362
|
["EV_TOGGLE_NODE_LABELS", 0],
|
|
3233
3363
|
["EV_FIND_RESULT", 0],
|
package/src/schema_import.js
CHANGED
|
@@ -38,7 +38,8 @@ import {
|
|
|
38
38
|
* the fkey, `order`) is composed, and `_geometry` belongs to whoever
|
|
39
39
|
* laid the graph out — an import must not move their boxes. */
|
|
40
40
|
const TOPIC_FIELDS = [
|
|
41
|
-
"pkey", "pkey2s", "system_flag", "tkey", "topic_version", "system_topic"
|
|
41
|
+
"pkey", "pkey2s", "system_flag", "tkey", "topic_version", "system_topic",
|
|
42
|
+
"main_topic"
|
|
42
43
|
];
|
|
43
44
|
|
|
44
45
|
/* Same for a `cols` record. */
|