浏览代码

Revert "Remove unused project name from export dialog (#2427)" (#2436)

David Luzar 4 年之前
父节点
当前提交
36980160ae

+ 15 - 0
src/actions/actionExport.tsx

@@ -1,4 +1,5 @@
 import React from "react";
+import { ProjectName } from "../components/ProjectName";
 import { saveAsJSON, loadFromJSON } from "../data";
 import { load, save, saveAs } from "../components/icons";
 import { ToolButton } from "../components/ToolButton";
@@ -8,6 +9,20 @@ import { register } from "./register";
 import { KEYS } from "../keys";
 import { muteFSAbortError } from "../utils";
 
+export const actionChangeProjectName = register({
+  name: "changeProjectName",
+  perform: (_elements, appState, value) => {
+    return { appState: { ...appState, name: value }, commitToHistory: false };
+  },
+  PanelComponent: ({ appState, updateData }) => (
+    <ProjectName
+      label={t("labels.fileTitle")}
+      value={appState.name || "Unnamed"}
+      onChange={(name: string) => updateData(name)}
+    />
+  ),
+});
+
 export const actionChangeExportBackground = register({
   name: "changeExportBackground",
   perform: (_elements, appState, value) => {

+ 1 - 0
src/actions/index.ts

@@ -31,6 +31,7 @@ export {
 export { actionFinalize } from "./actionFinalize";
 
 export {
+  actionChangeProjectName,
   actionChangeExportBackground,
   actionSaveScene,
   actionSaveAsScene,

+ 1 - 0
src/actions/types.ts

@@ -42,6 +42,7 @@ export type ActionName =
   | "undo"
   | "redo"
   | "finalize"
+  | "changeProjectName"
   | "changeExportBackground"
   | "changeExportEmbedScene"
   | "changeShouldAddWatermark"

+ 4 - 0
src/appState.ts

@@ -1,5 +1,7 @@
 import oc from "open-color";
 import { AppState, FlooredNumber, NormalizedZoomValue } from "./types";
+import { getDateTime } from "./utils";
+import { t } from "./i18n";
 import {
   DEFAULT_FONT_SIZE,
   DEFAULT_FONT_FAMILY,
@@ -44,6 +46,7 @@ export const getDefaultAppState = (): Omit<
     cursorY: 0,
     cursorButton: "up",
     scrolledOutside: false,
+    name: `${t("labels.untitled")}-${getDateTime()}`,
     username: "",
     isBindingEnabled: true,
     isCollaborating: false,
@@ -125,6 +128,7 @@ const APP_STATE_STORAGE_CONF = (<
   isRotating: { browser: false, export: false },
   lastPointerDownWith: { browser: true, export: false },
   multiElement: { browser: false, export: false },
+  name: { browser: true, export: false },
   openMenu: { browser: true, export: false },
   previousSelectedElementIds: { browser: true, export: false },
   resizingElement: { browser: false, export: false },

+ 3 - 0
src/components/ExportDialog.tsx

@@ -165,6 +165,9 @@ const ExportModal = ({
               onClick={() => onExportToBackend(exportedElements)}
             />
           </Stack.Row>
+          <div className="ExportDialog__name">
+            {actionManager.renderAction("changeProjectName")}
+          </div>
           <Stack.Row gap={2}>
             {scales.map((s) => {
               const [width, height] = getExportSize(

+ 1 - 0
src/components/LayerUI.tsx

@@ -326,6 +326,7 @@ const LayerUI = ({
       if (canvas) {
         await exportCanvas(type, exportedElements, appState, canvas, {
           exportBackground: appState.exportBackground,
+          name: appState.name,
           viewBackgroundColor: appState.viewBackgroundColor,
           scale,
           shouldAddWatermark: appState.shouldAddWatermark,

+ 62 - 0
src/components/ProjectName.tsx

@@ -0,0 +1,62 @@
+import "./TextInput.scss";
+
+import React, { Component } from "react";
+import { selectNode, removeSelection } from "../utils";
+
+type Props = {
+  value: string;
+  onChange: (value: string) => void;
+  label: string;
+};
+
+export class ProjectName extends Component<Props> {
+  private handleFocus = (event: React.FocusEvent<HTMLElement>) => {
+    selectNode(event.currentTarget);
+  };
+
+  private handleBlur = (event: React.FocusEvent<HTMLElement>) => {
+    const value = event.currentTarget.innerText.trim();
+    if (value !== this.props.value) {
+      this.props.onChange(value);
+    }
+    removeSelection();
+  };
+
+  private handleKeyDown = (event: React.KeyboardEvent<HTMLElement>) => {
+    if (event.key === "Enter") {
+      event.preventDefault();
+      if (event.nativeEvent.isComposing || event.keyCode === 229) {
+        return;
+      }
+      event.currentTarget.blur();
+    }
+  };
+  private makeEditable = (editable: HTMLSpanElement | null) => {
+    if (!editable) {
+      return;
+    }
+    try {
+      editable.contentEditable = "plaintext-only";
+    } catch {
+      editable.contentEditable = "true";
+    }
+  };
+
+  public render() {
+    return (
+      <span
+        suppressContentEditableWarning
+        ref={this.makeEditable}
+        data-type="wysiwyg"
+        className="TextInput"
+        role="textbox"
+        aria-label={this.props.label}
+        onBlur={this.handleBlur}
+        onKeyDown={this.handleKeyDown}
+        onFocus={this.handleFocus}
+      >
+        {this.props.value}
+      </span>
+    );
+  }
+}

+ 5 - 0
src/data/index.ts

@@ -283,12 +283,14 @@ export const exportCanvas = async (
     exportBackground,
     exportPadding = 10,
     viewBackgroundColor,
+    name,
     scale = 1,
     shouldAddWatermark,
   }: {
     exportBackground: boolean;
     exportPadding?: number;
     viewBackgroundColor: string;
+    name: string;
     scale?: number;
     shouldAddWatermark: boolean;
   },
@@ -314,6 +316,7 @@ export const exportCanvas = async (
     });
     if (type === "svg") {
       await fileSave(new Blob([tempSvg.outerHTML], { type: "image/svg+xml" }), {
+        fileName: `${name}.svg`,
         extensions: [".svg"],
       });
       return;
@@ -334,6 +337,7 @@ export const exportCanvas = async (
   document.body.appendChild(tempCanvas);
 
   if (type === "png") {
+    const fileName = `${name}.png`;
     let blob = await canvasToBlob(tempCanvas);
     if (appState.exportEmbedScene) {
       blob = await (
@@ -345,6 +349,7 @@ export const exportCanvas = async (
     }
 
     await fileSave(blob, {
+      fileName,
       extensions: [".png"],
     });
   } else if (type === "clipboard") {

+ 1 - 0
src/data/json.ts

@@ -36,6 +36,7 @@ export const saveAsJSON = async (
   const fileHandle = await fileSave(
     blob,
     {
+      fileName: appState.name,
       description: "Excalidraw file",
       extensions: [".excalidraw"],
     },

+ 1 - 0
src/history.ts

@@ -24,6 +24,7 @@ const clearAppStatePropertiesForHistory = (appState: AppState) => {
     viewBackgroundColor: appState.viewBackgroundColor,
     editingLinearElement: appState.editingLinearElement,
     editingGroupId: appState.editingGroupId,
+    name: appState.name,
   };
 };
 

+ 2 - 0
src/locales/en.json

@@ -54,6 +54,7 @@
     "architect": "Architect",
     "artist": "Artist",
     "cartoonist": "Cartoonist",
+    "fileTitle": "File title",
     "colorPicker": "Color picker",
     "canvasBackground": "Canvas background",
     "drawingCanvas": "Drawing canvas",
@@ -62,6 +63,7 @@
     "language": "Language",
     "createRoom": "Share a live-collaboration session",
     "duplicateSelection": "Duplicate",
+    "untitled": "Untitled",
     "name": "Name",
     "yourName": "Your name",
     "madeWithExcalidraw": "Made with Excalidraw",

文件差异内容过多而无法显示
+ 126 - 0
src/tests/__snapshots__/regressionTests.test.tsx.snap


+ 1 - 0
src/types.ts

@@ -68,6 +68,7 @@ export type AppState = {
   cursorY: number;
   cursorButton: "up" | "down";
   scrolledOutside: boolean;
+  name: string;
   username: string;
   isCollaborating: boolean;
   isResizing: boolean;

部分文件因为文件数量过多而无法显示