@seamapi/react 1.61.1 → 1.62.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 +1 -1
- package/dist/elements.js +4586 -4403
- package/dist/elements.js.map +1 -1
- package/dist/index.css +124 -12
- 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/CheckGreen.d.ts +2 -0
- package/lib/icons/CheckGreen.js +7 -0
- package/lib/icons/CheckGreen.js.map +1 -0
- package/lib/icons/CloseWhite.d.ts +2 -0
- package/lib/icons/CloseWhite.js +7 -0
- package/lib/icons/CloseWhite.js.map +1 -0
- package/lib/seam/components/AccessCodeTable/AccessCodeTable.js +21 -8
- package/lib/seam/components/AccessCodeTable/AccessCodeTable.js.map +1 -1
- package/lib/seam/components/CreateAccessCodeForm/CreateAccessCodeForm.d.ts +2 -1
- package/lib/seam/components/CreateAccessCodeForm/CreateAccessCodeForm.js +7 -4
- package/lib/seam/components/CreateAccessCodeForm/CreateAccessCodeForm.js.map +1 -1
- package/lib/seam/components/DeviceDetails/ThermostatDeviceDetails.js +1 -2
- package/lib/seam/components/DeviceDetails/ThermostatDeviceDetails.js.map +1 -1
- package/lib/seam/components/EditAccessCodeForm/EditAccessCodeForm.d.ts +2 -1
- package/lib/seam/components/EditAccessCodeForm/EditAccessCodeForm.js +11 -4
- package/lib/seam/components/EditAccessCodeForm/EditAccessCodeForm.js.map +1 -1
- package/lib/seam/components/SupportedDeviceTable/FilterCategoryMenu.js +12 -9
- package/lib/seam/components/SupportedDeviceTable/FilterCategoryMenu.js.map +1 -1
- package/lib/ui/Menu/Menu.js +32 -25
- package/lib/ui/Menu/Menu.js.map +1 -1
- package/lib/ui/Snackbar/Snackbar.d.ts +17 -0
- package/lib/ui/Snackbar/Snackbar.js +34 -0
- package/lib/ui/Snackbar/Snackbar.js.map +1 -0
- package/lib/ui/thermostat/ClimateModeMenu.js +4 -4
- package/lib/ui/thermostat/ClimateModeMenu.js.map +1 -1
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +2 -2
- package/src/lib/icons/CheckGreen.tsx +36 -0
- package/src/lib/icons/CloseWhite.tsx +36 -0
- package/src/lib/seam/components/AccessCodeTable/AccessCodeTable.tsx +96 -58
- package/src/lib/seam/components/CreateAccessCodeForm/CreateAccessCodeForm.element.ts +1 -0
- package/src/lib/seam/components/CreateAccessCodeForm/CreateAccessCodeForm.tsx +20 -4
- package/src/lib/seam/components/DeviceDetails/ThermostatDeviceDetails.tsx +1 -6
- package/src/lib/seam/components/EditAccessCodeForm/EditAccessCodeForm.element.ts +1 -0
- package/src/lib/seam/components/EditAccessCodeForm/EditAccessCodeForm.tsx +18 -2
- package/src/lib/seam/components/SupportedDeviceTable/FilterCategoryMenu.tsx +20 -15
- package/src/lib/ui/Menu/Menu.tsx +50 -40
- package/src/lib/ui/Snackbar/Snackbar.tsx +93 -0
- package/src/lib/ui/thermostat/ClimateModeMenu.tsx +4 -4
- package/src/lib/version.ts +1 -1
- package/src/styles/_colors.scss +2 -0
- package/src/styles/_loading_toast.scss +5 -16
- package/src/styles/_main.scss +2 -0
- package/src/styles/_motion.scss +34 -0
- package/src/styles/_snackbar.scss +110 -0
- package/src/styles/_supported-device-table.scss +9 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Automatically generated by SVGR from assets/icons/*.svg.
|
|
3
|
+
* Do not edit this file or add other components to this directory.
|
|
4
|
+
*/
|
|
5
|
+
import type { SVGProps } from 'react'
|
|
6
|
+
export function CheckGreenIcon(props: SVGProps<SVGSVGElement>): JSX.Element {
|
|
7
|
+
return (
|
|
8
|
+
<svg
|
|
9
|
+
xmlns='http://www.w3.org/2000/svg'
|
|
10
|
+
width={24}
|
|
11
|
+
height={24}
|
|
12
|
+
fill='none'
|
|
13
|
+
{...props}
|
|
14
|
+
>
|
|
15
|
+
<mask
|
|
16
|
+
id='check-green_svg__a'
|
|
17
|
+
width={24}
|
|
18
|
+
height={24}
|
|
19
|
+
x={0}
|
|
20
|
+
y={0}
|
|
21
|
+
maskUnits='userSpaceOnUse'
|
|
22
|
+
style={{
|
|
23
|
+
maskType: 'alpha',
|
|
24
|
+
}}
|
|
25
|
+
>
|
|
26
|
+
<path fill='#D9D9D9' d='M0 0h24v24H0z' />
|
|
27
|
+
</mask>
|
|
28
|
+
<g mask='url(#check-green_svg__a)'>
|
|
29
|
+
<path
|
|
30
|
+
fill='#27AE60'
|
|
31
|
+
d='m10.6 16.6 7.05-7.05-1.4-1.4-5.65 5.65-2.85-2.85-1.4 1.4 4.25 4.25ZM12 22a9.733 9.733 0 0 1-3.9-.788 10.092 10.092 0 0 1-3.175-2.137c-.9-.9-1.612-1.958-2.137-3.175A9.733 9.733 0 0 1 2 12c0-1.383.263-2.683.788-3.9a10.092 10.092 0 0 1 2.137-3.175c.9-.9 1.958-1.613 3.175-2.138A9.743 9.743 0 0 1 12 2c1.383 0 2.683.262 3.9.787a10.105 10.105 0 0 1 3.175 2.138c.9.9 1.612 1.958 2.137 3.175A9.733 9.733 0 0 1 22 12a9.733 9.733 0 0 1-.788 3.9 10.092 10.092 0 0 1-2.137 3.175c-.9.9-1.958 1.612-3.175 2.137A9.733 9.733 0 0 1 12 22Z'
|
|
32
|
+
/>
|
|
33
|
+
</g>
|
|
34
|
+
</svg>
|
|
35
|
+
)
|
|
36
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Automatically generated by SVGR from assets/icons/*.svg.
|
|
3
|
+
* Do not edit this file or add other components to this directory.
|
|
4
|
+
*/
|
|
5
|
+
import type { SVGProps } from 'react'
|
|
6
|
+
export function CloseWhiteIcon(props: SVGProps<SVGSVGElement>): JSX.Element {
|
|
7
|
+
return (
|
|
8
|
+
<svg
|
|
9
|
+
xmlns='http://www.w3.org/2000/svg'
|
|
10
|
+
width={24}
|
|
11
|
+
height={24}
|
|
12
|
+
fill='none'
|
|
13
|
+
{...props}
|
|
14
|
+
>
|
|
15
|
+
<mask
|
|
16
|
+
id='close-white_svg__a'
|
|
17
|
+
width={24}
|
|
18
|
+
height={24}
|
|
19
|
+
x={0}
|
|
20
|
+
y={0}
|
|
21
|
+
maskUnits='userSpaceOnUse'
|
|
22
|
+
style={{
|
|
23
|
+
maskType: 'alpha',
|
|
24
|
+
}}
|
|
25
|
+
>
|
|
26
|
+
<path fill='#D9D9D9' d='M0 0h24v24H0z' />
|
|
27
|
+
</mask>
|
|
28
|
+
<g mask='url(#close-white_svg__a)'>
|
|
29
|
+
<path
|
|
30
|
+
fill='#fff'
|
|
31
|
+
d='M6.4 19 5 17.6l5.6-5.6L5 6.4 6.4 5l5.6 5.6L17.6 5 19 6.4 13.4 12l5.6 5.6-1.4 1.4-5.6-5.6L6.4 19Z'
|
|
32
|
+
/>
|
|
33
|
+
</g>
|
|
34
|
+
</svg>
|
|
35
|
+
)
|
|
36
|
+
}
|
|
@@ -24,6 +24,7 @@ import { NestedEditAccessCodeForm } from 'lib/seam/components/EditAccessCodeForm
|
|
|
24
24
|
import { IconButton } from 'lib/ui/IconButton.js'
|
|
25
25
|
import { ContentHeader } from 'lib/ui/layout/ContentHeader.js'
|
|
26
26
|
import { LoadingToast } from 'lib/ui/LoadingToast/LoadingToast.js'
|
|
27
|
+
import { Snackbar } from 'lib/ui/Snackbar/Snackbar.js'
|
|
27
28
|
import { EmptyPlaceholder } from 'lib/ui/Table/EmptyPlaceholder.js'
|
|
28
29
|
import { TableBody } from 'lib/ui/Table/TableBody.js'
|
|
29
30
|
import { TableHeader } from 'lib/ui/Table/TableHeader.js'
|
|
@@ -130,6 +131,13 @@ export function AccessCodeTable({
|
|
|
130
131
|
[setSelectedEditAccessCodeId]
|
|
131
132
|
)
|
|
132
133
|
|
|
134
|
+
const [accessCodeResult, setAccessCodeResult] = useState<
|
|
135
|
+
'created' | 'updated' | null
|
|
136
|
+
>(null)
|
|
137
|
+
|
|
138
|
+
const accessCodeResultMessage =
|
|
139
|
+
accessCodeResult === 'created' ? t.accesCodeCreated : t.accesCodeUpdated
|
|
140
|
+
|
|
133
141
|
if (selectedEditAccessCodeId != null) {
|
|
134
142
|
return (
|
|
135
143
|
<NestedEditAccessCodeForm
|
|
@@ -141,6 +149,9 @@ export function AccessCodeTable({
|
|
|
141
149
|
onBack={() => {
|
|
142
150
|
setSelectedEditAccessCodeId(null)
|
|
143
151
|
}}
|
|
152
|
+
onSuccess={() => {
|
|
153
|
+
setAccessCodeResult('updated')
|
|
154
|
+
}}
|
|
144
155
|
className={className}
|
|
145
156
|
/>
|
|
146
157
|
)
|
|
@@ -148,20 +159,31 @@ export function AccessCodeTable({
|
|
|
148
159
|
|
|
149
160
|
if (selectedViewAccessCodeId != null) {
|
|
150
161
|
return (
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
162
|
+
<>
|
|
163
|
+
<Snackbar
|
|
164
|
+
variant='success'
|
|
165
|
+
message={accessCodeResultMessage}
|
|
166
|
+
visible={accessCodeResult != null}
|
|
167
|
+
autoDismiss
|
|
168
|
+
onClose={() => {
|
|
169
|
+
setAccessCodeResult(null)
|
|
170
|
+
}}
|
|
171
|
+
/>
|
|
172
|
+
<NestedAccessCodeDetails
|
|
173
|
+
accessCodeId={selectedViewAccessCodeId}
|
|
174
|
+
onEdit={() => {
|
|
175
|
+
setSelectedEditAccessCodeId(selectedViewAccessCodeId)
|
|
176
|
+
}}
|
|
177
|
+
disableLockUnlock={disableLockUnlock}
|
|
178
|
+
disableCreateAccessCode={disableCreateAccessCode}
|
|
179
|
+
disableEditAccessCode={disableEditAccessCode}
|
|
180
|
+
disableDeleteAccessCode={disableDeleteAccessCode}
|
|
181
|
+
onBack={() => {
|
|
182
|
+
setSelectedViewAccessCodeId(null)
|
|
183
|
+
}}
|
|
184
|
+
className={className}
|
|
185
|
+
/>
|
|
186
|
+
</>
|
|
165
187
|
)
|
|
166
188
|
}
|
|
167
189
|
|
|
@@ -175,6 +197,9 @@ export function AccessCodeTable({
|
|
|
175
197
|
disableDeleteAccessCode={disableDeleteAccessCode}
|
|
176
198
|
onBack={toggleAddAccessCodeForm}
|
|
177
199
|
className={className}
|
|
200
|
+
onSuccess={() => {
|
|
201
|
+
setAccessCodeResult('created')
|
|
202
|
+
}}
|
|
178
203
|
/>
|
|
179
204
|
)
|
|
180
205
|
}
|
|
@@ -184,52 +209,63 @@ export function AccessCodeTable({
|
|
|
184
209
|
}
|
|
185
210
|
|
|
186
211
|
return (
|
|
187
|
-
|
|
188
|
-
<
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
212
|
+
<>
|
|
213
|
+
<Snackbar
|
|
214
|
+
variant='success'
|
|
215
|
+
message={accessCodeResultMessage}
|
|
216
|
+
visible={accessCodeResult != null}
|
|
217
|
+
autoDismiss
|
|
218
|
+
onClose={() => {
|
|
219
|
+
setAccessCodeResult(null)
|
|
220
|
+
}}
|
|
221
|
+
/>
|
|
222
|
+
<div className={classNames('seam-table', className)}>
|
|
223
|
+
<ContentHeader onBack={onBack} />
|
|
224
|
+
<TableHeader>
|
|
225
|
+
<div className='seam-left'>
|
|
226
|
+
{title != null ? (
|
|
227
|
+
<TableTitle>
|
|
228
|
+
{heading ?? title ?? t.accessCodes}{' '}
|
|
229
|
+
<Caption>({filteredAccessCodes.length})</Caption>
|
|
230
|
+
</TableTitle>
|
|
231
|
+
) : (
|
|
232
|
+
<div className='seam-fragment' />
|
|
233
|
+
)}
|
|
234
|
+
{!disableCreateAccessCode && (
|
|
235
|
+
<IconButton
|
|
236
|
+
onClick={toggleAddAccessCodeForm}
|
|
237
|
+
className='seam-add-button'
|
|
238
|
+
>
|
|
239
|
+
<AddIcon />
|
|
240
|
+
</IconButton>
|
|
241
|
+
)}
|
|
242
|
+
</div>
|
|
243
|
+
<div className='seam-table-header-loading-wrap'>
|
|
244
|
+
<LoadingToast
|
|
245
|
+
isLoading={isInitialLoading}
|
|
246
|
+
label={t.loading}
|
|
247
|
+
top={-20}
|
|
248
|
+
/>
|
|
249
|
+
</div>
|
|
250
|
+
{!disableSearch && (
|
|
251
|
+
<SearchTextField
|
|
252
|
+
value={searchInputValue}
|
|
253
|
+
onChange={setSearchInputValue}
|
|
254
|
+
disabled={(accessCodes?.length ?? 0) === 0}
|
|
255
|
+
/>
|
|
206
256
|
)}
|
|
207
|
-
</
|
|
208
|
-
<
|
|
209
|
-
<
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
257
|
+
</TableHeader>
|
|
258
|
+
<TableBody>
|
|
259
|
+
<Content
|
|
260
|
+
accessCodes={filteredAccessCodes}
|
|
261
|
+
onAccessCodeClick={handleAccessCodeClick}
|
|
262
|
+
onAccessCodeEdit={handleAccessCodeEdit}
|
|
263
|
+
disableEditAccessCode={disableEditAccessCode}
|
|
264
|
+
disableDeleteAccessCode={disableDeleteAccessCode}
|
|
213
265
|
/>
|
|
214
|
-
</
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
value={searchInputValue}
|
|
218
|
-
onChange={setSearchInputValue}
|
|
219
|
-
disabled={(accessCodes?.length ?? 0) === 0}
|
|
220
|
-
/>
|
|
221
|
-
)}
|
|
222
|
-
</TableHeader>
|
|
223
|
-
<TableBody>
|
|
224
|
-
<Content
|
|
225
|
-
accessCodes={filteredAccessCodes}
|
|
226
|
-
onAccessCodeClick={handleAccessCodeClick}
|
|
227
|
-
onAccessCodeEdit={handleAccessCodeEdit}
|
|
228
|
-
disableEditAccessCode={disableEditAccessCode}
|
|
229
|
-
disableDeleteAccessCode={disableDeleteAccessCode}
|
|
230
|
-
/>
|
|
231
|
-
</TableBody>
|
|
232
|
-
</div>
|
|
266
|
+
</TableBody>
|
|
267
|
+
</div>
|
|
268
|
+
</>
|
|
233
269
|
)
|
|
234
270
|
}
|
|
235
271
|
|
|
@@ -296,4 +332,6 @@ const t = {
|
|
|
296
332
|
accessCodes: 'Access Codes',
|
|
297
333
|
noAccessCodesMessage: 'Sorry, no access codes were found',
|
|
298
334
|
loading: 'Loading access codes',
|
|
335
|
+
accesCodeUpdated: 'Access code updated',
|
|
336
|
+
accesCodeCreated: 'Access code created',
|
|
299
337
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useState } from 'react'
|
|
2
|
-
import type { SeamError } from 'seamapi'
|
|
2
|
+
import type { AccessCode, SeamError } from 'seamapi'
|
|
3
3
|
|
|
4
4
|
import { useComponentTelemetry } from 'lib/telemetry/index.js'
|
|
5
5
|
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
|
|
19
19
|
export interface CreateAccessCodeFormProps extends CommonProps {
|
|
20
20
|
deviceId: string
|
|
21
|
+
onSuccess?: (accessCodeId: string) => void
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
export const NestedCreateAccessCodeForm =
|
|
@@ -27,6 +28,7 @@ export function CreateAccessCodeForm({
|
|
|
27
28
|
className,
|
|
28
29
|
onBack,
|
|
29
30
|
deviceId,
|
|
31
|
+
onSuccess,
|
|
30
32
|
}: CreateAccessCodeFormProps): JSX.Element | null {
|
|
31
33
|
useComponentTelemetry('CreateAccessCodeForm')
|
|
32
34
|
|
|
@@ -38,18 +40,30 @@ export function CreateAccessCodeForm({
|
|
|
38
40
|
return null
|
|
39
41
|
}
|
|
40
42
|
|
|
41
|
-
return
|
|
43
|
+
return (
|
|
44
|
+
<Content
|
|
45
|
+
device={device}
|
|
46
|
+
className={className}
|
|
47
|
+
onBack={onBack}
|
|
48
|
+
onSuccess={onSuccess}
|
|
49
|
+
/>
|
|
50
|
+
)
|
|
42
51
|
}
|
|
43
52
|
|
|
44
53
|
function Content({
|
|
45
54
|
device,
|
|
46
55
|
className,
|
|
47
56
|
onBack,
|
|
57
|
+
onSuccess,
|
|
48
58
|
}: Omit<CreateAccessCodeFormProps, 'deviceId'> & {
|
|
49
59
|
device: NonNullable<UseDeviceData>
|
|
50
60
|
}): JSX.Element {
|
|
51
61
|
const { submit, isSubmitting, responseErrors } = useSubmitCreateAccessCode({
|
|
52
|
-
onSuccess: () => {
|
|
62
|
+
onSuccess: (accessCode: AccessCode) => {
|
|
63
|
+
if (onSuccess != null) {
|
|
64
|
+
onSuccess(accessCode.access_code_id)
|
|
65
|
+
}
|
|
66
|
+
|
|
53
67
|
if (onBack != null) {
|
|
54
68
|
onBack()
|
|
55
69
|
}
|
|
@@ -68,7 +82,9 @@ function Content({
|
|
|
68
82
|
)
|
|
69
83
|
}
|
|
70
84
|
|
|
71
|
-
function useSubmitCreateAccessCode(params: {
|
|
85
|
+
function useSubmitCreateAccessCode(params: {
|
|
86
|
+
onSuccess: (accessCode: AccessCode) => void
|
|
87
|
+
}): {
|
|
72
88
|
submit: (data: AccessCodeFormSubmitData) => void
|
|
73
89
|
isSubmitting: boolean
|
|
74
90
|
responseErrors: ResponseErrors | null
|
|
@@ -128,10 +128,7 @@ export function ThermostatDeviceDetails(
|
|
|
128
128
|
</DetailRow>
|
|
129
129
|
</DetailSection>
|
|
130
130
|
|
|
131
|
-
<DetailSection
|
|
132
|
-
label={t.deviceDetails}
|
|
133
|
-
tooltipContent={t.deviceDetailsTooltip}
|
|
134
|
-
>
|
|
131
|
+
<DetailSection label={t.deviceDetails}>
|
|
135
132
|
<DetailRow label={t.brand}>
|
|
136
133
|
<div className='seam-detail-row-hstack'>
|
|
137
134
|
{device.properties.model.manufacturer_display_name}
|
|
@@ -172,8 +169,6 @@ const t = {
|
|
|
172
169
|
defaultClimate: 'Default climate',
|
|
173
170
|
allowManualOverride: 'Allow manual override',
|
|
174
171
|
deviceDetails: 'Device details',
|
|
175
|
-
deviceDetailsTooltip:
|
|
176
|
-
'When a scheduled climate reaches its end time, the default settings will kick in.',
|
|
177
172
|
brand: 'Brand',
|
|
178
173
|
linkedAccount: 'Linked account',
|
|
179
174
|
deviceId: 'Device ID',
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
|
|
20
20
|
export interface EditAccessCodeFormProps extends CommonProps {
|
|
21
21
|
accessCodeId: string
|
|
22
|
+
onSuccess?: (accessCodeId: string) => void
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
export const NestedEditAccessCodeForm =
|
|
@@ -28,6 +29,7 @@ export function EditAccessCodeForm({
|
|
|
28
29
|
accessCodeId,
|
|
29
30
|
onBack,
|
|
30
31
|
className,
|
|
32
|
+
onSuccess,
|
|
31
33
|
}: EditAccessCodeFormProps): JSX.Element | null {
|
|
32
34
|
useComponentTelemetry('EditAccessCodeForm')
|
|
33
35
|
|
|
@@ -40,7 +42,12 @@ export function EditAccessCodeForm({
|
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
return (
|
|
43
|
-
<Content
|
|
45
|
+
<Content
|
|
46
|
+
accessCode={accessCode}
|
|
47
|
+
className={className}
|
|
48
|
+
onBack={onBack}
|
|
49
|
+
onSuccess={onSuccess}
|
|
50
|
+
/>
|
|
44
51
|
)
|
|
45
52
|
}
|
|
46
53
|
|
|
@@ -48,6 +55,7 @@ function Content({
|
|
|
48
55
|
className,
|
|
49
56
|
onBack,
|
|
50
57
|
accessCode,
|
|
58
|
+
onSuccess,
|
|
51
59
|
}: Omit<EditAccessCodeFormProps, 'accessCodeId'> & {
|
|
52
60
|
accessCode: NonNullable<UseAccessCodeData>
|
|
53
61
|
}): JSX.Element | null {
|
|
@@ -57,7 +65,15 @@ function Content({
|
|
|
57
65
|
|
|
58
66
|
const { submit, isSubmitting, responseErrors } = useSubmitEditAccessCode(
|
|
59
67
|
accessCode,
|
|
60
|
-
|
|
68
|
+
() => {
|
|
69
|
+
if (onSuccess != null) {
|
|
70
|
+
onSuccess(accessCode.access_code_id)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (onBack != null) {
|
|
74
|
+
onBack()
|
|
75
|
+
}
|
|
76
|
+
}
|
|
61
77
|
)
|
|
62
78
|
|
|
63
79
|
if (device == null) {
|
|
@@ -36,7 +36,10 @@ export function FilterCategoryMenu({
|
|
|
36
36
|
onAllOptionSelect,
|
|
37
37
|
buttonLabel,
|
|
38
38
|
}: FilterCategoryMenuProps): JSX.Element {
|
|
39
|
-
const
|
|
39
|
+
const sortedOptions = [...options].sort((a, b) => a.localeCompare(b))
|
|
40
|
+
const usableOptions = hideAllOption
|
|
41
|
+
? sortedOptions
|
|
42
|
+
: [allLabel, ...sortedOptions]
|
|
40
43
|
|
|
41
44
|
return (
|
|
42
45
|
<div className='seam-supported-device-table-filter-menu-wrap'>
|
|
@@ -49,20 +52,22 @@ export function FilterCategoryMenu({
|
|
|
49
52
|
</button>
|
|
50
53
|
)}
|
|
51
54
|
>
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
55
|
+
<div className='seam-supported-device-table-filter-menu-content'>
|
|
56
|
+
{usableOptions.map((option, index) => (
|
|
57
|
+
<MenuItem
|
|
58
|
+
key={`${index}:${option}`}
|
|
59
|
+
onClick={() => {
|
|
60
|
+
if (option === allLabel) {
|
|
61
|
+
onAllOptionSelect?.()
|
|
62
|
+
} else {
|
|
63
|
+
onSelect(option)
|
|
64
|
+
}
|
|
65
|
+
}}
|
|
66
|
+
>
|
|
67
|
+
<span>{option}</span>
|
|
68
|
+
</MenuItem>
|
|
69
|
+
))}
|
|
70
|
+
</div>
|
|
66
71
|
</Menu>
|
|
67
72
|
</div>
|
|
68
73
|
)
|
package/src/lib/ui/Menu/Menu.tsx
CHANGED
|
@@ -42,21 +42,20 @@ export function Menu({
|
|
|
42
42
|
backgroundProps,
|
|
43
43
|
}: MenuProps): JSX.Element | null {
|
|
44
44
|
const { Provider } = menuContext
|
|
45
|
+
const [documentEl, setDocumentEl] = useState<null | HTMLElement>(null)
|
|
46
|
+
const [bodyEl, setBodyEl] = useState<null | HTMLElement>(null)
|
|
45
47
|
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null)
|
|
46
|
-
const [documentEl, setDocumentEl] = useState<null | Element>(null)
|
|
47
48
|
const [contentEl, setContentEl] = useState<HTMLDivElement | null>(null)
|
|
48
49
|
const [top, setTop] = useState(0)
|
|
49
50
|
const [left, setLeft] = useState(0)
|
|
50
51
|
|
|
51
52
|
useEffect(() => {
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
setDocumentEl(el)
|
|
59
|
-
}
|
|
53
|
+
const documentEl = globalThis.document.documentElement
|
|
54
|
+
setDocumentEl(documentEl)
|
|
55
|
+
|
|
56
|
+
const bodyElements = documentEl?.getElementsByTagName('body')
|
|
57
|
+
if (bodyElements[0] == null) return
|
|
58
|
+
setBodyEl(bodyElements[0])
|
|
60
59
|
}, [setDocumentEl])
|
|
61
60
|
|
|
62
61
|
const handleClose = (): void => {
|
|
@@ -68,18 +67,24 @@ export function Menu({
|
|
|
68
67
|
}
|
|
69
68
|
|
|
70
69
|
const setPositions = useCallback(() => {
|
|
71
|
-
if (
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
70
|
+
if (
|
|
71
|
+
anchorEl == null ||
|
|
72
|
+
contentEl == null ||
|
|
73
|
+
bodyEl == null ||
|
|
74
|
+
documentEl == null
|
|
75
|
+
)
|
|
76
|
+
return
|
|
75
77
|
|
|
76
|
-
const
|
|
78
|
+
const containerRight = documentEl.offsetLeft + documentEl.clientWidth
|
|
79
|
+
const containerBottom = documentEl.offsetTop + documentEl.clientHeight
|
|
77
80
|
|
|
78
|
-
const
|
|
79
|
-
const
|
|
81
|
+
const anchorBox = anchorEl.getBoundingClientRect()
|
|
82
|
+
const anchorTop = anchorBox.top + bodyEl.clientTop
|
|
83
|
+
const anchorLeft = anchorBox.left + bodyEl.clientLeft
|
|
84
|
+
const anchorHeight = anchorEl.offsetHeight
|
|
80
85
|
|
|
81
|
-
const
|
|
82
|
-
|
|
86
|
+
const contentWidth = contentEl.offsetWidth
|
|
87
|
+
const contentHeight = contentEl.offsetHeight
|
|
83
88
|
|
|
84
89
|
const anchorBottom = anchorTop + anchorHeight
|
|
85
90
|
|
|
@@ -97,17 +102,20 @@ export function Menu({
|
|
|
97
102
|
|
|
98
103
|
// If the content would overflow bottom, position it above the anchor.
|
|
99
104
|
const isOverFlowingBottom = bottom > containerBottom
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
105
|
+
const topWhenAboveAnchor = anchorTop - contentHeight - verticalOffset
|
|
106
|
+
|
|
107
|
+
// Only open the menu above the anchor if it won't get clipped, i.e., not < 0.
|
|
108
|
+
const visibleTop =
|
|
109
|
+
isOverFlowingBottom && topWhenAboveAnchor > 0 ? topWhenAboveAnchor : top
|
|
103
110
|
setTop(visibleTop)
|
|
104
111
|
}, [
|
|
105
112
|
anchorEl,
|
|
106
113
|
horizontalOffset,
|
|
107
114
|
verticalOffset,
|
|
108
115
|
contentEl,
|
|
109
|
-
documentEl,
|
|
110
116
|
edgeOffset,
|
|
117
|
+
bodyEl,
|
|
118
|
+
documentEl,
|
|
111
119
|
])
|
|
112
120
|
|
|
113
121
|
useLayoutEffect(() => {
|
|
@@ -124,7 +132,7 @@ export function Menu({
|
|
|
124
132
|
const hasSetPosition = top !== 0 && left !== 0
|
|
125
133
|
const visible = isOpen && hasSetPosition
|
|
126
134
|
|
|
127
|
-
if (
|
|
135
|
+
if (bodyEl == null) {
|
|
128
136
|
return null
|
|
129
137
|
}
|
|
130
138
|
|
|
@@ -136,28 +144,30 @@ export function Menu({
|
|
|
136
144
|
>
|
|
137
145
|
{renderButton({ onOpen: handleOpen })}
|
|
138
146
|
{createPortal(
|
|
139
|
-
<div
|
|
140
|
-
className={classNames(
|
|
141
|
-
'seam-menu-bg',
|
|
142
|
-
backgroundProps?.className,
|
|
143
|
-
visible ? 'seam-menu-visible' : 'seam-menu-hidden'
|
|
144
|
-
)}
|
|
145
|
-
onClick={(event) => {
|
|
146
|
-
event.stopPropagation()
|
|
147
|
-
handleClose()
|
|
148
|
-
}}
|
|
149
|
-
>
|
|
147
|
+
<div className={seamComponentsClassName}>
|
|
150
148
|
<div
|
|
151
|
-
className=
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
149
|
+
className={classNames(
|
|
150
|
+
'seam-menu-bg',
|
|
151
|
+
backgroundProps?.className,
|
|
152
|
+
visible ? 'seam-menu-visible' : 'seam-menu-hidden'
|
|
153
|
+
)}
|
|
154
|
+
onClick={(event) => {
|
|
155
|
+
event.stopPropagation()
|
|
156
|
+
handleClose()
|
|
155
157
|
}}
|
|
156
158
|
>
|
|
157
|
-
|
|
159
|
+
<div
|
|
160
|
+
className='seam-menu-content'
|
|
161
|
+
style={{
|
|
162
|
+
top,
|
|
163
|
+
left,
|
|
164
|
+
}}
|
|
165
|
+
>
|
|
166
|
+
{children}
|
|
167
|
+
</div>
|
|
158
168
|
</div>
|
|
159
169
|
</div>,
|
|
160
|
-
|
|
170
|
+
bodyEl
|
|
161
171
|
)}
|
|
162
172
|
|
|
163
173
|
{/*
|