@servicetitan/dte-pdf-editor 1.42.0 → 1.43.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 (120) hide show
  1. package/README.md +89 -45
  2. package/dist/components/display-conditions/condition-group.d.ts +8 -3
  3. package/dist/components/display-conditions/condition-group.d.ts.map +1 -1
  4. package/dist/components/display-conditions/condition-group.js +7 -2
  5. package/dist/components/display-conditions/condition-group.js.map +1 -1
  6. package/dist/components/display-conditions/condition-groups-section.d.ts +8 -3
  7. package/dist/components/display-conditions/condition-groups-section.d.ts.map +1 -1
  8. package/dist/components/display-conditions/condition-groups-section.js +2 -2
  9. package/dist/components/display-conditions/condition-groups-section.js.map +1 -1
  10. package/dist/components/display-conditions/condition-row.d.ts +8 -3
  11. package/dist/components/display-conditions/condition-row.d.ts.map +1 -1
  12. package/dist/components/display-conditions/condition-row.js +154 -22
  13. package/dist/components/display-conditions/condition-row.js.map +1 -1
  14. package/dist/components/display-conditions/display-condition-modal.d.ts +6 -2
  15. package/dist/components/display-conditions/display-condition-modal.d.ts.map +1 -1
  16. package/dist/components/display-conditions/display-condition-modal.js +7 -7
  17. package/dist/components/display-conditions/display-condition-modal.js.map +1 -1
  18. package/dist/components/display-conditions/display-condition.d.ts +1 -1
  19. package/dist/components/display-conditions/display-condition.d.ts.map +1 -1
  20. package/dist/components/display-conditions/display-condition.js +2 -2
  21. package/dist/components/display-conditions/display-condition.js.map +1 -1
  22. package/dist/components/field-config-panel/field-config-panel-overlay.d.ts +5 -2
  23. package/dist/components/field-config-panel/field-config-panel-overlay.d.ts.map +1 -1
  24. package/dist/components/field-config-panel/field-config-panel-overlay.js +2 -2
  25. package/dist/components/field-config-panel/field-config-panel-overlay.js.map +1 -1
  26. package/dist/components/field-config-panel/field-config-panel.d.ts +5 -2
  27. package/dist/components/field-config-panel/field-config-panel.d.ts.map +1 -1
  28. package/dist/components/field-config-panel/field-config-panel.js +9 -4
  29. package/dist/components/field-config-panel/field-config-panel.js.map +1 -1
  30. package/dist/components/field-config-panel/field-sidebar.d.ts +5 -1
  31. package/dist/components/field-config-panel/field-sidebar.d.ts.map +1 -1
  32. package/dist/components/field-config-panel/field-sidebar.js +42 -8
  33. package/dist/components/field-config-panel/field-sidebar.js.map +1 -1
  34. package/dist/components/field-config-panel/formula-generator.d.ts +5 -1
  35. package/dist/components/field-config-panel/formula-generator.d.ts.map +1 -1
  36. package/dist/components/field-config-panel/formula-generator.js +2 -2
  37. package/dist/components/field-config-panel/formula-generator.js.map +1 -1
  38. package/dist/components/field-config-panel/formula-modal.d.ts +5 -1
  39. package/dist/components/field-config-panel/formula-modal.d.ts.map +1 -1
  40. package/dist/components/field-config-panel/formula-modal.js +38 -8
  41. package/dist/components/field-config-panel/formula-modal.js.map +1 -1
  42. package/dist/components/field-sidebar/field-sidebar.d.ts +6 -1
  43. package/dist/components/field-sidebar/field-sidebar.d.ts.map +1 -1
  44. package/dist/components/field-sidebar/field-sidebar.js +11 -6
  45. package/dist/components/field-sidebar/field-sidebar.js.map +1 -1
  46. package/dist/components/field-sidebar/form-fields-type-list.d.ts +13 -0
  47. package/dist/components/field-sidebar/form-fields-type-list.d.ts.map +1 -0
  48. package/dist/components/field-sidebar/form-fields-type-list.js +14 -0
  49. package/dist/components/field-sidebar/form-fields-type-list.js.map +1 -0
  50. package/dist/components/pdf-editor/pdf-editor.d.ts +3 -1
  51. package/dist/components/pdf-editor/pdf-editor.d.ts.map +1 -1
  52. package/dist/components/pdf-editor/pdf-editor.js +6 -5
  53. package/dist/components/pdf-editor/pdf-editor.js.map +1 -1
  54. package/dist/components/pdf-fields-overlay/pdf-overlay-field.d.ts.map +1 -1
  55. package/dist/components/pdf-fields-overlay/pdf-overlay-field.js +1 -1
  56. package/dist/components/pdf-fields-overlay/pdf-overlay-field.js.map +1 -1
  57. package/dist/components/pdf-view/pdf-view.d.ts.map +1 -1
  58. package/dist/components/pdf-view/pdf-view.js +2 -1
  59. package/dist/components/pdf-view/pdf-view.js.map +1 -1
  60. package/dist/constants/menu-group.d.ts.map +1 -1
  61. package/dist/constants/menu-group.js +2 -0
  62. package/dist/constants/menu-group.js.map +1 -1
  63. package/dist/hooks/index.d.ts +1 -0
  64. package/dist/hooks/index.d.ts.map +1 -1
  65. package/dist/hooks/index.js +1 -0
  66. package/dist/hooks/index.js.map +1 -1
  67. package/dist/hooks/useFormFields.d.ts +13 -0
  68. package/dist/hooks/useFormFields.d.ts.map +1 -0
  69. package/dist/hooks/useFormFields.js +121 -0
  70. package/dist/hooks/useFormFields.js.map +1 -0
  71. package/dist/hooks/useFormulaEditor.d.ts.map +1 -1
  72. package/dist/hooks/useFormulaEditor.js +7 -1
  73. package/dist/hooks/useFormulaEditor.js.map +1 -1
  74. package/dist/hooks/usePdfFieldDnD.d.ts.map +1 -1
  75. package/dist/hooks/usePdfFieldDnD.js +4 -0
  76. package/dist/hooks/usePdfFieldDnD.js.map +1 -1
  77. package/dist/interface/types.d.ts +35 -3
  78. package/dist/interface/types.d.ts.map +1 -1
  79. package/dist/interface/types.js +1 -0
  80. package/dist/interface/types.js.map +1 -1
  81. package/dist/utils/formula/expression.utils.d.ts +5 -2
  82. package/dist/utils/formula/expression.utils.d.ts.map +1 -1
  83. package/dist/utils/formula/expression.utils.js +8 -6
  84. package/dist/utils/formula/expression.utils.js.map +1 -1
  85. package/dist/utils/formula/form-fields.utils.d.ts +21 -0
  86. package/dist/utils/formula/form-fields.utils.d.ts.map +1 -0
  87. package/dist/utils/formula/form-fields.utils.js +101 -0
  88. package/dist/utils/formula/form-fields.utils.js.map +1 -0
  89. package/dist/utils/formula/index.d.ts +1 -0
  90. package/dist/utils/formula/index.d.ts.map +1 -1
  91. package/dist/utils/formula/index.js +1 -0
  92. package/dist/utils/formula/index.js.map +1 -1
  93. package/package.json +1 -1
  94. package/src/components/display-conditions/condition-group.tsx +32 -5
  95. package/src/components/display-conditions/condition-groups-section.tsx +24 -4
  96. package/src/components/display-conditions/condition-row.tsx +359 -80
  97. package/src/components/display-conditions/display-condition-modal.tsx +39 -10
  98. package/src/components/display-conditions/display-condition.tsx +19 -2
  99. package/src/components/field-config-panel/field-config-panel-overlay.tsx +19 -2
  100. package/src/components/field-config-panel/field-config-panel.tsx +25 -5
  101. package/src/components/field-config-panel/field-sidebar.tsx +187 -41
  102. package/src/components/field-config-panel/formula-generator.tsx +14 -0
  103. package/src/components/field-config-panel/formula-modal.tsx +62 -5
  104. package/src/components/field-sidebar/field-sidebar.tsx +35 -4
  105. package/src/components/field-sidebar/form-fields-type-list.tsx +113 -0
  106. package/src/components/pdf-editor/pdf-editor.tsx +39 -25
  107. package/src/components/pdf-fields-overlay/pdf-overlay-field.tsx +3 -1
  108. package/src/components/pdf-view/pdf-view.tsx +2 -1
  109. package/src/constants/menu-group.ts +2 -0
  110. package/src/hooks/index.ts +1 -0
  111. package/src/hooks/useFormFields.ts +157 -0
  112. package/src/hooks/useFormulaEditor.ts +7 -1
  113. package/src/hooks/usePdfFieldDnD.ts +4 -0
  114. package/src/interface/types.ts +43 -4
  115. package/src/styles/field-type-list.css +1 -0
  116. package/src/styles/formula-modal.css +66 -8
  117. package/src/styles/variables.css +4 -0
  118. package/src/utils/formula/expression.utils.ts +24 -6
  119. package/src/utils/formula/form-fields.utils.ts +165 -0
  120. package/src/utils/formula/index.ts +1 -0
