@shohojdhara/atomix 0.3.6 → 0.3.8

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 (74) hide show
  1. package/README.md +3 -3
  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 +50 -142
  7. package/dist/charts.js.map +1 -1
  8. package/dist/core.d.ts +2 -2
  9. package/dist/core.js +179 -274
  10. package/dist/core.js.map +1 -1
  11. package/dist/forms.js +50 -142
  12. package/dist/forms.js.map +1 -1
  13. package/dist/heavy.js +179 -274
  14. package/dist/heavy.js.map +1 -1
  15. package/dist/index.d.ts +1255 -1226
  16. package/dist/index.esm.js +2806 -2958
  17. package/dist/index.esm.js.map +1 -1
  18. package/dist/index.js +3113 -3269
  19. package/dist/index.js.map +1 -1
  20. package/dist/index.min.js +1 -1
  21. package/dist/index.min.js.map +1 -1
  22. package/dist/theme.d.ts +313 -667
  23. package/dist/theme.js +1818 -2589
  24. package/dist/theme.js.map +1 -1
  25. package/package.json +1 -1
  26. package/src/components/AtomixGlass/AtomixGlass.tsx +128 -356
  27. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +1 -1
  28. package/src/components/Button/Button.tsx +85 -167
  29. package/src/components/DataTable/DataTable.stories.tsx +238 -0
  30. package/src/components/DataTable/DataTable.test.tsx +450 -0
  31. package/src/components/DataTable/DataTable.tsx +384 -61
  32. package/src/components/DatePicker/DatePicker.tsx +29 -38
  33. package/src/components/Upload/Upload.tsx +539 -40
  34. package/src/lib/composables/useAtomixGlass.ts +7 -7
  35. package/src/lib/composables/useDataTable.ts +355 -15
  36. package/src/lib/composables/useDatePicker.ts +19 -0
  37. package/src/lib/config/loader.ts +2 -3
  38. package/src/lib/constants/components.ts +17 -0
  39. package/src/lib/hooks/usePerformanceMonitor.ts +1 -1
  40. package/src/lib/theme/adapters/cssVariableMapper.ts +29 -14
  41. package/src/lib/theme/adapters/index.ts +1 -4
  42. package/src/lib/theme/config/configLoader.ts +82 -223
  43. package/src/lib/theme/config/loader.ts +15 -21
  44. package/src/lib/theme/constants/constants.ts +1 -1
  45. package/src/lib/theme/core/ThemeRegistry.ts +75 -279
  46. package/src/lib/theme/core/composeTheme.ts +30 -88
  47. package/src/lib/theme/core/createTheme.ts +88 -51
  48. package/src/lib/theme/core/createThemeObject.ts +2 -2
  49. package/src/lib/theme/core/index.ts +15 -2
  50. package/src/lib/theme/errors/errors.ts +1 -1
  51. package/src/lib/theme/generators/generateCSSNested.ts +131 -0
  52. package/src/lib/theme/generators/generateCSSVariables.ts +24 -16
  53. package/src/lib/theme/generators/index.ts +6 -0
  54. package/src/lib/theme/index.ts +45 -27
  55. package/src/lib/theme/runtime/ThemeApplicator.ts +6 -109
  56. package/src/lib/theme/runtime/ThemeErrorBoundary.tsx +1 -1
  57. package/src/lib/theme/runtime/ThemeProvider.tsx +393 -544
  58. package/src/lib/theme/runtime/index.ts +1 -0
  59. package/src/lib/theme/runtime/useTheme.ts +1 -1
  60. package/src/lib/theme/runtime/useThemeTokens.ts +122 -0
  61. package/src/lib/theme/test/testTheme.ts +2 -1
  62. package/src/lib/theme/types.ts +14 -14
  63. package/src/lib/theme/utils/componentTheming.ts +140 -0
  64. package/src/lib/theme/utils/domUtils.ts +57 -15
  65. package/src/lib/theme/utils/injectCSS.ts +0 -1
  66. package/src/lib/theme/utils/naming.ts +100 -0
  67. package/src/lib/theme/utils/themeHelpers.ts +1 -39
  68. package/src/lib/theme/utils/themeUtils.ts +1 -170
  69. package/src/lib/types/components.ts +145 -0
  70. package/src/lib/utils/componentUtils.ts +1 -1
  71. package/src/lib/utils/dataTableExport.ts +143 -0
  72. package/src/lib/utils/memoryMonitor.ts +3 -3
  73. package/src/lib/utils/themeNaming.ts +135 -0
  74. package/src/styles/06-components/_components.data-table.scss +95 -0
