@seamapi/react 3.0.2 → 3.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.md +2 -2
  2. package/dist/elements.js +7485 -7348
  3. package/dist/elements.js.map +1 -1
  4. package/dist/index.css +3 -0
  5. package/dist/index.css.map +1 -1
  6. package/dist/index.min.css +1 -1
  7. package/dist/index.min.css.map +1 -1
  8. package/lib/seam/access-codes/use-delete-access-code.js +9 -0
  9. package/lib/seam/access-codes/use-delete-access-code.js.map +1 -1
  10. package/lib/seam/components/AccessCodeDetails/AccessCodeDetails.d.ts +5 -2
  11. package/lib/seam/components/AccessCodeDetails/AccessCodeDetails.js +65 -10
  12. package/lib/seam/components/AccessCodeDetails/AccessCodeDetails.js.map +1 -1
  13. package/lib/seam/components/AccessCodeTable/AccessCodeMenu.d.ts +1 -0
  14. package/lib/seam/components/AccessCodeTable/AccessCodeMenu.js +13 -5
  15. package/lib/seam/components/AccessCodeTable/AccessCodeMenu.js.map +1 -1
  16. package/lib/seam/components/AccessCodeTable/AccessCodeRow.d.ts +2 -1
  17. package/lib/seam/components/AccessCodeTable/AccessCodeRow.js +10 -5
  18. package/lib/seam/components/AccessCodeTable/AccessCodeRow.js.map +1 -1
  19. package/lib/seam/components/AccessCodeTable/AccessCodeTable.js +40 -24
  20. package/lib/seam/components/AccessCodeTable/AccessCodeTable.js.map +1 -1
  21. package/lib/seam/components/DeviceDetails/ThermostatDeviceDetails.js +5 -5
  22. package/lib/seam/components/DeviceDetails/ThermostatDeviceDetails.js.map +1 -1
  23. package/lib/seam/thermostats/temperature-bounds.d.ts +1 -2
  24. package/lib/seam/thermostats/temperature-bounds.js +0 -16
  25. package/lib/seam/thermostats/temperature-bounds.js.map +1 -1
  26. package/lib/seam/thermostats/thermostat-device.d.ts +1 -3
  27. package/lib/seam/thermostats/thermostat-device.js.map +1 -1
  28. package/lib/seam/thermostats/use-cool-thermostat.js +0 -2
  29. package/lib/seam/thermostats/use-cool-thermostat.js.map +1 -1
  30. package/lib/seam/thermostats/use-heat-cool-thermostat.js +0 -2
  31. package/lib/seam/thermostats/use-heat-cool-thermostat.js.map +1 -1
  32. package/lib/seam/thermostats/use-heat-thermostat.js +0 -2
  33. package/lib/seam/thermostats/use-heat-thermostat.js.map +1 -1
  34. package/lib/seam/thermostats/use-set-thermostat-off.js +0 -2
  35. package/lib/seam/thermostats/use-set-thermostat-off.js.map +1 -1
  36. package/lib/ui/ClimateSettingForm/set-point-bounds.js +3 -3
  37. package/lib/ui/ClimateSettingForm/set-point-bounds.js.map +1 -1
  38. package/lib/ui/Menu/Menu.d.ts +2 -1
  39. package/lib/ui/Menu/Menu.js +2 -1
  40. package/lib/ui/Menu/Menu.js.map +1 -1
  41. package/lib/version.d.ts +1 -1
  42. package/lib/version.js +1 -1
  43. package/package.json +3 -3
  44. package/src/lib/seam/access-codes/use-delete-access-code.ts +15 -0
  45. package/src/lib/seam/components/AccessCodeDetails/AccessCodeDetails.element.ts +3 -0
  46. package/src/lib/seam/components/AccessCodeDetails/AccessCodeDetails.tsx +169 -71
  47. package/src/lib/seam/components/AccessCodeTable/AccessCodeMenu.tsx +34 -13
  48. package/src/lib/seam/components/AccessCodeTable/AccessCodeRow.tsx +24 -9
  49. package/src/lib/seam/components/AccessCodeTable/AccessCodeTable.tsx +62 -53
  50. package/src/lib/seam/components/DeviceDetails/ThermostatDeviceDetails.tsx +5 -5
  51. package/src/lib/seam/thermostats/temperature-bounds.ts +1 -26
  52. package/src/lib/seam/thermostats/thermostat-device.ts +0 -4
  53. package/src/lib/seam/thermostats/use-cool-thermostat.ts +0 -2
  54. package/src/lib/seam/thermostats/use-heat-cool-thermostat.ts +0 -2
  55. package/src/lib/seam/thermostats/use-heat-thermostat.ts +0 -2
  56. package/src/lib/seam/thermostats/use-set-thermostat-off.ts +0 -2
  57. package/src/lib/ui/ClimateSettingForm/set-point-bounds.ts +3 -3
  58. package/src/lib/ui/Menu/Menu.tsx +3 -0
  59. package/src/lib/version.ts +1 -1
  60. package/src/styles/_seam-table.scss +4 -0
