apexgrid-skill 2.0.0 → 2.1.1
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/.cursorrules +283 -0
- package/SKILL.md +44 -6
- package/index.js +1 -0
- package/install/claude-code.md +19 -0
- package/install/copilot.md +22 -0
- package/install/cursor.md +18 -0
- package/package.json +4 -2
- package/references/columns-and-templates.md +17 -0
- package/references/state-and-features.md +204 -0
package/.cursorrules
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
# Apex Grid — AI Coding Rules
|
|
2
|
+
|
|
3
|
+
You are working with `apex-grid`, a Lit-based web-component data grid by ApexCharts.
|
|
4
|
+
Documentation: https://github.com/apexcharts/apexgrid
|
|
5
|
+
NPM: apex-grid (with a hyphen — no `apexgrid` package exists)
|
|
6
|
+
GitHub: https://github.com/apexcharts/apexgrid
|
|
7
|
+
Confirmed versions: apex-grid@3.0.1, lit@^3.0.0
|
|
8
|
+
|
|
9
|
+
## Quickstart — register + size the host
|
|
10
|
+
|
|
11
|
+
The grid styles itself out of the box — there is **no theme CSS to import**.
|
|
12
|
+
The one real gotcha is host sizing: it virtualizes rows and needs a bounded
|
|
13
|
+
height or it collapses and renders nothing. `setup()` handles both.
|
|
14
|
+
|
|
15
|
+
### 1. Install
|
|
16
|
+
```bash
|
|
17
|
+
npm install apex-grid lit
|
|
18
|
+
```
|
|
19
|
+
`igniteui-webcomponents` installs transitively — you do NOT import theme CSS from it.
|
|
20
|
+
|
|
21
|
+
### 2. Register + size — `setup()` does both
|
|
22
|
+
```ts
|
|
23
|
+
import { setup } from 'apex-grid';
|
|
24
|
+
setup(); // registers <apex-grid> + adopts host sizing. Idempotent.
|
|
25
|
+
```
|
|
26
|
+
Manual alternative: `import 'apex-grid/define'` (or `ApexGrid.register()`) plus your own
|
|
27
|
+
`apex-grid { height: 480px; }`. `setup({ hostStyles: false })` skips the injected sizing.
|
|
28
|
+
|
|
29
|
+
### 3. Style via `--ag-*` CSS variables (no theme import)
|
|
30
|
+
The grid is styled out of the box; retheme by overriding `--ag-*` custom properties on
|
|
31
|
+
`apex-grid`. The deprecated `theme` option / `configureTheme()` does NOT affect appearance.
|
|
32
|
+
```css
|
|
33
|
+
apex-grid { height: 480px; /* + any --ag-* overrides */ }
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Minimal example
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
import { html, render } from 'lit';
|
|
40
|
+
import { setup } from 'apex-grid';
|
|
41
|
+
import type { ColumnConfiguration } from 'apex-grid';
|
|
42
|
+
|
|
43
|
+
setup(); // register + host sizing; no theme import needed
|
|
44
|
+
|
|
45
|
+
type User = { id: number; name: string; age: number; subscribed: boolean };
|
|
46
|
+
|
|
47
|
+
const data: User[] = [
|
|
48
|
+
{ id: 1, name: 'Ada Lovelace', age: 36, subscribed: true },
|
|
49
|
+
{ id: 2, name: 'Carl Sagan', age: 62, subscribed: false },
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
// Explicit widths prevent the last-column-stretches trap.
|
|
53
|
+
const columns: ColumnConfiguration<User>[] = [
|
|
54
|
+
{ key: 'id', type: 'number', width: '80px', sort: true, filter: true },
|
|
55
|
+
{ key: 'name', type: 'string', width: '240px', sort: true, filter: true },
|
|
56
|
+
{ key: 'age', type: 'number', width: '100px', sort: true, filter: true },
|
|
57
|
+
{ key: 'subscribed', type: 'boolean', width: '140px', sort: true, filter: true },
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
render(
|
|
61
|
+
html`<apex-grid .data=${data} .columns=${columns}></apex-grid>`,
|
|
62
|
+
document.getElementById('app')!,
|
|
63
|
+
);
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### What success looks like
|
|
67
|
+
|
|
68
|
+
- Visible borders between rows and columns
|
|
69
|
+
- Sort arrows (↕) next to each column header
|
|
70
|
+
- A filter row below the headers with a "Filter" chip per column
|
|
71
|
+
- Hover state on rows
|
|
72
|
+
- Smooth scrolling; only ~20 `<apex-grid-row>` in the DOM at any time
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Naming
|
|
77
|
+
|
|
78
|
+
| Form | What it is |
|
|
79
|
+
|---|---|
|
|
80
|
+
| `apex-grid` | npm package name (with hyphen) |
|
|
81
|
+
| `<apex-grid>` | the custom-element tag rendered in the DOM |
|
|
82
|
+
| `ApexGrid<T>` | the exported class — used for types and `ApexGrid.register()` |
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Critical Rules
|
|
87
|
+
|
|
88
|
+
1. **Apex Grid is a Lit web component, not a JS class.** Don't `new ApexGrid(...)`. Render `<apex-grid>` and set `.data` / `.columns` as properties.
|
|
89
|
+
2. **Two setup steps:** register the element and give the host a bounded height — `setup()` does both. **No theme CSS import is needed** (the grid self-styles via `--ag-*`).
|
|
90
|
+
3. **`columns` and `data` are properties, not attributes.** Use property binding.
|
|
91
|
+
4. **`column.key` must be a real `keyof T`.** No aliases. Compute derived values in `cellTemplate`.
|
|
92
|
+
5. **Set `type` per column.** Primitive `'string'`/`'number'`/`'boolean'` drive sort/filter/editing (default `'string'`). Presentation types also exist: `'select'`, `'rating'`, `'date'`, `'image'`, `'currency'`, `'avatar'`, `'badge'`, `'progress'`, `'sparkline'`, `'status'`.
|
|
93
|
+
6. **`cellTemplate` and `headerTemplate` must return Lit `TemplateResult`s** (`html\`...\``). Strings render as escaped text.
|
|
94
|
+
7. **Sort/filter events fire only for UI-initiated operations.** Programmatic `grid.sort(...)` / `grid.filter(...)` are silent.
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Column Configuration
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
interface ColumnConfiguration<T, K extends keyof T> {
|
|
102
|
+
key: K; // REQUIRED
|
|
103
|
+
type?: DataType; // default 'string' (13 types — see rule 5)
|
|
104
|
+
headerText?: string; // default: String(key)
|
|
105
|
+
width?: string; // CSS — recommended
|
|
106
|
+
hidden?: boolean;
|
|
107
|
+
resizable?: boolean;
|
|
108
|
+
pinned?: 'start' | 'end' | null; // freeze during horizontal scroll
|
|
109
|
+
reorderable?: boolean; // per-column opt-out of drag reorder
|
|
110
|
+
exportable?: boolean; // default true
|
|
111
|
+
editable?: boolean; // requires grid editing.enabled
|
|
112
|
+
group?: string; // id of a columnGroups spanning header
|
|
113
|
+
validators?: Validator<T, K>[]; // edit-time validation (3.2+)
|
|
114
|
+
sort?: boolean | { caseSensitive?, comparer? };
|
|
115
|
+
filter?: boolean | { caseSensitive? };
|
|
116
|
+
headerTemplate?: (ctx) => TemplateResult;
|
|
117
|
+
cellTemplate?: (ctx) => TemplateResult;
|
|
118
|
+
editorTemplate?: (ctx) => TemplateResult;
|
|
119
|
+
// type-specific: options ('select'), max ('rating'/'progress'), format ('date'),
|
|
120
|
+
// shape/alt ('image'), currency/locale ('currency'), badgeVariant, statusVariant, showDelta
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Applied defaults: `{ type: 'string', resizable: false, hidden: false, sort: false, filter: false, exportable: true }`.
|
|
125
|
+
|
|
126
|
+
### Templates
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
import { html } from 'lit';
|
|
130
|
+
|
|
131
|
+
cellTemplate: ({ value, row, parent, column }) =>
|
|
132
|
+
html`<strong>${value}</strong>`,
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Auto-generation — 3-line demo
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
render(html`<apex-grid auto-generate .data=${data}></apex-grid>`, root);
|
|
139
|
+
// Ignored if `columns` is non-empty. Reset grid.columns = [] before swapping data.
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Filter Operands
|
|
145
|
+
|
|
146
|
+
| Type | Operands |
|
|
147
|
+
|---|---|
|
|
148
|
+
| `'string'` | `contains`, `doesNotContain`, `startsWith`, `endsWith`, `equals`, `doesNotEqual`, `empty`, `notEmpty` |
|
|
149
|
+
| `'number'` | `equals`, `doesNotEqual`, `greaterThan`, `lessThan`, `greaterThanOrEqual`, `lessThanOrEqual`, `empty`, `notEmpty` |
|
|
150
|
+
| `'boolean'` | `all`, `true`, `false`, `empty`, `notEmpty` (all unary — no `searchTerm`) |
|
|
151
|
+
|
|
152
|
+
```ts
|
|
153
|
+
import { StringOperands, NumberOperands, BooleanOperands } from 'apex-grid';
|
|
154
|
+
grid.filter({ key: 'name', condition: 'contains', searchTerm: 'ad' });
|
|
155
|
+
grid.filter({ key: 'subscribed', condition: 'true' }); // unary
|
|
156
|
+
grid.filter([
|
|
157
|
+
{ key: 'age', condition: 'greaterThan', searchTerm: 30 },
|
|
158
|
+
{ key: 'age', condition: 'lessThan', searchTerm: 50, criteria: 'and' },
|
|
159
|
+
]);
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## API
|
|
165
|
+
|
|
166
|
+
| Property | Type | Notes |
|
|
167
|
+
|---|---|---|
|
|
168
|
+
| `data` | `T[]` | |
|
|
169
|
+
| `columns` | `ColumnConfiguration<T>[]` | |
|
|
170
|
+
| `autoGenerate` | `boolean` | Ignored if `columns` non-empty |
|
|
171
|
+
| `sortConfiguration` | `{ multiple, triState }` | Multi-column + tri-state toggles |
|
|
172
|
+
| `dataPipelineConfiguration` | `{ sort?, filter?, pagination?, quickFilter? }` | Server-side hooks |
|
|
173
|
+
| `selection` / `editing` / `pagination` / `tree` / `expansion` | config objects | Opt-in: `{ enabled, ... }` (see SKILL.md §5) |
|
|
174
|
+
| `quickFilter` / `columnReordering` | `string` / `boolean` | Global search / drag-reorder headers |
|
|
175
|
+
| `sortExpressions` / `filterExpressions` | array (get/set) | To clear, use `clearSort()` / `clearFilter()` |
|
|
176
|
+
| `selectedRows` / `expandedRows` | `T[]` (get/set) | Snapshots; set to replace |
|
|
177
|
+
| `rows` (getter) | `ApexGridRow<T>[]` | Currently rendered only (virtualized) |
|
|
178
|
+
| `dataView` / `pageItems` (getters) | `readonly T[]` | Full post-filter/sort / current page |
|
|
179
|
+
| `totalItems` / `pageCount` (getters) | `number` | |
|
|
180
|
+
|
|
181
|
+
| Method | Description |
|
|
182
|
+
|---|---|
|
|
183
|
+
| `sort(expr \| expr[])` | Programmatic. **No events.** |
|
|
184
|
+
| `filter(expr \| expr[])` | Programmatic. **No events.** |
|
|
185
|
+
| `clearSort(key?)` / `clearFilter(key?)` | Reset state. |
|
|
186
|
+
| `getColumn(keyOrIndex)` / `updateColumns(col \| col[])` | Find / merge columns by `key`. |
|
|
187
|
+
| `moveColumn(from, to, position?)` | Reorder within pinning group. |
|
|
188
|
+
| `exportToCSV(opts?)` / `exportAs(formatId, opts?)` | Export data (CSV built in). |
|
|
189
|
+
| `selectRow`/`deselectRow`/`selectAllRows`/`clearSelection`/`isRowSelected` | Selection. |
|
|
190
|
+
| `expandRow`/`collapseRow`/`isRowExpanded` (+ `…TreeRow` variants) | Expansion / tree. |
|
|
191
|
+
| `commitEdit()` / `cancelEdit()` | Row-edit batch (`editing.mode: 'row'`). |
|
|
192
|
+
| `ApexGrid.register()` | Static; idempotent. |
|
|
193
|
+
|
|
194
|
+
Events are **UI-initiated only** (programmatic calls are silent). `*-ing` events are
|
|
195
|
+
cancellable (`preventDefault()`); `*-ed` are notifications. Pairs:
|
|
196
|
+
`sorting/sorted`, `filtering/filtered`, `quickFilterChanging/Changed`, `pageChanging/Changed`,
|
|
197
|
+
`columnPinning/Pinned`, `columnMoving/Moved`, `cellValueChanging/Changed`,
|
|
198
|
+
`rowEditStarted/Ended`, `rowSelecting/Selected`, `rowExpanding/Expanded`, `treeRowExpanding/Expanded`.
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## Server-side data
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
grid.dataPipelineConfiguration = {
|
|
206
|
+
filter: async ({ data, grid }) =>
|
|
207
|
+
fetch('/api/users/filter', {
|
|
208
|
+
method: 'POST',
|
|
209
|
+
body: JSON.stringify(grid.filterExpressions),
|
|
210
|
+
}).then((r) => r.json()),
|
|
211
|
+
};
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Built-in pipeline is skipped for whichever operations have a hook. Omit one to keep it in-memory. Hooks fire for both UI and programmatic operations.
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## Pitfalls — ❌ Wrong vs ✅ Correct
|
|
219
|
+
|
|
220
|
+
### 1. `new ApexGrid(...)`
|
|
221
|
+
❌ `new ApexGrid({ container, columns, data })`
|
|
222
|
+
✅ Render `<apex-grid .data=${data} .columns=${columns}></apex-grid>`
|
|
223
|
+
|
|
224
|
+
### 2. Missing registration
|
|
225
|
+
❌ Importing `ApexGrid` without `register()` — `<apex-grid>` is inert.
|
|
226
|
+
✅ `import 'apex-grid/define'` once at startup.
|
|
227
|
+
|
|
228
|
+
### 3. Importing a theme CSS that no longer exists
|
|
229
|
+
❌ `import 'igniteui-webcomponents/themes/.../bootstrap.css'` + `configureTheme(...)` — obsolete; no effect.
|
|
230
|
+
✅ No import needed — grid self-styles; retheme via `--ag-*`. The only required CSS is host height.
|
|
231
|
+
|
|
232
|
+
### 4. Stringified attribute
|
|
233
|
+
❌ `<apex-grid columns="[...]">`
|
|
234
|
+
✅ `.columns=${columns}` / `:columns.prop=` / `[columns]=` / `el.columns = ...`
|
|
235
|
+
|
|
236
|
+
### 5. `column.key` not a `keyof T`
|
|
237
|
+
❌ `{ key: 'fullName' }` when not in `T`
|
|
238
|
+
✅ `cellTemplate: ({ row }) => html\`${row.firstName} ${row.lastName}\``
|
|
239
|
+
|
|
240
|
+
### 6. String from `cellTemplate`
|
|
241
|
+
❌ `cellTemplate: ({ value }) => \`<b>\${value}</b>\``
|
|
242
|
+
✅ `cellTemplate: ({ value }) => html\`<b>\${value}</b>\``
|
|
243
|
+
|
|
244
|
+
### 7. Numbers sorted as strings
|
|
245
|
+
❌ `{ key: 'age', sort: true }` (default `type: 'string'`)
|
|
246
|
+
✅ `{ key: 'age', type: 'number', sort: true }`
|
|
247
|
+
|
|
248
|
+
### 8. Faking date columns
|
|
249
|
+
❌ `type: 'number'` timestamps or string + custom comparer to handle dates.
|
|
250
|
+
✅ `{ key: 'created', type: 'date', format: 'medium' }` — real type; accepts Date/ISO/timestamp.
|
|
251
|
+
|
|
252
|
+
### 9. Clearing sort/filter
|
|
253
|
+
❌ Rebuilding state by hand to clear it.
|
|
254
|
+
✅ `grid.clearSort()` / `grid.clearFilter()` (optionally per `key`).
|
|
255
|
+
|
|
256
|
+
### 10. Expecting events from programmatic calls
|
|
257
|
+
❌ `grid.addEventListener('sorted'); grid.sort(...)` — silent.
|
|
258
|
+
✅ Wrap the call yourself, or watch `grid.dataView`.
|
|
259
|
+
|
|
260
|
+
### 11. Wrong package name
|
|
261
|
+
❌ `import ... from 'apexgrid'` — no such package.
|
|
262
|
+
✅ `import ... from 'apex-grid'`.
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## New in 3.1–3.3
|
|
267
|
+
|
|
268
|
+
- **Row pinning**: `rowPinning = { enabled }`, `pinRow(row, 'top'|'bottom')`/`unpinRow(row)`, `pinnedRows` getter, `rowPinning`/`rowPinned` events.
|
|
269
|
+
- **Row reordering**: `rowReordering = { enabled, applyToData?, handle? }`, `moveRow(from, to, position?)`, `rowMoving`/`rowMoved` events (clears on sort).
|
|
270
|
+
- **Inline-edit undo/redo**: `editing.history = { enabled, stackSize? }`, `undo()`/`redo()`/`clearHistory()`, `canUndo`/`canRedo`, `historyChanged` event.
|
|
271
|
+
- **Validators**: `column.validators: Validator[]` + `required`/`min`/`max`/`pattern`/`custom` factories, `cellValidationFailed` event.
|
|
272
|
+
- **Column groups**: `columnGroups = [{ id, headerText }]` + `column.group` (members contiguous within a pin region).
|
|
273
|
+
- **State/schema**: `getState()`/`setState()`/`getSchema()`, `rowId`, `stateChanged` event (`setState` is partial + defensive).
|
|
274
|
+
- **Programmatic (these DO emit events, unlike sort/filter)**: `gotoPage`/`nextPage`/`setPageSize`/…, `setQuickFilter()`, `pinColumn`/`unpinColumn`.
|
|
275
|
+
- **Localization**: `localeText` (`GridLocaleText`), `localize()`, `esLocale` export.
|
|
276
|
+
- **Misc**: `columnMenu`/`columnSeparator`; `exportToCSV`/`exportAs` `source: 'view'|'page'|'selected'|'all'`.
|
|
277
|
+
- Full detail: `references/state-and-features.md`.
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## Dev-server reminder
|
|
282
|
+
|
|
283
|
+
ES module imports require an HTTP server — opening with `file://` fails CORS. Use Vite (`npm create vite@latest`) or any static server.
|
package/SKILL.md
CHANGED
|
@@ -12,8 +12,8 @@ description: >
|
|
|
12
12
|
model, and the `dataPipelineConfiguration` hooks for server-side data.
|
|
13
13
|
metadata:
|
|
14
14
|
author: ApexCharts
|
|
15
|
-
version: "2.
|
|
16
|
-
library_version: "3.0
|
|
15
|
+
version: "2.1.0"
|
|
16
|
+
library_version: "3.3.0"
|
|
17
17
|
category: data-visualization
|
|
18
18
|
tags: [grid, data-grid, table, web-component, lit, apex-grid]
|
|
19
19
|
docs: https://github.com/apexcharts/apexgrid
|
|
@@ -165,9 +165,11 @@ interface ColumnConfiguration<T extends object, K extends keyof T = keyof T> {
|
|
|
165
165
|
pinned?: 'start' | 'end' | null; // freeze to a side during horizontal scroll
|
|
166
166
|
reorderable?: boolean; // per-column opt-out of drag reorder
|
|
167
167
|
exportable?: boolean; // default true; false omits from CSV export
|
|
168
|
+
group?: string; // id of a columnGroups spanning header (§5)
|
|
168
169
|
sort?: boolean | { caseSensitive?: boolean; comparer?: (a: T[K], b: T[K]) => number };
|
|
169
170
|
filter?: boolean | { caseSensitive?: boolean };
|
|
170
171
|
editable?: boolean; // requires grid `editing.enabled`
|
|
172
|
+
validators?: Validator<T, K>[]; // declarative edit-time validation (§5)
|
|
171
173
|
headerTemplate?: (ctx: ApexHeaderContext<T>) => TemplateResult | unknown;
|
|
172
174
|
cellTemplate?: (ctx: ApexCellContext<T, K>) => TemplateResult | unknown;
|
|
173
175
|
editorTemplate?: (ctx: ApexEditorContext<T, K>) => TemplateResult | unknown;
|
|
@@ -276,6 +278,16 @@ grid.sort({ key: 'name', direction: 'ascending' }); // programmatic (silen
|
|
|
276
278
|
| `quickFilter` (attr `quick-filter`) | `string` | Global substring search across visible columns. |
|
|
277
279
|
| `showQuickFilter` / `showExport` | `boolean` | Toolbar UI toggles for quick-filter input / export menu. |
|
|
278
280
|
| `columnReordering` (attr `column-reordering`) | `boolean` | Drag-reorder headers; per-column opt-out via `reorderable`. |
|
|
281
|
+
| `columnMenu` | `boolean` | Header kebab-menu button. Default `true`; set `false` to hide. |
|
|
282
|
+
| `columnSeparator` (attr `column-separator`) | `boolean` | Persistent header dividers (also the resize handle). Default `true`; theme via `--ag-header-separator`. |
|
|
283
|
+
| `columnGroups` | `ColumnGroupConfiguration[]` | Spanning headers over contiguous columns (join via `column.group`). See `references/state-and-features.md`. |
|
|
284
|
+
| `localeText` | `GridLocaleText` | Partial locale-key → string overrides; ready-made `esLocale` export. Omitted keys fall back to English. |
|
|
285
|
+
| `rowId` | `(row: T) => string \| number` | Stable row-identity resolver so `getState` selection/expansion survives a data reload. |
|
|
286
|
+
| `displayColumns` *(getter)* | `readonly ColumnConfiguration<T>[]` | Columns in visual render order (start-pinned → unpinned → end-pinned). |
|
|
287
|
+
| `hasColumnGroups` / `columnGroupDepth` *(getters)* | `boolean` / `number` | Whether a group header row renders / its depth (0–1). |
|
|
288
|
+
| `pinnedRows` *(getter)* | `{ top: T[]; bottom: T[] }` | Currently pinned rows per band. |
|
|
289
|
+
| `canUndo` / `canRedo` *(getters)* | `boolean` | Undo/redo availability (needs `editing.history.enabled`). |
|
|
290
|
+
| `exportFormats` *(getter)* | `readonly { id, label }[]` | Export menu formats (CSV in the community grid). |
|
|
279
291
|
| `sortExpressions` / `filterExpressions` | `…[]` (get/set) | Current state. To clear, use `clearSort()` / `clearFilter()`. |
|
|
280
292
|
| `selectedRows` / `expandedRows` | `T[]` (get/set) | Snapshots; set to replace (goes through cancellable events). |
|
|
281
293
|
| `rows` *(getter)* | `ApexGridRow<T>[]` | **Currently rendered only** (virtualized). |
|
|
@@ -288,18 +300,24 @@ grid.sort({ key: 'name', direction: 'ascending' }); // programmatic (silen
|
|
|
288
300
|
|
|
289
301
|
| Property | Shape |
|
|
290
302
|
|---|---|
|
|
291
|
-
| `editing` | `{ enabled, mode?: 'cell' \| 'row', trigger?: 'click' \| 'doubleClick' }` — plus per-column `editable: true` |
|
|
303
|
+
| `editing` | `{ enabled, mode?: 'cell' \| 'row', trigger?: 'click' \| 'doubleClick', history?: { enabled, stackSize? } }` — plus per-column `editable: true` |
|
|
292
304
|
| `selection` | `{ enabled, mode?: 'single' \| 'multiple', showCheckboxColumn? }` |
|
|
293
305
|
| `pagination` | `{ enabled, mode?: 'local' \| 'remote', page?, pageSize?, pageSizeOptions?, totalItems? }` |
|
|
294
306
|
| `tree` | `{ enabled, getDataPath: (row) => string[], groupColumnKey?, defaultExpanded?, childIndent? }` — flat data, hierarchy derived |
|
|
295
307
|
| `expansion` | `{ enabled, detailTemplate: (ctx) => TemplateResult, isExpandable?, showToggleColumn? }` — master-detail |
|
|
308
|
+
| `rowPinning` | `{ enabled }` — sticky top/bottom bands via `pinRow()` / `unpinRow()` |
|
|
309
|
+
| `rowReordering` | `{ enabled, applyToData?, handle? }` — drag/keyboard reorder + `moveRow()`; manual order is mutually exclusive with sort |
|
|
296
310
|
|
|
297
311
|
```ts
|
|
298
|
-
grid.editing = { enabled: true, mode: 'cell', trigger: 'doubleClick' };
|
|
312
|
+
grid.editing = { enabled: true, mode: 'cell', trigger: 'doubleClick', history: { enabled: true } };
|
|
299
313
|
grid.selection = { enabled: true, mode: 'multiple', showCheckboxColumn: true };
|
|
300
314
|
grid.pagination = { enabled: true, pageSize: 25 };
|
|
315
|
+
grid.rowPinning = { enabled: true };
|
|
316
|
+
grid.rowReordering = { enabled: true };
|
|
301
317
|
```
|
|
302
318
|
|
|
319
|
+
Declarative validators (`column.validators`), column groups (`columnGroups`), undo/redo, and state persistence (`getState` / `setState` / `getSchema`) are covered in `references/state-and-features.md`.
|
|
320
|
+
|
|
303
321
|
### Methods
|
|
304
322
|
|
|
305
323
|
| Method | Description |
|
|
@@ -309,11 +327,18 @@ grid.pagination = { enabled: true, pageSize: 25 };
|
|
|
309
327
|
| `getColumn(keyOrIndex)` | Find a column. |
|
|
310
328
|
| `updateColumns(col \| col[])` | Merge new fields into existing columns by `key`. Triggers a pipeline run. |
|
|
311
329
|
| `moveColumn(fromKey, toKey, position?)` | Reorder a column (within its pinning group). |
|
|
312
|
-
| `
|
|
330
|
+
| `pinColumn(key, 'start' \| 'end' \| null)` / `unpinColumn(key)` | Programmatic column pinning → `Promise<boolean>`; emits `columnPinning`/`columnPinned`. |
|
|
331
|
+
| `exportToCSV(options?)` / `exportAs(formatId, options?)` | Export the data. CSV is built in. `options.source`: `'view'`(def)/`'page'`/`'selected'`/`'all'`. |
|
|
332
|
+
| pagination | `gotoPage(n)`, `setPageSize(n)`, `nextPage()`, `previousPage()`, `firstPage()`, `lastPage()` → `Promise<boolean>`; emit `pageChanging`/`pageChanged`. |
|
|
333
|
+
| `setQuickFilter(value)` | Apply global search (`''` clears) → `Promise<boolean>`; emits `quickFilterChanging`/`quickFilterChanged`. |
|
|
313
334
|
| selection | `selectRow`, `deselectRow`, `toggleRowSelection`, `selectAllRows`, `clearSelection`, `isRowSelected` |
|
|
314
335
|
| expansion | `expandRow`, `collapseRow`, `toggleRowExpansion`, `expandAllRows`, `collapseAllRows`, `isRowExpanded` |
|
|
315
336
|
| tree | `expandTreeRow`, `collapseTreeRow`, `toggleTreeRow`, `expandAllTreeRows`, `collapseAllTreeRows`, `isTreeRowExpanded` |
|
|
337
|
+
| row pinning | `pinRow(row, 'top' \| 'bottom')`, `unpinRow(row)` (needs `rowPinning.enabled`) |
|
|
338
|
+
| row reorder | `moveRow(from, to, position?)` (needs `rowReordering.enabled`) |
|
|
316
339
|
| editing | `commitEdit()`, `cancelEdit()` (used with `mode: 'row'`) |
|
|
340
|
+
| undo/redo | `undo()`, `redo()`, `clearHistory()` (needs `editing.history.enabled`) |
|
|
341
|
+
| state | `getState(options?)` → `GridState`, `setState(partial, options?)` → `SetStateResult`, `getSchema()` → `GridSchema` |
|
|
317
342
|
| `ApexGrid.register()` / `ApexGrid.tagName` | Static element registration / `'apex-grid'`. |
|
|
318
343
|
|
|
319
344
|
### Events — UI-initiated only
|
|
@@ -333,8 +358,20 @@ Every event fires only for operations driven through the grid's own UI; programm
|
|
|
333
358
|
| `rowSelecting` / `rowSelected` | row selection |
|
|
334
359
|
| `rowExpanding` / `rowExpanded` | master-detail expand |
|
|
335
360
|
| `treeRowExpanding` / `treeRowExpanded` | tree-row expand |
|
|
361
|
+
| `rowPinning` / `rowPinned` | row pin / unpin (also fires for `pinRow()`/`unpinRow()`) |
|
|
362
|
+
| `rowMoving` / `rowMoved` | row reorder (also fires for `moveRow()`) |
|
|
363
|
+
|
|
364
|
+
**Exceptions to "UI-only".** The dedicated programmatic methods added for pagination (`gotoPage`, `nextPage`, …), quick-filter (`setQuickFilter`), column pinning (`pinColumn` / `unpinColumn`), row pinning (`pinRow` / `unpinRow`), and row reorder (`moveRow`) **do** emit their `*-ing` / `*-ed` pairs (unlike `sort()` / `filter()`, which stay silent).
|
|
365
|
+
|
|
366
|
+
Single-shot events (no `-ing` pair):
|
|
367
|
+
|
|
368
|
+
| Event | `event.detail` | Fires when |
|
|
369
|
+
|---|---|---|
|
|
370
|
+
| `cellValidationFailed` | `{ key, rowIndex, data, value, errors: string[] }` | a candidate value is rejected by `column.validators` (keeps editor open) |
|
|
371
|
+
| `historyChanged` | `{ canUndo, canRedo }` | undo/redo stacks change (record / undo / redo / clear) |
|
|
372
|
+
| `stateChanged` | `{ state: GridState }` | restorable state changes (debounced; UI or programmatic incl. `setState`) |
|
|
336
373
|
|
|
337
|
-
Operand reference, multi-column sort, and server-side hooks: `references/sort-and-filter.md` and `references/data-pipeline.md`.
|
|
374
|
+
Operand reference, multi-column sort, and server-side hooks: `references/sort-and-filter.md` and `references/data-pipeline.md`. Row pinning/reorder, undo/redo, validators, column groups, state/schema, and localization: `references/state-and-features.md`.
|
|
338
375
|
|
|
339
376
|
---
|
|
340
377
|
|
|
@@ -389,5 +426,6 @@ Operand reference, multi-column sort, and server-side hooks: `references/sort-an
|
|
|
389
426
|
| Column types, templates, auto-generation, per-column styling, header/editor templates | `references/columns-and-templates.md` |
|
|
390
427
|
| Sort & filter — operands, expressions, multi-column, quick-filter, events | `references/sort-and-filter.md` |
|
|
391
428
|
| Server-side data hooks (sort/filter/pagination/quickFilter), virtualization, `dataView` | `references/data-pipeline.md` |
|
|
429
|
+
| Row pinning / reordering, undo-redo, validators, column groups, state & schema (`getState`/`setState`/`getSchema`), localization, export source | `references/state-and-features.md` |
|
|
392
430
|
| End-to-end vanilla JS (no Lit `render`) | `references/vanilla-js.md` |
|
|
393
431
|
| Lit, React, Vue, Angular integration | `references/framework-integration.md` |
|
package/index.js
CHANGED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Installing Apex Grid Skill for Claude Code
|
|
2
|
+
|
|
3
|
+
```bash
|
|
4
|
+
mkdir -p .claude/skills
|
|
5
|
+
cd .claude/skills
|
|
6
|
+
git clone https://github.com/apexcharts/apexgrid-skill.git
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Claude Code will automatically detect `SKILL.md` and load it when working on `apex-grid` code.
|
|
10
|
+
|
|
11
|
+
## Verification
|
|
12
|
+
|
|
13
|
+
> Build a sortable, filterable Apex Grid for a `User` table with a custom avatar cell template.
|
|
14
|
+
|
|
15
|
+
Claude should:
|
|
16
|
+
- Register the custom element via `import 'apex-grid/define'` (or `ApexGrid.register()`) at app startup
|
|
17
|
+
- Define a `ColumnConfiguration<User>[]` with `sort: true` and `filter: true` per column, plus the right `type`
|
|
18
|
+
- Return a Lit `html\`...\`` template from `cellTemplate`, not a string
|
|
19
|
+
- Set `.data` and `.columns` as **properties** on the `<apex-grid>` element, not as attributes
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Installing Apex Grid Skill for GitHub Copilot
|
|
2
|
+
|
|
3
|
+
## VS Code + Copilot Chat
|
|
4
|
+
|
|
5
|
+
### Option 1: Add as Workspace Context
|
|
6
|
+
|
|
7
|
+
1. Clone this repository or download `SKILL.md` into your project
|
|
8
|
+
2. In Copilot Chat: `@workspace #file:SKILL.md`
|
|
9
|
+
3. Ask your data-grid question
|
|
10
|
+
|
|
11
|
+
### Option 2: Custom Instructions
|
|
12
|
+
|
|
13
|
+
1. Open VS Code Settings (Cmd/Ctrl + ,)
|
|
14
|
+
2. Search for "Copilot Custom Instructions"
|
|
15
|
+
3. Paste the contents of `.cursorrules`
|
|
16
|
+
|
|
17
|
+
## Verification
|
|
18
|
+
|
|
19
|
+
Ask Copilot to generate an Apex Grid. It should:
|
|
20
|
+
- Import from `apex-grid` (with a hyphen), not `apexgrid`
|
|
21
|
+
- Register the custom element exactly once at app startup
|
|
22
|
+
- Pass `columns` and `data` via property binding
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Installing Apex Grid Skill for Cursor
|
|
2
|
+
|
|
3
|
+
```bash
|
|
4
|
+
curl -o .cursorrules https://raw.githubusercontent.com/apexcharts/apexgrid-skill/main/.cursorrules
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
Restart Cursor or open a new window. Cursor automatically reads `.cursorrules` files in the project root as context.
|
|
8
|
+
|
|
9
|
+
## For Windsurf
|
|
10
|
+
|
|
11
|
+
Same approach — Windsurf also reads `.cursorrules`.
|
|
12
|
+
|
|
13
|
+
## Verification
|
|
14
|
+
|
|
15
|
+
Ask Cursor to generate an Apex Grid with multi-column sort and a server-side filter pipeline. It should:
|
|
16
|
+
- Set `sortConfiguration: { multiple: true, triState: true }` (or rely on the default)
|
|
17
|
+
- Provide an async `dataPipelineConfiguration.filter` hook that returns the new data array
|
|
18
|
+
- Use property bindings (`.columns=${...}`), not attribute syntax
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apexgrid-skill",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"description": "AI coding skill for the apex-grid web component — structured reference docs that help AI assistants (Claude, Cursor, Copilot, etc.) generate correct apex-grid code.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"apex-grid",
|
|
@@ -39,7 +39,9 @@
|
|
|
39
39
|
"files": [
|
|
40
40
|
"index.js",
|
|
41
41
|
"SKILL.md",
|
|
42
|
-
"references"
|
|
42
|
+
"references",
|
|
43
|
+
"install",
|
|
44
|
+
".cursorrules"
|
|
43
45
|
],
|
|
44
46
|
"engines": {
|
|
45
47
|
"node": ">=18"
|
|
@@ -21,7 +21,9 @@ interface BaseColumnConfiguration<T extends object, K extends keyof T = keyof T>
|
|
|
21
21
|
pinned?: 'start' | 'end' | null; // freeze to a side during horizontal scroll
|
|
22
22
|
reorderable?: boolean; // per-column opt-out of drag reorder
|
|
23
23
|
exportable?: boolean; // default true; false omits from CSV export
|
|
24
|
+
group?: string; // id of a columnGroups spanning header (state-and-features.md)
|
|
24
25
|
editable?: boolean; // requires grid editing.enabled
|
|
26
|
+
validators?: Validator<T, K>[]; // edit-time validation (see below)
|
|
25
27
|
sort?: boolean | { caseSensitive?: boolean; comparer?: (a: T[K], b: T[K]) => number };
|
|
26
28
|
filter?: boolean | { caseSensitive?: boolean };
|
|
27
29
|
headerTemplate?: (ctx: ApexHeaderContext<T>) => TemplateResult | unknown;
|
|
@@ -156,6 +158,21 @@ interface ApexHeaderContext<T> {
|
|
|
156
158
|
}
|
|
157
159
|
```
|
|
158
160
|
|
|
161
|
+
## Column validators (`validators`) — 3.2+
|
|
162
|
+
|
|
163
|
+
`column.validators` run before a candidate value is written (inline edit, row-mode, and bulk paste / fill). Each returns an error `string` (reject) or `null` (pass); all run and every message is collected. A failing commit keeps the editor open, marks the cell `aria-invalid`, and emits `cellValidationFailed`. Requires the column to be `editable` and grid `editing.enabled`.
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
import { required, min, max, pattern, custom } from 'apex-grid';
|
|
167
|
+
|
|
168
|
+
{ key: 'age', type: 'number', editable: true, validators: [required(), min(0), max(120)] }
|
|
169
|
+
{ key: 'email', editable: true, validators: [pattern(/^\S+@\S+$/, 'Invalid email')] }
|
|
170
|
+
{ key: 'code', editable: true,
|
|
171
|
+
validators: [custom((value, ctx) => (value === ctx.data.id ? 'Cannot equal id' : null))] }
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Built-in factories: `required(message?)`, `min(limit, message?)`, `max(limit, message?)`, `pattern(regex, message?)`, `custom(fn)`. `min`/`max`/`pattern` pass on empty / non-numeric values — compose with `required`. See `references/state-and-features.md` for the `Validator` / `ValidatorContext` shapes and the `cellValidationFailed` payload.
|
|
175
|
+
|
|
159
176
|
## Auto-generation — the fastest demo
|
|
160
177
|
|
|
161
178
|
The shortest possible "just show me a table" example:
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# State, Localization & Interaction Features (3.1+)
|
|
2
|
+
|
|
3
|
+
> Everything here landed in **apex-grid 3.1 – 3.3** on top of the 3.0 API. The setup rules from `SKILL.md` (register + bounded host height, no theme import) still apply. Row pinning / reordering, undo-redo, and validators are all **opt-in** via a config object — off by default.
|
|
4
|
+
|
|
5
|
+
## Row pinning — sticky top / bottom bands
|
|
6
|
+
|
|
7
|
+
Lift rows out of the scrolling body into sticky top / bottom bands. Identity is **by reference**, so pass the same object held in `grid.data`.
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
grid.rowPinning = { enabled: true }; // GridRowPinningConfiguration
|
|
11
|
+
grid.pinRow(grid.data[0], 'top'); // 'top' | 'bottom' (default 'top')
|
|
12
|
+
grid.pinRow(grid.data[1], 'bottom');
|
|
13
|
+
grid.unpinRow(grid.data[0]);
|
|
14
|
+
grid.pinnedRows; // getter → { top: T[]; bottom: T[] }
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
| Member | Signature | Notes |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| `rowPinning` | `{ enabled: boolean }` | Disabled when omitted. |
|
|
20
|
+
| `pinRow(row, position?)` | `(T, 'top' \| 'bottom') => boolean` | Moves between bands if already pinned. `false` when disabled / cancelled. |
|
|
21
|
+
| `unpinRow(row)` | `(T) => boolean` | No-op (returns `true`) when not pinned. |
|
|
22
|
+
| `pinnedRows` *(getter)* | `{ top: T[]; bottom: T[] }` | In pin order. |
|
|
23
|
+
|
|
24
|
+
Events: `rowPinning` (cancellable) → `rowPinned`, both `CustomEvent<{ row: T; position: 'top' | 'bottom' | null }>` (`null` when unpinning). Pins survive sort / filter / pagination as long as row identity is preserved; re-pin after a wholesale `data` swap.
|
|
25
|
+
|
|
26
|
+
## Row reordering — drag / keyboard / API
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
grid.rowReordering = { enabled: true, applyToData: false, handle: true };
|
|
30
|
+
grid.moveRow(2, 0, 'before'); // (from, to, position?: 'before' | 'after')
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
| Field | Type | Default | Notes |
|
|
34
|
+
|---|---|---|---|
|
|
35
|
+
| `enabled` | `boolean` | — | Disabled when omitted. |
|
|
36
|
+
| `applyToData` | `boolean` | `false` | `true` also splices `grid.data` in place; otherwise the order lives in the grid and you persist via `rowMoved`. |
|
|
37
|
+
| `handle` | `boolean` | `true` | `true` renders a leading grip cell (drag only from it); `false` drags from anywhere on the row. |
|
|
38
|
+
|
|
39
|
+
`moveRow(from, to, position?)` indices are into `grid.pageItems`; returns `boolean`. A manual order is **mutually exclusive with column sorting** — applying a sort clears it. Events: `rowMoving` (cancellable) → `rowMoved`, both `CustomEvent<{ from: number; to: number; data: T }>`.
|
|
40
|
+
|
|
41
|
+
## Inline-edit undo / redo
|
|
42
|
+
|
|
43
|
+
Opt in via `editing.history` (requires `editing.enabled`). Tracks every committed cell edit — single, row-mode, and bulk paste / fill.
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
grid.editing = { enabled: true, history: { enabled: true, stackSize: 100 } };
|
|
47
|
+
grid.undo(); // Ctrl/Cmd+Z
|
|
48
|
+
grid.redo(); // Ctrl/Cmd+Shift+Z or Ctrl+Y
|
|
49
|
+
grid.clearHistory();
|
|
50
|
+
grid.canUndo; grid.canRedo; // getters (false unless history.enabled)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`stackSize` defaults to `100` (older commands evicted past the cap). Event: `historyChanged` — `CustomEvent<{ canUndo: boolean; canRedo: boolean }>`, fires after any record / undo / redo / clear (drive toolbar button enablement from it).
|
|
54
|
+
|
|
55
|
+
## Declarative column validators
|
|
56
|
+
|
|
57
|
+
`column.validators: Validator[]` run **before** a candidate value is written. Each returns an error `string` (reject) or `null` (pass). All run and every message is collected, so one commit can surface multiple errors. A failing commit keeps the editor open, marks the cell `aria-invalid`, and emits `cellValidationFailed`. Covers bulk edits (paste / fill) too.
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
import { required, min, max, pattern, custom } from 'apex-grid';
|
|
61
|
+
|
|
62
|
+
const columns: ColumnConfiguration<User>[] = [
|
|
63
|
+
{ key: 'name', editable: true, validators: [required('Name is required')] },
|
|
64
|
+
{ key: 'age', type: 'number', editable: true,
|
|
65
|
+
validators: [required(), min(0), max(120)] },
|
|
66
|
+
{ key: 'email', editable: true,
|
|
67
|
+
validators: [pattern(/^\S+@\S+$/, 'Invalid email')] },
|
|
68
|
+
{ key: 'code', editable: true,
|
|
69
|
+
validators: [custom((value, ctx) => value === ctx.data.id ? 'Cannot equal id' : null)] },
|
|
70
|
+
];
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
| Factory | Signature | Fails when |
|
|
74
|
+
|---|---|---|
|
|
75
|
+
| `required(message?)` | `(msg?) => Validator` | value is `null` / `undefined` / empty-or-whitespace string |
|
|
76
|
+
| `min(limit, message?)` | `(number, msg?) => Validator` | numeric value `< limit` (non-numeric / empty pass — compose with `required`) |
|
|
77
|
+
| `max(limit, message?)` | `(number, msg?) => Validator` | numeric value `> limit` (non-numeric / empty pass) |
|
|
78
|
+
| `pattern(regex, message?)` | `(RegExp, msg?) => Validator` | `String(value)` doesn't match `regex` (empty passes) |
|
|
79
|
+
| `custom(fn)` | `(Validator) => Validator` | wraps a predicate; an inline arrow works identically |
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
// Validator signature
|
|
83
|
+
type Validator<T, K> = (value: unknown, context: ValidatorContext<T, K>) => string | null;
|
|
84
|
+
interface ValidatorContext<T, K> { column: ColumnConfiguration<T, K>; data: T; rowIndex: number; }
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Event: `cellValidationFailed` — `CustomEvent<{ key; rowIndex; data: T; value: unknown; errors: readonly string[] }>`.
|
|
88
|
+
|
|
89
|
+
## Column groups — spanning headers
|
|
90
|
+
|
|
91
|
+
A presentational header layer above the column header row. `columns` stays flat; a column joins a group via its `group` id. Members must be **contiguous within one pin region** (the grid warns and skips the spanning cell otherwise). v1 ships static spanning headers only (`collapsible` is reserved / inert).
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
grid.columnGroups = [
|
|
95
|
+
{ id: 'name', headerText: 'Name' },
|
|
96
|
+
{ id: 'contact', headerText: 'Contact',
|
|
97
|
+
headerTemplate: ({ group, span }) => html`<em>${group.headerText} (${span})</em>` },
|
|
98
|
+
];
|
|
99
|
+
grid.columns = [
|
|
100
|
+
{ key: 'first', group: 'name' },
|
|
101
|
+
{ key: 'last', group: 'name' },
|
|
102
|
+
{ key: 'email', group: 'contact' },
|
|
103
|
+
{ key: 'phone', group: 'contact' },
|
|
104
|
+
];
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
| `ColumnGroupConfiguration` | Type | Notes |
|
|
108
|
+
|---|---|---|
|
|
109
|
+
| `id` | `string` | Referenced by a column's `group`. |
|
|
110
|
+
| `headerText` | `string` | Text in the spanning cell. |
|
|
111
|
+
| `headerTemplate?` | `(ctx: { group; span }) => TemplateResult` | Overrides `headerText`. |
|
|
112
|
+
| `collapsible?` | `boolean` | Reserved for a future collapse affordance; inert in v1. |
|
|
113
|
+
|
|
114
|
+
Getters: `hasColumnGroups` (a group has ≥1 visible member), `columnGroupDepth` (0 or 1 in v1), `displayColumns` (`readonly ColumnConfiguration<T>[]` in visual render order: start-pinned, unpinned, end-pinned).
|
|
115
|
+
|
|
116
|
+
## State & schema — persist / restore / drive AI
|
|
117
|
+
|
|
118
|
+
`getState()` returns a serializable, JSON-safe snapshot (functions / templates omitted); `setState()` applies a partial one. `getSchema()` returns a machine-readable descriptor of the grid (columns, per-column operations, capabilities, current state) — the contract an AI layer hands an LLM and validates a returned patch against.
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
const snapshot = grid.getState(); // GridState
|
|
122
|
+
localStorage.setItem('grid', JSON.stringify(snapshot));
|
|
123
|
+
// later …
|
|
124
|
+
grid.setState(JSON.parse(localStorage.getItem('grid')!)); // → SetStateResult
|
|
125
|
+
|
|
126
|
+
// Selection / expansion survive a wholesale data reload only with a stable id:
|
|
127
|
+
grid.rowId = (row) => row.id;
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
`GridState` (`version: 1`) captures: `columns` (order/width/pinning/visibility), `sort`, `filter` (condition by operand name), `quickFilter`, `pagination` (`{ page, pageSize }`), `selection`, `expansion`, `treeExpanded` / `treeExpandedKeys`, `rowPinning`, `rowOrder`, and per-`modules` state. Rows are captured as `RowRef` — a stable `id` when `rowId` is set, else a positional `index` into `data` (round-trips within a session, not across a data swap).
|
|
131
|
+
|
|
132
|
+
`setState(state, options?)` is **partial and defensive** — only present slices apply (omit a slice to leave it untouched; a present-but-empty `sort`/`filter` clears it). It never throws on malformed / stale / LLM input unless `options.strict` is set; unknown columns/operands, out-of-range pages, and unresolvable rows are dropped / clamped and reported. `rowOrder` is mutually exclusive with `sort` (a snapshot with both drops `rowOrder`).
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
interface SetStateResult { applied: string[]; skipped: string[]; warnings: string[]; }
|
|
136
|
+
interface GetStateOptions<T> { rowId?: (row: T) => string | number; }
|
|
137
|
+
interface SetStateOptions<T> { rowId?: (row: T) => string | number; strict?: boolean; }
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Event: `stateChanged` — `CustomEvent<{ state: GridState }>`, debounced after the render pipeline settles. Fires for programmatic (including `setState`) and UI changes. The payload is only computed while a listener is attached (zero-cost otherwise); identical snapshots don't re-fire, so compare before persisting to avoid a save loop.
|
|
141
|
+
|
|
142
|
+
## Programmatic pagination, quick-filter, and column pinning
|
|
143
|
+
|
|
144
|
+
All emit the same cancellable-then-notify event pairs as their UI equivalents and return `Promise<boolean>`.
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
await grid.gotoPage(2); // clamped into [0, pageCount - 1]
|
|
148
|
+
await grid.nextPage();
|
|
149
|
+
await grid.previousPage();
|
|
150
|
+
await grid.firstPage();
|
|
151
|
+
await grid.lastPage();
|
|
152
|
+
await grid.setPageSize(50); // returns to the first page
|
|
153
|
+
await grid.setQuickFilter('john'); // '' to clear
|
|
154
|
+
await grid.pinColumn('name', 'start'); // 'start' | 'end' | null
|
|
155
|
+
await grid.unpinColumn('actions'); // = pinColumn(key, null)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
`gotoPage` / `setPageSize` / `nextPage` / `previousPage` / `firstPage` / `lastPage` emit `pageChanging` → `pageChanged`. `setQuickFilter` emits `quickFilterChanging` → `quickFilterChanged`. `pinColumn` / `unpinColumn` emit `columnPinning` → `columnPinned` and only change the visual render order (the `columns` array isn't reordered).
|
|
159
|
+
|
|
160
|
+
## Localization — `localeText`
|
|
161
|
+
|
|
162
|
+
Every built-in UI string (pagination, filter operators, toolbar, row/selection labels) is localizable through a `GridLocaleText` map keyed by dot-namespaced `GridLocaleKey`s. Partial maps are valid — any omitted key falls back to the English default.
|
|
163
|
+
|
|
164
|
+
```ts
|
|
165
|
+
import { esLocale } from 'apex-grid';
|
|
166
|
+
|
|
167
|
+
grid.localeText = esLocale; // whole-UI Spanish
|
|
168
|
+
grid.localeText = { 'toolbar.searchPlaceholder': 'Buscar…' }; // or override a few keys
|
|
169
|
+
|
|
170
|
+
grid.localize('pagination.summary', { start: 1, end: 25, total: 100 }); // resolve a key
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Exports: `EN_LOCALE` (the default dictionary / source of truth for keys), `esLocale` (ready-made Spanish), `interpolate` (`{token}` substitution), `localize`, and the types `GridLocaleKey` / `GridLocaleText` / `LocaleParams`. `grid.localize(key, params?, fallback?)` applies any `localeText` override over the English default and interpolates `{placeholder}` tokens; `fallback` covers keys outside the built-in set (e.g. a custom operand label).
|
|
174
|
+
|
|
175
|
+
## Column menu & column separator
|
|
176
|
+
|
|
177
|
+
| Property | Type | Default | Notes |
|
|
178
|
+
|---|---|---|---|
|
|
179
|
+
| `columnMenu` | `boolean` (JS property, no attr) | `true` | Kebab-menu button in each header, always visible. Set `false` to hide it. |
|
|
180
|
+
| `columnSeparator` (attr `column-separator`) | `boolean` | `true` | Persistent trailing divider on every header. On resizable columns it doubles as the resize handle. Theme via `--ag-header-separator` / `--header-separator-color` and inset via `--apex-header-separator-inset`. |
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
grid.columnMenu = false;
|
|
184
|
+
grid.columnSeparator = false;
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Export source & formats
|
|
188
|
+
|
|
189
|
+
`exportToCSV` / `exportAs` accept a `source` selecting which rows to export:
|
|
190
|
+
|
|
191
|
+
| `source` | Rows exported |
|
|
192
|
+
|---|---|
|
|
193
|
+
| `'view'` *(default)* | post-filter, post-sort rows across all pages |
|
|
194
|
+
| `'page'` | current page slice only |
|
|
195
|
+
| `'selected'` | current row selection (insertion order) |
|
|
196
|
+
| `'all'` | the raw `data` array |
|
|
197
|
+
|
|
198
|
+
```ts
|
|
199
|
+
grid.exportToCSV(); // download data.csv (view)
|
|
200
|
+
grid.exportToCSV({ filename: 'users', source: 'selected' });
|
|
201
|
+
const text = grid.exportToCSV({ filename: '' }); // string only, no download
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
`exportFormats` *(getter)* → `readonly ExportFormat[]` (`{ id, label }`) — the community grid offers CSV only; it drives the toolbar export menu, one item per entry, dispatching to `exportAs(formatId, options?)`. Other `ExportOptions` fields: `columns?` (keys to include), `includeHeader?` (default `true`), `formatter?`; CSV adds `delimiter?` (`','`), `bom?` (`true`), `newline?` (`'\r\n'`).
|