kviewer 0.3.4 → 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/module.json +1 -1
- package/dist/runtime/annotation/engine/painter.d.ts +2 -2
- package/dist/runtime/annotation/engine/tools/form-field.d.ts +5 -5
- package/dist/runtime/annotation/engine/tools/form-field.js +1 -1
- package/dist/runtime/annotation/engine/types.d.ts +18 -9
- package/dist/runtime/assets/kviewer.css +1 -1
- package/dist/runtime/components/Viewer.d.vue.ts +17 -17
- package/dist/runtime/components/Viewer.vue +36 -36
- package/dist/runtime/components/Viewer.vue.d.ts +17 -17
- package/dist/runtime/components/ViewerTabs.d.vue.ts +12 -17
- package/dist/runtime/components/ViewerTabs.vue +3 -6
- package/dist/runtime/components/ViewerTabs.vue.d.ts +12 -17
- package/dist/runtime/components/form-fields/FormFieldWrapper.vue +21 -9
- package/dist/runtime/components/form-fields/FormSignatureField.vue +10 -2
- package/dist/runtime/components/form-fields/PlacedFieldChrome.d.vue.ts +4 -0
- package/dist/runtime/components/form-fields/PlacedFieldChrome.vue +27 -10
- package/dist/runtime/components/form-fields/PlacedFieldChrome.vue.d.ts +4 -0
- package/dist/runtime/composables/useFormFields.d.ts +28 -17
- package/dist/runtime/composables/useFormFields.js +20 -24
- package/dist/runtime/embed/bridge-client.d.ts +4 -2
- package/dist/runtime/embed/bridge-client.js +2 -1
- package/dist/runtime/embed/bridge-host.d.ts +2 -0
- package/dist/runtime/embed/bridge-host.js +16 -0
- package/dist/runtime/embed/protocol.d.ts +12 -0
- package/package.json +1 -1
package/dist/module.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import Konva from 'konva';
|
|
2
2
|
import type { IRect } from 'konva/lib/types';
|
|
3
|
-
import { type IAnnotationStore, type IAnnotationStyle, type IAnnotationType, type PlaceFieldPayload } from './types.js';
|
|
3
|
+
import { type FormFieldType, type IAnnotationStore, type IAnnotationStyle, type IAnnotationType, type PlaceFieldPayload } from './types.js';
|
|
4
4
|
import { type IEditorFreeTextOptions } from './tools/free-text.js';
|
|
5
5
|
export interface KonvaCanvas {
|
|
6
6
|
pageNumber: number;
|
|
@@ -26,7 +26,7 @@ export interface PainterCallbacks {
|
|
|
26
26
|
/** Optional accessor returning the color to use for the form-field
|
|
27
27
|
* placement preview. Lets the active role color override the default
|
|
28
28
|
* blue without rebuilding the editor between placements. */
|
|
29
|
-
getFormFieldPreviewColor?: () => string | undefined;
|
|
29
|
+
getFormFieldPreviewColor?: (fieldType: FormFieldType) => string | undefined;
|
|
30
30
|
}
|
|
31
31
|
export declare class Painter {
|
|
32
32
|
private userName?;
|
|
@@ -6,11 +6,11 @@ export interface IEditorFormFieldOptions {
|
|
|
6
6
|
/** PDF-space page height, needed to flip Konva page-local Y → PDF Y. */
|
|
7
7
|
pageHeight: number;
|
|
8
8
|
onPlaceField: (payload: PlaceFieldPayload) => void;
|
|
9
|
-
/** Resolves to the
|
|
10
|
-
* the color the field will
|
|
11
|
-
* so the
|
|
12
|
-
* the editor. */
|
|
13
|
-
getPreviewColor?: () => string | undefined;
|
|
9
|
+
/** Resolves to the fieldColor preset for this field type so the
|
|
10
|
+
* placement preview matches the color the field will land with.
|
|
11
|
+
* Re-read on every drag start so the host can change defaults between
|
|
12
|
+
* placements without rebuilding the editor. */
|
|
13
|
+
getPreviewColor?: (fieldType: FormFieldType) => string | undefined;
|
|
14
14
|
}
|
|
15
15
|
export declare class EditorFormField extends Editor {
|
|
16
16
|
private fieldType;
|
|
@@ -38,7 +38,7 @@ export class EditorFormField extends Editor {
|
|
|
38
38
|
y: pos.y,
|
|
39
39
|
width: 0,
|
|
40
40
|
height: 0,
|
|
41
|
-
stroke: this.getPreviewColor?.() ?? "#1677ff",
|
|
41
|
+
stroke: this.getPreviewColor?.(this.fieldType) ?? "#1677ff",
|
|
42
42
|
strokeWidth: 1,
|
|
43
43
|
dash: [4, 4],
|
|
44
44
|
strokeScaleEnabled: false,
|
|
@@ -216,11 +216,17 @@ export interface FormFieldDefinition {
|
|
|
216
216
|
color?: number[];
|
|
217
217
|
backgroundColor?: number[];
|
|
218
218
|
textAlignment?: number;
|
|
219
|
-
/**
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
|
|
219
|
+
/** CSS color driving the field's editor chrome (outline, grip, hover
|
|
220
|
+
* accents). Host-owned; falls back to the default blue when unset.
|
|
221
|
+
* Not exported to the PDF — use `color`/`backgroundColor` for the
|
|
222
|
+
* widget's native appearance. Typically set per placement via
|
|
223
|
+
* `fieldDefaults` (e.g. one color per signer). */
|
|
224
|
+
fieldColor?: string;
|
|
225
|
+
/** Host-owned metadata bag. The editor carries it around untouched —
|
|
226
|
+
* it is never read, validated, or exported to the PDF. Must stay
|
|
227
|
+
* JSON-serializable so it survives the embed bridge. Replaced
|
|
228
|
+
* wholesale by an `updateFormField()` patch (no deep merge). */
|
|
229
|
+
meta?: Record<string, unknown>;
|
|
224
230
|
}
|
|
225
231
|
export interface FormFieldValue {
|
|
226
232
|
fieldId: string;
|
|
@@ -251,8 +257,13 @@ export interface AddFormFieldPayload {
|
|
|
251
257
|
* mirrored across them on edit. Radios with the same `fieldName` form
|
|
252
258
|
* one option group. */
|
|
253
259
|
fieldName?: string;
|
|
254
|
-
/**
|
|
255
|
-
|
|
260
|
+
/** CSS color for the field's editor chrome (see
|
|
261
|
+
* {@link FormFieldDefinition.fieldColor}). */
|
|
262
|
+
fieldColor?: string;
|
|
263
|
+
/** Host-owned metadata bag, carried untouched (see
|
|
264
|
+
* {@link FormFieldDefinition.meta}). Also usable as a preset in
|
|
265
|
+
* `fieldDefaults` so tool placements get their metadata atomically. */
|
|
266
|
+
meta?: Record<string, unknown>;
|
|
256
267
|
/** Default value for the field. Becomes the initial value too. */
|
|
257
268
|
defaultValue?: string;
|
|
258
269
|
readOnly?: boolean;
|
|
@@ -316,8 +327,6 @@ export interface SignatureFieldStatus {
|
|
|
316
327
|
fieldName: string;
|
|
317
328
|
pageNumber: number;
|
|
318
329
|
signed: boolean;
|
|
319
|
-
/** Present when the field is tagged with a host role. */
|
|
320
|
-
roleId?: string;
|
|
321
330
|
}
|
|
322
331
|
export interface DetectedShapeRect {
|
|
323
332
|
x: number;
|
|
@@ -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-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}
|
|
1
|
+
.kviewer-text-layer{left:0;line-height:1;overflow:hidden;position:absolute;top:0;-webkit-user-select:none;-moz-user-select:none;user-select:none;z-index:1}.kviewer-text-layer--interactive{cursor:text;-webkit-user-select:text;-moz-user-select:text;user-select:text}.KViewer_is_painting.KViewer_painting_type_12 .kviewer-annotation-layer,.KViewer_is_painting.KViewer_painting_type_12 .kviewer-annotation-layer *,.KViewer_is_painting.KViewer_painting_type_13 .kviewer-annotation-layer,.KViewer_is_painting.KViewer_painting_type_13 .kviewer-annotation-layer *,.KViewer_is_painting.KViewer_painting_type_5 .kviewer-annotation-layer,.KViewer_is_painting.KViewer_painting_type_5 .kviewer-annotation-layer *,.KViewer_is_painting.KViewer_painting_type_6 .kviewer-annotation-layer,.KViewer_is_painting.KViewer_painting_type_6 .kviewer-annotation-layer *,.KViewer_is_painting.KViewer_painting_type_7 .kviewer-annotation-layer,.KViewer_is_painting.KViewer_painting_type_7 .kviewer-annotation-layer *,.KViewer_is_painting.KViewer_painting_type_8 .kviewer-annotation-layer,.KViewer_is_painting.KViewer_painting_type_8 .kviewer-annotation-layer *{cursor:crosshair!important}.KViewer_is_painting.KViewer_painting_type_4 .kviewer-annotation-layer,.KViewer_is_painting.KViewer_painting_type_4 .kviewer-annotation-layer *{cursor:text!important}.KViewer_is_painting.KViewer_painting_type_11 .kviewer-annotation-layer,.KViewer_is_painting.KViewer_painting_type_11 .kviewer-annotation-layer *{cursor:crosshair!important}.KViewer_selector_hover{cursor:pointer!important}.kviewer-form-layer{left:0;overflow:hidden;position:absolute;top:0}.kviewer-form-field{box-sizing:border-box;position:absolute}.kviewer-form-field--readonly.kviewer-form-field--placed{filter:grayscale(.6);opacity:.5}.kviewer-form-field--readonly .kviewer-form-signature,.kviewer-form-field--readonly .kviewer-form-signature:hover{background:rgba(255,255,200,.15);border-color:rgba(0,0,0,.2);cursor:not-allowed}.kviewer-form-input{background-color:color-mix(in srgb,var(--kvw-field-color,#1677ff) 14%,transparent);border:1px solid transparent;box-sizing:border-box;color:#000;display:block;font-family:-apple-system,BlinkMacSystemFont,Helvetica Neue,Helvetica,Arial,sans-serif;font-weight:500;height:100%;line-height:normal;margin:0;outline:none;padding:1px 2px;resize:none;width:100%;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-rendering:optimizeLegibility;vertical-align:top}.kviewer-form-input:hover{background-color:color-mix(in srgb,var(--kvw-field-color,#1677ff) 22%,transparent);border-color:rgba(0,0,0,.2)}.kviewer-form-input:focus{background-color:color-mix(in srgb,var(--kvw-field-color,#1677ff) 28%,transparent);border-color:var(--kvw-field-color,var(--color-primary,#3b82f6));outline:2px solid var(--kvw-field-color,var(--color-primary,#3b82f6))}.kviewer-form-uselect{align-items:center;background-color:color-mix(in srgb,var(--kvw-field-color,#1677ff) 14%,transparent);border:1px solid transparent;border-radius:0;box-sizing:border-box;color:#000;cursor:pointer;display:flex;height:100%;line-height:1;padding:0 11px 0 2px;width:100%}.kviewer-form-uselect:hover{background-color:color-mix(in srgb,var(--kvw-field-color,#1677ff) 22%,transparent);border-color:rgba(0,0,0,.2)}.kviewer-form-uselect:focus-visible{background-color:color-mix(in srgb,var(--kvw-field-color,#1677ff) 28%,transparent);outline:2px solid var(--kvw-field-color,var(--color-primary,#3b82f6));outline-offset:-2px}.kviewer-form-uselect:disabled{cursor:default}.kviewer-form-listbox{overflow-y:auto;padding:0}.kviewer-form-listbox option{padding:1px 3px}.kviewer-form-editable-combo{height:100%;position:relative;width:100%}.kviewer-form-editable-combo input{height:100%;width:100%}.kviewer-form-checkbox,.kviewer-form-radio{align-items:center;background:#fff;box-sizing:border-box;cursor:pointer;display:flex;height:100%;justify-content:center;width:100%}.kviewer-form-checkbox input,.kviewer-form-radio input{accent-color:var(--kvw-field-color,var(--color-primary,#3b82f6));cursor:pointer;height:80%;margin:0;max-height:18px;max-width:18px;width:80%}.kviewer-form-checkbox--detected{background:transparent}.kviewer-form-checkbox--circle{border-radius:50%}.kviewer-form-checkbox__icon{color:#1a1a1a;height:65%;width:65%}.kviewer-form-checkbox--circle .kviewer-form-checkbox__icon{height:50%;width:50%}.kviewer-form-radio input{accent-color:var(--kvw-field-color,var(--color-primary,#3b82f6));cursor:pointer;height:80%;margin:0;max-height:18px;max-width:18px;width:80%}.kviewer-form-button{align-items:center;background:#c8c8c8;border:1px solid rgba(0,0,0,.35);box-sizing:border-box;color:#000;cursor:pointer;display:flex;font-family:inherit;font-weight:700;height:100%;justify-content:center;line-height:1;padding:0 6px;text-align:center;width:100%;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.kviewer-form-button:hover{background:#bdbdbd}.kviewer-form-button:active{background:#b0b0b0}.kviewer-form-signature{align-items:center;background:rgba(255,255,200,.15);border:1px dashed rgba(0,0,0,.2);cursor:pointer;display:flex;height:100%;justify-content:center;width:100%}.kviewer-form-signature:hover{background:color-mix(in srgb,var(--kvw-field-color,#3b82f6) 8%,transparent);border-color:var(--kvw-field-color,var(--color-primary,#3b82f6))}.kviewer-form-signature--filled{background:transparent;border-color:transparent;border-style:solid}.kviewer-form-signature__preview{max-height:100%;max-width:100%;-o-object-fit:contain;object-fit:contain}.kviewer-form-signature__placeholder{color:rgba(0,0,0,.4);font-size:10px;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.kviewer-form-field--editable .kviewer-form-signature,.kviewer-form-field__body--locked .kviewer-form-signature,.kviewer-form-field__body--locked .kviewer-form-signature:hover{border-color:transparent}.kviewer-form-signature--static{cursor:default}.kviewer-form-signature--static:hover{background:rgba(255,255,200,.15);border-color:rgba(0,0,0,.2)}.kviewer-form-signature__marker{align-items:center;display:inline-flex;gap:4px}.kviewer-form-signature__marker-icon{flex:0 0 auto;height:12px;width:12px}
|
|
@@ -26,16 +26,6 @@ type __VLS_Props = {
|
|
|
26
26
|
* with selection chrome, can be moved/resized, and the property panel
|
|
27
27
|
* is shown. Supports v-model via `v-model:form-edit-mode`. */
|
|
28
28
|
formEditMode?: boolean;
|
|
29
|
-
/** Map of roleId → CSS color used to color-code form fields in
|
|
30
|
-
* form-edit mode. The editor stays unopinionated about role
|
|
31
|
-
* definitions: the host owns the role list, names, and color picker,
|
|
32
|
-
* and just hands a flat lookup table to the viewer. Unknown or
|
|
33
|
-
* missing ids fall back to the default blue. */
|
|
34
|
-
roleColors?: Record<string, string>;
|
|
35
|
-
/** Active role id new field placements are auto-tagged with. The host
|
|
36
|
-
* drives this — typically by binding it to a role picker in its own
|
|
37
|
-
* UI. Supports v-model via `v-model:active-role-id`. */
|
|
38
|
-
activeRoleId?: string | null;
|
|
39
29
|
/** When true, clicking an unsigned signature field opens kviewer's own
|
|
40
30
|
* signing flow (saved signature or draw modal). Off by default so hosts
|
|
41
31
|
* that own the signing process (e.g. an embedding e-sign app) don't get
|
|
@@ -122,9 +112,11 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
122
112
|
* The new field is exported into the PDF on `exportPdf()` like any
|
|
123
113
|
* field placed via the placement tool. */
|
|
124
114
|
addFormField: (payload: AddFormFieldPayload) => FormFieldDefinition;
|
|
125
|
-
/** Patch a form field by id (rect, name, flags, type-specific props).
|
|
115
|
+
/** Patch a form field by id (rect, name, flags, type-specific props).
|
|
116
|
+
* Does not fire `field-updated` — that event is for user-driven edits. */
|
|
126
117
|
updateFormField: (id: string, patch: Partial<FormFieldDefinition>) => void;
|
|
127
|
-
/** Remove a form field by id (drops its value too).
|
|
118
|
+
/** Remove a form field by id (drops its value too). Does not fire
|
|
119
|
+
* `field-removed` — that event is for user-driven removals. */
|
|
128
120
|
removeFormField: (id: string) => void;
|
|
129
121
|
/** All form-field definitions (parsed, detected, and placed), flat. */
|
|
130
122
|
getFormFields: () => FormFieldDefinition[];
|
|
@@ -138,6 +130,14 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
138
130
|
* `field-placed` event, used by the embed bridge). Returns an
|
|
139
131
|
* unsubscribe function. */
|
|
140
132
|
onFieldPlaced: (cb: (field: FormFieldDefinition) => void) => (() => void);
|
|
133
|
+
/** Subscribe to user-driven field edits (imperative mirror of the
|
|
134
|
+
* `field-updated` event, used by the embed bridge). Returns an
|
|
135
|
+
* unsubscribe function. */
|
|
136
|
+
onFieldUpdated: (cb: (field: FormFieldDefinition, patch: Partial<FormFieldDefinition>) => void) => (() => void);
|
|
137
|
+
/** Subscribe to user-driven field removals (imperative mirror of the
|
|
138
|
+
* `field-removed` event, used by the embed bridge). Returns an
|
|
139
|
+
* unsubscribe function. */
|
|
140
|
+
onFieldRemoved: (cb: (field: FormFieldDefinition) => void) => (() => void);
|
|
141
141
|
/** True once every signature field in the document is signed — via a
|
|
142
142
|
* value applied in this session or a digital signature already in the
|
|
143
143
|
* source PDF. Vacuously true when the document has no signature
|
|
@@ -145,7 +145,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
145
145
|
* need "at least one". Use to gate e.g. envelope finalization. */
|
|
146
146
|
getSignedStatus: () => boolean;
|
|
147
147
|
/** Per-widget signed-state snapshot (id, fieldName, pageNumber,
|
|
148
|
-
* signed
|
|
148
|
+
* signed). Correlate to host data by `id`. */
|
|
149
149
|
getSignatureFieldStatus: () => SignatureFieldStatus[];
|
|
150
150
|
/** Subscribe to signed-status changes (imperative mirror of the
|
|
151
151
|
* `signed-status-changed` event, used by the embed bridge). Returns
|
|
@@ -171,8 +171,9 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
171
171
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
172
172
|
"all-pages-read": () => any;
|
|
173
173
|
"field-placed": (field: FormFieldDefinition) => any;
|
|
174
|
+
"field-updated": (field: FormFieldDefinition, patch: Partial<FormFieldDefinition>) => any;
|
|
175
|
+
"field-removed": (field: FormFieldDefinition) => any;
|
|
174
176
|
"update:formEditMode": (value: boolean) => any;
|
|
175
|
-
"update:activeRoleId": (value: string | null) => any;
|
|
176
177
|
"update:viewedPages": (pages: number[]) => any;
|
|
177
178
|
"signed-status-changed": (payload: {
|
|
178
179
|
allSigned: boolean;
|
|
@@ -181,8 +182,9 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
181
182
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
182
183
|
"onAll-pages-read"?: (() => any) | undefined;
|
|
183
184
|
"onField-placed"?: ((field: FormFieldDefinition) => any) | undefined;
|
|
185
|
+
"onField-updated"?: ((field: FormFieldDefinition, patch: Partial<FormFieldDefinition>) => any) | undefined;
|
|
186
|
+
"onField-removed"?: ((field: FormFieldDefinition) => any) | undefined;
|
|
184
187
|
"onUpdate:formEditMode"?: ((value: boolean) => any) | undefined;
|
|
185
|
-
"onUpdate:activeRoleId"?: ((value: string | null) => any) | undefined;
|
|
186
188
|
"onUpdate:viewedPages"?: ((pages: number[]) => any) | undefined;
|
|
187
189
|
"onSigned-status-changed"?: ((payload: {
|
|
188
190
|
allSigned: boolean;
|
|
@@ -204,8 +206,6 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
204
206
|
signatureHandlers: SignatureHandlers;
|
|
205
207
|
viewMode: ViewMode;
|
|
206
208
|
shapeDetection: boolean;
|
|
207
|
-
roleColors: Record<string, string>;
|
|
208
|
-
activeRoleId: string | null;
|
|
209
209
|
editablePlacedFields: boolean;
|
|
210
210
|
fieldDefaults: PlacedFieldDefaults;
|
|
211
211
|
selectionItems: ViewerSelectionItem[];
|
|
@@ -243,8 +243,6 @@ const props = defineProps({
|
|
|
243
243
|
active: { type: Boolean, required: false, default: true },
|
|
244
244
|
freehandGroupingDelay: { type: Number, required: false, default: 1e3 },
|
|
245
245
|
formEditMode: { type: Boolean, required: false, default: void 0 },
|
|
246
|
-
roleColors: { type: Object, required: false, default: void 0 },
|
|
247
|
-
activeRoleId: { type: [String, null], required: false, default: void 0 },
|
|
248
246
|
clickToSign: { type: Boolean, required: false, default: false },
|
|
249
247
|
editablePlacedFields: { type: Boolean, required: false, default: false },
|
|
250
248
|
fieldDefaults: { type: Object, required: false, default: void 0 },
|
|
@@ -260,9 +258,11 @@ const slots = useSlots();
|
|
|
260
258
|
const toolSlotNames = computed(
|
|
261
259
|
() => Object.keys(slots).filter((name) => name.startsWith("tool-"))
|
|
262
260
|
);
|
|
263
|
-
const emit = defineEmits(["update:formEditMode", "update:
|
|
261
|
+
const emit = defineEmits(["update:formEditMode", "update:viewedPages", "all-pages-read", "field-placed", "field-updated", "field-removed", "signed-status-changed"]);
|
|
264
262
|
const fieldPlacedCallbacks = /* @__PURE__ */ new Set();
|
|
265
263
|
const signedStatusCallbacks = /* @__PURE__ */ new Set();
|
|
264
|
+
const fieldUpdatedCallbacks = /* @__PURE__ */ new Set();
|
|
265
|
+
const fieldRemovedCallbacks = /* @__PURE__ */ new Set();
|
|
266
266
|
const viewerRoot = ref(null);
|
|
267
267
|
const scrollContainer = ref(null);
|
|
268
268
|
const { state: viewerState, setScrollToPageFn, setDownloadPdfFn, setPrintPdfFn, setApplyScaleFn } = provideViewerState();
|
|
@@ -291,6 +291,15 @@ watch(
|
|
|
291
291
|
);
|
|
292
292
|
const viewerSearch = provideViewerSearch();
|
|
293
293
|
const formFieldsState = provideFormFields();
|
|
294
|
+
formFieldsState.setFieldEventSink((event) => {
|
|
295
|
+
if (event.type === "updated") {
|
|
296
|
+
emit("field-updated", event.field, event.patch);
|
|
297
|
+
for (const cb of fieldUpdatedCallbacks) cb(event.field, event.patch);
|
|
298
|
+
} else {
|
|
299
|
+
emit("field-removed", event.field);
|
|
300
|
+
for (const cb of fieldRemovedCallbacks) cb(event.field);
|
|
301
|
+
}
|
|
302
|
+
});
|
|
294
303
|
const kviewerPublicConfig = useRuntimeConfig().public.kviewer;
|
|
295
304
|
provideKviewerI18n(
|
|
296
305
|
computed(() => {
|
|
@@ -299,13 +308,6 @@ provideKviewerI18n(
|
|
|
299
308
|
}),
|
|
300
309
|
computed(() => props.messages ?? {})
|
|
301
310
|
);
|
|
302
|
-
watch(
|
|
303
|
-
() => props.roleColors,
|
|
304
|
-
(v) => {
|
|
305
|
-
formFieldsState.setRoleColors(v ?? {});
|
|
306
|
-
},
|
|
307
|
-
{ immediate: true }
|
|
308
|
-
);
|
|
309
311
|
watch(
|
|
310
312
|
() => props.fieldDefaults,
|
|
311
313
|
(v) => {
|
|
@@ -347,22 +349,6 @@ watch(signatureStatus, (next, prev) => {
|
|
|
347
349
|
emit("signed-status-changed", payload);
|
|
348
350
|
for (const cb of signedStatusCallbacks) cb(payload);
|
|
349
351
|
});
|
|
350
|
-
watch(
|
|
351
|
-
() => props.activeRoleId,
|
|
352
|
-
(v) => {
|
|
353
|
-
if (v === void 0) return;
|
|
354
|
-
if (formFieldsState.activeRoleId.value !== v) {
|
|
355
|
-
formFieldsState.setActiveRole(v);
|
|
356
|
-
}
|
|
357
|
-
},
|
|
358
|
-
{ immediate: true }
|
|
359
|
-
);
|
|
360
|
-
watch(
|
|
361
|
-
() => formFieldsState.activeRoleId.value,
|
|
362
|
-
(v) => {
|
|
363
|
-
if (props.activeRoleId !== v) emit("update:activeRoleId", v);
|
|
364
|
-
}
|
|
365
|
-
);
|
|
366
352
|
const pageSettings = providePageSettings(viewerRoot);
|
|
367
353
|
const virtualization = createPageVirtualization();
|
|
368
354
|
setScrollToPageFn((pageNumber) => {
|
|
@@ -992,11 +978,9 @@ onMounted(() => {
|
|
|
992
978
|
for (const cb of fieldPlacedCallbacks) cb(def);
|
|
993
979
|
viewerState.selectTool("hand");
|
|
994
980
|
},
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
return formFieldsState.roleColors.value[id];
|
|
999
|
-
},
|
|
981
|
+
// The placement drag preview adopts the fieldColor preset of the tool's
|
|
982
|
+
// field type, so the rubber-band already shows the signer's color.
|
|
983
|
+
getFormFieldPreviewColor: (fieldType) => formFieldsState.placedFieldDefaults.value[fieldType]?.fieldColor,
|
|
1000
984
|
freehandGroupingDelay: props.freehandGroupingDelay,
|
|
1001
985
|
// Suspend panzoom gestures while an annotation is being dragged or
|
|
1002
986
|
// transformed. Otherwise single-finger pan runs in parallel with the
|
|
@@ -1144,10 +1128,12 @@ defineExpose({
|
|
|
1144
1128
|
* The new field is exported into the PDF on `exportPdf()` like any
|
|
1145
1129
|
* field placed via the placement tool. */
|
|
1146
1130
|
addFormField: (payload) => formFieldsState.addFormField(payload),
|
|
1147
|
-
/** Patch a form field by id (rect, name, flags, type-specific props).
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1131
|
+
/** Patch a form field by id (rect, name, flags, type-specific props).
|
|
1132
|
+
* Does not fire `field-updated` — that event is for user-driven edits. */
|
|
1133
|
+
updateFormField: (id, patch) => formFieldsState.updatePlacedField(id, patch, { silent: true }),
|
|
1134
|
+
/** Remove a form field by id (drops its value too). Does not fire
|
|
1135
|
+
* `field-removed` — that event is for user-driven removals. */
|
|
1136
|
+
removeFormField: (id) => formFieldsState.removePlacedField(id, { silent: true }),
|
|
1151
1137
|
/** All form-field definitions (parsed, detected, and placed), flat. */
|
|
1152
1138
|
getFormFields: () => formFieldsState.getAllFields(),
|
|
1153
1139
|
/** Replace the per-field-type presets applied to tool placements.
|
|
@@ -1165,6 +1151,20 @@ defineExpose({
|
|
|
1165
1151
|
fieldPlacedCallbacks.add(cb);
|
|
1166
1152
|
return () => fieldPlacedCallbacks.delete(cb);
|
|
1167
1153
|
},
|
|
1154
|
+
/** Subscribe to user-driven field edits (imperative mirror of the
|
|
1155
|
+
* `field-updated` event, used by the embed bridge). Returns an
|
|
1156
|
+
* unsubscribe function. */
|
|
1157
|
+
onFieldUpdated: (cb) => {
|
|
1158
|
+
fieldUpdatedCallbacks.add(cb);
|
|
1159
|
+
return () => fieldUpdatedCallbacks.delete(cb);
|
|
1160
|
+
},
|
|
1161
|
+
/** Subscribe to user-driven field removals (imperative mirror of the
|
|
1162
|
+
* `field-removed` event, used by the embed bridge). Returns an
|
|
1163
|
+
* unsubscribe function. */
|
|
1164
|
+
onFieldRemoved: (cb) => {
|
|
1165
|
+
fieldRemovedCallbacks.add(cb);
|
|
1166
|
+
return () => fieldRemovedCallbacks.delete(cb);
|
|
1167
|
+
},
|
|
1168
1168
|
/** True once every signature field in the document is signed — via a
|
|
1169
1169
|
* value applied in this session or a digital signature already in the
|
|
1170
1170
|
* source PDF. Vacuously true when the document has no signature
|
|
@@ -1172,7 +1172,7 @@ defineExpose({
|
|
|
1172
1172
|
* need "at least one". Use to gate e.g. envelope finalization. */
|
|
1173
1173
|
getSignedStatus: () => formFieldsState.getSignatureFieldStatus().every((s) => s.signed),
|
|
1174
1174
|
/** Per-widget signed-state snapshot (id, fieldName, pageNumber,
|
|
1175
|
-
* signed
|
|
1175
|
+
* signed). Correlate to host data by `id`. */
|
|
1176
1176
|
getSignatureFieldStatus: () => formFieldsState.getSignatureFieldStatus(),
|
|
1177
1177
|
/** Subscribe to signed-status changes (imperative mirror of the
|
|
1178
1178
|
* `signed-status-changed` event, used by the embed bridge). Returns
|
|
@@ -26,16 +26,6 @@ type __VLS_Props = {
|
|
|
26
26
|
* with selection chrome, can be moved/resized, and the property panel
|
|
27
27
|
* is shown. Supports v-model via `v-model:form-edit-mode`. */
|
|
28
28
|
formEditMode?: boolean;
|
|
29
|
-
/** Map of roleId → CSS color used to color-code form fields in
|
|
30
|
-
* form-edit mode. The editor stays unopinionated about role
|
|
31
|
-
* definitions: the host owns the role list, names, and color picker,
|
|
32
|
-
* and just hands a flat lookup table to the viewer. Unknown or
|
|
33
|
-
* missing ids fall back to the default blue. */
|
|
34
|
-
roleColors?: Record<string, string>;
|
|
35
|
-
/** Active role id new field placements are auto-tagged with. The host
|
|
36
|
-
* drives this — typically by binding it to a role picker in its own
|
|
37
|
-
* UI. Supports v-model via `v-model:active-role-id`. */
|
|
38
|
-
activeRoleId?: string | null;
|
|
39
29
|
/** When true, clicking an unsigned signature field opens kviewer's own
|
|
40
30
|
* signing flow (saved signature or draw modal). Off by default so hosts
|
|
41
31
|
* that own the signing process (e.g. an embedding e-sign app) don't get
|
|
@@ -122,9 +112,11 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
122
112
|
* The new field is exported into the PDF on `exportPdf()` like any
|
|
123
113
|
* field placed via the placement tool. */
|
|
124
114
|
addFormField: (payload: AddFormFieldPayload) => FormFieldDefinition;
|
|
125
|
-
/** Patch a form field by id (rect, name, flags, type-specific props).
|
|
115
|
+
/** Patch a form field by id (rect, name, flags, type-specific props).
|
|
116
|
+
* Does not fire `field-updated` — that event is for user-driven edits. */
|
|
126
117
|
updateFormField: (id: string, patch: Partial<FormFieldDefinition>) => void;
|
|
127
|
-
/** Remove a form field by id (drops its value too).
|
|
118
|
+
/** Remove a form field by id (drops its value too). Does not fire
|
|
119
|
+
* `field-removed` — that event is for user-driven removals. */
|
|
128
120
|
removeFormField: (id: string) => void;
|
|
129
121
|
/** All form-field definitions (parsed, detected, and placed), flat. */
|
|
130
122
|
getFormFields: () => FormFieldDefinition[];
|
|
@@ -138,6 +130,14 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
138
130
|
* `field-placed` event, used by the embed bridge). Returns an
|
|
139
131
|
* unsubscribe function. */
|
|
140
132
|
onFieldPlaced: (cb: (field: FormFieldDefinition) => void) => (() => void);
|
|
133
|
+
/** Subscribe to user-driven field edits (imperative mirror of the
|
|
134
|
+
* `field-updated` event, used by the embed bridge). Returns an
|
|
135
|
+
* unsubscribe function. */
|
|
136
|
+
onFieldUpdated: (cb: (field: FormFieldDefinition, patch: Partial<FormFieldDefinition>) => void) => (() => void);
|
|
137
|
+
/** Subscribe to user-driven field removals (imperative mirror of the
|
|
138
|
+
* `field-removed` event, used by the embed bridge). Returns an
|
|
139
|
+
* unsubscribe function. */
|
|
140
|
+
onFieldRemoved: (cb: (field: FormFieldDefinition) => void) => (() => void);
|
|
141
141
|
/** True once every signature field in the document is signed — via a
|
|
142
142
|
* value applied in this session or a digital signature already in the
|
|
143
143
|
* source PDF. Vacuously true when the document has no signature
|
|
@@ -145,7 +145,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
145
145
|
* need "at least one". Use to gate e.g. envelope finalization. */
|
|
146
146
|
getSignedStatus: () => boolean;
|
|
147
147
|
/** Per-widget signed-state snapshot (id, fieldName, pageNumber,
|
|
148
|
-
* signed
|
|
148
|
+
* signed). Correlate to host data by `id`. */
|
|
149
149
|
getSignatureFieldStatus: () => SignatureFieldStatus[];
|
|
150
150
|
/** Subscribe to signed-status changes (imperative mirror of the
|
|
151
151
|
* `signed-status-changed` event, used by the embed bridge). Returns
|
|
@@ -171,8 +171,9 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
171
171
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
172
172
|
"all-pages-read": () => any;
|
|
173
173
|
"field-placed": (field: FormFieldDefinition) => any;
|
|
174
|
+
"field-updated": (field: FormFieldDefinition, patch: Partial<FormFieldDefinition>) => any;
|
|
175
|
+
"field-removed": (field: FormFieldDefinition) => any;
|
|
174
176
|
"update:formEditMode": (value: boolean) => any;
|
|
175
|
-
"update:activeRoleId": (value: string | null) => any;
|
|
176
177
|
"update:viewedPages": (pages: number[]) => any;
|
|
177
178
|
"signed-status-changed": (payload: {
|
|
178
179
|
allSigned: boolean;
|
|
@@ -181,8 +182,9 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
181
182
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
182
183
|
"onAll-pages-read"?: (() => any) | undefined;
|
|
183
184
|
"onField-placed"?: ((field: FormFieldDefinition) => any) | undefined;
|
|
185
|
+
"onField-updated"?: ((field: FormFieldDefinition, patch: Partial<FormFieldDefinition>) => any) | undefined;
|
|
186
|
+
"onField-removed"?: ((field: FormFieldDefinition) => any) | undefined;
|
|
184
187
|
"onUpdate:formEditMode"?: ((value: boolean) => any) | undefined;
|
|
185
|
-
"onUpdate:activeRoleId"?: ((value: string | null) => any) | undefined;
|
|
186
188
|
"onUpdate:viewedPages"?: ((pages: number[]) => any) | undefined;
|
|
187
189
|
"onSigned-status-changed"?: ((payload: {
|
|
188
190
|
allSigned: boolean;
|
|
@@ -204,8 +206,6 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
204
206
|
signatureHandlers: SignatureHandlers;
|
|
205
207
|
viewMode: ViewMode;
|
|
206
208
|
shapeDetection: boolean;
|
|
207
|
-
roleColors: Record<string, string>;
|
|
208
|
-
activeRoleId: string | null;
|
|
209
209
|
editablePlacedFields: boolean;
|
|
210
210
|
fieldDefaults: PlacedFieldDefaults;
|
|
211
211
|
selectionItems: ViewerSelectionItem[];
|
|
@@ -54,11 +54,6 @@ type __VLS_Props = {
|
|
|
54
54
|
zoom?: number;
|
|
55
55
|
/** Minimum number of tabs -- prevents closing below this count. */
|
|
56
56
|
minTabs?: number;
|
|
57
|
-
/** Forwarded to every Viewer instance — see KViewer's `roleColors`. */
|
|
58
|
-
roleColors?: Record<string, string>;
|
|
59
|
-
/** Forwarded to every Viewer instance — see KViewer's `activeRoleId`.
|
|
60
|
-
* Supports v-model via `v-model:active-role-id`. */
|
|
61
|
-
activeRoleId?: string | null;
|
|
62
57
|
/** Forwarded to every Viewer instance — see KViewer's `scripting`.
|
|
63
58
|
* Per-tab override via `ViewerTabItem.scripting` wins when set. */
|
|
64
59
|
scripting?: boolean;
|
|
@@ -82,7 +77,7 @@ declare function addTab(item: Omit<ViewerTabItem, 'id'> & {
|
|
|
82
77
|
declare function removeTab(id: string): boolean;
|
|
83
78
|
declare function activateTab(id: string): void;
|
|
84
79
|
declare function getViewer(id: string): ComponentPublicInstance | null;
|
|
85
|
-
declare var __VLS_7: {}, __VLS_21: {},
|
|
80
|
+
declare var __VLS_7: {}, __VLS_21: {}, __VLS_35: {
|
|
86
81
|
tab: {
|
|
87
82
|
id: string;
|
|
88
83
|
label: string;
|
|
@@ -182,7 +177,7 @@ declare var __VLS_7: {}, __VLS_21: {}, __VLS_34: {
|
|
|
182
177
|
shapeDetection?: boolean | undefined;
|
|
183
178
|
scripting?: boolean | undefined;
|
|
184
179
|
};
|
|
185
|
-
},
|
|
180
|
+
}, __VLS_38: {
|
|
186
181
|
tab: {
|
|
187
182
|
id: string;
|
|
188
183
|
label: string;
|
|
@@ -282,21 +277,21 @@ declare var __VLS_7: {}, __VLS_21: {}, __VLS_34: {
|
|
|
282
277
|
shapeDetection?: boolean | undefined;
|
|
283
278
|
scripting?: boolean | undefined;
|
|
284
279
|
};
|
|
285
|
-
},
|
|
280
|
+
}, __VLS_42: string, __VLS_43: {
|
|
286
281
|
state: import("../composables/useViewerState.js").ViewerState;
|
|
287
|
-
},
|
|
282
|
+
}, __VLS_45: {};
|
|
288
283
|
type __VLS_Slots = {} & {
|
|
289
|
-
[K in NonNullable<typeof
|
|
284
|
+
[K in NonNullable<typeof __VLS_42>]?: (props: typeof __VLS_43) => any;
|
|
290
285
|
} & {
|
|
291
286
|
'tabs-leading'?: (props: typeof __VLS_7) => any;
|
|
292
287
|
} & {
|
|
293
288
|
'tabs-trailing'?: (props: typeof __VLS_21) => any;
|
|
294
289
|
} & {
|
|
295
|
-
header?: (props: typeof
|
|
290
|
+
header?: (props: typeof __VLS_35) => any;
|
|
296
291
|
} & {
|
|
297
|
-
footer?: (props: typeof
|
|
292
|
+
footer?: (props: typeof __VLS_38) => any;
|
|
298
293
|
} & {
|
|
299
|
-
empty?: (props: typeof
|
|
294
|
+
empty?: (props: typeof __VLS_45) => any;
|
|
300
295
|
};
|
|
301
296
|
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
302
297
|
addTab: typeof addTab;
|
|
@@ -405,7 +400,8 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
405
400
|
}[];
|
|
406
401
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
407
402
|
"field-placed": (field: FormFieldDefinition) => any;
|
|
408
|
-
"
|
|
403
|
+
"field-updated": (field: FormFieldDefinition, patch: Partial<FormFieldDefinition>) => any;
|
|
404
|
+
"field-removed": (field: FormFieldDefinition) => any;
|
|
409
405
|
"signed-status-changed": (payload: {
|
|
410
406
|
allSigned: boolean;
|
|
411
407
|
fields: SignatureFieldStatus[];
|
|
@@ -416,7 +412,8 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
416
412
|
"tab-removed": (id: string) => any;
|
|
417
413
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
418
414
|
"onField-placed"?: ((field: FormFieldDefinition) => any) | undefined;
|
|
419
|
-
"
|
|
415
|
+
"onField-updated"?: ((field: FormFieldDefinition, patch: Partial<FormFieldDefinition>) => any) | undefined;
|
|
416
|
+
"onField-removed"?: ((field: FormFieldDefinition) => any) | undefined;
|
|
420
417
|
"onSigned-status-changed"?: ((payload: {
|
|
421
418
|
allSigned: boolean;
|
|
422
419
|
fields: SignatureFieldStatus[];
|
|
@@ -435,8 +432,6 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
435
432
|
stamps: StampDefinition[];
|
|
436
433
|
signatureHandlers: SignatureHandlers;
|
|
437
434
|
viewMode: ViewMode;
|
|
438
|
-
roleColors: Record<string, string>;
|
|
439
|
-
activeRoleId: string | null;
|
|
440
435
|
editablePlacedFields: boolean;
|
|
441
436
|
fieldDefaults: PlacedFieldDefaults;
|
|
442
437
|
selectionItems: ViewerSelectionItem[];
|
|
@@ -63,13 +63,12 @@
|
|
|
63
63
|
:menu-items="menuItems"
|
|
64
64
|
:selection-items="selectionItems"
|
|
65
65
|
:tools="tools"
|
|
66
|
-
:role-colors="roleColors"
|
|
67
|
-
:active-role-id="activeRoleId"
|
|
68
66
|
:click-to-sign="clickToSign"
|
|
69
67
|
:editable-placed-fields="editablePlacedFields"
|
|
70
68
|
:field-defaults="fieldDefaults"
|
|
71
|
-
@update:active-role-id="(v) => emit('update:activeRoleId', v)"
|
|
72
69
|
@field-placed="(f) => emit('field-placed', f)"
|
|
70
|
+
@field-updated="(f, p) => emit('field-updated', f, p)"
|
|
71
|
+
@field-removed="(f) => emit('field-removed', f)"
|
|
73
72
|
@signed-status-changed="(p) => emit('signed-status-changed', p)"
|
|
74
73
|
>
|
|
75
74
|
<template v-if="$slots.header" #header>
|
|
@@ -109,8 +108,6 @@ const props = defineProps({
|
|
|
109
108
|
viewMode: { type: String, required: false, default: "fit-width" },
|
|
110
109
|
zoom: { type: Number, required: false, default: 1 },
|
|
111
110
|
minTabs: { type: Number, required: false, default: 0 },
|
|
112
|
-
roleColors: { type: Object, required: false, default: void 0 },
|
|
113
|
-
activeRoleId: { type: [String, null], required: false, default: void 0 },
|
|
114
111
|
scripting: { type: Boolean, required: false, default: false },
|
|
115
112
|
menuItems: { type: Array, required: false, default: void 0 },
|
|
116
113
|
selectionItems: { type: Array, required: false, default: void 0 },
|
|
@@ -123,7 +120,7 @@ const slots = useSlots();
|
|
|
123
120
|
const toolSlotNames = computed(
|
|
124
121
|
() => Object.keys(slots).filter((name) => name.startsWith("tool-"))
|
|
125
122
|
);
|
|
126
|
-
const emit = defineEmits(["update:activeTab", "tab-added", "tab-close", "tab-removed", "
|
|
123
|
+
const emit = defineEmits(["update:activeTab", "tab-added", "tab-close", "tab-removed", "field-placed", "field-updated", "field-removed", "signed-status-changed"]);
|
|
127
124
|
let idCounter = 0;
|
|
128
125
|
function generateId() {
|
|
129
126
|
return `tab-${Date.now()}-${++idCounter}`;
|
|
@@ -54,11 +54,6 @@ type __VLS_Props = {
|
|
|
54
54
|
zoom?: number;
|
|
55
55
|
/** Minimum number of tabs -- prevents closing below this count. */
|
|
56
56
|
minTabs?: number;
|
|
57
|
-
/** Forwarded to every Viewer instance — see KViewer's `roleColors`. */
|
|
58
|
-
roleColors?: Record<string, string>;
|
|
59
|
-
/** Forwarded to every Viewer instance — see KViewer's `activeRoleId`.
|
|
60
|
-
* Supports v-model via `v-model:active-role-id`. */
|
|
61
|
-
activeRoleId?: string | null;
|
|
62
57
|
/** Forwarded to every Viewer instance — see KViewer's `scripting`.
|
|
63
58
|
* Per-tab override via `ViewerTabItem.scripting` wins when set. */
|
|
64
59
|
scripting?: boolean;
|
|
@@ -82,7 +77,7 @@ declare function addTab(item: Omit<ViewerTabItem, 'id'> & {
|
|
|
82
77
|
declare function removeTab(id: string): boolean;
|
|
83
78
|
declare function activateTab(id: string): void;
|
|
84
79
|
declare function getViewer(id: string): ComponentPublicInstance | null;
|
|
85
|
-
declare var __VLS_7: {}, __VLS_21: {},
|
|
80
|
+
declare var __VLS_7: {}, __VLS_21: {}, __VLS_35: {
|
|
86
81
|
tab: {
|
|
87
82
|
id: string;
|
|
88
83
|
label: string;
|
|
@@ -182,7 +177,7 @@ declare var __VLS_7: {}, __VLS_21: {}, __VLS_34: {
|
|
|
182
177
|
shapeDetection?: boolean | undefined;
|
|
183
178
|
scripting?: boolean | undefined;
|
|
184
179
|
};
|
|
185
|
-
},
|
|
180
|
+
}, __VLS_38: {
|
|
186
181
|
tab: {
|
|
187
182
|
id: string;
|
|
188
183
|
label: string;
|
|
@@ -282,21 +277,21 @@ declare var __VLS_7: {}, __VLS_21: {}, __VLS_34: {
|
|
|
282
277
|
shapeDetection?: boolean | undefined;
|
|
283
278
|
scripting?: boolean | undefined;
|
|
284
279
|
};
|
|
285
|
-
},
|
|
280
|
+
}, __VLS_42: string, __VLS_43: {
|
|
286
281
|
state: import("../composables/useViewerState.js").ViewerState;
|
|
287
|
-
},
|
|
282
|
+
}, __VLS_45: {};
|
|
288
283
|
type __VLS_Slots = {} & {
|
|
289
|
-
[K in NonNullable<typeof
|
|
284
|
+
[K in NonNullable<typeof __VLS_42>]?: (props: typeof __VLS_43) => any;
|
|
290
285
|
} & {
|
|
291
286
|
'tabs-leading'?: (props: typeof __VLS_7) => any;
|
|
292
287
|
} & {
|
|
293
288
|
'tabs-trailing'?: (props: typeof __VLS_21) => any;
|
|
294
289
|
} & {
|
|
295
|
-
header?: (props: typeof
|
|
290
|
+
header?: (props: typeof __VLS_35) => any;
|
|
296
291
|
} & {
|
|
297
|
-
footer?: (props: typeof
|
|
292
|
+
footer?: (props: typeof __VLS_38) => any;
|
|
298
293
|
} & {
|
|
299
|
-
empty?: (props: typeof
|
|
294
|
+
empty?: (props: typeof __VLS_45) => any;
|
|
300
295
|
};
|
|
301
296
|
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
302
297
|
addTab: typeof addTab;
|
|
@@ -405,7 +400,8 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
405
400
|
}[];
|
|
406
401
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
407
402
|
"field-placed": (field: FormFieldDefinition) => any;
|
|
408
|
-
"
|
|
403
|
+
"field-updated": (field: FormFieldDefinition, patch: Partial<FormFieldDefinition>) => any;
|
|
404
|
+
"field-removed": (field: FormFieldDefinition) => any;
|
|
409
405
|
"signed-status-changed": (payload: {
|
|
410
406
|
allSigned: boolean;
|
|
411
407
|
fields: SignatureFieldStatus[];
|
|
@@ -416,7 +412,8 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
416
412
|
"tab-removed": (id: string) => any;
|
|
417
413
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
418
414
|
"onField-placed"?: ((field: FormFieldDefinition) => any) | undefined;
|
|
419
|
-
"
|
|
415
|
+
"onField-updated"?: ((field: FormFieldDefinition, patch: Partial<FormFieldDefinition>) => any) | undefined;
|
|
416
|
+
"onField-removed"?: ((field: FormFieldDefinition) => any) | undefined;
|
|
420
417
|
"onSigned-status-changed"?: ((payload: {
|
|
421
418
|
allSigned: boolean;
|
|
422
419
|
fields: SignatureFieldStatus[];
|
|
@@ -435,8 +432,6 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
435
432
|
stamps: StampDefinition[];
|
|
436
433
|
signatureHandlers: SignatureHandlers;
|
|
437
434
|
viewMode: ViewMode;
|
|
438
|
-
roleColors: Record<string, string>;
|
|
439
|
-
activeRoleId: string | null;
|
|
440
435
|
editablePlacedFields: boolean;
|
|
441
436
|
fieldDefaults: PlacedFieldDefaults;
|
|
442
437
|
selectionItems: ViewerSelectionItem[];
|
|
@@ -4,17 +4,19 @@
|
|
|
4
4
|
:class="{
|
|
5
5
|
'kviewer-form-field--editable': showChrome,
|
|
6
6
|
'kviewer-form-field--placed': isPlaced,
|
|
7
|
-
'kviewer-form-field--selected': props.selected
|
|
7
|
+
'kviewer-form-field--selected': props.selected,
|
|
8
|
+
'kviewer-form-field--readonly': isReadonlyOutsideAuthoring
|
|
8
9
|
}"
|
|
9
10
|
:style="[positionStyle, {
|
|
10
11
|
'--kvw-field-color': chromeColor,
|
|
11
12
|
'--kvw-handle-zoom': handleZoom
|
|
12
13
|
}]"
|
|
13
14
|
:data-element-id="props.field.id"
|
|
15
|
+
@contextmenu="onContextMenu"
|
|
14
16
|
>
|
|
15
17
|
<div
|
|
16
18
|
class="kviewer-form-field__body"
|
|
17
|
-
:class="{ 'kviewer-form-field__body--locked':
|
|
19
|
+
:class="{ 'kviewer-form-field__body--locked': chromeMode === 'authoring' }"
|
|
18
20
|
>
|
|
19
21
|
<FormTextField v-if="props.field.fieldType === 'text'" :field="props.field" />
|
|
20
22
|
<FormCheckbox v-else-if="props.field.fieldType === 'checkbox'" :field="props.field" />
|
|
@@ -30,6 +32,7 @@
|
|
|
30
32
|
:selected="props.selected"
|
|
31
33
|
:scale="props.scale"
|
|
32
34
|
:page-height="props.pageTransform.height"
|
|
35
|
+
:mode="chromeMode ?? 'authoring'"
|
|
33
36
|
@select="emit('select')"
|
|
34
37
|
/>
|
|
35
38
|
|
|
@@ -64,18 +67,27 @@ const state = useViewerState();
|
|
|
64
67
|
const formFields = useFormFields();
|
|
65
68
|
const isPlaced = computed(() => props.field.origin === "placed");
|
|
66
69
|
const isFormEditMode = computed(() => state.formEditMode.value);
|
|
67
|
-
const
|
|
68
|
-
(
|
|
70
|
+
const chromeMode = computed(() => {
|
|
71
|
+
if (isFormEditMode.value) return "authoring";
|
|
72
|
+
if (formFields.editablePlacedFields.value && isPlaced.value && !props.field.readOnly) {
|
|
73
|
+
return "live";
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
76
|
+
});
|
|
77
|
+
const showChrome = computed(() => chromeMode.value !== null);
|
|
78
|
+
const isReadonlyOutsideAuthoring = computed(
|
|
79
|
+
() => props.field.readOnly && !isFormEditMode.value
|
|
69
80
|
);
|
|
81
|
+
function onContextMenu(e) {
|
|
82
|
+
if (chromeMode.value !== "live") return;
|
|
83
|
+
e.preventDefault();
|
|
84
|
+
emit("select");
|
|
85
|
+
}
|
|
70
86
|
const handleZoom = computed(() => {
|
|
71
87
|
const totalZoom = (props.scale || 1) * (state.visualZoom.value || 1);
|
|
72
88
|
return (1 / totalZoom).toFixed(3);
|
|
73
89
|
});
|
|
74
|
-
const chromeColor = computed(() =>
|
|
75
|
-
const id = props.field.roleId;
|
|
76
|
-
if (!id) return "#1677ff";
|
|
77
|
-
return formFields.roleColors.value[id] ?? "#1677ff";
|
|
78
|
-
});
|
|
90
|
+
const chromeColor = computed(() => props.field.fieldColor ?? "#1677ff");
|
|
79
91
|
const positionStyle = computed(() => {
|
|
80
92
|
const rect = props.field.rect;
|
|
81
93
|
const r = convertPdfRectToCanvas(props.pageTransform, [rect[0], rect[1], rect[2], rect[3]]);
|
|
@@ -55,10 +55,18 @@ const signatureUrl = computed(() => {
|
|
|
55
55
|
});
|
|
56
56
|
const hasSigned = computed(() => !!signatureUrl.value);
|
|
57
57
|
const clickToSignEnabled = computed(() => state.clickToSign.value);
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
const liveEditing = computed(
|
|
59
|
+
() => formFields.editablePlacedFields.value && props.field.origin === "placed" && !state.formEditMode.value
|
|
60
|
+
);
|
|
61
|
+
async function onClickSign(e) {
|
|
60
62
|
if (props.field.readOnly || state.readonly.value) return;
|
|
61
63
|
if (formFields.isFieldLocked(props.field.id)) return;
|
|
64
|
+
if (liveEditing.value && (hasSigned.value || !clickToSignEnabled.value)) {
|
|
65
|
+
e.stopPropagation();
|
|
66
|
+
formFields.selectPlacedField(props.field.id);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (!clickToSignEnabled.value) return;
|
|
62
70
|
if (state.signatures.value.length > 0) {
|
|
63
71
|
const sig = state.signatures.value[0];
|
|
64
72
|
if (sig) {
|
|
@@ -6,6 +6,10 @@ type __VLS_Props = {
|
|
|
6
6
|
scale: number;
|
|
7
7
|
/** PDF-space page height (kept for parity; unused here). */
|
|
8
8
|
pageHeight: number;
|
|
9
|
+
/** 'authoring' = form-edit mode (chrome is the whole interactive
|
|
10
|
+
* surface). 'live' = editablePlacedFields outside form-edit mode
|
|
11
|
+
* (chrome is transparent except grip and handles). */
|
|
12
|
+
mode: 'authoring' | 'live';
|
|
9
13
|
};
|
|
10
14
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
11
15
|
select: () => any;
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<!--
|
|
3
|
-
click to select, drag anywhere to move
|
|
4
|
-
|
|
2
|
+
<!-- Two interaction modes. Authoring (form-edit mode): the whole chrome
|
|
3
|
+
is the interactive surface — click to select, drag anywhere to move.
|
|
4
|
+
Live (editablePlacedFields): the surface is transparent so the field
|
|
5
|
+
body stays usable (click-to-sign, text input); management happens on
|
|
6
|
+
the always-visible grip ("handle = manage, body = use"). -->
|
|
5
7
|
<div
|
|
6
8
|
ref="rootEl"
|
|
7
9
|
class="kviewer-placed-chrome"
|
|
8
|
-
:class="{
|
|
10
|
+
:class="{
|
|
11
|
+
'kviewer-placed-chrome--selected': selected,
|
|
12
|
+
'kviewer-placed-chrome--live': mode === 'live'
|
|
13
|
+
}"
|
|
9
14
|
@pointerdown.stop="onChromePointerDown"
|
|
10
15
|
@click.stop
|
|
11
16
|
>
|
|
@@ -17,9 +22,11 @@
|
|
|
17
22
|
}"
|
|
18
23
|
/>
|
|
19
24
|
<div
|
|
20
|
-
v-if="selected"
|
|
25
|
+
v-if="selected || mode === 'live'"
|
|
21
26
|
class="kviewer-placed-chrome__grip"
|
|
22
27
|
:title="field.fieldName"
|
|
28
|
+
@pointerdown.stop="onChromePointerDown"
|
|
29
|
+
@click.stop
|
|
23
30
|
>
|
|
24
31
|
<UIcon name="i-lucide-grip-vertical" class="kviewer-placed-chrome__grip-dots" />
|
|
25
32
|
<span class="kviewer-placed-chrome__grip-label">{{ field.fieldName }}</span>
|
|
@@ -46,7 +53,8 @@ const props = defineProps({
|
|
|
46
53
|
field: { type: Object, required: true },
|
|
47
54
|
selected: { type: Boolean, required: true },
|
|
48
55
|
scale: { type: Number, required: true },
|
|
49
|
-
pageHeight: { type: Number, required: true }
|
|
56
|
+
pageHeight: { type: Number, required: true },
|
|
57
|
+
mode: { type: String, required: true }
|
|
50
58
|
});
|
|
51
59
|
const emit = defineEmits(["select"]);
|
|
52
60
|
const formFields = useFormFields();
|
|
@@ -91,12 +99,16 @@ function onChromePointerDown(e) {
|
|
|
91
99
|
startRect[2] + dx,
|
|
92
100
|
startRect[3] + pdfDy
|
|
93
101
|
];
|
|
94
|
-
formFields.updatePlacedField(props.field.id, { rect: next });
|
|
102
|
+
formFields.updatePlacedField(props.field.id, { rect: next }, { silent: true });
|
|
95
103
|
};
|
|
96
104
|
const onUp = () => {
|
|
97
105
|
window.removeEventListener("pointermove", onMove);
|
|
98
106
|
window.removeEventListener("pointerup", onUp);
|
|
99
|
-
if (!dragging)
|
|
107
|
+
if (!dragging) {
|
|
108
|
+
emit("select");
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
formFields.updatePlacedField(props.field.id, { rect: [...props.field.rect] });
|
|
100
112
|
};
|
|
101
113
|
window.addEventListener("pointermove", onMove);
|
|
102
114
|
window.addEventListener("pointerup", onUp);
|
|
@@ -106,6 +118,7 @@ function onHandlePointerDown(e, name) {
|
|
|
106
118
|
const startY = e.clientY;
|
|
107
119
|
const [sx1, sy1, sx2, sy2] = props.field.rect;
|
|
108
120
|
const s = effectiveScale();
|
|
121
|
+
let resized = false;
|
|
109
122
|
const onMove = (ev) => {
|
|
110
123
|
const dx = (ev.clientX - startX) / s;
|
|
111
124
|
const dy = (ev.clientY - startY) / s;
|
|
@@ -115,11 +128,15 @@ function onHandlePointerDown(e, name) {
|
|
|
115
128
|
if (name.includes("s")) y1 = Math.min(y2 - 4, sy1 + pdfDy);
|
|
116
129
|
if (name.includes("e")) x2 = Math.max(x1 + 4, sx2 + dx);
|
|
117
130
|
if (name.includes("w")) x1 = Math.min(x2 - 4, sx1 + dx);
|
|
118
|
-
|
|
131
|
+
resized = true;
|
|
132
|
+
formFields.updatePlacedField(props.field.id, { rect: [x1, y1, x2, y2] }, { silent: true });
|
|
119
133
|
};
|
|
120
134
|
const onUp = () => {
|
|
121
135
|
window.removeEventListener("pointermove", onMove);
|
|
122
136
|
window.removeEventListener("pointerup", onUp);
|
|
137
|
+
if (resized) {
|
|
138
|
+
formFields.updatePlacedField(props.field.id, { rect: [...props.field.rect] });
|
|
139
|
+
}
|
|
123
140
|
};
|
|
124
141
|
window.addEventListener("pointermove", onMove);
|
|
125
142
|
window.addEventListener("pointerup", onUp);
|
|
@@ -127,5 +144,5 @@ function onHandlePointerDown(e, name) {
|
|
|
127
144
|
</script>
|
|
128
145
|
|
|
129
146
|
<style>
|
|
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}
|
|
147
|
+
.kviewer-placed-chrome{cursor:move;inset:0;pointer-events:auto;position:absolute;touch-action:none}.kviewer-placed-chrome--live{cursor:default;pointer-events: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
148
|
</style>
|
|
@@ -6,6 +6,10 @@ type __VLS_Props = {
|
|
|
6
6
|
scale: number;
|
|
7
7
|
/** PDF-space page height (kept for parity; unused here). */
|
|
8
8
|
pageHeight: number;
|
|
9
|
+
/** 'authoring' = form-edit mode (chrome is the whole interactive
|
|
10
|
+
* surface). 'live' = editablePlacedFields outside form-edit mode
|
|
11
|
+
* (chrome is transparent except grip and handles). */
|
|
12
|
+
mode: 'authoring' | 'live';
|
|
9
13
|
};
|
|
10
14
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
11
15
|
select: () => any;
|
|
@@ -27,10 +27,22 @@ export interface FormFieldsState {
|
|
|
27
27
|
* `addPlacedField` with full control over the field's properties.
|
|
28
28
|
* Returns the created definition. */
|
|
29
29
|
addFormField: (payload: AddFormFieldPayload) => FormFieldDefinition;
|
|
30
|
-
/** Update a form field's rect, name, or flags.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
/** Update a form field's rect, name, or flags. Notifies the field-event
|
|
31
|
+
* sink unless `opts.silent` — pass silent from the public API (the host
|
|
32
|
+
* knows about its own calls) and from high-frequency gesture updates
|
|
33
|
+
* (commit one non-silent update on gesture end instead). */
|
|
34
|
+
updatePlacedField: (id: string, patch: Partial<FormFieldDefinition>, opts?: {
|
|
35
|
+
silent?: boolean;
|
|
36
|
+
}) => void;
|
|
37
|
+
/** Remove a form field and its value. Same `silent` contract as
|
|
38
|
+
* `updatePlacedField`. */
|
|
39
|
+
removePlacedField: (id: string, opts?: {
|
|
40
|
+
silent?: boolean;
|
|
41
|
+
}) => void;
|
|
42
|
+
/** Install the sink that receives user-driven field mutations. Viewer.vue
|
|
43
|
+
* owns it and fans out to the `field-updated` / `field-removed` emits and
|
|
44
|
+
* the embed bridge. Survives document changes (unlike the value sink). */
|
|
45
|
+
setFieldEventSink: (sink: FieldEventSink | null) => void;
|
|
34
46
|
/** All form-field definitions, flattened across pages. */
|
|
35
47
|
getAllFields: () => FormFieldDefinition[];
|
|
36
48
|
/** All user-placed field definitions (used by export). */
|
|
@@ -45,19 +57,6 @@ export interface FormFieldsState {
|
|
|
45
57
|
selectedPlacedFieldId: Ref<string | null>;
|
|
46
58
|
/** Select or deselect a placed field. Pass null to deselect. */
|
|
47
59
|
selectPlacedField: (id: string | null) => void;
|
|
48
|
-
/** Opaque role id new placements are auto-tagged with. The host owns
|
|
49
|
-
* what roles exist; the editor only carries the active id forward to
|
|
50
|
-
* newly placed fields and looks up its color via roleColors. */
|
|
51
|
-
activeRoleId: Ref<string | null>;
|
|
52
|
-
/** Set or clear the active role for upcoming placements. */
|
|
53
|
-
setActiveRole: (id: string | null) => void;
|
|
54
|
-
/** Map from roleId → CSS color, supplied by the host via the KViewer
|
|
55
|
-
* `roleColors` prop. Reading this drives the chrome and placement
|
|
56
|
-
* preview color. The editor never mutates role definitions itself. */
|
|
57
|
-
roleColors: ShallowRef<Record<string, string>>;
|
|
58
|
-
/** Replace the host-provided color map. Called by KViewer when the
|
|
59
|
-
* prop changes. */
|
|
60
|
-
setRoleColors: (colors: Record<string, string>) => void;
|
|
61
60
|
/** Per-field-type presets merged into every tool placement. Host-owned
|
|
62
61
|
* via the KViewer `fieldDefaults` prop or the bridge; programmatic
|
|
63
62
|
* `addFormField` calls are NOT affected (the payload is explicit). */
|
|
@@ -110,5 +109,17 @@ export interface FormFieldsState {
|
|
|
110
109
|
/** Sink signature used by [[setValueWriteSink]]. Receives every user-driven
|
|
111
110
|
* field value write (the primary id AND each mirrored sibling). */
|
|
112
111
|
export type ValueWriteSink = (fieldId: string, value: string | boolean | string[]) => void;
|
|
112
|
+
/** User-driven field mutation, delivered to the sink installed via
|
|
113
|
+
* [[setFieldEventSink]]. `field` is the post-mutation definition for
|
|
114
|
+
* updates and the last-known definition for removals. */
|
|
115
|
+
export type FieldEvent = {
|
|
116
|
+
type: 'updated';
|
|
117
|
+
field: FormFieldDefinition;
|
|
118
|
+
patch: Partial<FormFieldDefinition>;
|
|
119
|
+
} | {
|
|
120
|
+
type: 'removed';
|
|
121
|
+
field: FormFieldDefinition;
|
|
122
|
+
};
|
|
123
|
+
export type FieldEventSink = (event: FieldEvent) => void;
|
|
113
124
|
export declare function provideFormFields(): FormFieldsState;
|
|
114
125
|
export declare function useFormFields(): FormFieldsState;
|
|
@@ -12,16 +12,8 @@ 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
15
|
const placedFieldDefaults = shallowRef({});
|
|
18
16
|
const editablePlacedFields = ref(false);
|
|
19
|
-
function setActiveRole(id) {
|
|
20
|
-
activeRoleId.value = id;
|
|
21
|
-
}
|
|
22
|
-
function setRoleColors(colors) {
|
|
23
|
-
roleColors.value = colors;
|
|
24
|
-
}
|
|
25
17
|
function setPlacedFieldDefaults(defaults) {
|
|
26
18
|
placedFieldDefaults.value = defaults;
|
|
27
19
|
}
|
|
@@ -43,6 +35,10 @@ export function provideFormFields() {
|
|
|
43
35
|
function setValueWriteSink(sink) {
|
|
44
36
|
valueWriteSink = sink;
|
|
45
37
|
}
|
|
38
|
+
let fieldEventSink = null;
|
|
39
|
+
function setFieldEventSink(sink) {
|
|
40
|
+
fieldEventSink = sink;
|
|
41
|
+
}
|
|
46
42
|
let checkboxStyleMap = null;
|
|
47
43
|
let buttonCaptionMap = null;
|
|
48
44
|
let signedSignatureNames = null;
|
|
@@ -297,9 +293,7 @@ export function provideFormFields() {
|
|
|
297
293
|
)
|
|
298
294
|
);
|
|
299
295
|
}
|
|
300
|
-
if (
|
|
301
|
-
def.roleId = activeRoleId.value;
|
|
302
|
-
}
|
|
296
|
+
if (def.meta) def.meta = { ...def.meta };
|
|
303
297
|
if (payload.fieldType === "radio") {
|
|
304
298
|
def.buttonValue = nextRadioOptionValue(fieldName);
|
|
305
299
|
}
|
|
@@ -340,7 +334,8 @@ export function provideFormFields() {
|
|
|
340
334
|
required: payload.required ?? false,
|
|
341
335
|
origin: "placed"
|
|
342
336
|
};
|
|
343
|
-
if (payload.
|
|
337
|
+
if (payload.fieldColor !== void 0) def.fieldColor = payload.fieldColor;
|
|
338
|
+
if (payload.meta !== void 0) def.meta = { ...payload.meta };
|
|
344
339
|
if (payload.defaultValue !== void 0) def.defaultValue = payload.defaultValue;
|
|
345
340
|
if (payload.fontSize !== void 0) def.fontSize = payload.fontSize;
|
|
346
341
|
if (payload.fontName !== void 0) def.fontName = payload.fontName;
|
|
@@ -401,7 +396,7 @@ export function provideFormFields() {
|
|
|
401
396
|
while (used.has(`Option_${n}`)) n += 1;
|
|
402
397
|
return `Option_${n}`;
|
|
403
398
|
}
|
|
404
|
-
function updatePlacedField(id, patch) {
|
|
399
|
+
function updatePlacedField(id, patch, opts) {
|
|
405
400
|
let found;
|
|
406
401
|
for (const [page, defs] of fieldDefinitions.value.entries()) {
|
|
407
402
|
const idx = defs.findIndex((d) => d.id === id);
|
|
@@ -422,12 +417,16 @@ export function provideFormFields() {
|
|
|
422
417
|
fv.fieldType = found.fieldType;
|
|
423
418
|
triggerRef(fieldValues);
|
|
424
419
|
}
|
|
420
|
+
if (!opts?.silent && fieldEventSink) {
|
|
421
|
+
fieldEventSink({ type: "updated", field: found, patch });
|
|
422
|
+
}
|
|
425
423
|
}
|
|
426
|
-
function removePlacedField(id) {
|
|
427
|
-
let removed
|
|
424
|
+
function removePlacedField(id, opts) {
|
|
425
|
+
let removed;
|
|
428
426
|
for (const [page, defs] of fieldDefinitions.value.entries()) {
|
|
429
427
|
const idx = defs.findIndex((d) => d.id === id);
|
|
430
428
|
if (idx === -1) continue;
|
|
429
|
+
removed = defs[idx];
|
|
431
430
|
const nextDefs = defs.slice();
|
|
432
431
|
nextDefs.splice(idx, 1);
|
|
433
432
|
if (nextDefs.length === 0) {
|
|
@@ -435,7 +434,6 @@ export function provideFormFields() {
|
|
|
435
434
|
} else {
|
|
436
435
|
fieldDefinitions.value.set(page, nextDefs);
|
|
437
436
|
}
|
|
438
|
-
removed = true;
|
|
439
437
|
break;
|
|
440
438
|
}
|
|
441
439
|
if (!removed) return;
|
|
@@ -446,6 +444,9 @@ export function provideFormFields() {
|
|
|
446
444
|
if (selectedPlacedFieldId.value === id) {
|
|
447
445
|
selectedPlacedFieldId.value = null;
|
|
448
446
|
}
|
|
447
|
+
if (!opts?.silent && fieldEventSink) {
|
|
448
|
+
fieldEventSink({ type: "removed", field: removed });
|
|
449
|
+
}
|
|
449
450
|
}
|
|
450
451
|
function getPlacedFields() {
|
|
451
452
|
const out = [];
|
|
@@ -477,14 +478,12 @@ export function provideFormFields() {
|
|
|
477
478
|
if (d.fieldType !== "signature") continue;
|
|
478
479
|
const fv = fieldValues.value.get(d.id);
|
|
479
480
|
const signed = typeof fv?.value === "string" && fv.value !== "" || d.signedInSource === true;
|
|
480
|
-
|
|
481
|
+
out.push({
|
|
481
482
|
id: d.id,
|
|
482
483
|
fieldName: d.fieldName,
|
|
483
484
|
pageNumber: d.pageNumber,
|
|
484
485
|
signed
|
|
485
|
-
};
|
|
486
|
-
if (d.roleId) status.roleId = d.roleId;
|
|
487
|
-
out.push(status);
|
|
486
|
+
});
|
|
488
487
|
}
|
|
489
488
|
}
|
|
490
489
|
return out;
|
|
@@ -556,11 +555,8 @@ export function provideFormFields() {
|
|
|
556
555
|
resetValues,
|
|
557
556
|
reset,
|
|
558
557
|
setValueWriteSink,
|
|
558
|
+
setFieldEventSink,
|
|
559
559
|
applyFieldValueFromExternal,
|
|
560
|
-
activeRoleId,
|
|
561
|
-
setActiveRole,
|
|
562
|
-
roleColors,
|
|
563
|
-
setRoleColors,
|
|
564
560
|
placedFieldDefaults,
|
|
565
561
|
setPlacedFieldDefaults,
|
|
566
562
|
editablePlacedFields,
|
|
@@ -21,7 +21,8 @@ type EventHandler<E extends EventName> = (payload: EventPayloads[E]) => void;
|
|
|
21
21
|
* an iframe. Wraps `postMessage` into a Promise-based RPC mirroring the
|
|
22
22
|
* viewer's exposed methods, and surfaces iframe-emitted events
|
|
23
23
|
* (`ready`, `formEditMode-changed`, `viewedPages-changed`, `all-pages-read`,
|
|
24
|
-
* `field-placed`, `signedStatus-changed`)
|
|
24
|
+
* `field-placed`, `field-updated`, `field-removed`, `signedStatus-changed`)
|
|
25
|
+
* via `on()`.
|
|
25
26
|
*/
|
|
26
27
|
export declare class KViewerEmbedClient {
|
|
27
28
|
private readonly iframe;
|
|
@@ -68,7 +69,8 @@ export declare class KViewerEmbedClient {
|
|
|
68
69
|
* a push-style signal subscribe via `on('signedStatus-changed', …)`. */
|
|
69
70
|
getSignedStatus(): Promise<boolean>;
|
|
70
71
|
/** Per-widget signed-state snapshot (id, fieldName, pageNumber,
|
|
71
|
-
* signed
|
|
72
|
+
* signed). Correlate to host data by `id` (see the `meta` bag on
|
|
73
|
+
* `getFormFields()` for grouping, e.g. by signer). */
|
|
72
74
|
getSignatureFieldStatus(): Promise<SignatureFieldStatus[]>;
|
|
73
75
|
getFormEditMode(): Promise<boolean>;
|
|
74
76
|
setFormEditMode(enabled: boolean): Promise<void>;
|
|
@@ -104,7 +104,8 @@ export class KViewerEmbedClient {
|
|
|
104
104
|
return this.call("getSignedStatus");
|
|
105
105
|
}
|
|
106
106
|
/** Per-widget signed-state snapshot (id, fieldName, pageNumber,
|
|
107
|
-
* signed
|
|
107
|
+
* signed). Correlate to host data by `id` (see the `meta` bag on
|
|
108
|
+
* `getFormFields()` for grouping, e.g. by signer). */
|
|
108
109
|
getSignatureFieldStatus() {
|
|
109
110
|
return this.call("getSignatureFieldStatus");
|
|
110
111
|
}
|
|
@@ -27,6 +27,8 @@ export interface KViewerApi {
|
|
|
27
27
|
setFieldDefaults: (defaults: PlacedFieldDefaults) => void;
|
|
28
28
|
setClickToSign: (enabled: boolean) => void;
|
|
29
29
|
onFieldPlaced: (cb: (field: FormFieldDefinition) => void) => () => void;
|
|
30
|
+
onFieldUpdated: (cb: (field: FormFieldDefinition, patch: Partial<FormFieldDefinition>) => void) => () => void;
|
|
31
|
+
onFieldRemoved: (cb: (field: FormFieldDefinition) => void) => () => void;
|
|
30
32
|
getSignedStatus: () => boolean;
|
|
31
33
|
getSignatureFieldStatus: () => SignatureFieldStatus[];
|
|
32
34
|
onSignedStatusChanged: (cb: (payload: {
|
|
@@ -202,6 +202,22 @@ export function useKViewerEmbedBridge(viewerRef, options) {
|
|
|
202
202
|
)
|
|
203
203
|
);
|
|
204
204
|
}
|
|
205
|
+
if (typeof viewer.onFieldUpdated === "function") {
|
|
206
|
+
events.push("field-updated");
|
|
207
|
+
stopViewerStateWatches.push(
|
|
208
|
+
viewer.onFieldUpdated(
|
|
209
|
+
(field, patch) => postEvent("field-updated", { field, patch })
|
|
210
|
+
)
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
if (typeof viewer.onFieldRemoved === "function") {
|
|
214
|
+
events.push("field-removed");
|
|
215
|
+
stopViewerStateWatches.push(
|
|
216
|
+
viewer.onFieldRemoved(
|
|
217
|
+
(field) => postEvent("field-removed", { field })
|
|
218
|
+
)
|
|
219
|
+
);
|
|
220
|
+
}
|
|
205
221
|
if (typeof viewer.onSignedStatusChanged === "function") {
|
|
206
222
|
events.push("signedStatus-changed");
|
|
207
223
|
stopViewerStateWatches.push(
|
|
@@ -150,6 +150,18 @@ export interface EventPayloads {
|
|
|
150
150
|
'field-placed': {
|
|
151
151
|
field: FormFieldDefinition;
|
|
152
152
|
};
|
|
153
|
+
/** The user changed a field through the viewer UI (moved/resized it or
|
|
154
|
+
* edited its properties). Not fired for `updateFormField()` calls.
|
|
155
|
+
* Continuous edits may fire repeatedly — treat as an upsert. */
|
|
156
|
+
'field-updated': {
|
|
157
|
+
field: FormFieldDefinition;
|
|
158
|
+
patch: Partial<FormFieldDefinition>;
|
|
159
|
+
};
|
|
160
|
+
/** The user removed a field through the viewer UI. Not fired for
|
|
161
|
+
* `removeFormField()` calls. `field` is the last-known definition. */
|
|
162
|
+
'field-removed': {
|
|
163
|
+
field: FormFieldDefinition;
|
|
164
|
+
};
|
|
153
165
|
/** A signature field's signed state changed (signed, cleared, added,
|
|
154
166
|
* removed, or renamed). Push-style counterpart to `getSignedStatus()`. */
|
|
155
167
|
'signedStatus-changed': {
|