import { forwardRef, memo, useCallback, useState } from "react"; import { StyledButton } from "./button.styled"; import type { WithTestId } from "../../types"; interface ButtonProps extends WithTestId> { /** * @deprecated Will be removed in 1.0.0. Use `children` instead */ text?: string | null | undefined; /** * If passed as string the button will automatically handle the styling. * Should be non-interactive phrasing content. */ children?: React.ReactNode; /** * Pass an extra element whose position relative `children` is controlled by the button. Typically this would be an icon. * Should be non-interactive phrasing content */ extra?: React.ReactNode; /** * Sets the position of `extra` relative to `children` */ extraPosition?: "left" | "right"; } const createRipple: React.MouseEventHandler = (evt) => { const button = evt.currentTarget; const circle = document.createElement("span"); const rect = button.getBoundingClientRect(); const clickX = evt.clientX - rect.left; const clickY = evt.clientY - rect.top; const maxX = Math.max(clickX, rect.width - clickX); const maxY = Math.max(clickY, rect.height - clickY); const diameter = Math.sqrt(maxX * maxX + maxY * maxY) * 2; circle.style.width = circle.style.height = `${diameter}px`; circle.style.left = `${clickX - diameter / 2}px`; circle.style.top = `${clickY - diameter / 2}px`; circle.classList.add("ripple"); const ripple = button.getElementsByClassName("ripple")[0]; if (ripple) ripple.remove(); button.appendChild(circle); }; // eslint-disable-next-line max-lines-per-function const ButtonComponent = forwardRef((props, ref) => { const { extra, extraPosition = "right", size = "l", text, children, css, type, onMouseDown, onMouseUp, className, ...rest } = props; const [shouldFadeOut, setShouldFadeOut] = useState(false); const startRippleEffect: React.MouseEventHandler = useCallback( (evt) => { setShouldFadeOut(false); createRipple(evt); onMouseDown?.(evt); }, [onMouseDown] ); const stopRippleEffect: React.MouseEventHandler = useCallback( (evt) => { setShouldFadeOut(true); onMouseUp?.(evt); }, [onMouseUp] ); if (text && children) { throw new Error( "Don't use `text` and `children` at the same time. `text` is deprecated use `children` instead!" ); } return ( {children ?? text} {extra} ); }); ButtonComponent.displayName = "Button"; /** * A Reusable `