@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.
- package/dist/atomix.css +9 -10
- package/dist/atomix.css.map +1 -0
- package/dist/atomix.min.css +15108 -11
- package/dist/atomix.min.css.map +1 -0
- package/dist/charts.d.ts +1929 -0
- package/dist/charts.js +6482 -0
- package/dist/charts.js.map +1 -0
- package/dist/core.d.ts +1289 -0
- package/dist/core.js +3357 -0
- package/dist/core.js.map +1 -0
- package/dist/forms.d.ts +1085 -0
- package/dist/forms.js +2450 -0
- package/dist/forms.js.map +1 -0
- package/dist/heavy.d.ts +636 -0
- package/dist/heavy.js +4550 -0
- package/dist/heavy.js.map +1 -0
- package/dist/index.d.ts +5161 -4990
- package/dist/index.esm.js +1457 -784
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1473 -790
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/layout.d.ts +300 -0
- package/dist/layout.js +336 -0
- package/dist/layout.js.map +1 -0
- package/dist/theme.d.ts +1992 -0
- package/dist/theme.js +5348 -0
- package/dist/theme.js.map +1 -0
- package/package.json +66 -20
- package/scripts/atomix-cli.js +544 -16
- package/scripts/cli/__tests__/cli-commands.test.js +204 -0
- package/scripts/cli/__tests__/utils.test.js +201 -0
- package/scripts/cli/__tests__/vitest.config.js +26 -0
- package/scripts/cli/interactive-init.js +1 -1
- package/scripts/cli/token-manager.js +32 -7
- package/scripts/cli/utils.js +347 -0
- package/src/components/Accordion/Accordion.tsx +5 -54
- package/src/components/Accordion/index.ts +1 -1
- package/src/components/Avatar/Avatar.tsx +3 -3
- package/src/components/Badge/Badge.tsx +3 -3
- package/src/components/Breadcrumb/Breadcrumb.tsx +3 -3
- package/src/components/Card/ElevationCard.tsx +1 -1
- package/src/components/Chart/AnimatedChart.tsx +19 -17
- package/src/components/Chart/AreaChart.tsx +5 -1
- package/src/components/Chart/BarChart.tsx +1 -0
- package/src/components/Chart/BubbleChart.tsx +6 -5
- package/src/components/Chart/ChartToolbar.tsx +1 -0
- package/src/components/Chart/FunnelChart.tsx +1 -1
- package/src/components/Chart/RadarChart.tsx +19 -12
- package/src/components/Chart/ScatterChart.tsx +3 -3
- package/src/components/Chart/TreemapChart.tsx +2 -1
- package/src/components/Chart/WaterfallChart.tsx +0 -1
- package/src/components/Chart/types.ts +12 -2
- package/src/components/Chart/utils.ts +4 -3
- package/src/components/DataTable/DataTable.tsx +3 -3
- package/src/components/Dropdown/Dropdown.tsx +12 -9
- package/src/components/Footer/FooterSection.tsx +3 -3
- package/src/components/Form/Checkbox.tsx +3 -3
- package/src/components/Form/Input.tsx +4 -2
- package/src/components/Form/Radio.tsx +3 -3
- package/src/components/Form/Select.tsx +3 -3
- package/src/components/Form/Textarea.tsx +4 -2
- package/src/components/List/List.stories.tsx +3 -3
- package/src/components/List/List.tsx +3 -3
- package/src/components/List/ListGroup.tsx +3 -1
- package/src/components/Modal/Modal.tsx +3 -3
- package/src/components/Navigation/Menu/MegaMenu.tsx +9 -3
- package/src/components/Navigation/Menu/Menu.tsx +9 -3
- package/src/components/Pagination/Pagination.tsx +6 -5
- package/src/components/PhotoViewer/PhotoViewerImage.tsx +2 -2
- package/src/components/Popover/Popover.tsx +4 -4
- package/src/components/Progress/Progress.tsx +6 -2
- package/src/components/Rating/Rating.tsx +5 -2
- package/src/components/Slider/Slider.tsx +10 -9
- package/src/components/Spinner/Spinner.tsx +3 -3
- package/src/components/Tabs/Tabs.tsx +3 -3
- package/src/components/Tooltip/Tooltip.tsx +3 -3
- package/src/components/index.ts +5 -2
- package/src/layouts/MasonryGrid/MasonryGrid.tsx +2 -2
- package/src/lib/composables/useChartPerformance.ts +102 -78
- package/src/lib/composables/useChartScale.ts +10 -0
- package/src/lib/composables/useHero.ts +9 -2
- package/src/lib/composables/useHeroBackgroundSlider.ts +5 -3
- package/src/lib/composables/useSideMenu.ts +1 -0
- package/src/lib/composables/useVideoPlayer.ts +3 -2
- package/src/lib/config/loader.ts +55 -13
- package/src/lib/hooks/index.ts +0 -1
- package/src/lib/hooks/useComponentCustomization.ts +10 -14
- package/src/lib/hooks/usePerformanceMonitor.ts +149 -0
- package/src/lib/patterns/index.ts +2 -2
- package/src/lib/patterns/slots.tsx +2 -2
- package/src/lib/theme/composeTheme.ts +1 -1
- package/src/lib/theme/core/ThemeEngine.ts +8 -0
- package/src/lib/theme/core/ThemeValidator.ts +5 -2
- package/src/lib/theme/devtools/Inspector.tsx +1 -1
- package/src/lib/theme/devtools/LiveEditor.tsx +11 -5
- package/src/lib/theme/generateCSSVariables.ts +1 -1
- package/src/lib/theme/i18n/rtl.ts +2 -1
- package/src/lib/theme/runtime/ThemeApplicator.ts +28 -11
- package/src/lib/theme/runtime/ThemeErrorBoundary.tsx +3 -3
- package/src/lib/theme/runtime/ThemeManager.ts +4 -0
- package/src/lib/theme-tools.ts +1 -1
- package/src/lib/types/components.ts +183 -34
- package/src/lib/types/partProps.ts +0 -16
- package/src/lib/utils/fontPreloader.ts +148 -0
- package/src/lib/utils/index.ts +11 -0
- package/src/lib/utils/memoryMonitor.ts +189 -0
- package/src/styles/01-settings/_settings.fonts.scss +2 -5
- package/src/styles/03-generic/_generated-root.css +22 -1
- package/src/styles/06-components/_components.navbar.scss +0 -6
- package/src/themes/themes.config.js +37 -4
- package/scripts/build-themes.js +0 -208
- 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 {
|
|
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,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="
|
|
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
|
|
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
|
-
}:
|
|
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}
|
|
@@ -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={{
|