@yuneta/gobj-ui 5.15.0 → 5.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -5
- package/dist/gobj-ui.cjs.js +63 -19
- package/dist/gobj-ui.es.js +63 -19
- package/package.json +1 -1
- package/src/c_yui_treedb_graph.js +99 -1
- package/src/c_yui_treedb_topic_with_form.js +16 -1
- package/src/c_yui_treedb_topics.js +2 -1
package/README.md
CHANGED
|
@@ -432,10 +432,11 @@ Logical DOM classes: `JSON_VIEWER`, `JSON_TOOLBAR`, `JSON_SEARCH`, `JSON_TREE`,
|
|
|
432
432
|
|
|
433
433
|
### Read-only treedbs: `readonly`
|
|
434
434
|
|
|
435
|
-
`C_YUI_TREEDB_TOPICS`
|
|
436
|
-
|
|
437
|
-
beats each `with_*` flag at once,
|
|
438
|
-
not master answers **every** write
|
|
435
|
+
`C_YUI_TREEDB_TOPICS` and `C_YUI_TREEDB_GRAPH` take a **`readonly`** attr; the
|
|
436
|
+
topics view propagates it to every topic it builds. It is not one more button
|
|
437
|
+
flag: it is the STATE of the treedb and it beats each `with_*` flag at once,
|
|
438
|
+
because a treedb whose tranger the yuno does not master answers **every** write
|
|
439
|
+
with
|
|
439
440
|
|
|
440
441
|
```
|
|
441
442
|
ERROR -1: <yuno>: treedb '<name>' is READ-ONLY, this yuno is not the master of its tranger
|
|
@@ -453,11 +454,17 @@ buttons, the in-row edit icons, and the write half of the record form's toolbar
|
|
|
453
454
|
(`copy` stays — reading a record includes taking it with you) with the cells not
|
|
454
455
|
editable. The record form still OPENS: looking is the point of a replica.
|
|
455
456
|
|
|
457
|
+
In the **graph** it takes away the `edition` operation mode, which is the only
|
|
458
|
+
one that draws the create / delete / link affordances — the mode select stops
|
|
459
|
+
offering it, and a graph left in edition on a master comes back in `reading` on
|
|
460
|
+
a replica (the mode is a persisted preference). The other modes are untouched:
|
|
461
|
+
panning, zooming and opening a node are reading.
|
|
462
|
+
|
|
456
463
|
Two implementation notes worth keeping:
|
|
457
464
|
|
|
458
465
|
- the decision lives in **`treedb_write_plan.js`** (pure, tested), not in five
|
|
459
466
|
`!readonly && with_x` expressions — five places to forget the sixth;
|
|
460
|
-
- and the write **events** are refused as well, in
|
|
467
|
+
- and the write **events** are refused as well, in every gclass, with a
|
|
461
468
|
`log_error`. Hiding a button is not the same as refusing a write: an event can
|
|
462
469
|
still arrive from a keyboard path or a form that outlived the flag, and an
|
|
463
470
|
ignored write is exactly the behaviour this whole change exists to stop.
|
package/dist/gobj-ui.cjs.js
CHANGED
|
@@ -15505,7 +15505,7 @@ function request_print_tranger$1(gobj, path) {
|
|
|
15505
15505
|
* would travel, be refused by the yuno, and come back as a toast --
|
|
15506
15506
|
* after the table had already drawn the row.
|
|
15507
15507
|
***************************************************************/
|
|
15508
|
-
function refuse_if_readonly$
|
|
15508
|
+
function refuse_if_readonly$2(gobj, event) {
|
|
15509
15509
|
if (!(0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "readonly")) return false;
|
|
15510
15510
|
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: ${event} refused, treedb '${(0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "treedb_name")}' is READ-ONLY`);
|
|
15511
15511
|
return true;
|
|
@@ -15577,7 +15577,8 @@ function ac_mt_command_answer$1(gobj, event, kw, src) {
|
|
|
15577
15577
|
treedb_name: (0, _yuneta_gobj_js.kw_get_str)(gobj, kw_command, "treedb_name", "", 0),
|
|
15578
15578
|
topic_name: (0, _yuneta_gobj_js.kw_get_str)(gobj, kw_command, "topic_name", "", 0),
|
|
15579
15579
|
record: (0, _yuneta_gobj_js.kw_get_dict)(gobj, kw_command, "record", {}, 0),
|
|
15580
|
-
created: command === "create-node"
|
|
15580
|
+
created: command === "create-node",
|
|
15581
|
+
command
|
|
15581
15582
|
});
|
|
15582
15583
|
break;
|
|
15583
15584
|
case "delete-node": break;
|
|
@@ -15782,7 +15783,7 @@ function ac_treedb_node_deleted$1(gobj, event, kw, src) {
|
|
|
15782
15783
|
* }
|
|
15783
15784
|
********************************************/
|
|
15784
15785
|
function ac_create_record(gobj, event, kw, src) {
|
|
15785
|
-
if (refuse_if_readonly$
|
|
15786
|
+
if (refuse_if_readonly$2(gobj, event)) return -1;
|
|
15786
15787
|
let treedb_name = (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "treedb_name");
|
|
15787
15788
|
let topic_name = (0, _yuneta_gobj_js.gobj_read_attr)(src, "topic_name");
|
|
15788
15789
|
let record = kw.record;
|
|
@@ -15800,7 +15801,7 @@ function ac_create_record(gobj, event, kw, src) {
|
|
|
15800
15801
|
* }
|
|
15801
15802
|
********************************************/
|
|
15802
15803
|
function ac_update_record(gobj, event, kw, src) {
|
|
15803
|
-
if (refuse_if_readonly$
|
|
15804
|
+
if (refuse_if_readonly$2(gobj, event)) return -1;
|
|
15804
15805
|
let treedb_name = (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "treedb_name");
|
|
15805
15806
|
let topic_name = (0, _yuneta_gobj_js.gobj_read_attr)(src, "topic_name");
|
|
15806
15807
|
let record = kw.record;
|
|
@@ -15817,7 +15818,7 @@ function ac_update_record(gobj, event, kw, src) {
|
|
|
15817
15818
|
* }
|
|
15818
15819
|
********************************************/
|
|
15819
15820
|
function ac_delete_record(gobj, event, kw, src) {
|
|
15820
|
-
if (refuse_if_readonly$
|
|
15821
|
+
if (refuse_if_readonly$2(gobj, event)) return -1;
|
|
15821
15822
|
let treedb_name = (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "treedb_name");
|
|
15822
15823
|
let topic_name = (0, _yuneta_gobj_js.gobj_read_attr)(src, "topic_name");
|
|
15823
15824
|
let record = kw.record;
|
|
@@ -17638,7 +17639,7 @@ function write_plan(gobj) {
|
|
|
17638
17639
|
* A read-only topic refuses the write EVENTS too, not only their
|
|
17639
17640
|
* buttons.
|
|
17640
17641
|
***************************************************************/
|
|
17641
|
-
function refuse_if_readonly(gobj, event) {
|
|
17642
|
+
function refuse_if_readonly$1(gobj, event) {
|
|
17642
17643
|
if (!(0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "readonly")) return false;
|
|
17643
17644
|
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: ${event} refused, topic '${(0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "topic_name")}' is READ-ONLY`);
|
|
17644
17645
|
return true;
|
|
@@ -17710,7 +17711,10 @@ function ac_load_node_updated(gobj, event, kw, src) {
|
|
|
17710
17711
|
return -1;
|
|
17711
17712
|
}
|
|
17712
17713
|
let tabulator = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "tabulator");
|
|
17713
|
-
if (tabulator)
|
|
17714
|
+
if (tabulator) {
|
|
17715
|
+
for (let record of data) if (tabulator.getRow(record.id)) tabulator.updateData([record]);
|
|
17716
|
+
else if (tabulator.getDataCount() > 0) (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: updated node is not in the loaded table: ${record.id}`);
|
|
17717
|
+
}
|
|
17714
17718
|
return 0;
|
|
17715
17719
|
}
|
|
17716
17720
|
/************************************************************
|
|
@@ -17735,7 +17739,7 @@ function ac_node_deleted$1(gobj, event, kw, src) {
|
|
|
17735
17739
|
* and the Op column is visible (with row's edit/delete icons)
|
|
17736
17740
|
************************************************************/
|
|
17737
17741
|
function ac_edition_mode(gobj, event, kw, src) {
|
|
17738
|
-
if (refuse_if_readonly(gobj, event)) return -1;
|
|
17742
|
+
if (refuse_if_readonly$1(gobj, event)) return -1;
|
|
17739
17743
|
let tabulator = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "tabulator");
|
|
17740
17744
|
let editable = (0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "editable");
|
|
17741
17745
|
editable = !editable;
|
|
@@ -17779,7 +17783,7 @@ function ac_edition_mode(gobj, event, kw, src) {
|
|
|
17779
17783
|
* - the backend will broadcast the new record that will be added to table
|
|
17780
17784
|
************************************************************/
|
|
17781
17785
|
function ac_new_row(gobj, event, kw, src) {
|
|
17782
|
-
if (refuse_if_readonly(gobj, event)) return -1;
|
|
17786
|
+
if (refuse_if_readonly$1(gobj, event)) return -1;
|
|
17783
17787
|
let row = {};
|
|
17784
17788
|
let desc = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "desc");
|
|
17785
17789
|
for (let i = 0; i < desc.cols.length; i++) {
|
|
@@ -17809,7 +17813,7 @@ function ac_new_row(gobj, event, kw, src) {
|
|
|
17809
17813
|
* kw {index:, row:}
|
|
17810
17814
|
************************************************************/
|
|
17811
17815
|
function ac_delete_rows(gobj, event, kw, src) {
|
|
17812
|
-
if (refuse_if_readonly(gobj, event)) return -1;
|
|
17816
|
+
if (refuse_if_readonly$1(gobj, event)) return -1;
|
|
17813
17817
|
let tabulator = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "tabulator");
|
|
17814
17818
|
if ((0, _yuneta_gobj_js.json_size)(kw) === 0) {
|
|
17815
17819
|
let rows = tabulator.getSelectedData();
|
|
@@ -17873,7 +17877,7 @@ function ac_copy_rows(gobj, event, kw, src) {
|
|
|
17873
17877
|
* From internal
|
|
17874
17878
|
************************************************************/
|
|
17875
17879
|
function ac_paste_rows(gobj, event, kw, src) {
|
|
17876
|
-
if (refuse_if_readonly(gobj, event)) return -1;
|
|
17880
|
+
if (refuse_if_readonly$1(gobj, event)) return -1;
|
|
17877
17881
|
if (kw.topic_name === (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "topic_name") && kw.treedb_name === (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "treedb_name")) for (let row of kw.rows) (0, _yuneta_gobj_js.gobj_publish_event)(gobj, "EV_CREATE_RECORD", {
|
|
17878
17882
|
topic_name: (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "topic_name"),
|
|
17879
17883
|
record: row
|
|
@@ -17886,7 +17890,7 @@ function ac_paste_rows(gobj, event, kw, src) {
|
|
|
17886
17890
|
* fkeys, times and numbers). Route by the form's mode.
|
|
17887
17891
|
************************************************************/
|
|
17888
17892
|
function ac_form_save_record(gobj, event, kw, src) {
|
|
17889
|
-
if (refuse_if_readonly(gobj, event)) return -1;
|
|
17893
|
+
if (refuse_if_readonly$1(gobj, event)) return -1;
|
|
17890
17894
|
let mode = (0, _yuneta_gobj_js.gobj_read_str_attr)(src, "form_mode");
|
|
17891
17895
|
(0, _yuneta_gobj_js.gobj_publish_event)(gobj, mode === "create" ? "EV_CREATE_RECORD" : "EV_UPDATE_RECORD", {
|
|
17892
17896
|
topic_name: (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "topic_name"),
|
|
@@ -18302,6 +18306,7 @@ var attrs_table$3 = [
|
|
|
18302
18306
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "treedb_name", 0, null, "Remote service treedb name"),
|
|
18303
18307
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_DICT, "descs", 0, null, "Descriptions of topics obtained"),
|
|
18304
18308
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "system", 0, false, "Manage system topics (true) or user topics (false)"),
|
|
18309
|
+
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "readonly", 0, false, "The whole treedb is read-only: the graph drops its `edition` mode (the only one that draws write affordances) and refuses every write event. Set it when this yuno is not the MASTER of the treedb's tranger -- only the master can write, and the yuno answers 'READ-ONLY' to a write since SDK 7.13.0 (ask `treedb-info`)"),
|
|
18305
18310
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "back_route", 0, "", "Optional hash route for a '← topics' button back to the topics grid (host-supplied; empty = no button)"),
|
|
18306
18311
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "base_route", 0, "", "This view's base route (host-supplied); used to declare its per-topic focus sub-routes to the site map (ROUTING.md contributor)."),
|
|
18307
18312
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_DICT, "records", 0, "{}", "Data of topics"),
|
|
@@ -18352,6 +18357,7 @@ function mt_create$3(gobj) {
|
|
|
18352
18357
|
if (!subscriber) subscriber = (0, _yuneta_gobj_js.gobj_parent)(gobj);
|
|
18353
18358
|
(0, _yuneta_gobj_js.gobj_subscribe_event)(gobj, null, {}, subscriber);
|
|
18354
18359
|
if (!priv.treedb_name) (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_name)(gobj)} -> treedb_name not configured`);
|
|
18360
|
+
if ((0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "readonly") && priv.operation_mode === "edition") (0, _yuneta_gobj_js.gobj_write_str_attr)(gobj, "operation_mode", "reading");
|
|
18355
18361
|
let canvas_id = (0, _yuneta_gobj_js.clean_name)((0, _yuneta_gobj_js.gobj_name)(gobj)) + "-canvas";
|
|
18356
18362
|
(0, _yuneta_gobj_js.gobj_write_attr)(gobj, "canvas_id", canvas_id);
|
|
18357
18363
|
let $container_canvas = build_ui$3(gobj).querySelector(`#${priv.canvas_id}`);
|
|
@@ -18506,6 +18512,8 @@ function destroy_ui(gobj) {
|
|
|
18506
18512
|
************************************************************/
|
|
18507
18513
|
function make_toolbar(gobj) {
|
|
18508
18514
|
gobj.priv;
|
|
18515
|
+
let modes = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "operation_modes");
|
|
18516
|
+
if ((0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "readonly")) modes = modes.filter((mode) => mode !== "edition");
|
|
18509
18517
|
const $toolbar_header = yui_toolbar({}, [
|
|
18510
18518
|
[
|
|
18511
18519
|
"div",
|
|
@@ -18544,7 +18552,7 @@ function make_toolbar(gobj) {
|
|
|
18544
18552
|
[[
|
|
18545
18553
|
"select",
|
|
18546
18554
|
{ class: "GRAPH_MODE_SELECT" },
|
|
18547
|
-
|
|
18555
|
+
modes.map((item) => [
|
|
18548
18556
|
"option",
|
|
18549
18557
|
{},
|
|
18550
18558
|
item
|
|
@@ -19096,6 +19104,19 @@ function refresh_data(gobj) {
|
|
|
19096
19104
|
if (priv.gobj_nodes_tree) (0, _yuneta_gobj_js.gobj_send_event)(priv.gobj_nodes_tree, "EV_CLEAR_DATA", {}, gobj);
|
|
19097
19105
|
request_treedb_descs(gobj);
|
|
19098
19106
|
}
|
|
19107
|
+
/********************************************
|
|
19108
|
+
* The last gate before a write leaves for the yuno. The `edition`
|
|
19109
|
+
* mode is gone on a replica, so no button can raise these -- but the
|
|
19110
|
+
* G6 child also publishes them from its undo/redo history and from
|
|
19111
|
+
* saving the node geometry, and a write that got this far would
|
|
19112
|
+
* travel, be refused by the yuno, and come back as a toast with the
|
|
19113
|
+
* graph already redrawn.
|
|
19114
|
+
********************************************/
|
|
19115
|
+
function refuse_if_readonly(gobj, event) {
|
|
19116
|
+
if (!(0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "readonly")) return false;
|
|
19117
|
+
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: ${event} refused, treedb '${(0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "treedb_name")}' is READ-ONLY`);
|
|
19118
|
+
return true;
|
|
19119
|
+
}
|
|
19099
19120
|
/***************************
|
|
19100
19121
|
* Actions
|
|
19101
19122
|
***************************/
|
|
@@ -19164,7 +19185,15 @@ function ac_mt_command_answer(gobj, event, kw, src) {
|
|
|
19164
19185
|
case "update-node":
|
|
19165
19186
|
case "delete-node":
|
|
19166
19187
|
case "link-nodes":
|
|
19167
|
-
case "unlink-nodes":
|
|
19188
|
+
case "unlink-nodes":
|
|
19189
|
+
if (result >= 0 && (0, _yuneta_gobj_js.kw_get_str)(gobj, kw_command, "topic_name", "", 0) !== "__graphs__") (0, _yuneta_gobj_js.gobj_publish_event)(gobj, "EV_RECORD_WRITTEN", {
|
|
19190
|
+
treedb_name: (0, _yuneta_gobj_js.kw_get_str)(gobj, kw_command, "treedb_name", "", 0),
|
|
19191
|
+
topic_name: (0, _yuneta_gobj_js.kw_get_str)(gobj, kw_command, "topic_name", "", 0),
|
|
19192
|
+
record: (0, _yuneta_gobj_js.kw_get_dict)(gobj, kw_command, "record", {}, 0),
|
|
19193
|
+
created: command === "create-node",
|
|
19194
|
+
command
|
|
19195
|
+
});
|
|
19196
|
+
break;
|
|
19168
19197
|
default: (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)} Command unknown: ${command}`);
|
|
19169
19198
|
}
|
|
19170
19199
|
return 0;
|
|
@@ -19383,7 +19412,9 @@ function ac_edge_clicked(gobj, event, kw, src) {
|
|
|
19383
19412
|
* Send to backend
|
|
19384
19413
|
********************************************/
|
|
19385
19414
|
function ac_create_node(gobj, event, kw, src) {
|
|
19386
|
-
let
|
|
19415
|
+
let priv = gobj.priv;
|
|
19416
|
+
if (refuse_if_readonly(gobj, event)) return -1;
|
|
19417
|
+
let treedb_name = priv.treedb_name;
|
|
19387
19418
|
let topic_name = kw.topic_name;
|
|
19388
19419
|
let record = kw.record;
|
|
19389
19420
|
return treedb_create_node(gobj, treedb_name, topic_name, record, kw.options || {});
|
|
@@ -19398,7 +19429,9 @@ function ac_create_node(gobj, event, kw, src) {
|
|
|
19398
19429
|
* Send to backend
|
|
19399
19430
|
********************************************/
|
|
19400
19431
|
function ac_update_node(gobj, event, kw, src) {
|
|
19401
|
-
let
|
|
19432
|
+
let priv = gobj.priv;
|
|
19433
|
+
if (refuse_if_readonly(gobj, event)) return -1;
|
|
19434
|
+
let treedb_name = priv.treedb_name;
|
|
19402
19435
|
let topic_name = kw.topic_name;
|
|
19403
19436
|
let record = kw.record;
|
|
19404
19437
|
return treedb_update_node(gobj, treedb_name, topic_name, record, kw.options || {});
|
|
@@ -19413,7 +19446,9 @@ function ac_update_node(gobj, event, kw, src) {
|
|
|
19413
19446
|
* Send to backend
|
|
19414
19447
|
********************************************/
|
|
19415
19448
|
function ac_delete_node(gobj, event, kw, src) {
|
|
19416
|
-
let
|
|
19449
|
+
let priv = gobj.priv;
|
|
19450
|
+
if (refuse_if_readonly(gobj, event)) return -1;
|
|
19451
|
+
let treedb_name = priv.treedb_name;
|
|
19417
19452
|
let topic_name = kw.topic_name;
|
|
19418
19453
|
let record = kw.record;
|
|
19419
19454
|
return treedb_delete_node(gobj, treedb_name, topic_name, record, kw.options || {});
|
|
@@ -19428,7 +19463,9 @@ function ac_delete_node(gobj, event, kw, src) {
|
|
|
19428
19463
|
* Send to backend
|
|
19429
19464
|
********************************************/
|
|
19430
19465
|
function ac_link_nodes(gobj, event, kw, src) {
|
|
19431
|
-
let
|
|
19466
|
+
let priv = gobj.priv;
|
|
19467
|
+
if (refuse_if_readonly(gobj, event)) return -1;
|
|
19468
|
+
let treedb_name = priv.treedb_name;
|
|
19432
19469
|
let parent_ref = kw.parent_ref;
|
|
19433
19470
|
let child_ref = kw.child_ref;
|
|
19434
19471
|
return treedb_link_nodes(gobj, treedb_name, parent_ref, child_ref, kw.options || {});
|
|
@@ -19443,7 +19480,9 @@ function ac_link_nodes(gobj, event, kw, src) {
|
|
|
19443
19480
|
* Send to backend
|
|
19444
19481
|
********************************************/
|
|
19445
19482
|
function ac_unlink_nodes(gobj, event, kw, src) {
|
|
19446
|
-
let
|
|
19483
|
+
let priv = gobj.priv;
|
|
19484
|
+
if (refuse_if_readonly(gobj, event)) return -1;
|
|
19485
|
+
let treedb_name = priv.treedb_name;
|
|
19447
19486
|
let parent_ref = kw.parent_ref;
|
|
19448
19487
|
let child_ref = kw.child_ref;
|
|
19449
19488
|
return treedb_unlink_nodes(gobj, treedb_name, parent_ref, child_ref, kw.options || {});
|
|
@@ -19483,6 +19522,10 @@ function ac_set_layout$1(gobj, event, kw, src) {
|
|
|
19483
19522
|
function ac_set_operation_mode$1(gobj, event, kw, src) {
|
|
19484
19523
|
let priv = gobj.priv;
|
|
19485
19524
|
let operation_mode = kw.operation_mode;
|
|
19525
|
+
if (operation_mode === "edition" && (0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "readonly")) {
|
|
19526
|
+
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: edition mode refused, treedb '${(0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "treedb_name")}' is READ-ONLY`);
|
|
19527
|
+
return -1;
|
|
19528
|
+
}
|
|
19486
19529
|
(0, _yuneta_gobj_js.gobj_write_str_attr)(gobj, "operation_mode", operation_mode);
|
|
19487
19530
|
(0, _yuneta_gobj_js.gobj_save_persistent_attrs)(gobj, "operation_mode");
|
|
19488
19531
|
let $operation_mode_select = priv.$container.querySelector(".GRAPH_MODE_SELECT");
|
|
@@ -19704,6 +19747,7 @@ function create_gclass$3(gclass_name) {
|
|
|
19704
19747
|
["EV_SET_OPERATION_MODE", 0],
|
|
19705
19748
|
["EV_SET_FOCUS_TOPIC", 0],
|
|
19706
19749
|
["EV_OPERATION_MODE_CHANGED", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT | _yuneta_gobj_js.event_flag_t.EVF_NO_WARN_SUBS],
|
|
19750
|
+
["EV_RECORD_WRITTEN", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT | _yuneta_gobj_js.event_flag_t.EVF_NO_WARN_SUBS],
|
|
19707
19751
|
["EV_SHOW", 0],
|
|
19708
19752
|
["EV_HIDE", 0],
|
|
19709
19753
|
["EV_TRANSPORT_STATE", 0]
|
package/dist/gobj-ui.es.js
CHANGED
|
@@ -15500,7 +15500,7 @@ function request_print_tranger$1(gobj, path) {
|
|
|
15500
15500
|
* would travel, be refused by the yuno, and come back as a toast --
|
|
15501
15501
|
* after the table had already drawn the row.
|
|
15502
15502
|
***************************************************************/
|
|
15503
|
-
function refuse_if_readonly$
|
|
15503
|
+
function refuse_if_readonly$2(gobj, event) {
|
|
15504
15504
|
if (!gobj_read_bool_attr(gobj, "readonly")) return false;
|
|
15505
15505
|
log_error(`${gobj_short_name(gobj)}: ${event} refused, treedb '${gobj_read_str_attr(gobj, "treedb_name")}' is READ-ONLY`);
|
|
15506
15506
|
return true;
|
|
@@ -15572,7 +15572,8 @@ function ac_mt_command_answer$1(gobj, event, kw, src) {
|
|
|
15572
15572
|
treedb_name: kw_get_str(gobj, kw_command, "treedb_name", "", 0),
|
|
15573
15573
|
topic_name: kw_get_str(gobj, kw_command, "topic_name", "", 0),
|
|
15574
15574
|
record: kw_get_dict(gobj, kw_command, "record", {}, 0),
|
|
15575
|
-
created: command === "create-node"
|
|
15575
|
+
created: command === "create-node",
|
|
15576
|
+
command
|
|
15576
15577
|
});
|
|
15577
15578
|
break;
|
|
15578
15579
|
case "delete-node": break;
|
|
@@ -15777,7 +15778,7 @@ function ac_treedb_node_deleted$1(gobj, event, kw, src) {
|
|
|
15777
15778
|
* }
|
|
15778
15779
|
********************************************/
|
|
15779
15780
|
function ac_create_record(gobj, event, kw, src) {
|
|
15780
|
-
if (refuse_if_readonly$
|
|
15781
|
+
if (refuse_if_readonly$2(gobj, event)) return -1;
|
|
15781
15782
|
let treedb_name = gobj_read_str_attr(gobj, "treedb_name");
|
|
15782
15783
|
let topic_name = gobj_read_attr(src, "topic_name");
|
|
15783
15784
|
let record = kw.record;
|
|
@@ -15795,7 +15796,7 @@ function ac_create_record(gobj, event, kw, src) {
|
|
|
15795
15796
|
* }
|
|
15796
15797
|
********************************************/
|
|
15797
15798
|
function ac_update_record(gobj, event, kw, src) {
|
|
15798
|
-
if (refuse_if_readonly$
|
|
15799
|
+
if (refuse_if_readonly$2(gobj, event)) return -1;
|
|
15799
15800
|
let treedb_name = gobj_read_str_attr(gobj, "treedb_name");
|
|
15800
15801
|
let topic_name = gobj_read_attr(src, "topic_name");
|
|
15801
15802
|
let record = kw.record;
|
|
@@ -15812,7 +15813,7 @@ function ac_update_record(gobj, event, kw, src) {
|
|
|
15812
15813
|
* }
|
|
15813
15814
|
********************************************/
|
|
15814
15815
|
function ac_delete_record(gobj, event, kw, src) {
|
|
15815
|
-
if (refuse_if_readonly$
|
|
15816
|
+
if (refuse_if_readonly$2(gobj, event)) return -1;
|
|
15816
15817
|
let treedb_name = gobj_read_str_attr(gobj, "treedb_name");
|
|
15817
15818
|
let topic_name = gobj_read_attr(src, "topic_name");
|
|
15818
15819
|
let record = kw.record;
|
|
@@ -17633,7 +17634,7 @@ function write_plan(gobj) {
|
|
|
17633
17634
|
* A read-only topic refuses the write EVENTS too, not only their
|
|
17634
17635
|
* buttons.
|
|
17635
17636
|
***************************************************************/
|
|
17636
|
-
function refuse_if_readonly(gobj, event) {
|
|
17637
|
+
function refuse_if_readonly$1(gobj, event) {
|
|
17637
17638
|
if (!gobj_read_bool_attr(gobj, "readonly")) return false;
|
|
17638
17639
|
log_error(`${gobj_short_name(gobj)}: ${event} refused, topic '${gobj_read_str_attr(gobj, "topic_name")}' is READ-ONLY`);
|
|
17639
17640
|
return true;
|
|
@@ -17705,7 +17706,10 @@ function ac_load_node_updated(gobj, event, kw, src) {
|
|
|
17705
17706
|
return -1;
|
|
17706
17707
|
}
|
|
17707
17708
|
let tabulator = gobj_read_attr(gobj, "tabulator");
|
|
17708
|
-
if (tabulator)
|
|
17709
|
+
if (tabulator) {
|
|
17710
|
+
for (let record of data) if (tabulator.getRow(record.id)) tabulator.updateData([record]);
|
|
17711
|
+
else if (tabulator.getDataCount() > 0) log_error(`${gobj_short_name(gobj)}: updated node is not in the loaded table: ${record.id}`);
|
|
17712
|
+
}
|
|
17709
17713
|
return 0;
|
|
17710
17714
|
}
|
|
17711
17715
|
/************************************************************
|
|
@@ -17730,7 +17734,7 @@ function ac_node_deleted$1(gobj, event, kw, src) {
|
|
|
17730
17734
|
* and the Op column is visible (with row's edit/delete icons)
|
|
17731
17735
|
************************************************************/
|
|
17732
17736
|
function ac_edition_mode(gobj, event, kw, src) {
|
|
17733
|
-
if (refuse_if_readonly(gobj, event)) return -1;
|
|
17737
|
+
if (refuse_if_readonly$1(gobj, event)) return -1;
|
|
17734
17738
|
let tabulator = gobj_read_attr(gobj, "tabulator");
|
|
17735
17739
|
let editable = gobj_read_bool_attr(gobj, "editable");
|
|
17736
17740
|
editable = !editable;
|
|
@@ -17774,7 +17778,7 @@ function ac_edition_mode(gobj, event, kw, src) {
|
|
|
17774
17778
|
* - the backend will broadcast the new record that will be added to table
|
|
17775
17779
|
************************************************************/
|
|
17776
17780
|
function ac_new_row(gobj, event, kw, src) {
|
|
17777
|
-
if (refuse_if_readonly(gobj, event)) return -1;
|
|
17781
|
+
if (refuse_if_readonly$1(gobj, event)) return -1;
|
|
17778
17782
|
let row = {};
|
|
17779
17783
|
let desc = gobj_read_attr(gobj, "desc");
|
|
17780
17784
|
for (let i = 0; i < desc.cols.length; i++) {
|
|
@@ -17804,7 +17808,7 @@ function ac_new_row(gobj, event, kw, src) {
|
|
|
17804
17808
|
* kw {index:, row:}
|
|
17805
17809
|
************************************************************/
|
|
17806
17810
|
function ac_delete_rows(gobj, event, kw, src) {
|
|
17807
|
-
if (refuse_if_readonly(gobj, event)) return -1;
|
|
17811
|
+
if (refuse_if_readonly$1(gobj, event)) return -1;
|
|
17808
17812
|
let tabulator = gobj_read_attr(gobj, "tabulator");
|
|
17809
17813
|
if (json_size(kw) === 0) {
|
|
17810
17814
|
let rows = tabulator.getSelectedData();
|
|
@@ -17868,7 +17872,7 @@ function ac_copy_rows(gobj, event, kw, src) {
|
|
|
17868
17872
|
* From internal
|
|
17869
17873
|
************************************************************/
|
|
17870
17874
|
function ac_paste_rows(gobj, event, kw, src) {
|
|
17871
|
-
if (refuse_if_readonly(gobj, event)) return -1;
|
|
17875
|
+
if (refuse_if_readonly$1(gobj, event)) return -1;
|
|
17872
17876
|
if (kw.topic_name === gobj_read_str_attr(gobj, "topic_name") && kw.treedb_name === gobj_read_str_attr(gobj, "treedb_name")) for (let row of kw.rows) gobj_publish_event(gobj, "EV_CREATE_RECORD", {
|
|
17873
17877
|
topic_name: gobj_read_str_attr(gobj, "topic_name"),
|
|
17874
17878
|
record: row
|
|
@@ -17881,7 +17885,7 @@ function ac_paste_rows(gobj, event, kw, src) {
|
|
|
17881
17885
|
* fkeys, times and numbers). Route by the form's mode.
|
|
17882
17886
|
************************************************************/
|
|
17883
17887
|
function ac_form_save_record(gobj, event, kw, src) {
|
|
17884
|
-
if (refuse_if_readonly(gobj, event)) return -1;
|
|
17888
|
+
if (refuse_if_readonly$1(gobj, event)) return -1;
|
|
17885
17889
|
let mode = gobj_read_str_attr(src, "form_mode");
|
|
17886
17890
|
gobj_publish_event(gobj, mode === "create" ? "EV_CREATE_RECORD" : "EV_UPDATE_RECORD", {
|
|
17887
17891
|
topic_name: gobj_read_str_attr(gobj, "topic_name"),
|
|
@@ -18289,6 +18293,7 @@ var attrs_table$3 = [
|
|
|
18289
18293
|
SDATA(data_type_t.DTP_STRING, "treedb_name", 0, null, "Remote service treedb name"),
|
|
18290
18294
|
SDATA(data_type_t.DTP_DICT, "descs", 0, null, "Descriptions of topics obtained"),
|
|
18291
18295
|
SDATA(data_type_t.DTP_BOOLEAN, "system", 0, false, "Manage system topics (true) or user topics (false)"),
|
|
18296
|
+
SDATA(data_type_t.DTP_BOOLEAN, "readonly", 0, false, "The whole treedb is read-only: the graph drops its `edition` mode (the only one that draws write affordances) and refuses every write event. Set it when this yuno is not the MASTER of the treedb's tranger -- only the master can write, and the yuno answers 'READ-ONLY' to a write since SDK 7.13.0 (ask `treedb-info`)"),
|
|
18292
18297
|
SDATA(data_type_t.DTP_STRING, "back_route", 0, "", "Optional hash route for a '← topics' button back to the topics grid (host-supplied; empty = no button)"),
|
|
18293
18298
|
SDATA(data_type_t.DTP_STRING, "base_route", 0, "", "This view's base route (host-supplied); used to declare its per-topic focus sub-routes to the site map (ROUTING.md contributor)."),
|
|
18294
18299
|
SDATA(data_type_t.DTP_DICT, "records", 0, "{}", "Data of topics"),
|
|
@@ -18339,6 +18344,7 @@ function mt_create$3(gobj) {
|
|
|
18339
18344
|
if (!subscriber) subscriber = gobj_parent(gobj);
|
|
18340
18345
|
gobj_subscribe_event(gobj, null, {}, subscriber);
|
|
18341
18346
|
if (!priv.treedb_name) log_error(`${gobj_name(gobj)} -> treedb_name not configured`);
|
|
18347
|
+
if (gobj_read_bool_attr(gobj, "readonly") && priv.operation_mode === "edition") gobj_write_str_attr(gobj, "operation_mode", "reading");
|
|
18342
18348
|
let canvas_id = clean_name(gobj_name(gobj)) + "-canvas";
|
|
18343
18349
|
gobj_write_attr(gobj, "canvas_id", canvas_id);
|
|
18344
18350
|
let $container_canvas = build_ui$3(gobj).querySelector(`#${priv.canvas_id}`);
|
|
@@ -18493,6 +18499,8 @@ function destroy_ui(gobj) {
|
|
|
18493
18499
|
************************************************************/
|
|
18494
18500
|
function make_toolbar(gobj) {
|
|
18495
18501
|
gobj.priv;
|
|
18502
|
+
let modes = gobj_read_attr(gobj, "operation_modes");
|
|
18503
|
+
if (gobj_read_bool_attr(gobj, "readonly")) modes = modes.filter((mode) => mode !== "edition");
|
|
18496
18504
|
const $toolbar_header = yui_toolbar({}, [
|
|
18497
18505
|
[
|
|
18498
18506
|
"div",
|
|
@@ -18531,7 +18539,7 @@ function make_toolbar(gobj) {
|
|
|
18531
18539
|
[[
|
|
18532
18540
|
"select",
|
|
18533
18541
|
{ class: "GRAPH_MODE_SELECT" },
|
|
18534
|
-
|
|
18542
|
+
modes.map((item) => [
|
|
18535
18543
|
"option",
|
|
18536
18544
|
{},
|
|
18537
18545
|
item
|
|
@@ -19083,6 +19091,19 @@ function refresh_data(gobj) {
|
|
|
19083
19091
|
if (priv.gobj_nodes_tree) gobj_send_event(priv.gobj_nodes_tree, "EV_CLEAR_DATA", {}, gobj);
|
|
19084
19092
|
request_treedb_descs(gobj);
|
|
19085
19093
|
}
|
|
19094
|
+
/********************************************
|
|
19095
|
+
* The last gate before a write leaves for the yuno. The `edition`
|
|
19096
|
+
* mode is gone on a replica, so no button can raise these -- but the
|
|
19097
|
+
* G6 child also publishes them from its undo/redo history and from
|
|
19098
|
+
* saving the node geometry, and a write that got this far would
|
|
19099
|
+
* travel, be refused by the yuno, and come back as a toast with the
|
|
19100
|
+
* graph already redrawn.
|
|
19101
|
+
********************************************/
|
|
19102
|
+
function refuse_if_readonly(gobj, event) {
|
|
19103
|
+
if (!gobj_read_bool_attr(gobj, "readonly")) return false;
|
|
19104
|
+
log_error(`${gobj_short_name(gobj)}: ${event} refused, treedb '${gobj_read_str_attr(gobj, "treedb_name")}' is READ-ONLY`);
|
|
19105
|
+
return true;
|
|
19106
|
+
}
|
|
19086
19107
|
/***************************
|
|
19087
19108
|
* Actions
|
|
19088
19109
|
***************************/
|
|
@@ -19151,7 +19172,15 @@ function ac_mt_command_answer(gobj, event, kw, src) {
|
|
|
19151
19172
|
case "update-node":
|
|
19152
19173
|
case "delete-node":
|
|
19153
19174
|
case "link-nodes":
|
|
19154
|
-
case "unlink-nodes":
|
|
19175
|
+
case "unlink-nodes":
|
|
19176
|
+
if (result >= 0 && kw_get_str(gobj, kw_command, "topic_name", "", 0) !== "__graphs__") gobj_publish_event(gobj, "EV_RECORD_WRITTEN", {
|
|
19177
|
+
treedb_name: kw_get_str(gobj, kw_command, "treedb_name", "", 0),
|
|
19178
|
+
topic_name: kw_get_str(gobj, kw_command, "topic_name", "", 0),
|
|
19179
|
+
record: kw_get_dict(gobj, kw_command, "record", {}, 0),
|
|
19180
|
+
created: command === "create-node",
|
|
19181
|
+
command
|
|
19182
|
+
});
|
|
19183
|
+
break;
|
|
19155
19184
|
default: log_error(`${gobj_short_name(gobj)} Command unknown: ${command}`);
|
|
19156
19185
|
}
|
|
19157
19186
|
return 0;
|
|
@@ -19370,7 +19399,9 @@ function ac_edge_clicked(gobj, event, kw, src) {
|
|
|
19370
19399
|
* Send to backend
|
|
19371
19400
|
********************************************/
|
|
19372
19401
|
function ac_create_node(gobj, event, kw, src) {
|
|
19373
|
-
let
|
|
19402
|
+
let priv = gobj.priv;
|
|
19403
|
+
if (refuse_if_readonly(gobj, event)) return -1;
|
|
19404
|
+
let treedb_name = priv.treedb_name;
|
|
19374
19405
|
let topic_name = kw.topic_name;
|
|
19375
19406
|
let record = kw.record;
|
|
19376
19407
|
return treedb_create_node(gobj, treedb_name, topic_name, record, kw.options || {});
|
|
@@ -19385,7 +19416,9 @@ function ac_create_node(gobj, event, kw, src) {
|
|
|
19385
19416
|
* Send to backend
|
|
19386
19417
|
********************************************/
|
|
19387
19418
|
function ac_update_node(gobj, event, kw, src) {
|
|
19388
|
-
let
|
|
19419
|
+
let priv = gobj.priv;
|
|
19420
|
+
if (refuse_if_readonly(gobj, event)) return -1;
|
|
19421
|
+
let treedb_name = priv.treedb_name;
|
|
19389
19422
|
let topic_name = kw.topic_name;
|
|
19390
19423
|
let record = kw.record;
|
|
19391
19424
|
return treedb_update_node(gobj, treedb_name, topic_name, record, kw.options || {});
|
|
@@ -19400,7 +19433,9 @@ function ac_update_node(gobj, event, kw, src) {
|
|
|
19400
19433
|
* Send to backend
|
|
19401
19434
|
********************************************/
|
|
19402
19435
|
function ac_delete_node(gobj, event, kw, src) {
|
|
19403
|
-
let
|
|
19436
|
+
let priv = gobj.priv;
|
|
19437
|
+
if (refuse_if_readonly(gobj, event)) return -1;
|
|
19438
|
+
let treedb_name = priv.treedb_name;
|
|
19404
19439
|
let topic_name = kw.topic_name;
|
|
19405
19440
|
let record = kw.record;
|
|
19406
19441
|
return treedb_delete_node(gobj, treedb_name, topic_name, record, kw.options || {});
|
|
@@ -19415,7 +19450,9 @@ function ac_delete_node(gobj, event, kw, src) {
|
|
|
19415
19450
|
* Send to backend
|
|
19416
19451
|
********************************************/
|
|
19417
19452
|
function ac_link_nodes(gobj, event, kw, src) {
|
|
19418
|
-
let
|
|
19453
|
+
let priv = gobj.priv;
|
|
19454
|
+
if (refuse_if_readonly(gobj, event)) return -1;
|
|
19455
|
+
let treedb_name = priv.treedb_name;
|
|
19419
19456
|
let parent_ref = kw.parent_ref;
|
|
19420
19457
|
let child_ref = kw.child_ref;
|
|
19421
19458
|
return treedb_link_nodes(gobj, treedb_name, parent_ref, child_ref, kw.options || {});
|
|
@@ -19430,7 +19467,9 @@ function ac_link_nodes(gobj, event, kw, src) {
|
|
|
19430
19467
|
* Send to backend
|
|
19431
19468
|
********************************************/
|
|
19432
19469
|
function ac_unlink_nodes(gobj, event, kw, src) {
|
|
19433
|
-
let
|
|
19470
|
+
let priv = gobj.priv;
|
|
19471
|
+
if (refuse_if_readonly(gobj, event)) return -1;
|
|
19472
|
+
let treedb_name = priv.treedb_name;
|
|
19434
19473
|
let parent_ref = kw.parent_ref;
|
|
19435
19474
|
let child_ref = kw.child_ref;
|
|
19436
19475
|
return treedb_unlink_nodes(gobj, treedb_name, parent_ref, child_ref, kw.options || {});
|
|
@@ -19470,6 +19509,10 @@ function ac_set_layout$1(gobj, event, kw, src) {
|
|
|
19470
19509
|
function ac_set_operation_mode$1(gobj, event, kw, src) {
|
|
19471
19510
|
let priv = gobj.priv;
|
|
19472
19511
|
let operation_mode = kw.operation_mode;
|
|
19512
|
+
if (operation_mode === "edition" && gobj_read_bool_attr(gobj, "readonly")) {
|
|
19513
|
+
log_error(`${gobj_short_name(gobj)}: edition mode refused, treedb '${gobj_read_str_attr(gobj, "treedb_name")}' is READ-ONLY`);
|
|
19514
|
+
return -1;
|
|
19515
|
+
}
|
|
19473
19516
|
gobj_write_str_attr(gobj, "operation_mode", operation_mode);
|
|
19474
19517
|
gobj_save_persistent_attrs(gobj, "operation_mode");
|
|
19475
19518
|
let $operation_mode_select = priv.$container.querySelector(".GRAPH_MODE_SELECT");
|
|
@@ -19691,6 +19734,7 @@ function create_gclass$3(gclass_name) {
|
|
|
19691
19734
|
["EV_SET_OPERATION_MODE", 0],
|
|
19692
19735
|
["EV_SET_FOCUS_TOPIC", 0],
|
|
19693
19736
|
["EV_OPERATION_MODE_CHANGED", event_flag_t.EVF_OUTPUT_EVENT | event_flag_t.EVF_NO_WARN_SUBS],
|
|
19737
|
+
["EV_RECORD_WRITTEN", event_flag_t.EVF_OUTPUT_EVENT | event_flag_t.EVF_NO_WARN_SUBS],
|
|
19694
19738
|
["EV_SHOW", 0],
|
|
19695
19739
|
["EV_HIDE", 0],
|
|
19696
19740
|
["EV_TRANSPORT_STATE", 0]
|
package/package.json
CHANGED
|
@@ -115,6 +115,7 @@ SDATA(data_type_t.DTP_POINTER, "gobj_remote_yuno", 0, null, "Remote Yuno to
|
|
|
115
115
|
SDATA(data_type_t.DTP_STRING, "treedb_name", 0, null, "Remote service treedb name"),
|
|
116
116
|
SDATA(data_type_t.DTP_DICT, "descs", 0, null, "Descriptions of topics obtained"),
|
|
117
117
|
SDATA(data_type_t.DTP_BOOLEAN, "system", 0, false, "Manage system topics (true) or user topics (false)"),
|
|
118
|
+
SDATA(data_type_t.DTP_BOOLEAN, "readonly", 0, false, "The whole treedb is read-only: the graph drops its `edition` mode (the only one that draws write affordances) and refuses every write event. Set it when this yuno is not the MASTER of the treedb's tranger -- only the master can write, and the yuno answers 'READ-ONLY' to a write since SDK 7.13.0 (ask `treedb-info`)"),
|
|
118
119
|
SDATA(data_type_t.DTP_STRING, "back_route", 0, "", "Optional hash route for a '← topics' button back to the topics grid (host-supplied; empty = no button)"),
|
|
119
120
|
SDATA(data_type_t.DTP_STRING, "base_route", 0, "", "This view's base route (host-supplied); used to declare its per-topic focus sub-routes to the site map (ROUTING.md contributor)."),
|
|
120
121
|
SDATA(data_type_t.DTP_DICT, "records", 0, "{}", "Data of topics"),
|
|
@@ -193,6 +194,16 @@ function mt_create(gobj)
|
|
|
193
194
|
log_error(`${gobj_name(gobj)} -> treedb_name not configured`);
|
|
194
195
|
}
|
|
195
196
|
|
|
197
|
+
/*
|
|
198
|
+
* `edition` is the ONLY mode that draws write affordances, and the
|
|
199
|
+
* mode is a PERSISTED user preference: a graph that was left in
|
|
200
|
+
* edition on a master would come back in edition on a replica.
|
|
201
|
+
* Fall back to `reading` before the G6 child is born with it.
|
|
202
|
+
*/
|
|
203
|
+
if(gobj_read_bool_attr(gobj, "readonly") && priv.operation_mode === "edition") {
|
|
204
|
+
gobj_write_str_attr(gobj, "operation_mode", "reading");
|
|
205
|
+
}
|
|
206
|
+
|
|
196
207
|
/*
|
|
197
208
|
* set canvas_id, before build_ui()
|
|
198
209
|
*/
|
|
@@ -433,6 +444,11 @@ function make_toolbar(gobj)
|
|
|
433
444
|
* Top Header toolbar
|
|
434
445
|
*---------------------------------------*/
|
|
435
446
|
let modes = gobj_read_attr(gobj, "operation_modes");
|
|
447
|
+
if(gobj_read_bool_attr(gobj, "readonly")) {
|
|
448
|
+
/* A mode the user cannot have is not offered: `edition` is the
|
|
449
|
+
* one that draws the create / delete / link affordances. */
|
|
450
|
+
modes = modes.filter(mode => mode !== "edition");
|
|
451
|
+
}
|
|
436
452
|
let mode_options = modes.map(item => ['option', {}, item]);
|
|
437
453
|
|
|
438
454
|
/*
|
|
@@ -1342,6 +1358,24 @@ function refresh_data(gobj)
|
|
|
1342
1358
|
request_treedb_descs(gobj);
|
|
1343
1359
|
}
|
|
1344
1360
|
|
|
1361
|
+
/********************************************
|
|
1362
|
+
* The last gate before a write leaves for the yuno. The `edition`
|
|
1363
|
+
* mode is gone on a replica, so no button can raise these -- but the
|
|
1364
|
+
* G6 child also publishes them from its undo/redo history and from
|
|
1365
|
+
* saving the node geometry, and a write that got this far would
|
|
1366
|
+
* travel, be refused by the yuno, and come back as a toast with the
|
|
1367
|
+
* graph already redrawn.
|
|
1368
|
+
********************************************/
|
|
1369
|
+
function refuse_if_readonly(gobj, event)
|
|
1370
|
+
{
|
|
1371
|
+
if(!gobj_read_bool_attr(gobj, "readonly")) {
|
|
1372
|
+
return false;
|
|
1373
|
+
}
|
|
1374
|
+
log_error(`${gobj_short_name(gobj)}: ${event} refused, treedb ` +
|
|
1375
|
+
`'${gobj_read_str_attr(gobj, "treedb_name")}' is READ-ONLY`);
|
|
1376
|
+
return true;
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1345
1379
|
|
|
1346
1380
|
|
|
1347
1381
|
|
|
@@ -1468,7 +1502,36 @@ function ac_mt_command_answer(gobj, event, kw, src)
|
|
|
1468
1502
|
case "delete-node":
|
|
1469
1503
|
case "link-nodes":
|
|
1470
1504
|
case "unlink-nodes":
|
|
1471
|
-
|
|
1505
|
+
/*
|
|
1506
|
+
* The graph redraws itself from the treedb's own EV_TREEDB_NODE_*
|
|
1507
|
+
* events, which arrive for EVERY writer. This says what those
|
|
1508
|
+
* cannot: THIS view has just written, and the yuno took it. A host
|
|
1509
|
+
* whose save is not finished with the record -- a schema editor,
|
|
1510
|
+
* where the yuno still has to be restarted to re-read the schema --
|
|
1511
|
+
* needs exactly that, and cannot use the node events without
|
|
1512
|
+
* answering its own writes in a loop.
|
|
1513
|
+
*
|
|
1514
|
+
* Same event and same kw as C_YUI_TREEDB_TOPICS, plus `command`:
|
|
1515
|
+
* in a graph a LINK is as much a write as a record is, and the
|
|
1516
|
+
* three of them carry different halves of the kw.
|
|
1517
|
+
*
|
|
1518
|
+
* `__graphs__` is EXCLUDED, and that exclusion is the point of
|
|
1519
|
+
* reading this comment: the view writes that topic ITSELF, one
|
|
1520
|
+
* record per topic, every time the layout is saved. It is this
|
|
1521
|
+
* view's bookkeeping, not the operator's data -- reporting it
|
|
1522
|
+
* would tell a schema editor that the schema changed because
|
|
1523
|
+
* somebody dragged a node.
|
|
1524
|
+
*/
|
|
1525
|
+
if(result >= 0 &&
|
|
1526
|
+
kw_get_str(gobj, kw_command, "topic_name", "", 0) !== "__graphs__") {
|
|
1527
|
+
gobj_publish_event(gobj, "EV_RECORD_WRITTEN", {
|
|
1528
|
+
treedb_name: kw_get_str(gobj, kw_command, "treedb_name", "", 0),
|
|
1529
|
+
topic_name: kw_get_str(gobj, kw_command, "topic_name", "", 0),
|
|
1530
|
+
record: kw_get_dict(gobj, kw_command, "record", {}, 0),
|
|
1531
|
+
created: (command === "create-node"),
|
|
1532
|
+
command: command
|
|
1533
|
+
});
|
|
1534
|
+
}
|
|
1472
1535
|
break;
|
|
1473
1536
|
|
|
1474
1537
|
default:
|
|
@@ -1787,6 +1850,11 @@ function ac_edge_clicked(gobj, event, kw, src)
|
|
|
1787
1850
|
function ac_create_node(gobj, event, kw, src)
|
|
1788
1851
|
{
|
|
1789
1852
|
let priv = gobj.priv;
|
|
1853
|
+
|
|
1854
|
+
if(refuse_if_readonly(gobj, event)) {
|
|
1855
|
+
return -1;
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1790
1858
|
let treedb_name = priv.treedb_name;
|
|
1791
1859
|
let topic_name = kw.topic_name;
|
|
1792
1860
|
let record = kw.record;
|
|
@@ -1813,6 +1881,11 @@ function ac_create_node(gobj, event, kw, src)
|
|
|
1813
1881
|
function ac_update_node(gobj, event, kw, src)
|
|
1814
1882
|
{
|
|
1815
1883
|
let priv = gobj.priv;
|
|
1884
|
+
|
|
1885
|
+
if(refuse_if_readonly(gobj, event)) {
|
|
1886
|
+
return -1;
|
|
1887
|
+
}
|
|
1888
|
+
|
|
1816
1889
|
let treedb_name = priv.treedb_name;
|
|
1817
1890
|
let topic_name = kw.topic_name;
|
|
1818
1891
|
let record = kw.record;
|
|
@@ -1839,6 +1912,11 @@ function ac_update_node(gobj, event, kw, src)
|
|
|
1839
1912
|
function ac_delete_node(gobj, event, kw, src)
|
|
1840
1913
|
{
|
|
1841
1914
|
let priv = gobj.priv;
|
|
1915
|
+
|
|
1916
|
+
if(refuse_if_readonly(gobj, event)) {
|
|
1917
|
+
return -1;
|
|
1918
|
+
}
|
|
1919
|
+
|
|
1842
1920
|
let treedb_name = priv.treedb_name;
|
|
1843
1921
|
let topic_name = kw.topic_name;
|
|
1844
1922
|
let record = kw.record;
|
|
@@ -1865,6 +1943,11 @@ function ac_delete_node(gobj, event, kw, src)
|
|
|
1865
1943
|
function ac_link_nodes(gobj, event, kw, src)
|
|
1866
1944
|
{
|
|
1867
1945
|
let priv = gobj.priv;
|
|
1946
|
+
|
|
1947
|
+
if(refuse_if_readonly(gobj, event)) {
|
|
1948
|
+
return -1;
|
|
1949
|
+
}
|
|
1950
|
+
|
|
1868
1951
|
let treedb_name = priv.treedb_name;
|
|
1869
1952
|
let parent_ref = kw.parent_ref;
|
|
1870
1953
|
let child_ref = kw.child_ref;
|
|
@@ -1891,6 +1974,11 @@ function ac_link_nodes(gobj, event, kw, src)
|
|
|
1891
1974
|
function ac_unlink_nodes(gobj, event, kw, src)
|
|
1892
1975
|
{
|
|
1893
1976
|
let priv = gobj.priv;
|
|
1977
|
+
|
|
1978
|
+
if(refuse_if_readonly(gobj, event)) {
|
|
1979
|
+
return -1;
|
|
1980
|
+
}
|
|
1981
|
+
|
|
1894
1982
|
let treedb_name = priv.treedb_name;
|
|
1895
1983
|
let parent_ref = kw.parent_ref;
|
|
1896
1984
|
let child_ref = kw.child_ref;
|
|
@@ -2001,6 +2089,14 @@ function ac_set_operation_mode(gobj, event, kw, src)
|
|
|
2001
2089
|
let priv = gobj.priv;
|
|
2002
2090
|
|
|
2003
2091
|
let operation_mode = kw.operation_mode;
|
|
2092
|
+
if(operation_mode === "edition" && gobj_read_bool_attr(gobj, "readonly")) {
|
|
2093
|
+
/* The select does not offer it on a replica, so getting here means
|
|
2094
|
+
* the event came from somewhere else -- and it is the mode that
|
|
2095
|
+
* turns on every write affordance. */
|
|
2096
|
+
log_error(`${gobj_short_name(gobj)}: edition mode refused, treedb ` +
|
|
2097
|
+
`'${gobj_read_str_attr(gobj, "treedb_name")}' is READ-ONLY`);
|
|
2098
|
+
return -1;
|
|
2099
|
+
}
|
|
2004
2100
|
gobj_write_str_attr(gobj, "operation_mode", operation_mode);
|
|
2005
2101
|
gobj_save_persistent_attrs(gobj, "operation_mode");
|
|
2006
2102
|
|
|
@@ -2168,6 +2264,8 @@ function create_gclass(gclass_name)
|
|
|
2168
2264
|
["EV_SET_FOCUS_TOPIC", 0],
|
|
2169
2265
|
["EV_OPERATION_MODE_CHANGED",
|
|
2170
2266
|
event_flag_t.EVF_OUTPUT_EVENT | event_flag_t.EVF_NO_WARN_SUBS],
|
|
2267
|
+
["EV_RECORD_WRITTEN",
|
|
2268
|
+
event_flag_t.EVF_OUTPUT_EVENT | event_flag_t.EVF_NO_WARN_SUBS],
|
|
2171
2269
|
["EV_SHOW", 0],
|
|
2172
2270
|
["EV_HIDE", 0],
|
|
2173
2271
|
["EV_TRANSPORT_STATE", 0],
|
|
@@ -2242,7 +2242,22 @@ function ac_load_node_updated(gobj, event, kw, src)
|
|
|
2242
2242
|
let tabulator = gobj_read_attr(gobj, "tabulator");
|
|
2243
2243
|
if(tabulator) {
|
|
2244
2244
|
for(let record of data) {
|
|
2245
|
-
|
|
2245
|
+
/*
|
|
2246
|
+
* updateData() REJECTS the promise on a row it cannot find and
|
|
2247
|
+
* nobody awaits it, so the rejection surfaces as an unhandled
|
|
2248
|
+
* "Update Error - Unable to find row" that names neither the
|
|
2249
|
+
* gclass nor the topic. Ask first, like ac_node_deleted does.
|
|
2250
|
+
*
|
|
2251
|
+
* A table nobody has opened holds NO rows, and a node event for
|
|
2252
|
+
* it is not news: its rows are read when the topic is shown. A
|
|
2253
|
+
* row missing from a table that IS loaded is.
|
|
2254
|
+
*/
|
|
2255
|
+
if(tabulator.getRow(record.id)) {
|
|
2256
|
+
tabulator.updateData([record]);
|
|
2257
|
+
} else if(tabulator.getDataCount() > 0) {
|
|
2258
|
+
log_error(`${gobj_short_name(gobj)}: updated node is not in ` +
|
|
2259
|
+
`the loaded table: ${record.id}`);
|
|
2260
|
+
}
|
|
2246
2261
|
}
|
|
2247
2262
|
}
|
|
2248
2263
|
|
|
@@ -1755,7 +1755,8 @@ function ac_mt_command_answer(gobj, event, kw, src)
|
|
|
1755
1755
|
treedb_name: kw_get_str(gobj, kw_command, "treedb_name", "", 0),
|
|
1756
1756
|
topic_name: kw_get_str(gobj, kw_command, "topic_name", "", 0),
|
|
1757
1757
|
record: kw_get_dict(gobj, kw_command, "record", {}, 0),
|
|
1758
|
-
created: (command === "create-node")
|
|
1758
|
+
created: (command === "create-node"),
|
|
1759
|
+
command: command
|
|
1759
1760
|
});
|
|
1760
1761
|
break;
|
|
1761
1762
|
|