<form-radiogroup value="other">
<fieldset>
<legend>Gender</legend>
<label>
<input type="radio" name="gender" value="female" />
<span>Female</span>
</label>
<label>
<input type="radio" name="gender" value="male" />
<span>Male</span>
</label>
<label class="selected">
<input type="radio" name="gender" value="other" checked />
<span>Other</span>
</label>
</fieldset>
</form-radiogroup>
<form-radiogroup value="all" class="split-button">
<fieldset>
<legend class="visually-hidden">Filter</legend>
<label class="selected">
<input
type="radio"
class="visually-hidden"
name="filter"
value="all"
checked
/>
<span>All</span>
</label>
<label>
<input
type="radio"
class="visually-hidden"
name="filter"
value="active"
/>
<span>Active</span>
</label>
<label>
<input
type="radio"
class="visually-hidden"
name="filter"
value="completed"
/>
<span>Completed</span>
</label>
</fieldset>
</form-radiogroup>
form-radiogroup {
display: inline-block;
> fieldset {
display: flex;
gap: var(--space-m);
border: none;
margin: 0;
padding: var(--space-xs) 0 var(--space-s);
}
& legend,
& label {
font-size: var(--font-size-s);
}
& label {
border-radius: var(--space-xs);
&:focus-within {
box-shadow: 0 0 var(--space-xxs) 2px var(--color-selection);
}
& input:focus {
outline: none;
box-shadow: none;
}
}
&.split-button {
& fieldset {
gap: 0;
padding: 0;
}
& label {
display: inline-block;
box-sizing: border-box;
height: var(--input-height);
min-width: var(--input-height);
border: 1px solid var(--color-border);
border-radius: 0;
border-left-width: 0;
background-color: var(--color-secondary);
color: var(--color-text);
padding: var(--space-xs) var(--space-s);
cursor: pointer;
line-height: var(--line-height-s);
opacity: var(--opacity-dimmed);
transition: opacity var(--transition-short) var(--easing-inout);
&:hover {
background-color: var(--color-secondary-hover);
opacity: var(--opacity-solid);
}
&:active {
background-color: var(--color-secondary-active);
}
&:focus-within {
z-index: 1;
}
&.selected {
color: var(--color-text-inverted);
background-color: var(--color-primary);
border-color: var(--color-primary-active);
&:hover {
background-color: var(--color-primary-hover);
}
&:active {
background-color: var(--color-primary-active);
}
}
}
& legend + label {
border-radius: var(--space-xs) 0 0 var(--space-xs);
border-left-width: 1px;
}
& label:last-child {
border-radius: 0 var(--space-xs) var(--space-xs) 0;
}
}
}
import {
type Component,
component,
fromEvents,
setAttribute,
setProperty,
toggleClass,
} from '../../..'
import { manageFocusOnKeydown } from '../../functions/event-listener/manage-focus-on-keydown'
export type FormRadiogroupProps = {
value: string
}
const changeHandler = ({ target }) => target.value
const keyupHandler = ({ event, target }) => {
if (event.key === 'Enter') target.click()
}
export default component(
'form-radiogroup',
{
value: fromEvents(
(host: HTMLElement) =>
host.querySelector<HTMLInputElement>('input:checked')?.value ??
'',
'input',
{
change: changeHandler,
keyup: keyupHandler,
},
),
},
(el, { all }) => [
setAttribute('value'),
all(
'input',
setProperty('tabIndex', target =>
target.value === el.value ? 0 : -1,
),
...manageFocusOnKeydown(
Array.from(el.querySelectorAll<HTMLInputElement>('input')),
inputs => inputs.findIndex(input => input.checked),
),
),
all(
'label',
toggleClass(
'selected',
target => el.value === target.querySelector('input')?.value,
),
),
],
)
declare global {
interface HTMLElementTagNameMap {
'form-radiogroup': Component<FormRadiogroupProps>
}
}