@rakeyshgidwani/roger-ui-bank-theme-stan-design 0.1.4 → 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,550 @@
1
+ // Keyboard key constants
2
+ export const KEY_CODES = {
3
+ // Navigation keys
4
+ TAB: 'Tab',
5
+ SHIFT_TAB: 'Shift+Tab',
6
+ ARROW_UP: 'ArrowUp',
7
+ ARROW_DOWN: 'ArrowDown',
8
+ ARROW_LEFT: 'ArrowLeft',
9
+ ARROW_RIGHT: 'ArrowRight',
10
+ HOME: 'Home',
11
+ END: 'End',
12
+ PAGE_UP: 'PageUp',
13
+ PAGE_DOWN: 'PageDown',
14
+
15
+ // Action keys
16
+ ENTER: 'Enter',
17
+ SPACE: ' ',
18
+ ESCAPE: 'Escape',
19
+ BACKSPACE: 'Backspace',
20
+ DELETE: 'Delete',
21
+
22
+ // Modifier keys
23
+ SHIFT: 'Shift',
24
+ CTRL: 'Control',
25
+ ALT: 'Alt',
26
+ META: 'Meta',
27
+
28
+ // Function keys
29
+ F1: 'F1',
30
+ F2: 'F2',
31
+ F3: 'F3',
32
+ F4: 'F4',
33
+ F5: 'F5',
34
+ F6: 'F6',
35
+ F7: 'F7',
36
+ F8: 'F8',
37
+ F9: 'F9',
38
+ F10: 'F10',
39
+ F11: 'F11',
40
+ F12: 'F12'
41
+ } as const;
42
+
43
+ // Keyboard navigation patterns
44
+ export const NAVIGATION_PATTERNS = {
45
+ // List navigation
46
+ LIST: {
47
+ NEXT: [KEY_CODES.ARROW_DOWN, KEY_CODES.ARROW_RIGHT],
48
+ PREVIOUS: [KEY_CODES.ARROW_UP, KEY_CODES.ARROW_LEFT],
49
+ FIRST: [KEY_CODES.HOME],
50
+ LAST: [KEY_CODES.END],
51
+ SELECT: [KEY_CODES.ENTER, KEY_CODES.SPACE]
52
+ },
53
+
54
+ // Grid navigation
55
+ GRID: {
56
+ NEXT_ROW: [KEY_CODES.ARROW_DOWN],
57
+ PREVIOUS_ROW: [KEY_CODES.ARROW_UP],
58
+ NEXT_COLUMN: [KEY_CODES.ARROW_RIGHT],
59
+ PREVIOUS_COLUMN: [KEY_CODES.ARROW_LEFT],
60
+ FIRST_ROW: [KEY_CODES.HOME],
61
+ LAST_ROW: [KEY_CODES.END],
62
+ FIRST_COLUMN: [KEY_CODES.CTRL + '+' + KEY_CODES.HOME],
63
+ LAST_COLUMN: [KEY_CODES.CTRL + '+' + KEY_CODES.END]
64
+ },
65
+
66
+ // Tree navigation
67
+ TREE: {
68
+ EXPAND: [KEY_CODES.ARROW_RIGHT],
69
+ COLLAPSE: [KEY_CODES.ARROW_LEFT],
70
+ NEXT_ITEM: [KEY_CODES.ARROW_DOWN],
71
+ PREVIOUS_ITEM: [KEY_CODES.ARROW_UP],
72
+ FIRST_ITEM: [KEY_CODES.HOME],
73
+ LAST_ITEM: [KEY_CODES.END],
74
+ SELECT: [KEY_CODES.ENTER, KEY_CODES.SPACE]
75
+ },
76
+
77
+ // Tab navigation
78
+ TABS: {
79
+ NEXT_TAB: [KEY_CODES.ARROW_RIGHT],
80
+ PREVIOUS_TAB: [KEY_CODES.ARROW_LEFT],
81
+ FIRST_TAB: [KEY_CODES.HOME],
82
+ LAST_TAB: [KEY_CODES.END],
83
+ SELECT_TAB: [KEY_CODES.ENTER, KEY_CODES.SPACE]
84
+ },
85
+
86
+ // Menu navigation
87
+ MENU: {
88
+ OPEN: [KEY_CODES.ENTER, KEY_CODES.SPACE, KEY_CODES.ARROW_DOWN],
89
+ CLOSE: [KEY_CODES.ESCAPE],
90
+ NEXT_ITEM: [KEY_CODES.ARROW_DOWN],
91
+ PREVIOUS_ITEM: [KEY_CODES.ARROW_UP],
92
+ FIRST_ITEM: [KEY_CODES.HOME],
93
+ LAST_ITEM: [KEY_CODES.END],
94
+ SELECT: [KEY_CODES.ENTER, KEY_CODES.SPACE]
95
+ },
96
+
97
+ // Modal navigation
98
+ MODAL: {
99
+ CLOSE: [KEY_CODES.ESCAPE],
100
+ NEXT_FOCUS: [KEY_CODES.TAB],
101
+ PREVIOUS_FOCUS: [KEY_CODES.SHIFT + '+' + KEY_CODES.TAB]
102
+ }
103
+ } as const;
104
+
105
+ // Keyboard navigation manager
106
+ export class KeyboardNavigationManager {
107
+ private focusableSelectors: string[];
108
+ private currentFocusIndex: number;
109
+ private focusableElements: HTMLElement[];
110
+ private container: HTMLElement | null;
111
+ private onFocusChange: ((element: HTMLElement, index: number) => void) | null;
112
+
113
+ constructor(
114
+ container?: HTMLElement,
115
+ focusableSelectors: string[] = [
116
+ 'button:not([disabled])',
117
+ 'input:not([disabled])',
118
+ 'select:not([disabled])',
119
+ 'textarea:not([disabled])',
120
+ 'a[href]',
121
+ '[tabindex]:not([tabindex="-1"])',
122
+ '[contenteditable="true"]'
123
+ ]
124
+ ) {
125
+ this.container = container || null;
126
+ this.focusableSelectors = focusableSelectors;
127
+ this.currentFocusIndex = -1;
128
+ this.focusableElements = [];
129
+ this.onFocusChange = null;
130
+
131
+ this.updateFocusableElements();
132
+ }
133
+
134
+ // Set container element
135
+ setContainer(container: HTMLElement) {
136
+ this.container = container;
137
+ this.updateFocusableElements();
138
+ }
139
+
140
+ // Update list of focusable elements
141
+ updateFocusableElements() {
142
+ if (!this.container) return;
143
+
144
+ this.focusableElements = Array.from(
145
+ this.container.querySelectorAll<HTMLElement>(this.focusableSelectors.join(','))
146
+ ).filter(element => {
147
+ // Filter out hidden elements
148
+ const style = window.getComputedStyle(element);
149
+ return style.display !== 'none' && style.visibility !== 'hidden';
150
+ });
151
+
152
+ // Reset focus index
153
+ this.currentFocusIndex = -1;
154
+ }
155
+
156
+ // Set focus change callback
157
+ onFocusChangeCallback(callback: (element: HTMLElement, index: number) => void) {
158
+ this.onFocusChange = callback;
159
+ }
160
+
161
+ // Get current focusable elements
162
+ getFocusableElements(): HTMLElement[] {
163
+ return [...this.focusableElements];
164
+ }
165
+
166
+ // Get current focus index
167
+ getCurrentFocusIndex(): number {
168
+ return this.currentFocusIndex;
169
+ }
170
+
171
+ // Focus first element
172
+ focusFirst(): boolean {
173
+ if (this.focusableElements.length === 0) return false;
174
+
175
+ this.currentFocusIndex = 0;
176
+ const element = this.focusableElements[0];
177
+ element.focus();
178
+
179
+ if (this.onFocusChange) {
180
+ this.onFocusChange(element, 0);
181
+ }
182
+
183
+ return true;
184
+ }
185
+
186
+ // Focus last element
187
+ focusLast(): boolean {
188
+ if (this.focusableElements.length === 0) return false;
189
+
190
+ this.currentFocusIndex = this.focusableElements.length - 1;
191
+ const element = this.focusableElements[this.currentFocusIndex];
192
+ element.focus();
193
+
194
+ if (this.onFocusChange) {
195
+ this.onFocusChange(element, this.currentFocusIndex);
196
+ }
197
+
198
+ return true;
199
+ }
200
+
201
+ // Focus next element
202
+ focusNext(): boolean {
203
+ if (this.focusableElements.length === 0) return false;
204
+
205
+ const nextIndex = (this.currentFocusIndex + 1) % this.focusableElements.length;
206
+ this.currentFocusIndex = nextIndex;
207
+ const element = this.focusableElements[nextIndex];
208
+ element.focus();
209
+
210
+ if (this.onFocusChange) {
211
+ this.onFocusChange(element, nextIndex);
212
+ }
213
+
214
+ return true;
215
+ }
216
+
217
+ // Focus previous element
218
+ focusPrevious(): boolean {
219
+ if (this.focusableElements.length === 0) return false;
220
+
221
+ const prevIndex = this.currentFocusIndex <= 0
222
+ ? this.focusableElements.length - 1
223
+ : this.currentFocusIndex - 1;
224
+ this.currentFocusIndex = prevIndex;
225
+ const element = this.focusableElements[prevIndex];
226
+ element.focus();
227
+
228
+ if (this.onFocusChange) {
229
+ this.onFocusChange(element, prevIndex);
230
+ }
231
+
232
+ return true;
233
+ }
234
+
235
+ // Focus element by index
236
+ focusByIndex(index: number): boolean {
237
+ if (index < 0 || index >= this.focusableElements.length) return false;
238
+
239
+ this.currentFocusIndex = index;
240
+ const element = this.focusableElements[index];
241
+ element.focus();
242
+
243
+ if (this.onFocusChange) {
244
+ this.onFocusChange(element, index);
245
+ }
246
+
247
+ return true;
248
+ }
249
+
250
+ // Focus element by ID
251
+ focusById(id: string): boolean {
252
+ const index = this.focusableElements.findIndex(element => element.id === id);
253
+ if (index === -1) return false;
254
+
255
+ return this.focusByIndex(index);
256
+ }
257
+
258
+ // Focus element by data attribute
259
+ focusByDataAttribute(attribute: string, value: string): boolean {
260
+ const index = this.focusableElements.findIndex(
261
+ element => element.getAttribute(`data-${attribute}`) === value
262
+ );
263
+ if (index === -1) return false;
264
+
265
+ return this.focusByIndex(index);
266
+ }
267
+
268
+ // Handle keyboard navigation
269
+ handleKeyDown(event: KeyboardEvent): boolean {
270
+ const key = event.key;
271
+ const isShift = event.shiftKey;
272
+ const isCtrl = event.ctrlKey;
273
+
274
+ // Handle Tab navigation
275
+ if (key === KEY_CODES.TAB) {
276
+ if (isShift) {
277
+ return this.focusPrevious();
278
+ } else {
279
+ return this.focusNext();
280
+ }
281
+ }
282
+
283
+ // Handle arrow key navigation
284
+ if (key === KEY_CODES.ARROW_DOWN || key === KEY_CODES.ARROW_RIGHT) {
285
+ event.preventDefault();
286
+ return this.focusNext();
287
+ }
288
+
289
+ if (key === KEY_CODES.ARROW_UP || key === KEY_CODES.ARROW_LEFT) {
290
+ event.preventDefault();
291
+ return this.focusPrevious();
292
+ }
293
+
294
+ // Handle Home/End navigation
295
+ if (key === KEY_CODES.HOME) {
296
+ event.preventDefault();
297
+ if (isCtrl) {
298
+ return this.focusFirst();
299
+ }
300
+ return this.focusFirst();
301
+ }
302
+
303
+ if (key === KEY_CODES.END) {
304
+ event.preventDefault();
305
+ if (isCtrl) {
306
+ return this.focusLast();
307
+ }
308
+ return this.focusLast();
309
+ }
310
+
311
+ // Handle Page Up/Down navigation
312
+ if (key === KEY_CODES.PAGE_UP) {
313
+ event.preventDefault();
314
+ // Jump to previous page of elements
315
+ const pageSize = Math.max(1, Math.floor(this.focusableElements.length / 10));
316
+ const targetIndex = Math.max(0, this.currentFocusIndex - pageSize);
317
+ return this.focusByIndex(targetIndex);
318
+ }
319
+
320
+ if (key === KEY_CODES.PAGE_DOWN) {
321
+ event.preventDefault();
322
+ // Jump to next page of elements
323
+ const pageSize = Math.max(1, Math.floor(this.focusableElements.length / 10));
324
+ const targetIndex = Math.min(
325
+ this.focusableElements.length - 1,
326
+ this.currentFocusIndex + pageSize
327
+ );
328
+ return this.focusByIndex(targetIndex);
329
+ }
330
+
331
+ // Handle Enter/Space for selection
332
+ if (key === KEY_CODES.ENTER || key === KEY_CODES.SPACE) {
333
+ if (this.currentFocusIndex >= 0) {
334
+ const element = this.focusableElements[this.currentFocusIndex];
335
+ // Trigger click or submit event
336
+ if (element.tagName === 'BUTTON' || element.tagName === 'A') {
337
+ element.click();
338
+ return true;
339
+ }
340
+ }
341
+ }
342
+
343
+ // Handle Escape for closing/canceling
344
+ if (key === KEY_CODES.ESCAPE) {
345
+ // This could trigger a close action depending on context
346
+ return false;
347
+ }
348
+
349
+ return false;
350
+ }
351
+
352
+ // Trap focus within container (for modals, etc.)
353
+ trapFocus(): void {
354
+ if (!this.container) return;
355
+
356
+ // Focus first element initially
357
+ this.focusFirst();
358
+
359
+ // Add event listener for focus trapping
360
+ const handleFocus = (event: FocusEvent) => {
361
+ const target = event.target as HTMLElement;
362
+
363
+ // If focus is outside the container, move it back
364
+ if (!this.container?.contains(target)) {
365
+ if (this.focusableElements.length > 0) {
366
+ this.focusFirst();
367
+ }
368
+ }
369
+ };
370
+
371
+ this.container.addEventListener('focusin', handleFocus);
372
+
373
+ // Store cleanup function
374
+ (this.container as any)._focusTrapCleanup = () => {
375
+ this.container?.removeEventListener('focusin', handleFocus);
376
+ };
377
+ }
378
+
379
+ // Release focus trap
380
+ releaseFocusTrap(): void {
381
+ if (this.container && (this.container as any)._focusTrapCleanup) {
382
+ (this.container as any)._focusTrapCleanup();
383
+ delete (this.container as any)._focusTrapCleanup;
384
+ }
385
+ }
386
+
387
+ // Restore focus to previously focused element
388
+ restoreFocus(): void {
389
+ if (this.currentFocusIndex >= 0 && this.currentFocusIndex < this.focusableElements.length) {
390
+ this.focusableElements[this.currentFocusIndex].focus();
391
+ }
392
+ }
393
+
394
+ // Get keyboard shortcuts for accessibility
395
+ getKeyboardShortcuts(): Record<string, string> {
396
+ return {
397
+ 'Navigate Next': 'Tab or Arrow Down/Right',
398
+ 'Navigate Previous': 'Shift+Tab or Arrow Up/Left',
399
+ 'Navigate First': 'Home',
400
+ 'Navigate Last': 'End',
401
+ 'Navigate Page Up': 'Page Up',
402
+ 'Navigate Page Down': 'Page Down',
403
+ 'Select/Activate': 'Enter or Space',
404
+ 'Close/Cancel': 'Escape'
405
+ };
406
+ }
407
+
408
+ // Validate keyboard navigation
409
+ validateNavigation(): boolean {
410
+ if (this.focusableElements.length === 0) {
411
+ console.warn('No focusable elements found in container');
412
+ return false;
413
+ }
414
+
415
+ // Check if all focusable elements have proper tabindex
416
+ const invalidElements = this.focusableElements.filter(element => {
417
+ const tabindex = element.getAttribute('tabindex');
418
+ return tabindex === null || tabindex === '';
419
+ });
420
+
421
+ if (invalidElements.length > 0) {
422
+ console.warn('Some focusable elements lack proper tabindex:', invalidElements);
423
+ return false;
424
+ }
425
+
426
+ return true;
427
+ }
428
+ }
429
+
430
+ // Focus management utilities
431
+ export class FocusManager {
432
+ private previousActiveElement: Element | null;
433
+ private focusableSelectors: string[];
434
+
435
+ constructor(focusableSelectors: string[] = [
436
+ 'button:not([disabled])',
437
+ 'input:not([disabled])',
438
+ 'select:not([disabled])',
439
+ 'textarea:not([disabled])',
440
+ 'a[href]',
441
+ '[tabindex]:not([tabindex="-1"])',
442
+ '[contenteditable="true"]'
443
+ ]) {
444
+ this.previousActiveElement = null;
445
+ this.focusableSelectors = focusableSelectors;
446
+ }
447
+
448
+ // Store current active element
449
+ storeActiveElement(): void {
450
+ this.previousActiveElement = document.activeElement;
451
+ }
452
+
453
+ // Restore previously active element
454
+ restoreActiveElement(): boolean {
455
+ if (this.previousActiveElement && this.previousActiveElement instanceof HTMLElement) {
456
+ this.previousActiveElement.focus();
457
+ return true;
458
+ }
459
+ return false;
460
+ }
461
+
462
+ // Get all focusable elements in a container
463
+ getFocusableElements(container: HTMLElement): HTMLElement[] {
464
+ return Array.from(
465
+ container.querySelectorAll<HTMLElement>(this.focusableSelectors.join(','))
466
+ ).filter(element => {
467
+ const style = window.getComputedStyle(element);
468
+ return style.display !== 'none' && style.visibility !== 'hidden';
469
+ });
470
+ }
471
+
472
+ // Focus first focusable element in container
473
+ focusFirst(container: HTMLElement): boolean {
474
+ const focusableElements = this.getFocusableElements(container);
475
+ if (focusableElements.length === 0) return false;
476
+
477
+ focusableElements[0].focus();
478
+ return true;
479
+ }
480
+
481
+ // Focus last focusable element in container
482
+ focusLast(container: HTMLElement): boolean {
483
+ const focusableElements = this.getFocusableElements(container);
484
+ if (focusableElements.length === 0) return false;
485
+
486
+ focusableElements[focusableElements.length - 1].focus();
487
+ return true;
488
+ }
489
+
490
+ // Check if element is focusable
491
+ isFocusable(element: HTMLElement): boolean {
492
+ // Check if element matches focusable selectors
493
+ const isSelectorMatch = this.focusableSelectors.some(selector => {
494
+ try {
495
+ return element.matches(selector);
496
+ } catch {
497
+ return false;
498
+ }
499
+ });
500
+
501
+ if (!isSelectorMatch) return false;
502
+
503
+ // Check if element is visible
504
+ const style = window.getComputedStyle(element);
505
+ if (style.display === 'none' || style.visibility === 'hidden') return false;
506
+
507
+ // Check if element is not disabled
508
+ if (element.hasAttribute('disabled')) return false;
509
+
510
+ return true;
511
+ }
512
+
513
+ // Get next focusable element
514
+ getNextFocusableElement(currentElement: HTMLElement, container: HTMLElement): HTMLElement | null {
515
+ const focusableElements = this.getFocusableElements(container);
516
+ const currentIndex = focusableElements.indexOf(currentElement);
517
+
518
+ if (currentIndex === -1 || currentIndex === focusableElements.length - 1) {
519
+ return focusableElements[0] || null;
520
+ }
521
+
522
+ return focusableElements[currentIndex + 1];
523
+ }
524
+
525
+ // Get previous focusable element
526
+ getPreviousFocusableElement(currentElement: HTMLElement, container: HTMLElement): HTMLElement | null {
527
+ const focusableElements = this.getFocusableElements(container);
528
+ const currentIndex = focusableElements.indexOf(currentElement);
529
+
530
+ if (currentIndex === -1 || currentIndex === 0) {
531
+ return focusableElements[focusableElements.length - 1] || null;
532
+ }
533
+
534
+ return focusableElements[currentIndex - 1];
535
+ }
536
+ }
537
+
538
+ // Export default instances
539
+ export const keyboardNavigationManager = new KeyboardNavigationManager();
540
+ export const focusManager = new FocusManager();
541
+
542
+ // Export default
543
+ export default {
544
+ KeyboardNavigationManager,
545
+ FocusManager,
546
+ KEY_CODES,
547
+ NAVIGATION_PATTERNS,
548
+ keyboardNavigationManager,
549
+ focusManager
550
+ };