Skip to content

Commit f5cd6a4

Browse files
committed
feat: add Radio and RadioGroup components to sandbox and update roadmap status to 'done'
1 parent 023c449 commit f5cd6a4

File tree

19 files changed

+1892
-1
lines changed

19 files changed

+1892
-1
lines changed

apps/sandbox/app/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ const primitives = [
1414
{ name: "Avatar", path: "/preview/avatar", description: "User profile images with intelligent fallback text support" },
1515
{ name: "Checkbox", path: "/preview/checkbox", description: "Individual checkbox with clickable labels" },
1616
{ name: "Checkbox Group", path: "/preview/checkbox-group", description: "Manage multiple checkboxes with coordinated state" },
17+
{ name: "Radio", path: "/preview/radio", description: "Individual radio button with clickable labels" },
18+
{ name: "Radio Group", path: "/preview/radiogroup", description: "Manage multiple radios with coordinated state (single selection)" },
1719
{ name: "Context Menu", path: "/preview/context-menu", description: "Cross-platform context menu: right-click on web, long press on native" },
1820
{ name: "Switch", path: "/preview/switch", description: "Switch between on/off states with customizable styling" },
1921
{ name: "Switch Group", path: "/preview/switch-group", description: "Manage multiple switches with coordinated state" },

apps/sandbox/app/preview/radio.tsx

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
import { Stack } from "expo-router";
2+
import { View, Text } from "@native-ui-org/primitives";
3+
import { Radio, RadioLabel, RadioIndicator } from "@native-ui-org/primitives";
4+
import { StyleSheet, Platform, ScrollView, Pressable } from "react-native";
5+
import React, { useState } from "react";
6+
7+
export default function RadioPreview() {
8+
const [controlled, setControlled] = useState(false);
9+
10+
return (
11+
<ScrollView style={styles.container}>
12+
<Stack.Screen options={{ title: "Radio" }} />
13+
14+
<View style={styles.content}>
15+
<View style={styles.section}>
16+
<Text as="h2" style={styles.sectionTitle}>Radio Primitive</Text>
17+
<Text as="p" style={styles.description}>
18+
Unstyled radio primitive. Use RadioIndicator to show checked state.
19+
</Text>
20+
</View>
21+
22+
<View style={styles.section}>
23+
<Text as="h3" style={styles.subTitle}>Platform Behavior</Text>
24+
<Text as="p" style={styles.description}>
25+
<Text style={styles.bold}>Web:</Text> Native label with ARIA (aria-checked, aria-disabled, aria-required){"\n"}
26+
<Text style={styles.bold}>Native:</Text> Accessibility role and state{"\n"}
27+
<Text style={styles.bold}>Keyboard:</Text> Space/Enter to toggle (web){"\n"}
28+
<Text style={styles.bold}>Form:</Text> Hidden input for HTML form submission
29+
</Text>
30+
</View>
31+
32+
<View style={styles.section}>
33+
<Text as="h3" style={styles.subTitle}>Controlled</Text>
34+
<Text as="p" style={styles.description}>
35+
Parent manages state
36+
</Text>
37+
38+
<Pressable style={styles.row} onPress={() => setControlled(!controlled)}>
39+
<Radio
40+
id="controlled"
41+
checked={controlled}
42+
onCheckedChange={(checked) => setControlled(checked)}
43+
style={styles.radioBox}
44+
>
45+
<RadioIndicator>
46+
<View style={styles.dot} />
47+
</RadioIndicator>
48+
</Radio>
49+
<RadioLabel htmlFor="controlled" style={styles.label}>
50+
Controlled radio
51+
</RadioLabel>
52+
</Pressable>
53+
</View>
54+
55+
<View style={styles.section}>
56+
<Text as="h3" style={styles.subTitle}>Uncontrolled</Text>
57+
<Text as="p" style={styles.description}>
58+
Internal state with defaultChecked
59+
</Text>
60+
61+
<Pressable style={styles.row}>
62+
<Radio
63+
id="uncontrolled"
64+
defaultChecked={true}
65+
style={styles.radioBox}
66+
>
67+
<RadioIndicator>
68+
<View style={styles.dot} />
69+
</RadioIndicator>
70+
</Radio>
71+
<RadioLabel htmlFor="uncontrolled" style={styles.label}>
72+
Uncontrolled radio (default checked)
73+
</RadioLabel>
74+
</Pressable>
75+
</View>
76+
77+
<View style={styles.section}>
78+
<Text as="h3" style={styles.subTitle}>States</Text>
79+
<Text as="p" style={styles.description}>
80+
Different visual states
81+
</Text>
82+
83+
<View style={styles.stateRow}>
84+
<View style={styles.stateItem}>
85+
<Radio checked={false} style={styles.radioBox}>
86+
<RadioIndicator>
87+
<View style={styles.dot} />
88+
</RadioIndicator>
89+
</Radio>
90+
<Text style={styles.stateLabel}>Unchecked</Text>
91+
</View>
92+
93+
<View style={styles.stateItem}>
94+
<Radio checked={true} style={styles.radioBox}>
95+
<RadioIndicator>
96+
<View style={styles.dot} />
97+
</RadioIndicator>
98+
</Radio>
99+
<Text style={styles.stateLabel}>Checked</Text>
100+
</View>
101+
102+
<View style={styles.stateItem}>
103+
<Radio checked={true} disabled={true} style={styles.radioBox}>
104+
<RadioIndicator>
105+
<View style={styles.dot} />
106+
</RadioIndicator>
107+
</Radio>
108+
<Text style={styles.stateLabel}>Disabled</Text>
109+
</View>
110+
</View>
111+
</View>
112+
</View>
113+
</ScrollView>
114+
);
115+
}
116+
117+
const styles = StyleSheet.create({
118+
container: {
119+
flex: 1,
120+
backgroundColor: Platform.OS === "web" ? "#fff" : "#f2f2f7",
121+
},
122+
content: {
123+
...Platform.select({
124+
web: {
125+
maxWidth: 800,
126+
marginHorizontal: "auto",
127+
width: "100%",
128+
paddingHorizontal: 24,
129+
paddingVertical: 32,
130+
},
131+
default: {
132+
padding: 16,
133+
},
134+
}),
135+
},
136+
section: {
137+
marginBottom: 32,
138+
},
139+
sectionTitle: {
140+
fontSize: 24,
141+
fontWeight: "600",
142+
marginBottom: 8,
143+
color: "#000",
144+
},
145+
subTitle: {
146+
fontSize: 18,
147+
fontWeight: "600",
148+
marginBottom: 8,
149+
color: "#000",
150+
},
151+
description: {
152+
fontSize: 14,
153+
color: "#666",
154+
marginBottom: 16,
155+
lineHeight: 20,
156+
},
157+
bold: {
158+
fontWeight: "600",
159+
},
160+
group: {
161+
gap: 16,
162+
},
163+
row: {
164+
flexDirection: "row",
165+
alignItems: "center",
166+
gap: 12,
167+
},
168+
label: {
169+
fontSize: 16,
170+
color: "#000",
171+
},
172+
radioBox: {
173+
width: 20,
174+
height: 20,
175+
borderWidth: 2,
176+
borderColor: "#007AFF",
177+
borderRadius: 10,
178+
justifyContent: "center",
179+
alignItems: "center",
180+
backgroundColor: "transparent",
181+
},
182+
dot: {
183+
width: 10,
184+
height: 10,
185+
backgroundColor: "#007AFF",
186+
borderRadius: 5,
187+
},
188+
stateRow: {
189+
flexDirection: "row",
190+
flexWrap: "wrap",
191+
gap: 24,
192+
},
193+
stateItem: {
194+
alignItems: "center",
195+
gap: 8,
196+
},
197+
stateLabel: {
198+
fontSize: 12,
199+
color: "#666",
200+
},
201+
});
202+

0 commit comments

Comments
 (0)