@yuneta/gobj-ui 7.0.1 → 7.2.0

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.
@@ -11017,7 +11017,7 @@ function ac_expand_collapsed(gobj, event, kw, src) {
11017
11017
  /************************************************************
11018
11018
  * EV_SEARCH { text }
11019
11019
  ************************************************************/
11020
- function ac_search(gobj, event, kw, src) {
11020
+ function ac_search$1(gobj, event, kw, src) {
11021
11021
  let priv = gobj.priv;
11022
11022
  priv.search = (kw.text || "").trim().toLowerCase();
11023
11023
  render_tree(gobj);
@@ -11166,7 +11166,7 @@ function create_gclass$10(gclass_name) {
11166
11166
  ],
11167
11167
  [
11168
11168
  "EV_SEARCH",
11169
- ac_search,
11169
+ ac_search$1,
11170
11170
  null
11171
11171
  ],
11172
11172
  [
@@ -16222,6 +16222,9 @@ var attrs_table$5 = [
16222
16222
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_refresh_button", 0, true, "Button toolbar REFRESH"),
16223
16223
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_search_button", 0, true, "Button toolbar SEARCH"),
16224
16224
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_schema_button", 0, true, "Button toolbar SCHEMA (show the topic's desc)"),
16225
+ (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_columns_button", 0, true, "Button toolbar COLUMNS (choose which columns the table shows)"),
16226
+ (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_export_button", 0, true, "Button toolbar EXPORT (download as CSV what the table holds)"),
16227
+ (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_header_filters", 0, true, "Per-column filter box in the table header, on the columns a text/number match means something (not hooks, fkeys or json)"),
16225
16228
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_in_row_edit_icons", 0, true, "Add a last column with internal EDIT/DELETE icon"),
16226
16229
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "editable", 0, false, "Edit state"),
16227
16230
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_checkbox", 0, true, "Auxiliary first column to select rows"),
@@ -16514,6 +16517,8 @@ function build_ui$5(gobj) {
16514
16517
  let with_refresh_button = (0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "with_refresh_button");
16515
16518
  let with_search_button = (0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "with_search_button");
16516
16519
  let with_schema_button = (0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "with_schema_button");
16520
+ let with_columns_button = (0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "with_columns_button");
16521
+ let with_export_button = (0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "with_export_button");
16517
16522
  if (with_search_button) {
16518
16523
  let search_id = `${toolbar_id}_search`;
16519
16524
  let $search_box = (0, _yuneta_gobj_js.createElement2)([
@@ -16534,17 +16539,7 @@ function build_ui$5(gobj) {
16534
16539
  let $search_input = $search_box.querySelector(`#${search_id}`);
16535
16540
  attach_clear($search_box, $search_input);
16536
16541
  if ($search_input) $search_input.addEventListener("input", (event) => {
16537
- let searchText = event.target.value.trim().toLowerCase();
16538
- let tabulator = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "tabulator");
16539
- if (!tabulator) return;
16540
- if (searchText) tabulator.setFilter(function(data) {
16541
- return Object.entries(data).some(([key, val]) => {
16542
- if (key.startsWith("_")) return false;
16543
- if (val === null || val === void 0) return false;
16544
- return String(val).toLowerCase().includes(searchText);
16545
- });
16546
- });
16547
- else tabulator.clearFilter();
16542
+ (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_SEARCH", { text: event.target.value.trim() }, gobj);
16548
16543
  });
16549
16544
  $view_toolbar.appendChild($search_box);
16550
16545
  }
@@ -16598,6 +16593,58 @@ function build_ui$5(gobj) {
16598
16593
  ]);
16599
16594
  $view_toolbar.appendChild($schema);
16600
16595
  }
16596
+ if (with_columns_button) {
16597
+ let $columns = (0, _yuneta_gobj_js.createElement2)([
16598
+ "button",
16599
+ {
16600
+ class: "TREEDB_TABLE_COLUMNS button mr-1",
16601
+ title: (0, i18next.t)("choose the columns to show"),
16602
+ "data-i18n-title": "choose the columns to show",
16603
+ "aria-label": (0, i18next.t)("columns"),
16604
+ "data-i18n-aria-label": "columns"
16605
+ },
16606
+ [["i", { class: "yi-table" }], [
16607
+ "span",
16608
+ {
16609
+ class: "is-hidden-mobile",
16610
+ i18n: "columns",
16611
+ style: "padding-left:5px;"
16612
+ },
16613
+ "columns"
16614
+ ]],
16615
+ { "click": (event) => {
16616
+ event.stopPropagation();
16617
+ (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_OPEN_COLUMNS", {}, gobj);
16618
+ } }
16619
+ ]);
16620
+ $view_toolbar.appendChild($columns);
16621
+ }
16622
+ if (with_export_button) {
16623
+ let $export = (0, _yuneta_gobj_js.createElement2)([
16624
+ "button",
16625
+ {
16626
+ class: "TREEDB_TABLE_EXPORT button mr-1",
16627
+ title: (0, i18next.t)("download the rows loaded in this table as csv"),
16628
+ "data-i18n-title": "download the rows loaded in this table as csv",
16629
+ "aria-label": (0, i18next.t)("export"),
16630
+ "data-i18n-aria-label": "export"
16631
+ },
16632
+ [["i", { class: "yi-download" }], [
16633
+ "span",
16634
+ {
16635
+ class: "is-hidden-mobile",
16636
+ i18n: "export",
16637
+ style: "padding-left:5px;"
16638
+ },
16639
+ "export"
16640
+ ]],
16641
+ { "click": (event) => {
16642
+ event.stopPropagation();
16643
+ (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_EXPORT_TABLE", {}, gobj);
16644
+ } }
16645
+ ]);
16646
+ $view_toolbar.appendChild($export);
16647
+ }
16601
16648
  let table_id = (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "table_id");
16602
16649
  let $container = (0, _yuneta_gobj_js.createElement2)([
16603
16650
  "div",
@@ -16686,6 +16733,7 @@ function col_title_formatter(gobj, col) {
16686
16733
  function create_tabulator(gobj) {
16687
16734
  let table_id = (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "table_id");
16688
16735
  let desc = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "desc");
16736
+ let with_header_filters = (0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "with_header_filters");
16689
16737
  let columns = [];
16690
16738
  let with_checkbox = (0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "with_checkbox");
16691
16739
  let with_radio = (0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "with_radio");
@@ -16729,6 +16777,46 @@ function create_tabulator(gobj) {
16729
16777
  col_id: cell.getField()
16730
16778
  }, gobj);
16731
16779
  }
16780
+ const FILTERABLE = [
16781
+ "string",
16782
+ "integer",
16783
+ "real",
16784
+ "rowid",
16785
+ "uuid",
16786
+ "qualified",
16787
+ "email",
16788
+ "url",
16789
+ "tel",
16790
+ "id",
16791
+ "hex",
16792
+ "currency",
16793
+ "percent"
16794
+ ];
16795
+ function fkey_matches(term, value) {
16796
+ if (term === "" || term === null || term === void 0) return true;
16797
+ if (value === null || value === void 0) return false;
16798
+ return (typeof value === "string" ? value : JSON.stringify(value)).toLowerCase().includes(String(term).toLowerCase());
16799
+ }
16800
+ function apply_header_filter(colDef, field_desc) {
16801
+ switch (field_desc.type) {
16802
+ case "boolean":
16803
+ colDef.headerFilter = "tickCross";
16804
+ colDef.headerFilterParams = { tristate: true };
16805
+ break;
16806
+ case "enum":
16807
+ colDef.headerFilter = "list";
16808
+ colDef.headerFilterParams = {
16809
+ values: field_desc.enum_list || [],
16810
+ clearable: true
16811
+ };
16812
+ break;
16813
+ case "fkey":
16814
+ colDef.headerFilter = "input";
16815
+ colDef.headerFilterFunc = fkey_matches;
16816
+ break;
16817
+ default: if (FILTERABLE.includes(field_desc.type)) colDef.headerFilter = "input";
16818
+ }
16819
+ }
16732
16820
  for (let i = 0; i < desc.cols.length; i++) {
16733
16821
  let col = desc.cols[i];
16734
16822
  if (!col.id || col.id[0] === "_") continue;
@@ -16809,6 +16897,7 @@ function create_tabulator(gobj) {
16809
16897
  hozAlign,
16810
16898
  formatter: colFormatter
16811
16899
  };
16900
+ if (with_header_filters) apply_header_filter(colDef, field_desc);
16812
16901
  if (vertAlign) colDef.vertAlign = vertAlign;
16813
16902
  if (formatterParams) colDef.formatterParams = formatterParams;
16814
16903
  if (cellClick) colDef.cellClick = cellClick;
@@ -18018,6 +18107,125 @@ function ac_show_schema(gobj, event, kw, src) {
18018
18107
  return 0;
18019
18108
  }
18020
18109
  /************************************************************
18110
+ * Filter the loaded rows by a term matched against every non-internal
18111
+ * field. `clearFilter()` with no argument drops only THIS filter: the
18112
+ * per-column header filters are a separate layer and survive, so a
18113
+ * cleared search box does not silently undo them.
18114
+ ************************************************************/
18115
+ function ac_search(gobj, event, kw, src) {
18116
+ let tabulator = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "tabulator");
18117
+ if (!tabulator) {
18118
+ (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: no table to search`);
18119
+ return -1;
18120
+ }
18121
+ let term = (kw && kw.text || "").toLowerCase();
18122
+ if (!term) {
18123
+ tabulator.clearFilter();
18124
+ return 0;
18125
+ }
18126
+ tabulator.setFilter(function(data) {
18127
+ return Object.entries(data).some(([key, val]) => {
18128
+ if (key.startsWith("_")) return false;
18129
+ if (val === null || val === void 0) return false;
18130
+ return String(val).toLowerCase().includes(term);
18131
+ });
18132
+ });
18133
+ return 0;
18134
+ }
18135
+ /************************************************************
18136
+ * Which columns the table shows: one checkbox per column, the current
18137
+ * visibility ticked. The internal columns (`_check_box_state_`,
18138
+ * `_operation`) are not offered — they are chrome, not data.
18139
+ ************************************************************/
18140
+ function ac_open_columns(gobj, event, kw, src) {
18141
+ let tabulator = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "tabulator");
18142
+ if (!tabulator) {
18143
+ (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: no table, no columns to choose`);
18144
+ return -1;
18145
+ }
18146
+ let shell = yui_shell_of(gobj);
18147
+ if (!shell) {
18148
+ (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: no shell, cannot open the column chooser`);
18149
+ return -1;
18150
+ }
18151
+ let $list = (0, _yuneta_gobj_js.createElement2)(["div", { class: "TREEDB_COLUMNS_LIST" }]);
18152
+ let columns = tabulator.getColumns();
18153
+ for (let i = 0; i < columns.length; i++) {
18154
+ let column = columns[i];
18155
+ let field = column.getField();
18156
+ if (!field || field[0] === "_") continue;
18157
+ let $cb = (0, _yuneta_gobj_js.createElement2)(["input", {
18158
+ type: "checkbox",
18159
+ class: "mr-2"
18160
+ }]);
18161
+ $cb.checked = column.isVisible();
18162
+ $cb.addEventListener("change", () => {
18163
+ (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_TOGGLE_COLUMN", {
18164
+ field,
18165
+ visible: $cb.checked
18166
+ }, gobj);
18167
+ });
18168
+ $list.appendChild((0, _yuneta_gobj_js.createElement2)([
18169
+ "label",
18170
+ { class: "checkbox is-block mb-1 TREEDB_COLUMN_ITEM" },
18171
+ [$cb, [
18172
+ "span",
18173
+ { class: "ml-2" },
18174
+ column.getDefinition().title || field
18175
+ ]]
18176
+ ]));
18177
+ }
18178
+ yui_shell_show_modal(shell, $list, {
18179
+ dialog: true,
18180
+ logical_class: "TREEDB_COLUMNS_DIALOG",
18181
+ title_prefix: (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "topic_name"),
18182
+ title: "columns",
18183
+ t: i18next.t
18184
+ });
18185
+ return 0;
18186
+ }
18187
+ /************************************************************
18188
+ * Show / hide one column.
18189
+ ************************************************************/
18190
+ function ac_toggle_column(gobj, event, kw, src) {
18191
+ let tabulator = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "tabulator");
18192
+ let field = kw && kw.field || "";
18193
+ if (!tabulator || !field) {
18194
+ (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: no column '${field}' to toggle`);
18195
+ return -1;
18196
+ }
18197
+ try {
18198
+ if (kw.visible) tabulator.showColumn(field);
18199
+ else tabulator.hideColumn(field);
18200
+ } catch (e) {
18201
+ (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: cannot toggle column '${field}': ${e}`);
18202
+ return -1;
18203
+ }
18204
+ return 0;
18205
+ }
18206
+ /************************************************************
18207
+ * Download what the table HOLDS as CSV: the loaded rows, the visible
18208
+ * columns, search and header filters applied — which is what the reader
18209
+ * is looking at. Not the topic: a server-side dump of every node is not
18210
+ * something this view can stream.
18211
+ ************************************************************/
18212
+ function ac_export_table(gobj, event, kw, src) {
18213
+ let tabulator = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "tabulator");
18214
+ if (!tabulator) {
18215
+ (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: no table to export`);
18216
+ return -1;
18217
+ }
18218
+ let name = `${(0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "treedb_name")}-${(0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "topic_name")}.csv`;
18219
+ name = name.replace(/[^\w.\-]+/g, "_");
18220
+ try {
18221
+ tabulator.download("csv", name);
18222
+ } catch (e) {
18223
+ (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: CSV export failed: ${e}`);
18224
+ return -1;
18225
+ }
18226
+ return 0;
18227
+ }
18228
+ /************************************************************
18021
18229
  * {
18022
18230
  * href: href
18023
18231
  * }
@@ -18141,6 +18349,26 @@ function create_gclass$5(gclass_name) {
18141
18349
  ac_show_schema,
18142
18350
  null
18143
18351
  ],
18352
+ [
18353
+ "EV_SEARCH",
18354
+ ac_search,
18355
+ null
18356
+ ],
18357
+ [
18358
+ "EV_OPEN_COLUMNS",
18359
+ ac_open_columns,
18360
+ null
18361
+ ],
18362
+ [
18363
+ "EV_TOGGLE_COLUMN",
18364
+ ac_toggle_column,
18365
+ null
18366
+ ],
18367
+ [
18368
+ "EV_EXPORT_TABLE",
18369
+ ac_export_table,
18370
+ null
18371
+ ],
18144
18372
  [
18145
18373
  "EV_SHOW",
18146
18374
  ac_show$4,
@@ -18171,6 +18399,10 @@ function create_gclass$5(gclass_name) {
18171
18399
  ["EV_CHANGE_LOCALE", 0],
18172
18400
  ["EV_REFRESH", 0],
18173
18401
  ["EV_SHOW_SCHEMA", 0],
18402
+ ["EV_SEARCH", 0],
18403
+ ["EV_OPEN_COLUMNS", 0],
18404
+ ["EV_TOGGLE_COLUMN", 0],
18405
+ ["EV_EXPORT_TABLE", 0],
18174
18406
  ["EV_CREATE_RECORD", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT],
18175
18407
  ["EV_UPDATE_RECORD", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT],
18176
18408
  ["EV_DELETE_RECORD", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT],
@@ -18540,101 +18772,140 @@ function make_toolbar(gobj) {
18540
18772
  gobj.priv;
18541
18773
  let modes = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "operation_modes");
18542
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 is-flex is-align-items-center",
18887
+ style: "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
+ ]];
18543
18899
  const $toolbar_header = yui_toolbar({}, [
18544
18900
  [
18545
18901
  "div",
18546
18902
  { class: "yui-horizontal-toolbar-section left" },
18547
- [
18548
- [
18549
- "span",
18550
- {
18551
- class: "GRAPH_LAYOUT_LABEL is-hidden-mobile",
18552
- style: "padding-right:5px;",
18553
- i18n: "layout"
18554
- },
18555
- "layout"
18556
- ],
18557
- [
18558
- "div",
18559
- { class: "select" },
18560
- [["select", { class: "GRAPH_LAYOUT_SELECT" }]],
18561
- { change: (evt) => {
18562
- evt.stopPropagation();
18563
- (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_SET_LAYOUT", { layout: evt.target.value }, gobj);
18564
- } }
18565
- ],
18566
- [
18567
- "span",
18568
- {
18569
- class: "GRAPH_MODE_LABEL is-hidden-mobile",
18570
- style: "padding-left:10px; padding-right:5px;",
18571
- i18n: "operation mode"
18572
- },
18573
- "operation mode"
18574
- ],
18575
- [
18576
- "div",
18577
- { class: "select" },
18578
- [[
18579
- "select",
18580
- { class: "GRAPH_MODE_SELECT" },
18581
- modes.map((item) => [
18582
- "option",
18583
- {},
18584
- item
18585
- ])
18586
- ]],
18587
- { change: (evt) => {
18588
- evt.stopPropagation();
18589
- (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_SET_OPERATION_MODE", { operation_mode: evt.target.value }, gobj);
18590
- } }
18591
- ],
18592
- [
18593
- "button",
18594
- { class: "GRAPH_REFRESH button" },
18595
- [["i", { class: "yi-arrows-rotate" }], [
18596
- "span",
18597
- {
18598
- class: "is-hidden-mobile",
18599
- style: "padding-left:5px;",
18600
- i18n: "refresh"
18601
- },
18602
- "refresh"
18603
- ]],
18604
- { click: (evt) => {
18605
- evt.stopPropagation();
18606
- (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_REFRESH_TREEDB", { evt }, gobj);
18607
- } }
18608
- ],
18609
- [
18610
- "button",
18611
- {
18612
- class: "button ml-2 TREEDB_JSON_BTN",
18613
- title: (0, i18next.t)("raw json"),
18614
- "aria-label": (0, i18next.t)("raw json"),
18615
- "data-i18n-title": "raw json",
18616
- "data-i18n-aria-label": "raw json"
18617
- },
18618
- [["i", { class: "yi-eye" }], [
18619
- "span",
18620
- {
18621
- class: "is-hidden-mobile",
18622
- style: "padding-left:5px;",
18623
- i18n: "raw json"
18624
- },
18625
- "raw json"
18626
- ]],
18627
- { click: (evt) => {
18628
- evt.stopPropagation();
18629
- (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_OPEN_JSON", {}, gobj);
18630
- } }
18631
- ]
18632
- ]
18903
+ left_items
18633
18904
  ],
18634
18905
  [
18635
18906
  "div",
18636
18907
  { class: "yui-horizontal-toolbar-section center" },
18637
- []
18908
+ center_items
18638
18909
  ],
18639
18910
  [
18640
18911
  "div",
@@ -19566,6 +19837,33 @@ function ac_set_operation_mode$1(gobj, event, kw, src) {
19566
19837
  * tree, which centres + highlights that topic's nodes (deferring
19567
19838
  * until its data is loaded).
19568
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
+ }
19569
19867
  function ac_set_focus_topic(gobj, event, kw, src) {
19570
19868
  let priv = gobj.priv;
19571
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);
@@ -19731,6 +20029,16 @@ function create_gclass$4(gclass_name) {
19731
20029
  ac_set_focus_topic,
19732
20030
  null
19733
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
+ ],
19734
20042
  [
19735
20043
  "EV_SHOW",
19736
20044
  ac_show$3,
@@ -19772,6 +20080,8 @@ function create_gclass$4(gclass_name) {
19772
20080
  ["EV_SET_LAYOUT", 0],
19773
20081
  ["EV_SET_OPERATION_MODE", 0],
19774
20082
  ["EV_SET_FOCUS_TOPIC", 0],
20083
+ ["EV_FIND_NODES", 0],
20084
+ ["EV_FIND_RESULT", 0],
19775
20085
  ["EV_OPERATION_MODE_CHANGED", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT | _yuneta_gobj_js.event_flag_t.EVF_NO_WARN_SUBS],
19776
20086
  ["EV_RECORD_WRITTEN", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT | _yuneta_gobj_js.event_flag_t.EVF_NO_WARN_SUBS],
19777
20087
  ["EV_SHOW", 0],
@@ -52437,7 +52747,8 @@ var PRIVATE_DATA$1 = {
52437
52747
  _link_saved_styles: [],
52438
52748
  _focus_topic: null,
52439
52749
  _focus_ids: [],
52440
- _pending_focus_topic: null
52750
+ _pending_focus_topic: null,
52751
+ _pending_find: null
52441
52752
  };
52442
52753
  var __gclass__$1 = null;
52443
52754
  /******************************
@@ -53611,6 +53922,67 @@ function graph_focus_topic(gobj, topic) {
53611
53922
  (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: focus topic apply failed: ${e}`);
53612
53923
  }
53613
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
+ }
53614
53986
  async function graph_zoom_in(gobj) {
53615
53987
  let graph = gobj.priv.graph;
53616
53988
  const z = graph.getZoom();
@@ -55905,6 +56277,11 @@ function ac_load_data(gobj, event, kw, src) {
55905
56277
  priv._pending_focus_topic = null;
55906
56278
  graph_focus_topic(gobj, ft);
55907
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
+ }
55908
56285
  });
55909
56286
  });
55910
56287
  });
@@ -56121,6 +56498,25 @@ function ac_focus_topic(gobj, event, kw, src) {
56121
56498
  graph_focus_topic(gobj, kw && kw.topic);
56122
56499
  return 0;
56123
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
+ }
56124
56520
  function ac_center(gobj, event, kw, src) {
56125
56521
  graph_center(gobj);
56126
56522
  return 0;
@@ -56422,6 +56818,11 @@ function create_gclass$1(gclass_name) {
56422
56818
  ac_focus_topic,
56423
56819
  null
56424
56820
  ],
56821
+ [
56822
+ "EV_FIND_NODES",
56823
+ ac_find_nodes,
56824
+ null
56825
+ ],
56425
56826
  [
56426
56827
  "EV_CENTER",
56427
56828
  ac_center,
@@ -56505,6 +56906,8 @@ function create_gclass$1(gclass_name) {
56505
56906
  ["EV_ZOOM_RESET", 0],
56506
56907
  ["EV_AUTO_FIT", 0],
56507
56908
  ["EV_FOCUS_TOPIC", 0],
56909
+ ["EV_FIND_NODES", 0],
56910
+ ["EV_FIND_RESULT", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT],
56508
56911
  ["EV_CENTER", 0],
56509
56912
  ["EV_FULLSCREEN", 0],
56510
56913
  ["EV_SET_LAYOUT", 0],