@cdx-ui/data-grid 0.3.10 → 0.3.12

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 (82) hide show
  1. package/README.md +3 -1
  2. package/coverage.svg +1 -1
  3. package/lib/commonjs/DataGrid.js +232 -62
  4. package/lib/commonjs/DataGrid.js.map +1 -1
  5. package/lib/commonjs/DataGridFilterPanel.js +420 -0
  6. package/lib/commonjs/DataGridFilterPanel.js.map +1 -0
  7. package/lib/commonjs/adapter/columnTypes.js +256 -0
  8. package/lib/commonjs/adapter/columnTypes.js.map +1 -0
  9. package/lib/commonjs/adapter/columns.js +132 -4
  10. package/lib/commonjs/adapter/columns.js.map +1 -1
  11. package/lib/commonjs/adapter/filterOperators.js +422 -0
  12. package/lib/commonjs/adapter/filterOperators.js.map +1 -0
  13. package/lib/commonjs/adapter/pinning.js +101 -0
  14. package/lib/commonjs/adapter/pinning.js.map +1 -0
  15. package/lib/commonjs/index.js +26 -0
  16. package/lib/commonjs/index.js.map +1 -1
  17. package/lib/commonjs/numberCells.js +280 -0
  18. package/lib/commonjs/numberCells.js.map +1 -0
  19. package/lib/commonjs/styles.js +61 -1
  20. package/lib/commonjs/styles.js.map +1 -1
  21. package/lib/commonjs/types.js +29 -1
  22. package/lib/commonjs/types.js.map +1 -1
  23. package/lib/commonjs/useDataGridTable.js +98 -13
  24. package/lib/commonjs/useDataGridTable.js.map +1 -1
  25. package/lib/module/DataGrid.js +237 -67
  26. package/lib/module/DataGrid.js.map +1 -1
  27. package/lib/module/DataGridFilterPanel.js +414 -0
  28. package/lib/module/DataGridFilterPanel.js.map +1 -0
  29. package/lib/module/adapter/columnTypes.js +240 -0
  30. package/lib/module/adapter/columnTypes.js.map +1 -0
  31. package/lib/module/adapter/columns.js +129 -4
  32. package/lib/module/adapter/columns.js.map +1 -1
  33. package/lib/module/adapter/filterOperators.js +406 -0
  34. package/lib/module/adapter/filterOperators.js.map +1 -0
  35. package/lib/module/adapter/pinning.js +90 -0
  36. package/lib/module/adapter/pinning.js.map +1 -0
  37. package/lib/module/index.js +2 -0
  38. package/lib/module/index.js.map +1 -1
  39. package/lib/module/numberCells.js +275 -0
  40. package/lib/module/numberCells.js.map +1 -0
  41. package/lib/module/styles.js +60 -0
  42. package/lib/module/styles.js.map +1 -1
  43. package/lib/module/types.js +28 -0
  44. package/lib/module/types.js.map +1 -1
  45. package/lib/module/useDataGridTable.js +99 -14
  46. package/lib/module/useDataGridTable.js.map +1 -1
  47. package/lib/typescript/DataGrid.d.ts.map +1 -1
  48. package/lib/typescript/DataGridFilterPanel.d.ts +17 -0
  49. package/lib/typescript/DataGridFilterPanel.d.ts.map +1 -0
  50. package/lib/typescript/adapter/columnTypes.d.ts +32 -0
  51. package/lib/typescript/adapter/columnTypes.d.ts.map +1 -0
  52. package/lib/typescript/adapter/columns.d.ts +14 -5
  53. package/lib/typescript/adapter/columns.d.ts.map +1 -1
  54. package/lib/typescript/adapter/filterOperators.d.ts +43 -0
  55. package/lib/typescript/adapter/filterOperators.d.ts.map +1 -0
  56. package/lib/typescript/adapter/pinning.d.ts +25 -0
  57. package/lib/typescript/adapter/pinning.d.ts.map +1 -0
  58. package/lib/typescript/index.d.ts +4 -1
  59. package/lib/typescript/index.d.ts.map +1 -1
  60. package/lib/typescript/numberCells.d.ts +46 -0
  61. package/lib/typescript/numberCells.d.ts.map +1 -0
  62. package/lib/typescript/styles.d.ts +21 -0
  63. package/lib/typescript/styles.d.ts.map +1 -1
  64. package/lib/typescript/types.d.ts +86 -7
  65. package/lib/typescript/types.d.ts.map +1 -1
  66. package/lib/typescript/useDataGridTable.d.ts +18 -4
  67. package/lib/typescript/useDataGridTable.d.ts.map +1 -1
  68. package/package.json +5 -5
  69. package/src/DataGrid.tsx +377 -67
  70. package/src/DataGridFilterPanel.tsx +477 -0
  71. package/src/adapter/columnTypes.ts +327 -0
  72. package/src/adapter/columns.test.ts +75 -1
  73. package/src/adapter/columns.ts +178 -3
  74. package/src/adapter/filterOperators.test.ts +292 -0
  75. package/src/adapter/filterOperators.ts +524 -0
  76. package/src/adapter/pinning.ts +125 -0
  77. package/src/index.ts +15 -0
  78. package/src/numberCells.tsx +379 -0
  79. package/src/styles.ts +75 -0
  80. package/src/types.ts +114 -7
  81. package/src/useDataGridTable.test.ts +69 -0
  82. package/src/useDataGridTable.ts +127 -8
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @cdx-ui/data-grid
2
2
 
