aural-ui 3.0.7 → 4.1.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 +290 -1199
- package/dist/components/avatar/Avatar.stories.tsx +235 -237
- package/dist/components/badge/Badge.stories.tsx +379 -116
- package/dist/components/banner/Banner.stories.tsx +445 -391
- package/dist/components/breadcrumb/Breadcrumb.stories.tsx +453 -199
- package/dist/components/button/Button.stories.tsx +585 -230
- package/dist/components/button/index.tsx +7 -7
- package/dist/components/card/Card.stories.tsx +619 -301
- package/dist/components/char-count/CharCount.stories.tsx +350 -248
- package/dist/components/checkbox/Checkbox.stories.tsx +309 -167
- package/dist/components/chip/Chip.stories.tsx +362 -168
- package/dist/components/circular-loader/CircularLoader.stories.tsx +221 -620
- package/dist/components/clamp-lines/ClampLines.stories.tsx +246 -117
- package/dist/components/collapsible/Collapsible.stories.tsx +391 -252
- package/dist/components/command/Command.stories.tsx +533 -856
- package/dist/components/dialog/Dialog.stories.tsx +505 -949
- package/dist/components/divider/Divider.stories.tsx +265 -502
- package/dist/components/dot-loader/DotLoader.stories.tsx +256 -257
- package/dist/components/drawer/Drawer.stories.tsx +659 -993
- package/dist/components/drawer/index.tsx +3 -3
- package/dist/components/dropdown/Dropdown.stories.tsx +643 -1018
- package/dist/components/form/Form.stories.tsx +560 -274
- package/dist/components/helper-text/HelperText.stories.tsx +199 -200
- package/dist/components/hover-card/HoverCard.stories.tsx +318 -1221
- package/dist/components/icon-button/IconButton.stories.tsx +837 -194
- package/dist/components/if-else/if-else.stories.tsx +370 -83
- package/dist/components/input/Input.stories.tsx +436 -368
- package/dist/components/label/Label.stories.tsx +156 -154
- package/dist/components/list/List.stories.tsx +485 -822
- package/dist/components/marquee/Marquee.stories.tsx +356 -694
- package/dist/components/otp-inputs/OtpInputs.stories.tsx +352 -410
- package/dist/components/overlay/Overlay.stories.tsx +452 -818
- package/dist/components/overlay/index.tsx +4 -4
- package/dist/components/pagination/Pagination.stories.tsx +721 -210
- package/dist/components/popover/Popover.stories.tsx +484 -873
- package/dist/components/radio/Radio.stories.tsx +432 -124
- package/dist/components/resizable/Resizable.stories.tsx +496 -752
- package/dist/components/scroll-area/ScrollArea.stories.tsx +384 -1006
- package/dist/components/search/Search.stories.tsx +314 -575
- package/dist/components/select/Select.stories.tsx +684 -787
- package/dist/components/sheet/Sheet.stories.tsx +671 -936
- package/dist/components/skelton/Skelton.stories.tsx +230 -764
- package/dist/components/slider/Slider.stories.tsx +384 -737
- package/dist/components/stepper/Stepper.stories.tsx +371 -514
- package/dist/components/switch/Switch.stories.tsx +461 -208
- package/dist/components/switch-case/SwitchCase.stories.tsx +367 -188
- package/dist/components/table/Table.stories.tsx +770 -914
- package/dist/components/tabs/Tabs.stories.tsx +459 -1400
- package/dist/components/tag/Tag.stories.tsx +714 -542
- package/dist/components/textarea/TextArea.stories.tsx +621 -562
- package/dist/components/thumbnail-tags/ThumbnailTags.stories.tsx +228 -148
- package/dist/components/toast/Toast.stories.tsx +452 -1333
- package/dist/components/toggle/Toggle.stories.tsx +488 -909
- package/dist/components/tooltip/Tooltip.stories.tsx +344 -1372
- package/dist/components/typography/Typography.stories.tsx +406 -89
- package/dist/hooks/use-change-state/UseChangeState.stories.tsx +309 -606
- package/dist/hooks/use-previous/UsePrevious.stories.tsx +367 -917
- package/dist/hooks/use-standalone-pagination/UseStandalonePagination.stories.tsx +639 -867
- package/dist/icons/Icons.stories.tsx +0 -12
- package/dist/icons/ai-avatar-icon/AiAvatarIcon.stories.tsx +226 -1013
- package/dist/icons/alert-icon/AlertIcon.stories.tsx +109 -929
- package/dist/icons/all-icons.tsx +124 -87
- package/dist/icons/angle-down-icon/AngleDownIcon.stories.tsx +140 -971
- package/dist/icons/apple-logo-icon/AppleLogoIcon.stories.tsx +148 -888
- package/dist/icons/arrow-box-left-icon/ArrowBoxLeftIcon.stories.tsx +135 -1019
- package/dist/icons/arrow-corner-up-left-icon/ArrowCornerUpLeftIcon.stories.tsx +137 -953
- package/dist/icons/arrow-corner-up-right-icon/ArrowCornerUpRightIcon.stories.tsx +138 -997
- package/dist/icons/arrow-left-icon/ArrowLeftIcon.stories.tsx +136 -942
- package/dist/icons/arrow-right-icon/ArrowRightIcon.stories.tsx +148 -1092
- package/dist/icons/arrow-right-up-icon/ArrowRightUpIcon.stories.tsx +146 -1211
- package/dist/icons/art-board-icon/ArtBoardIcon.stories.tsx +126 -615
- package/dist/icons/audio-bar-icon/AudioBarIcon.stories.tsx +144 -1164
- package/dist/icons/backward-ten-seconds-icon/BackwardTenSecondsIcon.stories.tsx +167 -985
- package/dist/icons/bubble-check-icon/BubbleCheckIcon.stories.tsx +122 -1179
- package/dist/icons/bubble-crossed-icon/BubbleCrossedIcon.stories.tsx +124 -1168
- package/dist/icons/bubble-sparkle-icon/BubbleSparkleIcon.stories.tsx +119 -850
- package/dist/icons/camera-icon/CameraIcon.stories.tsx +112 -1213
- package/dist/icons/capital-a-letter-icon/CapitalALetterIcon.stories.tsx +117 -934
- package/dist/icons/chevron-double-left-icon/ChevronDoubleLeftIcon.stories.tsx +160 -961
- package/dist/icons/chevron-double-right-icon/ChevronDoubleRightIcon.stories.tsx +163 -961
- package/dist/icons/chevron-down-icon/ChevronDownIcon.stories.tsx +144 -942
- package/dist/icons/chevron-left-icon/ChevronLeftIcon.stories.tsx +129 -966
- package/dist/icons/chevron-right-icon/ChevronRightIcon.stories.tsx +147 -964
- package/dist/icons/chevron-up-icon/ChevronUpIcon.stories.tsx +145 -975
- package/dist/icons/circle-tick-icon/CircleTickIcon.stories.tsx +150 -1142
- package/dist/icons/circular-play-icon/CircularPlayIcon.stories.tsx +114 -461
- package/dist/icons/coin-icon/CoinIcon.stories.tsx +124 -1322
- package/dist/icons/coin-toons-icon/CoinToonsIcon.stories.tsx +117 -1318
- package/dist/icons/column-wide-add-icon/ColumnWideAddIcon.stories.tsx +114 -903
- package/dist/icons/command-icon/CommandIcon.stories.tsx +127 -1042
- package/dist/icons/copy-icon/CopyIcon.stories.tsx +123 -962
- package/dist/icons/cross-circle-icon/CrossCircleIcon.stories.tsx +147 -999
- package/dist/icons/cross-icon/CrossIcon.stories.tsx +139 -960
- package/dist/icons/download-icon/DownloadIcon.stories.tsx +126 -820
- package/dist/icons/edit-big-icon/EditBigIcon.stories.tsx +124 -1031
- package/dist/icons/email-icon/EmailIcon.stories.tsx +115 -936
- package/dist/icons/expand-icon/ExpandIcon.stories.tsx +112 -1111
- package/dist/icons/eye-close-icon/EyeCloseIcon.stories.tsx +144 -1025
- package/dist/icons/eye-open-icon/EyeOpenIcon.stories.tsx +143 -1036
- package/dist/icons/feature-shine-icon/FeatureShineIcon.stories.tsx +127 -1011
- package/dist/icons/file-chart-icon/FileChartIcon.stories.tsx +126 -1056
- package/dist/icons/file-text-icon/FileTextIcon.stories.tsx +125 -614
- package/dist/icons/filter-bar-row-icon/FilterBarRowIcon.stories.tsx +119 -1050
- package/dist/icons/forward-ten-seconds-icon/ForwardTenSecondsIcon.stories.tsx +169 -989
- package/dist/icons/git-branch-icon/GitBranchIcon.stories.tsx +115 -1145
- package/dist/icons/git-fork-icon/GitForkIcon.stories.tsx +115 -1122
- package/dist/icons/globe-icon/GlobeIcon.stories.tsx +130 -313
- package/dist/icons/google-logo-icon/GoogleLogoIcon.stories.tsx +145 -940
- package/dist/icons/grip-vertical-icon/GripVerticalIcon.stories.tsx +119 -1174
- package/dist/icons/head-icon/HeadIcon.stories.tsx +111 -916
- package/dist/icons/heart-icon/HeartIcon.stories.tsx +120 -1019
- package/dist/icons/image-avatar-sparkle-icon/ImageAvatarSparkleIcon.stories.tsx +119 -683
- package/dist/icons/image-icon/ImageIcon.stories.tsx +105 -1121
- package/dist/icons/import-folder-icon/ImportFolderIcon.stories.tsx +111 -1192
- package/dist/icons/import-left-arrow-folder-icon/ImportLeftArrowFolderIcon.stories.tsx +136 -1256
- package/dist/icons/indian-flag-icon/IndianFlagIcon.stories.tsx +159 -962
- package/dist/icons/instagram-icon/InstagramIcon.stories.tsx +161 -1385
- package/dist/icons/layout-column-icon/LayoutColumnIcon.stories.tsx +124 -972
- package/dist/icons/layout-left-icon/LayoutLeftIcon.stories.tsx +119 -948
- package/dist/icons/layout-right-icon/LayoutRightIcon.stories.tsx +119 -942
- package/dist/icons/light-bulb-simple-icon/LightBulbSimpleIcon.stories.tsx +108 -1215
- package/dist/icons/linked-in-icon/LinkedInIcon.stories.tsx +154 -1517
- package/dist/icons/magic-book-icon/MagicBookIcon.stories.tsx +110 -1188
- package/dist/icons/magic-edit-icon/MagicEditIcon.stories.tsx +119 -678
- package/dist/icons/maintenance-icon/MaintenanceIcon.stories.tsx +123 -1184
- package/dist/icons/message-icon/MessageIcon.stories.tsx +114 -538
- package/dist/icons/minimize-icon/MinimizeIcon.stories.tsx +116 -1158
- package/dist/icons/moon-icon/MoonIcon.stories.tsx +120 -536
- package/dist/icons/move-horizontal-icon/MoveHorizontalIcon.stories.tsx +109 -1184
- package/dist/icons/move-vertical-icon/MoveVerticalIcon.stories.tsx +115 -1134
- package/dist/icons/musical-note-icon/MusicalNoteIcon.stories.tsx +119 -971
- package/dist/icons/notepad-icon/NotepadIcon.stories.tsx +111 -1100
- package/dist/icons/notes-icon/NotesIcon.stories.tsx +119 -1101
- package/dist/icons/page-search-icon/PageSearchIcon.stories.tsx +109 -1111
- package/dist/icons/page-text-icon/PageTextIcon.stories.tsx +122 -684
- package/dist/icons/paint-roll-icon/PaintRollIcon.stories.tsx +113 -954
- package/dist/icons/paper-plane-icon/PaperPlaneIcon.stories.tsx +112 -877
- package/dist/icons/pause-icon/PauseIcon.stories.tsx +113 -1000
- package/dist/icons/pencil-icon/PencilIcon.stories.tsx +115 -1070
- package/dist/icons/phone-icon/PhoneIcon.stories.tsx +115 -978
- package/dist/icons/plus-icon/PlusIcon.stories.tsx +106 -1093
- package/dist/icons/pocket-studio-icon/PocketStudioIcon.stories.tsx +107 -829
- package/dist/icons/scroll-down-icon/ScrollDownIcon.stories.tsx +102 -469
- package/dist/icons/search-icon/SearchIcon.stories.tsx +111 -1124
- package/dist/icons/setting-icon/SettingIcon.stories.tsx +107 -970
- package/dist/icons/share-icon/ShareIcon.stories.tsx +120 -1025
- package/dist/icons/shield-icon/ShieldIcon.stories.tsx +117 -931
- package/dist/icons/site-logo-icon/SiteLogoIcon.stories.tsx +137 -1104
- package/dist/icons/skip-backward-icon/SkipBackwardIcon.stories.tsx +172 -982
- package/dist/icons/skip-forward-icon/SkipForwardIcon.stories.tsx +164 -983
- package/dist/icons/sparkles-soft-icon/SparklesSoftIcon.stories.tsx +105 -958
- package/dist/icons/spinner-gradient-icon/SpinnerGradientIcon.stories.tsx +158 -580
- package/dist/icons/spinner-gradient-icon/index.tsx +6 -1
- package/dist/icons/spinner-solid-icon/SpinnerSolidIcon.stories.tsx +158 -587
- package/dist/icons/spinner-solid-icon/index.tsx +6 -1
- package/dist/icons/spinner-solid-neutral-icon/SpinnerSolidINeutralcon.stories.tsx +146 -682
- package/dist/icons/spinner-solid-neutral-icon/index.tsx +1 -1
- package/dist/icons/star-icon/StarIcon.stories.tsx +124 -904
- package/dist/icons/store-coin-icon/StoreCoinIcon.stories.tsx +112 -964
- package/dist/icons/suggestion-icon/SuggestionIcon.stories.tsx +116 -852
- package/dist/icons/sun-icon/SunIcon.stories.tsx +120 -831
- package/dist/icons/text-color-icon/TextColorIcon.stories.tsx +116 -950
- package/dist/icons/text-indicator-icon/TextIndicatorIcon.stories.tsx +123 -980
- package/dist/icons/threads-icon/ThreadsIcon.stories.tsx +156 -1427
- package/dist/icons/tick-circle-icon/TickCircleIcon.stories.tsx +146 -1142
- package/dist/icons/tick-icon/TickIcon.stories.tsx +145 -1276
- package/dist/icons/trash-icon/TrashIcon.stories.tsx +108 -933
- package/dist/icons/twitter-x-icon/TwitterXIcon.stories.tsx +157 -1402
- package/dist/icons/upload-icon/UploadIcon.stories.tsx +115 -889
- package/dist/icons/vertical-menu-icon/VerticalMenuIcon.stories.tsx +118 -984
- package/dist/icons/video-play-list-icon/VideoPlaylistIcon.stories.tsx +125 -1049
- package/dist/icons/voice-playing-icon/VoicePlayingIcon.stories.tsx +123 -1356
- package/dist/icons/volume-full-icon/VolumeFullIcon.stories.tsx +110 -1171
- package/dist/icons/volume-half-icon/VolumeHalfIcon.stories.tsx +112 -1093
- package/dist/icons/volume-off-icon/VolumeOffIcon.stories.tsx +115 -1087
- package/dist/icons/warning-icon/WarningIcon.stories.tsx +122 -1046
- package/dist/icons/youtube-icon/YoutubeIcon.stories.tsx +161 -936
- package/dist/index.cjs +84 -84
- package/dist/index.js +84 -84
- package/dist/styles/aural-all-theme.css +1222 -0
- package/dist/styles/{aural-theme.css → aural-dark-theme.css} +15 -3
- package/dist/styles/aural-light-theme.css +1047 -0
- package/package.json +1 -1
|
@@ -1,19 +1,38 @@
|
|
|
1
1
|
import React, { useState } from "react"
|
|
2
2
|
import type { Meta, StoryObj } from "@storybook/react-vite"
|
|
3
3
|
|
|
4
|
-
import
|
|
4
|
+
import { AuralComponentDocsPage } from "src/ui/story-spec/components/component-story-docs-page"
|
|
5
|
+
|
|
6
|
+
import CharCount, { type CharCountProps } from "."
|
|
5
7
|
|
|
6
8
|
const meta: Meta<typeof CharCount> = {
|
|
7
9
|
title: "Components/UI/CharCount",
|
|
8
10
|
component: CharCount,
|
|
9
11
|
parameters: {
|
|
10
12
|
layout: "centered",
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
docs: {
|
|
14
|
+
description: {
|
|
15
|
+
component:
|
|
16
|
+
"A compact character counter that displays current length against an optional maximum, transitioning through neutral, warning, and error states as the user types. Use it alongside any text input or textarea to give users at-a-glance feedback on their remaining characters.",
|
|
17
|
+
},
|
|
18
|
+
page: () => (
|
|
19
|
+
<AuralComponentDocsPage
|
|
20
|
+
features={[
|
|
21
|
+
{
|
|
22
|
+
title: "3 Color States",
|
|
23
|
+
description: "Neutral, warning, error",
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
title: "Live ARIA",
|
|
27
|
+
description: "Announces state changes",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
title: "maxLength Optional",
|
|
31
|
+
description: "Works without a cap",
|
|
32
|
+
},
|
|
33
|
+
]}
|
|
34
|
+
/>
|
|
35
|
+
),
|
|
17
36
|
},
|
|
18
37
|
},
|
|
19
38
|
tags: ["autodocs"],
|
|
@@ -26,10 +45,6 @@ const meta: Meta<typeof CharCount> = {
|
|
|
26
45
|
control: { type: "number", min: 0 },
|
|
27
46
|
description: "Maximum allowed characters (optional)",
|
|
28
47
|
},
|
|
29
|
-
className: {
|
|
30
|
-
control: { type: "text" },
|
|
31
|
-
description: "Additional CSS classes to apply",
|
|
32
|
-
},
|
|
33
48
|
"aria-live": {
|
|
34
49
|
control: { type: "select" },
|
|
35
50
|
options: ["polite", "assertive", "off"],
|
|
@@ -37,7 +52,7 @@ const meta: Meta<typeof CharCount> = {
|
|
|
37
52
|
},
|
|
38
53
|
id: {
|
|
39
54
|
control: { type: "text" },
|
|
40
|
-
description: "HTML id attribute for accessibility",
|
|
55
|
+
description: "HTML id attribute for accessibility linkage",
|
|
41
56
|
},
|
|
42
57
|
},
|
|
43
58
|
}
|
|
@@ -45,297 +60,384 @@ const meta: Meta<typeof CharCount> = {
|
|
|
45
60
|
export default meta
|
|
46
61
|
type Story = StoryObj<typeof meta>
|
|
47
62
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
63
|
+
function getCharCountColorClassName(
|
|
64
|
+
currentLength: number,
|
|
65
|
+
maxLength?: number
|
|
66
|
+
): string {
|
|
67
|
+
if (typeof maxLength !== "number") {
|
|
68
|
+
return "!text-fm-tertiary"
|
|
69
|
+
}
|
|
55
70
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
"aria-live": "polite",
|
|
60
|
-
},
|
|
61
|
-
}
|
|
71
|
+
if (currentLength > maxLength) {
|
|
72
|
+
return "!text-fm-negative"
|
|
73
|
+
}
|
|
62
74
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
maxLength: 100,
|
|
67
|
-
"aria-live": "polite",
|
|
68
|
-
},
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export const OverLimit: Story = {
|
|
72
|
-
args: {
|
|
73
|
-
currentLength: 105,
|
|
74
|
-
maxLength: 100,
|
|
75
|
-
"aria-live": "assertive",
|
|
76
|
-
},
|
|
77
|
-
}
|
|
75
|
+
if (currentLength > maxLength * 0.9) {
|
|
76
|
+
return "!text-fm-warning"
|
|
77
|
+
}
|
|
78
78
|
|
|
79
|
-
|
|
80
|
-
args: {
|
|
81
|
-
currentLength: 0,
|
|
82
|
-
maxLength: 50,
|
|
83
|
-
"aria-live": "polite",
|
|
84
|
-
},
|
|
79
|
+
return "!text-fm-tertiary"
|
|
85
80
|
}
|
|
86
81
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
82
|
+
function StoryCharCount({
|
|
83
|
+
className,
|
|
84
|
+
currentLength,
|
|
85
|
+
maxLength,
|
|
86
|
+
...props
|
|
87
|
+
}: CharCountProps) {
|
|
88
|
+
return (
|
|
89
|
+
<CharCount
|
|
90
|
+
currentLength={currentLength}
|
|
91
|
+
maxLength={maxLength}
|
|
92
|
+
className={[
|
|
93
|
+
getCharCountColorClassName(currentLength, maxLength),
|
|
94
|
+
className,
|
|
95
|
+
]
|
|
96
|
+
.filter(Boolean)
|
|
97
|
+
.join(" ")}
|
|
98
|
+
{...props}
|
|
99
|
+
/>
|
|
100
|
+
)
|
|
93
101
|
}
|
|
94
102
|
|
|
95
|
-
|
|
96
|
-
args: {
|
|
97
|
-
currentLength: 2847,
|
|
98
|
-
maxLength: 5000,
|
|
99
|
-
"aria-live": "polite",
|
|
100
|
-
},
|
|
101
|
-
}
|
|
103
|
+
// ─── 1. Playground ────────────────────────────────────────────────────────────
|
|
102
104
|
|
|
103
|
-
export const
|
|
105
|
+
export const Playground: Story = {
|
|
104
106
|
args: {
|
|
105
|
-
currentLength:
|
|
107
|
+
currentLength: 25,
|
|
106
108
|
maxLength: 100,
|
|
107
|
-
className: "!text-blue-600 !text-lg",
|
|
108
109
|
"aria-live": "polite",
|
|
109
110
|
},
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
render: () => (
|
|
115
|
-
<div className="w-96 space-y-6">
|
|
116
|
-
<div>
|
|
117
|
-
<h3 className="mb-2 text-sm font-medium">Normal State (25/100)</h3>
|
|
118
|
-
<CharCount currentLength={25} maxLength={100} />
|
|
111
|
+
render: (args) => (
|
|
112
|
+
<div className="w-full max-w-sm space-y-4">
|
|
113
|
+
<div className="flex justify-center">
|
|
114
|
+
<StoryCharCount {...args} />
|
|
119
115
|
</div>
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
</div>
|
|
125
|
-
|
|
126
|
-
<div>
|
|
127
|
-
<h3 className="mb-2 text-sm font-medium">Error State (105/100)</h3>
|
|
128
|
-
<CharCount currentLength={105} maxLength={100} />
|
|
116
|
+
<div className="border-fm-divider-secondary bg-fm-surface-secondary rounded-lg border px-4 py-3">
|
|
117
|
+
<code className="text-fm-secondary text-fm-md leading-fm-md font-(--font-fm-mono)">
|
|
118
|
+
{`<CharCount currentLength={${args.currentLength}}${args.maxLength !== undefined ? ` maxLength={${args.maxLength}}` : ""} />`}
|
|
119
|
+
</code>
|
|
129
120
|
</div>
|
|
121
|
+
</div>
|
|
122
|
+
),
|
|
123
|
+
parameters: {
|
|
124
|
+
docs: {
|
|
125
|
+
description: {
|
|
126
|
+
story:
|
|
127
|
+
"Adjust currentLength and maxLength in the sidebar controls to watch the counter change state in real time.",
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
}
|
|
130
132
|
|
|
131
|
-
|
|
132
|
-
<h3 className="mb-2 text-sm font-medium">Without Max Length (42)</h3>
|
|
133
|
-
<CharCount currentLength={42} />
|
|
134
|
-
</div>
|
|
133
|
+
// ─── 2. States ────────────────────────────────────────────────────────────────
|
|
135
134
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
135
|
+
export const States: Story = {
|
|
136
|
+
render: () => (
|
|
137
|
+
<div className="space-y-8">
|
|
138
|
+
<div className="space-y-4">
|
|
139
|
+
<h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md font-medium">
|
|
140
|
+
Count States
|
|
141
|
+
</h4>
|
|
142
|
+
<div className="flex flex-wrap items-center gap-8">
|
|
143
|
+
<div className="space-y-2 text-center">
|
|
144
|
+
<StoryCharCount currentLength={0} maxLength={100} />
|
|
145
|
+
<p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
|
|
146
|
+
Zero (0/100)
|
|
147
|
+
</p>
|
|
148
|
+
</div>
|
|
149
|
+
<div className="space-y-2 text-center">
|
|
150
|
+
<StoryCharCount currentLength={35} maxLength={100} />
|
|
151
|
+
<p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
|
|
152
|
+
Low (35/100)
|
|
153
|
+
</p>
|
|
154
|
+
</div>
|
|
155
|
+
<div className="space-y-2 text-center">
|
|
156
|
+
<StoryCharCount currentLength={92} maxLength={100} />
|
|
157
|
+
<p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
|
|
158
|
+
Near limit (92/100)
|
|
159
|
+
</p>
|
|
160
|
+
</div>
|
|
161
|
+
<div className="space-y-2 text-center">
|
|
162
|
+
<StoryCharCount
|
|
163
|
+
currentLength={105}
|
|
164
|
+
maxLength={100}
|
|
165
|
+
aria-live="assertive"
|
|
166
|
+
/>
|
|
167
|
+
<p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
|
|
168
|
+
Over limit (105/100)
|
|
169
|
+
</p>
|
|
170
|
+
</div>
|
|
171
|
+
<div className="space-y-2 text-center">
|
|
172
|
+
<StoryCharCount currentLength={42} />
|
|
173
|
+
<p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
|
|
174
|
+
No maxLength (42)
|
|
175
|
+
</p>
|
|
176
|
+
</div>
|
|
177
|
+
</div>
|
|
139
178
|
</div>
|
|
140
179
|
|
|
141
|
-
<div>
|
|
142
|
-
<
|
|
143
|
-
|
|
180
|
+
<div className="space-y-4">
|
|
181
|
+
<h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md font-medium">
|
|
182
|
+
Boundary Values
|
|
183
|
+
</h4>
|
|
184
|
+
<div className="flex flex-wrap items-center gap-8">
|
|
185
|
+
<div className="space-y-2 text-center">
|
|
186
|
+
<StoryCharCount currentLength={89} maxLength={100} />
|
|
187
|
+
<p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
|
|
188
|
+
Just below warning (89/100)
|
|
189
|
+
</p>
|
|
190
|
+
</div>
|
|
191
|
+
<div className="space-y-2 text-center">
|
|
192
|
+
<StoryCharCount currentLength={91} maxLength={100} />
|
|
193
|
+
<p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
|
|
194
|
+
Warning state (91/100)
|
|
195
|
+
</p>
|
|
196
|
+
</div>
|
|
197
|
+
<div className="space-y-2 text-center">
|
|
198
|
+
<StoryCharCount currentLength={100} maxLength={100} />
|
|
199
|
+
<p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
|
|
200
|
+
Exact limit (100/100)
|
|
201
|
+
</p>
|
|
202
|
+
</div>
|
|
203
|
+
<div className="space-y-2 text-center">
|
|
204
|
+
<StoryCharCount
|
|
205
|
+
currentLength={101}
|
|
206
|
+
maxLength={100}
|
|
207
|
+
aria-live="assertive"
|
|
208
|
+
/>
|
|
209
|
+
<p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
|
|
210
|
+
One over (101/100)
|
|
211
|
+
</p>
|
|
212
|
+
</div>
|
|
213
|
+
</div>
|
|
144
214
|
</div>
|
|
145
215
|
</div>
|
|
146
216
|
),
|
|
217
|
+
parameters: {
|
|
218
|
+
docs: {
|
|
219
|
+
description: {
|
|
220
|
+
story:
|
|
221
|
+
"All five states from left to right: zero, low, near-limit (≥90%), over-limit, and no-maxLength. Boundary values below show the exact thresholds where color transitions occur.",
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
},
|
|
147
225
|
}
|
|
148
226
|
|
|
149
|
-
//
|
|
150
|
-
|
|
227
|
+
// ─── 3. Use Cases ─────────────────────────────────────────────────────────────
|
|
228
|
+
|
|
229
|
+
export const UseCases: Story = {
|
|
151
230
|
render: () => {
|
|
152
|
-
const
|
|
153
|
-
|
|
231
|
+
const BioField = () => {
|
|
232
|
+
const [bio, setBio] = useState("")
|
|
233
|
+
const maxLength = 160
|
|
234
|
+
const isOver = bio.length > maxLength
|
|
154
235
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
236
|
+
return (
|
|
237
|
+
<div className="space-y-2">
|
|
238
|
+
<div className="flex items-baseline justify-between">
|
|
239
|
+
<label
|
|
240
|
+
htmlFor="bio-field"
|
|
241
|
+
className="text-fm-primary font-fm-text text-fm-sm leading-fm-sm font-medium"
|
|
242
|
+
>
|
|
243
|
+
Bio
|
|
244
|
+
</label>
|
|
245
|
+
<StoryCharCount
|
|
246
|
+
currentLength={bio.length}
|
|
247
|
+
maxLength={maxLength}
|
|
248
|
+
id="bio-count"
|
|
249
|
+
aria-live={isOver ? "assertive" : "polite"}
|
|
250
|
+
/>
|
|
251
|
+
</div>
|
|
161
252
|
<textarea
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
253
|
+
id="bio-field"
|
|
254
|
+
value={bio}
|
|
255
|
+
onChange={(e) => setBio(e.target.value)}
|
|
256
|
+
aria-describedby="bio-count"
|
|
257
|
+
className="border-fm-divider-secondary bg-fm-surface-secondary text-fm-primary placeholder:text-fm-placeholder font-fm-text text-fm-md leading-fm-xl w-full resize-none rounded-lg border px-3 py-2 focus:outline-none"
|
|
258
|
+
placeholder="Tell people about yourself…"
|
|
166
259
|
rows={4}
|
|
167
|
-
aria-describedby="tweet-count"
|
|
168
260
|
/>
|
|
169
|
-
|
|
170
|
-
<
|
|
171
|
-
|
|
261
|
+
{isOver && (
|
|
262
|
+
<p className="text-fm-negative font-fm-text text-fm-sm leading-fm-sm">
|
|
263
|
+
Bio is too long. Please shorten it.
|
|
264
|
+
</p>
|
|
265
|
+
)}
|
|
266
|
+
</div>
|
|
267
|
+
)
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const UsernameField = () => {
|
|
271
|
+
const [username, setUsername] = useState("")
|
|
272
|
+
const maxLength = 20
|
|
273
|
+
|
|
274
|
+
return (
|
|
275
|
+
<div className="space-y-2">
|
|
276
|
+
<div className="flex items-baseline justify-between">
|
|
277
|
+
<label
|
|
278
|
+
htmlFor="username-field"
|
|
279
|
+
className="text-fm-primary font-fm-text text-fm-sm leading-fm-sm font-medium"
|
|
280
|
+
>
|
|
281
|
+
Username
|
|
282
|
+
</label>
|
|
283
|
+
<StoryCharCount
|
|
284
|
+
currentLength={username.length}
|
|
172
285
|
maxLength={maxLength}
|
|
173
|
-
id="
|
|
286
|
+
id="username-count"
|
|
174
287
|
aria-live="polite"
|
|
175
288
|
/>
|
|
176
289
|
</div>
|
|
177
|
-
</div>
|
|
178
|
-
</div>
|
|
179
|
-
)
|
|
180
|
-
},
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// Interactive example with input
|
|
184
|
-
export const WithInput: Story = {
|
|
185
|
-
render: () => {
|
|
186
|
-
const [username, setUsername] = useState("")
|
|
187
|
-
const maxLength = 20
|
|
188
|
-
|
|
189
|
-
return (
|
|
190
|
-
<div className="w-96 space-y-4">
|
|
191
|
-
<div>
|
|
192
|
-
<label className="mb-2 block text-sm font-medium">Username</label>
|
|
193
290
|
<input
|
|
291
|
+
id="username-field"
|
|
194
292
|
type="text"
|
|
195
293
|
value={username}
|
|
196
294
|
onChange={(e) => setUsername(e.target.value)}
|
|
197
|
-
className="w-full rounded-md border border-gray-300 px-3 py-2 focus:ring-2 focus:ring-blue-500 focus:outline-none"
|
|
198
|
-
placeholder="Enter username"
|
|
199
295
|
aria-describedby="username-count"
|
|
296
|
+
className="border-fm-divider-secondary bg-fm-surface-secondary text-fm-primary placeholder:text-fm-placeholder font-fm-text text-fm-md leading-fm-md w-full rounded-lg border px-3 py-2 focus:outline-none"
|
|
297
|
+
placeholder="Choose a unique handle"
|
|
200
298
|
/>
|
|
201
|
-
<
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
</span>
|
|
205
|
-
<CharCount
|
|
206
|
-
currentLength={username.length}
|
|
207
|
-
maxLength={maxLength}
|
|
208
|
-
id="username-count"
|
|
209
|
-
aria-live="polite"
|
|
210
|
-
/>
|
|
211
|
-
</div>
|
|
299
|
+
<p className="text-fm-tertiary font-fm-text text-fm-sm leading-fm-sm">
|
|
300
|
+
Must be unique and memorable
|
|
301
|
+
</p>
|
|
212
302
|
</div>
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
},
|
|
216
|
-
}
|
|
303
|
+
)
|
|
304
|
+
}
|
|
217
305
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
const [description, setDescription] = useState("")
|
|
223
|
-
const [tags, setTags] = useState("")
|
|
306
|
+
const MultipleFields = () => {
|
|
307
|
+
const [title, setTitle] = useState("")
|
|
308
|
+
const [description, setDescription] = useState("")
|
|
309
|
+
const [tags, setTags] = useState("")
|
|
224
310
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
<
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
311
|
+
return (
|
|
312
|
+
<div className="space-y-5">
|
|
313
|
+
{/* Title */}
|
|
314
|
+
<div className="space-y-2">
|
|
315
|
+
<div className="flex items-baseline justify-between">
|
|
316
|
+
<label
|
|
317
|
+
htmlFor="title-field"
|
|
318
|
+
className="text-fm-primary font-fm-text text-fm-sm leading-fm-sm font-medium"
|
|
319
|
+
>
|
|
320
|
+
Post Title
|
|
321
|
+
</label>
|
|
322
|
+
<StoryCharCount
|
|
323
|
+
currentLength={title.length}
|
|
324
|
+
maxLength={60}
|
|
325
|
+
id="title-count"
|
|
326
|
+
aria-live="polite"
|
|
327
|
+
/>
|
|
328
|
+
</div>
|
|
329
|
+
<input
|
|
330
|
+
id="title-field"
|
|
331
|
+
type="text"
|
|
332
|
+
value={title}
|
|
333
|
+
onChange={(e) => setTitle(e.target.value)}
|
|
334
|
+
aria-describedby="title-count"
|
|
335
|
+
className="border-fm-divider-secondary bg-fm-surface-secondary text-fm-primary placeholder:text-fm-placeholder font-fm-text text-fm-md leading-fm-md w-full rounded-lg border px-3 py-2 focus:outline-none"
|
|
336
|
+
placeholder="Enter post title"
|
|
242
337
|
/>
|
|
243
338
|
</div>
|
|
244
|
-
</div>
|
|
245
339
|
|
|
246
|
-
|
|
247
|
-
<
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
340
|
+
{/* Description */}
|
|
341
|
+
<div className="space-y-2">
|
|
342
|
+
<div className="flex items-baseline justify-between">
|
|
343
|
+
<label
|
|
344
|
+
htmlFor="description-field"
|
|
345
|
+
className="text-fm-primary font-fm-text text-fm-sm leading-fm-sm font-medium"
|
|
346
|
+
>
|
|
347
|
+
Description
|
|
348
|
+
</label>
|
|
349
|
+
<StoryCharCount
|
|
350
|
+
currentLength={description.length}
|
|
351
|
+
maxLength={200}
|
|
352
|
+
id="description-count"
|
|
353
|
+
aria-live="polite"
|
|
354
|
+
/>
|
|
355
|
+
</div>
|
|
356
|
+
<textarea
|
|
357
|
+
id="description-field"
|
|
358
|
+
value={description}
|
|
359
|
+
onChange={(e) => setDescription(e.target.value)}
|
|
360
|
+
aria-describedby="description-count"
|
|
361
|
+
className="border-fm-divider-secondary bg-fm-surface-secondary text-fm-primary placeholder:text-fm-placeholder font-fm-text text-fm-md leading-fm-xl w-full resize-none rounded-lg border px-3 py-2 focus:outline-none"
|
|
362
|
+
placeholder="Describe your post"
|
|
363
|
+
rows={3}
|
|
261
364
|
/>
|
|
262
365
|
</div>
|
|
263
|
-
</div>
|
|
264
366
|
|
|
265
|
-
|
|
266
|
-
<
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
</
|
|
281
|
-
<
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
367
|
+
{/* Tags */}
|
|
368
|
+
<div className="space-y-2">
|
|
369
|
+
<div className="flex items-baseline justify-between">
|
|
370
|
+
<label
|
|
371
|
+
htmlFor="tags-field"
|
|
372
|
+
className="text-fm-primary font-fm-text text-fm-sm leading-fm-sm font-medium"
|
|
373
|
+
>
|
|
374
|
+
Tags
|
|
375
|
+
</label>
|
|
376
|
+
<StoryCharCount
|
|
377
|
+
currentLength={tags.length}
|
|
378
|
+
maxLength={50}
|
|
379
|
+
id="tags-count"
|
|
380
|
+
aria-live="polite"
|
|
381
|
+
/>
|
|
382
|
+
</div>
|
|
383
|
+
<input
|
|
384
|
+
id="tags-field"
|
|
385
|
+
type="text"
|
|
386
|
+
value={tags}
|
|
387
|
+
onChange={(e) => setTags(e.target.value)}
|
|
388
|
+
aria-describedby="tags-count"
|
|
389
|
+
className="border-fm-divider-secondary bg-fm-surface-secondary text-fm-primary placeholder:text-fm-placeholder font-fm-text text-fm-md leading-fm-md w-full rounded-lg border px-3 py-2 focus:outline-none"
|
|
390
|
+
placeholder="react, typescript, ui"
|
|
285
391
|
/>
|
|
392
|
+
<p className="text-fm-tertiary font-fm-text text-fm-sm leading-fm-sm">
|
|
393
|
+
Separate tags with commas
|
|
394
|
+
</p>
|
|
286
395
|
</div>
|
|
287
396
|
</div>
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
},
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// Accessibility demonstration
|
|
294
|
-
export const AccessibilityExample: Story = {
|
|
295
|
-
render: () => {
|
|
296
|
-
const [message, setMessage] = useState("")
|
|
297
|
-
const maxLength = 100
|
|
298
|
-
const isOverLimit = message.length > maxLength
|
|
397
|
+
)
|
|
398
|
+
}
|
|
299
399
|
|
|
300
400
|
return (
|
|
301
|
-
<div className="w-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
>
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
<
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
</div>
|
|
330
|
-
<CharCount
|
|
331
|
-
currentLength={message.length}
|
|
332
|
-
maxLength={maxLength}
|
|
333
|
-
id="message-count"
|
|
334
|
-
aria-live={isOverLimit ? "assertive" : "polite"}
|
|
335
|
-
/>
|
|
401
|
+
<div className="mx-auto max-w-3xl space-y-8 p-8">
|
|
402
|
+
{/* Bio + Textarea */}
|
|
403
|
+
<div className="space-y-4">
|
|
404
|
+
<h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md font-medium">
|
|
405
|
+
Paired with Textarea — Bio Field
|
|
406
|
+
</h4>
|
|
407
|
+
<div className="border-fm-divider-secondary bg-fm-surface-secondary mx-auto max-w-md rounded-xl border p-5">
|
|
408
|
+
<BioField />
|
|
409
|
+
</div>
|
|
410
|
+
</div>
|
|
411
|
+
|
|
412
|
+
{/* Username + Input */}
|
|
413
|
+
<div className="space-y-4">
|
|
414
|
+
<h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md font-medium">
|
|
415
|
+
Paired with Input — Username Field
|
|
416
|
+
</h4>
|
|
417
|
+
<div className="border-fm-divider-secondary bg-fm-surface-secondary mx-auto max-w-md rounded-xl border p-5">
|
|
418
|
+
<UsernameField />
|
|
419
|
+
</div>
|
|
420
|
+
</div>
|
|
421
|
+
|
|
422
|
+
{/* Multiple fields */}
|
|
423
|
+
<div className="space-y-4">
|
|
424
|
+
<h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md font-medium">
|
|
425
|
+
Multiple Fields with Independent Counts
|
|
426
|
+
</h4>
|
|
427
|
+
<div className="border-fm-divider-secondary bg-fm-surface-secondary mx-auto max-w-md rounded-xl border p-5">
|
|
428
|
+
<MultipleFields />
|
|
336
429
|
</div>
|
|
337
430
|
</div>
|
|
338
431
|
</div>
|
|
339
432
|
)
|
|
340
433
|
},
|
|
434
|
+
parameters: {
|
|
435
|
+
layout: "fullscreen",
|
|
436
|
+
docs: {
|
|
437
|
+
description: {
|
|
438
|
+
story:
|
|
439
|
+
"Real-world usage scenarios: CharCount paired with a textarea bio field (with overflow error message), an input username field, and a multi-field form where each counter tracks its own input independently.",
|
|
440
|
+
},
|
|
441
|
+
},
|
|
442
|
+
},
|
|
341
443
|
}
|