@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.
- package/assets/Android.png +0 -0
- package/assets/iOS.png +0 -0
- package/package.json +16 -2
- package/v2Components/HtmlEditor/HTMLEditor.js +508 -0
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +1809 -0
- package/v2Components/HtmlEditor/__tests__/index.lazy.test.js +532 -0
- package/v2Components/HtmlEditor/_htmlEditor.scss +304 -0
- package/v2Components/HtmlEditor/_index.lazy.scss +26 -0
- package/v2Components/HtmlEditor/components/CodeEditorPane/_codeEditorPane.scss +376 -0
- package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +331 -0
- package/v2Components/HtmlEditor/components/DeviceToggle/__tests__/index.test.js +314 -0
- package/v2Components/HtmlEditor/components/DeviceToggle/_deviceToggle.scss +244 -0
- package/v2Components/HtmlEditor/components/DeviceToggle/index.js +111 -0
- package/v2Components/HtmlEditor/components/EditorToolbar/PreviewModeGroup.js +72 -0
- package/v2Components/HtmlEditor/components/EditorToolbar/__tests__/PreviewModeGroup.test.js +1594 -0
- package/v2Components/HtmlEditor/components/EditorToolbar/_editorToolbar.scss +113 -0
- package/v2Components/HtmlEditor/components/EditorToolbar/_previewModeGroup.scss +82 -0
- package/v2Components/HtmlEditor/components/EditorToolbar/index.js +115 -0
- package/v2Components/HtmlEditor/components/FullscreenModal/_fullscreenModal.scss +57 -0
- package/v2Components/HtmlEditor/components/InAppPreviewPane/ContentOverlay.js +90 -0
- package/v2Components/HtmlEditor/components/InAppPreviewPane/DeviceFrame.js +60 -0
- package/v2Components/HtmlEditor/components/InAppPreviewPane/LayoutSelector.js +58 -0
- package/v2Components/HtmlEditor/components/InAppPreviewPane/__tests__/ContentOverlay.test.js +389 -0
- package/v2Components/HtmlEditor/components/InAppPreviewPane/__tests__/DeviceFrame.test.js +424 -0
- package/v2Components/HtmlEditor/components/InAppPreviewPane/__tests__/LayoutSelector.test.js +248 -0
- package/v2Components/HtmlEditor/components/InAppPreviewPane/_inAppPreviewPane.scss +253 -0
- package/v2Components/HtmlEditor/components/InAppPreviewPane/constants.js +104 -0
- package/v2Components/HtmlEditor/components/InAppPreviewPane/index.js +179 -0
- package/v2Components/HtmlEditor/components/PreviewPane/_previewPane.scss +220 -0
- package/v2Components/HtmlEditor/components/PreviewPane/index.js +229 -0
- package/v2Components/HtmlEditor/components/SplitContainer/SplitContainer.js +276 -0
- package/v2Components/HtmlEditor/components/SplitContainer/__tests__/SplitContainer.test.js +295 -0
- package/v2Components/HtmlEditor/components/SplitContainer/_splitContainer.scss +257 -0
- package/v2Components/HtmlEditor/components/SplitContainer/index.js +7 -0
- package/v2Components/HtmlEditor/components/ValidationErrorDisplay/__tests__/index.test.js +152 -0
- package/v2Components/HtmlEditor/components/ValidationErrorDisplay/_validationErrorDisplay.scss +31 -0
- package/v2Components/HtmlEditor/components/ValidationErrorDisplay/index.js +70 -0
- package/v2Components/HtmlEditor/components/ValidationPanel/__tests__/index.test.js +98 -0
- package/v2Components/HtmlEditor/components/ValidationPanel/_validationPanel.scss +311 -0
- package/v2Components/HtmlEditor/components/ValidationPanel/index.js +297 -0
- package/v2Components/HtmlEditor/components/ValidationPanel/messages.js +57 -0
- package/v2Components/HtmlEditor/components/common/EditorContext.js +84 -0
- package/v2Components/HtmlEditor/components/common/__tests__/EditorContext.test.js +660 -0
- package/v2Components/HtmlEditor/constants.js +241 -0
- package/v2Components/HtmlEditor/hooks/__tests__/useEditorContent.test.js +450 -0
- package/v2Components/HtmlEditor/hooks/__tests__/useInAppContent.test.js +785 -0
- package/v2Components/HtmlEditor/hooks/__tests__/useLayoutState.test.js +580 -0
- package/v2Components/HtmlEditor/hooks/__tests__/useValidation.enhanced.test.js +768 -0
- package/v2Components/HtmlEditor/hooks/__tests__/useValidation.test.js +590 -0
- package/v2Components/HtmlEditor/hooks/useEditorContent.js +274 -0
- package/v2Components/HtmlEditor/hooks/useInAppContent.js +407 -0
- package/v2Components/HtmlEditor/hooks/useLayoutState.js +247 -0
- package/v2Components/HtmlEditor/hooks/useValidation.js +325 -0
- package/v2Components/HtmlEditor/index.js +29 -0
- package/v2Components/HtmlEditor/index.lazy.js +114 -0
- package/v2Components/HtmlEditor/messages.js +389 -0
- package/v2Components/HtmlEditor/utils/__tests__/contentSanitizer.test.js +741 -0
- package/v2Components/HtmlEditor/utils/__tests__/htmlValidator.enhanced.test.js +1042 -0
- package/v2Components/HtmlEditor/utils/__tests__/liquidTemplateSupport.test.js +515 -0
- package/v2Components/HtmlEditor/utils/__tests__/properSyntaxHighlighting.test.js +473 -0
- package/v2Components/HtmlEditor/utils/__tests__/simplePerformance.test.js +1109 -0
- package/v2Components/HtmlEditor/utils/__tests__/validationAdapter.test.js +240 -0
- package/v2Components/HtmlEditor/utils/contentSanitizer.js +433 -0
- package/v2Components/HtmlEditor/utils/htmlValidator.js +508 -0
- package/v2Components/HtmlEditor/utils/liquidTemplateSupport.js +524 -0
- package/v2Components/HtmlEditor/utils/properSyntaxHighlighting.js +163 -0
- package/v2Components/HtmlEditor/utils/simplePerformance.js +145 -0
- package/v2Components/HtmlEditor/utils/validationAdapter.js +130 -0
- package/v2Containers/EmailWrapper/components/HTMLEditorTesting.js +200 -0
- package/v2Containers/EmailWrapper/components/__tests__/HTMLEditorTesting.test.js +545 -0
- package/v2Containers/EmailWrapper/index.js +8 -1
- package/v2Containers/Templates/constants.js +8 -0
- package/v2Containers/Templates/index.js +56 -28
- package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +5 -14
- package/v2Containers/Whatsapp/constants.js +26 -2
- package/v2Containers/Whatsapp/index.js +4 -1
- 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);
|