Skip to content

Commit

Permalink
Improve custom MultiSelect icon implementation Related - #4220
Browse files Browse the repository at this point in the history
Add MultiSelect icon templating support Related - #4226
  • Loading branch information
habubey committed Apr 14, 2023
1 parent 90b0bc7 commit ff9aa03
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 20 deletions.
27 changes: 24 additions & 3 deletions components/lib/multiselect/MultiSelect.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import { Tooltip } from '../tooltip/Tooltip';
import { classNames, DomHandler, IconUtils, ObjectUtils, ZIndexUtils } from '../utils/Utils';
import { MultiSelectBase } from './MultiSelectBase';
import { MultiSelectPanel } from './MultiSelectPanel';
import { TimesCircleIcon } from '../icon/timescircle';
import { TimesIcon } from '../icon/times';
import { ChevronDownIcon } from '../icon/chevrondown';

export const MultiSelect = React.memo(
React.forwardRef((inProps, ref) => {
Expand Down Expand Up @@ -489,7 +492,13 @@ export const MultiSelect = React.memo(

return value.map((val) => {
const label = getLabelByValue(val);
const icon = !props.disabled && IconUtils.getJSXIcon(props.removeIcon, { className: 'p-multiselect-token-icon', onClick: (e) => removeChip(e, val) }, { props });
const icon =
!props.disabled &&
(props.removeIcon ? (
IconUtils.getJSXIcon(props.removeIcon, { className: 'p-multiselect-token-icon', onClick: (e) => removeChip(e, val) }, { props })
) : (
<TimesCircleIcon className="p-multiselect-token-icon" onClick={(e) => removeChip(e, val)} />
));

return (
<div className="p-multiselect-token" key={label}>
Expand Down Expand Up @@ -560,8 +569,16 @@ export const MultiSelect = React.memo(
});

const createClearIcon = () => {
const iconClassName = 'p-multiselect-clear-icon';
const icon = props.clearIcon || <TimesIcon className={iconClassName} />;
const clearIcon = IconUtils.getJSXIcon(icon, { className: iconClassName }, { props });

if (!empty && props.showClear && !props.disabled) {
return <i className="p-multiselect-clear-icon pi pi-times" onClick={(e) => updateModel(e, null)}></i>;
return (
<i className={iconClassName} onClick={(e) => updateModel(e, null)}>
{clearIcon}
</i>
);
}

return null;
Expand Down Expand Up @@ -599,6 +616,10 @@ export const MultiSelect = React.memo(
},
props.className
);

const dropdownIconClass = 'p-multiselect-trigger-icon p-c';
const dropdownIcon = <div className="p-multiselect-trigger">{props.dropdownIcon ? IconUtils.getJSXIcon(props.dropdownIcon, { className: dropdownIconClass }, { props }) : <ChevronDownIcon className={dropdownIconClass} />}</div>;

const label = !props.inline && createLabel();
const clearIcon = !props.inline && createClearIcon();

Expand Down Expand Up @@ -626,7 +647,7 @@ export const MultiSelect = React.memo(
<>
{label}
{clearIcon}
<div className="p-multiselect-trigger">{IconUtils.getJSXIcon(props.dropdownIcon, { className: 'p-multiselect-trigger-icon p-c' }, { props })}</div>
{dropdownIcon}
</>
)}
<MultiSelectPanel
Expand Down
7 changes: 5 additions & 2 deletions components/lib/multiselect/MultiSelectBase.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ export const MultiSelectBase = {
appendTo: null,
ariaLabelledBy: null,
className: null,
closeIcon: null,
checkboxIcon: null,
dataKey: null,
disabled: false,
display: 'comma',
dropdownIcon: 'pi pi-chevron-down',
dropdownIcon: null,
emptyFilterMessage: null,
filter: false,
filterBy: null,
Expand All @@ -20,6 +22,7 @@ export const MultiSelectBase = {
fixedPlaceholder: false,
flex: false,
id: null,
itemCheckboxIcon: null,
inline: false,
inputId: null,
inputRef: null,
Expand Down Expand Up @@ -47,7 +50,7 @@ export const MultiSelectBase = {
panelHeaderTemplate: null,
panelStyle: null,
placeholder: null,
removeIcon: 'pi pi-times-circle',
removeIcon: null,
resetFilterOnHide: false,
scrollHeight: '200px',
selectAll: false,
Expand Down
31 changes: 25 additions & 6 deletions components/lib/multiselect/MultiSelectHeader.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ import { localeOption } from '../api/Api';
import { Checkbox } from '../checkbox/Checkbox';
import { InputText } from '../inputtext/InputText';
import { Ripple } from '../ripple/Ripple';
import { classNames, ObjectUtils } from '../utils/Utils';
import { classNames, IconUtils, ObjectUtils } from '../utils/Utils';
import { TimesIcon } from '../icon/times';
import { SearchIcon } from '../icon/search';
import { CheckIcon } from '../icon/check';

export const MultiSelectHeader = React.memo((props) => {
const filterOptions = {
Expand Down Expand Up @@ -32,12 +35,16 @@ export const MultiSelectHeader = React.memo((props) => {
};

const createFilterElement = () => {
const filterIconClassName = 'p-multiselect-filter-icon';
const icon = props.filterIcon || <SearchIcon className={filterIconClassName} />;
const filterIcon = IconUtils.getJSXIcon(icon, { className: filterIconClassName }, { props });

if (props.filter) {
const containerClassName = classNames('p-multiselect-filter-container');
let content = (
<div className={containerClassName}>
<InputText ref={props.filterRef} type="text" role="textbox" value={props.filterValue} onChange={onFilter} className="p-multiselect-filter" placeholder={props.filterPlaceholder} />
<span className="p-multiselect-filter-icon pi pi-search"></span>
{filterIcon}
</div>
);

Expand All @@ -47,7 +54,7 @@ export const MultiSelectHeader = React.memo((props) => {
element: content,
filterOptions: filterOptions,
onFilter: onFilter,
filterIconClassName: 'p-multeselect-filter-icon pi pi-search',
filterIconClassName,
props
};

Expand All @@ -61,13 +68,24 @@ export const MultiSelectHeader = React.memo((props) => {
};

const filterElement = createFilterElement();
const checkboxElement = props.showSelectAll && <Checkbox checked={props.selectAll} onChange={onSelectAll} role="checkbox" aria-checked={props.selectAll} />;

const checkboxIconClassName = 'p-checkbox-icon p-c';
const checkedIcon = props.itemCheckboxIcon || <CheckIcon className={checkboxIconClassName} />;
const itemCheckboxIcon = IconUtils.getJSXIcon(checkedIcon, { className: checkboxIconClassName }, { selected: props.selected });

const checkboxElement = props.showSelectAll && <Checkbox checked={props.selectAll} onChange={onSelectAll} role="checkbox" aria-checked={props.selectAll} icon={itemCheckboxIcon} />;

const iconProps = { className: 'p-multiselect-close-icon', 'aria-hidden': true };
const icon = props.closeIcon || <TimesIcon {...iconProps} />;
const closeIcon = IconUtils.getJSXIcon(icon, { ...iconProps }, { props });

const closeElement = (
<button type="button" className="p-multiselect-close p-link" aria-label={localeOption('close')} onClick={props.onClose}>
<span className="p-multiselect-close-icon pi pi-times" aria-hidden="true"></span>
{closeIcon}
<Ripple />
</button>
);

const element = (
<div className="p-multiselect-header">
{checkboxElement}
Expand All @@ -85,9 +103,10 @@ export const MultiSelectHeader = React.memo((props) => {
filterElement,
closeElement,
closeElementClassName: 'p-multiselect-close p-link',
closeIconClassName: 'p-multiselect-close-icon pi pi-times',
closeIconClassName: 'p-multiselect-close-icon',
onCloseClick: props.onClose,
element,
itemCheckboxIcon,
props
};

Expand Down
15 changes: 8 additions & 7 deletions components/lib/multiselect/MultiSelectItem.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from 'react';
import { Ripple } from '../ripple/Ripple';
import { classNames, ObjectUtils } from '../utils/Utils';
import { classNames, IconUtils, ObjectUtils } from '../utils/Utils';
import { CheckIcon } from '../icon/check';

export const MultiSelectItem = React.memo((props) => {
const onClick = (event) => {
Expand Down Expand Up @@ -35,18 +36,18 @@ export const MultiSelectItem = React.memo((props) => {
const checkboxClassName = classNames('p-checkbox-box', {
'p-highlight': props.selected
});
const checkboxIcon = classNames('p-checkbox-icon p-c', {
'pi pi-check': props.selected
});

const checkboxIconClassName = 'p-checkbox-icon p-c';
const icon = props.checkboxIcon || <CheckIcon className={checkboxIconClassName} />;
const checkboxIcon = IconUtils.getJSXIcon(icon, { className: checkboxIconClassName }, { selected: props.selected });

const content = props.template ? ObjectUtils.getJSXElement(props.template, props.option) : props.label;
const tabIndex = props.disabled ? null : props.tabIndex || 0;

return (
<li className={className} style={props.style} onClick={onClick} tabIndex={tabIndex} onKeyDown={onKeyDown} role="option" aria-selected={props.selected}>
<div className="p-checkbox p-component">
<div className={checkboxClassName}>
<span className={checkboxIcon}></span>
</div>
<div className={checkboxClassName}>{checkboxIcon}</div>
</div>
<span>{content}</span>
<Ripple />
Expand Down
5 changes: 5 additions & 0 deletions components/lib/multiselect/MultiSelectPanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ export const MultiSelectPanel = React.memo(
onSelectAll={props.onSelectAll}
template={props.panelHeaderTemplate}
resetFilter={props.resetFilter}
closeIcon={props.closeIcon}
filterIcon={props.filterIcon}
itemCheckboxIcon={props.itemCheckboxIcon}
/>
);
};
Expand Down Expand Up @@ -96,6 +99,7 @@ export const MultiSelectPanel = React.memo(
tabIndex={tabIndex}
disabled={disabled}
className={props.itemClassName}
checkboxIcon={props.checkboxIcon}
/>
);
});
Expand Down Expand Up @@ -143,6 +147,7 @@ export const MultiSelectPanel = React.memo(
tabIndex={tabIndex}
disabled={disabled}
className={props.itemClassName}
checkboxIcon={props.checkboxIcon}
/>
);
}
Expand Down
18 changes: 16 additions & 2 deletions components/lib/multiselect/multiselect.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,10 +214,18 @@ export interface MultiSelectProps extends Omit<React.DetailedHTMLProps<React.Inp
* @readonly
*/
children?: React.ReactNode | undefined;
/**
* Icon of the checkbox when checked.
*/
checkboxIcon?: IconType<MultiSelectProps> | undefined;
/**
* Style class of the element.
*/
className?: string | undefined;
/**
* Close icon of the multiselect panel header.
*/
closeIcon?: IconType<MultiSelectProps> | undefined;
/**
* A property to uniquely match the value in options for better performance.
*/
Expand All @@ -234,7 +242,6 @@ export interface MultiSelectProps extends Omit<React.DetailedHTMLProps<React.Inp
display?: 'comma' | 'chip' | undefined;
/**
* Icon class of the dropdown icon.
* @defaultValue pi pi-chevron-down
*/
dropdownIcon?: IconType<MultiSelectProps>;
/**
Expand All @@ -252,6 +259,10 @@ export interface MultiSelectProps extends Omit<React.DetailedHTMLProps<React.Inp
* @defaultValue label
*/
filterBy?: string | undefined;
/**
* Icon of the filter icon.
*/
filterIcon?: IconType<MultiSelect> | undefined;
/**
* Locale to use in filtering. The default locale is the host environment's current locale.
* @defaultValue undefined
Expand Down Expand Up @@ -301,6 +312,10 @@ export interface MultiSelectProps extends Omit<React.DetailedHTMLProps<React.Inp
* Style class of the items.
*/
itemClassName?: string | undefined;
/**
* The icon displayed in the header when all checkboxes are checked.
*/
itemCheckboxIcon?: IconType<MultiSelect> | undefined;
/**
* Function that gets the option and returns the content for it.
*/
Expand Down Expand Up @@ -368,7 +383,6 @@ export interface MultiSelectProps extends Omit<React.DetailedHTMLProps<React.Inp
placeholder?: string | undefined;
/**
* Icon of the remove chip element.
* @defaultValue pi pi-times-circle
*/
removeIcon?: IconType<MultiSelectProps> | undefined;
/**
Expand Down

0 comments on commit ff9aa03

Please sign in to comment.