@yuneta/gobj-ui 7.23.160 → 7.23.162

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";
@@ -24678,6 +24678,36 @@ function ac_request_page(gobj, event, kw, src) {
24678
24678
  return 0;
24679
24679
  }
24680
24680
  /********************************************
24681
+ * Event from formtable: open the records a hook of one row links --
24682
+ * the CHILD topic's table, filtered to the rows whose fkey names that
24683
+ * row. A hook is the question "which records hang from this one", and
24684
+ * the child's table is where that answer can be read: it pages, it
24685
+ * searches, it opens a record and it exports -- which a list of ids in
24686
+ * a popup could not do past a few dozen of them.
24687
+ * {
24688
+ * topic_name: the child topic
24689
+ * fkey: its fkey column that points back
24690
+ * parent_topic: the topic of the row the hook is on
24691
+ * parent_id: that row's id
24692
+ * hook: the hook column
24693
+ * }
24694
+ ********************************************/
24695
+ function ac_open_linked(gobj, event, kw, src) {
24696
+ let child = get_gobj_formtable(gobj, kw.topic_name);
24697
+ if (!child) {
24698
+ 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`);
24699
+ return -1;
24700
+ }
24701
+ select_topic_by_id(gobj, gobj_name(child));
24702
+ gobj_send_event(child, "EV_FILTER_BY_PARENT", {
24703
+ fkey: kw.fkey,
24704
+ parent_topic: kw.parent_topic,
24705
+ parent_id: kw.parent_id,
24706
+ hook: kw.hook
24707
+ }, gobj);
24708
+ return 0;
24709
+ }
24710
+ /********************************************
24681
24711
  * Event from formtable: the bytes of one asset a `file` column names,
24682
24712
  * for the form's preview or the table's file popup.
24683
24713
  *
@@ -24910,6 +24940,11 @@ function create_gclass$6(gclass_name) {
24910
24940
  ac_request_asset,
24911
24941
  null
24912
24942
  ],
24943
+ [
24944
+ "EV_OPEN_LINKED",
24945
+ ac_open_linked,
24946
+ null
24947
+ ],
24913
24948
  [
24914
24949
  "EV_DELETE_RECORD",
24915
24950
  ac_delete_record,
@@ -24991,6 +25026,7 @@ function create_gclass$6(gclass_name) {
24991
25026
  ["EV_UPDATE_FIELD", 0],
24992
25027
  ["EV_REQUEST_PAGE", 0],
24993
25028
  ["EV_REQUEST_ASSET", 0],
25029
+ ["EV_OPEN_LINKED", 0],
24994
25030
  ["EV_DELETE_RECORD", 0],
24995
25031
  ["EV_RECORD_WRITTEN", event_flag_t.EVF_OUTPUT_EVENT | event_flag_t.EVF_NO_WARN_SUBS],
24996
25032
  ["EV_REFRESH_TOPIC", 0],
@@ -25655,6 +25691,7 @@ function plan_treedb_writes(flags) {
25655
25691
  * How many things a hook/fkey value holds.
25656
25692
  ***************************************************************/
25657
25693
  function ref_count(value) {
25694
+ if (Array.isArray(value) && value.length === 1 && value[0] && typeof value[0] === "object" && typeof value[0].size === "number") return value[0].size;
25658
25695
  if (Array.isArray(value)) return value.length;
25659
25696
  if (value && typeof value === "object") return Object.keys(value).length;
25660
25697
  if (typeof value === "string" && value.length > 0) return 1;
@@ -25801,7 +25838,11 @@ var PRIVATE_DATA$5 = {
25801
25838
  cell_file_modal: null,
25802
25839
  cell_file_win: null,
25803
25840
  _pending_assets: null,
25804
- _asset_seq: 0
25841
+ _asset_seq: 0,
25842
+ parent_filter: null,
25843
+ search_term: "",
25844
+ $parent_filter: null,
25845
+ hook_choice_modal: null
25805
25846
  };
25806
25847
  var __gclass__$5 = null;
25807
25848
  function is_mobile$1() {
@@ -25935,6 +25976,7 @@ function mt_stop$5(gobj) {
25935
25976
  close_schema_dialog(gobj);
25936
25977
  close_cell_json_dialog(gobj);
25937
25978
  close_cell_file_dialog(gobj);
25979
+ close_hook_choice(gobj);
25938
25980
  drop_pending_assets(gobj);
25939
25981
  table__destroy(gobj);
25940
25982
  }
@@ -25946,6 +25988,7 @@ function mt_destroy$5(gobj) {
25946
25988
  close_schema_dialog(gobj);
25947
25989
  close_cell_json_dialog(gobj);
25948
25990
  close_cell_file_dialog(gobj);
25991
+ close_hook_choice(gobj);
25949
25992
  drop_pending_assets(gobj);
25950
25993
  destroy_ui$2(gobj);
25951
25994
  }
@@ -26305,6 +26348,50 @@ function build_ui$5(gobj) {
26305
26348
  bar.$el.style.flex = "none";
26306
26349
  $container.insertBefore(bar.$el, $container.querySelector(`#${table_id}`));
26307
26350
  }
26351
+ let $parent_filter = createElement2([
26352
+ "div",
26353
+ {
26354
+ class: "TREEDB_PARENT_FILTER is-hidden",
26355
+ style: "flex:none;"
26356
+ },
26357
+ [
26358
+ [
26359
+ "span",
26360
+ {
26361
+ class: "PARENT_FILTER_LABEL",
26362
+ i18n: "filtered by"
26363
+ },
26364
+ "filtered by"
26365
+ ],
26366
+ [
26367
+ "span",
26368
+ { class: "PARENT_FILTER_TOPIC" },
26369
+ ""
26370
+ ],
26371
+ [
26372
+ "strong",
26373
+ { class: "PARENT_FILTER_ID" },
26374
+ ""
26375
+ ],
26376
+ [
26377
+ "button",
26378
+ {
26379
+ class: "delete PARENT_FILTER_CLEAR",
26380
+ type: "button",
26381
+ title: t("clear filter"),
26382
+ "data-i18n-title": "clear filter",
26383
+ "aria-label": t("clear filter"),
26384
+ "data-i18n-aria-label": "clear filter"
26385
+ },
26386
+ [],
26387
+ { click: () => {
26388
+ gobj_send_event(gobj, "EV_CLEAR_PARENT_FILTER", {}, gobj);
26389
+ } }
26390
+ ]
26391
+ ]
26392
+ ]);
26393
+ gobj.priv.$parent_filter = $parent_filter;
26394
+ $container.insertBefore($parent_filter, $container.querySelector(`#${table_id}`));
26308
26395
  let $toolbar_slot = $container.querySelector(".toolbar_tabulator_table");
26309
26396
  if ($table_toolbar instanceof Element) $toolbar_slot.appendChild($table_toolbar);
26310
26397
  else $toolbar_slot.appendChild(createElement2(["div", {}]));
@@ -26797,6 +26884,7 @@ function create_tabulator(gobj) {
26797
26884
  yui_tabulator_name_filters(tabulator, t);
26798
26885
  yui_tabulator_name_row_selects(tabulator, t);
26799
26886
  tabulator._ready = true;
26887
+ apply_row_filters(gobj);
26800
26888
  update_rowcount();
26801
26889
  yui_table_filter_clear(tabulator);
26802
26890
  if (tabulator._pendingData !== void 0) {
@@ -27004,15 +27092,18 @@ function transform__treedb_value_2_table_value(gobj, col, value, row, field) {
27004
27092
  break;
27005
27093
  case "hook":
27006
27094
  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 = "";
27095
+ if (items > 0) {
27096
+ let hook_title = String(t("show linked records")).replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
27097
+ value = [
27098
+ "<a class=\"hook_cell\" ",
27099
+ `title="${hook_title}" aria-label="${hook_title}" `,
27100
+ `data-row_id="${row.id}" `,
27101
+ `data-col_id="${col.id}" > `,
27102
+ "<span style=\"\" class=\"icon yi-eye\"></span>",
27103
+ `<span>[&nbsp;<u>${items}</u>&nbsp;]</span>`,
27104
+ "</a>"
27105
+ ].join("");
27106
+ } else value = "";
27016
27107
  break;
27017
27108
  case "file":
27018
27109
  case "fkey":
@@ -27647,6 +27738,153 @@ function close_cell_file_dialog(gobj) {
27647
27738
  teardown_cell_file(gobj);
27648
27739
  }
27649
27740
  /************************************************************
27741
+ * Ask the host to open the rows of `topic_name` whose `fkey`
27742
+ * names this row -- what a hook of this row links.
27743
+ ************************************************************/
27744
+ function publish_open_linked(gobj, topic_name, fkey, parent_id, hook) {
27745
+ gobj_publish_event(gobj, "EV_OPEN_LINKED", {
27746
+ topic_name,
27747
+ fkey,
27748
+ parent_topic: gobj_read_str_attr(gobj, "topic_name"),
27749
+ parent_id,
27750
+ hook
27751
+ });
27752
+ }
27753
+ /************************************************************
27754
+ * "Which child topic?" -- for a hook whose children live in
27755
+ * more than one. The standardized dialog, so Escape, Back and
27756
+ * a click outside close it like every other one.
27757
+ ************************************************************/
27758
+ function open_hook_choice_dialog(gobj, targets, parent_id, hook) {
27759
+ close_hook_choice(gobj);
27760
+ let priv = gobj.priv;
27761
+ let shell = yui_shell_of(gobj);
27762
+ if (!shell) {
27763
+ log_error(`${gobj_short_name(gobj)}: no shell, cannot ask which topic to open`);
27764
+ return;
27765
+ }
27766
+ priv.hook_choice_modal = yui_shell_show_modal(shell, createElement2([
27767
+ "div",
27768
+ { class: "TREEDB_HOOK_CHOICE" },
27769
+ targets.map((tg) => {
27770
+ return [
27771
+ "button",
27772
+ {
27773
+ class: "button HOOK_CHOICE_TOPIC",
27774
+ type: "button",
27775
+ title: t(tg.topic_name),
27776
+ "data-i18n-title": tg.topic_name,
27777
+ "aria-label": t(tg.topic_name),
27778
+ "data-i18n-aria-label": tg.topic_name
27779
+ },
27780
+ [[
27781
+ "span",
27782
+ { i18n: tg.topic_name },
27783
+ tg.topic_name
27784
+ ]],
27785
+ { click: () => {
27786
+ gobj_send_event(gobj, "EV_CHOOSE_LINKED", {
27787
+ topic_name: tg.topic_name,
27788
+ fkey: tg.fkey,
27789
+ parent_id,
27790
+ hook
27791
+ }, gobj);
27792
+ } }
27793
+ ];
27794
+ })
27795
+ ]), {
27796
+ dialog: true,
27797
+ logical_class: "TREEDB_HOOK_CHOICE_SHEET",
27798
+ title: "choose a topic",
27799
+ t,
27800
+ on_close: function() {
27801
+ priv.hook_choice_modal = null;
27802
+ }
27803
+ });
27804
+ }
27805
+ /************************************************************
27806
+ * Close "which child topic?" if it is open.
27807
+ ************************************************************/
27808
+ function close_hook_choice(gobj) {
27809
+ let priv = gobj.priv;
27810
+ if (priv.hook_choice_modal) {
27811
+ let modal = priv.hook_choice_modal;
27812
+ priv.hook_choice_modal = null;
27813
+ modal.close();
27814
+ }
27815
+ }
27816
+ /************************************************************
27817
+ * Does this row's `pf.fkey` name the parent record? The fkey
27818
+ * arrives as one ref, a list of them or a dict keyed by ref,
27819
+ * each in any of the shapes treedb_decoder_fkey() reads. The
27820
+ * topic and the hook are compared when the ref carries them:
27821
+ * one row can hang from the same parent through two hooks.
27822
+ ************************************************************/
27823
+ function row_links_to(gobj, data, pf) {
27824
+ let col = get_schema_col(gobj, pf.fkey);
27825
+ let v = data ? data[pf.fkey] : null;
27826
+ if (!col || v === null || v === void 0 || v === "") return false;
27827
+ let refs;
27828
+ if (is_array(v)) refs = v;
27829
+ else if (is_object(v) && v.id === void 0) refs = Object.keys(v);
27830
+ else refs = [v];
27831
+ for (let ref of refs) {
27832
+ if (ref === null || ref === void 0 || ref === "") continue;
27833
+ let fk = treedb_decoder_fkey(col, ref);
27834
+ if (!fk || String(fk.id) !== pf.parent_id) continue;
27835
+ if (fk.topic_name && pf.parent_topic && fk.topic_name !== pf.parent_topic) continue;
27836
+ if (fk.hook_name && pf.hook && fk.hook_name !== pf.hook) continue;
27837
+ return true;
27838
+ }
27839
+ return false;
27840
+ }
27841
+ /************************************************************
27842
+ * The ONE programmatic filter of the table: the parent filter
27843
+ * AND the search term. Kept as state and applied here, because
27844
+ * Tabulator's setFilter() replaces every programmatic filter.
27845
+ * Before `tableBuilt` the state waits; tableBuilt applies it.
27846
+ ************************************************************/
27847
+ function apply_row_filters(gobj) {
27848
+ let priv = gobj.priv;
27849
+ let tabulator = gobj_read_attr(gobj, "tabulator");
27850
+ if (!tabulator) {
27851
+ log_error(`${gobj_short_name(gobj)}: no table to filter`);
27852
+ return;
27853
+ }
27854
+ if (!tabulator._ready) return;
27855
+ let term = priv.search_term || "";
27856
+ let pf = priv.parent_filter;
27857
+ if (!term && !pf) {
27858
+ tabulator.clearFilter();
27859
+ return;
27860
+ }
27861
+ tabulator.setFilter(function(data) {
27862
+ if (pf && !row_links_to(gobj, data, pf)) return false;
27863
+ return !term || row_matches(data, term);
27864
+ });
27865
+ }
27866
+ /************************************************************
27867
+ * Show or hide the chip that says the parent filter is on.
27868
+ ************************************************************/
27869
+ function render_parent_filter(gobj) {
27870
+ let priv = gobj.priv;
27871
+ let $chip = priv.$parent_filter;
27872
+ if (!$chip) {
27873
+ log_error(`${gobj_short_name(gobj)}: no parent-filter chip to paint`);
27874
+ return;
27875
+ }
27876
+ let pf = priv.parent_filter;
27877
+ if (!pf) {
27878
+ $chip.classList.add("is-hidden");
27879
+ return;
27880
+ }
27881
+ let $topic = $chip.querySelector(".PARENT_FILTER_TOPIC");
27882
+ $topic.setAttribute("data-i18n", pf.parent_topic);
27883
+ $topic.textContent = t(pf.parent_topic);
27884
+ $chip.querySelector(".PARENT_FILTER_ID").textContent = pf.parent_id;
27885
+ $chip.classList.remove("is-hidden");
27886
+ }
27887
+ /************************************************************
27650
27888
  * True if the topic's pkey col carries the "rowid" flag.
27651
27889
  ************************************************************/
27652
27890
  function pkey_is_rowid(gobj) {
@@ -27666,9 +27904,15 @@ function show_edit_form(gobj, row, index) {
27666
27904
  * Does a click on this column already mean something else?
27667
27905
  *
27668
27906
  * The `_` columns are the table's own scaffolding (the selection
27669
- * checkbox, the Op buttons); a hook opens its children and a col
27670
- * holding a json document opens it in a viewer. Anything else is
27671
- * plain data, and a click on it means "show me this record".
27907
+ * checkbox, the Op buttons); a hook opens its children, a col
27908
+ * holding a json document opens it in a viewer, and a `file` col
27909
+ * opens its asset in a popup. Anything else is plain data, and a
27910
+ * click on it means "show me this record".
27911
+ *
27912
+ * `file` was missing when its popup arrived (7.23.159), so a click
27913
+ * on a photo opened the popup AND the record on top of it: Tabulator
27914
+ * fires `rowClick` from its own dispatcher, and the cell's
27915
+ * stopPropagation() does not reach it.
27672
27916
  ************************************************************/
27673
27917
  var CELL_WITH_ACTION = [
27674
27918
  "hook",
@@ -27679,7 +27923,8 @@ var CELL_WITH_ACTION = [
27679
27923
  "list",
27680
27924
  "coordinates",
27681
27925
  "blob",
27682
- "gbuffer"
27926
+ "gbuffer",
27927
+ "file"
27683
27928
  ];
27684
27929
  function cell_has_its_own_action(gobj, field) {
27685
27930
  if (!field || field.charAt(0) === "_") return true;
@@ -27896,74 +28141,6 @@ function build_fkey_ref(gobj, col, value) {
27896
28141
  }
27897
28142
  return refs;
27898
28143
  }
27899
- /***************************************************************************
27900
- * TODO con límite máximo o máximo height o con scroll
27901
- * en un gobj propio para gestionar los datos en "page"s
27902
- * que los hook no vengan rellenos si son muchos y que se puedan gestionar
27903
- * con un gobj
27904
- ***************************************************************************/
27905
- function show_dropdown_popup_menu(gobj, x, y, items, callback) {
27906
- let $element = createElement2([
27907
- "div",
27908
- { class: "dropdown popup" },
27909
- [[
27910
- "div",
27911
- {
27912
- class: "dropdown-menu",
27913
- role: "menu",
27914
- style: "min-width:4rem; border: 2px solid var(--bulma-border); padding: 0px;"
27915
- },
27916
- [[
27917
- "div",
27918
- {
27919
- class: "dropdown-content",
27920
- style: "padding: 0;"
27921
- },
27922
- []
27923
- ]]
27924
- ]],
27925
- { "click": (evt) => {
27926
- evt.stopPropagation();
27927
- destroyModal();
27928
- if (callback) callback(evt);
27929
- } }
27930
- ]);
27931
- const destroyModal = () => {
27932
- $element.classList.remove("is-active");
27933
- $element.parentNode.removeChild($element);
27934
- };
27935
- let $dropdown_content = $element.querySelector(".dropdown-content");
27936
- let ids = kwid_get_ids(gobj, items);
27937
- for (let id of ids) {
27938
- let $item = createElement2([
27939
- "a",
27940
- {
27941
- class: "dropdown-item flex-horizontal-section",
27942
- "data-value": `${id}`,
27943
- style: "margin:0px;"
27944
- },
27945
- [[
27946
- "span",
27947
- { i18n: `${id}` },
27948
- `${id}`
27949
- ]],
27950
- { "click": (evt) => {
27951
- evt.stopPropagation();
27952
- destroyModal();
27953
- if (callback) callback(evt, this.dataset.value);
27954
- } }
27955
- ]);
27956
- $dropdown_content.appendChild($item);
27957
- }
27958
- refresh_language($element, t);
27959
- popup_mount_layer(gobj).appendChild($element);
27960
- $element.style.position = "absolute";
27961
- $element.style.top = y + "px";
27962
- $element.style.left = x + "px";
27963
- $element.classList.add("is-active");
27964
- let $with_focus = $element.querySelector(".with-focus");
27965
- if ($with_focus) $with_focus.focus();
27966
- }
27967
28144
  /***************************************************************
27968
28145
  * A read-only topic refuses the write EVENTS too, not only their
27969
28146
  * buttons. The buttons are gone, but an event can still arrive: a
@@ -28403,12 +28580,69 @@ function ac_change_locale(gobj, event, kw, src) {
28403
28580
  * };
28404
28581
  ************************************************************/
28405
28582
  function ac_show_hook_data$1(gobj, event, kw, src) {
28406
- let webix = gobj_command(gobj_parent(gobj), "get_topic_data", kw, gobj);
28407
- let row = kwid_find_one_record(gobj, webix.data, kw.row_id, null);
28408
- if (row) {
28409
- let cell = row[kw.col_id];
28410
- show_dropdown_popup_menu(gobj, kw.click_x, kw.click_y, cell);
28583
+ let col = get_schema_col(gobj, kw.col_id);
28584
+ let hook = col && is_object(col.hook) ? col.hook : null;
28585
+ if (!hook || kw.row_id === void 0 || kw.row_id === null) {
28586
+ log_error(`${gobj_short_name(gobj)}: hook '${kw.col_id}' names no child topic to open`);
28587
+ return -1;
28588
+ }
28589
+ let targets = Object.keys(hook).map((topic) => {
28590
+ return {
28591
+ topic_name: topic,
28592
+ fkey: hook[topic]
28593
+ };
28594
+ });
28595
+ if (targets.length === 1) {
28596
+ publish_open_linked(gobj, targets[0].topic_name, targets[0].fkey, String(kw.row_id), kw.col_id);
28597
+ return 0;
28598
+ }
28599
+ open_hook_choice_dialog(gobj, targets, String(kw.row_id), kw.col_id);
28600
+ return 0;
28601
+ }
28602
+ /************************************************************
28603
+ * A hook with several child topics: pick one.
28604
+ * {topic_name, fkey, parent_id, hook}
28605
+ ************************************************************/
28606
+ function ac_choose_linked(gobj, event, kw, src) {
28607
+ close_hook_choice(gobj);
28608
+ publish_open_linked(gobj, kw.topic_name, kw.fkey, kw.parent_id, kw.hook);
28609
+ return 0;
28610
+ }
28611
+ /************************************************************
28612
+ * Show only the rows linked to one record of another topic.
28613
+ * Sent by the host when a hook of that record was clicked.
28614
+ * {fkey, parent_topic, parent_id, hook}
28615
+ *
28616
+ * Over the rows the table HOLDS: with `with_remote_paging` that
28617
+ * is one page, which is one of the reasons that flag stays off.
28618
+ ************************************************************/
28619
+ function ac_filter_by_parent(gobj, event, kw, src) {
28620
+ let priv = gobj.priv;
28621
+ if (!kw || !kw.fkey || kw.parent_id === void 0 || kw.parent_id === null) {
28622
+ log_error(`${gobj_short_name(gobj)}: a parent filter with no fkey or no parent`);
28623
+ return -1;
28624
+ }
28625
+ if (!get_schema_col(gobj, kw.fkey)) {
28626
+ log_error(`${gobj_short_name(gobj)}: parent filter on '${kw.fkey}', which is not a column of '${gobj_read_str_attr(gobj, "topic_name")}'`);
28627
+ return -1;
28411
28628
  }
28629
+ priv.parent_filter = {
28630
+ fkey: kw.fkey,
28631
+ parent_topic: kw.parent_topic || "",
28632
+ parent_id: String(kw.parent_id),
28633
+ hook: kw.hook || ""
28634
+ };
28635
+ render_parent_filter(gobj);
28636
+ apply_row_filters(gobj);
28637
+ return 0;
28638
+ }
28639
+ /************************************************************
28640
+ * The ✕ of the parent filter: back to the whole topic.
28641
+ ************************************************************/
28642
+ function ac_clear_parent_filter(gobj, event, kw, src) {
28643
+ gobj.priv.parent_filter = null;
28644
+ render_parent_filter(gobj);
28645
+ apply_row_filters(gobj);
28412
28646
  return 0;
28413
28647
  }
28414
28648
  /************************************************************
@@ -28719,19 +28953,12 @@ function ac_page_timeout(gobj, event, kw, src) {
28719
28953
  * cleared search box does not silently undo them.
28720
28954
  ************************************************************/
28721
28955
  function ac_search(gobj, event, kw, src) {
28722
- let tabulator = gobj_read_attr(gobj, "tabulator");
28723
- if (!tabulator) {
28956
+ if (!gobj_read_attr(gobj, "tabulator")) {
28724
28957
  log_error(`${gobj_short_name(gobj)}: no table to search`);
28725
28958
  return -1;
28726
28959
  }
28727
- let term = (kw && kw.text || "").toLowerCase();
28728
- if (!term) {
28729
- tabulator.clearFilter();
28730
- return 0;
28731
- }
28732
- tabulator.setFilter(function(data) {
28733
- return row_matches(data, term);
28734
- });
28960
+ gobj.priv.search_term = (kw && kw.text || "").toLowerCase();
28961
+ apply_row_filters(gobj);
28735
28962
  return 0;
28736
28963
  }
28737
28964
  /************************************************************
@@ -28979,6 +29206,21 @@ function create_gclass$5(gclass_name) {
28979
29206
  ac_show_hook_data$1,
28980
29207
  null
28981
29208
  ],
29209
+ [
29210
+ "EV_CHOOSE_LINKED",
29211
+ ac_choose_linked,
29212
+ null
29213
+ ],
29214
+ [
29215
+ "EV_FILTER_BY_PARENT",
29216
+ ac_filter_by_parent,
29217
+ null
29218
+ ],
29219
+ [
29220
+ "EV_CLEAR_PARENT_FILTER",
29221
+ ac_clear_parent_filter,
29222
+ null
29223
+ ],
28982
29224
  [
28983
29225
  "EV_SHOW_CELL_JSON",
28984
29226
  ac_show_cell_json,
@@ -29097,7 +29339,11 @@ function create_gclass$5(gclass_name) {
29097
29339
  ["EV_CLEAR_SELECTION", 0],
29098
29340
  ["EV_SELECT_ROWS", event_flag_t.EVF_OUTPUT_EVENT],
29099
29341
  ["EV_UNSELECT_ROWS", event_flag_t.EVF_OUTPUT_EVENT],
29100
- ["EV_SHOW_HOOK_DATA", event_flag_t.EVF_OUTPUT_EVENT],
29342
+ ["EV_SHOW_HOOK_DATA", 0],
29343
+ ["EV_CHOOSE_LINKED", 0],
29344
+ ["EV_FILTER_BY_PARENT", 0],
29345
+ ["EV_CLEAR_PARENT_FILTER", 0],
29346
+ ["EV_OPEN_LINKED", event_flag_t.EVF_OUTPUT_EVENT],
29101
29347
  ["EV_SHOW_CELL_JSON", 0],
29102
29348
  ["EV_SHOW_CELL_FILE", 0],
29103
29349
  ["EV_ASSET_LOADED", 0],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuneta/gobj-ui",
3
- "version": "7.23.160",
3
+ "version": "7.23.162",
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
+ }