@ornery/ui-grid-vanilla 0.1.10 → 1.0.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/AGENTS.md +70 -0
- package/README.md +196 -0
- package/demo/main.ts +0 -1
- package/dist/index.d.mts +582 -55
- package/dist/index.d.ts +582 -55
- package/dist/index.js +4959 -1429
- package/dist/index.mjs +5137 -1560
- package/package.json +11 -8
- package/scss-plugin.ts +65 -0
- package/src/adopt-styles.ts +39 -0
- package/src/attribute-bridge.ts +291 -0
- package/src/auto-height.test.ts +142 -0
- package/src/auto-resize.test.ts +207 -0
- package/src/cell-nav.test.ts +262 -0
- package/src/components/grid-body-cell/grid-body-cell.ts +23 -0
- package/src/components/grid-body-cell/index.ts +1 -0
- package/src/components/grid-cell-editor/grid-cell-editor.ts +104 -0
- package/src/components/grid-cell-editor/index.ts +1 -0
- package/src/components/grid-filter-cell/grid-filter-cell.scss +14 -0
- package/src/components/grid-filter-cell/grid-filter-cell.template.ts +20 -0
- package/src/components/grid-filter-cell/grid-filter-cell.ts +121 -0
- package/src/components/grid-filter-cell/index.ts +1 -0
- package/src/components/grid-group-row/grid-group-row.scss +24 -0
- package/src/components/grid-group-row/grid-group-row.template.ts +23 -0
- package/src/components/grid-group-row/grid-group-row.ts +94 -0
- package/src/components/grid-group-row/index.ts +1 -0
- package/src/components/grid-header-cell/grid-header-cell.ts +17 -0
- package/src/components/grid-header-cell/index.ts +1 -0
- package/src/components/grid-pagination/grid-pagination.scss +39 -0
- package/src/components/grid-pagination/grid-pagination.template.ts +40 -0
- package/src/components/grid-pagination/grid-pagination.ts +204 -0
- package/src/components/grid-pagination/index.ts +1 -0
- package/src/components/grid-template/grid-template.ts +166 -0
- package/src/components/grid-template/index.ts +1 -0
- package/src/events.ts +824 -0
- package/src/exporter.test.ts +186 -0
- package/src/focus.ts +379 -0
- package/src/framework-slots.ts +440 -0
- package/src/grid-controller.ts +1532 -21
- package/src/icons.ts +88 -0
- package/src/importer.test.ts +147 -0
- package/src/index.integration.test.ts +2 -2
- package/src/index.ts +9 -0
- package/src/infinite-scroll.test.ts +220 -0
- package/src/keyboard-navigation.test.ts +579 -0
- package/src/patch.ts +722 -0
- package/src/render.ts +465 -0
- package/src/row-edit.test.ts +191 -0
- package/src/row-selection.test.ts +550 -0
- package/src/save-state.test.ts +235 -0
- package/src/scss.d.ts +4 -0
- package/src/templates/index.ts +62 -0
- package/src/test-setup.ts +23 -0
- package/src/ui-grid-empty.template.ts +23 -0
- package/src/ui-grid-shell.scss +25 -0
- package/src/ui-grid-shell.template.ts +45 -0
- package/src/ui-grid-standalone.element.ts +751 -1112
- package/src/utils/cell-class.ts +70 -0
- package/src/utils/display-items.ts +15 -0
- package/src/utils/dom.ts +64 -0
- package/src/validate.test.ts +170 -0
- package/tsup.config.ts +10 -0
- package/vite.config.ts +10 -1
package/AGENTS.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# @ornery/ui-grid-vanilla — Agent Instructions
|
|
2
|
+
|
|
3
|
+
The rendering engine. A framework-free custom element (`<ui-grid-element>`) that owns the Shadow DOM, CSS Grid layout, event handling, and the `VanillaGridController` state machine. Angular and React wrappers mount this element — they never render cells themselves.
|
|
4
|
+
|
|
5
|
+
## Build & Test
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm run build --prefix projects/ui-grid-vanilla # tsup → dist/
|
|
9
|
+
npm test --prefix projects/ui-grid-vanilla # vitest (138 tests)
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Depends on `@ornery/ui-grid-core` — build core first.
|
|
13
|
+
|
|
14
|
+
## Key Files
|
|
15
|
+
|
|
16
|
+
| File | Responsibility |
|
|
17
|
+
|------|---------------|
|
|
18
|
+
| `grid-controller.ts` | `VanillaGridController` — all mutable grid state, snapshot emission, column width calculation |
|
|
19
|
+
| `ui-grid-standalone.element.ts` | Custom element class: lifecycle, render orchestration, scroll handling, ResizeObserver |
|
|
20
|
+
| `render.ts` | HTML string builders for header/filter/body cells |
|
|
21
|
+
| `patch.ts` | DOM-diffing fast path: patches existing shadow DOM without full re-render |
|
|
22
|
+
| `events.ts` | All event listeners: click, mousedown (resize drag), dblclick (auto-fit), keydown, scroll, drag-and-drop |
|
|
23
|
+
| `focus.ts` | CellNav focus management, `measureAutoColumnWidth` |
|
|
24
|
+
| `framework-slots.ts` | `FrameworkSlotBridge` — stages slot add/remove deltas, `flush()` dispatches `cellSlotsChanged` |
|
|
25
|
+
| `attribute-bridge.ts` | Parses HTML attributes into `GridOptions` |
|
|
26
|
+
| `templates/index.ts` | Markup fragment helpers (filterRow, bodyVirtual, bodyStatic) |
|
|
27
|
+
| `ui-grid-shell.template.ts` | Full shell template (grid-frame → grid-table → header-strip + filter-strip + body-viewport) |
|
|
28
|
+
|
|
29
|
+
## Architecture
|
|
30
|
+
|
|
31
|
+
### Render Paths
|
|
32
|
+
|
|
33
|
+
1. **`renderFull`** — Full innerHTML swap of the shadow DOM shell. Used on structure changes (column add/remove, virtualization toggle).
|
|
34
|
+
2. **`renderPatch`** — In-place DOM mutation. Header grid-template-columns, filter cells, body rows patched individually. Preserves focus and scroll.
|
|
35
|
+
3. **`renderVirtualBody`** — Scroll-triggered fast path. Only replaces `.grid-virtual-body` innerHTML + calls `frameworkSlots.flush()`.
|
|
36
|
+
4. **`patchCells`** — Data-only update. Iterates existing body cells, updates `.cell-content` innerHTML.
|
|
37
|
+
|
|
38
|
+
### Column Width (drawnWidth)
|
|
39
|
+
|
|
40
|
+
Column widths are resolved to **fixed pixel values** by `buildGridTemplateColumns()`, mirroring the old ui-grid's `updateColumnWidths`/`drawnWidth` approach:
|
|
41
|
+
|
|
42
|
+
- Fixed-px columns: used as-is
|
|
43
|
+
- Percentage columns: resolved against viewport width
|
|
44
|
+
- Flex columns (default `minmax(11rem, 1fr)`): remaining space divided equally, minimum 176px
|
|
45
|
+
- Selection row-header: fixed at configured width, not resizable
|
|
46
|
+
|
|
47
|
+
The element feeds its measured width to the controller via `setViewportWidth()`. The ResizeObserver keeps it current.
|
|
48
|
+
|
|
49
|
+
### State Management
|
|
50
|
+
|
|
51
|
+
`setOptions()` must NOT clobber interactive state. The controller tracks `lastOptions*` values and only applies declarative state when the consumer's value structurally changed. Interactive state that survives `setOptions`: sort, filters, expanded rows, selection, pagination, cell navigation, column widths, column order.
|
|
52
|
+
|
|
53
|
+
### Framework Slot System
|
|
54
|
+
|
|
55
|
+
1. Wrapper calls `setFrameworkRenderedSlots({ cells: ['colA', 'colB'] })`
|
|
56
|
+
2. Render emits `<slot name="cell-colA-rowId">` placeholders
|
|
57
|
+
3. `flush()` dispatches `cellSlotsChanged` with `{ added, removed }` deltas
|
|
58
|
+
4. Wrapper listens, creates framework views, appends to light DOM with matching `slot` attribute
|
|
59
|
+
|
|
60
|
+
## Per-Column Flags
|
|
61
|
+
|
|
62
|
+
- `enableColumnResizing: false` — no resize handle rendered, column width locked
|
|
63
|
+
- `enableColumnMoving: false` — not draggable, not a valid drop target
|
|
64
|
+
|
|
65
|
+
## Do NOT
|
|
66
|
+
|
|
67
|
+
- Skip `frameworkSlots.flush()` after rendering DOM that contains slot placeholders
|
|
68
|
+
- Call `setFrameworkRenderedSlots` on every options update — only on structural column changes
|
|
69
|
+
- Use `innerHTML` on `.grid-body-viewport` — it destroys scroll position; only patch inside `.body-grid`
|
|
70
|
+
- Forget to sync horizontal scroll after a full render (`syncHeaderHorizontalScroll`)
|
package/README.md
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# @ornery/ui-grid-vanilla
|
|
2
|
+
|
|
3
|
+
A vanilla Web Components wrapper for `@ornery/ui-grid-core`. Renders a high-performance data grid as a custom element with no framework dependency.
|
|
4
|
+
|
|
5
|
+
## Architecture
|
|
6
|
+
|
|
7
|
+
The grid is built as a composition of autonomous custom elements, each following the `@ornery/web-components` pattern:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
<ui-grid-element> ← main grid element (shadow DOM)
|
|
11
|
+
├── ui-grid-shell.html ← declarative grid layout template
|
|
12
|
+
├── ui-grid-empty.html ← empty/no-options state template
|
|
13
|
+
│
|
|
14
|
+
├── <ui-grid-header-cell> ← column header (sort, group, pin, resize)
|
|
15
|
+
├── <ui-grid-filter-cell> ← column filter input (shadow DOM + .html template)
|
|
16
|
+
├── <ui-grid-body-cell> ← data cell (class-managed, perf-optimized)
|
|
17
|
+
├── <ui-grid-group-row> ← grouping disclosure row (shadow DOM + .html template)
|
|
18
|
+
├── <ui-grid-pagination> ← pagination controls (shadow DOM + .html template)
|
|
19
|
+
└── <ui-grid-template> ← declarative reusable template factory
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Component Pattern
|
|
23
|
+
|
|
24
|
+
Components follow the standard `@ornery/web-components` lifecycle:
|
|
25
|
+
|
|
26
|
+
```js
|
|
27
|
+
import template from './my-component.html';
|
|
28
|
+
|
|
29
|
+
class MyComponent extends HTMLElement {
|
|
30
|
+
// 1. Attach shadow DOM in constructor
|
|
31
|
+
constructor() {
|
|
32
|
+
super();
|
|
33
|
+
this.attachShadow({ mode: 'open' });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// 2. Declare instance properties as template context
|
|
37
|
+
myProp = 'default';
|
|
38
|
+
|
|
39
|
+
// 3. Render in connectedCallback
|
|
40
|
+
connectedCallback() {
|
|
41
|
+
this.myProp = this.getAttribute('data-my-prop') ?? 'default';
|
|
42
|
+
template(this).connect(); // auto-renders into shadowRoot
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The `.html` template uses ES6 template literal syntax:
|
|
48
|
+
```html
|
|
49
|
+
<div class="my-class">${this.myProp}</div>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Templates are compiled at build time by `@ornery/web-components/vite` (or `/webpack`, `/esbuild`, `/rollup`).
|
|
53
|
+
|
|
54
|
+
### Performance Strategy
|
|
55
|
+
|
|
56
|
+
- **Hot-path components** (`ui-grid-body-cell`, `ui-grid-header-cell`) skip `template().connect()` entirely — the parent pre-renders innerHTML as a string, and the component only manages CSS classes via `attributeChangedCallback`. This avoids DOM construction overhead per cell.
|
|
57
|
+
- **Low-frequency components** (`ui-grid-filter-cell`, `ui-grid-group-row`, `ui-grid-pagination`) use full `template(this).connect()` with shadow DOM for encapsulation and declarative rendering.
|
|
58
|
+
|
|
59
|
+
### Template Bindings
|
|
60
|
+
|
|
61
|
+
Cell and row slot templates support dual binding syntax:
|
|
62
|
+
|
|
63
|
+
| Syntax | Example | Description |
|
|
64
|
+
|--------|---------|-------------|
|
|
65
|
+
| `{{ }}` | `{{value}}` | Mustache-style, original grid syntax |
|
|
66
|
+
| `${ }` | `${this.value}` | ES6 template literal, `@ornery/web-components` native |
|
|
67
|
+
|
|
68
|
+
Both resolve against the same context: `{ $implicit, value, valueText, row, column, rowIndex }`.
|
|
69
|
+
|
|
70
|
+
## Installation
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
npm install @ornery/ui-grid-vanilla @ornery/ui-grid-core
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Usage
|
|
77
|
+
|
|
78
|
+
### Imperative (JavaScript)
|
|
79
|
+
|
|
80
|
+
```js
|
|
81
|
+
import { mountVanillaUiGrid } from '@ornery/ui-grid-vanilla';
|
|
82
|
+
|
|
83
|
+
const grid = await mountVanillaUiGrid(document.getElementById('grid'), {
|
|
84
|
+
id: 'my-grid',
|
|
85
|
+
columnDefs: [
|
|
86
|
+
{ name: 'name', field: 'name', displayName: 'Name' },
|
|
87
|
+
{ name: 'age', field: 'age', displayName: 'Age', align: 'end' },
|
|
88
|
+
],
|
|
89
|
+
data: [
|
|
90
|
+
{ name: 'Alice', age: 30 },
|
|
91
|
+
{ name: 'Bob', age: 25 },
|
|
92
|
+
],
|
|
93
|
+
enableSorting: true,
|
|
94
|
+
enableFiltering: true,
|
|
95
|
+
});
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Declarative (HTML attributes)
|
|
99
|
+
|
|
100
|
+
```html
|
|
101
|
+
<ui-grid-element
|
|
102
|
+
grid-id="my-grid"
|
|
103
|
+
enable-sorting
|
|
104
|
+
enable-filtering
|
|
105
|
+
column-defs='[{"name":"name","field":"name"},{"name":"age","field":"age","align":"end"}]'
|
|
106
|
+
data='[{"name":"Alice","age":30},{"name":"Bob","age":25}]'>
|
|
107
|
+
</ui-grid-element>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Custom Cell Templates (slots)
|
|
111
|
+
|
|
112
|
+
```html
|
|
113
|
+
<ui-grid-element id="grid">
|
|
114
|
+
<!-- Mustache syntax -->
|
|
115
|
+
<template slot="cell-name">
|
|
116
|
+
<strong>{{value}}</strong>
|
|
117
|
+
</template>
|
|
118
|
+
|
|
119
|
+
<!-- ES6 syntax -->
|
|
120
|
+
<template slot="cell-age">
|
|
121
|
+
<span style="color: ${this.value > 30 ? 'red' : 'green'}">${this.value}</span>
|
|
122
|
+
</template>
|
|
123
|
+
</ui-grid-element>
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Declarative Reusable Templates
|
|
127
|
+
|
|
128
|
+
Define reusable components from HTML alone with `<template is="ui-grid-template">`:
|
|
129
|
+
|
|
130
|
+
```html
|
|
131
|
+
<!-- Define a badge component -->
|
|
132
|
+
<template is="ui-grid-template" name="ui-status-badge" status="unknown">
|
|
133
|
+
<style>
|
|
134
|
+
:host { display: inline-block; }
|
|
135
|
+
.badge { padding: 2px 8px; border-radius: 4px; }
|
|
136
|
+
.badge-active { background: #d1fae5; color: #065f46; }
|
|
137
|
+
.badge-inactive { background: #fee2e2; color: #991b1b; }
|
|
138
|
+
</style>
|
|
139
|
+
<span class="badge badge-${this.status}">${this.status}</span>
|
|
140
|
+
</template>
|
|
141
|
+
|
|
142
|
+
<!-- Use it in a grid cell slot -->
|
|
143
|
+
<ui-grid-element>
|
|
144
|
+
<template slot="cell-status">
|
|
145
|
+
<ui-status-badge status="{{value}}"></ui-status-badge>
|
|
146
|
+
</template>
|
|
147
|
+
</ui-grid-element>
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Bundler Setup
|
|
151
|
+
|
|
152
|
+
### Vite
|
|
153
|
+
|
|
154
|
+
```js
|
|
155
|
+
import { defineConfig } from 'vite';
|
|
156
|
+
import webComponents from '@ornery/web-components/vite';
|
|
157
|
+
|
|
158
|
+
export default defineConfig({
|
|
159
|
+
plugins: [webComponents()]
|
|
160
|
+
});
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Webpack 5
|
|
164
|
+
|
|
165
|
+
```js
|
|
166
|
+
import webComponents from '@ornery/web-components/webpack';
|
|
167
|
+
|
|
168
|
+
export default {
|
|
169
|
+
plugins: [webComponents()]
|
|
170
|
+
};
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### tsup (esbuild)
|
|
174
|
+
|
|
175
|
+
```ts
|
|
176
|
+
import { defineConfig } from 'tsup';
|
|
177
|
+
import webComponents from '@ornery/web-components/esbuild';
|
|
178
|
+
|
|
179
|
+
export default defineConfig({
|
|
180
|
+
esbuildPlugins: [webComponents()],
|
|
181
|
+
});
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## API
|
|
185
|
+
|
|
186
|
+
### `mountVanillaUiGrid(target, options, rustModule?, tagName?)`
|
|
187
|
+
Mount a grid into a target element. Returns the grid element.
|
|
188
|
+
|
|
189
|
+
### `defineStandaloneUiGridElement(tagName?)`
|
|
190
|
+
Register the `<ui-grid-element>` custom element (and all sub-components).
|
|
191
|
+
|
|
192
|
+
### `UIGridTemplate`
|
|
193
|
+
Exported class for the `<template is="ui-grid-template">` element. Auto-registered when the grid is defined.
|
|
194
|
+
|
|
195
|
+
### Grid Element Properties
|
|
196
|
+
See `VanillaUiGridElement` type — all `GridOptions` fields are available as both HTML attributes and JS properties.
|
package/demo/main.ts
CHANGED