Forms on ReUI controls: radio-group installed, native checkboxes and radios replaced
Radios (PanelForm) is ReUI's RadioGroup; Check takes rich text; a bare Tick covers the table-cell ticks and the list-row switch. Settings IPv6 mode, Change Theme and all Zones modals no longer draw their own native inputs. Matrix and the data-grid's own selection stay as they are. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
This commit is contained in:
1 parent
7f60dc9960
commit
d2b05a1037
22 files changed
+458
-493
No files matched your search
+13
-2
@@ -25,8 +25,12 @@ export function Check({
|
||||
help,
|
||||
disabled,
|
||||
toggle = false,
|
||||
text,
|
||||
}: {
|
||||
/** The control's accessible name, and the visible text unless `text` replaces it. */
|
||||
label: string
|
||||
/** Visible text with markup in it (a link), when `label` alone would lose it. */
|
||||
text?: ReactNode
|
||||
checked: boolean
|
||||
onChange: (v: boolean) => void
|
||||
help?: ReactNode
|
||||
@@ -52,8 +56,15 @@ export function Check({
|
||||
) : (
|
||||
<Checkbox nativeButton render={<button type="button" />} {...common} />
|
||||
)}
|
||||
<span className={styles.text} onClick={() => !disabled && onChange(!checked)}>
|
||||
{label}
|
||||
<span
|
||||
className={styles.text}
|
||||
onClick={(e) => {
|
||||
// A link inside the text is followed, not a click on the box.
|
||||
if (disabled || (e.target as HTMLElement).closest('a')) return
|
||||
onChange(!checked)
|
||||
}}
|
||||
>
|
||||
{text ?? label}
|
||||
</span>
|
||||
</div>
|
||||
{help && <div className={toggle ? `${styles.help} ${styles.helpSwitch}` : styles.help}>{help}</div>}
|
||||
|
||||
+40
-15
@@ -1,4 +1,5 @@
|
||||
import { createContext, type ReactNode, useContext, useEffect, useRef, useState } from 'react'
|
||||
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'
|
||||
import { Alert } from './Alert'
|
||||
import { Button } from './Button'
|
||||
import { Empty } from './Empty'
|
||||
@@ -242,14 +243,21 @@ export function Trailer({ children }: { children: ReactNode }) {
|
||||
|
||||
export interface RadioOption {
|
||||
value: string
|
||||
label: string
|
||||
/** The option's text; with markup in it (a link), give `name` as well. */
|
||||
label: ReactNode
|
||||
/** The option's accessible name when `label` is not a string. */
|
||||
name?: string
|
||||
help?: ReactNode
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
/*
|
||||
The radio group. It shares its row and its help with `ui/Check`: they are the
|
||||
same control with different cardinality, and when each screen wrote its own, a
|
||||
radio's help and a checkbox's help did not land on the same left edge.
|
||||
The radio group, ReUI's `RadioGroup`. It shares its row and its help with `ui/Check`:
|
||||
they are the same control with different cardinality, and when each screen wrote
|
||||
its own, a radio's help and a checkbox's help did not land on the same left edge.
|
||||
|
||||
The item carries its own name and the text beside it only forwards the click, as in
|
||||
`Check`: a wrapping <label> would name the hidden input as well.
|
||||
*/
|
||||
export function Radios({
|
||||
name,
|
||||
@@ -257,32 +265,49 @@ export function Radios({
|
||||
options,
|
||||
onChange,
|
||||
disabled,
|
||||
label,
|
||||
}: {
|
||||
name: string
|
||||
value: string
|
||||
options: RadioOption[]
|
||||
onChange: (v: string) => void
|
||||
disabled?: boolean
|
||||
/** The group's accessible name, when its heading is not a `GroupRow` label. */
|
||||
label?: string
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<RadioGroup
|
||||
name={name}
|
||||
aria-label={label}
|
||||
value={value}
|
||||
disabled={disabled}
|
||||
onValueChange={(v) => onChange(String(v))}
|
||||
className="gap-0"
|
||||
>
|
||||
{options.map((o) => (
|
||||
<div key={o.value}>
|
||||
<label className={check.check}>
|
||||
<input
|
||||
type="radio"
|
||||
name={name}
|
||||
<div className={check.check}>
|
||||
<RadioGroupItem
|
||||
value={o.value}
|
||||
checked={value === o.value}
|
||||
disabled={disabled}
|
||||
onChange={() => onChange(o.value)}
|
||||
nativeButton
|
||||
render={<button type="button" />}
|
||||
disabled={o.disabled}
|
||||
aria-label={o.name ?? (typeof o.label === 'string' ? o.label : o.value)}
|
||||
/>
|
||||
<span className={check.text}>{o.label}</span>
|
||||
</label>
|
||||
<span
|
||||
className={check.text}
|
||||
onClick={(e) => {
|
||||
if (disabled || o.disabled || (e.target as HTMLElement).closest('a')) return
|
||||
onChange(o.value)
|
||||
}}
|
||||
>
|
||||
{o.label}
|
||||
</span>
|
||||
</div>
|
||||
{o.help && <div className={check.help}>{o.help}</div>}
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
</RadioGroup>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import { Checkbox } from '@/components/ui/checkbox'
|
||||
import { Switch } from '@/components/ui/switch'
|
||||
|
||||
/*
|
||||
The bare control, with no text beside it: a table cell's tick (a row selection, a
|
||||
permission), or the enable switch of a list row. It is `ui/Check` without the row and
|
||||
its label, and it is named the same way: through `aria-label`, announced as a
|
||||
checkbox either way (a switch too), so the tests and the keyboard keep one role.
|
||||
*/
|
||||
export function Tick({
|
||||
label,
|
||||
checked,
|
||||
onChange,
|
||||
disabled,
|
||||
toggle = false,
|
||||
indeterminate,
|
||||
}: {
|
||||
/** The accessible name: there is no visible text. */
|
||||
label: string
|
||||
checked: boolean
|
||||
onChange: (v: boolean) => void
|
||||
disabled?: boolean
|
||||
/** A setting that stays put (a switch), not a selection. */
|
||||
toggle?: boolean
|
||||
indeterminate?: boolean
|
||||
}) {
|
||||
const common = {
|
||||
checked,
|
||||
disabled,
|
||||
'aria-label': label,
|
||||
onCheckedChange: (v: boolean) => onChange(v),
|
||||
}
|
||||
return toggle ? (
|
||||
<Switch role="checkbox" nativeButton render={<button type="button" />} {...common} />
|
||||
) : (
|
||||
<Checkbox nativeButton render={<button type="button" />} indeterminate={indeterminate} {...common} />
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user