|
@@ -842,25 +842,19 @@ function generateDraw(element: ExcalidrawElement) {
|
|
|
};
|
|
|
} else if (element.type === "arrow") {
|
|
|
const [x1, y1, x2, y2, x3, y3, x4, y4] = getArrowPoints(element);
|
|
|
+ const options = {
|
|
|
+ stroke: element.strokeColor,
|
|
|
+ strokeWidth: element.strokeWidth,
|
|
|
+ roughness: element.roughness
|
|
|
+ };
|
|
|
+
|
|
|
const shapes = withCustomMathRandom(element.seed, () => [
|
|
|
// \
|
|
|
- generator.line(x3, y3, x2, y2, {
|
|
|
- stroke: element.strokeColor,
|
|
|
- strokeWidth: element.strokeWidth,
|
|
|
- roughness: element.roughness
|
|
|
- }),
|
|
|
+ generator.line(x3, y3, x2, y2, options),
|
|
|
// -----
|
|
|
- generator.line(x1, y1, x2, y2, {
|
|
|
- stroke: element.strokeColor,
|
|
|
- strokeWidth: element.strokeWidth,
|
|
|
- roughness: element.roughness
|
|
|
- }),
|
|
|
+ generator.line(x1, y1, x2, y2, options),
|
|
|
// /
|
|
|
- generator.line(x4, y4, x2, y2, {
|
|
|
- stroke: element.strokeColor,
|
|
|
- strokeWidth: element.strokeWidth,
|
|
|
- roughness: element.roughness
|
|
|
- })
|
|
|
+ generator.line(x4, y4, x2, y2, options)
|
|
|
]);
|
|
|
|
|
|
element.draw = (rc, context, { scrollX, scrollY }) => {
|
|
@@ -971,7 +965,17 @@ function restore(
|
|
|
? JSON.parse(savedElements)
|
|
|
: savedElements)
|
|
|
);
|
|
|
- elements.forEach((element: ExcalidrawElement) => generateDraw(element));
|
|
|
+ elements.forEach((element: ExcalidrawElement) => {
|
|
|
+ element.fillStyle = element.fillStyle || "hachure";
|
|
|
+ element.strokeWidth = element.strokeWidth || 1;
|
|
|
+ element.roughness = element.roughness || 1;
|
|
|
+ element.opacity =
|
|
|
+ element.opacity === null || element.opacity === undefined
|
|
|
+ ? 100
|
|
|
+ : element.opacity;
|
|
|
+
|
|
|
+ generateDraw(element);
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
return savedState ? JSON.parse(savedState) : null;
|
|
@@ -1106,7 +1110,7 @@ function getSelectedIndices() {
|
|
|
const someElementIsSelected = () =>
|
|
|
elements.some(element => element.isSelected);
|
|
|
|
|
|
-const someElementIsSelectedIsRectangleOrEllipseOrDiamond = () =>
|
|
|
+const hasBackground = () =>
|
|
|
elements.some(
|
|
|
element =>
|
|
|
element.isSelected &&
|
|
@@ -1115,7 +1119,7 @@ const someElementIsSelectedIsRectangleOrEllipseOrDiamond = () =>
|
|
|
element.type === "diamond")
|
|
|
);
|
|
|
|
|
|
-const someElementIsSelectedIsRectangleOrEllipseOrDiamondOrArrow = () =>
|
|
|
+const hasStroke = () =>
|
|
|
elements.some(
|
|
|
element =>
|
|
|
element.isSelected &&
|
|
@@ -1514,125 +1518,76 @@ class App extends React.Component<{}, AppState> {
|
|
|
<button onClick={this.moveOneLeft}>Send backward</button>
|
|
|
<button onClick={this.moveAllLeft}>Send to back</button>
|
|
|
</div>
|
|
|
- </>
|
|
|
- )}
|
|
|
- <h4>Canvas</h4>
|
|
|
- <div className="panelColumn">
|
|
|
- <h5>Canvas Background Color</h5>
|
|
|
- <div>
|
|
|
- <button
|
|
|
- className="swatch"
|
|
|
- style={{
|
|
|
- backgroundColor: this.state.viewBackgroundColor
|
|
|
- }}
|
|
|
- onClick={() =>
|
|
|
- this.setState(s => ({
|
|
|
- currentColorPicker:
|
|
|
- s.currentColorPicker === ColorPicker.CANVAS_BACKGROUND
|
|
|
- ? null
|
|
|
- : ColorPicker.CANVAS_BACKGROUND
|
|
|
- }))
|
|
|
- }
|
|
|
- />
|
|
|
- {this.state.currentColorPicker ===
|
|
|
- ColorPicker.CANVAS_BACKGROUND ? (
|
|
|
- <div className="popover">
|
|
|
- <div
|
|
|
- className="cover"
|
|
|
- onClick={() => this.setState({ currentColorPicker: null })}
|
|
|
- />
|
|
|
- <SketchPicker
|
|
|
- color={this.state.viewBackgroundColor}
|
|
|
- onChange={color => {
|
|
|
- this.setState({ viewBackgroundColor: color.hex });
|
|
|
+ <h4>Colors</h4>
|
|
|
+ <div className="panelColumn">
|
|
|
+ <h5>Shape Stroke Color</h5>
|
|
|
+ <div>
|
|
|
+ <button
|
|
|
+ className="swatch"
|
|
|
+ style={{
|
|
|
+ backgroundColor:
|
|
|
+ getSelectedStrokeColor() ||
|
|
|
+ this.state.currentItemStrokeColor
|
|
|
}}
|
|
|
+ onClick={() =>
|
|
|
+ this.setState(s => ({
|
|
|
+ currentColorPicker:
|
|
|
+ s.currentColorPicker === ColorPicker.SHAPE_STROKE
|
|
|
+ ? null
|
|
|
+ : ColorPicker.SHAPE_STROKE
|
|
|
+ }))
|
|
|
+ }
|
|
|
+ />
|
|
|
+ {this.state.currentColorPicker ===
|
|
|
+ ColorPicker.SHAPE_STROKE && (
|
|
|
+ <div className="popover">
|
|
|
+ <div
|
|
|
+ className="cover"
|
|
|
+ onClick={() =>
|
|
|
+ this.setState({ currentColorPicker: null })
|
|
|
+ }
|
|
|
+ />
|
|
|
+ <SketchPicker
|
|
|
+ color={this.state.currentItemStrokeColor}
|
|
|
+ onChange={color => this.changeStrokeColor(color.hex)}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ <input
|
|
|
+ type="text"
|
|
|
+ className="swatch-input"
|
|
|
+ value={
|
|
|
+ getSelectedStrokeColor() ||
|
|
|
+ this.state.currentItemStrokeColor
|
|
|
+ }
|
|
|
+ onChange={e => this.changeStrokeColor(e.target.value)}
|
|
|
/>
|
|
|
</div>
|
|
|
- ) : null}
|
|
|
- <input
|
|
|
- type="text"
|
|
|
- className="swatch-input"
|
|
|
- value={this.state.viewBackgroundColor}
|
|
|
- onChange={e =>
|
|
|
- this.setState({ viewBackgroundColor: e.target.value })
|
|
|
- }
|
|
|
- />
|
|
|
- </div>
|
|
|
- <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(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(this.state.name);
|
|
|
- }}
|
|
|
- >
|
|
|
- Save as...
|
|
|
- </button>
|
|
|
- <button
|
|
|
- onClick={() => {
|
|
|
- loadFromJSON().then(() => this.forceUpdate());
|
|
|
- }}
|
|
|
- >
|
|
|
- Load file...
|
|
|
- </button>
|
|
|
- </div>
|
|
|
- {someElementIsSelected() && (
|
|
|
- <>
|
|
|
- <>
|
|
|
- <h4>Colors</h4>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {hasBackground() && (
|
|
|
<div className="panelColumn">
|
|
|
- <h5>Shape Stroke Color</h5>
|
|
|
+ <h5>Shape Background Color</h5>
|
|
|
<div>
|
|
|
<button
|
|
|
className="swatch"
|
|
|
style={{
|
|
|
backgroundColor:
|
|
|
- getSelectedStrokeColor() ||
|
|
|
- this.state.currentItemStrokeColor
|
|
|
+ getSelectedBackgroundColor() ||
|
|
|
+ this.state.currentItemBackgroundColor
|
|
|
}}
|
|
|
onClick={() =>
|
|
|
this.setState(s => ({
|
|
|
currentColorPicker:
|
|
|
- s.currentColorPicker === ColorPicker.SHAPE_STROKE
|
|
|
+ s.currentColorPicker ===
|
|
|
+ ColorPicker.SHAPE_BACKGROUND
|
|
|
? null
|
|
|
- : ColorPicker.SHAPE_STROKE
|
|
|
+ : ColorPicker.SHAPE_BACKGROUND
|
|
|
}))
|
|
|
}
|
|
|
/>
|
|
|
{this.state.currentColorPicker ===
|
|
|
- ColorPicker.SHAPE_STROKE && (
|
|
|
+ ColorPicker.SHAPE_BACKGROUND ? (
|
|
|
<div className="popover">
|
|
|
<div
|
|
|
className="cover"
|
|
@@ -1641,82 +1596,30 @@ class App extends React.Component<{}, AppState> {
|
|
|
}
|
|
|
/>
|
|
|
<SketchPicker
|
|
|
- color={this.state.currentItemStrokeColor}
|
|
|
- onChange={color => this.changeStrokeColor(color.hex)}
|
|
|
+ color={this.state.currentItemBackgroundColor}
|
|
|
+ onChange={color =>
|
|
|
+ this.changeBackgroundColor(color.hex)
|
|
|
+ }
|
|
|
/>
|
|
|
</div>
|
|
|
- )}
|
|
|
+ ) : null}
|
|
|
<input
|
|
|
type="text"
|
|
|
className="swatch-input"
|
|
|
value={
|
|
|
- getSelectedStrokeColor() ||
|
|
|
- this.state.currentItemStrokeColor
|
|
|
+ getSelectedBackgroundColor() ||
|
|
|
+ this.state.currentItemBackgroundColor
|
|
|
}
|
|
|
- onChange={e => this.changeStrokeColor(e.target.value)}
|
|
|
+ onChange={e => this.changeBackgroundColor(e.target.value)}
|
|
|
/>
|
|
|
</div>
|
|
|
</div>
|
|
|
+ )}
|
|
|
|
|
|
- {someElementIsSelectedIsRectangleOrEllipseOrDiamond() && (
|
|
|
- <div className="panelColumn">
|
|
|
- <h5>Shape Background Color</h5>
|
|
|
- <div>
|
|
|
- <button
|
|
|
- className="swatch"
|
|
|
- style={{
|
|
|
- backgroundColor:
|
|
|
- getSelectedBackgroundColor() ||
|
|
|
- this.state.currentItemBackgroundColor
|
|
|
- }}
|
|
|
- onClick={() =>
|
|
|
- this.setState(s => ({
|
|
|
- currentColorPicker:
|
|
|
- s.currentColorPicker ===
|
|
|
- ColorPicker.SHAPE_BACKGROUND
|
|
|
- ? null
|
|
|
- : ColorPicker.SHAPE_BACKGROUND
|
|
|
- }))
|
|
|
- }
|
|
|
- />
|
|
|
- {this.state.currentColorPicker ===
|
|
|
- ColorPicker.SHAPE_BACKGROUND ? (
|
|
|
- <div className="popover">
|
|
|
- <div
|
|
|
- className="cover"
|
|
|
- onClick={() =>
|
|
|
- this.setState({ currentColorPicker: null })
|
|
|
- }
|
|
|
- />
|
|
|
- <SketchPicker
|
|
|
- color={this.state.currentItemBackgroundColor}
|
|
|
- onChange={color =>
|
|
|
- this.changeBackgroundColor(color.hex)
|
|
|
- }
|
|
|
- />
|
|
|
- </div>
|
|
|
- ) : null}
|
|
|
- <input
|
|
|
- type="text"
|
|
|
- className="swatch-input"
|
|
|
- value={
|
|
|
- getSelectedBackgroundColor() ||
|
|
|
- this.state.currentItemBackgroundColor
|
|
|
- }
|
|
|
- onChange={e =>
|
|
|
- this.changeBackgroundColor(e.target.value)
|
|
|
- }
|
|
|
- />
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- )}
|
|
|
- </>
|
|
|
-
|
|
|
- {someElementIsSelectedIsRectangleOrEllipseOrDiamond() && (
|
|
|
+ {hasBackground() && (
|
|
|
<>
|
|
|
<h4>Fill</h4>
|
|
|
<div className="panelColumn">
|
|
|
- {/* <select onChange={this.changeFillStyle} value={getSelectedFillStyles()}> */}
|
|
|
<button
|
|
|
onClick={() => this.changeFillStyle("hachure")}
|
|
|
className={
|
|
@@ -1752,14 +1655,6 @@ class App extends React.Component<{}, AppState> {
|
|
|
Cross-hatch
|
|
|
</button>
|
|
|
<button
|
|
|
- onClick={() => this.changeFillStyle("dots")}
|
|
|
- className={
|
|
|
- getSelectedFillStyles() === "dots" ? "active" : ""
|
|
|
- }
|
|
|
- >
|
|
|
- Dots
|
|
|
- </button>
|
|
|
- <button
|
|
|
onClick={() => this.changeFillStyle("sunburst")}
|
|
|
className={
|
|
|
getSelectedFillStyles() === "sunburst" ? "active" : ""
|
|
@@ -1785,12 +1680,11 @@ class App extends React.Component<{}, AppState> {
|
|
|
>
|
|
|
Zigzag-line
|
|
|
</button>
|
|
|
- {/* </select> */}
|
|
|
</div>
|
|
|
</>
|
|
|
)}
|
|
|
|
|
|
- {someElementIsSelectedIsRectangleOrEllipseOrDiamondOrArrow() && (
|
|
|
+ {hasStroke() && (
|
|
|
<>
|
|
|
<h4>Stroke width</h4>
|
|
|
<div className="panelColumn">
|
|
@@ -1833,6 +1727,98 @@ class App extends React.Component<{}, AppState> {
|
|
|
/>
|
|
|
</>
|
|
|
)}
|
|
|
+ <h4>Canvas</h4>
|
|
|
+ <div className="panelColumn">
|
|
|
+ <h5>Canvas Background Color</h5>
|
|
|
+ <div>
|
|
|
+ <button
|
|
|
+ className="swatch"
|
|
|
+ style={{
|
|
|
+ backgroundColor: this.state.viewBackgroundColor
|
|
|
+ }}
|
|
|
+ onClick={() =>
|
|
|
+ this.setState(s => ({
|
|
|
+ currentColorPicker:
|
|
|
+ s.currentColorPicker === ColorPicker.CANVAS_BACKGROUND
|
|
|
+ ? null
|
|
|
+ : ColorPicker.CANVAS_BACKGROUND
|
|
|
+ }))
|
|
|
+ }
|
|
|
+ />
|
|
|
+ {this.state.currentColorPicker ===
|
|
|
+ ColorPicker.CANVAS_BACKGROUND ? (
|
|
|
+ <div className="popover">
|
|
|
+ <div
|
|
|
+ className="cover"
|
|
|
+ onClick={() => this.setState({ currentColorPicker: null })}
|
|
|
+ />
|
|
|
+ <SketchPicker
|
|
|
+ color={this.state.viewBackgroundColor}
|
|
|
+ onChange={color => {
|
|
|
+ this.setState({ viewBackgroundColor: color.hex });
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ ) : null}
|
|
|
+ <input
|
|
|
+ type="text"
|
|
|
+ className="swatch-input"
|
|
|
+ value={this.state.viewBackgroundColor}
|
|
|
+ onChange={e =>
|
|
|
+ this.setState({ viewBackgroundColor: e.target.value })
|
|
|
+ }
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <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(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(this.state.name);
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ Save as...
|
|
|
+ </button>
|
|
|
+ <button
|
|
|
+ onClick={() => {
|
|
|
+ loadFromJSON().then(() => this.forceUpdate());
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ Load file...
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
<canvas
|
|
|
id="canvas"
|