{"version":3,"file":"BottomSheet.mjs","sources":["../../../src/common/bottomSheet/BottomSheet.tsx"],"sourcesContent":["import { clsx } from 'clsx';\nimport {\n  CSSProperties,\n  HTMLAttributes,\n  PropsWithChildren,\n  SyntheticEvent,\n  useContext,\n  useRef,\n  useState,\n} from 'react';\n\nimport Dimmer from '../../dimmer';\nimport Drawer from '../../drawer';\nimport { OverlayIdContext } from '../../provider/overlay/OverlayIdProvider';\nimport SlidingPanel from '../../slidingPanel';\nimport { CloseButton } from '../closeButton';\nimport { CommonProps } from '../commonProps';\nimport { isServerSide } from '../domHelpers';\nimport { useConditionalListener } from '../hooks';\nimport { useMedia } from '../hooks/useMedia';\nimport { Breakpoint } from '../propsValues/breakpoint';\nimport { Position } from '../propsValues/position';\n\nconst INITIAL_Y_POSITION = 0;\n\nconst CONTENT_SCROLL_THRESHOLD = 1;\n\nconst MOVE_OFFSET_THRESHOLD = 50;\n\nexport type BottomSheetProps = PropsWithChildren<\n  {\n    onClose?: (event: Event | SyntheticEvent) => void;\n    open: boolean;\n  } & CommonProps &\n    Pick<HTMLAttributes<HTMLDivElement>, 'role' | 'aria-labelledby' | 'aria-label'>\n>;\n\n/**\n * Neptune: https://transferwise.github.io/neptune/components/bottom-sheet/\n *\n * Neptune Web: https://transferwise.github.io/neptune-web/components/overlays/BottomSheet\n *\n */\nconst BottomSheet = ({ role = 'dialog', ...props }: BottomSheetProps) => {\n  const bottomSheetReference = useRef<HTMLDivElement>(null);\n  const topBarReference = useRef<HTMLDivElement>(null);\n  const contentReference = useRef<HTMLDivElement>(null);\n\n  const [pressed, setPressed] = useState<boolean>(false);\n\n  /**\n   * Used to track `requestAnimationFrame` requests\n   *\n   * @see https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame#return_value\n   */\n  const animationId = useRef<number>(0);\n\n  /**\n   * Difference between initial coordinate ({@link initialYCoordinate})\n   * and new position (when user moves component), it's get calculated on `onTouchMove` and `onMouseMove` events\n   *\n   * @see {@link calculateOffsetAfterMove}\n   */\n  const moveOffset = useRef<number>(0);\n  const initialYCoordinate = useRef<number>(0);\n\n  // apply shadow to the bottom of top-bar when scroll over content\n  useConditionalListener({\n    attachListener: props.open && !isServerSide(),\n    callback: () => {\n      if (topBarReference.current !== null) {\n        const { classList } = topBarReference.current;\n        if (!isContentScrollPositionAtTop()) {\n          classList.add('np-bottom-sheet--top-bar--shadow');\n        } else {\n          classList.remove('np-bottom-sheet--top-bar--shadow');\n        }\n      }\n    },\n    eventType: 'scroll',\n    parent: isServerSide() ? undefined : document,\n  });\n\n  function move(newHeight: number): void {\n    if (bottomSheetReference.current !== null) {\n      bottomSheetReference.current.style.transform = `translateY(${newHeight}px)`;\n    }\n  }\n\n  function close(event: Event | SyntheticEvent): void {\n    setPressed(false);\n    moveOffset.current = INITIAL_Y_POSITION;\n    if (bottomSheetReference.current !== null) {\n      bottomSheetReference.current.style.removeProperty('transform');\n    }\n    if (props.onClose) {\n      props.onClose(event);\n    }\n  }\n\n  const onSwipeStart = (\n    event: React.MouseEvent<HTMLDivElement> | React.TouchEvent<HTMLDivElement>,\n  ): void => {\n    initialYCoordinate.current = ('touches' in event ? event.touches[0] : event).clientY;\n    setPressed(true);\n  };\n\n  const onSwipeMove = (\n    event: React.MouseEvent<HTMLDivElement> | React.TouchEvent<HTMLDivElement>,\n  ): void => {\n    if (pressed) {\n      const { clientY } = 'touches' in event ? event.touches[0] : event;\n\n      const offset = calculateOffsetAfterMove(clientY);\n      // check whether move is to the bottom only and content scroll position is at the top\n      if (offset > INITIAL_Y_POSITION && isContentScrollPositionAtTop()) {\n        moveOffset.current = offset;\n        animationId.current = requestAnimationFrame(() => {\n          if (animationId.current !== undefined && bottomSheetReference.current !== null) {\n            move(offset);\n          }\n        });\n      }\n    }\n  };\n\n  function onSwipeEnd(\n    event: React.MouseEvent<HTMLDivElement> | React.TouchEvent<HTMLDivElement>,\n  ): void {\n    // stop moving component\n    cancelAnimationFrame(animationId.current);\n    setPressed(false);\n    // check whether move down is strong enough\n    // and content scroll position is at the top to close the component\n    if (moveOffset.current > MOVE_OFFSET_THRESHOLD && isContentScrollPositionAtTop()) {\n      close(event);\n    }\n    // otherwise move component back to default (initial) position\n    else {\n      move(INITIAL_Y_POSITION);\n    }\n    moveOffset.current = INITIAL_Y_POSITION;\n  }\n\n  function isContentScrollPositionAtTop(): boolean {\n    return (\n      contentReference?.current?.scrollTop !== undefined &&\n      contentReference.current.scrollTop <= CONTENT_SCROLL_THRESHOLD\n    );\n  }\n\n  /**\n   * Calculates how hard user moves component,\n   * result value used to determine whether to hide component or re-position to default state\n   *\n   * @param afterMoveYCoordinate\n   */\n  function calculateOffsetAfterMove(afterMoveYCoordinate: number): number {\n    return afterMoveYCoordinate - initialYCoordinate.current;\n  }\n\n  /**\n   * Set `max-height` for content part (in order to keep it scrollable for content overflow cases) of the component\n   * and ensures space for safe zone (32px) at the top.\n   */\n  function setContentMaxHeight(): CSSProperties {\n    const safeZoneHeight = '64px';\n    const topbarHeight = '32px';\n    const windowHight: number = isServerSide() ? 0 : window.innerHeight;\n    /**\n     * Calculate _real_ height of the screen (taking into account parts of browser interface).\n     *\n     * See https://css-tricks.com/the-trick-to-viewport-units-on-mobile for more details.\n     */\n    const screenHeight = `${windowHight * 0.01 * 100}px`;\n    return {\n      maxHeight: `calc(${screenHeight} - ${safeZoneHeight} - ${topbarHeight})`,\n    };\n  }\n\n  const is400Zoom = useMedia(`(max-width: ${Breakpoint.ZOOM_400}px)`);\n\n  const overlayId = useContext(OverlayIdContext);\n\n  return is400Zoom ? (\n    <Drawer\n      aria-labelledby={props['aria-labelledby']}\n      aria-label={props['aria-label']}\n      role={role}\n      open={props.open}\n      className={props.className}\n      onClose={close}\n    >\n      {props.children}\n    </Drawer>\n  ) : (\n    <Dimmer open={props.open} fadeContentOnEnter fadeContentOnExit onClose={close}>\n      <SlidingPanel\n        ref={bottomSheetReference}\n        open={props.open}\n        position={Position.BOTTOM}\n        className={clsx('np-bottom-sheet', props.className)}\n      >\n        <div\n          id={overlayId}\n          aria-labelledby={props['aria-labelledby'] || undefined}\n          aria-label={props['aria-label'] || undefined}\n          role={role}\n          aria-modal\n          onTouchStart={onSwipeStart}\n          onTouchMove={onSwipeMove}\n          onTouchEnd={onSwipeEnd}\n          onMouseDown={onSwipeStart}\n          onMouseMove={onSwipeMove}\n          onMouseUp={onSwipeEnd}\n        >\n          <div ref={topBarReference} className=\"np-bottom-sheet--top-bar\">\n            <div className=\"np-bottom-sheet--handler\" />\n            <CloseButton size=\"sm\" className=\"sr-only np-bottom-sheet--close-btn\" onClick={close} />\n          </div>\n          <div\n            ref={contentReference}\n            style={setContentMaxHeight()}\n            className=\"np-bottom-sheet--content\"\n          >\n            {props.children}\n          </div>\n        </div>\n      </SlidingPanel>\n    </Dimmer>\n  );\n};\n\nexport default BottomSheet;\n"],"names":["INITIAL_Y_POSITION","CONTENT_SCROLL_THRESHOLD","MOVE_OFFSET_THRESHOLD","BottomSheet","role","props","bottomSheetReference","useRef","topBarReference","contentReference","pressed","setPressed","useState","animationId","moveOffset","initialYCoordinate","useConditionalListener","attachListener","open","isServerSide","callback","current","classList","isContentScrollPositionAtTop","add","remove","eventType","parent","undefined","document","move","newHeight","style","transform","close","event","removeProperty","onClose","onSwipeStart","touches","clientY","onSwipeMove","offset","calculateOffsetAfterMove","requestAnimationFrame","onSwipeEnd","cancelAnimationFrame","scrollTop","afterMoveYCoordinate","setContentMaxHeight","safeZoneHeight","topbarHeight","windowHight","window","innerHeight","screenHeight","maxHeight","is400Zoom","useMedia","Breakpoint","ZOOM_400","overlayId","useContext","OverlayIdContext","_jsx","Drawer","className","children","Dimmer","fadeContentOnEnter","fadeContentOnExit","SlidingPanel","ref","position","Position","BOTTOM","clsx","_jsxs","id","onTouchStart","onTouchMove","onTouchEnd","onMouseDown","onMouseMove","onMouseUp","CloseButton","size","onClick"],"mappings":"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"}