@yuneta/gobj-ui 7.23.161 → 7.23.163

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.
@@ -1,5 +1,5 @@
1
1
  import * as gobj_js from "@yuneta/gobj-js";
2
- import { SDATA, SDATA_END, clean_name, createElement2, createOneHtml, create_template_record, data_type_t, debounce, delete_from_list, duplicate_objects, empty_json, empty_string, escapeHtml, event_flag_t, flat_diff, gclass_create, gclass_find_by_name, gclass_flag_t, getPositionRelativeToBody, gobj_bottom_gobj, gobj_change_state, gobj_command, gobj_create, gobj_create_pure_child, gobj_create_service, gobj_current_state, gobj_destroy, gobj_find_child, gobj_find_service, gobj_full_name, gobj_gclass_name, gobj_is_destroying, gobj_is_playing, gobj_is_pure_child, gobj_is_running, gobj_is_service, gobj_is_volatil, gobj_match_children, gobj_name, gobj_parent, gobj_post_event, gobj_publish_event, gobj_read_attr, gobj_read_bool_attr, gobj_read_integer_attr, gobj_read_pointer_attr, gobj_read_str_attr, gobj_save_persistent_attrs, gobj_send_event, gobj_set_trace_machine_format, gobj_short_name, gobj_start, gobj_start_tree, gobj_stop, gobj_stop_children, gobj_subscribe_event, gobj_unsubscribe_event, gobj_write_attr, gobj_write_bool_attr, gobj_write_integer_attr, gobj_write_str_attr, gobj_yuno, gobj_yuno_name, is_array, is_boolean, is_date, is_gobj, is_null, is_number, is_object, is_string, json2flat, json_deep_copy, json_object_size, json_object_update, json_object_update_missing, json_size, kw_clone_by_keys, kw_flag_t, kw_get_dict, kw_get_dict_value, kw_get_int, kw_get_local_storage_value, kw_get_str, kw_has_key, kw_set_dict_value, kw_set_local_storage_value, kwid_find_one_record, kwid_get_ids, kwid_new_dict, log_error, log_warning, msg_iev_get_stack, parseBoolean, parseSVG, refresh_language, safeSrc, sdata_flag_t, set_console_log_enabled, set_log_callback, sprintf, str_in_list, template_get_field_desc, trace_msg, treedb_decoder_fkey, treedb_encoder_fkey, treedb_get_field_desc, treedb_hook_data_size } from "@yuneta/gobj-js";
2
+ import { SDATA, SDATA_END, clean_name, createElement2, createOneHtml, create_template_record, data_type_t, debounce, delete_from_list, duplicate_objects, empty_json, empty_string, escapeHtml, event_flag_t, flat_diff, gclass_create, gclass_find_by_name, gclass_flag_t, getPositionRelativeToBody, gobj_bottom_gobj, gobj_change_state, gobj_command, gobj_create, gobj_create_pure_child, gobj_create_service, gobj_current_state, gobj_destroy, gobj_find_child, gobj_find_service, gobj_full_name, gobj_gclass_name, gobj_is_destroying, gobj_is_playing, gobj_is_pure_child, gobj_is_running, gobj_is_service, gobj_is_volatil, gobj_match_children, gobj_name, gobj_parent, gobj_post_event, gobj_publish_event, gobj_read_attr, gobj_read_bool_attr, gobj_read_integer_attr, gobj_read_pointer_attr, gobj_read_str_attr, gobj_save_persistent_attrs, gobj_send_event, gobj_set_trace_machine_format, gobj_short_name, gobj_start, gobj_start_tree, gobj_stop, gobj_stop_children, gobj_subscribe_event, gobj_unsubscribe_event, gobj_write_attr, gobj_write_bool_attr, gobj_write_integer_attr, gobj_write_str_attr, gobj_yuno, gobj_yuno_name, is_array, is_boolean, is_date, is_gobj, is_null, is_number, is_object, is_string, json2flat, json_deep_copy, json_object_size, json_object_update, json_object_update_missing, json_size, kw_clone_by_keys, kw_flag_t, kw_get_dict, kw_get_dict_value, kw_get_int, kw_get_local_storage_value, kw_get_str, kw_has_key, kw_set_dict_value, kw_set_local_storage_value, kwid_find_one_record, kwid_new_dict, log_error, log_warning, msg_iev_get_stack, parseBoolean, parseSVG, refresh_language, safeSrc, sdata_flag_t, set_console_log_enabled, set_log_callback, sprintf, str_in_list, template_get_field_desc, trace_msg, treedb_decoder_fkey, treedb_encoder_fkey, treedb_get_field_desc, treedb_hook_data_size } from "@yuneta/gobj-js";
3
3
  import i18next, { t } from "i18next";
