pushfeedback 0.1.68 → 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 +23 -71
  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 -9918
  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 -896
  28. package/dist/collection/components/feedback-modal/feedback-modal.js +0 -1700
  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 -9814
  32. package/dist/components/index.js +0 -3
  33. package/dist/esm/feedback-button_2.entry.js +0 -9913
  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-7406f7be.entry.js +0 -7
  47. package/dist/pushfeedback/p-af2a1f7f.js +0 -2
@@ -1,1700 +0,0 @@
1
- import { h } from '@stencil/core';
2
- import html2canvas from 'html2canvas-pro';
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
- if (this.isEmailRequired && !this.formEmail) {
16
- return;
17
- }
18
- this.resetOverflow();
19
- this.showScreenshotMode = false;
20
- this.showScreenshotTopBar = false;
21
- this.showModal = false;
22
- this.sending = true;
23
- try {
24
- const body = {
25
- url: window.location.href,
26
- message: this.formMessage,
27
- email: this.formEmail,
28
- project: this.project,
29
- screenshot: this.encodedScreenshot,
30
- rating: this.selectedRating,
31
- ratingMode: this.ratingMode,
32
- metadata: this.metadata,
33
- verification: this.formVerification,
34
- session: localStorage.getItem('pushfeedback_sessionid') || '',
35
- };
36
- const res = await fetch('https://app.pushfeedback.com/api/feedback/', {
37
- method: 'POST',
38
- body: JSON.stringify(body),
39
- headers: {
40
- 'Content-Type': 'application/json',
41
- },
42
- });
43
- if (res.status === 201) {
44
- const feedback_with_id = Object.assign(Object.assign({}, body), { id: await res.json() });
45
- this.feedbackSent.emit({ feedback: feedback_with_id });
46
- this.formSuccess = true;
47
- this.formError = false;
48
- }
49
- else {
50
- const errorText = await res.text();
51
- const response = {
52
- status: res.status,
53
- message: errorText,
54
- };
55
- this.feedbackError.emit({ error: response });
56
- this.formSuccess = false;
57
- this.formError = true;
58
- this.formErrorStatus = res.status;
59
- }
60
- }
61
- catch (error) {
62
- const response = {
63
- status: 500,
64
- message: error,
65
- };
66
- this.feedbackError.emit({ error: response });
67
- this.formSuccess = false;
68
- this.formError = true;
69
- this.formErrorStatus = 500;
70
- }
71
- finally {
72
- this.sending = false;
73
- this.showModal = true;
74
- }
75
- };
76
- this.close = () => {
77
- this.isAnimating = false;
78
- setTimeout(() => {
79
- this.sending = false;
80
- this.showModal = false;
81
- this.showScreenshotMode = false;
82
- this.showScreenshotTopBar = false;
83
- this.hasSelectedElement = false;
84
- this.encodedScreenshot = null;
85
- // Remove highlight from ALL selected elements
86
- document.querySelectorAll('.feedback-modal-element-selected').forEach(el => {
87
- el.classList.remove('feedback-modal-element-selected');
88
- });
89
- // Reset canvas editor states
90
- this.takingScreenshot = false;
91
- this.showPreviewModal = false;
92
- this.showCanvasEditor = false;
93
- this.annotations = [];
94
- this.currentAnnotation = null;
95
- this.isDrawing = false;
96
- this.canvasRef = null;
97
- this.canvasContext = null;
98
- this.originalImageData = null;
99
- // Reset resizing states
100
- this.isResizing = false;
101
- this.resizingAnnotation = null;
102
- this.resizeStartSize = 16;
103
- this.hoveredAnnotation = null;
104
- this.resizeHandle = false;
105
- // Reset form states
106
- this.formSuccess = false;
107
- this.formError = false;
108
- this.formErrorStatus = 500;
109
- this.formMessage = '';
110
- this.formEmail = '';
111
- this.resetOverflow();
112
- }, 200);
113
- };
114
- this.openScreenShot = async () => {
115
- // Show loading state immediately
116
- this.takingScreenshot = true;
117
- try {
118
- // Capture viewport screenshot immediately
119
- const dataUrl = await this.captureViewportScreenshot();
120
- this.encodedScreenshot = dataUrl;
121
- this.originalImageData = dataUrl;
122
- // Reset loading state
123
- this.takingScreenshot = false;
124
- // Skip preview modal and go directly to canvas editor
125
- this.showModal = false;
126
- this.showCanvasEditor = true;
127
- // Initialize canvas after a short delay to ensure DOM is ready
128
- setTimeout(() => {
129
- this.initializeCanvas();
130
- }, 100);
131
- }
132
- catch (error) {
133
- console.error('Failed to capture screenshot:', error);
134
- // Reset loading state on error
135
- this.takingScreenshot = false;
136
- // Show modal anyway
137
- this.showModal = true;
138
- }
139
- };
140
- this.openCanvasEditor = (event) => {
141
- if (event) {
142
- event.stopPropagation();
143
- }
144
- this.showModal = false;
145
- this.showCanvasEditor = true;
146
- // Initialize canvas after a short delay to ensure DOM is ready
147
- setTimeout(() => {
148
- this.initializeCanvas();
149
- }, 100);
150
- };
151
- this.closeCanvasEditor = () => {
152
- this.showCanvasEditor = false;
153
- this.showModal = true;
154
- };
155
- this.saveAnnotations = () => {
156
- if (this.canvasRef) {
157
- // Create final image with annotations
158
- const finalDataUrl = this.canvasRef.toDataURL('image/png');
159
- this.encodedScreenshot = finalDataUrl;
160
- }
161
- this.showCanvasEditor = false;
162
- this.showModal = true;
163
- };
164
- this.initializeCanvas = () => {
165
- if (!this.canvasRef || !this.originalImageData)
166
- return;
167
- this.canvasContext = this.canvasRef.getContext('2d');
168
- const img = new Image();
169
- img.onload = () => {
170
- // Set canvas to original image dimensions
171
- this.canvasRef.width = img.width;
172
- this.canvasRef.height = img.height;
173
- // Get available container dimensions
174
- const containerWidth = this.canvasRef.parentElement.clientWidth - 32; // Account for reduced padding (16px * 2)
175
- const containerHeight = this.canvasRef.parentElement.clientHeight - 32;
176
- // Calculate scale factors for both dimensions
177
- const scaleX = containerWidth / img.width;
178
- const scaleY = containerHeight / img.height;
179
- // Use the smaller scale to ensure complete image fits
180
- const scale = Math.min(scaleX, scaleY, 1); // Never scale up, only down
181
- // Calculate final display dimensions
182
- const displayWidth = img.width * scale;
183
- const displayHeight = img.height * scale;
184
- // Set CSS size for display (this scales the canvas visually)
185
- this.canvasRef.style.width = `${displayWidth}px`;
186
- this.canvasRef.style.height = `${displayHeight}px`;
187
- console.log('Canvas initialized with complete image fit:', {
188
- originalWidth: img.width,
189
- originalHeight: img.height,
190
- displayWidth,
191
- displayHeight,
192
- scale,
193
- scaleX,
194
- scaleY,
195
- containerWidth,
196
- containerHeight,
197
- usingScale: scale === scaleX ? 'width-limited' : 'height-limited'
198
- });
199
- // Draw the original image at full resolution
200
- this.canvasContext.drawImage(img, 0, 0);
201
- // Redraw existing annotations
202
- this.redrawAnnotations();
203
- };
204
- img.src = this.originalImageData;
205
- };
206
- this.redrawAnnotations = () => {
207
- if (!this.canvasContext)
208
- return;
209
- // Clear and redraw background image
210
- const img = new Image();
211
- img.onload = () => {
212
- this.canvasContext.clearRect(0, 0, this.canvasRef.width, this.canvasRef.height);
213
- this.canvasContext.drawImage(img, 0, 0);
214
- // Draw all annotations
215
- this.annotations.forEach(annotation => {
216
- this.drawAnnotation(annotation);
217
- });
218
- };
219
- img.src = this.originalImageData;
220
- };
221
- this.drawAnnotation = (annotation) => {
222
- if (!this.canvasContext)
223
- return;
224
- this.canvasContext.strokeStyle = annotation.color;
225
- this.canvasContext.lineWidth = annotation.lineWidth;
226
- this.canvasContext.lineCap = 'round';
227
- this.canvasContext.lineJoin = 'round';
228
- switch (annotation.type) {
229
- case 'rectangle':
230
- this.canvasContext.strokeRect(annotation.startX, annotation.startY, annotation.width, annotation.height);
231
- // Rectangle resize handles disabled for now
232
- break;
233
- case 'line':
234
- this.canvasContext.beginPath();
235
- this.canvasContext.moveTo(annotation.startX, annotation.startY);
236
- this.canvasContext.lineTo(annotation.endX, annotation.endY);
237
- this.canvasContext.stroke();
238
- // Draw resize handles if this annotation is hovered
239
- if (this.hoveredAnnotation === annotation) {
240
- this.drawLineResizeHandles(annotation);
241
- }
242
- break;
243
- case 'arrow':
244
- this.drawArrow(annotation.startX, annotation.startY, annotation.endX, annotation.endY);
245
- // Draw resize handles if this annotation is hovered
246
- if (this.hoveredAnnotation === annotation) {
247
- this.drawLineResizeHandles(annotation); // Same as line
248
- }
249
- break;
250
- case 'text':
251
- const fontSize = annotation.fontSize || 16;
252
- this.canvasContext.fillStyle = annotation.color;
253
- this.canvasContext.font = `${fontSize}px Arial`;
254
- this.canvasContext.fillText(annotation.text, annotation.x, annotation.y);
255
- // Draw resize handle if this annotation is hovered
256
- if (this.hoveredAnnotation === annotation) {
257
- this.drawTextResizeHandle(annotation);
258
- }
259
- break;
260
- }
261
- };
262
- // Draw resize handle for text annotation
263
- this.drawTextResizeHandle = (annotation) => {
264
- if (!this.canvasContext || annotation.type !== 'text')
265
- return;
266
- const fontSize = annotation.fontSize || 16;
267
- const textWidth = this.getTextWidth(annotation.text, fontSize);
268
- const handleSize = 8;
269
- const handleX = annotation.x + textWidth;
270
- const handleY = annotation.y;
271
- // Draw resize handle (small square) - using widget primary color
272
- this.canvasContext.fillStyle = '#0070F4'; // var(--feedback-primary-color)
273
- this.canvasContext.strokeStyle = '#ffffff';
274
- this.canvasContext.lineWidth = 2;
275
- this.canvasContext.fillRect(handleX - handleSize / 2, handleY - handleSize / 2, handleSize, handleSize);
276
- this.canvasContext.strokeRect(handleX - handleSize / 2, handleY - handleSize / 2, handleSize, handleSize);
277
- };
278
- this.drawArrow = (fromX, fromY, toX, toY) => {
279
- const headlen = 15; // Arrow head length
280
- const angle = Math.atan2(toY - fromY, toX - fromX);
281
- // Draw line
282
- this.canvasContext.beginPath();
283
- this.canvasContext.moveTo(fromX, fromY);
284
- this.canvasContext.lineTo(toX, toY);
285
- this.canvasContext.stroke();
286
- // Draw arrow head
287
- this.canvasContext.beginPath();
288
- this.canvasContext.moveTo(toX, toY);
289
- this.canvasContext.lineTo(toX - headlen * Math.cos(angle - Math.PI / 6), toY - headlen * Math.sin(angle - Math.PI / 6));
290
- this.canvasContext.moveTo(toX, toY);
291
- this.canvasContext.lineTo(toX - headlen * Math.cos(angle + Math.PI / 6), toY - headlen * Math.sin(angle + Math.PI / 6));
292
- this.canvasContext.stroke();
293
- };
294
- this.undoLastAnnotation = () => {
295
- this.annotations = this.annotations.slice(0, -1);
296
- this.redrawAnnotations();
297
- };
298
- // Handle color slot editing
299
- this.handleColorSlotClick = (colorIndex) => {
300
- if (this.editingColorIndex === colorIndex) {
301
- // If already editing this slot, just select the color
302
- this.canvasDrawingColor = this.defaultColors[colorIndex];
303
- this.showColorPicker = false;
304
- this.editingColorIndex = -1;
305
- }
306
- else {
307
- // Start editing this color slot
308
- this.editingColorIndex = colorIndex;
309
- this.showColorPicker = true;
310
- this.canvasDrawingColor = this.defaultColors[colorIndex];
311
- }
312
- };
313
- // Update color in slot
314
- this.updateColorSlot = (newColor) => {
315
- if (this.editingColorIndex >= 0 && this.editingColorIndex < this.defaultColors.length) {
316
- this.defaultColors[this.editingColorIndex] = newColor;
317
- this.canvasDrawingColor = newColor;
318
- this.showColorPicker = false;
319
- this.editingColorIndex = -1;
320
- // Force reactivity
321
- this.defaultColors = [...this.defaultColors];
322
- }
323
- };
324
- // Handle color picker input without closing
325
- this.handleColorPickerInput = (event) => {
326
- event.stopPropagation();
327
- const newColor = event.target.value;
328
- if (this.editingColorIndex >= 0 && this.editingColorIndex < this.defaultColors.length) {
329
- this.defaultColors[this.editingColorIndex] = newColor;
330
- this.canvasDrawingColor = newColor;
331
- // Force reactivity
332
- this.defaultColors = [...this.defaultColors];
333
- }
334
- };
335
- // Handle color picker click to prevent closing
336
- this.handleColorPickerClick = (event) => {
337
- event.stopPropagation();
338
- };
339
- // Close color picker
340
- this.closeColorPicker = () => {
341
- this.showColorPicker = false;
342
- this.editingColorIndex = -1;
343
- };
344
- // Check if point is in resize handle for any annotation type
345
- this.isPointInResizeHandle = (x, y, annotation) => {
346
- const handleSize = 8;
347
- switch (annotation.type) {
348
- case 'text':
349
- const textWidth = this.getTextWidth(annotation.text, annotation.fontSize || 16);
350
- const handleX = annotation.x + textWidth;
351
- const handleY = annotation.y;
352
- return x >= handleX - handleSize / 2 && x <= handleX + handleSize / 2 &&
353
- y >= handleY - handleSize / 2 && y <= handleY + handleSize / 2;
354
- case 'rectangle':
355
- // Rectangle resizing disabled for now
356
- return false;
357
- case 'line':
358
- case 'arrow':
359
- // Check both endpoint handles
360
- const lineHandles = [
361
- { x: annotation.startX, y: annotation.startY, point: 'start' },
362
- { x: annotation.endX, y: annotation.endY, point: 'end' }
363
- ];
364
- for (const handle of lineHandles) {
365
- if (x >= handle.x - handleSize / 2 && x <= handle.x + handleSize / 2 &&
366
- y >= handle.y - handleSize / 2 && y <= handle.y + handleSize / 2) {
367
- return handle.point; // Return which endpoint was clicked
368
- }
369
- }
370
- return false;
371
- default:
372
- return false;
373
- }
374
- };
375
- // Get text width for resize handle positioning
376
- this.getTextWidth = (text, fontSize) => {
377
- // Approximate text width calculation
378
- return text.length * fontSize * 0.6;
379
- };
380
- // Start text resize
381
- this.startTextResize = (annotation, startPos) => {
382
- this.isResizing = true;
383
- this.resizingAnnotation = annotation;
384
- this.resizeStartSize = annotation.fontSize || 16;
385
- this.dragStartPos = startPos;
386
- };
387
- // Handle text resize
388
- this.handleTextResize = (currentPos) => {
389
- if (!this.resizingAnnotation || !this.dragStartPos)
390
- return;
391
- const deltaX = currentPos.x - this.dragStartPos.x;
392
- const deltaY = currentPos.y - this.dragStartPos.y;
393
- const avgDelta = (deltaX + deltaY) / 2;
394
- // Calculate new font size (minimum 8px, maximum 72px)
395
- const newSize = Math.max(8, Math.min(72, this.resizeStartSize + avgDelta * 0.5));
396
- // Update annotation font size
397
- const index = this.annotations.findIndex(a => a === this.resizingAnnotation);
398
- if (index !== -1) {
399
- this.annotations[index] = Object.assign(Object.assign({}, this.resizingAnnotation), { fontSize: Math.round(newSize) });
400
- this.resizingAnnotation = this.annotations[index];
401
- }
402
- this.redrawAnnotations();
403
- };
404
- // Start resize for any annotation type
405
- this.startResize = (annotation, handle, startPos) => {
406
- this.isResizing = true;
407
- this.resizingAnnotation = annotation;
408
- this.resizeHandle = handle;
409
- this.dragStartPos = startPos;
410
- // Store original values for different annotation types
411
- if (annotation.type === 'text') {
412
- this.resizeStartSize = annotation.fontSize || 16;
413
- }
414
- };
415
- // Enhanced mouse down handler with resize detection for all annotation types
416
- this.handleCanvasMouseDown = (event) => {
417
- if (!this.canvasRef)
418
- return;
419
- // Close color picker if open
420
- if (this.showColorPicker) {
421
- this.closeColorPicker();
422
- }
423
- const coords = this.getCanvasCoordinates(event);
424
- // Check if clicking on existing annotation first
425
- const found = this.findAnnotationAt(coords.x, coords.y);
426
- if (found) {
427
- // Check if clicking on resize handle for any annotation type
428
- const handle = this.isPointInResizeHandle(coords.x, coords.y, found.annotation);
429
- if (handle) {
430
- this.startResize(found.annotation, handle, coords);
431
- this.canvasRef.style.cursor = 'nw-resize';
432
- return;
433
- }
434
- // Start dragging existing annotation
435
- if (!this.isDrawing) {
436
- this.isDragging = true;
437
- this.draggedAnnotation = found.annotation;
438
- this.dragStartPos = coords;
439
- this.canvasRef.style.cursor = 'grabbing';
440
- return;
441
- }
442
- }
443
- // Original drawing logic
444
- this.isDrawing = true;
445
- if (this.canvasDrawingTool === 'text') {
446
- const text = prompt('Enter text:');
447
- if (text) {
448
- const annotation = {
449
- type: 'text',
450
- x: coords.x,
451
- y: coords.y,
452
- text,
453
- color: this.canvasDrawingColor,
454
- fontSize: 16
455
- };
456
- this.annotations = [...this.annotations, annotation];
457
- this.redrawAnnotations();
458
- }
459
- this.isDrawing = false;
460
- }
461
- else {
462
- this.currentAnnotation = {
463
- type: this.canvasDrawingTool,
464
- startX: coords.x,
465
- startY: coords.y,
466
- color: this.canvasDrawingColor,
467
- lineWidth: this.canvasLineWidth
468
- };
469
- }
470
- };
471
- this.handleCanvasMouseMove = (event) => {
472
- if (!this.canvasRef)
473
- return;
474
- const coords = this.getCanvasCoordinates(event);
475
- // Handle resizing for any annotation type
476
- if (this.isResizing && this.resizingAnnotation) {
477
- this.handleResize(coords);
478
- return;
479
- }
480
- // Handle dragging existing annotation
481
- if (this.isDragging && this.draggedAnnotation && this.dragStartPos) {
482
- const deltaX = coords.x - this.dragStartPos.x;
483
- const deltaY = coords.y - this.dragStartPos.y;
484
- // Update annotation position
485
- const updatedAnnotation = Object.assign({}, this.draggedAnnotation);
486
- switch (updatedAnnotation.type) {
487
- case 'rectangle':
488
- updatedAnnotation.startX += deltaX;
489
- updatedAnnotation.startY += deltaY;
490
- break;
491
- case 'line':
492
- case 'arrow':
493
- updatedAnnotation.startX += deltaX;
494
- updatedAnnotation.startY += deltaY;
495
- updatedAnnotation.endX += deltaX;
496
- updatedAnnotation.endY += deltaY;
497
- break;
498
- case 'text':
499
- updatedAnnotation.x += deltaX;
500
- updatedAnnotation.y += deltaY;
501
- break;
502
- }
503
- // Update annotation in array
504
- const index = this.annotations.findIndex(a => a === this.draggedAnnotation);
505
- if (index !== -1) {
506
- this.annotations[index] = updatedAnnotation;
507
- this.draggedAnnotation = updatedAnnotation;
508
- }
509
- this.dragStartPos = coords;
510
- this.redrawAnnotations();
511
- return;
512
- }
513
- // Handle drawing new annotation
514
- if (this.isDrawing && this.currentAnnotation) {
515
- if (this.canvasDrawingTool === 'rectangle') {
516
- this.currentAnnotation.width = coords.x - this.currentAnnotation.startX;
517
- this.currentAnnotation.height = coords.y - this.currentAnnotation.startY;
518
- }
519
- else {
520
- this.currentAnnotation.endX = coords.x;
521
- this.currentAnnotation.endY = coords.y;
522
- }
523
- this.redrawAnnotations();
524
- this.drawAnnotation(this.currentAnnotation);
525
- return;
526
- }
527
- // Handle hover states and cursor changes
528
- const found = this.findAnnotationAt(coords.x, coords.y);
529
- if (found) {
530
- // Check if hovering over resize handle for any annotation type
531
- const handle = this.isPointInResizeHandle(coords.x, coords.y, found.annotation);
532
- if (handle) {
533
- this.canvasRef.style.cursor = 'nw-resize';
534
- this.hoveredAnnotation = found.annotation;
535
- this.redrawAnnotations();
536
- return;
537
- }
538
- // Regular hover over annotation
539
- this.canvasRef.style.cursor = 'grab';
540
- if (this.hoveredAnnotation !== found.annotation) {
541
- this.hoveredAnnotation = found.annotation;
542
- this.redrawAnnotations();
543
- }
544
- }
545
- else {
546
- // No annotation under cursor
547
- this.canvasRef.style.cursor = 'crosshair';
548
- if (this.hoveredAnnotation) {
549
- this.hoveredAnnotation = null;
550
- this.redrawAnnotations();
551
- }
552
- }
553
- };
554
- this.handleCanvasMouseUp = () => {
555
- // Handle end of text resizing
556
- if (this.isResizing) {
557
- this.isResizing = false;
558
- this.resizingAnnotation = null;
559
- this.dragStartPos = null;
560
- this.resizeHandle = false;
561
- if (this.canvasRef) {
562
- this.canvasRef.style.cursor = 'crosshair';
563
- }
564
- return;
565
- }
566
- // Handle end of dragging
567
- if (this.isDragging) {
568
- this.isDragging = false;
569
- this.draggedAnnotation = null;
570
- this.dragStartPos = null;
571
- if (this.canvasRef) {
572
- this.canvasRef.style.cursor = 'crosshair';
573
- }
574
- return;
575
- }
576
- // Handle end of drawing
577
- if (!this.isDrawing || !this.currentAnnotation)
578
- return;
579
- this.isDrawing = false;
580
- this.annotations = [...this.annotations, this.currentAnnotation];
581
- this.currentAnnotation = null;
582
- this.redrawAnnotations();
583
- };
584
- // Draw resize handles for rectangle annotation
585
- this.drawRectangleResizeHandles = (annotation) => {
586
- if (!this.canvasContext || annotation.type !== 'rectangle')
587
- return;
588
- const handleSize = 8;
589
- const left = annotation.startX;
590
- const top = annotation.startY;
591
- const right = annotation.startX + annotation.width;
592
- const bottom = annotation.startY + annotation.height;
593
- // Define handle positions (4 corners)
594
- const handles = [
595
- { x: left, y: top },
596
- { x: right, y: top },
597
- { x: right, y: bottom },
598
- { x: left, y: bottom } // Bottom-left
599
- ];
600
- // Draw each handle
601
- this.canvasContext.fillStyle = '#0070F4'; // Primary color
602
- this.canvasContext.strokeStyle = '#ffffff';
603
- this.canvasContext.lineWidth = 2;
604
- handles.forEach(handle => {
605
- this.canvasContext.fillRect(handle.x - handleSize / 2, handle.y - handleSize / 2, handleSize, handleSize);
606
- this.canvasContext.strokeRect(handle.x - handleSize / 2, handle.y - handleSize / 2, handleSize, handleSize);
607
- });
608
- };
609
- // Draw resize handles for line/arrow annotation
610
- this.drawLineResizeHandles = (annotation) => {
611
- if (!this.canvasContext || (annotation.type !== 'line' && annotation.type !== 'arrow'))
612
- return;
613
- const handleSize = 8;
614
- // Define handle positions (2 endpoints)
615
- const handles = [
616
- { x: annotation.startX, y: annotation.startY },
617
- { x: annotation.endX, y: annotation.endY } // End point
618
- ];
619
- // Draw each handle
620
- this.canvasContext.fillStyle = '#0070F4'; // Primary color
621
- this.canvasContext.strokeStyle = '#ffffff';
622
- this.canvasContext.lineWidth = 2;
623
- handles.forEach(handle => {
624
- this.canvasContext.fillRect(handle.x - handleSize / 2, handle.y - handleSize / 2, handleSize, handleSize);
625
- this.canvasContext.strokeRect(handle.x - handleSize / 2, handle.y - handleSize / 2, handleSize, handleSize);
626
- });
627
- };
628
- // Convert screen coordinates to canvas coordinates
629
- this.getCanvasCoordinates = (event) => {
630
- if (!this.canvasRef)
631
- return { x: 0, y: 0 };
632
- const rect = this.canvasRef.getBoundingClientRect();
633
- // Calculate the scale factor between display size and actual canvas size
634
- const scaleX = this.canvasRef.width / rect.width;
635
- const scaleY = this.canvasRef.height / rect.height;
636
- const x = (event.clientX - rect.left) * scaleX;
637
- const y = (event.clientY - rect.top) * scaleY;
638
- return { x, y };
639
- };
640
- // Find annotation under mouse cursor
641
- this.findAnnotationAt = (x, y) => {
642
- // Check in reverse order (top to bottom)
643
- for (let i = this.annotations.length - 1; i >= 0; i--) {
644
- const annotation = this.annotations[i];
645
- if (this.isPointInAnnotation(x, y, annotation)) {
646
- return { annotation, index: i };
647
- }
648
- }
649
- return null;
650
- };
651
- // Check if point is within annotation bounds
652
- this.isPointInAnnotation = (x, y, annotation) => {
653
- const tolerance = 10; // Click tolerance
654
- switch (annotation.type) {
655
- case 'rectangle':
656
- const left = Math.min(annotation.startX, annotation.startX + annotation.width);
657
- const right = Math.max(annotation.startX, annotation.startX + annotation.width);
658
- const top = Math.min(annotation.startY, annotation.startY + annotation.height);
659
- const bottom = Math.max(annotation.startY, annotation.startY + annotation.height);
660
- return x >= left - tolerance && x <= right + tolerance &&
661
- y >= top - tolerance && y <= bottom + tolerance;
662
- case 'line':
663
- case 'arrow':
664
- // Distance from point to line
665
- const A = annotation.endY - annotation.startY;
666
- const B = annotation.startX - annotation.endX;
667
- const C = annotation.endX * annotation.startY - annotation.startX * annotation.endY;
668
- const distance = Math.abs(A * x + B * y + C) / Math.sqrt(A * A + B * B);
669
- return distance <= tolerance;
670
- case 'text':
671
- // Simple bounding box for text
672
- return x >= annotation.x - tolerance && x <= annotation.x + 100 &&
673
- y >= annotation.y - 20 && y <= annotation.y + tolerance;
674
- default:
675
- return false;
676
- }
677
- };
678
- // Handle resize for different annotation types
679
- this.handleResize = (currentPos) => {
680
- if (!this.resizingAnnotation || !this.dragStartPos)
681
- return;
682
- const annotation = this.resizingAnnotation;
683
- const index = this.annotations.findIndex(a => a === annotation);
684
- if (index === -1)
685
- return;
686
- let updatedAnnotation = Object.assign({}, annotation);
687
- switch (annotation.type) {
688
- case 'text':
689
- // Text resize logic (existing)
690
- const deltaX = currentPos.x - this.dragStartPos.x;
691
- const deltaY = currentPos.y - this.dragStartPos.y;
692
- const avgDelta = (deltaX + deltaY) / 2;
693
- const newSize = Math.max(8, Math.min(72, this.resizeStartSize + avgDelta * 0.5));
694
- updatedAnnotation.fontSize = Math.round(newSize);
695
- break;
696
- case 'rectangle':
697
- // Rectangle resizing disabled for now
698
- return;
699
- case 'line':
700
- case 'arrow':
701
- // Line/arrow resize logic - move endpoints
702
- if (this.resizeHandle === 'start') {
703
- updatedAnnotation.startX = currentPos.x;
704
- updatedAnnotation.startY = currentPos.y;
705
- }
706
- else if (this.resizeHandle === 'end') {
707
- updatedAnnotation.endX = currentPos.x;
708
- updatedAnnotation.endY = currentPos.y;
709
- }
710
- break;
711
- }
712
- // Update annotation in array
713
- this.annotations[index] = updatedAnnotation;
714
- this.resizingAnnotation = updatedAnnotation;
715
- this.redrawAnnotations();
716
- };
717
- this.sending = false;
718
- this.formMessage = '';
719
- this.formEmail = '';
720
- this.formSuccess = false;
721
- this.formVerification = '';
722
- this.formError = false;
723
- this.formErrorStatus = 500;
724
- this.encodedScreenshot = undefined;
725
- this.isPrivacyChecked = false;
726
- this.whitelabel = false;
727
- this.selectedRating = -1;
728
- this.overlayVisible = false;
729
- this.isAnimating = false;
730
- this.takingScreenshot = false;
731
- this.showPreviewModal = false;
732
- this.showCanvasEditor = false;
733
- this.canvasDrawingTool = 'rectangle';
734
- this.canvasDrawingColor = '#ff0000';
735
- this.canvasLineWidth = 3;
736
- this.isDrawing = false;
737
- this.annotations = [];
738
- this.currentAnnotation = null;
739
- this.isDragging = false;
740
- this.draggedAnnotation = null;
741
- this.dragStartPos = null;
742
- this.showColorPicker = false;
743
- this.editingColorIndex = -1;
744
- this.isResizing = false;
745
- this.resizingAnnotation = null;
746
- this.resizeStartSize = 16;
747
- this.hoveredAnnotation = null;
748
- this.resizeHandle = false;
749
- this.defaultColors = ['#ff0000', '#00ff00', '#0000ff', '#000000'];
750
- this.customFont = false;
751
- this.emailAddress = '';
752
- this.hideEmail = false;
753
- this.isEmailRequired = false;
754
- this.ratingMode = 'thumbs';
755
- this.hasSelectedElement = false;
756
- this.hidePrivacyPolicy = true;
757
- this.hideRating = false;
758
- this.hideScreenshotButton = false;
759
- this.project = '';
760
- this.showScreenshotMode = false;
761
- this.showScreenshotTopBar = false;
762
- this.showModal = false;
763
- this.rating = undefined;
764
- this.metadata = undefined;
765
- this.fetchData = true;
766
- this.emailPlaceholder = 'Email address (optional)';
767
- this.errorMessage = 'Please try again later.';
768
- this.errorMessage403 = 'The request URL does not match the one defined in PushFeedback for this project.';
769
- this.errorMessage404 = 'We could not find the provided project ID in PushFeedback.';
770
- this.messagePlaceholder = 'Comments';
771
- this.footerText = '';
772
- this.modalPosition = 'center';
773
- this.modalTitle = 'Share your feedback';
774
- this.modalTitleError = 'Oops!';
775
- this.modalTitleSuccess = 'Thanks for your feedback!';
776
- this.privacyPolicyText = "I have read and expressly consent to the terms of the <a href='https://pushfeedback.com/privacy'>Privacy Policy</a>.";
777
- this.ratingPlaceholder = 'Was this page helpful?';
778
- this.ratingStarsPlaceholder = 'How would you rate this page?';
779
- this.sendButtonText = 'Send';
780
- this.screenshotAttachedText = 'Screenshot attached';
781
- this.screenshotButtonText = 'Add a screenshot';
782
- this.screenshotTakingText = 'Taking screenshot...';
783
- this.screenshotTopbarText = 'Select an element on this page';
784
- this.successMessage = '';
785
- this.canvasEditorTitle = 'Edit screenshot';
786
- this.canvasEditorCancelText = 'Cancel';
787
- this.canvasEditorSaveText = 'Save';
788
- }
789
- componentWillLoad() {
790
- if (this.fetchData)
791
- this.fetchProjectData();
792
- this.formEmail = this.emailAddress;
793
- if (this.rating) {
794
- this.selectedRating = this.rating;
795
- }
796
- if (this.ratingMode == 'thumbs' && this.rating == 0) {
797
- this.selectedRating = 5;
798
- }
799
- }
800
- async fetchProjectData() {
801
- try {
802
- const response = await fetch('https://app.pushfeedback.com/api/projects/' + this.project + '/');
803
- const data = await response.json();
804
- this.whitelabel = data.whitelabel;
805
- }
806
- catch (error) {
807
- console.log(error);
808
- }
809
- }
810
- resetOverflow() {
811
- // Just clean up any stray classes, don't add/remove during screenshot
812
- document.documentElement.classList.remove('feedback-modal-screenshot-open');
813
- document.documentElement.classList.remove('feedback-modal-screenshot-open--scroll');
814
- document.documentElement.classList.remove('feedback-modal-screenshot-closing');
815
- }
816
- handleMessageInput(event) {
817
- this.formMessage = event.target.value;
818
- }
819
- handleEmailInput(event) {
820
- this.formEmail = event.target.value;
821
- }
822
- captureViewportScreenshot() {
823
- return new Promise((resolve, reject) => {
824
- requestAnimationFrame(() => {
825
- // Get viewport dimensions and scroll position
826
- const viewportWidth = window.innerWidth;
827
- const viewportHeight = window.innerHeight;
828
- const scrollX = window.scrollX || window.pageXOffset || 0;
829
- const scrollY = window.scrollY || window.pageYOffset || 0;
830
- // Capture exactly what the user sees in their viewport
831
- html2canvas(document.documentElement, {
832
- x: scrollX,
833
- y: scrollY,
834
- width: viewportWidth,
835
- height: viewportHeight,
836
- scrollX: 0,
837
- scrollY: 0,
838
- allowTaint: false,
839
- useCORS: true,
840
- scale: 1,
841
- backgroundColor: '#ffffff',
842
- logging: false,
843
- foreignObjectRendering: false,
844
- imageTimeout: 15000,
845
- windowWidth: viewportWidth,
846
- windowHeight: viewportHeight,
847
- removeContainer: true,
848
- ignoreElements: (element) => {
849
- // Ignore all feedback modal elements
850
- return element.closest('feedback-modal') !== null ||
851
- element.classList.contains('feedback-overlay') ||
852
- element.classList.contains('feedback-modal-screenshot-header') ||
853
- element.tagName === 'FEEDBACK-MODAL' ||
854
- element.tagName === 'FEEDBACK-BUTTON';
855
- }
856
- })
857
- .then((canvas) => {
858
- const dataUrl = canvas.toDataURL('image/png');
859
- console.log('Screenshot captured successfully, size:', canvas.width, 'x', canvas.height);
860
- resolve(dataUrl);
861
- })
862
- .catch((error) => {
863
- console.error('Failed to capture viewport screenshot:', error);
864
- reject(error);
865
- });
866
- });
867
- });
868
- }
869
- handleCheckboxChange(event) {
870
- this.isPrivacyChecked = event.target.checked;
871
- }
872
- handleVerification(event) {
873
- this.formVerification = event.target.value;
874
- }
875
- handleRatingChange(newRating) {
876
- this.selectedRating = newRating;
877
- }
878
- render() {
879
- return (h("div", { class: `feedback-modal-wrapper ${this.customFont ? 'feedback-modal-wrapper--custom-font' : ''}` }, this.showModal && (h("div", { class: `feedback-overlay ${this.isAnimating ? 'feedback-overlay--visible' : ''}` })), this.showModal && (h("div", { class: `feedback-modal-content feedback-modal-content--${this.modalPosition} ${this.isAnimating ? '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
880
- ? 'feedback-modal-rating-button--selected'
881
- : ''}`, onClick: (event) => {
882
- event.preventDefault();
883
- this.handleRatingChange(1);
884
- } }, 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
885
- ? 'feedback-modal-rating-button--selected'
886
- : ''}`, onClick: (event) => {
887
- event.preventDefault();
888
- this.handleRatingChange(5);
889
- } }, 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
890
- ? 'feedback-modal-rating-button--selected'
891
- : ''}`, onClick: (event) => {
892
- event.preventDefault();
893
- this.handleRatingChange(rating);
894
- } }, 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, required: this.isEmailRequired }))), h("div", { class: "feedback-verification" }, h("input", { type: "text", name: "verification", style: { display: 'none' }, onInput: (event) => this.handleVerification(event), value: this.formVerification })), !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 || this.takingScreenshot }, this.encodedScreenshot && (h("div", { class: "screenshot-preview", onClick: this.openCanvasEditor }, h("img", { src: this.encodedScreenshot, alt: "Screenshot Preview" }))), !this.encodedScreenshot && !this.takingScreenshot && (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.takingScreenshot && (h("div", { class: "screenshot-loading" }, h("svg", { width: "16", height: "16", viewBox: "0 0 16 16" }, h("circle", { cx: "8", cy: "8", r: "6", fill: "none", stroke: "#666", "stroke-width": "2", "stroke-dasharray": "6 6", "transform-origin": "8 8" }, h("animateTransform", { attributeName: "transform", type: "rotate", values: "0 8 8;360 8 8", dur: "1s", repeatCount: "indefinite" }))))), this.takingScreenshot ? this.screenshotTakingText :
895
- this.encodedScreenshot ? this.screenshotAttachedText : this.screenshotButtonText)), h("button", { class: "feedback-modal-button feedback-modal-button--submit", type: "submit", disabled: this.sending }, this.sendButtonText)))) : this.formSuccess && !this.formError ? (h("div", { class: "feedback-modal-success" }, 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" }, h("div", { class: "feedback-logo", style: { display: this.whitelabel ? 'none' : 'block' } }, "Powered by", ' ', h("a", { target: "_blank", href: "https://pushfeedback.com" }, "PushFeedback.com")), this.footerText && (h("div", { class: "feedback-footer-text" }, h("span", { innerHTML: this.footerText })))))), this.showCanvasEditor && (h("div", { class: "canvas-editor-overlay" }, h("div", { class: "canvas-editor-modal" }, h("div", { class: "canvas-editor-header" }, h("div", { class: "canvas-editor-title" }, h("h3", null, this.canvasEditorTitle)), h("div", { class: "canvas-editor-toolbar" }, h("div", { class: "toolbar-section" }, h("div", { class: "tool-group" }, h("button", { class: `tool-btn ${this.canvasDrawingTool === 'rectangle' ? 'active' : ''}`, onClick: () => this.canvasDrawingTool = 'rectangle', title: "Rectangle" }, h("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, h("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2", ry: "2" }))), h("button", { class: `tool-btn ${this.canvasDrawingTool === 'line' ? 'active' : ''}`, onClick: () => this.canvasDrawingTool = 'line', title: "Line" }, h("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, h("line", { x1: "5", y1: "12", x2: "19", y2: "12" }))), h("button", { class: `tool-btn ${this.canvasDrawingTool === 'arrow' ? 'active' : ''}`, onClick: () => this.canvasDrawingTool = 'arrow', title: "Arrow" }, h("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, h("line", { x1: "7", y1: "17", x2: "17", y2: "7" }), h("polyline", { points: "7,7 17,7 17,17" }))), h("button", { class: `tool-btn ${this.canvasDrawingTool === 'text' ? 'active' : ''}`, onClick: () => this.canvasDrawingTool = 'text', title: "Text" }, h("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, h("polyline", { points: "4,7 4,4 20,4 20,7" }), h("line", { x1: "9", y1: "20", x2: "15", y2: "20" }), h("line", { x1: "12", y1: "4", x2: "12", y2: "20" }))), h("div", { class: "toolbar-divider" }), h("button", { class: "tool-btn undo-btn", onClick: this.undoLastAnnotation, disabled: this.annotations.length === 0, title: "Undo" }, h("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, h("polyline", { points: "1,4 1,10 7,10" }), 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" }))))), h("div", { class: "toolbar-section" }, h("div", { class: "color-palette" }, this.defaultColors.map((color, index) => (h("div", { class: "color-slot-wrapper" }, h("button", { class: `color-btn ${this.canvasDrawingColor === color ? 'active' : ''} ${this.editingColorIndex === index ? 'editing' : ''}`, style: { backgroundColor: color }, onClick: () => this.handleColorSlotClick(index), title: `Color ${index + 1} - Click to customize` }, this.editingColorIndex === index && (h("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "white", "stroke-width": "2" }, 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 && this.showColorPicker && (h("div", { class: "color-picker-dropdown" }, h("input", { type: "color", value: color, onInput: (e) => this.handleColorPickerInput(e), onClick: (e) => this.handleColorPickerClick(e) })))))))), h("div", { class: "toolbar-section" }, h("div", { class: "size-control" }, h("input", { type: "range", min: "1", max: "10", value: this.canvasLineWidth, onInput: (e) => this.canvasLineWidth = parseInt(e.target.value), class: "size-slider" }), h("span", { class: "size-value" }, this.canvasLineWidth, "px"))), h("div", { class: "toolbar-section" }, h("button", { class: "action-btn secondary", onClick: this.closeCanvasEditor }, this.canvasEditorCancelText), h("button", { class: "action-btn primary", onClick: this.saveAnnotations }, this.canvasEditorSaveText))), h("div", { class: "canvas-editor-content" }, h("canvas", { ref: (el) => this.canvasRef = el, class: "annotation-canvas", onMouseDown: this.handleCanvasMouseDown, onMouseMove: this.handleCanvasMouseMove, onMouseUp: this.handleCanvasMouseUp, onMouseLeave: this.handleCanvasMouseUp }))))))));
896
- }
897
- componentDidRender() {
898
- if (this.showModal) {
899
- requestAnimationFrame(() => {
900
- this.overlayVisible = true;
901
- });
902
- }
903
- }
904
- async openModal() {
905
- this.showModal = true;
906
- requestAnimationFrame(() => {
907
- requestAnimationFrame(() => {
908
- this.isAnimating = true;
909
- });
910
- });
911
- }
912
- static get is() { return "feedback-modal"; }
913
- static get encapsulation() { return "shadow"; }
914
- static get originalStyleUrls() {
915
- return {
916
- "$": ["feedback-modal.css"]
917
- };
918
- }
919
- static get styleUrls() {
920
- return {
921
- "$": ["feedback-modal.css"]
922
- };
923
- }
924
- static get properties() {
925
- return {
926
- "customFont": {
927
- "type": "boolean",
928
- "mutable": false,
929
- "complexType": {
930
- "original": "boolean",
931
- "resolved": "boolean",
932
- "references": {}
933
- },
934
- "required": false,
935
- "optional": false,
936
- "docs": {
937
- "tags": [],
938
- "text": ""
939
- },
940
- "attribute": "custom-font",
941
- "reflect": false,
942
- "defaultValue": "false"
943
- },
944
- "emailAddress": {
945
- "type": "string",
946
- "mutable": false,
947
- "complexType": {
948
- "original": "string",
949
- "resolved": "string",
950
- "references": {}
951
- },
952
- "required": false,
953
- "optional": false,
954
- "docs": {
955
- "tags": [],
956
- "text": ""
957
- },
958
- "attribute": "email-address",
959
- "reflect": false,
960
- "defaultValue": "''"
961
- },
962
- "hideEmail": {
963
- "type": "boolean",
964
- "mutable": false,
965
- "complexType": {
966
- "original": "boolean",
967
- "resolved": "boolean",
968
- "references": {}
969
- },
970
- "required": false,
971
- "optional": false,
972
- "docs": {
973
- "tags": [],
974
- "text": ""
975
- },
976
- "attribute": "hide-email",
977
- "reflect": false,
978
- "defaultValue": "false"
979
- },
980
- "isEmailRequired": {
981
- "type": "boolean",
982
- "mutable": false,
983
- "complexType": {
984
- "original": "boolean",
985
- "resolved": "boolean",
986
- "references": {}
987
- },
988
- "required": false,
989
- "optional": false,
990
- "docs": {
991
- "tags": [],
992
- "text": ""
993
- },
994
- "attribute": "is-email-required",
995
- "reflect": false,
996
- "defaultValue": "false"
997
- },
998
- "ratingMode": {
999
- "type": "string",
1000
- "mutable": false,
1001
- "complexType": {
1002
- "original": "string",
1003
- "resolved": "string",
1004
- "references": {}
1005
- },
1006
- "required": false,
1007
- "optional": false,
1008
- "docs": {
1009
- "tags": [],
1010
- "text": ""
1011
- },
1012
- "attribute": "rating-mode",
1013
- "reflect": false,
1014
- "defaultValue": "'thumbs'"
1015
- },
1016
- "hasSelectedElement": {
1017
- "type": "boolean",
1018
- "mutable": true,
1019
- "complexType": {
1020
- "original": "boolean",
1021
- "resolved": "boolean",
1022
- "references": {}
1023
- },
1024
- "required": false,
1025
- "optional": false,
1026
- "docs": {
1027
- "tags": [],
1028
- "text": ""
1029
- },
1030
- "attribute": "has-selected-element",
1031
- "reflect": true,
1032
- "defaultValue": "false"
1033
- },
1034
- "hidePrivacyPolicy": {
1035
- "type": "boolean",
1036
- "mutable": false,
1037
- "complexType": {
1038
- "original": "boolean",
1039
- "resolved": "boolean",
1040
- "references": {}
1041
- },
1042
- "required": false,
1043
- "optional": false,
1044
- "docs": {
1045
- "tags": [],
1046
- "text": ""
1047
- },
1048
- "attribute": "hide-privacy-policy",
1049
- "reflect": false,
1050
- "defaultValue": "true"
1051
- },
1052
- "hideRating": {
1053
- "type": "boolean",
1054
- "mutable": false,
1055
- "complexType": {
1056
- "original": "boolean",
1057
- "resolved": "boolean",
1058
- "references": {}
1059
- },
1060
- "required": false,
1061
- "optional": false,
1062
- "docs": {
1063
- "tags": [],
1064
- "text": ""
1065
- },
1066
- "attribute": "hide-rating",
1067
- "reflect": false,
1068
- "defaultValue": "false"
1069
- },
1070
- "hideScreenshotButton": {
1071
- "type": "boolean",
1072
- "mutable": false,
1073
- "complexType": {
1074
- "original": "boolean",
1075
- "resolved": "boolean",
1076
- "references": {}
1077
- },
1078
- "required": false,
1079
- "optional": false,
1080
- "docs": {
1081
- "tags": [],
1082
- "text": ""
1083
- },
1084
- "attribute": "hide-screenshot-button",
1085
- "reflect": false,
1086
- "defaultValue": "false"
1087
- },
1088
- "project": {
1089
- "type": "string",
1090
- "mutable": false,
1091
- "complexType": {
1092
- "original": "string",
1093
- "resolved": "string",
1094
- "references": {}
1095
- },
1096
- "required": false,
1097
- "optional": false,
1098
- "docs": {
1099
- "tags": [],
1100
- "text": ""
1101
- },
1102
- "attribute": "project",
1103
- "reflect": false,
1104
- "defaultValue": "''"
1105
- },
1106
- "showScreenshotMode": {
1107
- "type": "boolean",
1108
- "mutable": true,
1109
- "complexType": {
1110
- "original": "boolean",
1111
- "resolved": "boolean",
1112
- "references": {}
1113
- },
1114
- "required": false,
1115
- "optional": false,
1116
- "docs": {
1117
- "tags": [],
1118
- "text": ""
1119
- },
1120
- "attribute": "show-screenshot-mode",
1121
- "reflect": true,
1122
- "defaultValue": "false"
1123
- },
1124
- "showScreenshotTopBar": {
1125
- "type": "boolean",
1126
- "mutable": true,
1127
- "complexType": {
1128
- "original": "boolean",
1129
- "resolved": "boolean",
1130
- "references": {}
1131
- },
1132
- "required": false,
1133
- "optional": false,
1134
- "docs": {
1135
- "tags": [],
1136
- "text": ""
1137
- },
1138
- "attribute": "show-screenshot-top-bar",
1139
- "reflect": true,
1140
- "defaultValue": "false"
1141
- },
1142
- "showModal": {
1143
- "type": "boolean",
1144
- "mutable": true,
1145
- "complexType": {
1146
- "original": "boolean",
1147
- "resolved": "boolean",
1148
- "references": {}
1149
- },
1150
- "required": false,
1151
- "optional": false,
1152
- "docs": {
1153
- "tags": [],
1154
- "text": ""
1155
- },
1156
- "attribute": "show-modal",
1157
- "reflect": true,
1158
- "defaultValue": "false"
1159
- },
1160
- "rating": {
1161
- "type": "number",
1162
- "mutable": false,
1163
- "complexType": {
1164
- "original": "number",
1165
- "resolved": "number",
1166
- "references": {}
1167
- },
1168
- "required": false,
1169
- "optional": false,
1170
- "docs": {
1171
- "tags": [],
1172
- "text": ""
1173
- },
1174
- "attribute": "rating",
1175
- "reflect": false
1176
- },
1177
- "metadata": {
1178
- "type": "string",
1179
- "mutable": false,
1180
- "complexType": {
1181
- "original": "''",
1182
- "resolved": "\"\"",
1183
- "references": {}
1184
- },
1185
- "required": false,
1186
- "optional": false,
1187
- "docs": {
1188
- "tags": [],
1189
- "text": ""
1190
- },
1191
- "attribute": "metadata",
1192
- "reflect": false
1193
- },
1194
- "fetchData": {
1195
- "type": "boolean",
1196
- "mutable": false,
1197
- "complexType": {
1198
- "original": "boolean",
1199
- "resolved": "boolean",
1200
- "references": {}
1201
- },
1202
- "required": false,
1203
- "optional": false,
1204
- "docs": {
1205
- "tags": [],
1206
- "text": ""
1207
- },
1208
- "attribute": "fetch-data",
1209
- "reflect": false,
1210
- "defaultValue": "true"
1211
- },
1212
- "emailPlaceholder": {
1213
- "type": "string",
1214
- "mutable": false,
1215
- "complexType": {
1216
- "original": "string",
1217
- "resolved": "string",
1218
- "references": {}
1219
- },
1220
- "required": false,
1221
- "optional": false,
1222
- "docs": {
1223
- "tags": [],
1224
- "text": ""
1225
- },
1226
- "attribute": "email-placeholder",
1227
- "reflect": false,
1228
- "defaultValue": "'Email address (optional)'"
1229
- },
1230
- "errorMessage": {
1231
- "type": "string",
1232
- "mutable": false,
1233
- "complexType": {
1234
- "original": "string",
1235
- "resolved": "string",
1236
- "references": {}
1237
- },
1238
- "required": false,
1239
- "optional": false,
1240
- "docs": {
1241
- "tags": [],
1242
- "text": ""
1243
- },
1244
- "attribute": "error-message",
1245
- "reflect": false,
1246
- "defaultValue": "'Please try again later.'"
1247
- },
1248
- "errorMessage403": {
1249
- "type": "string",
1250
- "mutable": false,
1251
- "complexType": {
1252
- "original": "string",
1253
- "resolved": "string",
1254
- "references": {}
1255
- },
1256
- "required": false,
1257
- "optional": false,
1258
- "docs": {
1259
- "tags": [],
1260
- "text": ""
1261
- },
1262
- "attribute": "error-message-4-0-3",
1263
- "reflect": false,
1264
- "defaultValue": "'The request URL does not match the one defined in PushFeedback for this project.'"
1265
- },
1266
- "errorMessage404": {
1267
- "type": "string",
1268
- "mutable": false,
1269
- "complexType": {
1270
- "original": "string",
1271
- "resolved": "string",
1272
- "references": {}
1273
- },
1274
- "required": false,
1275
- "optional": false,
1276
- "docs": {
1277
- "tags": [],
1278
- "text": ""
1279
- },
1280
- "attribute": "error-message-4-0-4",
1281
- "reflect": false,
1282
- "defaultValue": "'We could not find the provided project ID in PushFeedback.'"
1283
- },
1284
- "messagePlaceholder": {
1285
- "type": "string",
1286
- "mutable": false,
1287
- "complexType": {
1288
- "original": "string",
1289
- "resolved": "string",
1290
- "references": {}
1291
- },
1292
- "required": false,
1293
- "optional": false,
1294
- "docs": {
1295
- "tags": [],
1296
- "text": ""
1297
- },
1298
- "attribute": "message-placeholder",
1299
- "reflect": false,
1300
- "defaultValue": "'Comments'"
1301
- },
1302
- "footerText": {
1303
- "type": "string",
1304
- "mutable": false,
1305
- "complexType": {
1306
- "original": "string",
1307
- "resolved": "string",
1308
- "references": {}
1309
- },
1310
- "required": false,
1311
- "optional": false,
1312
- "docs": {
1313
- "tags": [],
1314
- "text": ""
1315
- },
1316
- "attribute": "footer-text",
1317
- "reflect": false,
1318
- "defaultValue": "''"
1319
- },
1320
- "modalPosition": {
1321
- "type": "string",
1322
- "mutable": false,
1323
- "complexType": {
1324
- "original": "string",
1325
- "resolved": "string",
1326
- "references": {}
1327
- },
1328
- "required": false,
1329
- "optional": false,
1330
- "docs": {
1331
- "tags": [],
1332
- "text": ""
1333
- },
1334
- "attribute": "modal-position",
1335
- "reflect": false,
1336
- "defaultValue": "'center'"
1337
- },
1338
- "modalTitle": {
1339
- "type": "string",
1340
- "mutable": false,
1341
- "complexType": {
1342
- "original": "string",
1343
- "resolved": "string",
1344
- "references": {}
1345
- },
1346
- "required": false,
1347
- "optional": false,
1348
- "docs": {
1349
- "tags": [],
1350
- "text": ""
1351
- },
1352
- "attribute": "modal-title",
1353
- "reflect": false,
1354
- "defaultValue": "'Share your feedback'"
1355
- },
1356
- "modalTitleError": {
1357
- "type": "string",
1358
- "mutable": false,
1359
- "complexType": {
1360
- "original": "string",
1361
- "resolved": "string",
1362
- "references": {}
1363
- },
1364
- "required": false,
1365
- "optional": false,
1366
- "docs": {
1367
- "tags": [],
1368
- "text": ""
1369
- },
1370
- "attribute": "modal-title-error",
1371
- "reflect": false,
1372
- "defaultValue": "'Oops!'"
1373
- },
1374
- "modalTitleSuccess": {
1375
- "type": "string",
1376
- "mutable": false,
1377
- "complexType": {
1378
- "original": "string",
1379
- "resolved": "string",
1380
- "references": {}
1381
- },
1382
- "required": false,
1383
- "optional": false,
1384
- "docs": {
1385
- "tags": [],
1386
- "text": ""
1387
- },
1388
- "attribute": "modal-title-success",
1389
- "reflect": false,
1390
- "defaultValue": "'Thanks for your feedback!'"
1391
- },
1392
- "privacyPolicyText": {
1393
- "type": "string",
1394
- "mutable": false,
1395
- "complexType": {
1396
- "original": "string",
1397
- "resolved": "string",
1398
- "references": {}
1399
- },
1400
- "required": false,
1401
- "optional": false,
1402
- "docs": {
1403
- "tags": [],
1404
- "text": ""
1405
- },
1406
- "attribute": "privacy-policy-text",
1407
- "reflect": false,
1408
- "defaultValue": "\"I have read and expressly consent to the terms of the <a href='https://pushfeedback.com/privacy'>Privacy Policy</a>.\""
1409
- },
1410
- "ratingPlaceholder": {
1411
- "type": "string",
1412
- "mutable": false,
1413
- "complexType": {
1414
- "original": "string",
1415
- "resolved": "string",
1416
- "references": {}
1417
- },
1418
- "required": false,
1419
- "optional": false,
1420
- "docs": {
1421
- "tags": [],
1422
- "text": ""
1423
- },
1424
- "attribute": "rating-placeholder",
1425
- "reflect": false,
1426
- "defaultValue": "'Was this page helpful?'"
1427
- },
1428
- "ratingStarsPlaceholder": {
1429
- "type": "string",
1430
- "mutable": false,
1431
- "complexType": {
1432
- "original": "string",
1433
- "resolved": "string",
1434
- "references": {}
1435
- },
1436
- "required": false,
1437
- "optional": false,
1438
- "docs": {
1439
- "tags": [],
1440
- "text": ""
1441
- },
1442
- "attribute": "rating-stars-placeholder",
1443
- "reflect": false,
1444
- "defaultValue": "'How would you rate this page?'"
1445
- },
1446
- "sendButtonText": {
1447
- "type": "string",
1448
- "mutable": false,
1449
- "complexType": {
1450
- "original": "string",
1451
- "resolved": "string",
1452
- "references": {}
1453
- },
1454
- "required": false,
1455
- "optional": false,
1456
- "docs": {
1457
- "tags": [],
1458
- "text": ""
1459
- },
1460
- "attribute": "send-button-text",
1461
- "reflect": false,
1462
- "defaultValue": "'Send'"
1463
- },
1464
- "screenshotAttachedText": {
1465
- "type": "string",
1466
- "mutable": false,
1467
- "complexType": {
1468
- "original": "string",
1469
- "resolved": "string",
1470
- "references": {}
1471
- },
1472
- "required": false,
1473
- "optional": false,
1474
- "docs": {
1475
- "tags": [],
1476
- "text": ""
1477
- },
1478
- "attribute": "screenshot-attached-text",
1479
- "reflect": false,
1480
- "defaultValue": "'Screenshot attached'"
1481
- },
1482
- "screenshotButtonText": {
1483
- "type": "string",
1484
- "mutable": false,
1485
- "complexType": {
1486
- "original": "string",
1487
- "resolved": "string",
1488
- "references": {}
1489
- },
1490
- "required": false,
1491
- "optional": false,
1492
- "docs": {
1493
- "tags": [],
1494
- "text": ""
1495
- },
1496
- "attribute": "screenshot-button-text",
1497
- "reflect": false,
1498
- "defaultValue": "'Add a screenshot'"
1499
- },
1500
- "screenshotTakingText": {
1501
- "type": "string",
1502
- "mutable": false,
1503
- "complexType": {
1504
- "original": "string",
1505
- "resolved": "string",
1506
- "references": {}
1507
- },
1508
- "required": false,
1509
- "optional": false,
1510
- "docs": {
1511
- "tags": [],
1512
- "text": ""
1513
- },
1514
- "attribute": "screenshot-taking-text",
1515
- "reflect": false,
1516
- "defaultValue": "'Taking screenshot...'"
1517
- },
1518
- "screenshotTopbarText": {
1519
- "type": "string",
1520
- "mutable": false,
1521
- "complexType": {
1522
- "original": "string",
1523
- "resolved": "string",
1524
- "references": {}
1525
- },
1526
- "required": false,
1527
- "optional": false,
1528
- "docs": {
1529
- "tags": [],
1530
- "text": ""
1531
- },
1532
- "attribute": "screenshot-topbar-text",
1533
- "reflect": false,
1534
- "defaultValue": "'Select an element on this page'"
1535
- },
1536
- "successMessage": {
1537
- "type": "string",
1538
- "mutable": false,
1539
- "complexType": {
1540
- "original": "string",
1541
- "resolved": "string",
1542
- "references": {}
1543
- },
1544
- "required": false,
1545
- "optional": false,
1546
- "docs": {
1547
- "tags": [],
1548
- "text": ""
1549
- },
1550
- "attribute": "success-message",
1551
- "reflect": false,
1552
- "defaultValue": "''"
1553
- },
1554
- "canvasEditorTitle": {
1555
- "type": "string",
1556
- "mutable": false,
1557
- "complexType": {
1558
- "original": "string",
1559
- "resolved": "string",
1560
- "references": {}
1561
- },
1562
- "required": false,
1563
- "optional": false,
1564
- "docs": {
1565
- "tags": [],
1566
- "text": ""
1567
- },
1568
- "attribute": "canvas-editor-title",
1569
- "reflect": false,
1570
- "defaultValue": "'Edit screenshot'"
1571
- },
1572
- "canvasEditorCancelText": {
1573
- "type": "string",
1574
- "mutable": false,
1575
- "complexType": {
1576
- "original": "string",
1577
- "resolved": "string",
1578
- "references": {}
1579
- },
1580
- "required": false,
1581
- "optional": false,
1582
- "docs": {
1583
- "tags": [],
1584
- "text": ""
1585
- },
1586
- "attribute": "canvas-editor-cancel-text",
1587
- "reflect": false,
1588
- "defaultValue": "'Cancel'"
1589
- },
1590
- "canvasEditorSaveText": {
1591
- "type": "string",
1592
- "mutable": false,
1593
- "complexType": {
1594
- "original": "string",
1595
- "resolved": "string",
1596
- "references": {}
1597
- },
1598
- "required": false,
1599
- "optional": false,
1600
- "docs": {
1601
- "tags": [],
1602
- "text": ""
1603
- },
1604
- "attribute": "canvas-editor-save-text",
1605
- "reflect": false,
1606
- "defaultValue": "'Save'"
1607
- }
1608
- };
1609
- }
1610
- static get states() {
1611
- return {
1612
- "sending": {},
1613
- "formMessage": {},
1614
- "formEmail": {},
1615
- "formSuccess": {},
1616
- "formVerification": {},
1617
- "formError": {},
1618
- "formErrorStatus": {},
1619
- "encodedScreenshot": {},
1620
- "isPrivacyChecked": {},
1621
- "whitelabel": {},
1622
- "selectedRating": {},
1623
- "overlayVisible": {},
1624
- "isAnimating": {},
1625
- "takingScreenshot": {},
1626
- "showPreviewModal": {},
1627
- "showCanvasEditor": {},
1628
- "canvasDrawingTool": {},
1629
- "canvasDrawingColor": {},
1630
- "canvasLineWidth": {},
1631
- "isDrawing": {},
1632
- "annotations": {},
1633
- "currentAnnotation": {},
1634
- "isDragging": {},
1635
- "draggedAnnotation": {},
1636
- "dragStartPos": {},
1637
- "showColorPicker": {},
1638
- "editingColorIndex": {},
1639
- "isResizing": {},
1640
- "resizingAnnotation": {},
1641
- "resizeStartSize": {},
1642
- "hoveredAnnotation": {},
1643
- "resizeHandle": {},
1644
- "defaultColors": {}
1645
- };
1646
- }
1647
- static get events() {
1648
- return [{
1649
- "method": "feedbackSent",
1650
- "name": "feedbackSent",
1651
- "bubbles": true,
1652
- "cancelable": true,
1653
- "composed": true,
1654
- "docs": {
1655
- "tags": [],
1656
- "text": ""
1657
- },
1658
- "complexType": {
1659
- "original": "{ feedback: any }",
1660
- "resolved": "{ feedback: any; }",
1661
- "references": {}
1662
- }
1663
- }, {
1664
- "method": "feedbackError",
1665
- "name": "feedbackError",
1666
- "bubbles": true,
1667
- "cancelable": true,
1668
- "composed": true,
1669
- "docs": {
1670
- "tags": [],
1671
- "text": ""
1672
- },
1673
- "complexType": {
1674
- "original": "{ error: any }",
1675
- "resolved": "{ error: any; }",
1676
- "references": {}
1677
- }
1678
- }];
1679
- }
1680
- static get methods() {
1681
- return {
1682
- "openModal": {
1683
- "complexType": {
1684
- "signature": "() => Promise<void>",
1685
- "parameters": [],
1686
- "references": {
1687
- "Promise": {
1688
- "location": "global"
1689
- }
1690
- },
1691
- "return": "Promise<void>"
1692
- },
1693
- "docs": {
1694
- "text": "",
1695
- "tags": []
1696
- }
1697
- }
1698
- };
1699
- }
1700
- }