@shohojdhara/atomix 0.1.16 → 0.1.18

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 (525) hide show
  1. package/CONTRIBUTING.md +151 -0
  2. package/NEXTJS_INTEGRATION.md +358 -0
  3. package/README.md +168 -119
  4. package/babel.config.js +58 -0
  5. package/css.d.ts +10 -0
  6. package/dist/css/atomix.css +1 -2
  7. package/dist/js/194.js +1 -2
  8. package/dist/js/244.js +1 -0
  9. package/dist/js/atomix.react.cjs.js +1 -0
  10. package/dist/js/atomix.react.esm.js +1 -2
  11. package/dist/js/atomix.react.umd.js +1 -2
  12. package/dist/js/chunks/cjs/202.9d3b1ef1eaa0d5c8a309.js +1 -0
  13. package/dist/js/chunks/cjs/308.6ea9685ea38ead4120d0.js +1 -0
  14. package/dist/js/chunks/cjs/54.73db6922594e421ba6b1.js +1 -0
  15. package/dist/js/chunks/cjs/619.51feecaadcab819780d4.js +1 -0
  16. package/dist/js/chunks/cjs/690.90f6d11164081cbcbc4d.js +1 -0
  17. package/dist/js/chunks/cjs/894.24877561df336a8dfd14.js +1 -0
  18. package/dist/js/chunks/cjs/897.6c2a71fae95338890de7.js +1 -0
  19. package/dist/js/chunks/esm/{202.ff48d27672233280e021.js → 202.82aa7b3244e53c9edb72.js} +1 -2
  20. package/dist/js/chunks/esm/{308.f873332126eba90e5c62.js → 308.27e1e4005705ae320432.js} +1 -2
  21. package/dist/js/chunks/esm/{54.4db919e5e4e5cc6d7c72.js → 54.ece1fd6964f98d4d994f.js} +1 -2
  22. package/dist/js/chunks/esm/{619.afc5a718eff77fa423b5.js → 619.ebeb0298432a066ac05c.js} +1 -2
  23. package/dist/js/chunks/esm/{690.a9e968c7497d61e56cdc.js → 690.c5f412cc979b55740359.js} +1 -2
  24. package/dist/js/chunks/esm/{894.f1091a4a8758c26d29e4.js → 894.3604ddc9367d75191198.js} +1 -2
  25. package/dist/js/chunks/esm/{897.561a50f7d043d42169da.js → 897.a4aab2fad9401693eb12.js} +1 -2
  26. package/dist/js/chunks/umd/{202.dac7605cc555b6bda542.js → 202.5017dd0403d696bf1644.js} +1 -2
  27. package/dist/js/chunks/umd/{308.6709979849dcbdb90c9b.js → 308.4bc14b9d7b16b6ee0ab8.js} +1 -2
  28. package/dist/js/chunks/umd/{54.403470e1f7d0ef4424a7.js → 54.7fdfb4a031989470a70d.js} +1 -2
  29. package/dist/js/chunks/umd/{619.fa05ea98c10270eb64c5.js → 619.84a0c35ecee695250085.js} +1 -2
  30. package/dist/js/chunks/umd/{690.aa7054d1c53e5402c2d6.js → 690.d7041094a34a4a434be2.js} +1 -2
  31. package/dist/js/chunks/umd/{894.3e1eaf0a2aadf4434390.js → 894.c127ee4e9513c22ee97d.js} +1 -2
  32. package/dist/js/chunks/umd/{897.554ea37be4453698c167.js → 897.26932ac837a39fc91907.js} +1 -2
  33. package/dist/types/components/Badge/index.d.ts +3 -3
  34. package/dist/types/components/{Navbar → Navigation/Menu}/MegaMenu.d.ts +1 -1
  35. package/dist/types/components/{Navbar → Navigation/Menu}/Menu.d.ts +1 -1
  36. package/dist/types/components/Navigation/Nav/Nav.d.ts +20 -0
  37. package/dist/types/components/{Navbar → Navigation/Nav}/NavDropdown.d.ts +1 -1
  38. package/dist/types/components/Navigation/Nav/NavItem.d.ts +33 -0
  39. package/dist/types/components/Navigation/Navbar/Navbar.d.ts +19 -0
  40. package/dist/types/components/Navigation/SideMenu/SideMenu.d.ts +20 -0
  41. package/dist/types/components/Navigation/SideMenu/SideMenuItem.d.ts +30 -0
  42. package/dist/types/components/Navigation/SideMenu/SideMenuList.d.ts +17 -0
  43. package/dist/types/components/Navigation/index.d.ts +10 -0
  44. package/dist/types/components/PhotoViewer/PhotoViewer.d.ts +2 -2
  45. package/dist/types/components/PhotoViewer/PhotoViewerHeader.d.ts +2 -2
  46. package/dist/types/components/Tab/index.d.ts +2 -2
  47. package/dist/types/components/Toggle/index.d.ts +2 -2
  48. package/dist/types/components/Tooltip/index.d.ts +3 -3
  49. package/dist/types/components/index.d.ts +1 -1
  50. package/dist/types/lib/composables/index.d.ts +1 -0
  51. package/dist/types/lib/composables/useDatePicker.d.ts +1 -1
  52. package/dist/types/lib/composables/useDropdown.d.ts +1 -1
  53. package/dist/types/lib/composables/useModal.d.ts +1 -1
  54. package/dist/types/lib/composables/usePhotoViewer.d.ts +1 -1
  55. package/dist/types/lib/composables/useRating.d.ts +1 -1
  56. package/dist/types/lib/composables/useSideMenu.d.ts +28 -0
  57. package/dist/types/lib/constants/components.d.ts +72 -0
  58. package/dist/types/lib/types/components.d.ts +103 -0
  59. package/examples/nextjs-example.tsx +271 -0
  60. package/implementation-guide.md +505 -0
  61. package/next.config.js +69 -0
  62. package/package.json +80 -42
  63. package/postcss.config.js +28 -0
  64. package/src/Introduction.mdx +3 -5
  65. package/src/assets/fonts/HelveticaNeue/stylesheet.css +140 -127
  66. package/src/components/Accordion/Accordion.stories.tsx +58 -45
  67. package/src/components/Accordion/Accordion.tsx +14 -4
  68. package/src/components/Accordion/scripts/accordionInteractions.ts +9 -9
  69. package/src/components/Accordion/scripts/bundle.ts +1 -1
  70. package/src/components/Accordion/scripts/index.ts +3 -3
  71. package/src/components/AtomixLogo.tsx +13 -19
  72. package/src/components/Avatar/Avatar.stories.tsx +24 -21
  73. package/src/components/Avatar/Avatar.tsx +5 -8
  74. package/src/components/Avatar/AvatarGroup.tsx +11 -11
  75. package/src/components/Avatar/index.ts +1 -1
  76. package/src/components/Avatar/scripts/index.ts +66 -71
  77. package/src/components/Badge/Badge.stories.tsx +51 -21
  78. package/src/components/Badge/Badge.tsx +14 -12
  79. package/src/components/Badge/index.ts +3 -3
  80. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +40 -40
  81. package/src/components/Breadcrumb/Breadcrumb.tsx +19 -26
  82. package/src/components/Breadcrumb/index.ts +1 -1
  83. package/src/components/Breadcrumb/scripts/breadcrumb.ts +36 -34
  84. package/src/components/Breadcrumb/scripts/index.ts +1 -1
  85. package/src/components/Breadcrumb/scripts/types.ts +9 -9
  86. package/src/components/Button/Button.stories.tsx +36 -12
  87. package/src/components/Button/Button.tsx +52 -39
  88. package/src/components/Button/index.ts +1 -1
  89. package/src/components/Button/scripts/buttonInteractions.ts +9 -9
  90. package/src/components/Button/scripts/index.ts +1 -1
  91. package/src/components/Callout/Callout.stories.tsx +207 -114
  92. package/src/components/Callout/Callout.tsx +12 -12
  93. package/src/components/Callout/index.ts +1 -1
  94. package/src/components/Callout/scripts/CalloutInteractions.ts +58 -48
  95. package/src/components/Callout/scripts/bundle.ts +2 -2
  96. package/src/components/Callout/scripts/index.ts +19 -15
  97. package/src/components/Card/Card.stories.tsx +2 -2
  98. package/src/components/Card/Card.tsx +49 -72
  99. package/src/components/Card/ElevationCard.tsx +4 -8
  100. package/src/components/Card/index.ts +1 -1
  101. package/src/components/Card/scripts/bundle.ts +7 -7
  102. package/src/components/Card/scripts/cardInteractions.ts +24 -24
  103. package/src/components/Card/scripts/index.ts +25 -26
  104. package/src/components/ColorModeToggle/ColorModeToggle.stories.tsx +6 -3
  105. package/src/components/ColorModeToggle/ColorModeToggle.tsx +12 -15
  106. package/src/components/ColorModeToggle/index.ts +1 -1
  107. package/src/components/Countdown/Countdown.stories.tsx +6 -2
  108. package/src/components/Countdown/Countdown.tsx +56 -48
  109. package/src/components/Countdown/index.ts +1 -1
  110. package/src/components/Countdown/scripts/index.ts +33 -28
  111. package/src/components/DataTable/DataTable.stories.tsx +23 -18
  112. package/src/components/DataTable/DataTable.tsx +39 -39
  113. package/src/components/DataTable/index.ts +1 -1
  114. package/src/components/DataTable/scripts/bundle.ts +8 -3
  115. package/src/components/DataTable/scripts/index.ts +182 -164
  116. package/src/components/DatePicker/DatePicker.stories.tsx +136 -148
  117. package/src/components/DatePicker/DatePicker.tsx +461 -420
  118. package/src/components/DatePicker/scripts/bundle.ts +12 -7
  119. package/src/components/DatePicker/scripts/componentInteractions.ts +76 -46
  120. package/src/components/DatePicker/scripts/index.ts +176 -156
  121. package/src/components/DatePicker/types.ts +32 -32
  122. package/src/components/DatePicker/utils.ts +41 -30
  123. package/src/components/Dropdown/Dropdown.stories.tsx +85 -55
  124. package/src/components/Dropdown/Dropdown.tsx +97 -88
  125. package/src/components/Dropdown/index.ts +5 -10
  126. package/src/components/Dropdown/scripts/bundle.ts +10 -10
  127. package/src/components/Dropdown/scripts/componentInteractions.ts +10 -2
  128. package/src/components/Dropdown/scripts/index.ts +122 -117
  129. package/src/components/EdgePanel/EdgePanel.stories.tsx +142 -58
  130. package/src/components/EdgePanel/EdgePanel.tsx +7 -13
  131. package/src/components/EdgePanel/index.ts +1 -1
  132. package/src/components/EdgePanel/scripts/bundle.ts +5 -5
  133. package/src/components/EdgePanel/scripts/edgePanelInteractions.ts +26 -26
  134. package/src/components/EdgePanel/scripts/index.ts +53 -53
  135. package/src/components/Form/Checkbox.stories.tsx +2 -2
  136. package/src/components/Form/Checkbox.tsx +13 -7
  137. package/src/components/Form/Form.stories.tsx +144 -218
  138. package/src/components/Form/Form.tsx +6 -6
  139. package/src/components/Form/FormGroup.stories.tsx +21 -38
  140. package/src/components/Form/FormGroup.tsx +18 -20
  141. package/src/components/Form/Input.stories.tsx +1 -1
  142. package/src/components/Form/Input.tsx +18 -8
  143. package/src/components/Form/Radio.stories.tsx +12 -25
  144. package/src/components/Form/Radio.tsx +11 -6
  145. package/src/components/Form/Select.stories.tsx +6 -6
  146. package/src/components/Form/Select.tsx +31 -33
  147. package/src/components/Form/Textarea.stories.tsx +7 -2
  148. package/src/components/Form/Textarea.tsx +17 -8
  149. package/src/components/Form/index.ts +1 -1
  150. package/src/components/Hero/Hero.stories.tsx +44 -42
  151. package/src/components/Hero/Hero.tsx +28 -38
  152. package/src/components/Hero/index.ts +1 -1
  153. package/src/components/Hero/scripts/bundle.ts +6 -6
  154. package/src/components/Hero/scripts/heroInteractions.ts +24 -29
  155. package/src/components/Hero/scripts/index.ts +16 -17
  156. package/src/components/Icon/Icon.tsx +16 -18
  157. package/src/components/Icon/index.ts +1 -1
  158. package/src/components/List/List.stories.tsx +1 -3
  159. package/src/components/List/List.tsx +6 -10
  160. package/src/components/List/ListGroup.tsx +1 -1
  161. package/src/components/List/index.ts +1 -1
  162. package/src/components/Messages/Messages.stories.tsx +30 -29
  163. package/src/components/Messages/Messages.tsx +60 -55
  164. package/src/components/Messages/index.ts +1 -1
  165. package/src/components/Messages/scripts/bundle.ts +1 -6
  166. package/src/components/Messages/scripts/componentInteractions.ts +32 -37
  167. package/src/components/Messages/scripts/index.ts +61 -55
  168. package/src/components/Modal/Modal.stories.tsx +77 -53
  169. package/src/components/Modal/Modal.tsx +63 -62
  170. package/src/components/Modal/index.ts +1 -1
  171. package/src/components/Modal/scripts/bundle.ts +3 -3
  172. package/src/components/Modal/scripts/index.ts +96 -84
  173. package/src/components/Modal/scripts/modalInteractions.ts +16 -14
  174. package/src/components/{Navbar → Navigation/Menu}/MegaMenu.tsx +50 -59
  175. package/src/components/Navigation/Menu/Menu.stories.tsx +340 -0
  176. package/src/components/Navigation/Menu/Menu.tsx +110 -0
  177. package/src/components/Navigation/Nav/Nav.stories.tsx +458 -0
  178. package/src/components/Navigation/Nav/Nav.tsx +50 -0
  179. package/src/components/Navigation/Nav/NavDropdown.tsx +105 -0
  180. package/src/components/Navigation/Nav/NavItem.tsx +159 -0
  181. package/src/components/{Navbar → Navigation/Navbar}/Navbar.stories.tsx +198 -104
  182. package/src/components/Navigation/Navbar/Navbar.tsx +150 -0
  183. package/src/components/Navigation/README.md +314 -0
  184. package/src/components/Navigation/SideMenu/SideMenu.README.md +494 -0
  185. package/src/components/Navigation/SideMenu/SideMenu.stories.tsx +618 -0
  186. package/src/components/Navigation/SideMenu/SideMenu.tsx +101 -0
  187. package/src/components/Navigation/SideMenu/SideMenuItem.tsx +95 -0
  188. package/src/components/Navigation/SideMenu/SideMenuList.tsx +41 -0
  189. package/src/components/Navigation/index.ts +23 -0
  190. package/src/components/Navigation/scripts/NavbarInteractions.ts +171 -0
  191. package/src/components/Navigation/scripts/SideMenu.ts +319 -0
  192. package/src/components/Navigation/scripts/SideMenuBundle.ts +69 -0
  193. package/src/components/Navigation/scripts/SideMenuInteractions.ts +250 -0
  194. package/src/components/Navigation/scripts/bundle.ts +58 -0
  195. package/src/components/Navigation/scripts/index.ts +248 -0
  196. package/src/components/Pagination/Pagination.stories.tsx +34 -33
  197. package/src/components/Pagination/Pagination.tsx +25 -35
  198. package/src/components/Pagination/index.ts +1 -1
  199. package/src/components/Pagination/scripts/index.ts +42 -37
  200. package/src/components/PhotoViewer/PhotoViewer.stories.tsx +48 -48
  201. package/src/components/PhotoViewer/PhotoViewer.tsx +29 -46
  202. package/src/components/PhotoViewer/PhotoViewerHeader.tsx +20 -26
  203. package/src/components/PhotoViewer/PhotoViewerImage.tsx +19 -17
  204. package/src/components/PhotoViewer/PhotoViewerInfo.tsx +13 -5
  205. package/src/components/PhotoViewer/PhotoViewerNavigation.tsx +5 -5
  206. package/src/components/PhotoViewer/PhotoViewerThumbnails.tsx +31 -30
  207. package/src/components/PhotoViewer/examples/ImageGallery.tsx +27 -37
  208. package/src/components/PhotoViewer/examples/SimpleGallery.tsx +19 -13
  209. package/src/components/PhotoViewer/examples/index.ts +1 -1
  210. package/src/components/PhotoViewer/index.ts +1 -1
  211. package/src/components/PhotoViewer/scripts/PhotoViewerInteractions.ts +43 -33
  212. package/src/components/PhotoViewer/scripts/bundle.ts +14 -14
  213. package/src/components/PhotoViewer/scripts/index.ts +173 -129
  214. package/src/components/Popover/Popover.stories.tsx +11 -12
  215. package/src/components/Popover/Popover.tsx +36 -36
  216. package/src/components/Popover/index.ts +1 -1
  217. package/src/components/Popover/scripts/bundle.ts +1 -1
  218. package/src/components/Popover/scripts/componentInteractions.ts +34 -46
  219. package/src/components/Popover/scripts/index.ts +64 -53
  220. package/src/components/ProductReview/ProductReview.stories.tsx +10 -8
  221. package/src/components/ProductReview/ProductReview.tsx +28 -32
  222. package/src/components/ProductReview/scripts/componentInteractions.ts +20 -20
  223. package/src/components/Progress/Progress.tsx +36 -34
  224. package/src/components/Progress/scripts/bundle.ts +7 -2
  225. package/src/components/Progress/scripts/componentInteractions.ts +29 -23
  226. package/src/components/Progress/scripts/index.ts +45 -39
  227. package/src/components/Rating/Rating.stories.tsx +3 -16
  228. package/src/components/Rating/Rating.tsx +250 -231
  229. package/src/components/Rating/scripts/bundle.ts +11 -6
  230. package/src/components/Rating/scripts/index.ts +85 -80
  231. package/src/components/Rating/scripts/ratingInteractions.ts +27 -24
  232. package/src/components/River/River.stories.tsx +70 -24
  233. package/src/components/River/River.tsx +28 -38
  234. package/src/components/River/index.ts +1 -1
  235. package/src/components/River/scripts/index.ts +11 -12
  236. package/src/components/SectionIntro/SectionIntro.stories.tsx +12 -9
  237. package/src/components/SectionIntro/SectionIntro.tsx +25 -31
  238. package/src/components/SectionIntro/scripts/componentInteractions.ts +1 -1
  239. package/src/components/SectionIntro/scripts/index.ts +30 -16
  240. package/src/components/Spinner/Spinner.stories.tsx +5 -7
  241. package/src/components/Spinner/Spinner.tsx +11 -6
  242. package/src/components/Spinner/index.ts +2 -2
  243. package/src/components/Steps/Steps.stories.tsx +44 -48
  244. package/src/components/Steps/Steps.tsx +20 -20
  245. package/src/components/Steps/index.ts +1 -1
  246. package/src/components/Steps/scripts/index.ts +9 -9
  247. package/src/components/Tab/Tab.stories.tsx +14 -6
  248. package/src/components/Tab/Tab.tsx +16 -18
  249. package/src/components/Tab/index.ts +2 -2
  250. package/src/components/Tab/scripts/index.ts +13 -13
  251. package/src/components/Testimonial/Testimonial.stories.tsx +54 -51
  252. package/src/components/Testimonial/Testimonial.tsx +18 -19
  253. package/src/components/Testimonial/index.ts +1 -1
  254. package/src/components/Testimonial/scripts/index.ts +19 -8
  255. package/src/components/Todo/Todo.stories.tsx +7 -24
  256. package/src/components/Todo/Todo.tsx +35 -46
  257. package/src/components/Todo/index.ts +1 -1
  258. package/src/components/Todo/scripts/bundle.ts +1 -1
  259. package/src/components/Todo/scripts/index.ts +99 -81
  260. package/src/components/Todo/scripts/todoInteractions.ts +12 -12
  261. package/src/components/Todo/scripts/types.ts +3 -3
  262. package/src/components/Toggle/Toggle.stories.tsx +2 -2
  263. package/src/components/Toggle/Toggle.tsx +18 -18
  264. package/src/components/Toggle/index.ts +2 -2
  265. package/src/components/Toggle/scripts/bundle.ts +7 -2
  266. package/src/components/Toggle/scripts/index.ts +14 -10
  267. package/src/components/Toggle/scripts/toggleInteractions.ts +11 -14
  268. package/src/components/Tooltip/Tooltip.stories.tsx +13 -6
  269. package/src/components/Tooltip/Tooltip.tsx +25 -15
  270. package/src/components/Tooltip/index.ts +3 -3
  271. package/src/components/Tooltip/scripts/bundle.ts +0 -1
  272. package/src/components/Tooltip/scripts/index.ts +44 -41
  273. package/src/components/Tooltip/scripts/tooltipInteractions.ts +22 -22
  274. package/src/components/Upload/Upload.stories.tsx +28 -34
  275. package/src/components/Upload/Upload.tsx +86 -71
  276. package/src/components/Upload/index.ts +1 -1
  277. package/src/components/Upload/scripts/index.ts +58 -43
  278. package/src/components/index.ts +5 -6
  279. package/src/design-tokens/BoxShadow/BoxShadow.stories.tsx +13 -10
  280. package/src/design-tokens/Colors/colors.scss +10 -7
  281. package/src/design-tokens/Colors/colors.stories.tsx +46 -59
  282. package/src/design-tokens/Spacing/Spacing.scss +7 -5
  283. package/src/design-tokens/Spacing/Spacing.stories.tsx +19 -18
  284. package/src/design-tokens/Typography/Typography.scss +88 -25
  285. package/src/design-tokens/Typography/Typography.stories.tsx +22 -25
  286. package/src/docs/implementation-guide.mdx +2 -2
  287. package/src/htmlComponentsEntry.ts +23 -23
  288. package/src/index.ts +1 -1
  289. package/src/layouts/Grid/Container.tsx +6 -10
  290. package/src/layouts/Grid/Grid.stories.tsx +72 -34
  291. package/src/layouts/Grid/Grid.tsx +6 -13
  292. package/src/layouts/Grid/GridCol.tsx +29 -26
  293. package/src/layouts/Grid/Row.tsx +6 -13
  294. package/src/layouts/MasonryGrid/MasonryGrid.stories.tsx +79 -72
  295. package/src/layouts/MasonryGrid/MasonryGrid.tsx +23 -25
  296. package/src/layouts/MasonryGrid/MasonryGridItem.tsx +4 -12
  297. package/src/layouts/index.ts +3 -4
  298. package/src/lib/composables/index.ts +3 -3
  299. package/src/lib/composables/useAccordion.ts +15 -6
  300. package/src/lib/composables/useBadge.ts +3 -3
  301. package/src/lib/composables/useBreadcrumb.ts +6 -12
  302. package/src/lib/composables/useButton.ts +3 -3
  303. package/src/lib/composables/useCallout.ts +3 -3
  304. package/src/lib/composables/useCard.ts +50 -28
  305. package/src/lib/composables/useCheckbox.ts +7 -7
  306. package/src/lib/composables/useDataTable.ts +61 -57
  307. package/src/lib/composables/useDatePicker.ts +255 -231
  308. package/src/lib/composables/useDropdown.ts +90 -75
  309. package/src/lib/composables/useEdgePanel.ts +50 -47
  310. package/src/lib/composables/useForm.ts +4 -7
  311. package/src/lib/composables/useFormGroup.ts +11 -12
  312. package/src/lib/composables/useHero.ts +36 -38
  313. package/src/lib/composables/useInput.ts +9 -10
  314. package/src/lib/composables/useMessages.ts +12 -14
  315. package/src/lib/composables/useModal.ts +37 -34
  316. package/src/lib/composables/useNavbar.ts +33 -22
  317. package/src/lib/composables/usePagination.ts +3 -10
  318. package/src/lib/composables/usePhotoViewer.ts +625 -578
  319. package/src/lib/composables/usePopover.ts +68 -62
  320. package/src/lib/composables/useProgress.ts +9 -12
  321. package/src/lib/composables/useRadio.ts +4 -4
  322. package/src/lib/composables/useRating.ts +82 -70
  323. package/src/lib/composables/useRiver.ts +28 -31
  324. package/src/lib/composables/useSelect.ts +7 -8
  325. package/src/lib/composables/useSideMenu.ts +197 -0
  326. package/src/lib/composables/useSpinner.ts +3 -3
  327. package/src/lib/composables/useTextarea.ts +8 -9
  328. package/src/lib/composables/useTodo.ts +14 -18
  329. package/src/lib/constants/components.ts +191 -118
  330. package/src/lib/constants/index.ts +1 -1
  331. package/src/lib/types/components.ts +454 -324
  332. package/src/lib/utils/dom.ts +2 -6
  333. package/src/lib/utils/icons.ts +20 -12
  334. package/src/lib/utils/index.ts +2 -2
  335. package/src/lib/utils/useForkRef.ts +1 -2
  336. package/src/styles/01-settings/_index.scss +59 -59
  337. package/src/styles/01-settings/_settings.accordion.scss +21 -21
  338. package/src/styles/01-settings/_settings.animations.scss +2 -2
  339. package/src/styles/01-settings/_settings.avatar-group.scss +14 -14
  340. package/src/styles/01-settings/_settings.avatar.scss +31 -31
  341. package/src/styles/01-settings/_settings.badge.scss +11 -11
  342. package/src/styles/01-settings/_settings.border-radius.scss +10 -9
  343. package/src/styles/01-settings/_settings.border.scss +7 -7
  344. package/src/styles/01-settings/_settings.box-shadow.scss +24 -12
  345. package/src/styles/01-settings/_settings.breadcrumb.scss +18 -18
  346. package/src/styles/01-settings/_settings.btn-group.scss +1 -1
  347. package/src/styles/01-settings/_settings.button.scss +63 -49
  348. package/src/styles/01-settings/_settings.callout.scss +27 -7
  349. package/src/styles/01-settings/_settings.card.scss +27 -30
  350. package/src/styles/01-settings/_settings.checkbox-group.scss +2 -3
  351. package/src/styles/01-settings/_settings.checkbox.scss +17 -18
  352. package/src/styles/01-settings/_settings.color-mode.scss +1 -1
  353. package/src/styles/01-settings/_settings.colors.scss +197 -214
  354. package/src/styles/01-settings/_settings.config.scss +1 -3
  355. package/src/styles/01-settings/_settings.countdown.scss +14 -14
  356. package/src/styles/01-settings/_settings.data-table.scss +6 -6
  357. package/src/styles/01-settings/_settings.datepicker.scss +40 -42
  358. package/src/styles/01-settings/_settings.dropdown.scss +30 -30
  359. package/src/styles/01-settings/_settings.edge-panel.scss +16 -16
  360. package/src/styles/01-settings/_settings.fonts.scss +14 -12
  361. package/src/styles/01-settings/_settings.form-group.scss +10 -10
  362. package/src/styles/01-settings/_settings.form.scss +3 -3
  363. package/src/styles/01-settings/_settings.grid.scss +1 -1
  364. package/src/styles/01-settings/_settings.hero.scss +23 -23
  365. package/src/styles/01-settings/_settings.input.scss +32 -32
  366. package/src/styles/01-settings/_settings.link.scss +6 -6
  367. package/src/styles/01-settings/_settings.list-group.scss +12 -12
  368. package/src/styles/01-settings/_settings.list.scss +2 -2
  369. package/src/styles/01-settings/_settings.maps.scss +183 -188
  370. package/src/styles/01-settings/_settings.masonry-grid.scss +2 -2
  371. package/src/styles/01-settings/_settings.menu.scss +35 -38
  372. package/src/styles/01-settings/_settings.messages.scss +71 -77
  373. package/src/styles/01-settings/_settings.modal.scss +24 -24
  374. package/src/styles/01-settings/_settings.nav.scss +15 -15
  375. package/src/styles/01-settings/_settings.navbar.scss +39 -12
  376. package/src/styles/01-settings/_settings.pagination.scss +21 -21
  377. package/src/styles/01-settings/_settings.photoviewer.scss +1 -1
  378. package/src/styles/01-settings/_settings.popover.scss +3 -3
  379. package/src/styles/01-settings/_settings.position.scss +2 -2
  380. package/src/styles/01-settings/_settings.progress.scss +15 -18
  381. package/src/styles/01-settings/_settings.rating.scss +7 -7
  382. package/src/styles/01-settings/_settings.river.scss +25 -25
  383. package/src/styles/01-settings/_settings.sectionintro.scss +15 -16
  384. package/src/styles/01-settings/_settings.select.scss +31 -31
  385. package/src/styles/01-settings/_settings.side-menu.scss +64 -16
  386. package/src/styles/01-settings/_settings.skeleton.scss +12 -12
  387. package/src/styles/01-settings/_settings.spacing.scss +62 -33
  388. package/src/styles/01-settings/_settings.spinner.scss +10 -10
  389. package/src/styles/01-settings/_settings.steps.scss +22 -22
  390. package/src/styles/01-settings/_settings.tabs.scss +25 -25
  391. package/src/styles/01-settings/_settings.testimonials.scss +17 -19
  392. package/src/styles/01-settings/_settings.todo.scss +1 -1
  393. package/src/styles/01-settings/_settings.toggle.scss +26 -26
  394. package/src/styles/01-settings/_settings.tooltip.scss +15 -15
  395. package/src/styles/01-settings/_settings.typography.scss +65 -48
  396. package/src/styles/01-settings/_settings.upload.scss +70 -77
  397. package/src/styles/01-settings/_settings.z-layers.scss +1 -1
  398. package/src/styles/02-tools/_index.scss +19 -19
  399. package/src/styles/02-tools/_tools.animations.scss +4 -4
  400. package/src/styles/02-tools/_tools.border-radius.scss +4 -5
  401. package/src/styles/02-tools/_tools.breakpoints.scss +33 -34
  402. package/src/styles/02-tools/_tools.button.scss +49 -25
  403. package/src/styles/02-tools/_tools.color-mode.scss +11 -11
  404. package/src/styles/02-tools/_tools.event.scss +1 -1
  405. package/src/styles/02-tools/_tools.hidden-visually.scss +1 -1
  406. package/src/styles/02-tools/_tools.hidden.scss +1 -1
  407. package/src/styles/02-tools/_tools.map-loop.scss +9 -9
  408. package/src/styles/02-tools/_tools.media-queries.scss +5 -3
  409. package/src/styles/02-tools/_tools.object-fit.scss +3 -3
  410. package/src/styles/02-tools/_tools.placeholder.scss +0 -1
  411. package/src/styles/02-tools/_tools.rem.scss +1 -1
  412. package/src/styles/02-tools/_tools.spacing.scss +8 -34
  413. package/src/styles/02-tools/_tools.to-rgb.scss +3 -3
  414. package/src/styles/02-tools/_tools.transition.scss +1 -1
  415. package/src/styles/02-tools/_tools.utility-api.scss +29 -14
  416. package/src/styles/03-generic/_generic.fonts.scss +0 -1
  417. package/src/styles/03-generic/_generic.reset.scss +13 -8
  418. package/src/styles/03-generic/_generic.root.scss +5 -5
  419. package/src/styles/03-generic/_index.scss +4 -4
  420. package/src/styles/04-elements/_elements.all.scss +2 -2
  421. package/src/styles/04-elements/_elements.body.scss +1 -2
  422. package/src/styles/04-elements/_elements.heading.scss +37 -21
  423. package/src/styles/04-elements/_elements.links.scss +0 -1
  424. package/src/styles/04-elements/_index.scss +5 -5
  425. package/src/styles/05-objects/_index.scss +3 -3
  426. package/src/styles/05-objects/_objects.container.scss +5 -4
  427. package/src/styles/05-objects/_objects.grid.scss +12 -12
  428. package/src/styles/05-objects/_objects.masonry-grid.scss +80 -74
  429. package/src/styles/06-components/_components.accordion.scss +12 -6
  430. package/src/styles/06-components/_components.avatar-group.scss +9 -8
  431. package/src/styles/06-components/_components.avatar.scss +78 -77
  432. package/src/styles/06-components/_components.badge.scss +48 -48
  433. package/src/styles/06-components/_components.breadcrumb.scss +57 -58
  434. package/src/styles/06-components/_components.btn-group.scss +22 -22
  435. package/src/styles/06-components/_components.button.scss +118 -110
  436. package/src/styles/06-components/_components.callout.scss +21 -15
  437. package/src/styles/06-components/_components.card.scss +6 -10
  438. package/src/styles/06-components/_components.checkbox-group.scss +9 -10
  439. package/src/styles/06-components/_components.checkbox.scss +10 -17
  440. package/src/styles/06-components/_components.color-mode-toggle.scss +6 -6
  441. package/src/styles/06-components/_components.countdown.scss +53 -54
  442. package/src/styles/06-components/_components.data-table.scss +40 -36
  443. package/src/styles/06-components/_components.datepicker.scss +95 -73
  444. package/src/styles/06-components/_components.dropdown.scss +15 -9
  445. package/src/styles/06-components/_components.edge-panel.scss +87 -46
  446. package/src/styles/06-components/_components.form-group.scss +5 -4
  447. package/src/styles/06-components/_components.hero.scss +128 -132
  448. package/src/styles/06-components/_components.icon.scss +16 -16
  449. package/src/styles/06-components/_components.image-gallery.scss +9 -7
  450. package/src/styles/06-components/_components.input.scss +18 -16
  451. package/src/styles/06-components/_components.list-group.scss +52 -51
  452. package/src/styles/06-components/_components.list.scss +15 -15
  453. package/src/styles/06-components/_components.menu.scss +225 -220
  454. package/src/styles/06-components/_components.messages.scss +45 -32
  455. package/src/styles/06-components/_components.modal.scss +0 -1
  456. package/src/styles/06-components/_components.nav.scss +56 -11
  457. package/src/styles/06-components/_components.navbar.scss +278 -202
  458. package/src/styles/06-components/_components.pagination.scss +7 -6
  459. package/src/styles/06-components/_components.photoviewer.scss +121 -116
  460. package/src/styles/06-components/_components.popover.scss +10 -10
  461. package/src/styles/06-components/_components.product-review.scss +3 -3
  462. package/src/styles/06-components/_components.progress.scss +32 -35
  463. package/src/styles/06-components/_components.rating.scss +9 -8
  464. package/src/styles/06-components/_components.river.scss +4 -2
  465. package/src/styles/06-components/_components.sectionintro.scss +19 -26
  466. package/src/styles/06-components/_components.select.scss +7 -8
  467. package/src/styles/06-components/_components.side-menu.scss +154 -28
  468. package/src/styles/06-components/_components.skeleton.scss +0 -1
  469. package/src/styles/06-components/_components.spinner.scss +2 -4
  470. package/src/styles/06-components/_components.steps.scss +76 -77
  471. package/src/styles/06-components/_components.tabs.scss +17 -12
  472. package/src/styles/06-components/_components.testimonials.scss +49 -52
  473. package/src/styles/06-components/_components.todo.scss +26 -26
  474. package/src/styles/06-components/_components.tooltip.scss +114 -115
  475. package/src/styles/06-components/_components.upload.scss +8 -18
  476. package/src/styles/06-components/_index.scss +48 -48
  477. package/src/styles/99-utilities/_index.scss +18 -18
  478. package/src/styles/99-utilities/_utilities.background.scss +13 -13
  479. package/src/styles/99-utilities/_utilities.border.scss +30 -30
  480. package/src/styles/99-utilities/_utilities.clearfix.scss +1 -1
  481. package/src/styles/99-utilities/_utilities.display.scss +5 -4
  482. package/src/styles/99-utilities/_utilities.flex.scss +19 -19
  483. package/src/styles/99-utilities/_utilities.link.scss +52 -35
  484. package/src/styles/99-utilities/_utilities.object-fit.scss +3 -3
  485. package/src/styles/99-utilities/_utilities.opacity.scss +6 -6
  486. package/src/styles/99-utilities/_utilities.overflow.scss +4 -4
  487. package/src/styles/99-utilities/_utilities.position.scss +8 -8
  488. package/src/styles/99-utilities/_utilities.shadow.scss +13 -13
  489. package/src/styles/99-utilities/_utilities.sizes.scss +17 -17
  490. package/src/styles/99-utilities/_utilities.spacing.scss +72 -37
  491. package/src/styles/99-utilities/_utilities.text.scss +15 -15
  492. package/src/styles/99-utilities/_utilities.visibility.scss +8 -8
  493. package/src/styles/99-utilities/_utilities.visually-hidden.scss +1 -1
  494. package/src/styles/99-utilities/_utilities.z-index.scss +2 -2
  495. package/tsconfig.json +74 -0
  496. package/webpack.config.js +463 -0
  497. package/NPM_PUBLISHING.md +0 -221
  498. package/dist/css/atomix.css.map +0 -1
  499. package/dist/js/194.js.map +0 -1
  500. package/dist/js/atomix.react.esm.js.map +0 -1
  501. package/dist/js/atomix.react.umd.js.map +0 -1
  502. package/dist/js/chunks/esm/202.ff48d27672233280e021.js.map +0 -1
  503. package/dist/js/chunks/esm/308.f873332126eba90e5c62.js.map +0 -1
  504. package/dist/js/chunks/esm/54.4db919e5e4e5cc6d7c72.js.map +0 -1
  505. package/dist/js/chunks/esm/619.afc5a718eff77fa423b5.js.map +0 -1
  506. package/dist/js/chunks/esm/690.a9e968c7497d61e56cdc.js.map +0 -1
  507. package/dist/js/chunks/esm/894.f1091a4a8758c26d29e4.js.map +0 -1
  508. package/dist/js/chunks/esm/897.561a50f7d043d42169da.js.map +0 -1
  509. package/dist/js/chunks/umd/202.dac7605cc555b6bda542.js.map +0 -1
  510. package/dist/js/chunks/umd/308.6709979849dcbdb90c9b.js.map +0 -1
  511. package/dist/js/chunks/umd/54.403470e1f7d0ef4424a7.js.map +0 -1
  512. package/dist/js/chunks/umd/619.fa05ea98c10270eb64c5.js.map +0 -1
  513. package/dist/js/chunks/umd/690.aa7054d1c53e5402c2d6.js.map +0 -1
  514. package/dist/js/chunks/umd/894.3e1eaf0a2aadf4434390.js.map +0 -1
  515. package/dist/js/chunks/umd/897.554ea37be4453698c167.js.map +0 -1
  516. package/dist/types/components/Navbar/Nav.d.ts +0 -5
  517. package/dist/types/components/Navbar/NavItem.d.ts +0 -5
  518. package/dist/types/components/Navbar/Navbar.d.ts +0 -5
  519. package/dist/types/components/Navbar/index.d.ts +0 -6
  520. package/src/components/Navbar/Menu.tsx +0 -122
  521. package/src/components/Navbar/Nav.tsx +0 -35
  522. package/src/components/Navbar/NavDropdown.tsx +0 -108
  523. package/src/components/Navbar/NavItem.tsx +0 -128
  524. package/src/components/Navbar/Navbar.tsx +0 -124
  525. package/src/components/Navbar/index.ts +0 -6
