image cache on cloudflare r2

feat: fix double click

dunkirk.sh ed6afa8b 2038f615

verified
Changed files
+35 -11
src
+35 -11
src/dashboard.ts
··· 86 private lodCache: Partial<Record<Granularity, LodCacheEntry>> = {}; 87 private activeGranularity: Granularity | null = null; 88 private isLoading = false; 89 - private dblClickHandler: (() => void) | null = null; 90 91 private readonly totalHitsEl = document.getElementById( 92 "total-hits", ··· 299 this.originalRange = { start: first, end: last }; 300 this.renderCurrentViewport({ min: first, max: last }); 301 } else { 302 - this.renderCurrentViewport(); 303 } 304 } 305 ··· 368 private handleSelect(u: uPlot) { 369 if (u.select.width <= 10) return; 370 371 - const min = Math.floor(u.posToVal(u.select.left, "x")); 372 - const max = Math.floor(u.posToVal(u.select.left + u.select.width, "x")); 373 374 u.setSelect({ left: 0, top: 0, width: 0, height: 0 }, false); 375 376 this.currentRange = { start: min, end: max }; 377 378 const bestCache = this.getBestCacheForRange(min, max); ··· 396 max: this.originalRange.end, 397 }); 398 this.renderCurrentViewport(); 399 } 400 } 401 ··· 414 height: 0, 415 }, 416 scales: { 417 - x: { time: true }, 418 y: { auto: true }, 419 }, 420 axes: [ ··· 441 ], 442 hooks: { 443 setSelect: [(u) => this.handleSelect(u)], 444 }, 445 }; 446 447 this.chartEl.innerHTML = ""; 448 this.chart = new uPlot(opts, [timestamps, hits], this.chartEl); 449 - 450 - if (this.dblClickHandler) { 451 - this.chartEl.removeEventListener("dblclick", this.dblClickHandler); 452 - } 453 - this.dblClickHandler = () => this.resetZoom(); 454 - this.chartEl.addEventListener("dblclick", this.dblClickHandler); 455 } 456 457 private handleResize = () => {
··· 86 private lodCache: Partial<Record<Granularity, LodCacheEntry>> = {}; 87 private activeGranularity: Granularity | null = null; 88 private isLoading = false; 89 90 private readonly totalHitsEl = document.getElementById( 91 "total-hits", ··· 298 this.originalRange = { start: first, end: last }; 299 this.renderCurrentViewport({ min: first, max: last }); 300 } else { 301 + this.renderCurrentViewport({ 302 + min: this.currentRange.start, 303 + max: this.currentRange.end, 304 + }); 305 } 306 } 307 ··· 370 private handleSelect(u: uPlot) { 371 if (u.select.width <= 10) return; 372 373 + let min = Math.floor(u.posToVal(u.select.left, "x")); 374 + let max = Math.floor(u.posToVal(u.select.left + u.select.width, "x")); 375 376 u.setSelect({ left: 0, top: 0, width: 0, height: 0 }, false); 377 378 + const minSpan = 1.5 * 86400; 379 + const span = max - min; 380 + if (span < minSpan) { 381 + const center = (min + max) / 2; 382 + min = Math.floor(center - minSpan / 2); 383 + max = Math.floor(center + minSpan / 2); 384 + } 385 + 386 this.currentRange = { start: min, end: max }; 387 388 const bestCache = this.getBestCacheForRange(min, max); ··· 406 max: this.originalRange.end, 407 }); 408 this.renderCurrentViewport(); 409 + this.fetchData(); 410 } 411 } 412 ··· 425 height: 0, 426 }, 427 scales: { 428 + x: { 429 + time: true, 430 + range: (u, dataMin, dataMax) => { 431 + let min = dataMin; 432 + let max = dataMax; 433 + const minSpan = 1.5 * 86400; 434 + const span = max - min; 435 + if (span < minSpan) { 436 + const center = (min + max) / 2; 437 + min = center - minSpan / 2; 438 + max = center + minSpan / 2; 439 + } 440 + return [min, max]; 441 + }, 442 + }, 443 y: { auto: true }, 444 }, 445 axes: [ ··· 466 ], 467 hooks: { 468 setSelect: [(u) => this.handleSelect(u)], 469 + ready: [ 470 + (u) => { 471 + u.over.addEventListener("dblclick", () => this.resetZoom()); 472 + }, 473 + ], 474 }, 475 }; 476 477 this.chartEl.innerHTML = ""; 478 this.chart = new uPlot(opts, [timestamps, hits], this.chartEl); 479 } 480 481 private handleResize = () => {