Compare commits
No commits in common. "5ff0c2272417e5363ad1af852de973a4c5ced1b2" and "19279174e679ff97443b1887b7bf636eaf092140" have entirely different histories.
5ff0c22724
...
19279174e6
@ -5,7 +5,6 @@ import { Button, Space } from 'antd';
|
|||||||
import type { ResizeHandle } from './types';
|
import type { ResizeHandle } from './types';
|
||||||
import { rectFromPoints } from './geometry';
|
import { rectFromPoints } from './geometry';
|
||||||
import type { ContextMenuState } from './ContextMenu';
|
import type { ContextMenuState } from './ContextMenu';
|
||||||
import { selectionKeyOf } from './selection';
|
|
||||||
|
|
||||||
export interface CanvasProps {
|
export interface CanvasProps {
|
||||||
screen: Screen;
|
screen: Screen;
|
||||||
@ -41,6 +40,12 @@ function isPrimaryButton(e: React.PointerEvent | PointerEvent): boolean {
|
|||||||
return (e as PointerEvent).buttons === 1 || (e as PointerEvent).button === 0;
|
return (e as PointerEvent).buttons === 1 || (e as PointerEvent).button === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 function Canvas(props: CanvasProps) {
|
export function Canvas(props: CanvasProps) {
|
||||||
const ref = useRef<HTMLDivElement | null>(null);
|
const ref = useRef<HTMLDivElement | null>(null);
|
||||||
const [box, setBox] = useState<{ x1: number; y1: number; x2: number; y2: number } | null>(null);
|
const [box, setBox] = useState<{ x1: number; y1: number; x2: number; y2: number } | null>(null);
|
||||||
@ -436,18 +441,18 @@ function NodeView(props: {
|
|||||||
boxSizing: 'border-box',
|
boxSizing: 'border-box',
|
||||||
padding: `${node.props.paddingY ?? 0}px ${node.props.paddingX ?? 0}px`,
|
padding: `${node.props.paddingY ?? 0}px ${node.props.paddingX ?? 0}px`,
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
style={{
|
|
||||||
whiteSpace: 'pre-wrap',
|
|
||||||
fontSize: node.props.fontSize ?? 24,
|
|
||||||
color: node.props.color ?? '#fff',
|
|
||||||
fontWeight: node.props.fontWeight ?? 400,
|
|
||||||
letterSpacing: `${node.props.letterSpacing ?? 0}px`,
|
|
||||||
writingMode: writingModeStyle,
|
|
||||||
cursor: node.props.link ? 'pointer' : 'default',
|
|
||||||
}}
|
}}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
whiteSpace: 'pre-wrap',
|
||||||
|
fontSize: node.props.fontSize ?? 24,
|
||||||
|
color: node.props.color ?? '#fff',
|
||||||
|
fontWeight: node.props.fontWeight ?? 400,
|
||||||
|
letterSpacing: `${node.props.letterSpacing ?? 0}px`,
|
||||||
|
writingMode: writingModeStyle,
|
||||||
|
cursor: node.props.link ? 'pointer' : 'default',
|
||||||
|
}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (!node.props.link) return;
|
if (!node.props.link) return;
|
||||||
const head = node.props.linkHead ?? 'http://';
|
const head = node.props.linkHead ?? 'http://';
|
||||||
|
|||||||
@ -1,7 +1,11 @@
|
|||||||
import { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
import { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { Typography } from 'antd';
|
import { Typography } from 'antd';
|
||||||
|
|
||||||
import { selectionKeyOf } from './selection';
|
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 =
|
export type ContextMenuState =
|
||||||
| {
|
| {
|
||||||
|
|||||||
@ -14,7 +14,6 @@ import { bindEditorHotkeys } from './hotkeys';
|
|||||||
import { Canvas } from './Canvas';
|
import { Canvas } from './Canvas';
|
||||||
import { ContextMenu, type ContextMenuState } from './ContextMenu';
|
import { ContextMenu, type ContextMenuState } from './ContextMenu';
|
||||||
import { Inspector } from './Inspector';
|
import { Inspector } from './Inspector';
|
||||||
import { selectionKeyOf } from './selection';
|
|
||||||
import { createInitialState, editorReducer, exportScreenJSON } from './store';
|
import { createInitialState, editorReducer, exportScreenJSON } from './store';
|
||||||
|
|
||||||
const { Header, Sider, Content } = Layout;
|
const { Header, Sider, Content } = Layout;
|
||||||
@ -87,7 +86,10 @@ export function EditorApp() {
|
|||||||
|
|
||||||
const closeContextMenu = useCallback(() => setCtxMenu(null), []);
|
const closeContextMenu = useCallback(() => setCtxMenu(null), []);
|
||||||
|
|
||||||
// selectionKeyOf imported from ./selection
|
const selectionKeyOf = useCallback((ids: string[]) => {
|
||||||
|
// Keep stable regardless of ordering.
|
||||||
|
return [...ids].sort().join('|');
|
||||||
|
}, []);
|
||||||
|
|
||||||
const ctxMenuSyncedRef = useRef(false);
|
const ctxMenuSyncedRef = useRef(false);
|
||||||
|
|
||||||
@ -101,7 +103,7 @@ export function EditorApp() {
|
|||||||
}
|
}
|
||||||
dispatch(action);
|
dispatch(action);
|
||||||
},
|
},
|
||||||
[ctxMenu, dispatch, state.selection.ids],
|
[ctxMenu, dispatch, selectionKeyOf, state.selection.ids],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@ -1,5 +0,0 @@
|
|||||||
export 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('|');
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue
Block a user