Skip to content

Commit

Permalink
Undo added
Browse files Browse the repository at this point in the history
  • Loading branch information
ilyabo committed Sep 2, 2024
1 parent 2a87341 commit 14fb65d
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 41 deletions.
6 changes: 5 additions & 1 deletion assets/js/components/app-container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {MapView} from "../map/map-view";
import {useAppStore} from "../store/store";
import {ToolbarContainer} from "./toolbar-container";
import {TooltipProvider} from "./ui/tooltip";
import UndoContainer from "./undo-container";

const AppContainer: React.FC = () => {
const initialize = useAppStore((state) => state.initialize);
Expand All @@ -13,9 +14,12 @@ const AppContainer: React.FC = () => {
<div className="map-container absolute w-[100vw] h-[100vh] top-0 left-0">
<MapView />
</div>
<div className="absolute top-4 left-4">
<div className="absolute top-2 left-2">
<ToolbarContainer />
</div>
<div className="absolute bottom-2 left-2">
<UndoContainer />
</div>
</TooltipProvider>
);
};
Expand Down
35 changes: 35 additions & 0 deletions assets/js/components/toolbar-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import {LucideIcon} from "lucide-react";
import React, {FC} from "react";
import {Button} from "./ui/button";
import {Tooltip, TooltipContent, TooltipTrigger} from "./ui/tooltip";
import {cn} from "./ui/utils";

export const ToolbarButton: FC<{
icon: LucideIcon;
isSelected?: boolean;
tooltipText: string;
onClick: () => void;
}> = ({icon: Icon, isSelected, tooltipText, onClick}) => {
return (
<Tooltip>
<TooltipTrigger asChild>
<Button
className={cn(
"border bg-gray-400 hover:bg-gray-600 transition-colors",
{
"bg-gray-700": isSelected,
shadow: isSelected,
}
)}
size="icon"
onClick={onClick}
>
{Icon ? <Icon size="16px" /> : null}
</Button>
</TooltipTrigger>
<TooltipContent align="center" side="right" className="text-xs">
{tooltipText}
</TooltipContent>
</Tooltip>
);
};
35 changes: 1 addition & 34 deletions assets/js/components/toolbar-container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,44 +6,11 @@ import {
getDrawingModeLabel,
} from "../drawing/types";
import {useAppStore} from "../store/store";
import {Button} from "./ui/button";
import {LucideIcon} from "lucide-react";
import {cn} from "./ui/utils";
import {Tooltip, TooltipContent, TooltipTrigger} from "./ui/tooltip";
import {ColorSelector} from "./color-selector";
import {ToolbarButton} from "./toolbar-button";

export type ModeSelectorProps = {};

const ToolbarButton: FC<{
icon: LucideIcon;
isSelected?: boolean;
tooltipText: string;
onClick: () => void;
}> = ({icon: Icon, isSelected, tooltipText, onClick}) => {
return (
<Tooltip>
<TooltipTrigger asChild>
<Button
className={cn(
"border bg-gray-400 hover:bg-gray-600 transition-colors",
{
"bg-gray-700": isSelected,
shadow: isSelected,
}
)}
size="icon"
onClick={onClick}
>
{Icon ? <Icon size="16px" /> : null}
</Button>
</TooltipTrigger>
<TooltipContent align="center" side="right" className="text-xs">
{tooltipText}
</TooltipContent>
</Tooltip>
);
};

