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,585 +1,294 @@
|
|
|
1
1
|
import React from "react"
|
|
2
2
|
import type { Meta, StoryObj } from "@storybook/react-vite"
|
|
3
3
|
|
|
4
|
-
import {
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
<
|
|
15
|
-
<
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
|
|
35
|
-
|
|
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
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
<
|
|
67
|
-
<
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
</
|
|
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
|
-
</
|
|
111
|
+
</HookPlaygroundCanvas>
|
|
82
112
|
)
|
|
83
113
|
}
|
|
84
114
|
|
|
85
|
-
|
|
115
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
116
|
+
// Meta
|
|
117
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
118
|
+
|
|
119
|
+
const meta: Meta<typeof ChangeStatePlaygroundDemo> = {
|
|
86
120
|
title: "Hooks/useChangeState",
|
|
87
|
-
component:
|
|
121
|
+
component: ChangeStatePlaygroundDemo,
|
|
88
122
|
parameters: {
|
|
89
123
|
layout: "fullscreen",
|
|
90
124
|
backgrounds: {
|
|
91
125
|
default: "dark",
|
|
92
126
|
values: [
|
|
93
|
-
{ name: "dark", value: "
|
|
94
|
-
{ name: "darker", value: "
|
|
95
|
-
{ name: "light", value: "
|
|
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
|
-
{
|
|
102
|
-
|
|
103
|
-
{
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
-
|
|
239
|
-
</button>
|
|
240
|
-
|
|
149
|
+
<button onClick={() => onOpenChange(true)}>Open Modal</button>
|
|
150
|
+
|
|
241
151
|
{open && (
|
|
242
|
-
<div
|
|
243
|
-
<
|
|
244
|
-
|
|
245
|
-
|
|
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
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
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
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
{
|
|
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
|
-
|
|
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
|
-
|
|
507
|
-
|
|
508
|
-
|
|
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 ?
|
|
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
|
-
|
|
540
|
-
|
|
541
|
-
|
|
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
|
|
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(
|
|
563
|
-
return () => document.removeEventListener(
|
|
280
|
+
document.addEventListener("click", handleOutside)
|
|
281
|
+
return () => document.removeEventListener("click", handleOutside)
|
|
564
282
|
}
|
|
565
283
|
}, [open, onOpenChange])
|
|
566
284
|
|
|
567
285
|
return (
|
|
568
|
-
<div
|
|
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
|
-
|
|
592
|
-
|
|
593
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
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
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
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
|
-
|
|
362
|
+
label: {
|
|
725
363
|
control: "text",
|
|
726
|
-
description: "
|
|
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
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
},
|
|
738
|
-
}
|
|
372
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
373
|
+
// Story exports
|
|
374
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
739
375
|
|
|
740
376
|
export const Playground: Story = {
|
|
741
377
|
args: {
|
|
742
|
-
|
|
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
|
}
|