@meetelise/chat 1.20.64 → 1.20.65

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.
@@ -1,134 +1,30 @@
1
1
  import { css } from "lit";
2
2
 
3
- // hacky way to get the animation to not jump after loop.
4
- const elementsInScrollList = 16; // 4 elements in list, looped over 4 times
5
- const emojiListAnimation = css`
6
- .scroll-container {
7
- height: 32px;
8
- position: relative;
9
- overflow: hidden;
10
- }
11
- .scroll-container > ul {
12
- display: flex;
13
- flex-direction: column;
14
- animation: move calc(3s * ${elementsInScrollList})
15
- steps(${elementsInScrollList}) infinite;
16
- list-style: none;
17
- padding: 0;
18
- margin-top: 25px;
19
-
20
- -webkit-transition: 1.5s;
21
- -moz-transition: 1.5s;
22
- transition: 1.5s;
23
-
24
- -webkit-transition-timing-function: linear;
25
- -moz-transition-timing-function: linear;
26
- transition-timing-function: linear;
27
- }
28
- .scroll-container > ul li {
29
- width: 100%;
30
- animation: liMove calc(3s) infinite;
31
- display: flex;
32
- align-items: center;
33
- height: 18px;
34
-
35
- font-family: "Helvetica Neue", Arial;
36
- font-style: normal;
37
- font-size: 12px;
38
- font-weight: 700;
39
- color: #ffffff;
40
-
41
- -webkit-transition: 1.5s;
42
- -moz-transition: 1.5s;
43
- transition: 1.5s;
44
-
45
- -webkit-transition-timing-function: linear;
46
- -moz-transition-timing-function: linear;
47
- transition-timing-function: linear;
48
- }
49
- @keyframes move {
50
- 0% {
51
- transform: translate(0, 0);
52
- }
53
- 100% {
54
- transform: translate(
55
- 0,
56
- calc(${elementsInScrollList} * 18 * -1px)
57
- ); // 18 is the height of each list item
58
- }
59
- }
60
- @keyframes liMove {
61
- 0% {
62
- transform: translate(0, 0);
63
- }
64
- 80%,
65
- 100% {
66
- transform: translate(0, calc(18 * -1px));
67
- }
68
- }
69
- `;
70
-
71
3
  export const typeEmojiStyles = css`
72
4
  .type-hey__list {
73
5
  width: 280px;
74
6
  height: 300px;
75
7
  margin-right: 4px;
76
8
 
77
- border: 2px solid #a6b4ff;
78
-
79
9
  border-radius: 16px;
80
10
  display: flex;
81
11
  flex-direction: column;
82
12
  align-items: center;
83
13
  position: relative;
14
+ box-shadow: rgba(99, 99, 99, 0.2) 0px 2px 8px 0px;
84
15
  }
85
16
  .type-hey__top-section {
86
17
  width: 100%;
87
18
  border-radius: 16px;
88
19
  height: calc(100% + 56px);
89
20
  }
90
- .type-hey__top-section:hover {
91
- cursor: pointer;
92
- background: rgba(0, 0, 0, 0.6);
21
+ #type-hey__ask-prompt:hover {
22
+ filter: brightness(0.9);
93
23
  }
94
24
  .type-hey__chat-container {
95
25
  position: absolute;
96
26
  bottom: -72px;
97
27
  right: 0px;
98
-
99
- width: 56px;
100
- height: 56px;
101
-
102
- background: #ffffff;
103
- border: 1px solid rgba(95, 95, 95, 0.2);
104
- border-radius: 1000px;
105
-
106
- display: flex;
107
- justify-content: center;
108
- align-items: center;
109
- }
110
- .type-hey__inner-chat-container {
111
- width: 46px;
112
- height: 46px;
113
-
114
- border-radius: 1000px;
115
- border: 1px solid rgba(255, 255, 255, 0.7);
116
-
117
- display: flex;
118
- justify-content: center;
119
- align-items: center;
120
- }
121
- .type-hey__chat-icon {
122
- width: 100%;
123
- height: 100%;
124
- display: flex;
125
- justify-content: center;
126
- align-items: center;
127
- border-radius: 1000px;
128
- }
129
- .type-hey__chat-icon:hover {
130
- cursor: pointer;
131
- background: rgba(0, 0, 0, 0.6);
132
28
  }
133
29
 
134
30
  .type-hey__bottom-section {
@@ -238,50 +134,15 @@ export const typeEmojiStyles = css`
238
134
  color: #ffffff;
