@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.
- package/CHANGELOG.md +20 -0
- package/build-tools/EXAMPLES.md +372 -0
- package/build-tools/README.md +242 -0
- package/build-tools/__tests__/error-handler.test.js +230 -0
- package/build-tools/__tests__/index.test.js +141 -0
- package/build-tools/__tests__/rollup-plugin.test.js +194 -0
- package/build-tools/__tests__/utils.test.js +161 -0
- package/build-tools/__tests__/vite-plugin.test.js +129 -0
- package/build-tools/__tests__/webpack-loader.test.js +190 -0
- package/build-tools/error-handler.js +308 -0
- package/build-tools/index.d.ts +43 -0
- package/build-tools/index.js +88 -0
- package/build-tools/package.json +67 -0
- package/build-tools/rollup-plugin.js +236 -0
- package/build-tools/types.d.ts +163 -0
- package/build-tools/utils.js +203 -0
- package/build-tools/vite-plugin.js +161 -0
- package/build-tools/webpack-loader.js +123 -0
- package/dist/atomix.css +203 -90
- package/dist/atomix.css.map +1 -1
- package/dist/atomix.min.css +3 -3
- package/dist/atomix.min.css.map +1 -1
- package/dist/build-tools/EXAMPLES.md +372 -0
- package/dist/build-tools/README.md +242 -0
- package/dist/build-tools/__tests__/error-handler.test.js +230 -0
- package/dist/build-tools/__tests__/index.test.js +141 -0
- package/dist/build-tools/__tests__/rollup-plugin.test.js +194 -0
- package/dist/build-tools/__tests__/utils.test.js +161 -0
- package/dist/build-tools/__tests__/vite-plugin.test.js +129 -0
- package/dist/build-tools/__tests__/webpack-loader.test.js +190 -0
- package/dist/build-tools/error-handler.js +308 -0
- package/dist/build-tools/index.d.ts +43 -0
- package/dist/build-tools/index.js +88 -0
- package/dist/build-tools/package.json +67 -0
- package/dist/build-tools/rollup-plugin.js +236 -0
- package/dist/build-tools/types.d.ts +163 -0
- package/dist/build-tools/utils.js +203 -0
- package/dist/build-tools/vite-plugin.js +161 -0
- package/dist/build-tools/webpack-loader.js +123 -0
- package/dist/charts.d.ts +1 -1
- package/dist/charts.js +86 -57
- package/dist/charts.js.map +1 -1
- package/dist/core.d.ts +1 -1
- package/dist/core.js +136 -112
- package/dist/core.js.map +1 -1
- package/dist/forms.d.ts +2 -5
- package/dist/forms.js +140 -128
- package/dist/forms.js.map +1 -1
- package/dist/heavy.d.ts +1 -1
- package/dist/heavy.js +136 -112
- package/dist/heavy.js.map +1 -1
- package/dist/index.d.ts +9 -61
- package/dist/index.esm.js +237 -286
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +250 -299
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/package.json +23 -8
- package/scripts/atomix-cli.js +170 -73
- package/scripts/cli/__tests__/README.md +81 -0
- package/scripts/cli/__tests__/basic.test.js +115 -0
- package/scripts/cli/__tests__/component-generator.test.js +332 -0
- package/scripts/cli/__tests__/integration.test.js +327 -0
- package/scripts/cli/__tests__/test-setup.js +133 -0
- package/scripts/cli/__tests__/token-manager.test.js +251 -0
- package/scripts/cli/__tests__/utils.test.js +161 -0
- package/scripts/cli/component-generator.js +253 -299
- package/scripts/cli/dependency-checker.js +355 -0
- package/scripts/cli/interactive-init.js +46 -5
- package/scripts/cli/template-manager.js +0 -2
- package/scripts/cli/templates/common-templates.js +636 -0
- package/scripts/cli/templates/composable-templates.js +148 -126
- package/scripts/cli/templates/index.js +23 -16
- package/scripts/cli/templates/project-templates.js +151 -23
- package/scripts/cli/templates/react-templates.js +280 -210
- package/scripts/cli/templates/scss-templates.js +90 -91
- package/scripts/cli/templates/testing-templates.js +206 -27
- package/scripts/cli/templates/testing-utils.js +278 -0
- package/scripts/cli/templates/types-templates.js +70 -56
- package/scripts/cli/theme-bridge.js +8 -2
- package/scripts/cli/token-manager.js +318 -206
- package/scripts/cli/utils.js +0 -1
- package/src/components/Accordion/Accordion.stories.tsx +369 -870
- package/src/components/AtomixGlass/AtomixGlass.tsx +80 -39
- package/src/components/AtomixGlass/AtomixGlassContainer.tsx +103 -81
- package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +8 -7
- package/src/components/AtomixGlass/glass-utils.ts +2 -2
- package/src/components/AtomixGlass/shader-utils.ts +5 -0
- package/src/components/AtomixGlass/stories/Customization.stories.tsx +131 -0
- package/src/components/AtomixGlass/stories/Examples.stories.tsx +2957 -2853
- package/src/components/AtomixGlass/stories/Modes.stories.tsx +1 -1
- package/src/components/AtomixGlass/stories/Overview.stories.tsx +348 -0
- package/src/components/AtomixGlass/stories/Performance.stories.tsx +103 -0
- package/src/components/AtomixGlass/stories/Playground.stories.tsx +50 -35
- package/src/components/AtomixGlass/stories/{ShaderVariants.stories.tsx → Shaders.stories.tsx} +1 -1
- package/src/components/AtomixGlass/stories/shared-components.tsx +90 -190
- package/src/components/Avatar/Avatar.stories.tsx +213 -1
- package/src/components/Badge/Badge.stories.tsx +121 -362
- package/src/components/Block/Block.stories.tsx +21 -12
- package/src/components/Breadcrumb/Breadcrumb.stories.tsx +141 -23
- package/src/components/Button/Button.stories.tsx +463 -1126
- package/src/components/Button/Button.test.tsx +107 -0
- package/src/components/Button/Button.tsx +46 -50
- package/src/components/Button/ButtonGroup.stories.tsx +373 -217
- package/src/components/Callout/Callout.stories.tsx +289 -634
- package/src/components/Card/Card.stories.tsx +248 -68
- package/src/components/Chart/Chart.stories.tsx +150 -8
- package/src/components/ColorModeToggle/ColorModeToggle.stories.tsx +151 -69
- package/src/components/Countdown/Countdown.stories.tsx +115 -8
- package/src/components/DataTable/DataTable.stories.tsx +346 -146
- package/src/components/DatePicker/DatePicker.stories.tsx +325 -1066
- package/src/components/Dropdown/Dropdown.stories.tsx +153 -33
- package/src/components/EdgePanel/EdgePanel.stories.tsx +230 -21
- package/src/components/Footer/Footer.stories.tsx +392 -328
- package/src/components/Form/Checkbox.stories.tsx +140 -6
- package/src/components/Form/Checkbox.test.tsx +63 -0
- package/src/components/Form/Checkbox.tsx +87 -51
- package/src/components/Form/Form.stories.tsx +119 -20
- package/src/components/Form/FormGroup.stories.tsx +127 -4
- package/src/components/Form/Radio.stories.tsx +140 -5
- package/src/components/Form/Select.stories.tsx +140 -8
- package/src/components/Form/Textarea.stories.tsx +149 -6
- package/src/components/Hero/Hero.stories.tsx +333 -32
- package/src/components/List/List.stories.tsx +141 -3
- package/src/components/Modal/Modal.stories.tsx +181 -42
- package/src/components/Popover/Popover.stories.tsx +448 -98
- package/src/components/Progress/Progress.stories.tsx +167 -5
- package/src/components/River/River.stories.tsx +1 -1
- package/src/components/SectionIntro/SectionIntro.stories.tsx +240 -48
- package/src/components/Spinner/Spinner.stories.tsx +102 -8
- package/src/components/Steps/Steps.stories.tsx +172 -43
- package/src/components/Tabs/Tabs.stories.tsx +136 -10
- package/src/components/Testimonial/Testimonial.stories.tsx +120 -3
- package/src/components/Todo/Todo.stories.tsx +198 -9
- package/src/components/Toggle/Toggle.stories.tsx +126 -39
- package/src/components/Tooltip/Tooltip.stories.tsx +194 -104
- package/src/components/Upload/Upload.stories.tsx +113 -24
- package/src/lib/README.md +2 -2
- package/src/lib/__tests__/theme-tools.test.ts +193 -0
- package/src/lib/composables/index.ts +2 -2
- package/src/lib/composables/useAtomixGlass.ts +28 -56
- package/src/lib/composables/useChartExport.ts +2 -7
- package/src/lib/composables/useDataTable.ts +46 -29
- package/src/lib/constants/components.ts +9 -32
- package/src/lib/theme/devtools/CLI.ts +1 -1
- package/src/lib/types/components.ts +1 -1
- package/src/lib/utils/__tests__/csv.test.ts +45 -0
- package/src/lib/utils/csv.ts +17 -0
- package/src/lib/utils/dataTableExport.ts +1 -10
- package/src/styles/01-settings/_index.scss +2 -1
- package/src/styles/01-settings/_settings.accordion.scss +28 -7
- package/src/styles/01-settings/_settings.colors.scss +11 -11
- package/src/styles/01-settings/_settings.typography.scss +5 -5
- package/src/styles/02-tools/_tools.utility-api.scss +14 -0
- package/src/styles/06-components/_components.accordion.scss +56 -14
- package/src/styles/06-components/_components.checkbox.scss +23 -17
- package/src/styles/99-utilities/_index.scss +2 -0
- package/src/styles/99-utilities/_utilities.scss +3 -1
- package/src/styles/99-utilities/_utilities.text-gradient.scss +45 -0
- package/themes/dark-complementary/README.md +98 -0
- package/themes/dark-complementary/index.scss +158 -0
- package/themes/default-light/README.md +81 -0
- package/themes/default-light/index.scss +154 -0
- package/themes/high-contrast/README.md +105 -0
- package/themes/high-contrast/index.scss +172 -0
- package/themes/test-theme/README.md +38 -0
- package/themes/test-theme/index.scss +47 -0
- package/scripts/cli/templates-original-backup.js +0 -1655
- package/scripts/cli/templates_backup.js +0 -684
- package/src/components/AtomixGlass/stories/AtomixGlass.stories.tsx +0 -1438
- package/src/lib/composables/useButton.ts +0 -93
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
368
|
-
|
|
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
|
|
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 {
|
|
45
|
+
import { useAccordion } from '../lib/composables/useAccordion';
|
|
46
46
|
|
|
47
47
|
// In your component
|
|
48
|
-
const {
|
|
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
|
|
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
|
-
}, [
|
|
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.
|
|
941
|
-
brightness: Math.min(1.
|
|
942
|
-
saturationBoost:
|
|
943
|
-
shadowIntensity: Math.min(1.
|
|
944
|
-
borderOpacity: Math.min(1.0, Math.max(0.3, 0.7 + mouseInfluence * 0.
|
|
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.
|
|
962
|
-
brightness: validatedBrightness + mouseInfluence * 0.
|
|
963
|
-
saturationBoost: validatedSaturationBoost
|
|
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
|