diff --git a/src/App.tsx b/src/App.tsx
index 694ccd3..5078e2a 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -5,6 +5,7 @@ import Inspector from './components/Inspector';
import Toolbar from './components/Toolbar';
import Zoom from './components/Zoom';
import { isDrawingAtom, onKeyPressAtom } from './store/store';
+import HistoryControls from './components/HistoryControls';
const App = () => {
const [, onKeyPress] = useAtom(onKeyPressAtom);
@@ -30,7 +31,10 @@ const App = () => {
{/* */}
-
+
+
+
+
);
};
diff --git a/src/components/HistoryControls.tsx b/src/components/HistoryControls.tsx
new file mode 100644
index 0000000..6210d0a
--- /dev/null
+++ b/src/components/HistoryControls.tsx
@@ -0,0 +1,56 @@
+import { useAtom } from 'jotai';
+import { useHistoryAtom } from '../store/store';
+
+const HistoryControls = () => {
+ const [, doRedo] = useAtom(useHistoryAtom);
+
+ return (
+ //
+
+
+
+
+
+ //
+ );
+};
+
+export default HistoryControls;
diff --git a/src/components/SingleElement.tsx b/src/components/SingleElement.tsx
index 7ff6903..92f97f0 100644
--- a/src/components/SingleElement.tsx
+++ b/src/components/SingleElement.tsx
@@ -33,7 +33,7 @@ const SingleElement = ({ element }: Props) => {
onMouseDown={() => onDragStart(element)}
onMouseUp={onMouseUp}
>
- {element.type === 'foreignObject' && }
+ {element.type_name === 'text' && }
{isSelected && !isDrawing && }
diff --git a/src/components/Zoom.tsx b/src/components/Zoom.tsx
index 450b358..a635bd6 100644
--- a/src/components/Zoom.tsx
+++ b/src/components/Zoom.tsx
@@ -7,39 +7,39 @@ const Zoom = () => {
const [, zoomCanvas] = useAtom(zoomCanvasAtom);
return (
-
-
-
+ //
+
+
-
+
-
-
+
+ //
);
};
diff --git a/src/components/elements/textElement/Textarea.tsx b/src/components/elements/textElement/Textarea.tsx
index b1520ac..317d0e3 100644
--- a/src/components/elements/textElement/Textarea.tsx
+++ b/src/components/elements/textElement/Textarea.tsx
@@ -1,6 +1,10 @@
import { useRef } from 'react';
import { useAtom, useAtomValue } from 'jotai';
-import { isDrawingAtom, updateElementsAtom } from '../../../store/store';
+import {
+ isDrawingAtom,
+ setHistoryAtom,
+ updateElementsAtom,
+} from '../../../store/store';
import { Element } from '../../../types/CommonTypes';
type Props = {
@@ -9,6 +13,7 @@ type Props = {
const Textarea = ({ element }: Props) => {
const [, updateElements] = useAtom(updateElementsAtom);
+ const [, setHistory] = useAtom(setHistoryAtom);
const isDrawing = useAtomValue(isDrawingAtom);
const textareaRef = useRef
(null);
@@ -31,6 +36,7 @@ const Textarea = ({ element }: Props) => {
ref={textareaRef}
rows={1}
autoFocus={isDrawing}
+ onBlur={setHistory}
style={{
width: '100%',
height: '100%',
diff --git a/src/store/store.ts b/src/store/store.ts
index d858208..e2084a1 100644
--- a/src/store/store.ts
+++ b/src/store/store.ts
@@ -49,6 +49,10 @@ const initialCanvasViewBox = {
export const creationInitialElementAtom = atom(initialElement)
+export const historyAtom = atomWithStorage("history", [[]])
+
+export const currentHistoryIndexAtom = atomWithStorage("historyIndex", 0)
+
export const elementsAtom = atomWithStorage("elements", [])
export const selectedElementAtom = atom([])
@@ -164,9 +168,15 @@ export const updateElementsAtom = atom(
&& !get(isDraggingAtom)
&& !get(isDrawingAtom)
&& !get(resizeVectorAtom)
- ) set(selectedElementAtom, (prev) => {
- return prev?.map(el => el.id === updatedElement.id ? updatedElement : el) || null
- })
+ ) {
+ set(selectedElementAtom, (prev) => {
+ return prev?.map(el => el.id === updatedElement.id ? updatedElement : el) || null
+ })
+ if (updatedElement.type_name !== 'text') {
+ set(setHistoryAtom)
+ }
+ }
+
// if drawing with pencil we need to put fresh d
if (updatedElement.type_name === "pencil" && get(isDrawingAtom)) {
set(selectedElementAtom, (prev) => {
@@ -182,6 +192,7 @@ export const deleteElementsAtom = atom(
const selectedElement = get(selectedElementAtom)
set(elementsAtom, (prev) => prev.filter((el) => !selectedElement.find((selectedEl) => selectedEl.id === el.id)))
set(selectedElementAtom, [])
+ set(setHistoryAtom)
}
)
@@ -397,6 +408,7 @@ export const onMouseUpAtom = atom(
set(resizeVectorAtom, "")
set(isDraggingAtom, false)
set(selectingAreaAtom, null)
+ set(setHistoryAtom)
const isNeedtoResetCreatinType = creationInitialElement.type_name === "pencil" || creationInitialElement.type_name === "grab"
if (!isNeedtoResetCreatinType) {
@@ -448,6 +460,36 @@ export const duplicateAtom = atom(
set(elementsAtom, prev => [...prev, duplicatedElemment])
set(selectedElementAtom, prev => [...prev, duplicatedElemment])
+ set(setHistoryAtom)
+ })
+ }
+)
+
+export const setHistoryAtom = atom(
+ null,
+ (get, set) => {
+ set(historyAtom, (prev) => {
+ // it's need to cut newest and continue build history chain from some previous snapshot if user was clicked undo btn
+ const previousHistoryFromCurrentIndex = prev.slice(get(currentHistoryIndexAtom))
+ if (get(elementsAtom) !== previousHistoryFromCurrentIndex[0] && !!get(elementsAtom).length) {
+ return [get(elementsAtom), ...previousHistoryFromCurrentIndex]
+ } else { return prev }
})
+ set(currentHistoryIndexAtom, 0)
}
-)
\ No newline at end of file
+)
+
+export const useHistoryAtom = atom(
+ // this call of history (and current history index) is need to enable localStorage and use this history after refresh page when clicking hystory control btn
+ (get) => { get(historyAtom), get(currentHistoryIndexAtom) },
+ (get, set, direction: number) => {
+ const history = get(historyAtom)
+ const newIndex = get(currentHistoryIndexAtom) + direction
+ const isEndOfHistory = history.length - 1 < newIndex || newIndex < 0
+ if (!isEndOfHistory) {
+ const snapshot = history[newIndex]
+ set(elementsAtom, snapshot)
+ set(currentHistoryIndexAtom, newIndex)
+ }
+ }
+)