refactor(editor): stabilize updateWidgetProps typing

This commit is contained in:
clawdbot 2026-01-28 23:34:53 +08:00
parent 3c3c2b74aa
commit 4e7bf37fdd

View File

@ -16,11 +16,17 @@ import { didRectsChange } from './history';
import { snapRect, snapRectResize } from './snap';
import type { EditorState, ResizeHandle } from './types';
import { clampRectToBounds } from './types';
type UpdateWidgetPropsAction =
| { type: 'updateWidgetProps'; widgetType: 'text'; id: string; props: Partial<WidgetPropsByType['text']> }
| { type: 'updateWidgetProps'; widgetType: 'image'; id: string; props: Partial<WidgetPropsByType['image']> }
| { type: 'updateWidgetProps'; widgetType: 'iframe'; id: string; props: Partial<WidgetPropsByType['iframe']> }
| { type: 'updateWidgetProps'; widgetType: 'video'; id: string; props: Partial<WidgetPropsByType['video']> };
type EditorWidgetKind = 'text' | 'image' | 'iframe' | 'video';
type UpdateWidgetPropsAction = {
type: 'updateWidgetProps';
id: string;
} & {
[K in EditorWidgetKind]: {
widgetType: K;
props: Partial<WidgetPropsByType[K]>;
};
}[EditorWidgetKind];
export type EditorAction =
| { type: 'keyboard'; ctrl: boolean; space: boolean }
@ -95,12 +101,11 @@ function isWidgetType<K extends WidgetKind>(
return node.type === widgetType;
}
function updateWidgetProps<K extends WidgetKind>(
state: EditorRuntimeState,
action: Extract<UpdateWidgetPropsAction, { widgetType: K }>,
): EditorRuntimeState {
function updateWidgetProps(state: EditorRuntimeState, action: UpdateWidgetPropsAction): EditorRuntimeState {
// Helper to keep the per-widget prop merge strongly typed.
const update = <K extends EditorWidgetKind>(widgetType: K, props: Partial<WidgetPropsByType[K]>): EditorRuntimeState => {
const target = state.doc.screen.nodes.find(
(n): n is WidgetNodeByType[K] => n.id === action.id && n.type === action.widgetType,
(n): n is WidgetNodeByType[K] => n.id === action.id && n.type === widgetType,
);
if (!target) return state;
@ -110,15 +115,30 @@ function updateWidgetProps<K extends WidgetKind>(
screen: {
...state.doc.screen,
nodes: state.doc.screen.nodes.map((n) => {
if (n.id !== action.id || !isWidgetType(n, action.widgetType)) return n;
if (n.id !== action.id || !isWidgetType(n, widgetType)) return n;
return {
...n,
props: { ...n.props, ...action.props },
props: { ...n.props, ...props },
};
}),
},
},
};
};
switch (action.widgetType) {
case 'text':
return update('text', action.props);
case 'image':
return update('image', action.props);
case 'iframe':
return update('iframe', action.props);
case 'video':
return update('video', action.props);
default:
// If the SDK adds a new widget kind, the editor can safely ignore it until supported.
return state;
}
}
function historyPush(state: EditorRuntimeState): EditorRuntimeState {
@ -253,18 +273,7 @@ export function editorReducer(state: EditorRuntimeState, action: EditorAction):
}
case 'updateWidgetProps': {
switch (action.widgetType) {
case 'text':
return updateWidgetProps(state, action);
case 'image':
return updateWidgetProps(state, action);
case 'iframe':
return updateWidgetProps(state, action);
case 'video':
return updateWidgetProps(state, action);
default:
return assertNever(action);
}
}
case 'deleteSelected': {