@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,143 @@
1
+ import React, { useEffect, useRef, useCallback } from 'react';
2
+
3
+ export interface FocusManagerProps {
4
+ children: React.ReactNode;
5
+ isActive: boolean;
6
+ onFocusChange?: (focused: boolean) => void;
7
+ trapFocus?: boolean;
8
+ restoreFocus?: boolean;
9
+ initialFocus?: React.RefObject<HTMLElement>;
10
+ className?: string;
11
+ }
12
+
13
+ export const FocusManager: React.FC<FocusManagerProps> = ({
14
+ children,
15
+ isActive,
16
+ onFocusChange,
17
+ trapFocus = true,
18
+ restoreFocus = true,
19
+ initialFocus,
20
+ className = ''
21
+ }) => {
22
+ const containerRef = useRef<HTMLDivElement>(null);
23
+ const previousActiveElement = useRef<HTMLElement | null>(null);
24
+ const focusableElements = useRef<HTMLElement[]>([]);
25
+
26
+ // Get all focusable elements within the container
27
+ const getFocusableElements = useCallback(() => {
28
+ if (!containerRef.current) return [];
29
+
30
+ const selector = [
31
+ 'button:not([disabled])',
32
+ 'input:not([disabled])',
33
+ 'select:not([disabled])',
34
+ 'textarea:not([disabled])',
35
+ 'a[href]',
36
+ '[tabindex]:not([tabindex="-1"])',
37
+ '[contenteditable="true"]'
38
+ ].join(', ');
39
+
40
+ return Array.from(containerRef.current.querySelectorAll(selector)) as HTMLElement[];
41
+ }, []);
42
+
43
+ // Store the previously focused element
44
+ useEffect(() => {
45
+ if (isActive && restoreFocus) {
46
+ previousActiveElement.current = document.activeElement as HTMLElement;
47
+ }
48
+ }, [isActive, restoreFocus]);
49
+
50
+ // Focus management when overlay becomes active
51
+ useEffect(() => {
52
+ if (!isActive) return;
53
+
54
+ // Get focusable elements
55
+ focusableElements.current = getFocusableElements();
56
+
57
+ if (focusableElements.current.length === 0) return;
58
+
59
+ // Set initial focus
60
+ if (initialFocus?.current) {
61
+ initialFocus.current.focus();
62
+ } else {
63
+ // Focus the first focusable element
64
+ focusableElements.current[0].focus();
65
+ }
66
+
67
+ onFocusChange?.(true);
68
+ }, [isActive, initialFocus, onFocusChange, getFocusableElements]);
69
+
70
+ // Restore focus when overlay becomes inactive
71
+ useEffect(() => {
72
+ if (!isActive && restoreFocus && previousActiveElement.current) {
73
+ // Use setTimeout to ensure the overlay is fully removed
74
+ setTimeout(() => {
75
+ if (previousActiveElement.current && document.contains(previousActiveElement.current)) {
76
+ previousActiveElement.current.focus();
77
+ }
78
+ }, 0);
79
+
80
+ onFocusChange?.(false);
81
+ }
82
+ }, [isActive, restoreFocus, onFocusChange]);
83
+
84
+ // Handle keyboard navigation for focus trapping
85
+ const handleKeyDown = useCallback((event: KeyboardEvent) => {
86
+ if (!isActive || !trapFocus || focusableElements.current.length === 0) return;
87
+
88
+ const { key, shiftKey } = event;
89
+
90
+ if (key === 'Tab') {
91
+ event.preventDefault();
92
+
93
+ const currentIndex = focusableElements.current.findIndex(
94
+ el => el === document.activeElement
95
+ );
96
+
97
+ let nextIndex: number;
98
+
99
+ if (shiftKey) {
100
+ // Shift + Tab: move backward
101
+ nextIndex = currentIndex <= 0
102
+ ? focusableElements.current.length - 1
103
+ : currentIndex - 1;
104
+ } else {
105
+ // Tab: move forward
106
+ nextIndex = currentIndex >= focusableElements.current.length - 1
107
+ ? 0
108
+ : currentIndex + 1;
109
+ }
110
+
111
+ focusableElements.current[nextIndex]?.focus();
112
+ }
113
+ }, [isActive, trapFocus]);
114
+
115
+ // Add/remove keyboard event listeners
116
+ useEffect(() => {
117
+ if (isActive && trapFocus) {
118
+ document.addEventListener('keydown', handleKeyDown);
119
+ return () => {
120
+ document.removeEventListener('keydown', handleKeyDown);
121
+ };
122
+ }
123
+ }, [isActive, trapFocus, handleKeyDown]);
124
+
125
+ // Update focusable elements when children change
126
+ useEffect(() => {
127
+ if (isActive) {
128
+ focusableElements.current = getFocusableElements();
129
+ }
130
+ }, [isActive, getFocusableElements]);
131
+
132
+ return (
133
+ <div
134
+ ref={containerRef}
135
+ className={`focus-manager ${className}`}
136
+ tabIndex={-1} // Make container focusable for focus management
137
+ >
138
+ {children}
139
+ </div>
140
+ );
141
+ };
142
+
143
+ export default FocusManager;
@@ -0,0 +1,36 @@
1
+ // Overlay Architecture Components
2
+ export { Portal } from './portal';
3
+ export { OverlayProvider, OverlayManager, useOverlayManager } from './overlay-manager';
4
+ export { FocusManager } from './focus-manager';
5
+ export { Backdrop } from './backdrop';
6
+
7
+ // Overlay Components
8
+ export { Modal, ModalHeader, ModalBody, ModalFooter } from './modal';
9
+ export { Tooltip, TooltipContent } from './tooltip';
10
+ export { Popover, PopoverContent, PopoverHeader, PopoverBody, PopoverFooter } from './popover';
11
+
12
+ // Types
13
+ export type {
14
+ PortalProps,
15
+ OverlayManagerProps,
16
+ OverlayState,
17
+ OverlayAction,
18
+ FocusManagerProps,
19
+ BackdropProps
20
+ } from './types';
21
+
22
+ // Re-export types from the main types file
23
+ export type {
24
+ OverlayBaseProps,
25
+ ModalProps,
26
+ ModalHeaderProps,
27
+ ModalBodyProps,
28
+ ModalFooterProps,
29
+ TooltipProps,
30
+ TooltipContentProps,
31
+ PopoverProps,
32
+ PopoverContentProps,
33
+ PopoverHeaderProps,
34
+ PopoverBodyProps,
35
+ PopoverFooterProps
36
+ } from './types';
@@ -0,0 +1,270 @@
1
+ import React, { useEffect, useRef, useId } from 'react';
2
+ import { useTheme } from '../../../themes/useTheme';
3
+ import { Portal } from './portal';
4
+ import { Backdrop } from './backdrop';
5
+ import { FocusManager } from './focus-manager';
6
+ import { useOverlayManager } from './overlay-manager';
7
+ import { ModalProps, ModalHeaderProps, ModalBodyProps, ModalFooterProps } from './types';
8
+
9
+ // SVG Icons
10
+ const XMarkIcon: React.FC<{ className?: string }> = ({ className = 'w-6 h-6' }) => (
11
+ <svg className={className} fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
12
+ <path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
13
+ </svg>
14
+ );
15
+
16
+ // Default color fallbacks
17
+ const getDefaultColors = () => ({
18
+ surface: { background: '#ffffff', surface: '#f9fafb', border: '#e5e7eb', divider: '#e5e7eb' },
19
+ text: { primary: '#111827', secondary: '#6b7280', muted: '#9ca3af', inverse: '#ffffff' },
20
+ interactive: { hover: '#f3f4f6', active: '#e5e7eb', focus: '#3b82f6', disabled: '#d1d5db' },
21
+ primary: { 500: '#3b82f6', 600: '#2563eb' },
22
+ semantic: { success: '#10b981', warning: '#f59e0b', error: '#ef4444', info: '#3b82f6' }
23
+ });
24
+
25
+ // Modal Header Component
26
+ export const ModalHeader: React.FC<ModalHeaderProps> = ({
27
+ title,
28
+ subtitle,
29
+ onClose,
30
+ showCloseButton = true,
31
+ closeButtonLabel = 'Close',
32
+ className = '',
33
+ borderless = false,
34
+ noPadding = false,
35
+ children
36
+ }) => {
37
+ const headerClasses = [
38
+ 'modal__header',
39
+ borderless ? 'modal__header--borderless' : '',
40
+ noPadding ? 'modal__header--no-padding' : '',
41
+ className
42
+ ].filter(Boolean).join(' ');
43
+
44
+ return (
45
+ <div className={headerClasses}>
46
+ <div>
47
+ {title && (
48
+ <h2 className="modal__title">
49
+ {title}
50
+ </h2>
51
+ )}
52
+ {subtitle && (
53
+ <p className="modal__subtitle">
54
+ {subtitle}
55
+ </p>
56
+ )}
57
+ {children}
58
+ </div>
59
+
60
+ {showCloseButton && onClose && (
61
+ <button
62
+ type="button"
63
+ className="modal__close"
64
+ onClick={onClose}
65
+ aria-label={closeButtonLabel}
66
+ >
67
+ <XMarkIcon />
68
+ </button>
69
+ )}
70
+ </div>
71
+ );
72
+ };
73
+
74
+ // Modal Body Component
75
+ export const ModalBody: React.FC<ModalBodyProps> = ({
76
+ children,
77
+ className = '',
78
+ padding = 'md',
79
+ scrollable = true,
80
+ maxHeight = '70vh'
81
+ }) => {
82
+ const bodyClasses = [
83
+ 'modal__body',
84
+ padding === 'none' ? 'modal__body--no-padding' : '',
85
+ padding === 'sm' ? 'modal__body--compact' : '',
86
+ padding === 'lg' ? 'modal__body--spacious' : '',
87
+ scrollable ? 'modal__body--scrollable' : '',
88
+ className
89
+ ].filter(Boolean).join(' ');
90
+
91
+ return (
92
+ <div
93
+ className={bodyClasses}
94
+ style={{ maxHeight: scrollable ? maxHeight : undefined }}
95
+ >
96
+ {children}
97
+ </div>
98
+ );
99
+ };
100
+
101
+ // Modal Footer Component
102
+ export const ModalFooter: React.FC<ModalFooterProps> = ({
103
+ children,
104
+ className = '',
105
+ justify = 'end',
106
+ padding = 'md',
107
+ borderTop = true
108
+ }) => {
109
+ const footerClasses = [
110
+ 'modal__footer',
111
+ !borderTop ? 'modal__footer--borderless' : '',
112
+ padding === 'none' ? 'modal__footer--no-padding' : '',
113
+ justify === 'start' ? 'modal__footer--start' : '',
114
+ justify === 'center' ? 'modal__footer--center' : '',
115
+ justify === 'between' ? 'modal__footer--space-between' : '',
116
+ className
117
+ ].filter(Boolean).join(' ');
118
+
119
+ return (
120
+ <div className={footerClasses}>
121
+ {children}
122
+ </div>
123
+ );
124
+ };
125
+
126
+ // Main Modal Component
127
+ export const Modal: React.FC<ModalProps> = ({
128
+ isOpen,
129
+ onClose,
130
+ title,
131
+ children,
132
+ size = 'md',
133
+ variant = 'default',
134
+ closeOnOverlayClick = true,
135
+ closeOnEscape = true,
136
+ showBackdrop = true,
137
+ backdropBlur = false,
138
+ backdropOpacity = 0.5,
139
+ trapFocus = true,
140
+ restoreFocus = true,
141
+ initialFocus,
142
+ zIndex = 1000,
143
+ animation = 'fade',
144
+ className = '',
145
+ theme = 'stan-design',
146
+ loading = false
147
+ }) => {
148
+ const { getTheme } = useTheme();
149
+ const themeConfig = getTheme(theme);
150
+ const colors = themeConfig?.colors || getDefaultColors();
151
+
152
+ const modalId = useId();
153
+ const modalRef = useRef<HTMLDivElement>(null);
154
+ const { addOverlay, removeOverlay, getTopZIndex } = useOverlayManager();
155
+
156
+ // Modal container classes
157
+ const getContainerClasses = () => {
158
+ const classes = [
159
+ 'modal__container',
160
+ size ? `modal__container--size-${size}` : '',
161
+ variant ? `modal__container--variant-${variant}` : '',
162
+ loading ? 'modal__container--loading' : '',
163
+ className
164
+ ];
165
+
166
+ // Add animation class
167
+ if (animation !== 'none' && isOpen) {
168
+ classes.push(`modal__container--${animation}-enter`);
169
+ }
170
+
171
+ return classes.filter(Boolean).join(' ');
172
+ };
173
+
174
+ // Handle escape key
175
+ useEffect(() => {
176
+ if (!isOpen || !closeOnEscape) return;
177
+
178
+ const handleEscape = (event: KeyboardEvent) => {
179
+ if (event.key === 'Escape') {
180
+ onClose();
181
+ }
182
+ };
183
+
184
+ document.addEventListener('keydown', handleEscape);
185
+ return () => document.removeEventListener('keydown', handleEscape);
186
+ }, [isOpen, closeOnEscape, onClose]);
187
+
188
+ // Manage overlay registration
189
+ useEffect(() => {
190
+ if (isOpen) {
191
+ const currentZIndex = zIndex || getTopZIndex();
192
+ addOverlay(modalId, currentZIndex);
193
+
194
+ return () => {
195
+ removeOverlay(modalId);
196
+ };
197
+ }
198
+ }, [isOpen, modalId, addOverlay, removeOverlay, zIndex, getTopZIndex]);
199
+
200
+ // Handle backdrop click
201
+ const handleBackdropClick = () => {
202
+ if (closeOnOverlayClick) {
203
+ onClose();
204
+ }
205
+ };
206
+
207
+ if (!isOpen) {
208
+ return null;
209
+ }
210
+
211
+ const modalStyle: React.CSSProperties = {
212
+ backgroundColor: colors.surface.background,
213
+ borderColor: colors.surface.border,
214
+ color: colors.text.primary,
215
+ zIndex: zIndex || getTopZIndex()
216
+ };
217
+
218
+ return (
219
+ <Portal>
220
+ {showBackdrop && (
221
+ <Backdrop
222
+ isVisible={isOpen}
223
+ onClick={handleBackdropClick}
224
+ blur={backdropBlur}
225
+ opacity={backdropOpacity}
226
+ zIndex={(zIndex || getTopZIndex()) - 1}
227
+ />
228
+ )}
229
+
230
+ <div className="modal__overlay" style={{ zIndex: zIndex || getTopZIndex() }}>
231
+ <FocusManager
232
+ isActive={isOpen}
233
+ trapFocus={trapFocus}
234
+ restoreFocus={restoreFocus}
235
+ initialFocus={initialFocus}
236
+ >
237
+ <div
238
+ ref={modalRef}
239
+ role="dialog"
240
+ aria-modal="true"
241
+ aria-labelledby={title ? `${modalId}-title` : undefined}
242
+ className={getContainerClasses()}
243
+ style={modalStyle}
244
+ onClick={(e) => e.stopPropagation()}
245
+ >
246
+ {/* Render children or default layout */}
247
+ {React.Children.count(children) > 0 ? (
248
+ children
249
+ ) : (
250
+ <>
251
+ {title && (
252
+ <ModalHeader
253
+ title={title}
254
+ onClose={onClose}
255
+ />
256
+ )}
257
+ <ModalBody>
258
+ {/* Default empty content */}
259
+ <p>Modal content goes here...</p>
260
+ </ModalBody>
261
+ </>
262
+ )}
263
+ </div>
264
+ </FocusManager>
265
+ </div>
266
+ </Portal>
267
+ );
268
+ };
269
+
270
+ export default Modal;
@@ -0,0 +1,110 @@
1
+ import React, { createContext, useContext, useReducer, useCallback, ReactNode } from 'react';
2
+ import { OverlayManagerProps, OverlayState, OverlayAction } from './types';
3
+
4
+ // Overlay context for managing global overlay state
5
+ interface OverlayContextType {
6
+ overlays: OverlayState[];
7
+ addOverlay: (id: string, zIndex: number) => void;
8
+ removeOverlay: (id: string) => void;
9
+ getTopZIndex: () => number;
10
+ isOverlayActive: (id: string) => boolean;
11
+ }
12
+
13
+ const OverlayContext = createContext<OverlayContextType | undefined>(undefined);
14
+
15
+ // Custom hook to use overlay context
16
+ export const useOverlayManager = () => {
17
+ const context = useContext(OverlayContext);
18
+ if (!context) {
19
+ throw new Error('useOverlayManager must be used within an OverlayProvider');
20
+ }
21
+ return context;
22
+ };
23
+
24
+ // Overlay reducer for state management
25
+ const overlayReducer = (state: OverlayState[], action: OverlayAction): OverlayState[] => {
26
+ switch (action.type) {
27
+ case 'ADD_OVERLAY':
28
+ return [...state, { id: action.payload.id, zIndex: action.payload.zIndex || 1000 }];
29
+
30
+ case 'REMOVE_OVERLAY':
31
+ return state.filter(overlay => overlay.id !== action.payload.id);
32
+
33
+ case 'UPDATE_Z_INDEX':
34
+ return state.map(overlay =>
35
+ overlay.id === action.payload.id
36
+ ? { ...overlay, zIndex: action.payload.zIndex || overlay.zIndex }
37
+ : overlay
38
+ );
39
+
40
+ default:
41
+ return state;
42
+ }
43
+ };
44
+
45
+ // Overlay provider component
46
+ export const OverlayProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
47
+ const [overlays, dispatch] = useReducer(overlayReducer, []);
48
+
49
+ const addOverlay = useCallback((id: string, zIndex: number) => {
50
+ dispatch({ type: 'ADD_OVERLAY', payload: { id, zIndex } });
51
+ }, []);
52
+
53
+ const removeOverlay = useCallback((id: string) => {
54
+ dispatch({ type: 'REMOVE_OVERLAY', payload: { id } });
55
+ }, []);
56
+
57
+ const getTopZIndex = useCallback(() => {
58
+ if (overlays.length === 0) return 1000; // Base z-index
59
+ return Math.max(...overlays.map(o => o.zIndex)) + 1;
60
+ }, [overlays]);
61
+
62
+ const isOverlayActive = useCallback((id: string) => {
63
+ return overlays.some(overlay => overlay.id === id);
64
+ }, [overlays]);
65
+
66
+ const value: OverlayContextType = {
67
+ overlays,
68
+ addOverlay,
69
+ removeOverlay,
70
+ getTopZIndex,
71
+ isOverlayActive
72
+ };
73
+
74
+ return (
75
+ <OverlayContext.Provider value={value}>
76
+ {children}
77
+ </OverlayContext.Provider>
78
+ );
79
+ };
80
+
81
+ // Overlay manager component for direct usage
82
+ export const OverlayManager: React.FC<OverlayManagerProps> = ({
83
+ children,
84
+ maxOverlays = 10,
85
+ onOverlayChange
86
+ }) => {
87
+ const { overlays } = useOverlayManager();
88
+
89
+ // Notify parent of overlay changes
90
+ React.useEffect(() => {
91
+ if (onOverlayChange) {
92
+ onOverlayChange(overlays);
93
+ }
94
+ }, [overlays, onOverlayChange]);
95
+
96
+ // Enforce maximum overlay limit
97
+ React.useEffect(() => {
98
+ if (overlays.length > maxOverlays) {
99
+ console.warn(`Maximum overlay limit (${maxOverlays}) exceeded`);
100
+ }
101
+ }, [overlays.length, maxOverlays]);
102
+
103
+ return (
104
+ <div className="overlay-manager">
105
+ {children}
106
+ </div>
107
+ );
108
+ };
109
+
110
+ export default OverlayManager;