Skip to content

Commit

Permalink
react-aria
Browse files Browse the repository at this point in the history
  • Loading branch information
lerte committed Nov 12, 2024
1 parent 65de8d5 commit cef9af8
Show file tree
Hide file tree
Showing 10 changed files with 128 additions and 97 deletions.
33 changes: 15 additions & 18 deletions apps/docs/src/usages/radio.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,33 @@
import { Label, Radio, RadioGroup } from 'actify'
import { Radio, RadioGroup } from 'actify'

export default () => {
return (
<div className="flex flex-col gap-4">
<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" />
<Radio value="actify" aria-label="actify" />
<Radio value="ngroker" aria-label="ngroker" />
<Radio value="taildoor" aria-label="taildoor" />
<Radio value="hugola" aria-label="hugola" />
</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
orientation="vertical"
aria-label="project"
name="project"
defaultValue="actify"
>
<Radio value="actify">Actify</Radio>
<Radio value="ngroker">Ngroker</Radio>
<Radio value="taildoor">Taildoor</Radio>
<Radio value="hugola">Hugola</Radio>
</RadioGroup>
</div>
)
Expand Down
15 changes: 8 additions & 7 deletions apps/docs/src/usages/switch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,21 @@ export default () => {
return (
<>
<div className="flex gap-2 flex-wrap justify-between">
<Switch color="primary" />
<Switch color="secondary" />
<Switch color="tertiary" />
<Switch defaultSelected color="error" />
<Switch isDisabled />
<Switch color="primary" aria-label="primary" />
<Switch color="secondary" aria-label="secondary" />
<Switch color="tertiary" aria-label="tertiary" />
<Switch defaultSelected color="error" aria-label="error" />
<Switch isDisabled aria-label="disabled" />
</div>

<div className="mt-4 flex gap-2">
<Switch>label</Switch>
<Label className="flex items-center gap-2">
<Switch color="primary" />
<Switch color="primary" aria-label="primary" />
<span>with label</span>
</Label>
<Label className="flex items-center gap-2">
<Switch color="primary" defaultSelected icons />
<Switch color="primary" defaultSelected icons aria-label="primary" />
<span>with icons</span>
</Label>
</div>
Expand Down
5 changes: 3 additions & 2 deletions packages/actify/src/components/Field/Field.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { SupportingText, SupportingTextProps } from './SupportingText'
import { cubicBezier, motion } from 'framer-motion'

import { LabelAria } from 'react-aria'
import React from 'react'
import clsx from 'clsx'
import styles from './styles/field.module.css'
Expand Down Expand Up @@ -109,10 +110,10 @@ const Field = (props: FieldProps) => {
<div className={styles['container']}>
<div className={styles['start']}>{leadingIcon}</div>
<div className={styles['middle']}>
<div className={styles['label-wrapper']}>
<span className={styles['label-wrapper']}>
{restingLabel}
{outline ? '' : floatingLabel}
</div>
</span>
<div className={styles['content']}>{children}</div>
</div>
<div className={styles['end']}>{trailingIcon}</div>
Expand Down
110 changes: 61 additions & 49 deletions packages/actify/src/components/Radio/Radio.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,69 +9,81 @@ import { Ripple } from '../Ripple/Ripple'
import clsx from 'clsx'
import styles from './radio.module.css'

type RadioProps = {} & AriaRadioProps & React.ComponentProps<'input'>
interface RadioProps extends AriaRadioProps {
style?: React.CSSProperties
className?: string
}

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 { inputProps, labelProps, isSelected } = useRadio(
props,
state,
inputRef
)

const { isFocusVisible, focusProps } = useFocusRing()
const cutoutId = `radio-cutout${React.useId()}`

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

<Ripple
id={inputProps.id}
style={{
inset: 'unset',
borderRadius: '50%',
width: 'var(--md-radio-state-layer-size, 40px)',
height: 'var(--md-radio-state-layer-size, 40px)'
}}
/>
{isFocusVisible && (
<FocusRing
<Ripple
id={inputProps.id}
style={{
height: '44px',
inset: 'unset',
width: '44px',
borderRadius: '50%'
borderRadius: '50%',
width: 'var(--md-radio-state-layer-size, 40px)',
height: 'var(--md-radio-state-layer-size, 40px)'
}}
/>
)}
<svg className={styles['icon']} viewBox="0 0 20 20">
<mask id={cutoutId}>
<rect width="100%" height="100%" fill="white" />
<circle cx="10" cy="10" r="8" fill="black" />
</mask>
<circle
r="10"
cx="10"
cy="10"
mask={`url(#${cutoutId})`}
className={clsx(styles['outer'], styles['circle'])}
/>
<circle
r="5"
cx="10"
cy="10"
className={clsx(styles['inner'], styles['circle'])}
/>
</svg>
</div>
{children}
</Label>
{isFocusVisible && (
<FocusRing
style={{
height: '44px',
inset: 'unset',
width: '44px',
borderRadius: '50%'
}}
/>
)}
<svg className={styles['icon']} viewBox="0 0 20 20">
<mask id={cutoutId}>
<rect width="100%" height="100%" fill="white" />
<circle cx="10" cy="10" r="8" fill="black" />
</mask>
<circle
r="10"
cx="10"
cy="10"
mask={`url(#${cutoutId})`}
className={clsx(styles['outer'], styles['circle'])}
/>
<circle
r="5"
cx="10"
cy="10"
className={clsx(styles['inner'], styles['circle'])}
/>
</svg>
</div>
</Label>
<Label {...labelProps}>{props.children}</Label>
</div>
)
}

Expand Down
6 changes: 3 additions & 3 deletions packages/actify/src/components/Radio/RadioGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ interface RadioGroupProps extends AriaRadioGroupProps {
}

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

return (
<div {...radioGroupProps} style={style} className={className}>
{label && <Label {...labelProps}>{label}</Label>}
<RadioContext value={state}>{children}</RadioContext>
{props.label && <Label {...labelProps}>{props.label}</Label>}
<RadioContext value={state}>{props.children}</RadioContext>
{description && (
<div {...descriptionProps} style={{ fontSize: 12 }}>
{description}
Expand Down
10 changes: 9 additions & 1 deletion packages/actify/src/components/Radio/radio.module.css
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
.radio-wrapper {
gap: 8px;
height: 48px;
display: flex;
align-items: center;
}
.radio {
outline: none;
display: inline-flex;
height: var(--md-radio-icon-size, 20px);
outline: none;
position: relative;
vertical-align: top;
width: var(--md-radio-icon-size, 20px);
Expand Down Expand Up @@ -34,6 +40,8 @@
.icon {
fill: var(--md-radio-icon-color, rgb(var(--md-sys-color-on-surface-variant)));
inset: 0;
width: 100%;
height: 100%;
position: absolute;
}
.outer.circle {
Expand Down
29 changes: 18 additions & 11 deletions packages/actify/src/components/Switch/Switch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,26 @@ import {
import React, { useId } from 'react'

import { FocusRing } from './../FocusRing'
import { Label } from '../Label'
import { Ripple } from './../Ripple'
import { VisuallyHidden } from '../VisuallyHidden'
import clsx from 'clsx'
import styles from './switch.module.css'
import { useToggleState } from 'react-stately'

type SwitchProps = {
interface SwitchProps extends AriaCheckboxProps {
icons?: boolean
showOnlySelectedIcon?: boolean
color?: 'primary' | 'secondary' | 'tertiary' | 'error'
} & AriaCheckboxProps
}

const Switch = (props: SwitchProps) => {
const { id, icons, isDisabled, showOnlySelectedIcon } = props
const { id, icons, isDisabled, showOnlySelectedIcon, children } = props

const state = useToggleState(props)
const inputRef = React.useRef(null)

const { inputProps } = useSwitch(props, state, inputRef)
const { inputProps, labelProps } = useSwitch(props, state, inputRef)
const { isFocusVisible, focusProps } = useFocusRing()

const switchId = id || `actify-switch${useId()}`
Expand Down Expand Up @@ -66,13 +68,15 @@ const Switch = (props: SwitchProps) => {
state.isSelected ? styles['selected'] : styles['unselected']
)}
>
<input
role="switch"
id={switchId}
ref={inputRef}
className={styles['touch']}
{...mergeProps(inputProps, focusProps)}
/>
<VisuallyHidden>
<input
role="switch"
id={switchId}
ref={inputRef}
className={styles['touch']}
{...mergeProps(inputProps, focusProps)}
/>
</VisuallyHidden>
{isFocusVisible && <FocusRing />}
<span className={styles['track']}>
<span className={styles['handle-container']}>
Expand All @@ -95,6 +99,9 @@ const Switch = (props: SwitchProps) => {
</span>
</span>
</div>
<Label {...labelProps} id={switchId}>
{children}
</Label>
</div>
)
}
Expand Down
3 changes: 2 additions & 1 deletion packages/actify/src/components/Switch/switch.module.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
.host {
display: inline-flex;
gap: 4px;
outline: none;
display: inline-flex;
vertical-align: top;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
cursor: pointer;
Expand Down
6 changes: 3 additions & 3 deletions packages/actify/src/components/TextFields/TextField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ interface TextFieldProps extends AriaTextFieldProps {
| 'textarea'
}
const TextField = ({
label,
suffixText,
prefixText,
leadingIcon,
Expand All @@ -44,6 +43,7 @@ const TextField = ({

const {
inputProps: _inputProps,
labelProps,
descriptionProps,
errorMessageProps,
isInvalid,
Expand All @@ -67,14 +67,14 @@ const TextField = ({
const populated = mergeProps(_inputProps, inputProps).value ? true : false

return (
<label className={styles[variant]}>
<label {...labelProps} className={styles[variant]}>
<Tag
{...{
label,
leadingIcon,
trailingIcon,
count,
populated,
label: props.label,
focused: isFocused
}}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@ import styles from './visually-hidden.module.css'
interface Props extends VisuallyHiddenProps {
as?: keyof HTMLElementTagNameMap | React.ElementType
}
const VisuallyHidden = ({ as: Tag = 'span', ...props }: Props) => {
const VisuallyHidden = ({ as: Tag = 'span', children, ...props }: Props) => {
const { visuallyHiddenProps } = useVisuallyHidden(props)

return <Tag className={styles['visually-hidden']} {...visuallyHiddenProps} />
return (
<Tag className={styles['visually-hidden']} {...visuallyHiddenProps}>
{children}
</Tag>
)
}

export { VisuallyHidden }

0 comments on commit cef9af8

Please sign in to comment.