@@ -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 { Button } 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' ,
@@ -90,6 +93,15 @@ const TriggerElement = tasty({
9093 } ,
9194} ) ;
9295
96+ const ClearButton = tasty ( Button , {
97+ icon : < CloseIcon /> ,
98+ styles : {
99+ height : '($size - 1x)' ,
100+ width : '($size - 1x)' ,
101+ margin : '0 .5x' ,
102+ } ,
103+ } ) ;
104+
93105export interface CubeComboBoxProps < T >
94106 extends Omit <
95107 CubeSelectBaseProps < T > ,
@@ -125,6 +137,8 @@ export interface CubeComboBoxProps<T>
125137 suffixPosition ?: 'before' | 'after' ;
126138 menuTrigger ?: MenuTriggerAction ;
127139 allowsCustomValue ?: boolean ;
140+ /** Whether the combo box is clearable using ESC keyboard button or clear button inside the input */
141+ isClearable ?: boolean ;
128142}
129143
130144const PROP_STYLES = [ ...BASE_STYLES , ...OUTER_STYLES , ...COLOR_STYLES ] ;
@@ -201,6 +215,7 @@ export const ComboBox = forwardRef(function ComboBox<T extends object>(
201215 labelSuffix,
202216 selectedKey,
203217 defaultSelectedKey,
218+ isClearable,
204219 ...otherProps
205220 } = props ;
206221
@@ -306,6 +321,42 @@ export const ComboBox = forwardRef(function ComboBox<T extends object>(
306321 let validationIcon = isInvalid ? InvalidIcon : ValidIcon ;
307322 let validation = cloneElement ( validationIcon ) ;
308323
324+ // Clear button logic
325+ let hasValue = props . allowsCustomValue
326+ ? state . inputValue !== ''
327+ : state . selectedKey != null ;
328+ let showClearButton =
329+ isClearable && hasValue && ! isDisabled && ! props . isReadOnly ;
330+
331+ // Clear function
332+ let clearValue = useEvent ( ( ) => {
333+ if ( props . allowsCustomValue ) {
334+ props . onInputChange ?.( '' ) ;
335+ // If state has a setInputValue method, use it as well
336+ if (
337+ 'setInputValue' in state &&
338+ typeof state . setInputValue === 'function'
339+ ) {
340+ state . setInputValue ( '' ) ;
341+ }
342+ } else {
343+ props . onSelectionChange ?.( null ) ;
344+ // If state has a setSelectedKey method, use it as well
345+ if (
346+ 'setSelectedKey' in state &&
347+ typeof state . setSelectedKey === 'function'
348+ ) {
349+ state . setSelectedKey ( null ) ;
350+ }
351+ }
352+ // Close the popup if it's open
353+ if ( state . isOpen ) {
354+ state . close ( ) ;
355+ }
356+ // Focus back to the input
357+ inputRef . current ?. focus ( ) ;
358+ } ) ;
359+
309360 let comboBoxWidth = wrapperRef ?. current ?. offsetWidth ;
310361
311362 if ( icon ) {
@@ -333,6 +384,7 @@ export const ComboBox = forwardRef(function ComboBox<T extends object>(
333384 loading : isLoading ,
334385 prefix : ! ! prefix ,
335386 suffix : true ,
387+ clearable : showClearButton ,
336388 } ) ,
337389 [
338390 isInvalid ,
@@ -342,6 +394,7 @@ export const ComboBox = forwardRef(function ComboBox<T extends object>(
342394 isFocused ,
343395 isLoading ,
344396 prefix ,
397+ showClearButton ,
345398 ] ,
346399 ) ;
347400
@@ -453,6 +506,16 @@ export const ComboBox = forwardRef(function ComboBox<T extends object>(
453506 </ >
454507 ) : null }
455508 { suffixPosition === 'after' ? suffix : null }
509+ { showClearButton && (
510+ < ClearButton
511+ size = { size }
512+ type = { validationState === 'invalid' ? 'clear' : 'neutral' }
513+ theme = { validationState === 'invalid' ? 'danger' : undefined }
514+ qa = "ComboBoxClearButton"
515+ data-no-trigger = { hideTrigger ? '' : undefined }
516+ onPress = { clearValue }
517+ />
518+ ) }
456519 { ! hideTrigger ? (
457520 < TriggerElement
458521 data-popover-trigger
0 commit comments