feat(front-component-renderer): forward offset/movement coordinates on serialised events (#20046)

## Summary

Adds `offsetX`, `offsetY`, `movementX`, `movementY` to
`SerializedEventData` and the host event serialiser so apps can reason
about element-relative pointer positions without trying to read the host
element's bounding rect (which is impossible from a remote-DOM worker).

## Motivation

I was building a front-component with click-to-drop-pin and trackpad
pan/zoom (custom OSM tile renderer). Two real bugs surfaced from the
current event-serialisation surface:

1. **Wheel pan/zoom was broken.** The host already forwards
`deltaX`/`deltaY`, but app authors naturally read them off the
React-style event handler argument as `e.deltaX`/`e.deltaY`. Because
remote-DOM bridges everything via `RemoteEvent extends
CustomEvent<Detail>`, the payload actually arrives at `e.detail.deltaX`.
Reading the wrong place gives `undefined`, and `undefined < 0 ===
false`, so every wheel notch zoomed in the same direction. App code now
uses `e.detail`, but this was a sharp papercut worth flagging in docs /
a helper (separate change).

2. **Element-local click coords are unobtainable from a worker.** With
only `clientX/Y`, an app needs the stage's bounding rect to translate
viewport coordinates to local — which can't be read across the worker
boundary. `offsetX`/`offsetY` close that gap with a one-read solution.
`movementX`/`movementY` round out the set for any future drag-style
interactions if `mousemove` later joins the allow-list.
This commit is contained in:
Charles Bochet
2026-04-25 12:12:28 +02:00
committed by GitHub
parent 570038ad65
commit 41571ea377
2 changed files with 10 additions and 0 deletions
@@ -10,6 +10,10 @@ export type SerializedEventData = {
pageY?: number;
screenX?: number;
screenY?: number;
offsetX?: number;
offsetY?: number;
movementX?: number;
movementY?: number;
button?: number;
buttons?: number;
key?: string;
@@ -81,6 +81,12 @@ const serializeEvent = (event: unknown): SerializedEventData => {
if ('pageY' in domEvent) serialized.pageY = domEvent.pageY as number;
if ('screenX' in domEvent) serialized.screenX = domEvent.screenX as number;
if ('screenY' in domEvent) serialized.screenY = domEvent.screenY as number;
if ('offsetX' in domEvent) serialized.offsetX = domEvent.offsetX as number;
if ('offsetY' in domEvent) serialized.offsetY = domEvent.offsetY as number;
if ('movementX' in domEvent)
serialized.movementX = domEvent.movementX as number;
if ('movementY' in domEvent)
serialized.movementY = domEvent.movementY as number;
if ('button' in domEvent) serialized.button = domEvent.button as number;
if ('buttons' in domEvent) serialized.buttons = domEvent.buttons as number;