@toclocoinc/lattice-grid 1.44.2 → 1.46.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.
Files changed (72) hide show
  1. package/README.md +1 -1
  2. package/docs/API.html +10 -0
  3. package/docs/api-detail.html +114 -1
  4. package/lattice-grid.d.ts +91 -1
  5. package/lattice-grid.esm.min.js +174 -21
  6. package/lattice-grid.min.cjs +174 -21
  7. package/lattice-grid.min.css +1 -1
  8. package/lattice-grid.min.js +174 -21
  9. package/modules/ai.esm.min.js +6 -4
  10. package/modules/ai.min.cjs +6 -4
  11. package/modules/ai.min.js +6 -4
  12. package/modules/angular.esm.min.js +2 -2
  13. package/modules/angular.min.cjs +2 -2
  14. package/modules/angular.min.js +2 -2
  15. package/modules/chart-alluvial.esm.min.js +1 -1
  16. package/modules/chart-arc.esm.min.js +1 -1
  17. package/modules/chart-bubblemap.esm.min.js +1 -1
  18. package/modules/chart-bump.esm.min.js +1 -1
  19. package/modules/chart-calendar.esm.min.js +1 -1
  20. package/modules/chart-decomposition.esm.min.js +1 -1
  21. package/modules/chart-diverging.esm.min.js +1 -1
  22. package/modules/chart-dumbbell.esm.min.js +1 -1
  23. package/modules/chart-fan.esm.min.js +1 -1
  24. package/modules/chart-hexbin.esm.min.js +1 -1
  25. package/modules/chart-hexmap.esm.min.js +1 -1
  26. package/modules/chart-icicle.esm.min.js +1 -1
  27. package/modules/chart-parallel.esm.min.js +1 -1
  28. package/modules/chart-ridgeline.esm.min.js +1 -1
  29. package/modules/chart-roc.esm.min.js +1 -1
  30. package/modules/chart-slope.esm.min.js +1 -1
  31. package/modules/chart-splom.esm.min.js +1 -1
  32. package/modules/chart-waffle.esm.min.js +1 -1
  33. package/modules/charts.esm.min.js +4 -4
  34. package/modules/charts.min.cjs +4 -4
  35. package/modules/charts.min.js +4 -4
  36. package/modules/data-router.esm.min.js +4 -4
  37. package/modules/data-router.min.cjs +4 -4
  38. package/modules/data-router.min.js +4 -4
  39. package/modules/devtools.esm.min.js +2 -2
  40. package/modules/devtools.min.cjs +2 -2
  41. package/modules/devtools.min.js +2 -2
  42. package/modules/dhtmlx-compat.esm.min.js +4 -4
  43. package/modules/dhtmlx-compat.min.cjs +4 -4
  44. package/modules/dhtmlx-compat.min.js +4 -4
  45. package/modules/gantt.esm.min.js +4 -4
  46. package/modules/gantt.min.cjs +4 -4
  47. package/modules/gantt.min.js +4 -4
  48. package/modules/htmx.esm.min.js +174 -21
  49. package/modules/htmx.min.cjs +174 -21
  50. package/modules/htmx.min.js +174 -21
  51. package/modules/kanban.esm.min.js +4 -4
  52. package/modules/kanban.min.cjs +4 -4
  53. package/modules/kanban.min.js +4 -4
  54. package/modules/kpi.esm.min.js +4 -4
  55. package/modules/kpi.min.cjs +4 -4
  56. package/modules/kpi.min.js +4 -4
  57. package/modules/mock-socket.esm.min.js +2 -2
  58. package/modules/mock-socket.min.cjs +2 -2
  59. package/modules/mock-socket.min.js +2 -2
  60. package/modules/react.esm.min.js +2 -2
  61. package/modules/react.min.cjs +2 -2
  62. package/modules/react.min.js +2 -2
  63. package/modules/svelte.esm.min.js +2 -2
  64. package/modules/svelte.min.cjs +2 -2
  65. package/modules/svelte.min.js +2 -2
  66. package/modules/vue.esm.min.js +2 -2
  67. package/modules/vue.min.cjs +2 -2
  68. package/modules/vue.min.js +2 -2
  69. package/modules/webcomponent.esm.min.js +174 -21
  70. package/modules/webcomponent.min.cjs +174 -21
  71. package/modules/webcomponent.min.js +174 -21
  72. package/package.json +1 -1
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  dependencies, no build step required. Optional adapters for React, Vue, Svelte
5
5
  and Web Components ship alongside it.
6
6
 
