@@ -25,6 +25,7 @@ import { useWindowEvent } from '../../hooks/use-window-event'
25
25
import { useOpenClosed , State , useOpenClosedProvider } from '../../internal/open-closed'
26
26
import { match } from '../../utils/match'
27
27
import { useResolveButtonType } from '../../hooks/use-resolve-button-type'
28
+ import { useTreeWalker } from '../../hooks/use-tree-walker'
28
29
29
30
enum ComboboxStates {
30
31
Open ,
@@ -34,7 +35,7 @@ enum ComboboxStates {
34
35
type ComboboxOptionDataRef = Ref < { disabled : boolean ; value : unknown } >
35
36
type StateDefinition = {
36
37
// State
37
- ComboboxState : Ref < ComboboxStates >
38
+ comboboxState : Ref < ComboboxStates >
38
39
value : ComputedRef < unknown >
39
40
40
41
labelRef : Ref < HTMLLabelElement | null >
@@ -84,7 +85,7 @@ export let Combobox = defineComponent({
84
85
modelValue : { type : [ Object , String , Number , Boolean ] } ,
85
86
} ,
86
87
setup ( props , { slots, attrs, emit } ) {
87
- let ComboboxState = ref < StateDefinition [ 'ComboboxState ' ] [ 'value' ] > ( ComboboxStates . Closed )
88
+ let comboboxState = ref < StateDefinition [ 'comboboxState ' ] [ 'value' ] > ( ComboboxStates . Closed )
88
89
let labelRef = ref < StateDefinition [ 'labelRef' ] [ 'value' ] > ( null )
89
90
let inputRef = ref < StateDefinition [ 'inputRef' ] [ 'value' ] > ( null ) as StateDefinition [ 'inputRef' ]
90
91
let buttonRef = ref < StateDefinition [ 'buttonRef' ] [ 'value' ] > ( null ) as StateDefinition [ 'buttonRef' ]
@@ -97,7 +98,7 @@ export let Combobox = defineComponent({
97
98
let value = computed ( ( ) => props . modelValue )
98
99
99
100
let api = {
100
- ComboboxState ,
101
+ comboboxState ,
101
102
value,
102
103
inputRef,
103
104
labelRef,
@@ -109,18 +110,18 @@ export let Combobox = defineComponent({
109
110
inputPropsRef : ref < { displayValue ?: ( item : unknown ) => string } > ( { displayValue : undefined } ) ,
110
111
closeCombobox ( ) {
111
112
if ( props . disabled ) return
112
- if ( ComboboxState . value === ComboboxStates . Closed ) return
113
- ComboboxState . value = ComboboxStates . Closed
113
+ if ( comboboxState . value === ComboboxStates . Closed ) return
114
+ comboboxState . value = ComboboxStates . Closed
114
115
activeOptionIndex . value = null
115
116
} ,
116
117
openCombobox ( ) {
117
118
if ( props . disabled ) return
118
- if ( ComboboxState . value === ComboboxStates . Open ) return
119
- ComboboxState . value = ComboboxStates . Open
119
+ if ( comboboxState . value === ComboboxStates . Open ) return
120
+ comboboxState . value = ComboboxStates . Open
120
121
} ,
121
122
goToOption ( focus : Focus , id ?: string ) {
122
123
if ( props . disabled ) return
123
- if ( ComboboxState . value === ComboboxStates . Closed ) return
124
+ if ( comboboxState . value === ComboboxStates . Closed ) return
124
125
125
126
let nextActiveOptionIndex = calculateActiveIndex (
126
127
focus === Focus . Specific
@@ -209,7 +210,7 @@ export let Combobox = defineComponent({
209
210
let target = event . target as HTMLElement
210
211
let active = document . activeElement
211
212
212
- if ( ComboboxState . value !== ComboboxStates . Open ) return
213
+ if ( comboboxState . value !== ComboboxStates . Open ) return
213
214
214
215
if ( dom ( inputRef ) ?. contains ( target ) ) return
215
216
if ( dom ( buttonRef ) ?. contains ( target ) ) return
@@ -229,15 +230,15 @@ export let Combobox = defineComponent({
229
230
provide ( ComboboxContext , api )
230
231
useOpenClosedProvider (
231
232
computed ( ( ) =>
232
- match ( ComboboxState . value , {
233
+ match ( comboboxState . value , {
233
234
[ ComboboxStates . Open ] : State . Open ,
234
235
[ ComboboxStates . Closed ] : State . Closed ,
235
236
} )
236
237
)
237
238
)
238
239
239
240
return ( ) => {
240
- let slot = { open : ComboboxState . value === ComboboxStates . Open , disabled : props . disabled }
241
+ let slot = { open : comboboxState . value === ComboboxStates . Open , disabled : props . disabled }
241
242
return render ( {
242
243
props : omit ( props , [ 'modelValue' , 'onUpdate:modelValue' , 'disabled' , 'horizontal' ] ) ,
243
244
slot,
@@ -264,7 +265,7 @@ export let ComboboxLabel = defineComponent({
264
265
265
266
return ( ) => {
266
267
let slot = {
267
- open : api . ComboboxState . value === ComboboxStates . Open ,
268
+ open : api . comboboxState . value === ComboboxStates . Open ,
268
269
disabled : api . disabled . value ,
269
270
}
270
271
@@ -294,7 +295,7 @@ export let ComboboxButton = defineComponent({
294
295
295
296
function handleClick ( event : MouseEvent ) {
296
297
if ( api . disabled . value ) return
297
- if ( api . ComboboxState . value === ComboboxStates . Open ) {
298
+ if ( api . comboboxState . value === ComboboxStates . Open ) {
298
299
api . closeCombobox ( )
299
300
} else {
300
301
event . preventDefault ( )
@@ -311,7 +312,7 @@ export let ComboboxButton = defineComponent({
311
312
case Keys . ArrowDown :
312
313
event . preventDefault ( )
313
314
event . stopPropagation ( )
314
- if ( api . ComboboxState . value === ComboboxStates . Closed ) {
315
+ if ( api . comboboxState . value === ComboboxStates . Closed ) {
315
316
api . openCombobox ( )
316
317
// TODO: We can't do this outside next frame because the options aren't rendered yet
317
318
// But doing this in next frame results in a flicker because the dom mutations are async here
@@ -332,7 +333,7 @@ export let ComboboxButton = defineComponent({
332
333
case Keys . ArrowUp :
333
334
event . preventDefault ( )
334
335
event . stopPropagation ( )
335
- if ( api . ComboboxState . value === ComboboxStates . Closed ) {
336
+ if ( api . comboboxState . value === ComboboxStates . Closed ) {
336
337
api . openCombobox ( )
337
338
nextTick ( ( ) => {
338
339
if ( ! api . value . value ) {
@@ -359,7 +360,7 @@ export let ComboboxButton = defineComponent({
359
360
360
361
return ( ) => {
361
362
let slot = {
362
- open : api . ComboboxState . value === ComboboxStates . Open ,
363
+ open : api . comboboxState . value === ComboboxStates . Open ,
363
364
disabled : api . disabled . value ,
364
365
}
365
366
let propsWeControl = {
@@ -371,7 +372,7 @@ export let ComboboxButton = defineComponent({
371
372
'aria-controls' : dom ( api . optionsRef ) ?. id ,
372
373
'aria-expanded' : api . disabled . value
373
374
? undefined
374
- : api . ComboboxState . value === ComboboxStates . Open ,
375
+ : api . comboboxState . value === ComboboxStates . Open ,
375
376
'aria-labelledby' : api . labelRef . value ? [ dom ( api . labelRef ) ?. id , id ] . join ( ' ' ) : undefined ,
376
377
disabled : api . disabled . value === true ? true : undefined ,
377
378
onKeydown : handleKeydown ,
@@ -422,7 +423,7 @@ export let ComboboxInput = defineComponent({
422
423
case Keys . ArrowDown :
423
424
event . preventDefault ( )
424
425
event . stopPropagation ( )
425
- return match ( api . ComboboxState . value , {
426
+ return match ( api . comboboxState . value , {
426
427
[ ComboboxStates . Open ] : ( ) => api . goToOption ( Focus . Next ) ,
427
428
[ ComboboxStates . Closed ] : ( ) => {
428
429
api . openCombobox ( )
@@ -437,7 +438,7 @@ export let ComboboxInput = defineComponent({
437
438
case Keys . ArrowUp :
438
439
event . preventDefault ( )
439
440
event . stopPropagation ( )
440
- return match ( api . ComboboxState . value , {
441
+ return match ( api . comboboxState . value , {
441
442
[ ComboboxStates . Open ] : ( ) => api . goToOption ( Focus . Previous ) ,
442
443
[ ComboboxStates . Closed ] : ( ) => {
443
444
api . openCombobox ( )
@@ -480,7 +481,7 @@ export let ComboboxInput = defineComponent({
480
481
}
481
482
482
483
return ( ) => {
483
- let slot = { open : api . ComboboxState . value === ComboboxStates . Open }
484
+ let slot = { open : api . comboboxState . value === ComboboxStates . Open }
484
485
let propsWeControl = {
485
486
'aria-activedescendant' :
486
487
api . activeOptionIndex . value === null
@@ -527,11 +528,24 @@ export let ComboboxOptions = defineComponent({
527
528
return usesOpenClosedState . value === State . Open
528
529
}
529
530
530
- return api . ComboboxState . value === ComboboxStates . Open
531
+ return api . comboboxState . value === ComboboxStates . Open
532
+ } )
533
+
534
+ useTreeWalker ( {
535
+ container : computed ( ( ) => dom ( api . optionsRef ) ) ,
536
+ enabled : computed ( ( ) => api . comboboxState . value === ComboboxStates . Open ) ,
537
+ accept ( node ) {
538
+ if ( node . getAttribute ( 'role' ) === 'option' ) return NodeFilter . FILTER_REJECT
539
+ if ( node . hasAttribute ( 'role' ) ) return NodeFilter . FILTER_SKIP
540
+ return NodeFilter . FILTER_ACCEPT
541
+ } ,
542
+ walk ( node ) {
543
+ node . setAttribute ( 'role' , 'none' )
544
+ } ,
531
545
} )
532
546
533
547
return ( ) => {
534
- let slot = { open : api . ComboboxState . value === ComboboxStates . Open }
548
+ let slot = { open : api . comboboxState . value === ComboboxStates . Open }
535
549
let propsWeControl = {
536
550
'aria-activedescendant' :
537
551
api . activeOptionIndex . value === null
@@ -586,9 +600,9 @@ export let ComboboxOption = defineComponent({
586
600
587
601
onMounted ( ( ) => {
588
602
watch (
589
- [ api . ComboboxState , selected ] ,
603
+ [ api . comboboxState , selected ] ,
590
604
( ) => {
591
- if ( api . ComboboxState . value !== ComboboxStates . Open ) return
605
+ if ( api . comboboxState . value !== ComboboxStates . Open ) return
592
606
if ( ! selected . value ) return
593
607
api . goToOption ( Focus . Specific , id )
594
608
} ,
@@ -597,7 +611,7 @@ export let ComboboxOption = defineComponent({
597
611
} )
598
612
599
613
watchEffect ( ( ) => {
600
- if ( api . ComboboxState . value !== ComboboxStates . Open ) return
614
+ if ( api . comboboxState . value !== ComboboxStates . Open ) return
601
615
if ( ! active . value ) return
602
616
nextTick ( ( ) => document . getElementById ( id ) ?. scrollIntoView ?.( { block : 'nearest' } ) )
603
617
} )
0 commit comments