@servicetitan/dte-pdf-editor 1.16.0 → 1.18.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 (190) hide show
  1. package/README.md +35 -7
  2. package/dist/components/field-config-panel/advanced-settings.d.ts +9 -0
  3. package/dist/components/field-config-panel/advanced-settings.d.ts.map +1 -0
  4. package/dist/components/field-config-panel/advanced-settings.js +17 -0
  5. package/dist/components/field-config-panel/advanced-settings.js.map +1 -0
  6. package/dist/components/field-config-panel/field-config-panel-overlay.d.ts +4 -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-overlay.js +2 -2
  9. package/dist/components/field-config-panel/field-config-panel-overlay.js.map +1 -1
  10. package/dist/components/field-config-panel/field-config-panel.d.ts +4 -1
  11. package/dist/components/field-config-panel/field-config-panel.d.ts.map +1 -1
  12. package/dist/components/field-config-panel/field-config-panel.js +11 -5
  13. package/dist/components/field-config-panel/field-config-panel.js.map +1 -1
  14. package/dist/components/field-config-panel/field-sidebar.d.ts +13 -0
  15. package/dist/components/field-config-panel/field-sidebar.d.ts.map +1 -0
  16. package/dist/components/field-config-panel/field-sidebar.js +20 -0
  17. package/dist/components/field-config-panel/field-sidebar.js.map +1 -0
  18. package/dist/components/field-config-panel/formula-generator.d.ts +11 -0
  19. package/dist/components/field-config-panel/formula-generator.d.ts.map +1 -0
  20. package/dist/components/field-config-panel/formula-generator.js +51 -0
  21. package/dist/components/field-config-panel/formula-generator.js.map +1 -0
  22. package/dist/components/field-config-panel/formula-modal.d.ts +12 -0
  23. package/dist/components/field-config-panel/formula-modal.d.ts.map +1 -0
  24. package/dist/components/field-config-panel/formula-modal.js +99 -0
  25. package/dist/components/field-config-panel/formula-modal.js.map +1 -0
  26. package/dist/components/field-config-panel/formula-workspace.d.ts +23 -0
  27. package/dist/components/field-config-panel/formula-workspace.d.ts.map +1 -0
  28. package/dist/components/field-config-panel/formula-workspace.js +28 -0
  29. package/dist/components/field-config-panel/formula-workspace.js.map +1 -0
  30. package/dist/components/field-config-panel/result-type-selector.d.ts +9 -0
  31. package/dist/components/field-config-panel/result-type-selector.d.ts.map +1 -0
  32. package/dist/components/field-config-panel/result-type-selector.js +10 -0
  33. package/dist/components/field-config-panel/result-type-selector.js.map +1 -0
  34. package/dist/components/field-sidebar/calculated-field-type-list.d.ts +9 -0
  35. package/dist/components/field-sidebar/calculated-field-type-list.d.ts.map +1 -0
  36. package/dist/components/field-sidebar/calculated-field-type-list.js +12 -0
  37. package/dist/components/field-sidebar/calculated-field-type-list.js.map +1 -0
  38. package/dist/components/field-sidebar/data-model-field-type-list.d.ts +0 -1
  39. package/dist/components/field-sidebar/data-model-field-type-list.d.ts.map +1 -1
  40. package/dist/components/field-sidebar/data-model-field-type-list.js +8 -7
  41. package/dist/components/field-sidebar/data-model-field-type-list.js.map +1 -1
  42. package/dist/components/field-sidebar/field-menu-group.d.ts +11 -0
  43. package/dist/components/field-sidebar/field-menu-group.d.ts.map +1 -0
  44. package/dist/components/field-sidebar/field-menu-group.js +6 -0
  45. package/dist/components/field-sidebar/field-menu-group.js.map +1 -0
  46. package/dist/components/field-sidebar/field-sidebar.d.ts.map +1 -1
  47. package/dist/components/field-sidebar/field-sidebar.js +6 -15
  48. package/dist/components/field-sidebar/field-sidebar.js.map +1 -1
  49. package/dist/components/field-sidebar/fillable-field-type-list.d.ts +0 -1
  50. package/dist/components/field-sidebar/fillable-field-type-list.d.ts.map +1 -1
  51. package/dist/components/field-sidebar/fillable-field-type-list.js +8 -9
  52. package/dist/components/field-sidebar/fillable-field-type-list.js.map +1 -1
  53. package/dist/components/pdf-editor/pdf-editor.d.ts.map +1 -1
  54. package/dist/components/pdf-editor/pdf-editor.js +1 -1
  55. package/dist/components/pdf-editor/pdf-editor.js.map +1 -1
  56. package/dist/components/pdf-fields-overlay/pdf-overlay-field-calculated.d.ts +8 -0
  57. package/dist/components/pdf-fields-overlay/pdf-overlay-field-calculated.d.ts.map +1 -0
  58. package/dist/components/pdf-fields-overlay/pdf-overlay-field-calculated.js +5 -0
  59. package/dist/components/pdf-fields-overlay/pdf-overlay-field-calculated.js.map +1 -0
  60. package/dist/components/pdf-fields-overlay/pdf-overlay-field.d.ts.map +1 -1
  61. package/dist/components/pdf-fields-overlay/pdf-overlay-field.js +11 -6
  62. package/dist/components/pdf-fields-overlay/pdf-overlay-field.js.map +1 -1
  63. package/dist/components/pdf-view/pdf-view-calculated.d.ts +9 -0
  64. package/dist/components/pdf-view/pdf-view-calculated.d.ts.map +1 -0
  65. package/dist/components/pdf-view/pdf-view-calculated.js +18 -0
  66. package/dist/components/pdf-view/pdf-view-calculated.js.map +1 -0
  67. package/dist/components/pdf-view/pdf-view.d.ts.map +1 -1
  68. package/dist/components/pdf-view/pdf-view.js +2 -1
  69. package/dist/components/pdf-view/pdf-view.js.map +1 -1
  70. package/dist/constants/field.constants.d.ts +3 -2
  71. package/dist/constants/field.constants.d.ts.map +1 -1
  72. package/dist/constants/field.constants.js +6 -0
  73. package/dist/constants/field.constants.js.map +1 -1
  74. package/dist/constants/menu-group.d.ts +8 -0
  75. package/dist/constants/menu-group.d.ts.map +1 -0
  76. package/dist/constants/menu-group.js +20 -0
  77. package/dist/constants/menu-group.js.map +1 -0
  78. package/dist/hooks/index.d.ts +1 -0
  79. package/dist/hooks/index.d.ts.map +1 -1
  80. package/dist/hooks/index.js +1 -0
  81. package/dist/hooks/index.js.map +1 -1
  82. package/dist/hooks/useFieldDrag.d.ts +9 -2
  83. package/dist/hooks/useFieldDrag.d.ts.map +1 -1
  84. package/dist/hooks/useFieldDrag.js +60 -8
  85. package/dist/hooks/useFieldDrag.js.map +1 -1
  86. package/dist/hooks/useFormulaEditor.d.ts +22 -0
  87. package/dist/hooks/useFormulaEditor.d.ts.map +1 -0
  88. package/dist/hooks/useFormulaEditor.js +290 -0
  89. package/dist/hooks/useFormulaEditor.js.map +1 -0
  90. package/dist/hooks/usePdfFieldDnD.d.ts.map +1 -1
  91. package/dist/hooks/usePdfFieldDnD.js +19 -3
  92. package/dist/hooks/usePdfFieldDnD.js.map +1 -1
  93. package/dist/interface/types.d.ts +45 -3
  94. package/dist/interface/types.d.ts.map +1 -1
  95. package/dist/interface/types.js +3 -0
  96. package/dist/interface/types.js.map +1 -1
  97. package/dist/utils/data-model/extract-fields.utils.d.ts +5 -5
  98. package/dist/utils/data-model/extract-fields.utils.d.ts.map +1 -1
  99. package/dist/utils/data-model/extract-fields.utils.js +42 -8
  100. package/dist/utils/data-model/extract-fields.utils.js.map +1 -1
  101. package/dist/utils/formula/caret.utils.d.ts +3 -0
  102. package/dist/utils/formula/caret.utils.d.ts.map +1 -0
  103. package/dist/utils/formula/caret.utils.js +123 -0
  104. package/dist/utils/formula/caret.utils.js.map +1 -0
  105. package/dist/utils/formula/dom.utils.d.ts +4 -0
  106. package/dist/utils/formula/dom.utils.d.ts.map +1 -0
  107. package/dist/utils/formula/dom.utils.js +34 -0
  108. package/dist/utils/formula/dom.utils.js.map +1 -0
  109. package/dist/utils/formula/evaluate-formula.utils.d.ts +13 -0
  110. package/dist/utils/formula/evaluate-formula.utils.d.ts.map +1 -0
  111. package/dist/utils/formula/evaluate-formula.utils.js +134 -0
  112. package/dist/utils/formula/evaluate-formula.utils.js.map +1 -0
  113. package/dist/utils/formula/expression.utils.d.ts +18 -0
  114. package/dist/utils/formula/expression.utils.d.ts.map +1 -0
  115. package/dist/utils/formula/expression.utils.js +84 -0
  116. package/dist/utils/formula/expression.utils.js.map +1 -0
  117. package/dist/utils/formula/format-calculated-result.utils.d.ts +7 -0
  118. package/dist/utils/formula/format-calculated-result.utils.d.ts.map +1 -0
  119. package/dist/utils/formula/format-calculated-result.utils.js +50 -0
  120. package/dist/utils/formula/format-calculated-result.utils.js.map +1 -0
  121. package/dist/utils/formula/formula-types.d.ts +3 -0
  122. package/dist/utils/formula/formula-types.d.ts.map +1 -0
  123. package/dist/utils/formula/formula-types.js +2 -0
  124. package/dist/utils/formula/formula-types.js.map +1 -0
  125. package/dist/utils/formula/index.d.ts +11 -0
  126. package/dist/utils/formula/index.d.ts.map +1 -0
  127. package/dist/utils/formula/index.js +11 -0
  128. package/dist/utils/formula/index.js.map +1 -0
  129. package/dist/utils/formula/referenced-paths.utils.d.ts +7 -0
  130. package/dist/utils/formula/referenced-paths.utils.d.ts.map +1 -0
  131. package/dist/utils/formula/referenced-paths.utils.js +18 -0
  132. package/dist/utils/formula/referenced-paths.utils.js.map +1 -0
  133. package/dist/utils/formula/render-formula.utils.d.ts +8 -0
  134. package/dist/utils/formula/render-formula.utils.d.ts.map +1 -0
  135. package/dist/utils/formula/render-formula.utils.js +39 -0
  136. package/dist/utils/formula/render-formula.utils.js.map +1 -0
  137. package/dist/utils/formula/serialize-formula.utils.d.ts +14 -0
  138. package/dist/utils/formula/serialize-formula.utils.d.ts.map +1 -0
  139. package/dist/utils/formula/serialize-formula.utils.js +33 -0
  140. package/dist/utils/formula/serialize-formula.utils.js.map +1 -0
  141. package/dist/utils/formula/validate-formula.utils.d.ts +11 -0
  142. package/dist/utils/formula/validate-formula.utils.d.ts.map +1 -0
  143. package/dist/utils/formula/validate-formula.utils.js +79 -0
  144. package/dist/utils/formula/validate-formula.utils.js.map +1 -0
  145. package/dist/utils/index.d.ts +1 -0
  146. package/dist/utils/index.d.ts.map +1 -1
  147. package/dist/utils/index.js +1 -0
  148. package/dist/utils/index.js.map +1 -1
  149. package/package.json +2 -2
  150. package/src/components/field-config-panel/advanced-settings.tsx +113 -0
  151. package/src/components/field-config-panel/field-config-panel-overlay.tsx +8 -1
  152. package/src/components/field-config-panel/field-config-panel.tsx +43 -15
  153. package/src/components/field-config-panel/field-sidebar.tsx +91 -0
  154. package/src/components/field-config-panel/formula-generator.tsx +122 -0
  155. package/src/components/field-config-panel/formula-modal.tsx +229 -0
  156. package/src/components/field-config-panel/formula-workspace.tsx +116 -0
  157. package/src/components/field-config-panel/result-type-selector.tsx +34 -0
  158. package/src/components/field-sidebar/calculated-field-type-list.tsx +29 -0
  159. package/src/components/field-sidebar/data-model-field-type-list.tsx +10 -4
  160. package/src/components/field-sidebar/field-menu-group.tsx +36 -0
  161. package/src/components/field-sidebar/field-sidebar.tsx +14 -55
  162. package/src/components/field-sidebar/fillable-field-type-list.tsx +11 -9
  163. package/src/components/pdf-editor/pdf-editor.tsx +2 -0
  164. package/src/components/pdf-fields-overlay/pdf-overlay-field-calculated.tsx +15 -0
  165. package/src/components/pdf-fields-overlay/pdf-overlay-field.tsx +10 -5
  166. package/src/components/pdf-view/pdf-view-calculated.tsx +23 -0
  167. package/src/components/pdf-view/pdf-view.tsx +4 -0
  168. package/src/constants/field.constants.ts +9 -2
  169. package/src/constants/menu-group.ts +26 -0
  170. package/src/hooks/index.ts +1 -0
  171. package/src/hooks/useFieldDrag.ts +84 -8
  172. package/src/hooks/useFormulaEditor.ts +336 -0
  173. package/src/hooks/usePdfFieldDnD.ts +36 -14
  174. package/src/interface/types.ts +38 -2
  175. package/src/styles/formula-modal.css +307 -0
  176. package/src/styles/index.css +1 -0
  177. package/src/styles/pdf-field-overlay.css +1 -0
  178. package/src/utils/data-model/extract-fields.utils.ts +65 -7
  179. package/src/utils/formula/caret.utils.ts +125 -0
  180. package/src/utils/formula/dom.utils.ts +35 -0
  181. package/src/utils/formula/evaluate-formula.utils.ts +159 -0
  182. package/src/utils/formula/expression.utils.ts +99 -0
  183. package/src/utils/formula/format-calculated-result.utils.ts +79 -0
  184. package/src/utils/formula/formula-types.ts +2 -0
  185. package/src/utils/formula/index.ts +10 -0
  186. package/src/utils/formula/referenced-paths.utils.ts +18 -0
  187. package/src/utils/formula/render-formula.utils.ts +40 -0
  188. package/src/utils/formula/serialize-formula.utils.ts +40 -0
  189. package/src/utils/formula/validate-formula.utils.ts +94 -0
  190. package/src/utils/index.ts +1 -0
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Convert StructuredFormula tokens to an editable expression string.
3
+ * Uses field path as the identifier so parsing is unambiguous.
4
+ */
5
+ export function tokensToExpression(formula) {
6
+ var _a;
7
+ if (!((_a = formula === null || formula === void 0 ? void 0 : formula.tokens) === null || _a === void 0 ? void 0 : _a.length)) {
8
+ return '';
9
+ }
10
+ return formula.tokens
11
+ .map(t => {
12
+ if (t.type === 'field') {
13
+ return t.path;
14
+ }
15
+ return t.value;
16
+ })
17
+ .join(' ')
18
+ .replace(/\s+/g, ' ')
19
+ .trim();
20
+ }
21
+ // Field paths may contain hyphens (e.g. fillable_recipient_uuid with UUIDs)
22
+ const EXPRESSION_REGEX = /[A-Za-z_][A-Za-z0-9_.-]*|\d+(?:\.\d+)?|[()+\-*/]|\s+|./g;
23
+ export function tokenizeExpression(expression) {
24
+ const tokens = [];
25
+ let match;
26
+ while ((match = EXPRESSION_REGEX.exec(expression)) !== null) {
27
+ const value = match[0];
28
+ if (/^[A-Za-z_]/.test(value)) {
29
+ tokens.push({ type: 'field', value });
30
+ }
31
+ else if (/^\d/.test(value)) {
32
+ tokens.push({ type: 'number', value });
33
+ }
34
+ else if (/^[()+\-*/]$/.test(value)) {
35
+ tokens.push({ type: value === '(' || value === ')' ? 'paren' : 'operator', value });
36
+ }
37
+ else {
38
+ tokens.push({ type: 'text', value });
39
+ }
40
+ }
41
+ return tokens;
42
+ }
43
+ /**
44
+ * Parse an expression string into FormulaToken[] using valid paths and path->label map.
45
+ * Invalid or unknown identifiers are skipped (or could be left as placeholder); only known paths become field tokens.
46
+ */
47
+ export function parseExpression(expression, validPaths, pathToLabel) {
48
+ var _a;
49
+ const parts = tokenizeExpression(expression).filter(p => p.type !== 'text' || p.value.trim() !== '');
50
+ const tokens = [];
51
+ for (const part of parts) {
52
+ if (part.type === 'text') {
53
+ continue;
54
+ }
55
+ if (part.type === 'field') {
56
+ if (validPaths.has(part.value)) {
57
+ tokens.push({
58
+ type: 'field',
59
+ path: part.value,
60
+ label: (_a = pathToLabel.get(part.value)) !== null && _a !== void 0 ? _a : part.value,
61
+ });
62
+ }
63
+ continue;
64
+ }
65
+ if (part.type === 'number') {
66
+ tokens.push({ type: 'number', value: part.value });
67
+ continue;
68
+ }
69
+ if (part.type === 'operator') {
70
+ tokens.push({
71
+ type: 'operator',
72
+ value: part.value,
73
+ });
74
+ continue;
75
+ }
76
+ if (part.type === 'paren') {
77
+ tokens.push(part.value === '('
78
+ ? { type: 'lparen', value: '(' }
79
+ : { type: 'rparen', value: ')' });
80
+ }
81
+ }
82
+ return tokens;
83
+ }
84
+ //# sourceMappingURL=expression.utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expression.utils.js","sourceRoot":"","sources":["../../../src/utils/formula/expression.utils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import { CalculatedFieldFormat } from '../../interface/types';
2
+ /**
3
+ * Format a calculated formula result for display using advanced settings.
4
+ * Applies rounding, decimal places, thousands/decimal separators, result type (number/currency/percent), and prefix/postfix.
5
+ */
6
+ export declare function formatCalculatedResult(value: number, format: CalculatedFieldFormat | undefined): string;
7
+ //# sourceMappingURL=format-calculated-result.utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format-calculated-result.utils.d.ts","sourceRoot":"","sources":["../../../src/utils/formula/format-calculated-result.utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAsC9D;;;GAGG;AACH,wBAAgB,sBAAsB,CAClC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,qBAAqB,GAAG,SAAS,GAC1C,MAAM,CAiCR"}
@@ -0,0 +1,50 @@
1
+ function roundValue(value, decimals, mode) {
2
+ const factor = 10 ** decimals;
3
+ const scaled = value * factor;
4
+ switch (mode) {
5
+ case 'floor':
6
+ return Math.floor(scaled) / factor;
7
+ case 'ceil':
8
+ return Math.ceil(scaled) / factor;
9
+ default:
10
+ return Math.round(scaled) / factor;
11
+ }
12
+ }
13
+ function formatIntegerPart(s, thousandsSeparator, decimalSeparator) {
14
+ if (!thousandsSeparator) {
15
+ return s;
16
+ }
17
+ const sep = decimalSeparator === ',' ? '.' : ','; // opposite for thousands
18
+ const parts = [];
19
+ let i = s.length;
20
+ while (i > 0) {
21
+ const start = Math.max(0, i - 3);
22
+ parts.unshift(s.slice(start, i));
23
+ i = start;
24
+ }
25
+ return parts.join(sep);
26
+ }
27
+ /**
28
+ * Format a calculated formula result for display using advanced settings.
29
+ * Applies rounding, decimal places, thousands/decimal separators, result type (number/currency/percent), and prefix/postfix.
30
+ */
31
+ export function formatCalculatedResult(value, format) {
32
+ if (format == null) {
33
+ return String(value);
34
+ }
35
+ const { decimalSeparator, decimalSeparatorEnabled, decimals, postfixText, prefixText, resultType, roundingMode, thousandsSeparator, } = format;
36
+ let num = value;
37
+ if (resultType === 'percent') {
38
+ num = value * 100;
39
+ }
40
+ const rounded = roundValue(num, decimals, roundingMode);
41
+ const fixed = rounded.toFixed(decimals);
42
+ const [intPart, decPart] = fixed.split('.');
43
+ const decimalSep = decimalSeparatorEnabled ? decimalSeparator : '.';
44
+ const intFormatted = formatIntegerPart(intPart, thousandsSeparator, decimalSep);
45
+ const decSuffix = decimals > 0 ? decimalSep + (decPart !== null && decPart !== void 0 ? decPart : '') : '';
46
+ const numberStr = intFormatted + decSuffix;
47
+ const suffix = resultType === 'percent' ? '%' : '';
48
+ return `${prefixText}${numberStr}${suffix}${postfixText}`;
49
+ }
50
+ //# sourceMappingURL=format-calculated-result.utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format-calculated-result.utils.js","sourceRoot":"","sources":["../../../src/utils/formula/format-calculated-result.utils.ts"],"names":[],"mappings":"AAEA,SAAS,UAAU,CACf,KAAa,EACb,QAAgB,EAChB,IAA2C;IAE3C,MAAM,MAAM,GAAG,EAAE,IAAI,QAAQ,CAAC;IAC9B,MAAM,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;IAC9B,QAAQ,IAAI,EAAE,CAAC;QACX,KAAK,OAAO;YACR,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;QACvC,KAAK,MAAM;YACP,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;QACtC;YACI,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;IAC3C,CAAC;AACL,CAAC;AAED,SAAS,iBAAiB,CACtB,CAAS,EACT,kBAA2B,EAC3B,gBAA2B;IAE3B,IAAI,CAAC,kBAAkB,EAAE,CAAC;QACtB,OAAO,CAAC,CAAC;IACb,CAAC;IACD,MAAM,GAAG,GAAG,gBAAgB,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,yBAAyB;IAC3E,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC;IACjB,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACX,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;QACjC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;QACjC,CAAC,GAAG,KAAK,CAAC;IACd,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC3B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CAClC,KAAa,EACb,MAAyC;IAEzC,IAAI,MAAM,IAAI,IAAI,EAAE,CAAC;QACjB,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;IACzB,CAAC;IAED,MAAM,EACF,gBAAgB,EAChB,uBAAuB,EACvB,QAAQ,EACR,WAAW,EACX,UAAU,EACV,UAAU,EACV,YAAY,EACZ,kBAAkB,GACrB,GAAG,MAAM,CAAC;IAEX,IAAI,GAAG,GAAG,KAAK,CAAC;IAChB,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC3B,GAAG,GAAG,KAAK,GAAG,GAAG,CAAC;IACtB,CAAC;IAED,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAC;IACxD,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACxC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAE5C,MAAM,UAAU,GAAG,uBAAuB,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,GAAG,CAAC;IACpE,MAAM,YAAY,GAAG,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,EAAE,UAAU,CAAC,CAAC;IAChF,MAAM,SAAS,GAAG,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,OAAO,aAAP,OAAO,cAAP,OAAO,GAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAEnE,MAAM,SAAS,GAAG,YAAY,GAAG,SAAS,CAAC;IAC3C,MAAM,MAAM,GAAG,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAEnD,OAAO,GAAG,UAAU,GAAG,SAAS,GAAG,MAAM,GAAG,WAAW,EAAE,CAAC;AAC9D,CAAC"}
@@ -0,0 +1,3 @@
1
+ export type { FormulaOperator, FormulaToken, StructuredFormula } from '../../interface/types';
2
+ export { FORMULA_OPERATORS } from '../../interface/types';
3
+ //# sourceMappingURL=formula-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formula-types.d.ts","sourceRoot":"","sources":["../../../src/utils/formula/formula-types.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC9F,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { FORMULA_OPERATORS } from '../../interface/types';
2
+ //# sourceMappingURL=formula-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formula-types.js","sourceRoot":"","sources":["../../../src/utils/formula/formula-types.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC"}
@@ -0,0 +1,11 @@
1
+ export * from './caret.utils';
2
+ export * from './dom.utils';
3
+ export * from './evaluate-formula.utils';
4
+ export * from './expression.utils';
5
+ export * from './format-calculated-result.utils';
6
+ export * from './formula-types';
7
+ export * from './referenced-paths.utils';
8
+ export * from './render-formula.utils';
9
+ export * from './serialize-formula.utils';
10
+ export * from './validate-formula.utils';
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/formula/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,0BAA0B,CAAC;AACzC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kCAAkC,CAAC;AACjD,cAAc,iBAAiB,CAAC;AAChC,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,0BAA0B,CAAC"}
@@ -0,0 +1,11 @@
1
+ export * from './caret.utils';
2
+ export * from './dom.utils';
3
+ export * from './evaluate-formula.utils';
4
+ export * from './expression.utils';
5
+ export * from './format-calculated-result.utils';
6
+ export * from './formula-types';
7
+ export * from './referenced-paths.utils';
8
+ export * from './render-formula.utils';
9
+ export * from './serialize-formula.utils';
10
+ export * from './validate-formula.utils';
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/utils/formula/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,0BAA0B,CAAC;AACzC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kCAAkC,CAAC;AACjD,cAAc,iBAAiB,CAAC;AAChC,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,0BAA0B,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { StructuredFormula } from '../../interface/types';
2
+ /**
3
+ * Returns the set of data model field paths referenced in the formula.
4
+ * Used for recalculation scope: only these fields should trigger formula updates.
5
+ */
6
+ export declare function getReferencedPaths(formula: StructuredFormula | undefined | null): string[];
7
+ //# sourceMappingURL=referenced-paths.utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"referenced-paths.utils.d.ts","sourceRoot":"","sources":["../../../src/utils/formula/referenced-paths.utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAE1D;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,iBAAiB,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,EAAE,CAW1F"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Returns the set of data model field paths referenced in the formula.
3
+ * Used for recalculation scope: only these fields should trigger formula updates.
4
+ */
5
+ export function getReferencedPaths(formula) {
6
+ var _a;
7
+ if (!((_a = formula === null || formula === void 0 ? void 0 : formula.tokens) === null || _a === void 0 ? void 0 : _a.length)) {
8
+ return [];
9
+ }
10
+ const paths = [];
11
+ for (const token of formula.tokens) {
12
+ if (token.type === 'field') {
13
+ paths.push(token.path);
14
+ }
15
+ }
16
+ return paths;
17
+ }
18
+ //# sourceMappingURL=referenced-paths.utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"referenced-paths.utils.js","sourceRoot":"","sources":["../../../src/utils/formula/referenced-paths.utils.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAA6C;;IAC5E,IAAI,CAAC,CAAA,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,MAAM,0CAAE,MAAM,CAAA,EAAE,CAAC;QAC3B,OAAO,EAAE,CAAC;IACd,CAAC;IACD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACjC,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YACzB,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC3B,CAAC;IACL,CAAC;IACD,OAAO,KAAK,CAAC;AACjB,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { FormulaToken } from '../../interface/types';
2
+ /**
3
+ * Renders formula tokens to HTML for the read-only preview (e.g. formula box).
4
+ * Uses the same token classes as the modal (dte-formula-token, dte-formula-token-field, etc.).
5
+ */
6
+ export declare function renderFormulaPreviewHtml(tokens: FormulaToken[]): string;
7
+ export declare function renderFormulaHtml(expression: string, labelMap: Map<string, string>): string;
8
+ //# sourceMappingURL=render-formula.utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-formula.utils.d.ts","sourceRoot":"","sources":["../../../src/utils/formula/render-formula.utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAIrD;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,CAYvE;AAED,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAiB3F"}
@@ -0,0 +1,39 @@
1
+ import { escapeHtml } from './dom.utils';
2
+ import { tokenizeExpression } from './expression.utils';
3
+ /**
4
+ * Renders formula tokens to HTML for the read-only preview (e.g. formula box).
5
+ * Uses the same token classes as the modal (dte-formula-token, dte-formula-token-field, etc.).
6
+ */
7
+ export function renderFormulaPreviewHtml(tokens) {
8
+ if (!(tokens === null || tokens === void 0 ? void 0 : tokens.length)) {
9
+ return '';
10
+ }
11
+ return tokens
12
+ .map(token => {
13
+ const type = token.type;
14
+ const text = type === 'field' ? token.label : token.value;
15
+ const escaped = escapeHtml(text).replace(/ /g, '&nbsp;');
16
+ return `<span class="dte-formula-token dte-formula-token-${type}">${escaped}</span>`;
17
+ })
18
+ .join('');
19
+ }
20
+ export function renderFormulaHtml(expression, labelMap) {
21
+ if (!expression.trim()) {
22
+ return '';
23
+ }
24
+ const parts = tokenizeExpression(expression);
25
+ let fieldIndex = 0;
26
+ return parts
27
+ .map(part => {
28
+ var _a;
29
+ if (part.type === 'field') {
30
+ const displayValue = (_a = labelMap.get(part.value)) !== null && _a !== void 0 ? _a : part.value;
31
+ const currentIndex = fieldIndex;
32
+ fieldIndex += 1;
33
+ return `<span class="dte-formula-chip" contenteditable="false" data-field="${escapeHtml(part.value)}" data-field-index="${currentIndex}" draggable="true"><span class="dte-formula-chip-label">${escapeHtml(displayValue)}</span><span class="dte-formula-chip-remove" data-field-remove="true" role="button" aria-label="Remove field">&times;</span></span>`;
34
+ }
35
+ return `<span>${escapeHtml(part.value).replace(/ /g, '&nbsp;')}</span>`;
36
+ })
37
+ .join('');
38
+ }
39
+ //# sourceMappingURL=render-formula.utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-formula.utils.js","sourceRoot":"","sources":["../../../src/utils/formula/render-formula.utils.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAExD;;;GAGG;AACH,MAAM,UAAU,wBAAwB,CAAC,MAAsB;IAC3D,IAAI,CAAC,CAAA,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,MAAM,CAAA,EAAE,CAAC;QAClB,OAAO,EAAE,CAAC;IACd,CAAC;IACD,OAAO,MAAM;SACR,GAAG,CAAC,KAAK,CAAC,EAAE;QACT,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;QACxB,MAAM,IAAI,GAAG,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAE,KAA2B,CAAC,KAAK,CAAC;QACjF,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QACzD,OAAO,oDAAoD,IAAI,KAAK,OAAO,SAAS,CAAC;IACzF,CAAC,CAAC;SACD,IAAI,CAAC,EAAE,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,UAAkB,EAAE,QAA6B;IAC/E,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC;QACrB,OAAO,EAAE,CAAC;IACd,CAAC;IACD,MAAM,KAAK,GAAG,kBAAkB,CAAC,UAAU,CAAC,CAAC;IAC7C,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,OAAO,KAAK;SACP,GAAG,CAAC,IAAI,CAAC,EAAE;;QACR,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YACxB,MAAM,YAAY,GAAG,MAAA,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,mCAAI,IAAI,CAAC,KAAK,CAAC;YAC5D,MAAM,YAAY,GAAG,UAAU,CAAC;YAChC,UAAU,IAAI,CAAC,CAAC;YAChB,OAAO,sEAAsE,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,uBAAuB,YAAY,2DAA2D,UAAU,CAAC,YAAY,CAAC,qIAAqI,CAAC;QACnW,CAAC;QACD,OAAO,SAAS,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,SAAS,CAAC;IAC5E,CAAC,CAAC;SACD,IAAI,CAAC,EAAE,CAAC,CAAC;AAClB,CAAC"}
@@ -0,0 +1,14 @@
1
+ import { StructuredFormula } from '../../interface/types';
2
+ export interface SerializedFormula {
3
+ v: number;
4
+ tokens: StructuredFormula['tokens'];
5
+ }
6
+ /**
7
+ * Serialize structured formula for persistence (e.g. to JSON in store).
8
+ */
9
+ export declare function serializeFormula(formula: StructuredFormula): string;
10
+ /**
11
+ * Deserialize formula from persisted string. Returns null if invalid.
12
+ */
13
+ export declare function deserializeFormula(json: string): StructuredFormula | null;
14
+ //# sourceMappingURL=serialize-formula.utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serialize-formula.utils.d.ts","sourceRoot":"","sources":["../../../src/utils/formula/serialize-formula.utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAI1D,MAAM,WAAW,iBAAiB;IAC9B,CAAC,EAAE,MAAM,CAAC;IACV,MAAM,EAAE,iBAAiB,CAAC,QAAQ,CAAC,CAAC;CACvC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM,CAMnE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI,CAgBzE"}
@@ -0,0 +1,33 @@
1
+ const SERIALIZATION_VERSION = 1;
2
+ /**
3
+ * Serialize structured formula for persistence (e.g. to JSON in store).
4
+ */
5
+ export function serializeFormula(formula) {
6
+ const payload = {
7
+ v: SERIALIZATION_VERSION,
8
+ tokens: formula.tokens,
9
+ };
10
+ return JSON.stringify(payload);
11
+ }
12
+ /**
13
+ * Deserialize formula from persisted string. Returns null if invalid.
14
+ */
15
+ export function deserializeFormula(json) {
16
+ if (!json || typeof json !== 'string') {
17
+ return null;
18
+ }
19
+ try {
20
+ const payload = JSON.parse(json);
21
+ if ((payload === null || payload === void 0 ? void 0 : payload.v) !== SERIALIZATION_VERSION || !Array.isArray(payload.tokens)) {
22
+ return null;
23
+ }
24
+ if (payload.tokens.length === 0) {
25
+ return { tokens: [] };
26
+ }
27
+ return { tokens: payload.tokens };
28
+ }
29
+ catch (_a) {
30
+ return null;
31
+ }
32
+ }
33
+ //# sourceMappingURL=serialize-formula.utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serialize-formula.utils.js","sourceRoot":"","sources":["../../../src/utils/formula/serialize-formula.utils.ts"],"names":[],"mappings":"AAEA,MAAM,qBAAqB,GAAG,CAAC,CAAC;AAOhC;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAA0B;IACvD,MAAM,OAAO,GAAsB;QAC/B,CAAC,EAAE,qBAAqB;QACxB,MAAM,EAAE,OAAO,CAAC,MAAM;KACzB,CAAC;IACF,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;AACnC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC3C,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QACpC,OAAO,IAAI,CAAC;IAChB,CAAC;IACD,IAAI,CAAC;QACD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAsB,CAAC;QACtD,IAAI,CAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,CAAC,MAAK,qBAAqB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YACzE,OAAO,IAAI,CAAC;QAChB,CAAC;QACD,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC9B,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;QAC1B,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;IACtC,CAAC;IAAC,WAAM,CAAC;QACL,OAAO,IAAI,CAAC;IAChB,CAAC;AACL,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { StructuredFormula } from '../../interface/types';
2
+ export interface FormulaValidationResult {
3
+ valid: boolean;
4
+ errors: string[];
5
+ }
6
+ /**
7
+ * Validates a structured formula.
8
+ * Rules: not empty, valid operator placement, balanced parentheses, supported operators only, all field paths exist.
9
+ */
10
+ export declare function validateFormula(formula: StructuredFormula | undefined | null, validPaths: Set<string>): FormulaValidationResult;
11
+ //# sourceMappingURL=validate-formula.utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-formula.utils.d.ts","sourceRoot":"","sources":["../../../src/utils/formula/validate-formula.utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAGH,iBAAiB,EACpB,MAAM,uBAAuB,CAAC;AAE/B,MAAM,WAAW,uBAAuB;IACpC,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;CACpB;AAYD;;;GAGG;AACH,wBAAgB,eAAe,CAC3B,OAAO,EAAE,iBAAiB,GAAG,SAAS,GAAG,IAAI,EAC7C,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,GACxB,uBAAuB,CAiEzB"}
@@ -0,0 +1,79 @@
1
+ import { FORMULA_OPERATORS, } from '../../interface/types';
2
+ const OPERATOR_SET = new Set(FORMULA_OPERATORS);
3
+ function isOperatorToken(t) {
4
+ return t.type === 'operator';
5
+ }
6
+ function isOperandToken(t) {
7
+ return t.type === 'number' || t.type === 'field';
8
+ }
9
+ /**
10
+ * Validates a structured formula.
11
+ * Rules: not empty, valid operator placement, balanced parentheses, supported operators only, all field paths exist.
12
+ */
13
+ export function validateFormula(formula, validPaths) {
14
+ var _a;
15
+ const errors = [];
16
+ if (!((_a = formula === null || formula === void 0 ? void 0 : formula.tokens) === null || _a === void 0 ? void 0 : _a.length)) {
17
+ return { valid: false, errors: ['Formula cannot be empty'] };
18
+ }
19
+ const tokens = formula.tokens;
20
+ let parenBalance = 0;
21
+ for (let i = 0; i < tokens.length; i++) {
22
+ const t = tokens[i];
23
+ const prev = tokens[i - 1];
24
+ const next = tokens[i + 1];
25
+ if (t.type === 'lparen') {
26
+ parenBalance++;
27
+ if (prev != null && isOperandToken(prev)) {
28
+ errors.push('Operator required before opening parenthesis');
29
+ }
30
+ }
31
+ else if (t.type === 'rparen') {
32
+ parenBalance--;
33
+ if (parenBalance < 0) {
34
+ errors.push('Unbalanced parentheses');
35
+ }
36
+ if (prev != null && isOperatorToken(prev)) {
37
+ errors.push('Operand required before closing parenthesis');
38
+ }
39
+ }
40
+ else if (t.type === 'operator') {
41
+ if (prev == null || next == null) {
42
+ errors.push('Operator must be between two operands or expressions');
43
+ }
44
+ else if (prev.type === 'operator' || prev.type === 'lparen') {
45
+ errors.push('Operand required before operator');
46
+ }
47
+ else if (next.type === 'operator' || next.type === 'rparen') {
48
+ errors.push('Operand required after operator');
49
+ }
50
+ }
51
+ else if (t.type === 'field') {
52
+ if (!validPaths.has(t.path)) {
53
+ errors.push(`Unknown field: "${t.label}" (path: ${t.path})`);
54
+ }
55
+ }
56
+ else if (t.type === 'number') {
57
+ const num = parseFloat(t.value);
58
+ if (Number.isNaN(num)) {
59
+ errors.push(`Invalid number: "${t.value}"`);
60
+ }
61
+ }
62
+ }
63
+ if (parenBalance !== 0) {
64
+ errors.push('Unbalanced parentheses');
65
+ }
66
+ const first = tokens[0];
67
+ const last = tokens[tokens.length - 1];
68
+ if (first && (first.type === 'operator' || first.type === 'rparen')) {
69
+ errors.push('Formula must start with an operand or opening parenthesis');
70
+ }
71
+ if (last && (last.type === 'operator' || last.type === 'lparen')) {
72
+ errors.push('Formula must end with an operand or closing parenthesis');
73
+ }
74
+ return {
75
+ valid: errors.length === 0,
76
+ errors,
77
+ };
78
+ }
79
+ //# sourceMappingURL=validate-formula.utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-formula.utils.js","sourceRoot":"","sources":["../../../src/utils/formula/validate-formula.utils.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  export * from './field';
2
2
  export * from './pdf';
