kviewer 0.3.3 → 0.3.4
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 +2 -0
- package/dist/runtime/annotation/engine/painter.js +4 -0
- 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 +7 -0
- package/dist/runtime/components/Viewer.vue +24 -1
- package/dist/runtime/components/Viewer.vue.d.ts +7 -0
- 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 +4 -0
- package/dist/runtime/components/ViewerTabs.vue +2 -0
- package/dist/runtime/components/ViewerTabs.vue.d.ts +4 -0
- package/dist/runtime/components/form-fields/FormFieldWrapper.vue +15 -2
- package/dist/runtime/components/form-fields/PlacedFieldChrome.vue +9 -9
- 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/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) => {
|
|
@@ -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,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-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__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 = {
|
|
@@ -63,6 +64,11 @@ type __VLS_Props = {
|
|
|
63
64
|
* toggles or actions without replacing the entire header. Ignored
|
|
64
65
|
* when the host supplies a custom `#header` slot. */
|
|
65
66
|
menuItems?: ViewerMenuItem[];
|
|
67
|
+
/** Custom controls appended to the floating selection popover — shown
|
|
68
|
+
* when an annotation or a placed form field (e.g. a signature field)
|
|
69
|
+
* is selected. Buttons and selects; every callback receives the
|
|
70
|
+
* current selection. See {@link ViewerSelectionItem}. */
|
|
71
|
+
selectionItems?: ViewerSelectionItem[];
|
|
66
72
|
/** Configures which toolbar tools render and in what order. Entries are
|
|
67
73
|
* built-in tool IDs (strings) or `{ type: 'slot', ... }` objects that
|
|
68
74
|
* inject a custom button via a `#tool-<name>` slot. When omitted, the
|
|
@@ -202,6 +208,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
202
208
|
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>;
|
|
@@ -193,7 +193,8 @@ import {
|
|
|
193
193
|
triggerRef,
|
|
194
194
|
onMounted,
|
|
195
195
|
onBeforeUnmount,
|
|
196
|
-
useSlots
|
|
196
|
+
useSlots,
|
|
197
|
+
provide
|
|
197
198
|
} from "vue";
|
|
198
199
|
import { useRuntimeConfig } from "#imports";
|
|
199
200
|
import ViewerBar from "./ViewerBar.vue";
|
|
@@ -226,6 +227,7 @@ import { isIPad } from "../annotation/engine/input-device";
|
|
|
226
227
|
import { useInertiaPanzoom } from "../composables/useInertiaPanzoom";
|
|
227
228
|
import { createScriptingManager } from "../composables/useScriptingManager";
|
|
228
229
|
import { createScriptingBridge } from "../composables/useScriptingBridge";
|
|
230
|
+
import { SELECTION_ITEMS_KEY } from "../selection-items";
|
|
229
231
|
import { provideKviewerI18n } from "../composables/useKviewerI18n";
|
|
230
232
|
import { KVIEWER_LOCALES } from "../i18n/messages";
|
|
231
233
|
const props = defineProps({
|
|
@@ -248,10 +250,12 @@ const props = defineProps({
|
|
|
248
250
|
fieldDefaults: { type: Object, required: false, default: void 0 },
|
|
249
251
|
scripting: { type: Boolean, required: false, default: false },
|
|
250
252
|
menuItems: { type: Array, required: false, default: void 0 },
|
|
253
|
+
selectionItems: { type: Array, required: false, default: void 0 },
|
|
251
254
|
tools: { type: Array, required: false, default: void 0 },
|
|
252
255
|
locale: { type: null, required: false, default: void 0 },
|
|
253
256
|
messages: { type: Object, required: false, default: void 0 }
|
|
254
257
|
});
|
|
258
|
+
provide(SELECTION_ITEMS_KEY, computed(() => props.selectionItems));
|
|
255
259
|
const slots = useSlots();
|
|
256
260
|
const toolSlotNames = computed(
|
|
257
261
|
() => Object.keys(slots).filter((name) => name.startsWith("tool-"))
|
|
@@ -316,6 +320,25 @@ watch(
|
|
|
316
320
|
},
|
|
317
321
|
{ immediate: true }
|
|
318
322
|
);
|
|
323
|
+
watch(
|
|
324
|
+
() => formFieldsState.selectedPlacedFieldId.value,
|
|
325
|
+
(id) => {
|
|
326
|
+
if (!id) return;
|
|
327
|
+
viewerState.painter.value?.clearSelection();
|
|
328
|
+
viewerState.selectedAnnotation.value = null;
|
|
329
|
+
viewerState.selectedAnnotations.value = [];
|
|
330
|
+
viewerState.selectionRect.value = null;
|
|
331
|
+
}
|
|
332
|
+
);
|
|
333
|
+
watch(
|
|
334
|
+
[
|
|
335
|
+
() => viewerState.selectedAnnotation.value,
|
|
336
|
+
() => viewerState.selectedAnnotations.value
|
|
337
|
+
],
|
|
338
|
+
([single, multi]) => {
|
|
339
|
+
if (single || multi.length > 0) formFieldsState.selectPlacedField(null);
|
|
340
|
+
}
|
|
341
|
+
);
|
|
319
342
|
const signatureStatus = computed(() => formFieldsState.getSignatureFieldStatus());
|
|
320
343
|
const signatureStatusKey = (fields) => fields.map((s) => `${s.id}|${s.fieldName}|${s.signed ? 1 : 0}`).join(";");
|
|
321
344
|
watch(signatureStatus, (next, prev) => {
|
|
@@ -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 = {
|
|
@@ -63,6 +64,11 @@ type __VLS_Props = {
|
|
|
63
64
|
* toggles or actions without replacing the entire header. Ignored
|
|
64
65
|
* when the host supplies a custom `#header` slot. */
|
|
65
66
|
menuItems?: ViewerMenuItem[];
|
|
67
|
+
/** Custom controls appended to the floating selection popover — shown
|
|
68
|
+
* when an annotation or a placed form field (e.g. a signature field)
|
|
69
|
+
* is selected. Buttons and selects; every callback receives the
|
|
70
|
+
* current selection. See {@link ViewerSelectionItem}. */
|
|
71
|
+
selectionItems?: ViewerSelectionItem[];
|
|
66
72
|
/** Configures which toolbar tools render and in what order. Entries are
|
|
67
73
|
* built-in tool IDs (strings) or `{ type: 'slot', ... }` objects that
|
|
68
74
|
* inject a custom button via a `#tool-<name>` slot. When omitted, the
|
|
@@ -202,6 +208,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
202
208
|
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>;
|
|
@@ -8,15 +8,15 @@ type __VLS_Props = {
|
|
|
8
8
|
* omitted, the default toolbar is rendered. See {@link ViewerToolEntry}. */
|
|
9
9
|
tools?: ViewerToolEntry[];
|
|
10
10
|
};
|
|
11
|
-
declare var
|
|
11
|
+
declare var __VLS_104: string, __VLS_105: {
|
|
12
12
|
state: import("../composables/useViewerState.js").ViewerState;
|
|
13
|
-
},
|
|
13
|
+
}, __VLS_149: string, __VLS_150: {
|
|
14
14
|
state: import("../composables/useViewerState.js").ViewerState;
|
|
15
15
|
};
|
|
16
16
|
type __VLS_Slots = {} & {
|
|
17
|
-
[K in NonNullable<typeof
|
|
17
|
+
[K in NonNullable<typeof __VLS_104>]?: (props: typeof __VLS_105) => any;
|
|
18
18
|
} & {
|
|
19
|
-
[K in NonNullable<typeof
|
|
19
|
+
[K in NonNullable<typeof __VLS_149>]?: (props: typeof __VLS_150) => any;
|
|
20
20
|
};
|
|
21
21
|
declare const __VLS_base: 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>;
|
|
22
22
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
@@ -11,11 +11,12 @@
|
|
|
11
11
|
|
|
12
12
|
<ZoomControls />
|
|
13
13
|
|
|
14
|
+
<!-- No MarqueeTool button: hand mode covers mouse rubber-band
|
|
15
|
+
selection; `marquee` stays available via the `tools` prop. -->
|
|
14
16
|
<template v-if="!state.readonly.value">
|
|
15
17
|
<USeparator orientation="vertical" class="h-5" :ui="{ border: 'border-accented' }" />
|
|
16
18
|
|
|
17
19
|
<HandTool />
|
|
18
|
-
<MarqueeTool />
|
|
19
20
|
</template>
|
|
20
21
|
|
|
21
22
|
<div class="ml-auto flex items-center gap-2">
|
|
@@ -8,15 +8,15 @@ type __VLS_Props = {
|
|
|
8
8
|
* omitted, the default toolbar is rendered. See {@link ViewerToolEntry}. */
|
|
9
9
|
tools?: ViewerToolEntry[];
|
|
10
10
|
};
|
|
11
|
-
declare var
|
|
11
|
+
declare var __VLS_104: string, __VLS_105: {
|
|
12
12
|
state: import("../composables/useViewerState.js").ViewerState;
|
|
13
|
-
},
|
|
13
|
+
}, __VLS_149: string, __VLS_150: {
|
|
14
14
|
state: import("../composables/useViewerState.js").ViewerState;
|
|
15
15
|
};
|
|
16
16
|
type __VLS_Slots = {} & {
|
|
17
|
-
[K in NonNullable<typeof
|
|
17
|
+
[K in NonNullable<typeof __VLS_104>]?: (props: typeof __VLS_105) => any;
|
|
18
18
|
} & {
|
|
19
|
-
[K in NonNullable<typeof
|
|
19
|
+
[K in NonNullable<typeof __VLS_149>]?: (props: typeof __VLS_150) => any;
|
|
20
20
|
};
|
|
21
21
|
declare const __VLS_base: 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>;
|
|
22
22
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
@@ -2,6 +2,7 @@ import { type ComponentPublicInstance } from 'vue';
|
|
|
2
2
|
import type { ViewMode } from '../composables/useViewerState.js';
|
|
3
3
|
import type { FormFieldDefinition, PlacedFieldDefaults, SignatureFieldStatus, StampDefinition, SignatureHandlers } from '../annotation/engine/types.js';
|
|
4
4
|
import type { ViewerMenuItem } from '../menu-items.js';
|
|
5
|
+
import type { ViewerSelectionItem } from '../selection-items.js';
|
|
5
6
|
import type { ViewerToolEntry } from '../toolbar-tools.js';
|
|
6
7
|
export interface ViewerTabItem {
|
|
7
8
|
/** Unique identifier for the tab. */
|
|
@@ -63,6 +64,8 @@ type __VLS_Props = {
|
|
|
63
64
|
scripting?: boolean;
|
|
64
65
|
/** Forwarded to every Viewer instance — see KViewer's `menuItems`. */
|
|
65
66
|
menuItems?: ViewerMenuItem[];
|
|
67
|
+
/** Forwarded to every Viewer instance — see KViewer's `selectionItems`. */
|
|
68
|
+
selectionItems?: ViewerSelectionItem[];
|
|
66
69
|
/** Forwarded to every Viewer instance — see KViewer's `tools`. */
|
|
67
70
|
tools?: ViewerToolEntry[];
|
|
68
71
|
/** Forwarded to every Viewer instance — see KViewer's `clickToSign`. */
|
|
@@ -436,6 +439,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
436
439
|
activeRoleId: string | null;
|
|
437
440
|
editablePlacedFields: boolean;
|
|
438
441
|
fieldDefaults: PlacedFieldDefaults;
|
|
442
|
+
selectionItems: ViewerSelectionItem[];
|
|
439
443
|
defaultActiveTab: string;
|
|
440
444
|
minTabs: number;
|
|
441
445
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
@@ -61,6 +61,7 @@
|
|
|
61
61
|
:shape-detection="activeTab.shapeDetection ?? shapeDetection"
|
|
62
62
|
:scripting="activeTab.scripting ?? scripting"
|
|
63
63
|
:menu-items="menuItems"
|
|
64
|
+
:selection-items="selectionItems"
|
|
64
65
|
:tools="tools"
|
|
65
66
|
:role-colors="roleColors"
|
|
66
67
|
:active-role-id="activeRoleId"
|
|
@@ -112,6 +113,7 @@ const props = defineProps({
|
|
|
112
113
|
activeRoleId: { type: [String, null], required: false, default: void 0 },
|
|
113
114
|
scripting: { type: Boolean, required: false, default: false },
|
|
114
115
|
menuItems: { type: Array, required: false, default: void 0 },
|
|
116
|
+
selectionItems: { type: Array, required: false, default: void 0 },
|
|
115
117
|
tools: { type: Array, required: false, default: void 0 },
|
|
116
118
|
clickToSign: { type: Boolean, required: false, default: false },
|
|
117
119
|
editablePlacedFields: { type: Boolean, required: false, default: false },
|
|
@@ -2,6 +2,7 @@ import { type ComponentPublicInstance } from 'vue';
|
|
|
2
2
|
import type { ViewMode } from '../composables/useViewerState.js';
|
|
3
3
|
import type { FormFieldDefinition, PlacedFieldDefaults, SignatureFieldStatus, StampDefinition, SignatureHandlers } from '../annotation/engine/types.js';
|
|
4
4
|
import type { ViewerMenuItem } from '../menu-items.js';
|
|
5
|
+
import type { ViewerSelectionItem } from '../selection-items.js';
|
|
5
6
|
import type { ViewerToolEntry } from '../toolbar-tools.js';
|
|
6
7
|
export interface ViewerTabItem {
|
|
7
8
|
/** Unique identifier for the tab. */
|
|
@@ -63,6 +64,8 @@ type __VLS_Props = {
|
|
|
63
64
|
scripting?: boolean;
|
|
64
65
|
/** Forwarded to every Viewer instance — see KViewer's `menuItems`. */
|
|
65
66
|
menuItems?: ViewerMenuItem[];
|
|
67
|
+
/** Forwarded to every Viewer instance — see KViewer's `selectionItems`. */
|
|
68
|
+
selectionItems?: ViewerSelectionItem[];
|
|
66
69
|
/** Forwarded to every Viewer instance — see KViewer's `tools`. */
|
|
67
70
|
tools?: ViewerToolEntry[];
|
|
68
71
|
/** Forwarded to every Viewer instance — see KViewer's `clickToSign`. */
|
|
@@ -436,6 +439,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
436
439
|
activeRoleId: string | null;
|
|
437
440
|
editablePlacedFields: boolean;
|
|
438
441
|
fieldDefaults: PlacedFieldDefaults;
|
|
442
|
+
selectionItems: ViewerSelectionItem[];
|
|
439
443
|
defaultActiveTab: string;
|
|
440
444
|
minTabs: number;
|
|
441
445
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
@@ -6,7 +6,10 @@
|
|
|
6
6
|
'kviewer-form-field--placed': isPlaced,
|
|
7
7
|
'kviewer-form-field--selected': props.selected
|
|
8
8
|
}"
|
|
9
|
-
:style="[positionStyle, {
|
|
9
|
+
:style="[positionStyle, {
|
|
10
|
+
'--kvw-field-color': chromeColor,
|
|
11
|
+
'--kvw-handle-zoom': handleZoom
|
|
12
|
+
}]"
|
|
10
13
|
:data-element-id="props.field.id"
|
|
11
14
|
>
|
|
12
15
|
<div
|
|
@@ -29,6 +32,11 @@
|
|
|
29
32
|
:page-height="props.pageTransform.height"
|
|
30
33
|
@select="emit('select')"
|
|
31
34
|
/>
|
|
35
|
+
|
|
36
|
+
<PlacedFieldToolbar
|
|
37
|
+
v-if="showChrome && props.selected && !state.readonly.value"
|
|
38
|
+
:field="props.field"
|
|
39
|
+
/>
|
|
32
40
|
</div>
|
|
33
41
|
</template>
|
|
34
42
|
|
|
@@ -44,6 +52,7 @@ import FormDropdown from "./FormDropdown.vue";
|
|
|
44
52
|
import FormSignatureField from "./FormSignatureField.vue";
|
|
45
53
|
import FormButton from "./FormButton.vue";
|
|
46
54
|
import PlacedFieldChrome from "./PlacedFieldChrome.vue";
|
|
55
|
+
import PlacedFieldToolbar from "./PlacedFieldToolbar.vue";
|
|
47
56
|
const props = defineProps({
|
|
48
57
|
field: { type: Object, required: true },
|
|
49
58
|
pageTransform: { type: Object, required: true },
|
|
@@ -58,6 +67,10 @@ const isFormEditMode = computed(() => state.formEditMode.value);
|
|
|
58
67
|
const showChrome = computed(
|
|
59
68
|
() => isFormEditMode.value || formFields.editablePlacedFields.value && isPlaced.value
|
|
60
69
|
);
|
|
70
|
+
const handleZoom = computed(() => {
|
|
71
|
+
const totalZoom = (props.scale || 1) * (state.visualZoom.value || 1);
|
|
72
|
+
return (1 / totalZoom).toFixed(3);
|
|
73
|
+
});
|
|
61
74
|
const chromeColor = computed(() => {
|
|
62
75
|
const id = props.field.roleId;
|
|
63
76
|
if (!id) return "#1677ff";
|
|
@@ -77,5 +90,5 @@ const positionStyle = computed(() => {
|
|
|
77
90
|
</script>
|
|
78
91
|
|
|
79
92
|
<style>
|
|
80
|
-
.kviewer-form-field__body{height:100%;width:100%}.kviewer-form-field__body--locked{pointer-events:none}
|
|
93
|
+
.kviewer-form-field__body{height:100%;width:100%}.kviewer-form-field__body--locked{pointer-events:none}
|
|
81
94
|
</style>
|
|
@@ -6,10 +6,16 @@
|
|
|
6
6
|
ref="rootEl"
|
|
7
7
|
class="kviewer-placed-chrome"
|
|
8
8
|
:class="{ 'kviewer-placed-chrome--selected': selected }"
|
|
9
|
-
:style="{ '--kvw-handle-zoom': handleZoom }"
|
|
10
9
|
@pointerdown.stop="onChromePointerDown"
|
|
11
10
|
@click.stop
|
|
12
11
|
>
|
|
12
|
+
<div
|
|
13
|
+
class="kviewer-placed-chrome__outline"
|
|
14
|
+
:class="{
|
|
15
|
+
'kviewer-placed-chrome__outline--placed': field.origin === 'placed',
|
|
16
|
+
'kviewer-placed-chrome__outline--selected': selected
|
|
17
|
+
}"
|
|
18
|
+
/>
|
|
13
19
|
<div
|
|
14
20
|
v-if="selected"
|
|
15
21
|
class="kviewer-placed-chrome__grip"
|
|
@@ -33,15 +39,9 @@
|
|
|
33
39
|
</template>
|
|
34
40
|
|
|
35
41
|
<script setup>
|
|
36
|
-
import {
|
|
42
|
+
import { ref } from "vue";
|
|
37
43
|
import { useFormFields } from "../../composables/useFormFields";
|
|
38
|
-
import { useViewerState } from "../../composables/useViewerState";
|
|
39
44
|
const rootEl = ref(null);
|
|
40
|
-
const viewerState = useViewerState();
|
|
41
|
-
const handleZoom = computed(() => {
|
|
42
|
-
const totalZoom = (props.scale || 1) * (viewerState.visualZoom.value || 1);
|
|
43
|
-
return Math.max(0.4, 1 / totalZoom).toFixed(3);
|
|
44
|
-
});
|
|
45
45
|
const props = defineProps({
|
|
46
46
|
field: { type: Object, required: true },
|
|
47
47
|
selected: { type: Boolean, required: true },
|
|
@@ -127,5 +127,5 @@ function onHandlePointerDown(e, name) {
|
|
|
127
127
|
</script>
|
|
128
128
|
|
|
129
129
|
<style>
|
|
130
|
-
.kviewer-placed-chrome{cursor:move;inset:0;pointer-events:auto;position:absolute;touch-action:none}.kviewer-placed-
|
|
130
|
+
.kviewer-placed-chrome{cursor:move;inset:0;pointer-events:auto;position:absolute;touch-action:none}.kviewer-placed-chrome__outline{border:1px dashed color-mix(in srgb,var(--kvw-field-color,#1677ff) 45%,transparent);box-sizing:border-box;height:calc(100%/var(--kvw-handle-zoom, 1));left:0;pointer-events:none;position:absolute;top:0;transform:scale(var(--kvw-handle-zoom,1));transform-origin:0 0;width:calc(100%/var(--kvw-handle-zoom, 1))}.kviewer-placed-chrome__outline--placed{border-color:color-mix(in srgb,var(--kvw-field-color,#1677ff) 75%,transparent)}.kviewer-placed-chrome__outline--selected{border:1.5px dashed var(--kvw-field-color,#1677ff)}.kviewer-placed-chrome__grip{align-items:center;background:color-mix(in srgb,var(--kvw-field-color,#1677ff) 15%,transparent);border:1px solid color-mix(in srgb,var(--kvw-field-color,#1677ff) 40%,transparent);border-radius:4px;bottom:calc(100% + 4px*var(--kvw-handle-zoom, 1));box-sizing:border-box;color:var(--kvw-field-color,#1677ff);cursor:move;display:flex;font-size:12px;gap:4px;height:22px;left:0;line-height:1;overflow:hidden;padding:0 8px 0 3px;pointer-events:auto;position:absolute;touch-action:none;transform:scale(var(--kvw-handle-zoom,1));transform-origin:0 100%;-webkit-user-select:none;-moz-user-select:none;user-select:none;width:calc(100%/var(--kvw-handle-zoom, 1))}.kviewer-placed-chrome__grip-dots{color:var(--kvw-field-color,#1677ff);flex:0 0 auto;height:13px;width:13px}.kviewer-placed-chrome__grip-label{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kviewer-placed-chrome__handle{background:#fff;border:1px solid var(--kvw-field-color,#1677ff);border-radius:2px;height:8px;pointer-events:auto;position:absolute;touch-action:none;transform:scale(var(--kvw-handle-zoom,1));width:8px}.kviewer-placed-chrome__handle:before{content:"";inset:-8px;position:absolute}.kviewer-placed-chrome__handle--n{left:50%;top:-4px;transform:translateX(-50%) scale(var(--kvw-handle-zoom,1))}.kviewer-placed-chrome__handle--s{bottom:-4px;left:50%;transform:translateX(-50%) scale(var(--kvw-handle-zoom,1))}.kviewer-placed-chrome__handle--e{right:-4px;top:50%;transform:translateY(-50%) scale(var(--kvw-handle-zoom,1))}.kviewer-placed-chrome__handle--w{left:-4px;top:50%;transform:translateY(-50%) scale(var(--kvw-handle-zoom,1))}.kviewer-placed-chrome__handle--ne{right:-4px;top:-4px}.kviewer-placed-chrome__handle--nw{left:-4px;top:-4px}.kviewer-placed-chrome__handle--se{bottom:-4px;right:-4px}.kviewer-placed-chrome__handle--sw{bottom:-4px;left:-4px}
|
|
131
131
|
</style>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { FormFieldDefinition } from '../../annotation/engine/types.js';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
field: FormFieldDefinition;
|
|
4
|
+
};
|
|
5
|
+
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>;
|
|
6
|
+
declare const _default: typeof __VLS_export;
|
|
7
|
+
export default _default;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<!-- Same pill as AnnotationToolbar, but anchored in the page DOM below
|
|
3
|
+
the selected field so it follows pan/zoom for free (the Konva
|
|
4
|
+
selection popover is viewport-fixed because the stage has no DOM
|
|
5
|
+
anchor to attach to). Stops pointer events so a click here never
|
|
6
|
+
reaches page-level deselect handlers. -->
|
|
7
|
+
<div
|
|
8
|
+
class="kviewer-placed-toolbar"
|
|
9
|
+
@pointerdown.stop
|
|
10
|
+
@click.stop
|
|
11
|
+
>
|
|
12
|
+
<div class="flex items-center gap-0.5 bg-elevated rounded-lg shadow-lg border border-default p-1">
|
|
13
|
+
<SelectionToolbarItems :items="customItems" :ctx="selectionCtx" />
|
|
14
|
+
|
|
15
|
+
<button
|
|
16
|
+
class="flex items-center justify-center w-7 h-7 rounded-md hover:bg-default cursor-pointer"
|
|
17
|
+
:title="t('deleteField')"
|
|
18
|
+
data-testid="placed-field-delete"
|
|
19
|
+
@click="onDelete"
|
|
20
|
+
>
|
|
21
|
+
<UIcon name="i-lucide-trash-2" class="text-sm text-error" />
|
|
22
|
+
</button>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
</template>
|
|
26
|
+
|
|
27
|
+
<script setup>
|
|
28
|
+
import { computed, inject } from "vue";
|
|
29
|
+
import { useFormFields } from "../../composables/useFormFields";
|
|
30
|
+
import { useKviewerI18n } from "../../composables/useKviewerI18n";
|
|
31
|
+
import { SELECTION_ITEMS_KEY } from "../../selection-items";
|
|
32
|
+
import SelectionToolbarItems from "../SelectionToolbarItems.vue";
|
|
33
|
+
const props = defineProps({
|
|
34
|
+
field: { type: Object, required: true }
|
|
35
|
+
});
|
|
36
|
+
const formFields = useFormFields();
|
|
37
|
+
const { t } = useKviewerI18n();
|
|
38
|
+
const customItems = inject(SELECTION_ITEMS_KEY, void 0);
|
|
39
|
+
const selectionCtx = computed(() => ({
|
|
40
|
+
kind: "field",
|
|
41
|
+
annotations: [],
|
|
42
|
+
field: props.field
|
|
43
|
+
}));
|
|
44
|
+
function onDelete() {
|
|
45
|
+
formFields.removePlacedField(props.field.id);
|
|
46
|
+
}
|
|
47
|
+
</script>
|
|
48
|
+
|
|
49
|
+
<style>
|
|
50
|
+
.kviewer-placed-toolbar{left:50%;pointer-events:auto;position:absolute;top:calc(100% + 8px*var(--kvw-handle-zoom, 1));transform:translateX(-50%) scale(var(--kvw-handle-zoom,1));transform-origin:50% 0;width:-moz-max-content;width:max-content;z-index:1}
|
|
51
|
+
</style>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { FormFieldDefinition } from '../../annotation/engine/types.js';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
field: FormFieldDefinition;
|
|
4
|
+
};
|
|
5
|
+
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>;
|
|
6
|
+
declare const _default: typeof __VLS_export;
|
|
7
|
+
export default _default;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export type { ExportPdfOptions } from './annotation/pdf-export/export.js';
|
|
2
2
|
export type { ViewerTabItem, AddTabOptions } from './components/ViewerTabs.vue.js';
|
|
3
|
-
export type { AddFormFieldPayload, CheckboxStyle, FormFieldDefinition, FormFieldOrigin, FormFieldType, FormFieldValue, PlacedFieldDefaultProps, PlacedFieldDefaults, SignatureData, SignatureFieldStatus, SignatureHandlers, } from './annotation/engine/types.js';
|
|
3
|
+
export type { AddFormFieldPayload, CheckboxStyle, FormFieldDefinition, FormFieldOrigin, FormFieldType, FormFieldValue, IAnnotationStore, PlacedFieldDefaultProps, PlacedFieldDefaults, SignatureData, SignatureFieldStatus, SignatureHandlers, } from './annotation/engine/types.js';
|
|
4
4
|
export type { ViewMode } from './composables/viewMode.js';
|
|
5
5
|
export type { ViewerState, ActiveTool } from './composables/useViewerState.js';
|
|
6
6
|
export type { KViewerApi, KViewerHostApi, KViewerEmbedBridgeOptions, } from './embed/bridge-host.js';
|
|
@@ -10,4 +10,5 @@ export { KViewerEmbedClient } from './embed/bridge-client.js';
|
|
|
10
10
|
export type { EventName as KViewerEmbedEventName, EventPayloads as KViewerEmbedEventPayloads, MethodName as KViewerEmbedMethodName, } from './embed/protocol.js';
|
|
11
11
|
export { KVIEWER_EMBED_PROTOCOL_VERSION, UNSUPPORTED_METHOD_ERROR_NAME as KVIEWER_EMBED_UNSUPPORTED_METHOD_ERROR_NAME, } from './embed/protocol.js';
|
|
12
12
|
export type { ViewerMenuItem, ViewerMenuButtonItem, ViewerMenuCheckboxItem, ViewerMenuSeparatorItem, } from './menu-items.js';
|
|
13
|
+
export type { ViewerSelectionItem, ViewerSelectionButtonItem, ViewerSelectionSelectItem, ViewerSelectionContext, } from './selection-items.js';
|
|
13
14
|
export type { ViewerToolId, ViewerTopToolId, ViewerBottomToolId, ViewerLayoutToolId, ViewerToolEntry, ViewerToolSlot, } from './toolbar-tools.js';
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { InjectionKey, Ref } from 'vue';
|
|
2
|
+
import type { FormFieldDefinition, IAnnotationStore } from './annotation/engine/types.js';
|
|
3
|
+
/** What the floating selection popover is currently attached to. Passed to
|
|
4
|
+
* every custom-item callback so a single item list can serve both the
|
|
5
|
+
* Konva annotation selection and the HTML placed-field selection. */
|
|
6
|
+
export interface ViewerSelectionContext {
|
|
7
|
+
/** `'annotation'` = Konva-drawn annotation(s); `'field'` = a user-placed
|
|
8
|
+
* HTML form field (e.g. a signature field). */
|
|
9
|
+
kind: 'annotation' | 'field';
|
|
10
|
+
/** The selected annotation(s): one entry for a single selection, several
|
|
11
|
+
* for a marquee multi-select, empty when `kind` is `'field'`. */
|
|
12
|
+
annotations: IAnnotationStore[];
|
|
13
|
+
/** The selected placed form field. Null when `kind` is `'annotation'`. */
|
|
14
|
+
field: FormFieldDefinition | null;
|
|
15
|
+
}
|
|
16
|
+
/** A custom control appended to the floating selection popover shown when
|
|
17
|
+
* an annotation or a placed form field is selected. Pass an array of
|
|
18
|
+
* these to `<KViewer :selection-items="...">` (or `<KViewerTabs>`).
|
|
19
|
+
*
|
|
20
|
+
* Mirrors the `menuItems` discriminated-union pattern; scoped to the
|
|
21
|
+
* controls a compact floating toolbar can carry. */
|
|
22
|
+
export type ViewerSelectionItem = ViewerSelectionButtonItem | ViewerSelectionSelectItem;
|
|
23
|
+
export interface ViewerSelectionButtonItem {
|
|
24
|
+
type: 'button';
|
|
25
|
+
/** Stable key for Vue list rendering. Keep unique within the array. */
|
|
26
|
+
key: string;
|
|
27
|
+
/** Tooltip / accessible label. Rendered as text when no icon is set. */
|
|
28
|
+
label: string;
|
|
29
|
+
/** Lucide-style icon name. When set, the button is icon-only. */
|
|
30
|
+
icon?: string;
|
|
31
|
+
/** Fired on click with the current selection. */
|
|
32
|
+
onSelect: (ctx: ViewerSelectionContext) => void;
|
|
33
|
+
/** Disables the item without removing it. */
|
|
34
|
+
disabled?: boolean;
|
|
35
|
+
/** Show the item only for selections this predicate accepts. Omit to
|
|
36
|
+
* show it for both annotation and field selections. */
|
|
37
|
+
visible?: (ctx: ViewerSelectionContext) => boolean;
|
|
38
|
+
}
|
|
39
|
+
export interface ViewerSelectionSelectItem {
|
|
40
|
+
type: 'select';
|
|
41
|
+
key: string;
|
|
42
|
+
/** Accessible label / tooltip for the select. */
|
|
43
|
+
label?: string;
|
|
44
|
+
/** Options shown in the dropdown. */
|
|
45
|
+
items: {
|
|
46
|
+
label: string;
|
|
47
|
+
value: string;
|
|
48
|
+
}[];
|
|
49
|
+
/** Current value — a plain (reactive) string, or a getter deriving it
|
|
50
|
+
* from the current selection. */
|
|
51
|
+
value?: string | ((ctx: ViewerSelectionContext) => string | undefined);
|
|
52
|
+
placeholder?: string;
|
|
53
|
+
/** Fired with the chosen option value and the current selection. */
|
|
54
|
+
onUpdate: (value: string, ctx: ViewerSelectionContext) => void;
|
|
55
|
+
disabled?: boolean;
|
|
56
|
+
visible?: (ctx: ViewerSelectionContext) => boolean;
|
|
57
|
+
}
|
|
58
|
+
/** Provided by KViewer so both selection popovers (the Konva annotation
|
|
59
|
+
* toolbar and the placed-field toolbar, which lives deep inside the page
|
|
60
|
+
* DOM layer) can read the host's items without prop-drilling. */
|
|
61
|
+
export declare const SELECTION_ITEMS_KEY: InjectionKey<Ref<ViewerSelectionItem[] | undefined>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const SELECTION_ITEMS_KEY = Symbol("kviewer-selection-items");
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import type { InjectionKey, Ref } from 'vue';
|
|
2
2
|
import { AnnotationType } from './annotation/engine/types.js';
|
|
3
3
|
/** Built-in tools that live in the top navigation row. Each maps to a
|
|
4
|
-
* whole self-contained sub-component. `hand` / `marquee` are readonly-gated.
|
|
4
|
+
* whole self-contained sub-component. `hand` / `marquee` are readonly-gated.
|
|
5
|
+
* `marquee` has no button in the default toolbar — hand mode rubber-band
|
|
6
|
+
* selects on empty-space mouse drags; the dedicated mode remains for hosts
|
|
7
|
+
* that want touch-capable multi-select via the `tools` prop. */
|
|
5
8
|
export type ViewerTopToolId = 'menu' | 'pageSettings' | 'zoom' | 'hand' | 'marquee' | 'pageInfo' | 'search';
|
|
6
9
|
/** Built-in tools that live in the second (annotation) row, controlled
|
|
7
10
|
* per individual button. IDs match the annotation `name` in
|
package/dist/types.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { type ActiveTool, type AddFormFieldPayload, type CheckboxStyle, type ExportPdfOptions, type FormFieldDefinition, type FormFieldOrigin, type FormFieldType, type FormFieldValue, type KVIEWER_EMBED_PROTOCOL_VERSION, type KVIEWER_EMBED_UNSUPPORTED_METHOD_ERROR_NAME, type KViewerApi, type KViewerEmbedBridgeOptions, type KViewerEmbedClient, type KViewerEmbedClientOptions, type KViewerEmbedEventName, type KViewerEmbedEventPayloads, type KViewerEmbedMethodName, type KViewerHostApi, type PlacedFieldDefaults, type SignatureData, type SignatureFieldStatus, type SignatureHandlers, type ViewMode, type ViewerBottomToolId, type ViewerLayoutToolId, type ViewerMenuButtonItem, type ViewerMenuCheckboxItem, type ViewerMenuItem, type ViewerMenuSeparatorItem, type ViewerState, type ViewerToolEntry, type ViewerToolId, type ViewerToolSlot, type ViewerTopToolId, type useKViewerEmbedBridge } from '../dist/runtime/public-types.js'
|
|
1
|
+
export { type ActiveTool, type AddFormFieldPayload, type CheckboxStyle, type ExportPdfOptions, type FormFieldDefinition, type FormFieldOrigin, type FormFieldType, type FormFieldValue, type IAnnotationStore, type KVIEWER_EMBED_PROTOCOL_VERSION, type KVIEWER_EMBED_UNSUPPORTED_METHOD_ERROR_NAME, type KViewerApi, type KViewerEmbedBridgeOptions, type KViewerEmbedClient, type KViewerEmbedClientOptions, type KViewerEmbedEventName, type KViewerEmbedEventPayloads, type KViewerEmbedMethodName, type KViewerHostApi, type PlacedFieldDefaults, type SignatureData, type SignatureFieldStatus, type SignatureHandlers, type ViewMode, type ViewerBottomToolId, type ViewerLayoutToolId, type ViewerMenuButtonItem, type ViewerMenuCheckboxItem, type ViewerMenuItem, type ViewerMenuSeparatorItem, type ViewerSelectionButtonItem, type ViewerSelectionContext, type ViewerSelectionItem, type ViewerSelectionSelectItem, type ViewerState, type ViewerToolEntry, type ViewerToolId, type ViewerToolSlot, type ViewerTopToolId, type useKViewerEmbedBridge } from '../dist/runtime/public-types.js'
|
|
2
2
|
|
|
3
3
|
export { default } from './module.mjs'
|
|
4
4
|
|