@capillarytech/creatives-library 8.0.359 → 8.0.360-alpha.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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@capillarytech/creatives-library",
3
3
  "author": "meharaj",
4
- "version": "8.0.359",
4
+ "version": "8.0.360-alpha.0",
5
5
  "description": "Capillary creatives ui",
6
6
  "main": "./index.js",
7
7
  "module": "./index.es.js",
@@ -0,0 +1,132 @@
1
+ import React, { useLayoutEffect, useMemo, useRef, useState } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import CapRow from '@capillarytech/cap-ui-library/CapRow';
4
+ import CapLabel from '@capillarytech/cap-ui-library/CapLabel';
5
+ import CapImage from '@capillarytech/cap-ui-library/CapImage';
6
+
7
+ const getTrimmedText = (value = '') => (value ?? '').trim();
8
+ const hasTrimmedText = (value = '') => Boolean(getTrimmedText(value));
9
+
10
+ const getCarouselButtonsWithTitles = (card) => (
11
+ (card?.buttons ?? []).filter((button) => hasTrimmedText(button?.title)).slice(0, 2)
12
+ );
13
+
14
+ const getCarouselButtonSlotCount = (cards = []) => {
15
+ const buttonCounts = cards.map((card) => getCarouselButtonsWithTitles(card).length);
16
+ return Math.min(2, Math.max(0, ...buttonCounts, 0));
17
+ };
18
+
19
+ const ViberCarouselPreviewCards = ({ cards = [] }) => {
20
+ const previewCards = Array.isArray(cards) && cards.length ? cards : [{}];
21
+ const carouselButtonSlotCount = useMemo(
22
+ () => getCarouselButtonSlotCount(previewCards),
23
+ [previewCards],
24
+ );
25
+ const carouselTitleRefs = useRef([]);
26
+ const [carouselTitleLineCount, setCarouselTitleLineCount] = useState(1);
27
+
28
+ carouselTitleRefs.current = previewCards.map(() => null);
29
+
30
+ useLayoutEffect(() => {
31
+ let maxLines = 1;
32
+ carouselTitleRefs.current.forEach((node) => {
33
+ if (!node) {
34
+ return;
35
+ }
36
+ const { lineHeight } = window.getComputedStyle(node);
37
+ const parsedLineHeight = parseFloat(lineHeight) || 18;
38
+ const lines = Math.min(2, Math.max(1, Math.round(node.scrollHeight / parsedLineHeight)));
39
+ if (lines > maxLines) {
40
+ maxLines = lines;
41
+ }
42
+ });
43
+ setCarouselTitleLineCount((prev) => (prev === maxLines ? prev : maxLines));
44
+ }, [previewCards]);
45
+
46
+ return (
47
+ <CapRow className="viber-carousel-preview-scroll">
48
+ {previewCards.map((card, index) => {
49
+ const cardButtons = getCarouselButtonsWithTitles(card);
50
+ const titleLineClass = `viber-carousel-preview-text-wrap--${carouselTitleLineCount}-line`;
51
+
52
+ return (
53
+ <CapRow className="viber-carousel-preview-card" key={`viber-carousel-preview-card-${index}`}>
54
+ {hasTrimmedText(card?.mediaUrl) ? (
55
+ <CapImage
56
+ src={card?.mediaUrl}
57
+ className="viber-carousel-preview-image"
58
+ alt="Viber carousel card"
59
+ />
60
+ ) : (
61
+ <CapRow className="viber-carousel-preview-image-placeholder" />
62
+ )}
63
+ <CapRow className="viber-carousel-preview-card-body">
64
+ <CapRow className={`viber-carousel-preview-text-wrap ${titleLineClass}`}>
65
+ {hasTrimmedText(card?.text) ? (
66
+ <div
67
+ className="viber-carousel-preview-text-inner"
68
+ ref={(node) => {
69
+ carouselTitleRefs.current[index] = node;
70
+ }}
71
+ >
72
+ <CapLabel type="label15" className="viber-carousel-preview-text">
73
+ {card?.text}
74
+ </CapLabel>
75
+ </div>
76
+ ) : (
77
+ <CapLabel type="label15" className="viber-carousel-preview-text-placeholder" />
78
+ )}
79
+ </CapRow>
80
+ {carouselButtonSlotCount > 0 && (
81
+ <CapRow className="viber-carousel-preview-buttons">
82
+ {Array.from({ length: carouselButtonSlotCount }).map((_, btnIndex) => {
83
+ const cardButton = cardButtons[btnIndex];
84
+ const trimmedCardButtonTitle = cardButton
85
+ ? getTrimmedText(cardButton?.title)
86
+ : '';
87
+
88
+ if (!trimmedCardButtonTitle) {
89
+ return (
90
+ <CapRow
91
+ className="viber-carousel-preview-button viber-carousel-preview-button-placeholder"
92
+ key={`viber-carousel-preview-btn-placeholder-${index}-${btnIndex}`}
93
+ aria-hidden="true"
94
+ />
95
+ );
96
+ }
97
+
98
+ return (
99
+ <CapLabel
100
+ className={`viber-carousel-preview-button ${btnIndex === 1 ? 'viber-carousel-preview-button-secondary' : ''}`}
101
+ key={`viber-carousel-preview-btn-${index}-${btnIndex}-${trimmedCardButtonTitle}`}
102
+ >
103
+ {trimmedCardButtonTitle}
104
+ </CapLabel>
105
+ );
106
+ })}
107
+ </CapRow>
108
+ )}
109
+ </CapRow>
110
+ </CapRow>
111
+ );
112
+ })}
113
+ </CapRow>
114
+ );
115
+ };
116
+
117
+ ViberCarouselPreviewCards.propTypes = {
118
+ cards: PropTypes.arrayOf(PropTypes.shape({
119
+ text: PropTypes.string,
120
+ mediaUrl: PropTypes.string,
121
+ buttons: PropTypes.arrayOf(PropTypes.shape({
122
+ title: PropTypes.string,
123
+ action: PropTypes.string,
124
+ })),
125
+ })),
126
+ };
127
+
128
+ ViberCarouselPreviewCards.defaultProps = {
129
+ cards: [],
130
+ };
131
+
132
+ export default ViberCarouselPreviewCards;
@@ -14,14 +14,25 @@ import CapSpin from '@capillarytech/cap-ui-library/CapSpin';
14
14
  import CapImage from '@capillarytech/cap-ui-library/CapImage';
