import type { MouseEvent as ReactMouseEvent } from 'react'; import type { ElementRef, FC } from '../../lib/teact/teact'; import React, { useRef, useState } from '../../lib/teact/teact'; import { IS_TOUCH_ENV, MouseButton } from '../../util/browser/windowEnvironment'; import buildClassName from '../../util/buildClassName'; import buildStyle from '../../util/buildStyle'; import useLastCallback from '../../hooks/useLastCallback'; import useOldLang from '../../hooks/useOldLang'; import Sparkles from '../common/Sparkles'; import RippleEffect from './RippleEffect'; import Spinner from './Spinner'; import './Button.scss'; export type OwnProps = { ref?: ElementRef; type?: 'button' | 'submit' | 'reset'; children: React.ReactNode; size?: 'default' | 'smaller' | 'tiny'; color?: ( 'primary' | 'secondary' | 'gray' | 'danger' | 'translucent' | 'translucent-white' | 'translucent-black' | 'translucent-bordered' | 'dark' | 'green' | 'adaptive' | 'stars' | 'bluredStarsBadge' ); backgroundImage?: string; id?: string; className?: string; round?: boolean; pill?: boolean; badge?: boolean; fluid?: boolean; isText?: boolean; isLoading?: boolean; ariaLabel?: string; ariaControls?: string; hasPopup?: boolean; href?: string; download?: string; disabled?: boolean; nonInteractive?: boolean; allowDisabledClick?: boolean; noFastClick?: boolean; ripple?: boolean; faded?: boolean; tabIndex?: number; isRtl?: boolean; isShiny?: boolean; isRectangular?: boolean; withPremiumGradient?: boolean; withSparkleEffect?: boolean; noPreventDefault?: boolean; noForcedUpperCase?: boolean; shouldStopPropagation?: boolean; style?: string; onClick?: (e: ReactMouseEvent) => void; onContextMenu?: (e: ReactMouseEvent) => void; onMouseDown?: (e: ReactMouseEvent) => void; onMouseUp?: (e: ReactMouseEvent) => void; onMouseEnter?: (e: ReactMouseEvent) => void; onMouseLeave?: NoneToVoidFunction; onFocus?: NoneToVoidFunction; onTransitionEnd?: NoneToVoidFunction; }; // Longest animation duration; const CLICKED_TIMEOUT = 400; const Button: FC = ({ ref, type = 'button', id, onClick, onContextMenu, onMouseDown, onMouseUp, onMouseEnter, onMouseLeave, onFocus, children, size = 'default', color = 'primary', backgroundImage, className, round, pill, badge, fluid, isText, isLoading, isShiny, withPremiumGradient, withSparkleEffect, onTransitionEnd, ariaLabel, ariaControls, hasPopup, href, download, disabled, nonInteractive, allowDisabledClick, noFastClick = color === 'danger', ripple, faded, tabIndex, isRtl, isRectangular, noPreventDefault, shouldStopPropagation, noForcedUpperCase, style, }) => { let elementRef = useRef(); if (ref) { elementRef = ref; } const lang = useOldLang(); const [isClicked, setIsClicked] = useState(false); const isNotInteractive = disabled || nonInteractive; const fullClassName = buildClassName( 'Button', className, size, color, round && 'round', pill && 'pill', fluid && 'fluid', badge && 'badge', isNotInteractive && 'disabled', nonInteractive && 'non-interactive', allowDisabledClick && 'click-allowed', isText && 'text', isLoading && 'loading', ripple && 'has-ripple', faded && 'faded', isClicked && 'clicked', backgroundImage && 'with-image', isShiny && 'shiny', withPremiumGradient && 'premium', isRectangular && 'rectangular', noForcedUpperCase && 'no-upper-case', ); const handleClick = useLastCallback((e: ReactMouseEvent) => { if ((allowDisabledClick || !isNotInteractive) && onClick) { onClick(e); } if (shouldStopPropagation) e.stopPropagation(); setIsClicked(true); setTimeout(() => { setIsClicked(false); }, CLICKED_TIMEOUT); }); const handleMouseDown = useLastCallback((e: ReactMouseEvent) => { if (!noPreventDefault) e.preventDefault(); if ((allowDisabledClick || !isNotInteractive) && onMouseDown) { onMouseDown(e); } if (!IS_TOUCH_ENV && e.button === MouseButton.Main && !noFastClick) { handleClick(e); } }); const content = ( <> {withSparkleEffect && } {isLoading ? (
{lang('Cache.ClearProgress')}
) : children} {!isNotInteractive && ripple && ( )} ); if (href) { return ( } id={id} className={fullClassName} href={href} title={ariaLabel} download={download} tabIndex={tabIndex} dir={isRtl ? 'rtl' : undefined} aria-label={ariaLabel} aria-controls={ariaControls} style={style} onTransitionEnd={onTransitionEnd} target="_blank" rel="noreferrer" > {content} ); } return ( ); }; export default Button;