@rakeyshgidwani/roger-ui-bank-theme-stan-design 0.1.3 → 0.1.5

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 (164) hide show
  1. package/CHANGELOG.md +1 -1
  2. package/dist/index.d.ts +131 -131
  3. package/dist/index.esm.js +148 -148
  4. package/dist/index.js +148 -148
  5. package/dist/styles.css +1 -1
  6. package/package.json +1 -1
  7. package/src/components/ui/accessibility-demo.tsx +271 -0
  8. package/src/components/ui/advanced-component-architecture-demo.tsx +916 -0
  9. package/src/components/ui/advanced-transition-system-demo.tsx +670 -0
  10. package/src/components/ui/advanced-transition-system.tsx +395 -0
  11. package/src/components/ui/animation/animated-container.tsx +166 -0
  12. package/src/components/ui/animation/index.ts +19 -0
  13. package/src/components/ui/animation/staggered-container.tsx +68 -0
  14. package/src/components/ui/animation-demo.tsx +250 -0
  15. package/src/components/ui/badge.tsx +33 -0
  16. package/src/components/ui/battery-conscious-animation-demo.tsx +568 -0
  17. package/src/components/ui/border-radius-shadow-demo.tsx +187 -0
  18. package/src/components/ui/button.tsx +36 -0
  19. package/src/components/ui/card.tsx +207 -0
  20. package/src/components/ui/checkbox.tsx +30 -0
  21. package/src/components/ui/color-preview.tsx +411 -0
  22. package/src/components/ui/data-display/chart.tsx +653 -0
  23. package/src/components/ui/data-display/data-grid-simple.tsx +76 -0
  24. package/src/components/ui/data-display/data-grid.tsx +680 -0
  25. package/src/components/ui/data-display/list.tsx +456 -0
  26. package/src/components/ui/data-display/table.tsx +482 -0
  27. package/src/components/ui/data-display/timeline.tsx +441 -0
  28. package/src/components/ui/data-display/tree.tsx +602 -0
  29. package/src/components/ui/data-display/types.ts +536 -0
  30. package/src/components/ui/enterprise-mobile-experience-demo.tsx +749 -0
  31. package/src/components/ui/enterprise-mobile-experience.tsx +464 -0
  32. package/src/components/ui/feedback/alert.tsx +157 -0
  33. package/src/components/ui/feedback/progress.tsx +292 -0
  34. package/src/components/ui/feedback/skeleton.tsx +185 -0
  35. package/src/components/ui/feedback/toast.tsx +280 -0
  36. package/src/components/ui/feedback/types.ts +125 -0
  37. package/src/components/ui/font-preview.tsx +288 -0
  38. package/src/components/ui/form-demo.tsx +553 -0
  39. package/src/components/ui/hardware-acceleration-demo.tsx +547 -0
  40. package/src/components/ui/input.tsx +35 -0
  41. package/src/components/ui/label.tsx +16 -0
  42. package/src/components/ui/layout-demo.tsx +367 -0
  43. package/src/components/ui/layouts/adaptive-layout.tsx +139 -0
  44. package/src/components/ui/layouts/desktop-layout.tsx +224 -0
  45. package/src/components/ui/layouts/index.ts +10 -0
  46. package/src/components/ui/layouts/mobile-layout.tsx +162 -0
  47. package/src/components/ui/layouts/tablet-layout.tsx +197 -0
  48. package/src/components/ui/mobile-form-validation.tsx +451 -0
  49. package/src/components/ui/mobile-input-demo.tsx +201 -0
  50. package/src/components/ui/mobile-input.tsx +281 -0
  51. package/src/components/ui/mobile-skeleton-loading-demo.tsx +638 -0
  52. package/src/components/ui/navigation/breadcrumb.tsx +158 -0
  53. package/src/components/ui/navigation/index.ts +36 -0
  54. package/src/components/ui/navigation/menu.tsx +374 -0
  55. package/src/components/ui/navigation/navigation-demo.tsx +324 -0
  56. package/src/components/ui/navigation/pagination.tsx +272 -0
  57. package/src/components/ui/navigation/sidebar.tsx +383 -0
  58. package/src/components/ui/navigation/stepper.tsx +303 -0
  59. package/src/components/ui/navigation/tabs.tsx +205 -0
  60. package/src/components/ui/navigation/types.ts +299 -0
  61. package/src/components/ui/overlay/backdrop.tsx +81 -0
  62. package/src/components/ui/overlay/focus-manager.tsx +143 -0
  63. package/src/components/ui/overlay/index.ts +36 -0
  64. package/src/components/ui/overlay/modal.tsx +270 -0
  65. package/src/components/ui/overlay/overlay-manager.tsx +110 -0
  66. package/src/components/ui/overlay/popover.tsx +462 -0
  67. package/src/components/ui/overlay/portal.tsx +79 -0
  68. package/src/components/ui/overlay/tooltip.tsx +303 -0
  69. package/src/components/ui/overlay/types.ts +196 -0
  70. package/src/components/ui/performance-demo.tsx +596 -0
  71. package/src/components/ui/semantic-input-system-demo.tsx +502 -0
  72. package/src/components/ui/semantic-input-system-demo.tsx.disabled +873 -0
  73. package/src/components/ui/tablet-layout.tsx +192 -0
  74. package/src/components/ui/theme-customizer.tsx +386 -0
  75. package/src/components/ui/theme-preview.tsx +310 -0
  76. package/src/components/ui/theme-switcher.tsx +264 -0
  77. package/src/components/ui/theme-toggle.tsx +38 -0
  78. package/src/components/ui/token-demo.tsx +195 -0
  79. package/src/components/ui/touch-demo.tsx +462 -0
  80. package/src/components/ui/touch-friendly-interface-demo.tsx +519 -0
  81. package/src/components/ui/touch-friendly-interface.tsx +296 -0
  82. package/src/hooks/index.ts +190 -0
  83. package/src/hooks/use-accessibility-support.ts +518 -0
  84. package/src/hooks/use-adaptive-layout.ts +289 -0
  85. package/src/hooks/use-advanced-patterns.ts +294 -0
  86. package/src/hooks/use-advanced-transition-system.ts +393 -0
  87. package/src/hooks/use-animation-profile.ts +288 -0
  88. package/src/hooks/use-battery-animations.ts +384 -0
  89. package/src/hooks/use-battery-conscious-loading.ts +475 -0
  90. package/src/hooks/use-battery-optimization.ts +330 -0
  91. package/src/hooks/use-battery-status.ts +299 -0
  92. package/src/hooks/use-component-performance.ts +344 -0
  93. package/src/hooks/use-device-loading-states.ts +459 -0
  94. package/src/hooks/use-device.tsx +110 -0
  95. package/src/hooks/use-enterprise-mobile-experience.ts +488 -0
  96. package/src/hooks/use-form-feedback.ts +403 -0
  97. package/src/hooks/use-form-performance.ts +513 -0
  98. package/src/hooks/use-frame-rate.ts +251 -0
  99. package/src/hooks/use-gestures.ts +338 -0
  100. package/src/hooks/use-hardware-acceleration.ts +341 -0
  101. package/src/hooks/use-input-accessibility.ts +455 -0
  102. package/src/hooks/use-input-performance.ts +506 -0
  103. package/src/hooks/use-layout-performance.ts +319 -0
  104. package/src/hooks/use-loading-accessibility.ts +535 -0
  105. package/src/hooks/use-loading-performance.ts +473 -0
  106. package/src/hooks/use-memory-usage.ts +287 -0
  107. package/src/hooks/use-mobile-form-layout.ts +464 -0
  108. package/src/hooks/use-mobile-form-validation.ts +518 -0
  109. package/src/hooks/use-mobile-keyboard-optimization.ts +472 -0
  110. package/src/hooks/use-mobile-layout.ts +302 -0
  111. package/src/hooks/use-mobile-optimization.ts +406 -0
  112. package/src/hooks/use-mobile-skeleton.ts +402 -0
  113. package/src/hooks/use-mobile-touch.ts +414 -0
  114. package/src/hooks/use-performance-throttling.ts +348 -0
  115. package/src/hooks/use-performance.ts +316 -0
  116. package/src/hooks/use-reusable-architecture.ts +414 -0
  117. package/src/hooks/use-semantic-input-types.ts +357 -0
  118. package/src/hooks/use-semantic-input.ts +565 -0
  119. package/src/hooks/use-tablet-layout.ts +384 -0
  120. package/src/hooks/use-touch-friendly-input.ts +524 -0
  121. package/src/hooks/use-touch-friendly-interface.ts +331 -0
  122. package/src/hooks/use-touch-optimization.ts +375 -0
  123. package/src/index.ts +279 -279
  124. package/src/lib/utils.ts +6 -0
  125. package/src/themes/README.md +272 -0
  126. package/src/themes/ThemeContext.tsx +31 -0
  127. package/src/themes/ThemeProvider.tsx +232 -0
  128. package/src/themes/accessibility/index.ts +27 -0
  129. package/src/themes/accessibility.ts +259 -0
  130. package/src/themes/aria-patterns.ts +420 -0
  131. package/src/themes/base-themes.ts +55 -0
  132. package/src/themes/colorManager.ts +380 -0
  133. package/src/themes/examples/dark-theme.ts +154 -0
  134. package/src/themes/examples/minimal-theme.ts +108 -0
  135. package/src/themes/focus-management.ts +701 -0
  136. package/src/themes/fontLoader.ts +201 -0
  137. package/src/themes/high-contrast.ts +621 -0
  138. package/src/themes/index.ts +19 -0
  139. package/src/themes/inheritance.ts +227 -0
  140. package/src/themes/keyboard-navigation.ts +550 -0
  141. package/src/themes/motion-reduction.ts +662 -0
  142. package/src/themes/navigation.ts +238 -0
  143. package/src/themes/screen-reader.ts +645 -0
  144. package/src/themes/systemThemeDetector.ts +182 -0
  145. package/src/themes/themeCSSUpdater.ts +262 -0
  146. package/src/themes/themePersistence.ts +238 -0
  147. package/src/themes/themes/default.ts +586 -0
  148. package/src/themes/themes/harvey.ts +554 -0
  149. package/src/themes/themes/stan-design.ts +683 -0
  150. package/src/themes/types.ts +460 -0
  151. package/src/themes/useSystemTheme.ts +48 -0
  152. package/src/themes/useTheme.ts +87 -0
  153. package/src/themes/validation.ts +462 -0
  154. package/src/tokens/index.ts +34 -0
  155. package/src/tokens/tokenExporter.ts +397 -0
  156. package/src/tokens/tokenGenerator.ts +276 -0
  157. package/src/tokens/tokenManager.ts +248 -0
  158. package/src/tokens/tokenValidator.ts +543 -0
  159. package/src/tokens/types.ts +78 -0
  160. package/src/utils/bundle-analyzer.ts +260 -0
  161. package/src/utils/bundle-splitting.ts +483 -0
  162. package/src/utils/lazy-loading.ts +441 -0
  163. package/src/utils/performance-monitor.ts +513 -0
  164. package/src/utils/tree-shaking.ts +274 -0
