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.
- package/.github/workflows/ci.yml +69 -0
- package/.github/workflows/pages.yml +6 -12
- package/.storybook/main.ts +20 -0
- package/.storybook/preview.ts +18 -0
- package/.storybook/tsconfig.json +24 -0
- package/AGENTS.md +70 -27
- package/README.md +51 -34
- package/angular.json +66 -0
- package/biome.json +66 -0
- package/demo-app/e2e/selection-screenshot.spec.ts +20 -0
- package/docs/AG-GRID-COMPARISON.md +725 -0
- package/docs/CELL-RENDERER-GUIDE.md +241 -0
- package/docs/CONTEXT-MENU-GUIDE.md +371 -0
- package/docs/LIVE-DATA-OPTIMIZATIONS.md +497 -0
- package/docs/PERFORMANCE-OPTIMIZATIONS-PHASE1.md +162 -0
- package/docs/PERFORMANCE-REVIEW.md +571 -0
- package/docs/RESEARCH-STATUS.md +234 -0
- package/docs/STATE-PERSISTENCE-GUIDE.md +370 -0
- package/docs/STORYBOOK-REFACTOR.md +215 -0
- package/docs/STORYBOOK-STATUS.md +156 -0
- package/docs/TEST-COVERAGE-REPORT.md +276 -0
- package/docs/THEME-API-GUIDE.md +445 -0
- package/docs/THEME-API-PLAN.md +364 -0
- package/e2e/advanced.spec.ts +109 -0
- package/e2e/argentgrid.spec.ts +65 -0
- package/e2e/benchmark.spec.ts +52 -0
- package/e2e/cell-renderers.spec.ts +152 -0
- package/e2e/debug-streaming.spec.ts +31 -0
- package/e2e/dnd.spec.ts +73 -0
- package/e2e/screenshots.spec.ts +52 -0
- package/e2e/theming.spec.ts +35 -0
- package/e2e/visual.spec.ts +112 -0
- package/e2e/visual.spec.ts-snapshots/checkbox-renderer-mixed.png +0 -0
- package/e2e/visual.spec.ts-snapshots/debug.png +0 -0
- package/e2e/visual.spec.ts-snapshots/grid-column-group-headers.png +0 -0
- package/e2e/visual.spec.ts-snapshots/grid-default.png +0 -0
- package/e2e/visual.spec.ts-snapshots/grid-empty-state.png +0 -0
- package/e2e/visual.spec.ts-snapshots/grid-filter-popup.png +0 -0
- package/e2e/visual.spec.ts-snapshots/grid-scroll-borders.png +0 -0
- package/e2e/visual.spec.ts-snapshots/grid-sidebar-buttons.png +0 -0
- package/e2e/visual.spec.ts-snapshots/grid-text-filter.png +0 -0
- package/e2e/visual.spec.ts-snapshots/grid-with-selection.png +0 -0
- package/e2e/visual.spec.ts-snapshots/rating-renderer-varied.png +0 -0
- package/package.json +21 -7
- package/plan.md +56 -28
- package/playwright.config.ts +38 -0
- package/setup-vitest.ts +10 -13
- package/src/lib/argent-grid.module.ts +10 -12
- package/src/lib/components/argent-grid.component.css +281 -321
- package/src/lib/components/argent-grid.component.html +295 -207
- package/src/lib/components/argent-grid.component.spec.ts +120 -160
- package/src/lib/components/argent-grid.component.ts +1193 -290
- package/src/lib/components/argent-grid.regressions.spec.ts +301 -0
- package/src/lib/components/argent-grid.selection.spec.ts +132 -0
- package/src/lib/components/set-filter/set-filter.component.spec.ts +191 -0
- package/src/lib/components/set-filter/set-filter.component.ts +307 -0
- package/src/lib/directives/ag-grid-compatibility.directive.ts +16 -26
- package/src/lib/directives/click-outside.directive.ts +19 -0
- package/src/lib/rendering/canvas-renderer.spec.ts +513 -0
- package/src/lib/rendering/canvas-renderer.ts +456 -452
- package/src/lib/rendering/live-data-handler.ts +110 -0
- package/src/lib/rendering/live-data-optimizations.ts +133 -0
- package/src/lib/rendering/render/blit.spec.ts +16 -27
- package/src/lib/rendering/render/blit.ts +48 -36
- package/src/lib/rendering/render/cells.spec.ts +132 -0
- package/src/lib/rendering/render/cells.ts +167 -28
- package/src/lib/rendering/render/column-utils.ts +95 -0
- package/src/lib/rendering/render/hit-test.ts +50 -0
- package/src/lib/rendering/render/index.ts +88 -76
- package/src/lib/rendering/render/lines.ts +53 -47
- package/src/lib/rendering/render/primitives.ts +423 -0
- package/src/lib/rendering/render/theme.spec.ts +8 -12
- package/src/lib/rendering/render/theme.ts +7 -10
- package/src/lib/rendering/render/types.ts +3 -2
- package/src/lib/rendering/render/walk.spec.ts +35 -38
- package/src/lib/rendering/render/walk.ts +94 -64
- package/src/lib/rendering/utils/damage-tracker.spec.ts +8 -7
- package/src/lib/rendering/utils/damage-tracker.ts +6 -18
- package/src/lib/rendering/utils/index.ts +1 -1
- package/src/lib/services/grid.service.set-filter.spec.ts +219 -0
- package/src/lib/services/grid.service.spec.ts +1241 -201
- package/src/lib/services/grid.service.ts +1204 -235
- package/src/lib/themes/parts/color-schemes.ts +132 -0
- package/src/lib/themes/parts/icon-sets.ts +258 -0
- package/src/lib/themes/theme-builder.ts +347 -0
- package/src/lib/themes/theme-quartz.ts +72 -0
- package/src/lib/themes/types.ts +238 -0
- package/src/lib/types/ag-grid-types.ts +573 -14
- package/src/public-api.ts +39 -9
- package/src/stories/Advanced.stories.ts +249 -0
- package/src/stories/ArgentGrid.stories.ts +301 -0
- package/src/stories/Benchmark.stories.ts +76 -0
- package/src/stories/CellRenderers.stories.ts +395 -0
- package/src/stories/Filtering.stories.ts +292 -0
- package/src/stories/Grouping.stories.ts +290 -0
- package/src/stories/Streaming.stories.ts +57 -0
- package/src/stories/Theming.stories.ts +137 -0
- package/src/stories/Tooltips.stories.ts +381 -0
- package/src/stories/benchmark-wrapper.component.ts +355 -0
- package/src/stories/story-utils.ts +88 -0
- package/src/stories/streaming-wrapper.component.ts +441 -0
- package/tsconfig.json +1 -0
- package/tsconfig.storybook.json +10 -0
- package/vitest.config.ts +9 -9
- package/demo-app/README.md +0 -70
- package/demo-app/angular.json +0 -78
- package/demo-app/e2e/benchmark.spec.ts +0 -53
- package/demo-app/e2e/demo-page.spec.ts +0 -77
- package/demo-app/e2e/grid-features.spec.ts +0 -269
- package/demo-app/package-lock.json +0 -14023
- package/demo-app/package.json +0 -36
- package/demo-app/playwright-test-menu.js +0 -19
- package/demo-app/playwright.config.ts +0 -23
- package/demo-app/src/app/app.component.ts +0 -10
- package/demo-app/src/app/app.config.ts +0 -13
- package/demo-app/src/app/app.routes.ts +0 -7
- package/demo-app/src/app/demo-page/demo-page.component.css +0 -313
- package/demo-app/src/app/demo-page/demo-page.component.html +0 -124
- package/demo-app/src/app/demo-page/demo-page.component.ts +0 -366
- package/demo-app/src/index.html +0 -19
- package/demo-app/src/main.ts +0 -6
- 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)
|