1
- import { ENTER , SPACE } from '@angular/cdk/keycodes' ;
1
+ import { ENTER , ESCAPE , F2 , F7 , MAC_ENTER , SPACE } from '@angular/cdk/keycodes' ;
2
2
import {
3
3
AfterViewInit ,
4
4
ChangeDetectionStrategy ,
@@ -14,11 +14,12 @@ import {
14
14
Renderer2 ,
15
15
ViewChild ,
16
16
ViewEncapsulation ,
17
- booleanAttribute
17
+ booleanAttribute ,
18
+ signal
18
19
} from '@angular/core' ;
19
20
import { Subscription } from 'rxjs' ;
20
21
21
- import { KeyUtil , Nullable } from '@fundamental-ngx/cdk/utils' ;
22
+ import { KeyUtil , Nullable , TabbableElementService } from '@fundamental-ngx/cdk/utils' ;
22
23
23
24
import { NgTemplateOutlet } from '@angular/common' ;
24
25
import { FormsModule } from '@angular/forms' ;
@@ -35,7 +36,11 @@ import { TitleComponent } from '@fundamental-ngx/core/title';
35
36
import { FdTranslatePipe } from '@fundamental-ngx/i18n' ;
36
37
import { GridListItemBodyDirective } from '../../directives/grid-list-item-body.directive' ;
37
38
import { parseLayoutPattern } from '../../helpers/parse-layout-pattern' ;
38
- import { GridListSelectionActions , GridListSelectionMode } from '../../models/grid-list-selection.models' ;
39
+ import {
40
+ GridListSelectionActions ,
41
+ GridListSelectionMode ,
42
+ GridListSelectionModeEnum
43
+ } from '../../models/grid-list-selection.models' ;
39
44
import { GridListItemFooterBarComponent } from '../grid-list-item-footer-bar/grid-list-item-footer-bar.component' ;
40
45
import { GridListItemToolbarComponent } from '../grid-list-item-toolbar/grid-list-item-toolbar.component' ;
41
46
import { GridListTitleBarSpacerComponent } from '../grid-list-title-bar-spacer/grid-list-title-bar-spacer.component' ;
@@ -144,6 +149,7 @@ export class GridListItemComponent<T> implements AfterViewInit, OnDestroy {
144
149
set type ( value : Nullable < GridListItemType > ) {
145
150
this . _type = value ?? 'inactive' ;
146
151
}
152
+
147
153
get type ( ) : GridListItemType {
148
154
return this . _type ;
149
155
}
@@ -263,12 +269,13 @@ export class GridListItemComponent<T> implements AfterViewInit, OnDestroy {
263
269
set selectionMode ( mode : GridListSelectionMode | undefined ) {
264
270
this . _selectionMode = mode ;
265
271
266
- if ( mode !== 'delete' && mode !== 'none' && ! this . value ) {
272
+ if ( mode !== GridListSelectionModeEnum . DELETE && mode !== GridListSelectionModeEnum . NONE && ! this . value ) {
267
273
throw new Error ( 'Grid List Item must have [value] attribute.' ) ;
268
274
}
269
275
270
276
if ( this . selected && this . value && this . _index != null ) {
271
- const action = this . selectionMode !== 'multiSelect' ? null : GridListSelectionActions . ADD ;
277
+ const action =
278
+ this . selectionMode !== GridListSelectionModeEnum . MULTI_SELECT ? null : GridListSelectionActions . ADD ;
272
279
273
280
this . _gridList . setSelectedItem ( this . value , this . _index , action ) ;
274
281
}
@@ -279,9 +286,13 @@ export class GridListItemComponent<T> implements AfterViewInit, OnDestroy {
279
286
get selectionMode ( ) : GridListSelectionMode | undefined {
280
287
return this . _selectionMode ;
281
288
}
289
+
282
290
/** @hidden */
283
291
_index ?: number ;
284
292
293
+ /** tabIndex of the element */
294
+ tabIndex = signal ( - 1 ) ;
295
+
285
296
/** @hidden */
286
297
private _type : GridListItemType = 'inactive' ;
287
298
@@ -294,13 +305,17 @@ export class GridListItemComponent<T> implements AfterViewInit, OnDestroy {
294
305
/** @hidden */
295
306
private readonly subscription = new Subscription ( ) ;
296
307
308
+ /** @hidden */
309
+ private _innerElementFocused = signal < boolean > ( false ) ;
310
+
297
311
/** @hidden */
298
312
constructor (
299
313
private readonly _cd : ChangeDetectorRef ,
300
314
private readonly _elementRef : ElementRef ,
301
315
private readonly _render : Renderer2 ,
302
316
private readonly _gridList : GridList < T > ,
303
- readonly _contentDensityObserver : ContentDensityObserver
317
+ readonly _contentDensityObserver : ContentDensityObserver ,
318
+ private readonly _tabbableElementService : TabbableElementService
304
319
) {
305
320
const selectedItemsSub = this . _gridList . _selectedItems$ . subscribe ( ( items ) => {
306
321
this . _selectedItem = items . selection . find ( ( item ) => item === this . value ) ;
@@ -361,7 +376,7 @@ export class GridListItemComponent<T> implements AfterViewInit, OnDestroy {
361
376
return ;
362
377
}
363
378
const action =
364
- this . selectionMode !== 'multiSelect'
379
+ this . selectionMode !== GridListSelectionModeEnum . MULTI_SELECT
365
380
? null
366
381
: value || value === 0
367
382
? GridListSelectionActions . ADD
@@ -422,24 +437,49 @@ export class GridListItemComponent<T> implements AfterViewInit, OnDestroy {
422
437
423
438
this . press . emit ( this . _outputEventValue ) ;
424
439
}
425
-
426
440
/** @hidden */
427
441
_onKeyDown ( event : KeyboardEvent ) : void {
442
+ const activeElement = document . activeElement as HTMLElement ;
443
+ const isFocused = activeElement === this . _gridListItem . nativeElement ;
444
+ const shouldFocusChild = KeyUtil . isKeyCode ( event , [ ENTER , MAC_ENTER , F2 , F7 ] ) && ! event . shiftKey && isFocused ;
445
+ if ( shouldFocusChild ) {
446
+ event . stopPropagation ( ) ;
447
+ const interactiveElements = this . _gridListItem . nativeElement . querySelectorAll (
448
+ 'a, button, input, select, textarea'
449
+ ) ;
450
+
451
+ const firstInteractiveElement = interactiveElements [ 0 ] as HTMLElement ;
452
+ firstInteractiveElement . focus ( ) ;
453
+ interactiveElements . forEach ( ( element ) => {
454
+ element . setAttribute ( 'tabindex' , '0' ) ;
455
+ } ) ;
456
+ this . _innerElementFocused . set ( true ) ;
457
+ return ;
458
+ } else if ( this . _innerElementFocused ( ) && KeyUtil . isKeyCode ( event , [ F2 , F7 , ESCAPE ] ) ) {
459
+ event . stopPropagation ( ) ;
460
+ this . _gridListItem . nativeElement . focus ( ) ;
461
+ this . _innerElementFocused . set ( false ) ;
462
+ return ;
463
+ }
428
464
const target = event . target as HTMLDivElement ;
429
465
430
466
const isSelectionKeyDown = KeyUtil . isKeyCode ( event , [ ENTER , SPACE ] ) ;
431
467
432
- if ( isSelectionKeyDown && this . selectionMode === 'none' ) {
468
+ if ( isSelectionKeyDown && this . selectionMode === GridListSelectionModeEnum . NONE ) {
433
469
this . press . emit ( this . _outputEventValue ) ;
434
470
}
435
471
436
- if ( ! isSelectionKeyDown || this . selectionMode === 'none' || ! target . classList . contains ( 'fd-grid-list__item' ) ) {
472
+ if (
473
+ ! isSelectionKeyDown ||
474
+ this . selectionMode === GridListSelectionModeEnum . NONE ||
475
+ ! target . classList . contains ( 'fd-grid-list__item' )
476
+ ) {
437
477
return ;
438
478
}
439
479
440
480
this . _preventDefault ( event ) ;
441
481
442
- if ( this . selectionMode === 'multiSelect' ) {
482
+ if ( this . selectionMode === GridListSelectionModeEnum . MULTI_SELECT ) {
443
483
this . _selectionItem ( ! this . _selectedItem ) ;
444
484
445
485
return ;
0 commit comments