@dxos/react-ui 0.1.25 → 0.1.27-next.a0dff21
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 +1021 -472
- 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 +4 -3
- package/dist/types/src/components/IdentityList/IdentityListItem.d.ts.map +1 -1
- package/dist/types/src/components/IdentityList/IdentityListItem.stories.d.ts +3 -2
- 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/IdentityPopover/IdentityPopover.d.ts +2 -2
- package/dist/types/src/composites/IdentityPopover/IdentityPopover.d.ts.map +1 -1
- 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/IdentityPanel/IdentityPanel.d.ts +2 -2
- package/dist/types/src/panels/IdentityPanel/IdentityPanel.d.ts.map +1 -1
- 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 +6 -5
- package/dist/types/src/panels/JoinPanel/JoinPanelProps.d.ts.map +1 -1
- package/dist/types/src/panels/JoinPanel/view-states/AdditionMethodSelector.d.ts +2 -2
- 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 +12 -4
- 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 +2 -2
- 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/JoinPanel/view-states/ViewState.d.ts +2 -2
- package/dist/types/src/panels/JoinPanel/view-states/ViewState.d.ts.map +1 -1
- package/dist/types/src/panels/SpacePanel/SpacePanel.d.ts +12 -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 +17 -5
- package/src/components/IdentityList/SpaceMemberList.tsx +10 -8
- package/src/components/IdentityList/SpaceMemberListContainer.tsx +7 -6
- package/src/components/IdentityList/index.ts +1 -0
- package/src/components/InvitationList/InvitationList.stories.tsx +1 -1
- package/src/components/InvitationList/InvitationList.tsx +12 -6
- package/src/components/InvitationList/InvitationListItem.tsx +30 -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/IdentityPopover/IdentityPopover.tsx +3 -3
- package/src/composites/JoinDialog/JoinDialog.stories.tsx +59 -58
- package/src/composites/JoinDialog/JoinDialog.tsx +3 -1
- package/src/composites/Shell/Shell.tsx +86 -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 +101 -0
- package/src/panels/DevicesPanel/index.ts +5 -0
- package/src/panels/IdentityPanel/IdentityPanel.tsx +5 -5
- package/src/panels/JoinPanel/JoinHeading.tsx +38 -16
- package/src/panels/JoinPanel/JoinPanel.tsx +35 -14
- package/src/panels/JoinPanel/JoinPanelProps.ts +6 -10
- package/src/panels/JoinPanel/view-states/AdditionMethodSelector.tsx +8 -2
- package/src/panels/JoinPanel/view-states/IdentityAdded.tsx +67 -28
- package/src/panels/JoinPanel/view-states/IdentityInput.tsx +9 -2
- package/src/panels/JoinPanel/view-states/IdentitySelector.tsx +8 -6
- 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/JoinPanel/view-states/ViewState.tsx +3 -3
- package/src/panels/SpacePanel/SpacePanel.stories.tsx +53 -0
- package/src/panels/SpacePanel/SpacePanel.tsx +90 -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
|
@@ -35,7 +35,7 @@ export const Default = {
|
|
|
35
35
|
await Promise.all(clients.map((client) => client.initialize()));
|
|
36
36
|
log.info('[initialized]');
|
|
37
37
|
|
|
38
|
-
await clients[0].halo.
|
|
38
|
+
await clients[0].halo.createIdentity({ displayName: 'Os Mutantes' });
|
|
39
39
|
|
|
40
40
|
const space = await clients[0].echo.createSpace();
|
|
41
41
|
log.info('[space created]', space);
|
|
@@ -5,6 +5,7 @@ import { Root as AccordionRoot } from '@radix-ui/react-accordion';
|
|
|
5
5
|
import React from 'react';
|
|
6
6
|
|
|
7
7
|
import type { CancellableInvitationObservable } from '@dxos/client';
|
|
8
|
+
import { defaultDescription, DensityProvider, mx, useTranslation } from '@dxos/react-components';
|
|
8
9
|
|
|
9
10
|
import { InvitationListItem, InvitationListItemProps } from './InvitationListItem';
|
|
10
11
|
|
|
@@ -13,12 +14,17 @@ export interface InvitationListProps extends Omit<InvitationListItemProps, 'invi
|
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
export const InvitationList = ({ invitations, ...invitationProps }: InvitationListProps) => {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
const { t } = useTranslation('os');
|
|
18
|
+
return invitations.length ? (
|
|
19
|
+
<AccordionRoot type='single' collapsible className='flex flex-col gap-1'>
|
|
20
|
+
<DensityProvider density='fine'>
|
|
21
|
+
{invitations.map((invitation, index) => {
|
|
22
|
+
const value = invitation.invitation?.invitationId ?? `inv_${index}`;
|
|
23
|
+
return <InvitationListItem key={value} value={value} invitation={invitation} {...invitationProps} />;
|
|
24
|
+
})}
|
|
25
|
+
</DensityProvider>
|
|
22
26
|
</AccordionRoot>
|
|
27
|
+
) : (
|
|
28
|
+
<p className={mx(defaultDescription, 'text-center p-2')}>{t('empty invitations message')}</p>
|
|
23
29
|
);
|
|
24
30
|
};
|
|
@@ -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,29 @@ export const InvitationListItem = ({
|
|
|
63
70
|
<span role='none' className='grow' />
|
|
64
71
|
)}
|
|
65
72
|
{isCancellable ? (
|
|
66
|
-
<Button variant='ghost'
|
|
73
|
+
<Button variant='ghost' 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 variant='ghost'
|
|
78
|
+
<Button variant='ghost' className='flex gap-1' onClick={handleClickRemove} data-testid='remove-invitation'>
|
|
72
79
|
<span className='sr-only'>{t('remove invitation label')}</span>
|
|
73
80
|
<X className={getSize(4)} weight='bold' />
|
|
74
81
|
</Button>
|
|
75
82
|
)}
|
|
76
83
|
</AccordionPrimitive.Header>
|
|
77
84
|
{showShare && invitationUrl && (
|
|
78
|
-
<AccordionPrimitive.Content>
|
|
79
|
-
<
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
</span>
|
|
90
|
-
</Button>
|
|
85
|
+
<AccordionPrimitive.Content className='flex gap-2 is-full radix-state-open:p-1 items-center'>
|
|
86
|
+
<QRCodeSVG
|
|
87
|
+
bgColor='transparent'
|
|
88
|
+
fgColor='currentColor'
|
|
89
|
+
value={invitationUrl}
|
|
90
|
+
className={mx('grow-[2] aspect-square is-24 bs-auto')}
|
|
91
|
+
aria-labelledby={qrLabel}
|
|
92
|
+
/>
|
|
93
|
+
<span className='pli-1 flex-1 font-system-normal text-sm text-center' id={qrLabel}>
|
|
94
|
+
{t('qr label')}
|
|
95
|
+
</span>
|
|
91
96
|
</AccordionPrimitive.Content>
|
|
92
97
|
)}
|
|
93
98
|
</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,14 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
import React, { ComponentProps } from 'react';
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { Identity } from '@dxos/client';
|
|
8
8
|
import { Avatar, mx } from '@dxos/react-components';
|
|
9
9
|
|
|
10
10
|
import { PanelPopover } from '../../layouts';
|
|
11
11
|
import { IdentityPanel } from '../../panels';
|
|
12
12
|
|
|
13
13
|
export interface IdentityPopoverProps extends Partial<ComponentProps<typeof PanelPopover>> {
|
|
14
|
-
identity:
|
|
14
|
+
identity: Identity;
|
|
15
15
|
onClickManageProfile?: () => void;
|
|
16
16
|
}
|
|
17
17
|
|
|
@@ -32,7 +32,7 @@ export const IdentityPopover = ({
|
|
|
32
32
|
size={10}
|
|
33
33
|
variant='circle'
|
|
34
34
|
fallbackValue={identity.identityKey.toHex()}
|
|
35
|
-
label={identity.displayName ?? ''}
|
|
35
|
+
label={identity.profile?.displayName ?? ''}
|
|
36
36
|
/>
|
|
37
37
|
)
|
|
38
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.createIdentity({ 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,86 @@
|
|
|
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
|
+
}, [runtime]);
|
|
29
|
+
|
|
30
|
+
switch (layout) {
|
|
31
|
+
case ShellLayout.INITIALIZE_IDENTITY:
|
|
32
|
+
return (
|
|
33
|
+
<JoinDialog
|
|
34
|
+
mode='halo-only'
|
|
35
|
+
initialInvitationCode={invitationCode}
|
|
36
|
+
onDone={async () => {
|
|
37
|
+
// TODO(wittjosiah): Is this app-specific?
|
|
38
|
+
spaces.length > 0 || (await client.echo.createSpace());
|
|
39
|
+
await runtime.setAppContext({ display: ShellDisplay.NONE });
|
|
40
|
+
runtime.setLayout(ShellLayout.DEFAULT);
|
|
41
|
+
}}
|
|
42
|
+
/>
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
case ShellLayout.DEVICE_INVITATIONS:
|
|
46
|
+
return (
|
|
47
|
+
<DevicesDialog
|
|
48
|
+
createInvitationUrl={(invitationCode) => `${origin}?haloInvitationCode=${invitationCode}`}
|
|
49
|
+
onDone={async () => {
|
|
50
|
+
await runtime.setAppContext({ display: ShellDisplay.NONE });
|
|
51
|
+
runtime.setLayout(ShellLayout.DEFAULT);
|
|
52
|
+
}}
|
|
53
|
+
/>
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
case ShellLayout.SPACE_INVITATIONS:
|
|
57
|
+
return (
|
|
58
|
+
<SpaceDialog
|
|
59
|
+
space={space}
|
|
60
|
+
createInvitationUrl={(invitationCode) => `${origin}?spaceInvitationCode=${invitationCode}`}
|
|
61
|
+
onDone={async () => {
|
|
62
|
+
await runtime.setAppContext({ display: ShellDisplay.NONE });
|
|
63
|
+
runtime.setLayout(ShellLayout.DEFAULT);
|
|
64
|
+
}}
|
|
65
|
+
/>
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
case ShellLayout.JOIN_SPACE:
|
|
69
|
+
return (
|
|
70
|
+
<JoinDialog
|
|
71
|
+
initialInvitationCode={invitationCode}
|
|
72
|
+
onDone={async (result) => {
|
|
73
|
+
await runtime.setAppContext({ display: ShellDisplay.NONE, spaceKey: result?.spaceKey ?? undefined });
|
|
74
|
+
runtime.setLayout(ShellLayout.DEFAULT);
|
|
75
|
+
}}
|
|
76
|
+
onExit={async () => {
|
|
77
|
+
await runtime.setAppContext({ display: ShellDisplay.NONE });
|
|
78
|
+
runtime.setLayout(ShellLayout.DEFAULT);
|
|
79
|
+
}}
|
|
80
|
+
/>
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
default:
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
};
|
|
@@ -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?.(), ...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>
|