@yuneta/gobj-ui 7.23.67 → 7.23.69
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 +57 -0
- package/dist/gobj-ui.cjs.js +453 -83
- package/dist/gobj-ui.es.js +454 -84
- package/package.json +1 -1
- package/src/c_g6_nodes_tree.js +146 -8
- package/src/c_yui_treedb_graph.js +266 -80
- package/src/treedb_fold_model.js +171 -15
- package/src/treedb_fold_model.test.js +120 -2
|
@@ -115,6 +115,9 @@ SDATA(data_type_t.DTP_INTEGER, "fold_page_size", 0, 24, "Children of one
|
|
|
115
115
|
/*---------------- User last selections ----------------*/
|
|
116
116
|
SDATA(data_type_t.DTP_STRING, "operation_mode", sdata_flag_t.SDF_PERSIST, "reading", "Current operation mode (internal behaviour or role). Changed by the user trough the gui."),
|
|
117
117
|
SDATA(data_type_t.DTP_STRING, "layout", sdata_flag_t.SDF_PERSIST, "", "Current graph layout. User preference. Changed by the user through the gui."),
|
|
118
|
+
SDATA(data_type_t.DTP_LIST, "hidden_topics", sdata_flag_t.SDF_PERSIST, "[]", "Topics hidden from the graph (legend). User preference, per treedb"),
|
|
119
|
+
SDATA(data_type_t.DTP_STRING, "main_topic", sdata_flag_t.SDF_PERSIST, "", "The topic the tree hangs from; empty = deduced by the graph. User preference, per treedb"),
|
|
120
|
+
SDATA(data_type_t.DTP_LIST, "loose_topics", sdata_flag_t.SDF_PERSIST, "[]", "Topics whose LOOSE records (no parent) are shown. User preference, per treedb"),
|
|
118
121
|
|
|
119
122
|
/*---------------- Remote Connection ----------------*/
|
|
120
123
|
SDATA(data_type_t.DTP_POINTER, "gobj_remote_yuno", 0, null, "Remote Yuno to request data"),
|
|
@@ -165,6 +168,7 @@ let PRIVATE_DATA = {
|
|
|
165
168
|
operation_mode: null,
|
|
166
169
|
layout: null,
|
|
167
170
|
_topics_subscribed: {},
|
|
171
|
+
_legend_state: null, /* the engine's last EV_LEGEND_STATE */
|
|
168
172
|
_links_subscribed: false, /* EV_TREEDB_NODE_LINKED/UNLINKED (treedb-wide) */
|
|
169
173
|
|
|
170
174
|
is_pinhold_window: false, // inherited of v6, todo review
|
|
@@ -246,6 +250,9 @@ function mt_create(gobj)
|
|
|
246
250
|
fkey_port_position: "top",
|
|
247
251
|
expand_depth: gobj_read_integer_attr(gobj, "expand_depth"),
|
|
248
252
|
fold_page_size: gobj_read_integer_attr(gobj, "fold_page_size"),
|
|
253
|
+
hidden_topics: gobj_read_attr(gobj, "hidden_topics") || [],
|
|
254
|
+
main_topic: gobj_read_str_attr(gobj, "main_topic") || "",
|
|
255
|
+
loose_topics: gobj_read_attr(gobj, "loose_topics") || [],
|
|
249
256
|
},
|
|
250
257
|
gobj
|
|
251
258
|
);
|
|
@@ -363,13 +370,18 @@ function build_ui(gobj)
|
|
|
363
370
|
// Don't use is-flex, don't work well with is-hidden
|
|
364
371
|
['div', {class: 'C_YUI_TREEDB_GRAPH', style: `height:100%; display:flex; flex-direction:column;`}, [
|
|
365
372
|
['div', {class: 'GRAPH_TOOLBAR_ROW is-flex-grow-0 is-flex is-align-items-center'}, row_items],
|
|
366
|
-
/*
|
|
367
|
-
*
|
|
368
|
-
*
|
|
369
|
-
*
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
+
/* The legend: a strip under the toolbar, ALWAYS there, one
|
|
374
|
+
* chip per topic. It is the graph's layer control, not a
|
|
375
|
+
* colour key: a chip shows or hides its topic, marks the main
|
|
376
|
+
* one, shows the loose records, highlights. Painted from the
|
|
377
|
+
* engine's EV_LEGEND_STATE, which is the truth about what is
|
|
378
|
+
* drawn. A strip and not an overlay because it is read
|
|
379
|
+
* AGAINST the graph, and an overlay would cover the thing it
|
|
380
|
+
* explains. Scrolls sideways on a narrow screen. */
|
|
381
|
+
['div', {class: 'GRAPH_LEGEND is-flex-grow-0',
|
|
382
|
+
style: 'display:flex; flex-wrap:nowrap; align-items:center; ' +
|
|
383
|
+
'gap:.35rem; padding:.3rem .5rem; overflow-x:auto; ' +
|
|
384
|
+
'min-height:2.2rem;'}, []],
|
|
373
385
|
['div', {class: `GRAPH_BODY is-flex-grow-1 ${padding}`, style: 'height:100%; min-height:0; overflow:hidden;'}, [
|
|
374
386
|
['div', {id: priv.canvas_id, class: `GRAPH_CANVAS graph-container`, style: 'height:100%; min-height:0;border: 1px solid var(--bulma-border-weak);border-radius:0.2rem;'}, [
|
|
375
387
|
]]
|
|
@@ -575,23 +587,6 @@ function make_toolbar(gobj)
|
|
|
575
587
|
* way back to the default depth. */
|
|
576
588
|
...yui_graph_fold_items(gobj, gobj_read_str_attr(gobj, "wide")),
|
|
577
589
|
|
|
578
|
-
/* Which colour is which topic. The port colour of a node encodes
|
|
579
|
-
* the topic it links to, which is the whole point of the graph and
|
|
580
|
-
* which nothing on screen explained. Clicking an entry focuses that
|
|
581
|
-
* topic, so the legend is also the way to say "show me these" —
|
|
582
|
-
* and clicking the focused one again clears it. */
|
|
583
|
-
['button', {class: 'GRAPH_LEGEND_BTN button ml-2',
|
|
584
|
-
title: t('legend'), 'aria-label': t('legend'),
|
|
585
|
-
'data-i18n-title': 'legend', 'data-i18n-aria-label': 'legend'}, [
|
|
586
|
-
['i', {class: 'yi-square'}],
|
|
587
|
-
['span', {class: 'is-hidden-mobile', style: 'padding-left:5px;', i18n: 'legend'}, 'legend']
|
|
588
|
-
], {
|
|
589
|
-
click: (evt) => {
|
|
590
|
-
evt.stopPropagation();
|
|
591
|
-
gobj_send_event(gobj, "EV_TOGGLE_LEGEND", {}, gobj);
|
|
592
|
-
}
|
|
593
|
-
}],
|
|
594
|
-
|
|
595
590
|
/* Inspect the treedb's raw tranger json in the lazy tree viewer
|
|
596
591
|
* (print-tranger on the C_NODE service). A treedb can be huge, so
|
|
597
592
|
* the viewer drills in on demand — see open_json_viewer. */
|
|
@@ -2333,6 +2328,11 @@ function ac_set_operation_mode(gobj, event, kw, src)
|
|
|
2333
2328
|
* card's graph icon travels — as EV_TOPIC_SELECTED, which the host turns
|
|
2334
2329
|
* into the URL, so what you are looking at stays linkable.
|
|
2335
2330
|
************************************************************/
|
|
2331
|
+
/* The glyphs of a chip's small buttons: `is-small` sets a 0.75rem
|
|
2332
|
+
* font and a star or a crosshair at that size is a speck. The BUTTON
|
|
2333
|
+
* stays small so the strip keeps its height; the glyph inside grows. */
|
|
2334
|
+
const LEGEND_GLYPH_STYLE = 'font-size:1.5em; line-height:1; color:inherit;';
|
|
2335
|
+
|
|
2336
2336
|
function refresh_legend(gobj)
|
|
2337
2337
|
{
|
|
2338
2338
|
let priv = gobj.priv;
|
|
@@ -2345,72 +2345,194 @@ function refresh_legend(gobj)
|
|
|
2345
2345
|
return;
|
|
2346
2346
|
}
|
|
2347
2347
|
|
|
2348
|
-
let
|
|
2349
|
-
gobj_read_attr(priv.gobj_nodes_tree, "descs") : null;
|
|
2348
|
+
let state = priv._legend_state;
|
|
2350
2349
|
$legend.textContent = "";
|
|
2351
|
-
if(!
|
|
2352
|
-
return;
|
|
2350
|
+
if(!state || !is_array(state.topics)) {
|
|
2351
|
+
return; /* nothing drawn yet: the engine has not spoken */
|
|
2353
2352
|
}
|
|
2354
2353
|
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2354
|
+
/* The main topic leads the strip: it is the trunk everything else
|
|
2355
|
+
* hangs from, and the eye reads the strip left to right. */
|
|
2356
|
+
let topics = state.topics.slice().sort((a, b) => {
|
|
2357
|
+
let am = (a.topic === state.main_topic)? 0 : 1;
|
|
2358
|
+
let bm = (b.topic === state.main_topic)? 0 : 1;
|
|
2359
|
+
return am - bm;
|
|
2360
|
+
});
|
|
2361
|
+
|
|
2362
|
+
for(let entry of topics) {
|
|
2363
|
+
let topic_name = entry.topic;
|
|
2364
|
+
let is_main = (topic_name === state.main_topic);
|
|
2365
|
+
let focused = (priv.focus_topic === topic_name);
|
|
2366
|
+
let hidden = !!entry.hidden;
|
|
2367
|
+
|
|
2368
|
+
/* A control with a small, named target for each of the things
|
|
2369
|
+
* the strip can do, so a finger lands on ONE of them:
|
|
2370
|
+
* the body show / hide (the main topic cannot be hidden)
|
|
2371
|
+
* ★ make it the main topic
|
|
2372
|
+
* +N show / hide its loose records
|
|
2373
|
+
* ⌖ highlight (the focus)
|
|
2374
|
+
* The names are DATA: a topic name carries no i18n key and is
|
|
2375
|
+
* never translated; the counts are numbers. */
|
|
2376
|
+
let controls = [];
|
|
2377
|
+
|
|
2378
|
+
controls.push(
|
|
2379
|
+
['span', {class: 'GRAPH_LEGEND_SWATCH',
|
|
2380
|
+
style: `display:inline-block; width:.8rem; height:.8rem; ` +
|
|
2381
|
+
`border-radius:3px; margin-right:.35rem; flex:0 0 auto; ` +
|
|
2382
|
+
`background:${entry.color || "#94a3b8"}; ` +
|
|
2383
|
+
`border:1px solid rgba(0,0,0,.25);` +
|
|
2384
|
+
(hidden? 'opacity:.35;' : '')}],
|
|
2385
|
+
['span', {class: 'GRAPH_LEGEND_NAME',
|
|
2386
|
+
style: hidden? 'text-decoration:line-through; opacity:.6;' : ''},
|
|
2387
|
+
topic_name],
|
|
2388
|
+
['span', {class: 'GRAPH_LEGEND_COUNT has-text-grey',
|
|
2389
|
+
style: 'margin-left:.35rem; font-size:.8em;'},
|
|
2390
|
+
hidden? `${entry.total}` : `${entry.visible}/${entry.total}`]
|
|
2391
|
+
);
|
|
2392
|
+
|
|
2393
|
+
/* Built as an object first: an attribute that is `undefined`
|
|
2394
|
+
* is still SET by createElement2 (to the string "undefined"),
|
|
2395
|
+
* and a `disabled="undefined"` button is a disabled button --
|
|
2396
|
+
* every chip was dead except its star. */
|
|
2397
|
+
let body_attrs = {
|
|
2398
|
+
class: 'GRAPH_LEGEND_ITEM button is-small' +
|
|
2399
|
+
(focused? ' is-primary is-light' : '') +
|
|
2400
|
+
(is_main? ' GRAPH_LEGEND_MAIN' : ''),
|
|
2401
|
+
type: 'button',
|
|
2402
|
+
style: 'gap:0; flex:0 0 auto;',
|
|
2403
|
+
title: is_main? t('main topic') : (hidden? t('show topic') : t('hide topic')),
|
|
2404
|
+
'aria-label': topic_name,
|
|
2405
|
+
'aria-pressed': hidden? 'false' : 'true',
|
|
2406
|
+
};
|
|
2407
|
+
if(is_main) {
|
|
2408
|
+
body_attrs.disabled = 'disabled';
|
|
2358
2409
|
}
|
|
2359
|
-
|
|
2360
|
-
|
|
2410
|
+
let $body = createElement2(
|
|
2411
|
+
['button', body_attrs, controls, {
|
|
2412
|
+
click: (evt) => {
|
|
2413
|
+
evt.stopPropagation();
|
|
2414
|
+
gobj_send_event(gobj, "EV_LEGEND_TOPIC",
|
|
2415
|
+
{topic: topic_name, action: "toggle"}, gobj);
|
|
2416
|
+
}
|
|
2417
|
+
}]
|
|
2418
|
+
);
|
|
2419
|
+
|
|
2420
|
+
let extras = [];
|
|
2421
|
+
|
|
2422
|
+
/* The star: filled on the main one, hollow on the others,
|
|
2423
|
+
* where it moves the star. On a main topic the READER chose it
|
|
2424
|
+
* is a button too: pressing it hands the choice back to the
|
|
2425
|
+
* graph (deduced again). On a deduced one it is a mark. */
|
|
2426
|
+
if(is_main && state.main_chosen) {
|
|
2427
|
+
extras.push(['button', {class: 'GRAPH_LEGEND_STAR button is-small is-warning is-light',
|
|
2428
|
+
type: 'button', style: 'padding:0 .4rem;',
|
|
2429
|
+
title: t('main topic'), 'data-i18n-title': 'main topic',
|
|
2430
|
+
'aria-label': t('main topic'), 'aria-pressed': 'true'},
|
|
2431
|
+
[['span', {style: LEGEND_GLYPH_STYLE}, '★']], {
|
|
2432
|
+
click: (evt) => {
|
|
2433
|
+
evt.stopPropagation();
|
|
2434
|
+
gobj_send_event(gobj, "EV_LEGEND_TOPIC",
|
|
2435
|
+
{topic: "", action: "main"}, gobj);
|
|
2436
|
+
}
|
|
2437
|
+
}]);
|
|
2438
|
+
} else if(is_main) {
|
|
2439
|
+
extras.push(['span', {class: 'GRAPH_LEGEND_STAR button is-small is-static',
|
|
2440
|
+
style: 'padding:0 .4rem;',
|
|
2441
|
+
title: t('main topic'), 'data-i18n-title': 'main topic'},
|
|
2442
|
+
[['span', {style: LEGEND_GLYPH_STYLE}, '★']]]);
|
|
2443
|
+
} else if(!hidden) {
|
|
2444
|
+
extras.push(['button', {class: 'GRAPH_LEGEND_STAR button is-small',
|
|
2445
|
+
type: 'button', style: 'padding:0 .4rem;',
|
|
2446
|
+
title: t('main topic'), 'data-i18n-title': 'main topic',
|
|
2447
|
+
'aria-label': t('main topic')},
|
|
2448
|
+
[['span', {style: LEGEND_GLYPH_STYLE}, '☆']], {
|
|
2449
|
+
click: (evt) => {
|
|
2450
|
+
evt.stopPropagation();
|
|
2451
|
+
gobj_send_event(gobj, "EV_LEGEND_TOPIC",
|
|
2452
|
+
{topic: topic_name, action: "main"}, gobj);
|
|
2453
|
+
}
|
|
2454
|
+
}]);
|
|
2361
2455
|
}
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
class: '
|
|
2456
|
+
|
|
2457
|
+
/* Loose records: the ones that should hang from the main tree
|
|
2458
|
+
* and do not. Only where there are any, and only for a topic
|
|
2459
|
+
* the schema hangs from the main one. */
|
|
2460
|
+
if(!hidden && entry.linked && entry.loose > 0) {
|
|
2461
|
+
extras.push(['button', {
|
|
2462
|
+
class: 'GRAPH_LEGEND_LOOSE button is-small' +
|
|
2463
|
+
(entry.loose_shown? ' is-warning is-light' : ''),
|
|
2464
|
+
type: 'button', style: 'padding:0 .45rem;',
|
|
2465
|
+
title: t('loose records'), 'data-i18n-title': 'loose records',
|
|
2466
|
+
'aria-label': t('loose records'),
|
|
2467
|
+
'aria-pressed': entry.loose_shown? 'true' : 'false'
|
|
2468
|
+
}, `+${entry.loose}`, {
|
|
2469
|
+
click: (evt) => {
|
|
2470
|
+
evt.stopPropagation();
|
|
2471
|
+
gobj_send_event(gobj, "EV_LEGEND_TOPIC",
|
|
2472
|
+
{topic: topic_name, action: "loose"}, gobj);
|
|
2473
|
+
}
|
|
2474
|
+
}]);
|
|
2475
|
+
}
|
|
2476
|
+
|
|
2477
|
+
if(!hidden) {
|
|
2478
|
+
extras.push(['button', {
|
|
2479
|
+
class: 'GRAPH_LEGEND_FOCUS button is-small' +
|
|
2369
2480
|
(focused? ' is-primary is-light' : ''),
|
|
2370
|
-
|
|
2371
|
-
'
|
|
2481
|
+
type: 'button', style: 'padding:0 .4rem;',
|
|
2482
|
+
title: t('highlight topic'), 'data-i18n-title': 'highlight topic',
|
|
2483
|
+
'aria-label': t('highlight topic'),
|
|
2372
2484
|
'aria-pressed': focused? 'true' : 'false'
|
|
2373
|
-
}, [
|
|
2374
|
-
['span', {class: 'GRAPH_LEGEND_SWATCH',
|
|
2375
|
-
style: `display:inline-block; width:.8rem; height:.8rem; ` +
|
|
2376
|
-
`border-radius:3px; margin-right:.4rem; ` +
|
|
2377
|
-
`background:${desc.color}; ` +
|
|
2378
|
-
`border:1px solid rgba(0,0,0,.25);`}],
|
|
2379
|
-
/* The topic name is DATA: it carries no i18n key and is
|
|
2380
|
-
* never translated. */
|
|
2381
|
-
['span', {}, topic_name]
|
|
2382
|
-
], {
|
|
2485
|
+
}, [['i', {class: 'yi-location-crosshairs', style: LEGEND_GLYPH_STYLE}]], {
|
|
2383
2486
|
click: (evt) => {
|
|
2384
2487
|
evt.stopPropagation();
|
|
2385
|
-
gobj_send_event(gobj, "EV_LEGEND_TOPIC",
|
|
2488
|
+
gobj_send_event(gobj, "EV_LEGEND_TOPIC",
|
|
2489
|
+
{topic: topic_name, action: "focus"}, gobj);
|
|
2386
2490
|
}
|
|
2387
|
-
}]
|
|
2491
|
+
}]);
|
|
2492
|
+
}
|
|
2493
|
+
|
|
2494
|
+
let $chip = createElement2(
|
|
2495
|
+
['div', {class: 'GRAPH_LEGEND_CHIP buttons has-addons',
|
|
2496
|
+
style: 'margin:0; flex:0 0 auto; flex-wrap:nowrap;'},
|
|
2497
|
+
[$body].concat(extras.map((spec) => createElement2(spec)))]
|
|
2388
2498
|
);
|
|
2389
|
-
$legend.appendChild($
|
|
2499
|
+
$legend.appendChild($chip);
|
|
2390
2500
|
}
|
|
2391
2501
|
}
|
|
2392
2502
|
|
|
2503
|
+
/************************************************************
|
|
2504
|
+
* The engine said what is drawn: repaint the strip.
|
|
2505
|
+
************************************************************/
|
|
2506
|
+
function ac_legend_state(gobj, event, kw, src)
|
|
2507
|
+
{
|
|
2508
|
+
let priv = gobj.priv;
|
|
2509
|
+
priv._legend_state = kw || null;
|
|
2510
|
+
refresh_legend(gobj);
|
|
2511
|
+
return 0;
|
|
2512
|
+
}
|
|
2513
|
+
|
|
2393
2514
|
/************************************************************
|
|
2394
2515
|
*
|
|
2395
2516
|
************************************************************/
|
|
2396
|
-
|
|
2517
|
+
/************************************************************
|
|
2518
|
+
* One of the legend's settings changed: write it, SAVE it (they
|
|
2519
|
+
* are the reader's, per treedb) and tell the engine.
|
|
2520
|
+
************************************************************/
|
|
2521
|
+
function set_legend_list_attr(gobj, name, event_name, topics)
|
|
2397
2522
|
{
|
|
2398
|
-
let
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
log_error(`${gobj_short_name(gobj)}: no legend strip to toggle`);
|
|
2405
|
-
return -1;
|
|
2406
|
-
}
|
|
2407
|
-
if($legend.classList.contains("is-hidden")) {
|
|
2408
|
-
refresh_legend(gobj);
|
|
2409
|
-
$legend.classList.remove("is-hidden");
|
|
2410
|
-
} else {
|
|
2411
|
-
$legend.classList.add("is-hidden");
|
|
2523
|
+
let priv = gobj.priv;
|
|
2524
|
+
|
|
2525
|
+
gobj_write_attr(gobj, name, topics);
|
|
2526
|
+
gobj_save_persistent_attrs(gobj, name);
|
|
2527
|
+
if(priv.gobj_nodes_tree) {
|
|
2528
|
+
gobj_send_event(priv.gobj_nodes_tree, event_name, {topics: topics}, gobj);
|
|
2412
2529
|
}
|
|
2413
|
-
|
|
2530
|
+
}
|
|
2531
|
+
|
|
2532
|
+
function legend_list_attr(gobj, name)
|
|
2533
|
+
{
|
|
2534
|
+
let list = gobj_read_attr(gobj, name);
|
|
2535
|
+
return is_array(list)? list.slice() : [];
|
|
2414
2536
|
}
|
|
2415
2537
|
|
|
2416
2538
|
/************************************************************
|
|
@@ -2422,11 +2544,60 @@ function ac_legend_topic(gobj, event, kw, src)
|
|
|
2422
2544
|
{
|
|
2423
2545
|
let priv = gobj.priv;
|
|
2424
2546
|
let topic = (kw && kw.topic) || "";
|
|
2425
|
-
|
|
2547
|
+
let action = (kw && kw.action) || "focus";
|
|
2548
|
+
if(!topic && action !== "main") {
|
|
2426
2549
|
log_error(`${gobj_short_name(gobj)}: legend click with no topic`);
|
|
2427
2550
|
return -1;
|
|
2428
2551
|
}
|
|
2429
2552
|
|
|
2553
|
+
let state = priv._legend_state || {};
|
|
2554
|
+
|
|
2555
|
+
switch(action) {
|
|
2556
|
+
case "toggle": {
|
|
2557
|
+
/* Show or hide the topic. Never the main one: hiding the
|
|
2558
|
+
* trunk turns everything into roots, which is the pile. */
|
|
2559
|
+
if(topic === state.main_topic) {
|
|
2560
|
+
log_error(`${gobj_short_name(gobj)}: the main topic '${topic}' cannot be hidden`);
|
|
2561
|
+
return -1;
|
|
2562
|
+
}
|
|
2563
|
+
let hidden = legend_list_attr(gobj, "hidden_topics");
|
|
2564
|
+
if(str_in_list(hidden, topic)) {
|
|
2565
|
+
delete_from_list(hidden, topic);
|
|
2566
|
+
} else {
|
|
2567
|
+
hidden.push(topic);
|
|
2568
|
+
}
|
|
2569
|
+
set_legend_list_attr(gobj, "hidden_topics", "EV_SET_HIDDEN_TOPICS", hidden);
|
|
2570
|
+
return 0;
|
|
2571
|
+
}
|
|
2572
|
+
|
|
2573
|
+
case "loose": {
|
|
2574
|
+
let loose = legend_list_attr(gobj, "loose_topics");
|
|
2575
|
+
if(str_in_list(loose, topic)) {
|
|
2576
|
+
delete_from_list(loose, topic);
|
|
2577
|
+
} else {
|
|
2578
|
+
loose.push(topic);
|
|
2579
|
+
}
|
|
2580
|
+
set_legend_list_attr(gobj, "loose_topics", "EV_SET_LOOSE_TOPICS", loose);
|
|
2581
|
+
return 0;
|
|
2582
|
+
}
|
|
2583
|
+
|
|
2584
|
+
case "main": {
|
|
2585
|
+
gobj_write_str_attr(gobj, "main_topic", topic);
|
|
2586
|
+
gobj_save_persistent_attrs(gobj, "main_topic");
|
|
2587
|
+
if(priv.gobj_nodes_tree) {
|
|
2588
|
+
gobj_send_event(priv.gobj_nodes_tree, "EV_SET_MAIN_TOPIC", {topic: topic}, gobj);
|
|
2589
|
+
}
|
|
2590
|
+
return 0;
|
|
2591
|
+
}
|
|
2592
|
+
|
|
2593
|
+
case "focus":
|
|
2594
|
+
break;
|
|
2595
|
+
|
|
2596
|
+
default:
|
|
2597
|
+
log_error(`${gobj_short_name(gobj)}: legend action unknown: ${action}`);
|
|
2598
|
+
return -1;
|
|
2599
|
+
}
|
|
2600
|
+
|
|
2430
2601
|
let next = (priv.focus_topic === topic)? "" : topic;
|
|
2431
2602
|
|
|
2432
2603
|
/* APPLY, then announce — in that order, and both halves are needed.
|
|
@@ -2528,7 +2699,14 @@ function ac_find_result(gobj, event, kw, src)
|
|
|
2528
2699
|
return 0;
|
|
2529
2700
|
}
|
|
2530
2701
|
|
|
2531
|
-
|
|
2702
|
+
let text = t("matches", {count: (kw && kw.matches) || 0});
|
|
2703
|
+
/* Matches in HIDDEN topics, counted apart: "0" alone reads as
|
|
2704
|
+
* "does not exist" when it means "is hidden". */
|
|
2705
|
+
let hidden = (kw && kw.hidden_matches) || 0;
|
|
2706
|
+
if(hidden > 0) {
|
|
2707
|
+
text += ` (+${hidden} ${t("hidden topics")})`;
|
|
2708
|
+
}
|
|
2709
|
+
$count.textContent = text;
|
|
2532
2710
|
$result.classList.remove("is-hidden");
|
|
2533
2711
|
return 0;
|
|
2534
2712
|
}
|
|
@@ -2537,6 +2715,18 @@ function ac_set_focus_topic(gobj, event, kw, src)
|
|
|
2537
2715
|
{
|
|
2538
2716
|
let priv = gobj.priv;
|
|
2539
2717
|
priv.focus_topic = (kw && kw.topic) || "";
|
|
2718
|
+
|
|
2719
|
+
/* The URL wins over the strip: a route that lands on a hidden
|
|
2720
|
+
* topic shows it first, or it would highlight nothing and say
|
|
2721
|
+
* nothing about why. */
|
|
2722
|
+
if(priv.focus_topic) {
|
|
2723
|
+
let hidden = legend_list_attr(gobj, "hidden_topics");
|
|
2724
|
+
if(str_in_list(hidden, priv.focus_topic)) {
|
|
2725
|
+
delete_from_list(hidden, priv.focus_topic);
|
|
2726
|
+
set_legend_list_attr(gobj, "hidden_topics", "EV_SET_HIDDEN_TOPICS", hidden);
|
|
2727
|
+
}
|
|
2728
|
+
}
|
|
2729
|
+
|
|
2540
2730
|
if(priv.gobj_nodes_tree) {
|
|
2541
2731
|
gobj_send_event(
|
|
2542
2732
|
priv.gobj_nodes_tree,
|
|
@@ -2547,11 +2737,7 @@ function ac_set_focus_topic(gobj, event, kw, src)
|
|
|
2547
2737
|
}
|
|
2548
2738
|
/* Keep the legend's mark on the topic that is actually focused —
|
|
2549
2739
|
* including when the focus arrives from the URL and not from a click. */
|
|
2550
|
-
|
|
2551
|
-
let $legend = $container? $container.querySelector(".GRAPH_LEGEND") : null;
|
|
2552
|
-
if($legend && !$legend.classList.contains("is-hidden")) {
|
|
2553
|
-
refresh_legend(gobj);
|
|
2554
|
-
}
|
|
2740
|
+
refresh_legend(gobj);
|
|
2555
2741
|
return 0;
|
|
2556
2742
|
}
|
|
2557
2743
|
|
|
@@ -2644,8 +2830,8 @@ function create_gclass(gclass_name)
|
|
|
2644
2830
|
["EV_EXPAND_ALL", ac_expand_all, null],
|
|
2645
2831
|
["EV_COLLAPSE_ALL", ac_collapse_all, null],
|
|
2646
2832
|
["EV_LAYOUT_AUTOSET", ac_layout_autoset, null],
|
|
2647
|
-
["EV_TOGGLE_LEGEND", ac_toggle_legend, null],
|
|
2648
2833
|
["EV_LEGEND_TOPIC", ac_legend_topic, null],
|
|
2834
|
+
["EV_LEGEND_STATE", ac_legend_state, null],
|
|
2649
2835
|
["EV_FIND_RESULT", ac_find_result, null],
|
|
2650
2836
|
["EV_SHOW", ac_show, null],
|
|
2651
2837
|
["EV_HIDE", ac_hide, null],
|
|
@@ -2686,8 +2872,8 @@ function create_gclass(gclass_name)
|
|
|
2686
2872
|
["EV_COLLAPSE_ALL", 0],
|
|
2687
2873
|
["EV_FIND_RESULT", 0],
|
|
2688
2874
|
["EV_LAYOUT_AUTOSET", 0],
|
|
2689
|
-
["EV_TOGGLE_LEGEND", 0],
|
|
2690
2875
|
["EV_LEGEND_TOPIC", 0],
|
|
2876
|
+
["EV_LEGEND_STATE", 0],
|
|
2691
2877
|
["EV_TOPIC_SELECTED",
|
|
2692
2878
|
event_flag_t.EVF_OUTPUT_EVENT | event_flag_t.EVF_NO_WARN_SUBS],
|
|
2693
2879
|
["EV_OPERATION_MODE_CHANGED",
|