@jielga/tmdatagrid 2.1.4 → 2.3.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 (57) hide show
  1. package/dist/index.d.ts +78 -5
  2. package/dist/index.js +2194 -1992
  3. package/dist/index.js.map +1 -1
  4. package/dist/styles.css +1 -1
  5. package/docs/cell-selection.md +1 -1
  6. package/docs/column-layout.md +13 -8
  7. package/docs/columns.md +59 -3
  8. package/docs/components.md +1 -0
  9. package/docs/export.md +2 -2
  10. package/docs/filtering.md +108 -15
  11. package/docs/getting-started.md +40 -0
  12. package/docs/grouping.md +9 -6
  13. package/docs/loading-and-empty.md +29 -9
  14. package/docs/localization.md +130 -3
  15. package/docs/menu.md +1 -1
  16. package/docs/pagination.md +26 -7
  17. package/docs/persistence.md +101 -2
  18. package/docs/row-details.md +41 -4
  19. package/docs/row-interaction.md +2 -2
  20. package/docs/row-selection.md +6 -7
  21. package/docs/row-styling.md +17 -0
  22. package/docs/scrolling.md +1 -1
  23. package/docs/server-side.md +3 -1
  24. package/docs/testing.md +18 -0
  25. package/docs/use-tm-data-grid.md +75 -36
  26. package/package.json +1 -1
  27. package/skills/appearance/SKILL.md +6 -4
  28. package/skills/appearance/references/labels.md +126 -0
  29. package/skills/cell-selection/SKILL.md +2 -2
  30. package/skills/columns/SKILL.md +44 -43
  31. package/skills/columns/references/columns-api.md +16 -1
  32. package/skills/data/SKILL.md +77 -50
  33. package/skills/editing/SKILL.md +1 -1
  34. package/skills/filtering/SKILL.md +94 -60
  35. package/skills/filtering/references/custom-filters.md +118 -0
  36. package/skills/getting-started/SKILL.md +37 -2
  37. package/skills/grouping/SKILL.md +16 -9
  38. package/skills/migrating-to-2/SKILL.md +1 -1
  39. package/skills/options/SKILL.md +206 -55
  40. package/skills/rows/SKILL.md +70 -39
  41. package/skills/server-side/SKILL.md +1 -1
  42. package/skills/testing/SKILL.md +12 -1
  43. package/skills/testing-components/SKILL.md +1 -1
  44. package/skills/testing-editing/SKILL.md +1 -1
  45. package/src/components/TMDataGridFilterPills.module.css +9 -7
  46. package/src/components/TMDataGridTable.module.css +15 -6
  47. package/src/components/TMDataGridTable.tsx +253 -41
  48. package/src/components/sticky.module.css +27 -7
  49. package/src/core/autosize.ts +19 -7
  50. package/src/core/cellRange.ts +11 -2
  51. package/src/core/colSpan.ts +78 -0
  52. package/src/core/columnUtils.ts +7 -0
  53. package/src/core/filterControls.ts +8 -0
  54. package/src/core/filterOperators.ts +29 -1
  55. package/src/core/persistence.ts +322 -26
  56. package/src/index.ts +5 -0
  57. package/src/useTMDataGrid.tsx +133 -14
