Skip to content

Commit 75e9e9f

Browse files
cleanup editor state tree
1 parent 10c1339 commit 75e9e9f

7 files changed

Lines changed: 181 additions & 81 deletions

File tree

editor/core/actions/index.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import type { FrameworkConfig } from "@designto/config";
2+
import type { EditorState } from "core/states";
3+
14
export type WorkspaceAction =
25
//
36
| HistoryAction
@@ -12,13 +15,10 @@ export type HistoryAction =
1215
| Action;
1316

1417
export type Action =
15-
//
1618
| PageAction
17-
//
1819
| SelectNodeAction
19-
//
2020
| HighlightLayerAction
21-
//
21+
| CanvasModeAction
2222
| CodeEditorAction;
2323

2424
export type ActionType = Action["type"];
@@ -41,12 +41,23 @@ export interface HighlightLayerAction {
4141
id: string;
4242
}
4343

44+
type CanvasModeAction = CanvasModeSwitchAction | CanvasModeGobackAction;
45+
export interface CanvasModeSwitchAction {
46+
type: "canvas-mode-switch";
47+
mode: EditorState["canvasMode"];
48+
}
49+
50+
export interface CanvasModeGobackAction {
51+
type: "canvas-mode-goback";
52+
fallback?: EditorState["canvasMode"];
53+
}
54+
4455
export type CodeEditorAction = CodeEditorEditComponentCodeAction;
4556

4657
export interface CodeEditorEditComponentCodeAction {
4758
type: "code-editor-edit-component-code";
4859
id: string;
49-
framework: string;
60+
framework: FrameworkConfig["framework"];
5061
componentName: string;
5162
raw: string;
5263
}

editor/core/reducers/editor-reducer.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,20 @@ import type {
44
SelectNodeAction,
55
SelectPageAction,
66
CodeEditorEditComponentCodeAction,
7+
CanvasModeSwitchAction,
8+
CanvasModeGobackAction,
79
} from "core/actions";
810
import { EditorState } from "core/states";
911
import { useRouter } from "next/router";
1012
import { CanvasStateStore } from "@code-editor/canvas/stores";
13+
import assert from "assert";
1114

1215
const _editor_path_name = "/files/[key]/";
1316

