@servicetitan/dte-pdf-editor 1.92.4 → 1.93.1

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.
Files changed (209) hide show
  1. package/README.md +0 -2
  2. package/dist/components/field-config-panel/dropdown-field-items.d.ts +9 -0
  3. package/dist/components/field-config-panel/dropdown-field-items.d.ts.map +1 -0
  4. package/dist/components/field-config-panel/dropdown-field-items.js +60 -0
  5. package/dist/components/field-config-panel/dropdown-field-items.js.map +1 -0
  6. package/dist/components/field-config-panel/field-config-panel-overlay.d.ts +1 -1
  7. package/dist/components/field-config-panel/field-config-panel-overlay.d.ts.map +1 -1
  8. package/dist/components/field-config-panel/field-config-panel.d.ts +1 -1
  9. package/dist/components/field-config-panel/field-config-panel.d.ts.map +1 -1
  10. package/dist/components/field-config-panel/field-config-panel.js +5 -1
  11. package/dist/components/field-config-panel/field-config-panel.js.map +1 -1
  12. package/dist/components/field-config-panel/fillable-default-config.d.ts +19 -0
  13. package/dist/components/field-config-panel/fillable-default-config.d.ts.map +1 -0
  14. package/dist/components/field-config-panel/fillable-default-config.js +77 -0
  15. package/dist/components/field-config-panel/fillable-default-config.js.map +1 -0
  16. package/dist/components/field-sidebar/calculated-field-type-list.d.ts.map +1 -1
  17. package/dist/components/field-sidebar/calculated-field-type-list.js +1 -1
  18. package/dist/components/field-sidebar/calculated-field-type-list.js.map +1 -1
  19. package/dist/components/field-sidebar/e-sign-field-type-list.d.ts.map +1 -1
  20. package/dist/components/field-sidebar/e-sign-field-type-list.js +1 -1
  21. package/dist/components/field-sidebar/e-sign-field-type-list.js.map +1 -1
  22. package/dist/components/field-sidebar/field-type.d.ts +2 -0
  23. package/dist/components/field-sidebar/field-type.d.ts.map +1 -1
  24. package/dist/components/field-sidebar/field-type.js +2 -2
  25. package/dist/components/field-sidebar/field-type.js.map +1 -1
  26. package/dist/components/field-sidebar/fillable-field-type-list.d.ts.map +1 -1
  27. package/dist/components/field-sidebar/fillable-field-type-list.js +1 -1
  28. package/dist/components/field-sidebar/fillable-field-type-list.js.map +1 -1
  29. package/dist/components/field-sidebar/generic-field-type-list.d.ts.map +1 -1
  30. package/dist/components/field-sidebar/generic-field-type-list.js +1 -1
  31. package/dist/components/field-sidebar/generic-field-type-list.js.map +1 -1
  32. package/dist/components/pdf-canvas/canvas-controls.d.ts +9 -15
  33. package/dist/components/pdf-canvas/canvas-controls.d.ts.map +1 -1
  34. package/dist/components/pdf-canvas/canvas-controls.js +4 -14
  35. package/dist/components/pdf-canvas/canvas-controls.js.map +1 -1
  36. package/dist/components/pdf-canvas/pdf-canvas.d.ts +6 -2
  37. package/dist/components/pdf-canvas/pdf-canvas.d.ts.map +1 -1
  38. package/dist/components/pdf-canvas/pdf-canvas.js +11 -5
  39. package/dist/components/pdf-canvas/pdf-canvas.js.map +1 -1
  40. package/dist/components/pdf-editor/pdf-editor.d.ts +2 -0
  41. package/dist/components/pdf-editor/pdf-editor.d.ts.map +1 -1
  42. package/dist/components/pdf-editor/pdf-editor.js +2 -2
  43. package/dist/components/pdf-editor/pdf-editor.js.map +1 -1
  44. package/dist/components/pdf-fields-overlay/pdf-fields-overlay.js +4 -1
  45. package/dist/components/pdf-fields-overlay/pdf-fields-overlay.js.map +1 -1
  46. package/dist/components/pdf-fields-overlay/pdf-overlay-field-e-sign.d.ts.map +1 -1
  47. package/dist/components/pdf-fields-overlay/pdf-overlay-field-e-sign.js +3 -2
  48. package/dist/components/pdf-fields-overlay/pdf-overlay-field-e-sign.js.map +1 -1
  49. package/dist/components/pdf-fields-overlay/pdf-overlay-field-fillable.d.ts +9 -0
  50. package/dist/components/pdf-fields-overlay/pdf-overlay-field-fillable.d.ts.map +1 -1
  51. package/dist/components/pdf-fields-overlay/pdf-overlay-field-fillable.js +39 -12
  52. package/dist/components/pdf-fields-overlay/pdf-overlay-field-fillable.js.map +1 -1
  53. package/dist/components/pdf-fields-overlay/pdf-overlay-field.d.ts.map +1 -1
  54. package/dist/components/pdf-fields-overlay/pdf-overlay-field.js +6 -4
  55. package/dist/components/pdf-fields-overlay/pdf-overlay-field.js.map +1 -1
  56. package/dist/components/pdf-view/pdf-view-e-sign.d.ts.map +1 -1
  57. package/dist/components/pdf-view/pdf-view-e-sign.js +3 -2
  58. package/dist/components/pdf-view/pdf-view-e-sign.js.map +1 -1
  59. package/dist/components/pdf-view/pdf-view-field-container.d.ts.map +1 -1
  60. package/dist/components/pdf-view/pdf-view-field-container.js +9 -15
  61. package/dist/components/pdf-view/pdf-view-field-container.js.map +1 -1
  62. package/dist/components/pdf-view/pdf-view-fillable.d.ts.map +1 -1
  63. package/dist/components/pdf-view/pdf-view-fillable.js +7 -3
  64. package/dist/components/pdf-view/pdf-view-fillable.js.map +1 -1
  65. package/dist/components/pdf-view/pdf-view.d.ts +5 -0
  66. package/dist/components/pdf-view/pdf-view.d.ts.map +1 -1
  67. package/dist/components/pdf-view/pdf-view.js +58 -24
  68. package/dist/components/pdf-view/pdf-view.js.map +1 -1
  69. package/dist/constants/field.constants.d.ts +1 -0
  70. package/dist/constants/field.constants.d.ts.map +1 -1
  71. package/dist/constants/field.constants.js +36 -6
  72. package/dist/constants/field.constants.js.map +1 -1
  73. package/dist/hooks/index.d.ts +1 -0
  74. package/dist/hooks/index.d.ts.map +1 -1
  75. package/dist/hooks/index.js +1 -0
  76. package/dist/hooks/index.js.map +1 -1
  77. package/dist/hooks/usePdfFieldDnD.d.ts.map +1 -1
  78. package/dist/hooks/usePdfFieldDnD.js +6 -0
  79. package/dist/hooks/usePdfFieldDnD.js.map +1 -1
  80. package/dist/hooks/usePdfFieldSelection.d.ts +1 -1
  81. package/dist/hooks/usePdfFieldSelection.d.ts.map +1 -1
  82. package/dist/hooks/usePdfFieldSelection.js +3 -1
  83. package/dist/hooks/usePdfFieldSelection.js.map +1 -1
  84. package/dist/hooks/usePinchZoom.d.ts +14 -0
  85. package/dist/hooks/usePinchZoom.d.ts.map +1 -0
  86. package/dist/hooks/usePinchZoom.js +58 -0
  87. package/dist/hooks/usePinchZoom.js.map +1 -0
  88. package/dist/hooks/useZoom.d.ts +2 -0
  89. package/dist/hooks/useZoom.d.ts.map +1 -1
  90. package/dist/hooks/useZoom.js +18 -6
  91. package/dist/hooks/useZoom.js.map +1 -1
  92. package/dist/index.d.ts +1 -0
  93. package/dist/index.d.ts.map +1 -1
  94. package/dist/index.js +1 -0
  95. package/dist/index.js.map +1 -1
  96. package/dist/interface/types.d.ts +48 -1
  97. package/dist/interface/types.d.ts.map +1 -1
  98. package/dist/interface/types.js.map +1 -1
  99. package/dist/styles/index.css +1 -0
  100. package/dist/styles/inline-editable.css +2 -0
  101. package/dist/styles/pdf-canvas.css +92 -15
  102. package/dist/styles/pdf-editor.css +1 -0
  103. package/dist/styles/pdf-field-overlay.css +40 -118
  104. package/dist/styles/pdf-field.css +111 -0
  105. package/dist/styles/variables.css +4 -0
  106. package/dist/utils/data-model/index.d.ts +1 -0
  107. package/dist/utils/data-model/index.d.ts.map +1 -1
  108. package/dist/utils/data-model/index.js +1 -0
  109. package/dist/utils/data-model/index.js.map +1 -1
  110. package/dist/utils/data-model/resolve-fields-default-values.d.ts +3 -0
  111. package/dist/utils/data-model/resolve-fields-default-values.d.ts.map +1 -0
  112. package/dist/utils/data-model/resolve-fields-default-values.js +19 -0
  113. package/dist/utils/data-model/resolve-fields-default-values.js.map +1 -0
  114. package/dist/utils/exposes-utils.d.ts +2 -0
  115. package/dist/utils/exposes-utils.d.ts.map +1 -0
  116. package/dist/utils/exposes-utils.js +2 -0
  117. package/dist/utils/exposes-utils.js.map +1 -0
  118. package/dist/utils/field/field-background-color.utils.d.ts +1 -1
  119. package/dist/utils/field/field-background-color.utils.d.ts.map +1 -1
  120. package/dist/utils/field/field-background-color.utils.js +1 -1
  121. package/dist/utils/field/field-background-color.utils.js.map +1 -1
  122. package/dist/utils/field/get-dropdown-field-items.utils.d.ts +3 -0
  123. package/dist/utils/field/get-dropdown-field-items.utils.d.ts.map +1 -0
  124. package/dist/utils/field/get-dropdown-field-items.utils.js +7 -0
  125. package/dist/utils/field/get-dropdown-field-items.utils.js.map +1 -0
  126. package/dist/utils/field/get-field-bounds-style.utils.d.ts +1 -2
  127. package/dist/utils/field/get-field-bounds-style.utils.d.ts.map +1 -1
  128. package/dist/utils/field/get-field-bounds-style.utils.js +9 -4
  129. package/dist/utils/field/get-field-bounds-style.utils.js.map +1 -1
  130. package/dist/utils/field/get-field-type-option.utils.d.ts +3 -0
  131. package/dist/utils/field/get-field-type-option.utils.d.ts.map +1 -0
  132. package/dist/utils/field/get-field-type-option.utils.js +10 -0
  133. package/dist/utils/field/get-field-type-option.utils.js.map +1 -0
  134. package/dist/utils/field/get-fillable-default-value.utils.d.ts +29 -0
  135. package/dist/utils/field/get-fillable-default-value.utils.d.ts.map +1 -0
  136. package/dist/utils/field/get-fillable-default-value.utils.js +59 -0
  137. package/dist/utils/field/get-fillable-default-value.utils.js.map +1 -0
  138. package/dist/utils/field/get-navigable-fields.utils.js +1 -1
  139. package/dist/utils/field/get-navigable-fields.utils.js.map +1 -1
  140. package/dist/utils/field/index.d.ts +3 -0
  141. package/dist/utils/field/index.d.ts.map +1 -1
  142. package/dist/utils/field/index.js +3 -0
  143. package/dist/utils/field/index.js.map +1 -1
  144. package/dist/utils/pdf/index.d.ts +1 -0
  145. package/dist/utils/pdf/index.d.ts.map +1 -1
  146. package/dist/utils/pdf/index.js +1 -0
  147. package/dist/utils/pdf/index.js.map +1 -1
  148. package/dist/utils/pdf/pinch-zoom.utils.d.ts +6 -0
  149. package/dist/utils/pdf/pinch-zoom.utils.d.ts.map +1 -0
  150. package/dist/utils/pdf/pinch-zoom.utils.js +18 -0
  151. package/dist/utils/pdf/pinch-zoom.utils.js.map +1 -0
  152. package/dist/utils/recipients/map-colors.js +23 -23
  153. package/dist/utils/recipients/map-colors.js.map +1 -1
  154. package/package.json +1 -1
  155. package/src/__tests__/field-types/e-sign-fields.test.ts +3 -3
  156. package/src/__tests__/field-types/fillable-fields.test.ts +142 -6
  157. package/src/components/field-config-panel/dropdown-field-items.tsx +138 -0
  158. package/src/components/field-config-panel/field-config-panel-overlay.tsx +1 -1
  159. package/src/components/field-config-panel/field-config-panel.tsx +16 -1
  160. package/src/components/field-config-panel/fillable-default-config.tsx +155 -0
  161. package/src/components/field-sidebar/calculated-field-type-list.tsx +1 -0
  162. package/src/components/field-sidebar/e-sign-field-type-list.tsx +1 -0
  163. package/src/components/field-sidebar/field-type.tsx +13 -3
  164. package/src/components/field-sidebar/fillable-field-type-list.tsx +1 -0
  165. package/src/components/field-sidebar/generic-field-type-list.tsx +1 -0
  166. package/src/components/pdf-canvas/canvas-controls.tsx +30 -128
  167. package/src/components/pdf-canvas/pdf-canvas.tsx +18 -8
  168. package/src/components/pdf-editor/pdf-editor.tsx +5 -2
  169. package/src/components/pdf-fields-overlay/pdf-fields-overlay.tsx +2 -2
  170. package/src/components/pdf-fields-overlay/pdf-overlay-field-e-sign.tsx +8 -6
  171. package/src/components/pdf-fields-overlay/pdf-overlay-field-fillable.tsx +66 -3
  172. package/src/components/pdf-fields-overlay/pdf-overlay-field.tsx +22 -3
  173. package/src/components/pdf-view/pdf-view-e-sign.tsx +8 -6
  174. package/src/components/pdf-view/pdf-view-field-container.tsx +31 -31
  175. package/src/components/pdf-view/pdf-view-fillable.tsx +26 -3
  176. package/src/components/pdf-view/pdf-view.tsx +164 -95
  177. package/src/constants/field.constants.ts +38 -6
  178. package/src/hooks/index.ts +1 -0
  179. package/src/hooks/usePdfFieldDnD.ts +6 -0
  180. package/src/hooks/usePdfFieldSelection.ts +8 -2
  181. package/src/hooks/usePinchZoom.ts +86 -0
  182. package/src/hooks/useZoom.ts +20 -8
  183. package/src/index.ts +1 -0
  184. package/src/interface/types.ts +39 -1
  185. package/src/styles/index.css +1 -0
  186. package/src/styles/inline-editable.css +2 -0
  187. package/src/styles/pdf-canvas.css +92 -15
  188. package/src/styles/pdf-editor.css +1 -0
  189. package/src/styles/pdf-field-overlay.css +40 -118
  190. package/src/styles/pdf-field.css +111 -0
  191. package/src/styles/variables.css +4 -0
  192. package/src/utils/data-model/index.ts +1 -0
  193. package/src/utils/data-model/resolve-fields-default-values.ts +19 -0
  194. package/src/utils/exposes-utils.ts +1 -0
  195. package/src/utils/field/__tests__/field-background-color.utils.test.ts +3 -3
  196. package/src/utils/field/__tests__/get-field-bounds-style.utils.test.ts +0 -1
  197. package/src/utils/field/__tests__/get-field-type-option.utils.test.ts +53 -0
  198. package/src/utils/field/__tests__/get-fillable-default-value.utils.test.ts +137 -0
  199. package/src/utils/field/field-background-color.utils.ts +1 -1
  200. package/src/utils/field/get-dropdown-field-items.utils.ts +14 -0
  201. package/src/utils/field/get-field-bounds-style.utils.ts +11 -11
  202. package/src/utils/field/get-field-type-option.utils.ts +16 -0
  203. package/src/utils/field/get-fillable-default-value.utils.ts +70 -0
  204. package/src/utils/field/get-navigable-fields.utils.ts +1 -1
  205. package/src/utils/field/index.ts +3 -0
  206. package/src/utils/pdf/__tests__/pinch-zoom.utils.test.ts +58 -0
  207. package/src/utils/pdf/index.ts +1 -0
  208. package/src/utils/pdf/pinch-zoom.utils.ts +30 -0
  209. package/src/utils/recipients/map-colors.ts +23 -23
