@shohojdhara/atomix 0.4.7 → 0.4.9

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 (176) hide show
  1. package/atomix.config.ts +58 -1
  2. package/dist/atomix.css +172 -157
  3. package/dist/atomix.css.map +1 -1
  4. package/dist/atomix.min.css +4 -4
  5. package/dist/atomix.min.css.map +1 -1
  6. package/dist/charts.d.ts +33 -0
  7. package/dist/charts.js +1274 -164
  8. package/dist/charts.js.map +1 -1
  9. package/dist/core.d.ts +33 -10
  10. package/dist/core.js +1099 -83
  11. package/dist/core.js.map +1 -1
  12. package/dist/forms.d.ts +33 -0
  13. package/dist/forms.js +2106 -1050
  14. package/dist/forms.js.map +1 -1
  15. package/dist/heavy.d.ts +42 -1
  16. package/dist/heavy.js +1663 -638
  17. package/dist/heavy.js.map +1 -1
  18. package/dist/index.d.ts +442 -270
  19. package/dist/index.esm.js +1947 -680
  20. package/dist/index.esm.js.map +1 -1
  21. package/dist/index.js +1982 -712
  22. package/dist/index.js.map +1 -1
  23. package/dist/index.min.js +1 -1
  24. package/dist/index.min.js.map +1 -1
  25. package/package.json +6 -3
  26. package/scripts/atomix-cli.js +136 -1827
  27. package/scripts/cli/__tests__/basic.test.js +3 -2
  28. package/scripts/cli/__tests__/clean.test.js +278 -0
  29. package/scripts/cli/__tests__/component-validator.test.js +433 -0
  30. package/scripts/cli/__tests__/generator.test.js +613 -0
  31. package/scripts/cli/__tests__/glass-motion.test.js +256 -0
  32. package/scripts/cli/__tests__/integration.test.js +719 -108
  33. package/scripts/cli/__tests__/migrate.test.js +74 -0
  34. package/scripts/cli/__tests__/security.test.js +206 -0
  35. package/scripts/cli/__tests__/test-setup.js +3 -1
  36. package/scripts/cli/__tests__/theme-bridge.test.js +507 -0
  37. package/scripts/cli/__tests__/token-provider.test.js +361 -0
  38. package/scripts/cli/__tests__/utils.test.js +5 -5
  39. package/scripts/cli/commands/benchmark.js +105 -0
  40. package/scripts/cli/commands/build-theme.js +115 -0
  41. package/scripts/cli/commands/clean.js +109 -0
  42. package/scripts/cli/commands/doctor.js +88 -0
  43. package/scripts/cli/commands/generate.js +218 -0
  44. package/scripts/cli/commands/init.js +73 -0
  45. package/scripts/cli/commands/migrate.js +106 -0
  46. package/scripts/cli/commands/sync-tokens.js +206 -0
  47. package/scripts/cli/commands/theme-bridge.js +248 -0
  48. package/scripts/cli/commands/tokens.js +157 -0
  49. package/scripts/cli/commands/validate.js +194 -0
  50. package/scripts/cli/internal/ai-engine.js +156 -0
  51. package/scripts/cli/internal/compiler.js +114 -0
  52. package/scripts/cli/internal/component-validator.js +443 -0
  53. package/scripts/cli/internal/config-loader.js +162 -0
  54. package/scripts/cli/internal/filesystem.js +158 -0
  55. package/scripts/cli/internal/generator.js +430 -0
  56. package/scripts/cli/internal/glass-generator.js +398 -0
  57. package/scripts/cli/internal/hook-generator.js +369 -0
  58. package/scripts/cli/internal/hooks.js +61 -0
  59. package/scripts/cli/internal/itcss-generator.js +565 -0
  60. package/scripts/cli/internal/motion-generator.js +679 -0
  61. package/scripts/cli/internal/template-engine.js +301 -0
  62. package/scripts/cli/internal/theme-bridge.js +664 -0
  63. package/scripts/cli/internal/tokens/engine.js +122 -0
  64. package/scripts/cli/internal/tokens/provider.js +34 -0
  65. package/scripts/cli/internal/tokens/providers/figma.js +50 -0
  66. package/scripts/cli/internal/tokens/providers/style-dictionary.js +48 -0
  67. package/scripts/cli/internal/tokens/providers/w3c.js +48 -0
  68. package/scripts/cli/internal/tokens/token-provider.js +443 -0
  69. package/scripts/cli/internal/tokens/token-validator.js +513 -0
  70. package/scripts/cli/internal/validator.js +276 -0
  71. package/scripts/cli/internal/wizard.js +115 -0
  72. package/scripts/cli/mappings.js +23 -0
  73. package/scripts/cli/migration-tools.js +164 -94
  74. package/scripts/cli/plugins/style-dictionary.js +46 -0
  75. package/scripts/cli/templates/README.md +525 -95
  76. package/scripts/cli/templates/common-templates.js +40 -14
  77. package/scripts/cli/templates/components/react-component.ts +282 -0
  78. package/scripts/cli/templates/config/project-config.ts +112 -0
  79. package/scripts/cli/templates/hooks/use-component.ts +477 -0
  80. package/scripts/cli/templates/index.js +19 -4
  81. package/scripts/cli/templates/index.ts +171 -0
  82. package/scripts/cli/templates/next-templates.js +72 -0
  83. package/scripts/cli/templates/react-templates.js +70 -126
  84. package/scripts/cli/templates/scss-templates.js +35 -35
  85. package/scripts/cli/templates/stories/storybook-story.ts +241 -0
  86. package/scripts/cli/templates/styles/scss-component.ts +255 -0
  87. package/scripts/cli/templates/tests/vitest-test.ts +229 -0
  88. package/scripts/cli/templates/token-templates.js +337 -1
  89. package/scripts/cli/templates/tokens/token-generators.ts +1088 -0
  90. package/scripts/cli/templates/types/component-types.ts +145 -0
  91. package/scripts/cli/templates/utils/testing-utils.ts +144 -0
  92. package/scripts/cli/templates/vanilla-templates.js +39 -0
  93. package/scripts/cli/token-manager.js +8 -2
  94. package/scripts/cli/utils/cache-manager.js +240 -0
  95. package/scripts/cli/utils/detector.js +46 -0
  96. package/scripts/cli/utils/diagnostics.js +289 -0
  97. package/scripts/cli/utils/error.js +89 -0
  98. package/scripts/cli/utils/helpers.js +67 -0
  99. package/scripts/cli/utils/logger.js +75 -0
  100. package/scripts/cli/utils/security.js +302 -0
  101. package/scripts/cli/utils/telemetry.js +115 -0
  102. package/scripts/cli/utils/validation.js +37 -0
  103. package/scripts/cli/utils.js +28 -341
  104. package/src/components/Accordion/Accordion.stories.tsx +0 -18
  105. package/src/components/Accordion/Accordion.test.tsx +0 -17
  106. package/src/components/Accordion/Accordion.tsx +0 -4
  107. package/src/components/AtomixGlass/AtomixGlass.test.tsx +37 -3
  108. package/src/components/AtomixGlass/AtomixGlass.tsx +143 -31
  109. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +129 -31
  110. package/src/components/AtomixGlass/PerformanceDashboard.tsx +219 -0
  111. package/src/components/AtomixGlass/README.md +25 -10
  112. package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +216 -0
  113. package/src/components/AtomixGlass/animation-system.ts +578 -0
  114. package/src/components/AtomixGlass/shader-utils.ts +4 -1
  115. package/src/components/AtomixGlass/stories/Overview.stories.tsx +157 -6
  116. package/src/components/AtomixGlass/stories/Phase1-Animation.stories.tsx +653 -0
  117. package/src/components/AtomixGlass/stories/Phase1-Test.stories.tsx +95 -0
  118. package/src/components/AtomixGlass/stories/Playground.stories.tsx +51 -51
  119. package/src/components/AtomixGlass/stories/shared-components.tsx +6 -0
  120. package/src/components/Avatar/Avatar.tsx +1 -1
  121. package/src/components/Button/Button.stories.disabled-link.tsx +10 -0
  122. package/src/components/Button/Button.stories.tsx +10 -0
  123. package/src/components/Button/Button.test.tsx +16 -11
  124. package/src/components/Button/Button.tsx +4 -4
  125. package/src/components/Card/Card.tsx +1 -1
  126. package/src/components/Dropdown/Dropdown.tsx +12 -12
  127. package/src/components/Form/Select.tsx +62 -3
  128. package/src/components/Modal/Modal.tsx +14 -3
  129. package/src/components/Navigation/Navbar/Navbar.tsx +44 -0
  130. package/src/components/Slider/Slider.stories.tsx +3 -3
  131. package/src/components/Slider/Slider.tsx +38 -0
  132. package/src/components/Steps/Steps.tsx +3 -3
  133. package/src/components/Tabs/Tabs.tsx +77 -8
  134. package/src/components/Testimonial/Testimonial.tsx +1 -1
  135. package/src/components/TypedButton/TypedButton.stories.tsx +59 -0
  136. package/src/components/TypedButton/TypedButton.tsx +39 -0
  137. package/src/components/TypedButton/index.ts +2 -0
  138. package/src/components/VideoPlayer/VideoPlayer.tsx +11 -4
  139. package/src/lib/composables/index.ts +4 -7
  140. package/src/lib/composables/types.ts +45 -0
  141. package/src/lib/composables/useAccordion.ts +0 -7
  142. package/src/lib/composables/useAtomixGlass.ts +148 -6
  143. package/src/lib/composables/useAtomixGlassStyles.ts +9 -7
  144. package/src/lib/composables/useChartExport.ts +3 -13
  145. package/src/lib/composables/useDropdown.ts +66 -0
  146. package/src/lib/composables/useFocusTrap.ts +80 -0
  147. package/src/lib/composables/usePerformanceMonitor.ts +448 -0
  148. package/src/lib/composables/useResponsiveGlass.presets.ts +192 -0
  149. package/src/lib/composables/useResponsiveGlass.ts +441 -0
  150. package/src/lib/composables/useTooltip.ts +16 -0
  151. package/src/lib/composables/useTypedButton.ts +66 -0
  152. package/src/lib/config/index.ts +62 -5
  153. package/src/lib/constants/components.ts +62 -7
  154. package/src/lib/theme/devtools/__tests__/useHistory.test.tsx +150 -0
  155. package/src/lib/theme/tokens/centralized-tokens.ts +120 -0
  156. package/src/lib/theme/utils/__tests__/domUtils.test.ts +101 -0
  157. package/src/lib/types/components.ts +37 -11
  158. package/src/lib/types/glass.ts +35 -0
  159. package/src/lib/types/index.ts +1 -0
  160. package/src/lib/utils/displacement-generator.ts +1 -1
  161. package/src/styles/01-settings/_settings.testtypecheck.scss +53 -0
  162. package/src/styles/01-settings/_settings.typedbutton.scss +53 -0
  163. package/src/styles/06-components/_components.atomix-glass.scss +17 -21
  164. package/src/styles/06-components/_components.edge-panel.scss +1 -5
  165. package/src/styles/06-components/_components.modal.scss +1 -4
  166. package/src/styles/06-components/_components.navbar.scss +1 -1
  167. package/src/styles/06-components/_components.testbutton.scss +212 -0
  168. package/src/styles/06-components/_components.testtypecheck.scss +212 -0
  169. package/src/styles/06-components/_components.tooltip.scss +9 -5
  170. package/src/styles/06-components/_components.typedbutton.scss +212 -0
  171. package/src/styles/99-utilities/_index.scss +1 -0
  172. package/src/styles/99-utilities/_utilities.text.scss +1 -1
  173. package/src/styles/99-utilities/_utilities.touch-target.scss +36 -0
  174. package/scripts/cli/component-generator.js +0 -564
  175. package/scripts/cli/interactive-init.js +0 -357
  176. package/src/styles/06-components/old.chart.styles.scss +0 -2788
