1- import React , { ReactElement , useState } from 'react' ;
1+ import React from 'react' ;
22import {
33 Select as BaseSelect ,
44 SelectProps as BaseSelectProps ,
55 SelectValue ,
66 SelectStateContext ,
7- FieldError
7+ FieldError ,
8+ SelectValueRenderProps
89} from 'react-aria-components' ;
910import { useIsSSR } from 'react-aria' ;
1011import { useResizeObserver } from '@react-aria/utils' ;
@@ -41,42 +42,47 @@ const FakeButton = styled(FakeInput)`
4142 }
4243` ;
4344
44- interface SelectFieldProps extends Pick < FieldProps , 'label' | 'description' | 'errorMessage' | 'leadingIcon' > {
45+ interface SelectFieldProps < T > extends Pick < FieldProps , 'label' | 'description' | 'errorMessage' | 'leadingIcon' > {
4546 placeholder ?: string ;
47+ renderValue ?: ( props : SelectValueRenderProps < T > & { defaultChildren : React . ReactNode } ) => React . ReactNode ;
4648}
4749
48- const SelectTrigger = React . forwardRef < HTMLDivElement , SelectFieldProps > (
49- ( { label, leadingIcon, placeholder } , forwardedRef ) => {
50- const state = React . useContext ( SelectStateContext ) ;
51- const buttonRef = React . useRef < HTMLButtonElement > ( null ) ;
50+ // eslint-disable-next-line @typescript-eslint/ban-types
51+ function SelectTriggerWithRef < T extends object > (
52+ { label, leadingIcon, placeholder, renderValue } : SelectFieldProps < T > ,
53+ forwardedRef : React . ForwardedRef < HTMLDivElement >
54+ ) {
55+ const state = React . useContext ( SelectStateContext ) ;
56+ const buttonRef = React . useRef < HTMLButtonElement > ( null ) ;
5257
53- return (
54- < FakeButton
55- $isVisuallyFocused = { state ?. isOpen }
56- ref = { forwardedRef }
57- onClick = { ( ) => buttonRef . current ?. click ( ) }
58- >
59- { leadingIcon }
60- < InnerWrapper >
61- < Label $flying = { Boolean ( placeholder || state ?. selectedItem ) } > { label } </ Label >
62- < Button ref = { buttonRef } >
63- < SelectValue >
64- { ( { defaultChildren, isPlaceholder } ) =>
65- isPlaceholder
66- ? placeholder || < VisuallyHidden > { defaultChildren } </ VisuallyHidden >
67- : defaultChildren
68- }
69- </ SelectValue >
70- </ Button >
71- </ InnerWrapper >
72- { state ?. isOpen ? < DropupSelectIcon /> : < DropdownSelectIcon /> }
73- </ FakeButton >
74- ) ;
75- }
76- ) ;
58+ return (
59+ < FakeButton $isVisuallyFocused = { state ?. isOpen } ref = { forwardedRef } onClick = { ( ) => buttonRef . current ?. click ( ) } >
60+ { leadingIcon }
61+ < InnerWrapper >
62+ < Label $flying = { Boolean ( placeholder || state ?. selectedItem ) } > { label } </ Label >
63+ < Button ref = { buttonRef } >
64+ < SelectValue < T > >
65+ { selectValueRenderProps =>
66+ renderValue
67+ ? renderValue ( selectValueRenderProps )
68+ : ( function defaultRenderValue ( { isPlaceholder, defaultChildren } ) {
69+ return isPlaceholder
70+ ? placeholder || < VisuallyHidden > { defaultChildren } </ VisuallyHidden >
71+ : defaultChildren ;
72+ } ) ( selectValueRenderProps )
73+ }
74+ </ SelectValue >
75+ </ Button >
76+ </ InnerWrapper >
77+ { state ?. isOpen ? < DropupSelectIcon /> : < DropdownSelectIcon /> }
78+ </ FakeButton >
79+ ) ;
80+ }
81+
82+ const SelectTrigger = React . forwardRef ( SelectTriggerWithRef ) ;
7783
7884interface SelectProps < T extends Record < string , unknown > >
79- extends SelectFieldProps ,
85+ extends SelectFieldProps < T > ,
8086 Omit < BaseSelectProps < T > , 'children' > {
8187 items ?: Iterable < T > ;
8288 children : React . ReactNode | ( ( item : T ) => React . ReactNode ) ;
@@ -89,9 +95,10 @@ function Select<T extends Record<string, unknown>>({
8995 errorMessage,
9096 description,
9197 placeholder,
98+ renderValue,
9299 ...props
93- } : SelectProps < T > ) : ReactElement {
94- const [ menuWidth , setMenuWidth ] = useState < string | null > ( null ) ;
100+ } : SelectProps < T > ) : React . ReactElement {
101+ const [ menuWidth , setMenuWidth ] = React . useState < string | null > ( null ) ;
95102 const triggerRef = React . useRef < HTMLDivElement > ( null ) ;
96103 const isSSR = useIsSSR ( ) ;
97104
@@ -117,6 +124,7 @@ function Select<T extends Record<string, unknown>>({
117124 label = { label }
118125 leadingIcon = { leadingIcon }
119126 placeholder = { placeholder }
127+ renderValue = { renderValue }
120128 />
121129 < Footer > { isInvalid ? < FieldError > { errorMessage } </ FieldError > : description } </ Footer >
122130 </ Wrapper >
0 commit comments