mirror of
https://github.com/r2r90/canvas-label.git
synced 2025-12-16 21:19:38 +00:00
add history
This commit is contained in:
@@ -36,14 +36,24 @@ const GUIDELINE_OFFSET = 5;
|
|||||||
|
|
||||||
const Canvas = () => {
|
const Canvas = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const stage = useAppSelector((state) => state.app.stage);
|
const currentStep = useAppSelector((state) => state.app.currentStep);
|
||||||
|
|
||||||
|
const stage = useAppSelector(
|
||||||
|
(state) => state.app.history[currentStep]?.stage,
|
||||||
|
);
|
||||||
const stageRef = useRef<Konva.Stage>(null);
|
const stageRef = useRef<Konva.Stage>(null);
|
||||||
const layerRef = useRef<Konva.Layer>(null);
|
const layerRef = useRef<Konva.Layer>(null);
|
||||||
const selectedItemId = useAppSelector((state) => state.app.selectedItemId);
|
const selectedItemId = useAppSelector(
|
||||||
|
(state) => state.app.history[currentStep]?.selectedItemId,
|
||||||
|
);
|
||||||
const selectItem = (id: string) => dispatch(appSlice.actions.selectItem(id));
|
const selectItem = (id: string) => dispatch(appSlice.actions.selectItem(id));
|
||||||
const items = useAppSelector((state) => state.app.items);
|
const items = useAppSelector(
|
||||||
|
(state) => state.app.history[currentStep]?.items,
|
||||||
|
);
|
||||||
|
|
||||||
const backgroundRect = useAppSelector((state) => state.app.background);
|
const backgroundRect = useAppSelector(
|
||||||
|
(state) => state.app.history[currentStep]?.backgroundColor,
|
||||||
|
);
|
||||||
const backgroundId = "background";
|
const backgroundId = "background";
|
||||||
const deselectHandler = (
|
const deselectHandler = (
|
||||||
e: KonvaEventObject<MouseEvent> | KonvaEventObject<TouchEvent>,
|
e: KonvaEventObject<MouseEvent> | KonvaEventObject<TouchEvent>,
|
||||||
@@ -337,6 +347,7 @@ const Canvas = () => {
|
|||||||
layer?.find(".guid-line").forEach((l) => l.destroy());
|
layer?.find(".guid-line").forEach((l) => l.destroy());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!stage) return null;
|
||||||
return (
|
return (
|
||||||
<div className="relative flex h-full w-full flex-col items-center">
|
<div className="relative flex h-full w-full flex-col items-center">
|
||||||
<Toolbar />
|
<Toolbar />
|
||||||
@@ -368,7 +379,7 @@ const Canvas = () => {
|
|||||||
<Rect
|
<Rect
|
||||||
width={stage.width}
|
width={stage.width}
|
||||||
height={stage.height}
|
height={stage.height}
|
||||||
fill={backgroundRect.fill}
|
fill={backgroundRect}
|
||||||
onTouchStart={deselectHandler}
|
onTouchStart={deselectHandler}
|
||||||
onClick={deselectHandler}
|
onClick={deselectHandler}
|
||||||
id={backgroundId}
|
id={backgroundId}
|
||||||
@@ -382,7 +393,7 @@ const Canvas = () => {
|
|||||||
clipHeight={stage.height - 2 * CANVAS_PADDING_Y}
|
clipHeight={stage.height - 2 * CANVAS_PADDING_Y}
|
||||||
ref={layerRef}
|
ref={layerRef}
|
||||||
>
|
>
|
||||||
{items.toReversed().map((item) => {
|
{items?.toReversed().map((item) => {
|
||||||
if (item.type === StageItemType.Image) {
|
if (item.type === StageItemType.Image) {
|
||||||
return (
|
return (
|
||||||
<TransformableImage
|
<TransformableImage
|
||||||
|
|||||||
@@ -11,7 +11,12 @@ import {
|
|||||||
import { useAppSelector } from "@/hooks";
|
import { useAppSelector } from "@/hooks";
|
||||||
|
|
||||||
export default function LayerBorder() {
|
export default function LayerBorder() {
|
||||||
const stage = useAppSelector((state) => state.app.stage);
|
const currentStep = useAppSelector((state) => state.app.currentStep);
|
||||||
|
|
||||||
|
const stage = useAppSelector(
|
||||||
|
(state) => state.app.history[currentStep]?.stage,
|
||||||
|
);
|
||||||
|
if (!stage) return null;
|
||||||
return (
|
return (
|
||||||
<Layer>
|
<Layer>
|
||||||
<Line
|
<Line
|
||||||
|
|||||||
@@ -1,12 +1,22 @@
|
|||||||
import { UserNav } from "./user-nav";
|
import { UserNav } from "./user-nav";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import logo from "@/assets/logo.png";
|
import logo from "@/assets/logo.png";
|
||||||
import { Switch } from "@/components/ui/switch";
|
import { useAppDispatch } from "@/hooks";
|
||||||
|
import { goBack, goForward } from "@/store/app.slice";
|
||||||
|
|
||||||
export const Navbar = () => {
|
export const Navbar = () => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const handleGoBack = () => {
|
||||||
|
dispatch(goBack());
|
||||||
|
};
|
||||||
|
const handleGoForward = () => {
|
||||||
|
dispatch(goForward());
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<div className="border-b">
|
<div className="border-b">
|
||||||
<div className="flex h-16 items-center px-4">
|
<div className="flex h-16 items-center px-4">
|
||||||
|
<button onClick={handleGoBack}>Back</button>
|
||||||
|
<button onClick={handleGoForward}>Forward</button>
|
||||||
<div className="relative h-12 w-12">
|
<div className="relative h-12 w-12">
|
||||||
<Image
|
<Image
|
||||||
src={logo}
|
src={logo}
|
||||||
|
|||||||
@@ -14,15 +14,12 @@ import { selectBackground } from "@/store/app.slice";
|
|||||||
export const BackgroundSelect = () => {
|
export const BackgroundSelect = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const bgColor = useAppSelector((state) => state.app.background);
|
const bgColor = useAppSelector((state) => state.app.backgroundColor);
|
||||||
|
|
||||||
const handleBackgroundSelect = (color: string) => {
|
const handleBackgroundSelect = (color: string) => {
|
||||||
dispatch(selectBackground(color));
|
dispatch(selectBackground(color));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSizeSelect = () => {
|
|
||||||
setOpen(false);
|
|
||||||
};
|
|
||||||
return (
|
return (
|
||||||
<Popover open={open} onOpenChange={setOpen}>
|
<Popover open={open} onOpenChange={setOpen}>
|
||||||
<PopoverTrigger asChild>
|
<PopoverTrigger asChild>
|
||||||
@@ -49,7 +46,7 @@ export const BackgroundSelect = () => {
|
|||||||
<input
|
<input
|
||||||
className="m-2 "
|
className="m-2 "
|
||||||
type="color"
|
type="color"
|
||||||
value={bgColor}
|
value={bgColor ?? undefined}
|
||||||
onChange={(e) => handleBackgroundSelect(e.target.value)}
|
onChange={(e) => handleBackgroundSelect(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</Label>
|
</Label>
|
||||||
|
|||||||
@@ -19,8 +19,12 @@ import LayerItem from "@/components/layer-item";
|
|||||||
|
|
||||||
export const Layers = () => {
|
export const Layers = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const items = useAppSelector((state) => state.app.items);
|
|
||||||
|
|
||||||
|
const currentStep = useAppSelector((state) => state.app.currentStep);
|
||||||
|
const items = useAppSelector(
|
||||||
|
(state) => state.app.history[currentStep]?.items,
|
||||||
|
);
|
||||||
|
if (!items) return;
|
||||||
const handleDragEnd = (e: DragEndEvent) => {
|
const handleDragEnd = (e: DragEndEvent) => {
|
||||||
const { active, over } = e;
|
const { active, over } = e;
|
||||||
const oldIndex = items.findIndex((item) => item.id === active.id);
|
const oldIndex = items.findIndex((item) => item.id === active.id);
|
||||||
|
|||||||
@@ -8,10 +8,16 @@ import { Separator } from "@/components/ui/separator";
|
|||||||
|
|
||||||
export const Toolbar = () => {
|
export const Toolbar = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const selectedItemId = useAppSelector((state) => state.app.selectedItemId);
|
const currentStep = useAppSelector((state) => state.app.currentStep);
|
||||||
const items = useAppSelector((state) => state.app.items);
|
|
||||||
|
|
||||||
const currentItem = items.find((t) => t.id === selectedItemId);
|
const selectedItemId = useAppSelector(
|
||||||
|
(state) => state.app.history[currentStep]?.selectedItemId,
|
||||||
|
);
|
||||||
|
const items = useAppSelector(
|
||||||
|
(state) => state.app.history[currentStep]?.items,
|
||||||
|
);
|
||||||
|
|
||||||
|
const currentItem = items?.find((t) => t.id === selectedItemId);
|
||||||
|
|
||||||
if (!currentItem || !selectedItemId) return null;
|
if (!currentItem || !selectedItemId) return null;
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import type { TransformableImageProps } from "@/components/transformable-image";
|
import type { TransformableImageProps } from "@/components/transformable-image";
|
||||||
import type { TransformableTextProps } from "@/components/transformable-text";
|
import type { TransformableTextProps } from "@/components/transformable-text";
|
||||||
import type { PayloadAction } from "@reduxjs/toolkit";
|
import type { AnyAction, PayloadAction } from "@reduxjs/toolkit";
|
||||||
import { createSlice } from "@reduxjs/toolkit";
|
import { createSlice, current } from "@reduxjs/toolkit";
|
||||||
import { v1 } from "uuid";
|
import { v1 } from "uuid";
|
||||||
import { type RectConfig } from "konva/lib/shapes/Rect";
|
|
||||||
import { type WritableDraft } from "immer/src/types/types-external";
|
import { type WritableDraft } from "immer/src/types/types-external";
|
||||||
|
|
||||||
export enum StageItemType {
|
export enum StageItemType {
|
||||||
@@ -31,10 +30,15 @@ type StageItemSpecific = StageTextItem | StageImageItem;
|
|||||||
export type StageItem = StageItemCommon & StageItemSpecific;
|
export type StageItem = StageItemCommon & StageItemSpecific;
|
||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
stage: { width: 500, height: 500, scale: 1, x: 0, y: 0 },
|
history: [
|
||||||
items: [] as StageItem[],
|
{
|
||||||
selectedItemId: null as string | null,
|
stage: { width: 500, height: 500, scale: 1, x: 0, y: 0 },
|
||||||
background: null as RectConfig | null,
|
items: [] as StageItem[],
|
||||||
|
selectedItemId: null as string | null,
|
||||||
|
backgroundColor: null as string | null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
currentStep: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultTextConfig = {
|
const defaultTextConfig = {
|
||||||
@@ -57,156 +61,260 @@ const defaultImageConfig = {
|
|||||||
scaleY: 1,
|
scaleY: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const addNewItemToHistory =
|
||||||
|
(
|
||||||
|
callback: (
|
||||||
|
state: WritableDraft<typeof initialState>,
|
||||||
|
action: any,
|
||||||
|
currentHistoryEntry: WritableDraft<
|
||||||
|
(typeof initialState)["history"][number]
|
||||||
|
>,
|
||||||
|
) => void,
|
||||||
|
) =>
|
||||||
|
(state: WritableDraft<typeof initialState>, action: any) => {
|
||||||
|
state.history.slice(0, state.currentStep + 1);
|
||||||
|
const currentHistoryEntry = state.history[state.currentStep];
|
||||||
|
if (!currentHistoryEntry) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state.currentStep = state.history.length;
|
||||||
|
callback(state, action, currentHistoryEntry);
|
||||||
|
};
|
||||||
|
|
||||||
export const appSlice = createSlice({
|
export const appSlice = createSlice({
|
||||||
name: "app",
|
name: "app",
|
||||||
initialState,
|
initialState,
|
||||||
reducers: {
|
reducers: {
|
||||||
setStageItems: (state, action: PayloadAction<StageItem[]>) => {
|
goBack: (state) => {
|
||||||
state.items = action.payload;
|
if (state.currentStep === 0) {
|
||||||
},
|
|
||||||
setStageScale: (
|
|
||||||
state,
|
|
||||||
action: PayloadAction<{ x: number; y: number; scale: number }>,
|
|
||||||
) => {
|
|
||||||
state.stage = {
|
|
||||||
...state.stage,
|
|
||||||
...action.payload,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
addText: (state, action: PayloadAction<{ initialValue: string }>) => {
|
|
||||||
const textId = v1();
|
|
||||||
state.items.push({
|
|
||||||
type: StageItemType.Text,
|
|
||||||
id: textId,
|
|
||||||
isBlocked: false,
|
|
||||||
params: {
|
|
||||||
text: action.payload.initialValue,
|
|
||||||
...defaultTextConfig,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
addImage: (
|
|
||||||
state,
|
|
||||||
action: PayloadAction<{
|
|
||||||
imageUrl: string;
|
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
}>,
|
|
||||||
) => {
|
|
||||||
const file = action.payload;
|
|
||||||
if (!file) return;
|
|
||||||
const imageId = v1();
|
|
||||||
state.items.push({
|
|
||||||
type: StageItemType.Image,
|
|
||||||
id: imageId,
|
|
||||||
isBlocked: false,
|
|
||||||
params: {
|
|
||||||
imageUrl: action.payload.imageUrl,
|
|
||||||
width: action.payload.width,
|
|
||||||
height: action.payload.height,
|
|
||||||
...defaultImageConfig,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
setBlockedItem: (
|
|
||||||
state,
|
|
||||||
action: PayloadAction<{ id: string; blocked: boolean }>,
|
|
||||||
) => {
|
|
||||||
const itemToUpdate = state.items.find(
|
|
||||||
(item) => item.id === action.payload.id,
|
|
||||||
);
|
|
||||||
if (!itemToUpdate) return;
|
|
||||||
|
|
||||||
if (state.selectedItemId === itemToUpdate.id) {
|
|
||||||
state.selectedItemId = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
itemToUpdate.isBlocked = action.payload.blocked;
|
|
||||||
},
|
|
||||||
|
|
||||||
selectBackground: (state, action: PayloadAction<string>) => {
|
|
||||||
state.background = {
|
|
||||||
fill: action.payload,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
selectItem: (state, action: PayloadAction<string>) => {
|
|
||||||
const itemToSelect = state.items.find(
|
|
||||||
(item) => item.id === action.payload,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!itemToSelect || itemToSelect.isBlocked) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
state.currentStep = state.currentStep - 1;
|
||||||
state.selectedItemId = action.payload;
|
|
||||||
},
|
},
|
||||||
|
goForward: (state) => {
|
||||||
deselectItem: (state) => {
|
console.log(current(state));
|
||||||
state.selectedItemId = null;
|
if (state.history.length - 1 === state.currentStep) {
|
||||||
},
|
|
||||||
|
|
||||||
updateStage: (
|
|
||||||
state,
|
|
||||||
action: PayloadAction<{ width?: number; height?: number }>,
|
|
||||||
) => {
|
|
||||||
state.stage = { ...state.stage, ...action.payload };
|
|
||||||
},
|
|
||||||
|
|
||||||
updateText: (
|
|
||||||
state,
|
|
||||||
action: PayloadAction<{ id: string } & Partial<StageTextItem["params"]>>,
|
|
||||||
) => {
|
|
||||||
const { id, ...params } = action.payload;
|
|
||||||
const textToUpdateIndex = state.items.findIndex((t) => t.id === id);
|
|
||||||
const textToUpdate = state.items[textToUpdateIndex];
|
|
||||||
|
|
||||||
if (
|
|
||||||
!textToUpdate ||
|
|
||||||
textToUpdate.type !== StageItemType.Text ||
|
|
||||||
textToUpdate.isBlocked
|
|
||||||
)
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
state.items[textToUpdateIndex] = {
|
state.currentStep = state.currentStep + 1;
|
||||||
...textToUpdate,
|
|
||||||
params: {
|
|
||||||
...textToUpdate.params,
|
|
||||||
...params,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
|
//+
|
||||||
|
setStageItems: addNewItemToHistory(
|
||||||
|
(state, action: PayloadAction<StageItem[]>, currentHistoryEntry) => {
|
||||||
|
const newHistoryEntry = {
|
||||||
|
...currentHistoryEntry,
|
||||||
|
items: action.payload,
|
||||||
|
};
|
||||||
|
state.history.push(newHistoryEntry);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// +
|
||||||
|
addText: addNewItemToHistory(
|
||||||
|
(
|
||||||
|
state,
|
||||||
|
action: PayloadAction<{ initialValue: string }>,
|
||||||
|
currentHistoryEntry,
|
||||||
|
) => {
|
||||||
|
const newEntry = JSON.parse(
|
||||||
|
JSON.stringify(currentHistoryEntry),
|
||||||
|
) as typeof currentHistoryEntry;
|
||||||
|
const textId = v1();
|
||||||
|
const newText = {
|
||||||
|
type: StageItemType.Text,
|
||||||
|
id: textId,
|
||||||
|
isBlocked: false,
|
||||||
|
params: {
|
||||||
|
text: action.payload.initialValue,
|
||||||
|
...defaultTextConfig,
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
newEntry.items = [...newEntry.items, newText];
|
||||||
|
state.history.push(newEntry);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// +
|
||||||
|
addImage: addNewItemToHistory(
|
||||||
|
(
|
||||||
|
state,
|
||||||
|
action: PayloadAction<{
|
||||||
|
imageUrl: string;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
}>,
|
||||||
|
currentHistoryEntry,
|
||||||
|
) => {
|
||||||
|
const file = action.payload;
|
||||||
|
if (!file) return;
|
||||||
|
const imageId = v1();
|
||||||
|
const newImage = {
|
||||||
|
type: StageItemType.Image,
|
||||||
|
id: imageId,
|
||||||
|
isBlocked: false,
|
||||||
|
params: {
|
||||||
|
imageUrl: action.payload.imageUrl,
|
||||||
|
width: action.payload.width,
|
||||||
|
height: action.payload.height,
|
||||||
|
...defaultImageConfig,
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
const newHistoryEntry = {
|
||||||
|
...currentHistoryEntry,
|
||||||
|
items: [...currentHistoryEntry.items, newImage],
|
||||||
|
};
|
||||||
|
state.history.push(newHistoryEntry);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// +
|
||||||
|
setBlockedItem: addNewItemToHistory(
|
||||||
|
(
|
||||||
|
state,
|
||||||
|
action: PayloadAction<{ id: string; blocked: boolean }>,
|
||||||
|
currentHistoryEntry,
|
||||||
|
) => {
|
||||||
|
const newEntry = JSON.parse(
|
||||||
|
JSON.stringify(currentHistoryEntry),
|
||||||
|
) as typeof currentHistoryEntry;
|
||||||
|
|
||||||
updateImage: (
|
const itemToUpdate = newEntry.items.find(
|
||||||
state,
|
(item) => item.id === action.payload.id,
|
||||||
action: PayloadAction<{ id: string } & Partial<StageImageItem["params"]>>,
|
);
|
||||||
) => {
|
if (!itemToUpdate) return;
|
||||||
const { id, ...params } = action.payload;
|
if (newEntry.selectedItemId === itemToUpdate.id) {
|
||||||
const imageToUpdateIndex = state.items.findIndex((img) => img.id === id);
|
newEntry.selectedItemId = null;
|
||||||
const imageToUpdate = state.items[imageToUpdateIndex];
|
}
|
||||||
|
itemToUpdate.isBlocked = action.payload.blocked;
|
||||||
|
state.history.push(newEntry);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// +
|
||||||
|
selectBackground: addNewItemToHistory(
|
||||||
|
(state, action: PayloadAction<string>, currentHistoryEntry) => {
|
||||||
|
state.history.push({
|
||||||
|
...currentHistoryEntry,
|
||||||
|
backgroundColor: action.payload,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// -
|
||||||
|
selectItem: addNewItemToHistory(
|
||||||
|
(state, action: PayloadAction<string>, currentHistoryEntry) => {
|
||||||
|
const itemToSelect = currentHistoryEntry.items.find(
|
||||||
|
(item) => item.id === action.payload,
|
||||||
|
);
|
||||||
|
|
||||||
if (
|
if (!itemToSelect || itemToSelect.isBlocked) {
|
||||||
!imageToUpdate ||
|
return;
|
||||||
imageToUpdate.type !== StageItemType.Image ||
|
}
|
||||||
imageToUpdate.isBlocked
|
state.history.push({
|
||||||
)
|
...currentHistoryEntry,
|
||||||
return;
|
selectedItemId: action.payload,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// -
|
||||||
|
deselectItem: addNewItemToHistory((state, _action, currentHistoryEntry) => {
|
||||||
|
state.history.push({
|
||||||
|
...currentHistoryEntry,
|
||||||
|
selectedItemId: null,
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
// -
|
||||||
|
updateStage: addNewItemToHistory(
|
||||||
|
(
|
||||||
|
state,
|
||||||
|
action: PayloadAction<{ width?: number; height?: number }>,
|
||||||
|
currentHistoryEntry,
|
||||||
|
) => {
|
||||||
|
currentHistoryEntry.stage = {
|
||||||
|
...currentHistoryEntry.stage,
|
||||||
|
...action.payload,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// +
|
||||||
|
updateText: addNewItemToHistory(
|
||||||
|
(
|
||||||
|
state,
|
||||||
|
action: PayloadAction<
|
||||||
|
{ id: string } & Partial<StageTextItem["params"]>
|
||||||
|
>,
|
||||||
|
currentHistoryEntry,
|
||||||
|
) => {
|
||||||
|
const { id, ...params } = action.payload;
|
||||||
|
const newEntry = JSON.parse(
|
||||||
|
JSON.stringify(currentHistoryEntry),
|
||||||
|
) as typeof currentHistoryEntry;
|
||||||
|
const textToUpdateIndex = newEntry.items.findIndex((t) => t.id === id);
|
||||||
|
const textToUpdate = newEntry.items[textToUpdateIndex];
|
||||||
|
|
||||||
(state.items[imageToUpdateIndex] as WritableDraft<StageImageItem>) = {
|
if (
|
||||||
...imageToUpdate,
|
!textToUpdate ||
|
||||||
params: {
|
textToUpdate.type !== StageItemType.Text ||
|
||||||
...imageToUpdate.params,
|
textToUpdate.isBlocked
|
||||||
...params,
|
)
|
||||||
},
|
return;
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
deleteStageItem: (state, action: PayloadAction<string>) => {
|
newEntry.items[textToUpdateIndex] = {
|
||||||
return {
|
...textToUpdate,
|
||||||
...state,
|
params: {
|
||||||
items: state.items.filter((item) => item.id !== action.payload),
|
...textToUpdate.params,
|
||||||
};
|
...params,
|
||||||
},
|
},
|
||||||
|
};
|
||||||
|
state.history.push(newEntry);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// +
|
||||||
|
updateImage: addNewItemToHistory(
|
||||||
|
(
|
||||||
|
state,
|
||||||
|
action: PayloadAction<
|
||||||
|
{ id: string } & Partial<StageImageItem["params"]>
|
||||||
|
>,
|
||||||
|
currentHistoryEntry,
|
||||||
|
) => {
|
||||||
|
const { id, ...params } = action.payload;
|
||||||
|
const newEntry = JSON.parse(
|
||||||
|
JSON.stringify(currentHistoryEntry),
|
||||||
|
) as typeof currentHistoryEntry;
|
||||||
|
const imageToUpdateIndex = newEntry.items.findIndex(
|
||||||
|
(img) => img.id === id,
|
||||||
|
);
|
||||||
|
const imageToUpdate = newEntry.items[imageToUpdateIndex];
|
||||||
|
|
||||||
|
if (
|
||||||
|
!imageToUpdate ||
|
||||||
|
imageToUpdate.type !== StageItemType.Image ||
|
||||||
|
imageToUpdate.isBlocked
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
|
||||||
|
newEntry.items = newEntry.items.map((item, index) => {
|
||||||
|
if (index !== imageToUpdateIndex) return item;
|
||||||
|
return {
|
||||||
|
...imageToUpdate,
|
||||||
|
params: {
|
||||||
|
...imageToUpdate.params,
|
||||||
|
...params,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
state.history.push(newEntry);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// +
|
||||||
|
deleteStageItem: addNewItemToHistory(
|
||||||
|
(state, action: PayloadAction<string>, currentHistoryEntry) => {
|
||||||
|
const newEntry = {
|
||||||
|
...currentHistoryEntry,
|
||||||
|
items: currentHistoryEntry.items.filter(
|
||||||
|
(item) => item.id !== action.payload,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
state.history.push(newEntry);
|
||||||
|
},
|
||||||
|
),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -222,4 +330,6 @@ export const {
|
|||||||
setStageItems,
|
setStageItems,
|
||||||
selectItem,
|
selectItem,
|
||||||
setBlockedItem,
|
setBlockedItem,
|
||||||
|
goBack,
|
||||||
|
goForward,
|
||||||
} = appSlice.actions;
|
} = appSlice.actions;
|
||||||
|
|||||||
Reference in New Issue
Block a user