@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.
@@ -26914,17 +26914,26 @@ function parents_of(descs, desc, record) {
26914
26914
  * topics: [topic_name, ...] topics with records, in desc order
26915
26915
  * }
26916
26916
  ************************************************************/
26917
- function fold_build_model(descs, records) {
26917
+ function fold_build_model(descs, records, opts) {
26918
26918
  let nodes = /* @__PURE__ */ new Map();
26919
26919
  let groups = /* @__PURE__ */ new Map();
26920
26920
  let topics = [];
26921
26921
  descs = (0, _yuneta_gobj_js.is_object)(descs) ? descs : {};
26922
26922
  records = (0, _yuneta_gobj_js.is_object)(records) ? records : {};
26923
+ opts = (0, _yuneta_gobj_js.is_object)(opts) ? opts : {};
26924
+ let hidden = new Set((0, _yuneta_gobj_js.is_array)(opts.hidden_topics) ? opts.hidden_topics : []);
26925
+ let loose_shown = new Set((0, _yuneta_gobj_js.is_array)(opts.loose_topics) ? opts.loose_topics : []);
26926
+ let main_topic = opts.main_topic || fold_main_topic(descs);
26927
+ if (main_topic && (!descs[main_topic] || hidden.has(main_topic))) main_topic = "";
26928
+ let linked = fold_linked_topics(descs, main_topic);
26929
+ let totals = {};
26923
26930
  for (let topic_name of Object.keys(descs)) {
26924
26931
  if (topic_name.substring(0, 2) === "__") continue;
26925
26932
  descs[topic_name];
26926
26933
  let list = records[topic_name];
26927
26934
  if (!(0, _yuneta_gobj_js.is_array)(list)) continue;
26935
+ totals[topic_name] = list.length;
26936
+ if (hidden.has(topic_name)) continue;
26928
26937
  topics.push(topic_name);
26929
26938
  for (let i = 0; i < list.length; i++) {
26930
26939
  let record = list[i];
@@ -26953,21 +26962,100 @@ function fold_build_model(descs, records) {
26953
26962
  }
26954
26963
  for (let node of nodes.values()) for (let hook of Object.keys(node.hooks)) groups.set(fold_group_key(node.key, hook), node.hooks[hook]);
26955
26964
  let roots = {};
26956
- for (let topic_name of topics) roots[topic_name] = [];
26957
- for (let node of nodes.values()) if (node.parents.length === 0) roots[node.topic_name].push(node.key);
26965
+ let loose = {};
26966
+ for (let topic_name of topics) {
26967
+ roots[topic_name] = [];
26968
+ loose[topic_name] = [];
26969
+ }
26970
+ for (let node of nodes.values()) {
26971
+ if (node.parents.length > 0) continue;
26972
+ let t = node.topic_name;
26973
+ if (main_topic && t !== main_topic && linked.has(t)) {
26974
+ loose[t].push(node.key);
26975
+ if (loose_shown.has(t)) roots[t].push(node.key);
26976
+ } else roots[t].push(node.key);
26977
+ }
26958
26978
  let reached = reach_from(nodes, [].concat(...Object.values(roots)));
26959
- for (let node of nodes.values()) if (!reached.has(node.key)) {
26960
- roots[node.topic_name].push(node.key);
26979
+ for (let node of nodes.values()) {
26980
+ if (reached.has(node.key)) continue;
26981
+ let t = node.topic_name;
26982
+ if (main_topic && t !== main_topic && linked.has(t)) {
26983
+ if (node.parents.length > 0) loose[t].push(node.key);
26984
+ if (!loose_shown.has(t)) continue;
26985
+ }
26986
+ roots[t].push(node.key);
26961
26987
  for (let k of reach_from(nodes, [node.key])) reached.add(k);
26962
26988
  }
26963
26989
  for (let topic_name of topics) if (roots[topic_name].length > 0) groups.set(fold_root_group_key(topic_name), roots[topic_name]);
26964
26990
  return {
26965
26991
  nodes,
26966
26992
  groups,
26967
- topics
26993
+ topics,
26994
+ totals,
26995
+ hidden,
26996
+ main_topic,
26997
+ linked,
26998
+ loose,
26999
+ loose_shown
26968
27000
  };
26969
27001
  }
26970
27002
  /************************************************************
27003
+ * The topic the schema hangs the others from: the one whose hooks
27004
+ * reach the most OTHER topics, a self-referent hook breaking a tie
27005
+ * (a tree of places over a flat list of groups), schema order
27006
+ * breaking the rest. "" when no topic reaches another -- a treedb of
27007
+ * unrelated lists has no trunk, and every topic is a tree of its own.
27008
+ ************************************************************/
27009
+ function fold_main_topic(descs) {
27010
+ descs = (0, _yuneta_gobj_js.is_object)(descs) ? descs : {};
27011
+ let best = "";
27012
+ let best_score = 0;
27013
+ let best_self = false;
27014
+ for (let topic_name of Object.keys(descs)) {
27015
+ if (topic_name.substring(0, 2) === "__") continue;
27016
+ let reach = hook_targets(descs, topic_name);
27017
+ let self = reach.has(topic_name);
27018
+ reach.delete(topic_name);
27019
+ let score = reach.size;
27020
+ if (score === 0) continue;
27021
+ if (score > best_score || score === best_score && self && !best_self) {
27022
+ best = topic_name;
27023
+ best_score = score;
27024
+ best_self = self;
27025
+ }
27026
+ }
27027
+ return best;
27028
+ }
27029
+ function hook_targets(descs, topic_name) {
27030
+ let out = /* @__PURE__ */ new Set();
27031
+ let desc = descs[topic_name];
27032
+ if (!desc || desc.node_treedb_type === "extended") return out;
27033
+ let cols = (0, _yuneta_gobj_js.is_array)(desc.cols) ? desc.cols : [];
27034
+ for (let col of cols) {
27035
+ if (!(0, _yuneta_gobj_js.is_object)(col.hook) || !(0, _yuneta_gobj_js.is_array)(col.flag) || col.flag.indexOf("hook") < 0) continue;
27036
+ for (let child of Object.keys(col.hook)) if (child.substring(0, 2) !== "__" && descs[child]) out.add(child);
27037
+ }
27038
+ return out;
27039
+ }
27040
+ /************************************************************
27041
+ * The topics the schema hangs from `main_topic`, main included:
27042
+ * reached by following hooks from it. A record of one of these
27043
+ * with no parent is LOOSE; a topic outside the set is its own tree.
27044
+ * Empty when there is no main topic.
27045
+ ************************************************************/
27046
+ function fold_linked_topics(descs, main_topic) {
27047
+ let linked = /* @__PURE__ */ new Set();
27048
+ if (!main_topic || !(0, _yuneta_gobj_js.is_object)(descs) || !descs[main_topic]) return linked;
27049
+ let queue = [main_topic];
27050
+ while (queue.length) {
27051
+ let t = queue.shift();
27052
+ if (linked.has(t)) continue;
27053
+ linked.add(t);
27054
+ for (let child of hook_targets(descs, t)) if (!linked.has(child)) queue.push(child);
27055
+ }
27056
+ return linked;
27057
+ }
27058
+ /************************************************************
26971
27059
  * Everything reachable downwards from `start`, cycles included.
26972
27060
  ************************************************************/
26973
27061
  function reach_from(nodes, start) {
@@ -26998,7 +27086,10 @@ function fold_group_total(model, group_key) {
26998
27086
  }
26999
27087
  function fold_group_shown(model, state, group_key) {
27000
27088
  let total = fold_group_total(model, group_key);
27001
- let n = state.shown.get(group_key) || 0;
27089
+ let n;
27090
+ if (state.shown.has(group_key)) n = state.shown.get(group_key);
27091
+ else if (group_key.charAt(0) === "|") n = state.page_size;
27092
+ else n = 0;
27002
27093
  return Math.min(n, total);
27003
27094
  }
27004
27095
  function fold_is_open(state, group_key) {
@@ -54104,6 +54195,9 @@ var attrs_table$4 = [
54104
54195
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "fkey_port_position", 0, "top", "Fkey port position"),
54105
54196
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_INTEGER, "expand_depth", 0, 2, "Levels open when a treedb loads: 1 = the roots alone, 2 = the roots and their children. Every open hook shows its first page"),
54106
54197
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.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"),
54198
+ (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_LIST, "hidden_topics", 0, "[]", "Topics left out of the tree: no card, no pill counts them, no edge reaches them"),
54199
+ (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.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"),
54200
+ (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_LIST, "loose_topics", 0, "[]", "Topics whose loose records (no parent, in a topic the schema hangs from the main one) are shown as roots"),
54107
54201
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "confirm_delete_node", 0, true, "Ask confirmation before deleting a node"),
54108
54202
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "confirm_unlink_edge", 0, true, "Ask confirmation before unlinking an edge"),
54109
54203
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "wide", 0, "40px", "Height of header"),
@@ -54173,12 +54267,16 @@ var PRIVATE_DATA$4 = {
54173
54267
  _selected_paint_ids: [],
54174
54268
  _pending_focus_topic: null,
54175
54269
  _pending_focus_all: false,
54270
+ _find_hidden_matches: 0,
54176
54271
  _pending_find: null,
54177
54272
  _layout_asked: "",
54178
54273
  _nodes_total: 0,
54179
54274
  _nodes_placed: 0,
54180
54275
  expand_depth: 2,
54181
54276
  fold_page_size: 24,
54277
+ hidden_topics: null,
54278
+ main_topic: "",
54279
+ loose_topics: null,
54182
54280
  _fold_model: null,
54183
54281
  _fold_state: null,
54184
54282
  _fold_listener: null,
@@ -55754,23 +55852,26 @@ function graph_find_nodes(gobj, term) {
55754
55852
  let model = priv._fold_model;
55755
55853
  let state = priv._fold_state;
55756
55854
  let keys = [];
55855
+ let hidden_matches = 0;
55757
55856
  if (!(0, _yuneta_gobj_js.empty_string)(term)) {
55758
55857
  let needle = String(term).toLowerCase();
55759
- for (let node of model.nodes.values()) {
55760
- let record = node.record || {};
55858
+ let matches = (topic_name, record) => {
55761
55859
  let label = "";
55762
55860
  try {
55763
- label = node_label(priv.descs[node.topic_name] || {}, record) || "";
55861
+ label = node_label(priv.descs[topic_name] || {}, record) || "";
55764
55862
  } catch (e) {
55765
55863
  label = "";
55766
55864
  }
55767
- if ([
55865
+ return [
55768
55866
  label,
55769
55867
  record.id,
55770
- node.topic_name
55771
- ].filter((v) => typeof v === "string").join(" ").toLowerCase().includes(needle)) keys.push(node.key);
55772
- }
55868
+ topic_name
55869
+ ].filter((v) => typeof v === "string").join(" ").toLowerCase().includes(needle);
55870
+ };
55871
+ for (let node of model.nodes.values()) if (matches(node.topic_name, node.record || {})) keys.push(node.key);
55872
+ for (let topic_name of model.hidden) for (let record of priv.records[topic_name] || []) if ((0, _yuneta_gobj_js.is_object)(record) && matches(topic_name, record)) hidden_matches++;
55773
55873
  }
55874
+ priv._find_hidden_matches = hidden_matches;
55774
55875
  let visible = fold_visible_set(model, state);
55775
55876
  let revealed = 0;
55776
55877
  for (let key of keys) {
@@ -58701,7 +58802,11 @@ function uninstall_fold_listener(gobj) {
58701
58802
  ************************************************************/
58702
58803
  function rebuild_fold_model(gobj) {
58703
58804
  let priv = gobj.priv;
58704
- priv._fold_model = fold_build_model(priv.descs || {}, priv.records || {});
58805
+ priv._fold_model = fold_build_model(priv.descs || {}, priv.records || {}, {
58806
+ hidden_topics: (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "hidden_topics") || [],
58807
+ main_topic: (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "main_topic") || "",
58808
+ loose_topics: (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "loose_topics") || []
58809
+ });
58705
58810
  if (!priv._fold_state) priv._fold_state = fold_new_state((0, _yuneta_gobj_js.gobj_read_integer_attr)(gobj, "fold_page_size"));
58706
58811
  if (!priv._pill_sig) priv._pill_sig = /* @__PURE__ */ new Map();
58707
58812
  }
@@ -58763,8 +58868,47 @@ async function reconcile_fold_now(gobj, opts) {
58763
58868
  } finally {
58764
58869
  history_resume(gobj);
58765
58870
  }
58871
+ publish_legend_state(gobj, visible);
58766
58872
  return changed;
58767
58873
  }
58874
+ /************************************************************
58875
+ * What the legend has to say, published after every reconcile:
58876
+ * one entry per topic of the treedb, hidden ones included, with
58877
+ * its colour, how many records it has, how many are on screen,
58878
+ * how many are LOOSE, and the flags -- plus which topic is the
58879
+ * main one and whether it was deduced or chosen. The host draws
58880
+ * the strip from this and nothing else; the engine is the truth.
58881
+ ************************************************************/
58882
+ function publish_legend_state(gobj, visible) {
58883
+ let priv = gobj.priv;
58884
+ let model = priv._fold_model;
58885
+ if (!model || !priv.descs) return;
58886
+ let on_screen = {};
58887
+ for (let key of visible) {
58888
+ let node = model.nodes.get(key);
58889
+ if (node) on_screen[node.topic_name] = (on_screen[node.topic_name] || 0) + 1;
58890
+ }
58891
+ let topics = [];
58892
+ for (const [topic_name, desc] of Object.entries(priv.descs)) {
58893
+ if (topic_name.substring(0, 2) === "__" || !desc) continue;
58894
+ let loose = model.loose[topic_name] || [];
58895
+ topics.push({
58896
+ topic: topic_name,
58897
+ color: desc.color || "",
58898
+ total: model.totals[topic_name] || 0,
58899
+ visible: on_screen[topic_name] || 0,
58900
+ loose: loose.length,
58901
+ loose_shown: model.loose_shown.has(topic_name),
58902
+ hidden: model.hidden.has(topic_name),
58903
+ linked: model.linked.has(topic_name)
58904
+ });
58905
+ }
58906
+ (0, _yuneta_gobj_js.gobj_publish_event)(gobj, "EV_LEGEND_STATE", {
58907
+ main_topic: model.main_topic,
58908
+ main_chosen: !!(0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "main_topic"),
58909
+ topics
58910
+ });
58911
+ }
58768
58912
  async function reconcile_fold_apply(gobj, opts, visible) {
58769
58913
  let priv = gobj.priv;
58770
58914
  let graph = priv.graph;
@@ -59440,6 +59584,43 @@ function ac_collapse_all$1(gobj, event, kw, src) {
59440
59584
  return 0;
59441
59585
  }
59442
59586
  /************************************************************
59587
+ * The legend's three settings, from the host, which persists
59588
+ * them. Each rebuilds the tree; the fold state is kept -- it is
59589
+ * keyed by group, and a group that is gone is never read -- except
59590
+ * for a new MAIN topic, whose roots are other roots: the tree
59591
+ * opens again at `expand_depth`.
59592
+ ************************************************************/
59593
+ function ac_set_hidden_topics(gobj, event, kw, src) {
59594
+ let priv = gobj.priv;
59595
+ let topics = kw && (0, _yuneta_gobj_js.is_array)(kw.topics) ? kw.topics : [];
59596
+ (0, _yuneta_gobj_js.gobj_write_attr)(gobj, "hidden_topics", topics);
59597
+ if (!priv._fold_model) return 0;
59598
+ rebuild_fold_model(gobj);
59599
+ reconcile_fold(gobj, {});
59600
+ return 0;
59601
+ }
59602
+ function ac_set_main_topic(gobj, event, kw, src) {
59603
+ let priv = gobj.priv;
59604
+ (0, _yuneta_gobj_js.gobj_write_str_attr)(gobj, "main_topic", kw && kw.topic || "");
59605
+ if (!priv._fold_model) return 0;
59606
+ rebuild_fold_model(gobj);
59607
+ fold_expand_to_depth(priv._fold_model, priv._fold_state, (0, _yuneta_gobj_js.gobj_read_integer_attr)(gobj, "expand_depth"));
59608
+ reconcile_fold(gobj, {
59609
+ relayout: true,
59610
+ fit: true
59611
+ });
59612
+ return 0;
59613
+ }
59614
+ function ac_set_loose_topics(gobj, event, kw, src) {
59615
+ let priv = gobj.priv;
59616
+ let topics = kw && (0, _yuneta_gobj_js.is_array)(kw.topics) ? kw.topics : [];
59617
+ (0, _yuneta_gobj_js.gobj_write_attr)(gobj, "loose_topics", topics);
59618
+ if (!priv._fold_model) return 0;
59619
+ rebuild_fold_model(gobj);
59620
+ reconcile_fold(gobj, {});
59621
+ return 0;
59622
+ }
59623
+ /************************************************************
59443
59624
  * Tell whoever asked how many nodes the term matched. The count is
59444
59625
  * the answer to the question the box asks; without it "nothing moved"
59445
59626
  * and "nothing matched" look the same.
@@ -59447,7 +59628,8 @@ function ac_collapse_all$1(gobj, event, kw, src) {
59447
59628
  function publish_find_result(gobj, term, matches) {
59448
59629
  (0, _yuneta_gobj_js.gobj_publish_event)(gobj, "EV_FIND_RESULT", {
59449
59630
  term,
59450
- matches
59631
+ matches,
59632
+ hidden_matches: gobj.priv._find_hidden_matches || 0
59451
59633
  });
59452
59634
  }
59453
59635
  /************************************************************
@@ -59913,6 +60095,21 @@ function create_gclass$4(gclass_name) {
59913
60095
  ac_collapse_all$1,
59914
60096
  null
59915
60097
  ],
60098
+ [
60099
+ "EV_SET_HIDDEN_TOPICS",
60100
+ ac_set_hidden_topics,
60101
+ null
60102
+ ],
60103
+ [
60104
+ "EV_SET_MAIN_TOPIC",
60105
+ ac_set_main_topic,
60106
+ null
60107
+ ],
60108
+ [
60109
+ "EV_SET_LOOSE_TOPICS",
60110
+ ac_set_loose_topics,
60111
+ null
60112
+ ],
59916
60113
  [
59917
60114
  "EV_CENTER",
59918
60115
  ac_center,
@@ -60014,6 +60211,10 @@ function create_gclass$4(gclass_name) {
60014
60211
  ["EV_SHOW_MORE", 0],
60015
60212
  ["EV_EXPAND_ALL", 0],
60016
60213
  ["EV_COLLAPSE_ALL", 0],
60214
+ ["EV_SET_HIDDEN_TOPICS", 0],
60215
+ ["EV_SET_MAIN_TOPIC", 0],
60216
+ ["EV_SET_LOOSE_TOPICS", 0],
60217
+ ["EV_LEGEND_STATE", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT],
60017
60218
  ["EV_CENTER", 0],
60018
60219
  ["EV_FULLSCREEN", 0],
60019
60220
  ["EV_SET_LAYOUT", 0],
@@ -60075,6 +60276,9 @@ var attrs_table$3 = [
60075
60276
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.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"),
60076
60277
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "operation_mode", _yuneta_gobj_js.sdata_flag_t.SDF_PERSIST, "reading", "Current operation mode (internal behaviour or role). Changed by the user trough the gui."),
60077
60278
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "layout", _yuneta_gobj_js.sdata_flag_t.SDF_PERSIST, "", "Current graph layout. User preference. Changed by the user through the gui."),
60279
+ (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_LIST, "hidden_topics", _yuneta_gobj_js.sdata_flag_t.SDF_PERSIST, "[]", "Topics hidden from the graph (legend). User preference, per treedb"),
60280
+ (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "main_topic", _yuneta_gobj_js.sdata_flag_t.SDF_PERSIST, "", "The topic the tree hangs from; empty = deduced by the graph. User preference, per treedb"),
60281
+ (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_LIST, "loose_topics", _yuneta_gobj_js.sdata_flag_t.SDF_PERSIST, "[]", "Topics whose LOOSE records (no parent) are shown. User preference, per treedb"),
60078
60282
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_POINTER, "gobj_remote_yuno", 0, null, "Remote Yuno to request data"),
60079
60283
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "treedb_name", 0, null, "Remote service treedb name"),
60080
60284
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_DICT, "descs", 0, null, "Descriptions of topics obtained"),
@@ -60116,6 +60320,7 @@ var PRIVATE_DATA$3 = {
60116
60320
  operation_mode: null,
60117
60321
  layout: null,
60118
60322
  _topics_subscribed: {},
60323
+ _legend_state: null,
60119
60324
  _links_subscribed: false,
60120
60325
  is_pinhold_window: false
60121
60326
  };
@@ -60148,7 +60353,10 @@ function mt_create$3(gobj) {
60148
60353
  hook_port_position: "bottom",
60149
60354
  fkey_port_position: "top",
60150
60355
  expand_depth: (0, _yuneta_gobj_js.gobj_read_integer_attr)(gobj, "expand_depth"),
60151
- fold_page_size: (0, _yuneta_gobj_js.gobj_read_integer_attr)(gobj, "fold_page_size")
60356
+ fold_page_size: (0, _yuneta_gobj_js.gobj_read_integer_attr)(gobj, "fold_page_size"),
60357
+ hidden_topics: (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "hidden_topics") || [],
60358
+ main_topic: (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "main_topic") || "",
60359
+ loose_topics: (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "loose_topics") || []
60152
60360
  }, gobj);
60153
60361
  populate_nodes_tree_options(gobj);
60154
60362
  if (priv.with_treedb_tables) {}
@@ -60215,8 +60423,8 @@ function build_ui$3(gobj) {
60215
60423
  [
60216
60424
  "div",
60217
60425
  {
60218
- class: "GRAPH_LEGEND is-hidden is-flex-grow-0",
60219
- style: "display:flex; flex-wrap:wrap; align-items:center; gap:.4rem; padding:.35rem .5rem;"
60426
+ class: "GRAPH_LEGEND is-flex-grow-0",
60427
+ style: "display:flex; flex-wrap:nowrap; align-items:center; gap:.35rem; padding:.3rem .5rem; overflow-x:auto; min-height:2.2rem;"
60220
60428
  },
60221
60429
  []
60222
60430
  ],
@@ -60407,29 +60615,6 @@ function make_toolbar(gobj) {
60407
60615
  } }
60408
60616
  ],
60409
60617
  ...yui_graph_fold_items(gobj, (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "wide")),
60410
- [
60411
- "button",
60412
- {
60413
- class: "GRAPH_LEGEND_BTN button ml-2",
60414
- title: (0, i18next.t)("legend"),
60415
- "aria-label": (0, i18next.t)("legend"),
60416
- "data-i18n-title": "legend",
60417
- "data-i18n-aria-label": "legend"
60418
- },
60419
- [["i", { class: "yi-square" }], [
60420
- "span",
60421
- {
60422
- class: "is-hidden-mobile",
60423
- style: "padding-left:5px;",
60424
- i18n: "legend"
60425
- },
60426
- "legend"
60427
- ]],
60428
- { click: (evt) => {
60429
- evt.stopPropagation();
60430
- (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_TOGGLE_LEGEND", {}, gobj);
60431
- } }
60432
- ],
60433
60618
  [
60434
60619
  "button",
60435
60620
  {
@@ -61453,61 +61638,207 @@ function ac_set_operation_mode(gobj, event, kw, src) {
61453
61638
  * card's graph icon travels — as EV_TOPIC_SELECTED, which the host turns
61454
61639
  * into the URL, so what you are looking at stays linkable.
61455
61640
  ************************************************************/
61641
+ var LEGEND_GLYPH_STYLE = "font-size:1.5em; line-height:1; color:inherit;";
61456
61642
  function refresh_legend(gobj) {
61457
61643
  let priv = gobj.priv;
61458
61644
  let $container = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
61459
61645
  if (!$container) return;
61460
61646
  let $legend = $container.querySelector(".GRAPH_LEGEND");
61461
61647
  if (!$legend) return;
61462
- let descs = priv.gobj_nodes_tree ? (0, _yuneta_gobj_js.gobj_read_attr)(priv.gobj_nodes_tree, "descs") : null;
61648
+ let state = priv._legend_state;
61463
61649
  $legend.textContent = "";
61464
- if (!descs) return;
61465
- for (const [topic_name, desc] of Object.entries(descs)) {
61466
- if (topic_name.substring(0, 2) === "__") continue;
61467
- if (!desc || !desc.color) continue;
61650
+ if (!state || !(0, _yuneta_gobj_js.is_array)(state.topics)) return;
61651
+ let topics = state.topics.slice().sort((a, b) => {
61652
+ return (a.topic === state.main_topic ? 0 : 1) - (b.topic === state.main_topic ? 0 : 1);
61653
+ });
61654
+ for (let entry of topics) {
61655
+ let topic_name = entry.topic;
61656
+ let is_main = topic_name === state.main_topic;
61468
61657
  let focused = priv.focus_topic === topic_name;
61469
- let $item = (0, _yuneta_gobj_js.createElement2)([
61658
+ let hidden = !!entry.hidden;
61659
+ let controls = [];
61660
+ controls.push(["span", {
61661
+ class: "GRAPH_LEGEND_SWATCH",
61662
+ style: `display:inline-block; width:.8rem; height:.8rem; border-radius:3px; margin-right:.35rem; flex:0 0 auto; background:${entry.color || "#94a3b8"}; border:1px solid rgba(0,0,0,.25);` + (hidden ? "opacity:.35;" : "")
61663
+ }], [
61664
+ "span",
61665
+ {
61666
+ class: "GRAPH_LEGEND_NAME",
61667
+ style: hidden ? "text-decoration:line-through; opacity:.6;" : ""
61668
+ },
61669
+ topic_name
61670
+ ], [
61671
+ "span",
61672
+ {
61673
+ class: "GRAPH_LEGEND_COUNT has-text-grey",
61674
+ style: "margin-left:.35rem; font-size:.8em;"
61675
+ },
61676
+ hidden ? `${entry.total}` : `${entry.visible}/${entry.total}`
61677
+ ]);
61678
+ let body_attrs = {
61679
+ class: "GRAPH_LEGEND_ITEM button is-small" + (focused ? " is-primary is-light" : "") + (is_main ? " GRAPH_LEGEND_MAIN" : ""),
61680
+ type: "button",
61681
+ style: "gap:0; flex:0 0 auto;",
61682
+ title: is_main ? (0, i18next.t)("main topic") : hidden ? (0, i18next.t)("show topic") : (0, i18next.t)("hide topic"),
61683
+ "aria-label": topic_name,
61684
+ "aria-pressed": hidden ? "false" : "true"
61685
+ };
61686
+ if (is_main) body_attrs.disabled = "disabled";
61687
+ let $body = (0, _yuneta_gobj_js.createElement2)([
61688
+ "button",
61689
+ body_attrs,
61690
+ controls,
61691
+ { click: (evt) => {
61692
+ evt.stopPropagation();
61693
+ (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_LEGEND_TOPIC", {
61694
+ topic: topic_name,
61695
+ action: "toggle"
61696
+ }, gobj);
61697
+ } }
61698
+ ]);
61699
+ let extras = [];
61700
+ if (is_main && state.main_chosen) extras.push([
61470
61701
  "button",
61471
61702
  {
61472
- class: "GRAPH_LEGEND_ITEM button is-small" + (focused ? " is-primary is-light" : ""),
61473
- title: topic_name,
61474
- "aria-label": topic_name,
61475
- "aria-pressed": focused ? "true" : "false"
61703
+ class: "GRAPH_LEGEND_STAR button is-small is-warning is-light",
61704
+ type: "button",
61705
+ style: "padding:0 .4rem;",
61706
+ title: (0, i18next.t)("main topic"),
61707
+ "data-i18n-title": "main topic",
61708
+ "aria-label": (0, i18next.t)("main topic"),
61709
+ "aria-pressed": "true"
61476
61710
  },
61477
- [["span", {
61478
- class: "GRAPH_LEGEND_SWATCH",
61479
- style: `display:inline-block; width:.8rem; height:.8rem; border-radius:3px; margin-right:.4rem; background:${desc.color}; border:1px solid rgba(0,0,0,.25);`
61480
- }], [
61711
+ [[
61481
61712
  "span",
61482
- {},
61483
- topic_name
61713
+ { style: LEGEND_GLYPH_STYLE },
61714
+ "★"
61484
61715
  ]],
61485
61716
  { click: (evt) => {
61486
61717
  evt.stopPropagation();
61487
- (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_LEGEND_TOPIC", { topic: topic_name }, gobj);
61718
+ (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_LEGEND_TOPIC", {
61719
+ topic: "",
61720
+ action: "main"
61721
+ }, gobj);
61722
+ } }
61723
+ ]);
61724
+ else if (is_main) extras.push([
61725
+ "span",
61726
+ {
61727
+ class: "GRAPH_LEGEND_STAR button is-small is-static",
61728
+ style: "padding:0 .4rem;",
61729
+ title: (0, i18next.t)("main topic"),
61730
+ "data-i18n-title": "main topic"
61731
+ },
61732
+ [[
61733
+ "span",
61734
+ { style: LEGEND_GLYPH_STYLE },
61735
+ "★"
61736
+ ]]
61737
+ ]);
61738
+ else if (!hidden) extras.push([
61739
+ "button",
61740
+ {
61741
+ class: "GRAPH_LEGEND_STAR button is-small",
61742
+ type: "button",
61743
+ style: "padding:0 .4rem;",
61744
+ title: (0, i18next.t)("main topic"),
61745
+ "data-i18n-title": "main topic",
61746
+ "aria-label": (0, i18next.t)("main topic")
61747
+ },
61748
+ [[
61749
+ "span",
61750
+ { style: LEGEND_GLYPH_STYLE },
61751
+ "☆"
61752
+ ]],
61753
+ { click: (evt) => {
61754
+ evt.stopPropagation();
61755
+ (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_LEGEND_TOPIC", {
61756
+ topic: topic_name,
61757
+ action: "main"
61758
+ }, gobj);
61759
+ } }
61760
+ ]);
61761
+ if (!hidden && entry.linked && entry.loose > 0) extras.push([
61762
+ "button",
61763
+ {
61764
+ class: "GRAPH_LEGEND_LOOSE button is-small" + (entry.loose_shown ? " is-warning is-light" : ""),
61765
+ type: "button",
61766
+ style: "padding:0 .45rem;",
61767
+ title: (0, i18next.t)("loose records"),
61768
+ "data-i18n-title": "loose records",
61769
+ "aria-label": (0, i18next.t)("loose records"),
61770
+ "aria-pressed": entry.loose_shown ? "true" : "false"
61771
+ },
61772
+ `+${entry.loose}`,
61773
+ { click: (evt) => {
61774
+ evt.stopPropagation();
61775
+ (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_LEGEND_TOPIC", {
61776
+ topic: topic_name,
61777
+ action: "loose"
61778
+ }, gobj);
61779
+ } }
61780
+ ]);
61781
+ if (!hidden) extras.push([
61782
+ "button",
61783
+ {
61784
+ class: "GRAPH_LEGEND_FOCUS button is-small" + (focused ? " is-primary is-light" : ""),
61785
+ type: "button",
61786
+ style: "padding:0 .4rem;",
61787
+ title: (0, i18next.t)("highlight topic"),
61788
+ "data-i18n-title": "highlight topic",
61789
+ "aria-label": (0, i18next.t)("highlight topic"),
61790
+ "aria-pressed": focused ? "true" : "false"
61791
+ },
61792
+ [["i", {
61793
+ class: "yi-location-crosshairs",
61794
+ style: LEGEND_GLYPH_STYLE
61795
+ }]],
61796
+ { click: (evt) => {
61797
+ evt.stopPropagation();
61798
+ (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_LEGEND_TOPIC", {
61799
+ topic: topic_name,
61800
+ action: "focus"
61801
+ }, gobj);
61488
61802
  } }
61489
61803
  ]);
61490
- $legend.appendChild($item);
61804
+ let $chip = (0, _yuneta_gobj_js.createElement2)([
61805
+ "div",
61806
+ {
61807
+ class: "GRAPH_LEGEND_CHIP buttons has-addons",
61808
+ style: "margin:0; flex:0 0 auto; flex-wrap:nowrap;"
61809
+ },
61810
+ [$body].concat(extras.map((spec) => (0, _yuneta_gobj_js.createElement2)(spec)))
61811
+ ]);
61812
+ $legend.appendChild($chip);
61491
61813
  }
61492
61814
  }
61493
61815
  /************************************************************
61494
- *
61816
+ * The engine said what is drawn: repaint the strip.
61495
61817
  ************************************************************/
61496
- function ac_toggle_legend(gobj, event, kw, src) {
61497
- let $container = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
61498
- if (!$container) return 0;
61499
- let $legend = $container.querySelector(".GRAPH_LEGEND");
61500
- if (!$legend) {
61501
- (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: no legend strip to toggle`);
61502
- return -1;
61503
- }
61504
- if ($legend.classList.contains("is-hidden")) {
61505
- refresh_legend(gobj);
61506
- $legend.classList.remove("is-hidden");
61507
- } else $legend.classList.add("is-hidden");
61818
+ function ac_legend_state(gobj, event, kw, src) {
61819
+ let priv = gobj.priv;
61820
+ priv._legend_state = kw || null;
61821
+ refresh_legend(gobj);
61508
61822
  return 0;
61509
61823
  }
61510
61824
  /************************************************************
61825
+ *
61826
+ ************************************************************/
61827
+ /************************************************************
61828
+ * One of the legend's settings changed: write it, SAVE it (they
61829
+ * are the reader's, per treedb) and tell the engine.
61830
+ ************************************************************/
61831
+ function set_legend_list_attr(gobj, name, event_name, topics) {
61832
+ let priv = gobj.priv;
61833
+ (0, _yuneta_gobj_js.gobj_write_attr)(gobj, name, topics);
61834
+ (0, _yuneta_gobj_js.gobj_save_persistent_attrs)(gobj, name);
61835
+ if (priv.gobj_nodes_tree) (0, _yuneta_gobj_js.gobj_send_event)(priv.gobj_nodes_tree, event_name, { topics }, gobj);
61836
+ }
61837
+ function legend_list_attr(gobj, name) {
61838
+ let list = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, name);
61839
+ return (0, _yuneta_gobj_js.is_array)(list) ? list.slice() : [];
61840
+ }
61841
+ /************************************************************
61511
61842
  * A legend entry was clicked: focus that topic, or clear the focus if
61512
61843
  * it was already the focused one. Published UP rather than applied
61513
61844
  * here, so the URL follows — the host owns the route.
@@ -61515,10 +61846,41 @@ function ac_toggle_legend(gobj, event, kw, src) {
61515
61846
  function ac_legend_topic(gobj, event, kw, src) {
61516
61847
  let priv = gobj.priv;
61517
61848
  let topic = kw && kw.topic || "";
61518
- if (!topic) {
61849
+ let action = kw && kw.action || "focus";
61850
+ if (!topic && action !== "main") {
61519
61851
  (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: legend click with no topic`);
61520
61852
  return -1;
61521
61853
  }
61854
+ let state = priv._legend_state || {};
61855
+ switch (action) {
61856
+ case "toggle": {
61857
+ if (topic === state.main_topic) {
61858
+ (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: the main topic '${topic}' cannot be hidden`);
61859
+ return -1;
61860
+ }
61861
+ let hidden = legend_list_attr(gobj, "hidden_topics");
61862
+ if ((0, _yuneta_gobj_js.str_in_list)(hidden, topic)) (0, _yuneta_gobj_js.delete_from_list)(hidden, topic);
61863
+ else hidden.push(topic);
61864
+ set_legend_list_attr(gobj, "hidden_topics", "EV_SET_HIDDEN_TOPICS", hidden);
61865
+ return 0;
61866
+ }
61867
+ case "loose": {
61868
+ let loose = legend_list_attr(gobj, "loose_topics");
61869
+ if ((0, _yuneta_gobj_js.str_in_list)(loose, topic)) (0, _yuneta_gobj_js.delete_from_list)(loose, topic);
61870
+ else loose.push(topic);
61871
+ set_legend_list_attr(gobj, "loose_topics", "EV_SET_LOOSE_TOPICS", loose);
61872
+ return 0;
61873
+ }
61874
+ case "main":
61875
+ (0, _yuneta_gobj_js.gobj_write_str_attr)(gobj, "main_topic", topic);
61876
+ (0, _yuneta_gobj_js.gobj_save_persistent_attrs)(gobj, "main_topic");
61877
+ if (priv.gobj_nodes_tree) (0, _yuneta_gobj_js.gobj_send_event)(priv.gobj_nodes_tree, "EV_SET_MAIN_TOPIC", { topic }, gobj);
61878
+ return 0;
61879
+ case "focus": break;
61880
+ default:
61881
+ (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: legend action unknown: ${action}`);
61882
+ return -1;
61883
+ }
61522
61884
  let next = priv.focus_topic === topic ? "" : topic;
61523
61885
  (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_SET_FOCUS_TOPIC", {
61524
61886
  topic: next,
@@ -61579,20 +61941,28 @@ function ac_find_result(gobj, event, kw, src) {
61579
61941
  $count.textContent = "";
61580
61942
  return 0;
61581
61943
  }
61582
- $count.textContent = (0, i18next.t)("matches", { count: kw && kw.matches || 0 });
61944
+ let text = (0, i18next.t)("matches", { count: kw && kw.matches || 0 });
61945
+ let hidden = kw && kw.hidden_matches || 0;
61946
+ if (hidden > 0) text += ` (+${hidden} ${(0, i18next.t)("hidden topics")})`;
61947
+ $count.textContent = text;
61583
61948
  $result.classList.remove("is-hidden");
61584
61949
  return 0;
61585
61950
  }
61586
61951
  function ac_set_focus_topic(gobj, event, kw, src) {
61587
61952
  let priv = gobj.priv;
61588
61953
  priv.focus_topic = kw && kw.topic || "";
61954
+ if (priv.focus_topic) {
61955
+ let hidden = legend_list_attr(gobj, "hidden_topics");
61956
+ if ((0, _yuneta_gobj_js.str_in_list)(hidden, priv.focus_topic)) {
61957
+ (0, _yuneta_gobj_js.delete_from_list)(hidden, priv.focus_topic);
61958
+ set_legend_list_attr(gobj, "hidden_topics", "EV_SET_HIDDEN_TOPICS", hidden);
61959
+ }
61960
+ }
61589
61961
  if (priv.gobj_nodes_tree) (0, _yuneta_gobj_js.gobj_send_event)(priv.gobj_nodes_tree, "EV_FOCUS_TOPIC", {
61590
61962
  topic: priv.focus_topic,
61591
61963
  reveal: kw && kw.reveal || "page"
61592
61964
  }, gobj);
61593
- let $container = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
61594
- let $legend = $container ? $container.querySelector(".GRAPH_LEGEND") : null;
61595
- if ($legend && !$legend.classList.contains("is-hidden")) refresh_legend(gobj);
61965
+ refresh_legend(gobj);
61596
61966
  return 0;
61597
61967
  }
61598
61968
  /************************************************************
@@ -61776,13 +62146,13 @@ function create_gclass$3(gclass_name) {
61776
62146
  null
61777
62147
  ],
61778
62148
  [
61779
- "EV_TOGGLE_LEGEND",
61780
- ac_toggle_legend,
62149
+ "EV_LEGEND_TOPIC",
62150
+ ac_legend_topic,
61781
62151
  null
61782
62152
  ],
61783
62153
  [
61784
- "EV_LEGEND_TOPIC",
61785
- ac_legend_topic,
62154
+ "EV_LEGEND_STATE",
62155
+ ac_legend_state,
61786
62156
  null
61787
62157
  ],
61788
62158
  [
@@ -61836,8 +62206,8 @@ function create_gclass$3(gclass_name) {
61836
62206
  ["EV_COLLAPSE_ALL", 0],
61837
62207
  ["EV_FIND_RESULT", 0],
61838
62208
  ["EV_LAYOUT_AUTOSET", 0],
61839
- ["EV_TOGGLE_LEGEND", 0],
61840
62209
  ["EV_LEGEND_TOPIC", 0],
62210
+ ["EV_LEGEND_STATE", 0],
61841
62211
  ["EV_TOPIC_SELECTED", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT | _yuneta_gobj_js.event_flag_t.EVF_NO_WARN_SUBS],
61842
62212
  ["EV_OPERATION_MODE_CHANGED", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT | _yuneta_gobj_js.event_flag_t.EVF_NO_WARN_SUBS],
61843
62213
  ["EV_RECORD_WRITTEN", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT | _yuneta_gobj_js.event_flag_t.EVF_NO_WARN_SUBS],