@shohojdhara/atomix 0.2.6 → 0.2.8

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 (48) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/README.md +3 -3
  3. package/dist/atomix.css +373 -55
  4. package/dist/atomix.min.css +2 -2
  5. package/dist/index.d.ts +458 -19
  6. package/dist/index.esm.js +975 -261
  7. package/dist/index.esm.js.map +1 -1
  8. package/dist/index.js +984 -261
  9. package/dist/index.js.map +1 -1
  10. package/dist/index.min.js +1 -1
  11. package/dist/index.min.js.map +1 -1
  12. package/dist/themes/applemix.css +373 -55
  13. package/dist/themes/applemix.min.css +2 -2
  14. package/dist/themes/boomdevs.css +372 -54
  15. package/dist/themes/boomdevs.min.css +2 -2
  16. package/dist/themes/esrar.css +373 -55
  17. package/dist/themes/esrar.min.css +2 -2
  18. package/dist/themes/flashtrade.css +1776 -612
  19. package/dist/themes/flashtrade.min.css +113 -7
  20. package/dist/themes/mashroom.css +372 -54
  21. package/dist/themes/mashroom.min.css +2 -2
  22. package/dist/themes/shaj-default.css +372 -54
  23. package/dist/themes/shaj-default.min.css +2 -2
  24. package/package.json +1 -1
  25. package/src/components/Button/Button.stories.tsx +199 -0
  26. package/src/components/Button/Button.tsx +238 -78
  27. package/src/components/Card/Card.stories.tsx +202 -0
  28. package/src/components/Card/Card.tsx +248 -77
  29. package/src/components/Form/Input.stories.tsx +228 -2
  30. package/src/components/Hero/Hero.stories.tsx +297 -0
  31. package/src/components/Hero/Hero.tsx +79 -0
  32. package/src/components/{Tab/Tab.stories.tsx → Tabs/Tabs.stories.tsx} +9 -9
  33. package/src/components/{Tab/Tab.tsx → Tabs/Tabs.tsx} +7 -7
  34. package/src/components/Tabs/index.ts +2 -0
  35. package/src/components/Tooltip/Tooltip.tsx +68 -66
  36. package/src/components/index.ts +12 -2
  37. package/src/lib/composables/useButton.ts +37 -5
  38. package/src/lib/composables/useHero.ts +33 -4
  39. package/src/lib/composables/useHeroBackgroundSlider.ts +228 -0
  40. package/src/lib/composables/useInput.ts +39 -1
  41. package/src/lib/constants/components.ts +59 -0
  42. package/src/lib/types/components.ts +376 -4
  43. package/src/styles/01-settings/_settings.tooltip.scss +2 -2
  44. package/src/styles/06-components/_components.button.scss +100 -0
  45. package/src/styles/06-components/_components.card.scss +219 -1
  46. package/src/styles/06-components/_components.hero.scss +51 -1
  47. package/src/styles/06-components/_components.tooltip.scss +89 -66
  48. package/src/components/Tab/index.ts +0 -2