1417
export function editorReducer(state: EditorState, action: Action): EditorState {
1518
const router = useRouter();
1619
const filekey = state.design.key;
1720

18-
// TODO: handle actions here.
1921
switch (action.type) {
2022
case "select-node": {
2123
const { node } = <SelectNodeAction>action;
@@ -80,14 +82,40 @@ export function editorReducer(state: EditorState, action: Action): EditorState {
8082
case "code-editor-edit-component-code": {
8183
const { ...rest } = <CodeEditorEditComponentCodeAction>action;
8284
return produce(state, (draft) => {
83-
draft.editingCode = {
85+
draft.editingModule = {
8486
...rest,
8587
type: "single-file-component",
8688
lang: "unknown",
8789
};
8890
});
8991
//
9092
}
93+
case "canvas-mode-switch": {
94+
const { mode } = <CanvasModeSwitchAction>action;
95+
96+
router.push({
97+
pathname: _editor_path_name,
98+
query: { ...router.query, mode: mode },
99+
// no need to set shallow here.
100+
});
101+
102+
return produce(state, (draft) => {
103+
draft.canvasMode_previous = draft.canvasMode;
104+
draft.canvasMode = mode;
105+
});
106+
}
107+
case "canvas-mode-goback": {
108+
const { fallback } = <CanvasModeGobackAction>action;
109+
return produce(state, (draft) => {
110+
const dest = draft.canvasMode_previous ?? fallback;
111+
assert(
112+
dest,
113+
"canvas-mode-goback: cannot resolve destination. (no fallback provided)"
114+
);
115+
draft.canvasMode_previous = draft.canvasMode; // swap
116+
draft.canvasMode = dest; // previous or fallback
117+
});
118+
}
91119
default:
92120
throw new Error(`Unhandled action type: ${action["type"]}`);
93121
}

editor/core/states/editor-initial-state.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export function createInitialEditorState(editor: EditorSnapshot): EditorState {
77
selectedNodesInitial: editor.selectedNodes,
88
selectedLayersOnPreview: editor.selectedLayersOnPreview,
99
design: editor.design,
10+
canvasMode: editor.canvasMode,
1011
};
1112
}
1213

@@ -17,5 +18,6 @@ export function createPendingEditorState(): EditorState {
1718
selectedNodesInitial: null,
1819
selectedLayersOnPreview: [],
1920
design: null,
21+
canvasMode: "free",
2022
};
2123
}

editor/core/states/editor-state.ts

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
11
import type { ReflectSceneNode } from "@design-sdk/figma-node";
2+
import type { FrameworkConfig } from "@designto/config";
23
import { ComponentNode } from "@design-sdk/figma-types";
34
import { DesignInput } from "@designto/config/input";
45

6+
/**
7+
* View mode of the canvas.
8+
* - full - default
9+
* - isolated - focus to one scene
10+
*/
11+
type TCanvasMode = "free" | "isolated-view" | "fullscreen-preview";
12+
513
export interface EditorState {
614
selectedPage: string;
715
selectedNodes: string[];
@@ -13,13 +21,10 @@ export interface EditorState {
1321
*/
1422
selectedNodesInitial?: string[] | null;
1523
design: FigmaReflectRepository;
16-
editingCode?: {
17-
type: "single-file-component";
18-
componentName: string;
19-
framework: string;
20-
lang: string;
21-
raw: string;
22-
};
24+
canvasMode: TCanvasMode;
25+
canvasMode_previous?: TCanvasMode;
26+
code?: CodeRepository;
27+
editingModule?: EditingModule;
2328
}
2429

2530
export interface EditorSnapshot {
@@ -28,6 +33,7 @@ export interface EditorSnapshot {
2833
selectedLayersOnPreview: string[];
2934
selectedNodesInitial?: string[] | null;
3035
design: FigmaReflectRepository;
36+
canvasMode: TCanvasMode;
3137
}
3238

3339
export interface FigmaReflectRepository {
@@ -47,3 +53,18 @@ export interface FigmaReflectRepository {
4753
// styles: { [key: string]: {} };
4854
input: DesignInput;
4955
}
56+
57+
export interface CodeRepository {
58+
// TODO:
59+
// files: { [key: string]: string };
60+
}
61+
62+
type TEditingModuleType = "single-file-component";
63+
64+
export interface EditingModule {
65+
type: TEditingModuleType;
66+
componentName: string;
67+
framework: FrameworkConfig["framework"];
68+
lang: string;
69+
raw: string;
70+
}

editor/pages/files/[key]/index.tsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ function SetupEditor({
6464
initialDispatcher({ type: "update", value: action });
6565
}, []);
6666

67+
const initialCanvasMode = q_map_canvas_mode_from_query(
68+
router.query.mode as string
69+
); // TODO: change this to reflect the nodeid input
70+
6771
const initWith = (file: FileResponse) => {
6872
const prevstate =
6973
initialState.type == "success" && initialState.value.history.present;
@@ -107,6 +111,7 @@ function SetupEditor({
107111
key: filekey,
108112
pages: pages,
109113
},
114+
canvasMode: initialCanvasMode,
110115
};
111116
}
112117

@@ -219,3 +224,25 @@ function SetupEditor({
219224
220225
}
221226
*/
227+
228+
const q_map_canvas_mode_from_query = (
229+
mode: string
230+
): EditorSnapshot["canvasMode"] => {
231+
switch (mode) {
232+
case "free":
233+
case "isolated-view":
234+
case "fullscreen-preview":
235+
return mode;
236+
237+
// -------------------------
238+
// legacy query param key
239+
case "full":
240+
return "free";
241+
case "isolate":
242+
return "isolated-view";
243+
// -------------------------
244+
245+
default:
246+
return "free";
247+
}
248+
};

editor/scaffolds/canvas/canvas.tsx

Lines changed: 61 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useEffect, useState } from "react";
1+
import React, { useCallback } from "react";
22
import styled from "@emotion/styled";
33
import { Canvas } from "@code-editor/canvas";
44
import { useEditorState, useWorkspace } from "core/states";
@@ -7,57 +7,62 @@ import useMeasure from "react-use-measure";
77
import { useDispatch } from "core/dispatch";
88
import { FrameTitleRenderer } from "./render/frame-title";
99
import { IsolateModeCanvas } from "./isolate-mode";
10-
import { useRouter } from "next/router";
11-
12-
type ViewMode = "full" | "isolate";
13-
const _editor_path_name = "/files/[key]/";
10+
import { Dialog } from "@material-ui/core";
11+
import { FullScreenPreview } from "scaffolds/preview-full-screen";
1412

1513
/**
1614
* Statefull canvas segment that contains canvas as a child, with state-data connected.
1715
*/
1816
export function VisualContentArea() {
1917
const [state] = useEditorState();
20-
const router = useRouter();
21-
const { mode: q_mode } = router.query;
22-
23-
// this hook is used for focusing the node on the first load with the initial selection is provided externally.
24-
useEffect(() => {
25-
// if the initial selection is available, and not empty &&
26-
if (state.selectedNodesInitial?.length && q_mode == "isolate") {
27-
// trigger isolation mode once.
28-
setMode("isolate");
29-
30-
// TODO: set explicit canvas initial transform.
31-
// make the canvas fit to the initial target even when the isolation mode is complete by the user.
32-
}
33-
}, [state.selectedNodesInitial]);
34-
3518
const [canvasSizingRef, canvasBounds] = useMeasure();
3619

3720
const { highlightedLayer, highlightLayer } = useWorkspace();
3821
const dispatch = useDispatch();
3922

40-
const { selectedPage, design, selectedNodes } = state;
23+
const { selectedPage, design, selectedNodes, canvasMode } = state;
4124

4225
const thisPageNodes = selectedPage
43-
? state.design.pages
44-
.find((p) => p.id == selectedPage)
45-
.children.filter(Boolean)
26+
? design.pages.find((p) => p.id == selectedPage).children.filter(Boolean)
4627
: [];
4728

4829
const isEmptyPage = thisPageNodes?.length === 0;
4930

50-
const [mode, _setMode] = useState<ViewMode>("full");
31+
const startIsolatedViewMode = useCallback(
32+
() =>
33+
dispatch({
34+
type: "canvas-mode-switch",
35+
mode: "isolated-view",
36+
}),
37+
[dispatch]
38+
);
39+
40+
const startFullscreenPreviewMode = useCallback(
41+
() =>
42+
dispatch({
43+
type: "canvas-mode-switch",
44+
mode: "fullscreen-preview",
45+
}),
46+
[dispatch]
47+
);
5148

52-
const setMode = (m: ViewMode) => {
53-
_setMode(m);
49+
const endIsolatedViewMode = useCallback(
50+
() =>
51+
dispatch({
52+
type: "canvas-mode-switch",
53+
mode: "free",
54+
}),
55+
[dispatch]
56+
);
5457

55-
// update the router
56-
router.push({
57-
pathname: _editor_path_name,
58-
query: { ...router.query, mode: m },
59-
});
60-
};
58+
const exitFullscreenPreview = useCallback(
59+
() =>
60+
dispatch({
61+
type: "canvas-mode-goback",
62+
fallback: "isolated-view",
63+
}),
64+
[dispatch]
65+
);
6166

6267
return (
6368
<CanvasContainer ref={canvasSizingRef} id="canvas">
@@ -67,16 +72,19 @@ export function VisualContentArea() {
6772
<></>
6873
) : (
6974
<>
70-
{mode == "isolate" && (
75+
<FullScreenPreviewContainer
76+
show={canvasMode == "fullscreen-preview"}
77+
onExit={exitFullscreenPreview}
78+
/>
79+
{canvasMode == "isolated-view" && (
7180
<IsolateModeCanvas
72-
onClose={() => {
73-
setMode("full");
74-
}}
81+
onClose={endIsolatedViewMode}
82+
onEnterFullscreen={startFullscreenPreviewMode}
7583
/>
7684
)}
7785
<div
7886
style={{
79-
display: mode == "full" ? undefined : "none",
87+
display: canvasMode !== "free" && "none",
8088
}}
8189
>
8290
<Canvas
@@ -112,9 +120,7 @@ export function VisualContentArea() {
112120
<FrameTitleRenderer
113121
key={p.id}
114122
{...p}
115-
onRunClick={() => {
116-
setMode("isolate");
117-
}}
123+
onRunClick={startIsolatedViewMode}
118124
/>
119125
)}
120126
/>
@@ -125,6 +131,20 @@ export function VisualContentArea() {
125131
);
126132
}
127133

134+
function FullScreenPreviewContainer({
135+
onExit,
136+
show,
137+
}: {
138+
onExit: () => void;
139+
show: boolean;
140+
}) {
141+
return (
142+
<Dialog fullScreen onClose={onExit} open={show}>
143+
<FullScreenPreview onClose={onExit} />
144+
</Dialog>
145+
);
146+
}
147+
128148
const CanvasContainer = styled.div`
129149
display: flex;
130150
flex-direction: column;

0 commit comments

Comments
 (0)