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,6 +2,9 @@ import { proxyCustomElement, HTMLElement, createEvent, h } from '@stencil/core/i
|
|
|
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 = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement {
|
|
6
9
|
constructor() {
|
|
7
10
|
super();
|
|
@@ -10,6 +13,50 @@ const CanvasEditor = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement
|
|
|
10
13
|
this.screenshotReady = createEvent(this, "screenshotReady", 7);
|
|
11
14
|
this.screenshotCancelled = createEvent(this, "screenshotCancelled", 7);
|
|
12
15
|
this.screenshotFailed = createEvent(this, "screenshotFailed", 7);
|
|
16
|
+
this.handleKeyDown = (event) => {
|
|
17
|
+
if (!this.showCanvasEditor || !this.escapeClose || event.key !== 'Escape') {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
// While the browser is capturing the screen, the picker owns the keyboard
|
|
21
|
+
if (this.takingScreenshot) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
event.preventDefault();
|
|
25
|
+
// The colour picker is a popup on top of the editor, so Escape dismisses
|
|
26
|
+
// that first and leaves the editor (and any annotations) in place
|
|
27
|
+
if (this.showColorPicker) {
|
|
28
|
+
this.showColorPicker = false;
|
|
29
|
+
this.editingColorIndex = -1;
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
// Deselect before closing, the way drawing tools do. Closing throws away
|
|
33
|
+
// every annotation, so someone who has just selected one gets a cheap
|
|
34
|
+
// Escape that only drops the selection rather than their work
|
|
35
|
+
if (this.selectedAnnotation) {
|
|
36
|
+
this.selectedAnnotation = null;
|
|
37
|
+
this.redrawAnnotations();
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
// Same as the Cancel button: discards annotations and returns to the form
|
|
41
|
+
this.closeCanvasEditor();
|
|
42
|
+
};
|
|
43
|
+
// Tracks whether the press that started this click landed on the backdrop.
|
|
44
|
+
// Dragging an annotation and releasing past the edge of the editor fires a
|
|
45
|
+
// click on the backdrop too, and that must not be read as "clicked outside".
|
|
46
|
+
this.pressStartedOnOverlay = false;
|
|
47
|
+
this.handleOverlayMouseDown = (event) => {
|
|
48
|
+
this.pressStartedOnOverlay = event.target === event.currentTarget;
|
|
49
|
+
};
|
|
50
|
+
this.handleOverlayClick = (event) => {
|
|
51
|
+
const startedOutside = this.pressStartedOnOverlay;
|
|
52
|
+
this.pressStartedOnOverlay = false;
|
|
53
|
+
if (!this.clickOutsideClose || !startedOutside || event.target !== event.currentTarget) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
// Closes the editor only. The feedback form reopens behind it, so a stray
|
|
57
|
+
// click never dismisses the whole widget
|
|
58
|
+
this.closeCanvasEditor();
|
|
59
|
+
};
|
|
13
60
|
this.handleWindowResize = () => {
|
|
14
61
|
// Debounce resize events
|
|
15
62
|
if (this.resizeTimeout) {
|
|
@@ -689,8 +736,16 @@ const CanvasEditor = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement
|
|
|
689
736
|
if (!this.isDrawing || !this.currentAnnotation)
|
|
690
737
|
return;
|
|
691
738
|
this.isDrawing = false;
|
|
692
|
-
|
|
739
|
+
const annotation = this.currentAnnotation;
|
|
693
740
|
this.currentAnnotation = null;
|
|
741
|
+
// A click with no drag never reaches mousemove, so the shape has no end
|
|
742
|
+
// point or size. Committing it added an invisible annotation that could
|
|
743
|
+
// not be selected, dragged or deleted, and it still consumed an undo step.
|
|
744
|
+
if (this.isDegenerateAnnotation(annotation)) {
|
|
745
|
+
this.redrawAnnotations();
|
|
746
|
+
return;
|
|
747
|
+
}
|
|
748
|
+
this.annotations = [...this.annotations, annotation];
|
|
694
749
|
this.redrawAnnotations();
|
|
695
750
|
};
|
|
696
751
|
// Convert screen coordinates to canvas coordinates
|
|
@@ -732,11 +787,28 @@ const CanvasEditor = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement
|
|
|
732
787
|
}
|
|
733
788
|
case 'line':
|
|
734
789
|
case 'arrow': {
|
|
735
|
-
// Distance from point to line
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
790
|
+
// Distance from the point to the line *segment*, not to the infinite
|
|
791
|
+
// line through it. The previous formula measured the infinite line, so
|
|
792
|
+
// a click far past either end still counted as a hit, and a zero-length
|
|
793
|
+
// segment divided by zero and gave NaN, leaving that annotation
|
|
794
|
+
// impossible to select, drag or delete.
|
|
795
|
+
// A click with no drag never reaches mousemove, so endX/endY are
|
|
796
|
+
// undefined rather than equal to the start. Treat that as a segment
|
|
797
|
+
// collapsed to its start point instead of letting NaN propagate.
|
|
798
|
+
const endX = Number.isFinite(annotation.endX) ? annotation.endX : annotation.startX;
|
|
799
|
+
const endY = Number.isFinite(annotation.endY) ? annotation.endY : annotation.startY;
|
|
800
|
+
const dx = endX - annotation.startX;
|
|
801
|
+
const dy = endY - annotation.startY;
|
|
802
|
+
const lengthSquared = dx * dx + dy * dy;
|
|
803
|
+
// Zero-length segment: t stays 0 and we measure to the single point
|
|
804
|
+
let t = 0;
|
|
805
|
+
if (lengthSquared > 0) {
|
|
806
|
+
t = ((x - annotation.startX) * dx + (y - annotation.startY) * dy) / lengthSquared;
|
|
807
|
+
t = Math.max(0, Math.min(1, t));
|
|
808
|
+
}
|
|
809
|
+
const closestX = annotation.startX + t * dx;
|
|
810
|
+
const closestY = annotation.startY + t * dy;
|
|
811
|
+
const distance = Math.hypot(x - closestX, y - closestY);
|
|
740
812
|
return distance <= tolerance;
|
|
741
813
|
}
|
|
742
814
|
case 'text': {
|
|
@@ -761,6 +833,8 @@ const CanvasEditor = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement
|
|
|
761
833
|
this.screenshotTakingText = 'Taking screenshot...';
|
|
762
834
|
this.screenshotAttachedText = 'Screenshot attached';
|
|
763
835
|
this.screenshotButtonText = 'Add a screenshot';
|
|
836
|
+
this.escapeClose = true;
|
|
837
|
+
this.clickOutsideClose = true;
|
|
764
838
|
this.autoStartScreenshot = false;
|
|
765
839
|
this.existingScreenshot = '';
|
|
766
840
|
this.editTextButtonText = 'Edit Text';
|
|
@@ -816,10 +890,12 @@ const CanvasEditor = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement
|
|
|
816
890
|
// Add window resize listener for canvas adaptation
|
|
817
891
|
this.handleWindowResize = this.handleWindowResize.bind(this);
|
|
818
892
|
window.addEventListener('resize', this.handleWindowResize);
|
|
893
|
+
document.addEventListener('keydown', this.handleKeyDown);
|
|
819
894
|
}
|
|
820
895
|
disconnectedCallback() {
|
|
821
896
|
// Clean up resize listener
|
|
822
897
|
window.removeEventListener('resize', this.handleWindowResize);
|
|
898
|
+
document.removeEventListener('keydown', this.handleKeyDown);
|
|
823
899
|
}
|
|
824
900
|
async captureViewportScreenshot() {
|
|
825
901
|
try {
|
|
@@ -843,6 +919,30 @@ const CanvasEditor = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement
|
|
|
843
919
|
video.autoplay = true;
|
|
844
920
|
video.muted = true;
|
|
845
921
|
return new Promise((resolve, reject) => {
|
|
922
|
+
// If neither onloadedmetadata nor onerror ever fires, the promise would
|
|
923
|
+
// never settle: the capture stream would stay live with the browser's
|
|
924
|
+
// recording indicator on, and the widget would sit in "Taking
|
|
925
|
+
// screenshot..." forever with no way back short of a page reload.
|
|
926
|
+
let settled = false;
|
|
927
|
+
const stopStream = () => {
|
|
928
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
929
|
+
};
|
|
930
|
+
const finish = (fn) => {
|
|
931
|
+
if (settled) {
|
|
932
|
+
return;
|
|
933
|
+
}
|
|
934
|
+
settled = true;
|
|
935
|
+
clearTimeout(watchdog);
|
|
936
|
+
video.onloadedmetadata = null;
|
|
937
|
+
video.onerror = null;
|
|
938
|
+
fn();
|
|
939
|
+
};
|
|
940
|
+
const watchdog = setTimeout(() => {
|
|
941
|
+
finish(() => {
|
|
942
|
+
stopStream();
|
|
943
|
+
reject(new Error('Screenshot capture timed out'));
|
|
944
|
+
});
|
|
945
|
+
}, CAPTURE_TIMEOUT_MS);
|
|
846
946
|
video.onloadedmetadata = () => {
|
|
847
947
|
video.play();
|
|
848
948
|
// Wait a moment for video to stabilize
|
|
@@ -854,22 +954,26 @@ const CanvasEditor = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement
|
|
|
854
954
|
canvas.height = video.videoHeight;
|
|
855
955
|
const ctx = canvas.getContext('2d');
|
|
856
956
|
ctx.drawImage(video, 0, 0);
|
|
857
|
-
// Stop the stream
|
|
858
|
-
stream.getTracks().forEach((track) => track.stop());
|
|
859
957
|
// Convert to data URL
|
|
860
958
|
const dataUrl = canvas.toDataURL('image/png');
|
|
861
|
-
|
|
862
|
-
|
|
959
|
+
finish(() => {
|
|
960
|
+
stopStream();
|
|
961
|
+
resolve(dataUrl);
|
|
962
|
+
});
|
|
863
963
|
}
|
|
864
964
|
catch (error) {
|
|
865
|
-
|
|
866
|
-
|
|
965
|
+
finish(() => {
|
|
966
|
+
stopStream();
|
|
967
|
+
reject(error);
|
|
968
|
+
});
|
|
867
969
|
}
|
|
868
970
|
}, 100);
|
|
869
971
|
};
|
|
870
972
|
video.onerror = () => {
|
|
871
|
-
|
|
872
|
-
|
|
973
|
+
finish(() => {
|
|
974
|
+
stopStream();
|
|
975
|
+
reject(new Error('Failed to load video for screenshot capture'));
|
|
976
|
+
});
|
|
873
977
|
};
|
|
874
978
|
});
|
|
875
979
|
}
|
|
@@ -878,9 +982,27 @@ const CanvasEditor = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement
|
|
|
878
982
|
throw error;
|
|
879
983
|
}
|
|
880
984
|
}
|
|
985
|
+
// Whether a freshly drawn shape is too small to be worth keeping
|
|
986
|
+
isDegenerateAnnotation(annotation) {
|
|
987
|
+
const MIN_SIZE = 2; // canvas pixels
|
|
988
|
+
if (annotation.type === 'line' || annotation.type === 'arrow') {
|
|
989
|
+
if (!Number.isFinite(annotation.endX) || !Number.isFinite(annotation.endY)) {
|
|
990
|
+
return true;
|
|
991
|
+
}
|
|
992
|
+
return (Math.hypot(annotation.endX - annotation.startX, annotation.endY - annotation.startY) <
|
|
993
|
+
MIN_SIZE);
|
|
994
|
+
}
|
|
995
|
+
if (annotation.type === 'rectangle') {
|
|
996
|
+
if (!Number.isFinite(annotation.width) || !Number.isFinite(annotation.height)) {
|
|
997
|
+
return true;
|
|
998
|
+
}
|
|
999
|
+
return Math.abs(annotation.width) < MIN_SIZE && Math.abs(annotation.height) < MIN_SIZE;
|
|
1000
|
+
}
|
|
1001
|
+
return false;
|
|
1002
|
+
}
|
|
881
1003
|
render() {
|
|
882
1004
|
var _a, _b, _c, _d, _e, _f;
|
|
883
|
-
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",
|
|
1005
|
+
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' ||
|
|
884
1006
|
(!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) => {
|
|
885
1007
|
const newSize = parseInt(e.target.value);
|
|
886
1008
|
if (this.selectedAnnotation) {
|
|
@@ -909,6 +1031,8 @@ const CanvasEditor = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement
|
|
|
909
1031
|
"screenshotTakingText": [1, "screenshot-taking-text"],
|
|
910
1032
|
"screenshotAttachedText": [1, "screenshot-attached-text"],
|
|
911
1033
|
"screenshotButtonText": [1, "screenshot-button-text"],
|
|
1034
|
+
"escapeClose": [4, "escape-close"],
|
|
1035
|
+
"clickOutsideClose": [4, "click-outside-close"],
|
|
912
1036
|
"autoStartScreenshot": [4, "auto-start-screenshot"],
|
|
913
1037
|
"existingScreenshot": [1, "existing-screenshot"],
|
|
914
1038
|
"editTextButtonText": [1, "edit-text-button-text"],
|
|
@@ -2,7 +2,7 @@ import { proxyCustomElement, HTMLElement, createEvent, h, Host } from '@stencil/
|
|
|
2
2
|
import { d as defineCustomElement$3 } from './canvas-editor2.js';
|
|
3
3
|
import { d as defineCustomElement$2 } from './feedback-modal2.js';
|
|
4
4
|
|
|
5
|
-
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}}";
|
|
5
|
+
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}}";
|
|
6
6
|
|
|
7
7
|
const FeedbackButton$1 = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement {
|
|
8
8
|
constructor() {
|
|
@@ -18,6 +18,7 @@ const FeedbackButton$1 = /*@__PURE__*/ proxyCustomElement(class extends HTMLElem
|
|
|
18
18
|
this.sessionId = '';
|
|
19
19
|
this.metadata = '';
|
|
20
20
|
this.submit = false;
|
|
21
|
+
this.apiKey = '';
|
|
21
22
|
this.clickOutsideClose = true;
|
|
22
23
|
this.customFont = false;
|
|
23
24
|
this.emailAddress = '';
|
|
@@ -34,6 +35,7 @@ const FeedbackButton$1 = /*@__PURE__*/ proxyCustomElement(class extends HTMLElem
|
|
|
34
35
|
this.ratingMode = 'thumbs';
|
|
35
36
|
this.emailPlaceholder = 'Email address (optional)';
|
|
36
37
|
this.errorMessage = 'Please try again later.';
|
|
38
|
+
this.errorMessage401 = 'This project requires a valid API key to submit feedback. Check the api-key attribute.';
|
|
37
39
|
this.errorMessage403 = 'The request URL does not match the one defined in PushFeedback for this project.';
|
|
38
40
|
this.errorMessage404 = 'We could not find the provided project id in PushFeedback.';
|
|
39
41
|
this.footerText = '';
|
|
@@ -79,13 +81,19 @@ const FeedbackButton$1 = /*@__PURE__*/ proxyCustomElement(class extends HTMLElem
|
|
|
79
81
|
}
|
|
80
82
|
}
|
|
81
83
|
componentDidLoad() {
|
|
82
|
-
if (this.buttonPosition === 'center-right') {
|
|
84
|
+
if (this.buttonPosition === 'center-right' || this.buttonPosition === 'center-left') {
|
|
83
85
|
const buttonContent = this.el.shadowRoot.querySelector('.feedback-button-content');
|
|
84
86
|
let adjustement = 0;
|
|
85
87
|
if (this.isSafariBrowser()) {
|
|
86
88
|
adjustement = 5;
|
|
87
89
|
}
|
|
88
|
-
|
|
90
|
+
const offset = `${((buttonContent.offsetWidth + adjustement) / 2) * -1}px`;
|
|
91
|
+
if (this.buttonPosition === 'center-right') {
|
|
92
|
+
buttonContent.style.right = offset;
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
buttonContent.style.left = offset;
|
|
96
|
+
}
|
|
89
97
|
}
|
|
90
98
|
if (!this.customFont) {
|
|
91
99
|
this.loadInterFont();
|
|
@@ -94,6 +102,7 @@ const FeedbackButton$1 = /*@__PURE__*/ proxyCustomElement(class extends HTMLElem
|
|
|
94
102
|
connectedCallback() {
|
|
95
103
|
this.feedbackModal = document.createElement('feedback-modal');
|
|
96
104
|
const props = [
|
|
105
|
+
'apiKey',
|
|
97
106
|
'clickOutsideClose',
|
|
98
107
|
'customFont',
|
|
99
108
|
'emailAddress',
|
|
@@ -124,6 +133,7 @@ const FeedbackButton$1 = /*@__PURE__*/ proxyCustomElement(class extends HTMLElem
|
|
|
124
133
|
'screenshotErrorUnexpected',
|
|
125
134
|
'emailPlaceholder',
|
|
126
135
|
'errorMessage',
|
|
136
|
+
'errorMessage401',
|
|
127
137
|
'errorMessage403',
|
|
128
138
|
'errorMessage404',
|
|
129
139
|
'footerText',
|
|
@@ -188,12 +198,16 @@ const FeedbackButton$1 = /*@__PURE__*/ proxyCustomElement(class extends HTMLElem
|
|
|
188
198
|
}
|
|
189
199
|
}
|
|
190
200
|
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 }));
|
|
191
|
-
const
|
|
201
|
+
const headers = {
|
|
202
|
+
'Content-Type': 'application/json',
|
|
203
|
+
};
|
|
204
|
+
if (this.apiKey) {
|
|
205
|
+
headers['Authorization'] = `Api-Key ${this.apiKey}`;
|
|
206
|
+
}
|
|
207
|
+
const res = await fetch('https://app.pushfeedback.com/api/v1/feedback/', {
|
|
192
208
|
method: 'POST',
|
|
193
209
|
body: JSON.stringify(body),
|
|
194
|
-
headers
|
|
195
|
-
'Content-Type': 'application/json',
|
|
196
|
-
},
|
|
210
|
+
headers,
|
|
197
211
|
});
|
|
198
212
|
if (res.status === 201) {
|
|
199
213
|
const feedback_with_id = Object.assign(Object.assign({}, body), { id: await res.json() });
|
|
@@ -224,7 +238,7 @@ const FeedbackButton$1 = /*@__PURE__*/ proxyCustomElement(class extends HTMLElem
|
|
|
224
238
|
}
|
|
225
239
|
}
|
|
226
240
|
render() {
|
|
227
|
-
return (h(Host, null, h("
|
|
241
|
+
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))));
|
|
228
242
|
}
|
|
229
243
|
get el() { return this; }
|
|
230
244
|
static get style() { return feedbackButtonCss; }
|
|
@@ -236,6 +250,7 @@ const FeedbackButton$1 = /*@__PURE__*/ proxyCustomElement(class extends HTMLElem
|
|
|
236
250
|
"sessionId": [1537, "session-id"],
|
|
237
251
|
"metadata": [1],
|
|
238
252
|
"submit": [4],
|
|
253
|
+
"apiKey": [1, "api-key"],
|
|
239
254
|
"clickOutsideClose": [4, "click-outside-close"],
|
|
240
255
|
"customFont": [4, "custom-font"],
|
|
241
256
|
"emailAddress": [1, "email-address"],
|
|
@@ -252,6 +267,7 @@ const FeedbackButton$1 = /*@__PURE__*/ proxyCustomElement(class extends HTMLElem
|
|
|
252
267
|
"ratingMode": [1, "rating-mode"],
|
|
253
268
|
"emailPlaceholder": [1, "email-placeholder"],
|
|
254
269
|
"errorMessage": [1, "error-message"],
|
|
270
|
+
"errorMessage401": [1, "error-message-4-0-1"],
|
|
255
271
|
"errorMessage403": [1, "error-message-4-0-3"],
|
|
256
272
|
"errorMessage404": [1, "error-message-4-0-4"],
|
|
257
273
|
"footerText": [1, "footer-text"],
|