@shohojdhara/atomix 0.3.14 → 0.3.15

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 (173) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/build-tools/EXAMPLES.md +372 -0
  3. package/build-tools/README.md +242 -0
  4. package/build-tools/__tests__/error-handler.test.js +230 -0
  5. package/build-tools/__tests__/index.test.js +141 -0
  6. package/build-tools/__tests__/rollup-plugin.test.js +194 -0
  7. package/build-tools/__tests__/utils.test.js +161 -0
  8. package/build-tools/__tests__/vite-plugin.test.js +129 -0
  9. package/build-tools/__tests__/webpack-loader.test.js +190 -0
  10. package/build-tools/error-handler.js +308 -0
  11. package/build-tools/index.d.ts +43 -0
  12. package/build-tools/index.js +88 -0
  13. package/build-tools/package.json +67 -0
  14. package/build-tools/rollup-plugin.js +236 -0
  15. package/build-tools/types.d.ts +163 -0
  16. package/build-tools/utils.js +203 -0
  17. package/build-tools/vite-plugin.js +161 -0
  18. package/build-tools/webpack-loader.js +123 -0
  19. package/dist/atomix.css +203 -90
  20. package/dist/atomix.css.map +1 -1
  21. package/dist/atomix.min.css +3 -3
  22. package/dist/atomix.min.css.map +1 -1
  23. package/dist/build-tools/EXAMPLES.md +372 -0
  24. package/dist/build-tools/README.md +242 -0
  25. package/dist/build-tools/__tests__/error-handler.test.js +230 -0
  26. package/dist/build-tools/__tests__/index.test.js +141 -0
  27. package/dist/build-tools/__tests__/rollup-plugin.test.js +194 -0
  28. package/dist/build-tools/__tests__/utils.test.js +161 -0
  29. package/dist/build-tools/__tests__/vite-plugin.test.js +129 -0
  30. package/dist/build-tools/__tests__/webpack-loader.test.js +190 -0
  31. package/dist/build-tools/error-handler.js +308 -0
  32. package/dist/build-tools/index.d.ts +43 -0
  33. package/dist/build-tools/index.js +88 -0
  34. package/dist/build-tools/package.json +67 -0
  35. package/dist/build-tools/rollup-plugin.js +236 -0
  36. package/dist/build-tools/types.d.ts +163 -0
  37. package/dist/build-tools/utils.js +203 -0
  38. package/dist/build-tools/vite-plugin.js +161 -0
  39. package/dist/build-tools/webpack-loader.js +123 -0
  40. package/dist/charts.d.ts +1 -1
  41. package/dist/charts.js +86 -57
  42. package/dist/charts.js.map +1 -1
  43. package/dist/core.d.ts +1 -1
  44. package/dist/core.js +136 -112
  45. package/dist/core.js.map +1 -1
  46. package/dist/forms.d.ts +2 -5
  47. package/dist/forms.js +140 -128
  48. package/dist/forms.js.map +1 -1
  49. package/dist/heavy.d.ts +1 -1
  50. package/dist/heavy.js +136 -112
  51. package/dist/heavy.js.map +1 -1
  52. package/dist/index.d.ts +9 -61
  53. package/dist/index.esm.js +237 -286
  54. package/dist/index.esm.js.map +1 -1
  55. package/dist/index.js +250 -299
  56. package/dist/index.js.map +1 -1
  57. package/dist/index.min.js +1 -1
  58. package/dist/index.min.js.map +1 -1
  59. package/package.json +23 -8
  60. package/scripts/atomix-cli.js +170 -73
  61. package/scripts/cli/__tests__/README.md +81 -0
  62. package/scripts/cli/__tests__/basic.test.js +115 -0
  63. package/scripts/cli/__tests__/component-generator.test.js +332 -0
  64. package/scripts/cli/__tests__/integration.test.js +327 -0
  65. package/scripts/cli/__tests__/test-setup.js +133 -0
  66. package/scripts/cli/__tests__/token-manager.test.js +251 -0
  67. package/scripts/cli/__tests__/utils.test.js +161 -0
  68. package/scripts/cli/component-generator.js +253 -299
  69. package/scripts/cli/dependency-checker.js +355 -0
  70. package/scripts/cli/interactive-init.js +46 -5
  71. package/scripts/cli/template-manager.js +0 -2
  72. package/scripts/cli/templates/common-templates.js +636 -0
  73. package/scripts/cli/templates/composable-templates.js +148 -126
  74. package/scripts/cli/templates/index.js +23 -16
  75. package/scripts/cli/templates/project-templates.js +151 -23
  76. package/scripts/cli/templates/react-templates.js +280 -210
  77. package/scripts/cli/templates/scss-templates.js +90 -91
  78. package/scripts/cli/templates/testing-templates.js +206 -27
  79. package/scripts/cli/templates/testing-utils.js +278 -0
  80. package/scripts/cli/templates/types-templates.js +70 -56
  81. package/scripts/cli/theme-bridge.js +8 -2
  82. package/scripts/cli/token-manager.js +318 -206
  83. package/scripts/cli/utils.js +0 -1
  84. package/src/components/Accordion/Accordion.stories.tsx +369 -870
  85. package/src/components/AtomixGlass/AtomixGlass.tsx +80 -39
  86. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +103 -81
  87. package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +8 -7
  88. package/src/components/AtomixGlass/glass-utils.ts +2 -2
  89. package/src/components/AtomixGlass/shader-utils.ts +5 -0
  90. package/src/components/AtomixGlass/stories/Customization.stories.tsx +131 -0
  91. package/src/components/AtomixGlass/stories/Examples.stories.tsx +2957 -2853
  92. package/src/components/AtomixGlass/stories/Modes.stories.tsx +1 -1
  93. package/src/components/AtomixGlass/stories/Overview.stories.tsx +348 -0
  94. package/src/components/AtomixGlass/stories/Performance.stories.tsx +103 -0
  95. package/src/components/AtomixGlass/stories/Playground.stories.tsx +50 -35
  96. package/src/components/AtomixGlass/stories/{ShaderVariants.stories.tsx → Shaders.stories.tsx} +1 -1
  97. package/src/components/AtomixGlass/stories/shared-components.tsx +90 -190
  98. package/src/components/Avatar/Avatar.stories.tsx +213 -1
  99. package/src/components/Badge/Badge.stories.tsx +121 -362
  100. package/src/components/Block/Block.stories.tsx +21 -12
  101. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +141 -23
  102. package/src/components/Button/Button.stories.tsx +463 -1126
  103. package/src/components/Button/Button.test.tsx +107 -0
  104. package/src/components/Button/Button.tsx +46 -50
  105. package/src/components/Button/ButtonGroup.stories.tsx +373 -217
  106. package/src/components/Callout/Callout.stories.tsx +289 -634
  107. package/src/components/Card/Card.stories.tsx +248 -68
  108. package/src/components/Chart/Chart.stories.tsx +150 -8
  109. package/src/components/ColorModeToggle/ColorModeToggle.stories.tsx +151 -69
  110. package/src/components/Countdown/Countdown.stories.tsx +115 -8
  111. package/src/components/DataTable/DataTable.stories.tsx +346 -146
  112. package/src/components/DatePicker/DatePicker.stories.tsx +325 -1066
  113. package/src/components/Dropdown/Dropdown.stories.tsx +153 -33
  114. package/src/components/EdgePanel/EdgePanel.stories.tsx +230 -21
  115. package/src/components/Footer/Footer.stories.tsx +392 -328
  116. package/src/components/Form/Checkbox.stories.tsx +140 -6
  117. package/src/components/Form/Checkbox.test.tsx +63 -0
  118. package/src/components/Form/Checkbox.tsx +87 -51
  119. package/src/components/Form/Form.stories.tsx +119 -20
  120. package/src/components/Form/FormGroup.stories.tsx +127 -4
  121. package/src/components/Form/Radio.stories.tsx +140 -5
  122. package/src/components/Form/Select.stories.tsx +140 -8
  123. package/src/components/Form/Textarea.stories.tsx +149 -6
  124. package/src/components/Hero/Hero.stories.tsx +333 -32
  125. package/src/components/List/List.stories.tsx +141 -3
  126. package/src/components/Modal/Modal.stories.tsx +181 -42
  127. package/src/components/Popover/Popover.stories.tsx +448 -98
  128. package/src/components/Progress/Progress.stories.tsx +167 -5
  129. package/src/components/River/River.stories.tsx +1 -1
  130. package/src/components/SectionIntro/SectionIntro.stories.tsx +240 -48
  131. package/src/components/Spinner/Spinner.stories.tsx +102 -8
  132. package/src/components/Steps/Steps.stories.tsx +172 -43
  133. package/src/components/Tabs/Tabs.stories.tsx +136 -10
  134. package/src/components/Testimonial/Testimonial.stories.tsx +120 -3
  135. package/src/components/Todo/Todo.stories.tsx +198 -9
  136. package/src/components/Toggle/Toggle.stories.tsx +126 -39
  137. package/src/components/Tooltip/Tooltip.stories.tsx +194 -104
  138. package/src/components/Upload/Upload.stories.tsx +113 -24
  139. package/src/lib/README.md +2 -2
  140. package/src/lib/__tests__/theme-tools.test.ts +193 -0
  141. package/src/lib/composables/index.ts +2 -2
  142. package/src/lib/composables/useAtomixGlass.ts +28 -56
  143. package/src/lib/composables/useChartExport.ts +2 -7
  144. package/src/lib/composables/useDataTable.ts +46 -29
  145. package/src/lib/constants/components.ts +9 -32
  146. package/src/lib/theme/devtools/CLI.ts +1 -1
  147. package/src/lib/types/components.ts +1 -1
  148. package/src/lib/utils/__tests__/csv.test.ts +45 -0
  149. package/src/lib/utils/csv.ts +17 -0
  150. package/src/lib/utils/dataTableExport.ts +1 -10
  151. package/src/styles/01-settings/_index.scss +2 -1
  152. package/src/styles/01-settings/_settings.accordion.scss +28 -7
  153. package/src/styles/01-settings/_settings.colors.scss +11 -11
  154. package/src/styles/01-settings/_settings.typography.scss +5 -5
  155. package/src/styles/02-tools/_tools.utility-api.scss +14 -0
  156. package/src/styles/06-components/_components.accordion.scss +56 -14
  157. package/src/styles/06-components/_components.checkbox.scss +23 -17
  158. package/src/styles/99-utilities/_index.scss +2 -0
  159. package/src/styles/99-utilities/_utilities.scss +3 -1
  160. package/src/styles/99-utilities/_utilities.text-gradient.scss +45 -0
  161. package/themes/dark-complementary/README.md +98 -0
  162. package/themes/dark-complementary/index.scss +158 -0
  163. package/themes/default-light/README.md +81 -0
  164. package/themes/default-light/index.scss +154 -0
  165. package/themes/high-contrast/README.md +105 -0
  166. package/themes/high-contrast/index.scss +172 -0
  167. package/themes/test-theme/README.md +38 -0
  168. package/themes/test-theme/index.scss +47 -0
  169. package/scripts/cli/templates-original-backup.js +0 -1655
  170. package/scripts/cli/templates_backup.js +0 -684
  171. package/src/components/AtomixGlass/stories/AtomixGlass.stories.tsx +0 -1438
  172. package/src/lib/composables/useButton.ts +0 -93
  173. package/src/lib/composables/useCheckbox.ts +0 -70
