@seamapi/react 4.6.0 → 4.7.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 +11530 -9343
- package/dist/elements.js.map +1 -1
- package/dist/index.css +255 -3
- 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/Trash.d.ts +2 -0
- package/lib/icons/Trash.js +5 -0
- package/lib/icons/Trash.js.map +1 -0
- package/lib/seam/components/DeviceDetails/ThermostatDeviceDetails.js +17 -1
- package/lib/seam/components/DeviceDetails/ThermostatDeviceDetails.js.map +1 -1
- package/lib/seam/thermostats/thermostat-device.d.ts +2 -1
- package/lib/seam/thermostats/thermostat-device.js.map +1 -1
- package/lib/seam/thermostats/unit-conversion.d.ts +5 -2
- package/lib/seam/thermostats/unit-conversion.js +5 -2
- package/lib/seam/thermostats/unit-conversion.js.map +1 -1
- package/lib/seam/thermostats/use-create-thermostat-climate-preset.d.ts +6 -0
- package/lib/seam/thermostats/use-create-thermostat-climate-preset.js +55 -0
- package/lib/seam/thermostats/use-create-thermostat-climate-preset.js.map +1 -0
- package/lib/seam/thermostats/use-delete-thermostat-climate-preset.d.ts +6 -0
- package/lib/seam/thermostats/use-delete-thermostat-climate-preset.js +44 -0
- package/lib/seam/thermostats/use-delete-thermostat-climate-preset.js.map +1 -0
- package/lib/seam/thermostats/use-update-thermostat-climate-preset.d.ts +6 -0
- package/lib/seam/thermostats/use-update-thermostat-climate-preset.js +55 -0
- package/lib/seam/thermostats/use-update-thermostat-climate-preset.js.map +1 -0
- package/lib/ui/Button.d.ts +3 -2
- package/lib/ui/Button.js +12 -4
- package/lib/ui/Button.js.map +1 -1
- package/lib/ui/IconButton.d.ts +5 -2
- package/lib/ui/IconButton.js +2 -2
- package/lib/ui/IconButton.js.map +1 -1
- package/lib/ui/Popover/Popover.d.ts +17 -0
- package/lib/ui/Popover/Popover.js +85 -0
- package/lib/ui/Popover/Popover.js.map +1 -0
- package/lib/ui/Popover/PopoverContentPrompt.d.ts +11 -0
- package/lib/ui/Popover/PopoverContentPrompt.js +12 -0
- package/lib/ui/Popover/PopoverContentPrompt.js.map +1 -0
- package/lib/ui/thermostat/ClimateModeMenu.d.ts +7 -2
- package/lib/ui/thermostat/ClimateModeMenu.js +7 -2
- package/lib/ui/thermostat/ClimateModeMenu.js.map +1 -1
- package/lib/ui/thermostat/ClimatePreset.d.ts +8 -0
- package/lib/ui/thermostat/ClimatePreset.js +141 -0
- package/lib/ui/thermostat/ClimatePreset.js.map +1 -0
- package/lib/ui/thermostat/ClimatePresets.d.ts +9 -0
- package/lib/ui/thermostat/ClimatePresets.js +72 -0
- package/lib/ui/thermostat/ClimatePresets.js.map +1 -0
- package/lib/ui/thermostat/FanModeMenu.d.ts +3 -1
- package/lib/ui/thermostat/FanModeMenu.js +5 -2
- package/lib/ui/thermostat/FanModeMenu.js.map +1 -1
- package/lib/ui/thermostat/ThermostatCard.d.ts +1 -0
- package/lib/ui/thermostat/ThermostatCard.js +4 -2
- package/lib/ui/thermostat/ThermostatCard.js.map +1 -1
- package/lib/ui/types.d.ts +3 -3
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +3 -2
- package/src/lib/icons/Trash.tsx +28 -0
- package/src/lib/seam/components/DeviceDetails/ThermostatDeviceDetails.tsx +52 -1
- package/src/lib/seam/thermostats/thermostat-device.ts +4 -0
- package/src/lib/seam/thermostats/unit-conversion.ts +12 -2
- package/src/lib/seam/thermostats/use-create-thermostat-climate-preset.ts +101 -0
- package/src/lib/seam/thermostats/use-delete-thermostat-climate-preset.ts +84 -0
- package/src/lib/seam/thermostats/use-update-thermostat-climate-preset.ts +103 -0
- package/src/lib/ui/Button.tsx +20 -3
- package/src/lib/ui/IconButton.tsx +19 -2
- package/src/lib/ui/Popover/Popover.tsx +168 -0
- package/src/lib/ui/Popover/PopoverContentPrompt.tsx +58 -0
- package/src/lib/ui/thermostat/ClimateModeMenu.tsx +33 -1
- package/src/lib/ui/thermostat/ClimatePreset.tsx +373 -0
- package/src/lib/ui/thermostat/ClimatePresets.tsx +235 -0
- package/src/lib/ui/thermostat/FanModeMenu.tsx +20 -2
- package/src/lib/ui/thermostat/ThermostatCard.tsx +10 -4
- package/src/lib/ui/types.ts +3 -3
- package/src/lib/version.ts +1 -1
- package/src/styles/_buttons.scss +56 -2
- package/src/styles/_main.scss +2 -0
- package/src/styles/_popover.scss +46 -0
- package/src/styles/_spinner.scss +1 -1
- package/src/styles/_thermostat.scss +154 -2
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
SeamHttpApiError,
|
|
3
|
+
ThermostatsUpdateClimatePresetBody,
|
|
4
|
+
} from '@seamapi/http/connect'
|
|
5
|
+
import {
|
|
6
|
+
useMutation,
|
|
7
|
+
type UseMutationResult,
|
|
8
|
+
useQueryClient,
|
|
9
|
+
} from '@tanstack/react-query'
|
|
10
|
+
|
|
11
|
+
import type {
|
|
12
|
+
ThermostatClimatePreset,
|
|
13
|
+
ThermostatDevice,
|
|
14
|
+
} from 'lib/seam/thermostats/thermostat-device.js'
|
|
15
|
+
import { fahrenheitToCelsius } from 'lib/seam/thermostats/unit-conversion.js'
|
|
16
|
+
import { NullSeamClientError, useSeamClient } from 'lib/seam/use-seam-client.js'
|
|
17
|
+
|
|
18
|
+
export type UseUpdateThermostatClimatePresetParams = never
|
|
19
|
+
export type UseUpdateThermostatClimatePresetData = undefined
|
|
20
|
+
|
|
21
|
+
export type UseUpdateThermostatClimatePresetVariables = Omit<
|
|
22
|
+
ThermostatsUpdateClimatePresetBody,
|
|
23
|
+
'manual_override_allowed'
|
|
24
|
+
>
|
|
25
|
+
|
|
26
|
+
export function useUpdateThermostatClimatePreset(): UseMutationResult<
|
|
27
|
+
UseUpdateThermostatClimatePresetData,
|
|
28
|
+
SeamHttpApiError,
|
|
29
|
+
UseUpdateThermostatClimatePresetVariables
|
|
30
|
+
> {
|
|
31
|
+
const { client } = useSeamClient()
|
|
32
|
+
const queryClient = useQueryClient()
|
|
33
|
+
|
|
34
|
+
return useMutation<
|
|
35
|
+
UseUpdateThermostatClimatePresetData,
|
|
36
|
+
SeamHttpApiError,
|
|
37
|
+
UseUpdateThermostatClimatePresetVariables
|
|
38
|
+
>({
|
|
39
|
+
mutationFn: async (variables) => {
|
|
40
|
+
if (client === null) throw new NullSeamClientError()
|
|
41
|
+
await client.thermostats.createClimatePreset(variables)
|
|
42
|
+
},
|
|
43
|
+
onSuccess: (_data, variables) => {
|
|
44
|
+
queryClient.setQueryData<ThermostatDevice | null>(
|
|
45
|
+
['devices', 'get', { device_id: variables.device_id }],
|
|
46
|
+
(device) => {
|
|
47
|
+
if (device == null) {
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return getUpdatedDevice(device, variables)
|
|
52
|
+
}
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
queryClient.setQueryData<ThermostatDevice[]>(
|
|
56
|
+
['devices', 'list', { device_id: variables.device_id }],
|
|
57
|
+
(devices): ThermostatDevice[] => {
|
|
58
|
+
if (devices == null) {
|
|
59
|
+
return []
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return devices.map((device) => {
|
|
63
|
+
if (device.device_id === variables.device_id) {
|
|
64
|
+
return getUpdatedDevice(device, variables)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return device
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
)
|
|
71
|
+
},
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function getUpdatedDevice(
|
|
76
|
+
device: ThermostatDevice,
|
|
77
|
+
variables: UseUpdateThermostatClimatePresetVariables
|
|
78
|
+
): ThermostatDevice {
|
|
79
|
+
const preset: ThermostatClimatePreset = {
|
|
80
|
+
...variables,
|
|
81
|
+
cooling_set_point_celsius: fahrenheitToCelsius(
|
|
82
|
+
variables.cooling_set_point_fahrenheit
|
|
83
|
+
),
|
|
84
|
+
heating_set_point_celsius: fahrenheitToCelsius(
|
|
85
|
+
variables.heating_set_point_fahrenheit
|
|
86
|
+
),
|
|
87
|
+
display_name: variables.name ?? variables.climate_preset_key,
|
|
88
|
+
can_delete: true,
|
|
89
|
+
can_edit: true,
|
|
90
|
+
manual_override_allowed: true,
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
...device,
|
|
95
|
+
properties: {
|
|
96
|
+
...device.properties,
|
|
97
|
+
available_climate_presets: [
|
|
98
|
+
preset,
|
|
99
|
+
...(device.properties.available_climate_presets ?? []),
|
|
100
|
+
],
|
|
101
|
+
},
|
|
102
|
+
}
|
|
103
|
+
}
|
package/src/lib/ui/Button.tsx
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import classNames from 'classnames'
|
|
2
2
|
import type { MouseEventHandler, PropsWithChildren } from 'react'
|
|
3
3
|
|
|
4
|
+
import { Spinner } from 'lib/ui/Spinner/Spinner.js'
|
|
5
|
+
|
|
4
6
|
interface ButtonProps extends PropsWithChildren {
|
|
5
|
-
variant?: 'solid' | 'outline' | 'neutral'
|
|
7
|
+
variant?: 'solid' | 'outline' | 'neutral' | 'danger'
|
|
6
8
|
size?: 'small' | 'medium' | 'large'
|
|
7
9
|
type?: 'button' | 'submit'
|
|
8
10
|
disabled?: boolean
|
|
9
11
|
onClick?: MouseEventHandler<HTMLButtonElement>
|
|
10
12
|
className?: string
|
|
11
13
|
onMouseDown?: MouseEventHandler<HTMLButtonElement>
|
|
14
|
+
loading?: boolean
|
|
12
15
|
}
|
|
13
16
|
|
|
14
17
|
export function Button({
|
|
@@ -20,6 +23,7 @@ export function Button({
|
|
|
20
23
|
className,
|
|
21
24
|
onMouseDown,
|
|
22
25
|
type = 'button',
|
|
26
|
+
loading = false,
|
|
23
27
|
}: ButtonProps): JSX.Element {
|
|
24
28
|
return (
|
|
25
29
|
<button
|
|
@@ -27,15 +31,28 @@ export function Button({
|
|
|
27
31
|
`seam-btn seam-btn-${variant} seam-btn-${size}`,
|
|
28
32
|
{
|
|
29
33
|
'seam-btn-disabled': disabled,
|
|
34
|
+
'seam-btn-loading': loading,
|
|
30
35
|
},
|
|
31
36
|
className
|
|
32
37
|
)}
|
|
33
38
|
disabled={disabled}
|
|
34
|
-
onClick={
|
|
39
|
+
onClick={(e) => {
|
|
40
|
+
if (loading || disabled) {
|
|
41
|
+
e.preventDefault()
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
onClick?.(e)
|
|
46
|
+
}}
|
|
35
47
|
onMouseDown={onMouseDown}
|
|
36
48
|
type={type}
|
|
37
49
|
>
|
|
38
|
-
{children}
|
|
50
|
+
<span className='seam-btn-content'>{children}</span>
|
|
51
|
+
{loading && (
|
|
52
|
+
<div className='seam-btn-loading'>
|
|
53
|
+
<Spinner size='small' />
|
|
54
|
+
</div>
|
|
55
|
+
)}
|
|
39
56
|
</button>
|
|
40
57
|
)
|
|
41
58
|
}
|
|
@@ -1,9 +1,26 @@
|
|
|
1
1
|
import classNames from 'classnames'
|
|
2
|
+
import type { Ref } from 'react'
|
|
2
3
|
|
|
3
4
|
import type { ButtonProps } from 'lib/ui/types.js'
|
|
4
5
|
|
|
5
|
-
export
|
|
6
|
+
export type IconProps = ButtonProps & {
|
|
7
|
+
elRef?: Ref<HTMLButtonElement>
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function IconButton({
|
|
11
|
+
className,
|
|
12
|
+
elRef,
|
|
13
|
+
...props
|
|
14
|
+
}: IconProps): JSX.Element {
|
|
6
15
|
return (
|
|
7
|
-
<button
|
|
16
|
+
<button
|
|
17
|
+
{...props}
|
|
18
|
+
ref={elRef}
|
|
19
|
+
className={classNames(
|
|
20
|
+
'seam-icon-btn',
|
|
21
|
+
props.disabled === true && 'seam-icon-btn-disabled',
|
|
22
|
+
className
|
|
23
|
+
)}
|
|
24
|
+
/>
|
|
8
25
|
)
|
|
9
26
|
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import {
|
|
2
|
+
autoUpdate,
|
|
3
|
+
flip,
|
|
4
|
+
limitShift,
|
|
5
|
+
offset,
|
|
6
|
+
type ReferenceElement,
|
|
7
|
+
shift,
|
|
8
|
+
useFloating,
|
|
9
|
+
} from '@floating-ui/react'
|
|
10
|
+
import {
|
|
11
|
+
type ReactNode,
|
|
12
|
+
type Ref,
|
|
13
|
+
useCallback,
|
|
14
|
+
useEffect,
|
|
15
|
+
useImperativeHandle,
|
|
16
|
+
useMemo,
|
|
17
|
+
useRef,
|
|
18
|
+
useState,
|
|
19
|
+
} from 'react'
|
|
20
|
+
import { createPortal } from 'react-dom'
|
|
21
|
+
|
|
22
|
+
import { seamComponentsClassName } from 'lib/seam/SeamProvider.js'
|
|
23
|
+
|
|
24
|
+
export interface PopoverInstance {
|
|
25
|
+
show: () => void
|
|
26
|
+
hide: () => void
|
|
27
|
+
toggle: () => void
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
type PopoverChildren = (
|
|
31
|
+
params: {
|
|
32
|
+
setRef: (ref: HTMLElement | undefined | null) => void
|
|
33
|
+
} & PopoverInstance
|
|
34
|
+
) => ReactNode
|
|
35
|
+
|
|
36
|
+
export interface PopoverProps {
|
|
37
|
+
children: PopoverChildren
|
|
38
|
+
content: ReactNode | ((instance: PopoverInstance) => ReactNode)
|
|
39
|
+
instanceRef?: Ref<PopoverInstance>
|
|
40
|
+
preventCloseOnClickOutside?: boolean
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function Popover(props: PopoverProps): JSX.Element {
|
|
44
|
+
const { children, content, instanceRef, preventCloseOnClickOutside } = props
|
|
45
|
+
|
|
46
|
+
const [open, setOpen] = useState(false)
|
|
47
|
+
|
|
48
|
+
const { refs, floatingStyles } = useFloating({
|
|
49
|
+
whileElementsMounted: autoUpdate,
|
|
50
|
+
transform: false,
|
|
51
|
+
open,
|
|
52
|
+
onOpenChange: setOpen,
|
|
53
|
+
placement: 'bottom',
|
|
54
|
+
middleware: [
|
|
55
|
+
shift({
|
|
56
|
+
crossAxis: true,
|
|
57
|
+
limiter: limitShift(),
|
|
58
|
+
}),
|
|
59
|
+
flip(),
|
|
60
|
+
offset(5),
|
|
61
|
+
],
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
const referenceEl = useRef<HTMLElement | null>()
|
|
65
|
+
const floatingEl = useRef<HTMLElement | null>()
|
|
66
|
+
|
|
67
|
+
const setFLoating = useCallback(
|
|
68
|
+
(ref: HTMLElement | null): void => {
|
|
69
|
+
refs.setFloating(ref)
|
|
70
|
+
floatingEl.current = ref
|
|
71
|
+
},
|
|
72
|
+
[refs, floatingEl]
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
const toggle = useCallback(() => {
|
|
76
|
+
setOpen((value) => !value)
|
|
77
|
+
}, [])
|
|
78
|
+
|
|
79
|
+
const instance = useMemo(
|
|
80
|
+
() => ({
|
|
81
|
+
show: () => {
|
|
82
|
+
setOpen(true)
|
|
83
|
+
},
|
|
84
|
+
hide: () => {
|
|
85
|
+
setOpen(false)
|
|
86
|
+
},
|
|
87
|
+
toggle,
|
|
88
|
+
}),
|
|
89
|
+
[toggle]
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
const setReference = useCallback(
|
|
93
|
+
(ref: ReferenceElement | undefined | null): void => {
|
|
94
|
+
if (!(ref instanceof HTMLElement) || referenceEl.current === ref) return
|
|
95
|
+
|
|
96
|
+
if (referenceEl.current != null) {
|
|
97
|
+
referenceEl.current.removeEventListener('click', toggle)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
refs.setReference(ref)
|
|
101
|
+
ref.addEventListener('click', toggle)
|
|
102
|
+
referenceEl.current = ref
|
|
103
|
+
},
|
|
104
|
+
[toggle, refs]
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
useImperativeHandle(instanceRef, () => instance)
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Closes the popover when the user clicks outside of it.
|
|
111
|
+
*/
|
|
112
|
+
const windowClickHandler = useCallback((e: MouseEvent): void => {
|
|
113
|
+
const target = e.target as HTMLElement
|
|
114
|
+
|
|
115
|
+
// If the target is the reference element, do nothing.
|
|
116
|
+
if (
|
|
117
|
+
referenceEl.current === target ||
|
|
118
|
+
referenceEl.current?.contains(target) === true
|
|
119
|
+
) {
|
|
120
|
+
return
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const closest = target.closest('[data-seam-popover]')
|
|
124
|
+
|
|
125
|
+
// Prevents closing if target is floating element, also adds support for nested popovers somehow :)
|
|
126
|
+
if (
|
|
127
|
+
closest != null &&
|
|
128
|
+
referenceEl.current != null &&
|
|
129
|
+
!closest.contains(referenceEl.current)
|
|
130
|
+
) {
|
|
131
|
+
return
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
setOpen(false)
|
|
135
|
+
}, [])
|
|
136
|
+
|
|
137
|
+
useEffect(() => {
|
|
138
|
+
setTimeout(() => {
|
|
139
|
+
if (preventCloseOnClickOutside === false) return
|
|
140
|
+
|
|
141
|
+
globalThis.addEventListener('click', windowClickHandler)
|
|
142
|
+
}, 0)
|
|
143
|
+
|
|
144
|
+
return () => {
|
|
145
|
+
globalThis.removeEventListener('click', windowClickHandler)
|
|
146
|
+
}
|
|
147
|
+
}, [windowClickHandler, preventCloseOnClickOutside])
|
|
148
|
+
|
|
149
|
+
return (
|
|
150
|
+
<>
|
|
151
|
+
{children({ setRef: setReference, ...instance })}
|
|
152
|
+
{open &&
|
|
153
|
+
createPortal(
|
|
154
|
+
<div
|
|
155
|
+
className={seamComponentsClassName}
|
|
156
|
+
data-seam-popover=''
|
|
157
|
+
ref={setFLoating}
|
|
158
|
+
style={floatingStyles}
|
|
159
|
+
>
|
|
160
|
+
<div className='seam-popover'>
|
|
161
|
+
{typeof content === 'function' ? content(instance) : content}
|
|
162
|
+
</div>
|
|
163
|
+
</div>,
|
|
164
|
+
globalThis.document.body
|
|
165
|
+
)}
|
|
166
|
+
</>
|
|
167
|
+
)
|
|
168
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Button } from 'lib/ui/Button.js'
|
|
2
|
+
|
|
3
|
+
export interface PopoverContentPromptProps {
|
|
4
|
+
onConfirm?: () => void
|
|
5
|
+
onCancel?: () => void
|
|
6
|
+
prompt?: string
|
|
7
|
+
description?: string
|
|
8
|
+
confirmText?: string
|
|
9
|
+
cancelText?: string
|
|
10
|
+
confirmLoading?: boolean
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function PopoverContentPrompt(
|
|
14
|
+
props: PopoverContentPromptProps
|
|
15
|
+
): JSX.Element {
|
|
16
|
+
const {
|
|
17
|
+
confirmText = t.confirm,
|
|
18
|
+
cancelText = t.cancel,
|
|
19
|
+
confirmLoading = false,
|
|
20
|
+
prompt = t.areYouSure,
|
|
21
|
+
description,
|
|
22
|
+
onConfirm,
|
|
23
|
+
onCancel,
|
|
24
|
+
} = props
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<div className='seam-popover-content-prompt'>
|
|
28
|
+
<div>
|
|
29
|
+
<div className='seam-popover-content-prompt-text'>{prompt}</div>
|
|
30
|
+
{description != null && (
|
|
31
|
+
<div className='seam-popover-content-prompt-description'>
|
|
32
|
+
{description}
|
|
33
|
+
</div>
|
|
34
|
+
)}
|
|
35
|
+
</div>
|
|
36
|
+
<div className='seam-popover-content-prompt-buttons'>
|
|
37
|
+
<Button
|
|
38
|
+
variant='solid'
|
|
39
|
+
onClick={onConfirm}
|
|
40
|
+
loading={confirmLoading}
|
|
41
|
+
size='small'
|
|
42
|
+
>
|
|
43
|
+
{confirmText}
|
|
44
|
+
</Button>
|
|
45
|
+
|
|
46
|
+
<Button variant='danger' size='small' onClick={onCancel}>
|
|
47
|
+
{cancelText}
|
|
48
|
+
</Button>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const t = {
|
|
55
|
+
confirm: 'Confirm',
|
|
56
|
+
cancel: 'Cancel',
|
|
57
|
+
areYouSure: 'Are you sure?',
|
|
58
|
+
}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import classNames from 'classnames'
|
|
2
|
+
import type { CSSProperties } from 'react'
|
|
3
|
+
|
|
1
4
|
import { ChevronDownIcon } from 'lib/icons/ChevronDown.js'
|
|
2
5
|
import { OffIcon } from 'lib/icons/Off.js'
|
|
3
6
|
import { ThermostatCoolIcon } from 'lib/icons/ThermostatCool.js'
|
|
@@ -11,20 +14,49 @@ interface ClimateModeMenuProps {
|
|
|
11
14
|
mode: HvacModeSetting
|
|
12
15
|
onChange: (mode: HvacModeSetting) => void
|
|
13
16
|
supportedModes?: HvacModeSetting[]
|
|
17
|
+
buttonTextVisible?: boolean
|
|
18
|
+
className?: string
|
|
19
|
+
style?: CSSProperties
|
|
20
|
+
block?: boolean
|
|
21
|
+
size?: 'regular' | 'large'
|
|
14
22
|
}
|
|
15
23
|
|
|
16
24
|
export function ClimateModeMenu({
|
|
17
25
|
mode,
|
|
18
26
|
onChange,
|
|
19
27
|
supportedModes = ['heat', 'cool', 'heat_cool', 'off'],
|
|
28
|
+
buttonTextVisible = false,
|
|
29
|
+
className,
|
|
30
|
+
style,
|
|
31
|
+
block,
|
|
32
|
+
size = 'regular',
|
|
20
33
|
}: ClimateModeMenuProps): JSX.Element {
|
|
21
34
|
return (
|
|
22
35
|
<Menu
|
|
23
36
|
renderButton={({ onOpen }) => (
|
|
24
|
-
<button
|
|
37
|
+
<button
|
|
38
|
+
style={style}
|
|
39
|
+
onClick={onOpen}
|
|
40
|
+
className={classNames(
|
|
41
|
+
'seam-climate-mode-menu-button',
|
|
42
|
+
{
|
|
43
|
+
'seam-climate-mode-menu-button-block': block,
|
|
44
|
+
'seam-climate-mode-menu-button-regular': size === 'regular',
|
|
45
|
+
'seam-climate-mode-menu-button-large': size === 'large',
|
|
46
|
+
},
|
|
47
|
+
className
|
|
48
|
+
)}
|
|
49
|
+
>
|
|
25
50
|
<div className='seam-climate-mode-menu-button-icon'>
|
|
26
51
|
<ModeIcon mode={mode} />
|
|
27
52
|
</div>
|
|
53
|
+
|
|
54
|
+
{buttonTextVisible && (
|
|
55
|
+
<span className='seam-climate-mode-menu-button-text'>
|
|
56
|
+
{t[mode]}
|
|
57
|
+
</span>
|
|
58
|
+
)}
|
|
59
|
+
|
|
28
60
|
<ChevronDownIcon className='seam-climate-mode-menu-button-chevron' />
|
|
29
61
|
</button>
|
|
30
62
|
)}
|