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.
Files changed (183) hide show
  1. package/dist/components/aspect-ratio/AspectRatio.stories.tsx +290 -1199
  2. package/dist/components/avatar/Avatar.stories.tsx +235 -237
  3. package/dist/components/badge/Badge.stories.tsx +379 -116
  4. package/dist/components/banner/Banner.stories.tsx +445 -391
  5. package/dist/components/breadcrumb/Breadcrumb.stories.tsx +453 -199
  6. package/dist/components/button/Button.stories.tsx +585 -230
  7. package/dist/components/button/index.tsx +7 -7
  8. package/dist/components/card/Card.stories.tsx +619 -301
  9. package/dist/components/char-count/CharCount.stories.tsx +350 -248
  10. package/dist/components/checkbox/Checkbox.stories.tsx +309 -167
  11. package/dist/components/chip/Chip.stories.tsx +362 -168
  12. package/dist/components/circular-loader/CircularLoader.stories.tsx +221 -620
  13. package/dist/components/clamp-lines/ClampLines.stories.tsx +246 -117
  14. package/dist/components/collapsible/Collapsible.stories.tsx +391 -252
  15. package/dist/components/command/Command.stories.tsx +533 -856
  16. package/dist/components/dialog/Dialog.stories.tsx +505 -949
  17. package/dist/components/divider/Divider.stories.tsx +265 -502
  18. package/dist/components/dot-loader/DotLoader.stories.tsx +256 -257
  19. package/dist/components/drawer/Drawer.stories.tsx +659 -993
  20. package/dist/components/drawer/index.tsx +3 -3
  21. package/dist/components/dropdown/Dropdown.stories.tsx +643 -1018
  22. package/dist/components/form/Form.stories.tsx +560 -274
  23. package/dist/components/helper-text/HelperText.stories.tsx +199 -200
  24. package/dist/components/hover-card/HoverCard.stories.tsx +318 -1221
  25. package/dist/components/icon-button/IconButton.stories.tsx +837 -194
  26. package/dist/components/if-else/if-else.stories.tsx +370 -83
  27. package/dist/components/input/Input.stories.tsx +436 -368
  28. package/dist/components/label/Label.stories.tsx +156 -154
  29. package/dist/components/list/List.stories.tsx +485 -822
  30. package/dist/components/marquee/Marquee.stories.tsx +356 -694
  31. package/dist/components/otp-inputs/OtpInputs.stories.tsx +352 -410
  32. package/dist/components/overlay/Overlay.stories.tsx +452 -818
  33. package/dist/components/overlay/index.tsx +4 -4
  34. package/dist/components/pagination/Pagination.stories.tsx +721 -210
  35. package/dist/components/popover/Popover.stories.tsx +484 -873
  36. package/dist/components/radio/Radio.stories.tsx +432 -124
  37. package/dist/components/resizable/Resizable.stories.tsx +496 -752
  38. package/dist/components/scroll-area/ScrollArea.stories.tsx +384 -1006
  39. package/dist/components/search/Search.stories.tsx +314 -575
  40. package/dist/components/select/Select.stories.tsx +684 -787
  41. package/dist/components/sheet/Sheet.stories.tsx +671 -936
  42. package/dist/components/skelton/Skelton.stories.tsx +230 -764
  43. package/dist/components/slider/Slider.stories.tsx +384 -737
  44. package/dist/components/stepper/Stepper.stories.tsx +371 -514
  45. package/dist/components/switch/Switch.stories.tsx +461 -208
  46. package/dist/components/switch-case/SwitchCase.stories.tsx +367 -188
  47. package/dist/components/table/Table.stories.tsx +770 -914
  48. package/dist/components/tabs/Tabs.stories.tsx +459 -1400
  49. package/dist/components/tag/Tag.stories.tsx +714 -542
  50. package/dist/components/textarea/TextArea.stories.tsx +621 -562
  51. package/dist/components/thumbnail-tags/ThumbnailTags.stories.tsx +228 -148
  52. package/dist/components/toast/Toast.stories.tsx +452 -1333
  53. package/dist/components/toggle/Toggle.stories.tsx +488 -909
  54. package/dist/components/tooltip/Tooltip.stories.tsx +344 -1372
  55. package/dist/components/typography/Typography.stories.tsx +406 -89
  56. package/dist/hooks/use-change-state/UseChangeState.stories.tsx +309 -606
  57. package/dist/hooks/use-previous/UsePrevious.stories.tsx +367 -917
  58. package/dist/hooks/use-standalone-pagination/UseStandalonePagination.stories.tsx +639 -867
  59. package/dist/icons/Icons.stories.tsx +0 -12
  60. package/dist/icons/ai-avatar-icon/AiAvatarIcon.stories.tsx +226 -1013
  61. package/dist/icons/alert-icon/AlertIcon.stories.tsx +109 -929
  62. package/dist/icons/all-icons.tsx +124 -87
  63. package/dist/icons/angle-down-icon/AngleDownIcon.stories.tsx +140 -971
  64. package/dist/icons/apple-logo-icon/AppleLogoIcon.stories.tsx +148 -888
  65. package/dist/icons/arrow-box-left-icon/ArrowBoxLeftIcon.stories.tsx +135 -1019
  66. package/dist/icons/arrow-corner-up-left-icon/ArrowCornerUpLeftIcon.stories.tsx +137 -953
  67. package/dist/icons/arrow-corner-up-right-icon/ArrowCornerUpRightIcon.stories.tsx +138 -997
  68. package/dist/icons/arrow-left-icon/ArrowLeftIcon.stories.tsx +136 -942
  69. package/dist/icons/arrow-right-icon/ArrowRightIcon.stories.tsx +148 -1092
  70. package/dist/icons/arrow-right-up-icon/ArrowRightUpIcon.stories.tsx +146 -1211
  71. package/dist/icons/art-board-icon/ArtBoardIcon.stories.tsx +126 -615
  72. package/dist/icons/audio-bar-icon/AudioBarIcon.stories.tsx +144 -1164
  73. package/dist/icons/backward-ten-seconds-icon/BackwardTenSecondsIcon.stories.tsx +167 -985
  74. package/dist/icons/bubble-check-icon/BubbleCheckIcon.stories.tsx +122 -1179
  75. package/dist/icons/bubble-crossed-icon/BubbleCrossedIcon.stories.tsx +124 -1168
  76. package/dist/icons/bubble-sparkle-icon/BubbleSparkleIcon.stories.tsx +119 -850
  77. package/dist/icons/camera-icon/CameraIcon.stories.tsx +112 -1213
  78. package/dist/icons/capital-a-letter-icon/CapitalALetterIcon.stories.tsx +117 -934
  79. package/dist/icons/chevron-double-left-icon/ChevronDoubleLeftIcon.stories.tsx +160 -961
  80. package/dist/icons/chevron-double-right-icon/ChevronDoubleRightIcon.stories.tsx +163 -961
  81. package/dist/icons/chevron-down-icon/ChevronDownIcon.stories.tsx +144 -942
  82. package/dist/icons/chevron-left-icon/ChevronLeftIcon.stories.tsx +129 -966
  83. package/dist/icons/chevron-right-icon/ChevronRightIcon.stories.tsx +147 -964
  84. package/dist/icons/chevron-up-icon/ChevronUpIcon.stories.tsx +145 -975
  85. package/dist/icons/circle-tick-icon/CircleTickIcon.stories.tsx +150 -1142
  86. package/dist/icons/circular-play-icon/CircularPlayIcon.stories.tsx +114 -461
  87. package/dist/icons/coin-icon/CoinIcon.stories.tsx +124 -1322
  88. package/dist/icons/coin-toons-icon/CoinToonsIcon.stories.tsx +117 -1318
  89. package/dist/icons/column-wide-add-icon/ColumnWideAddIcon.stories.tsx +114 -903
  90. package/dist/icons/command-icon/CommandIcon.stories.tsx +127 -1042
  91. package/dist/icons/copy-icon/CopyIcon.stories.tsx +123 -962
  92. package/dist/icons/cross-circle-icon/CrossCircleIcon.stories.tsx +147 -999
  93. package/dist/icons/cross-icon/CrossIcon.stories.tsx +139 -960
  94. package/dist/icons/download-icon/DownloadIcon.stories.tsx +126 -820
  95. package/dist/icons/edit-big-icon/EditBigIcon.stories.tsx +124 -1031
  96. package/dist/icons/email-icon/EmailIcon.stories.tsx +115 -936
  97. package/dist/icons/expand-icon/ExpandIcon.stories.tsx +112 -1111
  98. package/dist/icons/eye-close-icon/EyeCloseIcon.stories.tsx +144 -1025
  99. package/dist/icons/eye-open-icon/EyeOpenIcon.stories.tsx +143 -1036
  100. package/dist/icons/feature-shine-icon/FeatureShineIcon.stories.tsx +127 -1011
  101. package/dist/icons/file-chart-icon/FileChartIcon.stories.tsx +126 -1056
  102. package/dist/icons/file-text-icon/FileTextIcon.stories.tsx +125 -614
  103. package/dist/icons/filter-bar-row-icon/FilterBarRowIcon.stories.tsx +119 -1050
  104. package/dist/icons/forward-ten-seconds-icon/ForwardTenSecondsIcon.stories.tsx +169 -989
  105. package/dist/icons/git-branch-icon/GitBranchIcon.stories.tsx +115 -1145
  106. package/dist/icons/git-fork-icon/GitForkIcon.stories.tsx +115 -1122
  107. package/dist/icons/globe-icon/GlobeIcon.stories.tsx +130 -313
  108. package/dist/icons/google-logo-icon/GoogleLogoIcon.stories.tsx +145 -940
  109. package/dist/icons/grip-vertical-icon/GripVerticalIcon.stories.tsx +119 -1174
  110. package/dist/icons/head-icon/HeadIcon.stories.tsx +111 -916
  111. package/dist/icons/heart-icon/HeartIcon.stories.tsx +120 -1019
  112. package/dist/icons/image-avatar-sparkle-icon/ImageAvatarSparkleIcon.stories.tsx +119 -683
  113. package/dist/icons/image-icon/ImageIcon.stories.tsx +105 -1121
  114. package/dist/icons/import-folder-icon/ImportFolderIcon.stories.tsx +111 -1192
  115. package/dist/icons/import-left-arrow-folder-icon/ImportLeftArrowFolderIcon.stories.tsx +136 -1256
  116. package/dist/icons/indian-flag-icon/IndianFlagIcon.stories.tsx +159 -962
  117. package/dist/icons/instagram-icon/InstagramIcon.stories.tsx +161 -1385
  118. package/dist/icons/layout-column-icon/LayoutColumnIcon.stories.tsx +124 -972
  119. package/dist/icons/layout-left-icon/LayoutLeftIcon.stories.tsx +119 -948
  120. package/dist/icons/layout-right-icon/LayoutRightIcon.stories.tsx +119 -942
  121. package/dist/icons/light-bulb-simple-icon/LightBulbSimpleIcon.stories.tsx +108 -1215
  122. package/dist/icons/linked-in-icon/LinkedInIcon.stories.tsx +154 -1517
  123. package/dist/icons/magic-book-icon/MagicBookIcon.stories.tsx +110 -1188
  124. package/dist/icons/magic-edit-icon/MagicEditIcon.stories.tsx +119 -678
  125. package/dist/icons/maintenance-icon/MaintenanceIcon.stories.tsx +123 -1184
  126. package/dist/icons/message-icon/MessageIcon.stories.tsx +114 -538
  127. package/dist/icons/minimize-icon/MinimizeIcon.stories.tsx +116 -1158
  128. package/dist/icons/moon-icon/MoonIcon.stories.tsx +120 -536
  129. package/dist/icons/move-horizontal-icon/MoveHorizontalIcon.stories.tsx +109 -1184
  130. package/dist/icons/move-vertical-icon/MoveVerticalIcon.stories.tsx +115 -1134
  131. package/dist/icons/musical-note-icon/MusicalNoteIcon.stories.tsx +119 -971
  132. package/dist/icons/notepad-icon/NotepadIcon.stories.tsx +111 -1100
  133. package/dist/icons/notes-icon/NotesIcon.stories.tsx +119 -1101
  134. package/dist/icons/page-search-icon/PageSearchIcon.stories.tsx +109 -1111
  135. package/dist/icons/page-text-icon/PageTextIcon.stories.tsx +122 -684
  136. package/dist/icons/paint-roll-icon/PaintRollIcon.stories.tsx +113 -954
  137. package/dist/icons/paper-plane-icon/PaperPlaneIcon.stories.tsx +112 -877
  138. package/dist/icons/pause-icon/PauseIcon.stories.tsx +113 -1000
  139. package/dist/icons/pencil-icon/PencilIcon.stories.tsx +115 -1070
  140. package/dist/icons/phone-icon/PhoneIcon.stories.tsx +115 -978
  141. package/dist/icons/plus-icon/PlusIcon.stories.tsx +106 -1093
  142. package/dist/icons/pocket-studio-icon/PocketStudioIcon.stories.tsx +107 -829
  143. package/dist/icons/scroll-down-icon/ScrollDownIcon.stories.tsx +102 -469
  144. package/dist/icons/search-icon/SearchIcon.stories.tsx +111 -1124
  145. package/dist/icons/setting-icon/SettingIcon.stories.tsx +107 -970
  146. package/dist/icons/share-icon/ShareIcon.stories.tsx +120 -1025
  147. package/dist/icons/shield-icon/ShieldIcon.stories.tsx +117 -931
  148. package/dist/icons/site-logo-icon/SiteLogoIcon.stories.tsx +137 -1104
  149. package/dist/icons/skip-backward-icon/SkipBackwardIcon.stories.tsx +172 -982
  150. package/dist/icons/skip-forward-icon/SkipForwardIcon.stories.tsx +164 -983
  151. package/dist/icons/sparkles-soft-icon/SparklesSoftIcon.stories.tsx +105 -958
  152. package/dist/icons/spinner-gradient-icon/SpinnerGradientIcon.stories.tsx +158 -580
  153. package/dist/icons/spinner-gradient-icon/index.tsx +6 -1
  154. package/dist/icons/spinner-solid-icon/SpinnerSolidIcon.stories.tsx +158 -587
  155. package/dist/icons/spinner-solid-icon/index.tsx +6 -1
  156. package/dist/icons/spinner-solid-neutral-icon/SpinnerSolidINeutralcon.stories.tsx +146 -682
  157. package/dist/icons/spinner-solid-neutral-icon/index.tsx +1 -1
  158. package/dist/icons/star-icon/StarIcon.stories.tsx +124 -904
  159. package/dist/icons/store-coin-icon/StoreCoinIcon.stories.tsx +112 -964
  160. package/dist/icons/suggestion-icon/SuggestionIcon.stories.tsx +116 -852
  161. package/dist/icons/sun-icon/SunIcon.stories.tsx +120 -831
  162. package/dist/icons/text-color-icon/TextColorIcon.stories.tsx +116 -950
  163. package/dist/icons/text-indicator-icon/TextIndicatorIcon.stories.tsx +123 -980
  164. package/dist/icons/threads-icon/ThreadsIcon.stories.tsx +156 -1427
  165. package/dist/icons/tick-circle-icon/TickCircleIcon.stories.tsx +146 -1142
  166. package/dist/icons/tick-icon/TickIcon.stories.tsx +145 -1276
  167. package/dist/icons/trash-icon/TrashIcon.stories.tsx +108 -933
  168. package/dist/icons/twitter-x-icon/TwitterXIcon.stories.tsx +157 -1402
  169. package/dist/icons/upload-icon/UploadIcon.stories.tsx +115 -889
  170. package/dist/icons/vertical-menu-icon/VerticalMenuIcon.stories.tsx +118 -984
  171. package/dist/icons/video-play-list-icon/VideoPlaylistIcon.stories.tsx +125 -1049
  172. package/dist/icons/voice-playing-icon/VoicePlayingIcon.stories.tsx +123 -1356
  173. package/dist/icons/volume-full-icon/VolumeFullIcon.stories.tsx +110 -1171
  174. package/dist/icons/volume-half-icon/VolumeHalfIcon.stories.tsx +112 -1093
  175. package/dist/icons/volume-off-icon/VolumeOffIcon.stories.tsx +115 -1087
  176. package/dist/icons/warning-icon/WarningIcon.stories.tsx +122 -1046
  177. package/dist/icons/youtube-icon/YoutubeIcon.stories.tsx +161 -936
  178. package/dist/index.cjs +84 -84
  179. package/dist/index.js +84 -84
  180. package/dist/styles/aural-all-theme.css +1222 -0
  181. package/dist/styles/{aural-theme.css → aural-dark-theme.css} +15 -3
  182. package/dist/styles/aural-light-theme.css +1047 -0
  183. package/package.json +1 -1
