@shohojdhara/atomix 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (201) hide show
  1. package/README.md +1 -28
  2. package/dist/atomix.css +1500 -241
  3. package/dist/atomix.min.css +6 -6
  4. package/dist/index.d.ts +1052 -194
  5. package/dist/index.esm.js +12201 -6066
  6. package/dist/index.esm.js.map +1 -1
  7. package/dist/index.js +5481 -2827
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.min.js +1 -1
  10. package/dist/index.min.js.map +1 -1
  11. package/dist/themes/boomdevs.css +1500 -301
  12. package/dist/themes/boomdevs.min.css +60 -8
  13. package/dist/themes/esrar.css +1500 -241
  14. package/dist/themes/esrar.min.css +6 -6
  15. package/dist/themes/mashroom.css +1496 -237
  16. package/dist/themes/mashroom.min.css +8 -8
  17. package/dist/themes/shaj-default.css +1451 -192
  18. package/dist/themes/shaj-default.min.css +6 -6
  19. package/package.json +66 -15
  20. package/src/components/Accordion/Accordion.stories.tsx +137 -0
  21. package/src/components/Accordion/Accordion.tsx +33 -3
  22. package/src/components/AtomixGlass/AtomixGlass.stories.tsx +3011 -0
  23. package/src/components/AtomixGlass/AtomixGlass.test.tsx +199 -0
  24. package/src/components/AtomixGlass/AtomixGlass.tsx +1281 -0
  25. package/src/components/AtomixGlass/AtomixGlassComprehensivePreview.stories.tsx +1369 -0
  26. package/src/components/AtomixGlass/README.md +134 -0
  27. package/src/components/AtomixGlass/index.ts +10 -0
  28. package/src/components/AtomixGlass/shader-utils.ts +140 -0
  29. package/src/components/AtomixGlass/utils.ts +8 -0
  30. package/src/components/Badge/Badge.stories.tsx +169 -0
  31. package/src/components/Badge/Badge.tsx +27 -2
  32. package/src/components/Button/Button.stories.tsx +345 -0
  33. package/src/components/Button/Button.tsx +35 -3
  34. package/src/components/Button/README.md +216 -0
  35. package/src/components/Callout/Callout.stories.tsx +813 -78
  36. package/src/components/Callout/Callout.test.tsx +368 -0
  37. package/src/components/Callout/Callout.tsx +26 -7
  38. package/src/components/Callout/README.md +409 -0
  39. package/src/components/Card/Card.stories.tsx +140 -0
  40. package/src/components/Card/Card.tsx +19 -3
  41. package/src/components/DatePicker/DatePicker copy.tsx +551 -0
  42. package/src/components/DatePicker/DatePicker.stories.tsx +188 -0
  43. package/src/components/DatePicker/DatePicker.tsx +379 -332
  44. package/src/components/DatePicker/readme.md +110 -1
  45. package/src/components/DatePicker/types.ts +8 -0
  46. package/src/components/Dropdown/Dropdown.stories.tsx +145 -0
  47. package/src/components/Dropdown/Dropdown.tsx +34 -5
  48. package/src/components/Footer/Footer.stories.tsx +388 -0
  49. package/src/components/Footer/Footer.tsx +197 -0
  50. package/src/components/Footer/FooterLink.tsx +72 -0
  51. package/src/components/Footer/FooterSection.tsx +87 -0
  52. package/src/components/Footer/FooterSocialLink.tsx +117 -0
  53. package/src/components/Footer/README.md +261 -0
  54. package/src/components/Footer/index.ts +13 -0
  55. package/src/components/Form/Checkbox.stories.tsx +101 -0
  56. package/src/components/Form/Checkbox.tsx +26 -2
  57. package/src/components/Form/Input.stories.tsx +124 -0
  58. package/src/components/Form/Input.tsx +36 -7
  59. package/src/components/Form/Radio.stories.tsx +139 -0
  60. package/src/components/Form/Radio.tsx +26 -2
  61. package/src/components/Form/Select.stories.tsx +110 -0
  62. package/src/components/Form/Select.tsx +26 -2
  63. package/src/components/Form/Textarea.stories.tsx +104 -0
  64. package/src/components/Form/Textarea.tsx +36 -7
  65. package/src/components/Hero/Hero.stories.tsx +54 -1
  66. package/src/components/Hero/Hero.tsx +70 -11
  67. package/src/components/Modal/Modal.stories.tsx +235 -0
  68. package/src/components/Modal/Modal.tsx +64 -35
  69. package/src/components/Pagination/Pagination.stories.tsx +101 -0
  70. package/src/components/Pagination/Pagination.tsx +25 -1
  71. package/src/components/Popover/Popover.stories.tsx +94 -0
  72. package/src/components/Popover/Popover.tsx +30 -4
  73. package/src/components/Rating/Rating.stories.tsx +112 -0
  74. package/src/components/Rating/Rating.tsx +25 -1
  75. package/src/components/SectionIntro/SectionIntro.tsx +9 -11
  76. package/src/components/Slider/Slider.stories.tsx +634 -50
  77. package/src/components/Slider/Slider.tsx +5 -3
  78. package/src/components/Steps/Steps.stories.tsx +119 -0
  79. package/src/components/Steps/Steps.tsx +32 -1
  80. package/src/components/Tab/Tab.stories.tsx +88 -0
  81. package/src/components/Tab/Tab.tsx +32 -1
  82. package/src/components/Toggle/Toggle.stories.tsx +92 -0
  83. package/src/components/Toggle/Toggle.tsx +32 -1
  84. package/src/components/Tooltip/Tooltip.stories.tsx +131 -0
  85. package/src/components/Tooltip/Tooltip.tsx +43 -7
  86. package/src/components/VideoPlayer/VideoPlayer.stories.tsx +1002 -196
  87. package/src/components/VideoPlayer/VideoPlayer.tsx +161 -4
  88. package/src/components/index.ts +14 -0
  89. package/src/layouts/Grid/Grid.stories.tsx +226 -159
  90. package/src/lib/composables/index.ts +4 -0
  91. package/src/lib/composables/useAtomixGlass.ts +71 -0
  92. package/src/lib/composables/useButton.ts +3 -1
  93. package/src/lib/composables/useCallout.ts +4 -1
  94. package/src/lib/composables/useFooter.ts +85 -0
  95. package/src/lib/composables/useGlassContainer.ts +168 -0
  96. package/src/lib/composables/useSlider.ts +191 -4
  97. package/src/lib/constants/components.ts +173 -0
  98. package/src/lib/types/components.ts +622 -0
  99. package/src/lib/utils/displacement-generator.ts +86 -0
  100. package/src/styles/01-settings/_index.scss +1 -0
  101. package/src/styles/01-settings/_settings.accordion.scss +20 -19
  102. package/src/styles/01-settings/_settings.animations.scss +5 -5
  103. package/src/styles/01-settings/_settings.avatar-group.scss +1 -1
  104. package/src/styles/01-settings/_settings.avatar.scss +17 -18
  105. package/src/styles/01-settings/_settings.background.scss +10 -0
  106. package/src/styles/01-settings/_settings.badge.scss +1 -1
  107. package/src/styles/01-settings/_settings.breadcrumb.scss +8 -2
  108. package/src/styles/01-settings/_settings.callout.scss +7 -7
  109. package/src/styles/01-settings/_settings.card.scss +2 -2
  110. package/src/styles/01-settings/_settings.chart.scss +7 -7
  111. package/src/styles/01-settings/_settings.checkbox-group.scss +5 -2
  112. package/src/styles/01-settings/_settings.checkbox.scss +10 -4
  113. package/src/styles/01-settings/_settings.countdown.scss +6 -4
  114. package/src/styles/01-settings/_settings.dropdown.scss +9 -7
  115. package/src/styles/01-settings/_settings.edge-panel.scss +3 -2
  116. package/src/styles/01-settings/_settings.footer.scss +125 -0
  117. package/src/styles/01-settings/_settings.form-group.scss +3 -1
  118. package/src/styles/01-settings/_settings.form.scss +4 -2
  119. package/src/styles/01-settings/_settings.hero.scss +9 -7
  120. package/src/styles/01-settings/_settings.input.scss +9 -7
  121. package/src/styles/01-settings/_settings.list-group.scss +4 -2
  122. package/src/styles/01-settings/_settings.list.scss +4 -2
  123. package/src/styles/01-settings/_settings.menu.scss +10 -8
  124. package/src/styles/01-settings/_settings.messages.scss +19 -17
  125. package/src/styles/01-settings/_settings.modal.scss +6 -4
  126. package/src/styles/01-settings/_settings.nav.scss +6 -4
  127. package/src/styles/01-settings/_settings.navbar.scss +8 -5
  128. package/src/styles/01-settings/_settings.pagination.scss +5 -3
  129. package/src/styles/01-settings/_settings.popover.scss +6 -4
  130. package/src/styles/01-settings/_settings.rating.scss +5 -3
  131. package/src/styles/01-settings/_settings.river.scss +8 -6
  132. package/src/styles/01-settings/_settings.sectionintro.scss +8 -6
  133. package/src/styles/01-settings/_settings.select.scss +7 -5
  134. package/src/styles/01-settings/_settings.side-menu.scss +15 -13
  135. package/src/styles/01-settings/_settings.spacing.scss +4 -0
  136. package/src/styles/01-settings/_settings.steps.scss +7 -5
  137. package/src/styles/01-settings/_settings.tabs.scss +7 -5
  138. package/src/styles/01-settings/_settings.testimonials.scss +6 -4
  139. package/src/styles/01-settings/_settings.toggle.scss +3 -1
  140. package/src/styles/01-settings/_settings.tooltip.scss +5 -3
  141. package/src/styles/01-settings/_settings.upload.scss +22 -20
  142. package/src/styles/02-tools/_tools.animations.scss +19 -0
  143. package/src/styles/02-tools/_tools.background.scss +87 -0
  144. package/src/styles/02-tools/_tools.glass.scss +1 -0
  145. package/src/styles/02-tools/_tools.rem.scss +18 -5
  146. package/src/styles/02-tools/_tools.utility-api.scss +32 -26
  147. package/src/styles/03-generic/_generic.root.scss +15 -2
  148. package/src/styles/04-elements/_elements.body.scss +6 -0
  149. package/src/styles/06-components/_components.accordion.scss +24 -4
  150. package/src/styles/06-components/_components.atomix-glass.scss +0 -0
  151. package/src/styles/06-components/_components.avatar-group.scss +2 -1
  152. package/src/styles/06-components/_components.avatar.scss +2 -1
  153. package/src/styles/06-components/_components.badge.scss +36 -1
  154. package/src/styles/06-components/_components.breadcrumb.scss +2 -1
  155. package/src/styles/06-components/_components.button.scss +14 -3
  156. package/src/styles/06-components/_components.callout.scss +44 -4
  157. package/src/styles/06-components/_components.card.scss +21 -2
  158. package/src/styles/06-components/_components.chart.scss +3 -2
  159. package/src/styles/06-components/_components.checkbox.scss +2 -1
  160. package/src/styles/06-components/_components.color-mode-toggle.scss +3 -2
  161. package/src/styles/06-components/_components.countdown.scss +2 -1
  162. package/src/styles/06-components/_components.data-table.scss +7 -6
  163. package/src/styles/06-components/_components.datepicker.scss +20 -1
  164. package/src/styles/06-components/_components.dropdown.scss +11 -4
  165. package/src/styles/06-components/_components.edge-panel.scss +4 -3
  166. package/src/styles/06-components/_components.footer.scss +825 -0
  167. package/src/styles/06-components/_components.form-group.scss +1 -0
  168. package/src/styles/06-components/_components.hero.scss +4 -4
  169. package/src/styles/06-components/_components.image-gallery.scss +1 -0
  170. package/src/styles/06-components/_components.input.scss +33 -2
  171. package/src/styles/06-components/_components.list-group.scss +3 -2
  172. package/src/styles/06-components/_components.list.scss +2 -1
  173. package/src/styles/06-components/_components.menu.scss +5 -4
  174. package/src/styles/06-components/_components.messages.scss +8 -7
  175. package/src/styles/06-components/_components.modal.scss +3 -2
  176. package/src/styles/06-components/_components.nav.scss +6 -5
  177. package/src/styles/06-components/_components.navbar.scss +4 -3
  178. package/src/styles/06-components/_components.pagination.scss +2 -1
  179. package/src/styles/06-components/_components.photoviewer.scss +4 -3
  180. package/src/styles/06-components/_components.popover.scss +3 -2
  181. package/src/styles/06-components/_components.product-review.scss +3 -2
  182. package/src/styles/06-components/_components.progress.scss +3 -2
  183. package/src/styles/06-components/_components.river.scss +3 -2
  184. package/src/styles/06-components/_components.sectionintro.scss +2 -1
  185. package/src/styles/06-components/_components.select.scss +5 -4
  186. package/src/styles/06-components/_components.side-menu.scss +8 -7
  187. package/src/styles/06-components/_components.skeleton.scss +3 -2
  188. package/src/styles/06-components/_components.slider.scss +7 -6
  189. package/src/styles/06-components/_components.spinner.scss +1 -0
  190. package/src/styles/06-components/_components.steps.scss +3 -2
  191. package/src/styles/06-components/_components.tabs.scss +4 -3
  192. package/src/styles/06-components/_components.testimonials.scss +2 -1
  193. package/src/styles/06-components/_components.todo.scss +3 -2
  194. package/src/styles/06-components/_components.toggle.scss +5 -4
  195. package/src/styles/06-components/_components.tooltip.scss +3 -2
  196. package/src/styles/06-components/_components.upload.scss +4 -3
  197. package/src/styles/06-components/_components.video-player.scss +50 -27
  198. package/src/styles/06-components/_index.scss +2 -0
  199. package/src/styles/99-utilities/_utilities.glass-fixes.scss +48 -0
  200. package/dist/themes/yabai.css +0 -13711
  201. package/dist/themes/yabai.min.css +0 -189
