aural-ui 2.0.2 → 2.0.3
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/button/Button.stories.tsx +43 -0
- package/dist/components/button/index.tsx +10 -4
- package/dist/components/chip/index.tsx +3 -3
- package/dist/components/icon-button/IconButton.stories.tsx +48 -0
- package/dist/components/icon-button/index.tsx +6 -2
- package/dist/components/input/index.tsx +4 -0
- package/dist/components/overlay/index.tsx +22 -5
- package/dist/components/tag/index.tsx +2 -2
- package/dist/components/textarea/index.tsx +2 -0
- package/dist/components/tooltip/index.tsx +45 -0
- package/dist/hooks/index.ts +1 -0
- package/dist/hooks/use-change-state/UseChangeState.stories.tsx +744 -0
- package/dist/hooks/use-change-state/index.tsx +17 -0
- package/dist/hooks/use-change-state/meta.ts +6 -0
- package/dist/icons/circle-tick-icon/CircleTickIcon.stories.tsx +1204 -0
- package/dist/icons/circle-tick-icon/index.tsx +22 -0
- package/dist/icons/circle-tick-icon/meta.ts +8 -0
- package/dist/icons/copy-icon/CopyIcon.stories.tsx +1021 -0
- package/dist/icons/copy-icon/index.tsx +21 -0
- package/dist/icons/copy-icon/meta.ts +8 -0
- package/dist/icons/download-icon/DownloadIcon.stories.tsx +877 -0
- package/dist/icons/download-icon/index.tsx +22 -0
- package/dist/icons/download-icon/meta.ts +8 -0
- package/dist/icons/filter-bar-row-icon/FilterBarRowIcon.stories.tsx +1109 -0
- package/dist/icons/filter-bar-row-icon/index.tsx +24 -0
- package/dist/icons/filter-bar-row-icon/meta.ts +8 -0
- package/dist/icons/index.ts +8 -0
- package/dist/icons/notepad-icon/NotepadIcon.stories.tsx +1159 -0
- package/dist/icons/notepad-icon/index.tsx +21 -0
- package/dist/icons/notepad-icon/meta.ts +8 -0
- package/dist/icons/notes-icon/NotesIcon.stories.tsx +1159 -0
- package/dist/icons/notes-icon/index.tsx +24 -0
- package/dist/icons/notes-icon/meta.ts +8 -0
- package/dist/icons/paper-plane-icon/PaperPlaneIcon.stories.tsx +936 -0
- package/dist/icons/paper-plane-icon/index.tsx +33 -0
- package/dist/icons/paper-plane-icon/meta.ts +8 -0
- package/dist/icons/suggestion-icon/SuggestionIcon.stories.tsx +907 -0
- package/dist/icons/suggestion-icon/index.tsx +33 -0
- package/dist/icons/suggestion-icon/meta.ts +8 -0
- package/dist/index.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,744 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react"
|
|
3
|
+
|
|
4
|
+
import { LightBulbSimpleIcon } from "src/ui/icons"
|
|
5
|
+
|
|
6
|
+
import { useChangeState } from "."
|
|
7
|
+
|
|
8
|
+
const ChangeStateDemo: React.FC<{
|
|
9
|
+
title?: string
|
|
10
|
+
}> = ({ title = "useChangeState Demo" }) => {
|
|
11
|
+
const { open, onOpenChange } = useChangeState()
|
|
12
|
+
|
|
13
|
+
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>
|
|
32
|
+
</div>
|
|
33
|
+
|
|
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>
|
|
62
|
+
|
|
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
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const meta: Meta<typeof ChangeStateDemo> = {
|
|
86
|
+
title: "Hooks/useChangeState",
|
|
87
|
+
component: ChangeStateDemo,
|
|
88
|
+
parameters: {
|
|
89
|
+
layout: "fullscreen",
|
|
90
|
+
backgrounds: {
|
|
91
|
+
default: "dark",
|
|
92
|
+
values: [
|
|
93
|
+
{ name: "dark", value: "#0a0a0a" },
|
|
94
|
+
{ name: "darker", value: "#000000" },
|
|
95
|
+
{ name: "light", value: "#ffffff" },
|
|
96
|
+
],
|
|
97
|
+
},
|
|
98
|
+
docs: {
|
|
99
|
+
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"
|
|
231
|
+
|
|
232
|
+
function Modal() {
|
|
233
|
+
const { open, onOpenChange } = useChangeState()
|
|
234
|
+
|
|
235
|
+
return (
|
|
236
|
+
<>
|
|
237
|
+
<button onClick={() => onOpenChange(true)}>
|
|
238
|
+
Open Modal
|
|
239
|
+
</button>
|
|
240
|
+
|
|
241
|
+
{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>
|
|
252
|
+
</div>
|
|
253
|
+
)}
|
|
254
|
+
</>
|
|
255
|
+
)
|
|
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(): {
|
|
319
|
+
open: boolean
|
|
320
|
+
onOpenChange: (value?: boolean) => void
|
|
321
|
+
}`}
|
|
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 }) {
|
|
475
|
+
const { open, onOpenChange } = useChangeState()
|
|
476
|
+
|
|
477
|
+
const handleBackdropClick = (e) => {
|
|
478
|
+
if (e.target === e.currentTarget) {
|
|
479
|
+
onOpenChange(false)
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
return (
|
|
484
|
+
<>
|
|
485
|
+
<button onClick={() => onOpenChange(true)}>
|
|
486
|
+
Delete Item
|
|
487
|
+
</button>
|
|
488
|
+
|
|
489
|
+
{open && (
|
|
490
|
+
<div
|
|
491
|
+
className="modal-overlay"
|
|
492
|
+
onClick={handleBackdropClick}
|
|
493
|
+
>
|
|
494
|
+
<div className="modal-content">
|
|
495
|
+
<p>{message}</p>
|
|
496
|
+
<button onClick={onConfirm}>Confirm</button>
|
|
497
|
+
<button onClick={() => onOpenChange(false)}>
|
|
498
|
+
Cancel
|
|
499
|
+
</button>
|
|
500
|
+
</div>
|
|
501
|
+
</div>
|
|
502
|
+
)}
|
|
503
|
+
</>
|
|
504
|
+
)
|
|
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 }) {
|
|
517
|
+
const { open, onOpenChange } = useChangeState()
|
|
518
|
+
|
|
519
|
+
return (
|
|
520
|
+
<div className="card">
|
|
521
|
+
<div
|
|
522
|
+
className="card-header"
|
|
523
|
+
onClick={() => onOpenChange()}
|
|
524
|
+
>
|
|
525
|
+
<h3>{title}</h3>
|
|
526
|
+
<span className={\`icon \${open ? 'rotate' : ''}\`}>
|
|
527
|
+
▼
|
|
528
|
+
</span>
|
|
529
|
+
</div>
|
|
530
|
+
|
|
531
|
+
{open && (
|
|
532
|
+
<div className="card-content">
|
|
533
|
+
{children}
|
|
534
|
+
</div>
|
|
535
|
+
)}
|
|
536
|
+
</div>
|
|
537
|
+
)
|
|
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 }) {
|
|
550
|
+
const { open, onOpenChange } = useChangeState()
|
|
551
|
+
const menuRef = useRef()
|
|
552
|
+
|
|
553
|
+
useEffect(() => {
|
|
554
|
+
const handleClickOutside = (event) => {
|
|
555
|
+
if (menuRef.current &&
|
|
556
|
+
!menuRef.current.contains(event.target)) {
|
|
557
|
+
onOpenChange(false)
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
if (open) {
|
|
562
|
+
document.addEventListener('click', handleClickOutside)
|
|
563
|
+
return () => document.removeEventListener('click', handleClickOutside)
|
|
564
|
+
}
|
|
565
|
+
}, [open, onOpenChange])
|
|
566
|
+
|
|
567
|
+
return (
|
|
568
|
+
<div className="dropdown" ref={menuRef}>
|
|
569
|
+
<button onClick={() => onOpenChange()}>
|
|
570
|
+
{trigger}
|
|
571
|
+
</button>
|
|
572
|
+
|
|
573
|
+
{open && (
|
|
574
|
+
<div className="dropdown-menu">
|
|
575
|
+
{items.map(item => (
|
|
576
|
+
<div
|
|
577
|
+
key={item.id}
|
|
578
|
+
onClick={() => {
|
|
579
|
+
item.onClick()
|
|
580
|
+
onOpenChange(false)
|
|
581
|
+
}}
|
|
582
|
+
>
|
|
583
|
+
{item.label}
|
|
584
|
+
</div>
|
|
585
|
+
))}
|
|
586
|
+
</div>
|
|
587
|
+
)}
|
|
588
|
+
</div>
|
|
589
|
+
)
|
|
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() {
|
|
602
|
+
const { open: showAdvanced, onOpenChange } = useChangeState()
|
|
603
|
+
const [formData, setFormData] = useState({})
|
|
604
|
+
|
|
605
|
+
const toggleAdvancedOptions = () => {
|
|
606
|
+
onOpenChange()
|
|
607
|
+
|
|
608
|
+
// Reset advanced fields when hiding
|
|
609
|
+
if (showAdvanced) {
|
|
610
|
+
setFormData(prev => ({
|
|
611
|
+
...prev,
|
|
612
|
+
advancedField1: '',
|
|
613
|
+
advancedField2: ''
|
|
614
|
+
}))
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
return (
|
|
619
|
+
<div>
|
|
620
|
+
<input placeholder="Name" />
|
|
621
|
+
<input placeholder="Email" />
|
|
622
|
+
|
|
623
|
+
<button
|
|
624
|
+
type="button"
|
|
625
|
+
onClick={toggleAdvancedOptions}
|
|
626
|
+
>
|
|
627
|
+
{showAdvanced ? 'Hide' : 'Show'} Advanced Options
|
|
628
|
+
</button>
|
|
629
|
+
|
|
630
|
+
{showAdvanced && (
|
|
631
|
+
<div className="advanced-section">
|
|
632
|
+
<input placeholder="Advanced Field 1" />
|
|
633
|
+
<input placeholder="Advanced Field 2" />
|
|
634
|
+
</div>
|
|
635
|
+
)}
|
|
636
|
+
</div>
|
|
637
|
+
)
|
|
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"
|
|
657
|
+
|
|
658
|
+
export const useChangeState = () => {
|
|
659
|
+
const [open, setOpen] = React.useState(false)
|
|
660
|
+
|
|
661
|
+
const onOpenChange = React.useCallback(
|
|
662
|
+
(_value = !open) => {
|
|
663
|
+
setOpen(_value)
|
|
664
|
+
},
|
|
665
|
+
[open]
|
|
666
|
+
)
|
|
667
|
+
|
|
668
|
+
return {
|
|
669
|
+
open,
|
|
670
|
+
onOpenChange,
|
|
671
|
+
}
|
|
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
|
+
</>
|
|
718
|
+
),
|
|
719
|
+
},
|
|
720
|
+
},
|
|
721
|
+
|
|
722
|
+
tags: ["autodocs"],
|
|
723
|
+
argTypes: {
|
|
724
|
+
title: {
|
|
725
|
+
control: "text",
|
|
726
|
+
description: "Title for the demo component",
|
|
727
|
+
},
|
|
728
|
+
},
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
export default meta
|
|
732
|
+
type Story = StoryObj<typeof meta>
|
|
733
|
+
|
|
734
|
+
export const Default: Story = {
|
|
735
|
+
args: {
|
|
736
|
+
title: "useChangeState Demo",
|
|
737
|
+
},
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
export const Playground: Story = {
|
|
741
|
+
args: {
|
|
742
|
+
title: "Toggle State Hook",
|
|
743
|
+
},
|
|
744
|
+
}
|