mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
Add EditableText component
Added editable text component and use component for project name edit.
This commit is contained in:
parent
67e499321c
commit
24c99c3e1c
4 changed files with 121 additions and 4 deletions
74
src/components/EditableText.tsx
Normal file
74
src/components/EditableText.tsx
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
import React, { createRef, Fragment, Component } from "react";
|
||||||
|
|
||||||
|
type InputState = {
|
||||||
|
initialValue: string;
|
||||||
|
value: string;
|
||||||
|
edit: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
value: string;
|
||||||
|
updateName: (name: string) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default class EditableInput extends Component<Props, InputState> {
|
||||||
|
constructor(props: Props) {
|
||||||
|
super(props);
|
||||||
|
const { value } = props;
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
initialValue: value,
|
||||||
|
value,
|
||||||
|
edit: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private inputRef = createRef<HTMLInputElement>();
|
||||||
|
|
||||||
|
private handleEdit(e: React.ChangeEvent<HTMLInputElement>) {
|
||||||
|
this.setState({ value: e.target.value });
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleBlur() {
|
||||||
|
const { value, initialValue } = this.state;
|
||||||
|
|
||||||
|
if (!value) {
|
||||||
|
this.setState({ value: initialValue, edit: false });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.props.updateName(value);
|
||||||
|
this.setState({ edit: false, initialValue: value });
|
||||||
|
}
|
||||||
|
|
||||||
|
private focusInput() {
|
||||||
|
const input = this.inputRef.current;
|
||||||
|
if (input) {
|
||||||
|
input.focus();
|
||||||
|
}
|
||||||
|
this.setState({ edit: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const { value, edit } = this.state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
{edit ? (
|
||||||
|
<input
|
||||||
|
className="project-name-input"
|
||||||
|
name="name"
|
||||||
|
maxLength={25}
|
||||||
|
value={value}
|
||||||
|
onChange={e => this.handleEdit(e)}
|
||||||
|
onBlur={() => this.handleBlur()}
|
||||||
|
ref={this.inputRef}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<span onClick={() => this.focusInput()} className="project-name">
|
||||||
|
{value}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,8 @@ import { SketchPicker } from "react-color";
|
||||||
|
|
||||||
import { moveOneLeft, moveAllLeft, moveOneRight, moveAllRight } from "./zindex";
|
import { moveOneLeft, moveAllLeft, moveOneRight, moveAllRight } from "./zindex";
|
||||||
import { roundRect } from "./roundRect";
|
import { roundRect } from "./roundRect";
|
||||||
|
import EditableText from "./components/EditableText";
|
||||||
|
import { getDateTime } from "./utils";
|
||||||
|
|
||||||
import "./styles.scss";
|
import "./styles.scss";
|
||||||
|
|
||||||
|
@ -573,17 +575,17 @@ function loadFromJSON() {
|
||||||
function exportAsPNG({
|
function exportAsPNG({
|
||||||
exportBackground,
|
exportBackground,
|
||||||
exportPadding = 10,
|
exportPadding = 10,
|
||||||
viewBackgroundColor
|
viewBackgroundColor,
|
||||||
|
name
|
||||||
}: {
|
}: {
|
||||||
exportBackground: boolean;
|
exportBackground: boolean;
|
||||||
exportPadding?: number;
|
exportPadding?: number;
|
||||||
viewBackgroundColor: string;
|
viewBackgroundColor: string;
|
||||||
scrollX: number;
|
scrollX: number;
|
||||||
scrollY: number;
|
scrollY: number;
|
||||||
|
name: string;
|
||||||
}) {
|
}) {
|
||||||
if (!elements.length) return window.alert("Cannot export empty canvas.");
|
if (!elements.length) return window.alert("Cannot export empty canvas.");
|
||||||
|
|
||||||
const fileName = prompt("Enter name of project");
|
|
||||||
// calculate smallest area to fit the contents in
|
// calculate smallest area to fit the contents in
|
||||||
|
|
||||||
let subCanvasX1 = Infinity;
|
let subCanvasX1 = Infinity;
|
||||||
|
@ -873,6 +875,7 @@ type AppState = {
|
||||||
viewBackgroundColor: string;
|
viewBackgroundColor: string;
|
||||||
scrollX: number;
|
scrollX: number;
|
||||||
scrollY: number;
|
scrollY: number;
|
||||||
|
name: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const KEYS = {
|
const KEYS = {
|
||||||
|
@ -988,6 +991,11 @@ class App extends React.Component<{}, AppState> {
|
||||||
if (savedState) {
|
if (savedState) {
|
||||||
this.setState(savedState);
|
this.setState(savedState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!(savedState && savedState.name)) {
|
||||||
|
const name = `excalidraw-${getDateTime()}`;
|
||||||
|
this.setState({ name });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount() {
|
public componentWillUnmount() {
|
||||||
|
@ -1005,7 +1013,8 @@ class App extends React.Component<{}, AppState> {
|
||||||
currentItemBackgroundColor: "#ffffff",
|
currentItemBackgroundColor: "#ffffff",
|
||||||
viewBackgroundColor: "#ffffff",
|
viewBackgroundColor: "#ffffff",
|
||||||
scrollX: 0,
|
scrollX: 0,
|
||||||
scrollY: 0
|
scrollY: 0,
|
||||||
|
name: ""
|
||||||
};
|
};
|
||||||
|
|
||||||
private onResize = () => {
|
private onResize = () => {
|
||||||
|
@ -1130,10 +1139,16 @@ class App extends React.Component<{}, AppState> {
|
||||||
|
|
||||||
private removeWheelEventListener: (() => void) | undefined;
|
private removeWheelEventListener: (() => void) | undefined;
|
||||||
|
|
||||||
|
private updateProjectName(name: string): void {
|
||||||
|
this.setState({ name });
|
||||||
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const canvasWidth = window.innerWidth - CANVAS_WINDOW_OFFSET_LEFT;
|
const canvasWidth = window.innerWidth - CANVAS_WINDOW_OFFSET_LEFT;
|
||||||
const canvasHeight = window.innerHeight - CANVAS_WINDOW_OFFSET_TOP;
|
const canvasHeight = window.innerHeight - CANVAS_WINDOW_OFFSET_TOP;
|
||||||
|
|
||||||
|
const { name } = this.state;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="container"
|
className="container"
|
||||||
|
@ -1178,6 +1193,14 @@ class App extends React.Component<{}, AppState> {
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="sidePanel">
|
<div className="sidePanel">
|
||||||
|
<h4>Project name</h4>
|
||||||
|
{name && (
|
||||||
|
<EditableText
|
||||||
|
value={name}
|
||||||
|
updateName={(name: string) => this.updateProjectName(name)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<h4>Shapes</h4>
|
<h4>Shapes</h4>
|
||||||
<div className="panelTools">
|
<div className="panelTools">
|
||||||
{SHAPES.map(({ value, icon }) => (
|
{SHAPES.map(({ value, icon }) => (
|
||||||
|
|
|
@ -173,3 +173,12 @@ button {
|
||||||
padding: 2px 4px;
|
padding: 2px 4px;
|
||||||
border: 1px solid #ddd;
|
border: 1px solid #ddd;
|
||||||
}
|
}
|
||||||
|
.project-name {
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-name-input {
|
||||||
|
width: 200px;
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
|
11
src/utils/index.ts
Normal file
11
src/utils/index.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
export const getDateTime = (): string => {
|
||||||
|
const date = new Date();
|
||||||
|
const year = date.getFullYear();
|
||||||
|
const month = date.getMonth() + 1;
|
||||||
|
const day = date.getDate();
|
||||||
|
const hr = date.getHours();
|
||||||
|
const min = date.getMinutes();
|
||||||
|
const secs = date.getSeconds();
|
||||||
|
|
||||||
|
return `${year}${month}${day}${hr}${min}${secs}`;
|
||||||
|
};
|
Loading…
Add table
Add a link
Reference in a new issue