@yuneta/gobj-ui 7.23.49 → 7.23.51

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 CHANGED
@@ -1780,6 +1780,51 @@ this facility: it is `formatter: "rowSelection"` with no `titleFormatter` and
1780
1780
  `selectableRows: 1`, and the header checkbox, the count and the bar all mean
1781
1781
  nothing there.
1782
1782
 
1783
+ ### Clearing a header filter — `yui_table_filter_clear.js`
1784
+
1785
+ A column filter is set by typing and undone by deleting what you typed, letter
1786
+ by letter, and nothing in the header says it can be dropped at all. With
1787
+ several columns filtered, getting back to the whole table is an exercise in
1788
+ remembering which ones you touched. One call, on a table that is already built:
1789
+
1790
+ ```js
1791
+ import {yui_table_filter_clear} from "@yuneta/gobj-ui/src/yui_table_filter_clear.js";
1792
+
1793
+ table.on("tableBuilt", () => {
1794
+ yui_table_filter_clear(table);
1795
+ });
1796
+ ```
1797
+
1798
+ The app must define the i18n key **`"clear"`**. Three decisions are baked in:
1799
+
1800
+ - **The ✕ is visible whenever the filter has content, focus or not** — the
1801
+ opposite of the ✕ in a form (`yui_inputs.js`), which shows only on the field
1802
+ being edited so a form full of filled fields does not light up an ✕ on every
1803
+ one. Here the filter is already set, the focus is elsewhere, and the whole
1804
+ point is to see it and drop it; hidden behind focus it would take two clicks.
1805
+ - **It clears through `setHeaderFilterValue()`**, not by synthesising a
1806
+ keystroke on the input — what a header filter listens to depends on that
1807
+ column's filter editor.
1808
+ - **It decorates the header by walking it**, because a table's columns are
1809
+ often built from data (a treedb schema) and the header rebuilds itself when
1810
+ they change. The walk is idempotent and gated by a single `querySelector`, so
1811
+ repeating it on every render costs nothing.
1812
+
1813
+ ### Searching a row that is not flat — `yui_row_search.js`
1814
+
1815
+ `row_matches(row, term)` is what the treedb topic table's search box uses, and
1816
+ it exists because **a treedb row is not flat**: with `list_dict` an fkey
1817
+ arrives as a list of objects — `[{id, topic_name, hook_name}]` — and
1818
+ `String(val)` of that is `"[object Object]"`. A search written the obvious way
1819
+ therefore never finds the place a node is linked to, while the cell plainly
1820
+ renders that id: what you see and what is searched are not the same thing.
1821
+
1822
+ It walks into lists and objects, and reads only the **`id`** of an fkey:
1823
+ `topic_name` and `hook_name` are the same two words on every row, so matching
1824
+ them turns any such term into a wildcard over the whole topic. Keys starting
1825
+ with `_` are never searched, at any level — scaffolding (`_operation`) and
1826
+ metadata (`__md_treedb__`).
1827
+
1783
1828
  ## Conventions
1784
1829
 
1785
1830
  ### i18n: a string must be able to CHANGE language, not just be translated once
@@ -22864,6 +22864,33 @@ function delete_impact(desc, records) {
22864
22864
  * Constants
22865
22865
  ***************************************************************/
22866
22866
  var GCLASS_NAME$5 = "C_YUI_TREEDB_TOPIC_WITH_FORM";
22867
+ var PAGE_SIZE_STORE_PREFIX = "yui_treedb_page_size:";
22868
+ /************************************************************
22869
+ * The size the reader left, or null when there is none.
22870
+ * `true` is Tabulator's "All" and it round-trips as the boolean:
22871
+ * JSON.parse gives it back as one, and a string "true" would be
22872
+ * read by Tabulator as a size of NaN.
22873
+ ************************************************************/
22874
+ function load_page_size(gobj) {
22875
+ try {
22876
+ let raw = window.localStorage.getItem(PAGE_SIZE_STORE_PREFIX + (0, _yuneta_gobj_js.gobj_name)(gobj));
22877
+ if (raw === null) return null;
22878
+ let size = JSON.parse(raw);
22879
+ if (size === true) return true;
22880
+ if (typeof size === "number" && size > 0) return size;
22881
+ return null;
22882
+ } catch (e) {
22883
+ return null;
22884
+ }
22885
+ }
22886
+ /************************************************************
22887
+ *
22888
+ ************************************************************/
22889
+ function save_page_size(gobj, size) {
22890
+ try {
22891
+ window.localStorage.setItem(PAGE_SIZE_STORE_PREFIX + (0, _yuneta_gobj_js.gobj_name)(gobj), JSON.stringify(size));
22892
+ } catch (e) {}
22893
+ }
22867
22894
  /***************************************************************
22868
22895
  * Data
22869
22896
  ***************************************************************/
@@ -22920,6 +22947,7 @@ var PRIVATE_DATA$5 = {
22920
22947
  _pending_pages: null,
22921
22948
  _page_seq: 0,
22922
22949
  _page_total: null,
22950
+ _page_size: null,
22923
22951
  $container: null,
22924
22952
  treedb_name: "",
22925
22953
  topic_name: "",
@@ -23806,6 +23834,9 @@ function create_tabulator(gobj) {
23806
23834
  }
23807
23835
  });