@@ -1,4 +1,5 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react';
2
+ import { fn } from '@storybook/test';
2
3
  import React, { useState } from 'react';
3
4
  import { Upload } from './Upload';
4
5
  import { SIZES } from '../../lib/constants/components';
@@ -10,8 +11,68 @@ const meta = {
10
11
  layout: 'centered',
11
12
  docs: {
12
13
  description: {
13
- component:
14
- 'The Upload component provides a modern file upload interface with drag & drop functionality, progress tracking, file preview, and validation. It supports single and multiple file uploads, custom file size limits, and provides visual feedback throughout the upload process. Ideal for forms requiring file attachments or media uploads.',
14
+ component: `
15
+ # Upload
16
+
17
+ ## Overview
18
+
19
+ Upload provides a modern file upload interface with drag & drop functionality, progress tracking, file preview, and validation. It supports single and multiple file uploads, custom file size limits, and provides visual feedback throughout the upload process. Ideal for forms requiring file attachments or media uploads.
20
+
21
+ ## Features
22
+
23
+ - Drag & drop file upload
24
+ - Progress tracking
25
+ - File preview
26
+ - Validation
27
+ - Multiple file support
28
+ - Customizable size limits
29
+ - Accessible design
30
+ - Responsive behavior
31
+
32
+ ## Accessibility
33
+
34
+ - Keyboard support: Upload via keyboard navigation
35
+ - Screen reader: File selection and status announced appropriately
36
+ - ARIA support: Proper roles and properties for upload components
37
+ - Focus management: Visible focus indicators maintained
38
+
39
+ ## Usage Examples
40
+
41
+ ### Basic Usage
42
+
43
+ \`\`\`tsx
44
+ <Upload
45
+ title="Drag and Drop files here"
46
+ onFileSelect={(files) => console.log(files)}
47
+ />
48
+ \`\`\`
49
+
50
+ ### Multiple Files
51
+
52
+ \`\`\`tsx
53
+ <Upload
54
+ multiple={true}
55
+ title="Drag and Drop files here"
56
+ onFileSelect={(files) => console.log(files)}
57
+ />
58
+ \`\`\`
59
+
60
+ ## API Reference
61
+
62
+ ### Props
63
+
64
+ | Prop | Type | Default | Description |
65
+ | ---- | ---- | ------- | ----------- |
66
+ | size | 'sm' \\| 'md' \\| 'lg' | 'md' | Size variant of the upload component |
67
+ | disabled | boolean | false | Whether the upload component is disabled |
68
+ | maxSizeInMB | number | 5 | Maximum file size in MB |
69
+ | multiple | boolean | false | Whether multiple files can be selected |
70
+ | title | string | 'Drag and Drop files here' | Text for the drag and drop section |
71
+ | supportedFilesText | string | 'Files supported: PDF, XSLS, JPEG, PNG, Scanner' | Text describing supported file types |
72
+ | buttonText | string | 'Choose File' | Text for the upload button |
73
+ | helperText | string | 'Maximum size: 5MB' | Helper text displayed below the button |
74
+ | onFileSelect | (files: File[]) => void | - | Callback when files are selected |
75
+ `,
15
76
  },
16
77
  },
17
78
  },
