{"version":3,"sources":["../src/use-clickable.ts"],"sourcesContent":["import { dataAttr } from \"@chakra-ui/shared-utils\"\nimport { mergeRefs } from \"@chakra-ui/react-use-merge-refs\"\nimport { useEventListeners } from \"./use-event-listeners\"\nimport { useCallback, useState } from \"react\"\n\nexport interface UseClickableProps extends React.HTMLAttributes<HTMLElement> {\n  /**\n   * If `true`, the element will be disabled.\n   * It will set the `disabled` HTML attribute\n   *\n   * @default false\n   */\n  isDisabled?: boolean\n  /**\n   * If `true` and isDisabled, the element will\n   * have only `aria-disabled` set to `true`\n   *\n   * @default false\n   */\n  isFocusable?: boolean\n  /**\n   * Whether or not trigger click on pressing `Enter`.\n   *\n   * @default true\n   */\n  clickOnEnter?: boolean\n  /**\n   * Whether or not trigger click on pressing `Space`.\n   *\n   * @default true\n   */\n  clickOnSpace?: boolean\n  /**\n   * The ref for the element\n   */\n  ref?: React.Ref<HTMLElement>\n}\n\nfunction isValidElement(event: KeyboardEvent): boolean {\n  const element = event.target as HTMLElement\n  const { tagName, isContentEditable } = element\n  return (\n    tagName !== \"INPUT\" && tagName !== \"TEXTAREA\" && isContentEditable !== true\n  )\n}\n\n/**\n * useClickable implements all the interactions of a native `button`\n * component with support for making it focusable even if it is disabled.\n *\n * It can be used with both native button elements or other elements (like `div`).\n */\nexport function useClickable(props: UseClickableProps = {}) {\n  const {\n    ref: htmlRef,\n    isDisabled,\n    isFocusable,\n    clickOnEnter = true,\n    clickOnSpace = true,\n    onMouseDown,\n    onMouseUp,\n    onClick,\n    onKeyDown,\n    onKeyUp,\n    tabIndex: tabIndexProp,\n    onMouseOver,\n    onMouseLeave,\n    ...htmlProps\n  } = props\n  /**\n   * We'll use this to track if the element is a button element\n   */\n  const [isButton, setIsButton] = useState(true)\n\n  /**\n   * For custom button implementation, we'll use this to track when\n   * we mouse down on the button, to enable use style its \":active\" style\n   */\n  const [isPressed, setIsPressed] = useState(false)\n\n  const listeners = useEventListeners()\n\n  /**\n   * The ref callback that fires as soon as the dom node is ready\n   */\n  const refCallback = (node: any) => {\n    if (!node) return\n    if (node.tagName !== \"BUTTON\") {\n      setIsButton(false)\n    }\n  }\n\n  const tabIndex = isButton ? tabIndexProp : tabIndexProp || 0\n  const trulyDisabled = isDisabled && !isFocusable\n\n  const handleClick = useCallback(\n    (event: React.MouseEvent<HTMLElement>) => {\n      if (isDisabled) {\n        event.stopPropagation()\n        event.preventDefault()\n        return\n      }\n\n      const self = event.currentTarget as HTMLElement\n      self.focus()\n      onClick?.(event)\n    },\n    [isDisabled, onClick],\n  )\n\n  const onDocumentKeyUp = useCallback(\n    (e: KeyboardEvent) => {\n      if (isPressed && isValidElement(e)) {\n        e.preventDefault()\n        e.stopPropagation()\n\n        setIsPressed(false)\n        // eslint-disable-next-line @typescript-eslint/no-unused-vars\n        listeners.remove(document, \"keyup\", onDocumentKeyUp, false)\n      }\n    },\n    [isPressed, listeners],\n  )\n\n  const handleKeyDown = useCallback(\n    (event: React.KeyboardEvent<HTMLElement>) => {\n      onKeyDown?.(event)\n\n      if (isDisabled || event.defaultPrevented || event.metaKey) {\n        return\n      }\n\n      if (!isValidElement(event.nativeEvent) || isButton) return\n\n      const shouldClickOnEnter = clickOnEnter && event.key === \"Enter\"\n      const shouldClickOnSpace = clickOnSpace && event.key === \" \"\n\n      if (shouldClickOnSpace) {\n        event.preventDefault()\n        setIsPressed(true)\n      }\n\n      if (shouldClickOnEnter) {\n        event.preventDefault()\n        const self = event.currentTarget as HTMLElement\n        self.click()\n      }\n\n      listeners.add(document, \"keyup\", onDocumentKeyUp, false)\n    },\n    [\n      isDisabled,\n      isButton,\n      onKeyDown,\n      clickOnEnter,\n      clickOnSpace,\n      listeners,\n      onDocumentKeyUp,\n    ],\n  )\n\n  const handleKeyUp = useCallback(\n    (event: React.KeyboardEvent<HTMLElement>) => {\n      onKeyUp?.(event)\n\n      if (isDisabled || event.defaultPrevented || event.metaKey) return\n\n      if (!isValidElement(event.nativeEvent) || isButton) return\n\n      const shouldClickOnSpace = clickOnSpace && event.key === \" \"\n\n      if (shouldClickOnSpace) {\n        event.preventDefault()\n        setIsPressed(false)\n\n        const self = event.currentTarget as HTMLElement\n        self.click()\n      }\n    },\n    [clickOnSpace, isButton, isDisabled, onKeyUp],\n  )\n\n  const onDocumentMouseUp = useCallback(\n    (event: MouseEvent) => {\n      if (event.button !== 0) return\n      setIsPressed(false)\n      listeners.remove(document, \"mouseup\", onDocumentMouseUp, false)\n    },\n    [listeners],\n  )\n\n  const handleMouseDown = useCallback(\n    (event: React.MouseEvent<HTMLElement>) => {\n      if (event.button !== 0) return\n\n      if (isDisabled) {\n        event.stopPropagation()\n        event.preventDefault()\n        return\n      }\n\n      if (!isButton) {\n        setIsPressed(true)\n      }\n\n      const target = event.currentTarget as HTMLElement\n      target.focus({ preventScroll: true })\n\n      listeners.add(document, \"mouseup\", onDocumentMouseUp, false)\n\n      onMouseDown?.(event)\n    },\n    [isDisabled, isButton, onMouseDown, listeners, onDocumentMouseUp],\n  )\n\n  const handleMouseUp = useCallback(\n    (event: React.MouseEvent<HTMLElement>) => {\n      if (event.button !== 0) return\n\n      if (!isButton) {\n        setIsPressed(false)\n      }\n\n      onMouseUp?.(event)\n    },\n    [onMouseUp, isButton],\n  )\n\n  const handleMouseOver = useCallback(\n    (event: React.MouseEvent<HTMLElement>) => {\n      if (isDisabled) {\n        event.preventDefault()\n        return\n      }\n\n      onMouseOver?.(event)\n    },\n    [isDisabled, onMouseOver],\n  )\n\n  const handleMouseLeave = useCallback(\n    (event: React.MouseEvent<HTMLElement>) => {\n      if (isPressed) {\n        event.preventDefault()\n        setIsPressed(false)\n      }\n      onMouseLeave?.(event)\n    },\n    [isPressed, onMouseLeave],\n  )\n\n  const ref = mergeRefs(htmlRef, refCallback)\n\n  if (isButton) {\n    return {\n      ...htmlProps,\n      ref,\n      type: \"button\" as React.ButtonHTMLAttributes<any>[\"type\"],\n      \"aria-disabled\": trulyDisabled ? undefined : isDisabled,\n      disabled: trulyDisabled,\n      onClick: handleClick,\n      onMouseDown,\n      onMouseUp,\n      onKeyUp,\n      onKeyDown,\n      onMouseOver,\n      onMouseLeave,\n    }\n  }\n\n  return {\n    ...htmlProps,\n    ref,\n    role: \"button\",\n    \"data-active\": dataAttr(isPressed),\n    \"aria-disabled\": isDisabled ? (\"true\" as const) : undefined,\n    tabIndex: trulyDisabled ? undefined : tabIndex,\n    onClick: handleClick,\n    onMouseDown: handleMouseDown,\n    onMouseUp: handleMouseUp,\n    onKeyUp: handleKeyUp,\n    onKeyDown: handleKeyDown,\n    onMouseOver: handleMouseOver,\n    onMouseLeave: handleMouseLeave,\n  }\n}\n\nexport type UseClickableReturn = ReturnType<typeof useClickable>\n"],"mappings":"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","names":[]}