@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.
- package/CHANGELOG.md +19 -1
- package/dist/atomix.css +77 -0
- package/dist/atomix.css.map +1 -1
- package/dist/atomix.min.css +77 -0
- package/dist/atomix.min.css.map +1 -1
- package/dist/charts.js.map +1 -1
- package/dist/core.js.map +1 -1
- package/dist/forms.js.map +1 -1
- package/dist/heavy.js.map +1 -1
- package/dist/index.d.ts +578 -515
- package/dist/index.esm.js +3150 -2632
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +10485 -9973
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/theme.d.ts +237 -420
- package/dist/theme.js +1616 -1701
- package/dist/theme.js.map +1 -1
- package/package.json +1 -1
- package/src/components/DataTable/DataTable.stories.tsx +238 -0
- package/src/components/DataTable/DataTable.test.tsx +450 -0
- package/src/components/DataTable/DataTable.tsx +384 -61
- package/src/components/DatePicker/DatePicker.tsx +29 -38
- package/src/components/Upload/Upload.tsx +539 -40
- package/src/lib/composables/useDataTable.ts +355 -15
- package/src/lib/composables/useDatePicker.ts +19 -0
- package/src/lib/constants/components.ts +10 -0
- package/src/lib/theme/adapters/cssVariableMapper.ts +29 -14
- package/src/lib/theme/adapters/index.ts +1 -4
- package/src/lib/theme/config/configLoader.ts +53 -35
- package/src/lib/theme/core/composeTheme.ts +22 -30
- package/src/lib/theme/core/createTheme.ts +49 -26
- package/src/lib/theme/core/index.ts +0 -1
- package/src/lib/theme/generators/generateCSSNested.ts +4 -3
- package/src/lib/theme/generators/generateCSSVariables.ts +24 -16
- package/src/lib/theme/index.ts +10 -17
- package/src/lib/theme/runtime/ThemeApplicator.ts +6 -109
- package/src/lib/theme/runtime/ThemeErrorBoundary.tsx +3 -3
- package/src/lib/theme/runtime/ThemeProvider.tsx +205 -64
- package/src/lib/theme/runtime/useTheme.ts +1 -1
- package/src/lib/theme/runtime/useThemeTokens.ts +7 -16
- package/src/lib/theme/test/testTheme.ts +2 -1
- package/src/lib/theme/types.ts +14 -14
- package/src/lib/theme/utils/componentTheming.ts +35 -27
- package/src/lib/theme/utils/domUtils.ts +57 -15
- package/src/lib/theme/utils/injectCSS.ts +0 -1
- package/src/lib/theme/utils/themeHelpers.ts +1 -39
- package/src/lib/theme/utils/themeUtils.ts +1 -170
- package/src/lib/types/components.ts +145 -0
- package/src/lib/utils/dataTableExport.ts +143 -0
- package/src/styles/06-components/_components.data-table.scss +95 -0
- 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
|
-
}
|