UNPKG

@yamada-ui/react

Version:

React UI components of the Yamada, by the Yamada, for the Yamada built with React and Emotion

1 lines 9.89 kB
{"version":3,"file":"index.cjs","names":["useEventListeners","mergeRefs"],"sources":["../../../../src/hooks/use-clickable/index.ts"],"sourcesContent":["\"use client\"\n\nimport type { HTMLAttributes, KeyboardEvent, MouseEvent, Ref } from \"react\"\nimport type { StyledProps } from \"../../core\"\nimport { useCallback, useState } from \"react\"\nimport { dataAttr, isTouchDevice, mergeRefs } from \"../../utils\"\nimport { useEventListeners } from \"../use-event-listener\"\n\ntype Props<Y extends HTMLElement = HTMLElement> = Omit<\n HTMLAttributes<Y>,\n \"ref\" | \"size\" | keyof StyledProps\n>\n\nexport type UseClickableProps<\n Y extends HTMLElement = HTMLElement,\n M extends Props<Y> = Props<Y>,\n> = M & {\n /**\n * The ref for the element.\n */\n ref?: Ref<Y>\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 * If `true`, the element will be disabled. It will set the `disabled` HTML attribute.\n *\n * @default false\n */\n disabled?: boolean\n /**\n * Disable the touch device behavior.\n *\n * @default true\n */\n disableTouchBehavior?: boolean\n /**\n * If `true` and isDisabled, the element will have only `aria-disabled` set to `true`.\n *\n * @default false\n */\n focusable?: boolean\n /**\n * Whether or not to focus the element when it is clicked.\n * If `true`, the element will receive focus upon click.\n *\n * @default true\n */\n focusOnClick?: boolean\n}\n\nconst isValidElement = (\n ev: KeyboardEvent | KeyboardEvent[\"nativeEvent\"],\n): boolean => {\n const { isContentEditable, tagName } = ev.target as HTMLElement\n\n return tagName !== \"INPUT\" && tagName !== \"TEXTAREA\" && !isContentEditable\n}\n\nexport const useClickable = <\n Y extends HTMLElement = HTMLElement,\n M extends Props<Y> = Props<Y>,\n>(\n {\n ref,\n clickOnEnter = true,\n clickOnSpace = true,\n disabled,\n disableTouchBehavior = true,\n focusable,\n focusOnClick = true,\n tabIndex: tabIndexProp,\n onClick: onClickProp,\n onKeyDown: onKeyDownProp,\n onKeyUp: onKeyUpProp,\n onMouseDown: onMouseDownProp,\n onMouseLeave: onMouseLeaveProp,\n onMouseOver: onMouseOverProp,\n onMouseUp: onMouseUpProp,\n ...props\n }: UseClickableProps<Y, M> = {} as UseClickableProps<Y, M>,\n) => {\n const [button, setButton] = useState<boolean>(true)\n const [pressed, setPressed] = useState<boolean>(false)\n\n const listeners = useEventListeners()\n\n const tabIndex = button ? tabIndexProp : tabIndexProp || 0\n const trulyDisabled = disabled && !focusable\n\n const refCb = (node: any) => {\n if (!node) return\n\n if (node.tagName !== \"BUTTON\") setButton(false)\n }\n\n const onClick = useCallback(\n (ev: MouseEvent<Y>) => {\n if (disabled) {\n ev.stopPropagation()\n ev.preventDefault()\n\n return\n }\n\n if (focusOnClick) ev.currentTarget.focus()\n onClickProp?.(ev)\n },\n [disabled, focusOnClick, onClickProp],\n )\n\n const onDocumentKeyUp = useCallback(\n (ev: globalThis.KeyboardEvent) => {\n if (pressed && isValidElement(ev)) {\n ev.preventDefault()\n ev.stopPropagation()\n\n setPressed(false)\n\n listeners.remove(document, \"keyup\", onDocumentKeyUp, false)\n }\n },\n [pressed, listeners],\n )\n\n const onKeyDown = useCallback(\n (ev: KeyboardEvent<Y>) => {\n onKeyDownProp?.(ev)\n\n if (disabled || ev.defaultPrevented || ev.metaKey) return\n\n if (!isValidElement(ev.nativeEvent) || button) return\n\n if (clickOnSpace && ev.key === \" \") {\n ev.preventDefault()\n setPressed(true)\n }\n\n if (clickOnEnter && ev.key === \"Enter\") {\n ev.preventDefault()\n ev.currentTarget.click()\n }\n\n listeners.add(document, \"keyup\", onDocumentKeyUp, false)\n },\n [\n disabled,\n button,\n onKeyDownProp,\n clickOnEnter,\n clickOnSpace,\n listeners,\n onDocumentKeyUp,\n ],\n )\n\n const onKeyUp = useCallback(\n (ev: KeyboardEvent<Y>) => {\n onKeyUpProp?.(ev)\n\n if (disabled || ev.defaultPrevented || ev.metaKey) return\n\n if (!isValidElement(ev.nativeEvent) || button) return\n\n if (clickOnSpace && ev.key === \" \") {\n ev.preventDefault()\n setPressed(false)\n\n ev.currentTarget.click()\n }\n },\n [clickOnSpace, button, disabled, onKeyUpProp],\n )\n\n const onDocumentMouseUp = useCallback(\n (ev: globalThis.MouseEvent) => {\n if (ev.button !== 0) return\n\n setPressed(false)\n\n listeners.remove(document, \"mouseup\", onDocumentMouseUp, false)\n },\n [listeners],\n )\n\n const onMouseDown = useCallback(\n (ev: MouseEvent<Y>) => {\n if (ev.button !== 0) return\n\n if (disabled) {\n ev.stopPropagation()\n ev.preventDefault()\n\n return\n }\n\n if (!button) setPressed(true)\n\n if (focusOnClick) ev.currentTarget.focus({ preventScroll: true })\n\n listeners.add(document, \"mouseup\", onDocumentMouseUp, false)\n\n onMouseDownProp?.(ev)\n },\n [\n disabled,\n button,\n onMouseDownProp,\n listeners,\n onDocumentMouseUp,\n focusOnClick,\n ],\n )\n\n const onMouseUp = useCallback(\n (ev: MouseEvent<Y>) => {\n if (ev.button !== 0) return\n\n if (!button) setPressed(false)\n\n onMouseUpProp?.(ev)\n },\n [onMouseUpProp, button],\n )\n\n const onMouseOver = useCallback(\n (ev: MouseEvent<Y>) => {\n if (disabled) {\n ev.preventDefault()\n\n return\n }\n\n if (disableTouchBehavior && isTouchDevice()) return\n\n onMouseOverProp?.(ev)\n },\n [disabled, onMouseOverProp, disableTouchBehavior],\n )\n\n const onMouseLeave = useCallback(\n (ev: MouseEvent<Y>) => {\n if (pressed) {\n ev.preventDefault()\n\n setPressed(false)\n }\n\n if (disableTouchBehavior && isTouchDevice()) return\n\n onMouseLeaveProp?.(ev)\n },\n [pressed, onMouseLeaveProp, disableTouchBehavior],\n )\n\n if (button) {\n return {\n ...props,\n ref: mergeRefs(ref, refCb),\n type: \"button\" as const,\n \"aria-disabled\": trulyDisabled ? undefined : disabled,\n disabled: trulyDisabled,\n onClick: onClick,\n onKeyDown: onKeyDownProp,\n onKeyUp: onKeyUpProp,\n onMouseDown: onMouseDownProp,\n onMouseLeave: onMouseLeaveProp,\n onMouseOver: onMouseOverProp,\n onMouseUp: onMouseUpProp,\n }\n } else {\n return {\n ...props,\n ref: mergeRefs(ref, refCb),\n \"aria-disabled\": disabled ? (\"true\" as const) : undefined,\n \"data-active\": dataAttr(pressed),\n role: \"button\",\n tabIndex: trulyDisabled ? undefined : tabIndex,\n onClick,\n onKeyDown,\n onKeyUp,\n onMouseDown,\n onMouseLeave,\n onMouseOver,\n onMouseUp,\n }\n }\n}\n\nexport type UseClickableReturn = ReturnType<typeof useClickable>\n"],"mappings":"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"}