7
- Version 1.44.2 · [latticegrid.dev](https://www.latticegrid.dev) · TOCLOCO Inc
7
+ Version 1.46.0 · [latticegrid.dev](https://www.latticegrid.dev) · TOCLOCO Inc
8
8
 
9
9
  ---
10
10
 
package/docs/API.html CHANGED
@@ -872,6 +872,8 @@ autoInit(document); <span class="cmt">// builds every [data-lattice-grid] under
872
872
  <tr><td class="name">gridLines</td><td class="type">boolean | 'both' | 'horizontal' | 'vertical' | 'none'</td><td class="dflt">'horizontal'</td><td class="desc">Which rules are drawn between cells. Horizontal is what the grid has always drawn; vertical rules are additive. <code>'rows'</code> and <code>'columns'</code> are accepted aliases. Only the rules between data are affected, the header underline and pinned seams are structure.</td></tr>
873
873
  <tr><td class="name">cornerRadius</td><td class="type">boolean | number | string</td><td class="dflt">, </td><td class="desc">Round the grid's outer corners. <code>true</code> adopts the theme's radius, a number is pixels, a string is used as written.</td></tr>
874
874
  <tr><td class="name">stripedRows</td><td class="type">boolean</td><td class="dflt">false</td><td class="desc">Shade alternate data rows (zebra striping). Strictly opt-in, so an existing grid is unchanged on upgrade. Parity follows each row's logical index, so a stripe survives a scroll; group headings, footers and the grand total are never striped; selection and hover still win. Uses the theme's <code>--lattice-surface-alt</code>, so dark, high-contrast and terminal come for free.</td></tr>
875
+ <tr><td class="name">verticalAlign</td><td class="type">'top' | 'middle' | 'bottom'</td><td class="dflt">, </td><td class="desc">Vertical alignment of cell content within a row, as a default for every column &mdash; the vertical counterpart to the per-column <code>align</code>. A column's own <code>verticalAlign</code> (or <code>cell.verticalAlign</code>) overrides it. Omitted, the grid keeps its historical placement (centred in a fixed-height row, top in an <code>autoHeight</code> row), so an existing grid is unchanged on upgrade. Setting a value aligns every column uniformly, including auto-height rows, unless a column opts out. See <a href="api-detail.html#vertical-align">Vertical alignment</a>.</td></tr>
876
+ <tr><td class="name">scrollbars</td><td class="type">'auto' | 'always' | { x, y }</td><td class="dflt">'auto'</td><td class="desc">Keep the scroll viewport's scrollbars visible. <code>'auto'</code> is the platform's native behaviour, where overlay scrollbars fade when idle; <code>'always'</code> keeps both axes shown whether or not the pointer is over the grid. The object form <code>{ x, y }</code> pins each axis on its own, so <code>{ y: 'always' }</code> keeps the vertical bar while the horizontal one stays native. Omitted, the grid is unchanged on upgrade. See <a href="api-detail.html#scrollbars">Always-visible scrollbars</a>.</td></tr>
875
877
  <tr><td class="name">columnTagFilter</td><td class="type">boolean | { multiple, label }</td><td class="dflt">, </td><td class="desc">A bar above the headings for showing only the columns carrying a chosen tag. Draws nothing unless some column has <code>tags</code>. See <a href="api-detail.html#column-tags">Column tags</a>.</td></tr>
876
878
  <tr><td class="name">rowTemplate</td><td class="type">string | { template, cardsPerRow, maxCardWidth, gap, className, role, itemRole }</td><td class="dflt">, </td><td class="desc">Draw each row with a template instead of dividing it into columns, a card list, a feed, a search-result list. Compiles once; binds with <code>{{data.field}}</code>. <code>cardsPerRow</code> or <code>maxCardWidth</code> puts several on a line. The pipeline underneath is unchanged. See <a href="api-detail.html#cards">Cards, lists and feeds</a>.</td></tr>
877
879
  <tr><td class="name">gallery</td><td class="type">boolean | { template, tileWidth, tileHeight, cardsPerRow, gap, className, role, itemRole }</td><td class="dflt">, </td><td class="desc">Present rows as a gallery of tiles, laid out by the same 2-D virtualisation the grid runs. <code>true</code> generates a tile per row from the columns; <code>tileWidth</code> sizes them and the count across follows the container, or <code>cardsPerRow</code> fixes it. Presentation only &mdash; sort, filter, group and export are unchanged. See <a href="api-detail.html#cards">Cards, lists and feeds</a>.</td></tr>
@@ -880,6 +882,7 @@ autoInit(document); <span class="cmt">// builds every [data-lattice-grid] under
880
882
  <tr><td class="name">responsive</td><td class="type">{ maxWidth, template, rowHeight }</td><td class="dflt">, </td><td class="desc">Collapse to cards when the <em>container</em> is at or below <code>maxWidth</code> (640 by default), and return to a table above it. Sorting, filtering and export keep working. Emits <code>presentation:changed</code>. See <a href="api-detail.html#cards">Cards, lists and feeds</a>.</td></tr>
881
883
  <tr><td class="name">rowForm</td><td class="type">boolean | { mode, load, fields, title, width, trigger, timeout, container }</td><td class="dflt">, </td><td class="desc">Open a row for editing on a form. <code>mode: 'drawer'</code> (default) or <code>'dialog'</code>; without <code>load</code> the fields are the grid's own columns. A field entry is <code>{ field, label, editor, type, props, lookup }</code>: any editor, including your own. Takes double-click on the row unless <code>trigger: false</code>. A load that has not answered within <code>timeout</code> milliseconds (2000; <code>false</code> waits indefinitely) is reported as a failure with a retry. <code>container</code> builds the form in an element of your own instead of over the grid. See <a href="api-detail.html#row-form">Editing a row on a form</a>.</td></tr>
882
884
  <tr><td class="name">showColumnFunctions</td><td class="type">boolean</td><td class="dflt">true</td><td class="desc"><code>false</code> leaves each heading as its label, with no sort, filter or menu control. Those remain reachable through the API, the keyboard and the tool panel.</td></tr>
885
+ <tr><td class="name">headerControls</td><td class="type">'hover' | 'always' | 'hidden'</td><td class="dflt">'hover'</td><td class="desc">When the per-column header controls &mdash; the sort arrow, the filter funnel and the menu button &mdash; are shown, as a default for every column. <code>'hover'</code> reveals them on hover or keyboard focus (the historical behaviour); <code>'always'</code> keeps them visible; <code>'hidden'</code> draws none of them for a clean read-only heading and leaves them out of the tab order. A column's own <code>headerControls</code> overrides this default for that column. Distinct from <code>showColumnFunctions: false</code>, which also drops the furniture but keeps the functions reachable from the keyboard; <code>'hidden'</code> is the read-only choice.</td></tr>
883
886
  <tr><td class="name">significantFigures</td><td class="type">number</td><td class="dflt">, </td><td class="desc">On a unit column, render to this many significant figures rather than a fixed number of decimals, so precision is the same on every rung of the ladder. Rounding is applied before the unit is chosen. Set inside the unit configuration a data type is built from. See <a href="api-detail.html#units">Units</a>.</td></tr>
884
887
  <tr><td class="name">typeOptions</td><td class="type">object</td><td class="dflt">, </td><td class="desc">Per-column options a data type reads. <code>ratio</code> and <code>percentRate</code> use <code>{ weight }</code> to name the column their average is weighted by. See <a href="api-detail.html#aggregate-safety">Aggregate safety</a>.</td></tr>
885
888
  <tr><td class="name">highlightOnChange</td><td class="type">boolean | string | object</td><td class="desc">Flash a cell when its value changes. <code>{ colour, duration }</code>; <code>duration: 0</code> stays until cleared.</td></tr>
@@ -6977,6 +6980,8 @@ return `next ${p.mean.toFixed(1)}; r2 ${f.r2.toFixed(1)}; band ${p.upper - p.low
6977
6980
  <tr><td class="name">spec</td><td class="type">{ lower?: number; upper?: number; target?: number }</td><td class="desc">The customer's tolerance, for process capability and control charts. Declared here rather than passed to each call so the capability figures, a control chart and any rule marking an out-of-tolerance cell cannot disagree about what the tolerance is. <small>(optional)</small></td></tr>
6978
6981
  <tr><td class="name">layout</td><td class="type">ColumnLayoutSpec | number</td><td class="desc">Width, pinning and flex. A bare number is the width in pixels. <small>(optional)</small></td></tr>
6979
6982
  <tr><td class="name">header</td><td class="type">ColumnHeaderSpec | string</td><td class="desc">The header cell: its text, tooltip, menu and any header chart. <small>(optional)</small></td></tr>
6983
+ <tr><td class="name">headerControls</td><td class="type">'hover' | 'always' | 'hidden'</td><td class="desc">When this column's header controls — its sort arrow, filter funnel and menu button — are shown, overriding the grid-level `headerControls` default for this column alone (BACKLOG-0000982). `'hover'` reveals them on hover or focus, `'always'` keeps them visible, `'hidden'` draws none of them and leaves them out of the tab order. Omitted, the column follows the grid default, which is itself `'hover'`. <small>(optional)</small></td></tr>
6984
+ <tr><td class="name">verticalAlign</td><td class="type">VAlign</td><td class="desc">Vertical alignment of this column's cell content within the row (BACKLOG-0000989). Overrides the grid-level `verticalAlign` for this column alone; `top`, `middle` or `bottom`. Also accepted as `cell.verticalAlign`, the way `align` is. Omitted, the column follows the grid default. <small>(optional)</small></td></tr>
6980
6985
  <tr><td class="name">export</td><td class="type">ColumnExportSpec</td><td class="desc">How the column leaves the grid, where that differs from how it is shown. <small>(optional)</small></td></tr>
6981
6986
  <tr><td class="name">allowGroup</td><td class="type">boolean</td><td class="desc">Whether the user may group by this column from the interface. <small>(optional)</small></td></tr>
6982
6987
  <tr><td class="name">allowPivot</td><td class="type">boolean</td><td class="desc">Whether the user may pivot on it. <small>(optional)</small></td></tr>
@@ -7001,6 +7006,7 @@ return `next ${p.mean.toFixed(1)}; r2 ${f.r2.toFixed(1)}; band ${p.upper - p.low
7001
7006
  <tr><td class="name">style</td><td class="type">CellStyle | ((p: CellParams) =&gt; CellStyle)</td><td class="desc"><small>(optional)</small></td></tr>
7002
7007
  <tr><td class="name">tooltip</td><td class="type">string | ((p: CellParams) =&gt; string)</td><td class="desc"><small>(optional)</small></td></tr>
7003
7008
  <tr><td class="name">align</td><td class="type">Align</td><td class="desc"><small>(optional)</small></td></tr>
7009
+ <tr><td class="name">verticalAlign</td><td class="type">VAlign</td><td class="desc">Vertical alignment of this column's cell content, overriding the grid-level `verticalAlign` for this column alone (BACKLOG-0000989). Accepted at the top level of the column too, as `align` is. <small>(optional)</small></td></tr>
7004
7010
  <tr><td class="name">wrap</td><td class="type">boolean</td><td class="desc"><small>(optional)</small></td></tr>
7005
7011
  <tr><td class="name">autoHeight</td><td class="type">boolean</td><td class="desc"><small>(optional)</small></td></tr>
7006
7012
  <tr><td class="name">flash</td><td class="type">boolean</td><td class="desc"><small>(optional)</small></td></tr>
@@ -8220,6 +8226,8 @@ return `next ${p.mean.toFixed(1)}; r2 ${f.r2.toFixed(1)}; band ${p.upper - p.low
8220
8226
  <tr><td class="name">gridLines</td><td class="type">boolean | 'both' | 'horizontal' | 'vertical' | 'none' | 'rows' | 'columns'</td><td class="desc">Which rules are drawn between cells. `'both'` by default. The two axes are separate decisions: horizontal rules help the eye track along a row, vertical ones stop adjacent values running together. `false` or `'none'` draws neither. Only the rules *between data* are affected, the header's underline, the pinned seams and the totals separator are structure, not grid lines. <small>(optional)</small></td></tr>
8221
8227
  <tr><td class="name">cornerRadius</td><td class="type">boolean | number | string</td><td class="desc">Round the grid's outer corners. Square by default. `true` adopts the theme's own radius; a number is pixels; a string is used as written, so a host can pass its own token or a relative unit. <small>(optional)</small></td></tr>
8222
8228
  <tr><td class="name">stripedRows</td><td class="type">boolean</td><td class="desc">Shade alternate data rows (zebra striping). Off by default, and strictly opt-in: an existing grid must look exactly the same on upgrade. When `true`, every other data row takes the theme's `--lattice-surface-alt` background, which every palette already defines, so dark, high-contrast and terminal stripe correctly without extra work. Parity follows the row's *logical* index, not its position in the DOM, so a row keeps its stripe across a scroll even though the rows are recycled. Structural rows — group headings, group footers and the grand total — are never striped, and both selection and hover still win over the stripe. <small>(optional)</small></td></tr>
8229
+ <tr><td class="name">verticalAlign</td><td class="type">VAlign</td><td class="desc">Vertical alignment of cell content within a row, as a default for every column (BACKLOG-0000989). `top`, `middle` or `bottom`; a column's own `verticalAlign` overrides it for that column. The horizontal counterpart is the per-column `align`. Omitted, the grid keeps its historical placement — content centred in a fixed-height row and top-aligned in an `autoHeight` row — so an existing grid is unchanged on upgrade. Setting a value aligns every column uniformly, including `autoHeight` rows, unless a column opts out. <small>(optional)</small></td></tr>
8230
+ <tr><td class="name">scrollbars</td><td class="type">ScrollbarMode | { x?: ScrollbarMode; y?: ScrollbarMode }</td><td class="desc">Keep the scroll viewport's scrollbars visible (BACKLOG-0000990). `'auto'` (the default) is the platform's native behaviour, where overlay scrollbars fade when idle. `'always'` keeps both axes shown whether or not the pointer is over the grid. The object form controls each axis on its own — `{ y: 'always' }` pins the vertical bar while the horizontal one stays native. Omitted, the grid is unchanged on upgrade. <small>(optional)</small></td></tr>
8223
8231
  <tr><td class="name">columnTagFilter</td><td class="type">boolean | { multiple?: boolean; label?: string }</td><td class="desc">Show a bar above the column headings for filtering columns by tag. Off by default, and it draws nothing unless some column carries a `tags` entry. `multiple: true` lets more than one tag be chosen at once. Only tagged columns are ever hidden, so an untagged account or total column stays visible whatever is selected. <small>(optional)</small></td></tr>
8224
8232
  <tr><td class="name">anomalySummary</td><td class="type">boolean | { column?: string; label?: string }</td><td class="desc">Show a small chip in the grid chrome that reads how many rows an anomaly shadow column has flagged, and filters the grid to exactly those when it is clicked (BACKLOG-0000799). Off by default, and it draws nothing unless a column declares a `shadow: { kind: 'anomalyFlag' }`. The count and the filter both read that one shadow column, so the number on the chip is the number of rows the click reveals. `column` names the base column to summarise when more than one anomaly-flag shadow is present; `label` overrides the chip's wording. <small>(optional)</small></td></tr>
8225
8233
  <tr><td class="name">typeOptions</td><td class="type">Record&lt;string, {</td><td class="desc">Per-column options a data type reads. `ratio` and `percentRate` use `{ weight }` to name the column their average is weighted by. A unit type reads `{ significantFigures }` to render to a fixed precision rather than a fixed number of decimals. <small>(optional)</small></td></tr>
@@ -8231,6 +8239,7 @@ return `next ${p.mean.toFixed(1)}; r2 ${f.r2.toFixed(1)}; band ${p.upper - p.low
8231
8239
  <tr><td class="name">responsive</td><td class="type">{</td><td class="desc">Present rows as cards when the grid's container is too narrow to be a table honestly, a phone, or a narrow panel on a wide screen. Measured on the container, not the viewport, so a grid in a sidebar collapses and a grid filling a small tablet does not. Sorting, filtering and export continue to work; the tool panel is where they live when there are no column headings to click. Emits `presentation:changed`. <small>(optional)</small></td></tr>
8232
8240
  <tr><td class="name">rowForm</td><td class="type">boolean | {</td><td class="desc"><small>(optional)</small></td></tr>
8233
8241
  <tr><td class="name">showColumnFunctions</td><td class="type">boolean</td><td class="desc">Draw the sort, filter and menu controls in the column headings. `true` by default. `false` leaves each heading as its label alone, which is what a dense grid wants: three affordances take roughly fifty pixels, and on an eighty-pixel column that leaves the heading nothing and the label disappears entirely. Only the furniture goes. Sorting, filtering and the column menu are still reachable through the API, the keyboard and the tool panel. <small>(optional)</small></td></tr>
8242
+ <tr><td class="name">headerControls</td><td class="type">'hover' | 'always' | 'hidden'</td><td class="desc">When the per-column header controls — the sort arrow, the filter funnel and the menu button — are shown, as a default for every column (BACKLOG-0000982). - `'hover'` (the default) reveals them when the heading is hovered or a keyboard user focuses into it, which is the historical behaviour: a wide header does not read as a row of identical icons. - `'always'` keeps them visible unconditionally, for a grid where the controls are the point and the discoverability of hover is not wanted. - `'hidden'` draws none of them, for a clean read-only heading; they leave the tab order with the elements that carried them. An active filter and a live sort are still reflected by the heading's state attributes, but no control furniture is built. A column's own `headerControls` overrides this default for that column. Distinct from `showColumnFunctions: false`, which also drops the furniture but keeps sorting, filtering and the menu reachable from the keyboard; `'hidden'` is the read-only choice that removes them outright. <small>(optional)</small></td></tr>
8234
8243
  <tr><td class="name">rowHeight</td><td class="type">number | ((row: Row) =&gt; number)</td><td class="desc">Row height in pixels, or a function of the row. A function makes the grid measure rather than assume, which costs a pass over what is on screen: worth it for wrapped text, wasteful for a uniform grid. <small>(optional)</small></td></tr>
8235
8244
  <tr><td class="name">title</td><td class="type">string</td><td class="desc">A caption for the grid, drawn above the column headings. Inside the grid rather than an element the host places above it: a title outside does not scroll with the grid, is not in the region a screen reader announces, and is left behind by image capture and print. <small>(optional)</small></td></tr>
8236
8245
  <tr><td class="name">showHeader</td><td class="type">boolean</td><td class="desc">Draw the column headings at all. `true` by default. `false` removes the row, and removes it from the accessibility tree rather than only from view, a heading a screen reader still announces is invisible, not hidden. What a small dashboard tile wants when its `title` already says what the panel is. Distinct from `showColumnFunctions`, which keeps the headings and drops only the sort, filter and menu controls inside them. <small>(optional)</small></td></tr>
@@ -9328,6 +9337,7 @@ return `next ${p.mean.toFixed(1)}; r2 ${f.r2.toFixed(1)}; band ${p.upper - p.low
9328
9337
  <tr><td class="name">dataType</td><td class="type">DataType</td><td class="desc"></td></tr>
9329
9338
  <tr><td class="name">nullable</td><td class="type">boolean</td><td class="desc"></td></tr>
9330
9339
  <tr><td class="name">align</td><td class="type">Align</td><td class="desc"></td></tr>
9340
+ <tr><td class="name">verticalAlign</td><td class="type">VAlign</td><td class="desc">The resolved vertical alignment (BACKLOG-0000989), or `undefined` when neither the column nor the grid set one — in which case the cell keeps the grid's historical vertical placement (centred, or top for `autoHeight`). <small>(optional)</small></td></tr>
9331
9341
  <tr><td class="name">value</td><td class="type">Required&lt;Pick&lt;ColumnValueSpec, 'pure'&gt;&gt; &amp; ColumnValueSpec</td><td class="desc"></td></tr>
9332
9342
  <tr><td class="name">cell</td><td class="type">ColumnCellSpec</td><td class="desc"></td></tr>
9333
9343
  <tr><td class="name">edit</td><td class="type">ColumnEditSpec</td><td class="desc"></td></tr>
@@ -437,7 +437,7 @@
437
437
  <div class="shell">
438
438
  <aside class="rail">
439
439
  <p class="rail__brand">Lattice Grid</p>
440
- <p class="rail__sub">Developer guide · v1.44.2</p>
440
+ <p class="rail__sub">Developer guide · v1.46.0</p>
441
441
  <nav>
442
442
  <div class="rail__group">
443
443
  <span class="rail__label">Start here</span>
@@ -2194,6 +2194,49 @@ grid.destroy();
2194
2194
  reporting a value it is unsure of.
2195
2195
  </p>
2196
2196
 
2197
+ <h3 id="header-controls">headerControls</h3>
2198
+ <p class="lead-in">
2199
+ The per-column header controls &mdash; the sort arrow, the filter funnel and the menu button
2200
+ &mdash; appear on hover by default, which keeps a wide header from reading as a row of identical
2201
+ icons. <code>headerControls</code> makes that a mode: <code>'hover'</code> (the default,
2202
+ unchanged), <code>'always'</code> to keep them visible, and <code>'hidden'</code> for a clean
2203
+ read-only heading that draws none of them and leaves them out of the tab order. It is a
2204
+ grid-level default; a column's own <code>headerControls</code> overrides it for that column.
2205
+ </p>
2206
+ <p class="lead-in">
2207
+ Each leaf heading carries the resolved mode as <code>data-controls</code>, and the theme keys
2208
+ the reveal off it, so <code>'always'</code> shows the controls without a hover and
2209
+ <code>'hidden'</code> removes them from the layout. This differs from
2210
+ <code>showColumnFunctions: false</code>, which also drops the furniture but keeps sorting,
2211
+ filtering and the menu reachable from the keyboard; <code>'hidden'</code> is the read-only
2212
+ choice that takes them away outright (BACKLOG-0000982).
2213
+ </p>
2214
+ <div class="example">
2215
+ <p class="example__label">A grid that hides its controls by default, with one column that keeps them</p>
2216
+ <pre data-run="js" data-expect="1" data-covers="config:headerControls"><code><span class="kw">const</span> { createHeadlessGrid } = <span class="kw">await</span> import('../packages/core/src/index.js');
2217
+
2218
+ <span class="kw">const</span> grid = createHeadlessGrid({
2219
+ headerControls: 'hidden', <span class="cmt">// the read-only default for every column</span>
2220
+ columns: [
2221
+ { field: 'name' }, <span class="cmt">// follows the default: hidden</span>
2222
+ { field: 'region', headerControls: 'always' }, <span class="cmt">// this column overrides the default</span>
2223
+ { field: 'qty', type: 'number' }, <span class="cmt">// follows the default: hidden</span>
2224
+ ],
2225
+ rows: [{ name: 'a', region: 'x', qty: 1 }],
2226
+ rowKey: 'name',
2227
+ });
2228
+
2229
+ <span class="cmt">// Resolve each column the way the header renderer does: its own value wins,</span>
2230
+ <span class="cmt">// then the grid default. Only the overriding column shows its controls.</span>
2231
+ <span class="kw">function</span> shown(id) {
2232
+ <span class="kw">const</span> own = grid.columns.get(id).def.headerControls;
2233
+ <span class="kw">return</span> (own || 'hidden') !== 'hidden';
2234
+ }
2235
+ <span class="kw">const</span> visible = ['name', 'region', 'qty'].filter(shown).length;
2236
+ grid.destroy();
2237
+ <span class="kw">return</span> visible;</code></pre>
2238
+ </div>
2239
+
2197
2240
  <h3 id="show-total-in-header">showTotalInHeader</h3>
2198
2241
  <p class="lead-in">
2199
2242
  Under grouping or pivot, a totalled column's cells hold an aggregate rather than a row's own
@@ -3469,6 +3512,76 @@ grid.edit.deleteRows();</code></pre>
3469
3512
  both selection and hover still win over it.
3470
3513
  </p>
3471
3514
 
3515
+ <h2 id="vertical-align">Vertical alignment</h2>
3516
+ <p class="lead-in">
3517
+ <code>verticalAlign</code> is the vertical counterpart to the per-column <code>align</code>:
3518
+ where <code>align</code> places cell content across the column (<code>start</code>,
3519
+ <code>center</code>, <code>end</code>), <code>verticalAlign</code> places it down the row &mdash;
3520
+ <code>'top'</code>, <code>'middle'</code> or <code>'bottom'</code>. It is a grid-level default
3521
+ with a per-column override: set it on the grid to align every column, and a column's own
3522
+ <code>verticalAlign</code> (or <code>cell.verticalAlign</code>) wins for that column alone.
3523
+ <code>center</code> and <code>centre</code> are accepted as synonyms for <code>middle</code>,
3524
+ the same leniency <code>align</code> gives the horizontal names.
3525
+ </p>
3526
+ <p class="lead-in">
3527
+ Omitted, the grid keeps the placement it has always had &mdash; content centred in a
3528
+ fixed-height row and top-aligned in an <code>autoHeight</code> row &mdash; so a grid that never
3529
+ mentions it is unchanged on upgrade. Where it earns its keep is a tall or <code>autoHeight</code>
3530
+ grid: a wrapped-text column can sit at the top while its single-line neighbours are
3531
+ <code>middle</code>, rather than every value floating in the middle of a tall row. The value
3532
+ beats the auto-height rule, so a column asked to sit <code>middle</code> does so even when the
3533
+ row is stretched to fit a wrapped sibling (BACKLOG-0000989).
3534
+ </p>
3535
+
3536
+ <div class="example">
3537
+ <p class="example__label">A grid default, with one column overriding it</p>
3538
+ <pre data-run="js" data-expect="middle, bottom" data-covers="config:verticalAlign"><code><span class="kw">const</span> { createHeadlessGrid } = <span class="kw">await</span> import('../packages/core/src/index.js');
3539
+
3540
+ <span class="kw">const</span> grid = createHeadlessGrid({
3541
+ verticalAlign: <span class="str">'middle'</span>, <span class="cmt">// the default for every column</span>
3542
+ columns: [
3543
+ { field: <span class="str">'name'</span> }, <span class="cmt">// follows the default: middle</span>
3544
+ { field: <span class="str">'note'</span>, verticalAlign: <span class="str">'bottom'</span> }, <span class="cmt">// this column overrides it</span>
3545
+ ],
3546
+ rows: [{ name: <span class="str">'a'</span>, note: <span class="str">'b'</span> }],
3547
+ rowKey: <span class="str">'name'</span>,
3548
+ });
3549
+
3550
+ <span class="cmt">// The resolved value the renderer reads for each column.</span>
3551
+ <span class="kw">const</span> a = grid.columns.get(<span class="str">'name'</span>).verticalAlign;
3552
+ <span class="kw">const</span> b = grid.columns.get(<span class="str">'note'</span>).verticalAlign;
3553
+ grid.destroy();
3554
+ <span class="kw">return</span> `${a}, ${b}`;</code></pre>
3555
+ </div>
3556
+
3557
+ <h2 id="scrollbars">Always-visible scrollbars</h2>
3558
+ <p class="lead-in">
3559
+ Native scrollbars are overlay bars on most platforms now: they fade away when the pointer is
3560
+ idle, which reads as a cleaner surface but hides the affordance &mdash; a touchpad user has no
3561
+ standing sign that a grid scrolls at all. <code>scrollbars: 'always'</code> keeps them shown
3562
+ whether or not the pointer is over the grid; <code>'auto'</code> (the default) leaves the
3563
+ platform's own behaviour alone, so an existing grid is unchanged on upgrade.
3564
+ </p>
3565
+ <p class="lead-in">
3566
+ The two axes are separate decisions, so the object form controls each on its own:
3567
+ <code>{ y: 'always' }</code> pins the vertical bar while the horizontal one stays native, and
3568
+ <code>{ x: 'always', y: 'always' }</code> is the same as the bare <code>'always'</code>. Under
3569
+ the hood the pinned axis switches to <code>overflow: scroll</code> so its track is present even
3570
+ when the content fits, and the scrollbar is styled as a classic, always-drawn bar rather than a
3571
+ fading overlay (BACKLOG-0000990). The same viewport also suppresses the overscroll rubber-band
3572
+ bounce, so a synchronised grid does not spring at its scroll boundary (BACKLOG-0000991).
3573
+ </p>
3574
+
3575
+ <div class="example">
3576
+ <p class="example__label">Pinning both axes, and one axis at a time</p>
3577
+ <pre data-run="js" data-expect="always: always/always; y-only: auto/always" data-covers="config:scrollbars"><code><span class="cmt">// The renderer resolves `scrollbars` to a per-axis mode it stamps on the root.</span>
3578
+ <span class="kw">const</span> { resolveScrollbars } = <span class="kw">await</span> import('../packages/dom/src/renderer/renderer.js');
3579
+
3580
+ <span class="kw">const</span> both = resolveScrollbars(<span class="str">'always'</span>); <span class="cmt">// pins both axes</span>
3581
+ <span class="kw">const</span> onlyY = resolveScrollbars({ y: <span class="str">'always'</span> }); <span class="cmt">// pins the vertical bar only</span>
3582
+ <span class="kw">return</span> `always: ${both.x}/${both.y}; y-only: ${onlyY.x}/${onlyY.y}`;</code></pre>
3583
+ </div>
3584
+
3472
3585
  <h2 id="cards">Cards, lists and feeds</h2>
3473
3586
  <p class="lead-in">
3474
3587
  <code>rowTemplate</code> draws each row with a layout of your own instead of dividing it into
package/lattice-grid.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Lattice Grid 1.44.2, type declarations
2
+ * Lattice Grid 1.46.0, type declarations
3
3
  * Copyright (c) 2026 TOCLOCO Inc. All rights reserved.
4
4
  * https://latticegrid.dev
5
5
  */
@@ -62,6 +62,24 @@ export type TypeName =
62
62
  * `center`.
63
63
  */
64
64
  export type Align = 'start' | 'center' | 'end' | 'left' | 'right' | 'centre';
65
+ /**
66
+ * Vertical alignment of a cell's content within its row (BACKLOG-0000989).
67
+ *
68
+ * The vertical counterpart to {@link Align}. `top` sits the content at the top
69
+ * of the row, `middle` centres it and `bottom` drops it to the bottom. It is
70
+ * most visible on tall or `autoHeight` rows, where a wrapped-text column can be
71
+ * `top` while its single-line neighbours are `middle`.
72
+ */
73
+ export type VAlign = 'top' | 'middle' | 'bottom';
74
+ /**
75
+ * When the grid's scroll viewport keeps its scrollbars visible
76
+ * (BACKLOG-0000990).
77
+ *
78
+ * `auto` is the platform's native behaviour — overlay scrollbars fade away when
79
+ * idle. `always` keeps the bar shown whether or not the pointer is over the
80
+ * grid, so the affordance never disappears on a touchpad or an overlay OS.
81
+ */
82
+ export type ScrollbarMode = 'auto' | 'always';
65
83
  /**
66
84
  * A named preset, or a raw scale where 1 is `standard`. Row heights are
67
85
  * 23.8 / 28 / 42 / 56px for the four presets; a number scales 28px.
@@ -507,6 +525,12 @@ export interface ColumnCellSpec {
507
525
  style?: CellStyle | ((p: CellParams) => CellStyle);
508
526
  tooltip?: string | ((p: CellParams) => string);
509
527
  align?: Align;
528
+ /**
529
+ * Vertical alignment of this column's cell content, overriding the grid-level
530
+ * `verticalAlign` for this column alone (BACKLOG-0000989). Accepted at the
531
+ * top level of the column too, as `align` is.
532
+ */
533
+ verticalAlign?: VAlign;
510
534
  wrap?: boolean;
511
535
  autoHeight?: boolean;
512
536
  flash?: boolean;
@@ -823,6 +847,22 @@ export interface Column {
823
847
  layout?: ColumnLayoutSpec | number;
824
848
  /** The header cell: its text, tooltip, menu and any header chart. */
825
849
  header?: ColumnHeaderSpec | string;
850
+ /**
851
+ * When this column's header controls — its sort arrow, filter funnel and menu
852
+ * button — are shown, overriding the grid-level `headerControls` default for
853
+ * this column alone (BACKLOG-0000982). `'hover'` reveals them on hover or
854
+ * focus, `'always'` keeps them visible, `'hidden'` draws none of them and
855
+ * leaves them out of the tab order. Omitted, the column follows the grid
856
+ * default, which is itself `'hover'`.
857
+ */
858
+ headerControls?: 'hover' | 'always' | 'hidden';
859
+ /**
860
+ * Vertical alignment of this column's cell content within the row
861
+ * (BACKLOG-0000989). Overrides the grid-level `verticalAlign` for this column
862
+ * alone; `top`, `middle` or `bottom`. Also accepted as `cell.verticalAlign`,
863
+ * the way `align` is. Omitted, the column follows the grid default.
864
+ */
865
+ verticalAlign?: VAlign;
826
866
  /** How the column leaves the grid, where that differs from how it is shown. */
827
867
  export?: ColumnExportSpec;
828
868
  /** Whether the user may group by this column from the interface. */
@@ -857,6 +897,12 @@ export interface ResolvedColumn {
857
897
  dataType: DataType;
858
898
  nullable: boolean;
859
899
  align: Align;
900
+ /**
901
+ * The resolved vertical alignment (BACKLOG-0000989), or `undefined` when
902
+ * neither the column nor the grid set one — in which case the cell keeps the
903
+ * grid's historical vertical placement (centred, or top for `autoHeight`).
904
+ */
905
+ verticalAlign?: VAlign;
860
906
  value: Required<Pick<ColumnValueSpec, 'pure'>> & ColumnValueSpec;
861
907
  cell: ColumnCellSpec;
862
908
  edit: ColumnEditSpec;
@@ -1466,6 +1512,30 @@ export interface GridConfig {
1466
1512
  */
1467
1513
  stripedRows?: boolean;
1468
1514
 
1515
+ /**
1516
+ * Vertical alignment of cell content within a row, as a default for every
1517
+ * column (BACKLOG-0000989). `top`, `middle` or `bottom`; a column's own
1518
+ * `verticalAlign` overrides it for that column.
1519
+ *
1520
+ * The horizontal counterpart is the per-column `align`. Omitted, the grid
1521
+ * keeps its historical placement — content centred in a fixed-height row and
1522
+ * top-aligned in an `autoHeight` row — so an existing grid is unchanged on
1523
+ * upgrade. Setting a value aligns every column uniformly, including
1524
+ * `autoHeight` rows, unless a column opts out.
1525
+ */
1526
+ verticalAlign?: VAlign;
1527
+
1528
+ /**
1529
+ * Keep the scroll viewport's scrollbars visible (BACKLOG-0000990).
1530
+ *
1531
+ * `'auto'` (the default) is the platform's native behaviour, where overlay
1532
+ * scrollbars fade when idle. `'always'` keeps both axes shown whether or not
1533
+ * the pointer is over the grid. The object form controls each axis on its
1534
+ * own — `{ y: 'always' }` pins the vertical bar while the horizontal one
1535
+ * stays native. Omitted, the grid is unchanged on upgrade.
1536
+ */
1537
+ scrollbars?: ScrollbarMode | { x?: ScrollbarMode; y?: ScrollbarMode };
1538
+
1469
1539
  /**
1470
1540
  * Show a bar above the column headings for filtering columns by tag.
1471
1541
  *
@@ -1733,6 +1803,26 @@ export interface GridConfig {
1733
1803
  * reachable through the API, the keyboard and the tool panel.
1734
1804
  */
1735
1805
  showColumnFunctions?: boolean;
1806
+ /**
1807
+ * When the per-column header controls — the sort arrow, the filter funnel and
1808
+ * the menu button — are shown, as a default for every column (BACKLOG-0000982).
1809
+ *
1810
+ * - `'hover'` (the default) reveals them when the heading is hovered or a
1811
+ * keyboard user focuses into it, which is the historical behaviour: a wide
1812
+ * header does not read as a row of identical icons.
1813
+ * - `'always'` keeps them visible unconditionally, for a grid where the
1814
+ * controls are the point and the discoverability of hover is not wanted.
1815
+ * - `'hidden'` draws none of them, for a clean read-only heading; they leave
1816
+ * the tab order with the elements that carried them. An active filter and a
1817
+ * live sort are still reflected by the heading's state attributes, but no
1818
+ * control furniture is built.
1819
+ *
1820
+ * A column's own `headerControls` overrides this default for that column.
1821
+ * Distinct from `showColumnFunctions: false`, which also drops the furniture
1822
+ * but keeps sorting, filtering and the menu reachable from the keyboard;
1823
+ * `'hidden'` is the read-only choice that removes them outright.
1824
+ */
1825
+ headerControls?: 'hover' | 'always' | 'hidden';
1736
1826
  /**
1737
1827
  * Row height in pixels, or a function of the row. A function makes the
1738
1828
  * grid measure rather than assume, which costs a pass over what is on