3
3
  export * from './data-model';
4
+ export * from './formula';
4
5
  export * from './path';
5
6
  export * from './recipients';
6
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,OAAO,CAAC;AACtB,cAAc,cAAc,CAAC;AAC7B,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,OAAO,CAAC;AACtB,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC"}
@@ -1,6 +1,7 @@
1
1
  export * from './field';
2
2
  export * from './pdf';
3
3
  export * from './data-model';
4
+ export * from './formula';
4
5
  export * from './path';
5
6
  export * from './recipients';
6
7
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,OAAO,CAAC;AACtB,cAAc,cAAc,CAAC;AAC7B,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,OAAO,CAAC;AACtB,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@servicetitan/dte-pdf-editor",
3
- "version": "1.16.0",
3
+ "version": "1.18.0",
4
4
  "description": "",
5
5
  "main": "./dist/index.js",
6
6
  "typings": "./dist/index.d.ts",
@@ -21,7 +21,7 @@
21
21
  "uuid": "^9.0.0"
22
22
  },
23
23
  "peerDependencies": {
24
- "@servicetitan/anvil2": "^1.46.9",
24
+ "@servicetitan/anvil2": "^2.0.2",
25
25
  "react": "~18.3.1",
26
26
  "react-dom": "~18.3.1"
27
27
  }
