@yuneta/gobj-ui 7.23.157 → 7.23.158

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.
@@ -56715,8 +56715,9 @@ var PRIVATE_DATA$4 = {
56715
56715
  _selected_paint_ids: [],
56716
56716
  _pending_focus_topic: null,
56717
56717
  _pending_focus_all: false,
56718
- _find_hidden_matches: 0,
56719
- _pending_find: null,
56718
+ _find_term: "",
56719
+ _find_current: "",
56720
+ _find_found: null,
56720
56721
  _layout_asked: "",
56721
56722
  _nodes_total: 0,
56722
56723
  _nodes_placed: 0,
@@ -58671,6 +58672,14 @@ function graph_focus_topic(gobj, topic, reveal_all) {
58671
58672
  }
58672
58673
  let model = priv._fold_model;
58673
58674
  let state = priv._fold_state;
58675
+ if (empty_string(topic) && priv._find_term) {
58676
+ priv._focus_topic = null;
58677
+ return;
58678
+ }
58679
+ if (!empty_string(topic)) {
58680
+ priv._find_term = "";
58681
+ priv._find_current = "";
58682
+ }
58674
58683
  let keys = [];
58675
58684
  if (!empty_string(topic)) {
58676
58685
  for (let node of model.nodes.values()) if (node.topic_name === topic) keys.push(node.key);
@@ -58686,17 +58695,17 @@ function graph_focus_topic(gobj, topic, reveal_all) {
58686
58695
  }
58687
58696
  priv._focus_topic = topic || null;
58688
58697
  reconcile_fold(gobj, {}).then(() => {
58689
- apply_focus_ids(gobj, keys, "focus topic");
58698
+ apply_focus_ids(gobj, keys, "focus topic", true);
58690
58699
  });
58691
58700
  }
58692
58701
  /************************************************************
58693
- * Paint the highlight on these records (by model key) and put
58694
- * the first of them in the middle. Shared by the find and the
58695
- * topic focus, after the reconcile that brought them on screen.
58696
- * Keys that are still off screen are skipped: a match beyond the
58697
- * page a reveal opened is counted, not painted.
58702
+ * Paint the highlight on these records (by model key) and, with
58703
+ * `center`, put the first of them in the middle. Shared by the
58704
+ * find and the topic focus. Keys that are not on screen are
58705
+ * skipped: a match beyond the page a reveal opened is counted,
58706
+ * not painted.
58698
58707
  ************************************************************/
58699
- function apply_focus_ids(gobj, keys, what) {
58708
+ function apply_focus_ids(gobj, keys, what, center) {
58700
58709
  let priv = gobj.priv;
58701
58710
  let graph = priv.graph;
58702
58711
  if (!graph) return;
@@ -58721,7 +58730,7 @@ function apply_focus_ids(gobj, keys, what) {
58721
58730
  try {
58722
58731
  if (typeof graph.setElementState === "function") graph.setElementState(states);
58723
58732
  apply_node_highlight(gobj, prev_ids, ids);
58724
- if (ids.length) yui_graph_center_on(graph, ids[0]);
58733
+ if (center && ids.length) yui_graph_center_on(graph, ids[0]);
58725
58734
  } catch (e) {
58726
58735
  log_error(`${gobj_short_name(gobj)}: ${what} apply failed: ${e}`);
58727
58736
  }
@@ -58732,61 +58741,139 @@ function node_id_of_key(gobj, key) {
58732
58741
  return build_node_name(gobj, key.slice(0, i), key.slice(i + 1));
58733
58742
  }
58734
58743
  /************************************************************
58735
- * Find the nodes whose name, id or topic contains `term`, highlight
58736
- * them with the same amber 'active' state the topic focus uses, and
58737
- * centre the viewport on them. Returns how many matched, which is the
58738
- * half the toolbar needs: a search that finds nothing must SAY so,
58739
- * because a graph that did not move is also what a graph looks like
58740
- * when the match is off-screen.
58744
+ * The find box: highlight the cards ON SCREEN whose name, id or
58745
+ * topic contains `term`, and change nothing else -- no group is
58746
+ * opened, no layout runs, the camera does not move. The graph the
58747
+ * reader arranged is the graph they are searching; emptying the
58748
+ * box takes the amber off and leaves it exactly as it was.
58749
+ *
58750
+ * It used to search the whole treedb and unfold a page of what it
58751
+ * found, which re-laid the graph out on every keystroke and left
58752
+ * the unfolded groups behind when the box was cleared. Opening up
58753
+ * to a topic is the legend's focus (graph_focus_topic); this only
58754
+ * LOOKS.
58755
+ *
58756
+ * The term is kept and repainted after every reconcile (see
58757
+ * reconcile_fold_now): a group opened by hand shows its matches
58758
+ * already lit, and the count follows what is on screen.
58741
58759
  *
58742
58760
  * It shares the highlight slot with graph_focus_topic on purpose —
58743
- * two amber sets at once would say nothing about either. An empty
58744
- * term clears it. Matching reads the node's LABEL and not only its
58745
- * id: on a topic keyed by rowid or by a qualified path, the id is a
58746
- * counter and the name a human knows is elsewhere (see node_label).
58761
+ * two amber sets at once would say nothing about either. Matching
58762
+ * reads the node's LABEL and not only its id: on a topic keyed by
58763
+ * rowid or by a qualified path, the id is a counter and the name a
58764
+ * human knows is elsewhere (see node_label).
58747
58765
  ************************************************************/
58748
58766
  function graph_find_nodes(gobj, term) {
58749
58767
  let priv = gobj.priv;
58750
- if (!priv.graph || !priv.graph_rendered || !priv._fold_model) {
58751
- priv._pending_find = term;
58752
- return 0;
58768
+ let next = empty_string(term) ? "" : String(term);
58769
+ let was = priv._find_term;
58770
+ priv._find_term = next;
58771
+ priv._find_current = "";
58772
+ priv._find_found = null;
58773
+ let ready = priv.graph && priv.graph_rendered && priv._fold_model;
58774
+ if (!next) {
58775
+ if (was && ready && !priv._focus_topic) apply_focus_ids(gobj, [], "find", false);
58776
+ publish_find_result(gobj);
58777
+ return;
58753
58778
  }
58779
+ priv._focus_topic = null;
58780
+ if (!ready) return;
58781
+ refresh_find(gobj, fold_visible_set(priv._fold_model, priv._fold_state));
58782
+ }
58783
+ /************************************************************
58784
+ * Repaint the find's highlight over the `visible` set and publish
58785
+ * the count. A no-op while no term is live, so the topic focus
58786
+ * keeps the slot.
58787
+ ************************************************************/
58788
+ function refresh_find(gobj, visible) {
58789
+ let priv = gobj.priv;
58790
+ if (!priv._find_term) return;
58791
+ let found = find_matches(gobj, priv._find_term, visible);
58792
+ apply_focus_ids(gobj, found.keys, "find", false);
58793
+ if (priv._find_current && (priv._focus_ids || []).indexOf(priv._find_current) < 0) priv._find_current = "";
58794
+ priv._find_found = found;
58795
+ publish_find_result(gobj);
58796
+ }
58797
+ /************************************************************
58798
+ * The records matching `term`: the keys ON SCREEN, and how many
58799
+ * more match that are NOT -- folded away in the fold model, or in
58800
+ * a hidden topic. Those two are only counted: "0" alone would read
58801
+ * as "does not exist" when it means "is not on screen".
58802
+ ************************************************************/
58803
+ function find_matches(gobj, term, visible) {
58804
+ let priv = gobj.priv;
58754
58805
  let model = priv._fold_model;
58755
- let state = priv._fold_state;
58756
- let keys = [];
58757
- let hidden_matches = 0;
58758
- if (!empty_string(term)) {
58759
- let needle = String(term).toLowerCase();
58760
- let matches = (topic_name, record) => {
58761
- let label = "";
58762
- try {
58763
- label = node_label(priv.descs[topic_name] || {}, record) || "";
58764
- } catch (e) {
58765
- label = "";
58766
- }
58767
- return [
58768
- label,
58769
- record.id,
58770
- topic_name
58771
- ].filter((v) => typeof v === "string").join(" ").toLowerCase().includes(needle);
58772
- };
58773
- for (let node of model.nodes.values()) if (matches(node.topic_name, node.record || {})) keys.push(node.key);
58774
- for (let topic_name of model.hidden) for (let record of priv.records[topic_name] || []) if (is_object(record) && matches(topic_name, record)) hidden_matches++;
58806
+ let found = {
58807
+ keys: [],
58808
+ folded: 0,
58809
+ hidden: 0
58810
+ };
58811
+ if (!model || empty_string(term)) return found;
58812
+ let needle = String(term).toLowerCase();
58813
+ let matches = (topic_name, record) => {
58814
+ let label = "";
58815
+ try {
58816
+ label = node_label(priv.descs[topic_name] || {}, record) || "";
58817
+ } catch (e) {
58818
+ label = "";
58819
+ }
58820
+ return [
58821
+ label,
58822
+ record.id,
58823
+ topic_name
58824
+ ].filter((v) => typeof v === "string").join(" ").toLowerCase().includes(needle);
58825
+ };
58826
+ for (let node of model.nodes.values()) if (matches(node.topic_name, node.record || {})) {
58827
+ if (visible.has(node.key)) found.keys.push(node.key);
58828
+ else found.folded++;
58775
58829
  }
58776
- priv._find_hidden_matches = hidden_matches;
58777
- let visible = fold_visible_set(model, state);
58778
- let revealed = 0;
58779
- for (let key of keys) {
58780
- if (visible.has(key)) continue;
58781
- if (revealed >= state.page_size) break;
58782
- fold_reveal(model, state, key);
58783
- revealed++;
58830
+ for (let topic_name of model.hidden) for (let record of priv.records[topic_name] || []) if (is_object(record) && matches(topic_name, record)) found.hidden++;
58831
+ return found;
58832
+ }
58833
+ /************************************************************
58834
+ * The lit cards in reading order: top to bottom, then left to
58835
+ * right. The order Enter walks, and the one the "k/N" counts in.
58836
+ ************************************************************/
58837
+ function find_order(gobj) {
58838
+ let priv = gobj.priv;
58839
+ let graph = priv.graph;
58840
+ let ids = (priv._focus_ids || []).slice();
58841
+ if (!graph) return ids;
58842
+ let pos = {};
58843
+ for (let id of ids) {
58844
+ let p = null;
58845
+ try {
58846
+ p = graph.getElementPosition(id);
58847
+ } catch (e) {
58848
+ p = null;
58849
+ }
58850
+ pos[id] = p || [0, 0];
58784
58851
  }
58785
- priv._focus_topic = null;
58786
- reconcile_fold(gobj, {}).then(() => {
58787
- apply_focus_ids(gobj, keys, "find");
58788
- });
58789
- return keys.length;
58852
+ ids.sort((a, b) => pos[a][1] - pos[b][1] || pos[a][0] - pos[b][0]);
58853
+ return ids;
58854
+ }
58855
+ /************************************************************
58856
+ * Enter in the find box: centre the next lit card (`back`: the
58857
+ * previous one). The one camera move of the find, and the reader
58858
+ * asks for it: the highlight alone cannot take anybody to a match
58859
+ * that is outside the frame.
58860
+ ************************************************************/
58861
+ function graph_find_step(gobj, back) {
58862
+ let priv = gobj.priv;
58863
+ let graph = priv.graph;
58864
+ if (!graph || !priv._find_term) return;
58865
+ let ids = find_order(gobj);
58866
+ if (ids.length === 0) return;
58867
+ let i = ids.indexOf(priv._find_current);
58868
+ if (i < 0) i = back ? ids.length - 1 : 0;
58869
+ else i = (i + (back ? ids.length - 1 : 1)) % ids.length;
58870
+ priv._find_current = ids[i];
58871
+ try {
58872
+ yui_graph_center_on(graph, ids[i]);
58873
+ } catch (e) {
58874
+ log_error(`${gobj_short_name(gobj)}: find step failed: ${e}`);
58875
+ }
58876
+ publish_find_result(gobj);
58790
58877
  }
58791
58878
  async function graph_zoom_in(gobj) {
58792
58879
  let graph = gobj.priv.graph;
@@ -62678,6 +62765,7 @@ async function reconcile_fold_now(gobj, opts) {
62678
62765
  history_resume(gobj);
62679
62766
  }
62680
62767
  publish_legend_state(gobj, visible);
62768
+ refresh_find(gobj, visible);
62681
62769
  return changed;
62682
62770
  }
62683
62771
  /************************************************************
@@ -63059,11 +63147,6 @@ function ac_load_data(gobj, event, kw, src) {
63059
63147
  priv._pending_focus_all = false;
63060
63148
  graph_focus_topic(gobj, ft, all);
63061
63149
  }
63062
- if (priv._pending_find !== null) {
63063
- let term = priv._pending_find;
63064
- priv._pending_find = null;
63065
- publish_find_result(gobj, term, graph_find_nodes(gobj, term));
63066
- }
63067
63150
  });
63068
63151
  }
63069
63152
  return 0;
@@ -63632,23 +63715,45 @@ function ac_node_dblclick(gobj, event, kw, src) {
63632
63715
  return 0;
63633
63716
  }
63634
63717
  /************************************************************
63635
- * Tell whoever asked how many nodes the term matched. The count is
63636
- * the answer to the question the box asks; without it "nothing moved"
63637
- * and "nothing matched" look the same.
63718
+ * Tell whoever asked how many cards the term lit. The count is the
63719
+ * answer to the question the box asks; without it "nothing lit" and
63720
+ * "nothing matched" look the same.
63721
+ *
63722
+ * matches lit cards (on screen)
63723
+ * folded_matches matches folded away, not on screen
63724
+ * hidden_matches matches in hidden topics
63725
+ * current 1-based place of the card Enter centred on
63726
+ * among the lit ones (reading order), 0 = none
63638
63727
  ************************************************************/
63639
- function publish_find_result(gobj, term, matches) {
63728
+ function publish_find_result(gobj) {
63729
+ let priv = gobj.priv;
63730
+ let found = priv._find_found || {
63731
+ keys: [],
63732
+ folded: 0,
63733
+ hidden: 0
63734
+ };
63735
+ let current = 0;
63736
+ if (priv._find_term && priv._find_current) current = find_order(gobj).indexOf(priv._find_current) + 1;
63640
63737
  gobj_publish_event(gobj, "EV_FIND_RESULT", {
63641
- term,
63642
- matches,
63643
- hidden_matches: gobj.priv._find_hidden_matches || 0
63738
+ term: priv._find_term,
63739
+ matches: priv._find_term ? (priv._focus_ids || []).length : 0,
63740
+ folded_matches: found.folded,
63741
+ hidden_matches: found.hidden,
63742
+ current
63644
63743
  });
63645
63744
  }
63646
63745
  /************************************************************
63647
63746
  *
63648
63747
  ************************************************************/
63649
63748
  function ac_find_nodes$1(gobj, event, kw, src) {
63650
- let term = kw && kw.text || "";
63651
- publish_find_result(gobj, term, graph_find_nodes(gobj, term));
63749
+ graph_find_nodes(gobj, kw && kw.text || "");
63750
+ return 0;
63751
+ }
63752
+ /************************************************************
63753
+ * Enter in the find box: {back: true} for Shift+Enter.
63754
+ ************************************************************/
63755
+ function ac_find_next$1(gobj, event, kw, src) {
63756
+ graph_find_step(gobj, !!(kw && kw.back));
63652
63757
  return 0;
63653
63758
  }
63654
63759
  function ac_center(gobj, event, kw, src) {
@@ -64109,6 +64214,11 @@ function create_gclass$4(gclass_name) {
64109
64214
  ac_find_nodes$1,
64110
64215
  null
64111
64216
  ],
64217
+ [
64218
+ "EV_FIND_NEXT",
64219
+ ac_find_next$1,
64220
+ null
64221
+ ],
64112
64222
  [
64113
64223
  "EV_TOGGLE_FOLD",
64114
64224
  ac_toggle_fold,
@@ -64264,6 +64374,7 @@ function create_gclass$4(gclass_name) {
64264
64374
  ["EV_ZOOM_SELECTION", 0],
64265
64375
  ["EV_FOCUS_TOPIC", 0],
64266
64376
  ["EV_FIND_NODES", 0],
64377
+ ["EV_FIND_NEXT", 0],
64267
64378
  ["EV_FIND_RESULT", event_flag_t.EVF_OUTPUT_EVENT],
64268
64379
  ["EV_LAYOUT_AUTOSET", event_flag_t.EVF_OUTPUT_EVENT],
64269
64380
  ["EV_CAMERA_CHANGED", event_flag_t.EVF_OUTPUT_EVENT | event_flag_t.EVF_NO_WARN_SUBS],
@@ -65056,19 +65167,34 @@ function make_toolbar(gobj) {
65056
65167
  type: "text",
65057
65168
  placeholder: t("search"),
65058
65169
  "data-i18n-placeholder": "search",
65059
- "aria-label": t("search"),
65060
- "data-i18n-aria-label": "search"
65170
+ title: t("find on screen"),
65171
+ "data-i18n-title": "find on screen",
65172
+ "aria-label": t("find on screen"),
65173
+ "data-i18n-aria-label": "find on screen"
65061
65174
  },
65062
65175
  [],
65063
- { input: (evt) => {
65064
- evt.stopPropagation();
65065
- let text = evt.target.value.trim();
65066
- if (priv.find_timer) clearTimeout(priv.find_timer);
65067
- priv.find_timer = setTimeout(function() {
65068
- priv.find_timer = null;
65069
- gobj_send_event(gobj, "EV_FIND_NODES", { text }, gobj);
65070
- }, 250);
65071
- } }
65176
+ {
65177
+ input: (evt) => {
65178
+ evt.stopPropagation();
65179
+ let text = evt.target.value.trim();
65180
+ if (priv.find_timer) clearTimeout(priv.find_timer);
65181
+ priv.find_timer = setTimeout(function() {
65182
+ priv.find_timer = null;
65183
+ gobj_send_event(gobj, "EV_FIND_NODES", { text }, gobj);
65184
+ }, 250);
65185
+ },
65186
+ keydown: (evt) => {
65187
+ if (evt.key !== "Enter" || evt.isComposing) return;
65188
+ evt.preventDefault();
65189
+ evt.stopPropagation();
65190
+ if (priv.find_timer) {
65191
+ clearTimeout(priv.find_timer);
65192
+ priv.find_timer = null;
65193
+ gobj_send_event(gobj, "EV_FIND_NODES", { text: evt.target.value.trim() }, gobj);
65194
+ }
65195
+ gobj_send_event(gobj, "EV_FIND_NEXT", { back: evt.shiftKey }, gobj);
65196
+ }
65197
+ }
65072
65198
  ]);
65073
65199
  let $find_control = createElement2([
65074
65200
  "div",
@@ -66397,6 +66523,14 @@ function ac_find_nodes(gobj, event, kw, src) {
66397
66523
  return 0;
66398
66524
  }
66399
66525
  /************************************************************
66526
+ * Enter in the find box: forward the step down to the graph child.
66527
+ ************************************************************/
66528
+ function ac_find_next(gobj, event, kw, src) {
66529
+ let priv = gobj.priv;
66530
+ if (priv.gobj_nodes_tree) gobj_send_event(priv.gobj_nodes_tree, "EV_FIND_NEXT", { back: !!(kw && kw.back) }, gobj);
66531
+ return 0;
66532
+ }
66533
+ /************************************************************
66400
66534
  * One press of the fold stepper (EV_EXPAND_LEVEL /
66401
66535
  * EV_COLLAPSE_LEVEL): the level moves by one, is SAVED (the
66402
66536
  * reader's, per treedb) and reaches the G6 child, which owns
@@ -66480,12 +66614,33 @@ function refresh_find_result(gobj) {
66480
66614
  return 0;
66481
66615
  }
66482
66616
  let text = t("matches", { count: kw && kw.matches || 0 });
66617
+ let current = kw && kw.current || 0;
66618
+ if (current > 0) text = `${current}/${text}`;
66619
+ let extra = [];
66620
+ let folded = kw && kw.folded_matches || 0;
66621
+ if (folded > 0) extra.push(`+${folded} ${t("not shown")}`);
66483
66622
  let hidden = kw && kw.hidden_matches || 0;
66484
- if (hidden > 0) text += ` (+${hidden} ${t("hidden topics")})`;
66623
+ if (hidden > 0) extra.push(`+${hidden} ${t("hidden topics")}`);
66624
+ if (extra.length) text += ` (${extra.join(", ")})`;
66485
66625
  $count.textContent = text;
66486
66626
  $result.classList.remove("is-hidden");
66487
66627
  return 0;
66488
66628
  }
66629
+ function clear_find_box(gobj) {
66630
+ let priv = gobj.priv;
66631
+ if (priv.find_timer) {
66632
+ clearTimeout(priv.find_timer);
66633
+ priv.find_timer = null;
66634
+ }
66635
+ let $container = gobj_read_attr(gobj, "$container");
66636
+ let $input = $container ? $container.querySelector(".GRAPH_FIND_INPUT") : null;
66637
+ if ($input) {
66638
+ $input.value = "";
66639
+ refresh_clear($input);
66640
+ }
66641
+ priv._find_result = null;
66642
+ refresh_find_result(gobj);
66643
+ }
66489
66644
  function ac_set_focus_topic(gobj, event, kw, src) {
66490
66645
  let priv = gobj.priv;
66491
66646
  priv.focus_topic = kw && kw.topic || "";
@@ -66496,6 +66651,7 @@ function ac_set_focus_topic(gobj, event, kw, src) {
66496
66651
  set_legend_list_attr(gobj, "hidden_topics", "EV_SET_HIDDEN_TOPICS", hidden);
66497
66652
  }
66498
66653
  }
66654
+ if (priv.focus_topic) clear_find_box(gobj);
66499
66655
  if (priv.gobj_nodes_tree) gobj_send_event(priv.gobj_nodes_tree, "EV_FOCUS_TOPIC", {
66500
66656
  topic: priv.focus_topic,
66501
66657
  reveal: kw && kw.reveal || "page"
@@ -66673,6 +66829,11 @@ function create_gclass$3(gclass_name) {
66673
66829
  ac_find_nodes,
66674
66830
  null
66675
66831
  ],
66832
+ [
66833
+ "EV_FIND_NEXT",
66834
+ ac_find_next,
66835
+ null
66836
+ ],
66676
66837
  [
66677
66838
  "EV_EXPAND_LEVEL",
66678
66839
  ac_fold_level_step,
@@ -66771,6 +66932,7 @@ function create_gclass$3(gclass_name) {
66771
66932
  ["EV_SET_OPERATION_MODE", 0],
66772
66933
  ["EV_SET_FOCUS_TOPIC", 0],
66773
66934
  ["EV_FIND_NODES", 0],
66935
+ ["EV_FIND_NEXT", 0],
66774
66936
  ["EV_EXPAND_LEVEL", 0],
66775
66937
  ["EV_COLLAPSE_LEVEL", 0],
66776
66938
  ["EV_SET_NODE_MODE", 0],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuneta/gobj-ui",
3
- "version": "7.23.157",
3
+ "version": "7.23.158",
4
4
  "type": "module",
5
5
  "main": "dist/gobj-ui.cjs.js",
6
6
  "module": "dist/gobj-ui.es.js",