@yuneta/gobj-ui 7.1.0 → 7.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -561,6 +561,31 @@ Two implementation notes worth keeping:
561
561
  still arrive from a keyboard path or a form that outlived the flag, and an
562
562
  ignored write is exactly the behaviour this whole change exists to stop.
563
563
 
564
+ ### Finding a node in the graph
565
+
566
+ `C_YUI_TREEDB_GRAPH` carries a find box in the middle of its toolbar. It
567
+ matches the term against the node's **label**, its id and its topic name, puts
568
+ every match in the same amber `active` state the topic focus uses, and centres
569
+ the viewport on them.
570
+
571
+ Two details that are not decoration:
572
+
573
+ - it matches the **label**, not only the id. On a topic keyed by `rowid`,
574
+ `uuid` or `qualified` the id is a counter or a path and the name a human
575
+ knows the record by lives in a secondary key — the same reason `node_label()`
576
+ exists.
577
+ - it **says how many** it found. A graph that did not move looks identical
578
+ whether nothing matched or the only match was already on screen, so the count
579
+ is shown next to the box (hidden while the box is empty; a typed term that
580
+ matches nothing shows `0`, which is an answer).
581
+
582
+ The find and the topic focus **share the highlight**: starting one clears the
583
+ other. Two amber sets at once would say nothing about either.
584
+
585
+ Wiring: the box sends `EV_FIND_NODES {text}` to the view, which forwards it to
586
+ `C_G6_NODES_TREE`; the graph answers `EV_FIND_RESULT {term, matches}`, which the
587
+ view declares like every other event its child publishes.
588
+
564
589
  ### C_YUI_TREEDB_SCHEMA — the treedb drawn the way its `.c` draws it
565
590
 
566
591
  A landing view that draws a treedb the way its schema literal draws it in ASCII
@@ -18772,101 +18772,140 @@ function make_toolbar(gobj) {
18772
18772
  gobj.priv;
18773
18773
  let modes = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "operation_modes");
18774
18774
  if ((0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "readonly")) modes = modes.filter((mode) => mode !== "edition");
