table-minimap 1.1.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/shadcn.css +14 -1
- package/dist/table-minimap.cjs +1 -1
- package/dist/table-minimap.cjs.map +1 -1
- package/dist/table-minimap.js +42 -42
- package/package.json +1 -1
package/dist/shadcn.css
CHANGED
|
@@ -58,6 +58,19 @@
|
|
|
58
58
|
margin-top: 8px;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
.tm-minimap--fixed {
|
|
62
|
+
background: color-mix(in srgb, var(--tm-background) 72%, transparent);
|
|
63
|
+
border-color: color-mix(in srgb, var(--tm-border) 72%, transparent);
|
|
64
|
+
opacity: 0.78;
|
|
65
|
+
backdrop-filter: blur(2px);
|
|
66
|
+
-webkit-backdrop-filter: blur(2px);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.tm-minimap--fixed:hover,
|
|
70
|
+
.tm-minimap--fixed:focus-within {
|
|
71
|
+
opacity: 0.96;
|
|
72
|
+
}
|
|
73
|
+
|
|
61
74
|
.tm-minimap--compact {
|
|
62
75
|
border-radius: 999px;
|
|
63
76
|
}
|
|
@@ -70,7 +83,7 @@
|
|
|
70
83
|
background: color-mix(in srgb, var(--tm-background) 60%, transparent);
|
|
71
84
|
border-color: color-mix(in srgb, var(--tm-border) 45%, transparent);
|
|
72
85
|
box-shadow: 0 0 0 1px color-mix(in srgb, var(--tm-border) 20%, transparent);
|
|
73
|
-
opacity: 0.
|
|
86
|
+
opacity: 0.72;
|
|
74
87
|
transform: translate(2px, 2px);
|
|
75
88
|
}
|
|
76
89
|
|
package/dist/table-minimap.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
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;
|
|
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 r=0;r<a;r++){const l=s/a;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.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 r=0;if(e>1&&this.scrollContainer){const{scrollLeft:m,scrollWidth:E,clientWidth:g}=this.scrollContainer,u=Math.max(E-g,1);r=m/u*(1-o)}const l=r*i,c=Math.floor(l),h=Math.min(Math.ceil(l+a)+1,i),f=-(l-c)*s;return{width:t,zoom:e,numCols:i,visibleRatio:o,visibleCols:a,cellWidth:s,panX:r,startColFloat:l,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,r=o+s*a,l=Math.floor(r*i);return Math.max(0,Math.min(i-1,l))}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,k;if(!this.canvasCtx)return;const i=this.canvasCtx,{width:o,numCols:a,cellWidth:s,startCol:r,endCol:l,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=r;d<l;d++){const b=c+(d-r)*s;if(b+s<0||b>o)continue;i.strokeStyle=u.border,i.lineWidth=1,i.strokeRect(b,0,s,p);const C=((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(b+2,0,s-4,p),i.clip(),i.fillText(C,b+4,p/2),i.restore()}i.font=`${E}px system-ui, sans-serif`;for(let d=0;d<h.length;d++){const b=h[d];if(b.closest("thead"))continue;const C=p+d*m;if(C+m<0||C>e)continue;d%2===1&&(i.fillStyle=u.altRow,i.fillRect(0,C,o,m));const X=Array.from(b.querySelectorAll("th, td"));for(let M=r;M<l;M++){const w=c+(M-r)*s;if(w+s<0||w>o)continue;i.strokeStyle=u.border,i.lineWidth=.5,i.strokeRect(w,C,s,m);const D=(k=(P=X[M])==null?void 0:P.textContent)==null?void 0:k.trim();D&&(i.fillStyle=u.text,i.save(),i.beginPath(),i.rect(w+2,C,s-4,m),i.clip(),i.fillText(D,w+4,C+m/2),i.restore())}}if(this.hoveredColumn>=r&&this.hoveredColumn<l){const d=c+(this.hoveredColumn-r)*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 l=s/a.width*o;this.scrollContainer.scrollTo({left:Math.max(0,Math.min(o,l)),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,r=(t.clientX-this.dragStartX)/o*a,l=this.dragStartScrollLeft+r;this.scrollContainer.scrollLeft=Math.max(0,Math.min(a,l)),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,r=this.minimapEl.offsetWidth,l=s/r,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)+l*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-l*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:r,clientWidth:l}=this.scrollContainer,c=Math.max(r-l,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,r=i/s,l=o/i;this.setZoom(r,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;background:color-mix(in srgb,var(--tm-background) 72%,transparent);border-color:color-mix(in srgb,var(--tm-border) 72%,transparent);box-shadow:0 4px 12px #00000026;border-radius:8px;opacity:.78;backdrop-filter:blur(2px);-webkit-backdrop-filter:blur(2px)}.tm-minimap--fixed:hover,.tm-minimap--fixed:focus-within{opacity:.96}.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:.72;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 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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"}
|
|
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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"}
|
package/dist/table-minimap.js
CHANGED
|
@@ -172,12 +172,12 @@ class F {
|
|
|
172
172
|
const i = this.table.offsetWidth || 1;
|
|
173
173
|
Array.from(e).forEach((o) => {
|
|
174
174
|
const a = o.colSpan || 1, s = o.offsetWidth;
|
|
175
|
-
for (let
|
|
176
|
-
const
|
|
175
|
+
for (let r = 0; r < a; r++) {
|
|
176
|
+
const l = s / a;
|
|
177
177
|
this.columns.push({
|
|
178
178
|
index: this.columns.length,
|
|
179
|
-
width:
|
|
180
|
-
widthPercent:
|
|
179
|
+
width: l,
|
|
180
|
+
widthPercent: l / i * 100
|
|
181
181
|
});
|
|
182
182
|
}
|
|
183
183
|
}), this.columns.length === 0 && this.columns.push({
|
|
@@ -264,12 +264,12 @@ class F {
|
|
|
264
264
|
getCanvasMetrics() {
|
|
265
265
|
var p;
|
|
266
266
|
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;
|
|
267
|
-
let
|
|
267
|
+
let r = 0;
|
|
268
268
|
if (e > 1 && this.scrollContainer) {
|
|
269
269
|
const { scrollLeft: m, scrollWidth: E, clientWidth: g } = this.scrollContainer, u = Math.max(E - g, 1);
|
|
270
|
-
|
|
270
|
+
r = m / u * (1 - o);
|
|
271
271
|
}
|
|
272
|
-
const
|
|
272
|
+
const l = r * i, c = Math.floor(l), h = Math.min(Math.ceil(l + a) + 1, i), f = -(l - c) * s;
|
|
273
273
|
return {
|
|
274
274
|
width: t,
|
|
275
275
|
zoom: e,
|
|
@@ -277,8 +277,8 @@ class F {
|
|
|
277
277
|
visibleRatio: o,
|
|
278
278
|
visibleCols: a,
|
|
279
279
|
cellWidth: s,
|
|
280
|
-
panX:
|
|
281
|
-
startColFloat:
|
|
280
|
+
panX: r,
|
|
281
|
+
startColFloat: l,
|
|
282
282
|
startCol: c,
|
|
283
283
|
endCol: h,
|
|
284
284
|
xOffset: f
|
|
@@ -290,8 +290,8 @@ class F {
|
|
|
290
290
|
getColumnAtX(t) {
|
|
291
291
|
const { width: e, numCols: i, panX: o, visibleRatio: a } = this.getCanvasMetrics();
|
|
292
292
|
if (i === 0 || e === 0) return -1;
|
|
293
|
-
const s = t / e,
|
|
294
|
-
return Math.max(0, Math.min(i - 1,
|
|
293
|
+
const s = t / e, r = o + s * a, l = Math.floor(r * i);
|
|
294
|
+
return Math.max(0, Math.min(i - 1, l));
|
|
295
295
|
}
|
|
296
296
|
/**
|
|
297
297
|
* Updates the viewport indicator position and size
|
|
@@ -318,9 +318,9 @@ class F {
|
|
|
318
318
|
* Renders the visible portion of the table directly onto the canvas
|
|
319
319
|
*/
|
|
320
320
|
renderTableDirect(t, e) {
|
|
321
|
-
var S, y, P,
|
|
321
|
+
var S, y, P, k;
|
|
322
322
|
if (!this.canvasCtx) return;
|
|
323
|
-
const i = this.canvasCtx, { width: o, numCols: a, cellWidth: s, startCol:
|
|
323
|
+
const i = this.canvasCtx, { width: o, numCols: a, cellWidth: s, startCol: r, endCol: l, xOffset: c } = t, h = Array.from(this.table.querySelectorAll("tr")), f = h.length;
|
|
324
324
|
if (f === 0 || a === 0) return;
|
|
325
325
|
const p = Math.min(e * 0.15, 30), m = (e - p) / f, E = Math.min(m * 0.6, s * 0.15, 14), g = Math.min(p * 0.6, s * 0.15, 14), u = {
|
|
326
326
|
bg: "#ffffff",
|
|
@@ -335,31 +335,31 @@ class F {
|
|
|
335
335
|
i.fillStyle = u.bg, i.fillRect(0, 0, o, e), i.fillStyle = u.headerBg, i.fillRect(0, 0, o, p);
|
|
336
336
|
const x = this.table.querySelector("thead tr") || h[0], L = x ? Array.from(x.querySelectorAll("th, td")) : [];
|
|
337
337
|
i.font = `bold ${g}px system-ui, sans-serif`, i.textBaseline = "middle";
|
|
338
|
-
for (let d =
|
|
339
|
-
const
|
|
340
|
-
if (
|
|
341
|
-
i.strokeStyle = u.border, i.lineWidth = 1, i.strokeRect(
|
|
342
|
-
const
|
|
343
|
-
i.fillStyle = u.headerText, i.save(), i.beginPath(), i.rect(
|
|
338
|
+
for (let d = r; d < l; d++) {
|
|
339
|
+
const b = c + (d - r) * s;
|
|
340
|
+
if (b + s < 0 || b > o) continue;
|
|
341
|
+
i.strokeStyle = u.border, i.lineWidth = 1, i.strokeRect(b, 0, s, p);
|
|
342
|
+
const C = ((y = (S = L[d]) == null ? void 0 : S.textContent) == null ? void 0 : y.trim()) || `Col ${d + 1}`;
|
|
343
|
+
i.fillStyle = u.headerText, i.save(), i.beginPath(), i.rect(b + 2, 0, s - 4, p), i.clip(), i.fillText(C, b + 4, p / 2), i.restore();
|
|
344
344
|
}
|
|
345
345
|
i.font = `${E}px system-ui, sans-serif`;
|
|
346
346
|
for (let d = 0; d < h.length; d++) {
|
|
347
|
-
const
|
|
348
|
-
if (
|
|
349
|
-
const
|
|
350
|
-
if (
|
|
351
|
-
d % 2 === 1 && (i.fillStyle = u.altRow, i.fillRect(0,
|
|
352
|
-
const R = Array.from(
|
|
353
|
-
for (let M =
|
|
354
|
-
const w = c + (M -
|
|
347
|
+
const b = h[d];
|
|
348
|
+
if (b.closest("thead")) continue;
|
|
349
|
+
const C = p + d * m;
|
|
350
|
+
if (C + m < 0 || C > e) continue;
|
|
351
|
+
d % 2 === 1 && (i.fillStyle = u.altRow, i.fillRect(0, C, o, m));
|
|
352
|
+
const R = Array.from(b.querySelectorAll("th, td"));
|
|
353
|
+
for (let M = r; M < l; M++) {
|
|
354
|
+
const w = c + (M - r) * s;
|
|
355
355
|
if (w + s < 0 || w > o) continue;
|
|
356
|
-
i.strokeStyle = u.border, i.lineWidth = 0.5, i.strokeRect(w,
|
|
357
|
-
const
|
|
358
|
-
|
|
356
|
+
i.strokeStyle = u.border, i.lineWidth = 0.5, i.strokeRect(w, C, s, m);
|
|
357
|
+
const D = (k = (P = R[M]) == null ? void 0 : P.textContent) == null ? void 0 : k.trim();
|
|
358
|
+
D && (i.fillStyle = u.text, i.save(), i.beginPath(), i.rect(w + 2, C, s - 4, m), i.clip(), i.fillText(D, w + 4, C + m / 2), i.restore());
|
|
359
359
|
}
|
|
360
360
|
}
|
|
361
|
-
if (this.hoveredColumn >=
|
|
362
|
-
const d = c + (this.hoveredColumn -
|
|
361
|
+
if (this.hoveredColumn >= r && this.hoveredColumn < l) {
|
|
362
|
+
const d = c + (this.hoveredColumn - r) * s;
|
|
363
363
|
i.fillStyle = u.hoverFill, i.fillRect(d, 0, s, e), i.strokeStyle = u.hoverStroke, i.lineWidth = 1, i.strokeRect(d, 0, s, e);
|
|
364
364
|
}
|
|
365
365
|
}
|
|
@@ -403,9 +403,9 @@ class F {
|
|
|
403
403
|
}
|
|
404
404
|
return;
|
|
405
405
|
}
|
|
406
|
-
const
|
|
406
|
+
const l = s / a.width * o;
|
|
407
407
|
this.scrollContainer.scrollTo({
|
|
408
|
-
left: Math.max(0, Math.min(o,
|
|
408
|
+
left: Math.max(0, Math.min(o, l)),
|
|
409
409
|
behavior: "smooth"
|
|
410
410
|
});
|
|
411
411
|
}
|
|
@@ -431,8 +431,8 @@ class F {
|
|
|
431
431
|
}
|
|
432
432
|
if (!this.isDragging || !this.minimapEl || !this.scrollContainer) return;
|
|
433
433
|
t.preventDefault();
|
|
434
|
-
const { scrollWidth: e, clientWidth: i } = this.scrollContainer, o = this.minimapEl.offsetWidth, a = e - i,
|
|
435
|
-
this.scrollContainer.scrollLeft = Math.max(0, Math.min(a,
|
|
434
|
+
const { scrollWidth: e, clientWidth: i } = this.scrollContainer, o = this.minimapEl.offsetWidth, a = e - i, r = (t.clientX - this.dragStartX) / o * a, l = this.dragStartScrollLeft + r;
|
|
435
|
+
this.scrollContainer.scrollLeft = Math.max(0, Math.min(a, l)), this.updateScrollState(), this.updateViewport();
|
|
436
436
|
}
|
|
437
437
|
/**
|
|
438
438
|
* Handles pointer up to end drag
|
|
@@ -467,7 +467,7 @@ class F {
|
|
|
467
467
|
Math.min(this.options.maxZoom, e + i)
|
|
468
468
|
);
|
|
469
469
|
if (o === e) return;
|
|
470
|
-
const a = this.canvasEl.getBoundingClientRect(), s = t.clientX - a.left,
|
|
470
|
+
const a = this.canvasEl.getBoundingClientRect(), s = t.clientX - a.left, r = this.minimapEl.offsetWidth, l = s / r, 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) + l * c;
|
|
471
471
|
this.zoomState = {
|
|
472
472
|
level: o,
|
|
473
473
|
panX: 0,
|
|
@@ -476,7 +476,7 @@ class F {
|
|
|
476
476
|
};
|
|
477
477
|
const x = 1 / o;
|
|
478
478
|
if (o > 1) {
|
|
479
|
-
const S = (u -
|
|
479
|
+
const S = (u - l * x) / (1 - x), y = Math.max(0, Math.min(m, S * m));
|
|
480
480
|
this.scrollContainer.scrollLeft = y;
|
|
481
481
|
}
|
|
482
482
|
this.updateScrollState(), this.render();
|
|
@@ -678,7 +678,7 @@ class F {
|
|
|
678
678
|
isMinZoom: i <= this.options.minZoom,
|
|
679
679
|
isMaxZoom: i >= this.options.maxZoom
|
|
680
680
|
}, i > 1 && s > 0) {
|
|
681
|
-
const { scrollWidth:
|
|
681
|
+
const { scrollWidth: r, clientWidth: l } = this.scrollContainer, c = Math.max(r - l, 1), h = s / (1 - o);
|
|
682
682
|
this.scrollContainer.scrollLeft = Math.max(0, Math.min(c, h * c));
|
|
683
683
|
} else i <= 1 && (this.scrollContainer.scrollLeft = 0);
|
|
684
684
|
this.updateScrollState(), this.render();
|
|
@@ -699,8 +699,8 @@ class F {
|
|
|
699
699
|
if (this.isDestroyed || this.options.mode !== "canvas") return;
|
|
700
700
|
const i = this.columns.length;
|
|
701
701
|
if (i === 0) return;
|
|
702
|
-
const o = Math.max(0, Math.min(i - 1, t)), s = Math.max(o + 1, Math.min(i, e)) - o,
|
|
703
|
-
this.setZoom(
|
|
702
|
+
const o = Math.max(0, Math.min(i - 1, t)), s = Math.max(o + 1, Math.min(i, e)) - o, r = i / s, l = o / i;
|
|
703
|
+
this.setZoom(r, l);
|
|
704
704
|
}
|
|
705
705
|
/**
|
|
706
706
|
* Destroys the minimap instance and cleans up resources
|
|
@@ -709,7 +709,7 @@ class F {
|
|
|
709
709
|
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 = []);
|
|
710
710
|
}
|
|
711
711
|
}
|
|
712
|
-
const I = ':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:.
|
|
712
|
+
const I = ':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;background:color-mix(in srgb,var(--tm-background) 72%,transparent);border-color:color-mix(in srgb,var(--tm-border) 72%,transparent);box-shadow:0 4px 12px #00000026;border-radius:8px;opacity:.78;backdrop-filter:blur(2px);-webkit-backdrop-filter:blur(2px)}.tm-minimap--fixed:hover,.tm-minimap--fixed:focus-within{opacity:.96}.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:.72;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}';
|
|
713
713
|
let W = !1;
|
|
714
714
|
function Z() {
|
|
715
715
|
if (W || typeof document > "u") return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "table-minimap",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Tired of explaining to your boss why having too many columns in a table isn't a good idea? Give him Table-Minimap so he'll leave you alone.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/table-minimap.cjs",
|