refactor: improve goView import and context menu selection parity
This commit is contained in:
parent
62b1d60ff2
commit
9d95f34bd9
@ -141,6 +141,15 @@ export function Canvas(props: CanvasProps) {
|
||||
const additive =
|
||||
(e as React.MouseEvent).ctrlKey || (e as React.MouseEvent).metaKey || (e as React.MouseEvent).shiftKey;
|
||||
|
||||
const nextSelectionIds = targetId
|
||||
? props.selectionIds.includes(targetId)
|
||||
? props.selectionIds
|
||||
: additive
|
||||
? [...props.selectionIds, targetId]
|
||||
: [targetId]
|
||||
: props.selectionIds;
|
||||
const selectionKey = nextSelectionIds.join('|');
|
||||
|
||||
if (targetId) {
|
||||
if (!props.selectionIds.includes(targetId)) {
|
||||
// goView-ish: right click selects the item.
|
||||
@ -158,8 +167,25 @@ export function Canvas(props: CanvasProps) {
|
||||
|
||||
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 },
|
||||
? {
|
||||
kind: 'node',
|
||||
clientX: e.clientX,
|
||||
clientY: e.clientY,
|
||||
worldX: p.x,
|
||||
worldY: p.y,
|
||||
targetId,
|
||||
selectionKey,
|
||||
selectionIds: nextSelectionIds,
|
||||
}
|
||||
: {
|
||||
kind: 'canvas',
|
||||
clientX: e.clientX,
|
||||
clientY: e.clientY,
|
||||
worldX: p.x,
|
||||
worldY: p.y,
|
||||
selectionKey,
|
||||
selectionIds: nextSelectionIds,
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -8,6 +8,8 @@ export type ContextMenuState =
|
||||
clientY: number;
|
||||
worldX: number;
|
||||
worldY: number;
|
||||
selectionKey: string;
|
||||
selectionIds: string[];
|
||||
}
|
||||
| {
|
||||
kind: 'node';
|
||||
@ -16,6 +18,8 @@ export type ContextMenuState =
|
||||
worldX: number;
|
||||
worldY: number;
|
||||
targetId: string;
|
||||
selectionKey: string;
|
||||
selectionIds: string[];
|
||||
};
|
||||
|
||||
export function ContextMenu(props: {
|
||||
|
||||
@ -80,7 +80,10 @@ export function EditorApp() {
|
||||
// Selection parity: if selection changes via hotkeys/toolbar, close any open context menu.
|
||||
useEffect(() => {
|
||||
if (!ctxMenu) return;
|
||||
setCtxMenu(null);
|
||||
const currentKey = state.selection.ids.join('|');
|
||||
if (currentKey !== ctxMenu.selectionKey) {
|
||||
setCtxMenu(null);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [state.selection.ids]);
|
||||
|
||||
@ -468,6 +471,12 @@ export function EditorApp() {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const additive = e.ctrlKey || e.metaKey || e.shiftKey;
|
||||
const nextSelectionIds = state.selection.ids.includes(node.id)
|
||||
? state.selection.ids
|
||||
: additive
|
||||
? [...state.selection.ids, node.id]
|
||||
: [node.id];
|
||||
const selectionKey = nextSelectionIds.join('|');
|
||||
if (!state.selection.ids.includes(node.id)) {
|
||||
if (additive) {
|
||||
dispatch({ type: 'toggleSelect', id: node.id });
|
||||
@ -482,6 +491,8 @@ export function EditorApp() {
|
||||
worldX: node.rect.x + node.rect.w / 2,
|
||||
worldY: node.rect.y + node.rect.h / 2,
|
||||
targetId: node.id,
|
||||
selectionKey,
|
||||
selectionIds: nextSelectionIds,
|
||||
});
|
||||
}}
|
||||
style={{
|
||||
|
||||
@ -175,11 +175,12 @@ function isVideo(c: GoViewComponentLike): boolean {
|
||||
|
||||
// Chinese low-code widget names.
|
||||
if (
|
||||
k.includes('视频') ||
|
||||
k.includes('监控') ||
|
||||
k.includes('直播') ||
|
||||
k.includes('摄像') ||
|
||||
k.includes('摄像头')
|
||||
k.includes('\u89c6\u9891') ||
|
||||
k.includes('\u76d1\u63a7') ||
|
||||
k.includes('\u76f4\u64ad') ||
|
||||
k.includes('\u6444\u50cf') ||
|
||||
k.includes('\u6444\u50cf\u5934') ||
|
||||
k.includes('\u56de\u653e')
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
@ -206,7 +207,10 @@ function isVideo(c: GoViewComponentLike): boolean {
|
||||
k.includes('live') ||
|
||||
k.includes('camera') ||
|
||||
k.includes('cctv') ||
|
||||
k.includes('monitor')
|
||||
k.includes('monitor') ||
|
||||
k.includes('playback') ||
|
||||
k.includes('streaming') ||
|
||||
k.includes('videostream')
|
||||
);
|
||||
}
|
||||
|
||||
@ -276,7 +280,11 @@ function looksLikeIframeOption(option: unknown): boolean {
|
||||
|
||||
if (typeof option === 'string') {
|
||||
const trimmed = option.trim();
|
||||
if (trimmed.startsWith('<') && trimmed.includes('>')) return true;
|
||||
if (trimmed.startsWith('<') && trimmed.includes('>')) {
|
||||
// If the HTML is a video tag, let the video detector handle it.
|
||||
if (/\bvideo\b/i.test(trimmed) || /\bsource\b/i.test(trimmed)) return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Prefer explicit iframe-ish keys when option is an object (including nested shapes).
|
||||
@ -331,6 +339,13 @@ function looksLikeVideoOption(option: unknown): boolean {
|
||||
// Avoid false positives: some TextCommon widgets carry link-like fields.
|
||||
if (looksLikeTextOption(option)) return false;
|
||||
|
||||
if (typeof option === 'string') {
|
||||
const trimmed = option.trim();
|
||||
if (trimmed.startsWith('<') && trimmed.includes('>')) {
|
||||
return /\bvideo\b/i.test(trimmed) || /\bsource\b/i.test(trimmed);
|
||||
}
|
||||
}
|
||||
|
||||
// Prefer explicit video-ish keys when option is an object (including nested shapes).
|
||||
if (typeof option === 'object') {
|
||||
if (
|
||||
|
||||
Loading…
Reference in New Issue
Block a user