A CheckboxGroup serves as the basis for multiple selection of any element.
CheckboxGroups are created using the generic component factory function fun <T> checkboxGroup()
.
The type parameter T
can be replaced by any type, e.g. a domain class.
An option can be selected or deselected by clicking on that option or by pressing the Space key if the option is focused. Any number of options can be selected. The focus can be switched by using the standard browser mechanisms like Tab or Shift + Tab keys.
It is therefore mandatory to specify a data stream or a store of a List<T>
as data binding via the value
property.
The component supports two-way data binding, i.e. it reflects selected elements from the outside by a Flow<List<T>>
but also emits the updated selection to the outside by some Handler
.
The available options are not injected directly into the component as parameter, but one at a time by calling the
factory checkboxGroupOption
and providing the option as first parameter. A typical pattern is therefore the use of a
loop in which this factory function is called accordingly.
Beware: Options cannot be removed from the CheckboxGroup. Once an option is added to the group
by checkboxGroupOption
, it will remain inside forever. So never use some reactive pattern for populating the
CheckboxGroup as some Flow<List<T>>
combined with some call to renderEach
or alike!
If the options change, you must re-render the whole component.
For selecting or deselecting, a Tag
must be created using checkboxGroupOptionToggle
.
// some domain type for this example, a collection to choose from, and an external store
data class Newsletter(val id: Int, val title: String, val description: String, val users: Int)
val mailingList = listOf<Newsletter>(/* ... */)
val subscriptions = storeOf(emptyList<Newsletter>(), job = Job())
checkboxGroup<HTMLFieldSetElement, Newsletter>(tag = RenderContext::fieldset) {
// set up (two-way) data binding
value(subscriptions)
// using a loop is a typical pattern to create the options
mailingList.forEach { option ->
checkboxGroupOption(option) { // needs to be created for each selectable option!
checkboxGroupOptionToggle(tag = RenderContext::label) {
+option.title
}
}
}
}
Since a headless component does not provide any styling, the component supports the user to react to the current selection.
Within the scope of the checkboxGroupOption
factory, the component provides the boolean stream selected
. This can be
used to query whether this option is currently selected or not.
A common pattern is to dynamically add or remove CSS classes. Of course also whole elements can be added or removed from
the DOM depending on the state selected
.
checkboxGroup<HTMLFieldSetElement, Newsletter>(tag = RenderContext::fieldset) {
value(subscriptions)
mailingList.forEach { option ->
checkboxGroupOption(option) {
checkboxGroupOptionToggle(tag = RenderContext::label) {
// use `selected`-Flow with `className` to react to state changes
className(selected.map {
if (it) "ring-2 ring-indigo-500 border-transparent"
else "border-gray-300"
})
}
// render some Icon to support the visual "selected" impression only if option is selected
selected.render {
if (it) {
svg("h-5 w-5 text-indigo-600") {
content(HeroIcons.check_circle)
fill("currentColor")
}
}
}
}
}
}
The CheckboxGroup can be labeled using checkboxGroupLabel
, the individual options can be enriched per
checkboxGroupOptionLabel
and checkboxGroupOptionDescription
with a label and some descriptions.
checkboxGroup<HTMLFieldSetElement, Newsletter>(tag = RenderContext::fieldset) {
value(subscriptions)
// describe the whole checkbox-group itself
checkboxGroupLabel(tag = RenderContext::legend) { +"Select some mailing lists" }
mailingList.forEach { option ->
checkboxGroupOption(option) {
checkboxGroupOptionToggle(tag = RenderContext::label) {
div("flex-1 flex") {
div("flex flex-col") {
// enrich the single options with label and description(s)
checkboxGroupOptionLabel("block text-sm font-medium text-gray-900") {
+option.title
}
checkboxGroupOptionDescription("flex items-center text-sm text-gray-500") {
+option.description
}
checkboxGroupOptionDescription("mt-2 text-sm font-medium text-gray-900") {
+"${option.users} users"
}
}
}
}
}
}
}
Data binding allows the CheckboxGroup component to grab the validation messages and provide its own building
block checkboxGroupValidationMessages
that is only rendered when there are some messages. These messages are exposed
within its scope as a data stream msgs
.
checkboxGroup<HTMLFieldSetElement, Newsletter>(tag = RenderContext::fieldset) {
value(subscriptions)
mailingList.forEach { option ->
checkboxGroupOption(option) {
checkboxGroupOptionToggle(tag = RenderContext::label) {
+option.title
}
}
}
checkboxGroupValidationMessages(tag = RenderContext::ul) {
msgs.renderEach { li { +it.message } }
}
}
Clicking on an element created with checkboxGroupOptionToggle
selects or deselects the corresponding option.
Command | Description |
---|---|
Space when an option-toggle is focused | Selects or deselects the corresponding option. |
Tab / Shift + Tab | Moves the focus to the next / previous element (if no other focusable element is added in between or other manual changes to the focus management of the browser is applied!) |
checkboxGroup<T>() {
val value: DatabindingPropert<List<T>>
checkboxGroupLabel() { }
checkboxGroupValidationMessages() {
val msgs: Flow<List<ComponentValidationMessage>>
}
// for each T {
checkboxGroupOption(option: T) {
val selected: Flow<Boolean>
checkboxGroupOptionToggle() { }
checkboxGroupOptionLabel() { }
checkboxGroupOptionDescription() { } // use multiple times
}
// }
}
Parameters: classes
, id
, scope
, tag
, initialize
Default-Tag: div
Scope property | Typ | Description |
---|---|---|
value |
DatabindingProperty<List<T>> |
Mandatory (two-way) data binding for any number of selected options. |
Available in the scope of: checkboxGroup
Parameters: classes
, scope
, tag
, initialize
Default-Tag: label
Available in the scope of: checkboxGroup
Parameters: classes
, scope
, tag
, initialize
Default-Tag: div
Scope property | Typ | Description |
---|---|---|
msgs |
Flow<List<ComponentValidationMessage>> |
provides a data stream with a list of ComponentValidationMessage s |
Available in the scope of: checkboxGroup
Parameters:
option: T
: Mandatory instance of an option that this block should handle.classes
, scope
, tag
, initialize
Default-Tag: div
Scope property | Typ | Description |
---|---|---|
selected |
Flow<Boolean> |
This data stream provides the selection state of its managed option: true the option is part of the selection, false elsewhere. |
Available in the scope of: checkboxGroupOption
Parameters: classes
, scope
, tag
, initialize
Default-Tag: div
Available in the scope of: checkboxGroupOption
Parameters: classes
, scope
, tag
, initialize
Default-Tag: label
Available in the scope of: checkboxGroupOption
Parameters: classes
, scope
, tag
, initialize
Default-Tag: span