@@ -20,43 +81,71 @@ const meta = {
20
81
  size: {
21
82
  control: { type: 'select' },
22
83
  options: SIZES,
23
- defaultValue: 'md',
24
84
  description: 'Size variant of the upload component',
85
+ table: {
86
+ type: { summary: '"sm" | "md" | "lg"' },
87
+ defaultValue: { summary: 'md' },
88
+ },
25
89
  },
26
90
  disabled: {
27
91
  control: { type: 'boolean' },
28
- defaultValue: false,
29
92
  description: 'Whether the upload component is disabled',
93
+ table: {
94
+ type: { summary: 'boolean' },
95
+ defaultValue: { summary: false },
96
+ },
30
97
  },
31
98
  maxSizeInMB: {
32
99
  control: { type: 'number' },
33
- defaultValue: 5,
34
100
  description: 'Maximum file size in MB',
101
+ table: {
102
+ type: { summary: 'number' },
103
+ defaultValue: { summary: 5 },
104
+ },
35
105
  },
36
106
  multiple: {
37
107
  control: { type: 'boolean' },
38
- defaultValue: false,
39
108
  description: 'Whether multiple files can be selected',
109
+ table: {
110
+ type: { summary: 'boolean' },
111
+ defaultValue: { summary: false },
112
+ },
40
113
  },
41
114
  title: {
42
115
  control: { type: 'text' },
43
- defaultValue: 'Drag and Drop files here',
44
116
  description: 'Text for the drag and drop section',
117
+ table: {
118
+ type: { summary: 'string' },
119
+ defaultValue: { summary: 'Drag and Drop files here' },
120
+ },
45
121
  },
46
122
  supportedFilesText: {
47
123
  control: { type: 'text' },
48
- defaultValue: 'Files supported: PDF, XSLS, JPEG, PNG, Scanner',
49
124
  description: 'Text describing supported file types',
125
+ table: {
126
+ type: { summary: 'string' },
127
+ defaultValue: { summary: 'Files supported: PDF, XSLS, JPEG, PNG, Scanner' },
128
+ },
50
129
  },
51
130
  buttonText: {
52
131
  control: { type: 'text' },
53
- defaultValue: 'Choose File',
54
132
  description: 'Text for the upload button',
133
+ table: {
134
+ type: { summary: 'string' },
135
+ defaultValue: { summary: 'Choose File' },
136
+ },
55
137
  },
56
138
  helperText: {
57
139
  control: { type: 'text' },
58
- defaultValue: 'Maximum size: 5MB',
59
140
  description: 'Helper text displayed below the button',
141
+ table: {
142
+ type: { summary: 'string' },
143
+ defaultValue: { summary: 'Maximum size: 5MB' },
144
+ },
145
+ },
146
+ onFileSelect: {
147
+ action: 'file selected',
148
+ description: 'Callback when files are selected',
60
149
  },
61
150
  },
