feat(editor): a tap opens a device's menu in the simulation; undo and redo drawn

In the simulation a tap on a device chooses it and opens its menu where
the finger was; the three-dot button is gone there, the long press stays.
The context menu moves in from the surface's edge instead of being cut off.
Undo and redo show drawn arrows: the characters took whatever font the
device had and looked squashed on an iPad (the buttons measured 44 x 44 at
every width; the size was not the cause).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-11 08:04:57 +00:00
co-authored by Claude Opus 5.5
parent ae10ee6c31
commit 15498a2bed
8 changed files with 108 additions and 42 deletions
+12 -9
View File
@@ -9,6 +9,9 @@
// tool a drag from one device to another plugs a cable, and so do two
// taps, one on each (Java's way).
// - On a cable: a tap selects it, a long press opens its menu.
// - In the simulation a tap on a device chooses it and opens its menu at
// once (Java's click opens a desktop or a table; its menu holds both), and
// nothing moves; there is no three-dot button.
// - In the documentation mode the documentation items lie over the network
// and take the pointer (Java's documentation panel moves to the top): a
// tap gives one the focus, a drag on a rectangle (on a text: on the band
@@ -84,10 +87,10 @@ export interface CanvasProps {
/** A cable drawn active, as Java draws the cable of the card whose tab is open (Kabel.setAktiv). */
readonly highlightedLink?: string | null;
/**
* The simulation: a tap on a device only chooses it, so that its
* three-dot button shows as in the design (Java's click opens a desktop or
* a table; here the menu does); a long press opens its menu; nothing
* moves, as in Java's simulation mode. The documentation mode: see above.
* The simulation: a tap on a device chooses it and opens its menu (Java's
* click opens a desktop or a table; here the menu holds them), as a long
* press does; nothing moves, as in Java's simulation mode. The
* documentation mode: see above.
*/
readonly mode?: 'design' | 'simulation' | 'documentation';
/**
@@ -104,8 +107,6 @@ export interface CanvasProps {
readonly renderDocText?: (item: DocumentationView, frame: ScreenFrame) => React.ReactNode;
/** The cables that carry a frame now, drawn as Java draws them then (JCablePanel.farbeBlinken). */
readonly activeLinks?: ReadonlySet<string>;
/** In the simulation: whether the chosen device has a menu at all (a switch has none) — only then its three-dot button. */
readonly hasMenu?: (target: Selection) => boolean;
}
const NO_LINKS: ReadonlySet<string> = new Set();
@@ -287,7 +288,6 @@ export const Canvas = forwardRef<CanvasHandle, CanvasProps>(function Canvas(
locked = false,
chosenBy = NO_CHOICES,
activeLinks = NO_LINKS,
hasMenu,
onDocFrame,
renderDocText,
},
@@ -598,7 +598,10 @@ export const Canvas = forwardRef<CanvasHandle, CanvasProps>(function Canvas(
}
if (now.longPressed) return;
if (simulating) {
if (!now.moved) onSelect?.({ kind: 'device', id: now.id });
if (now.moved) return;
const target = { kind: 'device', id: now.id } as const;
onSelect?.(target);
onMenu?.(target, now.start);
return;
}
if (locked) {
@@ -793,7 +796,7 @@ export const Canvas = forwardRef<CanvasHandle, CanvasProps>(function Canvas(
)}
</div>
)}
{menuButton === null || selection === null || (simulating ? hasMenu?.(selection) !== true : locked) ? null : (
{menuButton === null || selection === null || simulating || locked ? null : (
<button
type="button"
className="device-menu-button"