@@ -0,0 +1,680 @@
1
+ import React, { useState, useCallback, useMemo, useRef, useEffect } from 'react';
2
+ import {
3
+ DataGridProps,
4
+ DataGridColumnProps,
5
+ SortableColumn,
6
+ FilterConfig
7
+ } from './types';
8
+
9
+ // Simple icon components
10
+ const ChevronUpIcon: React.FC<{ className?: string }> = ({ className = '' }) => (
11
+ <svg className={`data-grid__icon ${className}`} fill="none" viewBox="0 0 24 24" stroke="currentColor">
12
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 15l7-7 7 7" />
13
+ </svg>
14
+ );
15
+
16
+ const ChevronDownIcon: React.FC<{ className?: string }> = ({ className = '' }) => (
17
+ <svg className={`data-grid__icon ${className}`} fill="none" viewBox="0 0 24 24" stroke="currentColor">
18
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
19
+ </svg>
20
+ );
21
+
22
+ const SearchIcon: React.FC<{ className?: string }> = ({ className = '' }) => (
23
+ <svg className={`data-grid__icon ${className}`} fill="none" viewBox="0 0 24 24" stroke="currentColor">
24
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
25
+ </svg>
26
+ );
27
+
28
+ const FilterIcon: React.FC<{ className?: string }> = ({ className = '' }) => (
29
+ <svg className={`data-grid__icon ${className}`} fill="none" viewBox="0 0 24 24" stroke="currentColor">
30
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.207A1 1 0 013 6.5V4z" />
31
+ </svg>
32
+ );
33
+
34
+ const MenuIcon: React.FC<{ className?: string }> = ({ className = '' }) => (
35
+ <svg className={`data-grid__icon ${className}`} fill="none" viewBox="0 0 24 24" stroke="currentColor">
36
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
37
+ </svg>
38
+ );
39
+
40
+ // const DragIcon: React.FC<{ className?: string }> = ({ className = '' }) => (
41
+ // <svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor">
42
+ // <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 9l3 3-3 3m5 0h3M8 9l3 3-3 3m5 0h3" />
43
+ // </svg>
44
+ // ); // TODO: Implement drag functionality
45
+
46
+ const GroupIcon: React.FC<{ className?: string }> = ({ className = '' }) => (
47
+ <svg className={`data-grid__icon ${className}`} fill="none" viewBox="0 0 24 24" stroke="currentColor">
48
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z" />
49
+ </svg>
50
+ );
51
+
52
+ // Resizable Column Header Component
53
+ const DataGridColumn: React.FC<DataGridColumnProps> = ({
54
+ column,
55
+ // index, // TODO: Use for column ordering
56
+ onSort,
57
+ onResize,
58
+ sortConfig,
59
+ // theme,
60
+ size,
61
+ resizable
62
+ }) => {
63
+ // const { getTheme } = useTheme();
64
+ // const themeConfig = getTheme(theme);
65
+ // const colors = themeConfig?.colors || getDefaultColors();
66
+
67
+ const [isResizing, setIsResizing] = useState(false);
68
+ const [startX, setStartX] = useState(0);
69
+ const [startWidth, setStartWidth] = useState(0);
70
+ const headerRef = useRef<HTMLTableHeaderCellElement>(null);
71
+
72
+ const handleMouseDown = useCallback((e: React.MouseEvent) => {
73
+ if (!resizable) return;
74
+
75
+ e.preventDefault();
76
+ setIsResizing(true);
77
+ setStartX(e.clientX);
78
+ setStartWidth(headerRef.current?.offsetWidth || 0);
79
+ }, [resizable]);
80
+
81
+ const handleMouseMove = useCallback((e: MouseEvent) => {
82
+ if (!isResizing) return;
83
+
84
+ const diff = e.clientX - startX;
85
+ const newWidth = Math.max(50, startWidth + diff); // Minimum width of 50px
86
+ onResize(newWidth);
87
+ }, [isResizing, startX, startWidth, onResize]);
88
+
89
+ const handleMouseUp = useCallback(() => {
90
+ setIsResizing(false);
91
+ }, []);
92
+
93
+ useEffect(() => {
94
+ if (isResizing) {
95
+ document.addEventListener('mousemove', handleMouseMove);
96
+ document.addEventListener('mouseup', handleMouseUp);
97
+
98
+ return () => {
99
+ document.removeEventListener('mousemove', handleMouseMove);
100
+ document.removeEventListener('mouseup', handleMouseUp);
101
+ };
102
+ }
103
+ }, [isResizing, handleMouseMove, handleMouseUp]);
104
+
105
+
106
+ const isSorted = sortConfig?.key === column.key;
107
+
108
+ return (
109
+ <th
110
+ ref={headerRef}
111
+ className={`data-grid__header data-grid__header--${size} data-grid__header--${column.align || 'left'} ${isSorted ? 'data-grid__header--sorted' : ''}`}
112
+ style={{
113
+ width: column.width,
114
+ }}
115
+ onClick={onSort}
116
+ >
117
+ <div className={`data-grid__header-content data-grid__header-content--${column.align || 'left'}`}>
118
+ <span>{column.label}</span>
119
+ {column.sortable && isSorted && (
120
+ sortConfig?.direction === 'asc' ? (
121
+ <ChevronUpIcon className="data-grid__sort-icon" />
122
+ ) : (
123
+ <ChevronDownIcon className="data-grid__sort-icon" />
124
+ )
125
+ )}
126
+ </div>
127
+
128
+ {resizable && (
129
+ <div
130
+ className={`data-grid__resize-handle ${isResizing ? 'data-grid__resize-handle--active' : ''}`}
131
+ onMouseDown={handleMouseDown}
132
+ />
133
+ )}
134
+ </th>
135
+ );
136
+ };
137
+
138
+ // Column Management Panel
139
+ const ColumnPanel: React.FC<{
140
+ columns: SortableColumn[];
141
+ groups?: string[];
142
+ onGroupChange?: (groups: string[]) => void;
143
+ onColumnToggle?: (columnKey: string, visible: boolean) => void;
144
+ theme: 'stan-design' | 'enterprise' | 'harvey';
145
+ size: 'sm' | 'md' | 'lg';
146
+ onClose: () => void;
147
+ }> = ({ columns, groups = [], onGroupChange, onColumnToggle, size, onClose }) => {
148
+ // const { getTheme } = useTheme();
149
+ // const themeConfig = getTheme(theme);
150
+ // const colors = themeConfig?.colors || getDefaultColors();
151
+
152
+ const handleGroupToggle = (columnKey: string) => {
153
+ if (!onGroupChange) return;
154
+
155
+ const newGroups = groups.includes(columnKey)
156
+ ? groups.filter(g => g !== columnKey)
157
+ : [...groups, columnKey];
158
+
159
+ onGroupChange(newGroups);
160
+ };
161
+
162
+ return (
163
+ <div className="data-grid__column-panel">
164
+ <div className="data-grid__column-panel-header">
165
+ <h3 className={`data-grid__column-panel-title data-grid__column-panel-title--${size}`}>
166
+ Column Settings
167
+ </h3>
168
+ <button
169
+ onClick={onClose}
170
+ className="data-grid__column-panel-close"
171
+ >
172
+ ×
173
+ </button>
174
+ </div>
175
+
176
+ <div className="data-grid__column-panel-section">
177
+ <h4 className={`data-grid__column-panel-subtitle data-grid__column-panel-subtitle--${size}`}>
178
+ Group By
179
+ </h4>
180
+ {columns.map(column => (
181
+ <label
182
+ key={`group-${column.key}`}
183
+ className="data-grid__column-panel-item"
184
+ >
185
+ <input
186
+ type="checkbox"
187
+ checked={groups.includes(column.key)}
188
+ onChange={() => handleGroupToggle(column.key)}
189
+ className="data-grid__column-panel-checkbox"
190
+ />
191
+ <span className={`data-grid__column-panel-label data-grid__column-panel-label--${size}`}>
192
+ {column.label}
193
+ </span>
194
+ </label>
195
+ ))}
196
+ </div>
197
+
198
+ <div className="data-grid__column-panel-section">
199
+ <h4 className={`data-grid__column-panel-subtitle data-grid__column-panel-subtitle--${size}`}>
200
+ Visible Columns
201
+ </h4>
202
+ {columns.map(column => (
203
+ <label
204
+ key={`visible-${column.key}`}
205
+ className="data-grid__column-panel-item"
206
+ >
207
+ <input
208
+ type="checkbox"
209
+ checked={true} // All columns are visible by default for this demo
210
+ onChange={(e) => onColumnToggle?.(column.key, e.target.checked)}
211
+ className="data-grid__column-panel-checkbox"
212
+ />
213
+ <span className={`data-grid__column-panel-label data-grid__column-panel-label--${size}`}>
214
+ {column.label}
215
+ </span>
216
+ </label>
217
+ ))}
218
+ </div>
219
+ </div>
220
+ );
221
+ };
222
+
223
+ // Advanced Search and Filter Bar
224
+ const DataGridToolbar: React.FC<{
225
+ searchable?: boolean;
226
+ searchValue?: string;
227
+ onSearchChange?: (value: string) => void;
228
+ filterable?: boolean;
229
+ filters?: FilterConfig[];
230
+ onFiltersChange?: (filters: FilterConfig[]) => void;
231
+ groupable?: boolean;
232
+ groups?: string[];
233
+ onGroupChange?: (groups: string[]) => void;
234
+ columns: SortableColumn[];
235
+ theme: 'stan-design' | 'enterprise' | 'harvey';
236
+ size: 'sm' | 'md' | 'lg';
237
+ }> = ({
238
+ searchable,
239
+ searchValue,
240
+ onSearchChange,
241
+ filterable,
242
+ filters = [],
243
+ onFiltersChange,
244
+ groupable,
245
+ groups = [],
246
+ onGroupChange,
247
+ columns,
248
+ theme,
249
+ size
250
+ }) => {
251
+ // const { getTheme } = useTheme();
252
+ // const themeConfig = getTheme(theme);
253
+ // const colors = themeConfig?.colors || getDefaultColors();
254
+
255
+ const [showColumnPanel, setShowColumnPanel] = useState(false);
256
+ const [filterColumn, setFilterColumn] = useState('');
257
+ const [filterValue, setFilterValue] = useState('');
258
+
259
+ const addFilter = () => {
260
+ if (!filterColumn || !filterValue || !onFiltersChange) return;
261
+
262
+ const newFilter: FilterConfig = {
263
+ key: filterColumn,
264
+ value: filterValue,
265
+ operator: 'contains'
266
+ };
267
+
268
+ onFiltersChange([...filters, newFilter]);
269
+ setFilterColumn('');
270
+ setFilterValue('');
271
+ };
272
+
273
+ const removeFilter = (index: number) => {
274
+ if (!onFiltersChange) return;
275
+ const newFilters = filters.filter((_, i) => i !== index);
276
+ onFiltersChange(newFilters);
277
+ };
278
+
279
+ return (
280
+ <div className={`data-grid__toolbar data-grid__toolbar--${size}`}>
281
+ {/* Top Row - Search and Actions */}
282
+ <div className="data-grid__toolbar-row">
283
+ {searchable && (
284
+ <div className="data-grid__search-container">
285
+ <div className="data-grid__search-icon">
286
+ <SearchIcon />
287
+ </div>
288
+ <input
289
+ type="text"
290
+ placeholder="Search..."
291
+ value={searchValue || ''}
292
+ onChange={(e) => onSearchChange?.(e.target.value)}
293
+ className={`data-grid__search-input data-grid__search-input--${size}`}
294
+ />
295
+ </div>
296
+ )}
297
+
298
+ <div className="data-grid__toolbar-actions">
299
+ {filterable && (
300
+ <button
301
+ className={`data-grid__toolbar-button data-grid__toolbar-button--${size}`}
302
+ onClick={() => {/* TODO: Open filter dialog */}}
303
+ >
304
+ <FilterIcon className="data-grid__toolbar-icon" />
305
+ <span>Filter ({filters.length})</span>
306
+ </button>
307
+ )}
308
+
309
+ {groupable && (
310
+ <button
311
+ className={`data-grid__toolbar-button data-grid__toolbar-button--${size}`}
312
+ onClick={() => {/* TODO: Open group dialog */}}
313
+ >
314
+ <GroupIcon className="data-grid__toolbar-icon" />
315
+ <span>Group ({groups.length})</span>
316
+ </button>
317
+ )}
318
+
319
+ <div className="data-grid__toolbar-dropdown">
320
+ <button
321
+ className={`data-grid__toolbar-button data-grid__toolbar-button--${size}`}
322
+ onClick={() => setShowColumnPanel(!showColumnPanel)}
323
+ >
324
+ <MenuIcon className="data-grid__toolbar-icon" />
325
+ <span>Columns</span>
326
+ </button>
327
+
328
+ {showColumnPanel && (
329
+ <ColumnPanel
330
+ columns={columns}
331
+ groups={groups}
332
+ onGroupChange={onGroupChange}
333
+ theme={theme}
334
+ size={size}
335
+ onClose={() => setShowColumnPanel(false)}
336
+ />
337
+ )}
338
+ </div>
339
+ </div>
340
+ </div>
341
+
342
+ {/* Active Filters */}
343
+ {filters.length > 0 && (
344
+ <div className="data-grid__active-filters">
345
+ <span className={`data-grid__active-filters-label data-grid__active-filters-label--${size}`}>
346
+ Active Filters:
347
+ </span>
348
+ {filters.map((filter, index) => (
349
+ <div
350
+ key={index}
351
+ className={`data-grid__filter-tag data-grid__filter-tag--${size}`}
352
+ >
353
+ <span>{filter.key}: {String(filter.value)}</span>
354
+ <button
355
+ onClick={() => removeFilter(index)}
356
+ className="data-grid__filter-tag-remove"
357
+ >
358
+ ×
359
+ </button>
360
+ </div>
361
+ ))}
362
+ </div>
363
+ )}
364
+
365
+ {/* Quick Filter Add */}
366
+ {filterable && (
367
+ <div className="data-grid__filter-add">
368
+ <select
369
+ value={filterColumn}
370
+ onChange={(e) => setFilterColumn(e.target.value)}
371
+ className={`data-grid__filter-select data-grid__filter-select--${size}`}
372
+ >
373
+ <option value="">Select column...</option>
374
+ {columns.map(column => (
375
+ <option key={column.key} value={column.key}>
376
+ {column.label}
377
+ </option>
378
+ ))}
379
+ </select>
380
+
381
+ <input
382
+ type="text"
383
+ placeholder="Filter value..."
384
+ value={filterValue}
385
+ onChange={(e) => setFilterValue(e.target.value)}
386
+ className={`data-grid__filter-input data-grid__filter-input--${size}`}
387
+ />
388
+
389
+ <button
390
+ onClick={addFilter}
391
+ disabled={!filterColumn || !filterValue}
392
+ className={`data-grid__filter-add-button data-grid__filter-add-button--${size} ${(!filterColumn || !filterValue) ? 'data-grid__filter-add-button--disabled' : ''}`}
393
+ >
394
+ Add Filter
395
+ </button>
396
+ </div>
397
+ )}
398
+ </div>
399
+ );
400
+ };
401
+
402
+ // Main DataGrid Component
403
+ export const DataGrid: React.FC<DataGridProps> = ({
404
+ columns,
405
+ data,
406
+ sortable = true,
407
+ filterable = true,
408
+ groupable = true,
409
+ resizable = true,
410
+ // reorderable = false, // TODO: Implement column reordering
411
+ onSort,
412
+ onFiltersChange,
413
+ onGroupChange,
414
+ onColumnResize,
415
+ // onColumnReorder, // TODO: Implement column reordering
416
+ sortConfig,
417
+ filters = [],
418
+ groups = [],
419
+ columnWidths = {},
420
+ // columnOrder = [], // TODO: Implement column ordering
421
+ pagination,
422
+ onPageChange,
423
+ // onPageSizeChange, // TODO: Implement page size change
424
+ searchable = true,
425
+ searchValue,
426
+ onSearchChange,
427
+ rowKey = 'id',
428
+ selectable = false,
429
+ onSelectionChange,
430
+ selectedKeys = [],
431
+ loading = false,
432
+ error = null,
433
+ emptyMessage = 'No data available',
434
+ className = '',
435
+ theme = 'stan-design',
436
+ size = 'md',
437
+ variant = 'default'
438
+ }) => {
439
+ // const { getTheme } = useTheme();
440
+ // const themeConfig = getTheme(theme);
441
+ // const colors = themeConfig?.colors || getDefaultColors();
442
+
443
+ // Internal state for selection
444
+ const [internalSelectedKeys, setInternalSelectedKeys] = useState<string[]>(selectedKeys);
445
+
446
+ // Update internal state when props change
447
+ React.useEffect(() => {
448
+ setInternalSelectedKeys(selectedKeys);
449
+ }, [selectedKeys]);
450
+
451
+ // Enhanced columns with widths
452
+ const enhancedColumns = useMemo(() => {
453
+ return columns.map(column => ({
454
+ ...column,
455
+ width: columnWidths[column.key] || column.width || 150
456
+ }));
457
+ }, [columns, columnWidths]);
458
+
459
+ // Get row key function
460
+ const getRowKey = (row: Record<string, any>, index: number): string => {
461
+ if (typeof rowKey === 'function') {
462
+ return rowKey(row);
463
+ }
464
+ return row[rowKey] || `row-${index}`;
465
+ };
466
+
467
+ // Handle column resize
468
+ const handleColumnResize = useCallback((columnKey: string, newWidth: number) => {
469
+ onColumnResize?.(columnKey, newWidth);
470
+ }, [onColumnResize]);
471
+
472
+ // Handle sorting
473
+ const handleSort = useCallback((column: SortableColumn) => {
474
+ if (!sortable || !onSort || !column.sortable) return;
475
+
476
+ const newDirection = sortConfig?.key === column.key && sortConfig.direction === 'asc' ? 'desc' : 'asc';
477
+ onSort({ key: column.key, direction: newDirection });
478
+ }, [sortable, onSort, sortConfig]);
479
+
480
+ // Handle row selection
481
+ const handleRowSelect = useCallback((row: Record<string, any>, checked: boolean) => {
482
+ const key = typeof rowKey === 'function' ? rowKey(row) : row[rowKey] || `row-0`;
483
+ let newSelectedKeys: string[];
484
+
485
+ if (checked) {
486
+ newSelectedKeys = [...internalSelectedKeys, key];
487
+ } else {
488
+ newSelectedKeys = internalSelectedKeys.filter(k => k !== key);
489
+ }
490
+
491
+ setInternalSelectedKeys(newSelectedKeys);
492
+ onSelectionChange?.(newSelectedKeys);
493
+ }, [internalSelectedKeys, onSelectionChange, rowKey]);
494
+
495
+ // Handle select all
496
+ const handleSelectAll = useCallback((checked: boolean) => {
497
+ const newSelectedKeys = checked ? data.map((row, index) => {
498
+ if (typeof rowKey === 'function') {
499
+ return rowKey(row);
500
+ }
501
+ return row[rowKey] || `row-${index}`;
502
+ }) : [];
503
+ setInternalSelectedKeys(newSelectedKeys);
504
+ onSelectionChange?.(newSelectedKeys);
505
+ }, [data, onSelectionChange, rowKey]);
506
+
507
+ // Check if all rows are selected
508
+ const allSelected = data.length > 0 && internalSelectedKeys.length === data.length;
509
+
510
+
511
+ // Loading state
512
+ if (loading) {
513
+ return (
514
+ <div className={`data-grid__container ${className || ''}`}>
515
+ <div className="data-grid__loading">
516
+ <div className={`data-grid__loading-text data-grid__loading-text--${size}`}>
517
+ Loading...
518
+ </div>
519
+ </div>
520
+ </div>
521
+ );
522
+ }
523
+
524
+ // Error state
525
+ if (error) {
526
+ return (
527
+ <div className={`data-grid__container ${className || ''}`}>
528
+ <div className="data-grid__error">
529
+ <div className={`data-grid__error-text data-grid__error-text--${size}`}>
530
+ Error: {error}
531
+ </div>
532
+ </div>
533
+ </div>
534
+ );
535
+ }
536
+
537
+ // Empty state
538
+ if (data.length === 0) {
539
+ return (
540
+ <div className={`data-grid__container ${className || ''}`}>
541
+ <DataGridToolbar
542
+ searchable={searchable}
543
+ searchValue={searchValue}
544
+ onSearchChange={onSearchChange}
545
+ filterable={filterable}
546
+ filters={filters}
547
+ onFiltersChange={onFiltersChange}
548
+ groupable={groupable}
549
+ groups={groups}
550
+ onGroupChange={onGroupChange}
551
+ columns={enhancedColumns}
552
+ theme={theme}
553
+ size={size}
554
+ />
555
+ <div className="data-grid__empty">
556
+ <div className={`data-grid__empty-text data-grid__empty-text--${size}`}>
557
+ {emptyMessage}
558
+ </div>
559
+ </div>
560
+ </div>
561
+ );
562
+ }
563
+
564
+ return (
565
+ <div className={`data-grid__container ${className || ''}`}>
566
+ <DataGridToolbar
567
+ searchable={searchable}
568
+ searchValue={searchValue}
569
+ onSearchChange={onSearchChange}
570
+ filterable={filterable}
571
+ filters={filters}
572
+ onFiltersChange={onFiltersChange}
573
+ groupable={groupable}
574
+ groups={groups}
575
+ onGroupChange={onGroupChange}
576
+ columns={enhancedColumns}
577
+ theme={theme}
578
+ size={size}
579
+ />
580
+
581
+ <div className="data-grid__table-wrapper">
582
+ <table className={`data-grid__table data-grid__table--${variant} data-grid__table--${size}`}>
583
+ <thead>
584
+ <tr>
585
+ {selectable && (
586
+ <th className={`data-grid__select-header data-grid__select-header--${size}`}>
587
+ <input
588
+ type="checkbox"
589
+ checked={allSelected}
590
+ onChange={(e) => handleSelectAll(e.target.checked)}
591
+ className="data-grid__select-checkbox"
592
+ />
593
+ </th>
594
+ )}
595
+ {enhancedColumns.map((column, index) => (
596
+ <DataGridColumn
597
+ key={column.key}
598
+ column={column}
599
+ index={index}
600
+ onSort={() => handleSort(column)}
601
+ onResize={(width) => handleColumnResize(column.key, width)}
602
+ sortConfig={sortConfig}
603
+ theme={theme}
604
+ size={size}
605
+ resizable={resizable}
606
+ />
607
+ ))}
608
+ </tr>
609
+ </thead>
610
+ <tbody>
611
+ {data.map((row, index) => {
612
+ const key = getRowKey(row, index);
613
+ const isSelected = internalSelectedKeys.includes(key);
614
+
615
+ return (
616
+ <tr
617
+ key={key}
618
+ className={`data-grid__row data-grid__row--${size} ${isSelected ? 'data-grid__row--selected' : ''} ${variant === 'striped' && index % 2 === 1 ? 'data-grid__row--striped' : ''}`}
619
+ >
620
+ {selectable && (
621
+ <td className={`data-grid__select-cell data-grid__select-cell--${size}`}>
622
+ <input
623
+ type="checkbox"
624
+ checked={isSelected}
625
+ onChange={(e) => handleRowSelect(row, e.target.checked)}
626
+ onClick={(e) => e.stopPropagation()}
627
+ className="data-grid__select-checkbox"
628
+ />
629
+ </td>
630
+ )}
631
+ {enhancedColumns.map((column) => (
632
+ <td
633
+ key={column.key}
634
+ className={`data-grid__cell data-grid__cell--${size} data-grid__cell--${column.align || 'left'}`}
635
+ style={{ width: column.width }}
636
+ >
637
+ {String(row[column.key] || '')}
638
+ </td>
639
+ ))}
640
+ </tr>
641
+ );
642
+ })}
643
+ </tbody>
644
+ </table>
645
+ </div>
646
+
647
+ {pagination && (
648
+ <div className={`data-grid__pagination data-grid__pagination--${size}`}>
649
+ <div className={`data-grid__pagination-info data-grid__pagination-info--${size}`}>
650
+ Showing {((pagination.currentPage - 1) * pagination.pageSize) + 1}-{Math.min(pagination.currentPage * pagination.pageSize, pagination.totalItems)} of {pagination.totalItems} items
651
+ </div>
652
+
653
+ <div className="data-grid__pagination-controls">
654
+ <button
655
+ onClick={() => onPageChange?.(pagination.currentPage - 1)}
656
+ disabled={pagination.currentPage === 1}
657
+ className={`data-grid__pagination-button data-grid__pagination-button--${size} ${pagination.currentPage === 1 ? 'data-grid__pagination-button--disabled' : ''}`}
658
+ >
659
+ Previous
660
+ </button>
661
+
662
+ <span className="data-grid__pagination-status">
663
+ Page {pagination.currentPage} of {Math.ceil(pagination.totalItems / pagination.pageSize)}
664
+ </span>
665
+
666
+ <button
667
+ onClick={() => onPageChange?.(pagination.currentPage + 1)}
668
+ disabled={pagination.currentPage >= Math.ceil(pagination.totalItems / pagination.pageSize)}
669
+ className={`data-grid__pagination-button data-grid__pagination-button--${size} ${pagination.currentPage >= Math.ceil(pagination.totalItems / pagination.pageSize) ? 'data-grid__pagination-button--disabled' : ''}`}
670
+ >
671
+ Next
672
+ </button>
673
+ </div>
674
+ </div>
675
+ )}
676
+ </div>
677
+ );
678
+ };
679
+
680
+ export default DataGrid;