@jielga/tmdatagrid 1.0.0 → 1.0.1

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 (68) hide show
  1. package/README.md +8 -8
  2. package/dist/index.d.ts +225 -225
  3. package/dist/index.js +58 -50
  4. package/dist/index.js.map +1 -1
  5. package/dist/styles.css +1 -1
  6. package/package.json +3 -2
  7. package/skills/columns/SKILL.md +4 -4
  8. package/skills/features/SKILL.md +30 -30
  9. package/skills/getting-started/SKILL.md +16 -16
  10. package/skills/options/SKILL.md +8 -8
  11. package/skills/server-side/SKILL.md +7 -7
  12. package/skills/testing/SKILL.md +12 -12
  13. package/src/tmdatagrid/TMDataGridContext.ts +2 -2
  14. package/src/tmdatagrid/components/TMDataGrid.module.css +2 -2
  15. package/src/tmdatagrid/components/TMDataGrid.tsx +5 -5
  16. package/src/tmdatagrid/components/TMDataGridCellEditor.tsx +4 -4
  17. package/src/tmdatagrid/components/TMDataGridColumnsPanel.tsx +2 -2
  18. package/src/tmdatagrid/components/TMDataGridDetailsColumn.tsx +6 -6
  19. package/src/tmdatagrid/components/TMDataGridEditActions.tsx +2 -2
  20. package/src/tmdatagrid/components/TMDataGridEditColumn.tsx +4 -4
  21. package/src/tmdatagrid/components/TMDataGridEntryRows.tsx +6 -6
  22. package/src/tmdatagrid/components/TMDataGridFilterPanel.tsx +6 -6
  23. package/src/tmdatagrid/components/TMDataGridFilterPills.module.css +2 -2
  24. package/src/tmdatagrid/components/TMDataGridFilterPills.tsx +1 -1
  25. package/src/tmdatagrid/components/TMDataGridFooter.module.css +1 -1
  26. package/src/tmdatagrid/components/TMDataGridFooter.tsx +12 -6
  27. package/src/tmdatagrid/components/TMDataGridGroupColumn.module.css +1 -1
  28. package/src/tmdatagrid/components/TMDataGridGroupColumn.tsx +5 -5
  29. package/src/tmdatagrid/components/TMDataGridHeaderCell.module.css +8 -8
  30. package/src/tmdatagrid/components/TMDataGridHeaderCell.tsx +12 -12
  31. package/src/tmdatagrid/components/TMDataGridRowNumberColumn.tsx +4 -4
  32. package/src/tmdatagrid/components/TMDataGridSearch.tsx +5 -5
  33. package/src/tmdatagrid/components/TMDataGridSelectColumn.tsx +8 -8
  34. package/src/tmdatagrid/components/TMDataGridTable.module.css +18 -18
  35. package/src/tmdatagrid/components/TMDataGridTable.tsx +116 -116
  36. package/src/tmdatagrid/components/TMDataGridToolbar.tsx +5 -5
  37. package/src/tmdatagrid/components/editors/TMDataGridBooleanEditor.tsx +1 -1
  38. package/src/tmdatagrid/components/editors/TMDataGridDateEditor.tsx +2 -2
  39. package/src/tmdatagrid/components/editors/TMDataGridMultiSelectEditor.tsx +1 -1
  40. package/src/tmdatagrid/components/editors/TMDataGridNumberEditor.tsx +1 -1
  41. package/src/tmdatagrid/components/editors/TMDataGridSelectEditor.tsx +2 -2
  42. package/src/tmdatagrid/components/editors/editorShared.ts +3 -3
  43. package/src/tmdatagrid/components/filters/DgDateRangeFilter.tsx +1 -1
  44. package/src/tmdatagrid/components/filters/DgRangeSliderFilter.tsx +1 -1
  45. package/src/tmdatagrid/components/filters/DgTriStateFilter.tsx +1 -1
  46. package/src/tmdatagrid/components/filters/TMDataGridFilterValueInput.tsx +2 -2
  47. package/src/tmdatagrid/components/sticky.module.css +8 -8
  48. package/src/tmdatagrid/core/autosize.ts +4 -4
  49. package/src/tmdatagrid/core/capabilities.ts +17 -17
  50. package/src/tmdatagrid/core/cellExport.ts +13 -13
  51. package/src/tmdatagrid/core/cellNavigation.ts +6 -6
  52. package/src/tmdatagrid/core/cellRange.ts +4 -4
  53. package/src/tmdatagrid/core/columnOptions.ts +2 -2
  54. package/src/tmdatagrid/core/columnOrdering.ts +2 -2
  55. package/src/tmdatagrid/core/columnUtils.ts +3 -3
  56. package/src/tmdatagrid/core/editEngine.ts +49 -49
  57. package/src/tmdatagrid/core/expanding.ts +5 -5
  58. package/src/tmdatagrid/core/filterControls.ts +5 -5
  59. package/src/tmdatagrid/core/filterOperators.ts +14 -14
  60. package/src/tmdatagrid/core/labels.ts +8 -8
  61. package/src/tmdatagrid/core/matchHighlight.ts +4 -4
  62. package/src/tmdatagrid/core/persistence.ts +8 -8
  63. package/src/tmdatagrid/core/quickSearch.ts +8 -8
  64. package/src/tmdatagrid/core/rowPinning.ts +3 -3
  65. package/src/tmdatagrid/core/rowSelection.ts +12 -12
  66. package/src/tmdatagrid/core/sizes.ts +1 -1
  67. package/src/tmdatagrid/core/summary.ts +3 -3
  68. package/src/tmdatagrid/useTMDataGrid.tsx +79 -79
