@dxos/react-ui 0.1.24 → 0.1.25-next.3c6b542
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/dist/lib/browser/index.mjs +1092 -452
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/types/src/components/IdentityList/DeviceList.d.ts +7 -0
- package/dist/types/src/components/IdentityList/DeviceList.d.ts.map +1 -0
- package/dist/types/src/components/IdentityList/IdentityListItem.d.ts +2 -1
- package/dist/types/src/components/IdentityList/IdentityListItem.d.ts.map +1 -1
- package/dist/types/src/components/IdentityList/IdentityListItem.stories.d.ts +2 -1
- package/dist/types/src/components/IdentityList/IdentityListItem.stories.d.ts.map +1 -1
- package/dist/types/src/components/IdentityList/SpaceMemberList.d.ts +3 -2
- package/dist/types/src/components/IdentityList/SpaceMemberList.d.ts.map +1 -1
- package/dist/types/src/components/IdentityList/SpaceMemberListContainer.d.ts +3 -1
- package/dist/types/src/components/IdentityList/SpaceMemberListContainer.d.ts.map +1 -1
- package/dist/types/src/components/IdentityList/index.d.ts +1 -0
- package/dist/types/src/components/IdentityList/index.d.ts.map +1 -1
- package/dist/types/src/components/InvitationList/InvitationList.d.ts.map +1 -1
- package/dist/types/src/components/InvitationList/InvitationListItem.d.ts.map +1 -1
- package/dist/types/src/components/InvitationList/InvitationStatusAvatar.d.ts +6 -1
- package/dist/types/src/components/InvitationList/InvitationStatusAvatar.d.ts.map +1 -1
- package/dist/types/src/components/InvitationList/InvitationStatusAvatar.stories.d.ts +1 -1
- package/dist/types/src/components/SpaceList/SpaceListItem.d.ts +7 -0
- package/dist/types/src/components/SpaceList/SpaceListItem.d.ts.map +1 -0
- package/dist/types/src/components/SpaceList/index.d.ts +2 -0
- package/dist/types/src/components/SpaceList/index.d.ts.map +1 -0
- package/dist/types/src/components/index.d.ts +1 -0
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/composites/DevicesDialog/DevicesDialog.d.ts +6 -0
- package/dist/types/src/composites/DevicesDialog/DevicesDialog.d.ts.map +1 -0
- package/dist/types/src/composites/DevicesDialog/index.d.ts +2 -0
- package/dist/types/src/composites/DevicesDialog/index.d.ts.map +1 -0
- package/dist/types/src/composites/JoinDialog/JoinDialog.d.ts +1 -1
- package/dist/types/src/composites/JoinDialog/JoinDialog.d.ts.map +1 -1
- package/dist/types/src/composites/JoinDialog/JoinDialog.stories.d.ts +2 -2
- package/dist/types/src/composites/JoinDialog/JoinDialog.stories.d.ts.map +1 -1
- package/dist/types/src/composites/Shell/Shell.d.ts +6 -0
- package/dist/types/src/composites/Shell/Shell.d.ts.map +1 -0
- package/dist/types/src/composites/Shell/index.d.ts +2 -0
- package/dist/types/src/composites/Shell/index.d.ts.map +1 -0
- package/dist/types/src/composites/SpaceDialog/SpaceDialog.d.ts +6 -0
- package/dist/types/src/composites/SpaceDialog/SpaceDialog.d.ts.map +1 -0
- package/dist/types/src/composites/SpaceDialog/index.d.ts +2 -0
- package/dist/types/src/composites/SpaceDialog/index.d.ts.map +1 -0
- package/dist/types/src/composites/index.d.ts +2 -0
- package/dist/types/src/composites/index.d.ts.map +1 -1
- package/dist/types/src/layouts/PanelAlertDialog/PanelAlertDialog.d.ts.map +1 -1
- package/dist/types/src/layouts/PanelDialog/PanelDialog.d.ts +14 -0
- package/dist/types/src/layouts/PanelDialog/PanelDialog.d.ts.map +1 -0
- package/dist/types/src/layouts/PanelDialog/index.d.ts +2 -0
- package/dist/types/src/layouts/PanelDialog/index.d.ts.map +1 -0
- package/dist/types/src/layouts/index.d.ts +1 -0
- package/dist/types/src/layouts/index.d.ts.map +1 -1
- package/dist/types/src/panels/DevicesPanel/DevicesPanel.d.ts +10 -0
- package/dist/types/src/panels/DevicesPanel/DevicesPanel.d.ts.map +1 -0
- package/dist/types/src/panels/DevicesPanel/index.d.ts +2 -0
- package/dist/types/src/panels/DevicesPanel/index.d.ts.map +1 -0
- package/dist/types/src/panels/JoinPanel/JoinHeading.d.ts +3 -0
- package/dist/types/src/panels/JoinPanel/JoinHeading.d.ts.map +1 -1
- package/dist/types/src/panels/JoinPanel/JoinPanel.d.ts +1 -1
- package/dist/types/src/panels/JoinPanel/JoinPanel.d.ts.map +1 -1
- package/dist/types/src/panels/JoinPanel/JoinPanelProps.d.ts +4 -3
- package/dist/types/src/panels/JoinPanel/JoinPanelProps.d.ts.map +1 -1
- package/dist/types/src/panels/JoinPanel/view-states/AdditionMethodSelector.d.ts +1 -1
- package/dist/types/src/panels/JoinPanel/view-states/AdditionMethodSelector.d.ts.map +1 -1
- package/dist/types/src/panels/JoinPanel/view-states/IdentityAdded.d.ts +10 -2
- package/dist/types/src/panels/JoinPanel/view-states/IdentityAdded.d.ts.map +1 -1
- package/dist/types/src/panels/JoinPanel/view-states/IdentityInput.d.ts.map +1 -1
- package/dist/types/src/panels/JoinPanel/view-states/IdentitySelector.d.ts +1 -1
- package/dist/types/src/panels/JoinPanel/view-states/IdentitySelector.d.ts.map +1 -1
- package/dist/types/src/panels/JoinPanel/view-states/InvitationAccepted.d.ts.map +1 -1
- package/dist/types/src/panels/JoinPanel/view-states/InvitationAuthenticator.d.ts.map +1 -1
- package/dist/types/src/panels/JoinPanel/view-states/InvitationInput.d.ts.map +1 -1
- package/dist/types/src/panels/JoinPanel/view-states/InvitationRescuer.d.ts.map +1 -1
- package/dist/types/src/panels/SpacePanel/SpacePanel.d.ts +18 -0
- package/dist/types/src/panels/SpacePanel/SpacePanel.d.ts.map +1 -0
- package/dist/types/src/panels/SpacePanel/SpacePanel.stories.d.ts +9 -0
- package/dist/types/src/panels/SpacePanel/SpacePanel.stories.d.ts.map +1 -0
- package/dist/types/src/panels/SpacePanel/index.d.ts +2 -0
- package/dist/types/src/panels/SpacePanel/index.d.ts.map +1 -0
- package/dist/types/src/panels/index.d.ts +2 -0
- package/dist/types/src/panels/index.d.ts.map +1 -1
- package/dist/types/src/styles/dialogStyles.d.ts +3 -0
- package/dist/types/src/styles/dialogStyles.d.ts.map +1 -0
- package/dist/types/src/styles/index.d.ts +1 -0
- package/dist/types/src/styles/index.d.ts.map +1 -1
- package/dist/types/src/translations/locales/en-US.d.ts +12 -1
- package/dist/types/src/translations/locales/en-US.d.ts.map +1 -1
- package/package.json +12 -11
- package/src/components/IdentityList/DeviceList.tsx +31 -0
- package/src/components/IdentityList/IdentityListItem.tsx +15 -3
- package/src/components/IdentityList/SpaceMemberList.tsx +10 -8
- package/src/components/IdentityList/SpaceMemberListContainer.tsx +5 -4
- package/src/components/IdentityList/index.ts +1 -0
- package/src/components/InvitationList/InvitationList.tsx +6 -2
- package/src/components/InvitationList/InvitationListItem.tsx +36 -25
- package/src/components/InvitationList/InvitationStatusAvatar.tsx +10 -5
- package/src/components/SpaceList/SpaceListItem.tsx +32 -0
- package/src/components/SpaceList/index.ts +5 -0
- package/src/components/index.ts +1 -0
- package/src/composites/DevicesDialog/DevicesDialog.tsx +38 -0
- package/src/composites/DevicesDialog/index.ts +5 -0
- package/src/composites/JoinDialog/JoinDialog.stories.tsx +59 -58
- package/src/composites/JoinDialog/JoinDialog.tsx +3 -1
- package/src/composites/Shell/Shell.tsx +92 -0
- package/src/composites/Shell/index.ts +5 -0
- package/src/composites/SpaceDialog/SpaceDialog.tsx +38 -0
- package/src/composites/SpaceDialog/index.ts +5 -0
- package/src/composites/index.ts +3 -0
- package/src/layouts/PanelAlertDialog/PanelAlertDialog.tsx +4 -8
- package/src/layouts/PanelDialog/PanelDialog.tsx +39 -0
- package/src/layouts/PanelDialog/index.ts +5 -0
- package/src/layouts/PanelSidebar/PanelSidebar.tsx +1 -1
- package/src/layouts/index.ts +1 -0
- package/src/panels/DevicesPanel/DevicesPanel.tsx +98 -0
- package/src/panels/DevicesPanel/index.ts +5 -0
- package/src/panels/JoinPanel/JoinHeading.tsx +44 -16
- package/src/panels/JoinPanel/JoinPanel.tsx +32 -11
- package/src/panels/JoinPanel/JoinPanelProps.ts +4 -8
- package/src/panels/JoinPanel/view-states/AdditionMethodSelector.tsx +5 -1
- package/src/panels/JoinPanel/view-states/IdentityAdded.tsx +63 -24
- package/src/panels/JoinPanel/view-states/IdentityInput.tsx +8 -1
- package/src/panels/JoinPanel/view-states/IdentitySelector.tsx +4 -1
- package/src/panels/JoinPanel/view-states/InvitationAccepted.tsx +1 -0
- package/src/panels/JoinPanel/view-states/InvitationAuthenticator.tsx +4 -1
- package/src/panels/JoinPanel/view-states/InvitationInput.tsx +8 -1
- package/src/panels/JoinPanel/view-states/InvitationRescuer.tsx +4 -1
- package/src/panels/SpacePanel/SpacePanel.stories.tsx +53 -0
- package/src/panels/SpacePanel/SpacePanel.tsx +174 -0
- package/src/panels/SpacePanel/index.ts +5 -0
- package/src/panels/index.ts +2 -0
- package/src/styles/dialogStyles.ts +8 -0
- package/src/styles/index.ts +1 -0
- package/src/translations/locales/en-US.ts +14 -3
|
@@ -8,7 +8,7 @@ import React, { useCallback } from 'react';
|
|
|
8
8
|
|
|
9
9
|
import { CancellableInvitationObservable } from '@dxos/client';
|
|
10
10
|
import { useInvitationStatus } from '@dxos/react-client';
|
|
11
|
-
import { Button, getSize, mx, useTranslation } from '@dxos/react-components';
|
|
11
|
+
import { Button, getSize, mx, useId, useTranslation } from '@dxos/react-components';
|
|
12
12
|
|
|
13
13
|
import { invitationStatusValue } from '../../util';
|
|
14
14
|
import { SharedInvitationListProps } from './InvitationListProps';
|
|
@@ -27,18 +27,19 @@ export const InvitationListItem = ({
|
|
|
27
27
|
createInvitationUrl
|
|
28
28
|
}: InvitationListItemProps) => {
|
|
29
29
|
const { t } = useTranslation('os');
|
|
30
|
+
const qrLabel = useId('qrLabel');
|
|
30
31
|
|
|
31
32
|
const { cancel, status, haltedAt, invitationCode, authenticationCode } = useInvitationStatus(invitation);
|
|
32
33
|
const statusValue = invitationStatusValue.get(status) ?? 0;
|
|
33
34
|
|
|
34
35
|
const isCancellable = statusValue < 4 && statusValue >= 0;
|
|
35
36
|
const showShare = statusValue < 3 && statusValue >= 0;
|
|
36
|
-
const
|
|
37
|
+
const showAuthCode = statusValue === 3;
|
|
37
38
|
|
|
38
39
|
const handleClickRemove = useCallback(() => onClickRemove(invitation), [invitation, onClickRemove]);
|
|
39
40
|
|
|
40
41
|
const invitationUrl = invitationCode && createInvitationUrl(invitationCode);
|
|
41
|
-
const
|
|
42
|
+
const handleClickCopy = useCallback(() => {
|
|
42
43
|
if (invitationUrl) {
|
|
43
44
|
void navigator.clipboard.writeText(invitationUrl);
|
|
44
45
|
}
|
|
@@ -47,15 +48,21 @@ export const InvitationListItem = ({
|
|
|
47
48
|
return (
|
|
48
49
|
<AccordionPrimitive.Item value={value}>
|
|
49
50
|
<AccordionPrimitive.Header className='flex gap-2 items-center'>
|
|
50
|
-
<InvitationStatusAvatar {...{ status, haltedAt }} />
|
|
51
|
+
<InvitationStatusAvatar {...{ status, haltedAt, size: 8 }} />
|
|
51
52
|
{showShare && invitationUrl ? (
|
|
52
|
-
|
|
53
|
-
<
|
|
54
|
-
<
|
|
55
|
-
|
|
53
|
+
<>
|
|
54
|
+
<AccordionPrimitive.Trigger asChild>
|
|
55
|
+
<Button className='grow flex gap-1' data-testid='show-qrcode'>
|
|
56
|
+
<span>{t('open share panel label')}</span>
|
|
57
|
+
<QrCode className={getSize(4)} weight='bold' />
|
|
58
|
+
</Button>
|
|
59
|
+
</AccordionPrimitive.Trigger>
|
|
60
|
+
<Button className='flex gap-1' onClick={handleClickCopy} data-testid='copy-invitation'>
|
|
61
|
+
<span className='pli-1'>{t('copy invitation code label')}</span>
|
|
62
|
+
<Copy className={getSize(4)} weight='bold' />
|
|
56
63
|
</Button>
|
|
57
|
-
|
|
58
|
-
) :
|
|
64
|
+
</>
|
|
65
|
+
) : showAuthCode ? (
|
|
59
66
|
<p className='grow text-xl text-center text-success-500 dark:text-success-300 font-mono'>
|
|
60
67
|
{authenticationCode}
|
|
61
68
|
</p>
|
|
@@ -63,31 +70,35 @@ export const InvitationListItem = ({
|
|
|
63
70
|
<span role='none' className='grow' />
|
|
64
71
|
)}
|
|
65
72
|
{isCancellable ? (
|
|
66
|
-
<Button variant='ghost' compact className='flex gap-1' onClick={cancel}>
|
|
73
|
+
<Button variant='ghost' compact className='flex gap-1' onClick={cancel} data-testid='cancel-invitation'>
|
|
67
74
|
<span className='sr-only'>{t('cancel invitation label')}</span>
|
|
68
75
|
<ProhibitInset className={getSize(4)} weight='bold' />
|
|
69
76
|
</Button>
|
|
70
77
|
) : (
|
|
71
|
-
<Button
|
|
78
|
+
<Button
|
|
79
|
+
variant='ghost'
|
|
80
|
+
compact
|
|
81
|
+
className='flex gap-1'
|
|
82
|
+
onClick={handleClickRemove}
|
|
83
|
+
data-testid='remove-invitation'
|
|
84
|
+
>
|
|
72
85
|
<span className='sr-only'>{t('remove invitation label')}</span>
|
|
73
86
|
<X className={getSize(4)} weight='bold' />
|
|
74
87
|
</Button>
|
|
75
88
|
)}
|
|
76
89
|
</AccordionPrimitive.Header>
|
|
77
90
|
{showShare && invitationUrl && (
|
|
78
|
-
<AccordionPrimitive.Content>
|
|
79
|
-
<
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
</span>
|
|
90
|
-
</Button>
|
|
91
|
+
<AccordionPrimitive.Content className='flex gap-2 is-full radix-state-open:p-1 items-center'>
|
|
92
|
+
<QRCodeSVG
|
|
93
|
+
bgColor='transparent'
|
|
94
|
+
fgColor='currentColor'
|
|
95
|
+
value={invitationUrl}
|
|
96
|
+
className={mx('grow-[2] aspect-square is-24 bs-auto')}
|
|
97
|
+
aria-labelledby={qrLabel}
|
|
98
|
+
/>
|
|
99
|
+
<span className='pli-1 flex-1 font-system-normal text-sm text-center' id={qrLabel}>
|
|
100
|
+
{t('qr label')}
|
|
101
|
+
</span>
|
|
91
102
|
</AccordionPrimitive.Content>
|
|
92
103
|
)}
|
|
93
104
|
</AccordionPrimitive.Item>
|
|
@@ -5,19 +5,24 @@
|
|
|
5
5
|
import React, { ComponentPropsWithoutRef } from 'react';
|
|
6
6
|
|
|
7
7
|
import { Invitation } from '@dxos/client';
|
|
8
|
-
import { getSize, Size } from '@dxos/react-components';
|
|
8
|
+
import { getSize, mx, Size } from '@dxos/react-components';
|
|
9
9
|
|
|
10
10
|
import { inactiveStrokeColor, activeStrokeColor, resolvedStrokeColor } from '../../styles';
|
|
11
11
|
import { invitationStatusValue } from '../../util';
|
|
12
12
|
|
|
13
|
+
export interface InvitationStatusAvatarSlots {
|
|
14
|
+
svg?: ComponentPropsWithoutRef<'svg'>;
|
|
15
|
+
}
|
|
16
|
+
|
|
13
17
|
export interface InvitationStatusAvatarProps {
|
|
14
18
|
size?: Size;
|
|
15
19
|
status: Invitation.State;
|
|
16
20
|
haltedAt?: Invitation.State;
|
|
21
|
+
slots?: InvitationStatusAvatarSlots;
|
|
17
22
|
}
|
|
18
23
|
|
|
19
24
|
const svgSize = 32;
|
|
20
|
-
const strokeWidth =
|
|
25
|
+
const strokeWidth = 5;
|
|
21
26
|
const radius = (svgSize - strokeWidth) / 2;
|
|
22
27
|
const circumference = Math.PI * 2 * radius;
|
|
23
28
|
const gap = circumference / 12;
|
|
@@ -31,17 +36,17 @@ const circleProps: ComponentPropsWithoutRef<'circle'> = {
|
|
|
31
36
|
cx: svgSize / 2,
|
|
32
37
|
cy: svgSize / 2,
|
|
33
38
|
r: radius,
|
|
34
|
-
strokeWidth
|
|
39
|
+
strokeWidth,
|
|
35
40
|
strokeDasharray: `${segment - gap} ${2 * segment + gap}`
|
|
36
41
|
};
|
|
37
42
|
|
|
38
|
-
export const InvitationStatusAvatar = ({ size = 10, status, haltedAt }: InvitationStatusAvatarProps) => {
|
|
43
|
+
export const InvitationStatusAvatar = ({ size = 10, status, haltedAt, slots = {} }: InvitationStatusAvatarProps) => {
|
|
39
44
|
const resolvedColor = resolvedStrokeColor(status);
|
|
40
45
|
const halted =
|
|
41
46
|
status === Invitation.State.CANCELLED || status === Invitation.State.TIMEOUT || status === Invitation.State.ERROR;
|
|
42
47
|
const cursor = invitationStatusValue.get(halted ? haltedAt! : status)!;
|
|
43
48
|
return (
|
|
44
|
-
<svg viewBox={`0 0 ${svgSize} ${svgSize}`} className={getSize(size)}>
|
|
49
|
+
<svg {...slots.svg} viewBox={`0 0 ${svgSize} ${svgSize}`} className={mx(getSize(size), slots.svg?.className)}>
|
|
45
50
|
{[...Array(nSegments)].map((_, index) => (
|
|
46
51
|
<circle
|
|
47
52
|
key={index}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React, { ForwardedRef, forwardRef } from 'react';
|
|
6
|
+
|
|
7
|
+
import type { Space } from '@dxos/client';
|
|
8
|
+
import { Avatar, mx } from '@dxos/react-components';
|
|
9
|
+
import { humanize } from '@dxos/util';
|
|
10
|
+
|
|
11
|
+
export const SpaceListItem = forwardRef(
|
|
12
|
+
({ space, onClick }: { space: Space; onClick?: () => void }, ref: ForwardedRef<HTMLLIElement>) => {
|
|
13
|
+
return (
|
|
14
|
+
<li
|
|
15
|
+
className={mx('flex gap-2 items-center', onClick && 'cursor-pointer')}
|
|
16
|
+
onClick={() => onClick?.()}
|
|
17
|
+
ref={ref}
|
|
18
|
+
data-testid='space-list-item'
|
|
19
|
+
>
|
|
20
|
+
<Avatar
|
|
21
|
+
{...{
|
|
22
|
+
variant: 'circle',
|
|
23
|
+
size: 9,
|
|
24
|
+
fallbackValue: space.key.toHex(),
|
|
25
|
+
label: <p className='text-sm truncate'>{space.properties.name ?? humanize(space.key)}</p>,
|
|
26
|
+
slots: { labels: { className: 'block shrink overflow-hidden' }, root: { className: 'shrink-0' } }
|
|
27
|
+
}}
|
|
28
|
+
/>
|
|
29
|
+
</li>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
);
|
package/src/components/index.ts
CHANGED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Close } from '@radix-ui/react-dialog';
|
|
6
|
+
import React from 'react';
|
|
7
|
+
|
|
8
|
+
import { ThemeContext, useId } from '@dxos/react-components';
|
|
9
|
+
|
|
10
|
+
import { PanelDialog, PanelDialogProps } from '../../layouts';
|
|
11
|
+
import { DevicesPanel, DevicesPanelProps } from '../../panels';
|
|
12
|
+
|
|
13
|
+
export interface DevicesDialogProps
|
|
14
|
+
extends Omit<PanelDialogProps, 'titleId' | 'children'>,
|
|
15
|
+
Omit<DevicesPanelProps, 'doneActionParent'> {}
|
|
16
|
+
|
|
17
|
+
export const DevicesDialog = ({ slots, ...devicesDialogProps }: DevicesDialogProps) => {
|
|
18
|
+
const titleId = useId('spaceDialog__title');
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<PanelDialog
|
|
22
|
+
{...{
|
|
23
|
+
slots: { ...slots, root: { onOpenChange: (open) => open || devicesDialogProps.onDone?.(), ...slots?.root } },
|
|
24
|
+
titleId
|
|
25
|
+
}}
|
|
26
|
+
>
|
|
27
|
+
<ThemeContext.Provider value={{ themeVariant: 'os' }}>
|
|
28
|
+
<DevicesPanel
|
|
29
|
+
{...{
|
|
30
|
+
...devicesDialogProps,
|
|
31
|
+
titleId,
|
|
32
|
+
doneActionParent: <Close asChild />
|
|
33
|
+
}}
|
|
34
|
+
/>
|
|
35
|
+
</ThemeContext.Provider>
|
|
36
|
+
</PanelDialog>
|
|
37
|
+
);
|
|
38
|
+
};
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import '@dxosTheme';
|
|
6
6
|
|
|
7
|
-
import { StoryFn } from '@storybook/react';
|
|
7
|
+
import { StoryContext, StoryFn } from '@storybook/react';
|
|
8
8
|
import React, { useCallback, useMemo, useState } from 'react';
|
|
9
9
|
|
|
10
10
|
import { Client, Invitation, InvitationEncoder } from '@dxos/client';
|
|
@@ -19,62 +19,63 @@ import { JoinDialog } from './JoinDialog';
|
|
|
19
19
|
export default {
|
|
20
20
|
component: JoinDialog
|
|
21
21
|
};
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
setInvitationCode(nextInvitationCode);
|
|
38
|
-
}
|
|
39
|
-
}, []);
|
|
40
|
-
|
|
41
|
-
useAsyncEffect(async () => {
|
|
42
|
-
await Promise.all(clients.map((client) => client.initialize()));
|
|
43
|
-
log.info('[initialized]');
|
|
44
|
-
|
|
45
|
-
await clients[0].halo.createProfile({ displayName: 'Os Mutantes' });
|
|
46
|
-
|
|
47
|
-
const space = await clients[0].echo.createSpace();
|
|
48
|
-
log.info('[space created]', space);
|
|
49
|
-
space.properties.name = 'Q3 2022 Planning';
|
|
50
|
-
|
|
51
|
-
const invitation = space.createInvitation({ type: Invitation.Type.INTERACTIVE });
|
|
52
|
-
log.info('[invitation created]', invitation);
|
|
53
|
-
|
|
54
|
-
invitation.subscribe({
|
|
55
|
-
onAuthenticating: onInvitationEvent,
|
|
56
|
-
onCancelled: (...args) => log.warn('[cancelled]', args),
|
|
57
|
-
onConnected: onInvitationEvent,
|
|
58
|
-
onConnecting: onInvitationEvent,
|
|
59
|
-
onError: onInvitationEvent,
|
|
60
|
-
onSuccess: onInvitationEvent,
|
|
61
|
-
onTimeout: (...args) => log.warn('[timeout]', args)
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
return () => {
|
|
65
|
-
void Promise.all(clients.map((client) => client.destroy()));
|
|
66
|
-
};
|
|
67
|
-
}, clients);
|
|
68
|
-
|
|
69
|
-
if (!invitationCode) {
|
|
70
|
-
return <Loading label='Setting things up…' />;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return (
|
|
74
|
-
<ClientProvider client={clients[1]} fallback={() => <Loading label='Loading client…' />}>
|
|
75
|
-
<Story args={{ initialInvitationCode: invitationCode }} />
|
|
76
|
-
</ClientProvider>
|
|
77
|
-
);
|
|
22
|
+
|
|
23
|
+
const JoinClientDecorator = (Story: StoryFn, { args }: StoryContext) => {
|
|
24
|
+
const n = 2;
|
|
25
|
+
const testBuilder = new TestBuilder();
|
|
26
|
+
const clients = useMemo(() => {
|
|
27
|
+
return [...Array(n)].map(() => new Client({ services: testBuilder.createClientServicesHost() }));
|
|
28
|
+
}, []);
|
|
29
|
+
|
|
30
|
+
const [invitationCode, setInvitationCode] = useState<string>();
|
|
31
|
+
|
|
32
|
+
const onInvitationEvent = useCallback((invitation: Invitation) => {
|
|
33
|
+
if (!invitationCode) {
|
|
34
|
+
const nextInvitationCode = InvitationEncoder.encode(invitation);
|
|
35
|
+
log.info('[next invitation code]', { nextInvitationCode });
|
|
36
|
+
setInvitationCode(nextInvitationCode);
|
|
78
37
|
}
|
|
79
|
-
]
|
|
38
|
+
}, []);
|
|
39
|
+
|
|
40
|
+
useAsyncEffect(async () => {
|
|
41
|
+
await Promise.all(clients.map((client) => client.initialize()));
|
|
42
|
+
log.info('[initialized]');
|
|
43
|
+
|
|
44
|
+
await clients[0].halo.createProfile({ displayName: 'Os Mutantes' });
|
|
45
|
+
|
|
46
|
+
const space = await clients[0].echo.createSpace();
|
|
47
|
+
log.info('[space created]', space);
|
|
48
|
+
space.properties.name = 'Q3 2022 Planning';
|
|
49
|
+
|
|
50
|
+
const invitation = space.createInvitation({ type: Invitation.Type.INTERACTIVE });
|
|
51
|
+
log.info('[invitation created]', invitation);
|
|
52
|
+
|
|
53
|
+
invitation.subscribe({
|
|
54
|
+
onAuthenticating: onInvitationEvent,
|
|
55
|
+
onCancelled: (...args) => log.warn('[cancelled]', args),
|
|
56
|
+
onConnected: onInvitationEvent,
|
|
57
|
+
onConnecting: onInvitationEvent,
|
|
58
|
+
onError: onInvitationEvent,
|
|
59
|
+
onSuccess: onInvitationEvent,
|
|
60
|
+
onTimeout: (...args) => log.warn('[timeout]', args)
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
return () => {
|
|
64
|
+
void Promise.all(clients.map((client) => client.destroy()));
|
|
65
|
+
};
|
|
66
|
+
}, clients);
|
|
67
|
+
|
|
68
|
+
if (!invitationCode) {
|
|
69
|
+
return <Loading label='Setting things up…' />;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<ClientProvider client={clients[1]} fallback={() => <Loading label='Loading client…' />}>
|
|
74
|
+
<Story args={{ initialInvitationCode: invitationCode, ...args }} />
|
|
75
|
+
</ClientProvider>
|
|
76
|
+
);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export const Default = {
|
|
80
|
+
decorators: [JoinClientDecorator]
|
|
80
81
|
};
|
|
@@ -10,7 +10,9 @@ import { ThemeContext, useId } from '@dxos/react-components';
|
|
|
10
10
|
import { PanelAlertDialog, PanelAlertDialogProps } from '../../layouts';
|
|
11
11
|
import { JoinPanel, JoinPanelProps } from '../../panels';
|
|
12
12
|
|
|
13
|
-
export interface JoinDialogProps
|
|
13
|
+
export interface JoinDialogProps
|
|
14
|
+
extends Omit<PanelAlertDialogProps, 'titleId' | 'children'>,
|
|
15
|
+
Omit<JoinPanelProps, 'exitActionParent' | 'doneActionParent'> {}
|
|
14
16
|
|
|
15
17
|
export const JoinDialog = ({ slots, ...joinPanelProps }: JoinDialogProps) => {
|
|
16
18
|
const titleId = useId('joinDialog__title');
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React, { useEffect, useState } from 'react';
|
|
6
|
+
|
|
7
|
+
import { ShellRuntime } from '@dxos/client';
|
|
8
|
+
import { LayoutRequest, ShellDisplay, ShellLayout } from '@dxos/protocols/proto/dxos/iframe';
|
|
9
|
+
import { useClient, useSpace, useSpaces } from '@dxos/react-client';
|
|
10
|
+
|
|
11
|
+
import { DevicesDialog } from '../DevicesDialog';
|
|
12
|
+
import { JoinDialog } from '../JoinDialog';
|
|
13
|
+
import { SpaceDialog } from '../SpaceDialog';
|
|
14
|
+
|
|
15
|
+
export const Shell = ({ runtime, origin }: { runtime: ShellRuntime; origin: string }) => {
|
|
16
|
+
const [{ layout, invitationCode, spaceKey }, setLayout] = useState<LayoutRequest>({
|
|
17
|
+
layout: runtime.layout,
|
|
18
|
+
invitationCode: runtime.invitationCode,
|
|
19
|
+
spaceKey: runtime.spaceKey
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const client = useClient();
|
|
23
|
+
const spaces = useSpaces();
|
|
24
|
+
const space = useSpace(spaceKey);
|
|
25
|
+
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
return runtime.layoutUpdate.on((request) => setLayout(request));
|
|
28
|
+
}, []);
|
|
29
|
+
|
|
30
|
+
switch (layout) {
|
|
31
|
+
case ShellLayout.AUTH:
|
|
32
|
+
return (
|
|
33
|
+
<JoinDialog
|
|
34
|
+
mode='halo-only'
|
|
35
|
+
initialInvitationCode={invitationCode}
|
|
36
|
+
onDone={async () => {
|
|
37
|
+
// TODO(wittjosiah): Is this app-specific?
|
|
38
|
+
const space = spaces[0] ?? (await client.echo.createSpace());
|
|
39
|
+
await runtime.setAppContext({ display: ShellDisplay.NONE, spaceKey: space.key });
|
|
40
|
+
runtime.setLayout(ShellLayout.DEFAULT);
|
|
41
|
+
}}
|
|
42
|
+
/>
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
case ShellLayout.CURRENT_SPACE:
|
|
46
|
+
case ShellLayout.SPACE_LIST:
|
|
47
|
+
return (
|
|
48
|
+
<SpaceDialog
|
|
49
|
+
space={space}
|
|
50
|
+
initialState={layout === ShellLayout.SPACE_LIST ? 'space list' : 'current space'}
|
|
51
|
+
createInvitationUrl={(invitationCode) => `${origin}?spaceInvitationCode=${invitationCode}`}
|
|
52
|
+
onClickJoinSpace={() => runtime.setLayout(ShellLayout.JOIN_SPACE, { spaceKey: space?.key })}
|
|
53
|
+
onClickMember={() => runtime.setLayout(ShellLayout.DEVICES_LIST, { spaceKey: space?.key })}
|
|
54
|
+
onDone={async (space) => {
|
|
55
|
+
// TODO(wittjosiah): If space is newly created the app won't yet know about it.
|
|
56
|
+
// This is a bug that needs to be fixed when upgrading the space service.
|
|
57
|
+
await runtime.setAppContext({ display: ShellDisplay.NONE, spaceKey: space ? space.key : spaceKey });
|
|
58
|
+
runtime.setLayout(ShellLayout.DEFAULT);
|
|
59
|
+
}}
|
|
60
|
+
/>
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
case ShellLayout.JOIN_SPACE:
|
|
64
|
+
return (
|
|
65
|
+
<JoinDialog
|
|
66
|
+
initialInvitationCode={invitationCode}
|
|
67
|
+
onDone={async (result) => {
|
|
68
|
+
await runtime.setAppContext({ display: ShellDisplay.NONE, spaceKey: result?.spaceKey ?? undefined });
|
|
69
|
+
runtime.setLayout(ShellLayout.DEFAULT);
|
|
70
|
+
}}
|
|
71
|
+
onExit={async () => {
|
|
72
|
+
await runtime.setAppContext({ display: ShellDisplay.NONE, spaceKey: runtime.spaceKey });
|
|
73
|
+
runtime.setLayout(ShellLayout.DEFAULT);
|
|
74
|
+
}}
|
|
75
|
+
/>
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
case ShellLayout.DEVICES_LIST:
|
|
79
|
+
return (
|
|
80
|
+
<DevicesDialog
|
|
81
|
+
createInvitationUrl={(invitationCode) => `${origin}?haloInvitationCode=${invitationCode}`}
|
|
82
|
+
onDone={async () => {
|
|
83
|
+
await runtime.setAppContext({ display: ShellDisplay.NONE, spaceKey: runtime.spaceKey });
|
|
84
|
+
runtime.setLayout(ShellLayout.DEFAULT);
|
|
85
|
+
}}
|
|
86
|
+
/>
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
default:
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Close } from '@radix-ui/react-dialog';
|
|
6
|
+
import React from 'react';
|
|
7
|
+
|
|
8
|
+
import { ThemeContext, useId } from '@dxos/react-components';
|
|
9
|
+
|
|
10
|
+
import { PanelDialog, PanelDialogProps } from '../../layouts';
|
|
11
|
+
import { SpacePanel, SpacePanelProps } from '../../panels';
|
|
12
|
+
|
|
13
|
+
export interface SpaceDialogProps
|
|
14
|
+
extends Omit<PanelDialogProps, 'titleId' | 'children'>,
|
|
15
|
+
Omit<SpacePanelProps, 'doneActionParent'> {}
|
|
16
|
+
|
|
17
|
+
export const SpaceDialog = ({ slots, ...spacePanelProps }: SpaceDialogProps) => {
|
|
18
|
+
const titleId = useId('spaceDialog__title');
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<PanelDialog
|
|
22
|
+
{...{
|
|
23
|
+
slots: { ...slots, root: { onOpenChange: (open) => open || spacePanelProps.onDone?.(null), ...slots?.root } },
|
|
24
|
+
titleId
|
|
25
|
+
}}
|
|
26
|
+
>
|
|
27
|
+
<ThemeContext.Provider value={{ themeVariant: 'os' }}>
|
|
28
|
+
<SpacePanel
|
|
29
|
+
{...{
|
|
30
|
+
...spacePanelProps,
|
|
31
|
+
titleId,
|
|
32
|
+
doneActionParent: <Close asChild />
|
|
33
|
+
}}
|
|
34
|
+
/>
|
|
35
|
+
</ThemeContext.Provider>
|
|
36
|
+
</PanelDialog>
|
|
37
|
+
);
|
|
38
|
+
};
|
package/src/composites/index.ts
CHANGED
|
@@ -7,6 +7,8 @@ import React, { ComponentPropsWithoutRef, PropsWithChildren } from 'react';
|
|
|
7
7
|
|
|
8
8
|
import { mx } from '@dxos/react-components';
|
|
9
9
|
|
|
10
|
+
import { defaultDialogContent, defaultOverlay } from '../../styles';
|
|
11
|
+
|
|
10
12
|
export interface PanelAlertDialogSlots {
|
|
11
13
|
root: ComponentPropsWithoutRef<typeof AlertDialog.Root>;
|
|
12
14
|
overlay: ComponentPropsWithoutRef<typeof AlertDialog.Overlay>;
|
|
@@ -23,18 +25,12 @@ export const PanelAlertDialog = ({ titleId, slots = {}, children }: PanelAlertDi
|
|
|
23
25
|
return (
|
|
24
26
|
<AlertDialog.Root defaultOpen {...slots.root}>
|
|
25
27
|
{slots.trigger && <AlertDialog.Trigger {...slots.trigger} />}
|
|
26
|
-
<AlertDialog.Overlay
|
|
27
|
-
{...slots.overlay}
|
|
28
|
-
className={mx(
|
|
29
|
-
'fixed inset-0 backdrop-blur z-50 overflow-auto grid place-items-center p-2 md:p-4 lg:p-8',
|
|
30
|
-
slots.overlay?.className
|
|
31
|
-
)}
|
|
32
|
-
>
|
|
28
|
+
<AlertDialog.Overlay {...slots.overlay} className={mx(defaultOverlay, 'z-40', slots.overlay?.className)}>
|
|
33
29
|
<AlertDialog.Content
|
|
34
30
|
onEscapeKeyDown={(event) => event.preventDefault()}
|
|
35
31
|
{...slots.content}
|
|
36
32
|
aria-labelledby={titleId}
|
|
37
|
-
className={mx(
|
|
33
|
+
className={mx(defaultDialogContent, slots.content?.className)}
|
|
38
34
|
>
|
|
39
35
|
{children}
|
|
40
36
|
</AlertDialog.Content>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Dialog from '@radix-ui/react-dialog';
|
|
6
|
+
import React, { ComponentPropsWithoutRef, PropsWithChildren } from 'react';
|
|
7
|
+
|
|
8
|
+
import { mx } from '@dxos/react-components';
|
|
9
|
+
|
|
10
|
+
import { defaultDialogContent, defaultOverlay } from '../../styles';
|
|
11
|
+
|
|
12
|
+
export interface PanelDialogSlots {
|
|
13
|
+
root: ComponentPropsWithoutRef<typeof Dialog.Root>;
|
|
14
|
+
overlay: ComponentPropsWithoutRef<typeof Dialog.Overlay>;
|
|
15
|
+
content: ComponentPropsWithoutRef<typeof Dialog.Content>;
|
|
16
|
+
trigger: ComponentPropsWithoutRef<typeof Dialog.Trigger>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type PanelDialogProps = PropsWithChildren<{
|
|
20
|
+
titleId: string;
|
|
21
|
+
slots?: Partial<PanelDialogSlots>;
|
|
22
|
+
}>;
|
|
23
|
+
|
|
24
|
+
export const PanelDialog = ({ titleId, slots = {}, children }: PanelDialogProps) => {
|
|
25
|
+
return (
|
|
26
|
+
<Dialog.Root defaultOpen {...slots.root}>
|
|
27
|
+
{slots.trigger && <Dialog.Trigger {...slots.trigger} />}
|
|
28
|
+
<Dialog.Overlay {...slots.overlay} className={mx(defaultOverlay, 'z-40', slots.overlay?.className)}>
|
|
29
|
+
<Dialog.Content
|
|
30
|
+
{...slots.content}
|
|
31
|
+
aria-labelledby={titleId}
|
|
32
|
+
className={mx(defaultDialogContent, slots.content?.className)}
|
|
33
|
+
>
|
|
34
|
+
{children}
|
|
35
|
+
</Dialog.Content>
|
|
36
|
+
</Dialog.Overlay>
|
|
37
|
+
</Dialog.Root>
|
|
38
|
+
);
|
|
39
|
+
};
|
|
@@ -82,7 +82,7 @@ export const PanelSidebarProvider = ({ children, slots }: PropsWithChildren<Pane
|
|
|
82
82
|
onCloseAutoFocus={(event) => isLg && event.preventDefault()}
|
|
83
83
|
{...slots?.content}
|
|
84
84
|
className={mx(
|
|
85
|
-
'fixed block-start-0 block-end-0 is-sidebar z-
|
|
85
|
+
'fixed block-start-0 block-end-0 is-sidebar z-30 overscroll-contain overflow-x-hidden overflow-y-auto',
|
|
86
86
|
'transition-[inset-inline-start,inset-inline-end] duration-200 ease-in-out',
|
|
87
87
|
transitionShow ? 'inline-start-0' : '-inline-start-sidebar',
|
|
88
88
|
slots?.content?.className
|
package/src/layouts/index.ts
CHANGED