@xapp/chat-widget 1.96.3 → 1.96.4
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/dist/index.css +1 -1
- package/dist/index.es.js +260 -15
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +260 -15
- package/dist/index.js.map +1 -1
- package/dist/xapp-chat-widget.css +1 -1
- package/dist/xapp-chat-widget.js +5 -5
- package/dist/xapp-chat-widget.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -79,7 +79,9 @@ color.invertColorHEX = invertColorHEX;
|
|
|
79
79
|
color.invertColorRgb = invertColorRgb;
|
|
80
80
|
color.invertColor = invertColor;
|
|
81
81
|
color.isSupportedColor = isSupportedColor;
|
|
82
|
+
color.usableColorOr = usableColorOr;
|
|
82
83
|
color.readableTextOn = readableTextOn;
|
|
84
|
+
color.darkenColor = darkenColor;
|
|
83
85
|
/**
|
|
84
86
|
* Colour maths for building a widget theme from a business's own branding.
|
|
85
87
|
*
|
|
@@ -188,6 +190,19 @@ function isSupportedColor(color) {
|
|
|
188
190
|
}
|
|
189
191
|
return parseRgb(color) !== null;
|
|
190
192
|
}
|
|
193
|
+
/**
|
|
194
|
+
* The colour to theme with: the one given, or the fallback when we cannot work
|
|
195
|
+
* out what text reads on it.
|
|
196
|
+
*
|
|
197
|
+
* Every builder needs this and each was doing it by hand, which is how the
|
|
198
|
+
* chat widget ended up guarded and the other two not. A background whose
|
|
199
|
+
* contrast cannot be computed is one we must not use — the foreground would be
|
|
200
|
+
* a guess paired with an unknown, and on a dark unparseable colour that is
|
|
201
|
+
* invisible text.
|
|
202
|
+
*/
|
|
203
|
+
function usableColorOr(color, fallback) {
|
|
204
|
+
return isSupportedColor(color) ? color : fallback;
|
|
205
|
+
}
|
|
191
206
|
/**
|
|
192
207
|
* The text colour that will read on this background: black or white, never
|
|
193
208
|
* anything else.
|
|
@@ -227,6 +242,77 @@ function hexToRgb(hex) {
|
|
|
227
242
|
b: parseInt(value.slice(4, 6), 16)
|
|
228
243
|
};
|
|
229
244
|
}
|
|
245
|
+
/**
|
|
246
|
+
* A shade of the input colour, darkened by `amount` percent (negative
|
|
247
|
+
* lightens). Ported alongside the rest so the form widget's theme does not
|
|
248
|
+
* need its own copy of it.
|
|
249
|
+
*/
|
|
250
|
+
function darkenColor(colorIn, amount) {
|
|
251
|
+
if (!colorIn) {
|
|
252
|
+
return colorIn;
|
|
253
|
+
}
|
|
254
|
+
let r, g, b, a;
|
|
255
|
+
let colorOut;
|
|
256
|
+
try {
|
|
257
|
+
// Parse the color format
|
|
258
|
+
if (colorIn.startsWith('#')) {
|
|
259
|
+
// Hex format
|
|
260
|
+
const hexMatch = colorIn.match(/\w\w/g);
|
|
261
|
+
if (hexMatch) {
|
|
262
|
+
[r, g, b] = hexMatch.map(x => parseInt(x, 16));
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
else if (colorIn.startsWith('rgb')) {
|
|
266
|
+
// RGB or RGBA format
|
|
267
|
+
const rgba = parseRgb(colorIn);
|
|
268
|
+
if (rgba) {
|
|
269
|
+
({ r, g, b, a } = rgba);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
if (r === undefined || g === undefined || b === undefined) {
|
|
273
|
+
console.error(`Unsupported color format: ${colorIn}`);
|
|
274
|
+
return colorIn;
|
|
275
|
+
}
|
|
276
|
+
if (a !== undefined) {
|
|
277
|
+
// Adjust luminance for RGBA
|
|
278
|
+
// Luminance is on the 0-255 scale the channels are, so the clamp
|
|
279
|
+
// has to be too. Bounding it to 0-1 collapsed it to 1 for every
|
|
280
|
+
// real colour, which made the ratio tiny and crushed the result to
|
|
281
|
+
// near-black instead of darkening it: rgba(20, 52, 204, 0.8) at 20%
|
|
282
|
+
// came back rgba(0, 1, 3, 1).
|
|
283
|
+
const luminance = 0.3 * r + 0.59 * g + 0.11 * b;
|
|
284
|
+
let newLuminance = luminance * (1 - amount / 100);
|
|
285
|
+
newLuminance = Math.max(0, Math.min(255, newLuminance));
|
|
286
|
+
const ratio = newLuminance / (luminance || 1); // no division by zero
|
|
287
|
+
r = Math.round(r * ratio);
|
|
288
|
+
g = Math.round(g * ratio);
|
|
289
|
+
b = Math.round(b * ratio);
|
|
290
|
+
// Adjust alpha
|
|
291
|
+
a = a + amount / 100;
|
|
292
|
+
a = Math.max(0, Math.min(1, a)); // within 0 and 1
|
|
293
|
+
colorOut = `rgba(${r}, ${g}, ${b}, ${a})`;
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
// Adjust the color for RGB or Hex
|
|
297
|
+
if (amount > 0) {
|
|
298
|
+
r = Math.max(0, r - amount);
|
|
299
|
+
g = Math.max(0, g - amount);
|
|
300
|
+
b = Math.max(0, b - amount);
|
|
301
|
+
}
|
|
302
|
+
else {
|
|
303
|
+
amount = Math.abs(amount); // positive for lightening
|
|
304
|
+
r = Math.min(255, r + amount);
|
|
305
|
+
g = Math.min(255, g + amount);
|
|
306
|
+
b = Math.min(255, b + amount);
|
|
307
|
+
}
|
|
308
|
+
colorOut = `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
catch (e) {
|
|
312
|
+
console.error(`Unsupported color format: ${colorIn} - Error: ${e}`);
|
|
313
|
+
}
|
|
314
|
+
return colorOut;
|
|
315
|
+
}
|
|
230
316
|
|
|
231
317
|
Object.defineProperty(buildChatWidgetChannel$1, "__esModule", { value: true });
|
|
232
318
|
buildChatWidgetChannel$1.buildChatWidgetChannel = buildChatWidgetChannel;
|
|
@@ -239,7 +325,7 @@ function buildChatWidgetChannel(props) {
|
|
|
239
325
|
// Only a colour whose contrast can actually be computed, so the background
|
|
240
326
|
// and the text on it are always a matched pair rather than one scraped
|
|
241
327
|
// value and one guess.
|
|
242
|
-
const primaryColor = (0, color_1.
|
|
328
|
+
const primaryColor = (0, color_1.usableColorOr)(props.primaryColor, DEFAULT_PRIMARY);
|
|
243
329
|
return {
|
|
244
330
|
type: "chat-widget",
|
|
245
331
|
// MCP by default. The widget runs on a public page, so homeowner mode
|
|
@@ -253,7 +339,7 @@ function buildChatWidgetChannel(props) {
|
|
|
253
339
|
mode: "homeowner"
|
|
254
340
|
}
|
|
255
341
|
},
|
|
256
|
-
theme: buildChatTheme(primaryColor),
|
|
342
|
+
theme: buildChatTheme(primaryColor, props.secondaryColor),
|
|
257
343
|
actionBar: buildActionBar({ apiUrl, formWidgetKey, phoneNumber })
|
|
258
344
|
};
|
|
259
345
|
}
|
|
@@ -304,19 +390,49 @@ function buildActionBar(props) {
|
|
|
304
390
|
};
|
|
305
391
|
}
|
|
306
392
|
/**
|
|
307
|
-
*
|
|
393
|
+
* The theme a new chat widget starts with, from the business's own colour.
|
|
308
394
|
*
|
|
309
|
-
*
|
|
310
|
-
*
|
|
311
|
-
* widget
|
|
395
|
+
* A faithful port of stentor-client's buildDefaultChatTheme, deliberately: the
|
|
396
|
+
* point of moving it is that onboarding can call this instead of its own copy,
|
|
397
|
+
* and that migration should not restyle every widget created afterwards. A
|
|
398
|
+
* thinner theme would have quietly dropped the CTA, message, input, footer and
|
|
399
|
+
* button styling that new widgets have today.
|
|
400
|
+
*
|
|
401
|
+
* The one change is how the contrast colour is derived — see below.
|
|
312
402
|
*/
|
|
313
|
-
function buildChatTheme(primaryColor) {
|
|
314
|
-
const
|
|
403
|
+
function buildChatTheme(primaryColor, secondaryColor) {
|
|
404
|
+
const fontFamily = "RobotoRegular";
|
|
405
|
+
// secondaryColor is the colour of text sitting ON primaryColor — header
|
|
406
|
+
// title, subtitle, footer branding. Left unset it is computed, which is
|
|
407
|
+
// what makes the theme readable against whatever colour was scraped off
|
|
408
|
+
// the business's own site.
|
|
409
|
+
if (!secondaryColor) {
|
|
410
|
+
secondaryColor = (0, color_1.readableTextOn)(primaryColor);
|
|
411
|
+
}
|
|
315
412
|
return {
|
|
316
413
|
zIndex: "10000",
|
|
317
414
|
border: {
|
|
318
415
|
color: primaryColor,
|
|
319
|
-
radius: "10px"
|
|
416
|
+
radius: "10px",
|
|
417
|
+
},
|
|
418
|
+
carousel: {
|
|
419
|
+
button: {
|
|
420
|
+
color: primaryColor,
|
|
421
|
+
},
|
|
422
|
+
subtitle: {
|
|
423
|
+
color: "#979797",
|
|
424
|
+
fontFamily,
|
|
425
|
+
fontSize: "12px",
|
|
426
|
+
fontStyle: "normal",
|
|
427
|
+
fontWeight: "normal",
|
|
428
|
+
},
|
|
429
|
+
title: {
|
|
430
|
+
color: "#000",
|
|
431
|
+
fontFamily,
|
|
432
|
+
fontSize: "14px",
|
|
433
|
+
fontStyle: "normal",
|
|
434
|
+
fontWeight: "bold",
|
|
435
|
+
},
|
|
320
436
|
},
|
|
321
437
|
chatButton: {
|
|
322
438
|
background: primaryColor,
|
|
@@ -324,18 +440,147 @@ function buildChatTheme(primaryColor) {
|
|
|
324
440
|
bottom: "10px",
|
|
325
441
|
left: "10px",
|
|
326
442
|
right: "10px",
|
|
327
|
-
top: "10px"
|
|
328
|
-
}
|
|
443
|
+
top: "10px",
|
|
444
|
+
},
|
|
329
445
|
},
|
|
330
446
|
content: {
|
|
331
|
-
background: "#ffffff"
|
|
447
|
+
background: "#ffffff",
|
|
448
|
+
},
|
|
449
|
+
cta: {
|
|
450
|
+
background: "#e1e2e1",
|
|
451
|
+
text: {
|
|
452
|
+
// Use a dark color that contrasts well with the light grey background
|
|
453
|
+
// instead of primaryColor which may not have sufficient contrast
|
|
454
|
+
color: "#333333",
|
|
455
|
+
fontFamily,
|
|
456
|
+
fontSize: "14px",
|
|
457
|
+
},
|
|
458
|
+
},
|
|
459
|
+
footer: {
|
|
460
|
+
background: primaryColor,
|
|
461
|
+
border: {
|
|
462
|
+
color: primaryColor,
|
|
463
|
+
radius: "0px",
|
|
464
|
+
width: "0px",
|
|
465
|
+
},
|
|
466
|
+
branding: {
|
|
467
|
+
text: {
|
|
468
|
+
color: "#ffffff",
|
|
469
|
+
fontFamily,
|
|
470
|
+
fontSize: "14px",
|
|
471
|
+
fontWeight: "normal",
|
|
472
|
+
fontStyle: "normal",
|
|
473
|
+
},
|
|
474
|
+
},
|
|
332
475
|
},
|
|
333
476
|
header: {
|
|
334
477
|
background: primaryColor,
|
|
478
|
+
avatarBackground: "transparent",
|
|
479
|
+
border: {
|
|
480
|
+
color: primaryColor,
|
|
481
|
+
radius: "0px",
|
|
482
|
+
width: "0px",
|
|
483
|
+
},
|
|
335
484
|
text: {
|
|
336
|
-
color:
|
|
337
|
-
|
|
338
|
-
|
|
485
|
+
color: secondaryColor,
|
|
486
|
+
fontFamily,
|
|
487
|
+
fontSize: "16px",
|
|
488
|
+
fontWeight: "normal",
|
|
489
|
+
fontStyle: "normal",
|
|
490
|
+
},
|
|
491
|
+
subtitle: {
|
|
492
|
+
color: secondaryColor,
|
|
493
|
+
fontFamily,
|
|
494
|
+
fontSize: "10px",
|
|
495
|
+
fontWeight: "normal",
|
|
496
|
+
fontStyle: "normal",
|
|
497
|
+
},
|
|
498
|
+
},
|
|
499
|
+
input: {
|
|
500
|
+
background: "#ffffff",
|
|
501
|
+
border: {
|
|
502
|
+
radius: "20px",
|
|
503
|
+
},
|
|
504
|
+
text: {
|
|
505
|
+
color: "#000000",
|
|
506
|
+
fontFamily,
|
|
507
|
+
fontSize: "14px",
|
|
508
|
+
},
|
|
509
|
+
placeholder: {
|
|
510
|
+
color: "#e1e2e1",
|
|
511
|
+
fontFamily,
|
|
512
|
+
fontSize: "14px",
|
|
513
|
+
fontWeight: "normal",
|
|
514
|
+
fontStyle: "normal",
|
|
515
|
+
},
|
|
516
|
+
},
|
|
517
|
+
textTypingStatus: {
|
|
518
|
+
text: {
|
|
519
|
+
color: "#bfbfbf",
|
|
520
|
+
fontFamily,
|
|
521
|
+
fontSize: "14px",
|
|
522
|
+
fontWeight: "normal",
|
|
523
|
+
fontStyle: "normal",
|
|
524
|
+
},
|
|
525
|
+
},
|
|
526
|
+
menu: {
|
|
527
|
+
item: {
|
|
528
|
+
height: "24px",
|
|
529
|
+
text: {
|
|
530
|
+
fontSize: "14px",
|
|
531
|
+
},
|
|
532
|
+
},
|
|
533
|
+
},
|
|
534
|
+
menuButton: {
|
|
535
|
+
color: "#ffffff",
|
|
536
|
+
},
|
|
537
|
+
messages: {
|
|
538
|
+
maxWidth: "400px",
|
|
539
|
+
mine: {
|
|
540
|
+
bubbleColor: "#f2f2f2",
|
|
541
|
+
text: {
|
|
542
|
+
color: "#454545",
|
|
543
|
+
fontFamily,
|
|
544
|
+
fontSize: "18px",
|
|
545
|
+
fontStyle: "normal",
|
|
546
|
+
fontWeight: "normal",
|
|
547
|
+
},
|
|
548
|
+
},
|
|
549
|
+
others: {
|
|
550
|
+
bubbleColor: "#e1e2e1",
|
|
551
|
+
text: {
|
|
552
|
+
color: "#262726",
|
|
553
|
+
fontFamily,
|
|
554
|
+
fontSize: "18px",
|
|
555
|
+
fontStyle: "normal",
|
|
556
|
+
fontWeight: "normal",
|
|
557
|
+
},
|
|
558
|
+
},
|
|
559
|
+
padding: {
|
|
560
|
+
bottom: "0px",
|
|
561
|
+
left: "20px",
|
|
562
|
+
right: "16px",
|
|
563
|
+
top: "16px",
|
|
564
|
+
},
|
|
565
|
+
},
|
|
566
|
+
refreshButton: {
|
|
567
|
+
color: secondaryColor,
|
|
568
|
+
},
|
|
569
|
+
minimizeButton: {
|
|
570
|
+
color: secondaryColor,
|
|
571
|
+
},
|
|
572
|
+
cancelButton: {
|
|
573
|
+
color: secondaryColor,
|
|
574
|
+
},
|
|
575
|
+
primaryColor: primaryColor,
|
|
576
|
+
sendButton: {
|
|
577
|
+
color: "#505050",
|
|
578
|
+
width: "20px",
|
|
579
|
+
},
|
|
580
|
+
size: {
|
|
581
|
+
width: "500px",
|
|
582
|
+
height: "700px",
|
|
583
|
+
},
|
|
339
584
|
};
|
|
340
585
|
}
|
|
341
586
|
|