@@ -32,6 +32,10 @@ export * from './useTodo';
32
32
  export * from './useCheckbox';
33
33
  export * from './useForm';
34
34
  export * from './useFormGroup';
35
+
36
+ // GlassContainer composables
37
+ export * from './useGlassContainer';
38
+ export * from './useAtomixGlass';
35
39
  export * from './useInput';
36
40
  export * from './useRadio';
37
41
  export * from './useSelect';
@@ -0,0 +1,71 @@
1
+ import { useCallback, useEffect, useRef, useState } from 'react';
2
+
3
+ export interface UseAtomixGlassOptions {
4
+ size?: 'sm' | 'md' | 'lg';
5
+ mode?: 'standard' | 'polar' | 'prominent' | 'shader';
6
+ overLight?: boolean;
7
+ clickable?: boolean;
8
+ active?: boolean;
9
+ className?: string;
10
+ }
11
+
12
+ export interface UseAtomixGlassReturn {
13
+ generateGlassClass: (options: UseAtomixGlassOptions) => string;
14
+ generateElementClass: (element: string, modifier?: string) => string;
15
+ }
16
+
17
+ /**
18
+ * Hook for generating AtomixGlass CSS classes following BEM methodology
19
+ */
20
+ export function useAtomixGlass(): UseAtomixGlassReturn {
21
+ // Generate CSS class for the main glass component
22
+ const generateGlassClass = useCallback((options: UseAtomixGlassOptions) => {
23
+ const classes = ['c-atomix-glass'];
24
+
25
+ // Add size modifier
26
+ if (options.size && options.size !== 'md') {
27
+ classes.push(`c-atomix-glass--${options.size}`);
28
+ }
29
+
30
+ // Add mode modifier
31
+ if (options.mode && options.mode !== 'standard') {
32
+ classes.push(`c-atomix-glass--${options.mode}`);
33
+ }
34
+
35
+ // Add state modifiers
36
+ if (options.clickable) {
37
+ classes.push('c-atomix-glass--clickable');
38
+ }
39
+
40
+ if (options.overLight) {
41
+ classes.push('c-atomix-glass--over-light');
42
+ }
43
+
44
+ if (options.active) {
45
+ classes.push('c-atomix-glass--active');
46
+ }
47
+
48
+ // Add custom className
49
+ if (options.className) {
50
+ classes.push(options.className);
51
+ }
52
+
53
+ return classes.filter(Boolean).join(' ');
54
+ }, []);
55
+
56
+ // Generate CSS class for elements
57
+ const generateElementClass = useCallback((element: string, modifier?: string) => {
58
+ const baseClass = `c-atomix-glass__${element}`;
59
+
60
+ if (modifier) {
61
+ return `${baseClass}--${modifier}`;
62
+ }
63
+
64
+ return baseClass;
65
+ }, []);
66
+
67
+ return {
68
+ generateGlassClass,
69
+ generateElementClass,
70
+ };
71
+ }
@@ -27,6 +27,7 @@ export function useButton(initialProps?: Partial<ButtonProps>) {
27
27
  disabled = defaultProps.disabled,
28
28
  rounded = defaultProps.rounded,
29
29
  iconOnly = false,
30
+ glass = defaultProps.glass,
30
31
  className = '',
31
32
  } = props;