239
135
  }
240
136
 
241
- .type-hey__ask-prompt {
137
+ #type-hey__ask-prompt {
242
138
  margin: auto;
243
139
  margin-top: 9px;
244
140
  margin-bottom: 24px;
245
141
 
246
- width: 220px;
247
- height: 32px;
248
- border: 1px solid rgba(255, 255, 255, 0.2);
249
-
250
- background: linear-gradient(
251
- 180deg,
252
- rgba(255, 255, 255, 0.55) -6.25%,
253
- rgba(255, 255, 255, 0) 35.46%,
254
- rgba(255, 255, 255, 0) 72.81%,
255
- #ffffff 104.69%
256
- );
257
- border-radius: 4px;
258
- cursor: pointer;
259
- }
260
- .type-hey__ask-prompt-inner {
261
- width: calc(100% - 30px); // 30px for padding
262
- height: 100%;
142
+ border: 1px solid rgba(255, 255, 255, 0.8);
143
+ box-shadow: 0px 12px 10px rgba(0, 0, 0, 0.1);
263
144
 
264
- display: flex;
265
- justify-content: space-between;
266
- align-items: center;
267
- padding-left: 15px;
268
- padding-right: 15px;
269
- position: relative;
270
- z-index: 10000000000000000000000000000000000000000000000000000000;
271
- }
272
- .type-hey__ask-start {
273
- display: flex;
274
- align-items: center;
275
- gap: 8px;
276
- }
277
- .type-hey__ask-start > h2 {
278
- font-family: "Helvetica Neue", Arial;
279
- font-style: normal;
280
- font-weight: 400;
281
- font-size: 12px;
282
- line-height: 18px;
283
145
  color: #ffffff;
284
- margin: 0;
285
146
  }
286
147
 
