@jielga/tmdatagrid 2.0.0-beta.0 → 2.0.0-beta.2

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 (52) hide show
  1. package/README.md +32 -34
  2. package/dist/index.d.ts +164 -102
  3. package/dist/index.js +2060 -1705
  4. package/dist/index.js.map +1 -1
  5. package/dist/styles.css +1 -1
  6. package/package.json +1 -1
  7. package/skills/appearance/SKILL.md +37 -34
  8. package/skills/cell-selection/SKILL.md +19 -19
  9. package/skills/columns/SKILL.md +9 -9
  10. package/skills/data/SKILL.md +53 -53
  11. package/skills/editing/SKILL.md +152 -311
  12. package/skills/editing/references/common-mistakes.md +177 -0
  13. package/skills/editing/references/editing-api.md +56 -28
  14. package/skills/editing/references/editors-and-validation.md +26 -19
  15. package/skills/filtering/SKILL.md +51 -50
  16. package/skills/getting-started/SKILL.md +10 -10
  17. package/skills/grouping/SKILL.md +41 -43
  18. package/skills/options/SKILL.md +4 -4
  19. package/skills/rows/SKILL.md +59 -59
  20. package/skills/rows/references/rows-api.md +7 -7
  21. package/skills/server-side/SKILL.md +1 -1
  22. package/skills/testing/SKILL.md +11 -10
  23. package/src/tmdatagrid/components/TMDataGrid.module.css +13 -1
  24. package/src/tmdatagrid/components/TMDataGridCellEditor.tsx +20 -8
  25. package/src/tmdatagrid/components/TMDataGridColumnsPanel.tsx +29 -18
  26. package/src/tmdatagrid/components/TMDataGridDetailsColumn.tsx +5 -8
  27. package/src/tmdatagrid/components/TMDataGridEditActions.tsx +2 -2
  28. package/src/tmdatagrid/components/TMDataGridEditColumn.tsx +246 -86
  29. package/src/tmdatagrid/components/TMDataGridEntryRows.tsx +176 -77
  30. package/src/tmdatagrid/components/TMDataGridHeaderCell.tsx +11 -6
  31. package/src/tmdatagrid/components/TMDataGridSelectColumn.tsx +9 -5
  32. package/src/tmdatagrid/components/TMDataGridTable.module.css +47 -3
  33. package/src/tmdatagrid/components/TMDataGridTable.tsx +101 -40
  34. package/src/tmdatagrid/components/editors/TMDataGridNumberEditor.tsx +1 -1
  35. package/src/tmdatagrid/components/icons.ts +1 -0
  36. package/src/tmdatagrid/core/autosize.ts +30 -6
  37. package/src/tmdatagrid/core/capabilities.ts +15 -5
  38. package/src/tmdatagrid/core/cellExport.ts +6 -7
  39. package/src/tmdatagrid/core/cellNavigation.ts +2 -2
  40. package/src/tmdatagrid/core/cellRange.ts +6 -6
  41. package/src/tmdatagrid/core/columnOrdering.ts +30 -1
  42. package/src/tmdatagrid/core/columnUtils.ts +14 -0
  43. package/src/tmdatagrid/core/draftCellContext.ts +68 -0
  44. package/src/tmdatagrid/core/editEngine.ts +111 -37
  45. package/src/tmdatagrid/core/filterOperators.ts +6 -6
  46. package/src/tmdatagrid/core/labels.ts +13 -1
  47. package/src/tmdatagrid/core/labelsSv.ts +4 -0
  48. package/src/tmdatagrid/core/matchHighlight.ts +3 -3
  49. package/src/tmdatagrid/core/persistence.ts +3 -3
  50. package/src/tmdatagrid/core/rowSelection.ts +3 -3
  51. package/src/tmdatagrid/index.ts +3 -1
  52. package/src/tmdatagrid/useTMDataGrid.tsx +141 -99
