@sci-grid/core 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/LICENSE +21 -0
- package/dist/__tests__/data.spec.d.ts +1 -0
- package/dist/__tests__/scrolling.spec.d.ts +1 -0
- package/dist/__tests__/selection.spec.d.ts +1 -0
- package/dist/core/coord-helper.d.ts +4 -0
- package/dist/core/data-manager.d.ts +10 -0
- package/dist/core/drag-manager.d.ts +18 -0
- package/dist/core/editor-manager.d.ts +21 -0
- package/dist/core/keyboard-handler.d.ts +14 -0
- package/dist/core/mouse-handler.d.ts +24 -0
- package/dist/core/renderer.d.ts +13 -0
- package/dist/core/scroller.d.ts +19 -0
- package/dist/core/selection-manager.d.ts +13 -0
- package/dist/core/units.d.ts +9 -0
- package/dist/index.cjs +50 -0
- package/dist/index.d.ts +44 -0
- package/dist/index.js +894 -0
- package/dist/test-setup.d.ts +1 -0
- package/dist/types/grid.d.ts +101 -0
- package/package.json +28 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 SciGrid Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { ViewportState, GridConfig } from '../types/grid.js';
|
|
2
|
+
export declare function getColumnWidth(state: ViewportState, config: GridConfig, col: number): number;
|
|
3
|
+
export declare function getColumnOffset(state: ViewportState, colIndex: number): number;
|
|
4
|
+
export declare function getColumnAt(state: ViewportState, x: number): number;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ViewportState, GridConfig, IDataGridProvider } from '../types/grid.js';
|
|
2
|
+
export declare class DataManager {
|
|
3
|
+
private state;
|
|
4
|
+
private provider;
|
|
5
|
+
private config;
|
|
6
|
+
constructor(state: ViewportState, provider: IDataGridProvider, config: GridConfig);
|
|
7
|
+
saveState(): void;
|
|
8
|
+
loadState(): any;
|
|
9
|
+
copyToClipboard(): void;
|
|
10
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ViewportState, GridConfig, IDataGridProvider } from '../types/grid.js';
|
|
2
|
+
export declare class DragManager {
|
|
3
|
+
private canvas;
|
|
4
|
+
private state;
|
|
5
|
+
private provider;
|
|
6
|
+
private config;
|
|
7
|
+
private actions;
|
|
8
|
+
constructor(canvas: HTMLCanvasElement, state: ViewportState, provider: IDataGridProvider, config: GridConfig, actions: {
|
|
9
|
+
invalidate: () => void;
|
|
10
|
+
updateSelection: (mode: any, r: number | null, c: number | null, ctrl: boolean, shift: boolean) => void;
|
|
11
|
+
updateVirtualSize: () => void;
|
|
12
|
+
saveState: () => void;
|
|
13
|
+
});
|
|
14
|
+
startResizing(col: number, e: MouseEvent): void;
|
|
15
|
+
startReordering(colIndex: number, e: MouseEvent): void;
|
|
16
|
+
private handleHeaderClick;
|
|
17
|
+
private commitReorder;
|
|
18
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ViewportState, GridConfig, IDataGridProvider } from '../types/grid.js';
|
|
2
|
+
export declare class EditorManager {
|
|
3
|
+
private uiLayer;
|
|
4
|
+
private provider;
|
|
5
|
+
private config;
|
|
6
|
+
private state;
|
|
7
|
+
private helpers;
|
|
8
|
+
private editor;
|
|
9
|
+
constructor(uiLayer: HTMLElement, provider: IDataGridProvider, config: GridConfig, state: ViewportState, helpers: {
|
|
10
|
+
getColumnWidth: (c: number) => number;
|
|
11
|
+
getColumnOffset: (c: number) => number;
|
|
12
|
+
invalidate: () => void;
|
|
13
|
+
});
|
|
14
|
+
get activeEditor(): HTMLElement | HTMLInputElement | null;
|
|
15
|
+
closeEditor(): void;
|
|
16
|
+
openHeaderEditor(col: number, subIndex: number): void;
|
|
17
|
+
private getHeaderHeights;
|
|
18
|
+
openCellEditor(row: number, col: number): void;
|
|
19
|
+
private renderTextEditor;
|
|
20
|
+
private renderSelectEditor;
|
|
21
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ViewportState, GridConfig, IDataGridProvider } from '../types/grid.js';
|
|
2
|
+
export declare class KeyboardHandler {
|
|
3
|
+
private state;
|
|
4
|
+
private provider;
|
|
5
|
+
private config;
|
|
6
|
+
private actions;
|
|
7
|
+
constructor(state: ViewportState, provider: IDataGridProvider, config: GridConfig, actions: {
|
|
8
|
+
updateSelection: (mode: any, r: number, c: number, ctrl: boolean, shift: boolean) => void;
|
|
9
|
+
scrollToCell: (r: number, c: number) => void;
|
|
10
|
+
copyToClipboard: () => void;
|
|
11
|
+
render: () => void;
|
|
12
|
+
});
|
|
13
|
+
handleKeyDown(e: KeyboardEvent, hasEditor: boolean): void;
|
|
14
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ViewportState, GridConfig, IDataGridProvider } from '../types/grid.js';
|
|
2
|
+
export declare class MouseHandler {
|
|
3
|
+
private canvas;
|
|
4
|
+
private state;
|
|
5
|
+
private provider;
|
|
6
|
+
private config;
|
|
7
|
+
private actions;
|
|
8
|
+
constructor(canvas: HTMLCanvasElement, state: ViewportState, provider: IDataGridProvider, config: GridConfig, actions: {
|
|
9
|
+
updateSelection: (mode: any, r: number | null, c: number | null, ctrl: boolean, shift: boolean) => void;
|
|
10
|
+
invalidate: () => void;
|
|
11
|
+
render: () => void;
|
|
12
|
+
startResizing: (col: number, e: MouseEvent) => void;
|
|
13
|
+
startReordering: (colIdx: number, e: MouseEvent) => void;
|
|
14
|
+
openEditor: (r: number, c: number) => void;
|
|
15
|
+
openHeaderEditor: (c: number, sub: number) => void;
|
|
16
|
+
closeEditor: () => void;
|
|
17
|
+
setSelecting: (val: boolean) => void;
|
|
18
|
+
});
|
|
19
|
+
handleHit(e: MouseEvent, isDoubleClick: boolean): void;
|
|
20
|
+
private handleHeaderHit;
|
|
21
|
+
private handleCellHit;
|
|
22
|
+
private getHeaderHit;
|
|
23
|
+
private getHeaderHeights;
|
|
24
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { GridConfig, IDataGridProvider, ViewportState } from '../types/grid.js';
|
|
2
|
+
export declare class GridRenderer {
|
|
3
|
+
private canvas;
|
|
4
|
+
private ctx;
|
|
5
|
+
private dpr;
|
|
6
|
+
constructor(canvas: HTMLCanvasElement);
|
|
7
|
+
resize(width: number, height: number): void;
|
|
8
|
+
render(state: ViewportState, config: GridConfig, provider: IDataGridProvider): void;
|
|
9
|
+
private renderHeader;
|
|
10
|
+
private drawHeaderContent;
|
|
11
|
+
private renderReorderingOverlay;
|
|
12
|
+
private drawCellContent;
|
|
13
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ViewportState } from '../types/grid.js';
|
|
2
|
+
export declare class Scroller {
|
|
3
|
+
private container;
|
|
4
|
+
private shadow;
|
|
5
|
+
private onScroll;
|
|
6
|
+
private onMouseDown;
|
|
7
|
+
private onDoubleClick;
|
|
8
|
+
constructor(container: HTMLElement, onScroll: (state: Partial<ViewportState>) => void, onMouseDown: (e: MouseEvent) => void, onDoubleClick: (e: MouseEvent) => void);
|
|
9
|
+
private styleId;
|
|
10
|
+
private injectScrollStyle;
|
|
11
|
+
updateScrollStyle(thumbColor?: string, trackColor?: string): void;
|
|
12
|
+
private readonly MAX_BROWSER_SIZE;
|
|
13
|
+
private scaleX;
|
|
14
|
+
private scaleY;
|
|
15
|
+
updateVirtualSize(width: number, height: number): void;
|
|
16
|
+
setScroll(x: number, y: number): void;
|
|
17
|
+
scrollToCell(x: number, y: number, width: number, height: number): void;
|
|
18
|
+
private setupEvents;
|
|
19
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ViewportState, GridConfig, IDataGridProvider, SelectionMode } from '../types/grid.js';
|
|
2
|
+
export declare class SelectionManager {
|
|
3
|
+
private state;
|
|
4
|
+
private provider;
|
|
5
|
+
private config;
|
|
6
|
+
private invalidate;
|
|
7
|
+
constructor(state: ViewportState, provider: IDataGridProvider, config: GridConfig, invalidate: () => void);
|
|
8
|
+
updateSelection(mode: SelectionMode, row: number | null, col: number | null, isCtrl: boolean, isShift: boolean): void;
|
|
9
|
+
private handleRangeSelection;
|
|
10
|
+
private handlePivotSelection;
|
|
11
|
+
populateLookupSets(): void;
|
|
12
|
+
private notifySelectionChange;
|
|
13
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const SI_PREFIXES: Record<string, number>;
|
|
2
|
+
/**
|
|
3
|
+
* Parses a scientific value string like "10mA", "5.5kV", or "2u" into a number.
|
|
4
|
+
*/
|
|
5
|
+
export declare function parseScientificValue(val: string): number;
|
|
6
|
+
/**
|
|
7
|
+
* Formats a number into a scientific string with appropriate prefix.
|
|
8
|
+
*/
|
|
9
|
+
export declare function formatScientificValue(val: number, unit?: string): string;
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";var L=Object.defineProperty;var $=(m,t,e)=>t in m?L(m,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):m[t]=e;var p=(m,t,e)=>$(m,typeof t!="symbol"?t+"":t,e);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class D{constructor(t){p(this,"canvas");p(this,"ctx");p(this,"dpr");this.canvas=t;const e=t.getContext("2d",{alpha:!1});if(!e)throw new Error("Could not get 2d context");this.ctx=e,this.dpr=window.devicePixelRatio||1}resize(t,e){this.canvas.width=t*this.dpr,this.canvas.height=e*this.dpr,this.canvas.style.width=`${t}px`,this.canvas.style.height=`${e}px`,this.ctx.scale(this.dpr,this.dpr)}render(t,e,i){const{ctx:s}=this,{width:r,height:n,scrollX:o,scrollY:l,selectedRows:h,selectionMode:a,headerHeight:u}=t;s.fillStyle=e.backgroundColor,s.fillRect(0,0,r,n);const c=e.showRowNumbers?e.rowNumbersWidth:0,g=Math.max(0,Math.floor(l/e.rowHeight)),d=Math.min(i.getRowCount()-1,Math.ceil((l+n)/e.rowHeight)),C=[],f=t.columnOffsets;let v=0,y=f.length-2,S=0;for(;v<=y;){const w=v+y>>1;f[w+1]>o?(S=w,y=w-1):v=w+1}for(let w=S;w<i.getColumnCount();w++){const b=t.columnOrder[w]??w,H=t.columnWidths[b]??e.columnWidth,R=c-o+f[w];if(R<r)C.push({index:b,x:R,width:H});else break}s.font=e.font,s.textBaseline="middle",s.save(),s.beginPath(),s.rect(c,u,r-c,n-u),s.clip();for(let w=g;w<=d;w++){const b=Math.floor(w*e.rowHeight-l+u);e.alternateRowColor&&w%2===1&&(s.fillStyle=e.alternateRowColor,s.fillRect(c,b,r-c,e.rowHeight));for(const H of C){const{index:R,x,width:O}=H,W=t.reorderingCol!==null&&t.columnOrder[t.reorderingCol]===R;W&&(s.globalAlpha=.2);let E=!1;if(a==="all")E=!0;else for(const T of t.selectionRanges)if(w>=T.startRow&&w<=T.endRow&&R>=T.startCol&&R<=T.endCol){E=!0;break}E&&(s.fillStyle=e.selectionColor,s.fillRect(x,b,O,e.rowHeight));const z=i.getCellData(w,R),A=i.getHeader(R);this.drawCellContent(s,z,A,x,b,O,e.rowHeight,e,E&&a==="cell"),s.strokeStyle=e.gridLineColor,s.lineWidth=1,s.strokeRect(x,b,O,e.rowHeight),W&&(s.globalAlpha=1)}}if(s.restore(),e.showRowNumbers){s.fillStyle=e.rowNumberBackground,s.fillRect(0,u,c,n),s.fillStyle=e.rowNumberTextColor,s.strokeStyle=e.gridLineColor,s.textAlign="left";for(let w=g;w<=d;w++){const b=Math.floor(w*e.rowHeight-l+u);h.has(w)&&(a==="row"||a==="cell")&&(s.fillStyle=e.selectionColor,s.fillRect(0,b,c,e.rowHeight)),s.strokeRect(0,b,c,e.rowHeight),s.fillStyle=e.rowNumberTextColor,s.fillText((w+1).toString(),e.cellPadding,b+e.rowHeight/2)}}this.renderHeader(t,e,i,c),e.showRowNumbers&&(s.fillStyle=e.headerBackground,s.fillRect(0,0,c,u),s.strokeStyle=e.gridLineColor,s.strokeRect(0,0,c,u)),this.renderReorderingOverlay(t,e,i,c)}renderHeader(t,e,i,s){const{width:r,headerHeight:n,scrollX:o,columnOrder:l,reorderingCol:h}=t,a=this.ctx;a.save(),a.beginPath(),a.rect(s,0,r-s,n),a.clip(),a.fillStyle=e.headerBackground,a.fillRect(s,0,r-s,n);const u=t.columnOffsets;let c=0,g=u.length-2,d=0;for(;c<=g;){const f=c+g>>1;u[f+1]>o?(d=f,g=f-1):c=f+1}const C=i.getColumnCount();for(let f=d;f<C;f++){const v=l[f]??f,y=t.columnWidths[v]??e.columnWidth,S=s-o+u[f];if(S<r){const w=i.getHeader(v),b=h===f;b&&(a.globalAlpha=.2),a.strokeStyle=e.gridLineColor,a.strokeRect(S,0,y,n),this.drawHeaderContent(w,S,0,y,n,e),b&&(a.globalAlpha=1)}else break}a.restore()}drawHeaderContent(t,e,i,s,r,n){var g;const o=this.ctx,l=n.headerSubTextCount;let h=r,a=0;if(l===1?(h=r*.75,a=r*.25):l===2&&(h=r*.5,a=r*.25),o.save(),o.beginPath(),o.rect(e,i,s,r),o.clip(),l>0&&(o.strokeStyle=n.headerDividerColor||n.gridLineColor,o.lineWidth=1,o.globalAlpha=n.headerDividerAlpha??.2,o.beginPath(),o.moveTo(e,i+h),o.lineTo(e+s,i+h),o.stroke(),l===2&&(o.beginPath(),o.moveTo(e,i+h+a),o.lineTo(e+s,i+h+a),o.stroke()),o.globalAlpha=1),t.markIcon&&(o.fillStyle=((g=n.headerTitleStyle)==null?void 0:g.color)||n.headerTextColor,o.font="bold 14px sans-serif",o.textAlign="right",o.textBaseline="top",o.fillText(t.markIcon,e+s-5,i+4)),t.sortOrder){o.fillStyle=n.headerTextColor,o.font="10px sans-serif",o.textAlign="right",o.textBaseline="middle";const d=t.sortOrder==="asc"?"▲":"▼";o.fillText(d,e+s-(t.markIcon?20:8),i+(l===0?r/2:h/2))}const u=n.cellPadding;o.textAlign="left",o.textBaseline="middle";const c=n.headerTitleStyle;if(o.font=(c==null?void 0:c.font)||n.headerFont,o.fillStyle=(c==null?void 0:c.color)||n.headerTextColor,o.globalAlpha=(c==null?void 0:c.alpha)??1,o.fillText(t.name||"",e+u,i+h/2),l>=1){const d=n.headerUnitsStyle;o.font=(d==null?void 0:d.font)||`italic ${Math.max(6,Math.floor(r/6))}px Inter, sans-serif`,o.fillStyle=(d==null?void 0:d.color)||n.headerTextColor,o.globalAlpha=(d==null?void 0:d.alpha)??.6;const C=t.units||n.headerPlaceholder;o.fillText(C,e+u,i+h+a/2)}if(l===2){const d=n.headerDescriptionStyle;o.font=(d==null?void 0:d.font)||`italic ${Math.max(6,Math.floor(r/8))}px Inter, sans-serif`,o.fillStyle=(d==null?void 0:d.color)||n.headerTextColor,o.globalAlpha=(d==null?void 0:d.alpha)??.6;const C=t.description||n.headerPlaceholder;o.fillText(C,e+u,i+h+a+a/2)}o.restore()}renderReorderingOverlay(t,e,i,s){const{ctx:r}=this,{height:n,headerHeight:o,scrollX:l,scrollY:h}=t;if(t.reorderingTarget!==null){let a=s-l;for(let d=0;d<t.reorderingTarget;d++){const C=t.columnOrder[d];C!==void 0?a+=t.columnWidths[C]??e.columnWidth:a+=e.columnWidth}const u=i.getRowCount()*e.rowHeight+o-h,c=Math.min(n,Math.max(o,u)),g=e.dragHandleColor||"#4facfe";r.save(),r.strokeStyle=g,r.lineWidth=4,r.beginPath(),r.moveTo(a,0),r.lineTo(a,c),r.stroke(),r.restore()}if(t.reorderingCol!==null&&t.reorderingX!==null){const a=t.columnOrder[t.reorderingCol]??t.reorderingCol,u=t.columnWidths[a]??e.columnWidth,c=t.reorderingX-u/2;r.save(),r.globalAlpha=.6,r.fillStyle=e.headerBackground,r.fillRect(c,0,u,o),r.strokeStyle="#4facfe",r.strokeRect(c,0,u,o);const g=i.getHeader(a);this.drawHeaderContent(g,c,0,u,o,e);const d=Math.max(0,Math.floor(h/e.rowHeight)),C=Math.min(i.getRowCount()-1,Math.ceil((h+n)/e.rowHeight));r.fillStyle="rgba(18, 18, 20, 0.8)",r.fillRect(c,o,u,n-o);for(let f=d;f<=C;f++){const v=Math.floor(f*e.rowHeight-h+o),y=i.getCellData(f,a);y!=null&&(r.fillStyle=e.textColor,r.font=e.font,r.fillText(y.toString(),c+e.cellPadding,v+e.rowHeight/2)),r.strokeStyle="rgba(79, 172, 254, 0.3)",r.strokeRect(c,v,u,e.rowHeight)}r.restore()}}drawCellContent(t,e,i,s,r,n,o,l,h){const a=i.type||"text",u=l.cellPadding,c=h?l.selectedTextColor:l.textColor;if(a==="checkbox"){const d=s+(n-14)/2,C=r+(o-14)/2,f=!!e;t.beginPath(),t.rect(d,C,14,14),t.strokeStyle="#999",t.stroke(),f&&(t.fillStyle="#4facfe",t.fillRect(d+2,C+2,10,10));return}if(a==="progress"){const g=typeof e=="number"?Math.min(100,Math.max(0,e)):0,d=o*.6,C=r+(o-d)/2,f=n-u*2,v=s+u;t.fillStyle=l.alternateRowColor||"#e0e0e033",t.fillRect(v,C,f,d),t.fillStyle="#4facfe",t.fillRect(v,C,f*(g/100),d),t.fillStyle=c,t.font="10px sans-serif",t.textAlign="center",t.fillText(`${g}%`,s+n/2,r+o/2);return}if(a==="sparkline"&&Array.isArray(e)){const g=e;if(g.length>1){const d=n-u*2,C=o*.6,f=s+u,v=r+(o-C)/2,y=Math.min(...g),w=Math.max(...g)-y||1;t.save(),t.beginPath(),t.strokeStyle="#4facfe",t.lineWidth=1.5,g.forEach((b,H)=>{const R=f+H/(g.length-1)*d,x=v+C-(b-y)/w*C;H===0?t.moveTo(R,x):t.lineTo(R,x)}),t.stroke(),t.restore()}return}if(e!=null){t.fillStyle=c,t.textAlign=a==="numeric"?"right":"left",t.font=l.font;const g=e.toString(),d=a==="numeric"?s+n-u:s+u;t.fillText(g,d,r+o/2)}if(a==="select"){t.fillStyle=c,t.globalAlpha=.5,t.beginPath();const g=s+n-15,d=r+o/2-2;t.moveTo(g,d),t.lineTo(g+8,d),t.lineTo(g+4,d+5),t.fill(),t.globalAlpha=1}}}class N{constructor(t,e,i,s){p(this,"container");p(this,"shadow");p(this,"onScroll");p(this,"onMouseDown");p(this,"onDoubleClick");p(this,"styleId","scigrid-scroll-style-"+Math.random().toString(36).substr(2,9));p(this,"MAX_BROWSER_SIZE",15e6);p(this,"scaleX",1);p(this,"scaleY",1);this.container=t,this.onScroll=e,this.onMouseDown=i,this.onDoubleClick=s,this.shadow=document.createElement("div"),this.shadow.style.position="absolute",this.shadow.style.top="0",this.shadow.style.left="0",this.shadow.style.width="1px",this.shadow.style.height="1px",this.shadow.style.pointerEvents="none",this.container.style.overflow="auto",this.container.style.position="relative",this.container.appendChild(this.shadow),this.setupEvents(),this.injectScrollStyle()}injectScrollStyle(){this.container.classList.add(this.styleId);const t=document.createElement("style");t.id=this.styleId,t.innerHTML=`
|
|
2
|
+
.${this.styleId} {
|
|
3
|
+
scrollbar-width: thin;
|
|
4
|
+
scrollbar-color: rgba(155, 155, 155, 0.5) transparent;
|
|
5
|
+
}
|
|
6
|
+
.${this.styleId}::-webkit-scrollbar {
|
|
7
|
+
width: 10px;
|
|
8
|
+
height: 10px;
|
|
9
|
+
}
|
|
10
|
+
.${this.styleId}::-webkit-scrollbar-track {
|
|
11
|
+
background: transparent;
|
|
12
|
+
}
|
|
13
|
+
.${this.styleId}::-webkit-scrollbar-thumb {
|
|
14
|
+
background-color: rgba(155, 155, 155, 0.5);
|
|
15
|
+
border-radius: 6px;
|
|
16
|
+
border: 2px solid transparent;
|
|
17
|
+
background-clip: content-box;
|
|
18
|
+
}
|
|
19
|
+
.${this.styleId}::-webkit-scrollbar-thumb:hover {
|
|
20
|
+
background-color: rgba(155, 155, 155, 0.8);
|
|
21
|
+
}
|
|
22
|
+
.${this.styleId}::-webkit-scrollbar-corner {
|
|
23
|
+
background: transparent;
|
|
24
|
+
}
|
|
25
|
+
`,document.head.appendChild(t)}updateScrollStyle(t,e){const i=document.getElementById(this.styleId);if(i&&(t||e)){const s=t||"rgba(155, 155, 155, 0.5)",r=e||"transparent";i.innerHTML=`
|
|
26
|
+
.${this.styleId} {
|
|
27
|
+
scrollbar-width: thin;
|
|
28
|
+
scrollbar-color: ${s} ${r};
|
|
29
|
+
}
|
|
30
|
+
.${this.styleId}::-webkit-scrollbar {
|
|
31
|
+
width: 10px;
|
|
32
|
+
height: 10px;
|
|
33
|
+
}
|
|
34
|
+
.${this.styleId}::-webkit-scrollbar-track {
|
|
35
|
+
background: ${r};
|
|
36
|
+
}
|
|
37
|
+
.${this.styleId}::-webkit-scrollbar-thumb {
|
|
38
|
+
background-color: ${s};
|
|
39
|
+
border-radius: 6px;
|
|
40
|
+
border: 2px solid ${r==="transparent"?"transparent":r};
|
|
41
|
+
background-clip: content-box;
|
|
42
|
+
}
|
|
43
|
+
.${this.styleId}::-webkit-scrollbar-thumb:hover {
|
|
44
|
+
opacity: 0.8;
|
|
45
|
+
}
|
|
46
|
+
.${this.styleId}::-webkit-scrollbar-corner {
|
|
47
|
+
background: ${r};
|
|
48
|
+
}
|
|
49
|
+
`}}updateVirtualSize(t,e){const i=this.container.clientWidth||100,s=this.container.clientHeight||100,r=Math.min(t,this.MAX_BROWSER_SIZE),n=Math.min(e,this.MAX_BROWSER_SIZE);this.scaleX=(t-i)/Math.max(1,r-i),this.scaleY=(e-s)/Math.max(1,n-s),this.shadow.style.width=`${r}px`,this.shadow.style.height=`${n}px`}setScroll(t,e){this.container.scrollLeft=t/(this.scaleX||1),this.container.scrollTop=e/(this.scaleY||1)}scrollToCell(t,e,i,s){const r=this.container.scrollLeft*this.scaleX,n=this.container.scrollTop*this.scaleY,o=this.container.clientWidth,l=this.container.clientHeight;let h=r,a=n;t<r?h=t:t+i>r+o&&(h=t+i-o),e<n?a=e:e+s>n+l&&(a=e+s-l),(h!==r||a!==n)&&this.setScroll(h,a)}setupEvents(){let t=0,e=0;this.container.addEventListener("scroll",()=>{const i=Math.round(this.container.scrollLeft*this.scaleX),s=Math.round(this.container.scrollTop*this.scaleY);(i!==t||s!==e)&&(t=i,e=s,this.onScroll({scrollX:i,scrollY:s}))},{passive:!0}),this.container.addEventListener("mousedown",i=>{this.onMouseDown(i)}),this.container.addEventListener("dblclick",i=>{this.onDoubleClick(i)})}}class P{constructor(t,e,i,s){this.state=t,this.provider=e,this.config=i,this.invalidate=s}updateSelection(t,e,i,s,r){if(this.state.selectionMode=t,t==="all"){this.state.selectionRanges=[{startRow:0,endRow:this.provider.getRowCount()-1,startCol:0,endCol:this.provider.getColumnCount()-1}],this.state.anchorRow=0,this.state.anchorCol=0,this.populateLookupSets(),this.invalidate(),this.notifySelectionChange();return}if(e===null&&t!=="column"||i===null&&t!=="row")return;const n=e??0,o=i??0;!s&&!r&&(this.state.selectionRanges=[]),r&&this.state.anchorRow!==null&&this.state.anchorCol!==null?this.handleRangeSelection(t,n,o,s):this.handlePivotSelection(t,n,o,s),this.populateLookupSets(),this.notifySelectionChange()}handleRangeSelection(t,e,i,s){const r=t==="column"?0:Math.min(e,this.state.anchorRow),n=t==="column"?this.provider.getRowCount()-1:Math.max(e,this.state.anchorRow),o=t==="row"?0:Math.min(i,this.state.anchorCol),l=t==="row"?this.provider.getColumnCount()-1:Math.max(i,this.state.anchorCol),h={startRow:r,endRow:n,startCol:o,endCol:l};s?this.state.selectionRanges.length>0?this.state.selectionRanges[this.state.selectionRanges.length-1]=h:this.state.selectionRanges.push(h):this.state.selectionRanges=[h]}handlePivotSelection(t,e,i,s){const r={startRow:t==="column"?0:e,endRow:t==="column"?this.provider.getRowCount()-1:e,startCol:t==="row"?0:i,endCol:t==="row"?this.provider.getColumnCount()-1:i};if(s){const n=this.state.selectionRanges.findIndex(o=>o.startRow===r.startRow&&o.endRow===r.endRow&&o.startCol===r.startCol&&o.endCol===r.endCol);n!==-1?this.state.selectionRanges.splice(n,1):this.state.selectionRanges.push(r)}else this.state.selectionRanges=[r];this.state.anchorRow=e,this.state.anchorCol=i}populateLookupSets(){this.state.selectedRows.clear(),this.state.selectedCols.clear();for(const t of this.state.selectionRanges){for(let e=t.startRow;e<=t.endRow;e++)this.state.selectedRows.add(e);for(let e=t.startCol;e<=t.endCol;e++)this.state.selectedCols.add(e)}}notifySelectionChange(){this.config.onSelectionChange&&this.config.onSelectionChange({mode:this.state.selectionMode,ranges:[...this.state.selectionRanges],anchorRow:this.state.anchorRow,anchorCol:this.state.anchorCol})}}class Y{constructor(t,e,i,s){this.state=t,this.provider=e,this.config=i,this.actions=s}handleKeyDown(t,e){if(e)return;let i=this.state.anchorRow??0,s=this.state.anchorCol??0;const r=this.provider.getRowCount(),n=this.provider.getColumnCount();let o=this.state.columnOrder.indexOf(s);switch(o===-1&&(o=s),t.key){case"ArrowUp":i=Math.max(0,i-1);break;case"ArrowDown":i=Math.min(r-1,i+1);break;case"ArrowLeft":o=Math.max(0,o-1);break;case"ArrowRight":o=Math.min(n-1,o+1);break;case"PageUp":i=Math.max(0,i-Math.floor(this.state.height/this.config.rowHeight));break;case"PageDown":i=Math.min(r-1,i+Math.floor(this.state.height/this.config.rowHeight));break;case"Home":o=0,t.ctrlKey&&(i=0);break;case"End":o=n-1,t.ctrlKey&&(i=r-1);break;case"c":if(t.ctrlKey||t.metaKey){this.actions.copyToClipboard();return}return;default:return}s=this.state.columnOrder[o]??o,t.preventDefault(),this.actions.updateSelection("cell",i,s,t.ctrlKey,t.shiftKey),this.actions.scrollToCell(i,s),this.actions.render()}}const I={Y:1e24,Z:1e21,E:1e18,P:1e15,T:1e12,G:1e9,M:1e6,k:1e3,h:100,da:10,d:.1,c:.01,m:.001,u:1e-6,μ:1e-6,n:1e-9,p:1e-12,f:1e-15,a:1e-18,z:1e-21,y:1e-24};function B(m){if(typeof m!="string")return parseFloat(m);const t=m.trim();if(!t)return NaN;const e=/^([+-]?\d*\.?\d+(?:[eE][+-]?\d+)?)\s*([YZEPTGMkhdacmuμnpfaz y]?)([a-zA-Z]*)$/,i=t.match(e);if(!i)return parseFloat(t);const[s,r,n]=i;let o=parseFloat(r);return n&&I[n]&&(o*=I[n]),o}class K{constructor(t,e,i,s,r){p(this,"editor",null);this.uiLayer=t,this.provider=e,this.config=i,this.state=s,this.helpers=r}get activeEditor(){return this.editor}closeEditor(){if(this.editor){const t=this.editor;this.editor=null,t.remove()}}openHeaderEditor(t,e){const i=this.provider.getHeader(t),s=e===0?i.name||"":e===1?i.units||"":i.description||"",r=this.state.columnOrder.indexOf(t),n=r===-1?t:r,o=this.helpers.getColumnOffset(n)-this.state.scrollX+(this.config.showRowNumbers?this.config.rowNumbersWidth:0),{titleH:l,subH:h}=this.getHeaderHeights(),a=e===0?0:e===1?l:l+h,u=e===0?l:h,c=document.createElement("input");this.editor=c,c.type="text",c.value=s,Object.assign(c.style,{position:"absolute",left:`${o}px`,top:`${a}px`,width:`${this.helpers.getColumnWidth(t)}px`,height:`${u}px`,boxSizing:"border-box",border:"2px solid #4facfe",outline:"none",zIndex:"100",pointerEvents:"auto",backgroundColor:this.config.headerBackground,padding:`0 ${this.config.cellPadding}px`});const g=()=>{var C,f;if(!this.editor)return;const d={...i};e===0?d.name=c.value:e===1?d.units=c.value:d.description=c.value,(f=(C=this.provider).setHeader)==null||f.call(C,t,d),this.closeEditor(),this.helpers.invalidate()};c.onblur=g,c.onkeydown=d=>{d.key==="Enter"&&g(),d.key==="Escape"&&this.closeEditor()},this.uiLayer.appendChild(c),setTimeout(()=>{this.editor===c&&(c.focus(),c.select())},0)}getHeaderHeights(){const t=this.state.headerHeight;return this.config.headerSubTextCount===1?{titleH:t*.75,subH:t*.25}:this.config.headerSubTextCount===2?{titleH:t*.5,subH:t*.25}:{titleH:t,subH:0}}openCellEditor(t,e){if(!this.provider.setCellData)return;const i=this.state.columnOrder.indexOf(e),s=i===-1?e:i,r=this.helpers.getColumnOffset(s)-this.state.scrollX+(this.config.showRowNumbers?this.config.rowNumbersWidth:0),n=t*this.config.rowHeight-this.state.scrollY+this.state.headerHeight;this.provider.getHeader(e).type==="select"?this.renderSelectEditor(t,e,r,n):this.renderTextEditor(t,e,r,n)}renderTextEditor(t,e,i,s){var o;const r=document.createElement("input");this.editor=r,r.value=((o=this.provider.getCellData(t,e))==null?void 0:o.toString())||"",Object.assign(r.style,{position:"absolute",left:`${i}px`,top:`${s}px`,zIndex:"10",width:`${this.helpers.getColumnWidth(e)}px`,height:`${this.config.rowHeight}px`,boxSizing:"border-box",border:"2px solid #4facfe",outline:"none",padding:`${this.config.cellPadding}px`,pointerEvents:"auto"});const n=()=>{if(this.editor){let l=r.value;if(this.provider.getHeader(e).type==="numeric"){const a=B(r.value);isNaN(a)||(l=a)}this.provider.setCellData(t,e,l),this.closeEditor(),this.helpers.invalidate()}};r.onkeydown=l=>{l.key==="Enter"&&n(),l.key==="Escape"&&this.closeEditor()},r.onblur=n,this.uiLayer.appendChild(r),setTimeout(()=>{r.focus(),r.select()},0)}renderSelectEditor(t,e,i,s){var l;const r=document.createElement("div");this.editor=r;const n=((l=this.provider.getCellData(t,e))==null?void 0:l.toString())||"";r.setAttribute("data-value",n),Object.assign(r.style,{position:"absolute",left:`${i}px`,top:`${s}px`,zIndex:"1000",width:`${this.helpers.getColumnWidth(e)}px`,height:`${this.config.rowHeight}px`,pointerEvents:"auto"});const o=document.createElement("ul");Object.assign(o.style,{position:"absolute",top:"100%",left:"0",width:"100%",maxHeight:"200px",overflowY:"auto",margin:"0",padding:"0",listStyle:"none",backgroundColor:this.config.headerBackground,border:`1px solid ${this.config.gridLineColor}`}),(this.provider.getHeader(e).selectOptions||[]).forEach(h=>{const a=document.createElement("li");a.textContent=h,a.style.padding="6px 10px",a.style.cursor="pointer",a.onclick=u=>{u.stopPropagation(),this.provider.setCellData(t,e,h),this.closeEditor(),this.helpers.invalidate()},o.appendChild(a)}),r.appendChild(o),this.uiLayer.appendChild(r),r.tabIndex=0,r.onkeydown=h=>{h.key==="Escape"&&this.closeEditor()},setTimeout(()=>r.focus(),0)}}function k(m,t,e){return e===-1?t.rowNumbersWidth:m.columnWidths[e]??t.columnWidth}function X(m,t){return m.columnOffsets[t]??m.columnOffsets[m.columnOffsets.length-1]??0}function M(m,t){const e=m.columnOffsets;if(!e||e.length===0)return-1;let i=0,s=e.length-2;for(;i<=s;){const n=i+s>>1,o=e[n],l=e[n+1];if(t>=o&&t<l)return n;t<o?s=n-1:i=n+1}const r=e[e.length-1]||0;return t>=r?Math.max(0,e.length-2):-1}class j{constructor(t,e,i,s,r){this.canvas=t,this.state=e,this.provider=i,this.config=s,this.actions=r}handleHit(t,e){this.actions.closeEditor();const i=this.canvas.getBoundingClientRect(),s=t.clientX-i.left,r=t.clientY-i.top,n=this.config.showRowNumbers?this.config.rowNumbersWidth:0,o=t.ctrlKey||t.metaKey,l=t.shiftKey;if(this.config.showRowNumbers&&Math.abs(s-n)<5){t.preventDefault(),this.actions.startResizing(-1,t);return}if(s<n&&r<this.state.headerHeight)this.actions.updateSelection("all",null,null,!1,!1);else if(r<this.state.headerHeight&&s>=n)this.handleHeaderHit(s-n+this.state.scrollX,r,e,o,l,t);else if(s<n&&r>=this.state.headerHeight){const h=Math.floor((r-this.state.headerHeight+this.state.scrollY)/this.config.rowHeight);h>=0&&h<this.provider.getRowCount()&&this.actions.updateSelection("row",h,null,o,l)}else this.handleCellHit(s,r,n,e,o,l);this.actions.render()}handleHeaderHit(t,e,i,s,r,n){const o=this.getHeaderHit(t,e);if(o.colIndex===-1)return;const l=this.state.columnOrder[o.colIndex]??o.colIndex,h=this.provider.getHeader(l);o.type==="edge"&&h.isResizable!==!1&&this.config.allowResizing?(n.preventDefault(),this.actions.startResizing(l,n)):o.type==="handle"&&(i?(n.preventDefault(),this.actions.openHeaderEditor(l,o.subIndex||0)):(this.actions.updateSelection("column",null,l,s,r),n.preventDefault(),this.actions.startReordering(o.colIndex,n)))}handleCellHit(t,e,i,s,r,n){const o=Math.floor((e-this.state.headerHeight+this.state.scrollY)/this.config.rowHeight),l=t-i+this.state.scrollX,h=M(this.state,l),a=this.state.columnOrder[h]??h;if(o<0||o>=this.provider.getRowCount()||h===-1)return;const u=this.provider.getHeader(a);if(u.type==="checkbox"&&!s&&this.provider.setCellData&&(this.provider.setCellData(o,a,!this.provider.getCellData(o,a)),this.actions.invalidate()),n)this.actions.updateSelection("cell",o,a,r,n);else if(s)u.type!=="progress"&&u.type!=="checkbox"&&this.actions.openEditor(o,a);else{this.actions.setSelecting(!0),this.actions.updateSelection("cell",o,a,r,n);const c=()=>{this.actions.setSelecting(!1),window.removeEventListener("mouseup",c)};window.addEventListener("mouseup",c)}}getHeaderHit(t,e){let i=0;const s=this.state.headerHeight,{titleH:r,subH:n}=this.getHeaderHeights(s);for(let o=0;o<this.state.columnOrder.length;o++){const l=k(this.state,this.config,this.state.columnOrder[o]);if(t>=i&&t<=i+l){if(t>=i+l-5)return{type:"edge",colIndex:o};if(o>0&&t<=i+5)return{type:"edge",colIndex:o-1};let h=0;return e>r&&(h=e<r+n?1:2),{type:"handle",colIndex:o,subIndex:h}}i+=l}return{type:"handle",colIndex:-1}}getHeaderHeights(t){return this.config.headerSubTextCount===1?{titleH:t*.75,subH:t*.25}:this.config.headerSubTextCount===2?{titleH:t*.5,subH:t*.25}:{titleH:t,subH:0}}}class _{constructor(t,e,i,s,r){this.canvas=t,this.state=e,this.provider=i,this.config=s,this.actions=r}startResizing(t,e){this.state.resizingCol=t;const i=e.clientX,s=k(this.state,this.config,t),r=o=>{const l=Math.max(30,s+(o.clientX-i));t===-1?this.config.rowNumbersWidth=l:this.state.columnWidths[t]=l,this.actions.updateVirtualSize(),this.actions.invalidate()},n=()=>{this.state.resizingCol=null,window.removeEventListener("mousemove",r),window.removeEventListener("mouseup",n),this.actions.saveState()};window.addEventListener("mousemove",r),window.addEventListener("mouseup",n)}startReordering(t,e){const i=e.clientX;let s=!1;const r=o=>{if(!s&&Math.abs(o.clientX-i)>3&&(s=!0,this.state.reorderingCol=t),!s)return;const l=this.canvas.getBoundingClientRect(),h=o.clientX-l.left;this.state.reorderingX=h;const a=M(this.state,h-(this.config.showRowNumbers?this.config.rowNumbersWidth:0)+this.state.scrollX);a!==-1&&(this.state.reorderingTarget=a),this.actions.invalidate()},n=()=>{s?this.state.reorderingTarget!==null&&this.state.reorderingCol!==null&&this.commitReorder():this.handleHeaderClick(t),this.state.reorderingCol=this.state.reorderingTarget=this.state.reorderingX=null,window.removeEventListener("mousemove",r),window.removeEventListener("mouseup",n),this.actions.saveState()};window.addEventListener("mousemove",r),window.addEventListener("mouseup",n)}handleHeaderClick(t){const e=this.state.columnOrder[t]??t,i=this.provider.getHeader(e);if(i.isSortable!==!1&&this.config.onSort){const s=i.sortOrder==="asc"?"desc":i.sortOrder==="desc"?null:"asc";this.config.onSort(e,s)}}commitReorder(){const t=[...this.state.columnOrder];if(t.length===0)for(let s=0;s<this.provider.getColumnCount();s++)t.push(s);const e=t.splice(this.state.reorderingCol,1)[0];let i=this.state.reorderingTarget;i>this.state.reorderingCol&&i--,t.splice(i,0,e),this.state.columnOrder=t,this.actions.updateSelection("column",null,e,!1,!1)}}class F{constructor(t,e,i){this.state=t,this.provider=e,this.config=i}saveState(){if(!this.config.persistenceKey)return;const t={columnOrder:this.state.columnOrder,columnWidths:this.state.columnWidths};localStorage.setItem(`scigrid_state_${this.config.persistenceKey}`,JSON.stringify(t))}loadState(){if(!this.config.persistenceKey)return null;const t=localStorage.getItem(`scigrid_state_${this.config.persistenceKey}`);try{return t?JSON.parse(t):null}catch{return null}}copyToClipboard(){const t=this.state.selectionRanges;if(t.length===0)return;let e=1/0,i=-1/0,s=1/0,r=-1/0;for(const o of t)e=Math.min(e,o.startRow),i=Math.max(i,o.endRow),s=Math.min(s,o.startCol),r=Math.max(r,o.endCol);let n="";for(let o=e;o<=i;o++){const l=[];for(let h=s;h<=r;h++){let a=!1;for(const c of t)if(o>=c.startRow&&o<=c.endRow&&h>=c.startCol&&h<=c.endCol){a=!0;break}const u=a?this.provider.getCellData(o,h):"";l.push(u!=null?`"${u.toString().replace(/"/g,'""')}"`:"")}n+=l.join(" ")+`
|
|
50
|
+
`}navigator.clipboard.writeText(n).catch(o=>console.error("Copy failed:",o))}}class V{constructor(t,e,i={}){p(this,"canvas");p(this,"renderer");p(this,"scroller");p(this,"selection");p(this,"keyboard");p(this,"editors");p(this,"mouse");p(this,"drags");p(this,"data");p(this,"uiLayer");p(this,"config");p(this,"state",{scrollX:0,scrollY:0,width:0,height:0,headerHeight:60,columnWidths:{},columnOrder:[],columnOffsets:[0],selectedRows:new Set,selectedCols:new Set,selectionRanges:[],anchorRow:null,anchorCol:null,resizingCol:null,reorderingCol:null,reorderingTarget:null,reorderingX:null,hoveredCol:null,selectionMode:"cell"});p(this,"lastRange",{start:-1,end:-1});p(this,"isDirty",!1);p(this,"isSelecting",!1);this.container=t,this.provider=e,this.config={rowHeight:25,columnWidth:100,headerHeight:30,showRowNumbers:!0,rowNumbersWidth:40,headerSubTextCount:0,headerPlaceholder:"-",allowResizing:!0,allowFiltering:!0,backgroundColor:"#ffffff",gridLineColor:"#e0e0e0",textColor:"#333333",font:"12px Inter, sans-serif",headerBackground:"#f3f3f3",headerTextColor:"#333333",headerFont:"bold 12px Inter, sans-serif",rowNumberBackground:"#f9f9f9",rowNumberTextColor:"#666666",selectionColor:"rgba(0, 120, 215, 0.3)",selectedTextColor:"#000000",cellPadding:5,...i},this.state.headerHeight=this.config.headerHeight,this.initDOM(),this.renderer=new D(this.canvas),this.initManagers(),this.scroller=new N(this.container,s=>this.updateScroll(s),s=>this.mouse.handleHit(s,!1),s=>this.mouse.handleHit(s,!0)),this.init()}initDOM(){Object.assign(this.container.style,{overflow:"auto",position:"relative",outline:"none",userSelect:"none",webkitUserSelect:"none"}),this.container.tabIndex=0,this.container.setAttribute("role","grid"),this.container.setAttribute("aria-label","Data Grid"),this.updateAriaCounts();const t=document.createElement("div");Object.assign(t.style,{position:"sticky",top:"0",left:"0",width:"100%",height:"100%",zIndex:"10"}),this.container.appendChild(t),this.canvas=document.createElement("canvas"),Object.assign(this.canvas.style,{position:"absolute",top:"0",left:"0",display:"block",zIndex:"1"}),t.appendChild(this.canvas),this.uiLayer=document.createElement("div"),Object.assign(this.uiLayer.style,{position:"absolute",top:"0",left:"0",width:"100%",height:"100%",zIndex:"2",pointerEvents:"none"}),t.appendChild(this.uiLayer)}updateAriaCounts(){this.container.setAttribute("aria-rowcount",(this.provider.getRowCount()+1).toString()),this.container.setAttribute("aria-colcount",this.provider.getColumnCount().toString())}initManagers(){this.selection=new P(this.state,this.provider,this.config,()=>this.invalidate()),this.data=new F(this.state,this.provider,this.config);const t={getColumnWidth:e=>k(this.state,this.config,e),getColumnOffset:e=>X(this.state,e),invalidate:()=>this.invalidate()};this.editors=new K(this.uiLayer,this.provider,this.config,this.state,t),this.drags=new _(this.canvas,this.state,this.provider,this.config,{invalidate:()=>this.invalidate(),updateSelection:(e,i,s,r,n)=>this.selection.updateSelection(e,i,s,r,n),updateVirtualSize:()=>this.updateVirtualSize(),saveState:()=>this.data.saveState()}),this.mouse=new j(this.canvas,this.state,this.provider,this.config,{updateSelection:(e,i,s,r,n)=>this.selection.updateSelection(e,i,s,r,n),invalidate:()=>this.invalidate(),render:()=>this.render(),startResizing:(e,i)=>this.drags.startResizing(e,i),startReordering:(e,i)=>this.drags.startReordering(e,i),openEditor:(e,i)=>this.editors.openCellEditor(e,i),openHeaderEditor:(e,i)=>this.editors.openHeaderEditor(e,i),closeEditor:()=>this.editors.closeEditor(),setSelecting:e=>this.isSelecting=e}),this.keyboard=new Y(this.state,this.provider,this.config,{updateSelection:(e,i,s,r,n)=>this.selection.updateSelection(e,i,s,r,n),scrollToCell:(e,i)=>this.scrollToCell(e,i),copyToClipboard:()=>this.data.copyToClipboard(),render:()=>this.render()})}init(){const t=this.data.loadState();if(t&&(this.state.columnOrder=t.columnOrder||[],this.state.columnWidths=t.columnWidths||{}),this.state.columnOrder.length===0)for(let e=0;e<this.provider.getColumnCount();e++)this.state.columnOrder.push(e);new ResizeObserver(e=>e.forEach(i=>this.resize(Math.floor(i.contentRect.width),Math.floor(i.contentRect.height)))).observe(this.container),this.container.addEventListener("keydown",e=>this.keyboard.handleKeyDown(e,!!this.editors.activeEditor)),this.container.addEventListener("mousemove",e=>this.handleMouseMove(e)),window.addEventListener("mousedown",e=>{this.editors.activeEditor&&!this.editors.activeEditor.contains(e.target)&&!this.container.contains(e.target)&&this.editors.closeEditor()}),window.addEventListener("mousemove",e=>this.handleWindowMouseMove(e)),window.addEventListener("mouseup",()=>this.handleWindowMouseUp()),this.updateVirtualSize(),this.requestAnimationFrame()}resize(t,e){this.state.width=t,this.state.height=e,this.renderer.resize(t,e),this.invalidate()}updateVirtualSize(){const t=this.provider.getColumnCount(),e=new Array(t+1);let i=0;e[0]=0;for(let s=0;s<t;s++)i+=k(this.state,this.config,this.state.columnOrder[s]??s),e[s+1]=i;this.state.columnOffsets=e,this.scroller.updateVirtualSize((this.config.showRowNumbers?this.config.rowNumbersWidth:0)+i,this.provider.getRowCount()*this.config.rowHeight+this.state.headerHeight)}handleMouseMove(t){if(this.state.resizingCol!==null||this.state.reorderingCol!==null){this.container.style.cursor=this.state.resizingCol!==null?"col-resize":"grabbing";return}const e=this.config.showRowNumbers?this.config.rowNumbersWidth:0,i=this.canvas.getBoundingClientRect(),s=t.clientX-i.left,r=t.clientY-i.top;if(this.config.showRowNumbers&&r<this.state.headerHeight&&Math.abs(s-e)<5){this.container.style.cursor="col-resize";return}const n=s-e+this.state.scrollX;if((r>=this.state.headerHeight||s<e)&&this.state.hoveredCol!==null&&(this.state.hoveredCol=null,this.render()),r<this.state.headerHeight&&s>=e){const o=M(this.state,n),l=this.state.columnOrder[o]??o;this.state.hoveredCol!==l&&(this.state.hoveredCol=l,this.render()),this.container.style.cursor="grab"}else this.container.style.cursor="default"}handleWindowMouseMove(t){if(this.isSelecting){const e=this.config.showRowNumbers?this.config.rowNumbersWidth:0,i=this.canvas.getBoundingClientRect(),s=t.clientX-i.left,r=t.clientY-i.top,n=s-e+this.state.scrollX,o=Math.max(0,Math.min(this.provider.getRowCount()-1,Math.floor((r-this.state.headerHeight+this.state.scrollY)/this.config.rowHeight))),l=M(this.state,n);l!==-1&&(this.selection.updateSelection("cell",o,this.state.columnOrder[l]??l,t.ctrlKey,!0),this.render())}}handleWindowMouseUp(){this.isSelecting&&(this.isSelecting=!1,this.render())}getColumnAt(t){return M(this.state,t)}getColumnOffset(t){return X(this.state,t)}getColumnWidth(t){return k(this.state,this.config,t)}saveState(){this.data.saveState()}scrollToCell(t,e){const i=this.state.columnOrder.indexOf(e);this.scroller.scrollToCell(this.getColumnOffset(i===-1?e:i)+(this.config.showRowNumbers?this.config.rowNumbersWidth:0),t*this.config.rowHeight+this.state.headerHeight,this.getColumnWidth(e),this.config.rowHeight)}invalidate(){this.isDirty=!0}render(){if(this.provider.onRowsNeeded){const t=Math.floor(this.state.scrollY/this.config.rowHeight),e=Math.ceil((this.state.scrollY+this.state.height)/this.config.rowHeight);(Math.abs(t-this.lastRange.start)>5||Math.abs(e-this.lastRange.end)>5)&&(this.lastRange={start:t,end:e},this.provider.onRowsNeeded(Math.max(0,t-50),Math.min(this.provider.getRowCount()-1,e+50)))}this.renderer.render(this.state,this.config,this.provider),this.isDirty=!1}renderNow(){this.render()}requestAnimationFrame(){const t=()=>{this.isDirty&&this.render(),requestAnimationFrame(t)};requestAnimationFrame(t)}updateProvider(t){this.provider=t,this.state.columnOrder=[];for(let e=0;e<t.getColumnCount();e++)this.state.columnOrder.push(e);this.updateVirtualSize(),this.updateAriaCounts(),this.invalidate()}updateConfig(t){this.config={...this.config,...t},this.state.headerHeight=this.config.headerHeight,this.editors.closeEditor(),this.scroller.updateScrollStyle(this.config.scrollbarThumbColor,this.config.scrollbarColor),this.updateVirtualSize(),this.invalidate()}updateScroll(t){t.scrollX!==void 0&&(this.state.scrollX=t.scrollX),t.scrollY!==void 0&&(this.state.scrollY=t.scrollY),this.editors.closeEditor(),this.render()}destroy(){this.container.innerHTML=""}}exports.SciGrid=V;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { GridConfig, IDataGridProvider } from './types/grid.js';
|
|
2
|
+
export type { GridConfig, IDataGridProvider, ViewportState, SelectionInfo, SelectionMode, SelectionRange, ColumnHeaderInfo, GridDataValue, ColumnType } from './types/grid.js';
|
|
3
|
+
export declare class SciGrid {
|
|
4
|
+
private container;
|
|
5
|
+
private provider;
|
|
6
|
+
private canvas;
|
|
7
|
+
private renderer;
|
|
8
|
+
private scroller;
|
|
9
|
+
private selection;
|
|
10
|
+
private keyboard;
|
|
11
|
+
private editors;
|
|
12
|
+
private mouse;
|
|
13
|
+
private drags;
|
|
14
|
+
private data;
|
|
15
|
+
private uiLayer;
|
|
16
|
+
private config;
|
|
17
|
+
private state;
|
|
18
|
+
private lastRange;
|
|
19
|
+
private isDirty;
|
|
20
|
+
private isSelecting;
|
|
21
|
+
constructor(container: HTMLElement, provider: IDataGridProvider, config?: Partial<GridConfig>);
|
|
22
|
+
private initDOM;
|
|
23
|
+
private updateAriaCounts;
|
|
24
|
+
private initManagers;
|
|
25
|
+
private init;
|
|
26
|
+
private resize;
|
|
27
|
+
private updateVirtualSize;
|
|
28
|
+
private handleMouseMove;
|
|
29
|
+
private handleWindowMouseMove;
|
|
30
|
+
private handleWindowMouseUp;
|
|
31
|
+
getColumnAt(x: number): number;
|
|
32
|
+
getColumnOffset(idx: number): number;
|
|
33
|
+
getColumnWidth(col: number): number;
|
|
34
|
+
saveState(): void;
|
|
35
|
+
private scrollToCell;
|
|
36
|
+
invalidate(): void;
|
|
37
|
+
private render;
|
|
38
|
+
renderNow(): void;
|
|
39
|
+
private requestAnimationFrame;
|
|
40
|
+
updateProvider(p: IDataGridProvider): void;
|
|
41
|
+
updateConfig(c: Partial<GridConfig>): void;
|
|
42
|
+
private updateScroll;
|
|
43
|
+
destroy(): void;
|
|
44
|
+
}
|