@yuneta/gobj-ui 7.7.1 → 7.8.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/dist/gobj-ui.cjs.js +117 -1
- package/dist/gobj-ui.es.js +117 -1
- package/package.json +1 -1
- package/src/c_yui_treedb_topic_with_form.js +113 -0
- package/src/c_yui_treedb_topics.js +57 -0
package/dist/gobj-ui.cjs.js
CHANGED
|
@@ -15609,7 +15609,13 @@ function ac_mt_command_answer$2(gobj, event, kw, src) {
|
|
|
15609
15609
|
}
|
|
15610
15610
|
if (result < 0) {
|
|
15611
15611
|
if (command === "descs") show_load_error$1(gobj, (0, i18next.t)(comment));
|
|
15612
|
-
else
|
|
15612
|
+
else {
|
|
15613
|
+
yui_shell_show_error(yui_shell_of(gobj), comment, { t: i18next.t });
|
|
15614
|
+
if (command === "update-node" || command === "create-node") {
|
|
15615
|
+
let failed_topic = (0, _yuneta_gobj_js.kw_get_str)(gobj, kw_command, "topic_name", "", 0);
|
|
15616
|
+
if (failed_topic) treedb_nodes$1(gobj, (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "treedb_name"), failed_topic, { list_dict: true });
|
|
15617
|
+
}
|
|
15618
|
+
}
|
|
15613
15619
|
return 0;
|
|
15614
15620
|
}
|
|
15615
15621
|
switch (command) {
|
|
@@ -15849,6 +15855,35 @@ function ac_create_record(gobj, event, kw, src) {
|
|
|
15849
15855
|
});
|
|
15850
15856
|
}
|
|
15851
15857
|
/********************************************
|
|
15858
|
+
* Event from formtable: ONE field of one record, edited in place.
|
|
15859
|
+
*
|
|
15860
|
+
* Written as a PARTIAL update and, deliberately, with NO `autolink`.
|
|
15861
|
+
* `treedb_update_node()` merges (`json_object_update`), so the fields
|
|
15862
|
+
* it does not carry are left alone; and `autolink` is what wipes a
|
|
15863
|
+
* node's links to rebuild them from the fkeys the record carries — on
|
|
15864
|
+
* a partial record that reads as "no parents" and DETACHES the node,
|
|
15865
|
+
* answering success. A cell edit changes a scalar and must not be able
|
|
15866
|
+
* to touch a link at all.
|
|
15867
|
+
*
|
|
15868
|
+
* This is why it is its own event and not EV_UPDATE_RECORD: that one
|
|
15869
|
+
* DOES send autolink, and may, because the form hands it the whole
|
|
15870
|
+
* record with its fkeys in it.
|
|
15871
|
+
********************************************/
|
|
15872
|
+
function ac_update_field(gobj, event, kw, src) {
|
|
15873
|
+
if (refuse_if_readonly$3(gobj, event)) return -1;
|
|
15874
|
+
let topic_name = kw.topic_name || (0, _yuneta_gobj_js.gobj_read_attr)(src, "topic_name");
|
|
15875
|
+
let desc = ((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "descs") || {})[topic_name];
|
|
15876
|
+
let pkey = desc && desc.pkey || "id";
|
|
15877
|
+
if (kw.id === void 0 || kw.id === null || !kw.field) {
|
|
15878
|
+
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: a field write with no id or no field`);
|
|
15879
|
+
return -1;
|
|
15880
|
+
}
|
|
15881
|
+
let record = {};
|
|
15882
|
+
record[pkey] = kw.id;
|
|
15883
|
+
record[kw.field] = kw.value;
|
|
15884
|
+
return treedb_update_node$1(gobj, (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "treedb_name"), topic_name, record, { list_dict: true });
|
|
15885
|
+
}
|
|
15886
|
+
/********************************************
|
|
15852
15887
|
* Event from formtable
|
|
15853
15888
|
* kw: {
|
|
15854
15889
|
* topic_name,
|
|
@@ -15970,6 +16005,11 @@ function create_gclass$6(gclass_name) {
|
|
|
15970
16005
|
ac_update_record,
|
|
15971
16006
|
null
|
|
15972
16007
|
],
|
|
16008
|
+
[
|
|
16009
|
+
"EV_UPDATE_FIELD",
|
|
16010
|
+
ac_update_field,
|
|
16011
|
+
null
|
|
16012
|
+
],
|
|
15973
16013
|
[
|
|
15974
16014
|
"EV_DELETE_RECORD",
|
|
15975
16015
|
ac_delete_record,
|
|
@@ -16043,6 +16083,7 @@ function create_gclass$6(gclass_name) {
|
|
|
16043
16083
|
["EV_TREEDB_NODE_DELETED", _yuneta_gobj_js.event_flag_t.EVF_PUBLIC_EVENT],
|
|
16044
16084
|
["EV_CREATE_RECORD", 0],
|
|
16045
16085
|
["EV_UPDATE_RECORD", 0],
|
|
16086
|
+
["EV_UPDATE_FIELD", 0],
|
|
16046
16087
|
["EV_DELETE_RECORD", 0],
|
|
16047
16088
|
["EV_RECORD_WRITTEN", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT | _yuneta_gobj_js.event_flag_t.EVF_NO_WARN_SUBS],
|
|
16048
16089
|
["EV_REFRESH_TOPIC", 0],
|
|
@@ -16256,6 +16297,7 @@ var attrs_table$5 = [
|
|
|
16256
16297
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_columns_button", 0, true, "Button toolbar COLUMNS (choose which columns the table shows)"),
|
|
16257
16298
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_export_button", 0, true, "Button toolbar EXPORT (download as CSV what the table holds)"),
|
|
16258
16299
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_header_filters", 0, true, "Per-column filter box in the table header, on the columns a text/number match means something (not hooks, fkeys or json)"),
|
|
16300
|
+
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_inline_edit", 0, true, "Edit a writable scalar cell in place while in edition mode (the record form keeps the rest)"),
|
|
16259
16301
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_in_row_edit_icons", 0, true, "Add a last column with internal EDIT/DELETE icon"),
|
|
16260
16302
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "editable", 0, false, "Edit state"),
|
|
16261
16303
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_checkbox", 0, true, "Auxiliary first column to select rows"),
|
|
@@ -16765,6 +16807,7 @@ function create_tabulator(gobj) {
|
|
|
16765
16807
|
let table_id = (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "table_id");
|
|
16766
16808
|
let desc = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "desc");
|
|
16767
16809
|
let with_header_filters = (0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "with_header_filters");
|
|
16810
|
+
let with_inline_edit = (0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "with_inline_edit");
|
|
16768
16811
|
let columns = [];
|
|
16769
16812
|
let with_checkbox = (0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "with_checkbox");
|
|
16770
16813
|
let with_radio = (0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "with_radio");
|
|
@@ -16808,6 +16851,42 @@ function create_tabulator(gobj) {
|
|
|
16808
16851
|
col_id: cell.getField()
|
|
16809
16852
|
}, gobj);
|
|
16810
16853
|
}
|
|
16854
|
+
const INLINE_EDITABLE = [
|
|
16855
|
+
"string",
|
|
16856
|
+
"integer",
|
|
16857
|
+
"real",
|
|
16858
|
+
"email",
|
|
16859
|
+
"url",
|
|
16860
|
+
"tel",
|
|
16861
|
+
"id",
|
|
16862
|
+
"hex",
|
|
16863
|
+
"currency",
|
|
16864
|
+
"percent",
|
|
16865
|
+
"uuid"
|
|
16866
|
+
];
|
|
16867
|
+
function inline_editor_for(colDef, col, field_desc) {
|
|
16868
|
+
if (!field_desc.is_writable) return;
|
|
16869
|
+
if (col.id === (desc.pkey || "id")) return;
|
|
16870
|
+
switch (field_desc.type) {
|
|
16871
|
+
case "boolean":
|
|
16872
|
+
colDef.editor = "tickCross";
|
|
16873
|
+
break;
|
|
16874
|
+
case "enum":
|
|
16875
|
+
colDef.editor = "list";
|
|
16876
|
+
colDef.editorParams = { values: field_desc.enum_list || [] };
|
|
16877
|
+
break;
|
|
16878
|
+
case "integer":
|
|
16879
|
+
case "real":
|
|
16880
|
+
colDef.editor = "number";
|
|
16881
|
+
break;
|
|
16882
|
+
default:
|
|
16883
|
+
if (!INLINE_EDITABLE.includes(field_desc.type)) return;
|
|
16884
|
+
colDef.editor = "input";
|
|
16885
|
+
}
|
|
16886
|
+
colDef.editable = function() {
|
|
16887
|
+
return (0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "editable") && !(0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "readonly");
|
|
16888
|
+
};
|
|
16889
|
+
}
|
|
16811
16890
|
const FILTERABLE = [
|
|
16812
16891
|
"string",
|
|
16813
16892
|
"integer",
|
|
@@ -16930,6 +17009,7 @@ function create_tabulator(gobj) {
|
|
|
16930
17009
|
formatter: colFormatter
|
|
16931
17010
|
};
|
|
16932
17011
|
if (with_header_filters) apply_header_filter(colDef, field_desc);
|
|
17012
|
+
if (with_inline_edit) inline_editor_for(colDef, col, field_desc);
|
|
16933
17013
|
if (vertAlign) colDef.vertAlign = vertAlign;
|
|
16934
17014
|
if (formatterParams) colDef.formatterParams = formatterParams;
|
|
16935
17015
|
if (cellClick) colDef.cellClick = cellClick;
|
|
@@ -17016,6 +17096,21 @@ function create_tabulator(gobj) {
|
|
|
17016
17096
|
});
|
|
17017
17097
|
tabulator.on("dataProcessed", update_rowcount);
|
|
17018
17098
|
tabulator.on("dataChanged", update_rowcount);
|
|
17099
|
+
tabulator.on("cellEdited", function(cell) {
|
|
17100
|
+
let pkey = ((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "desc") || {}).pkey || "id";
|
|
17101
|
+
let data = cell.getRow().getData();
|
|
17102
|
+
let id = data ? data[pkey] : void 0;
|
|
17103
|
+
if (id === void 0 || id === null) {
|
|
17104
|
+
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: edited a row with no '${pkey}'`);
|
|
17105
|
+
cell.restoreOldValue();
|
|
17106
|
+
return;
|
|
17107
|
+
}
|
|
17108
|
+
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_CELL_EDITED", {
|
|
17109
|
+
id,
|
|
17110
|
+
field: cell.getField(),
|
|
17111
|
+
value: cell.getValue()
|
|
17112
|
+
}, gobj);
|
|
17113
|
+
});
|
|
17019
17114
|
tabulator.on("dataFiltered", function(filters, rows) {
|
|
17020
17115
|
update_rowcount(Array.isArray(rows) ? rows.length : void 0);
|
|
17021
17116
|
});
|
|
@@ -18136,6 +18231,20 @@ function ac_show_cell_json(gobj, event, kw, src) {
|
|
|
18136
18231
|
return 0;
|
|
18137
18232
|
}
|
|
18138
18233
|
/************************************************************
|
|
18234
|
+
* One cell of one record changed. Publish it UP as a field write;
|
|
18235
|
+
* the host owns the treedb command and its options.
|
|
18236
|
+
************************************************************/
|
|
18237
|
+
function ac_cell_edited(gobj, event, kw, src) {
|
|
18238
|
+
if (refuse_if_readonly$2(gobj, event)) return -1;
|
|
18239
|
+
(0, _yuneta_gobj_js.gobj_publish_event)(gobj, "EV_UPDATE_FIELD", {
|
|
18240
|
+
topic_name: (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "topic_name"),
|
|
18241
|
+
id: kw.id,
|
|
18242
|
+
field: kw.field,
|
|
18243
|
+
value: kw.value
|
|
18244
|
+
});
|
|
18245
|
+
return 0;
|
|
18246
|
+
}
|
|
18247
|
+
/************************************************************
|
|
18139
18248
|
* Show the schema (desc) of this topic
|
|
18140
18249
|
************************************************************/
|
|
18141
18250
|
function ac_show_schema(gobj, event, kw, src) {
|
|
@@ -18385,6 +18494,11 @@ function create_gclass$5(gclass_name) {
|
|
|
18385
18494
|
ac_show_schema,
|
|
18386
18495
|
null
|
|
18387
18496
|
],
|
|
18497
|
+
[
|
|
18498
|
+
"EV_CELL_EDITED",
|
|
18499
|
+
ac_cell_edited,
|
|
18500
|
+
null
|
|
18501
|
+
],
|
|
18388
18502
|
[
|
|
18389
18503
|
"EV_SEARCH",
|
|
18390
18504
|
ac_search,
|
|
@@ -18435,6 +18549,8 @@ function create_gclass$5(gclass_name) {
|
|
|
18435
18549
|
["EV_CHANGE_LOCALE", 0],
|
|
18436
18550
|
["EV_REFRESH", 0],
|
|
18437
18551
|
["EV_SHOW_SCHEMA", 0],
|
|
18552
|
+
["EV_CELL_EDITED", 0],
|
|
18553
|
+
["EV_UPDATE_FIELD", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT],
|
|
18438
18554
|
["EV_SEARCH", 0],
|
|
18439
18555
|
["EV_OPEN_COLUMNS", 0],
|
|
18440
18556
|
["EV_TOGGLE_COLUMN", 0],
|
package/dist/gobj-ui.es.js
CHANGED
|
@@ -15604,7 +15604,13 @@ function ac_mt_command_answer$2(gobj, event, kw, src) {
|
|
|
15604
15604
|
}
|
|
15605
15605
|
if (result < 0) {
|
|
15606
15606
|
if (command === "descs") show_load_error$1(gobj, t(comment));
|
|
15607
|
-
else
|
|
15607
|
+
else {
|
|
15608
|
+
yui_shell_show_error(yui_shell_of(gobj), comment, { t });
|
|
15609
|
+
if (command === "update-node" || command === "create-node") {
|
|
15610
|
+
let failed_topic = kw_get_str(gobj, kw_command, "topic_name", "", 0);
|
|
15611
|
+
if (failed_topic) treedb_nodes$1(gobj, gobj_read_str_attr(gobj, "treedb_name"), failed_topic, { list_dict: true });
|
|
15612
|
+
}
|
|
15613
|
+
}
|
|
15608
15614
|
return 0;
|
|
15609
15615
|
}
|
|
15610
15616
|
switch (command) {
|
|
@@ -15844,6 +15850,35 @@ function ac_create_record(gobj, event, kw, src) {
|
|
|
15844
15850
|
});
|
|
15845
15851
|
}
|
|
15846
15852
|
/********************************************
|
|
15853
|
+
* Event from formtable: ONE field of one record, edited in place.
|
|
15854
|
+
*
|
|
15855
|
+
* Written as a PARTIAL update and, deliberately, with NO `autolink`.
|
|
15856
|
+
* `treedb_update_node()` merges (`json_object_update`), so the fields
|
|
15857
|
+
* it does not carry are left alone; and `autolink` is what wipes a
|
|
15858
|
+
* node's links to rebuild them from the fkeys the record carries — on
|
|
15859
|
+
* a partial record that reads as "no parents" and DETACHES the node,
|
|
15860
|
+
* answering success. A cell edit changes a scalar and must not be able
|
|
15861
|
+
* to touch a link at all.
|
|
15862
|
+
*
|
|
15863
|
+
* This is why it is its own event and not EV_UPDATE_RECORD: that one
|
|
15864
|
+
* DOES send autolink, and may, because the form hands it the whole
|
|
15865
|
+
* record with its fkeys in it.
|
|
15866
|
+
********************************************/
|
|
15867
|
+
function ac_update_field(gobj, event, kw, src) {
|
|
15868
|
+
if (refuse_if_readonly$3(gobj, event)) return -1;
|
|
15869
|
+
let topic_name = kw.topic_name || gobj_read_attr(src, "topic_name");
|
|
15870
|
+
let desc = (gobj_read_attr(gobj, "descs") || {})[topic_name];
|
|
15871
|
+
let pkey = desc && desc.pkey || "id";
|
|
15872
|
+
if (kw.id === void 0 || kw.id === null || !kw.field) {
|
|
15873
|
+
log_error(`${gobj_short_name(gobj)}: a field write with no id or no field`);
|
|
15874
|
+
return -1;
|
|
15875
|
+
}
|
|
15876
|
+
let record = {};
|
|
15877
|
+
record[pkey] = kw.id;
|
|
15878
|
+
record[kw.field] = kw.value;
|
|
15879
|
+
return treedb_update_node$1(gobj, gobj_read_str_attr(gobj, "treedb_name"), topic_name, record, { list_dict: true });
|
|
15880
|
+
}
|
|
15881
|
+
/********************************************
|
|
15847
15882
|
* Event from formtable
|
|
15848
15883
|
* kw: {
|
|
15849
15884
|
* topic_name,
|
|
@@ -15965,6 +16000,11 @@ function create_gclass$6(gclass_name) {
|
|
|
15965
16000
|
ac_update_record,
|
|
15966
16001
|
null
|
|
15967
16002
|
],
|
|
16003
|
+
[
|
|
16004
|
+
"EV_UPDATE_FIELD",
|
|
16005
|
+
ac_update_field,
|
|
16006
|
+
null
|
|
16007
|
+
],
|
|
15968
16008
|
[
|
|
15969
16009
|
"EV_DELETE_RECORD",
|
|
15970
16010
|
ac_delete_record,
|
|
@@ -16038,6 +16078,7 @@ function create_gclass$6(gclass_name) {
|
|
|
16038
16078
|
["EV_TREEDB_NODE_DELETED", event_flag_t.EVF_PUBLIC_EVENT],
|
|
16039
16079
|
["EV_CREATE_RECORD", 0],
|
|
16040
16080
|
["EV_UPDATE_RECORD", 0],
|
|
16081
|
+
["EV_UPDATE_FIELD", 0],
|
|
16041
16082
|
["EV_DELETE_RECORD", 0],
|
|
16042
16083
|
["EV_RECORD_WRITTEN", event_flag_t.EVF_OUTPUT_EVENT | event_flag_t.EVF_NO_WARN_SUBS],
|
|
16043
16084
|
["EV_REFRESH_TOPIC", 0],
|
|
@@ -16251,6 +16292,7 @@ var attrs_table$5 = [
|
|
|
16251
16292
|
SDATA(data_type_t.DTP_BOOLEAN, "with_columns_button", 0, true, "Button toolbar COLUMNS (choose which columns the table shows)"),
|
|
16252
16293
|
SDATA(data_type_t.DTP_BOOLEAN, "with_export_button", 0, true, "Button toolbar EXPORT (download as CSV what the table holds)"),
|
|
16253
16294
|
SDATA(data_type_t.DTP_BOOLEAN, "with_header_filters", 0, true, "Per-column filter box in the table header, on the columns a text/number match means something (not hooks, fkeys or json)"),
|
|
16295
|
+
SDATA(data_type_t.DTP_BOOLEAN, "with_inline_edit", 0, true, "Edit a writable scalar cell in place while in edition mode (the record form keeps the rest)"),
|
|
16254
16296
|
SDATA(data_type_t.DTP_BOOLEAN, "with_in_row_edit_icons", 0, true, "Add a last column with internal EDIT/DELETE icon"),
|
|
16255
16297
|
SDATA(data_type_t.DTP_BOOLEAN, "editable", 0, false, "Edit state"),
|
|
16256
16298
|
SDATA(data_type_t.DTP_BOOLEAN, "with_checkbox", 0, true, "Auxiliary first column to select rows"),
|
|
@@ -16760,6 +16802,7 @@ function create_tabulator(gobj) {
|
|
|
16760
16802
|
let table_id = gobj_read_str_attr(gobj, "table_id");
|
|
16761
16803
|
let desc = gobj_read_attr(gobj, "desc");
|
|
16762
16804
|
let with_header_filters = gobj_read_bool_attr(gobj, "with_header_filters");
|
|
16805
|
+
let with_inline_edit = gobj_read_bool_attr(gobj, "with_inline_edit");
|
|
16763
16806
|
let columns = [];
|
|
16764
16807
|
let with_checkbox = gobj_read_bool_attr(gobj, "with_checkbox");
|
|
16765
16808
|
let with_radio = gobj_read_bool_attr(gobj, "with_radio");
|
|
@@ -16803,6 +16846,42 @@ function create_tabulator(gobj) {
|
|
|
16803
16846
|
col_id: cell.getField()
|
|
16804
16847
|
}, gobj);
|
|
16805
16848
|
}
|
|
16849
|
+
const INLINE_EDITABLE = [
|
|
16850
|
+
"string",
|
|
16851
|
+
"integer",
|
|
16852
|
+
"real",
|
|
16853
|
+
"email",
|
|
16854
|
+
"url",
|
|
16855
|
+
"tel",
|
|
16856
|
+
"id",
|
|
16857
|
+
"hex",
|
|
16858
|
+
"currency",
|
|
16859
|
+
"percent",
|
|
16860
|
+
"uuid"
|
|
16861
|
+
];
|
|
16862
|
+
function inline_editor_for(colDef, col, field_desc) {
|
|
16863
|
+
if (!field_desc.is_writable) return;
|
|
16864
|
+
if (col.id === (desc.pkey || "id")) return;
|
|
16865
|
+
switch (field_desc.type) {
|
|
16866
|
+
case "boolean":
|
|
16867
|
+
colDef.editor = "tickCross";
|
|
16868
|
+
break;
|
|
16869
|
+
case "enum":
|
|
16870
|
+
colDef.editor = "list";
|
|
16871
|
+
colDef.editorParams = { values: field_desc.enum_list || [] };
|
|
16872
|
+
break;
|
|
16873
|
+
case "integer":
|
|
16874
|
+
case "real":
|
|
16875
|
+
colDef.editor = "number";
|
|
16876
|
+
break;
|
|
16877
|
+
default:
|
|
16878
|
+
if (!INLINE_EDITABLE.includes(field_desc.type)) return;
|
|
16879
|
+
colDef.editor = "input";
|
|
16880
|
+
}
|
|
16881
|
+
colDef.editable = function() {
|
|
16882
|
+
return gobj_read_bool_attr(gobj, "editable") && !gobj_read_bool_attr(gobj, "readonly");
|
|
16883
|
+
};
|
|
16884
|
+
}
|
|
16806
16885
|
const FILTERABLE = [
|
|
16807
16886
|
"string",
|
|
16808
16887
|
"integer",
|
|
@@ -16925,6 +17004,7 @@ function create_tabulator(gobj) {
|
|
|
16925
17004
|
formatter: colFormatter
|
|
16926
17005
|
};
|
|
16927
17006
|
if (with_header_filters) apply_header_filter(colDef, field_desc);
|
|
17007
|
+
if (with_inline_edit) inline_editor_for(colDef, col, field_desc);
|
|
16928
17008
|
if (vertAlign) colDef.vertAlign = vertAlign;
|
|
16929
17009
|
if (formatterParams) colDef.formatterParams = formatterParams;
|
|
16930
17010
|
if (cellClick) colDef.cellClick = cellClick;
|
|
@@ -17011,6 +17091,21 @@ function create_tabulator(gobj) {
|
|
|
17011
17091
|
});
|
|
17012
17092
|
tabulator.on("dataProcessed", update_rowcount);
|
|
17013
17093
|
tabulator.on("dataChanged", update_rowcount);
|
|
17094
|
+
tabulator.on("cellEdited", function(cell) {
|
|
17095
|
+
let pkey = (gobj_read_attr(gobj, "desc") || {}).pkey || "id";
|
|
17096
|
+
let data = cell.getRow().getData();
|
|
17097
|
+
let id = data ? data[pkey] : void 0;
|
|
17098
|
+
if (id === void 0 || id === null) {
|
|
17099
|
+
log_error(`${gobj_short_name(gobj)}: edited a row with no '${pkey}'`);
|
|
17100
|
+
cell.restoreOldValue();
|
|
17101
|
+
return;
|
|
17102
|
+
}
|
|
17103
|
+
gobj_send_event(gobj, "EV_CELL_EDITED", {
|
|
17104
|
+
id,
|
|
17105
|
+
field: cell.getField(),
|
|
17106
|
+
value: cell.getValue()
|
|
17107
|
+
}, gobj);
|
|
17108
|
+
});
|
|
17014
17109
|
tabulator.on("dataFiltered", function(filters, rows) {
|
|
17015
17110
|
update_rowcount(Array.isArray(rows) ? rows.length : void 0);
|
|
17016
17111
|
});
|
|
@@ -18131,6 +18226,20 @@ function ac_show_cell_json(gobj, event, kw, src) {
|
|
|
18131
18226
|
return 0;
|
|
18132
18227
|
}
|
|
18133
18228
|
/************************************************************
|
|
18229
|
+
* One cell of one record changed. Publish it UP as a field write;
|
|
18230
|
+
* the host owns the treedb command and its options.
|
|
18231
|
+
************************************************************/
|
|
18232
|
+
function ac_cell_edited(gobj, event, kw, src) {
|
|
18233
|
+
if (refuse_if_readonly$2(gobj, event)) return -1;
|
|
18234
|
+
gobj_publish_event(gobj, "EV_UPDATE_FIELD", {
|
|
18235
|
+
topic_name: gobj_read_str_attr(gobj, "topic_name"),
|
|
18236
|
+
id: kw.id,
|
|
18237
|
+
field: kw.field,
|
|
18238
|
+
value: kw.value
|
|
18239
|
+
});
|
|
18240
|
+
return 0;
|
|
18241
|
+
}
|
|
18242
|
+
/************************************************************
|
|
18134
18243
|
* Show the schema (desc) of this topic
|
|
18135
18244
|
************************************************************/
|
|
18136
18245
|
function ac_show_schema(gobj, event, kw, src) {
|
|
@@ -18380,6 +18489,11 @@ function create_gclass$5(gclass_name) {
|
|
|
18380
18489
|
ac_show_schema,
|
|
18381
18490
|
null
|
|
18382
18491
|
],
|
|
18492
|
+
[
|
|
18493
|
+
"EV_CELL_EDITED",
|
|
18494
|
+
ac_cell_edited,
|
|
18495
|
+
null
|
|
18496
|
+
],
|
|
18383
18497
|
[
|
|
18384
18498
|
"EV_SEARCH",
|
|
18385
18499
|
ac_search,
|
|
@@ -18430,6 +18544,8 @@ function create_gclass$5(gclass_name) {
|
|
|
18430
18544
|
["EV_CHANGE_LOCALE", 0],
|
|
18431
18545
|
["EV_REFRESH", 0],
|
|
18432
18546
|
["EV_SHOW_SCHEMA", 0],
|
|
18547
|
+
["EV_CELL_EDITED", 0],
|
|
18548
|
+
["EV_UPDATE_FIELD", event_flag_t.EVF_OUTPUT_EVENT],
|
|
18433
18549
|
["EV_SEARCH", 0],
|
|
18434
18550
|
["EV_OPEN_COLUMNS", 0],
|
|
18435
18551
|
["EV_TOGGLE_COLUMN", 0],
|
package/package.json
CHANGED
|
@@ -111,6 +111,7 @@ SDATA(data_type_t.DTP_BOOLEAN, "with_schema_button", 0, true, "Button
|
|
|
111
111
|
SDATA(data_type_t.DTP_BOOLEAN, "with_columns_button", 0, true, "Button toolbar COLUMNS (choose which columns the table shows)"),
|
|
112
112
|
SDATA(data_type_t.DTP_BOOLEAN, "with_export_button", 0, true, "Button toolbar EXPORT (download as CSV what the table holds)"),
|
|
113
113
|
SDATA(data_type_t.DTP_BOOLEAN, "with_header_filters", 0, true, "Per-column filter box in the table header, on the columns a text/number match means something (not hooks, fkeys or json)"),
|
|
114
|
+
SDATA(data_type_t.DTP_BOOLEAN, "with_inline_edit", 0, true, "Edit a writable scalar cell in place while in edition mode (the record form keeps the rest)"),
|
|
114
115
|
SDATA(data_type_t.DTP_BOOLEAN, "with_in_row_edit_icons", 0, true, "Add a last column with internal EDIT/DELETE icon"),
|
|
115
116
|
|
|
116
117
|
SDATA(data_type_t.DTP_BOOLEAN, "editable", 0, false, "Edit state"),
|
|
@@ -730,6 +731,7 @@ function create_tabulator(gobj)
|
|
|
730
731
|
let table_id = gobj_read_str_attr(gobj, "table_id");
|
|
731
732
|
let desc = gobj_read_attr(gobj, "desc");
|
|
732
733
|
let with_header_filters = gobj_read_bool_attr(gobj, "with_header_filters");
|
|
734
|
+
let with_inline_edit = gobj_read_bool_attr(gobj, "with_inline_edit");
|
|
733
735
|
|
|
734
736
|
let columns = [];
|
|
735
737
|
|
|
@@ -819,6 +821,62 @@ function create_tabulator(gobj)
|
|
|
819
821
|
* value is stringified first because a fkey can arrive as a ref string,
|
|
820
822
|
* a list of them or a dict.
|
|
821
823
|
*/
|
|
824
|
+
/* A cell you can type into, and the ones you cannot.
|
|
825
|
+
*
|
|
826
|
+
* Editing in place is for a SCALAR the table can round-trip. A hook
|
|
827
|
+
* holds children and an fkey IS a link — both are edited by linking,
|
|
828
|
+
* not by typing — a dict or a list is a document (the form has an
|
|
829
|
+
* editor for it), and a date cell shows a formatted string over an
|
|
830
|
+
* epoch, so typing into it would write the string. Those stay with
|
|
831
|
+
* the form, which is one click away on the same row.
|
|
832
|
+
*
|
|
833
|
+
* The schema decides the rest: only a column flagged `writable` is
|
|
834
|
+
* offered, and never the pkey — renaming what a record is KEYED by
|
|
835
|
+
* is not a field edit.
|
|
836
|
+
*/
|
|
837
|
+
const INLINE_EDITABLE = [
|
|
838
|
+
"string", "integer", "real",
|
|
839
|
+
"email", "url", "tel", "id", "hex", "currency", "percent", "uuid"
|
|
840
|
+
];
|
|
841
|
+
|
|
842
|
+
function inline_editor_for(colDef, col, field_desc)
|
|
843
|
+
{
|
|
844
|
+
if(!field_desc.is_writable) {
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
847
|
+
if(col.id === (desc.pkey || "id")) {
|
|
848
|
+
return;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
switch(field_desc.type) {
|
|
852
|
+
case "boolean":
|
|
853
|
+
colDef.editor = "tickCross";
|
|
854
|
+
break;
|
|
855
|
+
case "enum":
|
|
856
|
+
colDef.editor = "list";
|
|
857
|
+
colDef.editorParams = {values: field_desc.enum_list || []};
|
|
858
|
+
break;
|
|
859
|
+
case "integer":
|
|
860
|
+
case "real":
|
|
861
|
+
colDef.editor = "number";
|
|
862
|
+
break;
|
|
863
|
+
default:
|
|
864
|
+
if(!INLINE_EDITABLE.includes(field_desc.type)) {
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
867
|
+
colDef.editor = "input";
|
|
868
|
+
break;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
/* A FUNCTION, not a flag: edition mode is toggled on a table that
|
|
872
|
+
* is already built (ac_edition_mode only shows and hides columns),
|
|
873
|
+
* so the answer has to be asked for at the moment of the click. */
|
|
874
|
+
colDef.editable = function() {
|
|
875
|
+
return gobj_read_bool_attr(gobj, "editable") &&
|
|
876
|
+
!gobj_read_bool_attr(gobj, "readonly");
|
|
877
|
+
};
|
|
878
|
+
}
|
|
879
|
+
|
|
822
880
|
const FILTERABLE = [
|
|
823
881
|
"string", "integer", "real", "rowid", "uuid", "qualified",
|
|
824
882
|
"email", "url", "tel", "id", "hex", "currency", "percent"
|
|
@@ -974,6 +1032,9 @@ function create_tabulator(gobj)
|
|
|
974
1032
|
if(with_header_filters) {
|
|
975
1033
|
apply_header_filter(colDef, field_desc);
|
|
976
1034
|
}
|
|
1035
|
+
if(with_inline_edit) {
|
|
1036
|
+
inline_editor_for(colDef, col, field_desc);
|
|
1037
|
+
}
|
|
977
1038
|
if(vertAlign) {
|
|
978
1039
|
colDef.vertAlign = vertAlign;
|
|
979
1040
|
}
|
|
@@ -1139,6 +1200,33 @@ function create_tabulator(gobj)
|
|
|
1139
1200
|
* box; a filter per column made it a claim you read on every keystroke. */
|
|
1140
1201
|
tabulator.on("dataProcessed", update_rowcount);
|
|
1141
1202
|
tabulator.on("dataChanged", update_rowcount);
|
|
1203
|
+
/* A cell edited in place. Announced as ONE FIELD of one record — not
|
|
1204
|
+
* as the row — and that is the whole safety of it: the host writes it
|
|
1205
|
+
* with a partial update and no `autolink`, so nothing but that field
|
|
1206
|
+
* moves. `autolink` wipes a node's links and rebuilds them from the
|
|
1207
|
+
* fkeys the record carries, so sending a row that the table shaped for
|
|
1208
|
+
* DISPLAY would unlink the node and answer success. */
|
|
1209
|
+
tabulator.on("cellEdited", function(cell) {
|
|
1210
|
+
let pkey = (gobj_read_attr(gobj, "desc") || {}).pkey || "id";
|
|
1211
|
+
let data = cell.getRow().getData();
|
|
1212
|
+
let id = data ? data[pkey] : undefined;
|
|
1213
|
+
if(id === undefined || id === null) {
|
|
1214
|
+
log_error(`${gobj_short_name(gobj)}: edited a row with no '${pkey}'`);
|
|
1215
|
+
cell.restoreOldValue();
|
|
1216
|
+
return;
|
|
1217
|
+
}
|
|
1218
|
+
gobj_send_event(
|
|
1219
|
+
gobj,
|
|
1220
|
+
"EV_CELL_EDITED",
|
|
1221
|
+
{
|
|
1222
|
+
id: id,
|
|
1223
|
+
field: cell.getField(),
|
|
1224
|
+
value: cell.getValue()
|
|
1225
|
+
},
|
|
1226
|
+
gobj
|
|
1227
|
+
);
|
|
1228
|
+
});
|
|
1229
|
+
|
|
1142
1230
|
tabulator.on("dataFiltered", function(filters, rows) {
|
|
1143
1231
|
update_rowcount(Array.isArray(rows)? rows.length : undefined);
|
|
1144
1232
|
});
|
|
@@ -2845,6 +2933,28 @@ function ac_show_cell_json(gobj, event, kw, src)
|
|
|
2845
2933
|
return 0;
|
|
2846
2934
|
}
|
|
2847
2935
|
|
|
2936
|
+
/************************************************************
|
|
2937
|
+
* One cell of one record changed. Publish it UP as a field write;
|
|
2938
|
+
* the host owns the treedb command and its options.
|
|
2939
|
+
************************************************************/
|
|
2940
|
+
function ac_cell_edited(gobj, event, kw, src)
|
|
2941
|
+
{
|
|
2942
|
+
if(refuse_if_readonly(gobj, event)) {
|
|
2943
|
+
return -1; /* Error already logged */
|
|
2944
|
+
}
|
|
2945
|
+
gobj_publish_event(
|
|
2946
|
+
gobj,
|
|
2947
|
+
"EV_UPDATE_FIELD",
|
|
2948
|
+
{
|
|
2949
|
+
topic_name: gobj_read_str_attr(gobj, "topic_name"),
|
|
2950
|
+
id: kw.id,
|
|
2951
|
+
field: kw.field,
|
|
2952
|
+
value: kw.value
|
|
2953
|
+
}
|
|
2954
|
+
);
|
|
2955
|
+
return 0;
|
|
2956
|
+
}
|
|
2957
|
+
|
|
2848
2958
|
/************************************************************
|
|
2849
2959
|
* Show the schema (desc) of this topic
|
|
2850
2960
|
************************************************************/
|
|
@@ -3076,6 +3186,7 @@ function create_gclass(gclass_name)
|
|
|
3076
3186
|
["EV_CHANGE_LOCALE", ac_change_locale, null],
|
|
3077
3187
|
["EV_REFRESH", ac_refresh, null],
|
|
3078
3188
|
["EV_SHOW_SCHEMA", ac_show_schema, null],
|
|
3189
|
+
["EV_CELL_EDITED", ac_cell_edited, null],
|
|
3079
3190
|
["EV_SEARCH", ac_search, null],
|
|
3080
3191
|
["EV_OPEN_COLUMNS", ac_open_columns, null],
|
|
3081
3192
|
["EV_TOGGLE_COLUMN", ac_toggle_column, null],
|
|
@@ -3108,6 +3219,8 @@ function create_gclass(gclass_name)
|
|
|
3108
3219
|
["EV_CHANGE_LOCALE", 0],
|
|
3109
3220
|
["EV_REFRESH", 0],
|
|
3110
3221
|
["EV_SHOW_SCHEMA", 0],
|
|
3222
|
+
["EV_CELL_EDITED", 0],
|
|
3223
|
+
["EV_UPDATE_FIELD", event_flag_t.EVF_OUTPUT_EVENT],
|
|
3111
3224
|
["EV_SEARCH", 0],
|
|
3112
3225
|
["EV_OPEN_COLUMNS", 0],
|
|
3113
3226
|
["EV_TOGGLE_COLUMN", 0],
|
|
@@ -1740,6 +1740,18 @@ function ac_mt_command_answer(gobj, event, kw, src)
|
|
|
1740
1740
|
show_load_error(gobj, t(comment));
|
|
1741
1741
|
} else {
|
|
1742
1742
|
yui_shell_show_error(yui_shell_of(gobj), comment, {t: t});
|
|
1743
|
+
/* A refused write leaves the TABLE showing what the operator
|
|
1744
|
+
* typed and the store holding what it had. That is tolerable
|
|
1745
|
+
* for a form (it stays open on the values it failed with) and
|
|
1746
|
+
* is not for a cell edited in place, which looks saved. Put the
|
|
1747
|
+
* topic back to what the treedb actually has. */
|
|
1748
|
+
if(command === "update-node" || command === "create-node") {
|
|
1749
|
+
let failed_topic = kw_get_str(gobj, kw_command, "topic_name", "", 0);
|
|
1750
|
+
if(failed_topic) {
|
|
1751
|
+
treedb_nodes(gobj, gobj_read_str_attr(gobj, "treedb_name"),
|
|
1752
|
+
failed_topic, {list_dict: true});
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1743
1755
|
}
|
|
1744
1756
|
return 0;
|
|
1745
1757
|
}
|
|
@@ -2126,6 +2138,49 @@ function ac_create_record(gobj, event, kw, src)
|
|
|
2126
2138
|
);
|
|
2127
2139
|
}
|
|
2128
2140
|
|
|
2141
|
+
/********************************************
|
|
2142
|
+
* Event from formtable: ONE field of one record, edited in place.
|
|
2143
|
+
*
|
|
2144
|
+
* Written as a PARTIAL update and, deliberately, with NO `autolink`.
|
|
2145
|
+
* `treedb_update_node()` merges (`json_object_update`), so the fields
|
|
2146
|
+
* it does not carry are left alone; and `autolink` is what wipes a
|
|
2147
|
+
* node's links to rebuild them from the fkeys the record carries — on
|
|
2148
|
+
* a partial record that reads as "no parents" and DETACHES the node,
|
|
2149
|
+
* answering success. A cell edit changes a scalar and must not be able
|
|
2150
|
+
* to touch a link at all.
|
|
2151
|
+
*
|
|
2152
|
+
* This is why it is its own event and not EV_UPDATE_RECORD: that one
|
|
2153
|
+
* DOES send autolink, and may, because the form hands it the whole
|
|
2154
|
+
* record with its fkeys in it.
|
|
2155
|
+
********************************************/
|
|
2156
|
+
function ac_update_field(gobj, event, kw, src)
|
|
2157
|
+
{
|
|
2158
|
+
if(refuse_if_readonly(gobj, event)) {
|
|
2159
|
+
return -1; /* Error already logged */
|
|
2160
|
+
}
|
|
2161
|
+
let topic_name = kw.topic_name || gobj_read_attr(src, "topic_name");
|
|
2162
|
+
let descs = gobj_read_attr(gobj, "descs") || {};
|
|
2163
|
+
let desc = descs[topic_name];
|
|
2164
|
+
let pkey = (desc && desc.pkey) || "id";
|
|
2165
|
+
|
|
2166
|
+
if(kw.id === undefined || kw.id === null || !kw.field) {
|
|
2167
|
+
log_error(`${gobj_short_name(gobj)}: a field write with no id or no field`);
|
|
2168
|
+
return -1;
|
|
2169
|
+
}
|
|
2170
|
+
|
|
2171
|
+
let record = {};
|
|
2172
|
+
record[pkey] = kw.id;
|
|
2173
|
+
record[kw.field] = kw.value;
|
|
2174
|
+
|
|
2175
|
+
return treedb_update_node(
|
|
2176
|
+
gobj,
|
|
2177
|
+
gobj_read_str_attr(gobj, "treedb_name"),
|
|
2178
|
+
topic_name,
|
|
2179
|
+
record,
|
|
2180
|
+
{list_dict: true}
|
|
2181
|
+
);
|
|
2182
|
+
}
|
|
2183
|
+
|
|
2129
2184
|
/********************************************
|
|
2130
2185
|
* Event from formtable
|
|
2131
2186
|
* kw: {
|
|
@@ -2292,6 +2347,7 @@ function create_gclass(gclass_name)
|
|
|
2292
2347
|
["EV_TREEDB_NODE_DELETED", ac_treedb_node_deleted, null],
|
|
2293
2348
|
["EV_CREATE_RECORD", ac_create_record, null],
|
|
2294
2349
|
["EV_UPDATE_RECORD", ac_update_record, null],
|
|
2350
|
+
["EV_UPDATE_FIELD", ac_update_field, null],
|
|
2295
2351
|
["EV_DELETE_RECORD", ac_delete_record, null],
|
|
2296
2352
|
["EV_REFRESH_TOPIC", ac_refresh_topic, null],
|
|
2297
2353
|
["EV_OPEN_JSON", ac_open_json, null],
|
|
@@ -2318,6 +2374,7 @@ function create_gclass(gclass_name)
|
|
|
2318
2374
|
["EV_TREEDB_NODE_DELETED", event_flag_t.EVF_PUBLIC_EVENT],
|
|
2319
2375
|
["EV_CREATE_RECORD", 0],
|
|
2320
2376
|
["EV_UPDATE_RECORD", 0],
|
|
2377
|
+
["EV_UPDATE_FIELD", 0],
|
|
2321
2378
|
["EV_DELETE_RECORD", 0],
|
|
2322
2379
|
["EV_RECORD_WRITTEN", event_flag_t.EVF_OUTPUT_EVENT|
|
|
2323
2380
|
event_flag_t.EVF_NO_WARN_SUBS],
|