@yuneta/gobj-ui 7.8.0 → 7.9.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 +33 -0
- package/dist/gobj-ui.cjs.js +264 -5
- package/dist/gobj-ui.es.js +264 -5
- package/package.json +1 -1
- package/src/c_yui_treedb_topic_with_form.js +163 -0
- package/src/c_yui_treedb_topics.js +108 -11
- package/src/nodes_answer.js +72 -0
- package/src/nodes_answer.test.js +50 -0
package/README.md
CHANGED
|
@@ -491,6 +491,39 @@ The buttons of that toolbar never shrink. When the row runs out of room, the
|
|
|
491
491
|
url is what gives way, cut with an ellipsis, and the whole value stays in the
|
|
492
492
|
`title` and the `aria-label`.
|
|
493
493
|
|
|
494
|
+
### Editing a topic table in place
|
|
495
|
+
|
|
496
|
+
A writable scalar is editable in the table, in edition mode
|
|
497
|
+
(`with_inline_edit`, default on). Changing one field used to mean opening the
|
|
498
|
+
record form, changing it, saving and closing.
|
|
499
|
+
|
|
500
|
+
**Which cells, and why not the rest.** The schema decides first: only a column
|
|
501
|
+
flagged `writable`, and never the pkey — renaming what a record is KEYED by is
|
|
502
|
+
not a field edit. Then the type: a hook holds children and an fkey IS a link,
|
|
503
|
+
so both are edited by linking; a dict or a list is a document the form has an
|
|
504
|
+
editor for; a date cell shows a formatted string over an epoch, so typing into
|
|
505
|
+
it would write the string. Those stay with the form, one click away on the
|
|
506
|
+
same row. `boolean` gets a tick, `enum` the list of its own values, numbers a
|
|
507
|
+
number editor.
|
|
508
|
+
|
|
509
|
+
**The write is a partial update with no `autolink`, and that is the whole
|
|
510
|
+
safety of it.** `treedb_update_node()` merges (`json_object_update`), so the
|
|
511
|
+
fields it does not carry are left alone; `autolink` is the option that wipes a
|
|
512
|
+
node's links and rebuilds them from the fkeys the record carries, and on a
|
|
513
|
+
partial record it reads that as "no parents", detaches the node and answers
|
|
514
|
+
**success**. So a cell edit travels as its own event, `EV_UPDATE_FIELD`, and
|
|
515
|
+
not as `EV_UPDATE_RECORD` — that one does send autolink, and may, because the
|
|
516
|
+
form hands it the whole record with its fkeys in it. See
|
|
517
|
+
`schema_write_options.js` for the rule and why each word of it is there.
|
|
518
|
+
|
|
519
|
+
`editable` is a **function** on the column, not a flag: edition mode is
|
|
520
|
+
toggled on a table that is already built, so the answer has to be asked for at
|
|
521
|
+
the moment of the click.
|
|
522
|
+
|
|
523
|
+
A refused write puts the topic back to what the treedb has. Leaving the typed
|
|
524
|
+
value on screen is tolerable for a form, which stays open on the values it
|
|
525
|
+
failed with; a cell edited in place would just look saved.
|
|
526
|
+
|
|
494
527
|
### Reading a topic table: filters, columns, CSV
|
|
495
528
|
|
|
496
529
|
`C_YUI_TREEDB_TOPIC_WITH_FORM` had one global search box over the loaded rows.
|
package/dist/gobj-ui.cjs.js
CHANGED
|
@@ -14416,6 +14416,66 @@ function yui_shell_show_route_map(shell, opts) {
|
|
|
14416
14416
|
return modal_ref.modal;
|
|
14417
14417
|
}
|
|
14418
14418
|
//#endregion
|
|
14419
|
+
//#region src/nodes_answer.js
|
|
14420
|
+
/***********************************************************************
|
|
14421
|
+
* nodes_answer.js
|
|
14422
|
+
*
|
|
14423
|
+
* What the `nodes` command answered, whatever shape it came in.
|
|
14424
|
+
*
|
|
14425
|
+
* A treedb lives in memory on the backend, so walking it is not
|
|
14426
|
+
* what costs: serializing every node, pushing it through a
|
|
14427
|
+
* websocket and parsing it here is. So `nodes` learned to answer a
|
|
14428
|
+
* PAGE, with the contract `list-keys` of C_TRANGER already used:
|
|
14429
|
+
*
|
|
14430
|
+
* no `limit` -> the plain list it has always answered
|
|
14431
|
+
* a `limit` -> {total_rows, pages, data}
|
|
14432
|
+
*
|
|
14433
|
+
* Both shapes are alive at once and will be for a long time: the
|
|
14434
|
+
* SPA talks to backends the operator configures, and an older one
|
|
14435
|
+
* answers the plain list whatever this app asks for. So the shape
|
|
14436
|
+
* is read, never assumed — a view that indexed `data` as an array
|
|
14437
|
+
* would render an object's keys as rows the day a backend
|
|
14438
|
+
* upgraded, which is the kind of break that looks like a bug in
|
|
14439
|
+
* the app.
|
|
14440
|
+
*
|
|
14441
|
+
* Pure and tested, because this is exactly the sort of thing that
|
|
14442
|
+
* cannot be exercised until the other side moves.
|
|
14443
|
+
*
|
|
14444
|
+
* Copyright (c) 2026, ArtGins.
|
|
14445
|
+
* All Rights Reserved.
|
|
14446
|
+
***********************************************************************/
|
|
14447
|
+
/***************************************************************
|
|
14448
|
+
* nodes_answer(data) -> {rows, total, pages, paged}
|
|
14449
|
+
*
|
|
14450
|
+
* `rows` is always an array, so a caller never has to ask which
|
|
14451
|
+
* shape it got. `paged` says whether the backend answered a page,
|
|
14452
|
+
* which is the one thing a caller may legitimately care about (it is
|
|
14453
|
+
* what tells a pager it has something to page).
|
|
14454
|
+
***************************************************************/
|
|
14455
|
+
function nodes_answer(data) {
|
|
14456
|
+
if (Array.isArray(data)) return {
|
|
14457
|
+
rows: data,
|
|
14458
|
+
total: data.length,
|
|
14459
|
+
pages: 1,
|
|
14460
|
+
paged: false
|
|
14461
|
+
};
|
|
14462
|
+
if (data && typeof data === "object" && Array.isArray(data.data)) {
|
|
14463
|
+
let rows = data.data;
|
|
14464
|
+
return {
|
|
14465
|
+
rows,
|
|
14466
|
+
total: typeof data.total_rows === "number" ? data.total_rows : rows.length,
|
|
14467
|
+
pages: typeof data.pages === "number" && data.pages > 0 ? data.pages : 1,
|
|
14468
|
+
paged: true
|
|
14469
|
+
};
|
|
14470
|
+
}
|
|
14471
|
+
return {
|
|
14472
|
+
rows: [],
|
|
14473
|
+
total: 0,
|
|
14474
|
+
pages: 1,
|
|
14475
|
+
paged: false
|
|
14476
|
+
};
|
|
14477
|
+
}
|
|
14478
|
+
//#endregion
|
|
14419
14479
|
//#region src/c_yui_treedb_topics.js
|
|
14420
14480
|
/***********************************************************************
|
|
14421
14481
|
* c_yui_treedb_topics.js
|
|
@@ -14437,6 +14497,7 @@ var attrs_table$6 = [
|
|
|
14437
14497
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_POINTER, "gobj_remote_yuno", 0, null, "Remote yuno for data fetching"),
|
|
14438
14498
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "treedb_name", 0, null, "Remote TreeDB service name"),
|
|
14439
14499
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_JSON, "descs", 0, null, "Description of topics"),
|
|
14500
|
+
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_remote_paging", 0, false, "Each topic table pulls its rows a PAGE at a time (`nodes` from/limit) instead of loading the topic whole. Safe against a backend that cannot page: it answers the whole list, which the table reads as one page"),
|
|
14440
14501
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "system", 0, false, "Manage system topics (true) or user topics (false)"),
|
|
14441
14502
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "tabs_style", 0, "is-toggle is-fullwidth", "Bulma tab styling"),
|
|
14442
14503
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_cards_landing", 0, false, "Land on a grid of topic cards (list->detail): a card opens its table, with the tabs bar + a back-to-grid button. Off = tabs only (legacy)."),
|
|
@@ -15250,7 +15311,8 @@ function process_treedb_descs$1(gobj) {
|
|
|
15250
15311
|
with_delete_button: !readonly,
|
|
15251
15312
|
treedb_name,
|
|
15252
15313
|
topic_name: key,
|
|
15253
|
-
desc
|
|
15314
|
+
desc,
|
|
15315
|
+
with_remote_paging: (0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "with_remote_paging")
|
|
15254
15316
|
};
|
|
15255
15317
|
let id = `${(0, _yuneta_gobj_js.gobj_name)(gobj)}?${desc.topic_name}`;
|
|
15256
15318
|
let gobj_topic_form = (0, _yuneta_gobj_js.gobj_create_service)(id, "C_YUI_TREEDB_TOPIC_WITH_FORM", kw_topic_form, gobj);
|
|
@@ -15321,6 +15383,8 @@ function register_sub_routes$1(gobj) {
|
|
|
15321
15383
|
function get_nodes$1(gobj, topic_name) {
|
|
15322
15384
|
const treedb_name = gobj.priv.treedb_name;
|
|
15323
15385
|
subscribe_treedb$1(gobj, topic_name);
|
|
15386
|
+
let gobj_topic_form = get_gobj_formtable(gobj, topic_name);
|
|
15387
|
+
if (gobj_topic_form && (0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj_topic_form, "with_remote_paging")) return;
|
|
15324
15388
|
treedb_nodes$1(gobj, treedb_name, topic_name, { list_dict: true });
|
|
15325
15389
|
}
|
|
15326
15390
|
/************************************************************
|
|
@@ -15368,7 +15432,7 @@ function get_gobj_formtable(gobj, topic_name) {
|
|
|
15368
15432
|
/************************************************************
|
|
15369
15433
|
* Command to remote service
|
|
15370
15434
|
************************************************************/
|
|
15371
|
-
function treedb_nodes$1(gobj, treedb_name, topic_name, options) {
|
|
15435
|
+
function treedb_nodes$1(gobj, treedb_name, topic_name, options, page) {
|
|
15372
15436
|
let command = "nodes";
|
|
15373
15437
|
let gobj_remote_yuno = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(gobj, "gobj_remote_yuno");
|
|
15374
15438
|
let kw = {
|
|
@@ -15378,8 +15442,17 @@ function treedb_nodes$1(gobj, treedb_name, topic_name, options) {
|
|
|
15378
15442
|
options: options || {}
|
|
15379
15443
|
};
|
|
15380
15444
|
kw.__md_command__ = { topic_name };
|
|
15445
|
+
if (page) {
|
|
15446
|
+
kw.from = page.from;
|
|
15447
|
+
kw.limit = page.limit;
|
|
15448
|
+
kw.__md_command__.req_id = page.req_id;
|
|
15449
|
+
}
|
|
15381
15450
|
let ret = (0, _yuneta_gobj_js.gobj_command)(gobj_remote_yuno, command, kw, gobj);
|
|
15382
|
-
if (ret)
|
|
15451
|
+
if (ret) {
|
|
15452
|
+
(0, _yuneta_gobj_js.log_error)(ret);
|
|
15453
|
+
return -1;
|
|
15454
|
+
}
|
|
15455
|
+
return 0;
|
|
15383
15456
|
}
|
|
15384
15457
|
/************************************************************
|
|
15385
15458
|
* Command to remote service
|
|
@@ -15613,7 +15686,7 @@ function ac_mt_command_answer$2(gobj, event, kw, src) {
|
|
|
15613
15686
|
yui_shell_show_error(yui_shell_of(gobj), comment, { t: i18next.t });
|
|
15614
15687
|
if (command === "update-node" || command === "create-node") {
|
|
15615
15688
|
let failed_topic = (0, _yuneta_gobj_js.kw_get_str)(gobj, kw_command, "topic_name", "", 0);
|
|
15616
|
-
if (failed_topic)
|
|
15689
|
+
if (failed_topic) (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_REFRESH_TOPIC", { topic_name: failed_topic }, gobj);
|
|
15617
15690
|
}
|
|
15618
15691
|
}
|
|
15619
15692
|
return 0;
|
|
@@ -15629,7 +15702,16 @@ function ac_mt_command_answer$2(gobj, event, kw, src) {
|
|
|
15629
15702
|
let topic_name = (0, _yuneta_gobj_js.kw_get_str)(gobj, kw_command, "topic_name", "", _yuneta_gobj_js.kw_flag_t.KW_REQUIRED);
|
|
15630
15703
|
if (result >= 0) {
|
|
15631
15704
|
let gobj_topic_form = (0, _yuneta_gobj_js.gobj_find_child)(gobj, { __gobj_name__: `${(0, _yuneta_gobj_js.gobj_name)(gobj)}?${topic_name}` });
|
|
15632
|
-
|
|
15705
|
+
let answer = nodes_answer(data);
|
|
15706
|
+
let md = (0, _yuneta_gobj_js.kw_get_dict)(gobj, kw_command, "__md_command__", {}, 0);
|
|
15707
|
+
let req_id = (0, _yuneta_gobj_js.kw_get_str)(gobj, md, "req_id", "", 0);
|
|
15708
|
+
if (req_id) (0, _yuneta_gobj_js.gobj_send_event)(gobj_topic_form, "EV_PAGE_LOADED", {
|
|
15709
|
+
req_id,
|
|
15710
|
+
rows: answer.rows,
|
|
15711
|
+
pages: answer.pages,
|
|
15712
|
+
total: answer.total
|
|
15713
|
+
}, gobj);
|
|
15714
|
+
else (0, _yuneta_gobj_js.gobj_send_event)(gobj_topic_form, "EV_LOAD_NODES", answer.rows, gobj);
|
|
15633
15715
|
}
|
|
15634
15716
|
break;
|
|
15635
15717
|
case "create-node":
|
|
@@ -15855,6 +15937,31 @@ function ac_create_record(gobj, event, kw, src) {
|
|
|
15855
15937
|
});
|
|
15856
15938
|
}
|
|
15857
15939
|
/********************************************
|
|
15940
|
+
* A topic table wants a page.
|
|
15941
|
+
*
|
|
15942
|
+
* The transport is ours, so the table asks and we fetch; the answer
|
|
15943
|
+
* finds its way back by the `req_id` we echo. A backend that cannot
|
|
15944
|
+
* page answers the whole list, which the table takes as one page —
|
|
15945
|
+
* which is the truth, and is why the table can ask without knowing
|
|
15946
|
+
* what it is talking to.
|
|
15947
|
+
********************************************/
|
|
15948
|
+
function ac_request_page(gobj, event, kw, src) {
|
|
15949
|
+
let topic_name = kw.topic_name || (0, _yuneta_gobj_js.gobj_read_attr)(src, "topic_name");
|
|
15950
|
+
if (!topic_name || !kw.req_id) {
|
|
15951
|
+
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: a page request with no topic or no id`);
|
|
15952
|
+
return -1;
|
|
15953
|
+
}
|
|
15954
|
+
if (treedb_nodes$1(gobj, (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "treedb_name"), topic_name, { list_dict: true }, {
|
|
15955
|
+
from: kw.from,
|
|
15956
|
+
limit: kw.limit,
|
|
15957
|
+
req_id: kw.req_id
|
|
15958
|
+
}) < 0) (0, _yuneta_gobj_js.gobj_send_event)(src, "EV_PAGE_FAILED", {
|
|
15959
|
+
req_id: kw.req_id,
|
|
15960
|
+
error: "cannot reach the backend"
|
|
15961
|
+
}, gobj);
|
|
15962
|
+
return 0;
|
|
15963
|
+
}
|
|
15964
|
+
/********************************************
|
|
15858
15965
|
* Event from formtable: ONE field of one record, edited in place.
|
|
15859
15966
|
*
|
|
15860
15967
|
* Written as a PARTIAL update and, deliberately, with NO `autolink`.
|
|
@@ -15921,6 +16028,11 @@ function ac_delete_record(gobj, event, kw, src) {
|
|
|
15921
16028
|
* }
|
|
15922
16029
|
********************************************/
|
|
15923
16030
|
function ac_refresh_topic(gobj, event, kw, src) {
|
|
16031
|
+
let gobj_topic_form = get_gobj_formtable(gobj, kw.topic_name);
|
|
16032
|
+
if (gobj_topic_form && (0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj_topic_form, "with_remote_paging")) {
|
|
16033
|
+
(0, _yuneta_gobj_js.gobj_send_event)(gobj_topic_form, "EV_REPULL_PAGE", {}, gobj);
|
|
16034
|
+
return 0;
|
|
16035
|
+
}
|
|
15924
16036
|
treedb_nodes$1(gobj, (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "treedb_name"), kw.topic_name, { list_dict: true });
|
|
15925
16037
|
return 0;
|
|
15926
16038
|
}
|
|
@@ -16010,6 +16122,11 @@ function create_gclass$6(gclass_name) {
|
|
|
16010
16122
|
ac_update_field,
|
|
16011
16123
|
null
|
|
16012
16124
|
],
|
|
16125
|
+
[
|
|
16126
|
+
"EV_REQUEST_PAGE",
|
|
16127
|
+
ac_request_page,
|
|
16128
|
+
null
|
|
16129
|
+
],
|
|
16013
16130
|
[
|
|
16014
16131
|
"EV_DELETE_RECORD",
|
|
16015
16132
|
ac_delete_record,
|
|
@@ -16084,6 +16201,7 @@ function create_gclass$6(gclass_name) {
|
|
|
16084
16201
|
["EV_CREATE_RECORD", 0],
|
|
16085
16202
|
["EV_UPDATE_RECORD", 0],
|
|
16086
16203
|
["EV_UPDATE_FIELD", 0],
|
|
16204
|
+
["EV_REQUEST_PAGE", 0],
|
|
16087
16205
|
["EV_DELETE_RECORD", 0],
|
|
16088
16206
|
["EV_RECORD_WRITTEN", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT | _yuneta_gobj_js.event_flag_t.EVF_NO_WARN_SUBS],
|
|
16089
16207
|
["EV_REFRESH_TOPIC", 0],
|
|
@@ -16298,6 +16416,8 @@ var attrs_table$5 = [
|
|
|
16298
16416
|
(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)"),
|
|
16299
16417
|
(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
16418
|
(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)"),
|
|
16419
|
+
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_remote_paging", 0, false, "Pull the topic a PAGE at a time from the backend (`nodes` from/limit) instead of loading it whole. Needs a backend that pages"),
|
|
16420
|
+
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_INTEGER, "page_size", 0, 200, "Rows per page when with_remote_paging. Generous on purpose: a treedb that fits in one page behaves exactly as it did, paginator hidden and every filter seeing every row"),
|
|
16301
16421
|
(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"),
|
|
16302
16422
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "editable", 0, false, "Edit state"),
|
|
16303
16423
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_checkbox", 0, true, "Auxiliary first column to select rows"),
|
|
@@ -16326,6 +16446,8 @@ var attrs_table$5 = [
|
|
|
16326
16446
|
(0, _yuneta_gobj_js.SDATA_END)()
|
|
16327
16447
|
];
|
|
16328
16448
|
var PRIVATE_DATA$5 = {
|
|
16449
|
+
_pending_pages: null,
|
|
16450
|
+
_page_seq: 0,
|
|
16329
16451
|
$container: null,
|
|
16330
16452
|
treedb_name: "",
|
|
16331
16453
|
topic_name: "",
|
|
@@ -17051,6 +17173,25 @@ function create_tabulator(gobj) {
|
|
|
17051
17173
|
let pkey = desc.pkey || "id";
|
|
17052
17174
|
let selectable = with_checkbox ? "highlight" : with_radio ? 1 : false;
|
|
17053
17175
|
let tabulator_settings = (0, _yuneta_gobj_js.json_deep_copy)((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "tabulator_settings"));
|
|
17176
|
+
if ((0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "with_remote_paging")) {
|
|
17177
|
+
let page_size = (0, _yuneta_gobj_js.gobj_read_integer_attr)(gobj, "page_size") || 200;
|
|
17178
|
+
Object.assign(tabulator_settings, {
|
|
17179
|
+
pagination: true,
|
|
17180
|
+
paginationMode: "remote",
|
|
17181
|
+
filterMode: "local",
|
|
17182
|
+
paginationSize: page_size,
|
|
17183
|
+
paginationSizeSelector: [
|
|
17184
|
+
50,
|
|
17185
|
+
100,
|
|
17186
|
+
200,
|
|
17187
|
+
500
|
|
17188
|
+
],
|
|
17189
|
+
ajaxURL: "nodes",
|
|
17190
|
+
ajaxRequestFunc: function(url, config, params) {
|
|
17191
|
+
return request_page(gobj, params.page || 1, params.size || page_size);
|
|
17192
|
+
}
|
|
17193
|
+
});
|
|
17194
|
+
}
|
|
17054
17195
|
Object.assign(tabulator_settings, {
|
|
17055
17196
|
index: pkey,
|
|
17056
17197
|
columns,
|
|
@@ -18252,6 +18393,99 @@ function ac_show_schema(gobj, event, kw, src) {
|
|
|
18252
18393
|
return 0;
|
|
18253
18394
|
}
|
|
18254
18395
|
/************************************************************
|
|
18396
|
+
* Ask the host for one page and park the promise Tabulator wants.
|
|
18397
|
+
*
|
|
18398
|
+
* The transport belongs to the HOST, so the request goes up as an event
|
|
18399
|
+
* and the answer comes back down as one; what lives here is only the
|
|
18400
|
+
* promise, keyed by a correlation id the host echoes.
|
|
18401
|
+
*
|
|
18402
|
+
* Rejected right away when there is nobody to ask: a request that can
|
|
18403
|
+
* never be answered would leave the table spinning for ever and leak
|
|
18404
|
+
* one entry per attempt. Same for the watchdog — the link can stay up
|
|
18405
|
+
* and the answer still never land.
|
|
18406
|
+
************************************************************/
|
|
18407
|
+
var PAGE_TIMEOUT_MS = 2e4;
|
|
18408
|
+
function request_page(gobj, page, size) {
|
|
18409
|
+
let priv = gobj.priv;
|
|
18410
|
+
return new Promise(function(resolve, reject) {
|
|
18411
|
+
if (!priv._pending_pages) priv._pending_pages = {};
|
|
18412
|
+
let req_id = `p${++priv._page_seq}`;
|
|
18413
|
+
let timer = window.setTimeout(function() {
|
|
18414
|
+
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_PAGE_TIMEOUT", { req_id }, gobj);
|
|
18415
|
+
}, PAGE_TIMEOUT_MS);
|
|
18416
|
+
priv._pending_pages[req_id] = {
|
|
18417
|
+
resolve,
|
|
18418
|
+
reject,
|
|
18419
|
+
timer
|
|
18420
|
+
};
|
|
18421
|
+
(0, _yuneta_gobj_js.gobj_publish_event)(gobj, "EV_REQUEST_PAGE", {
|
|
18422
|
+
topic_name: (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "topic_name"),
|
|
18423
|
+
req_id,
|
|
18424
|
+
from: (page - 1) * size + 1,
|
|
18425
|
+
limit: size
|
|
18426
|
+
});
|
|
18427
|
+
});
|
|
18428
|
+
}
|
|
18429
|
+
/************************************************************
|
|
18430
|
+
* Settle a parked page request, however it ended.
|
|
18431
|
+
************************************************************/
|
|
18432
|
+
function settle_page(gobj, req_id, value, error) {
|
|
18433
|
+
let priv = gobj.priv;
|
|
18434
|
+
let pend = priv._pending_pages ? priv._pending_pages[req_id] : null;
|
|
18435
|
+
if (!pend) return;
|
|
18436
|
+
delete priv._pending_pages[req_id];
|
|
18437
|
+
if (pend.timer) window.clearTimeout(pend.timer);
|
|
18438
|
+
if (error) {
|
|
18439
|
+
pend.reject(new Error(error));
|
|
18440
|
+
return;
|
|
18441
|
+
}
|
|
18442
|
+
pend.resolve(value);
|
|
18443
|
+
}
|
|
18444
|
+
/************************************************************
|
|
18445
|
+
* Pull the current page again. `replaceData()` re-runs
|
|
18446
|
+
* ajaxRequestFunc for the page the reader is on, so a refresh keeps
|
|
18447
|
+
* their position instead of throwing them back to the first page.
|
|
18448
|
+
************************************************************/
|
|
18449
|
+
function ac_repull_page(gobj, event, kw, src) {
|
|
18450
|
+
let tabulator = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "tabulator");
|
|
18451
|
+
if (!tabulator) return 0;
|
|
18452
|
+
try {
|
|
18453
|
+
tabulator.replaceData();
|
|
18454
|
+
} catch (e) {
|
|
18455
|
+
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: cannot re-pull the page: ${e}`);
|
|
18456
|
+
return -1;
|
|
18457
|
+
}
|
|
18458
|
+
return 0;
|
|
18459
|
+
}
|
|
18460
|
+
/************************************************************
|
|
18461
|
+
* The host answered a page.
|
|
18462
|
+
************************************************************/
|
|
18463
|
+
function ac_page_loaded(gobj, event, kw, src) {
|
|
18464
|
+
settle_page(gobj, kw.req_id, {
|
|
18465
|
+
data: (0, _yuneta_gobj_js.is_array)(kw.rows) ? kw.rows : [],
|
|
18466
|
+
last_page: kw.pages || 1,
|
|
18467
|
+
last_row: typeof kw.total === "number" ? kw.total : 0
|
|
18468
|
+
}, null);
|
|
18469
|
+
return 0;
|
|
18470
|
+
}
|
|
18471
|
+
/************************************************************
|
|
18472
|
+
* The host could not answer it.
|
|
18473
|
+
************************************************************/
|
|
18474
|
+
function ac_page_failed(gobj, event, kw, src) {
|
|
18475
|
+
let why = kw && kw.error || "page request failed";
|
|
18476
|
+
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: ${why}`);
|
|
18477
|
+
settle_page(gobj, kw.req_id, null, why);
|
|
18478
|
+
return 0;
|
|
18479
|
+
}
|
|
18480
|
+
/************************************************************
|
|
18481
|
+
* Nobody answered in time.
|
|
18482
|
+
************************************************************/
|
|
18483
|
+
function ac_page_timeout(gobj, event, kw, src) {
|
|
18484
|
+
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: no answer for page request '${kw.req_id}' in ${PAGE_TIMEOUT_MS}ms`);
|
|
18485
|
+
settle_page(gobj, kw.req_id, null, "page request timed out");
|
|
18486
|
+
return 0;
|
|
18487
|
+
}
|
|
18488
|
+
/************************************************************
|
|
18255
18489
|
* Filter the loaded rows by a term matched against every non-internal
|
|
18256
18490
|
* field. `clearFilter()` with no argument drops only THIS filter: the
|
|
18257
18491
|
* per-column header filters are a separate layer and survive, so a
|
|
@@ -18499,6 +18733,26 @@ function create_gclass$5(gclass_name) {
|
|
|
18499
18733
|
ac_cell_edited,
|
|
18500
18734
|
null
|
|
18501
18735
|
],
|
|
18736
|
+
[
|
|
18737
|
+
"EV_REPULL_PAGE",
|
|
18738
|
+
ac_repull_page,
|
|
18739
|
+
null
|
|
18740
|
+
],
|
|
18741
|
+
[
|
|
18742
|
+
"EV_PAGE_LOADED",
|
|
18743
|
+
ac_page_loaded,
|
|
18744
|
+
null
|
|
18745
|
+
],
|
|
18746
|
+
[
|
|
18747
|
+
"EV_PAGE_FAILED",
|
|
18748
|
+
ac_page_failed,
|
|
18749
|
+
null
|
|
18750
|
+
],
|
|
18751
|
+
[
|
|
18752
|
+
"EV_PAGE_TIMEOUT",
|
|
18753
|
+
ac_page_timeout,
|
|
18754
|
+
null
|
|
18755
|
+
],
|
|
18502
18756
|
[
|
|
18503
18757
|
"EV_SEARCH",
|
|
18504
18758
|
ac_search,
|
|
@@ -18550,7 +18804,12 @@ function create_gclass$5(gclass_name) {
|
|
|
18550
18804
|
["EV_REFRESH", 0],
|
|
18551
18805
|
["EV_SHOW_SCHEMA", 0],
|
|
18552
18806
|
["EV_CELL_EDITED", 0],
|
|
18807
|
+
["EV_REPULL_PAGE", 0],
|
|
18808
|
+
["EV_PAGE_LOADED", 0],
|
|
18809
|
+
["EV_PAGE_FAILED", 0],
|
|
18810
|
+
["EV_PAGE_TIMEOUT", 0],
|
|
18553
18811
|
["EV_UPDATE_FIELD", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT],
|
|
18812
|
+
["EV_REQUEST_PAGE", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT],
|
|
18554
18813
|
["EV_SEARCH", 0],
|
|
18555
18814
|
["EV_OPEN_COLUMNS", 0],
|
|
18556
18815
|
["EV_TOGGLE_COLUMN", 0],
|