pushfeedback 0.1.84 → 0.1.85
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/canvas-editor_3.cjs.entry.js +393 -41
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/cjs/pushfeedback.cjs.js +1 -1
- package/dist/collection/components/canvas-editor/canvas-editor.js +173 -15
- package/dist/collection/components/feedback-button/feedback-button.css +45 -1
- package/dist/collection/components/feedback-button/feedback-button.js +58 -8
- package/dist/collection/components/feedback-modal/feedback-modal.css +6 -0
- package/dist/collection/components/feedback-modal/feedback-modal.js +272 -17
- package/dist/collection/utils/sanitize-html.js +90 -0
- package/dist/components/canvas-editor2.js +139 -15
- package/dist/components/feedback-button.js +24 -8
- package/dist/components/feedback-modal2.js +241 -18
- package/dist/esm/canvas-editor_3.entry.js +393 -41
- package/dist/esm/loader.js +1 -1
- package/dist/esm/pushfeedback.js +1 -1
- package/dist/pushfeedback/p-17acbd1c.entry.js +1 -0
- package/dist/pushfeedback/pushfeedback.esm.js +1 -1
- package/dist/types/components/canvas-editor/canvas-editor.d.ts +7 -0
- package/dist/types/components/feedback-button/feedback-button.d.ts +14 -0
- package/dist/types/components/feedback-modal/feedback-modal.d.ts +24 -0
- package/dist/types/components.d.ts +40 -0
- package/dist/types/utils/sanitize-html.d.ts +8 -0
- package/package.json +2 -1
- package/dist/pushfeedback/p-06bb93cb.entry.js +0 -1
|
@@ -2,12 +2,59 @@ import { r as registerInstance, c as createEvent, h, H as Host, g as getElement
|
|
|
2
2
|
|
|
3
3
|
const canvasEditorCss = ":host{display:block}.canvas-editor-wrapper{position:relative}.canvas-editor-overlay{position:fixed;top:0;left:0;width:100vw;height:100vh;background:rgba(0, 0, 0, 0.8);display:flex;align-items:center;justify-content:center;z-index:9999}.canvas-editor-modal{width:95vw;height:95vh;max-width:none;max-height:none;background:var(--feedback-canvas-editor-bg-color, #ffffff);border-radius:12px;border:1px solid rgba(0, 0, 0, 0.08);display:flex;flex-direction:column;overflow:hidden;box-shadow:0 0 0 1px rgba(0, 0, 0, 0.02),\n 0 8px 16px rgba(0, 0, 0, 0.08),\n 0 16px 32px rgba(0, 0, 0, 0.08),\n 0 32px 64px rgba(0, 0, 0, 0.06)}.canvas-editor-header{background:var(--feedback-canvas-editor-header-bg-color, #fafafa);border-bottom:1px solid rgba(0, 0, 0, 0.06);padding:16px 20px;display:flex;flex-direction:column;gap:16px}.canvas-editor-title h3{margin:0;font-size:18px;font-weight:600;color:var(--feedback-canvas-editor-tool-text-color, #1a1a1a);letter-spacing:-0.02em}.canvas-editor-toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:16px}.toolbar-section{display:flex;align-items:center;gap:8px}.tool-group{display:flex;align-items:center;gap:4px}.tool-btn{width:36px;height:36px;display:flex;align-items:center;justify-content:center;background:var(--feedback-canvas-editor-tool-bg-color, #ffffff);border:1.5px solid rgba(0, 0, 0, 0.08);border-radius:8px;cursor:pointer;transition:all 0.2s cubic-bezier(0.4, 0, 0.2, 1);padding:0;box-shadow:0 1px 2px rgba(0, 0, 0, 0.04)}.tool-btn svg{width:18px;height:18px;color:var(--feedback-canvas-editor-tool-text-color, #333)}.tool-btn:hover:not(:disabled){background:var(--feedback-canvas-editor-tool-bg-hover, #f5f5f5);border-color:rgba(0, 0, 0, 0.12);transform:translateY(-1px);box-shadow:0 2px 4px rgba(0, 0, 0, 0.08)}.tool-btn.active,.tool-btn.active:hover{background:var(--feedback-canvas-editor-tool-bg-active, #0070f4);color:var(--feedback-canvas-editor-tool-text-active, #ffffff);border-color:var(--feedback-canvas-editor-tool-bg-active, #0070f4);box-shadow:0 2px 8px rgba(0, 112, 244, 0.3)}.tool-btn.active svg{color:var(--feedback-canvas-editor-tool-text-active, #ffffff)}.tool-btn:disabled{opacity:0.4;cursor:not-allowed;color:var(--feedback-canvas-editor-tool-text-color, #333)}.toolbar-divider{width:1px;height:24px;background:var(--feedback-canvas-editor-divider-color, #e0e0e0);margin:0 4px}.undo-btn,.delete-btn{background:var(--feedback-canvas-editor-tool-bg-color, #ffffff) !important;border:1px solid var(--feedback-canvas-editor-border-color, #e0e0e0) !important}.undo-btn:hover:not(:disabled),.delete-btn:hover:not(:disabled){background:var(--feedback-canvas-editor-tool-bg-hover, #f0f0f0) !important}.undo-btn:disabled,.delete-btn:disabled{opacity:0.3;cursor:not-allowed}.delete-btn:hover:not(:disabled){background:#fee !important;border-color:#fcc !important}.delete-btn:hover:not(:disabled) svg{color:#c53030}.color-palette{display:flex;align-items:center;gap:6px}.color-slot-wrapper{position:relative;display:flex;align-items:center}.color-btn{width:32px;height:32px;border-radius:8px;border:2px solid transparent;cursor:pointer;transition:all 0.2s cubic-bezier(0.4, 0, 0.2, 1);display:flex;align-items:center;justify-content:center;background:var(--feedback-canvas-editor-tool-bg-color, #ffffff);border:2px solid rgba(0, 0, 0, 0.08);box-shadow:0 1px 2px rgba(0, 0, 0, 0.04)}.color-btn:hover{transform:translateY(-2px) scale(1.05);box-shadow:0 4px 8px rgba(0, 0, 0, 0.12)}.color-btn.active{border-color:var(--feedback-primary-color, #0070f4);box-shadow:0 0 0 2px rgba(0, 112, 244, 0.2)}.color-btn.editing{border-color:var(--feedback-primary-color, #0070f4)}.color-picker-dropdown{position:absolute;top:100%;left:0;z-index:1000;margin-top:4px;background:var(--feedback-canvas-editor-tool-bg-color, #ffffff);border:1px solid var(--feedback-canvas-editor-border-color, #e0e0e0);border-radius:6px;padding:8px;box-shadow:0 4px 12px rgba(0, 0, 0, 0.1)}.color-picker-dropdown input[type=\"color\"]{width:40px;height:40px;border:none;border-radius:4px;cursor:pointer}.size-control{display:flex;align-items:center;gap:8px;background:var(--feedback-canvas-editor-tool-bg-color, #ffffff);border:1px solid var(--feedback-canvas-editor-border-color, #e0e0e0);border-radius:6px;padding:6px 12px}.size-slider{width:80px;height:14px;-webkit-appearance:none;appearance:none;background:transparent;border-radius:2px;outline:none;cursor:pointer;border:none;position:relative}.size-slider::before{content:'';position:absolute;top:50%;left:0;right:0;height:4px;background:rgba(0, 0, 0, 0.15);border-radius:2px;transform:translateY(-50%);pointer-events:none}.size-slider::-webkit-slider-track{width:100%;height:4px;background:transparent;border-radius:2px;border:none}.size-slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:14px;height:14px;background:var(--feedback-primary-color, #0070f4);border-radius:50%;cursor:pointer;position:relative;z-index:1}.size-slider::-moz-range-track{width:100%;height:4px;background:transparent;border-radius:2px;border:none}.size-slider::-moz-range-thumb{width:14px;height:14px;background:var(--feedback-primary-color, #0070f4);border-radius:50%;border:none;cursor:pointer}.size-slider::-ms-track{width:100%;height:4px;background:transparent;border-radius:2px;border:none;color:transparent}.size-slider::-ms-thumb{width:14px;height:14px;background:var(--feedback-primary-color, #0070f4);border-radius:50%;border:none;cursor:pointer}.size-value{font-weight:500;color:var(--feedback-canvas-editor-tool-text-color, #333);font-size:12px;min-width:30px}.selected-annotation-controls{border-left:2px solid var(--feedback-canvas-editor-divider-color, #e0e0e0);padding-left:12px;margin-left:8px;min-width:200px}.text-controls{display:flex;flex-direction:row;align-items:center;gap:12px}.font-size-control,.border-width-control{display:flex;align-items:center;gap:6px}.font-size-control label,.border-width-control label{font-size:12px;color:var(--feedback-canvas-editor-tool-text-color, #333);font-weight:500;min-width:35px}.action-btn{display:flex;align-items:center;gap:8px;padding:0 20px;border:1.5px solid rgba(0, 0, 0, 0.08);border-radius:8px;cursor:pointer;font-size:14px;font-weight:500;transition:all 0.2s cubic-bezier(0.4, 0, 0.2, 1);min-width:80px;justify-content:center;height:40px;box-shadow:0 1px 2px rgba(0, 0, 0, 0.04)}.action-btn.secondary{background:var(--feedback-canvas-editor-tool-bg-color, #ffffff);color:var(--feedback-canvas-editor-tool-text-color, #333);border-color:rgba(0, 0, 0, 0.08)}.action-btn.secondary:hover{background:var(--feedback-canvas-editor-tool-bg-hover, #f5f5f5);border-color:rgba(0, 0, 0, 0.12);transform:translateY(-1px);box-shadow:0 4px 8px rgba(0, 0, 0, 0.08)}.action-btn.primary{background:var(--feedback-primary-color, #0070f4);color:#ffffff;border-color:var(--feedback-primary-color, #0070f4);box-shadow:0 2px 4px rgba(0, 112, 244, 0.2)}.action-btn.primary:hover{background:#0056cc;border-color:#0056cc;transform:translateY(-1px);box-shadow:0 6px 12px rgba(0, 112, 244, 0.3)}.action-btn:active{transform:translateY(0)}.action-btn.small{height:28px;padding:4px 8px;font-size:12px;min-width:65px}.shape-controls{display:flex;flex-direction:column;gap:8px}.canvas-editor-content{flex:1;display:flex;align-items:center;justify-content:center;padding:24px;background:var(--feedback-canvas-editor-content-bg, #f5f5f5);overflow:hidden;min-height:0;min-width:0;position:relative}.canvas-editor-content::-webkit-scrollbar{width:8px;height:8px}.canvas-editor-content::-webkit-scrollbar-track{background:transparent}.canvas-editor-content::-webkit-scrollbar-thumb{background:rgba(0, 0, 0, 0.2);border-radius:4px}.canvas-editor-content::-webkit-scrollbar-thumb:hover{background:rgba(0, 0, 0, 0.3)}.annotation-canvas{max-width:calc(100% - 48px);max-height:calc(100% - 48px);width:auto !important;height:auto !important;cursor:crosshair;border-radius:8px;box-shadow:0 0 0 1px rgba(0, 0, 0, 0.04),\n 0 4px 6px rgba(0, 0, 0, 0.06),\n 0 8px 16px rgba(0, 0, 0, 0.08);background:#ffffff;transition:box-shadow 0.3s cubic-bezier(0.4, 0, 0.2, 1);object-fit:contain;display:block;margin:auto}.annotation-canvas:hover{box-shadow:0 0 0 1px rgba(0, 0, 0, 0.06),\n 0 6px 10px rgba(0, 0, 0, 0.08),\n 0 12px 24px rgba(0, 0, 0, 0.1)}@media screen and (min-width: 1920px){.canvas-editor-modal{width:90vw;height:90vh}.canvas-editor-content{padding:32px}}@media screen and (min-width: 1200px) and (max-width: 1919px){.canvas-editor-modal{width:92vw;height:92vh}.canvas-editor-content{padding:28px}}@media screen and (min-width: 769px) and (max-width: 1199px){.canvas-editor-modal{width:95vw;height:95vh}}@media screen and (max-width: 768px){.canvas-editor-modal{width:100vw;height:100vh;border-radius:0}.canvas-editor-toolbar{flex-direction:column;align-items:stretch;gap:8px}.toolbar-section{justify-content:center}.selected-annotation-controls{border-left:none;border-top:2px solid var(--feedback-canvas-editor-divider-color, #e0e0e0);padding-left:0;padding-top:8px;margin-left:0;margin-top:8px;min-width:auto}.canvas-editor-content{padding:16px}}";
|
|
4
4
|
|
|
5
|
+
// Upper bound on waiting for the capture stream to produce video metadata,
|
|
6
|
+
// after which the stream is stopped so it cannot leak
|
|
7
|
+
const CAPTURE_TIMEOUT_MS = 10000;
|
|
5
8
|
const CanvasEditor = class {
|
|
6
9
|
constructor(hostRef) {
|
|
7
10
|
registerInstance(this, hostRef);
|
|
8
11
|
this.screenshotReady = createEvent(this, "screenshotReady", 7);
|
|
9
12
|
this.screenshotCancelled = createEvent(this, "screenshotCancelled", 7);
|
|
10
13
|
this.screenshotFailed = createEvent(this, "screenshotFailed", 7);
|
|
14
|
+
this.handleKeyDown = (event) => {
|
|
15
|
+
if (!this.showCanvasEditor || !this.escapeClose || event.key !== 'Escape') {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
// While the browser is capturing the screen, the picker owns the keyboard
|
|
19
|
+
if (this.takingScreenshot) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
event.preventDefault();
|
|
23
|
+
// The colour picker is a popup on top of the editor, so Escape dismisses
|
|
24
|
+
// that first and leaves the editor (and any annotations) in place
|
|
25
|
+
if (this.showColorPicker) {
|
|
26
|
+
this.showColorPicker = false;
|
|
27
|
+
this.editingColorIndex = -1;
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
// Deselect before closing, the way drawing tools do. Closing throws away
|
|
31
|
+
// every annotation, so someone who has just selected one gets a cheap
|
|
32
|
+
// Escape that only drops the selection rather than their work
|
|
33
|
+
if (this.selectedAnnotation) {
|
|
34
|
+
this.selectedAnnotation = null;
|
|
35
|
+
this.redrawAnnotations();
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
// Same as the Cancel button: discards annotations and returns to the form
|
|
39
|
+
this.closeCanvasEditor();
|
|
40
|
+
};
|
|
41
|
+
// Tracks whether the press that started this click landed on the backdrop.
|
|
42
|
+
// Dragging an annotation and releasing past the edge of the editor fires a
|
|
43
|
+
// click on the backdrop too, and that must not be read as "clicked outside".
|
|
44
|
+
this.pressStartedOnOverlay = false;
|
|
45
|
+
this.handleOverlayMouseDown = (event) => {
|
|
46
|
+
this.pressStartedOnOverlay = event.target === event.currentTarget;
|
|
47
|
+
};
|
|
48
|
+
this.handleOverlayClick = (event) => {
|
|
49
|
+
const startedOutside = this.pressStartedOnOverlay;
|
|
50
|
+
this.pressStartedOnOverlay = false;
|
|
51
|
+
if (!this.clickOutsideClose || !startedOutside || event.target !== event.currentTarget) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
// Closes the editor only. The feedback form reopens behind it, so a stray
|
|
55
|
+
// click never dismisses the whole widget
|
|
56
|
+
this.closeCanvasEditor();
|
|
57
|
+
};
|
|
11
58
|
this.handleWindowResize = () => {
|
|
12
59
|
// Debounce resize events
|
|
13
60
|
if (this.resizeTimeout) {
|
|
@@ -687,8 +734,16 @@ const CanvasEditor = class {
|
|
|
687
734
|
if (!this.isDrawing || !this.currentAnnotation)
|
|
688
735
|
return;
|
|
689
736
|
this.isDrawing = false;
|
|
690
|
-
|
|
737
|
+
const annotation = this.currentAnnotation;
|
|
691
738
|
this.currentAnnotation = null;
|
|
739
|
+
// A click with no drag never reaches mousemove, so the shape has no end
|
|
740
|
+
// point or size. Committing it added an invisible annotation that could
|
|
741
|
+
// not be selected, dragged or deleted, and it still consumed an undo step.
|
|
742
|
+
if (this.isDegenerateAnnotation(annotation)) {
|
|
743
|
+
this.redrawAnnotations();
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
this.annotations = [...this.annotations, annotation];
|
|
692
747
|
this.redrawAnnotations();
|
|
693
748
|
};
|
|
694
749
|
// Convert screen coordinates to canvas coordinates
|
|
@@ -730,11 +785,28 @@ const CanvasEditor = class {
|
|
|
730
785
|
}
|
|
731
786
|
case 'line':
|
|
732
787
|
case 'arrow': {
|
|
733
|
-
// Distance from point to line
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
788
|
+
// Distance from the point to the line *segment*, not to the infinite
|
|
789
|
+
// line through it. The previous formula measured the infinite line, so
|
|
790
|
+
// a click far past either end still counted as a hit, and a zero-length
|
|
791
|
+
// segment divided by zero and gave NaN, leaving that annotation
|
|
792
|
+
// impossible to select, drag or delete.
|
|
793
|
+
// A click with no drag never reaches mousemove, so endX/endY are
|
|
794
|
+
// undefined rather than equal to the start. Treat that as a segment
|
|
795
|
+
// collapsed to its start point instead of letting NaN propagate.
|
|
796
|
+
const endX = Number.isFinite(annotation.endX) ? annotation.endX : annotation.startX;
|
|
797
|
+
const endY = Number.isFinite(annotation.endY) ? annotation.endY : annotation.startY;
|
|
798
|
+
const dx = endX - annotation.startX;
|
|
799
|
+
const dy = endY - annotation.startY;
|
|
800
|
+
const lengthSquared = dx * dx + dy * dy;
|
|
801
|
+
// Zero-length segment: t stays 0 and we measure to the single point
|
|
802
|
+
let t = 0;
|
|
803
|
+
if (lengthSquared > 0) {
|
|
804
|
+
t = ((x - annotation.startX) * dx + (y - annotation.startY) * dy) / lengthSquared;
|
|
805
|
+
t = Math.max(0, Math.min(1, t));
|
|
806
|
+
}
|
|
807
|
+
const closestX = annotation.startX + t * dx;
|
|
808
|
+
const closestY = annotation.startY + t * dy;
|
|
809
|
+
const distance = Math.hypot(x - closestX, y - closestY);
|
|
738
810
|
return distance <= tolerance;
|
|
739
811
|
}
|
|
740
812
|
case 'text': {
|
|
@@ -759,6 +831,8 @@ const CanvasEditor = class {
|
|
|
759
831
|
this.screenshotTakingText = 'Taking screenshot...';
|
|
760
832
|
this.screenshotAttachedText = 'Screenshot attached';
|
|
761
833
|
this.screenshotButtonText = 'Add a screenshot';
|
|
834
|
+
this.escapeClose = true;
|
|
835
|
+
this.clickOutsideClose = true;
|
|
762
836
|
this.autoStartScreenshot = false;
|
|
763
837
|
this.existingScreenshot = '';
|
|
764
838
|
this.editTextButtonText = 'Edit Text';
|
|
@@ -814,10 +888,12 @@ const CanvasEditor = class {
|
|
|
814
888
|
// Add window resize listener for canvas adaptation
|
|
815
889
|
this.handleWindowResize = this.handleWindowResize.bind(this);
|
|
816
890
|
window.addEventListener('resize', this.handleWindowResize);
|
|
891
|
+
document.addEventListener('keydown', this.handleKeyDown);
|
|
817
892
|
}
|
|
818
893
|
disconnectedCallback() {
|
|
819
894
|
// Clean up resize listener
|
|
820
895
|
window.removeEventListener('resize', this.handleWindowResize);
|
|
896
|
+
document.removeEventListener('keydown', this.handleKeyDown);
|
|
821
897
|
}
|
|
822
898
|
async captureViewportScreenshot() {
|
|
823
899
|
try {
|
|
@@ -841,6 +917,30 @@ const CanvasEditor = class {
|
|
|
841
917
|
video.autoplay = true;
|
|
842
918
|
video.muted = true;
|
|
843
919
|
return new Promise((resolve, reject) => {
|
|
920
|
+
// If neither onloadedmetadata nor onerror ever fires, the promise would
|
|
921
|
+
// never settle: the capture stream would stay live with the browser's
|
|
922
|
+
// recording indicator on, and the widget would sit in "Taking
|
|
923
|
+
// screenshot..." forever with no way back short of a page reload.
|
|
924
|
+
let settled = false;
|
|
925
|
+
const stopStream = () => {
|
|
926
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
927
|
+
};
|
|
928
|
+
const finish = (fn) => {
|
|
929
|
+
if (settled) {
|
|
930
|
+
return;
|
|
931
|
+
}
|
|
932
|
+
settled = true;
|
|
933
|
+
clearTimeout(watchdog);
|
|
934
|
+
video.onloadedmetadata = null;
|
|
935
|
+
video.onerror = null;
|
|
936
|
+
fn();
|
|
937
|
+
};
|
|
938
|
+
const watchdog = setTimeout(() => {
|
|
939
|
+
finish(() => {
|
|
940
|
+
stopStream();
|
|
941
|
+
reject(new Error('Screenshot capture timed out'));
|
|
942
|
+
});
|
|
943
|
+
}, CAPTURE_TIMEOUT_MS);
|
|
844
944
|
video.onloadedmetadata = () => {
|
|
845
945
|
video.play();
|
|
846
946
|
// Wait a moment for video to stabilize
|
|
@@ -852,22 +952,26 @@ const CanvasEditor = class {
|
|
|
852
952
|
canvas.height = video.videoHeight;
|
|
853
953
|
const ctx = canvas.getContext('2d');
|
|
854
954
|
ctx.drawImage(video, 0, 0);
|
|
855
|
-
// Stop the stream
|
|
856
|
-
stream.getTracks().forEach((track) => track.stop());
|
|
857
955
|
// Convert to data URL
|
|
858
956
|
const dataUrl = canvas.toDataURL('image/png');
|
|
859
|
-
|
|
860
|
-
|
|
957
|
+
finish(() => {
|
|
958
|
+
stopStream();
|
|
959
|
+
resolve(dataUrl);
|
|
960
|
+
});
|
|
861
961
|
}
|
|
862
962
|
catch (error) {
|
|
863
|
-
|
|
864
|
-
|
|
963
|
+
finish(() => {
|
|
964
|
+
stopStream();
|
|
965
|
+
reject(error);
|
|
966
|
+
});
|
|
865
967
|
}
|
|
866
968
|
}, 100);
|
|
867
969
|
};
|
|
868
970
|
video.onerror = () => {
|
|
869
|
-
|
|
870
|
-
|
|
971
|
+
finish(() => {
|
|
972
|
+
stopStream();
|
|
973
|
+
reject(new Error('Failed to load video for screenshot capture'));
|
|
974
|
+
});
|
|
871
975
|
};
|
|
872
976
|
});
|
|
873
977
|
}
|
|
@@ -876,9 +980,27 @@ const CanvasEditor = class {
|
|
|
876
980
|
throw error;
|
|
877
981
|
}
|
|
878
982
|
}
|
|
983
|
+
// Whether a freshly drawn shape is too small to be worth keeping
|
|
984
|
+
isDegenerateAnnotation(annotation) {
|
|
985
|
+
const MIN_SIZE = 2; // canvas pixels
|
|
986
|
+
if (annotation.type === 'line' || annotation.type === 'arrow') {
|
|
987
|
+
if (!Number.isFinite(annotation.endX) || !Number.isFinite(annotation.endY)) {
|
|
988
|
+
return true;
|
|
989
|
+
}
|
|
990
|
+
return (Math.hypot(annotation.endX - annotation.startX, annotation.endY - annotation.startY) <
|
|
991
|
+
MIN_SIZE);
|
|
992
|
+
}
|
|
993
|
+
if (annotation.type === 'rectangle') {
|
|
994
|
+
if (!Number.isFinite(annotation.width) || !Number.isFinite(annotation.height)) {
|
|
995
|
+
return true;
|
|
996
|
+
}
|
|
997
|
+
return Math.abs(annotation.width) < MIN_SIZE && Math.abs(annotation.height) < MIN_SIZE;
|
|
998
|
+
}
|
|
999
|
+
return false;
|
|
1000
|
+
}
|
|
879
1001
|
render() {
|
|
880
1002
|
var _a, _b, _c, _d, _e, _f;
|
|
881
|
-
return (h("div", { class: "canvas-editor-wrapper" }, this.showCanvasEditor && (h("div", { class: "canvas-editor-overlay", part: "overlay" }, h("div", { class: "canvas-editor-modal", part: "modal" }, h("div", { class: "canvas-editor-header", part: "header" }, h("div", { class: "canvas-editor-title", part: "title" }, h("h3",
|
|
1003
|
+
return (h("div", { class: "canvas-editor-wrapper" }, this.showCanvasEditor && (h("div", { class: "canvas-editor-overlay", part: "overlay", onMouseDown: this.handleOverlayMouseDown, onClick: this.handleOverlayClick }, h("div", { class: "canvas-editor-modal", part: "modal", role: "dialog", "aria-modal": "true", "aria-labelledby": "canvas-editor-title" }, h("div", { class: "canvas-editor-header", part: "header" }, h("div", { class: "canvas-editor-title", part: "title" }, h("h3", { id: "canvas-editor-title" }, this.canvasEditorTitle)), h("div", { class: "canvas-editor-toolbar", part: "toolbar" }, h("div", { class: "toolbar-section" }, h("div", { class: "tool-group" }, h("button", { class: `tool-btn ${this.canvasDrawingTool === 'rectangle' ? 'active' : ''}`, part: "tool-button", onClick: () => (this.canvasDrawingTool = 'rectangle'), title: "Rectangle" }, h("svg", { xmlns: "http://www.w3.org/2000/svg", width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", class: "lucide lucide-square" }, h("rect", { width: "18", height: "18", x: "3", y: "3", rx: "2" }))), h("button", { class: `tool-btn ${this.canvasDrawingTool === 'line' ? 'active' : ''}`, part: "tool-button", onClick: () => (this.canvasDrawingTool = 'line'), title: "Line" }, h("svg", { xmlns: "http://www.w3.org/2000/svg", width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", class: "lucide lucide-minus" }, h("path", { d: "M5 12h14" }))), h("button", { class: `tool-btn ${this.canvasDrawingTool === 'arrow' ? 'active' : ''}`, part: "tool-button", onClick: () => (this.canvasDrawingTool = 'arrow'), title: "Arrow" }, h("svg", { xmlns: "http://www.w3.org/2000/svg", width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", class: "lucide lucide-move-up-right" }, h("path", { d: "M13 5H19V11" }), h("path", { d: "M19 5L5 19" }))), h("button", { class: `tool-btn ${this.canvasDrawingTool === 'text' ? 'active' : ''}`, part: "tool-button", onClick: () => (this.canvasDrawingTool = 'text'), title: "Text" }, h("svg", { xmlns: "http://www.w3.org/2000/svg", width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", class: "lucide lucide-type" }, h("path", { d: "M12 4v16" }), h("path", { d: "M4 7V5a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v2" }), h("path", { d: "M9 20h6" }))), h("div", { class: "toolbar-divider" }), h("button", { class: "tool-btn undo-btn", part: "tool-button", onClick: this.undoLastAnnotation, disabled: this.annotations.length === 0, title: "Undo" }, h("svg", { xmlns: "http://www.w3.org/2000/svg", width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", class: "lucide lucide-undo" }, h("path", { d: "M3 7v6h6" }), h("path", { d: "M21 17a9 9 0 0 0-9-9 9 9 0 0 0-6 2.3L3 13" }))), this.selectedAnnotation && (h("button", { class: "tool-btn delete-btn", part: "tool-button", onClick: this.deleteSelectedAnnotation, title: "Delete" }, h("svg", { xmlns: "http://www.w3.org/2000/svg", width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", class: "lucide lucide-trash-2" }, h("path", { d: "M3 6h18" }), h("path", { d: "M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" }), h("path", { d: "M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" }), h("line", { x1: "10", x2: "10", y1: "11", y2: "17" }), h("line", { x1: "14", x2: "14", y1: "11", y2: "17" })))))), 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) })))))))), (this.selectedAnnotation || this.canvasDrawingTool) && (h("div", { class: "toolbar-section selected-annotation-controls" }, (((_a = this.selectedAnnotation) === null || _a === void 0 ? void 0 : _a.type) === 'text' ||
|
|
882
1004
|
(!this.selectedAnnotation && this.canvasDrawingTool === 'text')) && (h("div", { class: "text-controls" }, h("div", { class: "font-size-control" }, h("label", null, this.sizeLabelText), h("input", { type: "range", min: "8", max: "72", value: ((_b = this.selectedAnnotation) === null || _b === void 0 ? void 0 : _b.fontSize) || this.canvasTextSize, onInput: (e) => {
|
|
883
1005
|
const newSize = parseInt(e.target.value);
|
|
884
1006
|
if (this.selectedAnnotation) {
|
|
@@ -902,7 +1024,7 @@ const CanvasEditor = class {
|
|
|
902
1024
|
};
|
|
903
1025
|
CanvasEditor.style = canvasEditorCss;
|
|
904
1026
|
|
|
905
|
-
const feedbackButtonCss = ".feedback-button-content{cursor:pointer;max-width:fit-content;letter-spacing:var(--feedback-button-letter-spacing);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}}";
|
|
1027
|
+
const feedbackButtonCss = ".feedback-button-content{cursor:pointer;max-width:fit-content;letter-spacing:var(--feedback-button-letter-spacing);z-index:var(--feedback-button-z-index);font-family:var(--feedback-font-family);background:none;border:none;margin:0;padding:0;color:inherit;font-size:inherit;font-weight:inherit;line-height:inherit;text-align:inherit}.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--bottom-left{bottom:10px;left:10px;position:fixed}.feedback-button-content--top-right{position:fixed;right:10px;top:10px}.feedback-button-content--top-left{left:10px;position:fixed;top:10px}.feedback-button-content--center-right{position:fixed;transform:rotate(-90deg) translateY(-50%);top:50%}.feedback-button-content--center-left{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,.feedback-button-content--center-left.feedback-button-content--dark,.feedback-button-content--center-left.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}.feedback-button-content--center-left .feedback-button-content-icon{rotate:-90deg}@media screen and (max-width: 767px){.feedback-button-content--hide-mobile{display:none}}";
|
|
906
1028
|
|
|
907
1029
|
const FeedbackButton = class {
|
|
908
1030
|
constructor(hostRef) {
|
|
@@ -916,6 +1038,7 @@ const FeedbackButton = class {
|
|
|
916
1038
|
this.sessionId = '';
|
|
917
1039
|
this.metadata = '';
|
|
918
1040
|
this.submit = false;
|
|
1041
|
+
this.apiKey = '';
|
|
919
1042
|
this.clickOutsideClose = true;
|
|
920
1043
|
this.customFont = false;
|
|
921
1044
|
this.emailAddress = '';
|
|
@@ -932,6 +1055,7 @@ const FeedbackButton = class {
|
|
|
932
1055
|
this.ratingMode = 'thumbs';
|
|
933
1056
|
this.emailPlaceholder = 'Email address (optional)';
|
|
934
1057
|
this.errorMessage = 'Please try again later.';
|
|
1058
|
+
this.errorMessage401 = 'This project requires a valid API key to submit feedback. Check the api-key attribute.';
|
|
935
1059
|
this.errorMessage403 = 'The request URL does not match the one defined in PushFeedback for this project.';
|
|
936
1060
|
this.errorMessage404 = 'We could not find the provided project id in PushFeedback.';
|
|
937
1061
|
this.footerText = '';
|
|
@@ -977,13 +1101,19 @@ const FeedbackButton = class {
|
|
|
977
1101
|
}
|
|
978
1102
|
}
|
|
979
1103
|
componentDidLoad() {
|
|
980
|
-
if (this.buttonPosition === 'center-right') {
|
|
1104
|
+
if (this.buttonPosition === 'center-right' || this.buttonPosition === 'center-left') {
|
|
981
1105
|
const buttonContent = this.el.shadowRoot.querySelector('.feedback-button-content');
|
|
982
1106
|
let adjustement = 0;
|
|
983
1107
|
if (this.isSafariBrowser()) {
|
|
984
1108
|
adjustement = 5;
|
|
985
1109
|
}
|
|
986
|
-
|
|
1110
|
+
const offset = `${((buttonContent.offsetWidth + adjustement) / 2) * -1}px`;
|
|
1111
|
+
if (this.buttonPosition === 'center-right') {
|
|
1112
|
+
buttonContent.style.right = offset;
|
|
1113
|
+
}
|
|
1114
|
+
else {
|
|
1115
|
+
buttonContent.style.left = offset;
|
|
1116
|
+
}
|
|
987
1117
|
}
|
|
988
1118
|
if (!this.customFont) {
|
|
989
1119
|
this.loadInterFont();
|
|
@@ -992,6 +1122,7 @@ const FeedbackButton = class {
|
|
|
992
1122
|
connectedCallback() {
|
|
993
1123
|
this.feedbackModal = document.createElement('feedback-modal');
|
|
994
1124
|
const props = [
|
|
1125
|
+
'apiKey',
|
|
995
1126
|
'clickOutsideClose',
|
|
996
1127
|
'customFont',
|
|
997
1128
|
'emailAddress',
|
|
@@ -1022,6 +1153,7 @@ const FeedbackButton = class {
|
|
|
1022
1153
|
'screenshotErrorUnexpected',
|
|
1023
1154
|
'emailPlaceholder',
|
|
1024
1155
|
'errorMessage',
|
|
1156
|
+
'errorMessage401',
|
|
1025
1157
|
'errorMessage403',
|
|
1026
1158
|
'errorMessage404',
|
|
1027
1159
|
'footerText',
|
|
@@ -1086,12 +1218,16 @@ const FeedbackButton = class {
|
|
|
1086
1218
|
}
|
|
1087
1219
|
}
|
|
1088
1220
|
const body = Object.assign({ url: window.location.href, project: this.project, rating: this.rating || -1, ratingMode: this.ratingMode, message: '', metadata: this.metadata, session: localStorage.getItem('pushfeedback_sessionid') || '' }, (recaptchaToken && { recaptchaToken }));
|
|
1089
|
-
const
|
|
1221
|
+
const headers = {
|
|
1222
|
+
'Content-Type': 'application/json',
|
|
1223
|
+
};
|
|
1224
|
+
if (this.apiKey) {
|
|
1225
|
+
headers['Authorization'] = `Api-Key ${this.apiKey}`;
|
|
1226
|
+
}
|
|
1227
|
+
const res = await fetch('https://app.pushfeedback.com/api/v1/feedback/', {
|
|
1090
1228
|
method: 'POST',
|
|
1091
1229
|
body: JSON.stringify(body),
|
|
1092
|
-
headers
|
|
1093
|
-
'Content-Type': 'application/json',
|
|
1094
|
-
},
|
|
1230
|
+
headers,
|
|
1095
1231
|
});
|
|
1096
1232
|
if (res.status === 201) {
|
|
1097
1233
|
const feedback_with_id = Object.assign(Object.assign({}, body), { id: await res.json() });
|
|
@@ -1122,13 +1258,104 @@ const FeedbackButton = class {
|
|
|
1122
1258
|
}
|
|
1123
1259
|
}
|
|
1124
1260
|
render() {
|
|
1125
|
-
return (h(Host, null, h("
|
|
1261
|
+
return (h(Host, null, h("button", { type: "button", 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' : ''}`, part: "button", onClick: () => this.showModal() }, !this.hideIcon && this.buttonStyle != 'default' && (h("span", { class: "feedback-button-content-icon", part: "icon" }, 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" }, h("path", { d: "M12 20h9" }), h("path", { d: "M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z" })))), h("slot", null))));
|
|
1126
1262
|
}
|
|
1127
1263
|
get el() { return getElement(this); }
|
|
1128
1264
|
};
|
|
1129
1265
|
FeedbackButton.style = feedbackButtonCss;
|
|
1130
1266
|
|
|
1131
|
-
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.25s cubic-bezier(0.4, 0, 0.2, 1);backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px)}.feedback-overlay--visible{opacity:1}.feedback-modal{display:inline-block;position:relative}.feedback-modal-content{background-color:var(--feedback-modal-content-bg-color);border:1px solid rgba(0, 0, 0, 0.08);border-radius:var(--feedback-modal-content-border-radius);box-shadow:0px 0px 0px 1px rgba(0, 0, 0, 0.02),\n 0px 2px 4px rgba(0, 0, 0, 0.04),\n 0px 8px 16px rgba(0, 0, 0, 0.06),\n 0px 16px 32px rgba(0, 0, 0, 0.04);box-sizing:border-box;color:var(--feedback-modal-content-text-color);display:flex;flex-direction:column;left:50%;max-width:90%;padding:24px;position:fixed;top:50%;transform:translate(-50%, -50%) scale(0.96);opacity:0;width:100%;z-index:var(--feedback-modal-content-z-index);transition:transform 0.25s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.25s cubic-bezier(0.4, 0, 0.2, 1)}.feedback-modal-content--open{transform:translate(-50%, -50%) scale(1);opacity:1}.feedback-modal-header{color:var(--feedback-modal-header-text-color);display:flex;font-size:var(--feedback-modal-header-font-size);font-weight:var(--feedback-modal-header-font-weight);justify-content:space-between;align-items:flex-start;margin-bottom:24px;letter-spacing:-0.01em}.feedback-modal-header-content{flex:1}.feedback-modal-header-text{display:flex;flex-direction:column;gap:6px}.feedback-modal-title{display:block}.feedback-modal-powered-by{display:block;font-size:13px;font-weight:400;color:var(--feedback-color-gray-500);line-height:1.4}.feedback-modal-powered-by a{color:var(--feedback-modal-header-text-color);text-decoration:none;font-weight:500}.feedback-modal-powered-by a:hover{text-decoration:underline}.feedback-modal-header--no-content{margin-bottom:0}.feedback-modal-rating-buttons{width:100%;margin-bottom:24px;display:flex;gap:4px}.feedback-modal-rating-button{padding:0;background-color:transparent;border:transparent;margin-right:8px;cursor:pointer}.feedback-modal-rating-buttons--stars .feedback-modal-rating-button{transition:transform 0.15s ease, opacity 0.15s ease}.feedback-modal-rating-buttons--stars .feedback-modal-rating-button:hover{transform:scale(1.1)}.feedback-modal-rating-buttons--thumbs .feedback-modal-rating-button{border:1.5px 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:12px;justify-content:center;padding:10px 16px;transition:all 0.2s cubic-bezier(0.4, 0, 0.2, 1);box-shadow:0 1px 2px rgba(0, 0, 0, 0.04)}.feedback-modal-rating-buttons--thumbs .feedback-modal-rating-button--positive{background-color:var(--feedback-modal-rating-button-positive-bg-color);border-color:var(--feedback-modal-rating-button-positive-border-color)}.feedback-modal-rating-buttons--thumbs .feedback-modal-rating-button--negative{background-color:var(--feedback-modal-rating-button-negative-bg-color);border-color:var(--feedback-modal-rating-button-negative-border-color)}.feedback-modal-rating-buttons--thumbs .feedback-modal-rating-button:hover{transform:translateY(-2px);box-shadow:0 4px 8px rgba(0, 0, 0, 0.12)}.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:1.5px 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--thumbs .feedback-modal-rating-button--positive svg{stroke:var(--feedback-modal-rating-button-positive-color)}.feedback-modal-rating-buttons--thumbs .feedback-modal-rating-button--negative svg{stroke:var(--feedback-modal-rating-button-negative-color)}.feedback-modal-rating-buttons--thumbs .feedback-modal-rating-button--positive:hover,.feedback-modal-rating-buttons--thumbs .feedback-modal-rating-button--positive.feedback-modal-rating-button--selected{background-color:var(--feedback-modal-rating-button-positive-selected-bg-color);border-color:var(--feedback-modal-rating-button-positive-selected-border-color)}.feedback-modal-rating-buttons--thumbs .feedback-modal-rating-button--negative:hover,.feedback-modal-rating-buttons--thumbs .feedback-modal-rating-button--negative.feedback-modal-rating-button--selected{background-color:var(--feedback-modal-rating-button-negative-selected-bg-color);border-color:var(--feedback-modal-rating-button-negative-selected-border-color)}.feedback-modal-rating-buttons--thumbs .feedback-modal-rating-button--positive:hover svg,.feedback-modal-rating-buttons--thumbs .feedback-modal-rating-button--positive.feedback-modal-rating-button--selected svg{stroke:var(--feedback-modal-rating-button-positive-selected-color)}.feedback-modal-rating-buttons--thumbs .feedback-modal-rating-button--negative:hover svg,.feedback-modal-rating-buttons--thumbs .feedback-modal-rating-button--negative.feedback-modal-rating-button--selected svg{stroke:var(--feedback-modal-rating-button-negative-selected-color)}.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:1.5px 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:12px;resize:vertical;width:100%;transition:border-color 0.2s ease, box-shadow 0.2s ease;line-height:1.5}.feedback-modal-text textarea:hover{border-color:var(--feedback-modal-input-border-color-hover)}.feedback-modal-email input{background-color:var(--feedback-modal-input-bg-color);border:1.5px 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:44px;padding:12px;width:100%;transition:border-color 0.2s ease, box-shadow 0.2s ease}.feedback-modal-email input:hover{border-color:var(--feedback-modal-input-border-color-hover)}.feedback-modal-text textarea:-webkit-autofill,.feedback-modal-email input:-webkit-autofill,.feedback-modal-email input:-webkit-autofill:hover,.feedback-modal-email input:-webkit-autofill:focus,.feedback-modal-email input:-webkit-autofill:active{-webkit-box-shadow:0 0 0 1000px var(--feedback-modal-input-bg-color) inset !important;-webkit-text-fill-color:var(--feedback-modal-input-text-color) !important;transition:background-color 5000s ease-in-out 0s}.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:1.5px solid var(--feedback-modal-input-border-color-focused);outline:none;box-shadow:0 0 0 3px rgba(59, 130, 246, 0.1)}.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:1.5px 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:44px;padding:10px 16px;transition:all 0.2s cubic-bezier(0.4, 0, 0.2, 1);box-shadow:0 1px 2px rgba(0, 0, 0, 0.04)}.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:1.5px solid var(--feedback-modal-button-border-color-active);color:var(--feedback-modal-button-submit-text-color);box-shadow:0 2px 4px rgba(0, 0, 0, 0.1)}.feedback-modal-button:hover,.feedback-modal-button--active{background-color:var(--feedback-modal-button-bg-color-active);border:1.5px solid var(--feedback-modal-button-border-color-active);color:var(--feedback-modal-button-text-color-active);transform:translateY(-1px);box-shadow:0 4px 8px rgba(0, 0, 0, 0.12)}.feedback-modal-button--submit:hover{background-color:var(--feedback-modal-button-submit-bg-color-hover);border:1.5px solid var(--feedback-modal-button-submit-border-color-hover);color:var(--feedback-modal-button-submit-text-color-hover);transform:translateY(-1px);box-shadow:0 6px 12px rgba(0, 0, 0, 0.15)}.feedback-modal-button--submit:active{transform:translateY(0);box-shadow:0 2px 4px rgba(0, 0, 0, 0.1)}.feedback-modal-input-heading{display:block;font-size:14px;font-weight:500;padding-bottom:12px;color:var(--feedback-modal-header-text-color);letter-spacing:-0.01em}.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-footer-combined{display:block;text-align:center;font-size:11px;color:#666;line-height:1.5}.feedback-footer-combined a{color:#666;text-decoration:underline}.feedback-recaptcha-notice{display:block;text-align:center;margin-bottom:10px;font-size:11px;color:#666;line-height:1.4}.feedback-recaptcha-notice a{color:#666;text-decoration:underline}.feedback-modal-close{background-color:var(--feedback-modal-close-bg-color);border:0;border-radius:6px;cursor:pointer;height:32px;width:32px;margin-left:auto;padding:0;display:flex;align-items:center;justify-content:center;transition:all 0.2s ease}.feedback-modal-close:hover{background-color:rgba(0, 0, 0, 0.06);transform:scale(1.05)}.feedback-modal-close:active{transform:scale(0.95)}.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:0;line-height:1.6;color:var(--feedback-modal-content-text-color)}.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:32px;height:32px;overflow:hidden;border-radius:6px;margin-right:10px;box-shadow:0 2px 4px rgba(0, 0, 0, 0.1);cursor:pointer;transition:all 0.2s cubic-bezier(0.4, 0, 0.2, 1);border:2px solid rgba(255, 255, 255, 0.8)}.screenshot-preview:hover{transform:scale(1.15);box-shadow:0 4px 8px rgba(0, 0, 0, 0.15)}.screenshot-preview img{width:100%;height:100%;object-fit:cover}.screenshot-loading{display:inline-flex;align-items:center;margin-right:8px}@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)}}@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:1.5px solid #fcc;border-radius:10px;padding:14px 18px;display:flex;align-items:center;gap:12px;box-shadow:0 0 0 1px rgba(197, 48, 48, 0.05),\n 0 4px 6px rgba(0, 0, 0, 0.07),\n 0 10px 20px rgba(0, 0, 0, 0.1);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:6px;border-radius:6px;color:#c53030;flex-shrink:0;transition:all 0.2s ease;display:flex;align-items:center;justify-content:center}.error-close-btn:hover{background:rgba(197, 48, 48, 0.15);transform:scale(1.1)}.error-close-btn:active{transform:scale(0.95)}@media screen and (max-width: 768px){.feedback-modal-content{width:100vw;height:100dvh;max-width:none;border-radius:0;top:0;left:0;transform:scale(0.95);padding:24px 20px;display:flex;flex-direction:column}.feedback-modal-content--open{transform:scale(1)}.feedback-modal-buttons{display:flex;flex-direction:column;gap:12px}.feedback-modal-button--screenshot{display:none !important}.feedback-modal-button--submit{width:100%}.feedback-modal-text textarea{flex:1;min-height:120px;resize:none}}.feedback-modal-wrapper--embedded{position:relative;z-index:auto;display:block;width:100%}.feedback-modal-content--embedded{position:relative !important;left:auto !important;right:auto !important;top:auto !important;bottom:auto !important;transform:none !important;opacity:1 !important;max-width:100%;margin:0 auto;box-shadow:0 2px 8px rgba(0, 0, 0, 0.1);text-align:left}.feedback-modal-content--embedded.feedback-modal-content--open{transform:none !important}@media screen and (max-width: 768px){.feedback-modal-content--embedded{width:100%;height:auto;border-radius:var(--feedback-modal-content-border-radius);padding:24px}.feedback-modal-content--embedded .feedback-modal-button--screenshot{display:flex !important}}";
|
|
1267
|
+
// Tags allowed in the rich-text props (privacy policy, reCAPTCHA notice,
|
|
1268
|
+
// footer). These only ever need links and light inline emphasis.
|
|
1269
|
+
const ALLOWED_TAGS = new Set([
|
|
1270
|
+
'A',
|
|
1271
|
+
'B',
|
|
1272
|
+
'BR',
|
|
1273
|
+
'CODE',
|
|
1274
|
+
'EM',
|
|
1275
|
+
'I',
|
|
1276
|
+
'P',
|
|
1277
|
+
'SMALL',
|
|
1278
|
+
'SPAN',
|
|
1279
|
+
'STRONG',
|
|
1280
|
+
'U',
|
|
1281
|
+
]);
|
|
1282
|
+
// No `class`: an injected class name cannot reach the host page's CSS (this
|
|
1283
|
+
// renders inside a shadow root), but it could match the widget's own internal
|
|
1284
|
+
// styles. The default prop values do not use classes, so dropping it is free.
|
|
1285
|
+
const ALLOWED_ATTRS = new Set(['href', 'target', 'rel', 'title']);
|
|
1286
|
+
// Schemes permitted in href. Anything else, notably javascript:, is dropped.
|
|
1287
|
+
const SAFE_URL = /^(https?:|mailto:|tel:|#|\/|\.\/|\.\.\/)/i;
|
|
1288
|
+
/**
|
|
1289
|
+
* Removes anything that can execute from a fragment: script and style
|
|
1290
|
+
* elements, event-handler attributes, javascript: URLs, and any tag not on the
|
|
1291
|
+
* allowlist. Text inside a disallowed tag is kept, so stripping a wrapper does
|
|
1292
|
+
* not silently delete the words inside it.
|
|
1293
|
+
*/
|
|
1294
|
+
function scrub(root) {
|
|
1295
|
+
const elements = Array.from(root.querySelectorAll('*'));
|
|
1296
|
+
for (const el of elements) {
|
|
1297
|
+
// The node may already have been removed along with an ancestor
|
|
1298
|
+
if (!root.contains(el)) {
|
|
1299
|
+
continue;
|
|
1300
|
+
}
|
|
1301
|
+
const tag = el.tagName.toUpperCase();
|
|
1302
|
+
if (tag === 'SCRIPT' || tag === 'STYLE' || tag === 'IFRAME' || tag === 'OBJECT') {
|
|
1303
|
+
el.remove();
|
|
1304
|
+
continue;
|
|
1305
|
+
}
|
|
1306
|
+
if (!ALLOWED_TAGS.has(tag)) {
|
|
1307
|
+
// Unwrap: keep the text, drop the element
|
|
1308
|
+
el.replaceWith(...Array.from(el.childNodes));
|
|
1309
|
+
continue;
|
|
1310
|
+
}
|
|
1311
|
+
for (const attr of Array.from(el.attributes)) {
|
|
1312
|
+
const name = attr.name.toLowerCase();
|
|
1313
|
+
if (name.startsWith('on') || !ALLOWED_ATTRS.has(name)) {
|
|
1314
|
+
el.removeAttribute(attr.name);
|
|
1315
|
+
continue;
|
|
1316
|
+
}
|
|
1317
|
+
if (name === 'href' && !SAFE_URL.test(attr.value.trim())) {
|
|
1318
|
+
el.removeAttribute(attr.name);
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
// Links opening a new tab must not hand the opener over
|
|
1322
|
+
if (tag === 'A' && el.getAttribute('target') === '_blank') {
|
|
1323
|
+
el.setAttribute('rel', 'noopener noreferrer');
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
/**
|
|
1328
|
+
* Sanitizes a fragment of HTML for use with innerHTML.
|
|
1329
|
+
*
|
|
1330
|
+
* Prefers the browser's native Element.setHTML() where available, which parses
|
|
1331
|
+
* without executing. Otherwise parses in an inert document (no scripts run, no
|
|
1332
|
+
* images or other subresources are fetched) and scrubs the result.
|
|
1333
|
+
*/
|
|
1334
|
+
function sanitizeHtml(html) {
|
|
1335
|
+
if (!html) {
|
|
1336
|
+
return '';
|
|
1337
|
+
}
|
|
1338
|
+
const template = document.createElement('template');
|
|
1339
|
+
// Native sanitizer, when the browser has it
|
|
1340
|
+
const withSetHtml = template;
|
|
1341
|
+
if (typeof withSetHtml.setHTML === 'function') {
|
|
1342
|
+
try {
|
|
1343
|
+
withSetHtml.setHTML(html);
|
|
1344
|
+
scrub(template.content);
|
|
1345
|
+
return template.innerHTML;
|
|
1346
|
+
}
|
|
1347
|
+
catch (_a) {
|
|
1348
|
+
// Fall through to the manual path
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
// Inert parsing: content inside <template> is not rendered, so scripts do not
|
|
1352
|
+
// run and no subresource requests are made while we clean it up.
|
|
1353
|
+
template.innerHTML = html;
|
|
1354
|
+
scrub(template.content);
|
|
1355
|
+
return template.innerHTML;
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
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.25s cubic-bezier(0.4, 0, 0.2, 1);backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px)}.feedback-overlay--visible{opacity:1}.feedback-modal{display:inline-block;position:relative}.feedback-modal-content{background-color:var(--feedback-modal-content-bg-color);border:1px solid rgba(0, 0, 0, 0.08);border-radius:var(--feedback-modal-content-border-radius);box-shadow:0px 0px 0px 1px rgba(0, 0, 0, 0.02),\n 0px 2px 4px rgba(0, 0, 0, 0.04),\n 0px 8px 16px rgba(0, 0, 0, 0.06),\n 0px 16px 32px rgba(0, 0, 0, 0.04);box-sizing:border-box;color:var(--feedback-modal-content-text-color);display:flex;flex-direction:column;left:50%;max-width:90%;padding:24px;position:fixed;top:50%;transform:translate(-50%, -50%) scale(0.96);opacity:0;width:100%;z-index:var(--feedback-modal-content-z-index);transition:transform 0.25s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.25s cubic-bezier(0.4, 0, 0.2, 1)}.feedback-modal-content--open{transform:translate(-50%, -50%) scale(1);opacity:1}.feedback-modal-content:focus{outline:none}.feedback-modal-header{color:var(--feedback-modal-header-text-color);display:flex;font-size:var(--feedback-modal-header-font-size);font-weight:var(--feedback-modal-header-font-weight);justify-content:space-between;align-items:flex-start;margin-bottom:24px;letter-spacing:-0.01em}.feedback-modal-header-content{flex:1}.feedback-modal-header-text{display:flex;flex-direction:column;gap:6px}.feedback-modal-title{display:block}.feedback-modal-powered-by{display:block;font-size:13px;font-weight:400;color:var(--feedback-color-gray-500);line-height:1.4}.feedback-modal-powered-by a{color:var(--feedback-modal-header-text-color);text-decoration:none;font-weight:500}.feedback-modal-powered-by a:hover{text-decoration:underline}.feedback-modal-header--no-content{margin-bottom:0}.feedback-modal-rating-buttons{width:100%;margin-bottom:24px;display:flex;gap:4px}.feedback-modal-rating-button{padding:0;background-color:transparent;border:transparent;margin-right:8px;cursor:pointer}.feedback-modal-rating-buttons--stars .feedback-modal-rating-button{transition:transform 0.15s ease, opacity 0.15s ease}.feedback-modal-rating-buttons--stars .feedback-modal-rating-button:hover{transform:scale(1.1)}.feedback-modal-rating-buttons--thumbs .feedback-modal-rating-button{border:1.5px 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:12px;justify-content:center;padding:10px 16px;transition:all 0.2s cubic-bezier(0.4, 0, 0.2, 1);box-shadow:0 1px 2px rgba(0, 0, 0, 0.04)}.feedback-modal-rating-buttons--thumbs .feedback-modal-rating-button--positive{background-color:var(--feedback-modal-rating-button-positive-bg-color);border-color:var(--feedback-modal-rating-button-positive-border-color)}.feedback-modal-rating-buttons--thumbs .feedback-modal-rating-button--negative{background-color:var(--feedback-modal-rating-button-negative-bg-color);border-color:var(--feedback-modal-rating-button-negative-border-color)}.feedback-modal-rating-buttons--thumbs .feedback-modal-rating-button:hover{transform:translateY(-2px);box-shadow:0 4px 8px rgba(0, 0, 0, 0.12)}.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:1.5px 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--thumbs .feedback-modal-rating-button--positive svg{stroke:var(--feedback-modal-rating-button-positive-color)}.feedback-modal-rating-buttons--thumbs .feedback-modal-rating-button--negative svg{stroke:var(--feedback-modal-rating-button-negative-color)}.feedback-modal-rating-buttons--thumbs .feedback-modal-rating-button--positive:hover,.feedback-modal-rating-buttons--thumbs .feedback-modal-rating-button--positive.feedback-modal-rating-button--selected{background-color:var(--feedback-modal-rating-button-positive-selected-bg-color);border-color:var(--feedback-modal-rating-button-positive-selected-border-color)}.feedback-modal-rating-buttons--thumbs .feedback-modal-rating-button--negative:hover,.feedback-modal-rating-buttons--thumbs .feedback-modal-rating-button--negative.feedback-modal-rating-button--selected{background-color:var(--feedback-modal-rating-button-negative-selected-bg-color);border-color:var(--feedback-modal-rating-button-negative-selected-border-color)}.feedback-modal-rating-buttons--thumbs .feedback-modal-rating-button--positive:hover svg,.feedback-modal-rating-buttons--thumbs .feedback-modal-rating-button--positive.feedback-modal-rating-button--selected svg{stroke:var(--feedback-modal-rating-button-positive-selected-color)}.feedback-modal-rating-buttons--thumbs .feedback-modal-rating-button--negative:hover svg,.feedback-modal-rating-buttons--thumbs .feedback-modal-rating-button--negative.feedback-modal-rating-button--selected svg{stroke:var(--feedback-modal-rating-button-negative-selected-color)}.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:1.5px 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:12px;resize:vertical;width:100%;transition:border-color 0.2s ease, box-shadow 0.2s ease;line-height:1.5}.feedback-modal-text textarea:hover{border-color:var(--feedback-modal-input-border-color-hover)}.feedback-modal-email input{background-color:var(--feedback-modal-input-bg-color);border:1.5px 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:44px;padding:12px;width:100%;transition:border-color 0.2s ease, box-shadow 0.2s ease}.feedback-modal-email input:hover{border-color:var(--feedback-modal-input-border-color-hover)}.feedback-modal-text textarea:-webkit-autofill,.feedback-modal-email input:-webkit-autofill,.feedback-modal-email input:-webkit-autofill:hover,.feedback-modal-email input:-webkit-autofill:focus,.feedback-modal-email input:-webkit-autofill:active{-webkit-box-shadow:0 0 0 1000px var(--feedback-modal-input-bg-color) inset !important;-webkit-text-fill-color:var(--feedback-modal-input-text-color) !important;transition:background-color 5000s ease-in-out 0s}.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:1.5px solid var(--feedback-modal-input-border-color-focused);outline:none;box-shadow:0 0 0 3px rgba(59, 130, 246, 0.1)}.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:1.5px 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:44px;padding:10px 16px;transition:all 0.2s cubic-bezier(0.4, 0, 0.2, 1);box-shadow:0 1px 2px rgba(0, 0, 0, 0.04)}.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:1.5px solid var(--feedback-modal-button-border-color-active);color:var(--feedback-modal-button-submit-text-color);box-shadow:0 2px 4px rgba(0, 0, 0, 0.1)}.feedback-modal-button:hover,.feedback-modal-button--active{background-color:var(--feedback-modal-button-bg-color-active);border:1.5px solid var(--feedback-modal-button-border-color-active);color:var(--feedback-modal-button-text-color-active);transform:translateY(-1px);box-shadow:0 4px 8px rgba(0, 0, 0, 0.12)}.feedback-modal-button--submit:hover{background-color:var(--feedback-modal-button-submit-bg-color-hover);border:1.5px solid var(--feedback-modal-button-submit-border-color-hover);color:var(--feedback-modal-button-submit-text-color-hover);transform:translateY(-1px);box-shadow:0 6px 12px rgba(0, 0, 0, 0.15)}.feedback-modal-button--submit:active{transform:translateY(0);box-shadow:0 2px 4px rgba(0, 0, 0, 0.1)}.feedback-modal-input-heading{display:block;font-size:14px;font-weight:500;padding-bottom:12px;color:var(--feedback-modal-header-text-color);letter-spacing:-0.01em}.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-footer-combined{display:block;text-align:center;font-size:11px;color:#666;line-height:1.5}.feedback-footer-combined a{color:#666;text-decoration:underline}.feedback-recaptcha-notice{display:block;text-align:center;margin-bottom:10px;font-size:11px;color:#666;line-height:1.4}.feedback-recaptcha-notice a{color:#666;text-decoration:underline}.feedback-modal-close{background-color:var(--feedback-modal-close-bg-color);border:0;border-radius:6px;cursor:pointer;height:32px;width:32px;margin-left:auto;padding:0;display:flex;align-items:center;justify-content:center;transition:all 0.2s ease}.feedback-modal-close:hover{background-color:rgba(0, 0, 0, 0.06);transform:scale(1.05)}.feedback-modal-close:active{transform:scale(0.95)}.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:0;line-height:1.6;color:var(--feedback-modal-content-text-color)}.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:32px;height:32px;overflow:hidden;border-radius:6px;margin-right:10px;box-shadow:0 2px 4px rgba(0, 0, 0, 0.1);cursor:pointer;transition:all 0.2s cubic-bezier(0.4, 0, 0.2, 1);border:2px solid rgba(255, 255, 255, 0.8)}.screenshot-preview:hover{transform:scale(1.15);box-shadow:0 4px 8px rgba(0, 0, 0, 0.15)}.screenshot-preview img{width:100%;height:100%;object-fit:cover}.screenshot-loading{display:inline-flex;align-items:center;margin-right:8px}@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)}}@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:1.5px solid #fcc;border-radius:10px;padding:14px 18px;display:flex;align-items:center;gap:12px;box-shadow:0 0 0 1px rgba(197, 48, 48, 0.05),\n 0 4px 6px rgba(0, 0, 0, 0.07),\n 0 10px 20px rgba(0, 0, 0, 0.1);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:6px;border-radius:6px;color:#c53030;flex-shrink:0;transition:all 0.2s ease;display:flex;align-items:center;justify-content:center}.error-close-btn:hover{background:rgba(197, 48, 48, 0.15);transform:scale(1.1)}.error-close-btn:active{transform:scale(0.95)}@media screen and (max-width: 768px){.feedback-modal-content{width:100vw;height:100dvh;max-width:none;border-radius:0;top:0;left:0;transform:scale(0.95);padding:24px 20px;display:flex;flex-direction:column}.feedback-modal-content--open{transform:scale(1)}.feedback-modal-buttons{display:flex;flex-direction:column;gap:12px}.feedback-modal-button--screenshot{display:none !important}.feedback-modal-button--submit{width:100%}.feedback-modal-text textarea{flex:1;min-height:120px;resize:none}}.feedback-modal-wrapper--embedded{position:relative;z-index:auto;display:block;width:100%}.feedback-modal-content--embedded{position:relative !important;left:auto !important;right:auto !important;top:auto !important;bottom:auto !important;transform:none !important;opacity:1 !important;max-width:100%;margin:0 auto;box-shadow:0 2px 8px rgba(0, 0, 0, 0.1);text-align:left}.feedback-modal-content--embedded.feedback-modal-content--open{transform:none !important}@media screen and (max-width: 768px){.feedback-modal-content--embedded{width:100%;height:auto;border-radius:var(--feedback-modal-content-border-radius);padding:24px}.feedback-modal-content--embedded .feedback-modal-button--screenshot{display:flex !important}}";
|
|
1132
1359
|
|
|
1133
1360
|
const FeedbackModal = class {
|
|
1134
1361
|
constructor(hostRef) {
|
|
@@ -1137,6 +1364,24 @@ const FeedbackModal = class {
|
|
|
1137
1364
|
this.feedbackError = createEvent(this, "feedbackError", 7);
|
|
1138
1365
|
// Track the history entry pushed while the modal is open
|
|
1139
1366
|
this.historyEntryPushed = false;
|
|
1367
|
+
// Element that had focus before the modal opened, so it can be restored on close
|
|
1368
|
+
this.previouslyFocused = null;
|
|
1369
|
+
this.handleKeyDown = (event) => {
|
|
1370
|
+
// The canvas editor renders its own overlay and handles its own keys
|
|
1371
|
+
if (!this.showModal || this.embedded || this.showCanvasEditor) {
|
|
1372
|
+
return;
|
|
1373
|
+
}
|
|
1374
|
+
if (event.key === 'Escape' && this.escapeClose) {
|
|
1375
|
+
event.preventDefault();
|
|
1376
|
+
// Escape is an accidental-dismissal path like clicking outside, so keep
|
|
1377
|
+
// whatever the user already typed
|
|
1378
|
+
this.doClose(!this.formSuccess);
|
|
1379
|
+
return;
|
|
1380
|
+
}
|
|
1381
|
+
if (event.key === 'Tab') {
|
|
1382
|
+
this.trapFocus(event);
|
|
1383
|
+
}
|
|
1384
|
+
};
|
|
1140
1385
|
this.handlePopState = () => {
|
|
1141
1386
|
// Back button pressed while the modal is open: close it and consume our entry
|
|
1142
1387
|
if (this.historyEntryPushed) {
|
|
@@ -1180,12 +1425,16 @@ const FeedbackModal = class {
|
|
|
1180
1425
|
}
|
|
1181
1426
|
}
|
|
1182
1427
|
const body = Object.assign({ url: window.location.href, message: this.formMessage, email: this.formEmail, project: this.project, screenshot: this.encodedScreenshot, rating: this.selectedRating, ratingMode: this.ratingMode, metadata: this.metadata, verification: this.formVerification, session: localStorage.getItem('pushfeedback_sessionid') || '' }, (recaptchaToken && { recaptchaToken }));
|
|
1183
|
-
const
|
|
1428
|
+
const headers = {
|
|
1429
|
+
'Content-Type': 'application/json',
|
|
1430
|
+
};
|
|
1431
|
+
if (this.apiKey) {
|
|
1432
|
+
headers['Authorization'] = `Api-Key ${this.apiKey}`;
|
|
1433
|
+
}
|
|
1434
|
+
const res = await fetch('https://app.pushfeedback.com/api/v1/feedback/', {
|
|
1184
1435
|
method: 'POST',
|
|
1185
1436
|
body: JSON.stringify(body),
|
|
1186
|
-
headers
|
|
1187
|
-
'Content-Type': 'application/json',
|
|
1188
|
-
},
|
|
1437
|
+
headers,
|
|
1189
1438
|
});
|
|
1190
1439
|
if (res.status === 201) {
|
|
1191
1440
|
const feedback_with_id = Object.assign(Object.assign({}, body), { id: await res.json() });
|
|
@@ -1258,8 +1507,15 @@ const FeedbackModal = class {
|
|
|
1258
1507
|
this.encodedScreenshot = null;
|
|
1259
1508
|
this.formMessage = '';
|
|
1260
1509
|
this.formEmail = '';
|
|
1510
|
+
// The rating and the privacy checkbox are part of the same in-progress
|
|
1511
|
+
// input. Leaving the rating set meant reopening the modal showed the
|
|
1512
|
+
// previous session's choice still highlighted, and submitting then sent
|
|
1513
|
+
// that stale rating instead of one chosen for this submission.
|
|
1514
|
+
this.selectedRating = this.initialRating();
|
|
1515
|
+
this.isPrivacyChecked = false;
|
|
1261
1516
|
}
|
|
1262
1517
|
this.resetOverflow();
|
|
1518
|
+
this.restoreFocus();
|
|
1263
1519
|
}, 200);
|
|
1264
1520
|
};
|
|
1265
1521
|
// Handle screenshot events from canvas editor
|
|
@@ -1269,12 +1525,16 @@ const FeedbackModal = class {
|
|
|
1269
1525
|
this.takingScreenshot = false;
|
|
1270
1526
|
this.showCanvasEditor = false;
|
|
1271
1527
|
this.autoStartCapture = false;
|
|
1528
|
+
this.focusModal();
|
|
1272
1529
|
};
|
|
1273
1530
|
this.handleScreenshotCancelled = () => {
|
|
1274
1531
|
this.showModal = true;
|
|
1275
1532
|
this.takingScreenshot = false;
|
|
1276
1533
|
this.showCanvasEditor = false;
|
|
1277
1534
|
this.autoStartCapture = false;
|
|
1535
|
+
// Focus was inside the editor that just went away, so bring it back to the
|
|
1536
|
+
// form rather than letting it fall to the top of the page
|
|
1537
|
+
this.focusModal();
|
|
1278
1538
|
};
|
|
1279
1539
|
this.handleScreenshotError = (event) => {
|
|
1280
1540
|
console.error('Screenshot error:', event.detail.error);
|
|
@@ -1286,6 +1546,7 @@ const FeedbackModal = class {
|
|
|
1286
1546
|
this.takingScreenshot = false;
|
|
1287
1547
|
this.showCanvasEditor = false;
|
|
1288
1548
|
this.autoStartCapture = false;
|
|
1549
|
+
this.focusModal();
|
|
1289
1550
|
// Auto-hide error after 8 seconds
|
|
1290
1551
|
setTimeout(() => {
|
|
1291
1552
|
this.showScreenshotError = false;
|
|
@@ -1327,6 +1588,7 @@ const FeedbackModal = class {
|
|
|
1327
1588
|
this.screenshotError = '';
|
|
1328
1589
|
this.showCanvasEditor = false;
|
|
1329
1590
|
this.autoStartCapture = false;
|
|
1591
|
+
this.apiKey = '';
|
|
1330
1592
|
this.customFont = false;
|
|
1331
1593
|
this.emailAddress = '';
|
|
1332
1594
|
this.hideEmail = false;
|
|
@@ -1346,8 +1608,10 @@ const FeedbackModal = class {
|
|
|
1346
1608
|
this.embedded = false;
|
|
1347
1609
|
this.clickOutsideClose = true;
|
|
1348
1610
|
this.historyClose = true;
|
|
1611
|
+
this.escapeClose = true;
|
|
1349
1612
|
this.emailPlaceholder = 'Email address (optional)';
|
|
1350
1613
|
this.errorMessage = 'Please try again later.';
|
|
1614
|
+
this.errorMessage401 = 'This project requires a valid API key to submit feedback. Check the api-key attribute.';
|
|
1351
1615
|
this.errorMessage403 = 'The request URL does not match the one defined in PushFeedback for this project.';
|
|
1352
1616
|
this.errorMessage404 = 'We could not find the provided project ID in PushFeedback.';
|
|
1353
1617
|
this.messagePlaceholder = 'Share your thoughts...';
|
|
@@ -1360,6 +1624,10 @@ const FeedbackModal = class {
|
|
|
1360
1624
|
this.ratingPlaceholder = 'Was this page helpful?';
|
|
1361
1625
|
this.ratingStarsPlaceholder = 'How would you rate this page?';
|
|
1362
1626
|
this.sendButtonText = 'Send';
|
|
1627
|
+
this.closeButtonLabel = 'Close feedback form';
|
|
1628
|
+
this.ratingPositiveLabel = 'Yes';
|
|
1629
|
+
this.ratingNegativeLabel = 'No';
|
|
1630
|
+
this.ratingStarLabel = 'Rate {rating} out of 5';
|
|
1363
1631
|
this.successMessage = '';
|
|
1364
1632
|
this.recaptchaText = 'This form is protected by reCAPTCHA and the Google <a href="https://policies.google.com/privacy" target="_blank" rel="noopener noreferrer">Privacy Policy</a> and <a href="https://policies.google.com/terms" target="_blank" rel="noopener noreferrer">Terms of Service</a> apply.';
|
|
1365
1633
|
this.screenshotAttachedText = 'Screenshot attached';
|
|
@@ -1382,20 +1650,96 @@ const FeedbackModal = class {
|
|
|
1382
1650
|
}
|
|
1383
1651
|
connectedCallback() {
|
|
1384
1652
|
window.addEventListener('popstate', this.handlePopState);
|
|
1653
|
+
document.addEventListener('keydown', this.handleKeyDown);
|
|
1385
1654
|
}
|
|
1386
1655
|
disconnectedCallback() {
|
|
1387
1656
|
window.removeEventListener('popstate', this.handlePopState);
|
|
1657
|
+
document.removeEventListener('keydown', this.handleKeyDown);
|
|
1658
|
+
}
|
|
1659
|
+
// Keeps Tab cycling inside the dialog while it is open
|
|
1660
|
+
trapFocus(event) {
|
|
1661
|
+
var _a, _b;
|
|
1662
|
+
const focusable = this.getFocusableElements();
|
|
1663
|
+
if (focusable.length === 0) {
|
|
1664
|
+
return;
|
|
1665
|
+
}
|
|
1666
|
+
const first = focusable[0];
|
|
1667
|
+
const last = focusable[focusable.length - 1];
|
|
1668
|
+
const active = (_a = this.modalContent) === null || _a === void 0 ? void 0 : _a.getRootNode();
|
|
1669
|
+
const current = active === null || active === void 0 ? void 0 : active.activeElement;
|
|
1670
|
+
if (current === this.modalContent) {
|
|
1671
|
+
// Focus is on the dialog container itself (where it lands on open).
|
|
1672
|
+
// Tab falls through to the first control natively; Shift+Tab would
|
|
1673
|
+
// escape the dialog, so wrap it to the last one.
|
|
1674
|
+
if (event.shiftKey) {
|
|
1675
|
+
event.preventDefault();
|
|
1676
|
+
last.focus();
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
else if (event.shiftKey && current === first) {
|
|
1680
|
+
event.preventDefault();
|
|
1681
|
+
last.focus();
|
|
1682
|
+
}
|
|
1683
|
+
else if (!event.shiftKey && current === last) {
|
|
1684
|
+
event.preventDefault();
|
|
1685
|
+
first.focus();
|
|
1686
|
+
}
|
|
1687
|
+
else if (!current || !((_b = this.modalContent) === null || _b === void 0 ? void 0 : _b.contains(current))) {
|
|
1688
|
+
// Focus escaped the dialog (or never entered it) - pull it back in
|
|
1689
|
+
event.preventDefault();
|
|
1690
|
+
first.focus();
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
// offsetParent is not usable here: it is null for `position: fixed` elements
|
|
1694
|
+
// even when they are perfectly visible, and it is non-null for
|
|
1695
|
+
// `visibility: hidden` ones, which cannot be focused. getClientRects covers
|
|
1696
|
+
// display:none and detached nodes, so visibility is the only extra check.
|
|
1697
|
+
isFocusable(el) {
|
|
1698
|
+
if (el.hidden || el.getClientRects().length === 0) {
|
|
1699
|
+
return false;
|
|
1700
|
+
}
|
|
1701
|
+
return getComputedStyle(el).visibility !== 'hidden';
|
|
1702
|
+
}
|
|
1703
|
+
getFocusableElements() {
|
|
1704
|
+
if (!this.modalContent) {
|
|
1705
|
+
return [];
|
|
1706
|
+
}
|
|
1707
|
+
const selector = 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
1708
|
+
return Array.from(this.modalContent.querySelectorAll(selector)).filter((el) => this.isFocusable(el));
|
|
1709
|
+
}
|
|
1710
|
+
// Moves focus to the dialog container once it has rendered, so screen readers
|
|
1711
|
+
// announce the dialog title rather than jumping straight to the first control
|
|
1712
|
+
focusModal() {
|
|
1713
|
+
requestAnimationFrame(() => {
|
|
1714
|
+
if (this.modalContent) {
|
|
1715
|
+
this.modalContent.focus();
|
|
1716
|
+
}
|
|
1717
|
+
});
|
|
1718
|
+
}
|
|
1719
|
+
restoreFocus() {
|
|
1720
|
+
const target = this.previouslyFocused;
|
|
1721
|
+
this.previouslyFocused = null;
|
|
1722
|
+
if (target && typeof target.focus === 'function' && target.isConnected) {
|
|
1723
|
+
target.focus();
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
// The rating the widget starts from. Closing restores this rather than
|
|
1727
|
+
// blanking to neutral, so a consumer's configured `rating` default survives
|
|
1728
|
+
// the modal being closed and reopened.
|
|
1729
|
+
initialRating() {
|
|
1730
|
+
if (this.ratingMode == 'thumbs' && this.rating == 0) {
|
|
1731
|
+
return 5;
|
|
1732
|
+
}
|
|
1733
|
+
if (this.rating) {
|
|
1734
|
+
return this.rating;
|
|
1735
|
+
}
|
|
1736
|
+
return -1;
|
|
1388
1737
|
}
|
|
1389
1738
|
componentWillLoad() {
|
|
1390
1739
|
if (this.fetchData)
|
|
1391
1740
|
this.fetchProjectData();
|
|
1392
1741
|
this.formEmail = this.emailAddress;
|
|
1393
|
-
|
|
1394
|
-
this.selectedRating = this.rating;
|
|
1395
|
-
}
|
|
1396
|
-
if (this.ratingMode == 'thumbs' && this.rating == 0) {
|
|
1397
|
-
this.selectedRating = 5;
|
|
1398
|
-
}
|
|
1742
|
+
this.selectedRating = this.initialRating();
|
|
1399
1743
|
// Automatically show modal when embedded
|
|
1400
1744
|
if (this.embedded) {
|
|
1401
1745
|
this.showModal = true;
|
|
@@ -1403,7 +1747,9 @@ const FeedbackModal = class {
|
|
|
1403
1747
|
}
|
|
1404
1748
|
async fetchProjectData() {
|
|
1405
1749
|
try {
|
|
1406
|
-
|
|
1750
|
+
// Public config endpoint: never send the API key here, it is only
|
|
1751
|
+
// needed (and only allowed to travel) on the feedback POST
|
|
1752
|
+
const response = await fetch('https://app.pushfeedback.com/api/v1/projects/' + this.project + '/');
|
|
1407
1753
|
const data = await response.json();
|
|
1408
1754
|
this.whitelabel = data.whitelabel;
|
|
1409
1755
|
this.recaptchaEnabled = data.recaptcha_enabled || false;
|
|
@@ -1492,28 +1838,28 @@ const FeedbackModal = class {
|
|
|
1492
1838
|
this.selectedRating = newRating;
|
|
1493
1839
|
}
|
|
1494
1840
|
render() {
|
|
1495
|
-
return (h("div", { class: `feedback-modal-wrapper ${this.customFont ? 'feedback-modal-wrapper--custom-font' : ''} ${this.embedded ? 'feedback-modal-wrapper--embedded' : ''}`, part: "wrapper" }, this.showCanvasEditor && (h("canvas-editor", { ref: (el) => (this.canvasEditorRef = el), exportparts: "overlay: canvas-editor-overlay, modal: canvas-editor-modal, header: canvas-editor-header, title: canvas-editor-title, toolbar: canvas-editor-toolbar, tool-button: canvas-editor-tool-button, cancel-button: canvas-editor-cancel-button, save-button: canvas-editor-save-button, content: canvas-editor-content, canvas: canvas-editor-canvas", "canvas-editor-title": this.screenshotEditorTitle, "canvas-editor-cancel-text": this.screenshotEditorCancelText, "canvas-editor-save-text": this.screenshotEditorSaveText, "screenshot-taking-text": this.screenshotTakingText, "screenshot-attached-text": this.screenshotAttachedText, "screenshot-button-text": this.screenshotButtonText, "auto-start-screenshot": this.autoStartCapture, "existing-screenshot": this.encodedScreenshot || '', "edit-text-button-text": this.screenshotEditTextButtonText, "size-label-text": this.screenshotSizeLabelText, "border-label-text": this.screenshotBorderLabelText, "edit-text-prompt-text": this.screenshotEditTextPromptText, "screenshot-error-general": this.screenshotErrorGeneral, "screenshot-error-permission": this.screenshotErrorPermission, "screenshot-error-not-supported": this.screenshotErrorNotSupported, "screenshot-error-not-found": this.screenshotErrorNotFound, "screenshot-error-cancelled": this.screenshotErrorCancelled, "screenshot-error-browser-not-supported": this.screenshotErrorBrowserNotSupported, "screenshot-error-unexpected": this.screenshotErrorUnexpected, onScreenshotReady: this.handleScreenshotReady, onScreenshotCancelled: this.handleScreenshotCancelled, onScreenshotFailed: this.handleScreenshotError })), this.showScreenshotError && (h("div", { class: "screenshot-error-notification" }, h("div", { class: "screenshot-error-content" }, h("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, h("circle", { cx: "12", cy: "12", r: "10" }), h("line", { x1: "15", y1: "9", x2: "9", y2: "15" }), h("line", { x1: "9", y1: "9", x2: "15", y2: "15" })), h("span", null, this.screenshotError), h("button", { class: "error-close-btn", onClick: () => (this.showScreenshotError = false), title: "Close" }, h("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, h("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), h("line", { x1: "6", y1: "6", x2: "18", y2: "18" })))))), this.showModal && !this.embedded && (h("div", { class: `feedback-overlay ${this.isAnimating ? 'feedback-overlay--visible' : ''}`, part: "overlay", onClick: this.clickOutsideClose ? this.handleOverlayClick : undefined })), this.showModal && (h("div", { class: `feedback-modal-content feedback-modal-content--${this.modalPosition} ${this.isAnimating ? 'feedback-modal-content--open' : ''} ${this.embedded ? 'feedback-modal-content--embedded' : ''}`, part: "modal", ref: (el) => (this.modalContent = el) }, h("div", { class: `feedback-modal-header ${(this.formSuccess && !this.successMessage) || (this.formError && !this.errorMessage)
|
|
1841
|
+
return (h("div", { class: `feedback-modal-wrapper ${this.customFont ? 'feedback-modal-wrapper--custom-font' : ''} ${this.embedded ? 'feedback-modal-wrapper--embedded' : ''}`, part: "wrapper" }, this.showCanvasEditor && (h("canvas-editor", { ref: (el) => (this.canvasEditorRef = el), exportparts: "overlay: canvas-editor-overlay, modal: canvas-editor-modal, header: canvas-editor-header, title: canvas-editor-title, toolbar: canvas-editor-toolbar, tool-button: canvas-editor-tool-button, cancel-button: canvas-editor-cancel-button, save-button: canvas-editor-save-button, content: canvas-editor-content, canvas: canvas-editor-canvas", "canvas-editor-title": this.screenshotEditorTitle, "canvas-editor-cancel-text": this.screenshotEditorCancelText, "canvas-editor-save-text": this.screenshotEditorSaveText, "screenshot-taking-text": this.screenshotTakingText, "screenshot-attached-text": this.screenshotAttachedText, "screenshot-button-text": this.screenshotButtonText, "escape-close": this.escapeClose ? 'true' : 'false', "click-outside-close": this.clickOutsideClose ? 'true' : 'false', "auto-start-screenshot": this.autoStartCapture, "existing-screenshot": this.encodedScreenshot || '', "edit-text-button-text": this.screenshotEditTextButtonText, "size-label-text": this.screenshotSizeLabelText, "border-label-text": this.screenshotBorderLabelText, "edit-text-prompt-text": this.screenshotEditTextPromptText, "screenshot-error-general": this.screenshotErrorGeneral, "screenshot-error-permission": this.screenshotErrorPermission, "screenshot-error-not-supported": this.screenshotErrorNotSupported, "screenshot-error-not-found": this.screenshotErrorNotFound, "screenshot-error-cancelled": this.screenshotErrorCancelled, "screenshot-error-browser-not-supported": this.screenshotErrorBrowserNotSupported, "screenshot-error-unexpected": this.screenshotErrorUnexpected, onScreenshotReady: this.handleScreenshotReady, onScreenshotCancelled: this.handleScreenshotCancelled, onScreenshotFailed: this.handleScreenshotError })), this.showScreenshotError && (h("div", { class: "screenshot-error-notification" }, h("div", { class: "screenshot-error-content" }, h("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, h("circle", { cx: "12", cy: "12", r: "10" }), h("line", { x1: "15", y1: "9", x2: "9", y2: "15" }), h("line", { x1: "9", y1: "9", x2: "15", y2: "15" })), h("span", null, this.screenshotError), h("button", { class: "error-close-btn", onClick: () => (this.showScreenshotError = false), title: "Close" }, h("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, h("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), h("line", { x1: "6", y1: "6", x2: "18", y2: "18" })))))), this.showModal && !this.embedded && (h("div", { class: `feedback-overlay ${this.isAnimating ? 'feedback-overlay--visible' : ''}`, part: "overlay", "aria-hidden": "true", onClick: this.clickOutsideClose ? this.handleOverlayClick : undefined })), this.showModal && (h("div", { class: `feedback-modal-content feedback-modal-content--${this.modalPosition} ${this.isAnimating ? 'feedback-modal-content--open' : ''} ${this.embedded ? 'feedback-modal-content--embedded' : ''}`, part: "modal", role: this.embedded ? 'region' : 'dialog', "aria-modal": this.embedded ? undefined : 'true', "aria-labelledby": "feedback-modal-title", tabindex: "-1", ref: (el) => (this.modalContent = el) }, h("div", { class: `feedback-modal-header ${(this.formSuccess && !this.successMessage) || (this.formError && !this.errorMessage)
|
|
1496
1842
|
? 'feedback-modal-header--no-content'
|
|
1497
|
-
: ''}`, part: "header" }, h("div", { class: "feedback-modal-header-content" }, !this.formSuccess && !this.formError ? (h("div", { class: "feedback-modal-header-text" }, h("span", { class: "feedback-modal-title", part: "title" }, this.modalTitle), !this.whitelabel && (h("span", { class: "feedback-modal-powered-by", part: "powered-by" }, "Powered by", ' ', h("a", { target: "_blank", href: "https://pushfeedback.com" }, "PushFeedback"))))) : this.formSuccess ? (h("span", { class: "feedback-modal-title", part: "title" }, this.modalTitleSuccess)) : (h("span", { class: "feedback-modal-title", part: "title" }, this.modalTitleError))), !this.embedded && (h("button", { class: "feedback-modal-close", part: "close-button", 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", part: "body" }, !this.formSuccess && !this.formError ? (h("form", { part: "form", onSubmit: this.handleSubmit }, !this.hideRating && (h("div", { class: "feedback-modal-rating", part: "rating" }, this.ratingMode === 'thumbs' ? (h("div", { class: "feedback-modal-rating-content" }, h("span", { class: "feedback-modal-input-heading", part: "rating-title" }, this.ratingPlaceholder), h("div", { class: "feedback-modal-rating-buttons feedback-modal-rating-buttons--thumbs", part: "rating-buttons" }, h("button", { title: "
|
|
1843
|
+
: ''}`, part: "header" }, h("div", { class: "feedback-modal-header-content" }, !this.formSuccess && !this.formError ? (h("div", { class: "feedback-modal-header-text" }, h("span", { id: "feedback-modal-title", class: "feedback-modal-title", part: "title" }, this.modalTitle), !this.whitelabel && (h("span", { class: "feedback-modal-powered-by", part: "powered-by" }, "Powered by", ' ', h("a", { target: "_blank", href: "https://pushfeedback.com" }, "PushFeedback"))))) : this.formSuccess ? (h("span", { id: "feedback-modal-title", class: "feedback-modal-title", part: "title" }, this.modalTitleSuccess)) : (h("span", { id: "feedback-modal-title", class: "feedback-modal-title", part: "title" }, this.modalTitleError))), !this.embedded && (h("button", { class: "feedback-modal-close", part: "close-button", type: "button", "aria-label": this.closeButtonLabel, 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", part: "body" }, !this.formSuccess && !this.formError ? (h("form", { part: "form", onSubmit: this.handleSubmit }, !this.hideRating && (h("div", { class: "feedback-modal-rating", part: "rating" }, this.ratingMode === 'thumbs' ? (h("div", { class: "feedback-modal-rating-content" }, h("span", { class: "feedback-modal-input-heading", part: "rating-title" }, this.ratingPlaceholder), h("div", { class: "feedback-modal-rating-buttons feedback-modal-rating-buttons--thumbs", part: "rating-buttons" }, h("button", { type: "button", title: this.ratingPositiveLabel, "aria-label": this.ratingPositiveLabel, "aria-pressed": this.selectedRating === 1 ? 'true' : 'false', class: `feedback-modal-rating-button feedback-modal-rating-button--positive ${this.selectedRating === 1
|
|
1498
1844
|
? 'feedback-modal-rating-button--selected'
|
|
1499
1845
|
: ''}`, part: "rating-button rating-button-positive", onClick: (event) => {
|
|
1500
1846
|
event.preventDefault();
|
|
1501
1847
|
this.handleRatingChange(1);
|
|
1502
|
-
} }, 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: "
|
|
1848
|
+
} }, 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", { type: "button", title: this.ratingNegativeLabel, "aria-label": this.ratingNegativeLabel, "aria-pressed": this.selectedRating === 5 ? 'true' : 'false', class: `feedback-modal-rating-button feedback-modal-rating-button--negative ${this.selectedRating === 5
|
|
1503
1849
|
? 'feedback-modal-rating-button--selected'
|
|
1504
1850
|
: ''}`, part: "rating-button rating-button-negative", onClick: (event) => {
|
|
1505
1851
|
event.preventDefault();
|
|
1506
1852
|
this.handleRatingChange(5);
|
|
1507
|
-
} }, 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", part: "rating-title" }, this.ratingStarsPlaceholder), h("div", { class: "feedback-modal-rating-buttons feedback-modal-rating-buttons--stars", part: "rating-buttons" }, [1, 2, 3, 4, 5].map((rating) => (h("button", { key: rating, class: `feedback-modal-rating-button ${this.selectedRating >= rating
|
|
1853
|
+
} }, 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", part: "rating-title" }, this.ratingStarsPlaceholder), h("div", { class: "feedback-modal-rating-buttons feedback-modal-rating-buttons--stars", part: "rating-buttons" }, [1, 2, 3, 4, 5].map((rating) => (h("button", { key: rating, type: "button", "aria-label": this.ratingStarLabel.replace('{rating}', String(rating)), "aria-pressed": this.selectedRating === rating ? 'true' : 'false', class: `feedback-modal-rating-button ${this.selectedRating >= rating
|
|
1508
1854
|
? 'feedback-modal-rating-button--selected'
|
|
1509
1855
|
: ''}`, part: "rating-button", onClick: (event) => {
|
|
1510
1856
|
event.preventDefault();
|
|
1511
1857
|
this.handleRatingChange(rating);
|
|
1512
|
-
} }, 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", { part: "message-input", placeholder: this.messagePlaceholder, value: this.formMessage, onInput: (event) => this.handleMessageInput(event) })), !this.hideEmail && (h("div", { class: "feedback-modal-email" }, h("input", { part: "email-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", part: "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' : ''}`, part: "buttons" }, !this.hideScreenshotButton && (h("button", { type: "button", class: `feedback-modal-button feedback-modal-button--screenshot ${this.encodedScreenshot ? 'feedback-modal-button--active' : ''}`, part: "screenshot-button", 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 24 24", fill: "none", stroke: "#666", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", class: "feather-loader" }, h("line", { x1: "12", y1: "2", x2: "12", y2: "6" }), h("line", { x1: "12", y1: "18", x2: "12", y2: "22" }), h("line", { x1: "4.93", y1: "4.93", x2: "7.76", y2: "7.76" }), h("line", { x1: "16.24", y1: "16.24", x2: "19.07", y2: "19.07" }), h("line", { x1: "2", y1: "12", x2: "6", y2: "12" }), h("line", { x1: "18", y1: "12", x2: "22", y2: "12" }), h("line", { x1: "4.93", y1: "19.07", x2: "7.76", y2: "16.24" }), h("line", { x1: "16.24", y1: "7.76", x2: "19.07", y2: "4.93" })))), this.takingScreenshot
|
|
1858
|
+
} }, 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", { part: "message-input", placeholder: this.messagePlaceholder, value: this.formMessage, onInput: (event) => this.handleMessageInput(event) })), !this.hideEmail && (h("div", { class: "feedback-modal-email" }, h("input", { part: "email-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", part: "privacy" }, h("input", { type: "checkbox", id: "privacyPolicy", onChange: (ev) => this.handleCheckboxChange(ev), required: true }), h("span", { innerHTML: sanitizeHtml(this.privacyPolicyText) }))), h("div", { class: `feedback-modal-buttons ${this.hideScreenshotButton ? 'single' : ''}`, part: "buttons" }, !this.hideScreenshotButton && (h("button", { type: "button", class: `feedback-modal-button feedback-modal-button--screenshot ${this.encodedScreenshot ? 'feedback-modal-button--active' : ''}`, part: "screenshot-button", 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 24 24", fill: "none", stroke: "#666", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", class: "feather-loader" }, h("line", { x1: "12", y1: "2", x2: "12", y2: "6" }), h("line", { x1: "12", y1: "18", x2: "12", y2: "22" }), h("line", { x1: "4.93", y1: "4.93", x2: "7.76", y2: "7.76" }), h("line", { x1: "16.24", y1: "16.24", x2: "19.07", y2: "19.07" }), h("line", { x1: "2", y1: "12", x2: "6", y2: "12" }), h("line", { x1: "18", y1: "12", x2: "22", y2: "12" }), h("line", { x1: "4.93", y1: "19.07", x2: "7.76", y2: "16.24" }), h("line", { x1: "16.24", y1: "7.76", x2: "19.07", y2: "4.93" })))), this.takingScreenshot
|
|
1513
1859
|
? this.screenshotTakingText
|
|
1514
1860
|
: this.encodedScreenshot
|
|
1515
1861
|
? this.screenshotAttachedText
|
|
1516
|
-
: this.screenshotButtonText)), h("button", { class: "feedback-modal-button feedback-modal-button--submit", part: "submit-button", type: "submit", disabled: this.sending }, this.sendButtonText)))) : this.formSuccess && !this.formError ? (h("div", { class: "feedback-modal-success" }, h("p", { class: "feedback-modal-message", part: "success-message" }, this.successMessage))) : this.formError && this.formErrorStatus == 404 ? (h("p", { class: "feedback-modal-message", part: "error-message" }, this.errorMessage404)) : this.formError && this.formErrorStatus == 403 ? (h("p", { class: "feedback-modal-message", part: "error-message" }, this.errorMessage403)) : this.formError ? (h("p", { class: "feedback-modal-message", part: "error-message" }, this.errorMessage)) : (h("span", null))), !this.formSuccess && !this.formError && (this.recaptchaEnabled || this.footerText) && (h("div", { class: "feedback-modal-footer", part: "footer" }, h("div", { class: "feedback-footer-combined" }, this.recaptchaEnabled && h("span", { innerHTML: this.recaptchaText }), this.recaptchaEnabled && this.footerText && ' ', this.footerText && h("span", { innerHTML: this.footerText }))))))));
|
|
1862
|
+
: this.screenshotButtonText)), h("button", { class: "feedback-modal-button feedback-modal-button--submit", part: "submit-button", type: "submit", disabled: this.sending }, this.sendButtonText)))) : this.formSuccess && !this.formError ? (h("div", { class: "feedback-modal-success" }, h("p", { class: "feedback-modal-message", part: "success-message" }, this.successMessage))) : this.formError && this.formErrorStatus == 404 ? (h("p", { class: "feedback-modal-message", part: "error-message" }, this.errorMessage404)) : this.formError && this.formErrorStatus == 401 ? (h("p", { class: "feedback-modal-message", part: "error-message" }, this.errorMessage401)) : this.formError && this.formErrorStatus == 403 ? (h("p", { class: "feedback-modal-message", part: "error-message" }, this.errorMessage403)) : this.formError ? (h("p", { class: "feedback-modal-message", part: "error-message" }, this.errorMessage)) : (h("span", null))), !this.formSuccess && !this.formError && (this.recaptchaEnabled || this.footerText) && (h("div", { class: "feedback-modal-footer", part: "footer" }, h("div", { class: "feedback-footer-combined" }, this.recaptchaEnabled && (h("span", { innerHTML: sanitizeHtml(this.recaptchaText) })), this.recaptchaEnabled && this.footerText && ' ', this.footerText && h("span", { innerHTML: sanitizeHtml(this.footerText) }))))))));
|
|
1517
1863
|
}
|
|
1518
1864
|
componentDidRender() {
|
|
1519
1865
|
if (this.showModal) {
|
|
@@ -1523,7 +1869,13 @@ const FeedbackModal = class {
|
|
|
1523
1869
|
}
|
|
1524
1870
|
}
|
|
1525
1871
|
async openModal() {
|
|
1872
|
+
// Remember the trigger so focus can return there when the modal closes
|
|
1873
|
+
if (!this.embedded) {
|
|
1874
|
+
const active = document.activeElement;
|
|
1875
|
+
this.previouslyFocused = active && active !== document.body ? active : null;
|
|
1876
|
+
}
|
|
1526
1877
|
this.showModal = true;
|
|
1878
|
+
this.focusModal();
|
|
1527
1879
|
if (this.historyClose && !this.embedded && !this.historyEntryPushed) {
|
|
1528
1880
|
history.pushState({ feedbackModal: true }, '');
|
|
1529
1881
|
this.historyEntryPushed = true;
|