@seamapi/react 1.2.0 → 1.4.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 +56 -7
- package/index.css +43 -37
- package/index.css.map +1 -1
- package/index.min.css +1 -1
- package/index.min.css.map +1 -1
- package/lib/SeamProvider.d.ts +7 -5
- package/lib/SeamProvider.js +3 -3
- package/lib/SeamProvider.js.map +1 -1
- package/lib/seam/use-seam-font.d.ts +2 -2
- package/lib/seam/use-seam-font.js +11 -11
- package/lib/seam/use-seam-font.js.map +1 -1
- package/lib/seam/use-seam-styles.d.ts +3 -2
- package/lib/seam/use-seam-styles.js +17 -13
- package/lib/seam/use-seam-styles.js.map +1 -1
- package/lib/ui/AccessCodeDetails/AccessCodeDetails.d.ts +1 -0
- package/lib/ui/AccessCodeDetails/AccessCodeDevice.d.ts +1 -0
- package/lib/ui/AccessCodeTable/AccessCodeTable.d.ts +1 -0
- package/lib/ui/AccessCodeTable/CodeDetails.d.ts +1 -0
- package/lib/ui/Alert/Alert.js +1 -1
- package/lib/ui/Alert/Alert.js.map +1 -1
- package/lib/ui/Alert/Alerts.d.ts +2 -2
- package/lib/ui/Alert/Alerts.js.map +1 -1
- package/lib/ui/Button.js +1 -1
- package/lib/ui/Button.js.map +1 -1
- package/lib/ui/DeviceDetails/DeviceDetails.d.ts +1 -0
- package/lib/ui/DeviceDetails/DeviceDetails.js +10 -20
- package/lib/ui/DeviceDetails/DeviceDetails.js.map +1 -1
- package/lib/ui/DeviceDetails/DeviceModel.d.ts +1 -0
- package/lib/ui/DeviceTable/DeviceHealthBar.js +2 -2
- package/lib/ui/DeviceTable/DeviceHealthBar.js.map +1 -1
- package/lib/ui/DeviceTable/DeviceRow.d.ts +1 -0
- package/lib/ui/DeviceTable/DeviceTable.d.ts +1 -0
- package/lib/ui/IconButton.d.ts +1 -0
- package/lib/ui/Menu/Menu.js +1 -3
- package/lib/ui/Menu/Menu.js.map +1 -1
- package/lib/ui/SupportedDeviceTable/FilterCategoryMenu.d.ts +3 -9
- package/lib/ui/SupportedDeviceTable/FilterCategoryMenu.js +3 -7
- package/lib/ui/SupportedDeviceTable/FilterCategoryMenu.js.map +1 -1
- package/lib/ui/SupportedDeviceTable/SupportedDeviceContent.js +27 -34
- package/lib/ui/SupportedDeviceTable/SupportedDeviceContent.js.map +1 -1
- package/lib/ui/SupportedDeviceTable/SupportedDeviceFilterArea.js +3 -3
- package/lib/ui/SupportedDeviceTable/SupportedDeviceFilterArea.js.map +1 -1
- package/lib/ui/SupportedDeviceTable/SupportedDeviceRow.js +1 -1
- package/lib/ui/SupportedDeviceTable/SupportedDeviceRow.js.map +1 -1
- package/lib/ui/Table/EmptyPlaceholder.d.ts +1 -0
- package/lib/ui/Table/TableCell.d.ts +1 -0
- package/lib/ui/Table/TableFilterBar/TableFilterBar.d.ts +2 -2
- package/lib/ui/Table/TableFilterBar/TableFilterBar.js +2 -3
- package/lib/ui/Table/TableFilterBar/TableFilterBar.js.map +1 -1
- package/lib/ui/Table/TableRow.d.ts +1 -0
- package/lib/ui/TextButton.d.ts +1 -0
- package/lib/ui/TextField/SearchTextField.d.ts +1 -0
- package/lib/ui/TextField/TextField.js +1 -1
- package/lib/ui/TextField/TextField.js.map +1 -1
- package/lib/ui/device/BatteryStatus.d.ts +1 -0
- package/lib/ui/device/LockStatus.d.ts +1 -0
- package/lib/ui/device/OnlineStatus.d.ts +1 -0
- package/lib/ui/layout/ContentHeader.d.ts +1 -0
- package/lib/ui/layout/DotDivider.d.ts +1 -0
- package/lib/ui/typography/Title.d.ts +1 -0
- package/lib/version.d.ts +2 -0
- package/lib/version.js +1 -0
- package/lib/version.js.map +1 -0
- package/package.json +4 -3
- package/src/lib/SeamProvider.tsx +17 -10
- package/src/lib/seam/use-seam-font.ts +10 -11
- package/src/lib/seam/use-seam-styles.ts +23 -13
- package/src/lib/ui/Alert/Alert.tsx +1 -1
- package/src/lib/ui/Alert/Alerts.tsx +4 -2
- package/src/lib/ui/Button.tsx +1 -1
- package/src/lib/ui/DeviceDetails/DeviceDetails.tsx +11 -31
- package/src/lib/ui/DeviceTable/DeviceHealthBar.tsx +2 -2
- package/src/lib/ui/Menu/Menu.tsx +5 -4
- package/src/lib/ui/SupportedDeviceTable/FilterCategoryMenu.tsx +6 -11
- package/src/lib/ui/SupportedDeviceTable/SupportedDeviceContent.tsx +42 -54
- package/src/lib/ui/SupportedDeviceTable/SupportedDeviceFilterArea.tsx +4 -4
- package/src/lib/ui/SupportedDeviceTable/SupportedDeviceRow.tsx +1 -1
- package/src/lib/ui/Table/TableFilterBar/TableFilterBar.tsx +3 -6
- package/src/lib/ui/TextField/TextField.tsx +1 -1
- package/src/lib/version.ts +1 -0
- package/src/styles/_main.scss +2 -2
- package/src/styles/_menus.scss +9 -1
- package/src/styles/{_supported-devices.scss → _supported-device-table.scss} +8 -8
package/lib/version.d.ts
ADDED
package/lib/version.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default '1.4.0'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../src/lib/version.ts"],"names":[],"mappings":"AAAA,eAAe,IAAI,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seamapi/react",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Seam React component library.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -65,6 +65,7 @@
|
|
|
65
65
|
"lint": "eslint --ignore-path .gitignore --ext .js,.cjs,.mjs,.ts,.tsx .",
|
|
66
66
|
"prelint": "prettier --check --ignore-path .gitignore .",
|
|
67
67
|
"postlint": "stylelint '**/*.scss'",
|
|
68
|
+
"prepack": "echo \"export default '$(jq -r .version < package.json)'\" > lib/version.js",
|
|
68
69
|
"postversion": "git push --follow-tags",
|
|
69
70
|
"storybook": "storybook dev --port 6006",
|
|
70
71
|
"storybook:docs": "storybook dev --docs --port 6007",
|
|
@@ -94,7 +95,7 @@
|
|
|
94
95
|
"@tanstack/react-query": "^4.28.0",
|
|
95
96
|
"classnames": "^2.3.2",
|
|
96
97
|
"luxon": "^3.3.0",
|
|
97
|
-
"seamapi": "^
|
|
98
|
+
"seamapi": "^6.1.0",
|
|
98
99
|
"uuid": "^9.0.0"
|
|
99
100
|
},
|
|
100
101
|
"devDependencies": {
|
|
@@ -113,7 +114,7 @@
|
|
|
113
114
|
"@storybook/react": "^7.0.2",
|
|
114
115
|
"@storybook/react-webpack5": "^7.0.2",
|
|
115
116
|
"@storybook/testing-library": "^0.1.0",
|
|
116
|
-
"@svgr/cli": "^
|
|
117
|
+
"@svgr/cli": "^8.0.1",
|
|
117
118
|
"@testing-library/react": "^14.0.0",
|
|
118
119
|
"@types/jest": "^29.5.0",
|
|
119
120
|
"@types/luxon": "^3.3.0",
|
package/src/lib/SeamProvider.tsx
CHANGED
|
@@ -25,14 +25,12 @@ export interface SeamContext {
|
|
|
25
25
|
clientSessionToken?: string | undefined
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
type SeamProviderProps =
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
| (SeamProviderPropsWithClientSessionToken & AllowedSeamClientOptions)
|
|
35
|
-
)
|
|
28
|
+
type SeamProviderProps = SeamProviderBaseProps &
|
|
29
|
+
(
|
|
30
|
+
| SeamProviderPropsWithClient
|
|
31
|
+
| (SeamProviderPropsWithPublishableKey & AllowedSeamClientOptions)
|
|
32
|
+
| (SeamProviderPropsWithClientSessionToken & AllowedSeamClientOptions)
|
|
33
|
+
)
|
|
36
34
|
|
|
37
35
|
interface SeamProviderPropsWithClient {
|
|
38
36
|
client: Seam
|
|
@@ -47,10 +45,19 @@ interface SeamProviderPropsWithClientSessionToken {
|
|
|
47
45
|
clientSessionToken: string
|
|
48
46
|
}
|
|
49
47
|
|
|
48
|
+
interface SeamProviderBaseProps {
|
|
49
|
+
disableCssInjection?: boolean | undefined
|
|
50
|
+
disableFontInjection?: boolean | undefined
|
|
51
|
+
useUnminifiedCss?: boolean | undefined
|
|
52
|
+
}
|
|
53
|
+
|
|
50
54
|
type AllowedSeamClientOptions = Pick<SeamClientOptions, 'endpoint'>
|
|
51
55
|
|
|
52
56
|
export function SeamProvider({
|
|
53
57
|
children,
|
|
58
|
+
disableCssInjection = false,
|
|
59
|
+
disableFontInjection = false,
|
|
60
|
+
useUnminifiedCss = false,
|
|
54
61
|
...props
|
|
55
62
|
}: PropsWithChildren<SeamProviderProps>): JSX.Element {
|
|
56
63
|
const { Provider } = seamContext
|
|
@@ -76,8 +83,8 @@ export function SeamProvider({
|
|
|
76
83
|
)
|
|
77
84
|
}
|
|
78
85
|
|
|
79
|
-
useSeamStyles({
|
|
80
|
-
useSeamFont({
|
|
86
|
+
useSeamStyles({ disabled: disableCssInjection, unminified: useUnminifiedCss })
|
|
87
|
+
useSeamFont({ disabled: disableFontInjection })
|
|
81
88
|
|
|
82
89
|
return (
|
|
83
90
|
<div className='seam-components'>
|
|
@@ -3,19 +3,18 @@ import { useEffect } from 'react'
|
|
|
3
3
|
const fontUrl =
|
|
4
4
|
'https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@400;600;700&display=swap'
|
|
5
5
|
|
|
6
|
-
export const useSeamFont = ({
|
|
6
|
+
export const useSeamFont = ({ disabled = false }: { disabled?: boolean }) => {
|
|
7
7
|
useEffect(() => {
|
|
8
|
+
if (disabled) return
|
|
8
9
|
if (globalThis.document == null) return
|
|
9
|
-
if (!enabled) return
|
|
10
|
-
const linkEl = globalThis.document.querySelector(`link[href="${fontUrl}"]`)
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
link.rel = 'stylesheet'
|
|
15
|
-
link.type = 'text/css'
|
|
16
|
-
link.href = fontUrl
|
|
11
|
+
const linkEl = globalThis.document.querySelector(`link[href="${fontUrl}"]`)
|
|
12
|
+
if (linkEl != null) return
|
|
17
13
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
const link = globalThis.document.createElement('link')
|
|
15
|
+
link.rel = 'stylesheet'
|
|
16
|
+
link.type = 'text/css'
|
|
17
|
+
link.href = fontUrl
|
|
18
|
+
globalThis.document.head.appendChild(link)
|
|
19
|
+
}, [disabled])
|
|
21
20
|
}
|
|
@@ -1,21 +1,31 @@
|
|
|
1
1
|
import { useEffect } from 'react'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
import version from 'lib/version.js'
|
|
4
|
+
|
|
5
|
+
const origin = 'https://react.seam.co'
|
|
6
|
+
|
|
7
|
+
export const useSeamStyles = ({
|
|
8
|
+
disabled = false,
|
|
9
|
+
unminified = false,
|
|
10
|
+
}: {
|
|
11
|
+
unminified?: boolean
|
|
12
|
+
disabled?: boolean
|
|
13
|
+
}) => {
|
|
14
|
+
const ext = `${unminified ? '' : 'min.'}css`
|
|
15
|
+
const cssUrl = `${origin}/v/${version ?? ''}/index.${ext}`
|
|
5
16
|
|
|
6
|
-
export const useSeamStyles = ({ enabled }: { enabled: boolean }) => {
|
|
7
17
|
useEffect(() => {
|
|
18
|
+
if (version === null) return
|
|
19
|
+
if (disabled) return
|
|
8
20
|
if (globalThis.document == null) return
|
|
9
|
-
if (!enabled) return
|
|
10
|
-
const linkEl = globalThis.document.querySelector(`link[href="${cssUrl}"]`)
|
|
11
21
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
link.rel = 'stylesheet'
|
|
15
|
-
link.type = 'text/css'
|
|
16
|
-
link.href = cssUrl
|
|
22
|
+
const linkEl = globalThis.document.querySelector(`link[href="${cssUrl}"]`)
|
|
23
|
+
if (linkEl != null) return
|
|
17
24
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
25
|
+
const link = globalThis.document.createElement('link')
|
|
26
|
+
link.rel = 'stylesheet'
|
|
27
|
+
link.type = 'text/css'
|
|
28
|
+
link.href = cssUrl
|
|
29
|
+
globalThis.document.head.appendChild(link)
|
|
30
|
+
}, [disabled, cssUrl])
|
|
21
31
|
}
|
|
@@ -35,7 +35,7 @@ export function Alert(props: AlertProps): JSX.Element {
|
|
|
35
35
|
<p className='seam-alert-message'>{message}</p>
|
|
36
36
|
</div>
|
|
37
37
|
</div>
|
|
38
|
-
{props.action
|
|
38
|
+
{props.action != null && <Action {...props.action} />}
|
|
39
39
|
</div>
|
|
40
40
|
)
|
|
41
41
|
}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import classNames from 'classnames'
|
|
2
|
+
import type { PropsWithChildren } from 'react'
|
|
2
3
|
|
|
3
4
|
import { Alert, type AlertProps } from 'lib/ui/Alert/Alert.js'
|
|
4
5
|
|
|
5
6
|
interface AlertsProps {
|
|
6
7
|
alerts?: AlertProps[]
|
|
7
|
-
children?: JSX.Element
|
|
8
8
|
className?: string
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
export function Alerts(
|
|
11
|
+
export function Alerts(
|
|
12
|
+
props: PropsWithChildren<AlertsProps>
|
|
13
|
+
): JSX.Element | null {
|
|
12
14
|
const { alerts, children, className } = props
|
|
13
15
|
|
|
14
16
|
if (alerts?.length === 0) return null
|
package/src/lib/ui/Button.tsx
CHANGED
|
@@ -7,7 +7,6 @@ import { useAccessCodes } from 'lib/seam/access-codes/use-access-codes.js'
|
|
|
7
7
|
import { isLockDevice } from 'lib/seam/devices/types.js'
|
|
8
8
|
import { useToggleLock } from 'lib/seam/devices/use-toggle-lock.js'
|
|
9
9
|
import { AccessCodeTable } from 'lib/ui/AccessCodeTable/AccessCodeTable.js'
|
|
10
|
-
import type { AlertProps } from 'lib/ui/Alert/Alert.js'
|
|
11
10
|
import { Alerts } from 'lib/ui/Alert/Alerts.js'
|
|
12
11
|
import { Button } from 'lib/ui/Button.js'
|
|
13
12
|
import { BatteryStatus } from 'lib/ui/device/BatteryStatus.js'
|
|
@@ -57,36 +56,6 @@ function LockDeviceDetails(props: { device: LockDevice; onBack?: () => void }) {
|
|
|
57
56
|
const accessCodeLength =
|
|
58
57
|
device.properties?.schlage_metadata?.access_code_length
|
|
59
58
|
|
|
60
|
-
function generateDeviceAlerts() {
|
|
61
|
-
if (device.errors.length === 0 && device.warnings.length === 0) {
|
|
62
|
-
return []
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const alerts: AlertProps[] = []
|
|
66
|
-
|
|
67
|
-
if (device.errors.length > 0) {
|
|
68
|
-
alerts.push(
|
|
69
|
-
...device.errors.map((error) => ({
|
|
70
|
-
variant: 'error' as const,
|
|
71
|
-
message: error.message,
|
|
72
|
-
}))
|
|
73
|
-
)
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
if (device.warnings.length > 0) {
|
|
77
|
-
alerts.push(
|
|
78
|
-
...device.warnings.map((warning) => ({
|
|
79
|
-
variant: 'warning' as const,
|
|
80
|
-
message: warning.message,
|
|
81
|
-
}))
|
|
82
|
-
)
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return alerts
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const alerts = generateDeviceAlerts()
|
|
89
|
-
|
|
90
59
|
if (accessCodes == null) {
|
|
91
60
|
return null
|
|
92
61
|
}
|
|
@@ -100,6 +69,17 @@ function LockDeviceDetails(props: { device: LockDevice; onBack?: () => void }) {
|
|
|
100
69
|
)
|
|
101
70
|
}
|
|
102
71
|
|
|
72
|
+
const alerts = [
|
|
73
|
+
...device.errors.map((error) => ({
|
|
74
|
+
variant: 'error' as const,
|
|
75
|
+
message: error.message,
|
|
76
|
+
})),
|
|
77
|
+
...device.warnings.map((warning) => ({
|
|
78
|
+
variant: 'warning' as const,
|
|
79
|
+
message: warning.message,
|
|
80
|
+
})),
|
|
81
|
+
]
|
|
82
|
+
|
|
103
83
|
return (
|
|
104
84
|
<div className='seam-device-details'>
|
|
105
85
|
<ContentHeader title='Device' onBack={onBack} />
|
|
@@ -32,7 +32,7 @@ export function DeviceHealthBar({
|
|
|
32
32
|
|
|
33
33
|
if (issueCount === 0) {
|
|
34
34
|
return (
|
|
35
|
-
<TableFilterBar>
|
|
35
|
+
<TableFilterBar filterCleared>
|
|
36
36
|
<TableFilterItem>
|
|
37
37
|
<CheckIcon />
|
|
38
38
|
{t.devicesOk}
|
|
@@ -43,7 +43,7 @@ export function DeviceHealthBar({
|
|
|
43
43
|
|
|
44
44
|
return (
|
|
45
45
|
<TableFilterBar
|
|
46
|
-
|
|
46
|
+
filterCleared={filter == null}
|
|
47
47
|
onFilterClear={() => {
|
|
48
48
|
onFilterSelect(null)
|
|
49
49
|
}}
|
package/src/lib/ui/Menu/Menu.tsx
CHANGED
|
@@ -135,10 +135,11 @@ export function Menu({
|
|
|
135
135
|
{renderButton({ onOpen: handleOpen })}
|
|
136
136
|
{createPortal(
|
|
137
137
|
<div
|
|
138
|
-
className={classNames(
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
138
|
+
className={classNames(
|
|
139
|
+
'seam-menu-bg',
|
|
140
|
+
backgroundProps?.className,
|
|
141
|
+
visible ? 'seam-menu-visible' : 'seam-menu-hidden'
|
|
142
|
+
)}
|
|
142
143
|
onClick={(event) => {
|
|
143
144
|
event.stopPropagation()
|
|
144
145
|
handleClose()
|
|
@@ -11,26 +11,26 @@ interface FilterCategoryMenuBaseProps {
|
|
|
11
11
|
|
|
12
12
|
export type FilterCategoryMenuProps =
|
|
13
13
|
| (FilterCategoryMenuBaseProps & {
|
|
14
|
-
|
|
14
|
+
hideAllOption?: false
|
|
15
15
|
onAllOptionSelect: () => void
|
|
16
16
|
})
|
|
17
17
|
| (FilterCategoryMenuBaseProps & {
|
|
18
|
-
|
|
18
|
+
hideAllOption: true
|
|
19
19
|
onAllOptionSelect?: never
|
|
20
20
|
})
|
|
21
21
|
|
|
22
22
|
export function FilterCategoryMenu({
|
|
23
|
-
label,
|
|
23
|
+
label = 'Filter',
|
|
24
24
|
options,
|
|
25
|
-
|
|
25
|
+
hideAllOption = false,
|
|
26
26
|
onSelect,
|
|
27
27
|
onAllOptionSelect,
|
|
28
28
|
buttonLabel,
|
|
29
29
|
}: FilterCategoryMenuProps) {
|
|
30
|
-
const usableOptions =
|
|
30
|
+
const usableOptions = hideAllOption ? options : ['All', ...options]
|
|
31
31
|
|
|
32
32
|
return (
|
|
33
|
-
<div className='seam-supported-
|
|
33
|
+
<div className='seam-supported-device-table-filter-menu-wrap'>
|
|
34
34
|
<p>{label}</p>
|
|
35
35
|
<Menu
|
|
36
36
|
renderButton={({ onOpen }) => (
|
|
@@ -58,8 +58,3 @@ export function FilterCategoryMenu({
|
|
|
58
58
|
</div>
|
|
59
59
|
)
|
|
60
60
|
}
|
|
61
|
-
|
|
62
|
-
FilterCategoryMenu.defaultProps = {
|
|
63
|
-
addAllOption: true,
|
|
64
|
-
buttonLabel: 'Filter',
|
|
65
|
-
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { useQuery } from '@tanstack/react-query'
|
|
2
|
-
import
|
|
3
|
-
import { useCallback, useEffect, useState } from 'react'
|
|
2
|
+
import { useState } from 'react'
|
|
4
3
|
|
|
5
4
|
import { Button } from 'lib/ui/Button.js'
|
|
6
5
|
import { SupportedDeviceFilterArea } from 'lib/ui/SupportedDeviceTable/SupportedDeviceFilterArea.js'
|
|
@@ -9,8 +8,6 @@ import type { DeviceModel, Filters } from 'lib/ui/SupportedDeviceTable/types.js'
|
|
|
9
8
|
import { SupportedDeviceHeader } from './SupportedDeviceHeader.js'
|
|
10
9
|
import { SupportedDeviceRow } from './SupportedDeviceRow.js'
|
|
11
10
|
|
|
12
|
-
const BASE_URL = 'https://devicedb.seam.co/api/device_models/list'
|
|
13
|
-
|
|
14
11
|
export interface SupportedDeviceContentProps {
|
|
15
12
|
cannotFilter?: boolean
|
|
16
13
|
}
|
|
@@ -18,7 +15,6 @@ export interface SupportedDeviceContentProps {
|
|
|
18
15
|
export function SupportedDeviceContent({
|
|
19
16
|
cannotFilter = false,
|
|
20
17
|
}: SupportedDeviceContentProps) {
|
|
21
|
-
const [allDeviceModels, setAllDeviceModels] = useState<DeviceModel[]>([])
|
|
22
18
|
const [filterValue, setFilterValue] = useState('')
|
|
23
19
|
const [filters, setFilters] = useState<Filters>({
|
|
24
20
|
supportedOnly: false,
|
|
@@ -26,54 +22,46 @@ export function SupportedDeviceContent({
|
|
|
26
22
|
brand: null,
|
|
27
23
|
})
|
|
28
24
|
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if (filters.supportedOnly) {
|
|
37
|
-
queries.push('support_level=live')
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (filters.category !== null) {
|
|
41
|
-
queries.push(`main_category=${encodeURIComponent(filters.category)}`)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (filters.brand !== null) {
|
|
45
|
-
queries.push(`brand=${encodeURIComponent(filters.brand)}`)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const url = `${BASE_URL}?${queries.join('&')}`
|
|
49
|
-
return await axios.get(url)
|
|
50
|
-
}, [filterValue, filters])
|
|
51
|
-
|
|
52
|
-
const { data, isLoading, isError, refetch } = useQuery<{
|
|
53
|
-
data: {
|
|
54
|
-
device_models?: DeviceModel[]
|
|
55
|
-
}
|
|
56
|
-
}>({
|
|
25
|
+
const {
|
|
26
|
+
data: deviceModels,
|
|
27
|
+
isLoading,
|
|
28
|
+
isError,
|
|
29
|
+
refetch,
|
|
30
|
+
} = useQuery<DeviceModel[]>({
|
|
57
31
|
queryKey: ['supported_devices', filterValue, filters],
|
|
58
|
-
queryFn:
|
|
32
|
+
queryFn: async () => {
|
|
33
|
+
const url = new URL('https://devicedb.seam.co/api/device_models/list')
|
|
34
|
+
|
|
35
|
+
if (filterValue.trim() !== '') {
|
|
36
|
+
url.searchParams.set('text_search', filterValue.trim())
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (filters.supportedOnly) {
|
|
40
|
+
url.searchParams.set('support_level', 'live')
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (filters.category !== null) {
|
|
44
|
+
url.searchParams.set('main_category', filters.category)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (filters.brand !== null) {
|
|
48
|
+
url.searchParams.set('brand', filters.brand)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const res = await fetch(url)
|
|
52
|
+
if (!res.ok) {
|
|
53
|
+
throw new Error('Failed to load device models')
|
|
54
|
+
}
|
|
55
|
+
const data = await res.json()
|
|
56
|
+
return data?.device_models ?? []
|
|
57
|
+
},
|
|
59
58
|
})
|
|
60
59
|
|
|
61
|
-
useEffect(() => {
|
|
62
|
-
if (
|
|
63
|
-
data?.data?.device_models !== undefined &&
|
|
64
|
-
allDeviceModels.length === 0
|
|
65
|
-
) {
|
|
66
|
-
setAllDeviceModels(data.data.device_models)
|
|
67
|
-
}
|
|
68
|
-
}, [data, allDeviceModels.length])
|
|
69
|
-
|
|
70
|
-
const deviceModels = data?.data?.device_models ?? []
|
|
71
|
-
|
|
72
60
|
return (
|
|
73
|
-
<div className='seam-supported-
|
|
61
|
+
<div className='seam-supported-device-table-content-wrap'>
|
|
74
62
|
{!cannotFilter && (
|
|
75
63
|
<SupportedDeviceFilterArea
|
|
76
|
-
deviceModels={
|
|
64
|
+
deviceModels={deviceModels ?? []}
|
|
77
65
|
filterValue={filterValue}
|
|
78
66
|
setFilterValue={setFilterValue}
|
|
79
67
|
filters={filters}
|
|
@@ -82,13 +70,13 @@ export function SupportedDeviceContent({
|
|
|
82
70
|
)}
|
|
83
71
|
|
|
84
72
|
{isLoading && (
|
|
85
|
-
<div className='seam-supported-
|
|
73
|
+
<div className='seam-supported-device-table-content-state-block'>
|
|
86
74
|
<p>Loading device models...</p>
|
|
87
75
|
</div>
|
|
88
76
|
)}
|
|
89
77
|
|
|
90
78
|
{isError && (
|
|
91
|
-
<div className='seam-supported-
|
|
79
|
+
<div className='seam-supported-device-table-content-state-block'>
|
|
92
80
|
<p>There was an error fetching device models.</p>
|
|
93
81
|
<Button
|
|
94
82
|
variant='solid'
|
|
@@ -102,8 +90,8 @@ export function SupportedDeviceContent({
|
|
|
102
90
|
</div>
|
|
103
91
|
)}
|
|
104
92
|
|
|
105
|
-
{!isLoading && !isError &&
|
|
106
|
-
<table className='seam-supported-
|
|
93
|
+
{!isLoading && !isError && deviceModels !== null && (
|
|
94
|
+
<table className='seam-supported-device-table-content'>
|
|
107
95
|
<SupportedDeviceHeader />
|
|
108
96
|
<tbody>
|
|
109
97
|
{deviceModels.length !== 0 &&
|
|
@@ -115,9 +103,9 @@ export function SupportedDeviceContent({
|
|
|
115
103
|
))}
|
|
116
104
|
|
|
117
105
|
{deviceModels.length === 0 && (
|
|
118
|
-
<tr className='seam-supported-
|
|
106
|
+
<tr className='seam-supported-device-table-content-message-row'>
|
|
119
107
|
<td colSpan={6}>
|
|
120
|
-
<div className='seam-supported-
|
|
108
|
+
<div className='seam-supported-device-table-content-message'>
|
|
121
109
|
{filterValue.length === 0 ? (
|
|
122
110
|
<p>No device models found.</p>
|
|
123
111
|
) : (
|
|
@@ -129,7 +117,7 @@ export function SupportedDeviceContent({
|
|
|
129
117
|
onClick={() => {
|
|
130
118
|
setFilterValue('')
|
|
131
119
|
}}
|
|
132
|
-
className='seam-supported-
|
|
120
|
+
className='seam-supported-device-table-content-message-clear-search'
|
|
133
121
|
>
|
|
134
122
|
Clear search terms
|
|
135
123
|
</Button>
|
|
@@ -55,7 +55,7 @@ export function SupportedDeviceFilterArea({
|
|
|
55
55
|
: 'Filter'
|
|
56
56
|
|
|
57
57
|
return (
|
|
58
|
-
<div className='seam-supported-
|
|
58
|
+
<div className='seam-supported-device-table-filter-area'>
|
|
59
59
|
<div className='seam-deliberate-block' />
|
|
60
60
|
<div className='seam-filters-wrap'>
|
|
61
61
|
<Menu
|
|
@@ -70,7 +70,7 @@ export function SupportedDeviceFilterArea({
|
|
|
70
70
|
)}
|
|
71
71
|
>
|
|
72
72
|
<div
|
|
73
|
-
className='seam-supported-
|
|
73
|
+
className='seam-supported-device-table-filter-menu'
|
|
74
74
|
onClick={(event) => {
|
|
75
75
|
event.stopPropagation()
|
|
76
76
|
}}
|
|
@@ -116,13 +116,13 @@ export function SupportedDeviceFilterArea({
|
|
|
116
116
|
</div>
|
|
117
117
|
</Menu>
|
|
118
118
|
|
|
119
|
-
<div className='seam-supported-
|
|
119
|
+
<div className='seam-supported-device-table-filter-area-search-bar-wrap'>
|
|
120
120
|
<SearchTextField
|
|
121
121
|
value={filterValue}
|
|
122
122
|
onChange={(value) => {
|
|
123
123
|
setFilterValue(value)
|
|
124
124
|
}}
|
|
125
|
-
className='seam-supported-
|
|
125
|
+
className='seam-supported-device-table-filter-area-search-bar'
|
|
126
126
|
/>
|
|
127
127
|
</div>
|
|
128
128
|
</div>
|
|
@@ -11,7 +11,7 @@ export function SupportedDeviceRow({ deviceModel }: SupportedDeviceRowProps) {
|
|
|
11
11
|
return (
|
|
12
12
|
<tr>
|
|
13
13
|
<td>
|
|
14
|
-
<img
|
|
14
|
+
<img width={40} src={deviceModel.icon_url} />
|
|
15
15
|
</td>
|
|
16
16
|
<td>{deviceModel.main_category}</td>
|
|
17
17
|
<td>{deviceModel.model_name}</td>
|
|
@@ -3,23 +3,20 @@ import { IconButton } from 'lib/ui/IconButton.js'
|
|
|
3
3
|
import type { ButtonProps, DivProps } from 'lib/ui/types.js'
|
|
4
4
|
|
|
5
5
|
interface TableFilterBarProps extends DivProps {
|
|
6
|
-
|
|
6
|
+
filterCleared?: boolean
|
|
7
7
|
onFilterClear?: () => void
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export function TableFilterBar({
|
|
11
|
-
|
|
11
|
+
filterCleared = false,
|
|
12
12
|
onFilterClear,
|
|
13
13
|
children,
|
|
14
14
|
...props
|
|
15
15
|
}: TableFilterBarProps) {
|
|
16
|
-
const showClearFilterButton = isFilterCleared === false
|
|
17
16
|
return (
|
|
18
17
|
<div className='seam-table-filter-bar' {...props}>
|
|
19
18
|
{children}{' '}
|
|
20
|
-
{
|
|
21
|
-
<ClearFiltersButton onClick={onFilterClear} />
|
|
22
|
-
) : null}
|
|
19
|
+
{!filterCleared && <ClearFiltersButton onClick={onFilterClear} />}
|
|
23
20
|
</div>
|
|
24
21
|
)
|
|
25
22
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default null
|
package/src/styles/_main.scss
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
@use './device-details';
|
|
13
13
|
@use './device-table';
|
|
14
14
|
@use './access-code-details';
|
|
15
|
-
@use './supported-
|
|
15
|
+
@use './supported-device-table';
|
|
16
16
|
|
|
17
17
|
.seam-components {
|
|
18
18
|
// Reset
|
|
@@ -33,5 +33,5 @@
|
|
|
33
33
|
@include access-code-table.all;
|
|
34
34
|
@include access-code-details.all;
|
|
35
35
|
@include alert.all;
|
|
36
|
-
@include supported-
|
|
36
|
+
@include supported-device-table.all;
|
|
37
37
|
}
|
package/src/styles/_menus.scss
CHANGED
|
@@ -9,7 +9,15 @@
|
|
|
9
9
|
position: fixed;
|
|
10
10
|
inset: 0;
|
|
11
11
|
z-index: 999999;
|
|
12
|
-
display: none;
|
|
12
|
+
display: none;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.seam-menu-hidden {
|
|
16
|
+
display: none;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.seam-menu-visible {
|
|
20
|
+
display: flex;
|
|
13
21
|
}
|
|
14
22
|
|
|
15
23
|
.seam-menu-content {
|