Skip to content

Commit c4af590

Browse files
add editor global state management
1 parent 162c2c4 commit c4af590

21 files changed

Lines changed: 315 additions & 3 deletions

editor/babel.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ module.exports = {
1212
layouts: "./layouts",
1313
scaffolds: "./scaffolds",
1414
utils: "./utils",
15+
core: "./core",
1516
public: "./public",
1617
hooks: "./hooks",
1718
},

editor/core/actions/index.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
export type WorkspaceAction =
2+
//
3+
HistoryAction;
4+
5+
export type HistoryAction =
6+
//
7+
| { type: "undo" }
8+
//
9+
| { type: "redo" }
10+
| Action;
11+
12+
export type Action =
13+
//
14+
PageAction;
15+
16+
export type ActionType = Action["type"];
17+
18+
export type PageAction = SelectPageAction;
19+
20+
export interface SelectPageAction {
21+
type: "select-page";
22+
page: string;
23+
}

editor/core/dispatch/index.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { Action } from "../actions";
2+
import { createContext, useCallback, useContext } from "react";
3+
4+
export type Dispatcher = (action: Action) => void;
5+
6+
export type FlatDispatcher = (action: Action) => void;
7+
8+
const __noop = () => {};
9+
10+
export const DispatchContext = createContext<Dispatcher>(__noop);
11+
12+
export const useDispatch = (): FlatDispatcher => {
13+
const dispatch = useContext(DispatchContext);
14+
return useCallback(
15+
(action: Action) => {
16+
dispatch(action);
17+
},
18+
[dispatch]
19+
);
20+
};
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { Action } from "core/actions";
2+
import { EditorState } from "core/states";
3+
4+
export function editorReducer(state: EditorState, action: Action): EditorState {
5+
// TODO: handle actions here.
6+
switch (action.type) {
7+
case "select-page": {
8+
// return pageReducer(state, action);
9+
}
10+
}
11+
12+
return state;
13+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import produce from "immer";
2+
import { ActionType, HistoryAction } from "core/actions";
3+
import { EditorState, HistoryEntry, HistoryState } from "core/states";
4+
import { editorReducer } from "../editor-reducer";
5+
6+
export function historyReducer(state: HistoryState, action: HistoryAction) {
7+
const currentState = state.present;
8+
switch (action.type) {
9+
case "undo":
10+
if (state.past.length === 0) {
11+
return state;
12+
} else {
13+
return produce(state, (draft) => {
14+
const nextPresent = draft.past.pop();
15+
if (nextPresent) {
16+
draft.future.unshift(
17+
createHistoryEntry(nextPresent.actionType, currentState)
18+
);
19+
draft.present = nextPresent.state;
20+
}
21+
});
22+
}
23+
case "redo":
24+
if (state.future.length === 0) {
25+
return state;
26+
} else {
27+
return produce(state, (draft) => {
28+
const nextPresent = draft.future.shift();
29+
if (nextPresent) {
30+
draft.past.push(
31+
createHistoryEntry(nextPresent.actionType, currentState)
32+
);
33+
draft.present = nextPresent.state;
34+
}
35+
});
36+
}
37+
default:
38+
const nextState = editorReducer(currentState, action);
39+
const mergableEntry = getMergableHistoryEntry(state, action[0]);
40+
41+
return produce(state, (draft) => {
42+
const historyEntry = createHistoryEntry(action[0], {
43+
...currentState,
44+
});
45+
46+
if (mergableEntry) {
47+
draft.past[draft.past.length - 1] = {
48+
...historyEntry,
49+
state: mergableEntry.state,
50+
};
51+
} else {
52+
draft.past.push(historyEntry);
53+
}
54+
draft.future = [];
55+
draft.present = nextState;
56+
});
57+
}
58+
}
59+
60+
function createHistoryEntry(
61+
actionType: ActionType,
62+
state: EditorState
63+
): HistoryEntry {
64+
return {
65+
actionType,
66+
state,
67+
timestamp: Date.now(),
68+
};
69+
}
70+
71+
const CHANGE_DIFF_DURATION_MS = 300;
72+
73+
function getMergableHistoryEntry(
74+
state: HistoryState,
75+
actionType: ActionType
76+
): HistoryEntry | undefined {
77+
if (state.past.length === 0) {
78+
return;
79+
}
80+
81+
const newTimestamp = Date.now();
82+
const previousEntry = state.past[state.past.length - 1];
83+
84+
if (
85+
actionType !== previousEntry.actionType ||
86+
newTimestamp - previousEntry.timestamp > CHANGE_DIFF_DURATION_MS
87+
) {
88+
return;
89+
}
90+
91+
return previousEntry;
92+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from "./history-reducer";

editor/core/reducers/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from "./workspace-reducer";
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { WorkspaceState } from "core/states";
2+
import produce from "immer";
3+
import { WorkspaceAction } from "../actions";
4+
import { historyReducer } from "./history";
5+
6+
export function workspaceReducer(
7+
state: WorkspaceState,
8+
action: WorkspaceAction
9+
): WorkspaceState {
10+
switch (action.type) {
11+
default: {
12+
return produce(state, (draft) => {
13+
draft.history = historyReducer(state.history, action);
14+
});
15+
}
16+
}
17+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { createContext } from "react";
2+
import { WorkspaceState } from "./workspace-state";
3+
4+
export const StateContext = createContext<WorkspaceState | undefined>(
5+
undefined
6+
);
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { EditorSnapshot, EditorState } from "./editor-state";
2+
3+
export function createInitialEditorState(app: EditorSnapshot): EditorState {
4+
return {
5+
selectedPage: app.selectedPage,
6+
selectedNodes: app.selectedNodes,
7+
design: app.design,
8+
};
9+
}

0 commit comments

Comments
 (0)