123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108 |
- type ArgumentTypes<F> = F extends (...args: infer A) => any ? A : never;
- type EventStore<T> = {
- [K in keyof TraceRowWidthMeasurerEvents<T>]: Set<TraceRowWidthMeasurerEvents<T>[K]>;
- };
- interface TraceRowWidthMeasurerEvents<T> {
- ['max']: (max: number) => void;
- ['row measure']: (row: T) => void;
- ['row measure end']: () => void;
- }
- export class TraceRowWidthMeasurer<T> {
- cache: Map<T, number> = new Map();
- queue: [T, HTMLElement][] = [];
- drainRaf: number | null = null;
- max: number = 0;
- constructor() {
- this.drain = this.drain.bind(this);
- }
- listeners: EventStore<T> = {
- max: new Set(),
- 'row measure': new Set(),
- 'row measure end': new Set(),
- };
- once<K extends keyof TraceRowWidthMeasurerEvents<T>>(
- event: K,
- cb: (max: number) => void
- ) {
- const listener = (...args: any[]) => {
- cb(...(args as ArgumentTypes<typeof cb>));
- this.off(event, listener);
- };
- this.on(event, listener);
- }
- on<K extends keyof TraceRowWidthMeasurerEvents<T>>(
- eventName: K,
- cb: TraceRowWidthMeasurerEvents<T>[K]
- ): void {
- this.listeners?.[eventName]?.add?.(cb);
- }
- off<K extends keyof TraceRowWidthMeasurerEvents<T>>(
- eventName: K,
- cb: TraceRowWidthMeasurerEvents<T>[K]
- ): void {
- this.listeners?.[eventName]?.delete?.(cb);
- }
- dispatch<K extends keyof TraceRowWidthMeasurerEvents<T>>(
- event: K,
- ...args: ArgumentTypes<TraceRowWidthMeasurerEvents<T>[K]>
- ): void {
- if (!this.listeners[event] || this.listeners[event].size === 0) {
- return;
- }
- for (const handler of this.listeners[event]) {
- // @ts-expect-error
- handler(...args);
- }
- }
- enqueueMeasure(node: T, element: HTMLElement) {
- if (this.cache.has(node)) {
- return;
- }
- this.queue.push([node, element]);
- if (this.drainRaf !== null) {
- window.cancelAnimationFrame(this.drainRaf);
- }
- this.drainRaf = window.requestAnimationFrame(this.drain);
- }
- drain() {
- const startMax = this.max;
- while (this.queue.length > 0) {
- const next = this.queue.pop()!;
- const width = this.measure(next[0], next[1]);
- if (width > this.max) {
- this.max = width;
- }
- }
- if (this.max !== startMax) {
- this.dispatch('max', this.max);
- }
- this.dispatch('row measure end');
- }
- measure(node: T, element: HTMLElement): number {
- const cache = this.cache.get(node);
- if (cache !== undefined) {
- return cache;
- }
- const rect = element.getBoundingClientRect();
- this.cache.set(node, rect.width);
- return rect.width;
- }
- }
|