@yuneta/gobj-ui 7.23.51 → 7.23.53
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 +21 -5
- package/dist/gobj-ui.cjs.js +237 -157
- package/dist/gobj-ui.es.js +237 -157
- package/package.json +2 -2
- package/src/c_yui_form.js +4 -0
- package/src/c_yui_treedb_topic_with_form.css +10 -0
- package/src/c_yui_treedb_topic_with_form.js +89 -7
- package/src/c_yui_treedb_topics.js +4 -1
- package/src/lib_icons.js +51 -0
|
@@ -86,6 +86,7 @@ import {
|
|
|
86
86
|
} from "./yui_table_select.js";
|
|
87
87
|
|
|
88
88
|
import {yui_table_filter_clear} from "./yui_table_filter_clear.js";
|
|
89
|
+
import {yui_icon_is_defined} from "./lib_icons.js";
|
|
89
90
|
import {row_matches} from "./yui_row_search.js";
|
|
90
91
|
|
|
91
92
|
import {t} from "i18next";
|
|
@@ -188,8 +189,8 @@ SDATA(data_type_t.DTP_BOOLEAN, "with_columns_button", 0, true, "Button
|
|
|
188
189
|
SDATA(data_type_t.DTP_BOOLEAN, "with_export_button", 0, true, "Button toolbar EXPORT (download as CSV what the table holds)"),
|
|
189
190
|
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)"),
|
|
190
191
|
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)"),
|
|
191
|
-
SDATA(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"),
|
|
192
|
-
SDATA(data_type_t.DTP_INTEGER, "page_size", 0,
|
|
192
|
+
SDATA(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. ⚠️ DO NOT TURN THIS ON: it breaks LINKS -- see the note above the tabulator settings. It is off in every consumer until linking works with a partial topic"),
|
|
193
|
+
SDATA(data_type_t.DTP_INTEGER, "page_size", 0, 0, "Rows per page. 0 leaves the `tabulator_settings` value alone. With with_remote_paging it is also the size of each fetch (200 when unset)"),
|
|
193
194
|
SDATA(data_type_t.DTP_BOOLEAN, "with_in_row_edit_icons", 0, true, "Add a last column with internal EDIT/DELETE icon"),
|
|
194
195
|
|
|
195
196
|
SDATA(data_type_t.DTP_BOOLEAN, "editable", 0, false, "Edit state"),
|
|
@@ -1284,6 +1285,14 @@ function create_tabulator(gobj)
|
|
|
1284
1285
|
hozAlign = "center";
|
|
1285
1286
|
colFormatter = "color";
|
|
1286
1287
|
break;
|
|
1288
|
+
case "icon":
|
|
1289
|
+
/* The NAME of an icon of the app's set, not a file --
|
|
1290
|
+
* which is why `image` cannot stand in for it: it built
|
|
1291
|
+
* an `<img src="yi-bolt">` and drew a broken image. */
|
|
1292
|
+
hozAlign = "center";
|
|
1293
|
+
vertAlign = "middle";
|
|
1294
|
+
colFormatter = icon_formatter;
|
|
1295
|
+
break;
|
|
1287
1296
|
case "object":
|
|
1288
1297
|
case "dict":
|
|
1289
1298
|
case "template":
|
|
@@ -1393,16 +1402,29 @@ function create_tabulator(gobj)
|
|
|
1393
1402
|
let tabulator_settings = json_deep_copy(gobj_read_attr(gobj, "tabulator_settings"));
|
|
1394
1403
|
|
|
1395
1404
|
/* Remote paging: the TABLE pulls, instead of the host pushing the whole
|
|
1396
|
-
* topic down.
|
|
1397
|
-
*
|
|
1398
|
-
* hidden and every filter seeing every row. Only a topic that does NOT
|
|
1399
|
-
* fit pays for paging, and for that one loading it whole was never an
|
|
1400
|
-
* option anyway.
|
|
1405
|
+
* topic down. Only a topic that does NOT fit pays for it, and for that
|
|
1406
|
+
* one loading it whole was never an option anyway.
|
|
1401
1407
|
*
|
|
1402
1408
|
* `filterMode: "local"` says the plain truth: the header filters and the
|
|
1403
1409
|
* search box work on the page that is loaded. Same as the tranger
|
|
1404
1410
|
* browser's Rows card, and for the same reason — the alternative is
|
|
1405
1411
|
* pushing every filter to the backend and changing what "search" means.
|
|
1412
|
+
*
|
|
1413
|
+
* ⚠️ AND IT IS OFF IN EVERY CONSUMER, because it BREAKS LINKING.
|
|
1414
|
+
* `build_fkey_options()` asks the PARENT topic's table for the rows it
|
|
1415
|
+
* holds (`get_topic_data` -> `tabulator.getData()`), and with paging
|
|
1416
|
+
* that is one page. So the link picker offers 200 of the 5891 possible
|
|
1417
|
+
* parents -- the right one is usually not among them -- and a record
|
|
1418
|
+
* whose parent is not on the loaded page opens with an fkey the form
|
|
1419
|
+
* cannot match, which saving then drops. Local pagination has none of
|
|
1420
|
+
* this: `getData()` answers the WHOLE dataset whatever page is on
|
|
1421
|
+
* screen, so paginating the DISPLAY is safe and only paginating the
|
|
1422
|
+
* FETCH is not.
|
|
1423
|
+
*
|
|
1424
|
+
* A treedb is a memory database -- the backend holds the topic in RAM
|
|
1425
|
+
* either way -- so paging buys little and costs the links. It stays
|
|
1426
|
+
* here, working and tested, for the day linking learns to ask the
|
|
1427
|
+
* BACKEND for a parent instead of the sibling table.
|
|
1406
1428
|
*/
|
|
1407
1429
|
if(gobj_read_bool_attr(gobj, "with_remote_paging")) {
|
|
1408
1430
|
let page_size = gobj_read_integer_attr(gobj, "page_size") || 200;
|
|
@@ -1428,6 +1450,20 @@ function create_tabulator(gobj)
|
|
|
1428
1450
|
});
|
|
1429
1451
|
}
|
|
1430
1452
|
|
|
1453
|
+
/* Rows per page for the LOCAL paginator too, when the host asks for
|
|
1454
|
+
* one: with the fetch no longer paged, `page_size` is the only thing
|
|
1455
|
+
* left that says how many rows a reader wants in front of them, and a
|
|
1456
|
+
* host that had 200 should not drop to 25 for having stopped paging the
|
|
1457
|
+
* fetch. The sizes offered go up with it -- a selector that cannot name
|
|
1458
|
+
* the size in use is a selector Tabulator has to patch itself. */
|
|
1459
|
+
if(!gobj_read_bool_attr(gobj, "with_remote_paging")) {
|
|
1460
|
+
let page_size = gobj_read_integer_attr(gobj, "page_size");
|
|
1461
|
+
if(page_size > 0 && tabulator_settings.pagination) {
|
|
1462
|
+
tabulator_settings.paginationSize = page_size;
|
|
1463
|
+
tabulator_settings.paginationSizeSelector = [50, 100, 200, 500, true];
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1431
1467
|
/* The size the reader picked last time, which outranks both defaults
|
|
1432
1468
|
* above. Applied here, once the two paths have set theirs, so it is
|
|
1433
1469
|
* written in ONE place -- and applied to `paginationSize` only: the
|
|
@@ -1667,6 +1703,48 @@ function table__destroy(gobj)
|
|
|
1667
1703
|
}
|
|
1668
1704
|
}
|
|
1669
1705
|
|
|
1706
|
+
/************************************************************
|
|
1707
|
+
* Cell of a col flagged `icon`: the value is the NAME of an
|
|
1708
|
+
* icon of the app's set ("yi-bolt"), so it is DRAWN.
|
|
1709
|
+
*
|
|
1710
|
+
* Built as a DOM node and not as an HTML string: the value is
|
|
1711
|
+
* raw record data and must never be parsed as markup -- and
|
|
1712
|
+
* here it also becomes a CSS class, so it is filtered to the
|
|
1713
|
+
* shape of a class name before it gets near one.
|
|
1714
|
+
*
|
|
1715
|
+
* A name the set does not carry falls back to the TEXT of it.
|
|
1716
|
+
* The catch-all rule `[class^="yi-"]::before` paints a
|
|
1717
|
+
* currentColor box for any name, so drawing it anyway would
|
|
1718
|
+
* render a solid square -- a glyph nobody recognises rather
|
|
1719
|
+
* than a mistake somebody can fix.
|
|
1720
|
+
************************************************************/
|
|
1721
|
+
function icon_formatter(cell)
|
|
1722
|
+
{
|
|
1723
|
+
let name = cell.getValue();
|
|
1724
|
+
if(!is_string(name) || empty_string(name)) {
|
|
1725
|
+
return "";
|
|
1726
|
+
}
|
|
1727
|
+
name = name.trim();
|
|
1728
|
+
|
|
1729
|
+
if(!/^[a-z0-9-]+$/.test(name) || !yui_icon_is_defined(name)) {
|
|
1730
|
+
/* The NAME, in plain grey, and no invented label around it: it
|
|
1731
|
+
* is a developer's problem (a schema naming a glyph the set
|
|
1732
|
+
* does not carry) and the name IS the whole diagnosis -- while
|
|
1733
|
+
* a sentence here would be one more i18n key for every app
|
|
1734
|
+
* that mounts this table. */
|
|
1735
|
+
return createElement2([
|
|
1736
|
+
"span",
|
|
1737
|
+
{class: "ICON_CELL_UNKNOWN is-size-7 has-text-grey", title: name},
|
|
1738
|
+
name
|
|
1739
|
+
]);
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
return createElement2([
|
|
1743
|
+
"span",
|
|
1744
|
+
{class: `ICON_CELL ${name}`, title: name, "aria-label": name}
|
|
1745
|
+
]);
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1670
1748
|
/************************************************************
|
|
1671
1749
|
* Cell of a col holding a JSON document (dict/list/blob/
|
|
1672
1750
|
* template/coordinates/…): a one-line truncated preview,
|
|
@@ -1825,6 +1903,10 @@ function transform__treedb_value_2_table_value(gobj, col, value, row, field)
|
|
|
1825
1903
|
// Handled by Tabulator's built-in "color" formatter (see colDef above)
|
|
1826
1904
|
break;
|
|
1827
1905
|
|
|
1906
|
+
case "icon":
|
|
1907
|
+
// Handled by icon_formatter() (see colDef above)
|
|
1908
|
+
break;
|
|
1909
|
+
|
|
1828
1910
|
case "image":
|
|
1829
1911
|
// Handled by Tabulator's built-in "image" formatter (see colDef above)
|
|
1830
1912
|
break;
|
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
gobj_short_name,
|
|
33
33
|
gobj_read_str_attr,
|
|
34
34
|
gobj_read_bool_attr,
|
|
35
|
+
gobj_read_integer_attr,
|
|
35
36
|
gobj_start,
|
|
36
37
|
gobj_stop,
|
|
37
38
|
gobj_is_running,
|
|
@@ -75,7 +76,8 @@ SDATA(data_type_t.DTP_POINTER, "subscriber", 0, null, "Subscriber of o
|
|
|
75
76
|
SDATA(data_type_t.DTP_POINTER, "gobj_remote_yuno", 0, null, "Remote yuno for data fetching"),
|
|
76
77
|
SDATA(data_type_t.DTP_STRING, "treedb_name", 0, null, "Remote TreeDB service name"),
|
|
77
78
|
SDATA(data_type_t.DTP_JSON, "descs", 0, null, "Description of topics"),
|
|
78
|
-
SDATA(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.
|
|
79
|
+
SDATA(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. ⚠️ DO NOT TURN THIS ON: a partial topic BREAKS LINKING -- the link picker of a record is built from the rows the PARENT topic's table holds, which with paging is one page, so the right parent is usually not offered and an existing link the form cannot match is dropped on save. Off in every consumer until linking asks the backend instead of the sibling table"),
|
|
80
|
+
SDATA(data_type_t.DTP_INTEGER, "page_size", 0, 0, "Rows per page of each topic table (the DISPLAY; the fetch is whole). 0 leaves the table's own default"),
|
|
79
81
|
SDATA(data_type_t.DTP_BOOLEAN, "with_selection_bar", 0, false, "Each topic table shows the shared selection bar (\"N selected\" + a way out) above it while in edition mode. OFF by default: the bar takes its words from the HOST's i18n, so a host that turns it on must define \"{{n}} selected\" and \"clear selection\""),
|
|
80
82
|
SDATA(data_type_t.DTP_BOOLEAN, "system", 0, false, "Manage system topics (true) or user topics (false)"),
|
|
81
83
|
SDATA(data_type_t.DTP_STRING, "tabs_style", 0, "is-toggle is-fullwidth", "Bulma tab styling"),
|
|
@@ -1041,6 +1043,7 @@ function process_treedb_descs(gobj)
|
|
|
1041
1043
|
topic_name: key,
|
|
1042
1044
|
desc: desc,
|
|
1043
1045
|
with_remote_paging: gobj_read_bool_attr(gobj, "with_remote_paging"),
|
|
1046
|
+
page_size: gobj_read_integer_attr(gobj, "page_size"),
|
|
1044
1047
|
with_selection_bar: gobj_read_bool_attr(gobj, "with_selection_bar")
|
|
1045
1048
|
};
|
|
1046
1049
|
|
package/src/lib_icons.js
CHANGED
|
@@ -168,3 +168,54 @@ export function inject_svg_icons()
|
|
|
168
168
|
div.innerHTML = ICONS;
|
|
169
169
|
document.body.appendChild(div);
|
|
170
170
|
}
|
|
171
|
+
|
|
172
|
+
/*
|
|
173
|
+
* Is `yi-<name>` a real glyph of the loaded set?
|
|
174
|
+
*
|
|
175
|
+
* It has to be ASKED, not looked up in a list: the set is a CSS file
|
|
176
|
+
* (`yui_icons.css`) that an app may extend or replace, and a list here
|
|
177
|
+
* would be a second copy of it, wrong the first time somebody adds a
|
|
178
|
+
* mask rule. And the question matters because the catch-all rule
|
|
179
|
+
* `[class^="yi-"]::before` paints a `currentColor` box for ANY name --
|
|
180
|
+
* so an undefined icon does not fail, it renders as a solid black
|
|
181
|
+
* square, which reads as a glyph nobody recognises rather than as a
|
|
182
|
+
* mistake.
|
|
183
|
+
*
|
|
184
|
+
* The probe is the real stylesheet: a detached element gets no styles,
|
|
185
|
+
* so it is attached, hidden, measured and removed. Cached by name --
|
|
186
|
+
* a table draws one icon per row and the answer cannot change without
|
|
187
|
+
* a reload.
|
|
188
|
+
*/
|
|
189
|
+
const __icon_defined__ = {};
|
|
190
|
+
|
|
191
|
+
export function yui_icon_is_defined(name)
|
|
192
|
+
{
|
|
193
|
+
if(!name || typeof name !== "string") {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
if(__icon_defined__[name] !== undefined) {
|
|
197
|
+
return __icon_defined__[name];
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
let defined = false;
|
|
201
|
+
try {
|
|
202
|
+
const el = document.createElement("i");
|
|
203
|
+
el.className = name;
|
|
204
|
+
el.style.position = "absolute";
|
|
205
|
+
el.style.visibility = "hidden";
|
|
206
|
+
el.style.pointerEvents = "none";
|
|
207
|
+
document.body.appendChild(el);
|
|
208
|
+
const st = window.getComputedStyle(el, "::before");
|
|
209
|
+
const mask = st.maskImage || st.webkitMaskImage || "";
|
|
210
|
+
defined = !!mask && mask !== "none";
|
|
211
|
+
document.body.removeChild(el);
|
|
212
|
+
} catch(e) {
|
|
213
|
+
/* No DOM to ask (jsdom without the stylesheet, a test): answer
|
|
214
|
+
* "not defined" so the caller falls back to showing the name,
|
|
215
|
+
* which is the honest half of the pair. */
|
|
216
|
+
defined = false;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
__icon_defined__[name] = defined;
|
|
220
|
+
return defined;
|
|
221
|
+
}
|