aural-ui 4.2.0 → 4.2.3
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/components/tabs/Tabs.stories.tsx +217 -173
- package/dist/components/tabs/index.tsx +100 -16
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from "react"
|
|
2
|
+
import { useId } from "react"
|
|
2
3
|
import { cn } from "@lib/utils"
|
|
3
4
|
import * as TabsPrimitive from "@radix-ui/react-tabs"
|
|
4
5
|
import { cva } from "class-variance-authority"
|
|
@@ -16,23 +17,30 @@ const tabsVariants = cva("", {
|
|
|
16
17
|
},
|
|
17
18
|
})
|
|
18
19
|
|
|
19
|
-
// Create context for tabs size
|
|
20
|
+
// Create context for tabs size and baseId
|
|
20
21
|
const TabsContext = React.createContext<{
|
|
21
22
|
size: "sm" | "md" | "lg"
|
|
23
|
+
baseId: string
|
|
22
24
|
}>({
|
|
23
25
|
size: "md",
|
|
26
|
+
baseId: "",
|
|
24
27
|
})
|
|
25
28
|
|
|
26
29
|
interface TabsProps extends React.ComponentProps<typeof TabsPrimitive.Root> {
|
|
27
30
|
size?: "sm" | "md" | "lg"
|
|
31
|
+
/** Provide a stable id in SSR environments to avoid hydration mismatches. */
|
|
32
|
+
id?: string
|
|
28
33
|
}
|
|
29
34
|
|
|
30
|
-
function Tabs({ className, size = "md", ...props }: TabsProps) {
|
|
35
|
+
function Tabs({ className, size = "md", id, ...props }: TabsProps) {
|
|
36
|
+
const generatedId = useId()
|
|
37
|
+
const baseId = id ?? generatedId
|
|
31
38
|
return (
|
|
32
|
-
<TabsContext.Provider value={{ size }}>
|
|
39
|
+
<TabsContext.Provider value={{ size, baseId }}>
|
|
33
40
|
<TabsPrimitive.Root
|
|
34
41
|
data-slot="tabs"
|
|
35
42
|
className={cn("flex flex-col gap-2", className)}
|
|
43
|
+
id={baseId}
|
|
36
44
|
{...props}
|
|
37
45
|
/>
|
|
38
46
|
</TabsContext.Provider>
|
|
@@ -55,7 +63,7 @@ function TabsList({
|
|
|
55
63
|
)
|
|
56
64
|
}
|
|
57
65
|
|
|
58
|
-
type GlowDirection = "bottom" | "top"
|
|
66
|
+
type GlowDirection = "bottom" | "top" | "left" | "right"
|
|
59
67
|
|
|
60
68
|
interface TabsTriggerProps extends React.ComponentProps<
|
|
61
69
|
typeof TabsPrimitive.Trigger
|
|
@@ -63,17 +71,33 @@ interface TabsTriggerProps extends React.ComponentProps<
|
|
|
63
71
|
className?: string
|
|
64
72
|
size?: "sm" | "md" | "lg"
|
|
65
73
|
glowDirection?: GlowDirection
|
|
74
|
+
/** Provide a stable id in SSR environments to avoid hydration mismatches */
|
|
75
|
+
id?: string
|
|
66
76
|
}
|
|
67
77
|
|
|
68
78
|
function TabsTrigger({
|
|
69
79
|
className,
|
|
70
80
|
size: sizeProp,
|
|
71
81
|
glowDirection = "bottom",
|
|
82
|
+
id: idProp,
|
|
72
83
|
...props
|
|
73
84
|
}: TabsTriggerProps) {
|
|
74
|
-
const { size: contextSize } = React.useContext(TabsContext)
|
|
85
|
+
const { size: contextSize, baseId } = React.useContext(TabsContext)
|
|
75
86
|
const size = sizeProp || contextSize
|
|
76
87
|
|
|
88
|
+
const generatedId = useId()
|
|
89
|
+
const sanitizedId = idProp?.replace(/\s+/g, "-") ?? generatedId
|
|
90
|
+
const gradientId = `gradient-${sanitizedId}`
|
|
91
|
+
const filterId = `filter-${sanitizedId}`
|
|
92
|
+
|
|
93
|
+
const sanitizedValue = props.value?.replace(/\s+/g, "-")
|
|
94
|
+
const triggerId = sanitizedValue
|
|
95
|
+
? `${baseId}-trigger-${sanitizedValue}`
|
|
96
|
+
: sanitizedId
|
|
97
|
+
const contentId = sanitizedValue
|
|
98
|
+
? `${baseId}-content-${sanitizedValue}`
|
|
99
|
+
: undefined
|
|
100
|
+
|
|
77
101
|
const getGlowConfig = (direction: GlowDirection) => {
|
|
78
102
|
switch (direction) {
|
|
79
103
|
case "top":
|
|
@@ -91,6 +115,36 @@ function TabsTrigger({
|
|
|
91
115
|
edgeGradientTransform: "matrix(0 -4.03571 32.0526 0 32 0)",
|
|
92
116
|
}
|
|
93
117
|
|
|
118
|
+
case "left":
|
|
119
|
+
return {
|
|
120
|
+
containerClass: "absolute left-0 top-0 h-full w-1/2",
|
|
121
|
+
ellipse: { cx: "15", cy: "44", rx: "4", ry: "32" },
|
|
122
|
+
gradientTransform: "matrix(-16.1429 0 0 32.0526 1 44)",
|
|
123
|
+
viewBox: "0 0 25 92",
|
|
124
|
+
dimensions: { width: "25", height: "92" },
|
|
125
|
+
filter: { x: "-15", y: "-4", width: "40", height: "96" },
|
|
126
|
+
edgeClass: "absolute left-0 top-0 h-full w-[3px]",
|
|
127
|
+
edgePath: "M1 0S-.173 19.503 .022 32 1 64 1 64h1L1.995 0z",
|
|
128
|
+
edgeViewBox: "0 0 2 64",
|
|
129
|
+
edgeDimensions: { width: "2", height: "64" },
|
|
130
|
+
edgeGradientTransform: "matrix(-4.03571 0 0 32.0526 0 32)",
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
case "right":
|
|
134
|
+
return {
|
|
135
|
+
containerClass: "absolute right-0 top-0 h-full w-1/2",
|
|
136
|
+
ellipse: { cx: "15", cy: "44", rx: "4", ry: "32" },
|
|
137
|
+
gradientTransform: "matrix(-16.1429 0 0 32.0526 1 44)",
|
|
138
|
+
viewBox: "0 0 25 92",
|
|
139
|
+
dimensions: { width: "25", height: "92" },
|
|
140
|
+
filter: { x: "0", y: "-4", width: "40", height: "96" },
|
|
141
|
+
edgeClass: "absolute -right-0.5 top-0 h-full",
|
|
142
|
+
edgePath: "M1 0S-.173 19.503 .022 32 1 64 1 64h1L1.995 0z",
|
|
143
|
+
edgeViewBox: "0 0 2 64",
|
|
144
|
+
edgeDimensions: { width: "2", height: "64" },
|
|
145
|
+
edgeGradientTransform: "matrix(4.03571 0 0 -32.0526 2 32)",
|
|
146
|
+
}
|
|
147
|
+
|
|
94
148
|
case "bottom":
|
|
95
149
|
default:
|
|
96
150
|
return {
|
|
@@ -118,16 +172,18 @@ function TabsTrigger({
|
|
|
118
172
|
"group text-fm-tertiary relative inline-flex flex-1 items-center justify-center border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-[color,box-shadow]",
|
|
119
173
|
"focus-visible:ring-fm-primary focus-visible:ring-offset-fm-contrast outline-none focus-visible:ring-2 focus-visible:ring-offset-2",
|
|
120
174
|
"disabled:pointer-events-none disabled:opacity-50",
|
|
121
|
-
"data-[state=active]:text-fm-primary hover:text-fm-
|
|
175
|
+
"data-[state=active]:text-fm-primary hover:text-fm-primary",
|
|
122
176
|
tabsVariants({ size }),
|
|
123
177
|
className
|
|
124
178
|
)}
|
|
125
179
|
{...props}
|
|
180
|
+
id={triggerId}
|
|
181
|
+
aria-controls={contentId}
|
|
126
182
|
>
|
|
127
183
|
{props.children}
|
|
128
184
|
<div
|
|
129
185
|
className={cn(
|
|
130
|
-
"group-data-[state=active]:animate-fm-fadeIn
|
|
186
|
+
"group-data-[state=active]:animate-fm-fadeIn pointer-events-none absolute top-0 right-0 -bottom-0.5 left-0 z-10 group-data-[state=inactive]:hidden sm:-right-3"
|
|
131
187
|
)}
|
|
132
188
|
>
|
|
133
189
|
{/* Main glow ellipse */}
|
|
@@ -136,22 +192,24 @@ function TabsTrigger({
|
|
|
136
192
|
width={config.dimensions.width}
|
|
137
193
|
height={config.dimensions.height}
|
|
138
194
|
viewBox={config.viewBox}
|
|
195
|
+
aria-hidden={true}
|
|
196
|
+
focusable={false}
|
|
139
197
|
fill="none"
|
|
140
198
|
className={config.containerClass}
|
|
141
199
|
>
|
|
142
|
-
<g filter=
|
|
200
|
+
<g filter={`url(#${filterId})`}>
|
|
143
201
|
<ellipse
|
|
144
202
|
cx={config.ellipse.cx}
|
|
145
203
|
cy={config.ellipse.cy}
|
|
146
204
|
rx={config.ellipse.rx}
|
|
147
205
|
ry={config.ellipse.ry}
|
|
148
|
-
fill=
|
|
206
|
+
fill={`url(#${gradientId})`}
|
|
149
207
|
style={{ mixBlendMode: "screen" }}
|
|
150
208
|
/>
|
|
151
209
|
</g>
|
|
152
210
|
<defs>
|
|
153
211
|
<radialGradient
|
|
154
|
-
id=
|
|
212
|
+
id={gradientId}
|
|
155
213
|
cx="0"
|
|
156
214
|
cy="0"
|
|
157
215
|
r="1"
|
|
@@ -163,7 +221,7 @@ function TabsTrigger({
|
|
|
163
221
|
<stop offset="1" stopColor="var(--color-fm-neutral-50)" />
|
|
164
222
|
</radialGradient>
|
|
165
223
|
<filter
|
|
166
|
-
id=
|
|
224
|
+
id={filterId}
|
|
167
225
|
x={config.filter.x}
|
|
168
226
|
y={config.filter.y}
|
|
169
227
|
width={config.filter.width}
|
|
@@ -179,7 +237,7 @@ function TabsTrigger({
|
|
|
179
237
|
/>
|
|
180
238
|
<feGaussianBlur
|
|
181
239
|
stdDeviation="8"
|
|
182
|
-
result="
|
|
240
|
+
result="effect1_foregroundBlur_default"
|
|
183
241
|
/>
|
|
184
242
|
</filter>
|
|
185
243
|
</defs>
|
|
@@ -190,18 +248,20 @@ function TabsTrigger({
|
|
|
190
248
|
xmlns="http://www.w3.org/2000/svg"
|
|
191
249
|
width={config.edgeDimensions.width}
|
|
192
250
|
height={config.edgeDimensions.height}
|
|
251
|
+
aria-hidden={true}
|
|
252
|
+
focusable={false}
|
|
193
253
|
viewBox={config.edgeViewBox}
|
|
194
254
|
fill="none"
|
|
195
255
|
className={config.edgeClass}
|
|
196
256
|
>
|
|
197
257
|
<path
|
|
198
258
|
d={config.edgePath}
|
|
199
|
-
fill=
|
|
259
|
+
fill={`url(#${gradientId}-edge)`}
|
|
200
260
|
style={{ mixBlendMode: "screen" }}
|
|
201
261
|
/>
|
|
202
262
|
<defs>
|
|
203
263
|
<radialGradient
|
|
204
|
-
id=
|
|
264
|
+
id={`${gradientId}-edge`}
|
|
205
265
|
cx="0"
|
|
206
266
|
cy="0"
|
|
207
267
|
r="1"
|
|
@@ -210,7 +270,11 @@ function TabsTrigger({
|
|
|
210
270
|
>
|
|
211
271
|
<stop stopColor="var(--color-fm-primary-600)" />
|
|
212
272
|
<stop offset=".61" stopColor="var(--color-fm-secondary-300)" />
|
|
213
|
-
<stop
|
|
273
|
+
<stop
|
|
274
|
+
offset="1"
|
|
275
|
+
stopColor="var(--color-fm-neutral-50)"
|
|
276
|
+
stopOpacity="0.2"
|
|
277
|
+
/>
|
|
214
278
|
</radialGradient>
|
|
215
279
|
</defs>
|
|
216
280
|
</svg>
|
|
@@ -219,15 +283,35 @@ function TabsTrigger({
|
|
|
219
283
|
)
|
|
220
284
|
}
|
|
221
285
|
|
|
286
|
+
interface TabsContentProps extends React.ComponentProps<
|
|
287
|
+
typeof TabsPrimitive.Content
|
|
288
|
+
> {
|
|
289
|
+
id?: string
|
|
290
|
+
}
|
|
291
|
+
|
|
222
292
|
function TabsContent({
|
|
223
293
|
className,
|
|
294
|
+
id: idProp,
|
|
295
|
+
value,
|
|
224
296
|
...props
|
|
225
|
-
}:
|
|
297
|
+
}: TabsContentProps) {
|
|
298
|
+
const { baseId } = React.useContext(TabsContext)
|
|
299
|
+
const sanitizedValue = value?.replace(/\s+/g, "-")
|
|
300
|
+
const contentId =
|
|
301
|
+
idProp ??
|
|
302
|
+
(sanitizedValue ? `${baseId}-content-${sanitizedValue}` : undefined)
|
|
303
|
+
const triggerId = sanitizedValue
|
|
304
|
+
? `${baseId}-trigger-${sanitizedValue}`
|
|
305
|
+
: undefined
|
|
306
|
+
|
|
226
307
|
return (
|
|
227
308
|
<TabsPrimitive.Content
|
|
228
309
|
data-slot="tabs-content"
|
|
229
310
|
className={cn("flex-1 outline-none", className)}
|
|
230
311
|
{...props}
|
|
312
|
+
id={contentId}
|
|
313
|
+
value={value}
|
|
314
|
+
aria-labelledby={triggerId}
|
|
231
315
|
/>
|
|
232
316
|
)
|
|
233
317
|
}
|