@servicetitan/dte-pdf-editor 1.56.0 → 1.58.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/README.md +25 -1
- package/dist/components/display-conditions/condition-row.js +2 -2
- package/dist/components/pdf-fields-overlay/pdf-overlay-field-calculated.d.ts.map +1 -1
- package/dist/components/pdf-fields-overlay/pdf-overlay-field-calculated.js +2 -2
- package/dist/components/pdf-fields-overlay/pdf-overlay-field-calculated.js.map +1 -1
- package/dist/components/pdf-fields-overlay/pdf-overlay-field-data-model.d.ts.map +1 -1
- package/dist/components/pdf-fields-overlay/pdf-overlay-field-data-model.js +2 -1
- package/dist/components/pdf-fields-overlay/pdf-overlay-field-data-model.js.map +1 -1
- package/dist/components/pdf-fields-overlay/pdf-overlay-field-e-sign.d.ts.map +1 -1
- package/dist/components/pdf-fields-overlay/pdf-overlay-field-e-sign.js +1 -1
- package/dist/components/pdf-fields-overlay/pdf-overlay-field-e-sign.js.map +1 -1
- package/dist/components/pdf-fields-overlay/pdf-overlay-field-fillable.d.ts.map +1 -1
- package/dist/components/pdf-fields-overlay/pdf-overlay-field-fillable.js +14 -7
- package/dist/components/pdf-fields-overlay/pdf-overlay-field-fillable.js.map +1 -1
- package/dist/components/pdf-fields-overlay/pdf-overlay-field-generic.js +9 -1
- package/dist/components/pdf-fields-overlay/pdf-overlay-field-generic.js.map +1 -1
- package/dist/components/pdf-fields-overlay/pdf-overlay-field.d.ts.map +1 -1
- package/dist/components/pdf-fields-overlay/pdf-overlay-field.js +5 -11
- package/dist/components/pdf-fields-overlay/pdf-overlay-field.js.map +1 -1
- package/dist/components/pdf-view/pdf-view-calculated.d.ts.map +1 -1
- package/dist/components/pdf-view/pdf-view-calculated.js +2 -2
- package/dist/components/pdf-view/pdf-view-calculated.js.map +1 -1
- package/dist/components/pdf-view/pdf-view-data-model.js +2 -2
- package/dist/components/pdf-view/pdf-view-data-model.js.map +1 -1
- package/dist/components/pdf-view/pdf-view-e-sign.d.ts.map +1 -1
- package/dist/components/pdf-view/pdf-view-e-sign.js +1 -1
- package/dist/components/pdf-view/pdf-view-e-sign.js.map +1 -1
- package/dist/components/pdf-view/pdf-view-field-container.d.ts.map +1 -1
- package/dist/components/pdf-view/pdf-view-field-container.js +5 -15
- package/dist/components/pdf-view/pdf-view-field-container.js.map +1 -1
- package/dist/components/pdf-view/pdf-view-fillable.d.ts.map +1 -1
- package/dist/components/pdf-view/pdf-view-fillable.js +22 -33
- package/dist/components/pdf-view/pdf-view-fillable.js.map +1 -1
- package/dist/components/pdf-view/pdf-view-generic.js +1 -1
- package/dist/components/pdf-view/pdf-view-generic.js.map +1 -1
- package/dist/components/pdf-view/pdf-view.d.ts.map +1 -1
- package/dist/components/pdf-view/pdf-view.js +1 -1
- package/dist/components/pdf-view/pdf-view.js.map +1 -1
- package/dist/constants/field.constants.d.ts +3 -0
- package/dist/constants/field.constants.d.ts.map +1 -1
- package/dist/constants/field.constants.js +3 -0
- package/dist/constants/field.constants.js.map +1 -1
- package/dist/interface/types.d.ts +2 -0
- package/dist/interface/types.d.ts.map +1 -1
- package/dist/interface/types.js.map +1 -1
- package/dist/utils/field/get-field-bounds-style.utils.d.ts +20 -0
- package/dist/utils/field/get-field-bounds-style.utils.d.ts.map +1 -0
- package/dist/utils/field/get-field-bounds-style.utils.js +53 -0
- package/dist/utils/field/get-field-bounds-style.utils.js.map +1 -0
- package/dist/utils/field/get-field-text-clip.utils.d.ts +13 -0
- package/dist/utils/field/get-field-text-clip.utils.d.ts.map +1 -0
- package/dist/utils/field/get-field-text-clip.utils.js +25 -0
- package/dist/utils/field/get-field-text-clip.utils.js.map +1 -0
- package/dist/utils/field/get-field-typography.utils.d.ts +10 -0
- package/dist/utils/field/get-field-typography.utils.d.ts.map +1 -0
- package/dist/utils/field/get-field-typography.utils.js +20 -0
- package/dist/utils/field/get-field-typography.utils.js.map +1 -0
- package/dist/utils/field/index.d.ts +3 -0
- package/dist/utils/field/index.d.ts.map +1 -1
- package/dist/utils/field/index.js +3 -0
- package/dist/utils/field/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/display-conditions/condition-row.tsx +2 -2
- package/src/components/pdf-fields-overlay/pdf-overlay-field-calculated.tsx +5 -2
- package/src/components/pdf-fields-overlay/pdf-overlay-field-data-model.tsx +2 -1
- package/src/components/pdf-fields-overlay/pdf-overlay-field-e-sign.tsx +3 -1
- package/src/components/pdf-fields-overlay/pdf-overlay-field-fillable.tsx +28 -8
- package/src/components/pdf-fields-overlay/pdf-overlay-field-generic.tsx +9 -1
- package/src/components/pdf-fields-overlay/pdf-overlay-field.tsx +9 -13
- package/src/components/pdf-view/pdf-view-calculated.tsx +9 -2
- package/src/components/pdf-view/pdf-view-data-model.tsx +2 -2
- package/src/components/pdf-view/pdf-view-e-sign.tsx +3 -4
- package/src/components/pdf-view/pdf-view-field-container.tsx +6 -16
- package/src/components/pdf-view/pdf-view-fillable.tsx +29 -28
- package/src/components/pdf-view/pdf-view-generic.tsx +1 -1
- package/src/components/pdf-view/pdf-view.tsx +4 -1
- package/src/constants/field.constants.ts +5 -0
- package/src/interface/types.ts +2 -0
- package/src/styles/inline-editable.css +1 -0
- package/src/styles/pdf-field-overlay.css +80 -27
- package/src/utils/field/__tests__/get-field-bounds-style.utils.test.ts +50 -0
- package/src/utils/field/__tests__/get-field-text-clip.utils.test.ts +43 -0
- package/src/utils/field/__tests__/get-field-typography.utils.test.ts +56 -0
- package/src/utils/field/get-field-bounds-style.utils.ts +65 -0
- package/src/utils/field/get-field-text-clip.utils.ts +31 -0
- package/src/utils/field/get-field-typography.utils.ts +23 -0
- package/src/utils/field/index.ts +3 -0
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { FC, useMemo } from 'react';
|
|
2
2
|
import { DataModelValues, PdfField, PdfViewMode } from '../../interface/types';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
evaluateFormula,
|
|
5
|
+
formatCalculatedResult,
|
|
6
|
+
getFieldTextClipClassName,
|
|
7
|
+
resolvePdfDataValues,
|
|
8
|
+
} from '../../utils';
|
|
4
9
|
|
|
5
10
|
interface PdfViewCalculatedProps {
|
|
6
11
|
field: PdfField;
|
|
@@ -33,5 +38,7 @@ export const PdfViewCalculated: FC<PdfViewCalculatedProps> = ({
|
|
|
33
38
|
const shouldShowPreviewFallback = isPreview && displayValue === '';
|
|
34
39
|
const content = shouldShowPreviewFallback ? field.label || 'Calculated Field' : displayValue;
|
|
35
40
|
|
|
36
|
-
return
|
|
41
|
+
return (
|
|
42
|
+
<div className={`dte-pdf-field-value ${getFieldTextClipClassName(field)}`}>{content}</div>
|
|
43
|
+
);
|
|
37
44
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { FC } from 'react';
|
|
2
2
|
import { DataModelValues, PdfField } from '../../interface/types';
|
|
3
|
-
import { nl2br, resolvePdfDataValues } from '../../utils';
|
|
3
|
+
import { getFieldTextClipClassName, nl2br, resolvePdfDataValues } from '../../utils';
|
|
4
4
|
|
|
5
5
|
interface PdfViewDataModelProps {
|
|
6
6
|
field: PdfField;
|
|
@@ -16,7 +16,7 @@ export const PdfViewDataModel: FC<PdfViewDataModelProps> = ({ data, field }) =>
|
|
|
16
16
|
*/
|
|
17
17
|
return (
|
|
18
18
|
<div
|
|
19
|
-
className=
|
|
19
|
+
className={`dte-pdf-field-value ${getFieldTextClipClassName(field)}`}
|
|
20
20
|
// eslint-disable-next-line react/no-danger, @typescript-eslint/naming-convention
|
|
21
21
|
dangerouslySetInnerHTML={{ __html: nl2br(resolvedValue ?? '') }}
|
|
22
22
|
/>
|
|
@@ -9,10 +9,9 @@ export const PdfViewESign: FC<PdfViewESignProps> = ({ field }) => {
|
|
|
9
9
|
return (
|
|
10
10
|
<span>
|
|
11
11
|
{field.label} {field.required ? '*' : ''}
|
|
12
|
-
<div
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
>{`{{${field.path}}}`}</div>
|
|
12
|
+
<div className="dte-pdf-field-value" style={{ fontSize: '8px' }}>
|
|
13
|
+
{field.path}
|
|
14
|
+
</div>
|
|
16
15
|
</span>
|
|
17
16
|
);
|
|
18
17
|
};
|
|
@@ -3,9 +3,8 @@ import { DataModelValues, PdfField } from '../../interface/types';
|
|
|
3
3
|
import {
|
|
4
4
|
evaluateDisplayCondition,
|
|
5
5
|
getFieldBackgroundColor,
|
|
6
|
-
|
|
6
|
+
getFieldBoundsStyle,
|
|
7
7
|
getPagePosition,
|
|
8
|
-
supportsFieldFontSize,
|
|
9
8
|
} from '../../utils';
|
|
10
9
|
|
|
11
10
|
interface PdfViewFieldContainer {
|
|
@@ -35,21 +34,12 @@ export const PdfViewFieldContainer: FC<PropsWithChildren<PdfViewFieldContainer>>
|
|
|
35
34
|
const bgColor = getFieldBackgroundColor(field.recipient, recipientsColors);
|
|
36
35
|
return (
|
|
37
36
|
<div
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
padding: '4px',
|
|
43
|
-
background: bgColor,
|
|
44
|
-
left: `${pagePos.left + field.x}px`,
|
|
45
|
-
top: `${pagePos.top + field.y}px`,
|
|
46
|
-
width: `${field.width}px`,
|
|
47
|
-
height: `${field.height}px`,
|
|
48
|
-
...(supportsFieldFontSize(field)
|
|
49
|
-
? { fontSize: `${getFieldFontSizeInPixels(field)}px` }
|
|
50
|
-
: {}),
|
|
37
|
+
className="dte-pdf-field-view"
|
|
38
|
+
style={getFieldBoundsStyle(field, {
|
|
39
|
+
pageOffset: pagePos,
|
|
40
|
+
backgroundColor: bgColor,
|
|
51
41
|
borderColor: error ? 'red' : undefined,
|
|
52
|
-
}}
|
|
42
|
+
})}
|
|
53
43
|
>
|
|
54
44
|
{children}
|
|
55
45
|
{error && (
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { FC } from 'react';
|
|
2
2
|
import { DataChangePayload, DataModelValues, PdfField, PdfViewMode } from '../../interface/types';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
dateToUtcZero,
|
|
5
|
+
getFieldPlaceholderText,
|
|
6
|
+
getFieldTextClipClassName,
|
|
7
|
+
toDateInputValue,
|
|
8
|
+
} from '../../utils';
|
|
4
9
|
|
|
5
10
|
interface PdfViewFillableProps {
|
|
6
11
|
field: PdfField;
|
|
@@ -21,10 +26,13 @@ interface FillableTextInputProps {
|
|
|
21
26
|
|
|
22
27
|
const fillableTextStyles = {
|
|
23
28
|
background: 'inherit',
|
|
24
|
-
width: '
|
|
25
|
-
height: '
|
|
26
|
-
|
|
29
|
+
width: '100%',
|
|
30
|
+
height: '100%',
|
|
31
|
+
border: 'none',
|
|
27
32
|
fontSize: 'inherit',
|
|
33
|
+
boxSizing: 'border-box' as const,
|
|
34
|
+
padding: 0,
|
|
35
|
+
margin: 0,
|
|
28
36
|
} as const;
|
|
29
37
|
|
|
30
38
|
export const FillableTextInput: FC<FillableTextInputProps> = ({
|
|
@@ -38,6 +46,7 @@ export const FillableTextInput: FC<FillableTextInputProps> = ({
|
|
|
38
46
|
<input
|
|
39
47
|
readOnly={readOnly}
|
|
40
48
|
type="text"
|
|
49
|
+
className={`dte-pdf-field-fillable ${getFieldTextClipClassName(field)}`}
|
|
41
50
|
style={fillableTextStyles}
|
|
42
51
|
disabled={disabled}
|
|
43
52
|
id={field.path}
|
|
@@ -59,6 +68,7 @@ export const FillableTextareaInput: FC<FillableTextInputProps> = ({
|
|
|
59
68
|
return (
|
|
60
69
|
<textarea
|
|
61
70
|
readOnly={readOnly}
|
|
71
|
+
className={`dte-pdf-field-fillable ${getFieldTextClipClassName(field)}`}
|
|
62
72
|
style={{ ...fillableTextStyles, resize: 'none' }}
|
|
63
73
|
disabled={disabled}
|
|
64
74
|
id={field.path}
|
|
@@ -96,20 +106,20 @@ export const PdfViewFillable: FC<PdfViewFillableProps> = ({
|
|
|
96
106
|
if (field.subType === 'checkbox') {
|
|
97
107
|
return (
|
|
98
108
|
<div
|
|
109
|
+
className="dte-pdf-field-content"
|
|
99
110
|
style={{
|
|
100
111
|
display: 'flex',
|
|
101
112
|
alignItems: 'center',
|
|
102
113
|
background: 'inherit',
|
|
103
|
-
width: 'inherit',
|
|
104
|
-
height: 'inherit',
|
|
105
114
|
}}
|
|
106
115
|
>
|
|
107
116
|
<input
|
|
117
|
+
title={field.label}
|
|
108
118
|
style={{
|
|
109
|
-
width:
|
|
110
|
-
height:
|
|
119
|
+
width: field.width,
|
|
120
|
+
height: field.height,
|
|
111
121
|
margin: 0,
|
|
112
|
-
|
|
122
|
+
border: 'none',
|
|
113
123
|
}}
|
|
114
124
|
type="checkbox"
|
|
115
125
|
checked={resolvedValue ?? false}
|
|
@@ -125,20 +135,21 @@ export const PdfViewFillable: FC<PdfViewFillableProps> = ({
|
|
|
125
135
|
if (field.subType === 'radio') {
|
|
126
136
|
return (
|
|
127
137
|
<div
|
|
138
|
+
className="dte-pdf-field-content"
|
|
128
139
|
style={{
|
|
129
140
|
display: 'flex',
|
|
130
141
|
alignItems: 'center',
|
|
131
142
|
background: 'inherit',
|
|
132
|
-
width: '
|
|
133
|
-
height: '
|
|
143
|
+
width: '100%',
|
|
144
|
+
height: '100%',
|
|
134
145
|
}}
|
|
135
146
|
>
|
|
136
147
|
<input
|
|
137
148
|
style={{
|
|
138
|
-
width:
|
|
139
|
-
height:
|
|
149
|
+
width: field.width,
|
|
150
|
+
height: field.height,
|
|
140
151
|
margin: 0,
|
|
141
|
-
|
|
152
|
+
border: 'none',
|
|
142
153
|
}}
|
|
143
154
|
type="radio"
|
|
144
155
|
value={field.id}
|
|
@@ -158,13 +169,8 @@ export const PdfViewFillable: FC<PdfViewFillableProps> = ({
|
|
|
158
169
|
<input
|
|
159
170
|
readOnly={readOnly}
|
|
160
171
|
type="date"
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
width: 'inherit',
|
|
164
|
-
height: 'inherit',
|
|
165
|
-
borderColor: 'inherit',
|
|
166
|
-
fontSize: 'inherit',
|
|
167
|
-
}}
|
|
172
|
+
className={`dte-pdf-field-fillable ${getFieldTextClipClassName(field)}`}
|
|
173
|
+
style={fillableTextStyles}
|
|
168
174
|
disabled={!isFillable}
|
|
169
175
|
id={field.path}
|
|
170
176
|
name={field.path}
|
|
@@ -183,13 +189,8 @@ export const PdfViewFillable: FC<PdfViewFillableProps> = ({
|
|
|
183
189
|
<input
|
|
184
190
|
readOnly={readOnly}
|
|
185
191
|
type="number"
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
width: 'inherit',
|
|
189
|
-
height: 'inherit',
|
|
190
|
-
borderColor: 'inherit',
|
|
191
|
-
fontSize: 'inherit',
|
|
192
|
-
}}
|
|
192
|
+
className={`dte-pdf-field-fillable ${getFieldTextClipClassName(field)}`}
|
|
193
|
+
style={fillableTextStyles}
|
|
193
194
|
disabled={!isFillable}
|
|
194
195
|
id={field.path}
|
|
195
196
|
name={field.path}
|
|
@@ -24,7 +24,7 @@ interface GenericFieldTextProps {
|
|
|
24
24
|
|
|
25
25
|
export const GenericFieldText: FC<GenericFieldTextProps> = ({ data }) => {
|
|
26
26
|
return (
|
|
27
|
-
<div className="dte-pdf-field-value">
|
|
27
|
+
<div className="dte-pdf-field-value dte-pdf-field-text--wrapped">
|
|
28
28
|
<pre className="inline-editable-html-text">{data?.value ?? ''}</pre>
|
|
29
29
|
</div>
|
|
30
30
|
);
|
|
@@ -111,7 +111,10 @@ export const PdfView: FC<PdfViewProps> = ({
|
|
|
111
111
|
>
|
|
112
112
|
{(field.type === FieldTypeEnum.dataModel ||
|
|
113
113
|
field.type === FieldTypeEnum.forms) && (
|
|
114
|
-
<PdfViewDataModel
|
|
114
|
+
<PdfViewDataModel
|
|
115
|
+
field={field}
|
|
116
|
+
data={viewMode === 'preview' ? previewData : data}
|
|
117
|
+
/>
|
|
115
118
|
)}
|
|
116
119
|
{field.type === FieldTypeEnum.eSign && (
|
|
117
120
|
<PdfViewESign field={field} />
|
|
@@ -20,6 +20,11 @@ export const FIELD_CONSTANTS = {
|
|
|
20
20
|
|
|
21
21
|
export const DEFAULT_FIELD_FONT_SIZE = 8;
|
|
22
22
|
|
|
23
|
+
/** Matches the PdfSharp default face used by `/conversions/templatetopdf`. */
|
|
24
|
+
export const DEFAULT_FIELD_FONT_FAMILY = 'Arial, Helvetica, sans-serif';
|
|
25
|
+
|
|
26
|
+
export const DEFAULT_FIELD_FONT_WEIGHT = 'normal' as const;
|
|
27
|
+
|
|
23
28
|
export const FIELD_FONT_SIZE_OPTIONS = Array.from({ length: 17 }, (_, index) => {
|
|
24
29
|
return 4 + index;
|
|
25
30
|
});
|
package/src/interface/types.ts
CHANGED
|
@@ -11,38 +11,34 @@
|
|
|
11
11
|
pointer-events: auto;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
font-size: var(--typescale-1, 14px);
|
|
20
|
-
padding: var(--spacing-half, 4px);
|
|
21
|
-
box-sizing: content-box;
|
|
14
|
+
/* Editor-only chrome. Layout metrics come from getFieldBoundsStyle. */
|
|
15
|
+
.dte-pdf-field-editor {
|
|
16
|
+
outline: 1px dashed #999;
|
|
17
|
+
outline-offset: 0;
|
|
18
|
+
border: none;
|
|
22
19
|
pointer-events: auto;
|
|
23
20
|
}
|
|
24
21
|
|
|
25
|
-
.dte-pdf-field.--selected {
|
|
26
|
-
|
|
22
|
+
.dte-pdf-field-editor.--selected {
|
|
23
|
+
outline: 2px solid var(--border-color-active);
|
|
27
24
|
cursor: move;
|
|
28
25
|
}
|
|
29
26
|
|
|
30
|
-
.dte-pdf-field.--unselected {
|
|
27
|
+
.dte-pdf-field-editor.--unselected {
|
|
31
28
|
cursor: pointer;
|
|
32
29
|
}
|
|
33
30
|
|
|
34
|
-
.dte-pdf-field.--dragging {
|
|
31
|
+
.dte-pdf-field-editor.--dragging {
|
|
35
32
|
opacity: 0.5;
|
|
36
|
-
will-change: left, top;
|
|
37
33
|
}
|
|
38
34
|
|
|
39
|
-
.dte-pdf-field.--colored:not(.--selected) {
|
|
40
|
-
|
|
35
|
+
.dte-pdf-field-editor.--colored:not(.--selected) {
|
|
36
|
+
outline-color: var(--border-color-active) !important;
|
|
41
37
|
box-shadow: 0 0 2px 0 var(--border-color-active);
|
|
42
38
|
}
|
|
43
39
|
|
|
44
|
-
.dte-pdf-field.--error {
|
|
45
|
-
|
|
40
|
+
.dte-pdf-field-editor.--error {
|
|
41
|
+
outline-color: var(--danger) !important;
|
|
46
42
|
box-shadow: 0 0 4px 0 var(--danger);
|
|
47
43
|
}
|
|
48
44
|
|
|
@@ -82,38 +78,95 @@
|
|
|
82
78
|
height: 20px;
|
|
83
79
|
}
|
|
84
80
|
|
|
85
|
-
.dte-pdf-field:hover .dte-add-field-button {
|
|
81
|
+
.dte-pdf-field-editor:hover .dte-add-field-button {
|
|
86
82
|
display: block;
|
|
87
83
|
}
|
|
88
84
|
|
|
89
85
|
.dte-pdf-field-view {
|
|
90
86
|
border: none;
|
|
87
|
+
outline: none;
|
|
91
88
|
cursor: default;
|
|
89
|
+
pointer-events: auto;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.dte-pdf-field-content {
|
|
93
|
+
width: 100%;
|
|
94
|
+
min-width: 0;
|
|
95
|
+
min-height: 0;
|
|
92
96
|
overflow: hidden;
|
|
97
|
+
line-height: 1;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/*
|
|
101
|
+
* Printed PDF text modes (no ellipsis — hard clip like PdfSharp).
|
|
102
|
+
*/
|
|
103
|
+
.dte-pdf-field-text--single-line {
|
|
104
|
+
width: 100%;
|
|
105
|
+
min-width: 0;
|
|
106
|
+
line-height: 1;
|
|
93
107
|
white-space: nowrap;
|
|
94
|
-
|
|
95
|
-
justify-content: left;
|
|
96
|
-
text-overflow: ellipsis;
|
|
108
|
+
overflow: hidden;
|
|
97
109
|
}
|
|
98
110
|
|
|
99
|
-
.dte-pdf-field-
|
|
100
|
-
|
|
101
|
-
|
|
111
|
+
.dte-pdf-field-text--multiline {
|
|
112
|
+
width: 100%;
|
|
113
|
+
height: 100%;
|
|
114
|
+
min-width: 0;
|
|
115
|
+
min-height: 0;
|
|
116
|
+
line-height: 1;
|
|
117
|
+
white-space: pre-wrap;
|
|
118
|
+
overflow-wrap: break-word;
|
|
119
|
+
overflow: hidden;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.dte-pdf-field-text--wrapped {
|
|
123
|
+
width: 100%;
|
|
124
|
+
min-width: 0;
|
|
125
|
+
line-height: 1;
|
|
102
126
|
white-space: normal;
|
|
127
|
+
overflow-wrap: break-word;
|
|
103
128
|
overflow: hidden;
|
|
104
|
-
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.dte-pdf-field-value {
|
|
132
|
+
color: #333;
|
|
133
|
+
font: inherit;
|
|
105
134
|
}
|
|
106
135
|
|
|
107
136
|
.dte-pdf-field-fillable {
|
|
108
137
|
width: 100%;
|
|
109
138
|
height: 100%;
|
|
139
|
+
min-width: 0;
|
|
140
|
+
min-height: 0;
|
|
141
|
+
box-sizing: border-box;
|
|
110
142
|
background: inherit;
|
|
111
143
|
border: none;
|
|
112
|
-
|
|
113
|
-
margin: var(--spacing-0
|
|
144
|
+
padding: var(--spacing-0);
|
|
145
|
+
margin: var(--spacing-0);
|
|
146
|
+
line-height: 1;
|
|
114
147
|
font-size: inherit;
|
|
115
148
|
}
|
|
116
149
|
|
|
150
|
+
.dte-pdf-field-editor .dte-pdf-field-fillable {
|
|
151
|
+
pointer-events: none;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.dte-pdf-field-view .dte-pdf-field-fillable {
|
|
155
|
+
pointer-events: auto;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.dte-pdf-field-fillable.dte-pdf-field-text--single-line {
|
|
159
|
+
white-space: nowrap;
|
|
160
|
+
overflow: hidden;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.dte-pdf-field-fillable.dte-pdf-field-text--multiline {
|
|
164
|
+
white-space: pre-wrap;
|
|
165
|
+
overflow-wrap: break-word;
|
|
166
|
+
overflow: hidden;
|
|
167
|
+
resize: none;
|
|
168
|
+
}
|
|
169
|
+
|
|
117
170
|
.dte-pdf-field-generic-table {
|
|
118
171
|
padding: var(--spacing-0, 0);
|
|
119
172
|
margin: var(--spacing-0, 0);
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { FieldTypeEnum, PdfField } from '../../../interface/types';
|
|
2
|
+
import { getFieldBoundsStyle } from '../get-field-bounds-style.utils';
|
|
3
|
+
|
|
4
|
+
const buildField = (overrides: Partial<PdfField> = {}): PdfField => ({
|
|
5
|
+
id: 'field-1',
|
|
6
|
+
type: FieldTypeEnum.fillable,
|
|
7
|
+
subType: 'text',
|
|
8
|
+
x: 10,
|
|
9
|
+
y: 20,
|
|
10
|
+
page: 1,
|
|
11
|
+
label: 'Label',
|
|
12
|
+
width: 200,
|
|
13
|
+
height: 25,
|
|
14
|
+
...overrides,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
describe('getFieldBoundsStyle', () => {
|
|
18
|
+
const subject = (field: PdfField, options?: Parameters<typeof getFieldBoundsStyle>[1]) =>
|
|
19
|
+
getFieldBoundsStyle(field, options);
|
|
20
|
+
|
|
21
|
+
test('positions the field using page offset and field coordinates', () => {
|
|
22
|
+
expect(subject(buildField(), { pageOffset: { left: 5, top: 15 } })).toMatchObject({
|
|
23
|
+
left: 15,
|
|
24
|
+
top: 35,
|
|
25
|
+
width: 200,
|
|
26
|
+
height: 25,
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('matches printed PDF clip rect layout', () => {
|
|
31
|
+
expect(subject(buildField())).toMatchObject({
|
|
32
|
+
boxSizing: 'border-box',
|
|
33
|
+
padding: 0,
|
|
34
|
+
alignItems: 'flex-start',
|
|
35
|
+
justifyContent: 'flex-start',
|
|
36
|
+
overflow: 'hidden',
|
|
37
|
+
lineHeight: 1,
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('converts font size from points to editor pixels', () => {
|
|
42
|
+
const style = subject(buildField({ styles: { fontSize: 12 } }));
|
|
43
|
+
expect(style.fontSize).toBe(`${(12 * 980) / 612}px`);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('omits font size for field types that do not support it', () => {
|
|
47
|
+
const style = subject(buildField({ type: FieldTypeEnum.eSign, subType: 'signature' }));
|
|
48
|
+
expect(style.fontSize).toBeUndefined();
|
|
49
|
+
});
|
|
50
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { FieldTypeEnum, PdfField } from '../../../interface/types';
|
|
2
|
+
import { getFieldTextClipClassName, getFieldTextClipMode } from '../get-field-text-clip.utils';
|
|
3
|
+
|
|
4
|
+
const buildField = (overrides: Partial<PdfField> = {}): PdfField => ({
|
|
5
|
+
id: 'field-1',
|
|
6
|
+
type: FieldTypeEnum.fillable,
|
|
7
|
+
subType: 'text',
|
|
8
|
+
x: 0,
|
|
9
|
+
y: 0,
|
|
10
|
+
page: 1,
|
|
11
|
+
label: 'Label',
|
|
12
|
+
width: 200,
|
|
13
|
+
height: 25,
|
|
14
|
+
...overrides,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
describe('getFieldTextClipMode', () => {
|
|
18
|
+
const subject = (field: PdfField) => getFieldTextClipMode(field);
|
|
19
|
+
|
|
20
|
+
test('uses multiline for multiline fillable fields', () => {
|
|
21
|
+
expect(subject(buildField({ multiline: true }))).toBe('multiline');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('uses single-line for standard fillable fields', () => {
|
|
25
|
+
expect(subject(buildField())).toBe('single-line');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test('uses wrapped for data model and generic text fields', () => {
|
|
29
|
+
expect(subject(buildField({ type: FieldTypeEnum.dataModel }))).toBe('wrapped');
|
|
30
|
+
expect(
|
|
31
|
+
subject(buildField({ type: FieldTypeEnum.generic, subType: 'text' })),
|
|
32
|
+
).toBe('wrapped');
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe('getFieldTextClipClassName', () => {
|
|
37
|
+
const subject = (field: PdfField) => getFieldTextClipClassName(field);
|
|
38
|
+
|
|
39
|
+
test('maps mode to css class', () => {
|
|
40
|
+
expect(subject(buildField())).toBe('dte-pdf-field-text--single-line');
|
|
41
|
+
expect(subject(buildField({ multiline: true }))).toBe('dte-pdf-field-text--multiline');
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { FieldTypeEnum, PdfField } from '../../../interface/types';
|
|
2
|
+
import {
|
|
3
|
+
DEFAULT_FIELD_FONT_FAMILY,
|
|
4
|
+
DEFAULT_FIELD_FONT_WEIGHT,
|
|
5
|
+
} from '../../../constants/field.constants';
|
|
6
|
+
import { getFieldTypographyStyle } from '../get-field-typography.utils';
|
|
7
|
+
|
|
8
|
+
const buildField = (overrides: Partial<PdfField> = {}): PdfField => ({
|
|
9
|
+
id: 'field-1',
|
|
10
|
+
type: FieldTypeEnum.fillable,
|
|
11
|
+
subType: 'text',
|
|
12
|
+
x: 0,
|
|
13
|
+
y: 0,
|
|
14
|
+
page: 1,
|
|
15
|
+
label: 'Label',
|
|
16
|
+
width: 200,
|
|
17
|
+
height: 25,
|
|
18
|
+
...overrides,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe('getFieldTypographyStyle', () => {
|
|
22
|
+
const subject = (field: PdfField) => getFieldTypographyStyle(field);
|
|
23
|
+
|
|
24
|
+
test('applies backend default font family and weight', () => {
|
|
25
|
+
expect(subject(buildField())).toEqual({
|
|
26
|
+
fontFamily: DEFAULT_FIELD_FONT_FAMILY,
|
|
27
|
+
fontWeight: DEFAULT_FIELD_FONT_WEIGHT,
|
|
28
|
+
fontSize: `${(8 * 980) / 612}px`,
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test('uses per-field typography overrides when provided', () => {
|
|
33
|
+
expect(
|
|
34
|
+
subject(
|
|
35
|
+
buildField({
|
|
36
|
+
styles: {
|
|
37
|
+
fontSize: 12,
|
|
38
|
+
fontFamily: 'Times New Roman',
|
|
39
|
+
fontWeight: 'bold',
|
|
40
|
+
},
|
|
41
|
+
}),
|
|
42
|
+
),
|
|
43
|
+
).toEqual({
|
|
44
|
+
fontFamily: 'Times New Roman',
|
|
45
|
+
fontWeight: 'bold',
|
|
46
|
+
fontSize: `${(12 * 980) / 612}px`,
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test('omits font size for unsupported field types', () => {
|
|
51
|
+
expect(subject(buildField({ type: FieldTypeEnum.eSign, subType: 'signature' }))).toEqual({
|
|
52
|
+
fontFamily: DEFAULT_FIELD_FONT_FAMILY,
|
|
53
|
+
fontWeight: DEFAULT_FIELD_FONT_WEIGHT,
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { CSSProperties } from 'react';
|
|
2
|
+
import { FieldTypeEnum, PdfField, PdfFieldSubType } from '../../interface/types';
|
|
3
|
+
import { getFieldTypographyStyle } from './get-field-typography.utils';
|
|
4
|
+
|
|
5
|
+
interface FieldBoundsStyleOptions {
|
|
6
|
+
pageOffset?: { left: number; top: number };
|
|
7
|
+
backgroundColor?: string;
|
|
8
|
+
borderColor?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const getFieldTypeSpecificStyle = (field: PdfField): CSSProperties => {
|
|
12
|
+
if (!field.subType) {
|
|
13
|
+
return {};
|
|
14
|
+
}
|
|
15
|
+
const styles = {
|
|
16
|
+
[FieldTypeEnum.fillable]: {
|
|
17
|
+
checkbox: {
|
|
18
|
+
width: 'unset',
|
|
19
|
+
height: 'unset',
|
|
20
|
+
},
|
|
21
|
+
radio: {
|
|
22
|
+
width: 'unset',
|
|
23
|
+
height: 'unset',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
} as Record<FieldTypeEnum, Record<PdfFieldSubType, CSSProperties>>;
|
|
27
|
+
|
|
28
|
+
return styles[field.type]?.[field.subType] ?? {};
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Shared absolute-position style for PDF field overlays.
|
|
33
|
+
*
|
|
34
|
+
* Matches the backend `/conversions/templatetopdf` clip rectangle and
|
|
35
|
+
* `PdfView` preview: content-box sizing, top-left alignment,
|
|
36
|
+
* and shared typography (font size / family / weight).
|
|
37
|
+
*/
|
|
38
|
+
export const getFieldBoundsStyle = (
|
|
39
|
+
field: PdfField,
|
|
40
|
+
options?: FieldBoundsStyleOptions,
|
|
41
|
+
): CSSProperties => {
|
|
42
|
+
const { backgroundColor, borderColor, pageOffset = { left: 0, top: 0 } } = options ?? {};
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
position: 'absolute',
|
|
46
|
+
left: pageOffset.left + field.x,
|
|
47
|
+
top: pageOffset.top + field.y,
|
|
48
|
+
width: field.width,
|
|
49
|
+
height: field.height,
|
|
50
|
+
display: 'flex',
|
|
51
|
+
alignItems: 'flex-start',
|
|
52
|
+
justifyContent: 'flex-start',
|
|
53
|
+
boxSizing: 'content-box',
|
|
54
|
+
padding: 4,
|
|
55
|
+
margin: 0,
|
|
56
|
+
minWidth: 0,
|
|
57
|
+
minHeight: 0,
|
|
58
|
+
overflow: 'hidden',
|
|
59
|
+
lineHeight: 1,
|
|
60
|
+
...getFieldTypeSpecificStyle(field),
|
|
61
|
+
...getFieldTypographyStyle(field),
|
|
62
|
+
...(backgroundColor ? { backgroundColor } : {}),
|
|
63
|
+
...(borderColor ? { borderColor } : {}),
|
|
64
|
+
};
|
|
65
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { FieldTypeEnum, PdfField } from '../../interface/types';
|
|
2
|
+
|
|
3
|
+
export type FieldTextClipMode = 'single-line' | 'multiline' | 'wrapped';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Text layout mode for a field overlay.
|
|
7
|
+
*
|
|
8
|
+
* Mirrors the backend PDF renderer:
|
|
9
|
+
* - single-line: no wrap, hard clip at the field rect (no ellipsis)
|
|
10
|
+
* - multiline: pre-wrap with word breaks, clip overflow vertically
|
|
11
|
+
* - wrapped: word-wrap for HTML / long prose blocks
|
|
12
|
+
*/
|
|
13
|
+
export const getFieldTextClipMode = (field: PdfField): FieldTextClipMode => {
|
|
14
|
+
if (field.type === FieldTypeEnum.fillable && field.multiline) {
|
|
15
|
+
return 'multiline';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (field.type === FieldTypeEnum.generic && field.subType === 'text') {
|
|
19
|
+
return 'wrapped';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (field.type === FieldTypeEnum.dataModel || field.type === FieldTypeEnum.forms) {
|
|
23
|
+
return 'wrapped';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return 'single-line';
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const getFieldTextClipClassName = (field: PdfField) => {
|
|
30
|
+
return `dte-pdf-field-text--${getFieldTextClipMode(field)}`;
|
|
31
|
+
};
|