@taskctrl/canvas-grid 0.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 ADDED
@@ -0,0 +1,176 @@
1
+ # @taskctrl/canvas-grid
2
+
3
+ A React data grid that renders to stacked HTML `<canvas>` layers instead of DOM cells. Built for dense, tree-structured matrices (think a status grid of systems × disciplines) where thousands of cells would make a DOM grid sluggish.
4
+
5
+ - **Canvas rendering** — cells are drawn imperatively, so cell count barely affects performance.
6
+ - **Tree rows** — parent/child nesting with expand/collapse, shown in a sidebar or hosted on a pinned column.
7
+ - **Pinned & scrollable columns**, column groups, resize, reorder, and show/hide.
8
+ - **Virtualized** — only the visible row/column range is drawn.
9
+ - **Selection** — single, ctrl-toggle, and shift-rectangle, plus full keyboard navigation.
10
+ - **Accessible** — `role="grid"`, ARIA live announcements, keyboard-driven.
11
+ - **Export to image** — render the full grid to an offscreen canvas at retina scale.
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ yarn add @taskctrl/canvas-grid
17
+ # or
18
+ npm install @taskctrl/canvas-grid
19
+ ```
20
+
21
+ `react` and `react-dom` (>=17) are peer dependencies.
22
+
23
+ ## Usage
24
+
25
+ You provide three things: the `rows`/`columns` shape, a `getCellData` function that returns the raw value for a cell, and a `cellRenderer` that imperatively draws that cell onto the canvas.
26
+
27
+ ```tsx
28
+ import { CanvasGrid } from '@taskctrl/canvas-grid'
29
+ import type { Row, Column, CellRenderer } from '@taskctrl/canvas-grid'
30
+
31
+ const rows: Row[] = [
32
+ { id: 'floor1', title: 'Floor 1' },
33
+ { id: 'sys101', parentId: 'floor1', title: '101 - Main Panel' },
34
+ { id: 'sys102', parentId: 'floor1', title: '102 - Sub Panel A' },
35
+ ]
36
+
37
+ const columns: Column[] = [
38
+ { id: 'power', title: 'Power', width: 60 },
39
+ { id: 'vent', title: 'Ventilation', width: 60 },
40
+ ]
41
+
42
+ const statusColors: Record<string, string> = {
43
+ complete: '#22c55e',
44
+ in_progress: '#f59e0b',
45
+ issue: '#ef4444',
46
+ }
47
+
48
+ const getCellData = (rowId: string | number, colId: string) => {
49
+ return { status: 'complete' } // look up your real data here
50
+ }
51
+
52
+ const cellRenderer: CellRenderer = (ctx, cell, bounds, state, helpers) => {
53
+ const status = (cell.data as { status: string })?.status ?? 'empty'
54
+ const color = statusColors[status] ?? '#e5e7eb'
55
+ const size = Math.min(bounds.width, bounds.height) - 12
56
+ const x = bounds.x + (bounds.width - size) / 2
57
+ const y = bounds.y + (bounds.height - size) / 2
58
+ helpers.roundRect(x, y, size, size, 3, color)
59
+ }
60
+
61
+ function Grid() {
62
+ return (
63
+ <div style={{ width: '100%', height: 500 }}>
64
+ <CanvasGrid
65
+ rows={rows}
66
+ columns={columns}
67
+ getCellData={getCellData}
68
+ cellRenderer={cellRenderer}
69
+ defaultExpandedRows={['floor1']}
70
+ />
71
+ </div>
72
+ )
73
+ }
74
+ ```
75
+
76
+ The grid sizes itself to its content but is capped at the dimensions of its parent, so wrap it in a sized container.
77
+
78
+ ## The cell renderer
79
+
80
+ `cellRenderer(ctx, cell, bounds, state, helpers)` is called for every visible cell:
81
+
82
+ - `ctx` — the `CanvasRenderingContext2D` (already clipped to the cell rect).
83
+ - `cell` — `{ rowId, colId, data }`, where `data` is whatever `getCellData` returned.
84
+ - `bounds` — `{ x, y, width, height }` of the cell in canvas pixels.
85
+ - `state` — `{ selected, hovered, rowSelected, colSelected, depth, hasChildren, expanded, ... }`.
86
+ - `helpers` — drawing primitives so you don't reach for raw canvas APIs:
87
+
88
+ | Helper | Purpose |
89
+ | --- | --- |
90
+ | `roundRect(x, y, w, h, r, fill)` | filled rounded rectangle |
91
+ | `fillText(text, x, y, maxWidth?)` | draw text |
92
+ | `truncateText(text, maxWidth)` | returns text truncated with an ellipsis to fit |
93
+ | `badge(x, y, text, bg, fg)` | pill badge, returns its width |
94
+ | `progressBar(x, y, w, h, percent, color)` | track + fill bar (`percent` 0–1) |
95
+
96
+ ## Tree rows
97
+
98
+ A row becomes a child by setting `parentId` to another row's `id`. `defaultExpandedRows` sets the initially expanded rows. Without pinned columns the tree is shown in the left **sidebar**; if any column is `pinned: 'left'`, the first pinned column hosts the expand/collapse toggle instead and the sidebar is hidden.
99
+
100
+ Expansion is managed internally by default. Pass `onTreeToggle` to take over toggling, or `onExpandChange` to observe it.
101
+
102
+ ## Pinned columns & groups
103
+
104
+ ```tsx
105
+ const columns: Column[] = [
106
+ { id: 'name', title: 'System', width: 200, pinned: 'left' },
107
+ { id: 'power', title: 'Power', width: 60, group: 'electrical' },
108
+ { id: 'vent', title: 'Ventilation', width: 60, group: 'hvac' },
109
+ ]
110
+
111
+ const columnGroups = [
112
+ { id: 'electrical', title: 'Electrical', color: '#6366f1' },
113
+ { id: 'hvac', title: 'HVAC', color: '#059669' },
114
+ ]
115
+
116
+ <CanvasGrid columns={columns} columnGroups={columnGroups} ... />
117
+ ```
118
+
119
+ ## Export to image
120
+
121
+ Grab a ref and call `captureToCanvas()` to render the **entire** grid (not just the visible range) to an offscreen `<canvas>`, e.g. for screenshots or printing:
122
+
123
+ ```tsx
124
+ import { useRef } from 'react'
125
+ import { CanvasGrid } from '@taskctrl/canvas-grid'
126
+ import type { CanvasGridRef } from '@taskctrl/canvas-grid'
127
+
128
+ const ref = useRef<CanvasGridRef>(null)
129
+
130
+ // later:
131
+ const canvas = ref.current!.captureToCanvas({ scale: 2 }) // 2 = retina
132
+ const dataUrl = canvas.toDataURL('image/png')
133
+
134
+ <CanvasGrid ref={ref} ... />
135
+ ```
136
+
137
+ ## Props
138
+
139
+ Common props (see the exported `CanvasGridProps` type for the full list):
140
+
141
+ | Prop | Type | Notes |
142
+ | --- | --- | --- |
143
+ | `rows` | `Row[]` | `{ id, parentId?, ...your fields }` |
144
+ | `columns` | `Column[]` | `{ id, title, width, pinned?, group?, hidden?, minWidth? }` |
145
+ | `getCellData` | `(rowId, colId) => unknown` | raw value passed to the renderer |
146
+ | `cellRenderer` | `CellRenderer` | draws each cell |
147
+ | `columnGroups?` | `ColumnGroup[]` | banded header above columns |
148
+ | `sidebarRenderer?` | render the tree column yourself |
149
+ | `rowHeight?` | `number` | default 32 |
150
+ | `sidebarWidth?` | `number` | default 180 (ignored when columns are pinned) |
151
+ | `headerOrientation?` | `'horizontal' \| 'vertical'` | vertical headers are taller |
152
+ | `selection` / `onSelectionChange` | controlled selection |
153
+ | `onCellClick` / `onCellDoubleClick` / `onCellContextMenu` / `onCellHover` | cell events |
154
+ | `onColumnResize` / `onColumnReorder` / `onColumnVisibilityChange` | column edits (the grid also applies them locally) |
155
+ | `onLoadMore` / `hasMore` / `loading` | infinite scroll near the bottom |
156
+ | `showColumnVisibilityMenu?` | built-in column show/hide menu |
157
+
158
+ ## Keyboard
159
+
160
+ Arrow keys move the selection; **Shift+Arrow** extends a rectangle; **Tab**/**Shift+Tab** move with row wrap; **Home**/**End** jump to row start/end (with **Ctrl** for grid corners); **Ctrl/Cmd+A** selects all; **Esc** clears.
161
+
162
+ ## Development
163
+
164
+ ```bash
165
+ yarn install
166
+ yarn test # Vitest (jsdom)
167
+ yarn test:watch
168
+ yarn storybook dev -p 6006 # interactive examples in stories/
169
+ yarn build # library build -> dist/ (ES + CJS + .d.ts)
170
+ ```
171
+
172
+ See [CLAUDE.md](./CLAUDE.md) for an architecture overview (the three-layer canvas, the core engines, and the render scheduler).
173
+
174
+ ## License
175
+
176
+ UNLICENSED — internal taskctrl package.
@@ -0,0 +1,3 @@
1
+ import { default as React } from 'react';
2
+ import { CanvasGridProps, CanvasGridRef } from './types';
3
+ export declare const CanvasGrid: React.MemoExoticComponent<React.ForwardRefExoticComponent<CanvasGridProps & React.RefAttributes<CanvasGridRef>>>;
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const T=require("react/jsx-runtime"),m=require("react"),Yt=require("react-dom");class Ue{constructor(e){this.scrollX=0,this.scrollY=0,this.canvasWidth=0,this.canvasHeight=0,this.rowHeight=32,this.pinnedWidth=0,this._columnOffsets=[],this._columnWidths=[],this._pinnedOffsets=[],this._pinnedWidths=[],this._scrollableOffsets=[],this._scrollableWidths=[],e&&this.update(e)}update(e){e.scrollX!==void 0&&(this.scrollX=e.scrollX),e.scrollY!==void 0&&(this.scrollY=e.scrollY),e.canvasWidth!==void 0&&(this.canvasWidth=e.canvasWidth),e.canvasHeight!==void 0&&(this.canvasHeight=e.canvasHeight),e.rowHeight!==void 0&&(this.rowHeight=e.rowHeight)}setColumnOffsets(e){this._columnOffsets=e}setColumnWidths(e){this._columnWidths=e}setPinnedOffsets(e,o){this._pinnedOffsets=e,this._pinnedWidths=o}setScrollableOffsets(e,o){this._scrollableOffsets=e,this._scrollableWidths=o}rowToY(e){return e*this.rowHeight-this.scrollY}yToRow(e){return Math.floor((e+this.scrollY)/this.rowHeight)}colToX(e,o){return o===!0?this._pinnedOffsets[e]??0:o===!1?(this._scrollableOffsets[e]??0)-this.scrollX+this.pinnedWidth:(this._columnOffsets[e]??0)-this.scrollX}xToColPinned(e){for(let n=0;n<this._pinnedOffsets.length;n++){const r=this._pinnedOffsets[n],d=this._pinnedWidths[n]??0;if(e>=r&&e<r+d)return{index:n,pinned:!0}}const o=e-this.pinnedWidth+this.scrollX;for(let n=0;n<this._scrollableOffsets.length;n++){const r=this._scrollableOffsets[n],d=this._scrollableWidths[n]??0;if(o>=r&&o<r+d)return{index:n,pinned:!1}}return null}xToCol(e){const o=e+this.scrollX;for(let n=0;n<this._columnOffsets.length;n++){const r=this._columnOffsets[n],d=this._columnWidths[n]??0;if(o>=r&&o<r+d)return n}return-1}}const mt=["grid","cells","overlay"];class Vt{constructor(e){this._dirty={grid:!1,cells:!1,overlay:!1},this._rafId=null,this._drawFn=e}markDirty(e){this._dirty[e]=!0,this._rafId===null&&(this._rafId=requestAnimationFrame(()=>this._flush()))}markAllDirty(){for(const e of mt)this._dirty[e]=!0;this._rafId===null&&(this._rafId=requestAnimationFrame(()=>this._flush()))}destroy(){this._rafId!==null&&(cancelAnimationFrame(this._rafId),this._rafId=null)}_flush(){this._rafId=null;for(const e of mt)this._dirty[e]&&(this._dirty[e]=!1,this._drawFn(e))}}class xt{constructor(e,o){this._rows=e,this._expandedRows=o,this._depthMap=new Map,this._childrenMap=new Map,this._buildMaps(),this._flattenedRows=this._buildFlattenedRows()}_buildMaps(){for(const n of this._rows)this._childrenMap.has(n.id)||this._childrenMap.set(n.id,[]),n.parentId!==void 0&&(this._childrenMap.has(n.parentId)||this._childrenMap.set(n.parentId,[]),this._childrenMap.get(n.parentId).push(n));const o=this._rows.filter(n=>n.parentId===void 0).map(n=>({row:n,depth:0}));for(;o.length>0;){const{row:n,depth:r}=o.pop();this._depthMap.set(n.id,r);const d=this._childrenMap.get(n.id)??[];for(const a of d)o.push({row:a,depth:r+1})}}_buildFlattenedRows(){const e=[],o=this._rows.filter(r=>r.parentId===void 0),n=r=>{if(e.push(r),this._expandedRows.has(r.id)){const d=this._childrenMap.get(r.id)??[];for(const a of d)n(a)}};for(const r of o)n(r);return e}get flattenedRows(){return this._flattenedRows}getDepth(e){return this._depthMap.get(e)??0}hasChildren(e){const o=this._childrenMap.get(e);return o!==void 0&&o.length>0}isExpanded(e){return this._expandedRows.has(e)}toggle(e){const o=new Set(this._expandedRows);return o.has(e)?o.delete(e):o.add(e),o}getVisibleRange(e,o,n){if(n<=0||this._flattenedRows.length===0)return{startIndex:0,endIndex:0};const r=Math.max(0,Math.floor(e/n)),d=Math.min(this._flattenedRows.length-1,Math.ceil((e+o)/n)-1);return{startIndex:r,endIndex:d}}}const Gt=30;class me{constructor(e){this._columns=e}getVisibleColumns(){return this._columns.filter(e=>!e.hidden)}getPinnedColumns(){return this._columns.filter(e=>!e.hidden&&e.pinned==="left")}getScrollableColumns(){return this._columns.filter(e=>!e.hidden&&e.pinned!=="left")}getPinnedWidth(){return this.getPinnedColumns().reduce((e,o)=>e+o.width,0)}getAllColumns(){return[...this._columns]}getTotalWidth(){return this.getVisibleColumns().reduce((e,o)=>e+o.width,0)}getColumnX(e){const o=this.getPinnedColumns(),n=this.getScrollableColumns();let r=0;for(const d of o){if(d.id===e)return r;r+=d.width}for(const d of n){if(d.id===e)return r;r+=d.width}return-1}getColumnAtX(e){let o=0;for(const n of this.getPinnedColumns()){if(e>=o&&e<o+n.width)return n.id;o+=n.width}for(const n of this.getScrollableColumns()){if(e>=o&&e<o+n.width)return n.id;o+=n.width}return null}getVisibleRange(e,o){const n=this.getScrollableColumns();if(n.length===0)return{startIndex:0,endIndex:0};const r=this.getPinnedWidth(),d=o-r;let a=0,w=0,f=n.length-1;for(let S=0;S<n.length;S++)a+n[S].width<=e&&(w=S+1),a<e+d&&(f=S),a+=n[S].width;return w=Math.min(w,n.length-1),f=Math.min(f,n.length-1),{startIndex:w,endIndex:f}}getPinnedRange(){const e=this.getPinnedColumns();return e.length===0?{startIndex:0,endIndex:-1}:{startIndex:0,endIndex:e.length-1}}resize(e,o){const n=this._columns.map(r=>{if(r.id!==e)return r;const d=r.minWidth??Gt;return{...r,width:Math.max(d,o)}});return new me(n)}reorder(e,o){const n=[...this._columns],r=n.findIndex(w=>w.id===e);if(r===-1)return new me(n);const[d]=n.splice(r,1),a=Math.max(0,Math.min(o,n.length));return n.splice(a,0,d),new me(n)}hide(e){const o=this._columns.map(n=>n.id===e?{...n,hidden:!0}:n);return new me(o)}show(e){const o=this._columns.map(n=>n.id===e?{...n,hidden:!1}:n);return new me(o)}}function xe(t,e){return`${t}|${e}`}function $e(){return{cells:new Set,rows:new Set,columns:new Set,anchor:void 0}}class yt{constructor(e){e?this._selection={cells:new Set(e.cells),rows:new Set(e.rows),columns:new Set(e.columns),anchor:e.anchor}:this._selection=$e()}click(e,o,n,r){this._selection=$e(),this._selection.cells.add(xe(e,o)),this._selection.anchor={rowId:e,colId:o}}ctrlClick(e,o){const n=xe(e,o);this._selection.cells.has(n)?this._selection.cells.delete(n):this._selection.cells.add(n),this._selection.anchor={rowId:e,colId:o}}shiftClick(e,o,n,r){const d=this._selection.anchor;if(!d){this.click(e,o,n,r);return}const a=n.indexOf(d.rowId),w=n.indexOf(e),f=r.indexOf(d.colId),S=r.indexOf(o),y=Math.min(a,w),L=Math.max(a,w),j=Math.min(f,S),X=Math.max(f,S),k=new Set;for(let G=y;G<=L;G++)for(let $=j;$<=X;$++)k.add(xe(n[G],r[$]));this._selection.cells=k,this._selection.rows=new Set,this._selection.columns=new Set}selectRow(e,o){for(const n of o)this._selection.cells.add(xe(e,n));this._selection.rows.add(e),this._selection.anchor={rowId:e,colId:o[0]??""}}selectColumn(e,o){for(const n of o)this._selection.cells.add(xe(n,e));this._selection.columns.add(e),this._selection.anchor={rowId:o[0]??"",colId:e}}clear(){this._selection=$e()}isSelected(e,o){return this._selection.cells.has(xe(e,o))}isRowSelected(e){return this._selection.rows.has(e)}isColumnSelected(e){return this._selection.columns.has(e)}getSelection(){return{cells:new Set(this._selection.cells),rows:new Set(this._selection.rows),columns:new Set(this._selection.columns),anchor:this._selection.anchor}}}function Le(t,e,o,n,r){const d=o.yToRow(e),a=n.flattenedRows;if(d<0||d>=a.length)return null;const w=r.getPinnedColumns(),f=r.getPinnedWidth();if(w.length>0){if(t<f){let X=0;for(const k of w){if(t>=X&&t<X+k.width)return{rowId:a[d].id,colId:k.id};X+=k.width}return null}const y=t-f+o.scrollX,L=r.getScrollableColumns();let j=0;for(const X of L){if(y>=j&&y<j+X.width)return{rowId:a[d].id,colId:X.id};j+=X.width}return null}const S=r.getColumnAtX(t+o.scrollX);return S===null?null:{rowId:a[d].id,colId:S}}function $t(t,e,o){const n=window.devicePixelRatio||1;(t.width!==Math.round(e*n)||t.height!==Math.round(o*n))&&(t.width=Math.round(e*n),t.height=Math.round(o*n),t.style.width=`${e}px`,t.style.height=`${o}px`);const d=t.getContext("2d");return d.setTransform(n,0,0,n,0,0),d}function It(t,e,o,n,r={}){const{canvasWidth:d,canvasHeight:a,rowHeight:w}=e,{rowStyle:f}=r;t.clearRect(0,0,d,a),t.fillStyle="#f3f4f6",t.fillRect(0,0,d,a);const S=o.getVisibleRange(e.scrollY,a,w),y=o.flattenedRows,L=n.getPinnedColumns(),j=n.getScrollableColumns(),X=n.getPinnedWidth(),k=L.length>0;if(y.length===0||L.length===0&&j.length===0)return;const G=n.getTotalWidth(),$=Math.min(d,G-e.scrollX+(k?e.scrollX:0)),P=k?Math.min(d,X+n.getScrollableColumns().reduce((W,x)=>W+x.width,0)-e.scrollX):$,s=y.length*w,Y=Math.min(a,s-e.scrollY);for(let W=S.startIndex;W<=S.endIndex;W++){const x=y[W];if(!x)continue;const H=e.rowToY(W),b=o.getDepth(x.id);let h=W%2===0?"#ffffff":"#f9fafb";if(f){const u=f(x,b);u!=null&&u.backgroundColor&&(h=u.backgroundColor)}t.fillStyle=h,t.fillRect(0,H,Math.max(P,X),w),t.strokeStyle="#d1d5db",t.lineWidth=.5,t.beginPath(),t.moveTo(0,H+w),t.lineTo(Math.max(P,X),H+w),t.stroke()}if(Y>0){const W=n.getVisibleRange(e.scrollX,d);t.strokeStyle="#d1d5db",t.lineWidth=.5;for(let x=W.startIndex;x<=W.endIndex;x++){const H=j[x];if(!H)continue;const b=e.colToX(x,!1);t.beginPath(),t.moveTo(b+H.width,0),t.lineTo(b+H.width,Y),t.stroke()}}if(k&&Y>0){t.fillStyle="#ffffff",t.fillRect(0,0,X,a);for(let x=S.startIndex;x<=S.endIndex;x++){const H=y[x];if(!H)continue;const b=e.rowToY(x),h=o.getDepth(H.id);let u=x%2===0?"#ffffff":"#f9fafb";if(f){const I=f(H,h);I!=null&&I.backgroundColor&&(u=I.backgroundColor)}t.fillStyle=u,t.fillRect(0,b,X,w),t.strokeStyle="#d1d5db",t.lineWidth=.5,t.beginPath(),t.moveTo(0,b+w),t.lineTo(X,b+w),t.stroke()}const W=n.getPinnedRange();t.strokeStyle="#d1d5db",t.lineWidth=.5;for(let x=W.startIndex;x<=W.endIndex;x++){const H=L[x];if(!H)continue;const b=e.colToX(x,!0);t.beginPath(),t.moveTo(b+H.width,0),t.lineTo(b+H.width,Y),t.stroke()}t.strokeStyle="#d1d5db",t.lineWidth=1,t.beginPath(),t.moveTo(X,0),t.lineTo(X,Y),t.stroke()}}function Rt(t){function e(a,w,f,S,y,L){t.fillStyle=L,t.beginPath(),t.roundRect(a,w,f,S,y),t.fill()}function o(a,w,f,S){t.fillText(a,w,f,S)}function n(a,w){if(t.measureText(a).width<=w)return a;let f=a;for(;f.length>0;)if(f=f.slice(0,-1),t.measureText(f+"…").width<=w)return f+"…";return"…"}function r(a,w,f,S,y){const k=t.measureText(f).width+16;return e(a,w,k,20,20/2,S),t.fillStyle=y,t.fillText(f,a+8,w+20/2),k}function d(a,w,f,S,y,L){e(a,w,f,S,S/2,"#d1d5db");const j=f*Math.max(0,Math.min(1,y));j>0&&e(a,w,j,S,S/2,L)}return{roundRect:e,fillText:o,truncateText:n,badge:r,progressBar:d}}function St(t,e,o,n,r,d){const{canvasWidth:a,canvasHeight:w,rowHeight:f}=e,{cellRenderer:S,getCellData:y,hoveredCell:L}=d;t.clearRect(0,0,a,w);const j=o.getVisibleRange(e.scrollY,w,f),X=o.flattenedRows,k=n.getPinnedColumns(),G=n.getScrollableColumns(),$=k.length>0;if(X.length===0||k.length===0&&G.length===0)return;const P=Rt(t),s=(W,x,H,b)=>{const h={x:H,y:b,width:x.width,height:f},u={rowId:W.id,colId:x.id,data:y(W.id,x.id)},I={selected:r.isSelected(W.id,x.id),hovered:L!=null&&L.rowId===W.id&&L.colId===x.id,rowSelected:r.isRowSelected(W.id),colSelected:r.isColumnSelected(x.id),filtered:!0,depth:o.getDepth(W.id),hasChildren:o.hasChildren(W.id),expanded:o.isExpanded(W.id)};t.save(),t.beginPath(),t.rect(H,b,x.width,f),t.clip(),S(t,u,h,I,P),t.restore()},Y=n.getVisibleRange(e.scrollX,a);for(let W=j.startIndex;W<=j.endIndex;W++){const x=X[W];if(!x)continue;const H=e.rowToY(W);for(let b=Y.startIndex;b<=Y.endIndex;b++){const h=G[b];if(!h)continue;const u=e.colToX(b,$?!1:void 0);s(x,h,u,H)}}if($){n.getPinnedWidth();const W=n.getPinnedRange();for(let x=j.startIndex;x<=j.endIndex;x++){const H=X[x];if(!H)continue;const b=e.rowToY(x);t.save(),t.fillStyle="#ffffff",t.restore();for(let h=W.startIndex;h<=W.endIndex;h++){const u=k[h];if(!u)continue;const I=e.colToX(h,!0);s(H,u,I,b)}}}}function Bt(t,e,o,n,r,d={}){const{canvasWidth:a,canvasHeight:w,rowHeight:f}=e,{hoveredCell:S,hoveredRowId:y,selectedCell:L,resizingColumn:j,loading:X}=d;t.clearRect(0,0,a,w);const k=o.getVisibleRange(e.scrollY,w,f),G=o.flattenedRows,$=n.getPinnedColumns(),P=n.getScrollableColumns(),s=$.length>0;if(n.getPinnedWidth(),G.length===0||$.length===0&&P.length===0)return;const Y=n.getVisibleRange(e.scrollX,a),W=n.getPinnedRange(),x=b=>{for(let h=Y.startIndex;h<=Y.endIndex;h++){const u=P[h];if(!u)continue;const I=s?e.colToX(h,!1):e.colToX(h);b(u,I)}if(s)for(let h=W.startIndex;h<=W.endIndex;h++){const u=$[h];if(!u)continue;const I=e.colToX(h,!0);b(u,I)}};if(L){for(let b=k.startIndex;b<=k.endIndex;b++){const h=G[b];if(!h||h.id!==L.rowId)continue;const u=e.rowToY(b);x((I,F)=>{t.fillStyle="rgba(59,130,246,0.06)",t.fillRect(F,u,I.width,f)});break}x((b,h)=>{if(b.id===L.colId)for(let u=k.startIndex;u<=k.endIndex;u++){if(!G[u])continue;const F=e.rowToY(u);t.fillStyle="rgba(59,130,246,0.06)",t.fillRect(h,F,b.width,f)}})}const H=(b,h,u,I,F)=>{const B=b.width,K=f,c=r.isSelected(u.id,b.id),D=r.isRowSelected(u.id),E=r.isColumnSelected(b.id),z=y!=null&&y===u.id,V=S!=null&&S.rowId===u.id&&S.colId===b.id;c?(t.fillStyle="rgba(59,130,246,0.12)",t.fillRect(h,I,B,K),t.strokeStyle="#3b82f6",t.lineWidth=2,t.strokeRect(h+1,I+1,B-2,K-2)):D||E||V?(t.fillStyle="rgba(59,130,246,0.08)",t.fillRect(h,I,B,K)):z&&(t.fillStyle="rgba(59,130,246,0.04)",t.fillRect(h,I,B,K))};for(let b=k.startIndex;b<=k.endIndex;b++){const h=G[b];if(!h)continue;const u=e.rowToY(b);for(let I=Y.startIndex;I<=Y.endIndex;I++){const F=P[I];if(!F)continue;const B=s?e.colToX(I,!1):e.colToX(I);H(F,B,h,u)}}if(s)for(let b=k.startIndex;b<=k.endIndex;b++){const h=G[b];if(!h)continue;const u=e.rowToY(b);for(let I=W.startIndex;I<=W.endIndex;I++){const F=$[I];if(!F)continue;const B=e.colToX(I,!0);H(F,B,h,u)}}if(j&&(t.save(),t.strokeStyle="#3b82f6",t.lineWidth=1,t.setLineDash([4,4]),t.beginPath(),t.moveTo(j.x,0),t.lineTo(j.x,w),t.stroke(),t.setLineDash([]),t.restore()),X){const h=w-32;t.fillStyle="#f3f4f6",t.fillRect(0,h,a,32),t.strokeStyle="#e5e7eb",t.lineWidth=1,t.beginPath(),t.moveTo(0,h),t.lineTo(a,h),t.stroke(),t.fillStyle="#6b7280",t.font="13px system-ui, sans-serif",t.textAlign="center",t.textBaseline="middle",t.fillText("Loading...",a/2,h+32/2)}}function Kt({treeEngine:t,width:e,rowHeight:o,scrollTop:n,viewportHeight:r,sidebarRenderer:d,onToggle:a,onRowClick:w,hoveredRowId:f,onRowHover:S}){const y=t.flattenedRows,{startIndex:L,endIndex:j}=t.getVisibleRange(n,r,o),X=[];for(let $=L;$<=j;$++){const P=y[$];if(!P)continue;const s=t.getDepth(P.id),Y=t.hasChildren(P.id),W=t.isExpanded(P.id),x=$*o;let H;if(d)H=d(P,s,W,Y);else{const h=s*16+8;H=T.jsxs("div",{style:{display:"flex",alignItems:"center",height:"100%",paddingLeft:h,overflow:"hidden",whiteSpace:"nowrap",cursor:Y?"pointer":"default",userSelect:"none"},onPointerDown:Y?u=>{u.stopPropagation(),a(P.id)}:void 0,children:[Y?T.jsx("span",{style:{marginRight:4,flexShrink:0,fontSize:10,color:"#6b7280"},children:W?"▾":"▸"}):T.jsx("span",{style:{width:14,flexShrink:0}}),T.jsx("span",{style:{overflow:"hidden",textOverflow:"ellipsis",fontSize:12},children:String(P.title??P.id)})]})}const b=f===P.id;X.push(T.jsx("div",{role:"rowheader","aria-level":s+1,"aria-expanded":Y?W:void 0,style:{position:"absolute",top:x,left:0,width:"100%",height:o,boxSizing:"border-box",fontSize:13,color:"#374151",borderBottom:"1px solid #e5e7eb",cursor:w?"pointer":"default",backgroundColor:b?"rgba(59,130,246,0.04)":void 0},onPointerDown:w?h=>w(P.id,h):void 0,onPointerEnter:()=>S==null?void 0:S(P.id),onPointerLeave:()=>S==null?void 0:S(null),children:H},String(P.id)))}const k=y.length*o,G=Math.min(r,k-n);return T.jsxs("div",{style:{width:e,height:r,overflow:"hidden",position:"relative",flexShrink:0,backgroundColor:"#ffffff"},children:[T.jsx("div",{style:{position:"absolute",top:0,right:0,width:1,height:Math.max(0,G),backgroundColor:"#e5e7eb",zIndex:1}}),T.jsx("div",{style:{position:"absolute",top:0,left:0,width:"100%",height:k,transform:`translateY(-${n}px)`},children:X})]})}const Be=28,Ke=28,Ft=4;function Ut(t){switch(t){case"vertical":return 120;default:return 32}}function Nt({columnEngine:t,columnGroups:e,scrollX:o,orientation:n="horizontal",onColumnClick:r,onColumnResize:d,onColumnReorder:a,columnHeaderRenderer:w}){const f=t.getPinnedColumns(),S=t.getScrollableColumns(),y=t.getVisibleColumns(),L=t.getPinnedWidth(),j=new Map;if(e)for(const c of e)j.set(c.id,c);const X=m.useRef(null),[k,G]=m.useState(null),$=m.useRef(null),P=m.useRef(!1),s=m.useRef(null),Y=m.useRef(null);$.current=k;const W=[...f,...S],x=[];if(e&&e.length>0){let c,D=0,E=0,z=!1;for(const V of W){const J=t.getColumnX(V.id),Q=V.pinned==="left";if(V.group!==c){if(c!==void 0&&E>0){const Z=j.get(c);Z&&x.push({group:Z,startX:D,width:E,pinned:z})}c=V.group,D=J,E=V.width,z=Q}else E+=V.width}if(c!==void 0&&E>0){const V=j.get(c);V&&x.push({group:V,startX:D,width:E,pinned:z})}}const H=x.length>0,b=Ut(n),h=H?Be+b:b,u=m.useCallback((c,D)=>{D.stopPropagation(),D.preventDefault();const E=y.find(z=>z.id===c);E&&(X.current={colId:c,startX:D.clientX,startWidth:E.width})},[y]);m.useEffect(()=>{const c=E=>{const z=X.current;if(!z)return;const V=E.clientX-z.startX,J=Math.max(z.startWidth+V,30);d==null||d(z.colId,J)},D=()=>{X.current=null};return document.addEventListener("mousemove",c),document.addEventListener("mouseup",D),()=>{document.removeEventListener("mousemove",c),document.removeEventListener("mouseup",D)}},[d]);const I=m.useCallback(c=>{const D=Y.current;if(!D)return{dropIndex:0};const E=D.getBoundingClientRect(),z=c-E.left+o;let V=0;for(let J=0;J<y.length;J++){const Q=y[J],le=t.getColumnX(Q.id)+Q.width/2;z>le&&(V=J+1)}return{dropIndex:V}},[y,t,o]),F=m.useCallback((c,D)=>{D.target.dataset.resizeHandle||a&&(D.preventDefault(),P.current=!1,s.current={colId:c,startX:D.clientX,currentX:D.clientX})},[a]);m.useEffect(()=>{if(!a)return;const c=z=>{const V=s.current;if(!V)return;const J=Math.abs(z.clientX-V.startX);if(!P.current&&J>=Ft){P.current=!0;const Q=y.find(We=>We.id===V.colId),Z=t.getColumnX(V.colId),{dropIndex:le}=I(z.clientX);G({colId:V.colId,startX:V.startX,currentX:z.clientX,dropIndex:le,ghostLeft:Z-o,ghostWidth:(Q==null?void 0:Q.width)??80,ghostTitle:(Q==null?void 0:Q.title)??""});return}if(P.current){const{dropIndex:Q}=I(z.clientX);G(Z=>Z&&{...Z,currentX:z.clientX,dropIndex:Q})}},D=()=>{P.current&&$.current&&a($.current.colId,$.current.dropIndex),s.current=null,P.current=!1,G(null)},E=z=>{z.key==="Escape"&&(s.current=null,P.current=!1,G(null))};return document.addEventListener("mousemove",c),document.addEventListener("mouseup",D),document.addEventListener("keydown",E),()=>{document.removeEventListener("mousemove",c),document.removeEventListener("mouseup",D),document.removeEventListener("keydown",E)}},[a,y,t,o,I]);const B=()=>{if(!k)return 0;const{dropIndex:c}=k;if(c>=y.length){const D=y[y.length-1];return t.getColumnX(D.id)+D.width-o}return t.getColumnX(y[c].id)-o},K=(c,D,E)=>{const z=(k==null?void 0:k.colId)===c.id,J=c.pinned==="left"?"horizontal":n,Q=w==null?void 0:w(c.id,c.title);let Z;return Q?Z=Q:J==="vertical"?Z=T.jsx("div",{style:{writingMode:"vertical-rl",textOrientation:"mixed",transform:"rotate(180deg)",height:E-16,lineHeight:`${c.width}px`,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:c.title}):Z=T.jsx("span",{style:{overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap",flex:1},children:c.title}),T.jsxs("div",{role:"columnheader",title:c.title,style:{position:"absolute",left:D,width:c.width,height:E,boxSizing:"border-box",borderRight:"1px solid #d1d5db",display:"flex",alignItems:J==="horizontal"?"center":"flex-end",justifyContent:J==="horizontal"?"flex-start":"center",paddingLeft:J==="horizontal"?8:0,paddingBottom:J!=="horizontal"?6:0,cursor:a?"grab":r?"pointer":"default",userSelect:"none",fontSize:13,fontWeight:500,color:"#6C737F",opacity:z?.4:1},onMouseDown:a?le=>F(c.id,le):void 0,onPointerDown:r&&!a?le=>r(c.id,le):void 0,children:[Z,d&&T.jsx("div",{"data-resize-handle":"true",style:{position:"absolute",right:0,top:0,width:4,height:"100%",cursor:"col-resize",zIndex:1},onMouseDown:le=>u(c.id,le)})]},c.id)};return T.jsxs("div",{ref:Y,style:{flex:1,height:h+1,overflow:"hidden",position:"relative",backgroundColor:"#f9fafb"},children:[T.jsxs("div",{style:{position:"absolute",top:0,left:0,transform:`translateX(-${o}px)`,height:h},children:[T.jsx("div",{style:{position:"absolute",top:0,height:h},children:S.map(c=>{const D=t.getColumnX(c.id);return K(c,D,h)})}),H&&T.jsx("div",{style:{position:"relative",height:Be,zIndex:1},children:x.filter(c=>!c.pinned).map(c=>T.jsx("div",{style:{position:"absolute",left:c.startX,width:c.width,height:Be,borderBottom:`1px solid ${c.group.color??"#3b82f6"}`,display:"flex",alignItems:"center",paddingLeft:8,boxSizing:"border-box",fontSize:11,fontWeight:600,color:"#6b7280",overflow:"hidden",whiteSpace:"nowrap",backgroundColor:"#f9fafb"},children:c.group.title},c.group.id))})]}),f.length>0&&T.jsxs("div",{style:{position:"absolute",top:0,left:0,width:L,height:h,backgroundColor:"#f9fafb",zIndex:2,display:"flex",flexDirection:"column"},children:[H&&T.jsx("div",{style:{position:"absolute",top:0,left:0,width:L,height:h-Ke},children:x.filter(c=>c.pinned).map(c=>T.jsx("div",{style:{position:"absolute",left:c.startX,width:c.width,height:"100%",borderBottom:"1px solid #d1d5db",borderRight:"1px solid #d1d5db",display:"flex",alignItems:"center",justifyContent:"center",boxSizing:"border-box",fontSize:11,fontWeight:600,color:"#6b7280",overflow:"hidden",whiteSpace:"nowrap",backgroundColor:"#f9fafb"},children:c.group.title},c.group.id))}),T.jsx("div",{style:{position:"absolute",bottom:0,left:0,width:L,height:Ke,borderTop:"1px solid #d1d5db"},children:f.map(c=>{const D=t.getColumnX(c.id);return K(c,D,Ke)})}),T.jsx("div",{style:{position:"absolute",top:0,right:0,width:1,height:"100%",backgroundColor:"#d1d5db",zIndex:3}})]}),T.jsx("div",{style:{position:"absolute",bottom:0,left:0,right:0,height:1,backgroundColor:"#d1d5db",zIndex:5}}),k&&T.jsx("div",{style:{position:"absolute",top:0,left:B(),width:2,height:h,backgroundColor:"#3b82f6",pointerEvents:"none",zIndex:10}}),k&&T.jsx("div",{style:{position:"fixed",top:0,left:k.currentX-k.ghostWidth/2,width:k.ghostWidth,height:32,backgroundColor:"#dbeafe",border:"1px solid #3b82f6",borderRadius:4,display:"flex",alignItems:"center",paddingLeft:8,boxSizing:"border-box",fontSize:12,fontWeight:600,color:"#1d4ed8",opacity:.85,pointerEvents:"none",zIndex:9999,whiteSpace:"nowrap",overflow:"hidden"},children:k.ghostTitle})]})}function qt({columnEngine:t,onColumnVisibilityChange:e}){const[o,n]=m.useState(!1),r=m.useRef(null),d=t.getAllColumns();return m.useEffect(()=>{if(!o)return;const a=f=>{r.current&&!r.current.contains(f.target)&&n(!1)},w=f=>{f.key==="Escape"&&n(!1)};return document.addEventListener("mousedown",a),document.addEventListener("keydown",w),()=>{document.removeEventListener("mousedown",a),document.removeEventListener("keydown",w)}},[o]),T.jsxs("div",{ref:r,style:{position:"relative",display:"inline-block"},children:[T.jsx("button",{onClick:()=>n(a=>!a),style:{display:"flex",alignItems:"center",gap:4,padding:"0 8px",height:28,fontSize:12,fontWeight:600,color:"#374151",backgroundColor:o?"#e5e7eb":"#f9fafb",border:"1px solid #d1d5db",borderRadius:4,cursor:"pointer",whiteSpace:"nowrap"},title:"Toggle column visibility",children:"Columns"}),o&&T.jsx("div",{style:{position:"absolute",top:"100%",left:0,marginTop:4,minWidth:160,backgroundColor:"#ffffff",border:"1px solid #d1d5db",borderRadius:6,boxShadow:"0 4px 12px rgba(0,0,0,0.12)",zIndex:1e3,padding:"4px 0"},children:d.map(a=>{const w=!a.hidden;return T.jsxs("label",{style:{display:"flex",alignItems:"center",gap:8,padding:"6px 12px",cursor:"pointer",fontSize:13,color:"#374151",userSelect:"none"},onMouseEnter:f=>{f.currentTarget.style.backgroundColor="#f3f4f6"},onMouseLeave:f=>{f.currentTarget.style.backgroundColor=""},children:[T.jsx("input",{type:"checkbox",checked:w,onChange:f=>{e(a.id,f.target.checked)},style:{cursor:"pointer",width:14,height:14}}),T.jsx("span",{style:{overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:a.title})]},a.id)})})]})}function Jt({content:t,position:e}){return Yt.createPortal(T.jsx("div",{style:{position:"fixed",left:e.left,top:e.top,zIndex:9999,pointerEvents:"none"},children:t}),document.body)}function Qt({message:t}){return T.jsx("div",{role:"status","aria-live":"polite","aria-atomic":"true",style:{position:"absolute",width:1,height:1,padding:0,margin:-1,overflow:"hidden",clip:"rect(0,0,0,0)",whiteSpace:"nowrap",borderWidth:0},children:t})}const je=24,Fe=32;function Zt(t){const{treeEngine:e,columnEngine:o,columnGroups:n,rowHeight:r,sidebarWidth:d,gridLayerConfig:a,cellLayerConfig:w,scale:f=2}=t,S=n!=null&&n.length>0,y=S?je+Fe:Fe,L=o.getVisibleColumns(),j=e.flattenedRows,X=o.getTotalWidth(),k=j.length*r,G=d+X,$=y+k,P=document.createElement("canvas");P.width=Math.round(G*f),P.height=Math.round($*f),P.style.width=`${G}px`,P.style.height=`${$}px`;const s=P.getContext("2d");s.setTransform(f,0,0,f,0,0),s.fillStyle="#ffffff",s.fillRect(0,0,G,$);const Y=new Ue({scrollX:0,scrollY:0,canvasWidth:X,canvasHeight:k,rowHeight:r}),W=[],x=[];let H=0;for(const u of L)W.push(H),x.push(u.width),H+=u.width;Y.setColumnOffsets(W),Y.setColumnWidths(x),s.save(),s.translate(d,y),It(s,Y,e,o,a),St(s,Y,e,o,t.selectionEngine,w),s.restore(),s.save(),s.translate(0,y);const b="#ffffff";s.fillStyle=b,s.fillRect(0,0,d,k),s.strokeStyle="#e5e7eb",s.lineWidth=.5,s.beginPath(),s.moveTo(d,0),s.lineTo(d,k),s.stroke(),s.font="13px system-ui, sans-serif",s.textBaseline="middle",s.fillStyle="#374151";for(let u=0;u<j.length;u++){const I=j[u];if(!I)continue;const F=e.getDepth(I.id),B=u*r,K=F*16+8,c=String(I.title??I.id),D=d-K-8;s.fillStyle=u%2===0?"#ffffff":"#f9fafb",s.fillRect(0,B,d,r),s.strokeStyle="#e5e7eb",s.lineWidth=.5,s.beginPath(),s.moveTo(0,B+r),s.lineTo(d,B+r),s.stroke(),s.fillStyle="#374151";let E=c;if(s.measureText(E).width>D&&D>0){for(;E.length>0&&s.measureText(E+"…").width>D;)E=E.slice(0,-1);E=E+"…"}s.fillText(E,K,B+r/2)}if(s.restore(),s.save(),s.translate(0,0),s.fillStyle="#f9fafb",s.fillRect(0,0,G,y),s.strokeStyle="#e5e7eb",s.lineWidth=1,s.beginPath(),s.moveTo(0,y),s.lineTo(G,y),s.stroke(),s.fillStyle="#f9fafb",s.fillRect(0,0,d,y),s.strokeStyle="#e5e7eb",s.lineWidth=.5,s.beginPath(),s.moveTo(d,0),s.lineTo(d,y),s.stroke(),S&&n){const u=new Map;for(const c of n)u.set(c.id,c);let I,F=0,B=0;const K=(c,D,E)=>{if(c===void 0||E<=0)return;const z=u.get(c);if(!z)return;const V=d+D;s.fillStyle=z.color??"#3b82f6",s.fillRect(V,je-2,E,2),s.font="600 11px system-ui, sans-serif",s.fillStyle="#6b7280",s.textBaseline="middle",s.textAlign="left",s.fillText(z.title,V+8,je/2)};for(const c of L){const D=o.getColumnX(c.id);c.group!==I?(K(I,F,B),I=c.group,F=D,B=c.width):B+=c.width}K(I,F,B)}const h=S?je:0;s.font="600 12px system-ui, sans-serif",s.textBaseline="middle",s.textAlign="left",s.fillStyle="#374151";for(const u of L){const I=o.getColumnX(u.id),F=d+I;s.strokeStyle="#e5e7eb",s.lineWidth=.5,s.beginPath(),s.moveTo(F+u.width,h),s.lineTo(F+u.width,y),s.stroke(),s.fillStyle="#374151";const B=u.width-16;let K=u.title;if(B>0&&s.measureText(K).width>B){for(;K.length>0&&s.measureText(K+"…").width>B;)K=K.slice(0,-1);K=K+"…"}s.fillText(K,F+8,h+Fe/2)}return s.restore(),P}const wt=180,bt=32,en=24,Ct=200;function vt(t,e){let o;switch(t){case"vertical":o=120;break;default:o=32}return e?en+o:o}const tn=m.memo(m.forwardRef(function(e,o){const{rows:n,columns:r,columnGroups:d,getCellData:a,cellRenderer:w,sidebarRenderer:f,sidebarWidth:S=wt,rowHeight:y=bt,headerOrientation:L="horizontal",rowStyle:j,selection:X,onSelectionChange:k,onCellClick:G,onCellDoubleClick:$,onCellContextMenu:P,onCellHover:s,onColumnHeaderClick:Y,onTreeToggle:W,defaultExpandedRows:x,onExpandChange:H,onLoadMore:b,hasMore:h,loading:u,hoverContent:I,hoverPosition:F,showColumnVisibilityMenu:B,ariaLabel:K="Data grid"}=e,[c,D]=m.useState(()=>new Set(x??[])),[E,z]=m.useState(0),[V,J]=m.useState(0),[Q,Z]=m.useState(r),[le,We]=m.useState(null),[kt,we]=m.useState(""),[_t,ze]=m.useState(null),De=m.useRef(null),Ne=m.useRef(null);m.useEffect(()=>{Z(r)},[r]);const qe=m.useRef(null),fe=m.useRef([]),ie=m.useRef(new Ue({rowHeight:y})),N=m.useRef(null),A=m.useRef(e),de=m.useRef(null),Xe=m.useRef(null),He=m.useRef(!1);A.current=e;const ye=m.useMemo(()=>new xt(n,c),[n,c]),se=m.useMemo(()=>new me(Q),[Q]),Je=m.useMemo(()=>new yt(X),[X]);de.current={treeEngine:ye,columnEngine:se,selectionEngine:Je};const Ae=se.getPinnedColumns().length>0,Ie=Ae?0:S,Qe=m.useCallback(l=>{const g=de.current;if(!g)return;const{treeEngine:R,columnEngine:M,selectionEngine:v}=g,C=ie.current,i=l==="grid"?0:l==="cells"?1:2,p=fe.current[i];if(!p)return;const _=$t(p,C.canvasWidth,C.canvasHeight);l==="grid"?It(_,C,R,M,{rowStyle:A.current.rowStyle}):l==="cells"?St(_,C,R,M,v,{cellRenderer:A.current.cellRenderer,getCellData:A.current.getCellData,hoveredCell:Xe.current}):Bt(_,C,R,M,v,{hoveredCell:Xe.current,hoveredRowId:De.current,selectedCell:Ne.current,resizingColumn:le??void 0,loading:A.current.loading})},[le]);m.useEffect(()=>{const l=se.getPinnedColumns(),g=se.getScrollableColumns(),R=se.getPinnedWidth(),M=[],v=[];let C=0;for(const ne of l)M.push(C),v.push(ne.width),C+=ne.width;const i=[],p=[];let _=0;for(const ne of g)i.push(_),p.push(ne.width),_+=ne.width;const O=ie.current;O.pinnedWidth=R,O.setPinnedOffsets(M,v),O.setScrollableOffsets(i,p);const U=se.getVisibleColumns(),ee=[],re=[];let be=0;for(const ne of U)ee.push(be),re.push(ne.width),be+=ne.width;O.setColumnOffsets(ee),O.setColumnWidths(re),O.update({rowHeight:y})},[se,y]),m.useEffect(()=>{const l=new Vt(Qe);return N.current=l,l.markAllDirty(),()=>{l.destroy(),N.current=null}},[Qe]),m.useEffect(()=>{const l=qe.current;if(!l)return;const g=new ResizeObserver(R=>{var M,v;for(const C of R){const{width:i,height:p}=C.contentRect,_=i-Ie,O=vt(A.current.headerOrientation??"horizontal",(((M=A.current.columnGroups)==null?void 0:M.length)??0)>0),U=p-O;ie.current.update({canvasWidth:Math.max(0,_),canvasHeight:Math.max(0,U)}),(v=N.current)==null||v.markAllDirty()}});return g.observe(l),()=>g.disconnect()},[Ie]),m.useEffect(()=>{var l;(l=N.current)==null||l.markAllDirty()},[ye,se,Je]),m.useEffect(()=>{var l;(l=N.current)==null||l.markDirty("overlay")},[u]);const Ye=m.useCallback(l=>{var re;l.preventDefault();const g=ie.current,R=de.current;if(!R)return;const M=R.treeEngine.flattenedRows.length*g.rowHeight,v=R.columnEngine.getTotalWidth(),C=Math.max(0,M-g.canvasHeight),i=v-R.columnEngine.getPinnedWidth(),p=g.canvasWidth-R.columnEngine.getPinnedWidth(),_=Math.max(0,i-p),O=Math.max(0,Math.min(C,g.scrollY+l.deltaY)),U=Math.max(0,Math.min(_,g.scrollX+l.deltaX));g.update({scrollY:O,scrollX:U}),z(O),J(U),(re=N.current)==null||re.markAllDirty();const ee=A.current;ee.hasMore&&ee.onLoadMore&&!ee.loading&&C-O<Ct?He.current||(He.current=!0,ee.onLoadMore()):C-O>=Ct&&(He.current=!1)},[]);m.useEffect(()=>{const l=fe.current[2];if(l)return l.addEventListener("wheel",Ye,{passive:!1}),()=>l.removeEventListener("wheel",Ye)},[Ye]),m.useEffect(()=>{u||(He.current=!1)},[u]);const Tt=m.useCallback(l=>{var p,_,O;const g=de.current;if(!g)return;const R=fe.current[2];if(!R)return;const M=R.getBoundingClientRect(),v=l.clientX-M.left,C=l.clientY-M.top,i=Le(v,C,ie.current,g.treeEngine,g.columnEngine);Xe.current=i,De.current=(i==null?void 0:i.rowId)??null,ze((i==null?void 0:i.rowId)??null),(p=N.current)==null||p.markDirty("overlay"),(O=(_=A.current).onCellHover)==null||O.call(_,(i==null?void 0:i.rowId)??null,(i==null?void 0:i.colId)??null,l.nativeEvent)},[]),Ve=m.useCallback(l=>{D(g=>{var M,v;const R=new Set(g);return R.has(l)?(R.delete(l),we(`Row ${l} collapsed`)):(R.add(l),we(`Row ${l} expanded`)),(v=(M=A.current).onExpandChange)==null||v.call(M,[...R]),R})},[]),Mt=m.useCallback(l=>{var Re,Se,ke,_e,Te;const g=de.current;if(!g)return;const R=fe.current[2];if(!R)return;const M=R.getBoundingClientRect(),v=l.clientX-M.left,C=l.clientY-M.top,i=Le(v,C,ie.current,g.treeEngine,g.columnEngine);if(!i)return;const{treeEngine:p,columnEngine:_,selectionEngine:O}=g,U=_.getPinnedColumns();if(U.length>0){const ae=U[0].id;if(p.hasChildren(i.rowId)&&i.colId===ae){A.current.onTreeToggle?A.current.onTreeToggle(i.rowId):Ve(i.rowId);return}}const ee=p.flattenedRows.map(ae=>ae.id),re=_.getVisibleColumns().map(ae=>ae.id);l.ctrlKey||l.metaKey?O.ctrlClick(i.rowId,i.colId):l.shiftKey?O.shiftClick(i.rowId,i.colId,ee,re):O.click(i.rowId,i.colId,ee,re),Ne.current={rowId:i.rowId,colId:i.colId},(Se=(Re=A.current).onSelectionChange)==null||Se.call(Re,O.getSelection()),(_e=(ke=A.current).onCellClick)==null||_e.call(ke,i.rowId,i.colId,l.nativeEvent),(Te=N.current)==null||Te.markDirty("overlay");const be=String(i.rowId),ne=_.getVisibleColumns().find(ae=>ae.id===i.colId),Pe=(ne==null?void 0:ne.title)??i.colId;we(`Row ${be}, Column ${Pe} selected`)},[Ve]),Wt=m.useCallback(l=>{var p,_;const g=de.current;if(!g)return;const R=fe.current[2];if(!R)return;const M=R.getBoundingClientRect(),v=l.clientX-M.left,C=l.clientY-M.top,i=Le(v,C,ie.current,g.treeEngine,g.columnEngine);i&&((_=(p=A.current).onCellDoubleClick)==null||_.call(p,i.rowId,i.colId,l.nativeEvent))},[]),Dt=m.useCallback(l=>{var p,_;l.preventDefault();const g=de.current;if(!g)return;const R=fe.current[2];if(!R)return;const M=R.getBoundingClientRect(),v=l.clientX-M.left,C=l.clientY-M.top,i=Le(v,C,ie.current,g.treeEngine,g.columnEngine);i&&((_=(p=A.current).onCellContextMenu)==null||_.call(p,i.rowId,i.colId,l.nativeEvent))},[]),Xt=m.useCallback(()=>{var l,g,R;Xe.current=null,De.current=null,ze(null),(l=N.current)==null||l.markDirty("overlay"),(R=(g=A.current).onCellHover)==null||R.call(g,null,null,new PointerEvent("pointerleave"))},[]),Ht=m.useCallback((l,g)=>{var R,M;(M=(R=A.current).onColumnHeaderClick)==null||M.call(R,l,g.nativeEvent)},[]),Pt=m.useCallback((l,g)=>{var M,v,C,i;Z(p=>p.map(O=>{if(O.id!==l)return O;const U=O.minWidth??30;return{...O,width:Math.max(U,g)}}));const R=(M=de.current)==null?void 0:M.columnEngine;if(R){const p=R.getColumnX(l),_=R.getVisibleColumns().find(O=>O.id===l);if(_){const U=_.pinned==="left"?p+Math.max(_.minWidth??30,g):p+Math.max(_.minWidth??30,g)-ie.current.scrollX;We({colId:l,x:U})}}(C=(v=A.current).onColumnResize)==null||C.call(v,l,g),(i=N.current)==null||i.markAllDirty()},[]);m.useEffect(()=>{const l=()=>{We(null)};return document.addEventListener("mouseup",l),()=>document.removeEventListener("mouseup",l)},[]);const Et=m.useCallback((l,g)=>{var R,M,v;Z(C=>{const i=[...C],p=i.findIndex(U=>U.id===l);if(p===-1)return C;const[_]=i.splice(p,1),O=Math.max(0,Math.min(g,i.length));return i.splice(O,0,_),i}),(M=(R=A.current).onColumnReorder)==null||M.call(R,l,g),(v=N.current)==null||v.markAllDirty()},[]),Ot=m.useCallback((l,g)=>{var R,M,v;Z(C=>C.map(i=>i.id===l?{...i,hidden:!g}:i)),(M=(R=A.current).onColumnVisibilityChange)==null||M.call(R,l,g),(v=N.current)==null||v.markAllDirty()},[]),Lt=m.useCallback(l=>{var re,be,ne,Pe,Re,Se,ke,_e,Te,ae,Ge,et,tt,nt,ot,lt,st,it,rt,ct,dt,at,ht,ft;const g=de.current;if(!g)return;const{treeEngine:R,columnEngine:M,selectionEngine:v}=g,C=ie.current,i=R.flattenedRows.map(q=>q.id),p=M.getVisibleColumns().map(q=>q.id);if(i.length===0||p.length===0)return;const _=v.getSelection().anchor,O=l.ctrlKey||l.metaKey,U=(q,te)=>{const oe=i.length*C.rowHeight,ue=M.getTotalWidth(),ge=Math.max(0,oe-C.canvasHeight),Ee=Math.max(0,ue-C.canvasWidth),Ce=q*C.rowHeight,ve=Ce+C.rowHeight;let ce=C.scrollY;Ce<C.scrollY?ce=Ce:ve>C.scrollY+C.canvasHeight&&(ce=ve-C.canvasHeight),ce=Math.max(0,Math.min(ge,ce));const pe=M.getVisibleColumns()[te],Me=pe?M.getColumnX(pe.id):0,Oe=Me+((pe==null?void 0:pe.width)??0);let he=C.scrollX;Me<C.scrollX?he=Me:Oe>C.scrollX+C.canvasWidth&&(he=Oe-C.canvasWidth),he=Math.max(0,Math.min(Ee,he)),(ce!==C.scrollY||he!==C.scrollX)&&(C.update({scrollY:ce,scrollX:he}),z(ce),J(he))},ee=(q,te,oe)=>{var ut,gt,pt;let ue=_?i.indexOf(_.rowId):-1,ge=_?p.indexOf(_.colId):-1;ue===-1&&(ue=0),ge===-1&&(ge=0);const Ee=Math.max(0,Math.min(i.length-1,ue+q)),Ce=Math.max(0,Math.min(p.length-1,ge+te)),ve=i[Ee],ce=p[Ce];oe?v.shiftClick(ve,ce,i,p):v.click(ve,ce,i,p),(gt=(ut=A.current).onSelectionChange)==null||gt.call(ut,v.getSelection()),(pt=N.current)==null||pt.markDirty("overlay"),U(Ee,Ce);const pe=M.getVisibleColumns()[Ce],Me=String(ve),Oe=(pe==null?void 0:pe.title)??String(ce),he=v.getSelection().cells.size;he>1?we(`${he} cells selected`):we(`Row ${Me}, Column ${Oe} selected`)};switch(l.key){case"ArrowUp":l.preventDefault(),ee(-1,0,l.shiftKey);break;case"ArrowDown":l.preventDefault(),ee(1,0,l.shiftKey);break;case"ArrowLeft":l.preventDefault(),ee(0,-1,l.shiftKey);break;case"ArrowRight":l.preventDefault(),ee(0,1,l.shiftKey);break;case"Tab":{if(l.preventDefault(),l.shiftKey){const q=_?i.indexOf(_.rowId):0;if((_?p.indexOf(_.colId):0)>0)ee(0,-1,!1);else{const oe=Math.max(0,(q===-1?0:q)-1),ue=i[oe],ge=p[p.length-1];v.click(ue,ge,i,p),(Re=(Pe=A.current).onSelectionChange)==null||Re.call(Pe,v.getSelection()),(Se=N.current)==null||Se.markDirty("overlay"),U(oe,p.length-1)}}else{const q=_?i.indexOf(_.rowId):0;if((_?p.indexOf(_.colId):-1)<p.length-1)ee(0,1,!1);else{const oe=Math.min(i.length-1,(q===-1?0:q)+1),ue=i[oe],ge=p[0];v.click(ue,ge,i,p),(be=(re=A.current).onSelectionChange)==null||be.call(re,v.getSelection()),(ne=N.current)==null||ne.markDirty("overlay"),U(oe,0)}}break}case"Escape":l.preventDefault(),v.clear(),(_e=(ke=A.current).onSelectionChange)==null||_e.call(ke,v.getSelection()),(Te=N.current)==null||Te.markDirty("overlay"),we("Selection cleared");break;case"a":case"A":if(O){l.preventDefault();for(const q of i)for(const te of p)v.ctrlClick(q,te);(Ge=(ae=A.current).onSelectionChange)==null||Ge.call(ae,v.getSelection()),(et=N.current)==null||et.markDirty("overlay"),we(`${i.length*p.length} cells selected`)}break;case"Home":{l.preventDefault();const q=_?i.indexOf(_.rowId):0;if(O)v.click(i[0],p[0],i,p),(nt=(tt=A.current).onSelectionChange)==null||nt.call(tt,v.getSelection()),(ot=N.current)==null||ot.markDirty("overlay"),U(0,0);else{const te=q===-1?0:q;v.click(i[te],p[0],i,p),(st=(lt=A.current).onSelectionChange)==null||st.call(lt,v.getSelection()),(it=N.current)==null||it.markDirty("overlay"),U(te,0)}break}case"End":{l.preventDefault();const q=_?i.indexOf(_.rowId):0;if(O){const te=i.length-1,oe=p.length-1;v.click(i[te],p[oe],i,p),(ct=(rt=A.current).onSelectionChange)==null||ct.call(rt,v.getSelection()),(dt=N.current)==null||dt.markDirty("overlay"),U(te,oe)}else{const te=q===-1?0:q,oe=p.length-1;v.click(i[te],p[oe],i,p),(ht=(at=A.current).onSelectionChange)==null||ht.call(at,v.getSelection()),(ft=N.current)==null||ft.markDirty("overlay"),U(te,oe)}break}}},[]);m.useImperativeHandle(o,()=>({captureToCanvas(l){const g=de.current;if(!g)throw new Error("CanvasGrid: engines not initialized");const{treeEngine:R,columnEngine:M,selectionEngine:v}=g,C=A.current,i=C.rowHeight??bt,p=C.sidebarWidth??wt;return Zt({treeEngine:R,columnEngine:M,selectionEngine:v,columnGroups:C.columnGroups,rowHeight:i,sidebarWidth:p,gridLayerConfig:{rowStyle:C.rowStyle},cellLayerConfig:{cellRenderer:C.cellRenderer,getCellData:C.getCellData,hoveredCell:null},scale:(l==null?void 0:l.scale)??2})}}),[]);const jt=d!=null&&d.length>0,Ze=vt(L??"horizontal",jt),zt=Ie+se.getTotalWidth()+2,At=Ze+ye.flattenedRows.length*y+2;return T.jsxs("div",{ref:qe,tabIndex:0,role:"grid","aria-label":K,"aria-rowcount":ye.flattenedRows.length,"aria-colcount":se.getVisibleColumns().length,onKeyDown:Lt,style:{display:"flex",flexDirection:"column",width:zt,height:At,maxWidth:"100%",maxHeight:"100%",overflow:"hidden",fontFamily:"system-ui, sans-serif",outline:"none",border:"1px solid #d1d5db",borderRadius:4},children:[T.jsxs("div",{style:{display:"flex",flexDirection:"row",height:Ze,flexShrink:0},children:[!Ae&&T.jsx("div",{style:{width:Ie,flexShrink:0,borderRight:"1px solid #e5e7eb",borderBottom:"1px solid #e5e7eb",backgroundColor:"#f9fafb",display:"flex",flexDirection:"column",overflow:"hidden"},children:e.sidebarHeaderRenderer?T.jsx("div",{style:{width:"100%",flex:1},children:e.sidebarHeaderRenderer()}):B?T.jsx("div",{style:{display:"flex",alignItems:"center",justifyContent:"center",flex:1},children:T.jsx(qt,{columnEngine:se,onColumnVisibilityChange:Ot})}):null}),T.jsx(Nt,{columnEngine:se,columnGroups:d,scrollX:V,orientation:L,onColumnClick:Y?Ht:void 0,onColumnResize:e.onColumnResize!==void 0?Pt:void 0,onColumnReorder:e.onColumnReorder!==void 0?Et:void 0,columnHeaderRenderer:e.columnHeaderRenderer})]}),T.jsxs("div",{style:{display:"flex",flexDirection:"row",flex:1,overflow:"hidden"},children:[!Ae&&T.jsx(Kt,{treeEngine:ye,width:Ie,rowHeight:y,scrollTop:E,viewportHeight:ie.current.canvasHeight||600,sidebarRenderer:f,onToggle:Ve,onRowClick:e.onRowHeaderClick?(l,g)=>e.onRowHeaderClick(l,g.nativeEvent):void 0,hoveredRowId:_t,onRowHover:l=>{var g;De.current=l,ze(l),(g=N.current)==null||g.markDirty("overlay")}}),T.jsxs("div",{style:{flex:1,position:"relative",overflow:"hidden"},children:[T.jsx("canvas",{ref:l=>{l&&(fe.current[0]=l)},style:{position:"absolute",top:0,left:0,zIndex:0}}),T.jsx("canvas",{ref:l=>{l&&(fe.current[1]=l)},style:{position:"absolute",top:0,left:0,zIndex:1}}),T.jsx("canvas",{ref:l=>{l&&(fe.current[2]=l)},style:{position:"absolute",top:0,left:0,zIndex:2},onPointerMove:Tt,onPointerDown:Mt,onDoubleClick:Wt,onContextMenu:Dt,onPointerLeave:Xt})]})]}),I!=null&&F!=null&&T.jsx(Jt,{content:I,position:F}),T.jsx(Qt,{message:kt})]})}));exports.CanvasGrid=tn;exports.ColumnEngine=me;exports.SelectionEngine=yt;exports.TreeEngine=xt;exports.ViewState=Ue;exports.createDrawHelpers=Rt;