@@ -0,0 +1,228 @@
1
+ import React, { useEffect, useRef, useState, useCallback, useMemo } from 'react';
2
+ import { HeroBackgroundSliderConfig } from '../types/components';
3
+
4
+ /**
5
+ * Hook result interface for hero background slider
6
+ */
7
+ export interface UseHeroBackgroundSliderResult {
8
+ /**
9
+ * Current active slide index
10
+ */
11
+ currentIndex: number;
12
+
13
+ /**
14
+ * Whether a transition is currently in progress
15
+ */
16
+ isTransitioning: boolean;
17
+
18
+ /**
19
+ * Array of refs for slide container elements
20
+ */
21
+ slideRefs: React.RefObject<HTMLDivElement>[];
22
+
23
+ /**
24
+ * Array of refs for video elements
25
+ */
26
+ videoRefs: React.RefObject<HTMLVideoElement>[];
27
+
28
+ /**
29
+ * Handle slide transition to next index
30
+ */
31
+ handleSlideTransition: (nextIndex: number) => void;
32
+
33
+ /**
34
+ * Pause autoplay
35
+ */
36
+ pauseAutoplay: () => void;
37
+
38
+ /**
39
+ * Resume autoplay
40
+ */
41
+ resumeAutoplay: () => void;
42
+ }
43
+
44
+ /**
45
+ * Hook for Hero background slider functionality
46
+ * @param config - Background slider configuration
47
+ * @returns Slider state and methods
48
+ */
49
+ export function useHeroBackgroundSlider(
50
+ config: HeroBackgroundSliderConfig
51
+ ): UseHeroBackgroundSliderResult {
52
+ const {
53
+ slides,
54
+ autoplay,
55
+ loop = true,
56
+ transition = 'fade',
57
+ transitionDuration = 1000,
58
+ } = config;
59
+
60
+ const [currentIndex, setCurrentIndex] = useState(0);
61
+ const [isTransitioning, setIsTransitioning] = useState(false);
62
+ const autoplayRef = useRef<NodeJS.Timeout | null>(null);
63
+ const isPausedRef = useRef(false);
64
+
65
+ // Create refs for slide containers
66
+ const slideRefs = useMemo(
67
+ () => slides.map(() => React.createRef<HTMLDivElement>()),
68
+ [slides.length]
69
+ );
70
+
71
+ // Create refs for video elements
72
+ const videoRefs = useMemo(
73
+ () => slides.map(() => React.createRef<HTMLVideoElement>()),
74
+ [slides.length]
75
+ );
76
+
77
+ /**
78
+ * Handle slide transition
79
+ */
80
+ const handleSlideTransition = useCallback(
81
+ (nextIndex: number) => {
82
+ if (nextIndex === currentIndex || isTransitioning) return;
83
+ if (nextIndex < 0 || nextIndex >= slides.length) return;
84
+
85
+ setIsTransitioning(true);
86
+
87
+ // Update current index
88
+ setCurrentIndex(nextIndex);
89
+
90
+ // Play video if it's a video slide
91
+ const currentVideo = videoRefs[nextIndex]?.current;
92
+ if (currentVideo && slides[nextIndex].type === 'video') {
93
+ const videoOptions = slides[nextIndex].videoOptions || {};
94
+ if (videoOptions.autoplay !== false) {
95
+ currentVideo.play().catch(() => {
96
+ // Ignore autoplay errors
97
+ });
98
+ }
99
+ }
100
+
101
+ // Pause previous video if it exists
102
+ const prevVideo = videoRefs[currentIndex]?.current;
103
+ if (prevVideo && slides[currentIndex].type === 'video') {
104
+ prevVideo.pause();
105
+ }
106
+
107
+ // Reset transitioning state after transition duration
108
+ setTimeout(() => {
109
+ setIsTransitioning(false);
110
+ }, transitionDuration);
111
+ },
112
+ [currentIndex, isTransitioning, slides, videoRefs, transitionDuration]
113
+ );
114
+
115
+ /**
116
+ * Move to next slide
117
+ */
118
+ const nextSlide = useCallback(() => {
119
+ if (slides.length === 0) return;
120
+
121
+ let nextIndex: number;
122
+ if (loop) {
123
+ nextIndex = (currentIndex + 1) % slides.length;
124
+ } else {
125
+ nextIndex = Math.min(currentIndex + 1, slides.length - 1);
126
+ }
127
+
128
+ handleSlideTransition(nextIndex);
129
+ }, [currentIndex, slides.length, loop, handleSlideTransition]);
130
+
131
+ /**
132
+ * Pause autoplay
133
+ */
134
+ const pauseAutoplay = useCallback(() => {
135
+ isPausedRef.current = true;
136
+ if (autoplayRef.current) {
137
+ clearInterval(autoplayRef.current);
138
+ autoplayRef.current = null;
139
+ }
140
+ }, []);
141
+
142
+ /**
143
+ * Resume autoplay
144
+ */
145
+ const resumeAutoplay = useCallback(() => {
146
+ if (isPausedRef.current && autoplay && slides.length > 1) {
147
+ isPausedRef.current = false;
148
+ const delay = typeof autoplay === 'object' ? autoplay.delay : 3000;
149
+
150
+ // Restart autoplay
151
+ if (!autoplayRef.current) {
152
+ autoplayRef.current = setInterval(() => {
153
+ if (!isPausedRef.current && !isTransitioning) {
154
+ nextSlide();
155
+ }
156
+ }, delay);
157
+ }
158
+ }
159
+ }, [autoplay, slides.length, nextSlide, isTransitioning]);
160
+
161
+ // Autoplay effect
162
+ useEffect(() => {
163
+ if (!autoplay || slides.length <= 1) {
164
+ return;
165
+ }
166
+
167
+ const delay = typeof autoplay === 'object' ? autoplay.delay : 3000;
168
+ const pauseOnHover = typeof autoplay === 'object' ? autoplay.pauseOnHover : false;
169
+
170
+ // Clear any existing interval
171
+ if (autoplayRef.current) {
172
+ clearInterval(autoplayRef.current);
173
+ autoplayRef.current = null;
174
+ }
175
+
176
+ // Start autoplay if not paused
177
+ if (!isPausedRef.current) {
178
+ autoplayRef.current = setInterval(() => {
179
+ if (!isPausedRef.current && !isTransitioning) {
180
+ nextSlide();
181
+ }
182
+ }, delay);
183
+ }
184
+
185
+ return () => {
186
+ if (autoplayRef.current) {
187
+ clearInterval(autoplayRef.current);
188
+ autoplayRef.current = null;
189
+ }
190
+ };
191
+ }, [autoplay, slides.length, nextSlide, isTransitioning]);
192
+
193
+ // Initialize first video if needed
194
+ useEffect(() => {
195
+ if (slides.length > 0 && slides[currentIndex]?.type === 'video') {
196
+ const video = videoRefs[currentIndex]?.current;
197
+ if (video) {
198
+ const videoOptions = slides[currentIndex].videoOptions || {};
199
+ if (videoOptions.autoplay !== false) {
200
+ video.play().catch(() => {
201
+ // Ignore autoplay errors
202
+ });
203
+ }
204
+ }
205
+ }
206
+ }, [currentIndex, slides, videoRefs]);
207
+
208
+ // Cleanup on unmount
209
+ useEffect(() => {
210
+ return () => {
211
+ if (autoplayRef.current) {
212
+ clearInterval(autoplayRef.current);
213
+ autoplayRef.current = null;
214
+ }
215
+ };
216
+ }, []);
217
+
218
+ return {
219
+ currentIndex,
220
+ isTransitioning,
221
+ slideRefs,
222
+ videoRefs,
223
+ handleSlideTransition,
224
+ pauseAutoplay,
225
+ resumeAutoplay,
226
+ };
227
+ }
228
+
@@ -6,7 +6,14 @@ import { INPUT } from '../constants/components';
6
6
  * @param initialProps - Initial input properties
