Skip to content

Commit 97908e7

Browse files
committed
feat(list-group): implement ListGroup and StatefulListGroup
1 parent 32a4370 commit 97908e7

File tree

7 files changed

+213
-1
lines changed

7 files changed

+213
-1
lines changed

demo/ListGroupExamples.jsx

+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import React from 'react';
2+
3+
import { ListGroup, StatefulListGroup } from '../dist/main';
4+
5+
const items = [
6+
{
7+
title: 'Cras justo odio',
8+
description:
9+
'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Sequi doloremque itaque in tenetur quidem ab provident repellat vero commodi iste? Itaque sed mollitia dolores voluptatibus at tenetur totam aperiam incidunt.',
10+
archived: true,
11+
},
12+
{
13+
title: 'Morbi leo risus',
14+
description:
15+
'Magnam, amet assumenda fuga architecto cumque quod exercitationem temporibus facere aperiam officiis eaque odio sequi maxime dolores ullam earum, quidem voluptatibus quisquam qui dolor culpa illo. Saepe minus optio labore.',
16+
},
17+
{
18+
title: 'Porta ac consectetur ac',
19+
description:
20+
'Doloribus exercitationem vero, tenetur, ratione eius ipsum illo ipsa amet consequuntur adipisci perspiciatis labore aperiam mollitia molestias recusandae rerum dolorum alias dolores optio. Ea dignissimos nemo nostrum impedit quo sequi.',
21+
},
22+
];
23+
24+
export function ListGroupExamples() {
25+
return (
26+
<div className="row">
27+
<div className="col-6 mb-3">
28+
<h1 className="h4">Simple list group</h1>
29+
<ListGroup
30+
items={items}
31+
template={(item) => (
32+
<>
33+
<strong>{item.title}</strong> <br />
34+
{item.description}
35+
</>
36+
)}
37+
/>
38+
</div>
39+
<div className="col-6 mb-3">
40+
<h1 className="h4">Linked list group</h1>
41+
<ListGroup
42+
items={items}
43+
template={(item) => (
44+
<>
45+
<strong>{item.title}</strong> <br />
46+
{item.description}
47+
</>
48+
)}
49+
active={2}
50+
linked={true}
51+
isDisabled={(item) => item.archived}
52+
/>
53+
</div>
54+
<div className="col-6 mb-3">
55+
<h1 className="h4">No border list group</h1>
56+
<ListGroup
57+
items={items}
58+
template={(item) => (
59+
<>
60+
<strong>{item.title}</strong> <br />
61+
{item.description}
62+
</>
63+
)}
64+
active={2}
65+
bordered={false}
66+
/>
67+
</div>
68+
<div className="col-6 mb-3">
69+
<h1 className="h4">Horizontal list group</h1>
70+
<ListGroup
71+
items={items}
72+
template={(item) => (
73+
<>
74+
<strong>{item.title}</strong> <br />
75+
{item.description}
76+
</>
77+
)}
78+
active={1}
79+
horizontal={true}
80+
/>
81+
</div>
82+
<div className="col-6 mb-3">
83+
<h1 className="h4">Stateful list group</h1>
84+
<StatefulListGroup
85+
items={items}
86+
template={(item) => (
87+
<>
88+
<strong>{item.title}</strong> <br />
89+
{item.description}
90+
</>
91+
)}
92+
linked={true}
93+
/>
94+
</div>
95+
</div>
96+
);
97+
}

demo/demo.jsx

+6-1
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@ import { FormExamples } from './FormExamples';
66
import { TableExamples } from './TableExamples';
77
import { TabsExamples } from './TabsExamples';
88
import { DialogExamples } from './DialogExamples';
9+
import { ListGroupExamples } from './ListGroupExamples';
910

