/
ListItemChildren.tsx
173 lines (155 loc) · 5.05 KB
/
ListItemChildren.tsx
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
import type { ReactElement, ReactNode } from "react";
import type { ListItemAddonPosition, ListItemAddonType } from "./ListItemAddon";
import { ListItemAddon } from "./ListItemAddon";
import { ListItemText } from "./ListItemText";
export interface ListItemChildrenProps {
/**
* The main content to display. When the `textChildren` prop is enabled and
* there is child content, it will be treated as primary text and update the
* styles automatically.
*/
children?: ReactNode;
/**
* An optional className to apply to the `<span>` that surrounds the
* `primaryText` and optionally `secondaryText` within the list item.
*/
textClassName?: string;
/**
* An optional className to apply to the `<span>` that surrounds the
* `secondaryText` within the list item.
*/
secondaryTextClassName?: string;
/**
* Boolean if the children should be treated as the `primaryText` prop. This
* will wrap them in an additional class so that they have ellipsis for text
* overflow.
*
* If you want to have more "freedom" within the `ListItem`, you can disable
* this prop so that the height will grow depending on content.
*
* NOTE: If the `secondaryText` prop is provided, this will always be
* considered `true`.
*/
textChildren?: boolean;
/**
* An optional element that should be rendered as the `primaryText` within the
* list item. It is most likely easier to use the `children` prop instead, but
* this allows you to create more complex components with the `ListItem` since
* you can provided `children` and have the styles for the `primaryText` still
* applied. By default, this will only allow one line of text and add ellipsis
* for any text overflow.
*/
primaryText?: ReactNode;
/**
* An optional element that should be rendered as the `secondaryText` within
* the list item. By default, this will only span one line and add ellipsis
* for overflow.
*/
secondaryText?: ReactNode;
/**
* An optional addon to display to the left of the `primaryText` or
* `children` and should be used with the `leftAddonType` prop to adjust
* spacing.
*/
leftAddon?: ReactNode;
/**
* The type of the addon that appears to the left of the `primaryText` or
* `children`.
*/
leftAddonType?: ListItemAddonType;
/**
* The vertical position the left icon, avatar, media, or large media
* should be placed.
*/
leftAddonPosition?: ListItemAddonPosition;
/**
* An optional addon to display to the right of the `primaryText` or
* `children` and should be used with the `rightAddonType` prop to adjust
* spacing.
*/
rightAddon?: ReactNode;
/**
* The type of the addon that appears to the right of the `primaryText` or
* `children`.
*/
rightAddonType?: ListItemAddonType;
/**
* The vertical position the right icon, avatar, media, or large media
* should be placed.
*/
rightAddonPosition?: ListItemAddonPosition;
/**
* Boolean if the left and/or right addons should be "forcefully" wrapped in a
* `<span>` with the spacing class names applied instead of attempting to
* clone it into the provided icon element.
*/
forceAddonWrap?: boolean;
}
/**
* The `ListItemChildren` component is used to create a styled list item that
* can have optional addons to the left or right of the children in the form of
* icons, avatars, or media. The `children` can be replaced by the `primaryText`
* and `secondaryText` props to create stacked text spanning two or more lines
* with the default behavior of using `line-clamp` at three lines.
*
* Note: This will return a `React.Fragment` of the children and does not wrap
* in a DOM node for styling. The parent component should normally have
* `display: flex` for the styling to work.
*/
export function ListItemChildren({
textClassName,
secondaryTextClassName,
textChildren,
primaryText,
secondaryText,
leftAddon,
leftAddonType = "icon",
leftAddonPosition = "middle",
rightAddon,
rightAddonType = "icon",
rightAddonPosition = "middle",
forceAddonWrap,
children: propChildren,
}: ListItemChildrenProps): ReactElement {
const stringifiedChildren =
typeof propChildren === "number" ? `${propChildren}` : propChildren;
let children = stringifiedChildren;
if (primaryText || secondaryText || textChildren) {
children = (
<ListItemText
className={textClassName}
secondaryText={secondaryText}
secondaryTextClassName={secondaryTextClassName}
>
{(textChildren && children) || primaryText}
</ListItemText>
);
}
children = (
<ListItemAddon
addon={leftAddon}
type={leftAddonType}
position={leftAddonPosition}
forceAddonWrap={forceAddonWrap}
>
{children}
</ListItemAddon>
);
children = (
<ListItemAddon
addon={rightAddon}
addonAfter
type={rightAddonType}
position={rightAddonPosition}
forceAddonWrap={forceAddonWrap}
>
{children}
</ListItemAddon>
);
return (
<>
{children}
{(primaryText && stringifiedChildren) || null}
</>
);
}