@@ -0,0 +1,45 @@
1
+ /**
2
+ * TestTypeCheck Hook Types
3
+ * Generated by Atomix CLI
4
+ */
5
+
6
+ /**
7
+ * Component State Interface
8
+ */
9
+ export interface ComponentState {
10
+ isActive: boolean;
11
+ isHovered: boolean;
12
+ isFocused: boolean;
13
+ isLoading: boolean;
14
+ variant: 'primary' | 'secondary' | 'outline' | string;
15
+ size: 'sm' | 'md' | 'lg' | string;
16
+ }
17
+
18
+ /**
19
+ * Event Handler Types
20
+ */
21
+ export type ClickHandler = (event: React.MouseEvent<HTMLDivElement>) => void;
22
+ export type HoverHandler = (event: React.MouseEvent<HTMLDivElement>) => void;
23
+ export type FocusHandler = (event: React.FocusEvent<HTMLDivElement>) => void;
24
+ export type BlurHandler = (event: React.FocusEvent<HTMLDivElement>) => void;
25
+ export type StateChangeHandler = (state: ComponentState) => void;
26
+
27
+ /**
28
+ * Variant Configuration
29
+ */
30
+ export type VariantType = 'primary' | 'secondary' | 'outline' | 'ghost' | 'link';
31
+
32
+ /**
33
+ * Size Configuration
34
+ */
35
+ export type SizeType = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
36
+
37
+ /**
38
+ * Glass Effect Configuration
39
+ */
40
+ export interface GlassConfig {
41
+ displacementScale?: number;
42
+ blurAmount?: number;
43
+ saturation?: number;
44
+ elasticity?: number;
45
+ }
@@ -66,13 +66,6 @@ export function useAccordion(
66
66
  }
