argent-grid 0.1.0 → 0.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 (122) hide show
  1. package/.github/workflows/ci.yml +69 -0
  2. package/.github/workflows/pages.yml +6 -12
  3. package/.storybook/main.ts +20 -0
  4. package/.storybook/preview.ts +18 -0
  5. package/.storybook/tsconfig.json +24 -0
  6. package/AGENTS.md +70 -27
  7. package/README.md +51 -34
  8. package/angular.json +66 -0
  9. package/biome.json +66 -0
  10. package/demo-app/e2e/selection-screenshot.spec.ts +20 -0
  11. package/docs/AG-GRID-COMPARISON.md +725 -0
  12. package/docs/CELL-RENDERER-GUIDE.md +241 -0
  13. package/docs/CONTEXT-MENU-GUIDE.md +371 -0
  14. package/docs/LIVE-DATA-OPTIMIZATIONS.md +497 -0
  15. package/docs/PERFORMANCE-OPTIMIZATIONS-PHASE1.md +162 -0
  16. package/docs/PERFORMANCE-REVIEW.md +571 -0
  17. package/docs/RESEARCH-STATUS.md +234 -0
  18. package/docs/STATE-PERSISTENCE-GUIDE.md +370 -0
  19. package/docs/STORYBOOK-REFACTOR.md +215 -0
  20. package/docs/STORYBOOK-STATUS.md +156 -0
  21. package/docs/TEST-COVERAGE-REPORT.md +276 -0
  22. package/docs/THEME-API-GUIDE.md +445 -0
  23. package/docs/THEME-API-PLAN.md +364 -0
  24. package/e2e/advanced.spec.ts +109 -0
  25. package/e2e/argentgrid.spec.ts +65 -0
  26. package/e2e/benchmark.spec.ts +52 -0
  27. package/e2e/cell-renderers.spec.ts +152 -0
  28. package/e2e/debug-streaming.spec.ts +31 -0
  29. package/e2e/dnd.spec.ts +73 -0
  30. package/e2e/screenshots.spec.ts +52 -0
  31. package/e2e/theming.spec.ts +35 -0
  32. package/e2e/visual.spec.ts +112 -0
  33. package/e2e/visual.spec.ts-snapshots/checkbox-renderer-mixed.png +0 -0
  34. package/e2e/visual.spec.ts-snapshots/debug.png +0 -0
  35. package/e2e/visual.spec.ts-snapshots/grid-column-group-headers.png +0 -0
  36. package/e2e/visual.spec.ts-snapshots/grid-default.png +0 -0
  37. package/e2e/visual.spec.ts-snapshots/grid-empty-state.png +0 -0
  38. package/e2e/visual.spec.ts-snapshots/grid-filter-popup.png +0 -0
  39. package/e2e/visual.spec.ts-snapshots/grid-scroll-borders.png +0 -0
  40. package/e2e/visual.spec.ts-snapshots/grid-sidebar-buttons.png +0 -0
  41. package/e2e/visual.spec.ts-snapshots/grid-text-filter.png +0 -0
  42. package/e2e/visual.spec.ts-snapshots/grid-with-selection.png +0 -0
  43. package/e2e/visual.spec.ts-snapshots/rating-renderer-varied.png +0 -0
  44. package/package.json +21 -7
  45. package/plan.md +56 -28
  46. package/playwright.config.ts +38 -0
  47. package/setup-vitest.ts +10 -13
  48. package/src/lib/argent-grid.module.ts +10 -12
  49. package/src/lib/components/argent-grid.component.css +281 -321
  50. package/src/lib/components/argent-grid.component.html +295 -207
  51. package/src/lib/components/argent-grid.component.spec.ts +120 -160
  52. package/src/lib/components/argent-grid.component.ts +1193 -290
  53. package/src/lib/components/argent-grid.regressions.spec.ts +301 -0
  54. package/src/lib/components/argent-grid.selection.spec.ts +132 -0
  55. package/src/lib/components/set-filter/set-filter.component.spec.ts +191 -0
  56. package/src/lib/components/set-filter/set-filter.component.ts +307 -0
  57. package/src/lib/directives/ag-grid-compatibility.directive.ts +16 -26
  58. package/src/lib/directives/click-outside.directive.ts +19 -0
  59. package/src/lib/rendering/canvas-renderer.spec.ts +513 -0
  60. package/src/lib/rendering/canvas-renderer.ts +456 -452
  61. package/src/lib/rendering/live-data-handler.ts +110 -0
  62. package/src/lib/rendering/live-data-optimizations.ts +133 -0
  63. package/src/lib/rendering/render/blit.spec.ts +16 -27
  64. package/src/lib/rendering/render/blit.ts +48 -36
  65. package/src/lib/rendering/render/cells.spec.ts +132 -0
  66. package/src/lib/rendering/render/cells.ts +167 -28
  67. package/src/lib/rendering/render/column-utils.ts +95 -0
  68. package/src/lib/rendering/render/hit-test.ts +50 -0
  69. package/src/lib/rendering/render/index.ts +88 -76
  70. package/src/lib/rendering/render/lines.ts +53 -47
  71. package/src/lib/rendering/render/primitives.ts +423 -0
  72. package/src/lib/rendering/render/theme.spec.ts +8 -12
  73. package/src/lib/rendering/render/theme.ts +7 -10
  74. package/src/lib/rendering/render/types.ts +3 -2
  75. package/src/lib/rendering/render/walk.spec.ts +35 -38
  76. package/src/lib/rendering/render/walk.ts +94 -64
  77. package/src/lib/rendering/utils/damage-tracker.spec.ts +8 -7
  78. package/src/lib/rendering/utils/damage-tracker.ts +6 -18
  79. package/src/lib/rendering/utils/index.ts +1 -1
  80. package/src/lib/services/grid.service.set-filter.spec.ts +219 -0
  81. package/src/lib/services/grid.service.spec.ts +1241 -201
  82. package/src/lib/services/grid.service.ts +1204 -235
  83. package/src/lib/themes/parts/color-schemes.ts +132 -0
  84. package/src/lib/themes/parts/icon-sets.ts +258 -0
  85. package/src/lib/themes/theme-builder.ts +347 -0
  86. package/src/lib/themes/theme-quartz.ts +72 -0
  87. package/src/lib/themes/types.ts +238 -0
  88. package/src/lib/types/ag-grid-types.ts +573 -14
  89. package/src/public-api.ts +39 -9
  90. package/src/stories/Advanced.stories.ts +249 -0
  91. package/src/stories/ArgentGrid.stories.ts +301 -0
  92. package/src/stories/Benchmark.stories.ts +76 -0
  93. package/src/stories/CellRenderers.stories.ts +395 -0
  94. package/src/stories/Filtering.stories.ts +292 -0
  95. package/src/stories/Grouping.stories.ts +290 -0
  96. package/src/stories/Streaming.stories.ts +57 -0
  97. package/src/stories/Theming.stories.ts +137 -0
  98. package/src/stories/Tooltips.stories.ts +381 -0
  99. package/src/stories/benchmark-wrapper.component.ts +355 -0
  100. package/src/stories/story-utils.ts +88 -0
  101. package/src/stories/streaming-wrapper.component.ts +441 -0
  102. package/tsconfig.json +1 -0
  103. package/tsconfig.storybook.json +10 -0
  104. package/vitest.config.ts +9 -9
  105. package/demo-app/README.md +0 -70
  106. package/demo-app/angular.json +0 -78
  107. package/demo-app/e2e/benchmark.spec.ts +0 -53
  108. package/demo-app/e2e/demo-page.spec.ts +0 -77
  109. package/demo-app/e2e/grid-features.spec.ts +0 -269
  110. package/demo-app/package-lock.json +0 -14023
  111. package/demo-app/package.json +0 -36
  112. package/demo-app/playwright-test-menu.js +0 -19
  113. package/demo-app/playwright.config.ts +0 -23
  114. package/demo-app/src/app/app.component.ts +0 -10
  115. package/demo-app/src/app/app.config.ts +0 -13
  116. package/demo-app/src/app/app.routes.ts +0 -7
  117. package/demo-app/src/app/demo-page/demo-page.component.css +0 -313
  118. package/demo-app/src/app/demo-page/demo-page.component.html +0 -124
  119. package/demo-app/src/app/demo-page/demo-page.component.ts +0 -366
  120. package/demo-app/src/index.html +0 -19
  121. package/demo-app/src/main.ts +0 -6
  122. package/demo-app/tsconfig.json +0 -31
