table-minimap 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,626 @@
1
+ var D = Object.defineProperty;
2
+ var z = (C, t, e) => t in C ? D(C, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : C[t] = e;
3
+ var o = (C, t, e) => z(C, typeof t != "symbol" ? t + "" : t, e);
4
+ const T = {
5
+ mode: "columns",
6
+ height: 40,
7
+ position: "bottom",
8
+ fixedWidth: 300,
9
+ draggable: !0,
10
+ showViewport: !0,
11
+ zoomable: !1,
12
+ minZoom: 1,
13
+ maxZoom: 10,
14
+ zoomSpeed: 0.1
15
+ };
16
+ class A {
17
+ /**
18
+ * Creates a new TableMinimap instance
19
+ *
20
+ * @param selector - CSS selector string or HTMLTableElement
21
+ * @param options - Configuration options
22
+ * @throws Error if table element is not found or invalid
23
+ */
24
+ constructor(t, e = {}) {
25
+ /** The target table element */
26
+ o(this, "table");
27
+ /** Configuration options */
28
+ o(this, "options");
29
+ /** The scrollable container (parent of table) */
30
+ o(this, "scrollContainer", null);
31
+ /** Main minimap container element */
32
+ o(this, "minimapEl", null);
33
+ /** Columns container for columns mode */
34
+ o(this, "columnsEl", null);
35
+ /** Canvas element for canvas mode */
36
+ o(this, "canvasEl", null);
37
+ /** Canvas 2D rendering context */
38
+ o(this, "canvasCtx", null);
39
+ /** Viewport indicator element */
40
+ o(this, "viewportEl", null);
41
+ /** Detected column information */
42
+ o(this, "columns", []);
43
+ /** Current scroll state */
44
+ o(this, "scrollState", {
45
+ scrollLeft: 0,
46
+ scrollWidth: 0,
47
+ clientWidth: 0,
48
+ viewportRatio: 1,
49
+ positionRatio: 0
50
+ });
51
+ /** Current zoom state */
52
+ o(this, "zoomState", {
53
+ level: 1,
54
+ panX: 0,
55
+ isMinZoom: !0,
56
+ isMaxZoom: !1
57
+ });
58
+ /** Is the viewport being dragged */
59
+ o(this, "isDragging", !1);
60
+ /** Is the canvas being panned (when zoomed) */
61
+ o(this, "isPanning", !1);
62
+ /** Was just panning (to prevent click after pan) */
63
+ o(this, "wasPanning", !1);
64
+ /** Pan start position */
65
+ o(this, "panStartX", 0);
66
+ /** Currently hovered column index (-1 = none) */
67
+ o(this, "hoveredColumn", -1);
68
+ /** Currently focused/clicked column index (-1 = none) */
69
+ o(this, "focusedColumn", -1);
70
+ /** Drag start position */
71
+ o(this, "dragStartX", 0);
72
+ /** Drag start scroll position */
73
+ o(this, "dragStartScrollLeft", 0);
74
+ /** ResizeObserver instance */
75
+ o(this, "resizeObserver", null);
76
+ /** MutationObserver instance */
77
+ o(this, "mutationObserver", null);
78
+ /** Bound event handlers for cleanup */
79
+ o(this, "boundHandlers");
80
+ /** Animation frame ID for throttling */
81
+ o(this, "rafId", null);
82
+ /** Whether the instance has been destroyed */
83
+ o(this, "isDestroyed", !1);
84
+ /** Whether we started a potential pan (waiting to see if it's a click or drag) */
85
+ o(this, "isPotentialPan", !1);
86
+ this.table = this.resolveTable(t), this.options = { ...T, ...e }, this.boundHandlers = {
87
+ onScroll: this.onScroll.bind(this),
88
+ onPointerDown: this.onPointerDown.bind(this),
89
+ onPointerMove: this.onPointerMove.bind(this),
90
+ onPointerUp: this.onPointerUp.bind(this),
91
+ onMinimapClick: this.onMinimapClick.bind(this),
92
+ onWheel: this.onWheel.bind(this),
93
+ onCanvasPointerDown: this.onCanvasPointerDown.bind(this),
94
+ onCanvasMouseMove: this.onCanvasMouseMove.bind(this),
95
+ onCanvasMouseLeave: this.onCanvasMouseLeave.bind(this)
96
+ }, this.init();
97
+ }
98
+ /**
99
+ * Resolves the table element from a selector or element
100
+ *
101
+ * @param selector - CSS selector or HTMLTableElement
102
+ * @returns The resolved table element
103
+ * @throws Error if element is not found or not a table
104
+ */
105
+ resolveTable(t) {
106
+ let e;
107
+ if (typeof t == "string") {
108
+ if (e = document.querySelector(t), !e)
109
+ throw new Error(
110
+ `TableMinimap: No element found for selector "${t}"`
111
+ );
112
+ } else if (t instanceof HTMLTableElement)
113
+ e = t;
114
+ else
115
+ throw new Error(
116
+ "TableMinimap: Selector must be a CSS selector string or an HTMLTableElement"
117
+ );
118
+ if (e.tagName !== "TABLE")
119
+ throw new Error(
120
+ `TableMinimap: Element must be a <table>, got <${e.tagName.toLowerCase()}>`
121
+ );
122
+ return e;
123
+ }
124
+ /**
125
+ * Initializes the minimap
126
+ */
127
+ init() {
128
+ this.scrollContainer = this.findScrollContainer(), this.detectColumns(), this.createMinimapElement(), this.updateScrollState(), this.render(), this.attachEventListeners(), this.setupObservers();
129
+ }
130
+ /**
131
+ * Finds the nearest scrollable parent container
132
+ *
133
+ * @returns The scrollable container or the table's parent
134
+ */
135
+ findScrollContainer() {
136
+ let t = this.table.parentElement;
137
+ for (; t; ) {
138
+ const e = getComputedStyle(t), i = e.overflowX, s = e.overflow;
139
+ if (i === "auto" || i === "scroll" || s === "auto" || s === "scroll" || t.scrollWidth > t.clientWidth)
140
+ return t;
141
+ t = t.parentElement;
142
+ }
143
+ return this.table.parentElement || document.body;
144
+ }
145
+ /**
146
+ * Detects table columns from thead or first row
147
+ */
148
+ detectColumns() {
149
+ this.columns = [];
150
+ const t = this.table.querySelectorAll(
151
+ "thead th, thead td"
152
+ );
153
+ let e;
154
+ if (t.length > 0)
155
+ e = t;
156
+ else {
157
+ const s = this.table.querySelector("tr");
158
+ s ? e = s.querySelectorAll("th, td") : e = [];
159
+ }
160
+ const i = this.table.offsetWidth || 1;
161
+ Array.from(e).forEach((s) => {
162
+ const a = s.colSpan || 1, n = s.offsetWidth;
163
+ for (let l = 0; l < a; l++) {
164
+ const r = n / a;
165
+ this.columns.push({
166
+ index: this.columns.length,
167
+ width: r,
168
+ widthPercent: r / i * 100
169
+ });
170
+ }
171
+ }), this.columns.length === 0 && this.columns.push({
172
+ index: 0,
173
+ width: i,
174
+ widthPercent: 100
175
+ });
176
+ }
177
+ /**
178
+ * Creates the minimap DOM element
179
+ */
180
+ createMinimapElement() {
181
+ this.minimapEl = document.createElement("div"), this.minimapEl.className = `tm-minimap tm-minimap--${this.options.position}`, this.minimapEl.style.height = `${this.options.height}px`, this.options.position === "fixed" && (this.minimapEl.style.width = `${this.options.fixedWidth}px`), 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();
182
+ }
183
+ /**
184
+ * Creates columns-mode content
185
+ */
186
+ createColumnsContent() {
187
+ this.minimapEl && (this.columnsEl = document.createElement("div"), this.columnsEl.className = "tm-columns", this.columns.forEach((t) => {
188
+ const e = document.createElement("div");
189
+ e.className = "tm-column", e.style.width = `${t.widthPercent}%`, this.columnsEl.appendChild(e);
190
+ }), this.minimapEl.appendChild(this.columnsEl));
191
+ }
192
+ /**
193
+ * Creates canvas-mode content
194
+ */
195
+ createCanvasContent() {
196
+ this.minimapEl && (this.canvasEl = document.createElement("canvas"), this.canvasEl.className = "tm-canvas", this.canvasCtx = this.canvasEl.getContext("2d"), this.minimapEl.appendChild(this.canvasEl));
197
+ }
198
+ /**
199
+ * Creates the viewport indicator element
200
+ */
201
+ createViewportIndicator() {
202
+ 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));
203
+ }
204
+ /**
205
+ * Inserts the minimap into the DOM
206
+ */
207
+ insertMinimap() {
208
+ if (!this.minimapEl || !this.scrollContainer) return;
209
+ const t = this.scrollContainer.parentElement;
210
+ if (this.options.position === "fixed") {
211
+ if (t) {
212
+ getComputedStyle(t).position === "static" && (t.style.position = "relative");
213
+ const i = this.scrollContainer.nextSibling;
214
+ i ? t.insertBefore(this.minimapEl, i) : t.appendChild(this.minimapEl), this.minimapEl.style.position = "absolute", this.minimapEl.style.bottom = "12px", this.minimapEl.style.right = "12px", this.minimapEl.style.marginTop = "0";
215
+ }
216
+ } else if (this.options.position === "top")
217
+ t ? t.insertBefore(this.minimapEl, this.scrollContainer) : this.scrollContainer.insertBefore(this.minimapEl, this.scrollContainer.firstChild);
218
+ else if (t) {
219
+ const e = this.scrollContainer.nextSibling;
220
+ e ? t.insertBefore(this.minimapEl, e) : t.appendChild(this.minimapEl);
221
+ } else
222
+ this.scrollContainer.appendChild(this.minimapEl);
223
+ }
224
+ /**
225
+ * Updates the scroll state from the container
226
+ */
227
+ updateScrollState() {
228
+ if (!this.scrollContainer) return;
229
+ const { scrollLeft: t, scrollWidth: e, clientWidth: i } = this.scrollContainer;
230
+ this.scrollState = {
231
+ scrollLeft: t,
232
+ scrollWidth: e,
233
+ clientWidth: i,
234
+ viewportRatio: i / Math.max(e, 1),
235
+ positionRatio: t / Math.max(e - i, 1)
236
+ }, 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(
237
+ "aria-valuenow",
238
+ String(Math.round(this.scrollState.positionRatio * 100))
239
+ );
240
+ }
241
+ /**
242
+ * Renders the minimap
243
+ */
244
+ render() {
245
+ this.isDestroyed || (this.updateViewport(), this.options.mode === "canvas" && this.renderCanvas());
246
+ }
247
+ /**
248
+ * Canvas metrics for calculations - cached values to avoid repeated computations
249
+ */
250
+ getCanvasMetrics() {
251
+ var c;
252
+ const t = ((c = this.minimapEl) == null ? void 0 : c.offsetWidth) ?? 0, e = this.zoomState.level, i = this.columns.length, s = 1 / e, a = i * s, n = a > 0 ? t / a : 0;
253
+ let l = 0;
254
+ if (e > 1 && this.scrollContainer) {
255
+ const { scrollLeft: d, scrollWidth: b, clientWidth: w } = this.scrollContainer, h = Math.max(b - w, 1);
256
+ l = d / h * (1 - s);
257
+ }
258
+ const r = l * i, u = Math.floor(r), p = Math.min(Math.ceil(r + a) + 1, i), v = -(r - u) * n;
259
+ return {
260
+ width: t,
261
+ zoom: e,
262
+ numCols: i,
263
+ visibleRatio: s,
264
+ visibleCols: a,
265
+ cellWidth: n,
266
+ panX: l,
267
+ startColFloat: r,
268
+ startCol: u,
269
+ endCol: p,
270
+ xOffset: v
271
+ };
272
+ }
273
+ /**
274
+ * Calculates column index from mouse X position
275
+ */
276
+ getColumnAtX(t) {
277
+ const { width: e, numCols: i, panX: s, visibleRatio: a } = this.getCanvasMetrics();
278
+ if (i === 0 || e === 0) return -1;
279
+ const n = t / e, l = s + n * a, r = Math.floor(l * i);
280
+ return Math.max(0, Math.min(i - 1, r));
281
+ }
282
+ /**
283
+ * Updates the viewport indicator position and size
284
+ * In canvas mode, the viewport shows the focused (clicked) column
285
+ */
286
+ updateViewport() {
287
+ if (!this.viewportEl || !this.minimapEl) return;
288
+ if (this.options.mode === "canvas") {
289
+ if (this.focusedColumn >= 0 && this.columns.length > 0) {
290
+ const { cellWidth: a, startColFloat: n } = this.getCanvasMetrics(), l = (this.focusedColumn - n) * a;
291
+ this.viewportEl.style.cssText = `width:${a}px;left:${l}px;display:block`;
292
+ } else
293
+ this.viewportEl.style.display = "none";
294
+ return;
295
+ }
296
+ const t = this.minimapEl.offsetWidth, e = Math.max(t * this.scrollState.viewportRatio, 20), s = (t - e) * this.scrollState.positionRatio;
297
+ this.viewportEl.style.cssText = `width:${e}px;left:${s}px;display:block`;
298
+ }
299
+ /**
300
+ * Renders the canvas-mode visualization with table preview
301
+ */
302
+ renderCanvas() {
303
+ if (!this.canvasEl || !this.canvasCtx || !this.minimapEl) return;
304
+ const t = this.getCanvasMetrics(), e = this.options.height, i = window.devicePixelRatio || 1;
305
+ 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);
306
+ }
307
+ /**
308
+ * Renders the visible portion of the table directly onto the canvas
309
+ */
310
+ renderTableDirect(t, e) {
311
+ var P, y, L, W;
312
+ if (!this.canvasCtx) return;
313
+ const i = this.canvasCtx, { width: s, numCols: a, cellWidth: n, startCol: l, endCol: r, xOffset: u } = t, p = Array.from(this.table.querySelectorAll("tr")), v = p.length;
314
+ if (v === 0 || a === 0) return;
315
+ const c = Math.min(e * 0.15, 30), d = (e - c) / v, b = Math.min(d * 0.6, n * 0.15, 14), w = Math.min(c * 0.6, n * 0.15, 14), h = {
316
+ bg: "#ffffff",
317
+ headerBg: "#f1f5f9",
318
+ border: "#e2e8f0",
319
+ text: "#334155",
320
+ headerText: "#1e293b",
321
+ altRow: "#f8fafc",
322
+ hoverFill: "rgba(59, 130, 246, 0.08)",
323
+ hoverStroke: "rgba(59, 130, 246, 0.3)"
324
+ };
325
+ i.fillStyle = h.bg, i.fillRect(0, 0, s, e), i.fillStyle = h.headerBg, i.fillRect(0, 0, s, c);
326
+ const g = this.table.querySelector("thead tr") || p[0], x = g ? Array.from(g.querySelectorAll("th, td")) : [];
327
+ i.font = `bold ${w}px system-ui, sans-serif`, i.textBaseline = "middle";
328
+ for (let m = l; m < r; m++) {
329
+ const f = u + (m - l) * n;
330
+ if (f + n < 0 || f > s) continue;
331
+ i.strokeStyle = h.border, i.lineWidth = 1, i.strokeRect(f, 0, n, c);
332
+ const E = ((y = (P = x[m]) == null ? void 0 : P.textContent) == null ? void 0 : y.trim()) || `Col ${m + 1}`;
333
+ i.fillStyle = h.headerText, i.save(), i.beginPath(), i.rect(f + 2, 0, n - 4, c), i.clip(), i.fillText(E, f + 4, c / 2), i.restore();
334
+ }
335
+ i.font = `${b}px system-ui, sans-serif`;
336
+ for (let m = 0; m < p.length; m++) {
337
+ const f = p[m];
338
+ if (f.closest("thead")) continue;
339
+ const E = c + m * d;
340
+ if (E + d < 0 || E > e) continue;
341
+ m % 2 === 1 && (i.fillStyle = h.altRow, i.fillRect(0, E, s, d));
342
+ const X = Array.from(f.querySelectorAll("th, td"));
343
+ for (let M = l; M < r; M++) {
344
+ const S = u + (M - l) * n;
345
+ if (S + n < 0 || S > s) continue;
346
+ i.strokeStyle = h.border, i.lineWidth = 0.5, i.strokeRect(S, E, n, d);
347
+ const R = (W = (L = X[M]) == null ? void 0 : L.textContent) == null ? void 0 : W.trim();
348
+ R && (i.fillStyle = h.text, i.save(), i.beginPath(), i.rect(S + 2, E, n - 4, d), i.clip(), i.fillText(R, S + 4, E + d / 2), i.restore());
349
+ }
350
+ }
351
+ if (this.hoveredColumn >= l && this.hoveredColumn < r) {
352
+ const m = u + (this.hoveredColumn - l) * n;
353
+ i.fillStyle = h.hoverFill, i.fillRect(m, 0, n, e), i.strokeStyle = h.hoverStroke, i.lineWidth = 1, i.strokeRect(m, 0, n, e);
354
+ }
355
+ }
356
+ /**
357
+ * Attaches event listeners
358
+ */
359
+ attachEventListeners() {
360
+ !this.scrollContainer || !this.minimapEl || (this.scrollContainer.addEventListener("scroll", this.boundHandlers.onScroll, {
361
+ passive: !0
362
+ }), this.minimapEl.addEventListener("click", this.boundHandlers.onMinimapClick), 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));
363
+ }
364
+ /**
365
+ * Handles scroll events on the container
366
+ */
367
+ onScroll() {
368
+ this.isDragging || this.rafId === null && (this.rafId = requestAnimationFrame(() => {
369
+ this.updateScrollState(), this.updateViewport(), this.options.mode === "canvas" && this.render(), this.rafId = null;
370
+ }));
371
+ }
372
+ /**
373
+ * Handles click on the minimap to jump to position
374
+ *
375
+ * @param e - Mouse event
376
+ */
377
+ onMinimapClick(t) {
378
+ if (!this.minimapEl || !this.scrollContainer || this.isDragging || this.isPanning || this.wasPanning || t.target === this.viewportEl) return;
379
+ const { scrollWidth: e, clientWidth: i } = this.scrollContainer, s = this.columns.length;
380
+ if (this.hoveredColumn >= 0 && s > 0) {
381
+ this.focusedColumn = this.hoveredColumn;
382
+ const b = e / s, h = (this.hoveredColumn + 0.5) * b - i / 2, g = e - i, x = Math.max(0, Math.min(g, h));
383
+ this.scrollContainer.scrollTo({
384
+ left: x,
385
+ behavior: "smooth"
386
+ }), this.updateViewport();
387
+ return;
388
+ }
389
+ const a = this.minimapEl.getBoundingClientRect(), l = (t.clientX - a.left) / a.width, r = this.zoomState.level;
390
+ let u;
391
+ if (r > 1) {
392
+ const w = this.scrollContainer.scrollLeft / Math.max(e - i, 1), h = 1 / r;
393
+ u = w * (1 - h) + l * h;
394
+ } else
395
+ u = l;
396
+ const v = u * e - i / 2, c = e - i, d = Math.max(0, Math.min(c, v));
397
+ this.scrollContainer.scrollTo({
398
+ left: d,
399
+ behavior: "smooth"
400
+ });
401
+ }
402
+ /**
403
+ * Handles pointer down on viewport for drag start
404
+ *
405
+ * @param e - Pointer event
406
+ */
407
+ onPointerDown(t) {
408
+ !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));
409
+ }
410
+ /**
411
+ * Handles pointer move during drag
412
+ *
413
+ * @param e - Pointer event
414
+ */
415
+ onPointerMove(t) {
416
+ 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) {
417
+ t.preventDefault();
418
+ const l = t.clientX - this.panStartX, r = this.minimapEl.offsetWidth, { scrollWidth: u, clientWidth: p } = this.scrollContainer, v = u - p, c = l / r * v * this.zoomState.level, d = this.dragStartScrollLeft + c;
419
+ this.scrollContainer.scrollLeft = Math.max(0, Math.min(v, d)), this.updateScrollState(), this.updateViewport(), this.render();
420
+ return;
421
+ }
422
+ if (!this.isDragging || !this.minimapEl || !this.scrollContainer) return;
423
+ t.preventDefault();
424
+ const e = t.clientX - this.dragStartX, i = this.minimapEl.offsetWidth, s = this.scrollContainer.scrollWidth - this.scrollContainer.clientWidth, a = e / i * s, n = this.dragStartScrollLeft + a;
425
+ this.scrollContainer.scrollLeft = Math.max(
426
+ 0,
427
+ Math.min(s, n)
428
+ ), this.updateScrollState(), this.updateViewport();
429
+ }
430
+ /**
431
+ * Handles pointer up to end drag
432
+ *
433
+ * @param e - Pointer event
434
+ */
435
+ onPointerUp(t) {
436
+ if (this.isPotentialPan && !this.isPanning && this.canvasEl && this.minimapEl) {
437
+ this.isPotentialPan = !1, this.canvasEl.hasPointerCapture(t.pointerId) && this.canvasEl.releasePointerCapture(t.pointerId);
438
+ const e = new MouseEvent("click", {
439
+ clientX: t.clientX,
440
+ clientY: t.clientY,
441
+ bubbles: !0
442
+ });
443
+ this.minimapEl.dispatchEvent(e);
444
+ return;
445
+ }
446
+ 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(() => {
447
+ this.wasPanning = !1;
448
+ }, 100)), this.isDragging && (this.isDragging = !1, this.viewportEl && (this.viewportEl.classList.remove("tm-viewport--dragging"), this.viewportEl.releasePointerCapture(t.pointerId)));
449
+ }
450
+ /**
451
+ * Handles wheel events for zoom
452
+ *
453
+ * @param e - Wheel event
454
+ */
455
+ onWheel(t) {
456
+ if (!this.options.zoomable || this.options.mode !== "canvas") return;
457
+ t.preventDefault();
458
+ const e = -t.deltaY * this.options.zoomSpeed, i = Math.max(
459
+ this.options.minZoom,
460
+ Math.min(this.options.maxZoom, this.zoomState.level + e)
461
+ );
462
+ this.zoomState = {
463
+ level: i,
464
+ panX: 0,
465
+ // Not used anymore - derived from scroll position
466
+ isMinZoom: i <= this.options.minZoom,
467
+ isMaxZoom: i >= this.options.maxZoom
468
+ }, this.render();
469
+ }
470
+ /**
471
+ * Handles pointer down on canvas for drag start (scrolls table when zoomed)
472
+ *
473
+ * @param e - Pointer event
474
+ */
475
+ onCanvasPointerDown(t) {
476
+ !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)));
477
+ }
478
+ /**
479
+ * Handles mouse move on canvas for column hover highlighting
480
+ */
481
+ onCanvasMouseMove(t) {
482
+ if (!this.canvasEl || this.isPanning) return;
483
+ const e = this.canvasEl.getBoundingClientRect(), i = this.getColumnAtX(t.clientX - e.left);
484
+ i !== this.hoveredColumn && (this.hoveredColumn = i, this.canvasEl.style.cursor = i >= 0 ? "pointer" : this.zoomState.level > 1 ? "grab" : "default", this.render());
485
+ }
486
+ /**
487
+ * Handles mouse leave on canvas to clear hover state
488
+ */
489
+ onCanvasMouseLeave() {
490
+ this.hoveredColumn !== -1 && (this.hoveredColumn = -1, this.canvasEl && (this.canvasEl.style.cursor = this.zoomState.level > 1 ? "grab" : "default"), this.render());
491
+ }
492
+ /**
493
+ * Sets up ResizeObserver and MutationObserver
494
+ */
495
+ setupObservers() {
496
+ this.resizeObserver = new ResizeObserver(() => {
497
+ this.onResize();
498
+ }), this.scrollContainer && this.resizeObserver.observe(this.scrollContainer), this.resizeObserver.observe(this.table), this.mutationObserver = new MutationObserver((t) => {
499
+ t.some(
500
+ (i) => i.type === "childList" || i.attributeName === "colspan"
501
+ ) && this.onTableMutation();
502
+ }), this.mutationObserver.observe(this.table, {
503
+ childList: !0,
504
+ subtree: !0,
505
+ attributes: !0,
506
+ attributeFilter: ["colspan"]
507
+ });
508
+ }
509
+ /**
510
+ * Handles resize events
511
+ */
512
+ onResize() {
513
+ this.isDestroyed || (this.rafId !== null && cancelAnimationFrame(this.rafId), this.rafId = requestAnimationFrame(() => {
514
+ this.detectColumns(), this.updateScrollState(), this.render(), this.options.mode === "columns" && this.columnsEl && this.minimapEl && (this.columnsEl.innerHTML = "", this.columns.forEach((t) => {
515
+ const e = document.createElement("div");
516
+ e.className = "tm-column", e.style.width = `${t.widthPercent}%`, this.columnsEl.appendChild(e);
517
+ })), this.rafId = null;
518
+ }));
519
+ }
520
+ /**
521
+ * Handles table mutation events
522
+ */
523
+ onTableMutation() {
524
+ this.isDestroyed || (this.detectColumns(), this.updateScrollState(), this.render(), this.options.mode === "columns" && this.columnsEl && (this.columnsEl.innerHTML = "", this.columns.forEach((t) => {
525
+ const e = document.createElement("div");
526
+ e.className = "tm-column", e.style.width = `${t.widthPercent}%`, this.columnsEl.appendChild(e);
527
+ })));
528
+ }
529
+ /**
530
+ * Gets the current scroll state
531
+ *
532
+ * @returns Current scroll state
533
+ */
534
+ getScrollState() {
535
+ return { ...this.scrollState };
536
+ }
537
+ /**
538
+ * Gets the detected columns
539
+ *
540
+ * @returns Array of column information
541
+ */
542
+ getColumns() {
543
+ return [...this.columns];
544
+ }
545
+ /**
546
+ * Scrolls to a specific column
547
+ *
548
+ * @param columnIndex - Zero-based column index
549
+ * @param smooth - Use smooth scrolling
550
+ */
551
+ scrollToColumn(t, e = !0) {
552
+ if (!this.scrollContainer || t < 0 || t >= this.columns.length)
553
+ return;
554
+ const s = this.columns.slice(0, t).reduce((a, n) => a + n.width, 0);
555
+ this.scrollContainer.scrollTo({
556
+ left: s,
557
+ behavior: e ? "smooth" : "auto"
558
+ });
559
+ }
560
+ /**
561
+ * Forces a refresh of the minimap
562
+ */
563
+ refresh() {
564
+ this.isDestroyed || (this.scrollContainer = this.findScrollContainer(), this.detectColumns(), this.updateScrollState(), this.render(), this.options.mode === "columns" && this.columnsEl && (this.columnsEl.innerHTML = "", this.columns.forEach((t) => {
565
+ const e = document.createElement("div");
566
+ e.className = "tm-column", e.style.width = `${t.widthPercent}%`, this.columnsEl.appendChild(e);
567
+ })));
568
+ }
569
+ /**
570
+ * Gets the current zoom state (canvas mode only)
571
+ *
572
+ * @returns Current zoom state
573
+ */
574
+ getZoomState() {
575
+ return { ...this.zoomState };
576
+ }
577
+ /**
578
+ * Sets the zoom level programmatically (canvas mode only)
579
+ *
580
+ * @param level - Zoom level (1 = no zoom)
581
+ * @param panX - Optional pan position (0-1)
582
+ */
583
+ setZoom(t, e) {
584
+ if (this.isDestroyed || this.options.mode !== "canvas") return;
585
+ const i = Math.max(
586
+ this.options.minZoom,
587
+ Math.min(this.options.maxZoom, t)
588
+ ), a = 1 - 1 / i;
589
+ let n = e !== void 0 ? e : this.zoomState.panX;
590
+ n = Math.max(0, Math.min(a, n)), this.zoomState = {
591
+ level: i,
592
+ panX: i > 1 ? n : 0,
593
+ isMinZoom: i <= this.options.minZoom,
594
+ isMaxZoom: i >= this.options.maxZoom
595
+ }, this.render();
596
+ }
597
+ /**
598
+ * Resets zoom to default (shows full table overview)
599
+ */
600
+ resetZoom() {
601
+ this.setZoom(1, 0);
602
+ }
603
+ /**
604
+ * Zooms to a specific column range (canvas mode only)
605
+ *
606
+ * @param startCol - Start column index
607
+ * @param endCol - End column index
608
+ */
609
+ zoomToColumns(t, e) {
610
+ if (this.isDestroyed || this.options.mode !== "canvas") return;
611
+ const i = this.columns.length;
612
+ if (i === 0) return;
613
+ const s = Math.max(0, Math.min(i - 1, t)), n = Math.max(s + 1, Math.min(i, e)) - s, l = i / n, r = s / i;
614
+ this.setZoom(l, r);
615
+ }
616
+ /**
617
+ * Destroys the minimap instance and cleans up resources
618
+ */
619
+ destroy() {
620
+ this.isDestroyed || (this.isDestroyed = !0, 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.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 = []);
621
+ }
622
+ }
623
+ export {
624
+ A as TableMinimap
625
+ };
626
+ //# sourceMappingURL=table-minimap.js.map