@servicetitan/dte-unlayer 0.140.0 → 0.142.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.
- package/dist/display-conditions/ConditionRow.d.ts.map +1 -1
- package/dist/display-conditions/ConditionRow.js +11 -101
- package/dist/display-conditions/ConditionRow.js.map +1 -1
- package/dist/display-conditions/LookupValueControl.d.ts +19 -0
- package/dist/display-conditions/LookupValueControl.d.ts.map +1 -0
- package/dist/display-conditions/LookupValueControl.js +154 -0
- package/dist/display-conditions/LookupValueControl.js.map +1 -0
- package/dist/display-conditions/lookupOptionsController.d.ts +29 -7
- package/dist/display-conditions/lookupOptionsController.d.ts.map +1 -1
- package/dist/display-conditions/lookupOptionsController.js +10 -7
- package/dist/display-conditions/lookupOptionsController.js.map +1 -1
- package/dist/display-conditions/useLookupOptions.d.ts +24 -8
- package/dist/display-conditions/useLookupOptions.d.ts.map +1 -1
- package/dist/display-conditions/useLookupOptions.js +63 -10
- package/dist/display-conditions/useLookupOptions.js.map +1 -1
- package/package.json +1 -1
- package/src/display-conditions/ConditionRow.tsx +10 -95
- package/src/display-conditions/LookupValueControl.tsx +164 -0
- package/src/display-conditions/lookupOptionsController.ts +29 -10
- package/src/display-conditions/useLookupOptions.ts +78 -15
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConditionRow.d.ts","sourceRoot":"","sources":["../../src/display-conditions/ConditionRow.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,QAAQ,EAAqB,MAAM,iBAAiB,CAAC;AAE9D,OAAO,EAAoB,eAAe,EAA0C,MAAM,SAAS,CAAC;AACpG,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"ConditionRow.d.ts","sourceRoot":"","sources":["../../src/display-conditions/ConditionRow.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,QAAQ,EAAqB,MAAM,iBAAiB,CAAC;AAE9D,OAAO,EAAoB,eAAe,EAA0C,MAAM,SAAS,CAAC;AACpG,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAEhE,MAAM,WAAW,iBAAiB;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,eAAe,CAAC;IAC3B,gBAAgB,EAAE,eAAe,EAAE,CAAC;IACpC,gBAAgB,EAAE,eAAe,EAAE,CAAC;IACpC,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,QAAQ,EAAE,CAAC,CAAC,EAAE,eAAe,KAAK,IAAI,CAAC;IACvC,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,QAAQ,EAAE,MAAM,IAAI,CAAC;CACxB;AAaD,wBAAgB,YAAY,CAAC,EACzB,SAAS,EACT,SAAS,EACT,gBAAgB,EAChB,gBAAgB,EAChB,KAAK,EACL,QAAQ,EACR,YAAY,EACZ,QAAQ,GACX,EAAE,QAAQ,CAAC,iBAAiB,CAAC,2CAmR7B"}
|
|
@@ -1,24 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Button, Chip, Combobox, Flex
|
|
2
|
+
import { Button, Chip, Combobox, Flex } from '@servicetitan/anvil2';
|
|
3
3
|
import TrashIcon from '@servicetitan/anvil2/assets/icons/material/round/delete.svg';
|
|
4
4
|
import { useEffect, useMemo, useState } from 'react';
|
|
5
5
|
import { parseFormFieldKey } from '../shared/forms';
|
|
6
|
+
import { LookupValueControl } from './LookupValueControl';
|
|
6
7
|
import { NUMBER_OPERATORS, STRING_OPERATORS, VALUE_LESS_OPERATORS } from './types';
|
|
7
|
-
import { useLookupOptions } from './useLookupOptions';
|
|
8
|
-
function sanitizeNumericInput(raw) {
|
|
9
|
-
// Allow only a single leading minus and one decimal separator.
|
|
10
|
-
let value = raw.replaceAll(/[^0-9.-]/g, '');
|
|
11
|
-
const isNegative = value.startsWith('-');
|
|
12
|
-
value = value.replaceAll('-', '');
|
|
13
|
-
if (isNegative) {
|
|
14
|
-
value = `-${value}`;
|
|
15
|
-
}
|
|
16
|
-
const firstDot = value.indexOf('.');
|
|
17
|
-
if (firstDot >= 0) {
|
|
18
|
-
value = `${value.slice(0, firstDot + 1)}${value.slice(firstDot + 1).replaceAll('.', '')}`;
|
|
19
|
-
}
|
|
20
|
-
return value;
|
|
21
|
-
}
|
|
22
8
|
export function ConditionRow({ canRemove, condition, dataPointOptions, formFieldOptions, forms, onChange, onFormSelect, onRemove }) {
|
|
23
9
|
const selectedFormFieldMeta = useMemo(()=>parseFormFieldKey(condition.dataPointKey), [
|
|
24
10
|
condition.dataPointKey
|
|
@@ -77,15 +63,6 @@ export function ConditionRow({ canRemove, condition, dataPointOptions, formField
|
|
|
77
63
|
var _selectedDataPoint_fieldType;
|
|
78
64
|
const fieldType = (_selectedDataPoint_fieldType = selectedDataPoint === null || selectedDataPoint === void 0 ? void 0 : selectedDataPoint.fieldType) !== null && _selectedDataPoint_fieldType !== void 0 ? _selectedDataPoint_fieldType : 'string';
|
|
79
65
|
const isValueLess = VALUE_LESS_OPERATORS.includes(condition.operator);
|
|
80
|
-
// Value-less operators (is empty / is not empty) need no value, so skip the host lookup.
|
|
81
|
-
const { options: lookupOptions, status: lookupStatus } = useLookupOptions(isValueLess ? undefined : selectedDataPoint === null || selectedDataPoint === void 0 ? void 0 : selectedDataPoint.lookupSource);
|
|
82
|
-
const selectedLookupOption = useMemo(()=>{
|
|
83
|
-
var _lookupOptions_find;
|
|
84
|
-
return (_lookupOptions_find = lookupOptions.find((opt)=>opt.key === condition.value)) !== null && _lookupOptions_find !== void 0 ? _lookupOptions_find : null;
|
|
85
|
-
}, [
|
|
86
|
-
lookupOptions,
|
|
87
|
-
condition.value
|
|
88
|
-
]);
|
|
89
66
|
const operatorItems = useMemo(()=>fieldType === 'number' ? [
|
|
90
67
|
...NUMBER_OPERATORS
|
|
91
68
|
] : [
|
|
@@ -100,81 +77,6 @@ export function ConditionRow({ canRemove, condition, dataPointOptions, formField
|
|
|
100
77
|
operatorItems,
|
|
101
78
|
condition.operator
|
|
102
79
|
]);
|
|
103
|
-
const handleValueChange = (raw)=>{
|
|
104
|
-
const value = fieldType === 'number' ? sanitizeNumericInput(raw) : raw;
|
|
105
|
-
onChange({
|
|
106
|
-
...condition,
|
|
107
|
-
value
|
|
108
|
-
});
|
|
109
|
-
};
|
|
110
|
-
const renderValueControl = ()=>{
|
|
111
|
-
if (lookupStatus === 'loading') {
|
|
112
|
-
return /*#__PURE__*/ _jsx(TextField, {
|
|
113
|
-
disabled: true,
|
|
114
|
-
label: "Value",
|
|
115
|
-
size: "small",
|
|
116
|
-
value: "",
|
|
117
|
-
placeholder: "",
|
|
118
|
-
style: {
|
|
119
|
-
width: '100%'
|
|
120
|
-
},
|
|
121
|
-
"aria-label": "Value"
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
if (lookupStatus === 'ready') {
|
|
125
|
-
return /*#__PURE__*/ _jsxs(Combobox.Select, {
|
|
126
|
-
disableClearSelection: true,
|
|
127
|
-
itemToKey: (item)=>{
|
|
128
|
-
var _item_key;
|
|
129
|
-
return (_item_key = item === null || item === void 0 ? void 0 : item.key) !== null && _item_key !== void 0 ? _item_key : '';
|
|
130
|
-
},
|
|
131
|
-
itemToString: (item)=>{
|
|
132
|
-
var _item_label;
|
|
133
|
-
return (_item_label = item === null || item === void 0 ? void 0 : item.label) !== null && _item_label !== void 0 ? _item_label : '';
|
|
134
|
-
},
|
|
135
|
-
items: lookupOptions,
|
|
136
|
-
selectedItem: selectedLookupOption,
|
|
137
|
-
onChange: (item)=>{
|
|
138
|
-
var _item_key;
|
|
139
|
-
return onChange({
|
|
140
|
-
...condition,
|
|
141
|
-
value: (_item_key = item === null || item === void 0 ? void 0 : item.key) !== null && _item_key !== void 0 ? _item_key : ''
|
|
142
|
-
});
|
|
143
|
-
},
|
|
144
|
-
children: [
|
|
145
|
-
/*#__PURE__*/ _jsx(Combobox.SelectTrigger, {
|
|
146
|
-
label: "Value",
|
|
147
|
-
placeholder: "Select value...",
|
|
148
|
-
size: "small"
|
|
149
|
-
}),
|
|
150
|
-
/*#__PURE__*/ _jsx(Combobox.Content, {
|
|
151
|
-
children: ({ items })=>/*#__PURE__*/ _jsx(Combobox.List, {
|
|
152
|
-
children: items.map((item, i)=>/*#__PURE__*/ _jsx(Combobox.Item, {
|
|
153
|
-
index: i,
|
|
154
|
-
item: item,
|
|
155
|
-
children: item.label
|
|
156
|
-
}, item.key))
|
|
157
|
-
})
|
|
158
|
-
})
|
|
159
|
-
]
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
// idle / fallback (empty/error/timeout) -> never block the user.
|
|
163
|
-
return /*#__PURE__*/ _jsx(TextField, {
|
|
164
|
-
label: "Value",
|
|
165
|
-
size: "small",
|
|
166
|
-
value: condition.value,
|
|
167
|
-
onChange: (e)=>handleValueChange(e.target.value),
|
|
168
|
-
placeholder: fieldType === 'number' ? 'e.g. 1.5' : 'Enter value...',
|
|
169
|
-
style: {
|
|
170
|
-
width: '100%'
|
|
171
|
-
},
|
|
172
|
-
"aria-label": "Value",
|
|
173
|
-
...fieldType === 'number' && {
|
|
174
|
-
inputMode: 'decimal'
|
|
175
|
-
}
|
|
176
|
-
});
|
|
177
|
-
};
|
|
178
80
|
const handleDataPointChange = (item)=>{
|
|
179
81
|
const nextIsNumber = (item === null || item === void 0 ? void 0 : item.fieldType) === 'number';
|
|
180
82
|
const nextOperatorItems = nextIsNumber ? NUMBER_OPERATORS : STRING_OPERATORS;
|
|
@@ -371,7 +273,15 @@ export function ConditionRow({ canRemove, condition, dataPointOptions, formField
|
|
|
371
273
|
flex: '1 1 180px',
|
|
372
274
|
minWidth: 150
|
|
373
275
|
},
|
|
374
|
-
children:
|
|
276
|
+
children: /*#__PURE__*/ _jsx(LookupValueControl, {
|
|
277
|
+
value: condition.value,
|
|
278
|
+
fieldType: fieldType,
|
|
279
|
+
lookupSource: selectedDataPoint === null || selectedDataPoint === void 0 ? void 0 : selectedDataPoint.lookupSource,
|
|
280
|
+
onChange: (value)=>onChange({
|
|
281
|
+
...condition,
|
|
282
|
+
value
|
|
283
|
+
})
|
|
284
|
+
})
|
|
375
285
|
}),
|
|
376
286
|
canRemove && /*#__PURE__*/ _jsx("div", {
|
|
377
287
|
style: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/display-conditions/ConditionRow.tsx"],"sourcesContent":["import { Button, Chip, Combobox, Flex, TextField } from '@servicetitan/anvil2';\nimport TrashIcon from '@servicetitan/anvil2/assets/icons/material/round/delete.svg';\nimport { useEffect, useMemo, useState } from 'react';\nimport { FormInfo, parseFormFieldKey } from '../shared/forms';\nimport type { LookupOption } from './lookupOptionsController';\nimport { NUMBER_OPERATORS, SingleCondition, STRING_OPERATORS, VALUE_LESS_OPERATORS } from './types';\nimport type { DataPointOption, FormFieldOption } from './types';\nimport { useLookupOptions } from './useLookupOptions';\n\nexport interface ConditionRowProps {\n canRemove: boolean;\n condition: SingleCondition;\n dataPointOptions: DataPointOption[];\n formFieldOptions: FormFieldOption[];\n forms: FormInfo[];\n onChange: (c: SingleCondition) => void;\n onFormSelect: (formId: number) => void;\n onRemove: () => void;\n}\n\ninterface OperatorOption {\n label: string;\n value: string;\n}\n\ninterface SourceOption {\n formId?: number;\n label: string;\n value: string;\n}\n\nfunction sanitizeNumericInput(raw: string): string {\n // Allow only a single leading minus and one decimal separator.\n let value = raw.replaceAll(/[^0-9.-]/g, '');\n const isNegative = value.startsWith('-');\n value = value.replaceAll('-', '');\n if (isNegative) {\n value = `-${value}`;\n }\n const firstDot = value.indexOf('.');\n if (firstDot >= 0) {\n value = `${value.slice(0, firstDot + 1)}${value.slice(firstDot + 1).replaceAll('.', '')}`;\n }\n return value;\n}\n\nexport function ConditionRow({\n canRemove,\n condition,\n dataPointOptions,\n formFieldOptions,\n forms,\n onChange,\n onFormSelect,\n onRemove,\n}: Readonly<ConditionRowProps>) {\n const selectedFormFieldMeta = useMemo(\n () => parseFormFieldKey(condition.dataPointKey),\n [condition.dataPointKey],\n );\n const [selectedSourceValue, setSelectedSourceValue] = useState<string>(\n selectedFormFieldMeta ? `form:${selectedFormFieldMeta.formId}` : 'schema',\n );\n\n const sourceOptions = useMemo<SourceOption[]>(\n () => [\n { label: 'Data point', value: 'schema' },\n ...forms.map(form => ({\n formId: form.id,\n label: form.name,\n value: `form:${form.id}`,\n })),\n ],\n [forms],\n );\n\n const selectedSource = useMemo<SourceOption | null>(() => {\n return (\n sourceOptions.find(opt => opt.value === selectedSourceValue) ?? sourceOptions[0] ?? null\n );\n }, [selectedSourceValue, sourceOptions]);\n\n useEffect(() => {\n if (selectedFormFieldMeta) {\n setSelectedSourceValue(`form:${selectedFormFieldMeta.formId}`);\n return;\n }\n if (!condition.dataPointKey) {\n return;\n }\n const hasSchemaMatch = dataPointOptions.some(opt => opt.fullKey === condition.dataPointKey);\n if (hasSchemaMatch) {\n setSelectedSourceValue('schema');\n }\n }, [condition.dataPointKey, dataPointOptions, selectedFormFieldMeta]);\n\n const currentFormId = selectedSource?.formId;\n const activeDataPointOptions = useMemo(\n () =>\n currentFormId\n ? formFieldOptions.filter(option => option.formId === currentFormId)\n : dataPointOptions,\n [currentFormId, dataPointOptions, formFieldOptions],\n );\n\n const selectedDataPoint = useMemo(\n () => activeDataPointOptions.find(opt => opt.fullKey === condition.dataPointKey) ?? null,\n [activeDataPointOptions, condition.dataPointKey],\n );\n\n const fieldType = selectedDataPoint?.fieldType ?? 'string';\n\n const isValueLess = VALUE_LESS_OPERATORS.includes(condition.operator);\n\n // Value-less operators (is empty / is not empty) need no value, so skip the host lookup.\n const { options: lookupOptions, status: lookupStatus } = useLookupOptions(\n isValueLess ? undefined : selectedDataPoint?.lookupSource,\n );\n const selectedLookupOption = useMemo(\n () => lookupOptions.find(opt => opt.key === condition.value) ?? null,\n [lookupOptions, condition.value],\n );\n\n const operatorItems: OperatorOption[] = useMemo(\n () =>\n fieldType === 'number'\n ? ([...NUMBER_OPERATORS] as OperatorOption[])\n : ([...STRING_OPERATORS] as OperatorOption[]),\n [fieldType],\n );\n\n const selectedOperator = useMemo(\n () => operatorItems.find(op => op.value === condition.operator) ?? null,\n [operatorItems, condition.operator],\n );\n\n const handleValueChange = (raw: string) => {\n const value = fieldType === 'number' ? sanitizeNumericInput(raw) : raw;\n onChange({ ...condition, value });\n };\n\n const renderValueControl = () => {\n if (lookupStatus === 'loading') {\n return (\n <TextField\n disabled\n label=\"Value\"\n size=\"small\"\n value=\"\"\n placeholder=\"\"\n style={{ width: '100%' }}\n aria-label=\"Value\"\n />\n );\n }\n\n if (lookupStatus === 'ready') {\n return (\n <Combobox.Select\n disableClearSelection\n itemToKey={(item: LookupOption | null) => item?.key ?? ''}\n itemToString={(item: LookupOption | null) => item?.label ?? ''}\n items={lookupOptions}\n selectedItem={selectedLookupOption}\n onChange={(item: LookupOption | null) =>\n onChange({ ...condition, value: item?.key ?? '' })\n }\n >\n <Combobox.SelectTrigger\n label=\"Value\"\n placeholder=\"Select value...\"\n size=\"small\"\n />\n <Combobox.Content>\n {({ items }: { items: LookupOption[] }) => (\n <Combobox.List>\n {items.map((item, i) => (\n <Combobox.Item index={i} item={item} key={item.key}>\n {item.label}\n </Combobox.Item>\n ))}\n </Combobox.List>\n )}\n </Combobox.Content>\n </Combobox.Select>\n );\n }\n\n // idle / fallback (empty/error/timeout) -> never block the user.\n return (\n <TextField\n label=\"Value\"\n size=\"small\"\n value={condition.value}\n onChange={e => handleValueChange(e.target.value)}\n placeholder={fieldType === 'number' ? 'e.g. 1.5' : 'Enter value...'}\n style={{ width: '100%' }}\n aria-label=\"Value\"\n {...(fieldType === 'number' && { inputMode: 'decimal' })}\n />\n );\n };\n\n const handleDataPointChange = (item: DataPointOption | null) => {\n const nextIsNumber = item?.fieldType === 'number';\n const nextOperatorItems = nextIsNumber ? NUMBER_OPERATORS : STRING_OPERATORS;\n const operatorReset = !nextOperatorItems.some(op => op.value === condition.operator);\n onChange({\n ...condition,\n dataPointKey: item?.fullKey ?? '',\n operator: operatorReset\n ? nextIsNumber\n ? 'num_eq'\n : 'is_equal_to'\n : condition.operator,\n });\n };\n\n const handleSourceChange = (item: SourceOption | null) => {\n if (!item) {\n return;\n }\n setSelectedSourceValue(item.value);\n\n if (!item.formId) {\n const schemaField =\n dataPointOptions.find(opt => opt.fullKey === condition.dataPointKey) ?? null;\n const nextFieldType = schemaField?.fieldType ?? 'string';\n const nextIsNumber = nextFieldType === 'number';\n const nextOperatorItems = nextIsNumber ? NUMBER_OPERATORS : STRING_OPERATORS;\n const operatorReset = !nextOperatorItems.some(op => op.value === condition.operator);\n\n onChange({\n ...condition,\n dataPointKey: schemaField?.fullKey ?? '',\n operator: operatorReset\n ? nextIsNumber\n ? 'num_eq'\n : 'is_equal_to'\n : condition.operator,\n });\n return;\n }\n\n onFormSelect(item.formId);\n const nextFormFieldOptions = formFieldOptions.filter(\n option => option.formId === item.formId,\n );\n const currentSelected = nextFormFieldOptions.find(\n opt => opt.fullKey === condition.dataPointKey,\n );\n const nextSelected = currentSelected ?? nextFormFieldOptions[0] ?? null;\n const nextFieldType = nextSelected?.fieldType ?? 'string';\n const nextIsNumber = nextFieldType === 'number';\n const nextOperatorItems = nextIsNumber ? NUMBER_OPERATORS : STRING_OPERATORS;\n const operatorReset = !nextOperatorItems.some(op => op.value === condition.operator);\n\n onChange({\n ...condition,\n dataPointKey: nextSelected?.fullKey ?? '',\n operator: operatorReset\n ? nextIsNumber\n ? 'num_eq'\n : 'is_equal_to'\n : condition.operator,\n });\n };\n\n return (\n <Flex direction=\"column\" gap=\"3\" style={{ width: '100%' }}>\n <div style={{ width: '100%' }}>\n <Combobox.Select\n disableClearSelection\n itemToKey={(item: SourceOption | null) => item?.value ?? ''}\n itemToString={(item: SourceOption | null) => item?.label ?? ''}\n items={sourceOptions}\n selectedItem={selectedSource}\n onChange={handleSourceChange}\n >\n <Combobox.SelectTrigger\n label=\"Data Type\"\n placeholder=\"Select source...\"\n size=\"small\"\n />\n <Combobox.Content>\n {({ items }: { items: SourceOption[] }) => (\n <Combobox.List>\n {items.map((item, i) => (\n <Combobox.Item index={i} item={item} key={item.value}>\n {item.label}\n </Combobox.Item>\n ))}\n </Combobox.List>\n )}\n </Combobox.Content>\n </Combobox.Select>\n </div>\n <Flex\n direction=\"row\"\n alignItems=\"flex-end\"\n gap=\"2\"\n style={{ flexWrap: 'wrap', rowGap: 8, width: '100%' }}\n >\n <div\n style={{\n display: 'flex',\n alignItems: 'flex-end',\n flexShrink: 0,\n padding: '4px 8px',\n }}\n >\n <Chip label=\"IF\" size=\"small\" />\n </div>\n <div style={{ flex: '2 1 240px', minWidth: 200 }}>\n <Combobox.Select\n disableClearSelection\n itemToKey={(item: DataPointOption | null) => item?.fullKey ?? ''}\n itemToString={(item: DataPointOption | null) => item?.title ?? ''}\n items={activeDataPointOptions}\n selectedItem={selectedDataPoint}\n onChange={handleDataPointChange}\n >\n <Combobox.SelectTrigger\n label=\"Data Point\"\n placeholder={\n currentFormId ? 'Select form field...' : 'Select data point...'\n }\n size=\"small\"\n />\n <Combobox.Content>\n {({ items }: { items: DataPointOption[] }) => (\n <Combobox.List>\n {items.map((item, i) => (\n <Combobox.Item index={i} item={item} key={item.fullKey}>\n {item.title}\n </Combobox.Item>\n ))}\n </Combobox.List>\n )}\n </Combobox.Content>\n </Combobox.Select>\n </div>\n <div style={{ flex: '1 1 180px', minWidth: 150 }}>\n <Combobox.Select\n disableClearSelection\n itemToKey={(item: OperatorOption | null) => item?.value ?? ''}\n itemToString={(item: OperatorOption | null) => item?.label ?? ''}\n items={operatorItems}\n selectedItem={selectedOperator}\n onChange={(item: OperatorOption | null) =>\n onChange({\n ...condition,\n operator: (item?.value ??\n 'is_equal_to') as SingleCondition['operator'],\n })\n }\n >\n <Combobox.SelectTrigger\n label=\"Condition\"\n placeholder=\"Select...\"\n size=\"small\"\n />\n <Combobox.Content>\n {({ items }: { items: OperatorOption[] }) => (\n <Combobox.List>\n {items.map((item, i) => (\n <Combobox.Item index={i} item={item} key={item.value}>\n {item.label}\n </Combobox.Item>\n ))}\n </Combobox.List>\n )}\n </Combobox.Content>\n </Combobox.Select>\n </div>\n {!isValueLess && (\n <div style={{ flex: '1 1 180px', minWidth: 150 }}>{renderValueControl()}</div>\n )}\n {canRemove && (\n <div\n style={{\n display: 'flex',\n alignItems: 'flex-end',\n flexShrink: 0,\n padding: '4px 8px',\n }}\n >\n <Button\n appearance=\"secondary\"\n aria-label=\"Remove condition\"\n icon={{ before: TrashIcon }}\n size=\"small\"\n onClick={onRemove}\n />\n </div>\n )}\n </Flex>\n </Flex>\n );\n}\n"],"names":["Button","Chip","Combobox","Flex","TextField","TrashIcon","useEffect","useMemo","useState","parseFormFieldKey","NUMBER_OPERATORS","STRING_OPERATORS","VALUE_LESS_OPERATORS","useLookupOptions","sanitizeNumericInput","raw","value","replaceAll","isNegative","startsWith","firstDot","indexOf","slice","ConditionRow","canRemove","condition","dataPointOptions","formFieldOptions","forms","onChange","onFormSelect","onRemove","selectedFormFieldMeta","dataPointKey","selectedSourceValue","setSelectedSourceValue","formId","sourceOptions","label","map","form","id","name","selectedSource","find","opt","hasSchemaMatch","some","fullKey","currentFormId","activeDataPointOptions","filter","option","selectedDataPoint","fieldType","isValueLess","includes","operator","options","lookupOptions","status","lookupStatus","undefined","lookupSource","selectedLookupOption","key","operatorItems","selectedOperator","op","handleValueChange","renderValueControl","disabled","size","placeholder","style","width","aria-label","Select","disableClearSelection","itemToKey","item","itemToString","items","selectedItem","SelectTrigger","Content","List","i","Item","index","e","target","inputMode","handleDataPointChange","nextIsNumber","nextOperatorItems","operatorReset","handleSourceChange","schemaField","nextFieldType","nextFormFieldOptions","currentSelected","nextSelected","direction","gap","div","alignItems","flexWrap","rowGap","display","flexShrink","padding","flex","minWidth","title","appearance","icon","before","onClick"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/display-conditions/ConditionRow.tsx"],"sourcesContent":["import { Button, Chip, Combobox, Flex } from '@servicetitan/anvil2';\nimport TrashIcon from '@servicetitan/anvil2/assets/icons/material/round/delete.svg';\nimport { useEffect, useMemo, useState } from 'react';\nimport { FormInfo, parseFormFieldKey } from '../shared/forms';\nimport { LookupValueControl } from './LookupValueControl';\nimport { NUMBER_OPERATORS, SingleCondition, STRING_OPERATORS, VALUE_LESS_OPERATORS } from './types';\nimport type { DataPointOption, FormFieldOption } from './types';\n\nexport interface ConditionRowProps {\n canRemove: boolean;\n condition: SingleCondition;\n dataPointOptions: DataPointOption[];\n formFieldOptions: FormFieldOption[];\n forms: FormInfo[];\n onChange: (c: SingleCondition) => void;\n onFormSelect: (formId: number) => void;\n onRemove: () => void;\n}\n\ninterface OperatorOption {\n label: string;\n value: string;\n}\n\ninterface SourceOption {\n formId?: number;\n label: string;\n value: string;\n}\n\nexport function ConditionRow({\n canRemove,\n condition,\n dataPointOptions,\n formFieldOptions,\n forms,\n onChange,\n onFormSelect,\n onRemove,\n}: Readonly<ConditionRowProps>) {\n const selectedFormFieldMeta = useMemo(\n () => parseFormFieldKey(condition.dataPointKey),\n [condition.dataPointKey],\n );\n const [selectedSourceValue, setSelectedSourceValue] = useState<string>(\n selectedFormFieldMeta ? `form:${selectedFormFieldMeta.formId}` : 'schema',\n );\n\n const sourceOptions = useMemo<SourceOption[]>(\n () => [\n { label: 'Data point', value: 'schema' },\n ...forms.map(form => ({\n formId: form.id,\n label: form.name,\n value: `form:${form.id}`,\n })),\n ],\n [forms],\n );\n\n const selectedSource = useMemo<SourceOption | null>(() => {\n return (\n sourceOptions.find(opt => opt.value === selectedSourceValue) ?? sourceOptions[0] ?? null\n );\n }, [selectedSourceValue, sourceOptions]);\n\n useEffect(() => {\n if (selectedFormFieldMeta) {\n setSelectedSourceValue(`form:${selectedFormFieldMeta.formId}`);\n return;\n }\n if (!condition.dataPointKey) {\n return;\n }\n const hasSchemaMatch = dataPointOptions.some(opt => opt.fullKey === condition.dataPointKey);\n if (hasSchemaMatch) {\n setSelectedSourceValue('schema');\n }\n }, [condition.dataPointKey, dataPointOptions, selectedFormFieldMeta]);\n\n const currentFormId = selectedSource?.formId;\n const activeDataPointOptions = useMemo(\n () =>\n currentFormId\n ? formFieldOptions.filter(option => option.formId === currentFormId)\n : dataPointOptions,\n [currentFormId, dataPointOptions, formFieldOptions],\n );\n\n const selectedDataPoint = useMemo(\n () => activeDataPointOptions.find(opt => opt.fullKey === condition.dataPointKey) ?? null,\n [activeDataPointOptions, condition.dataPointKey],\n );\n\n const fieldType = selectedDataPoint?.fieldType ?? 'string';\n\n const isValueLess = VALUE_LESS_OPERATORS.includes(condition.operator);\n\n const operatorItems: OperatorOption[] = useMemo(\n () =>\n fieldType === 'number'\n ? ([...NUMBER_OPERATORS] as OperatorOption[])\n : ([...STRING_OPERATORS] as OperatorOption[]),\n [fieldType],\n );\n\n const selectedOperator = useMemo(\n () => operatorItems.find(op => op.value === condition.operator) ?? null,\n [operatorItems, condition.operator],\n );\n\n const handleDataPointChange = (item: DataPointOption | null) => {\n const nextIsNumber = item?.fieldType === 'number';\n const nextOperatorItems = nextIsNumber ? NUMBER_OPERATORS : STRING_OPERATORS;\n const operatorReset = !nextOperatorItems.some(op => op.value === condition.operator);\n onChange({\n ...condition,\n dataPointKey: item?.fullKey ?? '',\n operator: operatorReset\n ? nextIsNumber\n ? 'num_eq'\n : 'is_equal_to'\n : condition.operator,\n });\n };\n\n const handleSourceChange = (item: SourceOption | null) => {\n if (!item) {\n return;\n }\n setSelectedSourceValue(item.value);\n\n if (!item.formId) {\n const schemaField =\n dataPointOptions.find(opt => opt.fullKey === condition.dataPointKey) ?? null;\n const nextFieldType = schemaField?.fieldType ?? 'string';\n const nextIsNumber = nextFieldType === 'number';\n const nextOperatorItems = nextIsNumber ? NUMBER_OPERATORS : STRING_OPERATORS;\n const operatorReset = !nextOperatorItems.some(op => op.value === condition.operator);\n\n onChange({\n ...condition,\n dataPointKey: schemaField?.fullKey ?? '',\n operator: operatorReset\n ? nextIsNumber\n ? 'num_eq'\n : 'is_equal_to'\n : condition.operator,\n });\n return;\n }\n\n onFormSelect(item.formId);\n const nextFormFieldOptions = formFieldOptions.filter(\n option => option.formId === item.formId,\n );\n const currentSelected = nextFormFieldOptions.find(\n opt => opt.fullKey === condition.dataPointKey,\n );\n const nextSelected = currentSelected ?? nextFormFieldOptions[0] ?? null;\n const nextFieldType = nextSelected?.fieldType ?? 'string';\n const nextIsNumber = nextFieldType === 'number';\n const nextOperatorItems = nextIsNumber ? NUMBER_OPERATORS : STRING_OPERATORS;\n const operatorReset = !nextOperatorItems.some(op => op.value === condition.operator);\n\n onChange({\n ...condition,\n dataPointKey: nextSelected?.fullKey ?? '',\n operator: operatorReset\n ? nextIsNumber\n ? 'num_eq'\n : 'is_equal_to'\n : condition.operator,\n });\n };\n\n return (\n <Flex direction=\"column\" gap=\"3\" style={{ width: '100%' }}>\n <div style={{ width: '100%' }}>\n <Combobox.Select\n disableClearSelection\n itemToKey={(item: SourceOption | null) => item?.value ?? ''}\n itemToString={(item: SourceOption | null) => item?.label ?? ''}\n items={sourceOptions}\n selectedItem={selectedSource}\n onChange={handleSourceChange}\n >\n <Combobox.SelectTrigger\n label=\"Data Type\"\n placeholder=\"Select source...\"\n size=\"small\"\n />\n <Combobox.Content>\n {({ items }: { items: SourceOption[] }) => (\n <Combobox.List>\n {items.map((item, i) => (\n <Combobox.Item index={i} item={item} key={item.value}>\n {item.label}\n </Combobox.Item>\n ))}\n </Combobox.List>\n )}\n </Combobox.Content>\n </Combobox.Select>\n </div>\n <Flex\n direction=\"row\"\n alignItems=\"flex-end\"\n gap=\"2\"\n style={{ flexWrap: 'wrap', rowGap: 8, width: '100%' }}\n >\n <div\n style={{\n display: 'flex',\n alignItems: 'flex-end',\n flexShrink: 0,\n padding: '4px 8px',\n }}\n >\n <Chip label=\"IF\" size=\"small\" />\n </div>\n <div style={{ flex: '2 1 240px', minWidth: 200 }}>\n <Combobox.Select\n disableClearSelection\n itemToKey={(item: DataPointOption | null) => item?.fullKey ?? ''}\n itemToString={(item: DataPointOption | null) => item?.title ?? ''}\n items={activeDataPointOptions}\n selectedItem={selectedDataPoint}\n onChange={handleDataPointChange}\n >\n <Combobox.SelectTrigger\n label=\"Data Point\"\n placeholder={\n currentFormId ? 'Select form field...' : 'Select data point...'\n }\n size=\"small\"\n />\n <Combobox.Content>\n {({ items }: { items: DataPointOption[] }) => (\n <Combobox.List>\n {items.map((item, i) => (\n <Combobox.Item index={i} item={item} key={item.fullKey}>\n {item.title}\n </Combobox.Item>\n ))}\n </Combobox.List>\n )}\n </Combobox.Content>\n </Combobox.Select>\n </div>\n <div style={{ flex: '1 1 180px', minWidth: 150 }}>\n <Combobox.Select\n disableClearSelection\n itemToKey={(item: OperatorOption | null) => item?.value ?? ''}\n itemToString={(item: OperatorOption | null) => item?.label ?? ''}\n items={operatorItems}\n selectedItem={selectedOperator}\n onChange={(item: OperatorOption | null) =>\n onChange({\n ...condition,\n operator: (item?.value ??\n 'is_equal_to') as SingleCondition['operator'],\n })\n }\n >\n <Combobox.SelectTrigger\n label=\"Condition\"\n placeholder=\"Select...\"\n size=\"small\"\n />\n <Combobox.Content>\n {({ items }: { items: OperatorOption[] }) => (\n <Combobox.List>\n {items.map((item, i) => (\n <Combobox.Item index={i} item={item} key={item.value}>\n {item.label}\n </Combobox.Item>\n ))}\n </Combobox.List>\n )}\n </Combobox.Content>\n </Combobox.Select>\n </div>\n {!isValueLess && (\n <div style={{ flex: '1 1 180px', minWidth: 150 }}>\n <LookupValueControl\n value={condition.value}\n fieldType={fieldType}\n lookupSource={selectedDataPoint?.lookupSource}\n onChange={value => onChange({ ...condition, value })}\n />\n </div>\n )}\n {canRemove && (\n <div\n style={{\n display: 'flex',\n alignItems: 'flex-end',\n flexShrink: 0,\n padding: '4px 8px',\n }}\n >\n <Button\n appearance=\"secondary\"\n aria-label=\"Remove condition\"\n icon={{ before: TrashIcon }}\n size=\"small\"\n onClick={onRemove}\n />\n </div>\n )}\n </Flex>\n </Flex>\n );\n}\n"],"names":["Button","Chip","Combobox","Flex","TrashIcon","useEffect","useMemo","useState","parseFormFieldKey","LookupValueControl","NUMBER_OPERATORS","STRING_OPERATORS","VALUE_LESS_OPERATORS","ConditionRow","canRemove","condition","dataPointOptions","formFieldOptions","forms","onChange","onFormSelect","onRemove","selectedFormFieldMeta","dataPointKey","selectedSourceValue","setSelectedSourceValue","formId","sourceOptions","label","value","map","form","id","name","selectedSource","find","opt","hasSchemaMatch","some","fullKey","currentFormId","activeDataPointOptions","filter","option","selectedDataPoint","fieldType","isValueLess","includes","operator","operatorItems","selectedOperator","op","handleDataPointChange","item","nextIsNumber","nextOperatorItems","operatorReset","handleSourceChange","schemaField","nextFieldType","nextFormFieldOptions","currentSelected","nextSelected","direction","gap","style","width","div","Select","disableClearSelection","itemToKey","itemToString","items","selectedItem","SelectTrigger","placeholder","size","Content","List","i","Item","index","alignItems","flexWrap","rowGap","display","flexShrink","padding","flex","minWidth","title","lookupSource","appearance","aria-label","icon","before","onClick"],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface LookupValueControlProps {
|
|
2
|
+
value: string;
|
|
3
|
+
fieldType: 'number' | 'string';
|
|
4
|
+
/** The selected data point's `options.fieldLookupSource`; empty -> free-text input. */
|
|
5
|
+
lookupSource?: string;
|
|
6
|
+
onChange: (value: string) => void;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Renders the value control for a single condition based on the field's lookup source:
|
|
10
|
+
* - `static` -> full list returned by the host, filtered locally (`SelectFieldSync`).
|
|
11
|
+
* - `searchable` -> host searches per typed query (`SelectField` + `loadOptions`).
|
|
12
|
+
* - `idle`/`fallback`/`loading` -> free-text input (never block the user).
|
|
13
|
+
*
|
|
14
|
+
* Unlayer persists conditions as a Nunjucks string, so a saved searchable value
|
|
15
|
+
* has no stored label — we display the saved key as the label until the user
|
|
16
|
+
* re-searches and re-selects.
|
|
17
|
+
*/
|
|
18
|
+
export declare function LookupValueControl({ fieldType, lookupSource, onChange, value, }: Readonly<LookupValueControlProps>): import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
//# sourceMappingURL=LookupValueControl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LookupValueControl.d.ts","sourceRoot":"","sources":["../../src/display-conditions/LookupValueControl.tsx"],"names":[],"mappings":"AAMA,MAAM,WAAW,uBAAuB;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC/B,uFAAuF;IACvF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC;AAqBD;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,EAC/B,SAAS,EACT,YAAY,EACZ,QAAQ,EACR,KAAK,GACR,EAAE,QAAQ,CAAC,uBAAuB,CAAC,2CAmHnC"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { TextField } from '@servicetitan/anvil2';
|
|
3
|
+
import { SelectField, SelectFieldSync } from '@servicetitan/anvil2/beta';
|
|
4
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
5
|
+
import { useLookupOptions } from './useLookupOptions';
|
|
6
|
+
const toOption = (option)=>({
|
|
7
|
+
id: option.key,
|
|
8
|
+
label: option.label
|
|
9
|
+
});
|
|
10
|
+
const sanitizeNumericInput = (raw)=>{
|
|
11
|
+
let value = raw.replaceAll(/[^0-9.-]/g, '');
|
|
12
|
+
const isNegative = value.startsWith('-');
|
|
13
|
+
value = value.replaceAll('-', '');
|
|
14
|
+
if (isNegative) {
|
|
15
|
+
value = `-${value}`;
|
|
16
|
+
}
|
|
17
|
+
const firstDot = value.indexOf('.');
|
|
18
|
+
if (firstDot >= 0) {
|
|
19
|
+
value = `${value.slice(0, firstDot + 1)}${value.slice(firstDot + 1).replaceAll('.', '')}`;
|
|
20
|
+
}
|
|
21
|
+
return value;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Renders the value control for a single condition based on the field's lookup source:
|
|
25
|
+
* - `static` -> full list returned by the host, filtered locally (`SelectFieldSync`).
|
|
26
|
+
* - `searchable` -> host searches per typed query (`SelectField` + `loadOptions`).
|
|
27
|
+
* - `idle`/`fallback`/`loading` -> free-text input (never block the user).
|
|
28
|
+
*
|
|
29
|
+
* Unlayer persists conditions as a Nunjucks string, so a saved searchable value
|
|
30
|
+
* has no stored label — we display the saved key as the label until the user
|
|
31
|
+
* re-searches and re-selects.
|
|
32
|
+
*/ export function LookupValueControl({ fieldType, lookupSource, onChange, value }) {
|
|
33
|
+
const { loadOptions, options, resolveOptions, status } = useLookupOptions(lookupSource);
|
|
34
|
+
const staticSelected = useMemo(()=>{
|
|
35
|
+
var _options_filter_map_;
|
|
36
|
+
return (_options_filter_map_ = options.filter((option)=>option.key === value).map(toOption)[0]) !== null && _options_filter_map_ !== void 0 ? _options_filter_map_ : null;
|
|
37
|
+
}, [
|
|
38
|
+
options,
|
|
39
|
+
value
|
|
40
|
+
]);
|
|
41
|
+
const [searchableSelected, setSearchableSelected] = useState(value ? {
|
|
42
|
+
id: value,
|
|
43
|
+
label: value
|
|
44
|
+
} : null);
|
|
45
|
+
// Tracks the key we've already turned into a human-readable label so we don't re-resolve it.
|
|
46
|
+
const resolvedKeyRef = useRef(null);
|
|
47
|
+
useEffect(()=>{
|
|
48
|
+
setSearchableSelected((prev)=>{
|
|
49
|
+
if (!value) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
if ((prev === null || prev === void 0 ? void 0 : prev.id) === value) {
|
|
53
|
+
return prev;
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
id: value,
|
|
57
|
+
label: value
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
}, [
|
|
61
|
+
value
|
|
62
|
+
]);
|
|
63
|
+
// On open (or when a saved searchable value appears), ask the host to resolve key -> label.
|
|
64
|
+
useEffect(()=>{
|
|
65
|
+
if (status !== 'searchable' || !value) {
|
|
66
|
+
resolvedKeyRef.current = null;
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (resolvedKeyRef.current === value) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
let cancelled = false;
|
|
73
|
+
resolveOptions([
|
|
74
|
+
value
|
|
75
|
+
]).then((result)=>{
|
|
76
|
+
if (cancelled) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const match = result.find((option)=>option.key === value);
|
|
80
|
+
if (match) {
|
|
81
|
+
resolvedKeyRef.current = value;
|
|
82
|
+
setSearchableSelected({
|
|
83
|
+
id: match.key,
|
|
84
|
+
label: match.label
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
return ()=>{
|
|
89
|
+
cancelled = true;
|
|
90
|
+
};
|
|
91
|
+
}, [
|
|
92
|
+
status,
|
|
93
|
+
value,
|
|
94
|
+
resolveOptions
|
|
95
|
+
]);
|
|
96
|
+
const handleLoadOptions = useCallback(async (query)=>{
|
|
97
|
+
const result = await loadOptions(query);
|
|
98
|
+
return result.map(toOption);
|
|
99
|
+
}, [
|
|
100
|
+
loadOptions
|
|
101
|
+
]);
|
|
102
|
+
if (status === 'loading') {
|
|
103
|
+
return /*#__PURE__*/ _jsx(TextField, {
|
|
104
|
+
label: "Value",
|
|
105
|
+
flex: 1,
|
|
106
|
+
disabled: true,
|
|
107
|
+
value: "",
|
|
108
|
+
placeholder: "",
|
|
109
|
+
"aria-label": "Value"
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
if (status === 'static') {
|
|
113
|
+
return /*#__PURE__*/ _jsx(SelectFieldSync, {
|
|
114
|
+
flex: 1,
|
|
115
|
+
label: "Value",
|
|
116
|
+
placeholder: "Search value...",
|
|
117
|
+
options: options.map(toOption),
|
|
118
|
+
value: staticSelected,
|
|
119
|
+
onSelectedOptionChange: (option)=>onChange(option ? String(option.id) : '')
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
if (status === 'searchable') {
|
|
123
|
+
return /*#__PURE__*/ _jsx(SelectField, {
|
|
124
|
+
flex: 1,
|
|
125
|
+
label: "Value",
|
|
126
|
+
placeholder: "Type to search...",
|
|
127
|
+
initialLoad: "open",
|
|
128
|
+
loadOptions: handleLoadOptions,
|
|
129
|
+
value: searchableSelected,
|
|
130
|
+
onSelectedOptionChange: (option)=>{
|
|
131
|
+
resolvedKeyRef.current = option ? String(option.id) : null;
|
|
132
|
+
setSearchableSelected(option);
|
|
133
|
+
onChange(option ? String(option.id) : '');
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
// idle / fallback (no source, empty, error, or timeout) -> free text.
|
|
138
|
+
const handleTextChange = (raw)=>{
|
|
139
|
+
onChange(fieldType === 'number' ? sanitizeNumericInput(raw) : raw);
|
|
140
|
+
};
|
|
141
|
+
return /*#__PURE__*/ _jsx(TextField, {
|
|
142
|
+
label: "Value",
|
|
143
|
+
flex: 1,
|
|
144
|
+
value: value,
|
|
145
|
+
onChange: (e)=>handleTextChange(e.target.value),
|
|
146
|
+
placeholder: fieldType === 'number' ? 'e.g. 1.5' : 'Enter value...',
|
|
147
|
+
"aria-label": "Value",
|
|
148
|
+
...fieldType === 'number' && {
|
|
149
|
+
inputMode: 'decimal'
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
//# sourceMappingURL=LookupValueControl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/display-conditions/LookupValueControl.tsx"],"sourcesContent":["import { TextField } from '@servicetitan/anvil2';\nimport { SelectField, SelectFieldOption, SelectFieldSync } from '@servicetitan/anvil2/beta';\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { LookupOption } from './lookupOptionsController';\nimport { useLookupOptions } from './useLookupOptions';\n\nexport interface LookupValueControlProps {\n value: string;\n fieldType: 'number' | 'string';\n /** The selected data point's `options.fieldLookupSource`; empty -> free-text input. */\n lookupSource?: string;\n onChange: (value: string) => void;\n}\n\nconst toOption = (option: LookupOption): SelectFieldOption => ({\n id: option.key,\n label: option.label,\n});\n\nconst sanitizeNumericInput = (raw: string): string => {\n let value = raw.replaceAll(/[^0-9.-]/g, '');\n const isNegative = value.startsWith('-');\n value = value.replaceAll('-', '');\n if (isNegative) {\n value = `-${value}`;\n }\n const firstDot = value.indexOf('.');\n if (firstDot >= 0) {\n value = `${value.slice(0, firstDot + 1)}${value.slice(firstDot + 1).replaceAll('.', '')}`;\n }\n return value;\n};\n\n/**\n * Renders the value control for a single condition based on the field's lookup source:\n * - `static` -> full list returned by the host, filtered locally (`SelectFieldSync`).\n * - `searchable` -> host searches per typed query (`SelectField` + `loadOptions`).\n * - `idle`/`fallback`/`loading` -> free-text input (never block the user).\n *\n * Unlayer persists conditions as a Nunjucks string, so a saved searchable value\n * has no stored label — we display the saved key as the label until the user\n * re-searches and re-selects.\n */\nexport function LookupValueControl({\n fieldType,\n lookupSource,\n onChange,\n value,\n}: Readonly<LookupValueControlProps>) {\n const { loadOptions, options, resolveOptions, status } = useLookupOptions(lookupSource);\n\n const staticSelected = useMemo<SelectFieldOption | null>(\n () => options.filter(option => option.key === value).map(toOption)[0] ?? null,\n [options, value],\n );\n\n const [searchableSelected, setSearchableSelected] = useState<SelectFieldOption | null>(\n value ? { id: value, label: value } : null,\n );\n\n // Tracks the key we've already turned into a human-readable label so we don't re-resolve it.\n const resolvedKeyRef = useRef<string | null>(null);\n\n useEffect(() => {\n setSearchableSelected(prev => {\n if (!value) {\n return null;\n }\n if (prev?.id === value) {\n return prev;\n }\n return { id: value, label: value };\n });\n }, [value]);\n\n // On open (or when a saved searchable value appears), ask the host to resolve key -> label.\n useEffect(() => {\n if (status !== 'searchable' || !value) {\n resolvedKeyRef.current = null;\n return;\n }\n if (resolvedKeyRef.current === value) {\n return;\n }\n\n let cancelled = false;\n resolveOptions([value]).then(result => {\n if (cancelled) {\n return;\n }\n const match = result.find(option => option.key === value);\n if (match) {\n resolvedKeyRef.current = value;\n setSearchableSelected({ id: match.key, label: match.label });\n }\n });\n\n return () => {\n cancelled = true;\n };\n }, [status, value, resolveOptions]);\n\n const handleLoadOptions = useCallback(\n async (query: string) => {\n const result = await loadOptions(query);\n return result.map(toOption);\n },\n [loadOptions],\n );\n\n if (status === 'loading') {\n return (\n <TextField label=\"Value\" flex={1} disabled value=\"\" placeholder=\"\" aria-label=\"Value\" />\n );\n }\n\n if (status === 'static') {\n return (\n <SelectFieldSync\n flex={1}\n label=\"Value\"\n placeholder=\"Search value...\"\n options={options.map(toOption)}\n value={staticSelected}\n onSelectedOptionChange={option => onChange(option ? String(option.id) : '')}\n />\n );\n }\n\n if (status === 'searchable') {\n return (\n <SelectField\n flex={1}\n label=\"Value\"\n placeholder=\"Type to search...\"\n initialLoad=\"open\"\n loadOptions={handleLoadOptions}\n value={searchableSelected}\n onSelectedOptionChange={option => {\n resolvedKeyRef.current = option ? String(option.id) : null;\n setSearchableSelected(option);\n onChange(option ? String(option.id) : '');\n }}\n />\n );\n }\n\n // idle / fallback (no source, empty, error, or timeout) -> free text.\n const handleTextChange = (raw: string) => {\n onChange(fieldType === 'number' ? sanitizeNumericInput(raw) : raw);\n };\n\n return (\n <TextField\n label=\"Value\"\n flex={1}\n value={value}\n onChange={e => handleTextChange(e.target.value)}\n placeholder={fieldType === 'number' ? 'e.g. 1.5' : 'Enter value...'}\n aria-label=\"Value\"\n {...(fieldType === 'number' && { inputMode: 'decimal' })}\n />\n );\n}\n"],"names":["TextField","SelectField","SelectFieldSync","useCallback","useEffect","useMemo","useRef","useState","useLookupOptions","toOption","option","id","key","label","sanitizeNumericInput","raw","value","replaceAll","isNegative","startsWith","firstDot","indexOf","slice","LookupValueControl","fieldType","lookupSource","onChange","loadOptions","options","resolveOptions","status","staticSelected","filter","map","searchableSelected","setSearchableSelected","resolvedKeyRef","prev","current","cancelled","then","result","match","find","handleLoadOptions","query","flex","disabled","placeholder","aria-label","onSelectedOptionChange","String","initialLoad","handleTextChange","e","target","inputMode"],"mappings":"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"}
|
|
@@ -6,6 +6,16 @@
|
|
|
6
6
|
* UI, the editor does NOT own the option data — it asks the HOST for it over the
|
|
7
7
|
* EventTarget bus below and renders whatever the host returns.
|
|
8
8
|
*
|
|
9
|
+
* Two host-driven modes are supported (the host decides, the editor adapts):
|
|
10
|
+
* 1. Full-list source (e.g. "state", "jobType"): the host returns every option
|
|
11
|
+
* on the first request and omits `searchable`. The editor caches that list
|
|
12
|
+
* and filters it LOCALLY as the user types — no further requests are sent.
|
|
13
|
+
* 2. Searchable source (e.g. "city"): the list is too large to send at once.
|
|
14
|
+
* The host returns `searchable: true` (typically with an empty list on the
|
|
15
|
+
* first, query-less request). The editor then re-requests with the typed
|
|
16
|
+
* `query` on each keystroke and the host returns a fresh, server-filtered
|
|
17
|
+
* list per query.
|
|
18
|
+
*
|
|
9
19
|
* Transport mirrors the existing `displayConditionController` EventBus pattern.
|
|
10
20
|
*
|
|
11
21
|
* Event names (host <-> editor):
|
|
@@ -29,22 +39,34 @@ export interface LookupOptionsRequest {
|
|
|
29
39
|
requestId: string;
|
|
30
40
|
/** The selected field's `options.fieldLookupSource`. */
|
|
31
41
|
sourceKey: string;
|
|
32
|
-
/**
|
|
42
|
+
/**
|
|
43
|
+
* The text the user has typed. Sent on each (debounced) keystroke once the
|
|
44
|
+
* host has declared the source `searchable`. Empty/omitted on the first
|
|
45
|
+
* request, where the host decides whether the source is searchable.
|
|
46
|
+
*/
|
|
33
47
|
query?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Resolve mode: the editor is asking the host to return the option(s) for
|
|
50
|
+
* these EXACT stored keys (not a text search). Used on open to turn a saved
|
|
51
|
+
* searchable value (a bare key) back into a human-readable label. When
|
|
52
|
+
* present the host should match by key and ignore `query`.
|
|
53
|
+
*/
|
|
54
|
+
keys?: string[];
|
|
34
55
|
}
|
|
35
56
|
/** host -> editor */
|
|
36
57
|
export interface LookupOptionsResponse {
|
|
37
58
|
requestId: string;
|
|
38
59
|
options: LookupOption[];
|
|
60
|
+
/**
|
|
61
|
+
* Set to `true` by the host when the source must be searched per query
|
|
62
|
+
* (server-side). When omitted/false the editor treats `options` as the
|
|
63
|
+
* complete list and switches to local filtering. Only read from the first
|
|
64
|
+
* (query-less) response for a given source.
|
|
65
|
+
*/
|
|
66
|
+
searchable?: boolean;
|
|
39
67
|
}
|
|
40
68
|
export declare const LOOKUP_OPTIONS_REQUEST_EVENT = "dte-unlayer:lookup-options-request";
|
|
41
69
|
export declare const LOOKUP_OPTIONS_RESPONSE_EVENT = "dte-unlayer:lookup-options-response";
|
|
42
|
-
/**
|
|
43
|
-
* Searchable sources require the host to query an API per typed query. These are
|
|
44
|
-
* skipped for now and fall back to the free-text input.
|
|
45
|
-
*/
|
|
46
|
-
export declare const SEARCHABLE_LOOKUP_SOURCES: readonly ["city"];
|
|
47
|
-
export declare const isSearchableLookupSource: (sourceKey: string | undefined) => boolean;
|
|
48
70
|
/** editor -> host: ask the host to resolve options for a lookup source. */
|
|
49
71
|
export declare const emitLookupOptionsRequest: (request: LookupOptionsRequest) => void;
|
|
50
72
|
/** host side: listen for editor requests. Returns an unsubscribe function. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lookupOptionsController.d.ts","sourceRoot":"","sources":["../../src/display-conditions/lookupOptionsController.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"lookupOptionsController.d.ts","sourceRoot":"","sources":["../../src/display-conditions/lookupOptionsController.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,wDAAwD;AACxD,MAAM,WAAW,YAAY;IACzB,kDAAkD;IAClD,KAAK,EAAE,MAAM,CAAC;IACd,oDAAoD;IACpD,GAAG,EAAE,MAAM,CAAC;CACf;AAED,qBAAqB;AACrB,MAAM,WAAW,oBAAoB;IACjC,wDAAwD;IACxD,SAAS,EAAE,MAAM,CAAC;IAClB,wDAAwD;IACxD,SAAS,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,qBAAqB;AACrB,MAAM,WAAW,qBAAqB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,eAAO,MAAM,4BAA4B,uCAAuC,CAAC;AACjF,eAAO,MAAM,6BAA6B,wCAAwC,CAAC;AAInF,2EAA2E;AAC3E,eAAO,MAAM,wBAAwB,GAAI,SAAS,oBAAoB,SAIrE,CAAC;AAEF,8EAA8E;AAC9E,eAAO,MAAM,sBAAsB,GAAI,UAAU,CAAC,OAAO,EAAE,oBAAoB,KAAK,IAAI,eAcvF,CAAC;AAEF,mEAAmE;AACnE,eAAO,MAAM,yBAAyB,GAAI,UAAU,qBAAqB,SAIxE,CAAC;AAEF,+EAA+E;AAC/E,eAAO,MAAM,uBAAuB,GAAI,UAAU,CAAC,QAAQ,EAAE,qBAAqB,KAAK,IAAI,eAc1F,CAAC"}
|
|
@@ -6,6 +6,16 @@
|
|
|
6
6
|
* UI, the editor does NOT own the option data — it asks the HOST for it over the
|
|
7
7
|
* EventTarget bus below and renders whatever the host returns.
|
|
8
8
|
*
|
|
9
|
+
* Two host-driven modes are supported (the host decides, the editor adapts):
|
|
10
|
+
* 1. Full-list source (e.g. "state", "jobType"): the host returns every option
|
|
11
|
+
* on the first request and omits `searchable`. The editor caches that list
|
|
12
|
+
* and filters it LOCALLY as the user types — no further requests are sent.
|
|
13
|
+
* 2. Searchable source (e.g. "city"): the list is too large to send at once.
|
|
14
|
+
* The host returns `searchable: true` (typically with an empty list on the
|
|
15
|
+
* first, query-less request). The editor then re-requests with the typed
|
|
16
|
+
* `query` on each keystroke and the host returns a fresh, server-filtered
|
|
17
|
+
* list per query.
|
|
18
|
+
*
|
|
9
19
|
* Transport mirrors the existing `displayConditionController` EventBus pattern.
|
|
10
20
|
*
|
|
11
21
|
* Event names (host <-> editor):
|
|
@@ -17,13 +27,6 @@
|
|
|
17
27
|
* `emitLookupOptionsResponse`.
|
|
18
28
|
*/ /** The single dropdown option shape used end-to-end. */ export const LOOKUP_OPTIONS_REQUEST_EVENT = 'dte-unlayer:lookup-options-request';
|
|
19
29
|
export const LOOKUP_OPTIONS_RESPONSE_EVENT = 'dte-unlayer:lookup-options-response';
|
|
20
|
-
/**
|
|
21
|
-
* Searchable sources require the host to query an API per typed query. These are
|
|
22
|
-
* skipped for now and fall back to the free-text input.
|
|
23
|
-
*/ export const SEARCHABLE_LOOKUP_SOURCES = [
|
|
24
|
-
'city'
|
|
25
|
-
];
|
|
26
|
-
export const isSearchableLookupSource = (sourceKey)=>!!sourceKey && SEARCHABLE_LOOKUP_SOURCES.includes(sourceKey);
|
|
27
30
|
const lookupOptionsEvents = new EventTarget();
|
|
28
31
|
/** editor -> host: ask the host to resolve options for a lookup source. */ export const emitLookupOptionsRequest = (request)=>{
|
|
29
32
|
lookupOptionsEvents.dispatchEvent(new CustomEvent(LOOKUP_OPTIONS_REQUEST_EVENT, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/display-conditions/lookupOptionsController.ts"],"sourcesContent":["/**\n * Host <-> editor contract for \"data-model lookup source\" dropdown options.\n *\n * A schema field can be tagged (in the template-type editor) with\n * `options.fieldLookupSource`. When such a field is selected in the conditional\n * UI, the editor does NOT own the option data — it asks the HOST for it over the\n * EventTarget bus below and renders whatever the host returns.\n *\n * Transport mirrors the existing `displayConditionController` EventBus pattern.\n *\n * Event names (host <-> editor):\n * - \"dte-unlayer:lookup-options-request\" (editor -> host)\n * - \"dte-unlayer:lookup-options-response\" (host -> editor)\n *\n * The host-side resolver is intentionally NOT implemented in this package. The\n * host listens via `onLookupOptionsRequest` and replies via\n * `emitLookupOptionsResponse`.\n */\n\n/** The single dropdown option shape used end-to-end. */\nexport interface LookupOption {\n /** Human-readable label shown in the dropdown. */\n label: string;\n /** Stable value baked into the stored condition. */\n key: string;\n}\n\n/** editor -> host */\nexport interface LookupOptionsRequest {\n /** Correlates a response to its originating request. */\n requestId: string;\n /** The selected field's `options.fieldLookupSource`. */\n sourceKey: string;\n
|
|
1
|
+
{"version":3,"sources":["../../src/display-conditions/lookupOptionsController.ts"],"sourcesContent":["/**\n * Host <-> editor contract for \"data-model lookup source\" dropdown options.\n *\n * A schema field can be tagged (in the template-type editor) with\n * `options.fieldLookupSource`. When such a field is selected in the conditional\n * UI, the editor does NOT own the option data — it asks the HOST for it over the\n * EventTarget bus below and renders whatever the host returns.\n *\n * Two host-driven modes are supported (the host decides, the editor adapts):\n * 1. Full-list source (e.g. \"state\", \"jobType\"): the host returns every option\n * on the first request and omits `searchable`. The editor caches that list\n * and filters it LOCALLY as the user types — no further requests are sent.\n * 2. Searchable source (e.g. \"city\"): the list is too large to send at once.\n * The host returns `searchable: true` (typically with an empty list on the\n * first, query-less request). The editor then re-requests with the typed\n * `query` on each keystroke and the host returns a fresh, server-filtered\n * list per query.\n *\n * Transport mirrors the existing `displayConditionController` EventBus pattern.\n *\n * Event names (host <-> editor):\n * - \"dte-unlayer:lookup-options-request\" (editor -> host)\n * - \"dte-unlayer:lookup-options-response\" (host -> editor)\n *\n * The host-side resolver is intentionally NOT implemented in this package. The\n * host listens via `onLookupOptionsRequest` and replies via\n * `emitLookupOptionsResponse`.\n */\n\n/** The single dropdown option shape used end-to-end. */\nexport interface LookupOption {\n /** Human-readable label shown in the dropdown. */\n label: string;\n /** Stable value baked into the stored condition. */\n key: string;\n}\n\n/** editor -> host */\nexport interface LookupOptionsRequest {\n /** Correlates a response to its originating request. */\n requestId: string;\n /** The selected field's `options.fieldLookupSource`. */\n sourceKey: string;\n /**\n * The text the user has typed. Sent on each (debounced) keystroke once the\n * host has declared the source `searchable`. Empty/omitted on the first\n * request, where the host decides whether the source is searchable.\n */\n query?: string;\n /**\n * Resolve mode: the editor is asking the host to return the option(s) for\n * these EXACT stored keys (not a text search). Used on open to turn a saved\n * searchable value (a bare key) back into a human-readable label. When\n * present the host should match by key and ignore `query`.\n */\n keys?: string[];\n}\n\n/** host -> editor */\nexport interface LookupOptionsResponse {\n requestId: string;\n options: LookupOption[];\n /**\n * Set to `true` by the host when the source must be searched per query\n * (server-side). When omitted/false the editor treats `options` as the\n * complete list and switches to local filtering. Only read from the first\n * (query-less) response for a given source.\n */\n searchable?: boolean;\n}\n\nexport const LOOKUP_OPTIONS_REQUEST_EVENT = 'dte-unlayer:lookup-options-request';\nexport const LOOKUP_OPTIONS_RESPONSE_EVENT = 'dte-unlayer:lookup-options-response';\n\nconst lookupOptionsEvents = new EventTarget();\n\n/** editor -> host: ask the host to resolve options for a lookup source. */\nexport const emitLookupOptionsRequest = (request: LookupOptionsRequest) => {\n lookupOptionsEvents.dispatchEvent(\n new CustomEvent<LookupOptionsRequest>(LOOKUP_OPTIONS_REQUEST_EVENT, { detail: request }),\n );\n};\n\n/** host side: listen for editor requests. Returns an unsubscribe function. */\nexport const onLookupOptionsRequest = (listener: (request: LookupOptionsRequest) => void) => {\n const handler = (event: Event) => {\n const detail = (event as CustomEvent<LookupOptionsRequest>).detail;\n if (!detail) {\n return;\n }\n listener(detail);\n };\n\n lookupOptionsEvents.addEventListener(LOOKUP_OPTIONS_REQUEST_EVENT, handler);\n\n return () => {\n lookupOptionsEvents.removeEventListener(LOOKUP_OPTIONS_REQUEST_EVENT, handler);\n };\n};\n\n/** host -> editor: deliver resolved options back to the editor. */\nexport const emitLookupOptionsResponse = (response: LookupOptionsResponse) => {\n lookupOptionsEvents.dispatchEvent(\n new CustomEvent<LookupOptionsResponse>(LOOKUP_OPTIONS_RESPONSE_EVENT, { detail: response }),\n );\n};\n\n/** editor side: listen for host responses. Returns an unsubscribe function. */\nexport const onLookupOptionsResponse = (listener: (response: LookupOptionsResponse) => void) => {\n const handler = (event: Event) => {\n const detail = (event as CustomEvent<LookupOptionsResponse>).detail;\n if (!detail) {\n return;\n }\n listener(detail);\n };\n\n lookupOptionsEvents.addEventListener(LOOKUP_OPTIONS_RESPONSE_EVENT, handler);\n\n return () => {\n lookupOptionsEvents.removeEventListener(LOOKUP_OPTIONS_RESPONSE_EVENT, handler);\n };\n};\n"],"names":["LOOKUP_OPTIONS_REQUEST_EVENT","LOOKUP_OPTIONS_RESPONSE_EVENT","lookupOptionsEvents","EventTarget","emitLookupOptionsRequest","request","dispatchEvent","CustomEvent","detail","onLookupOptionsRequest","listener","handler","event","addEventListener","removeEventListener","emitLookupOptionsResponse","response","onLookupOptionsResponse"],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2BC,GAED,sDAAsD,GA0CtD,OAAO,MAAMA,+BAA+B,qCAAqC;AACjF,OAAO,MAAMC,gCAAgC,sCAAsC;AAEnF,MAAMC,sBAAsB,IAAIC;AAEhC,yEAAyE,GACzE,OAAO,MAAMC,2BAA2B,CAACC;IACrCH,oBAAoBI,aAAa,CAC7B,IAAIC,YAAkCP,8BAA8B;QAAEQ,QAAQH;IAAQ;AAE9F,EAAE;AAEF,4EAA4E,GAC5E,OAAO,MAAMI,yBAAyB,CAACC;IACnC,MAAMC,UAAU,CAACC;QACb,MAAMJ,SAAS,AAACI,MAA4CJ,MAAM;QAClE,IAAI,CAACA,QAAQ;YACT;QACJ;QACAE,SAASF;IACb;IAEAN,oBAAoBW,gBAAgB,CAACb,8BAA8BW;IAEnE,OAAO;QACHT,oBAAoBY,mBAAmB,CAACd,8BAA8BW;IAC1E;AACJ,EAAE;AAEF,iEAAiE,GACjE,OAAO,MAAMI,4BAA4B,CAACC;IACtCd,oBAAoBI,aAAa,CAC7B,IAAIC,YAAmCN,+BAA+B;QAAEO,QAAQQ;IAAS;AAEjG,EAAE;AAEF,6EAA6E,GAC7E,OAAO,MAAMC,0BAA0B,CAACP;IACpC,MAAMC,UAAU,CAACC;QACb,MAAMJ,SAAS,AAACI,MAA6CJ,MAAM;QACnE,IAAI,CAACA,QAAQ;YACT;QACJ;QACAE,SAASF;IACb;IAEAN,oBAAoBW,gBAAgB,CAACZ,+BAA+BU;IAEpE,OAAO;QACHT,oBAAoBY,mBAAmB,CAACb,+BAA+BU;IAC3E;AACJ,EAAE"}
|