@capillarytech/creatives-library 8.0.271 → 8.0.273
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/constants/unified.js +2 -1
- package/initialReducer.js +2 -0
- package/package.json +1 -1
- package/services/api.js +10 -0
- package/services/tests/api.test.js +34 -0
- package/tests/integration/TemplateCreation/TemplateCreation.integration.test.js +17 -35
- package/tests/integration/TemplateCreation/api-response.js +31 -1
- package/tests/integration/TemplateCreation/msw-handler.js +2 -0
- package/utils/common.js +5 -0
- package/utils/commonUtils.js +28 -5
- package/utils/imageUrlUpload.js +13 -14
- package/utils/tests/commonUtil.test.js +224 -0
- package/utils/tests/imageUrlUpload.test.js +298 -0
- package/utils/transformTemplateConfig.js +0 -10
- package/v2Components/CapDeviceContent/index.js +61 -56
- package/v2Components/CapTagList/index.js +6 -1
- package/v2Components/CapTagListWithInput/index.js +5 -1
- package/v2Components/CapTagListWithInput/messages.js +1 -1
- package/v2Components/CapWhatsappCTA/tests/index.test.js +5 -0
- package/v2Components/ErrorInfoNote/constants.js +1 -0
- package/v2Components/ErrorInfoNote/index.js +402 -72
- package/v2Components/ErrorInfoNote/messages.js +32 -6
- package/v2Components/ErrorInfoNote/style.scss +278 -6
- package/v2Components/FormBuilder/tests/index.test.js +13 -4
- package/v2Components/HtmlEditor/HTMLEditor.js +418 -99
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +870 -0
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +1882 -133
- package/v2Components/HtmlEditor/__tests__/index.lazy.test.js +27 -16
- package/v2Components/HtmlEditor/_htmlEditor.scss +108 -45
- package/v2Components/HtmlEditor/_index.lazy.scss +0 -1
- package/v2Components/HtmlEditor/components/CodeEditorPane/_codeEditorPane.scss +23 -102
- package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +148 -140
- package/v2Components/HtmlEditor/components/DeviceToggle/_deviceToggle.scss +2 -1
- package/v2Components/HtmlEditor/components/DeviceToggle/index.js +3 -3
- package/v2Components/HtmlEditor/components/EditorToolbar/_editorToolbar.scss +9 -1
- package/v2Components/HtmlEditor/components/EditorToolbar/index.js +31 -6
- package/v2Components/HtmlEditor/components/FullscreenModal/_fullscreenModal.scss +22 -0
- package/v2Components/HtmlEditor/components/InAppPreviewPane/DeviceFrame.js +4 -7
- package/v2Components/HtmlEditor/components/InAppPreviewPane/__tests__/DeviceFrame.test.js +35 -45
- package/v2Components/HtmlEditor/components/InAppPreviewPane/_inAppPreviewPane.scss +1 -3
- package/v2Components/HtmlEditor/components/InAppPreviewPane/constants.js +33 -33
- package/v2Components/HtmlEditor/components/InAppPreviewPane/index.js +7 -6
- package/v2Components/HtmlEditor/components/PreviewPane/_previewPane.scss +7 -10
- package/v2Components/HtmlEditor/components/PreviewPane/index.js +22 -43
- package/v2Components/HtmlEditor/components/SplitContainer/_splitContainer.scss +1 -1
- package/v2Components/HtmlEditor/components/ValidationErrorDisplay/_validationErrorDisplay.scss +18 -0
- package/v2Components/HtmlEditor/components/ValidationErrorDisplay/index.js +36 -31
- package/v2Components/HtmlEditor/components/ValidationPanel/_validationPanel.scss +46 -34
- package/v2Components/HtmlEditor/components/ValidationPanel/constants.js +6 -0
- package/v2Components/HtmlEditor/components/ValidationPanel/index.js +52 -46
- package/v2Components/HtmlEditor/components/ValidationTabs/_validationTabs.scss +277 -0
- package/v2Components/HtmlEditor/components/ValidationTabs/index.js +295 -0
- package/v2Components/HtmlEditor/components/ValidationTabs/messages.js +51 -0
- package/v2Components/HtmlEditor/constants.js +45 -20
- package/v2Components/HtmlEditor/hooks/__tests__/useInAppContent.test.js +373 -16
- package/v2Components/HtmlEditor/hooks/__tests__/useValidation.test.js +351 -16
- package/v2Components/HtmlEditor/hooks/useEditorContent.js +5 -2
- package/v2Components/HtmlEditor/hooks/useInAppContent.js +88 -146
- package/v2Components/HtmlEditor/hooks/useValidation.js +213 -56
- package/v2Components/HtmlEditor/index.js +1 -1
- package/v2Components/HtmlEditor/messages.js +102 -94
- package/v2Components/HtmlEditor/utils/__tests__/htmlValidator.enhanced.test.js +214 -45
- package/v2Components/HtmlEditor/utils/__tests__/validationAdapter.test.js +134 -0
- package/v2Components/HtmlEditor/utils/contentSanitizer.js +40 -41
- package/v2Components/HtmlEditor/utils/htmlValidator.js +71 -72
- package/v2Components/HtmlEditor/utils/liquidTemplateSupport.js +158 -124
- package/v2Components/HtmlEditor/utils/properSyntaxHighlighting.js +23 -25
- package/v2Components/HtmlEditor/utils/validationAdapter.js +66 -41
- package/v2Components/HtmlEditor/utils/validationConstants.js +38 -0
- package/v2Components/MobilePushPreviewV2/constants.js +6 -0
- package/v2Components/MobilePushPreviewV2/index.js +33 -7
- package/v2Components/TemplatePreview/_templatePreview.scss +55 -24
- package/v2Components/TemplatePreview/index.js +47 -32
- package/v2Components/TemplatePreview/messages.js +4 -0
- package/v2Components/TestAndPreviewSlidebox/_testAndPreviewSlidebox.scss +1 -0
- package/v2Containers/BeeEditor/index.js +172 -90
- package/v2Containers/BeePopupEditor/_beePopupEditor.scss +14 -0
- package/v2Containers/BeePopupEditor/constants.js +10 -0
- package/v2Containers/BeePopupEditor/index.js +194 -0
- package/v2Containers/BeePopupEditor/tests/index.test.js +627 -0
- package/v2Containers/CreativesContainer/SlideBoxContent.js +127 -51
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +156 -13
- package/v2Containers/CreativesContainer/SlideBoxHeader.js +2 -1
- package/v2Containers/CreativesContainer/constants.js +1 -0
- package/v2Containers/CreativesContainer/index.js +251 -47
- package/v2Containers/CreativesContainer/messages.js +8 -0
- package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +11 -2
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +38 -50
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +103 -0
- package/v2Containers/Email/actions.js +7 -0
- package/v2Containers/Email/constants.js +5 -1
- package/v2Containers/Email/index.js +234 -29
- package/v2Containers/Email/messages.js +32 -0
- package/v2Containers/Email/reducer.js +12 -1
- package/v2Containers/Email/sagas.js +61 -7
- package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +2 -0
- package/v2Containers/Email/tests/reducer.test.js +46 -0
- package/v2Containers/Email/tests/sagas.test.js +320 -29
- package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +1246 -0
- package/v2Containers/EmailWrapper/components/EmailWrapperView.js +212 -21
- package/v2Containers/EmailWrapper/components/HTMLEditorTesting.js +40 -74
- package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +2614 -0
- package/v2Containers/EmailWrapper/components/__tests__/EmailWrapperView.test.js +520 -0
- package/v2Containers/EmailWrapper/components/__tests__/HTMLEditorTesting.test.js +2 -67
- package/v2Containers/EmailWrapper/constants.js +2 -0
- package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +627 -79
- package/v2Containers/EmailWrapper/index.js +103 -23
- package/v2Containers/EmailWrapper/messages.js +65 -1
- package/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +955 -0
- package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +596 -82
- package/v2Containers/InApp/__tests__/InAppHTMLEditor.test.js +376 -0
- package/v2Containers/InApp/__tests__/sagas.test.js +363 -0
- package/v2Containers/InApp/actions.js +7 -0
- package/v2Containers/InApp/constants.js +20 -4
- package/v2Containers/InApp/index.js +802 -360
- package/v2Containers/InApp/index.scss +4 -3
- package/v2Containers/InApp/messages.js +7 -3
- package/v2Containers/InApp/reducer.js +21 -3
- package/v2Containers/InApp/sagas.js +29 -9
- package/v2Containers/InApp/selectors.js +25 -5
- package/v2Containers/InApp/tests/index.test.js +154 -50
- package/v2Containers/InApp/tests/reducer.test.js +34 -0
- package/v2Containers/InApp/tests/sagas.test.js +61 -9
- package/v2Containers/InApp/tests/selectors.test.js +612 -0
- package/v2Containers/InAppWrapper/components/InAppWrapperView.js +151 -0
- package/v2Containers/InAppWrapper/components/__tests__/InAppWrapperView.test.js +267 -0
- package/v2Containers/InAppWrapper/components/inAppWrapperView.scss +23 -0
- package/v2Containers/InAppWrapper/constants.js +16 -0
- package/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +473 -0
- package/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +198 -0
- package/v2Containers/InAppWrapper/index.js +148 -0
- package/v2Containers/InAppWrapper/messages.js +49 -0
- package/v2Containers/InappAdvance/index.js +1099 -0
- package/v2Containers/InappAdvance/index.scss +10 -0
- package/v2Containers/InappAdvance/tests/index.test.js +448 -0
- package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +3 -0
- package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/index.test.js.snap +2 -0
- package/v2Containers/Line/Container/Wrapper/tests/__snapshots__/index.test.js.snap +2 -0
- package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +9 -0
- package/v2Containers/MobilePush/Create/index.js +1 -1
- package/v2Containers/MobilePush/Edit/index.js +10 -6
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +12 -0
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4 -0
- package/v2Containers/TagList/index.js +62 -19
- package/v2Containers/Templates/_templates.scss +60 -1
- package/v2Containers/Templates/index.js +89 -4
- package/v2Containers/Templates/messages.js +4 -0
- package/v2Containers/TemplatesV2/TemplatesV2.style.js +4 -2
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +34 -0
- package/v2Components/HtmlEditor/components/ValidationErrorDisplay/__tests__/index.test.js +0 -152
- package/v2Containers/EmailWrapper/tests/EmailWrapperView.test.js +0 -214
|
@@ -8,33 +8,44 @@
|
|
|
8
8
|
* - Theme support with proper styling
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import React, {
|
|
11
|
+
import React, {
|
|
12
|
+
forwardRef, useImperativeHandle, useRef, useEffect, useCallback,
|
|
13
|
+
} from 'react';
|
|
12
14
|
import PropTypes from 'prop-types';
|
|
13
15
|
|
|
14
16
|
// CodeMirror 6 imports
|
|
15
17
|
import { EditorState } from '@codemirror/state';
|
|
16
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
EditorView, lineNumbers, highlightActiveLine, placeholder,
|
|
20
|
+
} from '@codemirror/view';
|
|
17
21
|
|
|
18
|
-
//
|
|
19
|
-
import { createRobustExtensions } from '../../utils/properSyntaxHighlighting';
|
|
22
|
+
// Define Theme and Highlighting inline to avoid "multiple instances of @codemirror/state" error
|
|
20
23
|
|
|
21
24
|
|
|
22
25
|
import { injectIntl, intlShape } from 'react-intl';
|
|
23
26
|
|
|
24
27
|
// Messages
|
|
28
|
+
import CapRow from '@capillarytech/cap-ui-library/CapRow';
|
|
29
|
+
import { createRobustExtensions } from '../../utils/properSyntaxHighlighting';
|
|
25
30
|
import messages from '../../messages';
|
|
26
31
|
|
|
27
32
|
// Cap UI Components
|
|
28
|
-
import CapRow from '@capillarytech/cap-ui-library/CapRow';
|
|
29
33
|
|
|
30
34
|
// Components
|
|
31
35
|
import TagList from '../../../../v2Containers/TagList';
|
|
36
|
+
import ValidationErrorDisplay from '../ValidationErrorDisplay';
|
|
37
|
+
|
|
38
|
+
// Constants - removed unused imports since tag fetching is handled by parent
|
|
32
39
|
|
|
33
40
|
// Context
|
|
34
41
|
import { useEditorContext } from '../common/EditorContext';
|
|
35
42
|
|
|
36
43
|
// Styles
|
|
37
44
|
import './_codeEditorPane.scss';
|
|
45
|
+
import { INAPP } from '../../../../constants/unified';
|
|
46
|
+
|
|
47
|
+
// Define Theme and Highlighting inline to avoid "multiple instances of @codemirror/state" error
|
|
48
|
+
|
|
38
49
|
|
|
39
50
|
// Legacy CodeMirrorEditor removed - using enhanced implementation only
|
|
40
51
|
|
|
@@ -42,12 +53,25 @@ const CodeEditorPaneComponent = ({
|
|
|
42
53
|
intl,
|
|
43
54
|
readOnly = false,
|
|
44
55
|
className = '',
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
56
|
+
forwardedRef,
|
|
57
|
+
// Tag-related props - tags are fetched and managed by parent component
|
|
58
|
+
tags = [],
|
|
59
|
+
injectedTags = {},
|
|
60
|
+
location,
|
|
61
|
+
eventContextTags = [],
|
|
62
|
+
selectedOfferDetails = [],
|
|
63
|
+
channel,
|
|
64
|
+
userLocale = 'en',
|
|
65
|
+
moduleFilterEnabled = true,
|
|
66
|
+
onTagContextChange,
|
|
67
|
+
// Validation props
|
|
68
|
+
onErrorClick,
|
|
48
69
|
}) => {
|
|
49
|
-
const
|
|
50
|
-
const {
|
|
70
|
+
const context = useEditorContext();
|
|
71
|
+
const {
|
|
72
|
+
content, validation, variant, isLiquidEnabled,
|
|
73
|
+
} = context || {};
|
|
74
|
+
const { content: contentValue, updateContent } = content || {};
|
|
51
75
|
const editorRef = useRef(null);
|
|
52
76
|
const viewRef = useRef(null);
|
|
53
77
|
|
|
@@ -61,7 +85,7 @@ const CodeEditorPaneComponent = ({
|
|
|
61
85
|
get view() {
|
|
62
86
|
return viewRef.current;
|
|
63
87
|
},
|
|
64
|
-
viewRef
|
|
88
|
+
viewRef, // For compatibility with existing code
|
|
65
89
|
|
|
66
90
|
focus: () => {
|
|
67
91
|
if (viewRef.current) {
|
|
@@ -75,7 +99,7 @@ const CodeEditorPaneComponent = ({
|
|
|
75
99
|
const pos = position !== undefined ? position : head;
|
|
76
100
|
view.dispatch({
|
|
77
101
|
changes: { from: pos, insert: text },
|
|
78
|
-
selection: { anchor: pos + text.length }
|
|
102
|
+
selection: { anchor: pos + text.length },
|
|
79
103
|
});
|
|
80
104
|
} else {
|
|
81
105
|
throw new Error('CodeMirror view not initialized');
|
|
@@ -88,9 +112,7 @@ const CodeEditorPaneComponent = ({
|
|
|
88
112
|
}
|
|
89
113
|
return 0;
|
|
90
114
|
},
|
|
91
|
-
getValue: () =>
|
|
92
|
-
return contentValue || '';
|
|
93
|
-
},
|
|
115
|
+
getValue: () => contentValue || '',
|
|
94
116
|
setValue: (value) => {
|
|
95
117
|
updateContent(value);
|
|
96
118
|
},
|
|
@@ -107,7 +129,7 @@ const CodeEditorPaneComponent = ({
|
|
|
107
129
|
|
|
108
130
|
view.dispatch({
|
|
109
131
|
selection: { anchor: pos },
|
|
110
|
-
effects: EditorView.scrollIntoView(pos, { y: 'center' })
|
|
132
|
+
effects: EditorView.scrollIntoView(pos, { y: 'center' }),
|
|
111
133
|
});
|
|
112
134
|
view.focus();
|
|
113
135
|
} catch (error) {
|
|
@@ -117,7 +139,7 @@ const CodeEditorPaneComponent = ({
|
|
|
117
139
|
}
|
|
118
140
|
}
|
|
119
141
|
}
|
|
120
|
-
}
|
|
142
|
+
},
|
|
121
143
|
}), [contentValue]);
|
|
122
144
|
|
|
123
145
|
// Note: handleContentChange removed - using updateContentRef directly in CodeMirror
|
|
@@ -133,7 +155,9 @@ const CodeEditorPaneComponent = ({
|
|
|
133
155
|
if (typeof tagData === 'string') {
|
|
134
156
|
tagText = tagData;
|
|
135
157
|
} else if (tagData) {
|
|
136
|
-
const {
|
|
158
|
+
const {
|
|
159
|
+
text, name, label, value,
|
|
160
|
+
} = tagData;
|
|
137
161
|
tagText = text || name || label || value;
|
|
138
162
|
if (!tagText) {
|
|
139
163
|
console.warn('Invalid tag data:', tagData);
|
|
@@ -147,49 +171,69 @@ const CodeEditorPaneComponent = ({
|
|
|
147
171
|
// For unified HTML editor, insert as template variable
|
|
148
172
|
const formattedTag = `{{${tagText}}}`;
|
|
149
173
|
|
|
150
|
-
// Insert the tag at cursor position
|
|
174
|
+
// Insert the tag at cursor position directly
|
|
151
175
|
view.dispatch({
|
|
152
176
|
changes: { from: pos, insert: formattedTag },
|
|
153
|
-
selection: { anchor: pos + formattedTag.length }
|
|
177
|
+
selection: { anchor: pos + formattedTag.length },
|
|
154
178
|
});
|
|
155
179
|
|
|
156
180
|
// Focus back to editor
|
|
157
181
|
view.focus();
|
|
158
182
|
|
|
159
|
-
//
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
183
|
+
// Note: We don't call onLabelInsert here because:
|
|
184
|
+
// 1. The tag is already inserted directly into the editor
|
|
185
|
+
// 2. onLabelInsert (handleLabelInsert from HTMLEditor) would try to insert again
|
|
186
|
+
// 3. This causes "Editor method not available" error
|
|
187
|
+
// The direct insertion via view.dispatch is sufficient
|
|
163
188
|
}
|
|
164
189
|
};
|
|
165
190
|
|
|
191
|
+
// Handle tag context change - delegate to parent component
|
|
192
|
+
// Tags are fetched in parent components (EmailHTMLEditor, INAPP, etc.)
|
|
193
|
+
// This component just passes the context change event up
|
|
194
|
+
const handleTagContextChange = useCallback((data) => {
|
|
195
|
+
if (onTagContextChange) {
|
|
196
|
+
// Parent component handles tag fetching and updates
|
|
197
|
+
onTagContextChange(data);
|
|
198
|
+
}
|
|
199
|
+
// No fallback - tags must be managed by parent component
|
|
200
|
+
}, [onTagContextChange]);
|
|
201
|
+
|
|
166
202
|
// Initialize CodeMirror effect
|
|
167
203
|
useEffect(() => {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
204
|
+
// Ensure editorRef.current is a valid DOM element before initializing CodeMirror
|
|
205
|
+
if (editorRef.current && editorRef.current instanceof HTMLElement && !viewRef.current) {
|
|
206
|
+
// Determine placeholder text based on channel
|
|
207
|
+
let placeholderText = intl.formatMessage(messages.editorPlaceholderEmail); // Default to email
|
|
208
|
+
if (channel === INAPP || channel === INAPP.toUpperCase()) {
|
|
209
|
+
placeholderText = intl.formatMessage(messages.editorPlaceholderInapp);
|
|
210
|
+
}
|
|
211
|
+
// Use the comprehensive extensions from properSyntaxHighlighting.js
|
|
212
|
+
// This includes: html(), syntaxHighlighting(comprehensiveVSCodeTheme), cleanEditorTheme
|
|
213
|
+
const robustExtensions = createRobustExtensions();
|
|
214
|
+
|
|
215
|
+
// Add additional extensions for line numbers, active line, placeholder, line wrapping, and update listener
|
|
216
|
+
const extensions = [
|
|
217
|
+
lineNumbers(),
|
|
218
|
+
highlightActiveLine(),
|
|
219
|
+
placeholder(placeholderText),
|
|
220
|
+
EditorView.lineWrapping, // Enable soft-wrapping of long lines
|
|
221
|
+
...robustExtensions, // Spread the robust extensions (html, syntax highlighting, theme)
|
|
222
|
+
EditorView.updateListener.of((update) => {
|
|
223
|
+
if (update.docChanged) {
|
|
224
|
+
updateContentRef.current(update.state.doc.toString());
|
|
225
|
+
}
|
|
226
|
+
}),
|
|
227
|
+
];
|
|
184
228
|
|
|
185
229
|
const state = EditorState.create({
|
|
186
230
|
doc: contentValue || '',
|
|
187
|
-
extensions
|
|
231
|
+
extensions,
|
|
188
232
|
});
|
|
189
233
|
|
|
190
234
|
viewRef.current = new EditorView({
|
|
191
235
|
state,
|
|
192
|
-
parent: editorRef.current
|
|
236
|
+
parent: editorRef.current,
|
|
193
237
|
});
|
|
194
238
|
}
|
|
195
239
|
|
|
@@ -204,116 +248,70 @@ const CodeEditorPaneComponent = ({
|
|
|
204
248
|
viewRef.current = null;
|
|
205
249
|
}
|
|
206
250
|
};
|
|
207
|
-
}, []);
|
|
251
|
+
}, [channel, intl]);
|
|
208
252
|
|
|
209
253
|
// Update editor content when content changes
|
|
210
254
|
useEffect(() => {
|
|
211
|
-
if (viewRef.current && contentValue !== viewRef.current.state.doc.toString()) {
|
|
255
|
+
if (viewRef.current && contentValue && contentValue !== viewRef.current.state.doc.toString()) {
|
|
212
256
|
const { current: view } = viewRef;
|
|
213
257
|
const { state: { doc: { length } } } = view;
|
|
214
258
|
view.dispatch({
|
|
215
259
|
changes: {
|
|
216
260
|
from: 0,
|
|
217
261
|
to: length,
|
|
218
|
-
insert: contentValue || ''
|
|
219
|
-
}
|
|
262
|
+
insert: contentValue || '',
|
|
263
|
+
},
|
|
220
264
|
});
|
|
221
265
|
}
|
|
222
266
|
}, [contentValue]);
|
|
223
267
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
'customer.phone': {
|
|
264
|
-
name: 'Phone',
|
|
265
|
-
desc: 'Customer phone number',
|
|
266
|
-
resolved: true
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
},
|
|
270
|
-
'Common Tags': {
|
|
271
|
-
name: 'Common Tags',
|
|
272
|
-
desc: 'Commonly used template tags',
|
|
273
|
-
resolved: true,
|
|
274
|
-
'tag-header': true,
|
|
275
|
-
subtags: {
|
|
276
|
-
'organization.name': {
|
|
277
|
-
name: 'Organization Name',
|
|
278
|
-
desc: 'Organization name',
|
|
279
|
-
resolved: true
|
|
280
|
-
},
|
|
281
|
-
'currentDate': {
|
|
282
|
-
name: 'Current Date',
|
|
283
|
-
desc: 'Current date',
|
|
284
|
-
resolved: true
|
|
285
|
-
},
|
|
286
|
-
'unsubscribeLink': {
|
|
287
|
-
name: 'Unsubscribe Link',
|
|
288
|
-
desc: 'Unsubscribe link',
|
|
289
|
-
resolved: true
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
}}
|
|
294
|
-
moduleFilterEnabled={true}
|
|
295
|
-
userLocale="en"
|
|
296
|
-
channel="email"
|
|
297
|
-
disabled={readOnly}
|
|
298
|
-
location={{
|
|
299
|
-
query: {
|
|
300
|
-
type: 'html-editor' // Identify the context
|
|
301
|
-
}
|
|
302
|
-
}}
|
|
303
|
-
selectedOfferDetails={[]}
|
|
304
|
-
eventContextTags={[]}
|
|
305
|
-
/>
|
|
306
|
-
</CapRow>
|
|
307
|
-
</div>
|
|
308
|
-
</CapRow>
|
|
309
|
-
|
|
310
|
-
</CapRow>
|
|
311
|
-
);
|
|
268
|
+
return (
|
|
269
|
+
<CapRow className={`code-editor-pane ${className}`}>
|
|
270
|
+
{/* Code Editor Content - Stacked vertically with validation */}
|
|
271
|
+
<div className="code-editor-pane__wrapper">
|
|
272
|
+
{/* Code Editor with Floating Add Label Button */}
|
|
273
|
+
<div className="codemirror-wrapper">
|
|
274
|
+
<div ref={editorRef} className="codemirror-editor" />
|
|
275
|
+
{/* Floating Add Label Button */}
|
|
276
|
+
<CapRow className="code-editor-pane__actions">
|
|
277
|
+
<TagList
|
|
278
|
+
key="html-editor-taglist"
|
|
279
|
+
label={intl.formatMessage(messages.addLabel)}
|
|
280
|
+
onTagSelect={handleTagSelect}
|
|
281
|
+
onContextChange={handleTagContextChange}
|
|
282
|
+
className="tag-list-trigger"
|
|
283
|
+
tags={tags}
|
|
284
|
+
injectedTags={injectedTags}
|
|
285
|
+
moduleFilterEnabled={moduleFilterEnabled}
|
|
286
|
+
userLocale={userLocale}
|
|
287
|
+
channel={channel}
|
|
288
|
+
disabled={readOnly}
|
|
289
|
+
location={location}
|
|
290
|
+
selectedOfferDetails={selectedOfferDetails}
|
|
291
|
+
eventContextTags={eventContextTags}
|
|
292
|
+
popoverPlacement="rightTop"
|
|
293
|
+
/>
|
|
294
|
+
</CapRow>
|
|
295
|
+
</div>
|
|
296
|
+
|
|
297
|
+
{/* Validation Error Display - Below editor, always visible */}
|
|
298
|
+
<ValidationErrorDisplay
|
|
299
|
+
validation={validation}
|
|
300
|
+
onErrorClick={onErrorClick}
|
|
301
|
+
isLiquidEnabled={isLiquidEnabled}
|
|
302
|
+
className="code-editor-pane__validation"
|
|
303
|
+
/>
|
|
304
|
+
</div>
|
|
305
|
+
</CapRow>
|
|
306
|
+
);
|
|
312
307
|
};
|
|
313
308
|
|
|
314
|
-
//
|
|
309
|
+
// Apply injectIntl to the component first, then wrap with forwardRef
|
|
310
|
+
const CodeEditorPaneWithIntl = injectIntl(CodeEditorPaneComponent);
|
|
311
|
+
|
|
312
|
+
// Create the forwardRef wrapper that forwards ref to the intl-wrapped component
|
|
315
313
|
const CodeEditorPane = forwardRef((props, ref) => (
|
|
316
|
-
<
|
|
314
|
+
<CodeEditorPaneWithIntl {...props} forwardedRef={ref} />
|
|
317
315
|
));
|
|
318
316
|
|
|
319
317
|
CodeEditorPane.displayName = 'CodeEditorPane';
|
|
@@ -323,9 +321,19 @@ CodeEditorPane.propTypes = {
|
|
|
323
321
|
readOnly: PropTypes.bool,
|
|
324
322
|
className: PropTypes.string,
|
|
325
323
|
isFullscreenMode: PropTypes.bool,
|
|
326
|
-
onLabelInsert: PropTypes.func
|
|
324
|
+
onLabelInsert: PropTypes.func,
|
|
325
|
+
onErrorClick: PropTypes.func,
|
|
326
|
+
// Tag-related props - tags are fetched and managed by parent component
|
|
327
|
+
tags: PropTypes.array,
|
|
328
|
+
injectedTags: PropTypes.object,
|
|
329
|
+
location: PropTypes.object,
|
|
330
|
+
eventContextTags: PropTypes.array,
|
|
331
|
+
selectedOfferDetails: PropTypes.array,
|
|
332
|
+
channel: PropTypes.string,
|
|
333
|
+
userLocale: PropTypes.string,
|
|
334
|
+
moduleFilterEnabled: PropTypes.bool,
|
|
335
|
+
onTagContextChange: PropTypes.func, // Required - parent must handle tag fetching
|
|
327
336
|
};
|
|
328
337
|
|
|
329
|
-
// Export
|
|
330
|
-
export default
|
|
331
|
-
|
|
338
|
+
// Export the forwardRef-wrapped component
|
|
339
|
+
export default CodeEditorPane;
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
.html-editor .device-toggle {
|
|
10
10
|
display: flex;
|
|
11
11
|
align-items: center;
|
|
12
|
-
|
|
12
|
+
margin-left: 2.5rem;
|
|
13
13
|
padding: 0;
|
|
14
14
|
background-color: $CAP_G10;
|
|
15
15
|
border-radius: 0.25rem 0.25rem 0 0;
|
|
@@ -220,6 +220,7 @@
|
|
|
220
220
|
|
|
221
221
|
// Integration with editor toolbar
|
|
222
222
|
.html-editor.html-editor--inapp {
|
|
223
|
+
margin-top: 4%;
|
|
223
224
|
.editor-toolbar {
|
|
224
225
|
padding: 0 1rem 0 0; // Remove left padding to align with device toggle
|
|
225
226
|
background-color: $CAP_G10;
|
|
@@ -32,7 +32,7 @@ const DeviceToggle = ({
|
|
|
32
32
|
onDeviceChange,
|
|
33
33
|
keepContentSame = false,
|
|
34
34
|
onKeepContentSameChange,
|
|
35
|
-
className = ''
|
|
35
|
+
className = '',
|
|
36
36
|
}) => {
|
|
37
37
|
const handleDeviceClick = (device) => {
|
|
38
38
|
if (onDeviceChange && device !== activeDevice) {
|
|
@@ -97,7 +97,7 @@ DeviceToggle.propTypes = {
|
|
|
97
97
|
onDeviceChange: PropTypes.func,
|
|
98
98
|
keepContentSame: PropTypes.bool,
|
|
99
99
|
onKeepContentSameChange: PropTypes.func,
|
|
100
|
-
className: PropTypes.string
|
|
100
|
+
className: PropTypes.string,
|
|
101
101
|
};
|
|
102
102
|
|
|
103
103
|
DeviceToggle.defaultProps = {
|
|
@@ -105,7 +105,7 @@ DeviceToggle.defaultProps = {
|
|
|
105
105
|
onDeviceChange: null,
|
|
106
106
|
keepContentSame: false,
|
|
107
107
|
onKeepContentSameChange: null,
|
|
108
|
-
className: ''
|
|
108
|
+
className: '',
|
|
109
109
|
};
|
|
110
110
|
|
|
111
111
|
export default injectIntl(DeviceToggle);
|
|
@@ -15,11 +15,11 @@
|
|
|
15
15
|
border-radius: 0.25rem 0.25rem 0 0;
|
|
16
16
|
box-sizing: border-box;
|
|
17
17
|
flex-shrink: 0;
|
|
18
|
+
position: relative;
|
|
18
19
|
|
|
19
20
|
&__left {
|
|
20
21
|
display: flex;
|
|
21
22
|
align-items: center;
|
|
22
|
-
gap: 1rem;
|
|
23
23
|
flex-shrink: 0;
|
|
24
24
|
}
|
|
25
25
|
|
|
@@ -110,4 +110,12 @@
|
|
|
110
110
|
display: none;
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Library mode override for InApp variant - Position absolute for toolbar
|
|
116
|
+
// These selectors target the toolbar when inside library mode InApp variant
|
|
117
|
+
.html-editor--inapp.html-editor--library-mode .html-editor__header .editor-toolbar,
|
|
118
|
+
.html-editor--inapp.html-editor--library-mode .html-editor__header .editor-toolbar.editor-toolbar,
|
|
119
|
+
.html-editor--inapp.html-editor--library-mode .html-editor__header .ant-layout-header.editor-toolbar {
|
|
120
|
+
position: absolute;
|
|
113
121
|
}
|
|
@@ -12,17 +12,19 @@
|
|
|
12
12
|
import React from 'react';
|
|
13
13
|
import PropTypes from 'prop-types';
|
|
14
14
|
import { Layout, Typography } from 'antd';
|
|
15
|
-
import { injectIntl, intlShape } from 'react-intl';
|
|
15
|
+
import { FormattedMessage, injectIntl, intlShape } from 'react-intl';
|
|
16
16
|
|
|
17
17
|
import CapButton from '@capillarytech/cap-ui-library/CapButton';
|
|
18
18
|
import CapIcon from '@capillarytech/cap-ui-library/CapIcon';
|
|
19
19
|
import CapRow from '@capillarytech/cap-ui-library/CapRow';
|
|
20
|
+
import CapTooltipWithInfo from '@capillarytech/cap-ui-library/CapTooltipWithInfo';
|
|
21
|
+
import { FONT_COLOR_01, CAP_SPACE_08 } from '@capillarytech/cap-ui-library/styled/variables';
|
|
20
22
|
|
|
21
23
|
// Component imports
|
|
22
24
|
import { useEditorContext } from '../common/EditorContext';
|
|
23
25
|
|
|
24
26
|
// Constants
|
|
25
|
-
import { HTML_EDITOR_VARIANTS } from '../../constants';
|
|
27
|
+
import { HTML_EDITOR_DOC_URL, HTML_EDITOR_VARIANTS } from '../../constants';
|
|
26
28
|
|
|
27
29
|
// Styles
|
|
28
30
|
import './_editorToolbar.scss';
|
|
@@ -42,7 +44,7 @@ const EditorToolbar = ({
|
|
|
42
44
|
className = '',
|
|
43
45
|
isFullscreenMode = false,
|
|
44
46
|
variant = HTML_EDITOR_VARIANTS.EMAIL,
|
|
45
|
-
showTitle = true
|
|
47
|
+
showTitle = true,
|
|
46
48
|
}) => {
|
|
47
49
|
// Context hooks - enabling for Step 10 testing
|
|
48
50
|
const { layout, content } = useEditorContext();
|
|
@@ -66,6 +68,29 @@ const EditorToolbar = ({
|
|
|
66
68
|
: intl.formatMessage(messages.htmlEditor)
|
|
67
69
|
}
|
|
68
70
|
</Text>
|
|
71
|
+
<CapTooltipWithInfo
|
|
72
|
+
title={(
|
|
73
|
+
<FormattedMessage
|
|
74
|
+
{...messages.htmlEditorTooltip}
|
|
75
|
+
values={{
|
|
76
|
+
docLink: (
|
|
77
|
+
<a
|
|
78
|
+
href={HTML_EDITOR_DOC_URL}
|
|
79
|
+
target="_blank"
|
|
80
|
+
rel="noopener noreferrer"
|
|
81
|
+
>
|
|
82
|
+
<FormattedMessage {...messages.viewDocumentation} />
|
|
83
|
+
</a>
|
|
84
|
+
),
|
|
85
|
+
}}
|
|
86
|
+
/>
|
|
87
|
+
)}
|
|
88
|
+
infoIconProps={{
|
|
89
|
+
style: { marginLeft: CAP_SPACE_08, color: FONT_COLOR_01 },
|
|
90
|
+
}}
|
|
91
|
+
autoAdjustOverflow
|
|
92
|
+
placement="top"
|
|
93
|
+
/>
|
|
69
94
|
</CapRow>
|
|
70
95
|
)}
|
|
71
96
|
|
|
@@ -81,7 +106,7 @@ const EditorToolbar = ({
|
|
|
81
106
|
)}
|
|
82
107
|
aria-pressed={isFullscreenMode}
|
|
83
108
|
>
|
|
84
|
-
<CapIcon type={isFullscreenMode ? "
|
|
109
|
+
<CapIcon type={isFullscreenMode ? "collapse2" : "expander"} size="m" />
|
|
85
110
|
</CapButton>
|
|
86
111
|
)}
|
|
87
112
|
</CapRow>
|
|
@@ -98,7 +123,7 @@ EditorToolbar.propTypes = {
|
|
|
98
123
|
className: PropTypes.string,
|
|
99
124
|
isFullscreenMode: PropTypes.bool,
|
|
100
125
|
variant: PropTypes.oneOf(Object.values(HTML_EDITOR_VARIANTS)),
|
|
101
|
-
showTitle: PropTypes.bool
|
|
126
|
+
showTitle: PropTypes.bool,
|
|
102
127
|
};
|
|
103
128
|
|
|
104
129
|
EditorToolbar.defaultProps = {
|
|
@@ -109,7 +134,7 @@ EditorToolbar.defaultProps = {
|
|
|
109
134
|
className: '',
|
|
110
135
|
isFullscreenMode: false,
|
|
111
136
|
variant: HTML_EDITOR_VARIANTS.EMAIL,
|
|
112
|
-
showTitle: true
|
|
137
|
+
showTitle: true,
|
|
113
138
|
};
|
|
114
139
|
|
|
115
140
|
export default injectIntl(EditorToolbar);
|
|
@@ -49,9 +49,31 @@ body .ant-modal-mask+.ant-modal-wrap .ant-modal.html-editor-fullscreen-modal .an
|
|
|
49
49
|
padding: 0;
|
|
50
50
|
min-height: 3.25rem;
|
|
51
51
|
height: 3.25rem;
|
|
52
|
+
position: relative;
|
|
52
53
|
|
|
53
54
|
&__right {
|
|
54
55
|
margin-left: auto;
|
|
55
56
|
}
|
|
56
57
|
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Library mode overrides for INAPP variant - Position absolute for toolbar
|
|
61
|
+
// These selectors match the high-specificity patterns above but include library-mode class
|
|
62
|
+
.html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .html-editor__header--fullscreen .editor-toolbar,
|
|
63
|
+
.html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .html-editor__header--fullscreen .editor-toolbar.editor-toolbar,
|
|
64
|
+
.html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .html-editor__header--fullscreen .ant-layout-header.editor-toolbar,
|
|
65
|
+
.html-editor.html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .html-editor__header--fullscreen .editor-toolbar,
|
|
66
|
+
.html-editor.html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .html-editor__header--fullscreen .editor-toolbar.editor-toolbar,
|
|
67
|
+
.html-editor.html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .html-editor__header--fullscreen .ant-layout-header.editor-toolbar,
|
|
68
|
+
.html-editor__header--fullscreen.html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .editor-toolbar,
|
|
69
|
+
.html-editor__header.html-editor__header--fullscreen.html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .editor-toolbar,
|
|
70
|
+
.html-editor__header.html-editor__header--fullscreen.html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .editor-toolbar.editor-toolbar,
|
|
71
|
+
.html-editor__header.html-editor__header--fullscreen.html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .ant-layout-header.editor-toolbar,
|
|
72
|
+
.ant-modal .html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .html-editor__header--fullscreen .editor-toolbar,
|
|
73
|
+
.ant-modal .html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .html-editor__header--fullscreen .editor-toolbar.editor-toolbar,
|
|
74
|
+
.ant-modal .html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .html-editor__header--fullscreen .ant-layout-header.editor-toolbar,
|
|
75
|
+
.ant-modal-content .html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .html-editor__header--fullscreen .editor-toolbar,
|
|
76
|
+
.ant-modal-content .html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .html-editor__header--fullscreen .editor-toolbar.editor-toolbar,
|
|
77
|
+
.ant-modal-content .html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .html-editor__header--fullscreen .ant-layout-header.editor-toolbar {
|
|
78
|
+
position: absolute;
|
|
57
79
|
}
|
|
@@ -14,9 +14,7 @@ const DeviceFrame = ({
|
|
|
14
14
|
className = '',
|
|
15
15
|
...props
|
|
16
16
|
}) => {
|
|
17
|
-
const getFrameAsset = () =>
|
|
18
|
-
return device === DEVICE_TYPES.IOS ? iosFrame : androidFrame;
|
|
19
|
-
};
|
|
17
|
+
const getFrameAsset = () => device === DEVICE_TYPES.IOS ? iosFrame : androidFrame;
|
|
20
18
|
|
|
21
19
|
const getFrameStyles = () => {
|
|
22
20
|
const baseStyles = {
|
|
@@ -26,14 +24,13 @@ const DeviceFrame = ({
|
|
|
26
24
|
backgroundRepeat: 'no-repeat',
|
|
27
25
|
backgroundPosition: 'center',
|
|
28
26
|
backgroundSize: 'contain',
|
|
29
|
-
filter: 'drop-shadow(0 12px 48px rgba(0, 0, 0, 0.25)) brightness(1.05) contrast(1.1)'
|
|
30
27
|
};
|
|
31
28
|
|
|
32
29
|
// Unified dimensions - both devices use the same size since assets are identical
|
|
33
30
|
return {
|
|
34
31
|
...baseStyles,
|
|
35
32
|
width: '450px',
|
|
36
|
-
height: '920px'
|
|
33
|
+
height: '920px',
|
|
37
34
|
};
|
|
38
35
|
};
|
|
39
36
|
|
|
@@ -45,7 +42,7 @@ const DeviceFrame = ({
|
|
|
45
42
|
{...props}
|
|
46
43
|
>
|
|
47
44
|
|
|
48
|
-
|
|
45
|
+
{children}
|
|
49
46
|
|
|
50
47
|
</div>
|
|
51
48
|
);
|
|
@@ -54,7 +51,7 @@ const DeviceFrame = ({
|
|
|
54
51
|
DeviceFrame.propTypes = {
|
|
55
52
|
device: PropTypes.oneOf(Object.values(DEVICE_TYPES)),
|
|
56
53
|
children: PropTypes.node,
|
|
57
|
-
className: PropTypes.string
|
|
54
|
+
className: PropTypes.string,
|
|
58
55
|
};
|
|
59
56
|
|
|
60
57
|
export default DeviceFrame;
|