|
|
|
@ -29,16 +29,19 @@ import {
|
|
|
|
|
import { AppState } from "./types";
|
|
|
|
|
import { ExcalidrawElement, ExcalidrawTextElement } from "./element/types";
|
|
|
|
|
|
|
|
|
|
import { getDateTime, capitalizeString, isInputLike } from "./utils";
|
|
|
|
|
import { getDateTime, isInputLike } from "./utils";
|
|
|
|
|
|
|
|
|
|
import { EditableText } from "./components/EditableText";
|
|
|
|
|
import { ButtonSelect } from "./components/ButtonSelect";
|
|
|
|
|
import { ColorPicker } from "./components/ColorPicker";
|
|
|
|
|
import { SHAPES, findShapeByKey, shapesShortcutKeys } from "./shapes";
|
|
|
|
|
import { findShapeByKey, shapesShortcutKeys } from "./shapes";
|
|
|
|
|
import { createHistory } from "./history";
|
|
|
|
|
|
|
|
|
|
import "./styles.scss";
|
|
|
|
|
import ContextMenu from "./components/ContextMenu";
|
|
|
|
|
import { PanelTools } from "./components/panels/PanelTools";
|
|
|
|
|
import { PanelSelection } from "./components/panels/PanelSelection";
|
|
|
|
|
import { PanelColor } from "./components/panels/PanelColor";
|
|
|
|
|
import { PanelExport } from "./components/panels/PanelExport";
|
|
|
|
|
import { PanelCanvas } from "./components/panels/PanelCanvas";
|
|
|
|
|
|
|
|
|
|
const { elements } = createScene();
|
|
|
|
|
const { history } = createHistory();
|
|
|
|
@ -368,59 +371,45 @@ class App extends React.Component<{}, AppState> {
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<div className="sidePanel">
|
|
|
|
|
<h4>Shapes</h4>
|
|
|
|
|
<div className="panelTools">
|
|
|
|
|
{SHAPES.map(({ value, icon }) => (
|
|
|
|
|
<label
|
|
|
|
|
key={value}
|
|
|
|
|
className="tool"
|
|
|
|
|
title={`${capitalizeString(value)} - ${
|
|
|
|
|
capitalizeString(value)[0]
|
|
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
<input
|
|
|
|
|
type="radio"
|
|
|
|
|
checked={this.state.elementType === value}
|
|
|
|
|
onChange={() => {
|
|
|
|
|
this.setState({ elementType: value });
|
|
|
|
|
clearSelection(elements);
|
|
|
|
|
document.documentElement.style.cursor =
|
|
|
|
|
value === "text" ? "text" : "crosshair";
|
|
|
|
|
this.forceUpdate();
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
<div className="toolIcon">{icon}</div>
|
|
|
|
|
</label>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
<PanelTools
|
|
|
|
|
activeTool={this.state.elementType}
|
|
|
|
|
onToolChange={value => {
|
|
|
|
|
this.setState({ elementType: value });
|
|
|
|
|
clearSelection(elements);
|
|
|
|
|
document.documentElement.style.cursor =
|
|
|
|
|
value === "text" ? "text" : "crosshair";
|
|
|
|
|
this.forceUpdate();
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
{someElementIsSelected(elements) && (
|
|
|
|
|
<div className="panelColumn">
|
|
|
|
|
<h4>Selection</h4>
|
|
|
|
|
<div className="buttonList">
|
|
|
|
|
<button onClick={this.moveOneRight}>Bring forward</button>
|
|
|
|
|
<button onClick={this.moveAllRight}>Bring to front</button>
|
|
|
|
|
<button onClick={this.moveOneLeft}>Send backward</button>
|
|
|
|
|
<button onClick={this.moveAllLeft}>Send to back</button>
|
|
|
|
|
</div>
|
|
|
|
|
<h5>Stroke Color</h5>
|
|
|
|
|
<ColorPicker
|
|
|
|
|
color={getSelectedAttribute(
|
|
|
|
|
<PanelSelection
|
|
|
|
|
onBringForward={this.moveOneRight}
|
|
|
|
|
onBringToFront={this.moveAllRight}
|
|
|
|
|
onSendBackward={this.moveOneLeft}
|
|
|
|
|
onSendToBack={this.moveAllLeft}
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<PanelColor
|
|
|
|
|
title="Stroke Color"
|
|
|
|
|
onColorChange={this.changeStrokeColor}
|
|
|
|
|
colorValue={getSelectedAttribute(
|
|
|
|
|
elements,
|
|
|
|
|
element => element.strokeColor
|
|
|
|
|
)}
|
|
|
|
|
onChange={color => this.changeStrokeColor(color)}
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
{hasBackground(elements) && (
|
|
|
|
|
<>
|
|
|
|
|
<h5>Background Color</h5>
|
|
|
|
|
<ColorPicker
|
|
|
|
|
color={getSelectedAttribute(
|
|
|
|
|
<PanelColor
|
|
|
|
|
title="Background Color"
|
|
|
|
|
onColorChange={this.changeBackgroundColor}
|
|
|
|
|
colorValue={getSelectedAttribute(
|
|
|
|
|
elements,
|
|
|
|
|
element => element.backgroundColor
|
|
|
|
|
)}
|
|
|
|
|
onChange={color => this.changeBackgroundColor(color)}
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<h5>Fill</h5>
|
|
|
|
|
<ButtonSelect
|
|
|
|
|
options={[
|
|
|
|
@ -498,63 +487,26 @@ class App extends React.Component<{}, AppState> {
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
<h4>Canvas</h4>
|
|
|
|
|
<div className="panelColumn">
|
|
|
|
|
<h5>Canvas Background Color</h5>
|
|
|
|
|
<ColorPicker
|
|
|
|
|
color={this.state.viewBackgroundColor}
|
|
|
|
|
onChange={color => this.setState({ viewBackgroundColor: color })}
|
|
|
|
|
/>
|
|
|
|
|
<button
|
|
|
|
|
onClick={this.clearCanvas}
|
|
|
|
|
title="Clear the canvas & reset background color"
|
|
|
|
|
>
|
|
|
|
|
Clear canvas
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
<h4>Export</h4>
|
|
|
|
|
<div className="panelColumn">
|
|
|
|
|
<h5>Name</h5>
|
|
|
|
|
{this.state.name && (
|
|
|
|
|
<EditableText
|
|
|
|
|
value={this.state.name}
|
|
|
|
|
onChange={(name: string) => this.updateProjectName(name)}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
<h5>Image</h5>
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => {
|
|
|
|
|
exportAsPNG(elements, canvas, this.state);
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Export to png
|
|
|
|
|
</button>
|
|
|
|
|
<label>
|
|
|
|
|
<input
|
|
|
|
|
type="checkbox"
|
|
|
|
|
checked={this.state.exportBackground}
|
|
|
|
|
onChange={e => {
|
|
|
|
|
this.setState({ exportBackground: e.target.checked });
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
background
|
|
|
|
|
</label>
|
|
|
|
|
<h5>Scene</h5>
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => {
|
|
|
|
|
saveAsJSON(elements, this.state.name);
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Save as...
|
|
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => {
|
|
|
|
|
loadFromJSON(elements).then(() => this.forceUpdate());
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Load file...
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
<PanelCanvas
|
|
|
|
|
onClearCanvas={this.clearCanvas}
|
|
|
|
|
onViewBackgroundColorChange={val =>
|
|
|
|
|
this.setState({ viewBackgroundColor: val })
|
|
|
|
|
}
|
|
|
|
|
viewBackgroundColor={this.state.viewBackgroundColor}
|
|
|
|
|
/>
|
|
|
|
|
<PanelExport
|
|
|
|
|
projectName={this.state.name}
|
|
|
|
|
onProjectNameChange={this.updateProjectName}
|
|
|
|
|
onExportAsPNG={() => exportAsPNG(elements, canvas, this.state)}
|
|
|
|
|
exportBackground={this.state.exportBackground}
|
|
|
|
|
onExportBackgroundChange={val =>
|
|
|
|
|
this.setState({ exportBackground: val })
|
|
|
|
|
}
|
|
|
|
|
onSaveScene={() => saveAsJSON(elements, this.state.name)}
|
|
|
|
|
onLoadScene={() =>
|
|
|
|
|
loadFromJSON(elements).then(() => this.forceUpdate())
|
|
|
|
|
}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<canvas
|
|
|
|
|
id="canvas"
|
|
|
|
|