@@ -2,6 +2,11 @@ import { Machine } from '../../machine'
22import { ActionTypes as StackActionTypes , stackMachines } from '../../machines/stack-machine'
33import type { EnsureArray } from '../../types'
44import { Focus , calculateActiveIndex } from '../../utils/calculate-active-index'
5+ import {
6+ ElementPositionState ,
7+ computeVisualPosition ,
8+ detectMovement ,
9+ } from '../../utils/element-movement'
510import { sortByDomNode } from '../../utils/focus-management'
611import { match } from '../../utils/match'
712
@@ -74,6 +79,9 @@ export interface State<T> {
7479 buttonElement : HTMLButtonElement | null
7580 optionsElement : HTMLElement | null
7681
82+ // Track input to determine if it moved
83+ inputPositionState : ElementPositionState
84+
7785 __demoMode : boolean
7886}
7987
@@ -96,6 +104,8 @@ export enum ActionTypes {
96104 SetInputElement ,
97105 SetButtonElement ,
98106 SetOptionsElement ,
107+
108+ MarkInputAsMoved ,
99109}
100110
101111function adjustOrderedState < T > (
@@ -160,13 +170,17 @@ type Actions<T> =
160170 | { type : ActionTypes . SetInputElement ; element : HTMLInputElement | null }
161171 | { type : ActionTypes . SetButtonElement ; element : HTMLButtonElement | null }
162172 | { type : ActionTypes . SetOptionsElement ; element : HTMLElement | null }
173+ | { type : ActionTypes . MarkInputAsMoved }
163174
164175let reducers : {
165176 [ P in ActionTypes ] : < T > ( state : State < T > , action : Extract < Actions < T > , { type : P } > ) => State < T >
166177} = {
167178 [ ActionTypes . CloseCombobox ] ( state ) {
168179 if ( state . dataRef . current ?. disabled ) return state
169180 if ( state . comboboxState === ComboboxState . Closed ) return state
181+ let inputPositionState = state . inputElement
182+ ? ElementPositionState . Tracked ( computeVisualPosition ( state . inputElement ) )
183+ : state . inputPositionState
170184
171185 return {
172186 ...state ,
@@ -181,6 +195,8 @@ let reducers: {
181195 // for example, not scrolling to the active option in a virtual list
182196 activationTrigger : ActivationTrigger . Other ,
183197
198+ inputPositionState,
199+
184200 __demoMode : false ,
185201 }
186202 } ,
@@ -197,11 +213,17 @@ let reducers: {
197213 activeOptionIndex : idx ,
198214 comboboxState : ComboboxState . Open ,
199215 __demoMode : false ,
216+ inputPositionState : ElementPositionState . Idle ,
200217 }
201218 }
202219 }
203220
204- return { ...state , comboboxState : ComboboxState . Open , __demoMode : false }
221+ return {
222+ ...state ,
223+ comboboxState : ComboboxState . Open ,
224+ inputPositionState : ElementPositionState . Idle ,
225+ __demoMode : false ,
226+ }
205227 } ,
206228 [ ActionTypes . SetTyping ] ( state , action ) {
207229 if ( state . isTyping === action . isTyping ) return state
@@ -404,6 +426,14 @@ let reducers: {
404426 if ( state . optionsElement === action . element ) return state
405427 return { ...state , optionsElement : action . element }
406428 } ,
429+ [ ActionTypes . MarkInputAsMoved ] ( state ) {
430+ if ( state . inputPositionState . kind !== 'Tracked' ) return state
431+
432+ return {
433+ ...state ,
434+ inputPositionState : ElementPositionState . Moved ,
435+ }
436+ } ,
407437}
408438
409439export class ComboboxMachine < T > extends Machine < State < T > , Actions < T > > {
@@ -438,6 +468,7 @@ export class ComboboxMachine<T> extends Machine<State<T>, Actions<T>> {
438468 buttonElement : null ,
439469 optionsElement : null ,
440470 __demoMode,
471+ inputPositionState : ElementPositionState . Idle ,
441472 } )
442473 }
443474
@@ -464,6 +495,20 @@ export class ComboboxMachine<T> extends Machine<State<T>, Actions<T>> {
464495 this . on ( ActionTypes . OpenCombobox , ( ) => stackMachine . actions . push ( id ) )
465496 this . on ( ActionTypes . CloseCombobox , ( ) => stackMachine . actions . pop ( id ) )
466497 }
498+
499+ // Track whether the input moved or not
500+ this . disposables . group ( ( d ) => {
501+ this . on ( ActionTypes . CloseCombobox , ( state ) => {
502+ if ( ! state . inputElement ) return
503+
504+ d . dispose ( )
505+ d . add (
506+ detectMovement ( state . inputElement , state . inputPositionState , ( ) => {
507+ this . send ( { type : ActionTypes . MarkInputAsMoved } )
508+ } )
509+ )
510+ } )
511+ } )
467512 }
468513
469514 actions = {
@@ -640,6 +685,10 @@ export class ComboboxMachine<T> extends Machine<State<T>, Actions<T>> {
640685
641686 return true
642687 } ,
688+
689+ didInputMove ( state : State < T > ) {
690+ return state . inputPositionState . kind === 'Moved'
691+ } ,
643692 }
644693
645694 reduce ( state : Readonly < State < T > > , action : Actions < T > ) : State < T > {
0 commit comments