@meetelise/chat 1.12.0 → 1.12.3
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 +2 -2
- package/public/demo/index.html +2 -2
- package/public/dist/index.js +541 -280
- package/src/WebComponent/InHouseLauncher.ts +56 -7
- package/src/WebComponent/MEChat.ts +4 -0
- package/src/WebComponent/Scheduler/date-picker.ts +90 -21
- package/src/WebComponent/Scheduler/me-select.ts +8 -13
- package/src/WebComponent/Scheduler/time-picker.ts +105 -58
- package/src/WebComponent/Scheduler/tour-scheduler.ts +728 -179
- package/src/WebComponent/Scheduler/tour-type-option.ts +0 -3
- package/src/WebComponent/actions/ActionConfirmButton.ts +0 -1
- package/src/WebComponent/actions/CallUsWindow.ts +23 -1
- package/src/WebComponent/actions/DetailsWindow.ts +0 -1
- package/src/WebComponent/actions/EmailUsWindow.ts +0 -2
- package/src/WebComponent/actions/InputStyles.ts +0 -1
- package/src/WebComponent/actions/TextUsWindow.ts +0 -1
- package/src/WebComponent/actions/formatPhoneNumber.ts +26 -10
- package/src/WebComponent/inHouseLauncherStyles.ts +1 -2
- package/src/fetchBuildingInfo.ts +7 -0
- package/src/getAvailabilities.ts +10 -0
- package/src/getRegisteredPhoneNumbers.ts +56 -0
|
@@ -3,16 +3,19 @@ import { html, LitElement, TemplateResult } from "lit";
|
|
|
3
3
|
import { customElement, property, state } from "lit/decorators.js";
|
|
4
4
|
import { createRef, ref, Ref } from "lit/directives/ref.js";
|
|
5
5
|
import { glowBarMp4, glowBarWebm } from "../assetUrls";
|
|
6
|
-
import {
|
|
6
|
+
import { inHouseLauncherStyles } from "./inHouseLauncherStyles";
|
|
7
7
|
import { EmailUsWindow, installEmailUsWindow } from "./actions/EmailUsWindow";
|
|
8
8
|
import { installTextUsWindow, TextUsWindow } from "./actions/TextUsWindow";
|
|
9
9
|
import { styleMap } from "lit/directives/style-map.js";
|
|
10
10
|
import { classMap } from "lit/directives/class-map.js";
|
|
11
11
|
import { installCallUsWindow } from "./actions/CallUsWindow";
|
|
12
|
+
import { getRegisteredPhoneNumbers } from "../getRegisteredPhoneNumbers";
|
|
13
|
+
import { TourScheduler } from "./Scheduler/tour-scheduler";
|
|
14
|
+
import { LabeledOption } from "../fetchBuildingInfo";
|
|
12
15
|
|
|
13
16
|
@customElement("in-house-launcher")
|
|
14
17
|
export class InHouseLauncher extends LitElement {
|
|
15
|
-
static styles =
|
|
18
|
+
static styles = inHouseLauncherStyles;
|
|
16
19
|
|
|
17
20
|
@property({ type: Boolean })
|
|
18
21
|
isMobile = false;
|
|
@@ -36,9 +39,13 @@ export class InHouseLauncher extends LitElement {
|
|
|
36
39
|
@property({ type: Boolean })
|
|
37
40
|
hasEmailEnabled = true;
|
|
38
41
|
@property({ type: Boolean })
|
|
39
|
-
hasSSTEnabled =
|
|
42
|
+
hasSSTEnabled = true;
|
|
40
43
|
@property({ type: Boolean })
|
|
41
|
-
hasTextUsEnabled =
|
|
44
|
+
hasTextUsEnabled = false;
|
|
45
|
+
@property({ attribute: false })
|
|
46
|
+
layoutOptions: LabeledOption[] = [];
|
|
47
|
+
@property({ attribute: false })
|
|
48
|
+
tourTypeOptions: LabeledOption[] = [];
|
|
42
49
|
@property({ attribute: false })
|
|
43
50
|
onChatTapped: () => void = () => {
|
|
44
51
|
return;
|
|
@@ -64,10 +71,19 @@ export class InHouseLauncher extends LitElement {
|
|
|
64
71
|
|
|
65
72
|
emailUsWindowRef: Ref<EmailUsWindow> = createRef();
|
|
66
73
|
textUsWindowRef: Ref<TextUsWindow> = createRef();
|
|
74
|
+
tourSchedulerRef: Ref<TourScheduler> = createRef();
|
|
67
75
|
|
|
68
|
-
updated = (): void => {
|
|
76
|
+
updated = async (): Promise<void> => {
|
|
69
77
|
this.attachOnClickToEmailUsWindow();
|
|
70
78
|
this.attachOnClickToTextUsWindow();
|
|
79
|
+
if (this.buildingId) {
|
|
80
|
+
const registeredPhoneNumbers = await getRegisteredPhoneNumbers(
|
|
81
|
+
this.buildingId
|
|
82
|
+
);
|
|
83
|
+
this.hasTextUsEnabled =
|
|
84
|
+
registeredPhoneNumbers.length > 0 && this.buildingId !== 4895;
|
|
85
|
+
}
|
|
86
|
+
this.attachOnClickToSSTWindow();
|
|
71
87
|
};
|
|
72
88
|
|
|
73
89
|
attachOnClickToEmailUsWindow = (): void => {
|
|
@@ -86,6 +102,14 @@ export class InHouseLauncher extends LitElement {
|
|
|
86
102
|
textUsWindowRef.onCloseClicked = this.onCloseTextUsWindow;
|
|
87
103
|
};
|
|
88
104
|
|
|
105
|
+
attachOnClickToSSTWindow = (): void => {
|
|
106
|
+
const sstWindowRef = this.tourSchedulerRef.value;
|
|
107
|
+
if (!sstWindowRef) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
sstWindowRef.onCloseClicked = this.onCloseSSTWindow;
|
|
111
|
+
};
|
|
112
|
+
|
|
89
113
|
onClickEmailOption = (e: MouseEvent): void => {
|
|
90
114
|
e.preventDefault();
|
|
91
115
|
e.stopPropagation();
|
|
@@ -138,7 +162,12 @@ export class InHouseLauncher extends LitElement {
|
|
|
138
162
|
renderSSTOption = (): TemplateResult => {
|
|
139
163
|
const text = this.getNumCallToActions() > 2 ? "Book" : "Book a tour";
|
|
140
164
|
return html`
|
|
141
|
-
<div
|
|
165
|
+
<div
|
|
166
|
+
class="in-house-launcher__call-to-action-option"
|
|
167
|
+
@click=${this.onClickSSTOption}
|
|
168
|
+
>
|
|
169
|
+
${text}
|
|
170
|
+
</div>
|
|
142
171
|
`;
|
|
143
172
|
};
|
|
144
173
|
|
|
@@ -229,6 +258,16 @@ export class InHouseLauncher extends LitElement {
|
|
|
229
258
|
></text-us-window>
|
|
230
259
|
</div>`
|
|
231
260
|
: ""}
|
|
261
|
+
${this.isSSTWindowOpen
|
|
262
|
+
? html`<div class="in-house-launcher__window-wrapper">
|
|
263
|
+
<tour-scheduler
|
|
264
|
+
.layoutOptions=${this.layoutOptions}
|
|
265
|
+
.tourTypeOptions=${this.tourTypeOptions}
|
|
266
|
+
buildingId=${this.buildingId}
|
|
267
|
+
${ref(this.tourSchedulerRef)}
|
|
268
|
+
></tour-scheduler>
|
|
269
|
+
</div>`
|
|
270
|
+
: ""}
|
|
232
271
|
${this.isCallUsWindowOpen
|
|
233
272
|
? html`
|
|
234
273
|
<div class="in-house-launcher__window-wrapper">
|
|
@@ -293,7 +332,7 @@ export class InHouseLauncher extends LitElement {
|
|
|
293
332
|
/>
|
|
294
333
|
</svg>
|
|
295
334
|
`,
|
|
296
|
-
|
|
335
|
+
this.onClickSSTOption,
|
|
297
336
|
true
|
|
298
337
|
)
|
|
299
338
|
: ""}
|
|
@@ -368,6 +407,16 @@ export class InHouseLauncher extends LitElement {
|
|
|
368
407
|
></text-us-window>
|
|
369
408
|
</div>`
|
|
370
409
|
: ""}
|
|
410
|
+
${this.isSSTWindowOpen
|
|
411
|
+
? html`<div class="in-house-launcher__window-wrapper">
|
|
412
|
+
<tour-scheduler
|
|
413
|
+
.layoutOptions=${this.layoutOptions}
|
|
414
|
+
.tourTypeOptions=${this.tourTypeOptions}
|
|
415
|
+
buildingId=${this.buildingId}
|
|
416
|
+
${ref(this.tourSchedulerRef)}
|
|
417
|
+
></tour-scheduler>
|
|
418
|
+
</div>`
|
|
419
|
+
: ""}
|
|
371
420
|
${this.isCallUsWindowOpen && this.phoneNumber
|
|
372
421
|
? html`
|
|
373
422
|
<div class="in-house-launcher__window-wrapper">
|
|
@@ -14,6 +14,7 @@ import { isMobile } from "../utils";
|
|
|
14
14
|
import { installInHouseLauncher } from "./InHouseLauncher";
|
|
15
15
|
|
|
16
16
|
import "./MEChat.css";
|
|
17
|
+
import { getRawAvailabilities } from "../getAvailabilities";
|
|
17
18
|
|
|
18
19
|
export interface Options {
|
|
19
20
|
building: string;
|
|
@@ -91,6 +92,7 @@ export class MEChat extends LitElement {
|
|
|
91
92
|
return;
|
|
92
93
|
}
|
|
93
94
|
this.building = await fetchBuildingInfo(this.orgSlug, this.buildingSlug);
|
|
95
|
+
getRawAvailabilities(this.building.id); // we're not using this here, just want to cache the result
|
|
94
96
|
this.chatId = getChatID(this.orgSlug, this.buildingSlug);
|
|
95
97
|
this.avatarSrc = this.avatarSrc || this.building.avatarSrc;
|
|
96
98
|
this.theme = getTheme(this.themeId ?? this.building.themeId);
|
|
@@ -243,6 +245,8 @@ export class MEChat extends LitElement {
|
|
|
243
245
|
.isFirstMount=${!this.hasMounted}
|
|
244
246
|
.isMini=${this.useMiniWidget}
|
|
245
247
|
.buildingId=${this.building?.id ?? 0}
|
|
248
|
+
.layoutOptions=${this.building?.layoutOptions ?? []}
|
|
249
|
+
.tourTypeOptions=${this.building?.tourTypeOptions ?? []}
|
|
246
250
|
phoneNumber="${this.building?.phoneNumber ?? ""}"
|
|
247
251
|
textColor="${this.theme.chatHeader.textColor}"
|
|
248
252
|
backgroundColor="${this.theme.chatPaneBackgroundColor}"
|
|
@@ -1,5 +1,10 @@
|
|
|
1
|
+
import { isSameMonth } from "date-fns";
|
|
2
|
+
import format from "date-fns/format";
|
|
3
|
+
import getDate from "date-fns/getDate";
|
|
4
|
+
import getMonth from "date-fns/getMonth";
|
|
5
|
+
import getYear from "date-fns/getYear";
|
|
1
6
|
import { LitElement, html, TemplateResult, css } from "lit";
|
|
2
|
-
import { property, state } from "lit/decorators.js";
|
|
7
|
+
import { customElement, property, state } from "lit/decorators.js";
|
|
3
8
|
import { classMap } from "lit/directives/class-map.js";
|
|
4
9
|
import {
|
|
5
10
|
dayNames,
|
|
@@ -11,6 +16,7 @@ import {
|
|
|
11
16
|
monthNames,
|
|
12
17
|
} from "../utils";
|
|
13
18
|
|
|
19
|
+
@customElement("date-picker")
|
|
14
20
|
export class DatePicker extends LitElement {
|
|
15
21
|
/**
|
|
16
22
|
* Optional attribute to set the date picker's default month.
|
|
@@ -26,16 +32,26 @@ export class DatePicker extends LitElement {
|
|
|
26
32
|
@property({ attribute: "year", type: Number })
|
|
27
33
|
defaultYear?: number;
|
|
28
34
|
|
|
29
|
-
|
|
30
|
-
|
|
35
|
+
@property({ attribute: false })
|
|
36
|
+
availabilities?: {
|
|
37
|
+
[day: string]: Date[];
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
private _selectedDate?: Date = undefined;
|
|
41
|
+
set selectedDate(date: Date | undefined) {
|
|
31
42
|
const old = this._selectedDate;
|
|
32
43
|
this._selectedDate = date;
|
|
33
44
|
this.requestUpdate("selectedDate", old);
|
|
34
|
-
this.dispatchEvent(
|
|
45
|
+
this.dispatchEvent(
|
|
46
|
+
new Event("change", {
|
|
47
|
+
composed: true,
|
|
48
|
+
bubbles: true,
|
|
49
|
+
})
|
|
50
|
+
);
|
|
35
51
|
}
|
|
36
52
|
|
|
37
|
-
@property({
|
|
38
|
-
get selectedDate(): undefined |
|
|
53
|
+
@property({ attribute: false })
|
|
54
|
+
get selectedDate(): undefined | Date {
|
|
39
55
|
return this._selectedDate;
|
|
40
56
|
}
|
|
41
57
|
|
|
@@ -47,6 +63,7 @@ export class DatePicker extends LitElement {
|
|
|
47
63
|
: this.now.getMonth();
|
|
48
64
|
|
|
49
65
|
// Make it easy to increment/decrement `this.monthShown` at year boundaries
|
|
66
|
+
// TODO: `willUpdate` is better: see https://lit.dev/docs/components/properties/ and https://lit.dev/docs/components/lifecycle/#willupdate
|
|
50
67
|
set monthShown(month: number) {
|
|
51
68
|
const oldMonthShown = this._monthShown;
|
|
52
69
|
if (month === 12) {
|
|
@@ -71,8 +88,6 @@ export class DatePicker extends LitElement {
|
|
|
71
88
|
yearShown = this.defaultYear ?? this.now.getFullYear();
|
|
72
89
|
|
|
73
90
|
static styles = css`
|
|
74
|
-
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700;900&display=swap");
|
|
75
|
-
|
|
76
91
|
:host {
|
|
77
92
|
box-sizing: border-box;
|
|
78
93
|
font-family: "Poppins";
|
|
@@ -81,6 +96,7 @@ export class DatePicker extends LitElement {
|
|
|
81
96
|
}
|
|
82
97
|
|
|
83
98
|
#calendar {
|
|
99
|
+
box-sizing: border-box;
|
|
84
100
|
display: flex;
|
|
85
101
|
flex-direction: column;
|
|
86
102
|
user-select: none;
|
|
@@ -89,19 +105,21 @@ export class DatePicker extends LitElement {
|
|
|
89
105
|
background: #e7e7e7;
|
|
90
106
|
border: 1px solid #ffffff;
|
|
91
107
|
border-radius: 10px;
|
|
92
|
-
padding: 15px 12px
|
|
108
|
+
padding: 15px 12px 15px;
|
|
93
109
|
}
|
|
94
110
|
|
|
95
111
|
#header {
|
|
96
112
|
display: flex;
|
|
97
113
|
align-items: center;
|
|
98
114
|
justify-content: space-between;
|
|
99
|
-
margin-bottom:
|
|
115
|
+
margin-bottom: 13px;
|
|
100
116
|
}
|
|
101
117
|
|
|
102
118
|
h1 {
|
|
103
119
|
font-weight: 600;
|
|
104
120
|
font-size: 12px;
|
|
121
|
+
margin: 0;
|
|
122
|
+
margin-left: 0.7em;
|
|
105
123
|
}
|
|
106
124
|
|
|
107
125
|
#arrows {
|
|
@@ -153,8 +171,21 @@ export class DatePicker extends LitElement {
|
|
|
153
171
|
transform: translate(-50%, -50%);
|
|
154
172
|
z-index: -1;
|
|
155
173
|
}
|
|
174
|
+
.dayNumber.today:not(.selected)::after {
|
|
175
|
+
content: "";
|
|
176
|
+
height: 23px;
|
|
177
|
+
aspect-ratio: 1;
|
|
178
|
+
border: 1px solid #202020;
|
|
179
|
+
border-radius: 50%;
|
|
180
|
+
position: absolute;
|
|
181
|
+
top: 50%;
|
|
182
|
+
left: 50%;
|
|
183
|
+
transform: translate(-50%, -50%);
|
|
184
|
+
z-index: 1;
|
|
185
|
+
}
|
|
156
186
|
.dayNumber:not(.selected):hover::after {
|
|
157
187
|
background-color: lightgray;
|
|
188
|
+
z-index: -1;
|
|
158
189
|
}
|
|
159
190
|
.dayNumber.selected::after {
|
|
160
191
|
background-color: #202020;
|
|
@@ -166,15 +197,12 @@ export class DatePicker extends LitElement {
|
|
|
166
197
|
.dayNumber.selected {
|
|
167
198
|
color: white;
|
|
168
199
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
color: #84838f;
|
|
200
|
+
.dayNumber:is(.differentMonth, .past, .noAvailabilities) {
|
|
201
|
+
opacity: 30%;
|
|
172
202
|
}
|
|
173
203
|
`;
|
|
174
204
|
|
|
175
205
|
render(): TemplateResult {
|
|
176
|
-
// TODO: check 0 vs. 1-indexing, make sure this works.
|
|
177
|
-
// TODO: handle year boundaries, e.g. if it's January and last month was last year. also same for next month when switching months.
|
|
178
206
|
const daysInMonth = getDaysInMonth(this.yearShown, this.monthShown);
|
|
179
207
|
const monthStartDay =
|
|
180
208
|
dayNames[getMonthStartDay(this.yearShown, this.monthShown)];
|
|
@@ -212,13 +240,40 @@ export class DatePicker extends LitElement {
|
|
|
212
240
|
];
|
|
213
241
|
|
|
214
242
|
const dayElements = dayNums.map((dayNumber, index) => {
|
|
243
|
+
const isPast =
|
|
244
|
+
this.yearShown < this.now.getFullYear() ||
|
|
245
|
+
(this.yearShown === this.now.getFullYear() &&
|
|
246
|
+
this.monthShown < this.now.getMonth()) ||
|
|
247
|
+
(this.yearShown === this.now.getFullYear() &&
|
|
248
|
+
this.monthShown === this.now.getMonth() &&
|
|
249
|
+
dayNumber < this.now.getDate());
|
|
215
250
|
const isDifferentMonth =
|
|
216
251
|
index < extraDaysAtBeginningOfMonth.length ||
|
|
217
252
|
index >= extraDaysAtBeginningOfMonth.length + daysOfMonth.length;
|
|
253
|
+
const isToday =
|
|
254
|
+
!isDifferentMonth &&
|
|
255
|
+
this.now.getMonth() === this.monthShown &&
|
|
256
|
+
this.now.getDate() === dayNumber;
|
|
257
|
+
const isSelected =
|
|
258
|
+
!!this.selectedDate &&
|
|
259
|
+
!isDifferentMonth &&
|
|
260
|
+
this.monthShown === getMonth(this.selectedDate) &&
|
|
261
|
+
this.yearShown === getYear(this.selectedDate) &&
|
|
262
|
+
dayNumber === getDate(this.selectedDate);
|
|
263
|
+
const dateString = format(
|
|
264
|
+
new Date(this.yearShown, this.monthShown, dayNumber),
|
|
265
|
+
"y-MM-dd"
|
|
266
|
+
);
|
|
267
|
+
const hasNoAvailabilities =
|
|
268
|
+
!this.availabilities?.[dateString] ||
|
|
269
|
+
this.availabilities?.[dateString]?.length === 0;
|
|
218
270
|
return html`<span
|
|
219
271
|
class="dayNumber ${classMap({
|
|
272
|
+
past: isPast,
|
|
220
273
|
differentMonth: isDifferentMonth,
|
|
221
|
-
|
|
274
|
+
today: isToday,
|
|
275
|
+
selected: isSelected,
|
|
276
|
+
noAvailabilities: hasNoAvailabilities,
|
|
222
277
|
})}"
|
|
223
278
|
>${dayNumber}</span
|
|
224
279
|
>`;
|
|
@@ -232,8 +287,13 @@ export class DatePicker extends LitElement {
|
|
|
232
287
|
<div
|
|
233
288
|
id="arrows"
|
|
234
289
|
@click="${(e: MouseEvent) => {
|
|
235
|
-
|
|
236
|
-
|
|
290
|
+
if (
|
|
291
|
+
(e.target as HTMLElement)?.closest("#back") &&
|
|
292
|
+
!isSameMonth(
|
|
293
|
+
this.now,
|
|
294
|
+
new Date(this.yearShown, this.monthShown, 1)
|
|
295
|
+
)
|
|
296
|
+
) {
|
|
237
297
|
this.monthShown--;
|
|
238
298
|
}
|
|
239
299
|
if ((e.target as HTMLElement)?.closest("#forward")) {
|
|
@@ -244,7 +304,13 @@ export class DatePicker extends LitElement {
|
|
|
244
304
|
if (![" ", "Enter"].includes(e.key)) {
|
|
245
305
|
return;
|
|
246
306
|
}
|
|
247
|
-
if (
|
|
307
|
+
if (
|
|
308
|
+
(e.target as HTMLElement)?.closest("#back") &&
|
|
309
|
+
!isSameMonth(
|
|
310
|
+
this.now,
|
|
311
|
+
new Date(this.yearShown, this.monthShown, 1)
|
|
312
|
+
)
|
|
313
|
+
) {
|
|
248
314
|
e.preventDefault();
|
|
249
315
|
e.stopPropagation();
|
|
250
316
|
this.monthShown--;
|
|
@@ -291,7 +357,11 @@ export class DatePicker extends LitElement {
|
|
|
291
357
|
@click="${(e: MouseEvent) => {
|
|
292
358
|
const target = e.target as HTMLElement | undefined;
|
|
293
359
|
if (target?.closest("span.dayNumber:not(.differentMonth)"))
|
|
294
|
-
this.selectedDate =
|
|
360
|
+
this.selectedDate = new Date(
|
|
361
|
+
this.yearShown,
|
|
362
|
+
this.monthShown,
|
|
363
|
+
parseInt(target.innerText)
|
|
364
|
+
);
|
|
295
365
|
}}"
|
|
296
366
|
>
|
|
297
367
|
<div class="row">
|
|
@@ -309,7 +379,6 @@ export class DatePicker extends LitElement {
|
|
|
309
379
|
`;
|
|
310
380
|
}
|
|
311
381
|
}
|
|
312
|
-
customElements.define("date-picker", DatePicker);
|
|
313
382
|
|
|
314
383
|
const chunk = <T>(chunkLength: number, array: T[]) => {
|
|
315
384
|
const chunks = [];
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { LitElement, html, TemplateResult, css } from "lit";
|
|
2
|
-
import { property, state, query } from "lit/decorators.js";
|
|
2
|
+
import { property, state, query, customElement } from "lit/decorators.js";
|
|
3
3
|
import { classMap } from "lit/directives/class-map.js";
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
@customElement("me-select")
|
|
7
6
|
export class MESelect extends LitElement {
|
|
8
7
|
@property({ attribute: false })
|
|
9
8
|
options: string[] = [];
|
|
@@ -12,10 +11,7 @@ export class MESelect extends LitElement {
|
|
|
12
11
|
placeholder?: string = "Select";
|
|
13
12
|
|
|
14
13
|
@property({ type: String })
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
@state()
|
|
18
|
-
private selected?: string;
|
|
14
|
+
value?: string;
|
|
19
15
|
|
|
20
16
|
@state()
|
|
21
17
|
private activeOption: string | null = null;
|
|
@@ -23,7 +19,7 @@ export class MESelect extends LitElement {
|
|
|
23
19
|
@state()
|
|
24
20
|
private isOpen?: boolean = false;
|
|
25
21
|
|
|
26
|
-
@query("#select")
|
|
22
|
+
@query("#select", true)
|
|
27
23
|
meSelect!: HTMLDivElement;
|
|
28
24
|
|
|
29
25
|
toggleSelect = (): void => {
|
|
@@ -34,11 +30,12 @@ export class MESelect extends LitElement {
|
|
|
34
30
|
};
|
|
35
31
|
|
|
36
32
|
setSelectedOption = (option: string, closeSelect = true): void => {
|
|
37
|
-
this.
|
|
33
|
+
this.value = option;
|
|
38
34
|
if (closeSelect) {
|
|
39
35
|
this.isOpen = !this.isOpen;
|
|
40
36
|
this.activeOption = null;
|
|
41
37
|
}
|
|
38
|
+
this.dispatchEvent(new Event("change", { bubbles: true, composed: true }));
|
|
42
39
|
};
|
|
43
40
|
|
|
44
41
|
handleKeydown = (
|
|
@@ -69,8 +66,6 @@ export class MESelect extends LitElement {
|
|
|
69
66
|
}
|
|
70
67
|
|
|
71
68
|
static styles = css`
|
|
72
|
-
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700;900&display=swap");
|
|
73
|
-
|
|
74
69
|
:host {
|
|
75
70
|
--light-grey: #e3e3e3;
|
|
76
71
|
--active-option-color: #f5f7f9;
|
|
@@ -109,6 +104,7 @@ export class MESelect extends LitElement {
|
|
|
109
104
|
position: absolute;
|
|
110
105
|
min-height: 40px;
|
|
111
106
|
max-width: 400px;
|
|
107
|
+
width: max-content;
|
|
112
108
|
background-color: white;
|
|
113
109
|
z-index: 20;
|
|
114
110
|
margin-top: 4px;
|
|
@@ -199,7 +195,7 @@ export class MESelect extends LitElement {
|
|
|
199
195
|
}
|
|
200
196
|
}}"
|
|
201
197
|
>
|
|
202
|
-
<span id="selectText">${this.
|
|
198
|
+
<span id="selectText">${this.value ?? this.placeholder}</span>
|
|
203
199
|
<svg
|
|
204
200
|
width="10"
|
|
205
201
|
height="6"
|
|
@@ -241,4 +237,3 @@ export class MESelect extends LitElement {
|
|
|
241
237
|
`;
|
|
242
238
|
}
|
|
243
239
|
}
|
|
244
|
-
customElements.define("me-select", MESelect);
|
|
@@ -5,97 +5,144 @@ import { classMap } from "lit/directives/class-map.js";
|
|
|
5
5
|
@customElement("time-picker")
|
|
6
6
|
export class TimePicker extends LitElement {
|
|
7
7
|
@property({ attribute: false })
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
"3:30pm",
|
|
16
|
-
"4:00pm",
|
|
17
|
-
"4:30pm",
|
|
18
|
-
"00:00pm",
|
|
19
|
-
];
|
|
8
|
+
options: string[] = [];
|
|
9
|
+
|
|
10
|
+
@property({ type: Boolean })
|
|
11
|
+
selectedDateExists = false;
|
|
12
|
+
|
|
13
|
+
@property({ type: Boolean })
|
|
14
|
+
horizontal = false;
|
|
20
15
|
|
|
21
16
|
@state()
|
|
22
17
|
private selected?: string;
|
|
23
18
|
|
|
19
|
+
@property({ attribute: false })
|
|
20
|
+
get selectedTime(): undefined | string {
|
|
21
|
+
return this.selected;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
24
|
static styles = css`
|
|
25
25
|
* {
|
|
26
26
|
box-sizing: border-box;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
:host {
|
|
30
|
+
color: #202020;
|
|
31
|
+
font-family: "Poppins";
|
|
32
|
+
}
|
|
33
|
+
|
|
29
34
|
#optionContainer {
|
|
35
|
+
display: grid;
|
|
36
|
+
grid-template-columns: 85px 85px;
|
|
37
|
+
grid-auto-rows: 40px;
|
|
38
|
+
align-items: start;
|
|
30
39
|
margin-left: 14px;
|
|
31
|
-
display: flex;
|
|
32
|
-
flex-wrap: wrap;
|
|
33
40
|
column-gap: 14px;
|
|
34
41
|
row-gap: 19px;
|
|
35
42
|
min-width: 200px;
|
|
43
|
+
max-height: 310px;
|
|
44
|
+
overflow-y: auto;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
#optionContainer.horizontal {
|
|
48
|
+
grid-template-columns: unset;
|
|
49
|
+
grid-auto-rows: unset;
|
|
50
|
+
max-height: unset;
|
|
51
|
+
overflow-y: unset;
|
|
52
|
+
grid-template-rows: 40px 40px;
|
|
53
|
+
grid-auto-columns: 85px;
|
|
54
|
+
grid-auto-flow: column;
|
|
55
|
+
row-gap: 13px;
|
|
56
|
+
column-gap: 14px;
|
|
57
|
+
max-width: 90%;
|
|
58
|
+
overflow-x: auto;
|
|
36
59
|
}
|
|
37
60
|
|
|
38
61
|
.option {
|
|
39
62
|
position: relative;
|
|
40
|
-
display: inline-
|
|
41
|
-
|
|
63
|
+
display: inline-flex;
|
|
64
|
+
justify-content: center;
|
|
65
|
+
align-items: center;
|
|
42
66
|
height: 40px;
|
|
43
|
-
width:
|
|
44
|
-
|
|
67
|
+
width: max-content;
|
|
68
|
+
padding: 11px 11px 11px 14px;
|
|
45
69
|
background: #e7e7e7;
|
|
46
70
|
border: 1px solid #ffffff;
|
|
47
71
|
border-radius: 10px;
|
|
48
|
-
color: #202020;
|
|
49
|
-
font-family: "Poppins";
|
|
50
72
|
font-style: normal;
|
|
51
|
-
font-weight: 400;
|
|
52
73
|
font-size: 14px;
|
|
53
|
-
text-align: center;
|
|
54
74
|
user-select: none;
|
|
55
75
|
}
|
|
56
76
|
|
|
57
|
-
.option > span {
|
|
58
|
-
position: absolute;
|
|
59
|
-
top: 50%;
|
|
60
|
-
left: 50%;
|
|
61
|
-
transform: translate(-50%, -50%);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
77
|
.option.selected {
|
|
65
78
|
color: white;
|
|
66
79
|
background: #202020;
|
|
67
|
-
|
|
80
|
+
box-shadow: inset 0px 0px 0px 3px rgb(131 129 142);
|
|
68
81
|
border-radius: 10px;
|
|
69
82
|
}
|
|
83
|
+
|
|
84
|
+
#noAvailabilityText {
|
|
85
|
+
font-size: 12px;
|
|
86
|
+
margin-left: 20px;
|
|
87
|
+
line-height: 18px;
|
|
88
|
+
max-width: 170px;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
#noAvailabilityText.horizontal {
|
|
92
|
+
margin-left: 0;
|
|
93
|
+
}
|
|
70
94
|
`;
|
|
71
95
|
render(): TemplateResult {
|
|
72
|
-
return
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
96
|
+
return this.options?.length
|
|
97
|
+
? html`
|
|
98
|
+
<div
|
|
99
|
+
id="optionContainer"
|
|
100
|
+
class=${this.horizontal ? "horizontal" : ""}
|
|
101
|
+
@click="${(e: MouseEvent) => {
|
|
102
|
+
const target = e.target as HTMLElement | undefined;
|
|
103
|
+
if (target?.closest(".option:not(.selected)"))
|
|
104
|
+
this.selected = target.innerText;
|
|
105
|
+
this.dispatchEvent(
|
|
106
|
+
new Event("change", { bubbles: true, composed: true })
|
|
107
|
+
);
|
|
108
|
+
}}"
|
|
109
|
+
@keydown="${(e: KeyboardEvent) => {
|
|
110
|
+
const target = e.target as HTMLElement | undefined;
|
|
111
|
+
if (
|
|
112
|
+
[" ", "Enter"].includes(e.key) &&
|
|
113
|
+
target?.closest(".option")
|
|
114
|
+
) {
|
|
115
|
+
e.preventDefault();
|
|
116
|
+
this.selected =
|
|
117
|
+
target.innerText !== this.selected
|
|
118
|
+
? target.innerText
|
|
119
|
+
: undefined;
|
|
120
|
+
this.dispatchEvent(
|
|
121
|
+
new Event("change", { bubbles: true, composed: true })
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
}}"
|
|
125
|
+
>
|
|
126
|
+
${this.options.map(
|
|
127
|
+
(option) =>
|
|
128
|
+
html`<div
|
|
129
|
+
class="option ${classMap({
|
|
130
|
+
selected: this.selected === option,
|
|
131
|
+
})}"
|
|
132
|
+
tabindex="0"
|
|
133
|
+
>
|
|
134
|
+
<span>${option}</span>
|
|
135
|
+
</div>`
|
|
136
|
+
)}
|
|
137
|
+
</div>
|
|
138
|
+
`
|
|
139
|
+
: html` <div
|
|
140
|
+
id="noAvailabilityText"
|
|
141
|
+
class=${this.horizontal ? "horizontal" : ""}
|
|
142
|
+
>
|
|
143
|
+
${this.selectedDateExists
|
|
144
|
+
? html`<p>No available appointments for this day</p>`
|
|
145
|
+
: html`<p>Select a date</p>`}
|
|
146
|
+
</div>`;
|
|
100
147
|
}
|
|
101
148
|
}
|