67
67
 
68
68
  defaultProps.onOpenChange?.(nextOpen);
69
-
70
- // Call legacy handlers
71
- if (nextOpen) {
72
- defaultProps.onOpen?.();
73
- } else {
74
- defaultProps.onClose?.();
75
- }
76
69
  }
77
70
  };
78
71
 
@@ -23,6 +23,13 @@ import {
23
23
  lerp,
24
24
  } from '../../components/AtomixGlass/glass-utils';
25
25
  import { updateAtomixGlassStyles } from './useAtomixGlassStyles';
26
+ // Phase 1: Time-Based Animation System
27
+ import {
28
+ createAnimationLoop,
29
+ createFBMEngine,
30
+ getFBMConfigForQuality,
31
+ liquidGlassWithTime,
32
+ } from '../../components/AtomixGlass/animation-system';
26
33
 
27
34
  const { CONSTANTS } = ATOMIX_GLASS;
28
35
 
@@ -150,6 +157,14 @@ interface UseAtomixGlassOptions extends Omit<AtomixGlassProps, 'children'> {
150
157
  contentRef: React.RefObject<HTMLDivElement>;
151
158
  wrapperRef?: React.RefObject<HTMLDivElement>;
152
159
  children?: React.ReactNode;
160
+ isFixedOrSticky?: boolean;
161
+ // Phase 1: Time-Based Animation System
162
+ withLiquidBlur?: boolean;
163
+ animationQuality?: 'low' | 'medium' | 'high';
164
+ timeSpeed?: number;
165
+ noiseAmplitude?: number;
166
+ noiseFrequency?: number;
167
+ displacementStrength?: number;
153
168
  }
