@servicetitan/dte-pdf-editor 1.96.2 → 1.97.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.
Files changed (68) hide show
  1. package/README.md +11 -2
  2. package/dist/components/field-config-panel/advanced-settings.d.ts.map +1 -1
  3. package/dist/components/field-config-panel/advanced-settings.js +1 -0
  4. package/dist/components/field-config-panel/advanced-settings.js.map +1 -1
  5. package/dist/components/field-config-panel/e-sign-offset-rule-row.js +1 -1
  6. package/dist/components/field-config-panel/e-sign-offset-rule-row.js.map +1 -1
  7. package/dist/components/field-config-panel/e-sign-options-modal.d.ts.map +1 -1
  8. package/dist/components/field-config-panel/e-sign-options-modal.js +1 -0
  9. package/dist/components/field-config-panel/e-sign-options-modal.js.map +1 -1
  10. package/dist/components/field-config-panel/field-config-panel-overlay.d.ts.map +1 -1
  11. package/dist/components/field-config-panel/field-config-panel-overlay.js +24 -42
  12. package/dist/components/field-config-panel/field-config-panel-overlay.js.map +1 -1
  13. package/dist/components/field-config-panel/field-config-panel.d.ts +5 -0
  14. package/dist/components/field-config-panel/field-config-panel.d.ts.map +1 -1
  15. package/dist/components/field-config-panel/field-config-panel.js +5 -2
  16. package/dist/components/field-config-panel/field-config-panel.js.map +1 -1
  17. package/dist/components/field-config-panel/formula-modal.d.ts.map +1 -1
  18. package/dist/components/field-config-panel/formula-modal.js +4 -3
  19. package/dist/components/field-config-panel/formula-modal.js.map +1 -1
  20. package/dist/components/pdf-editor/pdf-editor.d.ts +6 -8
  21. package/dist/components/pdf-editor/pdf-editor.d.ts.map +1 -1
  22. package/dist/components/pdf-editor/pdf-editor.js.map +1 -1
  23. package/dist/constants/field.constants.js +1 -1
  24. package/dist/interface/types.d.ts +4 -8
  25. package/dist/interface/types.d.ts.map +1 -1
  26. package/dist/interface/types.js.map +1 -1
  27. package/dist/styles/field-config-panel-overlay.css +24 -0
  28. package/dist/utils/formula/index.d.ts +0 -1
  29. package/dist/utils/formula/index.d.ts.map +1 -1
  30. package/dist/utils/formula/index.js +0 -1
  31. package/dist/utils/formula/index.js.map +1 -1
  32. package/dist/vendor/dte-shared/business-calendar-validation.utils.d.ts +3 -0
  33. package/dist/vendor/dte-shared/business-calendar-validation.utils.js +4 -0
  34. package/dist/vendor/dte-shared/components/business-calendar-section.d.ts +1 -1
  35. package/dist/vendor/dte-shared/components/business-calendar-section.js +3 -2
  36. package/dist/vendor/dte-shared/components/schedule-drawer.d.ts +5 -3
  37. package/dist/vendor/dte-shared/components/schedule-drawer.js +19 -12
  38. package/dist/vendor/dte-shared/components/schedule-picker.js +10 -10
  39. package/dist/vendor/dte-shared/constants.d.ts +21 -0
  40. package/dist/vendor/dte-shared/constants.js +24 -0
  41. package/dist/vendor/dte-shared/field-schedule-editor-context.d.ts +31 -0
  42. package/dist/vendor/dte-shared/field-schedule-editor-context.js +110 -0
  43. package/dist/vendor/dte-shared/index.d.ts +2 -0
  44. package/dist/vendor/dte-shared/index.js +2 -0
  45. package/dist/vendor/dte-shared/schedule-bindings.utils.d.ts +22 -0
  46. package/dist/vendor/dte-shared/schedule-bindings.utils.js +31 -0
  47. package/dist/vendor/dte-shared/styles/schedule.css +2 -2
  48. package/dist/vendor/dte-shared/types.d.ts +62 -0
  49. package/package.json +2 -2
  50. package/src/components/field-config-panel/__tests__/field-config-panel-overlay.test.tsx +16 -0
  51. package/src/components/field-config-panel/__tests__/schedule-editor-placement.test.tsx +289 -0
  52. package/src/components/field-config-panel/advanced-settings.tsx +1 -0
  53. package/src/components/field-config-panel/e-sign-offset-rule-row.tsx +1 -1
  54. package/src/components/field-config-panel/e-sign-options-modal.tsx +1 -0
  55. package/src/components/field-config-panel/field-config-panel-overlay.tsx +64 -95
  56. package/src/components/field-config-panel/field-config-panel.tsx +10 -1
  57. package/src/components/field-config-panel/formula-modal.tsx +5 -8
  58. package/src/components/pdf-editor/pdf-editor.tsx +6 -8
  59. package/src/constants/field.constants.ts +1 -1
  60. package/src/interface/types.ts +4 -8
  61. package/src/styles/field-config-panel-overlay.css +24 -0
  62. package/src/utils/formula/index.ts +0 -1
  63. package/dist/utils/formula/schedule-bindings.utils.d.ts +0 -12
  64. package/dist/utils/formula/schedule-bindings.utils.d.ts.map +0 -1
  65. package/dist/utils/formula/schedule-bindings.utils.js +0 -17
  66. package/dist/utils/formula/schedule-bindings.utils.js.map +0 -1
  67. package/src/utils/formula/__tests__/schedule-bindings.utils.test.ts +0 -73
  68. package/src/utils/formula/schedule-bindings.utils.ts +0 -27