23808
23836
  }
23837
+ let remembered_size = load_page_size(gobj);
23838
+ if (remembered_size !== null && tabulator_settings.pagination) tabulator_settings.paginationSize = remembered_size;
23839
+ gobj.priv._page_size = tabulator_settings.paginationSize;
23809
23840
  Object.assign(tabulator_settings, {
23810
23841
  index: pkey,
23811
23842
  columns,
@@ -23841,12 +23872,26 @@ function create_tabulator(gobj) {
23841
23872
  single_page = max === false || max <= 1;
23842
23873
  } else {
23843
23874
  let size = tabulator.getPageSize();
23844
- single_page = !size || n <= size;
23875
+ single_page = size === true || !size || n <= size;
23845
23876
  }
23846
23877
  } catch (e) {
23847
23878
  single_page = true;
23848
23879
  }
23880
+ let total_rows;
23881
+ if (paged) total_rows = total;
23882
+ else try {
23883
+ total_rows = tabulator.getDataCount();
23884
+ } catch (e) {
23885
+ total_rows = n;
23886
+ }
23887
+ let min_size = 0;
23888
+ let sizes = tabulator.options ? tabulator.options.paginationSizeSelector : null;
23889
+ if (Array.isArray(sizes)) sizes.forEach(function(size) {
23890
+ if (typeof size === "number" && (!min_size || size < min_size)) min_size = size;
23891
+ });
23892
+ let no_paginator = single_page && (!min_size || typeof total_rows !== "number" || total_rows <= min_size);
23849
23893
  tabulator.element.classList.toggle("yui-single-page", single_page);
23894
+ tabulator.element.classList.toggle("yui-no-paginator", no_paginator);
23850
23895
  }
23851
23896
  tabulator._ready = false;
