pushfeedback 0.1.69 → 0.1.70

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.
Files changed (47) hide show
  1. package/dist/components/canvas-editor.d.ts +11 -0
  2. package/dist/components/index.d.ts +1 -0
  3. package/dist/pushfeedback/app-globals-0f993ce5.js +3 -0
  4. package/dist/pushfeedback/canvas-editor.entry.js +860 -0
  5. package/dist/pushfeedback/css-shim-b7d3d95f.js +4 -0
  6. package/dist/pushfeedback/dom-64053c71.js +73 -0
  7. package/dist/{components/feedback-button.js → pushfeedback/feedback-button.entry.js} +30 -73
  8. package/dist/pushfeedback/feedback-modal.entry.js +295 -0
  9. package/dist/pushfeedback/index-36434da0.js +3371 -0
  10. package/dist/pushfeedback/index.esm.js +1 -0
  11. package/dist/pushfeedback/pushfeedback.css +146 -1
  12. package/dist/pushfeedback/pushfeedback.esm.js +148 -1
  13. package/dist/pushfeedback/shadow-css-98135883.js +387 -0
  14. package/dist/types/components/canvas-editor/canvas-editor.d.ts +108 -0
  15. package/dist/types/components/feedback-button/feedback-button.d.ts +11 -0
  16. package/dist/types/components/feedback-modal/feedback-modal.d.ts +22 -79
  17. package/dist/types/components.d.ts +102 -0
  18. package/package.json +3 -4
  19. package/dist/cjs/feedback-button_2.cjs.entry.js +0 -1202
  20. package/dist/cjs/index-9a8f4784.js +0 -1584
  21. package/dist/cjs/index.cjs.js +0 -2
  22. package/dist/cjs/loader.cjs.js +0 -22
  23. package/dist/cjs/pushfeedback.cjs.js +0 -23
  24. package/dist/collection/collection-manifest.json +0 -13
  25. package/dist/collection/components/feedback-button/feedback-button.css +0 -81
  26. package/dist/collection/components/feedback-button/feedback-button.js +0 -949
  27. package/dist/collection/components/feedback-modal/feedback-modal.css +0 -1003
  28. package/dist/collection/components/feedback-modal/feedback-modal.js +0 -1792
  29. package/dist/collection/index.js +0 -1
  30. package/dist/components/feedback-modal.js +0 -6
  31. package/dist/components/feedback-modal2.js +0 -1101
  32. package/dist/components/index.js +0 -3
  33. package/dist/esm/feedback-button_2.entry.js +0 -1197
  34. package/dist/esm/index-f65e9124.js +0 -1555
  35. package/dist/esm/index.js +0 -1
  36. package/dist/esm/loader.js +0 -18
  37. package/dist/esm/polyfills/core-js.js +0 -11
  38. package/dist/esm/polyfills/css-shim.js +0 -1
  39. package/dist/esm/polyfills/dom.js +0 -79
  40. package/dist/esm/polyfills/es5-html-element.js +0 -1
  41. package/dist/esm/polyfills/index.js +0 -34
  42. package/dist/esm/polyfills/system.js +0 -6
  43. package/dist/esm/pushfeedback.js +0 -18
  44. package/dist/index.cjs.js +0 -1
  45. package/dist/index.js +0 -1
  46. package/dist/pushfeedback/p-af2a1f7f.js +0 -2
  47. package/dist/pushfeedback/p-e7f48090.entry.js +0 -1
