23
23
import React from 'react' ;
24
24
import classnames from 'classnames' ;
25
25
import { MDCListFoundation } from '@material/list/foundation' ;
26
+ import { ListItemContext , ListItemContextShape } from './index' ;
27
+ import { closest } from '@material/dom/ponyfill' ;
26
28
27
- export interface ListItemProps < T > extends React . HTMLProps < T > {
29
+ export interface ListItemProps < T extends HTMLElement = HTMLElement > extends React . HTMLProps < T > , ListItemContextShape {
28
30
checkboxList ?: boolean ;
29
31
radioList ?: boolean ;
30
- onKeyDown ?: React . KeyboardEventHandler < T > ;
31
- onClick ?: React . MouseEventHandler < T > ;
32
- onFocus ?: React . FocusEventHandler < T > ;
33
- onBlur ?: React . FocusEventHandler < T > ;
34
32
tag ?: string ;
35
33
activated ?: boolean ;
36
34
selected ?: boolean ;
37
- onDestroy ?: ( ) => void ;
35
+ ref ?: React . Ref < any > ;
36
+ } ;
37
+
38
+ export interface ListItemState {
39
+ tabIndex ?: number ;
38
40
}
39
41
40
- export default class ListItem < T extends HTMLElement = HTMLElement > extends React . Component <
41
- ListItemProps < T > ,
42
- { }
43
- > {
42
+ export class ListItemBase < T extends HTMLElement = HTMLElement > extends React . Component <
43
+ ListItemProps < T > , ListItemState > {
44
44
private listItemElement = React . createRef < T > ( ) ;
45
45
46
46
static defaultProps : Partial < ListItemProps < HTMLElement > > = {
@@ -54,15 +54,54 @@ export default class ListItem<T extends HTMLElement = HTMLElement> extends React
54
54
onBlur : ( ) => { } ,
55
55
onDestroy : ( ) => { } ,
56
56
tag : 'li' ,
57
+ handleClick : ( ) => { } ,
58
+ handleKeyDown : ( ) => { } ,
59
+ handleBlur : ( ) => { } ,
60
+ handleFocus : ( ) => { } ,
61
+ getListItemInitialTabIndex : ( ) => - 1 ,
62
+ getClassNamesFromList : ( ) => ( { } ) ,
63
+ } ;
64
+
65
+ state = {
66
+ tabIndex : this . props . tabIndex ,
57
67
} ;
58
68
69
+ get listElements ( ) : Element [ ] {
70
+ if ( this . listItemElement . current ) {
71
+ const listElement = closest ( this . listItemElement . current , `.${ MDCListFoundation . cssClasses . ROOT } ` ) ;
72
+ if ( ! listElement ) return [ ] ;
73
+ return [ ] . slice . call (
74
+ listElement . querySelectorAll ( MDCListFoundation . strings . ENABLED_ITEMS_SELECTOR )
75
+ ) ;
76
+ }
77
+ return [ ] ;
78
+ }
79
+
80
+ componentDidMount ( ) {
81
+ this . initializeTabIndex ( ) ;
82
+ }
83
+
84
+ componentDidUpdate ( prevProps : ListItemProps ) {
85
+ if ( prevProps . tabIndex !== this . props . tabIndex ) {
86
+ this . setState ( { tabIndex : this . props . tabIndex } ) ;
87
+ }
88
+ }
89
+
59
90
componentWillUnmount ( ) {
60
- this . props . onDestroy ! ( ) ;
91
+ if ( this . listItemElement . current ) {
92
+ const index = this . getIndex ( this . listItemElement . current ) ;
93
+ this . props . onDestroy ! ( index ) ;
94
+ }
61
95
}
62
96
63
97
get classes ( ) {
64
- const { className, activated, disabled, selected} = this . props ;
65
- return classnames ( 'mdc-list-item' , className , {
98
+ const { className, activated, disabled, selected, getClassNamesFromList} = this . props ;
99
+ let classesFromList = [ '' ] ;
100
+ if ( this . listItemElement . current ) {
101
+ const index = this . getIndex ( this . listItemElement . current ) ;
102
+ classesFromList = getClassNamesFromList ! ( ) [ index ] ;
103
+ }
104
+ return classnames ( 'mdc-list-item' , className , classesFromList , {
66
105
[ MDCListFoundation . cssClasses . LIST_ITEM_ACTIVATED_CLASS ] : activated ,
67
106
[ MDCListFoundation . cssClasses . LIST_ITEM_SELECTED_CLASS ] : selected ,
68
107
'mdc-list-item--disabled' : disabled ,
@@ -81,6 +120,42 @@ export default class ListItem<T extends HTMLElement = HTMLElement> extends React
81
120
return null ;
82
121
}
83
122
123
+ private initializeTabIndex = ( ) => {
124
+ if ( this . listItemElement . current ) {
125
+ const index = this . getIndex ( this . listItemElement . current ) ;
126
+ const tabIndex = this . props . getListItemInitialTabIndex ! ( index ) ;
127
+ this . setState ( { tabIndex} ) ;
128
+ }
129
+ }
130
+
131
+ getIndex = ( listElement : Element ) => {
132
+ return this . listElements . indexOf ( listElement ) ;
133
+ }
134
+
135
+ handleClick = ( e : React . MouseEvent < any > ) => {
136
+ const { onClick} = this . props ;
137
+ onClick ! ( e ) ;
138
+ this . props . handleClick ! ( e , this . getIndex ( e . currentTarget ) ) ;
139
+ }
140
+
141
+ handleKeyDown = ( e : React . KeyboardEvent < any > ) => {
142
+ const { onKeyDown} = this . props ;
143
+ onKeyDown ! ( e ) ;
144
+ this . props . handleKeyDown ! ( e , this . getIndex ( e . currentTarget ) ) ;
145
+ }
146
+
147
+ handleFocus = ( e : React . FocusEvent < any > ) => {
148
+ const { onFocus} = this . props ;
149
+ onFocus ! ( e ) ;
150
+ this . props . handleFocus ! ( e , this . getIndex ( e . currentTarget ) ) ;
151
+ }
152
+
153
+ handleBlur = ( e : React . FocusEvent < any > ) => {
154
+ const { onBlur} = this . props ;
155
+ onBlur ! ( e ) ;
156
+ this . props . handleBlur ! ( e , this . getIndex ( e . currentTarget ) ) ;
157
+ }
158
+
84
159
render ( ) {
85
160
const {
86
161
/* eslint-disable no-unused-vars */
@@ -90,21 +165,51 @@ export default class ListItem<T extends HTMLElement = HTMLElement> extends React
90
165
checkboxList,
91
166
radioList,
92
167
onDestroy,
168
+ onClick,
169
+ onKeyDown,
170
+ onFocus,
171
+ onBlur,
172
+ handleClick,
173
+ handleKeyDown,
174
+ handleFocus,
175
+ handleBlur,
176
+ getListItemInitialTabIndex,
177
+ getClassNamesFromList,
178
+ tabIndex,
93
179
/* eslint-enable no-unused-vars */
94
180
tag : Tag ,
95
181
...otherProps
96
182
} = this . props ;
183
+
97
184
return (
98
185
// https://github.com/Microsoft/TypeScript/issues/28892
99
186
// @ts -ignore
100
187
< Tag
188
+ { ...otherProps }
189
+ { ...this . context }
101
190
role = { this . role }
102
191
className = { this . classes }
103
192
ref = { this . listItemElement }
104
- { ...otherProps }
193
+ onClick = { this . handleClick }
194
+ onKeyDown = { this . handleKeyDown }
195
+ onFocus = { this . handleFocus }
196
+ onBlur = { this . handleBlur }
197
+ tabIndex = { this . state . tabIndex }
105
198
>
106
- { this . props . children }
199
+ { children }
107
200
</ Tag >
108
201
) ;
109
202
}
110
203
}
204
+
205
+ const ListItem : React . FunctionComponent < ListItemProps > = ( props ) => {
206
+ return (
207
+ < ListItemContext . Consumer >
208
+ { ( context ) => (
209
+ < ListItemBase { ...context } { ...props } />
210
+ ) }
211
+ </ ListItemContext . Consumer >
212
+ ) ;
213
+ } ;
214
+
215
+ export default ListItem ;
0 commit comments