@seamapi/react 1.44.0 → 1.45.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 (34) hide show
  1. package/README.md +1 -1
  2. package/dist/elements.js +79 -75
  3. package/dist/elements.js.map +1 -1
  4. package/dist/index.css +16 -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/components/SupportedDeviceTable/SupportedDeviceContent.d.ts +2 -1
  9. package/lib/seam/components/SupportedDeviceTable/SupportedDeviceContent.js +2 -1
  10. package/lib/seam/components/SupportedDeviceTable/SupportedDeviceContent.js.map +1 -1
  11. package/lib/seam/components/SupportedDeviceTable/SupportedDeviceFilterArea.d.ts +2 -1
  12. package/lib/seam/components/SupportedDeviceTable/SupportedDeviceFilterArea.js +12 -11
  13. package/lib/seam/components/SupportedDeviceTable/SupportedDeviceFilterArea.js.map +1 -1
  14. package/lib/seam/components/SupportedDeviceTable/SupportedDeviceTable.d.ts +2 -1
  15. package/lib/seam/components/SupportedDeviceTable/SupportedDeviceTable.js +3 -3
  16. package/lib/seam/components/SupportedDeviceTable/SupportedDeviceTable.js.map +1 -1
  17. package/lib/seam/components/SupportedDeviceTable/use-filtered-device-models.d.ts +2 -1
  18. package/lib/seam/components/SupportedDeviceTable/use-filtered-device-models.js +11 -6
  19. package/lib/seam/components/SupportedDeviceTable/use-filtered-device-models.js.map +1 -1
  20. package/lib/ui/thermostat/ClimateSettingStatus.d.ts +9 -0
  21. package/lib/ui/thermostat/ClimateSettingStatus.js +33 -0
  22. package/lib/ui/thermostat/ClimateSettingStatus.js.map +1 -0
  23. package/lib/version.d.ts +1 -1
  24. package/lib/version.js +1 -1
  25. package/package.json +1 -1
  26. package/src/lib/seam/components/SupportedDeviceTable/SupportedDeviceContent.tsx +3 -0
  27. package/src/lib/seam/components/SupportedDeviceTable/SupportedDeviceFilterArea.tsx +18 -13
  28. package/src/lib/seam/components/SupportedDeviceTable/SupportedDeviceTable.element.ts +1 -0
  29. package/src/lib/seam/components/SupportedDeviceTable/SupportedDeviceTable.tsx +4 -0
  30. package/src/lib/seam/components/SupportedDeviceTable/use-filtered-device-models.ts +11 -8
  31. package/src/lib/ui/thermostat/ClimateSettingStatus.tsx +88 -0
  32. package/src/lib/version.ts +1 -1
  33. package/src/styles/_main.scss +2 -0
  34. package/src/styles/_thermostat.scss +21 -0
@@ -14,6 +14,7 @@ interface SupportedDeviceFilterAreaProps {
14
14
  filters: DeviceModelFilters
15
15
  setFilters: Dispatch<SetStateAction<DeviceModelFilters>>
16
16
  brands: string[] | null
17
+ excludedBrands: string[]
17
18
  }
18
19
 
