7
7
*/
8
8
9
9
import { Platform } from '@angular/cdk/platform' ;
10
+ import { DOCUMENT } from '@angular/common' ;
10
11
import {
11
12
AfterContentInit ,
13
+ AfterViewInit ,
14
+ ContentChildren ,
12
15
Directive ,
13
16
ElementRef ,
14
- forwardRef ,
15
17
HostBinding ,
18
+ HostListener ,
19
+ Inject ,
16
20
NgZone ,
17
21
OnDestroy ,
18
- QueryList ,
19
- ViewChildren
22
+ QueryList
20
23
} from '@angular/core' ;
21
24
import { RippleConfig , RippleRenderer , RippleTarget , setLines } from '@angular/material/core' ;
22
- import { MDCListAdapter } from '@material/list' ;
25
+ import { MDCListAdapter , MDCListFoundation } from '@material/list' ;
23
26
import { Subscription } from 'rxjs' ;
24
27
import { startWith } from 'rxjs/operators' ;
25
28
@@ -31,60 +34,12 @@ function toggleClass(el: Element, className: string, on: boolean) {
31
34
}
32
35
}
33
36
34
- @Directive ( )
35
- /** @docs -private */
36
- export abstract class MatListBase {
37
- // @HostBinding is used in the class as it is expected to be extended. Since @Component decorator
38
- // metadata is not inherited by child classes, instead the host binding data is defined in a way
39
- // that can be inherited.
40
- // tslint:disable-next-line:no-host-decorator-in-concrete
41
- @HostBinding ( 'class.mdc-list--non-interactive' )
42
- _isNonInteractive : boolean = false ;
43
-
44
- @ViewChildren ( forwardRef ( ( ) => MatListItemBase ) ) _items : QueryList < MatListItemBase > ;
45
-
46
- protected adapter : MDCListAdapter = {
47
- getListItemCount : ( ) => this . _items . length ,
48
- listItemAtIndexHasClass :
49
- ( index , className ) => this . _itemAtIndex ( index ) . _element . classList . contains ( className ) ,
50
- addClassForElementIndex :
51
- ( index , className ) => this . _itemAtIndex ( index ) . _element . classList . add ( className ) ,
52
- removeClassForElementIndex :
53
- ( index , className ) => this . _itemAtIndex ( index ) . _element . classList . remove ( className ) ,
54
- getAttributeForElementIndex :
55
- ( index , attr ) => this . _itemAtIndex ( index ) . _element . getAttribute ( attr ) ,
56
- setAttributeForElementIndex :
57
- ( index , attr , value ) => this . _itemAtIndex ( index ) . _element . setAttribute ( attr , value ) ,
58
- setTabIndexForListItemChildren :
59
- ( index , value ) => this . _itemAtIndex ( index ) . _element . tabIndex = value as unknown as number ,
60
- getFocusedElementIndex :
61
- ( ) => this . _items . map ( i => i . _element ) . findIndex ( e => e === this . doc ?. activeELement ) ,
62
- isFocusInsideList : ( ) => this . element . nativeElement . contains ( this . doc ?. activeElement ) ,
63
- isRootFocused : ( ) => this . element . nativeElement === this . doc ?. activeElement ,
64
- focusItemAtIndex : index => this . _itemAtIndex ( index ) . _element . focus ( ) ,
65
-
66
- // The following methods have a dummy implementation in the base class because they are only
67
- // applicable to certain types of lists
68
- hasCheckboxAtIndex : ( ) => false ,
69
- hasRadioAtIndex : ( ) => false ,
70
- setCheckedCheckboxOrRadioAtIndex : ( ) => { } ,
71
- isCheckboxCheckedAtIndex : ( ) => false ,
72
- notifyAction : ( ) => { } ,
73
-
74
- // TODO(mmalerba): Determine if we need to implement this.
75
- getPrimaryTextAtIndex : ( ) => '' ,
76
- } ;
77
-
78
- constructor ( protected element : ElementRef < HTMLElement > , protected doc : any ) { }
79
-
80
- private _itemAtIndex ( index : number ) : MatListItemBase {
81
- return this . _items . toArray ( ) [ index ] ;
82
- }
83
- }
84
-
85
37
@Directive ( )
86
38
/** @docs -private */
87
39
export abstract class MatListItemBase implements AfterContentInit , OnDestroy , RippleTarget {
40
+ @HostBinding ( 'tabindex' )
41
+ _tabIndex = - 1 ;
42
+
88
43
lines : QueryList < ElementRef < Element > > ;
89
44
90
45
rippleConfig : RippleConfig = { } ;
@@ -98,8 +53,8 @@ export abstract class MatListItemBase implements AfterContentInit, OnDestroy, Ri
98
53
99
54
private _rippleRenderer : RippleRenderer ;
100
55
101
- constructor ( private _elementRef : ElementRef < HTMLElement > , protected _ngZone : NgZone ,
102
- listBase : MatListBase , platform : Platform ) {
56
+ protected constructor ( private _elementRef : ElementRef < HTMLElement > , protected _ngZone : NgZone ,
57
+ listBase : MatListBase , platform : Platform ) {
103
58
this . _element = this . _elementRef . nativeElement ;
104
59
this . rippleDisabled = listBase . _isNonInteractive ;
105
60
if ( ! listBase . _isNonInteractive ) {
@@ -139,3 +94,90 @@ export abstract class MatListItemBase implements AfterContentInit, OnDestroy, Ri
139
94
this . _rippleRenderer . _removeTriggerEvents ( ) ;
140
95
}
141
96
}
97
+
98
+ @Directive ( )
99
+ /** @docs -private */
100
+ export abstract class MatListBase implements AfterViewInit , OnDestroy {
101
+ @HostBinding ( 'class.mdc-list--non-interactive' )
102
+ _isNonInteractive : boolean = false ;
103
+
104
+ @HostListener ( 'keydown' , [ '$event' ] )
105
+ _handleKeydown ( event : KeyboardEvent ) {
106
+ const index = this . _indexForElement ( event . target as HTMLElement ) ;
107
+ this . _foundation . handleKeydown (
108
+ event , this . _itemAtIndex ( index ) . _element === event . target , index ) ;
109
+ }
110
+
111
+ @HostListener ( 'click' , [ '$event' ] )
112
+ _handleClick ( event : MouseEvent ) {
113
+ this . _foundation . handleClick ( this . _indexForElement ( event . target as HTMLElement ) , false ) ;
114
+ }
115
+
116
+ @HostListener ( 'focusin' , [ '$event' ] )
117
+ _handleFocusin ( event : FocusEvent ) {
118
+ this . _foundation . handleFocusIn ( event , this . _indexForElement ( event . target as HTMLElement ) ) ;
119
+ }
120
+
121
+ @HostListener ( 'focusout' , [ '$event' ] )
122
+ _handleFocusout ( event : FocusEvent ) {
123
+ this . _foundation . handleFocusOut ( event , this . _indexForElement ( event . target as HTMLElement ) ) ;
124
+ }
125
+
126
+ @ContentChildren ( MatListItemBase ) _items : QueryList < MatListItemBase > ;
127
+
128
+ protected _adapter : MDCListAdapter = {
129
+ getListItemCount : ( ) => this . _items . length ,
130
+ listItemAtIndexHasClass :
131
+ ( index , className ) => this . _itemAtIndex ( index ) . _element . classList . contains ( className ) ,
132
+ addClassForElementIndex :
133
+ ( index , className ) => this . _itemAtIndex ( index ) . _element . classList . add ( className ) ,
134
+ removeClassForElementIndex :
135
+ ( index , className ) => this . _itemAtIndex ( index ) . _element . classList . remove ( className ) ,
136
+ getAttributeForElementIndex :
137
+ ( index , attr ) => this . _itemAtIndex ( index ) . _element . getAttribute ( attr ) ,
138
+ setAttributeForElementIndex :
139
+ ( index , attr , value ) => this . _itemAtIndex ( index ) . _element . setAttribute ( attr , value ) ,
140
+ setTabIndexForListItemChildren :
141
+ ( index , value ) => this . _itemAtIndex ( index ) . _element . tabIndex = value as unknown as number ,
142
+ getFocusedElementIndex : ( ) => this . _indexForElement ( this . _document ?. activeELement ) ,
143
+ isFocusInsideList : ( ) => this . _element . nativeElement . contains ( this . _document ?. activeElement ) ,
144
+ isRootFocused : ( ) => this . _element . nativeElement === this . _document ?. activeElement ,
145
+ focusItemAtIndex : index => this . _itemAtIndex ( index ) . _element . focus ( ) ,
146
+
147
+ // The following methods have a dummy implementation in the base class because they are only
148
+ // applicable to certain types of lists. They should be implemented for the concrete classes
149
+ // where they are applicable.
150
+ hasCheckboxAtIndex : ( ) => false ,
151
+ hasRadioAtIndex : ( ) => false ,
152
+ setCheckedCheckboxOrRadioAtIndex : ( ) => { } ,
153
+ isCheckboxCheckedAtIndex : ( ) => false ,
154
+ notifyAction : ( ) => { } ,
155
+
156
+ // TODO(mmalerba): Determine if we need to implement this.
157
+ getPrimaryTextAtIndex : ( ) => '' ,
158
+ } ;
159
+
160
+ protected _foundation : MDCListFoundation ;
161
+
162
+ constructor ( protected _element : ElementRef < HTMLElement > ,
163
+ @Inject ( DOCUMENT ) protected _document : any ) {
164
+ this . _foundation = new MDCListFoundation ( this . _adapter ) ;
165
+ }
166
+
167
+ ngAfterViewInit ( ) {
168
+ this . _foundation . init ( ) ;
169
+ this . _foundation . layout ( ) ;
170
+ }
171
+
172
+ ngOnDestroy ( ) {
173
+ this . _foundation . destroy ( ) ;
174
+ }
175
+
176
+ private _itemAtIndex ( index : number ) : MatListItemBase {
177
+ return this . _items . toArray ( ) [ index ] ;
178
+ }
179
+
180
+ private _indexForElement ( element : HTMLElement | null ) {
181
+ return element ? this . _items . toArray ( ) . findIndex ( i => i . _element . contains ( element ) ) : - 1 ;
182
+ }
183
+ }
0 commit comments