sonance-brand-mcp 1.1.3 → 1.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/components/accordion.stories.tsx +310 -0
- package/dist/assets/components/accordion.tsx +56 -30
- package/dist/assets/components/alert.stories.tsx +199 -0
- package/dist/assets/components/autocomplete.stories.tsx +307 -0
- package/dist/assets/components/autocomplete.tsx +28 -4
- package/dist/assets/components/avatar.stories.tsx +175 -0
- package/dist/assets/components/badge.stories.tsx +258 -0
- package/dist/assets/components/breadcrumbs.stories.tsx +175 -0
- package/dist/assets/components/button.stories.tsx +362 -0
- package/dist/assets/components/button.tsx +48 -3
- package/dist/assets/components/calendar.stories.tsx +247 -0
- package/dist/assets/components/card.stories.tsx +275 -0
- package/dist/assets/components/card.tsx +26 -1
- package/dist/assets/components/checkbox-group.stories.tsx +281 -0
- package/dist/assets/components/checkbox.stories.tsx +160 -0
- package/dist/assets/components/checkbox.tsx +32 -4
- package/dist/assets/components/code.stories.tsx +265 -0
- package/dist/assets/components/date-input.stories.tsx +278 -0
- package/dist/assets/components/date-input.tsx +24 -2
- package/dist/assets/components/date-picker.stories.tsx +337 -0
- package/dist/assets/components/date-picker.tsx +28 -4
- package/dist/assets/components/date-range-picker.stories.tsx +340 -0
- package/dist/assets/components/dialog.stories.tsx +285 -0
- package/dist/assets/components/divider.stories.tsx +176 -0
- package/dist/assets/components/drawer.stories.tsx +216 -0
- package/dist/assets/components/dropdown.stories.tsx +342 -0
- package/dist/assets/components/dropdown.tsx +55 -10
- package/dist/assets/components/form.stories.tsx +372 -0
- package/dist/assets/components/image.stories.tsx +348 -0
- package/dist/assets/components/input-otp.stories.tsx +336 -0
- package/dist/assets/components/input-otp.tsx +24 -2
- package/dist/assets/components/input.stories.tsx +223 -0
- package/dist/assets/components/input.tsx +27 -2
- package/dist/assets/components/kbd.stories.tsx +272 -0
- package/dist/assets/components/link.stories.tsx +199 -0
- package/dist/assets/components/link.tsx +50 -1
- package/dist/assets/components/listbox.stories.tsx +287 -0
- package/dist/assets/components/listbox.tsx +30 -7
- package/dist/assets/components/navbar.stories.tsx +218 -0
- package/dist/assets/components/number-input.stories.tsx +295 -0
- package/dist/assets/components/number-input.tsx +30 -8
- package/dist/assets/components/pagination.stories.tsx +280 -0
- package/dist/assets/components/pagination.tsx +45 -21
- package/dist/assets/components/popover.stories.tsx +219 -0
- package/dist/assets/components/progress.stories.tsx +153 -0
- package/dist/assets/components/radio-group.stories.tsx +187 -0
- package/dist/assets/components/radio-group.tsx +30 -6
- package/dist/assets/components/range-calendar.stories.tsx +334 -0
- package/dist/assets/components/scroll-shadow.stories.tsx +335 -0
- package/dist/assets/components/select.stories.tsx +192 -0
- package/dist/assets/components/select.tsx +54 -7
- package/dist/assets/components/skeleton.stories.tsx +166 -0
- package/dist/assets/components/slider.stories.tsx +145 -0
- package/dist/assets/components/slider.tsx +43 -8
- package/dist/assets/components/spacer.stories.tsx +216 -0
- package/dist/assets/components/spinner.stories.tsx +149 -0
- package/dist/assets/components/switch.stories.tsx +170 -0
- package/dist/assets/components/switch.tsx +29 -4
- package/dist/assets/components/table.stories.tsx +322 -0
- package/dist/assets/components/tabs.stories.tsx +306 -0
- package/dist/assets/components/tabs.tsx +25 -4
- package/dist/assets/components/textarea.stories.tsx +103 -0
- package/dist/assets/components/textarea.tsx +27 -3
- package/dist/assets/components/theme-toggle.stories.tsx +248 -0
- package/dist/assets/components/time-input.stories.tsx +365 -0
- package/dist/assets/components/time-input.tsx +25 -3
- package/dist/assets/components/toast.stories.tsx +195 -0
- package/dist/assets/components/tooltip.stories.tsx +226 -0
- package/dist/assets/components/user.stories.tsx +274 -0
- package/dist/index.js +1732 -13
- package/package.json +1 -1
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/nextjs-vite';
|
|
2
|
+
import { Progress, CircularProgress } from './progress';
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof Progress> = {
|
|
5
|
+
title: 'Components/Feedback/Progress',
|
|
6
|
+
component: Progress,
|
|
7
|
+
tags: ['autodocs'],
|
|
8
|
+
parameters: {
|
|
9
|
+
docs: {
|
|
10
|
+
description: {
|
|
11
|
+
component: 'Progress indicators for showing completion status. Includes linear and circular variants.',
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
argTypes: {
|
|
16
|
+
value: { control: { type: 'range', min: 0, max: 100 } },
|
|
17
|
+
max: { control: 'number' },
|
|
18
|
+
label: { control: 'text' },
|
|
19
|
+
showValue: { control: 'boolean' },
|
|
20
|
+
size: { control: 'select', options: ['sm', 'md', 'lg'] },
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export default meta;
|
|
25
|
+
type Story = StoryObj<typeof meta>;
|
|
26
|
+
|
|
27
|
+
export const Default: Story = {
|
|
28
|
+
args: {
|
|
29
|
+
value: 60,
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const WithLabel: Story = {
|
|
34
|
+
args: {
|
|
35
|
+
value: 75,
|
|
36
|
+
label: 'Progress',
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const WithValue: Story = {
|
|
41
|
+
args: {
|
|
42
|
+
value: 45,
|
|
43
|
+
label: 'Upload Progress',
|
|
44
|
+
showValue: true,
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const AllSizes: Story = {
|
|
49
|
+
render: () => (
|
|
50
|
+
<div className="space-y-6 w-80">
|
|
51
|
+
<Progress value={60} size="sm" label="Small" />
|
|
52
|
+
<Progress value={60} size="md" label="Medium" />
|
|
53
|
+
<Progress value={60} size="lg" label="Large" />
|
|
54
|
+
</div>
|
|
55
|
+
),
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const DifferentValues: Story = {
|
|
59
|
+
render: () => (
|
|
60
|
+
<div className="space-y-4 w-80">
|
|
61
|
+
<Progress value={0} showValue />
|
|
62
|
+
<Progress value={25} showValue />
|
|
63
|
+
<Progress value={50} showValue />
|
|
64
|
+
<Progress value={75} showValue />
|
|
65
|
+
<Progress value={100} showValue />
|
|
66
|
+
</div>
|
|
67
|
+
),
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// Circular Progress Stories
|
|
71
|
+
export const Circular: StoryObj<typeof CircularProgress> = {
|
|
72
|
+
render: () => <CircularProgress value={65} />,
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const CircularWithValue: StoryObj<typeof CircularProgress> = {
|
|
76
|
+
render: () => <CircularProgress value={75} showValue />,
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export const CircularSizes: StoryObj<typeof CircularProgress> = {
|
|
80
|
+
render: () => (
|
|
81
|
+
<div className="flex items-center gap-6">
|
|
82
|
+
<CircularProgress value={60} size="sm" showValue />
|
|
83
|
+
<CircularProgress value={60} size="md" showValue />
|
|
84
|
+
<CircularProgress value={60} size="lg" showValue />
|
|
85
|
+
</div>
|
|
86
|
+
),
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export const CircularCustomSize: StoryObj<typeof CircularProgress> = {
|
|
90
|
+
render: () => (
|
|
91
|
+
<CircularProgress value={80} size={100} strokeWidth={8} showValue />
|
|
92
|
+
),
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export const LoadingExample: Story = {
|
|
96
|
+
render: () => (
|
|
97
|
+
<div className="space-y-6 w-96">
|
|
98
|
+
<div>
|
|
99
|
+
<h4 className="text-sm font-medium text-foreground mb-2">File Upload</h4>
|
|
100
|
+
<Progress value={67} label="document.pdf" showValue />
|
|
101
|
+
</div>
|
|
102
|
+
<div>
|
|
103
|
+
<h4 className="text-sm font-medium text-foreground mb-2">Installation Progress</h4>
|
|
104
|
+
<Progress value={45} label="Installing dependencies..." showValue />
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
),
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// Responsive Matrix - Mobile, Tablet, Desktop
|
|
111
|
+
export const ResponsiveMatrix: Story = {
|
|
112
|
+
render: () => (
|
|
113
|
+
<div className="space-y-8">
|
|
114
|
+
{/* Mobile */}
|
|
115
|
+
<div>
|
|
116
|
+
<h4 className="text-xs uppercase text-foreground-muted mb-2">Mobile (375px)</h4>
|
|
117
|
+
<div className="w-[375px] border border-dashed border-border p-4 space-y-6">
|
|
118
|
+
<Progress value={65} label="Upload Progress" showValue />
|
|
119
|
+
<div className="flex justify-center">
|
|
120
|
+
<CircularProgress value={75} showValue />
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
{/* Tablet */}
|
|
125
|
+
<div>
|
|
126
|
+
<h4 className="text-xs uppercase text-foreground-muted mb-2">Tablet (768px)</h4>
|
|
127
|
+
<div className="w-[768px] border border-dashed border-border p-4">
|
|
128
|
+
<div className="grid grid-cols-2 gap-8 items-center">
|
|
129
|
+
<Progress value={45} label="Installation" showValue />
|
|
130
|
+
<div className="flex justify-center gap-4">
|
|
131
|
+
<CircularProgress value={60} size="sm" showValue />
|
|
132
|
+
<CircularProgress value={85} size="md" showValue />
|
|
133
|
+
</div>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
{/* Desktop */}
|
|
138
|
+
<div>
|
|
139
|
+
<h4 className="text-xs uppercase text-foreground-muted mb-2">Desktop (1280px)</h4>
|
|
140
|
+
<div className="w-[1280px] border border-dashed border-border p-4">
|
|
141
|
+
<div className="grid grid-cols-4 gap-8 items-center">
|
|
142
|
+
<Progress value={25} size="sm" showValue />
|
|
143
|
+
<Progress value={50} size="md" showValue />
|
|
144
|
+
<Progress value={75} size="lg" showValue />
|
|
145
|
+
<div className="flex justify-center">
|
|
146
|
+
<CircularProgress value={90} size="lg" showValue />
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
),
|
|
153
|
+
};
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/nextjs-vite';
|
|
2
|
+
import { ComponentProps } from 'react';
|
|
3
|
+
import { RadioGroup, RadioGroupItem, type RadioGroupItemState } from './radio-group';
|
|
4
|
+
|
|
5
|
+
type RadioGroupStoryProps = ComponentProps<typeof RadioGroup> & {
|
|
6
|
+
state?: RadioGroupItemState;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const meta: Meta<RadioGroupStoryProps> = {
|
|
10
|
+
title: 'Components/Forms/RadioGroup',
|
|
11
|
+
component: RadioGroup,
|
|
12
|
+
tags: ['autodocs'],
|
|
13
|
+
parameters: {
|
|
14
|
+
docs: {
|
|
15
|
+
description: {
|
|
16
|
+
component: 'A group of radio buttons for selecting a single option from multiple choices.',
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
argTypes: {
|
|
21
|
+
orientation: {
|
|
22
|
+
control: 'select',
|
|
23
|
+
options: ['vertical', 'horizontal'],
|
|
24
|
+
},
|
|
25
|
+
state: {
|
|
26
|
+
control: 'select',
|
|
27
|
+
options: ['default', 'hover', 'focus', 'checked', 'disabled'],
|
|
28
|
+
description: 'Visual state for documentation (RadioGroupItem)',
|
|
29
|
+
table: {
|
|
30
|
+
defaultValue: { summary: 'default' },
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export default meta;
|
|
37
|
+
type Story = StoryObj<RadioGroupStoryProps>;
|
|
38
|
+
|
|
39
|
+
export const Default: Story = {
|
|
40
|
+
render: () => (
|
|
41
|
+
<RadioGroup defaultValue="option1">
|
|
42
|
+
<RadioGroupItem value="option1" label="Option 1" />
|
|
43
|
+
<RadioGroupItem value="option2" label="Option 2" />
|
|
44
|
+
<RadioGroupItem value="option3" label="Option 3" />
|
|
45
|
+
</RadioGroup>
|
|
46
|
+
),
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const WithDescriptions: Story = {
|
|
50
|
+
render: () => (
|
|
51
|
+
<RadioGroup defaultValue="standard">
|
|
52
|
+
<RadioGroupItem
|
|
53
|
+
value="standard"
|
|
54
|
+
label="Standard Shipping"
|
|
55
|
+
description="Delivery in 5-7 business days"
|
|
56
|
+
/>
|
|
57
|
+
<RadioGroupItem
|
|
58
|
+
value="express"
|
|
59
|
+
label="Express Shipping"
|
|
60
|
+
description="Delivery in 2-3 business days"
|
|
61
|
+
/>
|
|
62
|
+
<RadioGroupItem
|
|
63
|
+
value="overnight"
|
|
64
|
+
label="Overnight Shipping"
|
|
65
|
+
description="Next day delivery"
|
|
66
|
+
/>
|
|
67
|
+
</RadioGroup>
|
|
68
|
+
),
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export const Horizontal: Story = {
|
|
72
|
+
render: () => (
|
|
73
|
+
<RadioGroup defaultValue="small" orientation="horizontal">
|
|
74
|
+
<RadioGroupItem value="small" label="Small" />
|
|
75
|
+
<RadioGroupItem value="medium" label="Medium" />
|
|
76
|
+
<RadioGroupItem value="large" label="Large" />
|
|
77
|
+
</RadioGroup>
|
|
78
|
+
),
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export const WithDisabledOption: Story = {
|
|
82
|
+
render: () => (
|
|
83
|
+
<RadioGroup defaultValue="available">
|
|
84
|
+
<RadioGroupItem value="available" label="Available" />
|
|
85
|
+
<RadioGroupItem value="limited" label="Limited Stock" />
|
|
86
|
+
<RadioGroupItem value="outofstock" label="Out of Stock" disabled />
|
|
87
|
+
</RadioGroup>
|
|
88
|
+
),
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export const PlanSelection: Story = {
|
|
92
|
+
render: () => (
|
|
93
|
+
<div className="w-80">
|
|
94
|
+
<h3 className="text-sm font-medium text-foreground mb-4">Select a Plan</h3>
|
|
95
|
+
<RadioGroup defaultValue="pro">
|
|
96
|
+
<RadioGroupItem
|
|
97
|
+
value="free"
|
|
98
|
+
label="Free"
|
|
99
|
+
description="Basic features for personal use"
|
|
100
|
+
/>
|
|
101
|
+
<RadioGroupItem
|
|
102
|
+
value="pro"
|
|
103
|
+
label="Pro"
|
|
104
|
+
description="Advanced features for professionals"
|
|
105
|
+
/>
|
|
106
|
+
<RadioGroupItem
|
|
107
|
+
value="enterprise"
|
|
108
|
+
label="Enterprise"
|
|
109
|
+
description="Custom solutions for large teams"
|
|
110
|
+
/>
|
|
111
|
+
</RadioGroup>
|
|
112
|
+
</div>
|
|
113
|
+
),
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
// State Matrix - Visual documentation of all states
|
|
117
|
+
export const StateMatrix: Story = {
|
|
118
|
+
render: () => {
|
|
119
|
+
const states: RadioGroupItemState[] = ['default', 'hover', 'focus', 'checked', 'disabled'];
|
|
120
|
+
return (
|
|
121
|
+
<div className="space-y-6">
|
|
122
|
+
<h3 className="text-sm font-medium text-foreground-muted">RadioGroupItem States</h3>
|
|
123
|
+
<div className="space-y-4">
|
|
124
|
+
{states.map((state) => (
|
|
125
|
+
<div key={state} className="flex items-center gap-4">
|
|
126
|
+
<span className="text-xs font-medium text-foreground-muted uppercase w-20">{state}</span>
|
|
127
|
+
<RadioGroup>
|
|
128
|
+
<RadioGroupItem
|
|
129
|
+
value="demo"
|
|
130
|
+
label={`${state} state`}
|
|
131
|
+
state={state}
|
|
132
|
+
/>
|
|
133
|
+
</RadioGroup>
|
|
134
|
+
</div>
|
|
135
|
+
))}
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
);
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// Responsive Matrix - Mobile, Tablet, Desktop
|
|
143
|
+
export const ResponsiveMatrix: Story = {
|
|
144
|
+
render: () => (
|
|
145
|
+
<div className="space-y-8">
|
|
146
|
+
{/* Mobile */}
|
|
147
|
+
<div>
|
|
148
|
+
<h4 className="text-xs uppercase text-foreground-muted mb-2">Mobile (375px)</h4>
|
|
149
|
+
<div className="w-[375px] border border-dashed border-border p-4">
|
|
150
|
+
<RadioGroup defaultValue="standard">
|
|
151
|
+
<RadioGroupItem value="standard" label="Standard" description="5-7 business days" />
|
|
152
|
+
<RadioGroupItem value="express" label="Express" description="2-3 business days" />
|
|
153
|
+
<RadioGroupItem value="overnight" label="Overnight" description="Next day" />
|
|
154
|
+
</RadioGroup>
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
{/* Tablet */}
|
|
158
|
+
<div>
|
|
159
|
+
<h4 className="text-xs uppercase text-foreground-muted mb-2">Tablet (768px)</h4>
|
|
160
|
+
<div className="w-[768px] border border-dashed border-border p-4">
|
|
161
|
+
<div className="grid grid-cols-2 gap-8">
|
|
162
|
+
<RadioGroup defaultValue="small">
|
|
163
|
+
<RadioGroupItem value="small" label="Small" />
|
|
164
|
+
<RadioGroupItem value="medium" label="Medium" />
|
|
165
|
+
<RadioGroupItem value="large" label="Large" />
|
|
166
|
+
</RadioGroup>
|
|
167
|
+
<RadioGroup defaultValue="monthly" orientation="horizontal">
|
|
168
|
+
<RadioGroupItem value="monthly" label="Monthly" />
|
|
169
|
+
<RadioGroupItem value="yearly" label="Yearly" />
|
|
170
|
+
</RadioGroup>
|
|
171
|
+
</div>
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
174
|
+
{/* Desktop */}
|
|
175
|
+
<div>
|
|
176
|
+
<h4 className="text-xs uppercase text-foreground-muted mb-2">Desktop (1280px)</h4>
|
|
177
|
+
<div className="w-[1280px] border border-dashed border-border p-4">
|
|
178
|
+
<RadioGroup defaultValue="pro" orientation="horizontal" className="flex gap-8">
|
|
179
|
+
<RadioGroupItem value="free" label="Free" description="Basic features" />
|
|
180
|
+
<RadioGroupItem value="pro" label="Pro" description="Advanced features" />
|
|
181
|
+
<RadioGroupItem value="enterprise" label="Enterprise" description="Custom solutions" />
|
|
182
|
+
</RadioGroup>
|
|
183
|
+
</div>
|
|
184
|
+
</div>
|
|
185
|
+
</div>
|
|
186
|
+
),
|
|
187
|
+
};
|
|
@@ -1,8 +1,24 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { forwardRef, createContext, useContext, useState } from "react";
|
|
3
|
+
import { forwardRef, createContext, useContext, useState, useId } from "react";
|
|
4
4
|
import { cn } from "@/lib/utils";
|
|
5
5
|
|
|
6
|
+
export type RadioGroupItemState = "default" | "hover" | "focus" | "checked" | "disabled";
|
|
7
|
+
|
|
8
|
+
// State styles for Storybook/Figma visualization
|
|
9
|
+
const getStateStyles = (state?: RadioGroupItemState) => {
|
|
10
|
+
if (!state || state === "default") return "";
|
|
11
|
+
|
|
12
|
+
const stateMap: Record<string, string> = {
|
|
13
|
+
hover: "border-border-hover",
|
|
14
|
+
focus: "ring-2 ring-border-focus ring-offset-2 ring-offset-background",
|
|
15
|
+
checked: "border-primary",
|
|
16
|
+
disabled: "opacity-50 cursor-not-allowed",
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
return stateMap[state] || "";
|
|
20
|
+
};
|
|
21
|
+
|
|
6
22
|
interface RadioGroupContextValue {
|
|
7
23
|
value: string;
|
|
8
24
|
onChange: (value: string) => void;
|
|
@@ -25,13 +41,15 @@ export function RadioGroup({
|
|
|
25
41
|
value: controlledValue,
|
|
26
42
|
defaultValue = "",
|
|
27
43
|
onValueChange,
|
|
28
|
-
name
|
|
44
|
+
name,
|
|
29
45
|
className,
|
|
30
46
|
children,
|
|
31
47
|
orientation = "vertical",
|
|
32
48
|
}: RadioGroupProps) {
|
|
33
49
|
const [internalValue, setInternalValue] = useState(defaultValue);
|
|
34
50
|
const value = controlledValue ?? internalValue;
|
|
51
|
+
const uniqueId = useId();
|
|
52
|
+
const groupName = name || `radio-group-${uniqueId}`;
|
|
35
53
|
|
|
36
54
|
const onChange = (newValue: string) => {
|
|
37
55
|
setInternalValue(newValue);
|
|
@@ -39,7 +57,7 @@ export function RadioGroup({
|
|
|
39
57
|
};
|
|
40
58
|
|
|
41
59
|
return (
|
|
42
|
-
<RadioGroupContext.Provider value={{ value, onChange, name }}>
|
|
60
|
+
<RadioGroupContext.Provider value={{ value, onChange, name: groupName }}>
|
|
43
61
|
<div
|
|
44
62
|
role="radiogroup"
|
|
45
63
|
className={cn(
|
|
@@ -58,10 +76,12 @@ interface RadioGroupItemProps extends Omit<React.InputHTMLAttributes<HTMLInputEl
|
|
|
58
76
|
value: string;
|
|
59
77
|
label?: string;
|
|
60
78
|
description?: string;
|
|
79
|
+
/** Visual state for Storybook/Figma documentation */
|
|
80
|
+
state?: RadioGroupItemState;
|
|
61
81
|
}
|
|
62
82
|
|
|
63
83
|
export const RadioGroupItem = forwardRef<HTMLInputElement, RadioGroupItemProps>(
|
|
64
|
-
({ className, value, label, description, id, ...props }, ref) => {
|
|
84
|
+
({ className, value, label, description, id, state, disabled, ...props }, ref) => {
|
|
65
85
|
const context = useContext(RadioGroupContext);
|
|
66
86
|
if (!context) {
|
|
67
87
|
throw new Error("RadioGroupItem must be used within a RadioGroup");
|
|
@@ -69,7 +89,8 @@ export const RadioGroupItem = forwardRef<HTMLInputElement, RadioGroupItemProps>(
|
|
|
69
89
|
|
|
70
90
|
const { value: groupValue, onChange, name } = context;
|
|
71
91
|
const inputId = id || `radio-${value}`;
|
|
72
|
-
const isChecked = groupValue === value;
|
|
92
|
+
const isChecked = groupValue === value || state === "checked";
|
|
93
|
+
const isDisabled = disabled || state === "disabled";
|
|
73
94
|
|
|
74
95
|
return (
|
|
75
96
|
<div className="flex items-start gap-3">
|
|
@@ -82,12 +103,14 @@ export const RadioGroupItem = forwardRef<HTMLInputElement, RadioGroupItemProps>(
|
|
|
82
103
|
value={value}
|
|
83
104
|
checked={isChecked}
|
|
84
105
|
onChange={() => onChange(value)}
|
|
106
|
+
disabled={isDisabled}
|
|
85
107
|
className={cn(
|
|
86
108
|
"peer h-5 w-5 shrink-0 appearance-none rounded-full border border-border bg-input",
|
|
87
109
|
"checked:border-primary",
|
|
88
110
|
"focus:outline-none focus:ring-2 focus:ring-border-focus focus:ring-offset-2 focus:ring-offset-background",
|
|
89
111
|
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
90
112
|
"transition-colors duration-150",
|
|
113
|
+
getStateStyles(state),
|
|
91
114
|
className
|
|
92
115
|
)}
|
|
93
116
|
{...props}
|
|
@@ -95,7 +118,8 @@ export const RadioGroupItem = forwardRef<HTMLInputElement, RadioGroupItemProps>(
|
|
|
95
118
|
<div
|
|
96
119
|
className={cn(
|
|
97
120
|
"pointer-events-none absolute h-2.5 w-2.5 rounded-full bg-primary",
|
|
98
|
-
"scale-0
|
|
121
|
+
"scale-0 transition-transform duration-150",
|
|
122
|
+
isChecked && "scale-100"
|
|
99
123
|
)}
|
|
100
124
|
/>
|
|
101
125
|
</div>
|