import classNames from 'classnames' import React, { useEffect, useRef } from 'react' import { MenuButton, useMenuState, Menu, MenuItem } from 'reakit' import CaretDownIcon from '~/icons/compiled/CaretDown' import IVSpinner from '~/components/IVSpinner' import { reportsAsAppleDevice } from '~/utils/usePlatform' import { useIsomorphicLink } from '~/utils/useIsomorphicLocation' interface IVButtonOption { label: string onClick?: () => void disabled?: boolean } export interface IVButtonProps { htmlFor?: string href?: string absolute?: boolean newTab?: boolean onClick?: (event: React.MouseEvent) => void disabled?: boolean loading?: boolean label?: React.ReactNode type?: 'button' | 'submit' | 'reset' theme?: 'primary' | 'secondary' | 'danger' | 'plain' condensed?: boolean className?: string id?: string tabIndex?: number autoFocus?: boolean title?: string reloadDocument?: boolean state?: Record options?: IVButtonOption[] shortcutKey?: string 'data-pw'?: string } export default function IVButton(props: IVButtonProps) { const { className = '', loading = false, disabled = false, type = 'button', theme = 'primary', condensed = false, tabIndex, autoFocus = false, onClick, title, reloadDocument, state, options: dropdownOptions, } = props const buttonRef = useRef(null) const linkRef = useRef(null) const Link = useIsomorphicLink() const dropdown = useMenuState({ placement: 'bottom-end', gutter: 4, }) useEffect(() => { if (autoFocus) { buttonRef.current?.focus() ?? linkRef.current?.focus() } }, [autoFocus]) const hasDropdown = !!dropdownOptions?.length // we apply these to the button and the optional dropdown button const focusClassName = 'focus-within:ring-offset-0 focus-within:ring-2 focus-within:outline-0 focus-within:outline-primary-500 focus-within:border-primary-500' const elementClassName = classNames( 'inline-flex border border-inherit justify-center relative outline-none ring-0 text-sm text-center w-full h-full', focusClassName, { 'text-white bg-primary-500': theme === 'primary', 'hover:bg-primary-400': theme === 'primary' && !disabled, 'bg-primary-200': theme === 'primary' && disabled && !loading, 'bg-primary-500 hover:bg-primary-500': theme === 'primary' && loading, 'text-gray-800': theme === 'secondary', 'bg-white hover:bg-gray-100': theme === 'secondary' && !disabled, 'bg-gray-100 opacity-75': theme === 'secondary' && disabled, 'text-white bg-red-500 focus-within:ring-primary-200 focus-within:border-red-700': theme === 'danger', 'hover:bg-red-400': theme === 'danger' && !disabled, 'bg-red-200': theme === 'danger' && disabled && !loading, 'bg-red-500 hover:bg-red-500': theme === 'danger' && loading, 'cursor-pointer': !disabled && !loading, 'cursor-default': disabled && !loading, 'rounded-md': !hasDropdown, 'rounded-l-md': hasDropdown, 'px-4 py-2': !condensed, 'px-3 py-1.5': condensed, 'font-medium': theme !== 'plain', } ) const labelClassName = classNames({ 'opacity-0 cursor-default': loading, 'opacity-100': !loading, }) const sharedProps = { tabIndex, title, id: props.id, className: elementClassName, state, 'data-pw': props['data-pw'], } const innerContents = ( ) return (
{props.href ? ( props.absolute ? ( {innerContents} ) : ( {innerContents} ) ) : props.htmlFor ? ( ) : disabled ? ( // The onmouseleave event isn't fired on disabled buttons, which interferes with tooltip behavior. {innerContents} ) : ( )} {!!dropdownOptions?.length && ( <> {dropdownOptions.map((option, key) => ( {option.label} ))} )}
) } function handleDisabledClick(event: React.MouseEvent) { event.preventDefault() event.stopPropagation() }