@alaarab/ogrid-react-material 2.1.15 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/esm/index.js +320 -204
  2. package/package.json +2 -2
package/dist/esm/index.js CHANGED
@@ -1,7 +1,7 @@
1
- import * as React3 from 'react';
1
+ import * as React4 from 'react';
2
2
  import { useMemo, useCallback, useState, useRef, useEffect } from 'react';
3
3
  import { Box, Tooltip, Typography, IconButton, Popover, Button, Select, MenuItem, useTheme, Checkbox, Table, TableHead, TableRow, TableCell, FormControlLabel, Avatar, TextField, InputAdornment, CircularProgress, Menu, Divider } from '@mui/material';
4
- import { useColumnHeaderFilterState, getColumnHeaderFilterStateParams, renderFilterContent, areGridRowPropsEqual, usePaginationControls, createOGrid, DateFilterContent, CHECKBOX_COLUMN_WIDTH, STOP_PROPAGATION, ROW_NUMBER_COLUMN_WIDTH, useDataGridTableOrchestration, useColumnMeta, getCellRenderDescriptor, buildInlineEditorProps, buildPopoverEditorProps, resolveCellDisplayContent, resolveCellStyle, getCellInteractionProps, CellErrorBoundary, PREVENT_DEFAULT, getHeaderFilterConfig, MarchingAntsOverlay, NOOP, useColumnChooserState, useListVirtualizer, BaseInlineCellEditor, getContextMenuHandlers, GRID_CONTEXT_MENU_ITEMS, formatShortcut, getColumnHeaderMenuItems, getStatusBarParts } from '@alaarab/ogrid-react';
4
+ import { useColumnHeaderFilterState, getColumnHeaderFilterStateParams, renderFilterContent, areGridRowPropsEqual, usePaginationControls, createOGrid, DateFilterContent, CHECKBOX_COLUMN_WIDTH, STOP_PROPAGATION, ROW_NUMBER_COLUMN_WIDTH, useDataGridTableOrchestration, useColumnMeta, getCellRenderDescriptor, buildInlineEditorProps, buildPopoverEditorProps, resolveCellDisplayContent, resolveCellStyle, getCellInteractionProps, CellErrorBoundary, PREVENT_DEFAULT, indexToColumnLetter, getHeaderFilterConfig, MarchingAntsOverlay, NOOP, useColumnChooserState, useListVirtualizer, BaseInlineCellEditor, partitionColumnsForVirtualization, getContextMenuHandlers, GRID_CONTEXT_MENU_ITEMS, formatShortcut, getColumnHeaderMenuItems, getStatusBarParts } from '@alaarab/ogrid-react';
5
5
  export { BaseColumnHeaderMenu, BaseDropIndicator, BaseEmptyState, BaseInlineCellEditor, BaseLoadingOverlay, CELL_PADDING, CHECKBOX_COLUMN_WIDTH, COLUMN_HEADER_MENU_ITEMS, CURSOR_CELL_STYLE, CellErrorBoundary, DEFAULT_MIN_COLUMN_WIDTH, DateFilterContent, EmptyState, GRID_BORDER_RADIUS, GRID_CONTEXT_MENU_ITEMS, GRID_ROOT_STYLE, GridContextMenu, MAX_PAGE_BUTTONS, MarchingAntsOverlay, NOOP, OGridLayout, PAGE_SIZE_OPTIONS, POPOVER_ANCHOR_STYLE, PREVENT_DEFAULT, ROW_NUMBER_COLUMN_WIDTH, STOP_PROPAGATION, SideBar, StatusBar, UndoRedoStack, areGridRowPropsEqual, booleanParser, buildCsvHeader, buildCsvRows, buildHeaderRows, buildInlineEditorProps, buildPopoverEditorProps, clampSelectionToBounds, computeAggregations, computeAutoScrollSpeed, computeTabNavigation, createOGrid, currencyParser, dateParser, deriveFilterOptionsFromData, editorInputStyle, editorWrapperStyle, emailParser, escapeCsvValue, exportToCsv, findCtrlArrowTarget, flattenColumns, formatCellValueForTsv, formatSelectionAsTsv, formatShortcut, getCellInteractionProps, getCellRenderDescriptor, getCellValue, getColumnHeaderFilterStateParams, getColumnHeaderMenuItems, getContextMenuHandlers, getDataGridStatusBarConfig, getDateFilterContentProps, getFilterField, getHeaderFilterConfig, getMultiSelectFilterFields, getPaginationViewModel, getStatusBarParts, isInSelectionRange, isRowInRange, mergeFilter, normalizeSelectionRange, numberParser, parseTsvClipboard, parseValue, processClientSideData, rangesEqual, renderFilterContent, resolveCellDisplayContent, resolveCellStyle, richSelectDropdownStyle, richSelectNoMatchesStyle, richSelectOptionHighlightedStyle, richSelectOptionStyle, richSelectWrapperStyle, selectChevronStyle, selectDisplayStyle, selectEditorStyle, toUserLike, triggerCsvDownload, useActiveCell, useCellEditing, useCellSelection, useClipboard, useColumnChooserState, useColumnHeaderFilterState, useColumnMeta, useColumnReorder, useColumnResize, useContextMenu, useDataGridState, useDataGridTableOrchestration, useDateFilterState, useDebounce, useFillHandle, useFilterOptions, useInlineCellEditorState, useKeyboardNavigation, useLatestRef, useListVirtualizer, useMultiSelectFilterState, useOGrid, usePaginationControls, usePeopleFilterState, useRichSelectState, useRowSelection, useSelectState, useSideBarState, useTableLayout, useTextFilterState, useUndoRedo, useVirtualScroll } from '@alaarab/ogrid-react';
