@bianic-ui/clickable
Version:
React hook and component that implements native button interactions
170 lines (149 loc) • 5.19 kB
JavaScript
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
import { dataAttr, isRightClick, mergeRefs } from "@bianic-ui/utils";
import { useCallback, useState } from "react";
/**
* useClickable
*
* React hook that implements all the interactions of a native `button`
* component with support for making it focusable even if it's disabled.
*
* It can be used with both native button elements or other elements (like `div`).
*/
export function useClickable(props) {
if (props === void 0) {
props = {};
}
var {
ref: htmlRef,
isDisabled,
isFocusable,
clickOnEnter = true,
clickOnSpace = true,
onMouseDown,
onMouseUp,
onClick,
onKeyDown,
onKeyUp,
tabIndex: tabIndexProp,
onMouseOver
} = props,
htmlProps = _objectWithoutPropertiesLoose(props, ["ref", "isDisabled", "isFocusable", "clickOnEnter", "clickOnSpace", "onMouseDown", "onMouseUp", "onClick", "onKeyDown", "onKeyUp", "tabIndex", "onMouseOver"]);
/**
* We'll use this to track if the element is a button element
*/
var [isButton, setIsButton] = useState(true);
/**
* For custom button implementation, we'll use this to track when
* we mouse down on the button, to enable use style it's ":active" style
*/
var [isActive, setIsActive] = useState(false);
/**
* The ref callback that fires as soon as the dom node is ready
*/
var refCallback = useCallback(node => {
if ((node == null ? void 0 : node.tagName) !== "BUTTON") {
setIsButton(false);
}
}, []);
var tabIndex = isButton ? tabIndexProp : tabIndexProp || 0;
var trulyDisabled = isDisabled && !isFocusable;
var handleClick = useCallback(event => {
if (isDisabled) {
event.stopPropagation();
event.preventDefault();
return;
}
var self = event.currentTarget;
self.focus();
onClick == null ? void 0 : onClick(event);
}, [isDisabled, onClick]);
var handleKeyDown = useCallback(event => {
onKeyDown == null ? void 0 : onKeyDown(event);
if (isDisabled || event.defaultPrevented || event.metaKey) {
return;
}
var shouldClickOnEnter = clickOnEnter && event.key === "Enter";
var shouldClickOnSpace = clickOnSpace && event.key === " ";
if (!isButton && shouldClickOnSpace) {
event.preventDefault();
setIsActive(true);
return;
}
if (!isButton && shouldClickOnEnter) {
event.preventDefault();
var self = event.currentTarget;
self.click();
return;
}
}, [isDisabled, isButton, onKeyDown, clickOnEnter, clickOnSpace]);
var handleKeyUp = useCallback(event => {
onKeyUp == null ? void 0 : onKeyUp(event);
if (isDisabled || event.defaultPrevented || event.metaKey) return;
var shouldClickOnSpace = clickOnSpace && event.key === " ";
if (!isButton && shouldClickOnSpace) {
event.preventDefault();
setIsActive(false);
var self = event.currentTarget;
self.click();
}
}, [clickOnSpace, isButton, isDisabled, onKeyUp]);
var handleMouseDown = useCallback(event => {
/**
* Prevent right-click from triggering the
* active state.
*/
if (isRightClick(event)) return;
if (isDisabled) {
event.stopPropagation();
event.preventDefault();
return;
}
if (!isButton) {
setIsActive(true);
}
onMouseDown == null ? void 0 : onMouseDown(event);
}, [isDisabled, isButton, onMouseDown]);
var handleMouseUp = useCallback(event => {
if (!isButton) {
setIsActive(false);
}
onMouseUp == null ? void 0 : onMouseUp(event);
}, [onMouseUp, isButton]);
var handleMouseOver = useCallback(event => {
if (isDisabled) {
event.preventDefault();
return;
}
onMouseOver == null ? void 0 : onMouseOver(event);
}, [isDisabled, onMouseOver]);
var ref = mergeRefs(htmlRef, refCallback);
if (isButton) {
return _extends({}, htmlProps, {
ref,
type: "button",
"aria-disabled": trulyDisabled ? undefined : isDisabled,
disabled: trulyDisabled,
onClick: handleClick,
onMouseDown,
onMouseUp,
onKeyUp,
onKeyDown,
onMouseOver
});
}
return _extends({}, htmlProps, {
ref,
role: "button",
"data-active": dataAttr(isActive),
"aria-disabled": !!isDisabled,
tabIndex: trulyDisabled ? undefined : tabIndex,
onClick: handleClick,
onMouseDown: handleMouseDown,
onMouseUp: handleMouseUp,
onKeyUp: handleKeyUp,
onKeyDown: handleKeyDown,
onMouseOver: handleMouseOver
});
}
//# sourceMappingURL=use-clickable.js.map