I am making a dropdown list in which I want to select all the sub-items if the main head is checked, and select the head, if all the sub-items are checked.
const DropDownList = ({ data }) => {
const [open, setOpen] = useState({});
const [checked, setChecked] = useState({});
const handleClick = (department) => {
setOpen((prevOpen) => ({
...prevOpen,
[department]: !prevOpen[department],
}));
};
const handleCheck = (item) => {
setChecked((prevChecked) => ({
...prevChecked,
[item]: !prevChecked[item],
}));
};
return (
<List>
{data.map((department) => (
<React.Fragment key={department.department}>
<ListItem
sx={{ cursor: "pointer" }}
onClick={() => handleClick(department.department)}
>
<Checkbox
checked={checked[department.department] || false}
onChange={() => handleCheck(department.department)}
/>
<ListItemText primary={department.department} />
{open[department.department] ? <ExpandLess /> : <ExpandMore />}
</ListItem>
<Collapse in={open[department.department]} unmountOnExit>
<List
component="div"
sx={{ padding: "0 35px", cursor: "pointer" }}
disablePadding
>
{department.sub_departments.map((subDepartment) => (
<ListItem key={subDepartment}>
<Checkbox
checked={checked[subDepartment] || false}
onChange={() => handleCheck(subDepartment)}
/>
<ListItemText primary={subDepartment} />
</ListItem>
))}
</List>
</Collapse>
</React.Fragment>
))}
</List>
);
};
export default DropDownList;
This is my List component.
I know the logic, but I can't implement it. I don't know how to. I know that by checking if all sub-items are checked, we can check the head too, but I don't know how to implement this bit.
For "select all the sub-items if the main head is checked", consider modifying handleCheck()
to pass an item
object. This will let us know of the checked item's sub_departments
:
const handleCheck = (item) => {
setChecked((prevChecked) => ({
...prevChecked,
[item.department]: !prevChecked[item.department],
}));
};
…
<Checkbox
…
onChange={() => handleCheck(department)}
/>
…
<Checkbox
…
onChange={() => handleCheck({ department: subDepartment })}
/>
If it is being checked, check its sub_departments
:
const handleCheck = (item, parent = {}) => {
setChecked((prevChecked) => {
const list = {
...prevChecked,
[item.department]: !prevChecked[item.department],
};
if (list[item.department]) {
if (item.sub_departments) {
item.sub_departments.forEach(sub => {
list[sub] = true;
});
}
}
return list;
});
};
For "select the head, if all the sub-items are checked", pass in the parent department item as a second argument to handleCheck()
:
const handleCheck = (item, parent = {}) => {
…
}
…
<Checkbox
…
onChange={() => handleCheck({ department: subDepartment }, department)}
/>
Then check the parent department if all the sub_departments
are checked:
const handleCheck = (item, parent = {}) => {
setChecked((prevChecked) => {
const list = {
...prevChecked,
[item.department]: !prevChecked[item.department],
};
if (list[item.department]) {
…
if (parent.sub_departments && parent.sub_departments.length) {
let checkParent = true;
for (const subItem of parent.sub_departments) {
if (!list[subItem]) {
checkParent = false;
break;
}
}
if (checkParent) {
list[parent.department] = true;
}
}
}
return list;
});
};
Full example:
const { useState } = React;
const { List, ListItem, Checkbox, ListItemText, Collapse } = MaterialUI;
const ExpandMore = () => 'ExpandMore';
const ExpandLess = () => 'ExpandLess';
const DropDownList = ({ data }) => {
const [open, setOpen] = useState({});
const [checked, setChecked] = useState({});
const handleClick = (department) => {
setOpen((prevOpen) => ({
...prevOpen,
[department]: !prevOpen[department],
}));
};
const handleCheck = (item, parent = {}) => {
setChecked((prevChecked) => {
const list = {
...prevChecked,
[item.department]: !prevChecked[item.department],
};
if (list[item.department]) {
if (item.sub_departments) {
item.sub_departments.forEach(sub => {
list[sub] = true;
});
}
if (parent.sub_departments && parent.sub_departments.length) {
let checkParent = true;
for (const subItem of parent.sub_departments) {
if (!list[subItem]) {
checkParent = false;
break;
}
}
if (checkParent) {
list[parent.department] = true;
}
}
}
return list;
});
};
return (
<List>
{data.map((department) => (
<React.Fragment key={department.department}>
<ListItem
sx={{ cursor: "pointer" }}
onClick={() => handleClick(department.department)}
>
<Checkbox
checked={checked[department.department] || false}
onChange={() => handleCheck(department)}
/>
<ListItemText primary={department.department} />
{open[department.department] ? <ExpandLess /> : <ExpandMore />}
</ListItem>
<Collapse in={open[department.department]} unmountOnExit>
<List
component="div"
sx={{ padding: "0 35px", cursor: "pointer" }}
disablePadding
>
{department.sub_departments.map((subDepartment) => (
<ListItem key={subDepartment}>
<Checkbox
checked={checked[subDepartment] || false}
onChange={() => handleCheck({ department: subDepartment }, department)}
/>
<ListItemText primary={subDepartment} />
</ListItem>
))}
</List>
</Collapse>
</React.Fragment>
))}
</List>
);
};
ReactDOM.createRoot(document.getElementById('app')).render(
<DropDownList
data={[
{
department: 'Fruit',
sub_departments: ['apple', 'pear', 'orange'],
},
{
department: 'Colors',
sub_departments: ['Red', 'Green', 'Blue'],
},
]}
/>
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js" integrity="sha512-8Q6Y9XnTbOE+JNvjBQwJ2H8S+UV4uA6hiRykhdtIyDYZ2TprdNmWOUaKdGzOhyr4dCyk287OejbPvwl7lrfqrQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js" integrity="sha512-MOCpqoRoisCTwJ8vQQiciZv0qcpROCidek3GTFS6KTk2+y7munJIlKCVkFCYY+p3ErYFXCjmFjnfTTRSC1OHWQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://unpkg.com/@mui/[email protected]/umd/material-ui.development.js"></script>
<div id="app"></div>