Skip to content
This repository was archived by the owner on Jul 8, 2025. It is now read-only.

Commit 6ec36da

Browse files
committed
feat(editor): supporting time-traveling
1 parent 70f51ba commit 6ec36da

File tree

17 files changed

+693
-68
lines changed

17 files changed

+693
-68
lines changed

package-lock.json

Lines changed: 543 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@
4545
"json-schema": "^0.2.5",
4646
"json-schema-defaults": "^0.4.0",
4747
"lodash.template": "^4.5.0",
48-
"mmlpx": "^4.0.2",
4948
"mobx": "^5.15.4",
5049
"mobx-react": "^6.1.8",
5150
"promise-timeout": "^1.3.0",

packages/editor/config-overrides.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ module.exports = {
5252
javascriptEnabled: true,
5353
}),
5454
setOutputPublicPath('/editor/'),
55-
addWebpackPlugin(new BundleAnalyzerPlugin()),
55+
// addWebpackPlugin(new BundleAnalyzerPlugin()),
5656
process.env.NODE_ENV === 'production'
5757
? addWebpackPlugin(
5858
new SentryWebpackPlugin({

packages/editor/src/api/user.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const DEBUG_USER = {
1313
createdTime: new Date(),
1414
bizs: [],
1515
isAdmin: 1,
16+
isDeveloper: 0,
1617
};
1718

1819
export async function getCurrentUser() {

packages/editor/src/components/ContextMenu/utils.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ export function createMouseEventFromIframe(e: React.MouseEvent): MouseEvent {
55
e.persist();
66

77
const [deltaX, deltaY] = getSimulatorNodeOffset();
8-
console.log(deltaX, deltaY);
98
const event = document.createEvent('MouseEvent');
109
event.initMouseEvent(
1110
e.type,

packages/editor/src/components/Simulator/index.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
height: 36px;
3333
left: calc(50vw - 140px);
3434
bottom: 18px;
35-
z-index: 999;
35+
z-index: 998;
3636

3737
& > div {
3838
cursor: pointer;

packages/editor/src/i18n/resources/zh.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,6 @@ export const zh = {
196196
'Universal triggers': '通用触发',
197197
'Hotarea Manager': '热区管理',
198198
Home: '主页',
199-
Intro: '总览',
200199
Pages: '页面',
201200
Templates: '模板',
202201
Materials: '物料',

packages/editor/src/index.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import * as React from 'react';
22
import { render } from 'react-dom';
33
import { App } from './App';
4-
import { initSentryReport } from './utils/report';
4+
import { isDev, initSentryReport } from './utils';
55
import './styles/index.scss';
66
import './styles/override.less';
77

88
function bootstrap() {
9-
initSentryReport();
9+
if (!isDev()) {
10+
initSentryReport();
11+
}
1012
return render(<App />, document.getElementById('main-entry'));
1113
}
1214

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { walkAndSerialize } from './utils';
2+
import { runInAction } from 'mobx';
3+
4+
const recordStates: { [key: string]: object } = {};
5+
const stacks: typeof recordStates[] = [];
6+
let cursor = 0;
7+
8+
(window as any).stacks = stacks;
9+
(window as any).undo = undo;
10+
(window as any).redo = redo;
11+
12+
export function undo() {
13+
if (stacks.length <= 1 || cursor === 0) {
14+
return;
15+
}
16+
const snapshot = stacks[--cursor];
17+
return restoreSnapshot(snapshot);
18+
}
19+
20+
export function redo() {
21+
if (stacks.length <= 1 || cursor === stacks.length) {
22+
return;
23+
}
24+
const snapshot = stacks[++cursor];
25+
return restoreSnapshot(snapshot);
26+
}
27+
28+
function restoreSnapshot(snapshot: typeof recordStates) {
29+
return runInAction(() => {
30+
Object.entries(snapshot).forEach(([key, v]) => {
31+
Object.assign(recordStates[key], v);
32+
});
33+
});
34+
}
35+
36+
export function recordHistory(states: typeof recordStates) {
37+
Object.assign(recordStates, states);
38+
updateSnapshots(true);
39+
}
40+
41+
let inReaction = false;
42+
export const withHistory = (
43+
target: { [key: string]: any },
44+
propertyKey: string,
45+
descriptor?: PropertyDescriptor,
46+
): void => {
47+
const { initializer } = descriptor as any;
48+
descriptor!.value = function(...args: Parameters<ReturnType<typeof initializer>>) {
49+
const reaction = inReaction;
50+
if (!reaction) {
51+
inReaction = true;
52+
}
53+
const result = initializer.apply(this).apply(this, args);
54+
if (!reaction) {
55+
setTimeout(async () => {
56+
if (result instanceof Promise) {
57+
await result;
58+
}
59+
updateSnapshots();
60+
inReaction = false;
61+
}, 0);
62+
}
63+
return result;
64+
};
65+
};
66+
67+
function updateSnapshots(assign = false) {
68+
const snapshot = Object.entries(recordStates).reduce<{ [key: string]: object }>((accu, [k, v]) => {
69+
accu[k] = walkAndSerialize(v);
70+
return accu;
71+
}, {});
72+
if (assign && stacks.length) {
73+
Object.assign(stacks[stacks.length - 1], snapshot);
74+
} else {
75+
stacks.push(snapshot);
76+
}
77+
cursor = stacks.length - 1;
78+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { isFunction } from '../../utils';
2+
3+
export type Snapshot = {
4+
[propName: string]: any;
5+
};
6+
7+
function isObject(obj: any) {
8+
return Object.prototype.toString.call(obj) === '[object Object]';
9+
}
10+
function isMap(obj: any) {
11+
return (
12+
Object.prototype.toString.call(obj) === '[object Map]' ||
13+
(obj && isFunction(obj.delete) && isFunction(obj.get) && isFunction(obj.set))
14+
);
15+
}
16+
function isArray(obj: any) {
17+
return obj && Array.isArray(obj);
18+
}
19+
20+
export function walkAndSerialize(model: any) {
21+
if (isArray(model)) {
22+
return model.length ? model.map(walkAndSerialize) : [];
23+
}
24+
25+
if (isMap(model)) {
26+
if (model.size) {
27+
const map: { [key: string]: any } = {};
28+
model.forEach((value: any, key: string) => {
29+
map[key] = walkAndSerialize(value);
30+
});
31+
return map;
32+
}
33+
return {};
34+
}
35+
36+
if (isObject(model)) {
37+
return Object.keys(model).reduce((acc, stateName) => {
38+
const value = walkAndSerialize(model[stateName]);
39+
if (value !== undefined) {
40+
acc[stateName] = value;
41+
}
42+
return acc;
43+
}, {} as Snapshot);
44+
}
45+
46+
if (isFunction(model)) {
47+
return undefined;
48+
}
49+
50+
return model;
51+
}

0 commit comments

Comments
 (0)