3
- Web DataGrid with a MUI Community–shaped `rows` / `columns` API. TanStack Table is an internal engine — do not import it from consumer code.
3
+ Web DataGrid with an MUI X–shaped `rows` / `columns` API. TanStack Table is an internal engine — do not import it from consumer code. `gridPolarityNumberColDef` and `gridDeltaNumberColDef` are spreadable `number` column presets for signed numbers and changes.
4
4
 
5
5
  ```tsx
6
6
  import { DataGrid, type GridColDef } from '@cdx-ui/data-grid';
@@ -16,3 +16,5 @@ Add the grid's stylesheet to your Tailwind CSS entry, after the components style
16
16
  ```
17
17
 
18
18
  The grid uses a **300px** viewport by default (MUI Data Grid Community). Headers stay visible while rows scroll; pagination stays at the bottom. Override with `className` or `style`, or set `autoHeight` to size the grid to its rows.
19
+
20
+ Pin columns with `pinnedColumns={{ start, end }}`. Deprecated `left` / `right` keys alias `start` / `end`. See the [Data Grid docs](https://design.candescent.com/docs/components/data-grid).
package/coverage.svg CHANGED
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="108" height="20" role="img" aria-label="coverage: 91.5%"><title>coverage: 91.5%</title><filter id="blur"><feGaussianBlur stdDeviation="16"/></filter><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="108" height="20" rx="3"/></clipPath><g clip-path="url(#r)"><rect width="61" height="20" fill="#555"/><rect x="61" width="47" height="20" fill="#4b0"/><rect width="108" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><g transform="scale(.1)"><g aria-hidden="true" fill="#010101"><text x="315" y="150" fill-opacity=".8" filter="url(#blur)" textLength="510">coverage</text><text x="315" y="150" fill-opacity=".3" textLength="510">coverage</text></g><text x="315" y="140" textLength="510">coverage</text></g><g transform="scale(.1)"><g aria-hidden="true" fill="#010101"><text x="835" y="150" fill-opacity=".8" filter="url(#blur)" textLength="370">91.5%</text><text x="835" y="150" fill-opacity=".3" textLength="370">91.5%</text></g><text x="835" y="140" textLength="370">91.5%</text></g></g></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="108" height="20" role="img" aria-label="coverage: 87.1%"><title>coverage: 87.1%</title><filter id="blur"><feGaussianBlur stdDeviation="16"/></filter><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="108" height="20" rx="3"/></clipPath><g clip-path="url(#r)"><rect width="61" height="20" fill="#555"/><rect x="61" width="47" height="20" fill="#4b0"/><rect width="108" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><g transform="scale(.1)"><g aria-hidden="true" fill="#010101"><text x="315" y="150" fill-opacity=".8" filter="url(#blur)" textLength="510">coverage</text><text x="315" y="150" fill-opacity=".3" textLength="510">coverage</text></g><text x="315" y="140" textLength="510">coverage</text></g><g transform="scale(.1)"><g aria-hidden="true" fill="#010101"><text x="835" y="150" fill-opacity=".8" filter="url(#blur)" textLength="370">87.1%</text><text x="835" y="150" fill-opacity=".3" textLength="370">87.1%</text></g><text x="835" y="140" textLength="370">87.1%</text></g></g></svg>
@@ -11,16 +11,20 @@ var _icons = require("@cdx-ui/icons");
11
11
  var _primitives = require("@cdx-ui/primitives");
12
12
  var _utils = require("@cdx-ui/utils");
13
13
  var _columns = require("./adapter/columns");
14
+ var _pinning = require("./adapter/pinning");
15
+ var _columnTypes = require("./adapter/columnTypes");
16
+ var _DataGridFilterPanel = require("./DataGridFilterPanel");
14
17
  var _styles = require("./styles");
15
18
  var _types = require("./types");
16
19
  var _useDataGridTable = require("./useDataGridTable");
17
20
  var _jsxRuntime = require("react/jsx-runtime");
18
21
  const MAX_SKELETON_ROW_COUNT = 10;
19
22
  const DEFAULT_WEB_COLUMN_WIDTH = 100;
20
- const SELECT_COLUMN_WIDTH = 56;
21
23
  const WEB_TABLE_CONTAINER_STYLE = {
22
24
  overflow: 'visible'
23
25
  };
26
+ // Same token as the pinned cells' `border-stroke-secondary` divider.
27
+ const PINNED_EDGE_COLOR = 'var(--color-stroke-secondary)';
24
28
  function cellStyle(col, webWidth) {
25
29
  if (!col) {
26
30
  return undefined;
@@ -43,36 +47,96 @@ function cellStyle(col, webWidth) {
43
47
  }
44
48
  return Object.keys(style).length > 0 ? style : undefined;
45
49
  }
46
- function webColumnWidths(columns, checkboxSelection) {
47
- const totalFlex = columns.reduce((total, column) => total + Math.max(column.flex ?? 0, 0), 0);
50
+ function effectiveFlex(column, pinned) {
51
+ if (pinned.has(column.field)) {
52
+ return 0;
53
+ }
54
+ return Math.max(column.flex ?? 0, 0);
55
+ }
56
+ function webColumnWidths(columns, checkboxSelection, pinned = new Set()) {
57
+ const totalFlex = columns.reduce((total, column) => total + effectiveFlex(column, pinned), 0);
48
58
  const widths = new Map();
49
59
  if (totalFlex === 0) {
50
60
  for (const column of columns) {
51
- if (column.width !== undefined) {
61
+ if (pinned.has(column.field)) {
62
+ widths.set(column.field, Math.max(column.width ?? _pinning.DEFAULT_PINNED_COLUMN_WIDTH, column.minWidth ?? 0));
63
+ } else if (column.width !== undefined) {
52
64
  widths.set(column.field, column.width);
53
65
  }
54
66
  }
55
67
  return widths;
56
68
  }
57
- const fixedWidth = (checkboxSelection ? SELECT_COLUMN_WIDTH : 0) + columns.reduce((total, column) => {
58
- if ((column.flex ?? 0) > 0) {
69
+ const fixedWidth = (checkboxSelection ? _pinning.SELECT_COLUMN_WIDTH : 0) + columns.reduce((total, column) => {
70
+ if (effectiveFlex(column, pinned) > 0) {
59
71
  return total;
60
72
  }
61
- return total + Math.max(column.width ?? DEFAULT_WEB_COLUMN_WIDTH, column.minWidth ?? 0);
73
+ const fallback = pinned.has(column.field) ? _pinning.DEFAULT_PINNED_COLUMN_WIDTH : DEFAULT_WEB_COLUMN_WIDTH;
74
+ return total + Math.max(column.width ?? fallback, column.minWidth ?? 0);
62
75
  }, 0);
63
76
  for (const column of columns) {
64
- if ((column.flex ?? 0) > 0) {
65
- const share = (column.flex ?? 0) / totalFlex;
77
+ const flex = effectiveFlex(column, pinned);
78
+ if (flex > 0) {
79
+ const share = flex / totalFlex;
66
80
  const percentage = share * 100;
67
81
  const fixedShare = fixedWidth * share;
68
82
  const width = `calc(${percentage.toString()}% - ${fixedShare.toString()}px)`;
69
83
  widths.set(column.field, column.minWidth === undefined ? width : `max(${column.minWidth.toString()}px, ${width})`);
70
84
  } else {
71
- widths.set(column.field, Math.max(column.width ?? DEFAULT_WEB_COLUMN_WIDTH, column.minWidth ?? 0));
85
+ const fallback = pinned.has(column.field) ? _pinning.DEFAULT_PINNED_COLUMN_WIDTH : DEFAULT_WEB_COLUMN_WIDTH;
86
+ widths.set(column.field, Math.max(column.width ?? fallback, column.minWidth ?? 0));
72
87
  }
73
88
  }
74
89
  return widths;
75
90
  }
91
+ function pinMetaFor(columnId, pinning) {
92
+ const startIndex = pinning.start.indexOf(columnId);
93
+ if (startIndex >= 0) {
94
+ return {
95
+ edge: 'start',
96
+ isEdgeDivider: startIndex === pinning.start.length - 1
97
+ };
98
+ }
99
+ const endIndex = pinning.end.indexOf(columnId);
100
+ if (endIndex >= 0) {
101
+ return {
102
+ edge: 'end',
103
+ isEdgeDivider: endIndex === 0
104
+ };
105
+ }
106
+ return {
107
+ edge: false,
108
+ isEdgeDivider: false
109
+ };
110
+ }
111
+ function mergePinnedStyle(base, pin, offsets, columnId, isHeader) {
112
+ if (!pin.edge || offsets == null) {
113
+ return base;
114
+ }
115
+ const offset = pin.edge === 'start' ? offsets.start.get(columnId) ?? 0 : offsets.end.get(columnId) ?? 0;
116
+ return {
117
+ ...base,
118
+ ...(0, _pinning.pinnedCellStyle)(pin.edge, offset, isHeader)
119
+ };
120
+ }
121
+ function pinnedClassName(pin, isHeader) {
122
+ if (!pin.edge) {
123
+ return undefined;
124
+ }
125
+ return (0, _utils.cn)((0, _styles.dataGridPinnedSurfaceVariants)({
126
+ header: isHeader
127
+ }), (0, _styles.dataGridPinnedEdgeVariants)({
128
+ edge: pin.isEdgeDivider ? pin.edge : 'none'
129
+ }));
130
+ }
131
+ function BooleanCell({
132
+ value
133
+ }) {
134
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Icon, {
135
+ as: value ? _icons.Check : _icons.Close,
136
+ accessibilityLabel: value ? 'Yes' : 'No',
137
+ className: "size-5"
138
+ });
139
+ }
76
140
  function defaultCellContent(value) {
77
141
  if (value == null) {
78
142
  return '';
@@ -94,28 +158,44 @@ function DefaultLoadingRows({
94
158
  columns,
95
159
  checkboxSelection,
96
160
  rowCount,
97
- columnWidths
161
+ columnWidths,
162
+ orderedColumnIds,
163
+ pinning,
164
+ offsets
98
165
  }) {
166
+ const colByField = new Map(columns.map(column => [column.field, column]));
167
+ const ids = orderedColumnIds ?? (checkboxSelection ? [_types.SELECT_COLUMN_ID, ...columns.map(column => column.field)] : columns.map(column => column.field));
99
168
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_jsxRuntime.Fragment, {
100
169
  children: Array.from({
101
170
  length: rowCount
102
- }, (_, rowIndex) => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_components.Table.Row, {
171
+ }, (_, rowIndex) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Table.Row, {
103
172
  className: "pointer-events-none web:hover:bg-surface-primary",
104
173
  testID: rowIndex === 0 ? 'data-grid-default-loading-rows' : undefined,
105
- children: [checkboxSelection ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Table.Cell, {
106
- align: "center",
107
- className: (0, _styles.dataGridSelectCellVariants)(),
108
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Skeleton, {
109
- className: "size-5"
110
- })
111
- }) : null, columns.map(column => /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Table.Cell, {
112
- align: (0, _columns.normalizeGridAlignment)(column.align),
113
- className: (0, _styles.dataGridCellVariants)(),
114
- style: cellStyle(column, columnWidths.get(column.field)),
115
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Skeleton, {
116
- className: "h-4 w-3/5"
117
- })
118
- }, column.field))]
174
+ children: ids.map(columnId => {
175
+ const pin = pinMetaFor(columnId, pinning);
176
+ if (columnId === _types.SELECT_COLUMN_ID) {
177
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Table.Cell, {
178
+ align: "center",
179
+ className: (0, _utils.cn)((0, _styles.dataGridSelectCellVariants)(), pinnedClassName(pin, false)),
180
+ style: mergePinnedStyle(undefined, pin, offsets, columnId, false),
181
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Skeleton, {
182
+ className: "size-5"
183
+ })
184
+ }, columnId);
185
+ }
186
+ const column = colByField.get(columnId);
187
+ if (!column) {
188
+ return null;
189
+ }
190
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Table.Cell, {
191
+ align: (0, _columns.resolveColumnAlign)(column),
192
+ className: (0, _utils.cn)((0, _styles.dataGridCellVariants)(), pinnedClassName(pin, false)),
193
+ style: mergePinnedStyle(cellStyle(column, columnWidths.get(column.field)), pin, offsets, column.field, false),
194
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Skeleton, {
195
+ className: "h-4 w-3/5"
196
+ })
197
+ }, column.field);
198
+ })
119
199
  }, rowIndex))
120
200
  });
