@jielga/tmdatagrid 0.2.0 → 0.5.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.
- package/dist/index.d.ts +1337 -94
- package/dist/index.js +2812 -1189
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +2 -2
- package/skills/columns/SKILL.md +1 -1
- package/skills/features/SKILL.md +61 -11
- package/skills/getting-started/SKILL.md +41 -6
- package/skills/options/SKILL.md +12 -2
- package/skills/server-side/SKILL.md +1 -1
- package/src/tmdatagrid/TMDataGridContext.ts +26 -0
- package/src/tmdatagrid/components/TMDataGrid.module.css +21 -1
- package/src/tmdatagrid/components/TMDataGrid.tsx +29 -1
- package/src/tmdatagrid/components/TMDataGridColumnsPanel.tsx +12 -6
- package/src/tmdatagrid/components/TMDataGridDetailsColumn.module.css +33 -0
- package/src/tmdatagrid/components/TMDataGridDetailsColumn.tsx +160 -0
- package/src/tmdatagrid/components/TMDataGridFilterPanel.module.css +15 -0
- package/src/tmdatagrid/components/TMDataGridFilterPanel.tsx +192 -37
- package/src/tmdatagrid/components/TMDataGridFilterPills.module.css +35 -0
- package/src/tmdatagrid/components/TMDataGridFilterPills.tsx +136 -0
- package/src/tmdatagrid/components/TMDataGridFooter.module.css +8 -0
- package/src/tmdatagrid/components/TMDataGridFooter.tsx +63 -43
- package/src/tmdatagrid/components/TMDataGridGroupColumn.module.css +48 -0
- package/src/tmdatagrid/components/TMDataGridGroupColumn.tsx +159 -0
- package/src/tmdatagrid/components/TMDataGridHeaderCell.module.css +18 -0
- package/src/tmdatagrid/components/TMDataGridHeaderCell.tsx +249 -44
- package/src/tmdatagrid/components/TMDataGridSearch.tsx +122 -0
- package/src/tmdatagrid/components/TMDataGridSelectColumn.tsx +103 -16
- package/src/tmdatagrid/components/TMDataGridTable.module.css +160 -5
- package/src/tmdatagrid/components/TMDataGridTable.tsx +1380 -43
- package/src/tmdatagrid/components/TMDataGridToolbar.tsx +33 -7
- package/src/tmdatagrid/components/icons.ts +6 -0
- package/src/tmdatagrid/components/sticky.module.css +77 -16
- package/src/tmdatagrid/core/autosize.ts +125 -0
- package/src/tmdatagrid/core/capabilities.ts +122 -11
- package/src/tmdatagrid/core/cellExport.ts +321 -0
- package/src/tmdatagrid/core/cellNavigation.ts +103 -0
- package/src/tmdatagrid/core/cellRange.ts +118 -0
- package/src/tmdatagrid/core/columnOptions.ts +123 -0
- package/src/tmdatagrid/core/columnUtils.ts +18 -0
- package/src/tmdatagrid/core/expanding.ts +93 -0
- package/src/tmdatagrid/core/filterOperators.ts +198 -12
- package/src/tmdatagrid/core/grouping.ts +21 -0
- package/src/tmdatagrid/core/labels.ts +211 -0
- package/src/tmdatagrid/core/labelsSv.ts +108 -0
- package/src/tmdatagrid/core/persistence.ts +12 -0
- package/src/tmdatagrid/core/rowSelection.ts +202 -0
- package/src/tmdatagrid/core/summary.ts +35 -0
- package/src/tmdatagrid/index.ts +96 -2
- package/src/tmdatagrid/useTMDataGrid.tsx +558 -37
package/dist/styles.css
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
._root_1jc4n_8[data-size=xs]{--dg-row-height:34px;--dg-header-height:32px;--dg-font-size:var(--mantine-font-size-xs);--dg-padding:6px}._root_1jc4n_8[data-size=sm]{--dg-row-height:42px;--dg-header-height:38px;--dg-font-size:var(--mantine-font-size-sm);--dg-padding:8px}._root_1jc4n_8[data-size=md]{--dg-row-height:52px;--dg-header-height:44px;--dg-font-size:var(--mantine-font-size-sm);--dg-padding:10px}._root_1jc4n_8[data-size=lg]{--dg-row-height:62px;--dg-header-height:52px;--dg-font-size:var(--mantine-font-size-md);--dg-padding:14px}._root_1jc4n_8[data-size=xl]{--dg-row-height:72px;--dg-header-height:60px;--dg-font-size:var(--mantine-font-size-lg);--dg-padding:18px}._root_1jc4n_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);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}._filterPanel_1jw69_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_1jw69_16{align-items:flex-end;gap:var(--mantine-spacing-xs);display:flex}._footer_6r35a_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}._tableWrapper_utldp_2{flex-direction:column;flex:1;min-height:0;display:flex;position:relative}._scrollContainer_utldp_10{flex:1;min-height:0;position:relative;overflow:auto}._grid_utldp_18{display:grid}._headerRow_utldp_24{grid-column:1/-1;grid-template-columns:subgrid;z-index:3;display:grid;position:sticky;top:0}._bodyRow_utldp_33{--row-bg:var(--mantine-color-body);grid-column:1/-1;grid-template-columns:subgrid;align-items:stretch;display:grid}._bodyRow_utldp_33:hover{--row-bg:var(--mantine-color-default-hover)}._bodyRow_utldp_33[data-highlighted=true]{--row-bg:var(--dg-row-selected-bg)}._bodyRow_utldp_33[data-selects-on-click=true]{-webkit-user-select:none;user-select:none}._bodyRow_utldp_33:focus-visible{outline:2px solid var(--mantine-primary-color-filled);outline-offset:-2px}._bodyCell_utldp_65{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}._cellContent_utldp_63{white-space:nowrap;text-overflow:ellipsis;min-width:0;overflow:hidden}._bodyCell_utldp_65[data-align=right]{justify-content:flex-end}._bodyCell_utldp_65[data-align=center]{justify-content:center}._bodyCell_utldp_65[data-align=left] ._cellContent_utldp_63{flex:auto}._messageRow_utldp_95{justify-content:center;align-items:center;gap:var(--mantine-spacing-xs);padding:var(--mantine-spacing-xl);flex-direction:column;grid-column:1/-1;display:flex}._stickyLeft_1iqrf_11{border-right:1px solid var(--mantine-color-default-border)}._stickyRight_1iqrf_15{border-left:1px solid var(--mantine-color-default-border)}._stickyLeft_1iqrf_11:after,._stickyRight_1iqrf_15:after{content:"";pointer-events:none;width:4px;position:absolute;top:0;bottom:0}._stickyLeft_1iqrf_11:after{background:linear-gradient(90deg,var(--lightningcss-light,#00000014)var(--lightningcss-dark,#00000047),#0000);left:100%}._stickyRight_1iqrf_15:after{background:linear-gradient(270deg,var(--lightningcss-light,#00000014)var(--lightningcss-dark,#00000047),#0000);right:100%}._headerCell_1oqkm_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_1oqkm_1[data-align=right]{justify-content:flex-end}._headerCell_1oqkm_1[data-align=center]{justify-content:center}._headerCell_1oqkm_1[data-active=true] ._headerTitle_1oqkm_25{color:var(--mantine-primary-color-filled)}._headerTitle_1oqkm_25{text-overflow:ellipsis;white-space:nowrap;flex:auto;min-width:0;overflow:hidden}._headerCell_1oqkm_1[data-align=right] ._headerTitle_1oqkm_25{text-align:right}._headerCell_1oqkm_1[data-align=center] ._headerTitle_1oqkm_25{justify-content:center;display:flex}._headerActions_1oqkm_48{flex-shrink:0;align-items:center;gap:0;margin-left:auto;display:flex}._headerCell_1oqkm_1[data-align=right] ._headerActions_1oqkm_48{order:-1;margin-left:0;margin-right:auto}._headerAction_1oqkm_48{display:none}._headerCell_1oqkm_1:hover ._headerAction_1oqkm_48,._headerCell_1oqkm_1:focus-within ._headerAction_1oqkm_48,._headerAction_1oqkm_48[data-pinned-visible=true]{display:flex}._headerCellSortable_1oqkm_77{cursor:pointer}._headerCellDragging_1oqkm_84{opacity:.4}._headerCellDropBefore_1oqkm_88:before,._headerCellDropAfter_1oqkm_89:before{content:"";background-color:var(--mantine-primary-color-filled);z-index:5;pointer-events:none;width:2px;position:absolute;top:0;bottom:0}._headerCellDropBefore_1oqkm_88:before{left:0}._headerCellDropAfter_1oqkm_89:before{right:0}._columnSeparator_1oqkm_109{z-index:1;justify-content:center;width:10px;height:50%;display:flex;position:absolute;top:25%;right:0;transform:translate(50%)}._columnSeparatorResizable_1oqkm_121{cursor:col-resize;touch-action:none}._columnSeparator_1oqkm_109:after{content:"";background-color:#0000;width:1px;height:100%;transition:background-color .12s,width .12s}._headerCell_1oqkm_1:hover ._columnSeparator_1oqkm_109:after,._headerCell_1oqkm_1:focus-within ._columnSeparator_1oqkm_109:after{background-color:var(--mantine-color-default-border)}._columnSeparatorResizable_1oqkm_121:hover:after,._columnSeparatorActive_1oqkm_144:after{background-color:var(--mantine-primary-color-filled);width:2px}._menuItemActive_1oqkm_150{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_qequm_8[data-size=xs]{--dg-row-height:34px;--dg-header-height:32px;--dg-font-size:var(--mantine-font-size-xs);--dg-padding:6px}._root_qequm_8[data-size=sm]{--dg-row-height:42px;--dg-header-height:38px;--dg-font-size:var(--mantine-font-size-sm);--dg-padding:8px}._root_qequm_8[data-size=md]{--dg-row-height:52px;--dg-header-height:44px;--dg-font-size:var(--mantine-font-size-sm);--dg-padding:10px}._root_qequm_8[data-size=lg]{--dg-row-height:62px;--dg-header-height:52px;--dg-font-size:var(--mantine-font-size-md);--dg-padding:14px}._root_qequm_8[data-size=xl]{--dg-row-height:72px;--dg-header-height:60px;--dg-font-size:var(--mantine-font-size-lg);--dg-padding:18px}._root_qequm_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-z-pinned-cell:2;--dg-z-focused-cell:3;--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_1nt60_2{flex-direction:column;flex:1;min-height:0;display:flex;position:relative}._scrollContainer_1nt60_10{flex:1;min-height:0;position:relative;overflow:auto}._grid_1nt60_18{display:grid}._headerRow_1nt60_24{grid-column:1/-1;grid-template-columns:subgrid;z-index:var(--dg-z-header,6);display:grid;position:sticky;top:0}._bodyRow_1nt60_33{--row-bg:var(--mantine-color-body);grid-column:1/-1;grid-template-columns:subgrid;align-items:stretch;display:grid}._bodyRow_1nt60_33:hover{--row-bg:var(--mantine-color-default-hover)}._bodyRow_1nt60_33[data-grouped=true]{--row-bg:var(--dg-row-group-bg)}._bodyRow_1nt60_33[data-context-menu=true]{--row-bg:var(--mantine-color-default-hover)}._bodyRow_1nt60_33[data-selected-bg=true]{--row-bg:var(--dg-row-selected-bg)}._bodyRow_1nt60_33[data-highlighted=true]{--row-bg:var(--dg-row-highlight-bg)}._bodyRow_1nt60_33[data-selects-on-click=true]{-webkit-user-select:none;user-select:none}._bodyRow_1nt60_33:focus-visible{outline:2px solid var(--mantine-primary-color-filled);outline-offset:-2px}._bodyCell_1nt60_91{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_1nt60_91[data-control-column=true]{padding:0}._grid_1nt60_18[data-range-dragging]{-webkit-user-select:none;user-select:none}._bodyCell_1nt60_91[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_1nt60_91[data-edge-top=true]{--dg-edge-top:var(--mantine-primary-color-filled)}._bodyCell_1nt60_91[data-edge-bottom=true]{--dg-edge-bottom:var(--mantine-primary-color-filled)}._bodyCell_1nt60_91[data-edge-left=true]{--dg-edge-left:var(--mantine-primary-color-filled)}._bodyCell_1nt60_91[data-edge-right=true]{--dg-edge-right:var(--mantine-primary-color-filled)}._bodyCell_1nt60_91[data-focused=true]{outline:2px solid var(--mantine-primary-color-filled);outline-offset:-2px}._cellContent_1nt60_89{white-space:nowrap;text-overflow:ellipsis;min-width:0;overflow:hidden}._bodyCell_1nt60_91[data-align=right]{justify-content:flex-end}._bodyCell_1nt60_91[data-align=center]{justify-content:center}._bodyCell_1nt60_91[data-align=left] ._cellContent_1nt60_89{flex:auto}._detailsCell_1nt60_204{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_1nt60_215{justify-content:center;align-items:center;gap:var(--mantine-spacing-xs);padding:var(--mantine-spacing-xl);flex-direction:column;grid-column:1/-1;display:flex}._summaryRow_1nt60_228{grid-column:1/-1;grid-template-columns:subgrid;z-index:var(--dg-z-summary-row,4);display:grid;position:sticky;bottom:0}._summaryCell_1nt60_237{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_1nt60_237[data-control-column=true]{padding:0}._summaryCell_1nt60_237[data-align=right]{justify-content:flex-end}._summaryCell_1nt60_237[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_1b6k8_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_1b6k8_1[data-align=right]{justify-content:flex-end}._headerCell_1b6k8_1[data-align=center]{justify-content:center}._headerCell_1b6k8_1[data-control-column=true]{padding:0}._headerCell_1b6k8_1[data-active=true] ._headerTitle_1b6k8_32{color:var(--mantine-primary-color-filled)}._headerTitle_1b6k8_32{text-overflow:ellipsis;white-space:nowrap;flex:auto;min-width:0;overflow:hidden}._headerCell_1b6k8_1[data-align=right] ._headerTitle_1b6k8_32{text-align:right}._headerCell_1b6k8_1[data-align=center] ._headerTitle_1b6k8_32{justify-content:center;display:flex}._headerActions_1b6k8_55{flex-shrink:0;align-items:center;gap:0;margin-left:auto;display:flex}._headerCell_1b6k8_1[data-align=right] ._headerActions_1b6k8_55{order:-1;margin-left:0;margin-right:auto}._headerAction_1b6k8_55{display:none}._headerCell_1b6k8_1:hover ._headerAction_1b6k8_55,._headerCell_1b6k8_1:focus-within ._headerAction_1b6k8_55,._headerAction_1b6k8_55[data-pinned-visible=true]{display:flex}._headerCellSortable_1b6k8_84{cursor:pointer}._sortIndex_1b6k8_90{color:var(--mantine-color-dimmed);text-align:center;min-width:10px;font-size:10px;font-weight:600;line-height:1}._headerCellDragging_1b6k8_102{opacity:.4}._headerCellDropBefore_1b6k8_106:before,._headerCellDropAfter_1b6k8_107:before{content:"";background-color:var(--mantine-primary-color-filled);z-index:5;pointer-events:none;width:2px;position:absolute;top:0;bottom:0}._headerCellDropBefore_1b6k8_106:before{left:0}._headerCellDropAfter_1b6k8_107:before{right:0}._columnSeparator_1b6k8_127{z-index:1;justify-content:center;width:10px;height:50%;display:flex;position:absolute;top:25%;right:0;transform:translate(50%)}._columnSeparatorResizable_1b6k8_139{cursor:col-resize;touch-action:none}._columnSeparator_1b6k8_127:after{content:"";background-color:#0000;width:1px;height:100%;transition:background-color .12s,width .12s}._headerCell_1b6k8_1:hover ._columnSeparator_1b6k8_127:after,._headerCell_1b6k8_1:focus-within ._columnSeparator_1b6k8_127:after{background-color:var(--mantine-color-default-border)}._columnSeparatorResizable_1b6k8_139:hover:after,._columnSeparatorActive_1b6k8_162:after{background-color:var(--mantine-primary-color-filled);width:2px}._menuItemActive_1b6k8_168{background-color:var(--mantine-color-default-hover);font-weight:600}._toolbar_4my8t_1{align-items:center;gap:var(--mantine-spacing-xs);padding:6px var(--mantine-spacing-xs);flex-shrink:0;min-height:42px;display:flex}._toolbarSpacer_4my8t_10{flex:1}
|
|
2
2
|
/*$vite$:1*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jielga/tmdatagrid",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "A React data grid built on TanStack Table v9 and Mantine — always virtualized, with resizable, reorderable, sortable, filterable, hideable and pinnable columns.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"type": "git",
|
|
12
12
|
"url": "git+https://github.com/Jielga/TMDataGrid.git"
|
|
13
13
|
},
|
|
14
|
-
"homepage": "https://github.
|
|
14
|
+
"homepage": "https://jielga.github.io/TMDataGrid/",
|
|
15
15
|
"bugs": {
|
|
16
16
|
"url": "https://github.com/Jielga/TMDataGrid/issues"
|
|
17
17
|
},
|
package/skills/columns/SKILL.md
CHANGED
package/skills/features/SKILL.md
CHANGED
|
@@ -5,19 +5,20 @@ description: >
|
|
|
5
5
|
table and column options, and reuse the same checks in your own components.
|
|
6
6
|
Covers the enableSorting/enableColumnFilters/enableHiding/enableColumnPinning/
|
|
7
7
|
enableColumnResizing/enableColumnOrdering/enableRowSelection matrix, the
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
selection modes behind rowSelectionMode, the highlightSelectedRows flag and
|
|
9
|
+
the --dg-row-selected-bg colour, the default-off enablePagination switch and
|
|
10
|
+
its three modes (none, client, manual), getGridCapabilities and
|
|
11
|
+
getColumnCapabilities, why the features argument is required for reactivity
|
|
12
|
+
under the React Compiler, column ordering with moveColumn and
|
|
13
|
+
moveColumnByStep, cell selection with arrow-key navigation, drag-selected
|
|
14
|
+
ranges, Ctrl+C copy and Excel-compatible CSV export, and always-on
|
|
14
15
|
virtualization. Load when building a read-only grid, hiding grid chrome,
|
|
15
|
-
enabling pagination, reordering columns from code,
|
|
16
|
-
button.
|
|
16
|
+
enabling pagination, reordering columns from code, adding cell navigation or
|
|
17
|
+
copy/export, or writing a custom toolbar button.
|
|
17
18
|
metadata:
|
|
18
19
|
type: core
|
|
19
20
|
library: '@jielga/tmdatagrid'
|
|
20
|
-
library_version: '0.
|
|
21
|
+
library_version: '0.5.0'
|
|
21
22
|
sources:
|
|
22
23
|
- 'Jielga/TMDataGrid:src/docs/features.md'
|
|
23
24
|
- 'Jielga/TMDataGrid:src/tmdatagrid/core/capabilities.ts'
|
|
@@ -242,10 +243,59 @@ const next = getStepTargetColumn({ table, columnId: "salary", direction: 1 });
|
|
|
242
243
|
Both movers are no-ops for a move that is not allowed, including one across
|
|
243
244
|
regions.
|
|
244
245
|
|
|
246
|
+
## Cell selection
|
|
247
|
+
|
|
248
|
+
Off by default. `cellSelection: "single"` gives one focused cell moved with the
|
|
249
|
+
arrow keys; `"range"` adds a selectable rectangle, Ctrl+C and CSV export.
|
|
250
|
+
|
|
251
|
+
```tsx
|
|
252
|
+
const grid = useTMDataGrid({ data, columns, cellSelection: "range" });
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
On, the body's tab stop moves from the row to a cell (the whole grid is one Tab
|
|
256
|
+
stop), the container reports `role="grid"` with `gridcell` children, and cells
|
|
257
|
+
carry `data-focused` / `data-selected` / `data-edge-*`.
|
|
258
|
+
|
|
259
|
+
| Key | Does |
|
|
260
|
+
| --- | --- |
|
|
261
|
+
| Arrows | one cell, clamped at the edges |
|
|
262
|
+
| Shift+arrows | extends the rectangle from its anchor |
|
|
263
|
+
| PageUp / PageDown | one viewport of rows |
|
|
264
|
+
| Home / End, Ctrl+Home / Ctrl+End | ends of the row, corners of the grid |
|
|
265
|
+
| Enter or F2 | steps into the cell's control; Escape steps back out |
|
|
266
|
+
| Space | selects the row |
|
|
267
|
+
| Ctrl+C | copies the block as tab-separated text |
|
|
268
|
+
|
|
269
|
+
The body is one tab stop: controls inside body cells take `tabindex="-1"` while
|
|
270
|
+
cell selection is on, so Tab leaves the grid instead of walking one control per
|
|
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 —
|
|
273
|
+
`tabIndex={useCellControlTabIndex()}`. Header controls are untouched.
|
|
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
|
|
277
|
+
those disables Copy and Export.
|
|
278
|
+
|
|
279
|
+
Drag across cells to select a rectangle; Shift+click extends one. The state is
|
|
280
|
+
`ui.state.focusedCell` and `ui.state.cellRange`, both held as `{ rowId,
|
|
281
|
+
columnId }` pairs so sorting and filtering carry the selection with the cells.
|
|
282
|
+
Move them with `ui.actions.setFocusedCell` / `setCellRange`, follow them with
|
|
283
|
+
`onFocusedCellChange`. One rectangle at a time.
|
|
284
|
+
|
|
285
|
+
Right-clicking inside the selection offers Copy, "Export as CSV for Excel" and
|
|
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.
|
|
289
|
+
Generated lanes (checkbox, tree, details) are never exported, and what is
|
|
290
|
+
written is the cell's value rather than what it renders.
|
|
291
|
+
|
|
245
292
|
## Virtualization
|
|
246
293
|
|
|
247
|
-
Always enabled
|
|
248
|
-
|
|
294
|
+
Always enabled. Only rows within the viewport, plus a small overscan, are
|
|
295
|
+
mounted, so page size does not affect how much is rendered.
|
|
296
|
+
|
|
297
|
+
`overscan` on `useTMDataGrid` (default `6`) sets how many rows are kept mounted
|
|
298
|
+
on each side of the viewport — the one configurable part.
|
|
249
299
|
|
|
250
300
|
Row height comes from `meta.rowHeight`, or from the `size` prop when unset. Rows
|
|
251
301
|
are fixed height; an accurate value keeps scrolling precise.
|
|
@@ -4,13 +4,13 @@ description: >
|
|
|
4
4
|
Set up TMDataGrid, a compound React data grid built on TanStack Table v9 and
|
|
5
5
|
Mantine. Covers useTMDataGrid, the TMDataGrid root, context, the component
|
|
6
6
|
catalog (Table, Footer, Toolbar, Spacer, SummaryCount, FilterButton,
|
|
7
|
-
ColumnsButton, FilterPanel, ColumnsPanel), the size scale and the
|
|
7
|
+
ColumnsButton, FilterPanel, FilterPills, ColumnsPanel), the size scale and the
|
|
8
8
|
bounded-height layout requirement. Load when adding a grid, choosing which
|
|
9
9
|
parts to render, or when rows do not appear.
|
|
10
10
|
metadata:
|
|
11
11
|
type: core
|
|
12
12
|
library: '@jielga/tmdatagrid'
|
|
13
|
-
library_version: '0.
|
|
13
|
+
library_version: '0.5.0'
|
|
14
14
|
sources:
|
|
15
15
|
- 'Jielga/TMDataGrid:src/docs/getting-started.md'
|
|
16
16
|
- 'Jielga/TMDataGrid:src/docs/components.md'
|
|
@@ -123,21 +123,24 @@ takes a fixed pixel width once resized or pinned.
|
|
|
123
123
|
|
|
124
124
|
## Components
|
|
125
125
|
|
|
126
|
-
All read the grid from context and must be rendered inside `TMDataGrid
|
|
127
|
-
|
|
126
|
+
All read the grid from context and must be rendered inside `TMDataGrid`, except
|
|
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
|
|
129
|
+
flex column.
|
|
128
130
|
|
|
129
131
|
| Component | Props | Notes |
|
|
130
132
|
| --- | --- | --- |
|
|
131
133
|
| `TMDataGrid` | `table`, `ui`, `features`, `size`, `className`, `style` | Root. Provides context. `style` also takes CSS variables: `--dg-row-selected-bg`, `--dg-row-height`, `--dg-header-height`, `--dg-font-size`, `--dg-padding`. |
|
|
132
|
-
| `TMDataGrid.Table` | `onRowClick(row)` | Header, virtualized body, filter panel. `onRowClick` runs in addition to selection under `rowSelectionMode: "row"`. |
|
|
134
|
+
| `TMDataGrid.Table` | `onRowClick(row)`, `rowContextMenu` render prop, `rowContextMenuProps` | Header, virtualized body, filter panel. `onRowClick` runs in addition to selection under `rowSelectionMode: "row"`. |
|
|
133
135
|
| `TMDataGrid.Footer` | `pageSizeOptions` (default `[10, 25, 50, 100]`), `pagination` render prop | Pagination controls. Renders nothing unless pagination is enabled. |
|
|
134
136
|
| `TMDataGrid.Toolbar` | `children` | Flex row above the grid. |
|
|
135
137
|
| `TMDataGrid.Spacer` | — | Pushes later toolbar items right. |
|
|
136
138
|
| `TMDataGrid.SummaryCount` | `children` | Visible rows out of total. |
|
|
137
139
|
| `TMDataGrid.FilterButton` | — | Toggles filter panel. Renders nothing if no column is filterable. |
|
|
138
140
|
| `TMDataGrid.ColumnsButton` | — | Opens column manager. Renders nothing if no column is hideable. |
|
|
139
|
-
| `TMDataGrid.FilterPanel` | — | Rendered by `.Table`; exported for custom layouts. |
|
|
141
|
+
| `TMDataGrid.FilterPanel` | — | Rendered by `.Table`; exported for custom layouts. Header close button, Escape, click-away, "Add filter" and "Clear all". |
|
|
140
142
|
| `TMDataGrid.ColumnsPanel` | — | Rendered by `.ColumnsButton`; exported for custom layouts. |
|
|
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`. |
|
|
141
144
|
|
|
142
145
|
Pass the row type so `onRowClick` stays typed:
|
|
143
146
|
|
|
@@ -145,6 +148,37 @@ Pass the row type so `onRowClick` stays typed:
|
|
|
145
148
|
<TMDataGrid.Table<Employee> onRowClick={(row) => open(row.original.id)} />
|
|
146
149
|
```
|
|
147
150
|
|
|
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 —
|
|
154
|
+
so the render prop only says what goes in it:
|
|
155
|
+
|
|
156
|
+
```tsx
|
|
157
|
+
<TMDataGrid.Table<Employee>
|
|
158
|
+
rowContextMenu={({ table, row, cell, close }) => (
|
|
159
|
+
<>
|
|
160
|
+
<Menu.Label>{row.original.firstName}</Menu.Label>
|
|
161
|
+
<Menu.Item onClick={() => open(row.original.id)}>Open</Menu.Item>
|
|
162
|
+
<Menu.Item
|
|
163
|
+
onClick={() =>
|
|
164
|
+
navigator.clipboard.writeText(String(cell?.getValue() ?? ""))
|
|
165
|
+
}
|
|
166
|
+
>
|
|
167
|
+
Copy cell value
|
|
168
|
+
</Menu.Item>
|
|
169
|
+
</>
|
|
170
|
+
)}
|
|
171
|
+
/>
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
`cell` is the one that was right-clicked, `table` is there for actions that read
|
|
175
|
+
the selection, and `close` is for dropdown content that is not a `Menu.Item`
|
|
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
|
|
178
|
+
work in the item handlers. Right-clicking does not change the selection or the
|
|
179
|
+
highlight; the row carries `data-context-menu` while its menu is open. Pass
|
|
180
|
+
`rowContextMenuProps` for `width`, `shadow`, `position` and the rest.
|
|
181
|
+
|
|
148
182
|
`TMDataGrid.Footer` reads totals from `table.getRowCount()`, which prefers
|
|
149
183
|
`options.rowCount`. `TMDataGrid.SummaryCount` uses `meta.totalRowCount` when
|
|
150
184
|
provided, otherwise the pre-filtered row count.
|
|
@@ -194,6 +228,7 @@ mirrors them. Set `meta.rowHeight` for a height outside the scale.
|
|
|
194
228
|
| `openColumnFilter(api, columnId)` | Adds an empty filter for a column and opens the panel. |
|
|
195
229
|
| `getColumnLabel(column)` | `meta.label`, a string header, or the column id. |
|
|
196
230
|
| `getColumnType(column)` | `meta.type`, defaulting to `"string"`. |
|
|
231
|
+
| `formatFilterLabel({ label, type, filter })` | The one-line filter description used on the pills. |
|
|
197
232
|
| `moveColumn({ table, columnId, targetId, side })` | Moves a column next to another. See the `features` skill. |
|
|
198
233
|
| `moveColumnByStep({ table, columnId, direction })` | Moves a column one position within its region. |
|
|
199
234
|
| `SELECT_COLUMN_ID` | Id of the generated checkbox column. |
|
package/skills/options/SKILL.md
CHANGED
|
@@ -13,7 +13,7 @@ description: >
|
|
|
13
13
|
metadata:
|
|
14
14
|
type: core
|
|
15
15
|
library: '@jielga/tmdatagrid'
|
|
16
|
-
library_version: '0.
|
|
16
|
+
library_version: '0.5.0'
|
|
17
17
|
sources:
|
|
18
18
|
- 'Jielga/TMDataGrid:src/docs/use-tm-data-grid.md'
|
|
19
19
|
- 'Jielga/TMDataGrid:src/tmdatagrid/useTMDataGrid.tsx'
|
|
@@ -57,6 +57,9 @@ rather than forwarded to TanStack.
|
|
|
57
57
|
| `enableColumnResizing` | `boolean` | `true` | Enables resizing for the table. |
|
|
58
58
|
| `enableColumnOrdering` | `boolean` | `true` | Enables header dragging and the move menu items. Defined by the grid. |
|
|
59
59
|
| `enablePagination` | `boolean` | `false` | Enables client-side paging and the `Footer` pager. Implied by `manualPagination`. Defined by the grid. |
|
|
60
|
+
| `cellSelection` | `"none" \| "single" \| "range"` | `"none"` | Cell cursor; `"range"` adds a drag-selectable rectangle, Ctrl+C and CSV export. Defined by the grid. |
|
|
61
|
+
| `onFocusedCellChange` | `(cell \| null) => void` | – | Fires whenever the focused cell moves. |
|
|
62
|
+
| `overscan` | `number` | `6` | Rows the virtualizer keeps mounted above and below the viewport. Defined by the grid. |
|
|
60
63
|
| `columnResizeMode` | `"onChange" \| "onEnd"` | `"onChange"` | Resize update strategy. |
|
|
61
64
|
| `initialState` | `Partial<TableState>` | See below | Merged over the grid defaults. |
|
|
62
65
|
| `meta` | `TMDataGridTableMeta` | `{}` | Grid configuration, see below. |
|
|
@@ -168,8 +171,15 @@ const filterPanelOpen = useSelector(grid.ui, (state) => state.filterPanelOpen);
|
|
|
168
171
|
| `toggleColumnsPanel` | `() => void` |
|
|
169
172
|
| `startColumnDrag` | `(columnId: string) => void` |
|
|
170
173
|
| `endColumnDrag` | `() => void` |
|
|
174
|
+
| `setHighlightedRow` | `(rowId: string \| null) => void` |
|
|
175
|
+
| `setSelectionAnchor` | `(rowId: string \| null) => void` |
|
|
176
|
+
| `setFocusedCell` | `(cell: TMDataGridCellPosition \| null) => void` |
|
|
177
|
+
| `setCellRange` | `(range: TMDataGridCellRange \| null) => void` |
|
|
171
178
|
|
|
172
|
-
The last two
|
|
179
|
+
The last two move the cell cursor and the selected rectangle under
|
|
180
|
+
`cellSelection`; DOM focus follows `focusedCell` and scrolls its row into view.
|
|
181
|
+
|
|
182
|
+
`startColumnDrag` / `endColumnDrag` are called by the header cells while a column is being dragged, and
|
|
173
183
|
`ui.draggedColumnId` holds the column being moved — browsers keep `dataTransfer`
|
|
174
184
|
unreadable until the drop.
|
|
175
185
|
|
|
@@ -30,3 +30,29 @@ export function useTMDataGridContext(): TMDataGridContextValue {
|
|
|
30
30
|
}
|
|
31
31
|
return api as TMDataGridContextValue;
|
|
32
32
|
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* What a control inside a *body* cell should put in its `tabIndex`.
|
|
36
|
+
*
|
|
37
|
+
* `-1` once cell selection is on, and this is what makes the promise of one tab
|
|
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
|
|
40
|
+
* scrolling would change how many. Enter or F2 steps into the cell instead,
|
|
41
|
+
* which reaches a `-1` control perfectly well.
|
|
42
|
+
*
|
|
43
|
+
* Header controls are not covered: the header row is not part of cell
|
|
44
|
+
* navigation, so its sort buttons and menus stay in the tab order, where they
|
|
45
|
+
* are the only way to reach them.
|
|
46
|
+
*
|
|
47
|
+
* A custom cell renderer with a control in it wants the same:
|
|
48
|
+
*
|
|
49
|
+
* ```tsx
|
|
50
|
+
* cell: ({ row }) => (
|
|
51
|
+
* <Button tabIndex={useCellControlTabIndex()} onClick={...}>Open</Button>
|
|
52
|
+
* )
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export function useCellControlTabIndex(): 0 | -1 {
|
|
56
|
+
const { features } = useTMDataGridContext();
|
|
57
|
+
return features.cellSelection ? -1 : 0;
|
|
58
|
+
}
|
|
@@ -46,9 +46,29 @@
|
|
|
46
46
|
--dg-header-height: 44px;
|
|
47
47
|
--dg-font-size: var(--mantine-font-size-sm);
|
|
48
48
|
--dg-padding: 10px;
|
|
49
|
-
/* Background of a
|
|
49
|
+
/* Background of a selected row, when `showSelectedBackground` is on. Override
|
|
50
|
+
it on the grid:
|
|
50
51
|
<TMDataGrid style={{ "--dg-row-selected-bg": "var(--mantine-color-blue-0)" }} /> */
|
|
51
52
|
--dg-row-selected-bg: var(--mantine-primary-color-light);
|
|
53
|
+
/* Background of the highlighted row, under a selectionMode that has one.
|
|
54
|
+
Stronger than the selected background by default, since under
|
|
55
|
+
"checkboxAndHighlight" the two show up together. */
|
|
56
|
+
--dg-row-highlight-bg: var(--mantine-primary-color-light-hover);
|
|
57
|
+
/* Background of a group row. Transparent by default — the bold label and the
|
|
58
|
+
chevron are enough, and with the tree collapsed every row on screen is a
|
|
59
|
+
group row, so a tint would grey out the whole grid. Set it to give groups a
|
|
60
|
+
band of their own. */
|
|
61
|
+
--dg-row-group-bg: var(--mantine-color-body);
|
|
62
|
+
|
|
63
|
+
/* The stacking ladder, stated once. Body cells sit at auto (0); everything
|
|
64
|
+
sticky stacks over them in this order, and a sticky row crossing a sticky
|
|
65
|
+
column takes the row's slot plus one. */
|
|
66
|
+
--dg-z-pinned-cell: 2;
|
|
67
|
+
--dg-z-focused-cell: 3;
|
|
68
|
+
--dg-z-summary-row: 4;
|
|
69
|
+
--dg-z-summary-pinned-cell: 5;
|
|
70
|
+
--dg-z-header: 6;
|
|
71
|
+
--dg-z-header-pinned-cell: 7;
|
|
52
72
|
|
|
53
73
|
display: flex;
|
|
54
74
|
flex-direction: column;
|
|
@@ -4,11 +4,14 @@ import classes from "./TMDataGrid.module.css";
|
|
|
4
4
|
import { TMDataGridColumnsPanel } from "./TMDataGridColumnsPanel";
|
|
5
5
|
import { TMDataGridContextProvider } from "../TMDataGridContext";
|
|
6
6
|
import { TMDataGridFilterPanel } from "./TMDataGridFilterPanel";
|
|
7
|
+
import { TMDataGridFilterPills } from "./TMDataGridFilterPills";
|
|
7
8
|
import { TMDataGridFooter } from "./TMDataGridFooter";
|
|
9
|
+
import { TMDataGridSearch } from "./TMDataGridSearch";
|
|
8
10
|
import { TMDataGridTable } from "./TMDataGridTable";
|
|
9
11
|
import {
|
|
10
12
|
TMDataGridColumnsButton,
|
|
11
13
|
TMDataGridFilterButton,
|
|
14
|
+
TMDataGridLoadingIndicator,
|
|
12
15
|
TMDataGridSummaryCount,
|
|
13
16
|
TMDataGridToolbar,
|
|
14
17
|
TMDataGridToolbarSpacer,
|
|
@@ -59,6 +62,10 @@ function TMDataGridRoot<TData extends RowData>({
|
|
|
59
62
|
table,
|
|
60
63
|
ui,
|
|
61
64
|
features,
|
|
65
|
+
labels,
|
|
66
|
+
renderDetails,
|
|
67
|
+
renderDetailsEstHeight,
|
|
68
|
+
overscan,
|
|
62
69
|
size = DEFAULT_TMDATAGRID_SIZE,
|
|
63
70
|
children,
|
|
64
71
|
className,
|
|
@@ -71,11 +78,25 @@ function TMDataGridRoot<TData extends RowData>({
|
|
|
71
78
|
table,
|
|
72
79
|
ui,
|
|
73
80
|
features,
|
|
81
|
+
labels,
|
|
82
|
+
renderDetails,
|
|
83
|
+
renderDetailsEstHeight,
|
|
84
|
+
overscan,
|
|
74
85
|
size,
|
|
75
86
|
rowHeight,
|
|
76
87
|
controlSize: SIZE_CONTROL_SIZE[size],
|
|
77
88
|
}),
|
|
78
|
-
[
|
|
89
|
+
[
|
|
90
|
+
table,
|
|
91
|
+
ui,
|
|
92
|
+
features,
|
|
93
|
+
labels,
|
|
94
|
+
renderDetails,
|
|
95
|
+
renderDetailsEstHeight,
|
|
96
|
+
overscan,
|
|
97
|
+
size,
|
|
98
|
+
rowHeight,
|
|
99
|
+
],
|
|
79
100
|
);
|
|
80
101
|
|
|
81
102
|
return (
|
|
@@ -95,12 +116,19 @@ export const TMDataGrid = Object.assign(TMDataGridRoot, {
|
|
|
95
116
|
Toolbar: TMDataGridToolbar,
|
|
96
117
|
Spacer: TMDataGridToolbarSpacer,
|
|
97
118
|
SummaryCount: TMDataGridSummaryCount,
|
|
119
|
+
LoadingIndicator: TMDataGridLoadingIndicator,
|
|
120
|
+
Search: TMDataGridSearch,
|
|
98
121
|
ColumnsButton: TMDataGridColumnsButton,
|
|
99
122
|
FilterButton: TMDataGridFilterButton,
|
|
100
123
|
Table: TMDataGridTable,
|
|
101
124
|
Footer: TMDataGridFooter,
|
|
102
125
|
/** Rendered by `TMDataGrid.Table`; exported for custom layouts. */
|
|
103
126
|
FilterPanel: TMDataGridFilterPanel,
|
|
127
|
+
/**
|
|
128
|
+
* Takes the grid as an `api` prop rather than from context, so it can be
|
|
129
|
+
* rendered outside `<TMDataGrid>` — a page header, for instance.
|
|
130
|
+
*/
|
|
131
|
+
FilterPills: TMDataGridFilterPills,
|
|
104
132
|
/** Rendered by `TMDataGrid.ColumnsButton`; exported for custom layouts. */
|
|
105
133
|
ColumnsPanel: TMDataGridColumnsPanel,
|
|
106
134
|
});
|
|
@@ -4,6 +4,7 @@ import { useState } from "react";
|
|
|
4
4
|
import classes from "./TMDataGridColumnsPanel.module.css";
|
|
5
5
|
import { useTMDataGridContext } from "../TMDataGridContext";
|
|
6
6
|
import { getColumnLabel } from "../core/columnUtils";
|
|
7
|
+
import { GROUP_COLUMN_ID } from "./TMDataGridGroupColumn";
|
|
7
8
|
import { SearchIcon } from "./icons";
|
|
8
9
|
|
|
9
10
|
/**
|
|
@@ -12,7 +13,7 @@ import { SearchIcon } from "./icons";
|
|
|
12
13
|
* `TMDataGrid.ColumnsButton`.
|
|
13
14
|
*/
|
|
14
15
|
export function TMDataGridColumnsPanel() {
|
|
15
|
-
const { table, controlSize } = useTMDataGridContext();
|
|
16
|
+
const { table, labels, controlSize } = useTMDataGridContext();
|
|
16
17
|
const [search, setSearch] = useState("");
|
|
17
18
|
|
|
18
19
|
const columnVisibility = useSelector(
|
|
@@ -20,7 +21,12 @@ export function TMDataGridColumnsPanel() {
|
|
|
20
21
|
(state) => state.columnVisibility,
|
|
21
22
|
);
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
// The tree column is left out rather than listed and disabled: its visibility
|
|
25
|
+
// is not a setting at all, it follows the grouping state, so an unchecked box
|
|
26
|
+
// that cannot be ticked would only invite the question.
|
|
27
|
+
const columns = table
|
|
28
|
+
.getAllLeafColumns()
|
|
29
|
+
.filter((column) => column.id !== GROUP_COLUMN_ID);
|
|
24
30
|
const needle = search.trim().toLowerCase();
|
|
25
31
|
const visibleInPanel = needle
|
|
26
32
|
? columns.filter((column) =>
|
|
@@ -39,7 +45,7 @@ export function TMDataGridColumnsPanel() {
|
|
|
39
45
|
<TextInput
|
|
40
46
|
value={search}
|
|
41
47
|
onChange={(event) => setSearch(event.currentTarget.value)}
|
|
42
|
-
placeholder=
|
|
48
|
+
placeholder={labels.columnsSearchPlaceholder}
|
|
43
49
|
leftSection={<SearchIcon size={16} stroke={1.6} />}
|
|
44
50
|
size={controlSize}
|
|
45
51
|
data-autofocus
|
|
@@ -62,7 +68,7 @@ export function TMDataGridColumnsPanel() {
|
|
|
62
68
|
))}
|
|
63
69
|
{visibleInPanel.length === 0 && (
|
|
64
70
|
<Text size={controlSize} c="dimmed">
|
|
65
|
-
|
|
71
|
+
{labels.columnsNoMatch(search)}
|
|
66
72
|
</Text>
|
|
67
73
|
)}
|
|
68
74
|
</div>
|
|
@@ -71,7 +77,7 @@ export function TMDataGridColumnsPanel() {
|
|
|
71
77
|
<div className={classes.columnsPanelFooter}>
|
|
72
78
|
<Checkbox
|
|
73
79
|
size={controlSize}
|
|
74
|
-
label=
|
|
80
|
+
label={labels.columnsShowHideAll}
|
|
75
81
|
checked={shownCount === hideableColumns.length}
|
|
76
82
|
indeterminate={shownCount > 0 && shownCount < hideableColumns.length}
|
|
77
83
|
onChange={(event) =>
|
|
@@ -83,7 +89,7 @@ export function TMDataGridColumnsPanel() {
|
|
|
83
89
|
size="compact-sm"
|
|
84
90
|
onClick={() => table.resetColumnVisibility()}
|
|
85
91
|
>
|
|
86
|
-
|
|
92
|
+
{labels.columnsReset}
|
|
87
93
|
</Button>
|
|
88
94
|
</div>
|
|
89
95
|
</div>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/* The details lane is a fixed track holding one control, so the button fills it
|
|
2
|
+
and the whole cell is the hit target. */
|
|
3
|
+
.detailsToggle {
|
|
4
|
+
display: flex;
|
|
5
|
+
align-items: center;
|
|
6
|
+
justify-content: center;
|
|
7
|
+
width: 100%;
|
|
8
|
+
height: 100%;
|
|
9
|
+
color: inherit;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.detailsToggle:hover .chevron {
|
|
13
|
+
color: var(--mantine-color-text);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.chevron {
|
|
17
|
+
display: flex;
|
|
18
|
+
align-items: center;
|
|
19
|
+
color: var(--mantine-color-dimmed);
|
|
20
|
+
transition: transform 120ms ease;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.chevron[data-expanded="true"] {
|
|
24
|
+
transform: rotate(90deg);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/* One icon rotated rather than two swapped: the turn is what reads as the
|
|
28
|
+
expand, and a swap would jump. */
|
|
29
|
+
@media (prefers-reduced-motion: reduce) {
|
|
30
|
+
.chevron {
|
|
31
|
+
transition: none;
|
|
32
|
+
}
|
|
33
|
+
}
|