Skip to content

Commit

Permalink
Adding collapsable prop for multiselect groups
Browse files Browse the repository at this point in the history
  • Loading branch information
glagioia committed Nov 16, 2024
1 parent e7dc619 commit 98a4133
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 10 deletions.
32 changes: 32 additions & 0 deletions components/doc/common/apidoc/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -3882,6 +3882,14 @@
"default": "",
"description": "Template of an option group item."
},
{
"name": "optionGroupCollapsable",
"optional": true,
"readonly": false,
"type": "boolean",
"default": "",
"description": "Option to collapse group items."
},
{
"name": "panelClassName",
"optional": true,
Expand Down Expand Up @@ -23421,6 +23429,14 @@
"default": "",
"description": "Template of an option group item."
},
{
"name": "optionGroupCollapsable",
"optional": true,
"readonly": false,
"type": "boolean",
"default": "",
"description": "Option to collapse group items."
},
{
"name": "optionLabel",
"optional": true,
Expand Down Expand Up @@ -32963,6 +32979,14 @@
"default": "",
"description": "Template of an option group item."
},
{
"name": "optionGroupCollapsable",
"optional": true,
"readonly": false,
"type": "boolean",
"default": "",
"description": "Option to collapse group items."
},
{
"name": "optionLabel",
"optional": true,
Expand Down Expand Up @@ -36544,6 +36568,14 @@
"default": "",
"description": "Template of an option group item."
},
{
"name": "optionGroupCollapsable",
"optional": true,
"readonly": false,
"type": "boolean",
"default": "",
"description": "Option to collapse group items."
},
{
"name": "optionLabel",
"optional": true,
Expand Down
9 changes: 5 additions & 4 deletions components/doc/multiselect/groupdoc.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export function GroupDoc(props) {
const code = {
basic: `
<MultiSelect value={selectedCities} options={groupedCities} onChange={(e) => setSelectedCities(e.value)} optionLabel="label"
optionGroupLabel="label" optionGroupChildren="items" optionGroupTemplate={groupedItemTemplate}
optionGroupLabel="label" optionGroupChildren="items" optionGroupTemplate={groupedItemTemplate} optionGroupCollapsable
placeholder="Select Cities" display="chip" className="w-full md:w-20rem" />
`,
javascript: `
Expand Down Expand Up @@ -104,7 +104,7 @@ export default function GroupedDoc() {
return (
<div className="card flex justify-content-center">
<MultiSelect value={selectedCities} options={groupedCities} onChange={(e) => setSelectedCities(e.value)} optionLabel="label"
optionGroupLabel="label" optionGroupChildren="items" optionGroupTemplate={groupedItemTemplate}
optionGroupLabel="label" optionGroupChildren="items" optionGroupCollapsable optionGroupTemplate={groupedItemTemplate}
placeholder="Select Cities" display="chip" className="w-full md:w-20rem" />
</div>
);
Expand Down Expand Up @@ -172,7 +172,7 @@ export default function GroupedDoc() {
return (
<div className="card flex justify-content-center">
<MultiSelect value={selectedCities} options={groupedCities} onChange={(e) => setSelectedCities(e.value)} optionLabel="label"
optionGroupLabel="label" optionGroupChildren="items" optionGroupTemplate={groupedItemTemplate}
optionGroupLabel="label" optionGroupChildren="items" optionGroupCollapsable optionGroupTemplate={groupedItemTemplate}
placeholder="Select Cities" display="chip" className="w-full md:w-20rem" />
</div>
);
Expand All @@ -185,7 +185,7 @@ export default function GroupedDoc() {
<DocSectionText {...props}>
<p>
Options can be grouped when a nested data structures is provided. To define the label of a group <i>optionGroupLabel</i> property is needed and also <i>optionGroupChildren</i> is required to define the property that refers to the
children of a group.
children of a group. Also you can provide a <i>optionGroupCollapsable</i> property to collapse the groups by default.
</p>
</DocSectionText>
<div className="card flex justify-content-center">
Expand All @@ -200,6 +200,7 @@ export default function GroupedDoc() {
placeholder="Select Cities"
display="chip"
className="w-full md:w-20rem"
optionGroupCollapsable
/>
</div>
<DocSectionCode code={code} />
Expand Down
3 changes: 1 addition & 2 deletions components/lib/multiselect/MultiSelect.js
Original file line number Diff line number Diff line change
Expand Up @@ -872,7 +872,6 @@ export const MultiSelect = React.memo(
const labelKey = label + '_' + i;
const iconProps = mergeProps(
{
'aria-label': localeOption('removeTokenIcon'),
className: cx('removeTokenIcon'),
onClick: (e) => removeChip(e, val),
onKeyDown: (e) => onRemoveTokenIconKeyDown(e, val),
Expand Down Expand Up @@ -1022,7 +1021,6 @@ export const MultiSelect = React.memo(
const clearIconProps = mergeProps(
{
className: cx('clearIcon'),
'aria-label': localeOption('clear'),
onClick: (e) => updateModel(e, [], []),
onKeyDown: (e) => onClearIconKeyDown(e),
tabIndex: props.tabIndex || '0'
Expand Down Expand Up @@ -1199,6 +1197,7 @@ export const MultiSelect = React.memo(
isUnstyled={isUnstyled}
metaData={metaData}
changeFocusedOptionIndex={changeFocusedOptionIndex}
optionGroupCollapsable={props.optionGroupCollapsable}
/>
</div>
{hasTooltip && <Tooltip target={elementRef} content={props.tooltip} pt={ptm('tooltip')} {...props.tooltipOptions} />}
Expand Down
12 changes: 12 additions & 0 deletions components/lib/multiselect/MultiSelectBase.js
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,18 @@ const styles = `
.p-fluid .p-multiselect {
display: flex;
}
.p-multiselect-group-header{
display: flex;
align-items: center;
justify-content: space-between;
padding: 0.75rem 1.25rem;
cursor: pointer;
}
}
.p-multiselect-group-header:hover{
background-color: var(--primary-100);
}
}
`;

Expand Down
55 changes: 51 additions & 4 deletions components/lib/multiselect/MultiSelectPanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,35 @@ export const MultiSelectPanel = React.memo(
const filterInputRef = React.useRef(null);
const mergeProps = useMergeProps();
const context = React.useContext(PrimeReactContext);
const { ptm, cx, sx, isUnstyled } = props;

const { ptm, cx, sx, isUnstyled, optionGroupCollapsable = false } = props;
const [groupVisibility, setGroupVisibility] = React.useState({});

const toggleGroupVisibility = (groupLabel) => {
setGroupVisibility((prev) => ({
...prev,
[groupLabel]: !prev[groupLabel]
}));
};
const getPTOptions = (key, options) => {
return ptm(key, {
hostName: props.hostName,
...options
});
};

React.useEffect(() => {
if (props.optionGroupLabel) {
const initialVisibility = {};
props.visibleOptions.forEach((option) => {
if (option.group) {
const groupLabel = props.getOptionGroupLabel(option);
initialVisibility[groupLabel] = true;
}
});
setGroupVisibility(initialVisibility);
}
}, []);

const onEnter = () => {
props.onEnter(() => {
if (virtualScrollerRef.current) {
Expand Down Expand Up @@ -189,9 +209,36 @@ export const MultiSelectPanel = React.memo(

const createItems = () => {
if (ObjectUtils.isNotEmpty(props.visibleOptions)) {
return props.visibleOptions.map(createItem);
return props.visibleOptions.map((option, index) => {
if (option.group && props.optionGroupLabel) {
const groupLabel = props.getOptionGroupLabel(option);
const isVisible = groupVisibility[groupLabel];
const groupContent = props.optionGroupTemplate ? ObjectUtils.getJSXElement(props.optionGroupTemplate, option, index) : props.getOptionGroupLabel(option);

const groupHeaderClass = classNames('p-multiselect-group-header');

return (
<div key={`group_${index}`} className="p-multiselect-group">
<div onClick={() => toggleGroupVisibility(groupLabel)} className={groupHeaderClass} style={{ cursor: 'pointer' }}>
<span>{groupContent}</span>
{optionGroupCollapsable && (
<i
className={classNames('pi', {
'pi-chevron-right': !isVisible,
'pi-chevron-down': isVisible
})}
/>
)}
</div>
{(isVisible || !optionGroupCollapsable) && props.getOptionGroupChildren(option).map((child, childIndex) => createItem(child, `${index}_${childIndex}`))}
</div>
);
} else if (!props.optionGroupLabel) {
return createItem(option, index);
}
return null;
});
}

return props.hasFilter ? createEmptyFilter() : createEmptyContent();
};

Expand Down
4 changes: 4 additions & 0 deletions components/lib/multiselect/multiselect.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,10 @@ export interface MultiSelectProps extends Omit<React.DetailedHTMLProps<React.Inp
* Name of the label field of an option when an arbitrary objects instead of SelectItems are used as options.
*/
optionLabel?: string | undefined;
/**
* Collapsable option group item.
*/
optionGroupCollapsable?: boolean;
/**
* Property name or getter function to use as the value of an option, defaults to the option itself when not defined.
*/
Expand Down

0 comments on commit 98a4133

Please sign in to comment.