@shohojdhara/atomix 0.3.7 → 0.3.9

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 (53) hide show
  1. package/CHANGELOG.md +19 -1
  2. package/dist/atomix.css +77 -0
  3. package/dist/atomix.css.map +1 -1
  4. package/dist/atomix.min.css +77 -0
  5. package/dist/atomix.min.css.map +1 -1
  6. package/dist/charts.js.map +1 -1
  7. package/dist/core.js.map +1 -1
  8. package/dist/forms.js.map +1 -1
  9. package/dist/heavy.js.map +1 -1
  10. package/dist/index.d.ts +578 -515
  11. package/dist/index.esm.js +3150 -2632
  12. package/dist/index.esm.js.map +1 -1
  13. package/dist/index.js +10485 -9973
  14. package/dist/index.js.map +1 -1
  15. package/dist/index.min.js +1 -1
  16. package/dist/index.min.js.map +1 -1
  17. package/dist/theme.d.ts +237 -420
  18. package/dist/theme.js +1616 -1701
  19. package/dist/theme.js.map +1 -1
  20. package/package.json +1 -1
  21. package/src/components/DataTable/DataTable.stories.tsx +238 -0
  22. package/src/components/DataTable/DataTable.test.tsx +450 -0
  23. package/src/components/DataTable/DataTable.tsx +384 -61
  24. package/src/components/DatePicker/DatePicker.tsx +29 -38
  25. package/src/components/Upload/Upload.tsx +539 -40
  26. package/src/lib/composables/useDataTable.ts +355 -15
  27. package/src/lib/composables/useDatePicker.ts +19 -0
  28. package/src/lib/constants/components.ts +10 -0
  29. package/src/lib/theme/adapters/cssVariableMapper.ts +29 -14
  30. package/src/lib/theme/adapters/index.ts +1 -4
  31. package/src/lib/theme/config/configLoader.ts +53 -35
  32. package/src/lib/theme/core/composeTheme.ts +22 -30
  33. package/src/lib/theme/core/createTheme.ts +49 -26
  34. package/src/lib/theme/core/index.ts +0 -1
  35. package/src/lib/theme/generators/generateCSSNested.ts +4 -3
  36. package/src/lib/theme/generators/generateCSSVariables.ts +24 -16
  37. package/src/lib/theme/index.ts +10 -17
  38. package/src/lib/theme/runtime/ThemeApplicator.ts +6 -109
  39. package/src/lib/theme/runtime/ThemeErrorBoundary.tsx +3 -3
  40. package/src/lib/theme/runtime/ThemeProvider.tsx +205 -64
  41. package/src/lib/theme/runtime/useTheme.ts +1 -1
  42. package/src/lib/theme/runtime/useThemeTokens.ts +7 -16
  43. package/src/lib/theme/test/testTheme.ts +2 -1
  44. package/src/lib/theme/types.ts +14 -14
  45. package/src/lib/theme/utils/componentTheming.ts +35 -27
  46. package/src/lib/theme/utils/domUtils.ts +57 -15
  47. package/src/lib/theme/utils/injectCSS.ts +0 -1
  48. package/src/lib/theme/utils/themeHelpers.ts +1 -39
  49. package/src/lib/theme/utils/themeUtils.ts +1 -170
  50. package/src/lib/types/components.ts +145 -0
  51. package/src/lib/utils/dataTableExport.ts +143 -0
  52. package/src/styles/06-components/_components.data-table.scss +95 -0
  53. package/src/lib/hooks/useThemeTokens.ts +0 -105