121
201
  }
@@ -148,6 +228,7 @@ function DataGridInner(props) {
148
228
  rowCount,
149
229
  showToolbar = false,
150
230
  headerFilters = false,
231
+ pinnedColumns,
151
232
  pageSizeOptions = _types.DEFAULT_PAGE_SIZE_OPTIONS,
152
233
  noRowsOverlay,
153
234
  loadingOverlay,
@@ -173,6 +254,11 @@ function DataGridInner(props) {
173
254
  defaultProp: props.defaultFilterModel ?? [],
174
255
  onChange: props.onFilterModelChange
175
256
  });
257
+ const [filterLogicOperator, setFilterLogicOperator] = (0, _utils.useControllableState)({
258
+ prop: props.filterLogicOperator,
259
+ defaultProp: props.defaultFilterLogicOperator ?? 'and',
260
+ onChange: props.onFilterLogicOperatorChange
261
+ });
176
262
  const [quickFilter, setQuickFilter] = (0, _utils.useControllableState)({
177
263
  prop: props.quickFilter,
178
264
  defaultProp: props.defaultQuickFilter ?? '',
@@ -186,12 +272,18 @@ function DataGridInner(props) {
186
272
  const resolvedSortModel = sortModel ?? [];
187
273
  const resolvedPagination = paginationModel ?? _types.DEFAULT_PAGINATION_MODEL;
188
274
  const resolvedFilterModel = filterModel ?? [];
275
+ const resolvedFilterLogicOperator = filterLogicOperator ?? 'and';
189
276
  const resolvedQuickFilter = quickFilter ?? '';
190
277
  const resolvedSelection = rowSelectionModel ?? _types.EMPTY_SELECTION;
278
+ const knownFields = (0, _react.useMemo)(() => new Set(columns.map(column => column.field)), [columns]);
279
+ const columnPinning = (0, _react.useMemo)(() => (0, _columns.normalizePinnedColumns)(pinnedColumns, knownFields, checkboxSelection), [pinnedColumns, knownFields, checkboxSelection]);
280
+ const pinningActive = (0, _pinning.isColumnPinningActive)(columnPinning);
281
+ const pinnedFields = (0, _react.useMemo)(() => (0, _pinning.pinnedFieldSet)(columnPinning), [columnPinning]);
191
282
  const {
192
283
  table,
193
284
  setQuickFilter: setQuickFilterWithPageReset,
194
- setColumnFilter
285
+ setColumnFilter,
286
+ setFilterModel: setFilterModelWithPageReset
195
287
  } = (0, _useDataGridTable.useDataGridTable)({
196
288
  rows,
197
289
  columns,
@@ -208,10 +300,12 @@ function DataGridInner(props) {
208
300
  onPaginationModelChange: model => setPaginationModel(model),
209
301
  filterModel: resolvedFilterModel,
210
302
  onFilterModelChange: model => setFilterModel(model),
303
+ filterLogicOperator: resolvedFilterLogicOperator,
211
304
  quickFilter: resolvedQuickFilter,
212
305
  onQuickFilterChange: value => setQuickFilter(value),
213
306
  rowSelectionModel: resolvedSelection,
214
- onRowSelectionModelChange: model => setRowSelectionModel(model)
307
+ onRowSelectionModelChange: model => setRowSelectionModel(model),
308
+ columnPinning
215
309
  });
216
310
  const colByField = (0, _react.useMemo)(() => {
217
311
  const map = new Map();
@@ -220,8 +314,32 @@ function DataGridInner(props) {
220
314
  }
221
315
  return map;
222
316
  }, [columns]);
223
- const columnWidths = (0, _react.useMemo)(() => webColumnWidths(columns, checkboxSelection), [checkboxSelection, columns]);
224
- const hasFlexColumns = (0, _react.useMemo)(() => columns.some(column => (column.flex ?? 0) > 0), [columns]);
317
+ (0, _pinning.warnPinnedFlexColumns)(columns, pinnedFields);
318
+ const columnWidths = (0, _react.useMemo)(() => webColumnWidths(columns, checkboxSelection, pinnedFields), [checkboxSelection, columns, pinnedFields]);
319
+ const hasFlexColumns = (0, _react.useMemo)(() => columns.some(column => effectiveFlex(column, pinnedFields) > 0), [columns, pinnedFields]);
320
+ const pinnedOffsets = (0, _react.useMemo)(() => pinningActive ? (0, _pinning.buildPinnedOffsets)(columnPinning, colByField) : null, [pinningActive, columnPinning, colByField]);
321
+ const orderedLeafColumnIds = (0, _react.useMemo)(() => {
322
+ if (!pinningActive) {
323
+ return undefined;
324
+ }
325
+ return [...table.getStartVisibleLeafColumns().map(column => column.id), ...table.getCenterVisibleLeafColumns().map(column => column.id), ...table.getEndVisibleLeafColumns().map(column => column.id)];
326
+ }, [pinningActive, table]);
327
+ const isEmpty = !loading && rows.length === 0;
328
+ const mainScrollStyle = (0, _react.useMemo)(() => {
329
+ if (!pinningActive || pinnedOffsets == null) {
330
+ return undefined;
331
+ }
332
+ const guides = _reactNative.Platform.OS === 'web' && !isEmpty ? (0, _pinning.pinnedEdgeGuidesImage)(pinnedOffsets, PINNED_EDGE_COLOR, _reactNative.I18nManager.isRTL) : undefined;
333
+ // Logical scroll-padding keeps focused cells out from under pinned edges (web).
334
+ return {
335
+ scrollPaddingInlineStart: pinnedOffsets.startTotal,
336
+ scrollPaddingInlineEnd: pinnedOffsets.endTotal,
337
+ ...(guides ? {
338
+ backgroundImage: guides,
339
+ backgroundRepeat: 'no-repeat'
340
+ } : {})
341
+ };
342
+ }, [pinningActive, pinnedOffsets, isEmpty]);
225
343
  const visibleRows = table.getRowModel().rows;
226
344
  const pageCount = Math.max(table.getPageCount(), 1);
227
345
  const selectedCount = table.getFilteredSelectedRowModel().rows.length;
@@ -237,14 +355,20 @@ function DataGridInner(props) {
237
355
  indeterminate: true
238
356
  };
239
357
  const accessibleName = ariaLabel ?? label;
240
- const isEmpty = !loading && rows.length === 0;
241
358
  const totalCount = paginationMode === 'server' ? rowCount ?? rows.length : filterableCount;
242
- const filterValueFor = field => resolvedFilterModel.find(item => item.field === field)?.value ?? '';
359
+ const filterValueFor = field => {
360
+ const value = resolvedFilterModel.find(item => item.field === field)?.value;
361
+ return typeof value === 'string' ? value : '';
362
+ };
243
363
  const showToolbarBar = showToolbar || toolbarActions != null;
364
+ const filterPanelColumns = (0, _react.useMemo)(() => columns.filter(column => column.filterable !== false), [columns]);
365
+ const [filterPanelOpen, setFilterPanelOpen] = (0, _react.useState)(false);
366
+ const filterPanelButtonRef = (0, _react.useRef)(null);
367
+ const showFilterPanelButton = showToolbar && filterPanelColumns.length > 0;
244
368
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
245
369
  className: (0, _utils.cn)((0, _styles.dataGridRootVariants)({
246
370
  autoHeight
247
- }), className),
371
+ }), className, filterPanelOpen && 'overflow-visible'),
248
372
  style: style,
249
373
  ...(0, _primitives.mergeDataAttributes)({}, (0, _primitives.dataAttributes)({
250
374
  slot: 'data-grid'
@@ -262,15 +386,43 @@ function DataGridInner(props) {
262
386
  placeholder: "Search",
263
387
  accessibilityLabel: "Search"
264
388
  })]
389
+ }) : null, showFilterPanelButton ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.IconButton, {
390
+ ref: filterPanelButtonRef,
391
+ as: _icons.FilterList,
392
+ variant: "ghost",
393
+ color: "neutral",
394
+ size: "small",
395
+ accessibilityLabel: "Filters",
396
+ accessibilityState: {
397
+ expanded: filterPanelOpen
398
+ },
399
+ onPress: () => setFilterPanelOpen(open => !open)
265
400
  }) : null, toolbarActions ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
