Skip to content

Commit 98a4133

Browse files
committed
Adding collapsable prop for multiselect groups
1 parent e7dc619 commit 98a4133

File tree

6 files changed

+105
-10
lines changed

6 files changed

+105
-10
lines changed

components/doc/common/apidoc/index.json

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3882,6 +3882,14 @@
38823882
"default": "",
38833883
"description": "Template of an option group item."
38843884
},
3885+
{
3886+
"name": "optionGroupCollapsable",
3887+
"optional": true,
3888+
"readonly": false,
3889+
"type": "boolean",
3890+
"default": "",
3891+
"description": "Option to collapse group items."
3892+
},
38853893
{
38863894
"name": "panelClassName",
38873895
"optional": true,
@@ -23421,6 +23429,14 @@
2342123429
"default": "",
2342223430
"description": "Template of an option group item."
2342323431
},
23432+
{
23433+
"name": "optionGroupCollapsable",
23434+
"optional": true,
23435+
"readonly": false,
23436+
"type": "boolean",
23437+
"default": "",
23438+
"description": "Option to collapse group items."
23439+
},
2342423440
{
2342523441
"name": "optionLabel",
2342623442
"optional": true,
@@ -32963,6 +32979,14 @@
3296332979
"default": "",
3296432980
"description": "Template of an option group item."
3296532981
},
32982+
{
32983+
"name": "optionGroupCollapsable",
32984+
"optional": true,
32985+
"readonly": false,
32986+
"type": "boolean",
32987+
"default": "",
32988+
"description": "Option to collapse group items."
32989+
},
3296632990
{
3296732991
"name": "optionLabel",
3296832992
"optional": true,
@@ -36544,6 +36568,14 @@
3654436568
"default": "",
3654536569
"description": "Template of an option group item."
3654636570
},
36571+
{
36572+
"name": "optionGroupCollapsable",
36573+
"optional": true,
36574+
"readonly": false,
36575+
"type": "boolean",
36576+
"default": "",
36577+
"description": "Option to collapse group items."
36578+
},
3654736579
{
3654836580
"name": "optionLabel",
3654936581
"optional": true,

components/doc/multiselect/groupdoc.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ export function GroupDoc(props) {
5050
const code = {
5151
basic: `
5252
<MultiSelect value={selectedCities} options={groupedCities} onChange={(e) => setSelectedCities(e.value)} optionLabel="label"
53-
optionGroupLabel="label" optionGroupChildren="items" optionGroupTemplate={groupedItemTemplate}
53+
optionGroupLabel="label" optionGroupChildren="items" optionGroupTemplate={groupedItemTemplate} optionGroupCollapsable
5454
placeholder="Select Cities" display="chip" className="w-full md:w-20rem" />
5555
`,
5656
javascript: `
@@ -104,7 +104,7 @@ export default function GroupedDoc() {
104104
return (
105105
<div className="card flex justify-content-center">
106106
<MultiSelect value={selectedCities} options={groupedCities} onChange={(e) => setSelectedCities(e.value)} optionLabel="label"
107-
optionGroupLabel="label" optionGroupChildren="items" optionGroupTemplate={groupedItemTemplate}
107+
optionGroupLabel="label" optionGroupChildren="items" optionGroupCollapsable optionGroupTemplate={groupedItemTemplate}
108108
placeholder="Select Cities" display="chip" className="w-full md:w-20rem" />
109109
</div>
110110
);
@@ -172,7 +172,7 @@ export default function GroupedDoc() {
172172
return (
173173
<div className="card flex justify-content-center">
174174
<MultiSelect value={selectedCities} options={groupedCities} onChange={(e) => setSelectedCities(e.value)} optionLabel="label"
175-
optionGroupLabel="label" optionGroupChildren="items" optionGroupTemplate={groupedItemTemplate}
175+
optionGroupLabel="label" optionGroupChildren="items" optionGroupCollapsable optionGroupTemplate={groupedItemTemplate}
176176
placeholder="Select Cities" display="chip" className="w-full md:w-20rem" />
177177
</div>
178178
);
@@ -185,7 +185,7 @@ export default function GroupedDoc() {
185185
<DocSectionText {...props}>
186186
<p>
187187
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
188-
children of a group.
188+
children of a group. Also you can provide a <i>optionGroupCollapsable</i> property to collapse the groups by default.
189189
</p>
190190
</DocSectionText>
191191
<div className="card flex justify-content-center">
@@ -200,6 +200,7 @@ export default function GroupedDoc() {
200200
placeholder="Select Cities"
201201
display="chip"
202202
className="w-full md:w-20rem"
203+
optionGroupCollapsable
203204
/>
204205
</div>
205206
<DocSectionCode code={code} />

components/lib/multiselect/MultiSelect.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -872,7 +872,6 @@ export const MultiSelect = React.memo(
872872
const labelKey = label + '_' + i;
873873
const iconProps = mergeProps(
874874
{
875-
'aria-label': localeOption('removeTokenIcon'),
876875
className: cx('removeTokenIcon'),
877876
onClick: (e) => removeChip(e, val),
878877
onKeyDown: (e) => onRemoveTokenIconKeyDown(e, val),
@@ -1022,7 +1021,6 @@ export const MultiSelect = React.memo(
10221021
const clearIconProps = mergeProps(
10231022
{
10241023
className: cx('clearIcon'),
1025-
'aria-label': localeOption('clear'),
10261024
onClick: (e) => updateModel(e, [], []),
10271025
onKeyDown: (e) => onClearIconKeyDown(e),
10281026
tabIndex: props.tabIndex || '0'
@@ -1199,6 +1197,7 @@ export const MultiSelect = React.memo(
11991197
isUnstyled={isUnstyled}
12001198
metaData={metaData}
12011199
changeFocusedOptionIndex={changeFocusedOptionIndex}
1200+
optionGroupCollapsable={props.optionGroupCollapsable}
12021201
/>
12031202
</div>
12041203
{hasTooltip && <Tooltip target={elementRef} content={props.tooltip} pt={ptm('tooltip')} {...props.tooltipOptions} />}

components/lib/multiselect/MultiSelectBase.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,18 @@ const styles = `
190190
.p-fluid .p-multiselect {
191191
display: flex;
192192
}
193+
.p-multiselect-group-header{
194+
display: flex;
195+
align-items: center;
196+
justify-content: space-between;
197+
padding: 0.75rem 1.25rem;
198+
cursor: pointer;
199+
}
200+
}
201+
.p-multiselect-group-header:hover{
202+
background-color: var(--primary-100);
203+
204+
}
193205
}
194206
`;
195207

components/lib/multiselect/MultiSelectPanel.js

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,35 @@ export const MultiSelectPanel = React.memo(
1414
const filterInputRef = React.useRef(null);
1515
const mergeProps = useMergeProps();
1616
const context = React.useContext(PrimeReactContext);
17-
const { ptm, cx, sx, isUnstyled } = props;
18-
17+
const { ptm, cx, sx, isUnstyled, optionGroupCollapsable = false } = props;
18+
const [groupVisibility, setGroupVisibility] = React.useState({});
19+
20+
const toggleGroupVisibility = (groupLabel) => {
21+
setGroupVisibility((prev) => ({
22+
...prev,
23+
[groupLabel]: !prev[groupLabel]
24+
}));
25+
};
1926
const getPTOptions = (key, options) => {
2027
return ptm(key, {
2128
hostName: props.hostName,
2229
...options
2330
});
2431
};
2532

33+
React.useEffect(() => {
34+
if (props.optionGroupLabel) {
35+
const initialVisibility = {};
36+
props.visibleOptions.forEach((option) => {
37+
if (option.group) {
38+
const groupLabel = props.getOptionGroupLabel(option);
39+
initialVisibility[groupLabel] = true;
40+
}
41+
});
42+
setGroupVisibility(initialVisibility);
43+
}
44+
}, []);
45+
2646
const onEnter = () => {
2747
props.onEnter(() => {
2848
if (virtualScrollerRef.current) {
@@ -189,9 +209,36 @@ export const MultiSelectPanel = React.memo(
189209

190210
const createItems = () => {
191211
if (ObjectUtils.isNotEmpty(props.visibleOptions)) {
192-
return props.visibleOptions.map(createItem);
212+
return props.visibleOptions.map((option, index) => {
213+
if (option.group && props.optionGroupLabel) {
214+
const groupLabel = props.getOptionGroupLabel(option);
215+
const isVisible = groupVisibility[groupLabel];
216+
const groupContent = props.optionGroupTemplate ? ObjectUtils.getJSXElement(props.optionGroupTemplate, option, index) : props.getOptionGroupLabel(option);
217+
218+
const groupHeaderClass = classNames('p-multiselect-group-header');
219+
220+
return (
221+
<div key={`group_${index}`} className="p-multiselect-group">
222+
<div onClick={() => toggleGroupVisibility(groupLabel)} className={groupHeaderClass} style={{ cursor: 'pointer' }}>
223+
<span>{groupContent}</span>
224+
{optionGroupCollapsable && (
225+
<i
226+
className={classNames('pi', {
227+
'pi-chevron-right': !isVisible,
228+
'pi-chevron-down': isVisible
229+
})}
230+
/>
231+
)}
232+
</div>
233+
{(isVisible || !optionGroupCollapsable) && props.getOptionGroupChildren(option).map((child, childIndex) => createItem(child, `${index}_${childIndex}`))}
234+
</div>
235+
);
236+
} else if (!props.optionGroupLabel) {
237+
return createItem(option, index);
238+
}
239+
return null;
240+
});
193241
}
194-
195242
return props.hasFilter ? createEmptyFilter() : createEmptyContent();
196243
};
197244

components/lib/multiselect/multiselect.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,10 @@ export interface MultiSelectProps extends Omit<React.DetailedHTMLProps<React.Inp
579579
* Name of the label field of an option when an arbitrary objects instead of SelectItems are used as options.
580580
*/
581581
optionLabel?: string | undefined;
582+
/**
583+
* Collapsable option group item.
584+
*/
585+
optionGroupCollapsable?: boolean;
582586
/**
583587
* Property name or getter function to use as the value of an option, defaults to the option itself when not defined.
584588
*/

0 commit comments

Comments
 (0)