aural-ui 2.0.7 → 2.0.9

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.
@@ -3,6 +3,8 @@ import { Button } from "@components/button"
3
3
  import Label from "@components/label"
4
4
  import type { Meta, StoryObj } from "@storybook/react"
5
5
 
6
+ import { CrossIcon, TickIcon } from "src/ui/icons"
7
+
6
8
  import { Switch } from "."
7
9
 
8
10
  const meta: Meta<typeof Switch> = {
@@ -90,3 +92,19 @@ export const WithForm: Story = {
90
92
  </form>
91
93
  ),
92
94
  }
95
+
96
+ export const WithIcons: Story = {
97
+ args: {
98
+ checked: false,
99
+ onIcon: <TickIcon />,
100
+ offIcon: <CrossIcon />,
101
+ },
102
+ }
103
+
104
+ export const CheckedWithIcons: Story = {
105
+ args: {
106
+ checked: true,
107
+ onIcon: <TickIcon />,
108
+ offIcon: <CrossIcon />,
109
+ },
110
+ }
@@ -2,33 +2,53 @@ import * as React from "react"
2
2
  import { cn } from "@lib/utils"
3
3
  import * as SwitchPrimitives from "@radix-ui/react-switch"
4
4
 
5
+ type SwitchWithIconsProps = React.ComponentPropsWithoutRef<
6
+ typeof SwitchPrimitives.Root
7
+ > & {
8
+ offIcon?: React.ReactNode
9
+ onIcon?: React.ReactNode
10
+ }
11
+
5
12
  const Switch = React.forwardRef<
6
13
  React.ElementRef<typeof SwitchPrimitives.Root>,
