@servicetitan/dte-unlayer 0.123.0 → 0.125.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.
@@ -1 +1 @@
1
- {"version":3,"file":"DisplayConditionModal.d.ts","sourceRoot":"","sources":["../../src/display-conditions/DisplayConditionModal.tsx"],"names":[],"mappings":"AAGA,OAAO,EAEH,aAAa,EACb,QAAQ,EAGX,MAAM,iBAAiB,CAAC;AAmDzB,MAAM,WAAW,0BAA0B;IACvC,qBAAqB,CAAC,EAAE,CACpB,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,KAAK,IAAI,KAChE,IAAI,CAAC;IACV,kBAAkB,CAAC,EAAE,CACjB,WAAW,EAAE,MAAM,EAAE,EACrB,YAAY,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,IAAI,EACzC,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,KAAK,IAAI,KAChE,IAAI,CAAC;IACV,MAAM,CAAC,EAAE,OAAO,kBAAkB,EAAE,YAAY,CAAC;CACpD;AAED,eAAO,MAAM,qBAAqB,GAAI,OAAO,0BAA0B,uCA2QtE,CAAC"}
1
+ {"version":3,"file":"DisplayConditionModal.d.ts","sourceRoot":"","sources":["../../src/display-conditions/DisplayConditionModal.tsx"],"names":[],"mappings":"AAGA,OAAO,EAEH,aAAa,EACb,QAAQ,EAGX,MAAM,iBAAiB,CAAC;AAmDzB,MAAM,WAAW,0BAA0B;IACvC,qBAAqB,CAAC,EAAE,CACpB,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,KAAK,IAAI,KAChE,IAAI,CAAC;IACV,kBAAkB,CAAC,EAAE,CACjB,WAAW,EAAE,MAAM,EAAE,EACrB,YAAY,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,IAAI,EACzC,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,KAAK,IAAI,KAChE,IAAI,CAAC;IACV,MAAM,CAAC,EAAE,OAAO,kBAAkB,EAAE,YAAY,CAAC;CACpD;AAED,eAAO,MAAM,qBAAqB,GAAI,OAAO,0BAA0B,uCA0StE,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Button, Combobox, Dialog, Flex, Text } from '@servicetitan/anvil2';
3
- import { useCallback, useEffect, useMemo, useState } from 'react';
3
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
4
4
  import { createPortal } from 'react-dom';
5
5
  import { buildFormFieldKey, getConditionalFieldTypeFromFormItemType, parseFormFieldKey } from '../shared/forms';
6
6
  import { ConditionGroupsSection } from './ConditionGroupsSection';
@@ -55,6 +55,8 @@ export const DisplayConditionModal = (props)=>{
55
55
  const [formFieldsByFormId, setFormFieldsByFormId] = useState({});
56
56
  const [loadingFormIds, setLoadingFormIds] = useState([]);
57
57
  const [isFormsListLoading, setIsFormsListLoading] = useState(false);
58
+ const formFieldsByFormIdRef = useRef({});
59
+ const loadingFormIdsRef = useRef([]);
58
60
  const dataPointOptions = useMemo(()=>getSchemaDataPointOptions(schema), [
59
61
  schema
60
62
  ]);
@@ -91,6 +93,37 @@ export const DisplayConditionModal = (props)=>{
91
93
  formFieldOptions
92
94
  ]);
93
95
  const isFieldDataLoading = isFormsListLoading || loadingFormIds.length > 0;
96
+ useEffect(()=>{
97
+ formFieldsByFormIdRef.current = formFieldsByFormId;
98
+ }, [
99
+ formFieldsByFormId
100
+ ]);
101
+ useEffect(()=>{
102
+ loadingFormIdsRef.current = loadingFormIds;
103
+ }, [
104
+ loadingFormIds
105
+ ]);
106
+ const requestFormFields = useCallback((formId)=>{
107
+ if (!onConditionFormSelect) {
108
+ return;
109
+ }
110
+ if (formFieldsByFormIdRef.current[formId] || loadingFormIdsRef.current.includes(formId)) {
111
+ return;
112
+ }
113
+ setLoadingFormIds((prev)=>prev.includes(formId) ? prev : [
114
+ ...prev,
115
+ formId
116
+ ]);
117
+ onConditionFormSelect(formId, (selectedFormId, fields)=>{
118
+ setFormFieldsByFormId((prev)=>({
119
+ ...prev,
120
+ [selectedFormId]: fields
121
+ }));
122
+ setLoadingFormIds((prev)=>prev.filter((id)=>id !== selectedFormId));
123
+ });
124
+ }, [
125
+ onConditionFormSelect
126
+ ]);
94
127
  const portalTarget = useMemo(()=>{
95
128
  if (typeof document === 'undefined') {
96
129
  return null;
@@ -110,7 +143,7 @@ export const DisplayConditionModal = (props)=>{
110
143
  setForms([]);
111
144
  setFormFieldsByFormId({});
112
145
  setIsFormsListLoading(!!onConditionalsOpen);
113
- setLoadingFormIds(usedFormIds);
146
+ setLoadingFormIds([]);
114
147
  if (onConditionalsOpen) {
115
148
  onConditionalsOpen(usedFormIds, (nextForms)=>{
116
149
  setForms(nextForms);
@@ -122,6 +155,11 @@ export const DisplayConditionModal = (props)=>{
122
155
  }));
123
156
  setLoadingFormIds((prev)=>prev.filter((id)=>id !== formId));
124
157
  });
158
+ /*
159
+ * Keep display-condition flow aligned with calculated-field flow:
160
+ * when modal opens, request fields for already-used forms so labels
161
+ * are available in edit mode.
162
+ */ usedFormIds.forEach((formId)=>requestFormFields(formId));
125
163
  } else {
126
164
  setIsFormsListLoading(false);
127
165
  setLoadingFormIds([]);
@@ -131,7 +169,8 @@ export const DisplayConditionModal = (props)=>{
131
169
  setIsOpen(true);
132
170
  });
133
171
  }, [
134
- onConditionalsOpen
172
+ onConditionalsOpen,
173
+ requestFormFields
135
174
  ]);
136
175
  const handleClose = useCallback(()=>{
137
176
  setRequest(null);
@@ -182,23 +221,9 @@ export const DisplayConditionModal = (props)=>{
182
221
  }
183
222
  }, []);
184
223
  const handleFormSelect = useCallback((formId)=>{
185
- if (!onConditionFormSelect || formFieldsByFormId[formId]) {
186
- return;
187
- }
188
- setLoadingFormIds((prev)=>prev.includes(formId) ? prev : [
189
- ...prev,
190
- formId
191
- ]);
192
- onConditionFormSelect(formId, (selectedFormId, fields)=>{
193
- setFormFieldsByFormId((prev)=>({
194
- ...prev,
195
- [selectedFormId]: fields
196
- }));
197
- setLoadingFormIds((prev)=>prev.filter((id)=>id !== selectedFormId));
198
- });
224
+ requestFormFields(formId);
199
225
  }, [
200
- formFieldsByFormId,
201
- onConditionFormSelect
226
+ requestFormFields
202
227
  ]);
203
228
  const canSave = useMemo(()=>{
204
229
  if (isFieldDataLoading) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/display-conditions/DisplayConditionModal.tsx"],"sourcesContent":["import { Button, Combobox, Dialog, Flex, Text } from '@servicetitan/anvil2';\nimport { useCallback, useEffect, useMemo, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport {\n buildFormFieldKey,\n FormFieldInfo,\n FormInfo,\n getConditionalFieldTypeFromFormItemType,\n parseFormFieldKey,\n} from '../shared/forms';\nimport { ConditionGroupsSection, MatchType } from './ConditionGroupsSection';\nimport { defaultState, MODAL_CONTENT_MAX_HEIGHT } from './constants';\nimport { DisplayConditionRequest, onDisplayCondition } from './displayConditionController';\nimport { buildUnlayerDisplayCondition, parseUnlayerDisplayCondition } from './nunjucks';\nimport { getSchemaDataPointOptions } from './schemaDataPoints';\nimport {\n ConditionGroup as ConditionGroupType,\n DisplayBehavior,\n DisplayConditionState,\n FormFieldOption,\n LogicalOperator,\n} from './types';\n\nconst BEHAVIOR_OPTIONS = [\n { label: 'Show', value: 'show' },\n { label: 'Hide', value: 'hide' },\n] as const;\nconst NUMERIC_VALUE_RE = /^-?(?:\\d+\\.?\\d*|\\.\\d+)$/;\ntype BehaviorOption = (typeof BEHAVIOR_OPTIONS)[number];\n\nfunction deriveMatchType(conditionState: DisplayConditionState): MatchType {\n const firstGroup = conditionState.groups[0];\n if (!firstGroup || firstGroup.conditions.length <= 1) {\n return 'all';\n }\n const hasOr = firstGroup.conditions.some(c => c.logicalOperator === 'or');\n return hasOr ? 'any' : 'all';\n}\n\nfunction applyMatchTypeToState(\n conditionState: DisplayConditionState,\n matchType: MatchType,\n): DisplayConditionState {\n const logicalOp: LogicalOperator = matchType === 'all' ? 'and' : 'or';\n return {\n ...conditionState,\n groups: conditionState.groups.map((group, groupIndex) => {\n if (groupIndex !== 0) {\n return group;\n }\n return {\n ...group,\n conditions: group.conditions.map((c, i) =>\n i === 0 ? c : { ...c, logicalOperator: logicalOp },\n ),\n };\n }),\n };\n}\n\nexport interface DisplayConditionModalProps {\n onConditionFormSelect?: (\n formId: number,\n sendFormFields: (formId: number, fields: FormFieldInfo[]) => void,\n ) => void;\n onConditionalsOpen?: (\n usedFormIds: number[],\n sendFormList: (forms: FormInfo[]) => void,\n sendFormFields: (formId: number, fields: FormFieldInfo[]) => void,\n ) => void;\n schema?: import('../shared/schema').SchemaObject;\n}\n\nexport const DisplayConditionModal = (props: DisplayConditionModalProps) => {\n const { onConditionFormSelect, onConditionalsOpen, schema } = props;\n const [request, setRequest] = useState<DisplayConditionRequest | null>(null);\n const [isOpen, setIsOpen] = useState(false);\n const [state, setState] = useState(defaultState);\n const [matchType, setMatchType] = useState<MatchType>('all');\n const [forms, setForms] = useState<FormInfo[]>([]);\n const [formFieldsByFormId, setFormFieldsByFormId] = useState<Record<number, FormFieldInfo[]>>(\n {},\n );\n const [loadingFormIds, setLoadingFormIds] = useState<number[]>([]);\n const [isFormsListLoading, setIsFormsListLoading] = useState(false);\n\n const dataPointOptions = useMemo(() => getSchemaDataPointOptions(schema), [schema]);\n const formFieldOptions = useMemo<FormFieldOption[]>(() => {\n const nextOptions: FormFieldOption[] = [];\n for (const form of forms) {\n const fields = formFieldsByFormId[form.id];\n if (!fields) {\n continue;\n }\n for (const field of fields) {\n const fieldType = getConditionalFieldTypeFromFormItemType(field.itemType);\n if (!fieldType) {\n continue;\n }\n nextOptions.push({\n fieldType,\n formId: form.id,\n fullKey: buildFormFieldKey(form.id, field.id),\n title: field.header,\n });\n }\n }\n return nextOptions.sort((left, right) => left.title.localeCompare(right.title));\n }, [forms, formFieldsByFormId]);\n const allFieldOptions = useMemo(\n () => [...dataPointOptions, ...formFieldOptions],\n [dataPointOptions, formFieldOptions],\n );\n const isFieldDataLoading = isFormsListLoading || loadingFormIds.length > 0;\n\n const portalTarget = useMemo(() => {\n if (typeof document === 'undefined') {\n return null;\n }\n return document.body;\n }, []);\n\n useEffect(() => {\n return onDisplayCondition(nextRequest => {\n setRequest(nextRequest);\n const existing = nextRequest.data;\n const parsed = parseUnlayerDisplayCondition(existing);\n const initialState = parsed ?? defaultState();\n const usedFormIds = Array.from(\n new Set(\n initialState.groups\n .flatMap(group => group.conditions)\n .map(condition => parseFormFieldKey(condition.dataPointKey)?.formId)\n .filter((formId): formId is number => formId != null),\n ),\n );\n\n setForms([]);\n setFormFieldsByFormId({});\n setIsFormsListLoading(!!onConditionalsOpen);\n setLoadingFormIds(usedFormIds);\n\n if (onConditionalsOpen) {\n onConditionalsOpen(\n usedFormIds,\n nextForms => {\n setForms(nextForms);\n setIsFormsListLoading(false);\n },\n (formId, fields) => {\n setFormFieldsByFormId(prev => ({ ...prev, [formId]: fields }));\n setLoadingFormIds(prev => prev.filter(id => id !== formId));\n },\n );\n } else {\n setIsFormsListLoading(false);\n setLoadingFormIds([]);\n }\n\n setState(initialState);\n setMatchType(deriveMatchType(initialState));\n setIsOpen(true);\n });\n }, [onConditionalsOpen]);\n\n const handleClose = useCallback(() => {\n setRequest(null);\n setIsOpen(false);\n }, []);\n\n const handleSave = useCallback(() => {\n if (!request) {\n return;\n }\n const finalState = applyMatchTypeToState(state, matchType);\n const condition = buildUnlayerDisplayCondition(finalState);\n if (condition) {\n request.done(condition);\n } else {\n request.done(null);\n }\n setRequest(null);\n setIsOpen(false);\n }, [matchType, request, state]);\n\n const updateGroup = useCallback((index: number, group: ConditionGroupType) => {\n setState(prev => {\n const next = [...prev.groups];\n next[index] = group;\n return { ...prev, groups: next };\n });\n }, []);\n\n const selectedBehavior = useMemo(\n () => BEHAVIOR_OPTIONS.find(opt => opt.value === state.behavior) ?? BEHAVIOR_OPTIONS[0],\n [state.behavior],\n );\n\n const handleBehaviorChange = useCallback((item: BehaviorOption | null) => {\n if (item) {\n setState(prev => ({ ...prev, behavior: item.value as DisplayBehavior }));\n }\n }, []);\n\n const handleFormSelect = useCallback(\n (formId: number) => {\n if (!onConditionFormSelect || formFieldsByFormId[formId]) {\n return;\n }\n setLoadingFormIds(prev => (prev.includes(formId) ? prev : [...prev, formId]));\n onConditionFormSelect(formId, (selectedFormId, fields) => {\n setFormFieldsByFormId(prev => ({ ...prev, [selectedFormId]: fields }));\n setLoadingFormIds(prev => prev.filter(id => id !== selectedFormId));\n });\n },\n [formFieldsByFormId, onConditionFormSelect],\n );\n\n const canSave = useMemo(() => {\n if (isFieldDataLoading) {\n return false;\n }\n for (const group of state.groups) {\n for (const condition of group.conditions) {\n if (!condition.dataPointKey) {\n return false;\n }\n if (condition.operator === 'is_empty' || condition.operator === 'is_not_empty') {\n continue;\n }\n const trimmedValue = condition.value.trim();\n if (!trimmedValue) {\n return false;\n }\n const fieldType =\n allFieldOptions.find(opt => opt.fullKey === condition.dataPointKey)\n ?.fieldType ?? 'string';\n if (fieldType === 'number' && !NUMERIC_VALUE_RE.test(trimmedValue)) {\n return false;\n }\n }\n }\n return state.groups.length > 0;\n }, [allFieldOptions, isFieldDataLoading, state.groups]);\n\n if (!portalTarget || !isOpen) {\n return null;\n }\n\n return createPortal(\n <Dialog open={isOpen} onClose={handleClose} size=\"xlarge\">\n <Dialog.Header>Conditions</Dialog.Header>\n <Dialog.Content>\n <Flex\n direction=\"column\"\n gap=\"4\"\n style={{\n maxHeight: MODAL_CONTENT_MAX_HEIGHT,\n overflowY: 'auto',\n paddingRight: 8,\n width: '100%',\n }}\n >\n <ConditionGroupsSection\n behaviorSection={\n <Flex\n direction=\"row\"\n alignItems=\"center\"\n gap=\"2\"\n style={{ flexWrap: 'wrap' }}\n >\n <Text size=\"small\" subdued variant=\"body\">\n Select to include or exclude selected template/file.\n </Text>\n <div style={{ width: 90 }}>\n <Combobox.Select\n {...({ disableClearSelection: true } as object)}\n itemToKey={(item: BehaviorOption | null) =>\n item?.value ?? ''\n }\n itemToString={(item: BehaviorOption | null) =>\n item?.label ?? ''\n }\n items={[...BEHAVIOR_OPTIONS]}\n selectedItem={selectedBehavior}\n onChange={handleBehaviorChange}\n >\n <Combobox.SelectTrigger\n label=\"\"\n placeholder=\"Select...\"\n size=\"small\"\n />\n <Combobox.Content>\n {({ items }: { items: BehaviorOption[] }) => (\n <Combobox.List>\n {items.map((item, i) => (\n <Combobox.Item\n index={i}\n item={item}\n key={item.value}\n >\n {item.label}\n </Combobox.Item>\n ))}\n </Combobox.List>\n )}\n </Combobox.Content>\n </Combobox.Select>\n </div>\n </Flex>\n }\n dataPointOptions={dataPointOptions}\n formFieldOptions={formFieldOptions}\n forms={forms}\n groups={state.groups}\n matchType={matchType}\n onFormSelect={handleFormSelect}\n onMatchTypeChange={setMatchType}\n onUpdateGroup={updateGroup}\n />\n </Flex>\n </Dialog.Content>\n <Dialog.Footer sticky>\n <Flex\n justifyContent=\"flex-end\"\n alignItems=\"center\"\n gap=\"3\"\n style={{ width: '100%' }}\n >\n <Button appearance=\"secondary\" onClick={handleClose}>\n Cancel\n </Button>\n <Button appearance=\"primary\" disabled={!canSave} onClick={handleSave}>\n Save Changes\n </Button>\n </Flex>\n </Dialog.Footer>\n </Dialog>,\n portalTarget,\n );\n};\n"],"names":["Button","Combobox","Dialog","Flex","Text","useCallback","useEffect","useMemo","useState","createPortal","buildFormFieldKey","getConditionalFieldTypeFromFormItemType","parseFormFieldKey","ConditionGroupsSection","defaultState","MODAL_CONTENT_MAX_HEIGHT","onDisplayCondition","buildUnlayerDisplayCondition","parseUnlayerDisplayCondition","getSchemaDataPointOptions","BEHAVIOR_OPTIONS","label","value","NUMERIC_VALUE_RE","deriveMatchType","conditionState","firstGroup","groups","conditions","length","hasOr","some","c","logicalOperator","applyMatchTypeToState","matchType","logicalOp","map","group","groupIndex","i","DisplayConditionModal","props","onConditionFormSelect","onConditionalsOpen","schema","request","setRequest","isOpen","setIsOpen","state","setState","setMatchType","forms","setForms","formFieldsByFormId","setFormFieldsByFormId","loadingFormIds","setLoadingFormIds","isFormsListLoading","setIsFormsListLoading","dataPointOptions","formFieldOptions","nextOptions","form","fields","id","field","fieldType","itemType","push","formId","fullKey","title","header","sort","left","right","localeCompare","allFieldOptions","isFieldDataLoading","portalTarget","document","body","nextRequest","existing","data","parsed","initialState","usedFormIds","Array","from","Set","flatMap","condition","dataPointKey","filter","nextForms","prev","handleClose","handleSave","finalState","done","updateGroup","index","next","selectedBehavior","find","opt","behavior","handleBehaviorChange","item","handleFormSelect","includes","selectedFormId","canSave","operator","trimmedValue","trim","test","open","onClose","size","Header","Content","direction","gap","style","maxHeight","overflowY","paddingRight","width","behaviorSection","alignItems","flexWrap","subdued","variant","div","Select","disableClearSelection","itemToKey","itemToString","items","selectedItem","onChange","SelectTrigger","placeholder","List","Item","onFormSelect","onMatchTypeChange","onUpdateGroup","Footer","sticky","justifyContent","appearance","onClick","disabled"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/display-conditions/DisplayConditionModal.tsx"],"sourcesContent":["import { Button, Combobox, Dialog, Flex, Text } from '@servicetitan/anvil2';\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport {\n buildFormFieldKey,\n FormFieldInfo,\n FormInfo,\n getConditionalFieldTypeFromFormItemType,\n parseFormFieldKey,\n} from '../shared/forms';\nimport { ConditionGroupsSection, MatchType } from './ConditionGroupsSection';\nimport { defaultState, MODAL_CONTENT_MAX_HEIGHT } from './constants';\nimport { DisplayConditionRequest, onDisplayCondition } from './displayConditionController';\nimport { buildUnlayerDisplayCondition, parseUnlayerDisplayCondition } from './nunjucks';\nimport { getSchemaDataPointOptions } from './schemaDataPoints';\nimport {\n ConditionGroup as ConditionGroupType,\n DisplayBehavior,\n DisplayConditionState,\n FormFieldOption,\n LogicalOperator,\n} from './types';\n\nconst BEHAVIOR_OPTIONS = [\n { label: 'Show', value: 'show' },\n { label: 'Hide', value: 'hide' },\n] as const;\nconst NUMERIC_VALUE_RE = /^-?(?:\\d+\\.?\\d*|\\.\\d+)$/;\ntype BehaviorOption = (typeof BEHAVIOR_OPTIONS)[number];\n\nfunction deriveMatchType(conditionState: DisplayConditionState): MatchType {\n const firstGroup = conditionState.groups[0];\n if (!firstGroup || firstGroup.conditions.length <= 1) {\n return 'all';\n }\n const hasOr = firstGroup.conditions.some(c => c.logicalOperator === 'or');\n return hasOr ? 'any' : 'all';\n}\n\nfunction applyMatchTypeToState(\n conditionState: DisplayConditionState,\n matchType: MatchType,\n): DisplayConditionState {\n const logicalOp: LogicalOperator = matchType === 'all' ? 'and' : 'or';\n return {\n ...conditionState,\n groups: conditionState.groups.map((group, groupIndex) => {\n if (groupIndex !== 0) {\n return group;\n }\n return {\n ...group,\n conditions: group.conditions.map((c, i) =>\n i === 0 ? c : { ...c, logicalOperator: logicalOp },\n ),\n };\n }),\n };\n}\n\nexport interface DisplayConditionModalProps {\n onConditionFormSelect?: (\n formId: number,\n sendFormFields: (formId: number, fields: FormFieldInfo[]) => void,\n ) => void;\n onConditionalsOpen?: (\n usedFormIds: number[],\n sendFormList: (forms: FormInfo[]) => void,\n sendFormFields: (formId: number, fields: FormFieldInfo[]) => void,\n ) => void;\n schema?: import('../shared/schema').SchemaObject;\n}\n\nexport const DisplayConditionModal = (props: DisplayConditionModalProps) => {\n const { onConditionFormSelect, onConditionalsOpen, schema } = props;\n const [request, setRequest] = useState<DisplayConditionRequest | null>(null);\n const [isOpen, setIsOpen] = useState(false);\n const [state, setState] = useState(defaultState);\n const [matchType, setMatchType] = useState<MatchType>('all');\n const [forms, setForms] = useState<FormInfo[]>([]);\n const [formFieldsByFormId, setFormFieldsByFormId] = useState<Record<number, FormFieldInfo[]>>(\n {},\n );\n const [loadingFormIds, setLoadingFormIds] = useState<number[]>([]);\n const [isFormsListLoading, setIsFormsListLoading] = useState(false);\n const formFieldsByFormIdRef = useRef<Record<number, FormFieldInfo[]>>({});\n const loadingFormIdsRef = useRef<number[]>([]);\n\n const dataPointOptions = useMemo(() => getSchemaDataPointOptions(schema), [schema]);\n const formFieldOptions = useMemo<FormFieldOption[]>(() => {\n const nextOptions: FormFieldOption[] = [];\n for (const form of forms) {\n const fields = formFieldsByFormId[form.id];\n if (!fields) {\n continue;\n }\n for (const field of fields) {\n const fieldType = getConditionalFieldTypeFromFormItemType(field.itemType);\n if (!fieldType) {\n continue;\n }\n nextOptions.push({\n fieldType,\n formId: form.id,\n fullKey: buildFormFieldKey(form.id, field.id),\n title: field.header,\n });\n }\n }\n return nextOptions.sort((left, right) => left.title.localeCompare(right.title));\n }, [forms, formFieldsByFormId]);\n const allFieldOptions = useMemo(\n () => [...dataPointOptions, ...formFieldOptions],\n [dataPointOptions, formFieldOptions],\n );\n const isFieldDataLoading = isFormsListLoading || loadingFormIds.length > 0;\n\n useEffect(() => {\n formFieldsByFormIdRef.current = formFieldsByFormId;\n }, [formFieldsByFormId]);\n\n useEffect(() => {\n loadingFormIdsRef.current = loadingFormIds;\n }, [loadingFormIds]);\n\n const requestFormFields = useCallback(\n (formId: number) => {\n if (!onConditionFormSelect) {\n return;\n }\n if (\n formFieldsByFormIdRef.current[formId] ||\n loadingFormIdsRef.current.includes(formId)\n ) {\n return;\n }\n\n setLoadingFormIds(prev => (prev.includes(formId) ? prev : [...prev, formId]));\n onConditionFormSelect(formId, (selectedFormId, fields) => {\n setFormFieldsByFormId(prev => ({ ...prev, [selectedFormId]: fields }));\n setLoadingFormIds(prev => prev.filter(id => id !== selectedFormId));\n });\n },\n [onConditionFormSelect],\n );\n\n const portalTarget = useMemo(() => {\n if (typeof document === 'undefined') {\n return null;\n }\n return document.body;\n }, []);\n\n useEffect(() => {\n return onDisplayCondition(nextRequest => {\n setRequest(nextRequest);\n const existing = nextRequest.data;\n const parsed = parseUnlayerDisplayCondition(existing);\n const initialState = parsed ?? defaultState();\n const usedFormIds = Array.from(\n new Set(\n initialState.groups\n .flatMap(group => group.conditions)\n .map(condition => parseFormFieldKey(condition.dataPointKey)?.formId)\n .filter((formId): formId is number => formId != null),\n ),\n );\n\n setForms([]);\n setFormFieldsByFormId({});\n setIsFormsListLoading(!!onConditionalsOpen);\n setLoadingFormIds([]);\n\n if (onConditionalsOpen) {\n onConditionalsOpen(\n usedFormIds,\n nextForms => {\n setForms(nextForms);\n setIsFormsListLoading(false);\n },\n (formId, fields) => {\n setFormFieldsByFormId(prev => ({ ...prev, [formId]: fields }));\n setLoadingFormIds(prev => prev.filter(id => id !== formId));\n },\n );\n\n /*\n * Keep display-condition flow aligned with calculated-field flow:\n * when modal opens, request fields for already-used forms so labels\n * are available in edit mode.\n */\n usedFormIds.forEach(formId => requestFormFields(formId));\n } else {\n setIsFormsListLoading(false);\n setLoadingFormIds([]);\n }\n\n setState(initialState);\n setMatchType(deriveMatchType(initialState));\n setIsOpen(true);\n });\n }, [onConditionalsOpen, requestFormFields]);\n\n const handleClose = useCallback(() => {\n setRequest(null);\n setIsOpen(false);\n }, []);\n\n const handleSave = useCallback(() => {\n if (!request) {\n return;\n }\n const finalState = applyMatchTypeToState(state, matchType);\n const condition = buildUnlayerDisplayCondition(finalState);\n if (condition) {\n request.done(condition);\n } else {\n request.done(null);\n }\n setRequest(null);\n setIsOpen(false);\n }, [matchType, request, state]);\n\n const updateGroup = useCallback((index: number, group: ConditionGroupType) => {\n setState(prev => {\n const next = [...prev.groups];\n next[index] = group;\n return { ...prev, groups: next };\n });\n }, []);\n\n const selectedBehavior = useMemo(\n () => BEHAVIOR_OPTIONS.find(opt => opt.value === state.behavior) ?? BEHAVIOR_OPTIONS[0],\n [state.behavior],\n );\n\n const handleBehaviorChange = useCallback((item: BehaviorOption | null) => {\n if (item) {\n setState(prev => ({ ...prev, behavior: item.value as DisplayBehavior }));\n }\n }, []);\n\n const handleFormSelect = useCallback(\n (formId: number) => {\n requestFormFields(formId);\n },\n [requestFormFields],\n );\n\n const canSave = useMemo(() => {\n if (isFieldDataLoading) {\n return false;\n }\n for (const group of state.groups) {\n for (const condition of group.conditions) {\n if (!condition.dataPointKey) {\n return false;\n }\n if (condition.operator === 'is_empty' || condition.operator === 'is_not_empty') {\n continue;\n }\n const trimmedValue = condition.value.trim();\n if (!trimmedValue) {\n return false;\n }\n const fieldType =\n allFieldOptions.find(opt => opt.fullKey === condition.dataPointKey)\n ?.fieldType ?? 'string';\n if (fieldType === 'number' && !NUMERIC_VALUE_RE.test(trimmedValue)) {\n return false;\n }\n }\n }\n return state.groups.length > 0;\n }, [allFieldOptions, isFieldDataLoading, state.groups]);\n\n if (!portalTarget || !isOpen) {\n return null;\n }\n\n return createPortal(\n <Dialog open={isOpen} onClose={handleClose} size=\"xlarge\">\n <Dialog.Header>Conditions</Dialog.Header>\n <Dialog.Content>\n <Flex\n direction=\"column\"\n gap=\"4\"\n style={{\n maxHeight: MODAL_CONTENT_MAX_HEIGHT,\n overflowY: 'auto',\n paddingRight: 8,\n width: '100%',\n }}\n >\n <ConditionGroupsSection\n behaviorSection={\n <Flex\n direction=\"row\"\n alignItems=\"center\"\n gap=\"2\"\n style={{ flexWrap: 'wrap' }}\n >\n <Text size=\"small\" subdued variant=\"body\">\n Select to include or exclude selected template/file.\n </Text>\n <div style={{ width: 90 }}>\n <Combobox.Select\n {...({ disableClearSelection: true } as object)}\n itemToKey={(item: BehaviorOption | null) =>\n item?.value ?? ''\n }\n itemToString={(item: BehaviorOption | null) =>\n item?.label ?? ''\n }\n items={[...BEHAVIOR_OPTIONS]}\n selectedItem={selectedBehavior}\n onChange={handleBehaviorChange}\n >\n <Combobox.SelectTrigger\n label=\"\"\n placeholder=\"Select...\"\n size=\"small\"\n />\n <Combobox.Content>\n {({ items }: { items: BehaviorOption[] }) => (\n <Combobox.List>\n {items.map((item, i) => (\n <Combobox.Item\n index={i}\n item={item}\n key={item.value}\n >\n {item.label}\n </Combobox.Item>\n ))}\n </Combobox.List>\n )}\n </Combobox.Content>\n </Combobox.Select>\n </div>\n </Flex>\n }\n dataPointOptions={dataPointOptions}\n formFieldOptions={formFieldOptions}\n forms={forms}\n groups={state.groups}\n matchType={matchType}\n onFormSelect={handleFormSelect}\n onMatchTypeChange={setMatchType}\n onUpdateGroup={updateGroup}\n />\n </Flex>\n </Dialog.Content>\n <Dialog.Footer sticky>\n <Flex\n justifyContent=\"flex-end\"\n alignItems=\"center\"\n gap=\"3\"\n style={{ width: '100%' }}\n >\n <Button appearance=\"secondary\" onClick={handleClose}>\n Cancel\n </Button>\n <Button appearance=\"primary\" disabled={!canSave} onClick={handleSave}>\n Save Changes\n </Button>\n </Flex>\n </Dialog.Footer>\n </Dialog>,\n portalTarget,\n );\n};\n"],"names":["Button","Combobox","Dialog","Flex","Text","useCallback","useEffect","useMemo","useRef","useState","createPortal","buildFormFieldKey","getConditionalFieldTypeFromFormItemType","parseFormFieldKey","ConditionGroupsSection","defaultState","MODAL_CONTENT_MAX_HEIGHT","onDisplayCondition","buildUnlayerDisplayCondition","parseUnlayerDisplayCondition","getSchemaDataPointOptions","BEHAVIOR_OPTIONS","label","value","NUMERIC_VALUE_RE","deriveMatchType","conditionState","firstGroup","groups","conditions","length","hasOr","some","c","logicalOperator","applyMatchTypeToState","matchType","logicalOp","map","group","groupIndex","i","DisplayConditionModal","props","onConditionFormSelect","onConditionalsOpen","schema","request","setRequest","isOpen","setIsOpen","state","setState","setMatchType","forms","setForms","formFieldsByFormId","setFormFieldsByFormId","loadingFormIds","setLoadingFormIds","isFormsListLoading","setIsFormsListLoading","formFieldsByFormIdRef","loadingFormIdsRef","dataPointOptions","formFieldOptions","nextOptions","form","fields","id","field","fieldType","itemType","push","formId","fullKey","title","header","sort","left","right","localeCompare","allFieldOptions","isFieldDataLoading","current","requestFormFields","includes","prev","selectedFormId","filter","portalTarget","document","body","nextRequest","existing","data","parsed","initialState","usedFormIds","Array","from","Set","flatMap","condition","dataPointKey","nextForms","forEach","handleClose","handleSave","finalState","done","updateGroup","index","next","selectedBehavior","find","opt","behavior","handleBehaviorChange","item","handleFormSelect","canSave","operator","trimmedValue","trim","test","open","onClose","size","Header","Content","direction","gap","style","maxHeight","overflowY","paddingRight","width","behaviorSection","alignItems","flexWrap","subdued","variant","div","Select","disableClearSelection","itemToKey","itemToString","items","selectedItem","onChange","SelectTrigger","placeholder","List","Item","onFormSelect","onMatchTypeChange","onUpdateGroup","Footer","sticky","justifyContent","appearance","onClick","disabled"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"nunjucks.d.ts","sourceRoot":"","sources":["../../src/display-conditions/nunjucks.ts"],"names":[],"mappings":"AAEA,OAAO,EAKH,qBAAqB,EAGrB,uBAAuB,EAE1B,MAAM,SAAS,CAAC;AA4KjB,wBAAgB,4BAA4B,CACxC,KAAK,EAAE,qBAAqB,GAC7B,uBAAuB,GAAG,IAAI,CAUhC;AAoRD;;;GAGG;AACH,wBAAgB,4BAA4B,CACxC,SAAS,EAAE,uBAAuB,GAAG,IAAI,GAAG,SAAS,GACtD,qBAAqB,GAAG,IAAI,CA4C9B"}
1
+ {"version":3,"file":"nunjucks.d.ts","sourceRoot":"","sources":["../../src/display-conditions/nunjucks.ts"],"names":[],"mappings":"AAEA,OAAO,EAIH,qBAAqB,EAGrB,uBAAuB,EAE1B,MAAM,SAAS,CAAC;AAsKjB,wBAAgB,4BAA4B,CACxC,KAAK,EAAE,qBAAqB,GAC7B,uBAAuB,GAAG,IAAI,CAShC;AAoRD;;;GAGG;AACH,wBAAgB,4BAA4B,CACxC,SAAS,EAAE,uBAAuB,GAAG,IAAI,GAAG,SAAS,GACtD,qBAAqB,GAAG,IAAI,CA4C9B"}
@@ -1,10 +1,6 @@
1
1
  import { buildFormFieldKey, parseFormFieldKey, toNunjucksFieldReference } from '../shared/forms';
2
2
  import { generateId } from './constants';
3
- import { CONDITION_OPERATORS, VALUE_LESS_OPERATORS } from './types';
4
- const OPERATOR_LABEL_MAP = new Map(CONDITION_OPERATORS.map((op)=>[
5
- op.value,
6
- op.label
7
- ]));
3
+ import { VALUE_LESS_OPERATORS } from './types';
8
4
  function escapeNunjucksString(s) {
9
5
  return s.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
10
6
  }
@@ -118,30 +114,14 @@ function isValueLessOperator(operator) {
118
114
  }
119
115
  return combined;
120
116
  }
121
- function operatorLabel(operator) {
122
- var _OPERATOR_LABEL_MAP_get;
123
- return (_OPERATOR_LABEL_MAP_get = OPERATOR_LABEL_MAP.get(operator)) !== null && _OPERATOR_LABEL_MAP_get !== void 0 ? _OPERATOR_LABEL_MAP_get : operator;
124
- }
125
- function generateTypeAndLabel(state) {
126
- const parts = [];
127
- for (const group of state.groups){
128
- for (const c of group.conditions){
129
- if (!c.dataPointKey) {
130
- continue;
131
- }
132
- if (isValueLessOperator(c.operator)) {
133
- parts.push(`${c.dataPointKey} ${operatorLabel(c.operator)}`);
134
- } else if (c.value.trim()) {
135
- parts.push(`${c.dataPointKey} ${operatorLabel(c.operator)} ${c.value.trim()}`);
136
- }
137
- }
138
- }
139
- const summary = parts.slice(0, 2).join('; ') + (parts.length > 2 ? '…' : '');
140
- const type = state.behavior === 'show' ? 'Display when' : 'Hide when';
141
- const label = summary || (state.behavior === 'show' ? 'Show when' : 'Hide when');
117
+ function generateConditionText(state) {
118
+ const conditionCount = state.groups.reduce((count, group)=>count + group.conditions.filter((c)=>c.dataPointKey && (isValueLessOperator(c.operator) || c.value.trim() !== '')).length, 0);
119
+ const label = state.behavior === 'show' ? 'Show condition configured' : 'Hide condition configured';
120
+ const description = conditionCount === 1 ? '1 condition is currently applied' : `${conditionCount} conditions are currently applied`;
142
121
  return {
143
- type,
144
- label
122
+ description,
123
+ label,
124
+ type: ''
145
125
  };
146
126
  }
147
127
  export function buildUnlayerDisplayCondition(state) {
@@ -151,8 +131,7 @@ export function buildUnlayerDisplayCondition(state) {
151
131
  }
152
132
  const before = `{% if ${expr} %}`;
153
133
  const after = '{% endif %}';
154
- const { label, type } = generateTypeAndLabel(state);
155
- const description = state.groups.length > 1 ? `${state.groups.length} rules` : undefined;
134
+ const { description, label, type } = generateConditionText(state);
156
135
  return {
157
136
  after,
158
137
  before,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/display-conditions/nunjucks.ts"],"sourcesContent":["import { buildFormFieldKey, parseFormFieldKey, toNunjucksFieldReference } from '../shared/forms';\nimport { generateId } from './constants';\nimport {\n CONDITION_OPERATORS,\n ConditionGroup,\n ConditionOperator,\n DisplayBehavior,\n DisplayConditionState,\n LogicalOperator,\n SingleCondition,\n UnlayerDisplayCondition,\n VALUE_LESS_OPERATORS,\n} from './types';\n\nconst OPERATOR_LABEL_MAP = new Map<string, string>(\n CONDITION_OPERATORS.map(op => [op.value, op.label]),\n);\n\nfunction escapeNunjucksString(s: string): string {\n return s.replace(/\\\\/g, '\\\\\\\\').replace(/'/g, \"\\\\'\");\n}\n\nfunction nunjucksValueLiteral(value: string): string {\n return `'${escapeNunjucksString(value)}'`;\n}\n\nfunction isEscapedAt(s: string, index: number): boolean {\n let slashCount = 0;\n for (let i = index - 1; i >= 0 && s[i] === '\\\\'; i--) {\n slashCount++;\n }\n return slashCount % 2 === 1;\n}\n\nfunction isValueLessOperator(operator: string): boolean {\n return VALUE_LESS_OPERATORS.includes(operator as ConditionOperator);\n}\n\n/**\n * Build a single condition expression in Nunjucks.\n * Wrapped in () so splitting never breaks composite expressions.\n */\nfunction buildSingleConditionExpression(\n dataPointKey: string,\n operator: string,\n value: string,\n): string {\n const path = toNunjucksFieldReference(dataPointKey);\n const defaulted = `(${path} | default(''))`;\n const numDefaulted = `(${path} | default(0))`;\n const normalizedValue = value.trim();\n const literal = normalizedValue ? nunjucksValueLiteral(normalizedValue) : \"''\";\n const numLiteral =\n /^-?(?:\\d+\\.?\\d*|\\.\\d+)$/.test(normalizedValue) && normalizedValue !== '-'\n ? normalizedValue\n : '0';\n let inner: string;\n\n switch (operator) {\n case 'is_equal_to':\n inner = `${defaulted} == ${literal}`;\n break;\n case 'is_not_equal_to':\n inner = `${defaulted} != ${literal}`;\n break;\n case 'contains':\n inner = `${literal} in ${defaulted}`;\n break;\n case 'does_not_contain':\n inner = `not (${literal} in ${defaulted})`;\n break;\n case 'starts_with':\n inner = `(${defaulted} | startswith(${literal}))`;\n break;\n case 'ends_with':\n inner = `(${defaulted} | endswith(${literal}))`;\n break;\n case 'is_empty':\n inner = `${defaulted} == '' or (${defaulted} | length) == 0`;\n break;\n case 'is_not_empty':\n inner = `${defaulted} != '' and (${defaulted} | length) > 0`;\n break;\n case 'num_eq':\n inner = `${numDefaulted} == ${numLiteral}`;\n break;\n case 'num_neq':\n inner = `${numDefaulted} != ${numLiteral}`;\n break;\n case 'num_gt':\n inner = `${numDefaulted} > ${numLiteral}`;\n break;\n case 'num_lt':\n inner = `${numDefaulted} < ${numLiteral}`;\n break;\n case 'num_gte':\n inner = `${numDefaulted} >= ${numLiteral}`;\n break;\n case 'num_lte':\n inner = `${numDefaulted} <= ${numLiteral}`;\n break;\n default:\n inner = `${defaulted} == ${literal}`;\n }\n return `(${inner})`;\n}\n\n/**\n * Build group expression. Each condition is connected by its own logical operator.\n * E.g. (cond1) and (cond2) or (cond3)\n */\nfunction buildGroupExpression(group: ConditionGroup): string {\n const valid = group.conditions.filter(\n c => c.dataPointKey && (isValueLessOperator(c.operator) || c.value.trim() !== ''),\n );\n if (valid.length === 0) {\n return '';\n }\n let result = buildSingleConditionExpression(\n valid[0].dataPointKey,\n valid[0].operator,\n valid[0].value,\n );\n for (let i = 1; i < valid.length; i++) {\n const joiner = valid[i].logicalOperator === 'or' ? ' or ' : ' and ';\n result +=\n joiner +\n buildSingleConditionExpression(\n valid[i].dataPointKey,\n valid[i].operator,\n valid[i].value,\n );\n }\n // Wrap group in parens so top-level rule AND split works cleanly\n return valid.length > 1 ? `(${result})` : result;\n}\n\n/**\n * Build full condition expression.\n * Rules (groups) are connected by each group's logical operator.\n * hide behavior wraps everything in not().\n */\nfunction buildFullConditionExpression(state: DisplayConditionState): string {\n const validGroups = state.groups\n .map(group => ({ expression: buildGroupExpression(group), group }))\n .filter(item => !!item.expression);\n if (validGroups.length === 0) {\n return '';\n }\n let combined = validGroups[0].expression;\n for (let i = 1; i < validGroups.length; i++) {\n const joiner = validGroups[i].group.logicalOperator === 'or' ? ' or ' : ' and ';\n combined += `${joiner}${validGroups[i].expression}`;\n }\n if (state.behavior === 'hide') {\n return `not (${combined})`;\n }\n return combined;\n}\n\nfunction operatorLabel(operator: string): string {\n return OPERATOR_LABEL_MAP.get(operator) ?? operator;\n}\n\nfunction generateTypeAndLabel(state: DisplayConditionState): { type: string; label: string } {\n const parts: string[] = [];\n for (const group of state.groups) {\n for (const c of group.conditions) {\n if (!c.dataPointKey) {\n continue;\n }\n if (isValueLessOperator(c.operator)) {\n parts.push(`${c.dataPointKey} ${operatorLabel(c.operator)}`);\n } else if (c.value.trim()) {\n parts.push(`${c.dataPointKey} ${operatorLabel(c.operator)} ${c.value.trim()}`);\n }\n }\n }\n const summary = parts.slice(0, 2).join('; ') + (parts.length > 2 ? '…' : '');\n const type = state.behavior === 'show' ? 'Display when' : 'Hide when';\n const label = summary || (state.behavior === 'show' ? 'Show when' : 'Hide when');\n return { type, label };\n}\n\nexport function buildUnlayerDisplayCondition(\n state: DisplayConditionState,\n): UnlayerDisplayCondition | null {\n const expr = buildFullConditionExpression(state);\n if (!expr) {\n return null;\n }\n const before = `{% if ${expr} %}`;\n const after = '{% endif %}';\n const { label, type } = generateTypeAndLabel(state);\n const description = state.groups.length > 1 ? `${state.groups.length} rules` : undefined;\n return { after, before, description, label, type };\n}\n\n/*\n * ---------------------------------------------------------------------------\n * Parse helpers\n * ---------------------------------------------------------------------------\n */\n\nfunction stripOuterParens(s: string): string {\n const t = s.trim();\n if (t.length < 2 || !t.startsWith('(') || t.at(-1) !== ')') {\n return t;\n }\n let depth = 1;\n for (let i = 1; i < t.length - 1; i++) {\n if (t[i] === '(') {\n depth++;\n } else if (t[i] === ')') {\n depth--;\n if (depth === 0) {\n return t;\n }\n }\n }\n return depth === 1 ? t.slice(1, -1).trim() : t;\n}\n\n/**\n * Tokenize an expression into alternating tokens of (expr, operator).\n * Returns: [expr, 'and'|'or', expr, 'and'|'or', expr, ...]\n *\n * This scans for top-level \" and \" / \" or \" while respecting parens,\n * and records which separator was found between each pair.\n */\nfunction tokenizeGroupExpression(expr: string): {\n conditionExprs: string[];\n operators: LogicalOperator[];\n} {\n const conditionExprs: string[] = [];\n const operators: LogicalOperator[] = [];\n let depth = 0;\n let inSingleQuote = false;\n let start = 0;\n\n for (let i = 0; i < expr.length; i++) {\n const c = expr[i];\n if (c === \"'\" && !isEscapedAt(expr, i)) {\n inSingleQuote = !inSingleQuote;\n } else if (!inSingleQuote && c === '(') {\n depth++;\n } else if (!inSingleQuote && c === ')') {\n depth = Math.max(0, depth - 1);\n } else if (!inSingleQuote && depth === 0) {\n // Check for \" and \" or \" or \" at this position\n if (expr.slice(i, i + 5) === ' and ') {\n conditionExprs.push(expr.slice(start, i).trim());\n operators.push('and');\n start = i + 5;\n i += 4;\n continue;\n }\n if (expr.slice(i, i + 4) === ' or ') {\n conditionExprs.push(expr.slice(start, i).trim());\n operators.push('or');\n start = i + 4;\n i += 3;\n continue;\n }\n }\n }\n conditionExprs.push(expr.slice(start).trim());\n return { conditionExprs, operators };\n}\n\nfunction unescapeNunjucksString(s: string): string {\n return s.replaceAll('\\\\\\\\', '\\\\').replaceAll(String.raw`\\'`, \"'\");\n}\n\nconst SIMPLE_DATA_POINT_PATH = String.raw`[\\w.]+`;\nconst FORM_RUNTIME_DATA_POINT_PATH = String.raw`__submission_fields\\[\"\\d+\"\\]\\[\"[A-Za-z0-9]+\"\\]`;\nconst DATA_POINT_PATH = String.raw`(${SIMPLE_DATA_POINT_PATH}|${FORM_RUNTIME_DATA_POINT_PATH})`;\nconst RE_STR_DEFAULT = String.raw`\\(${DATA_POINT_PATH}\\s*\\|\\s*default\\s*\\(\\s*''\\s*\\)\\)`;\nconst RE_NUM_DEFAULT = String.raw`\\(${DATA_POINT_PATH}\\s*\\|\\s*default\\s*\\(\\s*0\\s*\\)\\)`;\nconst QUOTED_CONTENT = String.raw`((?:[^'\\\\]|\\\\.)*)`;\n\nfunction toSingleCondition(\n operator: ConditionOperator,\n dataPointKey: string,\n value: string,\n logicalOp?: LogicalOperator,\n): SingleCondition {\n const normalizedDataPointKey = (() => {\n const parsed = parseFormFieldKey(dataPointKey.trim());\n if (!parsed) {\n return dataPointKey.trim();\n }\n return buildFormFieldKey(parsed.formId, parsed.fieldId);\n })();\n const condition: SingleCondition = {\n dataPointKey: normalizedDataPointKey,\n id: generateId(),\n operator,\n value,\n };\n if (logicalOp) {\n condition.logicalOperator = logicalOp;\n }\n return condition;\n}\n\nfunction parseQuotedValue(s: string): { value: string; rest: string } | null {\n if (!s.startsWith(\"'\")) {\n return null;\n }\n let i = 1;\n let value = '';\n while (i < s.length) {\n if (s[i] === '\\\\' && i + 1 < s.length) {\n value += s[i + 1] === \"'\" ? \"'\" : s[i + 1];\n i += 2;\n continue;\n }\n if (s[i] === \"'\") {\n return { rest: s.slice(i + 1).trim(), value: unescapeNunjucksString(value) };\n }\n value += s[i];\n i++;\n }\n return null;\n}\n\ninterface ParsePattern {\n getValue: (match: RegExpMatchArray) => string | null;\n operator: ConditionOperator;\n pathGroup: number;\n pattern: RegExp;\n}\n\nconst PARSE_PATTERNS: ParsePattern[] = [\n // value-less\n {\n getValue: () => '',\n operator: 'is_empty',\n pathGroup: 1,\n pattern: new RegExp(\n String.raw`^${RE_STR_DEFAULT}\\s*==\\s*''\\s+or\\s+\\(\\(${DATA_POINT_PATH}\\s*\\|\\s*default\\s*\\(\\s*''\\s*\\)\\)\\s*\\|\\s*length\\)\\s*==\\s*0$`,\n ),\n },\n {\n getValue: () => '',\n operator: 'is_not_empty',\n pathGroup: 1,\n pattern: new RegExp(\n String.raw`^${RE_STR_DEFAULT}\\s*!=\\s*''\\s+and\\s+\\(\\(${DATA_POINT_PATH}\\s*\\|\\s*default\\s*\\(\\s*''\\s*\\)\\)\\s*\\|\\s*length\\)\\s*>\\s*0$`,\n ),\n },\n // string with value\n {\n getValue: m => parseQuotedValue(m[2].trim())?.value ?? null,\n operator: 'is_equal_to',\n pathGroup: 1,\n pattern: new RegExp(String.raw`^${RE_STR_DEFAULT}\\s*==\\s+(.+)$`),\n },\n {\n getValue: m => parseQuotedValue(m[2].trim())?.value ?? null,\n operator: 'is_not_equal_to',\n pathGroup: 1,\n pattern: new RegExp(String.raw`^${RE_STR_DEFAULT}\\s*!=\\s+(.+)$`),\n },\n {\n getValue: m => unescapeNunjucksString(m[1]),\n operator: 'contains',\n pathGroup: 2,\n pattern: new RegExp(String.raw`^'${QUOTED_CONTENT}'\\s+in\\s+${RE_STR_DEFAULT}$`),\n },\n {\n getValue: m => unescapeNunjucksString(m[1]),\n operator: 'does_not_contain',\n pathGroup: 2,\n pattern: new RegExp(\n String.raw`^not\\s*\\(\\s*'${QUOTED_CONTENT}'\\s+in\\s+${RE_STR_DEFAULT}\\s*\\)\\s*$`,\n ),\n },\n {\n getValue: m => parseQuotedValue(m[2].trim())?.value ?? null,\n operator: 'starts_with',\n pathGroup: 1,\n pattern: new RegExp(String.raw`^${RE_STR_DEFAULT}\\s*\\|\\s*startswith\\s*\\(\\s*(.+)\\s*\\)\\s*$`),\n },\n {\n getValue: m => parseQuotedValue(m[2].trim())?.value ?? null,\n operator: 'ends_with',\n pathGroup: 1,\n pattern: new RegExp(String.raw`^${RE_STR_DEFAULT}\\s*\\|\\s*endswith\\s*\\(\\s*(.+)\\s*\\)\\s*$`),\n },\n // number operators (>= and <= before > and < to avoid partial match)\n {\n getValue: m => m[2].trim(),\n operator: 'num_gte',\n pathGroup: 1,\n pattern: new RegExp(String.raw`^${RE_NUM_DEFAULT}\\s*>=\\s*(.+)$`),\n },\n {\n getValue: m => m[2].trim(),\n operator: 'num_lte',\n pathGroup: 1,\n pattern: new RegExp(String.raw`^${RE_NUM_DEFAULT}\\s*<=\\s*(.+)$`),\n },\n {\n getValue: m => m[2].trim(),\n operator: 'num_eq',\n pathGroup: 1,\n pattern: new RegExp(String.raw`^${RE_NUM_DEFAULT}\\s*==\\s*(.+)$`),\n },\n {\n getValue: m => m[2].trim(),\n operator: 'num_neq',\n pathGroup: 1,\n pattern: new RegExp(String.raw`^${RE_NUM_DEFAULT}\\s*!=\\s*(.+)$`),\n },\n {\n getValue: m => m[2].trim(),\n operator: 'num_gt',\n pathGroup: 1,\n pattern: new RegExp(String.raw`^${RE_NUM_DEFAULT}\\s*>\\s*(.+)$`),\n },\n {\n getValue: m => m[2].trim(),\n operator: 'num_lt',\n pathGroup: 1,\n pattern: new RegExp(String.raw`^${RE_NUM_DEFAULT}\\s*<\\s*(.+)$`),\n },\n];\n\nfunction parseSingleConditionExpression(\n expr: string,\n logicalOp?: LogicalOperator,\n): SingleCondition | null {\n const trimmed = expr.trim();\n for (const { getValue, operator, pathGroup, pattern } of PARSE_PATTERNS) {\n const match = new RegExp(pattern).exec(trimmed);\n if (!match) {\n continue;\n }\n const value = getValue(match);\n if (value === null) {\n continue;\n }\n return toSingleCondition(operator, match[pathGroup], value, logicalOp);\n }\n return null;\n}\n\n/**\n * Parse a group expression into conditions with per-condition logical operators.\n */\nfunction parseGroupExpression(groupExpr: string): SingleCondition[] | null {\n const { conditionExprs, operators } = tokenizeGroupExpression(groupExpr);\n const conditions: SingleCondition[] = [];\n\n for (let i = 0; i < conditionExprs.length; i++) {\n const cs = conditionExprs[i];\n if (!cs) {\n continue;\n }\n const unwrapped = stripOuterParens(cs);\n const logicalOp = i > 0 ? operators[i - 1] : undefined;\n const single = parseSingleConditionExpression(unwrapped, logicalOp);\n if (single) {\n conditions.push(single);\n }\n }\n\n return conditions.length > 0 ? conditions : null;\n}\n\n/**\n * Parse a saved Unlayer display condition back into modal state for prefilling.\n * Returns null if the condition is missing or cannot be parsed.\n */\nexport function parseUnlayerDisplayCondition(\n condition: UnlayerDisplayCondition | null | undefined,\n): DisplayConditionState | null {\n if (!condition?.before) {\n return null;\n }\n const ifMatch = new RegExp(/\\{%\\s*if\\s+(.+)\\s*%\\}/s).exec(condition.before);\n if (!ifMatch) {\n return null;\n }\n let expr = ifMatch[1].trim();\n\n let behavior: DisplayBehavior = 'show';\n if (expr.startsWith('not (') && expr.endsWith(')')) {\n behavior = 'hide';\n expr = expr.slice(5, -1).trim();\n }\n\n const { conditionExprs: ruleStrings, operators: groupOperators } =\n tokenizeGroupExpression(expr);\n const groups: ConditionGroup[] = [];\n\n for (let i = 0; i < ruleStrings.length; i++) {\n const rs = ruleStrings[i];\n if (!rs) {\n continue;\n }\n const unwrapped = stripOuterParens(rs);\n const conditions = parseGroupExpression(unwrapped);\n if (conditions) {\n const group: ConditionGroup = {\n conditions,\n id: generateId(),\n };\n if (i > 0) {\n group.logicalOperator = groupOperators[i - 1] ?? 'and';\n }\n groups.push(group);\n }\n }\n\n if (groups.length === 0) {\n return null;\n }\n\n return { behavior, groups };\n}\n"],"names":["buildFormFieldKey","parseFormFieldKey","toNunjucksFieldReference","generateId","CONDITION_OPERATORS","VALUE_LESS_OPERATORS","OPERATOR_LABEL_MAP","Map","map","op","value","label","escapeNunjucksString","s","replace","nunjucksValueLiteral","isEscapedAt","index","slashCount","i","isValueLessOperator","operator","includes","buildSingleConditionExpression","dataPointKey","path","defaulted","numDefaulted","normalizedValue","trim","literal","numLiteral","test","inner","buildGroupExpression","group","valid","conditions","filter","c","length","result","joiner","logicalOperator","buildFullConditionExpression","state","validGroups","groups","expression","item","combined","behavior","operatorLabel","get","generateTypeAndLabel","parts","push","summary","slice","join","type","buildUnlayerDisplayCondition","expr","before","after","description","undefined","stripOuterParens","t","startsWith","at","depth","tokenizeGroupExpression","conditionExprs","operators","inSingleQuote","start","Math","max","unescapeNunjucksString","replaceAll","String","raw","SIMPLE_DATA_POINT_PATH","FORM_RUNTIME_DATA_POINT_PATH","DATA_POINT_PATH","RE_STR_DEFAULT","RE_NUM_DEFAULT","QUOTED_CONTENT","toSingleCondition","logicalOp","normalizedDataPointKey","parsed","formId","fieldId","condition","id","parseQuotedValue","rest","PARSE_PATTERNS","getValue","pathGroup","pattern","RegExp","m","parseSingleConditionExpression","trimmed","match","exec","parseGroupExpression","groupExpr","cs","unwrapped","single","parseUnlayerDisplayCondition","ifMatch","endsWith","ruleStrings","groupOperators","rs"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/display-conditions/nunjucks.ts"],"sourcesContent":["import { buildFormFieldKey, parseFormFieldKey, toNunjucksFieldReference } from '../shared/forms';\nimport { generateId } from './constants';\nimport {\n ConditionGroup,\n ConditionOperator,\n DisplayBehavior,\n DisplayConditionState,\n LogicalOperator,\n SingleCondition,\n UnlayerDisplayCondition,\n VALUE_LESS_OPERATORS,\n} from './types';\n\nfunction escapeNunjucksString(s: string): string {\n return s.replace(/\\\\/g, '\\\\\\\\').replace(/'/g, \"\\\\'\");\n}\n\nfunction nunjucksValueLiteral(value: string): string {\n return `'${escapeNunjucksString(value)}'`;\n}\n\nfunction isEscapedAt(s: string, index: number): boolean {\n let slashCount = 0;\n for (let i = index - 1; i >= 0 && s[i] === '\\\\'; i--) {\n slashCount++;\n }\n return slashCount % 2 === 1;\n}\n\nfunction isValueLessOperator(operator: string): boolean {\n return VALUE_LESS_OPERATORS.includes(operator as ConditionOperator);\n}\n\n/**\n * Build a single condition expression in Nunjucks.\n * Wrapped in () so splitting never breaks composite expressions.\n */\nfunction buildSingleConditionExpression(\n dataPointKey: string,\n operator: string,\n value: string,\n): string {\n const path = toNunjucksFieldReference(dataPointKey);\n const defaulted = `(${path} | default(''))`;\n const numDefaulted = `(${path} | default(0))`;\n const normalizedValue = value.trim();\n const literal = normalizedValue ? nunjucksValueLiteral(normalizedValue) : \"''\";\n const numLiteral =\n /^-?(?:\\d+\\.?\\d*|\\.\\d+)$/.test(normalizedValue) && normalizedValue !== '-'\n ? normalizedValue\n : '0';\n let inner: string;\n\n switch (operator) {\n case 'is_equal_to':\n inner = `${defaulted} == ${literal}`;\n break;\n case 'is_not_equal_to':\n inner = `${defaulted} != ${literal}`;\n break;\n case 'contains':\n inner = `${literal} in ${defaulted}`;\n break;\n case 'does_not_contain':\n inner = `not (${literal} in ${defaulted})`;\n break;\n case 'starts_with':\n inner = `(${defaulted} | startswith(${literal}))`;\n break;\n case 'ends_with':\n inner = `(${defaulted} | endswith(${literal}))`;\n break;\n case 'is_empty':\n inner = `${defaulted} == '' or (${defaulted} | length) == 0`;\n break;\n case 'is_not_empty':\n inner = `${defaulted} != '' and (${defaulted} | length) > 0`;\n break;\n case 'num_eq':\n inner = `${numDefaulted} == ${numLiteral}`;\n break;\n case 'num_neq':\n inner = `${numDefaulted} != ${numLiteral}`;\n break;\n case 'num_gt':\n inner = `${numDefaulted} > ${numLiteral}`;\n break;\n case 'num_lt':\n inner = `${numDefaulted} < ${numLiteral}`;\n break;\n case 'num_gte':\n inner = `${numDefaulted} >= ${numLiteral}`;\n break;\n case 'num_lte':\n inner = `${numDefaulted} <= ${numLiteral}`;\n break;\n default:\n inner = `${defaulted} == ${literal}`;\n }\n return `(${inner})`;\n}\n\n/**\n * Build group expression. Each condition is connected by its own logical operator.\n * E.g. (cond1) and (cond2) or (cond3)\n */\nfunction buildGroupExpression(group: ConditionGroup): string {\n const valid = group.conditions.filter(\n c => c.dataPointKey && (isValueLessOperator(c.operator) || c.value.trim() !== ''),\n );\n if (valid.length === 0) {\n return '';\n }\n let result = buildSingleConditionExpression(\n valid[0].dataPointKey,\n valid[0].operator,\n valid[0].value,\n );\n for (let i = 1; i < valid.length; i++) {\n const joiner = valid[i].logicalOperator === 'or' ? ' or ' : ' and ';\n result +=\n joiner +\n buildSingleConditionExpression(\n valid[i].dataPointKey,\n valid[i].operator,\n valid[i].value,\n );\n }\n // Wrap group in parens so top-level rule AND split works cleanly\n return valid.length > 1 ? `(${result})` : result;\n}\n\n/**\n * Build full condition expression.\n * Rules (groups) are connected by each group's logical operator.\n * hide behavior wraps everything in not().\n */\nfunction buildFullConditionExpression(state: DisplayConditionState): string {\n const validGroups = state.groups\n .map(group => ({ expression: buildGroupExpression(group), group }))\n .filter(item => !!item.expression);\n if (validGroups.length === 0) {\n return '';\n }\n let combined = validGroups[0].expression;\n for (let i = 1; i < validGroups.length; i++) {\n const joiner = validGroups[i].group.logicalOperator === 'or' ? ' or ' : ' and ';\n combined += `${joiner}${validGroups[i].expression}`;\n }\n if (state.behavior === 'hide') {\n return `not (${combined})`;\n }\n return combined;\n}\n\nfunction generateConditionText(state: DisplayConditionState): {\n description: string;\n label: string;\n type: string;\n} {\n const conditionCount = state.groups.reduce(\n (count, group) =>\n count +\n group.conditions.filter(\n c => c.dataPointKey && (isValueLessOperator(c.operator) || c.value.trim() !== ''),\n ).length,\n 0,\n );\n const label =\n state.behavior === 'show' ? 'Show condition configured' : 'Hide condition configured';\n const description =\n conditionCount === 1\n ? '1 condition is currently applied'\n : `${conditionCount} conditions are currently applied`;\n return { description, label, type: '' };\n}\n\nexport function buildUnlayerDisplayCondition(\n state: DisplayConditionState,\n): UnlayerDisplayCondition | null {\n const expr = buildFullConditionExpression(state);\n if (!expr) {\n return null;\n }\n const before = `{% if ${expr} %}`;\n const after = '{% endif %}';\n const { description, label, type } = generateConditionText(state);\n return { after, before, description, label, type };\n}\n\n/*\n * ---------------------------------------------------------------------------\n * Parse helpers\n * ---------------------------------------------------------------------------\n */\n\nfunction stripOuterParens(s: string): string {\n const t = s.trim();\n if (t.length < 2 || !t.startsWith('(') || t.at(-1) !== ')') {\n return t;\n }\n let depth = 1;\n for (let i = 1; i < t.length - 1; i++) {\n if (t[i] === '(') {\n depth++;\n } else if (t[i] === ')') {\n depth--;\n if (depth === 0) {\n return t;\n }\n }\n }\n return depth === 1 ? t.slice(1, -1).trim() : t;\n}\n\n/**\n * Tokenize an expression into alternating tokens of (expr, operator).\n * Returns: [expr, 'and'|'or', expr, 'and'|'or', expr, ...]\n *\n * This scans for top-level \" and \" / \" or \" while respecting parens,\n * and records which separator was found between each pair.\n */\nfunction tokenizeGroupExpression(expr: string): {\n conditionExprs: string[];\n operators: LogicalOperator[];\n} {\n const conditionExprs: string[] = [];\n const operators: LogicalOperator[] = [];\n let depth = 0;\n let inSingleQuote = false;\n let start = 0;\n\n for (let i = 0; i < expr.length; i++) {\n const c = expr[i];\n if (c === \"'\" && !isEscapedAt(expr, i)) {\n inSingleQuote = !inSingleQuote;\n } else if (!inSingleQuote && c === '(') {\n depth++;\n } else if (!inSingleQuote && c === ')') {\n depth = Math.max(0, depth - 1);\n } else if (!inSingleQuote && depth === 0) {\n // Check for \" and \" or \" or \" at this position\n if (expr.slice(i, i + 5) === ' and ') {\n conditionExprs.push(expr.slice(start, i).trim());\n operators.push('and');\n start = i + 5;\n i += 4;\n continue;\n }\n if (expr.slice(i, i + 4) === ' or ') {\n conditionExprs.push(expr.slice(start, i).trim());\n operators.push('or');\n start = i + 4;\n i += 3;\n continue;\n }\n }\n }\n conditionExprs.push(expr.slice(start).trim());\n return { conditionExprs, operators };\n}\n\nfunction unescapeNunjucksString(s: string): string {\n return s.replaceAll('\\\\\\\\', '\\\\').replaceAll(String.raw`\\'`, \"'\");\n}\n\nconst SIMPLE_DATA_POINT_PATH = String.raw`[\\w.]+`;\nconst FORM_RUNTIME_DATA_POINT_PATH = String.raw`__submission_fields\\[\"\\d+\"\\]\\[\"[A-Za-z0-9]+\"\\]`;\nconst DATA_POINT_PATH = String.raw`(${SIMPLE_DATA_POINT_PATH}|${FORM_RUNTIME_DATA_POINT_PATH})`;\nconst RE_STR_DEFAULT = String.raw`\\(${DATA_POINT_PATH}\\s*\\|\\s*default\\s*\\(\\s*''\\s*\\)\\)`;\nconst RE_NUM_DEFAULT = String.raw`\\(${DATA_POINT_PATH}\\s*\\|\\s*default\\s*\\(\\s*0\\s*\\)\\)`;\nconst QUOTED_CONTENT = String.raw`((?:[^'\\\\]|\\\\.)*)`;\n\nfunction toSingleCondition(\n operator: ConditionOperator,\n dataPointKey: string,\n value: string,\n logicalOp?: LogicalOperator,\n): SingleCondition {\n const normalizedDataPointKey = (() => {\n const parsed = parseFormFieldKey(dataPointKey.trim());\n if (!parsed) {\n return dataPointKey.trim();\n }\n return buildFormFieldKey(parsed.formId, parsed.fieldId);\n })();\n const condition: SingleCondition = {\n dataPointKey: normalizedDataPointKey,\n id: generateId(),\n operator,\n value,\n };\n if (logicalOp) {\n condition.logicalOperator = logicalOp;\n }\n return condition;\n}\n\nfunction parseQuotedValue(s: string): { value: string; rest: string } | null {\n if (!s.startsWith(\"'\")) {\n return null;\n }\n let i = 1;\n let value = '';\n while (i < s.length) {\n if (s[i] === '\\\\' && i + 1 < s.length) {\n value += s[i + 1] === \"'\" ? \"'\" : s[i + 1];\n i += 2;\n continue;\n }\n if (s[i] === \"'\") {\n return { rest: s.slice(i + 1).trim(), value: unescapeNunjucksString(value) };\n }\n value += s[i];\n i++;\n }\n return null;\n}\n\ninterface ParsePattern {\n getValue: (match: RegExpMatchArray) => string | null;\n operator: ConditionOperator;\n pathGroup: number;\n pattern: RegExp;\n}\n\nconst PARSE_PATTERNS: ParsePattern[] = [\n // value-less\n {\n getValue: () => '',\n operator: 'is_empty',\n pathGroup: 1,\n pattern: new RegExp(\n String.raw`^${RE_STR_DEFAULT}\\s*==\\s*''\\s+or\\s+\\(\\(${DATA_POINT_PATH}\\s*\\|\\s*default\\s*\\(\\s*''\\s*\\)\\)\\s*\\|\\s*length\\)\\s*==\\s*0$`,\n ),\n },\n {\n getValue: () => '',\n operator: 'is_not_empty',\n pathGroup: 1,\n pattern: new RegExp(\n String.raw`^${RE_STR_DEFAULT}\\s*!=\\s*''\\s+and\\s+\\(\\(${DATA_POINT_PATH}\\s*\\|\\s*default\\s*\\(\\s*''\\s*\\)\\)\\s*\\|\\s*length\\)\\s*>\\s*0$`,\n ),\n },\n // string with value\n {\n getValue: m => parseQuotedValue(m[2].trim())?.value ?? null,\n operator: 'is_equal_to',\n pathGroup: 1,\n pattern: new RegExp(String.raw`^${RE_STR_DEFAULT}\\s*==\\s+(.+)$`),\n },\n {\n getValue: m => parseQuotedValue(m[2].trim())?.value ?? null,\n operator: 'is_not_equal_to',\n pathGroup: 1,\n pattern: new RegExp(String.raw`^${RE_STR_DEFAULT}\\s*!=\\s+(.+)$`),\n },\n {\n getValue: m => unescapeNunjucksString(m[1]),\n operator: 'contains',\n pathGroup: 2,\n pattern: new RegExp(String.raw`^'${QUOTED_CONTENT}'\\s+in\\s+${RE_STR_DEFAULT}$`),\n },\n {\n getValue: m => unescapeNunjucksString(m[1]),\n operator: 'does_not_contain',\n pathGroup: 2,\n pattern: new RegExp(\n String.raw`^not\\s*\\(\\s*'${QUOTED_CONTENT}'\\s+in\\s+${RE_STR_DEFAULT}\\s*\\)\\s*$`,\n ),\n },\n {\n getValue: m => parseQuotedValue(m[2].trim())?.value ?? null,\n operator: 'starts_with',\n pathGroup: 1,\n pattern: new RegExp(String.raw`^${RE_STR_DEFAULT}\\s*\\|\\s*startswith\\s*\\(\\s*(.+)\\s*\\)\\s*$`),\n },\n {\n getValue: m => parseQuotedValue(m[2].trim())?.value ?? null,\n operator: 'ends_with',\n pathGroup: 1,\n pattern: new RegExp(String.raw`^${RE_STR_DEFAULT}\\s*\\|\\s*endswith\\s*\\(\\s*(.+)\\s*\\)\\s*$`),\n },\n // number operators (>= and <= before > and < to avoid partial match)\n {\n getValue: m => m[2].trim(),\n operator: 'num_gte',\n pathGroup: 1,\n pattern: new RegExp(String.raw`^${RE_NUM_DEFAULT}\\s*>=\\s*(.+)$`),\n },\n {\n getValue: m => m[2].trim(),\n operator: 'num_lte',\n pathGroup: 1,\n pattern: new RegExp(String.raw`^${RE_NUM_DEFAULT}\\s*<=\\s*(.+)$`),\n },\n {\n getValue: m => m[2].trim(),\n operator: 'num_eq',\n pathGroup: 1,\n pattern: new RegExp(String.raw`^${RE_NUM_DEFAULT}\\s*==\\s*(.+)$`),\n },\n {\n getValue: m => m[2].trim(),\n operator: 'num_neq',\n pathGroup: 1,\n pattern: new RegExp(String.raw`^${RE_NUM_DEFAULT}\\s*!=\\s*(.+)$`),\n },\n {\n getValue: m => m[2].trim(),\n operator: 'num_gt',\n pathGroup: 1,\n pattern: new RegExp(String.raw`^${RE_NUM_DEFAULT}\\s*>\\s*(.+)$`),\n },\n {\n getValue: m => m[2].trim(),\n operator: 'num_lt',\n pathGroup: 1,\n pattern: new RegExp(String.raw`^${RE_NUM_DEFAULT}\\s*<\\s*(.+)$`),\n },\n];\n\nfunction parseSingleConditionExpression(\n expr: string,\n logicalOp?: LogicalOperator,\n): SingleCondition | null {\n const trimmed = expr.trim();\n for (const { getValue, operator, pathGroup, pattern } of PARSE_PATTERNS) {\n const match = new RegExp(pattern).exec(trimmed);\n if (!match) {\n continue;\n }\n const value = getValue(match);\n if (value === null) {\n continue;\n }\n return toSingleCondition(operator, match[pathGroup], value, logicalOp);\n }\n return null;\n}\n\n/**\n * Parse a group expression into conditions with per-condition logical operators.\n */\nfunction parseGroupExpression(groupExpr: string): SingleCondition[] | null {\n const { conditionExprs, operators } = tokenizeGroupExpression(groupExpr);\n const conditions: SingleCondition[] = [];\n\n for (let i = 0; i < conditionExprs.length; i++) {\n const cs = conditionExprs[i];\n if (!cs) {\n continue;\n }\n const unwrapped = stripOuterParens(cs);\n const logicalOp = i > 0 ? operators[i - 1] : undefined;\n const single = parseSingleConditionExpression(unwrapped, logicalOp);\n if (single) {\n conditions.push(single);\n }\n }\n\n return conditions.length > 0 ? conditions : null;\n}\n\n/**\n * Parse a saved Unlayer display condition back into modal state for prefilling.\n * Returns null if the condition is missing or cannot be parsed.\n */\nexport function parseUnlayerDisplayCondition(\n condition: UnlayerDisplayCondition | null | undefined,\n): DisplayConditionState | null {\n if (!condition?.before) {\n return null;\n }\n const ifMatch = new RegExp(/\\{%\\s*if\\s+(.+)\\s*%\\}/s).exec(condition.before);\n if (!ifMatch) {\n return null;\n }\n let expr = ifMatch[1].trim();\n\n let behavior: DisplayBehavior = 'show';\n if (expr.startsWith('not (') && expr.endsWith(')')) {\n behavior = 'hide';\n expr = expr.slice(5, -1).trim();\n }\n\n const { conditionExprs: ruleStrings, operators: groupOperators } =\n tokenizeGroupExpression(expr);\n const groups: ConditionGroup[] = [];\n\n for (let i = 0; i < ruleStrings.length; i++) {\n const rs = ruleStrings[i];\n if (!rs) {\n continue;\n }\n const unwrapped = stripOuterParens(rs);\n const conditions = parseGroupExpression(unwrapped);\n if (conditions) {\n const group: ConditionGroup = {\n conditions,\n id: generateId(),\n };\n if (i > 0) {\n group.logicalOperator = groupOperators[i - 1] ?? 'and';\n }\n groups.push(group);\n }\n }\n\n if (groups.length === 0) {\n return null;\n }\n\n return { behavior, groups };\n}\n"],"names":["buildFormFieldKey","parseFormFieldKey","toNunjucksFieldReference","generateId","VALUE_LESS_OPERATORS","escapeNunjucksString","s","replace","nunjucksValueLiteral","value","isEscapedAt","index","slashCount","i","isValueLessOperator","operator","includes","buildSingleConditionExpression","dataPointKey","path","defaulted","numDefaulted","normalizedValue","trim","literal","numLiteral","test","inner","buildGroupExpression","group","valid","conditions","filter","c","length","result","joiner","logicalOperator","buildFullConditionExpression","state","validGroups","groups","map","expression","item","combined","behavior","generateConditionText","conditionCount","reduce","count","label","description","type","buildUnlayerDisplayCondition","expr","before","after","stripOuterParens","t","startsWith","at","depth","slice","tokenizeGroupExpression","conditionExprs","operators","inSingleQuote","start","Math","max","push","unescapeNunjucksString","replaceAll","String","raw","SIMPLE_DATA_POINT_PATH","FORM_RUNTIME_DATA_POINT_PATH","DATA_POINT_PATH","RE_STR_DEFAULT","RE_NUM_DEFAULT","QUOTED_CONTENT","toSingleCondition","logicalOp","normalizedDataPointKey","parsed","formId","fieldId","condition","id","parseQuotedValue","rest","PARSE_PATTERNS","getValue","pathGroup","pattern","RegExp","m","parseSingleConditionExpression","trimmed","match","exec","parseGroupExpression","groupExpr","cs","unwrapped","undefined","single","parseUnlayerDisplayCondition","ifMatch","endsWith","ruleStrings","groupOperators","rs"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@servicetitan/dte-unlayer",
3
- "version": "0.123.0",
3
+ "version": "0.125.0",
4
4
  "description": "",
5
5
  "main": "./dist/index.js",
6
6
  "typings": "./dist/index.d.ts",
@@ -1,5 +1,5 @@
1
1
  import { Button, Combobox, Dialog, Flex, Text } from '@servicetitan/anvil2';
2
- import { useCallback, useEffect, useMemo, useState } from 'react';
2
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
3
3
  import { createPortal } from 'react-dom';
4
4
  import {
5
5
  buildFormFieldKey,
@@ -83,6 +83,8 @@ export const DisplayConditionModal = (props: DisplayConditionModalProps) => {
83
83
  );
84
84
  const [loadingFormIds, setLoadingFormIds] = useState<number[]>([]);
85
85
  const [isFormsListLoading, setIsFormsListLoading] = useState(false);
86
+ const formFieldsByFormIdRef = useRef<Record<number, FormFieldInfo[]>>({});
87
+ const loadingFormIdsRef = useRef<number[]>([]);
86
88
 
87
89
  const dataPointOptions = useMemo(() => getSchemaDataPointOptions(schema), [schema]);
88
90
  const formFieldOptions = useMemo<FormFieldOption[]>(() => {
@@ -113,6 +115,35 @@ export const DisplayConditionModal = (props: DisplayConditionModalProps) => {
113
115
  );
114
116
  const isFieldDataLoading = isFormsListLoading || loadingFormIds.length > 0;
115
117
 
118
+ useEffect(() => {
119
+ formFieldsByFormIdRef.current = formFieldsByFormId;
120
+ }, [formFieldsByFormId]);
121
+
122
+ useEffect(() => {
123
+ loadingFormIdsRef.current = loadingFormIds;
124
+ }, [loadingFormIds]);
125
+
126
+ const requestFormFields = useCallback(
127
+ (formId: number) => {
128
+ if (!onConditionFormSelect) {
129
+ return;
130
+ }
131
+ if (
132
+ formFieldsByFormIdRef.current[formId] ||
133
+ loadingFormIdsRef.current.includes(formId)
134
+ ) {
135
+ return;
136
+ }
137
+
138
+ setLoadingFormIds(prev => (prev.includes(formId) ? prev : [...prev, formId]));
139
+ onConditionFormSelect(formId, (selectedFormId, fields) => {
140
+ setFormFieldsByFormId(prev => ({ ...prev, [selectedFormId]: fields }));
141
+ setLoadingFormIds(prev => prev.filter(id => id !== selectedFormId));
142
+ });
143
+ },
144
+ [onConditionFormSelect],
145
+ );
146
+
116
147
  const portalTarget = useMemo(() => {
117
148
  if (typeof document === 'undefined') {
118
149
  return null;
@@ -138,7 +169,7 @@ export const DisplayConditionModal = (props: DisplayConditionModalProps) => {
138
169
  setForms([]);
139
170
  setFormFieldsByFormId({});
140
171
  setIsFormsListLoading(!!onConditionalsOpen);
141
- setLoadingFormIds(usedFormIds);
172
+ setLoadingFormIds([]);
142
173
 
143
174
  if (onConditionalsOpen) {
144
175
  onConditionalsOpen(
@@ -152,6 +183,13 @@ export const DisplayConditionModal = (props: DisplayConditionModalProps) => {
152
183
  setLoadingFormIds(prev => prev.filter(id => id !== formId));
153
184
  },
154
185
  );
186
+
187
+ /*
188
+ * Keep display-condition flow aligned with calculated-field flow:
189
+ * when modal opens, request fields for already-used forms so labels
190
+ * are available in edit mode.
191
+ */
192
+ usedFormIds.forEach(formId => requestFormFields(formId));
155
193
  } else {
156
194
  setIsFormsListLoading(false);
157
195
  setLoadingFormIds([]);
@@ -161,7 +199,7 @@ export const DisplayConditionModal = (props: DisplayConditionModalProps) => {
161
199
  setMatchType(deriveMatchType(initialState));
162
200
  setIsOpen(true);
163
201
  });
164
- }, [onConditionalsOpen]);
202
+ }, [onConditionalsOpen, requestFormFields]);
165
203
 
166
204
  const handleClose = useCallback(() => {
167
205
  setRequest(null);
@@ -204,16 +242,9 @@ export const DisplayConditionModal = (props: DisplayConditionModalProps) => {
204
242
 
205
243
  const handleFormSelect = useCallback(
206
244
  (formId: number) => {
207
- if (!onConditionFormSelect || formFieldsByFormId[formId]) {
208
- return;
209
- }
210
- setLoadingFormIds(prev => (prev.includes(formId) ? prev : [...prev, formId]));
211
- onConditionFormSelect(formId, (selectedFormId, fields) => {
212
- setFormFieldsByFormId(prev => ({ ...prev, [selectedFormId]: fields }));
213
- setLoadingFormIds(prev => prev.filter(id => id !== selectedFormId));
214
- });
245
+ requestFormFields(formId);
215
246
  },
216
- [formFieldsByFormId, onConditionFormSelect],
247
+ [requestFormFields],
217
248
  );
218
249
 
219
250
  const canSave = useMemo(() => {
@@ -1,7 +1,6 @@
1
1
  import { buildFormFieldKey, parseFormFieldKey, toNunjucksFieldReference } from '../shared/forms';
2
2
  import { generateId } from './constants';
3
3
  import {
4
- CONDITION_OPERATORS,
5
4
  ConditionGroup,
6
5
  ConditionOperator,
7
6
  DisplayBehavior,
@@ -12,10 +11,6 @@ import {
12
11
  VALUE_LESS_OPERATORS,
13
12
  } from './types';
14
13
 
15
- const OPERATOR_LABEL_MAP = new Map<string, string>(
16
- CONDITION_OPERATORS.map(op => [op.value, op.label]),
17
- );
18
-
19
14
  function escapeNunjucksString(s: string): string {
20
15
  return s.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
21
16
  }
@@ -158,28 +153,26 @@ function buildFullConditionExpression(state: DisplayConditionState): string {
158
153
  return combined;
159
154
  }
160
155
 
161
- function operatorLabel(operator: string): string {
162
- return OPERATOR_LABEL_MAP.get(operator) ?? operator;
163
- }
164
-
165
- function generateTypeAndLabel(state: DisplayConditionState): { type: string; label: string } {
166
- const parts: string[] = [];
167
- for (const group of state.groups) {
168
- for (const c of group.conditions) {
169
- if (!c.dataPointKey) {
170
- continue;
171
- }
172
- if (isValueLessOperator(c.operator)) {
173
- parts.push(`${c.dataPointKey} ${operatorLabel(c.operator)}`);
174
- } else if (c.value.trim()) {
175
- parts.push(`${c.dataPointKey} ${operatorLabel(c.operator)} ${c.value.trim()}`);
176
- }
177
- }
178
- }
179
- const summary = parts.slice(0, 2).join('; ') + (parts.length > 2 ? '…' : '');
180
- const type = state.behavior === 'show' ? 'Display when' : 'Hide when';
181
- const label = summary || (state.behavior === 'show' ? 'Show when' : 'Hide when');
182
- return { type, label };
156
+ function generateConditionText(state: DisplayConditionState): {
157
+ description: string;
158
+ label: string;
159
+ type: string;
160
+ } {
161
+ const conditionCount = state.groups.reduce(
162
+ (count, group) =>
163
+ count +
164
+ group.conditions.filter(
165
+ c => c.dataPointKey && (isValueLessOperator(c.operator) || c.value.trim() !== ''),
166
+ ).length,
167
+ 0,
168
+ );
169
+ const label =
170
+ state.behavior === 'show' ? 'Show condition configured' : 'Hide condition configured';
171
+ const description =
172
+ conditionCount === 1
173
+ ? '1 condition is currently applied'
174
+ : `${conditionCount} conditions are currently applied`;
175
+ return { description, label, type: '' };
183
176
  }
184
177
 
185
178
  export function buildUnlayerDisplayCondition(
@@ -191,8 +184,7 @@ export function buildUnlayerDisplayCondition(
191
184
  }
192
185
  const before = `{% if ${expr} %}`;
193
186
  const after = '{% endif %}';
194
- const { label, type } = generateTypeAndLabel(state);
195
- const description = state.groups.length > 1 ? `${state.groups.length} rules` : undefined;
187
+ const { description, label, type } = generateConditionText(state);
196
188
  return { after, before, description, label, type };
197
189
  }
198
190