Added UI elements for the new drawing system

Removed old gesture system
Refactored map interaction into separate component
This commit is contained in:
Mitchell McCaffrey
2020-04-27 17:29:46 +10:00
parent 3112890fd3
commit 2cf93ab77f
29 changed files with 952 additions and 540 deletions

View File

@@ -0,0 +1,19 @@
import React from "react";
import { IconButton } from "theme-ui";
import BlendOnIcon from "../../../icons/BlendOnIcon";
import BlendOffIcon from "../../../icons/BlendOffIcon";
function AlphaBlendToggle({ useBlending, onBlendingChange }) {
return (
<IconButton
aria-label={useBlending ? "Disable Blending" : "Enable Blending"}
title={useBlending ? "Disable Blending" : "Enable Blending"}
onClick={() => onBlendingChange(!useBlending)}
>
{useBlending ? <BlendOnIcon /> : <BlendOffIcon />}
</IconButton>
);
}
export default AlphaBlendToggle;

View File

@@ -0,0 +1,51 @@
import React from "react";
import { Flex } from "theme-ui";
import ColorControl from "./ColorControl";
import AlphaBlendToggle from "./AlphaBlendToggle";
import GridSnappingToggle from "./GridSnappingToggle";
import RadioIconButton from "./RadioIconButton";
import BrushStrokeIcon from "../../../icons/BrushStrokeIcon";
import BrushFillIcon from "../../../icons/BrushFillIcon";
import Divider from "./Divider";
function BrushToolSettings({ settings, onSettingChange }) {
return (
<Flex sx={{ alignItems: "center" }}>
<ColorControl
color={settings.color}
onColorChange={(color) => onSettingChange({ color })}
/>
<Divider vertical />
<RadioIconButton
title="Brush Type Stroke"
onClick={() => onSettingChange({ type: "stroke" })}
isSelected={settings.type === "stroke"}
>
<BrushStrokeIcon />
</RadioIconButton>
<RadioIconButton
title="Brush Type Fill"
onClick={() => onSettingChange({ type: "fill" })}
isSelected={settings.type === "fill"}
>
<BrushFillIcon />
</RadioIconButton>
<Divider vertical />
<AlphaBlendToggle
useBlending={settings.useBlending}
onBlendingChange={(useBlending) => onSettingChange({ useBlending })}
/>
<GridSnappingToggle
useGridSnapping={settings.useGridSnapping}
onGridSnappingChange={(useGridSnapping) =>
onSettingChange({ useGridSnapping })
}
/>
</Flex>
);
}
export default BrushToolSettings;

View File

@@ -0,0 +1,107 @@
import React, { useState } from "react";
import { Box } from "theme-ui";
import colors, { colorOptions } from "../../../helpers/colors";
import MapMenu from "../MapMenu";
function ColorCircle({ color, selected, onClick, sx }) {
return (
<Box
key={color}
sx={{
borderRadius: "50%",
transform: "scale(0.75)",
backgroundColor: colors[color],
cursor: "pointer",
...sx,
}}
onClick={onClick}
aria-label={`Brush Color ${color}`}
>
{selected && (
<Box
sx={{
width: "100%",
height: "100%",
border: "2px solid white",
position: "absolute",
top: 0,
borderRadius: "50%",
}}
/>
)}
</Box>
);
}
function ColorControl({ color, onColorChange }) {
const [showColorMenu, setShowColorMenu] = useState(false);
const [colorMenuOptions, setColorMenuOptions] = useState({});
function handleControlClick(event) {
if (showColorMenu) {
setShowColorMenu(false);
setColorMenuOptions({});
} else {
setShowColorMenu(true);
const rect = event.target.getBoundingClientRect();
setColorMenuOptions({
// Align the right of the submenu to the left of the tool and center vertically
left: `${rect.left + rect.width / 2}px`,
top: `${rect.bottom + 16}px`,
style: { transform: "translateX(-50%)" },
// Exclude this node from the sub menus auto close
excludeNode: event.target,
});
}
}
const colorMenu = (
<MapMenu
isOpen={showColorMenu}
onRequestClose={() => {
setShowColorMenu(false);
setColorMenuOptions({});
}}
{...colorMenuOptions}
>
<Box
sx={{
width: "104px",
display: "flex",
flexWrap: "wrap",
justifyContent: "space-between",
}}
p={1}
>
{colorOptions.map((c) => (
<ColorCircle
key={c}
color={c}
selected={c === color}
onClick={() => {
onColorChange(c);
setShowColorMenu(false);
setColorMenuOptions({});
}}
sx={{ width: "25%", paddingTop: "25%" }}
/>
))}
</Box>
</MapMenu>
);
return (
<>
<ColorCircle
color={color}
selected
onClick={handleControlClick}
sx={{ width: "24px", height: "24px" }}
/>
{colorMenu}
</>
);
}
export default ColorControl;