@@ -25,6 +25,52 @@
25
25
  flex-direction: column;
26
26
  min-height: 0;
27
27
  overflow-y: auto;
28
+ transition:
29
+ background-color 0.28s ease,
30
+ border-color 0.28s ease,
31
+ box-shadow 0.28s ease;
32
+ }
33
+
34
+ .dte-formula-sidebar-slide-outer {
35
+ flex: 1;
36
+ min-height: 0;
37
+ overflow-x: hidden;
38
+ overflow-y: auto;
39
+ width: 100%;
40
+ }
41
+
42
+ .dte-formula-sidebar-slide-track {
43
+ align-items: stretch;
44
+ display: flex;
45
+ flex-direction: row;
46
+ min-height: min-content;
47
+ width: 200%;
48
+ transition: transform 0.32s cubic-bezier(0.4, 0, 0.2, 1);
49
+ will-change: transform;
50
+ }
51
+
52
+ .dte-formula-sidebar-slide-track.--detail {
53
+ transform: translateX(-50%);
54
+ }
55
+
56
+ .dte-formula-sidebar-slide-panel {
57
+ box-sizing: border-box;
58
+ display: flex;
59
+ flex: 0 0 50%;
60
+ flex-direction: column;
61
+ gap: var(--spacing-2);
62
+ min-height: min-content;
63
+ padding-right: var(--spacing-1);
64
+ width: 50%;
65
+ }
66
+
67
+ .dte-formula-sidebar-slide-panel.--form-fields {
68
+ flex-shrink: 0;
69
+ gap: 0;
70
+ }
71
+
72
+ .dte-formula-sidebar-forms-heading {
73
+ flex-shrink: 0;
28
74
  }
