Repository: ionic-team/ionic-framework
File: core/src/components/modal/gestures/sheet.ts (TypeScript)

Code before:
  const height = wrapperEl.clientHeight;
  let currentBreakpoint = initialBreakpoint;
  let offset = 0;
  const wrapperAnimation = animation.childAnimations.find(ani => ani.id === 'wrapperAnimation');
  const backdropAnimation = animation.childAnimations.find(ani => ani.id === 'backdropAnimation');
  const maxBreakpoint = breakpoints[breakpoints.length - 1];

  /* tslint:disable-next-line */
  if (typeof window !== 'undefined') {
    const keyboardOpenCallback = () => {
      /**
       * When the native keyboard is opened and the webview
       * is resized, the gesture implementation will become unresponsive
       * and enter a free-scroll mode.
       *
       * When the keyboard is opened, we disable the gesture for
       * a single frame and re-enable once the contents have repositioned
       * from the keyboard placement.
       */
      gesture.enable(false);
      raf(() => {
        gesture.enable(true)
      });
    };

    window.addEventListener(KEYBOARD_DID_OPEN, keyboardOpenCallback);
  }

  /**
   * After the entering animation completes,
   * we need to set the animation to go from
   * offset 0 to offset 1 so that users can
   * swipe in any direction. We then set the
   * animation offset to the current breakpoint
   * so there is no flickering.
   */
  if (wrapperAnimation && backdropAnimation) {
    wrapperAnimation.keyframes([...SheetDefaults.WRAPPER_KEYFRAMES]);
    backdropAnimation.keyframes([...SheetDefaults.BACKDROP_KEYFRAMES]);
    animation.progressStart(true, 1 - currentBreakpoint);

    /**
     * Backdrop should become enabled
     * after the backdropBreakpoint value
     */
    const backdropEnabled = currentBreakpoint > backdropBreakpoint
    backdropEl.style.setProperty('pointer-events', backdropEnabled ? 'auto' : 'none');
  }

  if (contentEl && currentBreakpoint !== maxBreakpoint) {
    contentEl.scrollY = false;

Review comment (suggestion):
It looks like this only gets called once when the modal is presented, but do we need to remove the listener when it's dismissed? 🤔

Code after:
  const height = wrapperEl.clientHeight;
  let currentBreakpoint = initialBreakpoint;
  let offset = 0;
  const wrapperAnimation = animation.childAnimations.find(ani => ani.id === 'wrapperAnimation');
  const backdropAnimation = animation.childAnimations.find(ani => ani.id === 'backdropAnimation');
  const maxBreakpoint = breakpoints[breakpoints.length - 1];

  const keyboardOpenCallback = () => {
    /**
     * When the native keyboard is opened and the webview
     * is resized, the gesture implementation will become unresponsive
     * and enter a free-scroll mode.
     *
     * When the keyboard is opened, we disable the gesture for
     * a single frame and re-enable once the contents have repositioned
     * from the keyboard placement.
     */
    gesture.enable(false);
    raf(() => {
      gesture.enable(true)
    });
  };

  /* tslint:disable-next-line */
  if (typeof window !== 'undefined') {
    window.addEventListener(KEYBOARD_DID_OPEN, keyboardOpenCallback);
  }

  /**
   * After the entering animation completes,
   * we need to set the animation to go from
   * offset 0 to offset 1 so that users can
   * swipe in any direction. We then set the
   * animation offset to the current breakpoint
   * so there is no flickering.
   */
  if (wrapperAnimation && backdropAnimation) {
    wrapperAnimation.keyframes([...SheetDefaults.WRAPPER_KEYFRAMES]);
    backdropAnimation.keyframes([...SheetDefaults.BACKDROP_KEYFRAMES]);
    animation.progressStart(true, 1 - currentBreakpoint);

    /**
     * Backdrop should become enabled
     * after the backdropBreakpoint value
     */
    const backdropEnabled = currentBreakpoint > backdropBreakpoint
    backdropEl.style.setProperty('pointer-events', backdropEnabled ? 'auto' : 'none');
  }

  if (contentEl && currentBreakpoint !== maxBreakpoint) {
    contentEl.scrollY = false;