@yuneta/gobj-ui 7.23.49 → 7.23.50

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
@@ -23841,12 +23841,26 @@ function create_tabulator(gobj) {
23841
23841
  single_page = max === false || max <= 1;
23842
23842
  } else {
23843
23843
  let size = tabulator.getPageSize();
23844
- single_page = !size || n <= size;
23844
+ single_page = size === true || !size || n <= size;
23845
23845
  }
23846
23846
  } catch (e) {
23847
23847
  single_page = true;
23848
23848
  }
23849
+ let total_rows;
23850
+ if (paged) total_rows = total;
23851
+ else try {
23852
+ total_rows = tabulator.getDataCount();
23853
+ } catch (e) {
23854
+ total_rows = n;
23855
+ }
23856
+ let min_size = 0;
23857
+ let sizes = tabulator.options ? tabulator.options.paginationSizeSelector : null;
23858
+ if (Array.isArray(sizes)) sizes.forEach(function(size) {
23859
+ if (typeof size === "number" && (!min_size || size < min_size)) min_size = size;
23860
+ });
23861
+ let no_paginator = single_page && (!min_size || typeof total_rows !== "number" || total_rows <= min_size);
23849
23862
  tabulator.element.classList.toggle("yui-single-page", single_page);
23863
+ tabulator.element.classList.toggle("yui-no-paginator", no_paginator);
23850
23864
  }
23851
23865
  tabulator._ready = false;
23852
23866
  tabulator.on("tableBuilt", function() {
@@ -23836,12 +23836,26 @@ function create_tabulator(gobj) {
23836
23836
  single_page = max === false || max <= 1;
23837
23837
  } else {
23838
23838
  let size = tabulator.getPageSize();
23839
- single_page = !size || n <= size;
23839
+ single_page = size === true || !size || n <= size;
23840
23840
  }
23841
23841
  } catch (e) {
23842
23842
  single_page = true;
23843
23843
  }
23844
+ let total_rows;
23845
+ if (paged) total_rows = total;
23846
+ else try {
23847
+ total_rows = tabulator.getDataCount();
23848
+ } catch (e) {
23849
+ total_rows = n;
23850
+ }
23851
+ let min_size = 0;
23852
+ let sizes = tabulator.options ? tabulator.options.paginationSizeSelector : null;
23853
+ if (Array.isArray(sizes)) sizes.forEach(function(size) {
23854
+ if (typeof size === "number" && (!min_size || size < min_size)) min_size = size;
23855
+ });
23856
+ let no_paginator = single_page && (!min_size || typeof total_rows !== "number" || total_rows <= min_size);
23844
23857
  tabulator.element.classList.toggle("yui-single-page", single_page);
23858
+ tabulator.element.classList.toggle("yui-no-paginator", no_paginator);
23845
23859
  }
23846
23860
  tabulator._ready = false;
23847
23861
  tabulator.on("tableBuilt", function() {
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.50",
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
 
@@ -1462,14 +1462,50 @@ function create_tabulator(gobj)
1462
1462
  single_page = (max === false) || max <= 1;
1463
1463
  } else {
1464
1464
  /* Derived from the count and the page size rather than read
1465
- * from getPageMax(), which is stale in the filter path. */
1465
+ * from getPageMax(), which is stale in the filter path.
1466
+ * `true` is Tabulator's "All" and it is the SIZE itself, so
1467
+ * it has to be read as a word: compared as a number it is 1,
1468
+ * and a table showing every one of its 300 rows in a single
1469
+ * page answered that it needed 300 pages. */
1466
1470
  let size = tabulator.getPageSize();
1467
- single_page = !size || n <= size;
1471
+ single_page = (size === true) || !size || n <= size;
1468
1472
  }
1469
1473
  } catch(e) {
1470
1474
  single_page = true;
1471
1475
  }
1476
+ /* The page-size selector lives INSIDE the paginator, so hiding the
1477
+ * whole strip when everything fits took away the very control that
1478
+ * got the reader there: pick "All" on a topic of 300 rows and the
1479
+ * table becomes one page, the paginator goes, and 50 is
1480
+ * unreachable. The selector is noise only when no choice in it
1481
+ * would change anything -- when the topic is smaller than the
1482
+ * smallest size it offers. Measure that on the WHOLE topic, never
1483
+ * on the filtered count, or typing a filter that leaves 3 rows
1484
+ * takes the selector away too. */
1485
+ let total_rows;
1486
+ if(paged) {
1487
+ total_rows = total;
1488
+ } else {
1489
+ try {
1490
+ total_rows = tabulator.getDataCount();
1491
+ } catch(e) {
1492
+ total_rows = n;
1493
+ }
1494
+ }
1495
+ let min_size = 0;
1496
+ let sizes = tabulator.options? tabulator.options.paginationSizeSelector : null;
1497
+ if(Array.isArray(sizes)) {
1498
+ sizes.forEach(function(size) {
1499
+ if(typeof size === "number" && (!min_size || size < min_size)) {
1500
+ min_size = size;
1501
+ }
1502
+ });
1503
+ }
1504
+ let no_paginator = single_page &&
1505
+ (!min_size || typeof total_rows !== "number" || total_rows <= min_size);
1506
+
1472
1507
  tabulator.element.classList.toggle("yui-single-page", single_page);
1508
+ tabulator.element.classList.toggle("yui-no-paginator", no_paginator);
1473
1509
  }
1474
1510
 
1475
1511
  tabulator._ready = false;