@shohojdhara/atomix 0.3.4 → 0.3.5

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 (114) hide show
  1. package/dist/atomix.css +9 -10
  2. package/dist/atomix.css.map +1 -0
  3. package/dist/atomix.min.css +15108 -11
  4. package/dist/atomix.min.css.map +1 -0
  5. package/dist/charts.d.ts +1929 -0
  6. package/dist/charts.js +6482 -0
  7. package/dist/charts.js.map +1 -0
  8. package/dist/core.d.ts +1289 -0
  9. package/dist/core.js +3357 -0
  10. package/dist/core.js.map +1 -0
  11. package/dist/forms.d.ts +1085 -0
  12. package/dist/forms.js +2450 -0
  13. package/dist/forms.js.map +1 -0
  14. package/dist/heavy.d.ts +636 -0
  15. package/dist/heavy.js +4550 -0
  16. package/dist/heavy.js.map +1 -0
  17. package/dist/index.d.ts +5161 -4990
  18. package/dist/index.esm.js +1457 -784
  19. package/dist/index.esm.js.map +1 -1
  20. package/dist/index.js +1473 -790
  21. package/dist/index.js.map +1 -1
  22. package/dist/index.min.js +1 -1
  23. package/dist/index.min.js.map +1 -1
  24. package/dist/layout.d.ts +300 -0
  25. package/dist/layout.js +336 -0
  26. package/dist/layout.js.map +1 -0
  27. package/dist/theme.d.ts +1992 -0
  28. package/dist/theme.js +5348 -0
  29. package/dist/theme.js.map +1 -0
  30. package/package.json +66 -20
  31. package/scripts/atomix-cli.js +544 -16
  32. package/scripts/cli/__tests__/cli-commands.test.js +204 -0
  33. package/scripts/cli/__tests__/utils.test.js +201 -0
  34. package/scripts/cli/__tests__/vitest.config.js +26 -0
  35. package/scripts/cli/interactive-init.js +1 -1
  36. package/scripts/cli/token-manager.js +32 -7
  37. package/scripts/cli/utils.js +347 -0
  38. package/src/components/Accordion/Accordion.tsx +5 -54
  39. package/src/components/Accordion/index.ts +1 -1
  40. package/src/components/Avatar/Avatar.tsx +3 -3
  41. package/src/components/Badge/Badge.tsx +3 -3
  42. package/src/components/Breadcrumb/Breadcrumb.tsx +3 -3
  43. package/src/components/Card/ElevationCard.tsx +1 -1
  44. package/src/components/Chart/AnimatedChart.tsx +19 -17
  45. package/src/components/Chart/AreaChart.tsx +5 -1
  46. package/src/components/Chart/BarChart.tsx +1 -0
  47. package/src/components/Chart/BubbleChart.tsx +6 -5
  48. package/src/components/Chart/ChartToolbar.tsx +1 -0
  49. package/src/components/Chart/FunnelChart.tsx +1 -1
  50. package/src/components/Chart/RadarChart.tsx +19 -12
  51. package/src/components/Chart/ScatterChart.tsx +3 -3
  52. package/src/components/Chart/TreemapChart.tsx +2 -1
  53. package/src/components/Chart/WaterfallChart.tsx +0 -1
  54. package/src/components/Chart/types.ts +12 -2
  55. package/src/components/Chart/utils.ts +4 -3
  56. package/src/components/DataTable/DataTable.tsx +3 -3
  57. package/src/components/Dropdown/Dropdown.tsx +12 -9
  58. package/src/components/Footer/FooterSection.tsx +3 -3
  59. package/src/components/Form/Checkbox.tsx +3 -3
  60. package/src/components/Form/Input.tsx +4 -2
  61. package/src/components/Form/Radio.tsx +3 -3
  62. package/src/components/Form/Select.tsx +3 -3
  63. package/src/components/Form/Textarea.tsx +4 -2
  64. package/src/components/List/List.stories.tsx +3 -3
  65. package/src/components/List/List.tsx +3 -3
  66. package/src/components/List/ListGroup.tsx +3 -1
  67. package/src/components/Modal/Modal.tsx +3 -3
  68. package/src/components/Navigation/Menu/MegaMenu.tsx +9 -3
  69. package/src/components/Navigation/Menu/Menu.tsx +9 -3
  70. package/src/components/Pagination/Pagination.tsx +6 -5
  71. package/src/components/PhotoViewer/PhotoViewerImage.tsx +2 -2
  72. package/src/components/Popover/Popover.tsx +4 -4
  73. package/src/components/Progress/Progress.tsx +6 -2
  74. package/src/components/Rating/Rating.tsx +5 -2
  75. package/src/components/Slider/Slider.tsx +10 -9
  76. package/src/components/Spinner/Spinner.tsx +3 -3
  77. package/src/components/Tabs/Tabs.tsx +3 -3
  78. package/src/components/Tooltip/Tooltip.tsx +3 -3
  79. package/src/components/index.ts +5 -2
  80. package/src/layouts/MasonryGrid/MasonryGrid.tsx +2 -2
  81. package/src/lib/composables/useChartPerformance.ts +102 -78
  82. package/src/lib/composables/useChartScale.ts +10 -0
  83. package/src/lib/composables/useHero.ts +9 -2
  84. package/src/lib/composables/useHeroBackgroundSlider.ts +5 -3
  85. package/src/lib/composables/useSideMenu.ts +1 -0
  86. package/src/lib/composables/useVideoPlayer.ts +3 -2
  87. package/src/lib/config/loader.ts +55 -13
  88. package/src/lib/hooks/index.ts +0 -1
  89. package/src/lib/hooks/useComponentCustomization.ts +10 -14
  90. package/src/lib/hooks/usePerformanceMonitor.ts +149 -0
  91. package/src/lib/patterns/index.ts +2 -2
  92. package/src/lib/patterns/slots.tsx +2 -2
  93. package/src/lib/theme/composeTheme.ts +1 -1
  94. package/src/lib/theme/core/ThemeEngine.ts +8 -0
  95. package/src/lib/theme/core/ThemeValidator.ts +5 -2
  96. package/src/lib/theme/devtools/Inspector.tsx +1 -1
  97. package/src/lib/theme/devtools/LiveEditor.tsx +11 -5
  98. package/src/lib/theme/generateCSSVariables.ts +1 -1
  99. package/src/lib/theme/i18n/rtl.ts +2 -1
  100. package/src/lib/theme/runtime/ThemeApplicator.ts +28 -11
  101. package/src/lib/theme/runtime/ThemeErrorBoundary.tsx +3 -3
  102. package/src/lib/theme/runtime/ThemeManager.ts +4 -0
  103. package/src/lib/theme-tools.ts +1 -1
  104. package/src/lib/types/components.ts +183 -34
  105. package/src/lib/types/partProps.ts +0 -16
  106. package/src/lib/utils/fontPreloader.ts +148 -0
  107. package/src/lib/utils/index.ts +11 -0
  108. package/src/lib/utils/memoryMonitor.ts +189 -0
  109. package/src/styles/01-settings/_settings.fonts.scss +2 -5
  110. package/src/styles/03-generic/_generated-root.css +22 -1
  111. package/src/styles/06-components/_components.navbar.scss +0 -6
  112. package/src/themes/themes.config.js +37 -4
  113. package/scripts/build-themes.js +0 -208
  114. package/src/components/AtomixGlass/atomixGLass.old.tsx +0 -1263
