refactor: improve context menu selection sync and goview embed detection
This commit is contained in:
parent
b1ca80d5fe
commit
947946edc6
@ -1,6 +1,12 @@
|
||||
import { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
||||
import { Typography } from 'antd';
|
||||
|
||||
function selectionKeyOf(ids: string[]): string {
|
||||
// Keep stable regardless of selection order.
|
||||
// This avoids false mismatches between context-menu state and reducer state.
|
||||
return [...ids].sort().join('|');
|
||||
}
|
||||
|
||||
export type ContextMenuState =
|
||||
| {
|
||||
kind: 'canvas';
|
||||
@ -101,11 +107,18 @@ export function ContextMenu(props: {
|
||||
|
||||
if (!ctx || !position) return null;
|
||||
|
||||
const hasSelection = props.selectionIds.length > 0;
|
||||
const canModifySelection = hasSelection && props.selectionHasUnlocked;
|
||||
const currentSelectionKey = selectionKeyOf(props.selectionIds);
|
||||
const selectionInSync = currentSelectionKey === ctx.selectionKey;
|
||||
|
||||
// Use captured selection while waiting for store selection to catch up.
|
||||
// This prevents transient "disabled" menu items right after right-click selecting.
|
||||
const selectionIds = selectionInSync ? props.selectionIds : ctx.selectionIds;
|
||||
|
||||
const hasSelection = selectionIds.length > 0;
|
||||
const canModifySelection = selectionInSync && hasSelection && props.selectionHasUnlocked;
|
||||
const hasTarget = ctx.kind === 'node';
|
||||
const targetId = hasTarget ? ctx.targetId : undefined;
|
||||
const targetInSelection = !!targetId && props.selectionIds.includes(targetId);
|
||||
const targetInSelection = !!targetId && selectionIds.includes(targetId);
|
||||
const canSelectSingle = !!props.onSelectSingle;
|
||||
|
||||
return (
|
||||
@ -136,7 +149,7 @@ export function ContextMenu(props: {
|
||||
}}
|
||||
/>
|
||||
|
||||
{canSelectSingle && hasTarget && targetInSelection && props.selectionIds.length > 1 ? (
|
||||
{canSelectSingle && hasTarget && targetInSelection && selectionIds.length > 1 ? (
|
||||
<MenuItem
|
||||
label="Select Only"
|
||||
onClick={() => {
|
||||
@ -185,7 +198,7 @@ export function ContextMenu(props: {
|
||||
? 'Toggle Lock'
|
||||
: 'Lock'
|
||||
}
|
||||
disabled={!hasSelection || !props.onToggleLockSelected}
|
||||
disabled={!selectionInSync || !hasSelection || !props.onToggleLockSelected}
|
||||
onClick={() => {
|
||||
props.onToggleLockSelected?.();
|
||||
onClose();
|
||||
@ -199,7 +212,7 @@ export function ContextMenu(props: {
|
||||
? 'Toggle Visibility'
|
||||
: 'Hide'
|
||||
}
|
||||
disabled={!hasSelection || !props.onToggleHideSelected}
|
||||
disabled={!selectionInSync || !hasSelection || !props.onToggleHideSelected}
|
||||
onClick={() => {
|
||||
props.onToggleHideSelected?.();
|
||||
onClose();
|
||||
@ -210,7 +223,7 @@ export function ContextMenu(props: {
|
||||
|
||||
<MenuItem
|
||||
label="Bring To Front"
|
||||
disabled={!hasSelection || !props.onBringToFrontSelected}
|
||||
disabled={!selectionInSync || !hasSelection || !props.onBringToFrontSelected}
|
||||
onClick={() => {
|
||||
props.onBringToFrontSelected?.();
|
||||
onClose();
|
||||
@ -218,7 +231,7 @@ export function ContextMenu(props: {
|
||||
/>
|
||||
<MenuItem
|
||||
label="Send To Back"
|
||||
disabled={!hasSelection || !props.onSendToBackSelected}
|
||||
disabled={!selectionInSync || !hasSelection || !props.onSendToBackSelected}
|
||||
onClick={() => {
|
||||
props.onSendToBackSelected?.();
|
||||
onClose();
|
||||
@ -235,7 +248,13 @@ export function ContextMenu(props: {
|
||||
}}
|
||||
/>
|
||||
|
||||
{!selectionInSync ? (
|
||||
<Typography.Text style={{ display: 'block', marginTop: 6, color: 'rgba(255,255,255,0.45)', fontSize: 11 }}>
|
||||
(updating selection…)
|
||||
</Typography.Text>
|
||||
) : null}
|
||||
|
||||
<Typography.Text style={{ display: 'block', marginTop: 2, color: 'rgba(255,255,255,0.45)', fontSize: 11 }}>
|
||||
({Math.round(ctx.worldX)}, {Math.round(ctx.worldY)})
|
||||
</Typography.Text>
|
||||
</div>
|
||||
|
||||
@ -678,7 +678,7 @@ export function convertGoViewProjectToScreen(input: GoViewProjectLike | GoViewSt
|
||||
const urlLike = pickUrlLike(option);
|
||||
const urlLooksLikeEmbedPage =
|
||||
!!urlLike &&
|
||||
/(youtube\.com|youtu\.be|vimeo\.com|twitch\.tv|tiktok\.com|douyin\.com|kuaishou\.com|ixigua\.com|huya\.com|douyu\.com|bilibili\.com|live\.bilibili\.com|player\.bilibili\.com|youku\.com|iqiyi\.com|mgtv\.com|tencent|qq\.com|music\.163\.com)/i.test(
|
||||
/(youtube\.com|youtu\.be|vimeo\.com|twitch\.tv|tiktok\.com|douyin\.com|kuaishou\.com|ixigua\.com|huya\.com|douyu\.com|bilibili\.com|live\.bilibili\.com|player\.bilibili\.com|youku\.com|iqiyi\.com|mgtv\.com|tencent|qq\.com|music\.163\.com|spotify\.com|soundcloud\.com|figma\.com|google\.com\/maps|amap\.com|gaode\.com|map\.baidu\.com)/i.test(
|
||||
urlLike,
|
||||
) &&
|
||||
// Keep actual media URLs as video.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user