@meetelise/chat 1.9.1 → 1.12.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/.eslintrc.cjs +1 -0
- package/.github/workflows/release.yml +1 -0
- package/.vscode/settings.json +6 -1
- package/CONTRIBUTING.md +8 -0
- package/package.json +9 -10
- package/public/demo/index.html +78 -11
- package/public/dist/index.js +1714 -1
- package/public/dist/index.js.LICENSE.txt +26 -14
- package/public/index.html +2 -1
- package/src/MEChat.test.ts +5 -5
- package/src/MEChat.ts +53 -0
- package/src/WebComponent/InHouseLauncher.ts +446 -0
- package/src/WebComponent/MEChat.css +5 -0
- package/src/WebComponent/MEChat.ts +282 -0
- package/src/WebComponent/OfficeHours.ts +73 -0
- package/src/WebComponent/Scheduler/date-picker.ts +320 -0
- package/src/WebComponent/Scheduler/me-select.ts +244 -0
- package/src/WebComponent/Scheduler/time-picker.ts +101 -0
- package/src/WebComponent/Scheduler/tour-scheduler.ts +383 -0
- package/src/WebComponent/Scheduler/tour-type-option.ts +92 -0
- package/src/WebComponent/actions/ActionConfirmButton.ts +94 -0
- package/src/WebComponent/actions/CallUsWindow.ts +110 -0
- package/src/WebComponent/actions/DetailsWindow.ts +109 -0
- package/src/WebComponent/actions/EmailUsWindow.ts +432 -0
- package/src/WebComponent/actions/InputStyles.ts +31 -0
- package/src/WebComponent/actions/TextUsWindow.ts +226 -0
- package/src/WebComponent/actions/formatPhoneNumber.ts +42 -0
- package/src/WebComponent/inHouseLauncherStyles.ts +300 -0
- package/src/WebComponent/index.ts +2 -0
- package/src/WebComponent/utils.ts +82 -0
- package/src/analytics.ts +48 -15
- package/src/assetUrls.ts +4 -0
- package/src/fetchBuildingInfo.ts +1 -0
- package/src/getAvailabilities.ts +71 -0
- package/src/themes.ts +5 -3
- package/tsconfig.json +9 -3
- package/web-test-runner.config.js +0 -6
- package/webpack.config.cjs +8 -25
- package/public/dist/index.d.ts +0 -1
- package/public/dist/src/ChatButton.d.ts +0 -9
- package/public/dist/src/ChatIcon.d.ts +0 -6
- package/public/dist/src/InHouseLauncher.d.ts +0 -11
- package/public/dist/src/MEChat.d.ts +0 -73
- package/public/dist/src/analytics.d.ts +0 -18
- package/public/dist/src/chatID.d.ts +0 -11
- package/public/dist/src/createConversation.d.ts +0 -4
- package/public/dist/src/fetchBuildingInfo.d.ts +0 -25
- package/public/dist/src/themes.d.ts +0 -52
- package/public/dist/src/utils.d.ts +0 -2
- package/src/ChatButton.module.scss +0 -52
- package/src/ChatButton.tsx +0 -26
- package/src/ChatIcon.tsx +0 -26
- package/src/DemoApp.tsx +0 -113
- package/src/InHouseLauncher.module.scss +0 -140
- package/src/InHouseLauncher.tsx +0 -65
- package/src/MEChat.module.scss +0 -22
- package/src/MEChat.tsx +0 -293
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { css, html, LitElement, TemplateResult } from "lit";
|
|
2
|
+
import { customElement, property, state } from "lit/decorators.js";
|
|
3
|
+
import { classMap } from "lit/directives/class-map.js";
|
|
4
|
+
import { createRef, ref, Ref } from "lit/directives/ref.js";
|
|
5
|
+
import { installActionConfirmButton } from "./ActionConfirmButton";
|
|
6
|
+
import { installDetailsWindow } from "./DetailsWindow";
|
|
7
|
+
import {
|
|
8
|
+
formatToPhone,
|
|
9
|
+
isModifierKey,
|
|
10
|
+
isNumericInput,
|
|
11
|
+
} from "./formatPhoneNumber";
|
|
12
|
+
import { InputStyles } from "./InputStyles";
|
|
13
|
+
import axios from "axios";
|
|
14
|
+
|
|
15
|
+
@customElement("text-us-window")
|
|
16
|
+
export class TextUsWindow extends LitElement {
|
|
17
|
+
static styles = [
|
|
18
|
+
css`
|
|
19
|
+
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700;900&display=swap");
|
|
20
|
+
.text-us-wrapper {
|
|
21
|
+
font-family: "Poppins";
|
|
22
|
+
color: #202020;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.text-us-window__description {
|
|
26
|
+
font-size: 18px;
|
|
27
|
+
line-height: 24px;
|
|
28
|
+
margin-top: 32px;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.text-us-window__phone-input {
|
|
32
|
+
width: -webkit-fill-available;
|
|
33
|
+
height: 49px;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.text-us-window__vertical-spacer {
|
|
37
|
+
height: 20px;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.text-us-window__subtext {
|
|
41
|
+
font-size: 12px;
|
|
42
|
+
line-height: 22px;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.text-us-window__error {
|
|
46
|
+
font-size: 10px;
|
|
47
|
+
line-height: 22px;
|
|
48
|
+
margin-top: 8px;
|
|
49
|
+
}
|
|
50
|
+
`,
|
|
51
|
+
InputStyles,
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
@property({ attribute: false })
|
|
55
|
+
onCloseClicked?: (e: MouseEvent) => void;
|
|
56
|
+
|
|
57
|
+
@property({ attribute: false })
|
|
58
|
+
buildingId = 0;
|
|
59
|
+
|
|
60
|
+
phoneNumberInputRef: Ref<HTMLInputElement> = createRef();
|
|
61
|
+
|
|
62
|
+
@state()
|
|
63
|
+
phoneNumber = "";
|
|
64
|
+
@state()
|
|
65
|
+
hasSubmittedForm = false;
|
|
66
|
+
@state()
|
|
67
|
+
hasPhoneNumberError = false;
|
|
68
|
+
@state()
|
|
69
|
+
hasSubmissionError = false;
|
|
70
|
+
@state()
|
|
71
|
+
isSubmitting = false;
|
|
72
|
+
|
|
73
|
+
onChangePhoneNumber = (e: Event): void => {
|
|
74
|
+
if (!e.target || !this.phoneNumberInputRef.value) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (isModifierKey(e as KeyboardEvent)) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const inputElement = e.target as HTMLInputElement;
|
|
81
|
+
|
|
82
|
+
this.phoneNumber = formatToPhone(inputElement.value);
|
|
83
|
+
|
|
84
|
+
this.phoneNumberInputRef.value.value = this.phoneNumber;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
enforceFormat = (e: KeyboardEvent): void => {
|
|
88
|
+
if (!isNumericInput(e) && !isModifierKey(e)) {
|
|
89
|
+
e.preventDefault();
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
validateFormFields = (): void => {
|
|
94
|
+
this.hasPhoneNumberError = false;
|
|
95
|
+
this.hasSubmissionError = false;
|
|
96
|
+
if (!this.phoneNumber || this.phoneNumber.length !== 14) {
|
|
97
|
+
this.hasPhoneNumberError = true;
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
onClick = async (): Promise<void> => {
|
|
102
|
+
this.validateFormFields();
|
|
103
|
+
if (this.hasPhoneNumberError) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
this.isSubmitting = true;
|
|
108
|
+
await createTextWithUs(this.phoneNumber, this.buildingId);
|
|
109
|
+
this.hasSubmittedForm = true;
|
|
110
|
+
this.isSubmitting = false;
|
|
111
|
+
} catch (e) {
|
|
112
|
+
this.isSubmitting = false;
|
|
113
|
+
this.hasSubmissionError = true;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
render = (): TemplateResult => {
|
|
118
|
+
installDetailsWindow();
|
|
119
|
+
installActionConfirmButton();
|
|
120
|
+
if (this.hasSubmittedForm) {
|
|
121
|
+
return html`
|
|
122
|
+
<details-window
|
|
123
|
+
headerText="Text us"
|
|
124
|
+
.onCloseClick=${this.onCloseClicked}
|
|
125
|
+
>
|
|
126
|
+
<div class="text-us-wrapper">
|
|
127
|
+
<div class="text-us-window__vertical-spacer"></div>
|
|
128
|
+
<svg
|
|
129
|
+
width="20"
|
|
130
|
+
height="20"
|
|
131
|
+
viewBox="0 0 20 20"
|
|
132
|
+
fill="none"
|
|
133
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
134
|
+
>
|
|
135
|
+
<path
|
|
136
|
+
d="M4.455 16L0 19.5V1C0 0.734784 0.105357 0.48043 0.292893 0.292893C0.48043 0.105357 0.734784 0 1 0H19C19.2652 0 19.5196 0.105357 19.7071 0.292893C19.8946 0.48043 20 0.734784 20 1V15C20 15.2652 19.8946 15.5196 19.7071 15.7071C19.5196 15.8946 19.2652 16 19 16H4.455ZM2 15.385L3.763 14H18V2H2V15.385ZM10 7V4L14 8L10 12V9H6V7H10Z"
|
|
137
|
+
fill="#202020"
|
|
138
|
+
/>
|
|
139
|
+
</svg>
|
|
140
|
+
<div class="text-us-window__description">
|
|
141
|
+
Thank you!<br />Look for a text message from our team. We can
|
|
142
|
+
answer questions and help you book a tour through text.
|
|
143
|
+
</div>
|
|
144
|
+
<div class="text-us-window__vertical-spacer"></div>
|
|
145
|
+
<div class="text-us-window__subtext">
|
|
146
|
+
Opt out at anytime by texting “Stop”
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
149
|
+
</details-window>
|
|
150
|
+
`;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return html`
|
|
154
|
+
<details-window headerText="Text us" .onCloseClick=${this.onCloseClicked}>
|
|
155
|
+
<div class="text-us-wrapper">
|
|
156
|
+
<div class="text-us-window__description">
|
|
157
|
+
Have questions? <br />
|
|
158
|
+
Our team can answer via <br />text message.
|
|
159
|
+
</div>
|
|
160
|
+
<div class="text-us-window__vertical-spacer"></div>
|
|
161
|
+
<input
|
|
162
|
+
${ref(this.phoneNumberInputRef)}
|
|
163
|
+
maxlength="14"
|
|
164
|
+
type="text"
|
|
165
|
+
placeholder="Phone number"
|
|
166
|
+
inputmode="tel"
|
|
167
|
+
class=${classMap({
|
|
168
|
+
["webchat-input"]: true,
|
|
169
|
+
["text-us-window__phone-input"]: true,
|
|
170
|
+
})}
|
|
171
|
+
.value=${this.phoneNumber}
|
|
172
|
+
@keydown=${this.enforceFormat}
|
|
173
|
+
@keyup=${this.onChangePhoneNumber}
|
|
174
|
+
/>
|
|
175
|
+
${this.hasPhoneNumberError
|
|
176
|
+
? html`
|
|
177
|
+
<div class="text-us-window__error">
|
|
178
|
+
Enter a valid phone number
|
|
179
|
+
</div>
|
|
180
|
+
`
|
|
181
|
+
: ""}
|
|
182
|
+
<div class="text-us-window__vertical-spacer"></div>
|
|
183
|
+
<action-confirm-button
|
|
184
|
+
.onClick=${this.onClick}
|
|
185
|
+
.isLoading=${this.isSubmitting}
|
|
186
|
+
text="Send"
|
|
187
|
+
></action-confirm-button>
|
|
188
|
+
<div class="text-us-window__vertical-spacer"></div>
|
|
189
|
+
<div class="text-us-window__subtext">
|
|
190
|
+
By entering your number and checking the box, you consent to be
|
|
191
|
+
contacted by our AI Leasing Assistant. Your consent to this process
|
|
192
|
+
is not a requirement for leasing at our property. Message and data
|
|
193
|
+
rates may apply.
|
|
194
|
+
</div>
|
|
195
|
+
</div>
|
|
196
|
+
</details-window>
|
|
197
|
+
`;
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export const installTextUsWindow = (): void => {
|
|
202
|
+
if (!window.customElements.get("text-us-window")) {
|
|
203
|
+
window.customElements.define("text-us-window", TextUsWindow);
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
const createTextWithUs = async (rawPhoneNumber: string, buildingId: number) => {
|
|
208
|
+
const formattedPhoneNumber =
|
|
209
|
+
"+1" +
|
|
210
|
+
rawPhoneNumber
|
|
211
|
+
.replace("(", "")
|
|
212
|
+
.replace(")", "")
|
|
213
|
+
.replace(" ", "")
|
|
214
|
+
.replace("-", "");
|
|
215
|
+
const requestBody = {
|
|
216
|
+
building_id: buildingId,
|
|
217
|
+
lead_source: "property-website",
|
|
218
|
+
phone_number: formattedPhoneNumber,
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
await axios.post(
|
|
222
|
+
"https://app.meetelise.com/platformApi/state/create/textMe",
|
|
223
|
+
requestBody,
|
|
224
|
+
{ headers: { ["X-SecurityKey"]: "JRL8jV4VcSCwOSir5gWkpgNLfKghmhBG" } }
|
|
225
|
+
);
|
|
226
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* For now, only handles the US phone number case.....
|
|
3
|
+
*/
|
|
4
|
+
export const formatToPhone = (phoneNumber: string): string => {
|
|
5
|
+
const input = phoneNumber.replace(/\D/g, "").substring(0, 10);
|
|
6
|
+
const zip = input.substring(0, 3);
|
|
7
|
+
const middle = input.substring(3, 6);
|
|
8
|
+
const last = input.substring(6, 10);
|
|
9
|
+
|
|
10
|
+
if (input.length > 6) {
|
|
11
|
+
return `(${zip}) ${middle}-${last}`;
|
|
12
|
+
}
|
|
13
|
+
if (input.length > 3) {
|
|
14
|
+
return `(${zip}) ${middle}`;
|
|
15
|
+
}
|
|
16
|
+
if (input.length > 0) {
|
|
17
|
+
return `(${zip}`;
|
|
18
|
+
}
|
|
19
|
+
return "";
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const isNumericInput = (event: KeyboardEvent): boolean => {
|
|
23
|
+
const key = event.keyCode;
|
|
24
|
+
return (key >= 48 && key <= 57) || (key >= 96 && key <= 105);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const isModifierKey = (event: KeyboardEvent): boolean => {
|
|
28
|
+
const key = event.keyCode;
|
|
29
|
+
// Allow left, up, right, down, Backspace, Tab, Enter, Delete, Ctrl/Command + A,C,V,X,Z, Shift, Home, End
|
|
30
|
+
return (
|
|
31
|
+
event.shiftKey === true ||
|
|
32
|
+
key === 35 ||
|
|
33
|
+
key === 36 || // Allow
|
|
34
|
+
key === 8 ||
|
|
35
|
+
key === 9 ||
|
|
36
|
+
key === 13 ||
|
|
37
|
+
key === 46 ||
|
|
38
|
+
(key > 36 && key < 41) ||
|
|
39
|
+
((event.ctrlKey === true || event.metaKey === true) &&
|
|
40
|
+
(key === 65 || key === 67 || key === 86 || key === 88 || key === 90))
|
|
41
|
+
);
|
|
42
|
+
};
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { css } from "lit";
|
|
2
|
+
|
|
3
|
+
export const inHousLauncherStyles = css`
|
|
4
|
+
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700;900&display=swap");
|
|
5
|
+
:host {
|
|
6
|
+
--glowBarHeight: 11.2px;
|
|
7
|
+
--enterAnimationDuration: 0.5s;
|
|
8
|
+
--desktopZIndex: 100000;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
@keyframes slideInFromRight {
|
|
12
|
+
from {
|
|
13
|
+
transform: translateX(100%);
|
|
14
|
+
}
|
|
15
|
+
to {
|
|
16
|
+
transform: translateX(0);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
.launcher {
|
|
20
|
+
font-family: Poppins;
|
|
21
|
+
user-select: none;
|
|
22
|
+
position: fixed;
|
|
23
|
+
}
|
|
24
|
+
.launcher:not(.miniLauncher) {
|
|
25
|
+
display: flex;
|
|
26
|
+
justify-content: space-evenly;
|
|
27
|
+
align-items: center;
|
|
28
|
+
background-color: rgba(255, 255, 255, 0.8);
|
|
29
|
+
color: #202020;
|
|
30
|
+
backdrop-filter: blur(10px);
|
|
31
|
+
box-shadow: 0px 8px 8px 4px rgba(0, 0, 0, 0.25);
|
|
32
|
+
}
|
|
33
|
+
.launcher:not(.miniLauncher).in-house-launcher__mobile {
|
|
34
|
+
width: 100%;
|
|
35
|
+
bottom: 0px;
|
|
36
|
+
left: 0px;
|
|
37
|
+
padding: 5px;
|
|
38
|
+
}
|
|
39
|
+
.launcher:not(.miniLauncher).in-house-launcher__desktop {
|
|
40
|
+
width: 245px;
|
|
41
|
+
height: 112px;
|
|
42
|
+
padding-left: 10px;
|
|
43
|
+
}
|
|
44
|
+
.launcher.in-house-launcher__desktop {
|
|
45
|
+
right: 0px;
|
|
46
|
+
overflow: hidden;
|
|
47
|
+
border-radius: 10px 0px 0px 10px;
|
|
48
|
+
bottom: 40px;
|
|
49
|
+
z-index: 100000;
|
|
50
|
+
}
|
|
51
|
+
.launcher.in-house-launcher__desktop.firstMount {
|
|
52
|
+
animation: slideInFromRight var(--enterAnimationDuration);
|
|
53
|
+
}
|
|
54
|
+
.launcher .glowBar {
|
|
55
|
+
overflow: hidden;
|
|
56
|
+
background-position: center;
|
|
57
|
+
position: absolute;
|
|
58
|
+
top: 3px;
|
|
59
|
+
left: 3px;
|
|
60
|
+
height: 11.2px;
|
|
61
|
+
width: 100%;
|
|
62
|
+
object-fit: fill;
|
|
63
|
+
border-top-left-radius: 10px;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.launcher .glowBar + * {
|
|
67
|
+
margin-top: calc(var(--glowBarHeight) + 8px);
|
|
68
|
+
}
|
|
69
|
+
.launcher .content {
|
|
70
|
+
display: flex;
|
|
71
|
+
flex-direction: column;
|
|
72
|
+
align-items: center;
|
|
73
|
+
gap: 10px;
|
|
74
|
+
margin-bottom: 6px;
|
|
75
|
+
}
|
|
76
|
+
.launcher .content .header {
|
|
77
|
+
display: flex;
|
|
78
|
+
align-items: center;
|
|
79
|
+
}
|
|
80
|
+
.launcher .content .header .headerText {
|
|
81
|
+
font-weight: 600;
|
|
82
|
+
font-size: 20px;
|
|
83
|
+
}
|
|
84
|
+
.launcher .content .subtitle {
|
|
85
|
+
font-size: 12px;
|
|
86
|
+
font-weight: 600;
|
|
87
|
+
}
|
|
88
|
+
.miniLauncher {
|
|
89
|
+
display: flex;
|
|
90
|
+
align-items: center;
|
|
91
|
+
background-color: #202020;
|
|
92
|
+
position: fixed;
|
|
93
|
+
}
|
|
94
|
+
.miniLauncher:hover {
|
|
95
|
+
background: radial-gradient(
|
|
96
|
+
36.85% 65.32% at 50% 106.31%,
|
|
97
|
+
#03ecc4 0%,
|
|
98
|
+
rgba(131, 129, 142, 1) 100%
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
.miniLauncher.firstMount {
|
|
102
|
+
animation: slideInFromRight 0.5s;
|
|
103
|
+
}
|
|
104
|
+
.miniLauncher.in-house-launcher__desktop {
|
|
105
|
+
padding-right: 20px;
|
|
106
|
+
right: 0px;
|
|
107
|
+
overflow: hidden;
|
|
108
|
+
bottom: 40px;
|
|
109
|
+
z-index: var(--desktopZIndex);
|
|
110
|
+
}
|
|
111
|
+
.miniLauncher.in-house-launcher__mobile {
|
|
112
|
+
right: 10px;
|
|
113
|
+
bottom: 20px;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.in-house-launcher__primary-action-text {
|
|
117
|
+
font-family: "Poppins";
|
|
118
|
+
font-weight: 700;
|
|
119
|
+
font-size: 24px;
|
|
120
|
+
line-height: 22px;
|
|
121
|
+
cursor: pointer;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.in-house-launcher__primary-action:hover {
|
|
125
|
+
color: #350da6;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.in-house-launcher__primary-action {
|
|
129
|
+
transition: color 0.5s cubic-bezier(0.2, 0.19, 0.27, 0.98),
|
|
130
|
+
fill 0.5s cubic-bezier(0.2, 0.19, 0.27, 0.98);
|
|
131
|
+
cursor: pointer;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.in-house-launcher__primary-action:hover path {
|
|
135
|
+
fill: #350da6;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.in-house-launcher__primary-action:hover .in-house-launcher__ask-underline {
|
|
139
|
+
background: #350da6;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.in-house-launcher__ask-underline {
|
|
143
|
+
width: 47px;
|
|
144
|
+
height: 3px;
|
|
145
|
+
background: #1e1e1e;
|
|
146
|
+
transition: background 0.5s cubic-bezier(0.2, 0.19, 0.27, 0.98);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.in-house-launcher__filler-text {
|
|
150
|
+
font-size: 10px;
|
|
151
|
+
line-height: 22px;
|
|
152
|
+
color: #1e1e1e;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.in-house-launcher__call-to-action-option {
|
|
156
|
+
font-weight: 700;
|
|
157
|
+
font-size: 12px;
|
|
158
|
+
line-height: 22px;
|
|
159
|
+
|
|
160
|
+
color: #1e1e1e;
|
|
161
|
+
border-bottom: 2px solid #1e1e1e;
|
|
162
|
+
width: fit-content;
|
|
163
|
+
height: fit-content;
|
|
164
|
+
cursor: pointer;
|
|
165
|
+
transition: color 0.2s cubic-bezier(0.2, 0.19, 0.27, 0.98);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.in-house-launcher__call-to-action-option:hover {
|
|
169
|
+
color: #350da6;
|
|
170
|
+
border-bottom: 2px solid #350da6;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.in-house-launcher__call-to-actions-wrapper {
|
|
174
|
+
width: 100%;
|
|
175
|
+
display: flex;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.in-house-launcher__call-to-actions-wrapper
|
|
179
|
+
> .in-house-launcher__call-to-action-option:not(:last-child) {
|
|
180
|
+
margin-right: 15px;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.in-house-launcher__window-wrapper {
|
|
184
|
+
position: fixed;
|
|
185
|
+
right: 0;
|
|
186
|
+
bottom: 172px;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.in-house-launcher__mini-launcher-wrapper {
|
|
190
|
+
position: fixed;
|
|
191
|
+
right: 0px;
|
|
192
|
+
bottom: 40px;
|
|
193
|
+
z-index: 100000;
|
|
194
|
+
display: flex;
|
|
195
|
+
align-items: center;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.in-house-launcher__mini-option {
|
|
199
|
+
position: relative;
|
|
200
|
+
// This is so that the pseudo elements we created will be visible despite being beneath the element it is a pseudo element for
|
|
201
|
+
border: 4px solid transparent;
|
|
202
|
+
height: 48px;
|
|
203
|
+
width: 48px;
|
|
204
|
+
display: flex;
|
|
205
|
+
align-items: center;
|
|
206
|
+
justify-content: center;
|
|
207
|
+
border-radius: 50%;
|
|
208
|
+
background: rgba(240, 240, 240, 0.9);
|
|
209
|
+
background-clip: padding-box;
|
|
210
|
+
padding: 4px;
|
|
211
|
+
box-shadow: 0px 8px 8px 4px rgba(0, 0, 0, 0.25);
|
|
212
|
+
cursor: pointer;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.in-house-launcher__mini-option-wrapper {
|
|
216
|
+
margin-right: 12px;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.in-house-launcher__mini-option:not(:hover)::before {
|
|
220
|
+
position: absolute;
|
|
221
|
+
top: -4px;
|
|
222
|
+
bottom: -4px;
|
|
223
|
+
left: -4px;
|
|
224
|
+
right: -4px;
|
|
225
|
+
background: #ffffff;
|
|
226
|
+
content: "";
|
|
227
|
+
z-index: -1;
|
|
228
|
+
border-radius: 50%;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.in-house-launcher__secondary-option {
|
|
232
|
+
width: 30px;
|
|
233
|
+
height: 30px;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.in-house-launcher__mini-option-wrapper {
|
|
237
|
+
background-clip: padding-box;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.in-house-launcher__mini-option:hover {
|
|
241
|
+
border: none;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.in-house-launcher__mini-option:hover::before {
|
|
245
|
+
position: absolute;
|
|
246
|
+
top: -4px;
|
|
247
|
+
bottom: -4px;
|
|
248
|
+
left: -4px;
|
|
249
|
+
right: -4px;
|
|
250
|
+
background: linear-gradient(to right, #350da6, #8c58e5, #e66933);
|
|
251
|
+
background-size: cover;
|
|
252
|
+
content: "";
|
|
253
|
+
z-index: -1;
|
|
254
|
+
border-radius: 50%;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
@media screen and (max-width: 767px) {
|
|
258
|
+
.launcher:not(.miniLauncher).in-house-launcher__desktop {
|
|
259
|
+
width: 100%;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
.launcher.in-house-launcher__desktop {
|
|
263
|
+
bottom: 0px;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.launcher.in-house-launcher__desktop {
|
|
267
|
+
border-radius: 0px;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.launcher .glowBar {
|
|
271
|
+
top: 3px;
|
|
272
|
+
left: 0;
|
|
273
|
+
border-top-left-radius: 0px;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.launcher .content {
|
|
277
|
+
flex-direction: row;
|
|
278
|
+
padding-top: 15px;
|
|
279
|
+
padding-bottom: 15px;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
.in-house-launcher__primary-action-text {
|
|
283
|
+
font-size: 18px;
|
|
284
|
+
flex-grow: 1;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
.in-house-launcher__call-to-actions-wrapper {
|
|
288
|
+
width: fit-content;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
.in-house-launcher__ask-underline {
|
|
292
|
+
width: 35px;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
.in-house-launcher__window-wrapper {
|
|
296
|
+
left: 0;
|
|
297
|
+
bottom: 0;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
`;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/** Pass in the year and the 0-indexed number of the month. E.g., for January 2022,
|
|
2
|
+
* `getDaysInMonth(2022, 0)`.
|
|
3
|
+
*/
|
|
4
|
+
export const getDaysInMonth = (year: number, month: number): number => {
|
|
5
|
+
// In the Date constructor, day 0 is the last day of the
|
|
6
|
+
// previous month. We get the date of next month's day 0
|
|
7
|
+
// to find out how many days this month has.
|
|
8
|
+
return new Date(year, month + 1, 0).getDate();
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/** Pass in the year and the 0-indexed number of the month. E.g., for January 2022,
|
|
12
|
+
* `getDaysInMonth(2022, 0)`. Returns the 0-indexed day of the week of the first
|
|
13
|
+
* day of the month, e.g. `0` for Sunday.
|
|
14
|
+
*/
|
|
15
|
+
export const getMonthStartDay = (year: number, month: number): number =>
|
|
16
|
+
new Date(year, month, 1).getDay();
|
|
17
|
+
|
|
18
|
+
/** Pass in the year and the 0-indexed number of the month. E.g., for January 2022,
|
|
19
|
+
* `getDaysInMonth(2022, 0)`. Returns the 0-indexed day of the week of the last day
|
|
20
|
+
* of the month, e.g. `0` for Sunday.
|
|
21
|
+
*/
|
|
22
|
+
export const getMonthEndDay = (year: number, month: number): number => {
|
|
23
|
+
const monthStartDay = getMonthStartDay(year, month);
|
|
24
|
+
const daysInMonth = getDaysInMonth(year, month);
|
|
25
|
+
return (monthStartDay + (daysInMonth % 7) - 1) % 7;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/** Takes a 0-indexed month and returns the previous month.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* getPreviousMonth(5) -> 4 // June -> May
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* getPreviousMonth(0) -> 11 // January -> December
|
|
35
|
+
*
|
|
36
|
+
*/
|
|
37
|
+
export const getPreviousMonth = (month: number): number =>
|
|
38
|
+
month > 0 ? month - 1 : 11;
|
|
39
|
+
|
|
40
|
+
/** Pass in the year and the 0-indexed number of the month. E.g., for January 2022,
|
|
41
|
+
* `getDaysInMonth(2022, 0)`. Returns the 0-indexed day of the week, e.g. `0` for Sunday.
|
|
42
|
+
*/
|
|
43
|
+
export const getDaysInPreviousMonth = (year: number, month: number): number =>
|
|
44
|
+
getDaysInMonth(month > 0 ? year : year - 1, getPreviousMonth(month));
|
|
45
|
+
|
|
46
|
+
export const dayNames = [
|
|
47
|
+
"Sunday",
|
|
48
|
+
"Monday",
|
|
49
|
+
"Tuesday",
|
|
50
|
+
"Wednesday",
|
|
51
|
+
"Thursday",
|
|
52
|
+
"Friday",
|
|
53
|
+
"Saturday",
|
|
54
|
+
];
|
|
55
|
+
export const monthNames = [
|
|
56
|
+
"January",
|
|
57
|
+
"February",
|
|
58
|
+
"March",
|
|
59
|
+
"April",
|
|
60
|
+
"May",
|
|
61
|
+
"June",
|
|
62
|
+
"July",
|
|
63
|
+
"August",
|
|
64
|
+
"September",
|
|
65
|
+
"October",
|
|
66
|
+
"November",
|
|
67
|
+
"December",
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
export type Month =
|
|
71
|
+
| "January"
|
|
72
|
+
| "February"
|
|
73
|
+
| "March"
|
|
74
|
+
| "April"
|
|
75
|
+
| "May"
|
|
76
|
+
| "June"
|
|
77
|
+
| "July"
|
|
78
|
+
| "August"
|
|
79
|
+
| "September"
|
|
80
|
+
| "October"
|
|
81
|
+
| "November"
|
|
82
|
+
| "December";
|