@opencorestack/opengridx 0.1.5 → 0.1.7

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 (130) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +9 -0
  3. package/dist/index.d.ts +6 -0
  4. package/dist/opengridx.css +1 -1
  5. package/dist/opengridx.es.js +319 -319
  6. package/dist/opengridx.es.js.map +1 -1
  7. package/dist/opengridx.umd.js +7 -7
  8. package/dist/opengridx.umd.js.map +1 -1
  9. package/docs/API_REFERENCE.md +177 -0
  10. package/docs/README.md +60 -0
  11. package/docs/components/cell.md +44 -0
  12. package/docs/components/column-group-header.md +57 -0
  13. package/docs/components/column-resize.md +63 -0
  14. package/docs/components/column-visibility.md +61 -0
  15. package/docs/components/datagrid.md +70 -0
  16. package/docs/components/filter-panel.md +26 -0
  17. package/docs/components/header.md +39 -0
  18. package/docs/components/pagination.md +34 -0
  19. package/docs/components/row.md +31 -0
  20. package/docs/components/toolbar.md +40 -0
  21. package/docs/components/tooltip.md +68 -0
  22. package/docs/customization/slots-api.md +227 -0
  23. package/docs/customization/theming.md +293 -0
  24. package/docs/features/aggregation-pivot.md +79 -0
  25. package/docs/features/cell-spanning.md +520 -0
  26. package/docs/features/clipboard.md +76 -0
  27. package/docs/features/custom-pagination.md +143 -0
  28. package/docs/features/data-source.md +72 -0
  29. package/docs/features/editing-reordering.md +99 -0
  30. package/docs/features/export-guide.md +434 -0
  31. package/docs/features/filtering.md +83 -0
  32. package/docs/features/infinite-scroll.md +87 -0
  33. package/docs/features/list-view.md +65 -0
  34. package/docs/features/loading-states.md +68 -0
  35. package/docs/features/master-detail.md +46 -0
  36. package/docs/features/pinning.md +67 -0
  37. package/docs/features/selection.md +70 -0
  38. package/docs/features/sorting-pagination.md +66 -0
  39. package/docs/features/state-persistence.md +67 -0
  40. package/docs/features/tree-data-grouping.md +67 -0
  41. package/docs/features/virtualization.md +188 -0
  42. package/docs/roadmap.md +71 -0
  43. package/lib/components/Cell/Cell.css +119 -0
  44. package/lib/components/Cell/Cell.tsx +264 -0
  45. package/lib/components/Cell/GridEditInputCell.css +62 -0
  46. package/lib/components/Cell/GridEditInputCell.tsx +152 -0
  47. package/lib/components/ColumnGroupHeader/ColumnGroupHeader.css +67 -0
  48. package/lib/components/ColumnGroupHeader/ColumnGroupHeader.tsx +220 -0
  49. package/lib/components/ColumnResizeHandle/ColumnResizeHandle.css +31 -0
  50. package/lib/components/ColumnResizeHandle/ColumnResizeHandle.tsx +85 -0
  51. package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.css +137 -0
  52. package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.tsx +190 -0
  53. package/lib/components/DataGrid/DataGrid.css +224 -0
  54. package/lib/components/DataGrid/DataGrid.tsx +2253 -0
  55. package/lib/components/DetailPanel/DetailPanel.css +36 -0
  56. package/lib/components/DetailPanel/DetailPanel.tsx +34 -0
  57. package/lib/components/FilterPanel/FilterPanel.css +196 -0
  58. package/lib/components/FilterPanel/FilterPanel.tsx +183 -0
  59. package/lib/components/FilterPanel/index.ts +1 -0
  60. package/lib/components/Header/ColumnMenu.css +68 -0
  61. package/lib/components/Header/ColumnMenu.tsx +249 -0
  62. package/lib/components/Header/Header.css +224 -0
  63. package/lib/components/Header/Header.tsx +516 -0
  64. package/lib/components/ListView/ListView.css +112 -0
  65. package/lib/components/ListView/ListViewRow.tsx +63 -0
  66. package/lib/components/Pagination/Pagination.css +91 -0
  67. package/lib/components/Pagination/Pagination.tsx +151 -0
  68. package/lib/components/QuickFilter/QuickFilter.css +23 -0
  69. package/lib/components/QuickFilter/QuickFilter.tsx +79 -0
  70. package/lib/components/Row/Row.css +126 -0
  71. package/lib/components/Row/Row.tsx +377 -0
  72. package/lib/components/Skeleton/Skeleton.css +55 -0
  73. package/lib/components/Skeleton/Skeleton.tsx +22 -0
  74. package/lib/components/SkeletonRow/SkeletonRow.css +32 -0
  75. package/lib/components/SkeletonRow/SkeletonRow.tsx +89 -0
  76. package/lib/components/SkeletonRow/index.ts +1 -0
  77. package/lib/components/Toolbar/GlobalSearch.css +113 -0
  78. package/lib/components/Toolbar/GlobalSearch.tsx +120 -0
  79. package/lib/components/Toolbar/GridToolbar.css +204 -0
  80. package/lib/components/Toolbar/GridToolbar.tsx +647 -0
  81. package/lib/components/Toolbar/PivotPanel.css +212 -0
  82. package/lib/components/Toolbar/PivotPanel.tsx +239 -0
  83. package/lib/components/Tooltip/Tooltip.css +59 -0
  84. package/lib/components/Tooltip/Tooltip.tsx +106 -0
  85. package/lib/components/ui/Button.css +89 -0
  86. package/lib/components/ui/Button.tsx +39 -0
  87. package/lib/components/ui/Checkbox.css +71 -0
  88. package/lib/components/ui/Checkbox.tsx +66 -0
  89. package/lib/components/ui/DragHandleIcon.css +19 -0
  90. package/lib/components/ui/DragHandleIcon.tsx +18 -0
  91. package/lib/components/ui/ExpandIcon.css +38 -0
  92. package/lib/components/ui/ExpandIcon.tsx +75 -0
  93. package/lib/components/ui/Input.css +59 -0
  94. package/lib/components/ui/Input.tsx +45 -0
  95. package/lib/components/ui/index.ts +15 -0
  96. package/lib/hooks/core/useDataGrid.ts +392 -0
  97. package/lib/hooks/core/useGridApiRef.ts +10 -0
  98. package/lib/hooks/features/useAggregation.ts +143 -0
  99. package/lib/hooks/features/useGridClipboard.ts +133 -0
  100. package/lib/hooks/features/useGridDataSource.ts +190 -0
  101. package/lib/hooks/features/useGridEditing.ts +100 -0
  102. package/lib/hooks/features/useGridSpanning.ts +349 -0
  103. package/lib/hooks/features/usePivot.ts +149 -0
  104. package/lib/hooks/useColumnReorder.ts +114 -0
  105. package/lib/hooks/useRowGrouping.ts +302 -0
  106. package/lib/hooks/useRowReorder.ts +83 -0
  107. package/lib/hooks/useTreeData.ts +323 -0
  108. package/lib/index.ts +106 -0
  109. package/lib/state/index.ts +17 -0
  110. package/lib/state/types.ts +48 -0
  111. package/lib/state/useGridStateStorage.ts +113 -0
  112. package/lib/styles/base/reset.css +21 -0
  113. package/lib/styles/base/variables.css +161 -0
  114. package/lib/styles/opengridx.css +27 -0
  115. package/lib/styles/opengridx.css.d.ts +4 -0
  116. package/lib/theme/DataGridThemeProvider.tsx +276 -0
  117. package/lib/theme/defaultStyles.ts +0 -0
  118. package/lib/theme/index.ts +21 -0
  119. package/lib/theme/presets.ts +148 -0
  120. package/lib/theme/types.ts +155 -0
  121. package/lib/types/index.ts +764 -0
  122. package/lib/utils/export/exportToExcelAdvanced.ts +516 -0
  123. package/lib/utils/export/index.ts +550 -0
  124. package/lib/utils/filtering/index.ts +240 -0
  125. package/lib/utils/pinning/index.ts +282 -0
  126. package/lib/utils/sorting/index.ts +55 -0
  127. package/lib/utils/virtualization/index.ts +106 -0
  128. package/lib/vite-env.d.ts +1 -0
  129. package/llms.txt +12 -2
  130. package/package.json +13 -4
