Added UI elements for the new drawing system
Removed old gesture system Refactored map interaction into separate component
This commit is contained in:
19
src/components/map/controls/AlphaBlendToggle.js
Normal file
19
src/components/map/controls/AlphaBlendToggle.js
Normal 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;
|
||||
51
src/components/map/controls/BrushToolSettings.js
Normal file
51
src/components/map/controls/BrushToolSettings.js
Normal 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;
|
||||
107
src/components/map/controls/ColorControl.js
Normal file
107
src/components/map/controls/ColorControl.js
Normal 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;
|
||||
24
src/components/map/controls/Divider.js
Normal file
24
src/components/map/controls/Divider.js
Normal 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;
|
||||
21
src/components/map/controls/EdgeSnappingToggle.js
Normal file
21
src/components/map/controls/EdgeSnappingToggle.js
Normal 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;
|
||||
16
src/components/map/controls/EraseToolSettings.js
Normal file
16
src/components/map/controls/EraseToolSettings.js
Normal 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;
|
||||
55
src/components/map/controls/FogToolSettings.js
Normal file
55
src/components/map/controls/FogToolSettings.js
Normal 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;
|
||||
21
src/components/map/controls/GridSnappingToggle.js
Normal file
21
src/components/map/controls/GridSnappingToggle.js
Normal 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;
|
||||
22
src/components/map/controls/RadioIconButton.js
Normal file
22
src/components/map/controls/RadioIconButton.js
Normal 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;
|
||||
59
src/components/map/controls/ShapeToolSettings.js
Normal file
59
src/components/map/controls/ShapeToolSettings.js
Normal 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;
|
||||
Reference in New Issue
Block a user