const Checkbox = antd.Checkbox;
const CheckboxGroup = Checkbox.Group;
class Row extends React.Component {
isAllChecked = () => {
const { options, checked } = this.props;
return checked.length === options.length;
};
isIndeterminate = () => {
const { options, checked } = this.props;
return (
checked.length > 0 && checked.length < options.length
);
};
render() {
const {
options,
checked,
onChange,
onToggleAll,
stateKey,
label,
} = this.props; //all data and behaviour is passed by App
return (
<div>
<div className="site-checkbox-all-wrapper">
<Checkbox
indeterminate={this.isIndeterminate()}
onChange={e =>
onToggleAll(e.target.checked, stateKey)
}
checked={this.isAllChecked()}
>
Check all {label}
</Checkbox>
<CheckboxGroup
options={options}
value={checked}
onChange={val => {
onChange(stateKey, val);
}}
/>
</div>
</div>
);
}
}
//helper from https://gist.github.com/amsterdamharu/659bb39912096e74ba1c8c676948d5d9
const REMOVE = () => REMOVE;
const get = (object, path, defaultValue) => {
const recur = (current, path) => {
if (current === undefined) {
return defaultValue;
}
if (path.length === 0) {
return current;
}
return recur(current[path[0]], path.slice(1));
};
return recur(object, path);
};
const set = (object, path, callback) => {
const setKey = (current, key, value) => {
if (Array.isArray(current)) {
return value === REMOVE
? current.filter((_, i) => key !== i)
: current.map((c, i) => (i === key ? value : c));
}
return value === REMOVE
? Object.entries(current).reduce((result, [k, v]) => {
if (k !== key) {
result[k] = v;
}
return result;
}, {})
: { ...current, [key]: value };
};
const recur = (current, path) => {
if (path.length === 1) {
return setKey(
current,
path[0],
callback(current[path[0]])
);
}
return setKey(
current,
path[0],
recur(current[path[0]], path.slice(1))
);
};
return recur(object, path, callback);
};
class App extends React.Component {
state = {
permission: { group: [] },
topGroup: [],
some: { other: [{ nested: { state: [] } }] },
};
permissionsKey = ['permission', 'group']; //where to find permissions in state
topGroupKey = ['topGroup']; //where to find top group in state
someKey = ['some', 'other', 0, 'nested', 'state']; //where other group is in state
onChange = (key, value) => {
//use set helper to set state
this.setState(set(this.state, key, arr => value));
};
isIndeterminate = () =>
!this.isEverythingChecked() &&
[
this.permissionsKey,
this.topGroupKey,
this.someKey,
].reduce(
(result, key) =>
result || get(this.state, key).length,
false
);
toggleEveryting = e => {
const checked = e.target.checked;
this.setState(
[
this.permissionsKey,
this.topGroupKey,
this.someKey,
].reduce(
(result, key) =>
set(result, key, () =>
checked
? this.plainOptions.map(({ value }) => value)
: []
),
this.state
)
);
};
onToggleAll = (checked, key) => {
this.setState(
//use set helper to set state
set(this.state, key, () =>
checked
? this.plainOptions.map(({ value }) => value)
: []
)
);
};
isEverythingChecked = () =>
[
this.permissionsKey,
this.topGroupKey,
this.someKey,
].reduce(
(result, key) =>
result &&
get(this.state, key).length ===
this.plainOptions.length,
true
);
plainOptions = [
{ value: 1, name: 'Apple' },
{ value: 2, name: 'Pear' },
{ value: 3, name: 'Orange' },
];
render() {
return (
<React.Fragment>
<h1>App state</h1>
{JSON.stringify(this.state)}
<div>
<Checkbox
indeterminate={this.isIndeterminate()}
onChange={this.toggleEveryting}
checked={this.isEverythingChecked()}
>
Toggle everything
</Checkbox>
</div>
{[
{ label: 'group', stateKey: this.permissionsKey },
{ label: 'top', stateKey: this.topGroupKey },
{ label: 'other', stateKey: this.someKey },
].map(({ label, stateKey }) => (
<Row
key={label}
options={this.plainOptions}
// use getter to get state selected value
// for this particular group
checked={get(this.state, stateKey)}
label={label}
onChange={this.onChange} //change behaviour from App
onToggleAll={this.onToggleAll} //toggle all from App
//state key to indicate what state needs to change
// used in setState in App and passed to set helper
stateKey={stateKey}
/>
))}
</React.Fragment>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<link href="https://cdnjs.cloudflare.com/ajax/libs/antd/4.0.3/antd.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/antd/4.0.3/antd.js"></script>
<div id="root"></div>