@seamapi/react 0.22.0 → 0.22.2
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/index.css +122 -0
- package/index.css.map +1 -1
- package/index.min.css +1 -1
- package/index.min.css.map +1 -1
- package/lib/copy.d.ts +1 -0
- package/lib/copy.js +4 -0
- package/lib/copy.js.map +1 -0
- package/lib/icons/Copy.d.ts +2 -0
- package/lib/icons/Copy.js +5 -0
- package/lib/icons/Copy.js.map +1 -0
- package/lib/ui/AccessCodeTable/AccessCodeTable.js +12 -1
- package/lib/ui/AccessCodeTable/AccessCodeTable.js.map +1 -1
- package/lib/ui/Menu/Menu.d.ts +19 -0
- package/lib/ui/Menu/Menu.js +93 -0
- package/lib/ui/Menu/Menu.js.map +1 -0
- package/lib/ui/Menu/MenuItem.d.ts +6 -0
- package/lib/ui/Menu/MenuItem.js +10 -0
- package/lib/ui/Menu/MenuItem.js.map +1 -0
- package/lib/ui/Menu/MoreActionsMenu.d.ts +6 -0
- package/lib/ui/Menu/MoreActionsMenu.js +11 -0
- package/lib/ui/Menu/MoreActionsMenu.js.map +1 -0
- package/lib/ui/SupportedDevices/SupportedDeviceRow.d.ts +5 -0
- package/lib/ui/SupportedDevices/SupportedDeviceRow.js +18 -0
- package/lib/ui/SupportedDevices/SupportedDeviceRow.js.map +1 -0
- package/lib/ui/SupportedDevices/SupportedDevices.d.ts +1 -0
- package/lib/ui/SupportedDevices/SupportedDevices.js +21 -0
- package/lib/ui/SupportedDevices/SupportedDevices.js.map +1 -0
- package/lib/ui/SupportedDevices/SupportedDevicesHeader.d.ts +1 -0
- package/lib/ui/SupportedDevices/SupportedDevicesHeader.js +5 -0
- package/lib/ui/SupportedDevices/SupportedDevicesHeader.js.map +1 -0
- package/lib/ui/SupportedDevices/types.d.ts +10 -0
- package/lib/ui/SupportedDevices/types.js +2 -0
- package/lib/ui/SupportedDevices/types.js.map +1 -0
- package/package.json +2 -2
- package/src/lib/copy.ts +3 -0
- package/src/lib/icons/Copy.tsx +30 -0
- package/src/lib/ui/AccessCodeTable/AccessCodeTable.tsx +27 -1
- package/src/lib/ui/Menu/Menu.tsx +179 -0
- package/src/lib/ui/Menu/MenuItem.tsx +24 -0
- package/src/lib/ui/Menu/MoreActionsMenu.tsx +27 -0
- package/src/lib/ui/SupportedDevices/SupportedDeviceRow.tsx +46 -0
- package/src/lib/ui/SupportedDevices/SupportedDevices.tsx +65 -0
- package/src/lib/ui/SupportedDevices/SupportedDevicesHeader.tsx +14 -0
- package/src/lib/ui/SupportedDevices/types.ts +10 -0
- package/src/styles/_access-code-table.scss +9 -0
- package/src/styles/_main.scss +4 -0
- package/src/styles/_menus.scss +46 -0
- package/src/styles/_supported-devices.scss +105 -0
package/src/lib/copy.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { SVGProps } from 'react'
|
|
2
|
+
export const CopyIcon = (props: SVGProps<SVGSVGElement>): JSX.Element => (
|
|
3
|
+
<svg
|
|
4
|
+
xmlns='http://www.w3.org/2000/svg'
|
|
5
|
+
width={18}
|
|
6
|
+
height={18}
|
|
7
|
+
fill='none'
|
|
8
|
+
{...props}
|
|
9
|
+
>
|
|
10
|
+
<mask
|
|
11
|
+
id='copy_svg__a'
|
|
12
|
+
width={18}
|
|
13
|
+
height={18}
|
|
14
|
+
x={0}
|
|
15
|
+
y={0}
|
|
16
|
+
maskUnits='userSpaceOnUse'
|
|
17
|
+
style={{
|
|
18
|
+
maskType: 'alpha',
|
|
19
|
+
}}
|
|
20
|
+
>
|
|
21
|
+
<path fill='#D9D9D9' d='M0 0h18v18H0z' />
|
|
22
|
+
</mask>
|
|
23
|
+
<g mask='url(#copy_svg__a)'>
|
|
24
|
+
<path
|
|
25
|
+
fill='#007AFC'
|
|
26
|
+
d='M3.75 16.5c-.413 0-.766-.147-1.06-.44A1.446 1.446 0 0 1 2.25 15V4.5h1.5V15H12v1.5H3.75Zm3-3c-.412 0-.766-.147-1.059-.44A1.445 1.445 0 0 1 5.25 12V3c0-.413.147-.766.441-1.06.293-.293.647-.44 1.059-.44h6.75c.412 0 .766.147 1.06.44.293.294.44.647.44 1.06v9c0 .412-.147.766-.44 1.06-.294.293-.648.44-1.06.44H6.75Zm0-1.5h6.75V3H6.75v9Z'
|
|
27
|
+
/>
|
|
28
|
+
</g>
|
|
29
|
+
</svg>
|
|
30
|
+
)
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import { useState } from 'react'
|
|
2
2
|
import type { AccessCode } from 'seamapi'
|
|
3
3
|
|
|
4
|
+
import { copy } from 'lib/copy.js'
|
|
4
5
|
import { AccessCodeKeyIcon } from 'lib/icons/AccessCodeKey.js'
|
|
6
|
+
import { CopyIcon } from 'lib/icons/Copy.js'
|
|
5
7
|
import { useAccessCodes } from 'lib/seam/access-codes/use-access-codes.js'
|
|
6
8
|
import { AccessCodeDetails } from 'lib/ui/AccessCodeDetails/AccessCodeDetails.js'
|
|
7
9
|
import { CodeDetails } from 'lib/ui/AccessCodeTable/CodeDetails.js'
|
|
8
10
|
import { ContentHeader } from 'lib/ui/layout/ContentHeader.js'
|
|
11
|
+
import { MenuItem } from 'lib/ui/Menu/MenuItem.js'
|
|
12
|
+
import { MoreActionsMenu } from 'lib/ui/Menu/MoreActionsMenu.js'
|
|
9
13
|
import { TableBody } from 'lib/ui/Table/TableBody.js'
|
|
10
14
|
import { TableCell } from 'lib/ui/Table/TableCell.js'
|
|
11
15
|
import { TableHeader } from 'lib/ui/Table/TableHeader.js'
|
|
@@ -71,7 +75,28 @@ export function AccessCodeTable(
|
|
|
71
75
|
<Title>{code.name}</Title>
|
|
72
76
|
<CodeDetails accessCode={code} />
|
|
73
77
|
</TableCell>
|
|
74
|
-
<TableCell className='seam-action-cell'
|
|
78
|
+
<TableCell className='seam-action-cell'>
|
|
79
|
+
<MoreActionsMenu
|
|
80
|
+
MenuProps={{
|
|
81
|
+
BackgroundProps: {
|
|
82
|
+
className: 'seam-access-code-table-action-menu',
|
|
83
|
+
},
|
|
84
|
+
}}
|
|
85
|
+
>
|
|
86
|
+
<MenuItem
|
|
87
|
+
onClick={() => {
|
|
88
|
+
void copy(code.code ?? '')
|
|
89
|
+
}}
|
|
90
|
+
>
|
|
91
|
+
<div className='menu-item-copy'>
|
|
92
|
+
<span>
|
|
93
|
+
{t.copyCode} - {code.code}
|
|
94
|
+
</span>
|
|
95
|
+
<CopyIcon />
|
|
96
|
+
</div>
|
|
97
|
+
</MenuItem>
|
|
98
|
+
</MoreActionsMenu>
|
|
99
|
+
</TableCell>
|
|
75
100
|
</TableRow>
|
|
76
101
|
))}
|
|
77
102
|
</TableBody>
|
|
@@ -81,4 +106,5 @@ export function AccessCodeTable(
|
|
|
81
106
|
|
|
82
107
|
const t = {
|
|
83
108
|
accessCodes: 'Access Codes',
|
|
109
|
+
copyCode: 'Copy code',
|
|
84
110
|
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import classNames from 'classnames'
|
|
2
|
+
import {
|
|
3
|
+
createContext,
|
|
4
|
+
useCallback,
|
|
5
|
+
useContext,
|
|
6
|
+
useEffect,
|
|
7
|
+
useLayoutEffect,
|
|
8
|
+
useState,
|
|
9
|
+
} from 'react'
|
|
10
|
+
import { createPortal } from 'react-dom'
|
|
11
|
+
|
|
12
|
+
export interface MenuProps {
|
|
13
|
+
children: JSX.Element | JSX.Element[]
|
|
14
|
+
verticalOffset?: number
|
|
15
|
+
horizontalOffset?: number
|
|
16
|
+
edgeOffset?: number
|
|
17
|
+
button: (props: {
|
|
18
|
+
open: (event: React.MouseEvent<HTMLElement>) => void
|
|
19
|
+
}) => React.ReactElement
|
|
20
|
+
BackgroundProps?: Partial<{
|
|
21
|
+
className?: string
|
|
22
|
+
}>
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface MenuContextProps {
|
|
26
|
+
close: () => void
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const MenuContext = createContext<MenuContextProps | undefined>(undefined)
|
|
30
|
+
|
|
31
|
+
const Menu = ({
|
|
32
|
+
verticalOffset = 5,
|
|
33
|
+
horizontalOffset = 0,
|
|
34
|
+
edgeOffset = 5,
|
|
35
|
+
children,
|
|
36
|
+
button,
|
|
37
|
+
BackgroundProps,
|
|
38
|
+
}: MenuProps) => {
|
|
39
|
+
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null)
|
|
40
|
+
const [documentEl, setDocumentEl] = useState<null | Element>(null)
|
|
41
|
+
const [contentEl, setContentEl] = useState<HTMLDivElement | null>(null)
|
|
42
|
+
const [top, setTop] = useState(0)
|
|
43
|
+
const [left, setLeft] = useState(0)
|
|
44
|
+
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
const containers = document.querySelectorAll('.seam-components')
|
|
47
|
+
const el = containers[containers.length - 1]
|
|
48
|
+
if (el != null) {
|
|
49
|
+
setDocumentEl(el)
|
|
50
|
+
}
|
|
51
|
+
}, [setDocumentEl])
|
|
52
|
+
|
|
53
|
+
const handleClose = () => {
|
|
54
|
+
setAnchorEl(null)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const open = (event: React.MouseEvent<HTMLElement>) => {
|
|
58
|
+
setAnchorEl(event.currentTarget)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const setPositions = useCallback(() => {
|
|
62
|
+
if (anchorEl == null || contentEl == null || documentEl == null) return
|
|
63
|
+
|
|
64
|
+
const { right: containerRight, bottom: containerBottom } =
|
|
65
|
+
documentEl.getBoundingClientRect()
|
|
66
|
+
|
|
67
|
+
const {
|
|
68
|
+
top: anchorTop,
|
|
69
|
+
left: anchorLeft,
|
|
70
|
+
height: anchorHeight,
|
|
71
|
+
} = anchorEl.getBoundingClientRect()
|
|
72
|
+
|
|
73
|
+
const { width: contentWidth, height: contentHeight } =
|
|
74
|
+
contentEl.getBoundingClientRect()
|
|
75
|
+
|
|
76
|
+
const anchorBottom = anchorTop + anchorHeight
|
|
77
|
+
|
|
78
|
+
const top = anchorBottom + verticalOffset
|
|
79
|
+
const left = anchorLeft + horizontalOffset
|
|
80
|
+
const right = left + contentWidth
|
|
81
|
+
const bottom = top + contentHeight
|
|
82
|
+
|
|
83
|
+
// If the content would overflow right, we'll need to set it relative
|
|
84
|
+
// to the right of the container.
|
|
85
|
+
const isOverflowingRight = right > containerRight
|
|
86
|
+
const visibleLeft = isOverflowingRight
|
|
87
|
+
? containerRight - contentWidth - horizontalOffset - edgeOffset
|
|
88
|
+
: left
|
|
89
|
+
setLeft(visibleLeft)
|
|
90
|
+
|
|
91
|
+
// If the content would overflow bottom, we'll position it to be
|
|
92
|
+
// above the anchor.
|
|
93
|
+
const isOverFlowingBottom = bottom > containerBottom
|
|
94
|
+
const visibleTop = isOverFlowingBottom
|
|
95
|
+
? anchorTop - contentHeight - verticalOffset
|
|
96
|
+
: top
|
|
97
|
+
setTop(visibleTop)
|
|
98
|
+
}, [
|
|
99
|
+
anchorEl,
|
|
100
|
+
horizontalOffset,
|
|
101
|
+
verticalOffset,
|
|
102
|
+
contentEl,
|
|
103
|
+
documentEl,
|
|
104
|
+
edgeOffset,
|
|
105
|
+
])
|
|
106
|
+
|
|
107
|
+
const isOpen = Boolean(anchorEl)
|
|
108
|
+
|
|
109
|
+
useLayoutEffect(() => {
|
|
110
|
+
setPositions()
|
|
111
|
+
window.addEventListener('scroll', setPositions)
|
|
112
|
+
window.addEventListener('resize', setPositions)
|
|
113
|
+
return () => {
|
|
114
|
+
window.removeEventListener('scroll', setPositions)
|
|
115
|
+
window.removeEventListener('resize', setPositions)
|
|
116
|
+
}
|
|
117
|
+
}, [setPositions])
|
|
118
|
+
|
|
119
|
+
const hasSetPosition = top !== 0 && left !== 0
|
|
120
|
+
const visible = isOpen && hasSetPosition
|
|
121
|
+
|
|
122
|
+
if (documentEl == null) {
|
|
123
|
+
return null
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return (
|
|
127
|
+
<MenuContext.Provider
|
|
128
|
+
value={{
|
|
129
|
+
close: handleClose,
|
|
130
|
+
}}
|
|
131
|
+
>
|
|
132
|
+
{button({ open })}
|
|
133
|
+
{createPortal(
|
|
134
|
+
<div
|
|
135
|
+
className={classNames('seam-menu-bg', BackgroundProps?.className)}
|
|
136
|
+
style={{
|
|
137
|
+
display: visible ? 'flex' : 'none',
|
|
138
|
+
}}
|
|
139
|
+
onClick={(e) => {
|
|
140
|
+
e.stopPropagation()
|
|
141
|
+
handleClose()
|
|
142
|
+
}}
|
|
143
|
+
>
|
|
144
|
+
<div
|
|
145
|
+
className='seam-menu-content'
|
|
146
|
+
style={{
|
|
147
|
+
top,
|
|
148
|
+
left,
|
|
149
|
+
}}
|
|
150
|
+
>
|
|
151
|
+
{children}
|
|
152
|
+
</div>
|
|
153
|
+
</div>,
|
|
154
|
+
documentEl
|
|
155
|
+
)}
|
|
156
|
+
|
|
157
|
+
{/*
|
|
158
|
+
* Render a shadow element to calculate the content size even if we're
|
|
159
|
+
* not actually showing the content yet.
|
|
160
|
+
*/}
|
|
161
|
+
<div className='seam-menu-shadow'>
|
|
162
|
+
<div className='seam-menu-content' ref={setContentEl}>
|
|
163
|
+
{children}
|
|
164
|
+
</div>
|
|
165
|
+
</div>
|
|
166
|
+
</MenuContext.Provider>
|
|
167
|
+
)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function useMenu() {
|
|
171
|
+
const context = useContext(MenuContext)
|
|
172
|
+
if (context === undefined) {
|
|
173
|
+
throw new Error('useMenu must be used within a Menu.')
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return context
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export default Menu
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
import { useMenu } from 'lib/ui/Menu/Menu.js'
|
|
4
|
+
|
|
5
|
+
export interface MenuItemProps {
|
|
6
|
+
children: ReactNode
|
|
7
|
+
onClick: () => void
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function MenuItem({ children, onClick }: MenuItemProps) {
|
|
11
|
+
const { close: closeMenu } = useMenu()
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<div
|
|
15
|
+
className='seam-menu-item'
|
|
16
|
+
onClick={() => {
|
|
17
|
+
onClick()
|
|
18
|
+
closeMenu()
|
|
19
|
+
}}
|
|
20
|
+
>
|
|
21
|
+
{children}
|
|
22
|
+
</div>
|
|
23
|
+
)
|
|
24
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { DotsEllipsisMoreIcon } from 'lib/icons/DotsEllipsisMore.js'
|
|
2
|
+
import { IconButton } from 'lib/ui/IconButton.js'
|
|
3
|
+
import Menu, { type MenuProps } from 'lib/ui/Menu/Menu.js'
|
|
4
|
+
|
|
5
|
+
export interface MoreActionsMenuProps {
|
|
6
|
+
children: JSX.Element | JSX.Element[]
|
|
7
|
+
MenuProps?: Partial<MenuProps>
|
|
8
|
+
}
|
|
9
|
+
export function MoreActionsMenu({ children, MenuProps }: MoreActionsMenuProps) {
|
|
10
|
+
return (
|
|
11
|
+
<Menu
|
|
12
|
+
button={({ open }) => (
|
|
13
|
+
<IconButton
|
|
14
|
+
onClick={(e) => {
|
|
15
|
+
e.stopPropagation()
|
|
16
|
+
open(e)
|
|
17
|
+
}}
|
|
18
|
+
>
|
|
19
|
+
<DotsEllipsisMoreIcon />
|
|
20
|
+
</IconButton>
|
|
21
|
+
)}
|
|
22
|
+
{...MenuProps}
|
|
23
|
+
>
|
|
24
|
+
{children}
|
|
25
|
+
</Menu>
|
|
26
|
+
)
|
|
27
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import classNames from 'classnames'
|
|
2
|
+
|
|
3
|
+
import type { DeviceModel } from 'lib/ui/SupportedDevices/types.js'
|
|
4
|
+
|
|
5
|
+
export interface SupportedDeviceRowProps {
|
|
6
|
+
deviceModel: DeviceModel
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export default function SupportedDeviceRow({
|
|
10
|
+
deviceModel,
|
|
11
|
+
}: SupportedDeviceRowProps) {
|
|
12
|
+
function getColorFromSupportLevel() {
|
|
13
|
+
switch (deviceModel.support_level) {
|
|
14
|
+
case 'Live':
|
|
15
|
+
return 'green'
|
|
16
|
+
case 'Beta':
|
|
17
|
+
return 'blue'
|
|
18
|
+
case 'Unsupported':
|
|
19
|
+
return 'orange'
|
|
20
|
+
default:
|
|
21
|
+
return 'gray'
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<tr>
|
|
27
|
+
<td>
|
|
28
|
+
<img style={{ width: 40 }} src={deviceModel.icon_url} />
|
|
29
|
+
</td>
|
|
30
|
+
<td>{deviceModel.main_category}</td>
|
|
31
|
+
<td>{deviceModel.model_name}</td>
|
|
32
|
+
<td>{deviceModel.manufacturer_model_id}</td>
|
|
33
|
+
<td>{deviceModel.connection_type}</td>
|
|
34
|
+
<td>
|
|
35
|
+
<div
|
|
36
|
+
className={classNames(
|
|
37
|
+
'seam-status-pill',
|
|
38
|
+
`status-${getColorFromSupportLevel()}`
|
|
39
|
+
)}
|
|
40
|
+
>
|
|
41
|
+
<span>{deviceModel.support_level}</span>
|
|
42
|
+
</div>
|
|
43
|
+
</td>
|
|
44
|
+
</tr>
|
|
45
|
+
)
|
|
46
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { useQuery } from '@tanstack/react-query'
|
|
2
|
+
import axios from 'axios'
|
|
3
|
+
|
|
4
|
+
import { Button } from 'lib/ui/Button.js'
|
|
5
|
+
import type { DeviceModel } from 'lib/ui/SupportedDevices/types.js'
|
|
6
|
+
|
|
7
|
+
import SupportedDeviceRow from './SupportedDeviceRow.js'
|
|
8
|
+
import SupportedDevicesHeader from './SupportedDevicesHeader.js'
|
|
9
|
+
|
|
10
|
+
export default function SupportedDevices() {
|
|
11
|
+
const { data, isLoading, isError, refetch } = useQuery<{
|
|
12
|
+
data: {
|
|
13
|
+
device_models?: DeviceModel[]
|
|
14
|
+
}
|
|
15
|
+
}>({
|
|
16
|
+
queryKey: ['supported_devices'],
|
|
17
|
+
queryFn: async () => {
|
|
18
|
+
return await axios.get('https://devicedb.seam.co/api/device_models/list')
|
|
19
|
+
},
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<>
|
|
24
|
+
<div className='seam-supported-devices-table-wrap'>
|
|
25
|
+
{isLoading && (
|
|
26
|
+
<div className='seam-supported-devices-table-state-block'>
|
|
27
|
+
<p>Loading device models...</p>
|
|
28
|
+
</div>
|
|
29
|
+
)}
|
|
30
|
+
|
|
31
|
+
{isError && (
|
|
32
|
+
<div className='seam-supported-devices-table-state-block'>
|
|
33
|
+
<p>There was an error fetching device models.</p>
|
|
34
|
+
<Button
|
|
35
|
+
variant='solid'
|
|
36
|
+
size='small'
|
|
37
|
+
onClick={() => {
|
|
38
|
+
void refetch()
|
|
39
|
+
}}
|
|
40
|
+
>
|
|
41
|
+
Retry
|
|
42
|
+
</Button>
|
|
43
|
+
</div>
|
|
44
|
+
)}
|
|
45
|
+
|
|
46
|
+
{!isLoading &&
|
|
47
|
+
!isError &&
|
|
48
|
+
data?.data?.device_models !== null &&
|
|
49
|
+
Array.isArray(data?.data?.device_models) && (
|
|
50
|
+
<table className='seam-supported-devices-table'>
|
|
51
|
+
<SupportedDevicesHeader />
|
|
52
|
+
<tbody>
|
|
53
|
+
{data.data.device_models.map((deviceModel) => (
|
|
54
|
+
<SupportedDeviceRow
|
|
55
|
+
key={deviceModel.manufacturer_model_id}
|
|
56
|
+
deviceModel={deviceModel}
|
|
57
|
+
/>
|
|
58
|
+
))}
|
|
59
|
+
</tbody>
|
|
60
|
+
</table>
|
|
61
|
+
)}
|
|
62
|
+
</div>
|
|
63
|
+
</>
|
|
64
|
+
)
|
|
65
|
+
}
|
package/src/styles/_main.scss
CHANGED
|
@@ -7,9 +7,11 @@
|
|
|
7
7
|
@use './access-code-table';
|
|
8
8
|
@use './colors';
|
|
9
9
|
@use './icons';
|
|
10
|
+
@use './menus';
|
|
10
11
|
@use './device-details';
|
|
11
12
|
@use './device-table';
|
|
12
13
|
@use './access-code-details';
|
|
14
|
+
@use './supported-devices';
|
|
13
15
|
|
|
14
16
|
.seam-components {
|
|
15
17
|
// Reset
|
|
@@ -22,10 +24,12 @@
|
|
|
22
24
|
@include buttons.all;
|
|
23
25
|
@include layout.all;
|
|
24
26
|
@include icons.all;
|
|
27
|
+
@include menus.all;
|
|
25
28
|
|
|
26
29
|
// Components
|
|
27
30
|
@include device-details.all;
|
|
28
31
|
@include device-table.all;
|
|
29
32
|
@include access-code-table.all;
|
|
30
33
|
@include access-code-details.all;
|
|
34
|
+
@include supported-devices.all;
|
|
31
35
|
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
@use './colors';
|
|
2
|
+
|
|
3
|
+
@mixin all {
|
|
4
|
+
.seam-menu-bg {
|
|
5
|
+
width: 100vw;
|
|
6
|
+
height: 100vh;
|
|
7
|
+
justify-content: center;
|
|
8
|
+
align-items: center;
|
|
9
|
+
position: fixed;
|
|
10
|
+
inset: 0;
|
|
11
|
+
z-index: 999999;
|
|
12
|
+
display: none; // Toggled in JS (component)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.seam-menu-content {
|
|
16
|
+
width: 250px;
|
|
17
|
+
position: absolute;
|
|
18
|
+
padding: 8px 0;
|
|
19
|
+
background: #fff;
|
|
20
|
+
box-shadow: 0 0 1px rgb(0 0 0 / 30%), 0 2px 16px 2px rgb(15 22 28 / 15%);
|
|
21
|
+
border-radius: 8px;
|
|
22
|
+
|
|
23
|
+
.seam-menu-item {
|
|
24
|
+
padding: 0 16px;
|
|
25
|
+
height: 36px;
|
|
26
|
+
display: flex;
|
|
27
|
+
align-items: center;
|
|
28
|
+
font-size: 16px;
|
|
29
|
+
line-height: 134%;
|
|
30
|
+
cursor: pointer;
|
|
31
|
+
|
|
32
|
+
&:hover {
|
|
33
|
+
background: colors.$item-hover-bg;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.seam-menu-shadow {
|
|
39
|
+
z-index: -1;
|
|
40
|
+
opacity: 0;
|
|
41
|
+
visibility: hidden;
|
|
42
|
+
top: 0;
|
|
43
|
+
right: 0;
|
|
44
|
+
position: fixed;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
@use './colors';
|
|
2
|
+
|
|
3
|
+
$row-padding: 8px;
|
|
4
|
+
|
|
5
|
+
@mixin all {
|
|
6
|
+
.seam-supported-devices-table-state-block {
|
|
7
|
+
width: 100%;
|
|
8
|
+
background-color: colors.$bg-a;
|
|
9
|
+
padding: 32px 8px;
|
|
10
|
+
display: flex;
|
|
11
|
+
justify-content: center;
|
|
12
|
+
align-items: center;
|
|
13
|
+
flex-direction: column;
|
|
14
|
+
gap: 8px;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.seam-supported-devices-table {
|
|
18
|
+
width: 100%;
|
|
19
|
+
overflow-x: auto;
|
|
20
|
+
table-layout: auto;
|
|
21
|
+
border-collapse: collapse;
|
|
22
|
+
|
|
23
|
+
th,
|
|
24
|
+
td {
|
|
25
|
+
white-space: nowrap;
|
|
26
|
+
padding: 0 8px;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
thead {
|
|
30
|
+
width: 100%;
|
|
31
|
+
height: 50px;
|
|
32
|
+
background: colors.$bg-a;
|
|
33
|
+
overflow: hidden;
|
|
34
|
+
|
|
35
|
+
tr {
|
|
36
|
+
border-radius: 8px;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
th {
|
|
40
|
+
vertical-align: middle;
|
|
41
|
+
text-align: left;
|
|
42
|
+
font-size: 16px;
|
|
43
|
+
font-weight: 600;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
tbody {
|
|
48
|
+
tr {
|
|
49
|
+
height: 60px;
|
|
50
|
+
border-bottom: 1px solid colors.$bg-b;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
td {
|
|
54
|
+
vertical-align: middle;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// stylelint-disable-next-line no-descending-specificity
|
|
59
|
+
tr {
|
|
60
|
+
th:first-of-type,
|
|
61
|
+
td:first-of-type {
|
|
62
|
+
padding-left: $row-padding;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
th:last-of-type,
|
|
66
|
+
td:last-of-type {
|
|
67
|
+
padding-right: $row-padding;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.seam-status-pill {
|
|
72
|
+
display: inline-flex;
|
|
73
|
+
padding: 4px 8px;
|
|
74
|
+
border-radius: 90px;
|
|
75
|
+
background-color: colors.$status-green;
|
|
76
|
+
|
|
77
|
+
& span {
|
|
78
|
+
color: white;
|
|
79
|
+
font-size: 14px;
|
|
80
|
+
font-weight: 600;
|
|
81
|
+
line-height: 1;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
&.status-green {
|
|
85
|
+
background-color: colors.$status-green;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
&.status-orange {
|
|
89
|
+
background-color: colors.$status-orange;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
&.status-red {
|
|
93
|
+
background-color: colors.$status-red;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
&.status-blue {
|
|
97
|
+
background-color: colors.$primary;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
&.status-gray {
|
|
101
|
+
background-color: colors.$bg-d;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|