package/dist/styles.css CHANGED
@@ -1,2 +1,2 @@
1
- ._root_nuit2_8[data-size=xs]{--dg-row-height:34px;--dg-header-height:32px;--dg-font-size:var(--mantine-font-size-xs);--dg-padding:6px}._root_nuit2_8[data-size=sm]{--dg-row-height:42px;--dg-header-height:38px;--dg-font-size:var(--mantine-font-size-sm);--dg-padding:8px}._root_nuit2_8[data-size=md]{--dg-row-height:52px;--dg-header-height:44px;--dg-font-size:var(--mantine-font-size-sm);--dg-padding:10px}._root_nuit2_8[data-size=lg]{--dg-row-height:62px;--dg-header-height:52px;--dg-font-size:var(--mantine-font-size-md);--dg-padding:14px}._root_nuit2_8[data-size=xl]{--dg-row-height:72px;--dg-header-height:60px;--dg-font-size:var(--mantine-font-size-lg);--dg-padding:18px}._root_nuit2_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_h06xv_4{width:100%;min-width:0;font-size:var(--dg-font-size);color:inherit;align-items:center;gap:6px;display:flex}._chevron_h06xv_14{color:var(--mantine-color-dimmed);flex:none;align-items:center;transition:transform .12s;display:flex}._chevron_h06xv_14[data-expanded=true]{transform:rotate(90deg)}@media (prefers-reduced-motion:reduce){._chevron_h06xv_14{transition:none}}._groupLabel_h06xv_34{white-space:nowrap;text-overflow:ellipsis;min-width:0;font-weight:600;overflow:hidden}._groupCount_h06xv_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_1yybb_3{align-items:center;gap:var(--mantine-spacing-xs);flex-wrap:wrap;display:flex}._filterPills_1yybb_3 ._pill_1yybb_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_1yybb_27{font:inherit;color:inherit;cursor:pointer}._pillLabel_1yybb_27:hover{text-decoration:underline}._footer_149ca_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_149ca_1[data-paging-suspended=true]{opacity:.55}._tableWrapper_18r3a_2{flex-direction:column;flex:1;min-height:0;display:flex;position:relative}._scrollContainer_18r3a_10{flex:1;min-height:0;position:relative;overflow:auto;container-type:inline-size}._grid_18r3a_21{display:grid}._headerRow_18r3a_27{grid-column:1/-1;grid-template-columns:subgrid;z-index:var(--dg-z-header,6);display:grid;position:sticky;top:0}._headerRow_18r3a_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_18r3a_27[data-dg-header-last]:after{animation:linear both _dg-header-shadow_18r3a_1;animation-timeline:scroll();animation-range:0 24px}}@keyframes _dg-header-shadow_18r3a_1{0%{opacity:0}to{opacity:1}}._bodyRow_18r3a_76{--row-bg:var(--mantine-color-body);grid-column:1/-1;grid-template-columns:subgrid;align-items:stretch;display:grid}._bodyRow_18r3a_76[data-striped=true]{--row-bg:var(--dg-row-striped-bg)}._bodyRow_18r3a_76:hover{--row-bg:var(--mantine-color-default-hover)}._bodyRow_18r3a_76[data-grouped=true]{--row-bg:var(--dg-row-group-bg)}._bodyRow_18r3a_76[data-context-menu=true]{--row-bg:var(--mantine-color-default-hover)}._bodyRow_18r3a_76[data-selected-bg=true]{--row-bg:var(--dg-row-selected-bg)}._bodyRow_18r3a_76[data-highlighted=true]{--row-bg:var(--dg-row-highlight-bg)}._bodyRow_18r3a_76[data-selects-on-click=true]{-webkit-user-select:none;user-select:none}._bodyRow_18r3a_76:focus-visible{outline:2px solid var(--mantine-primary-color-filled);outline-offset:-2px}._bodyCell_18r3a_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_18r3a_143[data-control-column=true]{padding:0}._grid_18r3a_21[data-range-dragging]{-webkit-user-select:none;user-select:none}._bodyCell_18r3a_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_18r3a_143[data-edge-top=true]{--dg-edge-top:var(--mantine-primary-color-filled)}._bodyCell_18r3a_143[data-edge-bottom=true]{--dg-edge-bottom:var(--mantine-primary-color-filled)}._bodyCell_18r3a_143[data-edge-left=true]{--dg-edge-left:var(--mantine-primary-color-filled)}._bodyCell_18r3a_143[data-edge-right=true]{--dg-edge-right:var(--mantine-primary-color-filled)}._bodyCell_18r3a_143[data-focused=true]{outline:2px solid var(--mantine-primary-color-filled);outline-offset:-2px}._cellContent_18r3a_141{white-space:nowrap;text-overflow:ellipsis;min-width:0;overflow:hidden}._bodyCell_18r3a_143[data-editing]{padding:0 calc(var(--dg-padding) / 2)}._cellEditor_18r3a_243{align-items:center;gap:2px;width:100%;min-width:0;display:flex}._cellEditorActions_18r3a_252{flex:none;align-items:center;display:flex}._bodyCell_18r3a_143[data-dirty],._bodyCell_18r3a_143[data-invalid]{position:relative}._bodyCell_18r3a_143[data-dirty]:after,._bodyCell_18r3a_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_18r3a_143[data-invalid]:after{border-top-color:var(--mantine-color-error);border-right-color:var(--mantine-color-error)}._bodyCell_18r3a_143[data-align=right]{justify-content:flex-end}._bodyCell_18r3a_143[data-align=center]{justify-content:center}._bodyCell_18r3a_143[data-align=left] ._cellContent_18r3a_141{flex:auto}._detailsCell_18r3a_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_18r3a_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_18r3a_76[data-deleted=true]{opacity:.55;pointer-events:none;text-decoration:line-through}._entryBlock_18r3a_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_18r3a_356{grid-column:1/-1;grid-template-columns:subgrid;display:grid}._pinnedTopBlock_18r3a_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_18r3a_369>:last-child>[role=cell]{border-bottom:2px solid var(--mantine-color-default-border)}._pinnedBottomBlock_18r3a_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_18r3a_382>:first-child>[role=cell]{border-top:2px solid var(--mantine-color-default-border)}._entryCell_18r3a_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_18r3a_395[data-control-column=true]{justify-content:center;padding:0}._entryCell_18r3a_395[data-align=right]{justify-content:flex-end}._entryCell_18r3a_395[data-align=center]{justify-content:center}._matchHighlight_18r3a_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_18r3a_434{grid-column:1/-1;grid-template-columns:subgrid;z-index:var(--dg-z-summary-row,4);display:grid;position:sticky;bottom:0}._summaryCell_18r3a_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_18r3a_443[data-control-column=true]{padding:0}._summaryCell_18r3a_443[data-align=right]{justify-content:flex-end}._summaryCell_18r3a_443[data-align=center]{justify-content:center}._stickyLeft_6z1iq_16:after,._stickyRight_6z1iq_17:after{content:"";pointer-events:none;width:8px;position:absolute;top:0;bottom:0}._stickyLeft_6z1iq_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_6z1iq_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_6z1iq_16:after,._stickyRight_6z1iq_17:after{--dg-sticky-edge-range:20px;opacity:0;animation-timing-function:linear;animation-fill-mode:both;animation-timeline:scroll(inline)}._stickyLeft_6z1iq_16:after{animation-name:_stickyEdgeIn_6z1iq_1;animation-range:0 var(--dg-sticky-edge-range)}._stickyRight_6z1iq_17:after{animation-name:_stickyEdgeOut_6z1iq_1;animation-range:calc(100% - var(--dg-sticky-edge-range)) 100%}}@keyframes _stickyEdgeIn_6z1iq_1{0%{opacity:0}to{opacity:1}}@keyframes _stickyEdgeOut_6z1iq_1{0%{opacity:1}to{opacity:0}}._headerCell_ikfbg_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_ikfbg_1[data-align=right]{justify-content:flex-end}._headerCell_ikfbg_1[data-align=center]{justify-content:center}._headerCell_ikfbg_1[data-control-column=true]{padding:0}._headerCellFiltered_ikfbg_35 ._headerTitle_ikfbg_35{color:var(--mantine-primary-color-filled)}._headerTitle_ikfbg_35{text-overflow:ellipsis;white-space:nowrap;flex:auto;min-width:0;overflow:hidden}._headerCell_ikfbg_1[data-align=right] ._headerTitle_ikfbg_35{text-align:right}._headerCell_ikfbg_1[data-align=center] ._headerTitle_ikfbg_35{justify-content:center;display:flex}._headerActions_ikfbg_58{flex-shrink:0;align-items:center;gap:0;margin-left:auto;display:flex}._headerCell_ikfbg_1[data-align=right] ._headerActions_ikfbg_58{order:-1;margin-left:0;margin-right:auto}._headerAction_ikfbg_58{display:none}._headerCell_ikfbg_1:hover ._headerAction_ikfbg_58,._headerCell_ikfbg_1:focus-within ._headerAction_ikfbg_58,._headerAction_ikfbg_58[data-pinned-visible=true]{display:flex}._headerCellSortable_ikfbg_87{cursor:pointer}._sortAction_ikfbg_93[data-sorted=false]{opacity:.55}._sortAction_ikfbg_93[data-sorted=false]:hover{opacity:1}._sortIndex_ikfbg_103{color:var(--mantine-color-dimmed);text-align:center;min-width:10px;font-size:10px;font-weight:600;line-height:1}._headerCellDragging_ikfbg_115{opacity:.4}._headerCellDropBefore_ikfbg_119:before,._headerCellDropAfter_ikfbg_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_ikfbg_119:before{left:0}._headerCellDropAfter_ikfbg_120:before{right:0}._columnSeparator_ikfbg_140{z-index:1;justify-content:center;width:10px;height:50%;display:flex;position:absolute;top:25%;right:0;transform:translate(50%)}._columnSeparatorResizable_ikfbg_152{cursor:col-resize;touch-action:none}._columnSeparator_ikfbg_140:after{content:"";background-color:#0000;width:1px;height:100%;transition:background-color .12s,width .12s}._headerCell_ikfbg_1:hover ._columnSeparator_ikfbg_140:after,._headerCell_ikfbg_1:focus-within ._columnSeparator_ikfbg_140:after{background-color:var(--mantine-color-default-border)}._columnSeparatorResizable_ikfbg_152:hover:after,._columnSeparatorActive_ikfbg_175:after{background-color:var(--mantine-primary-color-filled);width:2px}._menuItemActive_ikfbg_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_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}
2
2
  /*$vite$:1*/
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@jielga/tmdatagrid",
3
- "version": "1.0.0",
4
- "description": "A React data grid built on TanStack Table v9 and Mantine — always virtualized, with resizable, reorderable, sortable, filterable, hideable and pinnable columns.",
3
+ "version": "1.0.1",
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",
7
7
  "sideEffects": [
@@ -60,6 +60,7 @@
60
60
  "build": "tsc -b && vite build",
61
61
  "build:lib": "vite build --config vite.lib.config.ts && tsc -p tsconfig.lib.json && rolldown -c rolldown.dts.config.mjs",
62
62
  "lint": "oxlint",
63
+ "check:style": "node scripts/check-house-style.mjs",
63
64
  "test": "vitest run",
64
65
  "test:watch": "vitest",
65
66
  "preview": "vite preview",
@@ -11,14 +11,14 @@ description: >
11
11
  metadata:
12
12
  type: core
13
13
  library: '@jielga/tmdatagrid'
14
- library_version: '1.0.0'
14
+ library_version: '1.0.1'
15
15
  sources:
16
16
  - 'Jielga/TMDataGrid:src/docs/columns.md'
17
17
  - 'Jielga/TMDataGrid:src/tmdatagrid/core/filterOperators.ts'
18
18
  - 'Jielga/TMDataGrid:src/tmdatagrid/core/columnUtils.ts'
19
19
  ---
20
20
 
21
- # TMDataGrid — Columns
21
+ # TMDataGrid - Columns
22
22
 
23
23
  `createTMDataGridColumnHelper<TData>()` returns a TanStack column helper bound to
24
24
  the grid feature set, which gives `meta` and `filterFn` their correct types.
@@ -160,7 +160,7 @@ When `header` is a component there is no string to fall back to, so the column
160
160
  manager and column menus show the raw id (`"fullName"`) instead of a name.
161
161
 
162
162
  ```tsx
163
- // Wrong — menus show "fullName".
163
+ // Wrong - menus show "fullName".
164
164
  columnHelper.accessor("fullName", { header: () => <Icon /> });
165
165
 
166
166
  // Right.
@@ -185,7 +185,7 @@ const active = columnFilters.filter((filter) => isFilterActive(filter.value));
185
185
  ### Numeric operators on a column without meta.type
186
186
 
187
187
  `getColumnType(column)` defaults to `"string"`, so a numeric column that omits
188
- `meta: { type: "number" }` offers only the string operators — `greaterThan` and
188
+ `meta: { type: "number" }` offers only the string operators - `greaterThan` and
189
189
  the other comparisons never appear in the panel, and comparisons run as text.
190
190
 
191
191
  ### Setting size to control width
@@ -18,14 +18,14 @@ description: >
18
18
  metadata:
19
19
  type: core
20
20
  library: '@jielga/tmdatagrid'
21
- library_version: '1.0.0'
21
+ library_version: '1.0.1'
22
22
  sources:
23
23
  - 'Jielga/TMDataGrid:src/docs/features.md'
24
24
  - 'Jielga/TMDataGrid:src/tmdatagrid/core/capabilities.ts'
25
25
  - 'Jielga/TMDataGrid:src/tmdatagrid/core/columnOrdering.ts'
26
26
  ---
27
27
 
28
- # TMDataGrid — Features and capabilities
28
+ # TMDataGrid - Features and capabilities
29
29
 
30
30
  Almost every control is bound to the TanStack capability check for that feature,
31
31
  so setting the standard table or column option removes the corresponding
@@ -51,7 +51,7 @@ like the options around it; pagination is the one switch that defaults to off.
51
51
  | `enableColumnOrdering: false` | Table | Header dragging and the move menu items |
52
52
  | `meta.enableOrdering: false` | Column | That column's header dragging and move menu items |
53
53
  | `enableRowSelection: false` | Table | The checkbox column |
54
- | `rowSelectionMode: "row"` | Table | The checkbox column — the row itself selects instead. See Row selection |
54
+ | `rowSelectionMode: "row"` | Table | The checkbox column - the row itself selects instead. See Row selection |
55
55
  | `highlightSelectedRows: false` | Table | The highlight background on selected rows. Follows the selection mode by default |
56
56
  | `enablePagination: true` | Table | Opt-in: adds paging and the `Footer` pager. Off by default |
57
57
 
@@ -73,10 +73,10 @@ const grid = useTMDataGrid({
73
73
  ## Row selection
74
74
 
75
75
  On by default, in two modes. Both write to the same `rowSelection` state, so
76
- everything downstream — the toolbar count, `getSelectedRowModel()`, persistence
77
- — is unaffected by the choice.
76
+ everything downstream (the toolbar count, `getSelectedRowModel()`, persistence)
77
+ is unaffected by the choice.
78
78
 
79
- **Checkbox** — the default. A checkbox column is added as the first column, with
79
+ **Checkbox** - the default. A checkbox column is added as the first column, with
80
80
  a select-all box in its header. Clicking a row elsewhere does not select it, and
81
81
  a selected row is not highlighted: the checkbox already says so.
82
82
 
@@ -84,8 +84,8 @@ a selected row is not highlighted: the checkbox already says so.
84
84
  const grid = useTMDataGrid({ data, columns });
85
85
  ```
86
86
 
87
- **Row** — no checkbox column. Clicking a row toggles it, and the row takes the
88
- highlight background — the only feedback a click gives. Other rows keep their
87
+ **Row** - no checkbox column. Clicking a row toggles it, and the row takes the
88
+ highlight background - the only feedback a click gives. Other rows keep their
89
89
  state, so a click never clears the rest of the selection. Rows are focusable in
90
90
  this mode and Space or Enter toggles the focused row.
91
91
 
@@ -94,11 +94,11 @@ const grid = useTMDataGrid({ data, columns, rowSelectionMode: "row" });
94
94
  ```
95
95
 
96
96
  `enableRowSelection: false` removes both, and `rowSelectionMode` is then
97
- ignored. `TMDataGrid.Table`'s `onRowClick` still fires in either mode — under
97
+ ignored. `TMDataGrid.Table`'s `onRowClick` still fires in either mode - under
98
98
  `"row"` it runs in addition to the selection, not instead of it.
99
99
 
100
- `highlightSelectedRows` follows the mode — on for `"row"`, off for
101
- `"checkbox"` — and can be set to override either way. The colour is the
100
+ `highlightSelectedRows` follows the mode - on for `"row"`, off for
101
+ `"checkbox"` - and can be set to override either way. The colour is the
102
102
  `--dg-row-selected-bg` CSS variable (default `--mantine-primary-color-light`);
103
103
  change it on the grid element without touching the flag:
104
104
 
@@ -121,13 +121,13 @@ Off by default: the grid renders every filtered and sorted row and relies on
121
121
  virtualization. Three modes:
122
122
 
123
123
  ```tsx
124
- // No pagination (default) — TMDataGrid.Footer renders nothing.
124
+ // No pagination (default) - TMDataGrid.Footer renders nothing.
125
125
  const grid = useTMDataGrid({ data, columns });
126
126
 
127
- // Client pagination — pages locally, Footer renders its pager.
127
+ // Client pagination - pages locally, Footer renders its pager.
128
128
  const grid = useTMDataGrid({ data, columns, enablePagination: true });
129
129
 
130
- // Manual pagination — manualPagination implies enablePagination.
130
+ // Manual pagination - manualPagination implies enablePagination.
131
131
  const grid = useTMDataGrid({
132
132
  data: page.rows,
133
133
  columns,
@@ -215,8 +215,8 @@ grid into left, centre and right, then `columnOrder` sequences the centre while
215
215
  `columnPinning.left` / `.right` sequence the pinned lanes. Unpin first to move a
216
216
  column out of a pinned region.
217
217
 
218
- Ordering writes `columnOrder` as the complete leaf order — hidden and pinned
219
- columns included — so a column keeps its position when it is later shown or
218
+ Ordering writes `columnOrder` as the complete leaf order - hidden and pinned
219
+ columns included - so a column keeps its position when it is later shown or
220
220
  unpinned; moving a pinned column also rewrites its `columnPinning` array. Both
221
221
  slices are covered by `settingsKey`.
222
222
 
@@ -236,7 +236,7 @@ import {
236
236
  moveColumn({ table, columnId: "salary", targetId: "age", side: "before" });
237
237
  moveColumnByStep({ table, columnId: "salary", direction: 1 });
238
238
 
239
- // null at the edge of a region — what the move menu items disable themselves on.
239
+ // null at the edge of a region - what the move menu items disable themselves on.
240
240
  const next = getStepTargetColumn({ table, columnId: "salary", direction: 1 });
241
241
  ```
242
242
 
@@ -269,11 +269,11 @@ carry `data-focused` / `data-selected` / `data-edge-*`.
269
269
  The body is one tab stop: controls inside body cells take `tabindex="-1"` while
270
270
  cell selection is on, so Tab leaves the grid instead of walking one control per
271
271
  mounted row. Enter/F2 steps in, Escape steps out, Space ticks the row from any
272
- cell. Custom cell controls should do the same —
272
+ cell. Custom cell controls should do the same -
273
273
  `tabIndex={useCellControlTabIndex()}`. Header controls are untouched.
274
274
 
275
- The generated lanes (checkbox, tree, details) are selectable and navigable —
276
- they take the tint and the outline — but never exported; a block covering only
275
+ The generated lanes (checkbox, tree, details) are selectable and navigable -
276
+ they take the tint and the outline - but never exported; a block covering only
277
277
  those disables Copy and Export.
278
278
 
279
279
  Drag across cells to select a rectangle; Shift+click extends one. The state is
@@ -284,8 +284,8 @@ Move them with `ui.actions.setFocusedCell` / `setCellRange`, follow them with
284
284
 
285
285
  Right-clicking inside the selection offers Copy, "Export as CSV for Excel" and
286
286
  an Include headers toggle; a consumer's `rowContextMenu` items follow a divider.
287
- The CSV is Nordic-Excel shaped — `sep=;` line, UTF-8 BOM, CRLF, semicolons,
288
- decimal comma — and `cellExport` on `TMDataGrid.Table` changes any of that.
287
+ The CSV is Nordic-Excel shaped - `sep=;` line, UTF-8 BOM, CRLF, semicolons,
288
+ decimal comma - and `cellExport` on `TMDataGrid.Table` changes any of that.
289
289
  Generated lanes (checkbox, tree, details) are never exported, and what is
290
290
  written is the cell's value rather than what it renders.
291
291
 
@@ -295,7 +295,7 @@ Always enabled. Only rows within the viewport, plus a small overscan, are
295
295
  mounted, so page size does not affect how much is rendered.
296
296
 
297
297
  `overscan` on `useTMDataGrid` (default `6`) sets how many rows are kept mounted
298
- on each side of the viewport — the one configurable part.
298
+ on each side of the viewport - the one configurable part.
299
299
 
300
300
  Row height comes from `meta.rowHeight`, or from the `size` prop when unset. Rows
301
301
  are fixed height; an accurate value keeps scrolling precise.
@@ -305,12 +305,12 @@ are fixed height; an accurate value keeps scrolling precise.
305
305
  ### Calling getCanX() in a custom component
306
306
 
307
307
  ```tsx
308
- // Wrong — memoized under the React Compiler, so the button
308
+ // Wrong - memoized under the React Compiler, so the button
309
309
  // never reappears when enableColumnFilters flips back to true.
310
310
  const { table } = useTMDataGridContext();
311
311
  if (!table.getAllLeafColumns().some((c) => c.getCanFilter())) return null;
312
312
 
313
- // Right — features is a value that changes.
313
+ // Right - features is a value that changes.
314
314
  const { table, features } = useTMDataGridContext();
315
315
  const { canFilterAny } = getGridCapabilities(table, features);
316
316
  if (!canFilterAny) return null;
@@ -325,28 +325,28 @@ overlap or leave gaps as you scroll. Set `meta.rowHeight` instead.
325
325
  ### Reordering a pinned column through columnOrder
326
326
 
327
327
  ```tsx
328
- // Wrong — the left and right regions are sequenced by columnPinning,
328
+ // Wrong - the left and right regions are sequenced by columnPinning,
329
329
  // so this moves nothing on screen.
330
330
  table.setColumnOrder(["__select__", "department", "id", …]);
331
331
 
332
- // Right — moveColumn writes whichever slice the column's region uses.
332
+ // Right - moveColumn writes whichever slice the column's region uses.
333
333
  moveColumn({ table, columnId: "department", targetId: "id", side: "before" });
334
334
  ```
335
335
 
336
336
  ### Rendering the Footer without enabling pagination
337
337
 
338
338
  ```tsx
339
- // Wrong — pagination defaults to off, so the Footer renders nothing.
339
+ // Wrong - pagination defaults to off, so the Footer renders nothing.
340
340
  const grid = useTMDataGrid({ data, columns });
341
341
  <TMDataGrid.Footer />
342
342
 
343
- // Right — opt in (manualPagination: true also counts).
343
+ // Right - opt in (manualPagination: true also counts).
344
344
  const grid = useTMDataGrid({ data, columns, enablePagination: true });
345
345
  <TMDataGrid.Footer />
346
346
  ```
347
347
 
348
348
  ### Expecting a table-level flag to override a column
349
349
 
350
- Table and column options compose — the table flag gates the feature, and the
350
+ Table and column options compose - the table flag gates the feature, and the
351
351
  column flag narrows it further. `enableSorting: true` on a column does not
352
352
  re-enable sorting for a table that set `enableSorting: false`.
@@ -10,7 +10,7 @@ description: >
10
10
  metadata:
11
11
  type: core
12
12
  library: '@jielga/tmdatagrid'
13
- library_version: '1.0.0'
13
+ library_version: '1.0.1'
14
14
  sources:
15
15
  - 'Jielga/TMDataGrid:src/docs/getting-started.md'
16
16
  - 'Jielga/TMDataGrid:src/docs/components.md'
@@ -18,12 +18,12 @@ sources:
18
18
  - 'Jielga/TMDataGrid:src/tmdatagrid/core/sizes.ts'
19
19
  ---
20
20
 
21
- # TMDataGrid — Getting started
21
+ # TMDataGrid - Getting started
22
22
 
23
23
  `useTMDataGrid` creates the table, `TMDataGrid` provides it through context, and
24
24
  the parts rendered inside read what they need from that context. Only the parts
25
25
  you render exist, and only the features you enable have state: pagination is
26
- opt-in via `enablePagination` — by default every row renders, virtualized.
26
+ opt-in via `enablePagination` - by default every row renders, virtualized.
27
27
 
28
28
  ## Install
29
29
 
@@ -107,7 +107,7 @@ Spread the hook result (`{...grid}`) rather than assigning `table`, `ui` and
107
107
  | Pagination | Off by default: all rows render, virtualized. Opt in with `enablePagination: true`. |
108
108
  | Sizing | `size="xs"` to `size="xl"` scales rows, type and controls. |
109
109
 
110
- Each is bound to a capability check — disabling the standard table or column
110
+ Each is bound to a capability check - disabling the standard table or column
111
111
  option also removes its interface. Column ordering (`enableColumnOrdering`) and
112
112
  pagination (`enablePagination`) are the two switches the grid defines itself.
113
113
  See the `features` skill.
@@ -125,7 +125,7 @@ takes a fixed pixel width once resized or pinned.
125
125
 
126
126
  All read the grid from context and must be rendered inside `TMDataGrid`, except
127
127
  `TMDataGrid.FilterPills`, which takes the grid as an `api` prop and can live
128
- anywhere on the page. Order and presence are up to you — the root is a plain
128
+ anywhere on the page. Order and presence are up to you - the root is a plain
129
129
  flex column.
130
130
 
131
131
  | Component | Props | Notes |
@@ -134,12 +134,12 @@ flex column.
134
134
  | `TMDataGrid.Table` | `onRowClick(row)`, `rowContextMenu` render prop, `rowContextMenuProps` | Header, virtualized body, filter panel. `onRowClick` runs in addition to selection under `rowSelectionMode: "row"`. |
135
135
  | `TMDataGrid.Footer` | `pageSizeOptions` (default `[10, 25, 50, 100]`), `pagination` render prop | Pagination controls. Renders nothing unless pagination is enabled. |
136
136
  | `TMDataGrid.Toolbar` | `children` | Flex row above the grid. |
137
- | `TMDataGrid.Spacer` | — | Pushes later toolbar items right. |
137
+ | `TMDataGrid.Spacer` | - | Pushes later toolbar items right. |
138
138
  | `TMDataGrid.SummaryCount` | `children` | Visible rows out of total. |
139
- | `TMDataGrid.FilterButton` | — | Toggles filter panel. Renders nothing if no column is filterable. |
140
- | `TMDataGrid.ColumnsButton` | — | Opens column manager. Renders nothing if no column is hideable. |
141
- | `TMDataGrid.FilterPanel` | — | Rendered by `.Table`; exported for custom layouts. Header close button, Escape, click-away, "Add filter" and "Clear all". |
142
- | `TMDataGrid.ColumnsPanel` | — | Rendered by `.ColumnsButton`; exported for custom layouts. |
139
+ | `TMDataGrid.FilterButton` | - | Toggles filter panel. Renders nothing if no column is filterable. |
140
+ | `TMDataGrid.ColumnsButton` | - | Opens column manager. Renders nothing if no column is hideable. |
141
+ | `TMDataGrid.FilterPanel` | - | Rendered by `.Table`; exported for custom layouts. Header close button, Escape, click-away, "Add filter" and "Clear all". |
142
+ | `TMDataGrid.ColumnsPanel` | - | Rendered by `.ColumnsButton`; exported for custom layouts. |
143
143
  | `TMDataGrid.FilterPills` | `api`, `size` (default `"sm"`), `showClearAll` (default `true`), `onPillClick(columnId)`, `className` | One pill per active filter, ✕ to clear it. Takes the api as a prop, so it can be rendered outside the grid. Also exported as `TMDataGridFilterPills`. |
144
144
 
145
145
  Pass the row type so `onRowClick` stays typed:
@@ -149,8 +149,8 @@ Pass the row type so `onRowClick` stays typed:
149
149
  ```
150
150
 
151
151
  `rowContextMenu` fills the dropdown of a Mantine `Menu` the grid opens at the
152
- pointer on a right-click (or a long press). The grid owns the `Menu` — opening,
153
- positioning, and closing on Escape, outside click, body scroll or an item pick —
152
+ pointer on a right-click (or a long press). The grid owns the `Menu` - opening,
153
+ positioning, and closing on Escape, outside click, body scroll or an item pick -
154
154
  so the render prop only says what goes in it:
155
155
 
156
156
  ```tsx
@@ -174,7 +174,7 @@ so the render prop only says what goes in it:
174
174
  `cell` is the one that was right-clicked, `table` is there for actions that read
175
175
  the selection, and `close` is for dropdown content that is not a `Menu.Item`
176
176
  (those close themselves). Return `null` to leave a row without a menu. It is
177
- called during render, and only for the open row, so it must stay pure — put the
177
+ called during render, and only for the open row, so it must stay pure - put the
178
178
  work in the item handlers. Right-clicking does not change the selection or the
179
179
  highlight; the row carries `data-context-menu` while its menu is open. Pass
180
180
  `rowContextMenuProps` for `width`, `shadow`, `position` and the rest.
@@ -241,7 +241,7 @@ A new array on every render rebuilds the table's column model. Define `columns`
241
241
  at module scope; keep `data` stable with `useMemo`.
242
242
 
243
243
  ```tsx
244
- // Wrong — rebuilds the column model every render.
244
+ // Wrong - rebuilds the column model every render.
245
245
  export function Employees({ data }: { data: Employee[] }) {
246
246
  const columns = columnHelper.columns([...]);
247
247
  const grid = useTMDataGrid({ data, columns });
@@ -251,7 +251,7 @@ export function Employees({ data }: { data: Employee[] }) {
251
251
  ### Root with no bounded height
252
252
 
253
253
  The root is `overflow: hidden`. Without `flex: 1; minHeight: 0` in a flex parent
254
- or an explicit height, the table area collapses and no rows are visible — there
254
+ or an explicit height, the table area collapses and no rows are visible - there
255
255
  is no error.
256
256
 
257
257
  ### Assigning the hook result field by field
@@ -261,7 +261,7 @@ and feature toggles stop being reactive. Spread instead: `<TMDataGrid {...grid}>
261
261
 
262
262
  ### Expecting Footer to enable pagination
263
263
 
264
- Rendering `TMDataGrid.Footer` does not switch pagination on — the option does.
264
+ Rendering `TMDataGrid.Footer` does not switch pagination on - the option does.
265
265
  With pagination off (the default) the Footer renders nothing and every row is
266
266
  rendered, virtualized. Set `enablePagination: true` (or `manualPagination:
267
267
  true` for server paging) to page the data and show the pager.
@@ -13,14 +13,14 @@ description: >
13
13
  metadata:
14
14
  type: core
15
15
  library: '@jielga/tmdatagrid'
16
- library_version: '1.0.0'
16
+ library_version: '1.0.1'
17
17
  sources:
18
18
  - 'Jielga/TMDataGrid:src/docs/use-tm-data-grid.md'
19
19
  - 'Jielga/TMDataGrid:src/tmdatagrid/useTMDataGrid.tsx'
20
20
  - 'Jielga/TMDataGrid:src/tmdatagrid/core/persistence.ts'
21
21
  ---
22
22
 
23
- # TMDataGrid — useTMDataGrid
23
+ # TMDataGrid - useTMDataGrid
24
24
 
25
25
  Creates the table instance and the state used by the grid interface.
26
26
 
@@ -69,7 +69,7 @@ rather than forwarded to TanStack.
69
69
 
70
70
  | Slice | Default |
71
71
  | --- | --- |
72
- | `pagination` | `{ pageIndex: 0, pageSize: 25 }` — inert until pagination is enabled |
72
+ | `pagination` | `{ pageIndex: 0, pageSize: 25 }` - inert until pagination is enabled |
73
73
  | `columnPinning.left` | The checkbox column, followed by any columns you provide |
74
74
  | `globalFilterFn` | `"includesString"` |
75
75
 
@@ -136,7 +136,7 @@ names are typed per group, so only valid names are accepted.
136
136
  Restoring happens once on mount through `initialState`. Writing is a subscription
137
137
  to the table store, so state changed directly through the table API is persisted
138
138
  too. Only selected slices are read back, and unrecognised keys are ignored. All
139
- storage access is guarded — if storage is unavailable, disabled or full,
139
+ storage access is guarded - if storage is unavailable, disabled or full,
140
140
  persistence is skipped rather than throwing.
141
141
 
142
142
  ## Return value
@@ -180,7 +180,7 @@ The last two move the cell cursor and the selected rectangle under
180
180
  `cellSelection`; DOM focus follows `focusedCell` and scrolls its row into view.
181
181
 
182
182
  `startColumnDrag` / `endColumnDrag` are called by the header cells while a column is being dragged, and
183
- `ui.draggedColumnId` holds the column being moved — browsers keep `dataTransfer`
183
+ `ui.draggedColumnId` holds the column being moved - browsers keep `dataTransfer`
184
184
  unreadable until the drop.
185
185
 
186
186
  `openColumnFilter(grid, columnId)` combines the two steps the column menu uses:
@@ -190,11 +190,11 @@ add an empty filter row for the column if none exists, then open the panel.
190
190
 
191
191
  ### Reading store state during render
192
192
 
193
- `grid.table.store.state` is a plain read — it does not subscribe the component,
193
+ `grid.table.store.state` is a plain read - it does not subscribe the component,
194
194
  so the value is correct on first render and then never updates.
195
195
 
196
196
  ```tsx
197
- // Wrong — never re-renders when the selection changes.
197
+ // Wrong - never re-renders when the selection changes.
198
198
  const count = Object.keys(grid.table.store.state.rowSelection).length;
199
199
 
200
200
  // Right.
@@ -219,5 +219,5 @@ Include a tenant or user identifier in the key.
219
219
  ### Unstable data reference
220
220
 
221
221
  A new array identity on each render re-runs the row model. `data={rows ?? []}`
222
- allocates a fresh `[]` every render when `rows` is undefined — hoist the empty
222
+ allocates a fresh `[]` every render when `rows` is undefined - hoist the empty
223
223
  array to a module-scope constant or `useMemo` the expression.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: server-side
3
3
  description: >
4
- Drive TMDataGrid from a server with TanStack manual modes — manualPagination,
4
+ Drive TMDataGrid from a server with TanStack manual modes - manualPagination,
5
5
  manualSorting, manualFiltering, rowCount, controlled state and onXChange
6
6
  callbacks. Covers the loading and totalRowCount meta fields, forwarding the
7
7
  plain-JSON columnFilters model to an API with isFilterActive, persistence
@@ -10,18 +10,18 @@ description: >
10
10
  metadata:
11
11
  type: core
12
12
  library: '@jielga/tmdatagrid'
13
- library_version: '1.0.0'
13
+ library_version: '1.0.1'
14
14
  sources:
15
15
  - 'Jielga/TMDataGrid:src/docs/server-side.md'
16
16
  - 'Jielga/TMDataGrid:src/tmdatagrid/useTMDataGrid.tsx'
17
17
  ---
18
18
 
19
- # TMDataGrid — Server-side data
19
+ # TMDataGrid - Server-side data
20
20
 
21
21
  The grid reads rows through `getPaginatedRowModel()` and totals through
22
22
  `getRowCount()` and `getPageCount()`, all of which respect TanStack's manual
23
23
  modes. A server-driven grid therefore needs only the standard `manual*`
24
- configuration — no grid-specific options. `manualPagination: true` also
24
+ configuration - no grid-specific options. `manualPagination: true` also
25
25
  switches the grid's pagination flag on, so `TMDataGrid.Footer` renders its
26
26
  pager without `enablePagination`. Declare `pageCount: -1` when the total is
27
27
  unknown; the next button then stays enabled.
@@ -85,7 +85,7 @@ Filter values are plain JSON, so `columnFilters` forwards without transformation
85
85
  ]
86
86
  ```
87
87
 
88
- Translate at the API boundary, and skip entries whose value is still empty —
88
+ Translate at the API boundary, and skip entries whose value is still empty -
89
89
  those match all rows:
90
90
 
91
91
  ```ts
@@ -122,8 +122,8 @@ const selectedIds = useSelector(grid.table.store, (state) =>
122
122
 
123
123
  ### Omitting rowCount under manualPagination
124
124
 
125
- Without `rowCount` the table derives the total from the rows it was handed —
126
- one page — so `getPageCount()` returns 1. The footer shows "1–25 of 25" and the
125
+ Without `rowCount` the table derives the total from the rows it was handed -
126
+ one page - so `getPageCount()` returns 1. The footer shows "1–25 of 25" and the
127
127
  next-page button is disabled, with no error. Pass the server total.
128
128
 
129
129
  ### Filters sent without isFilterActive
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: testing
3
3
  description: >
4
- Write tests against TMDataGrid from a consuming app — Playwright or React
4
+ Write tests against TMDataGrid from a consuming app - Playwright or React
5
5
  Testing Library. Covers the data-dg-part contract, data-row-id/data-column-id
6
6
  coordinates, naming a grid with data-testid, the roles and ARIA the grid
7
7
  publishes, the cell/gridcell role flip under cell selection, reaching rows
@@ -11,25 +11,25 @@ description: >
11
11
  metadata:
12
12
  type: core
13
13
  library: '@jielga/tmdatagrid'
14
- library_version: '1.0.0'
14
+ library_version: '1.0.1'
15
15
  sources:
16
16
  - 'Jielga/TMDataGrid:src/docs/testing.md'
17
17
  - 'Jielga/TMDataGrid:src/tmdatagrid/components/TMDataGrid.tsx'
18
18
  - 'Jielga/TMDataGrid:src/tmdatagrid/components/TMDataGridTable.tsx'
19
19
  ---
20
20
 
21
- # TMDataGrid — Testing
21
+ # TMDataGrid - Testing
22
22
 
23
23
  The grid names its own pieces so a suite can be written against structure
24
24
  rather than against copy or class names. Three hooks carry it:
25
25
 
26
- - **`data-dg-part`** — *what* an element is (`"row"`, `"filter-button"`)
27
- - **`data-row-id` / `data-column-id`** — *which* one, using your own ids
28
- - **roles and ARIA** — framework-neutral, and what a screen reader reads
26
+ - **`data-dg-part`** - *what* an element is (`"row"`, `"filter-button"`)
27
+ - **`data-row-id` / `data-column-id`** - *which* one, using your own ids
28
+ - **roles and ARIA** - framework-neutral, and what a screen reader reads
29
29
 
30
30
  Selectors compose: `[data-dg-part="save-row"][data-row-id="42"]`.
31
31
 
32
- The grid mints no `data-testid` of its own — that attribute belongs to the app,
32
+ The grid mints no `data-testid` of its own - that attribute belongs to the app,
33
33
  and Playwright's `testIdAttribute` is configurable. `@mantine/core` and
34
34
  `@tanstack/*` ship none either.
35
35
 
@@ -56,7 +56,7 @@ accessible name belongs to the element carrying the `grid` role.
56
56
  | Body cell | `cell`, or `gridcell` under cell selection | `data-row-id`, `data-column-id`, `data-editing`, `data-dirty`, `data-invalid`, `data-focused` |
57
57
  | Header cell | `columnheader` | `data-column-id`, `aria-sort` |
58
58
 
59
- Body cells carry no `data-dg-part` — the coordinate pair already names them.
59
+ Body cells carry no `data-dg-part` - the coordinate pair already names them.
60
60
 
61
61
  ## Parts
62
62
 
@@ -79,7 +79,7 @@ Body cells carry no `data-dg-part` — the coordinate pair already names them.
79
79
  **Keyed by both**: `editor`.
80
80
 
81
81
  Inside a `filter-row` the controls are `filter-column`, `filter-operator` and
82
- `filter-value` — or `filter-value-from` / `filter-value-to` for `between`.
82
+ `filter-value` - or `filter-value-from` / `filter-value-to` for `between`.
83
83
 
84
84
  A column declaring `meta.filterControl` or `meta.editor` renders your own
85
85
  component in that slot, so `filter-value` and `editor-input` cover the built-ins
@@ -131,10 +131,10 @@ Only the rows in the viewport plus overscan are in the DOM. A row at index 500
131
131
  has no element, and Playwright cannot scroll to what it cannot find.
132
132
 
133
133
  **Count rows off the grid.** `data-dg-row-count` is the body rows the grid is
134
- showing — the current page under pagination, everything the filters left
134
+ showing - the current page under pagination, everything the filters left
135
135
  otherwise. (`aria-rowcount` also counts the header and summary rows.)
136
136
 
137
- **Reach a row by narrowing to it** — filter or search. Faster, more stable, and
137
+ **Reach a row by narrowing to it** - filter or search. Faster, more stable, and
138
138
  what a user would do. Where the row must be reached in place,
139
139
  `grid.scrollToRow({ rowId, align })` moves the virtualizer and answers whether
140
140
  the row was reachable; from Playwright that needs the app to expose the api.
@@ -164,7 +164,7 @@ layout, so the count depends on the stubbed element size. Assert
164
164
  ### getByRole("cell") on a grid with cell selection
165
165
 
166
166
  `enableCellSelection` turns every `cell` into a `gridcell`, and the grid's
167
- `table` into a `grid` — a widget with a keyboard cursor is not a table of
167
+ `table` into a `grid` - a widget with a keyboard cursor is not a table of
168
168
  content. Tests written on the role break when the feature is switched on. Query
169
169
  cells by `[data-row-id][data-column-id]`, which do not move.
170
170
 
@@ -5,7 +5,7 @@ import type { TMDataGridApi } from "./useTMDataGrid";
5
5
  /**
6
6
  * Row shape the chrome components work against. They only ever touch columns,
7
7
  * rows and cells generically, so the concrete row type is erased at the context
8
- * boundary — the same trick Mantine uses for its compound components.
8
+ * boundary - the same trick Mantine uses for its compound components.
9
9
  */
10
10
  export type TMDataGridRowData = Record<string, unknown>;
11
11
 
@@ -36,7 +36,7 @@ export function useTMDataGridContext(): TMDataGridContextValue {
36
36
  *
37
37
  * `-1` once cell selection is on, and this is what makes the promise of one tab
38
38
  * stop true. Without it the browser walks Tab into the checkbox of every
39
- * mounted row — a grid showing twenty rows would be twenty tab stops, and
39
+ * mounted row - a grid showing twenty rows would be twenty tab stops, and
40
40
  * scrolling would change how many. Enter or F2 steps into the cell instead,
41
41
  * which reaches a `-1` control perfectly well.
42
42
  *