7
7
  * @returns Input state and methods
8
8
  */
9
- export function useInput(initialProps?: Partial<InputProps>) {
9
+ export function useInput(initialProps?: Partial<InputProps> & {
10
+ prefixIcon?: boolean;
11
+ suffixIcon?: boolean;
12
+ clearable?: boolean;
13
+ showCounter?: boolean;
14
+ showPasswordToggle?: boolean;
15
+ fullWidth?: boolean;
16
+ }) {
10
17
  // Default input properties
11
18
  const defaultProps: Partial<InputProps> = {
12
19
  size: 'md',
@@ -51,8 +58,39 @@ export function useInput(initialProps?: Partial<InputProps>) {
51
58
  return `${INPUT.CLASSES.BASE} ${sizeClass} ${variantClass} ${textareaClass} ${validationClass} ${disabledClass} ${className}`.trim();
52
59
  };
53
60
 
61
+ /**
62
+ * Generate wrapper class based on properties
63
+ * @param props - Wrapper properties
64
+ * @returns Class string
65
+ */
66
+ const generateWrapperClass = (props: { className?: string }): string => {
67
+ const { className = '' } = props;
68
+ const {
69
+ prefixIcon = false,
70
+ suffixIcon = false,
71
+ clearable = false,
72
+ showCounter = false,
73
+ showPasswordToggle = false,
74
+ fullWidth = false,
75
+ } = initialProps || {};
76
+
77
+ const classes = [INPUT.ELEMENTS.WRAPPER];
78
+
79
+ if (prefixIcon) classes.push(INPUT.CLASSES.PREFIX_ICON);
80
+ if (suffixIcon || clearable || showPasswordToggle) classes.push(INPUT.CLASSES.SUFFIX_ICON);
81
+ if (clearable) classes.push(INPUT.CLASSES.CLEARABLE);
82
+ if (showCounter) classes.push(INPUT.CLASSES.WITH_COUNTER);
83
+ if (showPasswordToggle) classes.push(INPUT.CLASSES.PASSWORD_TOGGLE);
84
+ if (fullWidth) classes.push(INPUT.CLASSES.FULL_WIDTH);
85
+
86
+ if (className) classes.push(className);
87
+
88
+ return classes.filter(Boolean).join(' ');
89
+ };
90
+
54
91
  return {
55
92
  defaultProps,
56
93
  generateInputClass,
94
+ generateWrapperClass,
57
95
  };
58
96
  }
@@ -35,7 +35,17 @@ export const CLASS_PREFIX = {
35
35
  export const BUTTON = {
36
36
  BASE_CLASS: 'c-btn',
37
37
  ICON_CLASS: 'c-btn__icon',
38
+ LABEL_CLASS: 'c-btn__label',
39
+ SPINNER_CLASS: 'c-btn__spinner',
38
40
  VARIANT_PREFIX: 'c-btn--',
41
+ CLASSES: {
42
+ BASE: 'c-btn',
43
+ LOADING: 'c-btn--loading',
44
+ FULL_WIDTH: 'c-btn--full-width',
45
+ BLOCK: 'c-btn--block',
46
+ ACTIVE: 'c-btn--active',
47
+ SELECTED: 'c-btn--selected',
48
+ },
39
49
  };
40
50
 
41
51
  /**
@@ -177,12 +187,18 @@ export const HERO = {
177
187
  BG_IMAGE: '.c-hero__bg-image',
178
188
  OVERLAY: '.c-hero__overlay',
179
189
  IMAGE_WRAPPER: '.c-hero__image-wrapper',
190
+ SLIDER: '.c-hero__slider',
191
+ SLIDER_ITEM: '.c-hero__slider-item',
180
192
  },
181
193
  CLASSES: {
182
194
  CENTER: 'c-hero--center',
183
195
  RIGHT: 'c-hero--right',
184
196
  LEFT: 'c-hero--left',
185
197
  FULL_VH: 'c-hero--full-vh',
198
+ SLIDER_FADE: 'c-hero__slider--fade',
199
+ SLIDER_SLIDE: 'c-hero__slider--slide',
200
+ SLIDER_CUSTOM: 'c-hero__slider--custom',
201
+ SLIDER_ITEM_ACTIVE: 'c-hero__slider-item--active',
186
202
  },
187
203
  };
188
204
 
@@ -724,6 +740,22 @@ export const INPUT = {
724
740
  INVALID: 'is-invalid',
725
741
  VALID: 'is-valid',
726
742
  DISABLED: 'is-disabled',
743
+ FULL_WIDTH: 'c-input--full-width',
744
+ PREFIX_ICON: 'c-input--prefix-icon',
745
+ SUFFIX_ICON: 'c-input--suffix-icon',
746
+ CLEARABLE: 'c-input--clearable',
747
+ WITH_COUNTER: 'c-input--with-counter',
748
+ PASSWORD_TOGGLE: 'c-input--password-toggle',
749
+ },
750
+ ELEMENTS: {
751
+ WRAPPER: 'c-input-wrapper',
752
+ PREFIX: 'c-input__prefix',
753
+ SUFFIX: 'c-input__suffix',
754
+ CLEAR_BUTTON: 'c-input__clear',
755
+ PASSWORD_TOGGLE: 'c-input__password-toggle',
756
+ COUNTER: 'c-input__counter',
757
+ ERROR_MESSAGE: 'c-input__error',
758
+ HELPER_TEXT: 'c-input__helper',
727
759
  },
728
760
  };
729
761
 
@@ -766,15 +798,42 @@ export const CARD = {
766
798
  },
767
799
  CLASSES: {
768
800
  BASE: 'c-card',
801
+ // Size modifiers
802
+ SM: 'c-card--sm',
803
+ MD: 'c-card--md',
804
+ LG: 'c-card--lg',
805
+ // Layout modifiers
769
806
  ROW: 'c-card--row',
770
807
  FLAT: 'c-card--flat',
808
+ // Appearance modifiers
809
+ FILLED: 'c-card--filled',
810
+ OUTLINED: 'c-card--outlined',
811
+ GHOST: 'c-card--ghost',
812
+ ELEVATED: 'c-card--elevated',
813
+ // Elevation modifiers
814
+ ELEVATION_NONE: 'c-card--elevation-none',
815
+ ELEVATION_SM: 'c-card--elevation-sm',
816
+ ELEVATION_MD: 'c-card--elevation-md',
817
+ ELEVATION_LG: 'c-card--elevation-lg',
818
+ ELEVATION_XL: 'c-card--elevation-xl',
819
+ // State modifiers
771
820
  ACTIVE: 'is-active',
821
+ DISABLED: 'c-card--disabled',
822
+ LOADING: 'c-card--loading',
823
+ SELECTED: 'c-card--selected',
824
+ INTERACTIVE: 'c-card--interactive',
825
+ // Other modifiers
772
826
  FLIPPED: 'is-flipped',
773
827
  FOCUSED: 'is-focused',
774
828
  CLICKABLE: 'is-clickable',
829
+ GLASS: 'c-card--glass',
775
830
  },
776
831
  DEFAULTS: {
777
832
  HOVER: true,
833
+ SIZE: 'md',
834
+ VARIANT: 'primary',
835
+ APPEARANCE: 'filled',
836
+ ELEVATION: 'none',
778
837
  },
779
838
  };
780
839