You can use a checkbox for the Select All input and handle the toggling of that in a hook. This method does not require network trips for selecting/deselecting and will return to the original selection when cancel is clicked, or the dropdown is closed. There is a JS.dispatch("cancel") on the close trigger.
<div phx-hook="ColumnOptionsFilter" ...>
<Form ...>
...
<Field name="__select_all__">
<Checkbox
value={Enum.all?(@options, & &1[:selected])}
opts={
"phx-click": JS.dispatch("select-all"),
role: "menuitemcheckbox"
}
/>
<Label>Select All</Label>
</Field>
... other inputs
</Form>
</div>
The Hook:
const ColumnOptionFilter = {
checkboxes() {
return Array.from(
this.el.querySelectorAll(`#${this.el.id}-options input[type=checkbox]`)
);
},
mounted() {
this.state = {
checkboxes: null,
initialSelection: null,
selectAllCheckbox: null,
};
this.state.checkboxes = this.checkboxes();
this.state.initialSelection = selection(this.state.checkboxes);
this.state.selectAllCheckbox = this.el.querySelector(
`#${this.el.id}-form___select_all__`
);
if (!this.state.selectAllCheckbox) {
throw new Error(
`Expected a checkbox with id '#${this.el.id}-select-all'`
);
}
this.el.addEventListener("select-all", (e) => {
const allSelected = areAllSelected(this.state.checkboxes);
this.state.checkboxes.forEach(
(checkbox) => (checkbox.checked = !allSelected)
);
});
this.el.addEventListener("cancel", (e) => {
this.state.checkboxes.forEach((checkbox) =>
this.state.initialSelection.includes(checkbox.name)
? (checkbox.checked = true)
: (checkbox.checked = false)
);
this.state.selectAllCheckbox.value = areAllSelected(
this.state.checkboxes
);
});
},
updated() {
this.state.checkboxes = this.checkboxes();
this.state.initialSelection = selection(this.state.checkboxes);
this.state.selectAllCheckbox.value = areAllSelected(this.state.checkboxes);
},
};
function selection(checkboxes) {
return checkboxes
.filter((checkbox) => checkbox.checked === true)
.map((checkbox) => checkbox.name);
}
function areAllSelected(all) {
return all.reduce((acc, checkbox) => {
return checkbox.checked === true && acc;
}, true);
}
export { ColumnOptionFilter };
The component takes a list of options of the form %{id: 1, value: "Some Value", available: true/false, selected: true/false}






