@@ -309,14 +309,14 @@ export function useAtomixGlass({
309
309
  // timestamp: new Date().toISOString(),
310
310
  // });
311
311
  // }
312
- } else if (process.env.NODE_ENV !== 'production' && debugCornerRadius) {
312
+ } else if ((typeof process === 'undefined' || process.env?.NODE_ENV !== 'production') && debugCornerRadius) {
313
313
  // console.log(
314
314
  // '[AtomixGlass] No corner radius found, using default:',
315
315
  // CONSTANTS.DEFAULT_CORNER_RADIUS
316
316
  // );
317
317
  }
318
318
  } catch (error) {
319
- if (process.env.NODE_ENV !== 'production' && debugCornerRadius) {
319
+ if ((typeof process === 'undefined' || process.env?.NODE_ENV !== 'production') && debugCornerRadius) {
320
320
  console.error('[AtomixGlass] Error extracting corner radius:', error);
321
321
  }
322
322
  }
@@ -412,7 +412,7 @@ export function useAtomixGlass({
412
412
  }
413
413
  } catch (styleError) {
414
414
  // Silently continue if getting computed style fails for this element
415
- if (process.env.NODE_ENV === 'development') {
415
+ if (typeof process === 'undefined' || process.env?.NODE_ENV === 'development') {
416
416
  console.debug('AtomixGlass: Error getting computed style for element:', styleError);
417
417
  }
418
418
  }
@@ -484,7 +484,7 @@ export function useAtomixGlass({
484
484
  }
485
485
  } catch (error) {
486
486
  // Enhanced error logging with context
487
- if (process.env.NODE_ENV === 'development') {
487
+ if (typeof process === 'undefined' || process.env?.NODE_ENV === 'development') {
488
488
  console.warn('AtomixGlass: Error detecting background brightness:', error);
489
489
  }
490
490
  const result = false;
@@ -607,7 +607,7 @@ export function useAtomixGlass({
607
607
  setInternalMouseOffset(newOffset);
608
608
  setInternalGlobalMousePosition(globalPos);
609
609
 
610
- if (process.env.NODE_ENV !== 'production' && enablePerformanceMonitoring) {
610
+ if ((typeof process === 'undefined' || process.env?.NODE_ENV !== 'production') && enablePerformanceMonitoring) {
611
611
  const endTime = performance.now();
612
612
  const duration = endTime - startTime;
613
613
  // if (duration > 5) {
@@ -964,7 +964,7 @@ export function useAtomixGlass({
964
964
  };
965
965
 
966
966
  // Debug logging
967
- if (process.env.NODE_ENV !== 'production' && debugOverLight) {
967
+ if ((typeof process === 'undefined' || process.env?.NODE_ENV !== 'production') && debugOverLight) {
968
968
  console.log('[AtomixGlass] OverLight Config:', {
969
969
  isOverLight,
970
970
  config: {
@@ -996,7 +996,7 @@ export function useAtomixGlass({
996
996
  }
997
997
 
998
998
  // Debug logging for non-object configs
999
- if (process.env.NODE_ENV !== 'production' && debugOverLight) {
999
+ if ((typeof process === 'undefined' || process.env?.NODE_ENV !== 'production') && debugOverLight) {
1000
1000
  console.log('[AtomixGlass] OverLight Config:', {
1001
1001
  isOverLight,
1002
1002
  configType: typeof overLight === 'boolean' ? (overLight ? 'true' : 'false') : overLight,
@@ -1,5 +1,5 @@
1
- import { useState, useEffect, useCallback, useMemo } from 'react';
2
- import { DataTableColumn, SortConfig } from '../types/components';
1
+ import { useState, useEffect, useCallback, useMemo, useRef } from 'react';
2
+ import { DataTableColumn, SortConfig, SelectionMode } from '../types/components';
3
3
 
4
4
  export interface UseDataTableProps {
5
5
  /**
@@ -36,6 +36,46 @@ export interface UseDataTableProps {
36
36
  * Initial sort configuration
37
37
  */
38
38
  initialSortConfig?: SortConfig;
39
+
40
+ /**
41
+ * Row selection mode
42
+ */
43
+ selectionMode?: SelectionMode;
44
+
45
+ /**
46
+ * Selected row IDs (controlled)
47
+ */
48
+ selectedRowIds?: (string | number)[];
49
+
50
+ /**
51
+ * Callback when selection changes
52
+ */
53
+ onSelectionChange?: (selectedRows: any[], selectedIds: (string | number)[]) => void;
54
+
55
+ /**
56
+ * Key to use as unique identifier for rows
57
+ */
58
+ rowKey?: string | ((row: any) => string | number);
59
+
60
+ /**
61
+ * Column-specific filters
62
+ */
63
+ columnFilters?: boolean;
64
+
65
+ /**
66
+ * Whether columns can be reordered
67
+ */
68
+ reorderable?: boolean;
69
+
70
+ /**
71
+ * Callback when column order changes
72
+ */
73
+ onColumnReorder?: (columnKeys: string[]) => void;
74
+
75
+ /**
76
+ * Callback when column visibility changes
77
+ */
78
+ onColumnVisibilityChange?: (visibleColumns: string[]) => void;
39
79
  }
40
80
 
41
81
  export interface UseDataTableReturn {
@@ -73,6 +113,84 @@ export interface UseDataTableReturn {
73
113
  * Handle search input
74
114
  */
75
115
  handleSearch: (query: string) => void;
116
+
117
+ /**
118
+ * Selected row IDs
119
+ */
120
+ selectedRowIds: (string | number)[];
121
+
122
+ /**
123
+ * Selected rows data
124
+ */
125
+ selectedRows: any[];
126
+
127
+ /**
128
+ * Handle row selection
129
+ */
130
+ handleRowSelect: (rowId: string | number, selected: boolean) => void;
131
+
132
+ /**
133
+ * Handle select all
134
+ */
135
+ handleSelectAll: (selected: boolean) => void;
136
+
137
+ /**
138
+ * Whether all rows are selected
139
+ */
140
+ isAllSelected: boolean;
141
+
142
+ /**
143
+ * Whether some rows are selected
144
+ */
145
+ isIndeterminate: boolean;
146
+
147
+ /**
148
+ * Column order
149
+ */
150
+ columnOrder: string[];
151
+
152
+ /**
153
+ * Visible columns
154
+ */
155
+ visibleColumns: DataTableColumn[];
156
+
157
+ /**
158
+ * Column visibility map
159
+ */
160
+ columnVisibility: Record<string, boolean>;
161
+
162
+ /**
163
+ * Handle column visibility toggle
164
+ */
165
+ handleColumnVisibilityToggle: (columnKey: string) => void;
166
+
167
+ /**
168
+ * Column-specific filter values
169
+ */
170
+ columnFilterValues: Record<string, string>;
171
+
172
+ /**
173
+ * Handle column filter change
174
+ */
175
+ handleColumnFilterChange: (columnKey: string, value: string) => void;
176
+
177
+ /**
178
+ * Clear all column filters
179
+ */
180
+ clearColumnFilters: () => void;
181
+ }
182
+
183
+ /**
184
+ * Get unique row ID
185
+ */
186
+ function getRowId(row: any, rowKey?: string | ((row: any) => string | number)): string | number {
187
+ if (typeof rowKey === 'function') {
188
+ return rowKey(row);
189
+ }
190
+ if (typeof rowKey === 'string') {
191
+ return row[rowKey];
192
+ }
193
+ return row.id ?? row.key ?? JSON.stringify(row);
76
194
  }
77
195
 
78
196
  /**
@@ -86,6 +204,14 @@ export function useDataTable({
86
204
  pageSize = 10,
87
205
  onSort,
88
206
  initialSortConfig,
207
+ selectionMode = 'none',
208
+ selectedRowIds: controlledSelectedRowIds,
209
+ onSelectionChange,
210
+ rowKey,
211
+ columnFilters = false,
212
+ reorderable = false,
213
+ onColumnReorder,
214
+ onColumnVisibilityChange,
89
215
  }: UseDataTableProps): UseDataTableReturn {
90
216
  // Sort state
91
217
  const [sortConfig, setSortConfig] = useState<SortConfig | null>(initialSortConfig || null);
@@ -96,6 +222,61 @@ export function useDataTable({
96
222
  // Search state
97
223
  const [searchQuery, setSearchQuery] = useState<string>('');
98
224
 
225
+ // Selection state
226
+ const [internalSelectedRowIds, setInternalSelectedRowIds] = useState<(string | number)[]>([]);
227
+ const selectedRowIds = controlledSelectedRowIds ?? internalSelectedRowIds;
228
+
229
+ // Column order state
230
+ const [columnOrder, setColumnOrder] = useState<string[]>(() => columns.map(col => col.key));
231
+
232
+ // Column visibility state
233
+ const [columnVisibility, setColumnVisibility] = useState<Record<string, boolean>>(() => {
234
+ const visibility: Record<string, boolean> = {};
235
+ columns.forEach(col => {
236
+ visibility[col.key] = col.visible !== false;
237
+ });
238
+ return visibility;
239
+ });
240
+
241
+ // Column-specific filter values
242
+ const [columnFilterValues, setColumnFilterValues] = useState<Record<string, string>>({});
243
+
244
+ // Update column order when columns prop changes
245
+ useEffect(() => {
246
+ const newOrder = columns.map(col => col.key);
247
+ const currentOrderSet = new Set(columnOrder);
248
+ const newOrderSet = new Set(newOrder);
249
+
250
+ // Only update if there are actual differences
251
+ if (
252
+ newOrder.length !== columnOrder.length ||
253
+ !newOrder.every(key => currentOrderSet.has(key)) ||
254
+ !columnOrder.every(key => newOrderSet.has(key))
255
+ ) {
256
+ setColumnOrder(newOrder);
257
+ }
258
+ }, [columns]);
259
+
260
+ // Update column visibility when columns prop changes
261
+ useEffect(() => {
262
+ setColumnVisibility(prev => {
263
+ const updated: Record<string, boolean> = { ...prev };
264
+ columns.forEach(col => {
265
+ if (!(col.key in updated)) {
266
+ updated[col.key] = col.visible !== false;
267
+ }
268
+ });
269
+ return updated;
270
+ });
271
+ }, [columns]);
272
+
273
+ // Visible columns based on order and visibility
274
+ const visibleColumns = useMemo(() => {
275
+ return columnOrder
276
+ .map(key => columns.find(col => col.key === key))
277
+ .filter((col): col is DataTableColumn => col !== undefined && columnVisibility[col.key] !== false);
278
+ }, [columns, columnOrder, columnVisibility]);
279
+
99
280
  // Handle sorting
100
281
  const handleSort = useCallback(
101
282
  (key: string) => {
@@ -120,10 +301,10 @@ export function useDataTable({
120
301
  // Handle page change
121
302
  const handlePageChange = useCallback(
122
303
  (page: number) => {
123
- if (page < 1 || page > Math.ceil(data.length / pageSize)) return;
304
+ if (page < 1) return;
124
305
  setCurrentPage(page);
125
306
  },
126
- [data.length, pageSize]
307
+ []
127
308
  );
128
309
 
129
310
  // Handle search
@@ -132,20 +313,62 @@ export function useDataTable({
132
313
  setCurrentPage(1); // Reset to first page when searching
133
314
  }, []);
134
315
 
135
- // Filter data based on search query
316
+ // Handle column filter change
317
+ const handleColumnFilterChange = useCallback((columnKey: string, value: string) => {
318
+ setColumnFilterValues(prev => ({
319
+ ...prev,
320
+ [columnKey]: value,
321
+ }));
322
+ setCurrentPage(1); // Reset to first page when filtering
323
+ }, []);
324
+
325
+ // Clear all column filters
326
+ const clearColumnFilters = useCallback(() => {
327
+ setColumnFilterValues({});
328
+ setCurrentPage(1);
329
+ }, []);
330
+
331
+ // Filter data based on search query and column filters
136
332
  const filteredData = useMemo(() => {
137
- if (!searchQuery) return data;
333
+ let result = data;
334
+
335
+ // Apply global search
336
+ if (searchQuery) {
337
+ const lowercaseQuery = searchQuery.toLowerCase();
338
+ result = result.filter(row => {
339
+ return visibleColumns.some(column => {
340
+ const value = row[column.key];
341
+ if (value == null) return false;
342
+ return String(value).toLowerCase().includes(lowercaseQuery);
343
+ });
344
+ });
345
+ }
346
+
347
+ // Apply column-specific filters
348
+ if (columnFilters) {
349
+ result = result.filter(row => {
350
+ return Object.entries(columnFilterValues).every(([columnKey, filterValue]) => {
351
+ if (!filterValue) return true;
138
352
 
139
- const lowercaseQuery = searchQuery.toLowerCase();
353
+ const column = columns.find(col => col.key === columnKey);
354
+ if (!column || !column.filterable) return true;
140
355
 
141
- return data.filter(row => {
142
- return columns.some(column => {
143
- const value = row[column.key];
144
- if (value == null) return false;
145
- return String(value).toLowerCase().includes(lowercaseQuery);
356
+ const cellValue = row[columnKey];
357
+ if (cellValue == null) return false;
358
+
359
+ // Use custom filter function if provided
360
+ if (column.filterFunction) {
361
+ return column.filterFunction(cellValue, filterValue);
362
+ }
363
+
364
+ // Default text filter
365
+ return String(cellValue).toLowerCase().includes(filterValue.toLowerCase());
366
+ });
146
367
  });
147
- });
148
- }, [data, columns, searchQuery]);
368
+ }
369
+
370
+ return result;
371
+ }, [data, visibleColumns, searchQuery, columnFilterValues, columnFilters, columns]);
149
372
 
150
373
  // Sort data
151
374
  const sortedData = useMemo(() => {
@@ -182,6 +405,110 @@ export function useDataTable({
182
405
  return Math.max(1, Math.ceil(sortedData.length / pageSize));
183
406
  }, [sortedData.length, paginated, pageSize]);
184
407
 
408
+ // Selected rows data
409
+ const selectedRows = useMemo(() => {
410
+ if (selectionMode === 'none' || selectedRowIds.length === 0) return [];
411
+ return sortedData.filter(row => selectedRowIds.includes(getRowId(row, rowKey)));
412
+ }, [sortedData, selectedRowIds, selectionMode, rowKey]);
413
+
414
+ // Handle row selection
415
+ const handleRowSelect = useCallback(
416
+ (rowId: string | number, selected: boolean) => {
417
+ if (selectionMode === 'none') return;
418
+
419
+ let newSelectedIds: (string | number)[];
420
+
421
+ if (selectionMode === 'single') {
422
+ newSelectedIds = selected ? [rowId] : [];
423
+ } else {
424
+ // multiple
425
+ if (selected) {
426
+ newSelectedIds = [...selectedRowIds, rowId];
427
+ } else {
428
+ newSelectedIds = selectedRowIds.filter(id => id !== rowId);
429
+ }
430
+ }
431
+
432
+ if (!controlledSelectedRowIds) {
433
+ setInternalSelectedRowIds(newSelectedIds);
434
+ }
435
+
436
+ if (onSelectionChange) {
437
+ const selectedRowsData = sortedData.filter(row => newSelectedIds.includes(getRowId(row, rowKey)));
438
+ onSelectionChange(selectedRowsData, newSelectedIds);
439
+ }
440
+ },
441
+ [selectionMode, selectedRowIds, controlledSelectedRowIds, onSelectionChange, sortedData, rowKey]
442
+ );
443
+
444
+ // Handle select all
445
+ const handleSelectAll = useCallback(
446
+ (selected: boolean) => {
447
+ if (selectionMode !== 'multiple') return;
448
+
449
+ const newSelectedIds = selected
450
+ ? paginatedData.map(row => getRowId(row, rowKey))
451
+ : [];
452
+
453
+ if (!controlledSelectedRowIds) {
454
+ setInternalSelectedRowIds(newSelectedIds);
455
+ }
456
+
457
+ if (onSelectionChange) {
458
+ const selectedRowsData = sortedData.filter(row => newSelectedIds.includes(getRowId(row, rowKey)));
459
+ onSelectionChange(selectedRowsData, newSelectedIds);
460
+ }
461
+ },
462
+ [selectionMode, paginatedData, sortedData, controlledSelectedRowIds, onSelectionChange, rowKey]
463
+ );
464
+
465
+ // Check if all rows are selected
466
+ const isAllSelected = useMemo(() => {
467
+ if (selectionMode !== 'multiple' || paginatedData.length === 0) return false;
468
+ return paginatedData.every(row => selectedRowIds.includes(getRowId(row, rowKey)));
469
+ }, [selectionMode, paginatedData, selectedRowIds, rowKey]);
470
+
471
+ // Check if some rows are selected (indeterminate)
472
+ const isIndeterminate = useMemo(() => {
473
+ if (selectionMode !== 'multiple' || paginatedData.length === 0) return false;
474
+ const selectedCount = paginatedData.filter(row => selectedRowIds.includes(getRowId(row, rowKey))).length;
475
+ return selectedCount > 0 && selectedCount < paginatedData.length;
476
+ }, [selectionMode, paginatedData, selectedRowIds, rowKey]);
477
+
478
+ // Handle column visibility toggle
479
+ const handleColumnVisibilityToggle = useCallback(
480
+ (columnKey: string) => {
481
+ setColumnVisibility(prev => {
482
+ const updated = { ...prev, [columnKey]: !prev[columnKey] };
483
+ if (onColumnVisibilityChange) {
484
+ const visibleKeys = Object.entries(updated)
485
+ .filter(([, visible]) => visible)
486
+ .map(([key]) => key);
487
+ onColumnVisibilityChange(visibleKeys);
488
+ }
489
+ return updated;
490
+ });
491
+ },
492
+ [onColumnVisibilityChange]
493
+ );
494
+
495
+ // Handle column reorder
496
+ const handleColumnReorder = useCallback(
497
+ (fromIndex: number, toIndex: number) => {
498
+ const newOrder = [...columnOrder];
499
+ const [removed] = newOrder.splice(fromIndex, 1);
500
+ if (removed) {
501
+ newOrder.splice(toIndex, 0, removed);
502
+ setColumnOrder(newOrder);
503
+
504
+ if (onColumnReorder) {
505
+ onColumnReorder(newOrder);
506
+ }
507
+ }
508
+ },
509
+ [columnOrder, onColumnReorder]
510
+ );
511
+
185
512
  // Reset to first page when data changes
186
513
  useEffect(() => {
187
514
  setCurrentPage(1);
@@ -189,7 +516,7 @@ export function useDataTable({
189
516
 
190
517
  // Reset current page if it's out of bounds
191
518
  useEffect(() => {
192
- if (currentPage > totalPages) {
519
+ if (currentPage > totalPages && totalPages > 0) {
193
520
  setCurrentPage(Math.max(1, totalPages));
194
521
  }
195
522
  }, [currentPage, totalPages]);
@@ -202,6 +529,19 @@ export function useDataTable({
202
529
  handleSort,
203
530
  handlePageChange,
204
531
  handleSearch,
532
+ selectedRowIds,
533
+ selectedRows,
534
+ handleRowSelect,
535
+ handleSelectAll,
536
+ isAllSelected,
537
+ isIndeterminate,
538
+ columnOrder,
539
+ visibleColumns,
540
+ columnVisibility,
541
+ handleColumnVisibilityToggle,
542
+ columnFilterValues,
543
+ handleColumnFilterChange,
544
+ clearColumnFilters,
205
545
  };
206
546
  }
207
547
 
@@ -248,6 +248,22 @@ export function useDatePicker({
248
248
  [currentMonth]
249
249
  );
250
250
 
251
+ // Decade navigation handlers
252
+ const handlePrevDecade = useCallback(() => {
253
+ // Move back 12 years (since generateYears shows 12 years)
254
+ setViewDate(new Date(currentYear - 12, currentMonth, 1));
255
+ }, [currentYear, currentMonth]);
256
+
257
+ const handleNextDecade = useCallback(() => {
258
+ // Move forward 12 years (since generateYears shows 12 years)
259
+ setViewDate(new Date(currentYear + 12, currentMonth, 1));
260
+ }, [currentYear, currentMonth]);
261
+
262
+ // Switch from year view back to day view
263
+ const switchToDayView = useCallback(() => {
264
+ setViewMode('days');
265
+ }, []);
266
+
251
267
  // Handle today button click
252
268
  const handleTodayClick = useCallback(() => {
253
269
  const todayDate = new Date();
@@ -546,8 +562,11 @@ export function useDatePicker({
546
562
  // View mode handlers
547
563
  switchToMonthView,
548
564
  switchToYearView,
565
+ switchToDayView,
549
566
  selectMonth,
550
567
  selectYear,
568
+ handlePrevDecade,
569
+ handleNextDecade,
551
570
 
552
571
  // Data generators
553
572
  generateDays,
@@ -46,7 +46,7 @@ export function loadAtomixConfig(
46
46
  // In browser environments, config loading is not supported
47
47
  if (typeof window !== 'undefined') {
48
48
  if (required) {
49
- throw new Error('Config loading not supported in browser environment');
49
+ throw new Error('loadAtomixConfig: Not available in browser environment. Config loading requires Node.js/SSR environment.');
50
50
  }
51
51
  return defaultConfig;
52
52
  }
@@ -144,5 +144,4 @@ export function resolveConfigPath(): string | null {
144
144
  return null;
145
145
  }
146
146
 
147
- export default loadAtomixConfig;
148
-
147
+ export default loadAtomixConfig;
@@ -32,6 +32,13 @@ export const CLASS_PREFIX = {
32
32
  /**
33
33
  * Button-specific constants
34
34
  */
35
+ export const THEME_NAMING = {
36
+ BUTTON_PREFIX: 'btn',
37
+ ICON_ELEMENT: 'icon',
38
+ LABEL_ELEMENT: 'label',
39
+ SPINNER_ELEMENT: 'spinner',
40
+ };
41
+
35
42
  export const BUTTON = {
36
43
  BASE_CLASS: 'c-btn',
37
44
  ICON_CLASS: 'c-btn__icon',
@@ -626,21 +633,31 @@ export const DATA_TABLE_CLASSES = {
626
633
  header: 'c-data-table__header',
627
634
  headerCell: 'c-data-table__header-cell',
628
635
  headerContent: 'c-data-table__header-content',
636
+ headerActions: 'c-data-table__header-actions',
629
637
  sortable: 'c-data-table__header-cell--sortable',
630
638
  sortIcon: 'c-data-table__sort-icon',
631
639
  row: 'c-data-table__row',
640
+ rowSelected: 'c-data-table__row--selected',
632
641
  cell: 'c-data-table__cell',
642
+ selectionCell: 'c-data-table__cell--selection',
633
643
  loadingCell: 'c-data-table__loading-cell',
634
644
  loadingIndicator: 'c-data-table__loading-indicator',
635
645
  emptyCell: 'c-data-table__empty-cell',
636
646
  toolbar: 'c-data-table-toolbar',
647
+ toolbarLeft: 'c-data-table-toolbar__left',
648
+ toolbarRight: 'c-data-table-toolbar__right',
637
649
  search: 'c-data-table-search',
638
650
  searchInput: 'c-data-table-search__input',
651
+ columnFilter: 'c-data-table__column-filter',
639
652
  pagination: 'c-data-table__pagination-container',
640
653
  striped: 'c-data-table--striped',
641
654
  bordered: 'c-data-table--bordered',
642
655
  dense: 'c-data-table--dense',
643
656
  loading: 'c-data-table--loading',
657
+ stickyHeader: 'c-data-table--sticky-header',
658
+ dragging: 'c-data-table__header-cell--dragging',
659
+ dragOver: 'c-data-table__header-cell--drag-over',
660
+ resizeHandle: 'c-data-table__resize-handle',
644
661
  open: 'is-open',
645
662
  };
646
663
 
@@ -77,7 +77,7 @@ export interface UsePerformanceMonitorOptions {
77
77
  export function usePerformanceMonitor(options: UsePerformanceMonitorOptions) {
78
78
  const {
79
79
  componentName,
80
- logToConsole = process.env.NODE_ENV === 'development',
80
+ logToConsole = (typeof process === 'undefined' || process.env?.NODE_ENV === 'development'),
81
81
  warnThreshold = 16,
82
82
  onMetrics,
83
83
  } = options;
@@ -164,16 +164,21 @@ export function mapSCSSTokensToCSSVars(
164
164
  /**
165
165
  * Apply CSS variables to an element
166
166
  *
167
- * @param element - Target element (defaults to document.documentElement)
168
167
  * @param vars - CSS variables to apply
168
+ * @param element - Target element (defaults to document.documentElement)
169
169
  */
170
170
  export function applyCSSVariables(
171
- vars: Record<string, string | number>,
172
- element: HTMLElement = document.documentElement
171
+ vars: Record<string, string | number>,
172
+ element?: HTMLElement
173
173
  ): void {
174
- Object.entries(vars).forEach(([key, value]) => {
175
- element.style.setProperty(key, String(value));
176
- });
174
+ if (typeof window === 'undefined') {
175
+ return; // SSR safety
176
+ }
177
+
178
+ const target = element || document.documentElement;
179
+ Object.entries(vars).forEach(([key, value]) => {
180
+ target.style.setProperty(key, String(value));
181
+ });
177
182
  }
178
183
 
179
184
  /**
@@ -183,12 +188,17 @@ export function applyCSSVariables(
183
188
  * @param element - Target element (defaults to document.documentElement)
184
189
  */
185
190
  export function removeCSSVariables(
186
- varNames: string[],
187
- element: HTMLElement = document.documentElement
191
+ varNames: string[],
192
+ element?: HTMLElement
188
193
  ): void {
189
- varNames.forEach((varName) => {
190
- element.style.removeProperty(varName);
191
- });
194
+ if (typeof window === 'undefined') {
195
+ return; // SSR safety
196
+ }
197
+
198
+ const target = element || document.documentElement;
199
+ varNames.forEach((varName) => {
200
+ target.style.removeProperty(varName);
201
+ });
192
202
  }
193
203
 
194
204
  /**
@@ -199,10 +209,15 @@ export function removeCSSVariables(
199
209
  * @returns Variable value or null if not found
200
210
  */
201
211
  export function getCSSVariable(
202
- varName: string,
203
- element: HTMLElement = document.documentElement
212
+ varName: string,
213
+ element?: HTMLElement
204
214
  ): string | null {
205
- return getComputedStyle(element).getPropertyValue(varName).trim() || null;
215
+ if (typeof window === 'undefined') {
216
+ return null; // SSR safety
217
+ }
218
+
219
+ const target = element || document.documentElement;
220
+ return getComputedStyle(target).getPropertyValue(varName).trim() || null;
206
221
  }
207
222
 
208
223
  /**
@@ -1,14 +1,11 @@
1
1
  /**
2
2
  * Theme Adapters
3
3
  *
4
- * Adapters for converting between Theme objects and DesignTokens
4
+ * Adapters for working with DesignTokens and CSS variables
5
5
  */
6
6
 
7
7
  export {
8
- themeToDesignTokens,
9
8
  designTokensToCSSVars,
10
- createDesignTokensFromTheme,
11
- designTokensToTheme,
12
9
  } from './themeAdapter';
13
10
 
14
11
  export {