@shohojdhara/atomix 0.3.14 → 0.4.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 (343) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/build-tools/EXAMPLES.md +372 -0
  3. package/build-tools/README.md +242 -0
  4. package/build-tools/__tests__/error-handler.test.js +230 -0
  5. package/build-tools/__tests__/index.test.js +141 -0
  6. package/build-tools/__tests__/rollup-plugin.test.js +194 -0
  7. package/build-tools/__tests__/utils.test.js +161 -0
  8. package/build-tools/__tests__/vite-plugin.test.js +129 -0
  9. package/build-tools/__tests__/webpack-loader.test.js +190 -0
  10. package/build-tools/error-handler.js +308 -0
  11. package/build-tools/index.d.ts +44 -0
  12. package/build-tools/index.js +88 -0
  13. package/build-tools/package.json +50 -0
  14. package/build-tools/rollup-plugin.js +236 -0
  15. package/build-tools/types.d.ts +163 -0
  16. package/build-tools/utils.js +203 -0
  17. package/build-tools/vite-plugin.js +161 -0
  18. package/build-tools/webpack-loader.js +123 -0
  19. package/dist/atomix.css +21044 -2618
  20. package/dist/atomix.css.map +1 -1
  21. package/dist/atomix.min.css +77 -3
  22. package/dist/atomix.min.css.map +1 -1
  23. package/dist/build-tools/EXAMPLES.md +372 -0
  24. package/dist/build-tools/README.md +242 -0
  25. package/dist/build-tools/__tests__/error-handler.test.js +230 -0
  26. package/dist/build-tools/__tests__/index.test.js +141 -0
  27. package/dist/build-tools/__tests__/rollup-plugin.test.js +194 -0
  28. package/dist/build-tools/__tests__/utils.test.js +161 -0
  29. package/dist/build-tools/__tests__/vite-plugin.test.js +129 -0
  30. package/dist/build-tools/__tests__/webpack-loader.test.js +190 -0
  31. package/dist/build-tools/error-handler.js +308 -0
  32. package/dist/build-tools/index.d.ts +44 -0
  33. package/dist/build-tools/index.js +88 -0
  34. package/dist/build-tools/package.json +50 -0
  35. package/dist/build-tools/rollup-plugin.js +236 -0
  36. package/dist/build-tools/types.d.ts +163 -0
  37. package/dist/build-tools/utils.js +203 -0
  38. package/dist/build-tools/vite-plugin.js +161 -0
  39. package/dist/build-tools/webpack-loader.js +123 -0
  40. package/dist/charts.d.ts +1 -1
  41. package/dist/charts.js +86 -57
  42. package/dist/charts.js.map +1 -1
  43. package/dist/core.d.ts +1 -1
  44. package/dist/core.js +136 -112
  45. package/dist/core.js.map +1 -1
  46. package/dist/forms.d.ts +2 -5
  47. package/dist/forms.js +140 -128
  48. package/dist/forms.js.map +1 -1
  49. package/dist/heavy.d.ts +1 -1
  50. package/dist/heavy.js +136 -112
  51. package/dist/heavy.js.map +1 -1
  52. package/dist/index.d.ts +152 -78
  53. package/dist/index.esm.js +346 -340
  54. package/dist/index.esm.js.map +1 -1
  55. package/dist/index.js +359 -353
  56. package/dist/index.js.map +1 -1
  57. package/dist/index.min.js +1 -1
  58. package/dist/index.min.js.map +1 -1
  59. package/dist/layout.js.map +1 -1
  60. package/dist/theme.d.ts +9 -9
  61. package/dist/theme.js.map +1 -1
  62. package/package.json +23 -8
  63. package/scripts/atomix-cli.js +170 -73
  64. package/scripts/cli/__tests__/README.md +81 -0
  65. package/scripts/cli/__tests__/basic.test.js +115 -0
  66. package/scripts/cli/__tests__/component-generator.test.js +332 -0
  67. package/scripts/cli/__tests__/integration.test.js +327 -0
  68. package/scripts/cli/__tests__/test-setup.js +133 -0
  69. package/scripts/cli/__tests__/token-manager.test.js +251 -0
  70. package/scripts/cli/__tests__/utils.test.js +161 -0
  71. package/scripts/cli/component-generator.js +253 -299
  72. package/scripts/cli/dependency-checker.js +355 -0
  73. package/scripts/cli/interactive-init.js +46 -5
  74. package/scripts/cli/template-manager.js +0 -2
  75. package/scripts/cli/templates/common-templates.js +636 -0
  76. package/scripts/cli/templates/composable-templates.js +148 -126
  77. package/scripts/cli/templates/index.js +23 -16
  78. package/scripts/cli/templates/project-templates.js +151 -23
  79. package/scripts/cli/templates/react-templates.js +280 -210
  80. package/scripts/cli/templates/scss-templates.js +90 -91
  81. package/scripts/cli/templates/testing-templates.js +206 -27
  82. package/scripts/cli/templates/testing-utils.js +278 -0
  83. package/scripts/cli/templates/types-templates.js +70 -56
  84. package/scripts/cli/theme-bridge.js +8 -2
  85. package/scripts/cli/token-manager.js +318 -206
  86. package/scripts/cli/utils.js +0 -1
  87. package/src/components/Accordion/Accordion.stories.tsx +358 -850
  88. package/src/components/Accordion/Accordion.test.tsx +70 -50
  89. package/src/components/Accordion/Accordion.tsx +99 -94
  90. package/src/components/AtomixGlass/AtomixGlass.test.tsx +1 -1
  91. package/src/components/AtomixGlass/AtomixGlass.tsx +80 -39
  92. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +103 -81
  93. package/src/components/AtomixGlass/GlassFilter.tsx +9 -16
  94. package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +8 -7
  95. package/src/components/AtomixGlass/glass-utils.ts +6 -5
  96. package/src/components/AtomixGlass/shader-utils.ts +133 -52
  97. package/src/components/AtomixGlass/stories/Customization.stories.tsx +131 -0
  98. package/src/components/AtomixGlass/stories/Examples.stories.tsx +2957 -2853
  99. package/src/components/AtomixGlass/stories/Modes.stories.tsx +1 -1
  100. package/src/components/AtomixGlass/stories/Overview.stories.tsx +348 -0
  101. package/src/components/AtomixGlass/stories/Performance.stories.tsx +103 -0
  102. package/src/components/AtomixGlass/stories/Playground.stories.tsx +51 -36
  103. package/src/components/AtomixGlass/stories/{ShaderVariants.stories.tsx → Shaders.stories.tsx} +2 -2
  104. package/src/components/AtomixGlass/stories/shared-components.tsx +90 -190
  105. package/src/components/Avatar/Avatar.stories.tsx +195 -0
  106. package/src/components/Avatar/Avatar.tsx +58 -56
  107. package/src/components/Badge/Badge.stories.tsx +122 -352
  108. package/src/components/Badge/Badge.test.tsx +41 -41
  109. package/src/components/Badge/Badge.tsx +64 -62
  110. package/src/components/Block/Block.stories.tsx +30 -11
  111. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +142 -23
  112. package/src/components/Breadcrumb/Breadcrumb.tsx +62 -60
  113. package/src/components/Button/Button.stories.tsx +454 -1126
  114. package/src/components/Button/Button.test.tsx +123 -0
  115. package/src/components/Button/Button.tsx +88 -60
  116. package/src/components/Button/ButtonGroup.stories.tsx +376 -215
  117. package/src/components/Button/ButtonGroup.tsx +4 -15
  118. package/src/components/Callout/Callout.stories.tsx +316 -568
  119. package/src/components/Card/Card.stories.tsx +292 -81
  120. package/src/components/Card/Card.tsx +30 -14
  121. package/src/components/Chart/AreaChart.tsx +1 -1
  122. package/src/components/Chart/CandlestickChart.tsx +23 -16
  123. package/src/components/Chart/Chart.stories.tsx +153 -16
  124. package/src/components/Chart/Chart.tsx +40 -44
  125. package/src/components/Chart/ChartRenderer.tsx +39 -12
  126. package/src/components/Chart/ChartToolbar.tsx +21 -5
  127. package/src/components/Chart/DonutChart.tsx +1 -1
  128. package/src/components/Chart/FunnelChart.tsx +4 -1
  129. package/src/components/Chart/GaugeChart.tsx +3 -1
  130. package/src/components/Chart/HeatmapChart.tsx +50 -37
  131. package/src/components/Chart/LineChart.tsx +3 -2
  132. package/src/components/Chart/MultiAxisChart.tsx +24 -16
  133. package/src/components/Chart/RadarChart.tsx +19 -17
  134. package/src/components/Chart/ScatterChart.tsx +29 -21
  135. package/src/components/ColorModeToggle/ColorModeToggle.stories.tsx +152 -66
  136. package/src/components/ColorModeToggle/ColorModeToggle.tsx +15 -3
  137. package/src/components/Countdown/Countdown.stories.tsx +114 -7
  138. package/src/components/DataTable/DataTable.stories.tsx +349 -144
  139. package/src/components/DataTable/DataTable.test.tsx +26 -148
  140. package/src/components/DataTable/DataTable.tsx +485 -456
  141. package/src/components/DatePicker/DatePicker.stories.tsx +310 -1066
  142. package/src/components/DatePicker/DatePicker.tsx +31 -26
  143. package/src/components/Dropdown/Dropdown.stories.tsx +153 -36
  144. package/src/components/Dropdown/Dropdown.tsx +313 -299
  145. package/src/components/EdgePanel/EdgePanel.stories.tsx +221 -25
  146. package/src/components/EdgePanel/EdgePanel.tsx +1 -3
  147. package/src/components/Footer/Footer.stories.tsx +396 -327
  148. package/src/components/Footer/Footer.tsx +130 -128
  149. package/src/components/Footer/FooterLink.tsx +2 -2
  150. package/src/components/Form/Checkbox.stories.tsx +140 -6
  151. package/src/components/Form/Checkbox.test.tsx +63 -0
  152. package/src/components/Form/Checkbox.tsx +122 -78
  153. package/src/components/Form/Form.stories.tsx +110 -19
  154. package/src/components/Form/FormGroup.stories.tsx +127 -4
  155. package/src/components/Form/Input.stories.tsx +22 -39
  156. package/src/components/Form/Input.test.tsx +38 -44
  157. package/src/components/Form/Radio.stories.tsx +146 -17
  158. package/src/components/Form/Radio.tsx +68 -66
  159. package/src/components/Form/Select.stories.tsx +140 -8
  160. package/src/components/Form/Select.tsx +184 -182
  161. package/src/components/Form/Textarea.stories.tsx +149 -6
  162. package/src/components/Form/Textarea.test.tsx +27 -32
  163. package/src/components/Hero/Hero.stories.tsx +372 -38
  164. package/src/components/Hero/Hero.tsx +201 -55
  165. package/src/components/Icon/index.ts +7 -1
  166. package/src/components/List/List.stories.tsx +141 -3
  167. package/src/components/List/List.tsx +19 -23
  168. package/src/components/Modal/Modal.stories.tsx +183 -43
  169. package/src/components/Modal/Modal.tsx +130 -127
  170. package/src/components/Navigation/Menu/MegaMenu.tsx +70 -70
  171. package/src/components/Navigation/Nav/NavDropdown.tsx +1 -5
  172. package/src/components/Navigation/SideMenu/SideMenu.stories.tsx +128 -28
  173. package/src/components/Navigation/SideMenu/SideMenu.tsx +5 -7
  174. package/src/components/Navigation/SideMenu/SideMenuItem.tsx +4 -5
  175. package/src/components/Pagination/Pagination.stories.tsx +7 -4
  176. package/src/components/Pagination/Pagination.tsx +199 -202
  177. package/src/components/PhotoViewer/PhotoViewer.tsx +4 -1
  178. package/src/components/Popover/Popover.stories.tsx +354 -97
  179. package/src/components/Popover/Popover.tsx +41 -37
  180. package/src/components/Progress/Progress.stories.tsx +160 -7
  181. package/src/components/River/River.stories.tsx +3 -2
  182. package/src/components/SectionIntro/SectionIntro.stories.tsx +239 -47
  183. package/src/components/Slider/Slider.stories.tsx +12 -4
  184. package/src/components/Spinner/Spinner.stories.tsx +104 -8
  185. package/src/components/Spinner/Spinner.test.tsx +23 -23
  186. package/src/components/Spinner/Spinner.tsx +43 -46
  187. package/src/components/Steps/Steps.stories.tsx +173 -42
  188. package/src/components/Tabs/Tabs.stories.tsx +141 -12
  189. package/src/components/Tabs/Tabs.tsx +74 -72
  190. package/src/components/Testimonial/Testimonial.stories.tsx +120 -3
  191. package/src/components/Todo/Todo.stories.tsx +198 -9
  192. package/src/components/Toggle/Toggle.stories.tsx +137 -36
  193. package/src/components/Toggle/Toggle.test.tsx +65 -70
  194. package/src/components/Toggle/Toggle.tsx +4 -1
  195. package/src/components/Tooltip/Tooltip.stories.tsx +194 -100
  196. package/src/components/Tooltip/Tooltip.tsx +104 -106
  197. package/src/components/Upload/Upload.stories.tsx +241 -150
  198. package/src/components/Upload/Upload.tsx +287 -283
  199. package/src/components/VideoPlayer/VideoPlayer.tsx +6 -1
  200. package/src/components/index.ts +13 -2
  201. package/src/layouts/Grid/Grid.stories.tsx +9 -3
  202. package/src/layouts/MasonryGrid/MasonryGrid.tsx +5 -1
  203. package/src/lib/README.md +2 -2
  204. package/src/lib/__tests__/theme-tools.test.ts +219 -0
  205. package/src/lib/composables/index.ts +2 -2
  206. package/src/lib/composables/shared-mouse-tracker.ts +13 -14
  207. package/src/lib/composables/useAtomixGlass.ts +126 -97
  208. package/src/lib/composables/useChartExport.ts +3 -8
  209. package/src/lib/composables/useDataTable.ts +72 -43
  210. package/src/lib/composables/useHero.ts +58 -14
  211. package/src/lib/composables/useHeroBackgroundSlider.ts +2 -9
  212. package/src/lib/composables/useInput.ts +10 -8
  213. package/src/lib/composables/useSideMenu.ts +6 -5
  214. package/src/lib/composables/useTooltip.ts +1 -2
  215. package/src/lib/composables/useVideoPlayer.ts +44 -35
  216. package/src/lib/config/index.ts +154 -154
  217. package/src/lib/constants/components.ts +9 -32
  218. package/src/lib/constants/cssVariables.ts +29 -29
  219. package/src/lib/hooks/__tests__/useComponentCustomization.test.ts +2 -6
  220. package/src/lib/hooks/index.ts +1 -1
  221. package/src/lib/hooks/useComponentCustomization.ts +11 -17
  222. package/src/lib/hooks/usePerformanceMonitor.ts +6 -7
  223. package/src/lib/patterns/__tests__/slots.test.ts +1 -1
  224. package/src/lib/patterns/index.ts +1 -1
  225. package/src/lib/patterns/slots.tsx +8 -13
  226. package/src/lib/storybook/InteractiveDemo.tsx +13 -18
  227. package/src/lib/storybook/PreviewContainer.tsx +1 -1
  228. package/src/lib/storybook/VariantsGrid.tsx +3 -7
  229. package/src/lib/storybook/index.ts +1 -1
  230. package/src/lib/theme/adapters/cssVariableMapper.ts +47 -74
  231. package/src/lib/theme/adapters/index.ts +3 -9
  232. package/src/lib/theme/adapters/themeAdapter.ts +41 -26
  233. package/src/lib/theme/config/index.ts +1 -1
  234. package/src/lib/theme/config/types.ts +2 -2
  235. package/src/lib/theme/config/validator.ts +10 -5
  236. package/src/lib/theme/constants/constants.ts +2 -2
  237. package/src/lib/theme/constants/index.ts +1 -2
  238. package/src/lib/theme/core/__tests__/createTheme.test.ts +20 -22
  239. package/src/lib/theme/core/composeTheme.ts +32 -26
  240. package/src/lib/theme/core/createTheme.ts +1 -1
  241. package/src/lib/theme/core/createThemeObject.ts +308 -301
  242. package/src/lib/theme/core/index.ts +3 -3
  243. package/src/lib/theme/devtools/CLI.ts +107 -105
  244. package/src/lib/theme/devtools/Comparator.tsx +50 -32
  245. package/src/lib/theme/devtools/DesignTokensCustomizer.stories.tsx +50 -48
  246. package/src/lib/theme/devtools/DesignTokensCustomizer.tsx +257 -63
  247. package/src/lib/theme/devtools/Inspector.tsx +75 -60
  248. package/src/lib/theme/devtools/LiveEditor.tsx +97 -76
  249. package/src/lib/theme/devtools/Preview.tsx +150 -106
  250. package/src/lib/theme/devtools/ThemeValidator.ts +29 -21
  251. package/src/lib/theme/devtools/index.ts +3 -9
  252. package/src/lib/theme/devtools/useHistory.ts +23 -21
  253. package/src/lib/theme/errors/errors.ts +12 -11
  254. package/src/lib/theme/errors/index.ts +2 -7
  255. package/src/lib/theme/generators/generateCSS.ts +9 -13
  256. package/src/lib/theme/generators/generateCSSNested.ts +1 -6
  257. package/src/lib/theme/generators/generateCSSVariables.ts +673 -630
  258. package/src/lib/theme/generators/index.ts +1 -4
  259. package/src/lib/theme/i18n/index.ts +1 -1
  260. package/src/lib/theme/i18n/rtl.ts +13 -13
  261. package/src/lib/theme/index.ts +7 -16
  262. package/src/lib/theme/runtime/ThemeApplicator.ts +4 -4
  263. package/src/lib/theme/runtime/ThemeContext.tsx +1 -1
  264. package/src/lib/theme/runtime/ThemeErrorBoundary.tsx +19 -23
  265. package/src/lib/theme/runtime/ThemeProvider.tsx +230 -239
  266. package/src/lib/theme/runtime/__tests__/ThemeProvider.integration.test.tsx +1 -1
  267. package/src/lib/theme/runtime/__tests__/ThemeProvider.test.tsx +24 -29
  268. package/src/lib/theme/runtime/index.ts +2 -5
  269. package/src/lib/theme/runtime/useTheme.ts +18 -18
  270. package/src/lib/theme/runtime/useThemeTokens.ts +22 -22
  271. package/src/lib/theme/test/testTheme.ts +15 -16
  272. package/src/lib/theme/tokens/index.ts +2 -7
  273. package/src/lib/theme/tokens/tokens.ts +25 -24
  274. package/src/lib/theme/types.ts +428 -411
  275. package/src/lib/theme/utils/__tests__/themeValidation.test.ts +3 -3
  276. package/src/lib/theme/utils/componentTheming.ts +18 -18
  277. package/src/lib/theme/utils/domUtils.ts +277 -289
  278. package/src/lib/theme/utils/index.ts +1 -2
  279. package/src/lib/theme/utils/injectCSS.ts +10 -14
  280. package/src/lib/theme/utils/naming.ts +20 -16
  281. package/src/lib/theme/utils/themeHelpers.ts +10 -12
  282. package/src/lib/theme/utils/themeUtils.ts +85 -86
  283. package/src/lib/theme/utils/themeValidation.ts +82 -33
  284. package/src/lib/theme-tools.ts +8 -6
  285. package/src/lib/types/components.ts +172 -71
  286. package/src/lib/types/partProps.ts +1 -1
  287. package/src/lib/utils/__tests__/csv.test.ts +45 -0
  288. package/src/lib/utils/componentUtils.ts +8 -12
  289. package/src/lib/utils/csv.ts +19 -0
  290. package/src/lib/utils/dataTableExport.ts +2 -15
  291. package/src/lib/utils/fontPreloader.ts +10 -19
  292. package/src/lib/utils/icons.ts +4 -1
  293. package/src/lib/utils/index.ts +2 -6
  294. package/src/lib/utils/memoryMonitor.ts +10 -8
  295. package/src/lib/utils/themeNaming.ts +2 -2
  296. package/src/styles/01-settings/_index.scss +1 -1
  297. package/src/styles/01-settings/_settings.accordion.scss +28 -7
  298. package/src/styles/01-settings/_settings.colors.scss +11 -11
  299. package/src/styles/01-settings/_settings.design-tokens.scss +61 -50
  300. package/src/styles/01-settings/_settings.navbar.scss +1 -1
  301. package/src/styles/01-settings/_settings.spacing.scss +3 -4
  302. package/src/styles/01-settings/_settings.tooltip.scss +1 -1
  303. package/src/styles/01-settings/_settings.typography.scss +4 -4
  304. package/src/styles/02-tools/_tools.button.scss +51 -21
  305. package/src/styles/02-tools/_tools.utility-api.scss +38 -12
  306. package/src/styles/03-generic/_generic.root.scss +4 -3
  307. package/src/styles/06-components/_components.accordion.scss +56 -14
  308. package/src/styles/06-components/_components.atomix-glass.scss +13 -9
  309. package/src/styles/06-components/_components.button.scss +16 -4
  310. package/src/styles/06-components/_components.callout.scss +27 -21
  311. package/src/styles/06-components/_components.card.scss +5 -14
  312. package/src/styles/06-components/_components.chart.scss +22 -19
  313. package/src/styles/06-components/_components.checkbox.scss +25 -17
  314. package/src/styles/06-components/_components.color-mode-toggle.scss +3 -1
  315. package/src/styles/06-components/_components.edge-panel.scss +9 -2
  316. package/src/styles/06-components/_components.footer.scss +1 -1
  317. package/src/styles/06-components/_components.side-menu.scss +5 -5
  318. package/src/styles/06-components/_components.toggle.scss +18 -0
  319. package/src/styles/06-components/_index.scss +1 -1
  320. package/src/styles/06-components/old.chart.styles.scss +0 -2
  321. package/src/styles/99-utilities/_index.scss +2 -0
  322. package/src/styles/99-utilities/_utilities.border.scss +69 -27
  323. package/src/styles/99-utilities/_utilities.display.scss +1 -1
  324. package/src/styles/99-utilities/_utilities.opacity.scss +10 -0
  325. package/src/styles/99-utilities/_utilities.position.scss +16 -9
  326. package/src/styles/99-utilities/_utilities.scss +2 -0
  327. package/src/styles/99-utilities/_utilities.sizes.scss +47 -18
  328. package/src/styles/99-utilities/_utilities.spacing.scss +118 -66
  329. package/src/styles/99-utilities/_utilities.text-gradient.scss +45 -0
  330. package/src/styles/99-utilities/_utilities.text.scss +67 -46
  331. package/themes/dark-complementary/README.md +98 -0
  332. package/themes/dark-complementary/index.scss +158 -0
  333. package/themes/default-light/README.md +81 -0
  334. package/themes/default-light/index.scss +154 -0
  335. package/themes/high-contrast/README.md +105 -0
  336. package/themes/high-contrast/index.scss +172 -0
  337. package/themes/test-theme/README.md +38 -0
  338. package/themes/test-theme/index.scss +47 -0
  339. package/scripts/cli/templates-original-backup.js +0 -1655
  340. package/scripts/cli/templates_backup.js +0 -684
  341. package/src/components/AtomixGlass/stories/AtomixGlass.stories.tsx +0 -1438
  342. package/src/lib/composables/useButton.ts +0 -93
  343. package/src/lib/composables/useCheckbox.ts +0 -70
