aural-ui 2.0.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 (308) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +456 -0
  3. package/dist/components/aspect-ratio/AspectRatio.stories.tsx +1327 -0
  4. package/dist/components/aspect-ratio/index.tsx +10 -0
  5. package/dist/components/aspect-ratio/meta.ts +8 -0
  6. package/dist/components/avatar/Avatar.stories.tsx +645 -0
  7. package/dist/components/avatar/index.tsx +50 -0
  8. package/dist/components/avatar/meta.ts +8 -0
  9. package/dist/components/badge/Badge.stories.tsx +169 -0
  10. package/dist/components/badge/index.tsx +28 -0
  11. package/dist/components/badge/meta.ts +6 -0
  12. package/dist/components/banner/Banner.stories.tsx +475 -0
  13. package/dist/components/banner/index.tsx +256 -0
  14. package/dist/components/banner/meta.ts +36 -0
  15. package/dist/components/button/Button.stories.tsx +74 -0
  16. package/dist/components/button/index.tsx +158 -0
  17. package/dist/components/button/meta.ts +33 -0
  18. package/dist/components/card/Card.stories.tsx +377 -0
  19. package/dist/components/card/index.tsx +85 -0
  20. package/dist/components/card/meta.ts +14 -0
  21. package/dist/components/char-count/CharCount.stories.tsx +334 -0
  22. package/dist/components/char-count/index.tsx +51 -0
  23. package/dist/components/char-count/meta.ts +13 -0
  24. package/dist/components/checkbox/Checkbox.stories.tsx +209 -0
  25. package/dist/components/checkbox/index.tsx +34 -0
  26. package/dist/components/checkbox/meta.ts +19 -0
  27. package/dist/components/chip/Chip.stories.tsx +207 -0
  28. package/dist/components/chip/index.tsx +92 -0
  29. package/dist/components/chip/meta.ts +17 -0
  30. package/dist/components/circular-loader/CircularLoader.stories.tsx +741 -0
  31. package/dist/components/circular-loader/index.tsx +138 -0
  32. package/dist/components/circular-loader/meta.ts +11 -0
  33. package/dist/components/collapsible/Collapsible.stories.tsx +319 -0
  34. package/dist/components/collapsible/index.tsx +158 -0
  35. package/dist/components/collapsible/meta.ts +22 -0
  36. package/dist/components/command/Command.stories.tsx +996 -0
  37. package/dist/components/command/index.tsx +324 -0
  38. package/dist/components/command/meta.ts +18 -0
  39. package/dist/components/dialog/Dialog.stories.tsx +963 -0
  40. package/dist/components/dialog/index.tsx +250 -0
  41. package/dist/components/dialog/meta.ts +28 -0
  42. package/dist/components/divider/Divider.stories.tsx +633 -0
  43. package/dist/components/divider/index.tsx +181 -0
  44. package/dist/components/divider/meta.ts +12 -0
  45. package/dist/components/dot-loader/DotLoader.stories.tsx +352 -0
  46. package/dist/components/dot-loader/index.tsx +78 -0
  47. package/dist/components/dot-loader/meta.ts +14 -0
  48. package/dist/components/dropdown/Dropdown.stories.tsx +1210 -0
  49. package/dist/components/dropdown/index.tsx +479 -0
  50. package/dist/components/dropdown/meta.ts +21 -0
  51. package/dist/components/form/Form.stories.tsx +320 -0
  52. package/dist/components/form/index.tsx +183 -0
  53. package/dist/components/form/meta.ts +11 -0
  54. package/dist/components/helper-text/HelperText.stories.tsx +254 -0
  55. package/dist/components/helper-text/index.tsx +102 -0
  56. package/dist/components/helper-text/meta.ts +18 -0
  57. package/dist/components/hover-card/HoverCard.stories.tsx +1328 -0
  58. package/dist/components/hover-card/index.tsx +42 -0
  59. package/dist/components/hover-card/meta.ts +12 -0
  60. package/dist/components/icon-button/IconButton.stories.tsx +252 -0
  61. package/dist/components/icon-button/index.tsx +130 -0
  62. package/dist/components/icon-button/meta.ts +20 -0
  63. package/dist/components/if-else/if-else.stories.tsx +100 -0
  64. package/dist/components/if-else/index.tsx +56 -0
  65. package/dist/components/if-else/meta.ts +6 -0
  66. package/dist/components/index.ts +70 -0
  67. package/dist/components/input/Input.stories.tsx +431 -0
  68. package/dist/components/input/index.tsx +487 -0
  69. package/dist/components/input/meta.ts +28 -0
  70. package/dist/components/label/Label.stories.tsx +200 -0
  71. package/dist/components/label/index.tsx +43 -0
  72. package/dist/components/label/meta.ts +14 -0
  73. package/dist/components/list/List.stories.tsx +963 -0
  74. package/dist/components/list/index.tsx +567 -0
  75. package/dist/components/list/meta.ts +24 -0
  76. package/dist/components/marquee/Marquee.stories.tsx +819 -0
  77. package/dist/components/marquee/index.tsx +107 -0
  78. package/dist/components/marquee/meta.ts +6 -0
  79. package/dist/components/overlay/Overlay.stories.tsx +954 -0
  80. package/dist/components/overlay/index.tsx +58 -0
  81. package/dist/components/overlay/meta.ts +10 -0
  82. package/dist/components/pagination/Pagination.stories.tsx +354 -0
  83. package/dist/components/pagination/index.tsx +455 -0
  84. package/dist/components/pagination/meta.ts +29 -0
  85. package/dist/components/popover/Popover.stories.tsx +1037 -0
  86. package/dist/components/popover/index.tsx +67 -0
  87. package/dist/components/popover/meta.ts +12 -0
  88. package/dist/components/radio/Radio.stories.tsx +146 -0
  89. package/dist/components/radio/index.tsx +41 -0
  90. package/dist/components/radio/meta.ts +19 -0
  91. package/dist/components/resizable/Resizable.stories.tsx +866 -0
  92. package/dist/components/resizable/index.tsx +55 -0
  93. package/dist/components/resizable/meta.ts +12 -0
  94. package/dist/components/scroll-area/ScrollArea.stories.tsx +1104 -0
  95. package/dist/components/scroll-area/index.tsx +55 -0
  96. package/dist/components/scroll-area/meta.ts +8 -0
  97. package/dist/components/search/Search.stories.tsx +678 -0
  98. package/dist/components/search/index.tsx +141 -0
  99. package/dist/components/search/meta.ts +6 -0
  100. package/dist/components/select/Select.stories.tsx +962 -0
  101. package/dist/components/select/index.tsx +512 -0
  102. package/dist/components/select/meta.ts +40 -0
  103. package/dist/components/sheet/Sheet.stories.tsx +1060 -0
  104. package/dist/components/sheet/index.tsx +440 -0
  105. package/dist/components/sheet/meta.ts +38 -0
  106. package/dist/components/skelton/Skelton.stories.tsx +859 -0
  107. package/dist/components/skelton/index.tsx +17 -0
  108. package/dist/components/skelton/meta.ts +6 -0
  109. package/dist/components/slider/Slider.stories.tsx +876 -0
  110. package/dist/components/slider/index.tsx +156 -0
  111. package/dist/components/slider/meta.ts +29 -0
  112. package/dist/components/stepper/Stepper.stories.tsx +639 -0
  113. package/dist/components/stepper/index.tsx +650 -0
  114. package/dist/components/stepper/meta.ts +19 -0
  115. package/dist/components/switch/Switch.stories.tsx +85 -0
  116. package/dist/components/switch/index.tsx +37 -0
  117. package/dist/components/switch/meta.ts +24 -0
  118. package/dist/components/switch-case/SwitchCase.stories.tsx +209 -0
  119. package/dist/components/switch-case/index.tsx +89 -0
  120. package/dist/components/switch-case/meta.ts +6 -0
  121. package/dist/components/table/Table.stories.tsx +1095 -0
  122. package/dist/components/table/index.tsx +113 -0
  123. package/dist/components/table/meta.ts +20 -0
  124. package/dist/components/tabs/Tabs.stories.tsx +1379 -0
  125. package/dist/components/tabs/index.tsx +186 -0
  126. package/dist/components/tabs/meta.ts +25 -0
  127. package/dist/components/tag/Tag.stories.tsx +625 -0
  128. package/dist/components/tag/index.tsx +320 -0
  129. package/dist/components/tag/meta.ts +52 -0
  130. package/dist/components/textarea/TextArea.stories.tsx +723 -0
  131. package/dist/components/textarea/index.tsx +480 -0
  132. package/dist/components/textarea/meta.ts +23 -0
  133. package/dist/components/toast/Toast.stories.tsx +1427 -0
  134. package/dist/components/toast/index.tsx +26 -0
  135. package/dist/components/toast/meta.ts +19 -0
  136. package/dist/components/toggle/Toggle.stories.tsx +1093 -0
  137. package/dist/components/toggle/index.tsx +44 -0
  138. package/dist/components/toggle/meta.ts +19 -0
  139. package/dist/components/tooltip/Tooltip.stories.tsx +1548 -0
  140. package/dist/components/tooltip/index.tsx +304 -0
  141. package/dist/components/tooltip/meta.ts +21 -0
  142. package/dist/components/typography/Typography.stories.tsx +197 -0
  143. package/dist/components/typography/index.tsx +184 -0
  144. package/dist/components/typography/meta.ts +38 -0
  145. package/dist/fonts/LabGrotesque-Regular.ttf +0 -0
  146. package/dist/fonts/LabGrotesqueTRIAL-Bold.otf +0 -0
  147. package/dist/fonts/LabGrotesqueTRIAL-Light.otf +0 -0
  148. package/dist/fonts/LabGrotesqueTRIAL-Medium.otf +0 -0
  149. package/dist/fonts/LabGrotesqueTRIAL-Regular.otf +0 -0
  150. package/dist/fonts/PPSupplySans-Regular (1).otf +0 -0
  151. package/dist/fonts/PPSupplySans-Regular.otf +0 -0
  152. package/dist/fonts/PPSupplySans-Ultralight.otf +0 -0
  153. package/dist/hooks/index.ts +3 -0
  154. package/dist/hooks/use-previous/UsePrevious.stories.tsx +997 -0
  155. package/dist/hooks/use-previous/index.ts +15 -0
  156. package/dist/hooks/use-previous/meta.ts +6 -0
  157. package/dist/hooks/use-standalone-pagination/UseStandalonePagination.stories.tsx +983 -0
  158. package/dist/hooks/use-standalone-pagination/index.ts +146 -0
  159. package/dist/hooks/use-standalone-pagination/meta.ts +6 -0
  160. package/dist/icons/Icons.stories.tsx +29 -0
  161. package/dist/icons/alert-icon/AlertIcon.stories.tsx +991 -0
  162. package/dist/icons/alert-icon/index.tsx +48 -0
  163. package/dist/icons/alert-icon/meta.ts +8 -0
  164. package/dist/icons/all-icons.tsx +738 -0
  165. package/dist/icons/angle-down-icon/AngleDownIcon.stories.tsx +1031 -0
  166. package/dist/icons/angle-down-icon/index.tsx +25 -0
  167. package/dist/icons/angle-down-icon/meta.ts +8 -0
  168. package/dist/icons/arrow-box-left-icon/ArrowBoxLeftIcon.stories.tsx +1080 -0
  169. package/dist/icons/arrow-box-left-icon/index.tsx +24 -0
  170. package/dist/icons/arrow-box-left-icon/meta.ts +8 -0
  171. package/dist/icons/arrow-right-icon/ArrowRightIcon.stories.tsx +1151 -0
  172. package/dist/icons/arrow-right-icon/index.tsx +26 -0
  173. package/dist/icons/arrow-right-icon/meta.ts +8 -0
  174. package/dist/icons/arrow-right-up-icon/ArrowRightUpIcon.stories.tsx +1273 -0
  175. package/dist/icons/arrow-right-up-icon/index.tsx +24 -0
  176. package/dist/icons/arrow-right-up-icon/meta.ts +8 -0
  177. package/dist/icons/art-board-icon/ArtBoardIcon.stories.tsx +670 -0
  178. package/dist/icons/art-board-icon/index.tsx +24 -0
  179. package/dist/icons/art-board-icon/meta.ts +7 -0
  180. package/dist/icons/audio-bar-icon/AudioBarIcon.stories.tsx +1244 -0
  181. package/dist/icons/audio-bar-icon/index.tsx +19 -0
  182. package/dist/icons/audio-bar-icon/meta.ts +8 -0
  183. package/dist/icons/bubble-check-icon/BubbleCheckIcon.stories.tsx +1239 -0
  184. package/dist/icons/bubble-check-icon/index.tsx +24 -0
  185. package/dist/icons/bubble-check-icon/meta.ts +8 -0
  186. package/dist/icons/bubble-crossed-icon/BubbleCrossedIcon.stories.tsx +1228 -0
  187. package/dist/icons/bubble-crossed-icon/index.tsx +24 -0
  188. package/dist/icons/bubble-crossed-icon/meta.ts +8 -0
  189. package/dist/icons/bubble-sparkle-icon/BubbleSparkleIcon.stories.tsx +912 -0
  190. package/dist/icons/bubble-sparkle-icon/index.tsx +26 -0
  191. package/dist/icons/bubble-sparkle-icon/meta.ts +8 -0
  192. package/dist/icons/chevron-double-left-icon/ChevronDoubleLeftIcon.stories.tsx +1021 -0
  193. package/dist/icons/chevron-double-left-icon/index.tsx +34 -0
  194. package/dist/icons/chevron-double-left-icon/meta.ts +8 -0
  195. package/dist/icons/chevron-double-right-icon/ChevronDoubleRightIcon.stories.tsx +1021 -0
  196. package/dist/icons/chevron-double-right-icon/index.tsx +34 -0
  197. package/dist/icons/chevron-double-right-icon/meta.ts +8 -0
  198. package/dist/icons/chevron-down-icon/ChevronDownIcon.stories.tsx +1001 -0
  199. package/dist/icons/chevron-down-icon/index.tsx +27 -0
  200. package/dist/icons/chevron-down-icon/meta.ts +8 -0
  201. package/dist/icons/chevron-left-icon/ChevronLeftIcon.stories.tsx +1029 -0
  202. package/dist/icons/chevron-left-icon/index.tsx +27 -0
  203. package/dist/icons/chevron-left-icon/meta.ts +8 -0
  204. package/dist/icons/chevron-right-icon/ChevronRightIcon.stories.tsx +1021 -0
  205. package/dist/icons/chevron-right-icon/index.tsx +27 -0
  206. package/dist/icons/chevron-right-icon/meta.ts +8 -0
  207. package/dist/icons/chevron-up-icon/ChevronUpIcon.stories.tsx +1036 -0
  208. package/dist/icons/chevron-up-icon/index.tsx +27 -0
  209. package/dist/icons/chevron-up-icon/meta.ts +8 -0
  210. package/dist/icons/command-icon/CommandIcon.stories.tsx +1098 -0
  211. package/dist/icons/command-icon/index.tsx +24 -0
  212. package/dist/icons/command-icon/meta.ts +8 -0
  213. package/dist/icons/cross-circle-icon/CrossCircleIcon.stories.tsx +1061 -0
  214. package/dist/icons/cross-circle-icon/index.tsx +23 -0
  215. package/dist/icons/cross-circle-icon/meta.ts +8 -0
  216. package/dist/icons/cross-icon/CrossIcon.stories.tsx +1027 -0
  217. package/dist/icons/cross-icon/index.tsx +24 -0
  218. package/dist/icons/cross-icon/meta.ts +8 -0
  219. package/dist/icons/edit-big-icon/EditBigIcon.stories.tsx +1092 -0
  220. package/dist/icons/edit-big-icon/index.tsx +22 -0
  221. package/dist/icons/edit-big-icon/meta.ts +8 -0
  222. package/dist/icons/eye-close-icon/EyeCloseIcon.stories.tsx +1090 -0
  223. package/dist/icons/eye-close-icon/index.tsx +26 -0
  224. package/dist/icons/eye-close-icon/meta.ts +8 -0
  225. package/dist/icons/eye-open-icon/EyeOpenIcon.stories.tsx +1098 -0
  226. package/dist/icons/eye-open-icon/index.tsx +24 -0
  227. package/dist/icons/eye-open-icon/meta.ts +8 -0
  228. package/dist/icons/feature-shine-icon/FeatureShineIcon.stories.tsx +1071 -0
  229. package/dist/icons/feature-shine-icon/index.tsx +29 -0
  230. package/dist/icons/feature-shine-icon/meta.ts +8 -0
  231. package/dist/icons/file-chart-icon/FileChartIcon.stories.tsx +1115 -0
  232. package/dist/icons/file-chart-icon/index.tsx +24 -0
  233. package/dist/icons/file-chart-icon/meta.ts +8 -0
  234. package/dist/icons/file-text-icon/FileTextIcon.stories.tsx +668 -0
  235. package/dist/icons/file-text-icon/index.tsx +24 -0
  236. package/dist/icons/file-text-icon/meta.ts +8 -0
  237. package/dist/icons/grip-vertical-icon/GripVerticalIcon.stories.tsx +1239 -0
  238. package/dist/icons/grip-vertical-icon/index.tsx +28 -0
  239. package/dist/icons/grip-vertical-icon/meta.ts +8 -0
  240. package/dist/icons/image-icon/ImageIcon.stories.tsx +1181 -0
  241. package/dist/icons/image-icon/index.tsx +24 -0
  242. package/dist/icons/image-icon/meta.ts +8 -0
  243. package/dist/icons/import-folder-icon/ImportFolderIcon.stories.tsx +1248 -0
  244. package/dist/icons/import-folder-icon/index.tsx +22 -0
  245. package/dist/icons/import-folder-icon/meta.ts +8 -0
  246. package/dist/icons/index.ts +46 -0
  247. package/dist/icons/light-bulb-simple-icon/LightBulbSimpleIcon.stories.tsx +1272 -0
  248. package/dist/icons/light-bulb-simple-icon/index.tsx +24 -0
  249. package/dist/icons/light-bulb-simple-icon/meta.ts +8 -0
  250. package/dist/icons/magic-book-icon/MagicBookIcon.stories.tsx +1245 -0
  251. package/dist/icons/magic-book-icon/index.tsx +32 -0
  252. package/dist/icons/magic-book-icon/meta.ts +8 -0
  253. package/dist/icons/maintenance-icon/MaintenanceIcon.stories.tsx +1251 -0
  254. package/dist/icons/maintenance-icon/index.tsx +23 -0
  255. package/dist/icons/maintenance-icon/meta.ts +8 -0
  256. package/dist/icons/message-icon/MessageIcon.stories.tsx +595 -0
  257. package/dist/icons/message-icon/index.tsx +30 -0
  258. package/dist/icons/message-icon/meta.ts +8 -0
  259. package/dist/icons/move-horizontal-icon/MoveHorizontalIcon.stories.tsx +1245 -0
  260. package/dist/icons/move-horizontal-icon/index.tsx +23 -0
  261. package/dist/icons/move-horizontal-icon/meta.ts +8 -0
  262. package/dist/icons/move-vertical-icon/MoveVerticalIcon.stories.tsx +1196 -0
  263. package/dist/icons/move-vertical-icon/index.tsx +23 -0
  264. package/dist/icons/move-vertical-icon/meta.ts +8 -0
  265. package/dist/icons/page-search-icon/PageSearchIcon.stories.tsx +1167 -0
  266. package/dist/icons/page-search-icon/index.tsx +21 -0
  267. package/dist/icons/page-search-icon/meta.ts +8 -0
  268. package/dist/icons/pencil-icon/PencilIcon.stories.tsx +1131 -0
  269. package/dist/icons/pencil-icon/index.tsx +21 -0
  270. package/dist/icons/pencil-icon/meta.ts +8 -0
  271. package/dist/icons/plus-icon/PlusIcon.stories.tsx +1151 -0
  272. package/dist/icons/plus-icon/index.tsx +24 -0
  273. package/dist/icons/plus-icon/meta.ts +8 -0
  274. package/dist/icons/search-icon/SearchIcon.stories.tsx +1181 -0
  275. package/dist/icons/search-icon/index.tsx +24 -0
  276. package/dist/icons/search-icon/meta.ts +8 -0
  277. package/dist/icons/site-logo-icon/SiteLogoIcon.stories.tsx +1167 -0
  278. package/dist/icons/site-logo-icon/index.tsx +79 -0
  279. package/dist/icons/site-logo-icon/meta.ts +8 -0
  280. package/dist/icons/spinner-gradient-icon/SpinnerGradientIcon.stories.tsx +637 -0
  281. package/dist/icons/spinner-gradient-icon/index.tsx +53 -0
  282. package/dist/icons/spinner-gradient-icon/meta.ts +8 -0
  283. package/dist/icons/spinner-solid-icon/SpinnerSolidIcon.stories.tsx +644 -0
  284. package/dist/icons/spinner-solid-icon/index.tsx +59 -0
  285. package/dist/icons/spinner-solid-icon/meta.ts +8 -0
  286. package/dist/icons/spinner-solid-neutral-icon/SpinnerSolidINeutralcon.stories.tsx +736 -0
  287. package/dist/icons/spinner-solid-neutral-icon/index.tsx +53 -0
  288. package/dist/icons/spinner-solid-neutral-icon/meta.ts +8 -0
  289. package/dist/icons/tick-circle-icon/TickCircleIcon.stories.tsx +1204 -0
  290. package/dist/icons/tick-circle-icon/index.tsx +23 -0
  291. package/dist/icons/tick-circle-icon/meta.ts +8 -0
  292. package/dist/icons/tick-icon/TickIcon.stories.tsx +1340 -0
  293. package/dist/icons/tick-icon/index.tsx +24 -0
  294. package/dist/icons/tick-icon/meta.ts +8 -0
  295. package/dist/icons/trash-icon/TrashIcon.stories.tsx +996 -0
  296. package/dist/icons/trash-icon/index.tsx +24 -0
  297. package/dist/icons/trash-icon/meta.ts +8 -0
  298. package/dist/icons/upload-icon/UploadIcon.stories.tsx +947 -0
  299. package/dist/icons/upload-icon/index.tsx +24 -0
  300. package/dist/icons/upload-icon/meta.ts +8 -0
  301. package/dist/icons/vertical-menu-icon/VerticalMenuIcon.stories.tsx +1045 -0
  302. package/dist/icons/vertical-menu-icon/index.tsx +27 -0
  303. package/dist/icons/vertical-menu-icon/meta.ts +8 -0
  304. package/dist/index.d.ts +6 -0
  305. package/dist/index.js +206 -0
  306. package/dist/lib/utils.ts +6 -0
  307. package/dist/styles/aural-theme.css +1008 -0
  308. package/package.json +142 -0
