ngAfterViewInit()

in tensorboard/webapp/widgets/line_chart_v2/sub_view/line_chart_interactive_view.ts [216:433]


  ngAfterViewInit() {
    // dblclick event cannot be prevented. Using css to disallow selecting instead.
    fromEvent<MouseEvent>(this.dotsContainer.nativeElement, 'dblclick', {
      passive: true,
    })
      .pipe(takeUntil(this.ngUnsubscribe))
      .subscribe(() => {
        this.onViewExtentReset.emit();
        this.state = InteractionState.NONE;
        this.changeDetector.markForCheck();
      });

    fromEvent<MouseEvent>(window, 'keydown', {
      passive: true,
    })
      .pipe(takeUntil(this.ngUnsubscribe))
      .subscribe((event) => {
        const newState = this.shouldPan(event);
        if (newState !== this.specialKeyPressed) {
          this.specialKeyPressed = newState;
          this.changeDetector.markForCheck();
        }
      });

    fromEvent<MouseEvent>(window, 'keyup', {
      passive: true,
    })
      .pipe(takeUntil(this.ngUnsubscribe))
      .subscribe((event) => {
        const newState = this.shouldPan(event);
        if (newState !== this.specialKeyPressed) {
          this.specialKeyPressed = newState;
          this.changeDetector.markForCheck();
        }
      });

    fromEvent<MouseEvent>(this.dotsContainer.nativeElement, 'mousedown', {
      passive: true,
    })
      .pipe(takeUntil(this.ngUnsubscribe))
      .subscribe((event) => {
        const prevState = this.state;
        const nextState = this.shouldPan(event)
          ? InteractionState.PANNING
          : InteractionState.DRAG_ZOOMING;

        // Override the dragStartCoord and zoomBox only when started to zoom.
        // For instance, if you press left button then right, drag zoom should start at
        // the left button down so the second mousedown is ignored.
        if (
          prevState === InteractionState.NONE &&
          nextState === InteractionState.DRAG_ZOOMING
        ) {
          this.dragStartCoord = {x: event.offsetX, y: event.offsetY};
          this.zoomBoxInUiCoordinate = {
            x: event.offsetX,
            width: 0,
            y: event.offsetY,
            height: 0,
          };
        }

        if (prevState !== nextState) {
          this.state = nextState;
          this.changeDetector.markForCheck();
        }
      });

    fromEvent<MouseEvent>(this.dotsContainer.nativeElement, 'mouseup', {
      passive: true,
    })
      .pipe(takeUntil(this.ngUnsubscribe))
      .subscribe((event) => {
        const leftClicked =
          (event.buttons & MouseEventButtons.LEFT) === MouseEventButtons.LEFT;
        this.dragStartCoord = null;

        const zoomBox = this.zoomBoxInUiCoordinate;
        if (
          !leftClicked &&
          this.state === InteractionState.DRAG_ZOOMING &&
          zoomBox.width > 0 &&
          zoomBox.height > 0
        ) {
          const xMin = this.getDataX(zoomBox.x);
          const xMax = this.getDataX(zoomBox.x + zoomBox.width);
          const yMin = this.getDataY(zoomBox.y + zoomBox.height);
          const yMax = this.getDataY(zoomBox.y);

          this.onViewExtentChange.emit({
            dataExtent: {
              x: [xMin, xMax],
              y: [yMin, yMax],
            },
          });
        }
        if (this.state !== InteractionState.NONE) {
          this.state = InteractionState.NONE;
          this.changeDetector.markForCheck();
        }
      });

    fromEvent<MouseEvent>(this.dotsContainer.nativeElement, 'mouseenter', {
      passive: true,
    })
      .pipe(takeUntil(this.ngUnsubscribe))
      .subscribe((event) => {
        this.isCursorInside = true;
        this.updateTooltip(event);
        this.changeDetector.markForCheck();
      });

    fromEvent<MouseEvent>(this.dotsContainer.nativeElement, 'mouseleave', {
      passive: true,
    })
      .pipe(takeUntil(this.ngUnsubscribe))
      .subscribe((event) => {
        this.dragStartCoord = null;
        this.isCursorInside = false;
        this.updateTooltip(event);
        this.state = InteractionState.NONE;
        this.changeDetector.markForCheck();
      });

    fromEvent<MouseEvent>(this.dotsContainer.nativeElement, 'mousemove', {
      passive: true,
    })
      .pipe(takeUntil(this.ngUnsubscribe))
      .subscribe((event) => {
        switch (this.state) {
          case InteractionState.SCROLL_ZOOMING: {
            this.state = InteractionState.NONE;
            this.updateTooltip(event);
            this.changeDetector.markForCheck();
            break;
          }
          case InteractionState.NONE:
            this.updateTooltip(event);
            this.changeDetector.markForCheck();
            break;
          case InteractionState.PANNING: {
            const deltaX = -event.movementX;
            const deltaY = -event.movementY;
            const {width: domWidth, height: domHeight} = this.domDim;
            const xMin = this.getDataX(deltaX);
            const xMax = this.getDataX(domWidth + deltaX);
            const yMin = this.getDataY(domHeight + deltaY);
            const yMax = this.getDataY(deltaY);
            this.onViewExtentChange.emit({
              dataExtent: {
                x: [xMin, xMax],
                y: [yMin, yMax],
              },
            });
            break;
          }
          case InteractionState.DRAG_ZOOMING:
            {
              if (!this.dragStartCoord) {
                break;
              }
              const xs = [this.dragStartCoord.x, event.offsetX];
              const ys = [this.dragStartCoord.y, event.offsetY];
              this.zoomBoxInUiCoordinate = {
                x: Math.min(...xs),
                width: Math.max(...xs) - Math.min(...xs),
                y: Math.min(...ys),
                height: Math.max(...ys) - Math.min(...ys),
              };
            }
            this.changeDetector.markForCheck();
            break;
        }
      });

    fromEvent<WheelEvent>(this.dotsContainer.nativeElement, 'wheel', {
      passive: false,
    })
      .pipe(
        takeUntil(this.ngUnsubscribe),
        switchMap((event: WheelEvent) => {
          const shouldZoom = !event.ctrlKey && !event.shiftKey && event.altKey;
          this.showZoomInstruction = !shouldZoom;
          this.changeDetector.markForCheck();

          if (shouldZoom) {
            event.preventDefault();
            return of(event);
          }
          return timer(3000).pipe(
            tap(() => {
              this.showZoomInstruction = false;
              this.changeDetector.markForCheck();
            }),
            map(() => null)
          );
        }),
        filter((eventOrNull) => Boolean(eventOrNull))
      )
      .subscribe((eventOrNull) => {
        const event = eventOrNull!;
        this.onViewExtentChange.emit({
          dataExtent: getProposedViewExtentOnZoom(
            event,
            this.viewExtent,
            this.domDim,
            SCROLL_ZOOM_SPEED_FACTOR,
            this.xScale,
            this.yScale
          ),
        });

        if (this.state !== InteractionState.SCROLL_ZOOMING) {
          this.state = InteractionState.SCROLL_ZOOMING;
          this.changeDetector.markForCheck();
        }
      });
  }