@yuneta/gobj-ui 7.23.192 → 7.23.194
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.
- package/README.md +24 -0
- package/dist/gobj-ui.cjs.js +239 -62
- package/dist/gobj-ui.es.js +239 -62
- package/package.json +1 -1
- package/src/c_g6_nodes_tree.js +15 -2
- package/src/c_yui_treedb_topic_with_form.js +143 -49
- package/src/c_yui_treedb_topics.js +68 -15
- package/src/graph_save_plan.js +40 -0
- package/src/graph_save_plan.test.js +57 -1
- package/src/yui_row_search.js +7 -2
- package/src/yui_row_search.test.js +22 -0
- package/src/yui_table_select.js +50 -0
- package/src/yui_table_select.test.js +54 -0
package/README.md
CHANGED
|
@@ -774,6 +774,30 @@ A refused write puts the topic back to what the treedb has. Leaving the typed
|
|
|
774
774
|
value on screen is tolerable for a form, which stays open on the values it
|
|
775
775
|
failed with; a cell edited in place would just look saved.
|
|
776
776
|
|
|
777
|
+
**The form stays open until the backend answers** (7.23.193). With
|
|
778
|
+
`form_waits_for_answer` set — `C_YUI_TREEDB_TOPICS` sets it — a Save leaves the
|
|
779
|
+
form open and busy, and `EV_CREATE_RECORD` / `EV_UPDATE_RECORD` carry a
|
|
780
|
+
`form_write` serial. The host echoes it back in `EV_WRITE_DONE` (the form
|
|
781
|
+
closes) or `EV_WRITE_REFUSED` (it stays open on what was typed), and it must
|
|
782
|
+
answer EVERY way the write can end: refused before it left (read-only, no
|
|
783
|
+
session) as well as answered by the backend, or the form waits forever. A host
|
|
784
|
+
that does not answer leaves the attribute off, and the form closes when the
|
|
785
|
+
write is published, as before 7.23.193 — whatever the backend says.
|
|
786
|
+
|
|
787
|
+
```js
|
|
788
|
+
// A host of C_YUI_TREEDB_TOPIC_WITH_FORM that answers the form's writes
|
|
789
|
+
gobj_create_pure_child(name, "C_YUI_TREEDB_TOPIC_WITH_FORM",
|
|
790
|
+
{topic_name: "users", form_waits_for_answer: true /* ... */}, host);
|
|
791
|
+
// on the backend's answer to the write that carried kw.form_write:
|
|
792
|
+
gobj_send_event(topic_form, ok ? "EV_WRITE_DONE" : "EV_WRITE_REFUSED",
|
|
793
|
+
{form_write: kw.form_write}, host);
|
|
794
|
+
```
|
|
795
|
+
|
|
796
|
+
**A delete is resolved by IDENTITY, never by position** (7.23.193). The row's
|
|
797
|
+
`id` crosses the confirmation dialog; a Tabulator position is regenerated by
|
|
798
|
+
every row the node events add or remove while the dialog is open, and it used
|
|
799
|
+
to delete the row that sat there when the person answered.
|
|
800
|
+
|
|
777
801
|
### Reading a topic table: filters, columns, CSV
|
|
778
802
|
|
|
779
803
|
`C_YUI_TREEDB_TOPIC_WITH_FORM` had one global search box over the loaded rows.
|
package/dist/gobj-ui.cjs.js
CHANGED
|
@@ -24549,7 +24549,8 @@ function process_treedb_descs$1(gobj) {
|
|
|
24549
24549
|
desc,
|
|
24550
24550
|
with_remote_paging: (0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "with_remote_paging"),
|
|
24551
24551
|
page_size: (0, _yuneta_gobj_js.gobj_read_integer_attr)(gobj, "page_size"),
|
|
24552
|
-
with_selection_bar: (0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "with_selection_bar")
|
|
24552
|
+
with_selection_bar: (0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "with_selection_bar"),
|
|
24553
|
+
form_waits_for_answer: true
|
|
24553
24554
|
};
|
|
24554
24555
|
let id = `${(0, _yuneta_gobj_js.gobj_name)(gobj)}?${desc.topic_name}`;
|
|
24555
24556
|
let gobj_topic_form = (0, _yuneta_gobj_js.gobj_create_service)(id, "C_YUI_TREEDB_TOPIC_WITH_FORM", kw_topic_form, gobj);
|
|
@@ -24695,7 +24696,7 @@ function treedb_nodes$1(gobj, treedb_name, topic_name, options, page, purpose) {
|
|
|
24695
24696
|
/************************************************************
|
|
24696
24697
|
* Command to remote service
|
|
24697
24698
|
************************************************************/
|
|
24698
|
-
function treedb_update_node$1(gobj, treedb_name, topic_name, record, options) {
|
|
24699
|
+
function treedb_update_node$1(gobj, treedb_name, topic_name, record, options, form_write) {
|
|
24699
24700
|
let command = "update-node";
|
|
24700
24701
|
let gobj_remote_yuno = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(gobj, "gobj_remote_yuno");
|
|
24701
24702
|
let kw = {
|
|
@@ -24706,8 +24707,13 @@ function treedb_update_node$1(gobj, treedb_name, topic_name, record, options) {
|
|
|
24706
24707
|
options: options || {}
|
|
24707
24708
|
};
|
|
24708
24709
|
kw.__md_command__ = { topic_name };
|
|
24710
|
+
if (form_write) kw.__md_command__.form_write = form_write;
|
|
24709
24711
|
let ret = (0, _yuneta_gobj_js.gobj_command)(gobj_remote_yuno, command, kw, gobj);
|
|
24710
|
-
if (ret)
|
|
24712
|
+
if (ret) {
|
|
24713
|
+
(0, _yuneta_gobj_js.log_error)(ret);
|
|
24714
|
+
return -1;
|
|
24715
|
+
}
|
|
24716
|
+
return 0;
|
|
24711
24717
|
}
|
|
24712
24718
|
/************************************************************
|
|
24713
24719
|
* Command to remote service
|
|
@@ -24970,6 +24976,7 @@ function ac_mt_command_answer$2(gobj, event, kw, src) {
|
|
|
24970
24976
|
if (command === "update-node" || command === "create-node") {
|
|
24971
24977
|
let failed_topic = (0, _yuneta_gobj_js.kw_get_str)(gobj, kw_command, "topic_name", "", 0);
|
|
24972
24978
|
if (failed_topic) (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_REFRESH_TOPIC", { topic_name: failed_topic }, gobj);
|
|
24979
|
+
answer_form_write(gobj, get_gobj_formtable(gobj, failed_topic), (0, _yuneta_gobj_js.kw_get_int)(gobj, kw_command, "form_write", 0, 0), false);
|
|
24973
24980
|
}
|
|
24974
24981
|
}
|
|
24975
24982
|
return 0;
|
|
@@ -25006,6 +25013,7 @@ function ac_mt_command_answer$2(gobj, event, kw, src) {
|
|
|
25006
25013
|
created: command === "create-node",
|
|
25007
25014
|
command
|
|
25008
25015
|
});
|
|
25016
|
+
answer_form_write(gobj, get_gobj_formtable(gobj, (0, _yuneta_gobj_js.kw_get_str)(gobj, kw_command, "topic_name", "", 0)), (0, _yuneta_gobj_js.kw_get_int)(gobj, kw_command, "form_write", 0, 0), true);
|
|
25009
25017
|
break;
|
|
25010
25018
|
case "delete-node": break;
|
|
25011
25019
|
default: (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)} Command unknown: ${command}`);
|
|
@@ -25221,15 +25229,24 @@ function ac_treedb_node_deleted$1(gobj, event, kw, src) {
|
|
|
25221
25229
|
* }
|
|
25222
25230
|
********************************************/
|
|
25223
25231
|
function ac_create_record(gobj, event, kw, src) {
|
|
25224
|
-
|
|
25232
|
+
let form_write = kw && kw.form_write || 0;
|
|
25233
|
+
if (refuse_if_readonly$3(gobj, event)) {
|
|
25234
|
+
answer_form_write(gobj, src, form_write, false);
|
|
25235
|
+
return -1;
|
|
25236
|
+
}
|
|
25225
25237
|
let treedb_name = (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "treedb_name");
|
|
25226
25238
|
let topic_name = (0, _yuneta_gobj_js.gobj_read_attr)(src, "topic_name");
|
|
25227
25239
|
let record = kw.record;
|
|
25228
|
-
|
|
25240
|
+
if (treedb_update_node$1(gobj, treedb_name, topic_name, record, {
|
|
25229
25241
|
list_dict: true,
|
|
25230
25242
|
create: true,
|
|
25243
|
+
create_only: true,
|
|
25231
25244
|
autolink: true
|
|
25232
|
-
})
|
|
25245
|
+
}, form_write) < 0) {
|
|
25246
|
+
answer_form_write(gobj, src, form_write, false);
|
|
25247
|
+
return -1;
|
|
25248
|
+
}
|
|
25249
|
+
return 0;
|
|
25233
25250
|
}
|
|
25234
25251
|
/********************************************
|
|
25235
25252
|
* A topic table wants its records as json, WITH the metadata of
|
|
@@ -25379,14 +25396,37 @@ function ac_update_field(gobj, event, kw, src) {
|
|
|
25379
25396
|
* }
|
|
25380
25397
|
********************************************/
|
|
25381
25398
|
function ac_update_record(gobj, event, kw, src) {
|
|
25382
|
-
|
|
25399
|
+
let form_write = kw && kw.form_write || 0;
|
|
25400
|
+
if (refuse_if_readonly$3(gobj, event)) {
|
|
25401
|
+
answer_form_write(gobj, src, form_write, false);
|
|
25402
|
+
return -1;
|
|
25403
|
+
}
|
|
25383
25404
|
let treedb_name = (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "treedb_name");
|
|
25384
25405
|
let topic_name = (0, _yuneta_gobj_js.gobj_read_attr)(src, "topic_name");
|
|
25385
25406
|
let record = kw.record;
|
|
25386
|
-
|
|
25407
|
+
if (treedb_update_node$1(gobj, treedb_name, topic_name, record, {
|
|
25387
25408
|
list_dict: true,
|
|
25388
25409
|
autolink: true
|
|
25389
|
-
})
|
|
25410
|
+
}, form_write) < 0) {
|
|
25411
|
+
answer_form_write(gobj, src, form_write, false);
|
|
25412
|
+
return -1;
|
|
25413
|
+
}
|
|
25414
|
+
return 0;
|
|
25415
|
+
}
|
|
25416
|
+
/************************************************************
|
|
25417
|
+
* Tell a topic form how its write ended: EV_WRITE_DONE closes
|
|
25418
|
+
* it, EV_WRITE_REFUSED leaves it open on what was typed. Every
|
|
25419
|
+
* way a form's write can end passes here -- refused before it
|
|
25420
|
+
* left (read-only, no session) or answered by the backend -- so
|
|
25421
|
+
* a form never waits for an answer that will not come.
|
|
25422
|
+
************************************************************/
|
|
25423
|
+
function answer_form_write(gobj, gobj_topic_form, form_write, ok) {
|
|
25424
|
+
if (!form_write) return;
|
|
25425
|
+
if (!gobj_topic_form) {
|
|
25426
|
+
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: no topic form to answer write ${form_write}`);
|
|
25427
|
+
return;
|
|
25428
|
+
}
|
|
25429
|
+
(0, _yuneta_gobj_js.gobj_send_event)(gobj_topic_form, ok ? "EV_WRITE_DONE" : "EV_WRITE_REFUSED", { form_write }, gobj);
|
|
25390
25430
|
}
|
|
25391
25431
|
/********************************************
|
|
25392
25432
|
* Event from formtable
|
|
@@ -25757,6 +25797,46 @@ function yui_selected_rows(tabulator) {
|
|
|
25757
25797
|
}
|
|
25758
25798
|
}
|
|
25759
25799
|
/***************************************************************
|
|
25800
|
+
* The IDENTITY of some rows: the value of the table's `index`
|
|
25801
|
+
* field (Tabulator's default is `id`). This is what crosses a
|
|
25802
|
+
* confirmation dialog -- never the rows' POSITIONS, which the
|
|
25803
|
+
* table regenerates with every addData / deleteRow while the
|
|
25804
|
+
* dialog is open, so a position names whatever row sits there
|
|
25805
|
+
* when the person answers.
|
|
25806
|
+
***************************************************************/
|
|
25807
|
+
function yui_row_ids(tabulator, rows) {
|
|
25808
|
+
let field = tabulator && tabulator.options && tabulator.options.index || "id";
|
|
25809
|
+
let ids = [];
|
|
25810
|
+
for (let row of rows || []) {
|
|
25811
|
+
let id = row ? row[field] : void 0;
|
|
25812
|
+
if (id !== void 0 && id !== null && id !== "") ids.push(id);
|
|
25813
|
+
}
|
|
25814
|
+
return ids;
|
|
25815
|
+
}
|
|
25816
|
+
/***************************************************************
|
|
25817
|
+
* The rows that carry these ids NOW -> {rows, missing}.
|
|
25818
|
+
* `rows` are the rows' data; `missing` the ids no row carries
|
|
25819
|
+
* any more. A missing id is never replaced by another row.
|
|
25820
|
+
***************************************************************/
|
|
25821
|
+
function yui_rows_by_ids(tabulator, ids) {
|
|
25822
|
+
let rows = [];
|
|
25823
|
+
let missing = [];
|
|
25824
|
+
for (let id of ids || []) {
|
|
25825
|
+
let row = null;
|
|
25826
|
+
if (tabulator && typeof tabulator.getRow === "function") try {
|
|
25827
|
+
row = tabulator.getRow(id) || null;
|
|
25828
|
+
} catch (e) {
|
|
25829
|
+
row = null;
|
|
25830
|
+
}
|
|
25831
|
+
if (row) rows.push(row.getData());
|
|
25832
|
+
else missing.push(id);
|
|
25833
|
+
}
|
|
25834
|
+
return {
|
|
25835
|
+
rows,
|
|
25836
|
+
missing
|
|
25837
|
+
};
|
|
25838
|
+
}
|
|
25839
|
+
/***************************************************************
|
|
25760
25840
|
* Drop the selection (after acting on it, or after a
|
|
25761
25841
|
* reload that leaves the ticked rows behind).
|
|
25762
25842
|
***************************************************************/
|
|
@@ -26184,10 +26264,18 @@ function yui_icon_is_defined(name) {
|
|
|
26184
26264
|
***********************************************************************/
|
|
26185
26265
|
var MAX_DEPTH = 4;
|
|
26186
26266
|
/***************************************************************
|
|
26187
|
-
*
|
|
26267
|
+
* A REFERENCE to a node: an fkey as `nodes` answers it with
|
|
26268
|
+
* `list_dict` -- `{id, topic_name, hook_name}` -- or a hook's child
|
|
26269
|
+
* as the node events deliver it, `{id, topic_name}`. It carries
|
|
26270
|
+
* nothing but those keys; an object with more is data.
|
|
26188
26271
|
***************************************************************/
|
|
26272
|
+
var REF_KEYS = /* @__PURE__ */ new Set([
|
|
26273
|
+
"id",
|
|
26274
|
+
"topic_name",
|
|
26275
|
+
"hook_name"
|
|
26276
|
+
]);
|
|
26189
26277
|
function is_fkey_ref(value) {
|
|
26190
|
-
return !!value && typeof value === "object" && !Array.isArray(value) && "id" in value && "topic_name" in value &&
|
|
26278
|
+
return !!value && typeof value === "object" && !Array.isArray(value) && "id" in value && "topic_name" in value && Object.keys(value).every((k) => REF_KEYS.has(k));
|
|
26191
26279
|
}
|
|
26192
26280
|
/***************************************************************
|
|
26193
26281
|
* A hook read with `hook_size`: the COUNT of its children,
|
|
@@ -26447,6 +26535,7 @@ var attrs_table$5 = [
|
|
|
26447
26535
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_delete_button", 0, true, "Button toolbar DELETE"),
|
|
26448
26536
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_copy_button", 0, true, "Button toolbar COPY"),
|
|
26449
26537
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_paste_button", 0, true, "Button toolbar PASTE"),
|
|
26538
|
+
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "form_waits_for_answer", 0, false, "The form stays open (busy) after a Save until the host answers EV_WRITE_DONE (close) or EV_WRITE_REFUSED (stay open on what was typed). Off: the form closes when the write is published, whatever the backend says. The host must echo the `form_write` serial of EV_CREATE_RECORD / EV_UPDATE_RECORD; C_YUI_TREEDB_TOPICS does"),
|
|
26450
26539
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_refresh_button", 0, true, "Button toolbar REFRESH"),
|
|
26451
26540
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_search_button", 0, true, "Button toolbar SEARCH"),
|
|
26452
26541
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_schema_button", 0, true, "Button toolbar SCHEMA (show the topic's desc)"),
|
|
@@ -26622,6 +26711,8 @@ function popup_mount_layer(gobj) {
|
|
|
26622
26711
|
function mt_create$5(gobj) {
|
|
26623
26712
|
gobj.priv.reading_files = 0;
|
|
26624
26713
|
gobj.priv.read_serial = 0;
|
|
26714
|
+
gobj.priv.awaiting_write = 0;
|
|
26715
|
+
gobj.priv.write_serial = 0;
|
|
26625
26716
|
let name = (0, _yuneta_gobj_js.clean_name)((0, _yuneta_gobj_js.gobj_name)(gobj));
|
|
26626
26717
|
(0, _yuneta_gobj_js.gobj_write_attr)(gobj, "table_id", "table" + name);
|
|
26627
26718
|
(0, _yuneta_gobj_js.gobj_write_attr)(gobj, "toolbar_id", "toolbar" + name);
|
|
@@ -27140,14 +27231,11 @@ function render_selection_state(gobj) {
|
|
|
27140
27231
|
let $container = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
|
|
27141
27232
|
let $delete = $container ? $container.querySelector(`.button-delete-record`) : null;
|
|
27142
27233
|
let $copy = $container ? $container.querySelector(`.button-copy-record`) : null;
|
|
27143
|
-
|
|
27144
|
-
|
|
27145
|
-
|
|
27146
|
-
|
|
27147
|
-
|
|
27148
|
-
$delete.setAttribute("disabled", true);
|
|
27149
|
-
$copy.setAttribute("disabled", true);
|
|
27150
|
-
}
|
|
27234
|
+
let enable = yui_selected_rows((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "tabulator")).length > 0 && (0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "editable");
|
|
27235
|
+
for (let $button of [$delete, $copy]) {
|
|
27236
|
+
if (!$button) continue;
|
|
27237
|
+
if (enable) $button.removeAttribute("disabled");
|
|
27238
|
+
else $button.setAttribute("disabled", true);
|
|
27151
27239
|
}
|
|
27152
27240
|
render_selection_bar(gobj);
|
|
27153
27241
|
}
|
|
@@ -27455,7 +27543,7 @@ function create_tabulator(gobj) {
|
|
|
27455
27543
|
hozAlign,
|
|
27456
27544
|
formatter: colFormatter
|
|
27457
27545
|
};
|
|
27458
|
-
if (max_col_width > 0) colDef.
|
|
27546
|
+
if (max_col_width > 0) colDef.maxInitialWidth = max_col_width;
|
|
27459
27547
|
if (with_header_filters) apply_header_filter(colDef, field_desc);
|
|
27460
27548
|
if (with_inline_edit) inline_editor_for(colDef, col, field_desc);
|
|
27461
27549
|
if (vertAlign) colDef.vertAlign = vertAlign;
|
|
@@ -27493,10 +27581,7 @@ function create_tabulator(gobj) {
|
|
|
27493
27581
|
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_EDIT_RECORD", { index }, gobj);
|
|
27494
27582
|
} else if (e.target.closest(".remove")) {
|
|
27495
27583
|
e.stopPropagation();
|
|
27496
|
-
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_DELETE_ROWS", {
|
|
27497
|
-
index,
|
|
27498
|
-
row
|
|
27499
|
-
}, gobj);
|
|
27584
|
+
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_DELETE_ROWS", { row }, gobj);
|
|
27500
27585
|
}
|
|
27501
27586
|
}
|
|
27502
27587
|
});
|
|
@@ -28034,6 +28119,7 @@ function form_may_close(gobj) {
|
|
|
28034
28119
|
function teardown_form_child(gobj) {
|
|
28035
28120
|
let priv = gobj.priv;
|
|
28036
28121
|
priv.reading_files = 0;
|
|
28122
|
+
priv.awaiting_write = 0;
|
|
28037
28123
|
if (priv.form) {
|
|
28038
28124
|
if ((0, _yuneta_gobj_js.gobj_is_running)(priv.form)) (0, _yuneta_gobj_js.gobj_stop)(priv.form);
|
|
28039
28125
|
(0, _yuneta_gobj_js.gobj_destroy)(priv.form);
|
|
@@ -29043,25 +29129,31 @@ function ac_edition_mode(gobj, event, kw, src) {
|
|
|
29043
29129
|
let $button_copy_record = $container.querySelector(`.button-copy-record`);
|
|
29044
29130
|
let $button_paste_record = $container.querySelector(`.button-paste-record`);
|
|
29045
29131
|
if (editable) {
|
|
29046
|
-
$button_edit_record.classList.add("is-primary");
|
|
29047
|
-
$button_new_record
|
|
29048
|
-
|
|
29132
|
+
if ($button_edit_record) $button_edit_record.classList.add("is-primary");
|
|
29133
|
+
if ($button_new_record) {
|
|
29134
|
+
$button_new_record.classList.add("is-info");
|
|
29135
|
+
$button_new_record.removeAttribute("disabled");
|
|
29136
|
+
}
|
|
29137
|
+
if ($button_delete_record) $button_delete_record.classList.add("is-danger");
|
|
29049
29138
|
tabulator.showColumn("_operation");
|
|
29050
29139
|
tabulator.showColumn("_check_box_state_");
|
|
29051
|
-
$button_new_record.removeAttribute("disabled");
|
|
29052
29140
|
if ($button_paste_record) $button_paste_record.removeAttribute("disabled");
|
|
29053
29141
|
if (yui_selected_rows(tabulator).length) {
|
|
29054
|
-
$button_delete_record.removeAttribute("disabled");
|
|
29142
|
+
if ($button_delete_record) $button_delete_record.removeAttribute("disabled");
|
|
29055
29143
|
if ($button_copy_record) $button_copy_record.removeAttribute("disabled");
|
|
29056
29144
|
}
|
|
29057
29145
|
} else {
|
|
29058
|
-
$button_edit_record.classList.remove("is-primary");
|
|
29059
|
-
$button_new_record
|
|
29060
|
-
|
|
29146
|
+
if ($button_edit_record) $button_edit_record.classList.remove("is-primary");
|
|
29147
|
+
if ($button_new_record) {
|
|
29148
|
+
$button_new_record.classList.remove("is-info");
|
|
29149
|
+
$button_new_record.setAttribute("disabled", true);
|
|
29150
|
+
}
|
|
29151
|
+
if ($button_delete_record) {
|
|
29152
|
+
$button_delete_record.classList.remove("is-danger");
|
|
29153
|
+
$button_delete_record.setAttribute("disabled", true);
|
|
29154
|
+
}
|
|
29061
29155
|
tabulator.hideColumn("_operation");
|
|
29062
29156
|
tabulator.hideColumn("_check_box_state_");
|
|
29063
|
-
$button_new_record.setAttribute("disabled", true);
|
|
29064
|
-
$button_delete_record.setAttribute("disabled", true);
|
|
29065
29157
|
if ($button_copy_record) $button_copy_record.setAttribute("disabled", true);
|
|
29066
29158
|
if ($button_paste_record) $button_paste_record.setAttribute("disabled", true);
|
|
29067
29159
|
}
|
|
@@ -29103,7 +29195,12 @@ function ac_new_row(gobj, event, kw, src) {
|
|
|
29103
29195
|
* kw {} empty
|
|
29104
29196
|
* - From the column _operation delete icon inside a row
|
|
29105
29197
|
* It will delete this one row
|
|
29106
|
-
* kw {
|
|
29198
|
+
* kw {row:}
|
|
29199
|
+
*
|
|
29200
|
+
* What crosses the confirmation is the rows' IDENTITY, never their
|
|
29201
|
+
* position: the view applies EV_TREEDB_NODE_* of every writer while the
|
|
29202
|
+
* dialog is open, and a position names whatever row sits there when the
|
|
29203
|
+
* person answers -- with force:true (A6 of the 2026-09-21 review).
|
|
29107
29204
|
************************************************************/
|
|
29108
29205
|
function ac_delete_rows(gobj, event, kw, src) {
|
|
29109
29206
|
if (refuse_if_readonly$2(gobj, event)) return -1;
|
|
@@ -29117,17 +29214,20 @@ function ac_delete_rows(gobj, event, kw, src) {
|
|
|
29117
29214
|
});
|
|
29118
29215
|
return 0;
|
|
29119
29216
|
}
|
|
29120
|
-
confirm_then$1(gobj, build_delete_question(gobj, rows), {
|
|
29217
|
+
confirm_then$1(gobj, build_delete_question(gobj, rows), {
|
|
29218
|
+
what: "delete_selection",
|
|
29219
|
+
ids: yui_row_ids(tabulator, rows)
|
|
29220
|
+
});
|
|
29121
29221
|
} else confirm_then$1(gobj, build_delete_question(gobj, kw.row), {
|
|
29122
29222
|
what: "delete_row",
|
|
29123
|
-
|
|
29223
|
+
ids: yui_row_ids(tabulator, [kw.row])
|
|
29124
29224
|
});
|
|
29125
29225
|
return 0;
|
|
29126
29226
|
}
|
|
29127
29227
|
/************************************************************
|
|
29128
29228
|
* {what, ...} -- the person said yes to what confirm_then()
|
|
29129
|
-
* asked. The rows are
|
|
29130
|
-
*
|
|
29229
|
+
* asked. The rows are found again HERE by the ids the question
|
|
29230
|
+
* named: the kw is plain json, and a Tabulator row is not.
|
|
29131
29231
|
************************************************************/
|
|
29132
29232
|
function ac_confirmed$2(gobj, event, kw, src) {
|
|
29133
29233
|
let priv = gobj.priv;
|
|
@@ -29139,31 +29239,16 @@ function ac_confirmed$2(gobj, event, kw, src) {
|
|
|
29139
29239
|
if (refuse_if_readonly$2(gobj, event)) return -1;
|
|
29140
29240
|
let tabulator = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "tabulator");
|
|
29141
29241
|
let topic_name = (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "topic_name");
|
|
29142
|
-
if (what === "delete_selection") {
|
|
29143
|
-
let
|
|
29144
|
-
if (
|
|
29145
|
-
|
|
29146
|
-
|
|
29147
|
-
}
|
|
29148
|
-
for (let row of rows) (0, _yuneta_gobj_js.gobj_publish_event)(gobj, "EV_DELETE_RECORD", {
|
|
29242
|
+
if (what === "delete_selection" || what === "delete_row") {
|
|
29243
|
+
let found = yui_rows_by_ids(tabulator, Array.isArray(kw.ids) ? kw.ids : []);
|
|
29244
|
+
if (found.missing.length) (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: ${event}: rows gone while confirming the delete, not deleted: ${found.missing.join(", ")}`);
|
|
29245
|
+
if (!found.rows.length) return -1;
|
|
29246
|
+
for (let row of found.rows) (0, _yuneta_gobj_js.gobj_publish_event)(gobj, "EV_DELETE_RECORD", {
|
|
29149
29247
|
topic_name,
|
|
29150
29248
|
record: row
|
|
29151
29249
|
});
|
|
29152
29250
|
return 0;
|
|
29153
29251
|
}
|
|
29154
|
-
if (what === "delete_row") {
|
|
29155
|
-
let index = kw.index;
|
|
29156
|
-
let row = tabulator && typeof index === "number" ? tabulator.getRowFromPosition(index) : null;
|
|
29157
|
-
if (!row) {
|
|
29158
|
-
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: ${event} names no row: ${index}`);
|
|
29159
|
-
return -1;
|
|
29160
|
-
}
|
|
29161
|
-
(0, _yuneta_gobj_js.gobj_publish_event)(gobj, "EV_DELETE_RECORD", {
|
|
29162
|
-
topic_name,
|
|
29163
|
-
record: row.getData()
|
|
29164
|
-
});
|
|
29165
|
-
return 0;
|
|
29166
|
-
}
|
|
29167
29252
|
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: ${event} of nothing this view asked about: ${what}`);
|
|
29168
29253
|
return -1;
|
|
29169
29254
|
}
|
|
@@ -29221,6 +29306,10 @@ function ac_form_save_record(gobj, event, kw, src) {
|
|
|
29221
29306
|
if (answer && answer.result >= 0 && answer.data) picked = answer.data;
|
|
29222
29307
|
const cols = Object.keys(picked);
|
|
29223
29308
|
if (cols.length === 0) {
|
|
29309
|
+
if (wait_for_write_answer(gobj)) {
|
|
29310
|
+
publish_treedb_write(gobj, mode, kw, null, priv.awaiting_write);
|
|
29311
|
+
return 0;
|
|
29312
|
+
}
|
|
29224
29313
|
publish_treedb_write(gobj, mode, kw);
|
|
29225
29314
|
(0, _yuneta_gobj_js.gobj_post_event)(gobj, "EV_CLOSE_FORM", {}, gobj);
|
|
29226
29315
|
return 0;
|
|
@@ -29291,11 +29380,12 @@ function read_is_current(gobj, kw) {
|
|
|
29291
29380
|
/************************************************************
|
|
29292
29381
|
* The one place a write of this view leaves for the app.
|
|
29293
29382
|
************************************************************/
|
|
29294
|
-
function publish_treedb_write(gobj, mode, record, jn_files) {
|
|
29383
|
+
function publish_treedb_write(gobj, mode, record, jn_files, form_write) {
|
|
29295
29384
|
const kw_write = {
|
|
29296
29385
|
topic_name: (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "topic_name"),
|
|
29297
29386
|
record: strip_read_only_cols(gobj, record)
|
|
29298
29387
|
};
|
|
29388
|
+
if (form_write) kw_write.form_write = form_write;
|
|
29299
29389
|
if (jn_files && Object.keys(jn_files).length > 0) kw_write.record.__files__ = jn_files;
|
|
29300
29390
|
(0, _yuneta_gobj_js.gobj_publish_event)(gobj, mode === "create" ? "EV_CREATE_RECORD" : "EV_UPDATE_RECORD", kw_write);
|
|
29301
29391
|
}
|
|
@@ -29314,11 +29404,53 @@ function ac_files_read(gobj, event, kw, src) {
|
|
|
29314
29404
|
if (!read_is_current(gobj, kw)) return -1;
|
|
29315
29405
|
gobj.priv.reading_files = 0;
|
|
29316
29406
|
const { record, __files__ } = yui_files_manifest(kw.picks, kw.record);
|
|
29407
|
+
if (wait_for_write_answer(gobj)) {
|
|
29408
|
+
publish_treedb_write(gobj, kw.mode, record, __files__, gobj.priv.awaiting_write);
|
|
29409
|
+
return 0;
|
|
29410
|
+
}
|
|
29317
29411
|
publish_treedb_write(gobj, kw.mode, record, __files__);
|
|
29318
29412
|
close_form_dialog(gobj);
|
|
29319
29413
|
return 0;
|
|
29320
29414
|
}
|
|
29321
29415
|
/************************************************************
|
|
29416
|
+
* Does the form wait for the answer of its write? Then it stays
|
|
29417
|
+
* open and BUSY, and the serial it waits for is taken here.
|
|
29418
|
+
*
|
|
29419
|
+
* A refused write used to close the form all the same and throw
|
|
29420
|
+
* away what was typed, although the README said it stayed open
|
|
29421
|
+
* (M25 of the 2026-09-21 review): the close was posted with the
|
|
29422
|
+
* publish, before the backend had answered anything.
|
|
29423
|
+
************************************************************/
|
|
29424
|
+
function wait_for_write_answer(gobj) {
|
|
29425
|
+
let priv = gobj.priv;
|
|
29426
|
+
if (!(0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "form_waits_for_answer") || !priv.form) return false;
|
|
29427
|
+
priv.awaiting_write = ++priv.write_serial;
|
|
29428
|
+
set_form_busy(gobj, true);
|
|
29429
|
+
return true;
|
|
29430
|
+
}
|
|
29431
|
+
/************************************************************
|
|
29432
|
+
* {form_write} -- the host says the form's write was done
|
|
29433
|
+
************************************************************/
|
|
29434
|
+
function ac_write_done(gobj, event, kw, src) {
|
|
29435
|
+
let priv = gobj.priv;
|
|
29436
|
+
if (!priv.awaiting_write || !kw || kw.form_write !== priv.awaiting_write) return 0;
|
|
29437
|
+
priv.awaiting_write = 0;
|
|
29438
|
+
set_form_busy(gobj, false);
|
|
29439
|
+
close_form_dialog(gobj);
|
|
29440
|
+
return 0;
|
|
29441
|
+
}
|
|
29442
|
+
/************************************************************
|
|
29443
|
+
* {form_write} -- the host says the form's write was refused.
|
|
29444
|
+
* The form stays OPEN on what was typed; the host has shown why.
|
|
29445
|
+
************************************************************/
|
|
29446
|
+
function ac_write_refused(gobj, event, kw, src) {
|
|
29447
|
+
let priv = gobj.priv;
|
|
29448
|
+
if (!priv.awaiting_write || !kw || kw.form_write !== priv.awaiting_write) return 0;
|
|
29449
|
+
priv.awaiting_write = 0;
|
|
29450
|
+
set_form_busy(gobj, false);
|
|
29451
|
+
return 0;
|
|
29452
|
+
}
|
|
29453
|
+
/************************************************************
|
|
29322
29454
|
* {reason} -- the browser could not read what was picked
|
|
29323
29455
|
*
|
|
29324
29456
|
* The form stays OPEN: the record is still there, and closing it would
|
|
@@ -30078,6 +30210,16 @@ function create_gclass$5(gclass_name) {
|
|
|
30078
30210
|
ac_close_form,
|
|
30079
30211
|
null
|
|
30080
30212
|
],
|
|
30213
|
+
[
|
|
30214
|
+
"EV_WRITE_DONE",
|
|
30215
|
+
ac_write_done,
|
|
30216
|
+
null
|
|
30217
|
+
],
|
|
30218
|
+
[
|
|
30219
|
+
"EV_WRITE_REFUSED",
|
|
30220
|
+
ac_write_refused,
|
|
30221
|
+
null
|
|
30222
|
+
],
|
|
30081
30223
|
[
|
|
30082
30224
|
"EV_FILES_READ",
|
|
30083
30225
|
ac_files_read,
|
|
@@ -30273,6 +30415,8 @@ function create_gclass$5(gclass_name) {
|
|
|
30273
30415
|
["EV_EDITION_MODE", 0],
|
|
30274
30416
|
["EV_SAVE_RECORD", 0],
|
|
30275
30417
|
["EV_CLOSE_FORM", 0],
|
|
30418
|
+
["EV_WRITE_DONE", 0],
|
|
30419
|
+
["EV_WRITE_REFUSED", 0],
|
|
30276
30420
|
["EV_FILES_READ", 0],
|
|
30277
30421
|
["EV_FILES_FAILED", 0],
|
|
30278
30422
|
["EV_NEW_ROW", 0],
|
|
@@ -30414,6 +30558,38 @@ function plan_graph_saves(live, saved) {
|
|
|
30414
30558
|
for (const [topic_name, properties] of Object.entries(live)) if (topic_arrangement_changed(properties, have[topic_name])) plan.push(topic_name);
|
|
30415
30559
|
return plan;
|
|
30416
30560
|
}
|
|
30561
|
+
/***************************************************************
|
|
30562
|
+
* An ECHO of one __graphs__ record arrived: the node event a save
|
|
30563
|
+
* of it sends back, from this browser or from another one.
|
|
30564
|
+
*
|
|
30565
|
+
* live {topic_name: properties} what the view shows
|
|
30566
|
+
* saved {topic_name: properties} what the backend has
|
|
30567
|
+
* records every __graphs__ record the view holds, `rec`
|
|
30568
|
+
* already in its place among them
|
|
30569
|
+
* rec the echoed record
|
|
30570
|
+
*
|
|
30571
|
+
* Only rec's TOPIC moves: it becomes the record, as shown and as
|
|
30572
|
+
* saved. The other topics are left alone -- rebuilding their saved
|
|
30573
|
+
* copies here took them from the live objects the view had already
|
|
30574
|
+
* rearranged in place, so their unsaved changes counted as saved.
|
|
30575
|
+
*
|
|
30576
|
+
* `live[topic]` IS the record's own object, as when the view loads:
|
|
30577
|
+
* the view arranges it in place. `saved[topic]` is a copy.
|
|
30578
|
+
***************************************************************/
|
|
30579
|
+
function apply_graphs_echo(live, saved, records, rec) {
|
|
30580
|
+
if (!(0, _yuneta_gobj_js.is_object)(rec) || !rec.topic) return;
|
|
30581
|
+
if (rec.active && (0, _yuneta_gobj_js.is_object)(rec.properties)) {
|
|
30582
|
+
live[rec.topic] = rec.properties;
|
|
30583
|
+
saved[rec.topic] = (0, _yuneta_gobj_js.json_deep_copy)(rec.properties);
|
|
30584
|
+
return;
|
|
30585
|
+
}
|
|
30586
|
+
if (!(records || []).some((r) => {
|
|
30587
|
+
return r && r !== rec && r.topic === rec.topic && r.active && (0, _yuneta_gobj_js.is_object)(r.properties);
|
|
30588
|
+
})) {
|
|
30589
|
+
delete live[rec.topic];
|
|
30590
|
+
delete saved[rec.topic];
|
|
30591
|
+
}
|
|
30592
|
+
}
|
|
30417
30593
|
//#endregion
|
|
30418
30594
|
//#region src/treedb_node_label.js
|
|
30419
30595
|
/***********************************************************************
|
|
@@ -65266,7 +65442,7 @@ function ac_node_updated(gobj, event, kw, src) {
|
|
|
65266
65442
|
break;
|
|
65267
65443
|
}
|
|
65268
65444
|
if (!found) priv.__graphs__.push(node);
|
|
65269
|
-
|
|
65445
|
+
apply_graphs_echo(priv._graph_properties, priv._saved_graph_properties, priv.__graphs__, node);
|
|
65270
65446
|
return 0;
|
|
65271
65447
|
}
|
|
65272
65448
|
let desc = priv.descs[topic_name];
|
|
@@ -65294,8 +65470,9 @@ function ac_node_updated(gobj, event, kw, src) {
|
|
|
65294
65470
|
update_local_node(gobj, topic_name, node);
|
|
65295
65471
|
let on_screen = false;
|
|
65296
65472
|
try {
|
|
65297
|
-
on_screen = !!graph.getNodeData(node_name);
|
|
65473
|
+
on_screen = !!priv.graph.getNodeData(node_name);
|
|
65298
65474
|
} catch (e) {
|
|
65475
|
+
if (e instanceof ReferenceError) throw e;
|
|
65299
65476
|
on_screen = false;
|
|
65300
65477
|
}
|
|
65301
65478
|
if (on_screen) update_topic_node(gobj, desc, node_name, node);
|