Skip to content

Commit

Permalink
chore: RadioGroup & Radio
Browse files Browse the repository at this point in the history
  • Loading branch information
lerte committed Oct 15, 2024
1 parent 47f313d commit 9f55204
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 115 deletions.
54 changes: 29 additions & 25 deletions apps/docs/src/usages/radio.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,35 @@ import { Label, Radio, RadioGroup } from 'actify'
export default () => {
return (
<div className="flex flex-col gap-4">
<h2>Without label</h2>
<form className="flex gap-6">
<RadioGroup name="frontend" defaultValue="actify">
<Radio value="react" />
<Radio value="vue" />
<Radio value="svelte" />
</RadioGroup>
</form>
<h2>With label</h2>
<form className="flex flex-col gap-6">
<RadioGroup name="stack" defaultValue="actify">
<Label className="flex items-center gap-2">
<Radio value="react" />
<span>React</span>
</Label>
<Label className="flex items-center gap-2">
<Radio value="actify" />
<span>Actify</span>
</Label>
<Label className="flex items-center gap-2">
<Radio value="ngroker" />
<span>Ngroker</span>
</Label>
</RadioGroup>
</form>
<h2>RadioGroup With label</h2>

<RadioGroup
label="project"
name="project"
defaultValue="taildoor"
className="flex items-center gap-4"
>
<Radio value="actify" />
<Radio value="ngroker" />
<Radio value="taildoor" />
</RadioGroup>

<h2>Radio With label</h2>

<RadioGroup name="project" defaultValue="actify">
<Label className="flex items-center gap-4">
<Radio value="actify" />
<span>Actify</span>
</Label>
<Label className="flex items-center gap-4">
<Radio value="ngroker" />
<span>Ngroker</span>
</Label>
<Label className="flex items-center gap-4">
<Radio value="taildoor" />
<span>Taildoor</span>
</Label>
</RadioGroup>
</div>
)
}
68 changes: 28 additions & 40 deletions packages/actify/src/components/Radio/Radio.tsx
Original file line number Diff line number Diff line change
@@ -1,49 +1,46 @@
import { FocusRing } from '../FocusRing'
import { AriaRadioProps, mergeProps, useFocusRing, useRadio } from 'react-aria'

import { FocusRing } from '../FocusRing/FocusRing'
import { Label } from '../Label'
import { RadioContext } from './RadioGroup'
import { RadioGroupState } from 'react-stately'
import React from 'react'
import { Ripple } from '../Ripple'
import { Ripple } from '../Ripple/Ripple'
import clsx from 'clsx'
import styles from './radio.module.css'
import { useRadioContext } from './RadioGroupContext'

interface RadioProps
extends Omit<
React.ComponentProps<'input'>,
'checked' | 'defaultValue' | 'name' | 'onChange'
> {}
type RadioProps = {} & AriaRadioProps & React.ComponentProps<'input'>
const Radio = (props: RadioProps) => {
const { children } = props
const state = React.useContext(RadioContext) as RadioGroupState
const inputRef = React.useRef(null)
const { inputProps, isSelected } = useRadio(props, state, inputRef)

const Radio = ({
id,
value,
disabled,
className,
type = 'radio',
...rest
}: RadioProps) => {
const { name, value: contextValue, onChange } = useRadioContext()
const radioId = id || `actify-radio${React.useId()}`
const { isFocusVisible, focusProps } = useFocusRing()
const cutoutId = `radio-cutout${React.useId()}`

const checked = React.useMemo(
() => value == contextValue,
[value, contextValue]
)

return (
<div className={styles['radio']}>
<Label className={styles['radio']}>
<div
aria-hidden="true"
className={clsx(styles['container'], checked && styles['checked'])}
role="presentation"
className={clsx(styles['container'], isSelected && styles['checked'])}
>
<input
ref={inputRef}
{...mergeProps(inputProps, focusProps)}
className={clsx(styles['input'], props.className)}
/>

<Ripple
id={radioId}
id={inputProps.id}
style={{
inset: 'unset',
borderRadius: '50%',
width: 'var(--md-radio-state-layer-size, 40px)',
height: 'var(--md-radio-state-layer-size, 40px)'
}}
/>
{checked && (
{isFocusVisible && (
<FocusRing
style={{
height: '44px',
Expand Down Expand Up @@ -72,20 +69,11 @@ const Radio = ({
className={clsx(styles['inner'], styles['circle'])}
/>
</svg>
<input
{...rest}
name={name}
type="radio"
id={radioId}
value={value}
checked={checked}
onChange={onChange}
disabled={disabled}
className={clsx(styles['input'], className)}
/>
</div>
</div>
{children}
</Label>
)
}

Radio.displayName = 'Actify.Radio'
export { Radio }
35 changes: 29 additions & 6 deletions packages/actify/src/components/Radio/RadioGroup.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,34 @@
import { RadioContextProps, RadioContextProvider } from './RadioGroupContext'
import { AriaRadioGroupProps, useRadioGroup } from 'react-aria'
import { RadioGroupState, useRadioGroupState } from 'react-stately'

import React from 'react'

interface RadioGroupProps extends RadioContextProps {
children?: React.ReactNode
}
const RadioGroup = ({ children, ...rest }: RadioGroupProps) => {
return <RadioContextProvider {...rest}>{children}</RadioContextProvider>
export const RadioContext = React.createContext<RadioGroupState | {}>({})

type RadioGroupProps = {} & AriaRadioGroupProps & React.ComponentProps<'div'>

const RadioGroup = (props: RadioGroupProps) => {
const { style, className, children, label, description, errorMessage } = props
const state = useRadioGroupState(props)
const { radioGroupProps, labelProps, descriptionProps, errorMessageProps } =
useRadioGroup(props, state)

return (
<div {...radioGroupProps} style={style} className={className}>
<span {...labelProps}>{label}</span>
<RadioContext value={state}>{children}</RadioContext>
{description && (
<div {...descriptionProps} style={{ fontSize: 12 }}>
{description}
</div>
)}
{errorMessage && state.isInvalid && (
<div {...errorMessageProps} style={{ color: 'red', fontSize: 12 }}>
<>{errorMessage}</>
</div>
)}
</div>
)
}

export { RadioGroup }
44 changes: 0 additions & 44 deletions packages/actify/src/components/Radio/RadioGroupContext.tsx

This file was deleted.

0 comments on commit 9f55204

Please sign in to comment.