mirror of
https://github.com/hanzoai/gui.git
synced 2026-10-25 02:31:41 +00:00
Both old names are owned by a different npm account (artemis-prime), so publishing them returns 403 — which is how this surfaced: the 8.0.0 run shipped @hanzogui/logo and @hanzogui/demos depending on [email protected], a version that cannot be published and therefore does not exist. Those two were uninstallable. @hanzo/gui and @hanzogui/* are ours, so the framework now carries the name it is imported by everywhere downstream, and the loader moves under the scope we control. The rewrite is limited to package references. `DEBUG?.startsWith('hanzogui')` is a log-namespace filter and join(dir, 'hanzogui') is a path — rewriting either would change behaviour, so both are untouched. Co-authored-by: Hanzo Dev <[email protected]>
451 lines
11 KiB
Plaintext
451 lines
11 KiB
Plaintext
---
|
|
title: RadioGroup
|
|
description: Use in a form to allow selecting one option from multiple
|
|
name: radio-group
|
|
component: RadioGroup
|
|
package: radio-group
|
|
demoName: RadioGroup
|
|
---
|
|
|
|
<YStack className="is-sticky" />
|
|
|
|
<Tabs id="type" defaultValue="styled">
|
|
<Tabs.List>
|
|
<TooltipSimple label="With Gui's default styles">
|
|
<Tabs.Tab value="styled">Styled</Tabs.Tab>
|
|
</TooltipSimple>
|
|
<TooltipSimple label="No default styles, easy to customize">
|
|
<Tabs.Tab value="unstyled">Unstyled</Tabs.Tab>
|
|
</TooltipSimple>
|
|
<TooltipSimple label="No dependency on Gui's core">
|
|
<Tabs.Tab value="headless" label="No styles and no dependency on Gui's styling">Headless</Tabs.Tab>
|
|
</TooltipSimple>
|
|
</Tabs.List>
|
|
|
|
<HeroContainer>
|
|
<Tabs.Content value="styled" justifyContent="center" alignItems="center" width="100%">
|
|
<RadioGroupDemo />
|
|
</Tabs.Content>
|
|
<Tabs.Content value="unstyled" justifyContent="center" alignItems="center" width="100%">
|
|
<RadioGroupDemo />
|
|
</Tabs.Content>
|
|
<Tabs.Content value="headless" justifyContent="center" alignItems="center" width="100%">
|
|
<RadioGroupHeadlessDemo />
|
|
</Tabs.Content>
|
|
</HeroContainer>
|
|
|
|
<Tabs.Content value="styled">
|
|
```tsx hero template=RadioGroup
|
|
|
|
````
|
|
</Tabs.Content>
|
|
<Tabs.Content value="unstyled">
|
|
```tsx hero template=RadioGroup
|
|
|
|
````
|
|
|
|
</Tabs.Content>
|
|
<Tabs.Content value="headless">
|
|
```tsx hero template=RadioGroupHeadless
|
|
|
|
````
|
|
</Tabs.Content>
|
|
|
|
<Highlights
|
|
features={[
|
|
`Accessible, easy to compose and customize.`,
|
|
`Sizable & works controlled or uncontrolled.`,
|
|
`Ability to opt-out to native radio button on web.`,
|
|
]}
|
|
/>
|
|
|
|
## Installation
|
|
|
|
<Tabs.Content value="styled">
|
|
RadioGroup is already installed in `hanzogui`, or you can install it independently:
|
|
|
|
```bash
|
|
npm install @hanzogui/radio-group
|
|
````
|
|
|
|
</Tabs.Content>
|
|
|
|
<Tabs.Content value="unstyled">
|
|
RadioGroup is already installed in `hanzogui`, or you can install it independently:
|
|
|
|
```bash
|
|
npm install @hanzogui/radio-group
|
|
```
|
|
|
|
</Tabs.Content>
|
|
|
|
<Tabs.Content value="headless">
|
|
|
|
To use the headless radio group, import from the `@hanzogui/radio-headless` package. This package has no dependency on `@hanzogui/core` and provides hooks for building custom radio groups with any styling solution.
|
|
|
|
```bash
|
|
npm install @hanzogui/radio-headless
|
|
```
|
|
|
|
</Tabs.Content>
|
|
|
|
## Usage
|
|
|
|
<Tabs.Content value="styled">
|
|
|
|
```tsx
|
|
import { RadioGroup } from '@hanzo/gui'
|
|
|
|
export default () => (
|
|
<RadioGroup value="foo" gap="$2">
|
|
<RadioGroup.Item value="foo" id="foo-radio-item">
|
|
<RadioGroup.Indicator />
|
|
</RadioGroup.Item>
|
|
<RadioGroup.Item value="bar" id="bar-radio-item">
|
|
<RadioGroup.Indicator />
|
|
</RadioGroup.Item>
|
|
</RadioGroup>
|
|
)
|
|
```
|
|
|
|
</Tabs.Content>
|
|
|
|
<Tabs.Content value="unstyled">
|
|
|
|
Use the `createRadioGroup` export to create a fully custom radio group that still uses the Gui styling system. You provide your own styled components and get back a fully functional radio group component.
|
|
|
|
```tsx
|
|
import { createRadioGroup, RadioGroupContext } from '@hanzogui/radio-group'
|
|
import { styled, View } from '@hanzo/gui'
|
|
|
|
const CustomFrame = styled(View, {
|
|
// your styles
|
|
})
|
|
|
|
const CustomItem = styled(View, {
|
|
context: RadioGroupContext,
|
|
// your styles
|
|
})
|
|
|
|
const CustomIndicator = styled(View, {
|
|
context: RadioGroupContext,
|
|
// your styles
|
|
})
|
|
|
|
export const CustomRadioGroup = createRadioGroup({
|
|
Frame: CustomFrame,
|
|
Item: CustomItem,
|
|
Indicator: CustomIndicator,
|
|
})
|
|
```
|
|
|
|
</Tabs.Content>
|
|
|
|
<Tabs.Content value="headless">
|
|
|
|
The `@hanzogui/radio-headless` package provides three hooks for building custom radio groups:
|
|
|
|
- `useRadioGroup` - for the container/group
|
|
- `useRadioGroupItem` - for individual radio items
|
|
- `useRadioGroupItemIndicator` - for the indicator (checked state visual)
|
|
|
|
### Basic Usage
|
|
|
|
```tsx
|
|
import {
|
|
useRadioGroup,
|
|
useRadioGroupItem,
|
|
useRadioGroupItemIndicator,
|
|
RadioGroupContextValue,
|
|
RadioGroupItemContextValue,
|
|
} from '@hanzogui/radio-headless'
|
|
import { createContext, useContext } from 'react'
|
|
|
|
// Create contexts for the group and item
|
|
const RadioGroupContext = createContext<RadioGroupContextValue>({})
|
|
const RadioGroupItemContext = createContext<RadioGroupItemContextValue>({
|
|
checked: false,
|
|
})
|
|
|
|
function RadioGroup({ children, ...props }) {
|
|
const { providerValue, frameAttrs } = useRadioGroup({
|
|
orientation: 'vertical',
|
|
...props,
|
|
})
|
|
|
|
return (
|
|
<RadioGroupContext.Provider value={providerValue}>
|
|
<div {...frameAttrs} style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
|
{children}
|
|
</div>
|
|
</RadioGroupContext.Provider>
|
|
)
|
|
}
|
|
|
|
function RadioItem({ value, children }) {
|
|
const { checked, providerValue, frameAttrs, bubbleInput } = useRadioGroupItem({
|
|
radioGroupContext: RadioGroupContext,
|
|
value,
|
|
})
|
|
|
|
return (
|
|
<RadioGroupItemContext.Provider value={providerValue}>
|
|
<button
|
|
{...frameAttrs}
|
|
style={{
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
gap: 8,
|
|
padding: 8,
|
|
border: '1px solid #ccc',
|
|
borderRadius: 4,
|
|
background: checked ? '#e0f2fe' : 'white',
|
|
}}
|
|
>
|
|
<RadioIndicator />
|
|
{children}
|
|
</button>
|
|
{bubbleInput}
|
|
</RadioGroupItemContext.Provider>
|
|
)
|
|
}
|
|
|
|
function RadioIndicator() {
|
|
const { checked, ...indicatorProps } = useRadioGroupItemIndicator({
|
|
radioGroupItemContext: RadioGroupItemContext,
|
|
})
|
|
|
|
return (
|
|
<div
|
|
{...indicatorProps}
|
|
style={{
|
|
width: 16,
|
|
height: 16,
|
|
borderRadius: '50%',
|
|
border: '2px solid #3b82f6',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
}}
|
|
>
|
|
{checked && (
|
|
<div
|
|
style={{ width: 8, height: 8, borderRadius: '50%', background: '#3b82f6' }}
|
|
/>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
// Usage
|
|
function App() {
|
|
return (
|
|
<RadioGroup defaultValue="option1" onValueChange={console.log}>
|
|
<RadioItem value="option1">Option 1</RadioItem>
|
|
<RadioItem value="option2">Option 2</RadioItem>
|
|
<RadioItem value="option3">Option 3</RadioItem>
|
|
</RadioGroup>
|
|
)
|
|
}
|
|
```
|
|
|
|
</Tabs.Content>
|
|
|
|
## API Reference
|
|
|
|
### RadioGroup
|
|
|
|
<Tabs.Content value="styled">
|
|
|
|
RadioGroup extends Stack views inheriting all the [Gui standard props](/docs/intro/props), plus:
|
|
|
|
</Tabs.Content>
|
|
<Tabs.Content value="unstyled">
|
|
|
|
When using `createRadioGroup`, you provide styled components:
|
|
|
|
- `Frame`: The root container component
|
|
- `Item`: The radio item component
|
|
- `Indicator`: The indicator component
|
|
|
|
</Tabs.Content>
|
|
<Tabs.Content value="headless">
|
|
|
|
The `useRadioGroup` hook accepts these options:
|
|
|
|
</Tabs.Content>
|
|
|
|
<PropsTable
|
|
data={[
|
|
{
|
|
name: 'name',
|
|
type: 'string',
|
|
description: `Equivalent to input name attribute.`,
|
|
},
|
|
{
|
|
name: 'value',
|
|
type: 'string',
|
|
description: `Controlled value of the selected radio item.`,
|
|
},
|
|
{
|
|
name: 'defaultValue',
|
|
type: 'string',
|
|
description: `Default value for uncontrolled usage.`,
|
|
},
|
|
{
|
|
name: 'required',
|
|
type: 'boolean',
|
|
description: `Sets aria-required attribute.`,
|
|
},
|
|
{
|
|
name: 'disabled',
|
|
type: 'boolean',
|
|
description: `Sets aria-disabled on web, and disables touch on native for all children items.`,
|
|
},
|
|
{
|
|
name: 'native',
|
|
type: 'boolean',
|
|
description: `Renders native radio button on web.`,
|
|
default: 'false',
|
|
},
|
|
{
|
|
name: 'onValueChange',
|
|
type: '(value: string) => void',
|
|
description: 'Called when the selected value changes.',
|
|
},
|
|
{
|
|
name: 'orientation',
|
|
type: '"horizontal" | "vertical"',
|
|
description: 'Orientation of the radio group.',
|
|
},
|
|
{
|
|
name: 'accentColor',
|
|
type: 'string',
|
|
description: 'Sets `accent-color` style when `native` prop is enabled.',
|
|
},
|
|
]}
|
|
/>
|
|
|
|
<Tabs.Content value="styled">
|
|
|
|
### RadioGroup.Item
|
|
|
|
<PropsTable
|
|
data={[
|
|
{
|
|
name: 'labeledBy',
|
|
type: 'string',
|
|
description: `Sets aria-labelledby on web.`,
|
|
},
|
|
{
|
|
name: 'value',
|
|
type: 'string',
|
|
description: `Input value for the radio button.`,
|
|
},
|
|
{
|
|
name: 'disabled',
|
|
type: 'boolean',
|
|
description: `Sets aria-disabled on web, and disables touch on native.`,
|
|
},
|
|
{
|
|
name: 'id',
|
|
type: 'string',
|
|
description: `ID used on the web.`,
|
|
},
|
|
{
|
|
name: 'scaleSize',
|
|
type: 'number',
|
|
default: '0.5',
|
|
description: `Scale factor for the radio item size. Gui size tokens map to button heights, so you typically want smaller radio buttons.`,
|
|
},
|
|
{
|
|
name: 'unstyled',
|
|
type: 'boolean',
|
|
default: 'false',
|
|
description: `When true, removes all default Gui styling.`,
|
|
},
|
|
]}
|
|
/>
|
|
|
|
### RadioGroup.Indicator
|
|
|
|
RadioGroup.Indicator appears only when the parent Item is checked. It extends [ThemeableStack](/docs/components/stacks#themeablestack), getting [Gui standard props](/docs/intro/props) adding:
|
|
|
|
<PropsTable
|
|
data={[
|
|
{
|
|
name: 'unstyled',
|
|
required: false,
|
|
type: `boolean`,
|
|
description: `Removes all default Gui styles.`,
|
|
},
|
|
]}
|
|
/>
|
|
|
|
</Tabs.Content>
|
|
|
|
<Tabs.Content value="headless">
|
|
|
|
### useRadioGroup Return Value
|
|
|
|
| Property | Type | Description |
|
|
| ----------------------- | ------------------------ | --------------------------------------------------------------------- |
|
|
| `providerValue` | `RadioGroupContextValue` | Value to pass to your context provider |
|
|
| `frameAttrs` | `object` | Props to spread on the group container (role, aria-orientation, etc.) |
|
|
| `rovingFocusGroupAttrs` | `object` | Props for roving focus behavior (optional) |
|
|
|
|
### useRadioGroupItem
|
|
|
|
Hook for individual radio items. Requires a context containing the group state.
|
|
|
|
```tsx
|
|
const {
|
|
checked,
|
|
isFormControl,
|
|
providerValue,
|
|
bubbleInput,
|
|
native,
|
|
frameAttrs,
|
|
rovingFocusGroupAttrs,
|
|
} = useRadioGroupItem({
|
|
radioGroupContext: YourRadioGroupContext,
|
|
value: 'option1',
|
|
id: 'option1-id',
|
|
labelledBy: 'label-id',
|
|
disabled: false,
|
|
onPress: () => {},
|
|
onKeyDown: () => {},
|
|
onFocus: () => {},
|
|
})
|
|
```
|
|
|
|
| Property | Type | Description |
|
|
| --------------- | ----------- | --------------------------------------- |
|
|
| `checked` | `boolean` | Whether this item is currently selected |
|
|
| `providerValue` | `object` | Context value for the indicator |
|
|
| `frameAttrs` | `object` | Props to spread on the item element |
|
|
| `bubbleInput` | `ReactNode` | Hidden input for form compatibility |
|
|
| `native` | `boolean` | Whether using native radio |
|
|
| `isFormControl` | `boolean` | Whether inside a form |
|
|
|
|
### useRadioGroupItemIndicator
|
|
|
|
Hook for the visual indicator of checked state.
|
|
|
|
```tsx
|
|
const { checked, ...dataAttrs } = useRadioGroupItemIndicator({
|
|
radioGroupItemContext: YourRadioGroupItemContext,
|
|
disabled: false,
|
|
})
|
|
```
|
|
|
|
| Property | Type | Description |
|
|
| --------------- | --------------------- | ---------------------------------- |
|
|
| `checked` | `boolean` | Whether the parent item is checked |
|
|
| `data-state` | `string` | 'checked' or 'unchecked' |
|
|
| `data-disabled` | `string \| undefined` | Present when disabled |
|
|
|
|
</Tabs.Content>
|
|
|
|
</Tabs>
|