-
Notifications
You must be signed in to change notification settings - Fork 508
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
1ee6fd5
commit 3e24e11
Showing
8 changed files
with
304 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
import { useState } from 'react'; | ||
import CheckBoxList from './CheckBoxList'; | ||
import { checkboxList } from './constants'; | ||
import styles from './styles.module.css'; | ||
|
||
const NestedCheckbox = () => { | ||
const [checkList, setCheckList] = useState(checkboxList); | ||
|
||
const toggleAllChildNodes = (list, value) => { | ||
for (let i = 0; i < list.length; i++) { | ||
list[i].checked = value; | ||
toggleAllChildNodes(list[i].children, value); | ||
} | ||
}; | ||
|
||
const dfs = (list, id, value, isFound) => { | ||
if (list.length === 0) return isFound; | ||
for (let i = 0; i < list.length; i++) { | ||
if (list[i].id === id) { | ||
list[i].checked = value; | ||
isFound = true; | ||
toggleAllChildNodes(list[i].children, value); | ||
break; | ||
} | ||
isFound = dfs(list[i].children, id, value, isFound); | ||
if (isFound) break; | ||
} | ||
return isFound; | ||
}; | ||
|
||
const getActiveChildCount = (list) => { | ||
if (list.length === 0) return 0; | ||
let count = 0; | ||
for (let i = 0; i < list.length; i++) { | ||
// If it's checked then increase the count. | ||
if (list[i].checked) { | ||
count += 1; | ||
} | ||
|
||
// If there is no children for the node then we skip the rest of the code. | ||
if (list[i].children.length === 0) continue; | ||
|
||
// we need to get the active child count. | ||
let childCount = getActiveChildCount(list[i].children); | ||
|
||
// If count is not equal then we need to un-check the parent node else we need to check the parent node. | ||
if (childCount !== list[i].children.length) { | ||
count = list[i].checked ? count - 1 : count; | ||
list[i].checked = false; | ||
} else { | ||
count = list[i].checked ? count : count + 1; | ||
list[i].checked = true; | ||
} | ||
} | ||
return count; | ||
}; | ||
|
||
const handleChange = (id, value) => { | ||
let clone = JSON.parse(JSON.stringify(checkList)); | ||
let isFound = false; | ||
let parentIndex = 0; | ||
/** | ||
* Iterate the root node and the child node until we find the target node. | ||
* If we found the target node we need to make all the children of the target flag as the selected value ( true | false ). | ||
*/ | ||
for (let i = 0; i < clone.length; i++) { | ||
if (clone[i].id === id) { | ||
clone[i].checked = value; | ||
isFound = true; | ||
parentIndex = i; | ||
toggleAllChildNodes(clone[i].children, value); | ||
break; | ||
} | ||
|
||
// If the target node is not a root. We need to check all the child nodes. | ||
isFound = dfs(clone[i].children, id, value, false); | ||
|
||
// If we found the targetNode we no need to check the rest of the node. | ||
if (isFound) { | ||
parentIndex = i; | ||
break; | ||
} | ||
} | ||
|
||
/** | ||
* Now we want to check/unCheck the parent node based on the child node values. | ||
* If all the child nodes are check we need to make the parent node check. | ||
* if any one of the child nodes are unChecked we need to make the parent node un-check. | ||
*/ | ||
const childCount = getActiveChildCount(clone[parentIndex].children); | ||
if (clone[parentIndex].children.length > 0) { | ||
if (childCount !== clone[parentIndex].children.length) { | ||
clone[parentIndex].checked = false; | ||
} else { | ||
clone[parentIndex].checked = true; | ||
} | ||
} | ||
|
||
// Finally set the updated list. | ||
setCheckList(clone); | ||
}; | ||
|
||
return ( | ||
<div className={styles.nested_checkbox_wrapper}> | ||
{<CheckBoxList childCheckBoxList={checkList} onCheckBoxChange={handleChange} />} | ||
</div> | ||
); | ||
}; | ||
|
||
export default NestedCheckbox; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
/* eslint-disable react/prop-types */ | ||
import styles from './styles.module.css'; | ||
|
||
const Checkbox = (props) => { | ||
const { id, isChecked, label, onCheckBoxChange } = props; | ||
|
||
return ( | ||
<label className={styles.nested_checkbox_label}> | ||
<input | ||
type="checkbox" | ||
checked={isChecked} | ||
onChange={() => onCheckBoxChange(id, !isChecked)} | ||
/> | ||
{label} | ||
</label> | ||
); | ||
}; | ||
|
||
export default Checkbox; |
31 changes: 31 additions & 0 deletions
31
apps/react/src/challenges/nested-checkbox/CheckBoxList.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
/* eslint-disable react/prop-types */ | ||
import { Fragment } from 'react'; | ||
import Checkbox from './CheckBox'; | ||
import styles from './styles.module.css'; | ||
|
||
const CheckBoxList = (props) => { | ||
const { childCheckBoxList, onCheckBoxChange } = props; | ||
|
||
return ( | ||
<Fragment> | ||
{childCheckBoxList.map((childBox) => ( | ||
<div key={childBox.id} className={styles.nested_checkbox_child}> | ||
<Checkbox | ||
id={childBox.id} | ||
label={childBox.label} | ||
isChecked={childBox.checked} | ||
onCheckBoxChange={onCheckBoxChange} | ||
/> | ||
{childBox.children.length > 0 && ( | ||
<CheckBoxList | ||
childCheckBoxList={childBox.children} | ||
onCheckBoxChange={onCheckBoxChange} | ||
/> | ||
)} | ||
</div> | ||
))} | ||
</Fragment> | ||
); | ||
}; | ||
|
||
export default CheckBoxList; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
export const checkboxList = [ | ||
{ | ||
label: 'p1', | ||
id: 1, | ||
checked: false, | ||
children: [ | ||
{ | ||
label: 'p1-c1', | ||
id: 2, | ||
checked: false, | ||
children: [ | ||
{ | ||
label: 'p1-c1-c1', | ||
id: 3, | ||
checked: false, | ||
children: [], | ||
}, | ||
{ | ||
label: 'p1-c1-c2', | ||
id: 4, | ||
checked: false, | ||
children: [ | ||
{ | ||
label: 'p1-c1-c2-c1', | ||
id: 5, | ||
checked: false, | ||
children: [], | ||
}, | ||
{ | ||
label: 'p1-c1-c2-c2', | ||
id: 6, | ||
checked: false, | ||
children: [ | ||
{ | ||
label: 'p1-c1-c2-c2-c1', | ||
id: 7, | ||
checked: false, | ||
children: [], | ||
}, | ||
{ | ||
label: 'p1-c1-c2-c2-c2', | ||
id: 8, | ||
checked: false, | ||
children: [], | ||
}, | ||
], | ||
}, | ||
{ | ||
label: 'p1-c1-c2-c3', | ||
id: 9, | ||
checked: false, | ||
children: [], | ||
}, | ||
], | ||
}, | ||
], | ||
}, | ||
{ | ||
label: 'p1-c2', | ||
id: 10, | ||
checked: false, | ||
children: [], | ||
}, | ||
{ | ||
label: 'p1-c3', | ||
id: 11, | ||
checked: false, | ||
children: [], | ||
}, | ||
], | ||
}, | ||
{ | ||
label: 'p2', | ||
id: 12, | ||
checked: false, | ||
children: [ | ||
{ | ||
label: 'p2-c1', | ||
id: 13, | ||
checked: false, | ||
children: [], | ||
}, | ||
{ | ||
label: 'p2-c2', | ||
id: 14, | ||
checked: false, | ||
children: [], | ||
}, | ||
], | ||
}, | ||
{ | ||
label: 'p3', | ||
id: 15, | ||
checked: false, | ||
children: [ | ||
{ | ||
label: 'p3-c1', | ||
id: 16, | ||
checked: false, | ||
children: [], | ||
}, | ||
], | ||
}, | ||
{ | ||
label: 'p4', | ||
id: 17, | ||
checked: false, | ||
children: [], | ||
}, | ||
]; |
15 changes: 15 additions & 0 deletions
15
apps/react/src/challenges/nested-checkbox/styles.module.css
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
.nested_checkbox_wrapper { | ||
margin: 20px 0px; | ||
width: 300px; | ||
} | ||
|
||
.nested_checkbox_label { | ||
display: flex; | ||
align-items: center; | ||
gap: 4px; | ||
} | ||
|
||
.nested_checkbox_child { | ||
margin: 10px 0px; | ||
padding: 0px 0px 0px 25px; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters