@umituz/web-design-system 3.1.21 → 4.0.0

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 (82) hide show
  1. package/README.md +137 -469
  2. package/dist/styles.css +2 -0
  3. package/package.json +30 -31
  4. package/src/__tests__/cn.test.ts +24 -0
  5. package/src/__tests__/components.smoke.test.tsx +78 -0
  6. package/src/__tests__/styles.contract.test.ts +60 -0
  7. package/src/domain/tokens/index.ts +8 -49
  8. package/src/index.ts +8 -10
  9. package/src/infrastructure/constants/component.constants.ts +3 -17
  10. package/src/infrastructure/constants/index.ts +4 -2
  11. package/src/infrastructure/constants/timing.constants.ts +46 -0
  12. package/src/infrastructure/utils/calculation.utils.ts +74 -0
  13. package/src/infrastructure/utils/index.ts +12 -0
  14. package/src/presentation/atoms/IconButton.tsx +55 -0
  15. package/src/presentation/atoms/Progress.tsx +2 -1
  16. package/src/presentation/atoms/Spinner.tsx +1 -1
  17. package/src/presentation/atoms/Switch.tsx +2 -2
  18. package/src/presentation/atoms/Text.tsx +15 -13
  19. package/src/presentation/atoms/Tooltip.tsx +2 -1
  20. package/src/presentation/atoms/index.ts +3 -0
  21. package/src/presentation/hooks/index.ts +0 -3
  22. package/src/presentation/hooks/useClickOutside.ts +14 -10
  23. package/src/presentation/hooks/useClipboard.ts +30 -12
  24. package/src/presentation/hooks/useDebounce.ts +8 -10
  25. package/src/presentation/hooks/useLocalStorage.ts +28 -18
  26. package/src/presentation/hooks/useMediaQuery.ts +2 -1
  27. package/src/presentation/hooks/useScrollLock.ts +40 -12
  28. package/src/presentation/hooks/useToggle.ts +8 -6
  29. package/src/presentation/molecules/ActiveFilterTags.tsx +19 -18
  30. package/src/presentation/molecules/Avatar.tsx +1 -1
  31. package/src/presentation/molecules/CodeBlock.tsx +51 -48
  32. package/src/presentation/molecules/ListItem.tsx +40 -29
  33. package/src/presentation/molecules/Select.tsx +1 -1
  34. package/src/presentation/molecules/Toggle.tsx +4 -4
  35. package/src/presentation/molecules/index.ts +0 -3
  36. package/src/presentation/organisms/Alert.tsx +1 -1
  37. package/src/presentation/organisms/DataTable.tsx +24 -11
  38. package/src/presentation/organisms/Dialog.tsx +1 -1
  39. package/src/presentation/organisms/DropdownMenu.tsx +2 -2
  40. package/src/presentation/organisms/HoverCard.tsx +1 -1
  41. package/src/presentation/organisms/LoadingState.tsx +1 -5
  42. package/src/presentation/organisms/Popover.tsx +1 -1
  43. package/src/presentation/organisms/StatCard.tsx +2 -2
  44. package/src/presentation/organisms/Table.tsx +2 -2
  45. package/src/presentation/organisms/index.ts +0 -60
  46. package/src/styles/compile.css +18 -0
  47. package/src/styles/theme.css +131 -0
  48. package/src/domain/tokens/animation.tokens.ts +0 -87
  49. package/src/domain/tokens/color.tokens.ts +0 -95
  50. package/src/domain/tokens/radius.tokens.ts +0 -27
  51. package/src/domain/tokens/shadow.tokens.ts +0 -25
  52. package/src/domain/tokens/spacing.tokens.ts +0 -65
  53. package/src/domain/tokens/typography.tokens.ts +0 -74
  54. package/src/infrastructure/error/ErrorBoundary.tsx +0 -258
  55. package/src/infrastructure/error/ErrorDisplay.tsx +0 -346
  56. package/src/infrastructure/error/SuspenseWrapper.tsx +0 -133
  57. package/src/infrastructure/error/index.ts +0 -5
  58. package/src/infrastructure/performance/index.ts +0 -92
  59. package/src/infrastructure/performance/useLazyLoading.ts +0 -342
  60. package/src/infrastructure/performance/useMemoryOptimization.ts +0 -293
  61. package/src/infrastructure/performance/usePerformanceMonitor.ts +0 -158
  62. package/src/infrastructure/security/index.ts +0 -10
  63. package/src/infrastructure/security/security-config.ts +0 -171
  64. package/src/infrastructure/security/useFormValidation.ts +0 -216
  65. package/src/infrastructure/security/validation.ts +0 -246
  66. package/src/presentation/hooks/useLanguage.ts +0 -109
  67. package/src/presentation/molecules/Breadcrumb.tsx +0 -54
  68. package/src/presentation/organisms/AlertDialog.tsx +0 -181
  69. package/src/presentation/organisms/Calendar.tsx +0 -79
  70. package/src/presentation/organisms/Comments.tsx +0 -103
  71. package/src/presentation/organisms/ConfirmDialog.tsx +0 -97
  72. package/src/presentation/organisms/FilterBar.tsx +0 -244
  73. package/src/presentation/organisms/FilterSidebar.tsx +0 -172
  74. package/src/presentation/organisms/Footer.tsx +0 -86
  75. package/src/presentation/organisms/FormModal.tsx +0 -126
  76. package/src/presentation/organisms/Grid.tsx +0 -128
  77. package/src/presentation/organisms/ImageLightbox.tsx +0 -252
  78. package/src/presentation/organisms/MainNavbar.tsx +0 -320
  79. package/src/presentation/organisms/Modal.tsx +0 -173
  80. package/src/presentation/organisms/Navbar.tsx +0 -75
  81. package/src/presentation/organisms/NewsletterSignup.tsx +0 -165
  82. package/src/presentation/organisms/QuickActionCard.tsx +0 -93