@@ -0,0 +1,347 @@
1
+ /**
2
+ * CLI Utility Functions
3
+ * Provides common utilities for the Atomix CLI including security, validation, and helpers
4
+ */
5
+
6
+ import { resolve, relative, isAbsolute, normalize } from 'path';
7
+ import { existsSync } from 'fs';
8
+ import { access } from 'fs/promises';
9
+
10
+ /**
11
+ * Validates and sanitizes file paths to prevent directory traversal attacks
12
+ * @param {string} inputPath - The path to validate
13
+ * @param {string} basePath - The base directory (defaults to process.cwd())
14
+ * @returns {Object} { isValid: boolean, safePath: string, error?: string }
15
+ */
16
+ export function validatePath(inputPath, basePath = process.cwd()) {
17
+ try {
18
+ // Normalize the paths to remove any '..' or '.' segments
19
+ const normalizedBase = normalize(resolve(basePath));
20
+ const normalizedInput = normalize(isAbsolute(inputPath)
21
+ ? inputPath
22
+ : resolve(basePath, inputPath));
23
+
24
+ // Check if the resolved path is within the base directory
25
+ const relativePath = relative(normalizedBase, normalizedInput);
26
+
27
+ // If the relative path starts with '..', it's outside the base directory
28
+ if (relativePath.startsWith('..')) {
29
+ return {
30
+ isValid: false,
31
+ safePath: null,
32
+ error: 'Path is outside the project directory'
33
+ };
34
+ }
35
+
36
+ // Additional checks for sensitive paths
37
+ const sensitivePatterns = [
38
+ /^\.git/,
39
+ /node_modules/,
40
+ /^\.env/,
41
+ /\.pem$/,
42
+ /\.key$/,
43
+ /private/i,
44
+ /secret/i
45
+ ];
46
+
47
+ for (const pattern of sensitivePatterns) {
48
+ if (pattern.test(relativePath)) {
49
+ return {
50
+ isValid: false,
51
+ safePath: null,
52
+ error: `Access to sensitive path is restricted: ${pattern}`
53
+ };
54
+ }
55
+ }
56
+
57
+ return {
58
+ isValid: true,
59
+ safePath: normalizedInput,
60
+ error: null
61
+ };
62
+ } catch (error) {
63
+ return {
64
+ isValid: false,
65
+ safePath: null,
66
+ error: `Invalid path: ${error.message}`
67
+ };
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Validates component names according to PascalCase convention
73
+ * @param {string} name - The component name to validate
74
+ * @returns {Object} { isValid: boolean, error?: string }
75
+ */
76
+ export function validateComponentName(name) {
77
+ if (!name || typeof name !== 'string') {
78
+ return {
79
+ isValid: false,
80
+ error: 'Component name must be a non-empty string'
81
+ };
82
+ }
83
+
84
+ // Check PascalCase: starts with uppercase, only contains letters and numbers
85
+ if (!/^[A-Z][a-zA-Z0-9]*$/.test(name)) {
86
+ return {
87
+ isValid: false,
88
+ error: 'Component name must be in PascalCase (e.g., Button, CardHeader)'
89
+ };
90
+ }
91
+
92
+ // Check for reserved words
93
+ const reservedWords = [
94
+ 'Component', 'React', 'Fragment', 'Suspense', 'StrictMode',
95
+ 'Error', 'Loading', 'App', 'Root', 'Document', 'Html'
96
+ ];
97
+
98
+ if (reservedWords.includes(name)) {
99
+ return {
100
+ isValid: false,
101
+ error: `"${name}" is a reserved word. Please choose a different name.`
102
+ };
103
+ }
104
+
105
+ // Check minimum length
106
+ if (name.length < 2) {
107
+ return {
108
+ isValid: false,
109
+ error: 'Component name must be at least 2 characters long'
110
+ };
111
+ }
112
+
113
+ return { isValid: true };
114
+ }
115
+
116
+ /**
117
+ * Validates theme names according to kebab-case convention
118
+ * @param {string} name - The theme name to validate
119
+ * @returns {Object} { isValid: boolean, error?: string }
120
+ */
121
+ export function validateThemeName(name) {
122
+ if (!name || typeof name !== 'string') {
123
+ return {
124
+ isValid: false,
125
+ error: 'Theme name must be a non-empty string'
126
+ };
127
+ }
128
+
129
+ // Check kebab-case: lowercase letters, numbers, and hyphens
130
+ if (!/^[a-z][a-z0-9-]*$/.test(name)) {
131
+ return {
132
+ isValid: false,
133
+ error: 'Theme name must be lowercase and use hyphens (e.g., dark-theme)'
134
+ };
135
+ }
136
+
137
+ // Check for consecutive hyphens
138
+ if (/--/.test(name)) {
139
+ return {
140
+ isValid: false,
141
+ error: 'Theme name cannot contain consecutive hyphens'
142
+ };
143
+ }
144
+
145
+ // Check for trailing hyphen
146
+ if (name.endsWith('-')) {
147
+ return {
148
+ isValid: false,
149
+ error: 'Theme name cannot end with a hyphen'
150
+ };
151
+ }
152
+
153
+ return { isValid: true };
154
+ }
155
+
156
+ /**
157
+ * Sanitizes user input to prevent injection attacks
158
+ * @param {string} input - The user input to sanitize
159
+ * @returns {string} Sanitized input
160
+ */
161
+ export function sanitizeInput(input) {
162
+ if (typeof input !== 'string') {
163
+ return String(input);
164
+ }
165
+
166
+ // Remove any shell metacharacters that could be dangerous
167
+ return input
168
+ .replace(/[;&|`$<>\\]/g, '')
169
+ .replace(/\0/g, '') // Remove null bytes
170
+ .trim();
171
+ }
172
+
173
+ /**
174
+ * Checks if a file exists and is accessible
175
+ * @param {string} filePath - Path to check
176
+ * @returns {Promise<boolean>}
177
+ */
178
+ export async function fileExists(filePath) {
179
+ try {
180
+ await access(filePath);
181
+ return true;
182
+ } catch {
183
+ return false;
184
+ }
185
+ }
186
+
187
+ /**
188
+ * Checks if running in CI environment
189
+ * @returns {boolean}
190
+ */
191
+ export function isCI() {
192
+ return !!(
193
+ process.env.CI ||
194
+ process.env.CONTINUOUS_INTEGRATION ||
195
+ process.env.GITHUB_ACTIONS ||
196
+ process.env.GITLAB_CI ||
197
+ process.env.CIRCLECI ||
198
+ process.env.TRAVIS ||
199
+ process.env.JENKINS_URL
200
+ );
201
+ }
202
+
203
+ /**
204
+ * Checks if running in debug mode
205
+ * @returns {boolean}
206
+ */
207
+ export function isDebug() {
208
+ return process.env.ATOMIX_DEBUG === 'true' ||
209
+ process.argv.includes('--debug') ||
210
+ process.argv.includes('-d');
211
+ }
212
+
213
+ /**
214
+ * Formats file size in human readable format
215
+ * @param {number} bytes - File size in bytes
216
+ * @returns {string} Formatted size
217
+ */
218
+ export function formatFileSize(bytes) {
219
+ const sizes = ['B', 'KB', 'MB', 'GB'];
220
+ if (bytes === 0) return '0 B';
221
+ const i = Math.floor(Math.log(bytes) / Math.log(1024));
222
+ return `${(bytes / Math.pow(1024, i)).toFixed(2)} ${sizes[i]}`;
223
+ }
224
+
225
+ /**
226
+ * Debounce function for watch mode
227
+ * @param {Function} func - Function to debounce
228
+ * @param {number} wait - Wait time in milliseconds
229
+ * @returns {Function} Debounced function
230
+ */
231
+ export function debounce(func, wait) {
232
+ let timeout;
233
+ return function executedFunction(...args) {
234
+ const later = () => {
235
+ clearTimeout(timeout);
236
+ func(...args);
237
+ };
238
+ clearTimeout(timeout);
239
+ timeout = setTimeout(later, wait);
240
+ };
241
+ }
242
+
243
+ /**
244
+ * Creates a safe file path for cross-platform compatibility
245
+ * @param {...string} segments - Path segments
246
+ * @returns {string} Safe file path
247
+ */
248
+ export function safePath(...segments) {
249
+ // Filter out empty segments and join with proper separator
250
+ return segments
251
+ .filter(Boolean)
252
+ .join('/')
253
+ .replace(/\/+/g, '/') // Remove duplicate slashes
254
+ .replace(/\\/g, '/'); // Convert Windows backslashes
255
+ }
256
+
257
+ /**
258
+ * Validates SCSS/CSS color values
259
+ * @param {string} color - Color value to validate
260
+ * @returns {boolean}
261
+ */
262
+ export function isValidColor(color) {
263
+ const patterns = [
264
+ /^#[0-9A-F]{3}$/i, // #RGB
265
+ /^#[0-9A-F]{4}$/i, // #RGBA
266
+ /^#[0-9A-F]{6}$/i, // #RRGGBB
267
+ /^#[0-9A-F]{8}$/i, // #RRGGBBAA
268
+ /^rgb\(/i, // rgb()
269
+ /^rgba\(/i, // rgba()
270
+ /^hsl\(/i, // hsl()
271
+ /^hsla\(/i, // hsla()
272
+ /^var\(--/ // CSS custom property
273
+ ];
274
+
275
+ return patterns.some(pattern => pattern.test(color));
276
+ }
277
+
278
+ /**
279
+ * Extracts and validates npm scripts from package.json
280
+ * @param {Object} packageJson - Parsed package.json content
281
+ * @param {Array<string>} requiredScripts - List of required script names
282
+ * @returns {Object} { valid: boolean, missing: Array<string> }
283
+ */
284
+ export function validateNpmScripts(packageJson, requiredScripts = []) {
285
+ const scripts = packageJson.scripts || {};
286
+ const missing = requiredScripts.filter(script => !scripts[script]);
287
+
288
+ return {
289
+ valid: missing.length === 0,
290
+ missing
291
+ };
292
+ }
293
+
294
+ /**
295
+ * Generates a unique ID for components/themes
296
+ * @param {string} prefix - Prefix for the ID
297
+ * @returns {string} Unique ID
298
+ */
299
+ export function generateId(prefix = 'atomix') {
300
+ const timestamp = Date.now().toString(36);
301
+ const random = Math.random().toString(36).substring(2, 7);
302
+ return `${prefix}-${timestamp}-${random}`;
303
+ }
304
+
305
+ /**
306
+ * Checks Node.js version compatibility
307
+ * @param {string} requiredVersion - Minimum required version (e.g., '18.0.0')
308
+ * @returns {Object} { compatible: boolean, current: string, required: string }
309
+ */
310
+ export function checkNodeVersion(requiredVersion = '18.0.0') {
311
+ const currentVersion = process.version.substring(1); // Remove 'v' prefix
312
+ const current = currentVersion.split('.').map(Number);
313
+ const required = requiredVersion.split('.').map(Number);
314
+
315
+ let compatible = true;
316
+ for (let i = 0; i < required.length; i++) {
317
+ if (current[i] < required[i]) {
318
+ compatible = false;
319
+ break;
320
+ } else if (current[i] > required[i]) {
321
+ break;
322
+ }
323
+ }
324
+
325
+ return {
326
+ compatible,
327
+ current: currentVersion,
328
+ required: requiredVersion
329
+ };
330
+ }
331
+
332
+ export default {
333
+ validatePath,
334
+ validateComponentName,
335
+ validateThemeName,
336
+ sanitizeInput,
337
+ fileExists,
338
+ isCI,
339
+ isDebug,
340
+ formatFileSize,
341
+ debounce,
342
+ safePath,
343
+ isValidColor,
344
+ validateNpmScripts,
345
+ generateId,
346
+ checkNodeVersion
347
+ };
@@ -1,61 +1,12 @@
1
- import React, { ReactNode, useId } from 'react';
1
+ import React, { ReactNode, useId, memo } from 'react';
2
2
  import { ACCORDION } from '../../lib/constants/components';
3
3
  import { useAccordion } from '../../lib/composables/useAccordion';
4
- import { BaseComponentProps, IconPosition, AtomixGlassProps } from '../../lib/types/components';
4
+ import type { AccordionProps as AccordionPropsType, AtomixGlassProps } from '../../lib/types/components';
5
5
  import { AtomixGlass } from '../AtomixGlass/AtomixGlass';
6
6
 
7
- /**
8
- * Accordion component for showing/hiding content panels
9
- */
10
- export interface AccordionProps extends BaseComponentProps {
11
- /**
12
- * Title of the accordion
13
- */
14
- title: string;
7
+ export type AccordionProps = AccordionPropsType;
15
8
 
16
- /**
17
- * Content to be shown when accordion is expanded
18
- */
19
- children: ReactNode;
20
-
21
- /**
22
- * Whether the accordion is initially open
23
- */
24
- defaultOpen?: boolean;
25
-
26
- /**
27
- * Position of the icon (right or left)
28
- */
29
- iconPosition?: IconPosition;
30
-
31
- /**
32
- * Custom icon for the accordion
33
- */
34
- icon?: ReactNode;
35
-
36
- /**
37
- * Controlled open state (overrides defaultOpen)
38
- */
39
- isOpen?: boolean;
40
-
41
- /**
42
- * Callback when open state changes (for controlled mode)
43
- */
44
- onOpenChange?: (open: boolean) => void;
45
-
46
- /**
47
- * Glass morphism effect for the accordion
48
- * Can be a boolean to enable with default settings, or an object with AtomixGlassProps to customize the effect
49
- */
50
- glass?: AtomixGlassProps | boolean;
51
-
52
- /**
53
- * Custom style for the accordion
54
- */
55
- style?: React.CSSProperties;
56
- }
57
-
58
- export const Accordion: React.FC<AccordionProps> = ({
9
+ export const Accordion: React.FC<AccordionProps> = memo(({
59
10
  title,
60
11
  children,
61
12
  defaultOpen = false,
@@ -159,7 +110,7 @@ export const Accordion: React.FC<AccordionProps> = ({
159
110
  }
160
111
 
161
112
  return accordionContent;
162
- };
113
+ });
163
114
 
164
115
  // Set display name for debugging
165
116
  Accordion.displayName = 'Accordion';
@@ -1,3 +1,3 @@
1
1
  export { Accordion } from './Accordion';
2
2
  export { default } from './Accordion';
3
- export type { AccordionProps } from './Accordion';
3
+ export type { AccordionProps } from '../../lib/types/components';
@@ -1,9 +1,9 @@
1
- import React, { useState } from 'react';
1
+ import React, { useState, memo } from 'react';
2
2
  import { AvatarProps } from '../../lib/types/components';
3
3
  import { AVATAR } from '../../lib/constants/components';
4
4
  import { Icon } from '../Icon/Icon';
5
5
 
6
- export const Avatar: React.FC<AvatarProps> = ({
6
+ export const Avatar: React.FC<AvatarProps> = memo(({
7
7
  src,
8
8
  alt = 'Avatar',
9
9
  initials,
@@ -62,7 +62,7 @@ export const Avatar: React.FC<AvatarProps> = ({
62
62
  )}
63
63
  </div>
64
64
  );
65
- };
65
+ });
66
66
 
67
67
  Avatar.displayName = 'Avatar';
68
68
 
@@ -1,10 +1,10 @@
1
- import React, { useRef } from 'react';
1
+ import React, { useRef, memo } from 'react';
2
2
  import { useBadge } from '../../lib/composables/useBadge';
3
3
  import { BADGE } from '../../lib/constants/components';
4
4
  import { BadgeProps } from '../../lib/types/components';
5
5
  import { AtomixGlass } from '../AtomixGlass/AtomixGlass';
6
6
 
7
- export const Badge: React.FC<BadgeProps> = ({
7
+ export const Badge: React.FC<BadgeProps> = memo(({
8
8
  label,
9
9
  variant = 'primary',
10
10
  size = 'md',
@@ -53,7 +53,7 @@ export const Badge: React.FC<BadgeProps> = ({
53
53
  }
54
54
 
55
55
  return badgeElement;
56
- };
56
+ });
57
57
 
58
58
  Badge.displayName = 'Badge';
59
59
 
@@ -1,4 +1,4 @@
1
- import React, { ReactNode } from 'react';
1
+ import React, { ReactNode, memo } from 'react';
2
2
  import { BREADCRUMB } from '../../lib/constants/components';
3
3
 
4
4
  export interface BreadcrumbItem {
@@ -69,7 +69,7 @@ export interface BreadcrumbProps {
69
69
  */
70
70
  style?: React.CSSProperties;
71
71
  }
72
- export const Breadcrumb: React.FC<BreadcrumbProps> = ({
72
+ export const Breadcrumb: React.FC<BreadcrumbProps> = memo(({
73
73
  items,
74
74
  divider,
75
75
  className = '',
@@ -129,7 +129,7 @@ export const Breadcrumb: React.FC<BreadcrumbProps> = ({
129
129
  </ol>
130
130
  </nav>
131
131
  );
132
- };
132
+ });
133
133
 
134
134
  Breadcrumb.displayName = 'Breadcrumb';
135
135
 
@@ -24,7 +24,7 @@ export const ElevationCard: React.FC<ElevationCardProps> = ({
24
24
  return (
25
25
  <div
26
26
  className={`${className} ${cardProps.className}`}
27
- ref={cardProps.ref}
27
+ ref={cardProps.ref as React.LegacyRef<HTMLDivElement>}
28
28
  style={style}
29
29
  tabIndex={cardProps.tabIndex}
30
30
  role={cardProps.role}
@@ -45,6 +45,22 @@ const AnimatedChart = memo(
45
45
  }>
46
46
  >([]);
47
47
 
48
+ // Animation time tracking - moved outside callback
49
+ useEffect(() => {
50
+ const animateFrame = (timestamp: number) => {
51
+ timeRef.current = timestamp;
52
+ animationRef.current = requestAnimationFrame(animateFrame);
53
+ };
54
+
55
+ animationRef.current = requestAnimationFrame(animateFrame);
56
+
57
+ return () => {
58
+ if (animationRef.current) {
59
+ cancelAnimationFrame(animationRef.current);
60
+ }
61
+ };
62
+ }, []);
63
+
48
64
  const renderContent = useCallback(
49
65
  ({
50
66
  scales,
@@ -57,21 +73,6 @@ const AnimatedChart = memo(
57
73
  }: ChartRenderContentParams) => {
58
74
  // Use toolbar state if available, fallback to config for backward compatibility
59
75
  const shouldAnimate = toolbarState?.animationsEnabled ?? renderConfig?.animate ?? true;
60
- // Animation time tracking
61
- useEffect(() => {
62
- const animateFrame = (timestamp: number) => {
63
- timeRef.current = timestamp;
64
- animationRef.current = requestAnimationFrame(animateFrame);
65
- };
66
-
67
- animationRef.current = requestAnimationFrame(animateFrame);
68
-
69
- return () => {
70
- if (animationRef.current) {
71
- cancelAnimationFrame(animationRef.current);
72
- }
73
- };
74
- }, []);
75
76
 
76
77
  if (!chartDatasets.length) return null;
77
78
 
@@ -117,7 +118,7 @@ const AnimatedChart = memo(
117
118
 
118
119
  case 'area':
119
120
  case 'line':
120
- default:
121
+ default: {
121
122
  // Create animated line/area
122
123
  const points = dataset.data.map((point: any, pointIndex: number) => ({
123
124
  x: padding + (pointIndex / (dataset.data.length - 1)) * chartWidth,
@@ -181,6 +182,7 @@ const AnimatedChart = memo(
181
182
  });
182
183
  }
183
184
  break;
185
+ }
184
186
  }
185
187
  });
186
188
 
@@ -213,7 +215,7 @@ const AnimatedChart = memo(
213
215
  return (
214
216
  <BaseChart
215
217
  ref={ref}
216
- type="animated"
218
+ type="line"
217
219
  datasets={datasets}
218
220
  config={config}
219
221
  renderContent={renderContent}
@@ -100,7 +100,11 @@ const AreaChart = memo(
100
100
  {showTooltips && hoveredPoint && (
101
101
  <ChartTooltip
102
102
  dataPoint={
103
- renderedDatasets[hoveredPoint.datasetIndex]?.data?.[hoveredPoint.pointIndex]
103
+ renderedDatasets[hoveredPoint.datasetIndex]?.data?.[hoveredPoint.pointIndex] ?? {
104
+ label: '',
105
+ value: 0,
106
+ color: '',
107
+ }
104
108
  }
105
109
  datasetLabel={renderedDatasets[hoveredPoint.datasetIndex]?.label}
106
110
  datasetColor={
@@ -58,6 +58,7 @@ const BarChart = memo(
58
58
  <>
59
59
  {barDimensions.map((bar, index) => {
60
60
  const dataset = renderedDatasets[bar.datasetIndex];
61
+ if (!dataset) return null;
61
62
  const point = dataset.data?.[bar.pointIndex];
62
63
  const color = dataset.color || colors[bar.datasetIndex];
63
64
  const isHovered =
@@ -8,7 +8,7 @@ export interface BubbleDataPoint {
8
8
  x: number;
9
9
  y: number;
10
10
  size: number;
11
- value?: number;
11
+ value: number;
12
12
  color?: string;
13
13
  metadata?: Record<string, any>;
14
14
  }
@@ -109,7 +109,7 @@ const BubbleChart = memo(
109
109
  hoveredPoint,
110
110
  toolbarState,
111
111
  config: renderConfig,
112
- }: ChartRenderContentParams) => {
112
+ }: any) => {
113
113
  if (!bubbleData.length) return null;
114
114
 
115
115
  // Use toolbar state if available, fallback to config for backward compatibility
@@ -197,9 +197,10 @@ const BubbleChart = memo(
197
197
  {bubbles}
198
198
  {showTooltips &&
199
199
  hoveredPoint &&
200
- hoveredPoint.pointIndex < bubbleData.length && (
200
+ hoveredPoint.pointIndex < bubbleData.length &&
201
+ bubbleData[hoveredPoint.pointIndex] && (
201
202
  <ChartTooltip
202
- dataPoint={bubbleData[hoveredPoint.pointIndex]}
203
+ dataPoint={bubbleData[hoveredPoint.pointIndex]!}
203
204
  datasetLabel="Bubbles"
204
205
  datasetColor={
205
206
  bubbleData[hoveredPoint.pointIndex]?.color ||
@@ -220,7 +221,7 @@ const BubbleChart = memo(
220
221
  <BaseChart
221
222
  ref={ref}
222
223
  type="bubble"
223
- datasets={datasets}
224
+ datasets={datasets as any}
224
225
  config={config}
225
226
  renderContent={renderContent}
226
227
  onDataPointClick={onDataPointClick}
@@ -201,6 +201,7 @@ const ChartToolbar = memo(
201
201
  document.removeEventListener('mousedown', handleClickOutside);
202
202
  };
203
203
  }
204
+ return undefined;
204
205
  }, [showExportMenu, showSettingsMenu]);
205
206
 
206
207
  // Handle keyboard shortcuts
@@ -281,7 +281,7 @@ const FunnelChart = memo(
281
281
  <g>{elements}</g>
282
282
  {showTooltips && hoveredPoint && funnelData[hoveredPoint.pointIndex] && (
283
283
  <ChartTooltip
284
- dataPoint={funnelData[hoveredPoint.pointIndex]}
284
+ dataPoint={funnelData[hoveredPoint.pointIndex]!}
285
285
  datasetLabel="Funnel Data"
286
286
  datasetColor={funnelData[hoveredPoint.pointIndex]?.color || colors[hoveredPoint.pointIndex % colors.length]}
287
287
  position={{