import "./ToolIcon.scss"; import React, { useEffect, useRef, useState } from "react"; import clsx from "clsx"; import { useExcalidrawContainer } from "./App"; import { AbortError } from "../errors"; import Spinner from "./Spinner"; import { PointerType } from "../element/types"; export type ToolButtonSize = "small" | "medium"; type ToolButtonBaseProps = { icon?: React.ReactNode; "aria-label": string; "aria-keyshortcuts"?: string; "data-testid"?: string; label?: string; title?: string; name?: string; id?: string; size?: ToolButtonSize; keyBindingLabel?: string; showAriaLabel?: boolean; hidden?: boolean; visible?: boolean; selected?: boolean; className?: string; }; type ToolButtonProps = | (ToolButtonBaseProps & { type: "button"; children?: React.ReactNode; onClick?(event: React.MouseEvent): void; }) | (ToolButtonBaseProps & { type: "icon"; children?: React.ReactNode; onClick?(): void; }) | (ToolButtonBaseProps & { type: "radio"; checked: boolean; onChange?(data: { pointerType: PointerType | null }): void; }); export const ToolButton = React.forwardRef((props: ToolButtonProps, ref) => { const { id: excalId } = useExcalidrawContainer(); const innerRef = React.useRef(null); React.useImperativeHandle(ref, () => innerRef.current); const sizeCn = `ToolIcon_size_${props.size}`; const [isLoading, setIsLoading] = useState(false); const isMountedRef = useRef(true); const onClick = async (event: React.MouseEvent) => { const ret = "onClick" in props && props.onClick?.(event); if (ret && "then" in ret) { try { setIsLoading(true); await ret; } catch (error: any) { if (!(error instanceof AbortError)) { throw error; } } finally { if (isMountedRef.current) { setIsLoading(false); } } } }; useEffect( () => () => { isMountedRef.current = false; }, [], ); const lastPointerTypeRef = useRef(null); if (props.type === "button" || props.type === "icon") { return ( ); } return ( ); }); ToolButton.defaultProps = { visible: true, className: "", size: "medium", };