export const ToolbarContainer: FC<ModeSelectorProps> = (props) => {
const drawingMode = useAppStore((state) => state.mode);
const setDrawingMode = useAppStore((state) => state.setDrawingMode);
Expand Down
17 changes: 17 additions & 0 deletions assets/js/components/undo-container.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React, {FC} from "react";
import {useAppStore} from "../store/store";
import {ToolbarButton} from "./toolbar-button";
import {Redo2Icon, Undo2Icon} from "lucide-react";
type Props = {};
const UndoContainer: FC<Props> = (props) => {
const undo = useAppStore((state) => state.undo);
const redo = useAppStore((state) => state.redo);
return (
<div className="flex flex-row gap-1 items-center">
<ToolbarButton icon={Undo2Icon} tooltipText={"Undo"} onClick={undo} />
<ToolbarButton icon={Redo2Icon} tooltipText={"Redo"} onClick={redo} />
</div>
);
};

export default UndoContainer;
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,46 @@ import {DRAWING_MODE_KEYSTROKES, DrawingMode} from "./types";
/**
* Add keyboard event listener for drawing modes
**/
export function useModeKeyStrokes() {
export function useKeyStrokes() {
const setDrawingMode = useAppStore((state) => state.setDrawingMode);
const undo = useAppStore((state) => state.undo);
const redo = useAppStore((state) => state.redo);
const dropSelectedFeatures = useAppStore(
(state) => state.dropSelectedFeatures
);

useEffect(() => {
const onKeyDown = (evt) => {
const onKeyDown = (event) => {
for (const [mode, keystroke] of Object.entries(DRAWING_MODE_KEYSTROKES)) {
if (evt.key === keystroke) {
if (event.key === keystroke) {
setDrawingMode(mode as DrawingMode);
break;
}
}
if (evt.key === "Delete" || evt.key === "Backspace") {
if (event.key === "Delete" || event.key === "Backspace") {
event.preventDefault();
dropSelectedFeatures();
}
// Undo
if (
(event.ctrlKey || event.metaKey) &&
event.key === "z" &&
!event.shiftKey
) {
event.preventDefault();
undo();
}

// 'Redo' command
if (
(event.ctrlKey || event.metaKey) &&
event.key === "z" &&
event.shiftKey
) {
event.preventDefault();
// Handle the redo action here
redo();
}
};
window.addEventListener("keydown", onKeyDown);
return () => {
Expand Down
4 changes: 2 additions & 2 deletions assets/js/map/map-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {useControl} from "react-map-gl/maplibre";
import {useDrawHandler} from "../drawing/use-draw-handlers";
import {useAppStore} from "../store/store";
import {colorToRGBA, findLastLabelLayerId} from "../store/utils";
import {useModeKeyStrokes} from "../drawing/use-mode-keystrokes";
import {useKeyStrokes} from "../drawing/use-key-strokes";
import {usePanning} from "../drawing/use-panning";

const defaultColor: [number, number, number, number] = [150, 150, 150, 200];
Expand Down Expand Up @@ -57,7 +57,7 @@ export const MapView: FC = () => {
const mapRef = useRef<MapRef>(null);
const [beforeId, setBeforeId] = useState();

useModeKeyStrokes();
useKeyStrokes();
usePanning();

const drawHandlers = useDrawHandler({mapRef: mapRef.current});
Expand Down
10 changes: 10 additions & 0 deletions assets/js/store/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ interface DrawingState {
selectedIds: string[] | undefined;
setColor: (color: string) => void;
setSelectedIds: (ids: string[] | undefined) => void;
undo: () => void;
redo: () => void;
initialized: boolean;
setPanning: (isPanning: boolean) => void;
addOrUpdateFeatures: (feature: PolygonFeature[]) => void;
Expand All @@ -32,6 +34,9 @@ export const useAppStore = create<DrawingState>((set, get) => {
const ydoc = new Y.Doc();
const yfeatures = ydoc.getMap<PolygonFeature>("features");

// We'll only start capturing undo/redo after the initial state is applied when joining the channel
let yfeaturesUndo: Y.UndoManager | undefined = undefined;

const socket = new Socket("/socket" /*{params: {token: window.userToken}}*/);
// @ts-ignore
socket.connect();
Expand Down Expand Up @@ -62,6 +67,7 @@ export const useAppStore = create<DrawingState>((set, get) => {
if (resp) {
const initialState = new Uint8Array(resp);
Y.applyUpdate(ydoc, initialState); // Apply the initial state to the Yjs document
yfeaturesUndo = new Y.UndoManager(yfeatures);
} else {
console.log("No initial state");
}
Expand Down Expand Up @@ -111,6 +117,10 @@ export const useAppStore = create<DrawingState>((set, get) => {
}));
}
},

undo: () => yfeaturesUndo?.undo(),
redo: () => yfeaturesUndo?.redo(),

setHexResolution: (resolution) => set({hexResolution: resolution}),
setDrawingMode: (mode) => set({mode}),
setPanning: (isPanning) => set({isPanning}),
Expand Down

0 comments on commit 14fb65d

Please sign in to comment.