Skip to content

fix(core): disable doubleClickZoom by default to eliminate 300ms click delay#10422

Draft
chrisgervang wants to merge 6 commits into
x/mjolnir-upfrom
chr/deckgl-controller-gesture-delays
Draft

fix(core): disable doubleClickZoom by default to eliminate 300ms click delay#10422
chrisgervang wants to merge 6 commits into
x/mjolnir-upfrom
chr/deckgl-controller-gesture-delays

Conversation

@chrisgervang

@chrisgervang chrisgervang commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Disables doubleClickZoom by default (false instead of true) to eliminate the 300ms click delay caused by mjolnir.js's TapRecognizer requireFailure mechanism
  • Disables doubleClickDragZoom by default for the same reason — once feat(core): upgrade mjolnir.js for DoubleClickDrag recognizer #10416 lands, dblclickdrag should be in click's requireFailure list to prevent spurious click events before a drag-zoom begins, which would reintroduce the 300ms delay
  • Updates docs to document the latency tradeoff when enabling either option

Background

When doubleClickZoom is enabled, the click recognizer has a requireFailure: ['dblclick'] dependency. This causes mjolnir.js's TapRecognizer to defer click emission by 300ms (the interval timeout) while waiting to see if a double-click follows. Disabling the dblclick recognizer causes hasRequireFailures() to return false (it only counts enabled recognizers), allowing clicks to fire immediately.

The same principle applies to doubleClickDragZoom — a single click is ambiguous with the first tap of a double-click-drag, so the dblclickdrag recognizer should also be in click's requireFailure list (to be added with #10416). That means enabling it would also reintroduce the 300ms delay.

See discussion in #10388 and the revert in #10420.

Relates to #10384

Test plan

  • All existing controller tests pass (36/36)
  • Verify clicks fire immediately without delay in a browser
  • Verify double-click zoom works when explicitly enabled via controller: { doubleClickZoom: true }
  • Verify double-click-drag zoom works when explicitly enabled via controller: { doubleClickDragZoom: true }

🤖 Generated with Claude Code


Note

Medium Risk
Changes default controller behavior for all consumers (double-click zoom off unless opted in); interaction timing and gesture disambiguation are user-facing and easy to miss in manual testing.

Overview
Default map interaction behavior changes: doubleClickZoom and doubleClickDragZoom now default to false instead of true, so single clicks on the deck canvas fire immediately instead of waiting ~300ms for the tap recognizer to rule out double-click gestures.

The controller defaults and API docs now call out that turning either option back on reintroduces that click latency. Recognizer wiring in RECOGNIZERS is adjusted so click coordinates with dblclick / dblclickdrag via requireFailure when those features are enabled.

Tests add EventManager-level coverage: immediate clicks with both zoom options off, and ~300ms delay when either is on.

Reviewed by Cursor Bugbot for commit b2b9627. Bugbot is set up for automated code reviews on this repo. Configure here.

@coveralls

coveralls commented Jul 4, 2026

Copy link
Copy Markdown

Coverage Status

Coverage is 83.139%chr/deckgl-controller-gesture-delays into x/mjolnir-up. No base build found for x/mjolnir-up.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit f567030. Configure here.

Comment thread modules/core/src/controllers/controller.ts
@chrisgervang chrisgervang changed the base branch from master to x/mjolnir-up July 4, 2026 00:33
chrisgervang and others added 3 commits July 3, 2026 17:35
…k delay

The click recognizer's requireFailure dependency on dblclick causes
mjolnir.js to defer click events by 300ms. Disabling doubleClickZoom
by default removes this delay while keeping doubleClickDragZoom
available for zoom-by-drag gestures.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Same principle — once #10416 lands, dblclickdrag should be in click's
requireFailure list to prevent spurious clicks before a drag-zoom.
That would reintroduce the 300ms delay, so disable by default too.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Prevents spurious click events from firing before a double-click-drag
zoom gesture is recognized. Without this, a click would emit on the
first tap before the recognizer determines the user intends to drag.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@chrisgervang chrisgervang force-pushed the chr/deckgl-controller-gesture-delays branch from f567030 to e250fc9 Compare July 4, 2026 00:37
Tests verify through the full EventManager → recognizer pipeline that:
- Click fires immediately when doubleClickZoom and doubleClickDragZoom
  are both disabled (the new default)
- Click is delayed ~300ms when doubleClickZoom is enabled
- Click is delayed ~300ms when doubleClickDragZoom is enabled

Also reorders RECOGNIZERS so dblclickdrag is registered before click,
which is required for click's requireFailure reference to resolve.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@chrisgervang chrisgervang marked this pull request as draft July 4, 2026 01:04
…m is false

The dblclick/dblclickdrag recognizers start with enable:false so they don't
cause the 300ms click delay out of the box. Deck's picking system uses watch()
(passive) for the dblclick event so it doesn't force-enable the recognizer.
Only the controller's doubleClickZoom option enables it.

Also adds integration tests that verify click latency at the Deck level using
real pointer events dispatched to the canvas.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Comment on lines +1090 to +1093
if (eventType === 'dblclick') {
// Use watch (passive) so the dblclick recognizer is only enabled by the
// controller's doubleClickZoom option — not by the picking system.
eventManager.watch(eventType, this._onEvent);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Pessimistress while trying to unit test this, the LLM gathered some interesting context and proposed this change

The watch change at deck.ts:1090 is necessary to make doubleClickZoom: false actually eliminate the click delay.

The problem: Deck registers handlers for both click and dblclick events (for the onClick prop — it fires on both single and double clicks for picking). Using eventManager.on('dblclick', ...) enables the dblclick recognizer. The click recognizer has dblclick in its requireFailure list, so when dblclick is enabled, hasRequireFailures() returns true and the TapRecognizer adds a 300ms setTimeout before emitting click.

Even with controller: {doubleClickZoom: false}, the controller calls eventManager.off('dblclick', handler) — but the Deck-level on('dblclick', ...) handler still exists, keeping the recognizer enabled and the delay in place.

The fix: watch() registers a passive handler that still receives dblclick events (so onClick still fires on double-clicks when doubleClickZoom: true), but passive handlers don't count toward isEmpty() — so they don't force-enable the recognizer. Only the controller's on/off (driven by doubleClickZoom) controls whether the dblclick recognizer is active.

Without this change, the enable: false default on the dblclick recognizer gets immediately overridden by Deck's on('dblclick', ...) call, and the 300ms click delay persists regardless of the controller setting.

I'm still investigating and learning about this area of deck..

Comment thread test/interaction/map-controller.spec.ts Outdated
The dblclick tests were broken because page.mouse.dblclick() doesn't
properly target the canvas inside vitest's browser iframe. Fixed by
dispatching PointerEvents directly on the canvas element, matching how
mjolnir's PointerEventInput listener works (pointerdown on element,
pointerup on window). Each test uses a fresh Deck instance for isolation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants