@@ -30,6 +30,15 @@ export interface ComboboxInputs<T extends ListItem<V>, V> {
3030
3131 /** The value of the first matching item in the popup. */
3232 firstMatch : SignalLike < V | undefined > ;
33+
34+ /** Whether the combobox is disabled. */
35+ disabled : SignalLike < boolean > ;
36+
37+ /** Whether the combobox is read-only. */
38+ readonly : SignalLike < boolean > ;
39+
40+ /** Whether the combobox is in a right-to-left context. */
41+ textDirection : SignalLike < 'rtl' | 'ltr' > ;
3342}
3443
3544/** An interface that allows combobox popups to expose the necessary controls for the combobox. */
@@ -119,10 +128,12 @@ export class ComboboxPattern<T extends ListItem<V>, V> {
119128 isFocused = signal ( false ) ;
120129
121130 /** The key used to navigate to the previous item in the list. */
122- expandKey = computed ( ( ) => 'ArrowRight' ) ; // TODO: RTL support.
131+ expandKey = computed ( ( ) => ( this . inputs . textDirection ( ) === 'rtl' ? 'ArrowLeft' : 'ArrowRight' ) ) ;
123132
124133 /** The key used to navigate to the next item in the list. */
125- collapseKey = computed ( ( ) => 'ArrowLeft' ) ; // TODO: RTL support.
134+ collapseKey = computed ( ( ) =>
135+ this . inputs . textDirection ( ) === 'rtl' ? 'ArrowRight' : 'ArrowLeft' ,
136+ ) ;
126137
127138 /** The ID of the popup associated with the combobox. */
128139 popupId = computed ( ( ) => this . inputs . popupControls ( ) ?. id ( ) || null ) ;
@@ -133,6 +144,9 @@ export class ComboboxPattern<T extends ListItem<V>, V> {
133144 /** The ARIA role of the popup associated with the combobox. */
134145 hasPopup = computed ( ( ) => this . inputs . popupControls ( ) ?. role ( ) || null ) ;
135146
147+ /** Whether the combobox is interactive. */
148+ isInteractive = computed ( ( ) => ! this . inputs . disabled ( ) && ! this . inputs . readonly ( ) ) ;
149+
136150 /** The keydown event manager for the combobox. */
137151 keydown = computed ( ( ) => {
138152 if ( ! this . expanded ( ) ) {
@@ -204,16 +218,24 @@ export class ComboboxPattern<T extends ListItem<V>, V> {
204218
205219 /** Handles keydown events for the combobox. */
206220 onKeydown ( event : KeyboardEvent ) {
207- this . keydown ( ) . handle ( event ) ;
221+ if ( this . isInteractive ( ) ) {
222+ this . keydown ( ) . handle ( event ) ;
223+ }
208224 }
209225
210226 /** Handles pointerup events for the combobox. */
211227 onPointerup ( event : PointerEvent ) {
212- this . pointerup ( ) . handle ( event ) ;
228+ if ( this . isInteractive ( ) ) {
229+ this . pointerup ( ) . handle ( event ) ;
230+ }
213231 }
214232
215233 /** Handles input events for the combobox. */
216234 onInput ( event : Event ) {
235+ if ( ! this . isInteractive ( ) ) {
236+ return ;
237+ }
238+
217239 const inputEl = this . inputs . inputEl ( ) ;
218240
219241 if ( ! inputEl ) {
@@ -233,12 +255,17 @@ export class ComboboxPattern<T extends ListItem<V>, V> {
233255 }
234256 }
235257
258+ /** Handles focus in events for the combobox. */
236259 onFocusIn ( ) {
237260 this . isFocused . set ( true ) ;
238261 }
239262
240263 /** Handles focus out events for the combobox. */
241264 onFocusOut ( event : FocusEvent ) {
265+ if ( this . inputs . disabled ( ) || this . inputs . readonly ( ) ) {
266+ return ;
267+ }
268+
242269 if (
243270 ! ( event . relatedTarget instanceof HTMLElement ) ||
244271 ! this . inputs . containerEl ( ) ?. contains ( event . relatedTarget )
@@ -261,6 +288,7 @@ export class ComboboxPattern<T extends ListItem<V>, V> {
261288 }
262289 }
263290
291+ /** The first matching item in the combobox. */
264292 firstMatch = computed ( ( ) => {
265293 // TODO(wagnermaciel): Consider whether we should not provide this default behavior for the
266294 // listbox. Instead, we may want to allow users to have no match so that typing does not focus
@@ -275,6 +303,7 @@ export class ComboboxPattern<T extends ListItem<V>, V> {
275303 . find ( i => i . value ( ) === this . inputs . firstMatch ( ) ) ;
276304 } ) ;
277305
306+ /** Handles filtering logic for the combobox. */
278307 onFilter ( ) {
279308 // TODO(wagnermaciel)
280309 // When the user first interacts with the combobox, the popup will lazily render for the first
@@ -315,6 +344,7 @@ export class ComboboxPattern<T extends ListItem<V>, V> {
315344 }
316345 }
317346
347+ /** Highlights the currently selected item in the combobox. */
318348 highlight ( ) {
319349 const inputEl = this . inputs . inputEl ( ) ;
320350 const item = this . inputs . popupControls ( ) ?. getSelectedItem ( ) ;
@@ -374,11 +404,13 @@ export class ComboboxPattern<T extends ListItem<V>, V> {
374404 this . _navigate ( ( ) => this . inputs . popupControls ( ) ?. last ( ) ) ;
375405 }
376406
407+ /** Collapses the currently focused item in the combobox. */
377408 collapseItem ( ) {
378409 const controls = this . inputs . popupControls ( ) as ComboboxTreeControls < T , V > ;
379410 this . _navigate ( ( ) => controls ?. collapseItem ( ) ) ;
380411 }
381412
413+ /** Expands the currently focused item in the combobox. */
382414 expandItem ( ) {
383415 const controls = this . inputs . popupControls ( ) as ComboboxTreeControls < T , V > ;
384416 this . _navigate ( ( ) => controls ?. expandItem ( ) ) ;
0 commit comments