Improve selection view (#192)

This commit is contained in:
Christopher Chedeau 2020-01-05 19:11:35 -08:00 committed by GitHub
parent d5c6dd49a2
commit 23cd62d148
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 137 additions and 169 deletions

View File

@ -795,12 +795,7 @@ function generateDraw(element: ExcalidrawElement) {
leftY leftY
] = getDiamondPoints(element); ] = getDiamondPoints(element);
return generator.polygon( return generator.polygon(
[ [[topX, topY], [rightX, rightY], [bottomX, bottomY], [leftX, leftY]],
[topX, topY],
[rightX, rightY],
[bottomX, bottomY],
[leftX, leftY]
],
{ {
stroke: element.strokeColor, stroke: element.strokeColor,
fill: element.backgroundColor, fill: element.backgroundColor,
@ -1039,7 +1034,7 @@ const SHAPES = [
icon: ( icon: (
// custom // custom
<svg viewBox="0 0 223.646 223.646"> <svg viewBox="0 0 223.646 223.646">
<path d="M111.823 0L16.622 111.823 111.823 223.646 207.025 111.823z"></path> <path d="M111.823 0L16.622 111.823 111.823 223.646 207.025 111.823z" />
</svg> </svg>
), ),
value: "diamond" value: "diamond"
@ -1137,7 +1132,7 @@ function getSelectedFillStyles() {
.map(element => element.fillStyle) .map(element => element.fillStyle)
) )
); );
return fillStyles.length === 1 ? fillStyles[0] : ""; return fillStyles.length === 1 ? fillStyles[0] : null;
} }
function getSelectedStrokeWidth() { function getSelectedStrokeWidth() {
@ -1145,10 +1140,10 @@ function getSelectedStrokeWidth() {
new Set( new Set(
elements elements
.filter(element => element.isSelected) .filter(element => element.isSelected)
.map(element => `${element.strokeWidth}`) .map(element => element.strokeWidth)
) )
); );
return strokeWidth.length === 1 ? +strokeWidth[0] : ""; return strokeWidth.length === 1 ? strokeWidth[0] : null;
} }
function getSelectedRoughness() { function getSelectedRoughness() {
@ -1156,10 +1151,10 @@ function getSelectedRoughness() {
new Set( new Set(
elements elements
.filter(element => element.isSelected) .filter(element => element.isSelected)
.map(element => `${element.roughness}`) .map(element => element.roughness)
) )
); );
return roughness.length === 1 ? +roughness[0] : ""; return roughness.length === 1 ? roughness[0] : null;
} }
function getSelectedOpacity() { function getSelectedOpacity() {
@ -1167,10 +1162,10 @@ function getSelectedOpacity() {
new Set( new Set(
elements elements
.filter(element => element.isSelected) .filter(element => element.isSelected)
.map(element => `${element.opacity}`) .map(element => element.opacity)
) )
); );
return opacity.length === 1 ? +opacity[0] : ""; return opacity.length === 1 ? opacity[0] : null;
} }
function getSelectedStrokeColor() { function getSelectedStrokeColor() {
@ -1236,6 +1231,29 @@ function getElementAtPosition(x: number, y: number) {
return hitElement; return hitElement;
} }
function ButtonSelect<T>({
options,
value,
onChange
}: {
options: { value: T; text: string }[];
value: T | null;
onChange: (value: T) => void;
}) {
return (
<div className="buttonList">
{options.map(option => (
<button
onClick={() => onChange(option.value)}
className={value === option.value ? "active" : ""}
>
{option.text}
</button>
))}
</div>
);
}
const ELEMENT_SHIFT_TRANSLATE_AMOUNT = 5; const ELEMENT_SHIFT_TRANSLATE_AMOUNT = 5;
const ELEMENT_TRANSLATE_AMOUNT = 1; const ELEMENT_TRANSLATE_AMOUNT = 1;
@ -1411,18 +1429,6 @@ class App extends React.Component<{}, AppState> {
this.forceUpdate(); this.forceUpdate();
}; };
private changeFillStyle = (style: string) => {
this.changeProperty(element => (element.fillStyle = style));
};
private changeStrokeWidth = (event: React.ChangeEvent<HTMLSelectElement>) => {
this.changeProperty(element => (element.strokeWidth = +event.target.value));
};
private changeRoughness = (event: React.ChangeEvent<HTMLSelectElement>) => {
this.changeProperty(element => (element.roughness = +event.target.value));
};
private changeOpacity = (event: React.ChangeEvent<HTMLInputElement>) => { private changeOpacity = (event: React.ChangeEvent<HTMLInputElement>) => {
this.changeProperty(element => (element.opacity = +event.target.value)); this.changeProperty(element => (element.opacity = +event.target.value));
}; };
@ -1509,65 +1515,60 @@ class App extends React.Component<{}, AppState> {
))} ))}
</div> </div>
{someElementIsSelected() && ( {someElementIsSelected() && (
<> <div className="panelColumn">
<h4>Selected Shapes</h4> <h4>Selection</h4>
<div className="panelColumn"> <div className="buttonList">
<button onClick={this.deleteSelectedElements}>Delete</button>
<button onClick={this.moveOneRight}>Bring forward</button> <button onClick={this.moveOneRight}>Bring forward</button>
<button onClick={this.moveAllRight}>Bring to front</button> <button onClick={this.moveAllRight}>Bring to front</button>
<button onClick={this.moveOneLeft}>Send backward</button> <button onClick={this.moveOneLeft}>Send backward</button>
<button onClick={this.moveAllLeft}>Send to back</button> <button onClick={this.moveAllLeft}>Send to back</button>
</div> </div>
<h4>Colors</h4> <h5>Stroke Color</h5>
<div className="panelColumn"> <div>
<h5>Shape Stroke Color</h5> <button
<div> className="swatch"
<button style={{
className="swatch" backgroundColor:
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() || getSelectedStrokeColor() ||
this.state.currentItemStrokeColor this.state.currentItemStrokeColor
} }}
onChange={e => this.changeStrokeColor(e.target.value)} onClick={() =>
/> this.setState(s => ({
</div> 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> </div>
{hasBackground() && ( {hasBackground() && (
<div className="panelColumn"> <>
<h5>Shape Background Color</h5> <h5>Background Color</h5>
<div> <div>
<button <button
className="swatch" className="swatch"
@ -1613,119 +1614,78 @@ class App extends React.Component<{}, AppState> {
onChange={e => this.changeBackgroundColor(e.target.value)} onChange={e => this.changeBackgroundColor(e.target.value)}
/> />
</div> </div>
</div> </>
)} )}
{hasBackground() && ( {hasBackground() && (
<> <>
<h4>Fill</h4> <h5>Fill</h5>
<div className="panelColumn"> <ButtonSelect
<button options={[
onClick={() => this.changeFillStyle("hachure")} { value: "solid", text: "Solid" },
className={ { value: "hachure", text: "Hachure" },
getSelectedFillStyles() === "hachure" ? "active" : "" { value: "cross-hatch", text: "Cross-hatch" }
} ]}
> value={getSelectedFillStyles()}
Hachure onChange={value => {
</button> this.changeProperty(element => {
<button element.fillStyle = value;
onClick={() => this.changeFillStyle("solid")} });
className={ }}
getSelectedFillStyles() === "solid" ? "active" : "" />
}
>
Solid
</button>
<button
onClick={() => this.changeFillStyle("zigzag")}
className={
getSelectedFillStyles() === "zigzag" ? "active" : ""
}
>
Zigzag
</button>
<button
onClick={() => this.changeFillStyle("cross-hatch")}
className={
getSelectedFillStyles() === "cross-hatch"
? "active"
: ""
}
>
Cross-hatch
</button>
<button
onClick={() => this.changeFillStyle("sunburst")}
className={
getSelectedFillStyles() === "sunburst" ? "active" : ""
}
>
Sunburst
</button>
<button
onClick={() => this.changeFillStyle("dashed")}
className={
getSelectedFillStyles() === "dashed" ? "active" : ""
}
>
Dashed
</button>
<button
onClick={() => this.changeFillStyle("zigzag-line")}
className={
getSelectedFillStyles() === "zigzag-line"
? "active"
: ""
}
>
Zigzag-line
</button>
</div>
</> </>
)} )}
{hasStroke() && ( {hasStroke() && (
<> <>
<h4>Stroke width</h4> <h5>Stroke Width</h5>
<div className="panelColumn"> <ButtonSelect
<select options={[
onChange={this.changeStrokeWidth} { value: 1, text: "Thin" },
value={getSelectedStrokeWidth()} { value: 2, text: "Bold" },
> { value: 4, text: "Extra Bold" }
<option hidden disabled value=""></option> ]}
<option value="1">1</option> value={getSelectedStrokeWidth()}
<option value="2">2</option> onChange={value => {
<option value="4">4</option> this.changeProperty(element => {
<option value="8">8</option> element.strokeWidth = value;
</select> });
</div> }}
/>
<h4>Roughness</h4> <h5>Slopiness</h5>
<div className="panelColumn"> <ButtonSelect
<select options={[
onChange={this.changeRoughness} { value: 0, text: "Draftsman" },
value={getSelectedRoughness()} { value: 1, text: "Artist" },
> { value: 3, text: "Cartoonist" }
<option hidden disabled value=""></option> ]}
<option value="1">1</option> value={getSelectedRoughness()}
<option value="2">2</option> onChange={value =>
<option value="4">4</option> this.changeProperty(element => {
<option value="8">8</option> element.roughness = value;
<option value="10">10</option> })
</select> }
</div> />
</> </>
)} )}
<h4>Opacity</h4> <h5>Opacity</h5>
<input <input
type="range" type="range"
min="0" min="0"
max="100" max="100"
onChange={this.changeOpacity} onChange={this.changeOpacity}
value={getSelectedOpacity()} value={
getSelectedOpacity() ||
0 /* Put the opacity at 0 if there are two conflicting ones */
}
/> />
</>
<button onClick={this.deleteSelectedElements}>
Delete selected
</button>
</div>
)} )}
<h4>Canvas</h4> <h4>Canvas</h4>
<div className="panelColumn"> <div className="panelColumn">

View File

@ -53,6 +53,14 @@ body {
h5:first-of-type { h5:first-of-type {
margin-top: 0; margin-top: 0;
} }
.buttonList {
flex-wrap: wrap;
button {
margin-right: 4px;
}
}
} }
} }