287
148
  .type-hey__bottom-info {
@@ -297,5 +158,4 @@ export const typeEmojiStyles = css`
297
158
  .title-bold {
298
159
  font-weight: 700;
299
160
  }
300
- ${emojiListAnimation}
301
161
  `;
@@ -21,7 +21,6 @@ export const typeMobileStyles = css`
21
21
  gap: 3px;
22
22
  }
23
23
  .typeMobile-bttn {
24
- background: #c4c4c4;
25
24
  width: 54px;
26
25
  height: 54px;
27
26
 
@@ -14,12 +14,13 @@ import {
14
14
  fetchFeatureFlagShowMarketingSourceDropdown,
15
15
  fetchFeatureFlagUsePhoneNumberBySource,
16
16
  } from "../fetchFeatureFlag";
17
+ import fetchWebchatPreferences from "../fetchWebchatPreferences";
17
18
  import fetchBuildingABTestType from "../fetchBuildingABTestType";
18
19
  import fetchCurrentParsedLeadSource from "../fetchCurrentParsedLeadSource";
19
20
  import fetchPhoneNumberFromSource, {
20
21
  NumberForSelectedSource,
21
22
  } from "../fetchPhoneNumberFromSource";
22
- import { getTheme, Theme } from "../themes";
23
+ import { defaultBrandColor, getTheme, Theme } from "../themes";
23
24
  import { isMobile } from "../utils";
24
25
  import { installLauncher } from "./launcher/Launcher";
25
26
  import parseISO from "date-fns/parseISO";
@@ -33,6 +34,9 @@ import formatISO from "date-fns/formatISO";
33
34
  import fetchLeadSources from "../fetchLeadSources";
34
35
  import { formatPhoneNumber } from "./actions/formatPhoneNumber";
35
36
 
37
+ import "./actions/minimize-expand-button";
38
+ import "./launcher/mobile-launcher";
39
+
36
40
  @customElement("me-chat")
37
41
  export class MEChat extends LitElement {
38
42
  static styles = css`
@@ -55,41 +59,30 @@ export class MEChat extends LitElement {
55
59
  .showTab {
56
60
  display: flex;
57
61
  align-items: center;
58
- justify-content: flex-start;
62
+ justify-content: space-between;
59
63
  gap: 32px;
60
64
  }
61
- #contactTabPopup {
65
+ #chatAdditionalActions {
62
66
  position: fixed;
63
- width: 300px;
64
- padding-left: 20px;
65
- padding-right: 20px;
66
- padding-bottom: 24px;
67
+ box-sizing: border-box;
68
+
69
+ box-sizing: border-box;
70
+ width: 340px;
71
+
72
+ padding-top: 0px;
73
+ padding-right: 12px;
74
+
67
75
  z-index: 1000000000;
68
- background: #000000;
69
- border-radius: 10px 0px 0px 0px;
70
- border-width: 1px 0px 0px 1px;
71
- border-style: solid;
72
- border-color: #ffffff;
73
76
  }
74
- .actionTabBttn {
75
- font-family: "Helvetica Neue", Arial;
76
- font-style: normal;
77
- font-size: 14px;
78
- line-height: 18px;
79
- color: #ffffff;
80
- border: none;
81
- background: none;
82
- padding: 0;
83
- display: flex;
84
- align-items: center;
85
- gap: 8px;
86
- }
87
- .actionTabBttn:hover {
88
- cursor: pointer;
89
- color: #f0f0f0;
90
- }
91
- .heavyLabel {
92
- font-weight: bold;
77
+ #chatAdditionalActions::after {
78
+ content: "";
79
+ position: absolute;
80
+ top: -6px;
81
+ right: 0;
82
+ width: 0;
83
+ height: 0;
84
+ border-bottom: 22px solid transparent;
85
+ border-right: 30px solid black;
93
86
  }
94
87
  `;
95
88
  static session: Promise<Talk.Session> = Talk.ready.then(() => {
@@ -117,8 +110,11 @@ export class MEChat extends LitElement {
117
110
  @property({ type: Object })
118
111
  launcherStyles: StyleInfo = {};
119
112
 
113
+ @property({ type: Boolean })
114
+ isMinimized = false;
115
+
120
116
  @property({ type: String })
121
- private brandColor = null;
117
+ private brandColor: string | null = null;
122
118
 
123
119
  @state()
124
120
  private popup: Talk.Popup | null = null;
@@ -178,6 +174,7 @@ export class MEChat extends LitElement {
178
174
  currentLeadSource,
179
175
  featureFlagShowDropdown,
180
176
  featureFlagUseDNI,
177
+ webchatPreferences,
181
178
  ] = await Promise.all([
182
179
  fetchBuildingInfo(this.orgSlug, this.buildingSlug),
183
180
  fetchBuildingABTestType(this.buildingSlug),
@@ -185,6 +182,7 @@ export class MEChat extends LitElement {
185
182
  fetchCurrentParsedLeadSource(this.buildingSlug, document.referrer),
186
183
  fetchFeatureFlagShowMarketingSourceDropdown(this.buildingSlug),
187
184
  fetchFeatureFlagUsePhoneNumberBySource(this.buildingSlug),
185
+ fetchWebchatPreferences(this.buildingSlug),
188
186
  ]);
189
187
 
190
188
  building.phoneNumber = formatPhoneNumber(building.phoneNumber);
@@ -194,6 +192,19 @@ export class MEChat extends LitElement {
194
192
  this.currentLeadSource = currentLeadSource;
195
193
  this.featureFlagShowDropdown = featureFlagShowDropdown;
196
194
 
195
+ if (webchatPreferences) {
196
+ if (this.brandColor === null) {
197
+ this.brandColor = webchatPreferences.primaryColor ?? null;
198
+ }
199
+ if (this.buildingABTestType === null) {
200
+ this.buildingABTestType = webchatPreferences.designConcept ?? null;
201
+ }
202
+ }
203
+
204
+ if (this.brandColor === null) {
205
+ this.brandColor = defaultBrandColor;
206
+ }
207
+
197
208
  let phoneNumberForSource = null;
198
209
  if (featureFlagUseDNI) {
199
210
  phoneNumberForSource = await fetchPhoneNumberFromSource(
@@ -346,17 +357,11 @@ export class MEChat extends LitElement {
346
357
  (talkjsPopupElement as HTMLElement).style.zIndex = "99999999999";
347
358
  this.popup = popup;
348
359
 
349
- const forceClose = true; // if we want to always keep the widget in the closed state
350
360
  const autoOpenedTimestamp = sessionStorage.getItem("autoOpenedTimestamp");
351
361
  const shouldAutoOpen =
352
362
  !autoOpenedTimestamp ||
353
363
  (autoOpenedTimestamp && isPast(parseISO(autoOpenedTimestamp)));
354
- if (
355
- !forceClose &&
356
- building.autoOpenChatWidget &&
357
- shouldAutoOpen &&
358
- !isMobile()
359
- ) {
364
+ if (building.autoOpenChatWidget && shouldAutoOpen && !isMobile()) {
360
365
  this.popup.show();
361
366
  this.hideLauncher = true;
362
367
  this.hasMounted = true;
@@ -431,9 +436,44 @@ export class MEChat extends LitElement {
431
436
  this.launcherRef.value?.onClickPhoneOption(e);
432
437
  };
433
438
 
439
+ // Talkjs is very limiting with changing it's design and working around it.
440
+ // For the top header contact, we change its pos based on the current x and y
441
+ // of the talkjs popup - so we must adjust its coords on resize and mount
442
+ adjustTopHeaderContactCoords = (): void => {
443
+ if (this.talkjsPopupElement) {
444
+ const talkjsPopupCoords = this.talkjsPopupElement.getBoundingClientRect();
445
+ const headerRef = this.shadowRoot?.getElementById(
446
+ "chatAdditionalActions"
447
+ );
448
+ if (!headerRef) return;
449
+ headerRef.style.left = `${talkjsPopupCoords.left + 20}px`;
450
+ headerRef.style.top = `${talkjsPopupCoords.bottom - 24}px`;
451
+ }
452
+ };
453
+ connectedCallback(): void {
454
+ super.connectedCallback();
455
+ window.addEventListener("resize", this.adjustTopHeaderContactCoords);
456
+ }
457
+ disconnectedCallback(): void {
458
+ window.removeEventListener("resize", this.adjustTopHeaderContactCoords);
459
+ super.disconnectedCallback();
460
+ }
461
+ updated(): void {
462
+ this.adjustTopHeaderContactCoords();
463
+ }
464
+
465
+ onClickMinimize = (e: MouseEvent): void => {
466
+ e.preventDefault();
467
+ e.stopPropagation();
468
+ this.isMinimized = !this.isMinimized;
469
+ };
470
+
434
471
  render(): TemplateResult {
435
472
  installLauncher();
436
473
 
474
+ const showChatAdditionalActions =
475
+ this.hideLauncher && !this.isLoading && !this.isMobile;
476
+
437
477
  return html`
438
478
  <meta name="viewport" content="width=device-width, initial-scale=1 user-scalable=1">
439
479
  <div id="aria-describe-info" style="display: none;">
@@ -464,6 +504,8 @@ export class MEChat extends LitElement {
464
504
  .tourTypeOptions=${this.building?.tourTypeOptions ?? []}
465
505
  .launcherStyles=${this.launcherStyles}
466
506
  .brandColor=${this.brandColor}
507
+ .isMinimized=${this.isMinimized}
508
+ .onClickMinimize=${this.onClickMinimize}
467
509
  .autoOpenChatWidget=${this.building.autoOpenChatWidget ??
468
510
  false}
469
511
  chatCallUsHeader=${this.building?.chatCallUsHeader ?? ""}
@@ -502,6 +544,31 @@ export class MEChat extends LitElement {
502
544
  </meetelise-launcher>`
503
545
  : ""
504
546
  }
547
+
548
+ </div>
549
+ <div
550
+ id="chatAdditionalActions"
551
+ class=${classMap({
552
+ ["showTab"]: showChatAdditionalActions,
553
+ ["hideTab"]: !showChatAdditionalActions,
554
+ })}
555
+ >
556
+ <mobile-launcher
557
+ .onClickMinimize=${this.onClickMinimize}
558
+ .onClickEmailOption=${this.handleContactClicked}
559
+ .onClickPhoneOption=${this.handleContactTabClicked}
560
+ .onClickSSTOption=${this.handleTourClicked}
561
+ .isMobile=${this.isMobile}
562
+ .brandColor=${this.brandColor}
563
+ .hideChat=${true}
564
+ ></mobile-launcher>
565
+ <minimize-expand-button
566
+ .brandColor=${this.brandColor}
567
+ .onClick=${() => {
568
+ this.popup?.hide();
569
+ this.hideLauncher = false;
570
+ }}
571
+ ></minimize-expand-button>
505
572
  </div>
506
573
  </meta>
507
574
  `;
@@ -0,0 +1,28 @@
1
+ import axios from "axios";
2
+
3
+ export interface WebchatPreferences {
4
+ primaryColor: string;
5
+ designConcept: string;
6
+ delayOpen: number;
7
+ }
8
+
9
+ export default async function fetchWebchatPreferences(
10
+ buildingSlug: string
11
+ ): Promise<WebchatPreferences | null> {
12
+ const host = "https://app.meetelise.com";
13
+ try {
14
+ const webchatPreferencesResponse = await axios.get(
15
+ `${host}/platformApi/webchat/${buildingSlug}/customization`
16
+ );
17
+ if (webchatPreferencesResponse.data) {
18
+ return {
19
+ primaryColor: webchatPreferencesResponse.data["primaryColor"],
20
+ designConcept: webchatPreferencesResponse.data["designConcept"],
21
+ delayOpen: +webchatPreferencesResponse.data["delayOpen"],
22
+ };
23
+ }
24
+ return null;
25
+ } catch (_) {
26
+ return null;
27
+ }
28
+ }
package/src/themes.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { hexToAlmostWhite } from "./utils";
2
+
1
3
  const white = "#FFFFFF";
2
4
  const darkGray = "#202020";
3
5
  const lightGray = "#83818E";
@@ -126,7 +128,7 @@ const themesById = {
126
128
  backgroundColor: white,
127
129
  textColor: darkGray,
128
130
  },
129
- chatPaneBackgroundColor: "rgba(193, 193, 193, 0.3)",
131
+ chatPaneBackgroundColor: "rgba(193, 193, 193, 0.5)",
130
132
  message: {
131
133
  user: {
132
134
  textColor: "#FFFFFF",
@@ -154,8 +156,9 @@ export const getTheme = ({
154
156
  ? (themeId as ThemeIdString)
155
157
  : defaultThemeId
156
158
  ];
157
- if (brandColor) {
159
+ if (brandColor && brandColor !== defaultBrandColor) {
158
160
  theme.message.user.backgroundColor = brandColor;
161
+ theme.chatHeader.backgroundColor = hexToAlmostWhite(brandColor, 0.6);
159
162
  }
160
163
  return theme;
161
164
  };
package/src/utils.ts CHANGED
@@ -30,11 +30,9 @@ export const tintColor = (
30
30
  originalColor: string,
31
31
  tintOpacity: number
32
32
  ): string => {
33
- // Convert the original color and tint color to RGB values
34
33
  const originalRgb = hexToRgb(originalColor);
35
34
  const tintRgb = { r: 0, g: 0, b: 0 };
36
35
 
37
- // Calculate the new color with tint added
38
36
  const newRgb = {
39
37
  r: originalRgb.r + Math.round((tintRgb.r - originalRgb.r) * tintOpacity),
40
38
  g: originalRgb.g + Math.round((tintRgb.g - originalRgb.g) * tintOpacity),
@@ -62,3 +60,21 @@ const rgbToHex = (r: number, g: number, b: number): string => {
62
60
  .join("")
63
61
  );
64
62
  };
63
+
64
+ export const hexToAlmostWhite = (hexColor: string, ratio: number): string => {
65
+ let r = parseInt(hexColor.substr(1, 2), 16);
66
+ let g = parseInt(hexColor.substr(3, 2), 16);
67
+ let b = parseInt(hexColor.substr(5, 2), 16);
68
+
69
+ // the ratio of white to add (1 = white, 0 = no change)
70
+ r = Math.round(r * (1 - ratio) + 255 * ratio);
71
+ g = Math.round(g * (1 - ratio) + 255 * ratio);
72
+ b = Math.round(b * (1 - ratio) + 255 * ratio);
73
+
74
+ return (
75
+ "#" +
76
+ r.toString(16).padStart(2, "0") +
77
+ g.toString(16).padStart(2, "0") +
78
+ b.toString(16).padStart(2, "0")
79
+ );
80
+ };