package/dist/styles.css CHANGED
@@ -1,2 +1,2 @@
1
- ._root_1e2oi_8[data-size=xs]{--dg-row-height:34px;--dg-header-height:32px;--dg-font-size:var(--mantine-font-size-xs);--dg-padding:6px}._root_1e2oi_8[data-size=sm]{--dg-row-height:42px;--dg-header-height:38px;--dg-font-size:var(--mantine-font-size-sm);--dg-padding:8px}._root_1e2oi_8[data-size=md]{--dg-row-height:52px;--dg-header-height:44px;--dg-font-size:var(--mantine-font-size-sm);--dg-padding:10px}._root_1e2oi_8[data-size=lg]{--dg-row-height:62px;--dg-header-height:52px;--dg-font-size:var(--mantine-font-size-md);--dg-padding:14px}._root_1e2oi_8[data-size=xl]{--dg-row-height:72px;--dg-header-height:60px;--dg-font-size:var(--mantine-font-size-lg);--dg-padding:18px}._root_1e2oi_8{--dg-row-height:52px;--dg-header-height:44px;--dg-font-size:var(--mantine-font-size-sm);--dg-padding:10px;--dg-row-selected-bg:var(--mantine-primary-color-light);--dg-row-highlight-bg:var(--mantine-primary-color-light-hover);--dg-row-group-bg:var(--mantine-color-body);--dg-row-striped-bg:color-mix(in srgb, var(--mantine-color-text) 3.5%, var(--mantine-color-body));--dg-z-pinned-cell:2;--dg-z-focused-cell:3;--dg-z-pinned-row:4;--dg-z-pinned-row-pinned-cell:5;--dg-z-summary-row:4;--dg-z-summary-pinned-cell:5;--dg-z-header:6;--dg-z-header-pinned-cell:7;text-align:left;border:1px solid var(--mantine-color-default-border);border-radius:var(--mantine-radius-md);background-color:var(--mantine-color-body);flex-direction:column;min-height:0;display:flex;overflow:hidden}._columnsPanel_1492o_1{flex-direction:column;width:280px;display:flex}._columnsPanelSearch_1492o_7{padding:var(--mantine-spacing-sm)}._columnsPanelList_1492o_11{gap:var(--mantine-spacing-sm);padding:0 var(--mantine-spacing-sm) var(--mantine-spacing-sm);flex-direction:column;display:flex}._columnsPanelFooter_1492o_18{justify-content:space-between;align-items:center;gap:var(--mantine-spacing-sm);padding:var(--mantine-spacing-sm);border-top:1px solid var(--mantine-color-default-border);display:flex}._detailsToggle_n39tz_3{width:100%;height:100%;color:inherit;justify-content:center;align-items:center;display:flex}._detailsToggle_n39tz_3:hover ._chevron_n39tz_12{color:var(--mantine-color-text)}._chevron_n39tz_12{color:var(--mantine-color-dimmed);align-items:center;transition:transform .12s;display:flex}._chevron_n39tz_12[data-expanded=true]{transform:rotate(90deg)}@media (prefers-reduced-motion:reduce){._chevron_n39tz_12{transition:none}}._groupToggle_1954o_4{width:100%;min-width:0;font-size:var(--dg-font-size);color:inherit;align-items:center;gap:6px;display:flex}._chevron_1954o_14{color:var(--mantine-color-dimmed);flex:none;align-items:center;transition:transform .12s;display:flex}._chevron_1954o_14[data-expanded=true]{transform:rotate(90deg)}@media (prefers-reduced-motion:reduce){._chevron_1954o_14{transition:none}}._groupLabel_1954o_34{white-space:nowrap;text-overflow:ellipsis;min-width:0;font-weight:600;overflow:hidden}._groupCount_1954o_44{color:var(--mantine-color-dimmed);font-variant-numeric:tabular-nums;flex:none}._filterPanel_izakx_4{top:calc(var(--dg-header-height) + 2px);left:var(--mantine-spacing-md);z-index:5;padding:var(--mantine-spacing-sm);border-radius:var(--mantine-radius-md);border:1px solid var(--mantine-color-default-border);background-color:var(--mantine-color-body);box-shadow:var(--mantine-shadow-md);position:absolute}._filterRow_izakx_16{align-items:flex-end;gap:var(--mantine-spacing-xs);display:flex}._filterPanelHeader_izakx_22{justify-content:space-between;align-items:center;gap:var(--mantine-spacing-md);padding-bottom:var(--mantine-spacing-xs);display:flex}._filterPanelFooter_izakx_30{justify-content:space-between;align-items:center;gap:var(--mantine-spacing-md);display:flex}._filterPills_14wl0_3{align-items:center;gap:var(--mantine-spacing-xs);flex-wrap:wrap;display:flex}._filterPills_14wl0_3 ._pill_14wl0_15{background-color:var(--lightningcss-light,var(--mantine-color-gray-1))var(--lightningcss-dark,var(--mantine-color-dark-5));color:var(--lightningcss-light,var(--mantine-color-black))var(--lightningcss-dark,var(--mantine-color-dark-0));border:1px solid var(--lightningcss-light,var(--mantine-color-gray-3))var(--lightningcss-dark,var(--mantine-color-dark-3))}._pillLabel_14wl0_27{font:inherit;color:inherit;cursor:pointer}._pillLabel_14wl0_27:hover{text-decoration:underline}._footer_te0a3_1{justify-content:flex-end;align-items:center;gap:var(--mantine-spacing-lg);padding:4px var(--mantine-spacing-sm);border-top:1px solid var(--mantine-color-default-border);flex-shrink:0;min-height:48px;display:flex}._footer_te0a3_1[data-paging-suspended=true]{opacity:.55}._tableWrapper_15qzd_2{flex-direction:column;flex:1;min-height:0;display:flex;position:relative}._scrollContainer_15qzd_10{flex:1;min-height:0;position:relative;overflow:auto;container-type:inline-size}._grid_15qzd_21{display:grid}._headerRow_15qzd_27{grid-column:1/-1;grid-template-columns:subgrid;z-index:var(--dg-z-header,6);display:grid;position:sticky;top:0}._headerRow_15qzd_27[data-dg-header-last]:after{content:"";pointer-events:none;background:linear-gradient(to bottom, var(--dg-header-shadow-color,#00000024), transparent);opacity:0;height:6px;position:absolute;inset:100% 0 auto}@supports (animation-timeline:scroll()){._headerRow_15qzd_27[data-dg-header-last]:after{animation:linear both _dg-header-shadow_15qzd_1;animation-timeline:scroll();animation-range:0 24px}}@keyframes _dg-header-shadow_15qzd_1{0%{opacity:0}to{opacity:1}}._bodyRow_15qzd_76{--row-bg:var(--mantine-color-body);grid-column:1/-1;grid-template-columns:subgrid;align-items:stretch;display:grid}._bodyRow_15qzd_76[data-striped=true]{--row-bg:var(--dg-row-striped-bg)}._bodyRow_15qzd_76:hover{--row-bg:var(--mantine-color-default-hover)}._bodyRow_15qzd_76[data-grouped=true]{--row-bg:var(--dg-row-group-bg)}._bodyRow_15qzd_76[data-context-menu=true]{--row-bg:var(--mantine-color-default-hover)}._bodyRow_15qzd_76[data-selected-bg=true]{--row-bg:var(--dg-row-selected-bg)}._bodyRow_15qzd_76[data-highlighted=true]{--row-bg:var(--dg-row-highlight-bg)}._bodyRow_15qzd_76[data-selects-on-click=true]{-webkit-user-select:none;user-select:none}._bodyRow_15qzd_76:focus-visible{outline:2px solid var(--mantine-primary-color-filled);outline-offset:-2px}._bodyCell_15qzd_143{padding:0 var(--dg-padding);border-bottom:1px solid var(--mantine-color-default-border);background-color:var(--row-bg);font-size:var(--dg-font-size);align-items:center;display:flex}._bodyCell_15qzd_143[data-control-column=true]{padding:0}._grid_15qzd_21[data-range-dragging]{-webkit-user-select:none;user-select:none}._bodyCell_15qzd_143[data-selected=true]{background-color:color-mix(in srgb, var(--mantine-primary-color-filled) 14%, var(--row-bg));--dg-edge-top:transparent;--dg-edge-bottom:transparent;--dg-edge-left:transparent;--dg-edge-right:transparent;box-shadow:inset 0 1px 0 0 var(--dg-edge-top), inset 0 -1px 0 0 var(--dg-edge-bottom), inset 1px 0 0 0 var(--dg-edge-left), inset -1px 0 0 0 var(--dg-edge-right)}._bodyCell_15qzd_143[data-edge-top=true]{--dg-edge-top:var(--mantine-primary-color-filled)}._bodyCell_15qzd_143[data-edge-bottom=true]{--dg-edge-bottom:var(--mantine-primary-color-filled)}._bodyCell_15qzd_143[data-edge-left=true]{--dg-edge-left:var(--mantine-primary-color-filled)}._bodyCell_15qzd_143[data-edge-right=true]{--dg-edge-right:var(--mantine-primary-color-filled)}._bodyCell_15qzd_143[data-focused=true]{outline:2px solid var(--mantine-primary-color-filled);outline-offset:-2px}._cellContent_15qzd_141{white-space:nowrap;text-overflow:ellipsis;min-width:0;overflow:hidden}._bodyCell_15qzd_143[data-editing]{padding:0 calc(var(--dg-padding) / 2)}._cellEditor_15qzd_243{align-items:center;gap:2px;width:100%;min-width:0;display:flex}._cellEditorActions_15qzd_252{flex:none;align-items:center;display:flex}._bodyCell_15qzd_143[data-dirty],._bodyCell_15qzd_143[data-invalid]{position:relative}._bodyCell_15qzd_143[data-dirty]:after,._bodyCell_15qzd_143[data-invalid]:after{content:"";border:5px solid #0000;border-top-color:var(--mantine-primary-color-filled);border-right-color:var(--mantine-primary-color-filled);position:absolute;top:0;right:0}._bodyCell_15qzd_143[data-invalid]:after{border-top-color:var(--mantine-color-error);border-right-color:var(--mantine-color-error)}._bodyCell_15qzd_143[data-align=right]{justify-content:flex-end}._bodyCell_15qzd_143[data-align=center]{justify-content:center}._bodyCell_15qzd_143[data-align=left] ._cellContent_15qzd_141{flex:auto}._detailsCell_15qzd_303{border-bottom:1px solid var(--mantine-color-default-border);background-color:var(--row-bg);padding:var(--dg-padding);-webkit-user-select:text;user-select:text;cursor:auto;grid-column:1/-1}._messageRow_15qzd_15{justify-content:center;justify-self:start;align-items:center;gap:var(--mantine-spacing-xs);width:100cqi;padding:var(--mantine-spacing-xl);flex-direction:column;grid-column:1/-1;display:flex;position:sticky;left:0}._bodyRow_15qzd_76[data-deleted=true]{opacity:.55;pointer-events:none;text-decoration:line-through}._entryBlock_15qzd_347{grid-column:1/-1;grid-template-columns:subgrid;top:var(--dg-header-height,0px);z-index:var(--dg-z-pinned-row,4);display:grid;position:sticky}._entryRow_15qzd_356{grid-column:1/-1;grid-template-columns:subgrid;display:grid}._pinnedTopBlock_15qzd_369{grid-column:1/-1;grid-template-columns:subgrid;top:calc(var(--dg-header-height,0px) + var(--dg-entry-height,0px));z-index:var(--dg-z-pinned-row,4);display:grid;position:sticky}._pinnedTopBlock_15qzd_369>:last-child>[role=cell]{border-bottom:2px solid var(--mantine-color-default-border)}._pinnedBottomBlock_15qzd_382{grid-column:1/-1;grid-template-columns:subgrid;bottom:var(--dg-summary-height,0px);z-index:var(--dg-z-pinned-row,4);display:grid;position:sticky}._pinnedBottomBlock_15qzd_382>:first-child>[role=cell]{border-top:2px solid var(--mantine-color-default-border)}._entryCell_15qzd_395{padding:0 calc(var(--dg-padding) / 2);border-bottom:2px solid var(--mantine-primary-color-filled);background-color:var(--mantine-color-body);font-size:var(--dg-font-size);align-items:center;display:flex}._entryCell_15qzd_395[data-control-column=true]{justify-content:center;padding:0}._entryCell_15qzd_395[data-align=right]{justify-content:flex-end}._entryCell_15qzd_395[data-align=center]{justify-content:center}._matchHighlight_15qzd_422{background-color:var(--dg-match-highlight-bg,var(--lightningcss-light,var(--mantine-color-yellow-2))var(--lightningcss-dark,#fab00559));color:inherit;border-radius:2px}._summaryRow_15qzd_434{grid-column:1/-1;grid-template-columns:subgrid;z-index:var(--dg-z-summary-row,4);display:grid;position:sticky;bottom:0}._summaryCell_15qzd_443{padding:0 var(--dg-padding);border-top:1px solid var(--mantine-color-default-border);background-color:var(--mantine-color-body);font-size:var(--dg-font-size);align-items:center;font-weight:600;display:flex}._summaryCell_15qzd_443[data-control-column=true]{padding:0}._summaryCell_15qzd_443[data-align=right]{justify-content:flex-end}._summaryCell_15qzd_443[data-align=center]{justify-content:center}._stickyLeft_1vw51_16:after,._stickyRight_1vw51_17:after{content:"";pointer-events:none;width:8px;position:absolute;top:0;bottom:0}._stickyLeft_1vw51_16:after{background:linear-gradient(90deg,var(--lightningcss-light,#00000012)var(--lightningcss-dark,#00000042),var(--lightningcss-light,#00000006)var(--lightningcss-dark,#00000017) 45%,#0000);left:100%}._stickyRight_1vw51_17:after{background:linear-gradient(270deg,var(--lightningcss-light,#00000012)var(--lightningcss-dark,#00000042),var(--lightningcss-light,#00000006)var(--lightningcss-dark,#00000017) 45%,#0000);right:100%}@supports (animation-timeline:scroll()){._stickyLeft_1vw51_16:after,._stickyRight_1vw51_17:after{--dg-sticky-edge-range:20px;opacity:0;animation-timing-function:linear;animation-fill-mode:both;animation-timeline:scroll(inline)}._stickyLeft_1vw51_16:after{animation-name:_stickyEdgeIn_1vw51_1;animation-range:0 var(--dg-sticky-edge-range)}._stickyRight_1vw51_17:after{animation-name:_stickyEdgeOut_1vw51_1;animation-range:calc(100% - var(--dg-sticky-edge-range)) 100%}}@keyframes _stickyEdgeIn_1vw51_1{0%{opacity:0}to{opacity:1}}@keyframes _stickyEdgeOut_1vw51_1{0%{opacity:1}to{opacity:0}}._headerCell_188ed_1{padding:0 var(--dg-padding);min-width:0;min-height:var(--dg-header-height);font-weight:600;font-size:var(--dg-font-size);background-color:var(--mantine-color-body);border-bottom:1px solid var(--mantine-color-default-border);-webkit-user-select:none;user-select:none;align-items:center;gap:2px;display:flex;position:relative}._headerCell_188ed_1[data-align=right]{justify-content:flex-end}._headerCell_188ed_1[data-align=center]{justify-content:center}._headerCell_188ed_1[data-control-column=true]{padding:0}._headerCellFiltered_188ed_35 ._headerTitle_188ed_35{color:var(--mantine-primary-color-filled)}._headerTitle_188ed_35{text-overflow:ellipsis;white-space:nowrap;flex:auto;min-width:0;overflow:hidden}._headerCell_188ed_1[data-align=right] ._headerTitle_188ed_35{text-align:right}._headerCell_188ed_1[data-align=center] ._headerTitle_188ed_35{justify-content:center;display:flex}._headerActions_188ed_58{flex-shrink:0;align-items:center;gap:0;margin-left:auto;display:flex}._headerCell_188ed_1[data-align=right] ._headerActions_188ed_58{order:-1;margin-left:0;margin-right:auto}._headerAction_188ed_58{display:none}._headerCell_188ed_1:hover ._headerAction_188ed_58,._headerCell_188ed_1:focus-within ._headerAction_188ed_58,._headerAction_188ed_58[data-pinned-visible=true]{display:flex}._headerCellSortable_188ed_87{cursor:pointer}._sortAction_188ed_93[data-sorted=false]{opacity:.55}._sortAction_188ed_93[data-sorted=false]:hover{opacity:1}._sortIndex_188ed_103{color:var(--mantine-color-dimmed);text-align:center;min-width:10px;font-size:10px;font-weight:600;line-height:1}._headerCellDragging_188ed_115{opacity:.4}._headerCellDropBefore_188ed_119:before,._headerCellDropAfter_188ed_120:before{content:"";background-color:var(--mantine-primary-color-filled);z-index:5;pointer-events:none;width:2px;position:absolute;top:0;bottom:0}._headerCellDropBefore_188ed_119:before{left:0}._headerCellDropAfter_188ed_120:before{right:0}._columnSeparator_188ed_140{z-index:1;justify-content:center;width:10px;height:50%;display:flex;position:absolute;top:25%;right:0;transform:translate(50%)}._columnSeparatorResizable_188ed_152{cursor:col-resize;touch-action:none}._columnSeparator_188ed_140:after{content:"";background-color:#0000;width:1px;height:100%;transition:background-color .12s,width .12s}._headerCell_188ed_1:hover ._columnSeparator_188ed_140:after,._headerCell_188ed_1:focus-within ._columnSeparator_188ed_140:after{background-color:var(--mantine-color-default-border)}._columnSeparatorResizable_188ed_152:hover:after,._columnSeparatorActive_188ed_175:after{background-color:var(--mantine-primary-color-filled);width:2px}._menuItemActive_188ed_181{background-color:var(--mantine-color-default-hover);font-weight:600}._toolbar_4my8t_1{align-items:center;gap:var(--mantine-spacing-xs);padding:6px var(--mantine-spacing-xs);flex-shrink:0;min-height:42px;display:flex}._toolbarSpacer_4my8t_10{flex:1}
1
+ ._root_1wvkg_8[data-size=xs]{--dg-row-height:34px;--dg-header-height:32px;--dg-font-size:var(--mantine-font-size-xs);--dg-padding:6px}._root_1wvkg_8[data-size=sm]{--dg-row-height:42px;--dg-header-height:38px;--dg-font-size:var(--mantine-font-size-sm);--dg-padding:8px}._root_1wvkg_8[data-size=md]{--dg-row-height:52px;--dg-header-height:44px;--dg-font-size:var(--mantine-font-size-sm);--dg-padding:10px}._root_1wvkg_8[data-size=lg]{--dg-row-height:62px;--dg-header-height:52px;--dg-font-size:var(--mantine-font-size-md);--dg-padding:14px}._root_1wvkg_8[data-size=xl]{--dg-row-height:72px;--dg-header-height:60px;--dg-font-size:var(--mantine-font-size-lg);--dg-padding:18px}._root_1wvkg_8{--dg-radius:var(--mantine-radius-md);--dg-row-height:52px;--dg-header-height:44px;--dg-font-size:var(--mantine-font-size-sm);--dg-padding:10px;--dg-row-selected-bg:var(--mantine-primary-color-light);--dg-row-highlight-bg:var(--mantine-primary-color-light-hover);--dg-row-group-bg:var(--mantine-color-body);--dg-row-striped-bg:color-mix(in srgb, var(--mantine-color-text) 3.5%, var(--mantine-color-body));--dg-row-new-bg:color-mix(in srgb, var(--mantine-color-green-6) 8%, var(--mantine-color-body));--dg-z-pinned-cell:2;--dg-z-focused-cell:3;--dg-z-pinned-row:4;--dg-z-pinned-row-pinned-cell:5;--dg-z-summary-row:4;--dg-z-summary-pinned-cell:5;--dg-z-header:6;--dg-z-header-pinned-cell:7;text-align:left;border:1px solid var(--mantine-color-default-border);border-radius:var(--dg-radius);background-color:var(--mantine-color-body);flex-direction:column;min-height:0;display:flex;overflow:hidden}._columnsPanel_1492o_1{flex-direction:column;width:280px;display:flex}._columnsPanelSearch_1492o_7{padding:var(--mantine-spacing-sm)}._columnsPanelList_1492o_11{gap:var(--mantine-spacing-sm);padding:0 var(--mantine-spacing-sm) var(--mantine-spacing-sm);flex-direction:column;display:flex}._columnsPanelFooter_1492o_18{justify-content:space-between;align-items:center;gap:var(--mantine-spacing-sm);padding:var(--mantine-spacing-sm);border-top:1px solid var(--mantine-color-default-border);display:flex}._detailsToggle_n39tz_3{width:100%;height:100%;color:inherit;justify-content:center;align-items:center;display:flex}._detailsToggle_n39tz_3:hover ._chevron_n39tz_12{color:var(--mantine-color-text)}._chevron_n39tz_12{color:var(--mantine-color-dimmed);align-items:center;transition:transform .12s;display:flex}._chevron_n39tz_12[data-expanded=true]{transform:rotate(90deg)}@media (prefers-reduced-motion:reduce){._chevron_n39tz_12{transition:none}}._tableWrapper_uqiy9_2{flex-direction:column;flex:1;min-height:0;display:flex;position:relative}._scrollContainer_uqiy9_10{flex:1;min-height:0;position:relative;overflow:auto;container-type:inline-size}._grid_uqiy9_21{display:grid}._headerRow_uqiy9_27{grid-column:1/-1;grid-template-columns:subgrid;z-index:var(--dg-z-header,6);display:grid;position:sticky;top:0}._headerRow_uqiy9_27[data-dg-header-last]:after{content:"";pointer-events:none;background:linear-gradient(to bottom, var(--dg-header-shadow-color,#00000024), transparent);opacity:0;height:6px;position:absolute;inset:100% 0 auto}@supports (animation-timeline:scroll()){._headerRow_uqiy9_27[data-dg-header-last]:after{animation:linear both _dg-header-shadow_uqiy9_1;animation-timeline:scroll();animation-range:0 24px}}@keyframes _dg-header-shadow_uqiy9_1{0%{opacity:0}to{opacity:1}}._bodyRow_uqiy9_76{--row-bg:var(--mantine-color-body);grid-column:1/-1;grid-template-columns:subgrid;align-items:stretch;display:grid}._bodyRow_uqiy9_76[data-striped=true]{--row-bg:var(--dg-row-striped-bg)}._bodyRow_uqiy9_76:hover{--row-bg:var(--mantine-color-default-hover)}._bodyRow_uqiy9_76[data-grouped=true]{--row-bg:var(--dg-row-group-bg)}._bodyRow_uqiy9_76[data-context-menu=true]{--row-bg:var(--mantine-color-default-hover)}._bodyRow_uqiy9_76[data-selected-bg=true]{--row-bg:var(--dg-row-selected-bg)}._bodyRow_uqiy9_76[data-highlighted=true]{--row-bg:var(--dg-row-highlight-bg)}._bodyRow_uqiy9_76[data-selects-on-click=true]{-webkit-user-select:none;user-select:none}._bodyRow_uqiy9_76:focus-visible{outline:2px solid var(--mantine-primary-color-filled);outline-offset:-2px}._bodyCell_uqiy9_143{padding:0 var(--dg-padding);border-bottom:1px solid var(--mantine-color-default-border);background-color:var(--row-bg);font-size:var(--dg-font-size);align-items:center;display:flex}._bodyCell_uqiy9_143[data-control-column=true]{padding:0}._grid_uqiy9_21[data-range-dragging]{-webkit-user-select:none;user-select:none}._bodyCell_uqiy9_143[data-selected=true]{background-color:color-mix(in srgb, var(--mantine-primary-color-filled) 14%, var(--row-bg));--dg-edge-top:transparent;--dg-edge-bottom:transparent;--dg-edge-left:transparent;--dg-edge-right:transparent;box-shadow:inset 0 1px 0 0 var(--dg-edge-top), inset 0 -1px 0 0 var(--dg-edge-bottom), inset 1px 0 0 0 var(--dg-edge-left), inset -1px 0 0 0 var(--dg-edge-right)}._bodyCell_uqiy9_143[data-edge-top=true]{--dg-edge-top:var(--mantine-primary-color-filled)}._bodyCell_uqiy9_143[data-edge-bottom=true]{--dg-edge-bottom:var(--mantine-primary-color-filled)}._bodyCell_uqiy9_143[data-edge-left=true]{--dg-edge-left:var(--mantine-primary-color-filled)}._bodyCell_uqiy9_143[data-edge-right=true]{--dg-edge-right:var(--mantine-primary-color-filled)}._bodyCell_uqiy9_143[data-focused=true]{outline:2px solid var(--mantine-primary-color-filled);outline-offset:-2px}._cellContent_uqiy9_141{white-space:nowrap;text-overflow:ellipsis;min-width:0;overflow:hidden}._bodyCell_uqiy9_143[data-editing]{padding:0 calc(var(--dg-padding) / 2)}._cellEditor_uqiy9_243{align-items:center;gap:2px;width:100%;min-width:0;display:flex}._cellEditorActions_uqiy9_252{flex:none;align-items:center;display:flex}._bodyCell_uqiy9_143[data-dirty],._bodyCell_uqiy9_143[data-invalid]{position:relative}._bodyCell_uqiy9_143[data-dirty]:after,._bodyCell_uqiy9_143[data-invalid]:after{content:"";border:5px solid #0000;border-top-color:var(--mantine-primary-color-filled);border-right-color:var(--mantine-primary-color-filled);position:absolute;top:0;right:0}._bodyCell_uqiy9_143[data-invalid]:after{border-top-color:var(--mantine-color-error);border-right-color:var(--mantine-color-error)}._bodyCell_uqiy9_143[data-align=right]{justify-content:flex-end}._bodyCell_uqiy9_143[data-align=center]{justify-content:center}._bodyCell_uqiy9_143[data-align=left] ._cellContent_uqiy9_141{flex:auto}._detailsCell_uqiy9_303{border-bottom:1px solid var(--mantine-color-default-border);background-color:var(--row-bg);padding:var(--dg-padding);-webkit-user-select:text;user-select:text;cursor:auto;grid-column:1/-1}._messageRow_uqiy9_15{justify-content:center;justify-self:start;align-items:center;gap:var(--mantine-spacing-xs);width:100cqi;padding:var(--mantine-spacing-xl);flex-direction:column;grid-column:1/-1;display:flex;position:sticky;left:0}._bodyRow_uqiy9_76[data-deleted=true]{opacity:.55;text-decoration:line-through}._bodyRow_uqiy9_76[data-deleted=true] ._bodyCell_uqiy9_143:not([data-control-column=true]){pointer-events:none}._entryBlock_uqiy9_353{grid-column:1/-1;grid-template-columns:subgrid;top:var(--dg-header-height,0px);z-index:var(--dg-z-pinned-row,4);display:grid;position:sticky}._entryRow_uqiy9_362,._entryFlowBlock_uqiy9_371{grid-column:1/-1;grid-template-columns:subgrid;display:grid}._pinnedTopBlock_uqiy9_384{grid-column:1/-1;grid-template-columns:subgrid;top:calc(var(--dg-header-height,0px) + var(--dg-entry-height,0px));z-index:var(--dg-z-pinned-row,4);display:grid;position:sticky}._pinnedTopBlock_uqiy9_384>:last-child>[role=cell]{border-bottom:2px solid var(--mantine-color-default-border)}._pinnedBottomBlock_uqiy9_397{grid-column:1/-1;grid-template-columns:subgrid;bottom:var(--dg-summary-height,0px);z-index:var(--dg-z-pinned-row,4);display:grid;position:sticky}._pinnedBottomBlock_uqiy9_397>:first-child>[role=cell]{border-top:2px solid var(--mantine-color-default-border)}._entryCell_uqiy9_410{padding:0 calc(var(--dg-padding) / 2);border-bottom:2px solid var(--mantine-primary-color-filled);background-color:var(--mantine-color-body);font-size:var(--dg-font-size);align-items:center;display:flex}._entryCell_uqiy9_410[data-control-column=true]{justify-content:center;padding:0}._entryRow_uqiy9_362[data-confirmed=true] ._entryCell_uqiy9_410{padding:0 var(--dg-padding);border-bottom:1px solid var(--mantine-color-default-border);background-color:var(--dg-row-new-bg,var(--mantine-color-body))}._entryRow_uqiy9_362[data-confirmed=true] ._entryCell_uqiy9_410[data-control-column=true]{padding:0}._rowStateIndicator_uqiy9_440{width:22px;height:22px;color:var(--mantine-color-dimmed);flex:none;justify-content:center;align-items:center;display:inline-flex}._rowStateIndicator_uqiy9_440[data-error]{color:var(--mantine-color-error)}._entryCell_uqiy9_410[data-align=right]{justify-content:flex-end}._entryCell_uqiy9_410[data-align=center]{justify-content:center}._matchHighlight_uqiy9_466{background-color:var(--dg-match-highlight-bg,var(--lightningcss-light,var(--mantine-color-yellow-2))var(--lightningcss-dark,#fab00559));color:inherit;border-radius:2px}._summaryRow_uqiy9_478{grid-column:1/-1;grid-template-columns:subgrid;z-index:var(--dg-z-summary-row,4);display:grid;position:sticky;bottom:0}._summaryCell_uqiy9_487{padding:0 var(--dg-padding);border-top:1px solid var(--mantine-color-default-border);background-color:var(--mantine-color-body);font-size:var(--dg-font-size);align-items:center;font-weight:600;display:flex}._summaryCell_uqiy9_487[data-control-column=true]{padding:0}._summaryCell_uqiy9_487[data-align=right]{justify-content:flex-end}._summaryCell_uqiy9_487[data-align=center]{justify-content:center}._groupToggle_1954o_4{width:100%;min-width:0;font-size:var(--dg-font-size);color:inherit;align-items:center;gap:6px;display:flex}._chevron_1954o_14{color:var(--mantine-color-dimmed);flex:none;align-items:center;transition:transform .12s;display:flex}._chevron_1954o_14[data-expanded=true]{transform:rotate(90deg)}@media (prefers-reduced-motion:reduce){._chevron_1954o_14{transition:none}}._groupLabel_1954o_34{white-space:nowrap;text-overflow:ellipsis;min-width:0;font-weight:600;overflow:hidden}._groupCount_1954o_44{color:var(--mantine-color-dimmed);font-variant-numeric:tabular-nums;flex:none}._filterPanel_izakx_4{top:calc(var(--dg-header-height) + 2px);left:var(--mantine-spacing-md);z-index:5;padding:var(--mantine-spacing-sm);border-radius:var(--mantine-radius-md);border:1px solid var(--mantine-color-default-border);background-color:var(--mantine-color-body);box-shadow:var(--mantine-shadow-md);position:absolute}._filterRow_izakx_16{align-items:flex-end;gap:var(--mantine-spacing-xs);display:flex}._filterPanelHeader_izakx_22{justify-content:space-between;align-items:center;gap:var(--mantine-spacing-md);padding-bottom:var(--mantine-spacing-xs);display:flex}._filterPanelFooter_izakx_30{justify-content:space-between;align-items:center;gap:var(--mantine-spacing-md);display:flex}._filterPills_14wl0_3{align-items:center;gap:var(--mantine-spacing-xs);flex-wrap:wrap;display:flex}._filterPills_14wl0_3 ._pill_14wl0_15{background-color:var(--lightningcss-light,var(--mantine-color-gray-1))var(--lightningcss-dark,var(--mantine-color-dark-5));color:var(--lightningcss-light,var(--mantine-color-black))var(--lightningcss-dark,var(--mantine-color-dark-0));border:1px solid var(--lightningcss-light,var(--mantine-color-gray-3))var(--lightningcss-dark,var(--mantine-color-dark-3))}._pillLabel_14wl0_27{font:inherit;color:inherit;cursor:pointer}._pillLabel_14wl0_27:hover{text-decoration:underline}._footer_te0a3_1{justify-content:flex-end;align-items:center;gap:var(--mantine-spacing-lg);padding:4px var(--mantine-spacing-sm);border-top:1px solid var(--mantine-color-default-border);flex-shrink:0;min-height:48px;display:flex}._footer_te0a3_1[data-paging-suspended=true]{opacity:.55}._stickyLeft_1vw51_16:after,._stickyRight_1vw51_17:after{content:"";pointer-events:none;width:8px;position:absolute;top:0;bottom:0}._stickyLeft_1vw51_16:after{background:linear-gradient(90deg,var(--lightningcss-light,#00000012)var(--lightningcss-dark,#00000042),var(--lightningcss-light,#00000006)var(--lightningcss-dark,#00000017) 45%,#0000);left:100%}._stickyRight_1vw51_17:after{background:linear-gradient(270deg,var(--lightningcss-light,#00000012)var(--lightningcss-dark,#00000042),var(--lightningcss-light,#00000006)var(--lightningcss-dark,#00000017) 45%,#0000);right:100%}@supports (animation-timeline:scroll()){._stickyLeft_1vw51_16:after,._stickyRight_1vw51_17:after{--dg-sticky-edge-range:20px;opacity:0;animation-timing-function:linear;animation-fill-mode:both;animation-timeline:scroll(inline)}._stickyLeft_1vw51_16:after{animation-name:_stickyEdgeIn_1vw51_1;animation-range:0 var(--dg-sticky-edge-range)}._stickyRight_1vw51_17:after{animation-name:_stickyEdgeOut_1vw51_1;animation-range:calc(100% - var(--dg-sticky-edge-range)) 100%}}@keyframes _stickyEdgeIn_1vw51_1{0%{opacity:0}to{opacity:1}}@keyframes _stickyEdgeOut_1vw51_1{0%{opacity:1}to{opacity:0}}._headerCell_188ed_1{padding:0 var(--dg-padding);min-width:0;min-height:var(--dg-header-height);font-weight:600;font-size:var(--dg-font-size);background-color:var(--mantine-color-body);border-bottom:1px solid var(--mantine-color-default-border);-webkit-user-select:none;user-select:none;align-items:center;gap:2px;display:flex;position:relative}._headerCell_188ed_1[data-align=right]{justify-content:flex-end}._headerCell_188ed_1[data-align=center]{justify-content:center}._headerCell_188ed_1[data-control-column=true]{padding:0}._headerCellFiltered_188ed_35 ._headerTitle_188ed_35{color:var(--mantine-primary-color-filled)}._headerTitle_188ed_35{text-overflow:ellipsis;white-space:nowrap;flex:auto;min-width:0;overflow:hidden}._headerCell_188ed_1[data-align=right] ._headerTitle_188ed_35{text-align:right}._headerCell_188ed_1[data-align=center] ._headerTitle_188ed_35{justify-content:center;display:flex}._headerActions_188ed_58{flex-shrink:0;align-items:center;gap:0;margin-left:auto;display:flex}._headerCell_188ed_1[data-align=right] ._headerActions_188ed_58{order:-1;margin-left:0;margin-right:auto}._headerAction_188ed_58{display:none}._headerCell_188ed_1:hover ._headerAction_188ed_58,._headerCell_188ed_1:focus-within ._headerAction_188ed_58,._headerAction_188ed_58[data-pinned-visible=true]{display:flex}._headerCellSortable_188ed_87{cursor:pointer}._sortAction_188ed_93[data-sorted=false]{opacity:.55}._sortAction_188ed_93[data-sorted=false]:hover{opacity:1}._sortIndex_188ed_103{color:var(--mantine-color-dimmed);text-align:center;min-width:10px;font-size:10px;font-weight:600;line-height:1}._headerCellDragging_188ed_115{opacity:.4}._headerCellDropBefore_188ed_119:before,._headerCellDropAfter_188ed_120:before{content:"";background-color:var(--mantine-primary-color-filled);z-index:5;pointer-events:none;width:2px;position:absolute;top:0;bottom:0}._headerCellDropBefore_188ed_119:before{left:0}._headerCellDropAfter_188ed_120:before{right:0}._columnSeparator_188ed_140{z-index:1;justify-content:center;width:10px;height:50%;display:flex;position:absolute;top:25%;right:0;transform:translate(50%)}._columnSeparatorResizable_188ed_152{cursor:col-resize;touch-action:none}._columnSeparator_188ed_140:after{content:"";background-color:#0000;width:1px;height:100%;transition:background-color .12s,width .12s}._headerCell_188ed_1:hover ._columnSeparator_188ed_140:after,._headerCell_188ed_1:focus-within ._columnSeparator_188ed_140:after{background-color:var(--mantine-color-default-border)}._columnSeparatorResizable_188ed_152:hover:after,._columnSeparatorActive_188ed_175:after{background-color:var(--mantine-primary-color-filled);width:2px}._menuItemActive_188ed_181{background-color:var(--mantine-color-default-hover);font-weight:600}._toolbar_4my8t_1{align-items:center;gap:var(--mantine-spacing-xs);padding:6px var(--mantine-spacing-xs);flex-shrink:0;min-height:42px;display:flex}._toolbarSpacer_4my8t_10{flex:1}
2
2
  /*$vite$:1*/
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jielga/tmdatagrid",
3
- "version": "2.0.0-beta.0",
3
+ "version": "2.0.0-beta.2",
4
4
  "description": "A React data grid built on TanStack Table v9 and Mantine - always virtualized, with resizable, reorderable, sortable, filterable, hideable and pinnable columns.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -16,7 +16,7 @@ description: >
16
16
  metadata:
17
17
  type: core
18
18
  library: '@jielga/tmdatagrid'
19
- library_version: '2.0.0-beta.0'
19
+ library_version: '2.0.0-beta.2'
20
20
  sources:
21
21
  - 'Jielga/TMDataGrid:src/docs/styling.md'
22
22
  - 'Jielga/TMDataGrid:src/docs/toolbar.md'
@@ -44,13 +44,15 @@ and selects the size of every Mantine control the grid renders.
44
44
  | `xl` | 72px | 60px | `lg` | 18px |
45
45
 
46
46
  Row height is also required by the virtualizer **as a number**, so it cannot be
47
- defined in CSS alone. For a height outside the scale set `meta.rowHeight`, not
48
- the variable. `SIZE_ROW_HEIGHT` is the exported source of these values.
47
+ defined in CSS alone. For a height outside the scale, set `meta.rowHeight`
48
+ rather than the variable. `SIZE_ROW_HEIGHT` is the exported source of these
49
+ values.
49
50
 
50
51
  ## CSS variables
51
52
 
52
53
  `style` accepts custom properties and `className` reaches the same element from
53
- a stylesheet. Both are per instance - a grid is themed without a provider.
54
+ a stylesheet. Both are per instance, so a grid can be themed without a
55
+ provider.
54
56
 
55
57
  ```tsx
56
58
  <TMDataGrid
@@ -62,17 +64,17 @@ a stylesheet. Both are per instance - a grid is themed without a provider.
62
64
 
63
65
  **Metrics:** `--dg-row-height`, `--dg-header-height`, `--dg-summary-height`,
64
66
  `--dg-entry-height`, `--dg-font-size`, `--dg-padding`. All default from `size`.
65
- The generated lanes are exempt from padding: they are fixed 36px tracks that
67
+ The generated lanes are excluded from padding: they are fixed 36px tracks that
66
68
  centre their control.
67
69
 
68
- **Colours:** `--row-bg` (one row's own background - set this, never
70
+ **Colours:** `--row-bg` (one row's own background; set this, never
69
71
  `background`), `--dg-row-selected-bg`, `--dg-row-highlight-bg`,
70
72
  `--dg-row-striped-bg`, `--dg-row-group-bg`, `--dg-match-highlight-bg`,
71
73
  `--dg-header-shadow-color`.
72
74
 
73
75
  **Layout internals:** `--dg-sticky-edge-range` (`20px`), the `--dg-edge-*`
74
- markers the grid sets on cell-range borders, and the `--dg-z-*` stacking ladder -
75
- change those only to slot something of your own between two layers.
76
+ markers the grid sets on cell-range borders, and the `--dg-z-*` stacking order.
77
+ Change those only to place something of your own between two layers.
76
78
 
77
79
  One stylesheet import, once, anywhere:
78
80
 
@@ -87,7 +89,8 @@ two, never both.
87
89
  ## Layout
88
90
 
89
91
  The grid fills the box you give it and scrolls inside it. It does not size
90
- itself to its content - a virtualized grid has no content height to measure.
92
+ itself to its content, because a virtualized grid has no content height to
93
+ measure.
91
94
 
92
95
  ```tsx
93
96
  <div style={{ display: "flex", flexDirection: "column", height: "100vh" }}>
@@ -97,16 +100,15 @@ itself to its content - a virtualized grid has no content height to measure.
97
100
  </div>
98
101
  ```
99
102
 
100
- `minHeight: 0` is the part everyone forgets: a flex item's default
101
- `min-height: auto` refuses to shrink below its content, so without it the grid
102
- grows past the viewport instead of scrolling.
103
+ `minHeight: 0` is required: a flex item's default `min-height: auto` will not
104
+ shrink below its content, so without it the grid grows past the viewport instead
105
+ of scrolling.
103
106
 
104
107
  ## Toolbar
105
108
 
106
- The toolbar is a flex row and nothing more. No slots API, no `actions` prop -
107
- your buttons sit beside the built-in ones because they are all just children,
108
- and `TMDataGrid.Spacer` pushes what follows to the right. That is the whole
109
- layout system.
109
+ The toolbar is a flex row. There is no slots API and no `actions` prop: your
110
+ buttons sit beside the built-in ones because they are all children of the same
111
+ element, and `TMDataGrid.Spacer` pushes what follows to the right.
110
112
 
111
113
  ```tsx
112
114
  <TMDataGrid.Toolbar>
@@ -121,11 +123,11 @@ layout system.
121
123
  ```
122
124
 
123
125
  Each built-in renders nothing when its feature is off, so a read-only grid needs
124
- no conditionals: `FilterButton` under `enableColumnFilters: false` is simply
125
- absent.
126
+ no conditionals: `FilterButton` under `enableColumnFilters: false` renders
127
+ nothing at all.
126
128
 
127
- A button of your own reads the grid from context - `{ table, ui, features,
128
- labels, controlSize, resetSettings }`:
129
+ A button of your own reads the grid from context, which returns
130
+ `{ table, ui, features, labels, controlSize, resetSettings }`:
129
131
 
130
132
  ```tsx
131
133
  import {
@@ -148,9 +150,9 @@ function ExportButton() {
148
150
  }
149
151
  ```
150
152
 
151
- `getGridCapabilities(table, features)` answers `canSortAny`, `canFilterAny`,
153
+ `getGridCapabilities(table, features)` returns `canSortAny`, `canFilterAny`,
152
154
  `canHideAny`, `canPinAny`, `canReorderAny`, `canGroupAny`, `canSelectRows`,
153
- `canPaginate` and `canSearch`. `getColumnCapabilities(column, features)` answers
155
+ `canPaginate` and `canSearch`. `getColumnCapabilities(column, features)` returns
154
156
  the same for one column as `canSort`, `canFilter`, `canHide`, `canPin`,
155
157
  `canResize`, `canReorder` and `canGroup`.
156
158
 
@@ -158,13 +160,13 @@ the same for one column as `canSort`, `canFilter`, `canHide`, `canPin`,
158
160
 
159
161
  `features` comes back from `useTMDataGrid` and is re-derived from the options
160
162
  object on every render. It is required **in addition to** TanStack's `getCanX()`
161
- methods because it is what makes the result reactive: `column.getCanSort()` is a
163
+ methods because it is what makes the result reactive. `column.getCanSort()` is a
162
164
  method call on a column object whose identity survives an options change, so
163
165
  under the React Compiler that call is memoized and a grid whose `enableSorting`
164
- flipped to `false` would carry on rendering sort indicators. `features` supplies
166
+ changed to `false` would carry on rendering sort indicators. `features` supplies
165
167
  a value that changes; `getCanX()` still decides the outcome.
166
168
 
167
- The same rule applies anywhere in your app: read state through
169
+ The same rule applies elsewhere in your application: read state through
168
170
  `useSelector(table.store, …)` and options through `features`, rather than
169
171
  calling methods on a long-lived object.
170
172
 
@@ -180,8 +182,8 @@ import { TMDATAGRID_LABELS_SV } from "@jielga/tmdatagrid";
180
182
  const grid = useTMDataGrid({ data, columns, labels: TMDATAGRID_LABELS_SV });
181
183
  ```
182
184
 
183
- Labels that carry a value are functions, so a language can put the value where
184
- its grammar wants it:
185
+ Labels that carry a value are functions, so each language can place the value
186
+ where its grammar requires:
185
187
 
186
188
  ```tsx
187
189
  const labels = {
@@ -190,10 +192,10 @@ const labels = {
190
192
  } satisfies TMDataGridLabelsOverride;
191
193
  ```
192
194
 
193
- `TMDataGridLabels` is the full dictionary type, which is what makes a new
194
- translation a typed exercise rather than a guess. The resolved dictionary comes
195
- back as `grid.labels` and from `useTMDataGridContext().labels`, so a component
196
- of your own uses the same strings as the built-in chrome.
195
+ `TMDataGridLabels` is the full dictionary type, so a missing key in a new
196
+ translation is a compile error. The resolved dictionary comes back as
197
+ `grid.labels` and from `useTMDataGridContext().labels`, so a component of your
198
+ own uses the same strings as the built-in parts.
197
199
 
198
200
  ## Common mistakes
199
201
 
@@ -229,7 +231,7 @@ Source: `src/docs/styling.md` (Layout).
229
231
  ### HIGH Setting `--dg-row-height` to change density
230
232
 
231
233
  The virtualizer needs the row height as a number, and takes it from
232
- `meta.rowHeight` or `size` - not from the variable. Setting the variable alone
234
+ `meta.rowHeight` or `size`, not from the variable. Setting the variable alone
233
235
  leaves the measurement and the render disagreeing, so rows overlap or gaps open
234
236
  as you scroll.
235
237
 
@@ -266,8 +268,9 @@ Source: `src/docs/toolbar.md` (Why `features` is a second argument).
266
268
 
267
269
  ### MEDIUM An inline `labels` object
268
270
 
269
- The chrome re-renders when the labels object changes identity, and an inline
270
- literal is a new object every render. Keep it at module scope, or `useMemo` it.
271
+ The grid re-renders when the labels object changes identity, and an inline
272
+ literal is a new object on every render. Define it at module scope, or memoize
273
+ it.
271
274
 
272
275
  Wrong:
273
276
 
@@ -14,7 +14,7 @@ description: >
14
14
  metadata:
15
15
  type: core
16
16
  library: '@jielga/tmdatagrid'
17
- library_version: '2.0.0-beta.0'
17
+ library_version: '2.0.0-beta.2'
18
18
  sources:
19
19
  - 'Jielga/TMDataGrid:src/docs/cell-selection.md'
20
20
  - 'Jielga/TMDataGrid:src/tmdatagrid/core/cellNavigation.ts'
@@ -24,9 +24,9 @@ sources:
24
24
 
25
25
  # TMDataGrid - Cell selection
26
26
 
27
- A cell cursor the arrow keys move, a rectangle of cells that can be dragged out,
28
- and a Ctrl+C that pastes into Excel as cells rather than as one string. Off by
29
- default.
27
+ A cell cursor moved with the arrow keys, a rectangle of cells that can be
28
+ dragged out, and a Ctrl+C that pastes into Excel as cells rather than as one
29
+ string. Off by default.
30
30
 
31
31
  ```tsx
32
32
  const grid = useTMDataGrid({ data, columns, cellSelection: "range" });
@@ -38,15 +38,15 @@ const grid = useTMDataGrid({ data, columns, cellSelection: "range" });
38
38
  | `"single"` | One focused cell, moved with the arrow keys |
39
39
  | `"range"` | As `"single"`, plus a rectangle, Ctrl+C and the export menu |
40
40
 
41
- Setting `editMode` defaults `cellSelection` to `"single"`, since editing
41
+ Setting `editing` defaults `cellSelection` to `"single"`, since editing
42
42
  navigates by cursor. An explicit `cellSelection` always wins.
43
43
 
44
44
  Turning it on changes three things about the body:
45
45
 
46
46
  - The tab stop moves from the row to a cell, so the whole grid is **one** Tab
47
- stop and the arrow keys walk it.
47
+ stop and the arrow keys move within it.
48
48
  - The grid reports itself as a `grid` of `gridcell`s rather than a `table` of
49
- `cell`s, which is what tells a screen reader those keys are live.
49
+ `cell`s, which tells a screen reader those keys are live.
50
50
  - The focused cell takes `data-focused`, selected ones `data-selected` and
51
51
  `data-edge-*`.
52
52
 
@@ -97,10 +97,11 @@ cell works either way.
97
97
  ## Where the selection lives
98
98
 
99
99
  `ui.state.focusedCell` is a `{ rowId, columnId }` pair, and `ui.state.cellRange`
100
- is two of them, the anchor and the moving corner. **Ids rather than indices**, so
101
- sorting, filtering and column reordering carry the selection with the cells
102
- instead of leaving it over whatever slid into those positions. A range whose
103
- corner is filtered away paints nothing, and comes back when the filter lifts.
100
+ is two of them: the anchor and the moving corner. They hold **ids rather than
101
+ indices**, so sorting, filtering and column reordering move the selection with
102
+ the cells instead of leaving it over whatever took those positions. A range
103
+ whose corner is filtered away paints nothing, and returns when the filter is
104
+ cleared.
104
105
 
105
106
  ```tsx
106
107
  const focusedCell = useSelector(grid.ui, (state) => state.focusedCell);
@@ -111,11 +112,10 @@ grid.ui.actions.setFocusedCell({ rowId: "42", columnId: "salary" });
111
112
  One rectangle at a time; Ctrl+drag for a second, disjoint block is not
112
113
  supported.
113
114
 
114
- The generated lanes (checkbox, tree, details) are part of the selection - they
115
- take the tint and the outline so the block stays a rectangle - but are never
116
- *exported*, since they hold controls rather than values. A block covering
117
- nothing else has nothing to copy, and the Copy and Export items say so by being
118
- disabled.
115
+ The generated lanes (checkbox, tree, details) are part of the selection: they
116
+ take the tint and the outline so the block stays a rectangle. They are never
117
+ *exported*, because they hold controls rather than values. A block covering
118
+ nothing else has nothing to copy, and the Copy and Export items are disabled.
119
119
 
120
120
  ## Copy and export
121
121
 
@@ -130,7 +130,7 @@ below a divider, so nothing is lost by turning cell selection on.
130
130
 
131
131
  The CSV is written for a Nordic Excel: a `sep=;` first line, a UTF-8 BOM, CRLF
132
132
  endings, semicolons between fields and a comma as the decimal mark. That
133
- combination is what makes the file open straight into columns with å ä ö intact.
133
+ combination opens straight into columns with å ä ö intact.
134
134
 
135
135
  ```tsx
136
136
  <TMDataGrid.Table
@@ -179,7 +179,7 @@ Source: `src/docs/cell-selection.md` (One tab stop).
179
179
 
180
180
  The grid reports `grid` and `gridcell` once cell selection is on, so a test or a
181
181
  query written against `getByRole("cell")` stops resolving the moment the option
182
- is set - including when `editMode` turns it on implicitly.
182
+ is set - including when `editing` turns it on implicitly.
183
183
 
184
184
  Source: `src/docs/cell-selection.md`, and the `testing` skill.
185
185
 
@@ -218,7 +218,7 @@ Source: `src/docs/cell-selection.md` (Copy and export).
218
218
 
219
219
  | Name | Kind | Type | Default | What it does |
220
220
  | --- | --- | --- | --- | --- |
221
- | `cellSelection` | Option | `"none" \| "single" \| "range"` | `"none"`, or `"single"` under `editMode` | Turns the cursor, and the rectangle, on. |
221
+ | `cellSelection` | Option | `"none" \| "single" \| "range"` | `"none"`, or `"single"` under `editing` | Turns the cursor, and the rectangle, on. |
222
222
  | `onFocusedCellChange` | Callback | `(cell \| null) => void` | – | Follows the cursor. |
223
223
  | `cellExport` | Table prop | `TMDataGridCellExportOptions` | Nordic Excel | Separator, decimal mark, headers, file name. |
224
224
  | `ui.state.focusedCell` | UI state | `{ rowId, columnId } \| null` | `null` | The cursor. |
@@ -16,7 +16,7 @@ description: >
16
16
  metadata:
17
17
  type: core
18
18
  library: '@jielga/tmdatagrid'
19
- library_version: '2.0.0-beta.0'
19
+ library_version: '2.0.0-beta.2'
20
20
  sources:
21
21
  - 'Jielga/TMDataGrid:src/docs/columns.md'
22
22
  - 'Jielga/TMDataGrid:src/docs/column-layout.md'
@@ -69,9 +69,9 @@ column model.
69
69
  | `filter` | `TMDataGridColumnFilterOptions` | – | How this column filters. See below. |
70
70
  | `edit` | `TMDataGridColumnEditOptions` | – | How this column is edited. See below. |
71
71
 
72
- What the column **is** stays flat; what a stage **does** with it sits in that
73
- stage's namespace. `type` and `options` are the shared ground both stages read,
74
- which is why they are not inside either.
72
+ What the column **is** stays flat; what the filter panel and the edit engine do
73
+ with it sits in their namespaces. `type` and `options` are read by both stages,
74
+ which is why they are in neither.
75
75
 
76
76
  `meta.filter`:
77
77
 
@@ -99,7 +99,7 @@ meta: {
99
99
  ```
100
100
 
101
101
  `enableOrdering` lives in `meta` because column ordering is the one feature
102
- TanStack defines no column option for. `meta.edit` only acts once `editMode` is
102
+ TanStack defines no column option for. `meta.edit` only acts once `editing` is
103
103
  set: see the `editing` skill. `meta.filter` belongs to the `filtering` skill.
104
104
 
105
105
  ### Column types
@@ -246,7 +246,7 @@ asks for it.
246
246
  | Checkbox | `SELECT_COLUMN_ID` | Selection is on and the mode has checkboxes |
247
247
  | Tree | `GROUP_COLUMN_ID` | A column is grouped |
248
248
  | Details | `DETAILS_COLUMN_ID` | `renderDetails` is set |
249
- | Edit | `EDIT_COLUMN_ID` | Row mode, `onRowDelete`, or batch with `onEditCommitBatch` |
249
+ | Edit | `EDIT_COLUMN_ID` | Row mode, draft mode, or `editing.onRowDelete` |
250
250
 
251
251
  They are structural: fixed width, no column menu, and they cannot be sorted,
252
252
  filtered, resized, re-pinned or moved. The checkbox lane anchors the left pinned
@@ -350,10 +350,10 @@ columnHelper.accessor("priority", { header: "Priority", sortFn: byRank });
350
350
  Source: `@tanstack/table-core` `rowSortingFeature.types.d.ts`, and
351
351
  `src/tmdatagrid/useTMDataGrid.tsx` (the registered `sortFns`).
352
352
 
353
- ### MEDIUM Autosize measured against the whole data set
353
+ ### MEDIUM Expecting autosize to measure every row
354
354
 
355
355
  Autosizing fits the **mounted** rows plus overscan, not every row, because
356
- virtualization means the rest have no DOM to measure. A column autosized at the
356
+ virtualization leaves the rest with no DOM to measure. A column autosized at the
357
357
  top of a long list can be too narrow for a value further down.
358
358
 
359
359
  Source: `src/docs/column-layout.md` (Autosizing).
@@ -387,7 +387,7 @@ Source: `src/docs/column-layout.md` (Regions).
387
387
  | `isColumnReorderable` | Export | `(column, features) => boolean` | – | Whether this column may move at all. |
388
388
  | `autosizeColumn` | Export | `({ table, columnId, container }) => void` | – | Fits a column to its mounted content. |
389
389
  | `measureColumnContentWidth` | Export | `(args) => number` | – | The measurement behind it. |
390
- | `getColumnLabel` · `getColumnType` · `getColumnDefaultOperator` · `isControlColumn` | Exports | – | – | What the chrome reads off a column. |
390
+ | `getColumnLabel` · `getColumnType` · `getColumnDefaultOperator` · `isControlColumn` | Exports | – | – | What the built-in controls read off a column. |
391
391
  | `SELECT_COLUMN_ID` · `GROUP_COLUMN_ID` · `DETAILS_COLUMN_ID` · `EDIT_COLUMN_ID` · `ROW_NUMBER_COLUMN_ID` | Exports | ids | – | The generated lanes. |
392
392
  | `TMDataGrid.ColumnsButton` · `TMDataGrid.ColumnsPanel` | Components | – | – | Manage columns, and Reset layout. |
393
393
 
@@ -15,7 +15,7 @@ description: >
15
15
  metadata:
16
16
  type: core
17
17
  library: '@jielga/tmdatagrid'
18
- library_version: '2.0.0-beta.0'
18
+ library_version: '2.0.0-beta.2'
19
19
  sources:
20
20
  - 'Jielga/TMDataGrid:src/docs/pagination.md'
21
21
  - 'Jielga/TMDataGrid:src/docs/scrolling.md'
@@ -28,8 +28,8 @@ sources:
28
28
  ## Pagination
29
29
 
30
30
  **Off by default.** The grid renders every filtered and sorted row and relies on
31
- virtualization, which handles any row count - so paging is a choice about how
32
- the reader navigates, not a performance workaround. Three modes:
31
+ virtualization, which handles any row count. Paging is not needed for
32
+ performance. Three modes:
33
33
 
34
34
  ```tsx
35
35
  // 1. None (default). TMDataGrid.Footer renders nothing.
@@ -50,12 +50,12 @@ const grid = useTMDataGrid({
50
50
  ```
51
51
 
52
52
  Initial page size is 25, through `initialState.pagination`. `enablePagination`
53
- is one of the two switches the grid defines itself, and the one switch that
53
+ is defined by the grid rather than by TanStack, and is the only grid option that
54
54
  defaults to off.
55
55
 
56
56
  `TMDataGrid.Footer` takes a `renderPagination` slot handed three things: the
57
- `state` the pager shows, the `actions` it can take, and `Controls` - the
58
- built-in pieces, already wired.
57
+ `state` the pager shows, the `actions` it can take, and `Controls`, the built-in
58
+ pieces, already wired.
59
59
 
60
60
  ```tsx
61
61
  <TMDataGrid.Footer
@@ -74,35 +74,34 @@ built-in pieces, already wired.
74
74
  />
75
75
  ```
76
76
 
77
- `Controls.PageSize`, `Controls.Range` and `Controls.Pager` are exactly what the
78
- default footer renders, in that order - so a custom layout keeps the parts it
79
- likes rather than rebuilding them to look the same, greying out under a
80
- suspended pager included.
77
+ `Controls.PageSize`, `Controls.Range` and `Controls.Pager` are what the default
78
+ footer renders, in that order, so a custom layout can keep the parts it wants
79
+ instead of rebuilding them. They behave exactly as before, including greying out
80
+ under a suspended pager.
81
81
 
82
82
  `state` carries `pageIndex`, `pageSize`, `pageCount`, `rowCount`,
83
83
  `canPreviousPage`, `canNextPage`, `from`, `to` and `isPagingActive`. `actions`
84
84
  carries `setPageIndex`, `setPageSize`, `previousPage`, `nextPage`, `firstPage`
85
85
  and `lastPage`. `getTMDataGridPaginationApi(table)` returns the same
86
- `{ state, actions }` outside the Footer; `Controls` are bound to the grid
87
- context, so they exist only inside the slot.
86
+ `{ state, actions }` outside the Footer. `Controls` is bound to the grid
87
+ context, so those components work only inside the slot.
88
88
 
89
- Grouping suspends the pager and wins: it greys out and the range becomes
90
- `Grouped · all N rows`. `isPagingActive(table, features)` is the live state -
91
- whether the pager is slicing anything right now - while
89
+ Grouping suspends the pager: it greys out and the range becomes
90
+ `Grouped · all N rows`. `isPagingActive(table, features)` is the live state,
91
+ whether the pager is slicing anything right now, while
92
92
  `getGridCapabilities(...).canPaginate` is the configuration. The two differ
93
- exactly while a grouping is active.
93
+ while a grouping is active.
94
94
 
95
95
  ## Scrolling
96
96
 
97
- Virtualization is **always on**. There is no flag, no threshold and no "enable
98
- for large data sets": only the rows within the viewport plus a small overscan
99
- are ever mounted.
97
+ Virtualization is **always on**. There is no flag and no threshold: only the
98
+ rows within the viewport plus a small overscan are mounted.
100
99
 
101
- `overscan` (default `6`) is the one knob - raise it if a fast scroll flashes
100
+ `overscan` (default `6`) is the only setting: raise it if a fast scroll flashes
102
101
  blank rows, lower it when rows are expensive. Row height comes from
103
102
  `meta.rowHeight`, or from `size` when that is not set, and rows are **fixed
104
- height**, so the virtualizer's estimate is exact and the scrollbar is honest.
105
- Row details are the exception: a row showing a panel is measured after it
103
+ height**, so the virtualizer's estimate is exact and the scrollbar does not
104
+ drift. Row details are the exception: a row showing a panel is measured after it
106
105
  mounts.
107
106
 
108
107
  ```tsx
@@ -117,20 +116,21 @@ const grid = useTMDataGrid({
117
116
  grid.scrollToRow({ rowId: "42", align: "center" });
118
117
  ```
119
118
 
120
- `scrollToRow` exists because the row may not be mounted, which is exactly what
121
- `element.scrollIntoView()` cannot handle. `align` is `"start"`, `"center"`,
122
- `"end"` or `"auto"`. `scrollerRef` is the scroll container itself.
119
+ `scrollToRow` exists because the target row may not be mounted, and
120
+ `element.scrollIntoView()` cannot find an element that is not rendered. `align`
121
+ is `"start"`, `"center"`, `"end"` or `"auto"`. `scrollerRef` is the scroll
122
+ container itself.
123
123
 
124
124
  `TMDataGrid.Table` reports arrivals at each edge - `onScrollToTop`,
125
125
  `onScrollToBottom`, `onScrollToLeft`, `onScrollToRight` - firing **once** on
126
- arrival rather than on every scroll event. For loading more rows prefer
127
- `onReachEnd` (the `server-side` skill): it fires rows early rather than at the
128
- very bottom, and latches per row count so a pending fetch is never asked twice.
126
+ arrival rather than on every scroll event. For loading more rows use
127
+ `onReachEnd` (the `server-side` skill) instead: it fires a number of rows before
128
+ the bottom, and latches per row count so a pending fetch is not requested twice.
129
129
 
130
- Two depth cues are scroll-driven animations on the compositor, with no scroll
131
- listener and no React render: a shadow under the sticky header once rows scroll
132
- beneath it, and a band beside a pinned lane while it is actually covering
133
- something.
130
+ Two depth cues are scroll-driven animations run on the compositor, with no
131
+ scroll listener and no React render: a shadow under the sticky header once rows
132
+ scroll beneath it, and a band beside a pinned lane while it is covering
133
+ content.
134
134
 
135
135
  ## Empty states
136
136
 
@@ -138,12 +138,12 @@ There are four ways to have nothing to show, and an empty body shows exactly one
138
138
  of them, in this order:
139
139
 
140
140
  1. **Loading** - `meta.loading` is true: a centred loader. A grid that is
141
- fetching never claims to be empty.
142
- 2. **Entry rows** - an open entry row from `edit.addRow()`: only the entry
143
- block, with no message competing with the form.
141
+ fetching never reports itself as empty.
142
+ 2. **Entry rows** - an open entry row from `edit.addRow()`: the entry block
143
+ only, with no message beside the form.
144
144
  3. **`renderEmptyState`** - your node, centred where the message would be.
145
- 4. **Filtered-empty** - a filter or search is active: `labels.noResults`,
146
- because this emptiness is the reader's own doing.
145
+ 4. **Filtered-empty** - a filter or search is active: `labels.noResults`, since
146
+ clearing the filter will bring rows back.
147
147
  5. **Truly-empty** - no data at all: `labels.noRows`.
148
148
 
149
149
  `renderEmptyState` replaces the last two with one render prop, and
@@ -163,12 +163,12 @@ of them, in this order:
163
163
  />
164
164
  ```
165
165
 
166
- An empty grid is where a reader is most likely to be stuck, so it is worth
167
- giving them the action that unsticks them.
166
+ An empty grid is a good place to offer the action that fills it, such as
167
+ clearing the filters or creating the first record.
168
168
 
169
169
  The body's loading state only appears while the grid is **empty**. A
170
- server-driven grid refetching with rows still on screen keeps showing them;
171
- `TMDataGrid.LoadingIndicator` is the signal for that case, a small spinner while
170
+ server-driven grid refetching with rows still on screen keeps showing them.
171
+ `TMDataGrid.LoadingIndicator` covers that case: a small spinner while
172
172
  `meta.loading` is true. `TMDataGrid.SummaryCount` shows visible rows out of
173
173
  total, where the total is `meta.totalRowCount` when provided and the pre-filtered
174
174
  count otherwise.
@@ -178,17 +178,17 @@ count otherwise.
178
178
  ### CRITICAL Turning pagination on to make a large grid fast
179
179
 
180
180
  Virtualization is already unconditional, so paging a 200 000-row grid changes
181
- nothing about rendering cost - it only takes navigation away from the reader.
182
- Reach for it when the reader should move page by page, not for performance.
181
+ nothing about rendering cost. It only changes how users navigate. Enable it when
182
+ they should move page by page, not for performance.
183
183
 
184
184
  Source: `src/docs/pagination.md`, `src/docs/scrolling.md`.
185
185
 
186
186
  ### CRITICAL A variable row height
187
187
 
188
188
  The virtualizer needs one number, and rows are fixed height so the scrollbar
189
- stays honest. Styling a taller row through CSS leaves the measurement and the
189
+ stays accurate. Styling a taller row through CSS leaves the measurement and the
190
190
  render disagreeing: rows overlap or gaps open as you scroll, and the effect
191
- depends on scroll position, so it looks intermittent.
191
+ depends on scroll position, so it appears intermittent.
192
192
 
193
193
  Wrong:
194
194
 
@@ -215,16 +215,16 @@ Correct:
215
215
  grid.scrollToRow({ rowId: "4000", align: "center" });
216
216
  ```
217
217
 
218
- `scrollToRow` returns `false` when the row is not in the current view - filtered
219
- out, on another page, or an id matching no row - and nothing scrolled.
218
+ `scrollToRow` returns `false` when the row is not in the current view (filtered
219
+ out, on another page, or an id matching no row) and nothing scrolled.
220
220
 
221
221
  Source: `src/docs/scrolling.md` (Scrolling to a row).
222
222
 
223
223
  ### HIGH Loading more rows from `onScrollToBottom`
224
224
 
225
- It fires at the very bottom, so the reader waits at the end of the list for the
226
- fetch. `onReachEnd` fires rows early and latches per row count, so a pending
227
- fetch is never asked twice.
225
+ It fires at the very bottom, so the user waits at the end of the list for the
226
+ fetch. `onReachEnd` fires a number of rows earlier and latches per row count, so
227
+ a pending fetch is not requested twice.
228
228
 
229
229
  Source: `src/docs/scrolling.md` (Edge callbacks).
230
230
 
@@ -249,7 +249,7 @@ Source: `src/docs/loading-and-empty.md` (Counting what is there).
249
249
 
250
250
  ### MEDIUM Rendering an empty message while data is loading
251
251
 
252
- The order exists so a fetching grid never claims to be empty. A hand-rolled
252
+ The order exists so a fetching grid never reports itself as empty. A hand-rolled
253
253
  `data.length === 0 ? <Empty /> : <Grid />` outside the grid flashes "No rows to
254
254
  show" on every load.
255
255
 
@@ -281,7 +281,7 @@ Source: `src/docs/pagination.md` (Grouping suspends it).
281
281
  | `TMDataGrid.Footer` | Component | `pageSizeOptions`, `pagination` | `[10, 25, 50, 100]` | The footer bar. Renders nothing when paging is off. |
282
282
  | `Footer` `renderPagination` | Slot | `({ state, actions, Controls }) => ReactNode` | Built-in pager | Replaces the pager, and hands over its pieces. |
283
283
  | `getTMDataGridPaginationApi` | Export | `(table) => { state, actions }` | – | The pager API, outside the Footer. |
284
- | `TMDataGridPaginationState` · `TMDataGridPaginationActions` · `TMDataGridPaginationControls` | Exports | types | – | The three halves of the slot argument. |
284
+ | `TMDataGridPaginationState` · `TMDataGridPaginationActions` · `TMDataGridPaginationControls` | Exports | types | – | The three parts of the slot argument. |
285
285
  | `isPagingActive` | Export | `(table, features) => boolean` | – | Whether the pager is slicing anything right now. |
286
286
  | `overscan` | Option | `number` | `6` | Rows kept mounted beyond each edge of the viewport. |
287
287
  | `meta.rowHeight` | Option | `number` | From `size` | Row height in pixels. The virtualizer needs a number. |
@@ -289,7 +289,7 @@ Source: `src/docs/pagination.md` (Grouping suspends it).
289
289
  | `scrollerRef` | Hook return | `RefObject<HTMLDivElement>` | – | The scroll container. |
290
290
  | `onScrollToTop` · `onScrollToBottom` · `onScrollToLeft` · `onScrollToRight` | Table props | `() => void` | – | Fire once on arriving at that edge. |
291
291
  | `TMDataGridScrollAlign` | Export | `"start" \| "center" \| "end" \| "auto"` | – | The `align` argument. |
292
- | `meta.loading` | Option | `boolean` | `false` | A fetch is in flight. Outranks every empty message. |
292
+ | `meta.loading` | Option | `boolean` | `false` | A fetch is in flight. Takes precedence over every empty message. |
293
293
  | `meta.noResultsLabel` | Option | `string` | `labels.noResults` | The filtered-empty message, without a render prop. |
294
294
  | `meta.totalRowCount` | Option | `number` | Pre-filtered count | The total `SummaryCount` reports. |
295
295
  | `renderEmptyState` | Table prop | `({ hasActiveFilters, table }) => ReactNode` | – | Replaces both built-in empty messages. |