Compare commits

...

9 Commits

Author SHA1 Message Date
Aakansha Doshi 016b054288 fix: Don't trigger onChange on initial load 2021-03-21 02:05:04 +05:30
David Luzar 94ad8eaa19 feat: support pasting file contents & always prefer system clip (#3257) 2021-03-20 20:20:47 +01:00
Aakansha Doshi 13d9374cde fix: Don't show export and delete when library is empty (#3288) 2021-03-20 21:58:37 +05:30
Aakansha Doshi efb6d0825b feat: Add label for name field and use input when editable in export dialog (#3286)
* feat: Add label for name field and use input when editable in export dialog

* fix

* review fix

* dnt allow to edit file name when view mode

* Update src/components/ProjectName.tsx

Co-authored-by: David Luzar <luzar.david@gmail.com>

Co-authored-by: David Luzar <luzar.david@gmail.com>
2021-03-20 21:57:58 +05:30
Aakansha Doshi 80a61db72f fix: overflow in textinput in export dialog (#3284)
* fix: overflow in textinput in export dialog

* use width
2021-03-20 18:21:48 +05:30
David Luzar 9a13dd8836 fix: bail on noop updates for newElementWith (#3279) 2021-03-20 13:29:53 +01:00
David Luzar cf6a5ff16b fix: state continuously updated when holding ctrl/cmd (#3283) 2021-03-20 13:28:28 +01:00
David Luzar fa8c7abf50 fix: debounce.flush not invoked if lastArgs not defined (#3281) 2021-03-20 13:15:28 +01:00
Arun c3ecbcb3ab feat: Allow host app to update title of drawing (#3273)
* Allow updating name on updateScene

* Revert "Allow updating name on updateScene"

This reverts commit 4e07a608d3.

* Make requested changes

* Make requested changes

* Remove customName from state

* Remove redundant if statement

* Add tests, update changelog and minor fixes

* remove eempty lines

* minor fixes

* no border and on hover no background change

* Give preference to name prop when initialData.appState.name is present and update specs

* minor fix

* Fix name input style in dark mode

Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>
2021-03-20 16:08:03 +05:30
25 changed files with 356 additions and 474 deletions
+4 -1
View File
@@ -18,11 +18,14 @@ export const actionChangeProjectName = register({
trackEvent("change", "title"); trackEvent("change", "title");
return { appState: { ...appState, name: value }, commitToHistory: false }; return { appState: { ...appState, name: value }, commitToHistory: false };
}, },
PanelComponent: ({ appState, updateData }) => ( PanelComponent: ({ appState, updateData, appProps }) => (
<ProjectName <ProjectName
label={t("labels.fileTitle")} label={t("labels.fileTitle")}
value={appState.name || "Unnamed"} value={appState.name || "Unnamed"}
onChange={(name: string) => updateData(name)} onChange={(name: string) => updateData(name)}
isNameEditable={
typeof appProps.name === "undefined" && !appState.viewModeEnabled
}
/> />
), ),
}); });
+1
View File
@@ -122,6 +122,7 @@ export class ActionManager implements ActionsManagerInterface {
appState={this.getAppState()} appState={this.getAppState()}
updateData={updateData} updateData={updateData}
id={id} id={id}
appProps={this.app.props}
/> />
); );
} }
+2 -1
View File
@@ -1,6 +1,6 @@
import React from "react"; import React from "react";
import { ExcalidrawElement } from "../element/types"; import { ExcalidrawElement } from "../element/types";
import { AppState } from "../types"; import { AppState, ExcalidrawProps } from "../types";
/** if false, the action should be prevented */ /** if false, the action should be prevented */
export type ActionResult = export type ActionResult =
@@ -94,6 +94,7 @@ export interface Action {
elements: readonly ExcalidrawElement[]; elements: readonly ExcalidrawElement[];
appState: AppState; appState: AppState;
updateData: (formData?: any) => void; updateData: (formData?: any) => void;
appProps: ExcalidrawProps;
id?: string; id?: string;
}>; }>;
perform: ActionFn; perform: ActionFn;
+14 -15
View File
@@ -7,12 +7,10 @@ import { AppState } from "./types";
import { SVG_EXPORT_TAG } from "./scene/export"; import { SVG_EXPORT_TAG } from "./scene/export";
import { tryParseSpreadsheet, Spreadsheet, VALID_SPREADSHEET } from "./charts"; import { tryParseSpreadsheet, Spreadsheet, VALID_SPREADSHEET } from "./charts";
import { canvasToBlob } from "./data/blob"; import { canvasToBlob } from "./data/blob";
import { EXPORT_DATA_TYPES } from "./constants";
const TYPE_ELEMENTS = "excalidraw/elements";
type ElementsClipboard = { type ElementsClipboard = {
type: typeof TYPE_ELEMENTS; type: typeof EXPORT_DATA_TYPES.excalidrawClipboard;
created: number;
elements: ExcalidrawElement[]; elements: ExcalidrawElement[];
}; };
@@ -31,8 +29,16 @@ export const probablySupportsClipboardBlob =
"ClipboardItem" in window && "ClipboardItem" in window &&
"toBlob" in HTMLCanvasElement.prototype; "toBlob" in HTMLCanvasElement.prototype;
const isElementsClipboard = (contents: any): contents is ElementsClipboard => { const clipboardContainsElements = (
if (contents?.type === TYPE_ELEMENTS) { contents: any,
): contents is { elements: ExcalidrawElement[] } => {
if (
[
EXPORT_DATA_TYPES.excalidraw,
EXPORT_DATA_TYPES.excalidrawClipboard,
].includes(contents?.type) &&
Array.isArray(contents.elements)
) {
return true; return true;
} }
return false; return false;
@@ -43,8 +49,7 @@ export const copyToClipboard = async (
appState: AppState, appState: AppState,
) => { ) => {
const contents: ElementsClipboard = { const contents: ElementsClipboard = {
type: TYPE_ELEMENTS, type: EXPORT_DATA_TYPES.excalidrawClipboard,
created: Date.now(),
elements: getSelectedElements(elements, appState), elements: getSelectedElements(elements, appState),
}; };
const json = JSON.stringify(contents); const json = JSON.stringify(contents);
@@ -131,15 +136,9 @@ export const parseClipboard = async (
try { try {
const systemClipboardData = JSON.parse(systemClipboard); const systemClipboardData = JSON.parse(systemClipboard);
// system clipboard elements are newer than in-app clipboard if (clipboardContainsElements(systemClipboardData)) {
if (
isElementsClipboard(systemClipboardData) &&
(!appClipboardData?.created ||
appClipboardData.created < systemClipboardData.created)
) {
return { elements: systemClipboardData.elements }; return { elements: systemClipboardData.elements };
} }
// in-app clipboard is newer than system clipboard
return appClipboardData; return appClipboardData;
} catch { } catch {
// system clipboard doesn't contain excalidraw elements → return plaintext // system clipboard doesn't contain excalidraw elements → return plaintext
+17 -3
View File
@@ -303,6 +303,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
zenModeEnabled = false, zenModeEnabled = false,
gridModeEnabled = false, gridModeEnabled = false,
theme = defaultAppState.theme, theme = defaultAppState.theme,
name = defaultAppState.name,
} = props; } = props;
this.state = { this.state = {
...defaultAppState, ...defaultAppState,
@@ -314,6 +315,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
viewModeEnabled, viewModeEnabled,
zenModeEnabled, zenModeEnabled,
gridSize: gridModeEnabled ? GRID_SIZE : null, gridSize: gridModeEnabled ? GRID_SIZE : null,
name,
}; };
if (excalidrawRef) { if (excalidrawRef) {
const readyPromise = const readyPromise =
@@ -523,7 +525,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
let zenModeEnabled = actionResult?.appState?.zenModeEnabled || false; let zenModeEnabled = actionResult?.appState?.zenModeEnabled || false;
let gridSize = actionResult?.appState?.gridSize || null; let gridSize = actionResult?.appState?.gridSize || null;
let theme = actionResult?.appState?.theme || "light"; let theme = actionResult?.appState?.theme || "light";
let name = actionResult?.appState?.name ?? this.state.name;
if (typeof this.props.viewModeEnabled !== "undefined") { if (typeof this.props.viewModeEnabled !== "undefined") {
viewModeEnabled = this.props.viewModeEnabled; viewModeEnabled = this.props.viewModeEnabled;
} }
@@ -540,6 +542,10 @@ class App extends React.Component<ExcalidrawProps, AppState> {
theme = this.props.theme; theme = this.props.theme;
} }
if (typeof this.props.name !== "undefined") {
name = this.props.name;
}
this.setState( this.setState(
(state) => { (state) => {
// using Object.assign instead of spread to fool TS 4.2.2+ into // using Object.assign instead of spread to fool TS 4.2.2+ into
@@ -556,6 +562,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
zenModeEnabled, zenModeEnabled,
gridSize, gridSize,
theme, theme,
name,
}); });
}, },
() => { () => {
@@ -890,6 +897,13 @@ class App extends React.Component<ExcalidrawProps, AppState> {
gridSize: this.props.gridModeEnabled ? GRID_SIZE : null, gridSize: this.props.gridModeEnabled ? GRID_SIZE : null,
}); });
} }
if (this.props.name && prevProps.name !== this.props.name) {
this.setState({
name: this.props.name,
});
}
document document
.querySelector(".excalidraw") .querySelector(".excalidraw")
?.classList.toggle("theme--dark", this.state.theme === "dark"); ?.classList.toggle("theme--dark", this.state.theme === "dark");
@@ -1008,7 +1022,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
// potential issues, this fixes a case where the tab isn't focused during // potential issues, this fixes a case where the tab isn't focused during
// init, which would trigger onChange with empty elements, which would then // init, which would trigger onChange with empty elements, which would then
// override whatever is in localStorage currently. // override whatever is in localStorage currently.
if (!this.state.isLoading) { if (!this.state.isLoading && !prevState.isLoading) {
this.props.onChange?.( this.props.onChange?.(
this.scene.getElementsIncludingDeleted(), this.scene.getElementsIncludingDeleted(),
this.state, this.state,
@@ -1390,7 +1404,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
return; return;
} }
if (event[KEYS.CTRL_OR_CMD]) { if (event[KEYS.CTRL_OR_CMD] && this.state.isBindingEnabled) {
this.setState({ isBindingEnabled: false }); this.setState({ isBindingEnabled: false });
} }
+18
View File
@@ -31,9 +31,27 @@
.ExportDialog__name { .ExportDialog__name {
grid-column: project-name; grid-column: project-name;
margin: auto; margin: auto;
display: flex;
align-items: center;
.TextInput { .TextInput {
height: calc(1rem - 3px); height: calc(1rem - 3px);
width: 200px;
overflow: hidden;
text-align: center;
margin-left: 8px;
text-overflow: ellipsis;
&--readonly {
background: none;
border: none;
&:hover {
background: none;
}
width: auto;
max-width: 200px;
padding-left: 2px;
}
} }
} }
+1
View File
@@ -257,6 +257,7 @@ export const ExportDialog = ({
onClick={() => { onClick={() => {
setModalIsShown(true); setModalIsShown(true);
}} }}
data-testid="export-button"
icon={exportFile} icon={exportFile}
type="button" type="button"
aria-label={t("buttons.export")} aria-label={t("buttons.export")}
+31 -28
View File
@@ -144,34 +144,37 @@ const LibraryMenuItems = ({
}); });
}} }}
/> />
<ToolButton {!!library.length && (
key="export" <>
type="button" <ToolButton
title={t("buttons.export")} key="export"
aria-label={t("buttons.export")} type="button"
icon={exportFile} title={t("buttons.export")}
onClick={() => { aria-label={t("buttons.export")}
saveLibraryAsJSON() icon={exportFile}
.catch(muteFSAbortError) onClick={() => {
.catch((error) => { saveLibraryAsJSON()
setAppState({ errorMessage: error.message }); .catch(muteFSAbortError)
}); .catch((error) => {
}} setAppState({ errorMessage: error.message });
/> });
<ToolButton }}
key="reset" />
type="button" <ToolButton
title={t("buttons.resetLibrary")} key="reset"
aria-label={t("buttons.resetLibrary")} type="button"
icon={trash} title={t("buttons.resetLibrary")}
onClick={() => { aria-label={t("buttons.resetLibrary")}
if (window.confirm(t("alerts.resetLibrary"))) { icon={trash}
Library.resetLibrary(); onClick={() => {
setLibraryItems([]); if (window.confirm(t("alerts.resetLibrary"))) {
} Library.resetLibrary();
}} setLibraryItems([]);
/> }
}}
/>
</>
)}
<a <a
href={`https://libraries.excalidraw.com?referrer=${referrer}`} href={`https://libraries.excalidraw.com?referrer=${referrer}`}
target="_excalidraw_libraries" target="_excalidraw_libraries"
+30 -31
View File
@@ -1,25 +1,26 @@
import "./TextInput.scss"; import "./TextInput.scss";
import React, { Component } from "react"; import React, { Component } from "react";
import { selectNode, removeSelection } from "../utils";
type Props = { type Props = {
value: string; value: string;
onChange: (value: string) => void; onChange: (value: string) => void;
label: string; label: string;
isNameEditable: boolean;
}; };
export class ProjectName extends Component<Props> { type State = {
private handleFocus = (event: React.FocusEvent<HTMLElement>) => { fileName: string;
selectNode(event.currentTarget); };
export class ProjectName extends Component<Props, State> {
state = {
fileName: this.props.value,
}; };
private handleBlur = (event: any) => {
private handleBlur = (event: React.FocusEvent<HTMLElement>) => { const value = event.target.value;
const value = event.currentTarget.innerText.trim();
if (value !== this.props.value) { if (value !== this.props.value) {
this.props.onChange(value); this.props.onChange(value);
} }
removeSelection();
}; };
private handleKeyDown = (event: React.KeyboardEvent<HTMLElement>) => { private handleKeyDown = (event: React.KeyboardEvent<HTMLElement>) => {
@@ -31,32 +32,30 @@ export class ProjectName extends Component<Props> {
event.currentTarget.blur(); event.currentTarget.blur();
} }
}; };
private makeEditable = (editable: HTMLSpanElement | null) => {
if (!editable) {
return;
}
try {
editable.contentEditable = "plaintext-only";
} catch {
editable.contentEditable = "true";
}
};
public render() { public render() {
return ( return (
<span <>
suppressContentEditableWarning <label htmlFor="file-name">
ref={this.makeEditable} {`${this.props.label}${this.props.isNameEditable ? "" : ":"}`}
data-type="wysiwyg" </label>
className="TextInput" {this.props.isNameEditable ? (
role="textbox" <input
aria-label={this.props.label} className="TextInput"
onBlur={this.handleBlur} onBlur={this.handleBlur}
onKeyDown={this.handleKeyDown} onKeyDown={this.handleKeyDown}
onFocus={this.handleFocus} id="file-name"
> value={this.state.fileName}
{this.props.value} onChange={(event) =>
</span> this.setState({ fileName: event.target.value })
}
/>
) : (
<span className="TextInput TextInput--readonly" id="file-name">
{this.props.value}
</span>
)}
</>
); );
} }
} }
+1
View File
@@ -58,6 +58,7 @@ export const ToolButton = React.forwardRef((props: ToolButtonProps, ref) => {
"ToolIcon--selected": props.selected, "ToolIcon--selected": props.selected,
}, },
)} )}
data-testid={props["data-testid"]}
hidden={props.hidden} hidden={props.hidden}
title={props.title} title={props.title}
aria-label={props["aria-label"]} aria-label={props["aria-label"]}
+7 -1
View File
@@ -84,9 +84,15 @@ export const MIME_TYPES = {
excalidrawlib: "application/vnd.excalidrawlib+json", excalidrawlib: "application/vnd.excalidrawlib+json",
}; };
export const EXPORT_DATA_TYPES = {
excalidraw: "excalidraw",
excalidrawClipboard: "excalidraw/clipboard",
excalidrawLibrary: "excalidrawlib",
} as const;
export const STORAGE_KEYS = { export const STORAGE_KEYS = {
LOCAL_STORAGE_LIBRARY: "excalidraw-library", LOCAL_STORAGE_LIBRARY: "excalidraw-library",
}; } as const;
// time in milliseconds // time in milliseconds
export const TAP_TWICE_TIMEOUT = 300; export const TAP_TWICE_TIMEOUT = 300;
+2 -2
View File
@@ -1,5 +1,5 @@
import { cleanAppStateForExport } from "../appState"; import { cleanAppStateForExport } from "../appState";
import { MIME_TYPES } from "../constants"; import { EXPORT_DATA_TYPES, MIME_TYPES } from "../constants";
import { clearElementsForExport } from "../element"; import { clearElementsForExport } from "../element";
import { CanvasError } from "../errors"; import { CanvasError } from "../errors";
import { t } from "../i18n"; import { t } from "../i18n";
@@ -121,7 +121,7 @@ export const loadFromBlob = async (
export const loadLibraryFromBlob = async (blob: Blob) => { export const loadLibraryFromBlob = async (blob: Blob) => {
const contents = await parseFileContents(blob); const contents = await parseFileContents(blob);
const data: LibraryData = JSON.parse(contents); const data: LibraryData = JSON.parse(contents);
if (data.type !== "excalidrawlib") { if (data.type !== EXPORT_DATA_TYPES.excalidrawLibrary) {
throw new Error(t("alerts.couldNotLoadInvalidFile")); throw new Error(t("alerts.couldNotLoadInvalidFile"));
} }
return data; return data;
+9 -3
View File
@@ -2,7 +2,7 @@ import decodePng from "png-chunks-extract";
import tEXt from "png-chunk-text"; import tEXt from "png-chunk-text";
import encodePng from "png-chunks-encode"; import encodePng from "png-chunks-encode";
import { stringToBase64, encode, decode, base64ToString } from "./encode"; import { stringToBase64, encode, decode, base64ToString } from "./encode";
import { MIME_TYPES } from "../constants"; import { EXPORT_DATA_TYPES, MIME_TYPES } from "../constants";
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// PNG // PNG
@@ -67,7 +67,10 @@ export const decodePngMetadata = async (blob: Blob) => {
const encodedData = JSON.parse(metadata.text); const encodedData = JSON.parse(metadata.text);
if (!("encoded" in encodedData)) { if (!("encoded" in encodedData)) {
// legacy, un-encoded scene JSON // legacy, un-encoded scene JSON
if ("type" in encodedData && encodedData.type === "excalidraw") { if (
"type" in encodedData &&
encodedData.type === EXPORT_DATA_TYPES.excalidraw
) {
return metadata.text; return metadata.text;
} }
throw new Error("FAILED"); throw new Error("FAILED");
@@ -115,7 +118,10 @@ export const decodeSvgMetadata = async ({ svg }: { svg: string }) => {
const encodedData = JSON.parse(json); const encodedData = JSON.parse(json);
if (!("encoded" in encodedData)) { if (!("encoded" in encodedData)) {
// legacy, un-encoded scene JSON // legacy, un-encoded scene JSON
if ("type" in encodedData && encodedData.type === "excalidraw") { if (
"type" in encodedData &&
encodedData.type === EXPORT_DATA_TYPES.excalidraw
) {
return json; return json;
} }
throw new Error("FAILED"); throw new Error("FAILED");
+5 -5
View File
@@ -1,6 +1,6 @@
import { fileOpen, fileSave } from "browser-fs-access"; import { fileOpen, fileSave } from "browser-fs-access";
import { cleanAppStateForExport } from "../appState"; import { cleanAppStateForExport } from "../appState";
import { MIME_TYPES } from "../constants"; import { EXPORT_DATA_TYPES, MIME_TYPES } from "../constants";
import { clearElementsForExport } from "../element"; import { clearElementsForExport } from "../element";
import { ExcalidrawElement } from "../element/types"; import { ExcalidrawElement } from "../element/types";
import { AppState } from "../types"; import { AppState } from "../types";
@@ -14,7 +14,7 @@ export const serializeAsJSON = (
): string => ): string =>
JSON.stringify( JSON.stringify(
{ {
type: "excalidraw", type: EXPORT_DATA_TYPES.excalidraw,
version: 2, version: 2,
source: window.location.origin, source: window.location.origin,
elements: clearElementsForExport(elements), elements: clearElementsForExport(elements),
@@ -69,7 +69,7 @@ export const isValidExcalidrawData = (data?: {
appState?: any; appState?: any;
}): data is ImportedDataState => { }): data is ImportedDataState => {
return ( return (
data?.type === "excalidraw" && data?.type === EXPORT_DATA_TYPES.excalidraw &&
(!data.elements || (!data.elements ||
(Array.isArray(data.elements) && (Array.isArray(data.elements) &&
(!data.appState || typeof data.appState === "object"))) (!data.appState || typeof data.appState === "object")))
@@ -80,7 +80,7 @@ export const isValidLibrary = (json: any) => {
return ( return (
typeof json === "object" && typeof json === "object" &&
json && json &&
json.type === "excalidrawlib" && json.type === EXPORT_DATA_TYPES.excalidrawLibrary &&
json.version === 1 json.version === 1
); );
}; };
@@ -89,7 +89,7 @@ export const saveLibraryAsJSON = async () => {
const library = await Library.loadLibrary(); const library = await Library.loadLibrary();
const serialized = JSON.stringify( const serialized = JSON.stringify(
{ {
type: "excalidrawlib", type: EXPORT_DATA_TYPES.excalidrawLibrary,
version: 1, version: 1,
library, library,
}, },
+27 -6
View File
@@ -87,9 +87,30 @@ export const mutateElement = <TElement extends Mutable<ExcalidrawElement>>(
export const newElementWith = <TElement extends ExcalidrawElement>( export const newElementWith = <TElement extends ExcalidrawElement>(
element: TElement, element: TElement,
updates: ElementUpdate<TElement>, updates: ElementUpdate<TElement>,
): TElement => ({ ): TElement => {
...element, let didChange = false;
...updates, for (const key in updates) {
version: element.version + 1, const value = (updates as any)[key];
versionNonce: randomInteger(), if (typeof value !== "undefined") {
}); if (
(element as any)[key] === value &&
// if object, always update in case its deep prop was mutated
(typeof value !== "object" || value === null || key === "groupIds")
) {
continue;
}
didChange = true;
}
}
if (!didChange) {
return element;
}
return {
...element,
...updates,
version: element.version + 1,
versionNonce: randomInteger(),
};
};
+1 -1
View File
@@ -61,7 +61,7 @@
"architect": "Architect", "architect": "Architect",
"artist": "Artist", "artist": "Artist",
"cartoonist": "Cartoonist", "cartoonist": "Cartoonist",
"fileTitle": "File title", "fileTitle": "File name",
"colorPicker": "Color picker", "colorPicker": "Color picker",
"canvasBackground": "Canvas background", "canvasBackground": "Canvas background",
"drawingCanvas": "Drawing canvas", "drawingCanvas": "Drawing canvas",
+1
View File
@@ -18,6 +18,7 @@ Please add the latest change on the top under the correct section.
### Features ### Features
- Add `name` prop to indicate the name of the drawing which will be used when exporting the drawing. When supplied, the value takes precedence over `intialData.appState.name`, the `name` will be fully controlled by host app and the users won't be able to edit from within Excalidraw [#3273](https://github.com/excalidraw/excalidraw/pull/3273).
- Export API `setCanvasOffsets` via `ref` to set the offsets for Excalidraw[#3265](https://github.com/excalidraw/excalidraw/pull/3265). - Export API `setCanvasOffsets` via `ref` to set the offsets for Excalidraw[#3265](https://github.com/excalidraw/excalidraw/pull/3265).
#### BREAKING CHANGE #### BREAKING CHANGE
- `offsetLeft` and `offsetTop` props have been removed now so you have to use the `setCanvasOffsets` via `ref` to achieve the same. - `offsetLeft` and `offsetTop` props have been removed now so you have to use the `setCanvasOffsets` via `ref` to achieve the same.
+5
View File
@@ -376,6 +376,7 @@ export default function IndexPage() {
| [`gridModeEnabled`](#gridModeEnabled) | boolean | | This implies if the grid mode is enabled | | [`gridModeEnabled`](#gridModeEnabled) | boolean | | This implies if the grid mode is enabled |
| [`libraryReturnUrl`](#libraryReturnUrl) | string | | What URL should [libraries.excalidraw.com](https://libraries.excalidraw.com) be installed to | | [`libraryReturnUrl`](#libraryReturnUrl) | string | | What URL should [libraries.excalidraw.com](https://libraries.excalidraw.com) be installed to |
| [`theme`](#theme) | `light` or `dark` | | The theme of the Excalidraw component | | [`theme`](#theme) | `light` or `dark` | | The theme of the Excalidraw component |
| [`name`](#name) | string | | Name of the drawing |
#### `width` #### `width`
@@ -534,6 +535,10 @@ If supplied, this URL will be used when user tries to install a library from [li
This prop controls Excalidraw's theme. When supplied, the value takes precedence over `intialData.appState.theme`, the theme will be fully controlled by the host app, and users won't be able to toggle it from within the app. This prop controls Excalidraw's theme. When supplied, the value takes precedence over `intialData.appState.theme`, the theme will be fully controlled by the host app, and users won't be able to toggle it from within the app.
### `name`
This prop sets the name of the drawing which will be used when exporting the drawing. When supplied, the value takes precedence over `intialData.appState.name`, the `name` will be fully controlled by host app and the users won't be able to edit from within Excalidraw.
### Extra API's ### Extra API's
#### `getSceneVersion` #### `getSceneVersion`
+2
View File
@@ -29,6 +29,7 @@ const Excalidraw = (props: ExcalidrawProps) => {
gridModeEnabled, gridModeEnabled,
libraryReturnUrl, libraryReturnUrl,
theme, theme,
name,
} = props; } = props;
useEffect(() => { useEffect(() => {
@@ -69,6 +70,7 @@ const Excalidraw = (props: ExcalidrawProps) => {
gridModeEnabled={gridModeEnabled} gridModeEnabled={gridModeEnabled}
libraryReturnUrl={libraryReturnUrl} libraryReturnUrl={libraryReturnUrl}
theme={theme} theme={theme}
name={name}
/> />
</IsMobileProvider> </IsMobileProvider>
</InitializeApp> </InitializeApp>
@@ -3357,8 +3357,8 @@ Object {
"strokeStyle": "solid", "strokeStyle": "solid",
"strokeWidth": 1, "strokeWidth": 1,
"type": "rectangle", "type": "rectangle",
"version": 8, "version": 4,
"versionNonce": 1116226695, "versionNonce": 453191,
"width": 10, "width": 10,
"x": 10, "x": 10,
"y": 10, "y": 10,
@@ -3429,7 +3429,7 @@ Object {
"elements": Array [ "elements": Array [
Object { Object {
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "#fa5252",
"boundElementIds": null, "boundElementIds": null,
"fillStyle": "hachure", "fillStyle": "hachure",
"groupIds": Array [], "groupIds": Array [],
@@ -3452,78 +3452,6 @@ Object {
}, },
], ],
}, },
Object {
"appState": Object {
"editingGroupId": null,
"editingLinearElement": null,
"name": "Untitled-201933152653",
"selectedElementIds": Object {
"id0": true,
},
"viewBackgroundColor": "#ffffff",
},
"elements": Array [
Object {
"angle": 0,
"backgroundColor": "#fa5252",
"boundElementIds": null,
"fillStyle": "hachure",
"groupIds": Array [],
"height": 10,
"id": "id0",
"isDeleted": false,
"opacity": 100,
"roughness": 1,
"seed": 337897,
"strokeColor": "#000000",
"strokeSharpness": "sharp",
"strokeStyle": "solid",
"strokeWidth": 1,
"type": "rectangle",
"version": 5,
"versionNonce": 401146281,
"width": 10,
"x": 10,
"y": 10,
},
],
},
Object {
"appState": Object {
"editingGroupId": null,
"editingLinearElement": null,
"name": "Untitled-201933152653",
"selectedElementIds": Object {
"id0": true,
},
"viewBackgroundColor": "#ffffff",
},
"elements": Array [
Object {
"angle": 0,
"backgroundColor": "#fa5252",
"boundElementIds": null,
"fillStyle": "hachure",
"groupIds": Array [],
"height": 10,
"id": "id0",
"isDeleted": false,
"opacity": 100,
"roughness": 1,
"seed": 337897,
"strokeColor": "#000000",
"strokeSharpness": "sharp",
"strokeStyle": "solid",
"strokeWidth": 1,
"type": "rectangle",
"version": 6,
"versionNonce": 2019559783,
"width": 10,
"x": 10,
"y": 10,
},
],
},
Object { Object {
"appState": Object { "appState": Object {
"editingGroupId": null, "editingGroupId": null,
@@ -3552,8 +3480,8 @@ Object {
"strokeStyle": "solid", "strokeStyle": "solid",
"strokeWidth": 1, "strokeWidth": 1,
"type": "rectangle", "type": "rectangle",
"version": 8, "version": 4,
"versionNonce": 1116226695, "versionNonce": 453191,
"width": 10, "width": 10,
"x": 10, "x": 10,
"y": 10, "y": 10,
@@ -14745,7 +14673,7 @@ Object {
"strokeWidth": 2, "strokeWidth": 2,
"type": "rectangle", "type": "rectangle",
"version": 3, "version": 3,
"versionNonce": 81784553, "versionNonce": 1505387817,
"width": 20, "width": 20,
"x": 10, "x": 10,
"y": 10, "y": 10,
@@ -14764,14 +14692,14 @@ Object {
"isDeleted": false, "isDeleted": false,
"opacity": 60, "opacity": 60,
"roughness": 2, "roughness": 2,
"seed": 23633383, "seed": 238820263,
"strokeColor": "#c92a2a", "strokeColor": "#c92a2a",
"strokeSharpness": "sharp", "strokeSharpness": "sharp",
"strokeStyle": "dotted", "strokeStyle": "dotted",
"strokeWidth": 2, "strokeWidth": 2,
"type": "rectangle", "type": "rectangle",
"version": 13, "version": 9,
"versionNonce": 915032327, "versionNonce": 1604849351,
"width": 20, "width": 20,
"x": 40, "x": 40,
"y": 40, "y": 40,
@@ -14934,7 +14862,7 @@ Object {
"opacity": 100, "opacity": 100,
"roughness": 1, "roughness": 1,
"seed": 449462985, "seed": 449462985,
"strokeColor": "#000000", "strokeColor": "#c92a2a",
"strokeSharpness": "sharp", "strokeSharpness": "sharp",
"strokeStyle": "solid", "strokeStyle": "solid",
"strokeWidth": 1, "strokeWidth": 1,
@@ -14981,6 +14909,42 @@ Object {
"x": 10, "x": 10,
"y": 10, "y": 10,
}, },
Object {
"angle": 0,
"backgroundColor": "#e64980",
"boundElementIds": null,
"fillStyle": "hachure",
"groupIds": Array [],
"height": 20,
"id": "id1",
"isDeleted": false,
"opacity": 100,
"roughness": 1,
"seed": 449462985,
"strokeColor": "#c92a2a",
"strokeSharpness": "sharp",
"strokeStyle": "solid",
"strokeWidth": 1,
"type": "rectangle",
"version": 4,
"versionNonce": 2019559783,
"width": 20,
"x": 40,
"y": 40,
},
],
},
Object {
"appState": Object {
"editingGroupId": null,
"editingLinearElement": null,
"name": "Untitled-201933152653",
"selectedElementIds": Object {
"id1": true,
},
"viewBackgroundColor": "#ffffff",
},
"elements": Array [
Object { Object {
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
@@ -14988,6 +14952,29 @@ Object {
"fillStyle": "hachure", "fillStyle": "hachure",
"groupIds": Array [], "groupIds": Array [],
"height": 20, "height": 20,
"id": "id0",
"isDeleted": false,
"opacity": 100,
"roughness": 1,
"seed": 337897,
"strokeColor": "#000000",
"strokeSharpness": "sharp",
"strokeStyle": "solid",
"strokeWidth": 1,
"type": "rectangle",
"version": 2,
"versionNonce": 1278240551,
"width": 20,
"x": 10,
"y": 10,
},
Object {
"angle": 0,
"backgroundColor": "#e64980",
"boundElementIds": null,
"fillStyle": "cross-hatch",
"groupIds": Array [],
"height": 20,
"id": "id1", "id": "id1",
"isDeleted": false, "isDeleted": false,
"opacity": 100, "opacity": 100,
@@ -15042,9 +15029,9 @@ Object {
}, },
Object { Object {
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "#e64980",
"boundElementIds": null, "boundElementIds": null,
"fillStyle": "hachure", "fillStyle": "cross-hatch",
"groupIds": Array [], "groupIds": Array [],
"height": 20, "height": 20,
"id": "id1", "id": "id1",
@@ -15055,7 +15042,7 @@ Object {
"strokeColor": "#c92a2a", "strokeColor": "#c92a2a",
"strokeSharpness": "sharp", "strokeSharpness": "sharp",
"strokeStyle": "solid", "strokeStyle": "solid",
"strokeWidth": 1, "strokeWidth": 2,
"type": "rectangle", "type": "rectangle",
"version": 6, "version": 6,
"versionNonce": 1116226695, "versionNonce": 1116226695,
@@ -15065,65 +15052,6 @@ Object {
}, },
], ],
}, },
Object {
"appState": Object {
"editingGroupId": null,
"editingLinearElement": null,
"name": "Untitled-201933152653",
"selectedElementIds": Object {
"id1": true,
},
"viewBackgroundColor": "#ffffff",
},
"elements": Array [
Object {
"angle": 0,
"backgroundColor": "transparent",
"boundElementIds": null,
"fillStyle": "hachure",
"groupIds": Array [],
"height": 20,
"id": "id0",
"isDeleted": false,
"opacity": 100,
"roughness": 1,
"seed": 337897,
"strokeColor": "#000000",
"strokeSharpness": "sharp",
"strokeStyle": "solid",
"strokeWidth": 1,
"type": "rectangle",
"version": 2,
"versionNonce": 1278240551,
"width": 20,
"x": 10,
"y": 10,
},
Object {
"angle": 0,
"backgroundColor": "#e64980",
"boundElementIds": null,
"fillStyle": "hachure",
"groupIds": Array [],
"height": 20,
"id": "id1",
"isDeleted": false,
"opacity": 100,
"roughness": 1,
"seed": 449462985,
"strokeColor": "#c92a2a",
"strokeSharpness": "sharp",
"strokeStyle": "solid",
"strokeWidth": 1,
"type": "rectangle",
"version": 8,
"versionNonce": 238820263,
"width": 20,
"x": 40,
"y": 40,
},
],
},
Object { Object {
"appState": Object { "appState": Object {
"editingGroupId": null, "editingGroupId": null,
@@ -15172,10 +15100,69 @@ Object {
"seed": 449462985, "seed": 449462985,
"strokeColor": "#c92a2a", "strokeColor": "#c92a2a",
"strokeSharpness": "sharp", "strokeSharpness": "sharp",
"strokeStyle": "dotted",
"strokeWidth": 2,
"type": "rectangle",
"version": 7,
"versionNonce": 1014066025,
"width": 20,
"x": 40,
"y": 40,
},
],
},
Object {
"appState": Object {
"editingGroupId": null,
"editingLinearElement": null,
"name": "Untitled-201933152653",
"selectedElementIds": Object {
"id1": true,
},
"viewBackgroundColor": "#ffffff",
},
"elements": Array [
Object {
"angle": 0,
"backgroundColor": "transparent",
"boundElementIds": null,
"fillStyle": "hachure",
"groupIds": Array [],
"height": 20,
"id": "id0",
"isDeleted": false,
"opacity": 100,
"roughness": 1,
"seed": 337897,
"strokeColor": "#000000",
"strokeSharpness": "sharp",
"strokeStyle": "solid", "strokeStyle": "solid",
"strokeWidth": 1, "strokeWidth": 1,
"type": "rectangle", "type": "rectangle",
"version": 9, "version": 2,
"versionNonce": 1278240551,
"width": 20,
"x": 10,
"y": 10,
},
Object {
"angle": 0,
"backgroundColor": "#e64980",
"boundElementIds": null,
"fillStyle": "cross-hatch",
"groupIds": Array [],
"height": 20,
"id": "id1",
"isDeleted": false,
"opacity": 100,
"roughness": 2,
"seed": 238820263,
"strokeColor": "#c92a2a",
"strokeSharpness": "sharp",
"strokeStyle": "dotted",
"strokeWidth": 2,
"type": "rectangle",
"version": 8,
"versionNonce": 400692809, "versionNonce": 400692809,
"width": 20, "width": 20,
"x": 40, "x": 40,
@@ -15183,183 +15170,6 @@ Object {
}, },
], ],
}, },
Object {
"appState": Object {
"editingGroupId": null,
"editingLinearElement": null,
"name": "Untitled-201933152653",
"selectedElementIds": Object {
"id1": true,
},
"viewBackgroundColor": "#ffffff",
},
"elements": Array [
Object {
"angle": 0,
"backgroundColor": "transparent",
"boundElementIds": null,
"fillStyle": "hachure",
"groupIds": Array [],
"height": 20,
"id": "id0",
"isDeleted": false,
"opacity": 100,
"roughness": 1,
"seed": 337897,
"strokeColor": "#000000",
"strokeSharpness": "sharp",
"strokeStyle": "solid",
"strokeWidth": 1,
"type": "rectangle",
"version": 2,
"versionNonce": 1278240551,
"width": 20,
"x": 10,
"y": 10,
},
Object {
"angle": 0,
"backgroundColor": "#e64980",
"boundElementIds": null,
"fillStyle": "cross-hatch",
"groupIds": Array [],
"height": 20,
"id": "id1",
"isDeleted": false,
"opacity": 100,
"roughness": 1,
"seed": 449462985,
"strokeColor": "#c92a2a",
"strokeSharpness": "sharp",
"strokeStyle": "solid",
"strokeWidth": 2,
"type": "rectangle",
"version": 10,
"versionNonce": 1604849351,
"width": 20,
"x": 40,
"y": 40,
},
],
},
Object {
"appState": Object {
"editingGroupId": null,
"editingLinearElement": null,
"name": "Untitled-201933152653",
"selectedElementIds": Object {
"id1": true,
},
"viewBackgroundColor": "#ffffff",
},
"elements": Array [
Object {
"angle": 0,
"backgroundColor": "transparent",
"boundElementIds": null,
"fillStyle": "hachure",
"groupIds": Array [],
"height": 20,
"id": "id0",
"isDeleted": false,
"opacity": 100,
"roughness": 1,
"seed": 337897,
"strokeColor": "#000000",
"strokeSharpness": "sharp",
"strokeStyle": "solid",
"strokeWidth": 1,
"type": "rectangle",
"version": 2,
"versionNonce": 1278240551,
"width": 20,
"x": 10,
"y": 10,
},
Object {
"angle": 0,
"backgroundColor": "#e64980",
"boundElementIds": null,
"fillStyle": "cross-hatch",
"groupIds": Array [],
"height": 20,
"id": "id1",
"isDeleted": false,
"opacity": 100,
"roughness": 1,
"seed": 449462985,
"strokeColor": "#c92a2a",
"strokeSharpness": "sharp",
"strokeStyle": "dotted",
"strokeWidth": 2,
"type": "rectangle",
"version": 11,
"versionNonce": 1505387817,
"width": 20,
"x": 40,
"y": 40,
},
],
},
Object {
"appState": Object {
"editingGroupId": null,
"editingLinearElement": null,
"name": "Untitled-201933152653",
"selectedElementIds": Object {
"id1": true,
},
"viewBackgroundColor": "#ffffff",
},
"elements": Array [
Object {
"angle": 0,
"backgroundColor": "transparent",
"boundElementIds": null,
"fillStyle": "hachure",
"groupIds": Array [],
"height": 20,
"id": "id0",
"isDeleted": false,
"opacity": 100,
"roughness": 1,
"seed": 337897,
"strokeColor": "#000000",
"strokeSharpness": "sharp",
"strokeStyle": "solid",
"strokeWidth": 1,
"type": "rectangle",
"version": 2,
"versionNonce": 1278240551,
"width": 20,
"x": 10,
"y": 10,
},
Object {
"angle": 0,
"backgroundColor": "#e64980",
"boundElementIds": null,
"fillStyle": "cross-hatch",
"groupIds": Array [],
"height": 20,
"id": "id1",
"isDeleted": false,
"opacity": 100,
"roughness": 2,
"seed": 23633383,
"strokeColor": "#c92a2a",
"strokeSharpness": "sharp",
"strokeStyle": "dotted",
"strokeWidth": 2,
"type": "rectangle",
"version": 12,
"versionNonce": 493213705,
"width": 20,
"x": 40,
"y": 40,
},
],
},
Object { Object {
"appState": Object { "appState": Object {
"editingGroupId": null, "editingGroupId": null,
@@ -15405,14 +15215,14 @@ Object {
"isDeleted": false, "isDeleted": false,
"opacity": 60, "opacity": 60,
"roughness": 2, "roughness": 2,
"seed": 23633383, "seed": 238820263,
"strokeColor": "#c92a2a", "strokeColor": "#c92a2a",
"strokeSharpness": "sharp", "strokeSharpness": "sharp",
"strokeStyle": "dotted", "strokeStyle": "dotted",
"strokeWidth": 2, "strokeWidth": 2,
"type": "rectangle", "type": "rectangle",
"version": 13, "version": 9,
"versionNonce": 915032327, "versionNonce": 1604849351,
"width": 20, "width": 20,
"x": 40, "x": 40,
"y": 40, "y": 40,
@@ -15448,7 +15258,7 @@ Object {
"strokeWidth": 2, "strokeWidth": 2,
"type": "rectangle", "type": "rectangle",
"version": 3, "version": 3,
"versionNonce": 81784553, "versionNonce": 1505387817,
"width": 20, "width": 20,
"x": 10, "x": 10,
"y": 10, "y": 10,
@@ -15464,14 +15274,14 @@ Object {
"isDeleted": false, "isDeleted": false,
"opacity": 60, "opacity": 60,
"roughness": 2, "roughness": 2,
"seed": 23633383, "seed": 238820263,
"strokeColor": "#c92a2a", "strokeColor": "#c92a2a",
"strokeSharpness": "sharp", "strokeSharpness": "sharp",
"strokeStyle": "dotted", "strokeStyle": "dotted",
"strokeWidth": 2, "strokeWidth": 2,
"type": "rectangle", "type": "rectangle",
"version": 13, "version": 9,
"versionNonce": 915032327, "versionNonce": 1604849351,
"width": 20, "width": 20,
"x": 40, "x": 40,
"y": 40, "y": 40,
@@ -17069,8 +16879,8 @@ Object {
"strokeStyle": "solid", "strokeStyle": "solid",
"strokeWidth": 1, "strokeWidth": 1,
"type": "rectangle", "type": "rectangle",
"version": 5, "version": 3,
"versionNonce": 401146281, "versionNonce": 449462985,
"width": 10, "width": 10,
"x": 0, "x": 0,
"y": 0, "y": 0,
@@ -17141,7 +16951,7 @@ Object {
"elements": Array [ "elements": Array [
Object { Object {
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "#fa5252",
"boundElementIds": null, "boundElementIds": null,
"fillStyle": "hachure", "fillStyle": "hachure",
"groupIds": Array [], "groupIds": Array [],
@@ -17164,42 +16974,6 @@ Object {
}, },
], ],
}, },
Object {
"appState": Object {
"editingGroupId": null,
"editingLinearElement": null,
"name": "Untitled-201933152653",
"selectedElementIds": Object {
"id0": true,
},
"viewBackgroundColor": "#ffffff",
},
"elements": Array [
Object {
"angle": 0,
"backgroundColor": "#fa5252",
"boundElementIds": null,
"fillStyle": "hachure",
"groupIds": Array [],
"height": 10,
"id": "id0",
"isDeleted": false,
"opacity": 100,
"roughness": 1,
"seed": 337897,
"strokeColor": "#000000",
"strokeSharpness": "sharp",
"strokeStyle": "solid",
"strokeWidth": 1,
"type": "rectangle",
"version": 5,
"versionNonce": 401146281,
"width": 10,
"x": 0,
"y": 0,
},
],
},
], ],
} }
`; `;
@@ -20607,7 +20381,7 @@ Object {
"strokeWidth": 1, "strokeWidth": 1,
"type": "rectangle", "type": "rectangle",
"version": 6, "version": 6,
"versionNonce": 1006504105, "versionNonce": 760410951,
"width": 20, "width": 20,
"x": 10, "x": 10,
"y": -10, "y": -10,
@@ -20633,7 +20407,7 @@ Object {
"strokeWidth": 1, "strokeWidth": 1,
"type": "rectangle", "type": "rectangle",
"version": 6, "version": 6,
"versionNonce": 289600103, "versionNonce": 1006504105,
"width": 30, "width": 30,
"x": 40, "x": 40,
"y": 0, "y": 0,
@@ -20676,8 +20450,8 @@ Object {
"strokeStyle": "solid", "strokeStyle": "solid",
"strokeWidth": 1, "strokeWidth": 1,
"type": "arrow", "type": "arrow",
"version": 11, "version": 9,
"versionNonce": 1315507081, "versionNonce": 81784553,
"width": 60, "width": 60,
"x": 130, "x": 130,
"y": 10, "y": 10,
+2 -1
View File
@@ -3,6 +3,7 @@ import { render, waitFor } from "./test-utils";
import ExcalidrawApp from "../excalidraw-app"; import ExcalidrawApp from "../excalidraw-app";
import { API } from "./helpers/api"; import { API } from "./helpers/api";
import { getDefaultAppState } from "../appState"; import { getDefaultAppState } from "../appState";
import { EXPORT_DATA_TYPES } from "../constants";
const { h } = window; const { h } = window;
@@ -29,7 +30,7 @@ describe("appState", () => {
new Blob( new Blob(
[ [
JSON.stringify({ JSON.stringify({
type: "excalidraw", type: EXPORT_DATA_TYPES.excalidraw,
appState: { appState: {
viewBackgroundColor: "#000", viewBackgroundColor: "#000",
}, },
+26
View File
@@ -3,6 +3,7 @@ import { fireEvent, GlobalTestState, render } from "./test-utils";
import Excalidraw from "../packages/excalidraw/index"; import Excalidraw from "../packages/excalidraw/index";
import { queryByText, queryByTestId } from "@testing-library/react"; import { queryByText, queryByTestId } from "@testing-library/react";
import { GRID_SIZE } from "../constants"; import { GRID_SIZE } from "../constants";
import { t } from "../i18n";
const { h } = window; const { h } = window;
@@ -104,4 +105,29 @@ describe("<Excalidraw/>", () => {
expect(queryByTestId(container, "toggle-dark-mode")).toBe(null); expect(queryByTestId(container, "toggle-dark-mode")).toBe(null);
}); });
}); });
describe("Test name prop", () => {
it('should allow editing name when the name prop is "undefined"', async () => {
const { container } = await render(<Excalidraw />);
fireEvent.click(queryByTestId(container, "export-button")!);
const textInput: HTMLInputElement | null = document.querySelector(
".ExportDialog__name .TextInput",
);
expect(textInput?.value).toContain(`${t("labels.untitled")}`);
expect(textInput?.nodeName).toBe("INPUT");
});
it('should set the name and not allow editing when the name prop is present"', async () => {
const name = "test";
const { container } = await render(<Excalidraw name={name} />);
await fireEvent.click(queryByTestId(container, "export-button")!);
const textInput = document.querySelector(
".ExportDialog__name .TextInput--readonly",
);
expect(textInput?.textContent).toEqual(name);
expect(textInput?.nodeName).toBe("SPAN");
});
});
}); });
+2 -1
View File
@@ -6,6 +6,7 @@ import { API } from "./helpers/api";
import { getDefaultAppState } from "../appState"; import { getDefaultAppState } from "../appState";
import { waitFor } from "@testing-library/react"; import { waitFor } from "@testing-library/react";
import { createUndoAction, createRedoAction } from "../actions/actionHistory"; import { createUndoAction, createRedoAction } from "../actions/actionHistory";
import { EXPORT_DATA_TYPES } from "../constants";
const { h } = window; const { h } = window;
@@ -76,7 +77,7 @@ describe("history", () => {
new Blob( new Blob(
[ [
JSON.stringify({ JSON.stringify({
type: "excalidraw", type: EXPORT_DATA_TYPES.excalidraw,
appState: { appState: {
...getDefaultAppState(), ...getDefaultAppState(),
viewBackgroundColor: "#000", viewBackgroundColor: "#000",
+1
View File
@@ -187,6 +187,7 @@ export interface ExcalidrawProps {
gridModeEnabled?: boolean; gridModeEnabled?: boolean;
libraryReturnUrl?: string; libraryReturnUrl?: string;
theme?: "dark" | "light"; theme?: "dark" | "light";
name?: string;
} }
export type SceneData = { export type SceneData = {
+1 -3
View File
@@ -131,9 +131,7 @@ export const debounce = <T extends any[]>(
}; };
ret.flush = () => { ret.flush = () => {
clearTimeout(handle); clearTimeout(handle);
if (lastArgs) { fn(...(lastArgs || []));
fn(...lastArgs);
}
}; };
ret.cancel = () => { ret.cancel = () => {
clearTimeout(handle); clearTimeout(handle);