refactor: improve goView import (video/iframe) + editor state
This commit is contained in:
parent
858097bfba
commit
01b7a3a722
@ -1,4 +1,5 @@
|
||||
import {
|
||||
assertNever,
|
||||
convertGoViewProjectToScreen,
|
||||
createEmptyScreen,
|
||||
migrateScreen,
|
||||
@ -78,13 +79,13 @@ interface BoxSelectSession {
|
||||
baseIds: string[];
|
||||
}
|
||||
|
||||
type InternalEditorState = EditorState & {
|
||||
type EditorRuntimeState = EditorState & {
|
||||
__pan?: PanSession;
|
||||
__boxSelect?: BoxSelectSession;
|
||||
__drag?: DragSession;
|
||||
};
|
||||
|
||||
function historyPush(state: EditorState): EditorState {
|
||||
function historyPush(state: EditorRuntimeState): EditorRuntimeState {
|
||||
return {
|
||||
...state,
|
||||
history: {
|
||||
@ -94,12 +95,12 @@ function historyPush(state: EditorState): EditorState {
|
||||
};
|
||||
}
|
||||
|
||||
function ensureSelected(state: EditorState, id: string): EditorState {
|
||||
function ensureSelected(state: EditorRuntimeState, id: string): EditorRuntimeState {
|
||||
if (state.selection.ids.includes(id)) return state;
|
||||
return { ...state, selection: { ids: [id] } };
|
||||
}
|
||||
|
||||
export function createInitialState(): EditorState {
|
||||
export function createInitialState(): EditorRuntimeState {
|
||||
const screen = createEmptyScreen({
|
||||
width: 1920,
|
||||
height: 1080,
|
||||
@ -141,8 +142,8 @@ export function createInitialState(): EditorState {
|
||||
};
|
||||
}
|
||||
|
||||
export function editorReducer(state: EditorState, action: EditorAction): EditorState {
|
||||
const s = state as InternalEditorState;
|
||||
export function editorReducer(state: EditorRuntimeState, action: EditorAction): EditorRuntimeState {
|
||||
const s = state;
|
||||
switch (action.type) {
|
||||
case 'keyboard':
|
||||
return { ...state, keyboard: { ctrl: action.ctrl, space: action.space } };
|
||||
@ -454,7 +455,7 @@ export function editorReducer(state: EditorState, action: EditorAction): EditorS
|
||||
startPanX: state.canvas.panX,
|
||||
startPanY: state.canvas.panY,
|
||||
},
|
||||
} as EditorState;
|
||||
};
|
||||
}
|
||||
|
||||
case 'updatePan': {
|
||||
@ -476,15 +477,14 @@ export function editorReducer(state: EditorState, action: EditorAction): EditorS
|
||||
|
||||
case 'endPan': {
|
||||
if (!state.canvas.isPanning) return state;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { __pan: _pan, ...rest } = s;
|
||||
return {
|
||||
...rest,
|
||||
...state,
|
||||
canvas: {
|
||||
...state.canvas,
|
||||
isPanning: false,
|
||||
},
|
||||
} as EditorState;
|
||||
__pan: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
case 'selectSingle':
|
||||
@ -525,7 +525,7 @@ export function editorReducer(state: EditorState, action: EditorAction): EditorS
|
||||
},
|
||||
selection: { ids: baseIds },
|
||||
__boxSelect: { additive: action.additive, baseIds },
|
||||
} as EditorState;
|
||||
};
|
||||
}
|
||||
|
||||
case 'updateBoxSelect': {
|
||||
@ -567,16 +567,15 @@ export function editorReducer(state: EditorState, action: EditorAction): EditorS
|
||||
|
||||
case 'endBoxSelect': {
|
||||
if (!state.canvas.isBoxSelecting) return state;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { __boxSelect: _box, ...rest } = s;
|
||||
return {
|
||||
...rest,
|
||||
...state,
|
||||
canvas: {
|
||||
...state.canvas,
|
||||
isBoxSelecting: false,
|
||||
mouse: { ...state.canvas.mouse, offsetX: 0, offsetY: 0, offsetStartX: 0, offsetStartY: 0 },
|
||||
},
|
||||
} as EditorState;
|
||||
__boxSelect: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
case 'beginMove': {
|
||||
@ -614,7 +613,7 @@ export function editorReducer(state: EditorState, action: EditorAction): EditorS
|
||||
},
|
||||
},
|
||||
__drag: drag,
|
||||
} as EditorState;
|
||||
};
|
||||
}
|
||||
|
||||
case 'updateMove': {
|
||||
@ -669,17 +668,19 @@ export function editorReducer(state: EditorState, action: EditorAction): EditorS
|
||||
|
||||
case 'endMove': {
|
||||
const drag = s.__drag;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { __drag: _drag, ...rest } = s;
|
||||
|
||||
if (!drag || drag.kind !== 'move') {
|
||||
return { ...rest, canvas: { ...state.canvas, isDragging: false, guides: { xs: [], ys: [] } } };
|
||||
return {
|
||||
...state,
|
||||
canvas: { ...state.canvas, isDragging: false, guides: { xs: [], ys: [] } },
|
||||
__drag: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
const changed = didRectsChange(drag.snapshot, state.doc.screen);
|
||||
return {
|
||||
...rest,
|
||||
...state,
|
||||
canvas: { ...state.canvas, isDragging: false, guides: { xs: [], ys: [] } },
|
||||
__drag: undefined,
|
||||
history: changed
|
||||
? {
|
||||
past: [...state.history.past, { screen: drag.beforeScreen }],
|
||||
@ -709,7 +710,7 @@ export function editorReducer(state: EditorState, action: EditorAction): EditorS
|
||||
...next,
|
||||
canvas: { ...next.canvas, isDragging: true, guides: { xs: [], ys: [] } },
|
||||
__drag: drag,
|
||||
} as EditorState;
|
||||
};
|
||||
}
|
||||
|
||||
case 'updateResize': {
|
||||
@ -774,17 +775,19 @@ export function editorReducer(state: EditorState, action: EditorAction): EditorS
|
||||
|
||||
case 'endResize': {
|
||||
const drag = s.__drag;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { __drag: _drag, ...rest } = s;
|
||||
|
||||
if (!drag || drag.kind !== 'resize') {
|
||||
return { ...rest, canvas: { ...state.canvas, isDragging: false, guides: { xs: [], ys: [] } } };
|
||||
return {
|
||||
...state,
|
||||
canvas: { ...state.canvas, isDragging: false, guides: { xs: [], ys: [] } },
|
||||
__drag: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
const changed = didRectsChange(drag.snapshot, state.doc.screen);
|
||||
return {
|
||||
...rest,
|
||||
...state,
|
||||
canvas: { ...state.canvas, isDragging: false, guides: { xs: [], ys: [] } },
|
||||
__drag: undefined,
|
||||
history: changed
|
||||
? {
|
||||
past: [...state.history.past, { screen: drag.beforeScreen }],
|
||||
@ -830,7 +833,7 @@ export function editorReducer(state: EditorState, action: EditorAction): EditorS
|
||||
}
|
||||
|
||||
default:
|
||||
return state;
|
||||
return assertNever(action);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -270,7 +270,22 @@ function looksLikeIframeOption(option: unknown): boolean {
|
||||
if (
|
||||
hasAnyKeyDeep(option, ['iframeUrl', 'iframeSrc', 'embedUrl', 'frameUrl', 'frameSrc'], 2) ||
|
||||
// Some exports store raw HTML instead of a URL.
|
||||
hasAnyKeyDeep(option, ['srcdoc', 'srcDoc', 'html', 'htmlContent', 'content', 'template'], 2)
|
||||
hasAnyKeyDeep(
|
||||
option,
|
||||
[
|
||||
'srcdoc',
|
||||
'srcDoc',
|
||||
'html',
|
||||
'htmlContent',
|
||||
'htmlString',
|
||||
'iframeHtml',
|
||||
'embedHtml',
|
||||
'embedCode',
|
||||
'content',
|
||||
'template',
|
||||
],
|
||||
2,
|
||||
)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
@ -401,6 +416,12 @@ function normalizeOption(option: unknown): unknown {
|
||||
return option;
|
||||
}
|
||||
|
||||
function toMaybeString(v: unknown): string | undefined {
|
||||
if (typeof v === 'string') return v;
|
||||
if (typeof v === 'number' && Number.isFinite(v)) return String(v);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function toNumber(v: unknown, fallback: number): number {
|
||||
if (typeof v === 'number' && Number.isFinite(v)) return v;
|
||||
if (typeof v === 'string') {
|
||||
@ -451,6 +472,15 @@ function pickSizeLike(option: unknown): { w?: number; h?: number } {
|
||||
return { w, h };
|
||||
}
|
||||
|
||||
function normalizeRect(
|
||||
rect: { x: number; y: number; w: number; h: number },
|
||||
fallback: { w: number; h: number },
|
||||
): { x: number; y: number; w: number; h: number } {
|
||||
const w = rect.w > 0 ? rect.w : fallback.w;
|
||||
const h = rect.h > 0 ? rect.h : fallback.h;
|
||||
return { x: rect.x, y: rect.y, w, h };
|
||||
}
|
||||
|
||||
export function convertGoViewProjectToScreen(input: GoViewProjectLike | GoViewStorageLike): Screen {
|
||||
// goView exports vary a lot; attempt a few common nesting shapes.
|
||||
const root = input as unknown as Record<string, unknown>;
|
||||
@ -566,7 +596,7 @@ export function convertGoViewProjectToScreen(input: GoViewProjectLike | GoViewSt
|
||||
const optSize = pickSizeLike(option);
|
||||
|
||||
const attr = c.attr as unknown as Record<string, unknown> | undefined;
|
||||
const rect = attr
|
||||
const rawRect = attr
|
||||
? {
|
||||
x: toNumber(attr.x, 0),
|
||||
y: toNumber(attr.y, 0),
|
||||
@ -575,13 +605,15 @@ export function convertGoViewProjectToScreen(input: GoViewProjectLike | GoViewSt
|
||||
h: toNumber(attr.h, optSize.h ?? defaults.h),
|
||||
}
|
||||
: { x: 0, y: 0, w: optSize.w ?? defaults.w, h: optSize.h ?? defaults.h };
|
||||
const rect = normalizeRect(rawRect, defaults);
|
||||
|
||||
const zIndex = attr?.zIndex === undefined ? undefined : toNumber(attr.zIndex, 0);
|
||||
const baseId = toMaybeString(c.id);
|
||||
|
||||
if (inferredType === 'text') {
|
||||
const props = convertGoViewTextOptionToNodeProps(option as GoViewTextOption);
|
||||
nodes.push({
|
||||
id: c.id ?? `import_text_${Math.random().toString(16).slice(2)}`,
|
||||
id: baseId ?? `import_text_${Math.random().toString(16).slice(2)}`,
|
||||
type: 'text',
|
||||
rect,
|
||||
zIndex,
|
||||
@ -595,7 +627,7 @@ export function convertGoViewProjectToScreen(input: GoViewProjectLike | GoViewSt
|
||||
if (inferredType === 'image') {
|
||||
const props = convertGoViewImageOptionToNodeProps(option as GoViewImageOption);
|
||||
nodes.push({
|
||||
id: c.id ?? `import_image_${Math.random().toString(16).slice(2)}`,
|
||||
id: baseId ?? `import_image_${Math.random().toString(16).slice(2)}`,
|
||||
type: 'image',
|
||||
rect,
|
||||
zIndex,
|
||||
@ -609,7 +641,7 @@ export function convertGoViewProjectToScreen(input: GoViewProjectLike | GoViewSt
|
||||
if (inferredType === 'iframe') {
|
||||
const props = convertGoViewIframeOptionToNodeProps(option as GoViewIframeOption);
|
||||
nodes.push({
|
||||
id: c.id ?? `import_iframe_${Math.random().toString(16).slice(2)}`,
|
||||
id: baseId ?? `import_iframe_${Math.random().toString(16).slice(2)}`,
|
||||
type: 'iframe',
|
||||
rect,
|
||||
zIndex,
|
||||
@ -623,7 +655,7 @@ export function convertGoViewProjectToScreen(input: GoViewProjectLike | GoViewSt
|
||||
if (inferredType === 'video') {
|
||||
const props = convertGoViewVideoOptionToNodeProps(option as GoViewVideoOption);
|
||||
nodes.push({
|
||||
id: c.id ?? `import_video_${Math.random().toString(16).slice(2)}`,
|
||||
id: baseId ?? `import_video_${Math.random().toString(16).slice(2)}`,
|
||||
type: 'video',
|
||||
rect,
|
||||
zIndex,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user