@yuneta/gobj-ui 7.23.192 → 7.23.193
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 +238 -62
- package/dist/gobj-ui.es.js +238 -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 +62 -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/dist/gobj-ui.es.js
CHANGED
|
@@ -24544,7 +24544,8 @@ function process_treedb_descs$1(gobj) {
|
|
|
24544
24544
|
desc,
|
|
24545
24545
|
with_remote_paging: gobj_read_bool_attr(gobj, "with_remote_paging"),
|
|
24546
24546
|
page_size: gobj_read_integer_attr(gobj, "page_size"),
|
|
24547
|
-
with_selection_bar: gobj_read_bool_attr(gobj, "with_selection_bar")
|
|
24547
|
+
with_selection_bar: gobj_read_bool_attr(gobj, "with_selection_bar"),
|
|
24548
|
+
form_waits_for_answer: true
|
|
24548
24549
|
};
|
|
24549
24550
|
let id = `${gobj_name(gobj)}?${desc.topic_name}`;
|
|
24550
24551
|
let gobj_topic_form = gobj_create_service(id, "C_YUI_TREEDB_TOPIC_WITH_FORM", kw_topic_form, gobj);
|
|
@@ -24690,7 +24691,7 @@ function treedb_nodes$1(gobj, treedb_name, topic_name, options, page, purpose) {
|
|
|
24690
24691
|
/************************************************************
|
|
24691
24692
|
* Command to remote service
|
|
24692
24693
|
************************************************************/
|
|
24693
|
-
function treedb_update_node$1(gobj, treedb_name, topic_name, record, options) {
|
|
24694
|
+
function treedb_update_node$1(gobj, treedb_name, topic_name, record, options, form_write) {
|
|
24694
24695
|
let command = "update-node";
|
|
24695
24696
|
let gobj_remote_yuno = gobj_read_pointer_attr(gobj, "gobj_remote_yuno");
|
|
24696
24697
|
let kw = {
|
|
@@ -24701,8 +24702,13 @@ function treedb_update_node$1(gobj, treedb_name, topic_name, record, options) {
|
|
|
24701
24702
|
options: options || {}
|
|
24702
24703
|
};
|
|
24703
24704
|
kw.__md_command__ = { topic_name };
|
|
24705
|
+
if (form_write) kw.__md_command__.form_write = form_write;
|
|
24704
24706
|
let ret = gobj_command(gobj_remote_yuno, command, kw, gobj);
|
|
24705
|
-
if (ret)
|
|
24707
|
+
if (ret) {
|
|
24708
|
+
log_error(ret);
|
|
24709
|
+
return -1;
|
|
24710
|
+
}
|
|
24711
|
+
return 0;
|
|
24706
24712
|
}
|
|
24707
24713
|
/************************************************************
|
|
24708
24714
|
* Command to remote service
|
|
@@ -24965,6 +24971,7 @@ function ac_mt_command_answer$2(gobj, event, kw, src) {
|
|
|
24965
24971
|
if (command === "update-node" || command === "create-node") {
|
|
24966
24972
|
let failed_topic = kw_get_str(gobj, kw_command, "topic_name", "", 0);
|
|
24967
24973
|
if (failed_topic) gobj_send_event(gobj, "EV_REFRESH_TOPIC", { topic_name: failed_topic }, gobj);
|
|
24974
|
+
answer_form_write(gobj, get_gobj_formtable(gobj, failed_topic), kw_get_int(gobj, kw_command, "form_write", 0, 0), false);
|
|
24968
24975
|
}
|
|
24969
24976
|
}
|
|
24970
24977
|
return 0;
|
|
@@ -25001,6 +25008,7 @@ function ac_mt_command_answer$2(gobj, event, kw, src) {
|
|
|
25001
25008
|
created: command === "create-node",
|
|
25002
25009
|
command
|
|
25003
25010
|
});
|
|
25011
|
+
answer_form_write(gobj, get_gobj_formtable(gobj, kw_get_str(gobj, kw_command, "topic_name", "", 0)), kw_get_int(gobj, kw_command, "form_write", 0, 0), true);
|
|
25004
25012
|
break;
|
|
25005
25013
|
case "delete-node": break;
|
|
25006
25014
|
default: log_error(`${gobj_short_name(gobj)} Command unknown: ${command}`);
|
|
@@ -25216,15 +25224,23 @@ function ac_treedb_node_deleted$1(gobj, event, kw, src) {
|
|
|
25216
25224
|
* }
|
|
25217
25225
|
********************************************/
|
|
25218
25226
|
function ac_create_record(gobj, event, kw, src) {
|
|
25219
|
-
|
|
25227
|
+
let form_write = kw && kw.form_write || 0;
|
|
25228
|
+
if (refuse_if_readonly$3(gobj, event)) {
|
|
25229
|
+
answer_form_write(gobj, src, form_write, false);
|
|
25230
|
+
return -1;
|
|
25231
|
+
}
|
|
25220
25232
|
let treedb_name = gobj_read_str_attr(gobj, "treedb_name");
|
|
25221
25233
|
let topic_name = gobj_read_attr(src, "topic_name");
|
|
25222
25234
|
let record = kw.record;
|
|
25223
|
-
|
|
25235
|
+
if (treedb_update_node$1(gobj, treedb_name, topic_name, record, {
|
|
25224
25236
|
list_dict: true,
|
|
25225
25237
|
create: true,
|
|
25226
25238
|
autolink: true
|
|
25227
|
-
})
|
|
25239
|
+
}, form_write) < 0) {
|
|
25240
|
+
answer_form_write(gobj, src, form_write, false);
|
|
25241
|
+
return -1;
|
|
25242
|
+
}
|
|
25243
|
+
return 0;
|
|
25228
25244
|
}
|
|
25229
25245
|
/********************************************
|
|
25230
25246
|
* A topic table wants its records as json, WITH the metadata of
|
|
@@ -25374,14 +25390,37 @@ function ac_update_field(gobj, event, kw, src) {
|
|
|
25374
25390
|
* }
|
|
25375
25391
|
********************************************/
|
|
25376
25392
|
function ac_update_record(gobj, event, kw, src) {
|
|
25377
|
-
|
|
25393
|
+
let form_write = kw && kw.form_write || 0;
|
|
25394
|
+
if (refuse_if_readonly$3(gobj, event)) {
|
|
25395
|
+
answer_form_write(gobj, src, form_write, false);
|
|
25396
|
+
return -1;
|
|
25397
|
+
}
|
|
25378
25398
|
let treedb_name = gobj_read_str_attr(gobj, "treedb_name");
|
|
25379
25399
|
let topic_name = gobj_read_attr(src, "topic_name");
|
|
25380
25400
|
let record = kw.record;
|
|
25381
|
-
|
|
25401
|
+
if (treedb_update_node$1(gobj, treedb_name, topic_name, record, {
|
|
25382
25402
|
list_dict: true,
|
|
25383
25403
|
autolink: true
|
|
25384
|
-
})
|
|
25404
|
+
}, form_write) < 0) {
|
|
25405
|
+
answer_form_write(gobj, src, form_write, false);
|
|
25406
|
+
return -1;
|
|
25407
|
+
}
|
|
25408
|
+
return 0;
|
|
25409
|
+
}
|
|
25410
|
+
/************************************************************
|
|
25411
|
+
* Tell a topic form how its write ended: EV_WRITE_DONE closes
|
|
25412
|
+
* it, EV_WRITE_REFUSED leaves it open on what was typed. Every
|
|
25413
|
+
* way a form's write can end passes here -- refused before it
|
|
25414
|
+
* left (read-only, no session) or answered by the backend -- so
|
|
25415
|
+
* a form never waits for an answer that will not come.
|
|
25416
|
+
************************************************************/
|
|
25417
|
+
function answer_form_write(gobj, gobj_topic_form, form_write, ok) {
|
|
25418
|
+
if (!form_write) return;
|
|
25419
|
+
if (!gobj_topic_form) {
|
|
25420
|
+
log_error(`${gobj_short_name(gobj)}: no topic form to answer write ${form_write}`);
|
|
25421
|
+
return;
|
|
25422
|
+
}
|
|
25423
|
+
gobj_send_event(gobj_topic_form, ok ? "EV_WRITE_DONE" : "EV_WRITE_REFUSED", { form_write }, gobj);
|
|
25385
25424
|
}
|
|
25386
25425
|
/********************************************
|
|
25387
25426
|
* Event from formtable
|
|
@@ -25752,6 +25791,46 @@ function yui_selected_rows(tabulator) {
|
|
|
25752
25791
|
}
|
|
25753
25792
|
}
|
|
25754
25793
|
/***************************************************************
|
|
25794
|
+
* The IDENTITY of some rows: the value of the table's `index`
|
|
25795
|
+
* field (Tabulator's default is `id`). This is what crosses a
|
|
25796
|
+
* confirmation dialog -- never the rows' POSITIONS, which the
|
|
25797
|
+
* table regenerates with every addData / deleteRow while the
|
|
25798
|
+
* dialog is open, so a position names whatever row sits there
|
|
25799
|
+
* when the person answers.
|
|
25800
|
+
***************************************************************/
|
|
25801
|
+
function yui_row_ids(tabulator, rows) {
|
|
25802
|
+
let field = tabulator && tabulator.options && tabulator.options.index || "id";
|
|
25803
|
+
let ids = [];
|
|
25804
|
+
for (let row of rows || []) {
|
|
25805
|
+
let id = row ? row[field] : void 0;
|
|
25806
|
+
if (id !== void 0 && id !== null && id !== "") ids.push(id);
|
|
25807
|
+
}
|
|
25808
|
+
return ids;
|
|
25809
|
+
}
|
|
25810
|
+
/***************************************************************
|
|
25811
|
+
* The rows that carry these ids NOW -> {rows, missing}.
|
|
25812
|
+
* `rows` are the rows' data; `missing` the ids no row carries
|
|
25813
|
+
* any more. A missing id is never replaced by another row.
|
|
25814
|
+
***************************************************************/
|
|
25815
|
+
function yui_rows_by_ids(tabulator, ids) {
|
|
25816
|
+
let rows = [];
|
|
25817
|
+
let missing = [];
|
|
25818
|
+
for (let id of ids || []) {
|
|
25819
|
+
let row = null;
|
|
25820
|
+
if (tabulator && typeof tabulator.getRow === "function") try {
|
|
25821
|
+
row = tabulator.getRow(id) || null;
|
|
25822
|
+
} catch (e) {
|
|
25823
|
+
row = null;
|
|
25824
|
+
}
|
|
25825
|
+
if (row) rows.push(row.getData());
|
|
25826
|
+
else missing.push(id);
|
|
25827
|
+
}
|
|
25828
|
+
return {
|
|
25829
|
+
rows,
|
|
25830
|
+
missing
|
|
25831
|
+
};
|
|
25832
|
+
}
|
|
25833
|
+
/***************************************************************
|
|
25755
25834
|
* Drop the selection (after acting on it, or after a
|
|
25756
25835
|
* reload that leaves the ticked rows behind).
|
|
25757
25836
|
***************************************************************/
|
|
@@ -26179,10 +26258,18 @@ function yui_icon_is_defined(name) {
|
|
|
26179
26258
|
***********************************************************************/
|
|
26180
26259
|
var MAX_DEPTH = 4;
|
|
26181
26260
|
/***************************************************************
|
|
26182
|
-
*
|
|
26261
|
+
* A REFERENCE to a node: an fkey as `nodes` answers it with
|
|
26262
|
+
* `list_dict` -- `{id, topic_name, hook_name}` -- or a hook's child
|
|
26263
|
+
* as the node events deliver it, `{id, topic_name}`. It carries
|
|
26264
|
+
* nothing but those keys; an object with more is data.
|
|
26183
26265
|
***************************************************************/
|
|
26266
|
+
var REF_KEYS = /* @__PURE__ */ new Set([
|
|
26267
|
+
"id",
|
|
26268
|
+
"topic_name",
|
|
26269
|
+
"hook_name"
|
|
26270
|
+
]);
|
|
26184
26271
|
function is_fkey_ref(value) {
|
|
26185
|
-
return !!value && typeof value === "object" && !Array.isArray(value) && "id" in value && "topic_name" in value &&
|
|
26272
|
+
return !!value && typeof value === "object" && !Array.isArray(value) && "id" in value && "topic_name" in value && Object.keys(value).every((k) => REF_KEYS.has(k));
|
|
26186
26273
|
}
|
|
26187
26274
|
/***************************************************************
|
|
26188
26275
|
* A hook read with `hook_size`: the COUNT of its children,
|
|
@@ -26442,6 +26529,7 @@ var attrs_table$5 = [
|
|
|
26442
26529
|
SDATA(data_type_t.DTP_BOOLEAN, "with_delete_button", 0, true, "Button toolbar DELETE"),
|
|
26443
26530
|
SDATA(data_type_t.DTP_BOOLEAN, "with_copy_button", 0, true, "Button toolbar COPY"),
|
|
26444
26531
|
SDATA(data_type_t.DTP_BOOLEAN, "with_paste_button", 0, true, "Button toolbar PASTE"),
|
|
26532
|
+
SDATA(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"),
|
|
26445
26533
|
SDATA(data_type_t.DTP_BOOLEAN, "with_refresh_button", 0, true, "Button toolbar REFRESH"),
|
|
26446
26534
|
SDATA(data_type_t.DTP_BOOLEAN, "with_search_button", 0, true, "Button toolbar SEARCH"),
|
|
26447
26535
|
SDATA(data_type_t.DTP_BOOLEAN, "with_schema_button", 0, true, "Button toolbar SCHEMA (show the topic's desc)"),
|
|
@@ -26617,6 +26705,8 @@ function popup_mount_layer(gobj) {
|
|
|
26617
26705
|
function mt_create$5(gobj) {
|
|
26618
26706
|
gobj.priv.reading_files = 0;
|
|
26619
26707
|
gobj.priv.read_serial = 0;
|
|
26708
|
+
gobj.priv.awaiting_write = 0;
|
|
26709
|
+
gobj.priv.write_serial = 0;
|
|
26620
26710
|
let name = clean_name(gobj_name(gobj));
|
|
26621
26711
|
gobj_write_attr(gobj, "table_id", "table" + name);
|
|
26622
26712
|
gobj_write_attr(gobj, "toolbar_id", "toolbar" + name);
|
|
@@ -27135,14 +27225,11 @@ function render_selection_state(gobj) {
|
|
|
27135
27225
|
let $container = gobj_read_attr(gobj, "$container");
|
|
27136
27226
|
let $delete = $container ? $container.querySelector(`.button-delete-record`) : null;
|
|
27137
27227
|
let $copy = $container ? $container.querySelector(`.button-copy-record`) : null;
|
|
27138
|
-
|
|
27139
|
-
|
|
27140
|
-
|
|
27141
|
-
|
|
27142
|
-
|
|
27143
|
-
$delete.setAttribute("disabled", true);
|
|
27144
|
-
$copy.setAttribute("disabled", true);
|
|
27145
|
-
}
|
|
27228
|
+
let enable = yui_selected_rows(gobj_read_attr(gobj, "tabulator")).length > 0 && gobj_read_bool_attr(gobj, "editable");
|
|
27229
|
+
for (let $button of [$delete, $copy]) {
|
|
27230
|
+
if (!$button) continue;
|
|
27231
|
+
if (enable) $button.removeAttribute("disabled");
|
|
27232
|
+
else $button.setAttribute("disabled", true);
|
|
27146
27233
|
}
|
|
27147
27234
|
render_selection_bar(gobj);
|
|
27148
27235
|
}
|
|
@@ -27450,7 +27537,7 @@ function create_tabulator(gobj) {
|
|
|
27450
27537
|
hozAlign,
|
|
27451
27538
|
formatter: colFormatter
|
|
27452
27539
|
};
|
|
27453
|
-
if (max_col_width > 0) colDef.
|
|
27540
|
+
if (max_col_width > 0) colDef.maxInitialWidth = max_col_width;
|
|
27454
27541
|
if (with_header_filters) apply_header_filter(colDef, field_desc);
|
|
27455
27542
|
if (with_inline_edit) inline_editor_for(colDef, col, field_desc);
|
|
27456
27543
|
if (vertAlign) colDef.vertAlign = vertAlign;
|
|
@@ -27488,10 +27575,7 @@ function create_tabulator(gobj) {
|
|
|
27488
27575
|
gobj_send_event(gobj, "EV_EDIT_RECORD", { index }, gobj);
|
|
27489
27576
|
} else if (e.target.closest(".remove")) {
|
|
27490
27577
|
e.stopPropagation();
|
|
27491
|
-
gobj_send_event(gobj, "EV_DELETE_ROWS", {
|
|
27492
|
-
index,
|
|
27493
|
-
row
|
|
27494
|
-
}, gobj);
|
|
27578
|
+
gobj_send_event(gobj, "EV_DELETE_ROWS", { row }, gobj);
|
|
27495
27579
|
}
|
|
27496
27580
|
}
|
|
27497
27581
|
});
|
|
@@ -28029,6 +28113,7 @@ function form_may_close(gobj) {
|
|
|
28029
28113
|
function teardown_form_child(gobj) {
|
|
28030
28114
|
let priv = gobj.priv;
|
|
28031
28115
|
priv.reading_files = 0;
|
|
28116
|
+
priv.awaiting_write = 0;
|
|
28032
28117
|
if (priv.form) {
|
|
28033
28118
|
if (gobj_is_running(priv.form)) gobj_stop(priv.form);
|
|
28034
28119
|
gobj_destroy(priv.form);
|
|
@@ -29038,25 +29123,31 @@ function ac_edition_mode(gobj, event, kw, src) {
|
|
|
29038
29123
|
let $button_copy_record = $container.querySelector(`.button-copy-record`);
|
|
29039
29124
|
let $button_paste_record = $container.querySelector(`.button-paste-record`);
|
|
29040
29125
|
if (editable) {
|
|
29041
|
-
$button_edit_record.classList.add("is-primary");
|
|
29042
|
-
$button_new_record
|
|
29043
|
-
|
|
29126
|
+
if ($button_edit_record) $button_edit_record.classList.add("is-primary");
|
|
29127
|
+
if ($button_new_record) {
|
|
29128
|
+
$button_new_record.classList.add("is-info");
|
|
29129
|
+
$button_new_record.removeAttribute("disabled");
|
|
29130
|
+
}
|
|
29131
|
+
if ($button_delete_record) $button_delete_record.classList.add("is-danger");
|
|
29044
29132
|
tabulator.showColumn("_operation");
|
|
29045
29133
|
tabulator.showColumn("_check_box_state_");
|
|
29046
|
-
$button_new_record.removeAttribute("disabled");
|
|
29047
29134
|
if ($button_paste_record) $button_paste_record.removeAttribute("disabled");
|
|
29048
29135
|
if (yui_selected_rows(tabulator).length) {
|
|
29049
|
-
$button_delete_record.removeAttribute("disabled");
|
|
29136
|
+
if ($button_delete_record) $button_delete_record.removeAttribute("disabled");
|
|
29050
29137
|
if ($button_copy_record) $button_copy_record.removeAttribute("disabled");
|
|
29051
29138
|
}
|
|
29052
29139
|
} else {
|
|
29053
|
-
$button_edit_record.classList.remove("is-primary");
|
|
29054
|
-
$button_new_record
|
|
29055
|
-
|
|
29140
|
+
if ($button_edit_record) $button_edit_record.classList.remove("is-primary");
|
|
29141
|
+
if ($button_new_record) {
|
|
29142
|
+
$button_new_record.classList.remove("is-info");
|
|
29143
|
+
$button_new_record.setAttribute("disabled", true);
|
|
29144
|
+
}
|
|
29145
|
+
if ($button_delete_record) {
|
|
29146
|
+
$button_delete_record.classList.remove("is-danger");
|
|
29147
|
+
$button_delete_record.setAttribute("disabled", true);
|
|
29148
|
+
}
|
|
29056
29149
|
tabulator.hideColumn("_operation");
|
|
29057
29150
|
tabulator.hideColumn("_check_box_state_");
|
|
29058
|
-
$button_new_record.setAttribute("disabled", true);
|
|
29059
|
-
$button_delete_record.setAttribute("disabled", true);
|
|
29060
29151
|
if ($button_copy_record) $button_copy_record.setAttribute("disabled", true);
|
|
29061
29152
|
if ($button_paste_record) $button_paste_record.setAttribute("disabled", true);
|
|
29062
29153
|
}
|
|
@@ -29098,7 +29189,12 @@ function ac_new_row(gobj, event, kw, src) {
|
|
|
29098
29189
|
* kw {} empty
|
|
29099
29190
|
* - From the column _operation delete icon inside a row
|
|
29100
29191
|
* It will delete this one row
|
|
29101
|
-
* kw {
|
|
29192
|
+
* kw {row:}
|
|
29193
|
+
*
|
|
29194
|
+
* What crosses the confirmation is the rows' IDENTITY, never their
|
|
29195
|
+
* position: the view applies EV_TREEDB_NODE_* of every writer while the
|
|
29196
|
+
* dialog is open, and a position names whatever row sits there when the
|
|
29197
|
+
* person answers -- with force:true (A6 of the 2026-09-21 review).
|
|
29102
29198
|
************************************************************/
|
|
29103
29199
|
function ac_delete_rows(gobj, event, kw, src) {
|
|
29104
29200
|
if (refuse_if_readonly$2(gobj, event)) return -1;
|
|
@@ -29112,17 +29208,20 @@ function ac_delete_rows(gobj, event, kw, src) {
|
|
|
29112
29208
|
});
|
|
29113
29209
|
return 0;
|
|
29114
29210
|
}
|
|
29115
|
-
confirm_then$1(gobj, build_delete_question(gobj, rows), {
|
|
29211
|
+
confirm_then$1(gobj, build_delete_question(gobj, rows), {
|
|
29212
|
+
what: "delete_selection",
|
|
29213
|
+
ids: yui_row_ids(tabulator, rows)
|
|
29214
|
+
});
|
|
29116
29215
|
} else confirm_then$1(gobj, build_delete_question(gobj, kw.row), {
|
|
29117
29216
|
what: "delete_row",
|
|
29118
|
-
|
|
29217
|
+
ids: yui_row_ids(tabulator, [kw.row])
|
|
29119
29218
|
});
|
|
29120
29219
|
return 0;
|
|
29121
29220
|
}
|
|
29122
29221
|
/************************************************************
|
|
29123
29222
|
* {what, ...} -- the person said yes to what confirm_then()
|
|
29124
|
-
* asked. The rows are
|
|
29125
|
-
*
|
|
29223
|
+
* asked. The rows are found again HERE by the ids the question
|
|
29224
|
+
* named: the kw is plain json, and a Tabulator row is not.
|
|
29126
29225
|
************************************************************/
|
|
29127
29226
|
function ac_confirmed$2(gobj, event, kw, src) {
|
|
29128
29227
|
let priv = gobj.priv;
|
|
@@ -29134,31 +29233,16 @@ function ac_confirmed$2(gobj, event, kw, src) {
|
|
|
29134
29233
|
if (refuse_if_readonly$2(gobj, event)) return -1;
|
|
29135
29234
|
let tabulator = gobj_read_attr(gobj, "tabulator");
|
|
29136
29235
|
let topic_name = gobj_read_str_attr(gobj, "topic_name");
|
|
29137
|
-
if (what === "delete_selection") {
|
|
29138
|
-
let
|
|
29139
|
-
if (
|
|
29140
|
-
|
|
29141
|
-
|
|
29142
|
-
}
|
|
29143
|
-
for (let row of rows) gobj_publish_event(gobj, "EV_DELETE_RECORD", {
|
|
29236
|
+
if (what === "delete_selection" || what === "delete_row") {
|
|
29237
|
+
let found = yui_rows_by_ids(tabulator, Array.isArray(kw.ids) ? kw.ids : []);
|
|
29238
|
+
if (found.missing.length) log_error(`${gobj_short_name(gobj)}: ${event}: rows gone while confirming the delete, not deleted: ${found.missing.join(", ")}`);
|
|
29239
|
+
if (!found.rows.length) return -1;
|
|
29240
|
+
for (let row of found.rows) gobj_publish_event(gobj, "EV_DELETE_RECORD", {
|
|
29144
29241
|
topic_name,
|
|
29145
29242
|
record: row
|
|
29146
29243
|
});
|
|
29147
29244
|
return 0;
|
|
29148
29245
|
}
|
|
29149
|
-
if (what === "delete_row") {
|
|
29150
|
-
let index = kw.index;
|
|
29151
|
-
let row = tabulator && typeof index === "number" ? tabulator.getRowFromPosition(index) : null;
|
|
29152
|
-
if (!row) {
|
|
29153
|
-
log_error(`${gobj_short_name(gobj)}: ${event} names no row: ${index}`);
|
|
29154
|
-
return -1;
|
|
29155
|
-
}
|
|
29156
|
-
gobj_publish_event(gobj, "EV_DELETE_RECORD", {
|
|
29157
|
-
topic_name,
|
|
29158
|
-
record: row.getData()
|
|
29159
|
-
});
|
|
29160
|
-
return 0;
|
|
29161
|
-
}
|
|
29162
29246
|
log_error(`${gobj_short_name(gobj)}: ${event} of nothing this view asked about: ${what}`);
|
|
29163
29247
|
return -1;
|
|
29164
29248
|
}
|
|
@@ -29216,6 +29300,10 @@ function ac_form_save_record(gobj, event, kw, src) {
|
|
|
29216
29300
|
if (answer && answer.result >= 0 && answer.data) picked = answer.data;
|
|
29217
29301
|
const cols = Object.keys(picked);
|
|
29218
29302
|
if (cols.length === 0) {
|
|
29303
|
+
if (wait_for_write_answer(gobj)) {
|
|
29304
|
+
publish_treedb_write(gobj, mode, kw, null, priv.awaiting_write);
|
|
29305
|
+
return 0;
|
|
29306
|
+
}
|
|
29219
29307
|
publish_treedb_write(gobj, mode, kw);
|
|
29220
29308
|
gobj_post_event(gobj, "EV_CLOSE_FORM", {}, gobj);
|
|
29221
29309
|
return 0;
|
|
@@ -29286,11 +29374,12 @@ function read_is_current(gobj, kw) {
|
|
|
29286
29374
|
/************************************************************
|
|
29287
29375
|
* The one place a write of this view leaves for the app.
|
|
29288
29376
|
************************************************************/
|
|
29289
|
-
function publish_treedb_write(gobj, mode, record, jn_files) {
|
|
29377
|
+
function publish_treedb_write(gobj, mode, record, jn_files, form_write) {
|
|
29290
29378
|
const kw_write = {
|
|
29291
29379
|
topic_name: gobj_read_str_attr(gobj, "topic_name"),
|
|
29292
29380
|
record: strip_read_only_cols(gobj, record)
|
|
29293
29381
|
};
|
|
29382
|
+
if (form_write) kw_write.form_write = form_write;
|
|
29294
29383
|
if (jn_files && Object.keys(jn_files).length > 0) kw_write.record.__files__ = jn_files;
|
|
29295
29384
|
gobj_publish_event(gobj, mode === "create" ? "EV_CREATE_RECORD" : "EV_UPDATE_RECORD", kw_write);
|
|
29296
29385
|
}
|
|
@@ -29309,11 +29398,53 @@ function ac_files_read(gobj, event, kw, src) {
|
|
|
29309
29398
|
if (!read_is_current(gobj, kw)) return -1;
|
|
29310
29399
|
gobj.priv.reading_files = 0;
|
|
29311
29400
|
const { record, __files__ } = yui_files_manifest(kw.picks, kw.record);
|
|
29401
|
+
if (wait_for_write_answer(gobj)) {
|
|
29402
|
+
publish_treedb_write(gobj, kw.mode, record, __files__, gobj.priv.awaiting_write);
|
|
29403
|
+
return 0;
|
|
29404
|
+
}
|
|
29312
29405
|
publish_treedb_write(gobj, kw.mode, record, __files__);
|
|
29313
29406
|
close_form_dialog(gobj);
|
|
29314
29407
|
return 0;
|
|
29315
29408
|
}
|
|
29316
29409
|
/************************************************************
|
|
29410
|
+
* Does the form wait for the answer of its write? Then it stays
|
|
29411
|
+
* open and BUSY, and the serial it waits for is taken here.
|
|
29412
|
+
*
|
|
29413
|
+
* A refused write used to close the form all the same and throw
|
|
29414
|
+
* away what was typed, although the README said it stayed open
|
|
29415
|
+
* (M25 of the 2026-09-21 review): the close was posted with the
|
|
29416
|
+
* publish, before the backend had answered anything.
|
|
29417
|
+
************************************************************/
|
|
29418
|
+
function wait_for_write_answer(gobj) {
|
|
29419
|
+
let priv = gobj.priv;
|
|
29420
|
+
if (!gobj_read_bool_attr(gobj, "form_waits_for_answer") || !priv.form) return false;
|
|
29421
|
+
priv.awaiting_write = ++priv.write_serial;
|
|
29422
|
+
set_form_busy(gobj, true);
|
|
29423
|
+
return true;
|
|
29424
|
+
}
|
|
29425
|
+
/************************************************************
|
|
29426
|
+
* {form_write} -- the host says the form's write was done
|
|
29427
|
+
************************************************************/
|
|
29428
|
+
function ac_write_done(gobj, event, kw, src) {
|
|
29429
|
+
let priv = gobj.priv;
|
|
29430
|
+
if (!priv.awaiting_write || !kw || kw.form_write !== priv.awaiting_write) return 0;
|
|
29431
|
+
priv.awaiting_write = 0;
|
|
29432
|
+
set_form_busy(gobj, false);
|
|
29433
|
+
close_form_dialog(gobj);
|
|
29434
|
+
return 0;
|
|
29435
|
+
}
|
|
29436
|
+
/************************************************************
|
|
29437
|
+
* {form_write} -- the host says the form's write was refused.
|
|
29438
|
+
* The form stays OPEN on what was typed; the host has shown why.
|
|
29439
|
+
************************************************************/
|
|
29440
|
+
function ac_write_refused(gobj, event, kw, src) {
|
|
29441
|
+
let priv = gobj.priv;
|
|
29442
|
+
if (!priv.awaiting_write || !kw || kw.form_write !== priv.awaiting_write) return 0;
|
|
29443
|
+
priv.awaiting_write = 0;
|
|
29444
|
+
set_form_busy(gobj, false);
|
|
29445
|
+
return 0;
|
|
29446
|
+
}
|
|
29447
|
+
/************************************************************
|
|
29317
29448
|
* {reason} -- the browser could not read what was picked
|
|
29318
29449
|
*
|
|
29319
29450
|
* The form stays OPEN: the record is still there, and closing it would
|
|
@@ -30073,6 +30204,16 @@ function create_gclass$5(gclass_name) {
|
|
|
30073
30204
|
ac_close_form,
|
|
30074
30205
|
null
|
|
30075
30206
|
],
|
|
30207
|
+
[
|
|
30208
|
+
"EV_WRITE_DONE",
|
|
30209
|
+
ac_write_done,
|
|
30210
|
+
null
|
|
30211
|
+
],
|
|
30212
|
+
[
|
|
30213
|
+
"EV_WRITE_REFUSED",
|
|
30214
|
+
ac_write_refused,
|
|
30215
|
+
null
|
|
30216
|
+
],
|
|
30076
30217
|
[
|
|
30077
30218
|
"EV_FILES_READ",
|
|
30078
30219
|
ac_files_read,
|
|
@@ -30268,6 +30409,8 @@ function create_gclass$5(gclass_name) {
|
|
|
30268
30409
|
["EV_EDITION_MODE", 0],
|
|
30269
30410
|
["EV_SAVE_RECORD", 0],
|
|
30270
30411
|
["EV_CLOSE_FORM", 0],
|
|
30412
|
+
["EV_WRITE_DONE", 0],
|
|
30413
|
+
["EV_WRITE_REFUSED", 0],
|
|
30271
30414
|
["EV_FILES_READ", 0],
|
|
30272
30415
|
["EV_FILES_FAILED", 0],
|
|
30273
30416
|
["EV_NEW_ROW", 0],
|
|
@@ -30409,6 +30552,38 @@ function plan_graph_saves(live, saved) {
|
|
|
30409
30552
|
for (const [topic_name, properties] of Object.entries(live)) if (topic_arrangement_changed(properties, have[topic_name])) plan.push(topic_name);
|
|
30410
30553
|
return plan;
|
|
30411
30554
|
}
|
|
30555
|
+
/***************************************************************
|
|
30556
|
+
* An ECHO of one __graphs__ record arrived: the node event a save
|
|
30557
|
+
* of it sends back, from this browser or from another one.
|
|
30558
|
+
*
|
|
30559
|
+
* live {topic_name: properties} what the view shows
|
|
30560
|
+
* saved {topic_name: properties} what the backend has
|
|
30561
|
+
* records every __graphs__ record the view holds, `rec`
|
|
30562
|
+
* already in its place among them
|
|
30563
|
+
* rec the echoed record
|
|
30564
|
+
*
|
|
30565
|
+
* Only rec's TOPIC moves: it becomes the record, as shown and as
|
|
30566
|
+
* saved. The other topics are left alone -- rebuilding their saved
|
|
30567
|
+
* copies here took them from the live objects the view had already
|
|
30568
|
+
* rearranged in place, so their unsaved changes counted as saved.
|
|
30569
|
+
*
|
|
30570
|
+
* `live[topic]` IS the record's own object, as when the view loads:
|
|
30571
|
+
* the view arranges it in place. `saved[topic]` is a copy.
|
|
30572
|
+
***************************************************************/
|
|
30573
|
+
function apply_graphs_echo(live, saved, records, rec) {
|
|
30574
|
+
if (!is_object(rec) || !rec.topic) return;
|
|
30575
|
+
if (rec.active && is_object(rec.properties)) {
|
|
30576
|
+
live[rec.topic] = rec.properties;
|
|
30577
|
+
saved[rec.topic] = json_deep_copy(rec.properties);
|
|
30578
|
+
return;
|
|
30579
|
+
}
|
|
30580
|
+
if (!(records || []).some((r) => {
|
|
30581
|
+
return r && r !== rec && r.topic === rec.topic && r.active && is_object(r.properties);
|
|
30582
|
+
})) {
|
|
30583
|
+
delete live[rec.topic];
|
|
30584
|
+
delete saved[rec.topic];
|
|
30585
|
+
}
|
|
30586
|
+
}
|
|
30412
30587
|
//#endregion
|
|
30413
30588
|
//#region src/treedb_node_label.js
|
|
30414
30589
|
/***********************************************************************
|
|
@@ -65261,7 +65436,7 @@ function ac_node_updated(gobj, event, kw, src) {
|
|
|
65261
65436
|
break;
|
|
65262
65437
|
}
|
|
65263
65438
|
if (!found) priv.__graphs__.push(node);
|
|
65264
|
-
|
|
65439
|
+
apply_graphs_echo(priv._graph_properties, priv._saved_graph_properties, priv.__graphs__, node);
|
|
65265
65440
|
return 0;
|
|
65266
65441
|
}
|
|
65267
65442
|
let desc = priv.descs[topic_name];
|
|
@@ -65289,8 +65464,9 @@ function ac_node_updated(gobj, event, kw, src) {
|
|
|
65289
65464
|
update_local_node(gobj, topic_name, node);
|
|
65290
65465
|
let on_screen = false;
|
|
65291
65466
|
try {
|
|
65292
|
-
on_screen = !!graph.getNodeData(node_name);
|
|
65467
|
+
on_screen = !!priv.graph.getNodeData(node_name);
|
|
65293
65468
|
} catch (e) {
|
|
65469
|
+
if (e instanceof ReferenceError) throw e;
|
|
65294
65470
|
on_screen = false;
|
|
65295
65471
|
}
|
|
65296
65472
|
if (on_screen) update_topic_node(gobj, desc, node_name, node);
|
package/package.json
CHANGED
package/src/c_g6_nodes_tree.js
CHANGED
|
@@ -93,6 +93,7 @@ import {
|
|
|
93
93
|
} from "@yuneta/gobj-js";
|
|
94
94
|
|
|
95
95
|
import {
|
|
96
|
+
apply_graphs_echo,
|
|
96
97
|
plan_graph_saves,
|
|
97
98
|
topic_arrangement_changed,
|
|
98
99
|
} from "./graph_save_plan.js";
|
|
@@ -10579,7 +10580,12 @@ function ac_node_updated(gobj, event, kw, src)
|
|
|
10579
10580
|
if(!found) {
|
|
10580
10581
|
priv.__graphs__.push(node);
|
|
10581
10582
|
}
|
|
10582
|
-
|
|
10583
|
+
/* Only THIS record's topic: a rebuild of every topic took their
|
|
10584
|
+
* saved copies from the live objects (M32 of the 2026-09-21
|
|
10585
|
+
* review). build_graph_properties() is for the load. */
|
|
10586
|
+
apply_graphs_echo(
|
|
10587
|
+
priv._graph_properties, priv._saved_graph_properties, priv.__graphs__, node
|
|
10588
|
+
);
|
|
10583
10589
|
return 0;
|
|
10584
10590
|
}
|
|
10585
10591
|
|
|
@@ -10628,10 +10634,17 @@ function ac_node_updated(gobj, event, kw, src)
|
|
|
10628
10634
|
* repaints the count on whatever pill reaches it.
|
|
10629
10635
|
*/
|
|
10630
10636
|
update_local_node(gobj, topic_name, node);
|
|
10637
|
+
/* `priv.graph`: this read an undeclared `graph`, the ReferenceError
|
|
10638
|
+
* fell into the catch, and a card on screen kept its old record after
|
|
10639
|
+
* every UPDATED (M33 of the 2026-09-21 review). The catch is for G6,
|
|
10640
|
+
* which throws on an id it does not hold -- a folded card. */
|
|
10631
10641
|
let on_screen = false;
|
|
10632
10642
|
try {
|
|
10633
|
-
on_screen = !!graph.getNodeData(node_name);
|
|
10643
|
+
on_screen = !!priv.graph.getNodeData(node_name);
|
|
10634
10644
|
} catch(e) {
|
|
10645
|
+
if(e instanceof ReferenceError) {
|
|
10646
|
+
throw e;
|
|
10647
|
+
}
|
|
10635
10648
|
on_screen = false;
|
|
10636
10649
|
}
|
|
10637
10650
|
if(on_screen) {
|