// Copyright 2017-2025 @pezkuwi/react-components authors & contributors // SPDX-License-Identifier: Apache-2.0 import type { ButtonProps as Props } from './types.js'; import React, { useCallback, useEffect } from 'react'; import Icon from '../Icon.js'; import Spinner from '../Spinner.js'; import { styled } from '../styled.js'; import Group from './Group.js'; function ButtonBase ({ activeOnEnter, children, className = '', dataTestId = '', icon, isBasic, isBusy, isCircular, isDisabled, isFull, isIcon, isSelected, isToplevel, label, onClick, isReadOnly = !onClick, onMouseEnter, onMouseLeave, tabIndex, withoutLink }: Props): React.ReactElement { const _onClick = useCallback( (): void => { !(isBusy || isDisabled) && onClick && Promise .resolve(onClick()) .catch(console.error); }, [isBusy, isDisabled, onClick] ); const _onMouseEnter = useCallback((): void => { onMouseEnter && Promise .resolve(onMouseEnter()) .catch(console.error); }, [onMouseEnter]); const _onMouseLeave = useCallback((): void => { onMouseLeave && Promise .resolve(onMouseLeave()) .catch(console.error); }, [onMouseLeave]); const listenKeyboard = useCallback((event: KeyboardEvent): void => { if (!isBusy && !isDisabled && event.key === 'Enter') { onClick && Promise .resolve(onClick()) .catch(console.error); } }, [isBusy, isDisabled, onClick]); useEffect(() => { if (activeOnEnter) { window.addEventListener('keydown', listenKeyboard, true); } return () => { if (activeOnEnter) { window.removeEventListener('keydown', listenKeyboard, true); } }; }, [activeOnEnter, listenKeyboard]); return ( {icon && } {label} {children} {isBusy && ( )} ); } const ICON_PADDING = 0.5; const StyledButton = styled.button` background: transparent; border: none; color: inherit; cursor: pointer; line-height: 1; margin: 0; outline: none; position: relative; vertical-align: middle; text-align: center; &:not(.hasLabel) { padding: 0.7em; .ui--Icon { padding: 0.6rem; margin: -0.6rem; } } &:not(.isCircular) { border-radius: 0.25rem; } &:focus { outline:0; } &.hasLabel { padding: 0.7rem 1.1rem 0.7rem ${1.1 - ICON_PADDING}rem; .ui--Icon { margin-right: 0.425rem !important; } } &.isBasic { background: var(--bg-table); } &.isCircular { border-radius: 10rem; } &.isDisabled, &.isReadOnly { background: none; box-shadow: none; cursor: not-allowed; } &.isBusy { cursor: wait; } &.isFull { display: block; width: 100%; } &.isIcon { background: transparent; } .ui--Button-overlay { background: rgba(253, 252, 251, 0.75); bottom: 0; left: 0; position: absolute; right: 0; top: 0; visibility: hidden; } .ui--Icon { border-radius: 50%; box-sizing: content-box; height: 1rem; margin: -${ICON_PADDING}rem 0; padding: ${ICON_PADDING}rem; width: 1rem; } &.isDisabled { color: #bcbbba; } `; const Button = React.memo(ButtonBase) as unknown as typeof ButtonBase & { Group: typeof Group }; Button.Group = Group; export default Button;