@servicetitan/dte-pdf-editor 1.20.0 → 1.22.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 (121) hide show
  1. package/dist/components/display-conditions/condition-group.d.ts +11 -0
  2. package/dist/components/display-conditions/condition-group.d.ts.map +1 -0
  3. package/dist/components/display-conditions/condition-group.js +52 -0
  4. package/dist/components/display-conditions/condition-group.js.map +1 -0
  5. package/dist/components/display-conditions/condition-groups-section.d.ts +10 -0
  6. package/dist/components/display-conditions/condition-groups-section.d.ts.map +1 -0
  7. package/dist/components/display-conditions/condition-groups-section.js +14 -0
  8. package/dist/components/display-conditions/condition-groups-section.js.map +1 -0
  9. package/dist/components/display-conditions/condition-row.d.ts +10 -0
  10. package/dist/components/display-conditions/condition-row.d.ts.map +1 -0
  11. package/dist/components/display-conditions/condition-row.js +60 -0
  12. package/dist/components/display-conditions/condition-row.js.map +1 -0
  13. package/dist/components/display-conditions/display-condition-modal.d.ts +12 -0
  14. package/dist/components/display-conditions/display-condition-modal.d.ts.map +1 -0
  15. package/dist/components/display-conditions/display-condition-modal.js +89 -0
  16. package/dist/components/display-conditions/display-condition-modal.js.map +1 -0
  17. package/dist/components/display-conditions/display-condition.d.ts +9 -0
  18. package/dist/components/display-conditions/display-condition.d.ts.map +1 -0
  19. package/dist/components/display-conditions/display-condition.js +10 -0
  20. package/dist/components/display-conditions/display-condition.js.map +1 -0
  21. package/dist/components/field-config-panel/advanced-settings.d.ts.map +1 -1
  22. package/dist/components/field-config-panel/advanced-settings.js +10 -8
  23. package/dist/components/field-config-panel/advanced-settings.js.map +1 -1
  24. package/dist/components/field-config-panel/field-config-panel.d.ts +1 -1
  25. package/dist/components/field-config-panel/field-config-panel.d.ts.map +1 -1
  26. package/dist/components/field-config-panel/field-config-panel.js +4 -2
  27. package/dist/components/field-config-panel/field-config-panel.js.map +1 -1
  28. package/dist/components/field-config-panel/formula-generator.d.ts.map +1 -1
  29. package/dist/components/field-config-panel/formula-generator.js +3 -20
  30. package/dist/components/field-config-panel/formula-generator.js.map +1 -1
  31. package/dist/components/field-config-panel/formula-modal.d.ts.map +1 -1
  32. package/dist/components/field-config-panel/formula-modal.js +9 -3
  33. package/dist/components/field-config-panel/formula-modal.js.map +1 -1
  34. package/dist/components/field-config-panel/formula-workspace.d.ts.map +1 -1
  35. package/dist/components/field-config-panel/formula-workspace.js +4 -3
  36. package/dist/components/field-config-panel/formula-workspace.js.map +1 -1
  37. package/dist/components/field-config-panel/result-type-selector.d.ts.map +1 -1
  38. package/dist/components/field-config-panel/result-type-selector.js +2 -2
  39. package/dist/components/field-config-panel/result-type-selector.js.map +1 -1
  40. package/dist/components/pdf-view/pdf-view-field-container.d.ts +2 -1
  41. package/dist/components/pdf-view/pdf-view-field-container.d.ts.map +1 -1
  42. package/dist/components/pdf-view/pdf-view-field-container.js +6 -2
  43. package/dist/components/pdf-view/pdf-view-field-container.js.map +1 -1
  44. package/dist/components/pdf-view/pdf-view.d.ts.map +1 -1
  45. package/dist/components/pdf-view/pdf-view.js +1 -1
  46. package/dist/components/pdf-view/pdf-view.js.map +1 -1
  47. package/dist/constants/calculated.constants.d.ts +2 -0
  48. package/dist/constants/calculated.constants.d.ts.map +1 -0
  49. package/dist/constants/calculated.constants.js +2 -0
  50. package/dist/constants/calculated.constants.js.map +1 -0
  51. package/dist/constants/conditions.constants.d.ts +6 -0
  52. package/dist/constants/conditions.constants.d.ts.map +1 -0
  53. package/dist/constants/conditions.constants.js +17 -0
  54. package/dist/constants/conditions.constants.js.map +1 -0
  55. package/dist/constants/index.d.ts +3 -0
  56. package/dist/constants/index.d.ts.map +1 -1
  57. package/dist/constants/index.js +3 -0
  58. package/dist/constants/index.js.map +1 -1
  59. package/dist/interface/types.d.ts +78 -1
  60. package/dist/interface/types.d.ts.map +1 -1
  61. package/dist/interface/types.js +25 -0
  62. package/dist/interface/types.js.map +1 -1
  63. package/dist/utils/conditions/evaluate.utils.d.ts +6 -0
  64. package/dist/utils/conditions/evaluate.utils.d.ts.map +1 -0
  65. package/dist/utils/conditions/evaluate.utils.js +95 -0
  66. package/dist/utils/conditions/evaluate.utils.js.map +1 -0
  67. package/dist/utils/conditions/index.d.ts +3 -0
  68. package/dist/utils/conditions/index.d.ts.map +1 -0
  69. package/dist/utils/conditions/index.js +3 -0
  70. package/dist/utils/conditions/index.js.map +1 -0
  71. package/dist/utils/conditions/schema-data-points.utils.d.ts +12 -0
  72. package/dist/utils/conditions/schema-data-points.utils.d.ts.map +1 -0
  73. package/dist/utils/conditions/schema-data-points.utils.js +67 -0
  74. package/dist/utils/conditions/schema-data-points.utils.js.map +1 -0
  75. package/dist/utils/data-model/extract-fields.utils.d.ts +1 -0
  76. package/dist/utils/data-model/extract-fields.utils.d.ts.map +1 -1
  77. package/dist/utils/data-model/extract-fields.utils.js +6 -3
  78. package/dist/utils/data-model/extract-fields.utils.js.map +1 -1
  79. package/dist/utils/formula/render-formula.utils.d.ts +0 -6
  80. package/dist/utils/formula/render-formula.utils.d.ts.map +1 -1
  81. package/dist/utils/formula/render-formula.utils.js +0 -17
  82. package/dist/utils/formula/render-formula.utils.js.map +1 -1
  83. package/dist/utils/index.d.ts +2 -0
  84. package/dist/utils/index.d.ts.map +1 -1
  85. package/dist/utils/index.js +2 -0
  86. package/dist/utils/index.js.map +1 -1
  87. package/dist/utils/shared/index.d.ts +2 -0
  88. package/dist/utils/shared/index.d.ts.map +1 -0
  89. package/dist/utils/shared/index.js +2 -0
  90. package/dist/utils/shared/index.js.map +1 -0
  91. package/dist/utils/shared/number.utils.d.ts +2 -0
  92. package/dist/utils/shared/number.utils.d.ts.map +1 -0
  93. package/dist/utils/shared/number.utils.js +12 -0
  94. package/dist/utils/shared/number.utils.js.map +1 -0
  95. package/package.json +1 -1
  96. package/src/components/display-conditions/condition-group.tsx +141 -0
  97. package/src/components/display-conditions/condition-groups-section.tsx +63 -0
  98. package/src/components/display-conditions/condition-row.tsx +182 -0
  99. package/src/components/display-conditions/display-condition-modal.tsx +180 -0
  100. package/src/components/display-conditions/display-condition.tsx +41 -0
  101. package/src/components/field-config-panel/advanced-settings.tsx +42 -46
  102. package/src/components/field-config-panel/field-config-panel.tsx +12 -2
  103. package/src/components/field-config-panel/formula-generator.tsx +9 -44
  104. package/src/components/field-config-panel/formula-modal.tsx +72 -82
  105. package/src/components/field-config-panel/formula-workspace.tsx +6 -5
  106. package/src/components/field-config-panel/result-type-selector.tsx +8 -11
  107. package/src/components/pdf-view/pdf-view-field-container.tsx +11 -2
  108. package/src/components/pdf-view/pdf-view.tsx +1 -0
  109. package/src/constants/calculated.constants.ts +1 -0
  110. package/src/constants/conditions.constants.ts +26 -0
  111. package/src/constants/index.ts +3 -0
  112. package/src/interface/types.ts +64 -1
  113. package/src/styles/formula-modal.css +1 -155
  114. package/src/utils/conditions/evaluate.utils.ts +134 -0
  115. package/src/utils/conditions/index.ts +2 -0
  116. package/src/utils/conditions/schema-data-points.utils.ts +93 -0
  117. package/src/utils/data-model/extract-fields.utils.ts +12 -7
  118. package/src/utils/formula/render-formula.utils.ts +0 -19
  119. package/src/utils/index.ts +2 -0
  120. package/src/utils/shared/index.ts +1 -0
  121. package/src/utils/shared/number.utils.ts +13 -0