18775
+ let left_items = [
18776
+ [
18777
+ "span",
18778
+ {
18779
+ class: "GRAPH_LAYOUT_LABEL is-hidden-mobile",
18780
+ style: "padding-right:5px;",
18781
+ i18n: "layout"
18782
+ },
18783
+ "layout"
18784
+ ],
18785
+ [
18786
+ "div",
18787
+ { class: "select" },
18788
+ [["select", { class: "GRAPH_LAYOUT_SELECT" }]],
18789
+ { change: (evt) => {
18790
+ evt.stopPropagation();
18791
+ (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_SET_LAYOUT", { layout: evt.target.value }, gobj);
18792
+ } }
18793
+ ],
18794
+ [
18795
+ "span",
18796
+ {
18797
+ class: "GRAPH_MODE_LABEL is-hidden-mobile",
18798
+ style: "padding-left:10px; padding-right:5px;",
18799
+ i18n: "operation mode"
18800
+ },
18801
+ "operation mode"
18802
+ ],
18803
+ [
18804
+ "div",
18805
+ { class: "select" },
18806
+ [[
18807
+ "select",
18808
+ { class: "GRAPH_MODE_SELECT" },
18809
+ modes.map((item) => [
18810
+ "option",
18811
+ {},
18812
+ item
18813
+ ])
18814
+ ]],
18815
+ { change: (evt) => {
18816
+ evt.stopPropagation();
18817
+ (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_SET_OPERATION_MODE", { operation_mode: evt.target.value }, gobj);
18818
+ } }
18819
+ ],
18820
+ [
18821
+ "button",
18822
+ { class: "GRAPH_REFRESH button" },
18823
+ [["i", { class: "yi-arrows-rotate" }], [
18824
+ "span",
18825
+ {
18826
+ class: "is-hidden-mobile",
18827
+ style: "padding-left:5px;",
18828
+ i18n: "refresh"
18829
+ },
18830
+ "refresh"
18831
+ ]],
18832
+ { click: (evt) => {
18833
+ evt.stopPropagation();
18834
+ (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_REFRESH_TREEDB", { evt }, gobj);
18835
+ } }
18836
+ ],
18837
+ [
18838
+ "button",
18839
+ {
18840
+ class: "button ml-2 TREEDB_JSON_BTN",
18841
+ title: (0, i18next.t)("raw json"),
18842
+ "aria-label": (0, i18next.t)("raw json"),
18843
+ "data-i18n-title": "raw json",
18844
+ "data-i18n-aria-label": "raw json"
18845
+ },
18846
+ [["i", { class: "yi-eye" }], [
18847
+ "span",
18848
+ {
18849
+ class: "is-hidden-mobile",
18850
+ style: "padding-left:5px;",
18851
+ i18n: "raw json"
18852
+ },
18853
+ "raw json"
18854
+ ]],
18855
+ { click: (evt) => {
18856
+ evt.stopPropagation();
18857
+ (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_OPEN_JSON", {}, gobj);
18858
+ } }
18859
+ ]
18860
+ ];
18861
+ let center_items = [[
18862
+ "div",
18863
+ {
18864
+ class: "GRAPH_FIND control has-icons-left",
18865
+ style: "margin-right:.5rem; max-width:12rem; min-width:7rem;"
18866
+ },
18867
+ [["input", {
18868
+ class: "GRAPH_FIND_INPUT input",
18869
+ type: "text",
18870
+ placeholder: (0, i18next.t)("search"),
18871
+ "data-i18n-placeholder": "search",
18872
+ "aria-label": (0, i18next.t)("search"),
18873
+ "data-i18n-aria-label": "search"
18874
+ }], [
18875
+ "span",
18876
+ { class: "icon is-left" },
18877
+ [["i", { class: "yi-magnifying-glass" }]]
18878
+ ]],
18879
+ { input: (evt) => {
18880
+ evt.stopPropagation();
18881
+ (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_FIND_NODES", { text: evt.target.value.trim() }, gobj);
18882
+ } }
18883
+ ], [
18884
+ "div",
18885
+ {
18886
+ class: "GRAPH_FIND_RESULT is-hidden",
18887
+ style: "display:flex; align-items:center; gap:.3rem; margin-right:.5rem; font-size:.85rem;"
18888
+ },
18889
+ [[
18890
+ "span",
18891
+ { class: "GRAPH_FIND_COUNT" },
18892
+ ""
18893
+ ], [
18894
+ "span",
18895
+ { i18n: "matches" },
18896
+ "matches"
18897
+ ]]
18898
+ ]];
18775
18899
  const $toolbar_header = yui_toolbar({}, [
18776
18900
  [
18777
18901
  "div",
18778
18902
  { class: "yui-horizontal-toolbar-section left" },
18779
- [
18780
- [
18781
- "span",
18782
- {
18783
- class: "GRAPH_LAYOUT_LABEL is-hidden-mobile",
18784
- style: "padding-right:5px;",
18785
- i18n: "layout"
18786
- },
18787
- "layout"
18788
- ],
18789
- [
18790
- "div",
18791
- { class: "select" },
18792
- [["select", { class: "GRAPH_LAYOUT_SELECT" }]],
18793
- { change: (evt) => {
18794
- evt.stopPropagation();
18795
- (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_SET_LAYOUT", { layout: evt.target.value }, gobj);
18796
- } }
18797
- ],
18798
- [
18799
- "span",
18800
- {
18801
- class: "GRAPH_MODE_LABEL is-hidden-mobile",
18802
- style: "padding-left:10px; padding-right:5px;",
18803
- i18n: "operation mode"
18804
- },
18805
- "operation mode"
18806
- ],
18807
- [
18808
- "div",
18809
- { class: "select" },
18810
- [[
18811
- "select",
18812
- { class: "GRAPH_MODE_SELECT" },
18813
- modes.map((item) => [
18814
- "option",
18815
- {},
18816
- item
18817
- ])
18818
- ]],
18819
- { change: (evt) => {
18820
- evt.stopPropagation();
18821
- (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_SET_OPERATION_MODE", { operation_mode: evt.target.value }, gobj);
18822
- } }
18823
- ],
18824
- [
18825
- "button",
18826
- { class: "GRAPH_REFRESH button" },
18827
- [["i", { class: "yi-arrows-rotate" }], [
18828
- "span",
18829
- {
18830
- class: "is-hidden-mobile",
18831
- style: "padding-left:5px;",
18832
- i18n: "refresh"
18833
- },
18834
- "refresh"
18835
- ]],
18836
- { click: (evt) => {
18837
- evt.stopPropagation();
18838
- (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_REFRESH_TREEDB", { evt }, gobj);
18839
- } }
18840
- ],
18841
- [
18842
- "button",
18843
- {
18844
- class: "button ml-2 TREEDB_JSON_BTN",
18845
- title: (0, i18next.t)("raw json"),
18846
- "aria-label": (0, i18next.t)("raw json"),
18847
- "data-i18n-title": "raw json",
18848
- "data-i18n-aria-label": "raw json"
18849
- },
18850
- [["i", { class: "yi-eye" }], [
18851
- "span",
18852
- {
18853
- class: "is-hidden-mobile",
18854
- style: "padding-left:5px;",
18855
- i18n: "raw json"
18856
- },
18857
- "raw json"
18858
- ]],
18859
- { click: (evt) => {
18860
- evt.stopPropagation();
18861
- (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_OPEN_JSON", {}, gobj);
18862
- } }
18863
- ]
18864
- ]
18903
+ left_items
18865
18904
  ],
18866
18905
  [
18867
18906
  "div",
18868
18907
  { class: "yui-horizontal-toolbar-section center" },
18869
- []
18908
+ center_items
18870
18909
  ],
18871
18910
  [
18872
18911
  "div",
@@ -19798,6 +19837,33 @@ function ac_set_operation_mode$1(gobj, event, kw, src) {
19798
19837
  * tree, which centres + highlights that topic's nodes (deferring
19799
19838
  * until its data is loaded).
19800
19839
  ************************************************************/
19840
+ /************************************************************
19841
+ * Forward the find down to the graph child.
19842
+ ************************************************************/
19843
+ function ac_find_nodes$1(gobj, event, kw, src) {
19844
+ let priv = gobj.priv;
19845
+ if (priv.gobj_nodes_tree) (0, _yuneta_gobj_js.gobj_send_event)(priv.gobj_nodes_tree, "EV_FIND_NODES", { text: kw && kw.text || "" }, gobj);
19846
+ return 0;
19847
+ }
19848
+ /************************************************************
19849
+ * How many nodes the term matched, from the graph child. Zero with a
19850
+ * term typed is an ANSWER and is shown; an empty box shows nothing.
19851
+ ************************************************************/
19852
+ function ac_find_result(gobj, event, kw, src) {
19853
+ let $container = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
19854
+ if (!$container) return 0;
19855
+ let $result = $container.querySelector(".GRAPH_FIND_RESULT");
19856
+ let $count = $container.querySelector(".GRAPH_FIND_COUNT");
19857
+ if (!$result || !$count) return 0;
19858
+ if (!(kw && kw.term || "")) {
19859
+ $result.classList.add("is-hidden");
19860
+ $count.textContent = "";
19861
+ return 0;
19862
+ }
19863
+ $count.textContent = String(kw && kw.matches || 0);
19864
+ $result.classList.remove("is-hidden");
19865
+ return 0;
19866
+ }
19801
19867
  function ac_set_focus_topic(gobj, event, kw, src) {
19802
19868
  let priv = gobj.priv;
19803
19869
  if (priv.gobj_nodes_tree) (0, _yuneta_gobj_js.gobj_send_event)(priv.gobj_nodes_tree, "EV_FOCUS_TOPIC", { topic: kw && kw.topic }, gobj);
@@ -19963,6 +20029,16 @@ function create_gclass$4(gclass_name) {
19963
20029
  ac_set_focus_topic,
19964
20030
  null
19965
20031
  ],
20032
+ [
20033
+ "EV_FIND_NODES",
20034
+ ac_find_nodes$1,
20035
+ null
20036
+ ],
20037
+ [
20038
+ "EV_FIND_RESULT",
20039
+ ac_find_result,
20040
+ null
20041
+ ],
19966
20042
  [
19967
20043
  "EV_SHOW",
19968
20044
  ac_show$3,
@@ -20004,6 +20080,8 @@ function create_gclass$4(gclass_name) {
20004
20080
  ["EV_SET_LAYOUT", 0],
20005
20081
  ["EV_SET_OPERATION_MODE", 0],
20006
20082
  ["EV_SET_FOCUS_TOPIC", 0],
20083
+ ["EV_FIND_NODES", 0],
20084
+ ["EV_FIND_RESULT", 0],
20007
20085
  ["EV_OPERATION_MODE_CHANGED", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT | _yuneta_gobj_js.event_flag_t.EVF_NO_WARN_SUBS],
20008
20086
  ["EV_RECORD_WRITTEN", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT | _yuneta_gobj_js.event_flag_t.EVF_NO_WARN_SUBS],
20009
20087
  ["EV_SHOW", 0],
@@ -52669,7 +52747,8 @@ var PRIVATE_DATA$1 = {
52669
52747
  _link_saved_styles: [],
52670
52748
  _focus_topic: null,
52671
52749
  _focus_ids: [],
52672
- _pending_focus_topic: null
52750
+ _pending_focus_topic: null,
52751
+ _pending_find: null
52673
52752
  };
52674
52753
  var __gclass__$1 = null;
52675
52754
  /******************************
@@ -53843,6 +53922,67 @@ function graph_focus_topic(gobj, topic) {
53843
53922
  (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: focus topic apply failed: ${e}`);
53844
53923
  }
53845
53924
  }
53925
+ /************************************************************
53926
+ * Find the nodes whose name, id or topic contains `term`, highlight
53927
+ * them with the same amber 'active' state the topic focus uses, and
53928
+ * centre the viewport on them. Returns how many matched, which is the
53929
+ * half the toolbar needs: a search that finds nothing must SAY so,
53930
+ * because a graph that did not move is also what a graph looks like
53931
+ * when the match is off-screen.
53932
+ *
53933
+ * It shares the highlight slot with graph_focus_topic on purpose —
53934
+ * two amber sets at once would say nothing about either. An empty
53935
+ * term clears it. Matching reads the node's LABEL and not only its
53936
+ * id: on a topic keyed by rowid or by a qualified path, the id is a
53937
+ * counter and the name a human knows is elsewhere (see node_label).
53938
+ ************************************************************/
53939
+ function graph_find_nodes(gobj, term) {
53940
+ let priv = gobj.priv;
53941
+ let graph = priv.graph;
53942
+ if (!graph || !priv.graph_rendered) {
53943
+ priv._pending_find = term;
53944
+ return 0;
53945
+ }
53946
+ let data = {};
53947
+ try {
53948
+ data = graph.getData() || {};
53949
+ } catch (e) {
53950
+ (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: graph.getData() failed: ${e}`);
53951
+ return 0;
53952
+ }
53953
+ let nodes = (0, _yuneta_gobj_js.is_array)(data.nodes) ? data.nodes : [];
53954
+ let states = {};
53955
+ for (let id of priv._focus_ids || []) states[id] = [];
53956
+ let ids = [];
53957
+ if (!(0, _yuneta_gobj_js.empty_string)(term)) {
53958
+ let needle = String(term).toLowerCase();
53959
+ for (let n of nodes) {
53960
+ if (!n || !n.data) continue;
53961
+ let record = n.data.record || {};
53962
+ let label = "";
53963
+ try {
53964
+ label = node_label(n.data.desc || {}, record) || "";
53965
+ } catch (e) {
53966
+ label = "";
53967
+ }
53968
+ if ([
53969
+ label,
53970
+ record.id,
53971
+ n.data.topic_name
53972
+ ].filter((v) => typeof v === "string").join(" ").toLowerCase().includes(needle)) ids.push(n.id);
53973
+ }
53974
+ for (let id of ids) states[id] = ["active"];
53975
+ }
53976
+ priv._focus_ids = ids;
53977
+ priv._focus_topic = null;
53978
+ try {
53979
+ if (typeof graph.setElementState === "function") graph.setElementState(states);
53980
+ if (ids.length && typeof graph.focusElement === "function") graph.focusElement(ids);
53981
+ } catch (e) {
53982
+ (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: find apply failed: ${e}`);
53983
+ }
53984
+ return ids.length;
53985
+ }
53846
53986
  async function graph_zoom_in(gobj) {
53847
53987
  let graph = gobj.priv.graph;
53848
53988
  const z = graph.getZoom();
@@ -56137,6 +56277,11 @@ function ac_load_data(gobj, event, kw, src) {
56137
56277
  priv._pending_focus_topic = null;
56138
56278
  graph_focus_topic(gobj, ft);
56139
56279
  }
56280
+ if (priv._pending_find !== null) {
56281
+ let term = priv._pending_find;
56282
+ priv._pending_find = null;
56283
+ publish_find_result(gobj, term, graph_find_nodes(gobj, term));
56284
+ }
56140
56285
  });
56141
56286
  });
56142
56287
  });
@@ -56353,6 +56498,25 @@ function ac_focus_topic(gobj, event, kw, src) {
56353
56498
  graph_focus_topic(gobj, kw && kw.topic);
56354
56499
  return 0;
56355
56500
  }
56501
+ /************************************************************
56502
+ * Tell whoever asked how many nodes the term matched. The count is
56503
+ * the answer to the question the box asks; without it "nothing moved"
56504
+ * and "nothing matched" look the same.
56505
+ ************************************************************/
56506
+ function publish_find_result(gobj, term, matches) {
56507
+ (0, _yuneta_gobj_js.gobj_publish_event)(gobj, "EV_FIND_RESULT", {
56508
+ term,
56509
+ matches
56510
+ });
56511
+ }
56512
+ /************************************************************
56513
+ *
56514
+ ************************************************************/
56515
+ function ac_find_nodes(gobj, event, kw, src) {
56516
+ let term = kw && kw.text || "";
56517
+ publish_find_result(gobj, term, graph_find_nodes(gobj, term));
56518
+ return 0;
56519
+ }
56356
56520
  function ac_center(gobj, event, kw, src) {
56357
56521
  graph_center(gobj);
56358
56522
  return 0;
@@ -56654,6 +56818,11 @@ function create_gclass$1(gclass_name) {
56654
56818
  ac_focus_topic,
56655
56819
  null
56656
56820
  ],
56821
+ [
56822
+ "EV_FIND_NODES",
56823
+ ac_find_nodes,
56824
+ null
56825
+ ],
56657
56826
  [
56658
56827
  "EV_CENTER",
56659
56828
  ac_center,
@@ -56737,6 +56906,8 @@ function create_gclass$1(gclass_name) {
56737
56906
  ["EV_ZOOM_RESET", 0],
56738
56907
  ["EV_AUTO_FIT", 0],
56739
56908
  ["EV_FOCUS_TOPIC", 0],
56909
+ ["EV_FIND_NODES", 0],
56910
+ ["EV_FIND_RESULT", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT],
56740
56911
  ["EV_CENTER", 0],
56741
56912
  ["EV_FULLSCREEN", 0],
56742
56913
  ["EV_SET_LAYOUT", 0],
@@ -18759,101 +18759,140 @@ function make_toolbar(gobj) {
18759
18759
  gobj.priv;
18760
18760
  let modes = gobj_read_attr(gobj, "operation_modes");
18761
18761
  if (gobj_read_bool_attr(gobj, "readonly")) modes = modes.filter((mode) => mode !== "edition");
18762
+ let left_items = [
18763
+ [
18764
+ "span",
18765
+ {
18766
+ class: "GRAPH_LAYOUT_LABEL is-hidden-mobile",
18767
+ style: "padding-right:5px;",
18768
+ i18n: "layout"
18769
+ },
18770
+ "layout"
18771
+ ],
18772
+ [
18773
+ "div",
18774
+ { class: "select" },
18775
+ [["select", { class: "GRAPH_LAYOUT_SELECT" }]],
18776
+ { change: (evt) => {
18777
+ evt.stopPropagation();
18778
+ gobj_send_event(gobj, "EV_SET_LAYOUT", { layout: evt.target.value }, gobj);
18779
+ } }
18780
+ ],
18781
+ [
18782
+ "span",
18783
+ {
18784
+ class: "GRAPH_MODE_LABEL is-hidden-mobile",
18785
+ style: "padding-left:10px; padding-right:5px;",
18786
+ i18n: "operation mode"
18787
+ },
18788
+ "operation mode"
18789
+ ],
18790
+ [
18791
+ "div",
18792
+ { class: "select" },
18793
+ [[
18794
+ "select",
18795
+ { class: "GRAPH_MODE_SELECT" },
18796
+ modes.map((item) => [
18797
+ "option",
18798
+ {},
18799
+ item
18800
+ ])
18801
+ ]],
18802
+ { change: (evt) => {
18803
+ evt.stopPropagation();
18804
+ gobj_send_event(gobj, "EV_SET_OPERATION_MODE", { operation_mode: evt.target.value }, gobj);
18805
+ } }
18806
+ ],
18807
+ [
18808
+ "button",
18809
+ { class: "GRAPH_REFRESH button" },
18810
+ [["i", { class: "yi-arrows-rotate" }], [
18811
+ "span",
18812
+ {
18813
+ class: "is-hidden-mobile",
18814
+ style: "padding-left:5px;",
18815
+ i18n: "refresh"
18816
+ },
18817
+ "refresh"
18818
+ ]],
18819
+ { click: (evt) => {
18820
+ evt.stopPropagation();
18821
+ gobj_send_event(gobj, "EV_REFRESH_TREEDB", { evt }, gobj);
18822
+ } }
18823
+ ],
18824
+ [
18825
+ "button",
18826
+ {
18827
+ class: "button ml-2 TREEDB_JSON_BTN",
18828
+ title: t("raw json"),
18829
+ "aria-label": t("raw json"),
18830
+ "data-i18n-title": "raw json",
18831
+ "data-i18n-aria-label": "raw json"
18832
+ },
18833
+ [["i", { class: "yi-eye" }], [
18834
+ "span",
18835
+ {
18836
+ class: "is-hidden-mobile",
18837
+ style: "padding-left:5px;",
18838
+ i18n: "raw json"
18839
+ },
18840
+ "raw json"
18841
+ ]],
18842
+ { click: (evt) => {
18843
+ evt.stopPropagation();
18844
+ gobj_send_event(gobj, "EV_OPEN_JSON", {}, gobj);
18845
+ } }
18846
+ ]
18847
+ ];
18848
+ let center_items = [[
18849
+ "div",
18850
+ {
18851
+ class: "GRAPH_FIND control has-icons-left",
18852
+ style: "margin-right:.5rem; max-width:12rem; min-width:7rem;"
18853
+ },
18854
+ [["input", {
18855
+ class: "GRAPH_FIND_INPUT input",
18856
+ type: "text",
18857
+ placeholder: t("search"),
18858
+ "data-i18n-placeholder": "search",
18859
+ "aria-label": t("search"),
18860
+ "data-i18n-aria-label": "search"
18861
+ }], [
18862
+ "span",
18863
+ { class: "icon is-left" },
18864
+ [["i", { class: "yi-magnifying-glass" }]]
18865
+ ]],
18866
+ { input: (evt) => {
18867
+ evt.stopPropagation();
18868
+ gobj_send_event(gobj, "EV_FIND_NODES", { text: evt.target.value.trim() }, gobj);
18869
+ } }
18870
+ ], [
18871
+ "div",
18872
+ {
18873
+ class: "GRAPH_FIND_RESULT is-hidden",
18874
+ style: "display:flex; align-items:center; gap:.3rem; margin-right:.5rem; font-size:.85rem;"
18875
+ },
18876
+ [[
18877
+ "span",
18878
+ { class: "GRAPH_FIND_COUNT" },
18879
+ ""
18880
+ ], [
18881
+ "span",
18882
+ { i18n: "matches" },
18883
+ "matches"
18884
+ ]]
18885
+ ]];
18762
18886
  const $toolbar_header = yui_toolbar({}, [
18763
18887
  [
18764
18888
  "div",
18765
18889
  { class: "yui-horizontal-toolbar-section left" },
18766
- [
18767
- [
18768
- "span",
18769
- {
18770
- class: "GRAPH_LAYOUT_LABEL is-hidden-mobile",
18771
- style: "padding-right:5px;",
18772
- i18n: "layout"
18773
- },
18774
- "layout"
18775
- ],
18776
- [
18777
- "div",
18778
- { class: "select" },
18779
- [["select", { class: "GRAPH_LAYOUT_SELECT" }]],
18780
- { change: (evt) => {
18781
- evt.stopPropagation();
18782
- gobj_send_event(gobj, "EV_SET_LAYOUT", { layout: evt.target.value }, gobj);
18783
- } }
18784
- ],
18785
- [
18786
- "span",
18787
- {
18788
- class: "GRAPH_MODE_LABEL is-hidden-mobile",
18789
- style: "padding-left:10px; padding-right:5px;",
18790
- i18n: "operation mode"
18791
- },
18792
- "operation mode"
18793
- ],
18794
- [
18795
- "div",
18796
- { class: "select" },
18797
- [[
18798
- "select",
18799
- { class: "GRAPH_MODE_SELECT" },
18800
- modes.map((item) => [
18801
- "option",
18802
- {},
18803
- item
18804
- ])
18805
- ]],
18806
- { change: (evt) => {
18807
- evt.stopPropagation();
18808
- gobj_send_event(gobj, "EV_SET_OPERATION_MODE", { operation_mode: evt.target.value }, gobj);
18809
- } }
18810
- ],
18811
- [
18812
- "button",
18813
- { class: "GRAPH_REFRESH button" },
18814
- [["i", { class: "yi-arrows-rotate" }], [
18815
- "span",
18816
- {
18817
- class: "is-hidden-mobile",
18818
- style: "padding-left:5px;",
18819
- i18n: "refresh"
18820
- },
18821
- "refresh"
18822
- ]],
18823
- { click: (evt) => {
18824
- evt.stopPropagation();
18825
- gobj_send_event(gobj, "EV_REFRESH_TREEDB", { evt }, gobj);
18826
- } }
18827
- ],
18828
- [
18829
- "button",
18830
- {
18831
- class: "button ml-2 TREEDB_JSON_BTN",
18832
- title: t("raw json"),
18833
- "aria-label": t("raw json"),
18834
- "data-i18n-title": "raw json",
18835
- "data-i18n-aria-label": "raw json"
18836
- },
18837
- [["i", { class: "yi-eye" }], [
18838
- "span",
18839
- {
18840
- class: "is-hidden-mobile",
18841
- style: "padding-left:5px;",
18842
- i18n: "raw json"
18843
- },
18844
- "raw json"
18845
- ]],
18846
- { click: (evt) => {
18847
- evt.stopPropagation();
18848
- gobj_send_event(gobj, "EV_OPEN_JSON", {}, gobj);
18849
- } }
18850
- ]
18851
- ]
18890
+ left_items
18852
18891
  ],
18853
18892
  [
18854
18893
  "div",
18855
18894
  { class: "yui-horizontal-toolbar-section center" },
18856
- []
18895
+ center_items
18857
18896
  ],
18858
18897
  [
18859
18898
  "div",
@@ -19785,6 +19824,33 @@ function ac_set_operation_mode$1(gobj, event, kw, src) {
19785
19824
  * tree, which centres + highlights that topic's nodes (deferring
19786
19825
  * until its data is loaded).
19787
19826
  ************************************************************/
19827
+ /************************************************************
19828
+ * Forward the find down to the graph child.
19829
+ ************************************************************/
19830
+ function ac_find_nodes$1(gobj, event, kw, src) {
19831
+ let priv = gobj.priv;
19832
+ if (priv.gobj_nodes_tree) gobj_send_event(priv.gobj_nodes_tree, "EV_FIND_NODES", { text: kw && kw.text || "" }, gobj);
19833
+ return 0;
19834
+ }
19835
+ /************************************************************
19836
+ * How many nodes the term matched, from the graph child. Zero with a
19837
+ * term typed is an ANSWER and is shown; an empty box shows nothing.
19838
+ ************************************************************/
19839
+ function ac_find_result(gobj, event, kw, src) {
19840
+ let $container = gobj_read_attr(gobj, "$container");
19841
+ if (!$container) return 0;
19842
+ let $result = $container.querySelector(".GRAPH_FIND_RESULT");
19843
+ let $count = $container.querySelector(".GRAPH_FIND_COUNT");
19844
+ if (!$result || !$count) return 0;
19845
+ if (!(kw && kw.term || "")) {
19846
+ $result.classList.add("is-hidden");
19847
+ $count.textContent = "";
19848
+ return 0;
19849
+ }
19850
+ $count.textContent = String(kw && kw.matches || 0);
19851
+ $result.classList.remove("is-hidden");
19852
+ return 0;
19853
+ }
19788
19854
  function ac_set_focus_topic(gobj, event, kw, src) {
19789
19855
  let priv = gobj.priv;
19790
19856
  if (priv.gobj_nodes_tree) gobj_send_event(priv.gobj_nodes_tree, "EV_FOCUS_TOPIC", { topic: kw && kw.topic }, gobj);
@@ -19950,6 +20016,16 @@ function create_gclass$4(gclass_name) {
19950
20016
  ac_set_focus_topic,
19951
20017
  null
19952
20018
  ],
20019
+ [
20020
+ "EV_FIND_NODES",
20021
+ ac_find_nodes$1,
20022
+ null
20023
+ ],
20024
+ [
20025
+ "EV_FIND_RESULT",
20026
+ ac_find_result,
20027
+ null
20028
+ ],
19953
20029
  [
19954
20030
  "EV_SHOW",
19955
20031
  ac_show$3,
@@ -19991,6 +20067,8 @@ function create_gclass$4(gclass_name) {
19991
20067
  ["EV_SET_LAYOUT", 0],
19992
20068
  ["EV_SET_OPERATION_MODE", 0],
19993
20069
  ["EV_SET_FOCUS_TOPIC", 0],
20070
+ ["EV_FIND_NODES", 0],
20071
+ ["EV_FIND_RESULT", 0],
19994
20072
  ["EV_OPERATION_MODE_CHANGED", event_flag_t.EVF_OUTPUT_EVENT | event_flag_t.EVF_NO_WARN_SUBS],
19995
20073
  ["EV_RECORD_WRITTEN", event_flag_t.EVF_OUTPUT_EVENT | event_flag_t.EVF_NO_WARN_SUBS],
19996
20074
  ["EV_SHOW", 0],
@@ -52656,7 +52734,8 @@ var PRIVATE_DATA$1 = {
52656
52734
  _link_saved_styles: [],
52657
52735
  _focus_topic: null,
52658
52736
  _focus_ids: [],
52659
- _pending_focus_topic: null
52737
+ _pending_focus_topic: null,
52738
+ _pending_find: null
52660
52739
  };
52661
52740
  var __gclass__$1 = null;
52662
52741
  /******************************
@@ -53830,6 +53909,67 @@ function graph_focus_topic(gobj, topic) {
53830
53909
  log_error(`${gobj_short_name(gobj)}: focus topic apply failed: ${e}`);
53831
53910
  }
53832
53911
  }
53912
+ /************************************************************
53913
+ * Find the nodes whose name, id or topic contains `term`, highlight
53914
+ * them with the same amber 'active' state the topic focus uses, and
53915
+ * centre the viewport on them. Returns how many matched, which is the
53916
+ * half the toolbar needs: a search that finds nothing must SAY so,
53917
+ * because a graph that did not move is also what a graph looks like
53918
+ * when the match is off-screen.
53919
+ *
53920
+ * It shares the highlight slot with graph_focus_topic on purpose —
53921
+ * two amber sets at once would say nothing about either. An empty
53922
+ * term clears it. Matching reads the node's LABEL and not only its
53923
+ * id: on a topic keyed by rowid or by a qualified path, the id is a
53924
+ * counter and the name a human knows is elsewhere (see node_label).
53925
+ ************************************************************/
53926
+ function graph_find_nodes(gobj, term) {
53927
+ let priv = gobj.priv;
53928
+ let graph = priv.graph;
53929
+ if (!graph || !priv.graph_rendered) {
53930
+ priv._pending_find = term;
53931
+ return 0;
53932
+ }
53933
+ let data = {};
53934
+ try {
53935
+ data = graph.getData() || {};
53936
+ } catch (e) {
53937
+ log_error(`${gobj_short_name(gobj)}: graph.getData() failed: ${e}`);
53938
+ return 0;
53939
+ }
53940
+ let nodes = is_array(data.nodes) ? data.nodes : [];
53941
+ let states = {};
53942
+ for (let id of priv._focus_ids || []) states[id] = [];
53943
+ let ids = [];
53944
+ if (!empty_string(term)) {
53945
+ let needle = String(term).toLowerCase();
53946
+ for (let n of nodes) {
53947
+ if (!n || !n.data) continue;
53948
+ let record = n.data.record || {};
53949
+ let label = "";
53950
+ try {
53951
+ label = node_label(n.data.desc || {}, record) || "";
53952
+ } catch (e) {
53953
+ label = "";
53954
+ }
53955
+ if ([
53956
+ label,
53957
+ record.id,
53958
+ n.data.topic_name
53959
+ ].filter((v) => typeof v === "string").join(" ").toLowerCase().includes(needle)) ids.push(n.id);
53960
+ }
53961
+ for (let id of ids) states[id] = ["active"];
53962
+ }
53963
+ priv._focus_ids = ids;
53964
+ priv._focus_topic = null;
53965
+ try {
53966
+ if (typeof graph.setElementState === "function") graph.setElementState(states);
53967
+ if (ids.length && typeof graph.focusElement === "function") graph.focusElement(ids);
53968
+ } catch (e) {
53969
+ log_error(`${gobj_short_name(gobj)}: find apply failed: ${e}`);
53970
+ }
53971
+ return ids.length;
53972
+ }
53833
53973
  async function graph_zoom_in(gobj) {
53834
53974
  let graph = gobj.priv.graph;
53835
53975
  const z = graph.getZoom();
@@ -56124,6 +56264,11 @@ function ac_load_data(gobj, event, kw, src) {
56124
56264
  priv._pending_focus_topic = null;
56125
56265
  graph_focus_topic(gobj, ft);
56126
56266
  }
56267
+ if (priv._pending_find !== null) {
56268
+ let term = priv._pending_find;
56269
+ priv._pending_find = null;
56270
+ publish_find_result(gobj, term, graph_find_nodes(gobj, term));
56271
+ }
56127
56272
  });
56128
56273
  });
56129
56274
  });
@@ -56340,6 +56485,25 @@ function ac_focus_topic(gobj, event, kw, src) {
56340
56485
  graph_focus_topic(gobj, kw && kw.topic);
56341
56486
  return 0;
56342
56487
  }
56488
+ /************************************************************
56489
+ * Tell whoever asked how many nodes the term matched. The count is
56490
+ * the answer to the question the box asks; without it "nothing moved"
56491
+ * and "nothing matched" look the same.
56492
+ ************************************************************/
56493
+ function publish_find_result(gobj, term, matches) {
56494
+ gobj_publish_event(gobj, "EV_FIND_RESULT", {
56495
+ term,
56496
+ matches
56497
+ });
56498
+ }
56499
+ /************************************************************
56500
+ *
56501
+ ************************************************************/
56502
+ function ac_find_nodes(gobj, event, kw, src) {
56503
+ let term = kw && kw.text || "";
56504
+ publish_find_result(gobj, term, graph_find_nodes(gobj, term));
56505
+ return 0;
56506
+ }
56343
56507
  function ac_center(gobj, event, kw, src) {
56344
56508
  graph_center(gobj);
56345
56509
  return 0;
@@ -56641,6 +56805,11 @@ function create_gclass$1(gclass_name) {
56641
56805
  ac_focus_topic,
56642
56806
  null
56643
56807
  ],
56808
+ [
56809
+ "EV_FIND_NODES",
56810
+ ac_find_nodes,
56811
+ null
56812
+ ],
56644
56813
  [
56645
56814
  "EV_CENTER",
56646
56815
  ac_center,
@@ -56724,6 +56893,8 @@ function create_gclass$1(gclass_name) {
56724
56893
  ["EV_ZOOM_RESET", 0],
56725
56894
  ["EV_AUTO_FIT", 0],
56726
56895
  ["EV_FOCUS_TOPIC", 0],
56896
+ ["EV_FIND_NODES", 0],
56897
+ ["EV_FIND_RESULT", event_flag_t.EVF_OUTPUT_EVENT],
56727
56898
  ["EV_CENTER", 0],
56728
56899
  ["EV_FULLSCREEN", 0],
56729
56900
  ["EV_SET_LAYOUT", 0],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuneta/gobj-ui",
3
- "version": "7.1.0",
3
+ "version": "7.2.1",
4
4
  "type": "module",
5
5
  "main": "dist/gobj-ui.cjs.js",
6
6
  "module": "dist/gobj-ui.es.js",
@@ -280,6 +280,7 @@ let PRIVATE_DATA = {
280
280
  _focus_topic: null, // topic currently focused (EV_FOCUS_TOPIC)
281
281
  _focus_ids: [], // node ids carrying the focus 'active' state
282
282
  _pending_focus_topic: null, // focus requested before data was loaded
283
+ _pending_find: null, // find requested before data was loaded
283
284
  };
284
285
 
285
286
  let __gclass__ = null;
@@ -2127,6 +2128,89 @@ function graph_focus_topic(gobj, topic)
2127
2128
  }
2128
2129
  }
2129
2130
 
2131
+ /************************************************************
2132
+ * Find the nodes whose name, id or topic contains `term`, highlight
2133
+ * them with the same amber 'active' state the topic focus uses, and
2134
+ * centre the viewport on them. Returns how many matched, which is the
2135
+ * half the toolbar needs: a search that finds nothing must SAY so,
2136
+ * because a graph that did not move is also what a graph looks like
2137
+ * when the match is off-screen.
2138
+ *
2139
+ * It shares the highlight slot with graph_focus_topic on purpose —
2140
+ * two amber sets at once would say nothing about either. An empty
2141
+ * term clears it. Matching reads the node's LABEL and not only its
2142
+ * id: on a topic keyed by rowid or by a qualified path, the id is a
2143
+ * counter and the name a human knows is elsewhere (see node_label).
2144
+ ************************************************************/
2145
+ function graph_find_nodes(gobj, term)
2146
+ {
2147
+ let priv = gobj.priv;
2148
+ let graph = priv.graph;
2149
+ if(!graph || !priv.graph_rendered) {
2150
+ priv._pending_find = term;
2151
+ return 0;
2152
+ }
2153
+
2154
+ let data = {};
2155
+ try {
2156
+ data = graph.getData() || {};
2157
+ } catch(e) {
2158
+ log_error(`${gobj_short_name(gobj)}: graph.getData() failed: ${e}`);
2159
+ return 0;
2160
+ }
2161
+ let nodes = is_array(data.nodes) ? data.nodes : [];
2162
+
2163
+ let states = {};
2164
+ for(let id of (priv._focus_ids || [])) {
2165
+ states[id] = [];
2166
+ }
2167
+
2168
+ let ids = [];
2169
+ if(!empty_string(term)) {
2170
+ let needle = String(term).toLowerCase();
2171
+ for(let n of nodes) {
2172
+ if(!n || !n.data) {
2173
+ continue;
2174
+ }
2175
+ let record = n.data.record || {};
2176
+ let label = "";
2177
+ try {
2178
+ label = node_label(n.data.desc || {}, record) || "";
2179
+ } catch(e) {
2180
+ label = "";
2181
+ }
2182
+ let haystack = [
2183
+ label,
2184
+ record.id,
2185
+ n.data.topic_name
2186
+ ].filter((v) => typeof v === "string").join(" ").toLowerCase();
2187
+
2188
+ if(haystack.includes(needle)) {
2189
+ ids.push(n.id);
2190
+ }
2191
+ }
2192
+ for(let id of ids) {
2193
+ states[id] = ['active'];
2194
+ }
2195
+ }
2196
+
2197
+ priv._focus_ids = ids;
2198
+ priv._focus_topic = null; /* a find takes over the highlight */
2199
+
2200
+ try {
2201
+ if(typeof graph.setElementState === "function") {
2202
+ graph.setElementState(states);
2203
+ }
2204
+ if(ids.length && typeof graph.focusElement === "function") {
2205
+ graph.focusElement(ids);
2206
+ }
2207
+ } catch(e) {
2208
+ log_error(`${gobj_short_name(gobj)}: find apply failed: ${e}`);
2209
+ }
2210
+
2211
+ return ids.length;
2212
+ }
2213
+
2130
2214
  async function graph_zoom_in(gobj)
2131
2215
  {
2132
2216
  let priv = gobj.priv;
@@ -5546,6 +5630,11 @@ function ac_load_data(gobj, event, kw, src)
5546
5630
  priv._pending_focus_topic = null;
5547
5631
  graph_focus_topic(gobj, ft);
5548
5632
  }
5633
+ if(priv._pending_find !== null) {
5634
+ let term = priv._pending_find;
5635
+ priv._pending_find = null;
5636
+ publish_find_result(gobj, term, graph_find_nodes(gobj, term));
5637
+ }
5549
5638
  });
5550
5639
  });
5551
5640
  });
@@ -5891,6 +5980,27 @@ function ac_focus_topic(gobj, event, kw, src)
5891
5980
  return 0;
5892
5981
  }
5893
5982
 
5983
+ /************************************************************
5984
+ * Tell whoever asked how many nodes the term matched. The count is
5985
+ * the answer to the question the box asks; without it "nothing moved"
5986
+ * and "nothing matched" look the same.
5987
+ ************************************************************/
5988
+ function publish_find_result(gobj, term, matches)
5989
+ {
5990
+ gobj_publish_event(gobj, "EV_FIND_RESULT", {term: term, matches: matches});
5991
+ }
5992
+
5993
+ /************************************************************
5994
+ *
5995
+ ************************************************************/
5996
+ function ac_find_nodes(gobj, event, kw, src)
5997
+ {
5998
+ let term = (kw && kw.text) || "";
5999
+ let matches = graph_find_nodes(gobj, term);
6000
+ publish_find_result(gobj, term, matches);
6001
+ return 0;
6002
+ }
6003
+
5894
6004
  function ac_center(gobj, event, kw, src)
5895
6005
  {
5896
6006
  graph_center(gobj);
@@ -6254,6 +6364,7 @@ function create_gclass(gclass_name)
6254
6364
  ["EV_ZOOM_RESET", ac_zoom_reset, null],
6255
6365
  ["EV_AUTO_FIT", ac_auto_fit, null],
6256
6366
  ["EV_FOCUS_TOPIC", ac_focus_topic, null],
6367
+ ["EV_FIND_NODES", ac_find_nodes, null],
6257
6368
  ["EV_CENTER", ac_center, null],
6258
6369
  ["EV_FULLSCREEN", ac_fullscreen, null],
6259
6370
  ["EV_SET_LAYOUT", ac_set_layout, null],
@@ -6297,6 +6408,8 @@ function create_gclass(gclass_name)
6297
6408
  ["EV_ZOOM_RESET", 0],
6298
6409
  ["EV_AUTO_FIT", 0],
6299
6410
  ["EV_FOCUS_TOPIC", 0],
6411
+ ["EV_FIND_NODES", 0],
6412
+ ["EV_FIND_RESULT", event_flag_t.EVF_OUTPUT_EVENT],
6300
6413
  ["EV_CENTER", 0],
6301
6414
  ["EV_FULLSCREEN", 0],
6302
6415
  ["EV_SET_LAYOUT", 0],
@@ -504,9 +504,58 @@ function make_toolbar(gobj)
504
504
  ];
505
505
 
506
506
  /*
507
- * Center, common controls
507
+ * Center: find a node.
508
+ *
509
+ * A graph of a few hundred records has no other way in: the only way
510
+ * to locate one was to read every card. The box highlights every
511
+ * match with the same amber the topic focus uses and centres the
512
+ * viewport on them, and it SAYS how many it found — a graph that did
513
+ * not move looks the same whether nothing matched or the match was
514
+ * already on screen.
508
515
  */
509
- let center_items = [];
516
+ let center_items = [
517
+ ['div', {class: 'GRAPH_FIND control has-icons-left',
518
+ style: 'margin-right:.5rem; max-width:12rem; min-width:7rem;'}, [
519
+ ['input', {
520
+ class: 'GRAPH_FIND_INPUT input',
521
+ type: 'text',
522
+ /* A placeholder is not a text node, so the data-i18n walk
523
+ * cannot reach it: it needs its own key. */
524
+ placeholder: t('search'),
525
+ 'data-i18n-placeholder': 'search',
526
+ 'aria-label': t('search'),
527
+ 'data-i18n-aria-label': 'search'
528
+ }],
529
+ ['span', {class: 'icon is-left'}, [
530
+ ['i', {class: 'yi-magnifying-glass'}]
531
+ ]]
532
+ ], {
533
+ input: (evt) => {
534
+ evt.stopPropagation();
535
+ gobj_send_event(
536
+ gobj,
537
+ "EV_FIND_NODES",
538
+ {text: evt.target.value.trim()},
539
+ gobj
540
+ );
541
+ }
542
+ }],
543
+ /* Two spans, not one string: the number is DATA and "matches" is
544
+ * the word, so a language switch re-translates the half that is a
545
+ * word. Spaced with CSS because createElement2 trims text nodes. */
546
+ /* `display:flex` inline and NOT the `is-flex` helper: both Bulma
547
+ * helpers carry !important, so `is-hidden is-flex` on one element
548
+ * is decided by whichever lands later in the stylesheet — and the
549
+ * count showed with an empty box. An inline rule loses to
550
+ * `is-hidden`'s !important and applies the moment it is removed,
551
+ * which is the toggle this needs. */
552
+ ['div', {class: 'GRAPH_FIND_RESULT is-hidden',
553
+ style: 'display:flex; align-items:center; gap:.3rem; ' +
554
+ 'margin-right:.5rem; font-size:.85rem;'}, [
555
+ ['span', {class: 'GRAPH_FIND_COUNT'}, ''],
556
+ ['span', {i18n: 'matches'}, 'matches']
557
+ ]]
558
+ ];
510
559
 
511
560
  /*
512
561
  * Right, fill in set_mode
@@ -2129,6 +2178,51 @@ function ac_set_operation_mode(gobj, event, kw, src)
2129
2178
  * tree, which centres + highlights that topic's nodes (deferring
2130
2179
  * until its data is loaded).
2131
2180
  ************************************************************/
2181
+ /************************************************************
2182
+ * Forward the find down to the graph child.
2183
+ ************************************************************/
2184
+ function ac_find_nodes(gobj, event, kw, src)
2185
+ {
2186
+ let priv = gobj.priv;
2187
+ if(priv.gobj_nodes_tree) {
2188
+ gobj_send_event(
2189
+ priv.gobj_nodes_tree,
2190
+ "EV_FIND_NODES",
2191
+ {text: (kw && kw.text) || ""},
2192
+ gobj
2193
+ );
2194
+ }
2195
+ return 0;
2196
+ }
2197
+
2198
+ /************************************************************
2199
+ * How many nodes the term matched, from the graph child. Zero with a
2200
+ * term typed is an ANSWER and is shown; an empty box shows nothing.
2201
+ ************************************************************/
2202
+ function ac_find_result(gobj, event, kw, src)
2203
+ {
2204
+ let $container = gobj_read_attr(gobj, "$container");
2205
+ if(!$container) {
2206
+ return 0;
2207
+ }
2208
+ let $result = $container.querySelector(".GRAPH_FIND_RESULT");
2209
+ let $count = $container.querySelector(".GRAPH_FIND_COUNT");
2210
+ if(!$result || !$count) {
2211
+ return 0;
2212
+ }
2213
+
2214
+ let term = (kw && kw.term) || "";
2215
+ if(!term) {
2216
+ $result.classList.add("is-hidden");
2217
+ $count.textContent = "";
2218
+ return 0;
2219
+ }
2220
+
2221
+ $count.textContent = String((kw && kw.matches) || 0);
2222
+ $result.classList.remove("is-hidden");
2223
+ return 0;
2224
+ }
2225
+
2132
2226
  function ac_set_focus_topic(gobj, event, kw, src)
2133
2227
  {
2134
2228
  let priv = gobj.priv;
@@ -2228,6 +2322,8 @@ function create_gclass(gclass_name)
2228
2322
  ["EV_SET_LAYOUT", ac_set_layout, null],
2229
2323
  ["EV_SET_OPERATION_MODE", ac_set_operation_mode, null],
2230
2324
  ["EV_SET_FOCUS_TOPIC", ac_set_focus_topic, null],
2325
+ ["EV_FIND_NODES", ac_find_nodes, null],
2326
+ ["EV_FIND_RESULT", ac_find_result, null],
2231
2327
  ["EV_SHOW", ac_show, null],
2232
2328
  ["EV_HIDE", ac_hide, null],
2233
2329
  ["EV_TRANSPORT_STATE", ac_transport_state, null],
@@ -2262,6 +2358,8 @@ function create_gclass(gclass_name)
2262
2358
  ["EV_SET_LAYOUT", 0],
2263
2359
  ["EV_SET_OPERATION_MODE", 0],
2264
2360
  ["EV_SET_FOCUS_TOPIC", 0],
2361
+ ["EV_FIND_NODES", 0],
2362
+ ["EV_FIND_RESULT", 0],
2265
2363
  ["EV_OPERATION_MODE_CHANGED",
2266
2364
  event_flag_t.EVF_OUTPUT_EVENT | event_flag_t.EVF_NO_WARN_SUBS],
2267
2365
  ["EV_RECORD_WRITTEN",