View File

@@ -0,0 +1,24 @@
import React from "react";
import { Divider } from "theme-ui";
function StyledDivider({ vertical }) {
return (
<Divider
my={vertical ? 0 : 2}
mx={vertical ? 2 : 0}
bg="text"
sx={{
height: vertical ? "24px" : "2px",
width: vertical ? "2px" : "24px",
borderRadius: "2px",
opacity: 0.5,
}}
/>
);
}
StyledDivider.defaultProps = {
vertical: false,
};
export default StyledDivider;

View File

@@ -0,0 +1,21 @@
import React from "react";
import { IconButton } from "theme-ui";
import SnappingOnIcon from "../../../icons/SnappingOnIcon";
import SnappingOffIcon from "../../../icons/SnappingOffIcon";
function EdgeSnappingToggle({ useEdgeSnapping, onEdgeSnappingChange }) {
return (
<IconButton
aria-label={
useEdgeSnapping ? "Disable Edge Snapping" : "Enable Edge Snapping"
}
title={useEdgeSnapping ? "Disable Edge Snapping" : "Enable Edge Snapping"}
onClick={() => onEdgeSnappingChange(!useEdgeSnapping)}
>
{useEdgeSnapping ? <SnappingOnIcon /> : <SnappingOffIcon />}
</IconButton>
);
}
export default EdgeSnappingToggle;

View File

@@ -0,0 +1,16 @@
import React from "react";
import { Flex, IconButton } from "theme-ui";
import EraseAllIcon from "../../../icons/EraseAllIcon";
function EraseToolSettings({ onEraseAll }) {
return (
<Flex sx={{ alignItems: "center" }}>
<IconButton aria-label="Erase All" title="Erase All" onClick={onEraseAll}>
<EraseAllIcon />
</IconButton>
</Flex>
);
}
export default EraseToolSettings;

View File

@@ -0,0 +1,55 @@
import React from "react";
import { Flex } from "theme-ui";
import GridSnappingToggle from "./GridSnappingToggle";
import EdgeSnappingToggle from "./EdgeSnappingToggle";
import RadioIconButton from "./RadioIconButton";
import FogAddIcon from "../../../icons/FogAddIcon";
import FogRemoveIcon from "../../../icons/FogRemoveIcon";
import FogToggleIcon from "../../../icons/FogToggleIcon";
import Divider from "./Divider";
function BrushToolSettings({ settings, onSettingChange }) {
return (
<Flex sx={{ alignItems: "center" }}>
<RadioIconButton
title="Add Fog"
onClick={() => onSettingChange({ type: "add" })}
isSelected={settings.type === "add"}
>
<FogAddIcon />
</RadioIconButton>
<RadioIconButton
title="Remove Fog"
onClick={() => onSettingChange({ type: "remove" })}
isSelected={settings.type === "remove"}
>
<FogRemoveIcon />
</RadioIconButton>
<RadioIconButton
title="Toggle Fog"
onClick={() => onSettingChange({ type: "toggle" })}
isSelected={settings.type === "toggle"}
>
<FogToggleIcon />
</RadioIconButton>
<Divider vertical />
<GridSnappingToggle
useGridSnapping={settings.useGridSnapping}
onGridSnappingChange={(useGridSnapping) =>
onSettingChange({ useGridSnapping })
}
/>
<EdgeSnappingToggle
useEdgeSnapping={settings.useEdgeSnapping}
onEdgeSnappingChange={(useEdgeSnapping) =>
onSettingChange({ useEdgeSnapping })
}
/>
</Flex>
);
}
export default BrushToolSettings;