@@ -1,133 +0,0 @@
1
- import React, { Suspense } from 'react';
2
- import { ErrorBoundary } from './ErrorBoundary';
3
-
4
- export interface SuspenseWrapperProps {
5
- children: React.ReactNode;
6
- fallback?: React.ReactNode;
7
- errorFallback?: React.ReactNode;
8
- loadingText?: string;
9
- variant?: 'default' | 'minimal' | 'card';
10
- className?: string;
11
- style?: React.CSSProperties;
12
- showErrorBoundary?: boolean;
13
- errorBoundaryLevel?: 'page' | 'component' | 'feature';
14
- }
15
-
16
- const DefaultLoadingSpinner: React.FC<{ text?: string; className?: string }> = ({ text, className }) => (
17
- <div className={className} style={{
18
- display: 'flex',
19
- flexDirection: 'column',
20
- alignItems: 'center',
21
- justifyContent: 'center',
22
- gap: '16px',
23
- padding: '32px'
24
- }}>
25
- <div style={{
26
- width: '40px',
27
- height: '40px',
28
- border: '4px solid #e5e7eb',
29
- borderTopColor: '#3b82f6',
30
- borderRadius: '50%',
31
- animation: 'spin 1s linear infinite'
32
- }} />
33
- {text && <p style={{ fontSize: '14px', color: '#666' }}>{text}</p>}
34
- </div>
35
- );
36
-
37
- export const SuspenseWrapper: React.FC<SuspenseWrapperProps> = ({
38
- children,
39
- fallback,
40
- errorFallback,
41
- loadingText = 'Loading...',
42
- variant = 'default',
43
- className = '',
44
- style,
45
- showErrorBoundary = true,
46
- errorBoundaryLevel = 'component'
47
- }) => {
48
- const defaultFallback = (
49
- <DefaultLoadingSpinner
50
- text={variant === 'card' ? loadingText : undefined}
51
- className={className}
52
- />
53
- );
54
-
55
- const suspenseContent = (
56
- <Suspense fallback={fallback || defaultFallback}>
57
- {children}
58
- </Suspense>
59
- );
60
-
61
- const content = showErrorBoundary ? (
62
- <ErrorBoundary
63
- level={errorBoundaryLevel}
64
- fallback={errorFallback}
65
- >
66
- {suspenseContent}
67
- </ErrorBoundary>
68
- ) : suspenseContent;
69
-
70
- // Wrap in div if className or style is provided
71
- if (className || style) {
72
- return <div className={className} style={style}>{content}</div>;
73
- }
74
-
75
- return content;
76
- };
77
-
78
- // Specialized Suspense wrappers
79
- export const PageSuspense: React.FC<{
80
- children: React.ReactNode;
81
- loadingText?: string
82
- }> = ({ children, loadingText = 'Loading page...' }) => (
83
- <SuspenseWrapper
84
- variant="card"
85
- loadingText={loadingText}
86
- errorBoundaryLevel="page"
87
- className="min-h-[400px]"
88
- >
89
- {children}
90
- </SuspenseWrapper>
91
- );
92
-
93
- export const ComponentSuspense: React.FC<{
94
- children: React.ReactNode;
95
- loadingText?: string;
96
- className?: string;
97
- }> = ({ children, loadingText = 'Loading component...', className }) => (
98
- <SuspenseWrapper
99
- variant="default"
100
- loadingText={loadingText}
101
- errorBoundaryLevel="component"
102
- className={className}
103
- >
104
- {children}
105
- </SuspenseWrapper>
106
- );
107
-
108
- export const FeatureSuspense: React.FC<{
109
- children: React.ReactNode;
110
- loadingText?: string
111
- }> = ({ children, loadingText = 'Loading feature...' }) => (
112
- <SuspenseWrapper
113
- variant="card"
114
- loadingText={loadingText}
115
- errorBoundaryLevel="feature"
116
- style={{ padding: '24px' }}
117
- >
118
- {children}
119
- </SuspenseWrapper>
120
- );
121
-
122
- export const InlineSuspense: React.FC<{
123
- children: React.ReactNode
124
- }> = ({ children }) => (
125
- <SuspenseWrapper
126
- variant="minimal"
127
- showErrorBoundary={false}
128
- >
129
- {children}
130
- </SuspenseWrapper>
131
- );
132
-
133
- export default SuspenseWrapper;
@@ -1,5 +0,0 @@
1
- export { ErrorBoundary } from './ErrorBoundary';
2
- export { ErrorDisplay, NetworkError, NotFoundError, PermissionError, ApiError } from './ErrorDisplay';
3
- export type { ErrorDisplayProps } from './ErrorDisplay';
4
- export { SuspenseWrapper, PageSuspense, ComponentSuspense, FeatureSuspense, InlineSuspense } from './SuspenseWrapper';
5
- export type { SuspenseWrapperProps } from './SuspenseWrapper';
@@ -1,92 +0,0 @@
1
- export { usePerformanceMonitor, useRenderPerformance } from './usePerformanceMonitor';
2
- export { useLazyLoading } from './useLazyLoading';
3
- export { useMemoryOptimization, useMemoryLeakDetector } from './useMemoryOptimization';
4
-
5
- export type { PerformanceMetrics, PerformanceConfig } from './usePerformanceMonitor';
6
- export type { MemoryOptimizationConfig, CleanupFunction } from './useMemoryOptimization';
7
-
8
- // Performance monitoring utilities
9
- export const performanceUtils = {
10
- // Measure render performance
11
- measureRender: (componentName: string, fn: () => void) => {
12
- const start = performance.now();
13
- fn();
14
- const end = performance.now();
15
-
16
- if (import.meta.env.DEV) {
17
- const duration = end - start;
18
- if (duration > 16) {
19
- console.warn(`[Performance] ${componentName} took ${duration.toFixed(2)}ms to render`);
20
- }
21
- }
22
-
23
- return end - start;
24
- },
25
-
26
- // Measure async operation
27
- measureAsync: async <T>(componentName: string, operation: string, fn: () => Promise<T>): Promise<T> => {
28
- const start = performance.now();
29
- try {
30
- const result = await fn();
31
- const duration = performance.now() - start;
32
-
33
- if (import.meta.env.DEV) {
34
- console.log(`[Performance] ${componentName} ${operation} completed in ${duration.toFixed(2)}ms`);
35
- }
36
-
37
- return result;
38
- } catch (error) {
39
- const duration = performance.now() - start;
40
- console.error(`[Performance] ${componentName} ${operation} failed after ${duration.toFixed(2)}ms:`, error);
41
- throw error;
42
- }
43
- },
44
-
45
- // Get performance metrics
46
- getMetrics: () => {
47
- if (typeof performance === 'undefined') {
48
- return null;
49
- }
50
-
51
- // performance.memory is not in standard TypeScript lib but available in Chrome-based browsers
52
- interface PerformanceMemory {
53
- usedJSHeapSize: number;
54
- totalJSHeapSize: number;
55
- jsHeapSizeLimit: number;
56
- }
57
-
58
- interface PerformanceWithMemory extends Performance {
59
- memory?: PerformanceMemory;
60
- }
61
-
62
- const mem = (performance as unknown as PerformanceWithMemory).memory;
63
- if (!mem) {
64
- return null;
65
- }
66
-
67
- return {
68
- memory: {
69
- usedJSHeapSize: mem.usedJSHeapSize,
70
- totalJSHeapSize: mem.totalJSHeapSize,
71
- jsHeapSizeLimit: mem.jsHeapSizeLimit,
72
- },
73
- navigation: performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming | undefined,
74
- };
75
- },
76
-
77
- // Log performance summary
78
- logSummary: () => {
79
- const metrics = performanceUtils.getMetrics();
80
- if (!metrics) return;
81
-
82
- if (import.meta.env.DEV && metrics.memory) {
83
- const memoryUsage = ((metrics.memory.usedJSHeapSize / metrics.memory.jsHeapSizeLimit) * 100).toFixed(2);
84
- console.log(`[Performance] Memory usage: ${memoryUsage}%`);
85
- }
86
-
87
- if (metrics.navigation) {
88
- const loadTime = metrics.navigation.loadEventEnd - metrics.navigation.fetchStart;
89
- console.log(`[Performance] Page load time: ${loadTime.toFixed(2)}ms`);
90
- }
91
- },
92
- };
@@ -1,342 +0,0 @@
1
- import React, { useState, useEffect, useRef, useCallback } from 'react';
2
-
3
- export const useIntersectionObserver = (
4
- options: IntersectionObserverInit = {}
5
- ) => {
6
- const [isIntersecting, setIsIntersecting] = useState(false);
7
- const [hasIntersected, setHasIntersected] = useState(false);
8
- const targetRef = useRef<HTMLElement>(null);
9
-
10
- const defaultOptions = React.useMemo(() => ({
11
- threshold: 0.1,
12
- rootMargin: '50px',
13
- ...options,
14
- }), [options]);
15
-
16
- useEffect(() => {
17
- const target = targetRef.current;
18
- if (!target) return;
19
-
20
- const observer = new IntersectionObserver(
21
- ([entry]) => {
22
- const isVisible = entry.isIntersecting;
23
- setIsIntersecting(isVisible);
24
-
25
- if (isVisible && !hasIntersected) {
26
- setHasIntersected(true);
27
- }
28
- },
29
- defaultOptions
30
- );
31
-
32
- observer.observe(target);
33
-
34
- return () => {
35
- observer.unobserve(target);
36
- };
37
- }, [defaultOptions, hasIntersected]);
38
-
39
- return {
40
- targetRef,
41
- isIntersecting,
42
- hasIntersected,
43
- };
44
- };
45
-
46
- export const useLazyImage = (src: string, placeholder?: string) => {
47
- const [imageSrc, setImageSrc] = useState(placeholder || '');
48
- const [isLoaded, setIsLoaded] = useState(false);
49
- const [isError, setIsError] = useState(false);
50
- const { targetRef, hasIntersected } = useIntersectionObserver();
51
-
52
- useEffect(() => {
53
- if (!hasIntersected || !src) return;
54
-
55
- const img = new Image();
56
-
57
- img.onload = () => {
58
- setImageSrc(src);
59
- setIsLoaded(true);
60
- setIsError(false);
61
- };
62
-
63
- img.onerror = () => {
64
- setIsError(true);
65
- setIsLoaded(false);
66
- };
67
-
68
- img.src = src;
69
-
70
- return () => {
71
- img.onload = null;
72
- img.onerror = null;
73
- };
74
- }, [hasIntersected, src]);
75
-
76
- return {
77
- targetRef,
78
- imageSrc,
79
- isLoaded,
80
- isError,
81
- hasIntersected,
82
- };
83
- };
84
-
85
- export const useVirtualList = <T>(
86
- items: T[],
87
- options: {
88
- itemHeight: number;
89
- containerHeight: number;
90
- overscan?: number;
91
- }
92
- ) => {
93
- const { itemHeight, containerHeight, overscan = 5 } = options;
94
- const [scrollTop, setScrollTop] = useState(0);
95
- const scrollElementRef = useRef<HTMLDivElement>(null);
96
-
97
- const totalHeight = items.length * itemHeight;
98
- const startIndex = Math.max(0, Math.floor(scrollTop / itemHeight) - overscan);
99
- const endIndex = Math.min(
100
- items.length - 1,
101
- Math.ceil((scrollTop + containerHeight) / itemHeight) + overscan
102
- );
103
-
104
- const visibleItems = items.slice(startIndex, endIndex + 1).map((item, index) => ({
105
- item,
106
- index: startIndex + index,
107
- style: {
108
- position: 'absolute' as const,
109
- top: (startIndex + index) * itemHeight,
110
- height: itemHeight,
111
- width: '100%',
112
- },
113
- }));
114
-
115
- const handleScroll = useCallback((event: React.UIEvent<HTMLDivElement>) => {
116
- setScrollTop(event.currentTarget.scrollTop);
117
- }, []);
118
-
119
- return {
120
- scrollElementRef,
121
- visibleItems,
122
- totalHeight,
123
- handleScroll,
124
- startIndex,
125
- endIndex,
126
- };
127
- };
128
-
129
- export const useResourcePreloader = () => {
130
- const [preloadedResources, setPreloadedResources] = useState<Set<string>>(new Set());
131
- const [loadingResources, setLoadingResources] = useState<Set<string>>(new Set());
132
-
133
- const preloadImage = useCallback(async (src: string): Promise<void> => {
134
- if (preloadedResources.has(src) || loadingResources.has(src)) {
135
- return;
136
- }
137
-
138
- setLoadingResources(prev => new Set(prev).add(src));
139
-
140
- return new Promise((resolve, reject) => {
141
- const img = new Image();
142
-
143
- img.onload = () => {
144
- setPreloadedResources(prev => new Set(prev).add(src));
145
- setLoadingResources(prev => {
146
- const newSet = new Set(prev);
147
- newSet.delete(src);
148
- return newSet;
149
- });
150
- resolve();
151
- };
152
-
153
- img.onerror = () => {
154
- setLoadingResources(prev => {
155
- const newSet = new Set(prev);
156
- newSet.delete(src);
157
- return newSet;
158
- });
159
- reject(new Error(`Failed to preload image: ${src}`));
160
- };
161
-
162
- img.src = src;
163
- });
164
- }, [preloadedResources, loadingResources]);
165
-
166
- return {
167
- preloadImage,
168
- preloadedResources: Array.from(preloadedResources),
169
- loadingResources: Array.from(loadingResources),
170
- isPreloaded: (src: string) => preloadedResources.has(src),
171
- isLoading: (src: string) => loadingResources.has(src),
172
- };
173
- };
174
-
175
- export const useLazyComponent = <TProps = Record<string, unknown>>(
176
- importFunc: () => Promise<{ default: React.ComponentType<TProps> }>,
177
- fallback?: React.ComponentType<TProps>
178
- ) => {
179
- const [Component, setComponent] = useState<React.ComponentType<TProps> | null>(null);
180
- const [isLoading, setIsLoading] = useState(false);
181
- const [error, setError] = useState<Error | null>(null);
182
- const { targetRef, hasIntersected } = useIntersectionObserver();
183
-
184
- const loadComponent = useCallback(async () => {
185
- if (Component || isLoading) return;
186
-
187
- setIsLoading(true);
188
- setError(null);
189
-
190
- try {
191
- const module = await importFunc();
192
- setComponent(() => module.default);
193
- } catch (err) {
194
- setError(err instanceof Error ? err : new Error('Failed to load component'));
195
- } finally {
196
- setIsLoading(false);
197
- }
198
- }, [importFunc, Component, isLoading]);
199
-
200
- useEffect(() => {
201
- if (hasIntersected) {
202
- loadComponent();
203
- }
204
- }, [hasIntersected, loadComponent]);
205
-
206
- const LazyComponent = useCallback((props: TProps) => {
207
- if (error) {
208
- return fallback ? React.createElement(fallback as React.ElementType, props) : null;
209
- }
210
-
211
- if (isLoading || !Component) {
212
- return fallback ? React.createElement(fallback as React.ElementType, props) : null;
213
- }
214
-
215
- return React.createElement(Component as React.ElementType, props);
216
- }, [Component, isLoading, error, fallback]);
217
-
218
- return {
219
- targetRef,
220
- LazyComponent,
221
- isLoading,
222
- error,
223
- hasIntersected,
224
- loadComponent,
225
- };
226
- };
227
-
228
- export const useProgressiveEnhancement = () => {
229
- const [isOnline, setIsOnline] = useState(navigator.onLine);
230
- const [connectionType, setConnectionType] = useState<'slow' | 'fast' | 'unknown'>('unknown');
231
-
232
- const handleOnline = useCallback(() => {
233
- setIsOnline(true);
234
- }, []);
235
-
236
- const handleOffline = useCallback(() => {
237
- setIsOnline(false);
238
- }, []);
239
-
240
- useEffect(() => {
241
- window.addEventListener('online', handleOnline);
242
- window.addEventListener('offline', handleOffline);
243
-
244
- interface NetworkInformation {
245
- effectiveType?: string;
246
- addEventListener?: (type: string, listener: () => void) => void;
247
- removeEventListener?: (type: string, listener: () => void) => void;
248
- }
249
-
250
- function getConnection(): NetworkInformation | undefined {
251
- const nav = navigator as unknown as {
252
- connection?: unknown;
253
- mozConnection?: unknown;
254
- webkitConnection?: unknown;
255
- };
256
- const conn = nav.connection || nav.mozConnection || nav.webkitConnection;
257
- if (
258
- conn &&
259
- (typeof conn === 'object') &&
260
- ('effectiveType' in conn)
261
- ) {
262
- return conn as NetworkInformation;
263
- }
264
- return undefined;
265
- }
266
-
267
- const connection = getConnection();
268
-
269
- if (connection) {
270
- const updateConnectionType = () => {
271
- const effectiveType = connection.effectiveType;
272
- if (effectiveType === 'slow-2g' || effectiveType === '2g') {
273
- setConnectionType('slow');
274
- } else if (effectiveType === '3g' || effectiveType === '4g') {
275
- setConnectionType('fast');
276
- } else {
277
- setConnectionType('unknown');
278
- }
279
- };
280
-
281
- updateConnectionType();
282
- connection.addEventListener?.('change', updateConnectionType);
283
-
284
- return () => {
285
- window.removeEventListener('online', handleOnline);
286
- window.removeEventListener('offline', handleOffline);
287
- connection.removeEventListener?.('change', updateConnectionType);
288
- };
289
- }
290
-
291
- return () => {
292
- window.removeEventListener('online', handleOnline);
293
- window.removeEventListener('offline', handleOffline);
294
- };
295
- }, [handleOnline, handleOffline]);
296
-
297
- const shouldLoadHeavyContent = useCallback(() => {
298
- return isOnline && connectionType !== 'slow';
299
- }, [isOnline, connectionType]);
300
-
301
- const shouldPreloadContent = useCallback(() => {
302
- return isOnline && connectionType === 'fast';
303
- }, [isOnline, connectionType]);
304
-
305
- return {
306
- isOnline,
307
- connectionType,
308
- shouldLoadHeavyContent,
309
- shouldPreloadContent,
310
- };
311
- };
312
-
313
- export const useLazyLoading = (options: {
314
- enableImageLazyLoading?: boolean;
315
- enableComponentLazyLoading?: boolean;
316
- enableVirtualization?: boolean;
317
- enablePreloading?: boolean;
318
- enableProgressiveEnhancement?: boolean;
319
- } = {}) => {
320
- const {
321
- enableImageLazyLoading = true,
322
- enableComponentLazyLoading = true,
323
- enableVirtualization = true,
324
- enablePreloading = true,
325
- enableProgressiveEnhancement = true,
326
- } = options;
327
-
328
- const resourcePreloader = useResourcePreloader();
329
- const progressiveEnhancement = useProgressiveEnhancement();
330
-
331
- return {
332
- useIntersectionObserver,
333
- useLazyImage: enableImageLazyLoading ? useLazyImage : null,
334
- useLazyComponent: enableComponentLazyLoading ? useLazyComponent : null,
335
- useVirtualList: enableVirtualization ? useVirtualList : null,
336
- preloadImage: enablePreloading ? resourcePreloader.preloadImage : undefined,
337
- shouldLoadHeavyContent: enableProgressiveEnhancement ? progressiveEnhancement.shouldLoadHeavyContent : undefined,
338
- shouldPreloadContent: enableProgressiveEnhancement ? progressiveEnhancement.shouldPreloadContent : undefined,
339
- isOnline: enableProgressiveEnhancement ? progressiveEnhancement.isOnline : undefined,
340
- connectionType: enableProgressiveEnhancement ? progressiveEnhancement.connectionType : undefined,
341
- };
342
- };