@@ -1,1202 +0,0 @@
1
- 'use strict';
2
-
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
- const index = require('./index-9a8f4784.js');
6
-
7
- const feedbackButtonCss = ".feedback-button-content{cursor:pointer;max-width:fit-content;z-index:var(--feedback-button-z-index);font-family:var(--feedback-font-family)}.feedback-button-content--custom-font{font-family:inherit}.feedback-button-content--light{align-items:center;background-color:var(--feedback-button-light-bg-color);border-radius:var(--feedback-button-border-radius);box-shadow:rgba(60, 64, 67, 0.3) 0px 1px 2px 0px, rgba(60, 64, 67, 0.15) 0px 2px 6px 2px;box-sizing:border-box;color:var(--feedback-button-light-text-color);display:flex;font-size:var(--feedback-button-text-font-size);font-weight:var(--feedback-button-text-font-weight);padding:8px 15px}.feedback-button-content--dark{align-items:center;background-color:var(--feedback-button-dark-bg-color);border-radius:var(--feedback-button-border-radius);box-shadow:rgba(60, 64, 67, 0.3) 0px 1px 2px 0px, rgba(60, 64, 67, 0.15) 0px 2px 6px 2px;box-sizing:border-box;color:var(--feedback-button-dark-text-color);display:flex;font-weight:var(--feedback-button-text-font-weight);font-size:var(--feedback-button-text-font-size);padding:8px 15px}.icon-edit{stroke:var(--feedback-button-light-icon-color)}.feedback-button-content--dark .icon-edit{stroke:var(--feedback-button-dark-icon-color)}.feedback-button-content--bottom-right{bottom:10px;position:fixed;right:10px}.feedback-button-content--center-right{position:fixed;transform:rotate(-90deg) translateY(-50%);top:50%}.feedback-button-content--center-right.feedback-button-content--dark,.feedback-button-content--center-right.feedback-button-content--light{border-radius:4px;border-bottom-left-radius:0px;border-bottom-right-radius:0px}.feedback-button-content-icon{height:16px;margin-right:5px;width:16px}.feedback-button-content--center-right .feedback-button-content-icon{rotate:90deg}@media screen and (max-width: 767px){.feedback-button-content--hide-mobile{display:none}}";
8
-
9
- const FeedbackButton = class {
10
- constructor(hostRef) {
11
- index.registerInstance(this, hostRef);
12
- this.feedbackSent = index.createEvent(this, "feedbackSent", 7);
13
- this.feedbackError = index.createEvent(this, "feedbackError", 7);
14
- this.buttonPosition = 'default';
15
- this.buttonStyle = 'default';
16
- this.hideIcon = false;
17
- this.hideMobile = false;
18
- this.sessionId = '';
19
- this.metadata = '';
20
- this.submit = false;
21
- this.customFont = false;
22
- this.emailAddress = '';
23
- this.isEmailRequired = false;
24
- this.fetchData = true;
25
- this.hideEmail = false;
26
- this.hidePrivacyPolicy = true;
27
- this.hideRating = false;
28
- this.hideScreenshotButton = false;
29
- this.modalPosition = 'center';
30
- this.project = '';
31
- this.rating = undefined;
32
- this.ratingMode = 'thumbs';
33
- this.canvasEditorTitle = 'Edit screenshot';
34
- this.canvasEditorCancelText = 'Cancel';
35
- this.canvasEditorSaveText = 'Save';
36
- this.emailPlaceholder = 'Email address (optional)';
37
- this.errorMessage = 'Please try again later.';
38
- this.errorMessage403 = 'The request URL does not match the one defined in PushFeedback for this project.';
39
- this.errorMessage404 = 'We could not find the provided project id in PushFeedback.';
40
- this.footerText = '';
41
- this.messagePlaceholder = 'Comments';
42
- this.modalTitle = 'Share your feedback';
43
- this.modalTitleError = 'Oops!';
44
- this.modalTitleSuccess = 'Thanks for your feedback!';
45
- this.privacyPolicyText = "I have read and expressly consent to the terms of the <a href='https://pushfeedback.com/privacy'>Privacy Policy</a>.";
46
- this.ratingPlaceholder = 'Was this page helpful?';
47
- this.ratingStarsPlaceholder = 'How would you rate this page?';
48
- this.screenshotAttachedText = 'Screenshot attached';
49
- this.screenshotButtonText = 'Add a screenshot';
50
- this.screenshotTakingText = 'Taking screenshot...';
51
- this.screenshotTopbarText = 'Select an element on this page';
52
- this.sendButtonText = 'Send';
53
- this.successMessage = '';
54
- }
55
- componentWillLoad() {
56
- if (!this.sessionId) {
57
- let storedSessionId = localStorage.getItem('pushfeedback_sessionid');
58
- if (!storedSessionId) {
59
- storedSessionId = this.generateRandomSessionId();
60
- localStorage.setItem('pushfeedback_sessionid', storedSessionId);
61
- this.sessionId = storedSessionId;
62
- }
63
- }
64
- else {
65
- localStorage.setItem('pushfeedback_sessionid', this.sessionId);
66
- }
67
- }
68
- componentDidLoad() {
69
- if (this.buttonPosition === 'center-right') {
70
- const buttonContent = this.el.shadowRoot.querySelector('.feedback-button-content');
71
- let adjustement = 0;
72
- if (this.isSafariBrowser()) {
73
- adjustement = 5;
74
- }
75
- buttonContent.style.right = `${((buttonContent.offsetWidth + adjustement) / 2) * -1}px`;
76
- }
77
- if (!this.customFont) {
78
- this.loadInterFont();
79
- }
80
- }
81
- connectedCallback() {
82
- this.feedbackModal = document.createElement('feedback-modal');
83
- const props = [
84
- 'customFont',
85
- 'emailAddress',
86
- 'fetchData',
87
- 'hideEmail',
88
- 'hidePrivacyPolicy',
89
- 'hideRating',
90
- 'hideScreenshotButton',
91
- 'isEmailRequired',
92
- 'modalPosition',
93
- 'project',
94
- 'rating',
95
- 'ratingMode',
96
- 'canvasEditorTitle',
97
- 'canvasEditorCancelText',
98
- 'canvasEditorSaveText',
99
- 'emailPlaceholder',
100
- 'errorMessage',
101
- 'errorMessage403',
102
- 'errorMessage404',
103
- 'footerText',
104
- 'messagePlaceholder',
105
- 'metadata',
106
- 'modalTitle',
107
- 'modalTitleError',
108
- 'modalTitleSuccess',
109
- 'privacyPolicyText',
110
- 'ratingPlaceholder',
111
- 'ratingStarsPlaceholder',
112
- 'screenshotAttachedText',
113
- 'screenshotButtonText',
114
- 'screenshotTakingText',
115
- 'screenshotTopbarText',
116
- 'sendButtonText',
117
- 'successMessage',
118
- ];
119
- props.forEach((prop) => {
120
- this.feedbackModal[prop] = this[prop];
121
- });
122
- document.body.appendChild(this.feedbackModal);
123
- }
124
- disconnectedCallback() {
125
- document.body.removeChild(this.feedbackModal);
126
- }
127
- generateRandomSessionId(length = 16) {
128
- return Math.random().toString(36).substr(2, length);
129
- }
130
- isSafariBrowser() {
131
- const isSafari = /safari/i.test(navigator.userAgent) && !/chrome/i.test(navigator.userAgent);
132
- return isSafari;
133
- }
134
- loadInterFont() {
135
- const link = document.createElement('link');
136
- link.href = 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500&display=swap';
137
- link.rel = 'stylesheet';
138
- document.head.appendChild(link);
139
- }
140
- showModal() {
141
- if (this.submit) {
142
- this.submitRatingFeedback();
143
- }
144
- else {
145
- this.feedbackModal.openModal();
146
- }
147
- }
148
- async submitRatingFeedback() {
149
- try {
150
- const body = {
151
- url: window.location.href,
152
- project: this.project,
153
- rating: this.rating || -1,
154
- ratingMode: this.ratingMode,
155
- message: '',
156
- metadata: this.metadata,
157
- session: localStorage.getItem('pushfeedback_sessionid') || '',
158
- };
159
- const res = await fetch('https://app.pushfeedback.com/api/feedback/', {
160
- method: 'POST',
161
- body: JSON.stringify(body),
162
- headers: {
163
- 'Content-Type': 'application/json',
164
- },
165
- });
166
- if (res.status === 201) {
167
- const feedback_with_id = Object.assign(Object.assign({}, body), { id: await res.json() });
168
- this.feedbackSent.emit({ feedback: feedback_with_id });
169
- }
170
- else {
171
- const errorText = await res.text();
172
- const response = {
173
- status: res.status,
174
- message: errorText,
175
- };
176
- this.feedbackError.emit({ error: response });
177
- }
178
- }
179
- catch (error) {
180
- const response = {
181
- status: 500,
182
- message: error,
183
- };
184
- this.feedbackError.emit({ error: response });
185
- }
186
- }
187
- render() {
188
- return (index.h(index.Host, null, index.h("a", { class: `feedback-button-content feedback-button-content--${this.buttonStyle} feedback-button-content--${this.buttonPosition} ${this.customFont ? 'feedback-button-content--custom-font' : ''} ${this.hideMobile ? 'feedback-button-content--hide-mobile' : ''}`, onClick: () => this.showModal() }, !this.hideIcon && this.buttonStyle != 'default' && (index.h("span", { class: "feedback-button-content-icon" }, index.h("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "#fff", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", class: "icon-edit" }, index.h("path", { d: "M12 20h9" }), index.h("path", { d: "M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z" })))), index.h("slot", null))));
189
- }
190
- get el() { return index.getElement(this); }
191
- };
192
- FeedbackButton.style = feedbackButtonCss;
193
-
194
- const feedbackModalCss = ".text-center{flex-grow:1;text-align:center}.feedback-modal-wrapper *{font-family:var(--feedback-font-family)}.feedback-modal-wrapper--custom-font *{font-family:inherit}.feedback-modal-wrapper{position:absolute;z-index:var(--feedback-modal-modal-wrapper-z-index)}.feedback-overlay{background-color:var(--feedback-modal-screenshot-bg-color);height:100%;left:0;opacity:0;position:fixed;top:0;width:100%;z-index:var(--feedback-modal-screnshot-z-index);transition:opacity 0.2s ease-out}.feedback-overlay--visible{opacity:1}.feedback-modal{display:inline-block;position:relative}.feedback-modal-content{background-color:var(--feedback-modal-content-bg-color);border-color:1px solid var(--feedback-modal-header-text-color);border-radius:var(--feedback-modal-content-border-radius);box-shadow:0px 1px 2px 0px rgba(60, 64, 67, .30), 0px 2px 6px 2px rgba(60, 64, 67, .15);box-sizing:border-box;color:var(--feedback-modal-content-text-color);display:flex;flex-direction:column;left:50%;max-width:90%;padding:20px;position:fixed;top:50%;transform:translate(-50%, -50%) scale(0.95);opacity:0;width:100%;z-index:var(--feedback-modal-content-z-index);transition:transform 0.2s ease-out, opacity 0.2s ease-out}.feedback-modal-content--open{transform:translate(-50%, -50%) scale(1);opacity:1}.feedback-modal-header{align-items:center;color:var(--feedback-modal-header-text-color);display:flex;font-size:var(--feedback-header-font-size);font-weight:var(--feedback-modal-header-font-weight);justify-content:space-between;margin-bottom:20px}.feedback-modal-rating-buttons{width:100%;margin-bottom:20px}.feedback-modal-rating-button{padding:0;background-color:transparent;border:transparent;margin-right:5px;cursor:pointer}.feedback-modal-rating-buttons--thumbs .feedback-modal-rating-button{border:1px solid var(--feedback-modal-button-border-color);border-radius:var(--feedback-modal-button-border-radius);color:var(--feedback-modal-button-text-color);font-size:var(--feedback-modal-button-font-size);font-weight:500;margin-right:10px;justify-content:center;padding:5px 10px}.feedback-modal-rating-buttons--thumbs .feedback-modal-rating-button:hover,.feedback-modal-rating-buttons--thumbs .feedback-modal-rating-button--selected{background-color:var(--feedback-modal-button-bg-color-active);border:1px solid var(--feedback-modal-button-border-color-active);color:var(--feedback-modal-button-text-color-active)}.feedback-modal-rating-buttons--thumbs .feedback-modal-rating-button:hover svg,.feedback-modal-rating-buttons--thumbs .feedback-modal-rating-button--selected svg{stroke:var(--feedback-modal-rating-button-selected-color)}.feedback-modal-rating-buttons svg{stroke:var(--feedback-modal-rating-button-color);cursor:pointer}.feedback-modal-rating-buttons--stars .feedback-modal-rating-button--selected svg{fill:var(--feedback-modal-rating-button-stars-selected-color);stroke:var(--feedback-modal-rating-button-stars-selected-color)}.feedback-modal-text textarea{background-color:var(--feedback-modal-input-bg-color);border:1px solid var(--feedback-modal-input-border-color);border-radius:var(--feedback-modal-input-border-radius);box-sizing:border-box;color:var(--feedback-modal-input-text-color);font-size:var(--feedback-modal-input-font-size);margin-bottom:20px;height:100px;min-height:100px;padding:10px;resize:vertical;width:100%}.feedback-modal-email input{background-color:var(--feedback-modal-input-bg-color);border:1px solid var(--feedback-modal-input-border-color);border-radius:var(--feedback-modal-input-border-radius);box-sizing:border-box;color:var(--feedback-modal-input-text-color);font-size:var(--feedback-modal-input-font-size);margin-bottom:20px;height:40px;padding:10px;width:100%;margin-bottom:20px}.feedback-modal-privacy{font-size:var(--feedback-modal-input-font-size);margin-bottom:20px}.feedback-modal-text textarea:focus,.feedback-modal-email input:focus{border:1px solid var(--feedback-modal-input-border-color-focused);outline:none}.feedback-modal-buttons{display:flex;flex-direction:column}.feedback-modal-buttons .feedback-modal-button{margin-bottom:20px}.feedback-modal-button{align-items:center;background-color:transparent;border:1px solid var(--feedback-modal-button-border-color);border-radius:var(--feedback-modal-button-border-radius);color:var(--feedback-modal-button-text-color);cursor:pointer;display:flex;font-size:var(--feedback-modal-button-font-size);font-weight:500;justify-content:center;min-height:40px;padding:5px 10px}.feedback-modal-button svg{margin-right:6px}.feedback-modal-button path{fill:var(--feedback-modal-button-icon-color)}.feedback-modal-button:hover path,.feedback-modal-button--active path{fill:var(--feedback-modal-button-icon-color-active)}.feedback-modal-button--submit{background-color:var(--feedback-modal-button-submit-bg-color);border:1px solid var(--feedback-modal-button-border-color-active);color:var(--feedback-modal-button-submit-text-color)}.feedback-modal-button:hover,.feedback-modal-button--active{background-color:var(--feedback-modal-button-bg-color-active);border:1px solid var(--feedback-modal-button-border-color-active);color:var(--feedback-modal-button-text-color-active)}.feedback-modal-button--submit:hover{background-color:var(--feedback-modal-button-submit-bg-color-hover);border:1px solid var(--feedback-modal-button-submit-border-color-hover);color:var(--feedback-modal-button-submit-text-color-hover)}.feedback-modal-input-heading{display:block;font-size:14px;font-weight:300;padding-bottom:10px}.feedback-modal-footer{font-size:12px;text-align:center}.feedback-modal-footer a{color:var(--feedback-modal-footer-link);font-weight:500;text-decoration:none}.feedback-logo,.feedback-footer-text{display:block;text-align:center;margin-top:5px}.feedback-footer-text{margin-top:10px;line-height:1.5}.feedback-modal-close{background-color:var(--feedback-modal-close-bg-color);border:0;border-radius:50%;cursor:pointer;height:22px;margin-left:auto;padding:0;width:22px}.feedback-modal-close svg{stroke:var(--feedback-modal-close-color)}.feedback-modal-screenshot{background-color:var(--feedback-modal-screenshot-bg-color);height:100%;left:0;position:fixed;top:0;width:100%;z-index:var(--feedback-modal-screnshot-z-index)}.feedback-modal-screenshot-header{align-items:center;background-color:var(--feedback-modal-screenshot-header-bg-color);border-radius:var(--feedback-modal-content-border-radius);box-shadow:0px 1px 2px 0px rgba(60, 64, 67, .30), 0px 2px 6px 2px rgba(60, 64, 67, .15);box-sizing:border-box;color:var(--feedback-modal-screenshot-header-text-color);cursor:pointer;display:flex;left:50%;top:20px;transform:translateX(-50%);padding:10px;position:fixed;width:max-content;z-index:var(--feedback-modal-screenshot-header-z-index)}.feedback-modal-screenshot-close{height:24px;padding-left:10px;width:24px}.feedback-modal-screenshot-close svg{stroke:var(--feedback-modal-close-color)}.feedback-modal-message{font-size:var(--feedback-modal-message-font-size);margin-top:0}.feedback-modal-element-hover{background-color:transparent;cursor:pointer;border:1px solid var(--feedback-modal-element-hover-border-color)}.feedback-modal-element-selected{background-color:transparent;border:3px solid var(--feedback-modal-element-selected-border-color) !important;box-shadow:0 0 0 2px rgba(0, 123, 255, 0.3) !important}.screenshot-preview{display:inline-block;width:30px;height:30px;overflow:hidden;border-radius:4px;margin-right:10px;box-shadow:0 2px 4px rgba(0, 0, 0, 0.1);cursor:pointer;transition:transform 0.2s ease}.screenshot-preview:hover{transform:scale(1.1)}.screenshot-preview img{width:100%;height:100%;object-fit:cover}.screenshot-loading{display:inline-flex;align-items:center;margin-right:8px}.canvas-editor-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background-color:var(--feedback-modal-screenshot-bg-color);z-index:10001;display:flex;align-items:center;justify-content:center}.canvas-editor-modal{width:95vw;height:98vh;background:var(--feedback-canvas-editor-bg-color);border-radius:var(--feedback-modal-content-border-radius);display:flex;flex-direction:column;overflow:hidden;box-shadow:0px 1px 2px 0px rgba(60, 64, 67, .30), 0px 2px 6px 2px rgba(60, 64, 67, .15)}.canvas-editor-header{background:var(--feedback-canvas-editor-header-bg-color);border-bottom:1px solid var(--feedback-canvas-editor-border-color);padding:12px 16px;display:flex;flex-direction:column;gap:12px;flex-shrink:0}.canvas-editor-title h3{margin:0;font-size:var(--feedback-modal-header-font-size);font-weight:var(--feedback-modal-header-font-weight);color:var(--feedback-modal-header-text-color);font-family:var(--feedback-modal-header-font-family)}.canvas-editor-toolbar{display:flex;align-items:center;gap:20px;flex-wrap:wrap}.toolbar-section{display:flex;align-items:center}.toolbar-section:last-child{margin-left:auto;gap:10px}.tool-group{display:flex;align-items:center;background:var(--feedback-canvas-editor-tool-bg-color);border:1px solid var(--feedback-canvas-editor-border-color);border-radius:var(--feedback-modal-button-border-radius);padding:4px;box-shadow:0px 1px 2px 0px rgba(60, 64, 67, .10)}.tool-btn{display:flex;align-items:center;justify-content:center;width:36px;height:36px;border:none;background:none;border-radius:var(--feedback-modal-button-border-radius);cursor:pointer;color:var(--feedback-canvas-editor-tool-text-color);transition:all 0.2s ease;position:relative}.tool-btn:hover{background:var(--feedback-canvas-editor-tool-bg-hover);color:var(--feedback-modal-button-text-color-active)}.tool-btn.active{background:var(--feedback-canvas-editor-tool-bg-active);color:var(--feedback-canvas-editor-tool-text-active)}.tool-btn:disabled{opacity:0.4;cursor:not-allowed}.tool-btn:disabled:hover{background:none;color:var(--feedback-canvas-editor-tool-text-color)}.toolbar-divider{width:1px;height:20px;background:var(--feedback-canvas-editor-divider-color);margin:0 6px}.undo-btn{background:var(--feedback-canvas-editor-tool-bg-color) !important;border:1px solid var(--feedback-canvas-editor-border-color) !important}.undo-btn:hover:not(:disabled){background:var(--feedback-canvas-editor-tool-bg-hover) !important}.color-palette{display:flex;align-items:center;gap:6px;background:var(--feedback-canvas-editor-tool-bg-color);border:1px solid var(--feedback-canvas-editor-border-color);border-radius:var(--feedback-modal-button-border-radius);padding:6px;box-shadow:0px 1px 2px 0px rgba(60, 64, 67, .10)}.color-slot-wrapper{position:relative}.color-btn{width:28px;height:28px;border-radius:var(--feedback-modal-button-border-radius);border:2px solid transparent;cursor:pointer;transition:all 0.2s ease;position:relative;box-shadow:0px 1px 2px 0px rgba(60, 64, 67, .15);display:flex;align-items:center;justify-content:center}.color-btn:hover{transform:scale(1.05);box-shadow:0px 2px 4px 0px rgba(60, 64, 67, .25)}.color-btn.active{border-color:var(--feedback-primary-color);transform:scale(1.1);box-shadow:0 0 0 2px rgba(0, 112, 244, 0.2)}.color-btn.editing{border-color:var(--feedback-highlight-color);box-shadow:0 0 0 2px rgba(255, 180, 34, 0.3)}.color-btn.editing:hover{border-color:var(--feedback-highlight-color);box-shadow:0 0 0 2px rgba(255, 180, 34, 0.4)}.color-picker-dropdown{position:absolute;top:100%;left:50%;transform:translateX(-50%);margin-top:6px;background:var(--feedback-canvas-editor-tool-bg-color);border:1px solid var(--feedback-canvas-editor-border-color);border-radius:var(--feedback-modal-button-border-radius);padding:6px;box-shadow:0px 1px 2px 0px rgba(60, 64, 67, .30), 0px 2px 6px 2px rgba(60, 64, 67, .15);z-index:1000}.color-picker-dropdown input[type=\"color\"]{width:50px;height:32px;border:none;border-radius:var(--feedback-modal-button-border-radius);cursor:pointer}.size-control{display:flex;align-items:center;gap:10px;background:var(--feedback-canvas-editor-tool-bg-color);border:1px solid var(--feedback-canvas-editor-border-color);border-radius:var(--feedback-modal-button-border-radius);padding:6px 12px;box-shadow:0px 1px 2px 0px rgba(60, 64, 67, .10)}.size-slider{width:70px;height:4px;border-radius:2px;background:var(--feedback-canvas-editor-slider-track);outline:none;-webkit-appearance:none;-moz-appearance:none;appearance:none;cursor:pointer;border:none}.size-slider::-webkit-slider-track{width:100%;height:4px;cursor:pointer;background:var(--feedback-canvas-editor-slider-track);border-radius:2px;border:none;box-shadow:none}.size-slider::-webkit-slider-thumb{-webkit-appearance:none;width:14px;height:14px;border-radius:50%;background:var(--feedback-primary-color);cursor:pointer;box-shadow:0px 1px 2px 0px rgba(60, 64, 67, .20);border:none;margin-top:-5px;}.size-slider::-moz-range-track{width:100%;height:4px;cursor:pointer;background:var(--feedback-canvas-editor-slider-track);border-radius:2px;border:none;box-shadow:none}.size-slider::-moz-range-thumb{width:14px;height:14px;border-radius:50%;background:var(--feedback-primary-color);cursor:pointer;border:none;box-shadow:0px 1px 2px 0px rgba(60, 64, 67, .20)}.size-slider::-moz-range-progress{background:var(--feedback-canvas-editor-slider-track);height:4px;border-radius:2px}.size-slider::-ms-track{width:100%;height:4px;cursor:pointer;background:transparent;border-color:transparent;color:transparent}.size-slider::-ms-fill-lower{background:var(--feedback-canvas-editor-slider-track);border-radius:2px}.size-slider::-ms-fill-upper{background:var(--feedback-canvas-editor-slider-track);border-radius:2px}.size-slider::-ms-thumb{width:14px;height:14px;border-radius:50%;background:var(--feedback-primary-color);cursor:pointer;border:none}.size-value{font-weight:500;color:var(--feedback-canvas-editor-tool-text-color);font-size:var(--feedback-text-font-size);min-width:30px}.action-btn{display:flex;align-items:center;gap:6px;padding:5px 12px;border:1px solid var(--feedback-canvas-editor-action-secondary-border);border-radius:var(--feedback-modal-button-border-radius);cursor:pointer;font-size:var(--feedback-modal-button-font-size);font-weight:500;transition:all 0.2s ease;min-width:65px;justify-content:center;height:36px;font-family:var(--feedback-font-family)}.action-btn.secondary{background:var(--feedback-canvas-editor-action-secondary-bg);color:var(--feedback-canvas-editor-action-secondary-text);border-color:var(--feedback-canvas-editor-action-secondary-border)}.action-btn.secondary:hover{background:var(--feedback-canvas-editor-tool-bg-hover);color:var(--feedback-modal-button-text-color-active);border-color:var(--feedback-modal-button-border-color-active)}.action-btn.primary{background:var(--feedback-canvas-editor-action-primary-bg);color:var(--feedback-canvas-editor-action-primary-text);border-color:var(--feedback-modal-button-border-color-active)}.action-btn.primary:hover{background:var(--feedback-modal-button-submit-bg-color-hover);border-color:var(--feedback-modal-button-submit-border-color-hover)}.canvas-editor-content{flex:1;display:flex;align-items:center;justify-content:center;padding:16px;background:var(--feedback-canvas-editor-content-bg);overflow:hidden;min-height:0;min-width:0}.annotation-canvas{max-width:100%;max-height:100%;width:auto;height:auto;cursor:crosshair;border-radius:var(--feedback-modal-content-border-radius);box-shadow:0px 1px 2px 0px rgba(60, 64, 67, .30), 0px 2px 6px 2px rgba(60, 64, 67, .15);background:var(--feedback-white-color);transition:box-shadow 0.3s ease;object-fit:contain;display:block}.annotation-canvas:hover{box-shadow:0px 2px 4px 0px rgba(60, 64, 67, .35), 0px 4px 12px 4px rgba(60, 64, 67, .20)}@media screen and (min-width: 768px){.feedback-modal-content{max-width:var(--feedback-modal-content-max-width)}.feedback-modal-content.feedback-modal-content--bottom-right{bottom:var(--feedback-modal-content-position-bottom);left:initial;right:var(--feedback-modal-content-position-right);top:initial;transform:initial}.feedback-modal-content.feedback-modal-content--bottom-left{bottom:var(--feedback-modal-content-position-bottom);left:var(--feedback-modal-content-position-left);top:initial;transform:initial}.feedback-modal-content.feedback-modal-content--top-right{right:var(--feedback-modal-content-position-right);top:var(--feedback-modal-content-position-top);transform:initial}.feedback-modal-content.feedback-modal-content--top-left{left:var(--feedback-modal-content-position-left);top:var(--feedback-modal-content-position-top);transform:initial}.feedback-modal-content.feedback-modal-content--center-left{left:5px;right:auto;top:50%;transform:translateY(-50%)}.feedback-modal-content.feedback-modal-content--center-right{left:auto;right:5px;top:50%;transform:translateY(-50%)}.feedback-modal-content.feedback-modal-content--sidebar-left.feedback-modal-content--open,.feedback-modal-content.feedback-modal-content--sidebar-right.feedback-modal-content--open{transform:translateX(0)}.feedback-modal-content.feedback-modal-content--sidebar-left{max-width:var(--feedback-modal-content-sidebar-max-width);left:0;right:auto;height:100vh;top:0;transform:translateX(-100%);transition:transform 0.5s ease-in-out;border-radius:0}.feedback-modal-content.feedback-modal-content--sidebar-right{max-width:var(--feedback-modal-content-sidebar-max-width);left:auto;right:0;height:100vh;top:0;transform:translateX(100%);transition:transform 0.5s ease-in-out;border-radius:0}.feedback-modal-text textarea{height:150px;min-height:150px}.feedback-modal-content.feedback-modal-content--bottom-right{transform:translateY(20px)}.feedback-modal-content.feedback-modal-content--bottom-right.feedback-modal-content--open{transform:translateY(0)}.feedback-modal-content.feedback-modal-content--bottom-left{transform:translateY(20px)}.feedback-modal-content.feedback-modal-content--bottom-left.feedback-modal-content--open{transform:translateY(0)}.feedback-modal-content.feedback-modal-content--top-right{transform:translateY(-20px)}.feedback-modal-content.feedback-modal-content--top-right.feedback-modal-content--open{transform:translateY(0)}.feedback-modal-content.feedback-modal-content--top-left{transform:translateY(-20px)}.feedback-modal-content.feedback-modal-content--top-left.feedback-modal-content--open{transform:translateY(0)}}@media (max-width: 768px){.canvas-editor-modal{width:100vw;height:100vh;border-radius:0}.canvas-editor-header{padding:8px 12px;gap:8px}.canvas-editor-toolbar{flex-direction:column;align-items:stretch;gap:12px}.toolbar-section{justify-content:center}.toolbar-section:last-child{margin-left:0;justify-content:stretch;gap:8px}.action-btn{flex:1;min-width:auto}.canvas-editor-content{padding:8px}.tool-group{flex-wrap:wrap;justify-content:center}.color-palette{flex-wrap:wrap;justify-content:center}.size-control{flex-direction:column;gap:6px;text-align:center}.size-slider{width:100px}}@keyframes feather-spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.feather-loader{animation:feather-spin 1s linear infinite;display:block}.screenshot-error-notification{position:fixed;top:20px;left:50%;transform:translateX(-50%);z-index:10001;max-width:500px;width:90%;animation:slideDown 0.3s ease-out}@keyframes slideDown{from{opacity:0;transform:translateX(-50%) translateY(-20px)}to{opacity:1;transform:translateX(-50%) translateY(0)}}.screenshot-error-content{background:#fee;border:1px solid #fcc;border-radius:8px;padding:12px 16px;display:flex;align-items:center;gap:12px;box-shadow:0 4px 12px rgba(0, 0, 0, 0.15);color:#c53030}.screenshot-error-content svg:first-child{color:#e53e3e;flex-shrink:0}.screenshot-error-content span{flex:1;font-size:14px;line-height:1.4;font-weight:500}.error-close-btn{background:none;border:none;cursor:pointer;padding:4px;border-radius:4px;color:#c53030;flex-shrink:0;transition:background-color 0.2s ease}.error-close-btn:hover{background:rgba(197, 48, 48, 0.1)}@media (max-width: 768px){.canvas-editor-toolbar .tool-group,.canvas-editor-toolbar .color-palette,.canvas-editor-toolbar .size-control,.canvas-editor-toolbar .toolbar-divider{display:none !important}.canvas-editor-toolbar{justify-content:center}.canvas-editor-toolbar .toolbar-section:first-child{display:none}.canvas-editor-toolbar .toolbar-section:nth-child(2){display:none}.canvas-editor-toolbar .toolbar-section:nth-child(3){display:none}.canvas-editor-title{display:none}}";
195
-
196
- const FeedbackModal = class {
197
- constructor(hostRef) {
198
- index.registerInstance(this, hostRef);
199
- this.feedbackSent = index.createEvent(this, "feedbackSent", 7);
200
- this.feedbackError = index.createEvent(this, "feedbackError", 7);
201
- this.onScrollDebounced = () => {
202
- clearTimeout(this.scrollTimeout);
203
- this.scrollTimeout = setTimeout(() => {
204
- document.documentElement.classList.remove('feedback-modal-screenshot-closing');
205
- document.documentElement.style.top = '';
206
- window.removeEventListener('scroll', this.onScrollDebounced);
207
- }, 200);
208
- };
209
- this.handleSubmit = async (event) => {
210
- event.preventDefault();
211
- if (this.isEmailRequired && !this.formEmail) {
212
- return;
213
- }
214
- this.resetOverflow();
215
- this.showScreenshotMode = false;
216
- this.showScreenshotTopBar = false;
217
- this.showModal = false;
218
- this.sending = true;
219
- try {
220
- const body = {
221
- url: window.location.href,
222
- message: this.formMessage,
223
- email: this.formEmail,
224
- project: this.project,
225
- screenshot: this.encodedScreenshot,
226
- rating: this.selectedRating,
227
- ratingMode: this.ratingMode,
228
- metadata: this.metadata,
229
- verification: this.formVerification,
230
- session: localStorage.getItem('pushfeedback_sessionid') || '',
231
- };
232
- const res = await fetch('https://app.pushfeedback.com/api/feedback/', {
233
- method: 'POST',
234
- body: JSON.stringify(body),
235
- headers: {
236
- 'Content-Type': 'application/json',
237
- },
238
- });
239
- if (res.status === 201) {
240
- const feedback_with_id = Object.assign(Object.assign({}, body), { id: await res.json() });
241
- this.feedbackSent.emit({ feedback: feedback_with_id });
242
- this.formSuccess = true;
243
- this.formError = false;
244
- }
245
- else {
246
- const errorText = await res.text();
247
- const response = {
248
- status: res.status,
249
- message: errorText,
250
- };
251
- this.feedbackError.emit({ error: response });
252
- this.formSuccess = false;
253
- this.formError = true;
254
- this.formErrorStatus = res.status;
255
- }
256
- }
257
- catch (error) {
258
- const response = {
259
- status: 500,
260
- message: error,
261
- };
262
- this.feedbackError.emit({ error: response });
263
- this.formSuccess = false;
264
- this.formError = true;
265
- this.formErrorStatus = 500;
266
- }
267
- finally {
268
- this.sending = false;
269
- this.showModal = true;
270
- }
271
- };
272
- this.close = () => {
273
- this.isAnimating = false;
274
- setTimeout(() => {
275
- this.sending = false;
276
- this.showModal = false;
277
- this.showScreenshotMode = false;
278
- this.showScreenshotTopBar = false;
279
- this.hasSelectedElement = false;
280
- this.encodedScreenshot = null;
281
- // Remove highlight from ALL selected elements
282
- document.querySelectorAll('.feedback-modal-element-selected').forEach(el => {
283
- el.classList.remove('feedback-modal-element-selected');
284
- });
285
- // Reset canvas editor states
286
- this.takingScreenshot = false;
287
- this.showPreviewModal = false;
288
- this.showCanvasEditor = false;
289
- this.annotations = [];
290
- this.currentAnnotation = null;
291
- this.isDrawing = false;
292
- this.canvasRef = null;
293
- this.canvasContext = null;
294
- this.originalImageData = null;
295
- // Reset error states
296
- this.showScreenshotError = false;
297
- this.screenshotError = '';
298
- // Reset resizing states
299
- this.isResizing = false;
300
- this.resizingAnnotation = null;
301
- this.resizeStartSize = 16;
302
- this.resizeStartDimensions = null;
303
- this.hoveredAnnotation = null;
304
- this.resizeHandle = false;
305
- // Reset form states
306
- this.formSuccess = false;
307
- this.formError = false;
308
- this.formErrorStatus = 500;
309
- this.formMessage = '';
310
- this.formEmail = '';
311
- this.resetOverflow();
312
- }, 200);
313
- };
314
- this.openScreenShot = async () => {
315
- // Show loading state immediately
316
- this.takingScreenshot = true;
317
- this.showScreenshotError = false;
318
- // Clear any previous annotations when taking a new screenshot
319
- this.annotations = [];
320
- this.currentAnnotation = null;
321
- this.isDrawing = false;
322
- this.hoveredAnnotation = null;
323
- // Hide the feedback modal temporarily to exclude it from screenshot
324
- const wasModalVisible = this.showModal;
325
- this.showModal = false;
326
- // Also hide any feedback buttons on the page
327
- this.hideAllFeedbackElements();
328
- try {
329
- // Wait a moment for UI to update before capturing
330
- await new Promise(resolve => setTimeout(resolve, 100));
331
- // Capture viewport screenshot using browser API
332
- const dataUrl = await this.captureViewportScreenshot();
333
- this.encodedScreenshot = dataUrl;
334
- this.originalImageData = dataUrl;
335
- // Reset loading state
336
- this.takingScreenshot = false;
337
- // Go directly to canvas editor (don't restore modal)
338
- this.showCanvasEditor = true;
339
- // Restore feedback elements visibility
340
- this.showAllFeedbackElements();
341
- // Initialize canvas after a short delay to ensure DOM is ready
342
- setTimeout(() => {
343
- this.initializeCanvas();
344
- }, 100);
345
- }
346
- catch (error) {
347
- console.error('Failed to capture screenshot:', error);
348
- // Reset loading state on error
349
- this.takingScreenshot = false;
350
- // Restore modal and feedback elements on error
351
- this.showModal = wasModalVisible;
352
- this.showAllFeedbackElements();
353
- // Show error message to user
354
- this.handleScreenshotError(error);
355
- }
356
- };
357
- this.hideAllFeedbackElements = () => {
358
- // Hide all feedback buttons and modals on the page
359
- const feedbackElements = document.querySelectorAll('feedback-button, feedback-modal');
360
- feedbackElements.forEach(element => {
361
- element.style.visibility = 'hidden';
362
- });
363
- };
364
- this.showAllFeedbackElements = () => {
365
- // Show all feedback buttons and modals on the page
366
- const feedbackElements = document.querySelectorAll('feedback-button, feedback-modal');
367
- feedbackElements.forEach(element => {
368
- element.style.visibility = 'visible';
369
- });
370
- };
371
- this.handleScreenshotError = (error) => {
372
- let errorMessage = 'Failed to capture screenshot. ';
373
- if (error.name === 'NotAllowedError') {
374
- errorMessage += 'Permission denied. Please allow screen sharing to take screenshots.';
375
- }
376
- else if (error.name === 'NotSupportedError') {
377
- errorMessage += 'Screen capture is not supported in this browser.';
378
- }
379
- else if (error.name === 'NotFoundError') {
380
- errorMessage += 'No screen sources available for capture.';
381
- }
382
- else if (error.name === 'AbortError') {
383
- errorMessage += 'Screenshot capture was cancelled.';
384
- }
385
- else if (error.message && error.message.includes('not supported')) {
386
- errorMessage += 'Your browser does not support screen capture. Please use a browser like Chrome, Firefox, or Safari.';
387
- }
388
- else {
389
- errorMessage += 'An unexpected error occurred. Please try again.';
390
- }
391
- this.screenshotError = errorMessage;
392
- this.showScreenshotError = true;
393
- // Auto-hide error after 5 seconds
394
- setTimeout(() => {
395
- this.showScreenshotError = false;
396
- }, 5000);
397
- };
398
- this.openCanvasEditor = (event) => {
399
- if (event) {
400
- event.stopPropagation();
401
- }
402
- this.showModal = false;
403
- this.showCanvasEditor = true;
404
- // Initialize canvas after a short delay to ensure DOM is ready
405
- setTimeout(() => {
406
- this.initializeCanvas();
407
- }, 100);
408
- };
409
- this.closeCanvasEditor = () => {
410
- this.showCanvasEditor = false;
411
- this.showModal = true;
412
- };
413
- this.saveAnnotations = () => {
414
- if (this.canvasRef) {
415
- // Create final image with annotations
416
- const finalDataUrl = this.canvasRef.toDataURL('image/png');
417
- this.encodedScreenshot = finalDataUrl;
418
- }
419
- this.showCanvasEditor = false;
420
- this.showModal = true;
421
- };
422
- this.initializeCanvas = () => {
423
- if (!this.canvasRef || !this.originalImageData)
424
- return;
425
- this.canvasContext = this.canvasRef.getContext('2d');
426
- const img = new Image();
427
- img.onload = () => {
428
- // Set canvas to original image dimensions
429
- this.canvasRef.width = img.width;
430
- this.canvasRef.height = img.height;
431
- // Get available container dimensions
432
- const containerWidth = this.canvasRef.parentElement.clientWidth - 32; // Account for reduced padding (16px * 2)
433
- const containerHeight = this.canvasRef.parentElement.clientHeight - 32;
434
- // Calculate scale factors for both dimensions
435
- const scaleX = containerWidth / img.width;
436
- const scaleY = containerHeight / img.height;
437
- // Use the smaller scale to ensure complete image fits
438
- const scale = Math.min(scaleX, scaleY, 1); // Never scale up, only down
439
- // Calculate final display dimensions
440
- const displayWidth = img.width * scale;
441
- const displayHeight = img.height * scale;
442
- // Set CSS size for display (this scales the canvas visually)
443
- this.canvasRef.style.width = `${displayWidth}px`;
444
- this.canvasRef.style.height = `${displayHeight}px`;
445
- console.log('Canvas initialized with complete image fit:', {
446
- originalWidth: img.width,
447
- originalHeight: img.height,
448
- displayWidth,
449
- displayHeight,
450
- scale,
451
- scaleX,
452
- scaleY,
453
- containerWidth,
454
- containerHeight,
455
- usingScale: scale === scaleX ? 'width-limited' : 'height-limited'
456
- });
457
- // Draw the original image at full resolution
458
- this.canvasContext.drawImage(img, 0, 0);
459
- // Redraw existing annotations
460
- this.redrawAnnotations();
461
- };
462
- img.src = this.originalImageData;
463
- };
464
- this.redrawAnnotations = () => {
465
- if (!this.canvasContext)
466
- return;
467
- // Clear and redraw background image
468
- const img = new Image();
469
- img.onload = () => {
470
- this.canvasContext.clearRect(0, 0, this.canvasRef.width, this.canvasRef.height);
471
- this.canvasContext.drawImage(img, 0, 0);
472
- // Draw all annotations
473
- this.annotations.forEach(annotation => {
474
- this.drawAnnotation(annotation);
475
- });
476
- };
477
- img.src = this.originalImageData;
478
- };
479
- this.drawAnnotation = (annotation) => {
480
- if (!this.canvasContext)
481
- return;
482
- this.canvasContext.strokeStyle = annotation.color;
483
- this.canvasContext.lineWidth = annotation.lineWidth;
484
- this.canvasContext.lineCap = 'round';
485
- this.canvasContext.lineJoin = 'round';
486
- switch (annotation.type) {
487
- case 'rectangle':
488
- this.canvasContext.strokeRect(annotation.startX, annotation.startY, annotation.width, annotation.height);
489
- // Draw resize handle if this annotation is hovered
490
- if (this.hoveredAnnotation === annotation) {
491
- this.drawRectangleResizeHandles(annotation);
492
- }
493
- break;
494
- case 'line':
495
- this.canvasContext.beginPath();
496
- this.canvasContext.moveTo(annotation.startX, annotation.startY);
497
- this.canvasContext.lineTo(annotation.endX, annotation.endY);
498
- this.canvasContext.stroke();
499
- // Draw resize handles if this annotation is hovered
500
- if (this.hoveredAnnotation === annotation) {
501
- this.drawLineResizeHandles(annotation);
502
- }
503
- break;
504
- case 'arrow':
505
- this.drawArrow(annotation.startX, annotation.startY, annotation.endX, annotation.endY);
506
- // Draw resize handles if this annotation is hovered
507
- if (this.hoveredAnnotation === annotation) {
508
- this.drawLineResizeHandles(annotation); // Same as line
509
- }
510
- break;
511
- case 'text':
512
- const fontSize = annotation.fontSize || 16;
513
- this.canvasContext.fillStyle = annotation.color;
514
- this.canvasContext.font = `${fontSize}px Arial`;
515
- this.canvasContext.fillText(annotation.text, annotation.x, annotation.y);
516
- // Draw resize handle if this annotation is hovered
517
- if (this.hoveredAnnotation === annotation) {
518
- this.drawTextResizeHandle(annotation);
519
- }
520
- break;
521
- }
522
- };
523
- // Draw resize handle for text annotation
524
- this.drawTextResizeHandle = (annotation) => {
525
- if (!this.canvasContext || annotation.type !== 'text')
526
- return;
527
- const fontSize = annotation.fontSize || 16;
528
- const textWidth = this.getTextWidth(annotation.text, fontSize);
529
- const handleSize = 8;
530
- const handleX = annotation.x + textWidth;
531
- const handleY = annotation.y;
532
- // Draw resize handle (small square) - using widget primary color
533
- this.canvasContext.fillStyle = '#0070F4'; // var(--feedback-primary-color)
534
- this.canvasContext.strokeStyle = '#ffffff';
535
- this.canvasContext.lineWidth = 2;
536
- this.canvasContext.fillRect(handleX - handleSize / 2, handleY - handleSize / 2, handleSize, handleSize);
537
- this.canvasContext.strokeRect(handleX - handleSize / 2, handleY - handleSize / 2, handleSize, handleSize);
538
- };
539
- this.drawArrow = (fromX, fromY, toX, toY) => {
540
- const headlen = 15; // Arrow head length
541
- const angle = Math.atan2(toY - fromY, toX - fromX);
542
- // Draw line
543
- this.canvasContext.beginPath();
544
- this.canvasContext.moveTo(fromX, fromY);
545
- this.canvasContext.lineTo(toX, toY);
546
- this.canvasContext.stroke();
547
- // Draw arrow head
548
- this.canvasContext.beginPath();
549
- this.canvasContext.moveTo(toX, toY);
550
- this.canvasContext.lineTo(toX - headlen * Math.cos(angle - Math.PI / 6), toY - headlen * Math.sin(angle - Math.PI / 6));
551
- this.canvasContext.moveTo(toX, toY);
552
- this.canvasContext.lineTo(toX - headlen * Math.cos(angle + Math.PI / 6), toY - headlen * Math.sin(angle + Math.PI / 6));
553
- this.canvasContext.stroke();
554
- };
555
- this.undoLastAnnotation = () => {
556
- this.annotations = this.annotations.slice(0, -1);
557
- this.redrawAnnotations();
558
- };
559
- // Handle color slot editing
560
- this.handleColorSlotClick = (colorIndex) => {
561
- if (this.editingColorIndex === colorIndex) {
562
- // If already editing this slot, just select the color
563
- this.canvasDrawingColor = this.defaultColors[colorIndex];
564
- this.showColorPicker = false;
565
- this.editingColorIndex = -1;
566
- }
567
- else {
568
- // Start editing this color slot
569
- this.editingColorIndex = colorIndex;
570
- this.showColorPicker = true;
571
- this.canvasDrawingColor = this.defaultColors[colorIndex];
572
- }
573
- };
574
- // Update color in slot
575
- this.updateColorSlot = (newColor) => {
576
- if (this.editingColorIndex >= 0 && this.editingColorIndex < this.defaultColors.length) {
577
- this.defaultColors[this.editingColorIndex] = newColor;
578
- this.canvasDrawingColor = newColor;
579
- this.showColorPicker = false;
580
- this.editingColorIndex = -1;
581
- // Force reactivity
582
- this.defaultColors = [...this.defaultColors];
583
- }
584
- };
585
- // Handle color picker input without closing
586
- this.handleColorPickerInput = (event) => {
587
- event.stopPropagation();
588
- const newColor = event.target.value;
589
- if (this.editingColorIndex >= 0 && this.editingColorIndex < this.defaultColors.length) {
590
- this.defaultColors[this.editingColorIndex] = newColor;
591
- this.canvasDrawingColor = newColor;
592
- // Force reactivity
593
- this.defaultColors = [...this.defaultColors];
594
- }
595
- };
596
- // Handle color picker click to prevent closing
597
- this.handleColorPickerClick = (event) => {
598
- event.stopPropagation();
599
- };
600
- // Close color picker
601
- this.closeColorPicker = () => {
602
- this.showColorPicker = false;
603
- this.editingColorIndex = -1;
604
- };
605
- // Check if point is in resize handle for any annotation type
606
- this.isPointInResizeHandle = (x, y, annotation) => {
607
- const handleSize = 8;
608
- switch (annotation.type) {
609
- case 'text':
610
- const textWidth = this.getTextWidth(annotation.text, annotation.fontSize || 16);
611
- const handleX = annotation.x + textWidth;
612
- const handleY = annotation.y;
613
- return x >= handleX - handleSize / 2 && x <= handleX + handleSize / 2 &&
614
- y >= handleY - handleSize / 2 && y <= handleY + handleSize / 2;
615
- case 'rectangle':
616
- const right = annotation.startX + annotation.width;
617
- const bottom = annotation.startY + annotation.height;
618
- // Only check bottom-right corner handle
619
- return x >= right - handleSize / 2 && x <= right + handleSize / 2 &&
620
- y >= bottom - handleSize / 2 && y <= bottom + handleSize / 2;
621
- case 'line':
622
- case 'arrow':
623
- // Check both endpoint handles
624
- const lineHandles = [
625
- { x: annotation.startX, y: annotation.startY, point: 'start' },
626
- { x: annotation.endX, y: annotation.endY, point: 'end' }
627
- ];
628
- for (const handle of lineHandles) {
629
- if (x >= handle.x - handleSize / 2 && x <= handle.x + handleSize / 2 &&
630
- y >= handle.y - handleSize / 2 && y <= handle.y + handleSize / 2) {
631
- return handle.point; // Return which endpoint was clicked
632
- }
633
- }
634
- return false;
635
- default:
636
- return false;
637
- }
638
- };
639
- // Get text width for resize handle positioning
640
- this.getTextWidth = (text, fontSize) => {
641
- // Approximate text width calculation
642
- return text.length * fontSize * 0.6;
643
- };
644
- // Start text resize
645
- this.startTextResize = (annotation, startPos) => {
646
- this.isResizing = true;
647
- this.resizingAnnotation = annotation;
648
- this.resizeStartSize = annotation.fontSize || 16;
649
- this.dragStartPos = startPos;
650
- };
651
- // Handle text resize
652
- this.handleTextResize = (currentPos) => {
653
- if (!this.resizingAnnotation || !this.dragStartPos)
654
- return;
655
- const deltaX = currentPos.x - this.dragStartPos.x;
656
- const deltaY = currentPos.y - this.dragStartPos.y;
657
- const avgDelta = (deltaX + deltaY) / 2;
658
- // Calculate new font size (minimum 8px, maximum 72px)
659
- const newSize = Math.max(8, Math.min(72, this.resizeStartSize + avgDelta * 0.5));
660
- // Update annotation font size
661
- const index = this.annotations.findIndex(a => a === this.resizingAnnotation);
662
- if (index !== -1) {
663
- this.annotations[index] = Object.assign(Object.assign({}, this.resizingAnnotation), { fontSize: Math.round(newSize) });
664
- this.resizingAnnotation = this.annotations[index];
665
- }
666
- this.redrawAnnotations();
667
- };
668
- // Start resize for any annotation type
669
- this.startResize = (annotation, handle, startPos) => {
670
- this.isResizing = true;
671
- this.resizingAnnotation = annotation;
672
- this.resizeHandle = handle;
673
- this.dragStartPos = startPos;
674
- // Store original values for different annotation types
675
- if (annotation.type === 'text') {
676
- this.resizeStartSize = annotation.fontSize || 16;
677
- }
678
- else if (annotation.type === 'rectangle') {
679
- this.resizeStartDimensions = { width: annotation.width, height: annotation.height };
680
- }
681
- };
682
- // Enhanced mouse down handler with resize detection for all annotation types
683
- this.handleCanvasMouseDown = (event) => {
684
- if (!this.canvasRef)
685
- return;
686
- // Disable drawing on mobile devices
687
- if (window.innerWidth <= 768)
688
- return;
689
- // Close color picker if open
690
- if (this.showColorPicker) {
691
- this.closeColorPicker();
692
- }
693
- const coords = this.getCanvasCoordinates(event);
694
- // Check if clicking on existing annotation first
695
- const found = this.findAnnotationAt(coords.x, coords.y);
696
- if (found) {
697
- // Check if clicking on resize handle for any annotation type
698
- const handle = this.isPointInResizeHandle(coords.x, coords.y, found.annotation);
699
- if (handle) {
700
- this.startResize(found.annotation, handle, coords);
701
- this.canvasRef.style.cursor = 'nw-resize';
702
- return;
703
- }
704
- // Start dragging existing annotation
705
- if (!this.isDrawing) {
706
- this.isDragging = true;
707
- this.draggedAnnotation = found.annotation;
708
- this.dragStartPos = coords;
709
- this.canvasRef.style.cursor = 'grabbing';
710
- return;
711
- }
712
- }
713
- // Original drawing logic
714
- this.isDrawing = true;
715
- if (this.canvasDrawingTool === 'text') {
716
- const text = prompt('Enter text:');
717
- if (text) {
718
- const annotation = {
719
- type: 'text',
720
- x: coords.x,
721
- y: coords.y,
722
- text,
723
- color: this.canvasDrawingColor,
724
- fontSize: 16
725
- };
726
- this.annotations = [...this.annotations, annotation];
727
- this.redrawAnnotations();
728
- }
729
- this.isDrawing = false;
730
- }
731
- else {
732
- this.currentAnnotation = {
733
- type: this.canvasDrawingTool,
734
- startX: coords.x,
735
- startY: coords.y,
736
- color: this.canvasDrawingColor,
737
- lineWidth: this.canvasLineWidth
738
- };
739
- }
740
- };
741
- this.handleCanvasMouseMove = (event) => {
742
- if (!this.canvasRef)
743
- return;
744
- // Disable drawing on mobile devices
745
- if (window.innerWidth <= 768)
746
- return;
747
- const coords = this.getCanvasCoordinates(event);
748
- // Handle resizing for any annotation type
749
- if (this.isResizing && this.resizingAnnotation) {
750
- this.handleResize(coords);
751
- return;
752
- }
753
- // Handle dragging existing annotation
754
- if (this.isDragging && this.draggedAnnotation && this.dragStartPos) {
755
- const deltaX = coords.x - this.dragStartPos.x;
756
- const deltaY = coords.y - this.dragStartPos.y;
757
- // Update annotation position
758
- const updatedAnnotation = Object.assign({}, this.draggedAnnotation);
759
- switch (updatedAnnotation.type) {
760
- case 'rectangle':
761
- updatedAnnotation.startX += deltaX;
762
- updatedAnnotation.startY += deltaY;
763
- break;
764
- case 'line':
765
- case 'arrow':
766
- updatedAnnotation.startX += deltaX;
767
- updatedAnnotation.startY += deltaY;
768
- updatedAnnotation.endX += deltaX;
769
- updatedAnnotation.endY += deltaY;
770
- break;
771
- case 'text':
772
- updatedAnnotation.x += deltaX;
773
- updatedAnnotation.y += deltaY;
774
- break;
775
- }
776
- // Update annotation in array
777
- const index = this.annotations.findIndex(a => a === this.draggedAnnotation);
778
- if (index !== -1) {
779
- this.annotations[index] = updatedAnnotation;
780
- this.draggedAnnotation = updatedAnnotation;
781
- }
782
- this.dragStartPos = coords;
783
- this.redrawAnnotations();
784
- return;
785
- }
786
- // Handle drawing new annotation
787
- if (this.isDrawing && this.currentAnnotation) {
788
- if (this.canvasDrawingTool === 'rectangle') {
789
- this.currentAnnotation.width = coords.x - this.currentAnnotation.startX;
790
- this.currentAnnotation.height = coords.y - this.currentAnnotation.startY;
791
- }
792
- else {
793
- this.currentAnnotation.endX = coords.x;
794
- this.currentAnnotation.endY = coords.y;
795
- }
796
- this.redrawAnnotations();
797
- this.drawAnnotation(this.currentAnnotation);
798
- return;
799
- }
800
- // Handle hover states and cursor changes
801
- const found = this.findAnnotationAt(coords.x, coords.y);
802
- if (found) {
803
- // Check if hovering over resize handle for any annotation type
804
- const handle = this.isPointInResizeHandle(coords.x, coords.y, found.annotation);
805
- if (handle) {
806
- this.canvasRef.style.cursor = 'nw-resize';
807
- this.hoveredAnnotation = found.annotation;
808
- this.redrawAnnotations();
809
- return;
810
- }
811
- // Regular hover over annotation
812
- this.canvasRef.style.cursor = 'grab';
813
- if (this.hoveredAnnotation !== found.annotation) {
814
- this.hoveredAnnotation = found.annotation;
815
- this.redrawAnnotations();
816
- }
817
- }
818
- else {
819
- // No annotation under cursor
820
- this.canvasRef.style.cursor = 'crosshair';
821
- if (this.hoveredAnnotation) {
822
- this.hoveredAnnotation = null;
823
- this.redrawAnnotations();
824
- }
825
- }
826
- };
827
- this.handleCanvasMouseUp = () => {
828
- // Disable drawing on mobile devices
829
- if (window.innerWidth <= 768)
830
- return;
831
- // Handle end of resizing
832
- if (this.isResizing) {
833
- this.isResizing = false;
834
- this.resizingAnnotation = null;
835
- this.dragStartPos = null;
836
- this.resizeHandle = false;
837
- this.resizeStartDimensions = null;
838
- if (this.canvasRef) {
839
- this.canvasRef.style.cursor = 'crosshair';
840
- }
841
- return;
842
- }
843
- // Handle end of dragging
844
- if (this.isDragging) {
845
- this.isDragging = false;
846
- this.draggedAnnotation = null;
847
- this.dragStartPos = null;
848
- if (this.canvasRef) {
849
- this.canvasRef.style.cursor = 'crosshair';
850
- }
851
- return;
852
- }
853
- // Handle end of drawing
854
- if (!this.isDrawing || !this.currentAnnotation)
855
- return;
856
- this.isDrawing = false;
857
- this.annotations = [...this.annotations, this.currentAnnotation];
858
- this.currentAnnotation = null;
859
- this.redrawAnnotations();
860
- };
861
- // Draw resize handles for rectangle annotation (only bottom-right corner)
862
- this.drawRectangleResizeHandles = (annotation) => {
863
- if (!this.canvasContext || annotation.type !== 'rectangle')
864
- return;
865
- const handleSize = 8;
866
- const right = annotation.startX + annotation.width;
867
- const bottom = annotation.startY + annotation.height;
868
- // Only draw bottom-right corner handle
869
- const handle = { x: right, y: bottom };
870
- // Draw the handle
871
- this.canvasContext.fillStyle = '#0070F4'; // Primary color
872
- this.canvasContext.strokeStyle = '#ffffff';
873
- this.canvasContext.lineWidth = 2;
874
- this.canvasContext.fillRect(handle.x - handleSize / 2, handle.y - handleSize / 2, handleSize, handleSize);
875
- this.canvasContext.strokeRect(handle.x - handleSize / 2, handle.y - handleSize / 2, handleSize, handleSize);
876
- };
877
- // Draw resize handles for line/arrow annotation
878
- this.drawLineResizeHandles = (annotation) => {
879
- if (!this.canvasContext || (annotation.type !== 'line' && annotation.type !== 'arrow'))
880
- return;
881
- const handleSize = 8;
882
- // Define handle positions (2 endpoints)
883
- const handles = [
884
- { x: annotation.startX, y: annotation.startY },
885
- { x: annotation.endX, y: annotation.endY } // End point
886
- ];
887
- // Draw each handle
888
- this.canvasContext.fillStyle = '#0070F4'; // Primary color
889
- this.canvasContext.strokeStyle = '#ffffff';
890
- this.canvasContext.lineWidth = 2;
891
- handles.forEach(handle => {
892
- this.canvasContext.fillRect(handle.x - handleSize / 2, handle.y - handleSize / 2, handleSize, handleSize);
893
- this.canvasContext.strokeRect(handle.x - handleSize / 2, handle.y - handleSize / 2, handleSize, handleSize);
894
- });
895
- };
896
- // Convert screen coordinates to canvas coordinates
897
- this.getCanvasCoordinates = (event) => {
898
- if (!this.canvasRef)
899
- return { x: 0, y: 0 };
900
- const rect = this.canvasRef.getBoundingClientRect();
901
- // Calculate the scale factor between display size and actual canvas size
902
- const scaleX = this.canvasRef.width / rect.width;
903
- const scaleY = this.canvasRef.height / rect.height;
904
- const x = (event.clientX - rect.left) * scaleX;
905
- const y = (event.clientY - rect.top) * scaleY;
906
- return { x, y };
907
- };
908
- // Find annotation under mouse cursor
909
- this.findAnnotationAt = (x, y) => {
910
- // Check in reverse order (top to bottom)
911
- for (let i = this.annotations.length - 1; i >= 0; i--) {
912
- const annotation = this.annotations[i];
913
- if (this.isPointInAnnotation(x, y, annotation)) {
914
- return { annotation, index: i };
915
- }
916
- }
917
- return null;
918
- };
919
- // Check if point is within annotation bounds
920
- this.isPointInAnnotation = (x, y, annotation) => {
921
- const tolerance = 10; // Click tolerance
922
- switch (annotation.type) {
923
- case 'rectangle':
924
- const left = Math.min(annotation.startX, annotation.startX + annotation.width);
925
- const right = Math.max(annotation.startX, annotation.startX + annotation.width);
926
- const top = Math.min(annotation.startY, annotation.startY + annotation.height);
927
- const bottom = Math.max(annotation.startY, annotation.startY + annotation.height);
928
- return x >= left - tolerance && x <= right + tolerance &&
929
- y >= top - tolerance && y <= bottom + tolerance;
930
- case 'line':
931
- case 'arrow':
932
- // Distance from point to line
933
- const A = annotation.endY - annotation.startY;
934
- const B = annotation.startX - annotation.endX;
935
- const C = annotation.endX * annotation.startY - annotation.startX * annotation.endY;
936
- const distance = Math.abs(A * x + B * y + C) / Math.sqrt(A * A + B * B);
937
- return distance <= tolerance;
938
- case 'text':
939
- // Simple bounding box for text
940
- return x >= annotation.x - tolerance && x <= annotation.x + 100 &&
941
- y >= annotation.y - 20 && y <= annotation.y + tolerance;
942
- default:
943
- return false;
944
- }
945
- };
946
- // Handle resize for different annotation types
947
- this.handleResize = (currentPos) => {
948
- if (!this.resizingAnnotation || !this.dragStartPos)
949
- return;
950
- const annotation = this.resizingAnnotation;
951
- const index = this.annotations.findIndex(a => a === annotation);
952
- if (index === -1)
953
- return;
954
- let updatedAnnotation = Object.assign({}, annotation);
955
- switch (annotation.type) {
956
- case 'text':
957
- // Text resize logic (existing)
958
- const deltaX = currentPos.x - this.dragStartPos.x;
959
- const deltaY = currentPos.y - this.dragStartPos.y;
960
- const avgDelta = (deltaX + deltaY) / 2;
961
- const newSize = Math.max(8, Math.min(72, this.resizeStartSize + avgDelta * 0.5));
962
- updatedAnnotation.fontSize = Math.round(newSize);
963
- break;
964
- case 'rectangle':
965
- // Rectangle resize logic - only bottom-right corner
966
- const rectDeltaX = currentPos.x - this.dragStartPos.x;
967
- const rectDeltaY = currentPos.y - this.dragStartPos.y;
968
- // Update width and height based on original dimensions plus delta
969
- updatedAnnotation.width = Math.max(10, this.resizeStartDimensions.width + rectDeltaX); // Minimum width of 10px
970
- updatedAnnotation.height = Math.max(10, this.resizeStartDimensions.height + rectDeltaY); // Minimum height of 10px
971
- break;
972
- case 'line':
973
- case 'arrow':
974
- // Line/arrow resize logic - move endpoints
975
- if (this.resizeHandle === 'start') {
976
- updatedAnnotation.startX = currentPos.x;
977
- updatedAnnotation.startY = currentPos.y;
978
- }
979
- else if (this.resizeHandle === 'end') {
980
- updatedAnnotation.endX = currentPos.x;
981
- updatedAnnotation.endY = currentPos.y;
982
- }
983
- break;
984
- }
985
- // Update annotation in array
986
- this.annotations[index] = updatedAnnotation;
987
- this.resizingAnnotation = updatedAnnotation;
988
- this.redrawAnnotations();
989
- };
990
- this.sending = false;
991
- this.formMessage = '';
992
- this.formEmail = '';
993
- this.formSuccess = false;
994
- this.formVerification = '';
995
- this.formError = false;
996
- this.formErrorStatus = 500;
997
- this.encodedScreenshot = undefined;
998
- this.isPrivacyChecked = false;
999
- this.whitelabel = false;
1000
- this.selectedRating = -1;
1001
- this.overlayVisible = false;
1002
- this.isAnimating = false;
1003
- this.takingScreenshot = false;
1004
- this.showPreviewModal = false;
1005
- this.screenshotError = '';
1006
- this.showScreenshotError = false;
1007
- this.showCanvasEditor = false;
1008
- this.canvasDrawingTool = 'rectangle';
1009
- this.canvasDrawingColor = '#ff0000';
1010
- this.canvasLineWidth = 3;
1011
- this.isDrawing = false;
1012
- this.annotations = [];
1013
- this.currentAnnotation = null;
1014
- this.isDragging = false;
1015
- this.draggedAnnotation = null;
1016
- this.dragStartPos = null;
1017
- this.showColorPicker = false;
1018
- this.editingColorIndex = -1;
1019
- this.isResizing = false;
1020
- this.resizingAnnotation = null;
1021
- this.resizeStartSize = 16;
1022
- this.resizeStartDimensions = null;
1023
- this.hoveredAnnotation = null;
1024
- this.resizeHandle = false;
1025
- this.defaultColors = ['#ff0000', '#00ff00', '#0000ff', '#000000'];
1026
- this.customFont = false;
1027
- this.emailAddress = '';
1028
- this.hideEmail = false;
1029
- this.isEmailRequired = false;
1030
- this.ratingMode = 'thumbs';
1031
- this.hasSelectedElement = false;
1032
- this.hidePrivacyPolicy = true;
1033
- this.hideRating = false;
1034
- this.hideScreenshotButton = false;
1035
- this.project = '';
1036
- this.showScreenshotMode = false;
1037
- this.showScreenshotTopBar = false;
1038
- this.showModal = false;
1039
- this.rating = undefined;
1040
- this.metadata = undefined;
1041
- this.fetchData = true;
1042
- this.emailPlaceholder = 'Email address (optional)';
1043
- this.errorMessage = 'Please try again later.';
1044
- this.errorMessage403 = 'The request URL does not match the one defined in PushFeedback for this project.';
1045
- this.errorMessage404 = 'We could not find the provided project ID in PushFeedback.';
1046
- this.messagePlaceholder = 'Comments';
1047
- this.footerText = '';
1048
- this.modalPosition = 'center';
1049
- this.modalTitle = 'Share your feedback';
1050
- this.modalTitleError = 'Oops!';
1051
- this.modalTitleSuccess = 'Thanks for your feedback!';
1052
- this.privacyPolicyText = "I have read and expressly consent to the terms of the <a href='https://pushfeedback.com/privacy'>Privacy Policy</a>.";
1053
- this.ratingPlaceholder = 'Was this page helpful?';
1054
- this.ratingStarsPlaceholder = 'How would you rate this page?';
1055
- this.sendButtonText = 'Send';
1056
- this.screenshotAttachedText = 'Screenshot attached';
1057
- this.screenshotButtonText = 'Add a screenshot';
1058
- this.screenshotTakingText = 'Taking screenshot...';
1059
- this.screenshotTopbarText = 'Select an element on this page';
1060
- this.successMessage = '';
1061
- this.canvasEditorTitle = 'Edit screenshot';
1062
- this.canvasEditorCancelText = 'Cancel';
1063
- this.canvasEditorSaveText = 'Save';
1064
- }
1065
- componentWillLoad() {
1066
- if (this.fetchData)
1067
- this.fetchProjectData();
1068
- this.formEmail = this.emailAddress;
1069
- if (this.rating) {
1070
- this.selectedRating = this.rating;
1071
- }
1072
- if (this.ratingMode == 'thumbs' && this.rating == 0) {
1073
- this.selectedRating = 5;
1074
- }
1075
- }
1076
- async fetchProjectData() {
1077
- try {
1078
- const response = await fetch('https://app.pushfeedback.com/api/projects/' + this.project + '/');
1079
- const data = await response.json();
1080
- this.whitelabel = data.whitelabel;
1081
- }
1082
- catch (error) {
1083
- console.log(error);
1084
- }
1085
- }
1086
- resetOverflow() {
1087
- // Just clean up any stray classes, don't add/remove during screenshot
1088
- document.documentElement.classList.remove('feedback-modal-screenshot-open');
1089
- document.documentElement.classList.remove('feedback-modal-screenshot-open--scroll');
1090
- document.documentElement.classList.remove('feedback-modal-screenshot-closing');
1091
- }
1092
- handleMessageInput(event) {
1093
- this.formMessage = event.target.value;
1094
- }
1095
- handleEmailInput(event) {
1096
- this.formEmail = event.target.value;
1097
- }
1098
- async captureViewportScreenshot() {
1099
- try {
1100
- // Check if Screen Capture API is supported
1101
- if (!navigator.mediaDevices || !navigator.mediaDevices.getDisplayMedia) {
1102
- throw new Error('Screen Capture API is not supported in this browser');
1103
- }
1104
- // Request screen capture with preference for current tab
1105
- const stream = await navigator.mediaDevices.getDisplayMedia({
1106
- video: {
1107
- mediaSource: 'screen',
1108
- width: { ideal: window.innerWidth },
1109
- height: { ideal: window.innerHeight }
1110
- },
1111
- audio: false,
1112
- preferCurrentTab: true
1113
- });
1114
- // Create video element to capture frame
1115
- const video = document.createElement('video');
1116
- video.srcObject = stream;
1117
- video.autoplay = true;
1118
- video.muted = true;
1119
- return new Promise((resolve, reject) => {
1120
- video.onloadedmetadata = () => {
1121
- video.play();
1122
- // Wait a moment for video to stabilize
1123
- setTimeout(() => {
1124
- try {
1125
- // Create canvas to capture frame
1126
- const canvas = document.createElement('canvas');
1127
- canvas.width = video.videoWidth;
1128
- canvas.height = video.videoHeight;
1129
- const ctx = canvas.getContext('2d');
1130
- ctx.drawImage(video, 0, 0);
1131
- // Stop the stream
1132
- stream.getTracks().forEach(track => track.stop());
1133
- // Convert to data URL
1134
- const dataUrl = canvas.toDataURL('image/png');
1135
- console.log('Screenshot captured successfully using Screen Capture API');
1136
- resolve(dataUrl);
1137
- }
1138
- catch (error) {
1139
- stream.getTracks().forEach(track => track.stop());
1140
- reject(error);
1141
- }
1142
- }, 100);
1143
- };
1144
- video.onerror = (_) => {
1145
- stream.getTracks().forEach(track => track.stop());
1146
- reject(new Error('Failed to load video for screenshot capture'));
1147
- };
1148
- });
1149
- }
1150
- catch (error) {
1151
- console.error('Screen capture failed:', error);
1152
- throw error;
1153
- }
1154
- }
1155
- handleCheckboxChange(event) {
1156
- this.isPrivacyChecked = event.target.checked;
1157
- }
1158
- handleVerification(event) {
1159
- this.formVerification = event.target.value;
1160
- }
1161
- handleRatingChange(newRating) {
1162
- this.selectedRating = newRating;
1163
- }
1164
- render() {
1165
- return (index.h("div", { class: `feedback-modal-wrapper ${this.customFont ? 'feedback-modal-wrapper--custom-font' : ''}` }, this.showScreenshotError && (index.h("div", { class: "screenshot-error-notification" }, index.h("div", { class: "screenshot-error-content" }, index.h("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, index.h("circle", { cx: "12", cy: "12", r: "10" }), index.h("line", { x1: "15", y1: "9", x2: "9", y2: "15" }), index.h("line", { x1: "9", y1: "9", x2: "15", y2: "15" })), index.h("span", null, this.screenshotError), index.h("button", { class: "error-close-btn", onClick: () => this.showScreenshotError = false, title: "Close" }, index.h("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, index.h("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), index.h("line", { x1: "6", y1: "6", x2: "18", y2: "18" })))))), this.showModal && (index.h("div", { class: `feedback-overlay ${this.isAnimating ? 'feedback-overlay--visible' : ''}` })), this.showModal && (index.h("div", { class: `feedback-modal-content feedback-modal-content--${this.modalPosition} ${this.isAnimating ? 'feedback-modal-content--open' : ''}`, ref: (el) => (this.modalContent = el) }, index.h("div", { class: "feedback-modal-header" }, !this.formSuccess && !this.formError ? (index.h("span", null, this.modalTitle)) : this.formSuccess ? (index.h("span", null, this.modalTitleSuccess)) : (index.h("span", null, this.modalTitleError)), index.h("button", { class: "feedback-modal-close", onClick: this.close }, index.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" }, index.h("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), index.h("line", { x1: "6", y1: "6", x2: "18", y2: "18" })))), index.h("div", { class: "feedback-modal-body" }, !this.formSuccess && !this.formError ? (index.h("form", { onSubmit: this.handleSubmit }, !this.hideRating && (index.h("div", { class: "feedback-modal-rating" }, this.ratingMode === 'thumbs' ? (index.h("div", { class: "feedback-modal-rating-content" }, index.h("span", { class: "feedback-modal-input-heading" }, this.ratingPlaceholder), index.h("div", { class: "feedback-modal-rating-buttons feedback-modal-rating-buttons--thumbs" }, index.h("button", { title: "Yes", class: `feedback-modal-rating-button ${this.selectedRating === 1
1166
- ? 'feedback-modal-rating-button--selected'
1167
- : ''}`, onClick: (event) => {
1168
- event.preventDefault();
1169
- this.handleRatingChange(1);
1170
- } }, index.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" }, index.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" }))), index.h("button", { title: "No", class: `feedback-modal-rating-button ${this.selectedRating === 5
1171
- ? 'feedback-modal-rating-button--selected'
1172
- : ''}`, onClick: (event) => {
1173
- event.preventDefault();
1174
- this.handleRatingChange(5);
1175
- } }, index.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" }, index.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" })))))) : (index.h("div", { class: "feedback-modal-rating-content" }, index.h("span", { class: "feedback-modal-input-heading" }, this.ratingStarsPlaceholder), index.h("div", { class: "feedback-modal-rating-buttons feedback-modal-rating-buttons--stars" }, [1, 2, 3, 4, 5].map((rating) => (index.h("button", { key: rating, class: `feedback-modal-rating-button ${this.selectedRating >= rating
1176
- ? 'feedback-modal-rating-button--selected'
1177
- : ''}`, onClick: (event) => {
1178
- event.preventDefault();
1179
- this.handleRatingChange(rating);
1180
- } }, index.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" }, index.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" })))))))))), index.h("div", { class: "feedback-modal-text" }, index.h("textarea", { placeholder: this.messagePlaceholder, value: this.formMessage, onInput: (event) => this.handleMessageInput(event) })), !this.hideEmail && (index.h("div", { class: "feedback-modal-email" }, index.h("input", { placeholder: this.emailPlaceholder, type: "email", onInput: (event) => this.handleEmailInput(event), value: this.formEmail, required: this.isEmailRequired }))), index.h("div", { class: "feedback-verification" }, index.h("input", { type: "text", name: "verification", style: { display: 'none' }, onInput: (event) => this.handleVerification(event), value: this.formVerification })), !this.hidePrivacyPolicy && (index.h("div", { class: "feedback-modal-privacy" }, index.h("input", { type: "checkbox", id: "privacyPolicy", onChange: (ev) => this.handleCheckboxChange(ev), required: true }), index.h("span", { innerHTML: this.privacyPolicyText }))), index.h("div", { class: `feedback-modal-buttons ${this.hideScreenshotButton ? 'single' : ''}` }, !this.hideScreenshotButton && (index.h("button", { type: "button", class: `feedback-modal-button feedback-modal-button--screenshot ${this.encodedScreenshot ? 'feedback-modal-button--active' : ''}`, onClick: this.openScreenShot, disabled: this.sending || this.takingScreenshot }, this.encodedScreenshot && (index.h("div", { class: "screenshot-preview", onClick: this.openCanvasEditor }, index.h("img", { src: this.encodedScreenshot, alt: "Screenshot Preview" }))), !this.encodedScreenshot && !this.takingScreenshot && (index.h("svg", { xmlns: "http://www.w3.org/2000/svg", height: "24", viewBox: "0 -960 960 960", width: "24" }, index.h("path", { d: "M680-80v-120H560v-80h120v-120h80v120h120v80H760v120h-80ZM200-200v-200h80v120h120v80H200Zm0-360v-200h200v80H280v120h-80Zm480 0v-120H560v-80h200v200h-80Z" }))), this.takingScreenshot && (index.h("div", { class: "screenshot-loading" }, index.h("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "#666", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", class: "feather-loader" }, index.h("line", { x1: "12", y1: "2", x2: "12", y2: "6" }), index.h("line", { x1: "12", y1: "18", x2: "12", y2: "22" }), index.h("line", { x1: "4.93", y1: "4.93", x2: "7.76", y2: "7.76" }), index.h("line", { x1: "16.24", y1: "16.24", x2: "19.07", y2: "19.07" }), index.h("line", { x1: "2", y1: "12", x2: "6", y2: "12" }), index.h("line", { x1: "18", y1: "12", x2: "22", y2: "12" }), index.h("line", { x1: "4.93", y1: "19.07", x2: "7.76", y2: "16.24" }), index.h("line", { x1: "16.24", y1: "7.76", x2: "19.07", y2: "4.93" })))), this.takingScreenshot ? this.screenshotTakingText :
1181
- this.encodedScreenshot ? this.screenshotAttachedText : this.screenshotButtonText)), index.h("button", { class: "feedback-modal-button feedback-modal-button--submit", type: "submit", disabled: this.sending }, this.sendButtonText)))) : this.formSuccess && !this.formError ? (index.h("div", { class: "feedback-modal-success" }, index.h("p", { class: "feedback-modal-message" }, this.successMessage))) : this.formError && this.formErrorStatus == 404 ? (index.h("p", { class: "feedback-modal-message" }, this.errorMessage404)) : this.formError && this.formErrorStatus == 403 ? (index.h("p", { class: "feedback-modal-message" }, this.errorMessage403)) : this.formError ? (index.h("p", { class: "feedback-modal-message" }, this.errorMessage)) : (index.h("span", null))), index.h("div", { class: "feedback-modal-footer" }, index.h("div", { class: "feedback-logo", style: { display: this.whitelabel ? 'none' : 'block' } }, "Powered by", ' ', index.h("a", { target: "_blank", href: "https://pushfeedback.com" }, "PushFeedback.com")), this.footerText && (index.h("div", { class: "feedback-footer-text" }, index.h("span", { innerHTML: this.footerText })))))), this.showCanvasEditor && (index.h("div", { class: "canvas-editor-overlay" }, index.h("div", { class: "canvas-editor-modal" }, index.h("div", { class: "canvas-editor-header" }, index.h("div", { class: "canvas-editor-title" }, index.h("h3", null, this.canvasEditorTitle)), index.h("div", { class: "canvas-editor-toolbar" }, index.h("div", { class: "toolbar-section" }, index.h("div", { class: "tool-group" }, index.h("button", { class: `tool-btn ${this.canvasDrawingTool === 'rectangle' ? 'active' : ''}`, onClick: () => this.canvasDrawingTool = 'rectangle', title: "Rectangle" }, index.h("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, index.h("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2", ry: "2" }))), index.h("button", { class: `tool-btn ${this.canvasDrawingTool === 'line' ? 'active' : ''}`, onClick: () => this.canvasDrawingTool = 'line', title: "Line" }, index.h("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, index.h("line", { x1: "5", y1: "12", x2: "19", y2: "12" }))), index.h("button", { class: `tool-btn ${this.canvasDrawingTool === 'arrow' ? 'active' : ''}`, onClick: () => this.canvasDrawingTool = 'arrow', title: "Arrow" }, index.h("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, index.h("line", { x1: "7", y1: "17", x2: "17", y2: "7" }), index.h("polyline", { points: "7,7 17,7 17,17" }))), index.h("button", { class: `tool-btn ${this.canvasDrawingTool === 'text' ? 'active' : ''}`, onClick: () => this.canvasDrawingTool = 'text', title: "Text" }, index.h("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, index.h("polyline", { points: "4,7 4,4 20,4 20,7" }), index.h("line", { x1: "9", y1: "20", x2: "15", y2: "20" }), index.h("line", { x1: "12", y1: "4", x2: "12", y2: "20" }))), index.h("div", { class: "toolbar-divider" }), index.h("button", { class: "tool-btn undo-btn", onClick: this.undoLastAnnotation, disabled: this.annotations.length === 0, title: "Undo" }, index.h("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, index.h("polyline", { points: "1,4 1,10 7,10" }), index.h("path", { d: "M3.51,15a9,9,0,0,0,14.85-3.36,9,9,0,0,0-9.19-10.15L1.83,10" }))))), index.h("div", { class: "toolbar-section" }, index.h("div", { class: "color-palette" }, this.defaultColors.map((color, index$1) => (index.h("div", { class: "color-slot-wrapper" }, index.h("button", { class: `color-btn ${this.canvasDrawingColor === color ? 'active' : ''} ${this.editingColorIndex === index$1 ? 'editing' : ''}`, style: { backgroundColor: color }, onClick: () => this.handleColorSlotClick(index$1), title: `Color ${index$1 + 1} - Click to customize` }, this.editingColorIndex === index$1 && (index.h("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "white", "stroke-width": "2" }, index.h("path", { d: "M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z" })))), this.editingColorIndex === index$1 && this.showColorPicker && (index.h("div", { class: "color-picker-dropdown" }, index.h("input", { type: "color", value: color, onInput: (e) => this.handleColorPickerInput(e), onClick: (e) => this.handleColorPickerClick(e) })))))))), index.h("div", { class: "toolbar-section" }, index.h("div", { class: "size-control" }, index.h("input", { type: "range", min: "1", max: "10", value: this.canvasLineWidth, onInput: (e) => this.canvasLineWidth = parseInt(e.target.value), class: "size-slider" }), index.h("span", { class: "size-value" }, this.canvasLineWidth, "px"))), index.h("div", { class: "toolbar-section" }, index.h("button", { class: "action-btn secondary", onClick: this.closeCanvasEditor }, this.canvasEditorCancelText), index.h("button", { class: "action-btn primary", onClick: this.saveAnnotations }, this.canvasEditorSaveText))), index.h("div", { class: "canvas-editor-content" }, index.h("canvas", { ref: (el) => this.canvasRef = el, class: "annotation-canvas", onMouseDown: this.handleCanvasMouseDown, onMouseMove: this.handleCanvasMouseMove, onMouseUp: this.handleCanvasMouseUp, onMouseLeave: this.handleCanvasMouseUp }))))))));
1182
- }
1183
- componentDidRender() {
1184
- if (this.showModal) {
1185
- requestAnimationFrame(() => {
1186
- this.overlayVisible = true;
1187
- });
1188
- }
1189
- }
1190
- async openModal() {
1191
- this.showModal = true;
1192
- requestAnimationFrame(() => {
1193
- requestAnimationFrame(() => {
1194
- this.isAnimating = true;
1195
- });
1196
- });
1197
- }
1198
- };
1199
- FeedbackModal.style = feedbackModalCss;
1200
-
1201
- exports.feedback_button = FeedbackButton;
1202
- exports.feedback_modal = FeedbackModal;