29
75
 
30
76
  .dte-formula-field-list {
@@ -36,8 +82,16 @@
36
82
  .dte-formula-field-list-item {
37
83
  padding: var(--spacing-1) var(--spacing-2);
38
84
  cursor: pointer;
39
- border-left: 3px solid transparent;
40
- border-bottom: 1px solid var(--border-color);
85
+ border: 1px solid var(--border-color);
86
+ margin-block: var(--spacing-1);
87
+ border-radius: var(--spacing-1);
88
+ font-size: var(--typescale-1, 14px);
89
+ }
90
+
91
+ .dte-text-ellipsis {
92
+ overflow: hidden;
93
+ text-overflow: ellipsis;
94
+ white-space: nowrap;
41
95
  }
42
96
 
43
97
  .dte-formula-field-list-item:hover,
@@ -46,7 +100,7 @@
46
100
  }
47
101
 
48
102
  .dte-formula-field-list-item.--selected {
49
- border-left-color: var(--border-color-active, #0265dc);
103
+ border-color: var(--border-color-active-dark, #2d2e31);
50
104
  }
51
105
 
52
106
  .dte-formula-editor {
@@ -79,15 +133,17 @@
79
133
  .dte-formula-chip {
80
134
  display: inline-flex;
81
135
  align-items: center;
82
- padding: 2px 6px;
136
+ padding: 6px 12px;
83
137
  margin: var(--spacing-0) 2px;
84
- background: var(--menu-active-color, #0265dc);
85
- border-radius: var(--spacing-half, 4px);
86
- color: var(--white);
138
+ background: var(--menu-active-bg-light-color, #e0f2ff);
139
+ border: 1px solid var(--border-color-active);
140
+ border-radius: 20px;
141
+ color: var(--black, #000000);
87
142
  gap: var(--spacing-half);
88
143
  margin-bottom: var(--spacing-half);
89
144
  cursor: grab;
90
- font-weight: 500;
145
+ font-weight: normal;
146
+ font-size: var(--typescale-0);
91
147
  }
92
148
 
93
149
  .dte-formula-chip-label {
@@ -98,6 +154,8 @@
98
154
  cursor: pointer;
99
155
  opacity: 0.9;
100
156
  padding: var(--spacing-0) 2px;
157
+ font-size: var(--typescale-3);
158
+ font-weight: var(--font-weight-semibold);
101
159
  line-height: 1;
102
160
  }
103
161
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  .dte-pdf-editor {
4
4
  --menu-active-color: var(--color-blue-400, #2270ee);
5
+ --menu-active-bg-light-color: var(--color-blue-100, #e0f2ff);
5
6
  --menu-hover-color: var(--color-neutral-100, #606162);
6
7
 
7
8
  /* Typography */
@@ -13,6 +14,7 @@
13
14
  --spacing-half: 4px;
14
15
  --spacing-1: 8px;
15
16
  --spacing-2: 16px;
17
+ --spacing-3: 24px;
16
18
 
17
19
  /* Colors - Neutral */
18
20
  --color-neutral-0: var(--color-neutral-0, #ffffff);
@@ -31,11 +33,13 @@
31
33
  --green: #28a745;
32
34
  --red: #dc3545;
33
35
  --white: var(--color-neutral-0, #ffffff);
36
+ --black: #000000;
34
37
 
35
38
  /* Border colors*/
36
39
  --border-color: #e0e0e0;
37
40
  --border-color-hover: #bdbdbd;
38
41
  --border-color-active: var(--color-blue-400, #2270ee);
42
+ --border-color-active-dark: var(--color-neutral-300, #2d2e31);
39
43
 
40
44
  /* Layout sizes */
41
45
  --layout-sidebar-menu-width: 85px;
@@ -1,4 +1,10 @@
1
- import { FormulaToken, StructuredFormula } from '../../interface/types';
1
+ import {
2
+ FormFieldsByFormIdI,
3
+ FormInfo,
4
+ FormulaToken,
5
+ StructuredFormula,
6
+ } from '../../interface/types';
7
+ import { tryBuildFormFieldFormulaSnapshot } from './form-fields.utils';
2
8
 
3
9
  /**
4
10
  * Convert StructuredFormula tokens to an editable expression string.
@@ -61,6 +67,10 @@ export function parseExpression(
61
67
  expression: string,
62
68
  validPaths: Set<string>,
63
69
  knownDateFields?: Set<string>,
70
+ formContext?: {
71
+ forms: FormInfo[];
72
+ formFieldsByFormId: FormFieldsByFormIdI;
73
+ },
64
74
  ): FormulaToken[] {
65
75
  const parts = tokenizeExpression(expression).filter(
66
76
  p => p.type !== 'text' || p.value.trim() !== '',
@@ -74,11 +84,19 @@ export function parseExpression(
74
84
  if (part.type === 'field') {
75
85
  if (validPaths.has(part.value)) {
76
86
  const fieldType = knownDateFields?.has(part.value) ? 'date' : 'number';
77
- tokens.push({
78
- type: 'field',
79
- path: part.value,
80
- fieldType,
81
- });
87
+ const snapshot =
88
+ formContext && formContext.forms.length > 0
89
+ ? tryBuildFormFieldFormulaSnapshot(
90
+ part.value,
91
+ formContext.forms,
92
+ formContext.formFieldsByFormId,
93
+ )
94
+ : undefined;
95
+ tokens.push(
96
+ snapshot
97
+ ? { type: 'field', path: part.value, fieldType, formSnapshot: snapshot }
98
+ : { type: 'field', path: part.value, fieldType },
99
+ );
82
100
  }
83
101
  continue;
84
102
  }
@@ -0,0 +1,165 @@
1
+ import {
2
+ DataPointOption,
3
+ FieldTypeEnum,
4
+ FieldTypeOption,
5
+ FormFieldInfo,
6
+ FormFieldsByFormIdI,
7
+ FormFieldType,
8
+ FormInfo,
9
+ FormulaFieldFormSnapshot,
10
+ SchemaFieldType,
11
+ } from '../../interface/types';
12
+
13
+ export const FORM_FIELD_TYPES_FOR_CALC = ['date', 'number'] as FormFieldType[];
14
+
15
+ /** Root key and path prefix for form submission field references (matches Unlayer/tools). */
16
+ export const SUBMISSION_FIELDS_PATH_PREFIX = '__submission_fields';
17
+
18
+ export const normalizeFormFieldIdForPath = (fieldId: string): string => fieldId.replaceAll('-', '');
19
+
20
+ export const buildFormFieldKey = (formId: number, fieldId: string): string =>
21
+ `${SUBMISSION_FIELDS_PATH_PREFIX}.${formId}.${normalizeFormFieldIdForPath(fieldId)}`;
22
+
23
+ const SUBMISSION_FIELDS_DOT_PATH_RE = /^__submission_fields\.(\d+)\.([A-Za-z0-9]+)$/;
24
+ const LEGACY_SUBMISSION_FIELD_SINGULAR_RE = /^__submission_field\.(\d+)\.(.+)$/;
25
+
26
+ export const parseFormFieldKey = (key: string): { formId: number; fieldId: string } | null => {
27
+ let match = SUBMISSION_FIELDS_DOT_PATH_RE.exec(key);
28
+ if (match) {
29
+ return { formId: Number(match[1]), fieldId: match[2] };
30
+ }
31
+ match = LEGACY_SUBMISSION_FIELD_SINGULAR_RE.exec(key);
32
+ if (match) {
33
+ return { formId: Number(match[1]), fieldId: normalizeFormFieldIdForPath(match[2]) };
34
+ }
35
+ const legacyMatch = key.match(/^__form_(\d+)_([a-f0-9]+)$/i);
36
+ if (legacyMatch) {
37
+ return { formId: Number(legacyMatch[1]), fieldId: legacyMatch[2] };
38
+ }
39
+ return null;
40
+ };
41
+
42
+ export const buildFormFieldSnapshot = (
43
+ formId: number,
44
+ formName: string,
45
+ field: FormFieldInfo,
46
+ ): FormulaFieldFormSnapshot => ({
47
+ formId,
48
+ fieldId: field.id,
49
+ formName,
50
+ fieldName: field.header,
51
+ fieldType: field.itemType,
52
+ });
53
+
54
+ export const tryBuildFormFieldFormulaSnapshot = (
55
+ path: string,
56
+ forms: FormInfo[],
57
+ formFieldsByFormId: FormFieldsByFormIdI,
58
+ ): FormulaFieldFormSnapshot | undefined => {
59
+ const parsed = parseFormFieldKey(path);
60
+ if (!parsed) {
61
+ return undefined;
62
+ }
63
+ const form = forms.find(f => f.id === parsed.formId);
64
+ const fields = formFieldsByFormId[parsed.formId];
65
+ if (!form || !fields?.length) {
66
+ return undefined;
67
+ }
68
+ const field = fields.find(f => normalizeFormFieldIdForPath(f.id) === parsed.fieldId);
69
+ if (!field) {
70
+ return undefined;
71
+ }
72
+ return buildFormFieldSnapshot(parsed.formId, form.name, field);
73
+ };
74
+
75
+ export const isFormFieldCalculationType = (itemType: FormFieldType): itemType is SchemaFieldType =>
76
+ FORM_FIELD_TYPES_FOR_CALC.includes(itemType);
77
+
78
+ export const formFieldInfosToCalculationOptions = (
79
+ formId: number,
80
+ fields: FormFieldInfo[],
81
+ formName: string,
82
+ ): FieldTypeOption[] =>
83
+ fields
84
+ .filter((f): f is FormFieldInfo & { itemType: SchemaFieldType } =>
85
+ isFormFieldCalculationType(f.itemType),
86
+ )
87
+ .map(field => ({
88
+ type: FieldTypeEnum.forms,
89
+ label: field.header,
90
+ path: buildFormFieldKey(formId, field.id),
91
+ fieldType: field.itemType,
92
+ formSnapshot: buildFormFieldSnapshot(formId, formName, field),
93
+ }));
94
+
95
+ export const formFieldInfoToFieldTypeOption = (
96
+ formId: number,
97
+ field: FormFieldInfo,
98
+ formName: string,
99
+ ): FieldTypeOption => ({
100
+ type: FieldTypeEnum.forms,
101
+ label: field.header,
102
+ path: buildFormFieldKey(formId, field.id),
103
+ formSnapshot: buildFormFieldSnapshot(formId, formName, field),
104
+ ...(isFormFieldCalculationType(field.itemType) ? { fieldType: field.itemType } : {}),
105
+ });
106
+
107
+ export const formFieldToDisplayConditionDataPointOption = (
108
+ form: FormInfo,
109
+ field: FormFieldInfo,
110
+ ): DataPointOption => ({
111
+ fullKey: buildFormFieldKey(form.id, field.id),
112
+ title: field.header,
113
+ fieldType: field.itemType === 'number' ? 'number' : 'string',
114
+ formSnapshot: buildFormFieldSnapshot(form.id, form.name, field),
115
+ });
116
+
117
+ export const getDisplayConditionFieldTypeForKey = (
118
+ dataPointKey: string,
119
+ mergeTagOptions: DataPointOption[],
120
+ fillableOptions: DataPointOption[],
121
+ formFieldsByFormId: FormFieldsByFormIdI,
122
+ ): 'number' | 'string' => {
123
+ const fromLists = [...mergeTagOptions, ...fillableOptions].find(
124
+ o => o.fullKey === dataPointKey,
125
+ );
126
+ if (fromLists) {
127
+ return fromLists.fieldType;
128
+ }
129
+ const parsed = parseFormFieldKey(dataPointKey);
130
+ if (!parsed) {
131
+ return 'string';
132
+ }
133
+ const fields = formFieldsByFormId[parsed.formId];
134
+ const field = fields?.find(f => normalizeFormFieldIdForPath(f.id) === parsed.fieldId);
135
+ if (!field) {
136
+ return 'string';
137
+ }
138
+ return field.itemType === 'number' ? 'number' : 'string';
139
+ };
140
+
141
+ /** Subset of {@link FieldTypeEnum} used as display-condition row “Source”. */
142
+ export type DisplayConditionSourceKind = Extract<
143
+ FieldTypeEnum,
144
+ FieldTypeEnum.dataModel | FieldTypeEnum.fillable | FieldTypeEnum.forms
145
+ >;
146
+
147
+ export const inferDisplayConditionSourceKind = (
148
+ dataPointKey: string,
149
+ mergeTagOptions: DataPointOption[],
150
+ fillableOptions: DataPointOption[],
151
+ ): DisplayConditionSourceKind | null => {
152
+ if (!dataPointKey) {
153
+ return null;
154
+ }
155
+ if (parseFormFieldKey(dataPointKey)) {
156
+ return FieldTypeEnum.forms;
157
+ }
158
+ if (fillableOptions.some(o => o.fullKey === dataPointKey)) {
159
+ return FieldTypeEnum.fillable;
160
+ }
161
+ if (mergeTagOptions.some(o => o.fullKey === dataPointKey)) {
162
+ return FieldTypeEnum.dataModel;
163
+ }
164
+ return null;
165
+ };
@@ -6,3 +6,4 @@ export * from './format-calculated-result.utils';
6
6
  export * from './formula-types';
7
7
  export * from './render-formula.utils';
8
8
  export * from './validate-formula.utils';
9
+ export * from './form-fields.utils';