feat(data): an own-rule selection owner keeps its whole old click handler

Vessels' click rule (sibling ownership, cyclone background, card hit
test, unkeyed records) does not map onto the plain or look-under rules:
an owner with onClick(ctx) gets every click with the top pick, no gesture
rule (its old LEFT_CLICK handler), and is another layer to the rest.
This commit is contained in:
German Khokhlov committed 2026-10-17 12:47:14 +04:00
1 parent 6eec8f6cb0
commit 6b27b21cc4
2 files changed
+61 -2

No files matched your search

+17 -2
View File
@@ -20,6 +20,10 @@
// nothing, is empty space for it (it releases its selection); a click that // nothing, is empty space for it (it releases its selection); a click that
// only meets what it looks under does nothing; no gesture rule (its old // only meets what it looks under does nothing; no gesture rule (its old
// handler took every click). To the others it is another layer. // handler took every click). To the others it is another layer.
// An own-rule owner (`onClick(ctx)`: vessels) keeps its whole old click
// handler: it gets every click with the top pick and decides itself (no
// gesture rule, as its old LEFT_CLICK handler); to the others it is another
// layer.
import { isPointerFree as pointerFree } from './inputOwnership.js'; import { isPointerFree as pointerFree } from './inputOwnership.js';
import { import {
@@ -104,7 +108,9 @@ export function createSelectionDispatcher(
} }
return lookedUnder ? 'under' : null; return lookedUnder ? 'under' : null;
}; };
const plain = (owner) => typeof owner.lookUnder !== 'function'; const ownRule = (owner) => typeof owner.onClick === 'function';
const plain = (owner) =>
typeof owner.lookUnder !== 'function' && !ownRule(owner);
const onClick = (event) => { const onClick = (event) => {
if (press) finish(event.position); if (press) finish(event.position);
@@ -119,6 +125,15 @@ export function createSelectionDispatcher(
}; };
for (const [name, owner] of [...owners]) { for (const [name, owner] of [...owners]) {
if (plain(owner)) continue; if (plain(owner)) continue;
if (ownRule(owner)) {
// Its old handler ran alone: a throw there leaves the others be.
try {
owner.onClick(ctx);
} catch (error) {
console.error(error);
}
continue;
}
const target = lookUnderTarget(name, owner, event); const target = lookUnderTarget(name, owner, event);
if (target === 'under') continue; if (target === 'under') continue;
if (target && target !== 'other') if (target && target !== 'other')
@@ -205,7 +220,7 @@ export function createSelectionDispatcher(
* @param {string} name the owning layer's id * @param {string} name the owning layer's id
* @param {{ owns: (id: string) => boolean, onSelect?: Function, onEmptyClick?: Function, * @param {{ owns: (id: string) => boolean, onSelect?: Function, onEmptyClick?: Function,
* onEscape?: Function, passThrough?: (picked: object) => boolean, drillThrough?: boolean, * onEscape?: Function, passThrough?: (picked: object) => boolean, drillThrough?: boolean,
* lookUnder?: (id: string) => boolean }} owner * lookUnder?: (id: string) => boolean, onClick?: (ctx: object) => void }} owner
* @returns {() => void} unregister * @returns {() => void} unregister
*/ */
register(name, owner) { register(name, owner) {
+44
View File
@@ -256,3 +256,47 @@ test('a look-under owner (local ADS-B) keeps its own click rule beside the other
unregisterPickOwner('cctv'); unregisterPickOwner('cctv');
} }
}); });
test('an own-rule owner (vessels) gets every click with its top pick and is another layer to the rest', () => {
const { dispatcher, click, escape } = setup({
10: [{ id: { mmsi: '111' } }], // its own object (Cesium: the record)
20: [{ id: 'ae01ce' }], // flights' contact
30: [], // empty map
});
const seen = [];
const flights = recorder();
let owned = true;
dispatcher.register('ais-live-vessels', {
owns: (id) => owned && id === '111',
onClick: (ctx) =>
seen.push([ctx.position.x, ctx.picked?.id ?? null, ctx.gesture.travelPx]),
});
dispatcher.register('flights', {
owns: (id) => id === 'ae01ce',
...flights.handlers,
});
click(10);
click(20);
click(30);
// No gesture rule: a long press and a drag still reach it (LEFT_CLICK).
click(30, { ms: 900 });
click(30, { travel: 40 });
escape();
assert.deepEqual(seen, [
[10, { mmsi: '111' }, 0],
[20, 'ae01ce', 0],
[30, null, 0],
[30, null, 0],
[70, null, 40],
]);
// To flights a vessel is another layer's object: not its empty space.
assert.deepEqual(flights.calls, [
['select', 'ae01ce', undefined],
['empty', 0],
['escape'],
]);
// A disowned vessel pick (the layer disabled) is empty space again.
owned = false;
click(10);
assert.deepEqual(flights.calls.at(-1), ['empty', 0]);
});