kviewer 0.0.11 → 0.1.0
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 +9 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +6 -1
- package/dist/runtime/annotation/engine/painter.d.ts +8 -1
- package/dist/runtime/annotation/engine/painter.js +11 -6
- package/dist/runtime/annotation/engine/tools/form-field.d.ts +6 -0
- package/dist/runtime/annotation/engine/tools/form-field.js +3 -1
- package/dist/runtime/annotation/engine/types.d.ts +64 -0
- package/dist/runtime/annotation/pdf-export/export-form-fields.js +102 -52
- package/dist/runtime/assets/kviewer.css +1 -1
- package/dist/runtime/components/PdfPage.vue +6 -1
- package/dist/runtime/components/Viewer.d.vue.ts +25 -1
- package/dist/runtime/components/Viewer.vue +42 -2
- package/dist/runtime/components/Viewer.vue.d.ts +25 -1
- package/dist/runtime/components/ViewerTabs.d.vue.ts +15 -6
- package/dist/runtime/components/ViewerTabs.vue +7 -2
- package/dist/runtime/components/ViewerTabs.vue.d.ts +15 -6
- package/dist/runtime/components/form-fields/FormFieldWrapper.vue +9 -2
- package/dist/runtime/components/form-fields/PlacedFieldChrome.vue +1 -1
- package/dist/runtime/composables/useAnnotationEngine.d.ts +1 -0
- package/dist/runtime/composables/useAnnotationEngine.js +2 -1
- package/dist/runtime/composables/useFormFields.d.ts +22 -3
- package/dist/runtime/composables/useFormFields.js +79 -1
- package/dist/runtime/composables/useInertiaPanzoom.js +1 -0
- package/dist/runtime/public-types.d.ts +1 -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 { ExportPdfOptions, SignatureData, SignatureHandlers, ViewMode } from '../dist/runtime/public-types.js';
|
|
2
|
+
export { AddFormFieldPayload, CheckboxStyle, ExportPdfOptions, FormFieldDefinition, FormFieldOrigin, FormFieldType, FormFieldValue, SignatureData, SignatureHandlers, ViewMode } from '../dist/runtime/public-types.js';
|
|
3
3
|
|
|
4
4
|
interface ModuleOptions {
|
|
5
5
|
/**
|
|
@@ -7,6 +7,14 @@ interface ModuleOptions {
|
|
|
7
7
|
* @defaultValue `K`
|
|
8
8
|
*/
|
|
9
9
|
prefix?: string;
|
|
10
|
+
/**
|
|
11
|
+
* Minimum pixel ratio used to rasterize PDF pages. The canvas is rendered
|
|
12
|
+
* at `cssSize * max(devicePixelRatio, minRenderPixelRatio)` and downscaled
|
|
13
|
+
* by CSS, producing a supersampled, sharper image on non-HiDPI displays.
|
|
14
|
+
* Higher values cost ~quadratically more memory and CPU.
|
|
15
|
+
* @defaultValue `2`
|
|
16
|
+
*/
|
|
17
|
+
minRenderPixelRatio?: number;
|
|
10
18
|
}
|
|
11
19
|
|
|
12
20
|
declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -9,10 +9,15 @@ const module$1 = defineNuxtModule({
|
|
|
9
9
|
},
|
|
10
10
|
// Default configuration options of the Nuxt module
|
|
11
11
|
defaults: {
|
|
12
|
-
prefix: "K"
|
|
12
|
+
prefix: "K",
|
|
13
|
+
minRenderPixelRatio: 2
|
|
13
14
|
},
|
|
14
15
|
setup(options, nuxt) {
|
|
15
16
|
const { resolve } = createResolver(import.meta.url);
|
|
17
|
+
nuxt.options.runtimeConfig.public.kviewer = {
|
|
18
|
+
...nuxt.options.runtimeConfig.public.kviewer,
|
|
19
|
+
minRenderPixelRatio: options.minRenderPixelRatio ?? 2
|
|
20
|
+
};
|
|
16
21
|
nuxt.options.css.push("pdfjs-dist/web/pdf_viewer.css");
|
|
17
22
|
nuxt.options.css.push(resolve("./runtime/assets/kviewer.css"));
|
|
18
23
|
addPlugin(resolve("./runtime/plugin"));
|
|
@@ -23,6 +23,10 @@ export interface PainterCallbacks {
|
|
|
23
23
|
onRequestTextInput: IEditorFreeTextOptions['onRequestTextInput'];
|
|
24
24
|
onRequestDeleteConfirm?: (id: string) => Promise<boolean>;
|
|
25
25
|
onPlaceField?: (payload: PlaceFieldPayload) => void;
|
|
26
|
+
/** Optional accessor returning the color to use for the form-field
|
|
27
|
+
* placement preview. Lets the active role color override the default
|
|
28
|
+
* blue without rebuilding the editor between placements. */
|
|
29
|
+
getFormFieldPreviewColor?: () => string | undefined;
|
|
26
30
|
}
|
|
27
31
|
export declare class Painter {
|
|
28
32
|
private userName;
|
|
@@ -54,7 +58,10 @@ export declare class Painter {
|
|
|
54
58
|
private updateStore;
|
|
55
59
|
/**
|
|
56
60
|
* Convert a Konva stage-relative rect to viewport-relative coordinates
|
|
57
|
-
* by combining with the stage container's DOM position.
|
|
61
|
+
* by combining with the stage container's DOM position. `getBoundingClientRect`
|
|
62
|
+
* already includes ancestor CSS transforms (panzoom), but `stageRect` is in
|
|
63
|
+
* pre-transform canvas pixels — scale it by the same factor so the offset
|
|
64
|
+
* matches the on-screen size.
|
|
58
65
|
*/
|
|
59
66
|
private toViewportRect;
|
|
60
67
|
private parseStampData;
|
|
@@ -182,17 +182,21 @@ export class Painter {
|
|
|
182
182
|
}
|
|
183
183
|
/**
|
|
184
184
|
* Convert a Konva stage-relative rect to viewport-relative coordinates
|
|
185
|
-
* by combining with the stage container's DOM position.
|
|
185
|
+
* by combining with the stage container's DOM position. `getBoundingClientRect`
|
|
186
|
+
* already includes ancestor CSS transforms (panzoom), but `stageRect` is in
|
|
187
|
+
* pre-transform canvas pixels — scale it by the same factor so the offset
|
|
188
|
+
* matches the on-screen size.
|
|
186
189
|
*/
|
|
187
190
|
toViewportRect(pageNumber, stageRect) {
|
|
188
191
|
const canvas = this.konvaCanvasStore.get(pageNumber);
|
|
189
192
|
if (!canvas) return stageRect;
|
|
190
193
|
const containerBounds = canvas.wrapper.getBoundingClientRect();
|
|
194
|
+
const visualScale = canvas.wrapper.offsetWidth > 0 ? containerBounds.width / canvas.wrapper.offsetWidth : 1;
|
|
191
195
|
return {
|
|
192
|
-
x: containerBounds.left + stageRect.x,
|
|
193
|
-
y: containerBounds.top + stageRect.y,
|
|
194
|
-
width: stageRect.width,
|
|
195
|
-
height: stageRect.height
|
|
196
|
+
x: containerBounds.left + stageRect.x * visualScale,
|
|
197
|
+
y: containerBounds.top + stageRect.y * visualScale,
|
|
198
|
+
width: stageRect.width * visualScale,
|
|
199
|
+
height: stageRect.height * visualScale
|
|
196
200
|
};
|
|
197
201
|
}
|
|
198
202
|
parseStampData() {
|
|
@@ -332,7 +336,8 @@ export class Painter {
|
|
|
332
336
|
editor = new EditorFormField(editorOpts, {
|
|
333
337
|
fieldType,
|
|
334
338
|
pageHeight,
|
|
335
|
-
onPlaceField
|
|
339
|
+
onPlaceField,
|
|
340
|
+
getPreviewColor: this.callbacks.getFormFieldPreviewColor
|
|
336
341
|
});
|
|
337
342
|
break;
|
|
338
343
|
}
|
|
@@ -6,11 +6,17 @@ 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 active role color so the placement preview matches
|
|
10
|
+
* the color the field will be tagged with. Re-read on every drag start
|
|
11
|
+
* so the user can switch roles between placements without rebuilding
|
|
12
|
+
* the editor. */
|
|
13
|
+
getPreviewColor?: () => string | undefined;
|
|
9
14
|
}
|
|
10
15
|
export declare class EditorFormField extends Editor {
|
|
11
16
|
private fieldType;
|
|
12
17
|
private pageHeight;
|
|
13
18
|
private onPlaceField;
|
|
19
|
+
private getPreviewColor?;
|
|
14
20
|
private preview;
|
|
15
21
|
private vertex;
|
|
16
22
|
constructor(editorOptions: IEditorOptions, opts: IEditorFormFieldOptions);
|
|
@@ -11,6 +11,7 @@ export class EditorFormField extends Editor {
|
|
|
11
11
|
fieldType;
|
|
12
12
|
pageHeight;
|
|
13
13
|
onPlaceField;
|
|
14
|
+
getPreviewColor;
|
|
14
15
|
preview = null;
|
|
15
16
|
vertex = { x: 0, y: 0 };
|
|
16
17
|
constructor(editorOptions, opts) {
|
|
@@ -21,6 +22,7 @@ export class EditorFormField extends Editor {
|
|
|
21
22
|
this.fieldType = opts.fieldType;
|
|
22
23
|
this.pageHeight = opts.pageHeight;
|
|
23
24
|
this.onPlaceField = opts.onPlaceField;
|
|
25
|
+
this.getPreviewColor = opts.getPreviewColor;
|
|
24
26
|
}
|
|
25
27
|
setPageHeight(pageHeight) {
|
|
26
28
|
this.pageHeight = pageHeight;
|
|
@@ -36,7 +38,7 @@ export class EditorFormField extends Editor {
|
|
|
36
38
|
y: pos.y,
|
|
37
39
|
width: 0,
|
|
38
40
|
height: 0,
|
|
39
|
-
stroke: "#1677ff",
|
|
41
|
+
stroke: this.getPreviewColor?.() ?? "#1677ff",
|
|
40
42
|
strokeWidth: 1,
|
|
41
43
|
dash: [4, 4],
|
|
42
44
|
strokeScaleEnabled: false,
|
|
@@ -209,6 +209,11 @@ export interface FormFieldDefinition {
|
|
|
209
209
|
color?: number[];
|
|
210
210
|
backgroundColor?: number[];
|
|
211
211
|
textAlignment?: number;
|
|
212
|
+
/** Opaque role tag for color-coding in form-edit mode. The editor
|
|
213
|
+
* doesn't own role definitions — the host (integrating app) maps
|
|
214
|
+
* roleId → color via the `roleColors` prop on KViewer. Not exported
|
|
215
|
+
* to the PDF. */
|
|
216
|
+
roleId?: string;
|
|
212
217
|
}
|
|
213
218
|
export interface FormFieldValue {
|
|
214
219
|
fieldId: string;
|
|
@@ -223,6 +228,65 @@ export interface PlaceFieldPayload {
|
|
|
223
228
|
/** PDF-space rect [x1, y1, x2, y2], bottom-left origin. */
|
|
224
229
|
rectPdf: [number, number, number, number];
|
|
225
230
|
}
|
|
231
|
+
/**
|
|
232
|
+
* Public payload for adding a form field programmatically. All optional
|
|
233
|
+
* fields fall back to sensible defaults — only `pageNumber`, `fieldType`,
|
|
234
|
+
* and `rect` are required. The created field has `origin: 'placed'` so it
|
|
235
|
+
* is exported into the PDF as a new widget.
|
|
236
|
+
*/
|
|
237
|
+
export interface AddFormFieldPayload {
|
|
238
|
+
pageNumber: number;
|
|
239
|
+
fieldType: FormFieldType;
|
|
240
|
+
/** PDF-space rect [x1, y1, x2, y2], bottom-left origin. */
|
|
241
|
+
rect: [number, number, number, number];
|
|
242
|
+
/** Field name. Auto-generated when omitted. Multiple widgets that share
|
|
243
|
+
* a `fieldName` (and `fieldType`) act as one PDF field — values are
|
|
244
|
+
* mirrored across them on edit. Radios with the same `fieldName` form
|
|
245
|
+
* one option group. */
|
|
246
|
+
fieldName?: string;
|
|
247
|
+
/** Opaque role tag for color-coding in form-edit mode. */
|
|
248
|
+
roleId?: string;
|
|
249
|
+
/** Default value for the field. Becomes the initial value too. */
|
|
250
|
+
defaultValue?: string;
|
|
251
|
+
readOnly?: boolean;
|
|
252
|
+
required?: boolean;
|
|
253
|
+
/** checkbox: glyph style. Defaults to 'check'. */
|
|
254
|
+
checkboxStyle?: CheckboxStyle;
|
|
255
|
+
/** signature: placeholder text on the unsigned widget. */
|
|
256
|
+
promptText?: string;
|
|
257
|
+
/** signature: lock other fields once signed. */
|
|
258
|
+
lockAction?: 'all' | 'include' | 'exclude';
|
|
259
|
+
/** signature: field names referenced by lockAction = 'include' / 'exclude'. */
|
|
260
|
+
lockFieldNames?: string[];
|
|
261
|
+
/** dropdown: editable combo box (true) vs list box (false). Defaults to true. */
|
|
262
|
+
combo?: boolean;
|
|
263
|
+
/** dropdown / listbox: allow multiple selections. */
|
|
264
|
+
multiSelect?: boolean;
|
|
265
|
+
/** dropdown / listbox: choices. */
|
|
266
|
+
options?: {
|
|
267
|
+
displayValue: string;
|
|
268
|
+
exportValue: string;
|
|
269
|
+
}[];
|
|
270
|
+
/** text: render as multiple lines. */
|
|
271
|
+
multiLine?: boolean;
|
|
272
|
+
/** text: render as a password field. */
|
|
273
|
+
password?: boolean;
|
|
274
|
+
/** text: render as evenly-spaced character cells (requires `maxLen`). */
|
|
275
|
+
comb?: boolean;
|
|
276
|
+
/** text: maximum input length. */
|
|
277
|
+
maxLen?: number;
|
|
278
|
+
/** radio: this widget's option value within its group. Auto-generated
|
|
279
|
+
* when omitted (e.g. "Option_1", "Option_2", …). */
|
|
280
|
+
buttonValue?: string;
|
|
281
|
+
fontSize?: number;
|
|
282
|
+
fontName?: string;
|
|
283
|
+
/** RGB color tuple in 0–1 (PDF native). */
|
|
284
|
+
color?: number[];
|
|
285
|
+
/** RGB background color tuple in 0–1 (PDF native). */
|
|
286
|
+
backgroundColor?: number[];
|
|
287
|
+
/** 0 = left, 1 = center, 2 = right. */
|
|
288
|
+
textAlignment?: number;
|
|
289
|
+
}
|
|
226
290
|
export interface DetectedShapeRect {
|
|
227
291
|
x: number;
|
|
228
292
|
y: number;
|
|
@@ -1,4 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
AcroFieldFlags,
|
|
3
|
+
AnnotationFlags,
|
|
4
|
+
PDFAcroSignature,
|
|
5
|
+
PDFBool,
|
|
6
|
+
PDFDict,
|
|
7
|
+
PDFName,
|
|
8
|
+
PDFRawStream,
|
|
9
|
+
PDFString,
|
|
10
|
+
StandardFonts
|
|
11
|
+
} from "pdf-lib";
|
|
2
12
|
import { CHECKBOX_STYLE_TABLE } from "../checkbox-styles.js";
|
|
3
13
|
export async function writeFormFieldsToPdf(pdfDoc, fieldValues, placedDefinitions = []) {
|
|
4
14
|
const valueByFieldId = new Map(fieldValues.map((v) => [v.fieldId, v]));
|
|
@@ -206,13 +216,7 @@ async function createWidgetForDefinition(pdfDoc, def, fv, radioGroupCache) {
|
|
|
206
216
|
break;
|
|
207
217
|
}
|
|
208
218
|
case "signature": {
|
|
209
|
-
|
|
210
|
-
const signed = typeof v === "string" && v.startsWith("data:image");
|
|
211
|
-
if (signed) {
|
|
212
|
-
await drawSignatureImage(pdfDoc, page, v, box);
|
|
213
|
-
} else {
|
|
214
|
-
await drawSignaturePlaceholder(pdfDoc, page, box);
|
|
215
|
-
}
|
|
219
|
+
await createSignatureField(pdfDoc, page, def, fv);
|
|
216
220
|
break;
|
|
217
221
|
}
|
|
218
222
|
case "dropdown": {
|
|
@@ -244,55 +248,101 @@ function applyFlags(field, def) {
|
|
|
244
248
|
if (def.readOnly) field.enableReadOnly();
|
|
245
249
|
if (def.required) field.enableRequired();
|
|
246
250
|
}
|
|
247
|
-
async function
|
|
251
|
+
async function createSignatureField(pdfDoc, page, def, fv) {
|
|
252
|
+
const { context } = pdfDoc;
|
|
253
|
+
const form = pdfDoc.getForm();
|
|
254
|
+
const [x1, y1, x2, y2] = def.rect;
|
|
255
|
+
const width = Math.max(1, x2 - x1);
|
|
256
|
+
const height = Math.max(1, y2 - y1);
|
|
257
|
+
const fieldDict = context.obj({
|
|
258
|
+
FT: "Sig",
|
|
259
|
+
Kids: []
|
|
260
|
+
});
|
|
261
|
+
const fieldRef = context.register(fieldDict);
|
|
262
|
+
const sig = PDFAcroSignature.fromDict(fieldDict, fieldRef);
|
|
263
|
+
sig.setPartialName(def.fieldName);
|
|
264
|
+
if (def.readOnly) sig.setFlagTo(AcroFieldFlags.ReadOnly, true);
|
|
265
|
+
if (def.required) sig.setFlagTo(AcroFieldFlags.Required, true);
|
|
266
|
+
const v = fv?.value;
|
|
267
|
+
const signed = typeof v === "string" && v.startsWith("data:image");
|
|
268
|
+
const appearanceRef = signed ? await buildSignedSignatureAppearance(pdfDoc, v, width, height) : await buildSignaturePlaceholderAppearance(pdfDoc, width, height);
|
|
269
|
+
const widgetDict = context.obj({
|
|
270
|
+
Type: "Annot",
|
|
271
|
+
Subtype: "Widget",
|
|
272
|
+
Rect: [x1, y1, x2, y2],
|
|
273
|
+
P: page.ref,
|
|
274
|
+
Parent: fieldRef,
|
|
275
|
+
F: 1 << AnnotationFlags.Print,
|
|
276
|
+
AP: { N: appearanceRef }
|
|
277
|
+
});
|
|
278
|
+
const widgetRef = context.register(widgetDict);
|
|
279
|
+
sig.addWidget(widgetRef);
|
|
280
|
+
form.acroForm.addField(fieldRef);
|
|
281
|
+
page.node.addAnnot(widgetRef);
|
|
282
|
+
}
|
|
283
|
+
async function buildSignaturePlaceholderAppearance(pdfDoc, width, height) {
|
|
284
|
+
const { context } = pdfDoc;
|
|
285
|
+
const font = await pdfDoc.embedFont(StandardFonts.Helvetica);
|
|
286
|
+
const label = "Sign here";
|
|
287
|
+
const fontSize = Math.max(6, Math.min(12, height * 0.4));
|
|
288
|
+
const textWidth = font.widthOfTextAtSize(label, fontSize);
|
|
289
|
+
const textHeight = font.heightAtSize(fontSize);
|
|
290
|
+
const textX = (width - textWidth) / 2;
|
|
291
|
+
const textY = (height - textHeight) / 2;
|
|
292
|
+
const ops = [
|
|
293
|
+
"q",
|
|
294
|
+
"0.55 0.55 0.55 RG",
|
|
295
|
+
"0.75 w",
|
|
296
|
+
"[3 2] 0 d",
|
|
297
|
+
`0.5 0.5 ${(width - 1).toFixed(3)} ${(height - 1).toFixed(3)} re`,
|
|
298
|
+
"S",
|
|
299
|
+
"0.55 0.55 0.55 rg",
|
|
300
|
+
"BT",
|
|
301
|
+
`/F1 ${fontSize.toFixed(3)} Tf`,
|
|
302
|
+
`${textX.toFixed(3)} ${textY.toFixed(3)} Td`,
|
|
303
|
+
`(${label}) Tj`,
|
|
304
|
+
"ET",
|
|
305
|
+
"Q"
|
|
306
|
+
].join("\n");
|
|
307
|
+
const dict = context.obj({
|
|
308
|
+
Type: "XObject",
|
|
309
|
+
Subtype: "Form",
|
|
310
|
+
BBox: [0, 0, width, height],
|
|
311
|
+
Matrix: [1, 0, 0, 1, 0, 0],
|
|
312
|
+
Resources: { Font: { F1: font.ref } }
|
|
313
|
+
});
|
|
314
|
+
const stream = PDFRawStream.of(dict, new TextEncoder().encode(ops));
|
|
315
|
+
return context.register(stream);
|
|
316
|
+
}
|
|
317
|
+
async function buildSignedSignatureAppearance(pdfDoc, dataUrl, width, height) {
|
|
248
318
|
const match = dataUrl.match(/^data:(image\/[a-zA-Z0-9.+-]+);base64,(.+)$/);
|
|
249
|
-
if (!match) return;
|
|
319
|
+
if (!match) return buildSignaturePlaceholderAppearance(pdfDoc, width, height);
|
|
250
320
|
const mimeType = match[1] ?? "";
|
|
251
321
|
const base64 = match[2] ?? "";
|
|
252
|
-
|
|
253
|
-
if (mimeType.includes("jpeg") || mimeType.includes("jpg")) {
|
|
254
|
-
image = await pdfDoc.embedJpg(base64);
|
|
255
|
-
} else {
|
|
256
|
-
image = await pdfDoc.embedPng(base64);
|
|
257
|
-
}
|
|
322
|
+
const image = mimeType.includes("jpeg") || mimeType.includes("jpg") ? await pdfDoc.embedJpg(base64) : await pdfDoc.embedPng(base64);
|
|
258
323
|
const imgAspect = image.width / image.height;
|
|
259
|
-
const boxAspect =
|
|
260
|
-
let drawW =
|
|
261
|
-
let drawH =
|
|
262
|
-
if (imgAspect > boxAspect)
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
borderWidth: 0.75,
|
|
279
|
-
borderDashArray: [3, 2]
|
|
324
|
+
const boxAspect = width / height;
|
|
325
|
+
let drawW = width;
|
|
326
|
+
let drawH = height;
|
|
327
|
+
if (imgAspect > boxAspect) drawH = width / imgAspect;
|
|
328
|
+
else drawW = height * imgAspect;
|
|
329
|
+
const drawX = (width - drawW) / 2;
|
|
330
|
+
const drawY = (height - drawH) / 2;
|
|
331
|
+
const ops = [
|
|
332
|
+
"q",
|
|
333
|
+
`${drawW.toFixed(3)} 0 0 ${drawH.toFixed(3)} ${drawX.toFixed(3)} ${drawY.toFixed(3)} cm`,
|
|
334
|
+
"/Img Do",
|
|
335
|
+
"Q"
|
|
336
|
+
].join("\n");
|
|
337
|
+
const dict = pdfDoc.context.obj({
|
|
338
|
+
Type: "XObject",
|
|
339
|
+
Subtype: "Form",
|
|
340
|
+
BBox: [0, 0, width, height],
|
|
341
|
+
Matrix: [1, 0, 0, 1, 0, 0],
|
|
342
|
+
Resources: { XObject: { Img: image.ref } }
|
|
280
343
|
});
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
const label = "Sign here";
|
|
284
|
-
const fontSize = Math.max(6, Math.min(12, box.height * 0.4));
|
|
285
|
-
const textWidth = font.widthOfTextAtSize(label, fontSize);
|
|
286
|
-
const textHeight = font.heightAtSize(fontSize);
|
|
287
|
-
page.drawText(label, {
|
|
288
|
-
x: box.x + (box.width - textWidth) / 2,
|
|
289
|
-
y: box.y + (box.height - textHeight) / 2,
|
|
290
|
-
size: fontSize,
|
|
291
|
-
font,
|
|
292
|
-
color: rgb(0.55, 0.55, 0.55)
|
|
293
|
-
});
|
|
294
|
-
} catch {
|
|
295
|
-
}
|
|
344
|
+
const stream = PDFRawStream.of(dict, new TextEncoder().encode(ops));
|
|
345
|
+
return pdfDoc.context.register(stream);
|
|
296
346
|
}
|
|
297
347
|
async function embedSignatureImage(pdfDoc, field) {
|
|
298
348
|
const dataUrl = field.value;
|
|
@@ -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:
|
|
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-select{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto;cursor:pointer}.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}
|
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
|
|
55
55
|
<script setup>
|
|
56
56
|
import { ref, shallowRef, computed, onMounted, onBeforeUnmount, watch } from "vue";
|
|
57
|
+
import { useRuntimeConfig } from "#imports";
|
|
57
58
|
import { useViewerState } from "../composables/useViewerState";
|
|
58
59
|
import { useViewerSearch } from "../composables/useViewerSearch";
|
|
59
60
|
import { usePageProxyCache } from "../composables/usePageProxyCache";
|
|
@@ -71,6 +72,10 @@ const props = defineProps({
|
|
|
71
72
|
const canvasRef = ref(null);
|
|
72
73
|
const textLayerRef = ref(null);
|
|
73
74
|
const annotationRef = ref(null);
|
|
75
|
+
const runtimeConfig = useRuntimeConfig();
|
|
76
|
+
const minRenderPixelRatio = Number(
|
|
77
|
+
runtimeConfig.public.kviewer?.minRenderPixelRatio ?? 2
|
|
78
|
+
);
|
|
74
79
|
const state = useViewerState();
|
|
75
80
|
const search = useViewerSearch();
|
|
76
81
|
const proxyCache = usePageProxyCache();
|
|
@@ -181,7 +186,7 @@ async function renderCanvas(opts) {
|
|
|
181
186
|
currentRenderTask.cancel();
|
|
182
187
|
currentRenderTask = null;
|
|
183
188
|
}
|
|
184
|
-
const dpr = window.devicePixelRatio || 1;
|
|
189
|
+
const dpr = Math.max(window.devicePixelRatio || 1, minRenderPixelRatio);
|
|
185
190
|
const quality = props.renderQuality ?? 1;
|
|
186
191
|
const viewport = proxy.getViewport({
|
|
187
192
|
scale: props.scale * quality * dpr
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ViewMode } from '../composables/useViewerState.js';
|
|
2
|
-
import type { IAnnotationStore, StampDefinition, SignatureHandlers } from '../annotation/engine/types.js';
|
|
2
|
+
import type { AddFormFieldPayload, FormFieldDefinition, IAnnotationStore, StampDefinition, SignatureHandlers } from '../annotation/engine/types.js';
|
|
3
3
|
import { type ExportPdfOptions } from '../annotation/pdf-export/export.js';
|
|
4
4
|
type __VLS_Props = {
|
|
5
5
|
source: string | Uint8Array | object;
|
|
@@ -21,6 +21,16 @@ type __VLS_Props = {
|
|
|
21
21
|
* with selection chrome, can be moved/resized, and the property panel
|
|
22
22
|
* is shown. Supports v-model via `v-model:form-edit-mode`. */
|
|
23
23
|
formEditMode?: boolean;
|
|
24
|
+
/** Map of roleId → CSS color used to color-code form fields in
|
|
25
|
+
* form-edit mode. The editor stays unopinionated about role
|
|
26
|
+
* definitions: the host owns the role list, names, and color picker,
|
|
27
|
+
* and just hands a flat lookup table to the viewer. Unknown or
|
|
28
|
+
* missing ids fall back to the default blue. */
|
|
29
|
+
roleColors?: Record<string, string>;
|
|
30
|
+
/** Active role id new field placements are auto-tagged with. The host
|
|
31
|
+
* drives this — typically by binding it to a role picker in its own
|
|
32
|
+
* UI. Supports v-model via `v-model:active-role-id`. */
|
|
33
|
+
activeRoleId?: string | null;
|
|
24
34
|
};
|
|
25
35
|
declare function exportPdf(options?: ExportPdfOptions): Promise<Uint8Array>;
|
|
26
36
|
type ImportMode = 'replace' | 'merge';
|
|
@@ -44,6 +54,16 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
44
54
|
getKonvaCanvasState: typeof getKonvaCanvasState;
|
|
45
55
|
getFormFieldValues: () => import("../annotation/engine/types.js").FormFieldValue[];
|
|
46
56
|
setFormFieldValue: (fieldName: string, value: string | boolean | string[]) => void;
|
|
57
|
+
/** Add a form field programmatically. Returns the created definition.
|
|
58
|
+
* The new field is exported into the PDF on `exportPdf()` like any
|
|
59
|
+
* field placed via the placement tool. */
|
|
60
|
+
addFormField: (payload: AddFormFieldPayload) => FormFieldDefinition;
|
|
61
|
+
/** Patch a form field by id (rect, name, flags, type-specific props). */
|
|
62
|
+
updateFormField: (id: string, patch: Partial<FormFieldDefinition>) => void;
|
|
63
|
+
/** Remove a form field by id (drops its value too). */
|
|
64
|
+
removeFormField: (id: string) => void;
|
|
65
|
+
/** All form-field definitions (parsed, detected, and placed), flat. */
|
|
66
|
+
getFormFields: () => FormFieldDefinition[];
|
|
47
67
|
/** Reactive form-edit-mode flag — read with `.value`. */
|
|
48
68
|
formEditMode: import("vue").Ref<boolean, boolean>;
|
|
49
69
|
/** Programmatically enter or leave form-edit mode. */
|
|
@@ -52,8 +72,10 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
52
72
|
toggleFormEditMode: () => boolean;
|
|
53
73
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
54
74
|
"update:formEditMode": (value: boolean) => any;
|
|
75
|
+
"update:activeRoleId": (value: string | null) => any;
|
|
55
76
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
56
77
|
"onUpdate:formEditMode"?: ((value: boolean) => any) | undefined;
|
|
78
|
+
"onUpdate:activeRoleId"?: ((value: string | null) => any) | undefined;
|
|
57
79
|
}>, {
|
|
58
80
|
userName: string;
|
|
59
81
|
freehandGroupingDelay: number;
|
|
@@ -65,6 +87,8 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
65
87
|
viewMode: ViewMode;
|
|
66
88
|
shapeDetection: boolean;
|
|
67
89
|
formEditMode: boolean;
|
|
90
|
+
roleColors: Record<string, string>;
|
|
91
|
+
activeRoleId: string | null;
|
|
68
92
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
69
93
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
70
94
|
declare const _default: typeof __VLS_export;
|
|
@@ -221,9 +221,11 @@ const props = defineProps({
|
|
|
221
221
|
shapeDetection: { type: Boolean, required: false, default: false },
|
|
222
222
|
active: { type: Boolean, required: false, default: true },
|
|
223
223
|
freehandGroupingDelay: { type: Number, required: false, default: 1e3 },
|
|
224
|
-
formEditMode: { type: Boolean, required: false, default: void 0 }
|
|
224
|
+
formEditMode: { type: Boolean, required: false, default: void 0 },
|
|
225
|
+
roleColors: { type: Object, required: false, default: void 0 },
|
|
226
|
+
activeRoleId: { type: [String, null], required: false, default: void 0 }
|
|
225
227
|
});
|
|
226
|
-
const emit = defineEmits(["update:formEditMode"]);
|
|
228
|
+
const emit = defineEmits(["update:formEditMode", "update:activeRoleId"]);
|
|
227
229
|
const viewerRoot = ref(null);
|
|
228
230
|
const scrollContainer = ref(null);
|
|
229
231
|
const { state: viewerState, setScrollToPageFn, setDownloadPdfFn } = provideViewerState();
|
|
@@ -249,6 +251,29 @@ watch(
|
|
|
249
251
|
);
|
|
250
252
|
const viewerSearch = provideViewerSearch();
|
|
251
253
|
const formFieldsState = provideFormFields();
|
|
254
|
+
watch(
|
|
255
|
+
() => props.roleColors,
|
|
256
|
+
(v) => {
|
|
257
|
+
formFieldsState.setRoleColors(v ?? {});
|
|
258
|
+
},
|
|
259
|
+
{ immediate: true }
|
|
260
|
+
);
|
|
261
|
+
watch(
|
|
262
|
+
() => props.activeRoleId,
|
|
263
|
+
(v) => {
|
|
264
|
+
if (v === void 0) return;
|
|
265
|
+
if (formFieldsState.activeRoleId.value !== v) {
|
|
266
|
+
formFieldsState.setActiveRole(v);
|
|
267
|
+
}
|
|
268
|
+
},
|
|
269
|
+
{ immediate: true }
|
|
270
|
+
);
|
|
271
|
+
watch(
|
|
272
|
+
() => formFieldsState.activeRoleId.value,
|
|
273
|
+
(v) => {
|
|
274
|
+
if (props.activeRoleId !== v) emit("update:activeRoleId", v);
|
|
275
|
+
}
|
|
276
|
+
);
|
|
252
277
|
const pageSettings = providePageSettings(viewerRoot);
|
|
253
278
|
const virtualization = createPageVirtualization();
|
|
254
279
|
setScrollToPageFn((pageNumber) => {
|
|
@@ -686,6 +711,11 @@ onMounted(() => {
|
|
|
686
711
|
formFieldsState.addPlacedField(payload);
|
|
687
712
|
viewerState.selectTool("hand");
|
|
688
713
|
},
|
|
714
|
+
getFormFieldPreviewColor: () => {
|
|
715
|
+
const id = formFieldsState.activeRoleId.value;
|
|
716
|
+
if (!id) return void 0;
|
|
717
|
+
return formFieldsState.roleColors.value[id];
|
|
718
|
+
},
|
|
689
719
|
freehandGroupingDelay: props.freehandGroupingDelay,
|
|
690
720
|
// Suspend panzoom gestures while an annotation is being dragged or
|
|
691
721
|
// transformed. Otherwise single-finger pan runs in parallel with the
|
|
@@ -802,6 +832,16 @@ defineExpose({
|
|
|
802
832
|
getKonvaCanvasState,
|
|
803
833
|
getFormFieldValues: () => formFieldsState.getAllFieldValues(),
|
|
804
834
|
setFormFieldValue: (fieldName, value) => formFieldsState.setFieldValueByName(fieldName, value),
|
|
835
|
+
/** Add a form field programmatically. Returns the created definition.
|
|
836
|
+
* The new field is exported into the PDF on `exportPdf()` like any
|
|
837
|
+
* field placed via the placement tool. */
|
|
838
|
+
addFormField: (payload) => formFieldsState.addFormField(payload),
|
|
839
|
+
/** Patch a form field by id (rect, name, flags, type-specific props). */
|
|
840
|
+
updateFormField: (id, patch) => formFieldsState.updatePlacedField(id, patch),
|
|
841
|
+
/** Remove a form field by id (drops its value too). */
|
|
842
|
+
removeFormField: (id) => formFieldsState.removePlacedField(id),
|
|
843
|
+
/** All form-field definitions (parsed, detected, and placed), flat. */
|
|
844
|
+
getFormFields: () => formFieldsState.getAllFields(),
|
|
805
845
|
/** Reactive form-edit-mode flag — read with `.value`. */
|
|
806
846
|
formEditMode: viewerState.formEditMode,
|
|
807
847
|
/** Programmatically enter or leave form-edit mode. */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ViewMode } from '../composables/useViewerState.js';
|
|
2
|
-
import type { IAnnotationStore, StampDefinition, SignatureHandlers } from '../annotation/engine/types.js';
|
|
2
|
+
import type { AddFormFieldPayload, FormFieldDefinition, IAnnotationStore, StampDefinition, SignatureHandlers } from '../annotation/engine/types.js';
|
|
3
3
|
import { type ExportPdfOptions } from '../annotation/pdf-export/export.js';
|
|
4
4
|
type __VLS_Props = {
|
|
5
5
|
source: string | Uint8Array | object;
|
|
@@ -21,6 +21,16 @@ type __VLS_Props = {
|
|
|
21
21
|
* with selection chrome, can be moved/resized, and the property panel
|
|
22
22
|
* is shown. Supports v-model via `v-model:form-edit-mode`. */
|
|
23
23
|
formEditMode?: boolean;
|
|
24
|
+
/** Map of roleId → CSS color used to color-code form fields in
|
|
25
|
+
* form-edit mode. The editor stays unopinionated about role
|
|
26
|
+
* definitions: the host owns the role list, names, and color picker,
|
|
27
|
+
* and just hands a flat lookup table to the viewer. Unknown or
|
|
28
|
+
* missing ids fall back to the default blue. */
|
|
29
|
+
roleColors?: Record<string, string>;
|
|
30
|
+
/** Active role id new field placements are auto-tagged with. The host
|
|
31
|
+
* drives this — typically by binding it to a role picker in its own
|
|
32
|
+
* UI. Supports v-model via `v-model:active-role-id`. */
|
|
33
|
+
activeRoleId?: string | null;
|
|
24
34
|
};
|
|
25
35
|
declare function exportPdf(options?: ExportPdfOptions): Promise<Uint8Array>;
|
|
26
36
|
type ImportMode = 'replace' | 'merge';
|
|
@@ -44,6 +54,16 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
44
54
|
getKonvaCanvasState: typeof getKonvaCanvasState;
|
|
45
55
|
getFormFieldValues: () => import("../annotation/engine/types.js").FormFieldValue[];
|
|
46
56
|
setFormFieldValue: (fieldName: string, value: string | boolean | string[]) => void;
|
|
57
|
+
/** Add a form field programmatically. Returns the created definition.
|
|
58
|
+
* The new field is exported into the PDF on `exportPdf()` like any
|
|
59
|
+
* field placed via the placement tool. */
|
|
60
|
+
addFormField: (payload: AddFormFieldPayload) => FormFieldDefinition;
|
|
61
|
+
/** Patch a form field by id (rect, name, flags, type-specific props). */
|
|
62
|
+
updateFormField: (id: string, patch: Partial<FormFieldDefinition>) => void;
|
|
63
|
+
/** Remove a form field by id (drops its value too). */
|
|
64
|
+
removeFormField: (id: string) => void;
|
|
65
|
+
/** All form-field definitions (parsed, detected, and placed), flat. */
|
|
66
|
+
getFormFields: () => FormFieldDefinition[];
|
|
47
67
|
/** Reactive form-edit-mode flag — read with `.value`. */
|
|
48
68
|
formEditMode: import("vue").Ref<boolean, boolean>;
|
|
49
69
|
/** Programmatically enter or leave form-edit mode. */
|
|
@@ -52,8 +72,10 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
52
72
|
toggleFormEditMode: () => boolean;
|
|
53
73
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
54
74
|
"update:formEditMode": (value: boolean) => any;
|
|
75
|
+
"update:activeRoleId": (value: string | null) => any;
|
|
55
76
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
56
77
|
"onUpdate:formEditMode"?: ((value: boolean) => any) | undefined;
|
|
78
|
+
"onUpdate:activeRoleId"?: ((value: string | null) => any) | undefined;
|
|
57
79
|
}>, {
|
|
58
80
|
userName: string;
|
|
59
81
|
freehandGroupingDelay: number;
|
|
@@ -65,6 +87,8 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
65
87
|
viewMode: ViewMode;
|
|
66
88
|
shapeDetection: boolean;
|
|
67
89
|
formEditMode: boolean;
|
|
90
|
+
roleColors: Record<string, string>;
|
|
91
|
+
activeRoleId: string | null;
|
|
68
92
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
69
93
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
70
94
|
declare const _default: typeof __VLS_export;
|
|
@@ -48,6 +48,11 @@ type __VLS_Props = {
|
|
|
48
48
|
zoom?: number;
|
|
49
49
|
/** Minimum number of tabs -- prevents closing below this count. */
|
|
50
50
|
minTabs?: number;
|
|
51
|
+
/** Forwarded to every Viewer instance — see KViewer's `roleColors`. */
|
|
52
|
+
roleColors?: Record<string, string>;
|
|
53
|
+
/** Forwarded to every Viewer instance — see KViewer's `activeRoleId`.
|
|
54
|
+
* Supports v-model via `v-model:active-role-id`. */
|
|
55
|
+
activeRoleId?: string | null;
|
|
51
56
|
};
|
|
52
57
|
declare function addTab(item: Omit<ViewerTabItem, 'id'> & {
|
|
53
58
|
id?: string;
|
|
@@ -55,7 +60,7 @@ declare function addTab(item: Omit<ViewerTabItem, 'id'> & {
|
|
|
55
60
|
declare function removeTab(id: string): boolean;
|
|
56
61
|
declare function activateTab(id: string): void;
|
|
57
62
|
declare function getViewer(id: string): ComponentPublicInstance | null;
|
|
58
|
-
declare var __VLS_7: {}, __VLS_21: {},
|
|
63
|
+
declare var __VLS_7: {}, __VLS_21: {}, __VLS_32: {
|
|
59
64
|
tab: {
|
|
60
65
|
id: string;
|
|
61
66
|
label: string;
|
|
@@ -154,7 +159,7 @@ declare var __VLS_7: {}, __VLS_21: {}, __VLS_30: {
|
|
|
154
159
|
zoom?: number | undefined;
|
|
155
160
|
shapeDetection?: boolean | undefined;
|
|
156
161
|
};
|
|
157
|
-
},
|
|
162
|
+
}, __VLS_35: {
|
|
158
163
|
tab: {
|
|
159
164
|
id: string;
|
|
160
165
|
label: string;
|
|
@@ -253,17 +258,17 @@ declare var __VLS_7: {}, __VLS_21: {}, __VLS_30: {
|
|
|
253
258
|
zoom?: number | undefined;
|
|
254
259
|
shapeDetection?: boolean | undefined;
|
|
255
260
|
};
|
|
256
|
-
},
|
|
261
|
+
}, __VLS_37: {};
|
|
257
262
|
type __VLS_Slots = {} & {
|
|
258
263
|
'tabs-leading'?: (props: typeof __VLS_7) => any;
|
|
259
264
|
} & {
|
|
260
265
|
'tabs-trailing'?: (props: typeof __VLS_21) => any;
|
|
261
266
|
} & {
|
|
262
|
-
header?: (props: typeof
|
|
267
|
+
header?: (props: typeof __VLS_32) => any;
|
|
263
268
|
} & {
|
|
264
|
-
footer?: (props: typeof
|
|
269
|
+
footer?: (props: typeof __VLS_35) => any;
|
|
265
270
|
} & {
|
|
266
|
-
empty?: (props: typeof
|
|
271
|
+
empty?: (props: typeof __VLS_37) => any;
|
|
267
272
|
};
|
|
268
273
|
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
269
274
|
addTab: typeof addTab;
|
|
@@ -370,11 +375,13 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
370
375
|
shapeDetection?: boolean | undefined;
|
|
371
376
|
}[];
|
|
372
377
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
378
|
+
"update:activeRoleId": (value: string | null) => any;
|
|
373
379
|
"update:activeTab": (id: string) => any;
|
|
374
380
|
"tab-added": (tab: ViewerTabItem) => any;
|
|
375
381
|
"tab-close": (id: string) => any;
|
|
376
382
|
"tab-removed": (id: string) => any;
|
|
377
383
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
384
|
+
"onUpdate:activeRoleId"?: ((value: string | null) => any) | undefined;
|
|
378
385
|
"onUpdate:activeTab"?: ((id: string) => any) | undefined;
|
|
379
386
|
"onTab-added"?: ((tab: ViewerTabItem) => any) | undefined;
|
|
380
387
|
"onTab-close"?: ((id: string) => any) | undefined;
|
|
@@ -385,6 +392,8 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
385
392
|
stamps: StampDefinition[];
|
|
386
393
|
signatureHandlers: SignatureHandlers;
|
|
387
394
|
viewMode: ViewMode;
|
|
395
|
+
roleColors: Record<string, string>;
|
|
396
|
+
activeRoleId: string | null;
|
|
388
397
|
defaultActiveTab: string;
|
|
389
398
|
minTabs: number;
|
|
390
399
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
@@ -59,6 +59,9 @@
|
|
|
59
59
|
:zoom="activeTab.zoom ?? zoom"
|
|
60
60
|
:readonly="readonly"
|
|
61
61
|
:shape-detection="activeTab.shapeDetection ?? shapeDetection"
|
|
62
|
+
:role-colors="roleColors"
|
|
63
|
+
:active-role-id="activeRoleId"
|
|
64
|
+
@update:active-role-id="(v) => emit('update:activeRoleId', v)"
|
|
62
65
|
>
|
|
63
66
|
<template v-if="$slots.header" #header>
|
|
64
67
|
<slot name="header" :tab="activeTab" />
|
|
@@ -91,9 +94,11 @@ const props = defineProps({
|
|
|
91
94
|
shapeDetection: { type: Boolean, required: false },
|
|
92
95
|
viewMode: { type: String, required: false, default: "fit-width" },
|
|
93
96
|
zoom: { type: Number, required: false, default: 1 },
|
|
94
|
-
minTabs: { type: Number, required: false, default: 0 }
|
|
97
|
+
minTabs: { type: Number, required: false, default: 0 },
|
|
98
|
+
roleColors: { type: Object, required: false, default: void 0 },
|
|
99
|
+
activeRoleId: { type: [String, null], required: false, default: void 0 }
|
|
95
100
|
});
|
|
96
|
-
const emit = defineEmits(["update:activeTab", "tab-added", "tab-close", "tab-removed"]);
|
|
101
|
+
const emit = defineEmits(["update:activeTab", "tab-added", "tab-close", "tab-removed", "update:activeRoleId"]);
|
|
97
102
|
let idCounter = 0;
|
|
98
103
|
function generateId() {
|
|
99
104
|
return `tab-${Date.now()}-${++idCounter}`;
|
|
@@ -48,6 +48,11 @@ type __VLS_Props = {
|
|
|
48
48
|
zoom?: number;
|
|
49
49
|
/** Minimum number of tabs -- prevents closing below this count. */
|
|
50
50
|
minTabs?: number;
|
|
51
|
+
/** Forwarded to every Viewer instance — see KViewer's `roleColors`. */
|
|
52
|
+
roleColors?: Record<string, string>;
|
|
53
|
+
/** Forwarded to every Viewer instance — see KViewer's `activeRoleId`.
|
|
54
|
+
* Supports v-model via `v-model:active-role-id`. */
|
|
55
|
+
activeRoleId?: string | null;
|
|
51
56
|
};
|
|
52
57
|
declare function addTab(item: Omit<ViewerTabItem, 'id'> & {
|
|
53
58
|
id?: string;
|
|
@@ -55,7 +60,7 @@ declare function addTab(item: Omit<ViewerTabItem, 'id'> & {
|
|
|
55
60
|
declare function removeTab(id: string): boolean;
|
|
56
61
|
declare function activateTab(id: string): void;
|
|
57
62
|
declare function getViewer(id: string): ComponentPublicInstance | null;
|
|
58
|
-
declare var __VLS_7: {}, __VLS_21: {},
|
|
63
|
+
declare var __VLS_7: {}, __VLS_21: {}, __VLS_32: {
|
|
59
64
|
tab: {
|
|
60
65
|
id: string;
|
|
61
66
|
label: string;
|
|
@@ -154,7 +159,7 @@ declare var __VLS_7: {}, __VLS_21: {}, __VLS_30: {
|
|
|
154
159
|
zoom?: number | undefined;
|
|
155
160
|
shapeDetection?: boolean | undefined;
|
|
156
161
|
};
|
|
157
|
-
},
|
|
162
|
+
}, __VLS_35: {
|
|
158
163
|
tab: {
|
|
159
164
|
id: string;
|
|
160
165
|
label: string;
|
|
@@ -253,17 +258,17 @@ declare var __VLS_7: {}, __VLS_21: {}, __VLS_30: {
|
|
|
253
258
|
zoom?: number | undefined;
|
|
254
259
|
shapeDetection?: boolean | undefined;
|
|
255
260
|
};
|
|
256
|
-
},
|
|
261
|
+
}, __VLS_37: {};
|
|
257
262
|
type __VLS_Slots = {} & {
|
|
258
263
|
'tabs-leading'?: (props: typeof __VLS_7) => any;
|
|
259
264
|
} & {
|
|
260
265
|
'tabs-trailing'?: (props: typeof __VLS_21) => any;
|
|
261
266
|
} & {
|
|
262
|
-
header?: (props: typeof
|
|
267
|
+
header?: (props: typeof __VLS_32) => any;
|
|
263
268
|
} & {
|
|
264
|
-
footer?: (props: typeof
|
|
269
|
+
footer?: (props: typeof __VLS_35) => any;
|
|
265
270
|
} & {
|
|
266
|
-
empty?: (props: typeof
|
|
271
|
+
empty?: (props: typeof __VLS_37) => any;
|
|
267
272
|
};
|
|
268
273
|
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
269
274
|
addTab: typeof addTab;
|
|
@@ -370,11 +375,13 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
370
375
|
shapeDetection?: boolean | undefined;
|
|
371
376
|
}[];
|
|
372
377
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
378
|
+
"update:activeRoleId": (value: string | null) => any;
|
|
373
379
|
"update:activeTab": (id: string) => any;
|
|
374
380
|
"tab-added": (tab: ViewerTabItem) => any;
|
|
375
381
|
"tab-close": (id: string) => any;
|
|
376
382
|
"tab-removed": (id: string) => any;
|
|
377
383
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
384
|
+
"onUpdate:activeRoleId"?: ((value: string | null) => any) | undefined;
|
|
378
385
|
"onUpdate:activeTab"?: ((id: string) => any) | undefined;
|
|
379
386
|
"onTab-added"?: ((tab: ViewerTabItem) => any) | undefined;
|
|
380
387
|
"onTab-close"?: ((id: string) => any) | undefined;
|
|
@@ -385,6 +392,8 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
385
392
|
stamps: StampDefinition[];
|
|
386
393
|
signatureHandlers: SignatureHandlers;
|
|
387
394
|
viewMode: ViewMode;
|
|
395
|
+
roleColors: Record<string, string>;
|
|
396
|
+
activeRoleId: string | null;
|
|
388
397
|
defaultActiveTab: string;
|
|
389
398
|
minTabs: number;
|
|
390
399
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
'kviewer-form-field--placed': isPlaced,
|
|
7
7
|
'kviewer-form-field--selected': props.selected
|
|
8
8
|
}"
|
|
9
|
-
:style="positionStyle"
|
|
9
|
+
:style="[positionStyle, { '--kvw-field-color': chromeColor }]"
|
|
10
10
|
>
|
|
11
11
|
<div
|
|
12
12
|
class="kviewer-form-field__body"
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
<script setup>
|
|
35
35
|
import { computed } from "vue";
|
|
36
36
|
import { useViewerState } from "../../composables/useViewerState";
|
|
37
|
+
import { useFormFields } from "../../composables/useFormFields";
|
|
37
38
|
import FormTextField from "./FormTextField.vue";
|
|
38
39
|
import FormCheckbox from "./FormCheckbox.vue";
|
|
39
40
|
import FormRadioButton from "./FormRadioButton.vue";
|
|
@@ -51,9 +52,15 @@ const props = defineProps({
|
|
|
51
52
|
});
|
|
52
53
|
const emit = defineEmits(["select"]);
|
|
53
54
|
const state = useViewerState();
|
|
55
|
+
const formFields = useFormFields();
|
|
54
56
|
const isPlaced = computed(() => props.field.origin === "placed");
|
|
55
57
|
const isFormEditMode = computed(() => state.formEditMode.value);
|
|
56
58
|
const showChrome = computed(() => isFormEditMode.value);
|
|
59
|
+
const chromeColor = computed(() => {
|
|
60
|
+
const id = props.field.roleId;
|
|
61
|
+
if (!id) return "#1677ff";
|
|
62
|
+
return formFields.roleColors.value[id] ?? "#1677ff";
|
|
63
|
+
});
|
|
57
64
|
const positionStyle = computed(() => {
|
|
58
65
|
const rect = props.field.rect;
|
|
59
66
|
const vx = props.viewOffsetX ?? 0;
|
|
@@ -73,5 +80,5 @@ const positionStyle = computed(() => {
|
|
|
73
80
|
</script>
|
|
74
81
|
|
|
75
82
|
<style>
|
|
76
|
-
.kviewer-form-field__body{height:100%;width:100%}.kviewer-form-field__body--locked{pointer-events:none}.kviewer-form-field--editable{outline:1px dashed
|
|
83
|
+
.kviewer-form-field__body{height:100%;width:100%}.kviewer-form-field__body--locked{pointer-events:none}.kviewer-form-field--editable{outline:1px dashed color-mix(in srgb,var(--kvw-field-color,#1677ff) 45%,transparent);outline-offset:0}.kviewer-form-field--editable.kviewer-form-field--placed{outline-color:color-mix(in srgb,var(--kvw-field-color,#1677ff) 75%,transparent)}.kviewer-form-field--editable.kviewer-form-field--selected{outline:none}
|
|
77
84
|
</style>
|
|
@@ -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-chrome--selected{outline:1.5px dashed
|
|
130
|
+
.kviewer-placed-chrome{cursor:move;inset:0;pointer-events:auto;position:absolute;touch-action:none}.kviewer-placed-chrome--selected{outline:1.5px dashed var(--kvw-field-color,#1677ff);outline-offset:0}.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:calc(3px*var(--kvw-handle-zoom, 1));bottom:calc(100% + 3px*var(--kvw-handle-zoom, 1));color:var(--kvw-field-color,#1677ff);cursor:move;display:flex;font-size:calc(9px*var(--kvw-handle-zoom, 1));gap:calc(3px*var(--kvw-handle-zoom, 1));height:calc(16px*var(--kvw-handle-zoom, 1));left:0;line-height:1;overflow:hidden;padding:0 calc(6px*var(--kvw-handle-zoom, 1)) 0 calc(2px*var(--kvw-handle-zoom, 1));pointer-events:auto;position:absolute;right:0;touch-action:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.kviewer-placed-chrome__grip-dots{color:var(--kvw-field-color,#1677ff);flex:0 0 auto;height:calc(10px*var(--kvw-handle-zoom, 1));width:calc(10px*var(--kvw-handle-zoom, 1))}.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:calc(8px*var(--kvw-handle-zoom, 1));pointer-events:auto;position:absolute;touch-action:none;width:calc(8px*var(--kvw-handle-zoom, 1))}.kviewer-placed-chrome__handle:before{content:"";inset:calc(-8px*var(--kvw-handle-zoom, 1));position:absolute}.kviewer-placed-chrome__handle--n{left:50%;top:calc(-4px*var(--kvw-handle-zoom, 1));transform:translateX(-50%)}.kviewer-placed-chrome__handle--s{bottom:calc(-4px*var(--kvw-handle-zoom, 1));left:50%;transform:translateX(-50%)}.kviewer-placed-chrome__handle--e{right:calc(-4px*var(--kvw-handle-zoom, 1));top:50%;transform:translateY(-50%)}.kviewer-placed-chrome__handle--w{left:calc(-4px*var(--kvw-handle-zoom, 1));top:50%;transform:translateY(-50%)}.kviewer-placed-chrome__handle--ne{right:calc(-4px*var(--kvw-handle-zoom, 1));top:calc(-4px*var(--kvw-handle-zoom, 1))}.kviewer-placed-chrome__handle--nw{left:calc(-4px*var(--kvw-handle-zoom, 1));top:calc(-4px*var(--kvw-handle-zoom, 1))}.kviewer-placed-chrome__handle--se{bottom:calc(-4px*var(--kvw-handle-zoom, 1));right:calc(-4px*var(--kvw-handle-zoom, 1))}.kviewer-placed-chrome__handle--sw{bottom:calc(-4px*var(--kvw-handle-zoom, 1));left:calc(-4px*var(--kvw-handle-zoom, 1))}
|
|
131
131
|
</style>
|
|
@@ -5,6 +5,7 @@ export declare function createAnnotationEngine(viewerState: ViewerState, options
|
|
|
5
5
|
onRequestTextInput: PainterCallbacks['onRequestTextInput'];
|
|
6
6
|
onRequestDeleteConfirm?: PainterCallbacks['onRequestDeleteConfirm'];
|
|
7
7
|
onPlaceField?: PainterCallbacks['onPlaceField'];
|
|
8
|
+
getFormFieldPreviewColor?: PainterCallbacks['getFormFieldPreviewColor'];
|
|
8
9
|
freehandGroupingDelay?: number;
|
|
9
10
|
setExternalGesturesEnabled?: (enabled: boolean) => void;
|
|
10
11
|
}): Painter;
|
|
@@ -61,7 +61,8 @@ export function createAnnotationEngine(viewerState, options) {
|
|
|
61
61
|
},
|
|
62
62
|
onRequestTextInput: options.onRequestTextInput,
|
|
63
63
|
onRequestDeleteConfirm: options.onRequestDeleteConfirm,
|
|
64
|
-
onPlaceField: options.onPlaceField
|
|
64
|
+
onPlaceField: options.onPlaceField,
|
|
65
|
+
getFormFieldPreviewColor: options.getFormFieldPreviewColor
|
|
65
66
|
};
|
|
66
67
|
const painter = new Painter({
|
|
67
68
|
userName: options.userName ?? "User",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Ref, type ShallowRef } from 'vue';
|
|
2
|
-
import type { CheckboxStyle, DetectedShape, FormFieldDefinition, FormFieldValue, PlaceFieldPayload } from '../annotation/engine/types.js';
|
|
2
|
+
import type { AddFormFieldPayload, CheckboxStyle, DetectedShape, FormFieldDefinition, FormFieldValue, PlaceFieldPayload } from '../annotation/engine/types.js';
|
|
3
3
|
export interface FormFieldsState {
|
|
4
4
|
/** Field definitions grouped by page number */
|
|
5
5
|
fieldDefinitions: ShallowRef<Map<number, FormFieldDefinition[]>>;
|
|
@@ -23,10 +23,16 @@ export interface FormFieldsState {
|
|
|
23
23
|
registerDetectedCheckboxes: (pageNumber: number, shapes: DetectedShape[], scale: number, pageHeight: number) => void;
|
|
24
24
|
/** Add a user-placed form field (from the placement tool). Returns the created def. */
|
|
25
25
|
addPlacedField: (payload: PlaceFieldPayload) => FormFieldDefinition;
|
|
26
|
-
/**
|
|
26
|
+
/** Add a form field programmatically. Public API counterpart to
|
|
27
|
+
* `addPlacedField` with full control over the field's properties.
|
|
28
|
+
* Returns the created definition. */
|
|
29
|
+
addFormField: (payload: AddFormFieldPayload) => FormFieldDefinition;
|
|
30
|
+
/** Update a form field's rect, name, or flags. */
|
|
27
31
|
updatePlacedField: (id: string, patch: Partial<FormFieldDefinition>) => void;
|
|
28
|
-
/** Remove a
|
|
32
|
+
/** Remove a form field and its value. */
|
|
29
33
|
removePlacedField: (id: string) => void;
|
|
34
|
+
/** All form-field definitions, flattened across pages. */
|
|
35
|
+
getAllFields: () => FormFieldDefinition[];
|
|
30
36
|
/** All user-placed field definitions (used by export). */
|
|
31
37
|
getPlacedFields: () => FormFieldDefinition[];
|
|
32
38
|
/** Look up a field definition by id (any origin). */
|
|
@@ -35,6 +41,19 @@ export interface FormFieldsState {
|
|
|
35
41
|
selectedPlacedFieldId: Ref<string | null>;
|
|
36
42
|
/** Select or deselect a placed field. Pass null to deselect. */
|
|
37
43
|
selectPlacedField: (id: string | null) => void;
|
|
44
|
+
/** Opaque role id new placements are auto-tagged with. The host owns
|
|
45
|
+
* what roles exist; the editor only carries the active id forward to
|
|
46
|
+
* newly placed fields and looks up its color via roleColors. */
|
|
47
|
+
activeRoleId: Ref<string | null>;
|
|
48
|
+
/** Set or clear the active role for upcoming placements. */
|
|
49
|
+
setActiveRole: (id: string | null) => void;
|
|
50
|
+
/** Map from roleId → CSS color, supplied by the host via the KViewer
|
|
51
|
+
* `roleColors` prop. Reading this drives the chrome and placement
|
|
52
|
+
* preview color. The editor never mutates role definitions itself. */
|
|
53
|
+
roleColors: ShallowRef<Record<string, string>>;
|
|
54
|
+
/** Replace the host-provided color map. Called by KViewer when the
|
|
55
|
+
* prop changes. */
|
|
56
|
+
setRoleColors: (colors: Record<string, string>) => void;
|
|
38
57
|
/** Apply checkbox-style hints (extracted from `/MK /CA` in the source PDF)
|
|
39
58
|
* to existing parsed checkbox defs and store the map for any defs
|
|
40
59
|
* parsed afterwards. Keyed by checkbox fieldName. */
|
|
@@ -12,6 +12,14 @@ export function provideFormFields() {
|
|
|
12
12
|
const fieldDefinitions = shallowRef(/* @__PURE__ */ new Map());
|
|
13
13
|
const fieldValues = shallowRef(/* @__PURE__ */ new Map());
|
|
14
14
|
const selectedPlacedFieldId = ref(null);
|
|
15
|
+
const activeRoleId = ref(null);
|
|
16
|
+
const roleColors = shallowRef({});
|
|
17
|
+
function setActiveRole(id) {
|
|
18
|
+
activeRoleId.value = id;
|
|
19
|
+
}
|
|
20
|
+
function setRoleColors(colors) {
|
|
21
|
+
roleColors.value = colors;
|
|
22
|
+
}
|
|
15
23
|
function selectPlacedField(id) {
|
|
16
24
|
selectedPlacedFieldId.value = id;
|
|
17
25
|
}
|
|
@@ -231,6 +239,9 @@ export function provideFormFields() {
|
|
|
231
239
|
required: false,
|
|
232
240
|
origin: "placed"
|
|
233
241
|
};
|
|
242
|
+
if (activeRoleId.value) {
|
|
243
|
+
def.roleId = activeRoleId.value;
|
|
244
|
+
}
|
|
234
245
|
if (payload.fieldType === "radio") {
|
|
235
246
|
def.buttonValue = nextRadioOptionValue(fieldName);
|
|
236
247
|
}
|
|
@@ -256,6 +267,67 @@ export function provideFormFields() {
|
|
|
256
267
|
}
|
|
257
268
|
return def;
|
|
258
269
|
}
|
|
270
|
+
function addFormField(payload) {
|
|
271
|
+
const id = `placed-${generateUUID()}`;
|
|
272
|
+
const fieldName = payload.fieldName ?? nextPlacedFieldName(payload.fieldType);
|
|
273
|
+
const def = {
|
|
274
|
+
id,
|
|
275
|
+
pageNumber: payload.pageNumber,
|
|
276
|
+
fieldType: payload.fieldType,
|
|
277
|
+
fieldName,
|
|
278
|
+
rect: [...payload.rect],
|
|
279
|
+
readOnly: payload.readOnly ?? false,
|
|
280
|
+
required: payload.required ?? false,
|
|
281
|
+
origin: "placed"
|
|
282
|
+
};
|
|
283
|
+
if (payload.roleId !== void 0) def.roleId = payload.roleId;
|
|
284
|
+
if (payload.defaultValue !== void 0) def.defaultValue = payload.defaultValue;
|
|
285
|
+
if (payload.fontSize !== void 0) def.fontSize = payload.fontSize;
|
|
286
|
+
if (payload.fontName !== void 0) def.fontName = payload.fontName;
|
|
287
|
+
if (payload.color !== void 0) def.color = payload.color;
|
|
288
|
+
if (payload.backgroundColor !== void 0) def.backgroundColor = payload.backgroundColor;
|
|
289
|
+
if (payload.textAlignment !== void 0) def.textAlignment = payload.textAlignment;
|
|
290
|
+
if (payload.fieldType === "radio") {
|
|
291
|
+
def.buttonValue = payload.buttonValue ?? nextRadioOptionValue(fieldName);
|
|
292
|
+
}
|
|
293
|
+
if (payload.fieldType === "checkbox") {
|
|
294
|
+
def.checkboxStyle = payload.checkboxStyle ?? "check";
|
|
295
|
+
}
|
|
296
|
+
if (payload.fieldType === "dropdown") {
|
|
297
|
+
def.combo = payload.combo ?? true;
|
|
298
|
+
def.options = payload.options ?? [{ exportValue: "Option 1", displayValue: "Option 1" }];
|
|
299
|
+
if (payload.multiSelect !== void 0) def.multiSelect = payload.multiSelect;
|
|
300
|
+
}
|
|
301
|
+
if (payload.fieldType === "text") {
|
|
302
|
+
if (payload.multiLine !== void 0) def.multiLine = payload.multiLine;
|
|
303
|
+
if (payload.password !== void 0) def.password = payload.password;
|
|
304
|
+
if (payload.comb !== void 0) def.comb = payload.comb;
|
|
305
|
+
if (payload.maxLen !== void 0) def.maxLen = payload.maxLen;
|
|
306
|
+
}
|
|
307
|
+
if (payload.fieldType === "signature") {
|
|
308
|
+
if (payload.promptText !== void 0) def.promptText = payload.promptText;
|
|
309
|
+
if (payload.lockAction !== void 0) def.lockAction = payload.lockAction;
|
|
310
|
+
if (payload.lockFieldNames !== void 0) def.lockFieldNames = payload.lockFieldNames;
|
|
311
|
+
}
|
|
312
|
+
const existingDefs = fieldDefinitions.value.get(payload.pageNumber) ?? [];
|
|
313
|
+
fieldDefinitions.value.set(payload.pageNumber, [...existingDefs, def]);
|
|
314
|
+
triggerRef(fieldDefinitions);
|
|
315
|
+
fieldValues.value.set(id, {
|
|
316
|
+
fieldId: id,
|
|
317
|
+
fieldName: def.fieldName,
|
|
318
|
+
fieldType: def.fieldType,
|
|
319
|
+
value: getDefaultValue(def)
|
|
320
|
+
});
|
|
321
|
+
triggerRef(fieldValues);
|
|
322
|
+
return def;
|
|
323
|
+
}
|
|
324
|
+
function getAllFields() {
|
|
325
|
+
const out = [];
|
|
326
|
+
for (const defs of fieldDefinitions.value.values()) {
|
|
327
|
+
for (const d of defs) out.push(d);
|
|
328
|
+
}
|
|
329
|
+
return out;
|
|
330
|
+
}
|
|
259
331
|
function nextRadioOptionValue(groupName) {
|
|
260
332
|
const used = /* @__PURE__ */ new Set();
|
|
261
333
|
for (const defs of fieldDefinitions.value.values()) {
|
|
@@ -387,8 +459,10 @@ export function provideFormFields() {
|
|
|
387
459
|
setFieldValueByName,
|
|
388
460
|
registerDetectedCheckboxes,
|
|
389
461
|
addPlacedField,
|
|
462
|
+
addFormField,
|
|
390
463
|
updatePlacedField,
|
|
391
464
|
removePlacedField,
|
|
465
|
+
getAllFields,
|
|
392
466
|
getPlacedFields,
|
|
393
467
|
getFieldById,
|
|
394
468
|
selectedPlacedFieldId,
|
|
@@ -397,7 +471,11 @@ export function provideFormFields() {
|
|
|
397
471
|
applyButtonCaptions,
|
|
398
472
|
isFieldLocked,
|
|
399
473
|
resetValues,
|
|
400
|
-
reset
|
|
474
|
+
reset,
|
|
475
|
+
activeRoleId,
|
|
476
|
+
setActiveRole,
|
|
477
|
+
roleColors,
|
|
478
|
+
setRoleColors
|
|
401
479
|
};
|
|
402
480
|
provide(FORM_FIELDS_KEY, state);
|
|
403
481
|
return state;
|
|
@@ -1,4 +1,4 @@
|
|
|
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 { SignatureData, SignatureHandlers } from './annotation/engine/types.js';
|
|
3
|
+
export type { AddFormFieldPayload, CheckboxStyle, FormFieldDefinition, FormFieldOrigin, FormFieldType, FormFieldValue, SignatureData, SignatureHandlers, } from './annotation/engine/types.js';
|
|
4
4
|
export type { ViewMode } from './composables/viewMode.js';
|
package/dist/types.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { type ExportPdfOptions, type SignatureData, type SignatureHandlers, type ViewMode } from '../dist/runtime/public-types.js'
|
|
1
|
+
export { type AddFormFieldPayload, type CheckboxStyle, type ExportPdfOptions, type FormFieldDefinition, type FormFieldOrigin, type FormFieldType, type FormFieldValue, type SignatureData, type SignatureHandlers, type ViewMode } from '../dist/runtime/public-types.js'
|
|
2
2
|
|
|
3
3
|
export { default } from './module.mjs'
|
|
4
4
|
|