All files / src/packlets/editor-core/slices viewport-slice.ts

100% Statements 23/23
100% Branches 0/0
100% Functions 9/9
100% Lines 23/23

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76                  1x   51x 51x 51x           36x 36x       51x 51x       10x       51x       51x       2332x       1413x       494x                 443x 443x 443x 443x 443x 443x   443x 443x 443x                
import { atom } from "nanostores";
import { createNanoEvents } from "nanoevents";
import { Slice } from "../slice";
import { ZoomSlice } from "./zoom-slice";
import { ChartSlice } from "./chart-slice";
import { Point, type Dimension } from "../../geometry";
import { BASE_SCALE_Y, PADDING_BOTTOM } from "../types";
 
export class ViewportSlice extends Slice {
  static readonly sliceKey = "viewport";
 
  $scroll = atom<Point>({ x: 0, y: 0 });
  $viewportSize = atom<Dimension>({ width: 0, height: 0 });
  private events = createNanoEvents<{
    viewportChanged: () => void;
    scrollRequest: (point: Point) => void;
  }>();
 
  setScroll(point: Point): void {
    this.$scroll.set(point);
    this.events.emit("viewportChanged");
  }
 
  setViewportSize(width: number, height: number): void {
    this.$viewportSize.set({ width, height });
    this.events.emit("viewportChanged");
  }
 
  requestScroll(point: Point): void {
    this.events.emit("scrollRequest", point);
  }
 
  onViewportChanged(cb: () => void): () => void {
    return this.events.on("viewportChanged", cb);
  }
 
  onScrollRequest(cb: (point: Point) => void): () => void {
    return this.events.on("scrollRequest", cb);
  }
 
  getScaleY(): number {
    return BASE_SCALE_Y * this.ctx.get(ZoomSlice).$zoom.get();
  }
 
  getTrackHeight(): number {
    return this.ctx.get(ChartSlice).getChartSize() * this.getScaleY();
  }
 
  getContentHeight(): number {
    return this.getTrackHeight() + PADDING_BOTTOM;
  }
 
  getVisiblePulseRange(): {
    start: number;
    end: number;
    rawStart: number;
    rawEnd: number;
  } {
    const size = this.ctx.get(ChartSlice).getChartSize();
    const scaleY = this.getScaleY();
    const trackHeight = this.getTrackHeight();
    const scrollTop = this.$scroll.get().y;
    const viewportHeight = this.$viewportSize.get().height;
    const viewportBottom = scrollTop + viewportHeight;
 
    const rawPulseStart = Math.max(0, Math.floor((trackHeight - viewportBottom) / scaleY));
    const rawPulseEnd = Math.min(size, Math.ceil((trackHeight - scrollTop) / scaleY));
    return {
      start: Math.max(0, rawPulseStart - 50),
      end: Math.min(size, rawPulseEnd + 50),
      rawStart: rawPulseStart,
      rawEnd: rawPulseEnd,
    };
  }
}