@yuneta/gobj-ui 5.12.0 → 5.14.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.
@@ -15557,6 +15557,13 @@ function ac_mt_command_answer$1(gobj, event, kw, src) {
15557
15557
  break;
15558
15558
  case "create-node":
15559
15559
  case "update-node":
15560
+ (0, _yuneta_gobj_js.gobj_publish_event)(gobj, "EV_RECORD_WRITTEN", {
15561
+ treedb_name: (0, _yuneta_gobj_js.kw_get_str)(gobj, kw_command, "treedb_name", "", 0),
15562
+ topic_name: (0, _yuneta_gobj_js.kw_get_str)(gobj, kw_command, "topic_name", "", 0),
15563
+ record: (0, _yuneta_gobj_js.kw_get_dict)(gobj, kw_command, "record", {}, 0),
15564
+ created: command === "create-node"
15565
+ });
15566
+ break;
15560
15567
  case "delete-node": break;
15561
15568
  default: (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)} Command unknown: ${command}`);
15562
15569
  }
@@ -15962,6 +15969,7 @@ function create_gclass$5(gclass_name) {
15962
15969
  ["EV_CREATE_RECORD", 0],
15963
15970
  ["EV_UPDATE_RECORD", 0],
15964
15971
  ["EV_DELETE_RECORD", 0],
15972
+ ["EV_RECORD_WRITTEN", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT | _yuneta_gobj_js.event_flag_t.EVF_NO_WARN_SUBS],
15965
15973
  ["EV_REFRESH_TOPIC", 0],
15966
15974
  ["EV_OPEN_JSON", 0],
15967
15975
  ["EV_EXPAND_PATH", 0],
@@ -16142,7 +16150,9 @@ var PRIVATE_DATA$4 = {
16142
16150
  form: null,
16143
16151
  form_modal: null,
16144
16152
  schema_gobj: null,
16145
- schema_modal: null
16153
+ schema_modal: null,
16154
+ cell_json_gobj: null,
16155
+ cell_json_modal: null
16146
16156
  };
16147
16157
  var __gclass__$4 = null;
16148
16158
  function popup_mount_layer(gobj) {
@@ -16182,6 +16192,7 @@ function mt_stop$4(gobj) {
16182
16192
  if (shell) (0, _yuneta_gobj_js.gobj_unsubscribe_event)(shell, "EV_LANGUAGE_CHANGED", {}, gobj);
16183
16193
  close_form_dialog(gobj);
16184
16194
  close_schema_dialog(gobj);
16195
+ close_cell_json_dialog(gobj);
16185
16196
  table__destroy(gobj);
16186
16197
  }
16187
16198
  /***************************************************************
@@ -16190,6 +16201,7 @@ function mt_stop$4(gobj) {
16190
16201
  function mt_destroy$4(gobj) {
16191
16202
  close_form_dialog(gobj);
16192
16203
  close_schema_dialog(gobj);
16204
+ close_cell_json_dialog(gobj);
16193
16205
  destroy_ui$1(gobj);
16194
16206
  }
16195
16207
  /************************************************************
@@ -16595,6 +16607,20 @@ function create_tabulator(gobj) {
16595
16607
  if (col) return transform__treedb_value_2_table_value(gobj, col, value, row, field);
16596
16608
  return "???";
16597
16609
  }
16610
+ function json_cell_click(e, cell) {
16611
+ if (!cell.getElement().querySelector(".JSON_CELL")) return;
16612
+ e.stopPropagation();
16613
+ let pkey = desc.pkey || "id";
16614
+ let row_id = cell.getData()[pkey];
16615
+ if (row_id === void 0 || row_id === null) {
16616
+ (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: row without pkey '${pkey}', cannot open the json of '${cell.getField()}'`);
16617
+ return;
16618
+ }
16619
+ (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_SHOW_CELL_JSON", {
16620
+ row_id,
16621
+ col_id: cell.getField()
16622
+ }, gobj);
16623
+ }
16598
16624
  for (let i = 0; i < desc.cols.length; i++) {
16599
16625
  let col = desc.cols[i];
16600
16626
  if (!col.id || col.id[0] === "_") continue;
@@ -16648,7 +16674,10 @@ function create_tabulator(gobj) {
16648
16674
  case "array":
16649
16675
  case "list":
16650
16676
  case "coordinates":
16651
- case "blob": break;
16677
+ case "blob":
16678
+ case "gbuffer":
16679
+ cellClick = json_cell_click;
16680
+ break;
16652
16681
  case "boolean":
16653
16682
  hozAlign = "center";
16654
16683
  vertAlign = "middle";
@@ -16773,6 +16802,36 @@ function table__destroy(gobj) {
16773
16802
  }
16774
16803
  }
16775
16804
  /************************************************************
16805
+ * Cell of a col holding a JSON document (dict/list/blob/
16806
+ * template/coordinates/…): a one-line truncated preview,
16807
+ * marked as a link because the click opens the whole value
16808
+ * in a viewer (json_cell_click -> EV_SHOW_CELL_JSON).
16809
+ *
16810
+ * An empty document gets no link — there is nothing to open,
16811
+ * and json_cell_click uses its absence to ignore the click.
16812
+ *
16813
+ * Built as a DOM node and not as an HTML string: the preview
16814
+ * is raw record data, so it must never be parsed as markup.
16815
+ ************************************************************/
16816
+ function build_json_cell_preview(value) {
16817
+ if (value === null || value === void 0 || (0, _yuneta_gobj_js.json_size)(value) === 0) return "";
16818
+ let text = JSON.stringify(value);
16819
+ if (text && text.length > 20) text = text.substring(0, 20) + "…";
16820
+ return (0, _yuneta_gobj_js.createElement2)([
16821
+ "a",
16822
+ {
16823
+ class: "JSON_CELL",
16824
+ title: (0, i18next.t)("show json"),
16825
+ "data-i18n-title": "show json"
16826
+ },
16827
+ [["span", { class: "JSON_CELL_ICON icon yi-eye" }], [
16828
+ "span",
16829
+ { class: "JSON_CELL_PREVIEW" },
16830
+ text
16831
+ ]]
16832
+ ]);
16833
+ }
16834
+ /************************************************************
16776
16835
  * Convert a record column value from backend to frontend
16777
16836
  ************************************************************/
16778
16837
  function transform__treedb_value_2_table_value(gobj, col, value, row, field) {
@@ -16794,8 +16853,7 @@ function transform__treedb_value_2_table_value(gobj, col, value, row, field) {
16794
16853
  case "coordinates":
16795
16854
  case "blob":
16796
16855
  case "gbuffer":
16797
- value = JSON.stringify(value);
16798
- if (value && value.length > 20) value = value.substring(0, 20) + "…";
16856
+ value = build_json_cell_preview(value);
16799
16857
  break;
16800
16858
  case "enum":
16801
16859
  switch (field_desc.real_type) {
@@ -17099,6 +17157,94 @@ function close_schema_dialog(gobj) {
17099
17157
  teardown_schema_child(gobj);
17100
17158
  }
17101
17159
  /************************************************************
17160
+ * Show the JSON document held by ONE CELL — the value of a
17161
+ * dict/list/object/array/blob/template/coordinates col — in
17162
+ * the standardized adaptive dialog, rendered by a C_YUI_JSON
17163
+ * child. The cell can only show a 20-char preview; this is
17164
+ * where the value is actually readable (collapsed, searchable).
17165
+ *
17166
+ * Read-only and offline: the record is already in the table,
17167
+ * so no command is issued and nothing can be edited here (the
17168
+ * edit form remains the way to change the value).
17169
+ ************************************************************/
17170
+ function open_cell_json_dialog(gobj, row_id, col_id) {
17171
+ close_cell_json_dialog(gobj);
17172
+ let priv = gobj.priv;
17173
+ let tabulator = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "tabulator");
17174
+ if (!tabulator) {
17175
+ (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: no table, no cell to show`);
17176
+ return;
17177
+ }
17178
+ let row = tabulator.getRow(row_id);
17179
+ if (!row) {
17180
+ (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: row '${row_id}' not found in the table`);
17181
+ return;
17182
+ }
17183
+ let value = row.getData()[col_id];
17184
+ if (value === void 0) {
17185
+ (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: row '${row_id}' has no col '${col_id}'`);
17186
+ return;
17187
+ }
17188
+ let shell = yui_shell_of(gobj);
17189
+ if (!shell) {
17190
+ (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: no shell, cannot open the cell json`);
17191
+ return;
17192
+ }
17193
+ let json_view = (0, _yuneta_gobj_js.gobj_create_pure_child)("cell_" + (0, _yuneta_gobj_js.clean_name)((0, _yuneta_gobj_js.gobj_name)(gobj)), "C_YUI_JSON", {}, gobj);
17194
+ if (!json_view) {
17195
+ (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: cannot create the cell json viewer`);
17196
+ return;
17197
+ }
17198
+ priv.cell_json_gobj = json_view;
17199
+ (0, _yuneta_gobj_js.gobj_start)(json_view);
17200
+ let $box = (0, _yuneta_gobj_js.gobj_read_attr)(json_view, "$container");
17201
+ if (!$box) {
17202
+ (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: the cell json viewer built no $container`);
17203
+ teardown_cell_json_child(gobj);
17204
+ return;
17205
+ }
17206
+ priv.cell_json_modal = yui_shell_show_modal(shell, $box, {
17207
+ dialog: true,
17208
+ logical_class: "TREEDB_CELL_JSON_SHEET",
17209
+ title_prefix: String(row_id),
17210
+ title: col_id,
17211
+ t: i18next.t,
17212
+ on_close: function() {
17213
+ teardown_cell_json_child(gobj);
17214
+ }
17215
+ });
17216
+ (0, _yuneta_gobj_js.gobj_send_event)(json_view, "EV_SET_JSON", { json: value }, gobj);
17217
+ }
17218
+ /************************************************************
17219
+ * Destroy the hosted C_YUI_JSON child. Called from the cell
17220
+ * modal's on_close — the shell has already removed the dialog
17221
+ * DOM and retired its Escape / history entries.
17222
+ ************************************************************/
17223
+ function teardown_cell_json_child(gobj) {
17224
+ let priv = gobj.priv;
17225
+ if (priv.cell_json_gobj) {
17226
+ if ((0, _yuneta_gobj_js.gobj_is_running)(priv.cell_json_gobj)) (0, _yuneta_gobj_js.gobj_stop)(priv.cell_json_gobj);
17227
+ (0, _yuneta_gobj_js.gobj_destroy)(priv.cell_json_gobj);
17228
+ priv.cell_json_gobj = null;
17229
+ }
17230
+ priv.cell_json_modal = null;
17231
+ }
17232
+ /************************************************************
17233
+ * Close the cell json dialog (teardown, or a second open).
17234
+ * Closing the modal runs its on_close, which tears the
17235
+ * viewer child down.
17236
+ ************************************************************/
17237
+ function close_cell_json_dialog(gobj) {
17238
+ let priv = gobj.priv;
17239
+ if (priv.cell_json_modal) {
17240
+ let modal = priv.cell_json_modal;
17241
+ priv.cell_json_modal = null;
17242
+ modal.close();
17243
+ return;
17244
+ }
17245
+ teardown_cell_json_child(gobj);
17246
+ }
17247
+ /************************************************************
17102
17248
  * True if the topic's pkey col carries the "rowid" flag.
17103
17249
  ************************************************************/
17104
17250
  function pkey_is_rowid(gobj) {
@@ -17701,6 +17847,17 @@ function ac_refresh$1(gobj, event, kw, src) {
17701
17847
  return 0;
17702
17848
  }
17703
17849
  /************************************************************
17850
+ * Show the JSON document of one cell
17851
+ * {
17852
+ * row_id:
17853
+ * col_id:
17854
+ * }
17855
+ ************************************************************/
17856
+ function ac_show_cell_json(gobj, event, kw, src) {
17857
+ open_cell_json_dialog(gobj, kw.row_id, kw.col_id);
17858
+ return 0;
17859
+ }
17860
+ /************************************************************
17704
17861
  * Show the schema (desc) of this topic
17705
17862
  ************************************************************/
17706
17863
  function ac_show_schema(gobj, event, kw, src) {
@@ -17811,6 +17968,11 @@ function create_gclass$4(gclass_name) {
17811
17968
  ac_show_hook_data$1,
17812
17969
  null
17813
17970
  ],
17971
+ [
17972
+ "EV_SHOW_CELL_JSON",
17973
+ ac_show_cell_json,
17974
+ null
17975
+ ],
17814
17976
  [
17815
17977
  "EV_CHANGE_LOCALE",
17816
17978
  ac_change_locale,
@@ -17852,6 +18014,7 @@ function create_gclass$4(gclass_name) {
17852
18014
  ["EV_SELECT_ROWS", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT],
17853
18015
  ["EV_UNSELECT_ROWS", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT],
17854
18016
  ["EV_SHOW_HOOK_DATA", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT],
18017
+ ["EV_SHOW_CELL_JSON", 0],
17855
18018
  ["EV_CHANGE_LOCALE", 0],
17856
18019
  ["EV_REFRESH", 0],
17857
18020
  ["EV_SHOW_SCHEMA", 0],
@@ -15552,6 +15552,13 @@ function ac_mt_command_answer$1(gobj, event, kw, src) {
15552
15552
  break;
15553
15553
  case "create-node":
15554
15554
  case "update-node":
15555
+ gobj_publish_event(gobj, "EV_RECORD_WRITTEN", {
15556
+ treedb_name: kw_get_str(gobj, kw_command, "treedb_name", "", 0),
15557
+ topic_name: kw_get_str(gobj, kw_command, "topic_name", "", 0),
15558
+ record: kw_get_dict(gobj, kw_command, "record", {}, 0),
15559
+ created: command === "create-node"
15560
+ });
15561
+ break;
15555
15562
  case "delete-node": break;
15556
15563
  default: log_error(`${gobj_short_name(gobj)} Command unknown: ${command}`);
15557
15564
  }
@@ -15957,6 +15964,7 @@ function create_gclass$5(gclass_name) {
15957
15964
  ["EV_CREATE_RECORD", 0],
15958
15965
  ["EV_UPDATE_RECORD", 0],
15959
15966
  ["EV_DELETE_RECORD", 0],
15967
+ ["EV_RECORD_WRITTEN", event_flag_t.EVF_OUTPUT_EVENT | event_flag_t.EVF_NO_WARN_SUBS],
15960
15968
  ["EV_REFRESH_TOPIC", 0],
15961
15969
  ["EV_OPEN_JSON", 0],
15962
15970
  ["EV_EXPAND_PATH", 0],
@@ -16137,7 +16145,9 @@ var PRIVATE_DATA$4 = {
16137
16145
  form: null,
16138
16146
  form_modal: null,
16139
16147
  schema_gobj: null,
16140
- schema_modal: null
16148
+ schema_modal: null,
16149
+ cell_json_gobj: null,
16150
+ cell_json_modal: null
16141
16151
  };
16142
16152
  var __gclass__$4 = null;
16143
16153
  function popup_mount_layer(gobj) {
@@ -16177,6 +16187,7 @@ function mt_stop$4(gobj) {
16177
16187
  if (shell) gobj_unsubscribe_event(shell, "EV_LANGUAGE_CHANGED", {}, gobj);
16178
16188
  close_form_dialog(gobj);
16179
16189
  close_schema_dialog(gobj);
16190
+ close_cell_json_dialog(gobj);
16180
16191
  table__destroy(gobj);
16181
16192
  }
16182
16193
  /***************************************************************
@@ -16185,6 +16196,7 @@ function mt_stop$4(gobj) {
16185
16196
  function mt_destroy$4(gobj) {
16186
16197
  close_form_dialog(gobj);
16187
16198
  close_schema_dialog(gobj);
16199
+ close_cell_json_dialog(gobj);
16188
16200
  destroy_ui$1(gobj);
16189
16201
  }
16190
16202
  /************************************************************
@@ -16590,6 +16602,20 @@ function create_tabulator(gobj) {
16590
16602
  if (col) return transform__treedb_value_2_table_value(gobj, col, value, row, field);
16591
16603
  return "???";
16592
16604
  }
16605
+ function json_cell_click(e, cell) {
16606
+ if (!cell.getElement().querySelector(".JSON_CELL")) return;
16607
+ e.stopPropagation();
16608
+ let pkey = desc.pkey || "id";
16609
+ let row_id = cell.getData()[pkey];
16610
+ if (row_id === void 0 || row_id === null) {
16611
+ log_error(`${gobj_short_name(gobj)}: row without pkey '${pkey}', cannot open the json of '${cell.getField()}'`);
16612
+ return;
16613
+ }
16614
+ gobj_send_event(gobj, "EV_SHOW_CELL_JSON", {
16615
+ row_id,
16616
+ col_id: cell.getField()
16617
+ }, gobj);
16618
+ }
16593
16619
  for (let i = 0; i < desc.cols.length; i++) {
16594
16620
  let col = desc.cols[i];
16595
16621
  if (!col.id || col.id[0] === "_") continue;
@@ -16643,7 +16669,10 @@ function create_tabulator(gobj) {
16643
16669
  case "array":
16644
16670
  case "list":
16645
16671
  case "coordinates":
16646
- case "blob": break;
16672
+ case "blob":
16673
+ case "gbuffer":
16674
+ cellClick = json_cell_click;
16675
+ break;
16647
16676
  case "boolean":
16648
16677
  hozAlign = "center";
16649
16678
  vertAlign = "middle";
@@ -16768,6 +16797,36 @@ function table__destroy(gobj) {
16768
16797
  }
16769
16798
  }
16770
16799
  /************************************************************
16800
+ * Cell of a col holding a JSON document (dict/list/blob/
16801
+ * template/coordinates/…): a one-line truncated preview,
16802
+ * marked as a link because the click opens the whole value
16803
+ * in a viewer (json_cell_click -> EV_SHOW_CELL_JSON).
16804
+ *
16805
+ * An empty document gets no link — there is nothing to open,
16806
+ * and json_cell_click uses its absence to ignore the click.
16807
+ *
16808
+ * Built as a DOM node and not as an HTML string: the preview
16809
+ * is raw record data, so it must never be parsed as markup.
16810
+ ************************************************************/
16811
+ function build_json_cell_preview(value) {
16812
+ if (value === null || value === void 0 || json_size(value) === 0) return "";
16813
+ let text = JSON.stringify(value);
16814
+ if (text && text.length > 20) text = text.substring(0, 20) + "…";
16815
+ return createElement2([
16816
+ "a",
16817
+ {
16818
+ class: "JSON_CELL",
16819
+ title: t("show json"),
16820
+ "data-i18n-title": "show json"
16821
+ },
16822
+ [["span", { class: "JSON_CELL_ICON icon yi-eye" }], [
16823
+ "span",
16824
+ { class: "JSON_CELL_PREVIEW" },
16825
+ text
16826
+ ]]
16827
+ ]);
16828
+ }
16829
+ /************************************************************
16771
16830
  * Convert a record column value from backend to frontend
16772
16831
  ************************************************************/
16773
16832
  function transform__treedb_value_2_table_value(gobj, col, value, row, field) {
@@ -16789,8 +16848,7 @@ function transform__treedb_value_2_table_value(gobj, col, value, row, field) {
16789
16848
  case "coordinates":
16790
16849
  case "blob":
16791
16850
  case "gbuffer":
16792
- value = JSON.stringify(value);
16793
- if (value && value.length > 20) value = value.substring(0, 20) + "…";
16851
+ value = build_json_cell_preview(value);
16794
16852
  break;
16795
16853
  case "enum":
16796
16854
  switch (field_desc.real_type) {
@@ -17094,6 +17152,94 @@ function close_schema_dialog(gobj) {
17094
17152
  teardown_schema_child(gobj);
17095
17153
  }
17096
17154
  /************************************************************
17155
+ * Show the JSON document held by ONE CELL — the value of a
17156
+ * dict/list/object/array/blob/template/coordinates col — in
17157
+ * the standardized adaptive dialog, rendered by a C_YUI_JSON
17158
+ * child. The cell can only show a 20-char preview; this is
17159
+ * where the value is actually readable (collapsed, searchable).
17160
+ *
17161
+ * Read-only and offline: the record is already in the table,
17162
+ * so no command is issued and nothing can be edited here (the
17163
+ * edit form remains the way to change the value).
17164
+ ************************************************************/
17165
+ function open_cell_json_dialog(gobj, row_id, col_id) {
17166
+ close_cell_json_dialog(gobj);
17167
+ let priv = gobj.priv;
17168
+ let tabulator = gobj_read_attr(gobj, "tabulator");
17169
+ if (!tabulator) {
17170
+ log_error(`${gobj_short_name(gobj)}: no table, no cell to show`);
17171
+ return;
17172
+ }
17173
+ let row = tabulator.getRow(row_id);
17174
+ if (!row) {
17175
+ log_error(`${gobj_short_name(gobj)}: row '${row_id}' not found in the table`);
17176
+ return;
17177
+ }
17178
+ let value = row.getData()[col_id];
17179
+ if (value === void 0) {
17180
+ log_error(`${gobj_short_name(gobj)}: row '${row_id}' has no col '${col_id}'`);
17181
+ return;
17182
+ }
17183
+ let shell = yui_shell_of(gobj);
17184
+ if (!shell) {
17185
+ log_error(`${gobj_short_name(gobj)}: no shell, cannot open the cell json`);
17186
+ return;
17187
+ }
17188
+ let json_view = gobj_create_pure_child("cell_" + clean_name(gobj_name(gobj)), "C_YUI_JSON", {}, gobj);
17189
+ if (!json_view) {
17190
+ log_error(`${gobj_short_name(gobj)}: cannot create the cell json viewer`);
17191
+ return;
17192
+ }
17193
+ priv.cell_json_gobj = json_view;
17194
+ gobj_start(json_view);
17195
+ let $box = gobj_read_attr(json_view, "$container");
17196
+ if (!$box) {
17197
+ log_error(`${gobj_short_name(gobj)}: the cell json viewer built no $container`);
17198
+ teardown_cell_json_child(gobj);
17199
+ return;
17200
+ }
17201
+ priv.cell_json_modal = yui_shell_show_modal(shell, $box, {
17202
+ dialog: true,
17203
+ logical_class: "TREEDB_CELL_JSON_SHEET",
17204
+ title_prefix: String(row_id),
17205
+ title: col_id,
17206
+ t,
17207
+ on_close: function() {
17208
+ teardown_cell_json_child(gobj);
17209
+ }
17210
+ });
17211
+ gobj_send_event(json_view, "EV_SET_JSON", { json: value }, gobj);
17212
+ }
17213
+ /************************************************************
17214
+ * Destroy the hosted C_YUI_JSON child. Called from the cell
17215
+ * modal's on_close — the shell has already removed the dialog
17216
+ * DOM and retired its Escape / history entries.
17217
+ ************************************************************/
17218
+ function teardown_cell_json_child(gobj) {
17219
+ let priv = gobj.priv;
17220
+ if (priv.cell_json_gobj) {
17221
+ if (gobj_is_running(priv.cell_json_gobj)) gobj_stop(priv.cell_json_gobj);
17222
+ gobj_destroy(priv.cell_json_gobj);
17223
+ priv.cell_json_gobj = null;
17224
+ }
17225
+ priv.cell_json_modal = null;
17226
+ }
17227
+ /************************************************************
17228
+ * Close the cell json dialog (teardown, or a second open).
17229
+ * Closing the modal runs its on_close, which tears the
17230
+ * viewer child down.
17231
+ ************************************************************/
17232
+ function close_cell_json_dialog(gobj) {
17233
+ let priv = gobj.priv;
17234
+ if (priv.cell_json_modal) {
17235
+ let modal = priv.cell_json_modal;
17236
+ priv.cell_json_modal = null;
17237
+ modal.close();
17238
+ return;
17239
+ }
17240
+ teardown_cell_json_child(gobj);
17241
+ }
17242
+ /************************************************************
17097
17243
  * True if the topic's pkey col carries the "rowid" flag.
17098
17244
  ************************************************************/
17099
17245
  function pkey_is_rowid(gobj) {
@@ -17696,6 +17842,17 @@ function ac_refresh$1(gobj, event, kw, src) {
17696
17842
  return 0;
17697
17843
  }
17698
17844
  /************************************************************
17845
+ * Show the JSON document of one cell
17846
+ * {
17847
+ * row_id:
17848
+ * col_id:
17849
+ * }
17850
+ ************************************************************/
17851
+ function ac_show_cell_json(gobj, event, kw, src) {
17852
+ open_cell_json_dialog(gobj, kw.row_id, kw.col_id);
17853
+ return 0;
17854
+ }
17855
+ /************************************************************
17699
17856
  * Show the schema (desc) of this topic
17700
17857
  ************************************************************/
17701
17858
  function ac_show_schema(gobj, event, kw, src) {
@@ -17806,6 +17963,11 @@ function create_gclass$4(gclass_name) {
17806
17963
  ac_show_hook_data$1,
17807
17964
  null
17808
17965
  ],
17966
+ [
17967
+ "EV_SHOW_CELL_JSON",
17968
+ ac_show_cell_json,
17969
+ null
17970
+ ],
17809
17971
  [
17810
17972
  "EV_CHANGE_LOCALE",
17811
17973
  ac_change_locale,
@@ -17847,6 +18009,7 @@ function create_gclass$4(gclass_name) {
17847
18009
  ["EV_SELECT_ROWS", event_flag_t.EVF_OUTPUT_EVENT],
17848
18010
  ["EV_UNSELECT_ROWS", event_flag_t.EVF_OUTPUT_EVENT],
17849
18011
  ["EV_SHOW_HOOK_DATA", event_flag_t.EVF_OUTPUT_EVENT],
18012
+ ["EV_SHOW_CELL_JSON", 0],
17850
18013
  ["EV_CHANGE_LOCALE", 0],
17851
18014
  ["EV_REFRESH", 0],
17852
18015
  ["EV_SHOW_SCHEMA", 0],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuneta/gobj-ui",
3
- "version": "5.12.0",
3
+ "version": "5.14.0",
4
4
  "type": "module",
5
5
  "main": "dist/gobj-ui.cjs.js",
6
6
  "module": "dist/gobj-ui.es.js",
@@ -82,6 +82,24 @@
82
82
  }
83
83
  }
84
84
 
85
+ /* Cell of a col holding a JSON document: a truncated preview that opens
86
+ * the whole value in a viewer. Bulma already paints an <a> as a link
87
+ * (colour + pointer cursor, href or not — the click is an FSM event,
88
+ * not a navigation), so the only thing to state here is that a preview
89
+ * of a JSON fragment must stay on ONE line: wrapped, it would make the
90
+ * row of every structured record three lines tall. */
91
+ .JSON_CELL {
92
+ white-space: nowrap;
93
+ }
94
+ .JSON_CELL .JSON_CELL_ICON {
95
+ margin-right: 0.25rem;
96
+ vertical-align: middle;
97
+ }
98
+ .JSON_CELL .JSON_CELL_PREVIEW {
99
+ text-decoration: underline;
100
+ vertical-align: middle;
101
+ }
102
+
85
103
  .without-border {
86
104
  border: none !important;
87
105
  box-shadow: none !important;
@@ -147,6 +147,8 @@ let PRIVATE_DATA = {
147
147
  form_modal: null, // { close } handle of the adaptive dialog
148
148
  schema_gobj: null, // hosted C_YUI_JSON child (while schema open)
149
149
  schema_modal: null, // { close } handle of the schema dialog
150
+ cell_json_gobj: null, // hosted C_YUI_JSON child (while a cell is open)
151
+ cell_json_modal: null, // { close } handle of the cell dialog
150
152
  };
151
153
 
152
154
  let __gclass__ = null;
@@ -226,6 +228,7 @@ function mt_stop(gobj)
226
228
  }
227
229
  close_form_dialog(gobj);
228
230
  close_schema_dialog(gobj);
231
+ close_cell_json_dialog(gobj);
229
232
  table__destroy(gobj);
230
233
  }
231
234
 
@@ -240,6 +243,7 @@ function mt_destroy(gobj)
240
243
  * listener) and removes the dialog DOM. */
241
244
  close_form_dialog(gobj);
242
245
  close_schema_dialog(gobj);
246
+ close_cell_json_dialog(gobj);
243
247
  destroy_ui(gobj);
244
248
  }
245
249
 
@@ -731,6 +735,36 @@ function create_tabulator(gobj)
731
735
  return "???";
732
736
  }
733
737
 
738
+ /*
739
+ * Click on a JSON-document cell (dict/list/blob/template/…): the cell
740
+ * can only ever show a truncated preview, so open the whole value in
741
+ * the viewer. The kw carries the IDENTITY of the cell (row id + col
742
+ * id), never the value itself — the machine trace dumps the kw.
743
+ */
744
+ function json_cell_click(e, cell)
745
+ {
746
+ if(!cell.getElement().querySelector('.JSON_CELL')) {
747
+ return; // empty document: nothing to open
748
+ }
749
+ e.stopPropagation();
750
+
751
+ let pkey = desc.pkey || "id";
752
+ let row_id = cell.getData()[pkey];
753
+ if(row_id === undefined || row_id === null) {
754
+ log_error(
755
+ `${gobj_short_name(gobj)}: row without pkey '${pkey}',` +
756
+ ` cannot open the json of '${cell.getField()}'`
757
+ );
758
+ return;
759
+ }
760
+ gobj_send_event(
761
+ gobj,
762
+ "EV_SHOW_CELL_JSON",
763
+ {row_id: row_id, col_id: cell.getField()},
764
+ gobj
765
+ );
766
+ }
767
+
734
768
  for (let i = 0; i < desc.cols.length; i++) {
735
769
  let col = desc.cols[i];
736
770
  if(!col.id || col.id[0]==='_') {
@@ -794,6 +828,8 @@ function create_tabulator(gobj)
794
828
  case "list":
795
829
  case "coordinates":
796
830
  case "blob":
831
+ case "gbuffer":
832
+ cellClick = json_cell_click;
797
833
  break;
798
834
  case "boolean":
799
835
  hozAlign = "center";
@@ -984,6 +1020,41 @@ function table__destroy(gobj)
984
1020
  }
985
1021
  }
986
1022
 
1023
+ /************************************************************
1024
+ * Cell of a col holding a JSON document (dict/list/blob/
1025
+ * template/coordinates/…): a one-line truncated preview,
1026
+ * marked as a link because the click opens the whole value
1027
+ * in a viewer (json_cell_click -> EV_SHOW_CELL_JSON).
1028
+ *
1029
+ * An empty document gets no link — there is nothing to open,
1030
+ * and json_cell_click uses its absence to ignore the click.
1031
+ *
1032
+ * Built as a DOM node and not as an HTML string: the preview
1033
+ * is raw record data, so it must never be parsed as markup.
1034
+ ************************************************************/
1035
+ function build_json_cell_preview(value)
1036
+ {
1037
+ if(value === null || value === undefined || json_size(value) === 0) {
1038
+ return "";
1039
+ }
1040
+
1041
+ let text = JSON.stringify(value);
1042
+ if(text && text.length > 20) {
1043
+ text = text.substring(0, 20) + "…";
1044
+ }
1045
+
1046
+ return createElement2(
1047
+ ['a', {
1048
+ class: 'JSON_CELL',
1049
+ title: t('show json'),
1050
+ 'data-i18n-title': 'show json'
1051
+ }, [
1052
+ ['span', {class: 'JSON_CELL_ICON icon yi-eye'}],
1053
+ ['span', {class: 'JSON_CELL_PREVIEW'}, text]
1054
+ ]]
1055
+ );
1056
+ }
1057
+
987
1058
  /************************************************************
988
1059
  * Convert a record column value from backend to frontend
989
1060
  ************************************************************/
@@ -1013,10 +1084,7 @@ function transform__treedb_value_2_table_value(gobj, col, value, row, field)
1013
1084
  case "coordinates":
1014
1085
  case "blob":
1015
1086
  case "gbuffer":
1016
- value = JSON.stringify(value);
1017
- if(value && value.length > 20) {
1018
- value = value.substring(0, 20) + "…";
1019
- }
1087
+ value = build_json_cell_preview(value);
1020
1088
  break;
1021
1089
 
1022
1090
  case "enum":
@@ -1462,6 +1530,124 @@ function close_schema_dialog(gobj)
1462
1530
  teardown_schema_child(gobj);
1463
1531
  }
1464
1532
 
1533
+ /************************************************************
1534
+ * Show the JSON document held by ONE CELL — the value of a
1535
+ * dict/list/object/array/blob/template/coordinates col — in
1536
+ * the standardized adaptive dialog, rendered by a C_YUI_JSON
1537
+ * child. The cell can only show a 20-char preview; this is
1538
+ * where the value is actually readable (collapsed, searchable).
1539
+ *
1540
+ * Read-only and offline: the record is already in the table,
1541
+ * so no command is issued and nothing can be edited here (the
1542
+ * edit form remains the way to change the value).
1543
+ ************************************************************/
1544
+ function open_cell_json_dialog(gobj, row_id, col_id)
1545
+ {
1546
+ close_cell_json_dialog(gobj); // only one at a time
1547
+
1548
+ let priv = gobj.priv;
1549
+
1550
+ let tabulator = gobj_read_attr(gobj, "tabulator");
1551
+ if(!tabulator) {
1552
+ log_error(`${gobj_short_name(gobj)}: no table, no cell to show`);
1553
+ return;
1554
+ }
1555
+ let row = tabulator.getRow(row_id);
1556
+ if(!row) {
1557
+ log_error(`${gobj_short_name(gobj)}: row '${row_id}' not found in the table`);
1558
+ return;
1559
+ }
1560
+ let value = row.getData()[col_id];
1561
+ if(value === undefined) {
1562
+ log_error(`${gobj_short_name(gobj)}: row '${row_id}' has no col '${col_id}'`);
1563
+ return;
1564
+ }
1565
+
1566
+ let shell = yui_shell_of(gobj);
1567
+ if(!shell) {
1568
+ log_error(`${gobj_short_name(gobj)}: no shell, cannot open the cell json`);
1569
+ return;
1570
+ }
1571
+
1572
+ let json_view = gobj_create_pure_child(
1573
+ "cell_" + clean_name(gobj_name(gobj)),
1574
+ "C_YUI_JSON",
1575
+ {
1576
+ /* No `title`: the dialog header already titles it. */
1577
+ },
1578
+ gobj
1579
+ );
1580
+ if(!json_view) {
1581
+ log_error(`${gobj_short_name(gobj)}: cannot create the cell json viewer`);
1582
+ return;
1583
+ }
1584
+ priv.cell_json_gobj = json_view;
1585
+ gobj_start(json_view);
1586
+
1587
+ let $box = gobj_read_attr(json_view, "$container");
1588
+ if(!$box) {
1589
+ log_error(`${gobj_short_name(gobj)}: the cell json viewer built no $container`);
1590
+ teardown_cell_json_child(gobj);
1591
+ return;
1592
+ }
1593
+
1594
+ /* Title split in two halves, same contract as the schema dialog: the
1595
+ * record id is DATA (never translated) and the column carries its own
1596
+ * i18n key — the shared `col.id` one, so the header re-translates on a
1597
+ * language switch and reads as the raw id where the app defines no key
1598
+ * (exactly what an untranslated column header already shows). */
1599
+ priv.cell_json_modal = yui_shell_show_modal(shell, $box, {
1600
+ dialog: true,
1601
+ logical_class: "TREEDB_CELL_JSON_SHEET",
1602
+ title_prefix: String(row_id),
1603
+ title: col_id,
1604
+ t: t,
1605
+ on_close: function() {
1606
+ teardown_cell_json_child(gobj);
1607
+ }
1608
+ });
1609
+
1610
+ /* EV_SET_JSON and not the `json_data` attr: the attr renders the
1611
+ * tree but leaves the viewer in ST_IDLE, where a click to expand a
1612
+ * node is an event nobody handles. */
1613
+ gobj_send_event(json_view, "EV_SET_JSON", {json: value}, gobj);
1614
+ }
1615
+
1616
+ /************************************************************
1617
+ * Destroy the hosted C_YUI_JSON child. Called from the cell
1618
+ * modal's on_close — the shell has already removed the dialog
1619
+ * DOM and retired its Escape / history entries.
1620
+ ************************************************************/
1621
+ function teardown_cell_json_child(gobj)
1622
+ {
1623
+ let priv = gobj.priv;
1624
+ if(priv.cell_json_gobj) {
1625
+ if(gobj_is_running(priv.cell_json_gobj)) {
1626
+ gobj_stop(priv.cell_json_gobj);
1627
+ }
1628
+ gobj_destroy(priv.cell_json_gobj);
1629
+ priv.cell_json_gobj = null;
1630
+ }
1631
+ priv.cell_json_modal = null;
1632
+ }
1633
+
1634
+ /************************************************************
1635
+ * Close the cell json dialog (teardown, or a second open).
1636
+ * Closing the modal runs its on_close, which tears the
1637
+ * viewer child down.
1638
+ ************************************************************/
1639
+ function close_cell_json_dialog(gobj)
1640
+ {
1641
+ let priv = gobj.priv;
1642
+ if(priv.cell_json_modal) {
1643
+ let modal = priv.cell_json_modal;
1644
+ priv.cell_json_modal = null;
1645
+ modal.close(); // -> on_close -> teardown_cell_json_child
1646
+ return;
1647
+ }
1648
+ teardown_cell_json_child(gobj);
1649
+ }
1650
+
1465
1651
  /************************************************************
1466
1652
  * True if the topic's pkey col carries the "rowid" flag.
1467
1653
  ************************************************************/
@@ -2418,6 +2604,20 @@ function ac_refresh(gobj, event, kw, src)
2418
2604
  return 0;
2419
2605
  }
2420
2606
 
2607
+ /************************************************************
2608
+ * Show the JSON document of one cell
2609
+ * {
2610
+ * row_id:
2611
+ * col_id:
2612
+ * }
2613
+ ************************************************************/
2614
+ function ac_show_cell_json(gobj, event, kw, src)
2615
+ {
2616
+ open_cell_json_dialog(gobj, kw.row_id, kw.col_id);
2617
+
2618
+ return 0;
2619
+ }
2620
+
2421
2621
  /************************************************************
2422
2622
  * Show the schema (desc) of this topic
2423
2623
  ************************************************************/
@@ -2500,6 +2700,7 @@ function create_gclass(gclass_name)
2500
2700
  ["EV_COPY_ROWS", ac_copy_rows, null],
2501
2701
  ["EV_PASTE_ROWS", ac_paste_rows, null],
2502
2702
  ["EV_SHOW_HOOK_DATA", ac_show_hook_data, null],
2703
+ ["EV_SHOW_CELL_JSON", ac_show_cell_json, null],
2503
2704
  ["EV_CHANGE_LOCALE", ac_change_locale, null],
2504
2705
  ["EV_REFRESH", ac_refresh, null],
2505
2706
  ["EV_SHOW_SCHEMA", ac_show_schema, null],
@@ -2527,6 +2728,7 @@ function create_gclass(gclass_name)
2527
2728
  ["EV_SELECT_ROWS", event_flag_t.EVF_OUTPUT_EVENT],
2528
2729
  ["EV_UNSELECT_ROWS", event_flag_t.EVF_OUTPUT_EVENT],
2529
2730
  ["EV_SHOW_HOOK_DATA", event_flag_t.EVF_OUTPUT_EVENT],
2731
+ ["EV_SHOW_CELL_JSON", 0],
2530
2732
  ["EV_CHANGE_LOCALE", 0],
2531
2733
  ["EV_REFRESH", 0],
2532
2734
  ["EV_SHOW_SCHEMA", 0],
@@ -1717,6 +1717,24 @@ function ac_mt_command_answer(gobj, event, kw, src)
1717
1717
 
1718
1718
  case "create-node":
1719
1719
  case "update-node":
1720
+ /*
1721
+ * The view refreshes itself from the treedb's own
1722
+ * EV_TREEDB_NODE_* events, which arrive for EVERY writer. This
1723
+ * says something those cannot: THIS view has just written THIS
1724
+ * record, and it succeeded. A host whose save is not finished
1725
+ * with one record — a schema editor, where a column change also
1726
+ * has to raise the versions that publish it — needs exactly that,
1727
+ * and cannot use the node events without answering its own writes
1728
+ * in a loop.
1729
+ */
1730
+ gobj_publish_event(gobj, "EV_RECORD_WRITTEN", {
1731
+ treedb_name: kw_get_str(gobj, kw_command, "treedb_name", "", 0),
1732
+ topic_name: kw_get_str(gobj, kw_command, "topic_name", "", 0),
1733
+ record: kw_get_dict(gobj, kw_command, "record", {}, 0),
1734
+ created: (command === "create-node")
1735
+ });
1736
+ break;
1737
+
1720
1738
  case "delete-node":
1721
1739
  // Don't process by here, process on subscribed events.
1722
1740
  break;
@@ -2239,6 +2257,8 @@ function create_gclass(gclass_name)
2239
2257
  ["EV_CREATE_RECORD", 0],
2240
2258
  ["EV_UPDATE_RECORD", 0],
2241
2259
  ["EV_DELETE_RECORD", 0],
2260
+ ["EV_RECORD_WRITTEN", event_flag_t.EVF_OUTPUT_EVENT|
2261
+ event_flag_t.EVF_NO_WARN_SUBS],
2242
2262
  ["EV_REFRESH_TOPIC", 0],
2243
2263
  ["EV_OPEN_JSON", 0],
2244
2264
  ["EV_EXPAND_PATH", 0],