23852
23897
  tabulator.on("tableBuilt", function() {
@@ -23860,6 +23905,9 @@ function create_tabulator(gobj) {
23860
23905
  });
23861
23906
  tabulator.on("dataProcessed", update_rowcount);
23862
23907
  tabulator.on("dataChanged", update_rowcount);
23908
+ tabulator.on("pageSizeChanged", function(size) {
23909
+ (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_PAGE_SIZE_CHANGED", { size }, gobj);
23910
+ });
23863
23911
  tabulator.on("cellEdited", function(cell) {
23864
23912
  let pkey = ((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "desc") || {}).pkey || "id";
23865
23913
  let data = cell.getRow().getData();
@@ -25311,6 +25359,21 @@ function ac_open_columns(gobj, event, kw, src) {
25311
25359
  return 0;
25312
25360
  }
25313
25361
  /************************************************************
25362
+ * The reader picked another page size: remember it, so coming back to
25363
+ * this topic opens where they left it instead of at 200.
25364
+ ************************************************************/
25365
+ function ac_page_size_changed(gobj, event, kw, src) {
25366
+ let size = kw ? kw.size : void 0;
25367
+ if (size !== true && !(typeof size === "number" && size > 0)) {
25368
+ (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: bad page size: ${size}`);
25369
+ return -1;
25370
+ }
25371
+ if (size === gobj.priv._page_size) return 0;
25372
+ gobj.priv._page_size = size;
25373
+ save_page_size(gobj, size);
25374
+ return 0;
25375
+ }
25376
+ /************************************************************
25314
25377
  * Show / hide one column.
25315
25378
  ************************************************************/
25316
25379
  function ac_toggle_column(gobj, event, kw, src) {
@@ -25515,6 +25578,11 @@ function create_gclass$5(gclass_name) {
25515
25578
  ac_open_columns,
25516
25579
  null
25517
25580
  ],
25581
+ [
25582
+ "EV_PAGE_SIZE_CHANGED",
25583
+ ac_page_size_changed,
25584
+ null
25585
+ ],
25518
25586
  [
25519
25587
  "EV_TOGGLE_COLUMN",
25520
25588
  ac_toggle_column,
@@ -25565,6 +25633,7 @@ function create_gclass$5(gclass_name) {
25565
25633
  ["EV_REQUEST_PAGE", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT],
25566
25634
  ["EV_SEARCH", 0],
25567
25635
  ["EV_OPEN_COLUMNS", 0],
25636
+ ["EV_PAGE_SIZE_CHANGED", 0],
25568
25637
  ["EV_TOGGLE_COLUMN", 0],
25569
25638
  ["EV_EXPORT_TABLE", 0],
25570
25639
  ["EV_CREATE_RECORD", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT],
@@ -22859,6 +22859,33 @@ function delete_impact(desc, records) {
22859
22859
  * Constants
22860
22860
  ***************************************************************/
22861
22861
  var GCLASS_NAME$5 = "C_YUI_TREEDB_TOPIC_WITH_FORM";
22862
+ var PAGE_SIZE_STORE_PREFIX = "yui_treedb_page_size:";
22863
+ /************************************************************
22864
+ * The size the reader left, or null when there is none.
22865
+ * `true` is Tabulator's "All" and it round-trips as the boolean:
22866
+ * JSON.parse gives it back as one, and a string "true" would be
22867
+ * read by Tabulator as a size of NaN.
22868
+ ************************************************************/
22869
+ function load_page_size(gobj) {
22870
+ try {
22871
+ let raw = window.localStorage.getItem(PAGE_SIZE_STORE_PREFIX + gobj_name(gobj));
22872
+ if (raw === null) return null;
22873
+ let size = JSON.parse(raw);
22874
+ if (size === true) return true;
22875
+ if (typeof size === "number" && size > 0) return size;
22876
+ return null;
22877
+ } catch (e) {
22878
+ return null;
22879
+ }
22880
+ }
22881
+ /************************************************************
22882
+ *
22883
+ ************************************************************/
22884
+ function save_page_size(gobj, size) {
22885
+ try {
22886
+ window.localStorage.setItem(PAGE_SIZE_STORE_PREFIX + gobj_name(gobj), JSON.stringify(size));
22887
+ } catch (e) {}
22888
+ }
22862
22889
  /***************************************************************
22863
22890
  * Data
22864
22891
  ***************************************************************/
@@ -22915,6 +22942,7 @@ var PRIVATE_DATA$5 = {
22915
22942
  _pending_pages: null,
22916
22943
  _page_seq: 0,
22917
22944
  _page_total: null,
22945
+ _page_size: null,
22918
22946
  $container: null,
22919
22947
  treedb_name: "",
22920
22948
  topic_name: "",
@@ -23801,6 +23829,9 @@ function create_tabulator(gobj) {
23801
23829
  }
23802
23830
  });
23803
23831
  }
23832
+ let remembered_size = load_page_size(gobj);
23833
+ if (remembered_size !== null && tabulator_settings.pagination) tabulator_settings.paginationSize = remembered_size;
23834
+ gobj.priv._page_size = tabulator_settings.paginationSize;
23804
23835
  Object.assign(tabulator_settings, {
23805
23836
  index: pkey,
23806
23837
  columns,
@@ -23836,12 +23867,26 @@ function create_tabulator(gobj) {
23836
23867
  single_page = max === false || max <= 1;
23837
23868
  } else {
23838
23869
  let size = tabulator.getPageSize();
23839
- single_page = !size || n <= size;
23870
+ single_page = size === true || !size || n <= size;
23840
23871
  }
23841
23872
  } catch (e) {
23842
23873
  single_page = true;
23843
23874
  }
23875
+ let total_rows;
23876
+ if (paged) total_rows = total;
23877
+ else try {
23878
+ total_rows = tabulator.getDataCount();
23879
+ } catch (e) {
23880
+ total_rows = n;
23881
+ }
23882
+ let min_size = 0;
23883
+ let sizes = tabulator.options ? tabulator.options.paginationSizeSelector : null;
23884
+ if (Array.isArray(sizes)) sizes.forEach(function(size) {
23885
+ if (typeof size === "number" && (!min_size || size < min_size)) min_size = size;
23886
+ });
23887
+ let no_paginator = single_page && (!min_size || typeof total_rows !== "number" || total_rows <= min_size);
23844
23888
  tabulator.element.classList.toggle("yui-single-page", single_page);
23889
+ tabulator.element.classList.toggle("yui-no-paginator", no_paginator);
23845
23890
  }
23846
23891
  tabulator._ready = false;
23847
23892
  tabulator.on("tableBuilt", function() {
@@ -23855,6 +23900,9 @@ function create_tabulator(gobj) {
23855
23900
  });
23856
23901
  tabulator.on("dataProcessed", update_rowcount);
23857
23902
  tabulator.on("dataChanged", update_rowcount);
23903
+ tabulator.on("pageSizeChanged", function(size) {
23904
+ gobj_send_event(gobj, "EV_PAGE_SIZE_CHANGED", { size }, gobj);
23905
+ });
23858
23906
  tabulator.on("cellEdited", function(cell) {
23859
23907
  let pkey = (gobj_read_attr(gobj, "desc") || {}).pkey || "id";
23860
23908
  let data = cell.getRow().getData();
@@ -25306,6 +25354,21 @@ function ac_open_columns(gobj, event, kw, src) {
25306
25354
  return 0;
25307
25355
  }
25308
25356
  /************************************************************
25357
+ * The reader picked another page size: remember it, so coming back to
25358
+ * this topic opens where they left it instead of at 200.
25359
+ ************************************************************/
25360
+ function ac_page_size_changed(gobj, event, kw, src) {
25361
+ let size = kw ? kw.size : void 0;
25362
+ if (size !== true && !(typeof size === "number" && size > 0)) {
25363
+ log_error(`${gobj_short_name(gobj)}: bad page size: ${size}`);
25364
+ return -1;
25365
+ }
25366
+ if (size === gobj.priv._page_size) return 0;
25367
+ gobj.priv._page_size = size;
25368
+ save_page_size(gobj, size);
25369
+ return 0;
25370
+ }
25371
+ /************************************************************
25309
25372
  * Show / hide one column.
25310
25373
  ************************************************************/
25311
25374
  function ac_toggle_column(gobj, event, kw, src) {
@@ -25510,6 +25573,11 @@ function create_gclass$5(gclass_name) {
25510
25573
  ac_open_columns,
25511
25574
  null
25512
25575
  ],
25576
+ [
25577
+ "EV_PAGE_SIZE_CHANGED",
25578
+ ac_page_size_changed,
25579
+ null
25580
+ ],
25513
25581
  [
25514
25582
  "EV_TOGGLE_COLUMN",
25515
25583
  ac_toggle_column,
@@ -25560,6 +25628,7 @@ function create_gclass$5(gclass_name) {
25560
25628
  ["EV_REQUEST_PAGE", event_flag_t.EVF_OUTPUT_EVENT],
25561
25629
  ["EV_SEARCH", 0],
25562
25630
  ["EV_OPEN_COLUMNS", 0],
25631
+ ["EV_PAGE_SIZE_CHANGED", 0],
25563
25632
  ["EV_TOGGLE_COLUMN", 0],
25564
25633
  ["EV_EXPORT_TABLE", 0],
25565
25634
  ["EV_CREATE_RECORD", event_flag_t.EVF_OUTPUT_EVENT],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuneta/gobj-ui",
3
- "version": "7.23.49",
3
+ "version": "7.23.51",
4
4
  "type": "module",
5
5
  "main": "dist/gobj-ui.cjs.js",
6
6
  "module": "dist/gobj-ui.es.js",
@@ -23,10 +23,20 @@
23
23
  line-height: 1.5;
24
24
  }
25
25
 
26
- /* All rows fit in one page: the page-size selector and the
27
- * First/Prev/1/Next/Last strip are pure noise (worst on mobile) —
28
- * update_rowcount() toggles the class on every data change. */
29
- .tabulator.yui-single-page .tabulator-footer .tabulator-paginator {
26
+ /* All rows fit in one page: the First/Prev/1/Next/Last strip is pure
27
+ * noise (worst on mobile) — update_rowcount() toggles the class on every
28
+ * data change. The page-size SELECTOR stays: it is what made the table
29
+ * one page in the first place ("All" on a topic of 300 rows), so hiding
30
+ * it together with the buttons left the reader no way back to 50. */
31
+ .tabulator.yui-single-page .tabulator-footer .tabulator-paginator .tabulator-page,
32
+ .tabulator.yui-single-page .tabulator-footer .tabulator-paginator .tabulator-pages {
33
+ display: none;
34
+ }
35
+
36
+ /* No choice in the selector would change anything either — the topic is
37
+ * smaller than the smallest page size it offers — so the whole strip,
38
+ * label and select included, is noise. */
39
+ .tabulator.yui-no-paginator .tabulator-footer .tabulator-paginator {
30
40
  display: none;
31
41
  }
32
42
 
@@ -103,6 +103,67 @@ import { TabulatorFull as Tabulator } from "tabulator-tables";
103
103
  ***************************************************************/
104
104
  const GCLASS_NAME = "C_YUI_TREEDB_TOPIC_WITH_FORM";
105
105
 
106
+ /*
107
+ * Where the reader's page size is remembered.
108
+ *
109
+ * In localStorage and not in a persistent attr: how many rows somebody
110
+ * wants in front of them is a fact about THAT browser, not about the
111
+ * yuno, and a library gclass cannot assume the host app wired the
112
+ * persistence functions. Same reasoning as `C_YUI_GOBJ_TREE_JS` and
113
+ * `C_YUI_JSON`.
114
+ *
115
+ * Keyed by the gobj name, which the host builds as `<view>?<topic>`, so
116
+ * the size is remembered PER TOPIC. That is the useful grain: a topic of
117
+ * 5891 devices and one of 22 do not want the same page.
118
+ */
119
+ const PAGE_SIZE_STORE_PREFIX = "yui_treedb_page_size:";
120
+
121
+ /************************************************************
122
+ * The size the reader left, or null when there is none.
123
+ * `true` is Tabulator's "All" and it round-trips as the boolean:
124
+ * JSON.parse gives it back as one, and a string "true" would be
125
+ * read by Tabulator as a size of NaN.
126
+ ************************************************************/
127
+ function load_page_size(gobj)
128
+ {
129
+ try {
130
+ let raw = window.localStorage.getItem(
131
+ PAGE_SIZE_STORE_PREFIX + gobj_name(gobj)
132
+ );
133
+ if(raw === null) {
134
+ return null;
135
+ }
136
+ let size = JSON.parse(raw);
137
+ if(size === true) {
138
+ return true;
139
+ }
140
+ if(typeof size === "number" && size > 0) {
141
+ return size;
142
+ }
143
+ return null;
144
+ } catch(e) {
145
+ /* localStorage unavailable (private mode) or a value somebody
146
+ * else wrote: the table opens at its default size, which is
147
+ * the same thing that happened before it was remembered. */
148
+ return null;
149
+ }
150
+ }
151
+
152
+ /************************************************************
153
+ *
154
+ ************************************************************/
155
+ function save_page_size(gobj, size)
156
+ {
157
+ try {
158
+ window.localStorage.setItem(
159
+ PAGE_SIZE_STORE_PREFIX + gobj_name(gobj),
160
+ JSON.stringify(size)
161
+ );
162
+ } catch(e) {
163
+ // localStorage unavailable (private mode) — non-fatal
164
+ }
165
+ }
166
+
106
167
  /***************************************************************
107
168
  * Data
108
169
  ***************************************************************/
@@ -168,6 +229,7 @@ let PRIVATE_DATA = {
168
229
  _pending_pages: null, // req_id -> {resolve, reject, timer}
169
230
  _page_seq: 0, // correlation id of a page request
170
231
  _page_total: null, // rows the topic HAS, from the last page answer
232
+ _page_size: null, // rows per page in effect (remembered across visits)
171
233
  $container: null,
172
234
  treedb_name: "",
173
235
  topic_name: "",
@@ -1366,6 +1428,22 @@ function create_tabulator(gobj)
1366
1428
  });
1367
1429
  }
1368
1430
 
1431
+ /* The size the reader picked last time, which outranks both defaults
1432
+ * above. Applied here, once the two paths have set theirs, so it is
1433
+ * written in ONE place -- and applied to `paginationSize` only: the
1434
+ * SELECTOR keeps offering the same sizes, and a remembered value that
1435
+ * is not one of them Tabulator adds to the list itself. */
1436
+ let remembered_size = load_page_size(gobj);
1437
+ if(remembered_size !== null && tabulator_settings.pagination) {
1438
+ tabulator_settings.paginationSize = remembered_size;
1439
+ }
1440
+ /* What the table OPENS at, remembered or default. Tabulator announces
1441
+ * `pageSizeChanged` at build time too, with the size it starts at, and
1442
+ * saving that one would freeze today's default in the reader's browser
1443
+ * for ever: a later change of the default would never reach anybody who
1444
+ * had once opened the table. Only a real change is written. */
1445
+ gobj.priv._page_size = tabulator_settings.paginationSize;
1446
+
1369
1447
  Object.assign(tabulator_settings, {
1370
1448
  index: pkey,
1371
1449
  columns: columns,
@@ -1462,14 +1540,50 @@ function create_tabulator(gobj)
1462
1540
  single_page = (max === false) || max <= 1;
1463
1541
  } else {
1464
1542
  /* Derived from the count and the page size rather than read
1465
- * from getPageMax(), which is stale in the filter path. */
1543
+ * from getPageMax(), which is stale in the filter path.
1544
+ * `true` is Tabulator's "All" and it is the SIZE itself, so
1545
+ * it has to be read as a word: compared as a number it is 1,
1546
+ * and a table showing every one of its 300 rows in a single
1547
+ * page answered that it needed 300 pages. */
1466
1548
  let size = tabulator.getPageSize();
1467
- single_page = !size || n <= size;
1549
+ single_page = (size === true) || !size || n <= size;
1468
1550
  }
1469
1551
  } catch(e) {
1470
1552
  single_page = true;
1471
1553
  }
1554
+ /* The page-size selector lives INSIDE the paginator, so hiding the
1555
+ * whole strip when everything fits took away the very control that
1556
+ * got the reader there: pick "All" on a topic of 300 rows and the
1557
+ * table becomes one page, the paginator goes, and 50 is
1558
+ * unreachable. The selector is noise only when no choice in it
1559
+ * would change anything -- when the topic is smaller than the
1560
+ * smallest size it offers. Measure that on the WHOLE topic, never
1561
+ * on the filtered count, or typing a filter that leaves 3 rows
1562
+ * takes the selector away too. */
1563
+ let total_rows;
1564
+ if(paged) {
1565
+ total_rows = total;
1566
+ } else {
1567
+ try {
1568
+ total_rows = tabulator.getDataCount();
1569
+ } catch(e) {
1570
+ total_rows = n;
1571
+ }
1572
+ }
1573
+ let min_size = 0;
1574
+ let sizes = tabulator.options? tabulator.options.paginationSizeSelector : null;
1575
+ if(Array.isArray(sizes)) {
1576
+ sizes.forEach(function(size) {
1577
+ if(typeof size === "number" && (!min_size || size < min_size)) {
1578
+ min_size = size;
1579
+ }
1580
+ });
1581
+ }
1582
+ let no_paginator = single_page &&
1583
+ (!min_size || typeof total_rows !== "number" || total_rows <= min_size);
1584
+
1472
1585
  tabulator.element.classList.toggle("yui-single-page", single_page);
1586
+ tabulator.element.classList.toggle("yui-no-paginator", no_paginator);
1473
1587
  }
1474
1588
 
1475
1589
  tabulator._ready = false;
@@ -1493,6 +1607,13 @@ function create_tabulator(gobj)
1493
1607
  * box; a filter per column made it a claim you read on every keystroke. */
1494
1608
  tabulator.on("dataProcessed", update_rowcount);
1495
1609
  tabulator.on("dataChanged", update_rowcount);
1610
+ /* The reader changed the page size. The widget has already done the
1611
+ * work -- it re-pages itself -- so what crosses the machine is the
1612
+ * DECISION, which is the half worth remembering and the half that
1613
+ * shows up in the trace. */
1614
+ tabulator.on("pageSizeChanged", function(size) {
1615
+ gobj_send_event(gobj, "EV_PAGE_SIZE_CHANGED", {size: size}, gobj);
1616
+ });
1496
1617
  /* A cell edited in place. Announced as ONE FIELD of one record — not
1497
1618
  * as the row — and that is the whole safety of it: the host writes it
1498
1619
  * with a partial update and no `autolink`, so nothing but that field
@@ -3570,6 +3691,27 @@ function ac_open_columns(gobj, event, kw, src)
3570
3691
  return 0;
3571
3692
  }
3572
3693
 
3694
+ /************************************************************
3695
+ * The reader picked another page size: remember it, so coming back to
3696
+ * this topic opens where they left it instead of at 200.
3697
+ ************************************************************/
3698
+ function ac_page_size_changed(gobj, event, kw, src)
3699
+ {
3700
+ let size = kw? kw.size : undefined;
3701
+ if(size !== true && !(typeof size === "number" && size > 0)) {
3702
+ log_error(`${gobj_short_name(gobj)}: bad page size: ${size}`);
3703
+ return -1;
3704
+ }
3705
+
3706
+ if(size === gobj.priv._page_size) {
3707
+ return 0; /* the build-time announcement, not a decision */
3708
+ }
3709
+ gobj.priv._page_size = size;
3710
+ save_page_size(gobj, size);
3711
+
3712
+ return 0;
3713
+ }
3714
+
3573
3715
  /************************************************************
3574
3716
  * Show / hide one column.
3575
3717
  ************************************************************/
@@ -3708,6 +3850,7 @@ function create_gclass(gclass_name)
3708
3850
  ["EV_PAGE_TIMEOUT", ac_page_timeout, null],
3709
3851
  ["EV_SEARCH", ac_search, null],
3710
3852
  ["EV_OPEN_COLUMNS", ac_open_columns, null],
3853
+ ["EV_PAGE_SIZE_CHANGED", ac_page_size_changed, null],
3711
3854
  ["EV_TOGGLE_COLUMN", ac_toggle_column, null],
3712
3855
  ["EV_EXPORT_TABLE", ac_export_table, null],
3713
3856
  ["EV_SHOW", ac_show, null],
@@ -3748,6 +3891,7 @@ function create_gclass(gclass_name)
3748
3891
  ["EV_REQUEST_PAGE", event_flag_t.EVF_OUTPUT_EVENT],
3749
3892
  ["EV_SEARCH", 0],
3750
3893
  ["EV_OPEN_COLUMNS", 0],
3894
+ ["EV_PAGE_SIZE_CHANGED", 0],
3751
3895
  ["EV_TOGGLE_COLUMN", 0],
3752
3896
  ["EV_EXPORT_TABLE", 0],
3753
3897
 
package/src/tabulator.css CHANGED
@@ -304,6 +304,23 @@
304
304
  color: var(--bulma-link-invert);
305
305
  }
306
306
 
307
+ /* The paginator paints its OWN colour -- tabulator_bulma.css sets
308
+ * `color: hsl(0, 0%, 21%)` on `.tabulator-paginator` itself -- so the
309
+ * footer's colour never reaches the "Rows per page" label: near-black
310
+ * text on the dark footer, legible only if you knew it was there. Say
311
+ * it on the element that sets it, and on the native <select>, which
312
+ * otherwise keeps the browser's light chrome. */
313
+ [data-theme=dark] .tabulator .tabulator-footer .tabulator-paginator,
314
+ [data-theme=dark] .tabulator .tabulator-footer .tabulator-paginator label {
315
+ color: var(--bulma-text);
316
+ }
317
+
318
+ [data-theme=dark] .tabulator .tabulator-footer .tabulator-page-size {
319
+ background-color: var(--bulma-background);
320
+ color: var(--bulma-text);
321
+ border-color: var(--bulma-border);
322
+ }
323
+
307
324
  [data-theme=dark] .tabulator-row .tabulator-cell.tabulator-frozen {
308
325
  background-color: var(--bulma-background) !important;
309
326
  }
@@ -386,5 +403,15 @@
386
403
  background-color: var(--bulma-link);
387
404
  color: var(--bulma-link-invert);
388
405
  }
406
+ /* same paginator-paints-its-own-colour rule as [data-theme=dark] */
407
+ [data-theme=system] .tabulator .tabulator-footer .tabulator-paginator,
408
+ [data-theme=system] .tabulator .tabulator-footer .tabulator-paginator label {
409
+ color: var(--bulma-text);
410
+ }
411
+ [data-theme=system] .tabulator .tabulator-footer .tabulator-page-size {
412
+ background-color: var(--bulma-background);
413
+ color: var(--bulma-text);
414
+ border-color: var(--bulma-border);
415
+ }
389
416
  }
390
417