pushfeedback 0.1.37 → 0.1.39

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,814 +1,838 @@
1
- import { h } from '@stencil/core';
2
- import html2canvas from 'html2canvas';
3
- export class FeedbackModal {
4
- constructor() {
5
- this.onScrollDebounced = () => {
6
- clearTimeout(this.scrollTimeout);
7
- this.scrollTimeout = setTimeout(() => {
8
- document.documentElement.classList.remove('feedback-modal-screenshot-closing');
9
- document.documentElement.style.top = "";
10
- window.removeEventListener('scroll', this.onScrollDebounced);
11
- }, 200);
12
- };
13
- this.handleSubmit = async (event) => {
14
- event.preventDefault();
15
- this.resetOverflow();
16
- this.showScreenshotMode = false;
17
- this.showScreenshotTopBar = false;
18
- this.showModal = false;
19
- this.sending = true;
20
- try {
21
- const res = await fetch('https://app.pushfeedback.com/api/feedback/', {
22
- method: 'POST',
23
- body: JSON.stringify({
24
- url: window.location.href,
25
- message: this.formMessage,
26
- email: this.formEmail,
27
- project: this.project,
28
- screenshot: this.encodedScreenshot,
29
- rating: this.selectedRating,
30
- ratingMode: this.ratingMode,
31
- }),
32
- headers: {
33
- 'Content-Type': 'application/json'
34
- }
35
- });
36
- if (res.status === 201) {
37
- this.formSuccess = true;
38
- this.formError = false;
39
- }
40
- else {
41
- this.formSuccess = false;
42
- this.formError = true;
43
- this.formErrorStatus = res.status;
44
- }
45
- }
46
- catch (error) {
47
- console.log(error);
48
- this.formSuccess = false;
49
- this.formError = true;
50
- this.formErrorStatus = 500;
51
- }
52
- finally {
53
- this.sending = false;
54
- this.showModal = true;
55
- }
56
- };
57
- this.close = () => {
58
- this.sending = false;
59
- this.showModal = false;
60
- this.showScreenshotMode = false;
61
- this.showScreenshotTopBar = false;
62
- this.hasSelectedElement = false;
63
- this.encodedScreenshot = null;
64
- this.formSuccess = false;
65
- this.formError = false;
66
- this.formErrorStatus = 500;
67
- this.formMessage = '';
68
- this.formEmail = '';
69
- this.resetOverflow();
70
- };
71
- this.openScreenShot = () => {
72
- this.hasSelectedElement = false;
73
- this.showModal = false;
74
- this.showScreenshotMode = true;
75
- this.showScreenshotTopBar = true;
76
- this.encodedScreenshot = null;
77
- if (window.innerWidth > document.documentElement.clientWidth) {
78
- document.documentElement.classList.add('feedback-modal-screenshot-open--scroll');
79
- }
80
- const scrollY = window.scrollY;
81
- document.documentElement.style.top = `-${scrollY}px`;
82
- window.scrollTo(0, parseInt(document.documentElement.style.top || '0') * -1);
83
- document.documentElement.classList.add('feedback-modal-screenshot-open');
84
- };
85
- this.closeScreenShot = () => {
86
- this.showModal = false;
87
- this.showScreenshotMode = false;
88
- this.showScreenshotTopBar = false;
89
- this.hasSelectedElement = false;
90
- this.encodedScreenshot = null;
91
- this.resetOverflow();
92
- };
93
- this.handleMouseOverScreenShot = (event) => {
94
- event.preventDefault();
95
- if (this.hasSelectedElement)
96
- return;
97
- const borderOffset = 2;
98
- this.screenshotModal.style.display = 'none';
99
- const elementUnder = document.elementFromPoint(event.clientX, event.clientY);
100
- const rect = elementUnder.getBoundingClientRect();
101
- this.screenshotModal.style.display = '';
102
- // Get the bounding box of the element selected
103
- this.elementSelected.style.position = "absolute";
104
- this.elementSelected.style.left = `${rect.left}px`;
105
- this.elementSelected.style.top = `${rect.top}px`;
106
- this.elementSelected.style.width = `${rect.width}px`;
107
- this.elementSelected.style.height = `${rect.height}px`;
108
- this.elementSelected.classList.add('feedback-modal-element-hover');
109
- // Set the background color of nonselected areas
110
- // Top
111
- this.topSide.style.position = "absolute";
112
- this.topSide.style.left = `${rect.left}px`;
113
- this.topSide.style.top = '0px';
114
- this.topSide.style.width = `${rect.width + borderOffset}px`;
115
- this.topSide.style.height = `${rect.top}px`;
116
- this.topSide.style.backgroundColor = "rgba(0, 0, 0, 0.4)";
117
- // Left
118
- this.leftSide.style.position = "absolute";
119
- this.leftSide.style.left = '0px';
120
- this.leftSide.style.top = '0px';
121
- this.leftSide.style.width = `${rect.left}px`;
122
- this.leftSide.style.height = '100vh';
123
- this.leftSide.style.backgroundColor = "rgba(0, 0, 0, 0.4)";
124
- // Bottom
125
- this.bottomSide.style.position = "absolute";
126
- this.bottomSide.style.left = `${rect.left}px`;
127
- this.bottomSide.style.top = `${rect.bottom + borderOffset}px`;
128
- this.bottomSide.style.width = `${rect.width + borderOffset}px`;
129
- this.bottomSide.style.height = '100vh';
130
- this.bottomSide.style.backgroundColor = "rgba(0, 0, 0, 0.4)";
131
- // Right
132
- this.rightSide.style.position = "absolute";
133
- this.rightSide.style.left = `${rect.right + borderOffset}px`;
134
- ;
135
- this.rightSide.style.top = '0px';
136
- this.rightSide.style.width = '100%';
137
- this.rightSide.style.height = '100vh';
138
- this.rightSide.style.backgroundColor = "rgba(0, 0, 0, 0.4)";
139
- // Restore the visibility of the screenshot-modal
140
- this.screenshotModal.style.backgroundColor = 'transparent';
141
- };
142
- this.handleMouseClickedSelectedElement = async (event) => {
143
- event.preventDefault();
144
- if (!this.elementSelected) {
145
- return;
146
- }
147
- this.hasSelectedElement = true;
148
- this.elementSelected.classList.add('feedback-modal-element-selected');
149
- // Get the top position including the scroll offset
150
- const rectTop = this.elementSelected.getBoundingClientRect().top;
151
- const topWithScroll = rectTop + window.scrollY;
152
- // Move the element with the scroll offset
153
- this.elementSelected.style.top = `${topWithScroll}px`;
154
- // Clone the selected element and append it to the body
155
- const clonedElementSelected = this.elementSelected.cloneNode(true);
156
- document.body.appendChild(clonedElementSelected);
157
- // Reset the top position of the original element
158
- this.elementSelected.style.top = `${rectTop}px`;
159
- this.showScreenshotTopBar = false;
160
- this.showModal = false;
161
- try {
162
- const dataUrl = await this.captureScreenshot();
163
- console.log('Screenshot captured');
164
- this.encodedScreenshot = dataUrl;
165
- }
166
- catch (error) {
167
- console.error('Failed to capture screenshot:', error);
168
- this.hasSelectedElement = false;
169
- }
170
- finally {
171
- // Remove the cloned element and show the modal again
172
- document.body.removeChild(clonedElementSelected);
173
- this.showModal = true;
174
- }
175
- };
176
- this.sending = false;
177
- this.formMessage = '';
178
- this.formEmail = '';
179
- this.formSuccess = false;
180
- this.formError = false;
181
- this.formErrorStatus = 500;
182
- this.encodedScreenshot = undefined;
183
- this.isPrivacyChecked = false;
184
- this.whitelabel = false;
185
- this.selectedRating = 0;
186
- this.errorMessage = "Please try again later.";
187
- this.errorMessage403 = "The request URL does not match the one defined in PushFeedback for this project.";
188
- this.errorMessage404 = "We could not find the provided project ID in PushFeedback.";
189
- this.modalTitle = 'Share your feedback';
190
- this.modalTitleSuccess = 'Thanks for your feedback!';
191
- this.modalTitleError = "Oops!";
192
- this.modalPosition = 'center';
193
- this.sendButtonText = 'Send';
194
- this.successMessage = "";
195
- this.project = '';
196
- this.screenshotButtonText = 'Add a screenshot';
197
- this.screenshotTopbarText = 'Select an element on this page';
198
- this.hideEmail = false;
199
- this.emailAddress = '';
200
- this.emailPlaceholder = 'Email address (optional)';
201
- this.messagePlaceholder = 'Comments';
202
- this.hideRating = false;
203
- this.ratingMode = 'thumbs';
204
- this.ratingPlaceholder = 'Was this page helpful?';
205
- this.ratingStarsPlaceholder = 'How would you rate this page?';
206
- this.showModal = false;
207
- this.showScreenshotMode = false;
208
- this.showScreenshotTopBar = false;
209
- this.hasSelectedElement = false;
210
- this.hideScreenshotButton = false;
211
- this.hidePrivacyPolicy = true;
212
- this.privacyPolicyText = "I have read and expressly consent to the terms of the <a href='https://pushfeedback.com/privacy'>Privacy Policy</a>.";
213
- this.fetchData = true;
214
- }
215
- componentWillLoad() {
216
- if (this.fetchData)
217
- this.fetchProjectData();
218
- this.formEmail = this.emailAddress;
219
- }
220
- async fetchProjectData() {
221
- try {
222
- const response = await fetch('https://app.pushfeedback.com/api/projects/' + this.project + '/');
223
- const data = await response.json();
224
- this.whitelabel = data.whitelabel;
225
- }
226
- catch (error) {
227
- console.log(error);
228
- }
229
- }
230
- resetOverflow() {
231
- document.documentElement.classList.remove('feedback-modal-screenshot-open');
232
- document.documentElement.classList.remove('feedback-modal-screenshot-open--scroll');
233
- document.documentElement.classList.add('feedback-modal-screenshot-closing');
234
- window.scrollTo(0, parseInt(document.documentElement.style.top || '0') * -1);
235
- window.addEventListener('scroll', this.onScrollDebounced);
236
- }
237
- handleMessageInput(event) {
238
- this.formMessage = event.target.value;
239
- }
240
- handleEmailInput(event) {
241
- this.formEmail = event.target.value;
242
- }
243
- captureScreenshot() {
244
- return new Promise((resolve, reject) => {
245
- requestAnimationFrame(() => {
246
- html2canvas(document.body, {
247
- x: window.scrollX,
248
- y: window.scrollY,
249
- width: window.innerWidth,
250
- height: window.innerHeight,
251
- }).then(canvas => {
252
- const dataUrl = canvas.toDataURL();
253
- resolve(dataUrl);
254
- })
255
- .catch(error => {
256
- console.error(error);
257
- reject(error);
258
- });
259
- });
260
- });
261
- }
262
- handleCheckboxChange(event) {
263
- this.isPrivacyChecked = event.target.checked;
264
- }
265
- handleRatingChange(newRating) {
266
- this.selectedRating = newRating;
267
- }
268
- render() {
269
- return (h("div", { class: 'feedback-modal-wrapper' }, this.showScreenshotMode && (h("div", { class: "feedback-modal-screenshot", ref: el => (this.screenshotModal = el), onMouseMove: this.handleMouseOverScreenShot }, h("div", { class: "feedback-modal-screenshot-element-selected", ref: el => (this.elementSelected = el), onClick: this.handleMouseClickedSelectedElement }), h("div", { class: "top-side", ref: el => (this.topSide = el) }), h("div", { class: "left-side", ref: el => (this.leftSide = el) }), h("div", { class: "bottom-side", ref: el => (this.bottomSide = el) }), h("div", { class: "right-side", ref: el => (this.rightSide = el) }), this.showScreenshotTopBar && (h("div", { class: "feedback-modal-screenshot-header", onClick: this.closeScreenShot }, h("span", null, this.screenshotTopbarText), h("span", { class: "feedback-modal-screenshot-close" }, h("svg", { xmlns: "http://www.w3.org/2000/svg", width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", stroke: "#191919", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", class: "feather feather-x" }, h("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), h("line", { x1: "6", y1: "6", x2: "18", y2: "18" }))))))), this.showModal && (h("div", { class: "feedback-overlay" })), this.showModal && (h("div", { class: `feedback-modal-content feedback-modal-content--${this.modalPosition} ${this.showModal ? 'feedback-modal-content--open' : ''}`, ref: el => (this.modalContent = el) }, h("div", { class: "feedback-modal-header" }, !this.formSuccess && !this.formError ? (h("span", null, this.modalTitle)) : this.formSuccess ? (h("span", null, this.modalTitleSuccess)) : h("span", null, this.modalTitleError), h("button", { class: "feedback-modal-close", onClick: this.close }, h("svg", { xmlns: "http://www.w3.org/2000/svg", width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", stroke: "#191919", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", class: "feather feather-x" }, h("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), h("line", { x1: "6", y1: "6", x2: "18", y2: "18" })))), h("div", { class: "feedback-modal-body" }, !this.formSuccess && !this.formError ? (h("form", { onSubmit: this.handleSubmit }, !this.hideRating && (h("div", { class: "feedback-modal-rating" }, this.ratingMode === 'thumbs' ? (h("div", { class: "feedback-modal-rating-content" }, h("span", { class: "feedback-modal-input-heading" }, this.ratingPlaceholder), h("div", { class: "feedback-modal-rating-buttons feedback-modal-rating-buttons--thumbs" }, h("button", { title: "Yes", class: `feedback-modal-rating-button ${this.selectedRating === 1 ? 'feedback-modal-rating-button--selected' : ''}`, onClick: (event) => {
270
- event.preventDefault();
271
- this.handleRatingChange(1);
272
- } }, h("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "#5F6368", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, h("path", { d: "M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3" }))), h("button", { title: "No", class: `feedback-modal-rating-button ${this.selectedRating === 5 ? 'feedback-modal-rating-button--selected' : ''}`, onClick: (event) => {
273
- event.preventDefault();
274
- this.handleRatingChange(5);
275
- } }, h("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "#5F6368", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, h("path", { d: "M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17" })))))) : (h("div", { class: "feedback-modal-rating-content" }, h("span", { class: "feedback-modal-input-heading" }, this.ratingStarsPlaceholder), h("div", { class: "feedback-modal-rating-buttons feedback-modal-rating-buttons--stars" }, [1, 2, 3, 4, 5].map((rating) => (h("button", { key: rating, class: `feedback-modal-rating-button ${this.selectedRating >= rating ? 'feedback-modal-rating-button--selected' : ''}`, onClick: (event) => {
276
- event.preventDefault();
277
- this.handleRatingChange(rating);
278
- } }, h("svg", { xmlns: "http://www.w3.org/2000/svg", width: "28", height: "28", viewBox: "0 0 24 24", fill: "none", stroke: "#5F6368", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, h("polygon", { points: "12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" })))))))))), h("div", { class: "feedback-modal-text" }, h("textarea", { placeholder: this.messagePlaceholder, value: this.formMessage, onInput: (event) => this.handleMessageInput(event) })), !this.hideEmail && (h("div", { class: "feedback-modal-email" }, h("input", { placeholder: this.emailPlaceholder, type: "email", onInput: (event) => this.handleEmailInput(event), value: this.formEmail }))), !this.hidePrivacyPolicy && (h("div", { class: "feedback-modal-privacy" }, h("input", { type: "checkbox", id: "privacyPolicy", onChange: (ev) => this.handleCheckboxChange(ev), required: true }), h("span", { innerHTML: this.privacyPolicyText }))), h("div", { class: `feedback-modal-buttons ${this.hideScreenshotButton ? 'single' : ''}` }, !this.hideScreenshotButton && (h("button", { type: "button", class: `feedback-modal-button feedback-modal-button--screenshot ${this.encodedScreenshot ? "feedback-modal-button--active" : ""}`, onClick: this.openScreenShot, disabled: this.sending }, h("svg", { xmlns: "http://www.w3.org/2000/svg", height: "24", viewBox: "0 -960 960 960", width: "24" }, h("path", { d: "M680-80v-120H560v-80h120v-120h80v120h120v80H760v120h-80ZM200-200v-200h80v120h120v80H200Zm0-360v-200h200v80H280v120h-80Zm480 0v-120H560v-80h200v200h-80Z" })), this.screenshotButtonText)), h("button", { class: "feedback-modal-button feedback-modal-button--submit", type: "submit", disabled: this.sending }, this.sendButtonText)))) : this.formSuccess && !this.formError ? (h("p", { class: "feedback-modal-message" }, this.successMessage)) : this.formError && this.formErrorStatus == 404 ? (h("p", { class: "feedback-modal-message" }, this.errorMessage404)) : this.formError && this.formErrorStatus == 403 ? (h("p", { class: "feedback-modal-message" }, this.errorMessage403)) : this.formError ? (h("p", { class: "feedback-modal-message" }, this.errorMessage)) : h("span", null)), h("div", { class: "feedback-modal-footer", style: { display: this.whitelabel ? 'none' : 'block' } }, h("div", { class: "feedback-logo" }, "Powered by ", h("a", { target: "_blank", href: "https://pushfeedback.com" }, "PushFeedback.com")))))));
279
- }
280
- static get is() { return "feedback-modal"; }
281
- static get encapsulation() { return "shadow"; }
282
- static get originalStyleUrls() {
283
- return {
284
- "$": ["feedback-modal.css"]
285
- };
286
- }
287
- static get styleUrls() {
288
- return {
289
- "$": ["feedback-modal.css"]
290
- };
291
- }
292
- static get properties() {
293
- return {
294
- "errorMessage": {
295
- "type": "string",
296
- "mutable": false,
297
- "complexType": {
298
- "original": "string",
299
- "resolved": "string",
300
- "references": {}
301
- },
302
- "required": false,
303
- "optional": false,
304
- "docs": {
305
- "tags": [],
306
- "text": ""
307
- },
308
- "attribute": "error-message",
309
- "reflect": false,
310
- "defaultValue": "\"Please try again later.\""
311
- },
312
- "errorMessage403": {
313
- "type": "string",
314
- "mutable": false,
315
- "complexType": {
316
- "original": "string",
317
- "resolved": "string",
318
- "references": {}
319
- },
320
- "required": false,
321
- "optional": false,
322
- "docs": {
323
- "tags": [],
324
- "text": ""
325
- },
326
- "attribute": "error-message-4-0-3",
327
- "reflect": false,
328
- "defaultValue": "\"The request URL does not match the one defined in PushFeedback for this project.\""
329
- },
330
- "errorMessage404": {
331
- "type": "string",
332
- "mutable": false,
333
- "complexType": {
334
- "original": "string",
335
- "resolved": "string",
336
- "references": {}
337
- },
338
- "required": false,
339
- "optional": false,
340
- "docs": {
341
- "tags": [],
342
- "text": ""
343
- },
344
- "attribute": "error-message-4-0-4",
345
- "reflect": false,
346
- "defaultValue": "\"We could not find the provided project ID in PushFeedback.\""
347
- },
348
- "modalTitle": {
349
- "type": "string",
350
- "mutable": false,
351
- "complexType": {
352
- "original": "string",
353
- "resolved": "string",
354
- "references": {}
355
- },
356
- "required": false,
357
- "optional": false,
358
- "docs": {
359
- "tags": [],
360
- "text": ""
361
- },
362
- "attribute": "modal-title",
363
- "reflect": false,
364
- "defaultValue": "'Share your feedback'"
365
- },
366
- "modalTitleSuccess": {
367
- "type": "string",
368
- "mutable": false,
369
- "complexType": {
370
- "original": "string",
371
- "resolved": "string",
372
- "references": {}
373
- },
374
- "required": false,
375
- "optional": false,
376
- "docs": {
377
- "tags": [],
378
- "text": ""
379
- },
380
- "attribute": "modal-title-success",
381
- "reflect": false,
382
- "defaultValue": "'Thanks for your feedback!'"
383
- },
384
- "modalTitleError": {
385
- "type": "string",
386
- "mutable": false,
387
- "complexType": {
388
- "original": "string",
389
- "resolved": "string",
390
- "references": {}
391
- },
392
- "required": false,
393
- "optional": false,
394
- "docs": {
395
- "tags": [],
396
- "text": ""
397
- },
398
- "attribute": "modal-title-error",
399
- "reflect": false,
400
- "defaultValue": "\"Oops!\""
401
- },
402
- "modalPosition": {
403
- "type": "string",
404
- "mutable": false,
405
- "complexType": {
406
- "original": "string",
407
- "resolved": "string",
408
- "references": {}
409
- },
410
- "required": false,
411
- "optional": false,
412
- "docs": {
413
- "tags": [],
414
- "text": ""
415
- },
416
- "attribute": "modal-position",
417
- "reflect": false,
418
- "defaultValue": "'center'"
419
- },
420
- "sendButtonText": {
421
- "type": "string",
422
- "mutable": false,
423
- "complexType": {
424
- "original": "string",
425
- "resolved": "string",
426
- "references": {}
427
- },
428
- "required": false,
429
- "optional": false,
430
- "docs": {
431
- "tags": [],
432
- "text": ""
433
- },
434
- "attribute": "send-button-text",
435
- "reflect": false,
436
- "defaultValue": "'Send'"
437
- },
438
- "successMessage": {
439
- "type": "string",
440
- "mutable": false,
441
- "complexType": {
442
- "original": "string",
443
- "resolved": "string",
444
- "references": {}
445
- },
446
- "required": false,
447
- "optional": false,
448
- "docs": {
449
- "tags": [],
450
- "text": ""
451
- },
452
- "attribute": "success-message",
453
- "reflect": false,
454
- "defaultValue": "\"\""
455
- },
456
- "project": {
457
- "type": "string",
458
- "mutable": false,
459
- "complexType": {
460
- "original": "string",
461
- "resolved": "string",
462
- "references": {}
463
- },
464
- "required": false,
465
- "optional": false,
466
- "docs": {
467
- "tags": [],
468
- "text": ""
469
- },
470
- "attribute": "project",
471
- "reflect": false,
472
- "defaultValue": "''"
473
- },
474
- "screenshotButtonText": {
475
- "type": "string",
476
- "mutable": false,
477
- "complexType": {
478
- "original": "string",
479
- "resolved": "string",
480
- "references": {}
481
- },
482
- "required": false,
483
- "optional": false,
484
- "docs": {
485
- "tags": [],
486
- "text": ""
487
- },
488
- "attribute": "screenshot-button-text",
489
- "reflect": false,
490
- "defaultValue": "'Add a screenshot'"
491
- },
492
- "screenshotTopbarText": {
493
- "type": "string",
494
- "mutable": false,
495
- "complexType": {
496
- "original": "string",
497
- "resolved": "string",
498
- "references": {}
499
- },
500
- "required": false,
501
- "optional": false,
502
- "docs": {
503
- "tags": [],
504
- "text": ""
505
- },
506
- "attribute": "screenshot-topbar-text",
507
- "reflect": false,
508
- "defaultValue": "'Select an element on this page'"
509
- },
510
- "hideEmail": {
511
- "type": "boolean",
512
- "mutable": false,
513
- "complexType": {
514
- "original": "boolean",
515
- "resolved": "boolean",
516
- "references": {}
517
- },
518
- "required": false,
519
- "optional": false,
520
- "docs": {
521
- "tags": [],
522
- "text": ""
523
- },
524
- "attribute": "hide-email",
525
- "reflect": false,
526
- "defaultValue": "false"
527
- },
528
- "emailAddress": {
529
- "type": "string",
530
- "mutable": false,
531
- "complexType": {
532
- "original": "string",
533
- "resolved": "string",
534
- "references": {}
535
- },
536
- "required": false,
537
- "optional": false,
538
- "docs": {
539
- "tags": [],
540
- "text": ""
541
- },
542
- "attribute": "email-address",
543
- "reflect": false,
544
- "defaultValue": "''"
545
- },
546
- "emailPlaceholder": {
547
- "type": "string",
548
- "mutable": false,
549
- "complexType": {
550
- "original": "string",
551
- "resolved": "string",
552
- "references": {}
553
- },
554
- "required": false,
555
- "optional": false,
556
- "docs": {
557
- "tags": [],
558
- "text": ""
559
- },
560
- "attribute": "email-placeholder",
561
- "reflect": false,
562
- "defaultValue": "'Email address (optional)'"
563
- },
564
- "messagePlaceholder": {
565
- "type": "string",
566
- "mutable": false,
567
- "complexType": {
568
- "original": "string",
569
- "resolved": "string",
570
- "references": {}
571
- },
572
- "required": false,
573
- "optional": false,
574
- "docs": {
575
- "tags": [],
576
- "text": ""
577
- },
578
- "attribute": "message-placeholder",
579
- "reflect": false,
580
- "defaultValue": "'Comments'"
581
- },
582
- "hideRating": {
583
- "type": "boolean",
584
- "mutable": false,
585
- "complexType": {
586
- "original": "boolean",
587
- "resolved": "boolean",
588
- "references": {}
589
- },
590
- "required": false,
591
- "optional": false,
592
- "docs": {
593
- "tags": [],
594
- "text": ""
595
- },
596
- "attribute": "hide-rating",
597
- "reflect": false,
598
- "defaultValue": "false"
599
- },
600
- "ratingMode": {
601
- "type": "string",
602
- "mutable": false,
603
- "complexType": {
604
- "original": "string",
605
- "resolved": "string",
606
- "references": {}
607
- },
608
- "required": false,
609
- "optional": false,
610
- "docs": {
611
- "tags": [],
612
- "text": ""
613
- },
614
- "attribute": "rating-mode",
615
- "reflect": false,
616
- "defaultValue": "'thumbs'"
617
- },
618
- "ratingPlaceholder": {
619
- "type": "string",
620
- "mutable": false,
621
- "complexType": {
622
- "original": "string",
623
- "resolved": "string",
624
- "references": {}
625
- },
626
- "required": false,
627
- "optional": false,
628
- "docs": {
629
- "tags": [],
630
- "text": ""
631
- },
632
- "attribute": "rating-placeholder",
633
- "reflect": false,
634
- "defaultValue": "'Was this page helpful?'"
635
- },
636
- "ratingStarsPlaceholder": {
637
- "type": "string",
638
- "mutable": false,
639
- "complexType": {
640
- "original": "string",
641
- "resolved": "string",
642
- "references": {}
643
- },
644
- "required": false,
645
- "optional": false,
646
- "docs": {
647
- "tags": [],
648
- "text": ""
649
- },
650
- "attribute": "rating-stars-placeholder",
651
- "reflect": false,
652
- "defaultValue": "'How would you rate this page?'"
653
- },
654
- "showModal": {
655
- "type": "boolean",
656
- "mutable": true,
657
- "complexType": {
658
- "original": "boolean",
659
- "resolved": "boolean",
660
- "references": {}
661
- },
662
- "required": false,
663
- "optional": false,
664
- "docs": {
665
- "tags": [],
666
- "text": ""
667
- },
668
- "attribute": "show-modal",
669
- "reflect": true,
670
- "defaultValue": "false"
671
- },
672
- "showScreenshotMode": {
673
- "type": "boolean",
674
- "mutable": true,
675
- "complexType": {
676
- "original": "boolean",
677
- "resolved": "boolean",
678
- "references": {}
679
- },
680
- "required": false,
681
- "optional": false,
682
- "docs": {
683
- "tags": [],
684
- "text": ""
685
- },
686
- "attribute": "show-screenshot-mode",
687
- "reflect": true,
688
- "defaultValue": "false"
689
- },
690
- "showScreenshotTopBar": {
691
- "type": "boolean",
692
- "mutable": true,
693
- "complexType": {
694
- "original": "boolean",
695
- "resolved": "boolean",
696
- "references": {}
697
- },
698
- "required": false,
699
- "optional": false,
700
- "docs": {
701
- "tags": [],
702
- "text": ""
703
- },
704
- "attribute": "show-screenshot-top-bar",
705
- "reflect": true,
706
- "defaultValue": "false"
707
- },
708
- "hasSelectedElement": {
709
- "type": "boolean",
710
- "mutable": true,
711
- "complexType": {
712
- "original": "boolean",
713
- "resolved": "boolean",
714
- "references": {}
715
- },
716
- "required": false,
717
- "optional": false,
718
- "docs": {
719
- "tags": [],
720
- "text": ""
721
- },
722
- "attribute": "has-selected-element",
723
- "reflect": true,
724
- "defaultValue": "false"
725
- },
726
- "hideScreenshotButton": {
727
- "type": "boolean",
728
- "mutable": false,
729
- "complexType": {
730
- "original": "boolean",
731
- "resolved": "boolean",
732
- "references": {}
733
- },
734
- "required": false,
735
- "optional": false,
736
- "docs": {
737
- "tags": [],
738
- "text": ""
739
- },
740
- "attribute": "hide-screenshot-button",
741
- "reflect": false,
742
- "defaultValue": "false"
743
- },
744
- "hidePrivacyPolicy": {
745
- "type": "boolean",
746
- "mutable": false,
747
- "complexType": {
748
- "original": "boolean",
749
- "resolved": "boolean",
750
- "references": {}
751
- },
752
- "required": false,
753
- "optional": false,
754
- "docs": {
755
- "tags": [],
756
- "text": ""
757
- },
758
- "attribute": "hide-privacy-policy",
759
- "reflect": false,
760
- "defaultValue": "true"
761
- },
762
- "privacyPolicyText": {
763
- "type": "string",
764
- "mutable": false,
765
- "complexType": {
766
- "original": "string",
767
- "resolved": "string",
768
- "references": {}
769
- },
770
- "required": false,
771
- "optional": false,
772
- "docs": {
773
- "tags": [],
774
- "text": ""
775
- },
776
- "attribute": "privacy-policy-text",
777
- "reflect": false,
778
- "defaultValue": "\"I have read and expressly consent to the terms of the <a href='https://pushfeedback.com/privacy'>Privacy Policy</a>.\""
779
- },
780
- "fetchData": {
781
- "type": "boolean",
782
- "mutable": false,
783
- "complexType": {
784
- "original": "boolean",
785
- "resolved": "boolean",
786
- "references": {}
787
- },
788
- "required": false,
789
- "optional": false,
790
- "docs": {
791
- "tags": [],
792
- "text": ""
793
- },
794
- "attribute": "fetch-data",
795
- "reflect": false,
796
- "defaultValue": "true"
797
- }
798
- };
799
- }
800
- static get states() {
801
- return {
802
- "sending": {},
803
- "formMessage": {},
804
- "formEmail": {},
805
- "formSuccess": {},
806
- "formError": {},
807
- "formErrorStatus": {},
808
- "encodedScreenshot": {},
809
- "isPrivacyChecked": {},
810
- "whitelabel": {},
811
- "selectedRating": {}
812
- };
813
- }
814
- }
1
+ import { h } from '@stencil/core';
2
+ import html2canvas from 'html2canvas';
3
+ export class FeedbackModal {
4
+ constructor() {
5
+ this.onScrollDebounced = () => {
6
+ clearTimeout(this.scrollTimeout);
7
+ this.scrollTimeout = setTimeout(() => {
8
+ document.documentElement.classList.remove('feedback-modal-screenshot-closing');
9
+ document.documentElement.style.top = "";
10
+ window.removeEventListener('scroll', this.onScrollDebounced);
11
+ }, 200);
12
+ };
13
+ this.handleSubmit = async (event) => {
14
+ event.preventDefault();
15
+ this.resetOverflow();
16
+ this.showScreenshotMode = false;
17
+ this.showScreenshotTopBar = false;
18
+ this.showModal = false;
19
+ this.sending = true;
20
+ try {
21
+ const res = await fetch('https://app.pushfeedback.com/api/feedback/', {
22
+ method: 'POST',
23
+ body: JSON.stringify({
24
+ url: window.location.href,
25
+ message: this.formMessage,
26
+ email: this.formEmail,
27
+ project: this.project,
28
+ screenshot: this.encodedScreenshot,
29
+ rating: this.selectedRating,
30
+ ratingMode: this.ratingMode,
31
+ }),
32
+ headers: {
33
+ 'Content-Type': 'application/json'
34
+ }
35
+ });
36
+ if (res.status === 201) {
37
+ this.formSuccess = true;
38
+ this.formError = false;
39
+ }
40
+ else {
41
+ this.formSuccess = false;
42
+ this.formError = true;
43
+ this.formErrorStatus = res.status;
44
+ }
45
+ }
46
+ catch (error) {
47
+ console.log(error);
48
+ this.formSuccess = false;
49
+ this.formError = true;
50
+ this.formErrorStatus = 500;
51
+ }
52
+ finally {
53
+ this.sending = false;
54
+ this.showModal = true;
55
+ }
56
+ };
57
+ this.close = () => {
58
+ this.sending = false;
59
+ this.showModal = false;
60
+ this.showScreenshotMode = false;
61
+ this.showScreenshotTopBar = false;
62
+ this.hasSelectedElement = false;
63
+ this.encodedScreenshot = null;
64
+ this.formSuccess = false;
65
+ this.formError = false;
66
+ this.formErrorStatus = 500;
67
+ this.formMessage = '';
68
+ this.formEmail = '';
69
+ this.resetOverflow();
70
+ };
71
+ this.openScreenShot = () => {
72
+ this.hasSelectedElement = false;
73
+ this.showModal = false;
74
+ this.showScreenshotMode = true;
75
+ this.showScreenshotTopBar = true;
76
+ this.encodedScreenshot = null;
77
+ if (window.innerWidth > document.documentElement.clientWidth) {
78
+ document.documentElement.classList.add('feedback-modal-screenshot-open--scroll');
79
+ }
80
+ const scrollY = window.scrollY;
81
+ document.documentElement.style.top = `-${scrollY}px`;
82
+ window.scrollTo(0, parseInt(document.documentElement.style.top || '0') * -1);
83
+ document.documentElement.classList.add('feedback-modal-screenshot-open');
84
+ };
85
+ this.closeScreenShot = () => {
86
+ this.showModal = false;
87
+ this.showScreenshotMode = false;
88
+ this.showScreenshotTopBar = false;
89
+ this.hasSelectedElement = false;
90
+ this.encodedScreenshot = null;
91
+ this.resetOverflow();
92
+ };
93
+ this.handleMouseOverScreenShot = (event) => {
94
+ event.preventDefault();
95
+ if (this.hasSelectedElement)
96
+ return;
97
+ const borderOffset = 2;
98
+ this.screenshotModal.style.display = 'none';
99
+ const elementUnder = document.elementFromPoint(event.clientX, event.clientY);
100
+ const rect = elementUnder.getBoundingClientRect();
101
+ this.screenshotModal.style.display = '';
102
+ // Get the bounding box of the element selected
103
+ this.elementSelected.style.position = "absolute";
104
+ this.elementSelected.style.left = `${rect.left}px`;
105
+ this.elementSelected.style.top = `${rect.top}px`;
106
+ this.elementSelected.style.width = `${rect.width}px`;
107
+ this.elementSelected.style.height = `${rect.height}px`;
108
+ this.elementSelected.classList.add('feedback-modal-element-hover');
109
+ // Set the background color of nonselected areas
110
+ // Top
111
+ this.topSide.style.position = "absolute";
112
+ this.topSide.style.left = `${rect.left}px`;
113
+ this.topSide.style.top = '0px';
114
+ this.topSide.style.width = `${rect.width + borderOffset}px`;
115
+ this.topSide.style.height = `${rect.top}px`;
116
+ this.topSide.style.backgroundColor = "rgba(0, 0, 0, 0.4)";
117
+ // Left
118
+ this.leftSide.style.position = "absolute";
119
+ this.leftSide.style.left = '0px';
120
+ this.leftSide.style.top = '0px';
121
+ this.leftSide.style.width = `${rect.left}px`;
122
+ this.leftSide.style.height = '100vh';
123
+ this.leftSide.style.backgroundColor = "rgba(0, 0, 0, 0.4)";
124
+ // Bottom
125
+ this.bottomSide.style.position = "absolute";
126
+ this.bottomSide.style.left = `${rect.left}px`;
127
+ this.bottomSide.style.top = `${rect.bottom + borderOffset}px`;
128
+ this.bottomSide.style.width = `${rect.width + borderOffset}px`;
129
+ this.bottomSide.style.height = '100vh';
130
+ this.bottomSide.style.backgroundColor = "rgba(0, 0, 0, 0.4)";
131
+ // Right
132
+ this.rightSide.style.position = "absolute";
133
+ this.rightSide.style.left = `${rect.right + borderOffset}px`;
134
+ ;
135
+ this.rightSide.style.top = '0px';
136
+ this.rightSide.style.width = '100%';
137
+ this.rightSide.style.height = '100vh';
138
+ this.rightSide.style.backgroundColor = "rgba(0, 0, 0, 0.4)";
139
+ // Restore the visibility of the screenshot-modal
140
+ this.screenshotModal.style.backgroundColor = 'transparent';
141
+ };
142
+ this.handleMouseClickedSelectedElement = async (event) => {
143
+ event.preventDefault();
144
+ if (!this.elementSelected) {
145
+ return;
146
+ }
147
+ this.hasSelectedElement = true;
148
+ this.elementSelected.classList.add('feedback-modal-element-selected');
149
+ // Get the top position including the scroll offset
150
+ const rectTop = this.elementSelected.getBoundingClientRect().top;
151
+ const topWithScroll = rectTop + window.scrollY;
152
+ // Move the element with the scroll offset
153
+ this.elementSelected.style.top = `${topWithScroll}px`;
154
+ // Clone the selected element and append it to the body
155
+ const clonedElementSelected = this.elementSelected.cloneNode(true);
156
+ document.body.appendChild(clonedElementSelected);
157
+ // Reset the top position of the original element
158
+ this.elementSelected.style.top = `${rectTop}px`;
159
+ this.showScreenshotTopBar = false;
160
+ this.showModal = false;
161
+ try {
162
+ const dataUrl = await this.captureScreenshot();
163
+ console.log('Screenshot captured');
164
+ this.encodedScreenshot = dataUrl;
165
+ }
166
+ catch (error) {
167
+ console.error('Failed to capture screenshot:', error);
168
+ this.hasSelectedElement = false;
169
+ }
170
+ finally {
171
+ // Remove the cloned element and show the modal again
172
+ document.body.removeChild(clonedElementSelected);
173
+ this.showModal = true;
174
+ }
175
+ };
176
+ this.sending = false;
177
+ this.formMessage = '';
178
+ this.formEmail = '';
179
+ this.formSuccess = false;
180
+ this.formError = false;
181
+ this.formErrorStatus = 500;
182
+ this.encodedScreenshot = undefined;
183
+ this.isPrivacyChecked = false;
184
+ this.whitelabel = false;
185
+ this.selectedRating = 0;
186
+ this.errorMessage = "Please try again later.";
187
+ this.errorMessage403 = "The request URL does not match the one defined in PushFeedback for this project.";
188
+ this.errorMessage404 = "We could not find the provided project ID in PushFeedback.";
189
+ this.modalTitle = 'Share your feedback';
190
+ this.modalTitleSuccess = 'Thanks for your feedback!';
191
+ this.modalTitleError = "Oops!";
192
+ this.modalPosition = 'center';
193
+ this.sendButtonText = 'Send';
194
+ this.successMessage = "";
195
+ this.project = '';
196
+ this.screenshotButtonText = 'Add a screenshot';
197
+ this.screenshotTopbarText = 'Select an element on this page';
198
+ this.hideEmail = false;
199
+ this.emailAddress = '';
200
+ this.emailPlaceholder = 'Email address (optional)';
201
+ this.messagePlaceholder = 'Comments';
202
+ this.hideRating = false;
203
+ this.rating = undefined;
204
+ this.ratingMode = 'thumbs';
205
+ this.ratingPlaceholder = 'Was this page helpful?';
206
+ this.ratingStarsPlaceholder = 'How would you rate this page?';
207
+ this.showModal = false;
208
+ this.showScreenshotMode = false;
209
+ this.showScreenshotTopBar = false;
210
+ this.hasSelectedElement = false;
211
+ this.hideScreenshotButton = false;
212
+ this.hidePrivacyPolicy = true;
213
+ this.privacyPolicyText = "I have read and expressly consent to the terms of the <a href='https://pushfeedback.com/privacy'>Privacy Policy</a>.";
214
+ this.fetchData = true;
215
+ }
216
+ componentWillLoad() {
217
+ if (this.fetchData)
218
+ this.fetchProjectData();
219
+ this.formEmail = this.emailAddress;
220
+ if (this.rating) {
221
+ this.selectedRating = this.rating;
222
+ }
223
+ if (this.ratingMode == "thumbs" && this.rating == 0) {
224
+ this.selectedRating = 5;
225
+ }
226
+ }
227
+ async fetchProjectData() {
228
+ try {
229
+ const response = await fetch('https://app.pushfeedback.com/api/projects/' + this.project + '/');
230
+ const data = await response.json();
231
+ this.whitelabel = data.whitelabel;
232
+ }
233
+ catch (error) {
234
+ console.log(error);
235
+ }
236
+ }
237
+ resetOverflow() {
238
+ document.documentElement.classList.remove('feedback-modal-screenshot-open');
239
+ document.documentElement.classList.remove('feedback-modal-screenshot-open--scroll');
240
+ document.documentElement.classList.add('feedback-modal-screenshot-closing');
241
+ window.scrollTo(0, parseInt(document.documentElement.style.top || '0') * -1);
242
+ window.addEventListener('scroll', this.onScrollDebounced);
243
+ }
244
+ handleMessageInput(event) {
245
+ this.formMessage = event.target.value;
246
+ }
247
+ handleEmailInput(event) {
248
+ this.formEmail = event.target.value;
249
+ }
250
+ captureScreenshot() {
251
+ return new Promise((resolve, reject) => {
252
+ requestAnimationFrame(() => {
253
+ html2canvas(document.body, {
254
+ x: window.scrollX,
255
+ y: window.scrollY,
256
+ width: window.innerWidth,
257
+ height: window.innerHeight,
258
+ }).then(canvas => {
259
+ const dataUrl = canvas.toDataURL();
260
+ resolve(dataUrl);
261
+ })
262
+ .catch(error => {
263
+ console.error(error);
264
+ reject(error);
265
+ });
266
+ });
267
+ });
268
+ }
269
+ handleCheckboxChange(event) {
270
+ this.isPrivacyChecked = event.target.checked;
271
+ }
272
+ handleRatingChange(newRating) {
273
+ this.selectedRating = newRating;
274
+ }
275
+ render() {
276
+ return (h("div", { class: 'feedback-modal-wrapper' }, this.showScreenshotMode && (h("div", { class: "feedback-modal-screenshot", ref: el => (this.screenshotModal = el), onMouseMove: this.handleMouseOverScreenShot }, h("div", { class: "feedback-modal-screenshot-element-selected", ref: el => (this.elementSelected = el), onClick: this.handleMouseClickedSelectedElement }), h("div", { class: "top-side", ref: el => (this.topSide = el) }), h("div", { class: "left-side", ref: el => (this.leftSide = el) }), h("div", { class: "bottom-side", ref: el => (this.bottomSide = el) }), h("div", { class: "right-side", ref: el => (this.rightSide = el) }), this.showScreenshotTopBar && (h("div", { class: "feedback-modal-screenshot-header", onClick: this.closeScreenShot }, h("span", null, this.screenshotTopbarText), h("span", { class: "feedback-modal-screenshot-close" }, h("svg", { xmlns: "http://www.w3.org/2000/svg", width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", stroke: "#191919", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", class: "feather feather-x" }, h("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), h("line", { x1: "6", y1: "6", x2: "18", y2: "18" }))))))), this.showModal && (h("div", { class: "feedback-overlay" })), this.showModal && (h("div", { class: `feedback-modal-content feedback-modal-content--${this.modalPosition} ${this.showModal ? 'feedback-modal-content--open' : ''}`, ref: el => (this.modalContent = el) }, h("div", { class: "feedback-modal-header" }, !this.formSuccess && !this.formError ? (h("span", null, this.modalTitle)) : this.formSuccess ? (h("span", null, this.modalTitleSuccess)) : h("span", null, this.modalTitleError), h("button", { class: "feedback-modal-close", onClick: this.close }, h("svg", { xmlns: "http://www.w3.org/2000/svg", width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", stroke: "#191919", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", class: "feather feather-x" }, h("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), h("line", { x1: "6", y1: "6", x2: "18", y2: "18" })))), h("div", { class: "feedback-modal-body" }, !this.formSuccess && !this.formError ? (h("form", { onSubmit: this.handleSubmit }, !this.hideRating && (h("div", { class: "feedback-modal-rating" }, this.ratingMode === 'thumbs' ? (h("div", { class: "feedback-modal-rating-content" }, h("span", { class: "feedback-modal-input-heading" }, this.ratingPlaceholder), h("div", { class: "feedback-modal-rating-buttons feedback-modal-rating-buttons--thumbs" }, h("button", { title: "Yes", class: `feedback-modal-rating-button ${this.selectedRating === 1 ? 'feedback-modal-rating-button--selected' : ''}`, onClick: (event) => {
277
+ event.preventDefault();
278
+ this.handleRatingChange(1);
279
+ } }, h("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "#5F6368", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, h("path", { d: "M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3" }))), h("button", { title: "No", class: `feedback-modal-rating-button ${this.selectedRating === 5 ? 'feedback-modal-rating-button--selected' : ''}`, onClick: (event) => {
280
+ event.preventDefault();
281
+ this.handleRatingChange(5);
282
+ } }, h("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "#5F6368", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, h("path", { d: "M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17" })))))) : (h("div", { class: "feedback-modal-rating-content" }, h("span", { class: "feedback-modal-input-heading" }, this.ratingStarsPlaceholder), h("div", { class: "feedback-modal-rating-buttons feedback-modal-rating-buttons--stars" }, [1, 2, 3, 4, 5].map((rating) => (h("button", { key: rating, class: `feedback-modal-rating-button ${this.selectedRating >= rating ? 'feedback-modal-rating-button--selected' : ''}`, onClick: (event) => {
283
+ event.preventDefault();
284
+ this.handleRatingChange(rating);
285
+ } }, h("svg", { xmlns: "http://www.w3.org/2000/svg", width: "28", height: "28", viewBox: "0 0 24 24", fill: "none", stroke: "#5F6368", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, h("polygon", { points: "12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" })))))))))), h("div", { class: "feedback-modal-text" }, h("textarea", { placeholder: this.messagePlaceholder, value: this.formMessage, onInput: (event) => this.handleMessageInput(event) })), !this.hideEmail && (h("div", { class: "feedback-modal-email" }, h("input", { placeholder: this.emailPlaceholder, type: "email", onInput: (event) => this.handleEmailInput(event), value: this.formEmail }))), !this.hidePrivacyPolicy && (h("div", { class: "feedback-modal-privacy" }, h("input", { type: "checkbox", id: "privacyPolicy", onChange: (ev) => this.handleCheckboxChange(ev), required: true }), h("span", { innerHTML: this.privacyPolicyText }))), h("div", { class: `feedback-modal-buttons ${this.hideScreenshotButton ? 'single' : ''}` }, !this.hideScreenshotButton && (h("button", { type: "button", class: `feedback-modal-button feedback-modal-button--screenshot ${this.encodedScreenshot ? "feedback-modal-button--active" : ""}`, onClick: this.openScreenShot, disabled: this.sending }, h("svg", { xmlns: "http://www.w3.org/2000/svg", height: "24", viewBox: "0 -960 960 960", width: "24" }, h("path", { d: "M680-80v-120H560v-80h120v-120h80v120h120v80H760v120h-80ZM200-200v-200h80v120h120v80H200Zm0-360v-200h200v80H280v120h-80Zm480 0v-120H560v-80h200v200h-80Z" })), this.screenshotButtonText)), h("button", { class: "feedback-modal-button feedback-modal-button--submit", type: "submit", disabled: this.sending }, this.sendButtonText)))) : this.formSuccess && !this.formError ? (h("p", { class: "feedback-modal-message" }, this.successMessage)) : this.formError && this.formErrorStatus == 404 ? (h("p", { class: "feedback-modal-message" }, this.errorMessage404)) : this.formError && this.formErrorStatus == 403 ? (h("p", { class: "feedback-modal-message" }, this.errorMessage403)) : this.formError ? (h("p", { class: "feedback-modal-message" }, this.errorMessage)) : h("span", null)), h("div", { class: "feedback-modal-footer", style: { display: this.whitelabel ? 'none' : 'block' } }, h("div", { class: "feedback-logo" }, "Powered by ", h("a", { target: "_blank", href: "https://pushfeedback.com" }, "PushFeedback.com")))))));
286
+ }
287
+ static get is() { return "feedback-modal"; }
288
+ static get encapsulation() { return "shadow"; }
289
+ static get originalStyleUrls() {
290
+ return {
291
+ "$": ["feedback-modal.css"]
292
+ };
293
+ }
294
+ static get styleUrls() {
295
+ return {
296
+ "$": ["feedback-modal.css"]
297
+ };
298
+ }
299
+ static get properties() {
300
+ return {
301
+ "errorMessage": {
302
+ "type": "string",
303
+ "mutable": false,
304
+ "complexType": {
305
+ "original": "string",
306
+ "resolved": "string",
307
+ "references": {}
308
+ },
309
+ "required": false,
310
+ "optional": false,
311
+ "docs": {
312
+ "tags": [],
313
+ "text": ""
314
+ },
315
+ "attribute": "error-message",
316
+ "reflect": false,
317
+ "defaultValue": "\"Please try again later.\""
318
+ },
319
+ "errorMessage403": {
320
+ "type": "string",
321
+ "mutable": false,
322
+ "complexType": {
323
+ "original": "string",
324
+ "resolved": "string",
325
+ "references": {}
326
+ },
327
+ "required": false,
328
+ "optional": false,
329
+ "docs": {
330
+ "tags": [],
331
+ "text": ""
332
+ },
333
+ "attribute": "error-message-4-0-3",
334
+ "reflect": false,
335
+ "defaultValue": "\"The request URL does not match the one defined in PushFeedback for this project.\""
336
+ },
337
+ "errorMessage404": {
338
+ "type": "string",
339
+ "mutable": false,
340
+ "complexType": {
341
+ "original": "string",
342
+ "resolved": "string",
343
+ "references": {}
344
+ },
345
+ "required": false,
346
+ "optional": false,
347
+ "docs": {
348
+ "tags": [],
349
+ "text": ""
350
+ },
351
+ "attribute": "error-message-4-0-4",
352
+ "reflect": false,
353
+ "defaultValue": "\"We could not find the provided project ID in PushFeedback.\""
354
+ },
355
+ "modalTitle": {
356
+ "type": "string",
357
+ "mutable": false,
358
+ "complexType": {
359
+ "original": "string",
360
+ "resolved": "string",
361
+ "references": {}
362
+ },
363
+ "required": false,
364
+ "optional": false,
365
+ "docs": {
366
+ "tags": [],
367
+ "text": ""
368
+ },
369
+ "attribute": "modal-title",
370
+ "reflect": false,
371
+ "defaultValue": "'Share your feedback'"
372
+ },
373
+ "modalTitleSuccess": {
374
+ "type": "string",
375
+ "mutable": false,
376
+ "complexType": {
377
+ "original": "string",
378
+ "resolved": "string",
379
+ "references": {}
380
+ },
381
+ "required": false,
382
+ "optional": false,
383
+ "docs": {
384
+ "tags": [],
385
+ "text": ""
386
+ },
387
+ "attribute": "modal-title-success",
388
+ "reflect": false,
389
+ "defaultValue": "'Thanks for your feedback!'"
390
+ },
391
+ "modalTitleError": {
392
+ "type": "string",
393
+ "mutable": false,
394
+ "complexType": {
395
+ "original": "string",
396
+ "resolved": "string",
397
+ "references": {}
398
+ },
399
+ "required": false,
400
+ "optional": false,
401
+ "docs": {
402
+ "tags": [],
403
+ "text": ""
404
+ },
405
+ "attribute": "modal-title-error",
406
+ "reflect": false,
407
+ "defaultValue": "\"Oops!\""
408
+ },
409
+ "modalPosition": {
410
+ "type": "string",
411
+ "mutable": false,
412
+ "complexType": {
413
+ "original": "string",
414
+ "resolved": "string",
415
+ "references": {}
416
+ },
417
+ "required": false,
418
+ "optional": false,
419
+ "docs": {
420
+ "tags": [],
421
+ "text": ""
422
+ },
423
+ "attribute": "modal-position",
424
+ "reflect": false,
425
+ "defaultValue": "'center'"
426
+ },
427
+ "sendButtonText": {
428
+ "type": "string",
429
+ "mutable": false,
430
+ "complexType": {
431
+ "original": "string",
432
+ "resolved": "string",
433
+ "references": {}
434
+ },
435
+ "required": false,
436
+ "optional": false,
437
+ "docs": {
438
+ "tags": [],
439
+ "text": ""
440
+ },
441
+ "attribute": "send-button-text",
442
+ "reflect": false,
443
+ "defaultValue": "'Send'"
444
+ },
445
+ "successMessage": {
446
+ "type": "string",
447
+ "mutable": false,
448
+ "complexType": {
449
+ "original": "string",
450
+ "resolved": "string",
451
+ "references": {}
452
+ },
453
+ "required": false,
454
+ "optional": false,
455
+ "docs": {
456
+ "tags": [],
457
+ "text": ""
458
+ },
459
+ "attribute": "success-message",
460
+ "reflect": false,
461
+ "defaultValue": "\"\""
462
+ },
463
+ "project": {
464
+ "type": "string",
465
+ "mutable": false,
466
+ "complexType": {
467
+ "original": "string",
468
+ "resolved": "string",
469
+ "references": {}
470
+ },
471
+ "required": false,
472
+ "optional": false,
473
+ "docs": {
474
+ "tags": [],
475
+ "text": ""
476
+ },
477
+ "attribute": "project",
478
+ "reflect": false,
479
+ "defaultValue": "''"
480
+ },
481
+ "screenshotButtonText": {
482
+ "type": "string",
483
+ "mutable": false,
484
+ "complexType": {
485
+ "original": "string",
486
+ "resolved": "string",
487
+ "references": {}
488
+ },
489
+ "required": false,
490
+ "optional": false,
491
+ "docs": {
492
+ "tags": [],
493
+ "text": ""
494
+ },
495
+ "attribute": "screenshot-button-text",
496
+ "reflect": false,
497
+ "defaultValue": "'Add a screenshot'"
498
+ },
499
+ "screenshotTopbarText": {
500
+ "type": "string",
501
+ "mutable": false,
502
+ "complexType": {
503
+ "original": "string",
504
+ "resolved": "string",
505
+ "references": {}
506
+ },
507
+ "required": false,
508
+ "optional": false,
509
+ "docs": {
510
+ "tags": [],
511
+ "text": ""
512
+ },
513
+ "attribute": "screenshot-topbar-text",
514
+ "reflect": false,
515
+ "defaultValue": "'Select an element on this page'"
516
+ },
517
+ "hideEmail": {
518
+ "type": "boolean",
519
+ "mutable": false,
520
+ "complexType": {
521
+ "original": "boolean",
522
+ "resolved": "boolean",
523
+ "references": {}
524
+ },
525
+ "required": false,
526
+ "optional": false,
527
+ "docs": {
528
+ "tags": [],
529
+ "text": ""
530
+ },
531
+ "attribute": "hide-email",
532
+ "reflect": false,
533
+ "defaultValue": "false"
534
+ },
535
+ "emailAddress": {
536
+ "type": "string",
537
+ "mutable": false,
538
+ "complexType": {
539
+ "original": "string",
540
+ "resolved": "string",
541
+ "references": {}
542
+ },
543
+ "required": false,
544
+ "optional": false,
545
+ "docs": {
546
+ "tags": [],
547
+ "text": ""
548
+ },
549
+ "attribute": "email-address",
550
+ "reflect": false,
551
+ "defaultValue": "''"
552
+ },
553
+ "emailPlaceholder": {
554
+ "type": "string",
555
+ "mutable": false,
556
+ "complexType": {
557
+ "original": "string",
558
+ "resolved": "string",
559
+ "references": {}
560
+ },
561
+ "required": false,
562
+ "optional": false,
563
+ "docs": {
564
+ "tags": [],
565
+ "text": ""
566
+ },
567
+ "attribute": "email-placeholder",
568
+ "reflect": false,
569
+ "defaultValue": "'Email address (optional)'"
570
+ },
571
+ "messagePlaceholder": {
572
+ "type": "string",
573
+ "mutable": false,
574
+ "complexType": {
575
+ "original": "string",
576
+ "resolved": "string",
577
+ "references": {}
578
+ },
579
+ "required": false,
580
+ "optional": false,
581
+ "docs": {
582
+ "tags": [],
583
+ "text": ""
584
+ },
585
+ "attribute": "message-placeholder",
586
+ "reflect": false,
587
+ "defaultValue": "'Comments'"
588
+ },
589
+ "hideRating": {
590
+ "type": "boolean",
591
+ "mutable": false,
592
+ "complexType": {
593
+ "original": "boolean",
594
+ "resolved": "boolean",
595
+ "references": {}
596
+ },
597
+ "required": false,
598
+ "optional": false,
599
+ "docs": {
600
+ "tags": [],
601
+ "text": ""
602
+ },
603
+ "attribute": "hide-rating",
604
+ "reflect": false,
605
+ "defaultValue": "false"
606
+ },
607
+ "rating": {
608
+ "type": "number",
609
+ "mutable": false,
610
+ "complexType": {
611
+ "original": "number",
612
+ "resolved": "number",
613
+ "references": {}
614
+ },
615
+ "required": false,
616
+ "optional": false,
617
+ "docs": {
618
+ "tags": [],
619
+ "text": ""
620
+ },
621
+ "attribute": "rating",
622
+ "reflect": false
623
+ },
624
+ "ratingMode": {
625
+ "type": "string",
626
+ "mutable": false,
627
+ "complexType": {
628
+ "original": "string",
629
+ "resolved": "string",
630
+ "references": {}
631
+ },
632
+ "required": false,
633
+ "optional": false,
634
+ "docs": {
635
+ "tags": [],
636
+ "text": ""
637
+ },
638
+ "attribute": "rating-mode",
639
+ "reflect": false,
640
+ "defaultValue": "'thumbs'"
641
+ },
642
+ "ratingPlaceholder": {
643
+ "type": "string",
644
+ "mutable": false,
645
+ "complexType": {
646
+ "original": "string",
647
+ "resolved": "string",
648
+ "references": {}
649
+ },
650
+ "required": false,
651
+ "optional": false,
652
+ "docs": {
653
+ "tags": [],
654
+ "text": ""
655
+ },
656
+ "attribute": "rating-placeholder",
657
+ "reflect": false,
658
+ "defaultValue": "'Was this page helpful?'"
659
+ },
660
+ "ratingStarsPlaceholder": {
661
+ "type": "string",
662
+ "mutable": false,
663
+ "complexType": {
664
+ "original": "string",
665
+ "resolved": "string",
666
+ "references": {}
667
+ },
668
+ "required": false,
669
+ "optional": false,
670
+ "docs": {
671
+ "tags": [],
672
+ "text": ""
673
+ },
674
+ "attribute": "rating-stars-placeholder",
675
+ "reflect": false,
676
+ "defaultValue": "'How would you rate this page?'"
677
+ },
678
+ "showModal": {
679
+ "type": "boolean",
680
+ "mutable": true,
681
+ "complexType": {
682
+ "original": "boolean",
683
+ "resolved": "boolean",
684
+ "references": {}
685
+ },
686
+ "required": false,
687
+ "optional": false,
688
+ "docs": {
689
+ "tags": [],
690
+ "text": ""
691
+ },
692
+ "attribute": "show-modal",
693
+ "reflect": true,
694
+ "defaultValue": "false"
695
+ },
696
+ "showScreenshotMode": {
697
+ "type": "boolean",
698
+ "mutable": true,
699
+ "complexType": {
700
+ "original": "boolean",
701
+ "resolved": "boolean",
702
+ "references": {}
703
+ },
704
+ "required": false,
705
+ "optional": false,
706
+ "docs": {
707
+ "tags": [],
708
+ "text": ""
709
+ },
710
+ "attribute": "show-screenshot-mode",
711
+ "reflect": true,
712
+ "defaultValue": "false"
713
+ },
714
+ "showScreenshotTopBar": {
715
+ "type": "boolean",
716
+ "mutable": true,
717
+ "complexType": {
718
+ "original": "boolean",
719
+ "resolved": "boolean",
720
+ "references": {}
721
+ },
722
+ "required": false,
723
+ "optional": false,
724
+ "docs": {
725
+ "tags": [],
726
+ "text": ""
727
+ },
728
+ "attribute": "show-screenshot-top-bar",
729
+ "reflect": true,
730
+ "defaultValue": "false"
731
+ },
732
+ "hasSelectedElement": {
733
+ "type": "boolean",
734
+ "mutable": true,
735
+ "complexType": {
736
+ "original": "boolean",
737
+ "resolved": "boolean",
738
+ "references": {}
739
+ },
740
+ "required": false,
741
+ "optional": false,
742
+ "docs": {
743
+ "tags": [],
744
+ "text": ""
745
+ },
746
+ "attribute": "has-selected-element",
747
+ "reflect": true,
748
+ "defaultValue": "false"
749
+ },
750
+ "hideScreenshotButton": {
751
+ "type": "boolean",
752
+ "mutable": false,
753
+ "complexType": {
754
+ "original": "boolean",
755
+ "resolved": "boolean",
756
+ "references": {}
757
+ },
758
+ "required": false,
759
+ "optional": false,
760
+ "docs": {
761
+ "tags": [],
762
+ "text": ""
763
+ },
764
+ "attribute": "hide-screenshot-button",
765
+ "reflect": false,
766
+ "defaultValue": "false"
767
+ },
768
+ "hidePrivacyPolicy": {
769
+ "type": "boolean",
770
+ "mutable": false,
771
+ "complexType": {
772
+ "original": "boolean",
773
+ "resolved": "boolean",
774
+ "references": {}
775
+ },
776
+ "required": false,
777
+ "optional": false,
778
+ "docs": {
779
+ "tags": [],
780
+ "text": ""
781
+ },
782
+ "attribute": "hide-privacy-policy",
783
+ "reflect": false,
784
+ "defaultValue": "true"
785
+ },
786
+ "privacyPolicyText": {
787
+ "type": "string",
788
+ "mutable": false,
789
+ "complexType": {
790
+ "original": "string",
791
+ "resolved": "string",
792
+ "references": {}
793
+ },
794
+ "required": false,
795
+ "optional": false,
796
+ "docs": {
797
+ "tags": [],
798
+ "text": ""
799
+ },
800
+ "attribute": "privacy-policy-text",
801
+ "reflect": false,
802
+ "defaultValue": "\"I have read and expressly consent to the terms of the <a href='https://pushfeedback.com/privacy'>Privacy Policy</a>.\""
803
+ },
804
+ "fetchData": {
805
+ "type": "boolean",
806
+ "mutable": false,
807
+ "complexType": {
808
+ "original": "boolean",
809
+ "resolved": "boolean",
810
+ "references": {}
811
+ },
812
+ "required": false,
813
+ "optional": false,
814
+ "docs": {
815
+ "tags": [],
816
+ "text": ""
817
+ },
818
+ "attribute": "fetch-data",
819
+ "reflect": false,
820
+ "defaultValue": "true"
821
+ }
822
+ };
823
+ }
824
+ static get states() {
825
+ return {
826
+ "sending": {},
827
+ "formMessage": {},
828
+ "formEmail": {},
829
+ "formSuccess": {},
830
+ "formError": {},
831
+ "formErrorStatus": {},
832
+ "encodedScreenshot": {},
833
+ "isPrivacyChecked": {},
834
+ "whitelabel": {},
835
+ "selectedRating": {}
836
+ };
837
+ }
838
+ }