kviewer 0.3.3 → 0.3.5
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/selector.d.ts +1 -0
- package/dist/runtime/annotation/engine/editor/selector.js +17 -5
- package/dist/runtime/annotation/engine/painter.d.ts +4 -2
- package/dist/runtime/annotation/engine/painter.js +4 -0
- package/dist/runtime/annotation/engine/tools/form-field.d.ts +5 -5
- package/dist/runtime/annotation/engine/tools/form-field.js +1 -1
- package/dist/runtime/annotation/engine/types.d.ts +18 -9
- package/dist/runtime/annotation/pdf-export/export-form-fields.js +6 -29
- package/dist/runtime/assets/kviewer.css +1 -1
- package/dist/runtime/components/AnnotationToolbar.vue +12 -1
- package/dist/runtime/components/FormFieldLayer.vue +7 -2
- package/dist/runtime/components/PdfPage.vue +24 -1
- package/dist/runtime/components/SelectionToolbarItems.d.vue.ts +8 -0
- package/dist/runtime/components/SelectionToolbarItems.vue +43 -0
- package/dist/runtime/components/SelectionToolbarItems.vue.d.ts +8 -0
- package/dist/runtime/components/Viewer.d.vue.ts +24 -17
- package/dist/runtime/components/Viewer.vue +60 -37
- package/dist/runtime/components/Viewer.vue.d.ts +24 -17
- package/dist/runtime/components/ViewerBar.d.vue.ts +4 -4
- package/dist/runtime/components/ViewerBar.vue +2 -1
- package/dist/runtime/components/ViewerBar.vue.d.ts +4 -4
- package/dist/runtime/components/ViewerTabs.d.vue.ts +16 -17
- package/dist/runtime/components/ViewerTabs.vue +5 -6
- package/dist/runtime/components/ViewerTabs.vue.d.ts +16 -17
- package/dist/runtime/components/form-fields/FormFieldWrapper.vue +35 -10
- package/dist/runtime/components/form-fields/FormSignatureField.vue +10 -2
- package/dist/runtime/components/form-fields/PlacedFieldChrome.d.vue.ts +4 -0
- package/dist/runtime/components/form-fields/PlacedFieldChrome.vue +35 -18
- package/dist/runtime/components/form-fields/PlacedFieldChrome.vue.d.ts +4 -0
- package/dist/runtime/components/form-fields/PlacedFieldToolbar.d.vue.ts +7 -0
- package/dist/runtime/components/form-fields/PlacedFieldToolbar.vue +51 -0
- package/dist/runtime/components/form-fields/PlacedFieldToolbar.vue.d.ts +7 -0
- package/dist/runtime/composables/useFormFields.d.ts +28 -17
- package/dist/runtime/composables/useFormFields.js +20 -24
- package/dist/runtime/embed/bridge-client.d.ts +4 -2
- package/dist/runtime/embed/bridge-client.js +2 -1
- package/dist/runtime/embed/bridge-host.d.ts +2 -0
- package/dist/runtime/embed/bridge-host.js +16 -0
- package/dist/runtime/embed/protocol.d.ts +12 -0
- package/dist/runtime/public-types.d.ts +2 -1
- package/dist/runtime/selection-items.d.ts +61 -0
- package/dist/runtime/selection-items.js +1 -0
- package/dist/runtime/toolbar-tools.d.ts +4 -1
- package/dist/types.d.mts +1 -1
- package/package.json +1 -1
package/dist/module.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
|
-
export { ActiveTool, AddFormFieldPayload, CheckboxStyle, ExportPdfOptions, FormFieldDefinition, FormFieldOrigin, FormFieldType, FormFieldValue, KVIEWER_EMBED_PROTOCOL_VERSION, KVIEWER_EMBED_UNSUPPORTED_METHOD_ERROR_NAME, KViewerApi, KViewerEmbedBridgeOptions, KViewerEmbedClient, KViewerEmbedClientOptions, KViewerEmbedEventName, KViewerEmbedEventPayloads, KViewerEmbedMethodName, KViewerHostApi, PlacedFieldDefaults, SignatureData, SignatureFieldStatus, SignatureHandlers, ViewMode, ViewerBottomToolId, ViewerLayoutToolId, ViewerMenuButtonItem, ViewerMenuCheckboxItem, ViewerMenuItem, ViewerMenuSeparatorItem, ViewerState, ViewerToolEntry, ViewerToolId, ViewerToolSlot, ViewerTopToolId, useKViewerEmbedBridge } from '../dist/runtime/public-types.js';
|
|
2
|
+
export { ActiveTool, AddFormFieldPayload, CheckboxStyle, ExportPdfOptions, FormFieldDefinition, FormFieldOrigin, FormFieldType, FormFieldValue, IAnnotationStore, KVIEWER_EMBED_PROTOCOL_VERSION, KVIEWER_EMBED_UNSUPPORTED_METHOD_ERROR_NAME, KViewerApi, KViewerEmbedBridgeOptions, KViewerEmbedClient, KViewerEmbedClientOptions, KViewerEmbedEventName, KViewerEmbedEventPayloads, KViewerEmbedMethodName, KViewerHostApi, PlacedFieldDefaults, SignatureData, SignatureFieldStatus, SignatureHandlers, ViewMode, ViewerBottomToolId, ViewerLayoutToolId, ViewerMenuButtonItem, ViewerMenuCheckboxItem, ViewerMenuItem, ViewerMenuSeparatorItem, ViewerSelectionButtonItem, ViewerSelectionContext, ViewerSelectionItem, ViewerSelectionSelectItem, ViewerState, ViewerToolEntry, ViewerToolId, ViewerToolSlot, ViewerTopToolId, useKViewerEmbedBridge } from '../dist/runtime/public-types.js';
|
|
3
3
|
|
|
4
4
|
interface ModuleOptions {
|
|
5
5
|
/**
|
package/dist/module.json
CHANGED
|
@@ -52,6 +52,7 @@ export declare class Selector {
|
|
|
52
52
|
private marqueeRect;
|
|
53
53
|
private marqueeStartPos;
|
|
54
54
|
private marqueeStage;
|
|
55
|
+
private suppressStageClick;
|
|
55
56
|
constructor({ konvaCanvasStore, getAnnotationStore, onDelete, onSelected, onMultiSelected, onMultiChanged, onCancel, onChanged, onRequestDeleteConfirm, onFreeTextDoubleClick, getStylusModeEnabled, setExternalGesturesEnabled, }: ISelectorOptions);
|
|
56
57
|
/** Returns true if this event should be rejected (finger touch in stylus mode). */
|
|
57
58
|
private isStylusRejected;
|
|
@@ -24,6 +24,7 @@ export class Selector {
|
|
|
24
24
|
marqueeRect = null;
|
|
25
25
|
marqueeStartPos = null;
|
|
26
26
|
marqueeStage = null;
|
|
27
|
+
suppressStageClick = false;
|
|
27
28
|
constructor({
|
|
28
29
|
konvaCanvasStore,
|
|
29
30
|
getAnnotationStore,
|
|
@@ -88,13 +89,17 @@ export class Selector {
|
|
|
88
89
|
}
|
|
89
90
|
disableStageEvents(konvaStage) {
|
|
90
91
|
konvaStage.off(
|
|
91
|
-
"click mousedown mousemove mouseup touchstart touchmove touchend pointerdown pointermove pointerup"
|
|
92
|
+
"click tap mousedown mousemove mouseup touchstart touchmove touchend pointerdown pointermove pointerup"
|
|
92
93
|
);
|
|
93
94
|
}
|
|
94
95
|
bindStageEvents(konvaStage) {
|
|
95
96
|
konvaStage.on("click tap", (e) => {
|
|
96
97
|
if (e.target !== konvaStage) return;
|
|
97
98
|
if (this.isStylusRejected(e.evt)) return;
|
|
99
|
+
if (this.suppressStageClick) {
|
|
100
|
+
this.suppressStageClick = false;
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
98
103
|
this.clearTransformers();
|
|
99
104
|
});
|
|
100
105
|
}
|
|
@@ -474,6 +479,7 @@ export class Selector {
|
|
|
474
479
|
this.handleShapeClick(shape, konvaStage, false);
|
|
475
480
|
}
|
|
476
481
|
clear() {
|
|
482
|
+
this.suppressStageClick = false;
|
|
477
483
|
this.clearTransformers();
|
|
478
484
|
this.konvaCanvasStore.forEach((konvaCanvas) => {
|
|
479
485
|
const { konvaStage } = konvaCanvas;
|
|
@@ -489,6 +495,7 @@ export class Selector {
|
|
|
489
495
|
const pageGroups = this.getPageShapeGroups(konvaStage);
|
|
490
496
|
this.disableStageEvents(konvaStage);
|
|
491
497
|
this.bindStageEvents(konvaStage);
|
|
498
|
+
this.bindMarqueeEvents(konvaStage, { mouseOnly: true });
|
|
492
499
|
this.enableShapeGroups(pageGroups, konvaStage);
|
|
493
500
|
if (this.selectedId) {
|
|
494
501
|
this.selectedShape(this.selectedId, konvaStage);
|
|
@@ -519,13 +526,17 @@ export class Selector {
|
|
|
519
526
|
this.enableShapeGroups(pageGroups, konvaStage);
|
|
520
527
|
this.bindMarqueeEvents(konvaStage);
|
|
521
528
|
}
|
|
522
|
-
bindMarqueeEvents(konvaStage) {
|
|
523
|
-
|
|
529
|
+
bindMarqueeEvents(konvaStage, opts = {}) {
|
|
530
|
+
const { mouseOnly = false } = opts;
|
|
531
|
+
konvaStage.on(mouseOnly ? "mousedown" : "mousedown touchstart", (e) => {
|
|
524
532
|
if (e.target !== konvaStage) return;
|
|
525
533
|
if (this.isStylusRejected(e.evt)) return;
|
|
534
|
+
const button = e.evt?.button;
|
|
535
|
+
if (mouseOnly && button !== void 0 && button !== 0) return;
|
|
526
536
|
if (this.getStylusModeEnabled?.() && e.evt) e.evt.preventDefault();
|
|
527
537
|
this.clearTransformers();
|
|
528
538
|
this.selectedIds = [];
|
|
539
|
+
this.marqueeRect?.destroy();
|
|
529
540
|
const pos = konvaStage.getRelativePointerPosition();
|
|
530
541
|
if (!pos) return;
|
|
531
542
|
this.marqueeStartPos = pos;
|
|
@@ -543,7 +554,7 @@ export class Selector {
|
|
|
543
554
|
});
|
|
544
555
|
this.getBackgroundLayer(konvaStage).add(this.marqueeRect);
|
|
545
556
|
});
|
|
546
|
-
konvaStage.on("mousemove touchmove", (e) => {
|
|
557
|
+
konvaStage.on(mouseOnly ? "mousemove" : "mousemove touchmove", (e) => {
|
|
547
558
|
if (!this.marqueeRect || !this.marqueeStartPos || !this.marqueeStage) return;
|
|
548
559
|
if (this.isStylusRejected(e.evt)) return;
|
|
549
560
|
if (this.getStylusModeEnabled?.() && e.evt) e.evt.preventDefault();
|
|
@@ -556,7 +567,7 @@ export class Selector {
|
|
|
556
567
|
this.marqueeRect.setAttrs({ x, y, width, height });
|
|
557
568
|
this.marqueeStage.batchDraw();
|
|
558
569
|
});
|
|
559
|
-
konvaStage.on("mouseup touchend", (e) => {
|
|
570
|
+
konvaStage.on(mouseOnly ? "mouseup" : "mouseup touchend", (e) => {
|
|
560
571
|
if (!this.marqueeRect || !this.marqueeStage) return;
|
|
561
572
|
if (this.isStylusRejected(e.evt)) return;
|
|
562
573
|
const selectionBox = this.marqueeRect.getClientRect();
|
|
@@ -567,6 +578,7 @@ export class Selector {
|
|
|
567
578
|
this.marqueeStage = null;
|
|
568
579
|
return;
|
|
569
580
|
}
|
|
581
|
+
if (mouseOnly) this.suppressStageClick = true;
|
|
570
582
|
const groups = this.getPageShapeGroups(this.marqueeStage);
|
|
571
583
|
const matchedIds = [];
|
|
572
584
|
groups.forEach((group) => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import Konva from 'konva';
|
|
2
2
|
import type { IRect } from 'konva/lib/types';
|
|
3
|
-
import { type IAnnotationStore, type IAnnotationStyle, type IAnnotationType, type PlaceFieldPayload } from './types.js';
|
|
3
|
+
import { type FormFieldType, type IAnnotationStore, type IAnnotationStyle, type IAnnotationType, type PlaceFieldPayload } from './types.js';
|
|
4
4
|
import { type IEditorFreeTextOptions } from './tools/free-text.js';
|
|
5
5
|
export interface KonvaCanvas {
|
|
6
6
|
pageNumber: number;
|
|
@@ -26,7 +26,7 @@ export interface PainterCallbacks {
|
|
|
26
26
|
/** Optional accessor returning the color to use for the form-field
|
|
27
27
|
* placement preview. Lets the active role color override the default
|
|
28
28
|
* blue without rebuilding the editor between placements. */
|
|
29
|
-
getFormFieldPreviewColor?: () => string | undefined;
|
|
29
|
+
getFormFieldPreviewColor?: (fieldType: FormFieldType) => string | undefined;
|
|
30
30
|
}
|
|
31
31
|
export declare class Painter {
|
|
32
32
|
private userName?;
|
|
@@ -128,6 +128,8 @@ export declare class Painter {
|
|
|
128
128
|
activateEraser(): void;
|
|
129
129
|
highlightRange(range: Range, annotation: IAnnotationType): void;
|
|
130
130
|
selectAnnotation(id: string): void;
|
|
131
|
+
/** Clears the current annotation selection without touching the store. */
|
|
132
|
+
clearSelection(): void;
|
|
131
133
|
initAnnotations(annotations: IAnnotationStore[]): Promise<void>;
|
|
132
134
|
update(id: string, updates: Partial<IAnnotationStore>): void;
|
|
133
135
|
delete(id: string, emit?: boolean): void;
|
|
@@ -624,6 +624,10 @@ export class Painter {
|
|
|
624
624
|
this.selector.select(id);
|
|
625
625
|
this.callbacks.setDefaultMode();
|
|
626
626
|
}
|
|
627
|
+
/** Clears the current annotation selection without touching the store. */
|
|
628
|
+
clearSelection() {
|
|
629
|
+
this.selector.delete();
|
|
630
|
+
}
|
|
627
631
|
async initAnnotations(annotations) {
|
|
628
632
|
annotations.forEach((annotation) => {
|
|
629
633
|
this.saveToStore(annotation, true);
|
|
@@ -6,11 +6,11 @@ export interface IEditorFormFieldOptions {
|
|
|
6
6
|
/** PDF-space page height, needed to flip Konva page-local Y → PDF Y. */
|
|
7
7
|
pageHeight: number;
|
|
8
8
|
onPlaceField: (payload: PlaceFieldPayload) => void;
|
|
9
|
-
/** Resolves to the
|
|
10
|
-
* the color the field will
|
|
11
|
-
* so the
|
|
12
|
-
* the editor. */
|
|
13
|
-
getPreviewColor?: () => string | undefined;
|
|
9
|
+
/** Resolves to the fieldColor preset for this field type so the
|
|
10
|
+
* placement preview matches the color the field will land with.
|
|
11
|
+
* Re-read on every drag start so the host can change defaults between
|
|
12
|
+
* placements without rebuilding the editor. */
|
|
13
|
+
getPreviewColor?: (fieldType: FormFieldType) => string | undefined;
|
|
14
14
|
}
|
|
15
15
|
export declare class EditorFormField extends Editor {
|
|
16
16
|
private fieldType;
|
|
@@ -38,7 +38,7 @@ export class EditorFormField extends Editor {
|
|
|
38
38
|
y: pos.y,
|
|
39
39
|
width: 0,
|
|
40
40
|
height: 0,
|
|
41
|
-
stroke: this.getPreviewColor?.() ?? "#1677ff",
|
|
41
|
+
stroke: this.getPreviewColor?.(this.fieldType) ?? "#1677ff",
|
|
42
42
|
strokeWidth: 1,
|
|
43
43
|
dash: [4, 4],
|
|
44
44
|
strokeScaleEnabled: false,
|
|
@@ -216,11 +216,17 @@ export interface FormFieldDefinition {
|
|
|
216
216
|
color?: number[];
|
|
217
217
|
backgroundColor?: number[];
|
|
218
218
|
textAlignment?: number;
|
|
219
|
-
/**
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
|
|
219
|
+
/** CSS color driving the field's editor chrome (outline, grip, hover
|
|
220
|
+
* accents). Host-owned; falls back to the default blue when unset.
|
|
221
|
+
* Not exported to the PDF — use `color`/`backgroundColor` for the
|
|
222
|
+
* widget's native appearance. Typically set per placement via
|
|
223
|
+
* `fieldDefaults` (e.g. one color per signer). */
|
|
224
|
+
fieldColor?: string;
|
|
225
|
+
/** Host-owned metadata bag. The editor carries it around untouched —
|
|
226
|
+
* it is never read, validated, or exported to the PDF. Must stay
|
|
227
|
+
* JSON-serializable so it survives the embed bridge. Replaced
|
|
228
|
+
* wholesale by an `updateFormField()` patch (no deep merge). */
|
|
229
|
+
meta?: Record<string, unknown>;
|
|
224
230
|
}
|
|
225
231
|
export interface FormFieldValue {
|
|
226
232
|
fieldId: string;
|
|
@@ -251,8 +257,13 @@ export interface AddFormFieldPayload {
|
|
|
251
257
|
* mirrored across them on edit. Radios with the same `fieldName` form
|
|
252
258
|
* one option group. */
|
|
253
259
|
fieldName?: string;
|
|
254
|
-
/**
|
|
255
|
-
|
|
260
|
+
/** CSS color for the field's editor chrome (see
|
|
261
|
+
* {@link FormFieldDefinition.fieldColor}). */
|
|
262
|
+
fieldColor?: string;
|
|
263
|
+
/** Host-owned metadata bag, carried untouched (see
|
|
264
|
+
* {@link FormFieldDefinition.meta}). Also usable as a preset in
|
|
265
|
+
* `fieldDefaults` so tool placements get their metadata atomically. */
|
|
266
|
+
meta?: Record<string, unknown>;
|
|
256
267
|
/** Default value for the field. Becomes the initial value too. */
|
|
257
268
|
defaultValue?: string;
|
|
258
269
|
readOnly?: boolean;
|
|
@@ -316,8 +327,6 @@ export interface SignatureFieldStatus {
|
|
|
316
327
|
fieldName: string;
|
|
317
328
|
pageNumber: number;
|
|
318
329
|
signed: boolean;
|
|
319
|
-
/** Present when the field is tagged with a host role. */
|
|
320
|
-
roleId?: string;
|
|
321
330
|
}
|
|
322
331
|
export interface DetectedShapeRect {
|
|
323
332
|
x: number;
|
|
@@ -6,8 +6,7 @@ import {
|
|
|
6
6
|
PDFDict,
|
|
7
7
|
PDFName,
|
|
8
8
|
PDFRawStream,
|
|
9
|
-
PDFString
|
|
10
|
-
StandardFonts
|
|
9
|
+
PDFString
|
|
11
10
|
} from "pdf-lib";
|
|
12
11
|
import { CHECKBOX_STYLE_TABLE } from "../checkbox-styles.js";
|
|
13
12
|
export async function writeFormFieldsToPdf(pdfDoc, fieldValues, placedDefinitions = []) {
|
|
@@ -265,7 +264,7 @@ async function createSignatureField(pdfDoc, page, def, fv) {
|
|
|
265
264
|
if (def.required) sig.setFlagTo(AcroFieldFlags.Required, true);
|
|
266
265
|
const v = fv?.value;
|
|
267
266
|
const signed = typeof v === "string" && v.startsWith("data:image");
|
|
268
|
-
const appearanceRef = signed ? await buildSignedSignatureAppearance(pdfDoc, v, width, height) :
|
|
267
|
+
const appearanceRef = signed ? await buildSignedSignatureAppearance(pdfDoc, v, width, height) : buildEmptySignatureAppearance(pdfDoc, width, height);
|
|
269
268
|
const widgetDict = context.obj({
|
|
270
269
|
Type: "Annot",
|
|
271
270
|
Subtype: "Widget",
|
|
@@ -282,43 +281,21 @@ async function createSignatureField(pdfDoc, page, def, fv) {
|
|
|
282
281
|
form.acroForm.addField(fieldRef);
|
|
283
282
|
page.node.addAnnot(widgetRef);
|
|
284
283
|
}
|
|
285
|
-
|
|
284
|
+
function buildEmptySignatureAppearance(pdfDoc, width, height) {
|
|
286
285
|
const { context } = pdfDoc;
|
|
287
|
-
const font = await pdfDoc.embedFont(StandardFonts.Helvetica);
|
|
288
|
-
const label = "Sign here";
|
|
289
|
-
const fontSize = Math.max(6, Math.min(12, height * 0.4));
|
|
290
|
-
const textWidth = font.widthOfTextAtSize(label, fontSize);
|
|
291
|
-
const textHeight = font.heightAtSize(fontSize);
|
|
292
|
-
const textX = (width - textWidth) / 2;
|
|
293
|
-
const textY = (height - textHeight) / 2;
|
|
294
|
-
const ops = [
|
|
295
|
-
"q",
|
|
296
|
-
"0.55 0.55 0.55 RG",
|
|
297
|
-
"0.75 w",
|
|
298
|
-
"[3 2] 0 d",
|
|
299
|
-
`0.5 0.5 ${(width - 1).toFixed(3)} ${(height - 1).toFixed(3)} re`,
|
|
300
|
-
"S",
|
|
301
|
-
"0.55 0.55 0.55 rg",
|
|
302
|
-
"BT",
|
|
303
|
-
`/F1 ${fontSize.toFixed(3)} Tf`,
|
|
304
|
-
`${textX.toFixed(3)} ${textY.toFixed(3)} Td`,
|
|
305
|
-
`(${label}) Tj`,
|
|
306
|
-
"ET",
|
|
307
|
-
"Q"
|
|
308
|
-
].join("\n");
|
|
309
286
|
const dict = context.obj({
|
|
310
287
|
Type: "XObject",
|
|
311
288
|
Subtype: "Form",
|
|
312
289
|
BBox: [0, 0, width, height],
|
|
313
290
|
Matrix: [1, 0, 0, 1, 0, 0],
|
|
314
|
-
Resources: {
|
|
291
|
+
Resources: {}
|
|
315
292
|
});
|
|
316
|
-
const stream = PDFRawStream.of(dict, new
|
|
293
|
+
const stream = PDFRawStream.of(dict, new Uint8Array(0));
|
|
317
294
|
return context.register(stream);
|
|
318
295
|
}
|
|
319
296
|
async function buildSignedSignatureAppearance(pdfDoc, dataUrl, width, height) {
|
|
320
297
|
const match = dataUrl.match(/^data:(image\/[a-zA-Z0-9.+-]+);base64,(.+)$/);
|
|
321
|
-
if (!match) return
|
|
298
|
+
if (!match) return buildEmptySignatureAppearance(pdfDoc, width, height);
|
|
322
299
|
const mimeType = match[1] ?? "";
|
|
323
300
|
const base64 = match[2] ?? "";
|
|
324
301
|
const image = mimeType.includes("jpeg") || mimeType.includes("jpg") ? await pdfDoc.embedJpg(base64) : await pdfDoc.embedPng(base64);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
.kviewer-text-layer{left:0;line-height:1;overflow:hidden;position:absolute;top:0;-webkit-user-select:none;-moz-user-select:none;user-select:none;z-index:1}.kviewer-text-layer--interactive{cursor:text;-webkit-user-select:text;-moz-user-select:text;user-select:text}.KViewer_is_painting.KViewer_painting_type_12 .kviewer-annotation-layer,.KViewer_is_painting.KViewer_painting_type_12 .kviewer-annotation-layer *,.KViewer_is_painting.KViewer_painting_type_13 .kviewer-annotation-layer,.KViewer_is_painting.KViewer_painting_type_13 .kviewer-annotation-layer *,.KViewer_is_painting.KViewer_painting_type_5 .kviewer-annotation-layer,.KViewer_is_painting.KViewer_painting_type_5 .kviewer-annotation-layer *,.KViewer_is_painting.KViewer_painting_type_6 .kviewer-annotation-layer,.KViewer_is_painting.KViewer_painting_type_6 .kviewer-annotation-layer *,.KViewer_is_painting.KViewer_painting_type_7 .kviewer-annotation-layer,.KViewer_is_painting.KViewer_painting_type_7 .kviewer-annotation-layer *,.KViewer_is_painting.KViewer_painting_type_8 .kviewer-annotation-layer,.KViewer_is_painting.KViewer_painting_type_8 .kviewer-annotation-layer *{cursor:crosshair!important}.KViewer_is_painting.KViewer_painting_type_4 .kviewer-annotation-layer,.KViewer_is_painting.KViewer_painting_type_4 .kviewer-annotation-layer *{cursor:text!important}.KViewer_is_painting.KViewer_painting_type_11 .kviewer-annotation-layer,.KViewer_is_painting.KViewer_painting_type_11 .kviewer-annotation-layer *{cursor:crosshair!important}.KViewer_selector_hover{cursor:pointer!important}.kviewer-form-layer{left:0;overflow:hidden;position:absolute;top:0}.kviewer-form-field{box-sizing:border-box;position:absolute}.kviewer-form-input{background-color:color-mix(in srgb,var(--kvw-field-color,#1677ff) 14%,transparent);border:1px solid transparent;box-sizing:border-box;color:#000;display:block;font-family:-apple-system,BlinkMacSystemFont,Helvetica Neue,Helvetica,Arial,sans-serif;font-weight:500;height:100%;line-height:normal;margin:0;outline:none;padding:1px 2px;resize:none;width:100%;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-rendering:optimizeLegibility;vertical-align:top}.kviewer-form-input:hover{background-color:color-mix(in srgb,var(--kvw-field-color,#1677ff) 22%,transparent);border-color:rgba(0,0,0,.2)}.kviewer-form-input:focus{background-color:color-mix(in srgb,var(--kvw-field-color,#1677ff) 28%,transparent);border-color:var(--kvw-field-color,var(--color-primary,#3b82f6));outline:2px solid var(--kvw-field-color,var(--color-primary,#3b82f6))}.kviewer-form-uselect{align-items:center;background-color:color-mix(in srgb,var(--kvw-field-color,#1677ff) 14%,transparent);border:1px solid transparent;border-radius:0;box-sizing:border-box;color:#000;cursor:pointer;display:flex;height:100%;line-height:1;padding:0 11px 0 2px;width:100%}.kviewer-form-uselect:hover{background-color:color-mix(in srgb,var(--kvw-field-color,#1677ff) 22%,transparent);border-color:rgba(0,0,0,.2)}.kviewer-form-uselect:focus-visible{background-color:color-mix(in srgb,var(--kvw-field-color,#1677ff) 28%,transparent);outline:2px solid var(--kvw-field-color,var(--color-primary,#3b82f6));outline-offset:-2px}.kviewer-form-uselect:disabled{cursor:default}.kviewer-form-listbox{overflow-y:auto;padding:0}.kviewer-form-listbox option{padding:1px 3px}.kviewer-form-editable-combo{height:100%;position:relative;width:100%}.kviewer-form-editable-combo input{height:100%;width:100%}.kviewer-form-checkbox,.kviewer-form-radio{align-items:center;background:#fff;box-sizing:border-box;cursor:pointer;display:flex;height:100%;justify-content:center;width:100%}.kviewer-form-checkbox input,.kviewer-form-radio input{accent-color:var(--kvw-field-color,var(--color-primary,#3b82f6));cursor:pointer;height:80%;margin:0;max-height:18px;max-width:18px;width:80%}.kviewer-form-checkbox--detected{background:transparent}.kviewer-form-checkbox--circle{border-radius:50%}.kviewer-form-checkbox__icon{color:#1a1a1a;height:65%;width:65%}.kviewer-form-checkbox--circle .kviewer-form-checkbox__icon{height:50%;width:50%}.kviewer-form-radio input{accent-color:var(--kvw-field-color,var(--color-primary,#3b82f6));cursor:pointer;height:80%;margin:0;max-height:18px;max-width:18px;width:80%}.kviewer-form-button{align-items:center;background:#c8c8c8;border:1px solid rgba(0,0,0,.35);box-sizing:border-box;color:#000;cursor:pointer;display:flex;font-family:inherit;font-weight:700;height:100%;justify-content:center;line-height:1;padding:0 6px;text-align:center;width:100%;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.kviewer-form-button:hover{background:#bdbdbd}.kviewer-form-button:active{background:#b0b0b0}.kviewer-form-signature{align-items:center;background:rgba(255,255,200,.15);border:1px dashed rgba(0,0,0,.2);cursor:pointer;display:flex;height:100%;justify-content:center;width:100%}.kviewer-form-signature:hover{background:color-mix(in srgb,var(--kvw-field-color,#3b82f6) 8%,transparent);border-color:var(--kvw-field-color,var(--color-primary,#3b82f6))}.kviewer-form-signature--filled{background:transparent;border-color:transparent;border-style:solid}.kviewer-form-signature__preview{max-height:100%;max-width:100%;-o-object-fit:contain;object-fit:contain}.kviewer-form-signature__placeholder{color:rgba(0,0,0,.4);font-size:10px;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.kviewer-form-signature--static{cursor:default}.kviewer-form-signature--static:hover{background:rgba(255,255,200,.15);border-color:rgba(0,0,0,.2)}.kviewer-form-signature__marker{align-items:center;display:inline-flex;gap:4px}.kviewer-form-signature__marker-icon{flex:0 0 auto;height:12px;width:12px}
|
|
1
|
+
.kviewer-text-layer{left:0;line-height:1;overflow:hidden;position:absolute;top:0;-webkit-user-select:none;-moz-user-select:none;user-select:none;z-index:1}.kviewer-text-layer--interactive{cursor:text;-webkit-user-select:text;-moz-user-select:text;user-select:text}.KViewer_is_painting.KViewer_painting_type_12 .kviewer-annotation-layer,.KViewer_is_painting.KViewer_painting_type_12 .kviewer-annotation-layer *,.KViewer_is_painting.KViewer_painting_type_13 .kviewer-annotation-layer,.KViewer_is_painting.KViewer_painting_type_13 .kviewer-annotation-layer *,.KViewer_is_painting.KViewer_painting_type_5 .kviewer-annotation-layer,.KViewer_is_painting.KViewer_painting_type_5 .kviewer-annotation-layer *,.KViewer_is_painting.KViewer_painting_type_6 .kviewer-annotation-layer,.KViewer_is_painting.KViewer_painting_type_6 .kviewer-annotation-layer *,.KViewer_is_painting.KViewer_painting_type_7 .kviewer-annotation-layer,.KViewer_is_painting.KViewer_painting_type_7 .kviewer-annotation-layer *,.KViewer_is_painting.KViewer_painting_type_8 .kviewer-annotation-layer,.KViewer_is_painting.KViewer_painting_type_8 .kviewer-annotation-layer *{cursor:crosshair!important}.KViewer_is_painting.KViewer_painting_type_4 .kviewer-annotation-layer,.KViewer_is_painting.KViewer_painting_type_4 .kviewer-annotation-layer *{cursor:text!important}.KViewer_is_painting.KViewer_painting_type_11 .kviewer-annotation-layer,.KViewer_is_painting.KViewer_painting_type_11 .kviewer-annotation-layer *{cursor:crosshair!important}.KViewer_selector_hover{cursor:pointer!important}.kviewer-form-layer{left:0;overflow:hidden;position:absolute;top:0}.kviewer-form-field{box-sizing:border-box;position:absolute}.kviewer-form-field--readonly.kviewer-form-field--placed{filter:grayscale(.6);opacity:.5}.kviewer-form-field--readonly .kviewer-form-signature,.kviewer-form-field--readonly .kviewer-form-signature:hover{background:rgba(255,255,200,.15);border-color:rgba(0,0,0,.2);cursor:not-allowed}.kviewer-form-input{background-color:color-mix(in srgb,var(--kvw-field-color,#1677ff) 14%,transparent);border:1px solid transparent;box-sizing:border-box;color:#000;display:block;font-family:-apple-system,BlinkMacSystemFont,Helvetica Neue,Helvetica,Arial,sans-serif;font-weight:500;height:100%;line-height:normal;margin:0;outline:none;padding:1px 2px;resize:none;width:100%;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-rendering:optimizeLegibility;vertical-align:top}.kviewer-form-input:hover{background-color:color-mix(in srgb,var(--kvw-field-color,#1677ff) 22%,transparent);border-color:rgba(0,0,0,.2)}.kviewer-form-input:focus{background-color:color-mix(in srgb,var(--kvw-field-color,#1677ff) 28%,transparent);border-color:var(--kvw-field-color,var(--color-primary,#3b82f6));outline:2px solid var(--kvw-field-color,var(--color-primary,#3b82f6))}.kviewer-form-uselect{align-items:center;background-color:color-mix(in srgb,var(--kvw-field-color,#1677ff) 14%,transparent);border:1px solid transparent;border-radius:0;box-sizing:border-box;color:#000;cursor:pointer;display:flex;height:100%;line-height:1;padding:0 11px 0 2px;width:100%}.kviewer-form-uselect:hover{background-color:color-mix(in srgb,var(--kvw-field-color,#1677ff) 22%,transparent);border-color:rgba(0,0,0,.2)}.kviewer-form-uselect:focus-visible{background-color:color-mix(in srgb,var(--kvw-field-color,#1677ff) 28%,transparent);outline:2px solid var(--kvw-field-color,var(--color-primary,#3b82f6));outline-offset:-2px}.kviewer-form-uselect:disabled{cursor:default}.kviewer-form-listbox{overflow-y:auto;padding:0}.kviewer-form-listbox option{padding:1px 3px}.kviewer-form-editable-combo{height:100%;position:relative;width:100%}.kviewer-form-editable-combo input{height:100%;width:100%}.kviewer-form-checkbox,.kviewer-form-radio{align-items:center;background:#fff;box-sizing:border-box;cursor:pointer;display:flex;height:100%;justify-content:center;width:100%}.kviewer-form-checkbox input,.kviewer-form-radio input{accent-color:var(--kvw-field-color,var(--color-primary,#3b82f6));cursor:pointer;height:80%;margin:0;max-height:18px;max-width:18px;width:80%}.kviewer-form-checkbox--detected{background:transparent}.kviewer-form-checkbox--circle{border-radius:50%}.kviewer-form-checkbox__icon{color:#1a1a1a;height:65%;width:65%}.kviewer-form-checkbox--circle .kviewer-form-checkbox__icon{height:50%;width:50%}.kviewer-form-radio input{accent-color:var(--kvw-field-color,var(--color-primary,#3b82f6));cursor:pointer;height:80%;margin:0;max-height:18px;max-width:18px;width:80%}.kviewer-form-button{align-items:center;background:#c8c8c8;border:1px solid rgba(0,0,0,.35);box-sizing:border-box;color:#000;cursor:pointer;display:flex;font-family:inherit;font-weight:700;height:100%;justify-content:center;line-height:1;padding:0 6px;text-align:center;width:100%;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.kviewer-form-button:hover{background:#bdbdbd}.kviewer-form-button:active{background:#b0b0b0}.kviewer-form-signature{align-items:center;background:rgba(255,255,200,.15);border:1px dashed rgba(0,0,0,.2);cursor:pointer;display:flex;height:100%;justify-content:center;width:100%}.kviewer-form-signature:hover{background:color-mix(in srgb,var(--kvw-field-color,#3b82f6) 8%,transparent);border-color:var(--kvw-field-color,var(--color-primary,#3b82f6))}.kviewer-form-signature--filled{background:transparent;border-color:transparent;border-style:solid}.kviewer-form-signature__preview{max-height:100%;max-width:100%;-o-object-fit:contain;object-fit:contain}.kviewer-form-signature__placeholder{color:rgba(0,0,0,.4);font-size:10px;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.kviewer-form-field--editable .kviewer-form-signature,.kviewer-form-field__body--locked .kviewer-form-signature,.kviewer-form-field__body--locked .kviewer-form-signature:hover{border-color:transparent}.kviewer-form-signature--static{cursor:default}.kviewer-form-signature--static:hover{background:rgba(255,255,200,.15);border-color:rgba(0,0,0,.2)}.kviewer-form-signature__marker{align-items:center;display:inline-flex;gap:4px}.kviewer-form-signature__marker-icon{flex:0 0 auto;height:12px;width:12px}
|
|
@@ -55,6 +55,9 @@
|
|
|
55
55
|
<UIcon name="i-lucide-pencil" class="text-sm text-muted" />
|
|
56
56
|
</button>
|
|
57
57
|
|
|
58
|
+
<!-- Host-supplied custom items (selectionItems prop) -->
|
|
59
|
+
<SelectionToolbarItems :items="customItems" :ctx="selectionCtx" />
|
|
60
|
+
|
|
58
61
|
<!-- Delete -->
|
|
59
62
|
<button
|
|
60
63
|
class="flex items-center justify-center w-7 h-7 rounded-md hover:bg-default cursor-pointer"
|
|
@@ -69,12 +72,20 @@
|
|
|
69
72
|
</template>
|
|
70
73
|
|
|
71
74
|
<script setup>
|
|
72
|
-
import { computed } from "vue";
|
|
75
|
+
import { computed, inject } from "vue";
|
|
73
76
|
import { useViewerState } from "../composables/useViewerState";
|
|
74
77
|
import { useKviewerI18n } from "../composables/useKviewerI18n";
|
|
75
78
|
import { defaultOptions, annotationDefinitions } from "../annotation/engine/config";
|
|
76
79
|
import { AnnotationType } from "../annotation/engine/types";
|
|
80
|
+
import { SELECTION_ITEMS_KEY } from "../selection-items";
|
|
81
|
+
import SelectionToolbarItems from "./SelectionToolbarItems.vue";
|
|
77
82
|
const state = useViewerState();
|
|
83
|
+
const customItems = inject(SELECTION_ITEMS_KEY, void 0);
|
|
84
|
+
const selectionCtx = computed(() => ({
|
|
85
|
+
kind: "annotation",
|
|
86
|
+
annotations: state.selectedAnnotations.value.length > 0 ? state.selectedAnnotations.value : state.selectedAnnotation.value ? [state.selectedAnnotation.value] : [],
|
|
87
|
+
field: null
|
|
88
|
+
}));
|
|
78
89
|
const { t } = useKviewerI18n();
|
|
79
90
|
const colors = defaultOptions.colors;
|
|
80
91
|
const isMultiSelect = computed(() => state.selectedAnnotations.value.length > 1);
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
<div
|
|
3
3
|
v-if="fields.length > 0"
|
|
4
4
|
class="kviewer-form-layer"
|
|
5
|
+
:class="{ 'kviewer-form-layer--interactive': props.pointerEvents === 'auto' }"
|
|
5
6
|
:style="{
|
|
6
7
|
position: 'absolute',
|
|
7
8
|
top: 0,
|
|
@@ -11,7 +12,6 @@
|
|
|
11
12
|
transform: `scale(${props.scale})`,
|
|
12
13
|
transformOrigin: '0 0',
|
|
13
14
|
zIndex: props.zIndex,
|
|
14
|
-
pointerEvents: props.pointerEvents,
|
|
15
15
|
// Force native form controls (inputs, checkboxes, radios, scrollbars)
|
|
16
16
|
// to render in light mode regardless of the user\'s OS theme — PDF
|
|
17
17
|
// pages have a white background, so dark-mode controls clash.
|
|
@@ -57,7 +57,8 @@ watch(() => state.formEditMode.value, (enabled) => {
|
|
|
57
57
|
});
|
|
58
58
|
watch(() => state.activeTool.value, (tool) => {
|
|
59
59
|
const isFormFieldTool = typeof tool === "number" && (tool === AnnotationType.FORM_TEXT || tool === AnnotationType.FORM_CHECKBOX || tool === AnnotationType.FORM_RADIO || tool === AnnotationType.FORM_SIGNATURE);
|
|
60
|
-
|
|
60
|
+
const keepsSelection = tool === "hand" || tool === AnnotationType.SELECT || isFormFieldTool;
|
|
61
|
+
if (!keepsSelection) formFields.selectPlacedField(null);
|
|
61
62
|
});
|
|
62
63
|
function onKeyDown(e) {
|
|
63
64
|
if (!selectedFieldId.value) return;
|
|
@@ -75,3 +76,7 @@ onBeforeUnmount(() => {
|
|
|
75
76
|
window.removeEventListener("keydown", onKeyDown);
|
|
76
77
|
});
|
|
77
78
|
</script>
|
|
79
|
+
|
|
80
|
+
<style>
|
|
81
|
+
.kviewer-form-layer{pointer-events:none}.kviewer-form-layer--interactive .kviewer-form-field{pointer-events:auto}.kviewer-form-layer:not(.kviewer-form-layer--interactive) .kviewer-form-field,.kviewer-form-layer:not(.kviewer-form-layer--interactive) .kviewer-placed-chrome,.kviewer-form-layer:not(.kviewer-form-layer--interactive) .kviewer-placed-toolbar{pointer-events:none}
|
|
82
|
+
</style>
|
|
@@ -63,6 +63,7 @@
|
|
|
63
63
|
height: cssHeight + 'px',
|
|
64
64
|
zIndex: 3,
|
|
65
65
|
pointerEvents: annotationPointerEvents,
|
|
66
|
+
cursor: annotationCursor,
|
|
66
67
|
transform: 'translateZ(0)',
|
|
67
68
|
overflow: 'hidden'
|
|
68
69
|
}"
|
|
@@ -134,9 +135,31 @@ const annotationPointerEvents = computed(() => {
|
|
|
134
135
|
if (state.readonly.value) return "none";
|
|
135
136
|
return isSearchTextInteractive.value ? "none" : isAnnotating.value ? "auto" : "none";
|
|
136
137
|
});
|
|
138
|
+
const annotationCursor = computed(() => {
|
|
139
|
+
const tool = state.activeTool.value;
|
|
140
|
+
if (tool === "marquee" || tool === "eraser") return "crosshair";
|
|
141
|
+
if (typeof tool !== "number") return "";
|
|
142
|
+
switch (tool) {
|
|
143
|
+
case AnnotationType.NONE:
|
|
144
|
+
case AnnotationType.SELECT:
|
|
145
|
+
return "";
|
|
146
|
+
// Stamp/signature hide the native cursor behind a floating image
|
|
147
|
+
// preview (body cursor 'none'); an element cursor here would
|
|
148
|
+
// override that and bring the arrow back on top of the preview.
|
|
149
|
+
case AnnotationType.STAMP:
|
|
150
|
+
case AnnotationType.SIGNATURE:
|
|
151
|
+
return "";
|
|
152
|
+
case AnnotationType.HIGHLIGHT:
|
|
153
|
+
case AnnotationType.STRIKEOUT:
|
|
154
|
+
case AnnotationType.UNDERLINE:
|
|
155
|
+
return "text";
|
|
156
|
+
default:
|
|
157
|
+
return "crosshair";
|
|
158
|
+
}
|
|
159
|
+
});
|
|
137
160
|
const isUsingAnnotationTool = computed(() => {
|
|
138
161
|
const tool = state.activeTool.value;
|
|
139
|
-
return tool === "marquee" || tool === "eraser" || typeof tool === "number" && tool !== AnnotationType.NONE;
|
|
162
|
+
return tool === "marquee" || tool === "eraser" || typeof tool === "number" && tool !== AnnotationType.NONE && tool !== AnnotationType.SELECT;
|
|
140
163
|
});
|
|
141
164
|
const formFieldPointerEvents = computed(() => {
|
|
142
165
|
if (state.readonly.value) return "none";
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ViewerSelectionContext, ViewerSelectionItem } from '../selection-items.js';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
items: ViewerSelectionItem[] | undefined;
|
|
4
|
+
ctx: ViewerSelectionContext;
|
|
5
|
+
};
|
|
6
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
7
|
+
declare const _default: typeof __VLS_export;
|
|
8
|
+
export default _default;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<template v-for="item in visibleItems" :key="item.key">
|
|
3
|
+
<button
|
|
4
|
+
v-if="item.type === 'button'"
|
|
5
|
+
class="flex items-center justify-center h-7 rounded-md hover:bg-default cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed"
|
|
6
|
+
:class="item.icon ? 'w-7' : 'px-2'"
|
|
7
|
+
:title="item.label"
|
|
8
|
+
:disabled="item.disabled"
|
|
9
|
+
:data-testid="`selection-item-${item.key}`"
|
|
10
|
+
@click="item.onSelect(props.ctx)"
|
|
11
|
+
>
|
|
12
|
+
<UIcon v-if="item.icon" :name="item.icon" class="text-sm text-muted" />
|
|
13
|
+
<span v-else class="text-xs text-muted whitespace-nowrap">{{ item.label }}</span>
|
|
14
|
+
</button>
|
|
15
|
+
<USelect
|
|
16
|
+
v-else-if="item.type === 'select'"
|
|
17
|
+
:model-value="selectValue(item)"
|
|
18
|
+
:items="item.items"
|
|
19
|
+
:placeholder="item.placeholder"
|
|
20
|
+
:disabled="item.disabled"
|
|
21
|
+
:aria-label="item.label"
|
|
22
|
+
:title="item.label"
|
|
23
|
+
class="w-24"
|
|
24
|
+
size="sm"
|
|
25
|
+
:data-testid="`selection-item-${item.key}`"
|
|
26
|
+
@update:model-value="(v) => item.onUpdate(String(v), props.ctx)"
|
|
27
|
+
/>
|
|
28
|
+
</template>
|
|
29
|
+
</template>
|
|
30
|
+
|
|
31
|
+
<script setup>
|
|
32
|
+
import { computed } from "vue";
|
|
33
|
+
const props = defineProps({
|
|
34
|
+
items: { type: null, required: true },
|
|
35
|
+
ctx: { type: Object, required: true }
|
|
36
|
+
});
|
|
37
|
+
const visibleItems = computed(
|
|
38
|
+
() => (props.items ?? []).filter((item) => item.visible?.(props.ctx) ?? true)
|
|
39
|
+
);
|
|
40
|
+
function selectValue(item) {
|
|
41
|
+
return typeof item.value === "function" ? item.value(props.ctx) : item.value;
|
|
42
|
+
}
|
|
43
|
+
</script>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ViewerSelectionContext, ViewerSelectionItem } from '../selection-items.js';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
items: ViewerSelectionItem[] | undefined;
|
|
4
|
+
ctx: ViewerSelectionContext;
|
|
5
|
+
};
|
|
6
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
7
|
+
declare const _default: typeof __VLS_export;
|
|
8
|
+
export default _default;
|
|
@@ -3,6 +3,7 @@ import type { AddFormFieldPayload, FormFieldDefinition, IAnnotationStore, Placed
|
|
|
3
3
|
import { type ExportPdfOptions } from '../annotation/pdf-export/export.js';
|
|
4
4
|
import type { ViewerMenuItem } from '../menu-items.js';
|
|
5
5
|
import type { ViewerToolEntry } from '../toolbar-tools.js';
|
|
6
|
+
import { type ViewerSelectionItem } from '../selection-items.js';
|
|
6
7
|
import { type KviewerMessageOverrides } from '../composables/useKviewerI18n.js';
|
|
7
8
|
import { type KviewerLocale } from '../i18n/messages.js';
|
|
8
9
|
type __VLS_Props = {
|
|
@@ -25,16 +26,6 @@ type __VLS_Props = {
|
|
|
25
26
|
* with selection chrome, can be moved/resized, and the property panel
|
|
26
27
|
* is shown. Supports v-model via `v-model:form-edit-mode`. */
|
|
27
28
|
formEditMode?: boolean;
|
|
28
|
-
/** Map of roleId → CSS color used to color-code form fields in
|
|
29
|
-
* form-edit mode. The editor stays unopinionated about role
|
|
30
|
-
* definitions: the host owns the role list, names, and color picker,
|
|
31
|
-
* and just hands a flat lookup table to the viewer. Unknown or
|
|
32
|
-
* missing ids fall back to the default blue. */
|
|
33
|
-
roleColors?: Record<string, string>;
|
|
34
|
-
/** Active role id new field placements are auto-tagged with. The host
|
|
35
|
-
* drives this — typically by binding it to a role picker in its own
|
|
36
|
-
* UI. Supports v-model via `v-model:active-role-id`. */
|
|
37
|
-
activeRoleId?: string | null;
|
|
38
29
|
/** When true, clicking an unsigned signature field opens kviewer's own
|
|
39
30
|
* signing flow (saved signature or draw modal). Off by default so hosts
|
|
40
31
|
* that own the signing process (e.g. an embedding e-sign app) don't get
|
|
@@ -63,6 +54,11 @@ type __VLS_Props = {
|
|
|
63
54
|
* toggles or actions without replacing the entire header. Ignored
|
|
64
55
|
* when the host supplies a custom `#header` slot. */
|
|
65
56
|
menuItems?: ViewerMenuItem[];
|
|
57
|
+
/** Custom controls appended to the floating selection popover — shown
|
|
58
|
+
* when an annotation or a placed form field (e.g. a signature field)
|
|
59
|
+
* is selected. Buttons and selects; every callback receives the
|
|
60
|
+
* current selection. See {@link ViewerSelectionItem}. */
|
|
61
|
+
selectionItems?: ViewerSelectionItem[];
|
|
66
62
|
/** Configures which toolbar tools render and in what order. Entries are
|
|
67
63
|
* built-in tool IDs (strings) or `{ type: 'slot', ... }` objects that
|
|
68
64
|
* inject a custom button via a `#tool-<name>` slot. When omitted, the
|
|
@@ -116,9 +112,11 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
116
112
|
* The new field is exported into the PDF on `exportPdf()` like any
|
|
117
113
|
* field placed via the placement tool. */
|
|
118
114
|
addFormField: (payload: AddFormFieldPayload) => FormFieldDefinition;
|
|
119
|
-
/** Patch a form field by id (rect, name, flags, type-specific props).
|
|
115
|
+
/** Patch a form field by id (rect, name, flags, type-specific props).
|
|
116
|
+
* Does not fire `field-updated` — that event is for user-driven edits. */
|
|
120
117
|
updateFormField: (id: string, patch: Partial<FormFieldDefinition>) => void;
|
|
121
|
-
/** Remove a form field by id (drops its value too).
|
|
118
|
+
/** Remove a form field by id (drops its value too). Does not fire
|
|
119
|
+
* `field-removed` — that event is for user-driven removals. */
|
|
122
120
|
removeFormField: (id: string) => void;
|
|
123
121
|
/** All form-field definitions (parsed, detected, and placed), flat. */
|
|
124
122
|
getFormFields: () => FormFieldDefinition[];
|
|
@@ -132,6 +130,14 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
132
130
|
* `field-placed` event, used by the embed bridge). Returns an
|
|
133
131
|
* unsubscribe function. */
|
|
134
132
|
onFieldPlaced: (cb: (field: FormFieldDefinition) => void) => (() => void);
|
|
133
|
+
/** Subscribe to user-driven field edits (imperative mirror of the
|
|
134
|
+
* `field-updated` event, used by the embed bridge). Returns an
|
|
135
|
+
* unsubscribe function. */
|
|
136
|
+
onFieldUpdated: (cb: (field: FormFieldDefinition, patch: Partial<FormFieldDefinition>) => void) => (() => void);
|
|
137
|
+
/** Subscribe to user-driven field removals (imperative mirror of the
|
|
138
|
+
* `field-removed` event, used by the embed bridge). Returns an
|
|
139
|
+
* unsubscribe function. */
|
|
140
|
+
onFieldRemoved: (cb: (field: FormFieldDefinition) => void) => (() => void);
|
|
135
141
|
/** True once every signature field in the document is signed — via a
|
|
136
142
|
* value applied in this session or a digital signature already in the
|
|
137
143
|
* source PDF. Vacuously true when the document has no signature
|
|
@@ -139,7 +145,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
139
145
|
* need "at least one". Use to gate e.g. envelope finalization. */
|
|
140
146
|
getSignedStatus: () => boolean;
|
|
141
147
|
/** Per-widget signed-state snapshot (id, fieldName, pageNumber,
|
|
142
|
-
* signed
|
|
148
|
+
* signed). Correlate to host data by `id`. */
|
|
143
149
|
getSignatureFieldStatus: () => SignatureFieldStatus[];
|
|
144
150
|
/** Subscribe to signed-status changes (imperative mirror of the
|
|
145
151
|
* `signed-status-changed` event, used by the embed bridge). Returns
|
|
@@ -165,8 +171,9 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
165
171
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
166
172
|
"all-pages-read": () => any;
|
|
167
173
|
"field-placed": (field: FormFieldDefinition) => any;
|
|
174
|
+
"field-updated": (field: FormFieldDefinition, patch: Partial<FormFieldDefinition>) => any;
|
|
175
|
+
"field-removed": (field: FormFieldDefinition) => any;
|
|
168
176
|
"update:formEditMode": (value: boolean) => any;
|
|
169
|
-
"update:activeRoleId": (value: string | null) => any;
|
|
170
177
|
"update:viewedPages": (pages: number[]) => any;
|
|
171
178
|
"signed-status-changed": (payload: {
|
|
172
179
|
allSigned: boolean;
|
|
@@ -175,8 +182,9 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
175
182
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
176
183
|
"onAll-pages-read"?: (() => any) | undefined;
|
|
177
184
|
"onField-placed"?: ((field: FormFieldDefinition) => any) | undefined;
|
|
185
|
+
"onField-updated"?: ((field: FormFieldDefinition, patch: Partial<FormFieldDefinition>) => any) | undefined;
|
|
186
|
+
"onField-removed"?: ((field: FormFieldDefinition) => any) | undefined;
|
|
178
187
|
"onUpdate:formEditMode"?: ((value: boolean) => any) | undefined;
|
|
179
|
-
"onUpdate:activeRoleId"?: ((value: string | null) => any) | undefined;
|
|
180
188
|
"onUpdate:viewedPages"?: ((pages: number[]) => any) | undefined;
|
|
181
189
|
"onSigned-status-changed"?: ((payload: {
|
|
182
190
|
allSigned: boolean;
|
|
@@ -198,10 +206,9 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
198
206
|
signatureHandlers: SignatureHandlers;
|
|
199
207
|
viewMode: ViewMode;
|
|
200
208
|
shapeDetection: boolean;
|
|
201
|
-
roleColors: Record<string, string>;
|
|
202
|
-
activeRoleId: string | null;
|
|
203
209
|
editablePlacedFields: boolean;
|
|
204
210
|
fieldDefaults: PlacedFieldDefaults;
|
|
211
|
+
selectionItems: ViewerSelectionItem[];
|
|
205
212
|
messages: KviewerMessageOverrides;
|
|
206
213
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
207
214
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|