1 line
11 KiB
Plaintext
1 line
11 KiB
Plaintext
{"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":[]} |