table-minimap 1.0.7 → 1.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/LICENSE +22 -0
- package/README.md +7 -0
- package/dist/index.d.ts +11 -6
- package/dist/shadcn.css +2 -1
- package/dist/table-minimap.cjs +1 -1
- package/dist/table-minimap.cjs.map +1 -1
- package/dist/table-minimap.js +126 -123
- package/dist/table-minimap.js.map +1 -1
- package/package.json +1 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kevin Imig
|
|
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.
|
|
22
|
+
|
package/README.md
CHANGED
|
@@ -190,6 +190,12 @@ interface TableMinimapOptions {
|
|
|
190
190
|
*/
|
|
191
191
|
position?: 'top' | 'bottom' | 'fixed';
|
|
192
192
|
|
|
193
|
+
/**
|
|
194
|
+
* Corner position when using position: 'fixed'
|
|
195
|
+
* @default "bottom-right"
|
|
196
|
+
*/
|
|
197
|
+
fixedPosition?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
|
|
198
|
+
|
|
193
199
|
/**
|
|
194
200
|
* Enable compact floating mode for fixed minimaps
|
|
195
201
|
* @default false
|
|
@@ -241,6 +247,7 @@ interface TableMinimapOptions {
|
|
|
241
247
|
| `mode` | `'columns'` |
|
|
242
248
|
| `height` | `40` |
|
|
243
249
|
| `position` | `'bottom'` |
|
|
250
|
+
| `fixedPosition` | `'bottom-right'` |
|
|
244
251
|
| `compact` | `false` |
|
|
245
252
|
| `draggable` | `true` |
|
|
246
253
|
| `showViewport` | `true` |
|
package/dist/index.d.ts
CHANGED
|
@@ -78,6 +78,8 @@ export declare class TableMinimap {
|
|
|
78
78
|
private readonly isCompactMode;
|
|
79
79
|
/** Whether the compact minimap is currently collapsed */
|
|
80
80
|
private isCompactCollapsed;
|
|
81
|
+
/** Whether the compact minimap is currently expanding (transition in progress) */
|
|
82
|
+
private isCompactExpanding;
|
|
81
83
|
/** Timeout used to collapse compact mode after pointer leave */
|
|
82
84
|
private compactCollapseTimer;
|
|
83
85
|
/** The scrollable container (parent of table) */
|
|
@@ -108,9 +110,7 @@ export declare class TableMinimap {
|
|
|
108
110
|
private panStartX;
|
|
109
111
|
/** Currently hovered column index (-1 = none) */
|
|
110
112
|
private hoveredColumn;
|
|
111
|
-
/**
|
|
112
|
-
private focusedColumn;
|
|
113
|
-
/** Drag start position */
|
|
113
|
+
/** Drag start X position */
|
|
114
114
|
private dragStartX;
|
|
115
115
|
/** Drag start scroll position */
|
|
116
116
|
private dragStartScrollLeft;
|
|
@@ -192,7 +192,7 @@ export declare class TableMinimap {
|
|
|
192
192
|
private getColumnAtX;
|
|
193
193
|
/**
|
|
194
194
|
* Updates the viewport indicator position and size
|
|
195
|
-
*
|
|
195
|
+
* Shows the visible portion of the table (columns mode only)
|
|
196
196
|
*/
|
|
197
197
|
private updateViewport;
|
|
198
198
|
/**
|
|
@@ -338,7 +338,7 @@ export declare class TableMinimap {
|
|
|
338
338
|
* Sets the zoom level programmatically (canvas mode only)
|
|
339
339
|
*
|
|
340
340
|
* @param level - Zoom level (1 = no zoom)
|
|
341
|
-
* @param panX - Optional pan position (0-1)
|
|
341
|
+
* @param panX - Optional pan position (0-1), controls which part of table is visible
|
|
342
342
|
*/
|
|
343
343
|
setZoom(level: number, panX?: number): void;
|
|
344
344
|
/**
|
|
@@ -386,6 +386,11 @@ export declare interface TableMinimapOptions {
|
|
|
386
386
|
* @default 300
|
|
387
387
|
*/
|
|
388
388
|
fixedWidth?: number;
|
|
389
|
+
/**
|
|
390
|
+
* Corner position when using position: 'fixed'
|
|
391
|
+
* @default "bottom-right"
|
|
392
|
+
*/
|
|
393
|
+
fixedPosition?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
|
|
389
394
|
/**
|
|
390
395
|
* Enable compact floating mode for fixed minimaps.
|
|
391
396
|
* When enabled, the minimap collapses into a small dot handle in the
|
|
@@ -436,7 +441,7 @@ export declare type TableSelector = string | HTMLTableElement;
|
|
|
436
441
|
export declare interface ZoomState {
|
|
437
442
|
/** Current zoom level (1 = no zoom) */
|
|
438
443
|
level: number;
|
|
439
|
-
/**
|
|
444
|
+
/** @deprecated Pan offset is now derived from scroll position */
|
|
440
445
|
panX: number;
|
|
441
446
|
/** Whether zoom is at minimum (showing full overview) */
|
|
442
447
|
isMinZoom: boolean;
|
package/dist/shadcn.css
CHANGED
package/dist/table-minimap.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var R=Object.defineProperty;var X=(u,t,e)=>t in u?R(u,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):u[t]=e;var n=(u,t,e)=>X(u,typeof t!="symbol"?t+"":t,e);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const A={mode:"columns",height:40,position:"bottom",fixedWidth:300,compact:!1,draggable:!0,showViewport:!0,zoomable:!1,minZoom:1,maxZoom:10,zoomSpeed:.1},T=24,O=5,I=180;class Z{constructor(t,e={}){n(this,"table");n(this,"options");n(this,"isCompactMode");n(this,"isCompactCollapsed",!1);n(this,"compactCollapseTimer",null);n(this,"scrollContainer",null);n(this,"minimapEl",null);n(this,"columnsEl",null);n(this,"canvasEl",null);n(this,"canvasCtx",null);n(this,"viewportEl",null);n(this,"columns",[]);n(this,"scrollState",{scrollLeft:0,scrollWidth:0,clientWidth:0,viewportRatio:1,positionRatio:0});n(this,"zoomState",{level:1,panX:0,isMinZoom:!0,isMaxZoom:!1});n(this,"isDragging",!1);n(this,"isPanning",!1);n(this,"wasPanning",!1);n(this,"panStartX",0);n(this,"hoveredColumn",-1);n(this,"focusedColumn",-1);n(this,"dragStartX",0);n(this,"dragStartScrollLeft",0);n(this,"resizeObserver",null);n(this,"mutationObserver",null);n(this,"boundHandlers");n(this,"rafId",null);n(this,"isDestroyed",!1);n(this,"isPotentialPan",!1);this.table=this.resolveTable(t),this.options={...A,...e},this.isCompactMode=this.options.compact&&this.options.position==="fixed",this.boundHandlers={onScroll:this.onScroll.bind(this),onPointerDown:this.onPointerDown.bind(this),onPointerMove:this.onPointerMove.bind(this),onPointerUp:this.onPointerUp.bind(this),onMinimapClick:this.onMinimapClick.bind(this),onWheel:this.onWheel.bind(this),onCanvasPointerDown:this.onCanvasPointerDown.bind(this),onCanvasMouseMove:this.onCanvasMouseMove.bind(this),onCanvasMouseLeave:this.onCanvasMouseLeave.bind(this),onCompactFocusIn:this.onCompactFocusIn.bind(this),onCompactFocusOut:this.onCompactFocusOut.bind(this),onCompactKeyDown:this.onCompactKeyDown.bind(this),onDocumentClick:this.onDocumentClick.bind(this)},this.init()}resolveTable(t){let e;if(typeof t=="string"){if(e=document.querySelector(t),!e)throw new Error(`TableMinimap: No element found for selector "${t}"`)}else if(t instanceof HTMLTableElement)e=t;else throw new Error("TableMinimap: Selector must be a CSS selector string or an HTMLTableElement");if(e.tagName!=="TABLE")throw new Error(`TableMinimap: Element must be a <table>, got <${e.tagName.toLowerCase()}>`);return e}init(){this.scrollContainer=this.findScrollContainer(),this.detectColumns(),this.createMinimapElement(),this.updateScrollState(),this.render(),this.attachEventListeners(),this.setupObservers()}findScrollContainer(){let t=this.table.parentElement;for(;t;){const e=getComputedStyle(t),i=e.overflowX,o=e.overflow;if(i==="auto"||i==="scroll"||o==="auto"||o==="scroll"||t.scrollWidth>t.clientWidth)return t;t=t.parentElement}return this.table.parentElement||document.body}detectColumns(){this.columns=[];const t=this.table.querySelectorAll("thead th, thead td");let e;if(t.length>0)e=t;else{const o=this.table.querySelector("tr");o?e=o.querySelectorAll("th, td"):e=[]}const i=this.table.offsetWidth||1;Array.from(e).forEach(o=>{const r=o.colSpan||1,s=o.offsetWidth;for(let a=0;a<r;a++){const l=s/r;this.columns.push({index:this.columns.length,width:l,widthPercent:l/i*100})}}),this.columns.length===0&&this.columns.push({index:0,width:i,widthPercent:100})}createMinimapElement(){this.minimapEl=document.createElement("div"),this.minimapEl.className=`tm-minimap tm-minimap--${this.options.position}`,this.minimapEl.style.setProperty("--tm-minimap-height",`${this.options.height}px`),this.options.position==="fixed"&&this.minimapEl.style.setProperty("--tm-minimap-width",`${this.options.fixedWidth}px`),this.isCompactMode?(this.minimapEl.classList.add("tm-minimap--compact","tm-minimap--compact-collapsed"),this.minimapEl.style.setProperty("--tm-compact-dot-size",`${O}px`),this.isCompactCollapsed=!0,this.applyCompactDimensions(!0),this.minimapEl.setAttribute("aria-expanded","false")):this.minimapEl.setAttribute("aria-expanded","true"),this.minimapEl.setAttribute("role","slider"),this.minimapEl.setAttribute("aria-label","Table minimap navigation"),this.minimapEl.setAttribute("aria-valuemin","0"),this.minimapEl.setAttribute("aria-valuemax","100"),this.minimapEl.setAttribute("tabindex","0"),this.options.mode==="canvas"?this.createCanvasContent():this.createColumnsContent(),this.options.showViewport&&this.createViewportIndicator(),this.insertMinimap()}createColumnsContent(){this.minimapEl&&(this.columnsEl=document.createElement("div"),this.columnsEl.className="tm-columns",this.columns.forEach(t=>{const e=document.createElement("div");e.className="tm-column",e.style.width=`${t.widthPercent}%`,this.columnsEl.appendChild(e)}),this.minimapEl.appendChild(this.columnsEl))}createCanvasContent(){this.minimapEl&&(this.canvasEl=document.createElement("canvas"),this.canvasEl.className="tm-canvas",this.canvasCtx=this.canvasEl.getContext("2d"),this.minimapEl.appendChild(this.canvasEl))}createViewportIndicator(){this.minimapEl&&(this.viewportEl=document.createElement("div"),this.viewportEl.className="tm-viewport",this.options.draggable||this.viewportEl.classList.add("tm-viewport--disabled"),this.minimapEl.appendChild(this.viewportEl))}insertMinimap(){if(!this.minimapEl||!this.scrollContainer)return;const t=this.scrollContainer.parentElement;if(this.options.position==="fixed"){if(t){getComputedStyle(t).position==="static"&&(t.style.position="relative");const i=this.scrollContainer.nextSibling;i?t.insertBefore(this.minimapEl,i):t.appendChild(this.minimapEl),this.minimapEl.style.position="absolute";const o=this.isCompactMode?8:12;this.minimapEl.style.bottom=`${o}px`,this.minimapEl.style.right=`${o}px`,this.minimapEl.style.marginTop="0"}}else if(this.options.position==="top")t?t.insertBefore(this.minimapEl,this.scrollContainer):this.scrollContainer.insertBefore(this.minimapEl,this.scrollContainer.firstChild);else if(t){const e=this.scrollContainer.nextSibling;e?t.insertBefore(this.minimapEl,e):t.appendChild(this.minimapEl)}else this.scrollContainer.appendChild(this.minimapEl)}updateScrollState(){if(!this.scrollContainer)return;const{scrollLeft:t,scrollWidth:e,clientWidth:i}=this.scrollContainer;this.scrollState={scrollLeft:t,scrollWidth:e,clientWidth:i,viewportRatio:i/Math.max(e,1),positionRatio:t/Math.max(e-i,1)},this.scrollState.viewportRatio=Math.min(1,Math.max(0,this.scrollState.viewportRatio)),this.scrollState.positionRatio=Math.min(1,Math.max(0,this.scrollState.positionRatio)),this.minimapEl&&this.minimapEl.setAttribute("aria-valuenow",String(Math.round(this.scrollState.positionRatio*100)))}render(){this.isDestroyed||this.isCompactMode&&this.isCompactCollapsed||(this.updateViewport(),this.options.mode==="canvas"&&this.renderCanvas())}getCanvasMetrics(){var m;const t=((m=this.minimapEl)==null?void 0:m.offsetWidth)??0,e=this.zoomState.level,i=this.columns.length,o=1/e,r=i*o,s=r>0?t/r:0;let a=0;if(e>1&&this.scrollContainer){const{scrollLeft:p,scrollWidth:E,clientWidth:g}=this.scrollContainer,c=Math.max(E-g,1);a=p/c*(1-o)}const l=a*i,d=Math.floor(l),v=Math.min(Math.ceil(l+r)+1,i),f=-(l-d)*s;return{width:t,zoom:e,numCols:i,visibleRatio:o,visibleCols:r,cellWidth:s,panX:a,startColFloat:l,startCol:d,endCol:v,xOffset:f}}getColumnAtX(t){const{width:e,numCols:i,panX:o,visibleRatio:r}=this.getCanvasMetrics();if(i===0||e===0)return-1;const s=t/e,a=o+s*r,l=Math.floor(a*i);return Math.max(0,Math.min(i-1,l))}updateViewport(){if(!this.viewportEl||!this.minimapEl)return;if(this.options.mode==="canvas"){if(this.focusedColumn>=0&&this.columns.length>0){const{cellWidth:r,startColFloat:s}=this.getCanvasMetrics(),a=(this.focusedColumn-s)*r;this.viewportEl.style.cssText=`width:${r}px;left:${a}px;display:block`}else this.viewportEl.style.display="none";return}const t=this.minimapEl.offsetWidth,e=Math.max(t*this.scrollState.viewportRatio,20),o=(t-e)*this.scrollState.positionRatio;this.viewportEl.style.cssText=`width:${e}px;left:${o}px;display:block`}renderCanvas(){if(!this.canvasEl||!this.canvasCtx||!this.minimapEl)return;const t=this.getCanvasMetrics(),e=this.options.height,i=window.devicePixelRatio||1;this.canvasEl.width=t.width*i,this.canvasEl.height=e*i,this.canvasEl.style.width=`${t.width}px`,this.canvasEl.style.height=`${e}px`,this.canvasCtx.scale(i,i),this.renderTableDirect(t,e)}renderTableDirect(t,e){var M,P,L,D;if(!this.canvasCtx)return;const i=this.canvasCtx,{width:o,numCols:r,cellWidth:s,startCol:a,endCol:l,xOffset:d}=t,v=Array.from(this.table.querySelectorAll("tr")),f=v.length;if(f===0||r===0)return;const m=Math.min(e*.15,30),p=(e-m)/f,E=Math.min(p*.6,s*.15,14),g=Math.min(m*.6,s*.15,14),c={bg:"#ffffff",headerBg:"#f1f5f9",border:"#e2e8f0",text:"#334155",headerText:"#1e293b",altRow:"#f8fafc",hoverFill:"rgba(59, 130, 246, 0.08)",hoverStroke:"rgba(59, 130, 246, 0.3)"};i.fillStyle=c.bg,i.fillRect(0,0,o,e),i.fillStyle=c.headerBg,i.fillRect(0,0,o,m);const w=this.table.querySelector("thead tr")||v[0],y=w?Array.from(w.querySelectorAll("th, td")):[];i.font=`bold ${g}px system-ui, sans-serif`,i.textBaseline="middle";for(let h=a;h<l;h++){const b=d+(h-a)*s;if(b+s<0||b>o)continue;i.strokeStyle=c.border,i.lineWidth=1,i.strokeRect(b,0,s,m);const C=((P=(M=y[h])==null?void 0:M.textContent)==null?void 0:P.trim())||`Col ${h+1}`;i.fillStyle=c.headerText,i.save(),i.beginPath(),i.rect(b+2,0,s-4,m),i.clip(),i.fillText(C,b+4,m/2),i.restore()}i.font=`${E}px system-ui, sans-serif`;for(let h=0;h<v.length;h++){const b=v[h];if(b.closest("thead"))continue;const C=m+h*p;if(C+p<0||C>e)continue;h%2===1&&(i.fillStyle=c.altRow,i.fillRect(0,C,o,p));const H=Array.from(b.querySelectorAll("th, td"));for(let S=a;S<l;S++){const x=d+(S-a)*s;if(x+s<0||x>o)continue;i.strokeStyle=c.border,i.lineWidth=.5,i.strokeRect(x,C,s,p);const k=(D=(L=H[S])==null?void 0:L.textContent)==null?void 0:D.trim();k&&(i.fillStyle=c.text,i.save(),i.beginPath(),i.rect(x+2,C,s-4,p),i.clip(),i.fillText(k,x+4,C+p/2),i.restore())}}if(this.hoveredColumn>=a&&this.hoveredColumn<l){const h=d+(this.hoveredColumn-a)*s;i.fillStyle=c.hoverFill,i.fillRect(h,0,s,e),i.strokeStyle=c.hoverStroke,i.lineWidth=1,i.strokeRect(h,0,s,e)}}attachEventListeners(){!this.scrollContainer||!this.minimapEl||(this.scrollContainer.addEventListener("scroll",this.boundHandlers.onScroll,{passive:!0}),this.minimapEl.addEventListener("click",this.boundHandlers.onMinimapClick),this.isCompactMode&&(this.minimapEl.addEventListener("focusin",this.boundHandlers.onCompactFocusIn),this.minimapEl.addEventListener("focusout",this.boundHandlers.onCompactFocusOut),this.minimapEl.addEventListener("keydown",this.boundHandlers.onCompactKeyDown),document.addEventListener("click",this.boundHandlers.onDocumentClick)),this.options.draggable&&this.viewportEl&&this.viewportEl.addEventListener("pointerdown",this.boundHandlers.onPointerDown),this.options.zoomable&&this.options.mode==="canvas"&&this.canvasEl&&(this.canvasEl.addEventListener("wheel",this.boundHandlers.onWheel,{passive:!1}),this.canvasEl.addEventListener("pointerdown",this.boundHandlers.onCanvasPointerDown),this.canvasEl.style.cursor=this.zoomState.level>1?"grab":"pointer",this.viewportEl&&this.viewportEl.addEventListener("wheel",this.boundHandlers.onWheel,{passive:!1})),this.options.mode==="canvas"&&this.canvasEl&&(this.canvasEl.addEventListener("mousemove",this.boundHandlers.onCanvasMouseMove),this.canvasEl.addEventListener("mouseleave",this.boundHandlers.onCanvasMouseLeave)),document.addEventListener("pointermove",this.boundHandlers.onPointerMove),document.addEventListener("pointerup",this.boundHandlers.onPointerUp))}onScroll(){this.isDragging||this.rafId===null&&(this.rafId=requestAnimationFrame(()=>{this.updateScrollState(),this.updateViewport(),this.options.mode==="canvas"&&this.render(),this.rafId=null}))}onMinimapClick(t){if(!this.minimapEl||!this.scrollContainer)return;if(this.isCompactMode&&this.isCompactCollapsed){t.preventDefault(),this.expandCompact();return}if(this.isDragging||this.isPanning||this.wasPanning||t.target===this.viewportEl)return;const{scrollWidth:e,clientWidth:i}=this.scrollContainer,o=this.columns.length;if(this.hoveredColumn>=0&&o>0){this.focusedColumn=this.hoveredColumn;const E=e/o,c=(this.hoveredColumn+.5)*E-i/2,w=e-i,y=Math.max(0,Math.min(w,c));this.scrollContainer.scrollTo({left:y,behavior:"smooth"}),this.updateViewport();return}const r=this.minimapEl.getBoundingClientRect(),a=(t.clientX-r.left)/r.width,l=this.zoomState.level;let d;if(l>1){const g=this.scrollContainer.scrollLeft/Math.max(e-i,1),c=1/l;d=g*(1-c)+a*c}else d=a;const f=d*e-i/2,m=e-i,p=Math.max(0,Math.min(m,f));this.scrollContainer.scrollTo({left:p,behavior:"smooth"})}onPointerDown(t){!this.viewportEl||!this.scrollContainer||(t.preventDefault(),t.stopPropagation(),this.isDragging=!0,this.dragStartX=t.clientX,this.dragStartScrollLeft=this.scrollContainer.scrollLeft,this.viewportEl.classList.add("tm-viewport--dragging"),this.viewportEl.setPointerCapture(t.pointerId))}onPointerMove(t){if(this.isPotentialPan&&!this.isPanning&&this.canvasEl&&this.zoomState.level>1&&Math.abs(t.clientX-this.panStartX)>3&&(this.isPanning=!0,this.canvasEl.style.cursor="grabbing"),this.isPanning&&this.canvasEl&&this.minimapEl&&this.scrollContainer){t.preventDefault();const a=t.clientX-this.panStartX,l=this.minimapEl.offsetWidth,{scrollWidth:d,clientWidth:v}=this.scrollContainer,f=d-v,m=a/l*f*this.zoomState.level,p=this.dragStartScrollLeft+m;this.scrollContainer.scrollLeft=Math.max(0,Math.min(f,p)),this.updateScrollState(),this.updateViewport(),this.render();return}if(!this.isDragging||!this.minimapEl||!this.scrollContainer)return;t.preventDefault();const e=t.clientX-this.dragStartX,i=this.minimapEl.offsetWidth,o=this.scrollContainer.scrollWidth-this.scrollContainer.clientWidth,r=e/i*o,s=this.dragStartScrollLeft+r;this.scrollContainer.scrollLeft=Math.max(0,Math.min(o,s)),this.updateScrollState(),this.updateViewport()}onPointerUp(t){if(this.isPotentialPan&&!this.isPanning&&this.canvasEl&&this.minimapEl){this.isPotentialPan=!1,this.canvasEl.hasPointerCapture(t.pointerId)&&this.canvasEl.releasePointerCapture(t.pointerId);const e=new MouseEvent("click",{clientX:t.clientX,clientY:t.clientY,bubbles:!0});this.minimapEl.dispatchEvent(e);return}this.isPotentialPan=!1,this.isPanning&&this.canvasEl&&(this.isPanning=!1,this.wasPanning=!0,this.canvasEl.hasPointerCapture(t.pointerId)&&this.canvasEl.releasePointerCapture(t.pointerId),this.canvasEl.style.cursor=this.zoomState.level>1?"grab":"pointer",setTimeout(()=>{this.wasPanning=!1},100)),this.isDragging&&(this.isDragging=!1,this.viewportEl&&(this.viewportEl.classList.remove("tm-viewport--dragging"),this.viewportEl.releasePointerCapture(t.pointerId)))}onWheel(t){if(!this.options.zoomable||this.options.mode!=="canvas")return;t.preventDefault();const e=-t.deltaY*this.options.zoomSpeed,i=Math.max(this.options.minZoom,Math.min(this.options.maxZoom,this.zoomState.level+e));this.zoomState={level:i,panX:0,isMinZoom:i<=this.options.minZoom,isMaxZoom:i>=this.options.maxZoom},this.render()}onCanvasPointerDown(t){!this.canvasEl||!this.scrollContainer||(this.isPotentialPan=!0,this.panStartX=t.clientX,this.dragStartScrollLeft=this.scrollContainer.scrollLeft,this.zoomState.level>1&&(t.preventDefault(),this.canvasEl.setPointerCapture(t.pointerId)))}onCanvasMouseMove(t){if(!this.canvasEl||this.isPanning)return;const e=this.canvasEl.getBoundingClientRect(),i=this.getColumnAtX(t.clientX-e.left);i!==this.hoveredColumn&&(this.hoveredColumn=i,this.canvasEl.style.cursor=i>=0?"pointer":this.zoomState.level>1?"grab":"default",this.render())}onCanvasMouseLeave(){this.hoveredColumn!==-1&&(this.hoveredColumn=-1,this.canvasEl&&(this.canvasEl.style.cursor=this.zoomState.level>1?"grab":"default"),this.render())}expandCompact(){!this.isCompactMode||!this.minimapEl||(this.clearCompactCollapseTimer(),this.applyCompactDimensions(!1),this.render())}collapseCompact(){!this.isCompactMode||!this.minimapEl||this.applyCompactDimensions(!0)}applyCompactDimensions(t){if(!(!this.minimapEl||!this.isCompactMode)){if(this.isCompactCollapsed=t,t){this.minimapEl.classList.add("tm-minimap--compact-collapsed"),this.minimapEl.classList.remove("tm-minimap--compact-expanded"),this.minimapEl.style.setProperty("--tm-minimap-width",`${T}px`),this.minimapEl.style.setProperty("--tm-minimap-height",`${T}px`),this.minimapEl.setAttribute("aria-expanded","false");return}this.minimapEl.classList.remove("tm-minimap--compact-collapsed"),this.minimapEl.classList.add("tm-minimap--compact-expanded"),this.minimapEl.style.setProperty("--tm-minimap-width",`${this.options.fixedWidth}px`),this.minimapEl.style.setProperty("--tm-minimap-height",`${this.options.height}px`),this.minimapEl.setAttribute("aria-expanded","true")}}clearCompactCollapseTimer(){this.compactCollapseTimer!==null&&(clearTimeout(this.compactCollapseTimer),this.compactCollapseTimer=null)}scheduleCompactCollapse(t=I){this.isCompactMode&&(this.clearCompactCollapseTimer(),this.compactCollapseTimer=window.setTimeout(()=>{this.collapseCompact(),this.compactCollapseTimer=null},t))}onDocumentClick(t){!this.isCompactMode||!this.minimapEl||this.isCompactCollapsed||this.minimapEl.contains(t.target)||this.collapseCompact()}onCompactFocusIn(){this.isCompactMode&&this.expandCompact()}onCompactFocusOut(){this.isCompactMode&&this.scheduleCompactCollapse(0)}onCompactKeyDown(t){if(this.isCompactMode){if(t.key==="Escape"){t.preventDefault(),this.collapseCompact();return}(t.key==="Enter"||t.key===" ")&&(t.preventDefault(),this.isCompactCollapsed?this.expandCompact():this.collapseCompact())}}setupObservers(){this.resizeObserver=new ResizeObserver(()=>{this.onResize()}),this.scrollContainer&&this.resizeObserver.observe(this.scrollContainer),this.resizeObserver.observe(this.table),this.mutationObserver=new MutationObserver(t=>{t.some(i=>i.type==="childList"||i.attributeName==="colspan")&&this.onTableMutation()}),this.mutationObserver.observe(this.table,{childList:!0,subtree:!0,attributes:!0,attributeFilter:["colspan"]})}onResize(){this.isDestroyed||(this.rafId!==null&&cancelAnimationFrame(this.rafId),this.rafId=requestAnimationFrame(()=>{this.detectColumns(),this.updateScrollState(),this.render(),this.options.mode==="columns"&&this.columnsEl&&this.minimapEl&&(this.columnsEl.innerHTML="",this.columns.forEach(t=>{const e=document.createElement("div");e.className="tm-column",e.style.width=`${t.widthPercent}%`,this.columnsEl.appendChild(e)})),this.rafId=null}))}onTableMutation(){this.isDestroyed||(this.detectColumns(),this.updateScrollState(),this.render(),this.options.mode==="columns"&&this.columnsEl&&(this.columnsEl.innerHTML="",this.columns.forEach(t=>{const e=document.createElement("div");e.className="tm-column",e.style.width=`${t.widthPercent}%`,this.columnsEl.appendChild(e)})))}getScrollState(){return{...this.scrollState}}getColumns(){return[...this.columns]}scrollToColumn(t,e=!0){if(!this.scrollContainer||t<0||t>=this.columns.length)return;const o=this.columns.slice(0,t).reduce((r,s)=>r+s.width,0);this.scrollContainer.scrollTo({left:o,behavior:e?"smooth":"auto"})}refresh(){this.isDestroyed||(this.scrollContainer=this.findScrollContainer(),this.detectColumns(),this.updateScrollState(),this.render(),this.options.mode==="columns"&&this.columnsEl&&(this.columnsEl.innerHTML="",this.columns.forEach(t=>{const e=document.createElement("div");e.className="tm-column",e.style.width=`${t.widthPercent}%`,this.columnsEl.appendChild(e)})))}getZoomState(){return{...this.zoomState}}setZoom(t,e){if(this.isDestroyed||this.options.mode!=="canvas")return;const i=Math.max(this.options.minZoom,Math.min(this.options.maxZoom,t)),r=1-1/i;let s=e!==void 0?e:this.zoomState.panX;s=Math.max(0,Math.min(r,s)),this.zoomState={level:i,panX:i>1?s:0,isMinZoom:i<=this.options.minZoom,isMaxZoom:i>=this.options.maxZoom},this.render()}resetZoom(){this.setZoom(1,0)}zoomToColumns(t,e){if(this.isDestroyed||this.options.mode!=="canvas")return;const i=this.columns.length;if(i===0)return;const o=Math.max(0,Math.min(i-1,t)),s=Math.max(o+1,Math.min(i,e))-o,a=i/s,l=o/i;this.setZoom(a,l)}destroy(){this.isDestroyed||(this.isDestroyed=!0,this.clearCompactCollapseTimer(),this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null),this.scrollContainer&&this.scrollContainer.removeEventListener("scroll",this.boundHandlers.onScroll),this.minimapEl&&(this.minimapEl.removeEventListener("click",this.boundHandlers.onMinimapClick),this.minimapEl.removeEventListener("focusin",this.boundHandlers.onCompactFocusIn),this.minimapEl.removeEventListener("focusout",this.boundHandlers.onCompactFocusOut),this.minimapEl.removeEventListener("keydown",this.boundHandlers.onCompactKeyDown)),document.removeEventListener("click",this.boundHandlers.onDocumentClick),this.viewportEl&&(this.viewportEl.removeEventListener("pointerdown",this.boundHandlers.onPointerDown),this.viewportEl.removeEventListener("wheel",this.boundHandlers.onWheel)),this.canvasEl&&(this.canvasEl.removeEventListener("wheel",this.boundHandlers.onWheel),this.canvasEl.removeEventListener("pointerdown",this.boundHandlers.onCanvasPointerDown),this.canvasEl.removeEventListener("mousemove",this.boundHandlers.onCanvasMouseMove),this.canvasEl.removeEventListener("mouseleave",this.boundHandlers.onCanvasMouseLeave)),document.removeEventListener("pointermove",this.boundHandlers.onPointerMove),document.removeEventListener("pointerup",this.boundHandlers.onPointerUp),this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),this.mutationObserver&&(this.mutationObserver.disconnect(),this.mutationObserver=null),this.minimapEl&&this.minimapEl.parentNode&&this.minimapEl.parentNode.removeChild(this.minimapEl),this.minimapEl=null,this.columnsEl=null,this.canvasEl=null,this.canvasCtx=null,this.viewportEl=null,this.scrollContainer=null,this.columns=[])}}const $=':root{--tm-background: #e3f2fd;--tm-border: #90caf9;--tm-viewport-color: rgba(25, 118, 210, .3);--tm-viewport-border: #1976d2;--tm-height: 40px;--tm-column-color: #64b5f6;--tm-column-gap: 1px;--tm-border-radius: 4px;--tm-canvas-empty: #bbdefb;--tm-canvas-filled: #1565c0;--tm-compact-dot-size: 5px;--tm-compact-transition-duration: .18s}@media (prefers-color-scheme: dark){:root{--tm-background: #1a237e;--tm-border: #3949ab;--tm-viewport-color: rgba(100, 180, 255, .3);--tm-viewport-border: #64b5f6;--tm-column-color: #3f51b5;--tm-canvas-empty: #283593;--tm-canvas-filled: #90caf9}}.tm-minimap{position:relative;width:var(--tm-minimap-width, 100%);height:var(--tm-minimap-height, var(--tm-height));background:var(--tm-background);border:1px solid var(--tm-border);border-radius:var(--tm-border-radius);box-sizing:border-box;overflow:hidden;user-select:none;-webkit-user-select:none;cursor:pointer;transition:width var(--tm-compact-transition-duration) ease-in-out,height var(--tm-compact-transition-duration) ease-in-out,opacity var(--tm-compact-transition-duration) ease-in-out,transform var(--tm-compact-transition-duration) ease-in-out,background-color var(--tm-compact-transition-duration) ease-in-out,border-color var(--tm-compact-transition-duration) ease-in-out,box-shadow var(--tm-compact-transition-duration) ease-in-out;will-change:width,height,opacity,transform}.tm-minimap--top{margin-bottom:8px}.tm-minimap--bottom{margin-top:8px}.tm-minimap--fixed{z-index:100;box-shadow:0 4px 12px #00000026;border-radius:8px}.tm-minimap--compact-collapsed{border-radius:999px}.tm-minimap--compact-expanded{border-radius:8px}.tm-minimap--compact>*{transition:opacity var(--tm-compact-transition-duration) ease-in-out}.tm-minimap--compact-collapsed{background:color-mix(in srgb,var(--tm-background) 60%,transparent);border-color:color-mix(in srgb,var(--tm-border) 45%,transparent);box-shadow:0 0 0 1px color-mix(in srgb,var(--tm-border) 20%,transparent);opacity:.92;transform:translate(2px,2px)}.tm-minimap--compact-collapsed>*{opacity:0;pointer-events:none}.tm-minimap--compact-collapsed:after{content:"";position:absolute;right:calc((var(--tm-minimap-width, 24px) - var(--tm-compact-dot-size)) / 2);bottom:calc((var(--tm-minimap-height, 24px) - var(--tm-compact-dot-size)) / 2);width:var(--tm-compact-dot-size);height:var(--tm-compact-dot-size);border-radius:999px;background:color-mix(in srgb,var(--tm-viewport-border) 72%,transparent);box-shadow:0 0 0 1px color-mix(in srgb,var(--tm-viewport-border) 24%,transparent);opacity:.78;pointer-events:none;transition:opacity var(--tm-compact-transition-duration) ease-in-out,transform var(--tm-compact-transition-duration) ease-in-out,background-color var(--tm-compact-transition-duration) ease-in-out}.tm-minimap--compact-expanded{transform:translate(0)}.tm-minimap--compact-expanded:after{opacity:0;transform:scale(.7)}.tm-columns{display:flex;align-items:stretch;height:100%;gap:var(--tm-column-gap);padding:4px;box-sizing:border-box}.tm-column{flex-shrink:0;height:100%;background:var(--tm-column-color);border-radius:2px;transition:background-color .15s ease;cursor:pointer}.tm-column:hover{background:color-mix(in srgb,var(--tm-column-color) 80%,black)}.tm-canvas{width:100%;height:100%;display:block;cursor:pointer}.tm-viewport{position:absolute;top:0;height:100%;background:var(--tm-viewport-color);border-left:2px solid var(--tm-viewport-border);border-right:2px solid var(--tm-viewport-border);box-sizing:border-box;cursor:grab;transition:background-color .15s ease;z-index:10}.tm-viewport:hover{background:color-mix(in srgb,var(--tm-viewport-color) 100%,black 10%)}.tm-viewport--dragging{cursor:grabbing;background:color-mix(in srgb,var(--tm-viewport-color) 100%,black 20%);transition:none}.tm-viewport--disabled{cursor:default;pointer-events:none}.tm-minimap:focus-visible{outline:2px solid var(--tm-viewport-border);outline-offset:2px}.tm-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}';let z=!1;function W(){if(z||typeof document>"u")return;const u=document.createElement("style");u.id="table-minimap-styles",u.textContent=$,document.head.appendChild(u),z=!0}W();exports.TableMinimap=Z;exports.injectStyles=W;
|
|
1
|
+
"use strict";var z=Object.defineProperty;var H=(v,t,e)=>t in v?z(v,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):v[t]=e;var n=(v,t,e)=>H(v,typeof t!="symbol"?t+"":t,e);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const A={mode:"columns",height:40,position:"bottom",fixedWidth:300,fixedPosition:"bottom-right",compact:!1,draggable:!0,showViewport:!0,zoomable:!1,minZoom:1,maxZoom:10,zoomSpeed:.1},T=24,O=5,I=180;class Z{constructor(t,e={}){n(this,"table");n(this,"options");n(this,"isCompactMode");n(this,"isCompactCollapsed",!1);n(this,"isCompactExpanding",!1);n(this,"compactCollapseTimer",null);n(this,"scrollContainer",null);n(this,"minimapEl",null);n(this,"columnsEl",null);n(this,"canvasEl",null);n(this,"canvasCtx",null);n(this,"viewportEl",null);n(this,"columns",[]);n(this,"scrollState",{scrollLeft:0,scrollWidth:0,clientWidth:0,viewportRatio:1,positionRatio:0});n(this,"zoomState",{level:1,panX:0,isMinZoom:!0,isMaxZoom:!1});n(this,"isDragging",!1);n(this,"isPanning",!1);n(this,"wasPanning",!1);n(this,"panStartX",0);n(this,"hoveredColumn",-1);n(this,"dragStartX",0);n(this,"dragStartScrollLeft",0);n(this,"resizeObserver",null);n(this,"mutationObserver",null);n(this,"boundHandlers");n(this,"rafId",null);n(this,"isDestroyed",!1);n(this,"isPotentialPan",!1);this.table=this.resolveTable(t),this.options={...A,...e},this.isCompactMode=this.options.compact&&this.options.position==="fixed",this.boundHandlers={onScroll:this.onScroll.bind(this),onPointerDown:this.onPointerDown.bind(this),onPointerMove:this.onPointerMove.bind(this),onPointerUp:this.onPointerUp.bind(this),onMinimapClick:this.onMinimapClick.bind(this),onWheel:this.onWheel.bind(this),onCanvasPointerDown:this.onCanvasPointerDown.bind(this),onCanvasMouseMove:this.onCanvasMouseMove.bind(this),onCanvasMouseLeave:this.onCanvasMouseLeave.bind(this),onCompactFocusIn:this.onCompactFocusIn.bind(this),onCompactFocusOut:this.onCompactFocusOut.bind(this),onCompactKeyDown:this.onCompactKeyDown.bind(this),onDocumentClick:this.onDocumentClick.bind(this)},this.init()}resolveTable(t){let e;if(typeof t=="string"){if(e=document.querySelector(t),!e)throw new Error(`TableMinimap: No element found for selector "${t}"`)}else if(t instanceof HTMLTableElement)e=t;else throw new Error("TableMinimap: Selector must be a CSS selector string or an HTMLTableElement");if(e.tagName!=="TABLE")throw new Error(`TableMinimap: Element must be a <table>, got <${e.tagName.toLowerCase()}>`);return e}init(){this.scrollContainer=this.findScrollContainer(),this.detectColumns(),this.createMinimapElement(),this.updateScrollState(),this.render(),this.attachEventListeners(),this.setupObservers()}findScrollContainer(){let t=this.table.parentElement;for(;t;){const e=getComputedStyle(t),i=e.overflowX,o=e.overflow;if(i==="auto"||i==="scroll"||o==="auto"||o==="scroll"||t.scrollWidth>t.clientWidth)return t;t=t.parentElement}return this.table.parentElement||document.body}detectColumns(){this.columns=[];const t=this.table.querySelectorAll("thead th, thead td");let e;if(t.length>0)e=t;else{const o=this.table.querySelector("tr");o?e=o.querySelectorAll("th, td"):e=[]}const i=this.table.offsetWidth||1;Array.from(e).forEach(o=>{const a=o.colSpan||1,s=o.offsetWidth;for(let l=0;l<a;l++){const r=s/a;this.columns.push({index:this.columns.length,width:r,widthPercent:r/i*100})}}),this.columns.length===0&&this.columns.push({index:0,width:i,widthPercent:100})}createMinimapElement(){this.minimapEl=document.createElement("div"),this.minimapEl.className=`tm-minimap tm-minimap--${this.options.position}`,this.minimapEl.style.setProperty("--tm-minimap-height",`${this.options.height}px`),this.options.position==="fixed"&&(this.minimapEl.style.setProperty("--tm-minimap-width",`${this.options.fixedWidth}px`),this.minimapEl.classList.add(`tm-minimap--${this.options.fixedPosition}`)),this.isCompactMode?(this.minimapEl.classList.add("tm-minimap--compact","tm-minimap--compact-collapsed"),this.minimapEl.style.setProperty("--tm-compact-dot-size",`${O}px`),this.isCompactCollapsed=!0,this.applyCompactDimensions(!0),this.minimapEl.setAttribute("aria-expanded","false")):this.minimapEl.setAttribute("aria-expanded","true"),this.minimapEl.setAttribute("role","slider"),this.minimapEl.setAttribute("aria-label","Table minimap navigation"),this.minimapEl.setAttribute("aria-valuemin","0"),this.minimapEl.setAttribute("aria-valuemax","100"),this.minimapEl.setAttribute("tabindex","0"),this.options.mode==="canvas"?this.createCanvasContent():this.createColumnsContent(),this.options.showViewport&&this.createViewportIndicator(),this.insertMinimap()}createColumnsContent(){this.minimapEl&&(this.columnsEl=document.createElement("div"),this.columnsEl.className="tm-columns",this.columns.forEach(()=>{const t=document.createElement("div");t.className="tm-column",this.columnsEl.appendChild(t)}),this.minimapEl.appendChild(this.columnsEl))}createCanvasContent(){this.minimapEl&&(this.canvasEl=document.createElement("canvas"),this.canvasEl.className="tm-canvas",this.canvasCtx=this.canvasEl.getContext("2d"),this.minimapEl.appendChild(this.canvasEl))}createViewportIndicator(){this.minimapEl&&(this.viewportEl=document.createElement("div"),this.viewportEl.className="tm-viewport",this.options.draggable||this.viewportEl.classList.add("tm-viewport--disabled"),this.minimapEl.appendChild(this.viewportEl))}insertMinimap(){if(!this.minimapEl||!this.scrollContainer)return;const t=this.scrollContainer.parentElement;if(this.options.position==="fixed"){if(t){getComputedStyle(t).position==="static"&&(t.style.position="relative");const i=this.scrollContainer.nextSibling;i?t.insertBefore(this.minimapEl,i):t.appendChild(this.minimapEl),this.minimapEl.style.position="absolute";const o=this.isCompactMode?8:12,a=this.options.fixedPosition;this.minimapEl.style.top="",this.minimapEl.style.bottom="",this.minimapEl.style.left="",this.minimapEl.style.right="",this.minimapEl.style.marginTop="0",a==="top-left"?(this.minimapEl.style.top=`${o}px`,this.minimapEl.style.left=`${o}px`):a==="top-right"?(this.minimapEl.style.top=`${o}px`,this.minimapEl.style.right=`${o}px`):a==="bottom-left"?(this.minimapEl.style.bottom=`${o}px`,this.minimapEl.style.left=`${o}px`):(this.minimapEl.style.bottom=`${o}px`,this.minimapEl.style.right=`${o}px`)}}else if(this.options.position==="top")t?t.insertBefore(this.minimapEl,this.scrollContainer):this.scrollContainer.insertBefore(this.minimapEl,this.scrollContainer.firstChild);else if(t){const e=this.scrollContainer.nextSibling;e?t.insertBefore(this.minimapEl,e):t.appendChild(this.minimapEl)}else this.scrollContainer.appendChild(this.minimapEl)}updateScrollState(){if(!this.scrollContainer)return;const{scrollLeft:t,scrollWidth:e,clientWidth:i}=this.scrollContainer;this.scrollState={scrollLeft:t,scrollWidth:e,clientWidth:i,viewportRatio:i/Math.max(e,1),positionRatio:t/Math.max(e-i,1)},this.scrollState.viewportRatio=Math.min(1,Math.max(0,this.scrollState.viewportRatio)),this.scrollState.positionRatio=Math.min(1,Math.max(0,this.scrollState.positionRatio)),this.minimapEl&&this.minimapEl.setAttribute("aria-valuenow",String(Math.round(this.scrollState.positionRatio*100)))}render(){this.isDestroyed||this.isCompactMode&&this.isCompactCollapsed||(this.updateViewport(),this.options.mode==="canvas"&&this.renderCanvas())}getCanvasMetrics(){var p;const t=((p=this.minimapEl)==null?void 0:p.offsetWidth)??0,e=this.zoomState.level,i=this.columns.length,o=1/e,a=i*o,s=a>0?t/a:0;let l=0;if(e>1&&this.scrollContainer){const{scrollLeft:m,scrollWidth:E,clientWidth:g}=this.scrollContainer,u=Math.max(E-g,1);l=m/u*(1-o)}const r=l*i,c=Math.floor(r),h=Math.min(Math.ceil(r+a)+1,i),f=-(r-c)*s;return{width:t,zoom:e,numCols:i,visibleRatio:o,visibleCols:a,cellWidth:s,panX:l,startColFloat:r,startCol:c,endCol:h,xOffset:f}}getColumnAtX(t){const{width:e,numCols:i,panX:o,visibleRatio:a}=this.getCanvasMetrics();if(i===0||e===0)return-1;const s=t/e,l=o+s*a,r=Math.floor(l*i);return Math.max(0,Math.min(i-1,r))}updateViewport(){if(!this.viewportEl||!this.minimapEl)return;if(this.options.mode==="canvas"){this.viewportEl.style.display="none";return}const t=this.minimapEl.offsetWidth,e=Math.max(t*this.scrollState.viewportRatio,20),o=(t-e)*this.scrollState.positionRatio;this.viewportEl.style.cssText=`width:${e}px;left:${o}px;display:block`}renderCanvas(){if(!this.canvasEl||!this.canvasCtx||!this.minimapEl)return;const t=this.getCanvasMetrics(),e=this.options.height,i=window.devicePixelRatio||1;this.canvasEl.width=t.width*i,this.canvasEl.height=e*i,this.canvasEl.style.width=`${t.width}px`,this.canvasEl.style.height=`${e}px`,this.canvasCtx.scale(i,i),this.renderTableDirect(t,e)}renderTableDirect(t,e){var S,y,P,D;if(!this.canvasCtx)return;const i=this.canvasCtx,{width:o,numCols:a,cellWidth:s,startCol:l,endCol:r,xOffset:c}=t,h=Array.from(this.table.querySelectorAll("tr")),f=h.length;if(f===0||a===0)return;const p=Math.min(e*.15,30),m=(e-p)/f,E=Math.min(m*.6,s*.15,14),g=Math.min(p*.6,s*.15,14),u={bg:"#ffffff",headerBg:"#f1f5f9",border:"#e2e8f0",text:"#334155",headerText:"#1e293b",altRow:"#f8fafc",hoverFill:"rgba(59, 130, 246, 0.08)",hoverStroke:"rgba(59, 130, 246, 0.3)"};i.fillStyle=u.bg,i.fillRect(0,0,o,e),i.fillStyle=u.headerBg,i.fillRect(0,0,o,p);const x=this.table.querySelector("thead tr")||h[0],L=x?Array.from(x.querySelectorAll("th, td")):[];i.font=`bold ${g}px system-ui, sans-serif`,i.textBaseline="middle";for(let d=l;d<r;d++){const C=c+(d-l)*s;if(C+s<0||C>o)continue;i.strokeStyle=u.border,i.lineWidth=1,i.strokeRect(C,0,s,p);const b=((y=(S=L[d])==null?void 0:S.textContent)==null?void 0:y.trim())||`Col ${d+1}`;i.fillStyle=u.headerText,i.save(),i.beginPath(),i.rect(C+2,0,s-4,p),i.clip(),i.fillText(b,C+4,p/2),i.restore()}i.font=`${E}px system-ui, sans-serif`;for(let d=0;d<h.length;d++){const C=h[d];if(C.closest("thead"))continue;const b=p+d*m;if(b+m<0||b>e)continue;d%2===1&&(i.fillStyle=u.altRow,i.fillRect(0,b,o,m));const X=Array.from(C.querySelectorAll("th, td"));for(let M=l;M<r;M++){const w=c+(M-l)*s;if(w+s<0||w>o)continue;i.strokeStyle=u.border,i.lineWidth=.5,i.strokeRect(w,b,s,m);const k=(D=(P=X[M])==null?void 0:P.textContent)==null?void 0:D.trim();k&&(i.fillStyle=u.text,i.save(),i.beginPath(),i.rect(w+2,b,s-4,m),i.clip(),i.fillText(k,w+4,b+m/2),i.restore())}}if(this.hoveredColumn>=l&&this.hoveredColumn<r){const d=c+(this.hoveredColumn-l)*s;i.fillStyle=u.hoverFill,i.fillRect(d,0,s,e),i.strokeStyle=u.hoverStroke,i.lineWidth=1,i.strokeRect(d,0,s,e)}}attachEventListeners(){!this.scrollContainer||!this.minimapEl||(this.scrollContainer.addEventListener("scroll",this.boundHandlers.onScroll,{passive:!0}),this.minimapEl.addEventListener("click",this.boundHandlers.onMinimapClick),this.isCompactMode&&(this.minimapEl.addEventListener("focusin",this.boundHandlers.onCompactFocusIn),this.minimapEl.addEventListener("focusout",this.boundHandlers.onCompactFocusOut),this.minimapEl.addEventListener("keydown",this.boundHandlers.onCompactKeyDown),document.addEventListener("click",this.boundHandlers.onDocumentClick)),this.options.draggable&&this.viewportEl&&this.viewportEl.addEventListener("pointerdown",this.boundHandlers.onPointerDown),this.options.zoomable&&this.options.mode==="canvas"&&this.canvasEl&&(this.canvasEl.addEventListener("wheel",this.boundHandlers.onWheel,{passive:!1}),this.canvasEl.addEventListener("pointerdown",this.boundHandlers.onCanvasPointerDown),this.canvasEl.style.cursor=this.zoomState.level>1?"grab":"pointer",this.viewportEl&&this.viewportEl.addEventListener("wheel",this.boundHandlers.onWheel,{passive:!1})),this.options.mode==="canvas"&&this.canvasEl&&(this.canvasEl.addEventListener("mousemove",this.boundHandlers.onCanvasMouseMove),this.canvasEl.addEventListener("mouseleave",this.boundHandlers.onCanvasMouseLeave)),document.addEventListener("pointermove",this.boundHandlers.onPointerMove),document.addEventListener("pointerup",this.boundHandlers.onPointerUp))}onScroll(){this.isDragging||this.rafId===null&&(this.rafId=requestAnimationFrame(()=>{this.updateScrollState(),this.updateViewport(),this.options.mode==="canvas"&&this.render(),this.rafId=null}))}onMinimapClick(t){if(!this.minimapEl||!this.scrollContainer)return;if(this.isCompactMode&&this.isCompactCollapsed){t.preventDefault(),this.expandCompact();return}if(this.isCompactExpanding||this.isDragging||this.isPanning||this.wasPanning)return;const{scrollWidth:e,clientWidth:i}=this.scrollContainer,o=e-i,a=this.minimapEl.getBoundingClientRect(),s=t.clientX-a.left;if(this.options.mode==="canvas"){const c=this.getColumnAtX(s);if(c>=0){const h=this.columns.length,f=e/h,m=(c+.5)*f-i/2;this.scrollContainer.scrollTo({left:Math.max(0,Math.min(o,m)),behavior:"smooth"})}return}const r=s/a.width*o;this.scrollContainer.scrollTo({left:Math.max(0,Math.min(o,r)),behavior:"smooth"})}onPointerDown(t){!this.viewportEl||!this.scrollContainer||(t.preventDefault(),t.stopPropagation(),this.isDragging=!0,this.dragStartX=t.clientX,this.dragStartScrollLeft=this.scrollContainer.scrollLeft,this.viewportEl.classList.add("tm-viewport--dragging"),this.viewportEl.setPointerCapture(t.pointerId))}onPointerMove(t){if(this.isPotentialPan&&!this.isPanning&&this.canvasEl&&this.zoomState.level>1&&Math.abs(t.clientX-this.panStartX)>3&&(this.isPanning=!0,this.canvasEl.style.cursor="grabbing"),this.isPanning&&this.canvasEl&&this.minimapEl&&this.scrollContainer){t.preventDefault();const c=t.clientX-this.panStartX,h=this.minimapEl.offsetWidth,{scrollWidth:f,clientWidth:p}=this.scrollContainer,m=f-p,E=c/h*m*this.zoomState.level,g=this.dragStartScrollLeft+E;this.scrollContainer.scrollLeft=Math.max(0,Math.min(m,g)),this.updateScrollState(),this.updateViewport(),this.render();return}if(!this.isDragging||!this.minimapEl||!this.scrollContainer)return;t.preventDefault();const{scrollWidth:e,clientWidth:i}=this.scrollContainer,o=this.minimapEl.offsetWidth,a=e-i,l=(t.clientX-this.dragStartX)/o*a,r=this.dragStartScrollLeft+l;this.scrollContainer.scrollLeft=Math.max(0,Math.min(a,r)),this.updateScrollState(),this.updateViewport()}onPointerUp(t){if(this.isPotentialPan&&!this.isPanning&&this.canvasEl&&this.minimapEl){this.isPotentialPan=!1,this.canvasEl.hasPointerCapture(t.pointerId)&&this.canvasEl.releasePointerCapture(t.pointerId);const e=new MouseEvent("click",{clientX:t.clientX,clientY:t.clientY,bubbles:!0});this.minimapEl.dispatchEvent(e);return}this.isPotentialPan=!1,this.isPanning&&this.canvasEl&&(this.isPanning=!1,this.wasPanning=!0,this.canvasEl.hasPointerCapture(t.pointerId)&&this.canvasEl.releasePointerCapture(t.pointerId),this.canvasEl.style.cursor=this.zoomState.level>1?"grab":"pointer",setTimeout(()=>{this.wasPanning=!1},100)),this.isDragging&&(this.isDragging=!1,this.viewportEl&&(this.viewportEl.classList.remove("tm-viewport--dragging"),this.viewportEl.releasePointerCapture(t.pointerId)))}onWheel(t){if(!this.options.zoomable||this.options.mode!=="canvas"||!this.canvasEl||!this.scrollContainer||!this.minimapEl)return;t.preventDefault();const e=this.zoomState.level,i=-t.deltaY*this.options.zoomSpeed,o=Math.max(this.options.minZoom,Math.min(this.options.maxZoom,e+i));if(o===e)return;const a=this.canvasEl.getBoundingClientRect(),s=t.clientX-a.left,l=this.minimapEl.offsetWidth,r=s/l,c=1/e,{scrollLeft:h,scrollWidth:f,clientWidth:p}=this.scrollContainer,m=Math.max(f-p,1),E=h/m,u=(e>1?E*(1-c):0)+r*c;this.zoomState={level:o,panX:0,isMinZoom:o<=this.options.minZoom,isMaxZoom:o>=this.options.maxZoom};const x=1/o;if(o>1){const S=(u-r*x)/(1-x),y=Math.max(0,Math.min(m,S*m));this.scrollContainer.scrollLeft=y}this.updateScrollState(),this.render()}onCanvasPointerDown(t){!this.canvasEl||!this.scrollContainer||(this.isPotentialPan=!0,this.panStartX=t.clientX,this.dragStartScrollLeft=this.scrollContainer.scrollLeft,this.zoomState.level>1&&(t.preventDefault(),this.canvasEl.setPointerCapture(t.pointerId)))}onCanvasMouseMove(t){if(!this.canvasEl||this.isPanning)return;const e=this.canvasEl.getBoundingClientRect(),i=this.getColumnAtX(t.clientX-e.left);i!==this.hoveredColumn&&(this.hoveredColumn=i,this.canvasEl.style.cursor=i>=0?"pointer":this.zoomState.level>1?"grab":"default",this.render())}onCanvasMouseLeave(){this.hoveredColumn!==-1&&(this.hoveredColumn=-1,this.canvasEl&&(this.canvasEl.style.cursor=this.zoomState.level>1?"grab":"default"),this.render())}expandCompact(){!this.isCompactMode||!this.minimapEl||this.isCompactExpanding||(this.clearCompactCollapseTimer(),this.isCompactExpanding=!0,this.applyCompactDimensions(!1),setTimeout(()=>{this.isCompactExpanding=!1,!this.isDestroyed&&!this.isCompactCollapsed&&(this.updateScrollState(),this.render())},200))}collapseCompact(){!this.isCompactMode||!this.minimapEl||this.applyCompactDimensions(!0)}applyCompactDimensions(t){if(!(!this.minimapEl||!this.isCompactMode)){if(this.isCompactCollapsed=t,t){this.minimapEl.classList.add("tm-minimap--compact-collapsed"),this.minimapEl.classList.remove("tm-minimap--compact-expanded"),this.minimapEl.style.setProperty("--tm-minimap-width",`${T}px`),this.minimapEl.style.setProperty("--tm-minimap-height",`${T}px`),this.minimapEl.setAttribute("aria-expanded","false");return}this.minimapEl.classList.remove("tm-minimap--compact-collapsed"),this.minimapEl.classList.add("tm-minimap--compact-expanded"),this.minimapEl.style.setProperty("--tm-minimap-width",`${this.options.fixedWidth}px`),this.minimapEl.style.setProperty("--tm-minimap-height",`${this.options.height}px`),this.minimapEl.setAttribute("aria-expanded","true")}}clearCompactCollapseTimer(){this.compactCollapseTimer!==null&&(clearTimeout(this.compactCollapseTimer),this.compactCollapseTimer=null)}scheduleCompactCollapse(t=I){this.isCompactMode&&(this.clearCompactCollapseTimer(),this.compactCollapseTimer=window.setTimeout(()=>{this.collapseCompact(),this.compactCollapseTimer=null},t))}onDocumentClick(t){!this.isCompactMode||!this.minimapEl||this.isCompactCollapsed||this.minimapEl.contains(t.target)||this.collapseCompact()}onCompactFocusIn(){this.isCompactMode&&this.expandCompact()}onCompactFocusOut(){this.isCompactMode&&this.scheduleCompactCollapse(0)}onCompactKeyDown(t){if(this.isCompactMode){if(t.key==="Escape"){t.preventDefault(),this.collapseCompact();return}(t.key==="Enter"||t.key===" ")&&(t.preventDefault(),this.isCompactCollapsed?this.expandCompact():this.collapseCompact())}}setupObservers(){this.resizeObserver=new ResizeObserver(()=>{this.onResize()}),this.scrollContainer&&this.resizeObserver.observe(this.scrollContainer),this.resizeObserver.observe(this.table),this.mutationObserver=new MutationObserver(t=>{t.some(i=>i.type==="childList"||i.attributeName==="colspan")&&this.onTableMutation()}),this.mutationObserver.observe(this.table,{childList:!0,subtree:!0,attributes:!0,attributeFilter:["colspan"]})}onResize(){this.isDestroyed||(this.rafId!==null&&cancelAnimationFrame(this.rafId),this.rafId=requestAnimationFrame(()=>{this.detectColumns(),this.updateScrollState(),this.render(),this.options.mode==="columns"&&this.columnsEl&&this.minimapEl&&(this.columnsEl.innerHTML="",this.columns.forEach(()=>{const t=document.createElement("div");t.className="tm-column",this.columnsEl.appendChild(t)})),this.rafId=null}))}onTableMutation(){this.isDestroyed||(this.detectColumns(),this.updateScrollState(),this.render(),this.options.mode==="columns"&&this.columnsEl&&(this.columnsEl.innerHTML="",this.columns.forEach(()=>{const t=document.createElement("div");t.className="tm-column",this.columnsEl.appendChild(t)})))}getScrollState(){return{...this.scrollState}}getColumns(){return[...this.columns]}scrollToColumn(t,e=!0){if(!this.scrollContainer||t<0||t>=this.columns.length)return;const o=this.columns.slice(0,t).reduce((a,s)=>a+s.width,0);this.scrollContainer.scrollTo({left:o,behavior:e?"smooth":"auto"})}refresh(){this.isDestroyed||(this.scrollContainer=this.findScrollContainer(),this.detectColumns(),this.updateScrollState(),this.render(),this.options.mode==="columns"&&this.columnsEl&&(this.columnsEl.innerHTML="",this.columns.forEach(()=>{const t=document.createElement("div");t.className="tm-column",this.columnsEl.appendChild(t)})))}getZoomState(){return{...this.zoomState}}setZoom(t,e){if(this.isDestroyed||this.options.mode!=="canvas"||!this.scrollContainer)return;const i=Math.max(this.options.minZoom,Math.min(this.options.maxZoom,t)),o=1/i,a=1-o;let s=e!==void 0?e:0;if(s=Math.max(0,Math.min(a,s)),this.zoomState={level:i,panX:0,isMinZoom:i<=this.options.minZoom,isMaxZoom:i>=this.options.maxZoom},i>1&&s>0){const{scrollWidth:l,clientWidth:r}=this.scrollContainer,c=Math.max(l-r,1),h=s/(1-o);this.scrollContainer.scrollLeft=Math.max(0,Math.min(c,h*c))}else i<=1&&(this.scrollContainer.scrollLeft=0);this.updateScrollState(),this.render()}resetZoom(){this.setZoom(1,0)}zoomToColumns(t,e){if(this.isDestroyed||this.options.mode!=="canvas")return;const i=this.columns.length;if(i===0)return;const o=Math.max(0,Math.min(i-1,t)),s=Math.max(o+1,Math.min(i,e))-o,l=i/s,r=o/i;this.setZoom(l,r)}destroy(){this.isDestroyed||(this.isDestroyed=!0,this.clearCompactCollapseTimer(),this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null),this.scrollContainer&&this.scrollContainer.removeEventListener("scroll",this.boundHandlers.onScroll),this.minimapEl&&(this.minimapEl.removeEventListener("click",this.boundHandlers.onMinimapClick),this.minimapEl.removeEventListener("focusin",this.boundHandlers.onCompactFocusIn),this.minimapEl.removeEventListener("focusout",this.boundHandlers.onCompactFocusOut),this.minimapEl.removeEventListener("keydown",this.boundHandlers.onCompactKeyDown)),document.removeEventListener("click",this.boundHandlers.onDocumentClick),this.viewportEl&&(this.viewportEl.removeEventListener("pointerdown",this.boundHandlers.onPointerDown),this.viewportEl.removeEventListener("wheel",this.boundHandlers.onWheel)),this.canvasEl&&(this.canvasEl.removeEventListener("wheel",this.boundHandlers.onWheel),this.canvasEl.removeEventListener("pointerdown",this.boundHandlers.onCanvasPointerDown),this.canvasEl.removeEventListener("mousemove",this.boundHandlers.onCanvasMouseMove),this.canvasEl.removeEventListener("mouseleave",this.boundHandlers.onCanvasMouseLeave)),document.removeEventListener("pointermove",this.boundHandlers.onPointerMove),document.removeEventListener("pointerup",this.boundHandlers.onPointerUp),this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),this.mutationObserver&&(this.mutationObserver.disconnect(),this.mutationObserver=null),this.minimapEl&&this.minimapEl.parentNode&&this.minimapEl.parentNode.removeChild(this.minimapEl),this.minimapEl=null,this.columnsEl=null,this.canvasEl=null,this.canvasCtx=null,this.viewportEl=null,this.scrollContainer=null,this.columns=[])}}const $=':root{--tm-background: #e3f2fd;--tm-border: #90caf9;--tm-viewport-color: rgba(25, 118, 210, .3);--tm-viewport-border: #1976d2;--tm-height: 40px;--tm-column-color: #64b5f6;--tm-column-gap: 1px;--tm-border-radius: 4px;--tm-canvas-empty: #bbdefb;--tm-canvas-filled: #1565c0;--tm-compact-dot-size: 5px;--tm-compact-transition-duration: .18s}@media (prefers-color-scheme: dark){:root{--tm-background: #1a237e;--tm-border: #3949ab;--tm-viewport-color: rgba(100, 180, 255, .3);--tm-viewport-border: #64b5f6;--tm-column-color: #3f51b5;--tm-canvas-empty: #283593;--tm-canvas-filled: #90caf9}}.tm-minimap{position:relative;width:var(--tm-minimap-width, 100%);height:var(--tm-minimap-height, var(--tm-height));background:var(--tm-background);border:1px solid var(--tm-border);border-radius:var(--tm-border-radius);box-sizing:border-box;overflow:hidden;user-select:none;-webkit-user-select:none;cursor:pointer;transition:width var(--tm-compact-transition-duration) ease-in-out,height var(--tm-compact-transition-duration) ease-in-out,opacity var(--tm-compact-transition-duration) ease-in-out,transform var(--tm-compact-transition-duration) ease-in-out,background-color var(--tm-compact-transition-duration) ease-in-out,border-color var(--tm-compact-transition-duration) ease-in-out,box-shadow var(--tm-compact-transition-duration) ease-in-out;will-change:width,height,opacity,transform}.tm-minimap--top{margin-bottom:8px}.tm-minimap--bottom{margin-top:8px}.tm-minimap--fixed{z-index:100;box-shadow:0 4px 12px #00000026;border-radius:8px}.tm-minimap--compact-collapsed{border-radius:999px}.tm-minimap--compact-expanded{border-radius:8px}.tm-minimap--compact>*{transition:opacity var(--tm-compact-transition-duration) ease-in-out}.tm-minimap--compact-collapsed{background:color-mix(in srgb,var(--tm-background) 60%,transparent);border-color:color-mix(in srgb,var(--tm-border) 45%,transparent);box-shadow:0 0 0 1px color-mix(in srgb,var(--tm-border) 20%,transparent);opacity:.92;transform:translate(2px,2px)}.tm-minimap--compact-collapsed>*{opacity:0;pointer-events:none}.tm-minimap--compact-collapsed:after{content:"";position:absolute;right:calc((var(--tm-minimap-width, 24px) - var(--tm-compact-dot-size)) / 2);bottom:calc((var(--tm-minimap-height, 24px) - var(--tm-compact-dot-size)) / 2);width:var(--tm-compact-dot-size);height:var(--tm-compact-dot-size);border-radius:999px;background:color-mix(in srgb,var(--tm-viewport-border) 72%,transparent);box-shadow:0 0 0 1px color-mix(in srgb,var(--tm-viewport-border) 24%,transparent);opacity:.78;pointer-events:none;transition:opacity var(--tm-compact-transition-duration) ease-in-out,transform var(--tm-compact-transition-duration) ease-in-out,background-color var(--tm-compact-transition-duration) ease-in-out}.tm-minimap--compact-expanded{transform:translate(0)}.tm-minimap--compact-expanded:after{opacity:0;transform:scale(.7)}.tm-columns{display:flex;align-items:stretch;height:100%;gap:var(--tm-column-gap);padding:4px;box-sizing:border-box}.tm-column{flex:1 1 0;min-width:0;height:100%;background:var(--tm-column-color);border-radius:2px;transition:background-color .15s ease;cursor:pointer}.tm-column:hover{background:color-mix(in srgb,var(--tm-column-color) 80%,black)}.tm-canvas{width:100%;height:100%;display:block;cursor:pointer}.tm-viewport{position:absolute;top:0;height:100%;background:var(--tm-viewport-color);border-left:2px solid var(--tm-viewport-border);border-right:2px solid var(--tm-viewport-border);box-sizing:border-box;cursor:grab;transition:background-color .15s ease;z-index:10}.tm-viewport:hover{background:color-mix(in srgb,var(--tm-viewport-color) 100%,black 10%)}.tm-viewport--dragging{cursor:grabbing;background:color-mix(in srgb,var(--tm-viewport-color) 100%,black 20%);transition:none}.tm-viewport--disabled{cursor:default;pointer-events:none}.tm-minimap:focus-visible{outline:2px solid var(--tm-viewport-border);outline-offset:2px}.tm-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}';let W=!1;function R(){if(W||typeof document>"u")return;const v=document.createElement("style");v.id="table-minimap-styles",v.textContent=$,document.head.appendChild(v),W=!0}R();exports.TableMinimap=Z;exports.injectStyles=R;
|
|
2
2
|
//# sourceMappingURL=table-minimap.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"table-minimap.cjs","sources":["../src/TableMinimap.ts","../src/index.ts"],"sourcesContent":["import type {\n TableMinimapOptions,\n RequiredOptions,\n ColumnInfo,\n ScrollState,\n TableSelector,\n ZoomState,\n} from './types';\n\n/**\n * Default configuration options\n */\nconst DEFAULT_OPTIONS: RequiredOptions = {\n mode: 'columns',\n height: 40,\n position: 'bottom',\n fixedWidth: 300,\n compact: false,\n draggable: true,\n showViewport: true,\n zoomable: false,\n minZoom: 1,\n maxZoom: 10,\n zoomSpeed: 0.1,\n};\n\n/** Compact floating minimap handle size in pixels */\nconst COMPACT_HANDLE_SIZE = 24;\n\n/** Visible dot size inside the compact handle in pixels */\nconst COMPACT_DOT_SIZE = 5;\n\n/** Delay before compact mode collapses after pointer leave */\nconst COMPACT_COLLAPSE_DELAY = 180;\n\n/**\n * TableMinimap - A framework-agnostic minimap component for large HTML tables\n *\n * @example\n * ```ts\n * import { TableMinimap } from 'table-minimap';\n * import 'table-minimap/style.css';\n *\n * const minimap = new TableMinimap('#my-table');\n *\n * // Or with options\n * const minimap = new TableMinimap('#my-table', {\n * mode: 'canvas',\n * height: 50,\n * position: 'top'\n * });\n *\n * // Cleanup\n * minimap.destroy();\n * ```\n */\nexport class TableMinimap {\n /** The target table element */\n private readonly table: HTMLTableElement;\n\n /** Configuration options */\n private readonly options: RequiredOptions;\n\n /** Whether compact fixed-overlay behavior is enabled */\n private readonly isCompactMode: boolean;\n\n /** Whether the compact minimap is currently collapsed */\n private isCompactCollapsed = false;\n\n /** Timeout used to collapse compact mode after pointer leave */\n private compactCollapseTimer: number | null = null;\n\n /** The scrollable container (parent of table) */\n private scrollContainer: HTMLElement | null = null;\n\n /** Main minimap container element */\n private minimapEl: HTMLDivElement | null = null;\n\n /** Columns container for columns mode */\n private columnsEl: HTMLDivElement | null = null;\n\n /** Canvas element for canvas mode */\n private canvasEl: HTMLCanvasElement | null = null;\n\n /** Canvas 2D rendering context */\n private canvasCtx: CanvasRenderingContext2D | null = null;\n\n\n /** Viewport indicator element */\n private viewportEl: HTMLDivElement | null = null;\n\n /** Detected column information */\n private columns: ColumnInfo[] = [];\n\n /** Current scroll state */\n private scrollState: ScrollState = {\n scrollLeft: 0,\n scrollWidth: 0,\n clientWidth: 0,\n viewportRatio: 1,\n positionRatio: 0,\n };\n\n /** Current zoom state */\n private zoomState: ZoomState = {\n level: 1,\n panX: 0,\n isMinZoom: true,\n isMaxZoom: false,\n };\n\n /** Is the viewport being dragged */\n private isDragging = false;\n\n /** Is the canvas being panned (when zoomed) */\n private isPanning = false;\n\n /** Was just panning (to prevent click after pan) */\n private wasPanning = false;\n\n /** Pan start position */\n private panStartX = 0;\n\n /** Currently hovered column index (-1 = none) */\n private hoveredColumn = -1;\n\n /** Currently focused/clicked column index (-1 = none) */\n private focusedColumn = -1;\n\n\n /** Drag start position */\n private dragStartX = 0;\n\n /** Drag start scroll position */\n private dragStartScrollLeft = 0;\n\n /** ResizeObserver instance */\n private resizeObserver: ResizeObserver | null = null;\n\n /** MutationObserver instance */\n private mutationObserver: MutationObserver | null = null;\n\n /** Bound event handlers for cleanup */\n private boundHandlers: {\n onScroll: () => void;\n onPointerDown: (e: PointerEvent) => void;\n onPointerMove: (e: PointerEvent) => void;\n onPointerUp: (e: PointerEvent) => void;\n onMinimapClick: (e: MouseEvent) => void;\n onWheel: (e: WheelEvent) => void;\n onCanvasPointerDown: (e: PointerEvent) => void;\n onCanvasMouseMove: (e: MouseEvent) => void;\n onCanvasMouseLeave: () => void;\n onCompactFocusIn: () => void;\n onCompactFocusOut: () => void;\n onCompactKeyDown: (e: KeyboardEvent) => void;\n onDocumentClick: (e: MouseEvent) => void;\n };\n\n /** Animation frame ID for throttling */\n private rafId: number | null = null;\n\n /** Whether the instance has been destroyed */\n private isDestroyed = false;\n\n /**\n * Creates a new TableMinimap instance\n *\n * @param selector - CSS selector string or HTMLTableElement\n * @param options - Configuration options\n * @throws Error if table element is not found or invalid\n */\n constructor(selector: TableSelector, options: TableMinimapOptions = {}) {\n this.table = this.resolveTable(selector);\n this.options = { ...DEFAULT_OPTIONS, ...options };\n this.isCompactMode = this.options.compact && this.options.position === 'fixed';\n\n // Bind event handlers\n this.boundHandlers = {\n onScroll: this.onScroll.bind(this),\n onPointerDown: this.onPointerDown.bind(this),\n onPointerMove: this.onPointerMove.bind(this),\n onPointerUp: this.onPointerUp.bind(this),\n onMinimapClick: this.onMinimapClick.bind(this),\n onWheel: this.onWheel.bind(this),\n onCanvasPointerDown: this.onCanvasPointerDown.bind(this),\n onCanvasMouseMove: this.onCanvasMouseMove.bind(this),\n onCanvasMouseLeave: this.onCanvasMouseLeave.bind(this),\n onCompactFocusIn: this.onCompactFocusIn.bind(this),\n onCompactFocusOut: this.onCompactFocusOut.bind(this),\n onCompactKeyDown: this.onCompactKeyDown.bind(this),\n onDocumentClick: this.onDocumentClick.bind(this),\n };\n\n this.init();\n }\n\n /**\n * Resolves the table element from a selector or element\n *\n * @param selector - CSS selector or HTMLTableElement\n * @returns The resolved table element\n * @throws Error if element is not found or not a table\n */\n private resolveTable(selector: TableSelector): HTMLTableElement {\n let element: HTMLTableElement | null;\n\n if (typeof selector === 'string') {\n element = document.querySelector<HTMLTableElement>(selector);\n if (!element) {\n throw new Error(\n `TableMinimap: No element found for selector \"${selector}\"`\n );\n }\n } else if (selector instanceof HTMLTableElement) {\n element = selector;\n } else {\n throw new Error(\n 'TableMinimap: Selector must be a CSS selector string or an HTMLTableElement'\n );\n }\n\n if (element.tagName !== 'TABLE') {\n throw new Error(\n `TableMinimap: Element must be a <table>, got <${element.tagName.toLowerCase()}>`\n );\n }\n\n return element;\n }\n\n /**\n * Initializes the minimap\n */\n private init(): void {\n this.scrollContainer = this.findScrollContainer();\n this.detectColumns();\n this.createMinimapElement();\n this.updateScrollState();\n this.render();\n this.attachEventListeners();\n this.setupObservers();\n }\n\n /**\n * Finds the nearest scrollable parent container\n *\n * @returns The scrollable container or the table's parent\n */\n private findScrollContainer(): HTMLElement {\n let parent = this.table.parentElement;\n\n while (parent) {\n const style = getComputedStyle(parent);\n const overflowX = style.overflowX;\n const overflowY = style.overflow;\n\n if (\n overflowX === 'auto' ||\n overflowX === 'scroll' ||\n overflowY === 'auto' ||\n overflowY === 'scroll'\n ) {\n return parent;\n }\n\n // Check if content overflows\n if (parent.scrollWidth > parent.clientWidth) {\n return parent;\n }\n\n parent = parent.parentElement;\n }\n\n // Fallback to table's parent or body\n return this.table.parentElement || document.body;\n }\n\n /**\n * Detects table columns from thead or first row\n */\n private detectColumns(): void {\n this.columns = [];\n\n // Try thead th first\n const theadCells = this.table.querySelectorAll<HTMLTableCellElement>(\n 'thead th, thead td'\n );\n\n let cells: NodeListOf<HTMLTableCellElement> | HTMLTableCellElement[];\n\n if (theadCells.length > 0) {\n cells = theadCells;\n } else {\n // Fallback to first row cells\n const firstRow = this.table.querySelector('tr');\n if (firstRow) {\n cells = firstRow.querySelectorAll<HTMLTableCellElement>('th, td');\n } else {\n cells = [];\n }\n }\n\n const tableWidth = this.table.offsetWidth || 1;\n\n Array.from(cells).forEach((cell) => {\n // Handle colspan\n const colspan = cell.colSpan || 1;\n const cellWidth = cell.offsetWidth;\n\n for (let i = 0; i < colspan; i++) {\n const width = cellWidth / colspan;\n this.columns.push({\n index: this.columns.length,\n width,\n widthPercent: (width / tableWidth) * 100,\n });\n }\n });\n\n // Ensure at least one column\n if (this.columns.length === 0) {\n this.columns.push({\n index: 0,\n width: tableWidth,\n widthPercent: 100,\n });\n }\n }\n\n /**\n * Creates the minimap DOM element\n */\n private createMinimapElement(): void {\n // Create main container\n this.minimapEl = document.createElement('div');\n this.minimapEl.className = `tm-minimap tm-minimap--${this.options.position}`;\n this.minimapEl.style.setProperty('--tm-minimap-height', `${this.options.height}px`);\n\n // Set width for fixed position\n if (this.options.position === 'fixed') {\n this.minimapEl.style.setProperty('--tm-minimap-width', `${this.options.fixedWidth}px`);\n }\n\n if (this.isCompactMode) {\n this.minimapEl.classList.add('tm-minimap--compact', 'tm-minimap--compact-collapsed');\n this.minimapEl.style.setProperty('--tm-compact-dot-size', `${COMPACT_DOT_SIZE}px`);\n this.isCompactCollapsed = true;\n this.applyCompactDimensions(true);\n this.minimapEl.setAttribute('aria-expanded', 'false');\n } else {\n this.minimapEl.setAttribute('aria-expanded', 'true');\n }\n \n this.minimapEl.setAttribute('role', 'slider');\n this.minimapEl.setAttribute('aria-label', 'Table minimap navigation');\n this.minimapEl.setAttribute('aria-valuemin', '0');\n this.minimapEl.setAttribute('aria-valuemax', '100');\n this.minimapEl.setAttribute('tabindex', '0');\n\n // Create content based on mode\n if (this.options.mode === 'canvas') {\n this.createCanvasContent();\n } else {\n this.createColumnsContent();\n }\n\n // Create viewport indicator\n if (this.options.showViewport) {\n this.createViewportIndicator();\n }\n\n // Insert minimap\n this.insertMinimap();\n }\n\n /**\n * Creates columns-mode content\n */\n private createColumnsContent(): void {\n if (!this.minimapEl) return;\n\n this.columnsEl = document.createElement('div');\n this.columnsEl.className = 'tm-columns';\n\n this.columns.forEach((col) => {\n const colEl = document.createElement('div');\n colEl.className = 'tm-column';\n colEl.style.width = `${col.widthPercent}%`;\n this.columnsEl!.appendChild(colEl);\n });\n\n this.minimapEl.appendChild(this.columnsEl);\n }\n\n /**\n * Creates canvas-mode content\n */\n private createCanvasContent(): void {\n if (!this.minimapEl) return;\n\n this.canvasEl = document.createElement('canvas');\n this.canvasEl.className = 'tm-canvas';\n this.canvasCtx = this.canvasEl.getContext('2d');\n\n this.minimapEl.appendChild(this.canvasEl);\n }\n\n /**\n * Creates the viewport indicator element\n */\n private createViewportIndicator(): void {\n if (!this.minimapEl) return;\n\n this.viewportEl = document.createElement('div');\n this.viewportEl.className = 'tm-viewport';\n\n if (!this.options.draggable) {\n this.viewportEl.classList.add('tm-viewport--disabled');\n }\n\n this.minimapEl.appendChild(this.viewportEl);\n }\n\n /**\n * Inserts the minimap into the DOM\n */\n private insertMinimap(): void {\n if (!this.minimapEl || !this.scrollContainer) return;\n\n const parent = this.scrollContainer.parentElement;\n\n if (this.options.position === 'fixed') {\n // Fixed position: create a wrapper with relative positioning\n // Insert minimap after scroll container, but style it to overlay\n if (parent) {\n // Ensure parent has relative positioning for our fixed overlay\n const parentStyle = getComputedStyle(parent);\n if (parentStyle.position === 'static') {\n parent.style.position = 'relative';\n }\n\n // Insert after scroll container\n const nextSibling = this.scrollContainer.nextSibling;\n if (nextSibling) {\n parent.insertBefore(this.minimapEl, nextSibling);\n } else {\n parent.appendChild(this.minimapEl);\n }\n\n // Apply fixed positioning styles\n this.minimapEl.style.position = 'absolute';\n const offset = this.isCompactMode ? 8 : 12;\n this.minimapEl.style.bottom = `${offset}px`;\n this.minimapEl.style.right = `${offset}px`;\n this.minimapEl.style.marginTop = '0';\n }\n } else if (this.options.position === 'top') {\n // Insert before the scroll container (outside, above it)\n if (parent) {\n parent.insertBefore(this.minimapEl, this.scrollContainer);\n } else {\n // Fallback: insert as first child of scroll container\n this.scrollContainer.insertBefore(this.minimapEl, this.scrollContainer.firstChild);\n }\n } else {\n // Insert after the scroll container (outside, below it)\n if (parent) {\n const nextSibling = this.scrollContainer.nextSibling;\n if (nextSibling) {\n parent.insertBefore(this.minimapEl, nextSibling);\n } else {\n parent.appendChild(this.minimapEl);\n }\n } else {\n this.scrollContainer.appendChild(this.minimapEl);\n }\n }\n }\n\n /**\n * Updates the scroll state from the container\n */\n private updateScrollState(): void {\n if (!this.scrollContainer) return;\n\n const { scrollLeft, scrollWidth, clientWidth } = this.scrollContainer;\n\n this.scrollState = {\n scrollLeft,\n scrollWidth,\n clientWidth,\n viewportRatio: clientWidth / Math.max(scrollWidth, 1),\n positionRatio: scrollLeft / Math.max(scrollWidth - clientWidth, 1),\n };\n\n // Clamp values\n this.scrollState.viewportRatio = Math.min(1, Math.max(0, this.scrollState.viewportRatio));\n this.scrollState.positionRatio = Math.min(1, Math.max(0, this.scrollState.positionRatio));\n\n // Update ARIA values\n if (this.minimapEl) {\n this.minimapEl.setAttribute(\n 'aria-valuenow',\n String(Math.round(this.scrollState.positionRatio * 100))\n );\n }\n }\n\n /**\n * Renders the minimap\n */\n private render(): void {\n if (this.isDestroyed) return;\n\n if (this.isCompactMode && this.isCompactCollapsed) return;\n\n this.updateViewport();\n\n if (this.options.mode === 'canvas') {\n this.renderCanvas();\n }\n }\n\n /**\n * Canvas metrics for calculations - cached values to avoid repeated computations\n */\n private getCanvasMetrics() {\n const width = this.minimapEl?.offsetWidth ?? 0;\n const zoom = this.zoomState.level;\n const numCols = this.columns.length;\n const visibleRatio = 1 / zoom;\n const visibleCols = numCols * visibleRatio;\n const cellWidth = visibleCols > 0 ? width / visibleCols : 0;\n\n // Calculate panX based on scroll position\n let panX = 0;\n if (zoom > 1 && this.scrollContainer) {\n const { scrollLeft, scrollWidth, clientWidth } = this.scrollContainer;\n const maxScroll = Math.max(scrollWidth - clientWidth, 1);\n const scrollRatio = scrollLeft / maxScroll;\n panX = scrollRatio * (1 - visibleRatio);\n }\n\n const startColFloat = panX * numCols;\n const startCol = Math.floor(startColFloat);\n const endCol = Math.min(Math.ceil(startColFloat + visibleCols) + 1, numCols);\n const xOffset = -(startColFloat - startCol) * cellWidth;\n\n return {\n width,\n zoom,\n numCols,\n visibleRatio,\n visibleCols,\n cellWidth,\n panX,\n startColFloat,\n startCol,\n endCol,\n xOffset,\n };\n }\n\n /**\n * Calculates column index from mouse X position\n */\n private getColumnAtX(mouseX: number): number {\n const { width, numCols, panX, visibleRatio } = this.getCanvasMetrics();\n if (numCols === 0 || width === 0) return -1;\n\n const relativeX = mouseX / width;\n const tableX = panX + relativeX * visibleRatio;\n const colIndex = Math.floor(tableX * numCols);\n\n return Math.max(0, Math.min(numCols - 1, colIndex));\n }\n\n /**\n * Updates the viewport indicator position and size\n * In canvas mode, the viewport shows the focused (clicked) column\n */\n private updateViewport(): void {\n if (!this.viewportEl || !this.minimapEl) return;\n\n // Canvas mode: show focused column\n if (this.options.mode === 'canvas') {\n if (this.focusedColumn >= 0 && this.columns.length > 0) {\n const { cellWidth, startColFloat } = this.getCanvasMetrics();\n const colPosInCanvas = (this.focusedColumn - startColFloat) * cellWidth;\n\n this.viewportEl.style.cssText = `width:${cellWidth}px;left:${colPosInCanvas}px;display:block`;\n } else {\n this.viewportEl.style.display = 'none';\n }\n return;\n }\n\n // Columns mode: standard viewport\n const minimapWidth = this.minimapEl.offsetWidth;\n const viewportWidth = Math.max(minimapWidth * this.scrollState.viewportRatio, 20);\n const maxLeft = minimapWidth - viewportWidth;\n const viewportLeft = maxLeft * this.scrollState.positionRatio;\n\n this.viewportEl.style.cssText = `width:${viewportWidth}px;left:${viewportLeft}px;display:block`;\n }\n\n /**\n * Renders the canvas-mode visualization with table preview\n */\n private renderCanvas(): void {\n if (!this.canvasEl || !this.canvasCtx || !this.minimapEl) return;\n\n const metrics = this.getCanvasMetrics();\n const height = this.options.height;\n const dpr = window.devicePixelRatio || 1;\n\n // Set canvas size with HiDPI support\n this.canvasEl.width = metrics.width * dpr;\n this.canvasEl.height = height * dpr;\n this.canvasEl.style.width = `${metrics.width}px`;\n this.canvasEl.style.height = `${height}px`;\n\n // Scale for HiDPI\n this.canvasCtx.scale(dpr, dpr);\n\n // Render table\n this.renderTableDirect(metrics, height);\n }\n\n /**\n * Renders the visible portion of the table directly onto the canvas\n */\n private renderTableDirect(\n metrics: ReturnType<typeof this.getCanvasMetrics>,\n height: number\n ): void {\n if (!this.canvasCtx) return;\n\n const ctx = this.canvasCtx;\n const { width, numCols, cellWidth, startCol, endCol, xOffset } = metrics;\n const rows = Array.from(this.table.querySelectorAll('tr'));\n const numRows = rows.length;\n\n if (numRows === 0 || numCols === 0) return;\n\n // Calculate dimensions\n const headerHeight = Math.min(height * 0.15, 30);\n const cellHeight = (height - headerHeight) / numRows;\n const fontSize = Math.min(cellHeight * 0.6, cellWidth * 0.15, 14);\n const headerFontSize = Math.min(headerHeight * 0.6, cellWidth * 0.15, 14);\n\n // Colors\n const colors = {\n bg: '#ffffff',\n headerBg: '#f1f5f9',\n border: '#e2e8f0',\n text: '#334155',\n headerText: '#1e293b',\n altRow: '#f8fafc',\n hoverFill: 'rgba(59, 130, 246, 0.08)',\n hoverStroke: 'rgba(59, 130, 246, 0.3)',\n };\n\n // Clear background\n ctx.fillStyle = colors.bg;\n ctx.fillRect(0, 0, width, height);\n\n // Draw header\n ctx.fillStyle = colors.headerBg;\n ctx.fillRect(0, 0, width, headerHeight);\n\n const headerRow = this.table.querySelector('thead tr') || rows[0];\n const headerCells = headerRow ? Array.from(headerRow.querySelectorAll('th, td')) : [];\n\n ctx.font = `bold ${headerFontSize}px system-ui, sans-serif`;\n ctx.textBaseline = 'middle';\n\n for (let col = startCol; col < endCol; col++) {\n const x = xOffset + (col - startCol) * cellWidth;\n if (x + cellWidth < 0 || x > width) continue;\n\n ctx.strokeStyle = colors.border;\n ctx.lineWidth = 1;\n ctx.strokeRect(x, 0, cellWidth, headerHeight);\n\n const text = headerCells[col]?.textContent?.trim() || `Col ${col + 1}`;\n ctx.fillStyle = colors.headerText;\n ctx.save();\n ctx.beginPath();\n ctx.rect(x + 2, 0, cellWidth - 4, headerHeight);\n ctx.clip();\n ctx.fillText(text, x + 4, headerHeight / 2);\n ctx.restore();\n }\n\n // Draw data rows\n ctx.font = `${fontSize}px system-ui, sans-serif`;\n\n for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {\n const row = rows[rowIndex];\n if (row.closest('thead')) continue;\n\n const y = headerHeight + rowIndex * cellHeight;\n if (y + cellHeight < 0 || y > height) continue;\n\n // Alternate row background\n if (rowIndex % 2 === 1) {\n ctx.fillStyle = colors.altRow;\n ctx.fillRect(0, y, width, cellHeight);\n }\n\n const cells = Array.from(row.querySelectorAll('th, td'));\n\n for (let col = startCol; col < endCol; col++) {\n const x = xOffset + (col - startCol) * cellWidth;\n if (x + cellWidth < 0 || x > width) continue;\n\n ctx.strokeStyle = colors.border;\n ctx.lineWidth = 0.5;\n ctx.strokeRect(x, y, cellWidth, cellHeight);\n\n const content = cells[col]?.textContent?.trim();\n if (content) {\n ctx.fillStyle = colors.text;\n ctx.save();\n ctx.beginPath();\n ctx.rect(x + 2, y, cellWidth - 4, cellHeight);\n ctx.clip();\n ctx.fillText(content, x + 4, y + cellHeight / 2);\n ctx.restore();\n }\n }\n }\n\n // Draw hover highlight\n if (this.hoveredColumn >= startCol && this.hoveredColumn < endCol) {\n const hoverX = xOffset + (this.hoveredColumn - startCol) * cellWidth;\n ctx.fillStyle = colors.hoverFill;\n ctx.fillRect(hoverX, 0, cellWidth, height);\n ctx.strokeStyle = colors.hoverStroke;\n ctx.lineWidth = 1;\n ctx.strokeRect(hoverX, 0, cellWidth, height);\n }\n }\n\n\n\n\n /**\n * Attaches event listeners\n */\n private attachEventListeners(): void {\n if (!this.scrollContainer || !this.minimapEl) return;\n\n // Scroll event on container\n this.scrollContainer.addEventListener('scroll', this.boundHandlers.onScroll, {\n passive: true,\n });\n\n // Click on minimap to jump\n this.minimapEl.addEventListener('click', this.boundHandlers.onMinimapClick);\n\n if (this.isCompactMode) {\n this.minimapEl.addEventListener('focusin', this.boundHandlers.onCompactFocusIn);\n this.minimapEl.addEventListener('focusout', this.boundHandlers.onCompactFocusOut);\n this.minimapEl.addEventListener('keydown', this.boundHandlers.onCompactKeyDown);\n document.addEventListener('click', this.boundHandlers.onDocumentClick);\n }\n\n // Drag events on viewport\n if (this.options.draggable && this.viewportEl) {\n this.viewportEl.addEventListener('pointerdown', this.boundHandlers.onPointerDown);\n }\n\n // Zoom events on canvas (wheel) - also listen on viewport so zoom works when hovering over it\n if (this.options.zoomable && this.options.mode === 'canvas' && this.canvasEl) {\n this.canvasEl.addEventListener('wheel', this.boundHandlers.onWheel, { passive: false });\n this.canvasEl.addEventListener('pointerdown', this.boundHandlers.onCanvasPointerDown);\n this.canvasEl.style.cursor = this.zoomState.level > 1 ? 'grab' : 'pointer';\n\n // Also listen on viewport for wheel events\n if (this.viewportEl) {\n this.viewportEl.addEventListener('wheel', this.boundHandlers.onWheel, { passive: false });\n }\n }\n\n // Hover events on canvas for column highlighting\n if (this.options.mode === 'canvas' && this.canvasEl) {\n this.canvasEl.addEventListener('mousemove', this.boundHandlers.onCanvasMouseMove);\n this.canvasEl.addEventListener('mouseleave', this.boundHandlers.onCanvasMouseLeave);\n }\n\n // Global pointer events for dragging\n document.addEventListener('pointermove', this.boundHandlers.onPointerMove);\n document.addEventListener('pointerup', this.boundHandlers.onPointerUp);\n }\n\n /**\n * Handles scroll events on the container\n */\n private onScroll(): void {\n if (this.isDragging) return;\n\n // Throttle with requestAnimationFrame\n if (this.rafId !== null) return;\n\n this.rafId = requestAnimationFrame(() => {\n this.updateScrollState();\n this.updateViewport();\n\n // Re-render canvas to update viewport position\n if (this.options.mode === 'canvas') {\n this.render();\n }\n\n this.rafId = null;\n });\n }\n\n /**\n * Handles click on the minimap to jump to position\n *\n * @param e - Mouse event\n */\n private onMinimapClick(e: MouseEvent): void {\n if (!this.minimapEl || !this.scrollContainer) return;\n\n if (this.isCompactMode && this.isCompactCollapsed) {\n e.preventDefault();\n this.expandCompact();\n return;\n }\n\n // Ignore clicks during dragging, panning, or right after panning\n if (this.isDragging || this.isPanning || this.wasPanning) return;\n\n // Ignore clicks on viewport\n if (e.target === this.viewportEl) return;\n\n const { scrollWidth, clientWidth } = this.scrollContainer;\n const numCols = this.columns.length;\n\n // If we have a hovered column (from canvas mode), scroll to that column and set focus\n if (this.hoveredColumn >= 0 && numCols > 0) {\n // Set the focused column\n this.focusedColumn = this.hoveredColumn;\n\n // Calculate the scroll position to center the hovered column\n const colWidth = scrollWidth / numCols;\n const colCenter = (this.hoveredColumn + 0.5) * colWidth;\n const targetScroll = colCenter - clientWidth / 2;\n\n // Clamp to valid scroll range\n const maxScroll = scrollWidth - clientWidth;\n const clampedScroll = Math.max(0, Math.min(maxScroll, targetScroll));\n\n this.scrollContainer.scrollTo({\n left: clampedScroll,\n behavior: 'smooth',\n });\n\n // Update viewport to show focused column\n this.updateViewport();\n return;\n }\n\n // Fallback: calculate position from click coordinates\n const rect = this.minimapEl.getBoundingClientRect();\n const clickX = e.clientX - rect.left;\n const clickRatio = clickX / rect.width; // 0-1 position within minimap\n\n const zoom = this.zoomState.level;\n\n let targetRatio: number;\n\n if (zoom > 1) {\n // When zoomed: the minimap shows a portion of the table\n // Calculate which portion based on current scroll position\n const scrollLeft = this.scrollContainer.scrollLeft;\n const scrollRatio = scrollLeft / Math.max(scrollWidth - clientWidth, 1);\n const visibleRatio = 1 / zoom;\n const panX = scrollRatio * (1 - visibleRatio);\n\n // Convert click within zoomed view to full table position\n targetRatio = panX + clickRatio * visibleRatio;\n } else {\n // Not zoomed: direct mapping\n targetRatio = clickRatio;\n }\n\n // Calculate target scroll position (center the clicked position)\n const targetPosition = targetRatio * scrollWidth;\n const targetScroll = targetPosition - clientWidth / 2;\n\n // Clamp to valid scroll range\n const maxScroll = scrollWidth - clientWidth;\n const clampedScroll = Math.max(0, Math.min(maxScroll, targetScroll));\n\n this.scrollContainer.scrollTo({\n left: clampedScroll,\n behavior: 'smooth',\n });\n }\n\n /**\n * Handles pointer down on viewport for drag start\n *\n * @param e - Pointer event\n */\n private onPointerDown(e: PointerEvent): void {\n if (!this.viewportEl || !this.scrollContainer) return;\n\n e.preventDefault();\n e.stopPropagation();\n\n this.isDragging = true;\n this.dragStartX = e.clientX;\n this.dragStartScrollLeft = this.scrollContainer.scrollLeft;\n\n this.viewportEl.classList.add('tm-viewport--dragging');\n this.viewportEl.setPointerCapture(e.pointerId);\n }\n\n /**\n * Handles pointer move during drag\n *\n * @param e - Pointer event\n */\n private onPointerMove(e: PointerEvent): void {\n // Check if we should start panning (threshold check for potential pan)\n if (this.isPotentialPan && !this.isPanning && this.canvasEl && this.zoomState.level > 1) {\n const deltaX = Math.abs(e.clientX - this.panStartX);\n const PAN_THRESHOLD = 3; // pixels before we consider it a drag vs click\n\n if (deltaX > PAN_THRESHOLD) {\n this.isPanning = true;\n this.canvasEl.style.cursor = 'grabbing';\n }\n }\n\n // Handle canvas dragging (scrolls the table)\n if (this.isPanning && this.canvasEl && this.minimapEl && this.scrollContainer) {\n e.preventDefault();\n\n const deltaX = e.clientX - this.panStartX;\n const minimapWidth = this.minimapEl.offsetWidth;\n const { scrollWidth, clientWidth } = this.scrollContainer;\n const maxScroll = scrollWidth - clientWidth;\n\n // Convert minimap movement to scroll movement\n // When zoomed, movement on minimap represents more scroll distance\n const scrollDelta = (deltaX / minimapWidth) * maxScroll * this.zoomState.level;\n const newScrollLeft = this.dragStartScrollLeft + scrollDelta;\n\n // Apply scroll\n this.scrollContainer.scrollLeft = Math.max(0, Math.min(maxScroll, newScrollLeft));\n\n // Update state and re-render\n this.updateScrollState();\n this.updateViewport();\n this.render();\n return;\n }\n\n\n // Handle viewport dragging\n if (!this.isDragging || !this.minimapEl || !this.scrollContainer) return;\n\n e.preventDefault();\n\n const deltaX = e.clientX - this.dragStartX;\n const minimapWidth = this.minimapEl.offsetWidth;\n const scrollableWidth =\n this.scrollContainer.scrollWidth - this.scrollContainer.clientWidth;\n\n // Convert minimap delta to scroll delta\n const scrollDelta = (deltaX / minimapWidth) * scrollableWidth;\n const newScrollLeft = this.dragStartScrollLeft + scrollDelta;\n\n // Apply scroll\n this.scrollContainer.scrollLeft = Math.max(\n 0,\n Math.min(scrollableWidth, newScrollLeft)\n );\n\n // Update state immediately\n this.updateScrollState();\n this.updateViewport();\n }\n\n /**\n * Handles pointer up to end drag\n *\n * @param e - Pointer event\n */\n private onPointerUp(e: PointerEvent): void {\n // Handle potential pan that was actually a click\n if (this.isPotentialPan && !this.isPanning && this.canvasEl && this.minimapEl) {\n this.isPotentialPan = false;\n\n if (this.canvasEl.hasPointerCapture(e.pointerId)) {\n this.canvasEl.releasePointerCapture(e.pointerId);\n }\n\n // Simulate a click on the minimap at this position\n const clickEvent = new MouseEvent('click', {\n clientX: e.clientX,\n clientY: e.clientY,\n bubbles: true,\n });\n this.minimapEl.dispatchEvent(clickEvent);\n return;\n }\n\n this.isPotentialPan = false;\n\n // End canvas panning\n if (this.isPanning && this.canvasEl) {\n this.isPanning = false;\n this.wasPanning = true;\n\n if (this.canvasEl.hasPointerCapture(e.pointerId)) {\n this.canvasEl.releasePointerCapture(e.pointerId);\n }\n\n this.canvasEl.style.cursor = this.zoomState.level > 1 ? 'grab' : 'pointer';\n\n // Reset wasPanning after a short delay to allow click event to be ignored\n setTimeout(() => {\n this.wasPanning = false;\n }, 100);\n }\n\n // End viewport dragging\n if (this.isDragging) {\n this.isDragging = false;\n\n if (this.viewportEl) {\n this.viewportEl.classList.remove('tm-viewport--dragging');\n this.viewportEl.releasePointerCapture(e.pointerId);\n }\n }\n }\n\n /**\n * Handles wheel events for zoom\n *\n * @param e - Wheel event\n */\n private onWheel(e: WheelEvent): void {\n if (!this.options.zoomable || this.options.mode !== 'canvas') return;\n\n e.preventDefault();\n\n const delta = -e.deltaY * this.options.zoomSpeed;\n const newZoom = Math.max(\n this.options.minZoom,\n Math.min(this.options.maxZoom, this.zoomState.level + delta)\n );\n\n // Just update zoom level - panX is derived from scroll position\n this.zoomState = {\n level: newZoom,\n panX: 0, // Not used anymore - derived from scroll position\n isMinZoom: newZoom <= this.options.minZoom,\n isMaxZoom: newZoom >= this.options.maxZoom,\n };\n\n this.render();\n }\n\n /** Whether we started a potential pan (waiting to see if it's a click or drag) */\n private isPotentialPan = false;\n\n /**\n * Handles pointer down on canvas for drag start (scrolls table when zoomed)\n *\n * @param e - Pointer event\n */\n private onCanvasPointerDown(e: PointerEvent): void {\n if (!this.canvasEl || !this.scrollContainer) return;\n\n // At any zoom level, track potential pan/click\n this.isPotentialPan = true;\n this.panStartX = e.clientX;\n this.dragStartScrollLeft = this.scrollContainer.scrollLeft;\n\n // Only capture for panning at zoom > 1\n if (this.zoomState.level > 1) {\n e.preventDefault();\n this.canvasEl.setPointerCapture(e.pointerId);\n }\n }\n\n /**\n * Handles mouse move on canvas for column hover highlighting\n */\n private onCanvasMouseMove(e: MouseEvent): void {\n if (!this.canvasEl || this.isPanning) return;\n\n const rect = this.canvasEl.getBoundingClientRect();\n const newHoveredColumn = this.getColumnAtX(e.clientX - rect.left);\n\n if (newHoveredColumn !== this.hoveredColumn) {\n this.hoveredColumn = newHoveredColumn;\n this.canvasEl.style.cursor = newHoveredColumn >= 0 ? 'pointer' : (this.zoomState.level > 1 ? 'grab' : 'default');\n this.render();\n }\n }\n\n /**\n * Handles mouse leave on canvas to clear hover state\n */\n private onCanvasMouseLeave(): void {\n if (this.hoveredColumn !== -1) {\n this.hoveredColumn = -1;\n if (this.canvasEl) {\n this.canvasEl.style.cursor = this.zoomState.level > 1 ? 'grab' : 'default';\n }\n this.render();\n }\n }\n\n /**\n * Expands the compact minimap and clears any pending collapse.\n */\n private expandCompact(): void {\n if (!this.isCompactMode || !this.minimapEl) return;\n\n this.clearCompactCollapseTimer();\n this.applyCompactDimensions(false);\n this.render();\n }\n\n /**\n * Collapses the compact minimap to the small dot handle.\n */\n private collapseCompact(): void {\n if (!this.isCompactMode || !this.minimapEl) return;\n\n this.applyCompactDimensions(true);\n }\n\n /**\n * Applies compact sizing/state to the minimap.\n */\n private applyCompactDimensions(collapsed: boolean): void {\n if (!this.minimapEl || !this.isCompactMode) return;\n\n this.isCompactCollapsed = collapsed;\n\n if (collapsed) {\n this.minimapEl.classList.add('tm-minimap--compact-collapsed');\n this.minimapEl.classList.remove('tm-minimap--compact-expanded');\n this.minimapEl.style.setProperty('--tm-minimap-width', `${COMPACT_HANDLE_SIZE}px`);\n this.minimapEl.style.setProperty('--tm-minimap-height', `${COMPACT_HANDLE_SIZE}px`);\n this.minimapEl.setAttribute('aria-expanded', 'false');\n return;\n }\n\n this.minimapEl.classList.remove('tm-minimap--compact-collapsed');\n this.minimapEl.classList.add('tm-minimap--compact-expanded');\n this.minimapEl.style.setProperty('--tm-minimap-width', `${this.options.fixedWidth}px`);\n this.minimapEl.style.setProperty('--tm-minimap-height', `${this.options.height}px`);\n this.minimapEl.setAttribute('aria-expanded', 'true');\n }\n\n /**\n * Clears a pending compact collapse timer.\n */\n private clearCompactCollapseTimer(): void {\n if (this.compactCollapseTimer === null) return;\n\n clearTimeout(this.compactCollapseTimer);\n this.compactCollapseTimer = null;\n }\n\n /**\n * Schedules the compact minimap to collapse.\n */\n private scheduleCompactCollapse(delay = COMPACT_COLLAPSE_DELAY): void {\n if (!this.isCompactMode) return;\n\n this.clearCompactCollapseTimer();\n this.compactCollapseTimer = window.setTimeout(() => {\n this.collapseCompact();\n this.compactCollapseTimer = null;\n }, delay);\n }\n\n /**\n * Handles document click for closing compact mode when clicking outside.\n */\n private onDocumentClick(e: MouseEvent): void {\n if (!this.isCompactMode || !this.minimapEl || this.isCompactCollapsed) return;\n\n // Check if click is outside the minimap\n if (!this.minimapEl.contains(e.target as Node)) {\n this.collapseCompact();\n }\n }\n\n /**\n * Handles focus entering compact mode.\n */\n private onCompactFocusIn(): void {\n if (!this.isCompactMode) return;\n\n this.expandCompact();\n }\n\n /**\n * Handles focus leaving compact mode.\n */\n private onCompactFocusOut(): void {\n if (!this.isCompactMode) return;\n\n this.scheduleCompactCollapse(0);\n }\n\n /**\n * Keyboard interactions for compact mode.\n */\n private onCompactKeyDown(e: KeyboardEvent): void {\n if (!this.isCompactMode) return;\n\n if (e.key === 'Escape') {\n e.preventDefault();\n this.collapseCompact();\n return;\n }\n\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n if (this.isCompactCollapsed) {\n this.expandCompact();\n } else {\n this.collapseCompact();\n }\n }\n }\n\n /**\n * Sets up ResizeObserver and MutationObserver\n */\n private setupObservers(): void {\n // ResizeObserver for responsive updates\n this.resizeObserver = new ResizeObserver(() => {\n this.onResize();\n });\n\n if (this.scrollContainer) {\n this.resizeObserver.observe(this.scrollContainer);\n }\n this.resizeObserver.observe(this.table);\n\n // MutationObserver for table structure changes\n this.mutationObserver = new MutationObserver((mutations) => {\n const hasStructuralChanges = mutations.some(\n (m) => m.type === 'childList' || m.attributeName === 'colspan'\n );\n\n if (hasStructuralChanges) {\n this.onTableMutation();\n }\n });\n\n this.mutationObserver.observe(this.table, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: ['colspan'],\n });\n }\n\n /**\n * Handles resize events\n */\n private onResize(): void {\n if (this.isDestroyed) return;\n\n // Debounce resize handling\n if (this.rafId !== null) {\n cancelAnimationFrame(this.rafId);\n }\n\n this.rafId = requestAnimationFrame(() => {\n this.detectColumns();\n this.updateScrollState();\n\n this.render();\n\n // Rebuild columns if in columns mode\n if (this.options.mode === 'columns' && this.columnsEl && this.minimapEl) {\n this.columnsEl.innerHTML = '';\n this.columns.forEach((col) => {\n const colEl = document.createElement('div');\n colEl.className = 'tm-column';\n colEl.style.width = `${col.widthPercent}%`;\n this.columnsEl!.appendChild(colEl);\n });\n }\n\n this.rafId = null;\n });\n }\n\n /**\n * Handles table mutation events\n */\n private onTableMutation(): void {\n if (this.isDestroyed) return;\n\n this.detectColumns();\n this.updateScrollState();\n\n this.render();\n\n // Rebuild columns if in columns mode\n if (this.options.mode === 'columns' && this.columnsEl) {\n this.columnsEl.innerHTML = '';\n this.columns.forEach((col) => {\n const colEl = document.createElement('div');\n colEl.className = 'tm-column';\n colEl.style.width = `${col.widthPercent}%`;\n this.columnsEl!.appendChild(colEl);\n });\n }\n }\n\n /**\n * Gets the current scroll state\n *\n * @returns Current scroll state\n */\n public getScrollState(): ScrollState {\n return { ...this.scrollState };\n }\n\n /**\n * Gets the detected columns\n *\n * @returns Array of column information\n */\n public getColumns(): ColumnInfo[] {\n return [...this.columns];\n }\n\n /**\n * Scrolls to a specific column\n *\n * @param columnIndex - Zero-based column index\n * @param smooth - Use smooth scrolling\n */\n public scrollToColumn(columnIndex: number, smooth = true): void {\n if (!this.scrollContainer || columnIndex < 0 || columnIndex >= this.columns.length) {\n return;\n }\n\n const cellsBefore = this.columns.slice(0, columnIndex);\n const offsetLeft = cellsBefore.reduce((sum, col) => sum + col.width, 0);\n\n this.scrollContainer.scrollTo({\n left: offsetLeft,\n behavior: smooth ? 'smooth' : 'auto',\n });\n }\n\n /**\n * Forces a refresh of the minimap\n */\n public refresh(): void {\n if (this.isDestroyed) return;\n\n this.scrollContainer = this.findScrollContainer();\n this.detectColumns();\n this.updateScrollState();\n\n this.render();\n\n if (this.options.mode === 'columns' && this.columnsEl) {\n this.columnsEl.innerHTML = '';\n this.columns.forEach((col) => {\n const colEl = document.createElement('div');\n colEl.className = 'tm-column';\n colEl.style.width = `${col.widthPercent}%`;\n this.columnsEl!.appendChild(colEl);\n });\n }\n }\n\n /**\n * Gets the current zoom state (canvas mode only)\n *\n * @returns Current zoom state\n */\n public getZoomState(): ZoomState {\n return { ...this.zoomState };\n }\n\n /**\n * Sets the zoom level programmatically (canvas mode only)\n *\n * @param level - Zoom level (1 = no zoom)\n * @param panX - Optional pan position (0-1)\n */\n public setZoom(level: number, panX?: number): void {\n if (this.isDestroyed || this.options.mode !== 'canvas') return;\n\n const newZoom = Math.max(\n this.options.minZoom,\n Math.min(this.options.maxZoom, level)\n );\n\n const visibleRange = 1 / newZoom;\n const maxPanX = 1 - visibleRange;\n\n let newPanX = panX !== undefined ? panX : this.zoomState.panX;\n newPanX = Math.max(0, Math.min(maxPanX, newPanX));\n\n this.zoomState = {\n level: newZoom,\n panX: newZoom > 1 ? newPanX : 0,\n isMinZoom: newZoom <= this.options.minZoom,\n isMaxZoom: newZoom >= this.options.maxZoom,\n };\n\n this.render();\n }\n\n /**\n * Resets zoom to default (shows full table overview)\n */\n public resetZoom(): void {\n this.setZoom(1, 0);\n }\n\n /**\n * Zooms to a specific column range (canvas mode only)\n *\n * @param startCol - Start column index\n * @param endCol - End column index\n */\n public zoomToColumns(startCol: number, endCol: number): void {\n if (this.isDestroyed || this.options.mode !== 'canvas') return;\n\n const numCols = this.columns.length;\n if (numCols === 0) return;\n\n // Clamp column indices\n const start = Math.max(0, Math.min(numCols - 1, startCol));\n const end = Math.max(start + 1, Math.min(numCols, endCol));\n\n const colRange = end - start;\n const zoom = numCols / colRange;\n const panX = start / numCols;\n\n this.setZoom(zoom, panX);\n }\n\n /**\n * Destroys the minimap instance and cleans up resources\n */\n public destroy(): void {\n if (this.isDestroyed) return;\n this.isDestroyed = true;\n\n this.clearCompactCollapseTimer();\n\n // Cancel any pending animation frame\n if (this.rafId !== null) {\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n\n // Remove event listeners\n if (this.scrollContainer) {\n this.scrollContainer.removeEventListener('scroll', this.boundHandlers.onScroll);\n }\n\n if (this.minimapEl) {\n this.minimapEl.removeEventListener('click', this.boundHandlers.onMinimapClick);\n this.minimapEl.removeEventListener('focusin', this.boundHandlers.onCompactFocusIn);\n this.minimapEl.removeEventListener('focusout', this.boundHandlers.onCompactFocusOut);\n this.minimapEl.removeEventListener('keydown', this.boundHandlers.onCompactKeyDown);\n }\n\n document.removeEventListener('click', this.boundHandlers.onDocumentClick);\n\n if (this.viewportEl) {\n this.viewportEl.removeEventListener('pointerdown', this.boundHandlers.onPointerDown);\n this.viewportEl.removeEventListener('wheel', this.boundHandlers.onWheel);\n }\n\n if (this.canvasEl) {\n this.canvasEl.removeEventListener('wheel', this.boundHandlers.onWheel);\n this.canvasEl.removeEventListener('pointerdown', this.boundHandlers.onCanvasPointerDown);\n this.canvasEl.removeEventListener('mousemove', this.boundHandlers.onCanvasMouseMove);\n this.canvasEl.removeEventListener('mouseleave', this.boundHandlers.onCanvasMouseLeave);\n }\n\n document.removeEventListener('pointermove', this.boundHandlers.onPointerMove);\n document.removeEventListener('pointerup', this.boundHandlers.onPointerUp);\n\n // Disconnect observers\n if (this.resizeObserver) {\n this.resizeObserver.disconnect();\n this.resizeObserver = null;\n }\n\n if (this.mutationObserver) {\n this.mutationObserver.disconnect();\n this.mutationObserver = null;\n }\n\n // Remove DOM elements\n if (this.minimapEl && this.minimapEl.parentNode) {\n this.minimapEl.parentNode.removeChild(this.minimapEl);\n }\n\n // Clear references\n this.minimapEl = null;\n this.columnsEl = null;\n this.canvasEl = null;\n this.canvasCtx = null;\n this.viewportEl = null;\n this.scrollContainer = null;\n this.columns = [];\n }\n}\n","/**\n * Table Minimap - A framework-agnostic minimap component for large HTML tables\n *\n * @packageDocumentation\n */\n\nexport { TableMinimap } from './TableMinimap';\nexport type {\n TableMinimapOptions,\n ColumnInfo,\n ScrollState,\n CellData,\n TableSelector,\n ZoomState,\n} from './types';\n\n// Auto-inject styles into DOM\nimport styles from './styles.css?inline';\n\nlet stylesInjected = false;\n\nexport function injectStyles(): void {\n if (stylesInjected || typeof document === 'undefined') return;\n\n const style = document.createElement('style');\n style.id = 'table-minimap-styles';\n style.textContent = styles;\n document.head.appendChild(style);\n stylesInjected = true;\n}\n\n// Auto-inject on import (can be tree-shaken if not used)\ninjectStyles();\n\n"],"names":["DEFAULT_OPTIONS","COMPACT_HANDLE_SIZE","COMPACT_DOT_SIZE","COMPACT_COLLAPSE_DELAY","TableMinimap","selector","options","__publicField","element","parent","style","overflowX","overflowY","theadCells","cells","firstRow","tableWidth","cell","colspan","cellWidth","i","width","col","colEl","nextSibling","offset","scrollLeft","scrollWidth","clientWidth","_a","zoom","numCols","visibleRatio","visibleCols","panX","maxScroll","startColFloat","startCol","endCol","xOffset","mouseX","relativeX","tableX","colIndex","colPosInCanvas","minimapWidth","viewportWidth","viewportLeft","metrics","height","dpr","ctx","rows","numRows","headerHeight","cellHeight","fontSize","headerFontSize","colors","headerRow","headerCells","x","text","_b","rowIndex","row","y","content","_d","_c","hoverX","e","colWidth","targetScroll","clampedScroll","rect","clickRatio","targetRatio","scrollRatio","deltaX","scrollDelta","newScrollLeft","scrollableWidth","clickEvent","delta","newZoom","newHoveredColumn","collapsed","delay","mutations","m","columnIndex","smooth","offsetLeft","sum","level","maxPanX","newPanX","start","colRange","stylesInjected","injectStyles","styles"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"table-minimap.cjs","sources":["../src/TableMinimap.ts","../src/index.ts"],"sourcesContent":["import type {\n TableMinimapOptions,\n RequiredOptions,\n ColumnInfo,\n ScrollState,\n TableSelector,\n ZoomState,\n} from './types';\n\n/**\n * Default configuration options\n */\nconst DEFAULT_OPTIONS: RequiredOptions = {\n mode: 'columns',\n height: 40,\n position: 'bottom',\n fixedWidth: 300,\n fixedPosition: 'bottom-right',\n compact: false,\n draggable: true,\n showViewport: true,\n zoomable: false,\n minZoom: 1,\n maxZoom: 10,\n zoomSpeed: 0.1,\n};\n\n/** Compact floating minimap handle size in pixels */\nconst COMPACT_HANDLE_SIZE = 24;\n\n/** Visible dot size inside the compact handle in pixels */\nconst COMPACT_DOT_SIZE = 5;\n\n/** Delay before compact mode collapses after pointer leave */\nconst COMPACT_COLLAPSE_DELAY = 180;\n\n/**\n * TableMinimap - A framework-agnostic minimap component for large HTML tables\n *\n * @example\n * ```ts\n * import { TableMinimap } from 'table-minimap';\n * import 'table-minimap/style.css';\n *\n * const minimap = new TableMinimap('#my-table');\n *\n * // Or with options\n * const minimap = new TableMinimap('#my-table', {\n * mode: 'canvas',\n * height: 50,\n * position: 'top'\n * });\n *\n * // Cleanup\n * minimap.destroy();\n * ```\n */\nexport class TableMinimap {\n /** The target table element */\n private readonly table: HTMLTableElement;\n\n /** Configuration options */\n private readonly options: RequiredOptions;\n\n /** Whether compact fixed-overlay behavior is enabled */\n private readonly isCompactMode: boolean;\n\n /** Whether the compact minimap is currently collapsed */\n private isCompactCollapsed = false;\n\n /** Whether the compact minimap is currently expanding (transition in progress) */\n private isCompactExpanding = false;\n\n /** Timeout used to collapse compact mode after pointer leave */\n private compactCollapseTimer: number | null = null;\n\n /** The scrollable container (parent of table) */\n private scrollContainer: HTMLElement | null = null;\n\n /** Main minimap container element */\n private minimapEl: HTMLDivElement | null = null;\n\n /** Columns container for columns mode */\n private columnsEl: HTMLDivElement | null = null;\n\n /** Canvas element for canvas mode */\n private canvasEl: HTMLCanvasElement | null = null;\n\n /** Canvas 2D rendering context */\n private canvasCtx: CanvasRenderingContext2D | null = null;\n\n\n /** Viewport indicator element */\n private viewportEl: HTMLDivElement | null = null;\n\n /** Detected column information */\n private columns: ColumnInfo[] = [];\n\n /** Current scroll state */\n private scrollState: ScrollState = {\n scrollLeft: 0,\n scrollWidth: 0,\n clientWidth: 0,\n viewportRatio: 1,\n positionRatio: 0,\n };\n\n /** Current zoom state */\n private zoomState: ZoomState = {\n level: 1,\n panX: 0,\n isMinZoom: true,\n isMaxZoom: false,\n };\n\n /** Is the viewport being dragged */\n private isDragging = false;\n\n /** Is the canvas being panned (when zoomed) */\n private isPanning = false;\n\n /** Was just panning (to prevent click after pan) */\n private wasPanning = false;\n\n /** Pan start position */\n private panStartX = 0;\n\n /** Currently hovered column index (-1 = none) */\n private hoveredColumn = -1;\n\n /** Drag start X position */\n private dragStartX = 0;\n\n /** Drag start scroll position */\n private dragStartScrollLeft = 0;\n\n /** ResizeObserver instance */\n private resizeObserver: ResizeObserver | null = null;\n\n /** MutationObserver instance */\n private mutationObserver: MutationObserver | null = null;\n\n /** Bound event handlers for cleanup */\n private boundHandlers: {\n onScroll: () => void;\n onPointerDown: (e: PointerEvent) => void;\n onPointerMove: (e: PointerEvent) => void;\n onPointerUp: (e: PointerEvent) => void;\n onMinimapClick: (e: MouseEvent) => void;\n onWheel: (e: WheelEvent) => void;\n onCanvasPointerDown: (e: PointerEvent) => void;\n onCanvasMouseMove: (e: MouseEvent) => void;\n onCanvasMouseLeave: () => void;\n onCompactFocusIn: () => void;\n onCompactFocusOut: () => void;\n onCompactKeyDown: (e: KeyboardEvent) => void;\n onDocumentClick: (e: MouseEvent) => void;\n };\n\n /** Animation frame ID for throttling */\n private rafId: number | null = null;\n\n /** Whether the instance has been destroyed */\n private isDestroyed = false;\n\n /**\n * Creates a new TableMinimap instance\n *\n * @param selector - CSS selector string or HTMLTableElement\n * @param options - Configuration options\n * @throws Error if table element is not found or invalid\n */\n constructor(selector: TableSelector, options: TableMinimapOptions = {}) {\n this.table = this.resolveTable(selector);\n this.options = { ...DEFAULT_OPTIONS, ...options };\n this.isCompactMode = this.options.compact && this.options.position === 'fixed';\n\n // Bind event handlers\n this.boundHandlers = {\n onScroll: this.onScroll.bind(this),\n onPointerDown: this.onPointerDown.bind(this),\n onPointerMove: this.onPointerMove.bind(this),\n onPointerUp: this.onPointerUp.bind(this),\n onMinimapClick: this.onMinimapClick.bind(this),\n onWheel: this.onWheel.bind(this),\n onCanvasPointerDown: this.onCanvasPointerDown.bind(this),\n onCanvasMouseMove: this.onCanvasMouseMove.bind(this),\n onCanvasMouseLeave: this.onCanvasMouseLeave.bind(this),\n onCompactFocusIn: this.onCompactFocusIn.bind(this),\n onCompactFocusOut: this.onCompactFocusOut.bind(this),\n onCompactKeyDown: this.onCompactKeyDown.bind(this),\n onDocumentClick: this.onDocumentClick.bind(this),\n };\n\n this.init();\n }\n\n /**\n * Resolves the table element from a selector or element\n *\n * @param selector - CSS selector or HTMLTableElement\n * @returns The resolved table element\n * @throws Error if element is not found or not a table\n */\n private resolveTable(selector: TableSelector): HTMLTableElement {\n let element: HTMLTableElement | null;\n\n if (typeof selector === 'string') {\n element = document.querySelector<HTMLTableElement>(selector);\n if (!element) {\n throw new Error(\n `TableMinimap: No element found for selector \"${selector}\"`\n );\n }\n } else if (selector instanceof HTMLTableElement) {\n element = selector;\n } else {\n throw new Error(\n 'TableMinimap: Selector must be a CSS selector string or an HTMLTableElement'\n );\n }\n\n if (element.tagName !== 'TABLE') {\n throw new Error(\n `TableMinimap: Element must be a <table>, got <${element.tagName.toLowerCase()}>`\n );\n }\n\n return element;\n }\n\n /**\n * Initializes the minimap\n */\n private init(): void {\n this.scrollContainer = this.findScrollContainer();\n this.detectColumns();\n this.createMinimapElement();\n this.updateScrollState();\n this.render();\n this.attachEventListeners();\n this.setupObservers();\n }\n\n /**\n * Finds the nearest scrollable parent container\n *\n * @returns The scrollable container or the table's parent\n */\n private findScrollContainer(): HTMLElement {\n let parent = this.table.parentElement;\n\n while (parent) {\n const style = getComputedStyle(parent);\n const overflowX = style.overflowX;\n const overflowY = style.overflow;\n\n if (\n overflowX === 'auto' ||\n overflowX === 'scroll' ||\n overflowY === 'auto' ||\n overflowY === 'scroll'\n ) {\n return parent;\n }\n\n // Check if content overflows\n if (parent.scrollWidth > parent.clientWidth) {\n return parent;\n }\n\n parent = parent.parentElement;\n }\n\n // Fallback to table's parent or body\n return this.table.parentElement || document.body;\n }\n\n /**\n * Detects table columns from thead or first row\n */\n private detectColumns(): void {\n this.columns = [];\n\n // Try thead th first\n const theadCells = this.table.querySelectorAll<HTMLTableCellElement>(\n 'thead th, thead td'\n );\n\n let cells: NodeListOf<HTMLTableCellElement> | HTMLTableCellElement[];\n\n if (theadCells.length > 0) {\n cells = theadCells;\n } else {\n // Fallback to first row cells\n const firstRow = this.table.querySelector('tr');\n if (firstRow) {\n cells = firstRow.querySelectorAll<HTMLTableCellElement>('th, td');\n } else {\n cells = [];\n }\n }\n\n const tableWidth = this.table.offsetWidth || 1;\n\n Array.from(cells).forEach((cell) => {\n // Handle colspan\n const colspan = cell.colSpan || 1;\n const cellWidth = cell.offsetWidth;\n\n for (let i = 0; i < colspan; i++) {\n const width = cellWidth / colspan;\n this.columns.push({\n index: this.columns.length,\n width,\n widthPercent: (width / tableWidth) * 100,\n });\n }\n });\n\n // Ensure at least one column\n if (this.columns.length === 0) {\n this.columns.push({\n index: 0,\n width: tableWidth,\n widthPercent: 100,\n });\n }\n }\n\n /**\n * Creates the minimap DOM element\n */\n private createMinimapElement(): void {\n // Create main container\n this.minimapEl = document.createElement('div');\n this.minimapEl.className = `tm-minimap tm-minimap--${this.options.position}`;\n this.minimapEl.style.setProperty('--tm-minimap-height', `${this.options.height}px`);\n\n // Set width and position class for fixed position\n if (this.options.position === 'fixed') {\n this.minimapEl.style.setProperty('--tm-minimap-width', `${this.options.fixedWidth}px`);\n this.minimapEl.classList.add(`tm-minimap--${this.options.fixedPosition}`);\n }\n\n if (this.isCompactMode) {\n this.minimapEl.classList.add('tm-minimap--compact', 'tm-minimap--compact-collapsed');\n this.minimapEl.style.setProperty('--tm-compact-dot-size', `${COMPACT_DOT_SIZE}px`);\n this.isCompactCollapsed = true;\n this.applyCompactDimensions(true);\n this.minimapEl.setAttribute('aria-expanded', 'false');\n } else {\n this.minimapEl.setAttribute('aria-expanded', 'true');\n }\n \n this.minimapEl.setAttribute('role', 'slider');\n this.minimapEl.setAttribute('aria-label', 'Table minimap navigation');\n this.minimapEl.setAttribute('aria-valuemin', '0');\n this.minimapEl.setAttribute('aria-valuemax', '100');\n this.minimapEl.setAttribute('tabindex', '0');\n\n // Create content based on mode\n if (this.options.mode === 'canvas') {\n this.createCanvasContent();\n } else {\n this.createColumnsContent();\n }\n\n // Create viewport indicator\n if (this.options.showViewport) {\n this.createViewportIndicator();\n }\n\n // Insert minimap\n this.insertMinimap();\n }\n\n /**\n * Creates columns-mode content\n */\n private createColumnsContent(): void {\n if (!this.minimapEl) return;\n\n this.columnsEl = document.createElement('div');\n this.columnsEl.className = 'tm-columns';\n\n this.columns.forEach(() => {\n const colEl = document.createElement('div');\n colEl.className = 'tm-column';\n this.columnsEl!.appendChild(colEl);\n });\n\n this.minimapEl.appendChild(this.columnsEl);\n }\n\n /**\n * Creates canvas-mode content\n */\n private createCanvasContent(): void {\n if (!this.minimapEl) return;\n\n this.canvasEl = document.createElement('canvas');\n this.canvasEl.className = 'tm-canvas';\n this.canvasCtx = this.canvasEl.getContext('2d');\n\n this.minimapEl.appendChild(this.canvasEl);\n }\n\n /**\n * Creates the viewport indicator element\n */\n private createViewportIndicator(): void {\n if (!this.minimapEl) return;\n\n this.viewportEl = document.createElement('div');\n this.viewportEl.className = 'tm-viewport';\n\n if (!this.options.draggable) {\n this.viewportEl.classList.add('tm-viewport--disabled');\n }\n\n this.minimapEl.appendChild(this.viewportEl);\n }\n\n /**\n * Inserts the minimap into the DOM\n */\n private insertMinimap(): void {\n if (!this.minimapEl || !this.scrollContainer) return;\n\n const parent = this.scrollContainer.parentElement;\n\n if (this.options.position === 'fixed') {\n // Fixed position: create a wrapper with relative positioning\n // Insert minimap after scroll container, but style it to overlay\n if (parent) {\n // Ensure parent has relative positioning for our fixed overlay\n const parentStyle = getComputedStyle(parent);\n if (parentStyle.position === 'static') {\n parent.style.position = 'relative';\n }\n\n // Insert after scroll container\n const nextSibling = this.scrollContainer.nextSibling;\n if (nextSibling) {\n parent.insertBefore(this.minimapEl, nextSibling);\n } else {\n parent.appendChild(this.minimapEl);\n }\n\n // Apply fixed positioning styles based on fixedPosition\n this.minimapEl.style.position = 'absolute';\n const offset = this.isCompactMode ? 8 : 12;\n const pos = this.options.fixedPosition;\n\n // Reset all positions first\n this.minimapEl.style.top = '';\n this.minimapEl.style.bottom = '';\n this.minimapEl.style.left = '';\n this.minimapEl.style.right = '';\n this.minimapEl.style.marginTop = '0';\n\n // Apply position based on fixedPosition option\n if (pos === 'top-left') {\n this.minimapEl.style.top = `${offset}px`;\n this.minimapEl.style.left = `${offset}px`;\n } else if (pos === 'top-right') {\n this.minimapEl.style.top = `${offset}px`;\n this.minimapEl.style.right = `${offset}px`;\n } else if (pos === 'bottom-left') {\n this.minimapEl.style.bottom = `${offset}px`;\n this.minimapEl.style.left = `${offset}px`;\n } else {\n // bottom-right (default)\n this.minimapEl.style.bottom = `${offset}px`;\n this.minimapEl.style.right = `${offset}px`;\n }\n }\n } else if (this.options.position === 'top') {\n // Insert before the scroll container (outside, above it)\n if (parent) {\n parent.insertBefore(this.minimapEl, this.scrollContainer);\n } else {\n // Fallback: insert as first child of scroll container\n this.scrollContainer.insertBefore(this.minimapEl, this.scrollContainer.firstChild);\n }\n } else {\n // Insert after the scroll container (outside, below it)\n if (parent) {\n const nextSibling = this.scrollContainer.nextSibling;\n if (nextSibling) {\n parent.insertBefore(this.minimapEl, nextSibling);\n } else {\n parent.appendChild(this.minimapEl);\n }\n } else {\n this.scrollContainer.appendChild(this.minimapEl);\n }\n }\n }\n\n /**\n * Updates the scroll state from the container\n */\n private updateScrollState(): void {\n if (!this.scrollContainer) return;\n\n const { scrollLeft, scrollWidth, clientWidth } = this.scrollContainer;\n\n this.scrollState = {\n scrollLeft,\n scrollWidth,\n clientWidth,\n viewportRatio: clientWidth / Math.max(scrollWidth, 1),\n positionRatio: scrollLeft / Math.max(scrollWidth - clientWidth, 1),\n };\n\n // Clamp values\n this.scrollState.viewportRatio = Math.min(1, Math.max(0, this.scrollState.viewportRatio));\n this.scrollState.positionRatio = Math.min(1, Math.max(0, this.scrollState.positionRatio));\n\n // Update ARIA values\n if (this.minimapEl) {\n this.minimapEl.setAttribute(\n 'aria-valuenow',\n String(Math.round(this.scrollState.positionRatio * 100))\n );\n }\n }\n\n /**\n * Renders the minimap\n */\n private render(): void {\n if (this.isDestroyed) return;\n\n if (this.isCompactMode && this.isCompactCollapsed) return;\n\n this.updateViewport();\n\n if (this.options.mode === 'canvas') {\n this.renderCanvas();\n }\n }\n\n /**\n * Canvas metrics for calculations - cached values to avoid repeated computations\n */\n private getCanvasMetrics() {\n const width = this.minimapEl?.offsetWidth ?? 0;\n const zoom = this.zoomState.level;\n const numCols = this.columns.length;\n const visibleRatio = 1 / zoom;\n const visibleCols = numCols * visibleRatio;\n const cellWidth = visibleCols > 0 ? width / visibleCols : 0;\n\n // Calculate panX based on scroll position\n let panX = 0;\n if (zoom > 1 && this.scrollContainer) {\n const { scrollLeft, scrollWidth, clientWidth } = this.scrollContainer;\n const maxScroll = Math.max(scrollWidth - clientWidth, 1);\n const scrollRatio = scrollLeft / maxScroll;\n panX = scrollRatio * (1 - visibleRatio);\n }\n\n const startColFloat = panX * numCols;\n const startCol = Math.floor(startColFloat);\n const endCol = Math.min(Math.ceil(startColFloat + visibleCols) + 1, numCols);\n const xOffset = -(startColFloat - startCol) * cellWidth;\n\n return {\n width,\n zoom,\n numCols,\n visibleRatio,\n visibleCols,\n cellWidth,\n panX,\n startColFloat,\n startCol,\n endCol,\n xOffset,\n };\n }\n\n /**\n * Calculates column index from mouse X position\n */\n private getColumnAtX(mouseX: number): number {\n const { width, numCols, panX, visibleRatio } = this.getCanvasMetrics();\n if (numCols === 0 || width === 0) return -1;\n\n const relativeX = mouseX / width;\n const tableX = panX + relativeX * visibleRatio;\n const colIndex = Math.floor(tableX * numCols);\n\n return Math.max(0, Math.min(numCols - 1, colIndex));\n }\n\n /**\n * Updates the viewport indicator position and size\n * Shows the visible portion of the table (columns mode only)\n */\n private updateViewport(): void {\n if (!this.viewportEl || !this.minimapEl) return;\n\n // No viewport in canvas mode\n if (this.options.mode === 'canvas') {\n this.viewportEl.style.display = 'none';\n return;\n }\n\n const minimapWidth = this.minimapEl.offsetWidth;\n \n // Columns mode: viewport showing visible area\n const viewportWidth = Math.max(minimapWidth * this.scrollState.viewportRatio, 20);\n const maxLeft = minimapWidth - viewportWidth;\n const viewportLeft = maxLeft * this.scrollState.positionRatio;\n this.viewportEl.style.cssText = `width:${viewportWidth}px;left:${viewportLeft}px;display:block`;\n }\n\n /**\n * Renders the canvas-mode visualization with table preview\n */\n private renderCanvas(): void {\n if (!this.canvasEl || !this.canvasCtx || !this.minimapEl) return;\n\n const metrics = this.getCanvasMetrics();\n const height = this.options.height;\n const dpr = window.devicePixelRatio || 1;\n\n // Set canvas size with HiDPI support\n this.canvasEl.width = metrics.width * dpr;\n this.canvasEl.height = height * dpr;\n this.canvasEl.style.width = `${metrics.width}px`;\n this.canvasEl.style.height = `${height}px`;\n\n // Scale for HiDPI\n this.canvasCtx.scale(dpr, dpr);\n\n // Render table\n this.renderTableDirect(metrics, height);\n }\n\n /**\n * Renders the visible portion of the table directly onto the canvas\n */\n private renderTableDirect(\n metrics: ReturnType<typeof this.getCanvasMetrics>,\n height: number\n ): void {\n if (!this.canvasCtx) return;\n\n const ctx = this.canvasCtx;\n const { width, numCols, cellWidth, startCol, endCol, xOffset } = metrics;\n const rows = Array.from(this.table.querySelectorAll('tr'));\n const numRows = rows.length;\n\n if (numRows === 0 || numCols === 0) return;\n\n // Calculate dimensions\n const headerHeight = Math.min(height * 0.15, 30);\n const cellHeight = (height - headerHeight) / numRows;\n const fontSize = Math.min(cellHeight * 0.6, cellWidth * 0.15, 14);\n const headerFontSize = Math.min(headerHeight * 0.6, cellWidth * 0.15, 14);\n\n // Colors\n const colors = {\n bg: '#ffffff',\n headerBg: '#f1f5f9',\n border: '#e2e8f0',\n text: '#334155',\n headerText: '#1e293b',\n altRow: '#f8fafc',\n hoverFill: 'rgba(59, 130, 246, 0.08)',\n hoverStroke: 'rgba(59, 130, 246, 0.3)',\n };\n\n // Clear background\n ctx.fillStyle = colors.bg;\n ctx.fillRect(0, 0, width, height);\n\n // Draw header\n ctx.fillStyle = colors.headerBg;\n ctx.fillRect(0, 0, width, headerHeight);\n\n const headerRow = this.table.querySelector('thead tr') || rows[0];\n const headerCells = headerRow ? Array.from(headerRow.querySelectorAll('th, td')) : [];\n\n ctx.font = `bold ${headerFontSize}px system-ui, sans-serif`;\n ctx.textBaseline = 'middle';\n\n for (let col = startCol; col < endCol; col++) {\n const x = xOffset + (col - startCol) * cellWidth;\n if (x + cellWidth < 0 || x > width) continue;\n\n ctx.strokeStyle = colors.border;\n ctx.lineWidth = 1;\n ctx.strokeRect(x, 0, cellWidth, headerHeight);\n\n const text = headerCells[col]?.textContent?.trim() || `Col ${col + 1}`;\n ctx.fillStyle = colors.headerText;\n ctx.save();\n ctx.beginPath();\n ctx.rect(x + 2, 0, cellWidth - 4, headerHeight);\n ctx.clip();\n ctx.fillText(text, x + 4, headerHeight / 2);\n ctx.restore();\n }\n\n // Draw data rows\n ctx.font = `${fontSize}px system-ui, sans-serif`;\n\n for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {\n const row = rows[rowIndex];\n if (row.closest('thead')) continue;\n\n const y = headerHeight + rowIndex * cellHeight;\n if (y + cellHeight < 0 || y > height) continue;\n\n // Alternate row background\n if (rowIndex % 2 === 1) {\n ctx.fillStyle = colors.altRow;\n ctx.fillRect(0, y, width, cellHeight);\n }\n\n const cells = Array.from(row.querySelectorAll('th, td'));\n\n for (let col = startCol; col < endCol; col++) {\n const x = xOffset + (col - startCol) * cellWidth;\n if (x + cellWidth < 0 || x > width) continue;\n\n ctx.strokeStyle = colors.border;\n ctx.lineWidth = 0.5;\n ctx.strokeRect(x, y, cellWidth, cellHeight);\n\n const content = cells[col]?.textContent?.trim();\n if (content) {\n ctx.fillStyle = colors.text;\n ctx.save();\n ctx.beginPath();\n ctx.rect(x + 2, y, cellWidth - 4, cellHeight);\n ctx.clip();\n ctx.fillText(content, x + 4, y + cellHeight / 2);\n ctx.restore();\n }\n }\n }\n\n // Draw hover highlight\n if (this.hoveredColumn >= startCol && this.hoveredColumn < endCol) {\n const hoverX = xOffset + (this.hoveredColumn - startCol) * cellWidth;\n ctx.fillStyle = colors.hoverFill;\n ctx.fillRect(hoverX, 0, cellWidth, height);\n ctx.strokeStyle = colors.hoverStroke;\n ctx.lineWidth = 1;\n ctx.strokeRect(hoverX, 0, cellWidth, height);\n }\n }\n\n\n\n\n /**\n * Attaches event listeners\n */\n private attachEventListeners(): void {\n if (!this.scrollContainer || !this.minimapEl) return;\n\n // Scroll event on container\n this.scrollContainer.addEventListener('scroll', this.boundHandlers.onScroll, {\n passive: true,\n });\n\n // Click on minimap to jump\n this.minimapEl.addEventListener('click', this.boundHandlers.onMinimapClick);\n\n if (this.isCompactMode) {\n this.minimapEl.addEventListener('focusin', this.boundHandlers.onCompactFocusIn);\n this.minimapEl.addEventListener('focusout', this.boundHandlers.onCompactFocusOut);\n this.minimapEl.addEventListener('keydown', this.boundHandlers.onCompactKeyDown);\n document.addEventListener('click', this.boundHandlers.onDocumentClick);\n }\n\n // Drag events on viewport\n if (this.options.draggable && this.viewportEl) {\n this.viewportEl.addEventListener('pointerdown', this.boundHandlers.onPointerDown);\n }\n\n // Zoom events on canvas (wheel) - also listen on viewport so zoom works when hovering over it\n if (this.options.zoomable && this.options.mode === 'canvas' && this.canvasEl) {\n this.canvasEl.addEventListener('wheel', this.boundHandlers.onWheel, { passive: false });\n this.canvasEl.addEventListener('pointerdown', this.boundHandlers.onCanvasPointerDown);\n this.canvasEl.style.cursor = this.zoomState.level > 1 ? 'grab' : 'pointer';\n\n // Also listen on viewport for wheel events\n if (this.viewportEl) {\n this.viewportEl.addEventListener('wheel', this.boundHandlers.onWheel, { passive: false });\n }\n }\n\n // Hover events on canvas for column highlighting\n if (this.options.mode === 'canvas' && this.canvasEl) {\n this.canvasEl.addEventListener('mousemove', this.boundHandlers.onCanvasMouseMove);\n this.canvasEl.addEventListener('mouseleave', this.boundHandlers.onCanvasMouseLeave);\n }\n\n // Global pointer events for dragging\n document.addEventListener('pointermove', this.boundHandlers.onPointerMove);\n document.addEventListener('pointerup', this.boundHandlers.onPointerUp);\n }\n\n /**\n * Handles scroll events on the container\n */\n private onScroll(): void {\n if (this.isDragging) return;\n\n // Throttle with requestAnimationFrame\n if (this.rafId !== null) return;\n\n this.rafId = requestAnimationFrame(() => {\n this.updateScrollState();\n this.updateViewport();\n\n // Re-render canvas\n if (this.options.mode === 'canvas') {\n this.render();\n }\n\n this.rafId = null;\n });\n }\n\n /**\n * Handles click on the minimap to jump to position\n *\n * @param e - Mouse event\n */\n private onMinimapClick(e: MouseEvent): void {\n if (!this.minimapEl || !this.scrollContainer) return;\n\n if (this.isCompactMode && this.isCompactCollapsed) {\n e.preventDefault();\n this.expandCompact();\n return;\n }\n\n // Ignore clicks during expansion transition, dragging, panning\n if (this.isCompactExpanding || this.isDragging || this.isPanning || this.wasPanning) return;\n\n const { scrollWidth, clientWidth } = this.scrollContainer;\n const maxScroll = scrollWidth - clientWidth;\n\n // Get click position relative to minimap\n const rect = this.minimapEl.getBoundingClientRect();\n const clickX = e.clientX - rect.left;\n\n // Canvas mode: scroll to center the clicked column\n if (this.options.mode === 'canvas') {\n const clickedColumn = this.getColumnAtX(clickX);\n\n if (clickedColumn >= 0) {\n const numCols = this.columns.length;\n const colWidth = scrollWidth / numCols;\n const colCenter = (clickedColumn + 0.5) * colWidth;\n const targetScroll = colCenter - clientWidth / 2;\n\n this.scrollContainer.scrollTo({\n left: Math.max(0, Math.min(maxScroll, targetScroll)),\n behavior: 'smooth',\n });\n }\n return;\n }\n\n // Columns mode: scroll to clicked position (percentage-based)\n const clickRatio = clickX / rect.width;\n const targetScroll = clickRatio * maxScroll;\n \n this.scrollContainer.scrollTo({\n left: Math.max(0, Math.min(maxScroll, targetScroll)),\n behavior: 'smooth',\n });\n }\n\n /**\n * Handles pointer down on viewport for drag start\n *\n * @param e - Pointer event\n */\n private onPointerDown(e: PointerEvent): void {\n if (!this.viewportEl || !this.scrollContainer) return;\n\n e.preventDefault();\n e.stopPropagation();\n\n this.isDragging = true;\n this.dragStartX = e.clientX;\n this.dragStartScrollLeft = this.scrollContainer.scrollLeft;\n\n this.viewportEl.classList.add('tm-viewport--dragging');\n this.viewportEl.setPointerCapture(e.pointerId);\n }\n\n /**\n * Handles pointer move during drag\n *\n * @param e - Pointer event\n */\n private onPointerMove(e: PointerEvent): void {\n // Check if we should start panning (threshold check for potential pan)\n if (this.isPotentialPan && !this.isPanning && this.canvasEl && this.zoomState.level > 1) {\n const deltaX = Math.abs(e.clientX - this.panStartX);\n const PAN_THRESHOLD = 3; // pixels before we consider it a drag vs click\n\n if (deltaX > PAN_THRESHOLD) {\n this.isPanning = true;\n this.canvasEl.style.cursor = 'grabbing';\n }\n }\n\n // Handle canvas dragging (scrolls the table)\n if (this.isPanning && this.canvasEl && this.minimapEl && this.scrollContainer) {\n e.preventDefault();\n\n const deltaX = e.clientX - this.panStartX;\n const minimapWidth = this.minimapEl.offsetWidth;\n const { scrollWidth, clientWidth } = this.scrollContainer;\n const maxScroll = scrollWidth - clientWidth;\n\n // Convert minimap movement to scroll movement\n // When zoomed, movement on minimap represents more scroll distance\n const scrollDelta = (deltaX / minimapWidth) * maxScroll * this.zoomState.level;\n const newScrollLeft = this.dragStartScrollLeft + scrollDelta;\n\n // Apply scroll\n this.scrollContainer.scrollLeft = Math.max(0, Math.min(maxScroll, newScrollLeft));\n\n // Update state and re-render\n this.updateScrollState();\n this.updateViewport();\n this.render();\n return;\n }\n\n\n // Handle viewport dragging\n if (!this.isDragging || !this.minimapEl || !this.scrollContainer) return;\n\n e.preventDefault();\n\n const { scrollWidth, clientWidth } = this.scrollContainer;\n const minimapWidth = this.minimapEl.offsetWidth;\n const maxScroll = scrollWidth - clientWidth;\n\n // Scroll based on drag delta\n const deltaX = e.clientX - this.dragStartX;\n const scrollDelta = (deltaX / minimapWidth) * maxScroll;\n const newScrollLeft = this.dragStartScrollLeft + scrollDelta;\n\n this.scrollContainer.scrollLeft = Math.max(0, Math.min(maxScroll, newScrollLeft));\n this.updateScrollState();\n this.updateViewport();\n }\n\n /**\n * Handles pointer up to end drag\n *\n * @param e - Pointer event\n */\n private onPointerUp(e: PointerEvent): void {\n // Handle potential pan that was actually a click\n if (this.isPotentialPan && !this.isPanning && this.canvasEl && this.minimapEl) {\n this.isPotentialPan = false;\n\n if (this.canvasEl.hasPointerCapture(e.pointerId)) {\n this.canvasEl.releasePointerCapture(e.pointerId);\n }\n\n // Simulate a click on the minimap at this position\n const clickEvent = new MouseEvent('click', {\n clientX: e.clientX,\n clientY: e.clientY,\n bubbles: true,\n });\n this.minimapEl.dispatchEvent(clickEvent);\n return;\n }\n\n this.isPotentialPan = false;\n\n // End canvas panning\n if (this.isPanning && this.canvasEl) {\n this.isPanning = false;\n this.wasPanning = true;\n\n if (this.canvasEl.hasPointerCapture(e.pointerId)) {\n this.canvasEl.releasePointerCapture(e.pointerId);\n }\n\n this.canvasEl.style.cursor = this.zoomState.level > 1 ? 'grab' : 'pointer';\n\n // Reset wasPanning after a short delay to allow click event to be ignored\n setTimeout(() => {\n this.wasPanning = false;\n }, 100);\n }\n\n // End viewport dragging\n if (this.isDragging) {\n this.isDragging = false;\n\n if (this.viewportEl) {\n this.viewportEl.classList.remove('tm-viewport--dragging');\n this.viewportEl.releasePointerCapture(e.pointerId);\n }\n }\n }\n\n /**\n * Handles wheel events for zoom\n *\n * @param e - Wheel event\n */\n private onWheel(e: WheelEvent): void {\n if (!this.options.zoomable || this.options.mode !== 'canvas') return;\n if (!this.canvasEl || !this.scrollContainer || !this.minimapEl) return;\n\n e.preventDefault();\n\n const oldZoom = this.zoomState.level;\n const delta = -e.deltaY * this.options.zoomSpeed;\n const newZoom = Math.max(\n this.options.minZoom,\n Math.min(this.options.maxZoom, oldZoom + delta)\n );\n\n if (newZoom === oldZoom) return;\n\n // Get mouse position relative to canvas\n const rect = this.canvasEl.getBoundingClientRect();\n const mouseX = e.clientX - rect.left;\n const width = this.minimapEl.offsetWidth;\n const relativeX = mouseX / width; // 0-1 position in canvas\n\n // Calculate the table position (0-1) under the mouse BEFORE zoom\n const oldVisibleRatio = 1 / oldZoom;\n const { scrollLeft, scrollWidth, clientWidth } = this.scrollContainer;\n const maxScroll = Math.max(scrollWidth - clientWidth, 1);\n const oldScrollRatio = scrollLeft / maxScroll;\n const oldPanX = oldZoom > 1 ? oldScrollRatio * (1 - oldVisibleRatio) : 0;\n const tableX = oldPanX + relativeX * oldVisibleRatio; // Position in table (0-1)\n\n // Update zoom level\n this.zoomState = {\n level: newZoom,\n panX: 0,\n isMinZoom: newZoom <= this.options.minZoom,\n isMaxZoom: newZoom >= this.options.maxZoom,\n };\n\n // Calculate new scroll position to keep tableX under the mouse\n const newVisibleRatio = 1 / newZoom;\n\n if (newZoom > 1) {\n // newPanX + relativeX * newVisibleRatio = tableX\n // newPanX = tableX - relativeX * newVisibleRatio\n // newScrollRatio * (1 - newVisibleRatio) = newPanX\n // newScrollRatio = newPanX / (1 - newVisibleRatio)\n const newPanX = tableX - relativeX * newVisibleRatio;\n const newScrollRatio = newPanX / (1 - newVisibleRatio);\n const newScrollLeft = Math.max(0, Math.min(maxScroll, newScrollRatio * maxScroll));\n\n this.scrollContainer.scrollLeft = newScrollLeft;\n }\n\n this.updateScrollState();\n this.render();\n }\n\n /** Whether we started a potential pan (waiting to see if it's a click or drag) */\n private isPotentialPan = false;\n\n /**\n * Handles pointer down on canvas for drag start (scrolls table when zoomed)\n *\n * @param e - Pointer event\n */\n private onCanvasPointerDown(e: PointerEvent): void {\n if (!this.canvasEl || !this.scrollContainer) return;\n\n // At any zoom level, track potential pan/click\n this.isPotentialPan = true;\n this.panStartX = e.clientX;\n this.dragStartScrollLeft = this.scrollContainer.scrollLeft;\n\n // Only capture for panning at zoom > 1\n if (this.zoomState.level > 1) {\n e.preventDefault();\n this.canvasEl.setPointerCapture(e.pointerId);\n }\n }\n\n /**\n * Handles mouse move on canvas for column hover highlighting\n */\n private onCanvasMouseMove(e: MouseEvent): void {\n if (!this.canvasEl || this.isPanning) return;\n\n const rect = this.canvasEl.getBoundingClientRect();\n const newHoveredColumn = this.getColumnAtX(e.clientX - rect.left);\n\n if (newHoveredColumn !== this.hoveredColumn) {\n this.hoveredColumn = newHoveredColumn;\n this.canvasEl.style.cursor = newHoveredColumn >= 0 ? 'pointer' : (this.zoomState.level > 1 ? 'grab' : 'default');\n this.render();\n }\n }\n\n /**\n * Handles mouse leave on canvas to clear hover state\n */\n private onCanvasMouseLeave(): void {\n if (this.hoveredColumn !== -1) {\n this.hoveredColumn = -1;\n if (this.canvasEl) {\n this.canvasEl.style.cursor = this.zoomState.level > 1 ? 'grab' : 'default';\n }\n this.render();\n }\n }\n\n /**\n * Expands the compact minimap and clears any pending collapse.\n */\n private expandCompact(): void {\n if (!this.isCompactMode || !this.minimapEl || this.isCompactExpanding) return;\n\n this.clearCompactCollapseTimer();\n this.isCompactExpanding = true;\n this.applyCompactDimensions(false);\n \n // Wait for CSS transition to complete before rendering\n // This ensures correct dimensions are used for viewport calculation\n setTimeout(() => {\n this.isCompactExpanding = false;\n if (!this.isDestroyed && !this.isCompactCollapsed) {\n this.updateScrollState();\n this.render();\n }\n }, 200); // Slightly longer than transition duration (180ms)\n }\n\n /**\n * Collapses the compact minimap to the small dot handle.\n */\n private collapseCompact(): void {\n if (!this.isCompactMode || !this.minimapEl) return;\n\n this.applyCompactDimensions(true);\n }\n\n /**\n * Applies compact sizing/state to the minimap.\n */\n private applyCompactDimensions(collapsed: boolean): void {\n if (!this.minimapEl || !this.isCompactMode) return;\n\n this.isCompactCollapsed = collapsed;\n\n if (collapsed) {\n this.minimapEl.classList.add('tm-minimap--compact-collapsed');\n this.minimapEl.classList.remove('tm-minimap--compact-expanded');\n this.minimapEl.style.setProperty('--tm-minimap-width', `${COMPACT_HANDLE_SIZE}px`);\n this.minimapEl.style.setProperty('--tm-minimap-height', `${COMPACT_HANDLE_SIZE}px`);\n this.minimapEl.setAttribute('aria-expanded', 'false');\n return;\n }\n\n this.minimapEl.classList.remove('tm-minimap--compact-collapsed');\n this.minimapEl.classList.add('tm-minimap--compact-expanded');\n this.minimapEl.style.setProperty('--tm-minimap-width', `${this.options.fixedWidth}px`);\n this.minimapEl.style.setProperty('--tm-minimap-height', `${this.options.height}px`);\n this.minimapEl.setAttribute('aria-expanded', 'true');\n }\n\n /**\n * Clears a pending compact collapse timer.\n */\n private clearCompactCollapseTimer(): void {\n if (this.compactCollapseTimer === null) return;\n\n clearTimeout(this.compactCollapseTimer);\n this.compactCollapseTimer = null;\n }\n\n /**\n * Schedules the compact minimap to collapse.\n */\n private scheduleCompactCollapse(delay = COMPACT_COLLAPSE_DELAY): void {\n if (!this.isCompactMode) return;\n\n this.clearCompactCollapseTimer();\n this.compactCollapseTimer = window.setTimeout(() => {\n this.collapseCompact();\n this.compactCollapseTimer = null;\n }, delay);\n }\n\n /**\n * Handles document click for closing compact mode when clicking outside.\n */\n private onDocumentClick(e: MouseEvent): void {\n if (!this.isCompactMode || !this.minimapEl || this.isCompactCollapsed) return;\n\n // Check if click is outside the minimap\n if (!this.minimapEl.contains(e.target as Node)) {\n this.collapseCompact();\n }\n }\n\n /**\n * Handles focus entering compact mode.\n */\n private onCompactFocusIn(): void {\n if (!this.isCompactMode) return;\n\n this.expandCompact();\n }\n\n /**\n * Handles focus leaving compact mode.\n */\n private onCompactFocusOut(): void {\n if (!this.isCompactMode) return;\n\n this.scheduleCompactCollapse(0);\n }\n\n /**\n * Keyboard interactions for compact mode.\n */\n private onCompactKeyDown(e: KeyboardEvent): void {\n if (!this.isCompactMode) return;\n\n if (e.key === 'Escape') {\n e.preventDefault();\n this.collapseCompact();\n return;\n }\n\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n if (this.isCompactCollapsed) {\n this.expandCompact();\n } else {\n this.collapseCompact();\n }\n }\n }\n\n /**\n * Sets up ResizeObserver and MutationObserver\n */\n private setupObservers(): void {\n // ResizeObserver for responsive updates\n this.resizeObserver = new ResizeObserver(() => {\n this.onResize();\n });\n\n if (this.scrollContainer) {\n this.resizeObserver.observe(this.scrollContainer);\n }\n this.resizeObserver.observe(this.table);\n\n // MutationObserver for table structure changes\n this.mutationObserver = new MutationObserver((mutations) => {\n const hasStructuralChanges = mutations.some(\n (m) => m.type === 'childList' || m.attributeName === 'colspan'\n );\n\n if (hasStructuralChanges) {\n this.onTableMutation();\n }\n });\n\n this.mutationObserver.observe(this.table, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: ['colspan'],\n });\n }\n\n /**\n * Handles resize events\n */\n private onResize(): void {\n if (this.isDestroyed) return;\n\n // Debounce resize handling\n if (this.rafId !== null) {\n cancelAnimationFrame(this.rafId);\n }\n\n this.rafId = requestAnimationFrame(() => {\n this.detectColumns();\n this.updateScrollState();\n\n this.render();\n\n // Rebuild columns if in columns mode\n if (this.options.mode === 'columns' && this.columnsEl && this.minimapEl) {\n this.columnsEl.innerHTML = '';\n this.columns.forEach(() => {\n const colEl = document.createElement('div');\n colEl.className = 'tm-column';\n this.columnsEl!.appendChild(colEl);\n });\n }\n\n this.rafId = null;\n });\n }\n\n /**\n * Handles table mutation events\n */\n private onTableMutation(): void {\n if (this.isDestroyed) return;\n\n this.detectColumns();\n this.updateScrollState();\n\n this.render();\n\n // Rebuild columns if in columns mode\n if (this.options.mode === 'columns' && this.columnsEl) {\n this.columnsEl.innerHTML = '';\n this.columns.forEach(() => {\n const colEl = document.createElement('div');\n colEl.className = 'tm-column';\n this.columnsEl!.appendChild(colEl);\n });\n }\n }\n\n /**\n * Gets the current scroll state\n *\n * @returns Current scroll state\n */\n public getScrollState(): ScrollState {\n return { ...this.scrollState };\n }\n\n /**\n * Gets the detected columns\n *\n * @returns Array of column information\n */\n public getColumns(): ColumnInfo[] {\n return [...this.columns];\n }\n\n /**\n * Scrolls to a specific column\n *\n * @param columnIndex - Zero-based column index\n * @param smooth - Use smooth scrolling\n */\n public scrollToColumn(columnIndex: number, smooth = true): void {\n if (!this.scrollContainer || columnIndex < 0 || columnIndex >= this.columns.length) {\n return;\n }\n\n const cellsBefore = this.columns.slice(0, columnIndex);\n const offsetLeft = cellsBefore.reduce((sum, col) => sum + col.width, 0);\n\n this.scrollContainer.scrollTo({\n left: offsetLeft,\n behavior: smooth ? 'smooth' : 'auto',\n });\n }\n\n /**\n * Forces a refresh of the minimap\n */\n public refresh(): void {\n if (this.isDestroyed) return;\n\n this.scrollContainer = this.findScrollContainer();\n this.detectColumns();\n this.updateScrollState();\n\n this.render();\n\n if (this.options.mode === 'columns' && this.columnsEl) {\n this.columnsEl.innerHTML = '';\n this.columns.forEach(() => {\n const colEl = document.createElement('div');\n colEl.className = 'tm-column';\n this.columnsEl!.appendChild(colEl);\n });\n }\n }\n\n /**\n * Gets the current zoom state (canvas mode only)\n *\n * @returns Current zoom state\n */\n public getZoomState(): ZoomState {\n return { ...this.zoomState };\n }\n\n /**\n * Sets the zoom level programmatically (canvas mode only)\n *\n * @param level - Zoom level (1 = no zoom)\n * @param panX - Optional pan position (0-1), controls which part of table is visible\n */\n public setZoom(level: number, panX?: number): void {\n if (this.isDestroyed || this.options.mode !== 'canvas' || !this.scrollContainer) return;\n\n const newZoom = Math.max(\n this.options.minZoom,\n Math.min(this.options.maxZoom, level)\n );\n\n const visibleRange = 1 / newZoom;\n const maxPanX = 1 - visibleRange;\n\n let newPanX = panX !== undefined ? panX : 0;\n newPanX = Math.max(0, Math.min(maxPanX, newPanX));\n\n this.zoomState = {\n level: newZoom,\n panX: 0, // Not used - panX is derived from scroll position\n isMinZoom: newZoom <= this.options.minZoom,\n isMaxZoom: newZoom >= this.options.maxZoom,\n };\n\n // Set scroll position to match the desired panX\n if (newZoom > 1 && newPanX > 0) {\n const { scrollWidth, clientWidth } = this.scrollContainer;\n const maxScroll = Math.max(scrollWidth - clientWidth, 1);\n // panX = scrollRatio * (1 - visibleRatio)\n // scrollRatio = panX / (1 - visibleRatio)\n const scrollRatio = newPanX / (1 - visibleRange);\n this.scrollContainer.scrollLeft = Math.max(0, Math.min(maxScroll, scrollRatio * maxScroll));\n } else if (newZoom <= 1) {\n this.scrollContainer.scrollLeft = 0;\n }\n\n this.updateScrollState();\n this.render();\n }\n\n /**\n * Resets zoom to default (shows full table overview)\n */\n public resetZoom(): void {\n this.setZoom(1, 0);\n }\n\n /**\n * Zooms to a specific column range (canvas mode only)\n *\n * @param startCol - Start column index\n * @param endCol - End column index\n */\n public zoomToColumns(startCol: number, endCol: number): void {\n if (this.isDestroyed || this.options.mode !== 'canvas') return;\n\n const numCols = this.columns.length;\n if (numCols === 0) return;\n\n // Clamp column indices\n const start = Math.max(0, Math.min(numCols - 1, startCol));\n const end = Math.max(start + 1, Math.min(numCols, endCol));\n\n const colRange = end - start;\n const zoom = numCols / colRange;\n const panX = start / numCols;\n\n this.setZoom(zoom, panX);\n }\n\n /**\n * Destroys the minimap instance and cleans up resources\n */\n public destroy(): void {\n if (this.isDestroyed) return;\n this.isDestroyed = true;\n\n this.clearCompactCollapseTimer();\n\n // Cancel any pending animation frame\n if (this.rafId !== null) {\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n\n // Remove event listeners\n if (this.scrollContainer) {\n this.scrollContainer.removeEventListener('scroll', this.boundHandlers.onScroll);\n }\n\n if (this.minimapEl) {\n this.minimapEl.removeEventListener('click', this.boundHandlers.onMinimapClick);\n this.minimapEl.removeEventListener('focusin', this.boundHandlers.onCompactFocusIn);\n this.minimapEl.removeEventListener('focusout', this.boundHandlers.onCompactFocusOut);\n this.minimapEl.removeEventListener('keydown', this.boundHandlers.onCompactKeyDown);\n }\n\n document.removeEventListener('click', this.boundHandlers.onDocumentClick);\n\n if (this.viewportEl) {\n this.viewportEl.removeEventListener('pointerdown', this.boundHandlers.onPointerDown);\n this.viewportEl.removeEventListener('wheel', this.boundHandlers.onWheel);\n }\n\n if (this.canvasEl) {\n this.canvasEl.removeEventListener('wheel', this.boundHandlers.onWheel);\n this.canvasEl.removeEventListener('pointerdown', this.boundHandlers.onCanvasPointerDown);\n this.canvasEl.removeEventListener('mousemove', this.boundHandlers.onCanvasMouseMove);\n this.canvasEl.removeEventListener('mouseleave', this.boundHandlers.onCanvasMouseLeave);\n }\n\n document.removeEventListener('pointermove', this.boundHandlers.onPointerMove);\n document.removeEventListener('pointerup', this.boundHandlers.onPointerUp);\n\n // Disconnect observers\n if (this.resizeObserver) {\n this.resizeObserver.disconnect();\n this.resizeObserver = null;\n }\n\n if (this.mutationObserver) {\n this.mutationObserver.disconnect();\n this.mutationObserver = null;\n }\n\n // Remove DOM elements\n if (this.minimapEl && this.minimapEl.parentNode) {\n this.minimapEl.parentNode.removeChild(this.minimapEl);\n }\n\n // Clear references\n this.minimapEl = null;\n this.columnsEl = null;\n this.canvasEl = null;\n this.canvasCtx = null;\n this.viewportEl = null;\n this.scrollContainer = null;\n this.columns = [];\n }\n}\n","/**\n * Table Minimap - A framework-agnostic minimap component for large HTML tables\n *\n * @packageDocumentation\n */\n\nexport { TableMinimap } from './TableMinimap';\nexport type {\n TableMinimapOptions,\n ColumnInfo,\n ScrollState,\n CellData,\n TableSelector,\n ZoomState,\n} from './types';\n\n// Auto-inject styles into DOM\nimport styles from './styles.css?inline';\n\nlet stylesInjected = false;\n\nexport function injectStyles(): void {\n if (stylesInjected || typeof document === 'undefined') return;\n\n const style = document.createElement('style');\n style.id = 'table-minimap-styles';\n style.textContent = styles;\n document.head.appendChild(style);\n stylesInjected = true;\n}\n\n// Auto-inject on import (can be tree-shaken if not used)\ninjectStyles();\n\n"],"names":["DEFAULT_OPTIONS","COMPACT_HANDLE_SIZE","COMPACT_DOT_SIZE","COMPACT_COLLAPSE_DELAY","TableMinimap","selector","options","__publicField","element","parent","style","overflowX","overflowY","theadCells","cells","firstRow","tableWidth","cell","colspan","cellWidth","i","width","colEl","nextSibling","offset","pos","scrollLeft","scrollWidth","clientWidth","_a","zoom","numCols","visibleRatio","visibleCols","panX","maxScroll","startColFloat","startCol","endCol","xOffset","mouseX","relativeX","tableX","colIndex","minimapWidth","viewportWidth","viewportLeft","metrics","height","dpr","ctx","rows","numRows","headerHeight","cellHeight","fontSize","headerFontSize","colors","headerRow","headerCells","col","x","text","_b","rowIndex","row","y","content","_d","_c","hoverX","e","rect","clickX","clickedColumn","colWidth","targetScroll","deltaX","scrollDelta","newScrollLeft","clickEvent","oldZoom","delta","newZoom","oldVisibleRatio","oldScrollRatio","newVisibleRatio","newScrollRatio","newHoveredColumn","collapsed","delay","mutations","m","columnIndex","smooth","offsetLeft","sum","level","visibleRange","maxPanX","newPanX","scrollRatio","start","colRange","stylesInjected","injectStyles","styles"],"mappings":"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"}
|