@@ -5,8 +5,8 @@ import { localize } from './localization';
55import centersSvg from './svg/centers.svg' ;
66import chevronDownSvg from './svg/chevron-down.svg' ;
77import chevronUpSvg from './svg/chevron-up.svg' ;
8- import circleSvg from './svg/circle.svg' ;
98import ringsSvg from './svg/rings.svg' ;
9+ import circleSvg from './svg/circle.svg' ;
1010import { Tooltips } from './tooltips' ;
1111
1212const createSvg = ( svgString : string ) => {
@@ -134,119 +134,9 @@ class ModeToggle extends Container {
134134
135135 this . dom . addEventListener ( 'pointerdown' , ( event ) => {
136136 event . stopPropagation ( ) ;
137+ dropdownMenu . hidden = ! dropdownMenu . hidden ;
137138 } ) ;
138139
139- // ---- Long-press / drag-to-select interaction ----
140- // Hold down on mode-toggle → after 200ms (or right-drag ≥20px) → open
141- // dropdown → drag to an item → release to select that mode.
142- let longPressTimer : ReturnType < typeof setTimeout > | null = null ;
143- let isLongPressDrag = false ;
144- let dragStartX = 0 ;
145- const SWIPE_THRESHOLD = 20 ;
146-
147- const modeActions : Array < { element : HTMLElement ; mode : string } > = [
148- { element : splatOption . dom , mode : 'splat' } ,
149- { element : centersOption . dom , mode : 'centers' } ,
150- { element : ringsOption . dom , mode : 'rings' }
151- ] ;
152-
153- const findModeItemAtPoint = ( x : number , y : number ) => {
154- const el = document . elementFromPoint ( x , y ) ;
155- if ( ! el ) return null ;
156- for ( const item of modeActions ) {
157- if ( item . element === el || item . element . contains ( el ) ) {
158- return item ;
159- }
160- }
161- return null ;
162- } ;
163-
164- const cleanupDrag = ( ) => {
165- isLongPressDrag = false ;
166- dropdownMenu . hidden = true ;
167- // eslint-disable-next-line no-use-before-define
168- document . removeEventListener ( 'mouseup' , onDocMouseUpCapture , true ) ;
169- // eslint-disable-next-line no-use-before-define
170- document . removeEventListener ( 'mousemove' , onDocMouseMove , true ) ;
171- for ( const item of modeActions ) {
172- item . element . classList . remove ( 'longpress-hover' ) ;
173- }
174- } ;
175-
176- const onDocMouseMove = ( e : MouseEvent ) => {
177- const hovered = findModeItemAtPoint ( e . clientX , e . clientY ) ;
178- for ( const item of modeActions ) {
179- item . element . classList . toggle ( 'longpress-hover' , item === hovered ) ;
180- }
181- } ;
182-
183- const onDocMouseUpCapture = ( e : MouseEvent ) => {
184- if ( e . button !== 0 ) return ;
185- const hit = findModeItemAtPoint ( e . clientX , e . clientY ) ;
186- if ( hit ) {
187- events . fire ( 'camera.setMode' , hit . mode ) ;
188- }
189- cleanupDrag ( ) ;
190- } ;
191-
192- const startDrag = ( ) => {
193- if ( longPressTimer ) {
194- clearTimeout ( longPressTimer ) ;
195- longPressTimer = null ;
196- }
197- isLongPressDrag = true ;
198- dropdownMenu . hidden = false ;
199- document . addEventListener ( 'mouseup' , onDocMouseUpCapture , true ) ;
200- document . addEventListener ( 'mousemove' , onDocMouseMove , true ) ;
201- } ;
202-
203- this . dom . addEventListener ( 'mousedown' , ( e ) => {
204- if ( e . button !== 0 ) return ;
205- isLongPressDrag = false ;
206- dragStartX = e . clientX ;
207- longPressTimer = setTimeout ( startDrag , 200 ) ;
208- } ) ;
209-
210- this . dom . addEventListener ( 'mousemove' , ( e ) => {
211- if ( isLongPressDrag || ! longPressTimer ) return ;
212- if ( e . clientX - dragStartX >= SWIPE_THRESHOLD ) {
213- startDrag ( ) ;
214- // highlight item under cursor after popup shown
215- const hovered = findModeItemAtPoint ( e . clientX , e . clientY ) ;
216- for ( const item of modeActions ) {
217- item . element . classList . toggle ( 'longpress-hover' , item === hovered ) ;
218- }
219- }
220- } ) ;
221-
222- this . dom . addEventListener ( 'mouseup' , ( e ) => {
223- if ( e . button !== 0 ) return ;
224- if ( isLongPressDrag ) return ;
225- if ( longPressTimer ) {
226- clearTimeout ( longPressTimer ) ;
227- longPressTimer = null ;
228- // Short click: toggle dropdown
229- dropdownMenu . hidden = ! dropdownMenu . hidden ;
230- }
231- } ) ;
232-
233- this . dom . addEventListener ( 'mouseleave' , ( ) => {
234- if ( longPressTimer ) {
235- clearTimeout ( longPressTimer ) ;
236- longPressTimer = null ;
237- }
238- } ) ;
239-
240- // Click outside closes dropdown (skip during drag)
241- document . addEventListener ( 'pointerdown' , ( e ) => {
242- if ( isLongPressDrag ) return ;
243- if ( ! this . dom . contains ( e . target as Node ) ) {
244- dropdownMenu . hidden = true ;
245- }
246- } ) ;
247-
248- // ---- Click handlers for dropdown items (for normal click users) ----
249-
250140 centersOption . dom . addEventListener ( 'pointerdown' , ( event ) => {
251141 event . stopPropagation ( ) ;
252142 events . fire ( 'camera.setMode' , 'centers' ) ;
@@ -265,6 +155,10 @@ class ModeToggle extends Container {
265155 dropdownMenu . hidden = true ;
266156 } ) ;
267157
158+ document . addEventListener ( 'pointerdown' , ( ) => {
159+ dropdownMenu . hidden = true ;
160+ } ) ;
161+
268162 events . on ( 'camera.mode' , ( mode : string ) => {
269163 this . class [ mode === 'centers' ? 'add' : 'remove' ] ( 'centers-mode' ) ;
270164 this . class [ mode === 'rings' ? 'add' : 'remove' ] ( 'rings-mode' ) ;
0 commit comments