@@ -0,0 +1,143 @@
1
+ import { DataTableColumn, ExportFormat } from '../types/components';
2
+
3
+ /**
4
+ * Sanitize cell content to prevent CSV injection
5
+ */
6
+ function sanitizeCSVCell(cell: any): string {
7
+ const sanitized = String(cell ?? '').replace(/[\r\n\t]/g, ' ').replace(/"/g, '""');
8
+ // Prevent formula injection by prefixing dangerous characters
9
+ const dangerous = /^[=+\-@]/;
10
+ return dangerous.test(sanitized) ? `'${sanitized}` : sanitized;
11
+ }
12
+
13
+ /**
14
+ * Export data as CSV
15
+ */
16
+ export function exportToCSV(
17
+ data: any[],
18
+ columns: DataTableColumn[],
19
+ filename: string = 'data-table.csv'
20
+ ): void {
21
+ if (!data.length || !columns.length) return;
22
+
23
+ // Create headers
24
+ const headers = columns.map(col => col.title || col.key);
25
+
26
+ // Create rows
27
+ const rows = data.map(row => {
28
+ return columns.map(col => {
29
+ const value = row[col.key];
30
+ if (col.render) {
31
+ // For rendered cells, try to extract text content
32
+ // This is a simplified approach - in production you might want to handle React elements differently
33
+ return value ?? '';
34
+ }
35
+ return value ?? '';
36
+ });
37
+ });
38
+
39
+ // Convert to CSV string
40
+ const csvContent = [
41
+ headers.map(h => `"${sanitizeCSVCell(h)}"`).join(','),
42
+ ...rows.map(row => row.map(cell => `"${sanitizeCSVCell(cell)}"`).join(',')),
43
+ ].join('\n');
44
+
45
+ // Download
46
+ const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
47
+ const url = URL.createObjectURL(blob);
48
+ const link = document.createElement('a');
49
+ link.download = filename.endsWith('.csv') ? filename : `${filename}.csv`;
50
+ link.href = url;
51
+ link.click();
52
+ URL.revokeObjectURL(url);
53
+ }
54
+
55
+ /**
56
+ * Export data as JSON
57
+ */
58
+ export function exportToJSON(
59
+ data: any[],
60
+ filename: string = 'data-table.json'
61
+ ): void {
62
+ if (!data.length) return;
63
+
64
+ const jsonContent = JSON.stringify(data, null, 2);
65
+ const blob = new Blob([jsonContent], { type: 'application/json;charset=utf-8;' });
66
+ const url = URL.createObjectURL(blob);
67
+ const link = document.createElement('a');
68
+ link.download = filename.endsWith('.json') ? filename : `${filename}.json`;
69
+ link.href = url;
70
+ link.click();
71
+ URL.revokeObjectURL(url);
72
+ }
73
+
74
+ /**
75
+ * Export data as Excel (XLSX) - simplified version using CSV with .xlsx extension
76
+ * Note: For true Excel format, you would need a library like xlsx or exceljs
77
+ */
78
+ export function exportToExcel(
79
+ data: any[],
80
+ columns: DataTableColumn[],
81
+ filename: string = 'data-table.xlsx'
82
+ ): void {
83
+ // For now, we'll export as CSV but with .xlsx extension
84
+ // In a production environment, you'd want to use a library like 'xlsx' or 'exceljs'
85
+ // to create a proper Excel file
86
+ if (!data.length || !columns.length) return;
87
+
88
+ // Create headers
89
+ const headers = columns.map(col => col.title || col.key);
90
+
91
+ // Create rows
92
+ const rows = data.map(row => {
93
+ return columns.map(col => {
94
+ const value = row[col.key];
95
+ return value ?? '';
96
+ });
97
+ });
98
+
99
+ // Convert to CSV format (Excel can open CSV files)
100
+ const csvContent = [
101
+ headers.map(h => `"${sanitizeCSVCell(h)}"`).join(','),
102
+ ...rows.map(row => row.map(cell => `"${sanitizeCSVCell(cell)}"`).join(',')),
103
+ ].join('\n');
104
+
105
+ // Download with .xlsx extension (though it's actually CSV)
106
+ // In production, use a proper Excel library
107
+ const blob = new Blob([csvContent], {
108
+ type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
109
+ });
110
+ const url = URL.createObjectURL(blob);
111
+ const link = document.createElement('a');
112
+ link.download = filename.endsWith('.xlsx') ? filename : `${filename}.xlsx`;
113
+ link.href = url;
114
+ link.click();
115
+ URL.revokeObjectURL(url);
116
+ }
117
+
118
+ /**
119
+ * Export data in the specified format
120
+ */
121
+ export function exportData(
122
+ format: ExportFormat,
123
+ data: any[],
124
+ columns: DataTableColumn[],
125
+ filename?: string
126
+ ): void {
127
+ const defaultFilename = filename || 'data-table';
128
+
129
+ switch (format) {
130
+ case 'csv':
131
+ exportToCSV(data, columns, defaultFilename);
132
+ break;
133
+ case 'excel':
134
+ exportToExcel(data, columns, defaultFilename);
135
+ break;
136
+ case 'json':
137
+ exportToJSON(data, defaultFilename);
138
+ break;
139
+ default:
140
+ console.warn(`Unsupported export format: ${format}`);
141
+ }
142
+ }
143
+
@@ -64,6 +64,13 @@
64
64
  display: flex;
65
65
  align-items: center;
66
66
  justify-content: space-between;
67
+ position: relative;
68
+ }
69
+
70
+ &__header-actions {
71
+ display: flex;
72
+ align-items: center;
73
+ gap: 0.5rem;
67
74
  }