32
33
 
@@ -34,8 +35,9 @@ export function useButton(initialProps?: Partial<ButtonProps>) {
34
35
  const iconOnlyClass = iconOnly ? 'c-btn--icon' : '';
35
36
  const roundedClass = rounded ? 'c-btn--rounded' : '';
36
37
  const disabledClass = disabled ? 'c-btn--disabled' : '';
38
+ const glassClass = glass ? 'c-btn--glass' : '';
37
39
 
38
- return `c-btn c-btn--${variant} ${sizeClass} ${iconOnlyClass} ${roundedClass} ${disabledClass} ${className}`.trim();
40
+ return `c-btn c-btn--${variant} ${sizeClass} ${iconOnlyClass} ${roundedClass} ${disabledClass} ${glassClass} ${className}`.trim();
39
41
  };
40
42
 
41
43
  /**
@@ -11,6 +11,7 @@ export function useCallout(initialProps?: Partial<CalloutProps>) {
11
11
  variant: 'primary',
12
12
  oneLine: false,
13
13
  toast: false,
14
+ glass: false,
14
15
  ...initialProps,
15
16
  };
16
17
 
@@ -24,14 +25,16 @@ export function useCallout(initialProps?: Partial<CalloutProps>) {
24
25
  variant = defaultProps.variant,
25
26
  oneLine = defaultProps.oneLine,
26
27
  toast = defaultProps.toast,
28
+ glass = defaultProps.glass,
27
29
  className = '',
28
30
  } = props;
29
31
 
30
32
  const oneLineClass = oneLine ? 'c-callout--oneline' : '';
31
33
  const toastClass = toast ? 'c-callout--toast' : '';
32
34
  const variantClass = variant ? `c-callout--${variant}` : '';
35
+ const glassClass = glass ? 'c-callout--glass' : '';
33
36
 
34
- return `c-callout ${variantClass} ${oneLineClass} ${toastClass} ${className}`.trim();
37
+ return `c-callout ${variantClass} ${oneLineClass} ${toastClass} ${glassClass} ${className}`.trim();
35
38
  };
36
39
 
37
40
  /**
@@ -0,0 +1,85 @@
1
+ import { FooterLayout, ThemeColor, Size, SocialLink } from '../types/components';
2
+ import { FOOTER } from '../constants/components';
3
+
4
+ export interface UseFooterOptions {
5
+ layout?: FooterLayout;
6
+ variant?: ThemeColor;
7
+ size?: Size;
8
+ sticky?: boolean;
9
+ showNewsletter?: boolean;
10
+ showBackToTop?: boolean;
11
+ socialLinks?: SocialLink[];
12
+ onNewsletterSubmit?: (email: string) => void | Promise<void>;
13
+ onBackToTop?: () => void;
14
+ className?: string;
15
+ }
16
+
17
+
18
+
19
+ export function useFooter(options: UseFooterOptions = {}) {
20
+ const {
21
+ layout = FOOTER.DEFAULTS.LAYOUT,
22
+ variant = FOOTER.DEFAULTS.VARIANT,
23
+ size = FOOTER.DEFAULTS.SIZE,
24
+ sticky = FOOTER.DEFAULTS.STICKY,
25
+ showNewsletter = FOOTER.DEFAULTS.SHOW_NEWSLETTER,
26
+ showBackToTop = FOOTER.DEFAULTS.SHOW_BACK_TO_TOP,
27
+ socialLinks = [],
28
+ onNewsletterSubmit,
29
+ onBackToTop,
30
+ className = '',
31
+ } = options;
32
+
33
+ // Generate footer classes
34
+ const footerClass = (() => {
35
+ const classes = [
36
+ FOOTER.CLASSES.BASE,
37
+ FOOTER.CLASSES[layout.toUpperCase() as keyof typeof FOOTER.CLASSES] || FOOTER.CLASSES.COLUMNS,
38
+ `c-footer--${variant}`,
39
+ FOOTER.CLASSES[size.toUpperCase() as keyof typeof FOOTER.CLASSES] || FOOTER.CLASSES.MD,
40
+ sticky && FOOTER.CLASSES.STICKY,
41
+ className,
42
+ ];
43
+ return classes.filter(Boolean).join(' ');
44
+ })();
45
+
46
+ const containerClass = FOOTER.CLASSES.CONTAINER;
47
+ const brandClass = FOOTER.CLASSES.BRAND;
48
+ const sectionsClass = (() => {
49
+ const classes = [
50
+ FOOTER.CLASSES.SECTIONS,
51
+ layout === 'columns' && 'c-footer__sections--columns',
52
+ layout === 'centered' && 'c-footer__sections--centered',
53
+ layout === 'stacked' && 'c-footer__sections--stacked',
54
+ ];
55
+ return classes.filter(Boolean).join(' ');
56
+ })();
57
+ const bottomClass = FOOTER.CLASSES.BOTTOM;
58
+
59
+ // Handle newsletter submission
60
+ const handleNewsletterSubmit = (email: string) => {
61
+ if (onNewsletterSubmit) {
62
+ onNewsletterSubmit(email);
63
+ }
64
+ };
65
+
66
+ // Handle back to top
67
+ const handleBackToTop = () => {
68
+ if (onBackToTop) {
69
+ onBackToTop();
70
+ } else {
71
+ window.scrollTo({ top: 0, behavior: 'smooth' });
72
+ }
73
+ };
74
+
75
+ return {
76
+ footerClass,
77
+ containerClass,
78
+ brandClass,
79
+ sectionsClass,
80
+ bottomClass,
81
+ handleNewsletterSubmit,
82
+ handleBackToTop,
83
+ socialLinks,
84
+ };
85
+ }
@@ -0,0 +1,168 @@
1
+ import { useCallback, useEffect, useId, useRef, useState } from 'react';
2
+ import { GlassContainerProps, MousePosition } from '../types/components';
3
+
4
+ /**
5
+ * Custom hook for managing GlassContainer state and interactions
6
+ */
7
+ export function useGlassContainer(props: GlassContainerProps) {
8
+ const {
9
+ glassSize = { width: 270, height: 69 },
10
+ elasticity = 0.15,
11
+ mouseContainer,
12
+ globalMousePos: externalGlobalMousePos,
13
+ mouseOffset: externalMouseOffset,
14
+ } = props;
15
+
16
+ const filterId = useId();
17
+ const glassRef = useRef<HTMLDivElement>(null);
18
+ const [isHovered, setIsHovered] = useState(false);
19
+ const [isActive, setIsActive] = useState(false);
20
+ const [currentGlassSize, setCurrentGlassSize] = useState(glassSize);
21
+ const [internalGlobalMousePos, setInternalGlobalMousePos] = useState<MousePosition>({ x: 0, y: 0 });
22
+ const [internalMouseOffset, setInternalMouseOffset] = useState<MousePosition>({ x: 0, y: 0 });
23
+
24
+ // Use external mouse position if provided, otherwise use internal
25
+ const globalMousePos = externalGlobalMousePos || internalGlobalMousePos;
26
+ const mouseOffset = externalMouseOffset || internalMouseOffset;
27
+
28
+ // Internal mouse tracking
29
+ const handleMouseMove = useCallback(
30
+ (e: MouseEvent) => {
31
+ const container = mouseContainer?.current || glassRef.current;
32
+ if (!container) return;
33
+
34
+ const rect = container.getBoundingClientRect();
35
+ const centerX = rect.left + rect.width / 2;
36
+ const centerY = rect.top + rect.height / 2;
37
+
38
+ setInternalMouseOffset({
39
+ x: ((e.clientX - centerX) / rect.width) * 100,
40
+ y: ((e.clientY - centerY) / rect.height) * 100,
41
+ });
42
+
43
+ setInternalGlobalMousePos({
44
+ x: e.clientX,
45
+ y: e.clientY,
46
+ });
47
+ },
48
+ [mouseContainer]
49
+ );
50
+
51
+ // Set up mouse tracking if no external mouse position is provided
52
+ useEffect(() => {
53
+ if (externalGlobalMousePos && externalMouseOffset) return;
54
+
55
+ const container = mouseContainer?.current || glassRef.current;
56
+ if (!container) return;
57
+
58
+ container.addEventListener('mousemove', handleMouseMove);
59
+ return () => container.removeEventListener('mousemove', handleMouseMove);
60
+ }, [handleMouseMove, mouseContainer, externalGlobalMousePos, externalMouseOffset]);
61
+
62
+ // Calculate directional scaling based on mouse position
63
+ const calculateDirectionalScale = useCallback(() => {
64
+ if (!globalMousePos.x || !globalMousePos.y || !glassRef.current) {
65
+ return 'scale(1)';
66
+ }
67
+
68
+ const rect = glassRef.current.getBoundingClientRect();
69
+ const pillCenterX = rect.left + rect.width / 2;
70
+ const pillCenterY = rect.top + rect.height / 2;
71
+ const pillWidth = currentGlassSize.width;
72
+ const pillHeight = currentGlassSize.height;
73
+
74
+ const deltaX = globalMousePos.x - pillCenterX;
75
+ const deltaY = globalMousePos.y - pillCenterY;
76
+
77
+ const edgeDistanceX = Math.max(0, Math.abs(deltaX) - pillWidth / 2);
78
+ const edgeDistanceY = Math.max(0, Math.abs(deltaY) - pillHeight / 2);
79
+ const edgeDistance = Math.sqrt(edgeDistanceX * edgeDistanceX + edgeDistanceY * edgeDistanceY);
80
+
81
+ const activationZone = 200;
82
+ if (edgeDistance > activationZone) return 'scale(1)';
83
+
84
+ const fadeInFactor = 1 - edgeDistance / activationZone;
85
+ const centerDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
86
+ if (centerDistance === 0) return 'scale(1)';
87
+
88
+ const normalizedX = deltaX / centerDistance;
89
+ const normalizedY = deltaY / centerDistance;
90
+ const stretchIntensity = Math.min(centerDistance / 300, 1) * elasticity * fadeInFactor;
91
+
92
+ const scaleX = 1 + Math.abs(normalizedX) * stretchIntensity * 0.3 - Math.abs(normalizedY) * stretchIntensity * 0.15;
93
+ const scaleY = 1 + Math.abs(normalizedY) * stretchIntensity * 0.3 - Math.abs(normalizedX) * stretchIntensity * 0.15;
94
+
95
+ return `scaleX(${Math.max(0.8, scaleX)}) scaleY(${Math.max(0.8, scaleY)})`;
96
+ }, [globalMousePos, elasticity, currentGlassSize]);
97
+
98
+ // Calculate elastic translation
99
+ const calculateElasticTranslation = useCallback(() => {
100
+ if (!glassRef.current) return { x: 0, y: 0 };
101
+
102
+ const rect = glassRef.current.getBoundingClientRect();
103
+ const pillCenterX = rect.left + rect.width / 2;
104
+ const pillCenterY = rect.top + rect.height / 2;
105
+ const pillWidth = currentGlassSize.width;
106
+ const pillHeight = currentGlassSize.height;
107
+
108
+ const edgeDistanceX = Math.max(0, Math.abs(globalMousePos.x - pillCenterX) - pillWidth / 2);
109
+ const edgeDistanceY = Math.max(0, Math.abs(globalMousePos.y - pillCenterY) - pillHeight / 2);
110
+ const edgeDistance = Math.sqrt(edgeDistanceX * edgeDistanceX + edgeDistanceY * edgeDistanceY);
111
+
112
+ const activationZone = 200;
113
+ const fadeInFactor = edgeDistance > activationZone ? 0 : 1 - edgeDistance / activationZone;
114
+
115
+ return {
116
+ x: (globalMousePos.x - pillCenterX) * elasticity * 0.1 * fadeInFactor,
117
+ y: (globalMousePos.y - pillCenterY) * elasticity * 0.1 * fadeInFactor,
118
+ };
119
+ }, [globalMousePos, elasticity, currentGlassSize]);
120
+
121
+ // Update glass size
122
+ useEffect(() => {
123
+ const updateGlassSize = () => {
124
+ if (glassRef.current) {
125
+ const rect = glassRef.current.getBoundingClientRect();
126
+ setCurrentGlassSize({ width: rect.width, height: rect.height });
127
+ }
128
+ };
129
+
130
+ updateGlassSize();
131
+ window.addEventListener('resize', updateGlassSize);
132
+ return () => window.removeEventListener('resize', updateGlassSize);
133
+ }, []);
134
+
135
+ const handleMouseEnter = useCallback(() => {
136
+ setIsHovered(true);
137
+ }, []);
138
+
139
+ const handleMouseLeave = useCallback(() => {
140
+ setIsHovered(false);
141
+ }, []);
142
+
143
+ const handleMouseDown = useCallback(() => {
144
+ setIsActive(true);
145
+ }, []);
146
+
147
+ const handleMouseUp = useCallback(() => {
148
+ setIsActive(false);
149
+ }, []);
150
+
151
+ return {
152
+ filterId,
153
+ glassRef,
154
+ isHovered,
155
+ isActive,
156
+ currentGlassSize,
157
+ globalMousePos,
158
+ mouseOffset,
159
+ calculateDirectionalScale,
160
+ calculateElasticTranslation,
161
+ handleMouseEnter,
162
+ handleMouseLeave,
163
+ handleMouseDown,
164
+ handleMouseUp,
165
+ };
166
+ }
167
+
168
+ export default useGlassContainer;
@@ -35,12 +35,15 @@ export function useSlider(options: UseSliderOptions): UseSliderReturn {
35
35
  speed = 300,
36
36
  allowTouchMove = true,
37
37
  threshold = 50,
38
+ autoplay,
38
39
  onSlideChange,
39
40
  } = options;
