stimulus-pdf-viewer-rails 0.2.1 → 0.3.1
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.
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6fe6b668cbffa8acaf9ade9ef15614aad4d7677e3dc505bebe4065ba8ccdb726
|
|
4
|
+
data.tar.gz: f8f51d4e310e36beb6dd51efc9dd3501daaa15067b2a853912d074427a04b9f7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ad37d63e9f0c26f373b293737818fd85121bf78945f8e97ab06371f75d2aa3ac6b8b4b9976a27cb5553b5cb3f738b51258a82541761bb78531116e34fa27e90f
|
|
7
|
+
data.tar.gz: 41b654cae115f12ef9a8f33c80d5d0645657cbccc7b70ee94651dd557b2ea416b38ace501b7a0e43cf9387dc8b16237c2f7e442915d338e1c03dd0ee3108ad45
|
data/CHANGELOG.md
CHANGED
|
@@ -2578,12 +2578,12 @@ class AnnotationEditToolbar {
|
|
|
2578
2578
|
});
|
|
2579
2579
|
|
|
2580
2580
|
// Handle keyboard shortcuts
|
|
2581
|
-
|
|
2581
|
+
this._keydownHandler = (e) => {
|
|
2582
2582
|
if (this.element.classList.contains("hidden")) return
|
|
2583
2583
|
|
|
2584
2584
|
// Don't intercept if user is typing
|
|
2585
2585
|
const activeEl = document.activeElement;
|
|
2586
|
-
if (activeEl && (activeEl.tagName === "INPUT" || activeEl.tagName === "TEXTAREA")) return
|
|
2586
|
+
if (activeEl && (activeEl.tagName === "INPUT" || activeEl.tagName === "TEXTAREA" || activeEl.isContentEditable)) return
|
|
2587
2587
|
|
|
2588
2588
|
if (e.key === "Escape") {
|
|
2589
2589
|
if (this.colorDropdownOpen) {
|
|
@@ -2613,7 +2613,8 @@ class AnnotationEditToolbar {
|
|
|
2613
2613
|
this.onComment(this.currentAnnotation);
|
|
2614
2614
|
}
|
|
2615
2615
|
}
|
|
2616
|
-
}
|
|
2616
|
+
};
|
|
2617
|
+
document.addEventListener("keydown", this._keydownHandler);
|
|
2617
2618
|
}
|
|
2618
2619
|
|
|
2619
2620
|
_toggleColorDropdown() {
|
|
@@ -2722,6 +2723,347 @@ class AnnotationEditToolbar {
|
|
|
2722
2723
|
}
|
|
2723
2724
|
|
|
2724
2725
|
destroy() {
|
|
2726
|
+
if (this._keydownHandler) {
|
|
2727
|
+
document.removeEventListener("keydown", this._keydownHandler);
|
|
2728
|
+
}
|
|
2729
|
+
this.element.remove();
|
|
2730
|
+
}
|
|
2731
|
+
}
|
|
2732
|
+
|
|
2733
|
+
class AnnotationDetailPanel {
|
|
2734
|
+
constructor(options = {}) {
|
|
2735
|
+
this.container = options.container;
|
|
2736
|
+
this.onColorChange = options.onColorChange;
|
|
2737
|
+
this.onDelete = options.onDelete;
|
|
2738
|
+
this.onEdit = options.onEdit;
|
|
2739
|
+
this.onComment = options.onComment;
|
|
2740
|
+
this.onClose = options.onClose;
|
|
2741
|
+
this.colors = options.colors || ColorPicker.COLORS.map(c => c.value);
|
|
2742
|
+
|
|
2743
|
+
this.currentAnnotation = null;
|
|
2744
|
+
this.anchorElement = null;
|
|
2745
|
+
this.colorDropdownOpen = false;
|
|
2746
|
+
|
|
2747
|
+
this._createPanel();
|
|
2748
|
+
this._setupEventListeners();
|
|
2749
|
+
}
|
|
2750
|
+
|
|
2751
|
+
_createPanel() {
|
|
2752
|
+
this.element = document.createElement("div");
|
|
2753
|
+
this.element.className = "annotation-detail-panel hidden";
|
|
2754
|
+
this.element.innerHTML = `
|
|
2755
|
+
<div class="annotation-detail-header">
|
|
2756
|
+
<div class="annotation-detail-header-info">
|
|
2757
|
+
<span class="annotation-detail-type"></span>
|
|
2758
|
+
</div>
|
|
2759
|
+
<div class="annotation-detail-header-actions">
|
|
2760
|
+
<button class="toolbar-btn comment-btn hidden" title="Add Comment (C)">
|
|
2761
|
+
${Icons.comment}
|
|
2762
|
+
</button>
|
|
2763
|
+
<button class="color-picker-btn" title="Change color" aria-haspopup="true" aria-expanded="false">
|
|
2764
|
+
<span class="color-swatch"></span>
|
|
2765
|
+
${Icons.chevronDown}
|
|
2766
|
+
</button>
|
|
2767
|
+
<div class="color-dropdown hidden">
|
|
2768
|
+
${this.colors.map(color => `
|
|
2769
|
+
<button class="color-option" data-color="${color}" aria-selected="false">
|
|
2770
|
+
<span class="color-swatch" style="background-color: ${color}"></span>
|
|
2771
|
+
</button>
|
|
2772
|
+
`).join("")}
|
|
2773
|
+
</div>
|
|
2774
|
+
<button class="toolbar-btn edit-btn hidden" title="Edit (E)">
|
|
2775
|
+
${Icons.edit}
|
|
2776
|
+
</button>
|
|
2777
|
+
<div class="toolbar-divider"></div>
|
|
2778
|
+
<button class="toolbar-btn delete-btn" title="Delete (Delete)">
|
|
2779
|
+
${Icons.delete}
|
|
2780
|
+
</button>
|
|
2781
|
+
<div class="toolbar-divider"></div>
|
|
2782
|
+
<button class="annotation-detail-close" title="Close (Escape)">
|
|
2783
|
+
${Icons.close}
|
|
2784
|
+
</button>
|
|
2785
|
+
</div>
|
|
2786
|
+
</div>
|
|
2787
|
+
<div class="annotation-detail-body">
|
|
2788
|
+
<div class="annotation-detail-text hidden"></div>
|
|
2789
|
+
<div class="annotation-detail-content-slot"></div>
|
|
2790
|
+
</div>
|
|
2791
|
+
`;
|
|
2792
|
+
|
|
2793
|
+
this.commentBtn = this.element.querySelector(".comment-btn");
|
|
2794
|
+
this.editBtn = this.element.querySelector(".edit-btn");
|
|
2795
|
+
this.typeLabel = this.element.querySelector(".annotation-detail-type");
|
|
2796
|
+
this.textContent = this.element.querySelector(".annotation-detail-text");
|
|
2797
|
+
this.contentSlot = this.element.querySelector(".annotation-detail-content-slot");
|
|
2798
|
+
}
|
|
2799
|
+
|
|
2800
|
+
_setupEventListeners() {
|
|
2801
|
+
// Prevent pointer/click events inside the panel from triggering annotation tools
|
|
2802
|
+
// Tools listen on pointerdown on the pages container, so we must stop all phases
|
|
2803
|
+
this.element.addEventListener("pointerdown", (e) => {
|
|
2804
|
+
e.stopPropagation();
|
|
2805
|
+
});
|
|
2806
|
+
this.element.addEventListener("click", (e) => {
|
|
2807
|
+
e.stopPropagation();
|
|
2808
|
+
});
|
|
2809
|
+
|
|
2810
|
+
// Close button
|
|
2811
|
+
this.element.querySelector(".annotation-detail-close").addEventListener("click", (e) => {
|
|
2812
|
+
e.stopPropagation();
|
|
2813
|
+
this.onClose?.();
|
|
2814
|
+
});
|
|
2815
|
+
|
|
2816
|
+
// Color picker button
|
|
2817
|
+
const colorBtn = this.element.querySelector(".color-picker-btn");
|
|
2818
|
+
colorBtn.addEventListener("click", (e) => {
|
|
2819
|
+
e.stopPropagation();
|
|
2820
|
+
this._toggleColorDropdown();
|
|
2821
|
+
});
|
|
2822
|
+
|
|
2823
|
+
// Color options
|
|
2824
|
+
this.element.querySelectorAll(".color-option").forEach(option => {
|
|
2825
|
+
option.addEventListener("click", (e) => {
|
|
2826
|
+
e.stopPropagation();
|
|
2827
|
+
this._selectColor(option.dataset.color);
|
|
2828
|
+
});
|
|
2829
|
+
});
|
|
2830
|
+
|
|
2831
|
+
// Comment button
|
|
2832
|
+
this.commentBtn.addEventListener("click", (e) => {
|
|
2833
|
+
e.stopPropagation();
|
|
2834
|
+
if (this.currentAnnotation && this.onComment) {
|
|
2835
|
+
this.onComment(this.currentAnnotation);
|
|
2836
|
+
}
|
|
2837
|
+
});
|
|
2838
|
+
|
|
2839
|
+
// Edit button
|
|
2840
|
+
this.editBtn.addEventListener("click", (e) => {
|
|
2841
|
+
e.stopPropagation();
|
|
2842
|
+
if (this.currentAnnotation && this.onEdit) {
|
|
2843
|
+
this.onEdit(this.currentAnnotation);
|
|
2844
|
+
}
|
|
2845
|
+
});
|
|
2846
|
+
|
|
2847
|
+
// Delete button (hide is handled by _deselectAnnotation cascade)
|
|
2848
|
+
this.element.querySelector(".delete-btn").addEventListener("click", (e) => {
|
|
2849
|
+
e.stopPropagation();
|
|
2850
|
+
if (this.currentAnnotation && this.onDelete) {
|
|
2851
|
+
this.onDelete(this.currentAnnotation);
|
|
2852
|
+
}
|
|
2853
|
+
});
|
|
2854
|
+
|
|
2855
|
+
// Close color dropdown on outside click
|
|
2856
|
+
document.addEventListener("click", (e) => {
|
|
2857
|
+
if (this.colorDropdownOpen && !this.element.contains(e.target)) {
|
|
2858
|
+
this._closeColorDropdown();
|
|
2859
|
+
}
|
|
2860
|
+
});
|
|
2861
|
+
|
|
2862
|
+
// Keyboard shortcuts
|
|
2863
|
+
this._keydownHandler = (e) => {
|
|
2864
|
+
if (this.element.classList.contains("hidden")) return
|
|
2865
|
+
|
|
2866
|
+
const activeEl = document.activeElement;
|
|
2867
|
+
if (activeEl && (activeEl.tagName === "INPUT" || activeEl.tagName === "TEXTAREA" || activeEl.isContentEditable)) return
|
|
2868
|
+
|
|
2869
|
+
if (e.key === "Escape") {
|
|
2870
|
+
if (this.colorDropdownOpen) {
|
|
2871
|
+
this._closeColorDropdown();
|
|
2872
|
+
} else {
|
|
2873
|
+
this.onClose?.();
|
|
2874
|
+
}
|
|
2875
|
+
e.preventDefault();
|
|
2876
|
+
} else if (e.key === "Delete" || e.key === "Backspace") {
|
|
2877
|
+
e.preventDefault();
|
|
2878
|
+
if (this.currentAnnotation && this.onDelete) {
|
|
2879
|
+
this.onDelete(this.currentAnnotation);
|
|
2880
|
+
}
|
|
2881
|
+
} else if (e.key === "e" || e.key === "E") {
|
|
2882
|
+
if (this.currentAnnotation?.annotation_type === "note" && this.onEdit) {
|
|
2883
|
+
e.preventDefault();
|
|
2884
|
+
this.onEdit(this.currentAnnotation);
|
|
2885
|
+
}
|
|
2886
|
+
} else if (e.key === "c" || e.key === "C") {
|
|
2887
|
+
const supportsComment = ["highlight", "line", "ink"].includes(this.currentAnnotation?.annotation_type);
|
|
2888
|
+
if (supportsComment && this.onComment) {
|
|
2889
|
+
e.preventDefault();
|
|
2890
|
+
this.onComment(this.currentAnnotation);
|
|
2891
|
+
}
|
|
2892
|
+
}
|
|
2893
|
+
};
|
|
2894
|
+
document.addEventListener("keydown", this._keydownHandler);
|
|
2895
|
+
}
|
|
2896
|
+
|
|
2897
|
+
_toggleColorDropdown() {
|
|
2898
|
+
if (this.colorDropdownOpen) {
|
|
2899
|
+
this._closeColorDropdown();
|
|
2900
|
+
} else {
|
|
2901
|
+
this._openColorDropdown();
|
|
2902
|
+
}
|
|
2903
|
+
}
|
|
2904
|
+
|
|
2905
|
+
_openColorDropdown() {
|
|
2906
|
+
const dropdown = this.element.querySelector(".color-dropdown");
|
|
2907
|
+
const btn = this.element.querySelector(".color-picker-btn");
|
|
2908
|
+
dropdown.classList.remove("hidden");
|
|
2909
|
+
btn.setAttribute("aria-expanded", "true");
|
|
2910
|
+
this.colorDropdownOpen = true;
|
|
2911
|
+
}
|
|
2912
|
+
|
|
2913
|
+
_closeColorDropdown() {
|
|
2914
|
+
const dropdown = this.element.querySelector(".color-dropdown");
|
|
2915
|
+
const btn = this.element.querySelector(".color-picker-btn");
|
|
2916
|
+
dropdown.classList.add("hidden");
|
|
2917
|
+
btn.setAttribute("aria-expanded", "false");
|
|
2918
|
+
this.colorDropdownOpen = false;
|
|
2919
|
+
}
|
|
2920
|
+
|
|
2921
|
+
_selectColor(color) {
|
|
2922
|
+
if (this.currentAnnotation && this.onColorChange) {
|
|
2923
|
+
this.onColorChange(this.currentAnnotation, color);
|
|
2924
|
+
}
|
|
2925
|
+
this._updateSelectedColor(color);
|
|
2926
|
+
this._closeColorDropdown();
|
|
2927
|
+
}
|
|
2928
|
+
|
|
2929
|
+
_updateSelectedColor(color) {
|
|
2930
|
+
const swatch = this.element.querySelector(".color-picker-btn .color-swatch");
|
|
2931
|
+
swatch.style.backgroundColor = color;
|
|
2932
|
+
|
|
2933
|
+
this.element.querySelectorAll(".color-option").forEach(option => {
|
|
2934
|
+
option.setAttribute("aria-selected", option.dataset.color === color ? "true" : "false");
|
|
2935
|
+
});
|
|
2936
|
+
}
|
|
2937
|
+
|
|
2938
|
+
_getTypeLabel(annotationType) {
|
|
2939
|
+
const labels = {
|
|
2940
|
+
highlight: "Highlight",
|
|
2941
|
+
line: "Underline",
|
|
2942
|
+
note: "Note",
|
|
2943
|
+
ink: "Drawing"
|
|
2944
|
+
};
|
|
2945
|
+
return labels[annotationType] || "Annotation"
|
|
2946
|
+
}
|
|
2947
|
+
|
|
2948
|
+
show(annotation, anchorElement, options = {}) {
|
|
2949
|
+
this.currentAnnotation = annotation;
|
|
2950
|
+
this.anchorElement = anchorElement;
|
|
2951
|
+
|
|
2952
|
+
// Update header info
|
|
2953
|
+
const page = annotation.page || 1;
|
|
2954
|
+
this.typeLabel.textContent = `${this._getTypeLabel(annotation.annotation_type)} \u2014 Page ${page}`;
|
|
2955
|
+
|
|
2956
|
+
// Update color swatch
|
|
2957
|
+
const color = annotation.color || ColorPicker.DEFAULT_HIGHLIGHT_COLOR;
|
|
2958
|
+
this._updateSelectedColor(color);
|
|
2959
|
+
|
|
2960
|
+
// Show/hide buttons based on annotation type
|
|
2961
|
+
const isNote = annotation.annotation_type === "note";
|
|
2962
|
+
const supportsComment = ["highlight", "line", "ink"].includes(annotation.annotation_type);
|
|
2963
|
+
this.commentBtn.classList.toggle("hidden", !supportsComment);
|
|
2964
|
+
this.editBtn.classList.toggle("hidden", !isNote);
|
|
2965
|
+
|
|
2966
|
+
if (supportsComment) {
|
|
2967
|
+
const hasComment = annotation.contents && annotation.contents.trim();
|
|
2968
|
+
this.commentBtn.title = hasComment ? "Edit Comment (C)" : "Add Comment (C)";
|
|
2969
|
+
}
|
|
2970
|
+
|
|
2971
|
+
// Show annotation text content if present
|
|
2972
|
+
if (annotation.contents) {
|
|
2973
|
+
this.textContent.textContent = annotation.contents;
|
|
2974
|
+
this.textContent.classList.remove("hidden");
|
|
2975
|
+
} else {
|
|
2976
|
+
this.textContent.textContent = "";
|
|
2977
|
+
this.textContent.classList.add("hidden");
|
|
2978
|
+
}
|
|
2979
|
+
|
|
2980
|
+
// Inject custom content if provided (HTML string or DOM element)
|
|
2981
|
+
if (options.content) {
|
|
2982
|
+
if (typeof options.content === "string") {
|
|
2983
|
+
this.contentSlot.innerHTML = options.content;
|
|
2984
|
+
} else {
|
|
2985
|
+
this.contentSlot.innerHTML = "";
|
|
2986
|
+
this.contentSlot.appendChild(options.content);
|
|
2987
|
+
}
|
|
2988
|
+
}
|
|
2989
|
+
|
|
2990
|
+
// Position and show the panel
|
|
2991
|
+
this._positionPanel(anchorElement);
|
|
2992
|
+
this.element.classList.remove("hidden");
|
|
2993
|
+
}
|
|
2994
|
+
|
|
2995
|
+
_positionPanel(anchorElement) {
|
|
2996
|
+
const pageContainer = anchorElement.closest(".pdf-page");
|
|
2997
|
+
if (!pageContainer) return
|
|
2998
|
+
|
|
2999
|
+
// Append to page container so it scrolls with the page
|
|
3000
|
+
pageContainer.appendChild(this.element);
|
|
3001
|
+
|
|
3002
|
+
// Get annotation position relative to the page
|
|
3003
|
+
const anchorRect = anchorElement.getBoundingClientRect();
|
|
3004
|
+
const pageRect = pageContainer.getBoundingClientRect();
|
|
3005
|
+
|
|
3006
|
+
const anchorTop = anchorRect.top - pageRect.top;
|
|
3007
|
+
const anchorLeft = anchorRect.left - pageRect.left;
|
|
3008
|
+
const anchorRight = anchorLeft + anchorRect.width;
|
|
3009
|
+
const pageWidth = pageRect.width;
|
|
3010
|
+
|
|
3011
|
+
const panelWidth = parseInt(
|
|
3012
|
+
getComputedStyle(this.element).getPropertyValue("--panel-width") || "320", 10
|
|
3013
|
+
);
|
|
3014
|
+
|
|
3015
|
+
// Try to position to the right of the annotation
|
|
3016
|
+
if (anchorRight + panelWidth + 12 <= pageWidth) {
|
|
3017
|
+
this.element.style.left = `${anchorRight + 8}px`;
|
|
3018
|
+
this.element.style.right = "auto";
|
|
3019
|
+
}
|
|
3020
|
+
// Fall back to the left side
|
|
3021
|
+
else if (anchorLeft - panelWidth - 12 >= 0) {
|
|
3022
|
+
this.element.style.left = `${anchorLeft - panelWidth - 8}px`;
|
|
3023
|
+
this.element.style.right = "auto";
|
|
3024
|
+
}
|
|
3025
|
+
// Fall back to below the annotation (centered)
|
|
3026
|
+
else {
|
|
3027
|
+
const centerX = anchorLeft + anchorRect.width / 2;
|
|
3028
|
+
this.element.style.left = `${Math.max(8, centerX - panelWidth / 2)}px`;
|
|
3029
|
+
this.element.style.right = "auto";
|
|
3030
|
+
}
|
|
3031
|
+
|
|
3032
|
+
// Vertical alignment: align top of panel with top of annotation
|
|
3033
|
+
this.element.style.top = `${anchorTop}px`;
|
|
3034
|
+
}
|
|
3035
|
+
|
|
3036
|
+
hide() {
|
|
3037
|
+
this._closeColorDropdown();
|
|
3038
|
+
this.element.classList.add("hidden");
|
|
3039
|
+
this.currentAnnotation = null;
|
|
3040
|
+
this.anchorElement = null;
|
|
3041
|
+
|
|
3042
|
+
// Clear text content
|
|
3043
|
+
this.textContent.textContent = "";
|
|
3044
|
+
this.textContent.classList.add("hidden");
|
|
3045
|
+
|
|
3046
|
+
// Clear injected content (but preserve the slot container)
|
|
3047
|
+
this.contentSlot.innerHTML = "";
|
|
3048
|
+
|
|
3049
|
+
// Remove from parent when hidden
|
|
3050
|
+
if (this.element.parentNode) {
|
|
3051
|
+
this.element.parentNode.removeChild(this.element);
|
|
3052
|
+
}
|
|
3053
|
+
}
|
|
3054
|
+
|
|
3055
|
+
isVisible() {
|
|
3056
|
+
return !this.element.classList.contains("hidden")
|
|
3057
|
+
}
|
|
3058
|
+
|
|
3059
|
+
getContentContainer() {
|
|
3060
|
+
return this.contentSlot
|
|
3061
|
+
}
|
|
3062
|
+
|
|
3063
|
+
destroy() {
|
|
3064
|
+
if (this._keydownHandler) {
|
|
3065
|
+
document.removeEventListener("keydown", this._keydownHandler);
|
|
3066
|
+
}
|
|
2725
3067
|
this.element.remove();
|
|
2726
3068
|
}
|
|
2727
3069
|
}
|
|
@@ -3665,6 +4007,17 @@ class AnnotationSidebar {
|
|
|
3665
4007
|
this.listContainer.appendChild(item);
|
|
3666
4008
|
}
|
|
3667
4009
|
}
|
|
4010
|
+
|
|
4011
|
+
// Re-fire selected event after rebuild so consumers can re-attach detail panels
|
|
4012
|
+
if (this.selectedAnnotationId) {
|
|
4013
|
+
const annotation = this.annotationManager.getAnnotation(this.selectedAnnotationId);
|
|
4014
|
+
if (annotation) {
|
|
4015
|
+
this.element.dispatchEvent(new CustomEvent("pdf-sidebar:annotation-selected", {
|
|
4016
|
+
bubbles: true,
|
|
4017
|
+
detail: { annotationId: this.selectedAnnotationId, annotation }
|
|
4018
|
+
}));
|
|
4019
|
+
}
|
|
4020
|
+
}
|
|
3668
4021
|
}
|
|
3669
4022
|
|
|
3670
4023
|
_matchesFilter(annotation) {
|
|
@@ -3887,18 +4240,36 @@ class AnnotationSidebar {
|
|
|
3887
4240
|
}
|
|
3888
4241
|
|
|
3889
4242
|
_selectItem(annotationId) {
|
|
4243
|
+
const previousId = this.selectedAnnotationId;
|
|
4244
|
+
|
|
4245
|
+
// Skip if already selected
|
|
4246
|
+
if (previousId === annotationId) return
|
|
4247
|
+
|
|
3890
4248
|
// Deselect previous
|
|
3891
4249
|
const prev = this.listContainer.querySelector(".annotation-list-item.selected");
|
|
3892
4250
|
if (prev) {
|
|
3893
4251
|
prev.classList.remove("selected");
|
|
3894
4252
|
}
|
|
3895
4253
|
|
|
4254
|
+
if (previousId) {
|
|
4255
|
+
this.element.dispatchEvent(new CustomEvent("pdf-sidebar:annotation-deselected", {
|
|
4256
|
+
bubbles: true,
|
|
4257
|
+
detail: { annotationId: previousId }
|
|
4258
|
+
}));
|
|
4259
|
+
}
|
|
4260
|
+
|
|
3896
4261
|
// Select new
|
|
3897
4262
|
this.selectedAnnotationId = annotationId;
|
|
3898
4263
|
const item = this.listContainer.querySelector(`[data-annotation-id="${annotationId}"]`);
|
|
3899
4264
|
if (item) {
|
|
3900
4265
|
item.classList.add("selected");
|
|
3901
4266
|
}
|
|
4267
|
+
|
|
4268
|
+
const annotation = this.annotationManager.getAnnotation(annotationId);
|
|
4269
|
+
this.element.dispatchEvent(new CustomEvent("pdf-sidebar:annotation-selected", {
|
|
4270
|
+
bubbles: true,
|
|
4271
|
+
detail: { annotationId, annotation }
|
|
4272
|
+
}));
|
|
3902
4273
|
}
|
|
3903
4274
|
|
|
3904
4275
|
/**
|
|
@@ -3926,7 +4297,12 @@ class AnnotationSidebar {
|
|
|
3926
4297
|
if (this.isOpen) {
|
|
3927
4298
|
// Clear selection if deleted annotation was selected
|
|
3928
4299
|
if (this.selectedAnnotationId === annotation.id) {
|
|
4300
|
+
const previousId = this.selectedAnnotationId;
|
|
3929
4301
|
this.selectedAnnotationId = null;
|
|
4302
|
+
this.element.dispatchEvent(new CustomEvent("pdf-sidebar:annotation-deselected", {
|
|
4303
|
+
bubbles: true,
|
|
4304
|
+
detail: { annotationId: previousId }
|
|
4305
|
+
}));
|
|
3930
4306
|
}
|
|
3931
4307
|
this._refreshList();
|
|
3932
4308
|
}
|
|
@@ -3954,6 +4330,10 @@ class AnnotationSidebar {
|
|
|
3954
4330
|
this.element.classList.add("open");
|
|
3955
4331
|
this.container.classList.add("annotation-sidebar-open");
|
|
3956
4332
|
this._refreshList();
|
|
4333
|
+
|
|
4334
|
+
this.element.dispatchEvent(new CustomEvent("pdf-sidebar:annotation-sidebar-opened", {
|
|
4335
|
+
bubbles: true
|
|
4336
|
+
}));
|
|
3957
4337
|
}
|
|
3958
4338
|
|
|
3959
4339
|
/**
|
|
@@ -3963,6 +4343,10 @@ class AnnotationSidebar {
|
|
|
3963
4343
|
this.isOpen = false;
|
|
3964
4344
|
this.element.classList.remove("open");
|
|
3965
4345
|
this.container.classList.remove("annotation-sidebar-open");
|
|
4346
|
+
|
|
4347
|
+
this.element.dispatchEvent(new CustomEvent("pdf-sidebar:annotation-sidebar-closed", {
|
|
4348
|
+
bubbles: true
|
|
4349
|
+
}));
|
|
3966
4350
|
}
|
|
3967
4351
|
|
|
3968
4352
|
/**
|
|
@@ -6730,6 +7114,18 @@ class PdfViewer {
|
|
|
6730
7114
|
onDeselect: this._deselectAnnotation.bind(this)
|
|
6731
7115
|
});
|
|
6732
7116
|
|
|
7117
|
+
// Detail panel (opt-in: replaces edit toolbar when enabled)
|
|
7118
|
+
if (this.options.detailPanel && this.bodyContainer) {
|
|
7119
|
+
this.annotationDetailPanel = new AnnotationDetailPanel({
|
|
7120
|
+
container: this.bodyContainer,
|
|
7121
|
+
onColorChange: this._onAnnotationColorChange.bind(this),
|
|
7122
|
+
onDelete: this._onAnnotationDelete.bind(this),
|
|
7123
|
+
onEdit: this._onAnnotationEdit.bind(this),
|
|
7124
|
+
onComment: this._onAnnotationComment.bind(this),
|
|
7125
|
+
onClose: this._deselectAnnotation.bind(this)
|
|
7126
|
+
});
|
|
7127
|
+
}
|
|
7128
|
+
|
|
6733
7129
|
this.undoBar = new UndoBar(this.undoBarContainer, {
|
|
6734
7130
|
onUndo: this._onAnnotationUndo.bind(this)
|
|
6735
7131
|
});
|
|
@@ -6869,8 +7265,12 @@ class PdfViewer {
|
|
|
6869
7265
|
|
|
6870
7266
|
// Deselect annotation when clicking outside
|
|
6871
7267
|
this.pagesContainer.addEventListener("click", (e) => {
|
|
6872
|
-
// Don't deselect if clicking on an annotation or
|
|
6873
|
-
if (e.target.closest(".annotation") || e.target.closest(".annotation-edit-toolbar")) {
|
|
7268
|
+
// Don't deselect if clicking on an annotation, edit toolbar, or detail panel
|
|
7269
|
+
if (e.target.closest(".annotation") || e.target.closest(".annotation-edit-toolbar") || e.target.closest(".annotation-detail-panel")) {
|
|
7270
|
+
return
|
|
7271
|
+
}
|
|
7272
|
+
// Skip if an annotation was just created (click follows pointerup from text selection)
|
|
7273
|
+
if (this._suppressClickDeselect) {
|
|
6874
7274
|
return
|
|
6875
7275
|
}
|
|
6876
7276
|
this._deselectAnnotation();
|
|
@@ -6922,6 +7322,12 @@ class PdfViewer {
|
|
|
6922
7322
|
// Render annotations on all rendered pages
|
|
6923
7323
|
this._renderAnnotations();
|
|
6924
7324
|
|
|
7325
|
+
const annotations = this.annotationManager.getAllAnnotations();
|
|
7326
|
+
this.container.dispatchEvent(new CustomEvent("pdf-viewer:annotations-loaded", {
|
|
7327
|
+
bubbles: true,
|
|
7328
|
+
detail: { annotations, count: annotations.length }
|
|
7329
|
+
}));
|
|
7330
|
+
|
|
6925
7331
|
// Navigate to initial page if specified
|
|
6926
7332
|
if (this.initialPage > 1) {
|
|
6927
7333
|
this.viewer.goToPage(this.initialPage);
|
|
@@ -7049,11 +7455,15 @@ class PdfViewer {
|
|
|
7049
7455
|
|
|
7050
7456
|
// Auto-select the newly created annotation
|
|
7051
7457
|
const pageContainer = this.viewer.getPageContainer(annotation.page);
|
|
7052
|
-
const element = pageContainer?.querySelector(
|
|
7458
|
+
const element = pageContainer?.querySelector(`.annotation[data-annotation-id="${annotation.id}"]`);
|
|
7053
7459
|
if (element) {
|
|
7054
7460
|
this._selectAnnotation(annotation, element);
|
|
7055
7461
|
}
|
|
7056
7462
|
|
|
7463
|
+
// Suppress the click-to-deselect that follows pointerup after text selection
|
|
7464
|
+
this._suppressClickDeselect = true;
|
|
7465
|
+
setTimeout(() => { this._suppressClickDeselect = false; }, 100);
|
|
7466
|
+
|
|
7057
7467
|
// Notify annotation sidebar
|
|
7058
7468
|
this.annotationSidebar?.onAnnotationCreated(annotation);
|
|
7059
7469
|
|
|
@@ -7071,9 +7481,9 @@ class PdfViewer {
|
|
|
7071
7481
|
// Remember if this annotation was selected
|
|
7072
7482
|
const wasSelected = this.selectedAnnotation && this.selectedAnnotation.id === annotation.id;
|
|
7073
7483
|
|
|
7074
|
-
// Hide
|
|
7484
|
+
// Hide annotation UI before re-render (it will be re-shown after)
|
|
7075
7485
|
if (wasSelected) {
|
|
7076
|
-
this.
|
|
7486
|
+
this._hideAnnotationUI();
|
|
7077
7487
|
}
|
|
7078
7488
|
|
|
7079
7489
|
this._renderAnnotationsForPage(annotation.page, this.viewer.getPageContainer(annotation.page));
|
|
@@ -7081,7 +7491,7 @@ class PdfViewer {
|
|
|
7081
7491
|
// Re-select the annotation after re-render
|
|
7082
7492
|
if (wasSelected) {
|
|
7083
7493
|
const pageContainer = this.viewer.getPageContainer(annotation.page);
|
|
7084
|
-
const element = pageContainer?.querySelector(
|
|
7494
|
+
const element = pageContainer?.querySelector(`.annotation[data-annotation-id="${annotation.id}"]`);
|
|
7085
7495
|
if (element) {
|
|
7086
7496
|
// Get the fresh annotation data from the manager
|
|
7087
7497
|
const updatedAnnotation = this.annotationManager.getAnnotation(annotation.id);
|
|
@@ -7091,7 +7501,7 @@ class PdfViewer {
|
|
|
7091
7501
|
this.selectedAnnotation = updatedAnnotation;
|
|
7092
7502
|
this.selectedAnnotationElement = element;
|
|
7093
7503
|
element.classList.add("selected");
|
|
7094
|
-
this.
|
|
7504
|
+
this._showAnnotationUI(updatedAnnotation, element);
|
|
7095
7505
|
}
|
|
7096
7506
|
}
|
|
7097
7507
|
}
|
|
@@ -7102,6 +7512,11 @@ class PdfViewer {
|
|
|
7102
7512
|
// Announce to screen readers
|
|
7103
7513
|
const typeLabel = this._getAnnotationTypeLabel(annotation.annotation_type);
|
|
7104
7514
|
getAnnouncer().announce(`${typeLabel} updated`);
|
|
7515
|
+
|
|
7516
|
+
this.container.dispatchEvent(new CustomEvent("pdf-viewer:annotation-updated", {
|
|
7517
|
+
bubbles: true,
|
|
7518
|
+
detail: { annotation }
|
|
7519
|
+
}));
|
|
7105
7520
|
}
|
|
7106
7521
|
|
|
7107
7522
|
_onAnnotationDeleted(annotation) {
|
|
@@ -7733,7 +8148,7 @@ class PdfViewer {
|
|
|
7733
8148
|
if (this.selectedAnnotationElement !== element) {
|
|
7734
8149
|
this.selectedAnnotationElement = element;
|
|
7735
8150
|
element.classList.add("selected");
|
|
7736
|
-
this.
|
|
8151
|
+
this._showAnnotationUI(annotation, element, pageHeight);
|
|
7737
8152
|
}
|
|
7738
8153
|
return
|
|
7739
8154
|
}
|
|
@@ -7746,8 +8161,8 @@ class PdfViewer {
|
|
|
7746
8161
|
this.selectedAnnotationElement = element;
|
|
7747
8162
|
element.classList.add("selected");
|
|
7748
8163
|
|
|
7749
|
-
// Show the
|
|
7750
|
-
this.
|
|
8164
|
+
// Show the appropriate annotation UI
|
|
8165
|
+
this._showAnnotationUI(annotation, element, pageHeight);
|
|
7751
8166
|
|
|
7752
8167
|
this.container.dispatchEvent(new CustomEvent("pdf-viewer:annotation-selected", {
|
|
7753
8168
|
bubbles: true,
|
|
@@ -7755,15 +8170,51 @@ class PdfViewer {
|
|
|
7755
8170
|
}));
|
|
7756
8171
|
}
|
|
7757
8172
|
|
|
8173
|
+
_showAnnotationUI(annotation, element, pageHeight) {
|
|
8174
|
+
if (this.annotationDetailPanel) {
|
|
8175
|
+
this.annotationDetailPanel.show(annotation, element);
|
|
8176
|
+
this.container.dispatchEvent(new CustomEvent("pdf-viewer:detail-panel-opened", {
|
|
8177
|
+
bubbles: true,
|
|
8178
|
+
detail: { annotation, element }
|
|
8179
|
+
}));
|
|
8180
|
+
} else {
|
|
8181
|
+
this.annotationEditToolbar.show(annotation, element, pageHeight);
|
|
8182
|
+
}
|
|
8183
|
+
}
|
|
8184
|
+
|
|
8185
|
+
_hideAnnotationUI() {
|
|
8186
|
+
if (this.annotationDetailPanel) {
|
|
8187
|
+
const annotation = this.annotationDetailPanel.currentAnnotation;
|
|
8188
|
+
this.annotationDetailPanel.hide();
|
|
8189
|
+
if (annotation) {
|
|
8190
|
+
this.container.dispatchEvent(new CustomEvent("pdf-viewer:detail-panel-closed", {
|
|
8191
|
+
bubbles: true,
|
|
8192
|
+
detail: { annotation, annotationId: annotation.id }
|
|
8193
|
+
}));
|
|
8194
|
+
}
|
|
8195
|
+
} else {
|
|
8196
|
+
this.annotationEditToolbar.hide();
|
|
8197
|
+
}
|
|
8198
|
+
}
|
|
8199
|
+
|
|
7758
8200
|
_deselectAnnotation() {
|
|
8201
|
+
const previousAnnotation = this.selectedAnnotation;
|
|
8202
|
+
|
|
7759
8203
|
if (this.selectedAnnotationElement) {
|
|
7760
8204
|
this.selectedAnnotationElement.classList.remove("selected");
|
|
7761
8205
|
}
|
|
7762
8206
|
this.selectedAnnotation = null;
|
|
7763
8207
|
this.selectedAnnotationElement = null;
|
|
7764
8208
|
|
|
7765
|
-
// Hide the edit toolbar
|
|
7766
|
-
this.
|
|
8209
|
+
// Hide the annotation UI (detail panel or edit toolbar)
|
|
8210
|
+
this._hideAnnotationUI();
|
|
8211
|
+
|
|
8212
|
+
if (previousAnnotation) {
|
|
8213
|
+
this.container.dispatchEvent(new CustomEvent("pdf-viewer:annotation-deselected", {
|
|
8214
|
+
bubbles: true,
|
|
8215
|
+
detail: { annotationId: previousAnnotation.id }
|
|
8216
|
+
}));
|
|
8217
|
+
}
|
|
7767
8218
|
}
|
|
7768
8219
|
|
|
7769
8220
|
async _onAnnotationColorChange(annotation, color) {
|
|
@@ -7931,6 +8382,7 @@ class PdfViewer {
|
|
|
7931
8382
|
|
|
7932
8383
|
this.viewer.destroy();
|
|
7933
8384
|
this.annotationEditToolbar.destroy();
|
|
8385
|
+
this.annotationDetailPanel?.destroy();
|
|
7934
8386
|
this.undoBar.destroy();
|
|
7935
8387
|
this.thumbnailSidebar?.destroy();
|
|
7936
8388
|
this.annotationSidebar?.destroy();
|
|
@@ -7957,7 +8409,8 @@ class pdf_viewer_controller extends Controller {
|
|
|
7957
8409
|
trackingUrl: String,
|
|
7958
8410
|
initialPage: Number,
|
|
7959
8411
|
initialAnnotation: String,
|
|
7960
|
-
autoHeight: { type: Boolean, default: true }
|
|
8412
|
+
autoHeight: { type: Boolean, default: true },
|
|
8413
|
+
detailPanel: { type: Boolean, default: false }
|
|
7961
8414
|
}
|
|
7962
8415
|
|
|
7963
8416
|
initialize() {
|
|
@@ -7978,7 +8431,8 @@ class pdf_viewer_controller extends Controller {
|
|
|
7978
8431
|
userName: this.userNameValue,
|
|
7979
8432
|
documentId: this.documentIdValue,
|
|
7980
8433
|
initialPage: this.initialPageValue || 1,
|
|
7981
|
-
initialAnnotation: this.initialAnnotationValue
|
|
8434
|
+
initialAnnotation: this.initialAnnotationValue,
|
|
8435
|
+
detailPanel: this.detailPanelValue
|
|
7982
8436
|
});
|
|
7983
8437
|
|
|
7984
8438
|
// Set up the toolbar
|