@@ -0,0 +1,113 @@
1
+ import { Button, Flex, Text } from '@servicetitan/anvil2';
2
+ import { Dispatch, FC, SetStateAction } from 'react';
3
+ import { CalculatedFieldFormat } from '../../interface/types';
4
+
5
+ interface AdvancedSettingsProps {
6
+ format: CalculatedFieldFormat;
7
+ setFormat: Dispatch<SetStateAction<CalculatedFieldFormat>>;
8
+ }
9
+
10
+ const ROUNDING_OPTIONS: { label: string; value: CalculatedFieldFormat['roundingMode'] }[] = [
11
+ { label: 'Round', value: 'round' },
12
+ { label: 'Floor', value: 'floor' },
13
+ { label: 'Ceil', value: 'ceil' },
14
+ ];
15
+
16
+ export const AdvancedSettings: FC<AdvancedSettingsProps> = ({ format, setFormat }) => (
17
+ <div className="dte-formula-advanced">
18
+ <Text variant="body" size="small">
19
+ Rounding mode
20
+ </Text>
21
+ <Flex gap={1}>
22
+ {ROUNDING_OPTIONS.map(({ label, value }) => (
23
+ <Button
24
+ key={value}
25
+ appearance={format.roundingMode === value ? 'primary' : 'secondary'}
26
+ size="small"
27
+ onClick={() => setFormat(prev => ({ ...prev, roundingMode: value }))}
28
+ >
29
+ {label}
30
+ </Button>
31
+ ))}
32
+ </Flex>
33
+ <Flex
34
+ alignItems="center"
35
+ justifyContent="space-between"
36
+ className="dte-formula-advanced-row"
37
+ >
38
+ <Text variant="body" size="small">
39
+ Decimal places
40
+ </Text>
41
+ <Text variant="body" size="small" className="dte-formula-advanced-value">
42
+ {format.decimals}
43
+ </Text>
44
+ </Flex>
45
+ <input
46
+ type="range"
47
+ className="dte-formula-advanced-range"
48
+ min={0}
49
+ max={10}
50
+ value={format.decimals}
51
+ onChange={e =>
52
+ setFormat(prev => ({
53
+ ...prev,
54
+ decimals: Number(e.target.value),
55
+ }))
56
+ }
57
+ />
58
+ <label className="dte-formula-advanced-checkbox">
59
+ <input
60
+ type="checkbox"
61
+ checked={format.thousandsSeparator}
62
+ onChange={e =>
63
+ setFormat(prev => ({
64
+ ...prev,
65
+ thousandsSeparator: e.target.checked,
66
+ decimalSeparatorEnabled: e.target.checked,
67
+ decimalSeparator: e.target.checked ? prev.decimalSeparator : '.',
68
+ }))
69
+ }
70
+ />
71
+ <Text variant="body" size="small">
72
+ Thousands separator
73
+ </Text>
74
+ </label>
75
+ {format.thousandsSeparator && (
76
+ <Flex gap={2}>
77
+ {(['.', ','] as const).map(sep => (
78
+ <Button
79
+ key={sep}
80
+ appearance={format.decimalSeparator === sep ? 'primary' : 'secondary'}
81
+ size="small"
82
+ onClick={() => setFormat(prev => ({ ...prev, decimalSeparator: sep }))}
83
+ >
84
+ {sep}
85
+ </Button>
86
+ ))}
87
+ </Flex>
88
+ )}
89
+ <Text variant="body" size="small">
90
+ Result affixes
91
+ </Text>
92
+ <Flex gap="2" alignItems="center">
93
+ <Text variant="body" size="small">
94
+ Prefix
95
+ </Text>
96
+ <input
97
+ type="text"
98
+ className="dte-formula-advanced-input"
99
+ value={format.prefixText}
100
+ onChange={e => setFormat(prev => ({ ...prev, prefixText: e.target.value }))}
101
+ />
102
+ <Text variant="body" size="small">
103
+ Postfix
104
+ </Text>
105
+ <input
106
+ type="text"
107
+ className="dte-formula-advanced-input"
108
+ value={format.postfixText}
109
+ onChange={e => setFormat(prev => ({ ...prev, postfixText: e.target.value }))}
110
+ />
111
+ </Flex>
112
+ </div>
113
+ );