19
20
  export function SupportedDeviceFilterArea({
@@ -22,10 +23,11 @@ export function SupportedDeviceFilterArea({
22
23
  filters,
23
24
  setFilters,
24
25
  brands,
26
+ excludedBrands,
25
27
  }: SupportedDeviceFilterAreaProps): JSX.Element {
26
28
  const appliedFiltersCount = getAppliedFilterCount(filters)
27
29
 
28
- const availableBrands = useAvailableBrands(brands)
30
+ const availableBrands = useAvailableBrands(brands, excludedBrands)
29
31
 
30
32
  const resetFilter = (filterType: keyof DeviceModelFilters): void => {
31
33
  setFilters((filters) => ({
@@ -121,27 +123,30 @@ const getAppliedFilterCount = (filters: DeviceModelFilters): number => {
121
123
  return count
122
124
  }
123
125
 
124
- const useAvailableBrands = (brands: string[] | null): string[] => {
126
+ const useAvailableBrands = (
127
+ brands: string[] | null,
128
+ excludedBrands: string[]
129
+ ): string[] => {
125
130
  const { deviceModels } = useDeviceModels()
126
131
 
127
132
  if (deviceModels == null) return []
128
133
 
129
- const uniqueBrands = new Set<string>()
130
- for (const deviceModel of deviceModels) {
131
- const value = deviceModel.brand
132
-
133
- // UPSTREAM: API can return an empty value for brand.
134
- if (value.trim() === '') continue
135
-
136
- uniqueBrands.add(deviceModel.brand)
137
- }
138
-
139
- return Array.from(uniqueBrands)
134
+ const availableBrands = deviceModels
135
+ .map(({ brand }) => brand.trim())
136
+ .filter((brand) => {
137
+ // UPSTREAM: API can return an empty value for brand.
138
+ return brand !== ''
139
+ })
140
140
  .filter((brand) => {
141
141
  if (brands === null) return true
142
142
  return brands.includes(brand)
143
143
  })
144
+ .filter((brand) => {
145
+ return !excludedBrands.includes(brand)
146
+ })
144
147
  .map((brand) => capitalize(brand))
148
+
149
+ return Array.from(new Set(availableBrands))
145
150
  }
146
151
 
147
152
  const t = {
@@ -8,6 +8,7 @@ export const props: ElementProps<SupportedDeviceTableProps> = {
8
8
  cannotFilter: 'boolean',
9
9
  disableFilter: 'boolean',
10
10
  brands: 'array',
11
+ excludedBrands: 'array',
11
12
  className: 'string',
12
13
  }
13
14
 
@@ -12,6 +12,7 @@ export interface SupportedDeviceTableProps {
12
12
  */
13
13
  cannotFilter?: boolean
14
14
  brands?: string[] | null
15
+ excludedBrands?: string[]
15
16
  className?: string
16
17
  }
17
18
 
@@ -19,6 +20,7 @@ export function SupportedDeviceTable({
19
20
  disableFilter = false,
20
21
  cannotFilter,
21
22
  brands = null,
23
+ excludedBrands = [],
22
24
  className,
23
25
  }: SupportedDeviceTableProps = {}): JSX.Element {
24
26
  const [filterValue, setFilterValue] = useState('')
@@ -43,6 +45,7 @@ export function SupportedDeviceTable({
43
45
  filters={filters}
44
46
  setFilters={setFilters}
45
47
  brands={brands}
48
+ excludedBrands={excludedBrands}
46
49
  />
47
50
  )}
48
51
  <SupportedDeviceContent
@@ -52,6 +55,7 @@ export function SupportedDeviceTable({
52
55
  filterValue={filterValue}
53
56
  filters={filters}
54
57
  brands={brands}
58
+ excludedBrands={excludedBrands}
55
59
  />
56
60
  </div>
57
61
  )
@@ -12,10 +12,12 @@ export const useFilteredDeviceModels = ({
12
12
  filterValue,
13
13
  filters,
14
14
  brands,
15
+ excludedBrands,
15
16
  }: {
16
17
  filterValue: string
17
18
  filters: DeviceModelFilters
18
19
  brands: string[] | null
20
+ excludedBrands: string[]
19
21
  }): ReturnType<typeof useDeviceModels> => {
20
22
  const params: UseDeviceModelsParams = {}
21
23
 
@@ -33,16 +35,17 @@ export const useFilteredDeviceModels = ({
33
35
 
34
36
  const query = useDeviceModels(params)
35
37
 
36
- if (brands === null) {
37
- return query
38
- }
39
-
40
- // UPSTREAM: The API does not have a brands query parameter,
38
+ // UPSTREAM: The API does not have a brands or excludedBrands query parameter,
41
39
  // so selected brands are filtered here.
42
40
  return {
43
41
  ...query,
44
- deviceModels: query.deviceModels?.filter((deviceModel) =>
45
- brands.includes(deviceModel.brand)
46
- ),
42
+ deviceModels: query.deviceModels
43
+ ?.filter(({ brand }) => {
44
+ if (brands === null) return true
45
+ return brands.includes(brand)
46
+ })
47
+ .filter(({ brand }) => {
48
+ return !excludedBrands.includes(brand)
49
+ }),
47
50
  }
48
51
  }
@@ -0,0 +1,88 @@
1
+ import type { ClimateSetting } from 'seamapi'
2
+
3
+ import { ThermostatCoolIcon } from 'lib/icons/ThermostatCool.js'
4
+ import { ThermostatHeatIcon } from 'lib/icons/ThermostatHeat.js'
5
+ import { ThermostatHeatCoolIcon } from 'lib/icons/ThermostatHeatCool.js'
6
+ import { ThermostatOffIcon } from 'lib/icons/ThermostatOff.js'
7
+
8
+ interface ClimateSettingStatusProps {
9
+ climateSetting: ClimateSetting
10
+ temperatureUnit?: 'fahrenheit' | 'celsius'
11
+ iconPlacement?: 'left' | 'right'
12
+ }
13
+
14
+ export function ClimateSettingStatus({
15
+ climateSetting,
16
+ temperatureUnit = 'fahrenheit',
17
+ iconPlacement = 'left',
18
+ }: ClimateSettingStatusProps): JSX.Element {
19
+ return (
20
+ <div className='seam-climate-setting-status'>
21
+ {iconPlacement === 'left' && (
22
+ <ClimateSettingIcon mode={climateSetting.hvac_mode_setting} />
23
+ )}
24
+ <Content
25
+ mode={climateSetting.hvac_mode_setting}
26
+ coolingSetPoint={climateSetting.cooling_set_point_fahrenheit}
27
+ heatingSetPoint={climateSetting.heating_set_point_fahrenheit}
28
+ temperatureUnit={temperatureUnit}
29
+ />
30
+ {iconPlacement === 'right' && (
31
+ <ClimateSettingIcon mode={climateSetting.hvac_mode_setting} />
32
+ )}
33
+ </div>
34
+ )
35
+ }
36
+
37
+ function ClimateSettingIcon(props: {
38
+ mode: ClimateSetting['hvac_mode_setting']
39
+ }): JSX.Element | null {
40
+ const { mode } = props
41
+
42
+ return (
43
+ <div className='seam-climate-setting-status-icon'>
44
+ {mode === 'cool' && <ThermostatCoolIcon />}
45
+ {mode === 'heat' && <ThermostatHeatIcon />}
46
+ {mode === 'heatcool' && <ThermostatHeatCoolIcon />}
47
+ {mode === 'off' && <ThermostatOffIcon />}
48
+ </div>
49
+ )
50
+ }
51
+
52
+ function Content(props: {
53
+ mode: ClimateSetting['hvac_mode_setting']
54
+ coolingSetPoint:
55
+ | ClimateSetting['cooling_set_point_fahrenheit']
56
+ | ClimateSetting['cooling_set_point_celsius']
57
+ heatingSetPoint:
58
+ | ClimateSetting['heating_set_point_fahrenheit']
59
+ | ClimateSetting['heating_set_point_celsius']
60
+ temperatureUnit: 'fahrenheit' | 'celsius'
61
+ }): JSX.Element | null {
62
+ const { mode, coolingSetPoint, heatingSetPoint, temperatureUnit } = props
63
+ const hasCoolingSetPoint = coolingSetPoint !== undefined
64
+ const hasHeatingSetPoint = heatingSetPoint !== undefined
65
+ const unit =
66
+ temperatureUnit === 'fahrenheit' ? t.degreeFahrenheit : t.degreeCelsius
67
+
68
+ if (mode === 'cool' && hasCoolingSetPoint)
69
+ return <span>{`${coolingSetPoint}${unit}`}</span>
70
+
71
+ if (mode === 'heat' && hasHeatingSetPoint)
72
+ return <span>{`${heatingSetPoint}${unit}`}</span>
73
+
74
+ if (mode === 'heatcool' && hasHeatingSetPoint && hasCoolingSetPoint)
75
+ return (
76
+ <span>{`${heatingSetPoint}${unit} - ${coolingSetPoint}${unit}`}</span>
77
+ )
78
+
79
+ if (mode === 'off') return <span>{t.off}</span>
80
+
81
+ return null
82
+ }
83
+
84
+ const t = {
85
+ degreeFahrenheit: '°F',
86
+ degreeCelsius: '°C',
87
+ off: 'Off',
88
+ }
@@ -1,3 +1,3 @@
1
- const seamapiReactVersion = '1.44.0'
1
+ const seamapiReactVersion = '1.45.1'
2
2
 
3
3
  export default seamapiReactVersion
@@ -18,6 +18,7 @@
18
18
  @use './form-field';
19
19
  @use './radio-field';
20
20
  @use './checkbox';
21
+ @use './thermostat';
21
22
 
22
23
  .seam-components {
23
24
  // Reset
@@ -44,4 +45,5 @@
44
45
  @include access-code-form.all;
45
46
  @include alert.all;
46
47
  @include supported-device-table.all;
48
+ @include thermostat.all;
47
49
  }
@@ -0,0 +1,21 @@
1
+ @use './colors';
2
+
3
+ @mixin all {
4
+ .seam-climate-setting-status {
5
+ display: flex;
6
+ align-items: center;
7
+ gap: 4px;
8
+ color: colors.$text-gray-1;
9
+ font-size: 14px;
10
+ line-height: 118%;
11
+ white-space: nowrap;
12
+ }
13
+
14
+ .seam-climate-setting-status-icon {
15
+ width: 20px;
16
+ height: 20px;
17
+ display: flex;
18
+ justify-content: center;
19
+ align-items: center;
20
+ }
21
+ }