Skip to content

Commit

Permalink
fix: remove t from getDefaultAppState and allow name to be nullable (e…
Browse files Browse the repository at this point in the history
…xcalidraw#7666)

* fix: remove t and allow name to be nullable

* pass name as required prop

* remove Unnamed

* pass name to excalidrawPlus as well for better type safe

* render once we have excalidrawAPI to avoid defaulting

* rename `getAppName` -> `getName` (temporary)

* stop preventing editing filenames when `props.name` supplied

* keep `name` as optional param for export functions

* keep `appState.name` on `props.name` state separate

* fix lint

* assertive first

* fix lint

* Add TODO

---------

Co-authored-by: dwelle <[email protected]>
  • Loading branch information
ad1992 and dwelle authored Feb 15, 2024
1 parent 48c3465 commit 73bf50e
Show file tree
Hide file tree
Showing 15 changed files with 101 additions and 81 deletions.
46 changes: 25 additions & 21 deletions excalidraw-app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -709,27 +709,30 @@ const ExcalidrawWrapper = () => {
toggleTheme: true,
export: {
onExportToBackend,
renderCustomUI: (elements, appState, files) => {
return (
<ExportToExcalidrawPlus
elements={elements}
appState={appState}
files={files}
onError={(error) => {
excalidrawAPI?.updateScene({
appState: {
errorMessage: error.message,
},
});
}}
onSuccess={() => {
excalidrawAPI?.updateScene({
appState: { openDialog: null },
});
}}
/>
);
},
renderCustomUI: excalidrawAPI
? (elements, appState, files) => {
return (
<ExportToExcalidrawPlus
elements={elements}
appState={appState}
files={files}
name={excalidrawAPI.getName()}
onError={(error) => {
excalidrawAPI?.updateScene({
appState: {
errorMessage: error.message,
},
});
}}
onSuccess={() => {
excalidrawAPI.updateScene({
appState: { openDialog: null },
});
}}
/>
);
}
: undefined,
},
},
}}
Expand Down Expand Up @@ -775,6 +778,7 @@ const ExcalidrawWrapper = () => {
excalidrawAPI.getSceneElements(),
excalidrawAPI.getAppState(),
excalidrawAPI.getFiles(),
excalidrawAPI.getName(),
);
}}
>
Expand Down
8 changes: 5 additions & 3 deletions excalidraw-app/components/ExportToExcalidrawPlus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export const exportToExcalidrawPlus = async (
elements: readonly NonDeletedExcalidrawElement[],
appState: Partial<AppState>,
files: BinaryFiles,
name: string,
) => {
const firebase = await loadFirebaseStorage();

Expand All @@ -53,7 +54,7 @@ export const exportToExcalidrawPlus = async (
.ref(`/migrations/scenes/${id}`)
.put(blob, {
customMetadata: {
data: JSON.stringify({ version: 2, name: appState.name }),
data: JSON.stringify({ version: 2, name }),
created: Date.now().toString(),
},
});
Expand Down Expand Up @@ -89,9 +90,10 @@ export const ExportToExcalidrawPlus: React.FC<{
elements: readonly NonDeletedExcalidrawElement[];
appState: Partial<AppState>;
files: BinaryFiles;
name: string;
onError: (error: Error) => void;
onSuccess: () => void;
}> = ({ elements, appState, files, onError, onSuccess }) => {
}> = ({ elements, appState, files, name, onError, onSuccess }) => {
const { t } = useI18n();
return (
<Card color="primary">
Expand All @@ -117,7 +119,7 @@ export const ExportToExcalidrawPlus: React.FC<{
onClick={async () => {
try {
trackEvent("export", "eplus", `ui (${getFrame()})`);
await exportToExcalidrawPlus(elements, appState, files);
await exportToExcalidrawPlus(elements, appState, files, name);
onSuccess();
} catch (error: any) {
console.error(error);
Expand Down
2 changes: 2 additions & 0 deletions packages/excalidraw/actions/actionClipboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ export const actionCopyAsSvg = register({
{
...appState,
exportingFrame,
name: app.getName(),
},
);
return {
Expand Down Expand Up @@ -184,6 +185,7 @@ export const actionCopyAsPng = register({
await exportCanvas("clipboard", exportedElements, appState, app.files, {
...appState,
exportingFrame,
name: app.getName(),
});
return {
appState: {
Expand Down
17 changes: 10 additions & 7 deletions packages/excalidraw/actions/actionExport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,11 @@ export const actionChangeProjectName = register({
perform: (_elements, appState, value) => {
return { appState: { ...appState, name: value }, commitToHistory: false };
},
PanelComponent: ({ appState, updateData, appProps, data }) => (
PanelComponent: ({ appState, updateData, appProps, data, app }) => (
<ProjectName
label={t("labels.fileTitle")}
value={appState.name || "Unnamed"}
value={app.getName()}
onChange={(name: string) => updateData(name)}
isNameEditable={
typeof appProps.name === "undefined" && !appState.viewModeEnabled
}
ignoreFocus={data?.ignoreFocus ?? false}
/>
),
Expand Down Expand Up @@ -144,8 +141,13 @@ export const actionSaveToActiveFile = register({

try {
const { fileHandle } = isImageFileHandle(appState.fileHandle)
? await resaveAsImageWithScene(elements, appState, app.files)
: await saveAsJSON(elements, appState, app.files);
? await resaveAsImageWithScene(
elements,
appState,
app.files,
app.getName(),
)
: await saveAsJSON(elements, appState, app.files, app.getName());

return {
commitToHistory: false,
Expand Down Expand Up @@ -190,6 +192,7 @@ export const actionSaveFileToDisk = register({
fileHandle: null,
},
app.files,
app.getName(),
);
return {
commitToHistory: false,
Expand Down
4 changes: 1 addition & 3 deletions packages/excalidraw/appState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ import {
EXPORT_SCALES,
THEME,
} from "./constants";
import { t } from "./i18n";
import { AppState, NormalizedZoomValue } from "./types";
import { getDateTime } from "./utils";

const defaultExportScale = EXPORT_SCALES.includes(devicePixelRatio)
? devicePixelRatio
Expand Down Expand Up @@ -65,7 +63,7 @@ export const getDefaultAppState = (): Omit<
isRotating: false,
lastPointerDownWith: "mouse",
multiElement: null,
name: `${t("labels.untitled")}-${getDateTime()}`,
name: null,
contextMenu: null,
openMenu: null,
openPopup: null,
Expand Down
26 changes: 13 additions & 13 deletions packages/excalidraw/components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ import {
updateStable,
addEventListener,
normalizeEOL,
getDateTime,
} from "../utils";
import {
createSrcDoc,
Expand Down Expand Up @@ -619,7 +620,7 @@ class App extends React.Component<AppProps, AppState> {
gridModeEnabled = false,
objectsSnapModeEnabled = false,
theme = defaultAppState.theme,
name = defaultAppState.name,
name = `${t("labels.untitled")}-${getDateTime()}`,
} = props;
this.state = {
...defaultAppState,
Expand Down Expand Up @@ -662,6 +663,7 @@ class App extends React.Component<AppProps, AppState> {
getSceneElements: this.getSceneElements,
getAppState: () => this.state,
getFiles: () => this.files,
getName: this.getName,
registerAction: (action: Action) => {
this.actionManager.registerAction(action);
},
Expand Down Expand Up @@ -1734,7 +1736,7 @@ class App extends React.Component<AppProps, AppState> {
this.files,
{
exportBackground: this.state.exportBackground,
name: this.state.name,
name: this.getName(),
viewBackgroundColor: this.state.viewBackgroundColor,
exportingFrame: opts.exportingFrame,
},
Expand Down Expand Up @@ -2133,7 +2135,7 @@ class App extends React.Component<AppProps, AppState> {
let gridSize = actionResult?.appState?.gridSize || null;
const theme =
actionResult?.appState?.theme || this.props.theme || THEME.LIGHT;
let name = actionResult?.appState?.name ?? this.state.name;
const name = actionResult?.appState?.name ?? this.state.name;
const errorMessage =
actionResult?.appState?.errorMessage ?? this.state.errorMessage;
if (typeof this.props.viewModeEnabled !== "undefined") {
Expand All @@ -2148,10 +2150,6 @@ class App extends React.Component<AppProps, AppState> {
gridSize = this.props.gridModeEnabled ? GRID_SIZE : null;
}

if (typeof this.props.name !== "undefined") {
name = this.props.name;
}

editingElement =
editingElement || actionResult.appState?.editingElement || null;

Expand Down Expand Up @@ -2709,12 +2707,6 @@ class App extends React.Component<AppProps, AppState> {
});
}

if (this.props.name && prevProps.name !== this.props.name) {
this.setState({
name: this.props.name,
});
}

this.excalidrawContainerRef.current?.classList.toggle(
"theme--dark",
this.state.theme === "dark",
Expand Down Expand Up @@ -4122,6 +4114,14 @@ class App extends React.Component<AppProps, AppState> {
return gesture.pointers.size >= 2;
};

public getName = () => {
return (
this.state.name ||
this.props.name ||
`${t("labels.untitled")}-${getDateTime()}`
);
};

// fires only on Safari
private onGestureStart = withBatchedUpdates((event: GestureEvent) => {
event.preventDefault();
Expand Down
13 changes: 6 additions & 7 deletions packages/excalidraw/components/ImageExportDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ import { Switch } from "./Switch";
import { Tooltip } from "./Tooltip";

import "./ImageExportDialog.scss";
import { useAppProps } from "./App";
import { FilledButton } from "./FilledButton";
import { cloneJSON } from "../utils";
import { prepareElementsForExport } from "../data";
Expand All @@ -58,6 +57,7 @@ type ImageExportModalProps = {
files: BinaryFiles;
actionManager: ActionManager;
onExportImage: AppClassProperties["onExportImage"];
name: string;
};

const ImageExportModal = ({
Expand All @@ -66,14 +66,14 @@ const ImageExportModal = ({
files,
actionManager,
onExportImage,
name,
}: ImageExportModalProps) => {
const hasSelection = isSomeElementSelected(
elementsSnapshot,
appStateSnapshot,
);

const appProps = useAppProps();
const [projectName, setProjectName] = useState(appStateSnapshot.name);
const [projectName, setProjectName] = useState(name);
const [exportSelectionOnly, setExportSelectionOnly] = useState(hasSelection);
const [exportWithBackground, setExportWithBackground] = useState(
appStateSnapshot.exportBackground,
Expand Down Expand Up @@ -158,10 +158,6 @@ const ImageExportModal = ({
className="TextInput"
value={projectName}
style={{ width: "30ch" }}
disabled={
typeof appProps.name !== "undefined" ||
appStateSnapshot.viewModeEnabled
}
onChange={(event) => {
setProjectName(event.target.value);
actionManager.executeAction(
Expand Down Expand Up @@ -347,13 +343,15 @@ export const ImageExportDialog = ({
actionManager,
onExportImage,
onCloseRequest,
name,
}: {
appState: UIAppState;
elements: readonly NonDeletedExcalidrawElement[];
files: BinaryFiles;
actionManager: ActionManager;
onExportImage: AppClassProperties["onExportImage"];
onCloseRequest: () => void;
name: string;
}) => {
// we need to take a snapshot so that the exported state can't be modified
// while the dialog is open
Expand All @@ -372,6 +370,7 @@ export const ImageExportDialog = ({
files={files}
actionManager={actionManager}
onExportImage={onExportImage}
name={name}
/>
</Dialog>
);
Expand Down
1 change: 1 addition & 0 deletions packages/excalidraw/components/LayerUI.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ const LayerUI = ({
actionManager={actionManager}
onExportImage={onExportImage}
onCloseRequest={() => setAppState({ openDialog: null })}
name={app.getName()}
/>
);
};
Expand Down
27 changes: 10 additions & 17 deletions packages/excalidraw/components/ProjectName.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ type Props = {
value: string;
onChange: (value: string) => void;
label: string;
isNameEditable: boolean;
ignoreFocus?: boolean;
};

Expand Down Expand Up @@ -42,23 +41,17 @@ export const ProjectName = (props: Props) => {
return (
<div className="ProjectName">
<label className="ProjectName-label" htmlFor="filename">
{`${props.label}${props.isNameEditable ? "" : ":"}`}
{`${props.label}:`}
</label>
{props.isNameEditable ? (
<input
type="text"
className="TextInput"
onBlur={handleBlur}
onKeyDown={handleKeyDown}
id={`${id}-filename`}
value={fileName}
onChange={(event) => setFileName(event.target.value)}
/>
) : (
<span className="TextInput TextInput--readonly" id={`${id}-filename`}>
{props.value}
</span>
)}
<input
type="text"
className="TextInput"
onBlur={handleBlur}
onKeyDown={handleKeyDown}
id={`${id}-filename`}
value={fileName}
onChange={(event) => setFileName(event.target.value)}
/>
</div>
);
};
6 changes: 6 additions & 0 deletions packages/excalidraw/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -381,3 +381,9 @@ export const EDITOR_LS_KEYS = {
MERMAID_TO_EXCALIDRAW: "mermaid-to-excalidraw",
PUBLISH_LIBRARY: "publish-library-data",
} as const;

/**
* not translated as this is used only in public, stateless API as default value
* where filename is optional and we can't retrieve name from app state
*/
export const DEFAULT_FILENAME = "Untitled";
Loading

0 comments on commit 73bf50e

Please sign in to comment.