@@ -0,0 +1,241 @@
1
+ # Cell Renderer Guide
2
+
3
+ ## Overview
4
+
5
+ ArgentGrid supports custom cell renderers that return **plain text strings**. The renderer function receives cell data and returns a string that is rendered on the Canvas.
6
+
7
+ ---
8
+
9
+ ## ⚠️ Important Limitations
10
+
11
+ **Canvas rendering only supports plain text.** HTML/CSS styling is **NOT** supported.
12
+
13
+ ### ✅ Supported
14
+
15
+ ```typescript
16
+ // Plain text - works perfectly
17
+ cellRenderer: (params) => {
18
+ return params.value;
19
+ }
20
+
21
+ // HTML tags are stripped, text is rendered
22
+ cellRenderer: (params) => {
23
+ return `<span>${params.value}</span>`; // Renders as plain text
24
+ }
25
+ ```
26
+
27
+ ### ❌ NOT Supported
28
+
29
+ ```typescript
30
+ // Colors, backgrounds, borders are IGNORED
31
+ cellRenderer: (params) => {
32
+ return `<span style="color: red; background: yellow">
33
+ ${params.value}
34
+ </span>`;
35
+ // Only text is rendered - no color, no background
36
+ }
37
+
38
+ // Complex HTML layouts are NOT supported
39
+ cellRenderer: (params) => {
40
+ return `<div><strong>${params.value}</strong></div>`;
41
+ // Only text content is rendered
42
+ }
43
+ ```
44
+
45
+ ---
46
+
47
+ ## Usage
48
+
49
+ ### Basic Cell Renderer
50
+
51
+ ```typescript
52
+ columnDefs: [
53
+ {
54
+ field: 'status',
55
+ cellRenderer: (params) => {
56
+ return params.value; // Plain text
57
+ }
58
+ }
59
+ ]
60
+ ```
61
+
62
+ ### Conditional Text
63
+
64
+ ```typescript
65
+ columnDefs: [
66
+ {
67
+ field: 'status',
68
+ cellRenderer: (params) => {
69
+ return params.value === 'active' ? '✓ Active' : '✗ Inactive';
70
+ }
71
+ }
72
+ ]
73
+ ```
74
+
75
+ ### Formatted Values
76
+
77
+ ```typescript
78
+ columnDefs: [
79
+ {
80
+ field: 'salary',
81
+ cellRenderer: (params) => {
82
+ return `$${params.value.toLocaleString()}`;
83
+ }
84
+ }
85
+ ]
86
+ ```
87
+
88
+ ### With HTML Tags (stripped)
89
+
90
+ ```typescript
91
+ columnDefs: [
92
+ {
93
+ field: 'name',
94
+ cellRenderer: (params) => {
95
+ // HTML tags are stripped, but you can use them for semantic purposes
96
+ return `<strong>${params.value}</strong>`; // Renders as plain text
97
+ }
98
+ }
99
+ ]
100
+ ```
101
+
102
+ ---
103
+
104
+ ## Async Cell Renderers (Future)
105
+
106
+ Async renderers (returning `Promise<string>`) are **not yet supported**. The renderer must return a string synchronously.
107
+
108
+ ```typescript
109
+ // NOT YET SUPPORTED
110
+ cellRenderer: async (params) => {
111
+ const data = await fetch(`/api/format/${params.value}`);
112
+ return data.text();
113
+ }
114
+ ```
115
+
116
+ ---
117
+
118
+ ## Alternatives for Styled Cells
119
+
120
+ If you need colored backgrounds, borders, or other styling, use **valueFormatter** instead:
121
+
122
+ ```typescript
123
+ // Note: valueFormatter also returns plain text
124
+ // For styled cells, we recommend:
125
+ // 1. Use different columns for different statuses
126
+ // 2. Use row styling based on data
127
+ // 3. Future: Consider DOM overlay for complex cells (performance trade-off)
128
+ ```
129
+
130
+ ---
131
+
132
+ ## Examples
133
+
134
+ ### Status Badge (Text Only)
135
+
136
+ ```typescript
137
+ {
138
+ field: 'status',
139
+ cellRenderer: (params) => {
140
+ const icon = params.value === 'active' ? '✓' : '✗';
141
+ return `${icon} ${params.value}`;
142
+ }
143
+ }
144
+ ```
145
+
146
+ ### Currency Formatting
147
+
148
+ ```typescript
149
+ {
150
+ field: 'price',
151
+ cellRenderer: (params) => {
152
+ return `$${params.value.toFixed(2)}`;
153
+ }
154
+ }
155
+ ```
156
+
157
+ ### Percentage
158
+
159
+ ```typescript
160
+ {
161
+ field: 'completion',
162
+ cellRenderer: (params) => {
163
+ return `${params.value}%`;
164
+ }
165
+ }
166
+ ```
167
+
168
+ ### Date Formatting
169
+
170
+ ```typescript
171
+ {
172
+ field: 'startDate',
173
+ cellRenderer: (params) => {
174
+ return new Date(params.value).toLocaleDateString();
175
+ }
176
+ }
177
+ ```
178
+
179
+ ### Truncated Text
180
+
181
+ ```typescript
182
+ {
183
+ field: 'description',
184
+ cellRenderer: (params) => {
185
+ const maxLen = 50;
186
+ return params.value.length > maxLen
187
+ ? params.value.substring(0, maxLen) + '...'
188
+ : params.value;
189
+ }
190
+ }
191
+ ```
192
+
193
+ ---
194
+
195
+ ## Performance Notes
196
+
197
+ - Cell renderers are called **for every visible cell** on each render frame
198
+ - Keep renderers **fast and simple**
199
+ - Avoid expensive operations (DOM manipulation, network requests, etc.)
200
+ - Use **valueFormatter** for simple formatting (slightly faster)
201
+
202
+ ---
203
+
204
+ ## Migration from AG Grid
205
+
206
+ AG Grid Community supports the same pattern:
207
+
208
+ ```typescript
209
+ // AG Grid
210
+ {
211
+ field: 'status',
212
+ cellRenderer: (params) => params.value
213
+ }
214
+
215
+ // ArgentGrid (same API)
216
+ {
217
+ field: 'status',
218
+ cellRenderer: (params) => params.value
219
+ }
220
+ ```
221
+
222
+ **Key Difference:** AG Grid DOM-based rendering supports HTML/CSS. ArgentGrid Canvas-based rendering only supports plain text.
223
+
224
+ ---
225
+
226
+ ## Future Enhancements
227
+
228
+ Planned features (not yet implemented):
229
+
230
+ - [ ] Async cell renderers (`Promise<string>`)
231
+ - [ ] Registered renderer names (`cellRenderer: 'myRenderer'`)
232
+ - [ ] cellRendererSelector (choose renderer based on data)
233
+ - [ ] Basic style extraction (color, font-weight)
234
+
235
+ ---
236
+
237
+ ## See Also
238
+
239
+ - [Value Formatter Guide](./VALUE-FORMATTER-GUIDE.md)
240
+ - [Column Definitions](./COLUMN-DEFS.md)
241
+ - [Canvas Renderer Architecture](./CANVAS-RENDERER.md)
@@ -0,0 +1,371 @@
1
+ # Context Menu Guide
2
+
3
+ ## Overview
4
+
5
+ ArgentGrid supports custom context menus that appear on right-click. You can customize the menu items using the `getContextMenuItems` callback in `GridOptions`.
6
+
7
+ ---
8
+
9
+ ## Basic Usage
10
+
11
+ ### Default Context Menu
12
+
13
+ By default, right-clicking a cell shows:
14
+
15
+ - 📋 **Copy Cell** - Copy cell value to clipboard
16
+ - 📋 **Copy with Headers** - Copy range with column headers
17
+ - 📁 **Export** - Export to CSV or Excel
18
+ - ⟲ **Reset Columns** - Restore original column order/widths
19
+
20
+ ```typescript
21
+ gridOptions = {
22
+ // No configuration needed - default menu is automatic
23
+ };
24
+ ```
25
+
26
+ ---
27
+
28
+ ## Custom Context Menu
29
+
30
+ ### Using `getContextMenuItems`
31
+
32
+ ```typescript
33
+ gridOptions = {
34
+ getContextMenuItems: (params) => {
35
+ return [
36
+ 'copy',
37
+ 'copyWithHeaders',
38
+ 'separator',
39
+ {
40
+ name: 'Custom Action',
41
+ action: () => {
42
+ console.log('Custom action!', params.node.data);
43
+ },
44
+ icon: '⭐'
45
+ },
46
+ 'separator',
47
+ 'export',
48
+ 'resetColumns'
49
+ ];
50
+ }
51
+ };
52
+ ```
53
+
54
+ ### Parameters
55
+
56
+ The `getContextMenuItems` callback receives:
57
+
58
+ ```typescript
59
+ interface GetContextMenuItemsParams<TData> {
60
+ node: IRowNode<TData>; // Row node that was clicked
61
+ column: Column; // Column that was clicked
62
+ api: GridApi<TData>; // Grid API for programmatic control
63
+ type: 'cell' | 'header'; // Where the click occurred
64
+ event: MouseEvent; // Original mouse event
65
+ }
66
+ ```
67
+
68
+ ---
69
+
70
+ ## Menu Item Types
71
+
72
+ ### 1. Default Items (String)
73
+
74
+ Use predefined menu items:
75
+
76
+ | Key | Description |
77
+ |-----|-------------|
78
+ | `'copy'` | Copy cell value |
79
+ | `'copyWithHeaders'` | Copy range with headers |
80
+ | `'export'` | Export submenu (CSV/Excel) |
81
+ | `'resetColumns'` | Reset column state |
82
+ | `'separator'` | Visual separator |
83
+
84
+ ```typescript
85
+ getContextMenuItems: (params) => {
86
+ return ['copy', 'separator', 'export'];
87
+ }
88
+ ```
89
+
90
+ ### 2. Custom Items (MenuItemDef)
91
+
92
+ Define custom menu items:
93
+
94
+ ```typescript
95
+ interface MenuItemDef {
96
+ name: string; // Display name
97
+ action: () => void; // Click handler
98
+ icon?: string; // Optional emoji/icon
99
+ disabled?: boolean; // Disable item
100
+ subMenu?: MenuItemDef[]; // Nested submenu
101
+ separator?: boolean; // Separator line
102
+ tooltip?: string; // Hover tooltip
103
+ }
104
+ ```
105
+
106
+ **Example:**
107
+
108
+ ```typescript
109
+ getContextMenuItems: (params) => {
110
+ return [
111
+ 'copy',
112
+ {
113
+ name: 'Delete Row',
114
+ action: () => {
115
+ const rowData = params.node.data;
116
+ console.log('Deleting:', rowData);
117
+ // Your delete logic here
118
+ },
119
+ icon: '🗑️',
120
+ disabled: false,
121
+ tooltip: 'Delete this row'
122
+ }
123
+ ];
124
+ }
125
+ ```
126
+
127
+ ### 3. Submenus
128
+
129
+ Create nested menus:
130
+
131
+ ```typescript
132
+ getContextMenuItems: (params) => {
133
+ return [
134
+ 'copy',
135
+ {
136
+ name: 'Actions',
137
+ subMenu: [
138
+ {
139
+ name: 'Edit',
140
+ action: () => this.editRow(params.node.data)
141
+ },
142
+ {
143
+ name: 'Delete',
144
+ action: () => this.deleteRow(params.node.data)
145
+ }
146
+ ]
147
+ }
148
+ ];
149
+ }
150
+ ```
151
+
152
+ ---
153
+
154
+ ## Examples
155
+
156
+ ### Conditional Menu Items
157
+
158
+ Show different items based on row data:
159
+
160
+ ```typescript
161
+ getContextMenuItems: (params) => {
162
+ const items: (string | MenuItemDef)[] = ['copy'];
163
+
164
+ // Only show delete for certain rows
165
+ if (params.node.data.status === 'inactive') {
166
+ items.push({
167
+ name: 'Delete Inactive Row',
168
+ action: () => this.deleteRow(params.node.data),
169
+ icon: '🗑️'
170
+ });
171
+ }
172
+
173
+ items.push('separator', 'export');
174
+ return items;
175
+ }
176
+ ```
177
+
178
+ ### Column-Specific Menu
179
+
180
+ Different menu for specific columns:
181
+
182
+ ```typescript
183
+ getContextMenuItems: (params) => {
184
+ if (params.column.colId === 'salary') {
185
+ return [
186
+ 'copy',
187
+ {
188
+ name: 'Format as Currency',
189
+ action: () => this.formatAsCurrency(params.node.data)
190
+ },
191
+ 'separator',
192
+ 'export'
193
+ ];
194
+ }
195
+
196
+ return ['copy', 'export'];
197
+ }
198
+ ```
199
+
200
+ ### Copy with Custom Format
201
+
202
+ ```typescript
203
+ getContextMenuItems: (params) => {
204
+ return [
205
+ {
206
+ name: 'Copy Formatted',
207
+ action: () => {
208
+ const value = params.node.data[params.column.field!];
209
+ const formatted = `$${value.toLocaleString()}`;
210
+ navigator.clipboard.writeText(formatted);
211
+ }
212
+ },
213
+ 'copy',
214
+ 'export'
215
+ ];
216
+ }
217
+ ```
218
+
219
+ ### Disable Items Conditionally
220
+
221
+ ```typescript
222
+ getContextMenuItems: (params) => {
223
+ return [
224
+ 'copy',
225
+ {
226
+ name: 'Edit',
227
+ action: () => this.editRow(params.node.data),
228
+ disabled: params.node.data.readOnly === true,
229
+ tooltip: params.node.data.readOnly ? 'Row is read-only' : 'Edit this row'
230
+ }
231
+ ];
232
+ }
233
+ ```
234
+
235
+ ---
236
+
237
+ ## Default Menu Items Reference
238
+
239
+ | Key | Name | Description |
240
+ |-----|------|-------------|
241
+ | `'copy'` | Copy Cell | Copy cell value to clipboard |
242
+ | `'copyWithHeaders'` | Copy with Headers | Copy range with column headers |
243
+ | `'export'` | Export | Submenu with CSV/Excel options |
244
+ | `'resetColumns'` | Reset Columns | Restore original column state |
245
+ | `'separator'` | — | Visual separator line |
246
+
247
+ ---
248
+
249
+ ## Best Practices
250
+
251
+ ### 1. Keep Menus Short
252
+
253
+ ```typescript
254
+ // ✅ Good - 4-6 items max
255
+ getContextMenuItems: (params) => {
256
+ return ['copy', 'separator', 'export', 'resetColumns'];
257
+ }
258
+
259
+ // ❌ Bad - Too many items
260
+ getContextMenuItems: (params) => {
261
+ return [
262
+ 'copy', 'copyWithHeaders', 'export', 'resetColumns',
263
+ 'action1', 'action2', 'action3', 'action4', 'action5'
264
+ ];
265
+ }
266
+ ```
267
+
268
+ ### 2. Use Separators Wisely
269
+
270
+ ```typescript
271
+ // ✅ Good - Logical grouping
272
+ getContextMenuItems: (params) => {
273
+ return [
274
+ 'copy', 'copyWithHeaders', // Copy actions
275
+ 'separator',
276
+ 'export', // Export actions
277
+ 'separator',
278
+ 'resetColumns' // Reset actions
279
+ ];
280
+ }
281
+ ```
282
+
283
+ ### 3. Provide Visual Feedback
284
+
285
+ ```typescript
286
+ // ✅ Good - With icons
287
+ getContextMenuItems: (params) => {
288
+ return [
289
+ { name: 'Copy', action: () => {}, icon: '📋' },
290
+ { name: 'Delete', action: () => {}, icon: '🗑️' },
291
+ { name: 'Edit', action: () => {}, icon: '✏️' }
292
+ ];
293
+ }
294
+ ```
295
+
296
+ ### 4. Handle Errors Gracefully
297
+
298
+ ```typescript
299
+ getContextMenuItems: (params) => {
300
+ return [
301
+ {
302
+ name: 'Delete Row',
303
+ action: () => {
304
+ try {
305
+ this.deleteRow(params.node.data);
306
+ } catch (error) {
307
+ console.error('Delete failed:', error);
308
+ // Show user-friendly error message
309
+ }
310
+ }
311
+ }
312
+ ];
313
+ }
314
+ ```
315
+
316
+ ---
317
+
318
+ ## Keyboard Shortcuts
319
+
320
+ Context menu can be triggered by:
321
+
322
+ - **Right-click** on cell
323
+ - **Shift + F10** (Windows keyboard shortcut)
324
+
325
+ ---
326
+
327
+ ## Accessibility
328
+
329
+ - Context menu items are keyboard navigable
330
+ - **Arrow keys** - Navigate menu items
331
+ - **Enter** - Select item
332
+ - **Escape** - Close menu
333
+
334
+ ---
335
+
336
+ ## Limitations
337
+
338
+ - Context menu is **DOM-based overlay** (not Canvas)
339
+ - Menu items must be defined **synchronously**
340
+ - **Async menu items** not yet supported
341
+ - **Icons** are emoji/text only (no custom images yet)
342
+
343
+ ---
344
+
345
+ ## Migration from AG Grid
346
+
347
+ AG Grid uses the same API:
348
+
349
+ ```typescript
350
+ // AG Grid
351
+ gridOptions = {
352
+ getContextMenuItems: (params) => {
353
+ return ['copy', 'export'];
354
+ }
355
+ }
356
+
357
+ // ArgentGrid (same API)
358
+ gridOptions = {
359
+ getContextMenuItems: (params) => {
360
+ return ['copy', 'export'];
361
+ }
362
+ }
363
+ ```
364
+
365
+ ---
366
+
367
+ ## See Also
368
+
369
+ - [Cell Renderer Guide](./CELL-RENDERER-GUIDE.md)
370
+ - [Value Formatter Guide](./VALUE-FORMATTER-GUIDE.md)
371
+ - [Grid Options Reference](./GRID-OPTIONS.md)