@@ -31,7 +31,15 @@ import { useTreeWalker } from '../../hooks/use-tree-walker'
31
31
32
32
import { calculateActiveIndex , Focus } from '../../utils/calculate-active-index'
33
33
import { disposables } from '../../utils/disposables'
34
- import { forwardRefWithAs , render , compact , PropsForFeatures , Features } from '../../utils/render'
34
+ import {
35
+ forwardRefWithAs ,
36
+ render ,
37
+ compact ,
38
+ PropsForFeatures ,
39
+ Features ,
40
+ HasDisplayName ,
41
+ RefProp ,
42
+ } from '../../utils/render'
35
43
import { isDisabledReactIssue7711 } from '../../utils/bugs'
36
44
import { match } from '../../utils/match'
37
45
import { objectToFormEntries } from '../../utils/form'
@@ -313,12 +321,12 @@ function stateReducer<T>(state: StateDefinition<T>, action: Actions<T>) {
313
321
// ---
314
322
315
323
let DEFAULT_COMBOBOX_TAG = Fragment
316
- interface ComboboxRenderPropArg < T > {
324
+ interface ComboboxRenderPropArg < TValue , TActive = TValue > {
317
325
open : boolean
318
326
disabled : boolean
319
327
activeIndex : number | null
320
- activeOption : T | null
321
- value : T
328
+ activeOption : TActive | null
329
+ value : TValue
322
330
}
323
331
324
332
type O = 'value' | 'defaultValue' | 'nullable' | 'multiple' | 'onChange' | 'by'
@@ -336,7 +344,7 @@ type ComboboxValueProps<
336
344
multiple : true
337
345
onChange ?( value : EnsureArray < TValue > ) : void
338
346
by ?: ByComparator < TValue >
339
- } & Props < TTag , ComboboxRenderPropArg < EnsureArray < TValue > > , O > )
347
+ } & Props < TTag , ComboboxRenderPropArg < EnsureArray < TValue > , TValue > , O > )
340
348
| ( {
341
349
value ?: TValue | null
342
350
defaultValue ?: TValue | null
@@ -352,7 +360,7 @@ type ComboboxValueProps<
352
360
multiple : true
353
361
onChange ?( value : EnsureArray < TValue > ) : void
354
362
by ?: ByComparator < TValue extends Array < infer U > ? U : TValue >
355
- } & Expand < Props < TTag , ComboboxRenderPropArg < EnsureArray < TValue > > , O > > )
363
+ } & Expand < Props < TTag , ComboboxRenderPropArg < EnsureArray < TValue > , TValue > , O > > )
356
364
| ( {
357
365
value ?: TValue
358
366
nullable ?: false
@@ -364,7 +372,7 @@ type ComboboxValueProps<
364
372
{ nullable ?: TNullable ; multiple ?: TMultiple }
365
373
>
366
374
367
- type ComboboxProps <
375
+ export type ComboboxProps <
368
376
TValue ,
369
377
TNullable extends boolean | undefined ,
370
378
TMultiple extends boolean | undefined ,
@@ -678,7 +686,6 @@ function ComboboxFn<TValue, TTag extends ElementType = typeof DEFAULT_COMBOBOX_T
678
686
</ ComboboxActionsContext . Provider >
679
687
)
680
688
}
681
- let ComboboxRoot = forwardRefWithAs ( ComboboxFn )
682
689
683
690
// ---
684
691
@@ -697,23 +704,27 @@ type InputPropsWeControl =
697
704
| 'onChange'
698
705
| 'displayValue'
699
706
700
- let Input = forwardRefWithAs ( function Input <
707
+ export type ComboboxInputProps < TTag extends ElementType , TType > = Props <
708
+ TTag ,
709
+ InputRenderPropArg ,
710
+ InputPropsWeControl
711
+ > & {
712
+ displayValue ?( item : TType ) : string
713
+ onChange ?( event : React . ChangeEvent < HTMLInputElement > ) : void
714
+ }
715
+
716
+ function InputFn <
701
717
TTag extends ElementType = typeof DEFAULT_INPUT_TAG ,
702
718
// TODO: One day we will be able to infer this type from the generic in Combobox itself.
703
719
// But today is not that day..
704
720
TType = Parameters < typeof ComboboxRoot > [ 0 ] [ 'value' ]
705
- > (
706
- props : Props < TTag , InputRenderPropArg , InputPropsWeControl > & {
707
- displayValue ?( item : TType ) : string
708
- onChange ( event : React . ChangeEvent < HTMLInputElement > ) : void
709
- } ,
710
- ref : Ref < HTMLInputElement >
711
- ) {
721
+ > ( props : ComboboxInputProps < TTag , TType > , ref : Ref < HTMLInputElement > ) {
712
722
let internalId = useId ( )
713
723
let {
714
724
id = `headlessui-combobox-input-${ internalId } ` ,
715
725
onChange,
716
726
displayValue,
727
+ // @ts -ignore: We know this MAY NOT exist for a given tag but we only care when it _does_ exist.
717
728
type = 'text' ,
718
729
...theirProps
719
730
} = props
@@ -988,7 +999,7 @@ let Input = forwardRefWithAs(function Input<
988
999
defaultTag : DEFAULT_INPUT_TAG ,
989
1000
name : 'Combobox.Input' ,
990
1001
} )
991
- } )
1002
+ }
992
1003
993
1004
// ---
994
1005
@@ -999,7 +1010,7 @@ interface ButtonRenderPropArg {
999
1010
value : any
1000
1011
}
1001
1012
type ButtonPropsWeControl =
1002
- | 'type'
1013
+ // | 'type' // While we do "control" this prop we allow it to be overridden
1003
1014
| 'tabIndex'
1004
1015
| 'aria-haspopup'
1005
1016
| 'aria-controls'
@@ -1009,8 +1020,14 @@ type ButtonPropsWeControl =
1009
1020
| 'onClick'
1010
1021
| 'onKeyDown'
1011
1022
1012
- let Button = forwardRefWithAs ( function Button < TTag extends ElementType = typeof DEFAULT_BUTTON_TAG > (
1013
- props : Props < TTag , ButtonRenderPropArg , ButtonPropsWeControl > ,
1023
+ export type ComboboxButtonProps < TTag extends ElementType > = Props <
1024
+ TTag ,
1025
+ ButtonRenderPropArg ,
1026
+ ButtonPropsWeControl
1027
+ >
1028
+
1029
+ function ButtonFn < TTag extends ElementType = typeof DEFAULT_BUTTON_TAG > (
1030
+ props : ComboboxButtonProps < TTag > ,
1014
1031
ref : Ref < HTMLButtonElement >
1015
1032
) {
1016
1033
let data = useData ( 'Combobox.Button' )
@@ -1105,7 +1122,7 @@ let Button = forwardRefWithAs(function Button<TTag extends ElementType = typeof
1105
1122
defaultTag : DEFAULT_BUTTON_TAG ,
1106
1123
name : 'Combobox.Button' ,
1107
1124
} )
1108
- } )
1125
+ }
1109
1126
1110
1127
// ---
1111
1128
@@ -1116,8 +1133,14 @@ interface LabelRenderPropArg {
1116
1133
}
1117
1134
type LabelPropsWeControl = 'ref' | 'onClick'
1118
1135
1119
- let Label = forwardRefWithAs ( function Label < TTag extends ElementType = typeof DEFAULT_LABEL_TAG > (
1120
- props : Props < TTag , LabelRenderPropArg , LabelPropsWeControl > ,
1136
+ export type ComboboxLabelProps < TTag extends ElementType > = Props <
1137
+ TTag ,
1138
+ LabelRenderPropArg ,
1139
+ LabelPropsWeControl
1140
+ >
1141
+
1142
+ function LabelFn < TTag extends ElementType = typeof DEFAULT_LABEL_TAG > (
1143
+ props : ComboboxLabelProps < TTag > ,
1121
1144
ref : Ref < HTMLLabelElement >
1122
1145
) {
1123
1146
let internalId = useId ( )
@@ -1144,7 +1167,7 @@ let Label = forwardRefWithAs(function Label<TTag extends ElementType = typeof DE
1144
1167
defaultTag : DEFAULT_LABEL_TAG ,
1145
1168
name : 'Combobox.Label' ,
1146
1169
} )
1147
- } )
1170
+ }
1148
1171
1149
1172
// ---
1150
1173
@@ -1156,13 +1179,17 @@ type OptionsPropsWeControl = 'aria-labelledby' | 'hold' | 'onKeyDown' | 'role' |
1156
1179
1157
1180
let OptionsRenderFeatures = Features . RenderStrategy | Features . Static
1158
1181
1159
- let Options = forwardRefWithAs ( function Options <
1160
- TTag extends ElementType = typeof DEFAULT_OPTIONS_TAG
1161
- > (
1162
- props : Props < TTag , OptionsRenderPropArg , OptionsPropsWeControl > &
1163
- PropsForFeatures < typeof OptionsRenderFeatures > & {
1164
- hold ?: boolean
1165
- } ,
1182
+ export type ComboboxOptionsProps < TTag extends ElementType > = Props <
1183
+ TTag ,
1184
+ OptionsRenderPropArg ,
1185
+ OptionsPropsWeControl
1186
+ > &
1187
+ PropsForFeatures < typeof OptionsRenderFeatures > & {
1188
+ hold ?: boolean
1189
+ }
1190
+
1191
+ function OptionsFn < TTag extends ElementType = typeof DEFAULT_OPTIONS_TAG > (
1192
+ props : ComboboxOptionsProps < TTag > ,
1166
1193
ref : Ref < HTMLUListElement >
1167
1194
) {
1168
1195
let internalId = useId ( )
@@ -1226,7 +1253,7 @@ let Options = forwardRefWithAs(function Options<
1226
1253
visible,
1227
1254
name : 'Combobox.Options' ,
1228
1255
} )
1229
- } )
1256
+ }
1230
1257
1231
1258
// ---
1232
1259
@@ -1238,18 +1265,21 @@ interface OptionRenderPropArg {
1238
1265
}
1239
1266
type ComboboxOptionPropsWeControl = 'role' | 'tabIndex' | 'aria-disabled' | 'aria-selected'
1240
1267
1241
- let Option = forwardRefWithAs ( function Option <
1268
+ export type ComboboxOptionProps < TTag extends ElementType , TType > = Props <
1269
+ TTag ,
1270
+ OptionRenderPropArg ,
1271
+ ComboboxOptionPropsWeControl | 'value'
1272
+ > & {
1273
+ disabled ?: boolean
1274
+ value : TType
1275
+ }
1276
+
1277
+ function OptionFn <
1242
1278
TTag extends ElementType = typeof DEFAULT_OPTION_TAG ,
1243
1279
// TODO: One day we will be able to infer this type from the generic in Combobox itself.
1244
1280
// But today is not that day..
1245
1281
TType = Parameters < typeof ComboboxRoot > [ 0 ] [ 'value' ]
1246
- > (
1247
- props : Props < TTag , OptionRenderPropArg , ComboboxOptionPropsWeControl | 'value' > & {
1248
- disabled ?: boolean
1249
- value : TType
1250
- } ,
1251
- ref : Ref < HTMLLIElement >
1252
- ) {
1282
+ > ( props : ComboboxOptionProps < TTag , TType > , ref : Ref < HTMLLIElement > ) {
1253
1283
let internalId = useId ( )
1254
1284
let {
1255
1285
id = `headlessui-combobox-option-${ internalId } ` ,
@@ -1296,7 +1326,13 @@ let Option = forwardRefWithAs(function Option<
1296
1326
internalOptionRef . current ?. scrollIntoView ?.( { block : 'nearest' } )
1297
1327
} )
1298
1328
return d . dispose
1299
- } , [ internalOptionRef , active , data . comboboxState , data . activationTrigger , /* We also want to trigger this when the position of the active item changes so that we can re-trigger the scrollIntoView */ data . activeOptionIndex ] )
1329
+ } , [
1330
+ internalOptionRef ,
1331
+ active ,
1332
+ data . comboboxState ,
1333
+ data . activationTrigger ,
1334
+ /* We also want to trigger this when the position of the active item changes so that we can re-trigger the scrollIntoView */ data . activeOptionIndex ,
1335
+ ] )
1300
1336
1301
1337
let handleClick = useEvent ( ( event : { preventDefault : Function } ) => {
1302
1338
if ( disabled ) return event . preventDefault ( )
@@ -1379,8 +1415,63 @@ let Option = forwardRefWithAs(function Option<
1379
1415
defaultTag : DEFAULT_OPTION_TAG ,
1380
1416
name : 'Combobox.Option' ,
1381
1417
} )
1382
- } )
1418
+ }
1383
1419
1384
1420
// ---
1385
1421
1422
+ interface ComponentCombobox extends HasDisplayName {
1423
+ < TValue , TTag extends ElementType = typeof DEFAULT_COMBOBOX_TAG > (
1424
+ props : ComboboxProps < TValue , true , true , TTag > & RefProp < typeof ComboboxFn >
1425
+ ) : JSX . Element
1426
+ < TValue , TTag extends ElementType = typeof DEFAULT_COMBOBOX_TAG > (
1427
+ props : ComboboxProps < TValue , true , false , TTag > & RefProp < typeof ComboboxFn >
1428
+ ) : JSX . Element
1429
+ < TValue , TTag extends ElementType = typeof DEFAULT_COMBOBOX_TAG > (
1430
+ props : ComboboxProps < TValue , false , false , TTag > & RefProp < typeof ComboboxFn >
1431
+ ) : JSX . Element
1432
+ < TValue , TTag extends ElementType = typeof DEFAULT_COMBOBOX_TAG > (
1433
+ props : ComboboxProps < TValue , false , true , TTag > & RefProp < typeof ComboboxFn >
1434
+ ) : JSX . Element
1435
+ }
1436
+
1437
+ interface ComponentComboboxButton extends HasDisplayName {
1438
+ < TTag extends ElementType = typeof DEFAULT_BUTTON_TAG > (
1439
+ props : ComboboxButtonProps < TTag > & RefProp < typeof ButtonFn >
1440
+ ) : JSX . Element
1441
+ }
1442
+
1443
+ interface ComponentComboboxInput extends HasDisplayName {
1444
+ < TType , TTag extends ElementType = typeof DEFAULT_INPUT_TAG > (
1445
+ props : ComboboxInputProps < TTag , TType > & RefProp < typeof InputFn >
1446
+ ) : JSX . Element
1447
+ }
1448
+
1449
+ interface ComponentComboboxLabel extends HasDisplayName {
1450
+ < TTag extends ElementType = typeof DEFAULT_LABEL_TAG > (
1451
+ props : ComboboxLabelProps < TTag > & RefProp < typeof LabelFn >
1452
+ ) : JSX . Element
1453
+ }
1454
+
1455
+ interface ComponentComboboxOptions extends HasDisplayName {
1456
+ < TTag extends ElementType = typeof DEFAULT_OPTIONS_TAG > (
1457
+ props : ComboboxOptionsProps < TTag > & RefProp < typeof OptionsFn >
1458
+ ) : JSX . Element
1459
+ }
1460
+
1461
+ interface ComponentComboboxOption extends HasDisplayName {
1462
+ <
1463
+ TTag extends ElementType = typeof DEFAULT_OPTION_TAG ,
1464
+ TType = Parameters < typeof ComboboxRoot > [ 0 ] [ 'value' ]
1465
+ > (
1466
+ props : ComboboxOptionProps < TTag , TType > & RefProp < typeof OptionFn >
1467
+ ) : JSX . Element
1468
+ }
1469
+
1470
+ let ComboboxRoot = forwardRefWithAs ( ComboboxFn ) as unknown as ComponentCombobox
1471
+ let Button = forwardRefWithAs ( ButtonFn ) as unknown as ComponentComboboxButton
1472
+ let Input = forwardRefWithAs ( InputFn ) as unknown as ComponentComboboxInput
1473
+ let Label = forwardRefWithAs ( LabelFn ) as unknown as ComponentComboboxLabel
1474
+ let Options = forwardRefWithAs ( OptionsFn ) as unknown as ComponentComboboxOptions
1475
+ let Option = forwardRefWithAs ( OptionFn ) as unknown as ComponentComboboxOption
1476
+
1386
1477
export let Combobox = Object . assign ( ComboboxRoot , { Input, Button, Label, Options, Option } )
0 commit comments