kviewer 0.0.8 → 0.0.10
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/module.d.mts +1 -1
- package/dist/module.json +1 -1
- package/dist/runtime/annotation/engine/editor/editor.d.ts +11 -5
- package/dist/runtime/annotation/engine/editor/editor.js +27 -11
- package/dist/runtime/annotation/engine/editor/selector.d.ts +20 -1
- package/dist/runtime/annotation/engine/editor/selector.js +132 -33
- package/dist/runtime/annotation/engine/painter.d.ts +4 -1
- package/dist/runtime/annotation/engine/painter.js +21 -5
- package/dist/runtime/annotation/engine/tools/freehand.js +2 -2
- package/dist/runtime/components/PdfPage.d.vue.ts +6 -1
- package/dist/runtime/components/PdfPage.vue +51 -18
- package/dist/runtime/components/PdfPage.vue.d.ts +6 -1
- package/dist/runtime/components/ToolButton.d.vue.ts +4 -2
- package/dist/runtime/components/ToolButton.vue.d.ts +4 -2
- package/dist/runtime/components/Viewer.d.vue.ts +3 -0
- package/dist/runtime/components/Viewer.vue +225 -165
- package/dist/runtime/components/Viewer.vue.d.ts +3 -0
- package/dist/runtime/components/form-fields/FormSignatureField.vue +23 -1
- package/dist/runtime/components/modals/FreeTextModal.vue +27 -3
- package/dist/runtime/components/tools/ActionTools.vue +4 -5
- package/dist/runtime/components/tools/DrawingTools.vue +1 -1
- package/dist/runtime/composables/useAnnotationEngine.d.ts +2 -0
- package/dist/runtime/composables/useAnnotationEngine.js +5 -1
- package/dist/runtime/composables/useAnnotationHistory.d.ts +1 -0
- package/dist/runtime/composables/useAnnotationHistory.js +10 -3
- package/dist/runtime/composables/useInertiaPanzoom.d.ts +40 -0
- package/dist/runtime/composables/useInertiaPanzoom.js +472 -0
- package/dist/runtime/composables/usePageVirtualization.d.ts +3 -0
- package/dist/runtime/composables/usePageVirtualization.js +23 -0
- package/dist/runtime/composables/useViewerSearch.d.ts +1 -0
- package/dist/runtime/composables/useViewerSearch.js +15 -5
- package/dist/runtime/public-types.d.ts +2 -0
- package/dist/types.d.mts +1 -1
- package/package.json +1 -1
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
<template #body>
|
|
8
8
|
<div class="flex flex-col gap-3">
|
|
9
9
|
<UTextarea
|
|
10
|
+
ref="textareaRef"
|
|
10
11
|
v-model="inputValue"
|
|
11
12
|
placeholder="Start typing..."
|
|
12
13
|
:rows="3"
|
|
13
|
-
autofocus
|
|
14
14
|
/>
|
|
15
15
|
<div class="flex items-center justify-between">
|
|
16
16
|
<div class="flex gap-1">
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
c === currentColor ? 'border-white ring-2 ring-blue-500' : 'border-transparent'
|
|
23
23
|
"
|
|
24
24
|
:style="{ backgroundColor: c }"
|
|
25
|
+
@pointerdown="onPreserveFocus"
|
|
25
26
|
@click="currentColor = c"
|
|
26
27
|
/>
|
|
27
28
|
</div>
|
|
@@ -39,14 +40,19 @@
|
|
|
39
40
|
<UButton variant="ghost" color="neutral" @click="cancel">
|
|
40
41
|
Cancel
|
|
41
42
|
</UButton>
|
|
42
|
-
<UButton
|
|
43
|
+
<UButton
|
|
44
|
+
:disabled="!inputValue.trim()"
|
|
45
|
+
@click="submit"
|
|
46
|
+
>
|
|
47
|
+
OK
|
|
48
|
+
</UButton>
|
|
43
49
|
</div>
|
|
44
50
|
</template>
|
|
45
51
|
</UModal>
|
|
46
52
|
</template>
|
|
47
53
|
|
|
48
54
|
<script setup>
|
|
49
|
-
import { ref, watch } from "vue";
|
|
55
|
+
import { ref, watch, nextTick } from "vue";
|
|
50
56
|
import { defaultOptions } from "../../annotation/engine/config";
|
|
51
57
|
const props = defineProps({
|
|
52
58
|
open: { type: Boolean, required: true },
|
|
@@ -55,6 +61,20 @@ const props = defineProps({
|
|
|
55
61
|
initialText: { type: String, required: false }
|
|
56
62
|
});
|
|
57
63
|
const emit = defineEmits(["update:open", "submit", "cancel"]);
|
|
64
|
+
const textareaRef = ref(null);
|
|
65
|
+
function focusTextarea() {
|
|
66
|
+
const instance = textareaRef.value;
|
|
67
|
+
if (!instance) return;
|
|
68
|
+
const el = instance.inputRef ?? instance.$el?.querySelector?.("textarea") ?? instance;
|
|
69
|
+
if (el && typeof el.focus === "function") {
|
|
70
|
+
el.focus();
|
|
71
|
+
const len = el.value?.length ?? 0;
|
|
72
|
+
try {
|
|
73
|
+
el.setSelectionRange(len, len);
|
|
74
|
+
} catch {
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
58
78
|
const isOpen = ref(props.open);
|
|
59
79
|
watch(
|
|
60
80
|
() => props.open,
|
|
@@ -64,6 +84,7 @@ watch(
|
|
|
64
84
|
inputValue.value = props.initialText ?? "";
|
|
65
85
|
currentColor.value = props.defaultColor;
|
|
66
86
|
currentFontSize.value = String(props.defaultFontSize);
|
|
87
|
+
nextTick(() => requestAnimationFrame(() => focusTextarea()));
|
|
67
88
|
}
|
|
68
89
|
}
|
|
69
90
|
);
|
|
@@ -86,4 +107,7 @@ function submit() {
|
|
|
86
107
|
function cancel() {
|
|
87
108
|
emit("cancel");
|
|
88
109
|
}
|
|
110
|
+
function onPreserveFocus(e) {
|
|
111
|
+
if (e.pointerType === "mouse") e.preventDefault();
|
|
112
|
+
}
|
|
89
113
|
</script>
|
|
@@ -18,11 +18,9 @@
|
|
|
18
18
|
:disabled="!state.history.canRedo.value"
|
|
19
19
|
@click="state.history.redo"
|
|
20
20
|
/>
|
|
21
|
-
<
|
|
22
|
-
|
|
23
|
-
:
|
|
24
|
-
color="neutral"
|
|
25
|
-
size="sm"
|
|
21
|
+
<ToolButton
|
|
22
|
+
:tool="{ name: 'Eraser', icon: 'i-lucide-eraser' }"
|
|
23
|
+
:active="state.activeTool.value === 'eraser'"
|
|
26
24
|
data-testid="action-eraser"
|
|
27
25
|
@click="state.selectTool('eraser')"
|
|
28
26
|
/>
|
|
@@ -31,5 +29,6 @@
|
|
|
31
29
|
|
|
32
30
|
<script setup>
|
|
33
31
|
import { useViewerState } from "../../composables/useViewerState";
|
|
32
|
+
import ToolButton from "../ToolButton.vue";
|
|
34
33
|
const state = useViewerState();
|
|
35
34
|
</script>
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
@click="state.selectTool(AnnotationType.FREETEXT)"
|
|
19
19
|
/>
|
|
20
20
|
<StampPicker />
|
|
21
|
-
<SignaturePicker />
|
|
21
|
+
<SignaturePicker v-if="state.signatureHandlers" />
|
|
22
22
|
<ToolButton
|
|
23
23
|
:tool="getDef(AnnotationType.RECTANGLE)"
|
|
24
24
|
:active="state.activeTool.value === AnnotationType.RECTANGLE"
|
|
@@ -4,4 +4,6 @@ export declare function createAnnotationEngine(viewerState: ViewerState, options
|
|
|
4
4
|
userName?: string;
|
|
5
5
|
onRequestTextInput: PainterCallbacks['onRequestTextInput'];
|
|
6
6
|
onRequestDeleteConfirm?: PainterCallbacks['onRequestDeleteConfirm'];
|
|
7
|
+
freehandGroupingDelay?: number;
|
|
8
|
+
setExternalGesturesEnabled?: (enabled: boolean) => void;
|
|
7
9
|
}): Painter;
|
|
@@ -21,6 +21,8 @@ export function createAnnotationEngine(viewerState, options) {
|
|
|
21
21
|
if (annotationStore.type === AnnotationType.FREETEXT) {
|
|
22
22
|
viewerState.painter.value?.selectAnnotation(annotationStore.id);
|
|
23
23
|
}
|
|
24
|
+
} else {
|
|
25
|
+
viewerState.history.rebaseline();
|
|
24
26
|
}
|
|
25
27
|
},
|
|
26
28
|
onStoreDelete: (id) => {
|
|
@@ -64,7 +66,9 @@ export function createAnnotationEngine(viewerState, options) {
|
|
|
64
66
|
userName: options.userName ?? "User",
|
|
65
67
|
callbacks,
|
|
66
68
|
getStylusModeEnabled: () => viewerState.stylusMode.value,
|
|
67
|
-
getReadonly: () => viewerState.readonly.value
|
|
69
|
+
getReadonly: () => viewerState.readonly.value,
|
|
70
|
+
freehandGroupingDelay: options.freehandGroupingDelay,
|
|
71
|
+
setExternalGesturesEnabled: options.setExternalGesturesEnabled
|
|
68
72
|
});
|
|
69
73
|
viewerState.painter.value = painter;
|
|
70
74
|
return painter;
|
|
@@ -6,15 +6,19 @@ function cloneAnnotations(map) {
|
|
|
6
6
|
export function createAnnotationHistory(annotations, painter) {
|
|
7
7
|
const undoStack = ref([]);
|
|
8
8
|
const redoStack = ref([]);
|
|
9
|
+
let baseline = cloneAnnotations(annotations.value);
|
|
9
10
|
const canUndo = computed(() => undoStack.value.length > 0);
|
|
10
11
|
const canRedo = computed(() => redoStack.value.length > 0);
|
|
11
12
|
function commit() {
|
|
12
|
-
|
|
13
|
-
undoStack.value.push(snapshot);
|
|
13
|
+
undoStack.value.push(baseline);
|
|
14
14
|
if (undoStack.value.length > MAX_HISTORY) {
|
|
15
15
|
undoStack.value.shift();
|
|
16
16
|
}
|
|
17
17
|
redoStack.value = [];
|
|
18
|
+
baseline = cloneAnnotations(annotations.value);
|
|
19
|
+
}
|
|
20
|
+
function rebaseline() {
|
|
21
|
+
baseline = cloneAnnotations(annotations.value);
|
|
18
22
|
}
|
|
19
23
|
function restoreSnapshot(target) {
|
|
20
24
|
const p = painter.value;
|
|
@@ -53,6 +57,7 @@ export function createAnnotationHistory(annotations, painter) {
|
|
|
53
57
|
redoStack.value.push(currentSnapshot);
|
|
54
58
|
const target = undoStack.value.pop();
|
|
55
59
|
restoreSnapshot(target);
|
|
60
|
+
baseline = cloneAnnotations(annotations.value);
|
|
56
61
|
}
|
|
57
62
|
function redo() {
|
|
58
63
|
if (!canRedo.value) return;
|
|
@@ -60,10 +65,12 @@ export function createAnnotationHistory(annotations, painter) {
|
|
|
60
65
|
undoStack.value.push(currentSnapshot);
|
|
61
66
|
const target = redoStack.value.pop();
|
|
62
67
|
restoreSnapshot(target);
|
|
68
|
+
baseline = cloneAnnotations(annotations.value);
|
|
63
69
|
}
|
|
64
70
|
function reset() {
|
|
65
71
|
undoStack.value = [];
|
|
66
72
|
redoStack.value = [];
|
|
73
|
+
baseline = cloneAnnotations(annotations.value);
|
|
67
74
|
}
|
|
68
|
-
return { commit, undo, redo, reset, canUndo, canRedo };
|
|
75
|
+
return { commit, undo, redo, reset, rebaseline, canUndo, canRedo };
|
|
69
76
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export interface InertiaPanzoomOptions {
|
|
2
|
+
minScale?: number;
|
|
3
|
+
maxScale?: number;
|
|
4
|
+
/** Minimum pointer movement (px) before axis direction is committed. */
|
|
5
|
+
axisLockThreshold?: number;
|
|
6
|
+
/** Vertical must exceed horizontal by this ratio to lock to Y-axis. */
|
|
7
|
+
axisLockRatio?: number;
|
|
8
|
+
}
|
|
9
|
+
export interface ScrollToElementOptions {
|
|
10
|
+
/** Vertical alignment within the viewport. Default: 'start'. */
|
|
11
|
+
block?: 'start' | 'center';
|
|
12
|
+
/** Animate to the target with inertia, or snap instantly. Default: true. */
|
|
13
|
+
animate?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface Transform {
|
|
16
|
+
scale: number;
|
|
17
|
+
tx: number;
|
|
18
|
+
ty: number;
|
|
19
|
+
}
|
|
20
|
+
export interface InertiaPanzoomHandle {
|
|
21
|
+
/** Bind gesture handlers to the given element (its parent becomes the viewport). */
|
|
22
|
+
attach: (target: HTMLElement) => void;
|
|
23
|
+
/** Remove handlers and clear inline styles. */
|
|
24
|
+
detach: () => void;
|
|
25
|
+
/** Reset transform and cancel any in-flight momentum. */
|
|
26
|
+
reset: () => void;
|
|
27
|
+
/** Cancel in-flight momentum without changing transform. */
|
|
28
|
+
stop: () => void;
|
|
29
|
+
getScale: () => number;
|
|
30
|
+
getTransform: () => Transform;
|
|
31
|
+
/** Animate the view so `elem` is at the top (or center) of the viewport. */
|
|
32
|
+
scrollToElement: (elem: HTMLElement, opts?: ScrollToElementOptions) => void;
|
|
33
|
+
/** Pan by a delta in viewport pixels (clamped to bounds). */
|
|
34
|
+
scrollBy: (dx: number, dy: number) => void;
|
|
35
|
+
/** Enable/disable pan+pinch gestures (wheel/scrollbar/keyboard still work). */
|
|
36
|
+
setGesturesEnabled: (enabled: boolean) => void;
|
|
37
|
+
/** Subscribe to transform updates. Returns an unsubscribe function. */
|
|
38
|
+
onChange: (cb: (t: Transform) => void) => () => void;
|
|
39
|
+
}
|
|
40
|
+
export declare function useInertiaPanzoom(options?: InertiaPanzoomOptions): InertiaPanzoomHandle;
|