@@ -11,54 +11,75 @@ import { useToggle } from 'lib/ui/use-toggle.js'
11
11
  export interface AccessCodeMenuProps {
12
12
  accessCode: AccessCode
13
13
  onEdit: () => void
14
+ onDeleteSuccess: () => void
14
15
  onViewDetails: () => void
15
16
  disableEditAccessCode: boolean
16
17
  disableDeleteAccessCode: boolean
17
18
  }
18
19
 
19
20
  export function AccessCodeMenu(props: AccessCodeMenuProps): JSX.Element {
21
+ const [deleteConfirmationVisible, toggleDeleteConfirmation] = useToggle()
22
+
20
23
  return (
21
24
  <MoreActionsMenu
22
25
  menuProps={{
23
26
  backgroundProps: {
24
27
  className: 'seam-table-action-menu',
25
28
  },
29
+ onClose: () => {
30
+ if (deleteConfirmationVisible) {
31
+ toggleDeleteConfirmation()
32
+ }
33
+ },
26
34
  }}
27
35
  >
28
- <Content {...props} />
36
+ <Content
37
+ {...props}
38
+ deleteConfirmationVisible={deleteConfirmationVisible}
39
+ toggleDeleteConfirmation={toggleDeleteConfirmation}
40
+ />
29
41
  </MoreActionsMenu>
30
42
  )
31
43
  }
32
44
 