154
169
 
155
170
  interface UseAtomixGlassReturn {
@@ -181,6 +196,10 @@ interface UseAtomixGlassReturn {
181
196
  // Transform calculations
182
197
  transformStyle: string;
183
198
 
199
+ // Phase 1: Animation System - Shader time control
200
+ getShaderTime: () => number;
201
+ applyTimeBasedDistortion: (uv: { x: number; y: number }) => { x: number; y: number };
202
+
184
203
  // Event handlers
185
204
  handleMouseEnter: () => void;
186
205
  handleMouseLeave: () => void;
@@ -211,12 +230,20 @@ export function useAtomixGlass({
211
230
  onClick,
212
231
  debugBorderRadius = false,
213
232
  debugOverLight = false,
214
- debugPerformance = false,
215
233
  children,
216
234
  blurAmount,
217
235
  saturation,
218
236
  padding,
219
237
  withLiquidBlur,
238
+ isFixedOrSticky = false,
239
+ // Phase 1: Animation System Props
240
+ withTimeAnimation = ATOMIX_GLASS.DEFAULTS.WITH_TIME_ANIMATION,
241
+ animationSpeed = ATOMIX_GLASS.DEFAULTS.ANIMATION_SPEED,
242
+ withMultiLayerDistortion = ATOMIX_GLASS.DEFAULTS.WITH_MULTI_LAYER_DISTORTION,
243
+ distortionOctaves = ATOMIX_GLASS.DEFAULTS.DISTORTION_OCTAVES,
244
+ distortionLacunarity = ATOMIX_GLASS.DEFAULTS.DISTORTION_LACUNARITY,
245
+ distortionGain = ATOMIX_GLASS.DEFAULTS.DISTORTION_GAIN,
246
+ distortionQuality = ATOMIX_GLASS.DEFAULTS.DISTORTION_QUALITY,
220
247
  }: UseAtomixGlassOptions): UseAtomixGlassReturn {
221
248
  // State
222
249
  const [isHovered, setIsHovered] = useState(false);
@@ -242,6 +269,115 @@ export function useAtomixGlass({
242
269
  const [userPrefersHighContrast, setUserPrefersHighContrast] = useState(false);
243
270
  const [detectedOverLight, setDetectedOverLight] = useState(false);
244
271
 
272
+ // ============================================================================
273
+ // Phase 1: Time-Based Animation System (Feature 1.1)
274
+ // ============================================================================
275
+
276
+ // Animation state refs
277
+ const animationFrameIdRef = useRef<number | null>(null);
278
+ const animationStartTimeRef = useRef<number>(0);
279
+ const elapsedTimeRef = useRef<number>(0);
280
+ const shaderTimeRef = useRef<number>(0);
281
+
282
+ /**
283
+ * Get FBM configuration based on quality preset or custom values
284
+ */
285
+ const fbmConfig = useMemo(() => {
286
+ // If quality preset is provided, use it as base
287
+ const preset = getFBMConfigForQuality(distortionQuality);
288
+
289
+ // Override with custom values if provided
290
+ return {
291
+ octaves: distortionOctaves ?? preset.octaves,
292
+ lacunarity: distortionLacunarity ?? preset.lacunarity,
293
+ gain: distortionGain ?? preset.gain,
294
+ };
295
+ }, [distortionQuality, distortionOctaves, distortionLacunarity, distortionGain]);
296
+
297
+ /**
298
+ * Create FBM engine for multi-layer distortion
299
+ */
300
+ const fbmEngine = useMemo(() => {
301
+ if (!withMultiLayerDistortion) return null;
302
+ return createFBMEngine(fbmConfig);
303
+ }, [withMultiLayerDistortion, fbmConfig]);
304
+
305
+ /**
306
+ * Determine effective animation settings
307
+ */
308
+ const effectiveReducedMotion = useMemo(
309
+ () => reducedMotion || userPrefersReducedMotion,
310
+ [reducedMotion, userPrefersReducedMotion]
311
+ );
312
+
313
+ const effectiveWithTimeAnimation = useMemo(() => {
314
+ return withTimeAnimation && !effectiveReducedMotion;
315
+ }, [withTimeAnimation, effectiveReducedMotion]);
316
+
317
+ /**
318
+ * Animation loop for time-based effects
319
+ */
320
+ useEffect(() => {
321
+ if (!effectiveWithTimeAnimation || typeof window === 'undefined') {
322
+ return undefined;
323
+ }
324
+
325
+ let lastFrameTime = performance.now();
326
+
327
+ /**
328
+ * Animation frame handler
329
+ */
330
+ const animate = (currentTime: number) => {
331
+ // Calculate delta time
332
+ const deltaTime = currentTime - lastFrameTime;
333
+ lastFrameTime = currentTime;
334
+
335
+ // Apply animation speed multiplier
336
+ const scaledDelta = deltaTime * animationSpeed;
337
+ elapsedTimeRef.current += scaledDelta;
338
+ shaderTimeRef.current = elapsedTimeRef.current;
339
+
340
+ // Continue animation loop
341
+ animationFrameIdRef.current = requestAnimationFrame(animate);
342
+ };
343
+
344
+ // Start animation
345
+ animationStartTimeRef.current = performance.now();
346
+ animationFrameIdRef.current = requestAnimationFrame(animate);
347
+
348
+ // Cleanup
349
+ return () => {
350
+ if (animationFrameIdRef.current !== null) {
351
+ cancelAnimationFrame(animationFrameIdRef.current);
352
+ animationFrameIdRef.current = null;
353
+ }
354
+ };
355
+ }, [effectiveWithTimeAnimation, animationSpeed]);
356
+
357
+ /**
358
+ * Get current shader time for animations
359
+ */
360
+ const getShaderTime = useCallback(() => {
361
+ return shaderTimeRef.current;
362
+ }, []);
363
+
364
+ /**
365
+ * Apply time-based distortion to UV coordinates
366
+ */
367
+ const applyTimeBasedDistortion = useCallback(
368
+ (uv: { x: number; y: number }): { x: number; y: number } => {
369
+ if (!effectiveWithTimeAnimation || !fbmEngine) {
370
+ return uv;
371
+ }
372
+
373
+ const time = shaderTimeRef.current;
374
+
375
+ // Apply liquid glass distortion with time
376
+ return liquidGlassWithTime(uv, time, fbmConfig);
377
+ },
378
+ [effectiveWithTimeAnimation, fbmEngine, fbmConfig]
379
+ );
380
+
245
381
  // Memoized derived values
246
382
  const effectiveBorderRadius = useMemo(() => {
247
383
  if (borderRadius !== undefined) {
@@ -258,10 +394,6 @@ export function useAtomixGlass({
258
394
  cachedRectRef
259
395
  });
260
396
 
261
- const effectiveReducedMotion = useMemo(
262
- () => reducedMotion || userPrefersReducedMotion,
263
- [reducedMotion, userPrefersReducedMotion]
264
- );
265
397
 
266
398
  const effectiveHighContrast = useMemo(
267
399
  () => highContrast || userPrefersHighContrast,
@@ -700,6 +832,12 @@ export function useAtomixGlass({
700
832
  const tick = () => {
701
833
  if (!lerpActiveRef.current) return;
702
834
 
835
+ // Add ref validity check to prevent memory leaks
836
+ if (!glassRef.current || !wrapperRef?.current) {
837
+ lerpActiveRef.current = false;
838
+ return;
839
+ }
840
+
703
841
  const cur = internalMouseOffsetRef.current;
704
842
  const tgt = targetMouseOffsetRef.current;
705
843
 
@@ -725,7 +863,7 @@ export function useAtomixGlass({
725
863
 
726
864
  // Imperative style update with the smoothed values
727
865
  updateAtomixGlassStyles(
728
- wrapperRef?.current || null,
866
+ wrapperRef.current,
729
867
  glassRef.current,
730
868
  {
731
869
  mouseOffset: internalMouseOffsetRef.current,
@@ -745,6 +883,7 @@ export function useAtomixGlass({
745
883
  blurAmount,
746
884
  saturation,
747
885
  padding,
886
+ isFixedOrSticky,
748
887
  }
749
888
  );
750
889
 
@@ -768,6 +907,7 @@ export function useAtomixGlass({
768
907
  blurAmount,
769
908
  saturation,
770
909
  padding,
910
+ isFixedOrSticky,
771
911
  ]);
772
912
 
773
913
  const stopLerpLoop = useCallback(() => {
@@ -913,6 +1053,8 @@ export function useAtomixGlass({
913
1053
  mouseOffset, // This is now static (refs or props) unless prop changes
914
1054
  overLightConfig,
915
1055
  transformStyle,
1056
+ getShaderTime,
1057
+ applyTimeBasedDistortion,
916
1058
  handleMouseEnter,
917
1059
  handleMouseLeave,
918
1060
  handleMouseDown,
@@ -34,6 +34,7 @@ export const updateAtomixGlassStyles = (
34
34
  blurAmount?: number;
35
35
  saturation?: number;
36
36
  padding?: string;
37
+ isFixedOrSticky?: boolean;
37
38
  }
38
39
  ) => {
39
40
  if (!wrapperElement && !containerElement) return;
@@ -56,6 +57,7 @@ export const updateAtomixGlassStyles = (
56
57
  blurAmount = ATOMIX_GLASS.DEFAULTS.BLUR_AMOUNT,
57
58
  saturation = ATOMIX_GLASS.DEFAULTS.SATURATION,
58
59
  padding = ATOMIX_GLASS.DEFAULTS.PADDING,
60
+ isFixedOrSticky = false,
59
61
  } = params;
60
62
 
61
63
  // Calculate mouse influence
@@ -225,10 +227,10 @@ export const updateAtomixGlassStyles = (
225
227
  const my = mouseOffset.y;
226
228
 
227
229
  // Constants for blur calculation
228
- const EDGE_BLUR_MULTIPLIER = 1.25;
229
- const CENTER_BLUR_MULTIPLIER = 1.1;
230
- const FLOW_BLUR_MULTIPLIER = 1.2;
231
- const MOUSE_INFLUENCE_BLUR_FACTOR = 0.15;
230
+ const EDGE_BLUR_MULTIPLIER = 0.5;
231
+ const CENTER_BLUR_MULTIPLIER = 0.2;
232
+ const FLOW_BLUR_MULTIPLIER = 0.3;
233
+ const MOUSE_INFLUENCE_BLUR_FACTOR = 0.4;
232
234
  const EDGE_INTENSITY_MOUSE_FACTOR = 0.15;
233
235
  const CENTER_INTENSITY_MOUSE_FACTOR = 0.1;
234
236
  const MAX_BLUR_RELATIVE = 2;
@@ -296,8 +298,8 @@ export const updateAtomixGlassStyles = (
296
298
  // Container variables
297
299
  const style = containerElement.style;
298
300
 
299
- style.setProperty('--atomix-glass-container-width', `${glassSize.width}`);
300
- style.setProperty('--atomix-glass-container-height', `${glassSize.height}`);
301
+ style.setProperty('--atomix-glass-container-width', !isFixedOrSticky ? '100%' : `${glassSize.width}`);
302
+ style.setProperty('--atomix-glass-container-height', !isFixedOrSticky ? '100%' : `${glassSize.height}`);
301
303
  style.setProperty('--atomix-glass-container-padding', padding);
302
304
  style.setProperty('--atomix-glass-container-radius', `${effectiveBorderRadius}px`);
303
305
 
@@ -320,7 +322,7 @@ export const updateAtomixGlassStyles = (
320
322
  : 'none');
321
323
 
322
324
  style.setProperty('--atomix-glass-container-text-shadow', isOverLight
323
- ? '0px 2px 12px rgba(0, 0, 0, 0)'
325
+ ? '0px 1px 2px rgba(255, 255, 255, 0.15)'
324
326
  : '0px 2px 12px rgba(0, 0, 0, 0.4)');
325
327
 
326
328
  style.setProperty('--atomix-glass-container-box-shadow', isOverLight
@@ -139,20 +139,10 @@ export function useChartExport() {
139
139
 
140
140
  // Export as PDF
141
141
  const exportAsPDF = useCallback(
142
- async (svgElement: SVGSVGElement, options: ExportOptions): Promise<void> => {
143
- // Note: This requires a PDF library like jsPDF
144
- // For now, we'll convert to canvas and then to PDF
145
- const canvas = await svgToCanvas(svgElement, options);
146
-
147
- // This would require jsPDF library
148
- // const pdf = new jsPDF();
149
- // const imgData = canvas.toDataURL('image/png');
150
- // pdf.addImage(imgData, 'PNG', 0, 0);
151
- // pdf.save(options.filename || 'chart.pdf');
152
-
153
- console.warn('PDF export requires jsPDF library to be installed');
142
+ async (_svgElement: SVGSVGElement, _options: ExportOptions): Promise<void> => {
143
+ console.warn('PDF export requires a PDF library to be installed');
154
144
  },
155
- [svgToCanvas]
145
+ []
156
146
  );
157
147
 
158
148
  // Export data as CSV
@@ -107,6 +107,72 @@ export const useDropdown = ({
107
107
  };
108
108
  }, [isOpen, closeOnEscape, setIsOpen]);
109
109
 
110
+ // Handle arrow key navigation
111
+ useEffect(() => {
112
+ if (!isOpen || !menuRef.current) return undefined;
113
+
114
+ const handleKeyDown = (event: KeyboardEvent) => {
115
+ const menu = menuRef.current;
116
+ if (!menu) return;
117
+
118
+ const items = Array.from(
119
+ menu.querySelectorAll<HTMLElement>('[role="menuitem"]:not([disabled])')
120
+ );
121
+ if (items.length === 0) return;
122
+
123
+ const currentIndex = items.indexOf(document.activeElement as HTMLElement);
124
+
125
+ switch (event.key) {
126
+ case 'ArrowDown':
127
+ event.preventDefault();
128
+ const nextIndex = (currentIndex + 1) % items.length;
129
+ const nextItem = items[nextIndex];
130
+ if (nextItem) nextItem.focus();
131
+ break;
132
+ case 'ArrowUp':
133
+ event.preventDefault();
134
+ const prevIndex = (currentIndex - 1 + items.length) % items.length;
135
+ const prevItem = items[prevIndex];
136
+ if (prevItem) prevItem.focus();
137
+ break;
138
+ case 'Home':
139
+ event.preventDefault();
140
+ const firstItem = items[0];
141
+ if (firstItem) firstItem.focus();
142
+ break;
143
+ case 'End':
144
+ event.preventDefault();
145
+ const lastItem = items[items.length - 1];
146
+ if (lastItem) lastItem.focus();
147
+ break;
148
+ case 'Tab':
149
+ // Close dropdown on tab
150
+ setIsOpen(false);
151
+ break;
152
+ default:
153
+ break;
154
+ }
155
+ };
156
+
157
+ document.addEventListener('keydown', handleKeyDown);
158
+
159
+ return () => {
160
+ document.removeEventListener('keydown', handleKeyDown);
161
+ };
162
+ }, [isOpen, setIsOpen]);
163
+
164
+ // Focus management when dropdown opens
165
+ useEffect(() => {
166
+ if (isOpen && menuRef.current) {
167
+ const firstItem = menuRef.current.querySelector<HTMLElement>(
168
+ '[role="menuitem"]:not([disabled])'
169
+ );
170
+ if (firstItem) {
171
+ setTimeout(() => firstItem.focus(), 0);
172
+ }
173
+ }
174
+ }, [isOpen]);
175
+
110
176
  // Helper function to get the flipped placement if needed
111
177
  const getFlippedPlacement = useCallback(
112
178
  (
@@ -0,0 +1,80 @@
1
+ import { useEffect, useRef, useCallback } from 'react';
2
+
3
+ /**
4
+ * Hook to trap focus within an element
5
+ */
6
+ export function useFocusTrap(isOpen: boolean) {
7
+ const containerRef = useRef<HTMLDivElement>(null);
8
+ const previouslyFocusedElement = useRef<HTMLElement | null>(null);
9
+
10
+ const getFocusableElements = useCallback(() => {
11
+ if (!containerRef.current) return [];
12
+
13
+ return Array.from(
14
+ containerRef.current.querySelectorAll<HTMLElement>(
15
+ 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])'
16
+ )
17
+ ).filter((el) => {
18
+ // Check if visible
19
+ const style = window.getComputedStyle(el);
20
+ return style.display !== 'none' && style.visibility !== 'hidden';
21
+ });
22
+ }, []);
23
+
24
+ const handleKeyDown = useCallback(
25
+ (event: KeyboardEvent) => {
26
+ if (event.key !== 'Tab') return;
27
+
28
+ const focusableElements = getFocusableElements();
29
+ if (focusableElements.length === 0) {
30
+ return;
31
+ }
32
+
33
+ const firstElement = focusableElements[0] as HTMLElement;
34
+ const lastElement = focusableElements[focusableElements.length - 1] as HTMLElement;
35
+
36
+ if (event.shiftKey) {
37
+ // Shift + Tab
38
+ if (document.activeElement === firstElement) {
39
+ event.preventDefault();
40
+ lastElement.focus();
41
+ }
42
+ } else {
43
+ // Tab
44
+ if (document.activeElement === lastElement) {
45
+ event.preventDefault();
46
+ firstElement.focus();
47
+ }
48
+ }
49
+ },
50
+ [getFocusableElements]
51
+ );
52
+
53
+ useEffect(() => {
54
+ if (isOpen) {
55
+ previouslyFocusedElement.current = document.activeElement as HTMLElement;
56
+
57
+ const focusableElements = getFocusableElements();
58
+ if (focusableElements.length > 0 && focusableElements[0]) {
59
+ // Delay focus slightly to ensure element is rendered
60
+ const firstEl = focusableElements[0] as HTMLElement;
61
+ setTimeout(() => {
62
+ firstEl.focus();
63
+ }, 0);
64
+ }
65
+
66
+ document.addEventListener('keydown', handleKeyDown);
67
+ } else {
68
+ if (previouslyFocusedElement.current) {
69
+ previouslyFocusedElement.current.focus();
70
+ }
71
+ document.removeEventListener('keydown', handleKeyDown);
72
+ }
73
+
74
+ return () => {
75
+ document.removeEventListener('keydown', handleKeyDown);
76
+ };
77
+ }, [isOpen, getFocusableElements, handleKeyDown]);
78
+
79
+ return containerRef;
80
+ }