-
Notifications
You must be signed in to change notification settings - Fork 4k
Expand file tree
/
Copy pathListItem.js
More file actions
187 lines (164 loc) · 4.65 KB
/
ListItem.js
File metadata and controls
187 lines (164 loc) · 4.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
import cx from 'clsx'
import _ from 'lodash'
import PropTypes from 'prop-types'
import React, { isValidElement } from 'react'
import {
childrenUtils,
createShorthandFactory,
customPropTypes,
getElementType,
getUnhandledProps,
useKeyOnly,
useEventCallback,
} from '../../lib'
import Image from '../Image'
import ListContent from './ListContent'
import ListDescription from './ListDescription'
import ListHeader from './ListHeader'
import ListIcon from './ListIcon'
/**
* A list item can contain a set of items.
*/
const ListItem = React.forwardRef(function (props, ref) {
const {
active,
children,
className,
content,
description,
disabled,
header,
icon,
image,
value,
} = props
const ElementType = getElementType(ListItem, props)
const classes = cx(
useKeyOnly(active, 'active'),
useKeyOnly(disabled, 'disabled'),
useKeyOnly(ElementType !== 'li', 'item'),
className,
)
const rest = getUnhandledProps(ListItem, props)
const handleClick = useEventCallback((e) => {
if (!disabled) {
props.onClick?.(e, props)
}
})
const valueProp = ElementType === 'li' ? { value } : { 'data-value': value }
if (!childrenUtils.isNil(children)) {
return (
<ElementType
{...valueProp}
role='listitem'
{...rest}
className={classes}
onClick={handleClick}
ref={ref}
>
{children}
</ElementType>
)
}
const iconElement = ListIcon.create(icon, { autoGenerateKey: false })
const imageElement = Image.create(image, { autoGenerateKey: false })
// See description of `content` prop for explanation about why this is necessary.
if (!isValidElement(content) && _.isPlainObject(content)) {
return (
<ElementType
{...valueProp}
role='listitem'
{...rest}
className={classes}
onClick={handleClick}
ref={ref}
>
{iconElement || imageElement}
{ListContent.create(content, {
autoGenerateKey: false,
defaultProps: { header, description },
})}
</ElementType>
)
}
const headerElement = ListHeader.create(header, { autoGenerateKey: false })
const descriptionElement = ListDescription.create(description, { autoGenerateKey: false })
if (iconElement || imageElement) {
return (
<ElementType
{...valueProp}
role='listitem'
{...rest}
className={classes}
onClick={handleClick}
ref={ref}
>
{iconElement || imageElement}
{(content || headerElement || descriptionElement) && (
<ListContent>
{headerElement}
{descriptionElement}
{content}
</ListContent>
)}
</ElementType>
)
}
return (
<ElementType
{...valueProp}
role='listitem'
{...rest}
className={classes}
onClick={handleClick}
ref={ref}
>
{headerElement}
{descriptionElement}
{content}
</ElementType>
)
})
ListItem.displayName = 'ListItem'
ListItem.propTypes = {
/** An element type to render as (string or function). */
as: PropTypes.elementType,
/** A list item can active. */
active: PropTypes.bool,
/** Primary content. */
children: PropTypes.node,
/** Additional classes. */
className: PropTypes.string,
/**
* Shorthand for primary content.
*
* Heads up!
*
* This is handled slightly differently than the typical `content` prop since
* the wrapping ListContent is not used when there's no icon or image.
*
* If you pass content as:
* - an element/literal, it's treated as the sibling node to
* header/description (whether wrapped in Item.Content or not).
* - a props object, it forces the presence of Item.Content and passes those
* props to it. If you pass a content prop within that props object, it
* will be treated as the sibling node to header/description.
*/
content: customPropTypes.itemShorthand,
/** Shorthand for ListDescription. */
description: customPropTypes.itemShorthand,
/** A list item can disabled. */
disabled: PropTypes.bool,
/** Shorthand for ListHeader. */
header: customPropTypes.itemShorthand,
/** Shorthand for ListIcon. */
icon: customPropTypes.every([customPropTypes.disallow(['image']), customPropTypes.itemShorthand]),
/** Shorthand for Image. */
image: customPropTypes.every([customPropTypes.disallow(['icon']), customPropTypes.itemShorthand]),
/** A ListItem can be clicked */
onClick: PropTypes.func,
/** A value for an ordered list. */
value: PropTypes.string,
}
ListItem.create = createShorthandFactory(ListItem, (content) => ({ content }))
export default ListItem