kviewer 0.2.1 → 0.2.3
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/README.md +19 -0
- package/dist/module.d.mts +8 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +25 -3
- package/dist/runtime/annotation/engine/canvas-resolution.d.ts +7 -0
- package/dist/runtime/annotation/engine/canvas-resolution.js +21 -0
- package/dist/runtime/annotation/engine/editor/editor.d.ts +7 -2
- package/dist/runtime/annotation/engine/editor/editor.js +14 -0
- package/dist/runtime/annotation/engine/editor/selector.d.ts +13 -1
- package/dist/runtime/annotation/engine/editor/selector.js +60 -8
- package/dist/runtime/annotation/engine/import-normalize.js +3 -3
- package/dist/runtime/annotation/engine/painter.d.ts +33 -2
- package/dist/runtime/annotation/engine/painter.js +88 -2
- package/dist/runtime/annotation/engine/store.js +3 -0
- package/dist/runtime/annotation/engine/tools/arrow.js +0 -1
- package/dist/runtime/annotation/engine/tools/circle.js +0 -2
- package/dist/runtime/annotation/engine/tools/free-highlight.js +0 -2
- package/dist/runtime/annotation/engine/tools/freehand.js +0 -2
- package/dist/runtime/annotation/engine/tools/rectangle.js +0 -2
- package/dist/runtime/annotation/engine/types.d.ts +9 -2
- package/dist/runtime/annotation/font-style.d.ts +15 -0
- package/dist/runtime/annotation/font-style.js +19 -0
- package/dist/runtime/annotation/parsers/extractCheckboxStyles.d.ts +8 -0
- package/dist/runtime/annotation/parsers/extractCheckboxStyles.js +24 -1
- package/dist/runtime/annotation/pdf-export/annotation-flags.d.ts +8 -0
- package/dist/runtime/annotation/pdf-export/annotation-flags.js +5 -0
- package/dist/runtime/annotation/pdf-export/export-form-fields.js +3 -1
- package/dist/runtime/annotation/pdf-export/export.js +17 -4
- package/dist/runtime/annotation/pdf-export/parse.d.ts +22 -1
- package/dist/runtime/annotation/pdf-export/parse.js +32 -2
- package/dist/runtime/annotation/pdf-export/parse_circle.js +5 -5
- package/dist/runtime/annotation/pdf-export/parse_freetext.js +33 -24
- package/dist/runtime/annotation/pdf-export/parse_highlight.js +5 -5
- package/dist/runtime/annotation/pdf-export/parse_ink.js +7 -6
- package/dist/runtime/annotation/pdf-export/parse_line.js +7 -6
- package/dist/runtime/annotation/pdf-export/parse_polyline.js +7 -6
- package/dist/runtime/annotation/pdf-export/parse_square.js +5 -5
- package/dist/runtime/annotation/pdf-export/parse_stamp.js +6 -7
- package/dist/runtime/annotation/pdf-export/parse_strikeout.js +5 -5
- package/dist/runtime/annotation/pdf-export/parse_text.js +5 -5
- package/dist/runtime/annotation/pdf-export/parse_underline.js +5 -5
- package/dist/runtime/annotation/pdf-export/unicode-font.d.ts +5 -0
- package/dist/runtime/annotation/pdf-export/unicode-font.js +31 -0
- package/dist/runtime/annotation/pdf-export/winansi.d.ts +17 -0
- package/dist/runtime/annotation/pdf-export/winansi.js +77 -0
- package/dist/runtime/annotation/pdf-import/decode_circle.js +0 -1
- package/dist/runtime/annotation/pdf-import/decode_ink.js +0 -1
- package/dist/runtime/annotation/pdf-import/decode_line.js +0 -1
- package/dist/runtime/annotation/pdf-import/decode_square.js +0 -1
- package/dist/runtime/annotation/pdf-import/extract_stamp_appearance.d.ts +12 -0
- package/dist/runtime/annotation/pdf-import/extract_stamp_appearance.js +17 -4
- package/dist/runtime/annotation/pdf-import/types.d.ts +2 -0
- package/dist/runtime/assets/fonts/LICENSE-LiberationSans +102 -0
- package/dist/runtime/assets/fonts/LiberationSans-Regular.ttf +0 -0
- package/dist/runtime/assets/kviewer.css +1 -1
- package/dist/runtime/components/AnnotationToolbar.vue +6 -4
- package/dist/runtime/components/PdfPage.d.vue.ts +16 -0
- package/dist/runtime/components/PdfPage.vue +170 -9
- package/dist/runtime/components/PdfPage.vue.d.ts +16 -0
- package/dist/runtime/components/PerfHud.d.vue.ts +3 -0
- package/dist/runtime/components/PerfHud.vue +97 -0
- package/dist/runtime/components/PerfHud.vue.d.ts +3 -0
- package/dist/runtime/components/ToolButton.vue +4 -1
- package/dist/runtime/components/Viewer.d.vue.ts +25 -2
- package/dist/runtime/components/Viewer.vue +174 -12
- package/dist/runtime/components/Viewer.vue.d.ts +25 -2
- package/dist/runtime/components/ViewerBar.d.vue.ts +21 -1
- package/dist/runtime/components/ViewerBar.vue +183 -101
- package/dist/runtime/components/ViewerBar.vue.d.ts +21 -1
- package/dist/runtime/components/ViewerTabs.d.vue.ts +10 -2
- package/dist/runtime/components/ViewerTabs.vue +13 -2
- package/dist/runtime/components/ViewerTabs.vue.d.ts +10 -2
- package/dist/runtime/components/form-fields/FormDropdown.vue +50 -19
- package/dist/runtime/components/form-fields/FormSignatureField.vue +4 -2
- package/dist/runtime/components/form-fields/FormTextField.vue +7 -18
- package/dist/runtime/components/modals/FreeTextModal.vue +6 -4
- package/dist/runtime/components/modals/SignatureDrawModal.vue +6 -4
- package/dist/runtime/components/panels/PlacedFieldSidebar.vue +67 -50
- package/dist/runtime/components/panels/SignaturePicker.vue +6 -4
- package/dist/runtime/components/panels/StampPicker.vue +3 -1
- package/dist/runtime/components/tools/AnnotationToolButton.d.vue.ts +10 -0
- package/dist/runtime/components/tools/AnnotationToolButton.vue +33 -0
- package/dist/runtime/components/tools/AnnotationToolButton.vue.d.ts +10 -0
- package/dist/runtime/components/tools/MarqueeTool.vue +3 -1
- package/dist/runtime/components/tools/PageSettings.vue +13 -11
- package/dist/runtime/components/tools/SearchTool.vue +12 -10
- package/dist/runtime/components/tools/ToolProperties.vue +7 -5
- package/dist/runtime/components/tools/ToolbarMenu.d.vue.ts +9 -0
- package/dist/runtime/components/tools/ToolbarMenu.vue +79 -0
- package/dist/runtime/components/tools/ToolbarMenu.vue.d.ts +9 -0
- package/dist/runtime/components/tools/ZoomControls.vue +17 -9
- package/dist/runtime/composables/canvas-limits.d.ts +22 -0
- package/dist/runtime/composables/canvas-limits.js +10 -0
- package/dist/runtime/composables/useAnnotationEngine.js +3 -1
- package/dist/runtime/composables/useFormFields.d.ts +6 -0
- package/dist/runtime/composables/useFormFields.js +28 -0
- package/dist/runtime/composables/useInertiaPanzoom.d.ts +10 -0
- package/dist/runtime/composables/useInertiaPanzoom.js +57 -10
- package/dist/runtime/composables/useKviewerI18n.d.ts +18 -0
- package/dist/runtime/composables/useKviewerI18n.js +29 -0
- package/dist/runtime/composables/useScriptingManager.d.ts +8 -4
- package/dist/runtime/composables/useScriptingManager.js +2 -0
- package/dist/runtime/composables/useViewerState.d.ts +4 -0
- package/dist/runtime/composables/useViewerState.js +8 -0
- package/dist/runtime/i18n/messages.d.ts +252 -0
- package/dist/runtime/i18n/messages.js +249 -0
- package/dist/runtime/public-types.d.ts +2 -0
- package/dist/runtime/toolbar-tools.d.ts +54 -0
- package/dist/runtime/toolbar-tools.js +79 -0
- package/dist/types.d.mts +1 -1
- package/package.json +14 -31
package/README.md
CHANGED
|
@@ -71,6 +71,25 @@ const pdfUrl = '/documents/sample.pdf'
|
|
|
71
71
|
|
|
72
72
|
`source` accepts a URL string, `Uint8Array`, or a pdfjs-dist source object.
|
|
73
73
|
|
|
74
|
+
## Customizing the toolbar
|
|
75
|
+
|
|
76
|
+
By default the toolbar is fully built-in. Pass `tools` to choose which buttons render and in what order, and interleave custom buttons via `#tool-<name>` slots. Each entry is a built-in tool ID (a string) or a `{ type: 'slot' }` object. When omitted, the default toolbar is rendered unchanged.
|
|
77
|
+
|
|
78
|
+
```vue
|
|
79
|
+
<template>
|
|
80
|
+
<KViewer
|
|
81
|
+
:source="pdfUrl"
|
|
82
|
+
:tools="['menu', 'pageSettings', 'separator', 'zoom', { type: 'slot', key: 'save' }, 'spacer', 'search']"
|
|
83
|
+
>
|
|
84
|
+
<template #tool-save="{ state }">
|
|
85
|
+
<UButton icon="i-lucide-save" size="xs" variant="ghost" color="neutral" @click="save(state)" />
|
|
86
|
+
</template>
|
|
87
|
+
</KViewer>
|
|
88
|
+
</template>
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
`separator`/`spacer` are layout primitives (`spacer` right-aligns what follows). `tools` controls buttons only — it never gates programmatic `state.selectTool(...)` access. Form-edit mode is API-only (`v-model:form-edit-mode` / `setFormEditMode()` / `toggleFormEditMode()`); there is no built-in toggle button. See the [toolbar configuration docs](https://github.com/uppt/nuxt-kviewer) for the full tool-ID list and the array that reproduces the default toolbar.
|
|
92
|
+
|
|
74
93
|
## Viewer Ref API
|
|
75
94
|
|
|
76
95
|
`KViewer` exposes methods via a template ref:
|
package/dist/module.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
|
-
export { AddFormFieldPayload, CheckboxStyle, ExportPdfOptions, FormFieldDefinition, FormFieldOrigin, FormFieldType, FormFieldValue, KVIEWER_EMBED_PROTOCOL_VERSION, KViewerApi, KViewerEmbedBridgeOptions, KViewerEmbedClient, KViewerEmbedClientOptions, KViewerEmbedEventName, KViewerEmbedEventPayloads, KViewerEmbedMethodName, SignatureData, SignatureHandlers, ViewMode, ViewerMenuButtonItem, ViewerMenuCheckboxItem, ViewerMenuItem, ViewerMenuSeparatorItem, useKViewerEmbedBridge } from '../dist/runtime/public-types.js';
|
|
2
|
+
export { ActiveTool, AddFormFieldPayload, CheckboxStyle, ExportPdfOptions, FormFieldDefinition, FormFieldOrigin, FormFieldType, FormFieldValue, KVIEWER_EMBED_PROTOCOL_VERSION, KViewerApi, KViewerEmbedBridgeOptions, KViewerEmbedClient, KViewerEmbedClientOptions, KViewerEmbedEventName, KViewerEmbedEventPayloads, KViewerEmbedMethodName, SignatureData, SignatureHandlers, ViewMode, ViewerBottomToolId, ViewerLayoutToolId, ViewerMenuButtonItem, ViewerMenuCheckboxItem, ViewerMenuItem, ViewerMenuSeparatorItem, ViewerState, ViewerToolEntry, ViewerToolId, ViewerToolSlot, ViewerTopToolId, useKViewerEmbedBridge } from '../dist/runtime/public-types.js';
|
|
3
3
|
|
|
4
4
|
interface ModuleOptions {
|
|
5
5
|
/**
|
|
@@ -15,6 +15,13 @@ interface ModuleOptions {
|
|
|
15
15
|
* @defaultValue `2`
|
|
16
16
|
*/
|
|
17
17
|
minRenderPixelRatio?: number;
|
|
18
|
+
/**
|
|
19
|
+
* Default UI language for built-in viewer strings (toolbar, search,
|
|
20
|
+
* modals, form sidebar). The Viewer `locale` prop overrides this
|
|
21
|
+
* per instance.
|
|
22
|
+
* @defaultValue `en`
|
|
23
|
+
*/
|
|
24
|
+
locale?: 'en' | 'de';
|
|
18
25
|
}
|
|
19
26
|
|
|
20
27
|
declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -11,7 +11,8 @@ const module$1 = defineNuxtModule({
|
|
|
11
11
|
// Default configuration options of the Nuxt module
|
|
12
12
|
defaults: {
|
|
13
13
|
prefix: "K",
|
|
14
|
-
minRenderPixelRatio: 2
|
|
14
|
+
minRenderPixelRatio: 2,
|
|
15
|
+
locale: "en"
|
|
15
16
|
},
|
|
16
17
|
// Declare @nuxt/ui as a hard dependency so Nuxt resolves it as a
|
|
17
18
|
// top-level module before our setup() runs. We can NOT just call
|
|
@@ -38,10 +39,11 @@ const module$1 = defineNuxtModule({
|
|
|
38
39
|
"pdfjs-dist/legacy/build/pdf.mjs",
|
|
39
40
|
"pdfjs-dist/web/pdf_viewer.mjs"
|
|
40
41
|
);
|
|
41
|
-
}
|
|
42
|
+
});
|
|
42
43
|
nuxt.options.runtimeConfig.public.kviewer = {
|
|
43
44
|
...nuxt.options.runtimeConfig.public.kviewer,
|
|
44
|
-
minRenderPixelRatio: options.minRenderPixelRatio ?? 2
|
|
45
|
+
minRenderPixelRatio: options.minRenderPixelRatio ?? 2,
|
|
46
|
+
locale: options.locale ?? "en"
|
|
45
47
|
};
|
|
46
48
|
nuxt.options.css.push("pdfjs-dist/web/pdf_viewer.css");
|
|
47
49
|
nuxt.options.css.push(resolve("./runtime/assets/kviewer.css"));
|
|
@@ -80,6 +82,26 @@ const module$1 = defineNuxtModule({
|
|
|
80
82
|
dir: join(pdfjsDir, "legacy/build"),
|
|
81
83
|
baseURL: "/_kviewer/pdfjs",
|
|
82
84
|
maxAge: 60 * 60 * 24 * 365
|
|
85
|
+
},
|
|
86
|
+
// pdf.js v6 moved several features to WebAssembly, fetched at
|
|
87
|
+
// runtime from the `wasmUrl` API option (see Viewer.vue) and the
|
|
88
|
+
// scripting `wasmUrl` (see useScriptingManager): quickjs-eval.wasm
|
|
89
|
+
// backs the QuickJS scripting sandbox (no JS fallback — required
|
|
90
|
+
// for `scripting`), while jbig2/openjpeg/qcms.wasm (with bundled
|
|
91
|
+
// *_nowasm_fallback.js) decode JBIG2 / JPEG2000 images and ICC
|
|
92
|
+
// colour. Without this, scripting fails and those image/colour
|
|
93
|
+
// features silently degrade.
|
|
94
|
+
{
|
|
95
|
+
dir: join(pdfjsDir, "wasm"),
|
|
96
|
+
baseURL: "/_kviewer/wasm",
|
|
97
|
+
maxAge: 60 * 60 * 24 * 365
|
|
98
|
+
},
|
|
99
|
+
// CMYK ICC profile fetched via the `iccUrl` API option for
|
|
100
|
+
// DeviceCMYK ICC-based colour spaces.
|
|
101
|
+
{
|
|
102
|
+
dir: join(pdfjsDir, "iccs"),
|
|
103
|
+
baseURL: "/_kviewer/iccs",
|
|
104
|
+
maxAge: 60 * 60 * 24 * 365
|
|
83
105
|
}
|
|
84
106
|
);
|
|
85
107
|
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type Konva from 'konva';
|
|
2
|
+
/**
|
|
3
|
+
* Cap the backing-store resolution of every layer (scene + hit canvas)
|
|
4
|
+
* on the stage. Idempotent — call after adding a layer to a stage whose
|
|
5
|
+
* CSS size scales with zoom.
|
|
6
|
+
*/
|
|
7
|
+
export declare function applyStageResolutionCap(stage: Konva.Stage): void;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { isIPad } from "./input-device.js";
|
|
2
|
+
const MAX_LAYER_PIXELS_IPAD = 1e6;
|
|
3
|
+
const MAX_LAYER_PIXELS_DEFAULT = 8e6;
|
|
4
|
+
const baseRatios = /* @__PURE__ */ new WeakMap();
|
|
5
|
+
function capCanvas(canvas, cssArea, maxPixels) {
|
|
6
|
+
const current = canvas.getPixelRatio();
|
|
7
|
+
if (!baseRatios.has(canvas)) baseRatios.set(canvas, current);
|
|
8
|
+
const base = baseRatios.get(canvas);
|
|
9
|
+
const target = Math.min(base, Math.sqrt(maxPixels / cssArea));
|
|
10
|
+
if (Math.abs(target - current) > 0.01) canvas.setPixelRatio(target);
|
|
11
|
+
}
|
|
12
|
+
export function applyStageResolutionCap(stage) {
|
|
13
|
+
const cssArea = stage.width() * stage.height();
|
|
14
|
+
if (cssArea <= 0) return;
|
|
15
|
+
const maxPixels = isIPad() ? MAX_LAYER_PIXELS_IPAD : MAX_LAYER_PIXELS_DEFAULT;
|
|
16
|
+
for (const layer of stage.getLayers()) {
|
|
17
|
+
capCanvas(layer.getCanvas(), cssArea, maxPixels);
|
|
18
|
+
capCanvas(layer.getHitCanvas(), cssArea, maxPixels);
|
|
19
|
+
layer.batchDraw();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -2,7 +2,7 @@ import Konva from 'konva';
|
|
|
2
2
|
import type { KonvaEventObject } from 'konva/lib/Node';
|
|
3
3
|
import type { AnnotationType, IAnnotationContentsObj, IAnnotationStore, IAnnotationStyle, IAnnotationType } from '../types.js';
|
|
4
4
|
export interface IEditorOptions {
|
|
5
|
-
userName
|
|
5
|
+
userName?: string;
|
|
6
6
|
konvaStage: Konva.Stage;
|
|
7
7
|
pageNumber: number;
|
|
8
8
|
annotation: IAnnotationType | null;
|
|
@@ -21,7 +21,7 @@ export interface IShapeGroup {
|
|
|
21
21
|
annotation?: IAnnotationType;
|
|
22
22
|
}
|
|
23
23
|
export declare abstract class Editor {
|
|
24
|
-
protected userName
|
|
24
|
+
protected userName?: string;
|
|
25
25
|
readonly id: string;
|
|
26
26
|
readonly onAdd: (annotationStore: IAnnotationStore) => void;
|
|
27
27
|
readonly onChange: (id: string, updates: Partial<IAnnotationStore>) => void;
|
|
@@ -72,4 +72,9 @@ export declare abstract class Editor {
|
|
|
72
72
|
static TimerStart(pageNumber: number, callback: (pageNumber: number) => void, delay?: number): void;
|
|
73
73
|
/** Immediately execute and clear all pending timers (e.g. before export). */
|
|
74
74
|
static flushAllTimers(): void;
|
|
75
|
+
/** Immediately execute and clear one page's pending timer — e.g. before
|
|
76
|
+
* the page's Konva stage is destroyed (zoom commit, unmount), so an
|
|
77
|
+
* in-flight freehand group is finalized into the store instead of
|
|
78
|
+
* dying with the stage. */
|
|
79
|
+
static TimerFlush(pageNumber: number): void;
|
|
75
80
|
}
|
|
@@ -213,6 +213,9 @@ export class Editor {
|
|
|
213
213
|
addSerializedGroupToLayer(konvaStage, konvaString) {
|
|
214
214
|
this.konvaStage = konvaStage;
|
|
215
215
|
const ghostGroup = Konva.Node.create(konvaString);
|
|
216
|
+
if (ghostGroup instanceof Konva.Container) {
|
|
217
|
+
ghostGroup.find((node) => node instanceof Konva.Shape).forEach((shape) => shape.strokeScaleEnabled(true));
|
|
218
|
+
}
|
|
216
219
|
const id = ghostGroup.id();
|
|
217
220
|
this.getBgLayer(konvaStage).add(ghostGroup);
|
|
218
221
|
if (this.shapeGroupStore.has(id)) return;
|
|
@@ -258,4 +261,15 @@ export class Editor {
|
|
|
258
261
|
entry.callback(pageNumber);
|
|
259
262
|
}
|
|
260
263
|
}
|
|
264
|
+
/** Immediately execute and clear one page's pending timer — e.g. before
|
|
265
|
+
* the page's Konva stage is destroyed (zoom commit, unmount), so an
|
|
266
|
+
* in-flight freehand group is finalized into the store instead of
|
|
267
|
+
* dying with the stage. */
|
|
268
|
+
static TimerFlush(pageNumber) {
|
|
269
|
+
const entry = Editor.Timer.get(pageNumber);
|
|
270
|
+
if (!entry) return;
|
|
271
|
+
window.clearTimeout(entry.id);
|
|
272
|
+
Editor.Timer.delete(pageNumber);
|
|
273
|
+
entry.callback(pageNumber);
|
|
274
|
+
}
|
|
261
275
|
}
|
|
@@ -6,6 +6,13 @@ export interface ISelectorOptions {
|
|
|
6
6
|
getAnnotationStore: (id: string) => IAnnotationStore | undefined;
|
|
7
7
|
onSelected: (id: string, isClick: boolean, transformerRect: IRect) => void;
|
|
8
8
|
onMultiSelected: (ids: string[], combinedRect: IRect) => void;
|
|
9
|
+
/** Fired once after a marquee multi-drag with the final geometry of
|
|
10
|
+
* every moved group, so the move persists as a single history step. */
|
|
11
|
+
onMultiChanged: (changes: Array<{
|
|
12
|
+
id: string;
|
|
13
|
+
konvaGroupString: string;
|
|
14
|
+
konvaClientRect: IRect;
|
|
15
|
+
}>, transformerRect: IRect) => void;
|
|
9
16
|
onCancel: () => void;
|
|
10
17
|
onChanged: (id: string, konvaGroupString: string, rawAnnotationStore: IAnnotationStore, konvaClientRect: IRect, transformerRect: IRect) => void;
|
|
11
18
|
onDelete: (id: string) => void;
|
|
@@ -27,6 +34,11 @@ export declare class Selector {
|
|
|
27
34
|
readonly onDelete: (id: string) => void;
|
|
28
35
|
readonly onCancel: () => void;
|
|
29
36
|
readonly onMultiSelected: (ids: string[], combinedRect: IRect) => void;
|
|
37
|
+
readonly onMultiChanged: (changes: Array<{
|
|
38
|
+
id: string;
|
|
39
|
+
konvaGroupString: string;
|
|
40
|
+
konvaClientRect: IRect;
|
|
41
|
+
}>, transformerRect: IRect) => void;
|
|
30
42
|
private onRequestDeleteConfirm?;
|
|
31
43
|
private onFreeTextDoubleClick?;
|
|
32
44
|
private getStylusModeEnabled?;
|
|
@@ -40,7 +52,7 @@ export declare class Selector {
|
|
|
40
52
|
private marqueeRect;
|
|
41
53
|
private marqueeStartPos;
|
|
42
54
|
private marqueeStage;
|
|
43
|
-
constructor({ konvaCanvasStore, getAnnotationStore, onDelete, onSelected, onMultiSelected, onCancel, onChanged, onRequestDeleteConfirm, onFreeTextDoubleClick, getStylusModeEnabled, setExternalGesturesEnabled, }: ISelectorOptions);
|
|
55
|
+
constructor({ konvaCanvasStore, getAnnotationStore, onDelete, onSelected, onMultiSelected, onMultiChanged, onCancel, onChanged, onRequestDeleteConfirm, onFreeTextDoubleClick, getStylusModeEnabled, setExternalGesturesEnabled, }: ISelectorOptions);
|
|
44
56
|
/** Returns true if this event should be rejected (finger touch in stylus mode). */
|
|
45
57
|
private isStylusRejected;
|
|
46
58
|
private handleKeyDown;
|
|
@@ -3,12 +3,14 @@ import { AnnotationType } from "../types.js";
|
|
|
3
3
|
import { SELECTOR_HOVER_STYLE, SHAPE_GROUP_NAME } from "../const.js";
|
|
4
4
|
import { defaultOptions } from "../config.js";
|
|
5
5
|
import { isStylusTouch } from "../input-device.js";
|
|
6
|
+
import { applyStageResolutionCap } from "../canvas-resolution.js";
|
|
6
7
|
export class Selector {
|
|
7
8
|
onSelected;
|
|
8
9
|
onChanged;
|
|
9
10
|
onDelete;
|
|
10
11
|
onCancel;
|
|
11
12
|
onMultiSelected;
|
|
13
|
+
onMultiChanged;
|
|
12
14
|
onRequestDeleteConfirm;
|
|
13
15
|
onFreeTextDoubleClick;
|
|
14
16
|
getStylusModeEnabled;
|
|
@@ -28,6 +30,7 @@ export class Selector {
|
|
|
28
30
|
onDelete,
|
|
29
31
|
onSelected,
|
|
30
32
|
onMultiSelected,
|
|
33
|
+
onMultiChanged,
|
|
31
34
|
onCancel,
|
|
32
35
|
onChanged,
|
|
33
36
|
onRequestDeleteConfirm,
|
|
@@ -40,6 +43,7 @@ export class Selector {
|
|
|
40
43
|
this.onDelete = onDelete;
|
|
41
44
|
this.onSelected = onSelected;
|
|
42
45
|
this.onMultiSelected = onMultiSelected;
|
|
46
|
+
this.onMultiChanged = onMultiChanged;
|
|
43
47
|
this.onCancel = onCancel;
|
|
44
48
|
this.onChanged = onChanged;
|
|
45
49
|
this.onRequestDeleteConfirm = onRequestDeleteConfirm;
|
|
@@ -231,8 +235,9 @@ export class Selector {
|
|
|
231
235
|
const moveToDragLayer = () => {
|
|
232
236
|
if (dragLayer) return;
|
|
233
237
|
const bg = this.getBackgroundLayer(konvaStage);
|
|
234
|
-
dragLayer = new Konva.Layer();
|
|
238
|
+
dragLayer = new Konva.Layer({ listening: false });
|
|
235
239
|
konvaStage.add(dragLayer);
|
|
240
|
+
applyStageResolutionCap(konvaStage);
|
|
236
241
|
group.moveTo(dragLayer);
|
|
237
242
|
transformer.moveTo(dragLayer);
|
|
238
243
|
bg.batchDraw();
|
|
@@ -240,6 +245,7 @@ export class Selector {
|
|
|
240
245
|
};
|
|
241
246
|
const restoreFromDragLayer = () => {
|
|
242
247
|
if (!dragLayer) return;
|
|
248
|
+
endGhostDrag();
|
|
243
249
|
const bg = this.getBackgroundLayer(konvaStage);
|
|
244
250
|
group.moveTo(bg);
|
|
245
251
|
transformer.moveTo(bg);
|
|
@@ -247,6 +253,32 @@ export class Selector {
|
|
|
247
253
|
dragLayer = null;
|
|
248
254
|
bg.batchDraw();
|
|
249
255
|
};
|
|
256
|
+
let dragGhost = null;
|
|
257
|
+
let dragVisualOrigin = null;
|
|
258
|
+
const startGhostDrag = (origin) => {
|
|
259
|
+
if (!dragLayer || dragGhost) return;
|
|
260
|
+
dragLayer.draw();
|
|
261
|
+
const canvases = konvaStage.content.querySelectorAll("canvas");
|
|
262
|
+
const real = canvases[canvases.length - 1];
|
|
263
|
+
if (!real) return;
|
|
264
|
+
const ghost = document.createElement("canvas");
|
|
265
|
+
ghost.width = real.width;
|
|
266
|
+
ghost.height = real.height;
|
|
267
|
+
ghost.getContext("2d")?.drawImage(real, 0, 0);
|
|
268
|
+
ghost.style.cssText = `position:absolute;top:0;left:0;width:${real.style.width};height:${real.style.height};pointer-events:none;`;
|
|
269
|
+
konvaStage.content.appendChild(ghost);
|
|
270
|
+
dragGhost = ghost;
|
|
271
|
+
dragVisualOrigin = origin;
|
|
272
|
+
dragLayer.visible(false);
|
|
273
|
+
};
|
|
274
|
+
const endGhostDrag = () => {
|
|
275
|
+
dragVisualOrigin = null;
|
|
276
|
+
if (!dragGhost) return;
|
|
277
|
+
dragLayer?.visible(true);
|
|
278
|
+
dragLayer?.draw();
|
|
279
|
+
dragGhost.remove();
|
|
280
|
+
dragGhost = null;
|
|
281
|
+
};
|
|
250
282
|
transformer.on("transformstart", () => {
|
|
251
283
|
this.setExternalGesturesEnabled?.(false);
|
|
252
284
|
moveToDragLayer();
|
|
@@ -273,9 +305,13 @@ export class Selector {
|
|
|
273
305
|
dragStartRect = this.getTotalBox(boxes);
|
|
274
306
|
const firstNode = transformer.nodes()[0];
|
|
275
307
|
dragAnchorPos = firstNode ? firstNode.getAbsolutePosition() : null;
|
|
308
|
+
if (transformer.nodes().length === 1 && dragAnchorPos) {
|
|
309
|
+
startGhostDrag({ ...dragAnchorPos });
|
|
310
|
+
}
|
|
276
311
|
});
|
|
277
312
|
transformer.on("dragend", () => {
|
|
278
313
|
this.setExternalGesturesEnabled?.(true);
|
|
314
|
+
endGhostDrag();
|
|
279
315
|
restoreFromDragLayer();
|
|
280
316
|
dragStartRect = null;
|
|
281
317
|
dragAnchorPos = null;
|
|
@@ -299,14 +335,17 @@ export class Selector {
|
|
|
299
335
|
const boxY = dragStartRect.y + dy;
|
|
300
336
|
const halfWidth = dragStartRect.width / 2;
|
|
301
337
|
const halfHeight = dragStartRect.height / 2;
|
|
302
|
-
const
|
|
303
|
-
const
|
|
338
|
+
const pageCss = konvaStage.getAttr("kvPageCss");
|
|
339
|
+
const minX = konvaStage.x();
|
|
340
|
+
const minY = konvaStage.y();
|
|
341
|
+
const maxX = minX + (pageCss?.width ?? konvaStage.width());
|
|
342
|
+
const maxY = minY + (pageCss?.height ?? konvaStage.height());
|
|
304
343
|
let clampDx = 0;
|
|
305
344
|
let clampDy = 0;
|
|
306
|
-
if (boxX + halfWidth <
|
|
307
|
-
else if (boxX + halfWidth >
|
|
308
|
-
if (boxY + halfHeight <
|
|
309
|
-
else if (boxY + halfHeight >
|
|
345
|
+
if (boxX + halfWidth < minX) clampDx = minX - (boxX + halfWidth);
|
|
346
|
+
else if (boxX + halfWidth > maxX) clampDx = maxX - (boxX + halfWidth);
|
|
347
|
+
if (boxY + halfHeight < minY) clampDy = minY - (boxY + halfHeight);
|
|
348
|
+
else if (boxY + halfHeight > maxY) clampDy = maxY - (boxY + halfHeight);
|
|
310
349
|
if (clampDx !== 0 || clampDy !== 0) {
|
|
311
350
|
nodes.forEach((shape) => {
|
|
312
351
|
const pos = shape.getAbsolutePosition();
|
|
@@ -314,6 +353,10 @@ export class Selector {
|
|
|
314
353
|
});
|
|
315
354
|
dragAnchorPos = firstNode.getAbsolutePosition();
|
|
316
355
|
}
|
|
356
|
+
if (dragGhost && dragVisualOrigin) {
|
|
357
|
+
const pos = firstNode.getAbsolutePosition();
|
|
358
|
+
dragGhost.style.transform = `translate(${pos.x - dragVisualOrigin.x}px, ${pos.y - dragVisualOrigin.y}px)`;
|
|
359
|
+
}
|
|
317
360
|
});
|
|
318
361
|
transformer.nodes([group]);
|
|
319
362
|
this.getBackgroundLayer(konvaStage).add(transformer);
|
|
@@ -398,6 +441,7 @@ export class Selector {
|
|
|
398
441
|
transformer.nodes().forEach((group) => {
|
|
399
442
|
if (group instanceof Konva.Group) {
|
|
400
443
|
group.draggable(false);
|
|
444
|
+
group.off(".marquee");
|
|
401
445
|
}
|
|
402
446
|
});
|
|
403
447
|
transformer.nodes([]);
|
|
@@ -555,6 +599,12 @@ export class Selector {
|
|
|
555
599
|
});
|
|
556
600
|
node.on("dragend.marquee", () => {
|
|
557
601
|
this.setExternalGesturesEnabled?.(true);
|
|
602
|
+
const changes = transformer.nodes().filter((n) => n instanceof Konva.Group).map((n) => ({
|
|
603
|
+
id: n.id(),
|
|
604
|
+
konvaGroupString: n.toJSON(),
|
|
605
|
+
konvaClientRect: Konva.Node.create(n.toJSON()).getClientRect()
|
|
606
|
+
}));
|
|
607
|
+
this.onMultiChanged(changes, transformer.getClientRect());
|
|
558
608
|
this.onMultiSelected(matchedIds, transformer.getClientRect());
|
|
559
609
|
});
|
|
560
610
|
});
|
|
@@ -673,7 +723,9 @@ export class Selector {
|
|
|
673
723
|
return;
|
|
674
724
|
}
|
|
675
725
|
const scale = this.eraserStage.scaleX() ?? 1;
|
|
676
|
-
const
|
|
726
|
+
const sx = this.eraserStage.x();
|
|
727
|
+
const sy = this.eraserStage.y();
|
|
728
|
+
const screenPoints = points.map((v, i) => v * scale + (i % 2 === 0 ? sx : sy));
|
|
677
729
|
const tolerance = 6;
|
|
678
730
|
const groups = this.getPageShapeGroups(this.eraserStage);
|
|
679
731
|
const toDelete = [];
|
|
@@ -21,7 +21,7 @@ function normalizeComment(comment, fallbackTitle) {
|
|
|
21
21
|
if (!isString(content)) return null;
|
|
22
22
|
return {
|
|
23
23
|
id: isString(comment.id) && comment.id.length > 0 ? comment.id : generateUUID(),
|
|
24
|
-
title: isString(comment.title) && comment.title.length > 0 ? comment.title : fallbackTitle,
|
|
24
|
+
title: isString(comment.title) && comment.title.length > 0 ? comment.title : fallbackTitle || void 0,
|
|
25
25
|
date: isString(comment.date) && comment.date.length > 0 ? comment.date : formatTimestamp(Date.now()),
|
|
26
26
|
content,
|
|
27
27
|
status: isString(comment.status) ? comment.status : void 0
|
|
@@ -66,7 +66,7 @@ function normalizeAnnotation(raw, fallbackTitle) {
|
|
|
66
66
|
pageNumber,
|
|
67
67
|
konvaString,
|
|
68
68
|
konvaClientRect: { x, y, width, height },
|
|
69
|
-
title: isString(raw.title) && raw.title.length > 0 ? raw.title : fallbackTitle,
|
|
69
|
+
title: isString(raw.title) && raw.title.length > 0 ? raw.title : fallbackTitle || void 0,
|
|
70
70
|
type,
|
|
71
71
|
subtype,
|
|
72
72
|
pdfjsType,
|
|
@@ -81,7 +81,7 @@ function normalizeAnnotation(raw, fallbackTitle) {
|
|
|
81
81
|
};
|
|
82
82
|
return normalized;
|
|
83
83
|
}
|
|
84
|
-
export function normalizeImportedAnnotations(input, fallbackTitle
|
|
84
|
+
export function normalizeImportedAnnotations(input, fallbackTitle) {
|
|
85
85
|
if (!Array.isArray(input)) {
|
|
86
86
|
return { annotations: [], skipped: 0 };
|
|
87
87
|
}
|
|
@@ -29,10 +29,14 @@ export interface PainterCallbacks {
|
|
|
29
29
|
getFormFieldPreviewColor?: () => string | undefined;
|
|
30
30
|
}
|
|
31
31
|
export declare class Painter {
|
|
32
|
-
private userName
|
|
32
|
+
private userName?;
|
|
33
33
|
private konvaCanvasStore;
|
|
34
34
|
private editorStore;
|
|
35
35
|
private currentAnnotation;
|
|
36
|
+
/** Marquee/eraser set currentAnnotation to null, so the active mode is
|
|
37
|
+
* tracked separately to allow restoring its bindings after programmatic
|
|
38
|
+
* editor churn (undo/redo, import). */
|
|
39
|
+
private activeNonToolMode;
|
|
36
40
|
private store;
|
|
37
41
|
private selector;
|
|
38
42
|
private tempDataTransfer;
|
|
@@ -41,7 +45,7 @@ export declare class Painter {
|
|
|
41
45
|
private getReadonly?;
|
|
42
46
|
private freehandGroupingDelay;
|
|
43
47
|
constructor({ userName, callbacks, getStylusModeEnabled, getReadonly, freehandGroupingDelay, setExternalGesturesEnabled, }: {
|
|
44
|
-
userName
|
|
48
|
+
userName?: string;
|
|
45
49
|
callbacks: PainterCallbacks;
|
|
46
50
|
getStylusModeEnabled?: () => boolean;
|
|
47
51
|
getReadonly?: () => boolean;
|
|
@@ -52,6 +56,21 @@ export declare class Painter {
|
|
|
52
56
|
private bindGlobalEvents;
|
|
53
57
|
private globalKeyUpHandler;
|
|
54
58
|
private createKonvaStage;
|
|
59
|
+
/**
|
|
60
|
+
* Restrict the stage to the visible window of the page (page-CSS
|
|
61
|
+
* coords) — or restore the full page with `null`. iOS Safari blits a
|
|
62
|
+
* changing canvas at its CSS size in software: a page-sized canvas at
|
|
63
|
+
* high zoom costs hundreds of ms per frame on A-series iPads no matter
|
|
64
|
+
* how small the backing store is. Shrinking the stage CSS to the
|
|
65
|
+
* viewport window makes drag cost viewport-bound. The stage position
|
|
66
|
+
* offsets the content so shapes keep page coordinates.
|
|
67
|
+
*/
|
|
68
|
+
setStageWindow(pageNumber: number, win: {
|
|
69
|
+
x: number;
|
|
70
|
+
y: number;
|
|
71
|
+
width: number;
|
|
72
|
+
height: number;
|
|
73
|
+
} | null): void;
|
|
55
74
|
private applyStylusTouchAction;
|
|
56
75
|
private removeStylusTouchAction;
|
|
57
76
|
private saveToStore;
|
|
@@ -89,6 +108,18 @@ export declare class Painter {
|
|
|
89
108
|
* is currently active.
|
|
90
109
|
*/
|
|
91
110
|
private refreshSelectorForPages;
|
|
111
|
+
/**
|
|
112
|
+
* Re-bind the stage handlers of whatever tool is currently active.
|
|
113
|
+
*
|
|
114
|
+
* Creating or activating an editor rebinds the Konva stage events to
|
|
115
|
+
* THAT editor (the Editor constructor and activate() both run
|
|
116
|
+
* disableEditMode + enableEditMode). Programmatic add paths — undo/redo
|
|
117
|
+
* via reAddAnnotation, importAnnotations via merge/replace — may lazily
|
|
118
|
+
* create editors for the restored annotation's type, which would leave
|
|
119
|
+
* the stage bound to the wrong tool (e.g. undoing a rectangle delete
|
|
120
|
+
* while freehand is active made the stage draw rectangles).
|
|
121
|
+
*/
|
|
122
|
+
private restoreActiveToolBindings;
|
|
92
123
|
refreshStylusMode(): void;
|
|
93
124
|
initCanvasForPage(pageNumber: number, container: HTMLDivElement, width: number, height: number, scale: number): void;
|
|
94
125
|
destroyCanvasForPage(pageNumber: number): void;
|
|
@@ -26,6 +26,7 @@ import { EditorFormField } from "./tools/form-field.js";
|
|
|
26
26
|
import { Selector } from "./editor/selector.js";
|
|
27
27
|
import { Store } from "./store.js";
|
|
28
28
|
import { isIPad } from "./input-device.js";
|
|
29
|
+
import { applyStageResolutionCap } from "./canvas-resolution.js";
|
|
29
30
|
if (typeof window !== "undefined" && isIPad()) {
|
|
30
31
|
Konva.pixelRatio = 1;
|
|
31
32
|
}
|
|
@@ -34,6 +35,10 @@ export class Painter {
|
|
|
34
35
|
konvaCanvasStore = /* @__PURE__ */ new Map();
|
|
35
36
|
editorStore = /* @__PURE__ */ new Map();
|
|
36
37
|
currentAnnotation = null;
|
|
38
|
+
/** Marquee/eraser set currentAnnotation to null, so the active mode is
|
|
39
|
+
* tracked separately to allow restoring its bindings after programmatic
|
|
40
|
+
* editor churn (undo/redo, import). */
|
|
41
|
+
activeNonToolMode = null;
|
|
37
42
|
store;
|
|
38
43
|
selector;
|
|
39
44
|
tempDataTransfer = null;
|
|
@@ -83,6 +88,21 @@ export class Painter {
|
|
|
83
88
|
this.callbacks.onAnnotationChanged(ann, viewportRect);
|
|
84
89
|
}
|
|
85
90
|
},
|
|
91
|
+
onMultiChanged: (changes, transformerRect) => {
|
|
92
|
+
let lastChanged = null;
|
|
93
|
+
for (const change of changes) {
|
|
94
|
+
if (!this.store.annotation(change.id)) continue;
|
|
95
|
+
this.updateStore(change.id, {
|
|
96
|
+
konvaString: change.konvaGroupString,
|
|
97
|
+
konvaClientRect: change.konvaClientRect
|
|
98
|
+
});
|
|
99
|
+
lastChanged = this.store.annotation(change.id) ?? lastChanged;
|
|
100
|
+
}
|
|
101
|
+
if (lastChanged) {
|
|
102
|
+
const viewportRect = this.toViewportRect(lastChanged.pageNumber, transformerRect);
|
|
103
|
+
this.callbacks.onAnnotationChanged(lastChanged, viewportRect);
|
|
104
|
+
}
|
|
105
|
+
},
|
|
86
106
|
onCancel: () => {
|
|
87
107
|
this.callbacks.onAnnotationChanging();
|
|
88
108
|
},
|
|
@@ -137,13 +157,46 @@ export class Painter {
|
|
|
137
157
|
height: Math.floor(height * scale),
|
|
138
158
|
scale: { x: scale, y: scale }
|
|
139
159
|
});
|
|
160
|
+
stage.setAttr("kvPageCss", {
|
|
161
|
+
width: Math.floor(width * scale),
|
|
162
|
+
height: Math.floor(height * scale)
|
|
163
|
+
});
|
|
140
164
|
const backgroundLayer = new Konva.Layer();
|
|
141
165
|
stage.add(backgroundLayer);
|
|
166
|
+
applyStageResolutionCap(stage);
|
|
142
167
|
if (this.getStylusModeEnabled?.()) {
|
|
143
168
|
this.applyStylusTouchAction(stage);
|
|
144
169
|
}
|
|
145
170
|
return stage;
|
|
146
171
|
}
|
|
172
|
+
/**
|
|
173
|
+
* Restrict the stage to the visible window of the page (page-CSS
|
|
174
|
+
* coords) — or restore the full page with `null`. iOS Safari blits a
|
|
175
|
+
* changing canvas at its CSS size in software: a page-sized canvas at
|
|
176
|
+
* high zoom costs hundreds of ms per frame on A-series iPads no matter
|
|
177
|
+
* how small the backing store is. Shrinking the stage CSS to the
|
|
178
|
+
* viewport window makes drag cost viewport-bound. The stage position
|
|
179
|
+
* offsets the content so shapes keep page coordinates.
|
|
180
|
+
*/
|
|
181
|
+
setStageWindow(pageNumber, win) {
|
|
182
|
+
const canvas = this.konvaCanvasStore.get(pageNumber);
|
|
183
|
+
if (!canvas) return;
|
|
184
|
+
const stage = canvas.konvaStage;
|
|
185
|
+
const pageCss = stage.getAttr("kvPageCss");
|
|
186
|
+
const x = win ? Math.max(0, Math.min(win.x, pageCss.width)) : 0;
|
|
187
|
+
const y = win ? Math.max(0, Math.min(win.y, pageCss.height)) : 0;
|
|
188
|
+
const w = win ? Math.min(win.x + win.width, pageCss.width) - x : pageCss.width;
|
|
189
|
+
const h = win ? Math.min(win.y + win.height, pageCss.height) - y : pageCss.height;
|
|
190
|
+
if (w <= 0 || h <= 0) return;
|
|
191
|
+
if (Math.abs(stage.width() - w) < 1 && Math.abs(stage.height() - h) < 1 && Math.abs(stage.x() + x) < 1 && Math.abs(stage.y() + y) < 1) return;
|
|
192
|
+
stage.size({ width: Math.ceil(w), height: Math.ceil(h) });
|
|
193
|
+
stage.position({ x: -x, y: -y });
|
|
194
|
+
stage.content.style.position = "absolute";
|
|
195
|
+
stage.content.style.left = `${x}px`;
|
|
196
|
+
stage.content.style.top = `${y}px`;
|
|
197
|
+
applyStageResolutionCap(stage);
|
|
198
|
+
stage.batchDraw();
|
|
199
|
+
}
|
|
147
200
|
applyStylusTouchAction(stage) {
|
|
148
201
|
stage.preventDefault(false);
|
|
149
202
|
if (!isIPad()) {
|
|
@@ -190,8 +243,8 @@ export class Painter {
|
|
|
190
243
|
toViewportRect(pageNumber, stageRect) {
|
|
191
244
|
const canvas = this.konvaCanvasStore.get(pageNumber);
|
|
192
245
|
if (!canvas) return stageRect;
|
|
193
|
-
const containerBounds = canvas.
|
|
194
|
-
const visualScale = canvas.
|
|
246
|
+
const containerBounds = canvas.konvaStage.content.getBoundingClientRect();
|
|
247
|
+
const visualScale = canvas.konvaStage.width() > 0 ? containerBounds.width / canvas.konvaStage.width() : 1;
|
|
195
248
|
return {
|
|
196
249
|
x: containerBounds.left + stageRect.x * visualScale,
|
|
197
250
|
y: containerBounds.top + stageRect.y * visualScale,
|
|
@@ -460,6 +513,32 @@ export class Painter {
|
|
|
460
513
|
this.selector.activate(pageNumber);
|
|
461
514
|
}
|
|
462
515
|
}
|
|
516
|
+
/**
|
|
517
|
+
* Re-bind the stage handlers of whatever tool is currently active.
|
|
518
|
+
*
|
|
519
|
+
* Creating or activating an editor rebinds the Konva stage events to
|
|
520
|
+
* THAT editor (the Editor constructor and activate() both run
|
|
521
|
+
* disableEditMode + enableEditMode). Programmatic add paths — undo/redo
|
|
522
|
+
* via reAddAnnotation, importAnnotations via merge/replace — may lazily
|
|
523
|
+
* create editors for the restored annotation's type, which would leave
|
|
524
|
+
* the stage bound to the wrong tool (e.g. undoing a rectangle delete
|
|
525
|
+
* while freehand is active made the stage draw rectangles).
|
|
526
|
+
*/
|
|
527
|
+
restoreActiveToolBindings() {
|
|
528
|
+
if (this.currentAnnotation) {
|
|
529
|
+
this.enablePainting();
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
if (this.activeNonToolMode === "marquee") {
|
|
533
|
+
this.konvaCanvasStore.forEach(({ pageNumber }) => {
|
|
534
|
+
this.selector.activateMarquee(pageNumber);
|
|
535
|
+
});
|
|
536
|
+
} else if (this.activeNonToolMode === "eraser") {
|
|
537
|
+
this.konvaCanvasStore.forEach(({ pageNumber }) => {
|
|
538
|
+
this.selector.activateEraser(pageNumber);
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
}
|
|
463
542
|
// --- Public API ---
|
|
464
543
|
refreshStylusMode() {
|
|
465
544
|
const enabled = this.getStylusModeEnabled?.() ?? false;
|
|
@@ -489,12 +568,14 @@ export class Painter {
|
|
|
489
568
|
destroyCanvasForPage(pageNumber) {
|
|
490
569
|
const konvaCanvas = this.konvaCanvasStore.get(pageNumber);
|
|
491
570
|
if (konvaCanvas) {
|
|
571
|
+
Editor.TimerFlush(pageNumber);
|
|
492
572
|
konvaCanvas.konvaStage.destroy();
|
|
493
573
|
this.konvaCanvasStore.delete(pageNumber);
|
|
494
574
|
}
|
|
495
575
|
}
|
|
496
576
|
activate(annotation, dataTransfer) {
|
|
497
577
|
this.currentAnnotation = annotation;
|
|
578
|
+
this.activeNonToolMode = null;
|
|
498
579
|
this.disablePainting();
|
|
499
580
|
this.saveTempDataTransfer(dataTransfer);
|
|
500
581
|
if (!annotation) return;
|
|
@@ -522,6 +603,7 @@ export class Painter {
|
|
|
522
603
|
}
|
|
523
604
|
activateMarquee() {
|
|
524
605
|
this.currentAnnotation = null;
|
|
606
|
+
this.activeNonToolMode = "marquee";
|
|
525
607
|
this.disablePainting();
|
|
526
608
|
this.konvaCanvasStore.forEach(({ pageNumber }) => {
|
|
527
609
|
this.selector.activateMarquee(pageNumber);
|
|
@@ -529,6 +611,7 @@ export class Painter {
|
|
|
529
611
|
}
|
|
530
612
|
activateEraser() {
|
|
531
613
|
this.currentAnnotation = null;
|
|
614
|
+
this.activeNonToolMode = "eraser";
|
|
532
615
|
this.disablePainting();
|
|
533
616
|
this.konvaCanvasStore.forEach(({ pageNumber }) => {
|
|
534
617
|
this.selector.activateEraser(pageNumber);
|
|
@@ -585,6 +668,7 @@ export class Painter {
|
|
|
585
668
|
}
|
|
586
669
|
if (editor) {
|
|
587
670
|
editor.addSerializedGroupToLayer(konvaCanvas.konvaStage, annotation.konvaString);
|
|
671
|
+
this.restoreActiveToolBindings();
|
|
588
672
|
this.refreshSelectorForPages([annotation.pageNumber]);
|
|
589
673
|
}
|
|
590
674
|
}
|
|
@@ -622,6 +706,7 @@ export class Painter {
|
|
|
622
706
|
this.drawAnnotation(annotation);
|
|
623
707
|
touchedPages.add(annotation.pageNumber);
|
|
624
708
|
});
|
|
709
|
+
this.restoreActiveToolBindings();
|
|
625
710
|
this.refreshSelectorForPages(touchedPages);
|
|
626
711
|
}
|
|
627
712
|
mergeAnnotations(annotations) {
|
|
@@ -634,6 +719,7 @@ export class Painter {
|
|
|
634
719
|
this.drawAnnotation(annotation);
|
|
635
720
|
touchedPages.add(annotation.pageNumber);
|
|
636
721
|
});
|
|
722
|
+
this.restoreActiveToolBindings();
|
|
637
723
|
this.refreshSelectorForPages(touchedPages);
|
|
638
724
|
}
|
|
639
725
|
getCanvasState() {
|