diff --git a/src/components/EditableText.tsx b/src/components/EditableText.tsx new file mode 100644 index 00000000..cf32d6de --- /dev/null +++ b/src/components/EditableText.tsx @@ -0,0 +1,68 @@ +import React, { Fragment, Component } from "react"; + +type InputState = { + value: string; + edit: boolean; +}; + +type Props = { + value: string; + onChange: (value: string) => void; +}; + +export default class EditableText extends Component { + constructor(props: Props) { + super(props); + + this.state = { + value: props.value, + edit: false + }; + } + + componentWillReceiveProps(props: Props) { + this.setState({ value: props.value }); + } + + private handleEdit(e: React.ChangeEvent) { + this.setState({ value: e.target.value }); + } + + private handleBlur() { + const { value } = this.state; + + if (!value) { + this.setState({ value: this.props.value, edit: false }); + return; + } + this.props.onChange(value); + this.setState({ edit: false }); + } + + public render() { + const { value, edit } = this.state; + + return ( + + {edit ? ( + this.handleEdit(e)} + onBlur={() => this.handleBlur()} + autoFocus + /> + ) : ( + this.setState({ edit: true })} + className="project-name" + > + {value} + + )} + + ); + } +} diff --git a/src/index.tsx b/src/index.tsx index b308c6eb..5fa3060f 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -6,6 +6,8 @@ import { SketchPicker } from "react-color"; import { moveOneLeft, moveAllLeft, moveOneRight, moveAllRight } from "./zindex"; import { roundRect } from "./roundRect"; +import EditableText from "./components/EditableText"; +import { getDateTime } from "./utils"; import "./styles.scss"; @@ -22,6 +24,8 @@ const LOCAL_STORAGE_KEY_STATE = "excalidraw-state"; const elements = Array.of(); +const DEFAULT_PROJECT_NAME = `excalidraw-${getDateTime()}`; + let skipHistory = false; const stateHistory: string[] = []; function generateHistoryCurrentEntry() { @@ -529,7 +533,7 @@ function renderScene( } } -function saveAsJSON() { +function saveAsJSON(name: string) { const serialized = JSON.stringify({ version: 1, source: window.location.origin, @@ -537,7 +541,7 @@ function saveAsJSON() { }); saveFile( - "excalidraw.json", + `${name}.json`, "data:text/plain;charset=utf-8," + encodeURIComponent(serialized) ); } @@ -573,16 +577,17 @@ function loadFromJSON() { function exportAsPNG({ exportBackground, exportPadding = 10, - viewBackgroundColor + viewBackgroundColor, + name }: { exportBackground: boolean; exportPadding?: number; viewBackgroundColor: string; scrollX: number; scrollY: number; + name: string; }) { if (!elements.length) return window.alert("Cannot export empty canvas."); - // calculate smallest area to fit the contents in let subCanvasX1 = Infinity; @@ -623,7 +628,7 @@ function exportAsPNG({ } ); - saveFile("excalidraw.png", tempCanvas.toDataURL("image/png")); + saveFile(`${name}.png`, tempCanvas.toDataURL("image/png")); // clean up the DOM if (tempCanvas !== canvas) tempCanvas.remove(); @@ -872,6 +877,7 @@ type AppState = { viewBackgroundColor: string; scrollX: number; scrollY: number; + name: string; }; const KEYS = { @@ -1004,7 +1010,8 @@ class App extends React.Component<{}, AppState> { currentItemBackgroundColor: "#ffffff", viewBackgroundColor: "#ffffff", scrollX: 0, - scrollY: 0 + scrollY: 0, + name: DEFAULT_PROJECT_NAME }; private onResize = () => { @@ -1129,6 +1136,10 @@ class App extends React.Component<{}, AppState> { private removeWheelEventListener: (() => void) | undefined; + private updateProjectName(name: string): void { + this.setState({ name }); + } + public render() { const canvasWidth = window.innerWidth - CANVAS_WINDOW_OFFSET_LEFT; const canvasHeight = window.innerHeight - CANVAS_WINDOW_OFFSET_TOP; @@ -1349,11 +1360,20 @@ class App extends React.Component<{}, AppState> { background +

Project name

+
+ {this.state.name && ( + this.updateProjectName(name)} + /> + )} +

Save/Load