View File

@@ -0,0 +1,21 @@
import React from "react";
import { IconButton } from "theme-ui";
import GridOnIcon from "../../../icons/GridOnIcon";
import GridOffIcon from "../../../icons/GridOffIcon";
function GridSnappingToggle({ useGridSnapping, onGridSnappingChange }) {
return (
<IconButton
aria-label={
useGridSnapping ? "Disable Grid Snapping" : "Enable Grid Snapping"
}
title={useGridSnapping ? "Disable Grid Snapping" : "Enable Grid Snapping"}
onClick={() => onGridSnappingChange(!useGridSnapping)}
>
{useGridSnapping ? <GridOnIcon /> : <GridOffIcon />}
</IconButton>
);
}
export default GridSnappingToggle;

View File

@@ -0,0 +1,22 @@
import React from "react";
import { IconButton } from "theme-ui";
function RadioIconButton({ title, onClick, isSelected, disabled, children }) {
return (
<IconButton
aria-label={title}
title={title}
onClick={onClick}
sx={{ color: isSelected ? "primary" : "text" }}
disabled={disabled}
>
{children}
</IconButton>
);
}
RadioIconButton.defaultProps = {
disabled: false,
};
export default RadioIconButton;

View File

@@ -0,0 +1,59 @@
import React from "react";
import { Flex } from "theme-ui";
import ColorControl from "./ColorControl";
import AlphaBlendToggle from "./AlphaBlendToggle";
import GridSnappingToggle from "./GridSnappingToggle";
import RadioIconButton from "./RadioIconButton";
import ShapeRectangleIcon from "../../../icons/ShapeRectangleIcon";
import ShapeCircleIcon from "../../../icons/ShapeCircleIcon";
import ShapeTriangleIcon from "../../../icons/ShapeTriangleIcon";
import Divider from "./Divider";
function ShapeToolSettings({ settings, onSettingChange }) {
return (
<Flex sx={{ alignItems: "center" }}>
<ColorControl
color={settings.color}
onColorChange={(color) => onSettingChange({ color })}
/>
<Divider vertical />
<RadioIconButton
title="Shape Type Rectangle"
onClick={() => onSettingChange({ type: "rectangle" })}
isSelected={settings.type === "rectangle"}
>
<ShapeRectangleIcon />
</RadioIconButton>
<RadioIconButton
title="Shape Type Circle"
onClick={() => onSettingChange({ type: "circle" })}
isSelected={settings.type === "cricle"}
>
<ShapeTriangleIcon />
</RadioIconButton>
<RadioIconButton
title="Shape Type Triangle"
onClick={() => onSettingChange({ type: "triangle" })}
isSelected={settings.type === "triangle"}
>
<ShapeCircleIcon />
</RadioIconButton>
<Divider vertical />
<AlphaBlendToggle
useBlending={settings.useBlending}
onBlendingChange={(useBlending) => onSettingChange({ useBlending })}
/>
<GridSnappingToggle
useGridSnapping={settings.useGridSnapping}
onGridSnappingChange={(useGridSnapping) =>
onSettingChange({ useGridSnapping })
}
/>
</Flex>
);
}
export default ShapeToolSettings;