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