40
41
 
41
42
  const containerRef = useRef<HTMLDivElement | null>(null);
42
43
  const wrapperRef = useRef<HTMLDivElement | null>(null);
43
44
  const repositioningRef = useRef(false);
45
+ const autoplayRef = useRef<NodeJS.Timeout | null>(null);
46
+ const [autoplayRunning, setAutoplayRunning] = useState(false);
44
47
 
45
48
  const [realIndex, setRealIndex] = useState(initialSlide);
46
49
  const [internalIndex, setInternalIndex] = useState(0);
@@ -73,6 +76,160 @@ export function useSlider(options: UseSliderOptions): UseSliderReturn {
73
76
  return -(internalIndex * slideWidth) + dragOffset;
74
77
  }, [slideWidth, internalIndex, dragOffset]);
75
78
 
79
+ // Autoplay effect
80
+ useEffect(() => {
81
+ if (!autoplay) {
82
+ if (autoplayRef.current) {
83
+ clearInterval(autoplayRef.current);
84
+ autoplayRef.current = null;
85
+ }
86
+ setAutoplayRunning(false);
87
+ return;
88
+ }
89
+
90
+ const autoplayParams = typeof autoplay === 'boolean' ? { delay: 3000 } : autoplay;
91
+ const { delay = 3000, pauseOnMouseEnter = false, disableOnInteraction = false, reverseDirection = false } = autoplayParams;
92
+
93
+ // Clear any existing interval
94
+ if (autoplayRef.current) {
95
+ clearInterval(autoplayRef.current);
96
+ }
97
+
98
+ // Create new interval
99
+ autoplayRef.current = setInterval(() => {
100
+ // We need to use a functional update to get the latest values
101
+ setRealIndex(prevRealIndex => {
102
+ if (isTransitioning) return prevRealIndex;
103
+
104
+ // Stop autoplay on interaction if disableOnInteraction is true
105
+ if (disableOnInteraction && autoplayRef.current) {
106
+ clearInterval(autoplayRef.current);
107
+ autoplayRef.current = null;
108
+ setAutoplayRunning(false);
109
+ }
110
+
111
+ let nextIndex;
112
+ if (loop) {
113
+ nextIndex = (prevRealIndex + 1) % slides.length;
114
+ } else {
115
+ nextIndex = Math.min(prevRealIndex + 1, slides.length - slidesToShow);
116
+ }
117
+
118
+ // Trigger the slide change
119
+ if (reverseDirection) {
120
+ // For reverse direction, we would go to previous slide
121
+ const prevIndex = loop ? (prevRealIndex === 0 ? slides.length - 1 : prevRealIndex - 1) : Math.max(prevRealIndex - 1, 0);
122
+ setInternalIndex(loop ? slides.length + prevIndex : prevIndex);
123
+ setIsTransitioning(true);
124
+ setDragOffset(0);
125
+
126
+ setTimeout(() => {
127
+ setIsTransitioning(false);
128
+ onSlideChange?.(prevIndex);
129
+ }, speed);
130
+
131
+ return prevIndex;
132
+ } else {
133
+ // Normal direction
134
+ setInternalIndex(loop ? slides.length + nextIndex : nextIndex);
135
+ setIsTransitioning(true);
136
+ setDragOffset(0);
137
+
138
+ setTimeout(() => {
139
+ setIsTransitioning(false);
140
+ onSlideChange?.(nextIndex);
141
+
142
+ // Reposition after transition ends for looped sliders
143
+ if (loop && nextIndex >= slides.length * 2) {
144
+ repositioningRef.current = true;
145
+ setInternalIndex(slides.length + nextIndex);
146
+ setTimeout(() => {
147
+ repositioningRef.current = false;
148
+ }, 0);
149
+ }
150
+ }, speed);
151
+
152
+ return nextIndex;
153
+ }
154
+ });
155
+ }, delay);
156
+
157
+ setAutoplayRunning(true);
158
+
159
+ // Handle pause on mouse enter/leave if enabled
160
+ let containerElement: HTMLDivElement | null = null;
161
+ const handleMouseEnter = () => {
162
+ if (autoplayRef.current) {
163
+ clearInterval(autoplayRef.current);
164
+ autoplayRef.current = null;
165
+ setAutoplayRunning(false);
166
+ }
167
+ };
168
+
169
+ const handleMouseLeave = () => {
170
+ // Restart autoplay
171
+ if (autoplayRef.current) {
172
+ clearInterval(autoplayRef.current);
173
+ }
174
+
175
+ autoplayRef.current = setInterval(() => {
176
+ setRealIndex(prevRealIndex => {
177
+ if (isTransitioning) return prevRealIndex;
178
+
179
+ let nextIndex;
180
+ if (loop) {
181
+ nextIndex = (prevRealIndex + 1) % slides.length;
182
+ } else {
183
+ nextIndex = Math.min(prevRealIndex + 1, slides.length - slidesToShow);
184
+ }
185
+
186
+ setInternalIndex(loop ? slides.length + nextIndex : nextIndex);
187
+ setIsTransitioning(true);
188
+ setDragOffset(0);
189
+
190
+ setTimeout(() => {
191
+ setIsTransitioning(false);
192
+ onSlideChange?.(nextIndex);
193
+
194
+ if (loop) {
195
+ // Reposition after transition ends
196
+ if (nextIndex >= slides.length * 2) {
197
+ repositioningRef.current = true;
198
+ setInternalIndex(slides.length + nextIndex);
199
+ setTimeout(() => {
200
+ repositioningRef.current = false;
201
+ }, 0);
202
+ }
203
+ }
204
+ }, speed);
205
+
206
+ return nextIndex;
207
+ });
208
+ }, delay);
209
+
210
+ setAutoplayRunning(true);
211
+ };
212
+
213
+ if (pauseOnMouseEnter && containerRef.current) {
214
+ containerElement = containerRef.current;
215
+ containerElement.addEventListener('mouseenter', handleMouseEnter);
216
+ containerElement.addEventListener('mouseleave', handleMouseLeave);
217
+ }
218
+
219
+ // Cleanup
220
+ return () => {
221
+ if (autoplayRef.current) {
222
+ clearInterval(autoplayRef.current);
223
+ autoplayRef.current = null;
224
+ }
225
+ if (containerElement) {
226
+ containerElement.removeEventListener('mouseenter', handleMouseEnter);
227
+ containerElement.removeEventListener('mouseleave', handleMouseLeave);
228
+ }
229
+ setAutoplayRunning(false);
230
+ };
231
+ }, [autoplay, slides.length, loop, slidesToShow, isTransitioning, speed, onSlideChange, repositioningRef]);
232
+
76
233
  // Initialize
