Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding collapsable prop for multiselect groups #7427

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading