react-loadly 1.0.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 (117) hide show
  1. package/README.md +220 -0
  2. package/dist/index.d.ts +507 -0
  3. package/dist/index.esm.js +1371 -0
  4. package/dist/index.esm.js.map +1 -0
  5. package/dist/index.js +1409 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/styles.css +2 -0
  8. package/dist/styles.css.map +1 -0
  9. package/dist/types/@types/index.d.ts +15 -0
  10. package/dist/types/@types/index.d.ts.map +1 -0
  11. package/dist/types/@types/interfaces/IBaseLoaderProps.d.ts +30 -0
  12. package/dist/types/@types/interfaces/IBaseLoaderProps.d.ts.map +1 -0
  13. package/dist/types/@types/interfaces/IFallbackLoaderProps.d.ts +14 -0
  14. package/dist/types/@types/interfaces/IFallbackLoaderProps.d.ts.map +1 -0
  15. package/dist/types/@types/interfaces/IFluidLoaderProps.d.ts +8 -0
  16. package/dist/types/@types/interfaces/IFluidLoaderProps.d.ts.map +1 -0
  17. package/dist/types/@types/interfaces/IGeometricLoaderProps.d.ts +8 -0
  18. package/dist/types/@types/interfaces/IGeometricLoaderProps.d.ts.map +1 -0
  19. package/dist/types/@types/interfaces/ILoaderCSSVariables.d.ts +11 -0
  20. package/dist/types/@types/interfaces/ILoaderCSSVariables.d.ts.map +1 -0
  21. package/dist/types/@types/interfaces/ILoaderState.d.ts +7 -0
  22. package/dist/types/@types/interfaces/ILoaderState.d.ts.map +1 -0
  23. package/dist/types/@types/interfaces/ILoaderTheme.d.ts +20 -0
  24. package/dist/types/@types/interfaces/ILoaderTheme.d.ts.map +1 -0
  25. package/dist/types/@types/interfaces/ILogoLoaderProps.d.ts +12 -0
  26. package/dist/types/@types/interfaces/ILogoLoaderProps.d.ts.map +1 -0
  27. package/dist/types/@types/interfaces/ITextLoaderProps.d.ts +12 -0
  28. package/dist/types/@types/interfaces/ITextLoaderProps.d.ts.map +1 -0
  29. package/dist/types/@types/interfaces/IUseLoaderStateOptions.d.ts +9 -0
  30. package/dist/types/@types/interfaces/IUseLoaderStateOptions.d.ts.map +1 -0
  31. package/dist/types/@types/interfaces/IUseLoaderStateReturn.d.ts +10 -0
  32. package/dist/types/@types/interfaces/IUseLoaderStateReturn.d.ts.map +1 -0
  33. package/dist/types/@types/types/AnimationDirectionType.d.ts +2 -0
  34. package/dist/types/@types/types/AnimationDirectionType.d.ts.map +1 -0
  35. package/dist/types/@types/types/AnimationEasingType.d.ts +2 -0
  36. package/dist/types/@types/types/AnimationEasingType.d.ts.map +1 -0
  37. package/dist/types/@types/types/AnimationFillModeType.d.ts +2 -0
  38. package/dist/types/@types/types/AnimationFillModeType.d.ts.map +1 -0
  39. package/dist/types/@types/types/index.d.ts +4 -0
  40. package/dist/types/@types/types/index.d.ts.map +1 -0
  41. package/dist/types/components/atoms/Circle.d.ts +15 -0
  42. package/dist/types/components/atoms/Circle.d.ts.map +1 -0
  43. package/dist/types/components/atoms/Dot.d.ts +15 -0
  44. package/dist/types/components/atoms/Dot.d.ts.map +1 -0
  45. package/dist/types/components/atoms/Line.d.ts +29 -0
  46. package/dist/types/components/atoms/Line.d.ts.map +1 -0
  47. package/dist/types/components/atoms/Rectangle.d.ts +30 -0
  48. package/dist/types/components/atoms/Rectangle.d.ts.map +1 -0
  49. package/dist/types/components/atoms/index.d.ts +9 -0
  50. package/dist/types/components/atoms/index.d.ts.map +1 -0
  51. package/dist/types/components/index.d.ts +4 -0
  52. package/dist/types/components/index.d.ts.map +1 -0
  53. package/dist/types/components/molecules/DotCluster.d.ts +26 -0
  54. package/dist/types/components/molecules/DotCluster.d.ts.map +1 -0
  55. package/dist/types/components/molecules/LineGroup.d.ts +30 -0
  56. package/dist/types/components/molecules/LineGroup.d.ts.map +1 -0
  57. package/dist/types/components/molecules/ShapeGroup.d.ts +30 -0
  58. package/dist/types/components/molecules/ShapeGroup.d.ts.map +1 -0
  59. package/dist/types/components/molecules/index.d.ts +7 -0
  60. package/dist/types/components/molecules/index.d.ts.map +1 -0
  61. package/dist/types/components/organisms/BarsLoader.d.ts +4 -0
  62. package/dist/types/components/organisms/BarsLoader.d.ts.map +1 -0
  63. package/dist/types/components/organisms/BlobLoader.d.ts +4 -0
  64. package/dist/types/components/organisms/BlobLoader.d.ts.map +1 -0
  65. package/dist/types/components/organisms/BounceLoader.d.ts +4 -0
  66. package/dist/types/components/organisms/BounceLoader.d.ts.map +1 -0
  67. package/dist/types/components/organisms/DotsLoader.d.ts +4 -0
  68. package/dist/types/components/organisms/DotsLoader.d.ts.map +1 -0
  69. package/dist/types/components/organisms/FallbackLoader.d.ts +4 -0
  70. package/dist/types/components/organisms/FallbackLoader.d.ts.map +1 -0
  71. package/dist/types/components/organisms/FlowLoader.d.ts +4 -0
  72. package/dist/types/components/organisms/FlowLoader.d.ts.map +1 -0
  73. package/dist/types/components/organisms/GridLoader.d.ts +4 -0
  74. package/dist/types/components/organisms/GridLoader.d.ts.map +1 -0
  75. package/dist/types/components/organisms/LiquidLoader.d.ts +4 -0
  76. package/dist/types/components/organisms/LiquidLoader.d.ts.map +1 -0
  77. package/dist/types/components/organisms/LogoSpinLoader.d.ts +4 -0
  78. package/dist/types/components/organisms/LogoSpinLoader.d.ts.map +1 -0
  79. package/dist/types/components/organisms/PulseLoader.d.ts +4 -0
  80. package/dist/types/components/organisms/PulseLoader.d.ts.map +1 -0
  81. package/dist/types/components/organisms/RingLoader.d.ts +4 -0
  82. package/dist/types/components/organisms/RingLoader.d.ts.map +1 -0
  83. package/dist/types/components/organisms/RotateLoader.d.ts +4 -0
  84. package/dist/types/components/organisms/RotateLoader.d.ts.map +1 -0
  85. package/dist/types/components/organisms/SpinLoader.d.ts +4 -0
  86. package/dist/types/components/organisms/SpinLoader.d.ts.map +1 -0
  87. package/dist/types/components/organisms/TypingLoader.d.ts +4 -0
  88. package/dist/types/components/organisms/TypingLoader.d.ts.map +1 -0
  89. package/dist/types/components/organisms/WaveLoader.d.ts +4 -0
  90. package/dist/types/components/organisms/WaveLoader.d.ts.map +1 -0
  91. package/dist/types/components/organisms/index.d.ts +17 -0
  92. package/dist/types/components/organisms/index.d.ts.map +1 -0
  93. package/dist/types/hooks/index.d.ts +5 -0
  94. package/dist/types/hooks/index.d.ts.map +1 -0
  95. package/dist/types/hooks/useAsyncLoader.d.ts +15 -0
  96. package/dist/types/hooks/useAsyncLoader.d.ts.map +1 -0
  97. package/dist/types/hooks/useLoaderState.d.ts +10 -0
  98. package/dist/types/hooks/useLoaderState.d.ts.map +1 -0
  99. package/dist/types/hooks/useMultipleLoaderStates.d.ts +11 -0
  100. package/dist/types/hooks/useMultipleLoaderStates.d.ts.map +1 -0
  101. package/dist/types/index.d.ts +6 -0
  102. package/dist/types/index.d.ts.map +1 -0
  103. package/dist/types/setupTests.d.ts +2 -0
  104. package/dist/types/setupTests.d.ts.map +1 -0
  105. package/dist/types/utils/animationUtils.d.ts +29 -0
  106. package/dist/types/utils/animationUtils.d.ts.map +1 -0
  107. package/dist/types/utils/colorUtils.d.ts +18 -0
  108. package/dist/types/utils/colorUtils.d.ts.map +1 -0
  109. package/dist/types/utils/domUtils.d.ts +13 -0
  110. package/dist/types/utils/domUtils.d.ts.map +1 -0
  111. package/dist/types/utils/index.d.ts +6 -0
  112. package/dist/types/utils/index.d.ts.map +1 -0
  113. package/dist/types/utils/mathUtils.d.ts +9 -0
  114. package/dist/types/utils/mathUtils.d.ts.map +1 -0
  115. package/dist/types/utils/propUtils.d.ts +22 -0
  116. package/dist/types/utils/propUtils.d.ts.map +1 -0
  117. package/package.json +112 -0
package/dist/index.js ADDED
@@ -0,0 +1,1409 @@
1
+ 'use strict';
2
+
3
+ var jsxRuntime = require('react/jsx-runtime');
4
+ var react = require('react');
5
+
6
+ /**
7
+ * Merges default props with user props, handling undefined values gracefully
8
+ * @param defaultProps - The default props to merge
9
+ * @param userProps - The user provided props
10
+ * @returns Merged props object
11
+ */
12
+ function mergeProps(defaultProps, userProps) {
13
+ return { ...defaultProps, ...userProps };
14
+ }
15
+ /**
16
+ * Converts size prop to CSS value
17
+ * @param size - The size value (number or string)
18
+ * @param fallback - The fallback value if size is undefined
19
+ * @returns CSS size value as string
20
+ */
21
+ function getSizeValue(size, fallback = "40px") {
22
+ if (size === undefined)
23
+ return fallback;
24
+ if (typeof size === "number")
25
+ return `${size}px`;
26
+ return size;
27
+ }
28
+ /**
29
+ * Generates CSS custom properties object from loader variables
30
+ * @param variables - The loader CSS variables object
31
+ * @returns React CSS properties object
32
+ */
33
+ function generateCSSVariables(variables) {
34
+ const cssProps = {};
35
+ Object.entries(variables).forEach(([key, value]) => {
36
+ if (value !== undefined) {
37
+ cssProps[key] = value;
38
+ }
39
+ });
40
+ return cssProps;
41
+ }
42
+
43
+ /**
44
+ * Calculates animation duration based on speed multiplier
45
+ * @param baseMs - The base duration in milliseconds
46
+ * @param speed - The speed multiplier (default: 1)
47
+ * @returns Formatted duration string
48
+ */
49
+ function getAnimationDuration(baseMs, speed = 1) {
50
+ const duration = baseMs / Math.max(speed, 0.1); // Prevent division by zero
51
+ return `${duration}ms`;
52
+ }
53
+ /**
54
+ * Creates a CSS animation name with prefix
55
+ * @param name - The base name for the animation
56
+ * @returns Prefixed animation name
57
+ */
58
+ function createAnimationName(name) {
59
+ return `react-loadly-${name}`;
60
+ }
61
+ /**
62
+ * Check if reduced motion is preferred
63
+ * @returns Boolean indicating if reduced motion is preferred
64
+ */
65
+ function prefersReducedMotion() {
66
+ // Check if we're in a browser environment
67
+ if (typeof window === "undefined" || !window.matchMedia)
68
+ return false;
69
+ try {
70
+ return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
71
+ }
72
+ catch (e) {
73
+ return false;
74
+ }
75
+ }
76
+ /**
77
+ * Get optimized animation settings based on user preferences
78
+ * @param speed - The animation speed multiplier (default: 1)
79
+ * @returns Object with optimized animation settings
80
+ */
81
+ function getOptimizedAnimationSettings(speed = 1) {
82
+ // In test environments, disable animations to prevent test failures
83
+ if (typeof process !== "undefined" && process.env.NODE_ENV === "test") {
84
+ return {
85
+ duration: "0ms",
86
+ playState: "paused",
87
+ iterationCount: 1,
88
+ };
89
+ }
90
+ const reducedMotion = prefersReducedMotion();
91
+ return {
92
+ duration: reducedMotion ? "0ms" : getAnimationDuration(1000, speed),
93
+ playState: reducedMotion ? "paused" : "running",
94
+ iterationCount: reducedMotion ? 1 : "infinite",
95
+ };
96
+ }
97
+
98
+ /**
99
+ * Converts hex color to RGB values
100
+ * @param hex - The hex color string
101
+ * @returns Object with r, g, b values or null if invalid
102
+ */
103
+ function hexToRgb(hex) {
104
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
105
+ return result
106
+ ? {
107
+ r: parseInt(result[1], 16),
108
+ g: parseInt(result[2], 16),
109
+ b: parseInt(result[3], 16),
110
+ }
111
+ : null;
112
+ }
113
+ /**
114
+ * Generates rgba color with opacity
115
+ * @param color - The hex color string
116
+ * @param opacity - The opacity value (0-1)
117
+ * @returns RGBA color string
118
+ */
119
+ function rgba(color, opacity) {
120
+ const rgb = hexToRgb(color);
121
+ if (!rgb)
122
+ return color;
123
+ return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${clamp(opacity, 0, 1)})`;
124
+ }
125
+
126
+ /**
127
+ * Clamps a value between min and max
128
+ * @param value - The value to clamp
129
+ * @param min - The minimum value
130
+ * @param max - The maximum value
131
+ * @returns The clamped value
132
+ */
133
+ function clamp(value, min, max) {
134
+ return Math.min(Math.max(value, min), max);
135
+ }
136
+
137
+ /**
138
+ * Generates unique IDs for accessibility
139
+ * @param prefix - The prefix for the ID (default: "loader")
140
+ * @returns A unique ID string
141
+ */
142
+ function generateId(prefix = "loader") {
143
+ return `${prefix}-${Math.random().toString(36).substr(2, 9)}`;
144
+ }
145
+ /**
146
+ * Validates and sanitizes CSS values
147
+ * @param value - The CSS value to sanitize
148
+ * @returns Sanitized CSS value or undefined
149
+ */
150
+ function sanitizeCSSValue(value) {
151
+ if (value === undefined || value === null)
152
+ return undefined;
153
+ if (typeof value === "number")
154
+ return `${value}px`;
155
+ if (typeof value === "string") {
156
+ // Basic sanitization - remove potentially dangerous CSS
157
+ return value.replace(/[<>'"]/g, "");
158
+ }
159
+ return undefined;
160
+ }
161
+
162
+ const Dot = ({ size = 8, color = "var(--react-loadly-color)", opacity = 1, className = "", style = {}, animation, animationDuration, animationDelay, glowIntensity = 0, "data-testid": dataTestId, ...props }) => {
163
+ const sizeValue = getSizeValue(size);
164
+ const dotStyle = {
165
+ width: sizeValue,
166
+ height: sizeValue,
167
+ borderRadius: "50%",
168
+ backgroundColor: color,
169
+ opacity,
170
+ animation: animation ? `${animation} ${animationDuration || "1s"} infinite` : undefined,
171
+ animationDelay,
172
+ display: "inline-block",
173
+ boxShadow: glowIntensity > 0 ? `0 0 ${glowIntensity * 10}px ${color}` : undefined,
174
+ ...style,
175
+ };
176
+ return jsxRuntime.jsx("div", { className: `react-loadly-dot ${className}`.trim(), style: dotStyle, "data-testid": dataTestId, ...props });
177
+ };
178
+
179
+ const Line = ({ width = 30, height = 4, color = "var(--react-loadly-color)", opacity = 1, borderRadius = 2, className = "", style = {}, animation, animationDuration, animationDelay, orientation = "horizontal", "data-testid": dataTestId, ...props }) => {
180
+ const widthValue = getSizeValue(width);
181
+ const heightValue = getSizeValue(height);
182
+ const borderRadiusValue = sanitizeCSSValue(borderRadius);
183
+ const lineStyle = {
184
+ width: orientation === "vertical" ? heightValue : widthValue,
185
+ height: orientation === "vertical" ? widthValue : heightValue,
186
+ backgroundColor: color,
187
+ opacity,
188
+ borderRadius: borderRadiusValue,
189
+ animation: animation ? `${animation} ${animationDuration || "1s"} infinite` : undefined,
190
+ animationDelay,
191
+ display: "inline-block",
192
+ ...style,
193
+ };
194
+ return (jsxRuntime.jsx("div", { className: `react-loadly-line react-loadly-line-${orientation} ${className}`.trim(), style: lineStyle, "data-testid": dataTestId, ...props }));
195
+ };
196
+
197
+ const Rectangle = ({ width = 20, height = 20, color = "var(--react-loadly-color)", borderColor, borderWidth = 0, borderRadius = 0, opacity = 1, className = "", style = {}, animation, animationDuration, animationDelay, "data-testid": dataTestId, ...props }) => {
198
+ const widthValue = getSizeValue(width);
199
+ const heightValue = getSizeValue(height);
200
+ const borderWidthValue = sanitizeCSSValue(borderWidth);
201
+ const borderRadiusValue = sanitizeCSSValue(borderRadius);
202
+ const rectangleStyle = {
203
+ width: widthValue,
204
+ height: heightValue,
205
+ backgroundColor: borderColor ? "transparent" : color,
206
+ border: borderColor ? `${borderWidthValue} solid ${borderColor}` : undefined,
207
+ borderRadius: borderRadiusValue,
208
+ opacity,
209
+ animation: animation ? `${animation} ${animationDuration || "1s"} infinite` : undefined,
210
+ animationDelay,
211
+ display: "inline-block",
212
+ ...style,
213
+ };
214
+ return jsxRuntime.jsx("div", { className: `react-loadly-rectangle ${className}`.trim(), style: rectangleStyle, "data-testid": dataTestId, ...props });
215
+ };
216
+
217
+ const Circle = ({ size = 20, color = "var(--react-loadly-color)", borderColor, borderWidth = 0, opacity = 1, className = "", style = {}, animation, animationDuration, animationDelay, "data-testid": dataTestId, ...props }) => {
218
+ const sizeValue = getSizeValue(size);
219
+ const borderWidthValue = sanitizeCSSValue(borderWidth);
220
+ const circleStyle = {
221
+ width: sizeValue,
222
+ height: sizeValue,
223
+ borderRadius: "50%",
224
+ backgroundColor: borderColor ? "transparent" : color,
225
+ border: borderColor ? `${borderWidthValue} solid ${borderColor}` : undefined,
226
+ opacity,
227
+ animation: animation ? `${animation} ${animationDuration || "1s"} infinite` : undefined,
228
+ animationDelay,
229
+ display: "inline-block",
230
+ ...style,
231
+ };
232
+ return jsxRuntime.jsx("div", { className: `react-loadly-circle ${className}`.trim(), style: circleStyle, "data-testid": dataTestId, ...props });
233
+ };
234
+
235
+ const DotCluster = ({ count = 3, dotSize = 8, color = "var(--react-loadly-color)", secondaryColor, spacing = 8, speed = 1, arrangement = "linear", className = "", style = {}, animationType = "wave", "data-testid": dataTestId, ...props }) => {
236
+ const spacingValue = getSizeValue(spacing);
237
+ const animationDuration = getAnimationDuration(1200, speed);
238
+ const getArrangementStyle = () => {
239
+ switch (arrangement) {
240
+ case "circular":
241
+ return {
242
+ display: "flex",
243
+ alignItems: "center",
244
+ justifyContent: "center",
245
+ position: "relative",
246
+ width: `${(parseInt(getSizeValue(dotSize)) + parseInt(spacingValue)) * 2}px`,
247
+ height: `${(parseInt(getSizeValue(dotSize)) + parseInt(spacingValue)) * 2}px`,
248
+ };
249
+ case "grid": {
250
+ const gridSize = Math.ceil(Math.sqrt(count));
251
+ return {
252
+ display: "grid",
253
+ gridTemplateColumns: `repeat(${gridSize}, 1fr)`,
254
+ gap: spacingValue,
255
+ };
256
+ }
257
+ default: // linear
258
+ return {
259
+ display: "flex",
260
+ alignItems: "center",
261
+ gap: spacingValue,
262
+ };
263
+ }
264
+ };
265
+ const getDotPosition = (index) => {
266
+ if (arrangement === "circular") {
267
+ const angle = (index / count) * 2 * Math.PI;
268
+ const radius = parseInt(spacingValue);
269
+ return {
270
+ position: "absolute",
271
+ left: "50%",
272
+ top: "50%",
273
+ transform: `translate(-50%, -50%) translate(${Math.cos(angle) * radius}px, ${Math.sin(angle) * radius}px)`,
274
+ };
275
+ }
276
+ return {};
277
+ };
278
+ const getDotAnimationDelay = (index) => {
279
+ return `${(index * 0.1) / speed}s`;
280
+ };
281
+ const containerStyle = {
282
+ ...getArrangementStyle(),
283
+ ...style,
284
+ };
285
+ return (jsxRuntime.jsx("div", { className: `react-loadly-dot-cluster react-loadly-dot-cluster-${arrangement} ${className}`.trim(), style: containerStyle, "data-testid": dataTestId, ...props, children: Array.from({ length: count }, (_, index) => (jsxRuntime.jsx(Dot, { size: dotSize, color: secondaryColor && index % 2 === 1 ? secondaryColor : color, animation: `react-loadly-${animationType}`, animationDuration: animationDuration, animationDelay: getDotAnimationDelay(index), style: getDotPosition(index), "data-testid": dataTestId ? `${dataTestId}-dot-${index}` : undefined }, index))) }));
286
+ };
287
+
288
+ const LineGroup = ({ count = 5, lineWidth = 4, lineHeight = 35, color = "var(--react-loadly-color)", secondaryColor, spacing = 6, speed = 1, arrangement = "parallel", orientation = "vertical", className = "", style = {}, animationType = "wave", "data-testid": dataTestId, ...props }) => {
289
+ const spacingValue = getSizeValue(spacing);
290
+ const animationDuration = getAnimationDuration(1000, speed);
291
+ const getArrangementStyle = () => {
292
+ switch (arrangement) {
293
+ case "radial":
294
+ return {
295
+ display: "flex",
296
+ alignItems: "center",
297
+ justifyContent: "center",
298
+ position: "relative",
299
+ width: `${parseInt(getSizeValue(lineHeight)) * 1.5}px`,
300
+ height: `${parseInt(getSizeValue(lineHeight)) * 1.5}px`,
301
+ };
302
+ case "staggered":
303
+ return {
304
+ display: "flex",
305
+ alignItems: "flex-end",
306
+ justifyContent: "center",
307
+ gap: spacingValue,
308
+ };
309
+ default: // parallel
310
+ return {
311
+ display: "flex",
312
+ alignItems: "center",
313
+ justifyContent: "center",
314
+ gap: spacingValue,
315
+ };
316
+ }
317
+ };
318
+ const getLinePosition = (index) => {
319
+ if (arrangement === "radial") {
320
+ const angle = (index / count) * 2 * Math.PI;
321
+ return {
322
+ position: "absolute",
323
+ left: "50%",
324
+ top: "50%",
325
+ transformOrigin: "center",
326
+ transform: `translate(-50%, -50%) rotate(${angle}rad)`,
327
+ };
328
+ }
329
+ if (arrangement === "staggered") {
330
+ const heightMultiplier = 0.3 + 0.7 * Math.sin((index / count) * Math.PI);
331
+ return {
332
+ height: orientation === "vertical" ? `${parseInt(getSizeValue(lineHeight)) * heightMultiplier}px` : getSizeValue(lineHeight),
333
+ };
334
+ }
335
+ return {};
336
+ };
337
+ const getLineAnimationDelay = (index) => {
338
+ return `${(index * 0.1) / speed}s`;
339
+ };
340
+ const containerStyle = {
341
+ ...getArrangementStyle(),
342
+ ...style,
343
+ };
344
+ return (jsxRuntime.jsx("div", { className: `react-loadly-line-group react-loadly-line-group-${arrangement} ${className}`.trim(), style: containerStyle, "data-testid": dataTestId, ...props, children: Array.from({ length: count }, (_, index) => (jsxRuntime.jsx(Line, { width: orientation === "horizontal" ? lineWidth : lineHeight, height: orientation === "horizontal" ? lineHeight : lineWidth, color: secondaryColor && index % 2 === 1 ? secondaryColor : color, orientation: orientation, animation: `react-loadly-${animationType}`, animationDuration: animationDuration, animationDelay: getLineAnimationDelay(index), style: getLinePosition(index), "data-testid": dataTestId ? `${dataTestId}-line-${index}` : undefined }, index))) }));
345
+ };
346
+
347
+ const ShapeGroup = ({ count = 4, shapeSize = 16, color = "var(--react-loadly-color)", secondaryColor, spacing = 8, speed = 1, arrangement = "linear", shapeTypes = ["circle", "rectangle"], className = "", style = {}, animationType = "pulse", borderWidth = 0, "data-testid": dataTestId, ...props }) => {
348
+ const spacingValue = getSizeValue(spacing);
349
+ const animationDuration = getAnimationDuration(800, speed);
350
+ const getArrangementStyle = () => {
351
+ switch (arrangement) {
352
+ case "circular":
353
+ return {
354
+ display: "flex",
355
+ alignItems: "center",
356
+ justifyContent: "center",
357
+ position: "relative",
358
+ width: `${(parseInt(getSizeValue(shapeSize)) + parseInt(spacingValue)) * 2.5}px`,
359
+ height: `${(parseInt(getSizeValue(shapeSize)) + parseInt(spacingValue)) * 2.5}px`,
360
+ };
361
+ case "spiral":
362
+ return {
363
+ display: "flex",
364
+ alignItems: "center",
365
+ justifyContent: "center",
366
+ position: "relative",
367
+ width: `${(parseInt(getSizeValue(shapeSize)) + parseInt(spacingValue)) * 3}px`,
368
+ height: `${(parseInt(getSizeValue(shapeSize)) + parseInt(spacingValue)) * 3}px`,
369
+ };
370
+ default: // linear
371
+ return {
372
+ display: "flex",
373
+ alignItems: "center",
374
+ gap: spacingValue,
375
+ };
376
+ }
377
+ };
378
+ const getShapePosition = (index) => {
379
+ if (arrangement === "circular") {
380
+ const angle = (index / count) * 2 * Math.PI;
381
+ const radius = parseInt(spacingValue) * 2;
382
+ return {
383
+ position: "absolute",
384
+ left: "50%",
385
+ top: "50%",
386
+ transform: `translate(-50%, -50%) translate(${Math.cos(angle) * radius}px, ${Math.sin(angle) * radius}px)`,
387
+ };
388
+ }
389
+ if (arrangement === "spiral") {
390
+ const angle = (index / count) * 4 * Math.PI;
391
+ const radius = ((index + 1) * parseInt(spacingValue)) / 2;
392
+ return {
393
+ position: "absolute",
394
+ left: "50%",
395
+ top: "50%",
396
+ transform: `translate(-50%, -50%) translate(${Math.cos(angle) * radius}px, ${Math.sin(angle) * radius}px)`,
397
+ };
398
+ }
399
+ return {};
400
+ };
401
+ const getShapeAnimationDelay = (index) => {
402
+ return `${(index * 0.15) / speed}s`;
403
+ };
404
+ const getShapeType = (index) => {
405
+ return shapeTypes[index % shapeTypes.length];
406
+ };
407
+ const containerStyle = {
408
+ ...getArrangementStyle(),
409
+ ...style,
410
+ };
411
+ const renderShape = (index) => {
412
+ const shapeType = getShapeType(index);
413
+ const shapeColor = secondaryColor && index % 2 === 1 ? secondaryColor : color;
414
+ const commonProps = {
415
+ key: index,
416
+ color: borderWidth > 0 ? undefined : shapeColor,
417
+ borderColor: borderWidth > 0 ? shapeColor : undefined,
418
+ borderWidth: borderWidth > 0 ? borderWidth : undefined,
419
+ animation: `react-loadly-${animationType}`,
420
+ animationDuration,
421
+ animationDelay: getShapeAnimationDelay(index),
422
+ style: getShapePosition(index),
423
+ "data-testid": dataTestId ? `${dataTestId}-shape-${index}` : undefined,
424
+ };
425
+ if (shapeType === "circle") {
426
+ return jsxRuntime.jsx(Circle, { ...commonProps, size: shapeSize });
427
+ }
428
+ else {
429
+ return jsxRuntime.jsx(Rectangle, { ...commonProps, width: shapeSize, height: shapeSize });
430
+ }
431
+ };
432
+ return (jsxRuntime.jsx("div", { className: `react-loadly-shape-group react-loadly-shape-group-${arrangement} ${className}`.trim(), style: containerStyle, "data-testid": dataTestId, ...props, children: Array.from({ length: count }, (_, index) => renderShape(index)) }));
433
+ };
434
+
435
+ const defaultProps$d = {
436
+ size: 60,
437
+ color: "var(--react-loadly-color)",
438
+ speed: 1,
439
+ loading: true,
440
+ fluidity: 1,
441
+ amplitude: 1,
442
+ "aria-label": "Loading...",
443
+ };
444
+ const BlobLoader = (userProps) => {
445
+ const props = mergeProps(defaultProps$d, userProps);
446
+ const { size, color, secondaryColor, speed, loading, fluidity, amplitude, className = "", style = {}, showText, loadingText = "Loading...", "aria-label": ariaLabel, "data-testid": dataTestId, ...restProps } = props;
447
+ const id = react.useMemo(() => generateId("blob-loader"), []);
448
+ const sizeValue = getSizeValue(size);
449
+ const animationDuration = getAnimationDuration(2500, speed);
450
+ if (!loading)
451
+ return null;
452
+ const containerStyle = {
453
+ display: "inline-flex",
454
+ flexDirection: "column",
455
+ alignItems: "center",
456
+ ...style,
457
+ };
458
+ const blobStyle = {
459
+ width: sizeValue,
460
+ height: sizeValue,
461
+ background: `linear-gradient(45deg, ${color}, ${secondaryColor || color})`,
462
+ borderRadius: "30% 70% 70% 30% / 30% 30% 70% 70%",
463
+ animation: `blob-morph ${animationDuration} ease-in-out infinite`,
464
+ filter: "blur(1px)",
465
+ position: "relative",
466
+ };
467
+ const innerBlobStyle = {
468
+ position: "absolute",
469
+ top: "20%",
470
+ left: "20%",
471
+ width: "60%",
472
+ height: "60%",
473
+ background: `radial-gradient(circle, ${secondaryColor || color}, transparent)`,
474
+ borderRadius: "40% 60% 60% 40% / 40% 40% 60% 60%",
475
+ animation: `blob-inner ${animationDuration} ease-in-out infinite reverse`,
476
+ opacity: 0.7,
477
+ };
478
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("style", { children: `
479
+ @keyframes blob-morph {
480
+ 0%, 100% {
481
+ border-radius: 30% 70% 70% 30% / 30% 30% 70% 70%;
482
+ transform: scale(1) rotate(0deg);
483
+ }
484
+ 25% {
485
+ border-radius: 58% 42% 75% 25% / 76% 46% 54% 24%;
486
+ transform: scale(${1 + (amplitude ?? 1) * 0.1}) rotate(90deg);
487
+ }
488
+ 50% {
489
+ border-radius: 50% 50% 33% 67% / 55% 27% 73% 45%;
490
+ transform: scale(${1 - (amplitude ?? 1) * 0.05}) rotate(180deg);
491
+ }
492
+ 75% {
493
+ border-radius: 33% 67% 58% 42% / 63% 68% 32% 37%;
494
+ transform: scale(${1 + (amplitude ?? 1) * 0.08}) rotate(270deg);
495
+ }
496
+ }
497
+
498
+ @keyframes blob-inner {
499
+ 0%, 100% {
500
+ border-radius: 40% 60% 60% 40% / 40% 40% 60% 60%;
501
+ transform: scale(1) rotate(0deg);
502
+ }
503
+ 33% {
504
+ border-radius: 70% 30% 50% 50% / 30% 70% 30% 70%;
505
+ transform: scale(${1.1 + (fluidity ?? 1) * 0.1}) rotate(-120deg);
506
+ }
507
+ 66% {
508
+ border-radius: 30% 70% 40% 60% / 70% 30% 70% 30%;
509
+ transform: scale(${0.9 + (fluidity ?? 1) * 0.05}) rotate(-240deg);
510
+ }
511
+ }
512
+ ` }), jsxRuntime.jsxs("div", { className: `react-loadly react-loadly-blob ${className}`.trim(), style: containerStyle, role: "status", "aria-label": ariaLabel, "aria-live": "polite", "aria-busy": loading, "data-testid": dataTestId, ...restProps, children: [jsxRuntime.jsx("div", { style: blobStyle, "data-testid": dataTestId ? `${dataTestId}-blob` : undefined, children: jsxRuntime.jsx("div", { style: innerBlobStyle }) }), showText && (jsxRuntime.jsx("div", { className: "react-loadly-text", id: `${id}-text`, "aria-live": "polite", children: loadingText })), jsxRuntime.jsx("span", { className: "react-loadly-sr-only", children: ariaLabel })] })] }));
513
+ };
514
+
515
+ const ErrorIcon = ({ className = "" }) => (jsxRuntime.jsx("svg", { className: className, fill: "currentColor", viewBox: "0 0 20 20", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", children: jsxRuntime.jsx("path", { clipRule: "evenodd", fillRule: "evenodd", d: "M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-3a1 1 0 00-.867.5 1 1 0 11-1.731-1A3 3 0 0113 8a3.001 3.001 0 01-2 2.83V11a1 1 0 11-2 0v-1a1 1 0 011-1 1 1 0 100-2zm0 8a1 1 0 100-2 1 1 0 000 2z" }) }));
516
+ const NetworkIcon = ({ className = "" }) => (jsxRuntime.jsx("svg", { className: className, fill: "currentColor", viewBox: "0 0 20 20", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", children: jsxRuntime.jsx("path", { fillRule: "evenodd", d: "M9.243 3.03a1 1 0 01.727 1.213L9.53 6h2.94l.56-2.243a1 1 0 111.94.486L14.53 6H16a1 1 0 110 2h-1.97l-1 4H15a1 1 0 110 2h-2.47l-.56 2.242a1 1 0 11-1.94-.485L10.47 14H7.53l-.56 2.242a1 1 0 11-1.94-.485L5.47 14H4a1 1 0 110-2h1.97l1-4H5a1 1 0 110-2h2.47l.56-2.243a1 1 0 011.213-.727zM9.03 8l-1 4h2.94l1-4H9.03z", clipRule: "evenodd" }) }));
517
+ const TimeoutIcon = ({ className = "" }) => (jsxRuntime.jsx("svg", { className: className, fill: "currentColor", viewBox: "0 0 20 20", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", children: jsxRuntime.jsx("path", { fillRule: "evenodd", d: "M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z", clipRule: "evenodd" }) }));
518
+ const FallbackLoader = ({ error = "Something went wrong", onRetry, showRetry = true, children, type = "error" }) => {
519
+ const getIcon = () => {
520
+ switch (type) {
521
+ case "network":
522
+ return jsxRuntime.jsx(NetworkIcon, { className: "react-loadly-error-icon" });
523
+ case "timeout":
524
+ return jsxRuntime.jsx(TimeoutIcon, { className: "react-loadly-error-icon" });
525
+ default:
526
+ return jsxRuntime.jsx(ErrorIcon, { className: "react-loadly-error-icon" });
527
+ }
528
+ };
529
+ const getMessage = () => {
530
+ switch (type) {
531
+ case "network":
532
+ return error || "Network connection failed. Please check your internet connection.";
533
+ case "timeout":
534
+ return error || "Loading timeout. The operation took too long to complete.";
535
+ default:
536
+ return error || "Something went wrong. Please try again.";
537
+ }
538
+ };
539
+ if (children) {
540
+ return jsxRuntime.jsx("div", { className: "react-loadly-fallback", children: children });
541
+ }
542
+ return (jsxRuntime.jsxs("div", { className: "react-loadly-error", role: "alert", "aria-live": "polite", children: [getIcon(), jsxRuntime.jsx("p", { className: "react-loadly-error-message", children: getMessage() }), showRetry && onRetry && (jsxRuntime.jsx("button", { className: "react-loadly-retry-button", onClick: onRetry, type: "button", "aria-label": "Retry loading", children: "Try Again" }))] }));
543
+ };
544
+
545
+ const defaultProps$c = {
546
+ size: 60,
547
+ color: "var(--react-loadly-color)",
548
+ speed: 1,
549
+ loading: true,
550
+ fluidity: 1,
551
+ amplitude: 1,
552
+ "aria-label": "Loading...",
553
+ };
554
+ const FlowLoader = (userProps) => {
555
+ const props = mergeProps(defaultProps$c, userProps);
556
+ const { size, color, secondaryColor, speed, loading, amplitude, className = "", style = {}, showText, loadingText = "Loading...", "aria-label": ariaLabel, "data-testid": dataTestId, ...restProps } = props;
557
+ const id = react.useMemo(() => generateId("flow-loader"), []);
558
+ const sizeValue = getSizeValue(size);
559
+ const numericSize = parseInt(sizeValue);
560
+ const containerHeight = Math.max(numericSize * 0.4, 10); // Minimum height of 10px
561
+ const animationDuration = getAnimationDuration(1500, speed);
562
+ if (!loading)
563
+ return null;
564
+ const containerStyle = {
565
+ display: "inline-flex",
566
+ flexDirection: "column",
567
+ alignItems: "center",
568
+ ...style,
569
+ };
570
+ const flowContainerStyle = {
571
+ width: sizeValue,
572
+ height: `${containerHeight}px`,
573
+ position: "relative",
574
+ overflow: "hidden",
575
+ borderRadius: `${Math.max(numericSize * 0.2, 4)}px`, // Minimum radius of 4px
576
+ backgroundColor: "rgba(0, 0, 0, 0.1)",
577
+ };
578
+ // Ensure minimum particle count even for small sizes
579
+ const particleCount = Math.max(Math.floor(numericSize / 15), 3);
580
+ const createParticle = (index) => {
581
+ const delay = (index * 0.2) / (speed ?? 1);
582
+ const duration = parseFloat(animationDuration) + index * 100;
583
+ // Scale particle size based on container size
584
+ const minParticleSize = Math.max(numericSize / 10, 4); // Minimum 4px
585
+ const particleSize = minParticleSize + (index % 3) * (minParticleSize / 2);
586
+ return (jsxRuntime.jsx("div", { style: {
587
+ position: "absolute",
588
+ width: `${particleSize}px`,
589
+ height: `${particleSize}px`,
590
+ borderRadius: "50%",
591
+ background: index % 2 === 0 ? color : secondaryColor || color,
592
+ animation: `flow-particle-${id} ${duration}ms ease-in-out infinite`,
593
+ animationDelay: `${delay}s`,
594
+ opacity: 0.8 - index * 0.1,
595
+ left: "0px", // Start at the beginning of the container
596
+ top: `${(containerHeight - particleSize) / 2 + (index % 3) * (containerHeight / (particleCount + 1))}px`, // Distribute vertically
597
+ }, "data-testid": dataTestId ? `${dataTestId}-particle-${index}` : undefined }, index));
598
+ };
599
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("style", { children: `
600
+ @keyframes flow-particle-${id} {
601
+ 0% {
602
+ transform: translateX(0) translateY(0) scale(0);
603
+ opacity: 0;
604
+ }
605
+ 10% {
606
+ transform: translateX(${Math.min(numericSize * 0.1, 10)}px) translateY(${Math.sin(0.1) * (amplitude ?? Math.min(5, containerHeight / 4))}px) scale(1);
607
+ opacity: 0.8;
608
+ }
609
+ 50% {
610
+ transform: translateX(${Math.min(numericSize * 0.5, numericSize * 0.7)}px) translateY(${Math.sin(0.5) * (amplitude ?? Math.min(5, containerHeight / 4))}px) scale(1);
611
+ opacity: 0.8;
612
+ }
613
+ 90% {
614
+ transform: translateX(${Math.min(numericSize * 0.9, numericSize - 10)}px) translateY(${Math.sin(0.9) * (amplitude ?? Math.min(5, containerHeight / 4))}px) scale(1);
615
+ opacity: 0.8;
616
+ }
617
+ 100% {
618
+ transform: translateX(${numericSize}px) translateY(${Math.sin(1) * (amplitude ?? Math.min(5, containerHeight / 4))}px) scale(0);
619
+ opacity: 0;
620
+ }
621
+ }
622
+ ` }), jsxRuntime.jsxs("div", { className: `react-loadly react-loadly-flow ${className}`.trim(), style: containerStyle, role: "status", "aria-label": ariaLabel, "aria-live": "polite", "aria-busy": loading, "data-testid": dataTestId, ...restProps, children: [jsxRuntime.jsx("div", { style: flowContainerStyle, "data-testid": dataTestId ? `${dataTestId}-container` : undefined, children: Array.from({ length: particleCount }, (_, index) => createParticle(index)) }), showText && (jsxRuntime.jsx("div", { className: "react-loadly-text", id: `${id}-text`, "aria-live": "polite", children: loadingText })), jsxRuntime.jsx("span", { className: "react-loadly-sr-only", children: ariaLabel })] })] }));
623
+ };
624
+
625
+ const defaultProps$b = {
626
+ size: 40,
627
+ color: "var(--react-loadly-color)",
628
+ speed: 1,
629
+ loading: true,
630
+ count: 4,
631
+ "aria-label": "Loading...",
632
+ };
633
+ const GridLoader = (userProps) => {
634
+ const props = mergeProps(defaultProps$b, userProps);
635
+ const { size, color, secondaryColor, speed, loading, count, className = "", style = {}, showText, loadingText = "Loading...", "aria-label": ariaLabel, "data-testid": dataTestId, ...restProps } = props;
636
+ const id = react.useMemo(() => generateId("grid-loader"), []);
637
+ const shapeSize = react.useMemo(() => {
638
+ const sizeNum = typeof size === "number" ? size : parseInt(getSizeValue(size));
639
+ const gridSize = Math.ceil(Math.sqrt(count || 4));
640
+ return Math.max(sizeNum / (gridSize * 1.5), 8);
641
+ }, [size, count]);
642
+ if (!loading)
643
+ return null;
644
+ const containerStyle = {
645
+ display: "inline-flex",
646
+ flexDirection: "column",
647
+ alignItems: "center",
648
+ ...style,
649
+ };
650
+ return (jsxRuntime.jsxs("div", { className: `react-loadly react-loadly-grid ${className}`.trim(), style: containerStyle, role: "status", "aria-label": ariaLabel, "aria-live": "polite", "aria-busy": loading, "data-testid": dataTestId, ...restProps, children: [jsxRuntime.jsx("div", { style: {
651
+ display: "grid",
652
+ gridTemplateColumns: `repeat(${Math.ceil(Math.sqrt(count || 4))}, 1fr)`,
653
+ gap: `${shapeSize / 4}px`,
654
+ }, children: Array.from({ length: count || 4 }, (_, index) => (jsxRuntime.jsx("div", { style: {
655
+ width: shapeSize,
656
+ height: shapeSize,
657
+ backgroundColor: index % 2 === 0 ? color : secondaryColor || color,
658
+ borderRadius: "2px",
659
+ animation: `react-loadly-scale ${1.2 / (speed || 1)}s ease-in-out infinite`,
660
+ animationDelay: `${(index * 0.1) / (speed || 1)}s`,
661
+ }, "data-testid": dataTestId ? `${dataTestId}-shape-${index}` : undefined }, index))) }), showText && (jsxRuntime.jsx("div", { className: "react-loadly-text", id: `${id}-text`, "aria-live": "polite", children: loadingText })), jsxRuntime.jsx("span", { className: "react-loadly-sr-only", children: ariaLabel })] }));
662
+ };
663
+
664
+ const defaultProps$a = {
665
+ size: 60,
666
+ color: "var(--react-loadly-color)",
667
+ speed: 1,
668
+ loading: true,
669
+ fluidity: 1,
670
+ amplitude: 1,
671
+ "aria-label": "Loading...",
672
+ };
673
+ const LiquidLoader = (userProps) => {
674
+ const props = mergeProps(defaultProps$a, userProps);
675
+ const { size, color, secondaryColor, speed, loading, amplitude, className = "", style = {}, showText, loadingText = "Loading...", "aria-label": ariaLabel, "data-testid": dataTestId, ...restProps } = props;
676
+ const id = react.useMemo(() => generateId("liquid-loader"), []);
677
+ const sizeValue = getSizeValue(size);
678
+ const animationDuration = getAnimationDuration(2000, speed);
679
+ if (!loading)
680
+ return null;
681
+ const containerStyle = {
682
+ display: "inline-flex",
683
+ flexDirection: "column",
684
+ alignItems: "center",
685
+ ...style,
686
+ };
687
+ const liquidStyle = {
688
+ width: sizeValue,
689
+ height: sizeValue,
690
+ position: "relative",
691
+ overflow: "hidden",
692
+ borderRadius: "50%",
693
+ backgroundColor: "rgba(0, 0, 0, 0.1)",
694
+ };
695
+ // Calculate the translateY value based on amplitude
696
+ const translateY = 50 - (amplitude ?? 1) * 10;
697
+ const waveStyle = {
698
+ position: "absolute",
699
+ bottom: 0,
700
+ left: 0,
701
+ width: "200%",
702
+ height: "200%",
703
+ background: `linear-gradient(180deg, ${color} 0%, ${secondaryColor || color} 100%)`,
704
+ borderRadius: "40%",
705
+ animation: `react-loadly-liquid-wave ${animationDuration} ease-in-out infinite`,
706
+ transform: "translate(-25%, 50%) rotate(0deg)",
707
+ animationTimingFunction: `cubic-bezier(0.36, 0.45, 0.63, 0.53)`,
708
+ };
709
+ const wave2Style = {
710
+ ...waveStyle,
711
+ background: `linear-gradient(180deg, ${secondaryColor || color} 0%, ${color} 100%)`,
712
+ animation: `react-loadly-liquid-wave ${animationDuration} ease-in-out infinite reverse`,
713
+ animationDelay: `${ -0.5 / (speed ?? 1)}s`,
714
+ opacity: 0.8,
715
+ };
716
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("style", { children: `
717
+ @keyframes react-loadly-liquid-wave {
718
+ 0%, 100% {
719
+ transform: translate(-25%, 50%) rotate(0deg);
720
+ }
721
+ 50% {
722
+ transform: translate(-25%, ${translateY}%) rotate(180deg);
723
+ }
724
+ }
725
+ ` }), jsxRuntime.jsxs("div", { className: `react-loadly react-loadly-liquid ${className}`.trim(), style: containerStyle, role: "status", "aria-label": ariaLabel, "aria-live": "polite", "aria-busy": loading, "data-testid": dataTestId, ...restProps, children: [jsxRuntime.jsxs("div", { style: liquidStyle, "data-testid": dataTestId ? `${dataTestId}-container` : undefined, children: [jsxRuntime.jsx("div", { style: waveStyle }), jsxRuntime.jsx("div", { style: wave2Style })] }), showText && (jsxRuntime.jsx("div", { className: "react-loadly-text", id: `${id}-text`, "aria-live": "polite", children: loadingText })), jsxRuntime.jsx("span", { className: "react-loadly-sr-only", children: ariaLabel })] })] }));
726
+ };
727
+
728
+ const defaultProps$9 = {
729
+ size: 60,
730
+ speed: 1,
731
+ loading: true,
732
+ animationType: "spin",
733
+ glowIntensity: 0.3,
734
+ "aria-label": "Loading...",
735
+ };
736
+ const LogoSpinLoader = (userProps) => {
737
+ const props = mergeProps(defaultProps$9, userProps);
738
+ const { src, alt = "Loading", size, speed, loading, animationType, glowIntensity, className = "", style = {}, color = "var(--react-loadly-color)", "aria-label": ariaLabel, "data-testid": dataTestId, ...restProps } = props;
739
+ if (!loading)
740
+ return null;
741
+ const containerStyle = {
742
+ display: "inline-flex",
743
+ flexDirection: "column",
744
+ alignItems: "center",
745
+ ...style,
746
+ };
747
+ const logoStyle = {
748
+ width: typeof size === "number" ? `${size}px` : size,
749
+ height: typeof size === "number" ? `${size}px` : size,
750
+ animation: `react-loadly-${animationType} ${getAnimationDuration(2000, speed)} infinite`,
751
+ filter: (glowIntensity ?? 0) > 0 ? `drop-shadow(0 0 ${(glowIntensity ?? 0) * 20}px ${color})` : undefined,
752
+ };
753
+ // If no src provided, show a default loading circle
754
+ if (!src) {
755
+ return (jsxRuntime.jsxs("div", { className: `react-loadly react-loadly-logo ${className}`.trim(), style: containerStyle, role: "status", "aria-label": ariaLabel, "aria-live": "polite", "aria-busy": loading, "data-testid": dataTestId, ...restProps, children: [jsxRuntime.jsx("div", { style: {
756
+ ...logoStyle,
757
+ borderRadius: "50%",
758
+ backgroundColor: color,
759
+ opacity: 0.8,
760
+ }, "data-testid": dataTestId ? `${dataTestId}-default` : undefined }), jsxRuntime.jsx("span", { className: "react-loadly-sr-only", children: ariaLabel })] }));
761
+ }
762
+ return (jsxRuntime.jsxs("div", { className: `react-loadly react-loadly-logo ${className}`.trim(), style: containerStyle, role: "status", "aria-label": ariaLabel, "aria-live": "polite", "aria-busy": loading, "data-testid": dataTestId, ...restProps, children: [jsxRuntime.jsx("img", { src: src, alt: alt, style: logoStyle, "data-testid": dataTestId ? `${dataTestId}-image` : undefined }), jsxRuntime.jsx("span", { className: "react-loadly-sr-only", children: ariaLabel })] }));
763
+ };
764
+
765
+ const defaultProps$8 = {
766
+ size: 40,
767
+ color: "var(--react-loadly-color)",
768
+ speed: 1,
769
+ loading: true,
770
+ count: 3,
771
+ "aria-label": "Loading...",
772
+ };
773
+ const PulseLoader = (userProps) => {
774
+ const props = mergeProps(defaultProps$8, userProps);
775
+ const { size, color, secondaryColor, speed, loading, count, className = "", style = {}, showText, loadingText = "Loading...", "aria-label": ariaLabel, "data-testid": dataTestId, ...restProps } = props;
776
+ const id = react.useMemo(() => generateId("pulse-loader"), []);
777
+ const dotSize = react.useMemo(() => {
778
+ const sizeNum = typeof size === "number" ? size : parseInt(getSizeValue(size));
779
+ return Math.max(sizeNum / 5, 6); // Ensure minimum dot size
780
+ }, [size]);
781
+ if (!loading)
782
+ return null;
783
+ const containerStyle = {
784
+ display: "inline-flex",
785
+ flexDirection: "column",
786
+ alignItems: "center",
787
+ ...style,
788
+ };
789
+ return (jsxRuntime.jsxs("div", { className: `react-loadly react-loadly-pulse ${className}`.trim(), style: containerStyle, role: "status", "aria-label": ariaLabel, "aria-live": "polite", "aria-busy": loading, "data-testid": dataTestId, ...restProps, children: [jsxRuntime.jsx(DotCluster, { count: count, dotSize: dotSize, color: color, secondaryColor: secondaryColor, speed: speed, arrangement: "linear", animationType: "pulse", spacing: dotSize / 2, "data-testid": dataTestId ? `${dataTestId}-dots` : undefined }), showText && (jsxRuntime.jsx("div", { className: "react-loadly-text", id: `${id}-text`, "aria-live": "polite", children: loadingText })), jsxRuntime.jsx("span", { className: "react-loadly-sr-only", children: ariaLabel })] }));
790
+ };
791
+
792
+ const defaultProps$7 = {
793
+ size: 40,
794
+ color: "var(--react-loadly-color)",
795
+ speed: 1,
796
+ loading: true,
797
+ borderWidth: 4,
798
+ "aria-label": "Loading...",
799
+ };
800
+ const SpinLoader = (userProps) => {
801
+ const props = mergeProps(defaultProps$7, userProps);
802
+ const { size, color, speed, loading, className = "", style = {}, borderWidth, showText, loadingText = "Loading...", "aria-label": ariaLabel, "data-testid": dataTestId, ...restProps } = props;
803
+ // Use useRef instead of useMemo for better compatibility
804
+ const idRef = react.useRef(generateId("spin-loader"));
805
+ const sizeValue = getSizeValue(size);
806
+ const animationSettings = getOptimizedAnimationSettings(speed);
807
+ if (!loading)
808
+ return null;
809
+ const containerStyle = {
810
+ display: "inline-flex",
811
+ flexDirection: "column",
812
+ alignItems: "center",
813
+ ...style,
814
+ };
815
+ const spinnerStyle = {
816
+ width: sizeValue,
817
+ height: sizeValue,
818
+ border: `${borderWidth}px solid transparent`,
819
+ borderTop: `${borderWidth}px solid ${color}`,
820
+ borderRadius: "50%",
821
+ animation: `react-loadly-spin ${animationSettings.duration} linear infinite`,
822
+ animationPlayState: animationSettings.playState,
823
+ };
824
+ return (jsxRuntime.jsxs("div", { className: `react-loadly react-loadly-spin ${className}`.trim(), style: containerStyle, role: "status", "aria-label": ariaLabel, "aria-live": "polite", "aria-busy": loading, "data-testid": dataTestId, ...restProps, children: [jsxRuntime.jsx("div", { style: spinnerStyle, "data-testid": dataTestId ? `${dataTestId}-spinner` : undefined }), showText && (jsxRuntime.jsx("div", { className: "react-loadly-text", id: `${idRef.current}-text`, "aria-live": "polite", children: loadingText })), jsxRuntime.jsx("span", { className: "react-loadly-sr-only", children: ariaLabel })] }));
825
+ };
826
+
827
+ const defaultProps$6 = {
828
+ text: "Loading...",
829
+ speed: 1,
830
+ loading: true,
831
+ charDelay: 100,
832
+ "aria-label": "Loading...",
833
+ };
834
+ const TypingLoader = (userProps) => {
835
+ const props = mergeProps(defaultProps$6, userProps);
836
+ const { text, speed, loading, charDelay, className = "", style = {}, color = "var(--react-loadly-text-color)", fontFamily, fontWeight = 500, "aria-label": ariaLabel, "data-testid": dataTestId, ...restProps } = props;
837
+ const [displayText, setDisplayText] = react.useState("");
838
+ const [isTyping, setIsTyping] = react.useState(false);
839
+ const timeoutRef = react.useRef(null);
840
+ react.useEffect(() => {
841
+ // Clear any existing timeout
842
+ if (timeoutRef.current) {
843
+ clearTimeout(timeoutRef.current);
844
+ timeoutRef.current = null;
845
+ }
846
+ if (!loading || !text) {
847
+ setDisplayText("");
848
+ return;
849
+ }
850
+ setIsTyping(true);
851
+ setDisplayText("");
852
+ let currentIndex = 0;
853
+ const typeChar = () => {
854
+ if (currentIndex < text.length) {
855
+ setDisplayText(text.slice(0, currentIndex + 1));
856
+ currentIndex++;
857
+ timeoutRef.current = setTimeout(typeChar, (charDelay ?? 100) / (speed ?? 1));
858
+ }
859
+ else {
860
+ setIsTyping(false);
861
+ // Reset and start over
862
+ timeoutRef.current = setTimeout(() => {
863
+ currentIndex = 0;
864
+ setDisplayText("");
865
+ if (loading)
866
+ typeChar();
867
+ }, 1000 / (speed ?? 1));
868
+ }
869
+ };
870
+ typeChar();
871
+ return () => {
872
+ setIsTyping(false);
873
+ if (timeoutRef.current) {
874
+ clearTimeout(timeoutRef.current);
875
+ timeoutRef.current = null;
876
+ }
877
+ };
878
+ }, [text, loading, charDelay, speed]);
879
+ if (!loading)
880
+ return null;
881
+ const containerStyle = {
882
+ display: "inline-flex",
883
+ alignItems: "center",
884
+ fontFamily: fontFamily || "var(--react-loadly-font-family)",
885
+ fontSize: "var(--react-loadly-font-size)",
886
+ fontWeight,
887
+ color,
888
+ ...style,
889
+ };
890
+ const cursorStyle = {
891
+ marginLeft: "2px",
892
+ animation: isTyping ? "none" : "react-loadly-fade 1s infinite",
893
+ opacity: isTyping ? 1 : 0.5,
894
+ };
895
+ return (jsxRuntime.jsxs("div", { className: `react-loadly react-loadly-typing ${className}`.trim(), style: containerStyle, role: "status", "aria-label": ariaLabel, "aria-live": "polite", "aria-busy": loading, "data-testid": dataTestId, ...restProps, children: [jsxRuntime.jsx("span", { children: displayText }), jsxRuntime.jsx("span", { style: cursorStyle, children: "|" }), jsxRuntime.jsx("span", { className: "react-loadly-sr-only", children: ariaLabel })] }));
896
+ };
897
+
898
+ const defaultProps$5 = {
899
+ size: 40,
900
+ color: "var(--react-loadly-color)",
901
+ speed: 1,
902
+ loading: true,
903
+ count: 5,
904
+ "aria-label": "Loading...",
905
+ };
906
+ const WaveLoader = (userProps) => {
907
+ const props = mergeProps(defaultProps$5, userProps);
908
+ const { size, color, secondaryColor, speed, loading, count, className = "", style = {}, showText, loadingText = "Loading...", "aria-label": ariaLabel, "data-testid": dataTestId, ...restProps } = props;
909
+ const id = react.useMemo(() => generateId("wave-loader"), []);
910
+ const lineSpecs = react.useMemo(() => {
911
+ const sizeNum = typeof size === "number" ? size : parseInt(getSizeValue(size));
912
+ return {
913
+ width: Math.max(sizeNum / 10, 3), // Line thickness
914
+ height: sizeNum, // Line height
915
+ spacing: Math.max(sizeNum / 8, 4), // Spacing between lines
916
+ };
917
+ }, [size]);
918
+ if (!loading)
919
+ return null;
920
+ const containerStyle = {
921
+ display: "inline-flex",
922
+ flexDirection: "column",
923
+ alignItems: "center",
924
+ ...style,
925
+ };
926
+ return (jsxRuntime.jsxs("div", { className: `react-loadly react-loadly-wave ${className}`.trim(), style: containerStyle, role: "status", "aria-label": ariaLabel, "aria-live": "polite", "aria-busy": loading, "data-testid": dataTestId, ...restProps, children: [jsxRuntime.jsx(LineGroup, { count: count, lineWidth: lineSpecs.width, lineHeight: lineSpecs.height, color: color, secondaryColor: secondaryColor, speed: speed, arrangement: "staggered", orientation: "vertical", animationType: "wave", spacing: lineSpecs.spacing, "data-testid": dataTestId ? `${dataTestId}-lines` : undefined }), showText && (jsxRuntime.jsx("div", { className: "react-loadly-text", id: `${id}-text`, "aria-live": "polite", children: loadingText })), jsxRuntime.jsx("span", { className: "react-loadly-sr-only", children: ariaLabel })] }));
927
+ };
928
+
929
+ const defaultProps$4 = {
930
+ size: 20,
931
+ color: "var(--react-loadly-color)",
932
+ speed: 1,
933
+ loading: true,
934
+ count: 5,
935
+ "aria-label": "Loading...",
936
+ };
937
+ const BarsLoader = (userProps) => {
938
+ const props = mergeProps(defaultProps$4, userProps);
939
+ const { size, color, speed, loading, className = "", style = {}, count = 5, showText, loadingText = "Loading...", "aria-label": ariaLabel, "data-testid": dataTestId, ...restProps } = props;
940
+ const id = react.useMemo(() => generateId("bars-loader"), []);
941
+ const sizeValue = getSizeValue(size);
942
+ const animationSettings = getOptimizedAnimationSettings(speed);
943
+ if (!loading)
944
+ return null;
945
+ const containerStyle = {
946
+ display: "inline-flex",
947
+ flexDirection: "column",
948
+ alignItems: "center",
949
+ ...style,
950
+ };
951
+ const barsContainerStyle = {
952
+ display: "flex",
953
+ justifyContent: "center",
954
+ alignItems: "center",
955
+ gap: "4px",
956
+ };
957
+ const barStyle = {
958
+ width: "4px",
959
+ height: sizeValue,
960
+ backgroundColor: color,
961
+ borderRadius: "2px",
962
+ animation: `react-loadly-bars ${animationSettings.duration} ease-in-out infinite`,
963
+ animationPlayState: animationSettings.playState,
964
+ };
965
+ // Create bars with different animation delays
966
+ const bars = Array.from({ length: count }).map((_, index) => {
967
+ const delay = `${index * 0.1}s`;
968
+ const heightFactor = 0.5 + (index % 3) * 0.25; // Vary heights for visual interest
969
+ return (jsxRuntime.jsx("div", { style: {
970
+ ...barStyle,
971
+ animationDelay: delay,
972
+ height: `${parseFloat(sizeValue) * heightFactor}px`,
973
+ }, "data-testid": dataTestId ? `${dataTestId}-bar-${index}` : undefined }, index));
974
+ });
975
+ return (jsxRuntime.jsxs("div", { className: `react-loadly react-loadly-bars ${className}`.trim(), style: containerStyle, role: "status", "aria-label": ariaLabel, "aria-live": "polite", "aria-busy": loading, "data-testid": dataTestId, ...restProps, children: [jsxRuntime.jsx("div", { style: barsContainerStyle, children: bars }), showText && (jsxRuntime.jsx("div", { className: "react-loadly-text", id: `${id}-text`, "aria-live": "polite", children: loadingText })), jsxRuntime.jsx("span", { className: "react-loadly-sr-only", children: ariaLabel })] }));
976
+ };
977
+
978
+ const defaultProps$3 = {
979
+ size: 15,
980
+ color: "var(--react-loadly-color)",
981
+ speed: 1,
982
+ loading: true,
983
+ count: 3,
984
+ "aria-label": "Loading...",
985
+ };
986
+ const BounceLoader = (userProps) => {
987
+ const props = mergeProps(defaultProps$3, userProps);
988
+ const { size, color, speed, loading, className = "", style = {}, count = 3, showText, loadingText = "Loading...", "aria-label": ariaLabel, "data-testid": dataTestId, ...restProps } = props;
989
+ const id = react.useMemo(() => generateId("bounce-loader"), []);
990
+ const sizeValue = getSizeValue(size);
991
+ const animationSettings = getOptimizedAnimationSettings(speed);
992
+ if (!loading)
993
+ return null;
994
+ const containerStyle = {
995
+ display: "inline-flex",
996
+ flexDirection: "column",
997
+ alignItems: "center",
998
+ ...style,
999
+ };
1000
+ const bounceContainerStyle = {
1001
+ display: "flex",
1002
+ justifyContent: "center",
1003
+ alignItems: "center",
1004
+ gap: "8px",
1005
+ };
1006
+ const bounceBallStyle = {
1007
+ width: sizeValue,
1008
+ height: sizeValue,
1009
+ borderRadius: "50%",
1010
+ backgroundColor: color,
1011
+ animation: `react-loadly-bounce ${animationSettings.duration} ease-in-out infinite`,
1012
+ animationPlayState: animationSettings.playState,
1013
+ };
1014
+ // Create bounce animation delays for each ball
1015
+ const balls = Array.from({ length: count }).map((_, index) => {
1016
+ const delay = `${index * 0.1}s`;
1017
+ return (jsxRuntime.jsx("div", { style: {
1018
+ ...bounceBallStyle,
1019
+ animationDelay: delay,
1020
+ }, "data-testid": dataTestId ? `${dataTestId}-ball-${index}` : undefined }, index));
1021
+ });
1022
+ return (jsxRuntime.jsxs("div", { className: `react-loadly react-loadly-bounce ${className}`.trim(), style: containerStyle, role: "status", "aria-label": ariaLabel, "aria-live": "polite", "aria-busy": loading, "data-testid": dataTestId, ...restProps, children: [jsxRuntime.jsx("div", { style: bounceContainerStyle, children: balls }), showText && (jsxRuntime.jsx("div", { className: "react-loadly-text", id: `${id}-text`, "aria-live": "polite", children: loadingText })), jsxRuntime.jsx("span", { className: "react-loadly-sr-only", children: ariaLabel })] }));
1023
+ };
1024
+
1025
+ const defaultProps$2 = {
1026
+ size: 12,
1027
+ color: "var(--react-loadly-color)",
1028
+ speed: 1,
1029
+ loading: true,
1030
+ count: 3,
1031
+ "aria-label": "Loading...",
1032
+ };
1033
+ const DotsLoader = (userProps) => {
1034
+ const props = mergeProps(defaultProps$2, userProps);
1035
+ const { size, color, speed, loading, className = "", style = {}, count = 3, showText, loadingText = "Loading...", "aria-label": ariaLabel, "data-testid": dataTestId, ...restProps } = props;
1036
+ const id = react.useMemo(() => generateId("dots-loader"), []);
1037
+ const sizeValue = getSizeValue(size);
1038
+ const animationSettings = getOptimizedAnimationSettings(speed);
1039
+ if (!loading)
1040
+ return null;
1041
+ const containerStyle = {
1042
+ display: "inline-flex",
1043
+ flexDirection: "column",
1044
+ alignItems: "center",
1045
+ ...style,
1046
+ };
1047
+ const dotsContainerStyle = {
1048
+ display: "flex",
1049
+ justifyContent: "center",
1050
+ alignItems: "center",
1051
+ gap: "6px",
1052
+ };
1053
+ const dotStyle = {
1054
+ width: sizeValue,
1055
+ height: sizeValue,
1056
+ borderRadius: "50%",
1057
+ backgroundColor: color,
1058
+ animation: `react-loadly-dots ${animationSettings.duration} ease-in-out infinite`,
1059
+ animationPlayState: animationSettings.playState,
1060
+ };
1061
+ // Create dots with different animation delays
1062
+ const dots = Array.from({ length: count }).map((_, index) => {
1063
+ const delay = `${index * 0.2}s`;
1064
+ return (jsxRuntime.jsx("div", { style: {
1065
+ ...dotStyle,
1066
+ animationDelay: delay,
1067
+ }, "data-testid": dataTestId ? `${dataTestId}-dot-${index}` : undefined }, index));
1068
+ });
1069
+ return (jsxRuntime.jsxs("div", { className: `react-loadly react-loadly-dots ${className}`.trim(), style: containerStyle, role: "status", "aria-label": ariaLabel, "aria-live": "polite", "aria-busy": loading, "data-testid": dataTestId, ...restProps, children: [jsxRuntime.jsx("div", { style: dotsContainerStyle, children: dots }), showText && (jsxRuntime.jsx("div", { className: "react-loadly-text", id: `${id}-text`, "aria-live": "polite", children: loadingText })), jsxRuntime.jsx("span", { className: "react-loadly-sr-only", children: ariaLabel })] }));
1070
+ };
1071
+
1072
+ const defaultProps$1 = {
1073
+ size: 60,
1074
+ color: "var(--react-loadly-color)",
1075
+ speed: 1,
1076
+ loading: true,
1077
+ borderWidth: 4,
1078
+ "aria-label": "Loading...",
1079
+ };
1080
+ const RingLoader = (userProps) => {
1081
+ const props = mergeProps(defaultProps$1, userProps);
1082
+ const { size, color, speed, loading, className = "", style = {}, borderWidth, showText, loadingText = "Loading...", "aria-label": ariaLabel, "data-testid": dataTestId, ...restProps } = props;
1083
+ // Use useRef instead of useMemo for better compatibility
1084
+ const idRef = react.useRef(generateId("ring-loader"));
1085
+ const sizeValue = getSizeValue(size);
1086
+ const animationSettings = getOptimizedAnimationSettings(speed);
1087
+ // Don't render anything if not loading
1088
+ if (!loading)
1089
+ return null;
1090
+ const containerStyle = {
1091
+ display: "inline-flex",
1092
+ flexDirection: "column",
1093
+ alignItems: "center",
1094
+ ...style,
1095
+ };
1096
+ const ringStyle = {
1097
+ position: "relative",
1098
+ width: sizeValue,
1099
+ height: sizeValue,
1100
+ };
1101
+ const ringSegmentStyle = {
1102
+ boxSizing: "border-box",
1103
+ display: "block",
1104
+ position: "absolute",
1105
+ width: sizeValue,
1106
+ height: sizeValue,
1107
+ border: `${borderWidth}px solid transparent`,
1108
+ borderTop: `${borderWidth}px solid ${color}`,
1109
+ borderBottom: `${borderWidth}px solid ${color}`,
1110
+ borderRadius: "50%",
1111
+ animation: `react-loadly-ring ${animationSettings.duration} cubic-bezier(0.5, 0, 0.5, 1) infinite`,
1112
+ animationPlayState: animationSettings.playState,
1113
+ };
1114
+ // Create the 4 ring segments with their specific styles
1115
+ const segments = Array.from({ length: 4 }).map((_, index) => {
1116
+ const rotation = `${index * 90}deg`;
1117
+ const delay = `${index * -0.15}s`;
1118
+ return (jsxRuntime.jsx("div", { style: {
1119
+ ...ringSegmentStyle,
1120
+ transform: `rotate(${rotation})`,
1121
+ animationDelay: delay,
1122
+ }, "data-testid": dataTestId ? `${dataTestId}-segment-${index}` : undefined }, index));
1123
+ });
1124
+ return (jsxRuntime.jsxs("div", { className: `react-loadly react-loadly-ring ${className}`.trim(), style: containerStyle, role: "status", "aria-label": ariaLabel, "aria-live": "polite", "aria-busy": loading, "data-testid": dataTestId, ...restProps, children: [jsxRuntime.jsx("div", { style: ringStyle, children: segments }), showText && (jsxRuntime.jsx("div", { className: "react-loadly-text", id: `${idRef.current}-text`, "aria-live": "polite", children: loadingText })), jsxRuntime.jsx("span", { className: "react-loadly-sr-only", children: ariaLabel })] }));
1125
+ };
1126
+
1127
+ const defaultProps = {
1128
+ size: 15,
1129
+ color: "var(--react-loadly-color)",
1130
+ speed: 1,
1131
+ loading: true,
1132
+ count: 2,
1133
+ "aria-label": "Loading...",
1134
+ };
1135
+ const RotateLoader = (userProps) => {
1136
+ const props = mergeProps(defaultProps, userProps);
1137
+ const { size, color, speed, loading, className = "", style = {}, count = 2, showText, loadingText = "Loading...", "aria-label": ariaLabel, "data-testid": dataTestId, ...restProps } = props;
1138
+ const id = react.useMemo(() => generateId("rotate-loader"), []);
1139
+ const sizeValue = getSizeValue(size);
1140
+ const animationSettings = getOptimizedAnimationSettings(speed);
1141
+ if (!loading)
1142
+ return null;
1143
+ const containerStyle = {
1144
+ display: "inline-flex",
1145
+ flexDirection: "column",
1146
+ alignItems: "center",
1147
+ ...style,
1148
+ };
1149
+ const rotateContainerStyle = {
1150
+ position: "relative",
1151
+ width: sizeValue,
1152
+ height: sizeValue,
1153
+ };
1154
+ const rotateElementStyle = {
1155
+ position: "absolute",
1156
+ width: sizeValue,
1157
+ height: sizeValue,
1158
+ border: "2px solid transparent",
1159
+ borderTopColor: color,
1160
+ borderBottomColor: color,
1161
+ borderRadius: "50%",
1162
+ animation: `react-loadly-ring ${animationSettings.duration} cubic-bezier(0.5, 0, 0.5, 1) infinite`,
1163
+ animationPlayState: animationSettings.playState,
1164
+ transform: "rotate(0deg)",
1165
+ };
1166
+ // Create rotating elements
1167
+ const elements = Array.from({ length: count }).map((_, index) => {
1168
+ const sizeFactor = 1 - index * 0.2;
1169
+ const borderWidth = 2 + index;
1170
+ const delay = `${index * -0.15}s`;
1171
+ return (jsxRuntime.jsx("div", { style: {
1172
+ ...rotateElementStyle,
1173
+ width: `${parseFloat(sizeValue) * sizeFactor}px`,
1174
+ height: `${parseFloat(sizeValue) * sizeFactor}px`,
1175
+ borderWidth: `${borderWidth}px`,
1176
+ animationDuration: `${parseFloat(animationSettings.duration) * (1 + index * 0.5)}ms`,
1177
+ animationDelay: delay,
1178
+ }, "data-testid": dataTestId ? `${dataTestId}-element-${index}` : undefined }, index));
1179
+ });
1180
+ return (jsxRuntime.jsxs("div", { className: `react-loadly react-loadly-ring ${className}`.trim(), style: containerStyle, role: "status", "aria-label": ariaLabel, "aria-live": "polite", "aria-busy": loading, "data-testid": dataTestId, ...restProps, children: [jsxRuntime.jsx("div", { style: rotateContainerStyle, children: elements }), showText && (jsxRuntime.jsx("div", { className: "react-loadly-text", id: `${id}-text`, "aria-live": "polite", children: loadingText })), jsxRuntime.jsx("span", { className: "react-loadly-sr-only", children: ariaLabel })] }));
1181
+ };
1182
+
1183
+ /**
1184
+ * Custom React hook for managing loader state with advanced features
1185
+ * Provides centralized loading state management with timeout, retry, and progress tracking
1186
+ *
1187
+ * @param options - Configuration options for the loader state
1188
+ * @returns Object containing state and methods to control the loader
1189
+ */
1190
+ const useLoaderState = (options = {}) => {
1191
+ const { initialLoading = false, timeout, maxRetries = 3, onLoadingChange, onError, onProgress, } = options;
1192
+ const [state, setState] = react.useState({
1193
+ isLoading: initialLoading,
1194
+ progress: 0,
1195
+ error: null,
1196
+ retryCount: 0,
1197
+ });
1198
+ const timeoutRef = react.useRef();
1199
+ const retryTimeoutRef = react.useRef();
1200
+ // Clear timeouts on unmount
1201
+ react.useEffect(() => {
1202
+ return () => {
1203
+ if (timeoutRef.current) {
1204
+ clearTimeout(timeoutRef.current);
1205
+ }
1206
+ if (retryTimeoutRef.current) {
1207
+ clearTimeout(retryTimeoutRef.current);
1208
+ }
1209
+ };
1210
+ }, []);
1211
+ // Handle timeout
1212
+ react.useEffect(() => {
1213
+ if (state.isLoading && timeout) {
1214
+ timeoutRef.current = setTimeout(() => {
1215
+ setState((prev) => ({
1216
+ ...prev,
1217
+ isLoading: false,
1218
+ error: "Loading timeout exceeded",
1219
+ }));
1220
+ onError?.("Loading timeout exceeded");
1221
+ }, timeout);
1222
+ }
1223
+ else if (timeoutRef.current) {
1224
+ clearTimeout(timeoutRef.current);
1225
+ }
1226
+ return () => {
1227
+ if (timeoutRef.current) {
1228
+ clearTimeout(timeoutRef.current);
1229
+ }
1230
+ };
1231
+ }, [state.isLoading, timeout, onError]);
1232
+ // Call onLoadingChange when loading state changes
1233
+ react.useEffect(() => {
1234
+ onLoadingChange?.(state.isLoading);
1235
+ }, [state.isLoading, onLoadingChange]);
1236
+ // Call onProgress when progress changes
1237
+ react.useEffect(() => {
1238
+ if (state.progress !== undefined) {
1239
+ onProgress?.(state.progress);
1240
+ }
1241
+ }, [state.progress, onProgress]);
1242
+ const setLoading = react.useCallback((loading) => {
1243
+ setState((prev) => ({
1244
+ ...prev,
1245
+ isLoading: loading,
1246
+ error: loading ? null : prev.error, // Clear error when starting new loading
1247
+ progress: loading ? 0 : prev.progress, // Reset progress when starting
1248
+ }));
1249
+ }, []);
1250
+ const setProgress = react.useCallback((progress) => {
1251
+ const clampedProgress = Math.min(Math.max(progress, 0), 100);
1252
+ setState((prev) => ({
1253
+ ...prev,
1254
+ progress: clampedProgress,
1255
+ // Auto-complete when progress reaches 100%
1256
+ isLoading: clampedProgress >= 100 ? false : prev.isLoading,
1257
+ }));
1258
+ }, []);
1259
+ const setError = react.useCallback((error) => {
1260
+ setState((prev) => ({
1261
+ ...prev,
1262
+ error,
1263
+ isLoading: false,
1264
+ }));
1265
+ if (error) {
1266
+ onError?.(error);
1267
+ }
1268
+ }, [onError]);
1269
+ const retry = react.useCallback(() => {
1270
+ setState((prev) => {
1271
+ const newRetryCount = (prev.retryCount || 0) + 1;
1272
+ if (newRetryCount > maxRetries) {
1273
+ onError?.("Maximum retry attempts exceeded");
1274
+ return {
1275
+ ...prev,
1276
+ error: "Maximum retry attempts exceeded",
1277
+ isLoading: false,
1278
+ };
1279
+ }
1280
+ return {
1281
+ ...prev,
1282
+ retryCount: newRetryCount,
1283
+ isLoading: true,
1284
+ error: null,
1285
+ progress: 0,
1286
+ };
1287
+ });
1288
+ // Add exponential backoff for retries
1289
+ const backoffDelay = Math.min(1000 * Math.pow(2, state.retryCount || 0), 30000);
1290
+ retryTimeoutRef.current = setTimeout(() => {
1291
+ setState((prev) => ({ ...prev, isLoading: true }));
1292
+ }, backoffDelay);
1293
+ }, [state.retryCount, maxRetries, onError]);
1294
+ const reset = react.useCallback(() => {
1295
+ setState({
1296
+ isLoading: false,
1297
+ progress: 0,
1298
+ error: null,
1299
+ retryCount: 0,
1300
+ });
1301
+ if (timeoutRef.current) {
1302
+ clearTimeout(timeoutRef.current);
1303
+ }
1304
+ if (retryTimeoutRef.current) {
1305
+ clearTimeout(retryTimeoutRef.current);
1306
+ }
1307
+ }, []);
1308
+ return {
1309
+ state,
1310
+ setLoading,
1311
+ setProgress,
1312
+ setError,
1313
+ retry,
1314
+ reset,
1315
+ };
1316
+ };
1317
+
1318
+ /**
1319
+ * Hook for managing multiple loader states with shared options
1320
+ * Useful when you need to control multiple loaders with the same configuration
1321
+ *
1322
+ * @param keys - Array of string keys representing each loader state
1323
+ * @param options - Shared configuration options for all loader states
1324
+ * @returns Record mapping each key to its corresponding loader state methods
1325
+ */
1326
+ const useMultipleLoaderStates = (keys, options = {}) => {
1327
+ return keys.reduce((acc, key) => {
1328
+ // eslint-disable-next-line react-hooks/rules-of-hooks
1329
+ acc[key] = useLoaderState(options);
1330
+ return acc;
1331
+ }, {});
1332
+ };
1333
+
1334
+ /**
1335
+ * Hook for tracking async operations with automatic loading state management
1336
+ * Automatically handles loading states during async operations and provides error handling
1337
+ *
1338
+ * @param asyncFn - Async function to execute and track
1339
+ * @param dependencies - Dependency array to trigger re-execution (similar to useEffect)
1340
+ * @param options - Configuration options for the loader state
1341
+ * @returns Object containing loader state, data result, and execution method
1342
+ */
1343
+ const useAsyncLoader = (asyncFn, dependencies = [], options = {}) => {
1344
+ const loaderState = useLoaderState(options);
1345
+ const [data, setData] = react.useState(null);
1346
+ const execute = react.useCallback(async () => {
1347
+ try {
1348
+ loaderState.setLoading(true);
1349
+ loaderState.setError(null);
1350
+ const result = await asyncFn();
1351
+ setData(result);
1352
+ loaderState.setProgress(100);
1353
+ return result;
1354
+ }
1355
+ catch (error) {
1356
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
1357
+ loaderState.setError(errorMessage);
1358
+ return null;
1359
+ }
1360
+ }, [asyncFn, loaderState]);
1361
+ // Auto-execute on dependency changes
1362
+ react.useEffect(() => {
1363
+ execute();
1364
+ }, dependencies);
1365
+ return {
1366
+ ...loaderState,
1367
+ data,
1368
+ execute,
1369
+ };
1370
+ };
1371
+
1372
+ exports.BarsLoader = BarsLoader;
1373
+ exports.BlobLoader = BlobLoader;
1374
+ exports.BounceLoader = BounceLoader;
1375
+ exports.Circle = Circle;
1376
+ exports.Dot = Dot;
1377
+ exports.DotCluster = DotCluster;
1378
+ exports.DotsLoader = DotsLoader;
1379
+ exports.FallbackLoader = FallbackLoader;
1380
+ exports.FlowLoader = FlowLoader;
1381
+ exports.GridLoader = GridLoader;
1382
+ exports.Line = Line;
1383
+ exports.LineGroup = LineGroup;
1384
+ exports.LiquidLoader = LiquidLoader;
1385
+ exports.LogoSpinLoader = LogoSpinLoader;
1386
+ exports.PulseLoader = PulseLoader;
1387
+ exports.Rectangle = Rectangle;
1388
+ exports.RingLoader = RingLoader;
1389
+ exports.RotateLoader = RotateLoader;
1390
+ exports.ShapeGroup = ShapeGroup;
1391
+ exports.SpinLoader = SpinLoader;
1392
+ exports.TypingLoader = TypingLoader;
1393
+ exports.WaveLoader = WaveLoader;
1394
+ exports.clamp = clamp;
1395
+ exports.createAnimationName = createAnimationName;
1396
+ exports.generateCSSVariables = generateCSSVariables;
1397
+ exports.generateId = generateId;
1398
+ exports.getAnimationDuration = getAnimationDuration;
1399
+ exports.getOptimizedAnimationSettings = getOptimizedAnimationSettings;
1400
+ exports.getSizeValue = getSizeValue;
1401
+ exports.hexToRgb = hexToRgb;
1402
+ exports.mergeProps = mergeProps;
1403
+ exports.prefersReducedMotion = prefersReducedMotion;
1404
+ exports.rgba = rgba;
1405
+ exports.sanitizeCSSValue = sanitizeCSSValue;
1406
+ exports.useAsyncLoader = useAsyncLoader;
1407
+ exports.useLoaderState = useLoaderState;
1408
+ exports.useMultipleLoaderStates = useMultipleLoaderStates;
1409
+ //# sourceMappingURL=index.js.map