@@ -0,0 +1,520 @@
1
+ # Cell Spanning - OpenGridX
2
+
3
+ ## Overview
4
+
5
+ Cell spanning allows cells in the DataGrid to span across multiple columns (column spanning) or multiple rows (row spanning). This is useful for creating complex table layouts, summary rows, merged headers, and hierarchical data presentations.
6
+
7
+ ## Features
8
+
9
+ - **Column Spanning**: Cells can span horizontally across multiple columns
10
+ - **Row Spanning**: Cells can span vertically across multiple rows
11
+ - **Dynamic Spanning**: Span values can be calculated dynamically based on cell data
12
+ - **Static Spanning**: Span values can be set as fixed numbers
13
+ - **Accessibility**: Automatic ARIA attributes (`aria-colspan`, `aria-rowspan`)
14
+ - **CSS Grid Integration**: Uses CSS Grid for proper layout
15
+
16
+ ---
17
+
18
+ ## ⚠️ Important: Feature Conflicts
19
+
20
+ When using `colSpan` or `rowSpan`, some other DataGrid features may not work as expected or may create a confusing grid layout. **To avoid layout issues and unexpected behavior, consider disabling the following features for any columns that use cell spanning:**
21
+
22
+ ### Features to Disable
23
+
24
+ 1. **Sorting** (`sortable: false`)
25
+ - When rows are reordered by sorting, spanned cells may break visual continuity
26
+ - Subtotal/summary rows with spanning should remain in fixed positions
27
+
28
+ 2. **Filtering** (when implemented)
29
+ - Hidden rows can cause spanned cells to appear disconnected or incomplete
30
+ - Filtering may hide parts of a spanned cell group
31
+
32
+ 3. **Column Reordering** (`disableReorder: true`)
33
+ - Moving columns can break the spanning logic
34
+ - Spanned cells depend on specific column order
35
+
36
+ 4. **Column Hiding** (`hideable: false`)
37
+ - Hiding columns within a span creates layout gaps and visual artifacts
38
+ - The grid cannot properly render partial spans
39
+
40
+ 5. **Column Pinning** (avoid pinning spanned columns)
41
+ - Pinned columns with spanning can cause rendering conflicts
42
+ - Spanning across pinned and non-pinned columns is not supported
43
+
44
+ ### Recommended Configuration
45
+
46
+ ```tsx
47
+ const columns: GridColDef<Row>[] = [
48
+ {
49
+ field: 'summary',
50
+ headerName: 'Summary',
51
+ width: 200,
52
+ colSpan: (params) => params.row.isTotal ? 3 : 1,
53
+ // Disable conflicting features
54
+ sortable: false, // ✅ Disable sorting
55
+ hideable: false, // ✅ Disable column hiding
56
+ disableReorder: true, // ✅ Disable column reordering
57
+ // Note: Avoid pinning this column
58
+ renderCell: (params) => {
59
+ if (params.row.isTotal) {
60
+ return <strong>Total: {params.value}</strong>;
61
+ }
62
+ return params.value;
63
+ }
64
+ },
65
+ // Other columns...
66
+ ];
67
+ ```
68
+
69
+ ### Why These Limitations Exist
70
+
71
+ Cell spanning fundamentally changes the grid's layout model:
72
+ - **Sorting/Filtering** change row order/visibility, breaking span relationships
73
+ - **Column operations** (reorder/hide/pin) modify the column structure that spans depend on
74
+ - **CSS Grid** requires stable column positions for proper spanning
75
+
76
+ **Best Practice**: Use cell spanning primarily for static summary rows, headers, or fixed layout sections where these features are not needed.
77
+
78
+ ---
79
+
80
+ ## Column Spanning
81
+
82
+ ### Basic Usage
83
+
84
+ ```tsx
85
+ import { DataGrid, GridColDef } from '@opencorestack/opengridx';
86
+
87
+ interface Row {
88
+ id: number;
89
+ name: string;
90
+ value: number;
91
+ isTotal?: boolean;
92
+ }
93
+
94
+ const columns: GridColDef<Row>[] = [
95
+ {
96
+ field: 'name',
97
+ headerName: 'Name',
98
+ width: 200,
99
+ // Static column span
100
+ colSpan: 2 // This cell will span 2 columns
101
+ },
102
+ {
103
+ field: 'value',
104
+ headerName: 'Value',
105
+ width: 150
106
+ }
107
+ ];
108
+ ```
109
+
110
+ ### Dynamic Column Spanning
111
+
112
+ ```tsx
113
+ const columns: GridColDef<Row>[] = [
114
+ {
115
+ field: 'name',
116
+ headerName: 'Name',
117
+ width: 200,
118
+ // Dynamic column span based on row data
119
+ colSpan: (params) => {
120
+ // Span across both columns for total rows
121
+ if (params.row.isTotal) {
122
+ return 2;
123
+ }
124
+ return 1; // Normal span for regular rows
125
+ },
126
+ renderCell: (params) => {
127
+ if (params.row.isTotal) {
128
+ return <strong>Total: {params.row.name}</strong>;
129
+ }
130
+ return params.value;
131
+ }
132
+ },
133
+ {
134
+ field: 'value',
135
+ headerName: 'Value',
136
+ width: 150
137
+ }
138
+ ];
139
+ ```
140
+
141
+ ---
142
+
143
+ ## Row Spanning
144
+
145
+ ### Basic Usage
146
+
147
+ ```tsx
148
+ const columns: GridColDef<Row>[] = [
149
+ {
150
+ field: 'category',
151
+ headerName: 'Category',
152
+ width: 150,
153
+ // Static row span
154
+ rowSpan: 3 // This cell will span 3 rows
155
+ },
156
+ {
157
+ field: 'item',
158
+ headerName: 'Item',
159
+ width: 200
160
+ }
161
+ ];
162
+ ```
163
+
164
+ ### Dynamic Row Spanning
165
+
166
+ ```tsx
167
+ const columns: GridColDef<Row>[] = [
168
+ {
169
+ field: 'category',
170
+ headerName: 'Category',
171
+ width: 150,
172
+ // Dynamic row span based on data
173
+ rowSpan: (params) => {
174
+ // Calculate how many rows this category spans
175
+ const categoryRows = rows.filter(
176
+ r => r.category === params.row.category
177
+ );
178
+ return categoryRows.length;
179
+ }
180
+ },
181
+ {
182
+ field: 'item',
183
+ headerName: 'Item',
184
+ width: 200
185
+ }
186
+ ];
187
+ ```
188
+
189
+ ---
190
+
191
+ ## Combined Column and Row Spanning
192
+
193
+ You can use both `colSpan` and `rowSpan` together:
194
+
195
+ ```tsx
196
+ const columns: GridColDef<Row>[] = [
197
+ {
198
+ field: 'header',
199
+ headerName: 'Header',
200
+ width: 200,
201
+ colSpan: (params) => params.row.isHeader ? 3 : 1,
202
+ rowSpan: (params) => params.row.isHeader ? 2 : 1,
203
+ renderCell: (params) => {
204
+ if (params.row.isHeader) {
205
+ return (
206
+ <div style={{
207
+ fontSize: '1.2em',
208
+ fontWeight: 'bold',
209
+ textAlign: 'center',
210
+ padding: '20px'
211
+ }}>
212
+ {params.value}
213
+ </div>
214
+ );
215
+ }
216
+ return params.value;
217
+ }
218
+ }
219
+ ];
220
+ ```
221
+
222
+ ---
223
+
224
+ ## Complete Example: Sales Report with Subtotals
225
+
226
+ ```tsx
227
+ import { useState } from 'react';
228
+ import { DataGrid, GridColDef } from '@opencorestack/opengridx';
229
+
230
+ interface SalesData {
231
+ id: number;
232
+ region: string;
233
+ product: string;
234
+ q1: number;
235
+ q2: number;
236
+ q3: number;
237
+ q4: number;
238
+ total?: number;
239
+ isSubtotal?: boolean;
240
+ isGrandTotal?: boolean;
241
+ }
242
+
243
+ export default function SalesReport() {
244
+ const [rows] = useState<SalesData[]>([
245
+ // Regular rows
246
+ { id: 1, region: 'North', product: 'Laptops', q1: 45000, q2: 52000, q3: 48000, q4: 55000 },
247
+ { id: 2, region: 'North', product: 'Tablets', q1: 23000, q2: 28000, q3: 25000, q4: 30000 },
248
+
249
+ // Subtotal row - Region column spans across Region and Product
250
+ {
251
+ id: 3,
252
+ region: 'North',
253
+ product: '',
254
+ q1: 68000,
255
+ q2: 80000,
256
+ q3: 73000,
257
+ q4: 85000,
258
+ total: 306000,
259
+ isSubtotal: true
260
+ },
261
+
262
+ // Grand total row
263
+ {
264
+ id: 4,
265
+ region: '',
266
+ product: '',
267
+ q1: 68000,
268
+ q2: 80000,
269
+ q3: 73000,
270
+ q4: 85000,
271
+ total: 306000,
272
+ isGrandTotal: true
273
+ }
274
+ ]);
275
+
276
+ const columns: GridColDef<SalesData>[] = [
277
+ {
278
+ field: 'region',
279
+ headerName: 'Region',
280
+ width: 150,
281
+ // Span across Region and Product columns for subtotal/total rows
282
+ colSpan: (params) => {
283
+ if (params.row.isSubtotal || params.row.isGrandTotal) {
284
+ return 2;
285
+ }
286
+ return 1;
287
+ },
288
+ renderCell: (params) => {
289
+ if (params.row.isGrandTotal) {
290
+ return <strong style={{ fontSize: '1.1em' }}>Grand Total</strong>;
291
+ }
292
+ if (params.row.isSubtotal) {
293
+ return <em>{params.row.region} Subtotal</em>;
294
+ }
295
+ return params.value;
296
+ }
297
+ },
298
+ {
299
+ field: 'product',
300
+ headerName: 'Product',
301
+ width: 150
302
+ },
303
+ {
304
+ field: 'q1',
305
+ headerName: 'Q1',
306
+ width: 120,
307
+ type: 'number',
308
+ valueFormatter: ({ value }) => `$${value.toLocaleString()}`
309
+ },
310
+ {
311
+ field: 'q2',
312
+ headerName: 'Q2',
313
+ width: 120,
314
+ type: 'number',
315
+ valueFormatter: ({ value }) => `$${value.toLocaleString()}`
316
+ },
317
+ {
318
+ field: 'q3',
319
+ headerName: 'Q3',
320
+ width: 120,
321
+ type: 'number',
322
+ valueFormatter: ({ value }) => `$${value.toLocaleString()}`
323
+ },
324
+ {
325
+ field: 'q4',
326
+ headerName: 'Q4',
327
+ width: 120,
328
+ type: 'number',
329
+ valueFormatter: ({ value }) => `$${value.toLocaleString()}`
330
+ },
331
+ {
332
+ field: 'total',
333
+ headerName: 'Total',
334
+ width: 140,
335
+ type: 'number',
336
+ valueFormatter: ({ value }) => value ? `$${value.toLocaleString()}` : ''
337
+ }
338
+ ];
339
+
340
+ return (
341
+ <div style={{ height: 600, width: '100%' }}>
342
+ <DataGrid
343
+ rows={rows}
344
+ columns={columns}
345
+ rowHeight={48}
346
+ headerHeight={56}
347
+ />
348
+ </div>
349
+ );
350
+ }
351
+ ```
352
+
353
+ ---
354
+
355
+ ## API Reference
356
+
357
+ ### GridColDef Properties
358
+
359
+ #### `colSpan`
360
+
361
+ Defines how many columns a cell should span.
362
+
363
+ **Type**: `number | ((params: GridRenderCellParams<R>) => number)`
364
+
365
+ **Default**: `undefined` (no spanning)
366
+
367
+ **Examples**:
368
+ ```tsx
369
+ // Static spanning
370
+ colSpan: 2
371
+
372
+ // Dynamic spanning
373
+ colSpan: (params) => params.row.isTotal ? 3 : 1
374
+ ```
375
+
376
+ #### `rowSpan`
377
+
378
+ Defines how many rows a cell should span.
379
+
380
+ **Type**: `number | ((params: GridRenderCellParams<R>) => number)`
381
+
382
+ **Default**: `undefined` (no spanning)
383
+
384
+ **Examples**:
385
+ ```tsx
386
+ // Static spanning
387
+ rowSpan: 2
388
+
389
+ // Dynamic spanning
390
+ rowSpan: (params) => params.row.categorySize || 1
391
+ ```
392
+
393
+ ### GridRenderCellParams
394
+
395
+ Parameters passed to the `colSpan` and `rowSpan` functions:
396
+
397
+ ```tsx
398
+ interface GridRenderCellParams<R> {
399
+ value: any; // Cell value
400
+ row: R; // Complete row data
401
+ field: string; // Column field name
402
+ colDef: GridColDef<R>; // Column definition
403
+ rowIndex: number; // Row index
404
+ colIndex: number; // Column index
405
+ }
406
+ ```
407
+
408
+ ---
409
+
410
+ ## Styling Spanned Cells
411
+
412
+ Spanned cells can be styled using custom `renderCell`:
413
+
414
+ ```tsx
415
+ {
416
+ field: 'name',
417
+ headerName: 'Name',
418
+ colSpan: (params) => params.row.isHeader ? 3 : 1,
419
+ renderCell: (params) => {
420
+ if (params.row.isHeader) {
421
+ return (
422
+ <div style={{
423
+ width: '100%',
424
+ height: '100%',
425
+ display: 'flex',
426
+ alignItems: 'center',
427
+ justifyContent: 'center',
428
+ backgroundColor: '#e3f2fd',
429
+ fontWeight: 'bold',
430
+ fontSize: '1.2em',
431
+ borderBottom: '2px solid #1976d2'
432
+ }}>
433
+ {params.value}
434
+ </div>
435
+ );
436
+ }
437
+ return params.value;
438
+ }
439
+ }
440
+ ```
441
+
442
+ ---
443
+
444
+ ## Accessibility
445
+
446
+ The DataGrid automatically adds ARIA attributes for spanned cells:
447
+
448
+ - `aria-colspan`: Added when `colSpan > 1`
449
+ - `aria-rowspan`: Added when `rowSpan > 1`
450
+
451
+ This ensures screen readers properly announce the spanning behavior.
452
+
453
+ ---
454
+
455
+ ## Best Practices
456
+
457
+ 1. **Use Dynamic Spanning**: Prefer dynamic spanning functions over static values for flexibility
458
+ 2. **Combine with renderCell**: Always provide custom rendering for spanned cells to improve visual clarity
459
+ 3. **Consider Performance**: Spanning calculations run on every render, so keep them lightweight
460
+ 4. **Test with Virtualization**: Ensure spanning works correctly with virtual scrolling
461
+ 5. **Accessibility**: Provide meaningful content in spanned cells for screen readers
462
+
463
+ ---
464
+
465
+ ## Limitations
466
+
467
+ ### Feature Conflicts
468
+
469
+ **⚠️ See the [Feature Conflicts](#️-important-feature-conflicts) section above for important information about which features should be disabled when using cell spanning.**
470
+
471
+ ### Technical Limitations
472
+
473
+ 1. **Virtualization**:
474
+ - Row spanning may have visual artifacts with virtualization if spans cross virtual boundaries
475
+ - Large row spans (>10 rows) may cause performance issues with virtual scrolling
476
+ - Consider using row pinning for summary rows instead of row spanning
477
+
478
+ 2. **Pinned Columns**:
479
+ - Column spanning with pinned columns requires careful consideration
480
+ - Spanning across pinned and non-pinned columns is not supported
481
+ - Use spanning only within pinned or non-pinned column groups
482
+
483
+ 3. **Sorting/Filtering**:
484
+ - Spanned cells may break visual continuity when rows are reordered
485
+ - Always disable sorting on columns with dynamic spanning
486
+ - Filtering can hide parts of spanned cell groups
487
+
488
+ 4. **Column Operations**:
489
+ - Column reordering can break spanning logic
490
+ - Hiding columns within a span creates layout gaps
491
+ - Column resizing may not work optimally with spanned cells
492
+
493
+ 5. **Editing**:
494
+ - Cell editing on spanned cells may have unexpected behavior
495
+ - Consider disabling editing for columns with spanning
496
+
497
+ ### Workarounds
498
+
499
+ - **For Summary Rows**: Use [Row Pinning](./pinning.md) with pinned bottom rows instead of row spanning
500
+ - **For Headers**: Consider using column grouping instead of cell spanning
501
+ - **For Hierarchical Data**: Use [Tree Data](./tree-data-grouping.md) instead of row spanning
502
+
503
+ ---
504
+
505
+ ## Browser Support
506
+
507
+ Cell spanning uses CSS Grid, which is supported in:
508
+ - Chrome 57+
509
+ - Firefox 52+
510
+ - Safari 10.1+
511
+ - Edge 16+
512
+
513
+ ---
514
+
515
+ ## See Also
516
+
517
+ - [Row Pinning](./pinning.md)
518
+ - [Column Pinning](./pinning.md)
519
+ - [Custom Cell Rendering](./cell.md)
520
+ - [Aggregation](./aggregation-pivot.md)
@@ -0,0 +1,76 @@
1
+ # 📋 Clipboard & Copy/Paste
2
+
3
+ OpenGridX supports copying grid data directly to the clipboard. This feature allows users to effortlessly move data from the grid to external applications like Microsoft Excel, Google Sheets, or text editors.
4
+
5
+ ## 📑 Overview
6
+ - **Zero Configuration**: Enabled by default with the standard `DataGrid`.
7
+ - **Selected Rows**: Only selected rows are copied when selection is active.
8
+ - **TSV Format**: Data is copied in Tab-Separated Values (TSV) format for maximum compatibility.
9
+ - **Value Formatters**: Respects `valueFormatter` for accurate data representation.
10
+
11
+ ---
12
+
13
+ ## ⌨️ Keyboard Shortcuts
14
+ Selection and copying follow standard spreadsheet metaphors:
15
+ | Shortcut | Action |
16
+ | :--- | :--- |
17
+ | `Ctrl + C` / `Cmd + C` | Copy currently selected rows to the clipboard. |
18
+
19
+ > **Note**: The copy action is ignored if an input, textarea, or another editable element currently has focus.
20
+
21
+ ---
22
+
23
+ ## 🛠️ Usage
24
+
25
+ ### Auto-Integration
26
+ If `checkboxSelection` is enabled and rows are selected, pressing `Ctrl + C` will copy those rows.
27
+
28
+ ```tsx
29
+ <DataGrid
30
+ rows={rows}
31
+ columns={columns}
32
+ checkboxSelection
33
+ />
34
+ ```
35
+
36
+ ### Programmatic Copy
37
+ You can trigger a copy action programmatically using the exported hook if building a custom toolbar.
38
+
39
+ ```tsx
40
+ import { useGridClipboard } from '@opencorestack/opengridx';
41
+
42
+ function MyCustomToolbar({ state, columns, getVisibleRows }) {
43
+ const { copySelectedRows } = useGridClipboard({ state, columns, getVisibleRows });
44
+
45
+ return (
46
+ <button onClick={copySelectedRows}>
47
+ Copy to Clipboard
48
+ </button>
49
+ );
50
+ }
51
+ ```
52
+
53
+ ---
54
+
55
+ ## ⚙️ How it Works
56
+
57
+ 1. **Header Row**: The first line of the copied content includes the column header names.
58
+ 2. **Data Transformation**: The grid iterates through the selected rows and columns:
59
+ - **Exclusions**: Special internal columns (checkboxes, expanders, etc.) are excluded.
60
+ - **Formatting**: If a column has a `valueFormatter`, it is applied to the raw value before copying.
61
+ 3. **TSV Generation**: Fields are joined by tabs (`\t`), and rows are joined by newlines (`\n`).
62
+ 4. **Clipboard API**: Uses the modern `navigator.clipboard.writeText` API.
63
+
64
+ ---
65
+
66
+ ## 📊 Example Output
67
+ If you copy a grid with three rows, the clipboard content will look like this:
68
+
69
+ ```text
70
+ ID Name Email Status
71
+ 1 Jon Snow jon@winterfell.com Active
72
+ 2 Cersei Lannister cersei@kingslanding.com Inactive
73
+ 3 Jaime Lannister jaime@kingslanding.com Active
74
+ ```
75
+
76
+ When pasted into Excel or Google Sheets, this content will automatically be distributed into the correct cells.