@@ -1,585 +1,294 @@
1
1
  import React from "react"
2
2
  import type { Meta, StoryObj } from "@storybook/react-vite"
3
3
 
4
- import { LightBulbSimpleIcon } from "src/ui/icons"
4
+ import {
5
+ ChevronDownIcon,
6
+ EyeOpenIcon,
7
+ FeatureShineIcon,
8
+ LayoutColumnIcon,
9
+ LightBulbSimpleIcon,
10
+ NotepadIcon,
11
+ SettingIcon,
12
+ } from "src/ui/icons"
13
+ import {
14
+ HookCodeBlock,
15
+ HookControlButton,
16
+ HookPanel,
17
+ HookPlaygroundCanvas,
18
+ HookStateCard,
19
+ HookStateGrid,
20
+ HookUsageCanvas,
21
+ HookUsageSection,
22
+ } from "src/ui/story-spec/hooks/hook-story-canvas"
23
+ import { AuralHookDocsPage } from "src/ui/story-spec/hooks/hook-story-docs-page"
5
24
 
6
25
  import { useChangeState } from "."
7
26
 
8
- const ChangeStateDemo: React.FC<{
9
- title?: string
10
- }> = ({ title = "useChangeState Demo" }) => {
27
+ // ─────────────────────────────────────────────────────────────────────────────
28
+ // Demo components
29
+ // ─────────────────────────────────────────────────────────────────────────────
30
+
31
+ const ChangeStatePlaygroundDemo: React.FC<{ label?: string }> = ({
32
+ label = "State Label",
33
+ }) => {
11
34
  const { open, onOpenChange } = useChangeState()
12
35
 
13
36
  return (
14
- <div className="!space-y-6 rounded-lg border border-white/10 bg-white/5 !p-6">
15
- <h3 className="!text-xl font-semibold !text-white">{title}</h3>
16
-
17
- {/* State Display */}
18
- <div className="rounded-lg border border-blue-500/20 bg-blue-500/10 !p-4">
19
- <h4 className="!mb-3 !text-lg font-semibold !text-blue-300">
20
- Current State
21
- </h4>
22
- <div className="flex items-center justify-between rounded-lg bg-black/20 !p-3">
23
- <span className="!text-sm !text-white/70">Open:</span>
24
- <span
25
- className={`font-mono !text-lg font-bold ${
26
- open ? "!text-green-300" : "!text-red-300"
27
- }`}
28
- >
29
- {String(open)}
30
- </span>
31
- </div>
37
+ <HookPlaygroundCanvas>
38
+ <div className="mx-auto w-full max-w-sm space-y-6 p-8">
39
+ <HookStateGrid>
40
+ <HookStateCard label="open" value={String(open)} />
41
+ <HookStateCard label={label} value={open ? "Active" : "Inactive"} />
42
+ </HookStateGrid>
43
+ <HookPanel title="Controls">
44
+ <div className="flex flex-wrap gap-3">
45
+ <HookControlButton onClick={() => onOpenChange()}>
46
+ Toggle
47
+ </HookControlButton>
48
+ <HookControlButton
49
+ onClick={() => onOpenChange(true)}
50
+ variant="positive"
51
+ >
52
+ Set True
53
+ </HookControlButton>
54
+ <HookControlButton
55
+ onClick={() => onOpenChange(false)}
56
+ variant="negative"
57
+ >
58
+ Set False
59
+ </HookControlButton>
60
+ </div>
61
+ </HookPanel>
32
62
  </div>
63
+ </HookPlaygroundCanvas>
64
+ )
65
+ }
33
66
 
34
- {/* Controls */}
35
- <div className="!space-y-3">
36
- <button
37
- onClick={() => onOpenChange()}
38
- className={`w-full rounded-lg border !px-4 !py-3 !text-sm font-medium transition-colors ${
39
- open
40
- ? "border-green-500/30 bg-green-500/20 !text-green-300 hover:bg-green-500/30"
41
- : "border-red-500/30 bg-red-500/20 !text-red-300 hover:bg-red-500/30"
42
- }`}
43
- >
44
- Toggle State ({open ? "ON" : "OFF"})
45
- </button>
46
-
47
- <div className="grid grid-cols-2 gap-3">
48
- <button
49
- onClick={() => onOpenChange(true)}
50
- className="rounded-lg border border-green-500/30 bg-green-500/20 !px-3 !py-2 !text-sm !text-green-300 transition-colors hover:bg-green-500/30"
51
- >
52
- Set True
53
- </button>
54
- <button
55
- onClick={() => onOpenChange(false)}
56
- className="rounded-lg border border-red-500/30 bg-red-500/20 !px-3 !py-2 !text-sm !text-red-300 transition-colors hover:bg-red-500/30"
57
- >
58
- Set False
59
- </button>
60
- </div>
61
- </div>
67
+ const ChangeStateInteractiveDemo: React.FC = () => {
68
+ const { open, onOpenChange } = useChangeState()
62
69
 
63
- {/* Usage Information */}
64
- <div className="rounded-lg border border-white/10 bg-black/20 !p-4">
65
- <h4 className="!mb-3 !text-lg font-semibold !text-white">Hook Usage</h4>
66
- <div className="!space-y-2 !text-sm">
67
- <div className="!text-white/70">
68
- <span className="font-medium !text-white">Pattern:</span> const{" "}
69
- {`{ open, onOpenChange }`} = useChangeState()
70
- </div>
71
- <div className="!text-white/70">
72
- <span className="font-medium !text-white">Toggle:</span>{" "}
73
- onOpenChange() - toggles current state
74
- </div>
75
- <div className="!text-white/70">
76
- <span className="font-medium !text-white">Set value:</span>{" "}
77
- onOpenChange(true/false) - sets specific value
70
+ return (
71
+ <HookPlaygroundCanvas>
72
+ <div className="mx-auto w-full max-w-lg space-y-6 p-8">
73
+ <HookStateGrid>
74
+ <HookStateCard label="open" value={String(open)} />
75
+ <HookStateCard label="Status" value={open ? "Open" : "Closed"} />
76
+ </HookStateGrid>
77
+
78
+ <HookPanel title="Controls">
79
+ <div className="flex flex-wrap gap-3">
80
+ <HookControlButton
81
+ onClick={() => onOpenChange()}
82
+ className="flex-1"
83
+ >
84
+ Toggle
85
+ </HookControlButton>
86
+ <HookControlButton
87
+ onClick={() => onOpenChange(true)}
88
+ variant="positive"
89
+ className="flex-1"
90
+ >
91
+ Set True
92
+ </HookControlButton>
93
+ <HookControlButton
94
+ onClick={() => onOpenChange(false)}
95
+ variant="negative"
96
+ className="flex-1"
97
+ >
98
+ Set False
99
+ </HookControlButton>
78
100
  </div>
79
- </div>
101
+ </HookPanel>
102
+
103
+ <HookCodeBlock
104
+ code={`const { open, onOpenChange } = useChangeState()
105
+
106
+ onOpenChange() // toggles current state
107
+ onOpenChange(true) // sets open = true
108
+ onOpenChange(false) // sets open = false`}
109
+ />
80
110
  </div>
81
- </div>
111
+ </HookPlaygroundCanvas>
82
112
  )
83
113
  }
84
114
 
85
- const meta: Meta<typeof ChangeStateDemo> = {
115
+ // ─────────────────────────────────────────────────────────────────────────────
116
+ // Meta
117
+ // ─────────────────────────────────────────────────────────────────────────────
118
+
119
+ const meta: Meta<typeof ChangeStatePlaygroundDemo> = {
86
120
  title: "Hooks/useChangeState",
87
- component: ChangeStateDemo,
121
+ component: ChangeStatePlaygroundDemo,
88
122
  parameters: {
89
123
  layout: "fullscreen",
90
124
  backgrounds: {
91
125
  default: "dark",
92
126
  values: [
93
- { name: "dark", value: "#0a0a0a" },
94
- { name: "darker", value: "#000000" },
95
- { name: "light", value: "#ffffff" },
127
+ { name: "dark", value: "var(--color-fm-surface-primary)" },
128
+ { name: "darker", value: "var(--color-fm-neutral-0)" },
129
+ { name: "light", value: "var(--color-fm-neutral-1100)" },
96
130
  ],
97
131
  },
98
132
  docs: {
99
133
  page: () => (
100
- <>
101
- {/* Override default docs styling */}
102
- <style>
103
- {`
104
- .sbdocs-wrapper {
105
- padding: 0 !important;
106
- max-width: none !important;
107
- background: transparent !important;
108
- }
109
- .sbdocs-content {
110
- max-width: none !important;
111
- padding: 0 !important;
112
- margin: 0 !important;
113
- background: transparent !important;
114
- }
115
- .docs-story {
116
- background: transparent !important;
117
- }
118
- .sbdocs {
119
- background: transparent !important;
120
- }
121
- body {
122
- background: #0a0a0a !important;
123
- }
124
- #storybook-docs {
125
- background: #0a0a0a !important;
126
- }
127
- .sbdocs-preview {
128
- background: transparent !important;
129
- border: none !important;
130
- }
131
- .sbdocs-h1, .sbdocs-h2, .sbdocs-h3, .sbdocs-h4, .sbdocs-h5, .sbdocs-h6 {
132
- color: white !important;
133
- }
134
- .sbdocs-p, .sbdocs-li {
135
- color: rgba(255, 255, 255, 0.7) !important;
136
- }
137
- .sbdocs-code {
138
- background: rgba(255, 255, 255, 0.1) !important;
139
- color: rgba(168, 85, 247, 1) !important;
140
- border: 1px solid rgba(255, 255, 255, 0.1) !important;
141
- }
142
- .sbdocs-pre {
143
- background: rgba(0, 0, 0, 0.4) !important;
144
- border: 1px solid rgba(255, 255, 255, 0.1) !important;
145
- }
146
- .sbdocs-table {
147
- background: rgba(255, 255, 255, 0.05) !important;
148
- border: 1px solid rgba(255, 255, 255, 0.1) !important;
149
- }
150
- .sbdocs-table th {
151
- background: rgba(255, 255, 255, 0.05) !important;
152
- color: white !important;
153
- border-bottom: 1px solid rgba(255, 255, 255, 0.1) !important;
154
- }
155
- .sbdocs-table td {
156
- color: rgba(255, 255, 255, 0.7) !important;
157
- border-bottom: 1px solid rgba(255, 255, 255, 0.05) !important;
158
- }
159
- `}
160
- </style>
161
-
162
- <div className="min-h-screen bg-gradient-to-br from-gray-900 via-purple-900/20 to-gray-900">
163
- {/* Header */}
164
- <div className="relative overflow-hidden border-b border-white/10 bg-black/20 backdrop-blur-xl">
165
- <div className="absolute inset-0 bg-gradient-to-r from-purple-500/10 via-transparent to-indigo-500/10" />
166
- <div className="relative !mx-auto max-w-7xl !px-6 !py-16">
167
- <div className="!space-y-6 text-center">
168
- <div className="!mx-auto flex !h-24 !w-24 items-center justify-center rounded-2xl border border-purple-500/30 bg-gradient-to-br from-purple-500/20 to-indigo-500/20">
169
- <span className="!text-4xl">
170
- <LightBulbSimpleIcon className="text-fm-primary size-10" />
171
- </span>
172
- </div>
173
- <h1 className="!text-fm-primary !text-5xl font-bold">
174
- useChangeState
175
- </h1>
176
- <p className="!mx-auto max-w-3xl !text-xl leading-relaxed !text-white/70">
177
- A versatile React hook that manages boolean state with a
178
- flexible toggle function. Perfect for modals, dropdowns,
179
- accordions, and any component that needs show/hide
180
- functionality with customizable state control.
181
- </p>
182
-
183
- {/* Stats */}
184
- <div className="flex items-center justify-center gap-8 !pt-8">
185
- <div className="text-center">
186
- <div className="!text-3xl font-bold !text-purple-300">
187
- Flexible
188
- </div>
189
- <div className="!text-sm !text-white/60">
190
- Toggle or set specific value
191
- </div>
192
- </div>
193
- <div className="!h-8 !w-px bg-white/20" />
194
- <div className="text-center">
195
- <div className="!text-3xl font-bold !text-indigo-300">
196
- Optimized
197
- </div>
198
- <div className="!text-sm !text-white/60">
199
- Memoized callbacks
200
- </div>
201
- </div>
202
- <div className="!h-8 !w-px bg-white/20" />
203
- <div className="text-center">
204
- <div className="!text-3xl font-bold !text-cyan-300">
205
- Intuitive
206
- </div>
207
- <div className="!text-sm !text-white/60">
208
- Natural API design
209
- </div>
210
- </div>
211
- </div>
212
- </div>
213
- </div>
214
- </div>
215
-
216
- {/* Content */}
217
- <div className="!mx-auto max-w-7xl !space-y-16 !px-6 !py-12">
218
- {/* Quick Usage */}
219
- <div className="!space-y-8">
220
- <h2 className="text-center !text-3xl font-bold !text-white">
221
- Quick Start
222
- </h2>
223
- <div className="grid grid-cols-1 gap-8 lg:grid-cols-2">
224
- <div className="!space-y-4">
225
- <h3 className="!text-xl font-semibold !text-purple-300">
226
- Basic Usage
227
- </h3>
228
- <div className="rounded-lg bg-black/40 !p-4">
229
- <pre className="overflow-x-auto !text-sm !text-green-300">
230
- {`import { useChangeState } from "@hooks/use-change-state"
134
+ <AuralHookDocsPage
135
+ icon={LightBulbSimpleIcon}
136
+ features={[
137
+ { title: "Flexible", description: "Toggle or set specific value" },
138
+ { title: "Optimized", description: "Memoized callbacks" },
139
+ { title: "Intuitive", description: "Natural API design" },
140
+ ]}
141
+ quickStart={{
142
+ codeExample: `import { useChangeState } from "src/ui/hooks/use-change-state"
231
143
 
232
144
  function Modal() {
233
145
  const { open, onOpenChange } = useChangeState()
234
146
 
235
147
  return (
236
148
  <>
237
- <button onClick={() => onOpenChange(true)}>
238
- Open Modal
239
- </button>
240
-
149
+ <button onClick={() => onOpenChange(true)}>Open Modal</button>
150
+
241
151
  {open && (
242
- <div className="modal-overlay">
243
- <div className="modal-content">
244
- <h2>Modal Title</h2>
245
- <button onClick={() => onOpenChange(false)}>
246
- Close
247
- </button>
248
- <button onClick={() => onOpenChange()}>
249
- Toggle
250
- </button>
251
- </div>
152
+ <div>
153
+ <h2>Modal Title</h2>
154
+ <button onClick={() => onOpenChange(false)}>Close</button>
155
+ <button onClick={() => onOpenChange()}>Toggle</button>
252
156
  </div>
253
157
  )}
254
158
  </>
255
159
  )
256
- }`}
257
- </pre>
258
- </div>
259
- </div>
260
-
261
- <div className="!space-y-4">
262
- <h3 className="!text-xl font-semibold !text-purple-300">
263
- Hook Properties
264
- </h3>
265
- <div className="!space-y-3 rounded-lg border border-white/10 bg-white/5 !p-4">
266
- <div className="flex justify-between">
267
- <span className="!text-sm !text-white/70">
268
- Returns:
269
- </span>
270
- <span className="!text-sm font-medium !text-white">
271
- Object with state & handler
272
- </span>
273
- </div>
274
- <div className="flex justify-between">
275
- <span className="!text-sm !text-white/70">
276
- Initial state:
277
- </span>
278
- <span className="!text-sm font-medium !text-white">
279
- false
280
- </span>
281
- </div>
282
- <div className="flex justify-between">
283
- <span className="!text-sm !text-white/70">
284
- Toggle behavior:
285
- </span>
286
- <span className="!text-sm font-medium !text-white">
287
- Smart default
288
- </span>
289
- </div>
290
- <div className="flex justify-between">
291
- <span className="!text-sm !text-white/70">
292
- Performance:
293
- </span>
294
- <span className="!text-sm font-medium !text-white">
295
- Memoized callback
296
- </span>
297
- </div>
298
- </div>
299
- </div>
300
- </div>
301
- </div>
302
-
303
- {/* API Documentation */}
304
- <div className="!space-y-8">
305
- <h2 className="text-center !text-3xl font-bold !text-white">
306
- API Reference
307
- </h2>
308
-
309
- <div className="overflow-hidden rounded-lg border border-white/10 bg-white/5">
310
- <div className="bg-white/5 !p-4">
311
- <h3 className="!text-xl font-semibold !text-white">
312
- Hook Signature
313
- </h3>
314
- </div>
315
- <div className="!p-6">
316
- <div className="rounded-lg bg-black/40 !p-4">
317
- <pre className="!text-sm !text-green-300">
318
- {`function useChangeState(): {
160
+ }`,
161
+ hookProperties: [
162
+ { label: "Returns", value: "Object with state & handler" },
163
+ { label: "Initial state", value: "false" },
164
+ { label: "Toggle behavior", value: "Smart default" },
165
+ { label: "Performance", value: "Memoized callback" },
166
+ ],
167
+ }}
168
+ hookSignature={`function useChangeState(): {
319
169
  open: boolean
320
170
  onOpenChange: (value?: boolean) => void
321
171
  }`}
322
- </pre>
323
- </div>
324
- </div>
325
- </div>
326
-
327
- <div className="overflow-hidden rounded-lg border border-white/10 bg-white/5">
328
- <div className="bg-white/5 !p-4">
329
- <h3 className="!text-xl font-semibold !text-white">
330
- Return Value & Methods
331
- </h3>
332
- </div>
333
- <table className="!my-0 w-full">
334
- <thead className="bg-white/5">
335
- <tr className="border-b border-white/10">
336
- <th className="!px-6 !py-4 text-left !text-sm font-semibold !text-white">
337
- Property
338
- </th>
339
- <th className="!px-6 !py-4 text-left !text-sm font-semibold !text-white">
340
- Type
341
- </th>
342
- <th className="!px-6 !py-4 text-left !text-sm font-semibold !text-white">
343
- Description
344
- </th>
345
- </tr>
346
- </thead>
347
- <tbody>
348
- <tr className="border-b border-white/5">
349
- <td className="!px-6 !py-4 font-mono !text-sm !text-purple-300">
350
- open
351
- </td>
352
- <td className="!px-6 !py-4 !text-sm !text-white/70">
353
- boolean
354
- </td>
355
- <td className="!px-6 !py-4 !text-sm !text-white/70">
356
- Current state value (initially false)
357
- </td>
358
- </tr>
359
- <tr className="!bg-black/10">
360
- <td className="!px-6 !py-4 font-mono !text-sm !text-purple-300">
361
- onOpenChange
362
- </td>
363
- <td className="!px-6 !py-4 !text-sm !text-white/70">
364
- (value?: boolean) ={">"} void
365
- </td>
366
- <td className="!px-6 !py-4 !text-sm !text-white/70">
367
- Function to update state. Toggles if no value
368
- provided, sets specific value if provided
369
- </td>
370
- </tr>
371
- </tbody>
372
- </table>
373
- </div>
374
- </div>
375
-
376
- {/* Use Cases */}
377
- <div className="!space-y-8">
378
- <h2 className="text-center !text-3xl font-bold !text-white">
379
- Common Use Cases
380
- </h2>
381
- <div className="grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3">
382
- <div className="rounded-lg border border-white/10 bg-white/5 !p-6">
383
- <div className="!mb-4 flex !h-12 !w-12 items-center justify-center rounded-lg bg-blue-500/20">
384
- <span className="!text-2xl">🪟</span>
385
- </div>
386
- <h3 className="!mb-2 !text-lg font-semibold !text-white">
387
- Modals & Dialogs
388
- </h3>
389
- <p className="!text-sm !text-white/70">
390
- Control modal visibility with precise open/close
391
- functionality and smooth toggle behavior.
392
- </p>
393
- </div>
394
-
395
- <div className="rounded-lg border border-white/10 bg-white/5 !p-6">
396
- <div className="!mb-4 flex !h-12 !w-12 items-center justify-center rounded-lg bg-green-500/20">
397
- <span className="!text-2xl">📋</span>
398
- </div>
399
- <h3 className="!mb-2 !text-lg font-semibold !text-white">
400
- Dropdowns
401
- </h3>
402
- <p className="!text-sm !text-white/70">
403
- Manage dropdown menu states with intelligent toggle and
404
- explicit control methods.
405
- </p>
406
- </div>
407
-
408
- <div className="rounded-lg border border-white/10 bg-white/5 !p-6">
409
- <div className="!mb-4 flex !h-12 !w-12 items-center justify-center rounded-lg bg-purple-500/20">
410
- <span className="!text-2xl">📖</span>
411
- </div>
412
- <h3 className="!mb-2 !text-lg font-semibold !text-white">
413
- Accordions
414
- </h3>
415
- <p className="!text-sm !text-white/70">
416
- Handle expand/collapse states for accordion components and
417
- collapsible content sections.
418
- </p>
419
- </div>
420
-
421
- <div className="rounded-lg border border-white/10 bg-white/5 !p-6">
422
- <div className="!mb-4 flex !h-12 !w-12 items-center justify-center rounded-lg bg-orange-500/20">
423
- <span className="!text-2xl">👁️</span>
424
- </div>
425
- <h3 className="!mb-2 !text-lg font-semibold !text-white">
426
- Show/Hide Content
427
- </h3>
428
- <p className="!text-sm !text-white/70">
429
- Toggle visibility of any content with flexible control
430
- over show and hide operations.
431
- </p>
432
- </div>
433
-
434
- <div className="rounded-lg border border-white/10 bg-white/5 !p-6">
435
- <div className="!mb-4 flex !h-12 !w-12 items-center justify-center rounded-lg bg-cyan-500/20">
436
- <span className="!text-2xl">🎛️</span>
437
- </div>
438
- <h3 className="!mb-2 !text-lg font-semibold !text-white">
439
- UI Controls
440
- </h3>
441
- <p className="!text-sm !text-white/70">
442
- Power switches, toggles, and other boolean-based UI
443
- components with optimized state management.
444
- </p>
445
- </div>
446
-
447
- <div className="rounded-lg border border-white/10 bg-white/5 !p-6">
448
- <div className="!mb-4 flex !h-12 !w-12 items-center justify-center rounded-lg bg-pink-500/20">
449
- <span className="!text-2xl">🔧</span>
450
- </div>
451
- <h3 className="!mb-2 !text-lg font-semibold !text-white">
452
- Feature Flags
453
- </h3>
454
- <p className="!text-sm !text-white/70">
455
- Implement feature toggles and conditional rendering with
456
- clean state management patterns.
457
- </p>
458
- </div>
459
- </div>
460
- </div>
461
-
462
- {/* Usage Patterns */}
463
- <div className="!space-y-8">
464
- <h2 className="text-center !text-3xl font-bold !text-white">
465
- Usage Patterns
466
- </h2>
467
- <div className="grid grid-cols-1 gap-8 lg:grid-cols-2">
468
- <div className="!space-y-4">
469
- <h3 className="!text-xl font-semibold !text-purple-300">
470
- Modal with Backdrop Close
471
- </h3>
472
- <div className="rounded-lg bg-black/40 !p-4">
473
- <pre className="overflow-x-auto !text-sm !text-green-300">
474
- {`function ConfirmDialog({ message, onConfirm }) {
172
+ returns={[
173
+ {
174
+ name: "open",
175
+ type: "boolean",
176
+ description: "Current state value, initially false.",
177
+ },
178
+ {
179
+ name: "onOpenChange",
180
+ type: "(value?: boolean) => void",
181
+ description:
182
+ "Toggles the state when called with no argument; sets a specific value when a boolean is passed.",
183
+ },
184
+ ]}
185
+ useCases={[
186
+ {
187
+ icon: NotepadIcon,
188
+ title: "Modals & Dialogs",
189
+ description:
190
+ "Control modal visibility with precise open/close and toggle behavior.",
191
+ },
192
+ {
193
+ icon: ChevronDownIcon,
194
+ title: "Dropdowns",
195
+ description:
196
+ "Manage dropdown menu states with intelligent toggle and explicit control.",
197
+ },
198
+ {
199
+ icon: LayoutColumnIcon,
200
+ title: "Accordions",
201
+ description:
202
+ "Handle expand/collapse states for accordion and collapsible sections.",
203
+ },
204
+ {
205
+ icon: EyeOpenIcon,
206
+ title: "Show / Hide",
207
+ description:
208
+ "Toggle visibility of any content with flexible show and hide control.",
209
+ },
210
+ {
211
+ icon: SettingIcon,
212
+ title: "UI Controls",
213
+ description:
214
+ "Power switches, toggles, and other boolean-based UI components.",
215
+ },
216
+ {
217
+ icon: FeatureShineIcon,
218
+ title: "Feature Flags",
219
+ description:
220
+ "Implement feature toggles and conditional rendering patterns.",
221
+ },
222
+ ]}
223
+ usagePatterns={[
224
+ {
225
+ title: "Modal with Backdrop Close",
226
+ code: `function ConfirmDialog({ message, onConfirm }) {
475
227
  const { open, onOpenChange } = useChangeState()
476
228
 
477
229
  const handleBackdropClick = (e) => {
478
- if (e.target === e.currentTarget) {
479
- onOpenChange(false)
480
- }
230
+ if (e.target === e.currentTarget) onOpenChange(false)
481
231
  }
482
232
 
483
233
  return (
484
234
  <>
485
- <button onClick={() => onOpenChange(true)}>
486
- Delete Item
487
- </button>
488
-
235
+ <button onClick={() => onOpenChange(true)}>Delete Item</button>
236
+
489
237
  {open && (
490
- <div
491
- className="modal-overlay"
492
- onClick={handleBackdropClick}
493
- >
238
+ <div className="modal-overlay" onClick={handleBackdropClick}>
494
239
  <div className="modal-content">
495
240
  <p>{message}</p>
496
241
  <button onClick={onConfirm}>Confirm</button>
497
- <button onClick={() => onOpenChange(false)}>
498
- Cancel
499
- </button>
242
+ <button onClick={() => onOpenChange(false)}>Cancel</button>
500
243
  </div>
501
244
  </div>
502
245
  )}
503
246
  </>
504
247
  )
505
- }`}
506
- </pre>
507
- </div>
508
- </div>
509
-
510
- <div className="!space-y-4">
511
- <h3 className="!text-xl font-semibold !text-purple-300">
512
- Collapsible Section
513
- </h3>
514
- <div className="rounded-lg bg-black/40 !p-4">
515
- <pre className="overflow-x-auto !text-sm !text-green-300">
516
- {`function CollapsibleCard({ title, children }) {
248
+ }`,
249
+ },
250
+ {
251
+ title: "Collapsible Section",
252
+ code: `function CollapsibleCard({ title, children }) {
517
253
  const { open, onOpenChange } = useChangeState()
518
254
 
519
255
  return (
520
256
  <div className="card">
521
- <div
522
- className="card-header"
523
- onClick={() => onOpenChange()}
524
- >
257
+ <div className="card-header" onClick={() => onOpenChange()}>
525
258
  <h3>{title}</h3>
526
- <span className={\`icon \${open ? 'rotate' : ''}\`}>
527
-
528
- </span>
259
+ <span className={\`icon \${open ? "rotate" : ""}\`}>▼</span>
529
260
  </div>
530
-
531
- {open && (
532
- <div className="card-content">
533
- {children}
534
- </div>
535
- )}
261
+
262
+ {open && <div className="card-content">{children}</div>}
536
263
  </div>
537
264
  )
538
- }`}
539
- </pre>
540
- </div>
541
- </div>
542
-
543
- <div className="!space-y-4">
544
- <h3 className="!text-xl font-semibold !text-purple-300">
545
- Controlled Dropdown Menu
546
- </h3>
547
- <div className="rounded-lg bg-black/40 !p-4">
548
- <pre className="overflow-x-auto !text-sm !text-green-300">
549
- {`function DropdownMenu({ trigger, items }) {
265
+ }`,
266
+ },
267
+ {
268
+ title: "Controlled Dropdown",
269
+ code: `function DropdownMenu({ trigger, items }) {
550
270
  const { open, onOpenChange } = useChangeState()
551
271
  const menuRef = useRef()
552
272
 
553
273
  useEffect(() => {
554
- const handleClickOutside = (event) => {
555
- if (menuRef.current &&
556
- !menuRef.current.contains(event.target)) {
274
+ const handleOutside = (e) => {
275
+ if (menuRef.current && !menuRef.current.contains(e.target)) {
557
276
  onOpenChange(false)
558
277
  }
559
278
  }
560
-
561
279
  if (open) {
562
- document.addEventListener('click', handleClickOutside)
563
- return () => document.removeEventListener('click', handleClickOutside)
280
+ document.addEventListener("click", handleOutside)
281
+ return () => document.removeEventListener("click", handleOutside)
564
282
  }
565
283
  }, [open, onOpenChange])
566
284
 
567
285
  return (
568
- <div className="dropdown" ref={menuRef}>
569
- <button onClick={() => onOpenChange()}>
570
- {trigger}
571
- </button>
572
-
286
+ <div ref={menuRef}>
287
+ <button onClick={() => onOpenChange()}>{trigger}</button>
573
288
  {open && (
574
289
  <div className="dropdown-menu">
575
- {items.map(item => (
576
- <div
577
- key={item.id}
578
- onClick={() => {
579
- item.onClick()
580
- onOpenChange(false)
581
- }}
582
- >
290
+ {items.map((item) => (
291
+ <div key={item.id} onClick={() => { item.onClick(); onOpenChange(false) }}>
583
292
  {item.label}
584
293
  </div>
585
294
  ))}
@@ -587,73 +296,41 @@ function Modal() {
587
296
  )}
588
297
  </div>
589
298
  )
590
- }`}
591
- </pre>
592
- </div>
593
- </div>
594
-
595
- <div className="!space-y-4">
596
- <h3 className="!text-xl font-semibold !text-purple-300">
597
- Conditional Form Section
598
- </h3>
599
- <div className="rounded-lg bg-black/40 !p-4">
600
- <pre className="overflow-x-auto !text-sm !text-green-300">
601
- {`function ProfileForm() {
299
+ }`,
300
+ },
301
+ {
302
+ title: "Conditional Form Section",
303
+ code: `function ProfileForm() {
602
304
  const { open: showAdvanced, onOpenChange } = useChangeState()
603
305
  const [formData, setFormData] = useState({})
604
306
 
605
- const toggleAdvancedOptions = () => {
606
- onOpenChange()
607
-
608
- // Reset advanced fields when hiding
307
+ const toggleAdvanced = () => {
609
308
  if (showAdvanced) {
610
- setFormData(prev => ({
611
- ...prev,
612
- advancedField1: '',
613
- advancedField2: ''
614
- }))
309
+ setFormData((prev) => ({ ...prev, advField1: "", advField2: "" }))
615
310
  }
311
+ onOpenChange()
616
312
  }
617
313
 
618
314
  return (
619
315
  <div>
620
316
  <input placeholder="Name" />
621
317
  <input placeholder="Email" />
622
-
623
- <button
624
- type="button"
625
- onClick={toggleAdvancedOptions}
626
- >
627
- {showAdvanced ? 'Hide' : 'Show'} Advanced Options
318
+ <button type="button" onClick={toggleAdvanced}>
319
+ {showAdvanced ? "Hide" : "Show"} Advanced Options
628
320
  </button>
629
-
630
321
  {showAdvanced && (
631
- <div className="advanced-section">
322
+ <div>
632
323
  <input placeholder="Advanced Field 1" />
633
324
  <input placeholder="Advanced Field 2" />
634
325
  </div>
635
326
  )}
636
327
  </div>
637
328
  )
638
- }`}
639
- </pre>
640
- </div>
641
- </div>
642
- </div>
643
- </div>
644
-
645
- {/* Implementation Details */}
646
- <div className="!space-y-8">
647
- <h2 className="text-center !text-3xl font-bold !text-white">
648
- Implementation
649
- </h2>
650
- <div className="rounded-lg border border-white/10 bg-white/5 !p-6">
651
- <h3 className="!mb-4 !text-xl font-semibold !text-white">
652
- Hook Implementation
653
- </h3>
654
- <div className="rounded-lg bg-black/40 !p-4">
655
- <pre className="overflow-x-auto !text-sm !text-blue-300">
656
- {`import React from "react"
329
+ }`,
330
+ },
331
+ ]}
332
+ implementation={{
333
+ code: `import React from "react"
657
334
 
658
335
  export const useChangeState = () => {
659
336
  const [open, setOpen] = React.useState(false)
@@ -669,61 +346,22 @@ export const useChangeState = () => {
669
346
  open,
670
347
  onOpenChange,
671
348
  }
672
- }`}
673
- </pre>
674
- </div>
675
- <div className="!mt-4 !space-y-2 !text-sm !text-white/70">
676
- <p className="!text-white">
677
- The hook combines{" "}
678
- <code className="!text-purple-300">useState</code> for
679
- boolean state management with{" "}
680
- <code className="!text-purple-300">useCallback</code> for
681
- optimized performance.
682
- </p>
683
- <p className="!text-white">
684
- The <code className="!text-purple-300">onOpenChange</code>{" "}
685
- function defaults to toggling the current state when no
686
- parameter is provided, or sets the exact value when a
687
- boolean is passed.
688
- </p>
689
- <p className="!text-white">
690
- <code className="!text-purple-300">useCallback</code>{" "}
691
- prevents unnecessary re-renders by memoizing the state
692
- updater function.
693
- </p>
694
- </div>
695
- </div>
696
- </div>
697
- </div>
698
-
699
- {/* Footer */}
700
- <div className="border-t border-white/10 bg-black/20 backdrop-blur-xl">
701
- <div className="!mx-auto max-w-7xl !px-6 !py-8">
702
- <div className="!space-y-4 text-center">
703
- <p className="!text-white/60">
704
- useChangeState provides an elegant solution for boolean
705
- state management with intelligent defaults and flexible
706
- control options, perfect for any show/hide UI pattern in
707
- React applications.
708
- </p>
709
- <p className="!text-sm !text-white/40">
710
- Optimized, intuitive, and versatile - the essential hook for
711
- managing toggleable component states with precision.
712
- </p>
713
- </div>
714
- </div>
715
- </div>
716
- </div>
717
- </>
349
+ }`,
350
+ notes: [
351
+ "Combines useState for boolean state management with useCallback to produce a stable, memoized handler that avoids unnecessary re-renders.",
352
+ "onOpenChange toggles the current state when called with no argument, or sets the exact boolean value when one is passed — a single function covers both use cases.",
353
+ "Because the default parameter is evaluated at call time, passing undefined explicitly also triggers the toggle behavior, keeping the API predictable.",
354
+ ],
355
+ }}
356
+ />
718
357
  ),
719
358
  },
720
359
  },
721
-
722
360
  tags: ["autodocs"],
723
361
  argTypes: {
724
- title: {
362
+ label: {
725
363
  control: "text",
726
- description: "Title for the demo component",
364
+ description: "Label shown on the status card in the playground",
727
365
  },
728
366
  },
729
367
  }
@@ -731,14 +369,79 @@ export const useChangeState = () => {
731
369
  export default meta
732
370
  type Story = StoryObj<typeof meta>
733
371
 
734
- export const Default: Story = {
735
- args: {
736
- title: "useChangeState Demo",
737
- },
738
- }
372
+ // ─────────────────────────────────────────────────────────────────────────────
373
+ // Story exports
374
+ // ─────────────────────────────────────────────────────────────────────────────
739
375
 
740
376
  export const Playground: Story = {
741
377
  args: {
742
- title: "Toggle State Hook",
378
+ label: "State Label",
743
379
  },
380
+ render: (args) => <ChangeStatePlaygroundDemo {...args} />,
381
+ }
382
+
383
+ export const Interactive: Story = {
384
+ render: () => <ChangeStateInteractiveDemo />,
385
+ }
386
+
387
+ export const UseCases: Story = {
388
+ render: () => (
389
+ <HookUsageCanvas>
390
+ <HookUsageSection title="Modal with Backdrop Close">
391
+ <HookCodeBlock
392
+ code={`function Modal() {
393
+ const { open, onOpenChange } = useChangeState()
394
+ return (
395
+ <>
396
+ <button onClick={() => onOpenChange(true)}>Open</button>
397
+ {open && (
398
+ <div onClick={(e) => e.target === e.currentTarget && onOpenChange(false)}>
399
+ <div>
400
+ <h2>Modal</h2>
401
+ <button onClick={() => onOpenChange(false)}>Close</button>
402
+ </div>
403
+ </div>
404
+ )}
405
+ </>
406
+ )
407
+ }`}
408
+ />
409
+ </HookUsageSection>
410
+
411
+ <HookUsageSection title="Collapsible Section">
412
+ <HookCodeBlock
413
+ code={`function Collapsible({ title, children }) {
414
+ const { open, onOpenChange } = useChangeState()
415
+ return (
416
+ <div>
417
+ <button onClick={() => onOpenChange()}>{title} {open ? "▲" : "▼"}</button>
418
+ {open && <div>{children}</div>}
419
+ </div>
420
+ )
421
+ }`}
422
+ />
423
+ </HookUsageSection>
424
+
425
+ <HookUsageSection title="Feature Flag Toggle">
426
+ <HookCodeBlock
427
+ code={`function FeatureGate({ children }) {
428
+ const { open: enabled, onOpenChange } = useChangeState()
429
+ return (
430
+ <div>
431
+ <label>
432
+ <input
433
+ type="checkbox"
434
+ checked={enabled}
435
+ onChange={(e) => onOpenChange(e.target.checked)}
436
+ />
437
+ Enable feature
438
+ </label>
439
+ {enabled && children}
440
+ </div>
441
+ )
442
+ }`}
443
+ />
444
+ </HookUsageSection>
445
+ </HookUsageCanvas>
446
+ ),
744
447
  }