15
15
  import CapTooltip from '@capillarytech/cap-ui-library/CapTooltip';
16
16
  import CapRow from '@capillarytech/cap-ui-library/CapRow';
17
- import { ANDROID, IOS, ANDROID_DEVICE_NAME, IOS_DEVICE_NAME, VIBER_ACCOUNT_NAME } from '../constants';
17
+ import {
18
+ ANDROID,
19
+ IOS,
20
+ ANDROID_DEVICE_NAME,
21
+ IOS_DEVICE_NAME,
22
+ VIBER_ACCOUNT_NAME,
23
+ MEDIA_TYPE_CAROUSEL,
24
+ } from '../constants';
18
25
  import messages from '../messages';
19
26
  import videoPlay from '../../../assets/videoPlay.svg';
27
+ import ViberCarouselPreviewCards from './ViberCarouselPreviewCards';
20
28
 
21
29
  // Import device mockup images (same as SMS)
22
30
  const smsMobileAndroid = require('../../../assets/Android.png');
23
31
  const smsMobileIos = require('../../../assets/iOS.png');
24
32
 
33
+ const getTrimmedText = (value = '') => (value ?? '').trim();
34
+ const hasTrimmedText = (value = '') => Boolean(getTrimmedText(value));
35
+
25
36
  const ViberPreviewContent = ({
26
37
  content,
27
38
  device,
@@ -43,7 +54,34 @@ const ViberPreviewContent = ({
43
54
  imageURL = '',
44
55
  videoParams = {},
45
56
  buttonText = '',
57
+ type = '',
58
+ cards = [],
59
+ showCarouselEditorPreview = false,
46
60
  } = viberContent;
61
+ const hasCarouselContent = type === MEDIA_TYPE_CAROUSEL;
62
+
63
+ const cardHasMeaningfulContent = (card) => {
64
+ if (!card || typeof card !== 'object') return false;
65
+ if (hasTrimmedText(card?.text)) return true;
66
+ if (hasTrimmedText(card?.mediaUrl)) return true;
67
+ const buttons = card?.buttons ?? [];
68
+ return buttons.some((button) => hasTrimmedText(button?.title));
69
+ };
70
+
71
+ const hasMeaningfulCarousel =
72
+ hasCarouselContent &&
73
+ Array.isArray(cards) &&
74
+ cards.length > 0 &&
75
+ cards.some(cardHasMeaningfulContent);
76
+
77
+ const showCarouselInPreview =
78
+ hasCarouselContent && (Boolean(showCarouselEditorPreview) || hasMeaningfulCarousel);
79
+
80
+ const previewCarouselCards =
81
+ hasCarouselContent && Array.isArray(cards) && cards.length ? cards : hasCarouselContent ? [{}] : [];
82
+
83
+ const trimmedMessageContent = getTrimmedText(messageContent);
84
+ const trimmedButtonText = getTrimmedText(buttonText);
47
85
 
48
86
  // Get account name (first letter for icon)
49
87
  const accountIcon = (accountName || brandName || 'V')[0]?.toUpperCase();
@@ -99,8 +137,15 @@ const ViberPreviewContent = ({
99
137
  );
100
138
  }
101
139
 
102
- // Check if there's any content to display
103
- const hasContent = messageContent || imageURL || videoParams?.viberVideoSrc || buttonText;
140
+ // Check if there's any content to display (whitespace-only strings do not count)
141
+ const hasContent = Boolean(
142
+ trimmedMessageContent
143
+ || hasTrimmedText(imageURL)
144
+ || videoParams?.viberVideoSrc
145
+ || trimmedButtonText
146
+ || hasMeaningfulCarousel
147
+ || (hasCarouselContent && showCarouselEditorPreview)
148
+ );
104
149
 
105
150
  // Render normal Viber preview
106
151
  return (
@@ -127,21 +172,23 @@ const ViberPreviewContent = ({
127
172
  {hasContent && (
128
173
  <CapRow className={`viber-message-container ${device !== ANDROID ? 'viber-message-container-ios' : ''}`}>
129
174
  {/* Brand Name Display (from TemplatePreview line 1136) */}
130
- <CapRow className="msg-container viber-preview">
175
+ <CapRow className={`msg-container viber-preview ${showCarouselInPreview ? 'viber-preview-carousel' : ''}`}>
131
176
  {/* Account Icon (from TemplatePreview line 1146-1160) */}
132
- <CapRow className="viber-account-icon">
133
- {accountIcon}
134
- </CapRow>
177
+ {!hasCarouselContent && (
178
+ <CapRow className="viber-account-icon">
179
+ {accountIcon}
180
+ </CapRow>
181
+ )}
135
182
 
136
183
  {/* Message Bubble (from TemplatePreview line 1161-1223) */}
137
- <CapRow className="message-pop align-left viber-message-pop">
184
+ <CapRow className={`message-pop align-left viber-message-pop ${showCarouselInPreview ? 'viber-message-pop-carousel' : ''}`}>
138
185
  {/* Text Viber preview */}
139
- {messageContent && (
186
+ {trimmedMessageContent && !hasCarouselContent && (
140
187
  <CapLabel type="label15" className="viber-message-text">{messageContent}</CapLabel>
141
188
  )}
142
189
 
143
190
  {/* Image Viber preview */}
144
- {imageURL && (
191
+ {hasTrimmedText(imageURL) && (
145
192
  <CapImage
146
193
  src={imageURL}
147
194
  className="viber-image-preview"
@@ -171,16 +218,42 @@ const ViberPreviewContent = ({
171
218
  )}
172
219
 
173
220
  {/* Button Viber preview */}
174
- {buttonText && (
221
+ {trimmedButtonText && (
175
222
  <CapLabel className="viber-button-base">
176
223
  <p className="viber-button-card-text">
177
224
  {buttonText}
178
225
  </p>
179
226
  </CapLabel>
180
227
  )}
181
- <CapLabel type="label1" className="viber-timestamp">
182
- {timestamp}
183
- </CapLabel>
228
+ {/* Carousel Viber preview */}
229
+ {showCarouselInPreview && (
230
+ <>
231
+ <CapRow className="viber-carousel-message-pop">
232
+ {trimmedMessageContent ? (
233
+ <CapLabel
234
+ type="label15"
235
+ className="message-pop-item align-left viber-message-text viber-carousel-message-box-text"
236
+ >
237
+ {messageContent}
238
+ </CapLabel>
239
+ ) : (
240
+ <CapRow className="viber-carousel-message-box-placeholder" />
241
+ )}
242
+ <CapLabel type="label1" className="viber-carousel-message-timestamp">
243
+ {timestamp}
244
+ </CapLabel>
245
+ </CapRow>
246
+
247
+ <CapRow className="viber-carousel-cards-pop">
248
+ <ViberCarouselPreviewCards cards={previewCarouselCards} />
249
+ </CapRow>
250
+ </>
251
+ )}
252
+ {!showCarouselInPreview && (
253
+ <CapLabel type="label1" className="viber-timestamp">
254
+ {timestamp}
255
+ </CapLabel>
256
+ )}
184
257
  <CapRow className="empty-placeholder" />
185
258
  </CapRow>
186
259
 
@@ -214,6 +287,16 @@ ViberPreviewContent.propTypes = {
214
287
  viberVideoPreviewImg: PropTypes.string,
215
288
  }),
216
289
  buttonText: PropTypes.string,
290
+ type: PropTypes.string,
291
+ cards: PropTypes.arrayOf(PropTypes.shape({
292
+ text: PropTypes.string,
293
+ mediaUrl: PropTypes.string,
294
+ buttons: PropTypes.arrayOf(PropTypes.shape({
295
+ title: PropTypes.string,
296
+ action: PropTypes.string,
297
+ })),
298
+ })),
299
+ showCarouselEditorPreview: PropTypes.bool,
217
300
  }),
218
301
  }),
219
302
  device: PropTypes.oneOf([ANDROID, IOS]),
@@ -17,7 +17,7 @@ import CapTooltip from '@capillarytech/cap-ui-library/CapTooltip';
17
17
  import CapRow from '@capillarytech/cap-ui-library/CapRow';
18
18
  import { ANDROID, IOS } from '../constants';
19
19
  import messages from '../messages';
20
- import { getWhatsappQuickReply, getWhatsappCarouselButtonView, getWhatsappDocPreview } from '../../../v2Containers/Whatsapp/utils';
20
+ import { getWhatsappQuickReply, getWhatsappCarouselButtonView } from '../../../v2Containers/Whatsapp/utils';
21
21
  import { QUICK_REPLY, PHONE_NUMBER, WHATSAPP_CATEGORIES, TEMPLATE_VARIABLE_REGEX } from '../../../v2Containers/Whatsapp/constants';
22
22
  import videoPlay from '../../../assets/videoPlay.svg';
23
23
  import whatsappImageEmptyPreview from '../../TemplatePreview/assets/images/empty_image_preview.svg';
@@ -256,9 +256,9 @@ const WhatsAppPreviewContent = ({
256
256
  )}
257
257
 
258
258
  {/* Document Preview */}
259
- {content?.whatsappDocSource && content?.whatsappDocParams && (
259
+ {content?.docPreview && (
260
260
  <CapRow className="whatsapp-image">
261
- {getWhatsappDocPreview(content.whatsappDocParams)}
261
+ {content.docPreview}
262
262
  </CapRow>
263
263
  )}
264
264
 
@@ -1,4 +1,5 @@
1
1
  @import '~@capillarytech/cap-ui-library/styles/_variables';
2
+ @import './_viberCarouselPreviewCards.scss';
2
3
 
3
4
  /**
4
5
  * UnifiedPreview Styles
@@ -2116,6 +2117,8 @@
2116
2117
  flex: 1;
2117
2118
  display: flex;
2118
2119
  flex-direction: column;
2120
+ overflow-y: auto;
2121
+ -webkit-overflow-scrolling: touch;
2119
2122
  padding: 0 $CAP_SPACE_16;
2120
2123
  background-color: #ffffff;
2121
2124
  margin-left: $CAP_SPACE_06;
@@ -2141,6 +2144,12 @@
2141
2144
  margin-top: $CAP_SPACE_16;
2142
2145
  width: 68%;
2143
2146
 
2147
+ &.viber-preview-carousel {
2148
+ width: 100%;
2149
+ margin-left: $CAP_SPACE_12;
2150
+ max-height: none;
2151
+ }
2152
+
2144
2153
  // Account icon (from TemplatePreview line 1146-1160)
2145
2154
  .viber-account-icon {
2146
2155
  width: $CAP_SPACE_20;
@@ -2169,6 +2178,18 @@
2169
2178
  border-radius: $CAP_SPACE_04;
2170
2179
  padding: $CAP_SPACE_04;
2171
2180
 
2181
+ &.viber-message-pop-carousel {
2182
+ width: 100%;
2183
+ left: 0;
2184
+ margin-top: 0;
2185
+ padding: 0;
2186
+ background: transparent;
2187
+ display: flex;
2188
+ flex-direction: column;
2189
+ align-items: flex-start;
2190
+ gap: $CAP_SPACE_06;
2191
+ }
2192
+
2172
2193
  // Text Viber preview (from TemplatePreview line 1166-1174)
2173
2194
  .viber-message-text {
2174
2195
  margin: 0.107rem $CAP_SPACE_06 $CAP_SPACE_01 0.5rem;
@@ -2240,6 +2261,64 @@
2240
2261
  }
2241
2262
  }
2242
2263
 
2264
+ .viber-carousel-message-pop,
2265
+ .viber-carousel-cards-pop {
2266
+ width: 100%;
2267
+ background: $CAP_G08;
2268
+ border-radius: $CAP_SPACE_06;
2269
+ padding: $CAP_SPACE_08;
2270
+ }
2271
+
2272
+ .viber-carousel-message-pop {
2273
+ margin-top: 0;
2274
+ width: 68%;
2275
+ border-radius: 0 $CAP_SPACE_06 $CAP_SPACE_06 $CAP_SPACE_06;
2276
+ }
2277
+
2278
+ .viber-carousel-cards-pop {
2279
+ margin-top: 0;
2280
+ width: 100%;
2281
+ background: transparent;
2282
+ border: none;
2283
+ border-radius: 0;
2284
+ padding: 0;
2285
+ }
2286
+
2287
+ .viber-carousel-message-box {
2288
+ width: 100%;
2289
+ min-height: 2.25rem;
2290
+ height: auto;
2291
+ border-radius: $CAP_SPACE_04;
2292
+ background: transparent;
2293
+ padding: 0 $CAP_SPACE_08;
2294
+ display: flex;
2295
+ align-items: center;
2296
+ }
2297
+
2298
+ .viber-carousel-message-box-text {
2299
+ color: $CAP_G01;
2300
+ margin: 0.107rem $CAP_SPACE_06 $CAP_SPACE_01 0.5rem;
2301
+ white-space: normal;
2302
+ word-break: break-word;
2303
+ overflow: visible;
2304
+ width: 100%;
2305
+ }
2306
+
2307
+ .viber-carousel-message-box-placeholder {
2308
+ width: 100%;
2309
+ height: 0.875rem;
2310
+ border-radius: $CAP_SPACE_04;
2311
+ background: $CAP_G07;
2312
+ }
2313
+
2314
+ .viber-carousel-message-timestamp,
2315
+ .viber-carousel-cards-timestamp {
2316
+ display: block;
2317
+ text-align: right;
2318
+ margin-top: $CAP_SPACE_06;
2319
+ color: $CAP_G04;
2320
+ }
2321
+
2243
2322
  .empty-placeholder {
2244
2323
  height: $CAP_SPACE_08;
2245
2324
  }
@@ -0,0 +1,127 @@
1
+ // Shared Viber carousel card row (Test & Preview device preview)
2
+ .viber-carousel-preview-scroll {
3
+ display: flex;
4
+ align-items: stretch;
5
+ width: 100%;
6
+ overflow-x: auto;
7
+ overflow-y: visible;
8
+ scrollbar-width: none;
9
+ -webkit-overflow-scrolling: touch;
10
+
11
+ &::-webkit-scrollbar {
12
+ display: none;
13
+ }
14
+
15
+ .viber-carousel-preview-card {
16
+ flex: 0 0 68%;
17
+ min-width: 68%;
18
+ margin-right: $CAP_SPACE_08;
19
+ background: $CAP_G09;
20
+ border: 1px solid $CAP_G07;
21
+ border-radius: $CAP_SPACE_12;
22
+ overflow: hidden;
23
+ display: flex;
24
+ flex-direction: column;
25
+ align-self: stretch;
26
+
27
+ &:last-child {
28
+ margin-right: 0;
29
+ }
30
+
31
+ .viber-carousel-preview-card-body {
32
+ flex: 1;
33
+ padding: $CAP_SPACE_08;
34
+ display: flex;
35
+ flex-direction: column;
36
+ min-height: 0;
37
+ }
38
+
39
+ .viber-carousel-preview-text-wrap {
40
+ flex-shrink: 0;
41
+ width: 100%;
42
+
43
+ &--1-line {
44
+ min-height: 1.3em;
45
+ }
46
+
47
+ &--2-line {
48
+ min-height: 2.6em;
49
+ }
50
+ }
51
+
52
+ .viber-carousel-preview-text-inner {
53
+ width: 100%;
54
+ }
55
+
56
+ .viber-carousel-preview-buttons {
57
+ display: flex;
58
+ flex-direction: column;
59
+ gap: $CAP_SPACE_06;
60
+ margin-top: auto;
61
+ width: 100%;
62
+ flex-shrink: 0;
63
+ }
64
+
65
+ .viber-carousel-preview-image {
66
+ width: 100%;
67
+ height: 10rem;
68
+ object-fit: cover;
69
+ border-radius: 0;
70
+ }
71
+
72
+ .viber-carousel-preview-image-placeholder {
73
+ width: 100%;
74
+ height: 10rem;
75
+ border-radius: 0;
76
+ background: $CAP_G07;
77
+ }
78
+
79
+ .viber-carousel-preview-text {
80
+ color: $CAP_G01;
81
+ line-height: 1.3;
82
+ white-space: normal;
83
+ word-break: break-word;
84
+ display: block;
85
+ width: 100%;
86
+ margin: 0;
87
+ }
88
+
89
+ .viber-carousel-preview-text-placeholder {
90
+ width: 100%;
91
+ height: 0.875rem;
92
+ border-radius: $CAP_SPACE_04;
93
+ background: $CAP_G07;
94
+ min-height: 0.875rem;
95
+ display: block;
96
+ }
97
+
98
+ .viber-carousel-preview-button {
99
+ color: $CAP_WHITE;
100
+ background: $CAP_PURPLE01;
101
+ border-radius: $CAP_SPACE_12;
102
+ text-align: center;
103
+ width: 100%;
104
+ display: flex;
105
+ align-items: center;
106
+ justify-content: center;
107
+ min-height: 1.5rem;
108
+ padding: $CAP_SPACE_06 $CAP_SPACE_08;
109
+ white-space: normal;
110
+ box-sizing: border-box;
111
+ }
112
+
113
+ .viber-carousel-preview-button-placeholder {
114
+ visibility: hidden;
115
+ pointer-events: none;
116
+ padding: 0;
117
+ background: transparent;
118
+ border: none;
119
+ }
120
+
121
+ .viber-carousel-preview-button-secondary {
122
+ color: $CAP_PURPLE01;
123
+ background: transparent;
124
+ border: none;
125
+ }
126
+ }
127
+ }