@seamapi/react 1.48.0 → 1.49.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.
Files changed (123) hide show
  1. package/README.md +1 -1
  2. package/dist/elements.js +4653 -4626
  3. package/dist/elements.js.map +1 -1
  4. package/dist/index.css +587 -2
  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/dates.d.ts +9 -0
  9. package/lib/dates.js +15 -0
  10. package/lib/dates.js.map +1 -1
  11. package/lib/icons/ArrowRight.d.ts +2 -0
  12. package/lib/icons/ArrowRight.js +7 -0
  13. package/lib/icons/ArrowRight.js.map +1 -0
  14. package/lib/icons/ChevronWide.d.ts +2 -0
  15. package/lib/icons/ChevronWide.js +5 -0
  16. package/lib/icons/ChevronWide.js.map +1 -0
  17. package/lib/icons/Copy.js +2 -4
  18. package/lib/icons/Copy.js.map +1 -1
  19. package/lib/icons/TemperatureAdd.d.ts +2 -0
  20. package/lib/icons/TemperatureAdd.js +7 -0
  21. package/lib/icons/TemperatureAdd.js.map +1 -0
  22. package/lib/icons/TemperatureSubtract.d.ts +2 -0
  23. package/lib/icons/TemperatureSubtract.js +7 -0
  24. package/lib/icons/TemperatureSubtract.js.map +1 -0
  25. package/lib/seam/SeamProvider.d.ts +2 -2
  26. package/lib/seam/SeamProvider.js.map +1 -1
  27. package/lib/seam/access-codes/use-generate-access-code-code.d.ts +6 -0
  28. package/lib/seam/access-codes/use-generate-access-code-code.js +13 -0
  29. package/lib/seam/access-codes/use-generate-access-code-code.js.map +1 -0
  30. package/lib/seam/components/AccessCodeDetails/AccessCodeDetails.js +9 -2
  31. package/lib/seam/components/AccessCodeDetails/AccessCodeDetails.js.map +1 -1
  32. package/lib/seam/components/ClimateSettingScheduleDetails/ClimateSettingScheduleCard.d.ts +6 -0
  33. package/lib/seam/components/ClimateSettingScheduleDetails/ClimateSettingScheduleCard.js +41 -0
  34. package/lib/seam/components/ClimateSettingScheduleDetails/ClimateSettingScheduleCard.js.map +1 -0
  35. package/lib/seam/components/ClimateSettingScheduleDetails/ClimateSettingScheduleDetails.d.ts +7 -0
  36. package/lib/seam/components/ClimateSettingScheduleDetails/ClimateSettingScheduleDetails.js +32 -0
  37. package/lib/seam/components/ClimateSettingScheduleDetails/ClimateSettingScheduleDetails.js.map +1 -0
  38. package/lib/seam/components/ClimateSettingScheduleDetails/ClimateSettingScheduleDeviceBar.d.ts +4 -0
  39. package/lib/seam/components/ClimateSettingScheduleDetails/ClimateSettingScheduleDeviceBar.js +29 -0
  40. package/lib/seam/components/ClimateSettingScheduleDetails/ClimateSettingScheduleDeviceBar.js.map +1 -0
  41. package/lib/seam/components/ClimateSettingScheduleTable/ClimateSettingScheduleRow.js +2 -2
  42. package/lib/seam/components/ClimateSettingScheduleTable/ClimateSettingScheduleRow.js.map +1 -1
  43. package/lib/seam/components/ClimateSettingScheduleTable/{ClimateSettingScheduleDetails.d.ts → ClimateSettingScheduleRowDetails.d.ts} +1 -1
  44. package/lib/seam/components/ClimateSettingScheduleTable/{ClimateSettingScheduleDetails.js → ClimateSettingScheduleRowDetails.js} +2 -2
  45. package/lib/seam/components/ClimateSettingScheduleTable/ClimateSettingScheduleRowDetails.js.map +1 -0
  46. package/lib/seam/components/ClimateSettingScheduleTable/ClimateSettingScheduleTable.d.ts +3 -1
  47. package/lib/seam/components/ClimateSettingScheduleTable/ClimateSettingScheduleTable.js +20 -3
  48. package/lib/seam/components/ClimateSettingScheduleTable/ClimateSettingScheduleTable.js.map +1 -1
  49. package/lib/seam/use-seam-client.js +2 -2
  50. package/lib/seam/use-seam-client.js.map +1 -1
  51. package/lib/ui/AccessCodeForm/AccessCodeFormDatePicker.js +1 -1
  52. package/lib/ui/AccessCodeForm/AccessCodeFormDatePicker.js.map +1 -1
  53. package/lib/ui/Menu/Menu.js +5 -5
  54. package/lib/ui/Menu/Menu.js.map +1 -1
  55. package/lib/ui/Switch/Switch.d.ts +8 -0
  56. package/lib/ui/Switch/Switch.js +15 -0
  57. package/lib/ui/Switch/Switch.js.map +1 -0
  58. package/lib/ui/TextField/TextField.js +1 -3
  59. package/lib/ui/TextField/TextField.js.map +1 -1
  60. package/lib/ui/clipboard.js +1 -1
  61. package/lib/ui/clipboard.js.map +1 -1
  62. package/lib/ui/layout/AccordionRow.d.ts +7 -0
  63. package/lib/ui/layout/AccordionRow.js +8 -0
  64. package/lib/ui/layout/AccordionRow.js.map +1 -0
  65. package/lib/ui/layout/DetailRow.d.ts +6 -0
  66. package/lib/ui/layout/DetailRow.js +5 -0
  67. package/lib/ui/layout/DetailRow.js.map +1 -0
  68. package/lib/ui/layout/DetailSection.d.ts +7 -0
  69. package/lib/ui/layout/DetailSection.js +6 -0
  70. package/lib/ui/layout/DetailSection.js.map +1 -0
  71. package/lib/ui/layout/DetailSectionGroup.d.ts +2 -0
  72. package/lib/ui/layout/DetailSectionGroup.js +5 -0
  73. package/lib/ui/layout/DetailSectionGroup.js.map +1 -0
  74. package/lib/ui/thermostat/TemperatureControl.d.ts +11 -0
  75. package/lib/ui/thermostat/TemperatureControl.js +42 -0
  76. package/lib/ui/thermostat/TemperatureControl.js.map +1 -0
  77. package/lib/ui/use-current-time.d.ts +2 -0
  78. package/lib/ui/use-current-time.js +12 -0
  79. package/lib/ui/use-current-time.js.map +1 -0
  80. package/lib/version.d.ts +1 -1
  81. package/lib/version.js +1 -1
  82. package/package.json +7 -7
  83. package/src/lib/dates.ts +17 -0
  84. package/src/lib/element.tsx +6 -4
  85. package/src/lib/icons/ArrowRight.tsx +36 -0
  86. package/src/lib/icons/ChevronWide.tsx +21 -0
  87. package/src/lib/icons/Copy.tsx +6 -21
  88. package/src/lib/icons/TemperatureAdd.tsx +38 -0
  89. package/src/lib/icons/TemperatureSubtract.tsx +35 -0
  90. package/src/lib/seam/SeamProvider.tsx +2 -3
  91. package/src/lib/seam/access-codes/use-generate-access-code-code.ts +38 -0
  92. package/src/lib/seam/components/AccessCodeDetails/AccessCodeDetails.tsx +23 -2
  93. package/src/lib/seam/components/ClimateSettingScheduleDetails/ClimateSettingScheduleCard.tsx +101 -0
  94. package/src/lib/seam/components/ClimateSettingScheduleDetails/ClimateSettingScheduleDetails.element.ts +13 -0
  95. package/src/lib/seam/components/ClimateSettingScheduleDetails/ClimateSettingScheduleDetails.tsx +103 -0
  96. package/src/lib/seam/components/ClimateSettingScheduleDetails/ClimateSettingScheduleDeviceBar.tsx +60 -0
  97. package/src/lib/seam/components/ClimateSettingScheduleTable/ClimateSettingScheduleRow.tsx +2 -2
  98. package/src/lib/seam/components/ClimateSettingScheduleTable/{ClimateSettingScheduleDetails.tsx → ClimateSettingScheduleRowDetails.tsx} +1 -1
  99. package/src/lib/seam/components/ClimateSettingScheduleTable/ClimateSettingScheduleTable.element.ts +2 -0
  100. package/src/lib/seam/components/ClimateSettingScheduleTable/ClimateSettingScheduleTable.tsx +37 -2
  101. package/src/lib/seam/use-seam-client.ts +2 -2
  102. package/src/lib/ui/AccessCodeForm/AccessCodeFormDatePicker.tsx +2 -2
  103. package/src/lib/ui/Menu/Menu.tsx +5 -6
  104. package/src/lib/ui/Switch/Switch.tsx +38 -0
  105. package/src/lib/ui/TextField/TextField.tsx +1 -3
  106. package/src/lib/ui/clipboard.ts +1 -1
  107. package/src/lib/ui/layout/AccordionRow.tsx +36 -0
  108. package/src/lib/ui/layout/DetailRow.tsx +17 -0
  109. package/src/lib/ui/layout/DetailSection.tsx +25 -0
  110. package/src/lib/ui/layout/DetailSectionGroup.tsx +7 -0
  111. package/src/lib/ui/thermostat/TemperatureControl.tsx +121 -0
  112. package/src/lib/ui/use-current-time.ts +16 -0
  113. package/src/lib/version.ts +1 -1
  114. package/src/styles/_access-code-details.scss +42 -1
  115. package/src/styles/_climate-setting-schedule-details.scss +44 -0
  116. package/src/styles/_colors.scss +4 -0
  117. package/src/styles/_layout.scss +146 -0
  118. package/src/styles/_main.scss +4 -0
  119. package/src/styles/_seam-table.scss +8 -0
  120. package/src/styles/_switch.scss +53 -0
  121. package/src/styles/_thermostat.scss +387 -2
  122. package/src/styles/_tooltip.scss +3 -0
  123. package/lib/seam/components/ClimateSettingScheduleTable/ClimateSettingScheduleDetails.js.map +0 -1
