@pushwoosh/websdk-common 6.17.1 → 6.19.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.
@@ -0,0 +1,4 @@
1
+ import { type ReactElement } from 'react';
2
+ import { type InboxCellPreviewProps } from './types';
3
+ /** One inbox cell drawn the way the SDK of `platform` draws it; the web skin is the WebSDK widget cell itself. */
4
+ export declare function InboxCellPreview(props: InboxCellPreviewProps): ReactElement;
@@ -0,0 +1,497 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { INBOX_WIDGET_CSS } from './constants';
4
+ import { formatInboxMessageTime, getInboxEmptyLayout, getInboxHeroUrl, getInboxIconInitial, getInboxMobileTexts, getInboxValidSlides, isInboxContentEmpty, resolveInboxLayout, resolveInboxWidgetAppearance } from './helpers';
5
+ import { InboxWebCell, inboxWidgetPreviewFrame } from './InboxWidgetReader';
6
+ const MAIN = '#4B5057';
7
+ const LOCKED = '#767C82';
8
+ const PHANTOM = '#9AA0A6';
9
+ const FROZEN = '#F7F9FA';
10
+ const CLEAR = '#FFFFFF';
11
+ const DIVIDER = '#EBF0F5';
12
+ const BRIGHT = '#00A2FF';
13
+ const BRIGHT_LIGHT = '#80B7FF33';
14
+ const SOFT_LIGHT = '#889EB81A';
15
+ const RADIUS = '8px';
16
+ const FONT = 'Roboto, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif';
17
+ const CARD_EDGE_SHADOW = 'inset -1px -1px 1px 0 rgba(0, 0, 0, 0.09), inset 1px 1px 1px 0 rgba(0, 0, 0, 0.09)';
18
+ const SLIDE_SCRIM = 'rgba(0, 0, 0, 0.45)';
19
+ const DOT_SIZE = 6;
20
+ const MIN_WIDTH = 240;
21
+ const MAX_WIDTH = 350;
22
+ const SKELETON_HEIGHT = 56;
23
+ const TEXT = {
24
+ footnote: {
25
+ fontSize: 12,
26
+ lineHeight: 16 / 12,
27
+ fontWeight: 400
28
+ },
29
+ h5: {
30
+ fontSize: 12,
31
+ lineHeight: 16 / 12,
32
+ fontWeight: 500
33
+ },
34
+ h4: {
35
+ fontSize: 14,
36
+ lineHeight: 20 / 14,
37
+ fontWeight: 500
38
+ }
39
+ };
40
+ const ELLIPSIS = {
41
+ overflow: 'hidden',
42
+ textOverflow: 'ellipsis',
43
+ whiteSpace: 'nowrap'
44
+ };
45
+ const AVATAR = {
46
+ ios: {
47
+ size: 30,
48
+ radius: '50%',
49
+ text: TEXT.h5
50
+ },
51
+ android: {
52
+ size: 40,
53
+ radius: RADIUS,
54
+ text: TEXT.h4
55
+ }
56
+ };
57
+ function clamp(lines) {
58
+ return {
59
+ display: '-webkit-box',
60
+ WebkitLineClamp: lines,
61
+ WebkitBoxOrient: 'vertical',
62
+ overflow: 'hidden'
63
+ };
64
+ }
65
+ function UnreadDot({
66
+ style
67
+ }) {
68
+ return _jsx("div", {
69
+ style: {
70
+ width: DOT_SIZE,
71
+ height: DOT_SIZE,
72
+ flexShrink: 0,
73
+ borderRadius: '50%',
74
+ background: BRIGHT,
75
+ ...style
76
+ }
77
+ });
78
+ }
79
+ function Avatar({
80
+ content,
81
+ isEmpty,
82
+ platform
83
+ }) {
84
+ const {
85
+ size,
86
+ radius,
87
+ text
88
+ } = AVATAR[platform];
89
+ const box = {
90
+ width: size,
91
+ height: size,
92
+ flexShrink: 0,
93
+ borderRadius: radius
94
+ };
95
+ if (isEmpty) {
96
+ return _jsx("div", {
97
+ style: {
98
+ ...box,
99
+ background: BRIGHT_LIGHT,
100
+ border: `1px dashed ${BRIGHT}`,
101
+ boxSizing: 'border-box'
102
+ }
103
+ });
104
+ }
105
+ return _jsx("div", {
106
+ style: {
107
+ ...box,
108
+ display: 'flex',
109
+ alignItems: 'center',
110
+ justifyContent: 'center',
111
+ overflow: 'hidden',
112
+ background: PHANTOM
113
+ },
114
+ children: content.iconUrl ? _jsx("img", {
115
+ alt: content.title || '',
116
+ src: content.iconUrl,
117
+ style: {
118
+ width: '100%',
119
+ height: '100%',
120
+ objectFit: 'cover'
121
+ }
122
+ }) : _jsx("span", {
123
+ style: {
124
+ ...text,
125
+ color: MAIN
126
+ },
127
+ children: getInboxIconInitial(content)
128
+ })
129
+ });
130
+ }
131
+ const HERO = {
132
+ display: 'block',
133
+ width: '100%',
134
+ aspectRatio: '2 / 1',
135
+ objectFit: 'cover',
136
+ borderRadius: RADIUS
137
+ };
138
+ function Carousel({
139
+ content
140
+ }) {
141
+ const slides = getInboxValidSlides(content.carousel);
142
+ const [index, setIndex] = useState(0);
143
+ const active = Math.min(index, slides.length - 1);
144
+ const slide = slides[active];
145
+ return _jsxs(_Fragment, {
146
+ children: [_jsxs("div", {
147
+ onClick: () => setIndex((active + 1) % slides.length),
148
+ style: {
149
+ position: 'relative',
150
+ cursor: 'pointer'
151
+ },
152
+ children: [_jsx("img", {
153
+ alt: slide.caption || '',
154
+ src: slide.imageUrl,
155
+ style: HERO
156
+ }), !!slide.caption && _jsx("div", {
157
+ style: {
158
+ ...TEXT.h5,
159
+ ...clamp(2),
160
+ position: 'absolute',
161
+ left: 0,
162
+ right: 0,
163
+ bottom: 0,
164
+ padding: '14px 10px 6px',
165
+ color: CLEAR,
166
+ borderRadius: `0 0 ${RADIUS} ${RADIUS}`,
167
+ background: `linear-gradient(transparent, ${SLIDE_SCRIM})`
168
+ },
169
+ children: slide.caption
170
+ })]
171
+ }), slides.length > 1 && _jsx("div", {
172
+ style: {
173
+ display: 'flex',
174
+ alignItems: 'center',
175
+ justifyContent: 'center',
176
+ gap: 6,
177
+ paddingTop: 2
178
+ },
179
+ children: slides.map((_slide, slideIndex) => _jsx("button", {
180
+ onClick: event => {
181
+ event.stopPropagation();
182
+ setIndex(slideIndex);
183
+ },
184
+ style: {
185
+ width: DOT_SIZE,
186
+ height: DOT_SIZE,
187
+ padding: 0,
188
+ border: 'none',
189
+ borderRadius: '50%',
190
+ cursor: 'pointer',
191
+ background: slideIndex === active ? BRIGHT : DIVIDER
192
+ },
193
+ type: "button"
194
+ }, slideIndex))
195
+ })]
196
+ });
197
+ }
198
+ function Media({
199
+ content,
200
+ layout,
201
+ isEmpty
202
+ }) {
203
+ if (isEmpty) {
204
+ return _jsx("div", {
205
+ style: {
206
+ aspectRatio: '2 / 1',
207
+ flexShrink: 0,
208
+ borderRadius: RADIUS,
209
+ border: `1px dashed ${BRIGHT}`,
210
+ background: BRIGHT_LIGHT
211
+ }
212
+ });
213
+ }
214
+ if (layout === 'carousel') {
215
+ return _jsx(Carousel, {
216
+ content: content
217
+ });
218
+ }
219
+ return _jsx("img", {
220
+ alt: content.title || '',
221
+ src: getInboxHeroUrl(content),
222
+ style: HERO
223
+ });
224
+ }
225
+ const IOS_CARD = {
226
+ display: 'flex',
227
+ flexDirection: 'column',
228
+ gap: 5,
229
+ padding: 5,
230
+ background: CLEAR,
231
+ borderRadius: RADIUS,
232
+ boxShadow: CARD_EDGE_SHADOW
233
+ };
234
+ function IOSCell(props) {
235
+ const {
236
+ content,
237
+ layout,
238
+ isEmpty,
239
+ title,
240
+ body,
241
+ dateLabel,
242
+ unread
243
+ } = props;
244
+ const texts = _jsxs("div", {
245
+ style: {
246
+ display: 'flex',
247
+ flexDirection: 'column',
248
+ flex: 1,
249
+ gap: 2,
250
+ minWidth: 0
251
+ },
252
+ children: [_jsxs("div", {
253
+ style: {
254
+ display: 'flex',
255
+ alignItems: 'center'
256
+ },
257
+ children: [_jsx("div", {
258
+ style: {
259
+ ...TEXT.h5,
260
+ ...ELLIPSIS,
261
+ color: MAIN
262
+ },
263
+ children: title
264
+ }), _jsx("div", {
265
+ style: {
266
+ flex: 1,
267
+ minWidth: 4
268
+ }
269
+ }), _jsx("div", {
270
+ style: {
271
+ ...TEXT.footnote,
272
+ ...ELLIPSIS,
273
+ color: LOCKED
274
+ },
275
+ children: dateLabel
276
+ })]
277
+ }), !!body && _jsx("div", {
278
+ style: {
279
+ ...TEXT.footnote,
280
+ ...clamp(layout === 'classic' ? 3 : 2),
281
+ color: LOCKED
282
+ },
283
+ children: body
284
+ })]
285
+ });
286
+ if (layout === 'classic') {
287
+ return _jsxs("div", {
288
+ style: {
289
+ display: 'flex',
290
+ alignItems: 'center',
291
+ gap: 8,
292
+ padding: '16px 16px 16px 18px',
293
+ background: CLEAR,
294
+ borderRadius: RADIUS,
295
+ boxShadow: CARD_EDGE_SHADOW
296
+ },
297
+ children: [unread && _jsx(UnreadDot, {}), _jsx(Avatar, {
298
+ content: content,
299
+ isEmpty: isEmpty,
300
+ platform: "ios"
301
+ }), texts]
302
+ });
303
+ }
304
+ const media = _jsx(Media, {
305
+ content: content,
306
+ isEmpty: isEmpty,
307
+ layout: layout
308
+ });
309
+ if (layout === 'banner') {
310
+ return _jsx("div", {
311
+ style: IOS_CARD,
312
+ children: _jsxs("div", {
313
+ style: {
314
+ position: 'relative'
315
+ },
316
+ children: [media, unread && _jsx(UnreadDot, {
317
+ style: {
318
+ position: 'absolute',
319
+ left: 10,
320
+ top: 10
321
+ }
322
+ })]
323
+ })
324
+ });
325
+ }
326
+ return _jsxs("div", {
327
+ style: IOS_CARD,
328
+ children: [media, _jsxs("div", {
329
+ style: {
330
+ display: 'flex',
331
+ alignItems: 'flex-start',
332
+ gap: 4,
333
+ padding: 8,
334
+ background: FROZEN,
335
+ border: `1px solid ${SOFT_LIGHT}`,
336
+ borderRadius: RADIUS
337
+ },
338
+ children: [unread && _jsx(UnreadDot, {
339
+ style: {
340
+ marginTop: 3.5
341
+ }
342
+ }), texts]
343
+ })]
344
+ });
345
+ }
346
+ const ANDROID_ROW = {
347
+ display: 'flex',
348
+ flexDirection: 'column',
349
+ gap: 16,
350
+ padding: 16,
351
+ background: CLEAR,
352
+ borderBottom: `1px solid ${DIVIDER}`
353
+ };
354
+ function AndroidCell(props) {
355
+ const {
356
+ content,
357
+ layout,
358
+ isEmpty,
359
+ title,
360
+ body,
361
+ dateLabel,
362
+ unread
363
+ } = props;
364
+ const texts = _jsxs("div", {
365
+ style: {
366
+ display: 'flex',
367
+ flexDirection: 'column',
368
+ flex: 1,
369
+ gap: 8,
370
+ minWidth: 0
371
+ },
372
+ children: [_jsxs("div", {
373
+ style: {
374
+ ...TEXT.footnote,
375
+ ...ELLIPSIS,
376
+ color: unread ? MAIN : LOCKED
377
+ },
378
+ children: [title, ' ', _jsx("span", {
379
+ style: {
380
+ color: LOCKED
381
+ },
382
+ children: dateLabel
383
+ })]
384
+ }), !!body && _jsx("div", {
385
+ style: {
386
+ ...TEXT.footnote,
387
+ ...clamp(3),
388
+ color: MAIN
389
+ },
390
+ children: body
391
+ })]
392
+ });
393
+ if (layout === 'classic') {
394
+ return _jsx("div", {
395
+ style: ANDROID_ROW,
396
+ children: _jsxs("div", {
397
+ style: {
398
+ display: 'flex',
399
+ alignItems: 'flex-start',
400
+ gap: 12
401
+ },
402
+ children: [_jsx(Avatar, {
403
+ content: content,
404
+ isEmpty: isEmpty,
405
+ platform: "android"
406
+ }), texts]
407
+ })
408
+ });
409
+ }
410
+ return _jsxs("div", {
411
+ style: ANDROID_ROW,
412
+ children: [_jsx(Media, {
413
+ content: content,
414
+ isEmpty: isEmpty,
415
+ layout: layout
416
+ }), layout !== 'banner' && texts]
417
+ });
418
+ }
419
+ /** One inbox cell drawn the way the SDK of `platform` draws it; the web skin is the WebSDK widget cell itself. */
420
+ export function InboxCellPreview(props) {
421
+ const {
422
+ platform = 'ios',
423
+ content,
424
+ dateLabel = '12:12',
425
+ sendDate,
426
+ unread = true,
427
+ isLoading = false,
428
+ emptyTitle = 'Message preview',
429
+ emptyDescription = 'Enter text of your inbox message to see the preview',
430
+ renderText = text => text,
431
+ width = '100%'
432
+ } = props;
433
+ const frame = {
434
+ position: 'relative',
435
+ width,
436
+ minWidth: MIN_WIDTH,
437
+ maxWidth: MAX_WIDTH,
438
+ fontFamily: FONT,
439
+ boxSizing: 'border-box'
440
+ };
441
+ if (isLoading) {
442
+ return _jsx("div", {
443
+ style: {
444
+ ...frame,
445
+ minHeight: SKELETON_HEIGHT,
446
+ background: SOFT_LIGHT,
447
+ borderRadius: RADIUS
448
+ }
449
+ });
450
+ }
451
+ const picked = content || {};
452
+ const isEmpty = isInboxContentEmpty(picked);
453
+ const layout = isEmpty ? getInboxEmptyLayout(picked) : resolveInboxLayout(picked);
454
+ if (platform === 'web') {
455
+ const appearance = resolveInboxWidgetAppearance(props.appearance);
456
+ const widget = inboxWidgetPreviewFrame(appearance, 'pw-inbox-widget--cell');
457
+ return _jsxs("div", {
458
+ style: frame,
459
+ children: [_jsx("style", {
460
+ children: INBOX_WIDGET_CSS
461
+ }), _jsx("div", {
462
+ className: widget.className,
463
+ style: {
464
+ ...widget.style,
465
+ width: '100%'
466
+ },
467
+ children: _jsx("ul", {
468
+ className: "pw-inbox_list",
469
+ children: _jsx(InboxWebCell, {
470
+ body: isEmpty ? emptyDescription : picked.body,
471
+ content: picked,
472
+ isActionPerformed: !unread,
473
+ isRead: !unread,
474
+ layout: layout,
475
+ placeholder: isEmpty,
476
+ timeLabel: formatInboxMessageTime(sendDate || new Date().toISOString()),
477
+ title: isEmpty ? emptyTitle : picked.title
478
+ })
479
+ })
480
+ })]
481
+ });
482
+ }
483
+ const texts = getInboxMobileTexts(picked, layout);
484
+ const Cell = platform === 'android' ? AndroidCell : IOSCell;
485
+ return _jsx("div", {
486
+ style: frame,
487
+ children: _jsx(Cell, {
488
+ body: isEmpty ? emptyDescription : texts.body && renderText(texts.body),
489
+ content: picked,
490
+ dateLabel: dateLabel,
491
+ isEmpty: isEmpty,
492
+ layout: layout,
493
+ title: isEmpty ? emptyTitle : renderText(texts.title),
494
+ unread: unread
495
+ })
496
+ });
497
+ }
@@ -0,0 +1,27 @@
1
+ import { type CSSProperties, type ReactElement, type ReactNode } from 'react';
2
+ import { type InboxCellContent, type InboxLayout, type InboxWidgetAppearance, type InboxWidgetReaderProps } from './types';
3
+ /** Props of {@link InboxWebCell}. */
4
+ export interface InboxWebCellProps {
5
+ content: InboxCellContent;
6
+ layout: InboxLayout;
7
+ title: ReactNode;
8
+ body: ReactNode;
9
+ timeLabel: string;
10
+ isRead: boolean;
11
+ isActionPerformed: boolean;
12
+ code?: string;
13
+ /** Draw dashed placeholders instead of the icon and the media. */
14
+ placeholder?: boolean;
15
+ onClick?: () => void;
16
+ onRemove?: () => void;
17
+ onSlideLinkClick?: (link: string) => void;
18
+ }
19
+ /** One cell of the web inbox widget, the `li.pw-inbox_item` the WebSDK draws. */
20
+ export declare function InboxWebCell(props: InboxWebCellProps): ReactElement;
21
+ /** Classes and custom properties of a preview widget box; a live host sets them on its own element. */
22
+ export declare function inboxWidgetPreviewFrame(appearance: InboxWidgetAppearance, extraClass?: string): {
23
+ className: string;
24
+ style: CSSProperties;
25
+ };
26
+ /** The web inbox widget: message list or empty state. `live` fills the host's `.pw-inbox-widget` element, `preview` draws the box inline. */
27
+ export declare function InboxWidgetReader(props: InboxWidgetReaderProps): ReactElement;