From 2a5372ecb34e1d61a1366a8c3795824de2ce5966 Mon Sep 17 00:00:00 2001 From: ErSan Date: Tue, 27 Jan 2026 20:33:05 +0800 Subject: [PATCH] fix: improve goView media imports and editor context menu parity --- packages/editor/src/editor/Canvas.tsx | 44 ++++++++++++++++++------ packages/sdk/src/core/goview/convert.ts | 26 ++++++++++++-- packages/sdk/src/core/widgets/urlLike.ts | 24 +++++++++++-- 3 files changed, 79 insertions(+), 15 deletions(-) diff --git a/packages/editor/src/editor/Canvas.tsx b/packages/editor/src/editor/Canvas.tsx index 4d37546..6ece543 100644 --- a/packages/editor/src/editor/Canvas.tsx +++ b/packages/editor/src/editor/Canvas.tsx @@ -65,6 +65,23 @@ export function Canvas(props: CanvasProps) { const selectionAllLocked = selection.length > 0 && selection.every((n) => n.locked); const selectionAllHidden = selection.length > 0 && selection.every((n) => n.hidden); + const ctxMenuPos = useMemo(() => { + if (!ctx) return null; + + // Rough clamp so the menu stays inside the viewport. + // (We don't measure actual size to keep this simple + stable.) + const w = 220; + const h = 320; + + const vw = typeof window === 'undefined' ? 10_000 : window.innerWidth; + const vh = typeof window === 'undefined' ? 10_000 : window.innerHeight; + + return { + x: Math.max(8, Math.min(ctx.clientX, vw - w - 8)), + y: Math.max(8, Math.min(ctx.clientY, vh - h - 8)), + }; + }, [ctx]); + const clientToCanvas = useCallback((clientX: number, clientY: number) => { const el = ref.current; if (!el) return null; @@ -175,14 +192,21 @@ export function Canvas(props: CanvasProps) { const p = clientToWorld(e.clientX, e.clientY); if (!p) return; - if (targetId && !props.selectionIds.includes(targetId)) { - // goView-ish: right click selects the item. - // Ctrl/Cmd keeps multi-select parity (add to selection instead of replacing). - if ((e as React.MouseEvent).ctrlKey || (e as React.MouseEvent).metaKey) { - props.onToggleSelect(targetId); - } else { - props.onSelectSingle(targetId); + const additive = (e as React.MouseEvent).ctrlKey || (e as React.MouseEvent).metaKey; + + if (targetId) { + if (!props.selectionIds.includes(targetId)) { + // goView-ish: right click selects the item. + // Ctrl/Cmd keeps multi-select parity (add to selection instead of replacing). + if (additive) { + props.onToggleSelect(targetId); + } else { + props.onSelectSingle(targetId); + } } + } else { + // Editor parity: right-click on empty space clears selection (unless additive). + if (!additive) props.onSelectSingle(undefined); } setCtx({ clientX: e.clientX, clientY: e.clientY, worldX: p.x, worldY: p.y, targetId }); @@ -264,12 +288,12 @@ export function Canvas(props: CanvasProps) { cursor: props.keyboard.space ? 'grab' : 'default', }} > - {ctx && ( + {ctx && ctxMenuPos && (
(...values: Array): T | undefined { return undefined; } -function optionOf(c: GoViewComponentLike): unknown { +function optionOf(cIn: GoViewComponentLike): unknown { + const c = unwrapComponent(cIn); const chartData = c.chartConfig?.data as Record | undefined; return ( c.option ?? @@ -178,7 +196,9 @@ export function convertGoViewProjectToScreen(input: GoViewProjectLike | GoViewSt const nodes: Array = []; - for (const c of componentList) { + for (const raw of componentList) { + const c = unwrapComponent(raw); + const rect = c.attr ? { x: toNumber((c.attr as unknown as Record).x, 0), diff --git a/packages/sdk/src/core/widgets/urlLike.ts b/packages/sdk/src/core/widgets/urlLike.ts index db47cdf..b0bc538 100644 --- a/packages/sdk/src/core/widgets/urlLike.ts +++ b/packages/sdk/src/core/widgets/urlLike.ts @@ -30,7 +30,27 @@ function pickUrlLikeInner(input: unknown, depth: number): string { const obj = input as Record; // Common direct keys. - for (const key of ['value', 'url', 'src', 'href', 'link', 'path', 'iframeUrl', 'videoUrl', 'mp4']) { + // Keep this list generous; imports come from many low-code editors. + for (const key of [ + 'value', + 'url', + 'src', + 'href', + 'link', + 'path', + 'source', + 'address', + // iframe-ish + 'iframeUrl', + 'iframeSrc', + 'embedUrl', + // video-ish + 'videoUrl', + 'videoSrc', + 'mp4', + 'm3u8', + 'flv', + ]) { const v = obj[key]; if (typeof v === 'string' && v) return v; } @@ -38,7 +58,7 @@ function pickUrlLikeInner(input: unknown, depth: number): string { if (depth <= 0) return ''; // Common nesting keys. - for (const key of ['dataset', 'data', 'config', 'option', 'options', 'props']) { + for (const key of ['dataset', 'data', 'config', 'option', 'options', 'props', 'source', 'media']) { const v = obj[key]; const nested = pickUrlLikeInner(v, depth - 1); if (nested) return nested;