aural-ui 4.2.4 → 4.4.0
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/aspect-ratio/AspectRatio.stories.tsx +4 -4
- package/dist/components/badge/Badge.stories.tsx +3 -3
- package/dist/components/button/Button.stories.tsx +288 -366
- package/dist/components/button/index.tsx +3 -43
- package/dist/components/card/Card.stories.tsx +2 -2
- package/dist/components/card/index.tsx +1 -1
- package/dist/components/clamp-lines/index.tsx +0 -1
- package/dist/components/drawer/index.tsx +1 -1
- package/dist/components/hover-card/HoverCard.stories.tsx +7 -7
- package/dist/components/index.ts +1 -1
- package/dist/components/marquee/Marquee.stories.tsx +2 -6
- package/dist/components/offer-label/OfferLabel.stories.tsx +267 -0
- package/dist/components/offer-label/index.tsx +32 -0
- package/dist/components/offer-label/meta.ts +6 -0
- package/dist/components/otp-inputs/OtpInputs.stories.tsx +1 -1
- package/dist/components/popover/Popover.stories.tsx +1 -1
- package/dist/components/slider/index.tsx +1 -6
- package/dist/components/table/Table.stories.tsx +4 -4
- package/dist/components/toggle/Toggle.stories.tsx +4 -4
- package/dist/components/tooltip/index.tsx +1 -2
- package/dist/components/typography/Typography.stories.tsx +7 -19
- package/dist/components/typography/index.tsx +0 -2
- package/dist/fonts/LabGrotesque-Medium.ttf +0 -0
- package/dist/icons/all-icons.tsx +9 -7
- package/dist/icons/chain-link-icon/ChainLinkIcon.stories.tsx +167 -0
- package/dist/icons/chain-link-icon/index.tsx +30 -0
- package/dist/icons/chain-link-icon/meta.ts +8 -0
- package/dist/icons/clock-icon/ClockIcon.stories.tsx +210 -0
- package/dist/icons/clock-icon/index.tsx +28 -0
- package/dist/icons/clock-icon/meta.ts +8 -0
- package/dist/icons/headphone-icon/HeadphoneIcon.stories.tsx +186 -0
- package/dist/icons/headphone-icon/index.tsx +49 -0
- package/dist/icons/headphone-icon/meta.ts +8 -0
- package/dist/icons/index.ts +5 -1
- package/dist/icons/site-logo-icon/SiteLogoIcon.stories.tsx +3 -3
- package/dist/icons/underline-icon/UnderlineIcon.stories.tsx +238 -0
- package/dist/icons/underline-icon/index.tsx +58 -0
- package/dist/icons/underline-icon/meta.ts +8 -0
- package/dist/index.cjs +86 -86
- package/dist/index.js +86 -86
- package/package.json +5 -5
- package/dist/fonts/LabGrotesqueTRIAL-Bold.otf +0 -0
- package/dist/fonts/LabGrotesqueTRIAL-Light.otf +0 -0
- package/dist/fonts/LabGrotesqueTRIAL-Medium.otf +0 -0
- package/dist/fonts/LabGrotesqueTRIAL-Regular.otf +0 -0
- package/dist/fonts/PPSupplySans-Regular (1).otf +0 -0
- package/dist/fonts/PPSupplySans-Ultralight.otf +0 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react-vite"
|
|
3
|
+
|
|
4
|
+
import { AudioBarIcon } from "src/ui/icons/audio-bar-icon"
|
|
5
|
+
import { HeadIcon } from "src/ui/icons/head-icon"
|
|
6
|
+
import { MusicalNoteIcon } from "src/ui/icons/musical-note-icon"
|
|
7
|
+
import { VolumeFullIcon } from "src/ui/icons/volume-full-icon"
|
|
8
|
+
import {
|
|
9
|
+
IconColorVariations,
|
|
10
|
+
IconDefaultCanvas,
|
|
11
|
+
IconPlaygroundCanvas,
|
|
12
|
+
IconSizeVariations,
|
|
13
|
+
IconUsageCanvas,
|
|
14
|
+
IconUsageSection,
|
|
15
|
+
} from "src/ui/story-spec/icons/icon-story-canvas"
|
|
16
|
+
import { AuralIconDocsPage } from "src/ui/story-spec/icons/icon-story-docs-page"
|
|
17
|
+
|
|
18
|
+
import { HeadphoneIcon } from "."
|
|
19
|
+
|
|
20
|
+
const meta: Meta<typeof HeadphoneIcon> = {
|
|
21
|
+
title: "Icons/HeadphoneIcon",
|
|
22
|
+
component: HeadphoneIcon,
|
|
23
|
+
parameters: {
|
|
24
|
+
layout: "fullscreen",
|
|
25
|
+
backgrounds: {
|
|
26
|
+
default: "dark",
|
|
27
|
+
values: [
|
|
28
|
+
{ name: "dark", value: "var(--color-fm-surface-primary)" },
|
|
29
|
+
{ name: "darker", value: "var(--color-fm-neutral-0)" },
|
|
30
|
+
{ name: "light", value: "var(--color-fm-neutral-1100)" },
|
|
31
|
+
],
|
|
32
|
+
},
|
|
33
|
+
docs: {
|
|
34
|
+
page: () => (
|
|
35
|
+
<AuralIconDocsPage
|
|
36
|
+
accentToken="info"
|
|
37
|
+
features={[
|
|
38
|
+
{ title: "Audio Listening", description: "Headphone idle state" },
|
|
39
|
+
{ title: "Navigation", description: "Tab bar unselected state" },
|
|
40
|
+
{ title: "Accessible", description: "ARIA-ready by default" },
|
|
41
|
+
]}
|
|
42
|
+
quickStart={{
|
|
43
|
+
codeExample: `import { HeadphoneIcon } from "src/ui/icons/headphone-icon"
|
|
44
|
+
|
|
45
|
+
function ListenTab() {
|
|
46
|
+
return (
|
|
47
|
+
<div className="flex flex-col items-center gap-1">
|
|
48
|
+
<HeadphoneIcon className="h-6 w-6 text-fm-icon-inactive" />
|
|
49
|
+
<span className="text-fm-secondary text-xs">Listen</span>
|
|
50
|
+
</div>
|
|
51
|
+
)
|
|
52
|
+
}`,
|
|
53
|
+
livePreview: (
|
|
54
|
+
<div className="flex flex-col items-center gap-1">
|
|
55
|
+
<HeadphoneIcon className="text-fm-icon-inactive h-6 w-6" />
|
|
56
|
+
<span className="text-fm-secondary font-fm-text text-xs">
|
|
57
|
+
Listen
|
|
58
|
+
</span>
|
|
59
|
+
</div>
|
|
60
|
+
),
|
|
61
|
+
}}
|
|
62
|
+
relatedIcons={[
|
|
63
|
+
{
|
|
64
|
+
name: "AudioBarIcon",
|
|
65
|
+
description: "Audio playback bars",
|
|
66
|
+
icon: AudioBarIcon,
|
|
67
|
+
accentToken: "info",
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: "VolumeFullIcon",
|
|
71
|
+
description: "Full volume indicator",
|
|
72
|
+
icon: VolumeFullIcon,
|
|
73
|
+
accentToken: "positive",
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: "MusicalNoteIcon",
|
|
77
|
+
description: "Music / audio content",
|
|
78
|
+
icon: MusicalNoteIcon,
|
|
79
|
+
accentToken: "warning",
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: "HeadIcon",
|
|
83
|
+
description: "Narrator or character",
|
|
84
|
+
icon: HeadIcon,
|
|
85
|
+
accentToken: "negative",
|
|
86
|
+
},
|
|
87
|
+
]}
|
|
88
|
+
/>
|
|
89
|
+
),
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
tags: ["autodocs"],
|
|
93
|
+
argTypes: {
|
|
94
|
+
className: {
|
|
95
|
+
control: "text",
|
|
96
|
+
description: "CSS classes including color token",
|
|
97
|
+
},
|
|
98
|
+
withAccessibility: {
|
|
99
|
+
control: "boolean",
|
|
100
|
+
description: "Wrap with accessibility label",
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export default meta
|
|
106
|
+
type Story = StoryObj<typeof HeadphoneIcon>
|
|
107
|
+
|
|
108
|
+
export const Default: Story = {
|
|
109
|
+
args: {
|
|
110
|
+
className: "h-6 w-6 text-fm-icon-inactive",
|
|
111
|
+
withAccessibility: true,
|
|
112
|
+
},
|
|
113
|
+
render: (args) => (
|
|
114
|
+
<IconDefaultCanvas>
|
|
115
|
+
<HeadphoneIcon {...args} />
|
|
116
|
+
</IconDefaultCanvas>
|
|
117
|
+
),
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export const SizeVariations: Story = {
|
|
121
|
+
render: () => <IconSizeVariations icon={HeadphoneIcon} />,
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export const ColorVariations: Story = {
|
|
125
|
+
render: () => <IconColorVariations icon={HeadphoneIcon} />,
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export const UsageExamples: Story = {
|
|
129
|
+
render: () => (
|
|
130
|
+
<IconUsageCanvas>
|
|
131
|
+
<IconUsageSection title="Tab Bar Navigation">
|
|
132
|
+
<div className="border-fm-divider-secondary bg-fm-surface-secondary flex w-full max-w-sm items-center justify-around rounded-xl border px-4 py-3">
|
|
133
|
+
{["Home", "Listen", "Library"].map((label, i) => (
|
|
134
|
+
<div key={label} className="flex flex-col items-center gap-1">
|
|
135
|
+
<HeadphoneIcon
|
|
136
|
+
className={`h-6 w-6 ${i === 1 ? "text-fm-icon-active" : "text-fm-icon-inactive"}`}
|
|
137
|
+
/>
|
|
138
|
+
<span
|
|
139
|
+
className={`font-fm-text text-xs ${i === 1 ? "text-fm-primary" : "text-fm-secondary"}`}
|
|
140
|
+
>
|
|
141
|
+
{label}
|
|
142
|
+
</span>
|
|
143
|
+
</div>
|
|
144
|
+
))}
|
|
145
|
+
</div>
|
|
146
|
+
</IconUsageSection>
|
|
147
|
+
|
|
148
|
+
<IconUsageSection title="Audio Player Header">
|
|
149
|
+
<div className="border-fm-divider-secondary bg-fm-surface-secondary flex w-full max-w-sm items-center gap-3 rounded-xl border p-4">
|
|
150
|
+
<div className="bg-fm-divider-secondary flex h-10 w-10 items-center justify-center rounded-full">
|
|
151
|
+
<HeadphoneIcon className="text-fm-icon-inactive h-5 w-5" />
|
|
152
|
+
</div>
|
|
153
|
+
<div>
|
|
154
|
+
<div className="text-fm-primary font-fm-text text-sm font-medium">
|
|
155
|
+
Now Playing
|
|
156
|
+
</div>
|
|
157
|
+
<div className="text-fm-secondary font-fm-text text-xs">
|
|
158
|
+
Tap to open player
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
</IconUsageSection>
|
|
163
|
+
|
|
164
|
+
<IconUsageSection title="Empty State">
|
|
165
|
+
<div className="flex flex-col items-center gap-3 py-4">
|
|
166
|
+
<HeadphoneIcon className="text-fm-icon-inactive h-12 w-12" />
|
|
167
|
+
<p className="text-fm-secondary font-fm-text text-sm">
|
|
168
|
+
No audio playing
|
|
169
|
+
</p>
|
|
170
|
+
</div>
|
|
171
|
+
</IconUsageSection>
|
|
172
|
+
</IconUsageCanvas>
|
|
173
|
+
),
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export const Playground: Story = {
|
|
177
|
+
args: {
|
|
178
|
+
className: "h-8 w-8 text-fm-icon-inactive",
|
|
179
|
+
withAccessibility: true,
|
|
180
|
+
},
|
|
181
|
+
render: (args) => (
|
|
182
|
+
<IconPlaygroundCanvas>
|
|
183
|
+
<HeadphoneIcon {...args} />
|
|
184
|
+
</IconPlaygroundCanvas>
|
|
185
|
+
),
|
|
186
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import { AccessibleIcon } from "@radix-ui/react-accessible-icon"
|
|
3
|
+
|
|
4
|
+
export interface IHeadphoneIconProps extends React.SVGProps<SVGSVGElement> {
|
|
5
|
+
withAccessibility?: boolean
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const HeadphoneIcon = (props: IHeadphoneIconProps) => {
|
|
9
|
+
const { withAccessibility = true, ...svgProps } = props
|
|
10
|
+
|
|
11
|
+
const svg = (
|
|
12
|
+
<svg
|
|
13
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
14
|
+
width="24"
|
|
15
|
+
height="24"
|
|
16
|
+
viewBox="0 0 24 24"
|
|
17
|
+
fill="none"
|
|
18
|
+
{...svgProps}
|
|
19
|
+
>
|
|
20
|
+
<path
|
|
21
|
+
d="M3 18V11C3 8.61305 3.94821 6.32387 5.63604 4.63604C7.32387 2.94821 9.61305 2 12 2C14.3869 2 16.6761 2.94821 18.364 4.63604C20.0518 6.32387 21 8.61305 21 11V18"
|
|
22
|
+
stroke="currentColor"
|
|
23
|
+
strokeWidth="1.5"
|
|
24
|
+
strokeLinecap="round"
|
|
25
|
+
strokeLinejoin="round"
|
|
26
|
+
/>
|
|
27
|
+
<path
|
|
28
|
+
d="M21 19C21 19.5304 20.7893 20.0391 20.4142 20.4142C20.0391 20.7893 19.5304 21 19 21H18C17.4696 21 16.9609 20.7893 16.5858 20.4142C16.2107 20.0391 16 19.5304 16 19V16C16 15.4696 16.2107 14.9609 16.5858 14.5858C16.9609 14.2107 17.4696 14 18 14H21V19ZM3 19C3 19.5304 3.21071 20.0391 3.58579 20.4142C3.96086 20.7893 4.46957 21 5 21H6C6.53043 21 7.03914 20.7893 7.41421 20.4142C7.78929 20.0391 8 19.5304 8 19V16C8 15.4696 7.78929 14.9609 7.41421 14.5858C7.03914 14.2107 6.53043 14 6 14H3V19Z"
|
|
29
|
+
stroke="currentColor"
|
|
30
|
+
strokeWidth="1.5"
|
|
31
|
+
strokeLinecap="round"
|
|
32
|
+
strokeLinejoin="round"
|
|
33
|
+
/>
|
|
34
|
+
<path
|
|
35
|
+
d="M12 22V7"
|
|
36
|
+
stroke="currentColor"
|
|
37
|
+
strokeWidth="1.5"
|
|
38
|
+
strokeLinecap="round"
|
|
39
|
+
strokeLinejoin="round"
|
|
40
|
+
/>
|
|
41
|
+
</svg>
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
if (withAccessibility) {
|
|
45
|
+
return <AccessibleIcon label="Headphone Icon">{svg}</AccessibleIcon>
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return svg
|
|
49
|
+
}
|
package/dist/icons/index.ts
CHANGED
|
@@ -16,14 +16,16 @@ export * from "./bubble-crossed-icon"
|
|
|
16
16
|
export * from "./bubble-sparkle-icon"
|
|
17
17
|
export * from "./camera-icon"
|
|
18
18
|
export * from "./capital-a-letter-icon"
|
|
19
|
+
export * from "./chain-link-icon"
|
|
19
20
|
export * from "./chevron-double-left-icon"
|
|
20
21
|
export * from "./chevron-double-right-icon"
|
|
21
22
|
export * from "./chevron-down-icon"
|
|
22
23
|
export * from "./chevron-left-icon"
|
|
23
|
-
export * from "./circular-play-icon"
|
|
24
24
|
export * from "./chevron-right-icon"
|
|
25
25
|
export * from "./chevron-up-icon"
|
|
26
26
|
export * from "./circle-tick-icon"
|
|
27
|
+
export * from "./circular-play-icon"
|
|
28
|
+
export * from "./clock-icon"
|
|
27
29
|
export * from "./column-wide-add-icon"
|
|
28
30
|
export * from "./coin-icon"
|
|
29
31
|
export * from "./coin-toons-icon"
|
|
@@ -47,6 +49,7 @@ export * from "./globe-icon"
|
|
|
47
49
|
export * from "./google-logo-icon"
|
|
48
50
|
export * from "./grip-vertical-icon"
|
|
49
51
|
export * from "./head-icon"
|
|
52
|
+
export * from "./headphone-icon"
|
|
50
53
|
export * from "./heart-icon"
|
|
51
54
|
export * from "./image-avatar-sparkle-icon"
|
|
52
55
|
export * from "./image-icon"
|
|
@@ -103,6 +106,7 @@ export * from "./tick-circle-icon"
|
|
|
103
106
|
export * from "./tick-icon"
|
|
104
107
|
export * from "./trash-icon"
|
|
105
108
|
export * from "./twitter-x-icon"
|
|
109
|
+
export * from "./underline-icon"
|
|
106
110
|
export * from "./upload-icon"
|
|
107
111
|
export * from "./vertical-menu-icon"
|
|
108
112
|
export * from "./video-play-list-icon"
|
|
@@ -46,7 +46,7 @@ function Header() {
|
|
|
46
46
|
return (
|
|
47
47
|
<header className="flex items-center gap-2">
|
|
48
48
|
<SiteLogoIcon className="h-8 w-8" />
|
|
49
|
-
<span className="text-fm-primary font-fm-text font-
|
|
49
|
+
<span className="text-fm-primary font-fm-text font-medium">
|
|
50
50
|
Pocket FM
|
|
51
51
|
</span>
|
|
52
52
|
</header>
|
|
@@ -164,7 +164,7 @@ export const UsageExamples: Story = {
|
|
|
164
164
|
<div className="border-fm-divider-secondary bg-fm-surface-secondary flex w-full max-w-sm items-center justify-between rounded-xl border px-4 py-3">
|
|
165
165
|
<div className="flex items-center gap-2">
|
|
166
166
|
<SiteLogoIcon className="h-8 w-8" />
|
|
167
|
-
<span className="text-fm-primary font-fm-text text-sm font-
|
|
167
|
+
<span className="text-fm-primary font-fm-text text-sm font-medium">
|
|
168
168
|
Pocket FM
|
|
169
169
|
</span>
|
|
170
170
|
</div>
|
|
@@ -174,7 +174,7 @@ export const UsageExamples: Story = {
|
|
|
174
174
|
<IconUsageSection title="Login Screen Branding">
|
|
175
175
|
<div className="border-fm-divider-secondary bg-fm-surface-secondary flex w-full max-w-sm flex-col items-center gap-3 rounded-xl border p-6">
|
|
176
176
|
<SiteLogoIcon className="h-16 w-16" />
|
|
177
|
-
<div className="text-fm-primary font-fm-text text-lg font-
|
|
177
|
+
<div className="text-fm-primary font-fm-text text-lg font-medium">
|
|
178
178
|
Welcome to Pocket FM
|
|
179
179
|
</div>
|
|
180
180
|
<div className="text-fm-secondary font-fm-text text-sm">
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react-vite"
|
|
3
|
+
|
|
4
|
+
import { CapitalALetterIcon } from "src/ui/icons/capital-a-letter-icon"
|
|
5
|
+
import { FileTextIcon } from "src/ui/icons/file-text-icon"
|
|
6
|
+
import { TextColorIcon } from "src/ui/icons/text-color-icon"
|
|
7
|
+
import { TextIndicatorIcon } from "src/ui/icons/text-indicator-icon"
|
|
8
|
+
import {
|
|
9
|
+
IconDefaultCanvas,
|
|
10
|
+
IconPlaygroundCanvas,
|
|
11
|
+
IconUsageCanvas,
|
|
12
|
+
IconUsageSection,
|
|
13
|
+
} from "src/ui/story-spec/icons/icon-story-canvas"
|
|
14
|
+
import { AuralIconDocsPage } from "src/ui/story-spec/icons/icon-story-docs-page"
|
|
15
|
+
|
|
16
|
+
import { UnderlineIcon } from "."
|
|
17
|
+
|
|
18
|
+
const meta: Meta<typeof UnderlineIcon> = {
|
|
19
|
+
title: "Icons/UnderlineIcon",
|
|
20
|
+
component: UnderlineIcon,
|
|
21
|
+
parameters: {
|
|
22
|
+
layout: "fullscreen",
|
|
23
|
+
backgrounds: {
|
|
24
|
+
default: "dark",
|
|
25
|
+
values: [
|
|
26
|
+
{ name: "dark", value: "var(--color-fm-surface-primary)" },
|
|
27
|
+
{ name: "darker", value: "var(--color-fm-neutral-0)" },
|
|
28
|
+
{ name: "light", value: "var(--color-fm-neutral-1100)" },
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
docs: {
|
|
32
|
+
page: () => (
|
|
33
|
+
<AuralIconDocsPage
|
|
34
|
+
accentToken="warning"
|
|
35
|
+
features={[
|
|
36
|
+
{ title: "Gradient Stroke", description: "Custom gradient colors" },
|
|
37
|
+
{ title: "Decorative Mark", description: "Accent underline style" },
|
|
38
|
+
{ title: "Accessible", description: "ARIA-ready by default" },
|
|
39
|
+
]}
|
|
40
|
+
quickStart={{
|
|
41
|
+
codeExample: `import { UnderlineIcon } from "src/ui/icons/underline-icon"
|
|
42
|
+
|
|
43
|
+
function HighlightedHeading() {
|
|
44
|
+
return (
|
|
45
|
+
<span className="relative inline-block pb-1">
|
|
46
|
+
<span className="text-fm-primary font-fm-brand text-2xl font-bold">
|
|
47
|
+
Featured
|
|
48
|
+
</span>
|
|
49
|
+
<UnderlineIcon
|
|
50
|
+
className="absolute left-0 h-1 w-7.5"
|
|
51
|
+
withAccessibility={false}
|
|
52
|
+
/>
|
|
53
|
+
</span>
|
|
54
|
+
)
|
|
55
|
+
}`,
|
|
56
|
+
livePreview: (
|
|
57
|
+
<span className="relative inline-block pb-1">
|
|
58
|
+
<span className="text-fm-primary font-fm-brand text-2xl font-bold">
|
|
59
|
+
Featured
|
|
60
|
+
</span>
|
|
61
|
+
<UnderlineIcon
|
|
62
|
+
className="absolute left-0 h-1 w-7.5"
|
|
63
|
+
withAccessibility={false}
|
|
64
|
+
/>
|
|
65
|
+
</span>
|
|
66
|
+
),
|
|
67
|
+
}}
|
|
68
|
+
relatedIcons={[
|
|
69
|
+
{
|
|
70
|
+
name: "TextColorIcon",
|
|
71
|
+
description: "Text formatting accent",
|
|
72
|
+
icon: TextColorIcon,
|
|
73
|
+
accentToken: "info",
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: "CapitalALetterIcon",
|
|
77
|
+
description: "Typography size marker",
|
|
78
|
+
icon: CapitalALetterIcon,
|
|
79
|
+
accentToken: "positive",
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: "TextIndicatorIcon",
|
|
83
|
+
description: "Inline text indicator",
|
|
84
|
+
icon: TextIndicatorIcon,
|
|
85
|
+
accentToken: "warning",
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
name: "FileTextIcon",
|
|
89
|
+
description: "Document with text",
|
|
90
|
+
icon: FileTextIcon,
|
|
91
|
+
accentToken: "negative",
|
|
92
|
+
},
|
|
93
|
+
]}
|
|
94
|
+
/>
|
|
95
|
+
),
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
tags: ["autodocs"],
|
|
99
|
+
argTypes: {
|
|
100
|
+
fromColor: {
|
|
101
|
+
control: "color",
|
|
102
|
+
description: "Gradient start color",
|
|
103
|
+
},
|
|
104
|
+
toColor: {
|
|
105
|
+
control: "color",
|
|
106
|
+
description: "Gradient end color",
|
|
107
|
+
},
|
|
108
|
+
width: {
|
|
109
|
+
control: { type: "range", min: 30, max: 400, step: 10 },
|
|
110
|
+
description: "Width in pixels — keep ~7:1 ratio with height",
|
|
111
|
+
},
|
|
112
|
+
height: {
|
|
113
|
+
control: { type: "range", min: 4, max: 60, step: 1 },
|
|
114
|
+
description: "Height in pixels — keep ~7:1 ratio with width",
|
|
115
|
+
},
|
|
116
|
+
withAccessibility: {
|
|
117
|
+
control: "boolean",
|
|
118
|
+
description: "Wrap with accessibility label",
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export default meta
|
|
124
|
+
type Story = StoryObj<typeof UnderlineIcon>
|
|
125
|
+
|
|
126
|
+
// Natural viewBox is 32 × 5 (6.4:1). Default uses 3× scale (96 × 15).
|
|
127
|
+
export const Default: Story = {
|
|
128
|
+
args: {
|
|
129
|
+
width: 96,
|
|
130
|
+
height: 15,
|
|
131
|
+
fromColor: "#F9FFB8",
|
|
132
|
+
toColor: "#CFFF3F",
|
|
133
|
+
withAccessibility: true,
|
|
134
|
+
},
|
|
135
|
+
render: (args) => (
|
|
136
|
+
<IconDefaultCanvas>
|
|
137
|
+
<UnderlineIcon {...args} />
|
|
138
|
+
</IconDefaultCanvas>
|
|
139
|
+
),
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export const UsageExamples: Story = {
|
|
143
|
+
render: () => (
|
|
144
|
+
<IconUsageCanvas>
|
|
145
|
+
<IconUsageSection title="Decorated Heading">
|
|
146
|
+
<div className="space-y-2">
|
|
147
|
+
<span className="relative inline-block pb-1">
|
|
148
|
+
<h2 className="text-fm-primary font-fm-brand text-2xl font-bold">
|
|
149
|
+
Our Story
|
|
150
|
+
</h2>
|
|
151
|
+
<UnderlineIcon
|
|
152
|
+
className="absolute left-0 h-1 w-7.5"
|
|
153
|
+
withAccessibility={false}
|
|
154
|
+
/>
|
|
155
|
+
</span>
|
|
156
|
+
<p className="text-fm-secondary font-fm-text text-sm">
|
|
157
|
+
Building the future of audio entertainment.
|
|
158
|
+
</p>
|
|
159
|
+
</div>
|
|
160
|
+
</IconUsageSection>
|
|
161
|
+
|
|
162
|
+
<IconUsageSection title="Feature Card Accent">
|
|
163
|
+
<div className="border-fm-divider-secondary bg-fm-surface-secondary w-full max-w-sm rounded-xl border p-5">
|
|
164
|
+
<div className="mb-3">
|
|
165
|
+
<span className="relative inline-block pb-1">
|
|
166
|
+
<span className="text-fm-primary font-fm-brand text-base font-semibold">
|
|
167
|
+
Premium Audio
|
|
168
|
+
</span>
|
|
169
|
+
<UnderlineIcon
|
|
170
|
+
className="absolute left-0 h-1 w-7.5"
|
|
171
|
+
fromColor="#F9FFB8"
|
|
172
|
+
toColor="#CFFF3F"
|
|
173
|
+
withAccessibility={false}
|
|
174
|
+
/>
|
|
175
|
+
</span>
|
|
176
|
+
</div>
|
|
177
|
+
<p className="text-fm-secondary font-fm-text text-sm leading-relaxed">
|
|
178
|
+
Immersive listening experience crafted for creators.
|
|
179
|
+
</p>
|
|
180
|
+
</div>
|
|
181
|
+
</IconUsageSection>
|
|
182
|
+
|
|
183
|
+
<IconUsageSection title="Highlighted Quote">
|
|
184
|
+
<div className="border-fm-divider-secondary bg-fm-surface-secondary w-full max-w-sm rounded-xl border p-5">
|
|
185
|
+
<div className="flex flex-col gap-3">
|
|
186
|
+
<span className="relative inline-block self-start pb-1">
|
|
187
|
+
<blockquote className="text-fm-primary font-fm-text text-sm italic">
|
|
188
|
+
“Audio is the future.”
|
|
189
|
+
</blockquote>
|
|
190
|
+
<UnderlineIcon
|
|
191
|
+
className="absolute left-0 h-1 w-7.5"
|
|
192
|
+
fromColor="#F9FFB8"
|
|
193
|
+
toColor="#CFFF3F"
|
|
194
|
+
withAccessibility={false}
|
|
195
|
+
/>
|
|
196
|
+
</span>
|
|
197
|
+
<span className="text-fm-secondary font-fm-text text-xs">
|
|
198
|
+
— Product Team
|
|
199
|
+
</span>
|
|
200
|
+
</div>
|
|
201
|
+
</div>
|
|
202
|
+
</IconUsageSection>
|
|
203
|
+
|
|
204
|
+
<IconUsageSection title="Section Label Accent">
|
|
205
|
+
<div className="flex flex-wrap gap-6">
|
|
206
|
+
{["Discover", "Trending", "For You"].map((label) => (
|
|
207
|
+
<span key={label} className="relative inline-block pb-1">
|
|
208
|
+
<span className="text-fm-primary font-fm-brand text-lg font-semibold">
|
|
209
|
+
{label}
|
|
210
|
+
</span>
|
|
211
|
+
<UnderlineIcon
|
|
212
|
+
className="absolute left-0 h-1 w-7.5"
|
|
213
|
+
fromColor="#F9FFB8"
|
|
214
|
+
toColor="#CFFF3F"
|
|
215
|
+
withAccessibility={false}
|
|
216
|
+
/>
|
|
217
|
+
</span>
|
|
218
|
+
))}
|
|
219
|
+
</div>
|
|
220
|
+
</IconUsageSection>
|
|
221
|
+
</IconUsageCanvas>
|
|
222
|
+
),
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export const Playground: Story = {
|
|
226
|
+
args: {
|
|
227
|
+
width: 192,
|
|
228
|
+
height: 30,
|
|
229
|
+
fromColor: "#F9FFB8",
|
|
230
|
+
toColor: "#CFFF3F",
|
|
231
|
+
withAccessibility: true,
|
|
232
|
+
},
|
|
233
|
+
render: (args) => (
|
|
234
|
+
<IconPlaygroundCanvas>
|
|
235
|
+
<UnderlineIcon {...args} />
|
|
236
|
+
</IconPlaygroundCanvas>
|
|
237
|
+
),
|
|
238
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import React, { useId } from "react"
|
|
2
|
+
import { AccessibleIcon } from "@radix-ui/react-accessible-icon"
|
|
3
|
+
|
|
4
|
+
export interface IUnderlineIconProps extends React.SVGProps<SVGSVGElement> {
|
|
5
|
+
fromColor?: string
|
|
6
|
+
toColor?: string
|
|
7
|
+
withAccessibility?: boolean
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const UnderlineIcon = (props: IUnderlineIconProps) => {
|
|
11
|
+
const {
|
|
12
|
+
fromColor = "#F9FFB8",
|
|
13
|
+
toColor = "#CFFF3F",
|
|
14
|
+
withAccessibility = true,
|
|
15
|
+
id: idProp,
|
|
16
|
+
...svgProps
|
|
17
|
+
} = props
|
|
18
|
+
|
|
19
|
+
const autoId = useId()
|
|
20
|
+
const gradientId = `underline-gradient-${idProp ?? autoId}`
|
|
21
|
+
|
|
22
|
+
const svg = (
|
|
23
|
+
<svg
|
|
24
|
+
viewBox="0 0 32 5"
|
|
25
|
+
fill="none"
|
|
26
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
27
|
+
aria-hidden="true"
|
|
28
|
+
{...svgProps}
|
|
29
|
+
>
|
|
30
|
+
<path
|
|
31
|
+
d="M0.499845 2.55202C0.499845 2.55202 13.9101 0.777228 21.7693 0.643631C29.6284 0.510034 31.7935 0.279256 30.6512 1.00823C29.509 1.73721 24.0827 4.08102 24.6916 4.06392C25.3005 4.04681 30.6783 4.04011 30.6783 4.04011"
|
|
32
|
+
stroke={`url(#${gradientId})`}
|
|
33
|
+
strokeWidth={1}
|
|
34
|
+
strokeLinecap="round"
|
|
35
|
+
strokeLinejoin="round"
|
|
36
|
+
/>
|
|
37
|
+
<defs>
|
|
38
|
+
<linearGradient
|
|
39
|
+
id={gradientId}
|
|
40
|
+
x1="1.76936"
|
|
41
|
+
y1="3.77954"
|
|
42
|
+
x2="6.01158"
|
|
43
|
+
y2="-7.79593"
|
|
44
|
+
gradientUnits="userSpaceOnUse"
|
|
45
|
+
>
|
|
46
|
+
<stop style={{ stopColor: fromColor }} />
|
|
47
|
+
<stop offset="1" style={{ stopColor: toColor }} />
|
|
48
|
+
</linearGradient>
|
|
49
|
+
</defs>
|
|
50
|
+
</svg>
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
if (withAccessibility) {
|
|
54
|
+
return <AccessibleIcon label="Underline icon">{svg}</AccessibleIcon>
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return svg
|
|
58
|
+
}
|