@servicetitan/dte-pdf-editor 1.92.3 → 1.93.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 +0 -2
- package/dist/components/field-config-panel/dropdown-field-items.d.ts +9 -0
- package/dist/components/field-config-panel/dropdown-field-items.d.ts.map +1 -0
- package/dist/components/field-config-panel/dropdown-field-items.js +60 -0
- package/dist/components/field-config-panel/dropdown-field-items.js.map +1 -0
- package/dist/components/field-config-panel/field-config-panel-overlay.d.ts +1 -1
- package/dist/components/field-config-panel/field-config-panel-overlay.d.ts.map +1 -1
- package/dist/components/field-config-panel/field-config-panel.d.ts +1 -1
- package/dist/components/field-config-panel/field-config-panel.d.ts.map +1 -1
- package/dist/components/field-config-panel/field-config-panel.js +5 -1
- package/dist/components/field-config-panel/field-config-panel.js.map +1 -1
- package/dist/components/field-config-panel/fillable-default-config.d.ts +19 -0
- package/dist/components/field-config-panel/fillable-default-config.d.ts.map +1 -0
- package/dist/components/field-config-panel/fillable-default-config.js +77 -0
- package/dist/components/field-config-panel/fillable-default-config.js.map +1 -0
- package/dist/components/pdf-editor/pdf-editor.js +1 -1
- package/dist/components/pdf-editor/pdf-editor.js.map +1 -1
- package/dist/components/pdf-fields-overlay/pdf-fields-overlay.js +1 -1
- package/dist/components/pdf-fields-overlay/pdf-fields-overlay.js.map +1 -1
- package/dist/components/pdf-fields-overlay/pdf-overlay-field-fillable.d.ts +9 -0
- 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 +39 -12
- package/dist/components/pdf-fields-overlay/pdf-overlay-field-fillable.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 +4 -4
- package/dist/components/pdf-fields-overlay/pdf-overlay-field.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 +7 -20
- 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 +6 -2
- package/dist/components/pdf-view/pdf-view-fillable.js.map +1 -1
- package/dist/components/pdf-view/pdf-view.d.ts.map +1 -1
- package/dist/components/pdf-view/pdf-view.js +9 -2
- package/dist/components/pdf-view/pdf-view.js.map +1 -1
- package/dist/constants/field.constants.d.ts +1 -0
- package/dist/constants/field.constants.d.ts.map +1 -1
- package/dist/constants/field.constants.js +9 -0
- package/dist/constants/field.constants.js.map +1 -1
- package/dist/hooks/usePdfFieldDnD.d.ts.map +1 -1
- package/dist/hooks/usePdfFieldDnD.js +6 -0
- package/dist/hooks/usePdfFieldDnD.js.map +1 -1
- package/dist/hooks/usePdfFieldSelection.d.ts +1 -1
- package/dist/hooks/usePdfFieldSelection.d.ts.map +1 -1
- package/dist/hooks/usePdfFieldSelection.js +3 -1
- package/dist/hooks/usePdfFieldSelection.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/interface/types.d.ts +46 -1
- package/dist/interface/types.d.ts.map +1 -1
- package/dist/interface/types.js.map +1 -1
- package/dist/styles/index.css +1 -0
- package/dist/styles/inline-editable.css +2 -0
- package/dist/styles/pdf-canvas.css +2 -1
- package/dist/styles/pdf-field-overlay.css +2 -111
- package/dist/styles/pdf-field.css +111 -0
- package/dist/styles/variables.css +1 -0
- package/dist/utils/data-model/index.d.ts +1 -0
- package/dist/utils/data-model/index.d.ts.map +1 -1
- package/dist/utils/data-model/index.js +1 -0
- package/dist/utils/data-model/index.js.map +1 -1
- package/dist/utils/data-model/resolve-fields-default-values.d.ts +3 -0
- package/dist/utils/data-model/resolve-fields-default-values.d.ts.map +1 -0
- package/dist/utils/data-model/resolve-fields-default-values.js +19 -0
- package/dist/utils/data-model/resolve-fields-default-values.js.map +1 -0
- package/dist/utils/exposes-utils.d.ts +2 -0
- package/dist/utils/exposes-utils.d.ts.map +1 -0
- package/dist/utils/exposes-utils.js +2 -0
- package/dist/utils/exposes-utils.js.map +1 -0
- package/dist/utils/field/field-background-color.utils.d.ts +1 -1
- package/dist/utils/field/field-background-color.utils.d.ts.map +1 -1
- package/dist/utils/field/field-background-color.utils.js +1 -1
- package/dist/utils/field/field-background-color.utils.js.map +1 -1
- package/dist/utils/field/get-dropdown-field-items.utils.d.ts +3 -0
- package/dist/utils/field/get-dropdown-field-items.utils.d.ts.map +1 -0
- package/dist/utils/field/get-dropdown-field-items.utils.js +7 -0
- package/dist/utils/field/get-dropdown-field-items.utils.js.map +1 -0
- package/dist/utils/field/get-field-bounds-style.utils.d.ts +1 -2
- package/dist/utils/field/get-field-bounds-style.utils.d.ts.map +1 -1
- package/dist/utils/field/get-field-bounds-style.utils.js +9 -4
- package/dist/utils/field/get-field-bounds-style.utils.js.map +1 -1
- package/dist/utils/field/get-fillable-default-value.utils.d.ts +29 -0
- package/dist/utils/field/get-fillable-default-value.utils.d.ts.map +1 -0
- package/dist/utils/field/get-fillable-default-value.utils.js +59 -0
- package/dist/utils/field/get-fillable-default-value.utils.js.map +1 -0
- package/dist/utils/field/index.d.ts +2 -0
- package/dist/utils/field/index.d.ts.map +1 -1
- package/dist/utils/field/index.js +2 -0
- package/dist/utils/field/index.js.map +1 -1
- package/dist/utils/recipients/map-colors.js +23 -23
- package/dist/utils/recipients/map-colors.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/field-types/e-sign-fields.test.ts +3 -3
- package/src/__tests__/field-types/fillable-fields.test.ts +142 -6
- package/src/components/field-config-panel/dropdown-field-items.tsx +138 -0
- package/src/components/field-config-panel/field-config-panel-overlay.tsx +1 -1
- package/src/components/field-config-panel/field-config-panel.tsx +16 -1
- package/src/components/field-config-panel/fillable-default-config.tsx +155 -0
- package/src/components/pdf-editor/pdf-editor.tsx +1 -1
- package/src/components/pdf-fields-overlay/pdf-fields-overlay.tsx +1 -1
- package/src/components/pdf-fields-overlay/pdf-overlay-field-fillable.tsx +66 -3
- package/src/components/pdf-fields-overlay/pdf-overlay-field.tsx +6 -3
- package/src/components/pdf-view/pdf-view-field-container.tsx +21 -41
- package/src/components/pdf-view/pdf-view-fillable.tsx +25 -2
- package/src/components/pdf-view/pdf-view.tsx +11 -3
- package/src/constants/field.constants.ts +10 -0
- package/src/hooks/usePdfFieldDnD.ts +6 -0
- package/src/hooks/usePdfFieldSelection.ts +8 -2
- package/src/index.ts +1 -0
- package/src/interface/types.ts +36 -1
- package/src/styles/index.css +1 -0
- package/src/styles/inline-editable.css +2 -0
- package/src/styles/pdf-canvas.css +2 -1
- package/src/styles/pdf-field-overlay.css +2 -111
- package/src/styles/pdf-field.css +111 -0
- package/src/styles/variables.css +1 -0
- package/src/utils/data-model/index.ts +1 -0
- package/src/utils/data-model/resolve-fields-default-values.ts +19 -0
- package/src/utils/exposes-utils.ts +1 -0
- package/src/utils/field/__tests__/field-background-color.utils.test.ts +3 -3
- package/src/utils/field/__tests__/get-field-bounds-style.utils.test.ts +0 -1
- package/src/utils/field/__tests__/get-fillable-default-value.utils.test.ts +137 -0
- package/src/utils/field/field-background-color.utils.ts +1 -1
- package/src/utils/field/get-dropdown-field-items.utils.ts +14 -0
- package/src/utils/field/get-field-bounds-style.utils.ts +11 -11
- package/src/utils/field/get-fillable-default-value.utils.ts +70 -0
- package/src/utils/field/index.ts +2 -0
- package/src/utils/recipients/map-colors.ts +23 -23
|
@@ -2,9 +2,12 @@ import { FC, Fragment } from 'react';
|
|
|
2
2
|
import { v4 as uuidv4 } from 'uuid';
|
|
3
3
|
import { type PdfField } from '../../interface/types';
|
|
4
4
|
import {
|
|
5
|
+
getDropdownFieldItems,
|
|
5
6
|
getFieldPlaceholderText,
|
|
6
7
|
getFieldTextClipClassName,
|
|
7
8
|
getFillablePaintedLabel,
|
|
9
|
+
getTodayDateInputValue,
|
|
10
|
+
isRadioDefaultSelected,
|
|
8
11
|
} from '../../utils';
|
|
9
12
|
import { AddHandle } from './add-handle';
|
|
10
13
|
|
|
@@ -13,6 +16,15 @@ interface PdfOverlayFieldFillableProps {
|
|
|
13
16
|
handleAddNewField(field: PdfField): void;
|
|
14
17
|
}
|
|
15
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Every case below previews `field.default` through an uncontrolled, read-only DOM element
|
|
21
|
+
* (`defaultValue`/`defaultChecked`, never `value`/`checked` - there's no data model to bind to
|
|
22
|
+
* here, this is a static authoring mock-up). React only applies `defaultValue`/`defaultChecked`
|
|
23
|
+
* once, at mount, and silently ignores it on every later re-render - so each element carries a
|
|
24
|
+
* `key` derived from the same default it previews, forcing React to remount (not patch) it
|
|
25
|
+
* whenever the author changes that default in the config panel, so it doesn't keep showing the
|
|
26
|
+
* value it had when it first mounted.
|
|
27
|
+
*/
|
|
16
28
|
export const PdfOverlayFieldFillable: FC<PdfOverlayFieldFillableProps> = ({
|
|
17
29
|
field,
|
|
18
30
|
handleAddNewField,
|
|
@@ -33,9 +45,13 @@ export const PdfOverlayFieldFillable: FC<PdfOverlayFieldFillableProps> = ({
|
|
|
33
45
|
}}
|
|
34
46
|
>
|
|
35
47
|
<input
|
|
48
|
+
key={String(field.default?.type === 'checkbox' && field.default.value)}
|
|
36
49
|
title={field.label}
|
|
37
50
|
type="checkbox"
|
|
38
51
|
readOnly
|
|
52
|
+
defaultChecked={
|
|
53
|
+
field.default?.type === 'checkbox' ? field.default.value : false
|
|
54
|
+
}
|
|
39
55
|
className="dte-pdf-field-fillable"
|
|
40
56
|
style={{
|
|
41
57
|
width: field.width,
|
|
@@ -62,15 +78,18 @@ export const PdfOverlayFieldFillable: FC<PdfOverlayFieldFillableProps> = ({
|
|
|
62
78
|
}}
|
|
63
79
|
>
|
|
64
80
|
<input
|
|
81
|
+
key={String(isRadioDefaultSelected(field))}
|
|
65
82
|
title={field.label}
|
|
66
83
|
type="radio"
|
|
67
84
|
readOnly
|
|
85
|
+
defaultChecked={isRadioDefaultSelected(field)}
|
|
68
86
|
className="dte-pdf-field-fillable"
|
|
69
87
|
style={{
|
|
70
88
|
width: field.width,
|
|
71
89
|
height: field.height,
|
|
72
90
|
margin: 0,
|
|
73
91
|
border: 'none',
|
|
92
|
+
pointerEvents: 'none',
|
|
74
93
|
}}
|
|
75
94
|
/>
|
|
76
95
|
{getFillablePaintedLabel(field)}
|
|
@@ -79,6 +98,8 @@ export const PdfOverlayFieldFillable: FC<PdfOverlayFieldFillableProps> = ({
|
|
|
79
98
|
onClick={() =>
|
|
80
99
|
handleAddNewField({
|
|
81
100
|
...field,
|
|
101
|
+
default: undefined,
|
|
102
|
+
label: `${field.label} Copy`,
|
|
82
103
|
x: field.x + field.width + 15,
|
|
83
104
|
id: uuidv4(),
|
|
84
105
|
})
|
|
@@ -87,31 +108,70 @@ export const PdfOverlayFieldFillable: FC<PdfOverlayFieldFillableProps> = ({
|
|
|
87
108
|
</Fragment>
|
|
88
109
|
);
|
|
89
110
|
|
|
90
|
-
case 'date':
|
|
111
|
+
case 'date': {
|
|
112
|
+
const defaultDateValue =
|
|
113
|
+
field.default?.type === 'date'
|
|
114
|
+
? field.default.value
|
|
115
|
+
: field.default?.type === 'today'
|
|
116
|
+
? getTodayDateInputValue()
|
|
117
|
+
: '';
|
|
91
118
|
return (
|
|
92
119
|
<input
|
|
120
|
+
key={defaultDateValue}
|
|
93
121
|
type="date"
|
|
94
122
|
readOnly
|
|
123
|
+
defaultValue={defaultDateValue}
|
|
95
124
|
className={`dte-pdf-field-fillable ${clipClassName}`}
|
|
96
125
|
placeholder={placeholderText}
|
|
97
126
|
/>
|
|
98
127
|
);
|
|
128
|
+
}
|
|
99
129
|
|
|
100
|
-
case 'number':
|
|
130
|
+
case 'number': {
|
|
131
|
+
const defaultNumberValue = field.default?.type === 'number' ? field.default.value : '';
|
|
101
132
|
return (
|
|
102
133
|
<input
|
|
134
|
+
key={defaultNumberValue}
|
|
103
135
|
type="number"
|
|
104
136
|
readOnly
|
|
137
|
+
defaultValue={defaultNumberValue}
|
|
105
138
|
className={`dte-pdf-field-fillable ${clipClassName}`}
|
|
106
139
|
placeholder={placeholderText}
|
|
107
140
|
/>
|
|
108
141
|
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
case 'dropdown': {
|
|
145
|
+
const defaultDropdownValue =
|
|
146
|
+
field.default?.type === 'dropdown' ? field.default.value : '';
|
|
147
|
+
return (
|
|
148
|
+
<select
|
|
149
|
+
key={defaultDropdownValue}
|
|
150
|
+
defaultValue={defaultDropdownValue}
|
|
151
|
+
tabIndex={-1}
|
|
152
|
+
className="dte-pdf-field-fillable"
|
|
153
|
+
style={{ color: 'inherit', pointerEvents: 'none' }}
|
|
154
|
+
>
|
|
155
|
+
<option value="" disabled hidden>
|
|
156
|
+
{placeholderText || 'Select...'}
|
|
157
|
+
</option>
|
|
158
|
+
{getDropdownFieldItems(field).map(item => (
|
|
159
|
+
<option key={item.value} value={item.value}>
|
|
160
|
+
{item.label}
|
|
161
|
+
</option>
|
|
162
|
+
))}
|
|
163
|
+
</select>
|
|
164
|
+
);
|
|
165
|
+
}
|
|
109
166
|
|
|
110
|
-
default:
|
|
167
|
+
default: {
|
|
168
|
+
const defaultTextValue = field.default?.type === 'text' ? field.default.value : '';
|
|
111
169
|
if (field.multiline) {
|
|
112
170
|
return (
|
|
113
171
|
<textarea
|
|
172
|
+
key={defaultTextValue}
|
|
114
173
|
readOnly
|
|
174
|
+
defaultValue={defaultTextValue}
|
|
115
175
|
style={{ font: 'inherit', lineHeight: 'inherit', outline: 'none' }}
|
|
116
176
|
placeholder={placeholderText}
|
|
117
177
|
className={`dte-pdf-field-fillable ${clipClassName}`}
|
|
@@ -120,11 +180,14 @@ export const PdfOverlayFieldFillable: FC<PdfOverlayFieldFillableProps> = ({
|
|
|
120
180
|
}
|
|
121
181
|
return (
|
|
122
182
|
<input
|
|
183
|
+
key={defaultTextValue}
|
|
123
184
|
type="text"
|
|
124
185
|
readOnly
|
|
186
|
+
defaultValue={defaultTextValue}
|
|
125
187
|
placeholder={placeholderText}
|
|
126
188
|
className={`dte-pdf-field-fillable ${clipClassName}`}
|
|
127
189
|
/>
|
|
128
190
|
);
|
|
191
|
+
}
|
|
129
192
|
}
|
|
130
193
|
};
|
|
@@ -4,8 +4,8 @@ import { DISABLE_FIELD_RESIZE } from '../../constants';
|
|
|
4
4
|
import { useFieldDrag, useFieldResize } from '../../hooks';
|
|
5
5
|
import { FieldTypeEnum, type PdfField } from '../../interface/types';
|
|
6
6
|
import {
|
|
7
|
-
getFieldBackgroundColor,
|
|
8
7
|
getFieldBoundsStyle,
|
|
8
|
+
getFieldColor,
|
|
9
9
|
getFieldTypeOption,
|
|
10
10
|
getPagePosition,
|
|
11
11
|
} from '../../utils';
|
|
@@ -74,13 +74,13 @@ export const PdfOverlayField: FC<PdfOverlayFieldProps> = ({
|
|
|
74
74
|
{ ...field, page, x, y },
|
|
75
75
|
{
|
|
76
76
|
pageOffset: pagePos,
|
|
77
|
-
|
|
77
|
+
color: getFieldColor(field.recipient, recipientsColors),
|
|
78
78
|
zoom,
|
|
79
79
|
},
|
|
80
80
|
);
|
|
81
81
|
|
|
82
82
|
const className = [
|
|
83
|
-
'dte-pdf-field-
|
|
83
|
+
'dte-pdf-field-container',
|
|
84
84
|
`${field.type}-${field.subType}`,
|
|
85
85
|
fieldWrapperClassNames,
|
|
86
86
|
isSelected ? '--selected' : '--unselected',
|
|
@@ -103,6 +103,8 @@ export const PdfOverlayField: FC<PdfOverlayFieldProps> = ({
|
|
|
103
103
|
onDragEnd={handleDragEnd}
|
|
104
104
|
className={className}
|
|
105
105
|
style={style}
|
|
106
|
+
data-path={field.path}
|
|
107
|
+
data-id={field.id}
|
|
106
108
|
>
|
|
107
109
|
{fieldTypeOption && (
|
|
108
110
|
<div className="dte-pdf-field-label">
|
|
@@ -110,6 +112,7 @@ export const PdfOverlayField: FC<PdfOverlayFieldProps> = ({
|
|
|
110
112
|
<Icon svg={fieldTypeOption.icon} size="small" color="inherit" />
|
|
111
113
|
)}
|
|
112
114
|
{fieldTypeOption.label}
|
|
115
|
+
{!field.required && <span>(Optional)</span>}
|
|
113
116
|
</div>
|
|
114
117
|
)}
|
|
115
118
|
{field.type === FieldTypeEnum.calculated && (
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Icon } from '@servicetitan/anvil2';
|
|
2
|
-
import { FC,
|
|
2
|
+
import { FC, PropsWithChildren, RefObject } from 'react';
|
|
3
3
|
import { type DataModelValues, type PdfField } from '../../interface/types';
|
|
4
4
|
import {
|
|
5
5
|
evaluateDisplayCondition,
|
|
6
|
-
getFieldBackgroundColor,
|
|
7
6
|
getFieldBoundsStyle,
|
|
7
|
+
getFieldColor,
|
|
8
8
|
getFieldTypeOption,
|
|
9
9
|
getPagePosition,
|
|
10
10
|
} from '../../utils';
|
|
@@ -37,54 +37,34 @@ export const PdfViewFieldContainer: FC<PropsWithChildren<PdfViewFieldContainer>>
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
const pagePos = getPagePosition(field.page, pdfWrapperRef);
|
|
40
|
-
const
|
|
40
|
+
const color = getFieldColor(field.recipient, recipientsColors);
|
|
41
41
|
const fieldTypeOption = getFieldTypeOption(field);
|
|
42
42
|
|
|
43
43
|
return (
|
|
44
|
-
<
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
{children}
|
|
58
|
-
</div>
|
|
44
|
+
<div
|
|
45
|
+
id={`dte-pdf-field-${field.id}`}
|
|
46
|
+
data-path={field.path}
|
|
47
|
+
data-id={field.id}
|
|
48
|
+
className={`dte-pdf-field-container --view --${field.type} ${error && '--error'}`}
|
|
49
|
+
tabIndex={-1}
|
|
50
|
+
style={getFieldBoundsStyle(field, {
|
|
51
|
+
pageOffset: pagePos,
|
|
52
|
+
color,
|
|
53
|
+
zoom,
|
|
54
|
+
})}
|
|
55
|
+
onFocus={() => onFocus?.(field.id)}
|
|
56
|
+
>
|
|
59
57
|
{fieldTypeOption && (
|
|
60
|
-
<div
|
|
61
|
-
className="dte-pdf-field-label"
|
|
62
|
-
style={{
|
|
63
|
-
top: pagePos.top + field.y * zoom - 10,
|
|
64
|
-
left: pagePos.left + field.x * zoom + 3,
|
|
65
|
-
position: 'absolute',
|
|
66
|
-
}}
|
|
67
|
-
>
|
|
58
|
+
<div className="dte-pdf-field-label">
|
|
68
59
|
{fieldTypeOption.icon && (
|
|
69
60
|
<Icon svg={fieldTypeOption.icon} size="small" color="inherit" />
|
|
70
61
|
)}
|
|
71
62
|
{fieldTypeOption.label}
|
|
63
|
+
{!field.required && <span>(Optional)</span>}
|
|
72
64
|
</div>
|
|
73
65
|
)}
|
|
74
|
-
{
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
color: 'red',
|
|
78
|
-
top: pagePos.top + (field.y + field.height) * zoom + 8,
|
|
79
|
-
left: pagePos.left + field.x * zoom,
|
|
80
|
-
minWidth: '100px',
|
|
81
|
-
fontSize: '10px',
|
|
82
|
-
position: 'absolute',
|
|
83
|
-
}}
|
|
84
|
-
>
|
|
85
|
-
{error}
|
|
86
|
-
</span>
|
|
87
|
-
)}
|
|
88
|
-
</Fragment>
|
|
66
|
+
<div style={{ width: 'inherit', height: 'inherit' }}>{children}</div>
|
|
67
|
+
{error && <span className="dte-pdf-field-error-message">{error}</span>}
|
|
68
|
+
</div>
|
|
89
69
|
);
|
|
90
70
|
};
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
} from '../../interface/types';
|
|
8
8
|
import {
|
|
9
9
|
dateToUtcZero,
|
|
10
|
+
getDropdownFieldItems,
|
|
10
11
|
getFieldPlaceholderText,
|
|
11
12
|
getFieldTextClipClassName,
|
|
12
13
|
getFillablePaintedLabel,
|
|
@@ -36,6 +37,7 @@ const fillableTextStyles = {
|
|
|
36
37
|
height: '100%',
|
|
37
38
|
border: 'none',
|
|
38
39
|
fontSize: 'inherit',
|
|
40
|
+
color: 'inherit',
|
|
39
41
|
boxSizing: 'border-box' as const,
|
|
40
42
|
padding: 0,
|
|
41
43
|
margin: 0,
|
|
@@ -96,8 +98,7 @@ export const PdfViewFillable: FC<PdfViewFillableProps> = ({
|
|
|
96
98
|
}) => {
|
|
97
99
|
const resolvedValue = data?.[field.path!];
|
|
98
100
|
const isViewByCurrentRecipient = fillingBy?.includes(field.recipient!);
|
|
99
|
-
const isFillable =
|
|
100
|
-
viewMode === 'preview' ? true : viewMode === 'fill' ? false : isViewByCurrentRecipient;
|
|
101
|
+
const isFillable = viewMode === 'preview' ? true : isViewByCurrentRecipient;
|
|
101
102
|
|
|
102
103
|
const isInputDisabled = !isFillable || !!readOnly;
|
|
103
104
|
|
|
@@ -208,6 +209,28 @@ export const PdfViewFillable: FC<PdfViewFillableProps> = ({
|
|
|
208
209
|
);
|
|
209
210
|
}
|
|
210
211
|
|
|
212
|
+
if (field.subType === 'dropdown') {
|
|
213
|
+
return (
|
|
214
|
+
<select
|
|
215
|
+
style={fillableTextStyles}
|
|
216
|
+
disabled={isInputDisabled}
|
|
217
|
+
id={field.path}
|
|
218
|
+
name={field.path}
|
|
219
|
+
value={resolvedValue ?? ''}
|
|
220
|
+
onChange={e => handleDataChange(e.target.value)}
|
|
221
|
+
>
|
|
222
|
+
<option value="" disabled hidden>
|
|
223
|
+
{getFieldPlaceholderText(field) || 'Select...'}
|
|
224
|
+
</option>
|
|
225
|
+
{getDropdownFieldItems(field).map(item => (
|
|
226
|
+
<option key={item.value} value={item.value}>
|
|
227
|
+
{item.label}
|
|
228
|
+
</option>
|
|
229
|
+
))}
|
|
230
|
+
</select>
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
|
|
211
234
|
const TextComponent = field.multiline ? FillableTextareaInput : FillableTextInput;
|
|
212
235
|
return (
|
|
213
236
|
<TextComponent
|
|
@@ -144,7 +144,16 @@ export const PdfView: FC<PdfViewProps> = ({
|
|
|
144
144
|
useLayoutEffect(() => {
|
|
145
145
|
const field = navigableFields[currentFieldIndex];
|
|
146
146
|
const navControlsEl = navControlsRef.current;
|
|
147
|
-
|
|
147
|
+
|
|
148
|
+
if (!navControlsEl) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (currentFieldIndex === -1) {
|
|
153
|
+
navControlsEl.style.top = `20px`;
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
if (!field) {
|
|
148
157
|
return;
|
|
149
158
|
}
|
|
150
159
|
|
|
@@ -152,7 +161,6 @@ export const PdfView: FC<PdfViewProps> = ({
|
|
|
152
161
|
if (!fieldEl) {
|
|
153
162
|
return;
|
|
154
163
|
}
|
|
155
|
-
|
|
156
164
|
const containerTop = contentContainerRef.current?.getBoundingClientRect().top ?? 0;
|
|
157
165
|
navControlsEl.style.top = `${fieldEl.getBoundingClientRect().top - containerTop}px`;
|
|
158
166
|
}, [currentFieldIndex, navigableFields]);
|
|
@@ -247,7 +255,7 @@ export const PdfView: FC<PdfViewProps> = ({
|
|
|
247
255
|
key={field.id}
|
|
248
256
|
field={field}
|
|
249
257
|
data={viewMode === 'preview' ? previewData : data}
|
|
250
|
-
error={errors[field.path!] || ''}
|
|
258
|
+
error={errors[field.path!] || errors[field.id] || ''}
|
|
251
259
|
recipientsColors={recipientsColors}
|
|
252
260
|
pdfWrapperRef={pdfWrapperRef}
|
|
253
261
|
zoom={zoom}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import NumbersIcon from '@servicetitan/anvil2/assets/icons/material/round/123.svg';
|
|
2
2
|
import AbcIcon from '@servicetitan/anvil2/assets/icons/material/round/abc.svg';
|
|
3
|
+
import ArrowDropDownCircleIcon from '@servicetitan/anvil2/assets/icons/material/round/arrow_drop_down_circle.svg';
|
|
3
4
|
import BadgeIcon from '@servicetitan/anvil2/assets/icons/material/round/badge.svg';
|
|
4
5
|
import CalculateIcon from '@servicetitan/anvil2/assets/icons/material/round/calculate.svg';
|
|
5
6
|
import CalendarMonthIcon from '@servicetitan/anvil2/assets/icons/material/round/calendar_month.svg';
|
|
@@ -46,10 +47,13 @@ export const FIELD_FONT_SIZE_OPTIONS = Array.from({ length: 17 }, (_, index) =>
|
|
|
46
47
|
export const FILLABLE_FIELD_DEFAULT_SIZES = {
|
|
47
48
|
text: { width: 200, height: 25 },
|
|
48
49
|
date: { width: 200, height: 25 },
|
|
50
|
+
dropdown: { width: 200, height: 25 },
|
|
49
51
|
radio: { width: 20, height: 20 },
|
|
50
52
|
checkbox: { width: 20, height: 20 },
|
|
51
53
|
} as Record<PdfFieldSubType, { width: number; height: number }>;
|
|
52
54
|
|
|
55
|
+
export const DROPDOWN_FIELD_MAX_ITEMS = 50;
|
|
56
|
+
|
|
53
57
|
export const GENERIC_FIELD_TABLE_ROW_HEIGHT = 40;
|
|
54
58
|
|
|
55
59
|
export const GENERIC_FIELD_DEFAULT_SIZES = {
|
|
@@ -63,6 +67,12 @@ export const FILLABLE_FIELD_TYPES: FieldTypeOption[] = [
|
|
|
63
67
|
{ label: 'Date Field', type: FieldTypeEnum.fillable, subType: 'date', icon: CalendarMonthIcon },
|
|
64
68
|
{ label: 'Checkbox', type: FieldTypeEnum.fillable, subType: 'checkbox', icon: CheckboxIcon },
|
|
65
69
|
{ label: 'Radio', type: FieldTypeEnum.fillable, subType: 'radio', icon: RadioIcon },
|
|
70
|
+
{
|
|
71
|
+
label: 'Dropdown',
|
|
72
|
+
type: FieldTypeEnum.fillable,
|
|
73
|
+
subType: 'dropdown',
|
|
74
|
+
icon: ArrowDropDownCircleIcon,
|
|
75
|
+
},
|
|
66
76
|
];
|
|
67
77
|
|
|
68
78
|
export const E_SIGN_FIELD_TYPES: FieldTypeOption[] = [
|
|
@@ -61,12 +61,18 @@ const initializeDroppedFieldByType: Record<FieldTypeEnum, InitializeDroppedField
|
|
|
61
61
|
...base,
|
|
62
62
|
recipient: recipientName,
|
|
63
63
|
required: false,
|
|
64
|
+
hideLabel: base.subType === 'radio' || base.subType === 'checkbox',
|
|
64
65
|
path: generateFillablePath(recipientName, uuidv4()),
|
|
65
66
|
...(base.subType === 'radio'
|
|
66
67
|
? {
|
|
67
68
|
groupName: `Radio Group ${uuidv4()}`,
|
|
68
69
|
}
|
|
69
70
|
: {}),
|
|
71
|
+
...(base.subType === 'dropdown'
|
|
72
|
+
? {
|
|
73
|
+
dropdownItems: [{ value: '', label: '' }],
|
|
74
|
+
}
|
|
75
|
+
: {}),
|
|
70
76
|
...(defaultSize && {
|
|
71
77
|
width: defaultSize.width,
|
|
72
78
|
height: defaultSize.height,
|
|
@@ -28,9 +28,15 @@ export const usePdfFieldSelection = (
|
|
|
28
28
|
|
|
29
29
|
const updateMatchingFields = (
|
|
30
30
|
predicate: (field: PdfField) => boolean,
|
|
31
|
-
updates: Partial<PdfField
|
|
31
|
+
updates: Partial<PdfField> | ((field: PdfField) => Partial<PdfField>),
|
|
32
32
|
) => {
|
|
33
|
-
onFieldsChange(
|
|
33
|
+
onFieldsChange(
|
|
34
|
+
fields.map(f =>
|
|
35
|
+
predicate(f)
|
|
36
|
+
? { ...f, ...(typeof updates === 'function' ? updates(f) : updates) }
|
|
37
|
+
: f,
|
|
38
|
+
),
|
|
39
|
+
);
|
|
34
40
|
};
|
|
35
41
|
|
|
36
42
|
const deleteSelectedField = () => {
|
package/src/index.ts
CHANGED
package/src/interface/types.ts
CHANGED
|
@@ -17,7 +17,7 @@ export enum ESignFieldType {
|
|
|
17
17
|
fullName = 'fullName',
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
export type FillableFieldType = 'text' | 'date' | 'checkbox' | 'radio' | 'number';
|
|
20
|
+
export type FillableFieldType = 'text' | 'date' | 'checkbox' | 'radio' | 'number' | 'dropdown';
|
|
21
21
|
|
|
22
22
|
export type GenericFieldType = 'table' | 'text';
|
|
23
23
|
export type FormFieldType = 'number' | 'text' | 'date';
|
|
@@ -88,6 +88,11 @@ export interface ESignDateSignedOffsetSettings {
|
|
|
88
88
|
| [...ESignDateSignedOffsetSettingsRule[], ESignDateSignedOffsetSettingsFinalRule];
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
export interface FillableDropdownItem {
|
|
92
|
+
value: string;
|
|
93
|
+
label: string;
|
|
94
|
+
}
|
|
95
|
+
|
|
91
96
|
export type FieldDataType =
|
|
92
97
|
| GenericFieldTableDataType
|
|
93
98
|
| GenericFieldTextDataType
|
|
@@ -125,6 +130,33 @@ export interface PdfFieldStyles {
|
|
|
125
130
|
fontWeight?: 'normal' | 'bold';
|
|
126
131
|
}
|
|
127
132
|
|
|
133
|
+
/**
|
|
134
|
+
* Optional author-configured starting value for a fillable field. The `text`/`number`/`checkbox`/
|
|
135
|
+
* `date`/`today`/`radio` variants mirror the backend's `FillableDefault` discriminated union
|
|
136
|
+
* (`ByodField.Default`, DTE-4668) one-for-one: same `type` discriminator and `value` shape, so the
|
|
137
|
+
* JSON round-trips as-is with no FE-side translation.
|
|
138
|
+
*
|
|
139
|
+
* `today` and `radio` intentionally carry no `value` - `today` is a sentinel the client resolves
|
|
140
|
+
* to a calendar date in its own local timezone at render time (never frozen at author time), and
|
|
141
|
+
* `radio` just marks *this* option field as the group's pre-selected default (the "value" is which
|
|
142
|
+
* option, i.e. which sibling field carries the flag - there is nothing else to store).
|
|
143
|
+
*
|
|
144
|
+
* `dropdown` has no backend counterpart at all (the backend doesn't recognize a `dropdown`
|
|
145
|
+
* fillable subtype - see `FillableFieldSubTypes`) and is an FE-only extension of this same union:
|
|
146
|
+
* `value` is one of this field's own `dropdownItems[].value` entries. Unlike `radio`, a dropdown's
|
|
147
|
+
* options all live on this one field (`dropdownItems`), not on sibling fields, so - same as
|
|
148
|
+
* text/number/checkbox/date - there is exactly one field to attach the default to, and the value
|
|
149
|
+
* it points at is meaningful to store here directly.
|
|
150
|
+
*/
|
|
151
|
+
export type FillableDefault =
|
|
152
|
+
| { type: 'text'; value: string }
|
|
153
|
+
| { type: 'number'; value: number }
|
|
154
|
+
| { type: 'checkbox'; value: boolean }
|
|
155
|
+
| { type: 'date'; value: string }
|
|
156
|
+
| { type: 'today' }
|
|
157
|
+
| { type: 'radio' }
|
|
158
|
+
| { type: 'dropdown'; value: string };
|
|
159
|
+
|
|
128
160
|
export interface PdfField {
|
|
129
161
|
id: string;
|
|
130
162
|
type: FieldTypeEnum;
|
|
@@ -154,6 +186,9 @@ export interface PdfField {
|
|
|
154
186
|
*/
|
|
155
187
|
hideLabel?: boolean;
|
|
156
188
|
styles?: PdfFieldStyles;
|
|
189
|
+
default?: FillableDefault;
|
|
190
|
+
/** Manually-authored options for a fillable `dropdown` field. See `FillableDropdownItem`. */
|
|
191
|
+
dropdownItems?: FillableDropdownItem[];
|
|
157
192
|
}
|
|
158
193
|
|
|
159
194
|
export type SchemaFieldType = 'number' | 'date';
|
package/src/styles/index.css
CHANGED