11import { ListItem } from './list-item.types';
22import { ListSeparator } from '../../global/shared-types/separator.types';
3- import { MenuItem } from '../menu/menu.types';
43import { h } from '@stencil/core';
5- import { CheckboxTemplate } from '../checkbox/checkbox.template';
64import { ListRendererConfig } from './list-renderer-config';
7- import { RadioButtonTemplate } from '../radio-button-group/radio-button.template';
8- import {
9- getIconColor,
10- getIconName,
11- getIconTitle,
12- } from '../icon/get-icon-props';
13- import { isEmpty } from 'lodash-es';
145
156export class ListRenderer {
167 private defaultConfig: ListRendererConfig = {
@@ -20,11 +11,6 @@ export class ListRenderer {
2011
2112 private config: ListRendererConfig;
2213
23- private hasIcons: boolean;
24- private twoLines: boolean;
25- private avatarList: boolean;
26- private commandKey: boolean;
27-
2814 private applyTabIndexToItemAtIndex: number;
2915
3016 public render(
@@ -34,17 +20,6 @@ export class ListRenderer {
3420 items = items || [];
3521 this.config = { ...this.defaultConfig, ...config };
3622
37- this.twoLines = items.some((item) => {
38- return 'secondaryText' in item && !!item.secondaryText;
39- });
40-
41- this.hasIcons = items.some((item) => {
42- return 'icon' in item && !!item.icon;
43- });
44-
45- this.avatarList = this.config.badgeIcons && this.hasIcons;
46- const selectableListTypes = ['selectable', 'radio', 'checkbox'];
47-
4823 let role;
4924 switch (this.config.type) {
5025 case 'checkbox': {
@@ -63,19 +38,12 @@ export class ListRenderer {
6338 this.applyTabIndexToItemAtIndex =
6439 this.getIndexForWhichToApplyTabIndex(items);
6540
66- const classNames = {
67- 'mdc-deprecated-list': true,
68- 'mdc-deprecated-list--two-line': this.twoLines,
69- selectable: selectableListTypes.includes(this.config.type),
70- 'mdc-deprecated-list--avatar-list': this.avatarList,
71- 'list--compact':
72- this.twoLines &&
73- this.commandKey &&
74- ['small', 'x-small'].includes(this.config.iconSize),
75- };
76-
7741 return (
78- <ul class={classNames} role={role} aria-orientation="vertical">
42+ <ul
43+ class="mdc-deprecated-list"
44+ role={role}
45+ aria-orientation="vertical"
46+ >
7947 {items.map(this.renderListItem)}
8048 </ul>
8149 );
@@ -140,37 +108,37 @@ export class ListRenderer {
140108 );
141109 }
142110
143- if (['radio', 'checkbox'].includes(this.config.type)) {
144- return this.renderVariantListItem(this.config, item, index);
145- }
146-
147- const classNames = {
148- 'mdc-deprecated-list-item': true,
149- 'mdc-deprecated-list-item--disabled': item.disabled,
150- 'mdc-deprecated-list-item--selected': item.selected,
151- 'has-primary-component': this.hasPrimaryComponent(item),
152- };
153-
154111 const attributes: { tabindex?: string } = {};
155112 if (index === this.applyTabIndexToItemAtIndex) {
156113 attributes.tabindex = '0';
157114 }
158115
116+ let itemType: 'radio' | 'checkbox' | 'option' | 'listitem';
117+ if (this.config.type === 'radio' || this.config.type === 'checkbox') {
118+ itemType = this.config.type;
119+ } else if (this.config.type === 'selectable') {
120+ itemType = 'option';
121+ } else {
122+ itemType = 'listitem';
123+ }
124+
159125 return (
160- <li
161- class={classNames}
162- aria-disabled={item.disabled ? 'true' : 'false'}
163- aria-selected={item.selected ? 'true' : 'false'}
164- data-index={index}
126+ <limel-list-item
127+ class="mdc-deprecated-list-item" // required for keyboard navigation with arrow keys
165128 {...attributes}
166- >
167- {this.renderIcon(this.config, item)}
168- {this.renderPicture(item)}
169- {this.getPrimaryComponent(item)}
170- {this.renderText(item)}
171- {this.twoLines && this.avatarList ? this.renderDivider() : null}
172- {this.renderActionMenu(item.actions)}
173- </li>
129+ data-index={index}
130+ type={itemType}
131+ text={item.text}
132+ secondaryText={item.secondaryText}
133+ icon={item.icon}
134+ image={item.image}
135+ primaryComponent={item.primaryComponent}
136+ badgeIcon={this.config.badgeIcons}
137+ iconSize={this.config.iconSize}
138+ selected={item.selected}
139+ disabled={item.disabled}
140+ actions={item.actions}
141+ />
174142 );
175143 };
176144
@@ -179,202 +147,4 @@ export class ListRenderer {
179147 return <h2 class="limel-list-divider-title">{item.text}</h2>;
180148 }
181149 };
182-
183- private getPrimaryComponent(item: ListItem): Element {
184- if (!this.hasPrimaryComponent(item)) {
185- return;
186- }
187-
188- const PrimaryComponent = item.primaryComponent.name;
189- const props = item.primaryComponent.props;
190-
191- return <PrimaryComponent {...props} />;
192- }
193-
194- private hasPrimaryComponent = (item: ListItem) => {
195- return !!item?.primaryComponent?.name;
196- };
197-
198- /**
199- * Render the text of the list item
200- *
201- * @param item - the list item
202- * @returns the text for the list item
203- */
204- private renderText = (item: ListItem) => {
205- if (this.isSimpleItem(item)) {
206- return (
207- <span class="mdc-deprecated-list-item__text">{item.text}</span>
208- );
209- }
210-
211- return (
212- <div class="mdc-deprecated-list-item__text">
213- <div class="mdc-deprecated-list-item__primary-command-text">
214- <div class="mdc-deprecated-list-item__primary-text">
215- {item.text}
216- </div>
217- </div>
218- <div class="mdc-deprecated-list-item__secondary-text">
219- {item.secondaryText}
220- </div>
221- </div>
222- );
223- };
224-
225- private isSimpleItem = (item: ListItem): boolean => {
226- return !('secondaryText' in item);
227- };
228-
229- /**
230- * Render an icon for a list item
231- *
232- * @param config - the config object, passed on from the `renderListItem` function
233- * @param item - the list item
234- * @returns the icon element
235- */
236- private renderIcon = (config: ListRendererConfig, item: ListItem) => {
237- const style: any = {};
238- const name = getIconName(item.icon);
239- if (!name) {
240- return;
241- }
242-
243- const color = getIconColor(item.icon, item.iconColor);
244- const title = getIconTitle(item.icon);
245-
246- if (color) {
247- if (config.badgeIcons) {
248- style['--icon-background-color'] = color;
249- } else {
250- style.color = color;
251- }
252- }
253-
254- return (
255- <limel-icon
256- badge={config.badgeIcons}
257- class="mdc-deprecated-list-item__graphic"
258- name={name}
259- style={style}
260- size={config.iconSize}
261- aria-label={title}
262- aria-hidden={title ? null : 'true'}
263- />
264- );
265- };
266-
267- private renderPicture(item: ListItem) {
268- const image = item.image;
269- if (isEmpty(image)) {
270- return;
271- }
272-
273- return <img src={image.src} alt={image.alt} loading="lazy" />;
274- }
275-
276- private renderDivider = () => {
277- const classes = {
278- 'mdc-deprecated-list-divider': true,
279- 'mdc-deprecated-list-divider--inset': true,
280- };
281- if (this.config.iconSize) {
282- classes[this.config.iconSize] = true;
283- }
284-
285- return <hr class={classes} />;
286- };
287-
288- private renderActionMenu = (actions: Array<MenuItem | ListSeparator>) => {
289- if (!actions || actions.length === 0) {
290- return;
291- }
292-
293- return (
294- <limel-menu
295- class="mdc-deprecated-list-item__meta"
296- items={actions}
297- openDirection="left-start"
298- >
299- <limel-icon-button
300- class="action-menu-trigger"
301- slot="trigger"
302- icon="menu_2"
303- />
304- </limel-menu>
305- );
306- };
307-
308- private renderVariantListItem = (
309- config: ListRendererConfig,
310- item: ListItem,
311- index: number
312- ) => {
313- let itemTemplate;
314- if (config.type === 'radio') {
315- itemTemplate = (
316- <RadioButtonTemplate
317- id={`c_${index}`}
318- checked={item.selected}
319- disabled={item.disabled}
320- />
321- );
322- } else if (config.type === 'checkbox') {
323- itemTemplate = (
324- <CheckboxTemplate
325- id={`c_${index}`}
326- checked={item.selected}
327- disabled={item.disabled}
328- />
329- );
330- }
331-
332- const classNames = {
333- 'mdc-deprecated-list-item': true,
334- 'mdc-deprecated-list-item--disabled': item.disabled,
335- 'mdc-deprecated-list-item__text': !item.secondaryText,
336- 'has-primary-component': this.hasPrimaryComponent(item),
337- };
338-
339- const attributes: { tabindex?: string } = {};
340- if (index === this.applyTabIndexToItemAtIndex) {
341- attributes.tabindex = '0';
342- }
343-
344- return (
345- <li
346- class={classNames}
347- role={config.type}
348- aria-checked={item.selected ? 'true' : 'false'}
349- aria-disabled={item.disabled ? 'true' : 'false'}
350- data-index={index}
351- {...attributes}
352- >
353- {this.renderVariantListItemContent(config, item, itemTemplate)}
354- </li>
355- );
356- };
357-
358- private renderVariantListItemContent = (
359- config: ListRendererConfig,
360- item: ListItem,
361- itemTemplate: any
362- ) => {
363- if (this.hasIcons) {
364- return [
365- item.icon ? this.renderIcon(config, item) : null,
366- this.getPrimaryComponent(item),
367- this.renderText(item),
368- <div class="mdc-deprecated-list-item__meta">
369- {itemTemplate}
370- </div>,
371- ];
372- }
373-
374- return [
375- <div class="mdc-deprecated-list-item__graphic">{itemTemplate}</div>,
376- this.getPrimaryComponent(item),
377- this.renderText(item),
378- ];
379- };
380150}
0 commit comments