Make panels collapsible (#239)

* Make panels collapsible

- Add Panel component with collapse logic
- Use the component in all the necessary panel groups

* Remove unnecessary container from PanelCanvas

* Add "hide property" to Pane component to hide Panel contents using a prop

- Instead of doing conditional rendering, pass the condition to Panel as props

* Change collapse icon rotation for closed

- Use one icon and use CSS transforms to rotate it

* Remove unnecessary imports from PanelSelection
This commit is contained in:
Gasim Gasimzada 2020-01-09 01:06:36 +04:00 committed by Christopher Chedeau
parent e38f65dea7
commit 36ce6a26e6
7 changed files with 186 additions and 126 deletions

43
src/components/Panel.tsx Normal file
View File

@ -0,0 +1,43 @@
import React, { useState } from "react";
interface PanelProps {
title: string;
defaultCollapsed?: boolean;
hide?: boolean;
}
export const Panel: React.FC<PanelProps> = ({
title,
children,
defaultCollapsed = false,
hide = false
}) => {
const [collapsed, setCollapsed] = useState(defaultCollapsed);
if (hide) return null;
return (
<div className="panel">
<h4>{title}</h4>
<button
className="btn-panel-collapse"
type="button"
onClick={e => {
e.preventDefault();
setCollapsed(collapsed => !collapsed);
}}
>
{
<span
className={`btn-panel-collapse-icon ${
collapsed ? "btn-panel-collapse-icon-closed" : ""
}`}
>
</span>
}
</button>
{!collapsed && <div className="panelColumn">{children}</div>}
</div>
);
};

View File

@ -1,6 +1,7 @@
import React from "react";
import { ColorPicker } from "../ColorPicker";
import { Panel } from "../Panel";
interface PanelCanvasProps {
viewBackgroundColor: string;
@ -14,22 +15,19 @@ export const PanelCanvas: React.FC<PanelCanvasProps> = ({
onClearCanvas
}) => {
return (
<>
<h4>Canvas</h4>
<div className="panelColumn">
<h5>Canvas Background Color</h5>
<ColorPicker
color={viewBackgroundColor}
onChange={color => onViewBackgroundColorChange(color)}
/>
<button
type="button"
onClick={onClearCanvas}
title="Clear the canvas & reset background color"
>
Clear canvas
</button>
</div>
</>
<Panel title="Canvas">
<h5>Canvas Background Color</h5>
<ColorPicker
color={viewBackgroundColor}
onChange={color => onViewBackgroundColorChange(color)}
/>
<button
type="button"
onClick={onClearCanvas}
title="Clear the canvas & reset background color"
>
Clear canvas
</button>
</Panel>
);
};

View File

@ -1,5 +1,6 @@
import React from "react";
import { EditableText } from "../EditableText";
import { Panel } from "../Panel";
interface PanelExportProps {
projectName: string;
@ -21,8 +22,7 @@ export const PanelExport: React.FC<PanelExportProps> = ({
onExportAsPNG
}) => {
return (
<>
<h4>Export</h4>
<Panel title="Export">
<div className="panelColumn">
<h5>Name</h5>
{projectName && (
@ -47,6 +47,6 @@ export const PanelExport: React.FC<PanelExportProps> = ({
<button onClick={onSaveScene}>Save as...</button>
<button onClick={onLoadScene}>Load file...</button>
</div>
</>
</Panel>
);
};

View File

@ -14,8 +14,7 @@ export const PanelSelection: React.FC<PanelSelectionProps> = ({
onSendToBack
}) => {
return (
<>
<h4>Selection</h4>
<div>
<div className="buttonList">
<button type="button" onClick={onBringForward}>
Bring forward
@ -30,6 +29,6 @@ export const PanelSelection: React.FC<PanelSelectionProps> = ({
Send to back
</button>
</div>
</>
</div>
);
};

View File

@ -2,6 +2,7 @@ import React from "react";
import { SHAPES } from "../../shapes";
import { capitalizeString } from "../../utils";
import { Panel } from "../Panel";
interface PanelToolsProps {
activeTool: string;
@ -13,8 +14,7 @@ export const PanelTools: React.FC<PanelToolsProps> = ({
onToolChange
}) => {
return (
<>
<h4>Shapes</h4>
<Panel title="Shapes">
<div className="panelTools">
{SHAPES.map(({ value, icon }) => (
<label
@ -33,6 +33,6 @@ export const PanelTools: React.FC<PanelToolsProps> = ({
</label>
))}
</div>
</>
</Panel>
);
};

View File

@ -46,6 +46,7 @@ import { PanelSelection } from "./components/panels/PanelSelection";
import { PanelColor } from "./components/panels/PanelColor";
import { PanelExport } from "./components/panels/PanelExport";
import { PanelCanvas } from "./components/panels/PanelCanvas";
import { Panel } from "./components/Panel";
import "./styles.scss";
@ -381,112 +382,110 @@ class App extends React.Component<{}, AppState> {
this.forceUpdate();
}}
/>
{someElementIsSelected(elements) && (
<div className="panelColumn">
<PanelSelection
onBringForward={this.moveOneRight}
onBringToFront={this.moveAllRight}
onSendBackward={this.moveOneLeft}
onSendToBack={this.moveAllLeft}
/>
<Panel title="Selection" hide={!someElementIsSelected(elements)}>
<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
)}
/>
{hasBackground(elements) && (
<>
<PanelColor
title="Background Color"
onColorChange={this.changeBackgroundColor}
colorValue={getSelectedAttribute(
elements,
element => element.backgroundColor
)}
/>
<h5>Fill</h5>
<ButtonSelect
options={[
{ value: "solid", text: "Solid" },
{ value: "hachure", text: "Hachure" },
{ value: "cross-hatch", text: "Cross-hatch" }
]}
value={getSelectedAttribute(
elements,
element => element.fillStyle
)}
onChange={value => {
this.changeProperty(element => {
element.fillStyle = value;
});
}}
/>
</>
<PanelColor
title="Stroke Color"
onColorChange={this.changeStrokeColor}
colorValue={getSelectedAttribute(
elements,
element => element.strokeColor
)}
/>
{hasStroke(elements) && (
<>
<h5>Stroke Width</h5>
<ButtonSelect
options={[
{ value: 1, text: "Thin" },
{ value: 2, text: "Bold" },
{ value: 4, text: "Extra Bold" }
]}
value={getSelectedAttribute(
elements,
element => element.strokeWidth
)}
onChange={value => {
this.changeProperty(element => {
element.strokeWidth = value;
});
}}
/>
{hasBackground(elements) && (
<>
<PanelColor
title="Background Color"
onColorChange={this.changeBackgroundColor}
colorValue={getSelectedAttribute(
elements,
element => element.backgroundColor
)}
/>
<h5>Sloppiness</h5>
<ButtonSelect
options={[
{ value: 0, text: "Draftsman" },
{ value: 1, text: "Artist" },
{ value: 3, text: "Cartoonist" }
]}
value={getSelectedAttribute(
elements,
element => element.roughness
)}
onChange={value =>
this.changeProperty(element => {
element.roughness = value;
})
}
/>
</>
)}
<h5>Fill</h5>
<ButtonSelect
options={[
{ value: "solid", text: "Solid" },
{ value: "hachure", text: "Hachure" },
{ value: "cross-hatch", text: "Cross-hatch" }
]}
value={getSelectedAttribute(
elements,
element => element.fillStyle
)}
onChange={value => {
this.changeProperty(element => {
element.fillStyle = value;
});
}}
/>
</>
)}
<h5>Opacity</h5>
<input
type="range"
min="0"
max="100"
onChange={this.changeOpacity}
value={
getSelectedAttribute(elements, element => element.opacity) ||
0 /* Put the opacity at 0 if there are two conflicting ones */
}
/>
{hasStroke(elements) && (
<>
<h5>Stroke Width</h5>
<ButtonSelect
options={[
{ value: 1, text: "Thin" },
{ value: 2, text: "Bold" },
{ value: 4, text: "Extra Bold" }
]}
value={getSelectedAttribute(
elements,
element => element.strokeWidth
)}
onChange={value => {
this.changeProperty(element => {
element.strokeWidth = value;
});
}}
/>
<button onClick={this.deleteSelectedElements}>
Delete selected
</button>
</div>
)}
<h5>Sloppiness</h5>
<ButtonSelect
options={[
{ value: 0, text: "Draftsman" },
{ value: 1, text: "Artist" },
{ value: 3, text: "Cartoonist" }
]}
value={getSelectedAttribute(
elements,
element => element.roughness
)}
onChange={value =>
this.changeProperty(element => {
element.roughness = value;
})
}
/>
</>
)}
<h5>Opacity</h5>
<input
type="range"
min="0"
max="100"
onChange={this.changeOpacity}
value={
getSelectedAttribute(elements, element => element.opacity) ||
0 /* Put the opacity at 0 if there are two conflicting ones */
}
/>
<button onClick={this.deleteSelectedElements}>
Delete selected
</button>
</Panel>
<PanelCanvas
onClearCanvas={this.clearCanvas}
onViewBackgroundColorChange={val =>

View File

@ -30,6 +30,27 @@ body {
margin: 10px 0 10px 0;
}
.panel {
position: relative;
.btn-panel-collapse {
position: absolute;
top: -2px;
right: 5px;
background: none;
margin: 0px;
color: black;
}
.btn-panel-collapse-icon {
transform: none;
display: inline-block;
}
.btn-panel-collapse-icon-closed {
transform: rotateZ(90deg);
}
}
.panelTools {
display: flex;
flex-wrap: wrap;