@servicetitan/dte-unlayer 0.96.0 → 0.97.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":"AAkBA,MAAM,WAAW,0BAA0B;IACvC,MAAM,CAAC,EAAE,OAAO,kBAAkB,EAAE,YAAY,CAAC;CACpD;AAED,eAAO,MAAM,qBAAqB,GAAI,OAAO,0BAA0B,uCAgNtE,CAAC"}
1
+ {"version":3,"file":"DisplayConditionModal.d.ts","sourceRoot":"","sources":["../../src/display-conditions/DisplayConditionModal.tsx"],"names":[],"mappings":"AAiBA,MAAM,WAAW,0BAA0B;IACvC,MAAM,CAAC,EAAE,OAAO,kBAAkB,EAAE,YAAY,CAAC;CACpD;AAED,eAAO,MAAM,qBAAqB,GAAI,OAAO,0BAA0B,uCAgNtE,CAAC"}
@@ -41,15 +41,9 @@ export const DisplayConditionModal = (props)=>{
41
41
  });
42
42
  }, []);
43
43
  const handleClose = useCallback(()=>{
44
- if (request) {
45
- var _request_data;
46
- request.done((_request_data = request.data) !== null && _request_data !== void 0 ? _request_data : null);
47
- setRequest(null);
48
- }
44
+ setRequest(null);
49
45
  setIsOpen(false);
50
- }, [
51
- request
52
- ]);
46
+ }, []);
53
47
  const handleSave = useCallback(()=>{
54
48
  if (!request) {
55
49
  return;
@@ -169,6 +163,7 @@ export const DisplayConditionModal = (props)=>{
169
163
  style: {
170
164
  maxHeight: MODAL_CONTENT_MAX_HEIGHT,
171
165
  overflowY: 'auto',
166
+ paddingRight: 8,
172
167
  width: '100%'
173
168
  },
174
169
  children: [
@@ -190,7 +185,7 @@ export const DisplayConditionModal = (props)=>{
190
185
  }),
191
186
  /*#__PURE__*/ _jsxs(Flex, {
192
187
  direction: "row",
193
- alignItems: "center",
188
+ alignItems: "flex-end",
194
189
  gap: "3",
195
190
  style: {
196
191
  flexWrap: 'wrap'
@@ -200,13 +195,16 @@ export const DisplayConditionModal = (props)=>{
200
195
  size: "small",
201
196
  subdued: true,
202
197
  variant: "body",
198
+ style: {
199
+ paddingBottom: 10
200
+ },
203
201
  children: "Select to show or hide selected component."
204
202
  }),
205
203
  /*#__PURE__*/ _jsx("div", {
206
204
  style: {
207
205
  width: 160
208
206
  },
209
- children: /*#__PURE__*/ _jsxs(Combobox, {
207
+ children: /*#__PURE__*/ _jsxs(Combobox.Select, {
210
208
  ...{
211
209
  disableClearSelection: true
212
210
  },
@@ -225,7 +223,7 @@ export const DisplayConditionModal = (props)=>{
225
223
  onChange: handleBehaviorChange,
226
224
  children: [
227
225
  /*#__PURE__*/ _jsx(Combobox.SelectTrigger, {
228
- label: "Behavior",
226
+ label: "",
229
227
  placeholder: "Select..."
230
228
  }),
231
229
  /*#__PURE__*/ _jsx(Combobox.Content, {
@@ -244,36 +242,44 @@ export const DisplayConditionModal = (props)=>{
244
242
  })
245
243
  ]
246
244
  }),
247
- /*#__PURE__*/ _jsx(ConditionGroupsSection, {
248
- dataPointOptions: dataPointOptions,
249
- groups: state.groups,
250
- onAddGroup: addGroup,
251
- onRemoveGroup: removeGroup,
252
- onUpdateGroup: updateGroup
253
- }),
254
- !canSave && /*#__PURE__*/ _jsx(Text, {
255
- size: "small",
256
- subdued: true,
257
- variant: "body",
258
- children: "Complete each condition before saving. Data point and value are required, and numeric fields must contain a valid number."
245
+ /*#__PURE__*/ _jsx("div", {
246
+ style: {
247
+ padding: 8
248
+ },
249
+ children: /*#__PURE__*/ _jsx(ConditionGroupsSection, {
250
+ dataPointOptions: dataPointOptions,
251
+ groups: state.groups,
252
+ onAddGroup: addGroup,
253
+ onRemoveGroup: removeGroup,
254
+ onUpdateGroup: updateGroup
255
+ })
259
256
  })
260
257
  ]
261
258
  })
262
259
  }),
263
- /*#__PURE__*/ _jsxs(Dialog.Footer, {
260
+ /*#__PURE__*/ _jsx(Dialog.Footer, {
264
261
  sticky: true,
265
- children: [
266
- /*#__PURE__*/ _jsx(Dialog.CancelButton, {
267
- onClick: handleClose,
268
- children: "Cancel"
269
- }),
270
- /*#__PURE__*/ _jsx(Button, {
271
- appearance: "primary",
272
- disabled: !canSave,
273
- onClick: handleSave,
274
- children: "Save Changes"
275
- })
276
- ]
262
+ children: /*#__PURE__*/ _jsxs(Flex, {
263
+ justifyContent: "flex-end",
264
+ alignItems: "center",
265
+ gap: "3",
266
+ style: {
267
+ width: '100%'
268
+ },
269
+ children: [
270
+ /*#__PURE__*/ _jsx(Button, {
271
+ appearance: "secondary",
272
+ onClick: handleClose,
273
+ children: "Cancel"
274
+ }),
275
+ /*#__PURE__*/ _jsx(Button, {
276
+ appearance: "primary",
277
+ disabled: !canSave,
278
+ onClick: handleSave,
279
+ children: "Save Changes"
280
+ })
281
+ ]
282
+ })
277
283
  })
278
284
  ]
279
285
  }), portalTarget);
@@ -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 { ConditionGroupsSection } from './ConditionGroupsSection';\nimport { defaultGroup, defaultState, MODAL_CONTENT_MAX_HEIGHT } from './constants';\nimport { DisplayConditionRequest, onDisplayCondition } from './displayConditionController';\nimport { buildUnlayerDisplayCondition, parseUnlayerDisplayCondition } from './nunjucks';\nimport { getSchemaDataPointOptions } from './schemaDataPoints';\nimport { ConditionGroup as ConditionGroupType, DisplayBehavior } 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+)$/;\n\ntype BehaviorOption = (typeof BEHAVIOR_OPTIONS)[number];\n\nexport interface DisplayConditionModalProps {\n schema?: import('../shared/schema').SchemaObject;\n}\n\nexport const DisplayConditionModal = (props: DisplayConditionModalProps) => {\n const [request, setRequest] = useState<DisplayConditionRequest | null>(null);\n const [isOpen, setIsOpen] = useState(false);\n const [state, setState] = useState(defaultState);\n\n const dataPointOptions = useMemo(() => getSchemaDataPointOptions(props.schema), [props.schema]);\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 setState(parsed ?? defaultState());\n setIsOpen(true);\n });\n }, []);\n\n const handleClose = useCallback(() => {\n if (request) {\n request.done(request.data ?? null);\n setRequest(null);\n }\n setIsOpen(false);\n }, [request]);\n\n const handleSave = useCallback(() => {\n if (!request) {\n return;\n }\n const condition = buildUnlayerDisplayCondition(state);\n if (condition) {\n request.done(condition);\n } else {\n request.done(null);\n }\n setRequest(null);\n setIsOpen(false);\n }, [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 removeGroup = useCallback((index: number) => {\n setState(prev => ({\n ...prev,\n groups: (() => {\n const nextGroups = prev.groups.filter((_, i) => i !== index);\n if (nextGroups.length === 0) {\n return [defaultGroup()];\n }\n if (index === 0) {\n const { logicalOperator: unusedOp, ...firstGroup } = nextGroups[0];\n return [firstGroup, ...nextGroups.slice(1)];\n }\n return nextGroups;\n })(),\n }));\n }, []);\n\n const addGroup = useCallback(() => {\n setState(prev => ({\n ...prev,\n groups: [...prev.groups, { ...defaultGroup(), logicalOperator: 'and' }],\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 canSave = useMemo(() => {\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 dataPointOptions.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 }, [dataPointOptions, 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>Rules and Conditions</Dialog.Header>\n <Dialog.Content>\n <Flex\n direction=\"column\"\n gap=\"6\"\n style={{\n maxHeight: MODAL_CONTENT_MAX_HEIGHT,\n overflowY: 'auto',\n width: '100%',\n }}\n >\n <Flex\n direction=\"column\"\n gap=\"3\"\n style={{\n borderBottom: '1px solid #e0e0e0',\n paddingBottom: 16,\n }}\n >\n <Text size=\"medium\" variant=\"body\" style={{ fontWeight: 'bold' }}>\n When conditions are met:\n </Text>\n <Flex\n direction=\"row\"\n alignItems=\"center\"\n gap=\"3\"\n style={{ flexWrap: 'wrap' }}\n >\n <Text size=\"small\" subdued variant=\"body\">\n Select to show or hide selected component.\n </Text>\n <div style={{ width: 160 }}>\n <Combobox\n {...({ disableClearSelection: true } as object)}\n itemToKey={(item: BehaviorOption | null) => item?.value ?? ''}\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=\"Behavior\"\n placeholder=\"Select...\"\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>\n </div>\n </Flex>\n </Flex>\n <ConditionGroupsSection\n dataPointOptions={dataPointOptions}\n groups={state.groups}\n onAddGroup={addGroup}\n onRemoveGroup={removeGroup}\n onUpdateGroup={updateGroup}\n />\n {!canSave && (\n <Text size=\"small\" subdued variant=\"body\">\n Complete each condition before saving. Data point and value are\n required, and numeric fields must contain a valid number.\n </Text>\n )}\n </Flex>\n </Dialog.Content>\n <Dialog.Footer sticky>\n <Dialog.CancelButton onClick={handleClose}>Cancel</Dialog.CancelButton>\n <Button appearance=\"primary\" disabled={!canSave} onClick={handleSave}>\n Save Changes\n </Button>\n </Dialog.Footer>\n </Dialog>,\n portalTarget,\n );\n};\n"],"names":["Button","Combobox","Dialog","Flex","Text","useCallback","useEffect","useMemo","useState","createPortal","ConditionGroupsSection","defaultGroup","defaultState","MODAL_CONTENT_MAX_HEIGHT","onDisplayCondition","buildUnlayerDisplayCondition","parseUnlayerDisplayCondition","getSchemaDataPointOptions","BEHAVIOR_OPTIONS","label","value","NUMERIC_VALUE_RE","DisplayConditionModal","props","request","setRequest","isOpen","setIsOpen","state","setState","dataPointOptions","schema","portalTarget","document","body","nextRequest","existing","data","parsed","handleClose","done","handleSave","condition","updateGroup","index","group","prev","next","groups","removeGroup","nextGroups","filter","_","i","length","logicalOperator","unusedOp","firstGroup","slice","addGroup","selectedBehavior","find","opt","behavior","handleBehaviorChange","item","canSave","conditions","dataPointKey","operator","trimmedValue","trim","fieldType","fullKey","test","open","onClose","size","Header","Content","direction","gap","style","maxHeight","overflowY","width","borderBottom","paddingBottom","variant","fontWeight","alignItems","flexWrap","subdued","div","disableClearSelection","itemToKey","itemToString","items","selectedItem","onChange","SelectTrigger","placeholder","List","map","Item","onAddGroup","onRemoveGroup","onUpdateGroup","Footer","sticky","CancelButton","onClick","appearance","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, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport { ConditionGroupsSection } from './ConditionGroupsSection';\nimport { defaultGroup, defaultState, MODAL_CONTENT_MAX_HEIGHT } from './constants';\nimport { DisplayConditionRequest, onDisplayCondition } from './displayConditionController';\nimport { buildUnlayerDisplayCondition, parseUnlayerDisplayCondition } from './nunjucks';\nimport { getSchemaDataPointOptions } from './schemaDataPoints';\nimport { ConditionGroup as ConditionGroupType, DisplayBehavior } 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\nexport interface DisplayConditionModalProps {\n schema?: import('../shared/schema').SchemaObject;\n}\n\nexport const DisplayConditionModal = (props: DisplayConditionModalProps) => {\n const [request, setRequest] = useState<DisplayConditionRequest | null>(null);\n const [isOpen, setIsOpen] = useState(false);\n const [state, setState] = useState(defaultState);\n\n const dataPointOptions = useMemo(() => getSchemaDataPointOptions(props.schema), [props.schema]);\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 setState(parsed ?? defaultState());\n setIsOpen(true);\n });\n }, []);\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 condition = buildUnlayerDisplayCondition(state);\n if (condition) {\n request.done(condition);\n } else {\n request.done(null);\n }\n setRequest(null);\n setIsOpen(false);\n }, [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 removeGroup = useCallback((index: number) => {\n setState(prev => ({\n ...prev,\n groups: (() => {\n const nextGroups = prev.groups.filter((_, i) => i !== index);\n if (nextGroups.length === 0) {\n return [defaultGroup()];\n }\n if (index === 0) {\n const { logicalOperator: unusedOp, ...firstGroup } = nextGroups[0];\n return [firstGroup, ...nextGroups.slice(1)];\n }\n return nextGroups;\n })(),\n }));\n }, []);\n\n const addGroup = useCallback(() => {\n setState(prev => ({\n ...prev,\n groups: [...prev.groups, { ...defaultGroup(), logicalOperator: 'and' }],\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 canSave = useMemo(() => {\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 dataPointOptions.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 }, [dataPointOptions, 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>Rules and Conditions</Dialog.Header>\n <Dialog.Content>\n <Flex\n direction=\"column\"\n gap=\"6\"\n style={{\n maxHeight: MODAL_CONTENT_MAX_HEIGHT,\n overflowY: 'auto',\n paddingRight: 8,\n width: '100%',\n }}\n >\n <Flex\n direction=\"column\"\n gap=\"3\"\n style={{\n borderBottom: '1px solid #e0e0e0',\n paddingBottom: 16,\n }}\n >\n <Text size=\"medium\" variant=\"body\" style={{ fontWeight: 'bold' }}>\n When conditions are met:\n </Text>\n <Flex\n direction=\"row\"\n alignItems=\"flex-end\"\n gap=\"3\"\n style={{ flexWrap: 'wrap' }}\n >\n <Text size=\"small\" subdued variant=\"body\" style={{ paddingBottom: 10 }}>\n Select to show or hide selected component.\n </Text>\n <div style={{ width: 160 }}>\n <Combobox.Select\n {...({ disableClearSelection: true } as object)}\n itemToKey={(item: BehaviorOption | null) => item?.value ?? ''}\n itemToString={(item: BehaviorOption | null) =>\n item?.label ?? ''\n }\n items={[...BEHAVIOR_OPTIONS]}\n selectedItem={selectedBehavior}\n onChange={handleBehaviorChange}\n >\n <Combobox.SelectTrigger label=\"\" placeholder=\"Select...\" />\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 </Flex>\n <div style={{ padding: 8 }}>\n <ConditionGroupsSection\n dataPointOptions={dataPointOptions}\n groups={state.groups}\n onAddGroup={addGroup}\n onRemoveGroup={removeGroup}\n onUpdateGroup={updateGroup}\n />\n </div>\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","ConditionGroupsSection","defaultGroup","defaultState","MODAL_CONTENT_MAX_HEIGHT","onDisplayCondition","buildUnlayerDisplayCondition","parseUnlayerDisplayCondition","getSchemaDataPointOptions","BEHAVIOR_OPTIONS","label","value","NUMERIC_VALUE_RE","DisplayConditionModal","props","request","setRequest","isOpen","setIsOpen","state","setState","dataPointOptions","schema","portalTarget","document","body","nextRequest","existing","data","parsed","handleClose","handleSave","condition","done","updateGroup","index","group","prev","next","groups","removeGroup","nextGroups","filter","_","i","length","logicalOperator","unusedOp","firstGroup","slice","addGroup","selectedBehavior","find","opt","behavior","handleBehaviorChange","item","canSave","conditions","dataPointKey","operator","trimmedValue","trim","fieldType","fullKey","test","open","onClose","size","Header","Content","direction","gap","style","maxHeight","overflowY","paddingRight","width","borderBottom","paddingBottom","variant","fontWeight","alignItems","flexWrap","subdued","div","Select","disableClearSelection","itemToKey","itemToString","items","selectedItem","onChange","SelectTrigger","placeholder","List","map","Item","padding","onAddGroup","onRemoveGroup","onUpdateGroup","Footer","sticky","justifyContent","appearance","onClick","disabled"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@servicetitan/dte-unlayer",
3
- "version": "0.96.0",
3
+ "version": "0.97.0",
4
4
  "description": "",
5
5
  "main": "./dist/index.js",
6
6
  "typings": "./dist/index.d.ts",
@@ -13,7 +13,6 @@ const BEHAVIOR_OPTIONS = [
13
13
  { label: 'Hide', value: 'hide' },
14
14
  ] as const;
15
15
  const NUMERIC_VALUE_RE = /^-?(?:\d+\.?\d*|\.\d+)$/;
16
-
17
16
  type BehaviorOption = (typeof BEHAVIOR_OPTIONS)[number];
18
17
 
19
18
  export interface DisplayConditionModalProps {
@@ -45,12 +44,9 @@ export const DisplayConditionModal = (props: DisplayConditionModalProps) => {
45
44
  }, []);
46
45
 
47
46
  const handleClose = useCallback(() => {
48
- if (request) {
49
- request.done(request.data ?? null);
50
- setRequest(null);
51
- }
47
+ setRequest(null);
52
48
  setIsOpen(false);
53
- }, [request]);
49
+ }, []);
54
50
 
55
51
  const handleSave = useCallback(() => {
56
52
  if (!request) {
@@ -147,6 +143,7 @@ export const DisplayConditionModal = (props: DisplayConditionModalProps) => {
147
143
  style={{
148
144
  maxHeight: MODAL_CONTENT_MAX_HEIGHT,
149
145
  overflowY: 'auto',
146
+ paddingRight: 8,
150
147
  width: '100%',
151
148
  }}
152
149
  >
@@ -163,15 +160,15 @@ export const DisplayConditionModal = (props: DisplayConditionModalProps) => {
163
160
  </Text>
164
161
  <Flex
165
162
  direction="row"
166
- alignItems="center"
163
+ alignItems="flex-end"
167
164
  gap="3"
168
165
  style={{ flexWrap: 'wrap' }}
169
166
  >
170
- <Text size="small" subdued variant="body">
167
+ <Text size="small" subdued variant="body" style={{ paddingBottom: 10 }}>
171
168
  Select to show or hide selected component.
172
169
  </Text>
173
170
  <div style={{ width: 160 }}>
174
- <Combobox
171
+ <Combobox.Select
175
172
  {...({ disableClearSelection: true } as object)}
176
173
  itemToKey={(item: BehaviorOption | null) => item?.value ?? ''}
177
174
  itemToString={(item: BehaviorOption | null) =>
@@ -181,10 +178,7 @@ export const DisplayConditionModal = (props: DisplayConditionModalProps) => {
181
178
  selectedItem={selectedBehavior}
182
179
  onChange={handleBehaviorChange}
183
180
  >
184
- <Combobox.SelectTrigger
185
- label="Behavior"
186
- placeholder="Select..."
187
- />
181
+ <Combobox.SelectTrigger label="" placeholder="Select..." />
188
182
  <Combobox.Content>
189
183
  {({ items }: { items: BehaviorOption[] }) => (
190
184
  <Combobox.List>
@@ -200,30 +194,35 @@ export const DisplayConditionModal = (props: DisplayConditionModalProps) => {
200
194
  </Combobox.List>
201
195
  )}
202
196
  </Combobox.Content>
203
- </Combobox>
197
+ </Combobox.Select>
204
198
  </div>
205
199
  </Flex>
206
200
  </Flex>
207
- <ConditionGroupsSection
208
- dataPointOptions={dataPointOptions}
209
- groups={state.groups}
210
- onAddGroup={addGroup}
211
- onRemoveGroup={removeGroup}
212
- onUpdateGroup={updateGroup}
213
- />
214
- {!canSave && (
215
- <Text size="small" subdued variant="body">
216
- Complete each condition before saving. Data point and value are
217
- required, and numeric fields must contain a valid number.
218
- </Text>
219
- )}
201
+ <div style={{ padding: 8 }}>
202
+ <ConditionGroupsSection
203
+ dataPointOptions={dataPointOptions}
204
+ groups={state.groups}
205
+ onAddGroup={addGroup}
206
+ onRemoveGroup={removeGroup}
207
+ onUpdateGroup={updateGroup}
208
+ />
209
+ </div>
220
210
  </Flex>
221
211
  </Dialog.Content>
222
212
  <Dialog.Footer sticky>
223
- <Dialog.CancelButton onClick={handleClose}>Cancel</Dialog.CancelButton>
224
- <Button appearance="primary" disabled={!canSave} onClick={handleSave}>
225
- Save Changes
226
- </Button>
213
+ <Flex
214
+ justifyContent="flex-end"
215
+ alignItems="center"
216
+ gap="3"
217
+ style={{ width: '100%' }}
218
+ >
219
+ <Button appearance="secondary" onClick={handleClose}>
220
+ Cancel
221
+ </Button>
222
+ <Button appearance="primary" disabled={!canSave} onClick={handleSave}>
223
+ Save Changes
224
+ </Button>
225
+ </Flex>
227
226
  </Dialog.Footer>
228
227
  </Dialog>,
229
228
  portalTarget,