1011
ReactDOM.render(
1112
<div className="mt-3">
1213
<React.StrictMode>
1314
<StatefulTabs
1415
vertical={true}
15-
initialTab={0}
16+
initialTab={2}
1617
tabs={[
1718
{
1819
title: 'Dialog',
@@ -22,6 +23,10 @@ ReactDOM.render(
2223
title: 'Forms',
2324
content: <FormExamples />,
2425
},
26+
{
27+
title: 'List groups',
28+
content: <ListGroupExamples />,
29+
},
2530
{
2631
title: 'Pagination',
2732
content: <PaginationExamples />,

src/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export * from './dialog';
22
export * from './forms';
3+
export * from './list-group';
34
export * from './mixed';
45
export * from './table';
56
export * from './tabs';

src/list-group/ListGroup.jsx

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import { ListGroupItem } from './ListGroupItem';
4+
5+
export function ListGroup({ items, active, linked, bordered, horizontal, template, isDisabled, onSelect }) {
6+
const content = items.map((item, index) => (
7+
<ListGroupItem
8+
key={index}
9+
index={index}
10+
isActive={active === index}
11+
isDisabled={isDisabled(item)}
12+
item={item}
13+
linked={linked}
14+
onSelect={onSelect}
15+
>
16+
{template(item)}
17+
</ListGroupItem>
18+
));
19+
20+
const classes = ['list-group', !bordered && 'list-group-flush', horizontal && 'list-group-horizontal']
21+
.filter((v) => v)
22+
.join(' ');
23+
24+
if (linked) {
25+
return <div className={classes}>{content}</div>;
26+
}
27+
28+
return <ul className={classes}>{content}</ul>;
29+
}
30+
31+
ListGroup.defaultProps = {
32+
bordered: true,
33+
horizontal: false,
34+
isDisabled: () => {},
35+
linked: false,
36+
onSelect: () => {},
37+
};
38+
39+
ListGroup.propTypes = {
40+
active: PropTypes.number,
41+
bordered: PropTypes.bool,
42+
horizontal: PropTypes.bool,
43+
isDisabled: PropTypes.func,
44+
items: PropTypes.array.isRequired,
45+
linked: PropTypes.bool,
46+
onSelect: PropTypes.func,
47+
template: PropTypes.func.isRequired,
48+
};

src/list-group/ListGroupItem.jsx

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import { safeClick } from '../utils/event-handlers';
4+
5+
export function ListGroupItem({ index, isActive, children, linked, isDisabled, onSelect }) {
6+
const classes = [
7+
'list-group-item',
8+
isActive && 'active',
9+
isDisabled && 'disabled',
10+
linked && 'list-group-item-action',
11+
]
12+
.filter((v) => v)
13+
.join(' ');
14+
15+
if (linked) {
16+
return (
17+
<a href="#" className={classes} onClick={safeClick(onSelect, index)}>
18+
{children}
19+
</a>
20+
);
21+
}
22+
23+
return (
24+
<li className={classes} onClick={safeClick(onSelect, index)}>
25+
{children}
26+
</li>
27+
);
28+
}
29+
30+
ListGroupItem.defaultProps = {
31+
isActive: false,
32+
};
33+
34+
ListGroupItem.propTypes = {
35+
children: PropTypes.element,
36+
index: PropTypes.number.isRequired,
37+
isActive: PropTypes.bool,
38+
isDisabled: PropTypes.bool,
39+
linked: PropTypes.bool.isRequired,
40+
onSelect: PropTypes.func,
41+
template: PropTypes.func.isRequired,
42+
};

src/list-group/StatefulListGroup.jsx

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import { ListGroup } from './ListGroup';
4+
import { useSelectedItem } from '../utils/useSelectedItem';
5+
6+
export function StatefulListGroup({ initialItem, items, active: _, ...props }) {
7+
const { getSelected, select } = useSelectedItem(initialItem, items.length);
8+
useSelectedItem;
9+
10+
return <ListGroup items={items} active={getSelected()} onSelect={select} {...props} />;
11+
}
12+
13+
StatefulListGroup.propTypes = {
14+
active: PropTypes.any,
15+
items: PropTypes.arrayOf(PropTypes.object),
16+
initialItem: PropTypes.number,
17+
};

src/list-group/index.js

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './ListGroup';
2+
export * from './StatefulListGroup';

0 commit comments

Comments
 (0)