(frontend) add Switch Field component with custom layout handling

Introduce new Field variant using Switch input with different props
structure from other input components.

Displays description after switch component rather than mixed with
label due to layout requirements, preventing reuse of standard label
and description composition patterns.
This commit is contained in:
lebaudantoine
2025-08-26 23:24:59 +02:00
committed by aleb_the_flash
parent 34fde10854
commit 740355d494
2 changed files with 49 additions and 11 deletions

View File

@@ -20,6 +20,7 @@ import { Checkbox } from './Checkbox'
import { Select } from './Select'
import { Text } from './Text'
import { Div } from './Div'
import { Switch, type SwitchProps } from './Switch'
import { css } from '@/styled-system/css'
const FieldWrapper = styled('div', {
@@ -67,6 +68,7 @@ type PartialSelectProps<T extends object> = Omit<
SelectProps<T>,
OmittedRACProps
>
type PartialSwitchProps = Omit<SwitchProps, OmittedRACProps>
type FieldProps<T extends object> = (
| ({
type: 'text'
@@ -101,6 +103,11 @@ type FieldProps<T extends object> = (
validate?: (value: T) => ReactNode | ReactNode[] | true | null | undefined
} & Items<string> &
PartialSelectProps<T>)
| ({
type: 'switch'
items?: never
validate?: never
} & PartialSwitchProps)
) & {
label: string
description?: string
@@ -250,6 +257,34 @@ export const Field = <T extends object>({
</FieldWrapper>
)
}
if (type === 'switch') {
return (
<FieldWrapper {...props.wrapperProps}>
<div
className={css({
display: 'flex',
justifyContent: 'space-between',
})}
>
{label}
<Switch aria-label={label} {...(props as PartialSwitchProps)} />
</div>
{description && (
<Text
variant="note"
wrap={'pretty'}
className={css({
textStyle: 'sm',
marginBottom: '0.5rem',
})}
>
{description}
</Text>
)}
</FieldWrapper>
)
}
}
const FieldItem = ({

View File

@@ -4,7 +4,6 @@ import {
} from 'react-aria-components'
import { styled } from '@/styled-system/jsx'
import { StyledVariantProps } from '@/styled-system/types'
import { ReactNode } from 'react'
export const StyledSwitch = styled(RACSwitch, {
base: {
@@ -100,21 +99,25 @@ export const StyledSwitch = styled(RACSwitch, {
})
export type SwitchProps = StyledVariantProps<typeof StyledSwitch> &
RACSwitchProps & { children: ReactNode }
RACSwitchProps
/**
* Styled RAC Switch.
*/
export const Switch = ({ children, ...props }: SwitchProps) => (
<StyledSwitch {...props}>
<div className="indicator">
<span className="checkmark" aria-hidden="true">
</span>
<span className="cross" aria-hidden="true">
</span>
</div>
{children}
{(renderProps) => (
<>
<div className="indicator">
<span className="checkmark" aria-hidden="true">
</span>
<span className="cross" aria-hidden="true">
</span>
</div>
{typeof children === 'function' ? children(renderProps) : children}
</>
)}
</StyledSwitch>
)