6
6
  import { createPortal } from 'react-dom';
7
7
  import { FilterList, FirstPage, ChevronLeft, ChevronRight, LastPage, ExpandLess, ExpandMore, ViewColumn, Clear, Search } from '@mui/icons-material';
@@ -18,8 +18,10 @@ var STYLES = `
18
18
  .ogrid-mat-td--pinned-left { position: sticky; left: 0; z-index: var(--ogrid-z-pinned, 6); background-color: var(--ogrid-paper-bg, #fff); will-change: transform; border-right: 1px solid var(--ogrid-border, rgba(224,224,224,1)); box-shadow: 2px 0 4px -1px rgba(0,0,0,0.1); }
19
19
  .ogrid-mat-td--pinned-right { position: sticky; right: 0; z-index: var(--ogrid-z-pinned, 6); background-color: var(--ogrid-paper-bg, #fff); will-change: transform; border-left: 1px solid var(--ogrid-border, rgba(224,224,224,1)); box-shadow: -2px 0 4px -1px rgba(0,0,0,0.1); }
20
20
 
21
- .ogrid-mat-cell { width: 100%; height: 100%; display: flex; align-items: center; min-width: 0; box-sizing: border-box; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; user-select: none; outline: none; }
21
+ .ogrid-mat-cell { width: 100%; height: 100%; display: flex; align-items: center; min-width: 0; box-sizing: border-box; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; user-select: none; outline: none; contain: content; }
22
22
  .ogrid-mat-cell:focus-visible { outline: 2px solid var(--ogrid-primary, #1976d2); outline-offset: -2px; z-index: 3; }
23
+ .ogrid-mat-td--pinned-left .ogrid-mat-cell, .ogrid-mat-td--pinned-right .ogrid-mat-cell { contain: none; }
24
+ table:not([data-virtual-scroll]) .ogrid-mat-tbody tr { content-visibility: auto; }
23
25
 
24
26
  .ogrid-mat-cell--numeric { justify-content: flex-end; text-align: right; }
25
27
  .ogrid-mat-cell--boolean { justify-content: center; text-align: center; }
@@ -27,6 +29,7 @@ var STYLES = `
27
29
 