@@ -1,23 +1,32 @@
1
1
  import { Button, Divider, Flex, Icon, Text } from '@servicetitan/anvil2';
2
- import IconNavigateDown from '@servicetitan/anvil2/assets/icons/material/round/arrow_downward.svg';
3
- import IconNavigateUp from '@servicetitan/anvil2/assets/icons/material/round/arrow_upward.svg';
4
- import IconNavigateBefore from '@servicetitan/anvil2/assets/icons/material/round/navigate_before.svg';
5
- import IconNavigateNext from '@servicetitan/anvil2/assets/icons/material/round/navigate_next.svg';
6
2
  import IconZoomIn from '@servicetitan/anvil2/assets/icons/material/round/zoom_in.svg';
7
3
  import IconZoomOut from '@servicetitan/anvil2/assets/icons/material/round/zoom_out.svg';
8
4
  import { FC } from 'react';
9
5
  import { ZOOM_MAX, ZOOM_MIN } from '../../constants';
10
6
 
11
- interface ZoomControlsProps {
7
+ export interface ZoomControlsProps {
12
8
  zoom: number;
9
+ vertical?: boolean;
10
+ position?: 'right-center' | 'right-bottom';
13
11
  onZoomIn(): void;
14
12
  onZoomOut(): void;
15
13
  onResetZoom(): void;
16
14
  }
17
15
 
18
- const ZoomControls: FC<ZoomControlsProps> = ({ onResetZoom, onZoomIn, onZoomOut, zoom }) => {
16
+ export const ZoomControls: FC<ZoomControlsProps> = ({
17
+ onResetZoom,
18
+ onZoomIn,
19
+ onZoomOut,
20
+ position = 'right-center',
21
+ vertical,
22
+ zoom,
23
+ }) => {
19
24
  return (
20
- <Flex alignItems="center" gap={1} className="dte-pdf-zoom-controls">
25
+ <Flex
26
+ alignItems="center"
27
+ gap={1}
28
+ className={`dte-pdf-zoom-controls ${vertical ? '--vertical' : '--horizontal'} --${position}`}
29
+ >
21
30
  <Button
22
31
  size="xsmall"
23
32
  appearance="ghost"
@@ -28,7 +37,7 @@ const ZoomControls: FC<ZoomControlsProps> = ({ onResetZoom, onZoomIn, onZoomOut,
28
37
  >
29
38
  <Icon svg={IconZoomOut} size="medium" />
30
39
  </Button>
31
-
40
+ <Divider vertical={!vertical} style={vertical ? { width: 20 } : { height: 20 }} />
32
41
  <Text
33
42
  variant="body"
34
43
  size="small"
@@ -38,7 +47,7 @@ const ZoomControls: FC<ZoomControlsProps> = ({ onResetZoom, onZoomIn, onZoomOut,
38
47
  >
39
48
  {Math.round(zoom * 100)}%
40
49
  </Text>
41
-
50
+ <Divider vertical={!vertical} style={vertical ? { width: 20 } : { height: 20 }} />
42
51
  <Button
43
52
  size="xsmall"
44
53
  appearance="ghost"
@@ -54,130 +63,23 @@ const ZoomControls: FC<ZoomControlsProps> = ({ onResetZoom, onZoomIn, onZoomOut,
54
63
  };
55
64
 
56
65
  interface FieldNavigationControlsProps {
57
- disabledNext: boolean;
58
- disabledOnFirstField: boolean;
59
- disabledOnLastField: boolean;
60
- disabledPrev: boolean;
66
+ isStarted: boolean;
67
+ disabled?: boolean;
61
68
  onNextField(): void;
62
- onPrevField(): void;
63
- onFirstField(): void;
64
- onLastField(): void;
65
69
  }
66
70
 
67
- const FieldNavigationControls: FC<FieldNavigationControlsProps> = ({
68
- disabledNext,
69
- disabledOnFirstField,
70
- disabledOnLastField,
71
- disabledPrev,
72
- onFirstField,
73
- onLastField,
71
+ export const FieldNavigationControls: FC<FieldNavigationControlsProps> = ({
72
+ disabled,
73
+ isStarted,
74
74
  onNextField,
75
- onPrevField,
76
75
  }) => {
77
76
  return (
78
- <Flex alignItems="center" gap="2" className="dte-pdf-field-navigation-controls">
79
- <Button
80
- size="xsmall"
81
- appearance="ghost"
82
- onClick={onPrevField}
83
- disabled={disabledPrev}
84
- aria-label="Go to previous field"
85
- title="Go to previous field"
86
- >
87
- <Icon svg={IconNavigateBefore} size="medium" />
88
- </Button>
89
- <Button
90
- size="xsmall"
91
- appearance="ghost"
92
- onClick={onNextField}
93
- disabled={disabledNext}
94
- aria-label="Go to next field"
95
- title="Go to next field"
96
- >
97
- <Icon svg={IconNavigateNext} size="medium" />
98
- </Button>
99
- <Divider vertical style={{ height: '100%' }} />
100
- <Button
101
- size="xsmall"
102
- appearance="ghost"
103
- onClick={onFirstField}
104
- disabled={disabledOnFirstField}
105
- aria-label="Go to first field"
106
- title="Go to first field"
107
- >
108
- <Icon svg={IconNavigateUp} size="medium" />
109
- </Button>
110
- <Button
111
- size="xsmall"
112
- appearance="ghost"
113
- onClick={onLastField}
114
- disabled={disabledOnLastField}
115
- aria-label="Go to last field"
116
- title="Go to last field"
117
- >
118
- <Icon svg={IconNavigateDown} size="medium" />
119
- </Button>
120
- </Flex>
121
- );
122
- };
123
-
124
- export interface CanvasControlsProps extends ZoomControlsProps {
125
- /** Show the zoom in/out/reset controls. */
126
- zoomable?: boolean;
127
- /** Show the previous/next fillable-field navigation controls. */
128
- enableInputNavigation?: boolean;
129
- disabledNext?: boolean;
130
- disabledPrev?: boolean;
131
- disabledOnFirstField?: boolean;
132
- disabledOnLastField?: boolean;
133
- onNextField?(): void;
134
- onPrevField?(): void;
135
- onFirstField?(): void;
136
- onLastField?(): void;
137
- }
138
-
139
- export const CanvasControls: FC<CanvasControlsProps> = ({
140
- disabledNext = false,
141
- disabledOnFirstField = false,
142
- disabledOnLastField = false,
143
- disabledPrev = false,
144
- enableInputNavigation = false,
145
- onFirstField = () => {},
146
- onLastField = () => {},
147
- onNextField = () => {},
148
- onPrevField = () => {},
149
- onResetZoom,
150
- onZoomIn,
151
- onZoomOut,
152
- zoom,
153
- zoomable = false,
154
- }) => {
155
- if (!zoomable && !enableInputNavigation) {
156
- return null;
157
- }
158
-
159
- return (
160
- <Flex justifyContent="center" gap={2} className="dte-pdf-canvas-controls">
161
- {zoomable && (
162
- <ZoomControls
163
- zoom={zoom}
164
- onZoomIn={onZoomIn}
165
- onZoomOut={onZoomOut}
166
- onResetZoom={onResetZoom}
167
- />
168
- )}
169
- {enableInputNavigation && (
170
- <FieldNavigationControls
171
- disabledOnFirstField={disabledOnFirstField}
172
- disabledOnLastField={disabledOnLastField}
173
- onFirstField={onFirstField}
174
- onLastField={onLastField}
175
- disabledNext={disabledNext}
176
- disabledPrev={disabledPrev}
177
- onNextField={onNextField}
178
- onPrevField={onPrevField}
179
- />
180
- )}
181
- </Flex>
77
+ <div
78
+ className={`dte-pdf-field-next-controls-button ${disabled && '--disabled'} ${isStarted && '--started'}`}
79
+ role="button"
80
+ onClick={onNextField}
81
+ >
82
+ <span>{isStarted ? 'Next' : 'Start'}</span>
83
+ </div>
182
84
  );
183
85
  };
@@ -10,13 +10,13 @@ import {
10
10
  useState,
11
11
  } from 'react';
12
12
  import { BASE_PAGE_WIDTH } from '../../constants';
13
- import { useZoom } from '../../hooks';
13
+ import { usePinchZoom, useZoom } from '../../hooks';
14
14
  import { type PdfField } from '../../interface/types';
15
15
  import { PdfFieldsOverlay } from '../pdf-fields-overlay/pdf-fields-overlay';
16
- import { CanvasControls } from './canvas-controls';
16
+ import { ZoomControls, ZoomControlsProps } from './canvas-controls';
17
17
  import { PdfDocumentRenderer } from './pdf-document-renderer';
18
18
 
19
- interface PdfCanvasProps {
19
+ export interface PdfCanvasProps {
20
20
  pdfUrl: string;
21
21
  zoomable: boolean;
22
22
  errorPlaceholder?: ReactNode;
@@ -29,6 +29,10 @@ interface PdfCanvasProps {
29
29
  pdfWrapperRef: RefObject<HTMLDivElement>;
30
30
  recipientsColors: Record<string, string>;
31
31
  fieldWrapperClassNames?: string;
32
+ zoomProps?: {
33
+ vertical: ZoomControlsProps['vertical'];
34
+ position: ZoomControlsProps['position'];
35
+ };
32
36
  onLoadSuccess?(pageNums: number): void;
33
37
  handleAddNewField(field: PdfField): void;
34
38
  onDragOver(e: DragEvent<HTMLDivElement>): void;
@@ -61,10 +65,17 @@ export const PdfCanvas: FC<PdfCanvasProps> = ({
61
65
  pdfWrapperRef,
62
66
  recipientsColors,
63
67
  selectedField,
68
+ zoomProps,
64
69
  zoomable,
65
70
  }) => {
66
71
  const [isPdfLoaded, setIsPdfLoaded] = useState<boolean>(false);
67
- const { resetZoom, zoom, zoomIn, zoomOut } = useZoom();
72
+ const { resetZoom, setZoomValue, zoom, zoomByFactor, zoomIn, zoomOut } = useZoom();
73
+ usePinchZoom(pdfContainerRef, {
74
+ enabled: zoomable,
75
+ setZoomValue,
76
+ zoom,
77
+ zoomByFactor,
78
+ });
68
79
 
69
80
  const onDocumentLoadSuccess = (pageNum: number) => {
70
81
  setIsPdfLoaded(true);
@@ -77,10 +88,9 @@ export const PdfCanvas: FC<PdfCanvasProps> = ({
77
88
 
78
89
  return (
79
90
  <Fragment>
80
- {isPdfLoaded && (
81
- <CanvasControls
82
- enableInputNavigation={false}
83
- zoomable={zoomable}
91
+ {isPdfLoaded && zoomable && (
92
+ <ZoomControls
93
+ {...zoomProps}
84
94
  zoom={zoom}
85
95
  onZoomIn={zoomIn}
86
96
  onZoomOut={zoomOut}
@@ -12,7 +12,7 @@ import {
12
12
  import { extractGroupedFieldsFromDataModel, mapColorsToRecipients } from '../../utils';
13
13
  import { FieldConfigPanelOverlay } from '../field-config-panel/field-config-panel-overlay';
14
14
  import { FieldSidebar } from '../field-sidebar/field-sidebar';
15
- import { PdfCanvas } from '../pdf-canvas/pdf-canvas';
15
+ import { PdfCanvas, PdfCanvasProps } from '../pdf-canvas/pdf-canvas';
16
16
 
17
17
  import '../../styles/index.css';
18
18
 
@@ -29,6 +29,7 @@ export interface PdfEditorProps {
29
29
  hideFields?: FieldTypeEnum[];
30
30
  hideConditionalLogic?: boolean;
31
31
  forms?: FormInfo[];
32
+ zoomProps?: PdfCanvasProps['zoomProps'];
32
33
  onFormSelect?: (
33
34
  formIds: number[],
34
35
  sendFormFields: (formFieldsByFormId: FormFieldsByFormIdI) => void,
@@ -52,6 +53,7 @@ export const PdfEditor: FC<PdfEditorProps> = ({
52
53
  onLoadSuccess,
53
54
  pdfUrl,
54
55
  recipients = [],
56
+ zoomProps,
55
57
  zoomable = true,
56
58
  }) => {
57
59
  const pdfContainerRef = useRef<HTMLDivElement>(null);
@@ -122,6 +124,7 @@ export const PdfEditor: FC<PdfEditorProps> = ({
122
124
  className="dte-pdf-editor-content-container skeleton-item"
123
125
  >
124
126
  <PdfCanvas
127
+ zoomProps={zoomProps}
125
128
  zoomable={zoomable}
126
129
  pdfUrl={pdfUrl}
127
130
  fields={fields}
@@ -129,7 +132,7 @@ export const PdfEditor: FC<PdfEditorProps> = ({
129
132
  errors={errors}
130
133
  onLoadSuccess={onLoadSuccess}
131
134
  recipientsColors={recipientsColors}
132
- fieldWrapperClassNames="--edit-mode"
135
+ fieldWrapperClassNames="--edit"
133
136
  selectedField={pdfFieldSelection.selectedField}
134
137
  pdfContainerRef={pdfContainerRef}
135
138
  pdfWrapperRef={pdfWrapperRef}
@@ -49,7 +49,7 @@ export const PdfFieldsOverlay: FC<PdfFieldsOverlayProps> = ({
49
49
  field={field}
50
50
  fieldWrapperClassNames={fieldWrapperClassNames}
51
51
  onFieldConfigChange={onFieldConfigChange}
52
- error={errors[field.id]}
52
+ error={errors[field.id] ?? errors[field.path ?? '']}
53
53
  handleAddNewField={handleAddNewField}
54
54
  onFieldMove={onFieldMove}
55
55
  onFieldClick={onFieldClick}
@@ -57,7 +57,7 @@ export const PdfFieldsOverlay: FC<PdfFieldsOverlayProps> = ({
57
57
  onFieldResize={onFieldResize}
58
58
  isSelected={selectedField?.id === field.id}
59
59
  recipientsColors={recipientsColors}
60
- isSameGroup={selectedField?.path === field.path}
60
+ isSameGroup={!!selectedField?.path && selectedField.path === field.path}
61
61
  pathToLabel={pathToLabel}
62
62
  zoom={zoom}
63
63
  />
@@ -1,3 +1,4 @@
1
+ import { Flex } from '@servicetitan/anvil2';
1
2
  import { FC } from 'react';
2
3
  import { type PdfField } from '../../interface/types';
3
4
 
@@ -7,11 +8,12 @@ interface PdfOverlayFieldESignProps {
7
8
 
8
9
  export const PdfOverlayFieldESign: FC<PdfOverlayFieldESignProps> = ({ field }) => {
9
10
  return (
10
- <span>
11
- {field.label} {field.required ? '*' : ''}
12
- <div className="dte-pdf-field-value" style={{ fontSize: '8px' }}>
13
- {field.path}
14
- </div>
15
- </span>
11
+ <Flex
12
+ alignItems="center"
13
+ className="dte-pdf-field-value"
14
+ style={{ fontSize: '8px', height: '100%' }}
15
+ >
16
+ {field.path} {field.required ? '*' : ''}
17
+ </Flex>
16
18
  );
17
19
  };
@@ -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
  };
@@ -1,8 +1,14 @@
1
+ import { Icon } from '@servicetitan/anvil2';
1
2
  import { FC, MouseEvent, RefObject } from 'react';
2
3
  import { DISABLE_FIELD_RESIZE } from '../../constants';
3
4
  import { useFieldDrag, useFieldResize } from '../../hooks';
4
5
  import { FieldTypeEnum, type PdfField } from '../../interface/types';
5
- import { getFieldBackgroundColor, getFieldBoundsStyle, getPagePosition } from '../../utils';
6
+ import {
7
+ getFieldBoundsStyle,
8
+ getFieldColor,
9
+ getFieldTypeOption,
10
+ getPagePosition,
11
+ } from '../../utils';
6
12
  import { PdfOverlayFieldCalculated } from './pdf-overlay-field-calculated';
7
13
  import { PdfOverlayFieldDataModel } from './pdf-overlay-field-data-model';
8
14
  import { PdfOverlayFieldESign } from './pdf-overlay-field-e-sign';
@@ -68,13 +74,13 @@ export const PdfOverlayField: FC<PdfOverlayFieldProps> = ({
68
74
  { ...field, page, x, y },
69
75
  {
70
76
  pageOffset: pagePos,
71
- backgroundColor: getFieldBackgroundColor(field.recipient, recipientsColors),
77
+ color: getFieldColor(field.recipient, recipientsColors),
72
78
  zoom,
73
79
  },
74
80
  );
75
81
 
76
82
  const className = [
77
- 'dte-pdf-field-editor',
83
+ 'dte-pdf-field-container',
78
84
  `${field.type}-${field.subType}`,
79
85
  fieldWrapperClassNames,
80
86
  isSelected ? '--selected' : '--unselected',
@@ -85,6 +91,8 @@ export const PdfOverlayField: FC<PdfOverlayFieldProps> = ({
85
91
  .filter(Boolean)
86
92
  .join(' ');
87
93
 
94
+ const fieldTypeOption = getFieldTypeOption(field);
95
+
88
96
  return (
89
97
  <div
90
98
  title={field.description}
@@ -95,7 +103,18 @@ export const PdfOverlayField: FC<PdfOverlayFieldProps> = ({
95
103
  onDragEnd={handleDragEnd}
96
104
  className={className}
97
105
  style={style}
106
+ data-path={field.path}
107
+ data-id={field.id}
98
108
  >
109
+ {fieldTypeOption && (
110
+ <div className="dte-pdf-field-label">
111
+ {fieldTypeOption.icon && (
112
+ <Icon svg={fieldTypeOption.icon} size="small" color="inherit" />
113
+ )}
114
+ {fieldTypeOption.label}
115
+ {!field.required && <span>(Optional)</span>}
116
+ </div>
117
+ )}
99
118
  {field.type === FieldTypeEnum.calculated && (
100
119
  <PdfOverlayFieldCalculated field={field} pathToLabel={pathToLabel} />
101
120
  )}
@@ -1,3 +1,4 @@
1
+ import { Flex } from '@servicetitan/anvil2';
1
2
  import { FC } from 'react';
2
3
  import { type PdfField } from '../../interface/types';
3
4
 
@@ -7,11 +8,12 @@ interface PdfViewESignProps {
7
8
 
8
9
  export const PdfViewESign: FC<PdfViewESignProps> = ({ field }) => {
9
10
  return (
10
- <span>
11
- {field.label} {field.required ? '*' : ''}
12
- <div className="dte-pdf-field-value" style={{ fontSize: '8px' }}>
13
- {field.path}
14
- </div>
15
- </span>
11
+ <Flex
12
+ alignItems="center"
13
+ className="dte-pdf-field-value"
14
+ style={{ fontSize: '8px', height: '100%' }}
15
+ >
16
+ {field.path} {field.required ? '*' : ''}
17
+ </Flex>
16
18
  );
17
19
  };
@@ -1,9 +1,11 @@
1
- import { FC, Fragment, PropsWithChildren, RefObject } from 'react';
1
+ import { Icon } from '@servicetitan/anvil2';
2
+ import { FC, PropsWithChildren, RefObject } from 'react';
2
3
  import { type DataModelValues, type PdfField } from '../../interface/types';
3
4
  import {
4
5
  evaluateDisplayCondition,
5
- getFieldBackgroundColor,
6
6
  getFieldBoundsStyle,
7
+ getFieldColor,
8
+ getFieldTypeOption,
7
9
  getPagePosition,
8
10
  } from '../../utils';
9
11
 
@@ -35,36 +37,34 @@ export const PdfViewFieldContainer: FC<PropsWithChildren<PdfViewFieldContainer>>
35
37
  }
36
38
 
37
39
  const pagePos = getPagePosition(field.page, pdfWrapperRef);
38
- const bgColor = getFieldBackgroundColor(field.recipient, recipientsColors);
40
+ const color = getFieldColor(field.recipient, recipientsColors);
41
+ const fieldTypeOption = getFieldTypeOption(field);
42
+
39
43
  return (
40
- <Fragment>
41
- <div
42
- id={`dte-pdf-field-${field.id}`}
43
- className={`dte-pdf-field-view --${field.type}`}
44
- tabIndex={-1}
45
- style={getFieldBoundsStyle(field, {
46
- pageOffset: pagePos,
47
- backgroundColor: bgColor,
48
- borderColor: error ? 'red' : undefined,
49
- zoom,
50
- })}
51
- onFocus={() => onFocus?.(field.id)}
52
- >
53
- {children}
54
- </div>
55
- {error && (
56
- <span
57
- style={{
58
- color: 'red',
59
- top: pagePos.top + (field.y + field.height) * zoom + 8,
60
- left: pagePos.left + field.x * zoom,
61
- minWidth: '100px',
62
- position: 'absolute',
63
- }}
64
- >
65
- {error}
66
- </span>
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
+ >
57
+ {fieldTypeOption && (
58
+ <div className="dte-pdf-field-label">
59
+ {fieldTypeOption.icon && (
60
+ <Icon svg={fieldTypeOption.icon} size="small" color="inherit" />
61
+ )}
62
+ {fieldTypeOption.label}
63
+ {!field.required && <span>(Optional)</span>}
64
+ </div>
67
65
  )}
68
- </Fragment>
66
+ <div style={{ width: 'inherit', height: 'inherit' }}>{children}</div>
67
+ {error && <span className="dte-pdf-field-error-message">{error}</span>}
68
+ </div>
69
69
  );
70
70
  };