LCOV - code coverage report
Current view: top level - dashql-app/src/ui/foundations - outside_click.ts Coverage Total Hit
Test: DashQL 915828b Lines: 94.1 % 34 32
Test Date: 2026-11-06 19:30:32

            Line data    Source code
       1              : import * as React from 'react'
       2              : 
       3              : type TouchOrMouseEventCallback = (event: MouseEvent | TouchEvent) => boolean | undefined
       4              : 
       5            1 : const STOP_PROPAGATION = true;
       6            1 : const REGISTRY: { [id: number]: TouchOrMouseEventCallback } = {}
       7            1 : let NEXT_HANDLER_ID = 1
       8              : 
       9            1 : function handleClick(event: MouseEvent) {
      10            1 :     if (!event.defaultPrevented) {
      11            1 :         for (const handler of Object.values(REGISTRY).reverse()) {
      12            1 :             if (handler(event) === STOP_PROPAGATION || event.defaultPrevented) {
      13            1 :                 break
      14              :             }
      15              :         }
      16              :     }
      17            1 : }
      18              : 
      19              : interface UseOnOutsideClickArgs {
      20              :     containerRef: React.RefObject<HTMLDivElement | null> | React.RefObject<HTMLUListElement | null>
      21              :     ignoreClickRefs?: React.RefObject<Element | null>[]
      22              :     onClickOutside: (e: MouseEvent | TouchEvent) => void
      23              : }
      24              : 
      25            1 : export function useOnOutsideClick(args: UseOnOutsideClickArgs) {
      26            1 :     const id = React.useMemo(() => NEXT_HANDLER_ID++, [])
      27              : 
      28            1 :     const handler = React.useCallback<TouchOrMouseEventCallback>(
      29            1 :         event => {
      30              :             // Don't call click handler if the mouse event was triggered by an auxiliary button (wheel button/etc).
      31              :             // A right click closes the overlay like a native menu, the right click of a row may open the menu of another row.
      32            1 :             if (event instanceof MouseEvent && event.button !== 0 && event.button !== 2) {
      33            0 :                 return STOP_PROPAGATION;
      34              :             }
      35              :             // Don't call handler if the click happened inside of the container
      36            1 :             if (args.containerRef.current?.contains(event.target as Node)) {
      37            1 :                 return STOP_PROPAGATION;
      38              :             }
      39              :             // Don't call handler if click happened on an ignored ref
      40            1 :             if (args.ignoreClickRefs && args.ignoreClickRefs.some(({ current }) => current?.contains(event.target as Node))) {
      41            0 :                 return STOP_PROPAGATION;
      42              :             }
      43              : 
      44            1 :             args.onClickOutside(event)
      45            1 :         },
      46            1 :         [args.containerRef, args.ignoreClickRefs, args.onClickOutside],
      47            1 :     )
      48              : 
      49            1 :     React.useEffect(() => {
      50            1 :         if (Object.keys(REGISTRY).length === 0) {
      51              :             // use capture to ensure we get all events
      52            1 :             document.addEventListener('mousedown', handleClick, { capture: true })
      53              :         }
      54            1 :         REGISTRY[id] = handler;
      55              : 
      56            1 :         return () => {
      57            1 :             delete REGISTRY[id];
      58            1 :             if (Object.keys(REGISTRY).length === 0) {
      59            1 :                 document.removeEventListener('mousedown', handleClick, { capture: true })
      60              :             }
      61            1 :         }
      62            1 :     }, [id, handler])
      63            1 : }
        

Generated by: LCOV version 2.0-1