@@ -1,8 +1,8 @@
1
- import { Button, Flex, Text } from '@servicetitan/anvil2';
2
- import IconClose from '@servicetitan/anvil2/assets/icons/material/round/close.svg';
1
+ import { Button, Dialog, Divider, Flex } from '@servicetitan/anvil2';
3
2
  import IconRedo from '@servicetitan/anvil2/assets/icons/material/round/redo.svg';
4
3
  import IconUndo from '@servicetitan/anvil2/assets/icons/material/round/undo.svg';
5
4
  import { FC, useCallback, useMemo, useState } from 'react';
5
+ import { MODAL_CONTENT_MAX_HEIGHT } from '../../constants';
6
6
  import { useFormulaEditor } from '../../hooks';
7
7
  import {
8
8
  CalculatedFieldFormat,
@@ -132,6 +132,10 @@ export const FormulaModal: FC<FormulaModalProps> = ({
132
132
  const canSave =
133
133
  formulaValidation.valid && unknownFieldErrors.length === 0 && parsedTokens.length > 0;
134
134
 
135
+ const handleClose = useCallback(() => {
136
+ onClose();
137
+ }, [onClose]);
138
+
135
139
  const handleSave = useCallback(() => {
136
140
  if (!canSave) {
137
141
  return;
@@ -141,89 +145,75 @@ export const FormulaModal: FC<FormulaModalProps> = ({
141
145
  }, [canSave, format, onClose, onSave, parsedTokens]);
142
146
 
143
147
  return (
144
- <Flex
145
- alignItems="center"
146
- justifyContent="center"
147
- className="dte-formula-modal-overlay"
148
- role="dialog"
149
- aria-modal="true"
150
- aria-label="Formula"
151
- >
152
- <Flex direction="column" className="dte-formula-modal ">
148
+ <Dialog open onClose={handleClose} size="xlarge">
149
+ <Dialog.Header>Formula Builder</Dialog.Header>
150
+ <Divider />
151
+ <Dialog.Content>
153
152
  <Flex
154
- className="dte-formula-modal-header"
155
- alignItems="center"
156
- justifyContent="space-between"
153
+ flex={1}
154
+ alignItems="stretch"
155
+ gap={2}
156
+ style={{
157
+ height: MODAL_CONTENT_MAX_HEIGHT,
158
+ overflowY: 'auto',
159
+ }}
157
160
  >
158
- <Text variant="headline" el="h6" size="small">
159
- Formula Builder
160
- </Text>
161
- <Button
162
- appearance="ghost"
163
- size="small"
164
- onClick={onClose}
165
- aria-label="Close"
166
- icon={IconClose}
161
+ <FieldSidebar
162
+ fillableOptions={fillableFieldsFromDocument}
163
+ mergeTagOptions={allFields}
164
+ highlightElementPath={highlightElementPath}
165
+ onHover={setHighlightElementPath}
166
+ onSelect={formulaEditor.insertField}
167
+ selectedPaths={formulaEditor.selectedFieldPaths}
168
+ />
169
+ <FormulaWorkspace
170
+ editorRef={formulaEditor.editorRef}
171
+ isInvalid={isInvalid && formulaEditor.isDirty}
172
+ validationError={validationError}
173
+ format={format}
174
+ onResultTypeChange={nextType =>
175
+ setFormat(prev => ({ ...prev, resultType: nextType }))
176
+ }
177
+ advancedOpen={advancedOpen}
178
+ onToggleAdvanced={() => setAdvancedOpen(prev => !prev)}
179
+ setFormat={setFormat}
180
+ onInput={formulaEditor.handleEditorInput}
181
+ onKeyDown={formulaEditor.handleKeyDown}
182
+ onClick={() => {}}
183
+ onMouseUp={formulaEditor.handleEditorInput}
184
+ onPaste={formulaEditor.handlePaste}
185
+ onOperatorSelect={formulaEditor.insertOperator}
186
+ onRemoveField={formulaEditor.removeFieldAtIndex}
187
+ actions={
188
+ <Flex gap="1" alignItems="center">
189
+ <Button
190
+ appearance="ghost"
191
+ size="small"
192
+ onClick={formulaEditor.undo}
193
+ disabled={!formulaEditor.canUndo}
194
+ aria-label="Undo"
195
+ icon={IconUndo}
196
+ />
197
+ <Button
198
+ appearance="ghost"
199
+ size="small"
200
+ onClick={formulaEditor.redo}
201
+ disabled={!formulaEditor.canRedo}
202
+ aria-label="Redo"
203
+ icon={IconRedo}
204
+ />
205
+ </Flex>
206
+ }
167
207
  />
168
208
  </Flex>
169
- <div className="dte-formula-modal-body">
170
- <Flex alignItems="stretch" gap={2} className="dte-formula-modal-columns">
171
- <FieldSidebar
172
- fillableOptions={fillableFieldsFromDocument}
173
- mergeTagOptions={allFields}
174
- highlightElementPath={highlightElementPath}
175
- onHover={setHighlightElementPath}
176
- onSelect={formulaEditor.insertField}
177
- selectedPaths={formulaEditor.selectedFieldPaths}
178
- />
179
- <FormulaWorkspace
180
- editorRef={formulaEditor.editorRef}
181
- isInvalid={isInvalid && formulaEditor.isDirty}
182
- validationError={validationError}
183
- format={format}
184
- onResultTypeChange={nextType =>
185
- setFormat(prev => ({ ...prev, resultType: nextType }))
186
- }
187
- advancedOpen={advancedOpen}
188
- onToggleAdvanced={() => setAdvancedOpen(prev => !prev)}
189
- setFormat={setFormat}
190
- onInput={formulaEditor.handleEditorInput}
191
- onKeyDown={formulaEditor.handleKeyDown}
192
- onClick={() => {}}
193
- onMouseUp={formulaEditor.handleEditorInput}
194
- onPaste={formulaEditor.handlePaste}
195
- onOperatorSelect={formulaEditor.insertOperator}
196
- onRemoveField={formulaEditor.removeFieldAtIndex}
197
- actions={
198
- <Flex gap="1" alignItems="center">
199
- <Button
200
- appearance="ghost"
201
- size="small"
202
- onClick={formulaEditor.undo}
203
- disabled={!formulaEditor.canUndo}
204
- aria-label="Undo"
205
- icon={IconUndo}
206
- />
207
- <Button
208
- appearance="ghost"
209
- size="small"
210
- onClick={formulaEditor.redo}
211
- disabled={!formulaEditor.canRedo}
212
- aria-label="Redo"
213
- icon={IconRedo}
214
- />
215
- </Flex>
216
- }
217
- />
218
- </Flex>
219
- </div>
220
- <Flex className="dte-formula-modal-footer" gap="2" justifyContent="flex-end">
221
- <Button onClick={onClose}>Cancel</Button>
222
- <Button onClick={handleSave} appearance="primary" disabled={!canSave}>
223
- Save
224
- </Button>
225
- </Flex>
226
- </Flex>
227
- </Flex>
209
+ </Dialog.Content>
210
+ <Divider />
211
+ <Dialog.Footer sticky>
212
+ <Dialog.CancelButton onClick={handleClose}>Cancel</Dialog.CancelButton>
213
+ <Button onClick={handleSave} appearance="primary" disabled={!canSave}>
214
+ Save
215
+ </Button>
216
+ </Dialog.Footer>
217
+ </Dialog>
228
218
  );
229
219
  };
@@ -1,4 +1,4 @@
1
- import { Alert, Button, Flex, Text } from '@servicetitan/anvil2';
1
+ import { Alert, Button, Flex, LinkButton, Text } from '@servicetitan/anvil2';
2
2
  import {
3
3
  Dispatch,
4
4
  FC,
@@ -9,6 +9,7 @@ import {
9
9
  SetStateAction,
10
10
  useCallback,
11
11
  } from 'react';
12
+ import { CALCULATED_OPERATIONS } from '../../constants';
12
13
  import { CalculatedFieldFormat } from '../../interface/types';
13
14
  import { AdvancedSettings } from './advanced-settings';
14
15
  import { ResultTypeSelector } from './result-type-selector';
@@ -70,7 +71,7 @@ export const FormulaWorkspace: FC<FormulaWorkspaceProps> = ({
70
71
  );
71
72
 
72
73
  return (
73
- <Flex direction="column" flex={1} gap={2}>
74
+ <Flex direction="column" flex={1} gap={2} style={{ padding: '12px' }}>
74
75
  <Text variant="body" size="small">
75
76
  Click a field on the left to add it. Use +, -, *, /, and parentheses to build
76
77
  formulas.
@@ -100,16 +101,16 @@ export const FormulaWorkspace: FC<FormulaWorkspaceProps> = ({
100
101
  />
101
102
  {isInvalid && validationError && <Alert status="danger" title={validationError} />}
102
103
  <Flex gap={1}>
103
- {['+', '-', '*', '/', '(', ')'].map(op => (
104
+ {CALCULATED_OPERATIONS.map(op => (
104
105
  <Button key={op} onClick={() => onOperatorSelect(op)} size="small">
105
106
  {op}
106
107
  </Button>
107
108
  ))}
108
109
  </Flex>
109
110
  <ResultTypeSelector format={format} onChange={onResultTypeChange} />
110
- <Button appearance="ghost" onClick={onToggleAdvanced} size="small">
111
+ <LinkButton appearance="primary" onClick={onToggleAdvanced}>
111
112
  {advancedOpen ? 'Hide advanced settings' : 'Show advanced settings'}
112
- </Button>
113
+ </LinkButton>
113
114
  {advancedOpen && <AdvancedSettings format={format} setFormat={setFormat} />}
114
115
  </Flex>
115
116
  );
@@ -1,4 +1,4 @@
1
- import { Button, Flex, Text } from '@servicetitan/anvil2';
1
+ import { Flex, SegmentedControl, Text } from '@servicetitan/anvil2';
2
2
  import { FC, Fragment } from 'react';
3
3
  import { CalculatedFieldFormat } from '../../interface/types';
4
4
 
@@ -19,16 +19,13 @@ export const ResultTypeSelector: FC<ResultTypeSelectorProps> = ({ format, onChan
19
19
  Result type
20
20
  </Text>
21
21
  <Flex gap={2}>
22
- {RESULT_TYPE_OPTIONS.map(({ label, value }) => (
23
- <Button
24
- key={value}
25
- appearance={format.resultType === value ? 'primary' : 'secondary'}
26
- size="small"
27
- onClick={() => onChange(value)}
28
- >
29
- {label}
30
- </Button>
31
- ))}
22
+ <SegmentedControl size="medium" selected={format.resultType} onChange={onChange} fill>
23
+ {RESULT_TYPE_OPTIONS.map(opt => (
24
+ <SegmentedControl.Segment key={opt.value} value={opt.value}>
25
+ {opt.label}
26
+ </SegmentedControl.Segment>
27
+ ))}
28
+ </SegmentedControl>
32
29
  </Flex>
33
30
  </Fragment>
34
31
  );
@@ -1,9 +1,10 @@
1
1
  import { FC, PropsWithChildren, RefObject } from 'react';
2
- import { PdfField } from '../../interface/types';
3
- import { getFieldBackgroundColor, getPagePosition } from '../../utils';
2
+ import { DataModelValues, PdfField } from '../../interface/types';
3
+ import { evaluateDisplayCondition, getFieldBackgroundColor, getPagePosition } from '../../utils';
4
4
 
5
5
  interface PdfViewFieldContainer {
6
6
  pdfWrapperRef: RefObject<HTMLDivElement>;
7
+ data?: DataModelValues;
7
8
  error?: string;
8
9
  field: PdfField;
9
10
  recipientsColors: Record<string, string>;
@@ -11,11 +12,19 @@ interface PdfViewFieldContainer {
11
12
 
12
13
  export const PdfViewFieldContainer: FC<PropsWithChildren<PdfViewFieldContainer>> = ({
13
14
  children,
15
+ data,
14
16
  error,
15
17
  field,
16
18
  pdfWrapperRef,
17
19
  recipientsColors,
18
20
  }) => {
21
+ const visible =
22
+ !field.displayCondition || evaluateDisplayCondition(field.displayCondition, data);
23
+
24
+ if (!visible) {
25
+ return null;
26
+ }
27
+
19
28
  const pagePos = getPagePosition(field.page, pdfWrapperRef);
20
29
  const bgColor = getFieldBackgroundColor(field.recipient, recipientsColors);
21
30
  return (
@@ -90,6 +90,7 @@ export const PdfView: FC<PdfViewProps> = ({
90
90
  <PdfViewFieldContainer
91
91
  key={field.id}
92
92
  field={field}
93
+ data={previewMode === 'fillable' ? previewData : data}
93
94
  error={errors[field.path!] || ''}
94
95
  recipientsColors={recipientsColors}
95
96
  pdfWrapperRef={pdfWrapperRef}
@@ -0,0 +1 @@
1
+ export const CALCULATED_OPERATIONS = ['+', '-', '*', '/', '(', ')'];
@@ -0,0 +1,26 @@
1
+ import { v4 as uuidv4 } from 'uuid';
2
+
3
+ import type {
4
+ DisplayConditionGroup,
5
+ DisplayConditionSingle,
6
+ DisplayConditionState,
7
+ } from '../interface/types';
8
+
9
+ export const defaultCondition = (): DisplayConditionSingle => ({
10
+ dataPointKey: '',
11
+ id: uuidv4(),
12
+ operator: 'is_equal_to',
13
+ value: '',
14
+ });
15
+
16
+ export const defaultGroup = (): DisplayConditionGroup => ({
17
+ conditions: [defaultCondition()],
18
+ id: uuidv4(),
19
+ });
20
+
21
+ export const defaultState = (): DisplayConditionState => ({
22
+ behavior: 'show',
23
+ groups: [defaultGroup()],
24
+ });
25
+
26
+ export const MODAL_CONTENT_MAX_HEIGHT = '70vh';
@@ -1,2 +1,5 @@
1
+ export * from './calculated.constants';
2
+ export * from './conditions.constants';
1
3
  export * from './field.constants';
4
+ export * from './menu-group';
2
5
  export * from './pdf-editor.constants';
@@ -16,6 +16,25 @@ export type FillableFieldType = 'text' | 'date' | 'checkbox' | 'radio' | 'number
16
16
 
17
17
  export type PdfFieldSubType = FillableFieldType | ESignFieldType;
18
18
 
19
+ export interface DisplayConditionState {
20
+ behavior: 'show' | 'hide';
21
+ groups: DisplayConditionGroup[];
22
+ }
23
+
24
+ export interface DisplayConditionGroup {
25
+ id: string;
26
+ logicalOperator?: 'and' | 'or';
27
+ conditions: DisplayConditionSingle[];
28
+ }
29
+
30
+ export interface DisplayConditionSingle {
31
+ id: string;
32
+ dataPointKey: string;
33
+ logicalOperator?: 'and' | 'or';
34
+ operator: string;
35
+ value: string;
36
+ }
37
+
19
38
  export interface PdfField {
20
39
  id: string;
21
40
  type: FieldTypeEnum;
@@ -32,6 +51,7 @@ export interface PdfField {
32
51
  description?: string;
33
52
  formula?: StructuredFormula;
34
53
  formulaFormat?: CalculatedFieldFormat;
54
+ displayCondition?: DisplayConditionState | null;
35
55
  }
36
56
 
37
57
  export interface FieldTypeOption {
@@ -112,7 +132,11 @@ export interface SchemaSimpleNumber extends SchemaNodeProps {
112
132
  type: 'number';
113
133
  }
114
134
 
115
- export type SchemaSimple = SchemaSimpleString | SchemaSimpleNumber;
135
+ export interface SchemaSimpleDate extends SchemaNodeProps {
136
+ type: 'date';
137
+ }
138
+
139
+ export type SchemaSimple = SchemaSimpleString | SchemaSimpleNumber | SchemaSimpleDate;
116
140
 
117
141
  export type DataModelValues = Record<string, any>;
118
142
 
@@ -140,3 +164,42 @@ export interface DataChangePayload {
140
164
  * it is displayed.
141
165
  */
142
166
  export type PreviewMode = 'fillable' | 'view';
167
+
168
+ /**
169
+ * Display condition types for the Rules and Conditions modal and evaluation.
170
+ */
171
+
172
+ /** Operators for string-type fields. */
173
+ export const STRING_OPERATORS = [
174
+ { label: 'Contains', value: 'contains' },
175
+ { label: 'Does not contain', value: 'does_not_contain' },
176
+ { label: 'Is equal to', value: 'is_equal_to' },
177
+ { label: 'Is not equal to', value: 'is_not_equal_to' },
178
+ { label: 'Starts with', value: 'starts_with' },
179
+ { label: 'Ends with', value: 'ends_with' },
180
+ { label: 'Is empty', value: 'is_empty' },
181
+ { label: 'Is not empty', value: 'is_not_empty' },
182
+ ] as const;
183
+
184
+ /** Operators for number-type fields. */
185
+ export const NUMBER_OPERATORS = [
186
+ { label: '== (equal to)', value: 'num_eq' },
187
+ { label: '!= (not equal to)', value: 'num_neq' },
188
+ { label: '> (greater than)', value: 'num_gt' },
189
+ { label: '< (less than)', value: 'num_lt' },
190
+ { label: '>= (greater than or equal to)', value: 'num_gte' },
191
+ { label: '<= (less than or equal to)', value: 'num_lte' },
192
+ ] as const;
193
+
194
+ export type ConditionOperator =
195
+ | (typeof STRING_OPERATORS)[number]['value']
196
+ | (typeof NUMBER_OPERATORS)[number]['value'];
197
+
198
+ /** Operators that do not require a value (empty/is not empty) */
199
+ export const VALUE_LESS_OPERATORS: ConditionOperator[] = ['is_empty', 'is_not_empty'];
200
+
201
+ export interface DataPointOption {
202
+ fieldType: 'number' | 'string';
203
+ fullKey: string;
204
+ title: string;
205
+ }
@@ -15,19 +15,9 @@
15
15
  max-height: 85vh;
16
16
  }
17
17
 
18
- .dte-formula-modal-body {
19
- padding: var(--spacing-2);
20
- overflow: auto;
21
- }
22
-
23
- .dte-formula-modal-columns {
24
- min-height: 280px;
25
- max-height: 60vh;
26
- }
27
-
28
18
  /* Field sidebar (left) */
29
19
  .dte-formula-sidebar {
30
- flex: 0 0 220px;
20
+ flex: 0 0 250px;
31
21
  border-right: 1px solid var(--border-color);
32
22
  padding-right: var(--spacing-2);
33
23
  gap: var(--spacing-2);
@@ -115,20 +105,6 @@
115
105
  opacity: 1;
116
106
  }
117
107
 
118
- .dte-formula-advanced-toggle {
119
- background: none;
120
- border: none;
121
- padding: var(--spacing-0);
122
- font-size: var(--typescale-2);
123
- color: var(--menu-active-color, #0265dc);
124
- cursor: pointer;
125
- text-decoration: none;
126
- }
127
-
128
- .dte-formula-advanced-toggle:hover {
129
- text-decoration: underline;
130
- }
131
-
132
108
  .dte-formula-advanced {
133
109
  margin-top: var(--spacing-2);
134
110
  padding-top: var(--spacing-2);
@@ -138,30 +114,10 @@
138
114
  gap: var(--spacing-2);
139
115
  }
140
116
 
141
- .dte-formula-advanced-row {
142
- margin-bottom: var(--spacing-0);
143
- }
144
-
145
117
  .dte-formula-advanced-value {
146
118
  color: var(--color-neutral-300);
147
119
  }
148
120
 
149
- .dte-formula-advanced-range {
150
- width: 100%;
151
- margin: var(--spacing-0);
152
- }
153
-
154
- .dte-formula-advanced-checkbox {
155
- display: flex;
156
- align-items: center;
157
- gap: var(--spacing-1);
158
- cursor: pointer;
159
- }
160
-
161
- .dte-formula-advanced-checkbox input {
162
- margin: var(--spacing-0);
163
- }
164
-
165
121
  .dte-formula-advanced-input {
166
122
  flex: 1;
167
123
  min-width: 0;
@@ -182,70 +138,6 @@
182
138
  border-top: 1px solid var(--border-color);
183
139
  }
184
140
 
185
- /* Token input */
186
- .dte-formula-token-input-wrapper {
187
- display: flex;
188
- flex-direction: row;
189
- align-items: stretch;
190
- gap: var(--spacing-2);
191
- }
192
-
193
- .dte-formula-token-input {
194
- flex: 1;
195
- padding: var(--spacing-1) var(--spacing-2);
196
- border: 1px solid var(--border-color);
197
- border-radius: 4px;
198
- outline: none;
199
- flex-wrap: wrap;
200
- gap: 2px;
201
- cursor: text;
202
- }
203
-
204
- .dte-formula-token-input:focus {
205
- border-color: var(--border-color-active);
206
- box-shadow: 0 0 0 1px var(--border-color-active);
207
- }
208
-
209
- .dte-formula-placeholder {
210
- color: var(--color-neutral-200);
211
- }
212
-
213
- .dte-formula-token {
214
- margin: var(--spacing-half);
215
- user-select: none;
216
- font-size: var(--typescale-2);
217
- }
218
-
219
- .dte-formula-token-field {
220
- padding: 2px var(--spacing-half);
221
- background: var(--menu-active-color, #0265dc);
222
- border-radius: var(--spacing-half, 4px);
223
- color: var(--white);
224
- flex-shrink: 0;
225
- }
226
-
227
- .dte-formula-token-delete {
228
- display: inline-flex;
229
- align-items: center;
230
- justify-content: center;
231
- padding: var(--spacing-0);
232
- margin: var(--spacing-0);
233
- margin-left: 2px;
234
- border: none;
235
- background: transparent;
236
- color: var(--color-neutral-200);
237
- cursor: pointer;
238
- border-radius: 2px;
239
- }
240
-
241
- .dte-formula-token-delete:hover {
242
- color: var(--danger, #dc3545);
243
- }
244
-
245
- .dte-formula-token-number {
246
- font-variant-numeric: tabular-nums;
247
- }
248
-
249
141
  .dte-formula-caret {
250
142
  width: 0;
251
143
  height: 1em;
@@ -259,49 +151,3 @@
259
151
  opacity: 0;
260
152
  }
261
153
  }
262
-
263
- /* Autosuggest - left side, always visible */
264
- .dte-formula-autosuggest {
265
- margin: var(--spacing-0);
266
- padding: var(--spacing-0);
267
- list-style: none;
268
- width: 200px;
269
- min-width: 200px;
270
- max-height: 200px;
271
- overflow-y: auto;
272
- background: var(--white);
273
- border: 1px solid var(--border-color);
274
- border-radius: 4px;
275
- flex-shrink: 0;
276
- }
277
-
278
- .dte-formula-autosuggest-item {
279
- padding: var(--spacing-1) var(--spacing-2);
280
- cursor: pointer;
281
- }
282
-
283
- .dte-formula-autosuggest-item:hover,
284
- .dte-formula-autosuggest-item.--highlight {
285
- color: var(--menu-active-color);
286
- }
287
-
288
- .dte-formula-autosuggest-item.empty {
289
- cursor: default;
290
- color: var(--color-neutral-200);
291
- }
292
-
293
- .dte-formula-box {
294
- padding: var(--spacing-1) var(--spacing-2);
295
- border: 1px solid var(--border-color);
296
- border-radius: 4px;
297
- background: var(--color-neutral-0);
298
- cursor: pointer;
299
- display: flex;
300
- overflow-x: auto;
301
- align-items: center;
302
- gap: 2px;
303
- }
304
-
305
- .dte-formula-box:hover {
306
- border-color: var(--border-color-hover);
307
- }