@@ -3,11 +3,7 @@ import IconRedo from '@servicetitan/anvil2/assets/icons/material/round/redo.svg'
3
3
  import IconUndo from '@servicetitan/anvil2/assets/icons/material/round/undo.svg';
4
4
  import { type ScheduleEditorContext } from '@servicetitan/dte-shared';
5
5
  import { FC, useCallback, useEffect, useMemo, useState } from 'react';
6
- import {
7
- DEFAULT_DATE_CALCULATED_FORMAT,
8
- DEFAULT_NUMBER_FORMAT,
9
- MODAL_CONTENT_MAX_HEIGHT,
10
- } from '../../constants';
6
+ import { DEFAULT_DATE_CALCULATED_FORMAT, DEFAULT_NUMBER_FORMAT } from '../../constants';
11
7
  import { useFormulaEditor } from '../../hooks';
12
8
  import {
13
9
  type CalculatedFieldFormat,
@@ -305,16 +301,17 @@ export const FormulaModal: FC<FormulaModalProps> = ({
305
301
  parsedTokens.length > 0;
306
302
 
307
303
  return (
308
- <Dialog open onClose={handleClose} size="xlarge">
304
+ <Dialog open onClose={handleClose} size="xlarge" fullScreen>
309
305
  <Dialog.Header>Formula Builder</Dialog.Header>
310
306
  <Divider />
311
- <Dialog.Content>
307
+ <Dialog.Content style={{ overflowY: 'auto', paddingBlock: 0 }}>
312
308
  <Flex
313
309
  flex={1}
314
310
  alignItems="stretch"
315
311
  gap={2}
316
312
  style={{
317
- height: MODAL_CONTENT_MAX_HEIGHT,
313
+ flexGrow: 1,
314
+ minHeight: 0,
318
315
  overflowY: 'auto',
319
316
  }}
320
317
  >
@@ -75,10 +75,10 @@ export interface PdfEditorProps {
75
75
  */
76
76
  onScheduleBindingsChange?(bindings: FieldScheduleBinding[]): void;
77
77
  /**
78
- * Creates a tenant schedule from the "Create New Schedule" drawer. Always creates a new
79
- * schedule — it never overwrites a selected one. On success the created schedule is
80
- * auto-selected in the picker. A rejected promise's `message` is shown as the drawer's error
81
- * (for example a duplicate-name conflict).
78
+ * Creates a tenant schedule from the "Create New Schedule" form (a drawer, or inside the Field
79
+ * Configuration panel for an e-sign `dateSignedOffset` field). Always creates a new schedule — it never overwrites a
80
+ * selected one. On success the created schedule is auto-selected in the picker. A rejected
81
+ * promise's `message` is shown as the form's error (for example a duplicate-name conflict).
82
82
  */
83
83
  onCreateSchedule?(request: SaveScheduleRequest): Promise<BusinessDaySchedule>;
84
84
  /**
@@ -89,10 +89,8 @@ export interface PdfEditorProps {
89
89
  onUpdateSchedule?(id: number, request: SaveScheduleRequest): Promise<BusinessDaySchedule>;
90
90
  /**
91
91
  * Permanently deletes a tenant schedule, from the picker's "Delete" action (DTE's
92
- * `DELETE api/holidays/schedules/{id}`). Fields still bound to it fall back to the default
93
- * schedule — `PdfEditor` selects the default schedule for the current field on success, but
94
- * does not itself update other fields' `scheduleBindings`; the host should drop bindings to
95
- * a deleted schedule when it next saves them.
92
+ * `DELETE api/holidays/schedules/{id}`). On success every field bound to it, the current one
93
+ * included, moves to the default schedule (toggle still on) through `onScheduleBindingsChange`.
96
94
  */
97
95
  onDeleteSchedule?(id: number): Promise<void>;
98
96
  onLoadSuccess?(numPages: number): void;
@@ -95,7 +95,7 @@ export const E_SIGN_FIELD_TYPES: FieldTypeOption[] = [
95
95
  icon: EventAvailableIcon,
96
96
  },
97
97
  {
98
- label: 'Calculated Singing Date',
98
+ label: 'Calculated Signing Date',
99
99
  type: FieldTypeEnum.eSign,
100
100
  subType: ESignFieldType.dateSignedOffset,
101
101
  icon: IconAppointmentsIcon,
@@ -283,15 +283,11 @@ export type {
283
283
 
284
284
  /**
285
285
  * A date calculated field whose custom business-day exclusion toggle is on, and the schedule it
286
- * uses (DTE's bindings, full-replace via `PUT api/holidays/templates/{id}/bindings`). A field with
287
- * no entry here uses the default calendar with the toggle off (plain calendar-day arithmetic).
286
+ * uses (DTE's bindings, full-replace via `PUT api/holidays/templates/{id}/bindings`), shared with
287
+ * `@servicetitan/dte-unlayer`. Here `fieldKey` is `PdfField.id`, and a field with no entry uses
288
+ * plain calendar-day arithmetic.
288
289
  */
289
- export interface FieldScheduleBinding {
290
- /** `PdfField.id`. */
291
- fieldKey: string;
292
- /** One of the tenant's schedules, or undefined for the default schedule. */
293
- scheduleId?: number;
294
- }
290
+ export type { FieldScheduleBinding } from '@servicetitan/dte-shared';
295
291
 
296
292
  /** Format options for a calculated field with a numeric result (number, currency, percent) */
297
293
  export interface CalculatedFormatForNumber {
@@ -33,3 +33,27 @@
33
33
  padding: var(--spacing-2);
34
34
  flex: 1;
35
35
  }
36
+
37
+ /*
38
+ * The schedule form of an e-sign date offset is portaled into this slot. While it has content it
39
+ * takes the place of the panel (the `:empty` and `~` selectors need the slot to come first and to
40
+ * hold nothing, not even whitespace, when it is closed).
41
+ */
42
+ .dte-field-config-panel-editor-slot {
43
+ flex: 1;
44
+ padding: var(--spacing-2);
45
+ height: 100%;
46
+ overflow: auto;
47
+ }
48
+
49
+ .dte-field-config-panel-editor-slot:empty {
50
+ display: none;
51
+ }
52
+
53
+ .dte-field-config-panel-overlay-inner {
54
+ flex: 1;
55
+ }
56
+
57
+ .dte-field-config-panel-editor-slot:not(:empty) ~ .dte-field-config-panel-overlay-inner {
58
+ display: none;
59
+ }
@@ -5,6 +5,5 @@ export * from './expression.utils';
5
5
  export * from './format-calculated-result.utils';
6
6
  export * from './formula-types';
7
7
  export * from './render-formula.utils';
8
- export * from './schedule-bindings.utils';
9
8
  export * from './validate-formula.utils';
10
9
  export * from './form-fields.utils';
@@ -1,12 +0,0 @@
1
- import { type FieldScheduleBinding } from '../../interface/types';
2
- /**
3
- * Replaces `fieldKey`'s binding: enabling adds it (default schedule, `scheduleId` undefined);
4
- * disabling removes it. Every other field's binding is returned unchanged.
5
- */
6
- export declare function setFieldScheduleEnabled(bindings: FieldScheduleBinding[], fieldKey: string, enabled: boolean): FieldScheduleBinding[];
7
- /**
8
- * Sets `fieldKey`'s selected schedule (`scheduleId` undefined for the default schedule),
9
- * enabling its toggle if it wasn't already on. Every other field's binding is returned unchanged.
10
- */
11
- export declare function setFieldSchedule(bindings: FieldScheduleBinding[], fieldKey: string, scheduleId: number | undefined): FieldScheduleBinding[];
12
- //# sourceMappingURL=schedule-bindings.utils.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"schedule-bindings.utils.d.ts","sourceRoot":"","sources":["../../../src/utils/formula/schedule-bindings.utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAElE;;;GAGG;AACH,wBAAgB,uBAAuB,CACnC,QAAQ,EAAE,oBAAoB,EAAE,EAChC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,OAAO,GACjB,oBAAoB,EAAE,CAGxB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC5B,QAAQ,EAAE,oBAAoB,EAAE,EAChC,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GAAG,SAAS,GAC/B,oBAAoB,EAAE,CAGxB"}
@@ -1,17 +0,0 @@
1
- /**
2
- * Replaces `fieldKey`'s binding: enabling adds it (default schedule, `scheduleId` undefined);
3
- * disabling removes it. Every other field's binding is returned unchanged.
4
- */
5
- export function setFieldScheduleEnabled(bindings, fieldKey, enabled) {
6
- const withoutField = bindings.filter(b => b.fieldKey !== fieldKey);
7
- return enabled ? [...withoutField, { fieldKey }] : withoutField;
8
- }
9
- /**
10
- * Sets `fieldKey`'s selected schedule (`scheduleId` undefined for the default schedule),
11
- * enabling its toggle if it wasn't already on. Every other field's binding is returned unchanged.
12
- */
13
- export function setFieldSchedule(bindings, fieldKey, scheduleId) {
14
- const withoutField = bindings.filter(b => b.fieldKey !== fieldKey);
15
- return [...withoutField, { fieldKey, scheduleId }];
16
- }
17
- //# sourceMappingURL=schedule-bindings.utils.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"schedule-bindings.utils.js","sourceRoot":"","sources":["../../../src/utils/formula/schedule-bindings.utils.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CACnC,QAAgC,EAChC,QAAgB,EAChB,OAAgB;IAEhB,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC;IACnE,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,YAAY,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC;AACpE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAC5B,QAAgC,EAChC,QAAgB,EAChB,UAA8B;IAE9B,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC;IACnE,OAAO,CAAC,GAAG,YAAY,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,CAAC;AACvD,CAAC"}
@@ -1,73 +0,0 @@
1
- import { type FieldScheduleBinding } from '../../../interface/types';
2
- import { setFieldSchedule, setFieldScheduleEnabled } from '../schedule-bindings.utils';
3
-
4
- describe('setFieldScheduleEnabled', () => {
5
- test('adds a default-schedule binding when enabling a field with no existing binding', () => {
6
- expect(setFieldScheduleEnabled([], 'field-a', true)).toEqual([{ fieldKey: 'field-a' }]);
7
- });
8
-
9
- test('removing a field with no binding is a no-op', () => {
10
- const bindings: FieldScheduleBinding[] = [{ fieldKey: 'field-b', scheduleId: 7 }];
11
- expect(setFieldScheduleEnabled(bindings, 'field-a', false)).toEqual(bindings);
12
- });
13
-
14
- test('disabling removes the field from the bindings', () => {
15
- const bindings: FieldScheduleBinding[] = [
16
- { fieldKey: 'field-a', scheduleId: 3 },
17
- { fieldKey: 'field-b' },
18
- ];
19
- expect(setFieldScheduleEnabled(bindings, 'field-a', false)).toEqual([
20
- { fieldKey: 'field-b' },
21
- ]);
22
- });
23
-
24
- test('re-enabling after disabling resets to the default schedule, not the previous one', () => {
25
- const bindings: FieldScheduleBinding[] = [{ fieldKey: 'field-a', scheduleId: 3 }];
26
- const disabled = setFieldScheduleEnabled(bindings, 'field-a', false);
27
- expect(setFieldScheduleEnabled(disabled, 'field-a', true)).toEqual([
28
- { fieldKey: 'field-a' },
29
- ]);
30
- });
31
-
32
- test('leaves other fields untouched (field identity isolation)', () => {
33
- const bindings: FieldScheduleBinding[] = [
34
- { fieldKey: 'field-a', scheduleId: 1 },
35
- { fieldKey: 'field-b', scheduleId: 2 },
36
- ];
37
- const result = setFieldScheduleEnabled(bindings, 'field-a', false);
38
- expect(result).toEqual([{ fieldKey: 'field-b', scheduleId: 2 }]);
39
- });
40
- });
41
-
42
- describe('setFieldSchedule', () => {
43
- test('sets the default schedule (scheduleId undefined) for a new field', () => {
44
- expect(setFieldSchedule([], 'field-a', undefined)).toEqual([
45
- { fieldKey: 'field-a', scheduleId: undefined },
46
- ]);
47
- });
48
-
49
- test('sets a named schedule id for a new field', () => {
50
- expect(setFieldSchedule([], 'field-a', 42)).toEqual([
51
- { fieldKey: 'field-a', scheduleId: 42 },
52
- ]);
53
- });
54
-
55
- test('replaces an existing binding for the same field', () => {
56
- const bindings: FieldScheduleBinding[] = [{ fieldKey: 'field-a', scheduleId: 1 }];
57
- expect(setFieldSchedule(bindings, 'field-a', 2)).toEqual([
58
- { fieldKey: 'field-a', scheduleId: 2 },
59
- ]);
60
- });
61
-
62
- test('leaves other fields untouched (field identity isolation)', () => {
63
- const bindings: FieldScheduleBinding[] = [
64
- { fieldKey: 'field-a', scheduleId: 1 },
65
- { fieldKey: 'field-b', scheduleId: 2 },
66
- ];
67
- const result = setFieldSchedule(bindings, 'field-a', 9);
68
- expect(result).toEqual([
69
- { fieldKey: 'field-b', scheduleId: 2 },
70
- { fieldKey: 'field-a', scheduleId: 9 },
71
- ]);
72
- });
73
- });
@@ -1,27 +0,0 @@
1
- import { type FieldScheduleBinding } from '../../interface/types';
2
-
3
- /**
4
- * Replaces `fieldKey`'s binding: enabling adds it (default schedule, `scheduleId` undefined);
5
- * disabling removes it. Every other field's binding is returned unchanged.
6
- */
7
- export function setFieldScheduleEnabled(
8
- bindings: FieldScheduleBinding[],
9
- fieldKey: string,
10
- enabled: boolean,
11
- ): FieldScheduleBinding[] {
12
- const withoutField = bindings.filter(b => b.fieldKey !== fieldKey);
13
- return enabled ? [...withoutField, { fieldKey }] : withoutField;
14
- }
15
-
16
- /**
17
- * Sets `fieldKey`'s selected schedule (`scheduleId` undefined for the default schedule),
18
- * enabling its toggle if it wasn't already on. Every other field's binding is returned unchanged.
19
- */
20
- export function setFieldSchedule(
21
- bindings: FieldScheduleBinding[],
22
- fieldKey: string,
23
- scheduleId: number | undefined,
24
- ): FieldScheduleBinding[] {
25
- const withoutField = bindings.filter(b => b.fieldKey !== fieldKey);
26
- return [...withoutField, { fieldKey, scheduleId }];
27
- }