@seamapi/react 4.1.0 → 4.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/elements.js +5596 -5352
- package/dist/elements.js.map +1 -1
- package/dist/index.css +100 -12
- package/dist/index.css.map +1 -1
- package/dist/index.min.css +1 -1
- package/dist/index.min.css.map +1 -1
- package/lib/icons/AccessCodeKey.js +1 -1
- package/lib/icons/AccessCodeKey.js.map +1 -1
- package/lib/icons/Add.js +1 -1
- package/lib/icons/Add.js.map +1 -1
- package/lib/icons/ArrowBack.js +1 -1
- package/lib/icons/ArrowBack.js.map +1 -1
- package/lib/icons/ArrowRestart.js +1 -1
- package/lib/icons/ArrowRestart.js.map +1 -1
- package/lib/icons/ArrowRight.js +1 -1
- package/lib/icons/ArrowRight.js.map +1 -1
- package/lib/icons/BatteryLevelCritical.js +1 -1
- package/lib/icons/BatteryLevelCritical.js.map +1 -1
- package/lib/icons/BatteryLevelFull.js +1 -1
- package/lib/icons/BatteryLevelFull.js.map +1 -1
- package/lib/icons/BatteryLevelHigh.js +1 -1
- package/lib/icons/BatteryLevelHigh.js.map +1 -1
- package/lib/icons/BatteryLevelLow.js +1 -1
- package/lib/icons/BatteryLevelLow.js.map +1 -1
- package/lib/icons/BatteryLevelWired.js +1 -1
- package/lib/icons/BatteryLevelWired.js.map +1 -1
- package/lib/icons/Bee.js +1 -1
- package/lib/icons/Bee.js.map +1 -1
- package/lib/icons/Check.js +1 -1
- package/lib/icons/Check.js.map +1 -1
- package/lib/icons/CheckBlack.js +1 -1
- package/lib/icons/CheckBlack.js.map +1 -1
- package/lib/icons/CheckGreen.js +1 -1
- package/lib/icons/CheckGreen.js.map +1 -1
- package/lib/icons/CheckboxBlank.js +1 -1
- package/lib/icons/CheckboxBlank.js.map +1 -1
- package/lib/icons/CheckboxFilled.js +1 -1
- package/lib/icons/CheckboxFilled.js.map +1 -1
- package/lib/icons/ChevronDown.js +1 -1
- package/lib/icons/ChevronDown.js.map +1 -1
- package/lib/icons/ChevronRight.js +1 -1
- package/lib/icons/ChevronRight.js.map +1 -1
- package/lib/icons/ChevronWide.js +1 -1
- package/lib/icons/ChevronWide.js.map +1 -1
- package/lib/icons/ClimateSettingSchedule.js +1 -1
- package/lib/icons/ClimateSettingSchedule.js.map +1 -1
- package/lib/icons/Clock.js +1 -1
- package/lib/icons/Clock.js.map +1 -1
- package/lib/icons/Close.js +1 -1
- package/lib/icons/Close.js.map +1 -1
- package/lib/icons/CloseWhite.js +1 -1
- package/lib/icons/CloseWhite.js.map +1 -1
- package/lib/icons/Copy.js +1 -1
- package/lib/icons/Copy.js.map +1 -1
- package/lib/icons/DotsEllipsisMore.js +1 -1
- package/lib/icons/DotsEllipsisMore.js.map +1 -1
- package/lib/icons/Edit.js +2 -4
- package/lib/icons/Edit.js.map +1 -1
- package/lib/icons/ExclamationCircle.js +1 -1
- package/lib/icons/ExclamationCircle.js.map +1 -1
- package/lib/icons/ExclamationCircleOutline.js +1 -1
- package/lib/icons/ExclamationCircleOutline.js.map +1 -1
- package/lib/icons/Fan.js +1 -1
- package/lib/icons/Fan.js.map +1 -1
- package/lib/icons/FanOutline.js +1 -1
- package/lib/icons/FanOutline.js.map +1 -1
- package/lib/icons/Info.js +1 -1
- package/lib/icons/Info.js.map +1 -1
- package/lib/icons/InfoBlue.js +1 -1
- package/lib/icons/InfoBlue.js.map +1 -1
- package/lib/icons/InfoDark.js +1 -1
- package/lib/icons/InfoDark.js.map +1 -1
- package/lib/icons/LockLocked.js +1 -1
- package/lib/icons/LockLocked.js.map +1 -1
- package/lib/icons/LockUnlocked.js +1 -1
- package/lib/icons/LockUnlocked.js.map +1 -1
- package/lib/icons/NoiseLevels.js +1 -1
- package/lib/icons/NoiseLevels.js.map +1 -1
- package/lib/icons/NoiseLevelsRed.js +1 -1
- package/lib/icons/NoiseLevelsRed.js.map +1 -1
- package/lib/icons/Off.js +1 -1
- package/lib/icons/Off.js.map +1 -1
- package/lib/icons/OnlineStatusAccountOffline.js +1 -1
- package/lib/icons/OnlineStatusAccountOffline.js.map +1 -1
- package/lib/icons/OnlineStatusDeviceOffline.js +1 -1
- package/lib/icons/OnlineStatusDeviceOffline.js.map +1 -1
- package/lib/icons/OnlineStatusOnline.js +1 -1
- package/lib/icons/OnlineStatusOnline.js.map +1 -1
- package/lib/icons/RadioChecked.js +1 -1
- package/lib/icons/RadioChecked.js.map +1 -1
- package/lib/icons/RadioUnchecked.js +1 -1
- package/lib/icons/RadioUnchecked.js.map +1 -1
- package/lib/icons/Seam.js +1 -1
- package/lib/icons/Seam.js.map +1 -1
- package/lib/icons/Search.js +1 -1
- package/lib/icons/Search.js.map +1 -1
- package/lib/icons/TemperatureAdd.js +1 -1
- package/lib/icons/TemperatureAdd.js.map +1 -1
- package/lib/icons/TemperatureSubtract.js +1 -1
- package/lib/icons/TemperatureSubtract.js.map +1 -1
- package/lib/icons/ThermostatCool.js +1 -1
- package/lib/icons/ThermostatCool.js.map +1 -1
- package/lib/icons/ThermostatCoolLarge.js +1 -1
- package/lib/icons/ThermostatCoolLarge.js.map +1 -1
- package/lib/icons/ThermostatHeat.js +1 -1
- package/lib/icons/ThermostatHeat.js.map +1 -1
- package/lib/icons/ThermostatHeatCool.js +1 -1
- package/lib/icons/ThermostatHeatCool.js.map +1 -1
- package/lib/icons/ThermostatHeatLarge.js +1 -1
- package/lib/icons/ThermostatHeatLarge.js.map +1 -1
- package/lib/icons/ThermostatOff.js +1 -1
- package/lib/icons/ThermostatOff.js.map +1 -1
- package/lib/icons/TriangleWarning.js +1 -1
- package/lib/icons/TriangleWarning.js.map +1 -1
- package/lib/icons/TriangleWarningOutline.js +1 -1
- package/lib/icons/TriangleWarningOutline.js.map +1 -1
- package/lib/seam/components/DeviceDetails/DeviceDetails.js +15 -3
- package/lib/seam/components/DeviceDetails/DeviceDetails.js.map +1 -1
- package/lib/seam/components/DeviceDetails/DeviceInfo.js +1 -1
- package/lib/seam/components/DeviceDetails/DeviceInfo.js.map +1 -1
- package/lib/seam/components/DeviceDetails/LockDeviceDetails.d.ts +2 -1
- package/lib/seam/components/DeviceDetails/LockDeviceDetails.js +3 -2
- package/lib/seam/components/DeviceDetails/LockDeviceDetails.js.map +1 -1
- package/lib/seam/components/DeviceDetails/NoiseSensorDeviceDetails.d.ts +2 -1
- package/lib/seam/components/DeviceDetails/NoiseSensorDeviceDetails.js +3 -2
- package/lib/seam/components/DeviceDetails/NoiseSensorDeviceDetails.js.map +1 -1
- package/lib/seam/components/DeviceDetails/ThermostatDeviceDetails.d.ts +2 -1
- package/lib/seam/components/DeviceDetails/ThermostatDeviceDetails.js +2 -2
- package/lib/seam/components/DeviceDetails/ThermostatDeviceDetails.js.map +1 -1
- package/lib/seam/components/SeamEditableDeviceName/SeamEditableDeviceName.d.ts +8 -0
- package/lib/seam/components/SeamEditableDeviceName/SeamEditableDeviceName.js +85 -0
- package/lib/seam/components/SeamEditableDeviceName/SeamEditableDeviceName.js.map +1 -0
- package/lib/seam/devices/use-update-device-name.d.ts +8 -0
- package/lib/seam/devices/use-update-device-name.js +43 -0
- package/lib/seam/devices/use-update-device-name.js.map +1 -0
- package/lib/ui/Snackbar/Snackbar.js +1 -1
- package/lib/ui/Snackbar/Snackbar.js.map +1 -1
- package/lib/ui/device/BatteryStatusIndicator.js +4 -4
- package/lib/ui/device/BatteryStatusIndicator.js.map +1 -1
- package/lib/ui/device/OnlineStatus.js +3 -3
- package/lib/ui/device/OnlineStatus.js.map +1 -1
- package/lib/ui/thermostat/ClimateModeMenu.js +1 -1
- package/lib/ui/thermostat/ClimateModeMenu.js.map +1 -1
- package/lib/ui/thermostat/ClimateSettingStatus.js +7 -1
- package/lib/ui/thermostat/ClimateSettingStatus.js.map +1 -1
- package/lib/ui/thermostat/TemperatureControlGroup.js +1 -1
- package/lib/ui/thermostat/TemperatureControlGroup.js.map +1 -1
- package/lib/ui/thermostat/ThermostatCard.d.ts +2 -1
- package/lib/ui/thermostat/ThermostatCard.js +4 -3
- package/lib/ui/thermostat/ThermostatCard.js.map +1 -1
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +2 -2
- package/src/lib/icons/AccessCodeKey.tsx +3 -2
- package/src/lib/icons/Add.tsx +3 -2
- package/src/lib/icons/ArrowBack.tsx +3 -2
- package/src/lib/icons/ArrowRestart.tsx +3 -2
- package/src/lib/icons/ArrowRight.tsx +3 -2
- package/src/lib/icons/BatteryLevelCritical.tsx +3 -2
- package/src/lib/icons/BatteryLevelFull.tsx +3 -2
- package/src/lib/icons/BatteryLevelHigh.tsx +3 -2
- package/src/lib/icons/BatteryLevelLow.tsx +3 -2
- package/src/lib/icons/BatteryLevelWired.tsx +3 -2
- package/src/lib/icons/Bee.tsx +3 -2
- package/src/lib/icons/Check.tsx +3 -2
- package/src/lib/icons/CheckBlack.tsx +3 -2
- package/src/lib/icons/CheckGreen.tsx +3 -2
- package/src/lib/icons/CheckboxBlank.tsx +3 -2
- package/src/lib/icons/CheckboxFilled.tsx +3 -2
- package/src/lib/icons/ChevronDown.tsx +3 -2
- package/src/lib/icons/ChevronRight.tsx +3 -2
- package/src/lib/icons/ChevronWide.tsx +3 -2
- package/src/lib/icons/ClimateSettingSchedule.tsx +3 -2
- package/src/lib/icons/Clock.tsx +3 -2
- package/src/lib/icons/Close.tsx +3 -2
- package/src/lib/icons/CloseWhite.tsx +3 -2
- package/src/lib/icons/Copy.tsx +3 -2
- package/src/lib/icons/DotsEllipsisMore.tsx +3 -2
- package/src/lib/icons/Edit.tsx +9 -21
- package/src/lib/icons/ExclamationCircle.tsx +3 -2
- package/src/lib/icons/ExclamationCircleOutline.tsx +3 -2
- package/src/lib/icons/Fan.tsx +3 -2
- package/src/lib/icons/FanOutline.tsx +3 -2
- package/src/lib/icons/Info.tsx +3 -2
- package/src/lib/icons/InfoBlue.tsx +3 -2
- package/src/lib/icons/InfoDark.tsx +3 -2
- package/src/lib/icons/LockLocked.tsx +3 -2
- package/src/lib/icons/LockUnlocked.tsx +3 -2
- package/src/lib/icons/NoiseLevels.tsx +3 -2
- package/src/lib/icons/NoiseLevelsRed.tsx +3 -2
- package/src/lib/icons/Off.tsx +3 -2
- package/src/lib/icons/OnlineStatusAccountOffline.tsx +3 -2
- package/src/lib/icons/OnlineStatusDeviceOffline.tsx +3 -2
- package/src/lib/icons/OnlineStatusOnline.tsx +3 -2
- package/src/lib/icons/RadioChecked.tsx +3 -2
- package/src/lib/icons/RadioUnchecked.tsx +3 -2
- package/src/lib/icons/Seam.tsx +3 -2
- package/src/lib/icons/Search.tsx +3 -2
- package/src/lib/icons/TemperatureAdd.tsx +3 -2
- package/src/lib/icons/TemperatureSubtract.tsx +3 -2
- package/src/lib/icons/ThermostatCool.tsx +3 -2
- package/src/lib/icons/ThermostatCoolLarge.tsx +3 -2
- package/src/lib/icons/ThermostatHeat.tsx +3 -2
- package/src/lib/icons/ThermostatHeatCool.tsx +3 -2
- package/src/lib/icons/ThermostatHeatLarge.tsx +3 -2
- package/src/lib/icons/ThermostatOff.tsx +3 -2
- package/src/lib/icons/TriangleWarning.tsx +3 -2
- package/src/lib/icons/TriangleWarningOutline.tsx +3 -2
- package/src/lib/seam/components/DeviceDetails/DeviceDetails.tsx +35 -4
- package/src/lib/seam/components/DeviceDetails/DeviceInfo.tsx +3 -1
- package/src/lib/seam/components/DeviceDetails/LockDeviceDetails.tsx +9 -1
- package/src/lib/seam/components/DeviceDetails/NoiseSensorDeviceDetails.tsx +8 -1
- package/src/lib/seam/components/DeviceDetails/ThermostatDeviceDetails.tsx +3 -1
- package/src/lib/seam/components/SeamEditableDeviceName/SeamEditableDeviceName.tsx +208 -0
- package/src/lib/seam/devices/use-update-device-name.ts +93 -0
- package/src/lib/ui/Snackbar/Snackbar.tsx +4 -1
- package/src/lib/ui/device/BatteryStatusIndicator.tsx +4 -4
- package/src/lib/ui/device/OnlineStatus.tsx +3 -3
- package/src/lib/ui/thermostat/ClimateModeMenu.tsx +1 -1
- package/src/lib/ui/thermostat/ClimateSettingStatus.tsx +10 -1
- package/src/lib/ui/thermostat/TemperatureControlGroup.tsx +2 -2
- package/src/lib/ui/thermostat/ThermostatCard.tsx +11 -6
- package/src/lib/version.ts +1 -1
- package/src/styles/_access-code-details.scss +2 -2
- package/src/styles/_alert.scss +1 -0
- package/src/styles/_buttons.scss +6 -8
- package/src/styles/_checkbox.scss +1 -0
- package/src/styles/_device-details.scss +1 -1
- package/src/styles/_icons.scss +2 -0
- package/src/styles/_inputs.scss +1 -1
- package/src/styles/_layout.scss +2 -0
- package/src/styles/_main.scss +2 -0
- package/src/styles/_radio-field.scss +1 -0
- package/src/styles/_seam-editable-device-name.scss +62 -0
- package/src/styles/_seam-table.scss +4 -3
- package/src/styles/_snackbar.scss +6 -0
- package/src/styles/_supported-device-table-manufacturer-keys.scss +1 -0
- package/src/styles/_supported-device-table.scss +1 -0
- package/src/styles/_tables.scss +3 -2
- package/src/styles/_thermostat.scss +26 -0
- package/src/styles/_typography.scss +7 -0
|
@@ -6,6 +6,7 @@ import { LockDeviceDetails } from 'lib/seam/components/DeviceDetails/LockDeviceD
|
|
|
6
6
|
import { NoiseSensorDeviceDetails } from 'lib/seam/components/DeviceDetails/NoiseSensorDeviceDetails.js'
|
|
7
7
|
import { ThermostatDeviceDetails } from 'lib/seam/components/DeviceDetails/ThermostatDeviceDetails.js'
|
|
8
8
|
import { useDevice } from 'lib/seam/devices/use-device.js'
|
|
9
|
+
import { useUpdateDeviceName } from 'lib/seam/devices/use-update-device-name.js'
|
|
9
10
|
import { isLockDevice } from 'lib/seam/locks/lock-device.js'
|
|
10
11
|
import { isNoiseSensorDevice } from 'lib/seam/noise-sensors/noise-sensor-device.js'
|
|
11
12
|
import { isThermostatDevice } from 'lib/seam/thermostats/thermostat-device.js'
|
|
@@ -16,7 +17,6 @@ export interface DeviceDetailsProps extends CommonProps {
|
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
export const NestedDeviceDetails = withRequiredCommonProps(DeviceDetails)
|
|
19
|
-
|
|
20
20
|
export interface NestedSpecificDeviceDetailsProps
|
|
21
21
|
extends Required<Omit<CommonProps, 'onBack' | 'className'>> {
|
|
22
22
|
onBack: (() => void) | undefined
|
|
@@ -42,6 +42,19 @@ export function DeviceDetails({
|
|
|
42
42
|
device_id: deviceId,
|
|
43
43
|
})
|
|
44
44
|
|
|
45
|
+
const { mutate: setDeviceName } = useUpdateDeviceName({
|
|
46
|
+
device_id: deviceId,
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
const updateDeviceName = (newName: string): void => {
|
|
50
|
+
if (device != null) {
|
|
51
|
+
setDeviceName({
|
|
52
|
+
device_id: device.device_id,
|
|
53
|
+
name: newName,
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
45
58
|
if (device == null) {
|
|
46
59
|
return null
|
|
47
60
|
}
|
|
@@ -60,15 +73,33 @@ export function DeviceDetails({
|
|
|
60
73
|
}
|
|
61
74
|
|
|
62
75
|
if (isLockDevice(device)) {
|
|
63
|
-
return
|
|
76
|
+
return (
|
|
77
|
+
<LockDeviceDetails
|
|
78
|
+
device={device}
|
|
79
|
+
onEditName={updateDeviceName}
|
|
80
|
+
{...props}
|
|
81
|
+
/>
|
|
82
|
+
)
|
|
64
83
|
}
|
|
65
84
|
|
|
66
85
|
if (isThermostatDevice(device)) {
|
|
67
|
-
return
|
|
86
|
+
return (
|
|
87
|
+
<ThermostatDeviceDetails
|
|
88
|
+
device={device}
|
|
89
|
+
onEditName={updateDeviceName}
|
|
90
|
+
{...props}
|
|
91
|
+
/>
|
|
92
|
+
)
|
|
68
93
|
}
|
|
69
94
|
|
|
70
95
|
if (isNoiseSensorDevice(device)) {
|
|
71
|
-
return
|
|
96
|
+
return (
|
|
97
|
+
<NoiseSensorDeviceDetails
|
|
98
|
+
device={device}
|
|
99
|
+
onEditName={updateDeviceName}
|
|
100
|
+
{...props}
|
|
101
|
+
/>
|
|
102
|
+
)
|
|
72
103
|
}
|
|
73
104
|
|
|
74
105
|
return null
|
|
@@ -32,7 +32,9 @@ export function DeviceInfo({
|
|
|
32
32
|
<DetailRow label={t.manufacturer}>
|
|
33
33
|
<div className='seam-detail-row-hstack'>
|
|
34
34
|
{device.properties.model.manufacturer_display_name}
|
|
35
|
-
{device.properties.manufacturer === 'ecobee' &&
|
|
35
|
+
{device.properties.manufacturer === 'ecobee' && (
|
|
36
|
+
<BeeIcon style={{ fontSize: '33px' }} />
|
|
37
|
+
)}
|
|
36
38
|
</div>
|
|
37
39
|
</DetailRow>
|
|
38
40
|
{!disableConnectedAccountInformation && (
|
|
@@ -6,6 +6,7 @@ import { NestedAccessCodeTable } from 'lib/seam/components/AccessCodeTable/Acces
|
|
|
6
6
|
import type { NestedSpecificDeviceDetailsProps } from 'lib/seam/components/DeviceDetails/DeviceDetails.js'
|
|
7
7
|
import { DeviceInfo } from 'lib/seam/components/DeviceDetails/DeviceInfo.js'
|
|
8
8
|
import { DeviceModel } from 'lib/seam/components/DeviceDetails/DeviceModel.js'
|
|
9
|
+
import { SeamEditableDeviceName } from 'lib/seam/components/SeamEditableDeviceName/SeamEditableDeviceName.js'
|
|
9
10
|
import { deviceErrorFilter, deviceWarningFilter } from 'lib/seam/filters.js'
|
|
10
11
|
import type { LockDevice } from 'lib/seam/locks/lock-device.js'
|
|
11
12
|
import { useToggleLock } from 'lib/seam/locks/use-toggle-lock.js'
|
|
@@ -19,6 +20,7 @@ import { useToggle } from 'lib/ui/use-toggle.js'
|
|
|
19
20
|
|
|
20
21
|
interface LockDeviceDetailsProps extends NestedSpecificDeviceDetailsProps {
|
|
21
22
|
device: LockDevice
|
|
23
|
+
onEditName?: (newName: string) => void | Promise<void>
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
export function LockDeviceDetails({
|
|
@@ -33,6 +35,7 @@ export function LockDeviceDetails({
|
|
|
33
35
|
disableConnectedAccountInformation,
|
|
34
36
|
onBack,
|
|
35
37
|
className,
|
|
38
|
+
onEditName,
|
|
36
39
|
}: LockDeviceDetailsProps): JSX.Element | null {
|
|
37
40
|
const [accessCodesOpen, toggleAccessCodesOpen] = useToggle()
|
|
38
41
|
const toggleLock = useToggleLock()
|
|
@@ -95,7 +98,12 @@ export function LockDeviceDetails({
|
|
|
95
98
|
</div>
|
|
96
99
|
<div className='seam-info'>
|
|
97
100
|
<span className='seam-label'>{t.device}</span>
|
|
98
|
-
<
|
|
101
|
+
<SeamEditableDeviceName
|
|
102
|
+
tagName='h4'
|
|
103
|
+
value={device.properties.name}
|
|
104
|
+
className='seam-device-name'
|
|
105
|
+
onEdit={onEditName}
|
|
106
|
+
/>
|
|
99
107
|
<div className='seam-properties'>
|
|
100
108
|
<span className='seam-label'>{t.status}:</span>{' '}
|
|
101
109
|
<OnlineStatus device={device} />
|
|
@@ -4,6 +4,7 @@ import { useState } from 'react'
|
|
|
4
4
|
import type { NestedSpecificDeviceDetailsProps } from 'lib/seam/components/DeviceDetails/DeviceDetails.js'
|
|
5
5
|
import { DeviceInfo } from 'lib/seam/components/DeviceDetails/DeviceInfo.js'
|
|
6
6
|
import { DeviceModel } from 'lib/seam/components/DeviceDetails/DeviceModel.js'
|
|
7
|
+
import { SeamEditableDeviceName } from 'lib/seam/components/SeamEditableDeviceName/SeamEditableDeviceName.js'
|
|
7
8
|
import type { NoiseSensorDevice } from 'lib/seam/noise-sensors/noise-sensor-device.js'
|
|
8
9
|
import { DeviceImage } from 'lib/ui/device/DeviceImage.js'
|
|
9
10
|
import { NoiseLevelStatus } from 'lib/ui/device/NoiseLevelStatus.js'
|
|
@@ -18,6 +19,7 @@ type TabType = 'details' | 'activity'
|
|
|
18
19
|
interface NoiseSensorDeviceDetailsProps
|
|
19
20
|
extends NestedSpecificDeviceDetailsProps {
|
|
20
21
|
device: NoiseSensorDevice
|
|
22
|
+
onEditName?: (newName: string) => void | Promise<void>
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
export function NoiseSensorDeviceDetails({
|
|
@@ -26,6 +28,7 @@ export function NoiseSensorDeviceDetails({
|
|
|
26
28
|
disableResourceIds,
|
|
27
29
|
onBack,
|
|
28
30
|
className,
|
|
31
|
+
onEditName,
|
|
29
32
|
}: NoiseSensorDeviceDetailsProps): JSX.Element | null {
|
|
30
33
|
const [tab, setTab] = useState<TabType>('details')
|
|
31
34
|
|
|
@@ -45,7 +48,11 @@ export function NoiseSensorDeviceDetails({
|
|
|
45
48
|
</div>
|
|
46
49
|
<div className='seam-info'>
|
|
47
50
|
<span className='seam-label'>{t.noiseSensor}</span>
|
|
48
|
-
<
|
|
51
|
+
<SeamEditableDeviceName
|
|
52
|
+
onEdit={onEditName}
|
|
53
|
+
tagName='h4'
|
|
54
|
+
value={device.properties.name}
|
|
55
|
+
/>
|
|
49
56
|
<div className='seam-properties'>
|
|
50
57
|
<span className='seam-label'>{t.status}:</span>{' '}
|
|
51
58
|
<OnlineStatus device={device} />
|
|
@@ -29,6 +29,7 @@ import { ThermostatCard } from 'lib/ui/thermostat/ThermostatCard.js'
|
|
|
29
29
|
interface ThermostatDeviceDetailsProps
|
|
30
30
|
extends NestedSpecificDeviceDetailsProps {
|
|
31
31
|
device: ThermostatDevice
|
|
32
|
+
onEditName?: (newName: string) => void | Promise<void>
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
export function ThermostatDeviceDetails({
|
|
@@ -37,6 +38,7 @@ export function ThermostatDeviceDetails({
|
|
|
37
38
|
disableConnectedAccountInformation,
|
|
38
39
|
onBack,
|
|
39
40
|
className,
|
|
41
|
+
onEditName,
|
|
40
42
|
}: ThermostatDeviceDetailsProps): JSX.Element | null {
|
|
41
43
|
if (device == null) {
|
|
42
44
|
return null
|
|
@@ -47,7 +49,7 @@ export function ThermostatDeviceDetails({
|
|
|
47
49
|
<ContentHeader title={t.thermostat} onBack={onBack} />
|
|
48
50
|
|
|
49
51
|
<div className='seam-body'>
|
|
50
|
-
<ThermostatCard device={device} />
|
|
52
|
+
<ThermostatCard device={device} onEditName={onEditName} />
|
|
51
53
|
|
|
52
54
|
<div className='seam-thermostat-device-details'>
|
|
53
55
|
<DetailSectionGroup>
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import classNames from 'classnames'
|
|
2
|
+
import {
|
|
3
|
+
type ChangeEvent,
|
|
4
|
+
type HTMLAttributes,
|
|
5
|
+
type KeyboardEvent,
|
|
6
|
+
type PropsWithChildren,
|
|
7
|
+
useCallback,
|
|
8
|
+
useState,
|
|
9
|
+
} from 'react'
|
|
10
|
+
|
|
11
|
+
import { CheckIcon } from 'lib/icons/Check.js'
|
|
12
|
+
import { CloseIcon } from 'lib/icons/Close.js'
|
|
13
|
+
import { EditIcon } from 'lib/icons/Edit.js'
|
|
14
|
+
|
|
15
|
+
export type SeamDeviceNameProps = {
|
|
16
|
+
onEdit?: (newName: string) => void
|
|
17
|
+
editable?: boolean
|
|
18
|
+
tagName?: string
|
|
19
|
+
value: string
|
|
20
|
+
} & HTMLAttributes<HTMLElement>
|
|
21
|
+
|
|
22
|
+
function IconButton(
|
|
23
|
+
props: PropsWithChildren<HTMLAttributes<HTMLButtonElement>>
|
|
24
|
+
): JSX.Element {
|
|
25
|
+
return (
|
|
26
|
+
<button
|
|
27
|
+
{...props}
|
|
28
|
+
className={classNames(
|
|
29
|
+
'seam-editable-device-name-icon-button',
|
|
30
|
+
props.className
|
|
31
|
+
)}
|
|
32
|
+
>
|
|
33
|
+
{props.children}
|
|
34
|
+
</button>
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const fixName = (name: string): string => {
|
|
39
|
+
return name.replace(/\s+/g, ' ').trim()
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
type Result = { type: 'success' } | { type: 'error'; message: string }
|
|
43
|
+
|
|
44
|
+
const isValidName = (name: string): Result => {
|
|
45
|
+
if (name.length < 2) {
|
|
46
|
+
return {
|
|
47
|
+
type: 'error',
|
|
48
|
+
message: 'Name must be at least 2 characters long',
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (name.length > 64) {
|
|
53
|
+
return {
|
|
54
|
+
type: 'error',
|
|
55
|
+
message: 'Name must be at most 64 characters long',
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
type: 'success',
|
|
61
|
+
} as const
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function SeamEditableDeviceName({
|
|
65
|
+
onEdit,
|
|
66
|
+
editable = true,
|
|
67
|
+
tagName,
|
|
68
|
+
value,
|
|
69
|
+
...props
|
|
70
|
+
}: SeamDeviceNameProps): JSX.Element {
|
|
71
|
+
const [editing, setEditing] = useState(false)
|
|
72
|
+
const [errorText, setErrorText] = useState<null | string>(null)
|
|
73
|
+
const [currentValue, setCurrentValue] = useState(value)
|
|
74
|
+
const Tag = (tagName ?? 'span') as 'div'
|
|
75
|
+
|
|
76
|
+
const handleCheck = useCallback(() => {
|
|
77
|
+
const fixedName = fixName(currentValue)
|
|
78
|
+
const valid = isValidName(fixedName)
|
|
79
|
+
|
|
80
|
+
if (valid.type === 'error') {
|
|
81
|
+
setErrorText(valid.message)
|
|
82
|
+
return
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
setEditing(false)
|
|
86
|
+
setCurrentValue(fixedName)
|
|
87
|
+
onEdit?.(fixedName)
|
|
88
|
+
}, [currentValue, onEdit])
|
|
89
|
+
|
|
90
|
+
const handleChange = useCallback(
|
|
91
|
+
(event: ChangeEvent<HTMLInputElement>): void => {
|
|
92
|
+
setCurrentValue(event.target.value)
|
|
93
|
+
setErrorText(null)
|
|
94
|
+
},
|
|
95
|
+
[]
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
const handleCancel = useCallback(() => {
|
|
99
|
+
setEditing(false)
|
|
100
|
+
setCurrentValue(value)
|
|
101
|
+
setErrorText(null)
|
|
102
|
+
}, [value])
|
|
103
|
+
|
|
104
|
+
const handleInputKeydown = useCallback(
|
|
105
|
+
(e: KeyboardEvent<HTMLInputElement>): void => {
|
|
106
|
+
if (e.repeat) return
|
|
107
|
+
|
|
108
|
+
if (e.key === 'Enter') {
|
|
109
|
+
handleCheck()
|
|
110
|
+
} else if (e.key === 'Escape') {
|
|
111
|
+
handleCancel()
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
[handleCheck, handleCancel]
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
return (
|
|
118
|
+
<Tag
|
|
119
|
+
{...props}
|
|
120
|
+
className={classNames('seam-editable-device-name', props.className)}
|
|
121
|
+
>
|
|
122
|
+
<NameView
|
|
123
|
+
editing={editing}
|
|
124
|
+
value={currentValue}
|
|
125
|
+
onChange={handleChange}
|
|
126
|
+
onKeyDown={handleInputKeydown}
|
|
127
|
+
errorText={errorText}
|
|
128
|
+
/>
|
|
129
|
+
|
|
130
|
+
{editable && (
|
|
131
|
+
<span className='seam-editable-device-name-icon-wrapper'>
|
|
132
|
+
<ActionButtons
|
|
133
|
+
editing={editing}
|
|
134
|
+
onEdit={() => {
|
|
135
|
+
setEditing(true)
|
|
136
|
+
}}
|
|
137
|
+
onCancel={handleCancel}
|
|
138
|
+
onCheck={handleCheck}
|
|
139
|
+
/>
|
|
140
|
+
</span>
|
|
141
|
+
)}
|
|
142
|
+
</Tag>
|
|
143
|
+
)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
interface NameViewProps {
|
|
147
|
+
editing: boolean
|
|
148
|
+
value: string
|
|
149
|
+
onChange: (event: ChangeEvent<HTMLInputElement>) => void
|
|
150
|
+
onKeyDown: (event: KeyboardEvent<HTMLInputElement>) => void
|
|
151
|
+
errorText?: string | null
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function NameView(props: NameViewProps): JSX.Element {
|
|
155
|
+
if (!props.editing) {
|
|
156
|
+
return <span>{props.value}</span>
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return (
|
|
160
|
+
<span className='seam-editable-device-name-input-wrapper'>
|
|
161
|
+
<input
|
|
162
|
+
type='text'
|
|
163
|
+
defaultValue={props.value}
|
|
164
|
+
onChange={props.onChange}
|
|
165
|
+
onKeyDown={props.onKeyDown}
|
|
166
|
+
ref={(el) => {
|
|
167
|
+
setTimeout(() => {
|
|
168
|
+
el?.focus()
|
|
169
|
+
}, 0)
|
|
170
|
+
}}
|
|
171
|
+
/>
|
|
172
|
+
|
|
173
|
+
{props.errorText != null && (
|
|
174
|
+
<span className='seam-editable-device-name-input-error'>
|
|
175
|
+
{props.errorText}
|
|
176
|
+
</span>
|
|
177
|
+
)}
|
|
178
|
+
</span>
|
|
179
|
+
)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
interface ActionButtonsProps {
|
|
183
|
+
onEdit: () => void
|
|
184
|
+
onCancel: () => void
|
|
185
|
+
onCheck: () => void
|
|
186
|
+
editing: boolean
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function ActionButtons(props: ActionButtonsProps): JSX.Element {
|
|
190
|
+
if (props.editing) {
|
|
191
|
+
return (
|
|
192
|
+
<>
|
|
193
|
+
<IconButton onClick={props.onCheck}>
|
|
194
|
+
<CheckIcon width='1em' height='1em' viewBox='0 0 24 24' />
|
|
195
|
+
</IconButton>
|
|
196
|
+
<IconButton onClick={props.onCancel}>
|
|
197
|
+
<CloseIcon width='1em' height='1em' viewBox='0 0 24 24' />
|
|
198
|
+
</IconButton>
|
|
199
|
+
</>
|
|
200
|
+
)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return (
|
|
204
|
+
<IconButton onClick={props.onEdit}>
|
|
205
|
+
<EditIcon width='1em' height='1em' viewBox='0 0 24 24' />
|
|
206
|
+
</IconButton>
|
|
207
|
+
)
|
|
208
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
DevicesGetParams,
|
|
3
|
+
DevicesUpdateBody,
|
|
4
|
+
SeamHttpApiError,
|
|
5
|
+
} from '@seamapi/http/connect'
|
|
6
|
+
import type { Device } from '@seamapi/types/connect'
|
|
7
|
+
import {
|
|
8
|
+
useMutation,
|
|
9
|
+
type UseMutationResult,
|
|
10
|
+
useQueryClient,
|
|
11
|
+
} from '@tanstack/react-query'
|
|
12
|
+
|
|
13
|
+
import { NullSeamClientError, useSeamClient } from 'lib/seam/use-seam-client.js'
|
|
14
|
+
|
|
15
|
+
export type UseUpdateDeviceNameParams = never
|
|
16
|
+
|
|
17
|
+
export type UseUpdateDeviceNameData = undefined
|
|
18
|
+
|
|
19
|
+
export type UseUpdateDeviceNameMutationVariables = Pick<
|
|
20
|
+
DevicesUpdateBody,
|
|
21
|
+
'device_id' | 'name'
|
|
22
|
+
>
|
|
23
|
+
|
|
24
|
+
type MutationError = SeamHttpApiError
|
|
25
|
+
|
|
26
|
+
export function useUpdateDeviceName(
|
|
27
|
+
params: DevicesGetParams
|
|
28
|
+
): UseMutationResult<
|
|
29
|
+
UseUpdateDeviceNameData,
|
|
30
|
+
MutationError,
|
|
31
|
+
UseUpdateDeviceNameMutationVariables
|
|
32
|
+
> {
|
|
33
|
+
const { client } = useSeamClient()
|
|
34
|
+
const queryClient = useQueryClient()
|
|
35
|
+
|
|
36
|
+
return useMutation<
|
|
37
|
+
UseUpdateDeviceNameData,
|
|
38
|
+
MutationError,
|
|
39
|
+
UseUpdateDeviceNameMutationVariables
|
|
40
|
+
>({
|
|
41
|
+
mutationFn: async (variables) => {
|
|
42
|
+
if (client === null) throw new NullSeamClientError()
|
|
43
|
+
await client.devices.update(variables)
|
|
44
|
+
},
|
|
45
|
+
onSuccess: (_data, variables) => {
|
|
46
|
+
queryClient.setQueryData<Device | null>(
|
|
47
|
+
['devices', 'get', params],
|
|
48
|
+
(device) => {
|
|
49
|
+
if (device == null) {
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return getUpdatedDevice(
|
|
54
|
+
device,
|
|
55
|
+
variables.name ?? device.properties.name
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
queryClient.setQueryData<Device[]>(
|
|
61
|
+
['devices', 'list', { device_id: variables.device_id }],
|
|
62
|
+
(devices): Device[] => {
|
|
63
|
+
if (devices == null) {
|
|
64
|
+
return []
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return devices.map((device) => {
|
|
68
|
+
if (device.device_id === variables.device_id) {
|
|
69
|
+
return getUpdatedDevice(
|
|
70
|
+
device,
|
|
71
|
+
variables.name ?? device.properties.name
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return device
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
)
|
|
79
|
+
},
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const getUpdatedDevice = (device: Device, name: string): Device => {
|
|
84
|
+
const { properties } = device
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
...device,
|
|
88
|
+
properties: {
|
|
89
|
+
...properties,
|
|
90
|
+
name,
|
|
91
|
+
},
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -67,7 +67,10 @@ export function Snackbar({
|
|
|
67
67
|
'seam-snackbar-visible': automaticVisibility ? !hidden : visible,
|
|
68
68
|
})}
|
|
69
69
|
>
|
|
70
|
-
<
|
|
70
|
+
<span className='seam-snackbar-icon-wrap'>
|
|
71
|
+
<SnackbarIcon variant={variant} />
|
|
72
|
+
</span>
|
|
73
|
+
|
|
71
74
|
<div className='seam-snackbar-message-wrap'>
|
|
72
75
|
<p className='seam-snackbar-message'>{message}</p>
|
|
73
76
|
</div>
|
|
@@ -34,7 +34,7 @@ function Content(props: {
|
|
|
34
34
|
if (status === 'full') {
|
|
35
35
|
return (
|
|
36
36
|
<>
|
|
37
|
-
<BatteryLevelFullIcon />
|
|
37
|
+
<BatteryLevelFullIcon style={{ fontSize: '24px' }} />
|
|
38
38
|
<span className='seam-status-text'>
|
|
39
39
|
{t.full}
|
|
40
40
|
<Percentage level={level} />
|
|
@@ -46,7 +46,7 @@ function Content(props: {
|
|
|
46
46
|
if (status === 'good') {
|
|
47
47
|
return (
|
|
48
48
|
<>
|
|
49
|
-
<BatteryLevelHighIcon />
|
|
49
|
+
<BatteryLevelHighIcon style={{ fontSize: '24px' }} />
|
|
50
50
|
<span className='seam-status-text'>
|
|
51
51
|
{t.high}
|
|
52
52
|
<Percentage level={level} />
|
|
@@ -58,7 +58,7 @@ function Content(props: {
|
|
|
58
58
|
if (status === 'low') {
|
|
59
59
|
return (
|
|
60
60
|
<>
|
|
61
|
-
<BatteryLevelLowIcon />
|
|
61
|
+
<BatteryLevelLowIcon style={{ fontSize: '24px' }} />
|
|
62
62
|
<span className='seam-status-text'>
|
|
63
63
|
{t.low}
|
|
64
64
|
<Percentage level={level} />
|
|
@@ -70,7 +70,7 @@ function Content(props: {
|
|
|
70
70
|
if (status === 'critical') {
|
|
71
71
|
return (
|
|
72
72
|
<>
|
|
73
|
-
<BatteryLevelCriticalIcon />
|
|
73
|
+
<BatteryLevelCriticalIcon style={{ fontSize: '24px' }} />
|
|
74
74
|
<span className='seam-text-danger'>
|
|
75
75
|
{t.critical}
|
|
76
76
|
<Percentage level={level} />
|
|
@@ -25,7 +25,7 @@ export function OnlineStatus(props: OnlineStatusProps): JSX.Element {
|
|
|
25
25
|
function AccountOfflineContent(): JSX.Element {
|
|
26
26
|
return (
|
|
27
27
|
<>
|
|
28
|
-
<OnlineStatusAccountOfflineIcon />
|
|
28
|
+
<OnlineStatusAccountOfflineIcon className='seam-font-24' />
|
|
29
29
|
<span className='seam-text-danger'>{t.accountOffline}</span>
|
|
30
30
|
</>
|
|
31
31
|
)
|
|
@@ -36,7 +36,7 @@ function AccountOnlineContent(props: { isOnline: boolean }): JSX.Element {
|
|
|
36
36
|
if (isOnline) {
|
|
37
37
|
return (
|
|
38
38
|
<>
|
|
39
|
-
<OnlineStatusOnlineIcon />
|
|
39
|
+
<OnlineStatusOnlineIcon className='seam-font-24' />
|
|
40
40
|
<span className='seam-status-text'>{t.online}</span>
|
|
41
41
|
</>
|
|
42
42
|
)
|
|
@@ -44,7 +44,7 @@ function AccountOnlineContent(props: { isOnline: boolean }): JSX.Element {
|
|
|
44
44
|
|
|
45
45
|
return (
|
|
46
46
|
<>
|
|
47
|
-
<OnlineStatusDeviceOfflineIcon />
|
|
47
|
+
<OnlineStatusDeviceOfflineIcon className='seam-font-24' />
|
|
48
48
|
<span className='seam-text-danger'>{t.deviceOffline}</span>
|
|
49
49
|
</>
|
|
50
50
|
)
|
|
@@ -25,7 +25,7 @@ export function ClimateModeMenu({
|
|
|
25
25
|
<div className='seam-climate-mode-menu-button-icon'>
|
|
26
26
|
<ModeIcon mode={mode} />
|
|
27
27
|
</div>
|
|
28
|
-
<ChevronDownIcon />
|
|
28
|
+
<ChevronDownIcon className='seam-climate-mode-menu-button-chevron' />
|
|
29
29
|
</button>
|
|
30
30
|
)}
|
|
31
31
|
verticalOffset={-180}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import classNames from 'classnames'
|
|
2
|
+
|
|
1
3
|
import { ThermostatCoolIcon } from 'lib/icons/ThermostatCool.js'
|
|
2
4
|
import { ThermostatHeatIcon } from 'lib/icons/ThermostatHeat.js'
|
|
3
5
|
import { ThermostatHeatCoolIcon } from 'lib/icons/ThermostatHeatCool.js'
|
|
@@ -46,7 +48,14 @@ function ClimateSettingIcon(props: {
|
|
|
46
48
|
const { mode } = props
|
|
47
49
|
|
|
48
50
|
return (
|
|
49
|
-
<div
|
|
51
|
+
<div
|
|
52
|
+
className={classNames('seam-climate-setting-status-icon', {
|
|
53
|
+
'seam-climate-setting-status-icon-cool': mode === 'cool',
|
|
54
|
+
'seam-climate-setting-status-icon-heat': mode === 'heat',
|
|
55
|
+
'seam-climate-setting-status-icon-heat-cool': mode === 'heat_cool',
|
|
56
|
+
'seam-climate-setting-status-icon-off': mode === 'off',
|
|
57
|
+
})}
|
|
58
|
+
>
|
|
50
59
|
{mode === 'cool' && <ThermostatCoolIcon />}
|
|
51
60
|
{mode === 'heat' && <ThermostatHeatIcon />}
|
|
52
61
|
{mode === 'heat_cool' && <ThermostatHeatCoolIcon />}
|
|
@@ -138,7 +138,7 @@ export function TemperatureControlGroup({
|
|
|
138
138
|
<div className='seam-temperature-control-group'>
|
|
139
139
|
{showHeat && (
|
|
140
140
|
<div className='seam-temperature-control-group-block'>
|
|
141
|
-
<ThermostatHeatLargeIcon />
|
|
141
|
+
<ThermostatHeatLargeIcon className='seam-temperature-control-group-block-thermostat-icon' />
|
|
142
142
|
<TemperatureControl
|
|
143
143
|
variant='heat'
|
|
144
144
|
value={heatValue}
|
|
@@ -151,7 +151,7 @@ export function TemperatureControlGroup({
|
|
|
151
151
|
|
|
152
152
|
{showCool && (
|
|
153
153
|
<div className='seam-temperature-control-group-block'>
|
|
154
|
-
<ThermostatCoolLargeIcon />
|
|
154
|
+
<ThermostatCoolLargeIcon className='seam-temperature-control-group-block-thermostat-icon' />
|
|
155
155
|
<TemperatureControl
|
|
156
156
|
variant='cool'
|
|
157
157
|
value={coolValue}
|
|
@@ -3,6 +3,7 @@ import { useState } from 'react'
|
|
|
3
3
|
|
|
4
4
|
import { FanIcon } from 'lib/icons/Fan.js'
|
|
5
5
|
import { OffIcon } from 'lib/icons/Off.js'
|
|
6
|
+
import { SeamEditableDeviceName } from 'lib/seam/components/SeamEditableDeviceName/SeamEditableDeviceName.js'
|
|
6
7
|
import type { ThermostatDevice } from 'lib/seam/thermostats/thermostat-device.js'
|
|
7
8
|
import { DeviceImage } from 'lib/ui/device/DeviceImage.js'
|
|
8
9
|
import { ClimateSettingStatus } from 'lib/ui/thermostat/ClimateSettingStatus.js'
|
|
@@ -10,17 +11,18 @@ import { Temperature } from 'lib/ui/thermostat/Temperature.js'
|
|
|
10
11
|
|
|
11
12
|
interface ThermostatCardProps {
|
|
12
13
|
device: ThermostatDevice
|
|
14
|
+
onEditName?: (newName: string) => void
|
|
13
15
|
}
|
|
14
16
|
|
|
15
|
-
export function ThermostatCard(
|
|
17
|
+
export function ThermostatCard(props: ThermostatCardProps): JSX.Element {
|
|
16
18
|
return (
|
|
17
19
|
<div className='seam-thermostat-card'>
|
|
18
|
-
<Content device={device} />
|
|
20
|
+
<Content device={props.device} onEditName={props.onEditName} />
|
|
19
21
|
</div>
|
|
20
22
|
)
|
|
21
23
|
}
|
|
22
24
|
|
|
23
|
-
function Content(props:
|
|
25
|
+
function Content(props: ThermostatCardProps): JSX.Element | null {
|
|
24
26
|
const { device } = props
|
|
25
27
|
|
|
26
28
|
const [temperatureUnit, setTemperatureUnit] = useState<
|
|
@@ -50,9 +52,12 @@ function Content(props: { device: ThermostatDevice }): JSX.Element | null {
|
|
|
50
52
|
</div>
|
|
51
53
|
<div className='seam-thermostat-card-details'>
|
|
52
54
|
<div className='seam-thermostat-heading-wrap'>
|
|
53
|
-
<
|
|
54
|
-
{device.properties.name}
|
|
55
|
-
|
|
55
|
+
<SeamEditableDeviceName
|
|
56
|
+
value={device.properties.name}
|
|
57
|
+
tagName='h4'
|
|
58
|
+
className='seam-thermostat-card-heading'
|
|
59
|
+
onEdit={props.onEditName}
|
|
60
|
+
/>
|
|
56
61
|
<button
|
|
57
62
|
onClick={toggleTemperatureUnit}
|
|
58
63
|
className='seam-thermostat-temperature-toggle'
|
package/src/lib/version.ts
CHANGED