62
151
  } satisfies Meta<typeof Upload>;
@@ -64,8 +153,7 @@ const meta = {
64
153
  export default meta;
65
154
  type Story = StoryObj<typeof meta>;
66
155
 
67
- // Default upload component
68
- export const Default: Story = {
156
+ export const BasicUsage: Story = {
69
157
  render: args => (
70
158
  <div style={{ padding: '30px', maxWidth: '600px' }}>
71
159
  <Upload {...args} />
@@ -77,6 +165,7 @@ export const Default: Story = {
77
165
  supportedFilesText: 'Files supported: PDF, XSLS, JPEG, PNG, Scanner',
78
166
  buttonText: 'Choose File',
79
167
  helperText: 'Maximum size: 5MB',
168
+ onFileSelect: fn(),
80
169
  },
81
170
  parameters: {
82
171
  docs: {
@@ -142,8 +231,7 @@ export const SizeVariants: Story = {
142
231
  },
143
232
  };
144
233
 
145
- // Disabled state
146
- export const Disabled: Story = {
234
+ export const DisabledState: Story = {
147
235
  render: args => (
148
236
  <div style={{ padding: '30px', maxWidth: '600px' }}>
149
237
  <Upload {...args} />
@@ -151,16 +239,17 @@ export const Disabled: Story = {
151
239
  ),
152
240
  args: {
153
241
  size: 'md',
154
- title: 'Drag and Drop files here',
155
- supportedFilesText: 'Files supported: PDF, XSLS, JPEG, PNG, Scanner',
156
- buttonText: 'Choose File',
157
- helperText: 'Maximum size: 5MB',
158
242
  disabled: true,
243
+ title: 'Upload Disabled',
244
+ supportedFilesText: 'PDF, JPEG, PNG',
245
+ buttonText: 'Choose File',
246
+ helperText: 'This upload component is disabled',
247
+ onFileSelect: fn(),
159
248
  },
160
249
  parameters: {
161
250
  docs: {
162
251
  description: {
163
- story: 'Upload component in disabled state with reduced opacity and no interactions.',
252
+ story: 'Upload component in disabled state.',
164
253
  },
165
254
  },
166
255
  },
@@ -353,7 +442,6 @@ export const CustomIcon: Story = {
353
442
  },
354
443
  };
355
444
 
356
- // Multiple file upload
357
445
  export const MultipleFiles: Story = {
358
446
  render: args => (
359
447
  <div style={{ padding: '30px', maxWidth: '600px' }}>
@@ -363,15 +451,16 @@ export const MultipleFiles: Story = {
363
451
  args: {
364
452
  size: 'md',
365
453
  multiple: true,
454
+ title: 'Drag and Drop files here',
455
+ supportedFilesText: 'PDF, JPEG, PNG, DOCX',
366
456
  buttonText: 'Choose Files',
367
- title: 'Upload multiple files',
368
- supportedFilesText: 'Select multiple files at once',
369
- helperText: 'Maximum size: 5MB per file',
457
+ helperText: 'Multiple files supported, max size: 5MB each',
458
+ onFileSelect: fn(),
370
459
  },
371
460
  parameters: {
372
461
  docs: {
373
462
  description: {
374
- story: 'Upload component configured to accept multiple files at once.',
463
+ story: 'Upload component configured to allow multiple file selections.',
375
464
  },
376
465
  },
377
466
  },
package/src/lib/README.md CHANGED
@@ -42,10 +42,10 @@ src/
42
42
  Composables are reusable pieces of component logic that can be shared across components.
43
43
 
44
44
  ```typescript
45
- import { useButton } from '../lib/composables/useButton';
45
+ import { useAccordion } from '../lib/composables/useAccordion';
46
46
 
47
47
  // In your component
48
- const { generateButtonClass, handleClick } = useButton();
48
+ const { generateAccordionClass, handleToggle } = useAccordion();
49
49
  ```
50
50
 
51
51
  ### Component Interactions
@@ -0,0 +1,193 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import {
3
+ importTheme,
4
+ exportTheme,
5
+ quickTheme,
6
+ createDarkVariant,
7
+ validateTheme,
8
+ themeToCSS,
9
+ getThemeMetadata,
10
+ supportsDarkMode
11
+ } from '../theme-tools';
12
+ import { Theme } from '../theme/types';
13
+
14
+ describe('theme-tools', () => {
15
+ const mockTheme: Theme = {
16
+ name: 'Test Theme',
17
+ palette: {
18
+ primary: { main: '#7AFFD7' },
19
+ secondary: { main: '#FF5733' },
20
+ error: { main: '#FF0000' },
21
+ warning: { main: '#FFA500' },
22
+ info: { main: '#0000FF' },
23
+ success: { main: '#00FF00' },
24
+ background: {
25
+ default: '#ffffff',
26
+ paper: '#f5f5f5',
27
+ subtle: '#fafafa',
28
+ },
29
+ text: {
30
+ primary: '#000000',
31
+ secondary: '#666666',
32
+ disabled: '#999999',
33
+ },
34
+ },
35
+ typography: {
36
+ fontFamily: 'Arial',
37
+ fontSize: 16,
38
+ fontWeightLight: 300,
39
+ fontWeightRegular: 400,
40
+ fontWeightMedium: 500,
41
+ fontWeightSemiBold: 600,
42
+ fontWeightBold: 700,
43
+ h1: { fontSize: '2rem', fontWeight: 700, lineHeight: 1.2, letterSpacing: '0' },
44
+ h2: { fontSize: '1.5rem', fontWeight: 700, lineHeight: 1.2, letterSpacing: '0' },
45
+ h3: { fontSize: '1.25rem', fontWeight: 700, lineHeight: 1.2, letterSpacing: '0' },
46
+ h4: { fontSize: '1.1rem', fontWeight: 700, lineHeight: 1.2, letterSpacing: '0' },
47
+ h5: { fontSize: '1rem', fontWeight: 700, lineHeight: 1.2, letterSpacing: '0' },
48
+ h6: { fontSize: '0.9rem', fontWeight: 700, lineHeight: 1.2, letterSpacing: '0' },
49
+ body1: { fontSize: '1rem', fontWeight: 400, lineHeight: 1.5 },
50
+ body2: { fontSize: '0.875rem', fontWeight: 400, lineHeight: 1.5 },
51
+ },
52
+ spacing: (v: number) => `${v * 8}px`,
53
+ breakpoints: {
54
+ values: { xs: 0, sm: 600, md: 960, lg: 1280, xl: 1920 },
55
+ unit: 'px',
56
+ up: (k: any) => `@media (min-width: ${k}px)`,
57
+ down: (k: any) => `@media (max-width: ${k}px)`,
58
+ between: (s: any, e: any) => `@media (min-width: ${s}px) and (max-width: ${e}px)`,
59
+ },
60
+ shadows: { xs: 'none', sm: 'none', md: 'none', lg: 'none', xl: 'none' },
61
+ transitions: {
62
+ duration: { shortest: 150, shorter: 200, short: 250, standard: 300, complex: 375, enteringScreen: 225, leavingScreen: 195 },
63
+ easing: { easeInOut: 'ease', easeOut: 'ease', easeIn: 'ease', sharp: 'ease' }
64
+ },
65
+ zIndex: { mobileStepper: 1000, speedDial: 1050, appBar: 1100, drawer: 1200, modal: 1300, snackbar: 1400, tooltip: 1500 },
66
+ borderRadius: { base: 4, sm: 2, md: 4, lg: 8, xl: 12, xxl: 16, '3xl': 24, '4xl': 32, pill: 9999 },
67
+ custom: {},
68
+ __isJSTheme: true
69
+ };
70
+
71
+ describe('quickTheme', () => {
72
+ it('should create a theme with primary color', () => {
73
+ const theme = quickTheme('My Theme', '#ff0000');
74
+ expect(theme.name).toBe('My Theme');
75
+ expect(theme.palette.primary.main).toBe('#ff0000');
76
+ });
77
+
78
+ it('should create a theme with primary and secondary color', () => {
79
+ const theme = quickTheme('My Theme', '#ff0000', '#00ff00');
80
+ expect(theme.palette.secondary.main).toBe('#00ff00');
81
+ });
82
+ });
83
+
84
+ describe('createDarkVariant', () => {
85
+ it('should create a dark variant of a theme', () => {
86
+ const darkTheme = createDarkVariant(mockTheme);
87
+ expect(darkTheme.name).toBe('Test Theme Dark');
88
+ expect(darkTheme.palette.mode).toBe('dark');
89
+ expect(darkTheme.palette.background.default).toBe('#121212');
90
+ expect(darkTheme.palette.text.primary).toBe('#ffffff');
91
+ });
92
+ });
93
+
94
+ describe('validateTheme', () => {
95
+ it('should return valid for a correct theme', () => {
96
+ const result = validateTheme(mockTheme);
97
+ expect(result.valid).toBe(true);
98
+ expect(result.errors).toHaveLength(0);
99
+ });
100
+
101
+ it('should return errors for a theme missing name', () => {
102
+ const invalidTheme = { ...mockTheme, name: '' };
103
+ const result = validateTheme(invalidTheme as any);
104
+ expect(result.valid).toBe(false);
105
+ expect(result.errors).toContain('Theme must have a name');
106
+ });
107
+
108
+ it('should return errors for a theme missing palette', () => {
109
+ const invalidTheme = { ...mockTheme, palette: undefined };
110
+ const result = validateTheme(invalidTheme as any);
111
+ expect(result.valid).toBe(false);
112
+ expect(result.errors).toContain('Theme must have a palette');
113
+ });
114
+
115
+ it('should return errors for a theme missing primary color', () => {
116
+ const invalidTheme = { ...mockTheme, palette: { ...mockTheme.palette, primary: undefined } };
117
+ const result = validateTheme(invalidTheme as any);
118
+ expect(result.valid).toBe(false);
119
+ expect(result.errors).toContain('Theme palette must have a primary color');
120
+ });
121
+ });
122
+
123
+ describe('themeToCSS', () => {
124
+ it('should generate CSS variables', () => {
125
+ const css = themeToCSS(mockTheme);
126
+ expect(css).toContain(':root');
127
+ expect(css).toContain('--atomix-');
128
+ });
129
+
130
+ it('should use custom selector', () => {
131
+ const css = themeToCSS(mockTheme, '.custom-theme');
132
+ expect(css).toContain('.custom-theme');
133
+ });
134
+ });
135
+
136
+ describe('getThemeMetadata', () => {
137
+ it('should extract metadata correctly', () => {
138
+ const metadata = getThemeMetadata(mockTheme);
139
+ expect(metadata.name).toBe('Test Theme');
140
+ expect(metadata.color).toBe('#7AFFD7');
141
+ expect(metadata.supportsDarkMode).toBe(false);
142
+ });
143
+
144
+ it('should identify dark mode support from palette mode', () => {
145
+ const darkTheme = { ...mockTheme, palette: { ...mockTheme.palette, mode: 'dark' } };
146
+ const metadata = getThemeMetadata(darkTheme as any);
147
+ expect(metadata.supportsDarkMode).toBe(true);
148
+ });
149
+ });
150
+
151
+ describe('supportsDarkMode', () => {
152
+ it('should return true if palette mode is dark', () => {
153
+ const theme = { ...mockTheme, palette: { ...mockTheme.palette, mode: 'dark' } };
154
+ expect(supportsDarkMode(theme as any)).toBe(true);
155
+ });
156
+
157
+ it('should return true if supportsDarkMode property is true', () => {
158
+ const theme = { ...mockTheme, supportsDarkMode: true };
159
+ expect(supportsDarkMode(theme as any)).toBe(true);
160
+ });
161
+
162
+ it('should return true if a11y modes includes dark', () => {
163
+ const theme = { ...mockTheme, a11y: { modes: ['dark'] } };
164
+ expect(supportsDarkMode(theme as any)).toBe(true);
165
+ });
166
+
167
+ it('should return false otherwise', () => {
168
+ expect(supportsDarkMode(mockTheme)).toBe(false);
169
+ });
170
+ });
171
+
172
+ describe('exportTheme', () => {
173
+ it('should export theme to JSON string', () => {
174
+ const json = exportTheme(mockTheme);
175
+ const parsed = JSON.parse(json);
176
+ expect(parsed.name).toBe('Test Theme');
177
+ });
178
+ });
179
+
180
+ describe('importTheme', () => {
181
+ it('should import a valid theme JSON', () => {
182
+ const theme = { name: 'Imported Theme' };
183
+ const json = JSON.stringify(theme);
184
+ const result = importTheme(json);
185
+ expect(result.name).toBe('Imported Theme');
186
+ });
187
+
188
+ it('should throw an error for invalid JSON', () => {
189
+ const invalidJson = '{ invalid: json }';
190
+ expect(() => importTheme(invalidJson)).toThrow('Invalid theme JSON');
191
+ });
192
+ });
193
+ });
@@ -1,5 +1,5 @@
1
1
  // Button composables
2
- export * from './useButton';
2
+ // export * from './useButton';
3
3
 
4
4
  // Accordion composables
5
5
  export * from './useAccordion';
@@ -29,7 +29,7 @@ export * from './useEdgePanel';
29
29
  export * from './useTodo';
30
30
 
31
31
  // Form composables
32
- export * from './useCheckbox';
32
+ // export * from './useCheckbox';
33
33
  export * from './useForm';
34
34
  export * from './useFormGroup';
35
35
 
@@ -235,18 +235,12 @@ export function useAtomixGlass({
235
235
  const effectiveCornerRadius = useMemo(() => {
236
236
  if (cornerRadius !== undefined) {
237
237
  const result = Math.max(0, cornerRadius);
238
- // if (process.env.NODE_ENV !== 'production' && debugCornerRadius) {
239
- // console.log('[AtomixGlass] Using manual cornerRadius prop:', result);
240
- // }
241
238
  return result;
242
239
  }
243
240
 
244
241
  const result = Math.max(0, dynamicCornerRadius);
245
- // if (process.env.NODE_ENV !== 'production' && debugCornerRadius) {
246
- // console.log('[AtomixGlass] Using dynamic cornerRadius:', result);
247
- // }
248
242
  return result;
249
- }, [cornerRadius, dynamicCornerRadius, debugCornerRadius]);
243
+ }, [cornerRadius, dynamicCornerRadius]);
250
244
 
251
245
  const effectiveReducedMotion = useMemo(
252
246
  () => reducedMotion || userPrefersReducedMotion,
@@ -278,7 +272,6 @@ export function useAtomixGlass({
278
272
  const extractRadius = () => {
279
273
  try {
280
274
  let extractedRadius: number | null = null;
281
- let extractionSource = 'default';
282
275
 
283
276
  if (contentRef.current) {
284
277
  const firstChild = contentRef.current.firstElementChild as HTMLElement;
@@ -286,7 +279,6 @@ export function useAtomixGlass({
286
279
  const domRadius = extractBorderRadiusFromDOMElement(firstChild);
287
280
  if (domRadius !== null && domRadius > 0) {
288
281
  extractedRadius = domRadius;
289
- extractionSource = 'DOM element';
290
282
  }
291
283
  }
292
284
  }
@@ -295,25 +287,11 @@ export function useAtomixGlass({
295
287
  const childRadius = extractBorderRadiusFromChildren(children);
296
288
  if (childRadius > 0 && childRadius !== CONSTANTS.DEFAULT_CORNER_RADIUS) {
297
289
  extractedRadius = childRadius;
298
- extractionSource = 'React children';
299
290
  }
300
291
  }
301
292
 
302
293
  if (extractedRadius !== null && extractedRadius > 0) {
303
294
  setDynamicCornerRadius(extractedRadius);
304
-
305
- // if (process.env.NODE_ENV !== 'production' && debugCornerRadius) {
306
- // console.log('[AtomixGlass] Corner radius extracted:', {
307
- // value: extractedRadius,
308
- // source: extractionSource,
309
- // timestamp: new Date().toISOString(),
310
- // });
311
- // }
312
- } else if ((typeof process === 'undefined' || process.env?.NODE_ENV !== 'production') && debugCornerRadius) {
313
- // console.log(
314
- // '[AtomixGlass] No corner radius found, using default:',
315
- // CONSTANTS.DEFAULT_CORNER_RADIUS
316
- // );
317
295
  }
318
296
  } catch (error) {
319
297
  if ((typeof process === 'undefined' || process.env?.NODE_ENV !== 'production') && debugCornerRadius) {
@@ -451,19 +429,6 @@ export function useAtomixGlass({
451
429
  setCachedBackgroundDetection(element.parentElement, overLight, isOverLightDetected, threshold);
452
430
 
453
431
  setDetectedOverLight(isOverLightDetected);
454
-
455
- // Debug logging
456
- // if (process.env.NODE_ENV !== 'production' && debugOverLight) {
457
- // console.log('[AtomixGlass] OverLight Detection:', {
458
- // avgLuminance: avgLuminance.toFixed(3),
459
- // threshold: threshold.toFixed(3),
460
- // detected: isOverLightDetected,
461
- // validSamples,
462
- // totalLuminance: totalLuminance.toFixed(3),
463
- // configType: typeof overLight === 'object' ? 'object' : typeof overLight,
464
- // timestamp: new Date().toISOString(),
465
- // });
466
- // }
467
432
  } else {
468
433
  // Invalid luminance calculation, default to false
469
434
  const result = false;
@@ -501,17 +466,7 @@ export function useAtomixGlass({
501
466
  return () => clearTimeout(timeoutId);
502
467
  } else if (typeof overLight === 'boolean') {
503
468
  // For boolean values, disable auto-detection
504
- // Cache is automatically managed by WeakMap (no manual clearing needed)
505
469
  setDetectedOverLight(false);
506
-
507
- // Debug logging for boolean mode
508
- // if (process.env.NODE_ENV !== 'production' && debugOverLight) {
509
- // console.log('[AtomixGlass] OverLight Mode: boolean', {
510
- // value: overLight,
511
- // autoDetection: false,
512
- // timestamp: new Date().toISOString(),
513
- // });
514
- // }
515
470
  }
516
471
 
517
472
  if (typeof window.matchMedia !== 'function') {
@@ -609,7 +564,7 @@ export function useAtomixGlass({
609
564
 
610
565
  if ((typeof process === 'undefined' || process.env?.NODE_ENV !== 'production') && enablePerformanceMonitoring) {
611
566
  const endTime = performance.now();
612
- const duration = endTime - startTime;
567
+ // const duration = endTime - startTime;
613
568
  // if (duration > 5) {
614
569
  // console.warn(`AtomixGlass: Mouse tracking took ${duration.toFixed(2)}ms`);
615
570
  // }
@@ -684,6 +639,16 @@ export function useAtomixGlass({
684
639
 
685
640
  // Transform calculations
686
641
  const calculateDirectionalScale = useCallback(() => {
642
+ // Disable directional scaling if overLight is active (to prevent zooming/distorting the premium glass effect)
643
+ const isOverLightActive =
644
+ overLight === true ||
645
+ (overLight === 'auto' && detectedOverLight) ||
646
+ (typeof overLight === 'object' && overLight !== null && detectedOverLight);
647
+
648
+ if (isOverLightActive) {
649
+ return 'scale(1)';
650
+ }
651
+
687
652
  if (
688
653
  !globalMousePosition.x ||
689
654
  !globalMousePosition.y ||
@@ -727,7 +692,14 @@ export function useAtomixGlass({
727
692
  Math.abs(normalizedX) * stretchIntensity * 0.15;
728
693
 
729
694
  return `scaleX(${Math.max(0.8, scaleX)}) scaleY(${Math.max(0.8, scaleY)})`;
730
- }, [globalMousePosition, elasticity, glassSize, glassRef]);
695
+ }, [
696
+ globalMousePosition,
697
+ elasticity,
698
+ glassSize,
699
+ glassRef,
700
+ overLight,
701
+ detectedOverLight,
702
+ ]);
731
703
 
732
704
  const calculateFadeInFactor = useCallback(() => {
733
705
  if (
@@ -937,11 +909,11 @@ export function useAtomixGlass({
937
909
  isOverLight,
938
910
  threshold: 0.7,
939
911
  opacity: baseOpacity,
940
- contrast: Math.min(1.8, Math.max(1.0, 1.4 + mouseInfluence * 0.3)),
941
- brightness: Math.min(1.2, Math.max(0.7, 0.85 + mouseInfluence * 0.15)),
942
- saturationBoost: Math.min(2.0, Math.max(1.0, 1.3 + mouseInfluence * 0.4)),
943
- shadowIntensity: Math.min(1.5, Math.max(0.5, 0.9 + mouseInfluence * 0.5)),
944
- borderOpacity: Math.min(1.0, Math.max(0.3, 0.7 + mouseInfluence * 0.3)),
912
+ contrast: Math.min(1.6, Math.max(1.0, 1.4 + mouseInfluence * 0.1)),
913
+ brightness: Math.min(1.1, Math.max(0.8, 0.9 + mouseInfluence * 0.05)),
914
+ saturationBoost: 1.3, // Fixed value dynamic saturation amplifies perceived displacement
915
+ shadowIntensity: Math.min(1.2, Math.max(0.5, 0.9 + mouseInfluence * 0.2)),
916
+ borderOpacity: Math.min(1.0, Math.max(0.3, 0.7 + mouseInfluence * 0.1)),
945
917
  };
946
918
 
947
919
  if (typeof overLight === 'object' && overLight !== null) {
@@ -958,9 +930,9 @@ export function useAtomixGlass({
958
930
  ...baseConfig,
959
931
  threshold: validatedThreshold,
960
932
  opacity: validatedOpacity * hoverIntensity * activeIntensity,
961
- contrast: validatedContrast + mouseInfluence * 0.3,
962
- brightness: validatedBrightness + mouseInfluence * 0.15,
963
- saturationBoost: validatedSaturationBoost + mouseInfluence * 0.4,
933
+ contrast: Math.min(1.6, validatedContrast + mouseInfluence * 0.1),
934
+ brightness: Math.min(1.1, validatedBrightness + mouseInfluence * 0.05),
935
+ saturationBoost: validatedSaturationBoost, // Use validated value directly, no mouse influence
964
936
  };
965
937
 
966
938
  // Debug logging
@@ -1,4 +1,5 @@
1
1
  import { useCallback, useRef } from 'react';
2
+ import { sanitizeCSVCell } from '../utils/csv';
2
3
 
3
4
  export interface ExportOptions {
4
5
  /**
@@ -180,13 +181,7 @@ export function useChartExport() {
180
181
 
181
182
  // Convert to CSV string with sanitization
182
183
  const csvContent = rows
183
- .map(row => row.map(cell => {
184
- // Sanitize cell content to prevent CSV injection
185
- const sanitized = String(cell).replace(/[\r\n\t]/g, ' ').replace(/"/g, '""');
186
- // Prevent formula injection by prefixing dangerous characters
187
- const dangerous = /^[=+\-@]/;
188
- return `"${dangerous.test(sanitized) ? `'${sanitized}` : sanitized}"`;
189
- }).join(','))
184
+ .map(row => row.map(cell => `"${sanitizeCSVCell(cell)}"`).join(','))
190
185
  .join('\n');
191
186
 
192
187
  // Download