package/dist/styles.css CHANGED
@@ -1,2 +1,2 @@
1
- ._root_wlqnb_8[data-size=xs]{--dg-row-height:34px;--dg-header-height:32px;--dg-font-size:var(--mantine-font-size-xs);--dg-padding:6px}._root_wlqnb_8[data-size=sm]{--dg-row-height:42px;--dg-header-height:38px;--dg-font-size:var(--mantine-font-size-sm);--dg-padding:8px}._root_wlqnb_8[data-size=md]{--dg-row-height:52px;--dg-header-height:44px;--dg-font-size:var(--mantine-font-size-sm);--dg-padding:10px}._root_wlqnb_8[data-size=lg]{--dg-row-height:62px;--dg-header-height:52px;--dg-font-size:var(--mantine-font-size-md);--dg-padding:14px}._root_wlqnb_8[data-size=xl]{--dg-row-height:72px;--dg-header-height:60px;--dg-font-size:var(--mantine-font-size-lg);--dg-padding:18px}._root_wlqnb_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-header-font-size:var(--dg-font-size);--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-focused-cell:1;--dg-z-pinned-cell:2;--dg-z-pinned-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_a5do5_1{flex-direction:column;width:280px;display:flex}._columnsPanelSearch_a5do5_7{padding:var(--mantine-spacing-sm)}._columnsPanelList_a5do5_11{gap:var(--mantine-spacing-sm);padding:var(--mantine-spacing-sm);flex-direction:column;display:flex}._columnsPanelSearch_a5do5_7+* ._columnsPanelList_a5do5_11{padding-top:0}._columnsPanelFooter_a5do5_22{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_1qtpn_2{flex-direction:column;flex:1;min-height:0;display:flex;position:relative}._tableWrapper_1qtpn_2[data-dg-filter-sidebar]{flex-direction:row}._scrollContainer_1qtpn_17{flex:1;min-width:0;min-height:0;position:relative;overflow:auto;container-type:inline-size}._grid_1qtpn_29{align-content:start;min-height:100%;display:grid}._tabGuard_1qtpn_44{outline:none;width:0;height:0;position:absolute;overflow:hidden}._headerRow_1qtpn_54{grid-column:1/-1;grid-template-columns:subgrid;z-index:var(--dg-z-header,6);display:grid;position:sticky;top:0}._bodyRow_1qtpn_63{--row-bg:var(--mantine-color-body);grid-column:1/-1;grid-template-columns:subgrid;align-items:stretch;display:grid}._bodyRow_1qtpn_63[data-striped=true]{--row-bg:var(--dg-row-striped-bg)}._bodyRow_1qtpn_63[data-new=true]{--row-bg:var(--dg-row-new-bg,var(--mantine-color-body))}._bodyRow_1qtpn_63:hover{--row-bg:var(--mantine-color-default-hover)}._bodyRow_1qtpn_63[data-grouped=true]{--row-bg:var(--dg-row-group-bg)}._bodyRow_1qtpn_63[data-context-menu=true]{--row-bg:var(--mantine-color-default-hover)}._bodyRow_1qtpn_63[data-selected-bg=true]{--row-bg:var(--dg-row-selected-bg)}._bodyRow_1qtpn_63[data-highlighted=true]{--row-bg:var(--dg-row-highlight-bg)}._bodyRow_1qtpn_63[data-selects-on-click=true]{-webkit-user-select:none;user-select:none}._bodyRow_1qtpn_63:focus-visible{outline:2px solid var(--mantine-primary-color-filled);outline-offset:-2px}._bodyCell_1qtpn_137{padding:0 var(--dg-padding);border-bottom:1px solid var(--mantine-color-default-border);background-color:var(--mantine-color-body);background-image:linear-gradient(var(--row-bg), var(--row-bg));font-size:var(--dg-font-size);align-items:center;display:flex}._bodyCell_1qtpn_137[data-control-column=true]{padding:0}._grid_1qtpn_29[data-range-dragging]{-webkit-user-select:none;user-select:none}._bodyCell_1qtpn_137[data-selected=true]{--dg-range-tint:color-mix(in srgb, var(--mantine-primary-color-filled) 14%, var(--row-bg));background-image:linear-gradient(var(--dg-range-tint), var(--dg-range-tint));--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_1qtpn_137[data-edge-top=true]{--dg-edge-top:var(--mantine-primary-color-filled)}._bodyCell_1qtpn_137[data-edge-bottom=true]{--dg-edge-bottom:var(--mantine-primary-color-filled)}._bodyCell_1qtpn_137[data-edge-left=true]{--dg-edge-left:var(--mantine-primary-color-filled)}._bodyCell_1qtpn_137[data-edge-right=true]{--dg-edge-right:var(--mantine-primary-color-filled)}._bodyCell_1qtpn_137[data-focused=true]{outline:2px solid var(--mantine-primary-color-filled);outline-offset:-2px}._cellContent_1qtpn_135{white-space:nowrap;text-overflow:ellipsis;min-width:0;overflow:hidden}._bodyCell_1qtpn_137[data-editing]{padding:0 calc(var(--dg-padding) / 2)}._cellEditor_1qtpn_244{align-items:center;gap:2px;width:100%;min-width:0;display:flex}._cellEditorField_1qtpn_254{flex:auto;align-items:center;min-width:0;display:flex}._cellEditorActions_1qtpn_262{flex:none;align-items:center;display:flex}._bodyCell_1qtpn_137[data-dirty],._bodyCell_1qtpn_137[data-invalid],._entryCell_1qtpn_275[data-invalid]{position:relative}._bodyCell_1qtpn_137[data-dirty]:after,._bodyCell_1qtpn_137[data-invalid]:after,._entryCell_1qtpn_275[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_1qtpn_137[data-invalid]:after,._entryCell_1qtpn_275[data-invalid]:after{border-top-color:var(--mantine-color-error);border-right-color:var(--mantine-color-error)}._bodyCell_1qtpn_137[data-align=right]{justify-content:flex-end}._bodyCell_1qtpn_137[data-align=center]{justify-content:center}._bodyCell_1qtpn_137[data-align=left] ._cellContent_1qtpn_135{flex:auto}._detailsCell_1qtpn_317{border-bottom:1px solid var(--mantine-color-default-border);background-color:var(--mantine-color-body);background-image:linear-gradient(var(--row-bg), var(--row-bg));padding:var(--dg-padding);-webkit-user-select:text;user-select:text;cursor:auto;grid-column:1/-1}._messageRow_1qtpn_23{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_1qtpn_63[data-deleted=true]{opacity:.55;text-decoration:line-through}._bodyRow_1qtpn_63[data-deleted=true] ._bodyCell_1qtpn_137:not([data-control-column=true]){pointer-events:none}._entryBlock_1qtpn_369{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_1qtpn_378{grid-column:1/-1;grid-template-columns:subgrid;display:grid}._pinnedTopBlock_1qtpn_391{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_1qtpn_391>:last-child>[role=cell]{border-bottom:2px solid var(--mantine-color-default-border)}._pinnedBottomBlock_1qtpn_404{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_1qtpn_404>:first-child>[role=cell]{border-top:2px solid var(--mantine-color-default-border)}._entryCell_1qtpn_275{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_1qtpn_275[data-control-column=true]{justify-content:center;padding:0}._entryRow_1qtpn_378[data-committed=true] ._entryCell_1qtpn_275{padding:0 var(--dg-padding);border-bottom:1px solid var(--mantine-color-default-border);background-image:linear-gradient(var(--dg-row-new-bg,transparent), var(--dg-row-new-bg,transparent))}._entryRow_1qtpn_378[data-committed=true] ._entryCell_1qtpn_275[data-control-column=true]{padding:0}._rowStateIndicator_1qtpn_453{width:22px;height:22px;color:var(--mantine-color-dimmed);flex:none;justify-content:center;align-items:center;display:inline-flex}._rowStateIndicator_1qtpn_453[data-error]{color:var(--mantine-color-error)}._entryCell_1qtpn_275[data-align=right]{justify-content:flex-end}._entryCell_1qtpn_275[data-align=center]{justify-content:center}._matchHighlight_1qtpn_479{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_1qtpn_500{grid-column:1/-1;grid-template-columns:subgrid;top:calc(100% - var(--dg-summary-height,0px));z-index:var(--dg-z-summary-row,4);display:grid;position:sticky;bottom:0}._summaryCell_1qtpn_510{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_1qtpn_510[data-control-column=true]{padding:0}._summaryCell_1qtpn_510[data-align=right]{justify-content:flex-end}._summaryCell_1qtpn_510[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_wm77f_4{gap:var(--mantine-spacing-xs);flex-direction:column;display:flex}._filterRow_wm77f_12{align-items:flex-end;gap:var(--mantine-spacing-xs);display:flex}._filterRow_wm77f_12[data-layout=stacked]{align-items:end;gap:6px var(--mantine-spacing-xs);padding:var(--mantine-spacing-xs);border:1px solid var(--mantine-color-default-border);border-radius:var(--mantine-radius-sm);grid-template-columns:1fr auto;display:grid}._filterRow_wm77f_12[data-layout=stacked] ._filterRemove_wm77f_32{grid-area:1/2}._filterRow_wm77f_12[data-layout=stacked] ._filterColumnSlot_wm77f_37{grid-area:1/1;min-width:0}._filterRow_wm77f_12[data-layout=stacked] ._filterOperatorSlot_wm77f_43,._filterRow_wm77f_12[data-layout=stacked] ._filterValueSlot_wm77f_44{grid-column:1/-1;min-width:0}._filterPanelFooter_wm77f_49{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_1jwnm_16:after,._stickyRight_1jwnm_17:after{content:"";pointer-events:none;width:8px;position:absolute;top:0;bottom:0}._stickyLeft_1jwnm_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_1jwnm_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_1jwnm_16:after,._stickyRight_1jwnm_17:after{--dg-sticky-edge-range:20px;opacity:0;animation-timing-function:linear;animation-fill-mode:both;animation-timeline:scroll(inline)}._stickyLeft_1jwnm_16:after{animation-name:_stickyEdgeIn_1jwnm_1;animation-range:0 var(--dg-sticky-edge-range)}._stickyRight_1jwnm_17:after{animation-name:_stickyEdgeOut_1jwnm_1;animation-range:calc(100% - var(--dg-sticky-edge-range)) 100%}}@keyframes _stickyEdgeIn_1jwnm_1{0%{opacity:0}to{opacity:1}}@keyframes _stickyEdgeOut_1jwnm_1{0%{opacity:1}to{opacity:0}}._headerBoundary_1jwnm_120: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()){._headerBoundary_1jwnm_120:after{animation:linear both _dgHeaderShadow_1jwnm_1;animation-timeline:scroll();animation-range:0 24px}}@keyframes _dgHeaderShadow_1jwnm_1{0%{opacity:0}to{opacity:1}}._surfaceHeader_10bcx_5{justify-content:space-between;align-items:center;gap:var(--mantine-spacing-md);padding-bottom:var(--mantine-spacing-xs);display:flex}._filterPopup_10bcx_18{top:calc(var(--dg-header-stack-height,var(--dg-header-height)) + 2px);left:var(--mantine-spacing-md);z-index:var(--dg-z-filter-popup,20);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}._filterSidebar_10bcx_33{min-width:0;padding:var(--mantine-spacing-sm);background-color:var(--mantine-color-body);border-left:1px solid var(--mantine-color-default-border);flex-direction:column;flex:none;display:flex}._filterSidebar_10bcx_33[data-side=left]{border-left:none;border-right:1px solid var(--mantine-color-default-border);order:-1}._filterSidebarBody_10bcx_51{flex:1;min-height:0}._headerFilterRow_h1geg_9{grid-column:1/-1;grid-template-columns:subgrid;top:var(--dg-header-height);z-index:var(--dg-z-header,6);display:grid;position:sticky}._headerFilterCell_h1geg_18{min-width:0;padding:calc(var(--dg-padding) / 2);background-color:var(--mantine-color-body);border-bottom:1px solid var(--mantine-color-default-border);align-items:center;gap:2px;display:flex;overflow:hidden}._headerFilterCell_h1geg_18[data-control-column=true]{padding:0}._headerFilterValue_h1geg_39{flex:1;min-width:0}._headerFilterOperator_h1geg_44{flex:none}._operatorItemActive_h1geg_48{color:var(--mantine-primary-color-filled);font-weight:600}._headerCell_18ma1_1{padding:0 var(--dg-padding);min-width:0;min-height:var(--dg-header-height);font-weight:600;font-size:var(--dg-header-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_18ma1_1[data-align=right]{justify-content:flex-end}._headerCell_18ma1_1[data-align=center]{justify-content:center}._headerCell_18ma1_1[data-control-column=true]{padding:0}._headerCellFiltered_18ma1_35 ._headerTitle_18ma1_35{color:var(--mantine-primary-color-filled)}._headerTitle_18ma1_35{text-overflow:ellipsis;white-space:nowrap;flex:auto;min-width:0;overflow:hidden}._headerCell_18ma1_1[data-align=right] ._headerTitle_18ma1_35{text-align:right}._headerCell_18ma1_1[data-align=center] ._headerTitle_18ma1_35{justify-content:center;display:flex}._headerActions_18ma1_58{flex-shrink:0;align-items:center;gap:0;margin-left:auto;display:flex}._headerCell_18ma1_1[data-align=right] ._headerActions_18ma1_58{order:-1;margin-left:0;margin-right:auto}._headerAction_18ma1_58{display:none}._headerCell_18ma1_1:hover ._headerAction_18ma1_58,._headerCell_18ma1_1:focus-within ._headerAction_18ma1_58,._headerAction_18ma1_58[data-pinned-visible=true]{display:flex}._headerCellSortable_18ma1_87{cursor:pointer}._sortAction_18ma1_93:not([data-sorted]){opacity:.55}._sortAction_18ma1_93:not([data-sorted]):hover{opacity:1}._sortIndex_18ma1_103{color:var(--mantine-color-dimmed);text-align:center;min-width:10px;font-size:10px;font-weight:600;line-height:1}._headerCellDragging_18ma1_115{opacity:.4}._headerCellDropBefore_18ma1_119:before,._headerCellDropAfter_18ma1_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_18ma1_119:before{left:0}._headerCellDropAfter_18ma1_120:before{right:0}._columnSeparator_18ma1_140{z-index:1;justify-content:center;width:10px;height:50%;display:flex;position:absolute;top:25%;right:0;transform:translate(50%)}._headerCell_18ma1_1:last-child ._columnSeparator_18ma1_140{justify-content:flex-end;transform:none}._columnSeparatorResizable_18ma1_162{cursor:col-resize;touch-action:none}._columnSeparator_18ma1_140:after{content:"";background-color:#0000;width:1px;height:100%;transition:background-color .12s,width .12s}._headerCell_18ma1_1:hover ._columnSeparator_18ma1_140:after,._headerCell_18ma1_1:focus-within ._columnSeparator_18ma1_140:after{background-color:var(--mantine-color-default-border)}._columnSeparatorResizable_18ma1_162:hover:after,._columnSeparatorActive_18ma1_185:after{background-color:var(--mantine-primary-color-filled);width:2px}._menuItemActive_18ma1_191{background-color:var(--mantine-color-default-hover);font-weight:600}._toolbar_gwk05_1{align-items:center;gap:var(--mantine-spacing-xs);padding:6px var(--mantine-spacing-xs);flex-shrink:0;min-height:42px;display:flex}._toolbar_gwk05_1[data-with-bottom-border]{border-bottom:1px solid var(--mantine-color-default-border)}._toolbarSpacer_gwk05_15{flex:1}._summaryCount_gwk05_19{white-space:nowrap}._title_199r2_1{font-weight:500}._list_199r2_5{border:1px solid var(--mantine-color-default-border);border-radius:var(--mantine-radius-default);flex-direction:column;display:flex;overflow:hidden}._listHeader_199r2_13{justify-content:space-between;align-items:center;gap:var(--mantine-spacing-sm);padding:var(--mantine-spacing-xs) var(--mantine-spacing-sm);background-color:var(--mantine-color-default-hover);border-bottom:1px solid var(--mantine-color-default-border);display:flex}._headerLabel_199r2_23{cursor:pointer;font-weight:500}._rows_199r2_28{padding:calc(var(--mantine-spacing-xs) / 2) 0;flex-direction:column;display:flex}._row_199r2_28{padding:0 var(--mantine-spacing-sm)}._row_199r2_28:hover{background-color:var(--mantine-color-default-hover)}._rowBody_199r2_45{align-items:center}._rowLabelWrapper_199r2_49{flex:1;min-width:0}._rowLabel_199r2_49{justify-content:space-between;align-items:center;gap:var(--mantine-spacing-sm);padding-block:calc(var(--mantine-spacing-xs) * .75);cursor:pointer;display:flex}._rowText_199r2_63{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}._rowHint_199r2_69{font-size:var(--mantine-font-size-xs);color:var(--mantine-color-dimmed);flex:none}._empty_199r2_75{padding:var(--mantine-spacing-xs) var(--mantine-spacing-sm)}
1
+ ._root_wlqnb_8[data-size=xs]{--dg-row-height:34px;--dg-header-height:32px;--dg-font-size:var(--mantine-font-size-xs);--dg-padding:6px}._root_wlqnb_8[data-size=sm]{--dg-row-height:42px;--dg-header-height:38px;--dg-font-size:var(--mantine-font-size-sm);--dg-padding:8px}._root_wlqnb_8[data-size=md]{--dg-row-height:52px;--dg-header-height:44px;--dg-font-size:var(--mantine-font-size-sm);--dg-padding:10px}._root_wlqnb_8[data-size=lg]{--dg-row-height:62px;--dg-header-height:52px;--dg-font-size:var(--mantine-font-size-md);--dg-padding:14px}._root_wlqnb_8[data-size=xl]{--dg-row-height:72px;--dg-header-height:60px;--dg-font-size:var(--mantine-font-size-lg);--dg-padding:18px}._root_wlqnb_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-header-font-size:var(--dg-font-size);--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-focused-cell:1;--dg-z-pinned-cell:2;--dg-z-pinned-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_a5do5_1{flex-direction:column;width:280px;display:flex}._columnsPanelSearch_a5do5_7{padding:var(--mantine-spacing-sm)}._columnsPanelList_a5do5_11{gap:var(--mantine-spacing-sm);padding:var(--mantine-spacing-sm);flex-direction:column;display:flex}._columnsPanelSearch_a5do5_7+* ._columnsPanelList_a5do5_11{padding-top:0}._columnsPanelFooter_a5do5_22{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_34o3j_2{flex-direction:column;flex:1;min-height:0;display:flex;position:relative}._tableWrapper_34o3j_2[data-dg-filter-sidebar]{flex-direction:row}._scrollContainer_34o3j_17{flex:1;min-width:0;min-height:0;position:relative;overflow:auto;container-type:inline-size}._grid_34o3j_29{align-content:start;min-height:100%;display:grid}._tabGuard_34o3j_44{outline:none;width:0;height:0;position:absolute;overflow:hidden}._headerRow_34o3j_54{grid-column:1/-1;grid-template-columns:subgrid;z-index:var(--dg-z-header,6);display:grid;position:sticky;top:0}._bodyRow_34o3j_63{--row-bg:var(--mantine-color-body);grid-column:1/-1;grid-template-columns:subgrid;align-items:stretch;display:grid}._bodyRow_34o3j_63[data-striped=true]{--row-bg:var(--dg-row-striped-bg)}._bodyRow_34o3j_63[data-new=true]{--row-bg:var(--dg-row-new-bg,var(--mantine-color-body))}._bodyRow_34o3j_63:where([data-highlight-on-hover=true]):hover{--row-bg:var(--mantine-color-default-hover)}._bodyRow_34o3j_63[data-grouped=true]{--row-bg:var(--dg-row-group-bg)}._bodyRow_34o3j_63[data-context-menu=true]{--row-bg:var(--mantine-color-default-hover)}._bodyRow_34o3j_63[data-selected-bg=true]{--row-bg:var(--dg-row-selected-bg)}._bodyRow_34o3j_63[data-highlighted=true]{--row-bg:var(--dg-row-highlight-bg)}._bodyRow_34o3j_63[data-selects-on-click=true]{-webkit-user-select:none;user-select:none}._bodyRow_34o3j_63:focus-visible{outline:2px solid var(--mantine-primary-color-filled);outline-offset:-2px}._bodyCell_34o3j_143{padding:0 var(--dg-padding);border-bottom:1px solid var(--mantine-color-default-border);background-color:var(--mantine-color-body);background-image:linear-gradient(var(--row-bg), var(--row-bg));font-size:var(--dg-font-size);align-items:center;display:flex}._bodyCell_34o3j_143[data-control-column=true]{padding:0}._grid_34o3j_29[data-range-dragging]{-webkit-user-select:none;user-select:none}._bodyCell_34o3j_143[data-selected=true]{--dg-range-tint:color-mix(in srgb, var(--mantine-primary-color-filled) 14%, var(--row-bg));background-image:linear-gradient(var(--dg-range-tint), var(--dg-range-tint));--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_34o3j_143[data-edge-top=true]{--dg-edge-top:var(--mantine-primary-color-filled)}._bodyCell_34o3j_143[data-edge-bottom=true]{--dg-edge-bottom:var(--mantine-primary-color-filled)}._bodyCell_34o3j_143[data-edge-left=true]{--dg-edge-left:var(--mantine-primary-color-filled)}._bodyCell_34o3j_143[data-edge-right=true]{--dg-edge-right:var(--mantine-primary-color-filled)}._bodyCell_34o3j_143[data-focused=true]{outline:2px solid var(--mantine-primary-color-filled);outline-offset:-2px}._cellContent_34o3j_141{white-space:nowrap;text-overflow:ellipsis;min-width:0;overflow:hidden}._bodyCell_34o3j_143[data-editing]{padding:0 calc(var(--dg-padding) / 2)}._cellEditor_34o3j_250{align-items:center;gap:2px;width:100%;min-width:0;display:flex}._cellEditorField_34o3j_260{flex:auto;align-items:center;min-width:0;display:flex}._cellEditorActions_34o3j_268{flex:none;align-items:center;display:flex}._bodyCell_34o3j_143[data-dirty],._bodyCell_34o3j_143[data-invalid],._entryCell_34o3j_281[data-invalid]{position:relative}._bodyCell_34o3j_143[data-dirty]:after,._bodyCell_34o3j_143[data-invalid]:after,._entryCell_34o3j_281[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_34o3j_143[data-invalid]:after,._entryCell_34o3j_281[data-invalid]:after{border-top-color:var(--mantine-color-error);border-right-color:var(--mantine-color-error)}._bodyCell_34o3j_143[data-align=right]{justify-content:flex-end}._bodyCell_34o3j_143[data-align=center]{justify-content:center}._bodyCell_34o3j_143[data-align=left] ._cellContent_34o3j_141{flex:auto}._detailsCell_34o3j_323{border-bottom:1px solid var(--mantine-color-default-border);background-color:var(--mantine-color-body);background-image:linear-gradient(var(--row-bg), var(--row-bg));-webkit-user-select:text;user-select:text;cursor:auto;grid-column:1/-1}._messageRow_34o3j_23{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_34o3j_63[data-deleted=true]{opacity:.55;text-decoration:line-through}._bodyRow_34o3j_63[data-deleted=true] ._bodyCell_34o3j_143:not([data-control-column=true]){pointer-events:none}._entryBlock_34o3j_377{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_34o3j_386{grid-column:1/-1;grid-template-columns:subgrid;display:grid}._pinnedTopBlock_34o3j_399{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_34o3j_399>:last-child>[role=cell]{border-bottom:2px solid var(--mantine-color-default-border)}._pinnedBottomBlock_34o3j_412{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_34o3j_412>:first-child>[role=cell]{border-top:2px solid var(--mantine-color-default-border)}._entryCell_34o3j_281{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_34o3j_281[data-control-column=true]{justify-content:center;padding:0}._entryRow_34o3j_386[data-committed=true] ._entryCell_34o3j_281{padding:0 var(--dg-padding);border-bottom:1px solid var(--mantine-color-default-border);background-image:linear-gradient(var(--dg-row-new-bg,transparent), var(--dg-row-new-bg,transparent))}._entryRow_34o3j_386[data-committed=true] ._entryCell_34o3j_281[data-control-column=true]{padding:0}._rowStateIndicator_34o3j_461{width:22px;height:22px;color:var(--mantine-color-dimmed);flex:none;justify-content:center;align-items:center;display:inline-flex}._rowStateIndicator_34o3j_461[data-error]{color:var(--mantine-color-error)}._entryCell_34o3j_281[data-align=right]{justify-content:flex-end}._entryCell_34o3j_281[data-align=center]{justify-content:center}._matchHighlight_34o3j_487{background-color:var(--dg-match-highlight-bg,var(--mantine-color-yellow-2));color:inherit;border-radius:2px}:where([data-mantine-color-scheme=dark]) ._matchHighlight_34o3j_487{background-color:var(--dg-match-highlight-bg,#fab00559)}._summaryRow_34o3j_509{grid-column:1/-1;grid-template-columns:subgrid;top:calc(100% - var(--dg-summary-height,0px));z-index:var(--dg-z-summary-row,4);display:grid;position:sticky;bottom:0}._summaryCell_34o3j_519{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_34o3j_519[data-control-column=true]{padding:0}._summaryCell_34o3j_519[data-align=right]{justify-content:flex-end}._summaryCell_34o3j_519[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_wm77f_4{gap:var(--mantine-spacing-xs);flex-direction:column;display:flex}._filterRow_wm77f_12{align-items:flex-end;gap:var(--mantine-spacing-xs);display:flex}._filterRow_wm77f_12[data-layout=stacked]{align-items:end;gap:6px var(--mantine-spacing-xs);padding:var(--mantine-spacing-xs);border:1px solid var(--mantine-color-default-border);border-radius:var(--mantine-radius-sm);grid-template-columns:1fr auto;display:grid}._filterRow_wm77f_12[data-layout=stacked] ._filterRemove_wm77f_32{grid-area:1/2}._filterRow_wm77f_12[data-layout=stacked] ._filterColumnSlot_wm77f_37{grid-area:1/1;min-width:0}._filterRow_wm77f_12[data-layout=stacked] ._filterOperatorSlot_wm77f_43,._filterRow_wm77f_12[data-layout=stacked] ._filterValueSlot_wm77f_44{grid-column:1/-1;min-width:0}._filterPanelFooter_wm77f_49{justify-content:space-between;align-items:center;gap:var(--mantine-spacing-md);display:flex}._filterPills_c75fv_3{align-items:center;gap:var(--mantine-spacing-xs);flex-wrap:wrap;display:flex}._filterPills_c75fv_3 ._pill_c75fv_15{background-color:var(--mantine-color-gray-1);color:var(--mantine-color-black);border:1px solid var(--mantine-color-gray-3)}:where([data-mantine-color-scheme=dark]) ._filterPills_c75fv_3 ._pill_c75fv_15{background-color:var(--mantine-color-dark-5);color:var(--mantine-color-dark-0);border-color:var(--mantine-color-dark-3)}._pillLabel_c75fv_29{font:inherit;color:inherit;cursor:pointer}._pillLabel_c75fv_29: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_1opmk_18:after,._stickyRight_1opmk_19:after{content:"";pointer-events:none;width:8px;position:absolute;top:0;bottom:0}._stickyLeft_1opmk_18:after{background:linear-gradient(90deg,#00000012,#00000006 45%,#0000);left:100%}._stickyRight_1opmk_19:after{background:linear-gradient(270deg,#00000012,#00000006 45%,#0000);right:100%}:where([data-mantine-color-scheme=dark]) ._stickyLeft_1opmk_18:after{background:linear-gradient(90deg,#00000042,#00000017 45%,#0000)}:where([data-mantine-color-scheme=dark]) ._stickyRight_1opmk_19:after{background:linear-gradient(270deg,#00000042,#00000017 45%,#0000)}@supports (animation-timeline:scroll()){._stickyLeft_1opmk_18:after,._stickyRight_1opmk_19:after{--dg-sticky-edge-range:20px;opacity:0;animation-timing-function:linear;animation-fill-mode:both;animation-timeline:scroll(inline)}._stickyLeft_1opmk_18:after{animation-name:_stickyEdgeIn_1opmk_1;animation-range:0 var(--dg-sticky-edge-range)}._stickyRight_1opmk_19:after{animation-name:_stickyEdgeOut_1opmk_1;animation-range:calc(100% - var(--dg-sticky-edge-range)) 100%}}@keyframes _stickyEdgeIn_1opmk_1{0%{opacity:0}to{opacity:1}}@keyframes _stickyEdgeOut_1opmk_1{0%{opacity:1}to{opacity:0}}._headerBoundary_1opmk_140: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()){._headerBoundary_1opmk_140:after{animation:linear both _dgHeaderShadow_1opmk_1;animation-timeline:scroll();animation-range:0 24px}}@keyframes _dgHeaderShadow_1opmk_1{0%{opacity:0}to{opacity:1}}._surfaceHeader_10bcx_5{justify-content:space-between;align-items:center;gap:var(--mantine-spacing-md);padding-bottom:var(--mantine-spacing-xs);display:flex}._filterPopup_10bcx_18{top:calc(var(--dg-header-stack-height,var(--dg-header-height)) + 2px);left:var(--mantine-spacing-md);z-index:var(--dg-z-filter-popup,20);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}._filterSidebar_10bcx_33{min-width:0;padding:var(--mantine-spacing-sm);background-color:var(--mantine-color-body);border-left:1px solid var(--mantine-color-default-border);flex-direction:column;flex:none;display:flex}._filterSidebar_10bcx_33[data-side=left]{border-left:none;border-right:1px solid var(--mantine-color-default-border);order:-1}._filterSidebarBody_10bcx_51{flex:1;min-height:0}._headerFilterRow_h1geg_9{grid-column:1/-1;grid-template-columns:subgrid;top:var(--dg-header-height);z-index:var(--dg-z-header,6);display:grid;position:sticky}._headerFilterCell_h1geg_18{min-width:0;padding:calc(var(--dg-padding) / 2);background-color:var(--mantine-color-body);border-bottom:1px solid var(--mantine-color-default-border);align-items:center;gap:2px;display:flex;overflow:hidden}._headerFilterCell_h1geg_18[data-control-column=true]{padding:0}._headerFilterValue_h1geg_39{flex:1;min-width:0}._headerFilterOperator_h1geg_44{flex:none}._operatorItemActive_h1geg_48{color:var(--mantine-primary-color-filled);font-weight:600}._headerCell_18ma1_1{padding:0 var(--dg-padding);min-width:0;min-height:var(--dg-header-height);font-weight:600;font-size:var(--dg-header-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_18ma1_1[data-align=right]{justify-content:flex-end}._headerCell_18ma1_1[data-align=center]{justify-content:center}._headerCell_18ma1_1[data-control-column=true]{padding:0}._headerCellFiltered_18ma1_35 ._headerTitle_18ma1_35{color:var(--mantine-primary-color-filled)}._headerTitle_18ma1_35{text-overflow:ellipsis;white-space:nowrap;flex:auto;min-width:0;overflow:hidden}._headerCell_18ma1_1[data-align=right] ._headerTitle_18ma1_35{text-align:right}._headerCell_18ma1_1[data-align=center] ._headerTitle_18ma1_35{justify-content:center;display:flex}._headerActions_18ma1_58{flex-shrink:0;align-items:center;gap:0;margin-left:auto;display:flex}._headerCell_18ma1_1[data-align=right] ._headerActions_18ma1_58{order:-1;margin-left:0;margin-right:auto}._headerAction_18ma1_58{display:none}._headerCell_18ma1_1:hover ._headerAction_18ma1_58,._headerCell_18ma1_1:focus-within ._headerAction_18ma1_58,._headerAction_18ma1_58[data-pinned-visible=true]{display:flex}._headerCellSortable_18ma1_87{cursor:pointer}._sortAction_18ma1_93:not([data-sorted]){opacity:.55}._sortAction_18ma1_93:not([data-sorted]):hover{opacity:1}._sortIndex_18ma1_103{color:var(--mantine-color-dimmed);text-align:center;min-width:10px;font-size:10px;font-weight:600;line-height:1}._headerCellDragging_18ma1_115{opacity:.4}._headerCellDropBefore_18ma1_119:before,._headerCellDropAfter_18ma1_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_18ma1_119:before{left:0}._headerCellDropAfter_18ma1_120:before{right:0}._columnSeparator_18ma1_140{z-index:1;justify-content:center;width:10px;height:50%;display:flex;position:absolute;top:25%;right:0;transform:translate(50%)}._headerCell_18ma1_1:last-child ._columnSeparator_18ma1_140{justify-content:flex-end;transform:none}._columnSeparatorResizable_18ma1_162{cursor:col-resize;touch-action:none}._columnSeparator_18ma1_140:after{content:"";background-color:#0000;width:1px;height:100%;transition:background-color .12s,width .12s}._headerCell_18ma1_1:hover ._columnSeparator_18ma1_140:after,._headerCell_18ma1_1:focus-within ._columnSeparator_18ma1_140:after{background-color:var(--mantine-color-default-border)}._columnSeparatorResizable_18ma1_162:hover:after,._columnSeparatorActive_18ma1_185:after{background-color:var(--mantine-primary-color-filled);width:2px}._menuItemActive_18ma1_191{background-color:var(--mantine-color-default-hover);font-weight:600}._toolbar_gwk05_1{align-items:center;gap:var(--mantine-spacing-xs);padding:6px var(--mantine-spacing-xs);flex-shrink:0;min-height:42px;display:flex}._toolbar_gwk05_1[data-with-bottom-border]{border-bottom:1px solid var(--mantine-color-default-border)}._toolbarSpacer_gwk05_15{flex:1}._summaryCount_gwk05_19{white-space:nowrap}._title_199r2_1{font-weight:500}._list_199r2_5{border:1px solid var(--mantine-color-default-border);border-radius:var(--mantine-radius-default);flex-direction:column;display:flex;overflow:hidden}._listHeader_199r2_13{justify-content:space-between;align-items:center;gap:var(--mantine-spacing-sm);padding:var(--mantine-spacing-xs) var(--mantine-spacing-sm);background-color:var(--mantine-color-default-hover);border-bottom:1px solid var(--mantine-color-default-border);display:flex}._headerLabel_199r2_23{cursor:pointer;font-weight:500}._rows_199r2_28{padding:calc(var(--mantine-spacing-xs) / 2) 0;flex-direction:column;display:flex}._row_199r2_28{padding:0 var(--mantine-spacing-sm)}._row_199r2_28:hover{background-color:var(--mantine-color-default-hover)}._rowBody_199r2_45{align-items:center}._rowLabelWrapper_199r2_49{flex:1;min-width:0}._rowLabel_199r2_49{justify-content:space-between;align-items:center;gap:var(--mantine-spacing-sm);padding-block:calc(var(--mantine-spacing-xs) * .75);cursor:pointer;display:flex}._rowText_199r2_63{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}._rowHint_199r2_69{font-size:var(--mantine-font-size-xs);color:var(--mantine-color-dimmed);flex:none}._empty_199r2_75{padding:var(--mantine-spacing-xs) var(--mantine-spacing-sm)}
2
2
  /*$vite$:1*/
@@ -180,7 +180,7 @@ Unlike the Export cells item, `resolveRangeBounds` does not fall back to the foc
180
180
  | `cellSelection` | Option | `"none" \| "single" \| "range"` | `"none"` | Turns the cell cursor, and the rectangle, on. |
181
181
  | `TMDataGridCellSelectionMode` | Type | – | – | The type of `cellSelection`. |
182
182
  | `onFocusedCellChange` | Callback | `(cell \| null) => void` | – | Follows the cursor. |
183
- | `exportOptions` | Option | `TMDataGridExportOptions` | `DEFAULT_EXPORT_OPTIONS` | Format, file name and header row of the Export cells item. See [Export](/docs/export). |
183
+ | `exportOptions` | Option | `TMDataGridExportOptions` | `csvExcelFormat()`, `"export"`, headers on, `"visible"` columns | Format, file name and header row of the Export cells item. See [Export](/docs/export). |
184
184
  | `ui.state.focusedCell` | UI state | `{ rowId, columnId } \| null` | `null` | The cursor. |
185
185
  | `ui.state.cellRange` | UI state | `{ anchor, focus } \| null` | `null` | The rectangle's two corners. |
186
186
  | `ui.actions.setFocusedCell` · `setCellRange` | UI actions | – | – | Move either from your own code. |
@@ -14,7 +14,11 @@ hint: Drag a header to reorder · pin or hide from a column menu · drag a divid
14
14
  ## Hiding
15
15
 
16
16
  **Hide column** in any column menu, and the column chooser for the whole list at once: a search field, one checkbox per column, show/hide all, and **Reset layout**.
17
- The chooser is `TMDataGrid.Menu.Columns` in the [grid menu](/docs/menu), **Manage columns** in every column menu (a submenu of the same items), and `TMDataGrid.ColumnsPanel` as plain controls for a host that is not a menu.
17
+ The chooser comes in three forms:
18
+
19
+ - `TMDataGrid.Menu.Columns` in the [grid menu](/docs/menu).
20
+ - **Manage columns**, a submenu of every column menu with the same items.
21
+ - `TMDataGrid.ColumnsPanel`, plain controls for a host that is not a menu.
18
22
 
19
23
  `enableHiding: false` removes all of it; on a column it removes that column's
20
24
  menu item and leaves it out of the panel, which lists what can be hidden and
@@ -42,8 +46,8 @@ computed from an `fr` value. The grid writes the column's rendered width into
42
46
  To start pinned, set `initialState.columnPinning`. The slice is TanStack's
43
47
  full `ColumnPinningState`, so a partial does not compile - name both sides.
44
48
  A column pinned at mount has no rendered width to store, so it takes its
45
- `size` - TanStack's default of `150` where none is set - and `minSize` does
46
- not apply. Give such a column an explicit `size`:
49
+ `size`, clamped between `minSize` and `maxSize`; with none of them set that is
50
+ TanStack's default of `150`. Give such a column an explicit `size`:
47
51
 
48
52
  ```tsx
49
53
  columnHelper.accessor("registration", { header: "Registration", size: 220 });
@@ -114,10 +118,11 @@ Columns are fluid by default. Each track is `minmax(minSize, flex fr)`.
114
118
 
115
119
  | Column option | Effect |
116
120
  | --- | --- |
117
- | `minSize` | Minimum width, and the column's contribution to the grid minimum width. Defaults to `80`. |
121
+ | `minSize` | Minimum width, and the column's contribution to the grid minimum width. Also the floor of a resize and of a pinned width. Defaults to `80`, through `defaultColumn.minSize`. |
122
+ | `maxSize` | Ceiling of a resize, an autosize and a pinned width. Does not cap a fluid track. No default. |
118
123
  | `meta.flex` | Share of the remaining width. Defaults to `1`. |
119
124
  | `minSize === maxSize` | Fixed width. The column is never fluid. |
120
- | `size` | Applied once the column becomes fixed by resizing or pinning. |
125
+ | `size` | Width of a column pinned at mount, clamped by `minSize` and `maxSize`. Ignored on a fluid column; a resize or an interactive pin stores the rendered width instead. Defaults to `150`. |
121
126
 
122
127
  Drag a divider to resize. A column switches to a fixed pixel width the moment
123
128
  it is resized or pinned, and the width is stored in `columnSizing`. The drag
@@ -148,8 +153,8 @@ the column's cells.
148
153
 
149
154
  ## Reset the layout
150
155
 
151
- `resetSettings()` from the hook clears visibility, order, pinning and widths in
152
- one call. The columns panel offers it as **Reset layout**.
156
+ `resetSettings()` from the hook clears visibility, order, pinning, widths and
157
+ grouping in one call. The columns panel offers it as **Reset layout**.
153
158
 
154
159
  ```tsx
155
160
  const { resetSettings } = useTMDataGrid({ data, columns });
@@ -169,7 +174,7 @@ const { resetSettings } = useTMDataGrid({ data, columns });
169
174
  | `meta.flex` | Column meta | `number` | `1` | Share of the remaining width. |
170
175
  | `meta.autoSize` | Column meta | `boolean` | `false` | Autosize once, on the render the column's first cells appear in. |
171
176
  | `minSize` / `maxSize` / `size` | Column options | `number` | `80` / – / – | Width bounds, and the fixed width once one applies. |
172
- | `resetSettings` | Hook return | `() => void` | – | Clears visibility, order, pinning and widths. |
177
+ | `resetSettings` | Hook return | `() => void` | – | Clears visibility, order, pinning, widths and grouping. |
173
178
  | `moveColumn` | Export | `({ table, columnId, targetId, side }) => void` | – | Moves a column beside another. |
174
179
  | `moveColumnByStep` | Export | `({ table, columnId, direction }) => void` | – | Moves it one place. |
175
180
  | `MoveColumnArgs` · `ColumnStepArgs` | Types | – | – | What `moveColumn` takes, and what `moveColumnByStep` and `getStepTargetColumn` take. |
package/docs/columns.md CHANGED
@@ -88,6 +88,7 @@ Its type is `TMDataGridColumnMeta`.
88
88
  | `options` | `TMDataGridOptionsSource` | - | The choices of a `select` / `multiSelect` column. See [Options](#options). |
89
89
  | `flex` | `number` | `1` | Share of the remaining width. |
90
90
  | `align` | `"left" \| "right" \| "center"` | `"left"` | Alignment, applied to both header and cells. |
91
+ | `colSpan` | `number \| ({ row, table }) => number` | `1` | Columns the cell covers on a row, counting itself. See [Spanning columns](#spanning-columns). |
91
92
  | `autoSize` | `boolean` | `false` | Fit the column to its content once, on the render its first cells appear in. |
92
93
  | `enableOrdering` | `boolean` | `true` | `false` keeps the column where it is. |
93
94
  | `filter` | `TMDataGridColumnFilterOptions` | - | How this column filters. See [meta.filter](#metafilter). |
@@ -99,6 +100,7 @@ Its type is `TMDataGridColumnMeta`.
99
100
  | --- | --- | --- | --- |
100
101
  | `defaultOperator` | `TMDataGridFilterOperator` | The type's default | The operator a fresh filter starts with. See [Filtering](/docs/filtering#operators). |
101
102
  | `control` | `TMDataGridFilterControlComponent` | By type and operator | Replaces the filter panel's value control. See [Filtering](/docs/filtering#replacing-the-value-control). |
103
+ | `url` | `boolean` | `true` | Keeps the column's filter out of a `persist.urlKey` link when `false`. See [Deep links](/docs/persistence#deep-links). |
102
104
 
103
105
  ```tsx
104
106
  meta: {
@@ -185,6 +187,57 @@ A select column that declares no options still filters: the panel falls back to
185
187
  the faceted values. Mantine's dropdowns are not virtualized, so use the function
186
188
  form for very large sets.
187
189
 
190
+ ## Spanning columns
191
+
192
+ `meta.colSpan` makes a column's cell cover the columns to its right.
193
+ A cell spanning `n` is one cell `n` columns wide, with one background and one
194
+ border, and the `n - 1` cells it covers do not render on that row.
195
+ A function decides per row:
196
+
197
+ ```tsx
198
+ columnHelper.accessor("name", {
199
+ header: "Name",
200
+ meta: {
201
+ colSpan: ({ row }) => (row.getIsExpanded() ? Infinity : 1),
202
+ },
203
+ });
204
+ ```
205
+
206
+ ```demo
207
+ file: columns/SpanningCells.tsx
208
+ hint: Open a row with its chevron - its name takes the rest of the row, and the panel opens below.
209
+ ```
210
+
211
+ - The span is counted over the displayed columns. Hiding or moving a column
212
+ changes what a span covers, so `Infinity` - "to the last of my columns" - is
213
+ usually safer than a count.
214
+ - A span stops at the last of your columns. It never covers the checkbox,
215
+ tree, details or edit lane.
216
+ - A span stops at the edge of its pinned block.
217
+ - A column covered by a span ignores its own `colSpan` on that row.
218
+ - Group rows ignore `colSpan`, and so does a row open in
219
+ [row edit mode](/docs/editing), where every column holds an editor.
220
+ - Sorting, filtering, copy and export still see every column. Autosize leaves
221
+ a spanning cell out of its column's measurement, and a column whose every
222
+ mounted cell spans fits its header.
223
+ - Under [cell selection](/docs/cell-selection) the spanning cell is one cell:
224
+ the arrow keys step over it, and a move onto a column it covers lands on it.
225
+ It is addressed by its first column: a range holds it when it holds that
226
+ column, and a range over only the columns it covers leaves it out. Copy and
227
+ export take every column of the range either way. When the column the focus
228
+ is on becomes covered, the focus moves to the spanning cell.
229
+ - Under [cell editing](/docs/editing) a covered column has no cell to open,
230
+ and Tab from the spanning cell's editor moves past the columns it covers.
231
+ A row keeps its cells while an editor is open in a column a span would
232
+ cover, and the span lands when the edit ends.
233
+
234
+ A cell renderer that reads `row.getIsExpanded()`, as the demo's does, has to
235
+ subscribe to the store - see
236
+ [Opening a row from elsewhere](/docs/row-details#opening-a-row-from-elsewhere).
237
+
238
+ The cell carries `aria-colspan` and `data-col-span` with the number of columns
239
+ it covers.
240
+
188
241
  ## Header groups
189
242
 
190
243
  `columnHelper.group` nests columns under a shared header. The group is a header
@@ -231,8 +284,9 @@ Sizing, pinning, ordering and visibility are covered on
231
284
  ## The generated columns
232
285
 
233
286
  Five lanes the grid adds when a feature needs them. All are **system lanes**:
234
- fixed width, no column menu, no resize handle, never exported, never hidden and
235
- never listed in the columns panel.
287
+ pinned, with no column menu, never exported and never listed in the columns
288
+ panel. Four of them are fixed width with no resize handle; the tree lane can be
289
+ resized, and it is shown only while a grouping is active.
236
290
 
237
291
  | Id | Appears when | Where |
238
292
  | --- | --- | --- |
@@ -259,7 +313,9 @@ column can be placed in front of it.
259
313
  | `meta.options` | Column meta | array \| `"faceted"` \| `(args) => …` | – | The choices of an option column. |
260
314
  | `TMDataGridOptionsArgs` | Type | – | – | What a `meta.options` function receives: `{ table, column, row? }`. `row` is absent when the filter panel asks. |
261
315
  | `meta.align` | Column meta | `"left" \| "right" \| "center"` | `"left"` | Header and cell alignment. |
262
- | `meta.filter` | Column meta | `TMDataGridColumnFilterOptions` | – | How the column filters: `defaultOperator`, `control`. |
316
+ | `meta.colSpan` | Column meta | `number \| (args) => number` | `1` | Columns the cell covers on a row. See [Spanning columns](#spanning-columns). |
317
+ | `TMDataGridColSpanArgs` | Type | `{ row, table }` | – | What a `meta.colSpan` function receives. |
318
+ | `meta.filter` | Column meta | `TMDataGridColumnFilterOptions` | – | How the column filters: `operators`, `defaultOperator`, `control`, `url`. |
263
319
  | `meta.edit` | Column meta | `TMDataGridColumnEditOptions` | – | How the column edits: `enabled`, `field`, `editor`, `validate`, `mapValue`. |
264
320
  | `resolveColumnOptions` | Export | `({ table, column, row? }) => options` | – | Normalises all three `meta.options` forms. |
265
321
  | `optionsToComboboxData` | Export | `(options) => ComboboxData` | – | Options as Mantine `Select` data. |
@@ -88,6 +88,7 @@ Pass the row type so the handlers are typed:
88
88
  | `rowClassName` | `string \| (row) => string` | – | Class for a body row. See [Row styling](/docs/row-styling). |
89
89
  | `rowStyle` | `TMDataGridRowStyle \| (row) => TMDataGridRowStyle` | – | Inline style for a body row. Set `--row-bg` rather than `background`. See [Row styling](/docs/row-styling#set-the-row-background). |
90
90
  | `striped` | `boolean` | `false` | If set, every second row takes `--dg-row-striped-bg`. See [Striping](/docs/row-styling#striping). |
91
+ | `highlightOnHover` | `boolean` | On for rows that answer a click or a right-click | Whether a body row takes the hover tint. See [Hover](/docs/row-styling#hover). |
91
92
  | `onScrollToTop` · `onScrollToBottom` · `onScrollToLeft` · `onScrollToRight` | `() => void` | – | Called once on arriving at that edge, not on mount and not per scroll event. See [Edge callbacks](/docs/scrolling#edge-callbacks). |
92
93
  | `onReachEnd` | `() => void` | – | Called as the scroll nears the last row, once per row count. Sorting and filtering must be server-side. See [Infinite scroll](/docs/server-side#infinite-scroll). |
93
94
  | `reachEndThreshold` | `number` | `10` | Rows before the end at which `onReachEnd` fires. |
package/docs/export.md CHANGED
@@ -80,7 +80,7 @@ const grid = useTMDataGrid({
80
80
  | `includeHeaders` | `boolean` | `true` | Determines whether the column labels are written as the first row. |
81
81
  | `columns` | `"visible" \| "all" \| Array<string>` | `"visible"` | Which columns are written. See [What is written](#what-is-written). |
82
82
 
83
- The option is read field by field, so a literal is fine.
83
+ The option is read field by field, so an inline object literal needs no `useMemo`.
84
84
  Every export of the grid reads it: the menu items, the hook, and the export item of the cell-range menu.
85
85
 
86
86
  ## Formats
@@ -290,7 +290,7 @@ See [Testing](/docs/testing).
290
290
 
291
291
  | Name | Kind | Type | Default | What it does |
292
292
  | --- | --- | --- | --- | --- |
293
- | `exportOptions` | Option | `TMDataGridExportOptions` | `DEFAULT_EXPORT_OPTIONS` | Format, file name and header row for every export of the grid. |
293
+ | `exportOptions` | Option | `TMDataGridExportOptions` | `csvExcelFormat()`, `"export"`, headers on, `"visible"` columns | Format, file name and header row for every export of the grid. |
294
294
  | `meta.enableExport` | Column meta | `boolean` | `true` | `false` leaves the column out of every export and of Ctrl+C. |
295
295
  | `meta.exportValue` | Column meta | `({ value, row, column }) => unknown` | – | The value written in place of `row.getValue`. |
296
296
  | `TMDataGridExportValueGetter` | Type | – | – | The type of `meta.exportValue`. |
package/docs/filtering.md CHANGED
@@ -32,8 +32,8 @@ array only under `isAnyOf` / `isNoneOf` (the set the cell is tested against) and
32
32
 
33
33
  A filter with an empty value stays in state, so a panel row survives while the
34
34
  user types. (A header filter control has no row to keep alive and drops its
35
- entry instead - see [inHeader](#inheader).) It matches every row, does not set the header's filter
36
- indicator, and produces no pill. `isFilterActive(value)` tests for that
35
+ entry instead - see [inHeader](#inheader).) The built-in matcher lets every row through, and
36
+ the filter does not set the header's filter indicator and produces no pill. `isFilterActive(value)` tests for that
37
37
  state, and `activeColumnFilters` applies it across the whole slice, handing
38
38
  back the entries that narrow the grid with their values typed as
39
39
  `TMDataGridFilterValue` rather than as `unknown`. Entries in any other value
@@ -98,6 +98,8 @@ columnHelper.accessor("customer", {
98
98
 
99
99
  `getColumnOperators(column)` returns the resolved list, and `getColumnDefaultOperator(column)` the operator a fresh filter on it opens on.
100
100
 
101
+ `meta.filter.url` keeps a column's filter out of a `persist.urlKey` link. See [Deep links](/docs/persistence#deep-links).
102
+
101
103
  ## The filters option
102
104
 
103
105
  `filters` on `useTMDataGrid` decides where the filter controls render.
@@ -117,7 +119,7 @@ useTMDataGrid({ data, columns, filters: { surface: "sidebar", inHeader: true } }
117
119
  `surface` and `inHeader` are two separate choices, not one list: `inHeader` composes with all three surfaces.
118
120
  `{ surface: "none", inHeader: true }` is header filters and nothing else; `{ inHeader: true }` keeps the popup as well, for the multi-column work a header row has no room for.
119
121
 
120
- The option is read field by field, so a literal is fine - unlike `labels` and `persist`, it does not have to be referentially stable.
122
+ The option is read field by field, so an inline object literal needs no `useMemo` - unlike `labels` and `persist`, it does not have to be referentially stable.
121
123
 
122
124
  ### surface: "popup"
123
125
 
@@ -132,7 +134,7 @@ Closing only hides the popup; the filters stay.
132
134
  The same panel beside the rows, inside the grid frame and under the toolbar.
133
135
  It is a column of the frame rather than a layer over it: the rows give up the width instead of being covered, a click in the table does not dismiss it, and clearing the filters leaves it standing with its **Add filter** button.
134
136
  Escape closes it.
135
- It starts open (`defaultOpen` defaults to `true` here) and renders the panel with `layout="stacked"`, because 280px has no room for the side-by-side triple.
137
+ It starts open (`defaultOpen` defaults to `true` here) and renders the panel with `layout="stacked"`, because the default 280px cannot fit the column, operator and value fields side by side.
136
138
 
137
139
  ```demo
138
140
  file: columns/FilterSidebar.tsx
@@ -182,7 +184,7 @@ No props.
182
184
 
183
185
  The panel of filter rows - one column / operator / value triple per filter, over an "Add filter" / "Clear all" footer.
184
186
  It is a plain block with no title, no close button and no open state: it renders wherever it is mounted, and the popup and sidebar surfaces are wrappers around it.
185
- It must be inside `<TMDataGrid>`, since it reads the grid from context, and it only reads and writes the table's `columnFilters` state, so a `manualFiltering` grid gets the same panel for free.
187
+ It must be inside `<TMDataGrid>`, since it reads the grid from context, and it only reads and writes the table's `columnFilters` state, so the same panel works unchanged under `manualFiltering`.
186
188
 
187
189
  Pair it with `surface: "none"` so it is the only panel on the page, and drive it off `ui.state.filterPanelOpen` if it belongs behind a control of your own - `defaultOpen` sets where that state starts.
188
190
 
@@ -302,8 +304,8 @@ const SalaryFilter: TMDataGridFilterControlComponent = ({ layout, ...rest }) =>
302
304
  layout === "header" ? <NumberInput {...} /> : <RangeSlider {...} />;
303
305
  ```
304
306
 
305
- A control that ignores `layout` still works - it will simply look the same
306
- everywhere, which is fine until it has to fit a header cell. Every built-in
307
+ A control that ignores `layout` still works and looks the same in every
308
+ surface, including a header cell, where the room is smallest. Every built-in
307
309
  control honours it.
308
310
 
309
311
  ```demo
@@ -311,24 +313,111 @@ file: columns/CustomFilterControl.tsx
311
313
  hint: Open the filter panel - Status offers chips, every other column the built-in control.
312
314
  ```
313
315
 
316
+ ## Writing filters from your own interface
317
+
318
+ The grid's controls write plain TanStack state, so a control of your own does
319
+ the same. Write an envelope with `column.setFilterValue` or
320
+ `table.setColumnFilters`; the matcher, the pills, the header indicator and
321
+ `activeColumnFilters` all read it:
322
+
323
+ ```tsx
324
+ import { emptyValueForOperator, type TMDataGridFilterValue } from "@jielga/tmdatagrid";
325
+
326
+ const status = grid.table.getColumn("status")!;
327
+
328
+ // A set filter, as the panel would write it.
329
+ status.setFilterValue({
330
+ operator: "isAnyOf",
331
+ value: ["active", "paused"],
332
+ } satisfies TMDataGridFilterValue);
333
+
334
+ // An empty envelope stays in state and matches every row.
335
+ status.setFilterValue({ operator: "isAnyOf", value: emptyValueForOperator("isAnyOf") });
336
+
337
+ // `undefined` removes the entry.
338
+ status.setFilterValue(undefined);
339
+ ```
340
+
341
+ `getColumnOperators(column)` returns the operators a column offers and
342
+ `getColumnDefaultOperator(column)` the one a fresh filter opens on;
343
+ `emptyValueForOperator(operator)` returns the empty value of the right shape.
344
+
345
+ To keep the grid's panel and place it yourself, set `filters: { surface: "none" }`
346
+ and render [TMDataGrid.FilterPanel](#tmdatagridfilterpanel) where it belongs.
347
+ To keep filtering on and drop only the column menu's Filter item, pass
348
+ `renderColumnMenuItems` to `TMDataGrid.Table` and leave out the item keyed
349
+ `"filter"` (see [Column menu](/docs/column-menu)). To replace every filter
350
+ control, set `enableColumnFilters: false` and write the envelope yourself;
351
+ filters in state still apply.
352
+
314
353
  ## Custom matching
315
354
 
316
355
  To give a column its own matching logic instead of its own control, set
317
- `filterFn` on the column definition. The grid provides only `"tmDataGrid"`,
318
- which is the default.
356
+ `filterFn` on the column definition. A column `filterFn` replaces the shared
357
+ matcher for that column, so its third argument is the stored
358
+ `{ operator, value }` envelope, not the unwrapped value. Type it as
359
+ `TMDataGridFilterFn<TData>` and delegate to `tmDataGridFilterFn` for the
360
+ operators it does not handle itself. A column `filterFn` is also called with
361
+ half-typed envelopes, which the built-in matcher treats as matching every row,
362
+ so return `true` for `!isFilterActive(filter)` first:
363
+
364
+ ```ts
365
+ import {
366
+ isFilterActive,
367
+ tmDataGridFilterFn,
368
+ type TMDataGridFilterFn,
369
+ } from "@jielga/tmdatagrid";
370
+
371
+ // Reads "contains" as "has this tag", so "ops" matches the tag "ops" and not
372
+ // "devops"; every other operator keeps the built-in matching.
373
+ const matchesTags: TMDataGridFilterFn<Employee> = (row, columnId, filter) => {
374
+ if (!isFilterActive(filter)) return true;
375
+ if (filter.operator === "contains") {
376
+ const wanted = String(filter.value).toLowerCase();
377
+ return row.original.tags.some((tag) => tag.toLowerCase() === wanted);
378
+ }
379
+ return tmDataGridFilterFn(row, columnId, filter);
380
+ };
381
+
382
+ columnHelper.accessor((row) => row.tags.join(", "), {
383
+ id: "tags",
384
+ header: "Tags",
385
+ filterFn: matchesTags,
386
+ meta: { type: "string" },
387
+ });
388
+ ```
389
+
390
+ `filter.value` is a string under the scalar operators, a `ReadonlyArray<string>`
391
+ under `isAnyOf` / `isNoneOf` and a `[min, max]` pair under `between`;
392
+ `filterValueShape(operator)` tells which. `matchesFilter(cellValue, filter)`
393
+ applies the built-in matching to a value derived by the function itself.
394
+
395
+ The column's filter controls do not change: the operators still come from
396
+ `meta.type` and `meta.filter.operators`, and the panel, the pills and the header
397
+ indicator keep reading the envelope. A filter function named by string also
398
+ receives the envelope and does not unwrap it, so TanStack's built-ins such as
399
+ `"includesString"` do not work as a column `filterFn`. Write custom matching as
400
+ a function.
319
401
 
320
402
  ## Turning it off
321
403
 
322
- `enableColumnFilters: false` removes the filter menu item, the `FilterButton`,
323
- the panel and the header filter row. `enableColumnFilter: false` on a column
324
- removes that column's menu item, its entry in the panel's column list and its
325
- header control - the header cell stays, empty.
404
+ `enableColumnFilters: false` removes the Filter item from the column menu, the
405
+ `FilterButton`, the header filter row and the panel's column list, and disables
406
+ the panel's "Add filter" button. It does not clear `columnFilters`: an entry already in
407
+ state, or one written with `setFilterValue`, still narrows the rows, still shows
408
+ as a pill and still sets the header indicator. Call `table.resetColumnFilters()`
409
+ when switching the interface off. Under `filters.surface: "sidebar"`, set
410
+ `surface: "none"` as well, as the sidebar does not follow the switch.
411
+
412
+ `enableColumnFilter: false` on a column removes that column's menu item, its
413
+ entry in the panel's column list and its header control - the header cell
414
+ stays, empty.
326
415
 
327
416
  ## Reference
328
417
 
329
418
  | Name | Kind | Type | Default | What it does |
330
419
  | --- | --- | --- | --- | --- |
331
- | `enableColumnFilters` | Table option | `boolean` | `true` | `false` removes the panel, the button, the header row and the menu item. |
420
+ | `enableColumnFilters` | Table option | `boolean` | `true` | `false` removes the menu item, the button, the header row and the panel's column list, and disables the panel's "Add filter" button. Filters already in state still apply. |
332
421
  | `filters.surface` | Table option | `"popup" \| "sidebar" \| "none"` | `"popup"` | Which surface the table renders. |
333
422
  | `filters.sidebarSide` | Table option | `"left" \| "right"` | `"right"` | Which side the sidebar sits on. |
334
423
  | `filters.sidebarWidth` | Table option | `string` | `"280px"` | Width of the sidebar. |
@@ -339,7 +428,11 @@ header control - the header cell stays, empty.
339
428
  | `meta.filter.operators` | Column meta | `readonly TMDataGridFilterOperator[]` | The type's list | The operators this column offers, a subset of its type's. |
340
429
  | `meta.filter.defaultOperator` | Column meta | `TMDataGridFilterOperator` | The type's default, else the first offered | The operator a fresh filter opens on. |
341
430
  | `meta.filter.control` | Column meta | `TMDataGridFilterControlComponent` | By type and operator | Replaces the value control. |
342
- | `filterFn` | Column option | name \| fn | `"tmDataGrid"` | Custom matching for one column. |
431
+ | `meta.filter.url` | Column meta | `boolean` | `true` | Keeps the column's filter out of a `persist.urlKey` link when `false`. See [Deep links](/docs/persistence#deep-links). |
432
+ | `filterFn` | Column option | `TMDataGridFilterFn<TData>` \| name | `"tmDataGrid"` | Custom matching for one column. Receives the `{ operator, value }` envelope. |
433
+ | `TMDataGridFilterFn<TData>` | Type | `(row, columnId, filter: TMDataGridFilterValue, addMeta?) => boolean` | – | The signature of a column's own `filterFn`. |
434
+ | `tmDataGridFilterFn` | Export | `(row, columnId, filterValue: unknown) => boolean`, assignable to `TMDataGridFilterFn<TData>` | – | The shared matcher, for a custom `filterFn` to delegate to. |
435
+ | `matchesFilter` | Export | `(cellValue, filter: TMDataGridFilterValue) => boolean` | – | The built-in matching applied to one value. |
343
436
  | `TMDataGrid.FilterPanel` | Component | `layout: "row" \| "stacked"` | `"row"` | The panel of filter rows, as a plain block. |
344
437
  | `TMDataGrid.FilterButton` | Component | – | – | Toolbar button toggling the surface, with an active count. Seeds a filter row only when the panel is empty. |
345
438
  | `TMDataGrid.FilterPills` | Component | takes `api` | – | Active filters as removable pills, renderable anywhere. |
@@ -112,6 +112,46 @@ file: getting-started/ToolbarAndFooter.tsx
112
112
  [Grid anatomy](/docs/anatomy) for what each part is, and
113
113
  [Toolbar](/docs/toolbar) for adding your own buttons among them.
114
114
 
115
+ ## Coming from TanStack Table
116
+
117
+ `useTMDataGrid` takes TanStack's `TableOptions`, so a page built on
118
+ `useReactTable` keeps its `data`, `columns`, `state` and `onXChange` callbacks.
119
+ Go through these differences:
120
+
121
+ - **`getRowId`** - return the entity id. The index default points at another
122
+ record after a sort or a refetch, which breaks selection, expansion and
123
+ editing. Required once `editing` is set.
124
+ - **Stable `data` and `columns`** - define `columns` outside the component and
125
+ keep `data` referentially stable; a fresh `[]` for the empty case belongs in
126
+ a module-scope constant. Under `editing`, a transient `[]` drops every open
127
+ draft.
128
+ - **Sizing** - columns are fluid, `minmax(minSize, flex fr)`. `size` applies
129
+ only to a column pinned at mount, `maxSize` only to resizing and autosizing,
130
+ and `minSize` defaults to `80`. See
131
+ [Visibility, pinning, ordering and size](/docs/column-layout#sizing).
132
+ - **Defaults the grid changes** - `aggregationFn` is cleared, so grouping does
133
+ not sum anything on its own; `groupedColumnMode` is `"remove"`;
134
+ `autoResetExpanded` and `autoResetPageIndex` are off; `globalFilterFn` is the
135
+ fuzzy matcher. The full list is on
136
+ [useTMDataGrid](/docs/use-tm-data-grid#options).
137
+ - **Filters are envelopes** - `columnFilters` holds `{ operator, value }`, and
138
+ a column's own `filterFn` receives that envelope. See
139
+ [Filtering](/docs/filtering#custom-matching).
140
+ - **Interface the page already owns** - `enableGlobalFilter: false`, or no
141
+ `TMDataGrid.Search`, for the search; `enableColumnFilters: false`, or
142
+ `filters: { surface: "none" }` to keep filtering without the grid's panel.
143
+ See [Filtering](/docs/filtering#turning-it-off).
144
+ - **Page reset** - `resetPageOnQueryChange` (default `true`) returns to the
145
+ first page when a filter, the search, the sorting or the grouping changes
146
+ through the table. A data source re-filtered outside table state does not
147
+ trigger it; call `table.setPageIndex(0)` yourself, or use `manualFiltering`.
148
+ See [Server-side data](/docs/server-side).
149
+ - **State reads** - there is no `table.getState()`; read slices with
150
+ `useSelector(table.store, ...)`. See
151
+ [useTMDataGrid](/docs/use-tm-data-grid#reading-state).
152
+ - **Tests** - the grid publishes `data-dg-part`, `data-row-id` and
153
+ `data-column-id`, and no `data-testid`. See [Testing](/docs/testing).
154
+
115
155
  ## Where to go next
116
156
 
117
157
  - **[Defining columns](/docs/columns)** - accessors, `meta.type`, and what each
package/docs/grouping.md CHANGED
@@ -100,8 +100,8 @@ records inside collapsed sub-groups. It shows a tick once all of them are
100
100
  selected and a dash while only some are.
101
101
 
102
102
  Only the records are written to `rowSelection`. A group row is never in it, so
103
- `getSelectedRowModel()` and the toolbar count do not depend on how the tree is
104
- arranged.
103
+ `getSelectedRowModel()` and `onRowSelectionChange` do not depend on how the tree
104
+ is arranged.
105
105
 
106
106
  Under `enableMultiRowSelection: false` group rows carry no checkbox.
107
107
 
@@ -125,8 +125,11 @@ While a column is grouped the grid renders the whole tree and relies on
125
125
  virtualization. `TMDataGrid.Footer` greys its pager out and replaces the range
126
126
  with `Grouped · all N rows`. Ungroup and paging resumes where it left off.
127
127
 
128
- To page a grouped grid, group and page on the server: feed the grid one page of
129
- a tree at a time with `manualPagination` and `manualGrouping`.
128
+ There is no option to keep the built-in pager active while grouped, and
129
+ `manualGrouping` does not change that: the pager greys out whenever `grouping`
130
+ is non-empty. To page a grouped grid, group and page on the server with
131
+ `manualPagination` and `manualGrouping`, and render the pager through
132
+ `renderPagination`, ignoring `isPagingActive`.
130
133
 
131
134
  `isPagingActive(table, features)` is exported, so a custom pager can grey itself
132
135
  out the same way:
@@ -150,12 +153,12 @@ server-side can set `enableGrouping: true` alongside `manualGrouping: true`. See
150
153
 
151
154
  | Name | Kind | Type | Default | What it does |
152
155
  | --- | --- | --- | --- | --- |
153
- | `enableGrouping` | Table option | `boolean` | `true` | Group by and Ungroup menu items. Also a column option. |
156
+ | `enableGrouping` | Table option | `boolean` | `true`; `false` under `manualPagination` | Group by and Ungroup menu items, and the tree lane. Also a column option. |
154
157
  | `groupedColumnMode` | Table option | `"reorder" \| "remove" \| false` | `"remove"` | Whether a grouped column leaves the grid or moves to the front. |
155
158
  | `manualGrouping` | Table option | `boolean` | `false` | The rows arrive grouped. Required to group a server-paged grid. |
156
159
  | `initialState.grouping` | Table option | `string[]` | `[]` | Column ids to group on at mount. A settings slice, so it persists. |
157
160
  | `aggregationFn` | Column option | `TMDataGridAggregationName \| fn` | – | How a column fills in its group cells. Unset leaves them blank. |
158
- | `aggregatedCell` | Column option | `(ctx) => ReactNode` | The `cell` renderer | Renders a group row's value differently from a data row's. |
161
+ | `aggregatedCell` | Column option | `(ctx) => ReactNode` | `cell` over the aggregate when `aggregationFn` is set, else blank | Renders a group row's value differently from a data row's. |
159
162
  | `GROUP_COLUMN_ID` | Export | `"__group__"` | – | Id of the generated tree column. |
160
163
  | `formatGroupValue` | Export | `(value) => string` | – | How the tree lane renders a group's value. |
161
164
  | `getGroupDataRows` | Export | `(row) => Row[]` | – | Every record under a group row, at any depth. |