45
+ interface ContentProps extends AccessCodeMenuProps {
46
+ deleteConfirmationVisible: boolean
47
+ toggleDeleteConfirmation: () => void
48
+ }
49
+
33
50
  function Content({
34
51
  accessCode,
35
52
  onViewDetails,
36
53
  disableEditAccessCode,
37
54
  disableDeleteAccessCode,
38
55
  onEdit,
39
- }: AccessCodeMenuProps): JSX.Element {
40
- const [deleteConfirmationVisible, toggleDeleteConfirmation] = useToggle()
41
-
56
+ onDeleteSuccess,
57
+ deleteConfirmationVisible,
58
+ toggleDeleteConfirmation,
59
+ }: ContentProps): JSX.Element {
42
60
  const deleteAccessCode = useDeleteAccessCode()
61
+ const isAccessCodeBeingRemoved = accessCode.status === 'removing'
43
62
 
44
63
  if (deleteConfirmationVisible) {
45
64
  return (
46
65
  <div className='seam-delete-confirmation'>
47
66
  <span>{t.deleteCodeConfirmation}</span>
48
67
  <div className='seam-actions'>
49
- <Button
50
- onClick={toggleDeleteConfirmation}
51
- disabled={deleteAccessCode.isPending}
52
- >
68
+ <Button disabled={deleteAccessCode.isPending}>
53
69
  {t.cancelDelete}
54
70
  </Button>
55
71
  <Button
56
72
  variant='solid'
57
73
  disabled={deleteAccessCode.isPending}
58
74
  onClick={() => {
59
- deleteAccessCode.mutate({
60
- access_code_id: accessCode.access_code_id,
61
- })
75
+ deleteAccessCode.mutate(
76
+ {
77
+ access_code_id: accessCode.access_code_id,
78
+ },
79
+ {
80
+ onSuccess: onDeleteSuccess,
81
+ }
82
+ )
62
83
  }}
63
84
  >
64
85
  {t.confirmDelete}
@@ -84,10 +105,10 @@ function Content({
84
105
  </MenuItem>
85
106
  <div className='seam-divider' />
86
107
  <MenuItem onClick={onViewDetails}>{t.viewCodeDetails}</MenuItem>
87
- {!disableEditAccessCode && (
108
+ {!disableEditAccessCode && !isAccessCodeBeingRemoved && (
88
109
  <MenuItem onClick={onEdit}>{t.editCode}</MenuItem>
89
110
  )}
90
- {!disableDeleteAccessCode && (
111
+ {!disableDeleteAccessCode && !isAccessCodeBeingRemoved && (
91
112
  <>
92
113
  <div className='seam-divider' />
93
114
  <MenuItem
@@ -1,4 +1,5 @@
1
1
  import type { AccessCode } from '@seamapi/types/connect'
2
+ import classNames from 'classnames'
2
3
 
3
4
  import { ExclamationCircleOutlineIcon } from 'lib/icons/ExclamationCircleOutline.js'
4
5
  import { TriangleWarningOutlineIcon } from 'lib/icons/TriangleWarningOutline.js'
@@ -13,6 +14,7 @@ export interface AccessCodeRowProps {
13
14
  accessCode: AccessCode
14
15
  onClick: () => void
15
16
  onEdit: () => void
17
+ onDeleteSuccess: () => void
16
18
  disableEditAccessCode: boolean
17
19
  disableDeleteAccessCode: boolean
18
20
  }
@@ -21,25 +23,37 @@ export function AccessCodeRow({
21
23
  onClick,
22
24
  accessCode,
23
25
  onEdit,
26
+ onDeleteSuccess,
24
27
  disableEditAccessCode,
25
28
  disableDeleteAccessCode,
26
29
  }: AccessCodeRowProps): JSX.Element {
30
+ const isAccessCodeBeingRemoved = accessCode.status === 'removing'
31
+
27
32
  const errorCount = accessCode.errors.length
28
33
  const warningCount = accessCode.warnings.length
29
- const isPlural = errorCount === 0 || errorCount > 1
30
- const errorIconTitle = isPlural
31
- ? `${errorCount} ${t.codeIssues}`
32
- : `${errorCount} ${t.codeIssue}`
33
- const warningIconTitle = isPlural
34
- ? `${warningCount} ${t.codeIssues}`
35
- : `${warningCount} ${t.codeIssue}`
34
+ const errorIconTitle =
35
+ errorCount === 0 || errorCount > 1
36
+ ? `${errorCount} ${t.codeIssues}`
37
+ : `${errorCount} ${t.codeIssue}`
38
+ const warningIconTitle =
39
+ warningCount === 0 || warningCount > 1
40
+ ? `${warningCount} ${t.codeIssues}`
41
+ : `${warningCount} ${t.codeIssue}`
36
42
 
37
43
  return (
38
44
  <TableRow onClick={onClick}>
39
- <TableCell className='seam-icon-cell'>
45
+ <TableCell
46
+ className={classNames('seam-icon-cell', {
47
+ 'seam-grayed-out': isAccessCodeBeingRemoved,
48
+ })}
49
+ >
40
50
  <AccessCodeMainIcon accessCode={accessCode} />
41
51
  </TableCell>
42
- <TableCell className='seam-name-cell'>
52
+ <TableCell
53
+ className={classNames('seam-name-cell', {
54
+ 'seam-grayed-out': isAccessCodeBeingRemoved,
55
+ })}
56
+ >
43
57
  <Title className='seam-truncated-text'>{accessCode.name}</Title>
44
58
  <CodeDetails accessCode={accessCode} />
45
59
  </TableCell>
@@ -63,6 +77,7 @@ export function AccessCodeRow({
63
77
  <AccessCodeMenu
64
78
  accessCode={accessCode}
65
79
  onEdit={onEdit}
80
+ onDeleteSuccess={onDeleteSuccess}
66
81
  onViewDetails={onClick}
67
82
  disableDeleteAccessCode={disableDeleteAccessCode}
68
83
  disableEditAccessCode={disableEditAccessCode}
@@ -1,6 +1,6 @@
1
1
  import type { AccessCode } from '@seamapi/types/connect'
2
2
  import classNames from 'classnames'
3
- import { useCallback, useMemo, useState } from 'react'
3
+ import { useCallback, useEffect, useMemo, useState } from 'react'
4
4
 
5
5
  import { compareByCreatedAtDesc } from 'lib/dates.js'
6
6
  import { AddIcon } from 'lib/icons/Add.js'
@@ -48,20 +48,6 @@ export interface AccessCodeTableProps extends CommonProps {
48
48
  heading?: string | null
49
49
  }
50
50
 
51
- const defaultAccessCodeFilter = (
52
- accessCode: AccessCode,
53
- searchInputValue: string
54
- ): boolean => {
55
- const value = searchInputValue.trim().toLowerCase()
56
- if (value === '') return true
57
- const name = accessCode.name ?? ''
58
- const code = accessCode.code ?? ''
59
- return (
60
- name.trim().toLowerCase().includes(value) ||
61
- code.trim().toLowerCase().includes(value)
62
- )
63
- }
64
-
65
51
  export function AccessCodeTable({
66
52
  deviceId,
67
53
  disableSearch = false,
@@ -127,11 +113,21 @@ export function AccessCodeTable({
127
113
  )
128
114
 
129
115
  const [accessCodeResult, setAccessCodeResult] = useState<
130
- 'created' | 'updated' | null
116
+ 'created' | 'updated' | 'deleted' | null
131
117
  >(null)
118
+ const [snackbarMessage, setSnackbarMessage] = useState<string>('')
132
119
 
133
- const accessCodeResultMessage =
134
- accessCodeResult === 'created' ? t.accesCodeCreated : t.accesCodeUpdated
120
+ const handleAccessCodeDeleteSuccess = useCallback((): void => {
121
+ setAccessCodeResult('deleted')
122
+ }, [setAccessCodeResult])
123
+
124
+ // Circumvent Snackbar bug that causes it to switch to default message
125
+ // while the dismiss animation is playing
126
+ useEffect(() => {
127
+ if (accessCodeResult !== null) {
128
+ setSnackbarMessage(accessCodeResultToMessage(accessCodeResult))
129
+ }
130
+ }, [accessCodeResult])
135
131
 
136
132
  if (selectedEditAccessCodeId != null) {
137
133
  return (
@@ -159,38 +155,22 @@ export function AccessCodeTable({
159
155
 
160
156
  if (selectedViewAccessCodeId != null) {
161
157
  return (
162
- <>
163
- <Snackbar
164
- variant='success'
165
- message={accessCodeResultMessage}
166
- visible={accessCodeResult != null}
167
- autoDismiss
168
- onClose={() => {
169
- setAccessCodeResult(null)
170
- }}
171
- />
172
- <NestedAccessCodeDetails
173
- accessCodeId={selectedViewAccessCodeId}
174
- onEdit={() => {
175
- setSelectedEditAccessCodeId(selectedViewAccessCodeId)
176
- }}
177
- errorFilter={errorFilter}
178
- warningFilter={warningFilter}
179
- disableLockUnlock={disableLockUnlock}
180
- disableCreateAccessCode={disableCreateAccessCode}
181
- disableEditAccessCode={disableEditAccessCode}
182
- disableDeleteAccessCode={disableDeleteAccessCode}
183
- disableResourceIds={disableResourceIds}
184
- disableConnectedAccountInformation={
185
- disableConnectedAccountInformation
186
- }
187
- disableClimateSettingSchedules={disableClimateSettingSchedules}
188
- onBack={() => {
189
- setSelectedViewAccessCodeId(null)
190
- }}
191
- className={className}
192
- />
193
- </>
158
+ <NestedAccessCodeDetails
159
+ accessCodeId={selectedViewAccessCodeId}
160
+ errorFilter={errorFilter}
161
+ warningFilter={warningFilter}
162
+ disableLockUnlock={disableLockUnlock}
163
+ disableCreateAccessCode={disableCreateAccessCode}
164
+ disableEditAccessCode={disableEditAccessCode}
165
+ disableDeleteAccessCode={disableDeleteAccessCode}
166
+ disableResourceIds={disableResourceIds}
167
+ disableConnectedAccountInformation={disableConnectedAccountInformation}
168
+ disableClimateSettingSchedules={disableClimateSettingSchedules}
169
+ onBack={() => {
170
+ setSelectedViewAccessCodeId(null)
171
+ }}
172
+ className={className}
173
+ />
194
174
  )
195
175
  }
196
176
 
@@ -220,7 +200,7 @@ export function AccessCodeTable({
220
200
  <>
221
201
  <Snackbar
222
202
  variant='success'
223
- message={accessCodeResultMessage}
203
+ message={snackbarMessage}
224
204
  visible={accessCodeResult != null}
225
205
  autoDismiss
226
206
  onClose={() => {
@@ -267,6 +247,7 @@ export function AccessCodeTable({
267
247
  accessCodes={filteredAccessCodes}
268
248
  onAccessCodeClick={handleAccessCodeClick}
269
249
  onAccessCodeEdit={handleAccessCodeEdit}
250
+ onAccessCodeDeleteSuccess={handleAccessCodeDeleteSuccess}
270
251
  errorFilter={errorFilter}
271
252
  warningFilter={warningFilter}
272
253
  disableEditAccessCode={disableEditAccessCode}
@@ -295,6 +276,7 @@ function Content(props: {
295
276
  accessCodes: AccessCode[]
296
277
  onAccessCodeClick: (accessCodeId: string) => void
297
278
  onAccessCodeEdit: (accessCodeId: string) => void
279
+ onAccessCodeDeleteSuccess: (accessCodeId: string) => void
298
280
  errorFilter: (error: AccessCode['errors'][number]) => boolean
299
281
  warningFilter: (warning: AccessCode['warnings'][number]) => boolean
300
282
  disableEditAccessCode: boolean
@@ -304,6 +286,7 @@ function Content(props: {
304
286
  accessCodes,
305
287
  onAccessCodeClick,
306
288
  onAccessCodeEdit,
289
+ onAccessCodeDeleteSuccess,
307
290
  errorFilter,
308
291
  warningFilter,
309
292
  disableEditAccessCode,
@@ -350,18 +333,44 @@ function Content(props: {
350
333
  onEdit={() => {
351
334
  onAccessCodeEdit(accessCode.access_code_id)
352
335
  }}
336
+ onDeleteSuccess={() => {
337
+ onAccessCodeDeleteSuccess(accessCode.access_code_id)
338
+ }}
353
339
  />
354
340
  ))}
355
341
  </>
356
342
  )
357
343
  }
358
344
 
345
+ const defaultAccessCodeFilter = (
346
+ accessCode: AccessCode,
347
+ searchInputValue: string
348
+ ): boolean => {
349
+ const value = searchInputValue.trim().toLowerCase()
350
+ if (value === '') return true
351
+ const name = accessCode.name ?? ''
352
+ const code = accessCode.code ?? ''
353
+ return (
354
+ name.trim().toLowerCase().includes(value) ||
355
+ code.trim().toLowerCase().includes(value)
356
+ )
357
+ }
358
+
359
+ const accessCodeResultToMessage = (
360
+ result: 'created' | 'updated' | 'deleted'
361
+ ): string => {
362
+ if (result === 'created') return t.accessCodeCreated
363
+ if (result === 'deleted') return t.accessCodeDeleted
364
+ return t.accessCodeUpdated
365
+ }
366
+
359
367
  const t = {
360
368
  accessCodes: 'Access Codes',
361
369
  noAccessCodesMessage: 'Sorry, no access codes were found',
362
370
  loading: 'Loading access codes',
363
- accesCodeUpdated: 'Access code updated',
364
- accesCodeCreated: 'Access code created',
371
+ accessCodeUpdated: 'Access code updated',
372
+ accessCodeCreated: 'Access code created',
373
+ accessCodeDeleted: 'Access code is being removed',
365
374
  tryAgain: 'Try again',
366
375
  fallbackErrorMessage: 'Access codes could not be loaded',
367
376
  }
@@ -8,7 +8,6 @@ import { NestedClimateSettingScheduleTable } from 'lib/seam/components/ClimateSe
8
8
  import type { NestedSpecificDeviceDetailsProps } from 'lib/seam/components/DeviceDetails/DeviceDetails.js'
9
9
  import { DeviceInfo } from 'lib/seam/components/DeviceDetails/DeviceInfo.js'
10
10
  import { useClimateSettingSchedules } from 'lib/seam/thermostats/climate-setting-schedules/use-climate-setting-schedules.js'
11
- import { getSupportedThermostatModes } from 'lib/seam/thermostats/temperature-bounds.js'
12
11
  import type {
13
12
  HvacModeSetting,
14
13
  ThermostatDevice,
@@ -252,12 +251,13 @@ function ClimateSettingRow({
252
251
  const deviceCoolValue =
253
252
  device.properties.current_climate_setting.cooling_set_point_fahrenheit
254
253
 
255
- const supportedModes = getSupportedThermostatModes(device)
254
+ const availableHvacModes = device.properties.available_hvac_mode_settings
256
255
 
257
256
  const [showSuccess, setShowSuccess] = useState(false)
258
257
  const [mode, setMode] = useState<HvacModeSetting>(
259
- (supportedModes.includes('heat_cool') ? 'heat_cool' : supportedModes[0]) ??
260
- 'off'
258
+ (availableHvacModes.includes('heat_cool')
259
+ ? 'heat_cool'
260
+ : availableHvacModes[0]) ?? 'off'
261
261
  )
262
262
 
263
263
  const [heatValue, setHeatValue] = useState(
@@ -413,7 +413,7 @@ function ClimateSettingRow({
413
413
  <ClimateModeMenu
414
414
  mode={mode}
415
415
  onChange={setMode}
416
- supportedModes={supportedModes}
416
+ supportedModes={availableHvacModes}
417
417
  />
418
418
  </div>
419
419
  </AccordionRow>
@@ -1,7 +1,4 @@
1
- import type {
2
- HvacModeSetting,
3
- ThermostatDevice,
4
- } from 'lib/seam/thermostats/thermostat-device.js'
1
+ import type { HvacModeSetting } from 'lib/seam/thermostats/thermostat-device.js'
5
2
 
6
3
  export interface ControlBounds {
7
4
  mode: Exclude<HvacModeSetting, 'off'>
@@ -54,25 +51,3 @@ export const getTemperatureBounds = (
54
51
  heat: getHeatBounds(controlBounds),
55
52
  cool: getCoolBounds(controlBounds),
56
53
  })
57
-
58
- export const getSupportedThermostatModes = (
59
- device: ThermostatDevice
60
- ): HvacModeSetting[] => {
61
- const allModes: HvacModeSetting[] = ['heat', 'cool', 'heat_cool', 'off']
62
-
63
- return allModes.filter((mode) => {
64
- switch (mode) {
65
- case 'cool':
66
- return device.properties.is_cooling_available ?? false
67
- case 'heat':
68
- return device.properties.is_heating_available ?? false
69
- case 'heat_cool':
70
- return (
71
- (device.properties.is_heating_available ?? false) &&
72
- (device.properties.is_cooling_available ?? false)
73
- )
74
- default:
75
- return true
76
- }
77
- })
78
- }
@@ -10,8 +10,6 @@ export type ThermostatDevice = Omit<Device, 'properties'> & {
10
10
  | 'is_heating'
11
11
  | 'is_cooling'
12
12
  | 'is_fan_running'
13
- | 'is_cooling_available'
14
- | 'is_heating_available'
15
13
  | 'available_hvac_mode_settings'
16
14
  | 'fan_mode_setting'
17
15
  | 'current_climate_setting'
@@ -28,8 +26,6 @@ export type HvacModeSetting =
28
26
 
29
27
  // UPSTREAM: ClimateSetting missing in @seamapi/types.
30
28
  export interface ClimateSetting {
31
- automatic_heating_enabled?: boolean
32
- automatic_cooling_enabled?: boolean
33
29
  hvac_mode_setting?: HvacModeSetting
34
30
  cooling_set_point_celsius?: number
35
31
  heating_set_point_celsius?: number
@@ -100,8 +100,6 @@ const getUpdatedDevice = (
100
100
  current_climate_setting: {
101
101
  ...properties.current_climate_setting,
102
102
  hvac_mode_setting: 'cool',
103
- automatic_heating_enabled: false,
104
- automatic_cooling_enabled: true,
105
103
  heating_set_point_celsius: undefined,
106
104
  heating_set_point_fahrenheit: undefined,
107
105
  cooling_set_point_celsius: getCoolingSetPointCelsius(
@@ -106,8 +106,6 @@ const getUpdatedDevice = (
106
106
  current_climate_setting: {
107
107
  ...properties.current_climate_setting,
108
108
  hvac_mode_setting: 'heat_cool',
109
- automatic_heating_enabled: true,
110
- automatic_cooling_enabled: true,
111
109
  heating_set_point_celsius: getHeatingSetPointCelsius(
112
110
  variables,
113
111
  device
@@ -100,8 +100,6 @@ const getUpdatedDevice = (
100
100
  current_climate_setting: {
101
101
  ...properties.current_climate_setting,
102
102
  hvac_mode_setting: 'heat',
103
- automatic_heating_enabled: true,
104
- automatic_cooling_enabled: false,
105
103
  cooling_set_point_celsius: undefined,
106
104
  cooling_set_point_fahrenheit: undefined,
107
105
  heating_set_point_celsius: getHeatingSetPointCelsius(
@@ -92,8 +92,6 @@ const getUpdatedDevice = (device: Device): Device => {
92
92
  is_heating: false,
93
93
  current_climate_setting: {
94
94
  ...properties.current_climate_setting,
95
- automatic_cooling_enabled: false,
96
- automatic_heating_enabled: false,
97
95
  hvac_mode_setting: 'off',
98
96
  heating_set_point_celsius: undefined,
99
97
  heating_set_point_fahrenheit: undefined,
@@ -13,17 +13,17 @@ export const getSetPointBounds = (device: ThermostatDevice): SetPointBounds => {
13
13
 
14
14
  const setPointBounds: SetPointBounds = {}
15
15
 
16
- if (properties.is_cooling_available) {
16
+ if (properties.available_hvac_mode_settings.includes('cool')) {
17
17
  setPointBounds.minCool = properties.min_cooling_set_point_fahrenheit
18
18
  setPointBounds.maxCool = properties.max_cooling_set_point_fahrenheit
19
19
  }
20
20
 
21
- if (properties.is_heating_available) {
21
+ if (properties.available_hvac_mode_settings.includes('heat')) {
22
22
  setPointBounds.minHeat = properties.min_heating_set_point_fahrenheit
23
23
  setPointBounds.maxHeat = properties.max_heating_set_point_fahrenheit
24
24
  }
25
25
 
26
- if (properties.is_cooling_available && properties.is_heating_available) {
26
+ if (properties.available_hvac_mode_settings.includes('heat_cool')) {
27
27
  setPointBounds.delta = properties.min_heating_cooling_delta_fahrenheit
28
28
  }
29
29
 
@@ -23,6 +23,7 @@ export interface MenuProps extends PropsWithChildren {
23
23
  backgroundProps?: Partial<{
24
24
  className?: string
25
25
  }>
26
+ onClose?: () => void
26
27
  }
27
28
 
28
29
  interface MenuContext {
@@ -40,6 +41,7 @@ export function Menu({
40
41
  children,
41
42
  renderButton,
42
43
  backgroundProps,
44
+ onClose,
43
45
  }: MenuProps): JSX.Element | null {
44
46
  const { Provider } = menuContext
45
47
  const [documentEl, setDocumentEl] = useState<null | HTMLElement>(null)
@@ -59,6 +61,7 @@ export function Menu({
59
61
  }, [setDocumentEl])
60
62
 
61
63
  const handleClose = (): void => {
64
+ onClose?.()
62
65
  setAnchorEl(null)
63
66
  }
64
67
 
@@ -1,3 +1,3 @@
1
- const seamapiReactVersion = '3.0.2'
1
+ const seamapiReactVersion = '3.1.1'
2
2
 
3
3
  export default seamapiReactVersion
@@ -94,6 +94,10 @@
94
94
  margin-right: 8px;
95
95
  }
96
96
  }
97
+
98
+ .seam-grayed-out {
99
+ opacity: 0.4;
100
+ }
97
101
  }
98
102
 
99
103
  .seam-table-action-menu {