28
30
  .ogrid-mat-cell--active { outline: 2px solid var(--ogrid-selection, #217346); outline-offset: -1px; z-index: var(--ogrid-z-active-cell, 2); position: relative; overflow: visible; background-color: var(--ogrid-hover-bg); }
29
31
  .ogrid-mat-cell--active:focus-visible { outline: 2px solid var(--ogrid-selection, #217346); outline-offset: -1px; }
32
+ .ogrid-mat-cell--active-in-range { outline: none; background-color: var(--ogrid-bg, #fff); }
30
33
  .ogrid-mat-cell--range { background-color: var(--ogrid-bg-range, rgba(33,115,70,0.12)); }
31
34
  .ogrid-mat-cell--range:focus-visible { outline: none; }
32
35
  .ogrid-mat-cell--cut { background-color: var(--ogrid-hover-bg); opacity: 0.7; }
@@ -275,7 +278,7 @@ var materialRenderers = {
275
278
  }
276
279
  )
277
280
  };
278
- var ColumnHeaderFilter = React3.memo((props) => {
281
+ var ColumnHeaderFilter = React4.memo((props) => {
279
282
  const {
280
283
  columnName,
281
284
  filterType,
@@ -487,8 +490,8 @@ function StatusBar(props) {
487
490
  }
488
491
  function GridContextMenu(props) {
489
492
  const { x, y, hasSelection, canUndo, canRedo, onClose } = props;
490
- const handlers = React3.useMemo(() => getContextMenuHandlers(props), [props]);
491
- const isDisabled = React3.useCallback(
493
+ const handlers = React4.useMemo(() => getContextMenuHandlers(props), [props]);
494
+ const isDisabled = React4.useCallback(
492
495
  (item) => {
493
496
  if (item.disabledWhenNoSelection && !hasSelection) return true;
494
497
  if (item.id === "undo" && !canUndo) return true;
@@ -622,6 +625,19 @@ var STICKY_HEADER_SX = {
622
625
  "& th": { bgcolor: HEADER_BG }
623
626
  };
624
627
  var HEADER_ROW_SX = { bgcolor: HEADER_BG };
628
+ var COLUMN_LETTER_CELL_SX = {
629
+ textAlign: "center",
630
+ fontSize: "11px",
631
+ fontWeight: 500,
632
+ color: "text.secondary",
633
+ py: "2px",
634
+ px: "4px",
635
+ bgcolor: HEADER_BG,
636
+ borderBottom: 1,
637
+ borderColor: "divider",
638
+ userSelect: "none",
639
+ fontVariantNumeric: "tabular-nums"
640
+ };
625
641
  var GROUP_HEADER_CELL_SX = { textAlign: "center", fontWeight: 600, borderBottom: 2, borderColor: "divider", py: 0.75 };
626
642
  function getDensityPadding(density) {
627
643
  switch (density) {
@@ -738,6 +754,7 @@ var COLUMN_OPTIONS_BUTTON_SX = {
738
754
  };
739
755
  var TABLE_WRAPPER_SX = { position: "relative", opacity: 1 };
740
756
  var TABLE_WRAPPER_LOADING_SX = { position: "relative", opacity: 0.6 };
757
+ var SPACER_TD_STYLE = { padding: 0, border: "none" };
741
758
  function GridRowInner(props) {
742
759
  const {
743
760
  item,
@@ -752,7 +769,10 @@ function GridRowInner(props) {
752
769
  hasCheckboxCol,
753
770
  hasRowNumbersCol,
754
771
  rowNumberOffset,
755
- rowHeight
772
+ rowHeight,
773
+ leftSpacerWidth,
774
+ rightSpacerWidth,
775
+ globalColIndexMap
756
776
  } = props;
757
777
  return /* @__PURE__ */ jsxs(
758
778
  "tr",
@@ -795,22 +815,119 @@ function GridRowInner(props) {
795
815
  children: rowNumberOffset + rowIndex + 1
796
816
  }
797
817
  ),
798
- columnLayouts.map((cl, colIdx) => /* @__PURE__ */ jsx(
799
- "td",
800
- {
801
- "data-column-id": cl.col.columnId,
802
- className: cl.tdClassName,
803
- style: { ...cl.tdStyle, minWidth: cl.minWidth, width: cl.width, maxWidth: cl.maxWidth },
804
- children: renderCellContent(item, cl.col, rowIndex, colIdx)
805
- },
806
- cl.col.columnId
807
- ))
818
+ leftSpacerWidth != null && leftSpacerWidth > 0 && /* @__PURE__ */ jsx("td", { style: { ...SPACER_TD_STYLE, width: leftSpacerWidth, minWidth: leftSpacerWidth }, "aria-hidden": true }),
819
+ columnLayouts.map((cl, colIdx) => {
820
+ const globalIdx = globalColIndexMap ? globalColIndexMap[colIdx] : colIdx;
821
+ return /* @__PURE__ */ jsx(
822
+ "td",
823
+ {
824
+ "data-column-id": cl.col.columnId,
825
+ className: cl.tdClassName,
826
+ style: { ...cl.tdStyle, minWidth: cl.minWidth, width: cl.width, maxWidth: cl.maxWidth },
827
+ children: renderCellContent(item, cl.col, rowIndex, globalIdx)
828
+ },
829
+ cl.col.columnId
830
+ );
831
+ }),
832
+ rightSpacerWidth != null && rightSpacerWidth > 0 && /* @__PURE__ */ jsx("td", { style: { ...SPACER_TD_STYLE, width: rightSpacerWidth, minWidth: rightSpacerWidth }, "aria-hidden": true })
808
833
  ]
809
834
  }
810
835
  );
811
836
  }
812
- var GridRow = React3.memo(GridRowInner, areGridRowPropsEqual);
837
+ var GridRow = React4.memo(GridRowInner, areGridRowPropsEqual);
813
838
  injectDataGridStyles();
839
+ function MaterialTableBody(props) {
840
+ const {
841
+ virtualScrollEnabled,
842
+ visibleRange,
843
+ columnRange,
844
+ items,
845
+ getRowId,
846
+ selectedRowIds,
847
+ visibleCols,
848
+ columnLayouts,
849
+ renderCellContent,
850
+ handleSingleRowClick,
851
+ handleRowCheckboxChange,
852
+ lastMouseShiftRef,
853
+ hasCheckboxCol,
854
+ hasRowNumbersCol,
855
+ rowNumberOffset,
856
+ rowHeight,
857
+ selectionRange,
858
+ activeCell,
859
+ cutRange,
860
+ copyRange,
861
+ isDragging,
862
+ editingCell,
863
+ pinnedColumns
864
+ } = props;
865
+ const { rowLayouts, globalColIndexMap, leftSpacerWidth, rightSpacerWidth } = React4.useMemo(() => {
866
+ if (!columnRange) {
867
+ return { rowLayouts: columnLayouts, globalColIndexMap: void 0, leftSpacerWidth: void 0, rightSpacerWidth: void 0 };
868
+ }
869
+ const partition = partitionColumnsForVirtualization(
870
+ visibleCols,
871
+ columnRange,
872
+ pinnedColumns
873
+ );
874
+ const combined = [...partition.pinnedLeft, ...partition.virtualizedUnpinned, ...partition.pinnedRight];
875
+ const layoutMap = new Map(columnLayouts.map((cl) => [cl.col.columnId, cl]));
876
+ const partitionedLayouts = [];
877
+ const idxMap = [];
878
+ for (const col of combined) {
879
+ const layout = layoutMap.get(col.columnId);
880
+ if (layout) {
881
+ partitionedLayouts.push(layout);
882
+ idxMap.push(visibleCols.indexOf(col));
883
+ }
884
+ }
885
+ return {
886
+ rowLayouts: partitionedLayouts,
887
+ globalColIndexMap: idxMap,
888
+ leftSpacerWidth: partition.leftSpacerWidth,
889
+ rightSpacerWidth: partition.rightSpacerWidth
890
+ };
891
+ }, [columnRange, visibleCols, columnLayouts, pinnedColumns]);
892
+ const renderRow = (item, rowIndex) => {
893
+ const rowIdStr = getRowId(item);
894
+ return /* @__PURE__ */ jsx(
895
+ GridRow,
896
+ {
897
+ item,
898
+ rowIndex,
899
+ rowId: rowIdStr,
900
+ isSelected: selectedRowIds.has(rowIdStr),
901
+ columnLayouts: rowLayouts,
902
+ renderCellContent,
903
+ handleSingleRowClick,
904
+ handleRowCheckboxChange,
905
+ lastMouseShiftRef,
906
+ hasCheckboxCol,
907
+ hasRowNumbersCol,
908
+ rowNumberOffset,
909
+ selectionRange,
910
+ activeCell,
911
+ cutRange,
912
+ copyRange,
913
+ isDragging,
914
+ rowHeight,
915
+ editingRowId: editingCell?.rowId ?? null,
916
+ leftSpacerWidth,
917
+ rightSpacerWidth,
918
+ globalColIndexMap
919
+ },
920
+ rowIdStr
921
+ );
922
+ };
923
+ return /* @__PURE__ */ jsxs("tbody", { className: "ogrid-mat-tbody", children: [
924
+ virtualScrollEnabled && visibleRange.offsetTop > 0 && /* @__PURE__ */ jsx("tr", { style: { height: visibleRange.offsetTop }, "aria-hidden": true }),
925
+ virtualScrollEnabled ? items.slice(visibleRange.startIndex, visibleRange.endIndex + 1).map(
926
+ (item, i) => renderRow(item, visibleRange.startIndex + i)
927
+ ) : items.map((item, rowIndex) => renderRow(item, rowIndex)),
928
+ virtualScrollEnabled && visibleRange.offsetBottom > 0 && /* @__PURE__ */ jsx("tr", { style: { height: visibleRange.offsetBottom }, "aria-hidden": true })
929
+ ] });
930
+ }
814
931
  function DataGridTableInner(props) {
815
932
  const o = useDataGridTableOrchestration({ props });
816
933
  const {
@@ -827,6 +944,8 @@ function DataGridTableInner(props) {
827
944
  handleHeaderMouseDown,
828
945
  virtualScrollEnabled,
829
946
  visibleRange,
947
+ columnRange,
948
+ onHorizontalScroll,
830
949
  items,
831
950
  getRowId,
832
951
  emptyState,
@@ -842,6 +961,7 @@ function DataGridTableInner(props) {
842
961
  headerRows,
843
962
  allowOverflowX,
844
963
  fitToContent,
964
+ showColumnLetters,
845
965
  editCallbacks,
846
966
  interactionHandlers,
847
967
  cellDescriptorInputRef,
@@ -965,13 +1085,14 @@ function DataGridTableInner(props) {
965
1085
  ] });
966
1086
  } else {
967
1087
  const content = resolveCellDisplayContent(col, item, descriptor.displayValue);
968
- const cellStyle = resolveCellStyle(col, item);
1088
+ const cellStyle = resolveCellStyle(col, item, descriptor.displayValue);
969
1089
  const styledContent = cellStyle ? /* @__PURE__ */ jsx("span", { style: cellStyle, children: content }) : content;
970
1090
  let cls = "ogrid-mat-cell";
971
1091
  if (col.type === "numeric") cls += " ogrid-mat-cell--numeric";
972
1092
  else if (col.type === "boolean") cls += " ogrid-mat-cell--boolean";
973
1093
  if (descriptor.canEditAny) cls += " ogrid-mat-cell--editable";
974
1094
  if (descriptor.isActive) cls += " ogrid-mat-cell--active";
1095
+ if (descriptor.isActive && descriptor.isInRange) cls += " ogrid-mat-cell--active-in-range";
975
1096
  if (descriptor.isInRange && !descriptor.isActive) cls += " ogrid-mat-cell--range";
976
1097
  if (descriptor.isInCutRange) cls += " ogrid-mat-cell--cut";
977
1098
  const interactionProps = getCellInteractionProps(descriptor, col.columnId, interactionHandlers);
@@ -1004,6 +1125,7 @@ function DataGridTableInner(props) {
1004
1125
  onMouseDown: (e) => {
1005
1126
  lastMouseShiftRef.current = e.shiftKey;
1006
1127
  },
1128
+ onScroll: onHorizontalScroll ? (e) => onHorizontalScroll(e.target.scrollLeft) : void 0,
1007
1129
  onKeyDown: handleGridKeyDown,
1008
1130
  onContextMenu: PREVENT_DEFAULT,
1009
1131
  "data-density": density,
@@ -1018,204 +1140,198 @@ function DataGridTableInner(props) {
1018
1140
  size: "small",
1019
1141
  role: "grid",
1020
1142
  sx: { minWidth: minTableWidth, borderCollapse: "separate", borderSpacing: 0 },
1143
+ "data-virtual-scroll": virtualScrollEnabled ? "" : void 0,
1021
1144
  children: [
1022
- /* @__PURE__ */ jsx(TableHead, { sx: STICKY_HEADER_SX, children: headerRows.map((row, rowIdx) => /* @__PURE__ */ jsxs(TableRow, { sx: HEADER_ROW_SX, children: [
1023
- rowIdx === headerRows.length - 1 && hasCheckboxCol && /* @__PURE__ */ jsx(
1024
- TableCell,
1025
- {
1026
- ...{ padding: "checkbox", rowSpan: headerRows.length > 1 ? 1 : void 0, sx: CHECKBOX_CELL_SX },
1027
- children: /* @__PURE__ */ jsx(
1028
- Checkbox,
1029
- {
1030
- checked: allSelected,
1031
- indeterminate: someSelected,
1032
- onChange: (_, c) => handleSelectAll(!!c),
1033
- size: "small",
1034
- "aria-label": "Select all rows"
1035
- }
1036
- )
1037
- }
1038
- ),
1039
- rowIdx === 0 && rowIdx < headerRows.length - 1 && hasCheckboxCol && /* @__PURE__ */ jsx(TableCell, { ...{ rowSpan: headerRows.length - 1, sx: CHECKBOX_PLACEHOLDER_SX } }),
1040
- rowIdx === headerRows.length - 1 && hasRowNumbersCol && /* @__PURE__ */ jsx(
1041
- TableCell,
1042
- {
1043
- ...{
1044
- component: "th",
1045
- scope: "col",
1046
- rowSpan: headerRows.length > 1 ? 1 : void 0,
1047
- sx: {
1048
- width: ROW_NUMBER_COLUMN_WIDTH,
1049
- minWidth: ROW_NUMBER_COLUMN_WIDTH,
1050
- maxWidth: ROW_NUMBER_COLUMN_WIDTH,
1051
- textAlign: "center",
1052
- fontWeight: 600,
1053
- backgroundColor: HEADER_BG,
1054
- position: "sticky",
1055
- left: hasCheckboxCol ? CHECKBOX_COLUMN_WIDTH : 0,
1056
- zIndex: 4,
1057
- ...headerCellSx
1058
- }
1059
- },
1060
- children: "#"
1061
- }
1062
- ),
1063
- rowIdx === 0 && rowIdx < headerRows.length - 1 && hasRowNumbersCol && /* @__PURE__ */ jsx(
1064
- TableCell,
1065
- {
1066
- ...{
1067
- rowSpan: headerRows.length - 1,
1068
- sx: {
1069
- width: ROW_NUMBER_COLUMN_WIDTH,
1070
- minWidth: ROW_NUMBER_COLUMN_WIDTH,
1071
- position: "sticky",
1072
- left: hasCheckboxCol ? CHECKBOX_COLUMN_WIDTH : 0,
1073
- zIndex: 4,
1074
- backgroundColor: "background.paper"
1075
- }
1076
- }
1077
- }
1078
- ),
1079
- row.map((cell, cellIdx) => {
1080
- if (cell.isGroup) {
1145
+ /* @__PURE__ */ jsxs(TableHead, { sx: STICKY_HEADER_SX, children: [
1146
+ showColumnLetters && /* @__PURE__ */ jsxs(TableRow, { sx: HEADER_ROW_SX, children: [
1147
+ hasCheckboxCol && /* @__PURE__ */ jsx(TableCell, { sx: COLUMN_LETTER_CELL_SX }),
1148
+ hasRowNumbersCol && /* @__PURE__ */ jsx(TableCell, { sx: COLUMN_LETTER_CELL_SX }),
1149
+ visibleCols.map((col, colIdx) => {
1150
+ const hdrStyle = columnMeta.hdrStyles[col.columnId];
1081
1151
  return /* @__PURE__ */ jsx(
1082
1152
  TableCell,
1083
1153
  {
1084
1154
  ...{
1085
- colSpan: cell.colSpan,
1086
1155
  component: "th",
1087
- scope: "colgroup",
1088
- sx: GROUP_HEADER_CELL_SX
1156
+ sx: {
1157
+ ...COLUMN_LETTER_CELL_SX,
1158
+ minWidth: hdrStyle?.minWidth,
1159
+ width: hdrStyle?.width,
1160
+ maxWidth: hdrStyle?.maxWidth
1161
+ }
1089
1162
  },
1090
- children: cell.label
1163
+ children: indexToColumnLetter(colIdx)
1091
1164
  },
1092
- cellIdx
1165
+ col.columnId
1093
1166
  );
1094
- }
1095
- if (!cell.columnDef) return null;
1096
- const col = cell.columnDef;
1097
- const isPinnedLeft = pinning.pinnedColumns[col.columnId] === "left";
1098
- const isPinnedRight = pinning.pinnedColumns[col.columnId] === "right";
1099
- const baseHeaderSx = o.stickyHeader ? isPinnedLeft ? HEADER_PINNED_LEFT_SX : isPinnedRight ? HEADER_PINNED_RIGHT_SX : HEADER_BASE_SX : isPinnedLeft ? HEADER_PINNED_LEFT_NO_STICKY_SX : isPinnedRight ? HEADER_PINNED_RIGHT_NO_STICKY_SX : HEADER_BASE_NO_STICKY_SX;
1100
- const headerSx = isPinnedLeft && pinning.leftOffsets[col.columnId] != null ? { ...baseHeaderSx, left: pinning.leftOffsets[col.columnId] } : isPinnedRight && pinning.rightOffsets[col.columnId] != null ? { ...baseHeaderSx, right: pinning.rightOffsets[col.columnId] } : baseHeaderSx;
1101
- const hdrStyle = columnMeta.hdrStyles[col.columnId];
1102
- const isSorted = props.sortBy === col.columnId;
1103
- const ariaSort = isSorted ? props.sortDirection === "asc" ? "ascending" : "descending" : void 0;
1104
- return /* @__PURE__ */ jsxs(
1167
+ })
1168
+ ] }),
1169
+ headerRows.map((row, rowIdx) => /* @__PURE__ */ jsxs(TableRow, { sx: HEADER_ROW_SX, children: [
1170
+ rowIdx === headerRows.length - 1 && hasCheckboxCol && /* @__PURE__ */ jsx(
1171
+ TableCell,
1172
+ {
1173
+ ...{ padding: "checkbox", rowSpan: headerRows.length > 1 ? 1 : void 0, sx: CHECKBOX_CELL_SX },
1174
+ children: /* @__PURE__ */ jsx(
1175
+ Checkbox,
1176
+ {
1177
+ checked: allSelected,
1178
+ indeterminate: someSelected,
1179
+ onChange: (_, c) => handleSelectAll(!!c),
1180
+ size: "small",
1181
+ "aria-label": "Select all rows"
1182
+ }
1183
+ )
1184
+ }
1185
+ ),
1186
+ rowIdx === 0 && rowIdx < headerRows.length - 1 && hasCheckboxCol && /* @__PURE__ */ jsx(TableCell, { ...{ rowSpan: headerRows.length - 1, sx: CHECKBOX_PLACEHOLDER_SX } }),
1187
+ rowIdx === headerRows.length - 1 && hasRowNumbersCol && /* @__PURE__ */ jsx(
1105
1188
  TableCell,
1106
1189
  {
1107
1190
  ...{
1108
1191
  component: "th",
1109
1192
  scope: "col",
1110
- "data-column-id": col.columnId,
1111
- rowSpan: headerRows.length > 1 ? headerRows.length - rowIdx : void 0,
1112
- "aria-sort": ariaSort,
1193
+ rowSpan: headerRows.length > 1 ? 1 : void 0,
1113
1194
  sx: {
1114
- ...headerSx,
1115
- ...headerCellSx,
1116
- minWidth: hdrStyle?.minWidth,
1117
- width: hdrStyle?.width,
1118
- maxWidth: hdrStyle?.maxWidth,
1119
- ...columnReorder ? { cursor: isReorderDragging ? "grabbing" : "grab" } : {},
1120
- "&:focus-visible": {
1121
- outline: "2px solid",
1122
- outlineColor: "primary.main",
1123
- outlineOffset: "-2px",
1124
- zIndex: 11
1125
- }
1126
- },
1127
- onMouseDown: columnReorder ? (e) => handleHeaderMouseDown(col.columnId, e) : void 0
1195
+ width: ROW_NUMBER_COLUMN_WIDTH,
1196
+ minWidth: ROW_NUMBER_COLUMN_WIDTH,
1197
+ maxWidth: ROW_NUMBER_COLUMN_WIDTH,
1198
+ textAlign: "center",
1199
+ fontWeight: 600,
1200
+ backgroundColor: HEADER_BG,
1201
+ position: "sticky",
1202
+ left: hasCheckboxCol ? CHECKBOX_COLUMN_WIDTH : 0,
1203
+ zIndex: 4,
1204
+ ...headerCellSx
1205
+ }
1128
1206
  },
1129
- children: [
1130
- /* @__PURE__ */ jsxs(Box, { sx: HEADER_CONTENT_FLEX_SX, children: [
1131
- /* @__PURE__ */ jsx(ColumnHeaderFilter, { ...getHeaderFilterConfig(col, headerFilterInput) }),
1132
- /* @__PURE__ */ jsx(
1133
- Box,
1134
- {
1135
- component: "button",
1136
- onClick: (e) => {
1137
- e.stopPropagation();
1138
- headerMenu.open(col.columnId, e.currentTarget);
1139
- },
1140
- "aria-label": "Column options",
1141
- title: "Column options",
1142
- sx: COLUMN_OPTIONS_BUTTON_SX,
1143
- children: "\u22EE"
1144
- }
1145
- )
1146
- ] }),
1147
- /* @__PURE__ */ jsx(Box, { onMouseDown: (e) => {
1148
- setActiveCell(null);
1149
- interaction.setSelectionRange(null);
1150
- wrapperRef.current?.focus({ preventScroll: true });
1151
- handleResizeStart(e, col);
1152
- }, onDoubleClick: (e) => handleResizeDoubleClick(e, col), sx: RESIZE_HANDLE_SX })
1153
- ]
1154
- },
1155
- col.columnId
1156
- );
1157
- })
1158
- ] }, rowIdx)) }),
1159
- !showEmptyInGrid && /* @__PURE__ */ jsxs("tbody", { className: "ogrid-mat-tbody", children: [
1160
- virtualScrollEnabled && visibleRange.offsetTop > 0 && /* @__PURE__ */ jsx("tr", { style: { height: visibleRange.offsetTop }, "aria-hidden": true }),
1161
- virtualScrollEnabled ? items.slice(visibleRange.startIndex, visibleRange.endIndex + 1).map((item, i) => {
1162
- const rowIndex = visibleRange.startIndex + i;
1163
- const rowIdStr = getRowId(item);
1164
- return /* @__PURE__ */ jsx(
1165
- GridRow,
1166
- {
1167
- item,
1168
- rowIndex,
1169
- rowId: rowIdStr,
1170
- isSelected: selectedRowIds.has(rowIdStr),
1171
- columnLayouts,
1172
- renderCellContent,
1173
- handleSingleRowClick,
1174
- handleRowCheckboxChange,
1175
- lastMouseShiftRef,
1176
- hasCheckboxCol,
1177
- hasRowNumbersCol,
1178
- rowNumberOffset,
1179
- selectionRange,
1180
- activeCell: interaction.activeCell,
1181
- cutRange,
1182
- copyRange,
1183
- isDragging,
1184
- rowHeight,
1185
- editingRowId: editingCell?.rowId ?? null
1186
- },
1187
- rowIdStr
1188
- );
1189
- }) : items.map((item, rowIndex) => {
1190
- const rowIdStr = getRowId(item);
1191
- return /* @__PURE__ */ jsx(
1192
- GridRow,
1207
+ children: "#"
1208
+ }
1209
+ ),
1210
+ rowIdx === 0 && rowIdx < headerRows.length - 1 && hasRowNumbersCol && /* @__PURE__ */ jsx(
1211
+ TableCell,
1193
1212
  {
1194
- item,
1195
- rowIndex,
1196
- rowId: rowIdStr,
1197
- isSelected: selectedRowIds.has(rowIdStr),
1198
- columnLayouts,
1199
- renderCellContent,
1200
- handleSingleRowClick,
1201
- handleRowCheckboxChange,
1202
- lastMouseShiftRef,
1203
- hasCheckboxCol,
1204
- hasRowNumbersCol,
1205
- rowNumberOffset,
1206
- selectionRange,
1207
- activeCell: interaction.activeCell,
1208
- cutRange,
1209
- copyRange,
1210
- isDragging,
1211
- rowHeight,
1212
- editingRowId: editingCell?.rowId ?? null
1213
- },
1214
- rowIdStr
1215
- );
1216
- }),
1217
- virtualScrollEnabled && visibleRange.offsetBottom > 0 && /* @__PURE__ */ jsx("tr", { style: { height: visibleRange.offsetBottom }, "aria-hidden": true })
1218
- ] })
1213
+ ...{
1214
+ rowSpan: headerRows.length - 1,
1215
+ sx: {
1216
+ width: ROW_NUMBER_COLUMN_WIDTH,
1217
+ minWidth: ROW_NUMBER_COLUMN_WIDTH,
1218
+ position: "sticky",
1219
+ left: hasCheckboxCol ? CHECKBOX_COLUMN_WIDTH : 0,
1220
+ zIndex: 4,
1221
+ backgroundColor: "background.paper"
1222
+ }
1223
+ }
1224
+ }
1225
+ ),
1226
+ row.map((cell, cellIdx) => {
1227
+ if (cell.isGroup) {
1228
+ return /* @__PURE__ */ jsx(
1229
+ TableCell,
1230
+ {
1231
+ ...{
1232
+ colSpan: cell.colSpan,
1233
+ component: "th",
1234
+ scope: "colgroup",
1235
+ sx: GROUP_HEADER_CELL_SX
1236
+ },
1237
+ children: cell.label
1238
+ },
1239
+ cellIdx
1240
+ );
1241
+ }
1242
+ if (!cell.columnDef) return null;
1243
+ const col = cell.columnDef;
1244
+ const isPinnedLeft = pinning.pinnedColumns[col.columnId] === "left";
1245
+ const isPinnedRight = pinning.pinnedColumns[col.columnId] === "right";
1246
+ const baseHeaderSx = o.stickyHeader ? isPinnedLeft ? HEADER_PINNED_LEFT_SX : isPinnedRight ? HEADER_PINNED_RIGHT_SX : HEADER_BASE_SX : isPinnedLeft ? HEADER_PINNED_LEFT_NO_STICKY_SX : isPinnedRight ? HEADER_PINNED_RIGHT_NO_STICKY_SX : HEADER_BASE_NO_STICKY_SX;
1247
+ const headerSx = isPinnedLeft && pinning.leftOffsets[col.columnId] != null ? { ...baseHeaderSx, left: pinning.leftOffsets[col.columnId] } : isPinnedRight && pinning.rightOffsets[col.columnId] != null ? { ...baseHeaderSx, right: pinning.rightOffsets[col.columnId] } : baseHeaderSx;
1248
+ const hdrStyle = columnMeta.hdrStyles[col.columnId];
1249
+ const isSorted = props.sortBy === col.columnId;
1250
+ const ariaSort = isSorted ? props.sortDirection === "asc" ? "ascending" : "descending" : void 0;
1251
+ return /* @__PURE__ */ jsxs(
1252
+ TableCell,
1253
+ {
1254
+ ...{
1255
+ component: "th",
1256
+ scope: "col",
1257
+ "data-column-id": col.columnId,
1258
+ rowSpan: headerRows.length > 1 ? headerRows.length - rowIdx : void 0,
1259
+ "aria-sort": ariaSort,
1260
+ sx: {
1261
+ ...headerSx,
1262
+ ...headerCellSx,
1263
+ minWidth: hdrStyle?.minWidth,
1264
+ width: hdrStyle?.width,
1265
+ maxWidth: hdrStyle?.maxWidth,
1266
+ ...columnReorder ? { cursor: isReorderDragging ? "grabbing" : "grab" } : {},
1267
+ "&:focus-visible": {
1268
+ outline: "2px solid",
1269
+ outlineColor: "primary.main",
1270
+ outlineOffset: "-2px",
1271
+ zIndex: 11
1272
+ }
1273
+ },
1274
+ onMouseDown: columnReorder ? (e) => handleHeaderMouseDown(col.columnId, e) : void 0
1275
+ },
1276
+ children: [
1277
+ /* @__PURE__ */ jsxs(Box, { sx: HEADER_CONTENT_FLEX_SX, children: [
1278
+ /* @__PURE__ */ jsx(ColumnHeaderFilter, { ...getHeaderFilterConfig(col, headerFilterInput) }),
1279
+ /* @__PURE__ */ jsx(
1280
+ Box,
1281
+ {
1282
+ component: "button",
1283
+ onClick: (e) => {
1284
+ e.stopPropagation();
1285
+ headerMenu.open(col.columnId, e.currentTarget);
1286
+ },
1287
+ "aria-label": "Column options",
1288
+ title: "Column options",
1289
+ sx: COLUMN_OPTIONS_BUTTON_SX,
1290
+ children: "\u22EE"
1291
+ }
1292
+ )
1293
+ ] }),
1294
+ /* @__PURE__ */ jsx(Box, { onMouseDown: (e) => {
1295
+ setActiveCell(null);
1296
+ interaction.setSelectionRange(null);
1297
+ wrapperRef.current?.focus({ preventScroll: true });
1298
+ handleResizeStart(e, col);
1299
+ }, onDoubleClick: (e) => handleResizeDoubleClick(e, col), sx: RESIZE_HANDLE_SX })
1300
+ ]
1301
+ },
1302
+ col.columnId
1303
+ );
1304
+ })
1305
+ ] }, rowIdx))
1306
+ ] }),
1307
+ !showEmptyInGrid && /* @__PURE__ */ jsx(
1308
+ MaterialTableBody,
1309
+ {
1310
+ virtualScrollEnabled,
1311
+ visibleRange,
1312
+ columnRange,
1313
+ items,
1314
+ getRowId,
1315
+ selectedRowIds,
1316
+ visibleCols,
1317
+ columnLayouts,
1318
+ renderCellContent,
1319
+ handleSingleRowClick,
1320
+ handleRowCheckboxChange,
1321
+ lastMouseShiftRef,
1322
+ hasCheckboxCol,
1323
+ hasRowNumbersCol,
1324
+ rowNumberOffset,
1325
+ rowHeight,
1326
+ selectionRange,
1327
+ activeCell: interaction.activeCell,
1328
+ cutRange,
1329
+ copyRange,
1330
+ isDragging,
1331
+ editingCell,
1332
+ pinnedColumns: pinning.pinnedColumns
1333
+ }
1334
+ )
1219
1335
  ]
1220
1336
  }
1221
1337
  ),
@@ -1296,7 +1412,7 @@ function DataGridTableInner(props) {
1296
1412
  isLoading && /* @__PURE__ */ jsx(LoadingOverlay, { message: loadingMessage })
1297
1413
  ] });
1298
1414
  }
1299
- var DataGridTable = React3.memo(DataGridTableInner);
1415
+ var DataGridTable = React4.memo(DataGridTableInner);
1300
1416
  var ColumnChooser = (props) => {
1301
1417
  const { columns, visibleColumns, onVisibilityChange, onSetVisibleColumns, className } = props;
1302
1418
  const [anchorEl, setAnchorEl] = useState(null);
@@ -1437,7 +1553,7 @@ var ColumnChooser = (props) => {
1437
1553
  )
1438
1554
  ] });
1439
1555
  };
1440
- var PaginationControls = React3.memo((props) => {
1556
+ var PaginationControls = React4.memo((props) => {
1441
1557
  const {
1442
1558
  currentPage,
1443
1559
  pageSize,
@@ -1593,10 +1709,10 @@ var PaginationControls = React3.memo((props) => {
1593
1709
  }
1594
1710
  );
1595
1711
  });
1596
- var MuiThemeContainer = React3.forwardRef(
1712
+ var MuiThemeContainer = React4.forwardRef(
1597
1713
  function MuiThemeContainer2(props, ref) {
1598
1714
  const theme = useTheme();
1599
- const sx = React3.useMemo(() => ({
1715
+ const sx = React4.useMemo(() => ({
1600
1716
  display: "flex",
1601
1717
  flexDirection: "column",
1602
1718
  gap: 1,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alaarab/ogrid-react-material",
3
- "version": "2.1.15",
3
+ "version": "2.3.0",
4
4
  "description": "OGrid React Material implementation – MUI Table–based data grid with sorting, filtering, pagination, column chooser, spreadsheet selection, and CSV export.",
5
5
  "main": "dist/esm/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -39,7 +39,7 @@
39
39
  "node": ">=18"
40
40
  },
41
41
  "dependencies": {
42
- "@alaarab/ogrid-react": "2.1.15"
42
+ "@alaarab/ogrid-react": "2.3.0"
43
43
  },
44
44
  "peerDependencies": {
45
45
  "@emotion/react": "^11.0.0",