@@ -4,7 +4,7 @@ import { TableCell } from 'lib/ui/Table/TableCell.js'
4
4
  import { TableRow } from 'lib/ui/Table/TableRow.js'
5
5
  import { Title } from 'lib/ui/typography/Title.js'
6
6
 
7
- import { ClimateSettingScheduleDetails } from './ClimateSettingScheduleDetails.js'
7
+ import { ClimateSettingScheduleRowDetails } from './ClimateSettingScheduleRowDetails.js'
8
8
 
9
9
  interface ClimateSettingScheduleRowProps {
10
10
  climateSettingSchedule: UseClimateSettingSchedulesData[number]
@@ -26,7 +26,7 @@ export function ClimateSettingScheduleRow({
26
26
  <Title className='seam-truncated-text'>
27
27
  {climateSettingSchedule.name}
28
28
  </Title>
29
- <ClimateSettingScheduleDetails
29
+ <ClimateSettingScheduleRowDetails
30
30
  climateSettingSchedule={climateSettingSchedule}
31
31
  />
32
32
  </TableCell>
@@ -6,7 +6,7 @@ import { DotDivider } from 'lib/ui/layout/DotDivider.js'
6
6
  import { ClimateSettingStatus } from 'lib/ui/thermostat/ClimateSettingStatus.js'
7
7
  import { useIsDateInPast } from 'lib/ui/use-is-date-in-past.js'
8
8
 
9
- export function ClimateSettingScheduleDetails(props: {
9
+ export function ClimateSettingScheduleRowDetails(props: {
10
10
  climateSettingSchedule: ClimateSettingSchedule
11
11
  }): JSX.Element {
12
12
  const { climateSettingSchedule } = props
@@ -7,6 +7,8 @@ export const name = 'seam-climate-setting-schedule-table'
7
7
  export const props: ElementProps<ClimateSettingScheduleTableProps> = {
8
8
  deviceId: 'string',
9
9
  disableSearch: 'boolean',
10
+ onClimateSettingScheduleClick: 'object',
11
+ preventDefaultOnClimateSettingScheduleClick: 'boolean',
10
12
  climateSettingScheduleFilter: 'object',
11
13
  climateSettingScheduleComparator: 'object',
12
14
  onBack: 'object',
@@ -1,8 +1,9 @@
1
1
  import classNames from 'classnames'
2
- import { useMemo, useState } from 'react'
2
+ import { useCallback, useMemo, useState } from 'react'
3
3
  import type { ClimateSettingSchedule } from 'seamapi'
4
4
 
5
5
  import { compareByCreatedAtDesc } from 'lib/dates.js'
6
+ import { ClimateSettingScheduleDetails } from 'lib/seam/components/ClimateSettingScheduleDetails/ClimateSettingScheduleDetails.js'
6
7
  import {
7
8
  useClimateSettingSchedules,
8
9
  type UseClimateSettingSchedulesData,
@@ -20,6 +21,8 @@ import { ClimateSettingScheduleRow } from './ClimateSettingScheduleRow.js'
20
21
  export interface ClimateSettingScheduleTableProps {
21
22
  deviceId: string
22
23
  disableSearch?: boolean
24
+ onClimateSettingScheduleClick?: (climateSettingScheduleId: string) => void
25
+ preventDefaultOnClimateSettingScheduleClick?: boolean
23
26
  climateSettingScheduleFilter?: (
24
27
  climateSettingSchedule: ClimateSettingSchedule,
25
28
  searchInputValue: string
@@ -46,8 +49,11 @@ const defaultClimateSettingScheduleFilter = (
46
49
  export function ClimateSettingScheduleTable({
47
50
  deviceId,
48
51
  disableSearch = false,
52
+ onClimateSettingScheduleClick = () => {},
53
+ preventDefaultOnClimateSettingScheduleClick = false,
49
54
  climateSettingScheduleFilter = defaultClimateSettingScheduleFilter,
50
55
  climateSettingScheduleComparator = compareByCreatedAtDesc,
56
+
51
57
  onBack,
52
58
  heading = t.climateSettingSchedules,
53
59
  className,
@@ -58,6 +64,10 @@ export function ClimateSettingScheduleTable({
58
64
  })
59
65
 
60
66
  const [searchInputValue, setSearchInputValue] = useState('')
67
+ const [
68
+ selectedViewClimateSettingScheduleId,
69
+ setSelectedViewClimateSettingScheduleId,
70
+ ] = useState<string | null>(null)
61
71
 
62
72
  const filteredClimateSettingSchedules = useMemo(
63
73
  () =>
@@ -74,6 +84,31 @@ export function ClimateSettingScheduleTable({
74
84
  ]
75
85
  )
76
86
 
87
+ const handleClimateSettingScheduleClick = useCallback(
88
+ (climateSettingScheduleId: string): void => {
89
+ onClimateSettingScheduleClick(climateSettingScheduleId)
90
+ if (preventDefaultOnClimateSettingScheduleClick) return
91
+ setSelectedViewClimateSettingScheduleId(climateSettingScheduleId)
92
+ },
93
+ [
94
+ onClimateSettingScheduleClick,
95
+ preventDefaultOnClimateSettingScheduleClick,
96
+ setSelectedViewClimateSettingScheduleId,
97
+ ]
98
+ )
99
+
100
+ if (selectedViewClimateSettingScheduleId != null) {
101
+ return (
102
+ <ClimateSettingScheduleDetails
103
+ className={className}
104
+ climateSettingScheduleId={selectedViewClimateSettingScheduleId}
105
+ onBack={() => {
106
+ setSelectedViewClimateSettingScheduleId(null)
107
+ }}
108
+ />
109
+ )
110
+ }
111
+
77
112
  if (isLoading) {
78
113
  return <p className={className}>...</p>
79
114
  }
@@ -105,7 +140,7 @@ export function ClimateSettingScheduleTable({
105
140
  <TableBody>
106
141
  <Content
107
142
  climateSettingSchedules={filteredClimateSettingSchedules}
108
- onClimateSettingScheduleClick={() => {}}
143
+ onClimateSettingScheduleClick={handleClimateSettingScheduleClick}
109
144
  />
110
145
  </TableBody>
111
146
  </div>
@@ -102,10 +102,10 @@ This is not recommended because the client session is now bound to this machine
102
102
  }
103
103
 
104
104
  const fingerprint =
105
- globalThis?.localStorage?.getItem('seam_user_fingerprint') ??
105
+ globalThis.localStorage?.getItem('seam_user_fingerprint') ??
106
106
  `fingerprint_${uuidv4()}`
107
107
 
108
- globalThis?.localStorage?.setItem('seam_user_fingerprint', fingerprint)
108
+ globalThis.localStorage?.setItem('seam_user_fingerprint', fingerprint)
109
109
 
110
110
  return fingerprint
111
111
  }
@@ -42,7 +42,7 @@ export function AccessCodeFormDatePicker({
42
42
  <FormField>
43
43
  <InputLabel>{t.startTimeLabel}</InputLabel>
44
44
  <DateTimePicker
45
- value={startDate == null ? '' : startDate}
45
+ value={startDate ?? ''}
46
46
  onChange={setStartDate}
47
47
  size='large'
48
48
  />
@@ -50,7 +50,7 @@ export function AccessCodeFormDatePicker({
50
50
  <FormField>
51
51
  <InputLabel>{t.endTimeLabel}</InputLabel>
52
52
  <DateTimePicker
53
- value={endDate == null ? '' : endDate}
53
+ value={endDate ?? ''}
54
54
  onChange={setEndDate}
55
55
  size='large'
56
56
  />
@@ -47,8 +47,7 @@ export function Menu({
47
47
  const [left, setLeft] = useState(0)
48
48
 
49
49
  useEffect(() => {
50
- const containers =
51
- globalThis?.document?.querySelectorAll('.seam-components')
50
+ const containers = globalThis.document?.querySelectorAll('.seam-components')
52
51
  if (containers == null) return
53
52
  const el = containers[containers.length - 1]
54
53
  if (el != null) {
@@ -110,11 +109,11 @@ export function Menu({
110
109
 
111
110
  useLayoutEffect(() => {
112
111
  setPositions()
113
- globalThis?.addEventListener('scroll', setPositions)
114
- globalThis?.addEventListener('resize', setPositions)
112
+ globalThis.addEventListener('scroll', setPositions)
113
+ globalThis.addEventListener('resize', setPositions)
115
114
  return () => {
116
- globalThis?.removeEventListener('scroll', setPositions)
117
- globalThis?.removeEventListener('resize', setPositions)
115
+ globalThis.removeEventListener('scroll', setPositions)
116
+ globalThis.removeEventListener('resize', setPositions)
118
117
  }
119
118
  }, [setPositions])
120
119
 
@@ -0,0 +1,38 @@
1
+ import classNames from 'classnames'
2
+
3
+ export interface SwitchProps {
4
+ checked: boolean
5
+ onChange: (checked: boolean) => void
6
+ enableLabel?: boolean
7
+ }
8
+
9
+ function Switch({
10
+ checked,
11
+ onChange,
12
+ enableLabel = false,
13
+ }: SwitchProps): JSX.Element {
14
+ return (
15
+ <div className='seam-switch-container'>
16
+ {enableLabel && (
17
+ <label className='seam-switch-label'>{checked ? t.on : t.off}</label>
18
+ )}
19
+ <div
20
+ className={classNames('seam-switch', {
21
+ 'seam-switch-checked': checked,
22
+ })}
23
+ onClick={() => {
24
+ onChange(!checked)
25
+ }}
26
+ >
27
+ <div className='seam-switch-slider' />
28
+ </div>
29
+ </div>
30
+ )
31
+ }
32
+
33
+ const t = {
34
+ on: 'On',
35
+ off: 'Off',
36
+ }
37
+
38
+ export default Switch
@@ -74,9 +74,7 @@ export const TextField = forwardRef<
74
74
  }
75
75
 
76
76
  // Maintain a local ref, and still forward it along
77
- useImperativeHandle(ref, () => (inputEl != null ? inputEl : undefined), [
78
- inputEl,
79
- ])
77
+ useImperativeHandle(ref, () => inputEl ?? undefined, [inputEl])
80
78
 
81
79
  const endAdornmentVisible = endAdornment != null || clearable
82
80
 
@@ -1,3 +1,3 @@
1
1
  export async function copyToClipboard(value: string): Promise<void> {
2
- await globalThis?.navigator?.clipboard?.writeText(value)
2
+ await globalThis.navigator?.clipboard?.writeText(value)
3
3
  }
@@ -0,0 +1,36 @@
1
+ import type { PropsWithChildren } from 'react'
2
+
3
+ import { ChevronWideIcon } from 'lib/icons/ChevronWide.js'
4
+ import { useToggle } from 'lib/ui/use-toggle.js'
5
+
6
+ interface AccordionRowProps extends PropsWithChildren {
7
+ label: string
8
+ rightCollapsedContent?: JSX.Element
9
+ }
10
+
11
+ export function AccordionRow({
12
+ label,
13
+ rightCollapsedContent,
14
+ children,
15
+ }: AccordionRowProps): JSX.Element {
16
+ const [isExpanded, toggle] = useToggle()
17
+
18
+ return (
19
+ <div className='seam-accordion-row' aria-expanded={isExpanded}>
20
+ <button className='seam-accordion-row-trigger' onClick={toggle}>
21
+ <p className='seam-row-label'>{label}</p>
22
+ <div className='seam-row-inner-wrap'>
23
+ <div className='seam-row-trigger-right-content'>
24
+ {rightCollapsedContent}
25
+ </div>
26
+ <div className='seam-accordion-icon-wrap'>
27
+ <ChevronWideIcon />
28
+ </div>
29
+ </div>
30
+ </button>
31
+ <div className='seam-accordion-row-content'>
32
+ <div className='seam-accordion-row-inner-content'>{children}</div>
33
+ </div>
34
+ </div>
35
+ )
36
+ }
@@ -0,0 +1,17 @@
1
+ import type { PropsWithChildren } from 'react'
2
+
3
+ interface DetailRowProps {
4
+ label: string
5
+ }
6
+
7
+ export function DetailRow({
8
+ label,
9
+ children,
10
+ }: PropsWithChildren<DetailRowProps>): JSX.Element {
11
+ return (
12
+ <div className='seam-detail-row'>
13
+ <p className='seam-row-label'>{label}</p>
14
+ <div className='seam-detail-row-right-area-content'>{children}</div>
15
+ </div>
16
+ )
17
+ }
@@ -0,0 +1,25 @@
1
+ import type { PropsWithChildren } from 'react'
2
+
3
+ import { Tooltip } from 'lib/ui/Tooltip/Tooltip.js'
4
+
5
+ interface DetailSectionProps {
6
+ label?: string
7
+ tooltipContent?: string
8
+ }
9
+
10
+ export function DetailSection({
11
+ label,
12
+ tooltipContent,
13
+ children,
14
+ }: PropsWithChildren<DetailSectionProps>): JSX.Element {
15
+ return (
16
+ <div className='seam-detail-section'>
17
+ <div className='seam-detail-label-wrap'>
18
+ {label != null && <p className='seam-detail-label'>{label}</p>}
19
+ {tooltipContent != null && <Tooltip>{tooltipContent}</Tooltip>}
20
+ </div>
21
+
22
+ <div className='seam-detail-group'>{children}</div>
23
+ </div>
24
+ )
25
+ }
@@ -0,0 +1,7 @@
1
+ import type { PropsWithChildren } from 'react'
2
+
3
+ export function DetailSectionGroup({
4
+ children,
5
+ }: PropsWithChildren): JSX.Element {
6
+ return <div className='seam-detail-sections'>{children}</div>
7
+ }
@@ -0,0 +1,121 @@
1
+ import { type ChangeEventHandler, useEffect, useRef } from 'react'
2
+
3
+ import { TemperatureAddIcon } from 'lib/icons/TemperatureAdd.js'
4
+ import { TemperatureSubtractIcon } from 'lib/icons/TemperatureSubtract.js'
5
+
6
+ interface TemperatureControlProps {
7
+ variant: 'heat' | 'cool'
8
+ value: number
9
+ onChange: (temperature: number) => void
10
+ min?: number
11
+ max?: number
12
+ unit?: 'fahrenheit' | 'celsius'
13
+ }
14
+
15
+ export function TemperatureControl({
16
+ variant,
17
+ value,
18
+ onChange,
19
+ min = 50,
20
+ max = 90,
21
+ unit = 'fahrenheit',
22
+ }: TemperatureControlProps): JSX.Element {
23
+ const increment = (): void => {
24
+ onChange(Math.min(value + 1, max))
25
+ }
26
+
27
+ const decrement = (): void => {
28
+ onChange(Math.max(value - 1, min))
29
+ }
30
+
31
+ return (
32
+ <div className='seam-temperature-control'>
33
+ <button className='seam-temperature-stepper-button' onClick={decrement}>
34
+ <TemperatureSubtractIcon />
35
+ </button>
36
+ <RangeSlider
37
+ variant={variant}
38
+ min={min}
39
+ max={max}
40
+ value={value}
41
+ onChange={(temperature) => {
42
+ onChange(temperature)
43
+ }}
44
+ unit={unit}
45
+ />
46
+ <button className='seam-temperature-stepper-button' onClick={increment}>
47
+ <TemperatureAddIcon />
48
+ </button>
49
+ </div>
50
+ )
51
+ }
52
+
53
+ interface RangeSliderProps {
54
+ variant: 'heat' | 'cool'
55
+ value: number
56
+ onChange: (temperature: number) => void
57
+ min: number
58
+ max: number
59
+ unit: 'fahrenheit' | 'celsius'
60
+ }
61
+
62
+ function RangeSlider({
63
+ variant,
64
+ value,
65
+ onChange,
66
+ min,
67
+ max,
68
+ unit,
69
+ }: RangeSliderProps): JSX.Element {
70
+ const wrapRef = useRef<HTMLDivElement>(null)
71
+ const inputRef = useRef<HTMLInputElement>(null)
72
+
73
+ useEffect(() => {
74
+ if (wrapRef.current == null || inputRef.current == null) return
75
+
76
+ const wrap = wrapRef.current
77
+ const input = inputRef.current
78
+
79
+ wrap.style.setProperty('--temperature-current', input.value)
80
+ wrap.style.setProperty('--temperature-min', input.min)
81
+ wrap.style.setProperty('--temperature-max', input.max)
82
+ }, [value])
83
+
84
+ const handleRangeChange: ChangeEventHandler<HTMLInputElement> = (
85
+ event
86
+ ): void => {
87
+ const wrap = wrapRef.current
88
+
89
+ if (wrap == null) return
90
+ const temperature = event.target.value
91
+ wrap.style.setProperty('--temperature-current', temperature)
92
+ onChange(Number(temperature))
93
+ }
94
+
95
+ return (
96
+ <div ref={wrapRef} className='seam-temperature-range-wrap'>
97
+ <input
98
+ ref={inputRef}
99
+ type='range'
100
+ min={min}
101
+ max={max}
102
+ value={value}
103
+ onChange={handleRangeChange}
104
+ className='seam-temperature-range'
105
+ data-variant={variant}
106
+ />
107
+
108
+ <div className='seam-floating-temperature'>
109
+ <span className='seam-floating-temperature-value'>{value}</span>
110
+ <span className='seam-floating-temperature-unit'>
111
+ {unit === 'fahrenheit' ? t.degreeFahrenheit : t.degreeCelsius}
112
+ </span>
113
+ </div>
114
+ </div>
115
+ )
116
+ }
117
+
118
+ const t = {
119
+ degreeFahrenheit: 'ºF',
120
+ degreeCelsius: 'ºC',
121
+ }
@@ -0,0 +1,16 @@
1
+ import { DateTime } from 'luxon'
2
+ import { useCallback, useState } from 'react'
3
+
4
+ import { useInterval } from 'lib/ui/use-interval.js'
5
+
6
+ export function useCurrentTime(): DateTime {
7
+ const [date, setDate] = useState<DateTime>(DateTime.now())
8
+
9
+ const update = useCallback(() => {
10
+ setDate(DateTime.now())
11
+ }, [setDate])
12
+
13
+ useInterval(update)
14
+
15
+ return date
16
+ }
@@ -1,3 +1,3 @@
1
- const seamapiReactVersion = '1.48.0'
1
+ const seamapiReactVersion = '1.49.0'
2
2
 
3
3
  export default seamapiReactVersion
@@ -33,11 +33,35 @@
33
33
  background: colors.$white;
34
34
  border: 1px solid colors.$text-gray-2;
35
35
  border-radius: 8px;
36
- padding: 10px 16px;
37
36
  font-weight: 600;
38
37
  font-size: 24px;
39
38
  line-height: 134%;
40
39
  margin-bottom: 8px;
40
+ display: flex;
41
+ justify-content: space-between;
42
+
43
+ > span {
44
+ padding: 10px 16px;
45
+ }
46
+
47
+ > .seam-icon-btn {
48
+ height: auto;
49
+ border: 0;
50
+ overflow: visible;
51
+ padding: 0;
52
+ padding: 16px 10px;
53
+ flex: 0 0 48px;
54
+ border-radius: 0 8px 8px 0;
55
+ border-left: 1px solid colors.$divider-stroke-light;
56
+
57
+ &:hover {
58
+ background: colors.$item-hover-bg;
59
+ }
60
+
61
+ svg {
62
+ scale: 0.8333;
63
+ }
64
+ }
41
65
  }
42
66
 
43
67
  .seam-duration {
@@ -105,6 +129,23 @@
105
129
  display: flex;
106
130
  flex: 1;
107
131
 
132
+ &.seam-code-id {
133
+ display: flex;
134
+ justify-content: space-between;
135
+
136
+ .seam-icon-btn {
137
+ border: 0;
138
+
139
+ &:hover {
140
+ background: transparent;
141
+ }
142
+
143
+ svg {
144
+ scale: 0.8333;
145
+ }
146
+ }
147
+ }
148
+
108
149
  .seam-times {
109
150
  display: flex;
110
151
  flex: 1;
@@ -0,0 +1,44 @@
1
+ @use './colors';
2
+
3
+ @mixin all {
4
+ .seam-climate-setting-schedule-details {
5
+ .seam-climate-setting-schedule-details-content {
6
+ margin: 0 16px;
7
+ }
8
+
9
+ .seam-climate-setting-details-value {
10
+ font-style: normal;
11
+ font-weight: 400;
12
+ line-height: 118%;
13
+ font-size: 14px;
14
+ color: colors.$text-gray-1;
15
+
16
+ &.seam-climate-setting-details-schedule-range {
17
+ display: flex;
18
+ align-items: center;
19
+ }
20
+ }
21
+
22
+ .seam-default-setting-message-container {
23
+ display: flex;
24
+ justify-content: center;
25
+ margin: 16px 0;
26
+
27
+ .seam-default-setting-message {
28
+ font-size: 14px;
29
+ font-style: normal;
30
+ font-weight: 400;
31
+ line-height: 115%;
32
+ color: colors.$text-gray-2;
33
+
34
+ .seam-default-setting-text {
35
+ color: colors.$text-gray-1;
36
+ }
37
+ }
38
+ }
39
+
40
+ .seam-creation-date {
41
+ color: colors.$text-gray-1;
42
+ }
43
+ }
44
+ }
@@ -34,3 +34,7 @@ $pill-green-bg: rgb(98 232 139 / 30%);
34
34
  $pill-green-text: #0d8841;
35
35
  $pill-blue-bg: rgb(125 192 241 / 30%);
36
36
  $pill-blue-text: #1653b8;
37
+ $thermo-orange: #fc8e28;
38
+ $thermo-orange-faded: #fff2e0;
39
+ $thermo-blue: #6b95ff;
40
+ $thermo-blue-faded: #e7f2ff;