4
4
  import TomSelect from "tom-select";
5
5
  import { createJSONEditor } from "vanilla-jsoneditor";
@@ -23052,6 +23052,10 @@ function nodes_answer(data) {
23052
23052
  * Constants
23053
23053
  ***************************************************************/
23054
23054
  var GCLASS_NAME$6 = "C_YUI_TREEDB_TOPICS";
23055
+ var TABLE_READ_OPTIONS = {
23056
+ list_dict: true,
23057
+ hook_size: true
23058
+ };
23055
23059
  /***************************************************************
23056
23060
  * Data
23057
23061
  ***************************************************************/
@@ -24084,7 +24088,7 @@ function get_nodes$1(gobj, topic_name) {
24084
24088
  subscribe_treedb$1(gobj, topic_name);
24085
24089
  let gobj_topic_form = get_gobj_formtable(gobj, topic_name);
24086
24090
  if (gobj_topic_form && gobj_read_bool_attr(gobj_topic_form, "with_remote_paging")) return;
24087
- treedb_nodes$1(gobj, treedb_name, topic_name, { list_dict: true });
24091
+ treedb_nodes$1(gobj, treedb_name, topic_name, Object.assign({}, TABLE_READ_OPTIONS));
24088
24092
  }
24089
24093
  /************************************************************
24090
24094
  *
@@ -24667,7 +24671,7 @@ function ac_request_page(gobj, event, kw, src) {
24667
24671
  log_error(`${gobj_short_name(gobj)}: a page request with no topic or no id`);
24668
24672
  return -1;
24669
24673
  }
24670
- if (treedb_nodes$1(gobj, gobj_read_str_attr(gobj, "treedb_name"), topic_name, { list_dict: true }, {
24674
+ if (treedb_nodes$1(gobj, gobj_read_str_attr(gobj, "treedb_name"), topic_name, Object.assign({}, TABLE_READ_OPTIONS), {
24671
24675
  from: kw.from,
24672
24676
  limit: kw.limit,
24673
24677
  req_id: kw.req_id
@@ -24678,6 +24682,36 @@ function ac_request_page(gobj, event, kw, src) {
24678
24682
  return 0;
24679
24683
  }
24680
24684
  /********************************************
24685
+ * Event from formtable: open the records a hook of one row links --
24686
+ * the CHILD topic's table, filtered to the rows whose fkey names that
24687
+ * row. A hook is the question "which records hang from this one", and
24688
+ * the child's table is where that answer can be read: it pages, it
24689
+ * searches, it opens a record and it exports -- which a list of ids in
24690
+ * a popup could not do past a few dozen of them.
24691
+ * {
24692
+ * topic_name: the child topic
24693
+ * fkey: its fkey column that points back
24694
+ * parent_topic: the topic of the row the hook is on
24695
+ * parent_id: that row's id
24696
+ * hook: the hook column
24697
+ * }
24698
+ ********************************************/
24699
+ function ac_open_linked(gobj, event, kw, src) {
24700
+ let child = get_gobj_formtable(gobj, kw.topic_name);
24701
+ if (!child) {
24702
+ log_error(`${gobj_short_name(gobj)}: cannot open '${kw.topic_name}', linked from '${kw.parent_topic}^${kw.parent_id}': that topic is not in this view`);
24703
+ return -1;
24704
+ }
24705
+ select_topic_by_id(gobj, gobj_name(child));
24706
+ gobj_send_event(child, "EV_FILTER_BY_PARENT", {
24707
+ fkey: kw.fkey,
24708
+ parent_topic: kw.parent_topic,
24709
+ parent_id: kw.parent_id,
24710
+ hook: kw.hook
24711
+ }, gobj);
24712
+ return 0;
24713
+ }
24714
+ /********************************************
24681
24715
  * Event from formtable: the bytes of one asset a `file` column names,
24682
24716
  * for the form's preview or the table's file popup.
24683
24717
  *
@@ -24791,7 +24825,8 @@ function ac_refresh_topic(gobj, event, kw, src) {
24791
24825
  gobj_send_event(gobj_topic_form, "EV_REPULL_PAGE", {}, gobj);
24792
24826
  return 0;
24793
24827
  }
24794
- treedb_nodes$1(gobj, gobj_read_str_attr(gobj, "treedb_name"), kw.topic_name, { list_dict: true });
24828
+ let options = Object.assign({}, TABLE_READ_OPTIONS);
24829
+ treedb_nodes$1(gobj, gobj_read_str_attr(gobj, "treedb_name"), kw.topic_name, options);
24795
24830
  return 0;
24796
24831
  }
24797
24832
  /********************************************
@@ -24910,6 +24945,11 @@ function create_gclass$6(gclass_name) {
24910
24945
  ac_request_asset,
24911
24946
  null
24912
24947
  ],
24948
+ [
24949
+ "EV_OPEN_LINKED",
24950
+ ac_open_linked,
24951
+ null
24952
+ ],
24913
24953
  [
24914
24954
  "EV_DELETE_RECORD",
24915
24955
  ac_delete_record,
@@ -24991,6 +25031,7 @@ function create_gclass$6(gclass_name) {
24991
25031
  ["EV_UPDATE_FIELD", 0],
24992
25032
  ["EV_REQUEST_PAGE", 0],
24993
25033
  ["EV_REQUEST_ASSET", 0],
25034
+ ["EV_OPEN_LINKED", 0],
24994
25035
  ["EV_DELETE_RECORD", 0],
24995
25036
  ["EV_RECORD_WRITTEN", event_flag_t.EVF_OUTPUT_EVENT | event_flag_t.EVF_NO_WARN_SUBS],
24996
25037
  ["EV_REFRESH_TOPIC", 0],
@@ -25655,6 +25696,7 @@ function plan_treedb_writes(flags) {
25655
25696
  * How many things a hook/fkey value holds.
25656
25697
  ***************************************************************/
25657
25698
  function ref_count(value) {
25699
+ if (Array.isArray(value) && value.length === 1 && value[0] && typeof value[0] === "object" && typeof value[0].size === "number") return value[0].size;
25658
25700
  if (Array.isArray(value)) return value.length;
25659
25701
  if (value && typeof value === "object") return Object.keys(value).length;
25660
25702
  if (typeof value === "string" && value.length > 0) return 1;
@@ -25801,7 +25843,11 @@ var PRIVATE_DATA$5 = {
25801
25843
  cell_file_modal: null,
25802
25844
  cell_file_win: null,
25803
25845
  _pending_assets: null,
25804
- _asset_seq: 0
25846
+ _asset_seq: 0,
25847
+ parent_filter: null,
25848
+ search_term: "",
25849
+ $parent_filter: null,
25850
+ hook_choice_modal: null
25805
25851
  };
25806
25852
  var __gclass__$5 = null;
25807
25853
  function is_mobile$1() {
@@ -25935,6 +25981,7 @@ function mt_stop$5(gobj) {
25935
25981
  close_schema_dialog(gobj);
25936
25982
  close_cell_json_dialog(gobj);
25937
25983
  close_cell_file_dialog(gobj);
25984
+ close_hook_choice(gobj);
25938
25985
  drop_pending_assets(gobj);
25939
25986
  table__destroy(gobj);
25940
25987
  }
@@ -25946,6 +25993,7 @@ function mt_destroy$5(gobj) {
25946
25993
  close_schema_dialog(gobj);
25947
25994
  close_cell_json_dialog(gobj);
25948
25995
  close_cell_file_dialog(gobj);
25996
+ close_hook_choice(gobj);
25949
25997
  drop_pending_assets(gobj);
25950
25998
  destroy_ui$2(gobj);
25951
25999
  }
@@ -26305,6 +26353,50 @@ function build_ui$5(gobj) {
26305
26353
  bar.$el.style.flex = "none";
26306
26354
  $container.insertBefore(bar.$el, $container.querySelector(`#${table_id}`));
26307
26355
  }
26356
+ let $parent_filter = createElement2([
26357
+ "div",
26358
+ {
26359
+ class: "TREEDB_PARENT_FILTER is-hidden",
26360
+ style: "flex:none;"
26361
+ },
26362
+ [
26363
+ [
26364
+ "span",
26365
+ {
26366
+ class: "PARENT_FILTER_LABEL",
26367
+ i18n: "filtered by"
26368
+ },
26369
+ "filtered by"
26370
+ ],
26371
+ [
26372
+ "span",
26373
+ { class: "PARENT_FILTER_TOPIC" },
26374
+ ""
26375
+ ],
26376
+ [
26377
+ "strong",
26378
+ { class: "PARENT_FILTER_ID" },
26379
+ ""
26380
+ ],
26381
+ [
26382
+ "button",
26383
+ {
26384
+ class: "delete PARENT_FILTER_CLEAR",
26385
+ type: "button",
26386
+ title: t("clear filter"),
26387
+ "data-i18n-title": "clear filter",
26388
+ "aria-label": t("clear filter"),
26389
+ "data-i18n-aria-label": "clear filter"
26390
+ },
26391
+ [],
26392
+ { click: () => {
26393
+ gobj_send_event(gobj, "EV_CLEAR_PARENT_FILTER", {}, gobj);
26394
+ } }
26395
+ ]
26396
+ ]
26397
+ ]);
26398
+ gobj.priv.$parent_filter = $parent_filter;
26399
+ $container.insertBefore($parent_filter, $container.querySelector(`#${table_id}`));
26308
26400
  let $toolbar_slot = $container.querySelector(".toolbar_tabulator_table");
26309
26401
  if ($table_toolbar instanceof Element) $toolbar_slot.appendChild($table_toolbar);
26310
26402
  else $toolbar_slot.appendChild(createElement2(["div", {}]));
@@ -26797,6 +26889,7 @@ function create_tabulator(gobj) {
26797
26889
  yui_tabulator_name_filters(tabulator, t);
26798
26890
  yui_tabulator_name_row_selects(tabulator, t);
26799
26891
  tabulator._ready = true;
26892
+ apply_row_filters(gobj);
26800
26893
  update_rowcount();
26801
26894
  yui_table_filter_clear(tabulator);
26802
26895
  if (tabulator._pendingData !== void 0) {
@@ -27004,15 +27097,18 @@ function transform__treedb_value_2_table_value(gobj, col, value, row, field) {
27004
27097
  break;
27005
27098
  case "hook":
27006
27099
  let items = treedb_hook_data_size(value);
27007
- if (items > 0) value = [
27008
- "<a class=\"hook_cell\" ",
27009
- `data-row_id="${row.id}" `,
27010
- `data-col_id="${col.id}" > `,
27011
- "<span style=\"\" class=\"icon yi-eye\"></span>",
27012
- `<span>[&nbsp;<u>${items}</u>&nbsp;]</span>`,
27013
- "</a>"
27014
- ].join("");
27015
- else value = "";
27100
+ if (items > 0) {
27101
+ let hook_title = String(t("show linked records")).replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
27102
+ value = [
27103
+ "<a class=\"hook_cell\" ",
27104
+ `title="${hook_title}" aria-label="${hook_title}" `,
27105
+ `data-row_id="${row.id}" `,
27106
+ `data-col_id="${col.id}" > `,
27107
+ "<span style=\"\" class=\"icon yi-eye\"></span>",
27108
+ `<span>[&nbsp;<u>${items}</u>&nbsp;]</span>`,
27109
+ "</a>"
27110
+ ].join("");
27111
+ } else value = "";
27016
27112
  break;
27017
27113
  case "file":
27018
27114
  case "fkey":
@@ -27647,6 +27743,153 @@ function close_cell_file_dialog(gobj) {
27647
27743
  teardown_cell_file(gobj);
27648
27744
  }
27649
27745
  /************************************************************
27746
+ * Ask the host to open the rows of `topic_name` whose `fkey`
27747
+ * names this row -- what a hook of this row links.
27748
+ ************************************************************/
27749
+ function publish_open_linked(gobj, topic_name, fkey, parent_id, hook) {
27750
+ gobj_publish_event(gobj, "EV_OPEN_LINKED", {
27751
+ topic_name,
27752
+ fkey,
27753
+ parent_topic: gobj_read_str_attr(gobj, "topic_name"),
27754
+ parent_id,
27755
+ hook
27756
+ });
27757
+ }
27758
+ /************************************************************
27759
+ * "Which child topic?" -- for a hook whose children live in
27760
+ * more than one. The standardized dialog, so Escape, Back and
27761
+ * a click outside close it like every other one.
27762
+ ************************************************************/
27763
+ function open_hook_choice_dialog(gobj, targets, parent_id, hook) {
27764
+ close_hook_choice(gobj);
27765
+ let priv = gobj.priv;
27766
+ let shell = yui_shell_of(gobj);
27767
+ if (!shell) {
27768
+ log_error(`${gobj_short_name(gobj)}: no shell, cannot ask which topic to open`);
27769
+ return;
27770
+ }
27771
+ priv.hook_choice_modal = yui_shell_show_modal(shell, createElement2([
27772
+ "div",
27773
+ { class: "TREEDB_HOOK_CHOICE" },
27774
+ targets.map((tg) => {
27775
+ return [
27776
+ "button",
27777
+ {
27778
+ class: "button HOOK_CHOICE_TOPIC",
27779
+ type: "button",
27780
+ title: t(tg.topic_name),
27781
+ "data-i18n-title": tg.topic_name,
27782
+ "aria-label": t(tg.topic_name),
27783
+ "data-i18n-aria-label": tg.topic_name
27784
+ },
27785
+ [[
27786
+ "span",
27787
+ { i18n: tg.topic_name },
27788
+ tg.topic_name
27789
+ ]],
27790
+ { click: () => {
27791
+ gobj_send_event(gobj, "EV_CHOOSE_LINKED", {
27792
+ topic_name: tg.topic_name,
27793
+ fkey: tg.fkey,
27794
+ parent_id,
27795
+ hook
27796
+ }, gobj);
27797
+ } }
27798
+ ];
27799
+ })
27800
+ ]), {
27801
+ dialog: true,
27802
+ logical_class: "TREEDB_HOOK_CHOICE_SHEET",
27803
+ title: "choose a topic",
27804
+ t,
27805
+ on_close: function() {
27806
+ priv.hook_choice_modal = null;
27807
+ }
27808
+ });
27809
+ }
27810
+ /************************************************************
27811
+ * Close "which child topic?" if it is open.
27812
+ ************************************************************/
27813
+ function close_hook_choice(gobj) {
27814
+ let priv = gobj.priv;
27815
+ if (priv.hook_choice_modal) {
27816
+ let modal = priv.hook_choice_modal;
27817
+ priv.hook_choice_modal = null;
27818
+ modal.close();
27819
+ }
27820
+ }
27821
+ /************************************************************
27822
+ * Does this row's `pf.fkey` name the parent record? The fkey
27823
+ * arrives as one ref, a list of them or a dict keyed by ref,
27824
+ * each in any of the shapes treedb_decoder_fkey() reads. The
27825
+ * topic and the hook are compared when the ref carries them:
27826
+ * one row can hang from the same parent through two hooks.
27827
+ ************************************************************/
27828
+ function row_links_to(gobj, data, pf) {
27829
+ let col = get_schema_col(gobj, pf.fkey);
27830
+ let v = data ? data[pf.fkey] : null;
27831
+ if (!col || v === null || v === void 0 || v === "") return false;
27832
+ let refs;
27833
+ if (is_array(v)) refs = v;
27834
+ else if (is_object(v) && v.id === void 0) refs = Object.keys(v);
27835
+ else refs = [v];
27836
+ for (let ref of refs) {
27837
+ if (ref === null || ref === void 0 || ref === "") continue;
27838
+ let fk = treedb_decoder_fkey(col, ref);
27839
+ if (!fk || String(fk.id) !== pf.parent_id) continue;
27840
+ if (fk.topic_name && pf.parent_topic && fk.topic_name !== pf.parent_topic) continue;
27841
+ if (fk.hook_name && pf.hook && fk.hook_name !== pf.hook) continue;
27842
+ return true;
27843
+ }
27844
+ return false;
27845
+ }
27846
+ /************************************************************
27847
+ * The ONE programmatic filter of the table: the parent filter
27848
+ * AND the search term. Kept as state and applied here, because
27849
+ * Tabulator's setFilter() replaces every programmatic filter.
27850
+ * Before `tableBuilt` the state waits; tableBuilt applies it.
27851
+ ************************************************************/
27852
+ function apply_row_filters(gobj) {
27853
+ let priv = gobj.priv;
27854
+ let tabulator = gobj_read_attr(gobj, "tabulator");
27855
+ if (!tabulator) {
27856
+ log_error(`${gobj_short_name(gobj)}: no table to filter`);
27857
+ return;
27858
+ }
27859
+ if (!tabulator._ready) return;
27860
+ let term = priv.search_term || "";
27861
+ let pf = priv.parent_filter;
27862
+ if (!term && !pf) {
27863
+ tabulator.clearFilter();
27864
+ return;
27865
+ }
27866
+ tabulator.setFilter(function(data) {
27867
+ if (pf && !row_links_to(gobj, data, pf)) return false;
27868
+ return !term || row_matches(data, term);
27869
+ });
27870
+ }
27871
+ /************************************************************
27872
+ * Show or hide the chip that says the parent filter is on.
27873
+ ************************************************************/
27874
+ function render_parent_filter(gobj) {
27875
+ let priv = gobj.priv;
27876
+ let $chip = priv.$parent_filter;
27877
+ if (!$chip) {
27878
+ log_error(`${gobj_short_name(gobj)}: no parent-filter chip to paint`);
27879
+ return;
27880
+ }
27881
+ let pf = priv.parent_filter;
27882
+ if (!pf) {
27883
+ $chip.classList.add("is-hidden");
27884
+ return;
27885
+ }
27886
+ let $topic = $chip.querySelector(".PARENT_FILTER_TOPIC");
27887
+ $topic.setAttribute("data-i18n", pf.parent_topic);
27888
+ $topic.textContent = t(pf.parent_topic);
27889
+ $chip.querySelector(".PARENT_FILTER_ID").textContent = pf.parent_id;
27890
+ $chip.classList.remove("is-hidden");
27891
+ }
27892
+ /************************************************************
27650
27893
  * True if the topic's pkey col carries the "rowid" flag.
27651
27894
  ************************************************************/
27652
27895
  function pkey_is_rowid(gobj) {
@@ -27903,74 +28146,6 @@ function build_fkey_ref(gobj, col, value) {
27903
28146
  }
27904
28147
  return refs;
27905
28148
  }
27906
- /***************************************************************************
27907
- * TODO con límite máximo o máximo height o con scroll
27908
- * en un gobj propio para gestionar los datos en "page"s
27909
- * que los hook no vengan rellenos si son muchos y que se puedan gestionar
27910
- * con un gobj
27911
- ***************************************************************************/
27912
- function show_dropdown_popup_menu(gobj, x, y, items, callback) {
27913
- let $element = createElement2([
27914
- "div",
27915
- { class: "dropdown popup" },
27916
- [[
27917
- "div",
27918
- {
27919
- class: "dropdown-menu",
27920
- role: "menu",
27921
- style: "min-width:4rem; border: 2px solid var(--bulma-border); padding: 0px;"
27922
- },
27923
- [[
27924
- "div",
27925
- {
27926
- class: "dropdown-content",
27927
- style: "padding: 0;"
27928
- },
27929
- []
27930
- ]]
27931
- ]],
27932
- { "click": (evt) => {
27933
- evt.stopPropagation();
27934
- destroyModal();
27935
- if (callback) callback(evt);
27936
- } }
27937
- ]);
27938
- const destroyModal = () => {
27939
- $element.classList.remove("is-active");
27940
- $element.parentNode.removeChild($element);
27941
- };
27942
- let $dropdown_content = $element.querySelector(".dropdown-content");
27943
- let ids = kwid_get_ids(gobj, items);
27944
- for (let id of ids) {
27945
- let $item = createElement2([
27946
- "a",
27947
- {
27948
- class: "dropdown-item flex-horizontal-section",
27949
- "data-value": `${id}`,
27950
- style: "margin:0px;"
27951
- },
27952
- [[
27953
- "span",
27954
- { i18n: `${id}` },
27955
- `${id}`
27956
- ]],
27957
- { "click": (evt) => {
27958
- evt.stopPropagation();
27959
- destroyModal();
27960
- if (callback) callback(evt, this.dataset.value);
27961
- } }
27962
- ]);
27963
- $dropdown_content.appendChild($item);
27964
- }
27965
- refresh_language($element, t);
27966
- popup_mount_layer(gobj).appendChild($element);
27967
- $element.style.position = "absolute";
27968
- $element.style.top = y + "px";
27969
- $element.style.left = x + "px";
27970
- $element.classList.add("is-active");
27971
- let $with_focus = $element.querySelector(".with-focus");
27972
- if ($with_focus) $with_focus.focus();
27973
- }
27974
28149
  /***************************************************************
27975
28150
  * A read-only topic refuses the write EVENTS too, not only their
27976
28151
  * buttons. The buttons are gone, but an event can still arrive: a
@@ -28410,12 +28585,69 @@ function ac_change_locale(gobj, event, kw, src) {
28410
28585
  * };
28411
28586
  ************************************************************/
28412
28587
  function ac_show_hook_data$1(gobj, event, kw, src) {
28413
- let webix = gobj_command(gobj_parent(gobj), "get_topic_data", kw, gobj);
28414
- let row = kwid_find_one_record(gobj, webix.data, kw.row_id, null);
28415
- if (row) {
28416
- let cell = row[kw.col_id];
28417
- show_dropdown_popup_menu(gobj, kw.click_x, kw.click_y, cell);
28588
+ let col = get_schema_col(gobj, kw.col_id);
28589
+ let hook = col && is_object(col.hook) ? col.hook : null;
28590
+ if (!hook || kw.row_id === void 0 || kw.row_id === null) {
28591
+ log_error(`${gobj_short_name(gobj)}: hook '${kw.col_id}' names no child topic to open`);
28592
+ return -1;
28593
+ }
28594
+ let targets = Object.keys(hook).map((topic) => {
28595
+ return {
28596
+ topic_name: topic,
28597
+ fkey: hook[topic]
28598
+ };
28599
+ });
28600
+ if (targets.length === 1) {
28601
+ publish_open_linked(gobj, targets[0].topic_name, targets[0].fkey, String(kw.row_id), kw.col_id);
28602
+ return 0;
28603
+ }
28604
+ open_hook_choice_dialog(gobj, targets, String(kw.row_id), kw.col_id);
28605
+ return 0;
28606
+ }
28607
+ /************************************************************
28608
+ * A hook with several child topics: pick one.
28609
+ * {topic_name, fkey, parent_id, hook}
28610
+ ************************************************************/
28611
+ function ac_choose_linked(gobj, event, kw, src) {
28612
+ close_hook_choice(gobj);
28613
+ publish_open_linked(gobj, kw.topic_name, kw.fkey, kw.parent_id, kw.hook);
28614
+ return 0;
28615
+ }
28616
+ /************************************************************
28617
+ * Show only the rows linked to one record of another topic.
28618
+ * Sent by the host when a hook of that record was clicked.
28619
+ * {fkey, parent_topic, parent_id, hook}
28620
+ *
28621
+ * Over the rows the table HOLDS: with `with_remote_paging` that
28622
+ * is one page, which is one of the reasons that flag stays off.
28623
+ ************************************************************/
28624
+ function ac_filter_by_parent(gobj, event, kw, src) {
28625
+ let priv = gobj.priv;
28626
+ if (!kw || !kw.fkey || kw.parent_id === void 0 || kw.parent_id === null) {
28627
+ log_error(`${gobj_short_name(gobj)}: a parent filter with no fkey or no parent`);
28628
+ return -1;
28629
+ }
28630
+ if (!get_schema_col(gobj, kw.fkey)) {
28631
+ log_error(`${gobj_short_name(gobj)}: parent filter on '${kw.fkey}', which is not a column of '${gobj_read_str_attr(gobj, "topic_name")}'`);
28632
+ return -1;
28418
28633
  }
28634
+ priv.parent_filter = {
28635
+ fkey: kw.fkey,
28636
+ parent_topic: kw.parent_topic || "",
28637
+ parent_id: String(kw.parent_id),
28638
+ hook: kw.hook || ""
28639
+ };
28640
+ render_parent_filter(gobj);
28641
+ apply_row_filters(gobj);
28642
+ return 0;
28643
+ }
28644
+ /************************************************************
28645
+ * The ✕ of the parent filter: back to the whole topic.
28646
+ ************************************************************/
28647
+ function ac_clear_parent_filter(gobj, event, kw, src) {
28648
+ gobj.priv.parent_filter = null;
28649
+ render_parent_filter(gobj);
28650
+ apply_row_filters(gobj);
28419
28651
  return 0;
28420
28652
  }
28421
28653
  /************************************************************
@@ -28726,19 +28958,12 @@ function ac_page_timeout(gobj, event, kw, src) {
28726
28958
  * cleared search box does not silently undo them.
28727
28959
  ************************************************************/
28728
28960
  function ac_search(gobj, event, kw, src) {
28729
- let tabulator = gobj_read_attr(gobj, "tabulator");
28730
- if (!tabulator) {
28961
+ if (!gobj_read_attr(gobj, "tabulator")) {
28731
28962
  log_error(`${gobj_short_name(gobj)}: no table to search`);
28732
28963
  return -1;
28733
28964
  }
28734
- let term = (kw && kw.text || "").toLowerCase();
28735
- if (!term) {
28736
- tabulator.clearFilter();
28737
- return 0;
28738
- }
28739
- tabulator.setFilter(function(data) {
28740
- return row_matches(data, term);
28741
- });
28965
+ gobj.priv.search_term = (kw && kw.text || "").toLowerCase();
28966
+ apply_row_filters(gobj);
28742
28967
  return 0;
28743
28968
  }
28744
28969
  /************************************************************
@@ -28986,6 +29211,21 @@ function create_gclass$5(gclass_name) {
28986
29211
  ac_show_hook_data$1,
28987
29212
  null
28988
29213
  ],
29214
+ [
29215
+ "EV_CHOOSE_LINKED",
29216
+ ac_choose_linked,
29217
+ null
29218
+ ],
29219
+ [
29220
+ "EV_FILTER_BY_PARENT",
29221
+ ac_filter_by_parent,
29222
+ null
29223
+ ],
29224
+ [
29225
+ "EV_CLEAR_PARENT_FILTER",
29226
+ ac_clear_parent_filter,
29227
+ null
29228
+ ],
28989
29229
  [
28990
29230
  "EV_SHOW_CELL_JSON",
28991
29231
  ac_show_cell_json,
@@ -29104,7 +29344,11 @@ function create_gclass$5(gclass_name) {
29104
29344
  ["EV_CLEAR_SELECTION", 0],
29105
29345
  ["EV_SELECT_ROWS", event_flag_t.EVF_OUTPUT_EVENT],
29106
29346
  ["EV_UNSELECT_ROWS", event_flag_t.EVF_OUTPUT_EVENT],
29107
- ["EV_SHOW_HOOK_DATA", event_flag_t.EVF_OUTPUT_EVENT],
29347
+ ["EV_SHOW_HOOK_DATA", 0],
29348
+ ["EV_CHOOSE_LINKED", 0],
29349
+ ["EV_FILTER_BY_PARENT", 0],
29350
+ ["EV_CLEAR_PARENT_FILTER", 0],
29351
+ ["EV_OPEN_LINKED", event_flag_t.EVF_OUTPUT_EVENT],
29108
29352
  ["EV_SHOW_CELL_JSON", 0],
29109
29353
  ["EV_SHOW_CELL_FILE", 0],
29110
29354
  ["EV_ASSET_LOADED", 0],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuneta/gobj-ui",
3
- "version": "7.23.161",
3
+ "version": "7.23.163",
4
4
  "type": "module",
5
5
  "main": "dist/gobj-ui.cjs.js",
6
6
  "module": "dist/gobj-ui.es.js",
@@ -208,3 +208,33 @@
208
208
  font-family: monospace;
209
209
  overflow-wrap: anywhere;
210
210
  }
211
+
212
+ /*
213
+ * The table is showing only the rows linked to one record, because a
214
+ * hook was clicked (EV_FILTER_BY_PARENT). Said above the table, with
215
+ * the way out beside it: a filter nobody can see is a table that looks
216
+ * like it lost rows.
217
+ */
218
+ .TREEDB_PARENT_FILTER {
219
+ display: flex;
220
+ align-items: center;
221
+ gap: 0.4rem;
222
+ flex-wrap: wrap;
223
+ padding: 0.35rem 0.75rem;
224
+ border-bottom: 1px solid var(--bulma-border, #dbdbdb);
225
+ background: var(--bulma-scheme-main-bis, transparent);
226
+ }
227
+ .TREEDB_PARENT_FILTER .PARENT_FILTER_ID {
228
+ font-family: monospace;
229
+ overflow-wrap: anywhere;
230
+ }
231
+
232
+ /*
233
+ * A hook whose children live in more than one topic: which one to open.
234
+ */
235
+ .TREEDB_HOOK_CHOICE {
236
+ display: flex;
237
+ flex-direction: column;
238
+ gap: 0.5rem;
239
+ padding: 0.75rem;
240
+ }