apexgrid-skill 1.0.0 → 2.1.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/README.md +22 -7
- package/SKILL.md +312 -291
- package/index.js +2 -0
- package/package.json +1 -1
- package/references/columns-and-templates.md +72 -13
- package/references/data-pipeline.md +12 -7
- package/references/framework-integration.md +92 -47
- package/references/state-and-features.md +204 -0
- package/references/vanilla-js.md +154 -0
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apexgrid-skill",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "2.1.0",
|
|
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",
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Column Configuration & Lit Templates
|
|
2
2
|
|
|
3
|
+
> **Before you start**: this reference assumes the Quickstart setup from `SKILL.md` is done — element registered and the host element sized (e.g. `setup()`). **No theme CSS import is needed**; the grid self-styles via `--ag-*` CSS variables. Without a sized host, no column configuration will produce a visible grid.
|
|
4
|
+
|
|
5
|
+
## A note on column widths
|
|
6
|
+
|
|
7
|
+
If you omit `width`, the column flex-fills the remaining space. With one un-widthed column among several explicit ones, that column **stretches to fill the grid** — including pushing its sort arrow to the far edge of the viewport, which looks like a phantom column. For predictable layouts, set explicit widths on every column.
|
|
8
|
+
|
|
3
9
|
## `ColumnConfiguration<T, K>`
|
|
4
10
|
|
|
5
11
|
Apex Grid is generic over a row type `T extends object`. Every column is typed against a key `K extends keyof T` so the cell template's `value` is correctly inferred.
|
|
@@ -7,18 +13,40 @@ Apex Grid is generic over a row type `T extends object`. Every column is typed a
|
|
|
7
13
|
```ts
|
|
8
14
|
interface BaseColumnConfiguration<T extends object, K extends keyof T = keyof T> {
|
|
9
15
|
key: K; // REQUIRED — keyof T
|
|
10
|
-
type?:
|
|
16
|
+
type?: DataType; // default 'string' (see "Column types" below)
|
|
11
17
|
headerText?: string; // default: String(key)
|
|
12
18
|
width?: string; // any CSS width unit
|
|
13
19
|
hidden?: boolean; // default false
|
|
14
20
|
resizable?: boolean; // default false
|
|
21
|
+
pinned?: 'start' | 'end' | null; // freeze to a side during horizontal scroll
|
|
22
|
+
reorderable?: boolean; // per-column opt-out of drag reorder
|
|
23
|
+
exportable?: boolean; // default true; false omits from CSV export
|
|
24
|
+
group?: string; // id of a columnGroups spanning header (state-and-features.md)
|
|
25
|
+
editable?: boolean; // requires grid editing.enabled
|
|
26
|
+
validators?: Validator<T, K>[]; // edit-time validation (see below)
|
|
15
27
|
sort?: boolean | { caseSensitive?: boolean; comparer?: (a: T[K], b: T[K]) => number };
|
|
16
28
|
filter?: boolean | { caseSensitive?: boolean };
|
|
17
29
|
headerTemplate?: (ctx: ApexHeaderContext<T>) => TemplateResult | unknown;
|
|
18
30
|
cellTemplate?: (ctx: ApexCellContext<T, K>) => TemplateResult | unknown;
|
|
31
|
+
editorTemplate?: (ctx: ApexEditorContext<T, K>) => TemplateResult | unknown;
|
|
32
|
+
// type-specific (ignored unless `type` matches):
|
|
33
|
+
options?: (V | { value: V; label?: string })[]; // 'select'
|
|
34
|
+
max?: number; // 'rating' (stars, def 5) / 'progress' (full, def 100)
|
|
35
|
+
format?: 'short' | 'medium' | 'long' | 'full'; // 'date' (def 'medium')
|
|
36
|
+
shape?: 'square' | 'circle'; // 'image' (def 'square')
|
|
37
|
+
alt?: string; // 'image' alt text
|
|
38
|
+
currency?: string; // 'currency' ISO 4217 (def 'USD')
|
|
39
|
+
locale?: string; // 'currency' BCP 47 locale
|
|
40
|
+
badgeVariant?: 'gold' | 'brand' | 'neutral' | 'muted' | ((v) => …); // 'badge'
|
|
41
|
+
statusVariant?: 'active' | 'trial' | 'churn' | ((v) => …); // 'status'
|
|
42
|
+
showDelta?: boolean; // 'sparkline' (def true)
|
|
19
43
|
}
|
|
20
44
|
```
|
|
21
45
|
|
|
46
|
+
### Column types (`DataType`)
|
|
47
|
+
|
|
48
|
+
`'string'` / `'number'` / `'boolean'` are the primitive types driving sort, filter, and the default editor. The rest are presentation renderers over a primitive value (and sort/filter as that value): `'select'` (label from `options`, `<select>` editor), `'rating'` (stars, 0..`max`), `'date'` (locale date via `format`; accepts `Date`/ISO/timestamp; `<input type="date">` editor), `'image'` (`<img>`, `shape`/`alt`), `'currency'` (`Intl.NumberFormat`, `currency`/`locale`), `'avatar'`, `'badge'` (`badgeVariant`), `'progress'` (0..`max`), `'sparkline'` (`number[]`, `showDelta`), `'status'` (`statusVariant`).
|
|
49
|
+
|
|
22
50
|
Defaults applied on assignment (`DEFAULT_COLUMN_CONFIG`):
|
|
23
51
|
|
|
24
52
|
```ts
|
|
@@ -33,17 +61,10 @@ Defaults applied on assignment (`DEFAULT_COLUMN_CONFIG`):
|
|
|
33
61
|
| `'number'` | numeric `<` / `>` | `equals`, `doesNotEqual`, `greaterThan`, `lessThan`, `greaterThanOrEqual`, `lessThanOrEqual`, `empty`, `notEmpty` |
|
|
34
62
|
| `'boolean'` | `false < true` | `all`, `true`, `false`, `empty`, `notEmpty` |
|
|
35
63
|
|
|
36
|
-
|
|
64
|
+
The table above lists the three **primitive** types. The presentation types (`'select'`, `'rating'`, `'date'`, `'image'`, `'currency'`, `'avatar'`, `'badge'`, `'progress'`, `'sparkline'`, `'status'`) sort and filter as their underlying value. For dates, use the real `'date'` type — it accepts `Date`, ISO strings, or millisecond timestamps, renders a locale date (`format`), and sorts chronologically. No timestamp/comparer workaround needed:
|
|
37
65
|
|
|
38
66
|
```ts
|
|
39
|
-
|
|
40
|
-
{ key: 'createdAt', type: 'number', sort: true, filter: true,
|
|
41
|
-
cellTemplate: ({ value }) => html`${new Date(value).toLocaleDateString()}` }
|
|
42
|
-
|
|
43
|
-
// Option B — store as ISO strings + custom comparer
|
|
44
|
-
{ key: 'createdAt', type: 'string',
|
|
45
|
-
sort: { comparer: (a, b) => a.localeCompare(b) },
|
|
46
|
-
cellTemplate: ({ value }) => html`${new Date(value).toLocaleDateString()}` }
|
|
67
|
+
{ key: 'createdAt', type: 'date', format: 'medium', sort: true, filter: true }
|
|
47
68
|
```
|
|
48
69
|
|
|
49
70
|
## Sort configuration per column
|
|
@@ -137,16 +158,54 @@ interface ApexHeaderContext<T> {
|
|
|
137
158
|
}
|
|
138
159
|
```
|
|
139
160
|
|
|
140
|
-
##
|
|
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
|
+
|
|
176
|
+
## Auto-generation — the fastest demo
|
|
177
|
+
|
|
178
|
+
The shortest possible "just show me a table" example:
|
|
141
179
|
|
|
142
180
|
```ts
|
|
143
|
-
|
|
181
|
+
import { html, render } from 'lit';
|
|
182
|
+
import { setup } from 'apex-grid';
|
|
183
|
+
setup();
|
|
184
|
+
|
|
185
|
+
const data = [
|
|
186
|
+
{ id: 1, name: 'Ada', age: 36 },
|
|
187
|
+
{ id: 2, name: 'Carl', age: 62 },
|
|
188
|
+
];
|
|
189
|
+
|
|
190
|
+
render(
|
|
191
|
+
html`<apex-grid auto-generate .data=${data}></apex-grid>`,
|
|
192
|
+
document.getElementById('app')!,
|
|
193
|
+
);
|
|
144
194
|
```
|
|
145
195
|
|
|
196
|
+
> Remember the host CSS — `apex-grid { height: 480px; }` — or you'll see no rows even with `auto-generate`.
|
|
197
|
+
|
|
198
|
+
**How it works:**
|
|
199
|
+
|
|
146
200
|
- Reads `Object.keys(data[0])` and creates a column for each, using the value's runtime type to pick `type`.
|
|
147
201
|
- **Skipped entirely if `columns` is non-empty.** Set `grid.columns = []` before swapping data to re-trigger.
|
|
148
202
|
- Late-bound data (e.g. fetched after first render) infers correctly because the watcher re-runs on `data` changes — but only when `columns` is still empty.
|
|
149
203
|
|
|
204
|
+
**Caveats:**
|
|
205
|
+
|
|
206
|
+
- `sort` and `filter` default to `false`, so the auto path is read-only. To get sortable / filterable columns from inferred data, run auto-generate first then patch via `grid.updateColumns([{ key: 'name', sort: true, filter: true }, ...])`.
|
|
207
|
+
- Auto-generated columns get no explicit `width` — they flex-fill across the grid. For mixed-type columns that's usually not what you want; assign explicit `columns` instead.
|
|
208
|
+
|
|
150
209
|
## Updating columns at runtime
|
|
151
210
|
|
|
152
211
|
```ts
|
|
@@ -186,6 +245,6 @@ The grid renders a drag handle on the right edge of the column header. Minimum r
|
|
|
186
245
|
| `{ key: 'fullName' }` when `fullName` isn't in `T` | Add to `T`, or compute via `cellTemplate: ({ row }) => html\`${row.firstName} ${row.lastName}\`` |
|
|
187
246
|
| `cellTemplate: ({ value }) => \`<b>\${value}</b>\`` (string) | `cellTemplate: ({ value }) => html\`<b>\${value}</b>\`` |
|
|
188
247
|
| `{ key: 'age', sort: true }` (default `type: 'string'`) | Set `type: 'number'` |
|
|
189
|
-
| `{ key: 'created', type: '
|
|
248
|
+
| `{ key: 'created', type: 'string', sort: { comparer } }` to fake dates | `{ key: 'created', type: 'date', format: 'medium' }` — a real type (accepts `Date`/ISO/timestamp) |
|
|
190
249
|
| `auto-generate` with existing columns | Reset `grid.columns = []` first |
|
|
191
250
|
| `updateColumns({ key: 'newField', ... })` to add | `updateColumns` only patches existing — assign a new `columns` array to add |
|
|
@@ -2,24 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
## How the pipeline runs
|
|
4
4
|
|
|
5
|
-
When `data`, `columns`, sort state,
|
|
5
|
+
When `data`, `columns`, sort state, filter state, the quick-filter value, or the page changes, Apex Grid runs an async pipeline:
|
|
6
6
|
|
|
7
7
|
```
|
|
8
|
-
data ─► (filter) ─► (sort) ─►
|
|
8
|
+
data ─► (quickFilter) ─► (filter) ─► (sort) ─► (pagination) ─► virtualizer ─► rendered rows
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
By default
|
|
11
|
+
By default every stage runs in-memory. Override any stage with `dataPipelineConfiguration`.
|
|
12
12
|
|
|
13
13
|
## `dataPipelineConfiguration`
|
|
14
14
|
|
|
15
15
|
```ts
|
|
16
|
+
type DataPipelineParams<T> = { data: T[]; grid: ApexGrid<T>; type: 'sort' | 'filter' | 'quickFilter' | 'pagination' };
|
|
17
|
+
type DataPipelineHook<T> = (state: DataPipelineParams<T>) => T[] | Promise<T[]>;
|
|
18
|
+
|
|
16
19
|
interface DataPipelineConfiguration<T extends object> {
|
|
17
|
-
sort?:
|
|
18
|
-
filter?:
|
|
20
|
+
sort?: DataPipelineHook<T>;
|
|
21
|
+
filter?: DataPipelineHook<T>; // column filters
|
|
22
|
+
quickFilter?: DataPipelineHook<T>; // global search (runs before column filter)
|
|
23
|
+
pagination?: DataPipelineHook<T>; // return the page slice; remote: set pagination.totalItems
|
|
19
24
|
}
|
|
20
25
|
```
|
|
21
26
|
|
|
22
|
-
The grid invokes the matching hook in place of its built-in pipeline stage. Whatever the hook returns becomes the
|
|
27
|
+
The grid invokes the matching hook in place of its built-in pipeline stage. Whatever the hook returns becomes the input for the next stage (or the final view). All four hooks are optional — omit a stage to keep it in-memory.
|
|
23
28
|
|
|
24
29
|
```ts
|
|
25
30
|
grid.dataPipelineConfiguration = {
|
|
@@ -57,7 +62,7 @@ grid.dataPipelineConfiguration = {
|
|
|
57
62
|
|
|
58
63
|
### Order of operations
|
|
59
64
|
|
|
60
|
-
The grid runs
|
|
65
|
+
The grid runs the stages in a fixed order: **quick-filter → column filter → sort → pagination**. If your server endpoint does several of these together, do the work in the earliest relevant hook (returning the fully processed array) and omit the later hooks. For server-driven paging, return the current page from the `pagination` hook and set `grid.pagination = { ..., mode: 'remote', totalItems }` so the paginator can compute the page count.
|
|
61
66
|
|
|
62
67
|
### Hooks fire on every state change
|
|
63
68
|
|
|
@@ -1,13 +1,46 @@
|
|
|
1
1
|
# Framework Integration — Lit, React, Vue, Angular
|
|
2
2
|
|
|
3
|
-
`apex-grid` is a standard custom element with no first-party framework wrappers. Every framework consumes it the same way:
|
|
3
|
+
`apex-grid` is a standard custom element with no first-party framework wrappers. Every framework consumes it the same way:
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
1. **Register the element + size the host once at app startup** — `setup()` does both (registers `<apex-grid>` and adopts a bounded host height). No theme CSS import is needed; the grid self-styles via `--ag-*` variables.
|
|
6
|
+
2. **Bind `data` and `columns` as properties** (not attributes).
|
|
7
|
+
3. **Listen for `sorting` / `sorted` / `filtering` / `filtered` (and the other UI) events** as standard DOM events.
|
|
6
8
|
|
|
7
|
-
The
|
|
9
|
+
The `setup()` call ideally lives in your application entry file (`main.ts` / `main.js` / `app.module.ts`) so it runs once, not per component.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## App-entry setup (all frameworks)
|
|
14
|
+
|
|
15
|
+
Put this at the top of your application entry point — `src/main.ts` for Vite/Lit/Vue, `src/index.tsx` for React, `src/main.ts` for Angular:
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { setup } from 'apex-grid';
|
|
19
|
+
setup(); // registers <apex-grid> + adopts a bounded host height
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
No theme import needed — the grid self-styles. Retheme by overriding `--ag-*` CSS variables on `apex-grid`. If you prefer manual control over sizing, call `setup({ hostStyles: false })` (or use `import 'apex-grid/define'`) and add your own rule:
|
|
23
|
+
|
|
24
|
+
```css
|
|
25
|
+
apex-grid {
|
|
26
|
+
height: 480px; /* or whatever fits your layout; + any --ag-* overrides */
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The framework examples below assume this app-entry setup is already done.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Lit
|
|
35
|
+
|
|
36
|
+
The most natural host — Lit `html` templates already understand `.property` and `@event` binding syntax.
|
|
8
37
|
|
|
9
38
|
```ts
|
|
10
|
-
|
|
39
|
+
// src/main.ts — done once
|
|
40
|
+
import { setup } from 'apex-grid';
|
|
41
|
+
setup();
|
|
42
|
+
|
|
43
|
+
// component
|
|
11
44
|
import { html, render } from 'lit';
|
|
12
45
|
import type { ColumnConfiguration } from 'apex-grid';
|
|
13
46
|
|
|
@@ -15,11 +48,11 @@ type User = { id: number; name: string; age: number; subscribed: boolean };
|
|
|
15
48
|
|
|
16
49
|
const data: User[] = [/* … */];
|
|
17
50
|
const columns: ColumnConfiguration<User>[] = [
|
|
18
|
-
{ key: 'id',
|
|
19
|
-
{ key: 'name',
|
|
51
|
+
{ key: 'id', type: 'number', width: '80px', sort: true, filter: true },
|
|
52
|
+
{ key: 'name', type: 'string', width: '240px', sort: true, filter: true,
|
|
20
53
|
cellTemplate: ({ value }) => html`<strong>${value}</strong>` },
|
|
21
|
-
{ key: 'age',
|
|
22
|
-
{ key: 'subscribed', type: 'boolean', sort: true, filter: true },
|
|
54
|
+
{ key: 'age', type: 'number', width: '100px', sort: true, filter: true },
|
|
55
|
+
{ key: 'subscribed', type: 'boolean', width: '140px', sort: true, filter: true },
|
|
23
56
|
];
|
|
24
57
|
|
|
25
58
|
render(
|
|
@@ -33,15 +66,16 @@ render(
|
|
|
33
66
|
);
|
|
34
67
|
```
|
|
35
68
|
|
|
69
|
+
---
|
|
70
|
+
|
|
36
71
|
## React
|
|
37
72
|
|
|
38
|
-
React 19+ supports custom elements natively, including property binding
|
|
73
|
+
React 19+ supports custom elements natively, including property binding for non-string values. For broad compatibility, use `ref` + `useEffect`:
|
|
39
74
|
|
|
40
75
|
```tsx
|
|
41
76
|
import { useEffect, useRef } from 'react';
|
|
42
|
-
import '
|
|
77
|
+
import { html } from 'lit'; // required for cellTemplate
|
|
43
78
|
import type { ApexGrid, ColumnConfiguration } from 'apex-grid';
|
|
44
|
-
import { html } from 'lit';
|
|
45
79
|
|
|
46
80
|
type User = { id: number; name: string; age: number; subscribed: boolean };
|
|
47
81
|
|
|
@@ -53,11 +87,11 @@ export function UsersGrid({ data }: { data: User[] }) {
|
|
|
53
87
|
if (!grid) return;
|
|
54
88
|
|
|
55
89
|
grid.columns = [
|
|
56
|
-
{ key: 'id', type: 'number', sort: true, filter: true },
|
|
57
|
-
{ key: 'name', type: 'string', sort: true, filter: true,
|
|
90
|
+
{ key: 'id', type: 'number', width: '80px', sort: true, filter: true },
|
|
91
|
+
{ key: 'name', type: 'string', width: '240px', sort: true, filter: true,
|
|
58
92
|
cellTemplate: ({ value }) => html`<strong>${value}</strong>` },
|
|
59
|
-
{ key: 'age', type: 'number', sort: true, filter: true },
|
|
60
|
-
{ key: 'subscribed', type: 'boolean', sort: true, filter: true },
|
|
93
|
+
{ key: 'age', type: 'number', width: '100px', sort: true, filter: true },
|
|
94
|
+
{ key: 'subscribed', type: 'boolean', width: '140px', sort: true, filter: true },
|
|
61
95
|
];
|
|
62
96
|
|
|
63
97
|
const onSorted = (e: Event) => console.log('sorted', (e as CustomEvent).detail);
|
|
@@ -65,20 +99,22 @@ export function UsersGrid({ data }: { data: User[] }) {
|
|
|
65
99
|
return () => grid.removeEventListener('sorted', onSorted);
|
|
66
100
|
}, []);
|
|
67
101
|
|
|
68
|
-
// Set data whenever it changes
|
|
69
102
|
useEffect(() => {
|
|
70
|
-
if (ref.current) ref.current.data = data;
|
|
103
|
+
if (ref.current) ref.current.data = data; // set property whenever data changes
|
|
71
104
|
}, [data]);
|
|
72
105
|
|
|
106
|
+
// The global `apex-grid { height: ... }` CSS handles sizing.
|
|
73
107
|
// @ts-expect-error — apex-grid is a custom element
|
|
74
|
-
return <apex-grid ref={ref}
|
|
108
|
+
return <apex-grid ref={ref} />;
|
|
75
109
|
}
|
|
76
110
|
```
|
|
77
111
|
|
|
78
|
-
|
|
112
|
+
If you'd rather size per instance instead of globally, replace the global CSS rule with a `style` prop: `style={{ height: '500px', display: 'block' }}`.
|
|
113
|
+
|
|
114
|
+
For full TypeScript / JSX intrinsic-element support:
|
|
79
115
|
|
|
80
116
|
```ts
|
|
81
|
-
// apex-grid.d.ts
|
|
117
|
+
// types/apex-grid.d.ts
|
|
82
118
|
import type { ApexGrid } from 'apex-grid';
|
|
83
119
|
|
|
84
120
|
declare module 'react' {
|
|
@@ -90,24 +126,31 @@ declare module 'react' {
|
|
|
90
126
|
}
|
|
91
127
|
```
|
|
92
128
|
|
|
129
|
+
---
|
|
130
|
+
|
|
93
131
|
## Vue 3
|
|
94
132
|
|
|
95
|
-
|
|
133
|
+
### Vite plugin config
|
|
134
|
+
|
|
135
|
+
Tell the Vue compiler that `apex-grid` is a custom element so it doesn't try to resolve it as a Vue component:
|
|
96
136
|
|
|
97
137
|
```ts
|
|
98
138
|
// vite.config.ts
|
|
99
139
|
export default defineConfig({
|
|
100
140
|
plugins: [
|
|
101
|
-
vue({
|
|
141
|
+
vue({
|
|
142
|
+
template: { compilerOptions: { isCustomElement: (tag) => tag.startsWith('apex-') } },
|
|
143
|
+
}),
|
|
102
144
|
],
|
|
103
145
|
});
|
|
104
146
|
```
|
|
105
147
|
|
|
148
|
+
### Component
|
|
149
|
+
|
|
106
150
|
```vue
|
|
107
151
|
<script setup lang="ts">
|
|
108
152
|
import { ref, onMounted } from 'vue';
|
|
109
|
-
import '
|
|
110
|
-
import { html } from 'lit';
|
|
153
|
+
import { html } from 'lit'; // required for cellTemplate
|
|
111
154
|
import type { ApexGrid, ColumnConfiguration } from 'apex-grid';
|
|
112
155
|
|
|
113
156
|
type User = { id: number; name: string; age: number; subscribed: boolean };
|
|
@@ -116,15 +159,15 @@ const grid = ref<ApexGrid<User> | null>(null);
|
|
|
116
159
|
|
|
117
160
|
const data = ref<User[]>([/* … */]);
|
|
118
161
|
const columns: ColumnConfiguration<User>[] = [
|
|
119
|
-
{ key: 'id', type: 'number', sort: true, filter: true },
|
|
120
|
-
{ key: 'name', type: 'string', sort: true, filter: true,
|
|
162
|
+
{ key: 'id', type: 'number', width: '80px', sort: true, filter: true },
|
|
163
|
+
{ key: 'name', type: 'string', width: '240px', sort: true, filter: true,
|
|
121
164
|
cellTemplate: ({ value }) => html`<strong>${value}</strong>` },
|
|
122
|
-
{ key: 'age', type: 'number', sort: true, filter: true },
|
|
123
|
-
{ key: 'subscribed', type: 'boolean', sort: true, filter: true },
|
|
165
|
+
{ key: 'age', type: 'number', width: '100px', sort: true, filter: true },
|
|
166
|
+
{ key: 'subscribed', type: 'boolean', width: '140px', sort: true, filter: true },
|
|
124
167
|
];
|
|
125
168
|
|
|
126
169
|
onMounted(() => {
|
|
127
|
-
grid.value!.columns = columns;
|
|
170
|
+
grid.value!.columns = columns; // set as property after mount
|
|
128
171
|
});
|
|
129
172
|
|
|
130
173
|
function onSorted(e: CustomEvent) {
|
|
@@ -137,22 +180,21 @@ function onSorted(e: CustomEvent) {
|
|
|
137
180
|
ref="grid"
|
|
138
181
|
:data.prop="data"
|
|
139
182
|
@sorted="onSorted"
|
|
140
|
-
style="height: 500px; display: block;"
|
|
141
183
|
/>
|
|
142
184
|
</template>
|
|
143
185
|
```
|
|
144
186
|
|
|
145
|
-
> Use `:data.prop="data"`
|
|
187
|
+
> Use `:data.prop="data"` to bind as a property. Vue's default `:data="data"` sets an attribute, which becomes the literal string `"[object Object]"` and the grid renders nothing.
|
|
188
|
+
|
|
189
|
+
---
|
|
146
190
|
|
|
147
191
|
## Angular
|
|
148
192
|
|
|
149
|
-
Add `CUSTOM_ELEMENTS_SCHEMA` to the
|
|
193
|
+
Add `CUSTOM_ELEMENTS_SCHEMA` to the component (or module). Use `[data]="..."` for property binding and `(sorted)="..."` for events:
|
|
150
194
|
|
|
151
195
|
```ts
|
|
152
|
-
// app.component.ts
|
|
153
196
|
import { Component, CUSTOM_ELEMENTS_SCHEMA, OnInit, ViewChild, ElementRef } from '@angular/core';
|
|
154
|
-
import '
|
|
155
|
-
import { html } from 'lit';
|
|
197
|
+
import { html } from 'lit'; // required for cellTemplate
|
|
156
198
|
import type { ApexGrid, ColumnConfiguration } from 'apex-grid';
|
|
157
199
|
|
|
158
200
|
type User = { id: number; name: string; age: number; subscribed: boolean };
|
|
@@ -165,9 +207,8 @@ type User = { id: number; name: string; age: number; subscribed: boolean };
|
|
|
165
207
|
<apex-grid
|
|
166
208
|
#grid
|
|
167
209
|
[data]="data"
|
|
168
|
-
(sorted)="onSorted($event)"
|
|
169
|
-
|
|
170
|
-
></apex-grid>
|
|
210
|
+
(sorted)="onSorted($event)">
|
|
211
|
+
</apex-grid>
|
|
171
212
|
`,
|
|
172
213
|
})
|
|
173
214
|
export class UsersGridComponent implements OnInit {
|
|
@@ -177,11 +218,11 @@ export class UsersGridComponent implements OnInit {
|
|
|
177
218
|
|
|
178
219
|
ngOnInit() {
|
|
179
220
|
this.grid.nativeElement.columns = [
|
|
180
|
-
{ key: 'id', type: 'number', sort: true, filter: true },
|
|
181
|
-
{ key: 'name', type: 'string', sort: true, filter: true,
|
|
221
|
+
{ key: 'id', type: 'number', width: '80px', sort: true, filter: true },
|
|
222
|
+
{ key: 'name', type: 'string', width: '240px', sort: true, filter: true,
|
|
182
223
|
cellTemplate: ({ value }) => html`<strong>${value}</strong>` },
|
|
183
|
-
{ key: 'age', type: 'number', sort: true, filter: true },
|
|
184
|
-
{ key: 'subscribed', type: 'boolean', sort: true, filter: true },
|
|
224
|
+
{ key: 'age', type: 'number', width: '100px', sort: true, filter: true },
|
|
225
|
+
{ key: 'subscribed', type: 'boolean', width: '140px', sort: true, filter: true },
|
|
185
226
|
];
|
|
186
227
|
}
|
|
187
228
|
|
|
@@ -191,13 +232,17 @@ export class UsersGridComponent implements OnInit {
|
|
|
191
232
|
}
|
|
192
233
|
```
|
|
193
234
|
|
|
235
|
+
---
|
|
236
|
+
|
|
194
237
|
## Common pitfalls across frameworks
|
|
195
238
|
|
|
196
239
|
| ❌ | ✅ |
|
|
197
240
|
|---|---|
|
|
198
|
-
|
|
|
199
|
-
|
|
|
241
|
+
| Forgetting to register the element | Top-level `import 'apex-grid/define'` once in the app entry |
|
|
242
|
+
| Importing an Ignite UI theme + `configureTheme(name)` | Obsolete — the grid self-styles; retheme via `--ag-*` CSS variables. No import does anything for appearance |
|
|
243
|
+
| Forgetting `setup()` / no host-height rule for `apex-grid` | `setup()` registers + sizes the host. Or `import 'apex-grid/define'` + `apex-grid { height: ... }` (never set `display` — it overrides the component's `:host { display: grid }`) |
|
|
244
|
+
| Vue: `:data="data"` (attribute, becomes `"[object Object]"`) | `:data.prop="data"`, or set via `ref` in `onMounted` |
|
|
245
|
+
| React: `<apex-grid columns={[...]}>` before React 19 | Use `ref` + `useEffect` to set as property |
|
|
200
246
|
| Angular: missing `CUSTOM_ELEMENTS_SCHEMA` | Add to the component / module schemas |
|
|
201
|
-
|
|
|
202
|
-
|
|
|
203
|
-
| Treating `cellTemplate` as framework-specific JSX | Always use Lit `html\`...\`` — the template runs inside the web component, not in the host framework |
|
|
247
|
+
| Writing string templates for cells | Always `import { html } from 'lit'` and use `html\`...\`` tagged literals |
|
|
248
|
+
| Treating `cellTemplate` as framework-specific JSX | The template runs inside the web component — only Lit `html` works |
|