77
234
  useEffect(() => {
78
235
  if (loop) {
@@ -100,6 +257,13 @@ export function useSlider(options: UseSliderOptions): UseSliderReturn {
100
257
  const slideNext = useCallback(() => {
101
258
  if (isTransitioning) return;
102
259
 
260
+ // Stop autoplay on interaction if disableOnInteraction is true
261
+ if (autoplay && typeof autoplay === 'object' && autoplay.disableOnInteraction && autoplayRef.current) {
262
+ clearInterval(autoplayRef.current);
263
+ autoplayRef.current = null;
264
+ setAutoplayRunning(false);
265
+ }
266
+
103
267
  if (loop) {
104
268
  const nextRealIndex = (realIndex + 1) % slides.length;
105
269
  const nextInternalIndex = internalIndex + 1;
@@ -145,11 +309,19 @@ export function useSlider(options: UseSliderOptions): UseSliderReturn {
145
309
  onSlideChange,
146
310
  allSlides.length,
147
311
  loopedSlides,
312
+ autoplay
148
313
  ]);
149
314
 
150
315
  const slidePrev = useCallback(() => {
151
316
  if (isTransitioning) return;
152
317
 
318
+ // Stop autoplay on interaction if disableOnInteraction is true
319
+ if (autoplay && typeof autoplay === 'object' && autoplay.disableOnInteraction && autoplayRef.current) {
320
+ clearInterval(autoplayRef.current);
321
+ autoplayRef.current = null;
322
+ setAutoplayRunning(false);
323
+ }
324
+
153
325
  if (loop) {
154
326
  const prevRealIndex = realIndex === 0 ? slides.length - 1 : realIndex - 1;
155
327
  const prevInternalIndex = internalIndex - 1;
@@ -194,12 +366,20 @@ export function useSlider(options: UseSliderOptions): UseSliderReturn {
194
366
  onSlideChange,
195
367
  allSlides.length,
196
368
  loopedSlides,
369
+ autoplay
197
370
  ]);
198
371
 
199
372
  const goToSlide = useCallback(
200
373
  (index: number) => {
201
374
  if (isTransitioning || index === realIndex) return;
202
375
 
376
+ // Stop autoplay on interaction if disableOnInteraction is true
377
+ if (autoplay && typeof autoplay === 'object' && autoplay.disableOnInteraction && autoplayRef.current) {
378
+ clearInterval(autoplayRef.current);
379
+ autoplayRef.current = null;
380
+ setAutoplayRunning(false);
381
+ }
382
+
203
383
  setIsTransitioning(true);
204
384
  setDragOffset(0);
205
385
 
@@ -211,13 +391,20 @@ export function useSlider(options: UseSliderOptions): UseSliderReturn {
211
391
  onSlideChange?.(index);
212
392
  }, speed);
213
393
  },
214
- [realIndex, isTransitioning, speed, onSlideChange, loop, loopedSlides]
394
+ [realIndex, isTransitioning, speed, onSlideChange, loop, loopedSlides, autoplay]
215
395
  );
216
396
 
217
397
  const handleTouchStart = useCallback(
218
398
  (e: React.TouchEvent | React.MouseEvent) => {
219
399
  if (!allowTouchMove) return;
220
400
 
401
+ // Stop autoplay on interaction if disableOnInteraction is true
402
+ if (autoplay && typeof autoplay === 'object' && autoplay.disableOnInteraction && autoplayRef.current) {
403
+ clearInterval(autoplayRef.current);
404
+ autoplayRef.current = null;
405
+ setAutoplayRunning(false);
406
+ }
407
+
221
408
  const client =
222
409
  direction === 'horizontal'
223
410
  ? 'touches' in e
@@ -230,7 +417,7 @@ export function useSlider(options: UseSliderOptions): UseSliderReturn {
230
417
  setTouching(true);
231
418
  setDragOffset(0);
232
419
  },
233
- [allowTouchMove, direction]
420
+ [allowTouchMove, direction, autoplay]
234
421
  );
235
422
 
236
423
  const handleTouchMove = useCallback(
@@ -293,7 +480,7 @@ export function useSlider(options: UseSliderOptions): UseSliderReturn {
293
480
  isBeginning: !loop && realIndex === 0,
294
481
  isEnd: !loop && realIndex >= slides.length - slidesToShow,
295
482
  progress: slides.length > 0 ? realIndex / (slides.length - 1) : 0,
296
- autoplayRunning: false,
483
+ autoplayRunning,
297
484
  transitioning: isTransitioning,
298
485
  touching,
299
486
  translate: translateValue,
@@ -336,4 +523,4 @@ export function useSlider(options: UseSliderOptions): UseSliderReturn {
336
523
  loopedSlides,
337
524
  repositioningRef,
338
525
  };
339
- }
526
+ }