266
401
  className: (0, _styles.dataGridToolbarActionsVariants)(),
267
402
  children: toolbarActions
268
403
  }) : null]
404
+ }) : null, showFilterPanelButton && filterPanelOpen ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_DataGridFilterPanel.DataGridFilterPanel, {
405
+ columns: filterPanelColumns,
406
+ filterModel: resolvedFilterModel,
407
+ onFilterModelChange: setFilterModelWithPageReset,
408
+ logicOperator: resolvedFilterLogicOperator,
409
+ onLogicOperatorChange: operator => {
410
+ setFilterLogicOperator(operator);
411
+ if (resolvedPagination.page !== 0) {
412
+ setPaginationModel({
413
+ ...resolvedPagination,
414
+ page: 0
415
+ });
416
+ }
417
+ },
418
+ onClose: () => setFilterPanelOpen(false),
419
+ triggerRef: filterPanelButtonRef
269
420
  }) : null, /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
270
421
  className: (0, _styles.dataGridMainVariants)({
271
422
  autoHeight,
272
423
  overlay: isEmpty || loading && loadingOverlay != null
273
424
  }),
425
+ style: mainScrollStyle,
274
426
  children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_components.Table, {
275
427
  className: (0, _utils.cn)((0, _styles.dataGridTableVariants)(), _reactNative.Platform.OS === 'web' && hasFlexColumns ? 'table-fixed' : undefined),
276
428
  containerStyle: _reactNative.Platform.OS === 'web' ? WEB_TABLE_CONTAINER_STYLE : undefined,
@@ -282,25 +434,30 @@ function DataGridInner(props) {
282
434
  } : {}),
