@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.
Files changed (63) hide show
  1. package/AGENTS.md +70 -0
  2. package/README.md +196 -0
  3. package/demo/main.ts +0 -1
  4. package/dist/index.d.mts +582 -55
  5. package/dist/index.d.ts +582 -55
  6. package/dist/index.js +4959 -1429
  7. package/dist/index.mjs +5137 -1560
  8. package/package.json +11 -8
  9. package/scss-plugin.ts +65 -0
  10. package/src/adopt-styles.ts +39 -0
  11. package/src/attribute-bridge.ts +291 -0
  12. package/src/auto-height.test.ts +142 -0
  13. package/src/auto-resize.test.ts +207 -0
  14. package/src/cell-nav.test.ts +262 -0
  15. package/src/components/grid-body-cell/grid-body-cell.ts +23 -0
  16. package/src/components/grid-body-cell/index.ts +1 -0
  17. package/src/components/grid-cell-editor/grid-cell-editor.ts +104 -0
  18. package/src/components/grid-cell-editor/index.ts +1 -0
  19. package/src/components/grid-filter-cell/grid-filter-cell.scss +14 -0
  20. package/src/components/grid-filter-cell/grid-filter-cell.template.ts +20 -0
  21. package/src/components/grid-filter-cell/grid-filter-cell.ts +121 -0
  22. package/src/components/grid-filter-cell/index.ts +1 -0
  23. package/src/components/grid-group-row/grid-group-row.scss +24 -0
  24. package/src/components/grid-group-row/grid-group-row.template.ts +23 -0
  25. package/src/components/grid-group-row/grid-group-row.ts +94 -0
  26. package/src/components/grid-group-row/index.ts +1 -0
  27. package/src/components/grid-header-cell/grid-header-cell.ts +17 -0
  28. package/src/components/grid-header-cell/index.ts +1 -0
  29. package/src/components/grid-pagination/grid-pagination.scss +39 -0
  30. package/src/components/grid-pagination/grid-pagination.template.ts +40 -0
  31. package/src/components/grid-pagination/grid-pagination.ts +204 -0
  32. package/src/components/grid-pagination/index.ts +1 -0
  33. package/src/components/grid-template/grid-template.ts +166 -0
  34. package/src/components/grid-template/index.ts +1 -0
  35. package/src/events.ts +824 -0
  36. package/src/exporter.test.ts +186 -0
  37. package/src/focus.ts +379 -0
  38. package/src/framework-slots.ts +440 -0
  39. package/src/grid-controller.ts +1532 -21
  40. package/src/icons.ts +88 -0
  41. package/src/importer.test.ts +147 -0
  42. package/src/index.integration.test.ts +2 -2
  43. package/src/index.ts +9 -0
  44. package/src/infinite-scroll.test.ts +220 -0
  45. package/src/keyboard-navigation.test.ts +579 -0
  46. package/src/patch.ts +722 -0
  47. package/src/render.ts +465 -0
  48. package/src/row-edit.test.ts +191 -0
  49. package/src/row-selection.test.ts +550 -0
  50. package/src/save-state.test.ts +235 -0
  51. package/src/scss.d.ts +4 -0
  52. package/src/templates/index.ts +62 -0
  53. package/src/test-setup.ts +23 -0
  54. package/src/ui-grid-empty.template.ts +23 -0
  55. package/src/ui-grid-shell.scss +25 -0
  56. package/src/ui-grid-shell.template.ts +45 -0
  57. package/src/ui-grid-standalone.element.ts +751 -1112
  58. package/src/utils/cell-class.ts +70 -0
  59. package/src/utils/display-items.ts +15 -0
  60. package/src/utils/dom.ts +64 -0
  61. package/src/validate.test.ts +170 -0
  62. package/tsup.config.ts +10 -0
  63. 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
@@ -24,7 +24,6 @@ const options: GridOptions = {
24
24
  title: 'UI Grid Vanilla Demo',
25
25
  emptyMessage: 'No rows match the current filters.',
26
26
  rowHeight: 48,
27
- viewportHeight: 620,
28
27
  enableSorting: true,
29
28
  enableFiltering: true,
30
29
  enableGrouping: true,