mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
feat: image support (#4011)
Co-authored-by: Emil Atanasov <heitara@gmail.com> Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>
This commit is contained in:
parent
0f0244224d
commit
163ad1f4c4
85 changed files with 3536 additions and 618 deletions
|
@ -1,8 +1,11 @@
|
|||
import "./ToolIcon.scss";
|
||||
|
||||
import React from "react";
|
||||
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";
|
||||
|
||||
|
@ -28,7 +31,7 @@ type ToolButtonProps =
|
|||
| (ToolButtonBaseProps & {
|
||||
type: "button";
|
||||
children?: React.ReactNode;
|
||||
onClick?(): void;
|
||||
onClick?(event: React.MouseEvent): void;
|
||||
})
|
||||
| (ToolButtonBaseProps & {
|
||||
type: "icon";
|
||||
|
@ -38,7 +41,7 @@ type ToolButtonProps =
|
|||
| (ToolButtonBaseProps & {
|
||||
type: "radio";
|
||||
checked: boolean;
|
||||
onChange?(): void;
|
||||
onChange?(data: { pointerType: PointerType | null }): void;
|
||||
});
|
||||
|
||||
export const ToolButton = React.forwardRef((props: ToolButtonProps, ref) => {
|
||||
|
@ -47,6 +50,38 @@ export const ToolButton = React.forwardRef((props: ToolButtonProps, ref) => {
|
|||
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) {
|
||||
if (!(error instanceof AbortError)) {
|
||||
throw error;
|
||||
}
|
||||
} finally {
|
||||
if (isMountedRef.current) {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(
|
||||
() => () => {
|
||||
isMountedRef.current = false;
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const lastPointerTypeRef = useRef<PointerType | null>(null);
|
||||
|
||||
if (props.type === "button" || props.type === "icon") {
|
||||
return (
|
||||
<button
|
||||
|
@ -68,8 +103,9 @@ export const ToolButton = React.forwardRef((props: ToolButtonProps, ref) => {
|
|||
title={props.title}
|
||||
aria-label={props["aria-label"]}
|
||||
type="button"
|
||||
onClick={props.onClick}
|
||||
onClick={onClick}
|
||||
ref={innerRef}
|
||||
disabled={isLoading}
|
||||
>
|
||||
{(props.icon || props.label) && (
|
||||
<div className="ToolIcon__icon" aria-hidden="true">
|
||||
|
@ -82,7 +118,9 @@ export const ToolButton = React.forwardRef((props: ToolButtonProps, ref) => {
|
|||
</div>
|
||||
)}
|
||||
{props.showAriaLabel && (
|
||||
<div className="ToolIcon__label">{props["aria-label"]}</div>
|
||||
<div className="ToolIcon__label">
|
||||
{props["aria-label"]} {isLoading && <Spinner />}
|
||||
</div>
|
||||
)}
|
||||
{props.children}
|
||||
</button>
|
||||
|
@ -90,7 +128,18 @@ export const ToolButton = React.forwardRef((props: ToolButtonProps, ref) => {
|
|||
}
|
||||
|
||||
return (
|
||||
<label className={clsx("ToolIcon", props.className)} title={props.title}>
|
||||
<label
|
||||
className={clsx("ToolIcon", props.className)}
|
||||
title={props.title}
|
||||
onPointerDown={(event) => {
|
||||
lastPointerTypeRef.current = event.pointerType || null;
|
||||
}}
|
||||
onPointerUp={() => {
|
||||
requestAnimationFrame(() => {
|
||||
lastPointerTypeRef.current = null;
|
||||
});
|
||||
}}
|
||||
>
|
||||
<input
|
||||
className={`ToolIcon_type_radio ${sizeCn}`}
|
||||
type="radio"
|
||||
|
@ -99,7 +148,9 @@ export const ToolButton = React.forwardRef((props: ToolButtonProps, ref) => {
|
|||
aria-keyshortcuts={props["aria-keyshortcuts"]}
|
||||
data-testid={props["data-testid"]}
|
||||
id={`${excalId}-${props.id}`}
|
||||
onChange={props.onChange}
|
||||
onChange={() => {
|
||||
props.onChange?.({ pointerType: lastPointerTypeRef.current });
|
||||
}}
|
||||
checked={props.checked}
|
||||
ref={innerRef}
|
||||
/>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue