refactor: improve goView import and editor selection
This commit is contained in:
parent
01b7a3a722
commit
903b0da44a
@ -156,7 +156,11 @@ export function Canvas(props: CanvasProps) {
|
||||
// (Left-click on background already clears unless additive via box-select.)
|
||||
}
|
||||
|
||||
props.onOpenContextMenu({ clientX: e.clientX, clientY: e.clientY, worldX: p.x, worldY: p.y, targetId });
|
||||
props.onOpenContextMenu(
|
||||
targetId
|
||||
? { kind: 'node', clientX: e.clientX, clientY: e.clientY, worldX: p.x, worldY: p.y, targetId }
|
||||
: { kind: 'canvas', clientX: e.clientX, clientY: e.clientY, worldX: p.x, worldY: p.y },
|
||||
);
|
||||
};
|
||||
|
||||
const onBackgroundPointerDown = (e: React.PointerEvent) => {
|
||||
|
||||
@ -1,22 +1,32 @@
|
||||
import { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
||||
import { Typography } from 'antd';
|
||||
|
||||
export type ContextMenuState = {
|
||||
export type ContextMenuState =
|
||||
| {
|
||||
kind: 'canvas';
|
||||
clientX: number;
|
||||
clientY: number;
|
||||
worldX: number;
|
||||
worldY: number;
|
||||
targetId?: string;
|
||||
};
|
||||
}
|
||||
| {
|
||||
kind: 'node';
|
||||
clientX: number;
|
||||
clientY: number;
|
||||
worldX: number;
|
||||
worldY: number;
|
||||
targetId: string;
|
||||
};
|
||||
|
||||
export function ContextMenu(props: {
|
||||
state: ContextMenuState | null;
|
||||
selectionIds: string[];
|
||||
selectionAllLocked: boolean;
|
||||
selectionSomeLocked?: boolean;
|
||||
selectionSomeLocked: boolean;
|
||||
selectionHasUnlocked: boolean;
|
||||
selectionAllHidden: boolean;
|
||||
selectionSomeHidden?: boolean;
|
||||
hasAnyNodes?: boolean;
|
||||
selectionSomeHidden: boolean;
|
||||
hasAnyNodes: boolean;
|
||||
onClose: () => void;
|
||||
onAddTextAt: (x: number, y: number) => void;
|
||||
onSelectSingle?: (id?: string) => void;
|
||||
@ -88,8 +98,10 @@ export function ContextMenu(props: {
|
||||
if (!ctx || !position) return null;
|
||||
|
||||
const hasSelection = props.selectionIds.length > 0;
|
||||
const hasTarget = !!ctx.targetId;
|
||||
const targetInSelection = !!ctx.targetId && props.selectionIds.includes(ctx.targetId);
|
||||
const canModifySelection = hasSelection && props.selectionHasUnlocked;
|
||||
const hasTarget = ctx.kind === 'node';
|
||||
const targetId = hasTarget ? ctx.targetId : undefined;
|
||||
const targetInSelection = !!targetId && props.selectionIds.includes(targetId);
|
||||
const canSelectSingle = !!props.onSelectSingle;
|
||||
|
||||
return (
|
||||
@ -124,7 +136,7 @@ export function ContextMenu(props: {
|
||||
<MenuItem
|
||||
label="Select Only"
|
||||
onClick={() => {
|
||||
props.onSelectSingle?.(ctx.targetId);
|
||||
props.onSelectSingle?.(targetId);
|
||||
onClose();
|
||||
}}
|
||||
/>
|
||||
@ -154,7 +166,7 @@ export function ContextMenu(props: {
|
||||
|
||||
<MenuItem
|
||||
label="Duplicate"
|
||||
disabled={!hasSelection || !props.onDuplicateSelected}
|
||||
disabled={!canModifySelection || !props.onDuplicateSelected}
|
||||
onClick={() => {
|
||||
props.onDuplicateSelected?.();
|
||||
onClose();
|
||||
@ -212,7 +224,7 @@ export function ContextMenu(props: {
|
||||
<MenuItem
|
||||
label="Delete"
|
||||
danger
|
||||
disabled={!hasSelection || !props.onDeleteSelected}
|
||||
disabled={!canModifySelection || !props.onDeleteSelected}
|
||||
onClick={() => {
|
||||
props.onDeleteSelected?.();
|
||||
onClose();
|
||||
|
||||
@ -55,6 +55,7 @@ export function EditorApp() {
|
||||
|
||||
const selectionAllLocked = selection.length > 0 && selection.every((n) => n.locked);
|
||||
const selectionSomeLocked = selection.length > 0 && selection.some((n) => n.locked) && !selectionAllLocked;
|
||||
const selectionHasUnlocked = selection.length > 0 && selection.some((n) => !n.locked);
|
||||
|
||||
const selectionAllHidden = selection.length > 0 && selection.every((n) => n.hidden);
|
||||
const selectionSomeHidden = selection.length > 0 && selection.some((n) => n.hidden) && !selectionAllHidden;
|
||||
@ -475,6 +476,7 @@ export function EditorApp() {
|
||||
}
|
||||
}
|
||||
setCtxMenu({
|
||||
kind: 'node',
|
||||
clientX: e.clientX,
|
||||
clientY: e.clientY,
|
||||
worldX: node.rect.x + node.rect.w / 2,
|
||||
@ -512,6 +514,7 @@ export function EditorApp() {
|
||||
selectionIds={state.selection.ids}
|
||||
selectionAllLocked={selectionAllLocked}
|
||||
selectionSomeLocked={selectionSomeLocked}
|
||||
selectionHasUnlocked={selectionHasUnlocked}
|
||||
selectionAllHidden={selectionAllHidden}
|
||||
selectionSomeHidden={selectionSomeHidden}
|
||||
hasAnyNodes={state.doc.screen.nodes.length > 0}
|
||||
|
||||
@ -287,15 +287,19 @@ export function editorReducer(state: EditorRuntimeState, action: EditorAction):
|
||||
case 'deleteSelected': {
|
||||
if (!state.selection.ids.length) return state;
|
||||
const ids = new Set(state.selection.ids);
|
||||
const deletableIds = new Set(
|
||||
state.doc.screen.nodes.filter((n) => ids.has(n.id) && !n.locked).map((n) => n.id),
|
||||
);
|
||||
if (!deletableIds.size) return state;
|
||||
return {
|
||||
...historyPush(state),
|
||||
doc: {
|
||||
screen: {
|
||||
...state.doc.screen,
|
||||
nodes: state.doc.screen.nodes.filter((n) => !ids.has(n.id)),
|
||||
nodes: state.doc.screen.nodes.filter((n) => !deletableIds.has(n.id)),
|
||||
},
|
||||
},
|
||||
selection: { ids: [] },
|
||||
selection: { ids: state.selection.ids.filter((id) => !deletableIds.has(id)) },
|
||||
};
|
||||
}
|
||||
|
||||
@ -328,7 +332,7 @@ export function editorReducer(state: EditorRuntimeState, action: EditorAction):
|
||||
const clones: WidgetNode[] = [];
|
||||
|
||||
for (const n of state.doc.screen.nodes) {
|
||||
if (!ids.has(n.id)) continue;
|
||||
if (!ids.has(n.id) || n.locked) continue;
|
||||
const id = `${n.type}_${Date.now()}_${Math.random().toString(16).slice(2)}`;
|
||||
clones.push({
|
||||
...n,
|
||||
|
||||
@ -521,17 +521,19 @@ export function convertGoViewProjectToScreen(input: GoViewProjectLike | GoViewSt
|
||||
nodes: [],
|
||||
});
|
||||
|
||||
const componentList =
|
||||
const componentListRaw =
|
||||
(input as GoViewStorageLike).componentList ??
|
||||
(input as GoViewProjectLike).componentList ??
|
||||
(data?.componentList as GoViewComponentLike[] | undefined) ??
|
||||
(state?.componentList as GoViewComponentLike[] | undefined) ??
|
||||
(project?.componentList as GoViewComponentLike[] | undefined) ??
|
||||
[];
|
||||
const componentList = Array.isArray(componentListRaw) ? componentListRaw : [];
|
||||
|
||||
const nodes: Array<TextWidgetNode | ImageWidgetNode | IframeWidgetNode | VideoWidgetNode> = [];
|
||||
|
||||
for (const raw of componentList) {
|
||||
if (!raw || typeof raw !== 'object') continue;
|
||||
const c = unwrapComponent(raw);
|
||||
const option = normalizeOption(optionOf(c));
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user