7
- React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>
8
- >(({ className, ...props }, ref) => (
14
+ SwitchWithIconsProps
15
+ >(({ className, onIcon, offIcon, checked, disabled, ...props }, ref) => (
9
16
  <SwitchPrimitives.Root
17
+ ref={ref}
18
+ checked={checked}
19
+ disabled={disabled}
10
20
  className={cn(
11
- "data-[state=checked]:not-[:disabled]:border-fm-divider-positive data-[state=checked]:not-[:disabled]:bg-fm-green-50 data-[state=unchecked]:not-[:disabled]:border-fm-divider-primary data-[state=unchecked]:not-[:disabled]:bg-fm-surface-primary data-[state=unchecked]:disabled:border-fm-divider-tertiary data-[state=unchecked]:disabled:bg-fm-surface-secondary data-[state=checked]:disabled:border-fm-green-100 data-[state=checked]:disabled:bg-fm-green-50 focus-visible:ring-fm-primary focus-visible:ring-offset-fm-green-50 hover:bg-fm-surface-secondary data-[state=unchecked]:not-[:disabled]:hover:bg-fm-surface-secondary h-8 w-14 rounded-full border border-solid transition-all duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2",
12
- "data-[state=unchecked]:not-[:disabled]:hover:border-fm-divider-primary relative disabled:cursor-not-allowed",
21
+ "data-[state=checked]:not-[:disabled]:border-fm-divider-positive data-[state=checked]:not-[:disabled]:bg-fm-green-50",
22
+ "data-[state=unchecked]:not-[:disabled]:border-fm-divider-primary data-[state=unchecked]:not-[:disabled]:bg-fm-surface-primary",
23
+ "data-[state=unchecked]:disabled:border-fm-divider-tertiary data-[state=unchecked]:disabled:bg-fm-surface-secondary",
24
+ "data-[state=checked]:disabled:border-fm-green-100 data-[state=checked]:disabled:bg-fm-green-50",
25
+ "focus-visible:ring-fm-primary focus-visible:ring-offset-fm-green-50",
26
+ "hover:bg-fm-surface-secondary data-[state=unchecked]:not-[:disabled]:hover:bg-fm-surface-secondary",
27
+ "data-[state=unchecked]:not-[:disabled]:hover:border-fm-divider-primary",
28
+ "relative h-8 w-14 rounded-full border border-solid transition-all duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed",
13
29
  className
14
30
  )}
15
31
  {...props}
16
- ref={ref}
17
32
  >
18
33
  <span
19
- className="font-fm-brand text-fm-positive data-[disabled=true]:text-fm-positive-tert absolute top-1/2 left-2 -translate-y-1/2 [font-size:var(--text-fm-sm)]"
20
- data-state={props.checked ? "checked" : "unchecked"}
21
- data-disabled={props.disabled || undefined}
34
+ className={cn(
35
+ "font-fm-brand text-fm-positive absolute top-1/2 left-2 -translate-y-1/2 [font-size:var(--text-fm-sm)]",
36
+ disabled && "text-fm-positive-tert"
37
+ )}
38
+ data-state={checked ? "checked" : "unchecked"}
39
+ data-disabled={disabled || undefined}
22
40
  >
23
- ON
41
+ {onIcon ?? "ON"}
24
42
  </span>
43
+
25
44
  <span
26
45
  className="font-fm-brand text-fm-tertiary absolute top-1/2 right-1.5 -translate-y-1/2 [font-size:var(--text-fm-sm)]"
27
- data-state={props.checked ? "checked" : "unchecked"}
28
- data-disabled={props.disabled || undefined}
46
+ data-state={checked ? "checked" : "unchecked"}
47
+ data-disabled={disabled || undefined}
29
48
  >
30
- OFF
49
+ {offIcon ?? "OFF"}
31
50
  </span>
51
+
32
52
  <SwitchPrimitives.Thumb className="bg-fm-icon-active data-[disabled]:bg-fm-icon-inactive pointer-events-none z-10 block size-6 rounded-full shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-7 data-[state=unchecked]:translate-x-1" />
33
53
  </SwitchPrimitives.Root>
34
54
  ))
@@ -71,7 +71,8 @@ A beautiful and accessible tabs component built on top of Radix UI's Tabs primit
71
71
  ## Visual Effects
72
72
 
73
73
  ### Active Tab Glow
74
- The active tab features a stunning dual-layer gradient effect:
74
+ The active tab features a stunning dual-layer gradient effect with configurable direction:
75
+ - **Glow Direction**: Choose between "bottom" (default) or "top" glow positioning
75
76
  - **Bottom Layer**: Curved gradient line at the tab base
76
77
  - **Top Layer**: Blurred elliptical glow with screen blend mode
77
78
  - **Colors**: Red to purple to black gradient (#FF3E4C → #5200A3 → #0A0A0A)
@@ -113,6 +114,20 @@ The active tab features a stunning dual-layer gradient effect:
113
114
  </Tabs>
114
115
  \`\`\`
115
116
 
117
+ ### With Glow Direction
118
+ \`\`\`tsx
119
+ <Tabs defaultValue="overview" size="md">
120
+ <TabsList>
121
+ <TabsTrigger value="overview" glowDirection="bottom">Overview</TabsTrigger>
122
+ <TabsTrigger value="analytics" glowDirection="top">Analytics</TabsTrigger>
123
+ <TabsTrigger value="settings" glowDirection="bottom">Settings</TabsTrigger>
124
+ </TabsList>
125
+ <TabsContent value="overview">Overview content</TabsContent>
126
+ <TabsContent value="analytics">Analytics content</TabsContent>
127
+ <TabsContent value="settings">Settings content</TabsContent>
128
+ </Tabs>
129
+ \`\`\`
130
+
116
131
  ### Individual Size Override
117
132
  \`\`\`tsx
118
133
  <Tabs defaultValue="home" size="md">
@@ -135,6 +150,7 @@ The active tab features a stunning dual-layer gradient effect:
135
150
 
136
151
  ### TabsTrigger Props
137
152
  - **size**: "sm" | "md" | "lg" - Optional override for individual triggers
153
+ - **glowDirection**: "bottom" | "top" - Direction of the glow effect (default: "bottom")
138
154
  - **value**: string - Unique identifier for the tab
139
155
  - **disabled**: boolean - Disable the tab trigger
140
156
 
@@ -1162,7 +1178,168 @@ export const ComplexContent: Story = {
1162
1178
  },
1163
1179
  }
1164
1180
 
1165
- // 6. Interactive States (keeping the same as it demonstrates good functionality)
1181
+ // 6. Glow Direction Variations
1182
+ export const GlowDirectionVariations: Story = {
1183
+ render: () => (
1184
+ <div className="w-full max-w-4xl space-y-8">
1185
+ <div className="space-y-4">
1186
+ <h3 className="text-lg font-medium text-white">
1187
+ Bottom Glow (Default)
1188
+ </h3>
1189
+ <Tabs defaultValue="tab1" size="md" className="w-full">
1190
+ <TabsList className="grid w-full grid-cols-3">
1191
+ <TabsTrigger value="tab1" glowDirection="bottom">
1192
+ Dashboard
1193
+ </TabsTrigger>
1194
+ <TabsTrigger value="tab2" glowDirection="bottom">
1195
+ Reports
1196
+ </TabsTrigger>
1197
+ <TabsTrigger value="tab3" glowDirection="bottom">
1198
+ Profile
1199
+ </TabsTrigger>
1200
+ </TabsList>
1201
+ <TabsContent value="tab1" className="mt-4">
1202
+ <div className="rounded-lg border border-white/10 bg-white/5 p-6">
1203
+ <p className="text-white/70">
1204
+ Bottom glow creates an underline effect that emphasizes the
1205
+ tab's connection to content below.
1206
+ </p>
1207
+ </div>
1208
+ </TabsContent>
1209
+ <TabsContent value="tab2" className="mt-4">
1210
+ <div className="rounded-lg border border-white/10 bg-white/5 p-6">
1211
+ <p className="text-white/70">
1212
+ Perfect for interfaces where you want to draw attention to the
1213
+ content area.
1214
+ </p>
1215
+ </div>
1216
+ </TabsContent>
1217
+ <TabsContent value="tab3" className="mt-4">
1218
+ <div className="rounded-lg border border-white/10 bg-white/5 p-6">
1219
+ <p className="text-white/70">
1220
+ Creates a strong visual connection between the tab and its
1221
+ associated content.
1222
+ </p>
1223
+ </div>
1224
+ </TabsContent>
1225
+ </Tabs>
1226
+ </div>
1227
+
1228
+ <div className="space-y-4">
1229
+ <h3 className="text-lg font-medium text-white">Top Glow</h3>
1230
+ <Tabs defaultValue="tab1" size="md" className="w-full">
1231
+ <TabsList className="grid w-full grid-cols-3">
1232
+ <TabsTrigger value="tab1" glowDirection="top">
1233
+ Overview
1234
+ </TabsTrigger>
1235
+ <TabsTrigger value="tab2" glowDirection="top">
1236
+ Analytics
1237
+ </TabsTrigger>
1238
+ <TabsTrigger value="tab3" glowDirection="top">
1239
+ Settings
1240
+ </TabsTrigger>
1241
+ </TabsList>
1242
+ <TabsContent value="tab1" className="mt-4">
1243
+ <div className="rounded-lg border border-white/10 bg-white/5 p-6">
1244
+ <p className="text-white/70">
1245
+ Top glow creates a subtle highlight effect above the active tab,
1246
+ perfect for elevated interfaces.
1247
+ </p>
1248
+ </div>
1249
+ </TabsContent>
1250
+ <TabsContent value="tab2" className="mt-4">
1251
+ <div className="rounded-lg border border-white/10 bg-white/5 p-6">
1252
+ <p className="text-white/70">
1253
+ Alternative glow direction for different visual effects.
1254
+ </p>
1255
+ </div>
1256
+ </TabsContent>
1257
+ <TabsContent value="tab3" className="mt-4">
1258
+ <div className="rounded-lg border border-white/10 bg-white/5 p-6">
1259
+ <p className="text-white/70">
1260
+ Ideal for creating variety in your interface design.
1261
+ </p>
1262
+ </div>
1263
+ </TabsContent>
1264
+ </Tabs>
1265
+ </div>
1266
+
1267
+ <div className="space-y-4">
1268
+ <h3 className="text-lg font-medium text-white">
1269
+ Mixed Glow Directions
1270
+ </h3>
1271
+ <Tabs defaultValue="tab1" size="md" className="w-full">
1272
+ <TabsList className="grid w-full grid-cols-4">
1273
+ <TabsTrigger value="tab1" glowDirection="bottom">
1274
+ Bottom Glow
1275
+ </TabsTrigger>
1276
+ <TabsTrigger value="tab2" glowDirection="top">
1277
+ Top Glow
1278
+ </TabsTrigger>
1279
+ <TabsTrigger value="tab3" glowDirection="bottom">
1280
+ Bottom Glow
1281
+ </TabsTrigger>
1282
+ <TabsTrigger value="tab4" glowDirection="top">
1283
+ Top Glow
1284
+ </TabsTrigger>
1285
+ </TabsList>
1286
+ <TabsContent value="tab1" className="mt-4">
1287
+ <div className="rounded-lg border border-white/10 bg-white/5 p-6">
1288
+ <p className="text-white/70">
1289
+ You can mix and match glow directions for different visual
1290
+ effects.
1291
+ </p>
1292
+ </div>
1293
+ </TabsContent>
1294
+ <TabsContent value="tab2" className="mt-4">
1295
+ <div className="rounded-lg border border-white/10 bg-white/5 p-6">
1296
+ <p className="text-white/70">
1297
+ Each tab can have its own glow direction for maximum
1298
+ flexibility.
1299
+ </p>
1300
+ </div>
1301
+ </TabsContent>
1302
+ <TabsContent value="tab3" className="mt-4">
1303
+ <div className="rounded-lg border border-white/10 bg-white/5 p-6">
1304
+ <p className="text-white/70">
1305
+ Useful for creating visual hierarchy or emphasizing specific
1306
+ tabs.
1307
+ </p>
1308
+ </div>
1309
+ </TabsContent>
1310
+ <TabsContent value="tab4" className="mt-4">
1311
+ <div className="rounded-lg border border-white/10 bg-white/5 p-6">
1312
+ <p className="text-white/70">
1313
+ Experiment with different combinations to achieve your desired
1314
+ design.
1315
+ </p>
1316
+ </div>
1317
+ </TabsContent>
1318
+ </Tabs>
1319
+ </div>
1320
+
1321
+ <div className="rounded-lg border border-blue-500/20 bg-blue-500/10 p-4">
1322
+ <div className="text-sm text-blue-200">
1323
+ <strong>Note:</strong> The glowDirection prop allows you to customize
1324
+ the visual effect of active tabs. Use "bottom" (default) for an
1325
+ underline-style glow or "top" for a subtle highlight effect. You can
1326
+ mix different directions within the same tab group for creative
1327
+ designs.
1328
+ </div>
1329
+ </div>
1330
+ </div>
1331
+ ),
1332
+ parameters: {
1333
+ docs: {
1334
+ description: {
1335
+ story:
1336
+ "Demonstrates the glowDirection prop with different configurations. Shows top glow (default), bottom glow, and mixed directions for various visual effects.",
1337
+ },
1338
+ },
1339
+ },
1340
+ }
1341
+
1342
+ // 7. Interactive States (keeping the same as it demonstrates good functionality)
1166
1343
  export const InteractiveStates: Story = {
1167
1344
  render: () => {
1168
1345
  const [activeTab, setActiveTab] = React.useState("normal")
@@ -55,19 +55,60 @@ function TabsList({
55
55
  )
56
56
  }
57
57
 
58
+ type GlowDirection = "bottom" | "top"
59
+
58
60
  interface TabsTriggerProps
59
61
  extends React.ComponentProps<typeof TabsPrimitive.Trigger> {
60
- size?: "sm" | "md" | "lg" // Optional override for individual triggers
61
62
  className?: string
63
+ size?: "sm" | "md" | "lg"
64
+ glowDirection?: GlowDirection
62
65
  }
63
66
 
64
67
  function TabsTrigger({
65
68
  className,
66
69
  size: sizeProp,
70
+ glowDirection = "bottom",
67
71
  ...props
68
72
  }: TabsTriggerProps) {
69
73
  const { size: contextSize } = React.useContext(TabsContext)
70
- const size = sizeProp || contextSize // Individual size prop overrides context
74
+ const size = sizeProp || contextSize
75
+
76
+ const getGlowConfig = (direction: GlowDirection) => {
77
+ switch (direction) {
78
+ case "top":
79
+ return {
80
+ containerClass: "absolute top-0 left-0 w-full h-1/2",
81
+ ellipse: { cx: "44", cy: "15", rx: "32", ry: "4" },
82
+ gradientTransform: "matrix(0 -16.1429 32.0526 0 44 1)",
83
+ viewBox: "0 0 92 25",
84
+ dimensions: { width: "92", height: "25" },
85
+ filter: { x: "-4", y: "-15", width: "96", height: "40" },
86
+ edgeClass: "absolute top-0 left-0 w-full h-[3.2px]",
87
+ edgePath: "M0 1S19.503-.173 32 .022 64 1 64 1v1L0 1.995z",
88
+ edgeViewBox: "0 0 64 2",
89
+ edgeDimensions: { width: "64", height: "2" },
90
+ edgeGradientTransform: "matrix(0 -4.03571 32.0526 0 32 0)",
91
+ }
92
+
93
+ case "bottom":
94
+ default:
95
+ return {
96
+ containerClass: "absolute bottom-0 left-0 w-full h-1/2",
97
+ ellipse: { cx: "44", cy: "20", rx: "32", ry: "4" },
98
+ gradientTransform: "matrix(0 16.1429 -32.0526 0 44 24)",
99
+ viewBox: "0 0 92 25",
100
+ dimensions: { width: "92", height: "25" },
101
+ filter: { x: "-4", y: "0", width: "96", height: "40" },
102
+ edgeClass: "absolute bottom-0 left-0 w-full h-[3px]",
103
+ edgePath: "M0 1S19.503-.173 32 .022 64 1 64 1v1L0 1.995z",
104
+ edgeViewBox: "0 0 64 2",
105
+ edgeDimensions: { width: "64", height: "2" },
106
+ edgeGradientTransform: "matrix(0 4.03571 -32.0526 0 32 2)",
107
+ }
108
+ }
109
+ }
110
+
111
+ const config = getGlowConfig(glowDirection)
71
112
 
72
113
  return (
73
114
  <TabsPrimitive.Trigger
@@ -83,21 +124,26 @@ function TabsTrigger({
83
124
  {...props}
84
125
  >
85
126
  {props.children}
86
- <div className="group-data-[state=active]:animate-fm-fadeIn group-hover:!animate-fm-fadeIn group-data-[state=inactive]:animate-fm-fadeOut pointer-events-none absolute top-0 right-0 -bottom-0.5 left-0 z-10">
127
+ <div
128
+ className={cn(
129
+ "group-data-[state=active]:animate-fm-fadeIn group-hover:!animate-fm-fadeIn group-data-[state=inactive]:animate-fm-fadeOut pointer-events-none absolute top-0 right-0 -bottom-0.5 left-0 z-10"
130
+ )}
131
+ >
132
+ {/* Main glow ellipse */}
87
133
  <svg
88
134
  xmlns="http://www.w3.org/2000/svg"
89
- width="92"
90
- height="25"
91
- viewBox="0 0 92 25"
135
+ width={config.dimensions.width}
136
+ height={config.dimensions.height}
137
+ viewBox={config.viewBox}
92
138
  fill="none"
93
- className="absolute bottom-0 h-1/2 w-full"
139
+ className={config.containerClass}
94
140
  >
95
141
  <g filter="url(#b)">
96
142
  <ellipse
97
- cx="44"
98
- cy="20"
99
- rx="32"
100
- ry="4"
143
+ cx={config.ellipse.cx}
144
+ cy={config.ellipse.cy}
145
+ rx={config.ellipse.rx}
146
+ ry={config.ellipse.ry}
101
147
  fill="url(#c)"
102
148
  style={{ mixBlendMode: "screen" }}
103
149
  />
@@ -109,7 +155,7 @@ function TabsTrigger({
109
155
  cy="0"
110
156
  r="1"
111
157
  gradientUnits="userSpaceOnUse"
112
- gradientTransform="matrix(0 16.1429 -32.0526 0 44 24)"
158
+ gradientTransform={config.gradientTransform}
113
159
  >
114
160
  <stop stopColor="var(--color-fm-primary-600)" />
115
161
  <stop offset=".805" stopColor="var(--color-fm-secondary-300)" />
@@ -117,10 +163,10 @@ function TabsTrigger({
117
163
  </radialGradient>
118
164
  <filter
119
165
  id="b"
120
- x="-4"
121
- y="0"
122
- width="96"
123
- height="40"
166
+ x={config.filter.x}
167
+ y={config.filter.y}
168
+ width={config.filter.width}
169
+ height={config.filter.height}
124
170
  filterUnits="userSpaceOnUse"
125
171
  colorInterpolationFilters="sRGB"
126
172
  >
@@ -137,16 +183,18 @@ function TabsTrigger({
137
183
  </filter>
138
184
  </defs>
139
185
  </svg>
186
+
187
+ {/* Edge line */}
140
188
  <svg
141
189
  xmlns="http://www.w3.org/2000/svg"
142
- width="64"
143
- height="2"
144
- viewBox="0 0 64 2"
190
+ width={config.edgeDimensions.width}
191
+ height={config.edgeDimensions.height}
192
+ viewBox={config.edgeViewBox}
145
193
  fill="none"
146
- className="absolute bottom-0 h-[3px] w-full"
194
+ className={config.edgeClass}
147
195
  >
148
196
  <path
149
- d="M0 1S19.503-.173 32 .022 64 1 64 1v1L0 1.995z"
197
+ d={config.edgePath}
150
198
  fill="url(#a)"
151
199
  style={{ mixBlendMode: "screen" }}
152
200
  />
@@ -157,7 +205,7 @@ function TabsTrigger({
157
205
  cy="0"
158
206
  r="1"
159
207
  gradientUnits="userSpaceOnUse"
160
- gradientTransform="matrix(0 4.03571 -32.0526 0 32 2)"
208
+ gradientTransform={config.edgeGradientTransform}
161
209
  >
162
210
  <stop stopColor="var(--color-fm-primary-600)" />
163
211
  <stop offset=".61" stopColor="var(--color-fm-secondary-300)" />
@@ -42,6 +42,7 @@ export * from "./light-bulb-simple-icon"
42
42
  export * from "./magic-book-icon"
43
43
  export * from "./maintenance-icon"
44
44
  export * from "./message-icon"
45
+ export * from "./moon-icon"
45
46
  export * from "./move-horizontal-icon"
46
47
  export * from "./move-vertical-icon"
47
48
  export * from "./musical-note-icon"
@@ -60,6 +61,7 @@ export * from "./spinner-gradient-icon"
60
61
  export * from "./spinner-solid-icon"
61
62
  export * from "./spinner-solid-neutral-icon"
62
63
  export * from "./suggestion-icon"
64
+ export * from "./sun-icon"
63
65
  export * from "./text-color-icon"
64
66
  export * from "./text-indicator-icon"
65
67
  export * from "./tick-circle-icon"