@capillarytech/creatives-library 8.0.207 → 8.0.209

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 (77) hide show
  1. package/assets/Android.png +0 -0
  2. package/assets/iOS.png +0 -0
  3. package/package.json +16 -2
  4. package/v2Components/HtmlEditor/HTMLEditor.js +508 -0
  5. package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +1809 -0
  6. package/v2Components/HtmlEditor/__tests__/index.lazy.test.js +532 -0
  7. package/v2Components/HtmlEditor/_htmlEditor.scss +304 -0
  8. package/v2Components/HtmlEditor/_index.lazy.scss +26 -0
  9. package/v2Components/HtmlEditor/components/CodeEditorPane/_codeEditorPane.scss +376 -0
  10. package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +331 -0
  11. package/v2Components/HtmlEditor/components/DeviceToggle/__tests__/index.test.js +314 -0
  12. package/v2Components/HtmlEditor/components/DeviceToggle/_deviceToggle.scss +244 -0
  13. package/v2Components/HtmlEditor/components/DeviceToggle/index.js +111 -0
  14. package/v2Components/HtmlEditor/components/EditorToolbar/PreviewModeGroup.js +72 -0
  15. package/v2Components/HtmlEditor/components/EditorToolbar/__tests__/PreviewModeGroup.test.js +1594 -0
  16. package/v2Components/HtmlEditor/components/EditorToolbar/_editorToolbar.scss +113 -0
  17. package/v2Components/HtmlEditor/components/EditorToolbar/_previewModeGroup.scss +82 -0
  18. package/v2Components/HtmlEditor/components/EditorToolbar/index.js +115 -0
  19. package/v2Components/HtmlEditor/components/FullscreenModal/_fullscreenModal.scss +57 -0
  20. package/v2Components/HtmlEditor/components/InAppPreviewPane/ContentOverlay.js +90 -0
  21. package/v2Components/HtmlEditor/components/InAppPreviewPane/DeviceFrame.js +60 -0
  22. package/v2Components/HtmlEditor/components/InAppPreviewPane/LayoutSelector.js +58 -0
  23. package/v2Components/HtmlEditor/components/InAppPreviewPane/__tests__/ContentOverlay.test.js +389 -0
  24. package/v2Components/HtmlEditor/components/InAppPreviewPane/__tests__/DeviceFrame.test.js +424 -0
  25. package/v2Components/HtmlEditor/components/InAppPreviewPane/__tests__/LayoutSelector.test.js +248 -0
  26. package/v2Components/HtmlEditor/components/InAppPreviewPane/_inAppPreviewPane.scss +253 -0
  27. package/v2Components/HtmlEditor/components/InAppPreviewPane/constants.js +104 -0
  28. package/v2Components/HtmlEditor/components/InAppPreviewPane/index.js +179 -0
  29. package/v2Components/HtmlEditor/components/PreviewPane/_previewPane.scss +220 -0
  30. package/v2Components/HtmlEditor/components/PreviewPane/index.js +229 -0
  31. package/v2Components/HtmlEditor/components/SplitContainer/SplitContainer.js +276 -0
  32. package/v2Components/HtmlEditor/components/SplitContainer/__tests__/SplitContainer.test.js +295 -0
  33. package/v2Components/HtmlEditor/components/SplitContainer/_splitContainer.scss +257 -0
  34. package/v2Components/HtmlEditor/components/SplitContainer/index.js +7 -0
  35. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/__tests__/index.test.js +152 -0
  36. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/_validationErrorDisplay.scss +31 -0
  37. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/index.js +70 -0
  38. package/v2Components/HtmlEditor/components/ValidationPanel/__tests__/index.test.js +98 -0
  39. package/v2Components/HtmlEditor/components/ValidationPanel/_validationPanel.scss +311 -0
  40. package/v2Components/HtmlEditor/components/ValidationPanel/index.js +297 -0
  41. package/v2Components/HtmlEditor/components/ValidationPanel/messages.js +57 -0
  42. package/v2Components/HtmlEditor/components/common/EditorContext.js +84 -0
  43. package/v2Components/HtmlEditor/components/common/__tests__/EditorContext.test.js +660 -0
  44. package/v2Components/HtmlEditor/constants.js +241 -0
  45. package/v2Components/HtmlEditor/hooks/__tests__/useEditorContent.test.js +450 -0
  46. package/v2Components/HtmlEditor/hooks/__tests__/useInAppContent.test.js +785 -0
  47. package/v2Components/HtmlEditor/hooks/__tests__/useLayoutState.test.js +580 -0
  48. package/v2Components/HtmlEditor/hooks/__tests__/useValidation.enhanced.test.js +768 -0
  49. package/v2Components/HtmlEditor/hooks/__tests__/useValidation.test.js +590 -0
  50. package/v2Components/HtmlEditor/hooks/useEditorContent.js +274 -0
  51. package/v2Components/HtmlEditor/hooks/useInAppContent.js +407 -0
  52. package/v2Components/HtmlEditor/hooks/useLayoutState.js +247 -0
  53. package/v2Components/HtmlEditor/hooks/useValidation.js +325 -0
  54. package/v2Components/HtmlEditor/index.js +29 -0
  55. package/v2Components/HtmlEditor/index.lazy.js +114 -0
  56. package/v2Components/HtmlEditor/messages.js +389 -0
  57. package/v2Components/HtmlEditor/utils/__tests__/contentSanitizer.test.js +741 -0
  58. package/v2Components/HtmlEditor/utils/__tests__/htmlValidator.enhanced.test.js +1042 -0
  59. package/v2Components/HtmlEditor/utils/__tests__/liquidTemplateSupport.test.js +515 -0
  60. package/v2Components/HtmlEditor/utils/__tests__/properSyntaxHighlighting.test.js +473 -0
  61. package/v2Components/HtmlEditor/utils/__tests__/simplePerformance.test.js +1109 -0
  62. package/v2Components/HtmlEditor/utils/__tests__/validationAdapter.test.js +240 -0
  63. package/v2Components/HtmlEditor/utils/contentSanitizer.js +433 -0
  64. package/v2Components/HtmlEditor/utils/htmlValidator.js +508 -0
  65. package/v2Components/HtmlEditor/utils/liquidTemplateSupport.js +524 -0
  66. package/v2Components/HtmlEditor/utils/properSyntaxHighlighting.js +163 -0
  67. package/v2Components/HtmlEditor/utils/simplePerformance.js +145 -0
  68. package/v2Components/HtmlEditor/utils/validationAdapter.js +130 -0
  69. package/v2Containers/EmailWrapper/components/HTMLEditorTesting.js +200 -0
  70. package/v2Containers/EmailWrapper/components/__tests__/HTMLEditorTesting.test.js +545 -0
  71. package/v2Containers/EmailWrapper/index.js +8 -1
  72. package/v2Containers/Templates/constants.js +8 -0
  73. package/v2Containers/Templates/index.js +56 -28
  74. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +5 -14
  75. package/v2Containers/Whatsapp/constants.js +26 -2
  76. package/v2Containers/Whatsapp/index.js +4 -1
  77. package/v2Containers/Whatsapp/tests/index.test.js +460 -18
@@ -0,0 +1,253 @@
1
+ /**
2
+ * InApp Preview Pane Styles
3
+ */
4
+
5
+ @import '~@capillarytech/cap-ui-library/styles/_variables.scss';
6
+
7
+ // ContentOverlay Variables
8
+ $content-overlay-bg: $CAP_WHITE;
9
+ $content-overlay-border-radius: 0.5rem;
10
+ $content-overlay-border-color: $CAP_G07;
11
+ $content-overlay-shadow: 0 0.125rem 0.75rem rgba($CAP_G01, 0.15);
12
+ $content-overlay-z-index: 10;
13
+
14
+ // Empty State Variables
15
+ $empty-state-color: $CAP_G10;
16
+ $empty-state-font-size: 0.75rem;
17
+ $empty-state-padding: 1.25rem;
18
+ $empty-state-icon-size: 1.5rem;
19
+ $empty-state-icon-margin: 0.5rem;
20
+ $empty-state-meta-font-size: 0.625rem;
21
+ $empty-state-meta-margin: 0.25rem;
22
+ $empty-state-meta-opacity: 0.7;
23
+
24
+ .inapp-preview-pane {
25
+ height: 100%;
26
+ position: relative;
27
+ background-color: $CAP_G08;
28
+ background-image: radial-gradient(circle at 50% 50%, rgba($CAP_WHITE, 0.1) 0%, transparent 70%);
29
+ display: flex;
30
+ flex-direction: column;
31
+ align-items: center;
32
+ justify-content: flex-start;
33
+ padding: 0.25rem;
34
+ overflow-x: hidden;
35
+ overflow-y: auto;
36
+ scroll-behavior: smooth;
37
+
38
+ &--adaptive-focus {
39
+ scroll-padding-top: 1.25rem;
40
+ scroll-padding-bottom: 1.25rem;
41
+ }
42
+
43
+ &--modal-context {
44
+ padding: 3.125rem 0.25rem 0.5rem 0.25rem;
45
+ justify-content: center;
46
+ }
47
+
48
+ &--fullscreen {
49
+ padding: 0.25rem;
50
+ }
51
+
52
+ .html-editor-fullscreen &,
53
+ .html-editor-fullscreen .inapp-preview-pane,
54
+ .ant-modal .inapp-preview-pane {
55
+ padding: 3.125rem 0.25rem 0.5rem 0.25rem;
56
+ justify-content: center;
57
+ min-height: 37.5rem;
58
+ overflow: auto;
59
+
60
+ &::-webkit-scrollbar {
61
+ width: 0.5rem;
62
+ }
63
+
64
+ .inapp-preview-pane__device-container,
65
+ .inapp-preview-pane__device-container.inapp-preview-pane__device-container {
66
+ margin: 1.25rem auto 0.3125rem auto;
67
+ min-height: 25rem;
68
+ }
69
+
70
+ .device-frame {
71
+ margin: 0.9375rem 0 0.125rem 0;
72
+ }
73
+ }
74
+
75
+ &::-webkit-scrollbar {
76
+ width: 0.375rem;
77
+ }
78
+
79
+ &::-webkit-scrollbar-track {
80
+ background: rgba($CAP_G01, 0.05);
81
+ border-radius: 0.1875rem;
82
+ }
83
+
84
+ &::-webkit-scrollbar-thumb {
85
+ background: rgba($CAP_G01, 0.2);
86
+ border-radius: 0.1875rem;
87
+
88
+ &:hover {
89
+ background: rgba($CAP_G01, 0.3);
90
+ }
91
+ }
92
+
93
+ &__device-container {
94
+ display: flex;
95
+ align-items: center;
96
+ justify-content: center;
97
+ flex-shrink: 0;
98
+ margin: 0.3125rem auto;
99
+ max-width: 100%;
100
+ min-width: min-content;
101
+ width: auto;
102
+ transition: transform 0.3s ease-in-out;
103
+ transform-origin: center center;
104
+
105
+ &--modal-context {
106
+ transform: scale(0.65);
107
+ margin: 1.25rem auto 0.3125rem auto;
108
+ }
109
+
110
+ &--fullscreen {
111
+ transform: scale(1.1);
112
+ margin: 0.3125rem auto;
113
+ }
114
+
115
+ &--normal {
116
+ transform: scale(0.95);
117
+ margin: 0.3125rem auto;
118
+ }
119
+ }
120
+
121
+ }
122
+
123
+ .device-frame {
124
+ position: relative;
125
+ display: inline-block;
126
+ filter: drop-shadow(0 0.75rem 3rem rgba($CAP_G01, 0.25));
127
+ transition: all 0.3s ease-in-out;
128
+
129
+ &:hover {
130
+ filter: drop-shadow(0 1rem 3.5rem rgba($CAP_G01, 0.3));
131
+ transform: translateY(-0.125rem);
132
+ }
133
+
134
+ &__content-area {
135
+ position: absolute;
136
+ overflow: hidden;
137
+ background-color: rgba($CAP_G01, 0.08);
138
+ border: 0.125rem solid rgba($CAP_G01, 0.15);
139
+ box-shadow: inset 0 0.125rem 0.5rem rgba($CAP_G01, 0.1);
140
+ }
141
+ }
142
+
143
+ .content-overlay {
144
+ position: absolute;
145
+ background-color: $content-overlay-bg;
146
+ border-radius: $content-overlay-border-radius;
147
+ box-shadow: $content-overlay-shadow;
148
+ border: 0.0625rem solid $content-overlay-border-color;
149
+ overflow: hidden;
150
+ z-index: $content-overlay-z-index;
151
+
152
+ &__screen-area {
153
+ position: absolute;
154
+ }
155
+
156
+ &--popup {
157
+ border-radius: 0.75rem;
158
+ box-shadow: 0 0.25rem 1.25rem rgba($CAP_G01, 0.2);
159
+ }
160
+
161
+ &--header {
162
+ border-radius: 0.5rem 0.5rem 0.75rem 0.75rem;
163
+ box-shadow: 0 0.125rem 0.5rem rgba($CAP_G01, 0.15);
164
+ }
165
+
166
+ &--footer {
167
+ border-radius: 0.75rem 0.75rem 0.5rem 0.5rem;
168
+ box-shadow: 0 -0.125rem 0.5rem rgba($CAP_G01, 0.15);
169
+ }
170
+
171
+ &--fullscreen {
172
+ border-radius: 0.25rem;
173
+ box-shadow: none;
174
+ border: none;
175
+ }
176
+
177
+ &__empty-state {
178
+ display: flex;
179
+ align-items: center;
180
+ justify-content: center;
181
+ height: 100%;
182
+ padding: $empty-state-padding;
183
+ text-align: center;
184
+ color: $empty-state-color;
185
+ font-size: $empty-state-font-size;
186
+
187
+ &__icon {
188
+ font-size: $empty-state-icon-size;
189
+ margin-bottom: $empty-state-icon-margin;
190
+ }
191
+
192
+ &__meta {
193
+ font-size: $empty-state-meta-font-size;
194
+ margin-top: $empty-state-meta-margin;
195
+ opacity: $empty-state-meta-opacity;
196
+ }
197
+ }
198
+
199
+ iframe {
200
+ width: 100%;
201
+ height: 100%;
202
+ border: none;
203
+ background-color: $content-overlay-bg;
204
+ display: block;
205
+ }
206
+ }
207
+
208
+ @media (max-width: 768px) {
209
+ .inapp-preview-pane {
210
+ padding: 0.125rem;
211
+
212
+ &__device-container,
213
+ &__device-container.inapp-preview-pane__device-container {
214
+ transform: scale(0.8);
215
+ margin: 0.25rem auto;
216
+ }
217
+
218
+ &__controls {
219
+ top: 0.5rem;
220
+
221
+ .layout-selector__radio-group {
222
+ .ant-radio-button-wrapper {
223
+ font-size: 0.625rem;
224
+ padding: 0.1875rem 0.375rem;
225
+ }
226
+ }
227
+ }
228
+ }
229
+ }
230
+
231
+ @media (max-width: 576px) {
232
+ .inapp-preview-pane {
233
+ padding: 0.125rem;
234
+
235
+ &__device-container,
236
+ &__device-container.inapp-preview-pane__device-container {
237
+ transform: scale(0.7);
238
+ margin: 0.1875rem auto;
239
+ }
240
+ }
241
+ }
242
+
243
+ @media (max-width: 480px) {
244
+ .inapp-preview-pane {
245
+ padding: 0.0625rem;
246
+
247
+ &__device-container,
248
+ &__device-container.inapp-preview-pane__device-container {
249
+ transform: scale(0.6);
250
+ margin: 0.125rem auto;
251
+ }
252
+ }
253
+ }
@@ -0,0 +1,104 @@
1
+ /**
2
+ * InApp Preview Constants
3
+ */
4
+
5
+ export const LAYOUT_TYPES = {
6
+ MODAL: 'POPUP',
7
+ HEADER: 'HEADER',
8
+ FOOTER: 'FOOTER',
9
+ FULLSCREEN: 'FULLSCREEN'
10
+ };
11
+
12
+ export const LAYOUT_OPTIONS = [
13
+ {
14
+ value: LAYOUT_TYPES.MODAL,
15
+ label: 'Modal',
16
+ description: 'Content displayed in center like a modal dialog'
17
+ },
18
+ {
19
+ value: LAYOUT_TYPES.HEADER,
20
+ label: 'Top Banner',
21
+ description: 'Content displayed at the top of the screen'
22
+ },
23
+ {
24
+ value: LAYOUT_TYPES.FOOTER,
25
+ label: 'Bottom Banner',
26
+ description: 'Content displayed at the bottom of the screen'
27
+ },
28
+ {
29
+ value: LAYOUT_TYPES.FULLSCREEN,
30
+ label: 'Fullscreen',
31
+ description: 'Content covers the entire screen'
32
+ }
33
+ ];
34
+
35
+ export const COMPONENT_SIZES = ['small', 'middle', 'large'];
36
+
37
+ export const DEVICE_FRAMES = {
38
+ ANDROID: 'android-frame.svg',
39
+ IOS: 'ios-frame.svg'
40
+ };
41
+
42
+ // Screen area dimensions within device frames (accounting for PNG bezels)
43
+ export const DEVICE_SCREEN_AREAS = {
44
+ IOS: {
45
+ // iOS screen area within 450x920 frame - accurate values for PNG asset
46
+ top: '85px', // Top bezel including notch area
47
+ left: '35px', // Side bezel width
48
+ width: '380px', // Actual screen width within bezels
49
+ height: '710px' // Actual screen height within bezels
50
+ },
51
+ ANDROID: {
52
+ // Android screen area within 450x920 frame - accurate values for PNG asset
53
+ top: '85px', // Top bezel including status bar
54
+ left: '35px', // Side bezel width (same as iOS for consistency)
55
+ width: '380px', // Actual screen width within bezels
56
+ height: '710px' // Actual screen height within bezels
57
+ }
58
+ };
59
+
60
+ // Content positioning configurations within the screen area (not the full frame)
61
+ export const LAYOUT_POSITIONS = {
62
+ [LAYOUT_TYPES.MODAL]: {
63
+ top: '50%',
64
+ left: '50%',
65
+ transform: 'translate(-50%, -50%)',
66
+ width: '85%', // Reduced to ensure content stays within screen
67
+ height: '55%', // Reduced to ensure content stays within screen
68
+ maxWidth: '320px', // Adjusted for screen area
69
+ maxHeight: '380px',
70
+ minHeight: '180px'
71
+ },
72
+ [LAYOUT_TYPES.HEADER]: {
73
+ top: '2%', // More margin from top of screen
74
+ left: '50%',
75
+ transform: 'translateX(-50%)',
76
+ width: '90%', // Reduced to stay within screen bounds
77
+ height: '22%',
78
+ maxHeight: '160px',
79
+ minHeight: '100px'
80
+ },
81
+ [LAYOUT_TYPES.FOOTER]: {
82
+ bottom: '2%', // More margin from bottom of screen
83
+ left: '50%',
84
+ transform: 'translateX(-50%)',
85
+ width: '90%', // Reduced to stay within screen bounds
86
+ height: '22%',
87
+ maxHeight: '160px',
88
+ minHeight: '100px'
89
+ },
90
+ [LAYOUT_TYPES.FULLSCREEN]: {
91
+ top: '2%', // Small margin to account for screen bounds
92
+ left: '2%',
93
+ width: '96%', // Slightly smaller to ensure it stays within screen
94
+ height: '96%'
95
+ }
96
+ };
97
+
98
+ // Scroll position multipliers for adaptive scrolling in InAppPreviewPane
99
+ export const SCROLL_POSITION_MULTIPLIERS = {
100
+ [LAYOUT_TYPES.HEADER]: 0.0, // Top of screen
101
+ [LAYOUT_TYPES.FOOTER]: 1.0, // Bottom of screen
102
+ [LAYOUT_TYPES.MODAL]: 0.4, // Center-ish position
103
+ [LAYOUT_TYPES.FULLSCREEN]: 0.3 // Slightly above center
104
+ };
@@ -0,0 +1,179 @@
1
+ /**
2
+ * InAppPreviewPane - Complete InApp preview with device frames and dynamic positioning
3
+ */
4
+
5
+ import React, { useMemo, useRef, useEffect, useCallback } from 'react';
6
+ import PropTypes from 'prop-types';
7
+ import { injectIntl, intlShape } from 'react-intl';
8
+
9
+ import CapRow from '@capillarytech/cap-ui-library/CapRow';
10
+
11
+ // Components
12
+ import DeviceFrame from './DeviceFrame';
13
+ import ContentOverlay from './ContentOverlay';
14
+
15
+ // Context
16
+ import { useEditorContext } from '../common/EditorContext';
17
+
18
+ // Constants
19
+ import { DEVICE_TYPES } from '../../constants';
20
+ import { LAYOUT_TYPES, SCROLL_POSITION_MULTIPLIERS } from './constants';
21
+
22
+ // Styles
23
+ import './_inAppPreviewPane.scss';
24
+
25
+ const InAppPreviewPane = ({
26
+ intl,
27
+ className = '',
28
+ isFullscreenMode = false,
29
+ isModalContext = false,
30
+ layoutType: propLayoutType = LAYOUT_TYPES.MODAL
31
+ }) => {
32
+ const {
33
+ content,
34
+ activeDevice,
35
+ getDeviceContent,
36
+ layoutType: contextLayoutType,
37
+ isFullscreenMode: contextIsFullscreenMode
38
+ } = useEditorContext();
39
+
40
+ // Refs for adaptive scrolling
41
+ const containerRef = useRef(null);
42
+ const deviceContainerRef = useRef(null);
43
+
44
+ // Use layoutType from context if available, otherwise fall back to prop
45
+ const layoutType = contextLayoutType || propLayoutType;
46
+
47
+ // Get current device content
48
+ const currentContent = useMemo(() => {
49
+ if (getDeviceContent) {
50
+ return getDeviceContent(activeDevice);
51
+ }
52
+ return content?.content || '';
53
+ }, [content?.content, activeDevice, getDeviceContent]);
54
+
55
+ // Adaptive scrolling effect based on layout type (disabled for modal context)
56
+ useEffect(() => {
57
+ if (isModalContext || !containerRef.current || !deviceContainerRef.current) {
58
+ return;
59
+ }
60
+
61
+ const container = containerRef.current;
62
+ const deviceContainer = deviceContainerRef.current;
63
+
64
+ const isComponentVisible = () => {
65
+ // Use context fullscreen state with fallback to prop, then fallback to container visibility check
66
+ const isFullscreen = contextIsFullscreenMode ?? isFullscreenMode;
67
+ return !isFullscreen && container.offsetParent !== null;
68
+ };
69
+
70
+ const performAdaptiveScroll = () => {
71
+ try {
72
+ if (!isComponentVisible()) return;
73
+
74
+ const containerHeight = container.clientHeight;
75
+ const deviceHeight = deviceContainer.offsetHeight;
76
+ const scrollableHeight = Math.max(0, deviceHeight - containerHeight);
77
+
78
+ if (scrollableHeight <= 0) return;
79
+
80
+ const scrollMultiplier = SCROLL_POSITION_MULTIPLIERS[layoutType] ?? SCROLL_POSITION_MULTIPLIERS[LAYOUT_TYPES.MODAL];
81
+ const targetScrollTop = Math.max(0, scrollableHeight * scrollMultiplier);
82
+
83
+ requestAnimationFrame(() => {
84
+ container.scrollTo({
85
+ top: targetScrollTop,
86
+ behavior: 'smooth'
87
+ });
88
+ });
89
+ } catch (error) {
90
+ console.warn('Adaptive scroll error:', error);
91
+ }
92
+ };
93
+
94
+ const timeoutId = setTimeout(performAdaptiveScroll, 150);
95
+ return () => clearTimeout(timeoutId);
96
+ }, [layoutType, isFullscreenMode, isModalContext, contextIsFullscreenMode]);
97
+
98
+
99
+ // Generate CSS classes based on context
100
+ const getContainerClasses = () => {
101
+ const baseClass = 'inapp-preview-pane';
102
+ const classes = [baseClass];
103
+
104
+ if (isModalContext) {
105
+ classes.push(`${baseClass}--modal-context`);
106
+ }
107
+
108
+ if (isFullscreenMode) {
109
+ classes.push(`${baseClass}--fullscreen`);
110
+ }
111
+
112
+ if (className) {
113
+ classes.push(className);
114
+ }
115
+
116
+ return classes.join(' ');
117
+ };
118
+
119
+ const getDeviceContainerClasses = () => {
120
+ const baseClass = 'inapp-preview-pane__device-container';
121
+ const classes = [baseClass];
122
+
123
+ if (isModalContext) {
124
+ classes.push(`${baseClass}--modal-context`);
125
+ } else if (isFullscreenMode) {
126
+ classes.push(`${baseClass}--fullscreen`);
127
+ } else {
128
+ classes.push(`${baseClass}--normal`);
129
+ }
130
+
131
+ return classes.join(' ');
132
+ };
133
+
134
+ const handleScroll = useCallback((e) => {
135
+ // Basic scroll handling
136
+ }, []);
137
+
138
+ const handleWheel = useCallback((e) => {
139
+ // Wheel event handling
140
+ }, []);
141
+
142
+ const handleTouchMove = useCallback((e) => {
143
+ // Touch move handling
144
+ }, []);
145
+
146
+ return (
147
+ <div
148
+ ref={containerRef}
149
+ className={getContainerClasses()}
150
+ onScroll={handleScroll}
151
+ onWheel={handleWheel}
152
+ onTouchMove={handleTouchMove}
153
+ >
154
+ <CapRow
155
+ ref={deviceContainerRef}
156
+ className={getDeviceContainerClasses()}
157
+ >
158
+ <DeviceFrame device={activeDevice}>
159
+ <ContentOverlay
160
+ layoutType={layoutType}
161
+ content={currentContent}
162
+ device={activeDevice}
163
+ className="inapp-preview-pane__content-overlay"
164
+ />
165
+ </DeviceFrame>
166
+ </CapRow>
167
+ </div>
168
+ );
169
+ };
170
+
171
+ InAppPreviewPane.propTypes = {
172
+ intl: intlShape.isRequired,
173
+ className: PropTypes.string,
174
+ isFullscreenMode: PropTypes.bool,
175
+ isModalContext: PropTypes.bool,
176
+ layoutType: PropTypes.oneOf(Object.values(LAYOUT_TYPES))
177
+ };
178
+
179
+ export default injectIntl(InAppPreviewPane);