@@ -19,7 +19,7 @@ import {
1919import { Section as BaseSection , useComboBoxState } from 'react-stately' ;
2020
2121import { useEvent } from '../../../_internal/index' ;
22- import { DownIcon , LoadingIcon } from '../../../icons' ;
22+ import { CloseIcon , DownIcon , LoadingIcon } from '../../../icons' ;
2323import { useProviderProps } from '../../../provider' ;
2424import { FieldBaseProps } from '../../../shared' ;
2525import {
@@ -37,6 +37,7 @@ import {
3737} from '../../../utils/react' ;
3838import { useFocus } from '../../../utils/react/interactions' ;
3939import { useEventBus } from '../../../utils/react/useEventBus' ;
40+ import { ItemAction } from '../../actions' ;
4041import { useFieldProps , useFormProps , wrapWithField } from '../../form' ;
4142import { Item } from '../../Item' ;
4243import { OverlayWrapper } from '../../overlays/OverlayWrapper' ;
@@ -70,7 +71,9 @@ const TriggerElement = tasty({
7071 placeContent : 'center' ,
7172 placeSelf : 'stretch' ,
7273 radius : '(1r - 1bw) right' ,
74+ padding : '0' ,
7375 width : '3x' ,
76+ boxSizing : 'border-box' ,
7477 color : {
7578 '' : '#dark-02' ,
7679 hovered : '#dark-02' ,
@@ -125,6 +128,8 @@ export interface CubeComboBoxProps<T>
125128 suffixPosition ?: 'before' | 'after' ;
126129 menuTrigger ?: MenuTriggerAction ;
127130 allowsCustomValue ?: boolean ;
131+ /** Whether the combo box is clearable using ESC keyboard button or clear button inside the input */
132+ isClearable ?: boolean ;
128133}
129134
130135const PROP_STYLES = [ ...BASE_STYLES , ...OUTER_STYLES , ...COLOR_STYLES ] ;
@@ -201,6 +206,7 @@ export const ComboBox = forwardRef(function ComboBox<T extends object>(
201206 labelSuffix,
202207 selectedKey,
203208 defaultSelectedKey,
209+ isClearable,
204210 ...otherProps
205211 } = props ;
206212
@@ -306,6 +312,32 @@ export const ComboBox = forwardRef(function ComboBox<T extends object>(
306312 let validationIcon = isInvalid ? InvalidIcon : ValidIcon ;
307313 let validation = cloneElement ( validationIcon ) ;
308314
315+ // Clear button logic
316+ let hasValue = props . allowsCustomValue
317+ ? state . inputValue !== ''
318+ : state . selectedKey != null ;
319+ let showClearButton =
320+ isClearable && hasValue && ! isDisabled && ! props . isReadOnly ;
321+
322+ // Clear function
323+ let clearValue = useEvent ( ( ) => {
324+ // Always clear input value in state so UI resets to placeholder
325+ state . setInputValue ( '' ) ;
326+ // Notify external input value only when custom value mode is enabled
327+ if ( props . allowsCustomValue ) {
328+ props . onInputChange ?.( '' ) ;
329+ }
330+ props . onSelectionChange ?.( null ) ;
331+ state . setSelectedKey ( null ) ;
332+
333+ // Close the popup if it's open
334+ if ( state . isOpen ) {
335+ state . close ( ) ;
336+ }
337+ // Focus back to the input
338+ inputRef . current ?. focus ( ) ;
339+ } ) ;
340+
309341 let comboBoxWidth = wrapperRef ?. current ?. offsetWidth ;
310342
311343 if ( icon ) {
@@ -333,6 +365,7 @@ export const ComboBox = forwardRef(function ComboBox<T extends object>(
333365 loading : isLoading ,
334366 prefix : ! ! prefix ,
335367 suffix : true ,
368+ clearable : showClearButton ,
336369 } ) ,
337370 [
338371 isInvalid ,
@@ -342,6 +375,7 @@ export const ComboBox = forwardRef(function ComboBox<T extends object>(
342375 isFocused ,
343376 isLoading ,
344377 prefix ,
378+ showClearButton ,
345379 ] ,
346380 ) ;
347381
@@ -453,6 +487,16 @@ export const ComboBox = forwardRef(function ComboBox<T extends object>(
453487 </ >
454488 ) : null }
455489 { suffixPosition === 'after' ? suffix : null }
490+ { showClearButton && (
491+ < ItemAction
492+ icon = { < CloseIcon /> }
493+ size = { size }
494+ theme = { validationState === 'invalid' ? 'danger' : undefined }
495+ qa = "ComboBoxClearButton"
496+ data-no-trigger = { hideTrigger ? '' : undefined }
497+ onPress = { clearValue }
498+ />
499+ ) }
456500 { ! hideTrigger ? (
457501 < TriggerElement
458502 data-popover-trigger
0 commit comments