@@ -0,0 +1,997 @@
1
+ import React, { useCallback, useState } from "react"
2
+ import type { Meta, StoryObj } from "@storybook/react"
3
+
4
+ // Import the hook (assuming it exists in the index file)
5
+ import { usePrevious } from "."
6
+
7
+ // Example component that uses the hook
8
+ const PreviousValueDemo: React.FC<{
9
+ title?: string
10
+ initialValue?: number
11
+ }> = ({ title = "usePrevious Demo", initialValue = 0 }) => {
12
+ const [value, setValue] = useState(initialValue)
13
+ const [stringValue, setStringValue] = useState("Hello")
14
+ const [boolValue, setBoolValue] = useState(false)
15
+ const [objectValue, setObjectValue] = useState({ count: 0, name: "test" })
16
+ const [arrayValue, setArrayValue] = useState([1, 2, 3])
17
+
18
+ // Use the hook for different value types
19
+ const previousValue = usePrevious(value)
20
+ const previousStringValue = usePrevious(stringValue)
21
+ const previousBoolValue = usePrevious(boolValue)
22
+ const previousObjectValue = usePrevious(objectValue)
23
+ const previousArrayValue = usePrevious(arrayValue)
24
+
25
+ const incrementValue = useCallback(
26
+ () => setValue((prev: number) => prev + 1),
27
+ []
28
+ )
29
+ const decrementValue = useCallback(
30
+ () => setValue((prev: number) => prev - 1),
31
+ []
32
+ )
33
+ const randomValue = useCallback(
34
+ () => setValue(Math.floor(Math.random() * 100)),
35
+ []
36
+ )
37
+
38
+ const toggleBool = useCallback(() => setBoolValue((prev) => !prev), [])
39
+
40
+ const updateObject = useCallback(() => {
41
+ setObjectValue((prev) => ({
42
+ count: prev.count + 1,
43
+ name: `test-${prev.count + 1}`,
44
+ }))
45
+ }, [])
46
+
47
+ const addToArray = useCallback(() => {
48
+ setArrayValue((prev) => [...prev, prev.length + 1])
49
+ }, [])
50
+
51
+ const removeFromArray = useCallback(() => {
52
+ setArrayValue((prev) => prev.slice(0, -1))
53
+ }, [])
54
+
55
+ const stringOptions = ["Hello", "World", "React", "Hook", "Previous", "Value"]
56
+ const updateString = useCallback(() => {
57
+ const randomString =
58
+ stringOptions[Math.floor(Math.random() * stringOptions.length)]
59
+ setStringValue(randomString)
60
+ }, [])
61
+
62
+ return (
63
+ <div className="!space-y-6 rounded-lg border border-white/10 bg-white/5 !p-6">
64
+ <div className="!space-y-4">
65
+ <h3 className="!text-xl font-semibold !text-white">{title}</h3>
66
+
67
+ {/* Number Value Demo */}
68
+ <div className="rounded-lg border border-blue-500/20 bg-blue-500/10 !p-4">
69
+ <h4 className="!mb-3 !text-lg font-semibold !text-blue-300">
70
+ Number Value
71
+ </h4>
72
+ <div className="grid grid-cols-1 gap-4 md:grid-cols-2">
73
+ <div className="!space-y-2">
74
+ <div className="flex items-center justify-between rounded-lg bg-black/20 !p-3">
75
+ <span className="!text-sm !text-white/70">Current:</span>
76
+ <span className="!text-xl font-bold !text-blue-100">
77
+ {value}
78
+ </span>
79
+ </div>
80
+ <div className="flex items-center justify-between rounded-lg bg-black/20 !p-3">
81
+ <span className="!text-sm !text-white/70">Previous:</span>
82
+ <span className="!text-xl font-bold !text-blue-300">
83
+ {previousValue !== undefined ? previousValue : "undefined"}
84
+ </span>
85
+ </div>
86
+ </div>
87
+ <div className="!space-y-2">
88
+ <button
89
+ onClick={incrementValue}
90
+ className="w-full 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"
91
+ >
92
+ Increment (+1)
93
+ </button>
94
+ <button
95
+ onClick={decrementValue}
96
+ className="w-full 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"
97
+ >
98
+ Decrement (-1)
99
+ </button>
100
+ <button
101
+ onClick={randomValue}
102
+ className="w-full rounded-lg border border-purple-500/30 bg-purple-500/20 !px-3 !py-2 !text-sm !text-purple-300 transition-colors hover:bg-purple-500/30"
103
+ >
104
+ Random Value
105
+ </button>
106
+ </div>
107
+ </div>
108
+ </div>
109
+
110
+ {/* String Value Demo */}
111
+ <div className="rounded-lg border border-green-500/20 bg-green-500/10 !p-4">
112
+ <h4 className="!mb-3 !text-lg font-semibold !text-green-300">
113
+ String Value
114
+ </h4>
115
+ <div className="grid grid-cols-1 gap-4 md:grid-cols-2">
116
+ <div className="!space-y-2">
117
+ <div className="flex items-center justify-between rounded-lg bg-black/20 !p-3">
118
+ <span className="!text-sm !text-white/70">Current:</span>
119
+ <span className="font-mono !text-green-100">
120
+ "{stringValue}"
121
+ </span>
122
+ </div>
123
+ <div className="flex items-center justify-between rounded-lg bg-black/20 !p-3">
124
+ <span className="!text-sm !text-white/70">Previous:</span>
125
+ <span className="font-mono !text-green-300">
126
+ {previousStringValue !== undefined
127
+ ? `"${previousStringValue}"`
128
+ : "undefined"}
129
+ </span>
130
+ </div>
131
+ </div>
132
+ <div className="!space-y-2">
133
+ <button
134
+ onClick={updateString}
135
+ className="w-full 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"
136
+ >
137
+ Random String
138
+ </button>
139
+ <input
140
+ type="text"
141
+ value={stringValue}
142
+ onChange={(e) => setStringValue(e.target.value)}
143
+ className="w-full rounded-lg border border-white/20 bg-white/5 !px-3 !py-2 !text-sm !text-white placeholder-white/50"
144
+ placeholder="Type to update..."
145
+ />
146
+ </div>
147
+ </div>
148
+ </div>
149
+
150
+ {/* Boolean Value Demo */}
151
+ <div className="rounded-lg border border-purple-500/20 bg-purple-500/10 !p-4">
152
+ <h4 className="!mb-3 !text-lg font-semibold !text-purple-300">
153
+ Boolean Value
154
+ </h4>
155
+ <div className="grid grid-cols-1 gap-4 md:grid-cols-2">
156
+ <div className="!space-y-2">
157
+ <div className="flex items-center justify-between rounded-lg bg-black/20 !p-3">
158
+ <span className="!text-sm !text-white/70">Current:</span>
159
+ <span
160
+ className={`font-mono !text-lg font-bold ${boolValue ? "!text-green-300" : "!text-red-300"}`}
161
+ >
162
+ {String(boolValue)}
163
+ </span>
164
+ </div>
165
+ <div className="flex items-center justify-between rounded-lg bg-black/20 !p-3">
166
+ <span className="!text-sm !text-white/70">Previous:</span>
167
+ <span className="font-mono !text-purple-300">
168
+ {previousBoolValue !== undefined
169
+ ? String(previousBoolValue)
170
+ : "undefined"}
171
+ </span>
172
+ </div>
173
+ </div>
174
+ <div>
175
+ <button
176
+ onClick={toggleBool}
177
+ className={`w-full rounded-lg border !px-4 !py-3 !text-sm font-medium transition-colors ${
178
+ boolValue
179
+ ? "border-green-500/30 bg-green-500/20 !text-green-300 hover:bg-green-500/30"
180
+ : "border-red-500/30 bg-red-500/20 !text-red-300 hover:bg-red-500/30"
181
+ }`}
182
+ >
183
+ Toggle: {boolValue ? "ON" : "OFF"}
184
+ </button>
185
+ </div>
186
+ </div>
187
+ </div>
188
+
189
+ {/* Object Value Demo */}
190
+ <div className="rounded-lg border border-orange-500/20 bg-orange-500/10 !p-4">
191
+ <h4 className="!mb-3 !text-lg font-semibold !text-orange-300">
192
+ Object Value
193
+ </h4>
194
+ <div className="grid grid-cols-1 gap-4 md:grid-cols-2">
195
+ <div className="!space-y-2">
196
+ <div className="rounded-lg bg-black/20 !p-3">
197
+ <div className="!mb-1 !text-sm !text-white/70">Current:</div>
198
+ <pre className="overflow-x-auto !text-xs !text-orange-100">
199
+ {JSON.stringify(objectValue, null, 2)}
200
+ </pre>
201
+ </div>
202
+ <div className="rounded-lg bg-black/20 !p-3">
203
+ <div className="!mb-1 !text-sm !text-white/70">Previous:</div>
204
+ <pre className="overflow-x-auto !text-xs !text-orange-300">
205
+ {previousObjectValue !== undefined
206
+ ? JSON.stringify(previousObjectValue, null, 2)
207
+ : "undefined"}
208
+ </pre>
209
+ </div>
210
+ </div>
211
+ <div>
212
+ <button
213
+ onClick={updateObject}
214
+ className="w-full rounded-lg border border-orange-500/30 bg-orange-500/20 !px-3 !py-2 !text-sm !text-orange-300 transition-colors hover:bg-orange-500/30"
215
+ >
216
+ Update Object
217
+ </button>
218
+ </div>
219
+ </div>
220
+ </div>
221
+
222
+ {/* Array Value Demo */}
223
+ <div className="rounded-lg border border-cyan-500/20 bg-cyan-500/10 !p-4">
224
+ <h4 className="!mb-3 !text-lg font-semibold !text-cyan-300">
225
+ Array Value
226
+ </h4>
227
+ <div className="grid grid-cols-1 gap-4 md:grid-cols-2">
228
+ <div className="!space-y-2">
229
+ <div className="rounded-lg bg-black/20 !p-3">
230
+ <div className="!mb-1 !text-sm !text-white/70">Current:</div>
231
+ <div className="font-mono !text-cyan-100">
232
+ [{arrayValue.join(", ")}]
233
+ </div>
234
+ <div className="!mt-1 !text-xs !text-white/50">
235
+ Length: {arrayValue.length}
236
+ </div>
237
+ </div>
238
+ <div className="rounded-lg bg-black/20 !p-3">
239
+ <div className="!mb-1 !text-sm !text-white/70">Previous:</div>
240
+ <div className="font-mono !text-cyan-300">
241
+ {previousArrayValue !== undefined
242
+ ? `[${previousArrayValue.join(", ")}]`
243
+ : "undefined"}
244
+ </div>
245
+ <div className="!mt-1 !text-xs !text-white/50">
246
+ Length:{" "}
247
+ {previousArrayValue ? previousArrayValue.length : "N/A"}
248
+ </div>
249
+ </div>
250
+ </div>
251
+ <div className="!space-y-2">
252
+ <button
253
+ onClick={addToArray}
254
+ className="w-full rounded-lg border border-cyan-500/30 bg-cyan-500/20 !px-3 !py-2 !text-sm !text-cyan-300 transition-colors hover:bg-cyan-500/30"
255
+ >
256
+ Add Item
257
+ </button>
258
+ <button
259
+ onClick={removeFromArray}
260
+ disabled={arrayValue.length === 0}
261
+ className="w-full 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 disabled:cursor-not-allowed disabled:opacity-50"
262
+ >
263
+ Remove Item
264
+ </button>
265
+ </div>
266
+ </div>
267
+ </div>
268
+
269
+ {/* Usage Information */}
270
+ <div className="rounded-lg border border-white/10 bg-black/20 !p-4">
271
+ <h4 className="!mb-3 !text-lg font-semibold !text-white">
272
+ Hook Usage
273
+ </h4>
274
+ <div className="!space-y-2 !text-sm">
275
+ <div className="!text-white/70">
276
+ <span className="font-medium !text-white">Pattern:</span> const
277
+ previousValue = usePrevious(currentValue)
278
+ </div>
279
+ <div className="!text-white/70">
280
+ <span className="font-medium !text-white">Returns:</span> The
281
+ previous value from the last render, or undefined on first render
282
+ </div>
283
+ <div className="!text-white/70">
284
+ <span className="font-medium !text-white">Use cases:</span>{" "}
285
+ Comparing values, animations, tracking changes, debugging
286
+ </div>
287
+ </div>
288
+ </div>
289
+ </div>
290
+ </div>
291
+ )
292
+ }
293
+
294
+ const meta: Meta<typeof PreviousValueDemo> = {
295
+ title: "Hooks/usePrevious",
296
+ component: PreviousValueDemo,
297
+ parameters: {
298
+ layout: "fullscreen",
299
+ backgrounds: {
300
+ default: "dark",
301
+ values: [
302
+ { name: "dark", value: "#0a0a0a" },
303
+ { name: "darker", value: "#000000" },
304
+ { name: "light", value: "#ffffff" },
305
+ ],
306
+ },
307
+ docs: {
308
+ page: () => (
309
+ <>
310
+ {/* Override default docs styling */}
311
+ <style>
312
+ {`
313
+ .sbdocs-wrapper {
314
+ padding: 0 !important;
315
+ max-width: none !important;
316
+ background: transparent !important;
317
+ }
318
+ .sbdocs-content {
319
+ max-width: none !important;
320
+ padding: 0 !important;
321
+ margin: 0 !important;
322
+ background: transparent !important;
323
+ }
324
+ .docs-story {
325
+ background: transparent !important;
326
+ }
327
+ .sbdocs {
328
+ background: transparent !important;
329
+ }
330
+ body {
331
+ background: #0a0a0a !important;
332
+ }
333
+ #storybook-docs {
334
+ background: #0a0a0a !important;
335
+ }
336
+ .sbdocs-preview {
337
+ background: transparent !important;
338
+ border: none !important;
339
+ }
340
+ .sbdocs-h1, .sbdocs-h2, .sbdocs-h3, .sbdocs-h4, .sbdocs-h5, .sbdocs-h6 {
341
+ color: white !important;
342
+ }
343
+ .sbdocs-p, .sbdocs-li {
344
+ color: rgba(255, 255, 255, 0.7) !important;
345
+ }
346
+ .sbdocs-code {
347
+ background: rgba(255, 255, 255, 0.1) !important;
348
+ color: rgba(168, 85, 247, 1) !important;
349
+ border: 1px solid rgba(255, 255, 255, 0.1) !important;
350
+ }
351
+ .sbdocs-pre {
352
+ background: rgba(0, 0, 0, 0.4) !important;
353
+ border: 1px solid rgba(255, 255, 255, 0.1) !important;
354
+ }
355
+ .sbdocs-table {
356
+ background: rgba(255, 255, 255, 0.05) !important;
357
+ border: 1px solid rgba(255, 255, 255, 0.1) !important;
358
+ }
359
+ .sbdocs-table th {
360
+ background: rgba(255, 255, 255, 0.05) !important;
361
+ color: white !important;
362
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1) !important;
363
+ }
364
+ .sbdocs-table td {
365
+ color: rgba(255, 255, 255, 0.7) !important;
366
+ border-bottom: 1px solid rgba(255, 255, 255, 0.05) !important;
367
+ }
368
+ `}
369
+ </style>
370
+
371
+ <div className="min-h-screen bg-gradient-to-br from-gray-900 via-purple-900/20 to-gray-900">
372
+ {/* Header */}
373
+ <div className="relative overflow-hidden border-b border-white/10 bg-black/20 backdrop-blur-xl">
374
+ <div className="absolute inset-0 bg-gradient-to-r from-purple-500/10 via-transparent to-indigo-500/10" />
375
+ <div className="relative !mx-auto max-w-7xl !px-6 !py-16">
376
+ <div className="!space-y-6 text-center">
377
+ <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">
378
+ <span className="!text-4xl">⏮️</span>
379
+ </div>
380
+ <h1 className="!text-fm-primary !text-5xl font-bold">
381
+ usePrevious
382
+ </h1>
383
+ <p className="!mx-auto max-w-3xl !text-xl leading-relaxed !text-white/70">
384
+ A simple yet powerful React hook that captures and returns
385
+ the previous value of any variable. Essential for tracking
386
+ changes, implementing smooth transitions, and debugging
387
+ state updates.
388
+ </p>
389
+
390
+ {/* Stats */}
391
+ <div className="flex items-center justify-center gap-8 !pt-8">
392
+ <div className="text-center">
393
+ <div className="!text-3xl font-bold !text-purple-300">
394
+ Simple
395
+ </div>
396
+ <div className="!text-sm !text-white/60">
397
+ Easy to use API
398
+ </div>
399
+ </div>
400
+ <div className="!h-8 !w-px bg-white/20" />
401
+ <div className="text-center">
402
+ <div className="!text-3xl font-bold !text-indigo-300">
403
+ Universal
404
+ </div>
405
+ <div className="!text-sm !text-white/60">
406
+ Works with any type
407
+ </div>
408
+ </div>
409
+ <div className="!h-8 !w-px bg-white/20" />
410
+ <div className="text-center">
411
+ <div className="!text-3xl font-bold !text-cyan-300">
412
+ Lightweight
413
+ </div>
414
+ <div className="!text-sm !text-white/60">
415
+ Minimal overhead
416
+ </div>
417
+ </div>
418
+ </div>
419
+ </div>
420
+ </div>
421
+ </div>
422
+
423
+ {/* Content */}
424
+ <div className="!mx-auto max-w-7xl !space-y-16 !px-6 !py-12">
425
+ {/* Quick Usage */}
426
+ <div className="!space-y-8">
427
+ <h2 className="text-center !text-3xl font-bold !text-white">
428
+ Quick Start
429
+ </h2>
430
+ <div className="grid grid-cols-1 gap-8 lg:grid-cols-2">
431
+ <div className="!space-y-4">
432
+ <h3 className="!text-xl font-semibold !text-purple-300">
433
+ Basic Usage
434
+ </h3>
435
+ <div className="rounded-lg bg-black/40 !p-4">
436
+ <pre className="overflow-x-auto !text-sm !text-green-300">
437
+ {`import { usePrevious } from "@hooks/use-previous"
438
+
439
+ function MyComponent() {
440
+ const [count, setCount] = useState(0)
441
+ const [name, setName] = useState("John")
442
+
443
+ // Get previous values
444
+ const previousCount = usePrevious(count)
445
+ const previousName = usePrevious(name)
446
+
447
+ return (
448
+ <div>
449
+ <div>
450
+ Count: {count} (was: {previousCount ?? "N/A"})
451
+ </div>
452
+ <div>
453
+ Name: {name} (was: {previousName ?? "N/A"})
454
+ </div>
455
+
456
+ <button onClick={() => setCount(c => c + 1)}>
457
+ Increment
458
+ </button>
459
+ <button onClick={() => setName("Jane")}>
460
+ Change Name
461
+ </button>
462
+ </div>
463
+ )
464
+ }`}
465
+ </pre>
466
+ </div>
467
+ </div>
468
+
469
+ <div className="!space-y-4">
470
+ <h3 className="!text-xl font-semibold !text-purple-300">
471
+ Hook Properties
472
+ </h3>
473
+ <div className="!space-y-3 rounded-lg border border-white/10 bg-white/5 !p-4">
474
+ <div className="flex justify-between">
475
+ <span className="!text-sm !text-white/70">Input:</span>
476
+ <span className="!text-sm font-medium !text-white">
477
+ Any value
478
+ </span>
479
+ </div>
480
+ <div className="flex justify-between">
481
+ <span className="!text-sm !text-white/70">Output:</span>
482
+ <span className="!text-sm font-medium !text-white">
483
+ Previous value
484
+ </span>
485
+ </div>
486
+ <div className="flex justify-between">
487
+ <span className="!text-sm !text-white/70">
488
+ First render:
489
+ </span>
490
+ <span className="!text-sm font-medium !text-white">
491
+ undefined
492
+ </span>
493
+ </div>
494
+ <div className="flex justify-between">
495
+ <span className="!text-sm !text-white/70">Memory:</span>
496
+ <span className="!text-sm font-medium !text-white">
497
+ One reference
498
+ </span>
499
+ </div>
500
+ </div>
501
+ </div>
502
+ </div>
503
+ </div>
504
+
505
+ {/* API Documentation */}
506
+ <div className="!space-y-8">
507
+ <h2 className="text-center !text-3xl font-bold !text-white">
508
+ API Reference
509
+ </h2>
510
+
511
+ <div className="overflow-hidden rounded-lg border border-white/10 bg-white/5">
512
+ <div className="bg-white/5 !p-4">
513
+ <h3 className="!text-xl font-semibold !text-white">
514
+ Hook Signature
515
+ </h3>
516
+ </div>
517
+ <div className="!p-6">
518
+ <div className="rounded-lg bg-black/40 !p-4">
519
+ <pre className="!text-sm !text-green-300">
520
+ {`function usePrevious<T>(value: T): T | undefined`}
521
+ </pre>
522
+ </div>
523
+ </div>
524
+ </div>
525
+
526
+ <div className="overflow-hidden rounded-lg border border-white/10 bg-white/5">
527
+ <div className="bg-white/5 !p-4">
528
+ <h3 className="!text-xl font-semibold !text-white">
529
+ Parameters & Return
530
+ </h3>
531
+ </div>
532
+ <table className="!my-0 w-full">
533
+ <thead className="bg-white/5">
534
+ <tr className="border-b border-white/10">
535
+ <th className="!px-6 !py-4 text-left !text-sm font-semibold !text-white">
536
+ Parameter
537
+ </th>
538
+ <th className="!px-6 !py-4 text-left !text-sm font-semibold !text-white">
539
+ Type
540
+ </th>
541
+ <th className="!px-6 !py-4 text-left !text-sm font-semibold !text-white">
542
+ Description
543
+ </th>
544
+ </tr>
545
+ </thead>
546
+ <tbody>
547
+ <tr className="border-b border-white/5">
548
+ <td className="!px-6 !py-4 font-mono !text-sm !text-purple-300">
549
+ value
550
+ </td>
551
+ <td className="!px-6 !py-4 !text-sm !text-white/70">
552
+ T (generic)
553
+ </td>
554
+ <td className="!px-6 !py-4 !text-sm !text-white/70">
555
+ The current value to track
556
+ </td>
557
+ </tr>
558
+ <tr className="!bg-black/10">
559
+ <td className="!px-6 !py-4 font-mono !text-sm !text-purple-300">
560
+ returns
561
+ </td>
562
+ <td className="!px-6 !py-4 !text-sm !text-white/70">
563
+ T | undefined
564
+ </td>
565
+ <td className="!px-6 !py-4 !text-sm !text-white/70">
566
+ Previous value from last render, undefined on first
567
+ render
568
+ </td>
569
+ </tr>
570
+ </tbody>
571
+ </table>
572
+ </div>
573
+ </div>
574
+
575
+ {/* Use Cases */}
576
+ <div className="!space-y-8">
577
+ <h2 className="text-center !text-3xl font-bold !text-white">
578
+ Common Use Cases
579
+ </h2>
580
+ <div className="grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3">
581
+ <div className="rounded-lg border border-white/10 bg-white/5 !p-6">
582
+ <div className="!mb-4 flex !h-12 !w-12 items-center justify-center rounded-lg bg-blue-500/20">
583
+ <span className="!text-2xl">📊</span>
584
+ </div>
585
+ <h3 className="!mb-2 !text-lg font-semibold !text-white">
586
+ Value Comparison
587
+ </h3>
588
+ <p className="!text-sm !text-white/70">
589
+ Compare current and previous values to detect changes and
590
+ trigger side effects.
591
+ </p>
592
+ </div>
593
+
594
+ <div className="rounded-lg border border-white/10 bg-white/5 !p-6">
595
+ <div className="!mb-4 flex !h-12 !w-12 items-center justify-center rounded-lg bg-green-500/20">
596
+ <span className="!text-2xl">🎬</span>
597
+ </div>
598
+ <h3 className="!mb-2 !text-lg font-semibold !text-white">
599
+ Animations
600
+ </h3>
601
+ <p className="!text-sm !text-white/70">
602
+ Create smooth transitions by animating between previous
603
+ and current values.
604
+ </p>
605
+ </div>
606
+
607
+ <div className="rounded-lg border border-white/10 bg-white/5 !p-6">
608
+ <div className="!mb-4 flex !h-12 !w-12 items-center justify-center rounded-lg bg-purple-500/20">
609
+ <span className="!text-2xl">🐛</span>
610
+ </div>
611
+ <h3 className="!mb-2 !text-lg font-semibold !text-white">
612
+ Debugging
613
+ </h3>
614
+ <p className="!text-sm !text-white/70">
615
+ Track state changes during development to understand
616
+ component behavior.
617
+ </p>
618
+ </div>
619
+
620
+ <div className="rounded-lg border border-white/10 bg-white/5 !p-6">
621
+ <div className="!mb-4 flex !h-12 !w-12 items-center justify-center rounded-lg bg-orange-500/20">
622
+ <span className="!text-2xl">🔄</span>
623
+ </div>
624
+ <h3 className="!mb-2 !text-lg font-semibold !text-white">
625
+ Undo/Redo
626
+ </h3>
627
+ <p className="!text-sm !text-white/70">
628
+ Implement simple undo functionality by maintaining
629
+ previous state values.
630
+ </p>
631
+ </div>
632
+
633
+ <div className="rounded-lg border border-white/10 bg-white/5 !p-6">
634
+ <div className="!mb-4 flex !h-12 !w-12 items-center justify-center rounded-lg bg-cyan-500/20">
635
+ <span className="!text-2xl">📈</span>
636
+ </div>
637
+ <h3 className="!mb-2 !text-lg font-semibold !text-white">
638
+ Change Tracking
639
+ </h3>
640
+ <p className="!text-sm !text-white/70">
641
+ Monitor value changes for analytics, logging, or
642
+ validation purposes.
643
+ </p>
644
+ </div>
645
+
646
+ <div className="rounded-lg border border-white/10 bg-white/5 !p-6">
647
+ <div className="!mb-4 flex !h-12 !w-12 items-center justify-center rounded-lg bg-pink-500/20">
648
+ <span className="!text-2xl">⚡</span>
649
+ </div>
650
+ <h3 className="!mb-2 !text-lg font-semibold !text-white">
651
+ Performance
652
+ </h3>
653
+ <p className="!text-sm !text-white/70">
654
+ Optimize renders by comparing previous values before
655
+ expensive operations.
656
+ </p>
657
+ </div>
658
+ </div>
659
+ </div>
660
+
661
+ {/* Usage Patterns */}
662
+ <div className="!space-y-8">
663
+ <h2 className="text-center !text-3xl font-bold !text-white">
664
+ Usage Patterns
665
+ </h2>
666
+ <div className="grid grid-cols-1 gap-8 lg:grid-cols-2">
667
+ <div className="!space-y-4">
668
+ <h3 className="!text-xl font-semibold !text-purple-300">
669
+ Conditional Effects
670
+ </h3>
671
+ <div className="rounded-lg bg-black/40 !p-4">
672
+ <pre className="overflow-x-auto !text-sm !text-green-300">
673
+ {`function UserProfile({ userId }) {
674
+ const [user, setUser] = useState(null)
675
+ const previousUserId = usePrevious(userId)
676
+
677
+ useEffect(() => {
678
+ // Only fetch if userId actually changed
679
+ if (userId !== previousUserId) {
680
+ fetchUser(userId).then(setUser)
681
+ }
682
+ }, [userId, previousUserId])
683
+
684
+ return <div>{user?.name}</div>
685
+ }`}
686
+ </pre>
687
+ </div>
688
+ </div>
689
+
690
+ <div className="!space-y-4">
691
+ <h3 className="!text-xl font-semibold !text-purple-300">
692
+ Animation Transitions
693
+ </h3>
694
+ <div className="rounded-lg bg-black/40 !p-4">
695
+ <pre className="overflow-x-auto !text-sm !text-green-300">
696
+ {`function AnimatedCounter({ value }) {
697
+ const previousValue = usePrevious(value)
698
+ const [displayValue, setDisplayValue] = useState(value)
699
+
700
+ useEffect(() => {
701
+ if (previousValue !== undefined) {
702
+ // Animate from previous to current
703
+ animateValue(previousValue, value, setDisplayValue)
704
+ }
705
+ }, [value, previousValue])
706
+
707
+ return <span>{displayValue}</span>
708
+ }`}
709
+ </pre>
710
+ </div>
711
+ </div>
712
+
713
+ <div className="!space-y-4">
714
+ <h3 className="!text-xl font-semibold !text-purple-300">
715
+ Change Detection
716
+ </h3>
717
+ <div className="rounded-lg bg-black/40 !p-4">
718
+ <pre className="overflow-x-auto !text-sm !text-green-300">
719
+ {`function FormField({ value, onChange }) {
720
+ const previousValue = usePrevious(value)
721
+ const [hasChanged, setHasChanged] = useState(false)
722
+
723
+ useEffect(() => {
724
+ if (previousValue !== undefined &&
725
+ previousValue !== value) {
726
+ setHasChanged(true)
727
+ // Log change for analytics
728
+ logFieldChange(previousValue, value)
729
+ }
730
+ }, [value, previousValue])
731
+
732
+ return (
733
+ <input
734
+ value={value}
735
+ onChange={onChange}
736
+ className={hasChanged ? 'changed' : ''}
737
+ />
738
+ )
739
+ }`}
740
+ </pre>
741
+ </div>
742
+ </div>
743
+
744
+ <div className="!space-y-4">
745
+ <h3 className="!text-xl font-semibold !text-purple-300">
746
+ State Validation
747
+ </h3>
748
+ <div className="rounded-lg bg-black/40 !p-4">
749
+ <pre className="overflow-x-auto !text-sm !text-green-300">
750
+ {`function DataTable({ sortColumn, sortDirection }) {
751
+ const [data, setData] = useState([])
752
+ const previousSort = usePrevious({
753
+ column: sortColumn,
754
+ direction: sortDirection
755
+ })
756
+
757
+ useEffect(() => {
758
+ const currentSort = {
759
+ column: sortColumn,
760
+ direction: sortDirection
761
+ }
762
+
763
+ // Only re-sort if sort config changed
764
+ if (!isEqual(currentSort, previousSort)) {
765
+ const sorted = sortData(data, currentSort)
766
+ setData(sorted)
767
+ }
768
+ }, [sortColumn, sortDirection, previousSort])
769
+
770
+ return <Table data={data} />
771
+ }`}
772
+ </pre>
773
+ </div>
774
+ </div>
775
+ </div>
776
+ </div>
777
+
778
+ {/* Implementation Details */}
779
+ <div className="!space-y-8">
780
+ <h2 className="text-center !text-3xl font-bold !text-white">
781
+ Implementation
782
+ </h2>
783
+ <div className="rounded-lg border border-white/10 bg-white/5 !p-6">
784
+ <h3 className="!mb-4 !text-xl font-semibold !text-white">
785
+ Hook Implementation
786
+ </h3>
787
+ <div className="rounded-lg bg-black/40 !p-4">
788
+ <pre className="overflow-x-auto !text-sm !text-blue-300">
789
+ {`import { useRef, useEffect } from 'react'
790
+
791
+ function usePrevious<T>(value: T): T | undefined {
792
+ const ref = useRef<T>()
793
+
794
+ useEffect(() => {
795
+ ref.current = value
796
+ })
797
+
798
+ return ref.current
799
+ }
800
+
801
+ export { usePrevious }`}
802
+ </pre>
803
+ </div>
804
+ <div className="!mt-4 !space-y-2 !text-sm !text-white/70">
805
+ <p className="!text-white">
806
+ The hook uses a{" "}
807
+ <code className="!text-purple-300">useRef</code> to store
808
+ the previous value and{" "}
809
+ <code className="!text-purple-300">useEffect</code> to
810
+ update it after each render.
811
+ </p>
812
+ <p className="!text-white">
813
+ Since <code className="!text-purple-300">useEffect</code>{" "}
814
+ runs after the render, the ref contains the value from the
815
+ previous render cycle.
816
+ </p>
817
+ </div>
818
+ </div>
819
+ </div>
820
+ </div>
821
+
822
+ {/* Footer */}
823
+ <div className="border-t border-white/10 bg-black/20 backdrop-blur-xl">
824
+ <div className="!mx-auto max-w-7xl !px-6 !py-8">
825
+ <div className="!space-y-4 text-center">
826
+ <p className="!text-white/60">
827
+ usePrevious is a fundamental hook that enables powerful
828
+ patterns for state management, animations, and performance
829
+ optimizations in React applications.
830
+ </p>
831
+ <p className="!text-sm !text-white/40">
832
+ Simple, reliable, and type-safe - perfect for tracking any
833
+ value changes across component re-renders.
834
+ </p>
835
+ </div>
836
+ </div>
837
+ </div>
838
+ </div>
839
+ </>
840
+ ),
841
+ },
842
+ },
843
+ tags: ["autodocs"],
844
+ argTypes: {
845
+ title: {
846
+ control: "text",
847
+ description: "Title for the demo component",
848
+ },
849
+ initialValue: {
850
+ control: { type: "number" },
851
+ description: "Initial value for the demo",
852
+ },
853
+ },
854
+ }
855
+
856
+ export default meta
857
+ type Story = StoryObj<typeof PreviousValueDemo>
858
+
859
+ // Story parameters for consistent dark theme
860
+ const storyParameters = {
861
+ backgrounds: {
862
+ default: "dark",
863
+ values: [
864
+ { name: "dark", value: "#0a0a0a" },
865
+ { name: "darker", value: "#000000" },
866
+ ],
867
+ },
868
+ }
869
+
870
+ export const Default: Story = {
871
+ args: {
872
+ title: "usePrevious Hook Demo",
873
+ initialValue: 0,
874
+ },
875
+ parameters: storyParameters,
876
+ render: (args) => (
877
+ <div className="min-h-dvh bg-gradient-to-br from-gray-900 to-gray-800 !p-8">
878
+ <PreviousValueDemo {...args} />
879
+ </div>
880
+ ),
881
+ }
882
+
883
+ export const NumberTracking: Story = {
884
+ args: {
885
+ title: "Number Value Tracking",
886
+ initialValue: 42,
887
+ },
888
+ parameters: {
889
+ ...storyParameters,
890
+ docs: {
891
+ description: {
892
+ story:
893
+ "Demonstrates tracking numeric values with the usePrevious hook.",
894
+ },
895
+ },
896
+ },
897
+ render: (args) => (
898
+ <div className="min-h-dvh bg-gradient-to-br from-gray-900 to-gray-800 !p-8">
899
+ <PreviousValueDemo {...args} />
900
+ </div>
901
+ ),
902
+ }
903
+
904
+ export const TypeVariations: Story = {
905
+ args: {
906
+ title: "Multiple Data Types",
907
+ initialValue: 100,
908
+ },
909
+ parameters: {
910
+ ...storyParameters,
911
+ docs: {
912
+ description: {
913
+ story:
914
+ "Shows how usePrevious works with different data types: numbers, strings, booleans, objects, and arrays.",
915
+ },
916
+ },
917
+ },
918
+ render: (args) => (
919
+ <div className="min-h-dvh bg-gradient-to-br from-gray-900 to-gray-800 !p-8">
920
+ <PreviousValueDemo {...args} />
921
+ </div>
922
+ ),
923
+ }
924
+
925
+ export const InteractiveDemo: Story = {
926
+ args: {
927
+ title: "Interactive Previous Value Demo",
928
+ initialValue: 0,
929
+ },
930
+ parameters: {
931
+ ...storyParameters,
932
+ docs: {
933
+ description: {
934
+ story:
935
+ "Full interactive demo showing the usePrevious hook with real-time value tracking across different data types.",
936
+ },
937
+ },
938
+ },
939
+ render: (args) => (
940
+ <div className="min-h-dvh bg-gradient-to-br from-gray-900 to-gray-800 !p-8">
941
+ <div className="!space-y-8">
942
+ <PreviousValueDemo {...args} />
943
+
944
+ {/* Additional Usage Example */}
945
+ <div className="rounded-lg border border-white/10 bg-white/5 !p-6">
946
+ <h3 className="!mb-4 !text-xl font-semibold !text-white">
947
+ Real-world Example
948
+ </h3>
949
+ <div className="rounded-lg bg-black/40 !p-4">
950
+ <pre className="overflow-x-auto !text-sm !text-blue-300">
951
+ {`// Compare with previous props to avoid unnecessary API calls
952
+ function UserDetails({ userId }) {
953
+ const [user, setUser] = useState(null)
954
+ const [loading, setLoading] = useState(false)
955
+ const previousUserId = usePrevious(userId)
956
+
957
+ useEffect(() => {
958
+ // Only fetch if userId changed (not on every render)
959
+ if (userId && userId !== previousUserId) {
960
+ setLoading(true)
961
+ fetchUserById(userId)
962
+ .then(setUser)
963
+ .finally(() => setLoading(false))
964
+ }
965
+ }, [userId, previousUserId])
966
+
967
+ if (loading) return <div>Loading...</div>
968
+ return <div>{user?.name}</div>
969
+ }`}
970
+ </pre>
971
+ </div>
972
+ </div>
973
+ </div>
974
+ </div>
975
+ ),
976
+ }
977
+
978
+ export const Playground: Story = {
979
+ parameters: {
980
+ ...storyParameters,
981
+ docs: {
982
+ description: {
983
+ story:
984
+ "Interactive playground to experiment with the usePrevious hook.",
985
+ },
986
+ },
987
+ },
988
+ args: {
989
+ title: "usePrevious Playground",
990
+ initialValue: 50,
991
+ },
992
+ render: (args) => (
993
+ <div className="min-h-dvh bg-gradient-to-br from-gray-900 to-gray-800 !p-8">
994
+ <PreviousValueDemo {...args} />
995
+ </div>
996
+ ),
997
+ }