Checkbox group
v0.0.10
mix mishka.ui.gen.mob checkbox_group
The Mob Checkbox Group is a labeled set of checkboxes with an optional select all parent, for iOS and Android apps built with Mob. It is part of Mishka Chelekom and is the native port of the headless Checkbox Group for Phoenix LiveView.
Overview of the Mob Checkbox Group component for iOS and Android
MishkaCheckboxGroup draws every row, and the parent, with the Checkbox component. The parent is an ordinary checkbox whose indeterminate state the group derives from its rows.
A checkbox group is mostly selection bookkeeping. The group exposes it as three helpers, so each handler does not derive it again:
toggle/2adds or removes one id from the selection.select_all/2returns what a tap on the parent should produce: all ids, unless every id is already selected, in which case none. A partly selected group fills up and does not clear.parent_state/2returns the{checked?, indeterminate?}that the parent should render.
MishkaCheckboxGroup.toggle([:a], :b)
# [:a, :b]
MishkaCheckboxGroup.select_all([:a], [:a, :b])
# [:a, :b]
MishkaCheckboxGroup.parent_state([:a], [:a, :b])
# {false, true}Write rows as <MishkaCheckboxGroupItem> tags, one per row, the native version of the Phoenix component's <:checkbox> slot. Each tag takes id, label and disabled. An item has no on_* prop of its own. The group's single on_change serves every row, because each row reports its own id, and the message arrives in handle_info/2. When the rows come from data, item/3 builds the identical node.
Set select_all to draw the parent above the rows with a divider under it. select_all_label changes its text, which defaults to Select all.
Give the group an id to name each row for device tests. With id="langs", the :otp row is tagged langs-otp-checked or langs-otp-empty, and the parent is tagged langs-all-mixed when only some rows are selected. The marks are drawn, not typed, so these tags are the only thing a device test can read.
Generate the checkbox group component
Run this from your Mob app's root to add the component to lib/<app>/components/:
mix mishka.ui.gen.mob checkbox_groupThe generator registers the component's tag, so you can use it in ~MOB markup. It also copies the shared Event module and generates the Checkbox component, because the group draws its rows with it. The examples on this page use the mishka_ prefix of the showcase app (--module-prefix mishka_), so the tag is <MishkaCheckboxGroup>. Without a prefix the tag is <CheckboxGroup>. To generate the prefixed version, run:
mix mishka.ui.gen.mob checkbox_group --module-prefix mishka_Checkbox group with select all
Set select_all and the group draws a parent checkbox above the rows. The parent shows the mixed state when only some rows are selected. Rows are <MishkaCheckboxGroupItem> tags, and the group derives the parent state from them.
The row handler receives {:tap, {tag, item_id}} and the parent handler receives {:tap, tag}. select_all/2 takes the current selection and the list of every row id, which the example keeps in @all_ids.
Checkbox group with select all
Left: the example as it loads. Right: after a tap. The select all parent ticks every row at once.
<MishkaCheckboxGroup
value={@value}
select_all={true}
on_change={:item}
on_select_all={:all}
id="langs"
>
<MishkaCheckboxGroupItem id={:beam} label="BEAM" />
<MishkaCheckboxGroupItem id={:otp} label="OTP" />
<MishkaCheckboxGroupItem id={:phoenix} label="Phoenix" />
<MishkaCheckboxGroupItem id={:ecto} label="Ecto" />
</MishkaCheckboxGroup>
# No item tag carries its own on_*: one on_change serves every row,
# because each reports its own id. An item tap carries that id; the
# parent's does not need to, because there is only one parent. Both
# reducers live in the component so a screen never re-derives them.
def handle_info({:tap, {:item, id}}, socket) do
{:noreply, Mob.Socket.assign(socket, :value,
MishkaCheckboxGroup.toggle(socket.assigns.value, id))}
end
def handle_info({:tap, :all}, socket) do
{:noreply, Mob.Socket.assign(socket, :value,
MishkaCheckboxGroup.select_all(socket.assigns.value, @all_ids))}
end
def handle_info(_msg, socket), do: {:noreply, socket}Checkbox group rows from data
When the rows come from a list, item/3 builds the same node that the tag does, so you can use either. Call it with an id and a label, as in the example. This group leaves select_all off, so it has no parent and no divider.
Checkbox group rows from data
@channels [{:email, "Email"}, {:sms, "SMS"}, {:push, "Push"}]
# A comprehension is easier to write than generated markup, so reach for
# item/2 here and for the tag when the rows are literal.
<MishkaCheckboxGroup value={@value} on_change={:item}>
{Enum.map(@channels, fn {id, label} -> item(id, label) end)}
</MishkaCheckboxGroup>
def handle_info({:tap, {:item, id}}, socket) do
{:noreply, Mob.Socket.assign(socket, :value,
MishkaCheckboxGroup.toggle(socket.assigns.value, id))}
endDisabled checkbox group
Put disabled on an item tag to disable one row, or on the group to disable every row and the parent.
Disabled checkbox group
<MishkaCheckboxGroup value={@value} on_change={:item}>
<MishkaCheckboxGroupItem id={:beam} label="BEAM" />
<MishkaCheckboxGroupItem id={:ecto} label="Ecto" disabled={true} />
</MishkaCheckboxGroup>
<MishkaCheckboxGroup disabled={true} select_all={true}>…</MishkaCheckboxGroup>Props
| Prop | Type | Default | Description |
|---|---|---|---|
value | list of ids | [] | The selected ids. |
label | string | nil | Group heading. |
select_all | boolean | false | Draws the parent checkbox. |
select_all_label | string | "Select all" | Text of the parent checkbox. |
disabled | boolean | false | Disables every row, the parent included. |
on_change | event tag | None | Sent as {:tap, {tag, item_id}}. |
on_select_all | event tag | None | Sent as {:tap, tag} for the parent. |
space | number | 12 | Gap between rows. |
color and size | see Checkbox | :primary and 22 | Passed to every row, the parent included. See Checkbox. |
id | string | nil | Prefix for each row's test tag, such as langs-otp-checked. |
<MishkaCheckboxGroupItem> | slot | None | One row, with id (what on_change reports), label and disabled. |
item/3 | builder | None | Builds the same node as the item tag, for rows that come from data. |
toggle/2, select_all/2 and parent_state/2 | helpers | None | The selection helpers, so each screen does not derive them again. |
Events
Your screen receives {:tap, {tag, item_id}} from on_change for a row and {:tap, tag} from on_select_all for the parent, both in handle_info/2. The parent message needs no id, because a group has one parent. These handlers use the group's toggle/2 and select_all/2 helpers:
def handle_info({:tap, {:item, id}}, socket) do
{:noreply, Mob.Socket.assign(socket, :value,
MishkaCheckboxGroup.toggle(socket.assigns.value, id))}
end
def handle_info({:tap, :all}, socket) do
{:noreply, Mob.Socket.assign(socket, :value,
MishkaCheckboxGroup.select_all(socket.assigns.value, @all_ids))}
end
def handle_info(_msg, socket), do: {:noreply, socket}Differences from the web component
nameis not ported. It is HTML form plumbing.- The
*_classattributes are not ported. - The
indicator_iconslot is not ported, because the Checkbox draws the tick and the dash.
Attributes
| Name | Type | Default | Values | Required | Description |
|---|---|---|---|---|---|
value
|
list of ids
|
[]
| — | No | The selected ids. |
label
|
string
| — | — | No | Group heading. |
select_all
|
boolean
|
false
| — | No | Render the tristate parent. |
select_all_label
|
string
|
"Select all"
| — | No | Parent's label. |
disabled
|
boolean
|
false
| — | No | Disables every row, parent included. |
on_change
|
event tag (atom)
| — | — | No | Sent as `{:tap, {tag, item_id}}`. |
on_select_all
|
event tag (atom)
| — | — | No | Sent as `{:tap, tag}` for the parent. |
space
|
number
|
12
| — | No | Gap between rows. |
color / size
|
see Checkbox
| — | — | No | Passed to every row. |
id
|
string
| — | — | No | Prefix for each row's test tag. |
Options
Each option takes one or more of its values, separated by commas.
| Option | Values |
|---|---|
--type
|
checkbox_group
|
--module
| A module name of your own |
What it generates
mix mishka.ui.gen.mob checkbox_group