@@ -1,4 +1,12 @@
1
- import React, { useRef, useState, useCallback, createContext, useContext, useEffect, memo } from 'react';
1
+ import React, {
2
+ useRef,
3
+ useState,
4
+ useCallback,
5
+ createContext,
6
+ useContext,
7
+ useEffect,
8
+ memo,
9
+ } from 'react';
2
10
  import { DROPDOWN } from '../../lib/constants/components';
3
11
  import { AtomixGlass } from '../AtomixGlass/AtomixGlass';
4
12
  import type {
@@ -27,91 +35,93 @@ const DropdownContext = createContext<DropdownContextType>({
27
35
  /**
28
36
  * DropdownItem component for menu items
29
37
  */
30
- export const DropdownItem: React.FC<DropdownItemProps> = memo(({
31
- children,
32
- href,
33
- active = false,
34
- disabled = false,
35
- icon,
36
- onClick,
37
- className = '',
38
- LinkComponent,
39
- ...props
40
- }) => {
41
- const { close } = useContext(DropdownContext);
42
-
43
- const handleClick = (e: React.MouseEvent<HTMLButtonElement | HTMLAnchorElement>) => {
44
- if (disabled) {
45
- e.preventDefault();
46
- return;
47
- }
38
+ export const DropdownItem: React.FC<DropdownItemProps> = memo(
39
+ ({
40
+ children,
41
+ href,
42
+ active = false,
43
+ disabled = false,
44
+ icon,
45
+ onClick,
46
+ className = '',
47
+ LinkComponent,
48
+ ...props
49
+ }) => {
50
+ const { close } = useContext(DropdownContext);
51
+
52
+ const handleClick = (e: React.MouseEvent<HTMLButtonElement | HTMLAnchorElement>) => {
53
+ if (disabled) {
54
+ e.preventDefault();
55
+ return;
56
+ }
48
57
 
49
- if (onClick) {
50
- onClick(e);
51
- }
58
+ if (onClick) {
59
+ onClick(e);
60
+ }
61
+
62
+ // Always close the dropdown when an item is clicked
63
+ close();
64
+ };
52
65
 
53
- // Always close the dropdown when an item is clicked
54
- close();
55
- };
66
+ const itemClasses = [
67
+ 'c-dropdown__menu-item',
68
+ active ? 'is-active' : '',
69
+ disabled ? 'is-disabled' : '',
70
+ className,
71
+ ]
72
+ .filter(Boolean)
73
+ .join(' ');
74
+
75
+ const linkProps = {
76
+ href,
77
+ className: itemClasses,
78
+ onClick: handleClick,
79
+ role: 'menuitem',
80
+ tabIndex: 0,
81
+ ...props,
82
+ };
56
83
 
57
- const itemClasses = [
58
- 'c-dropdown__menu-item',
59
- active ? 'is-active' : '',
60
- disabled ? 'is-disabled' : '',
61
- className,
62
- ]
63
- .filter(Boolean)
64
- .join(' ');
84
+ if (href && !disabled) {
85
+ return (
86
+ <li>
87
+ {LinkComponent ? (
88
+ (() => {
89
+ const Component = LinkComponent as React.ComponentType<any>;
90
+ return (
91
+ <Component {...linkProps}>
92
+ {icon && <span className="c-dropdown__menu-item-icon">{icon}</span>}
93
+ {children}
94
+ </Component>
95
+ );
96
+ })()
97
+ ) : (
98
+ <a {...linkProps}>
99
+ {icon && <span className="c-dropdown__menu-item-icon">{icon}</span>}
100
+ {children}
101
+ </a>
102
+ )}
103
+ </li>
104
+ );
105
+ }
65
106
 
66
- const linkProps = {
67
- href,
68
- className: itemClasses,
69
- onClick: handleClick,
70
- role: 'menuitem',
71
- tabIndex: 0,
72
- ...props,
73
- };
74
-
75
- if (href && !disabled) {
76
107
  return (
77
108
  <li>
78
- {LinkComponent ? (
79
- (() => {
80
- const Component = LinkComponent as React.ComponentType<any>;
81
- return (
82
- <Component {...linkProps}>
83
- {icon && <span className="c-dropdown__menu-item-icon">{icon}</span>}
84
- {children}
85
- </Component>
86
- );
87
- })()
88
- ) : (
89
- <a {...linkProps}>
90
- {icon && <span className="c-dropdown__menu-item-icon">{icon}</span>}
91
- {children}
92
- </a>
93
- )}
109
+ <button
110
+ type="button"
111
+ className={itemClasses}
112
+ onClick={handleClick}
113
+ disabled={disabled}
114
+ role="menuitem"
115
+ tabIndex={0}
116
+ {...props}
117
+ >
118
+ {icon && <span className="c-dropdown__menu-item-icon">{icon}</span>}
119
+ {children}
120
+ </button>
94
121
  </li>
95
122
  );
96
123
  }
97
-
98
- return (
99
- <li>
100
- <button
101
- type="button"
102
- className={itemClasses}
103
- onClick={handleClick}
104
- disabled={disabled}
105
- role="menuitem"
106
- tabIndex={0}
107
- {...props}
108
- >
109
- {icon && <span className="c-dropdown__menu-item-icon">{icon}</span>}
110
- {children}
111
- </button>
112
- </li>
113
- );
114
- });
124
+ );
115
125
 
116
126
  /**
117
127
  * DropdownDivider component for separating groups of items
@@ -123,254 +133,258 @@ export const DropdownDivider: React.FC<DropdownDividerProps> = memo(({ className
123
133
  /**
124
134
  * DropdownHeader component for section headers
125
135
  */
126
- export const DropdownHeader: React.FC<DropdownHeaderProps> = memo(({ children, className = '' }) => {
127
- return <li className={`c-dropdown__header ${className}`}>{children}</li>;
128
- });
136
+ export const DropdownHeader: React.FC<DropdownHeaderProps> = memo(
137
+ ({ children, className = '' }) => {
138
+ return <li className={`c-dropdown__header ${className}`}>{children}</li>;
139
+ }
140
+ );
129
141
 
130
142
  /**
131
143
  * Dropdown component for creating dropdown menus
132
144
  */
133
- export const Dropdown: React.FC<DropdownProps> = memo(({
134
- children,
135
- menu,
136
- placement = 'bottom-start',
137
- trigger = 'click',
138
- offset = DROPDOWN.DEFAULTS.OFFSET,
139
- isOpen: controlledIsOpen,
140
- onOpenChange,
141
- closeOnClickOutside = true,
142
- closeOnEscape = true,
143
- maxHeight,
144
- minWidth = DROPDOWN.DEFAULTS.MIN_WIDTH,
145
- variant,
146
- className = '',
147
- style,
148
- glass,
149
- ...props
150
- }) => {
151
- // Set up controlled vs uncontrolled state
152
- const [uncontrolledIsOpen, setUncontrolledIsOpen] = useState(false);
153
- const isControlled = controlledIsOpen !== undefined;
154
- const isOpen = isControlled ? controlledIsOpen : uncontrolledIsOpen;
155
-
156
- // Create refs
157
- const dropdownRef = useRef<HTMLDivElement>(null);
158
- const toggleRef = useRef<HTMLDivElement>(null);
159
- const menuRef = useRef<HTMLDivElement>(null);
160
-
161
- // Generate unique ID
162
- const dropdownId = useRef(`dropdown-${Math.random().toString(36).substring(2, 9)}`).current;
163
-
164
- // State change handlers
165
- const setIsOpen = useCallback(
166
- (nextIsOpen: boolean) => {
167
- if (!isControlled) {
168
- setUncontrolledIsOpen(nextIsOpen);
169
- }
170
- if (onOpenChange) {
171
- onOpenChange(nextIsOpen);
172
- }
173
- },
174
- [isControlled, onOpenChange]
175
- );
176
-
177
- const toggle = useCallback(() => setIsOpen(!isOpen), [isOpen, setIsOpen]);
178
-
179
- const close = useCallback(() => {
180
- setIsOpen(false);
181
- // Return focus to the toggle button after closing
182
- setTimeout(() => {
183
- toggleRef.current?.focus();
184
- }, 0);
185
- }, [setIsOpen]);
186
-
187
- // Click outside handler
188
- useEffect(() => {
189
- if (!isOpen || !closeOnClickOutside) return undefined;
190
-
191
- const handleClickOutside = (e: MouseEvent) => {
192
- if (dropdownRef.current && !dropdownRef.current.contains(e.target as Node)) {
193
- close();
194
- }
195
- };
196
-
197
- document.addEventListener('mousedown', handleClickOutside);
198
- return () => document.removeEventListener('mousedown', handleClickOutside);
199
- }, [isOpen, closeOnClickOutside, close]);
145
+ export const Dropdown: React.FC<DropdownProps> = memo(
146
+ ({
147
+ children,
148
+ menu,
149
+ placement = 'bottom-start',
150
+ trigger = 'click',
151
+ offset = DROPDOWN.DEFAULTS.OFFSET,
152
+ isOpen: controlledIsOpen,
153
+ onOpenChange,
154
+ closeOnClickOutside = true,
155
+ closeOnEscape = true,
156
+ maxHeight,
157
+ minWidth = DROPDOWN.DEFAULTS.MIN_WIDTH,
158
+ variant,
159
+ className = '',
160
+ style,
161
+ glass,
162
+ ...props
163
+ }) => {
164
+ // Set up controlled vs uncontrolled state
165
+ const [uncontrolledIsOpen, setUncontrolledIsOpen] = useState(false);
166
+ const isControlled = controlledIsOpen !== undefined;
167
+ const isOpen = isControlled ? controlledIsOpen : uncontrolledIsOpen;
168
+
169
+ // Create refs
170
+ const dropdownRef = useRef<HTMLDivElement>(null);
171
+ const toggleRef = useRef<HTMLDivElement>(null);
172
+ const menuRef = useRef<HTMLDivElement>(null);
173
+
174
+ // Generate unique ID
175
+ const dropdownId = useRef(`dropdown-${Math.random().toString(36).substring(2, 9)}`).current;
176
+
177
+ // State change handlers
178
+ const setIsOpen = useCallback(
179
+ (nextIsOpen: boolean) => {
180
+ if (!isControlled) {
181
+ setUncontrolledIsOpen(nextIsOpen);
182
+ }
183
+ if (onOpenChange) {
184
+ onOpenChange(nextIsOpen);
185
+ }
186
+ },
187
+ [isControlled, onOpenChange]
188
+ );
200
189
 
201
- // Escape key handler
202
- useEffect(() => {
203
- if (!isOpen || !closeOnEscape) return undefined;
190
+ const toggle = useCallback(() => setIsOpen(!isOpen), [isOpen, setIsOpen]);
204
191
 
205
- const handleKeyDown = (e: KeyboardEvent) => {
206
- if (e.key === 'Escape') {
207
- close();
208
- }
209
- };
192
+ const close = useCallback(() => {
193
+ setIsOpen(false);
194
+ // Return focus to the toggle button after closing
195
+ setTimeout(() => {
196
+ toggleRef.current?.focus();
197
+ }, 0);
198
+ }, [setIsOpen]);
210
199
 
211
- document.addEventListener('keydown', handleKeyDown);
212
- return () => document.removeEventListener('keydown', handleKeyDown);
213
- }, [isOpen, closeOnEscape, close]);
200
+ // Click outside handler
201
+ useEffect(() => {
202
+ if (!isOpen || !closeOnClickOutside) return undefined;
214
203
 
215
- // Keyboard navigation
216
- const handleKeyDown = useCallback((e: React.KeyboardEvent) => {
217
- if (!menuRef.current) return;
204
+ const handleClickOutside = (e: MouseEvent) => {
205
+ if (dropdownRef.current && !dropdownRef.current.contains(e.target as Node)) {
206
+ close();
207
+ }
208
+ };
218
209
 
219
- const focusableItems = menuRef.current.querySelectorAll<HTMLElement>(
220
- '[role="menuitem"]:not([disabled])'
221
- );
222
- if (!focusableItems.length) return;
210
+ document.addEventListener('mousedown', handleClickOutside);
211
+ return () => document.removeEventListener('mousedown', handleClickOutside);
212
+ }, [isOpen, closeOnClickOutside, close]);
223
213
 
224
- const currentIndex = Array.from(focusableItems).findIndex(
225
- item => item === document.activeElement
226
- );
214
+ // Escape key handler
215
+ useEffect(() => {
216
+ if (!isOpen || !closeOnEscape) return undefined;
227
217
 
228
- switch (e.key) {
229
- case 'ArrowDown':
230
- e.preventDefault();
231
- if (currentIndex < focusableItems.length - 1) {
232
- focusableItems[currentIndex + 1]?.focus();
233
- } else {
234
- focusableItems[0]?.focus();
218
+ const handleKeyDown = (e: KeyboardEvent) => {
219
+ if (e.key === 'Escape') {
220
+ close();
235
221
  }
236
- break;
222
+ };
223
+
224
+ document.addEventListener('keydown', handleKeyDown);
225
+ return () => document.removeEventListener('keydown', handleKeyDown);
226
+ }, [isOpen, closeOnEscape, close]);
227
+
228
+ // Keyboard navigation
229
+ const handleKeyDown = useCallback((e: React.KeyboardEvent) => {
230
+ if (!menuRef.current) return;
231
+
232
+ const focusableItems = menuRef.current.querySelectorAll<HTMLElement>(
233
+ '[role="menuitem"]:not([disabled])'
234
+ );
235
+ if (!focusableItems.length) return;
236
+
237
+ const currentIndex = Array.from(focusableItems).findIndex(
238
+ item => item === document.activeElement
239
+ );
240
+
241
+ switch (e.key) {
242
+ case 'ArrowDown':
243
+ e.preventDefault();
244
+ if (currentIndex < focusableItems.length - 1) {
245
+ focusableItems[currentIndex + 1]?.focus();
246
+ } else {
247
+ focusableItems[0]?.focus();
248
+ }
249
+ break;
250
+
251
+ case 'ArrowUp':
252
+ e.preventDefault();
253
+ if (currentIndex > 0) {
254
+ focusableItems[currentIndex - 1]?.focus();
255
+ } else {
256
+ focusableItems[focusableItems.length - 1]?.focus();
257
+ }
258
+ break;
259
+
260
+ case 'Home':
261
+ e.preventDefault();
262
+ focusableItems[0]?.focus();
263
+ break;
237
264
 
238
- case 'ArrowUp':
239
- e.preventDefault();
240
- if (currentIndex > 0) {
241
- focusableItems[currentIndex - 1]?.focus();
242
- } else {
265
+ case 'End':
266
+ e.preventDefault();
243
267
  focusableItems[focusableItems.length - 1]?.focus();
268
+ break;
269
+ }
270
+ }, []);
271
+
272
+ // Event handlers
273
+ const handleToggleClick = useCallback(
274
+ (e: React.MouseEvent) => {
275
+ if (trigger === 'click') {
276
+ e.preventDefault();
277
+ e.stopPropagation();
278
+ toggle();
244
279
  }
245
- break;
246
-
247
- case 'Home':
248
- e.preventDefault();
249
- focusableItems[0]?.focus();
250
- break;
251
-
252
- case 'End':
253
- e.preventDefault();
254
- focusableItems[focusableItems.length - 1]?.focus();
255
- break;
256
- }
257
- }, []);
280
+ },
281
+ [trigger, toggle]
282
+ );
258
283
 
259
- // Event handlers
260
- const handleToggleClick = useCallback(
261
- (e: React.MouseEvent) => {
262
- if (trigger === 'click') {
263
- e.preventDefault();
264
- e.stopPropagation();
265
- toggle();
266
- }
267
- },
268
- [trigger, toggle]
269
- );
284
+ const handleToggleKeyDown = useCallback(
285
+ (e: React.KeyboardEvent) => {
286
+ if ((e.key === 'Enter' || e.key === ' ' || e.key === 'ArrowDown') && !isOpen) {
287
+ e.preventDefault();
288
+ setIsOpen(true);
289
+
290
+ // Only focus the first menu item when using keyboard navigation
291
+ if (e.key === 'ArrowDown' && menuRef.current) {
292
+ setTimeout(() => {
293
+ const firstItem = menuRef.current?.querySelector<HTMLElement>('[role="menuitem"]');
294
+ firstItem?.focus();
295
+ }, 100);
296
+ }
297
+ } else if (e.key === 'Escape' && isOpen) {
298
+ e.preventDefault();
299
+ close();
300
+ }
301
+ },
302
+ [isOpen, setIsOpen, close]
303
+ );
270
304
 
271
- const handleToggleKeyDown = useCallback(
272
- (e: React.KeyboardEvent) => {
273
- if ((e.key === 'Enter' || e.key === ' ' || e.key === 'ArrowDown') && !isOpen) {
274
- e.preventDefault();
305
+ // Hover handlers for trigger="hover"
306
+ const handleHoverOpen = useCallback(() => {
307
+ if (trigger === 'hover') {
275
308
  setIsOpen(true);
276
-
277
- // Only focus the first menu item when using keyboard navigation
278
- if (e.key === 'ArrowDown' && menuRef.current) {
279
- setTimeout(() => {
280
- const firstItem = menuRef.current?.querySelector<HTMLElement>('[role="menuitem"]');
281
- firstItem?.focus();
282
- }, 100);
283
- }
284
- } else if (e.key === 'Escape' && isOpen) {
285
- e.preventDefault();
286
- close();
287
309
  }
288
- },
289
- [isOpen, setIsOpen, close]
290
- );
291
-
292
- // Hover handlers for trigger="hover"
293
- const handleHoverOpen = useCallback(() => {
294
- if (trigger === 'hover') {
295
- setIsOpen(true);
310
+ }, [trigger, setIsOpen]);
311
+
312
+ // Build class names
313
+ const dropdownClasses = [
314
+ 'c-dropdown',
315
+ trigger === 'click' ? 'c-dropdown--onclick' : '',
316
+ variant ? `c-dropdown--${variant}` : '',
317
+ isOpen ? 'is-open' : '',
318
+ glass ? 'c-dropdown--glass' : '',
319
+ className,
320
+ ]
321
+ .filter(Boolean)
322
+ .join(' ');
323
+
324
+ // Menu styles
325
+ const menuStyleProps: React.CSSProperties = {};
326
+ if (maxHeight) menuStyleProps.maxHeight = maxHeight;
327
+ if (minWidth !== undefined) {
328
+ menuStyleProps.minWidth = typeof minWidth === 'number' ? `${minWidth}px` : minWidth;
296
329
  }
297
- }, [trigger, setIsOpen]);
298
-
299
- // Build class names
300
- const dropdownClasses = [
301
- 'c-dropdown',
302
- trigger === 'click' ? 'c-dropdown--onclick' : '',
303
- variant ? `c-dropdown--${variant}` : '',
304
- isOpen ? 'is-open' : '',
305
- glass ? 'c-dropdown--glass' : '',
306
- className,
307
- ]
308
- .filter(Boolean)
309
- .join(' ');
310
-
311
- // Menu styles
312
- const menuStyleProps: React.CSSProperties = {};
313
- if (maxHeight) menuStyleProps.maxHeight = maxHeight;
314
- if (minWidth !== undefined) {
315
- menuStyleProps.minWidth = typeof minWidth === 'number' ? `${minWidth}px` : minWidth;
316
- }
317
330
 
318
- const menuContent = (
319
- <div className="c-dropdown__menu-inner" style={menuStyleProps}>
320
- <DropdownContext.Provider value={{ isOpen, close, id: dropdownId, trigger }}>
321
- <ul className={`c-dropdown__menu ${glass ? 'c-dropdown__menu--glass' : ''}`}>{menu}</ul>
322
- </DropdownContext.Provider>
323
- </div>
324
- );
325
-
326
- return (
327
- <div
328
- ref={dropdownRef}
329
- className={dropdownClasses}
330
- style={style}
331
- onMouseEnter={trigger === 'hover' ? handleHoverOpen : undefined}
332
- {...props}
333
- >
334
- <div
335
- ref={toggleRef}
336
- className="c-dropdown__toggle"
337
- onClick={handleToggleClick}
338
- onKeyDown={handleToggleKeyDown}
339
- aria-haspopup="menu"
340
- aria-expanded={isOpen}
341
- aria-controls={dropdownId}
342
- tabIndex={0}
343
- >
344
- {children}
331
+ const menuContent = (
332
+ <div className="c-dropdown__menu-inner" style={menuStyleProps}>
333
+ <DropdownContext.Provider value={{ isOpen, close, id: dropdownId, trigger }}>
334
+ <ul className={`c-dropdown__menu ${glass ? 'c-dropdown__menu--glass' : ''}`}>{menu}</ul>
335
+ </DropdownContext.Provider>
345
336
  </div>
337
+ );
346
338
 
339
+ return (
347
340
  <div
348
- ref={menuRef}
349
- id={dropdownId}
350
- className={`c-dropdown__menu-wrapper c-dropdown__menu-wrapper--${placement} ${isOpen ? 'is-open' : ''} ${glass ? 'is-glass' : ''}`}
351
- role="menu"
352
- aria-orientation="vertical"
353
- aria-hidden={!isOpen}
354
- onKeyDown={handleKeyDown}
341
+ ref={dropdownRef}
342
+ className={dropdownClasses}
343
+ style={style}
344
+ onMouseEnter={trigger === 'hover' ? handleHoverOpen : undefined}
345
+ {...props}
355
346
  >
356
- {glass
357
- ? // Default glass settings for dropdowns
358
- (() => {
359
- const defaultGlassProps = {
360
- displacementScale: 20,
361
- elasticity: 0,
362
- };
363
-
364
- const glassProps =
365
- glass === true ? defaultGlassProps : { ...defaultGlassProps, ...glass };
366
-
367
- return <AtomixGlass {...glassProps}>{menuContent}</AtomixGlass>;
368
- })()
369
- : menuContent}
347
+ <div
348
+ ref={toggleRef}
349
+ className="c-dropdown__toggle"
350
+ onClick={handleToggleClick}
351
+ onKeyDown={handleToggleKeyDown}
352
+ aria-haspopup="menu"
353
+ aria-expanded={isOpen}
354
+ aria-controls={dropdownId}
355
+ tabIndex={0}
356
+ >
357
+ {children}
358
+ </div>
359
+
360
+ <div
361
+ ref={menuRef}
362
+ id={dropdownId}
363
+ className={`c-dropdown__menu-wrapper c-dropdown__menu-wrapper--${placement} ${isOpen ? 'is-open' : ''} ${glass ? 'is-glass' : ''}`}
364
+ role="menu"
365
+ aria-orientation="vertical"
366
+ aria-hidden={!isOpen}
367
+ onKeyDown={handleKeyDown}
368
+ >
369
+ {glass
370
+ ? // Default glass settings for dropdowns
371
+ (() => {
372
+ const defaultGlassProps = {
373
+ displacementScale: 20,
374
+ elasticity: 0,
375
+ };
376
+
377
+ const glassProps =
378
+ glass === true ? defaultGlassProps : { ...defaultGlassProps, ...glass };
379
+
380
+ return <AtomixGlass {...glassProps}>{menuContent}</AtomixGlass>;
381
+ })()
382
+ : menuContent}
383
+ </div>
370
384
  </div>
371
- </div>
372
- );
373
- });
385
+ );
386
+ }
387
+ );
374
388
 
375
389
  export type { DropdownProps, DropdownItemProps, DropdownDividerProps, DropdownHeaderProps };
376
390