11<template >
2- <!-- Get passed down to MyLayout.vue
3- & listen for custom events emitted from MyLayout.vue -->
42 <router-view
53 v-on:undo =" undoTrigger"
64 v-on:redo =" redoTrigger"
108</template >
119
1210<script setup>
13- // import { defineComponent } from "vue";
14- // import ElementPlus from 'element-plus'; // importing element plus component library
15- import " element-plus/dist/index.css" ; // importing element plus styling
16- // import { defaultState } from "./store/state/index.ts";
11+ /* IMPORTS */
1712import { onMounted , ref } from " vue" ;
1813import { useStore } from " ./store/main.js" ;
1914const deepEqual = require (" lodash.isequal" );
2015const cloneDeep = require (" lodash.clonedeep" );
2116const throttle = require (" lodash.throttle" );
2217
18+ /* LIFECYCLE HOOKS */
19+
20+ onMounted (() => {
21+ const throttledUndo = throttle (undo, 300 );
22+ const throttledRedo = throttle (redo, 300 );
23+ // undo function calling
24+ window .addEventListener (" keydown" , (event ) => {
25+ if ((event .ctrlKey || event .metaKey ) && event .key === " z" ) {
26+ event .preventDefault ();
27+ throttledUndo ();
28+ }
29+ });
30+ // redo function calling
31+ window .addEventListener (" keydown" , (event ) => {
32+ if ((event .ctrlKey || event .metaKey ) && event .key === " y" ) {
33+ event .preventDefault ();
34+ throttledRedo ();
35+ }
36+ });
37+ });
38+
2339// use this to make sure these actions don't count as things you "undo"
2440const ignoredActions = new Set ([
2541 " setActiveComponent" ,
@@ -29,6 +45,7 @@ const ignoredActions = new Set([
2945 " updateComponentNameInputValue" ,
3046]);
3147
48+ /* DATA */
3249const doneAction = ref ([]);
3350const undoneAction = ref ([]);
3451const isTimetraveling = ref (false );
@@ -58,25 +75,6 @@ store.$onAction((action) => {
5875 }
5976});
6077
61- onMounted (() => {
62- const throttledUndo = throttle (undo, 300 );
63- const throttledRedo = throttle (redo, 300 );
64- // undo function calling
65- window .addEventListener (" keydown" , (event ) => {
66- if ((event .ctrlKey || event .metaKey ) && event .key === " z" ) {
67- event .preventDefault ();
68- throttledUndo ();
69- }
70- });
71- // redo function calling
72- window .addEventListener (" keydown" , (event ) => {
73- if ((event .ctrlKey || event .metaKey ) && event .key === " y" ) {
74- event .preventDefault ();
75- throttledRedo ();
76- }
77- });
78- });
79-
8078const undo = () => {
8179 if (doneAction .value .length === 0 ) {
8280 return ;
@@ -85,11 +83,6 @@ const undo = () => {
8583 isTimetraveling .value = true ;
8684 let undone = doneAction .value .pop ();
8785
88- /* assuming we have have something to undo, we check if we are undoing an action that has no feedback
89- e.g.: changing active elements or active components or going up and down a layer since these can be obscured from immediate feedback.
90- these will feel bad to undo (imagine someone clicking around out of boredom and then deciding to undo
91- this will force them to have to undo the 30 highlights they just did instead of the last "Real" action)
92- if this happens we keep popping the doneAction.value queue and building up the redo queue. */
9386 if (undone !== undefined ) {
9487 undoneAction .value .push (undone);
9588 if (ignoredActions .has (undone .name )) {
@@ -99,8 +92,7 @@ const undo = () => {
9992 ) {
10093 undoneAction .value .push (doneAction .value .pop ());
10194 }
102- /* if we get here, that means we have undone all "useless" actions
103- so we have to do one more final pop and push, have to make sure it isn't null though */
95+
10496 let finalPop = doneAction .value .pop ();
10597 if (finalPop !== undefined ) {
10698 undoneAction .value .push (finalPop);
@@ -119,14 +111,14 @@ const undo = () => {
119111
120112const redo = () => {
121113 let action = undoneAction .pop ();
122-
123- // we have to set timeTraveling to true to preserve the undoneAction array while we make changes
124114 isTimetraveling .value = true ;
115+
125116 if (action) {
126117 const actionName = action .name ;
127118 store[actionName](cloneDeep (action .args [0 ]));
128119 }
129120 isTimetraveling .value = false ;
121+
130122 if (action && ignoredActions .has (action .name )) {
131123 redo ();
132124 }
@@ -143,157 +135,4 @@ const redoTrigger = () => {
143135 const throttledRedo = throttle (redo, 300 );
144136 throttledRedo ();
145137};
146- </script >
147-
148- <!-- old options api script -->
149- <!-- <script>
150- import { defineComponent } from "vue";
151- import ElementPlus from 'element-plus'; // importing element plus component library
152- import 'element-plus/dist/index.css'; // importing element plus styling
153- const deepEqual = require("lodash.isequal");
154- const cloneDeep = require("lodash.clonedeep");
155- const throttle = require("lodash.throttle");
156- import { defaultState } from "./store/state/index.ts";
157-
158- // use this to make sure these actions don't count as things you "undo"
159- const ignoredActions = new Set([
160- "setActiveComponent",
161- "setActiveLayer",
162- "upOneLayer",
163- "setActiveHTML",
164- "updateComponentNameInputValue",
165- ]);
166-
167- let redoMixin = {
168- data() {
169- return {
170- doneAction: [],
171- undoneAction: [],
172- isTimetraveling: false,
173- initialState: {},
174- };
175- },
176- created() {
177- this.$store.subscribeAction((action, state) => {
178- if (typeof action.payload === "object") {
179- action.payload = cloneDeep(action.payload);
180- }
181- this.doneAction.push(action);
182- if (!this.isTimetraveling) {
183- if (this.undoneAction[this.undoneAction.length - 1]) {
184- if (
185- action.type ===
186- this.undoneAction[this.undoneAction.length - 1].type &&
187- deepEqual(
188- action.payload,
189- this.undoneAction[this.undoneAction.length - 1].payload
190- )
191- ) {
192- this.undoneAction.pop();
193- } else {
194- this.undoneAction = [];
195- }
196- }
197- }
198- });
199- },
200- // undo + redo function calling
201- // metaKey accounts for Command Key on Mac
202- mounted() {
203- const throttledUndo = throttle(this.undo, 300);
204- const throttledRedo = throttle(this.redo, 300);
205- // undo function calling
206- window.addEventListener("keydown", (event) => {
207- if ((event.ctrlKey || event.metaKey) && event.key === "z") {
208- event.preventDefault();
209- throttledUndo();
210- }
211- });
212- // redo function calling
213- window.addEventListener("keydown", (event) => {
214- if ((event.ctrlKey || event.metaKey) && event.key === "y") {
215- event.preventDefault();
216- throttledRedo();
217- }
218- });
219-
220- this.initialState = defaultState(this.$store.state);
221- },
222-
223- methods: {
224- undo: function () {
225- if (this.doneAction.length === 0) {
226- return;
227- }
228-
229- this.isTimetraveling = true;
230- let undone = this.doneAction.pop();
231-
232- /* assuming we have have something to undo, we check if we are undoing an action that has no feedback
233- e.g.: changing active elements or active components or going up and down a layer since these can be obscured from immediate feedback.
234- these will feel bad to undo (imagine someone clicking around out of boredom and then deciding to undo
235- this will force them to have to undo the 30 highlights they just did instead of the last "Real" action)
236- if this happens we keep popping the doneAction queue and building up the redo queue. */
237- if (undone !== undefined) {
238- this.undoneAction.push(undone);
239- if (ignoredActions.has(undone.type)) {
240- while (
241- this.doneAction[this.doneAction.length - 1] &&
242- ignoredActions.has(this.doneAction[this.doneAction.length - 1].type)
243- ) {
244- this.undoneAction.push(this.doneAction.pop());
245- }
246- /* if we get here, that means we have undone all "useless" actions
247- so we have to do one more final pop and push, have to make sure it isn't null though */
248- let finalPop = this.doneAction.pop();
249- if (finalPop !== undefined) {
250- this.undoneAction.push(finalPop);
251- }
252- }
253- }
254-
255- let payload = {
256- initialState: this.initialState,
257- store: this.$store,
258- };
259- this.$store.commit("EMPTY_STATE", payload);
260- this.doneAction.forEach((action) => {
261- this.$store.dispatch(action.type, cloneDeep(action.payload));
262- this.doneAction.pop();
263- });
264- this.isTimetraveling = false;
265- },
266-
267- redo: function () {
268- let action = this.undoneAction.pop();
269-
270- // we have to set timeTraveling to true to preserve the undoneAction array while we make changes
271- this.isTimetraveling = true;
272- if (action) {
273- this.$store.dispatch(action.type, cloneDeep(action.payload));
274- }
275- this.isTimetraveling = false;
276- if (action && ignoredActions.has(action.type)) {
277- this.redo();
278- }
279- },
280-
281- // Undo triggered from MyLayout.vue
282- undoTrigger: function () {
283- const throttledUndo = throttle(this.undo, 300);
284- throttledUndo();
285- },
286-
287- // Redo triggered from MyLayout.vue
288- redoTrigger: function () {
289- const throttledRedo = throttle(this.redo, 300);
290- throttledRedo();
291- },
292- },
293- };
294-
295- export default defineComponent({
296- name: "App",
297- mixins: [redoMixin],
298- });
299- </script> -->
138+ </script >
0 commit comments