@@ -20,12 +20,12 @@ interface ImageState {
20
20
  };
21
21
  }
22
22
 
23
- export const usePhotoViewer = ({
24
- images,
25
- startIndex = 0,
26
- enableGestures = true,
27
- onImageChange,
28
- onClose
23
+ export const usePhotoViewer = ({
24
+ images,
25
+ startIndex = 0,
26
+ enableGestures = true,
27
+ onImageChange,
28
+ onClose,
29
29
  }: UsePhotoViewerProps) => {
30
30
  const [currentIndex, setCurrentIndex] = useState(startIndex);
31
31
  const [isModalOpen, setIsModalOpen] = useState(false);
@@ -37,11 +37,11 @@ export const usePhotoViewer = ({
37
37
  const [isTransitioning, setIsTransitioning] = useState(false);
38
38
  const [isMounted, setIsMounted] = useState(false);
39
39
  const [momentumZoom, setMomentumZoom] = useState({ velocity: 0, timestamp: 0 });
40
-
40
+
41
41
  // Ref for the image element
42
42
  const imageRef = useRef<HTMLImageElement>(null);
43
43
  const containerRef = useRef<HTMLDivElement>(null);
44
-
44
+
45
45
  // Track touch points for pinch zoom
46
46
  const touchPointsRef = useRef<{ x: number; y: number }[]>([]);
47
47
  const lastDistanceRef = useRef<number | null>(null);
@@ -50,80 +50,86 @@ export const usePhotoViewer = ({
50
50
  const momentumTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
51
51
 
52
52
  // Calculate dragging bounds based on zoom level and image dimensions
53
- const calculateBounds = useCallback((zoomLevel: number, rotation: number) => {
54
- if (!isMounted || !imageRef.current || !containerRef.current) {
55
- return { minX: 0, maxX: 0, minY: 0, maxY: 0 };
56
- }
53
+ const calculateBounds = useCallback(
54
+ (zoomLevel: number, rotation: number) => {
55
+ if (!isMounted || !imageRef.current || !containerRef.current) {
56
+ return { minX: 0, maxX: 0, minY: 0, maxY: 0 };
57
+ }
57
58
 
58
- const image = imageRef.current;
59
- const container = containerRef.current;
60
-
61
- // Additional safety check for DOM readiness
62
- if (!image.naturalWidth && !image.width) {
63
- return { minX: 0, maxX: 0, minY: 0, maxY: 0 };
64
- }
65
-
66
- // Get natural image dimensions
67
- const imageWidth = image.naturalWidth || image.width || 800;
68
- const imageHeight = image.naturalHeight || image.height || 600;
69
-
70
- // Get container dimensions with null check
71
- try {
72
- const containerRect = container.getBoundingClientRect();
73
- if (!containerRect || containerRect.width === 0 || containerRect.height === 0) {
59
+ const image = imageRef.current;
60
+ const container = containerRef.current;
61
+
62
+ // Additional safety check for DOM readiness
63
+ if (!image.naturalWidth && !image.width) {
74
64
  return { minX: 0, maxX: 0, minY: 0, maxY: 0 };
75
65
  }
76
- const containerWidth = containerRect.width;
77
- const containerHeight = containerRect.height;
78
-
79
- // Calculate image display dimensions considering rotation
80
- const rotationRad = (rotation * Math.PI) / 180;
81
- const cos = Math.abs(Math.cos(rotationRad));
82
- const sin = Math.abs(Math.sin(rotationRad));
83
-
84
- // Calculate the actual display size of the image
85
- const aspectRatio = imageWidth / imageHeight;
86
- let displayWidth, displayHeight;
87
-
88
- if (containerWidth / containerHeight > aspectRatio) {
89
- displayHeight = Math.min(containerHeight * 0.9, imageHeight);
90
- displayWidth = displayHeight * aspectRatio;
91
- } else {
92
- displayWidth = Math.min(containerWidth * 0.9, imageWidth);
93
- displayHeight = displayWidth / aspectRatio;
94
- }
95
-
96
- // Account for rotation in bounds calculation
97
- const rotatedWidth = displayWidth * cos + displayHeight * sin;
98
- const rotatedHeight = displayWidth * sin + displayHeight * cos;
99
-
100
- // Calculate scaled dimensions
101
- const scaledWidth = rotatedWidth * zoomLevel;
102
- const scaledHeight = rotatedHeight * zoomLevel;
103
-
104
- // Calculate bounds - how far we can drag
105
- const maxX = Math.max(0, (scaledWidth - containerWidth) / 2);
106
- const maxY = Math.max(0, (scaledHeight - containerHeight) / 2);
107
-
108
- return {
109
- minX: -maxX,
110
- maxX: maxX,
111
- minY: -maxY,
112
- maxY: maxY
113
- };
114
- } catch (error) {
115
- console.warn('PhotoViewer: Error calculating bounds', error);
116
- return { minX: 0, maxX: 0, minY: 0, maxY: 0 };
117
- }
118
- }, [isMounted]);
66
+
67
+ // Get natural image dimensions
68
+ const imageWidth = image.naturalWidth || image.width || 800;
69
+ const imageHeight = image.naturalHeight || image.height || 600;
70
+
71
+ // Get container dimensions with null check
72
+ try {
73
+ const containerRect = container.getBoundingClientRect();
74
+ if (!containerRect || containerRect.width === 0 || containerRect.height === 0) {
75
+ return { minX: 0, maxX: 0, minY: 0, maxY: 0 };
76
+ }
77
+ const containerWidth = containerRect.width;
78
+ const containerHeight = containerRect.height;
79
+
80
+ // Calculate image display dimensions considering rotation
81
+ const rotationRad = (rotation * Math.PI) / 180;
82
+ const cos = Math.abs(Math.cos(rotationRad));
83
+ const sin = Math.abs(Math.sin(rotationRad));
84
+
85
+ // Calculate the actual display size of the image
86
+ const aspectRatio = imageWidth / imageHeight;
87
+ let displayWidth, displayHeight;
88
+
89
+ if (containerWidth / containerHeight > aspectRatio) {
90
+ displayHeight = Math.min(containerHeight * 0.9, imageHeight);
91
+ displayWidth = displayHeight * aspectRatio;
92
+ } else {
93
+ displayWidth = Math.min(containerWidth * 0.9, imageWidth);
94
+ displayHeight = displayWidth / aspectRatio;
95
+ }
96
+
97
+ // Account for rotation in bounds calculation
98
+ const rotatedWidth = displayWidth * cos + displayHeight * sin;
99
+ const rotatedHeight = displayWidth * sin + displayHeight * cos;
100
+
101
+ // Calculate scaled dimensions
102
+ const scaledWidth = rotatedWidth * zoomLevel;
103
+ const scaledHeight = rotatedHeight * zoomLevel;
104
+
105
+ // Calculate bounds - how far we can drag
106
+ const maxX = Math.max(0, (scaledWidth - containerWidth) / 2);
107
+ const maxY = Math.max(0, (scaledHeight - containerHeight) / 2);
108
+
109
+ return {
110
+ minX: -maxX,
111
+ maxX: maxX,
112
+ minY: -maxY,
113
+ maxY: maxY,
114
+ };
115
+ } catch (error) {
116
+ console.warn('PhotoViewer: Error calculating bounds', error);
117
+ return { minX: 0, maxX: 0, minY: 0, maxY: 0 };
118
+ }
119
+ },
120
+ [isMounted]
121
+ );
119
122
 
120
123
  // Constrain position within bounds
121
- const constrainPosition = useCallback((position: { x: number; y: number }, bounds: ImageState['bounds']) => {
122
- return {
123
- x: Math.max(bounds.minX, Math.min(bounds.maxX, position.x)),
124
- y: Math.max(bounds.minY, Math.min(bounds.maxY, position.y))
125
- };
126
- }, []);
124
+ const constrainPosition = useCallback(
125
+ (position: { x: number; y: number }, bounds: ImageState['bounds']) => {
126
+ return {
127
+ x: Math.max(bounds.minX, Math.min(bounds.maxX, position.x)),
128
+ y: Math.max(bounds.minY, Math.min(bounds.maxY, position.y)),
129
+ };
130
+ },
131
+ []
132
+ );
127
133
 
128
134
  // Mount tracking and ensure the current index is within bounds
129
135
  useEffect(() => {
@@ -156,15 +162,15 @@ export const usePhotoViewer = ({
156
162
  zoomLevel: 1,
157
163
  position: { x: 0, y: 0 },
158
164
  rotation: 0,
159
- bounds: { minX: 0, maxX: 0, minY: 0, maxY: 0 }
160
- }
165
+ bounds: { minX: 0, maxX: 0, minY: 0, maxY: 0 },
166
+ },
161
167
  };
162
168
  }
163
169
  return prev;
164
170
  });
165
171
  }
166
172
  }, [isModalOpen, currentIndex]);
167
-
173
+
168
174
  // Call onImageChange callback when current index changes
169
175
  useEffect(() => {
170
176
  if (onImageChange) {
@@ -176,32 +182,32 @@ export const usePhotoViewer = ({
176
182
  useEffect(() => {
177
183
  const image = imageRef.current;
178
184
  const container = containerRef.current;
179
-
185
+
180
186
  const updateImageBounds = (): void => {
181
187
  if (!isMounted || !image || !container) return undefined;
182
-
188
+
183
189
  setImageStates(prev => {
184
190
  const currentState = prev[currentIndex] || {
185
191
  zoomLevel: 1,
186
192
  position: { x: 0, y: 0 },
187
193
  rotation: 0,
188
- bounds: { minX: 0, maxX: 0, minY: 0, maxY: 0 }
194
+ bounds: { minX: 0, maxX: 0, minY: 0, maxY: 0 },
189
195
  };
190
-
196
+
191
197
  const newBounds = calculateBounds(currentState.zoomLevel, currentState.rotation);
192
198
  const constrainedPosition = constrainPosition(currentState.position, newBounds);
193
-
199
+
194
200
  return {
195
201
  ...prev,
196
202
  [currentIndex]: {
197
203
  ...currentState,
198
204
  bounds: newBounds,
199
- position: constrainedPosition
200
- }
205
+ position: constrainedPosition,
206
+ },
201
207
  };
202
208
  });
203
209
  };
204
-
210
+
205
211
  if (image && container && image.complete && isMounted) {
206
212
  updateImageBounds();
207
213
  return undefined;
@@ -216,29 +222,29 @@ export const usePhotoViewer = ({
216
222
  useEffect(() => {
217
223
  const handleResize = (): void => {
218
224
  if (!isMounted || !imageRef.current || !containerRef.current) return undefined;
219
-
225
+
220
226
  setImageStates(prev => {
221
227
  const currentState = prev[currentIndex] || {
222
228
  zoomLevel: 1,
223
229
  position: { x: 0, y: 0 },
224
230
  rotation: 0,
225
- bounds: { minX: 0, maxX: 0, minY: 0, maxY: 0 }
231
+ bounds: { minX: 0, maxX: 0, minY: 0, maxY: 0 },
226
232
  };
227
-
233
+
228
234
  const newBounds = calculateBounds(currentState.zoomLevel, currentState.rotation);
229
235
  const constrainedPosition = constrainPosition(currentState.position, newBounds);
230
-
236
+
231
237
  return {
232
238
  ...prev,
233
239
  [currentIndex]: {
234
240
  ...currentState,
235
241
  bounds: newBounds,
236
- position: constrainedPosition
237
- }
242
+ position: constrainedPosition,
243
+ },
238
244
  };
239
245
  });
240
246
  };
241
-
247
+
242
248
  window.addEventListener('resize', handleResize);
243
249
  return () => window.removeEventListener('resize', handleResize);
244
250
  }, [currentIndex, calculateBounds, constrainPosition, isMounted]);
@@ -272,529 +278,570 @@ export const usePhotoViewer = ({
272
278
  }
273
279
  }, [currentIndex, images.length]);
274
280
 
275
- const setZoomLevel = useCallback((zoom: number | ((prev: number) => number)) => {
276
- setImageStates(prev => {
277
- const currentState = prev[currentIndex] || {
278
- zoomLevel: 1,
279
- position: { x: 0, y: 0 },
280
- rotation: 0,
281
- bounds: { minX: 0, maxX: 0, minY: 0, maxY: 0 }
282
- };
283
-
284
- const newZoom = typeof zoom === 'function' ? zoom(currentState.zoomLevel) : zoom;
285
- const clampedZoom = Math.max(0.1, Math.min(5, newZoom));
286
-
287
- const newBounds = calculateBounds(clampedZoom, currentState.rotation);
288
- const constrainedPosition = constrainPosition(currentState.position, newBounds);
289
-
290
- return {
291
- ...prev,
292
- [currentIndex]: {
293
- ...currentState,
294
- zoomLevel: clampedZoom,
295
- bounds: newBounds,
296
- position: constrainedPosition
297
- }
298
- };
299
- });
300
- }, [isMounted, currentIndex, calculateBounds, constrainPosition]);
301
-
302
- const setImagePosition = useCallback((position: { x: number; y: number } | ((prev: { x: number; y: number }) => { x: number; y: number })) => {
303
- setImageStates(prev => {
304
- const currentState = prev[currentIndex] || {
305
- zoomLevel: 1,
306
- position: { x: 0, y: 0 },
307
- rotation: 0,
308
- bounds: { minX: 0, maxX: 0, minY: 0, maxY: 0 }
309
- };
310
-
311
- const newPosition = typeof position === 'function' ? position(currentState.position) : position;
312
- const constrainedPosition = constrainPosition(newPosition, currentState.bounds);
313
-
314
- return {
315
- ...prev,
316
- [currentIndex]: {
317
- ...currentState,
318
- position: constrainedPosition
319
- }
320
- };
321
- });
322
- }, [currentIndex, constrainPosition]);
323
-
324
- const setRotationAngle = useCallback((rotation: number | ((prev: number) => number)) => {
325
- setImageStates(prev => {
326
- const currentState = prev[currentIndex] || {
327
- zoomLevel: 1,
328
- position: { x: 0, y: 0 },
329
- rotation: 0,
330
- bounds: { minX: 0, maxX: 0, minY: 0, maxY: 0 }
331
- };
332
-
333
- const newRotation = typeof rotation === 'function' ? rotation(currentState.rotation) : rotation;
334
- const normalizedRotation = ((newRotation % 360) + 360) % 360;
335
-
336
- const newBounds = calculateBounds(currentState.zoomLevel, normalizedRotation);
337
- const constrainedPosition = constrainPosition(currentState.position, newBounds);
338
-
339
- return {
340
- ...prev,
341
- [currentIndex]: {
342
- ...currentState,
343
- rotation: normalizedRotation,
344
- bounds: newBounds,
345
- position: constrainedPosition
346
- }
347
- };
348
- });
349
- }, [isMounted, currentIndex, calculateBounds, constrainPosition]);
281
+ const setZoomLevel = useCallback(
282
+ (zoom: number | ((prev: number) => number)) => {
283
+ setImageStates(prev => {
284
+ const currentState = prev[currentIndex] || {
285
+ zoomLevel: 1,
286
+ position: { x: 0, y: 0 },
287
+ rotation: 0,
288
+ bounds: { minX: 0, maxX: 0, minY: 0, maxY: 0 },
289
+ };
290
+
291
+ const newZoom = typeof zoom === 'function' ? zoom(currentState.zoomLevel) : zoom;
292
+ const clampedZoom = Math.max(0.1, Math.min(5, newZoom));
293
+
294
+ const newBounds = calculateBounds(clampedZoom, currentState.rotation);
295
+ const constrainedPosition = constrainPosition(currentState.position, newBounds);
296
+
297
+ return {
298
+ ...prev,
299
+ [currentIndex]: {
300
+ ...currentState,
301
+ zoomLevel: clampedZoom,
302
+ bounds: newBounds,
303
+ position: constrainedPosition,
304
+ },
305
+ };
306
+ });
307
+ },
308
+ [isMounted, currentIndex, calculateBounds, constrainPosition]
309
+ );
310
+
311
+ const setImagePosition = useCallback(
312
+ (
313
+ position:
314
+ | { x: number; y: number }
315
+ | ((prev: { x: number; y: number }) => { x: number; y: number })
316
+ ) => {
317
+ setImageStates(prev => {
318
+ const currentState = prev[currentIndex] || {
319
+ zoomLevel: 1,
320
+ position: { x: 0, y: 0 },
321
+ rotation: 0,
322
+ bounds: { minX: 0, maxX: 0, minY: 0, maxY: 0 },
323
+ };
324
+
325
+ const newPosition =
326
+ typeof position === 'function' ? position(currentState.position) : position;
327
+ const constrainedPosition = constrainPosition(newPosition, currentState.bounds);
328
+
329
+ return {
330
+ ...prev,
331
+ [currentIndex]: {
332
+ ...currentState,
333
+ position: constrainedPosition,
334
+ },
335
+ };
336
+ });
337
+ },
338
+ [currentIndex, constrainPosition]
339
+ );
340
+
341
+ const setRotationAngle = useCallback(
342
+ (rotation: number | ((prev: number) => number)) => {
343
+ setImageStates(prev => {
344
+ const currentState = prev[currentIndex] || {
345
+ zoomLevel: 1,
346
+ position: { x: 0, y: 0 },
347
+ rotation: 0,
348
+ bounds: { minX: 0, maxX: 0, minY: 0, maxY: 0 },
349
+ };
350
+
351
+ const newRotation =
352
+ typeof rotation === 'function' ? rotation(currentState.rotation) : rotation;
353
+ const normalizedRotation = ((newRotation % 360) + 360) % 360;
354
+
355
+ const newBounds = calculateBounds(currentState.zoomLevel, normalizedRotation);
356
+ const constrainedPosition = constrainPosition(currentState.position, newBounds);
357
+
358
+ return {
359
+ ...prev,
360
+ [currentIndex]: {
361
+ ...currentState,
362
+ rotation: normalizedRotation,
363
+ bounds: newBounds,
364
+ position: constrainedPosition,
365
+ },
366
+ };
367
+ });
368
+ },
369
+ [isMounted, currentIndex, calculateBounds, constrainPosition]
370
+ );
350
371
 
351
372
  // Handle mouse wheel for zooming with proper bounds
352
- const handleWheel = useCallback((event: React.WheelEvent<HTMLDivElement>) => {
353
- if (!isMounted || !event || !event.currentTarget) return;
354
-
355
- // Additional safety check for the target element
356
- const target = event.currentTarget;
357
- if (!target || typeof target.getBoundingClientRect !== 'function') return;
358
-
359
- // Storybook-specific safety check - ensure DOM is ready
360
- if (typeof window !== 'undefined' && window.location?.href?.includes('storybook')) {
361
- try {
362
- // Test if getBoundingClientRect works before proceeding
363
- const testRect = target.getBoundingClientRect();
364
- if (!testRect || testRect.width === 0 || testRect.height === 0) return;
365
- } catch (e) {
366
- return;
373
+ const handleWheel = useCallback(
374
+ (event: React.WheelEvent<HTMLDivElement>) => {
375
+ if (!isMounted || !event || !event.currentTarget) return;
376
+
377
+ // Additional safety check for the target element
378
+ const target = event.currentTarget;
379
+ if (!target || typeof target.getBoundingClientRect !== 'function') return;
380
+
381
+ // Storybook-specific safety check - ensure DOM is ready
382
+ if (typeof window !== 'undefined' && window.location?.href?.includes('storybook')) {
383
+ try {
384
+ // Test if getBoundingClientRect works before proceeding
385
+ const testRect = target.getBoundingClientRect();
386
+ if (!testRect || testRect.width === 0 || testRect.height === 0) return;
387
+ } catch (e) {
388
+ return;
389
+ }
367
390
  }
368
- }
369
-
370
- setImageStates(prev => {
371
- const currentState = prev[currentIndex] || {
372
- zoomLevel: 1,
373
- position: { x: 0, y: 0 },
374
- rotation: 0,
375
- bounds: { minX: 0, maxX: 0, minY: 0, maxY: 0 }
376
- };
377
-
378
- // Advanced gesture detection for different input methods
379
- const isMac = /Mac|iPod|iPhone|iPad/.test(navigator.platform);
380
- const isTrackpadPinch = event.ctrlKey && isMac;
381
- const hasHorizontalScroll = Math.abs(event.deltaX) > 0;
382
- const isTrackpadScroll = !event.ctrlKey && hasHorizontalScroll && isMac;
383
- const isMagicMouse = !event.ctrlKey && !hasHorizontalScroll && isMac;
384
- const isRegularMouse = !isMac;
385
-
386
- // Handle different input methods with appropriate sensitivity
387
- let zoomAmount: number;
388
- let shouldPreventDefault = false;
389
-
390
- if (isTrackpadPinch) {
391
- // MacBook trackpad pinch zoom - natural, high sensitivity
392
- zoomAmount = event.deltaY * -0.02;
393
- shouldPreventDefault = true;
394
- } else if (isTrackpadScroll) {
395
- // MacBook trackpad scroll with two fingers
396
- if (currentState.zoomLevel > 1) {
397
- // Only zoom when already zoomed in, otherwise allow natural scroll
398
- zoomAmount = event.deltaY * -0.003;
391
+
392
+ setImageStates(prev => {
393
+ const currentState = prev[currentIndex] || {
394
+ zoomLevel: 1,
395
+ position: { x: 0, y: 0 },
396
+ rotation: 0,
397
+ bounds: { minX: 0, maxX: 0, minY: 0, maxY: 0 },
398
+ };
399
+
400
+ // Advanced gesture detection for different input methods
401
+ const isMac = /Mac|iPod|iPhone|iPad/.test(navigator.platform);
402
+ const isTrackpadPinch = event.ctrlKey && isMac;
403
+ const hasHorizontalScroll = Math.abs(event.deltaX) > 0;
404
+ const isTrackpadScroll = !event.ctrlKey && hasHorizontalScroll && isMac;
405
+ const isMagicMouse = !event.ctrlKey && !hasHorizontalScroll && isMac;
406
+ const isRegularMouse = !isMac;
407
+
408
+ // Handle different input methods with appropriate sensitivity
409
+ let zoomAmount: number;
410
+ let shouldPreventDefault = false;
411
+
412
+ if (isTrackpadPinch) {
413
+ // MacBook trackpad pinch zoom - natural, high sensitivity
414
+ zoomAmount = event.deltaY * -0.02;
415
+ shouldPreventDefault = true;
416
+ } else if (isTrackpadScroll) {
417
+ // MacBook trackpad scroll with two fingers
418
+ if (currentState.zoomLevel > 1) {
419
+ // Only zoom when already zoomed in, otherwise allow natural scroll
420
+ zoomAmount = event.deltaY * -0.003;
421
+ shouldPreventDefault = true;
422
+ } else {
423
+ return prev; // Allow page scroll when not zoomed
424
+ }
425
+ } else if (isMagicMouse) {
426
+ // Apple Magic Mouse - less sensitive
427
+ zoomAmount = event.deltaY * -0.004;
428
+ shouldPreventDefault = true;
429
+ } else if (isRegularMouse) {
430
+ // Regular mouse wheel - medium sensitivity
431
+ zoomAmount = event.deltaY * -0.006;
399
432
  shouldPreventDefault = true;
400
433
  } else {
401
- return prev; // Allow page scroll when not zoomed
434
+ // Fallback for other input methods
435
+ zoomAmount = event.deltaY * -0.005;
436
+ shouldPreventDefault = true;
402
437
  }
403
- } else if (isMagicMouse) {
404
- // Apple Magic Mouse - less sensitive
405
- zoomAmount = event.deltaY * -0.004;
406
- shouldPreventDefault = true;
407
- } else if (isRegularMouse) {
408
- // Regular mouse wheel - medium sensitivity
409
- zoomAmount = event.deltaY * -0.006;
410
- shouldPreventDefault = true;
411
- } else {
412
- // Fallback for other input methods
413
- zoomAmount = event.deltaY * -0.005;
414
- shouldPreventDefault = true;
415
- }
416
-
417
- if (shouldPreventDefault) {
418
- event.preventDefault();
419
- event.stopPropagation();
420
- }
421
-
422
- // Add momentum for trackpad gestures
423
- const currentTime = Date.now();
424
- const timeDelta = currentTime - lastWheelTime.current;
425
- lastWheelTime.current = currentTime;
426
-
427
- // Calculate velocity for momentum (trackpad specific)
428
- if (isTrackpadPinch && timeDelta < 100) {
429
- const velocity = Math.abs(zoomAmount) / timeDelta;
430
- setMomentumZoom({ velocity, timestamp: currentTime });
431
-
432
- // Clear any existing momentum timeout
433
- if (momentumTimeoutRef.current) {
434
- clearTimeout(momentumTimeoutRef.current);
438
+
439
+ if (shouldPreventDefault) {
440
+ event.preventDefault();
441
+ event.stopPropagation();
435
442
  }
436
-
437
- // Apply momentum decay after gesture ends
438
- momentumTimeoutRef.current = setTimeout(() => {
439
- const decayFactor = 0.95;
440
- const minVelocity = 0.001;
441
-
442
- const applyMomentum = () => {
443
- setMomentumZoom(prev => {
444
- if (prev.velocity < minVelocity) return prev;
445
-
446
- const newVelocity = prev.velocity * decayFactor;
447
- const momentumZoomAmount = newVelocity * (zoomAmount > 0 ? 1 : -1);
448
-
449
- // Apply momentum zoom
450
- setImageStates(current => {
451
- const state = current[currentIndex] || {
452
- zoomLevel: 1,
453
- position: { x: 0, y: 0 },
454
- rotation: 0,
455
- bounds: { minX: 0, maxX: 0, minY: 0, maxY: 0 }
456
- };
457
-
458
- const newZoom = Math.max(0.1, Math.min(5, state.zoomLevel + momentumZoomAmount));
459
- if (newZoom === state.zoomLevel) return current;
460
-
461
- const newBounds = calculateBounds(newZoom, state.rotation);
462
- const constrainedPosition = constrainPosition(state.position, newBounds);
463
-
464
- return {
465
- ...current,
466
- [currentIndex]: {
467
- ...state,
468
- zoomLevel: newZoom,
469
- bounds: newBounds,
470
- position: constrainedPosition
471
- }
472
- };
443
+
444
+ // Add momentum for trackpad gestures
445
+ const currentTime = Date.now();
446
+ const timeDelta = currentTime - lastWheelTime.current;
447
+ lastWheelTime.current = currentTime;
448
+
449
+ // Calculate velocity for momentum (trackpad specific)
450
+ if (isTrackpadPinch && timeDelta < 100) {
451
+ const velocity = Math.abs(zoomAmount) / timeDelta;
452
+ setMomentumZoom({ velocity, timestamp: currentTime });
453
+
454
+ // Clear any existing momentum timeout
455
+ if (momentumTimeoutRef.current) {
456
+ clearTimeout(momentumTimeoutRef.current);
457
+ }
458
+
459
+ // Apply momentum decay after gesture ends
460
+ momentumTimeoutRef.current = setTimeout(() => {
461
+ const decayFactor = 0.95;
462
+ const minVelocity = 0.001;
463
+
464
+ const applyMomentum = () => {
465
+ setMomentumZoom(prev => {
466
+ if (prev.velocity < minVelocity) return prev;
467
+
468
+ const newVelocity = prev.velocity * decayFactor;
469
+ const momentumZoomAmount = newVelocity * (zoomAmount > 0 ? 1 : -1);
470
+
471
+ // Apply momentum zoom
472
+ setImageStates(current => {
473
+ const state = current[currentIndex] || {
474
+ zoomLevel: 1,
475
+ position: { x: 0, y: 0 },
476
+ rotation: 0,
477
+ bounds: { minX: 0, maxX: 0, minY: 0, maxY: 0 },
478
+ };
479
+
480
+ const newZoom = Math.max(0.1, Math.min(5, state.zoomLevel + momentumZoomAmount));
481
+ if (newZoom === state.zoomLevel) return current;
482
+
483
+ const newBounds = calculateBounds(newZoom, state.rotation);
484
+ const constrainedPosition = constrainPosition(state.position, newBounds);
485
+
486
+ return {
487
+ ...current,
488
+ [currentIndex]: {
489
+ ...state,
490
+ zoomLevel: newZoom,
491
+ bounds: newBounds,
492
+ position: constrainedPosition,
493
+ },
494
+ };
495
+ });
496
+
497
+ if (newVelocity >= minVelocity) {
498
+ requestAnimationFrame(applyMomentum);
499
+ }
500
+
501
+ return { velocity: newVelocity, timestamp: Date.now() };
473
502
  });
474
-
475
- if (newVelocity >= minVelocity) {
476
- requestAnimationFrame(applyMomentum);
477
- }
478
-
479
- return { velocity: newVelocity, timestamp: Date.now() };
480
- });
503
+ };
504
+
505
+ requestAnimationFrame(applyMomentum);
506
+ }, 50);
507
+ }
508
+
509
+ // Safe getBoundingClientRect call with error handling
510
+ let rect;
511
+ try {
512
+ rect = target.getBoundingClientRect();
513
+ } catch (error) {
514
+ console.warn('PhotoViewer: Error getting bounding rect', error);
515
+ return prev;
516
+ }
517
+
518
+ if (!rect || rect.width === 0 || rect.height === 0) return prev;
519
+
520
+ const centerX = rect.width / 2;
521
+ const centerY = rect.height / 2;
522
+ const cursorX = event.clientX - rect.left - centerX;
523
+ const cursorY = event.clientY - rect.top - centerY;
524
+
525
+ const oldZoom = currentState.zoomLevel;
526
+ const newZoom = Math.max(0.1, Math.min(5, oldZoom + zoomAmount));
527
+
528
+ if (newZoom !== oldZoom) {
529
+ const zoomFactor = newZoom / oldZoom;
530
+ const newBounds = calculateBounds(newZoom, currentState.rotation);
531
+
532
+ // Calculate new position to zoom towards cursor
533
+ const newPosition = {
534
+ x: currentState.position.x + cursorX * (1 - zoomFactor) * 0.5,
535
+ y: currentState.position.y + cursorY * (1 - zoomFactor) * 0.5,
481
536
  };
482
-
483
- requestAnimationFrame(applyMomentum);
484
- }, 50);
485
- }
486
-
487
- // Safe getBoundingClientRect call with error handling
488
- let rect;
489
- try {
490
- rect = target.getBoundingClientRect();
491
- } catch (error) {
492
- console.warn('PhotoViewer: Error getting bounding rect', error);
537
+
538
+ const constrainedPosition = constrainPosition(newPosition, newBounds);
539
+
540
+ return {
541
+ ...prev,
542
+ [currentIndex]: {
543
+ ...currentState,
544
+ zoomLevel: newZoom,
545
+ bounds: newBounds,
546
+ position: constrainedPosition,
547
+ },
548
+ };
549
+ }
493
550
  return prev;
494
- }
495
-
496
- if (!rect || rect.width === 0 || rect.height === 0) return prev;
497
-
498
- const centerX = rect.width / 2;
499
- const centerY = rect.height / 2;
500
- const cursorX = event.clientX - rect.left - centerX;
501
- const cursorY = event.clientY - rect.top - centerY;
502
-
503
- const oldZoom = currentState.zoomLevel;
504
- const newZoom = Math.max(0.1, Math.min(5, oldZoom + zoomAmount));
505
-
506
- if (newZoom !== oldZoom) {
507
- const zoomFactor = newZoom / oldZoom;
508
- const newBounds = calculateBounds(newZoom, currentState.rotation);
509
-
510
- // Calculate new position to zoom towards cursor
511
- const newPosition = {
512
- x: currentState.position.x + cursorX * (1 - zoomFactor) * 0.5,
513
- y: currentState.position.y + cursorY * (1 - zoomFactor) * 0.5
551
+ });
552
+ },
553
+ [isMounted, currentIndex, calculateBounds, constrainPosition]
554
+ );
555
+
556
+ // Handle double click to zoom with smart zoom levels
557
+ const handleDoubleClick = useCallback(
558
+ (event: React.MouseEvent) => {
559
+ if (!isMounted || !event || !event.currentTarget) return;
560
+
561
+ const target = event.currentTarget;
562
+ if (!target || typeof target.getBoundingClientRect !== 'function') return;
563
+
564
+ setImageStates(prev => {
565
+ const currentState = prev[currentIndex] || {
566
+ zoomLevel: 1,
567
+ position: { x: 0, y: 0 },
568
+ rotation: 0,
569
+ bounds: { minX: 0, maxX: 0, minY: 0, maxY: 0 },
514
570
  };
515
-
571
+
572
+ let rect;
573
+ try {
574
+ rect = target.getBoundingClientRect();
575
+ } catch (error) {
576
+ console.warn('PhotoViewer: Error getting bounding rect in double click', error);
577
+ return prev;
578
+ }
579
+
580
+ if (!rect || rect.width === 0 || rect.height === 0) return prev;
581
+
582
+ const centerX = rect.width / 2;
583
+ const centerY = rect.height / 2;
584
+ const cursorX = event.clientX - rect.left - centerX;
585
+ const cursorY = event.clientY - rect.top - centerY;
586
+
587
+ let newZoom: number;
588
+ let newPosition = { x: 0, y: 0 };
589
+
590
+ if (currentState.zoomLevel < 1.5) {
591
+ newZoom = 2;
592
+ // Zoom towards cursor
593
+ newPosition = {
594
+ x: -cursorX * 0.5,
595
+ y: -cursorY * 0.5,
596
+ };
597
+ } else if (currentState.zoomLevel < 3) {
598
+ newZoom = 4;
599
+ newPosition = {
600
+ x: -cursorX * 0.75,
601
+ y: -cursorY * 0.75,
602
+ };
603
+ } else {
604
+ newZoom = 1;
605
+ newPosition = { x: 0, y: 0 };
606
+ }
607
+
608
+ const newBounds = calculateBounds(newZoom, currentState.rotation);
516
609
  const constrainedPosition = constrainPosition(newPosition, newBounds);
517
-
610
+
518
611
  return {
519
612
  ...prev,
520
613
  [currentIndex]: {
521
614
  ...currentState,
522
615
  zoomLevel: newZoom,
523
616
  bounds: newBounds,
524
- position: constrainedPosition
525
- }
617
+ position: constrainedPosition,
618
+ },
526
619
  };
527
- }
528
- return prev;
529
- });
530
- }, [isMounted, currentIndex, calculateBounds, constrainPosition]);
620
+ });
621
+ },
622
+ [isMounted, currentIndex, calculateBounds, constrainPosition]
623
+ );
531
624
 
532
- // Handle double click to zoom with smart zoom levels
533
- const handleDoubleClick = useCallback((event: React.MouseEvent) => {
534
- if (!isMounted || !event || !event.currentTarget) return;
535
-
536
- const target = event.currentTarget;
537
- if (!target || typeof target.getBoundingClientRect !== 'function') return;
538
-
539
- setImageStates(prev => {
540
- const currentState = prev[currentIndex] || {
541
- zoomLevel: 1,
542
- position: { x: 0, y: 0 },
543
- rotation: 0,
544
- bounds: { minX: 0, maxX: 0, minY: 0, maxY: 0 }
545
- };
546
-
547
- let rect;
548
- try {
549
- rect = target.getBoundingClientRect();
550
- } catch (error) {
551
- console.warn('PhotoViewer: Error getting bounding rect in double click', error);
625
+ // Handle mouse down for panning
626
+ const handleMouseDown = useCallback(
627
+ (event: React.MouseEvent<HTMLDivElement | HTMLImageElement, MouseEvent>) => {
628
+ setImageStates(prev => {
629
+ const currentState = prev[currentIndex] || {
630
+ zoomLevel: 1,
631
+ position: { x: 0, y: 0 },
632
+ rotation: 0,
633
+ bounds: { minX: 0, maxX: 0, minY: 0, maxY: 0 },
634
+ };
635
+
636
+ if (currentState.zoomLevel > 1) {
637
+ event.preventDefault();
638
+ setIsDragging(true);
639
+ setStartDragPosition({
640
+ x: event.clientX - currentState.position.x,
641
+ y: event.clientY - currentState.position.y,
642
+ });
643
+ }
552
644
  return prev;
553
- }
554
-
555
- if (!rect || rect.width === 0 || rect.height === 0) return prev;
556
-
557
- const centerX = rect.width / 2;
558
- const centerY = rect.height / 2;
559
- const cursorX = event.clientX - rect.left - centerX;
560
- const cursorY = event.clientY - rect.top - centerY;
561
-
562
- let newZoom: number;
563
- let newPosition = { x: 0, y: 0 };
564
-
565
- if (currentState.zoomLevel < 1.5) {
566
- newZoom = 2;
567
- // Zoom towards cursor
568
- newPosition = {
569
- x: -cursorX * 0.5,
570
- y: -cursorY * 0.5
645
+ });
646
+ },
647
+ [currentIndex]
648
+ );
649
+
650
+ // Handle mouse move for panning with bounds
651
+ const handleMouseMove = useCallback(
652
+ (event: React.MouseEvent<HTMLDivElement | HTMLImageElement, MouseEvent>) => {
653
+ if (!isDragging) return;
654
+
655
+ setImageStates(prev => {
656
+ const currentState = prev[currentIndex] || {
657
+ zoomLevel: 1,
658
+ position: { x: 0, y: 0 },
659
+ rotation: 0,
660
+ bounds: { minX: 0, maxX: 0, minY: 0, maxY: 0 },
571
661
  };
572
- } else if (currentState.zoomLevel < 3) {
573
- newZoom = 4;
574
- newPosition = {
575
- x: -cursorX * 0.75,
576
- y: -cursorY * 0.75
662
+
663
+ const newPosition = {
664
+ x: event.clientX - startDragPosition.x,
665
+ y: event.clientY - startDragPosition.y,
577
666
  };
578
- } else {
579
- newZoom = 1;
580
- newPosition = { x: 0, y: 0 };
581
- }
582
-
583
- const newBounds = calculateBounds(newZoom, currentState.rotation);
584
- const constrainedPosition = constrainPosition(newPosition, newBounds);
585
-
586
- return {
587
- ...prev,
588
- [currentIndex]: {
589
- ...currentState,
590
- zoomLevel: newZoom,
591
- bounds: newBounds,
592
- position: constrainedPosition
593
- }
594
- };
595
- });
596
- }, [isMounted, currentIndex, calculateBounds, constrainPosition]);
597
667
 
598
- // Handle mouse down for panning
599
- const handleMouseDown = useCallback((event: React.MouseEvent<HTMLDivElement | HTMLImageElement, MouseEvent>) => {
600
- setImageStates(prev => {
601
- const currentState = prev[currentIndex] || {
602
- zoomLevel: 1,
603
- position: { x: 0, y: 0 },
604
- rotation: 0,
605
- bounds: { minX: 0, maxX: 0, minY: 0, maxY: 0 }
606
- };
607
-
608
- if (currentState.zoomLevel > 1) {
609
- event.preventDefault();
610
- setIsDragging(true);
611
- setStartDragPosition({
612
- x: event.clientX - currentState.position.x,
613
- y: event.clientY - currentState.position.y
614
- });
615
- }
616
- return prev;
617
- });
618
- }, [currentIndex]);
668
+ const constrainedPosition = constrainPosition(newPosition, currentState.bounds);
619
669
 
620
- // Handle mouse move for panning with bounds
621
- const handleMouseMove = useCallback((event: React.MouseEvent<HTMLDivElement | HTMLImageElement, MouseEvent>) => {
622
- if (!isDragging) return;
623
-
624
- setImageStates(prev => {
625
- const currentState = prev[currentIndex] || {
626
- zoomLevel: 1,
627
- position: { x: 0, y: 0 },
628
- rotation: 0,
629
- bounds: { minX: 0, maxX: 0, minY: 0, maxY: 0 }
630
- };
631
-
632
- const newPosition = {
633
- x: event.clientX - startDragPosition.x,
634
- y: event.clientY - startDragPosition.y
635
- };
636
-
637
- const constrainedPosition = constrainPosition(newPosition, currentState.bounds);
638
-
639
- return {
640
- ...prev,
641
- [currentIndex]: {
642
- ...currentState,
643
- position: constrainedPosition
644
- }
645
- };
646
- });
647
- }, [isDragging, startDragPosition, currentIndex, constrainPosition]);
670
+ return {
671
+ ...prev,
672
+ [currentIndex]: {
673
+ ...currentState,
674
+ position: constrainedPosition,
675
+ },
676
+ };
677
+ });
678
+ },
679
+ [isDragging, startDragPosition, currentIndex, constrainPosition]
680
+ );
648
681
 
649
682
  // Handle mouse up for panning
650
683
  const handleMouseUp = useCallback(() => {
651
684
  setIsDragging(false);
652
685
  }, []);
653
-
686
+
654
687
  // Touch handlers for mobile gestures with bounds
655
- const handleTouchStart = useCallback((event: React.TouchEvent<HTMLImageElement | HTMLDivElement>) => {
656
- if (!enableGestures) return;
657
-
658
- const touches = event.touches;
659
-
660
- // Always prevent default for multi-touch to stop page zoom
661
- if (touches.length > 1) {
662
- event.preventDefault();
663
- event.stopPropagation();
664
- }
665
-
666
- touchPointsRef.current = Array.from(touches).map(touch => ({
667
- x: touch.clientX,
668
- y: touch.clientY
669
- }));
670
-
671
- setImageStates(prev => {
672
- const currentState = prev[currentIndex] || {
673
- zoomLevel: 1,
674
- position: { x: 0, y: 0 },
675
- rotation: 0,
676
- bounds: { minX: 0, maxX: 0, minY: 0, maxY: 0 }
677
- };
678
-
679
- if (touches.length === 1 && currentState.zoomLevel > 1) {
680
- setIsDragging(true);
681
- setStartDragPosition({
682
- x: touches[0].clientX - currentState.position.x,
683
- y: touches[0].clientY - currentState.position.y
684
- });
685
- } else if (touches.length === 2) {
686
- const dx = touches[0].clientX - touches[1].clientX;
687
- const dy = touches[0].clientY - touches[1].clientY;
688
- lastDistanceRef.current = Math.sqrt(dx * dx + dy * dy);
689
-
690
- lastMidpointRef.current = {
691
- x: (touches[0].clientX + touches[1].clientX) / 2,
692
- y: (touches[0].clientY + touches[1].clientY) / 2
693
- };
688
+ const handleTouchStart = useCallback(
689
+ (event: React.TouchEvent<HTMLImageElement | HTMLDivElement>) => {
690
+ if (!enableGestures) return;
691
+
692
+ const touches = event.touches;
693
+
694
+ // Always prevent default for multi-touch to stop page zoom
695
+ if (touches.length > 1) {
696
+ event.preventDefault();
697
+ event.stopPropagation();
694
698
  }
695
- return prev;
696
- });
697
- }, [enableGestures, currentIndex]);
699
+
700
+ touchPointsRef.current = Array.from(touches).map(touch => ({
701
+ x: touch.clientX,
702
+ y: touch.clientY,
703
+ }));
704
+
705
+ setImageStates(prev => {
706
+ const currentState = prev[currentIndex] || {
707
+ zoomLevel: 1,
708
+ position: { x: 0, y: 0 },
709
+ rotation: 0,
710
+ bounds: { minX: 0, maxX: 0, minY: 0, maxY: 0 },
711
+ };
712
+
713
+ if (touches.length === 1 && currentState.zoomLevel > 1) {
714
+ setIsDragging(true);
715
+ setStartDragPosition({
716
+ x: touches[0].clientX - currentState.position.x,
717
+ y: touches[0].clientY - currentState.position.y,
718
+ });
719
+ } else if (touches.length === 2) {
720
+ const dx = touches[0].clientX - touches[1].clientX;
721
+ const dy = touches[0].clientY - touches[1].clientY;
722
+ lastDistanceRef.current = Math.sqrt(dx * dx + dy * dy);
723
+
724
+ lastMidpointRef.current = {
725
+ x: (touches[0].clientX + touches[1].clientX) / 2,
726
+ y: (touches[0].clientY + touches[1].clientY) / 2,
727
+ };
728
+ }
729
+ return prev;
730
+ });
731
+ },
732
+ [enableGestures, currentIndex]
733
+ );
698
734
 
699
735
  // Handle touch move for dragging and pinch zoom with bounds
700
- const handleTouchMove = useCallback((event: React.TouchEvent<HTMLImageElement | HTMLDivElement>) => {
701
- if (!enableGestures) return;
702
-
703
- const touches = event.touches;
704
-
705
- // Always prevent default for multi-touch gestures to stop page zoom
706
- if (touches.length > 1) {
707
- event.preventDefault();
708
- event.stopPropagation();
709
- }
710
-
711
- setImageStates(prev => {
712
- const currentState = prev[currentIndex] || {
713
- zoomLevel: 1,
714
- position: { x: 0, y: 0 },
715
- rotation: 0,
716
- bounds: { minX: 0, maxX: 0, minY: 0, maxY: 0 }
717
- };
718
-
719
- // Prevent default for single touch when zoomed in to avoid conflicts
720
- if (currentState.zoomLevel > 1 && touches.length === 1) {
736
+ const handleTouchMove = useCallback(
737
+ (event: React.TouchEvent<HTMLImageElement | HTMLDivElement>) => {
738
+ if (!enableGestures) return;
739
+
740
+ const touches = event.touches;
741
+
742
+ // Always prevent default for multi-touch gestures to stop page zoom
743
+ if (touches.length > 1) {
721
744
  event.preventDefault();
745
+ event.stopPropagation();
722
746
  }
723
-
724
- if (touches.length === 1 && isDragging && currentState.zoomLevel > 1) {
725
- const newPosition = {
726
- x: touches[0].clientX - startDragPosition.x,
727
- y: touches[0].clientY - startDragPosition.y
728
- };
729
- const constrainedPosition = constrainPosition(newPosition, currentState.bounds);
730
-
731
- return {
732
- ...prev,
733
- [currentIndex]: {
734
- ...currentState,
735
- position: constrainedPosition
736
- }
737
- };
738
- } else if (touches.length === 2 && lastDistanceRef.current !== null) {
739
- const dx = touches[0].clientX - touches[1].clientX;
740
- const dy = touches[0].clientY - touches[1].clientY;
741
- const distance = Math.sqrt(dx * dx + dy * dy);
742
-
743
- const zoomDelta = (distance - lastDistanceRef.current) * 0.005;
744
- lastDistanceRef.current = distance;
745
-
746
- const currentMidpoint = {
747
- x: (touches[0].clientX + touches[1].clientX) / 2,
748
- y: (touches[0].clientY + touches[1].clientY) / 2
747
+
748
+ setImageStates(prev => {
749
+ const currentState = prev[currentIndex] || {
750
+ zoomLevel: 1,
751
+ position: { x: 0, y: 0 },
752
+ rotation: 0,
753
+ bounds: { minX: 0, maxX: 0, minY: 0, maxY: 0 },
749
754
  };
750
-
751
- const oldZoom = currentState.zoomLevel;
752
- const newZoom = Math.max(0.1, Math.min(5, oldZoom + zoomDelta));
753
-
754
- if (newZoom !== oldZoom && lastMidpointRef.current) {
755
- let rect;
756
- try {
757
- rect = event.currentTarget.getBoundingClientRect();
758
- } catch (error) {
759
- console.warn('PhotoViewer: Error getting bounding rect in touch move', error);
760
- return prev;
761
- }
762
-
763
- if (!rect || rect.width === 0 || rect.height === 0) return prev;
764
-
765
- const centerX = rect.width / 2;
766
- const centerY = rect.height / 2;
767
- const midpointX = currentMidpoint.x - rect.left - centerX;
768
- const midpointY = currentMidpoint.y - rect.top - centerY;
769
-
770
- const zoomFactor = newZoom / oldZoom;
771
- const newBounds = calculateBounds(newZoom, currentState.rotation);
772
-
755
+
756
+ // Prevent default for single touch when zoomed in to avoid conflicts
757
+ if (currentState.zoomLevel > 1 && touches.length === 1) {
758
+ event.preventDefault();
759
+ }
760
+
761
+ if (touches.length === 1 && isDragging && currentState.zoomLevel > 1) {
773
762
  const newPosition = {
774
- x: currentState.position.x + midpointX * (1 - zoomFactor) * 0.5,
775
- y: currentState.position.y + midpointY * (1 - zoomFactor) * 0.5
763
+ x: touches[0].clientX - startDragPosition.x,
764
+ y: touches[0].clientY - startDragPosition.y,
776
765
  };
777
-
778
- const constrainedPosition = constrainPosition(newPosition, newBounds);
779
-
780
- lastMidpointRef.current = currentMidpoint;
781
-
766
+ const constrainedPosition = constrainPosition(newPosition, currentState.bounds);
767
+
782
768
  return {
783
769
  ...prev,
784
770
  [currentIndex]: {
785
771
  ...currentState,
786
- zoomLevel: newZoom,
787
- bounds: newBounds,
788
- position: constrainedPosition
789
- }
772
+ position: constrainedPosition,
773
+ },
790
774
  };
775
+ } else if (touches.length === 2 && lastDistanceRef.current !== null) {
776
+ const dx = touches[0].clientX - touches[1].clientX;
777
+ const dy = touches[0].clientY - touches[1].clientY;
778
+ const distance = Math.sqrt(dx * dx + dy * dy);
779
+
780
+ const zoomDelta = (distance - lastDistanceRef.current) * 0.005;
781
+ lastDistanceRef.current = distance;
782
+
783
+ const currentMidpoint = {
784
+ x: (touches[0].clientX + touches[1].clientX) / 2,
785
+ y: (touches[0].clientY + touches[1].clientY) / 2,
786
+ };
787
+
788
+ const oldZoom = currentState.zoomLevel;
789
+ const newZoom = Math.max(0.1, Math.min(5, oldZoom + zoomDelta));
790
+
791
+ if (newZoom !== oldZoom && lastMidpointRef.current) {
792
+ let rect;
793
+ try {
794
+ rect = event.currentTarget.getBoundingClientRect();
795
+ } catch (error) {
796
+ console.warn('PhotoViewer: Error getting bounding rect in touch move', error);
797
+ return prev;
798
+ }
799
+
800
+ if (!rect || rect.width === 0 || rect.height === 0) return prev;
801
+
802
+ const centerX = rect.width / 2;
803
+ const centerY = rect.height / 2;
804
+ const midpointX = currentMidpoint.x - rect.left - centerX;
805
+ const midpointY = currentMidpoint.y - rect.top - centerY;
806
+
807
+ const zoomFactor = newZoom / oldZoom;
808
+ const newBounds = calculateBounds(newZoom, currentState.rotation);
809
+
810
+ const newPosition = {
811
+ x: currentState.position.x + midpointX * (1 - zoomFactor) * 0.5,
812
+ y: currentState.position.y + midpointY * (1 - zoomFactor) * 0.5,
813
+ };
814
+
815
+ const constrainedPosition = constrainPosition(newPosition, newBounds);
816
+
817
+ lastMidpointRef.current = currentMidpoint;
818
+
819
+ return {
820
+ ...prev,
821
+ [currentIndex]: {
822
+ ...currentState,
823
+ zoomLevel: newZoom,
824
+ bounds: newBounds,
825
+ position: constrainedPosition,
826
+ },
827
+ };
828
+ }
829
+
830
+ lastMidpointRef.current = currentMidpoint;
791
831
  }
792
-
793
- lastMidpointRef.current = currentMidpoint;
794
- }
795
- return prev;
796
- });
797
- }, [isMounted, enableGestures, isDragging, startDragPosition, currentIndex, constrainPosition, calculateBounds]);
832
+ return prev;
833
+ });
834
+ },
835
+ [
836
+ isMounted,
837
+ enableGestures,
838
+ isDragging,
839
+ startDragPosition,
840
+ currentIndex,
841
+ constrainPosition,
842
+ calculateBounds,
843
+ ]
844
+ );
798
845
 
799
846
  // Handle touch end
800
847
  const handleTouchEnd = useCallback(() => {
@@ -808,7 +855,7 @@ export const usePhotoViewer = ({
808
855
  zoomLevel: 1,
809
856
  position: { x: 0, y: 0 },
810
857
  rotation: 0,
811
- bounds: { minX: 0, maxX: 0, minY: 0, maxY: 0 }
858
+ bounds: { minX: 0, maxX: 0, minY: 0, maxY: 0 },
812
859
  };
813
860
 
814
861
  return {
@@ -849,9 +896,9 @@ export const usePhotoViewer = ({
849
896
  zoomLevel: 1,
850
897
  position: { x: 0, y: 0 },
851
898
  rotation: 0,
852
- bounds: { minX: 0, maxX: 0, minY: 0, maxY: 0 }
853
- }
899
+ bounds: { minX: 0, maxX: 0, minY: 0, maxY: 0 },
900
+ },
854
901
  }));
855
- }
902
+ },
856
903
  };
857
- };
904
+ };