Skip to content

Commit 024bd2d

Browse files
authored
add leading and trailing to listitem widget (#1690)
* convert list-item test to use test renderer * add leading and trailing to listitem widget closes #1570
1 parent 4784284 commit 024bd2d

12 files changed

+321
-848
lines changed

src/examples/src/config.tsx

+6
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ import BasicList from './widgets/list/Basic';
105105
import DividedList from './widgets/list/Dividers';
106106
import ControlledList from './widgets/list/Controlled';
107107
import ItemRenderer from './widgets/list/ItemRenderer';
108+
import ListItemRenderer from './widgets/list/ListItemRenderer';
108109
import FetchedResource from './widgets/list/FetchedResource';
109110
import DisabledList from './widgets/list/Disabled';
110111
import DraggableList from './widgets/list/Draggable';
@@ -1069,6 +1070,11 @@ export const config = {
10691070
module: ItemRenderer,
10701071
title: 'Item Renderer'
10711072
},
1073+
{
1074+
filename: 'ListItemRenderer',
1075+
module: ListItemRenderer,
1076+
title: 'ListItem Renderer'
1077+
},
10721078
{
10731079
description:
10741080
'This example shows the menu used as a Listbox. This allows for a selection to be made and persisted. Useful for user selections and within selects / typeahead etc.',
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { create, tsx } from '@dojo/framework/core/vdom';
2+
import List, { ListItem } from '@dojo/widgets/list';
3+
import Icon from '@dojo/widgets/icon';
4+
import states from './states';
5+
import icache from '@dojo/framework/core/middleware/icache';
6+
import Example from '../../Example';
7+
import {
8+
createResourceTemplate,
9+
createResourceMiddleware
10+
} from '@dojo/framework/core/middleware/resources';
11+
12+
const resource = createResourceMiddleware();
13+
const factory = create({ icache, resource });
14+
const template = createResourceTemplate<typeof states[0]>('value');
15+
16+
export default factory(function ListItemRenderer({ id, middleware: { icache, resource } }) {
17+
return (
18+
<Example>
19+
<List
20+
resource={resource({
21+
template: template({ id, data: states }),
22+
transform: { value: 'value', label: 'value' }
23+
})}
24+
onValue={(value) => {
25+
icache.set('value', value);
26+
}}
27+
itemsInView={8}
28+
>
29+
{({ value, label }, props) => (
30+
<ListItem {...props}>
31+
{{
32+
leading: <Icon type="locationIcon" />,
33+
primary: label,
34+
trailing: value
35+
}}
36+
</ListItem>
37+
)}
38+
</List>
39+
<p>{`Clicked On: ${JSON.stringify(icache.getOrSet('value', ''))}`}</p>
40+
</Example>
41+
);
42+
});

src/list/README.md

+6
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,9 @@ Dojo's `List` provides a base widget that can be used as a List or an options Li
1919
- Up Arrow: highlights the previous option
2020
- Down Arrow: highlights the next option
2121
- a-z, 0-9: highlights matching entries, ie. `ne` would match `nevada` in a list of states.
22+
23+
### ListItem
24+
When using a custom renderer, the `ListItem` widget supports different child properties.
25+
- Leading: Content to display at the start of the list item, such as an icon
26+
- Primary: Main content of the list item
27+
- Trailing: Content to display at the end of the list item, such as minor details

src/list/index.tsx

+22-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { RenderResult } from '@dojo/framework/core/interfaces';
22
import { focus } from '@dojo/framework/core/middleware/focus';
33
import { createICacheMiddleware } from '@dojo/framework/core/middleware/icache';
44
import { create, tsx } from '@dojo/framework/core/vdom';
5-
import { Keys } from '../common/util';
5+
import { Keys, isRenderResult } from '../common/util';
66
import theme, { ThemeProperties } from '../middleware/theme';
77
import offscreen from '../middleware/offscreen';
88
import * as listItemCss from '../theme/default/list-item.m.css';
@@ -104,7 +104,18 @@ export interface ListItemProperties {
104104
collapsed?: boolean;
105105
}
106106

107-
const listItemFactory = create({ theme }).properties<ListItemProperties>();
107+
export interface ListItemChildren {
108+
/** Icon or avatar to place before the primary content */
109+
leading?: RenderResult;
110+
/** The main content of the item, typically text */
111+
primary?: RenderResult;
112+
/** Icon or text to place after the primary content */
113+
trailing?: RenderResult;
114+
}
115+
116+
const listItemFactory = create({ theme })
117+
.properties<ListItemProperties>()
118+
.children<ListItemChildren | RenderResult | RenderResult[]>();
108119

109120
export const ListItem = listItemFactory(function ListItem({
110121
properties,
@@ -141,6 +152,11 @@ export const ListItem = listItemFactory(function ListItem({
141152
!disabled && !active && onRequestActive();
142153
}
143154

155+
const [firstChild, ...otherChildren] = children();
156+
const { leading = undefined, primary, trailing = undefined } = isRenderResult(firstChild)
157+
? { primary: [firstChild, ...otherChildren] }
158+
: firstChild;
159+
144160
return (
145161
<div
146162
id={widgetId}
@@ -176,8 +192,10 @@ export const ListItem = listItemFactory(function ListItem({
176192
ondrop={onDrop}
177193
styles={{ visibility: dragged ? 'hidden' : undefined }}
178194
>
179-
{children()}
180-
{draggable && (
195+
{leading ? <span classes={themedCss.leading}>{leading}</span> : undefined}
196+
<span classes={themedCss.primary}>{primary}</span>
197+
{trailing ? <span classes={themedCss.trailing}>{trailing}</span> : undefined}
198+
{draggable && !trailing && (
181199
<Icon
182200
type="barsIcon"
183201
classes={{ '@dojo/widgets/icon': { icon: [themedCss.dragIcon] } }}

0 commit comments

Comments
 (0)