@@ -4,100 +4,127 @@ import {
4
4
useVisibleTask$ ,
5
5
type QwikIntrinsicElements ,
6
6
} from '@builder.io/qwik' ;
7
+
7
8
import {
8
9
ReferenceElement ,
9
- arrow ,
10
10
autoUpdate ,
11
11
computePosition ,
12
- flip ,
13
- offset ,
14
- shift ,
12
+ offset as _offset ,
13
+ flip as _flip ,
14
+ shift as _shift ,
15
+ arrow as _arrow ,
16
+ size as _size ,
17
+ autoPlacement as _autoPlacement ,
18
+ hide as _hide ,
19
+ inline as _inline ,
20
+ type Placement ,
21
+ type DetectOverflowOptions ,
22
+ type ComputePositionReturn ,
15
23
} from '@floating-ui/dom' ;
24
+
25
+ import type {
26
+ ShiftOptions ,
27
+ OffsetOptions ,
28
+ ArrowOptions ,
29
+ FlipOptions ,
30
+ SizeOptions ,
31
+ AutoPlacementOptions ,
32
+ HideOptions ,
33
+ InlineOptions ,
34
+ Platform ,
35
+ } from '@floating-ui/core' ;
36
+
16
37
import ComboboxContextId from './combobox-context-id' ;
17
38
import type { ComboboxContext , Option } from './combobox-context.type' ;
18
39
19
- // type ArrowData = { element: HTMLElement; padding?: number | undefined };
20
-
21
40
export type ComboboxListboxProps = {
22
- // come back to shift later
23
- arrowData ?: { element : HTMLElement ; padding ?: number | undefined } ;
24
- setArrow ?: boolean ;
25
- setShift ?: {
26
- mainAxis ?: boolean ;
27
- crossAxis ?: boolean ;
28
- limiter ?: {
29
- fn : ( state : unknown ) => unknown ;
30
- options ?: unknown ;
31
- } ;
32
- } ;
33
- setOffset ?:
34
- | number
35
- | {
36
- mainAxis ?: number ;
37
- crossAxis ?: number ;
38
- alignmentAxis ?: number | null ;
39
- } ;
40
- setFlip ?: boolean ;
41
- placement ?:
42
- | 'top'
43
- | 'top-start'
44
- | 'top-end'
45
- | 'right'
46
- | 'right-start'
47
- | 'right-end'
48
- | 'bottom'
49
- | 'bottom-start'
50
- | 'bottom-end'
51
- | 'left'
52
- | 'left-start'
53
- | 'left-end' ;
41
+ // main floating UI props
42
+ placement ?: Placement ;
54
43
ancestorScroll ?: boolean ;
55
44
ancestorResize ?: boolean ;
56
45
elementResize ?: boolean ;
57
46
layoutShift ?: boolean ;
58
47
animationFrame ?: boolean ;
48
+
49
+ // middleware
50
+ offset ?: OffsetOptions ;
51
+ shift ?: Partial < ShiftOptions & DetectOverflowOptions > | boolean ;
52
+ flip ?: FlipOptions | boolean ;
53
+ arrow ?: ArrowOptions ;
54
+ size ?: SizeOptions ;
55
+ autoPlacement ?: AutoPlacementOptions | boolean ;
56
+ hide ?: HideOptions | boolean ;
57
+ inline ?: InlineOptions | boolean ;
58
+ onPositionComputed ?: ( resolvedData : ComputePositionReturn ) => void ;
59
+
60
+ // misc
61
+ transform : string ;
62
+ platform : Platform ;
59
63
} & QwikIntrinsicElements [ 'ul' ] ;
60
64
61
65
export const ComboboxListbox = component$ (
62
66
< O extends Option = Option > ( {
63
- setOffset ,
64
- setFlip = true ,
67
+ offset ,
68
+ flip = true ,
65
69
placement = 'bottom' ,
66
- setShift,
67
- setArrow,
68
- arrowData,
70
+ shift,
71
+ arrow,
72
+ size,
73
+ hide,
74
+ inline,
75
+ autoPlacement = false ,
69
76
ancestorScroll = true ,
70
77
ancestorResize = true ,
71
78
elementResize = true ,
72
79
animationFrame = false ,
80
+ onPositionComputed,
81
+ transform,
82
+ platform,
73
83
...props
74
84
} : ComboboxListboxProps ) => {
75
85
const context = useContext < ComboboxContext < O > > ( ComboboxContextId ) ;
76
86
const listboxId = `${ context . localId } -listbox` ;
77
87
78
- useVisibleTask$ ( function setListboxPosition ( { cleanup } ) {
79
- // Our settings from Floating UI
88
+ useVisibleTask$ ( function setFloatingUIConfig ( { cleanup } ) {
80
89
function updatePosition ( ) {
81
- const middleware = [ offset ( setOffset ) , setFlip && flip ( ) , setShift && shift ( ) ] ;
90
+ const middleware = [ _offset ( offset ) , arrow && _arrow ( arrow ) , size && _size ( size ) ] ;
82
91
83
- if ( setArrow && arrowData ) {
84
- middleware . push ( arrow ( arrowData ) ) ;
85
- }
92
+ // offers a bool to turn on or off default config, or customize it.
93
+ const middlewareFunctions = [ _flip , _shift , _autoPlacement , _hide , _inline ] ;
94
+ const middlewareProps = [ flip , shift , autoPlacement , hide , inline ] ;
95
+
96
+ middlewareFunctions . forEach ( ( func , index ) => {
97
+ const isMiddlewareEnabled = middlewareProps [ index ] ;
98
+
99
+ if ( isMiddlewareEnabled ) {
100
+ const middlewareConfig =
101
+ isMiddlewareEnabled === true ? undefined : isMiddlewareEnabled ;
102
+ middleware . push ( func ( middlewareConfig ) ) ;
103
+ }
104
+ } ) ;
86
105
87
106
computePosition (
88
107
context . inputRef . value as ReferenceElement ,
89
108
context . listboxRef . value as HTMLElement ,
90
109
{
91
- placement : placement ,
92
- middleware : middleware ,
110
+ placement,
111
+ middleware,
112
+ platform,
93
113
} ,
94
- ) . then ( ( { x, y } ) => {
114
+ ) . then ( ( resolvedData ) => {
115
+ const { x, y } = resolvedData ;
95
116
if ( context . listboxRef . value ) {
96
117
Object . assign ( context . listboxRef . value . style , {
97
118
left : `${ x } px` ,
98
119
top : `${ y } px` ,
120
+ transform,
99
121
} ) ;
100
122
}
123
+
124
+ // user-provided resolved code
125
+ if ( onPositionComputed ) {
126
+ onPositionComputed ( resolvedData ) ;
127
+ }
101
128
} ) ;
102
129
}
103
130
@@ -109,10 +136,10 @@ export const ComboboxListbox = component$(
109
136
context . listboxRef . value ,
110
137
updatePosition ,
111
138
{
112
- ancestorScroll : ancestorScroll ,
113
- ancestorResize : ancestorResize ,
114
- elementResize : elementResize ,
115
- animationFrame : animationFrame ,
139
+ ancestorScroll,
140
+ ancestorResize,
141
+ elementResize,
142
+ animationFrame,
116
143
} ,
117
144
) ;
118
145
@@ -128,9 +155,7 @@ export const ComboboxListbox = component$(
128
155
id = { listboxId }
129
156
ref = { context . listboxRef }
130
157
aria-label = {
131
- context . labelRef . value
132
- ? context . labelRef . value ?. innerText
133
- : context . inputRef . value ?. value
158
+ context . labelRef . value ? context . labelRef . value ?. innerText : 'Suggestions'
134
159
}
135
160
role = "listbox"
136
161
hidden = { ! context . isListboxOpenSig . value }
0 commit comments