283
435
  children: [label ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Table.Caption, {
284
436
  children: label
285
- }) : null, _reactNative.Platform.OS === 'web' && hasFlexColumns ? /*#__PURE__*/(0, _jsxRuntime.jsxs)("colgroup", {
286
- children: [checkboxSelection ? /*#__PURE__*/(0, _jsxRuntime.jsx)("col", {
437
+ }) : null, _reactNative.Platform.OS === 'web' && hasFlexColumns ? /*#__PURE__*/(0, _jsxRuntime.jsx)("colgroup", {
438
+ children: (orderedLeafColumnIds ?? (checkboxSelection ? [_types.SELECT_COLUMN_ID, ...columns.map(column => column.field)] : columns.map(column => column.field))).map(columnId => columnId === _types.SELECT_COLUMN_ID ? /*#__PURE__*/(0, _jsxRuntime.jsx)("col", {
287
439
  style: {
288
- width: SELECT_COLUMN_WIDTH
440
+ width: _pinning.SELECT_COLUMN_WIDTH
289
441
  }
290
- }) : null, columns.map(column => /*#__PURE__*/(0, _jsxRuntime.jsx)("col", {
442
+ }, columnId) : /*#__PURE__*/(0, _jsxRuntime.jsx)("col", {
291
443
  style: {
292
- width: columnWidths.get(column.field),
293
- minWidth: column.minWidth
444
+ width: columnWidths.get(columnId),
445
+ minWidth: colByField.get(columnId)?.minWidth
294
446
  }
295
- }, column.field))]
447
+ }, columnId))
296
448
  }) : null, /*#__PURE__*/(0, _jsxRuntime.jsxs)(_components.Table.Header, {
297
449
  className: (0, _styles.dataGridHeaderVariants)(),
298
- children: [table.getHeaderGroups().map(headerGroup => /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Table.Row, {
450
+ children: [(pinningActive ? [{
451
+ id: 'pinned',
452
+ headers: [...table.getStartLeafHeaders(), ...table.getCenterLeafHeaders(), ...table.getEndLeafHeaders()]
453
+ }] : table.getHeaderGroups()).map(headerGroup => /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Table.Row, {
299
454
  children: headerGroup.headers.map(header => {
455
+ const pin = pinMetaFor(header.column.id, columnPinning);
300
456
  if (header.id === _types.SELECT_COLUMN_ID) {
301
457
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Table.Head, {
302
458
  align: "center",
303
- className: (0, _styles.dataGridSelectHeadVariants)(),
459
+ className: (0, _utils.cn)((0, _styles.dataGridSelectHeadVariants)(), pinnedClassName(pin, true)),
460
+ style: mergePinnedStyle(undefined, pin, pinnedOffsets, header.id, true),
304
461
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Checkbox, {
305
462
  value: "select-all",
306
463
  isChecked: headerSelectState.checked,
@@ -314,7 +471,7 @@ function DataGridInner(props) {
314
471
  const col = colByField.get(header.column.id);
315
472
  const sorted = header.column.getIsSorted();
316
473
  const canSort = !disableColumnSorting && header.column.getCanSort();
317
- const headerAlign = (0, _columns.normalizeGridAlignment)(col?.headerAlign ?? col?.align);
474
+ const headerAlign = (0, _columns.resolveColumnHeaderAlign)(col);
318
475
  const headerName = col?.headerName ?? header.column.id;
319
476
  const SortIcon = sorted === 'asc' ? _icons.KeyboardArrowUp : _icons.KeyboardArrowDown;
320
477
  const sortButton = canSort ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.IconButton, {
@@ -334,8 +491,8 @@ function DataGridInner(props) {
334
491
  }) : null;
335
492
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Table.Head, {
336
493
  align: headerAlign,
337
- className: (0, _styles.dataGridHeadVariants)(),
338
- style: cellStyle(col, columnWidths.get(header.column.id)),
494
+ className: (0, _utils.cn)((0, _styles.dataGridHeadVariants)(), pinnedClassName(pin, true)),
495
+ style: mergePinnedStyle(cellStyle(col, columnWidths.get(header.column.id)), pin, pinnedOffsets, header.column.id, true),
339
496
  ...(_reactNative.Platform.OS === 'web' ? {
340
497
  title: col?.renderHeader ? undefined : headerName,
341
498
  ...(canSort ? {
@@ -356,26 +513,27 @@ function DataGridInner(props) {
356
513
  }, header.id);
357
514
  })
358
515
  }, headerGroup.id)), headerFilters ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Table.Row, {
359
- children: table.getAllLeafColumns().map(column => {
516
+ children: (pinningActive ? [...table.getStartVisibleLeafColumns(), ...table.getCenterVisibleLeafColumns(), ...table.getEndVisibleLeafColumns()] : table.getAllLeafColumns()).map(column => {
517
+ const pin = pinMetaFor(column.id, columnPinning);
360
518
  if (column.id === _types.SELECT_COLUMN_ID) {
361
519
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Table.Cell, {
362
520
  align: "center",
363
- className: (0, _styles.dataGridSelectHeadVariants)()
521
+ className: (0, _utils.cn)((0, _styles.dataGridSelectHeadVariants)(), pinnedClassName(pin, true)),
522
+ style: mergePinnedStyle(undefined, pin, pinnedOffsets, column.id, true)
364
523
  }, column.id);
365
524
  }
366
525
  const col = colByField.get(column.id);
367
- const headerAlign = col?.headerAlign ?? col?.align;
368
526
  if (col?.filterable === false) {
369
527
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Table.Head, {
370
- align: (0, _columns.normalizeGridAlignment)(headerAlign),
371
- className: (0, _styles.dataGridHeadVariants)(),
372
- style: cellStyle(col, columnWidths.get(column.id))
528
+ align: (0, _columns.resolveColumnHeaderAlign)(col),
529
+ className: (0, _utils.cn)((0, _styles.dataGridHeadVariants)(), pinnedClassName(pin, true)),
530
+ style: mergePinnedStyle(cellStyle(col, columnWidths.get(column.id)), pin, pinnedOffsets, column.id, true)
373
531
  }, column.id);
374
532
  }
375
533
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Table.Head, {
376
- align: (0, _columns.normalizeGridAlignment)(headerAlign),
377
- className: (0, _styles.dataGridHeadVariants)(),
378
- style: cellStyle(col, columnWidths.get(column.id)),
534
+ align: (0, _columns.resolveColumnHeaderAlign)(col),
535
+ className: (0, _utils.cn)((0, _styles.dataGridHeadVariants)(), pinnedClassName(pin, true)),
536
+ style: mergePinnedStyle(cellStyle(col, columnWidths.get(column.id)), pin, pinnedOffsets, column.id, true),
379
537
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Input, {
380
538
  size: "small",
381
539
  className: (0, _styles.dataGridFilterInputVariants)(),
@@ -395,15 +553,21 @@ function DataGridInner(props) {
395
553
  columns: columns,
396
554
  checkboxSelection: checkboxSelection,
397
555
  rowCount: Math.min(resolvedPagination.pageSize, MAX_SKELETON_ROW_COUNT),
398
- columnWidths: columnWidths
556
+ columnWidths: columnWidths,
557
+ orderedColumnIds: orderedLeafColumnIds,
558
+ pinning: columnPinning,
559
+ offsets: pinnedOffsets
399
560
  }) : isEmpty ? null : visibleRows.map(row => {
400
561
  const rowId = (0, _useDataGridTable.resolveRowId)(row.original, getRowId);
562
+ const cells = pinningActive ? [...row.getStartVisibleCells(), ...row.getCenterVisibleCells(), ...row.getEndVisibleCells()] : row.getAllCells();
401
563
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Table.Row, {
402
- children: row.getAllCells().map(cell => {
564
+ children: cells.map(cell => {
565
+ const pin = pinMetaFor(cell.column.id, columnPinning);
403
566
  if (cell.column.id === _types.SELECT_COLUMN_ID) {
404
567
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Table.Cell, {
405
568
  align: "center",
406
- className: (0, _styles.dataGridSelectCellVariants)(),
569
+ className: (0, _utils.cn)((0, _styles.dataGridSelectCellVariants)(), pinnedClassName(pin, false)),
570
+ style: mergePinnedStyle(undefined, pin, pinnedOffsets, cell.column.id, false),
407
571
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Checkbox, {
408
572
  value: String(rowId),
409
573
  isChecked: row.getIsSelected(),
@@ -421,17 +585,23 @@ function DataGridInner(props) {
421
585
  value,
422
586
  row: row.original,
423
587
  field: col.field,
424
- id: rowId
588
+ id: rowId,
589
+ col
425
590
  });
426
591
  } else if (col?.valueFormatter) {
427
592
  content = col.valueFormatter(value, row.original, col);
593
+ } else if (col?.type === 'boolean' && typeof value === 'boolean') {
594
+ content = /*#__PURE__*/(0, _jsxRuntime.jsx)(BooleanCell, {
595
+ value: value
596
+ });
428
597
  } else {
429
- content = defaultCellContent(value);
598
+ const typedText = (0, _columnTypes.getTypedCellText)(col, value, row.original);
599
+ content = typedText !== undefined ? typedText : defaultCellContent(value);
430
600
  }
431
601
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Table.Cell, {
432
- align: (0, _columns.normalizeGridAlignment)(col?.align),
433
- className: (0, _styles.dataGridCellVariants)(),
434
- style: cellStyle(col, columnWidths.get(cell.column.id)),
602
+ align: (0, _columns.resolveColumnAlign)(col),
603
+ className: (0, _utils.cn)((0, _styles.dataGridCellVariants)(), pinnedClassName(pin, false)),
604
+ style: mergePinnedStyle(cellStyle(col, columnWidths.get(cell.column.id)), pin, pinnedOffsets, cell.column.id, false),
435
605
  children: content
436
606
  }, cell.id);
437
607
  })