68
75
 
69
76
  &__sort-icon {
@@ -76,6 +83,55 @@
76
83
  }
77
84
  }
78
85
 
86
+ &__column-filter {
87
+ width: 120px;
88
+ padding: 0.25rem 0.5rem;
89
+ font-size: 0.875rem;
90
+ border: 1px solid var(--#{config.$prefix}border-color);
91
+ border-radius: 4px;
92
+ background-color: var(--#{config.$prefix}white);
93
+ color: var(--#{config.$prefix}body-color);
94
+
95
+ &:focus {
96
+ outline: none;
97
+ border-color: var(--#{config.$prefix}primary);
98
+ }
99
+ }
100
+
101
+ &__resize-handle {
102
+ position: absolute;
103
+ top: 0;
104
+ right: 0;
105
+ width: 4px;
106
+ height: 100%;
107
+ cursor: col-resize;
108
+ background-color: transparent;
109
+ z-index: 1;
110
+
111
+ &:hover {
112
+ background-color: var(--#{config.$prefix}primary);
113
+ opacity: 0.5;
114
+ }
115
+
116
+ &:active {
117
+ background-color: var(--#{config.$prefix}primary);
118
+ opacity: 1;
119
+ }
120
+ }
121
+
122
+ &__header-cell {
123
+ position: relative;
124
+
125
+ &--dragging {
126
+ opacity: 0.5;
127
+ cursor: move;
128
+ }
129
+
130
+ &--drag-over {
131
+ border-left: 2px solid var(--#{config.$prefix}primary);
132
+ }
133
+ }
134
+
79
135
  // Body cells
80
136
  &__cell {
81
137
  padding: data-table.$data-table-cell-padding-y data-table.$data-table-cell-padding-x;
@@ -95,6 +151,21 @@
95
151
  &[role='button'] {
96
152
  cursor: pointer;
97
153
  }
154
+
155
+ &--selected {
156
+ @include dynamic-background(rgba(var(--#{config.$prefix}primary-rgb), 0.1));
157
+ }
158
+ }
159
+
160
+ // Selection cell
161
+ &__cell--selection {
162
+ width: 48px;
163
+ text-align: center;
164
+ padding: data-table.$data-table-cell-padding-y 0.5rem;
165
+ }
166
+
167
+ &__radio {
168
+ cursor: pointer;
98
169
  }
99
170
 
100
171
  // Loading state
@@ -143,6 +214,15 @@
143
214
  &--loading {
144
215
  opacity: 0.7;
145
216
  }
217
+
218
+ &--sticky-header {
219
+ thead {
220
+ position: sticky;
221
+ top: var(--sticky-header-offset, 0);
222
+ z-index: 10;
223
+ @include dynamic-background(var(--#{config.$prefix}data-table-header-bg));
224
+ }
225
+ }
146
226
  }
147
227
 
148
228
  // Toolbar
@@ -151,6 +231,21 @@
151
231
  align-items: center;
152
232
  justify-content: space-between;
153
233
  margin-bottom: 1rem;
234
+ gap: 1rem;
235
+ flex-wrap: wrap;
236
+
237
+ &__left {
238
+ display: flex;
239
+ align-items: center;
240
+ gap: 1rem;
241
+ flex: 1;
242
+ }
243
+
244
+ &__right {
245
+ display: flex;
246
+ align-items: center;
247
+ gap: 0.5rem;
248
+ }
154
249
  }
155
250
 
156
251
  // Search
@@ -1,105 +0,0 @@
1
- import { useContext } from 'react';
2
- import { ThemeContext } from '../theme/runtime/ThemeContext';
3
- import { useTheme } from '../theme/runtime/useTheme';
4
-
5
- /**
6
- * Standardized hook for accessing theme tokens in components
7
- *
8
- * Provides consistent access to theme values across all components
9
- * using either CSS custom properties or theme object values.
10
- */
11
- export function useThemeTokens() {
12
- const { theme, activeTheme } = useTheme();
13
-
14
- // Helper function to get CSS variable value
15
- const getToken = useCallback((tokenName: string, fallback?: string) => {
16
- if (typeof window === 'undefined') return fallback || '';
17
-
18
- const cssVarName = `--atomix-${tokenName}`;
19
- const computedStyle = getComputedStyle(document.documentElement);
20
- return computedStyle.getPropertyValue(cssVarName).trim() || fallback || '';
21
- }, []);
22
-
23
- // Helper function to get theme object value
24
- const getThemeValue = useCallback((path: string, fallback?: any) => {
25
- if (!activeTheme) return fallback;
26
-
27
- // Navigate through nested theme object using dot notation
28
- return path.split('.').reduce((obj, key) => obj?.[key], activeTheme) || fallback;
29
- }, [activeTheme]);
30
-
31
- // Return unified API for accessing theme values
32
- return {
33
- theme,
34
- activeTheme,
35
- getToken,
36
- getThemeValue,
37
- // Commonly used tokens with fallbacks
38
- colors: {
39
- primary: getToken('primary', '#3b82f6'),
40
- secondary: getToken('secondary', '#10b981'),
41
- error: getToken('error', '#ef4444'),
42
- success: getToken('success', '#22c55e'),
43
- warning: getToken('warning', '#eab308'),
44
- info: getToken('info', '#3b82f6'),
45
- light: getToken('light', '#f9fafb'),
46
- dark: getToken('dark', '#111827'),
47
- },
48
- spacing: {
49
- 1: getToken('spacing-1', '0.25rem'),
50
- 2: getToken('spacing-2', '0.5rem'),
51
- 3: getToken('spacing-3', '0.75rem'),
52
- 4: getToken('spacing-4', '1rem'),
53
- 5: getToken('spacing-5', '1.25rem'),
54
- 6: getToken('spacing-6', '1.5rem'),
55
- 8: getToken('spacing-8', '2rem'),
56
- 10: getToken('spacing-10', '2.5rem'),
57
- 12: getToken('spacing-12', '3rem'),
58
- 16: getToken('spacing-16', '4rem'),
59
- 20: getToken('spacing-20', '5rem'),
60
- },
61
- borderRadius: {
62
- sm: getToken('border-radius-sm', '0.25rem'),
63
- md: getToken('border-radius-md', '0.5rem'),
64
- lg: getToken('border-radius-lg', '0.75rem'),
65
- xl: getToken('border-radius-xl', '1rem'),
66
- full: getToken('border-radius-full', '9999px'),
67
- },
68
- typography: {
69
- fontFamily: {
70
- sans: getToken('font-sans-serif', 'Inter, system-ui, sans-serif'),
71
- serif: getToken('font-serif', 'Georgia, serif'),
72
- mono: getToken('font-monospace', 'Fira Code, monospace'),
73
- },
74
- fontSize: {
75
- xs: getToken('font-size-xs', '0.75rem'),
76
- sm: getToken('font-size-sm', '0.875rem'),
77
- md: getToken('font-size-md', '1rem'),
78
- lg: getToken('font-size-lg', '1.125rem'),
79
- xl: getToken('font-size-xl', '1.25rem'),
80
- '2xl': getToken('font-size-2xl', '1.5rem'),
81
- '3xl': getToken('font-size-3xl', '1.875rem'),
82
- '4xl': getToken('font-size-4xl', '2.25rem'),
83
- },
84
- fontWeight: {
85
- light: getToken('font-weight-light', '300'),
86
- normal: getToken('font-weight-normal', '400'),
87
- medium: getToken('font-weight-medium', '500'),
88
- semibold: getToken('font-weight-semibold', '600'),
89
- bold: getToken('font-weight-bold', '700'),
90
- },
91
- },
92
- shadows: {
93
- sm: getToken('box-shadow-sm', '0 1px 2px 0 rgba(0, 0, 0, 0.05)'),
94
- md: getToken('box-shadow-md', '0 4px 6px -1px rgba(0, 0, 0, 0.1)'),
95
- lg: getToken('box-shadow-lg', '0 10px 15px -3px rgba(0, 0, 0, 0.1)'),
96
- xl: getToken('box-shadow-xl', '0 20px 25px -5px rgba(0, 0, 0, 0.1)'),
97
- inset: getToken('box-shadow-inset', 'inset 0 2px 4px 0 rgba(0, 0, 0, 0.06)'),
98
- },
99
- transitions: {
100
- fast: getToken('transition-fast', '150ms'),
101
- base: getToken('transition-base', '200ms'),
102
- slow: getToken('transition-slow', '300ms'),
103
- },
104
- };
105
- }