@dxos/react-ui 0.1.23 → 0.1.24-next.6b1f434
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 +2428 -72
- 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 +7 -0
- package/dist/types/src/components/IdentityList/IdentityListItem.d.ts.map +1 -0
- package/dist/types/src/components/{IdentityListItem → IdentityList}/IdentityListItem.stories.d.ts +2 -1
- package/dist/types/src/components/IdentityList/IdentityListItem.stories.d.ts.map +1 -0
- package/dist/types/src/components/IdentityList/SpaceMemberList.d.ts +7 -0
- package/dist/types/src/components/IdentityList/SpaceMemberList.d.ts.map +1 -0
- package/dist/types/src/components/IdentityList/SpaceMemberListContainer.d.ts +9 -0
- package/dist/types/src/components/IdentityList/SpaceMemberListContainer.d.ts.map +1 -0
- package/dist/types/src/components/IdentityList/index.d.ts +5 -0
- package/dist/types/src/components/IdentityList/index.d.ts.map +1 -0
- package/dist/types/src/components/InvitationList/InvitationList.d.ts +7 -0
- package/dist/types/src/components/InvitationList/InvitationList.d.ts.map +1 -0
- package/dist/types/src/components/InvitationList/InvitationList.stories.d.ts +10 -0
- package/dist/types/src/components/InvitationList/InvitationList.stories.d.ts.map +1 -0
- package/dist/types/src/components/InvitationList/InvitationListContainer.d.ts +7 -0
- package/dist/types/src/components/InvitationList/InvitationListContainer.d.ts.map +1 -0
- package/dist/types/src/components/InvitationList/InvitationListItem.d.ts +9 -0
- package/dist/types/src/components/InvitationList/InvitationListItem.d.ts.map +1 -0
- package/dist/types/src/components/InvitationList/InvitationListProps.d.ts +4 -0
- package/dist/types/src/components/InvitationList/InvitationListProps.d.ts.map +1 -0
- package/dist/types/src/components/InvitationList/InvitationStatusAvatar.d.ts +14 -0
- package/dist/types/src/components/InvitationList/InvitationStatusAvatar.d.ts.map +1 -0
- package/dist/types/src/components/InvitationList/InvitationStatusAvatar.stories.d.ts +9 -0
- package/dist/types/src/components/InvitationList/InvitationStatusAvatar.stories.d.ts.map +1 -0
- package/dist/types/src/components/InvitationList/index.d.ts +5 -0
- package/dist/types/src/components/InvitationList/index.d.ts.map +1 -0
- package/dist/types/src/components/PanelSeparator/PanelSeparator.d.ts +3 -0
- package/dist/types/src/components/PanelSeparator/PanelSeparator.d.ts.map +1 -0
- package/dist/types/src/components/PanelSeparator/index.d.ts +2 -0
- package/dist/types/src/components/PanelSeparator/index.d.ts.map +1 -0
- 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 +5 -0
- package/dist/types/src/components/index.d.ts.map +1 -0
- 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 +6 -0
- package/dist/types/src/composites/JoinDialog/JoinDialog.d.ts.map +1 -0
- package/dist/types/src/composites/JoinDialog/JoinDialog.stories.d.ts +10 -0
- package/dist/types/src/composites/JoinDialog/JoinDialog.stories.d.ts.map +1 -0
- package/dist/types/src/composites/JoinDialog/index.d.ts +2 -0
- package/dist/types/src/composites/JoinDialog/index.d.ts.map +1 -0
- 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 +3 -0
- package/dist/types/src/composites/index.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/layouts/PanelAlertDialog/PanelAlertDialog.d.ts +14 -0
- package/dist/types/src/layouts/PanelAlertDialog/PanelAlertDialog.d.ts.map +1 -0
- package/dist/types/src/layouts/PanelAlertDialog/index.d.ts +2 -0
- package/dist/types/src/layouts/PanelAlertDialog/index.d.ts.map +1 -0
- 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/PanelSidebar/PanelSidebar.d.ts +2 -3
- package/dist/types/src/layouts/PanelSidebar/PanelSidebar.d.ts.map +1 -1
- package/dist/types/src/layouts/index.d.ts +2 -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 +13 -0
- package/dist/types/src/panels/JoinPanel/JoinHeading.d.ts.map +1 -0
- package/dist/types/src/panels/JoinPanel/JoinPanel.d.ts +3 -0
- package/dist/types/src/panels/JoinPanel/JoinPanel.d.ts.map +1 -0
- package/dist/types/src/panels/JoinPanel/JoinPanelProps.d.ts +54 -0
- package/dist/types/src/panels/JoinPanel/JoinPanelProps.d.ts.map +1 -0
- package/dist/types/src/panels/JoinPanel/index.d.ts +3 -0
- package/dist/types/src/panels/JoinPanel/index.d.ts.map +1 -0
- package/dist/types/src/panels/JoinPanel/view-states/AdditionMethodSelector.d.ts +7 -0
- package/dist/types/src/panels/JoinPanel/view-states/AdditionMethodSelector.d.ts.map +1 -0
- package/dist/types/src/panels/JoinPanel/view-states/IdentityAdded.d.ts +15 -0
- package/dist/types/src/panels/JoinPanel/view-states/IdentityAdded.d.ts.map +1 -0
- package/dist/types/src/panels/JoinPanel/view-states/IdentityInput.d.ts +6 -0
- package/dist/types/src/panels/JoinPanel/view-states/IdentityInput.d.ts.map +1 -0
- package/dist/types/src/panels/JoinPanel/view-states/IdentitySelector.d.ts +7 -0
- package/dist/types/src/panels/JoinPanel/view-states/IdentitySelector.d.ts.map +1 -0
- package/dist/types/src/panels/JoinPanel/view-states/InvitationAccepted.d.ts +10 -0
- package/dist/types/src/panels/JoinPanel/view-states/InvitationAccepted.d.ts.map +1 -0
- package/dist/types/src/panels/JoinPanel/view-states/InvitationAuthenticator.d.ts +7 -0
- package/dist/types/src/panels/JoinPanel/view-states/InvitationAuthenticator.d.ts.map +1 -0
- package/dist/types/src/panels/JoinPanel/view-states/InvitationInput.d.ts +6 -0
- package/dist/types/src/panels/JoinPanel/view-states/InvitationInput.d.ts.map +1 -0
- package/dist/types/src/panels/JoinPanel/view-states/InvitationRescuer.d.ts +6 -0
- package/dist/types/src/panels/JoinPanel/view-states/InvitationRescuer.d.ts.map +1 -0
- package/dist/types/src/panels/JoinPanel/view-states/ViewState.d.ts +12 -0
- package/dist/types/src/panels/JoinPanel/view-states/ViewState.d.ts.map +1 -0
- package/dist/types/src/panels/JoinPanel/view-states/index.d.ts +9 -0
- package/dist/types/src/panels/JoinPanel/view-states/index.d.ts.map +1 -0
- 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 +3 -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 +2 -0
- package/dist/types/src/styles/index.d.ts.map +1 -1
- package/dist/types/src/styles/invitationStatusStyles.d.ts +20 -0
- package/dist/types/src/styles/invitationStatusStyles.d.ts.map +1 -0
- package/dist/types/src/styles/panelStyles.d.ts +2 -1
- package/dist/types/src/styles/panelStyles.d.ts.map +1 -1
- package/dist/types/src/translations/locales/en-US.d.ts +52 -0
- package/dist/types/src/translations/locales/en-US.d.ts.map +1 -1
- package/dist/types/src/util/index.d.ts +2 -0
- package/dist/types/src/util/index.d.ts.map +1 -0
- package/dist/types/src/util/invitationStatusValue.d.ts +8 -0
- package/dist/types/src/util/invitationStatusValue.d.ts.map +1 -0
- package/package.json +19 -11
- package/src/components/IdentityList/DeviceList.tsx +31 -0
- package/src/components/{IdentityListItem → IdentityList}/IdentityListItem.stories.tsx +0 -0
- package/src/components/{IdentityListItem → IdentityList}/IdentityListItem.tsx +18 -5
- package/src/components/IdentityList/SpaceMemberList.tsx +33 -0
- package/src/components/IdentityList/SpaceMemberListContainer.tsx +28 -0
- package/src/components/IdentityList/index.ts +8 -0
- package/src/components/InvitationList/InvitationList.stories.tsx +65 -0
- package/src/components/InvitationList/InvitationList.tsx +28 -0
- package/src/components/InvitationList/InvitationListContainer.tsx +27 -0
- package/src/components/InvitationList/InvitationListItem.tsx +106 -0
- package/src/components/InvitationList/InvitationListProps.ts +7 -0
- package/src/components/InvitationList/InvitationStatusAvatar.stories.tsx +52 -0
- package/src/components/InvitationList/InvitationStatusAvatar.tsx +80 -0
- package/src/components/InvitationList/index.ts +8 -0
- package/src/components/PanelSeparator/PanelSeparator.tsx +17 -0
- package/src/components/PanelSeparator/index.ts +5 -0
- package/src/components/SpaceList/SpaceListItem.tsx +32 -0
- package/src/components/SpaceList/index.ts +5 -0
- package/src/components/index.ts +8 -0
- package/src/composites/DevicesDialog/DevicesDialog.tsx +38 -0
- package/src/composites/DevicesDialog/index.ts +5 -0
- package/src/composites/JoinDialog/JoinDialog.stories.tsx +81 -0
- package/src/composites/JoinDialog/JoinDialog.tsx +33 -0
- package/src/composites/JoinDialog/index.ts +5 -0
- 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 +4 -0
- package/src/index.ts +1 -0
- package/src/layouts/PanelAlertDialog/PanelAlertDialog.tsx +40 -0
- package/src/layouts/PanelAlertDialog/index.ts +5 -0
- package/src/layouts/PanelDialog/PanelDialog.tsx +39 -0
- package/src/layouts/PanelDialog/index.ts +5 -0
- package/src/layouts/PanelPopover/PanelPopover.tsx +2 -2
- package/src/layouts/PanelSidebar/PanelSidebar.tsx +17 -25
- package/src/layouts/index.ts +2 -0
- package/src/panels/DevicesPanel/DevicesPanel.tsx +98 -0
- package/src/panels/DevicesPanel/index.ts +5 -0
- package/src/panels/JoinPanel/JoinHeading.tsx +85 -0
- package/src/panels/JoinPanel/JoinPanel.tsx +319 -0
- package/src/panels/JoinPanel/JoinPanelProps.ts +90 -0
- package/src/panels/JoinPanel/index.ts +6 -0
- package/src/panels/JoinPanel/view-states/AdditionMethodSelector.tsx +73 -0
- package/src/panels/JoinPanel/view-states/IdentityAdded.tsx +99 -0
- package/src/panels/JoinPanel/view-states/IdentityInput.tsx +79 -0
- package/src/panels/JoinPanel/view-states/IdentitySelector.tsx +101 -0
- package/src/panels/JoinPanel/view-states/InvitationAccepted.tsx +70 -0
- package/src/panels/JoinPanel/view-states/InvitationAuthenticator.tsx +142 -0
- package/src/panels/JoinPanel/view-states/InvitationInput.tsx +75 -0
- package/src/panels/JoinPanel/view-states/InvitationRescuer.tsx +98 -0
- package/src/panels/JoinPanel/view-states/ViewState.tsx +204 -0
- package/src/panels/JoinPanel/view-states/index.ts +12 -0
- 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 +3 -0
- package/src/styles/dialogStyles.ts +8 -0
- package/src/styles/index.ts +2 -0
- package/src/styles/invitationStatusStyles.ts +42 -0
- package/src/styles/panelStyles.ts +2 -1
- package/src/testing/ClientProvider.tsx +1 -1
- package/src/translations/locales/en-US.ts +53 -1
- package/src/util/index.ts +5 -0
- package/src/util/invitationStatusValue.ts +21 -0
- package/theme-extensions.js +11 -0
- package/dist/types/src/components/IdentityListItem/IdentityListItem.d.ts +0 -6
- package/dist/types/src/components/IdentityListItem/IdentityListItem.d.ts.map +0 -1
- package/dist/types/src/components/IdentityListItem/IdentityListItem.stories.d.ts.map +0 -1
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
import { DeviceInfo } from '@dxos/protocols/proto/dxos/halo/credentials/identity';
|
|
8
|
+
|
|
9
|
+
import { IdentityListItem } from './IdentityListItem';
|
|
10
|
+
|
|
11
|
+
export interface DeviceListProps {
|
|
12
|
+
devices: DeviceInfo[];
|
|
13
|
+
onSelect?: (device: DeviceInfo) => void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const DeviceList = ({ devices, onSelect }: DeviceListProps) => {
|
|
17
|
+
return (
|
|
18
|
+
<ul className='flex flex-col gap-2'>
|
|
19
|
+
{devices.map((device) => {
|
|
20
|
+
const identity = { identityKey: device.publicKey, displayName: device.displayName };
|
|
21
|
+
return (
|
|
22
|
+
<IdentityListItem
|
|
23
|
+
key={device.publicKey.toHex()}
|
|
24
|
+
identity={identity}
|
|
25
|
+
onClick={onSelect && (() => onSelect(device))}
|
|
26
|
+
/>
|
|
27
|
+
);
|
|
28
|
+
})}
|
|
29
|
+
</ul>
|
|
30
|
+
);
|
|
31
|
+
};
|
|
File without changes
|
|
@@ -4,17 +4,30 @@
|
|
|
4
4
|
import React from 'react';
|
|
5
5
|
|
|
6
6
|
import { Profile, SpaceMember } from '@dxos/client';
|
|
7
|
-
import { Avatar, useTranslation } from '@dxos/react-components';
|
|
7
|
+
import { Avatar, mx, useTranslation } from '@dxos/react-components';
|
|
8
8
|
|
|
9
|
-
export const IdentityListItem = ({
|
|
9
|
+
export const IdentityListItem = ({
|
|
10
|
+
identity,
|
|
11
|
+
presence,
|
|
12
|
+
onClick
|
|
13
|
+
}: {
|
|
14
|
+
identity: Profile;
|
|
15
|
+
presence?: SpaceMember['presence'];
|
|
16
|
+
onClick?: () => void;
|
|
17
|
+
}) => {
|
|
10
18
|
const { t } = useTranslation('os');
|
|
11
19
|
return (
|
|
12
|
-
<li
|
|
20
|
+
<li
|
|
21
|
+
className={mx('flex gap-2 items-center', onClick && 'cursor-pointer')}
|
|
22
|
+
onClick={() => onClick?.()}
|
|
23
|
+
data-testid='identity-list-item'
|
|
24
|
+
>
|
|
13
25
|
<Avatar
|
|
14
26
|
{...{
|
|
27
|
+
variant: 'circle',
|
|
15
28
|
size: 9,
|
|
16
29
|
fallbackValue: identity.identityKey.toHex(),
|
|
17
|
-
label: <p className='text-sm'>{identity.displayName ?? identity.identityKey.truncate()}</p>,
|
|
30
|
+
label: <p className='text-sm truncate'>{identity.displayName ?? identity.identityKey.truncate()}</p>,
|
|
18
31
|
...(presence === SpaceMember.PresenceState.OFFLINE && {
|
|
19
32
|
status: 'inactive',
|
|
20
33
|
description: (
|
|
@@ -24,7 +37,7 @@ export const IdentityListItem = ({ identity, presence }: { identity: Profile; pr
|
|
|
24
37
|
)
|
|
25
38
|
}),
|
|
26
39
|
...(presence === SpaceMember.PresenceState.ONLINE && { status: 'active' }),
|
|
27
|
-
slots: { labels: { className: 'block' } }
|
|
40
|
+
slots: { labels: { className: 'block shrink overflow-hidden' }, root: { className: 'shrink-0' } }
|
|
28
41
|
}}
|
|
29
42
|
/>
|
|
30
43
|
</li>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
import type { SpaceMember } from '@dxos/client';
|
|
8
|
+
|
|
9
|
+
import { IdentityListItem } from './IdentityListItem';
|
|
10
|
+
|
|
11
|
+
export interface SpaceMemberListProps {
|
|
12
|
+
members: SpaceMember[];
|
|
13
|
+
onSelect?: (member: SpaceMember) => void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const SpaceMemberList = ({ members, onSelect }: SpaceMemberListProps) => {
|
|
17
|
+
return (
|
|
18
|
+
<ul className='flex flex-col gap-2'>
|
|
19
|
+
{members
|
|
20
|
+
.filter((member) => member.profile)
|
|
21
|
+
.map((member) => {
|
|
22
|
+
return (
|
|
23
|
+
<IdentityListItem
|
|
24
|
+
key={member.identityKey.toHex()}
|
|
25
|
+
identity={member.profile!}
|
|
26
|
+
presence={member.presence}
|
|
27
|
+
onClick={onSelect && (() => onSelect(member))}
|
|
28
|
+
/>
|
|
29
|
+
);
|
|
30
|
+
})}
|
|
31
|
+
</ul>
|
|
32
|
+
);
|
|
33
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
import React, { useMemo } from 'react';
|
|
5
|
+
|
|
6
|
+
import { PublicKey } from '@dxos/keys';
|
|
7
|
+
import { SpaceMember, useClient, useMembers } from '@dxos/react-client';
|
|
8
|
+
|
|
9
|
+
import { SpaceMemberList } from './SpaceMemberList';
|
|
10
|
+
|
|
11
|
+
export interface SpaceMemberListContainerProps {
|
|
12
|
+
spaceKey: PublicKey;
|
|
13
|
+
includeSelf?: boolean;
|
|
14
|
+
onSelect?: (member: SpaceMember) => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const SpaceMemberListContainer = ({ spaceKey, includeSelf, onSelect }: SpaceMemberListContainerProps) => {
|
|
18
|
+
const client = useClient();
|
|
19
|
+
const allUnsortedMembers = useMembers(spaceKey);
|
|
20
|
+
const members = useMemo(
|
|
21
|
+
() =>
|
|
22
|
+
includeSelf
|
|
23
|
+
? allUnsortedMembers.sort((a) => (a.identityKey.equals(client.halo.profile!.identityKey) ? -1 : 1))
|
|
24
|
+
: allUnsortedMembers.filter((member) => !member.identityKey.equals(client.halo.profile!.identityKey)),
|
|
25
|
+
[allUnsortedMembers]
|
|
26
|
+
);
|
|
27
|
+
return <SpaceMemberList members={members} onSelect={onSelect} />;
|
|
28
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2022 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import '@dxosTheme';
|
|
6
|
+
|
|
7
|
+
import { StoryFn } from '@storybook/react';
|
|
8
|
+
import React, { useMemo, useState } from 'react';
|
|
9
|
+
|
|
10
|
+
import { CancellableInvitationObservable, Client, Invitation } from '@dxos/client';
|
|
11
|
+
import { TestBuilder } from '@dxos/client/testing';
|
|
12
|
+
import { log } from '@dxos/log';
|
|
13
|
+
import { useAsyncEffect } from '@dxos/react-async';
|
|
14
|
+
import { ClientProvider } from '@dxos/react-client';
|
|
15
|
+
import { Loading, mx, ThemeContext } from '@dxos/react-components';
|
|
16
|
+
|
|
17
|
+
import { defaultSurface } from '../../styles';
|
|
18
|
+
import { InvitationList } from './InvitationList';
|
|
19
|
+
|
|
20
|
+
export default {
|
|
21
|
+
component: InvitationList
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const Default = {
|
|
25
|
+
decorators: [
|
|
26
|
+
(Story: StoryFn) => {
|
|
27
|
+
const testBuilder = new TestBuilder();
|
|
28
|
+
const clients = useMemo(() => {
|
|
29
|
+
return [...Array(1)].map(() => new Client({ services: testBuilder.createClientServicesHost() }));
|
|
30
|
+
}, []);
|
|
31
|
+
|
|
32
|
+
const [invitations, setInvitations] = useState<CancellableInvitationObservable[]>([]);
|
|
33
|
+
|
|
34
|
+
useAsyncEffect(async () => {
|
|
35
|
+
await Promise.all(clients.map((client) => client.initialize()));
|
|
36
|
+
log.info('[initialized]');
|
|
37
|
+
|
|
38
|
+
await clients[0].halo.createProfile({ displayName: 'Os Mutantes' });
|
|
39
|
+
|
|
40
|
+
const space = await clients[0].echo.createSpace();
|
|
41
|
+
log.info('[space created]', space);
|
|
42
|
+
space.properties.name = 'Q3 2022 Planning';
|
|
43
|
+
log.info('[space title set]', space?.properties.name);
|
|
44
|
+
|
|
45
|
+
setInvitations([...Array(4)].map(() => space.createInvitation({ type: Invitation.Type.INTERACTIVE })));
|
|
46
|
+
|
|
47
|
+
log.info('[invitations created]');
|
|
48
|
+
|
|
49
|
+
return () => {
|
|
50
|
+
void Promise.all(clients.map((client) => client.destroy()));
|
|
51
|
+
};
|
|
52
|
+
}, clients);
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<ClientProvider client={clients[0]} fallback={() => <Loading label='Loading client…' />}>
|
|
56
|
+
<ThemeContext.Provider value={{ themeVariant: 'os' }}>
|
|
57
|
+
<div className={mx(defaultSurface, 'max-is-xs mli-auto rounded-md p-2 backdrop-blur-md')}>
|
|
58
|
+
<Story args={{ invitations, createInvitationUrl: (code: string) => code }} />
|
|
59
|
+
</div>
|
|
60
|
+
</ThemeContext.Provider>
|
|
61
|
+
</ClientProvider>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
]
|
|
65
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
import { Root as AccordionRoot } from '@radix-ui/react-accordion';
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
import type { CancellableInvitationObservable } from '@dxos/client';
|
|
8
|
+
import { defaultDescription, mx, useTranslation } from '@dxos/react-components';
|
|
9
|
+
|
|
10
|
+
import { InvitationListItem, InvitationListItemProps } from './InvitationListItem';
|
|
11
|
+
|
|
12
|
+
export interface InvitationListProps extends Omit<InvitationListItemProps, 'invitation' | 'value'> {
|
|
13
|
+
invitations: CancellableInvitationObservable[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const InvitationList = ({ invitations, ...invitationProps }: InvitationListProps) => {
|
|
17
|
+
const { t } = useTranslation('os');
|
|
18
|
+
return invitations.length ? (
|
|
19
|
+
<AccordionRoot type='single' collapsible className='flex flex-col gap-1'>
|
|
20
|
+
{invitations.map((invitation, index) => {
|
|
21
|
+
const value = invitation.invitation?.invitationId ?? `inv_${index}`;
|
|
22
|
+
return <InvitationListItem key={value} value={value} invitation={invitation} {...invitationProps} />;
|
|
23
|
+
})}
|
|
24
|
+
</AccordionRoot>
|
|
25
|
+
) : (
|
|
26
|
+
<p className={mx(defaultDescription, 'text-center p-2')}>{t('empty invitations message')}</p>
|
|
27
|
+
);
|
|
28
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
import React, { useCallback } from 'react';
|
|
5
|
+
|
|
6
|
+
import type { CancellableInvitationObservable } from '@dxos/client';
|
|
7
|
+
import { PublicKey } from '@dxos/keys';
|
|
8
|
+
import { useSpace, useSpaceInvitations } from '@dxos/react-client';
|
|
9
|
+
|
|
10
|
+
import { InvitationList } from './InvitationList';
|
|
11
|
+
import { SharedInvitationListProps } from './InvitationListProps';
|
|
12
|
+
|
|
13
|
+
export interface InvitationListContainerProps extends SharedInvitationListProps {
|
|
14
|
+
spaceKey: PublicKey;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const InvitationListContainer = ({ spaceKey, createInvitationUrl }: InvitationListContainerProps) => {
|
|
18
|
+
const space = useSpace(spaceKey);
|
|
19
|
+
const invitations = useSpaceInvitations(spaceKey);
|
|
20
|
+
const onClickRemove = useCallback(
|
|
21
|
+
({ invitation }: CancellableInvitationObservable) => {
|
|
22
|
+
invitation?.invitationId && space?.removeInvitation(invitation.invitationId);
|
|
23
|
+
},
|
|
24
|
+
[space]
|
|
25
|
+
);
|
|
26
|
+
return <InvitationList invitations={invitations} {...{ onClickRemove, createInvitationUrl }} />;
|
|
27
|
+
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
import * as AccordionPrimitive from '@radix-ui/react-accordion';
|
|
5
|
+
import { Copy, ProhibitInset, QrCode, X } from 'phosphor-react';
|
|
6
|
+
import { QRCodeSVG } from 'qrcode.react';
|
|
7
|
+
import React, { useCallback } from 'react';
|
|
8
|
+
|
|
9
|
+
import { CancellableInvitationObservable } from '@dxos/client';
|
|
10
|
+
import { useInvitationStatus } from '@dxos/react-client';
|
|
11
|
+
import { Button, getSize, mx, useId, useTranslation } from '@dxos/react-components';
|
|
12
|
+
|
|
13
|
+
import { invitationStatusValue } from '../../util';
|
|
14
|
+
import { SharedInvitationListProps } from './InvitationListProps';
|
|
15
|
+
import { InvitationStatusAvatar } from './InvitationStatusAvatar';
|
|
16
|
+
|
|
17
|
+
export interface InvitationListItemProps extends SharedInvitationListProps {
|
|
18
|
+
invitation: CancellableInvitationObservable;
|
|
19
|
+
value: string;
|
|
20
|
+
onClickRemove: (invitation: CancellableInvitationObservable) => void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const InvitationListItem = ({
|
|
24
|
+
invitation,
|
|
25
|
+
value,
|
|
26
|
+
onClickRemove,
|
|
27
|
+
createInvitationUrl
|
|
28
|
+
}: InvitationListItemProps) => {
|
|
29
|
+
const { t } = useTranslation('os');
|
|
30
|
+
const qrLabel = useId('qrLabel');
|
|
31
|
+
|
|
32
|
+
const { cancel, status, haltedAt, invitationCode, authenticationCode } = useInvitationStatus(invitation);
|
|
33
|
+
const statusValue = invitationStatusValue.get(status) ?? 0;
|
|
34
|
+
|
|
35
|
+
const isCancellable = statusValue < 4 && statusValue >= 0;
|
|
36
|
+
const showShare = statusValue < 3 && statusValue >= 0;
|
|
37
|
+
const showAuthCode = statusValue === 3;
|
|
38
|
+
|
|
39
|
+
const handleClickRemove = useCallback(() => onClickRemove(invitation), [invitation, onClickRemove]);
|
|
40
|
+
|
|
41
|
+
const invitationUrl = invitationCode && createInvitationUrl(invitationCode);
|
|
42
|
+
const handleClickCopy = useCallback(() => {
|
|
43
|
+
if (invitationUrl) {
|
|
44
|
+
void navigator.clipboard.writeText(invitationUrl);
|
|
45
|
+
}
|
|
46
|
+
}, [invitationUrl]);
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<AccordionPrimitive.Item value={value}>
|
|
50
|
+
<AccordionPrimitive.Header className='flex gap-2 items-center'>
|
|
51
|
+
<InvitationStatusAvatar {...{ status, haltedAt, size: 8 }} />
|
|
52
|
+
{showShare && invitationUrl ? (
|
|
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' />
|
|
63
|
+
</Button>
|
|
64
|
+
</>
|
|
65
|
+
) : showAuthCode ? (
|
|
66
|
+
<p className='grow text-xl text-center text-success-500 dark:text-success-300 font-mono'>
|
|
67
|
+
{authenticationCode}
|
|
68
|
+
</p>
|
|
69
|
+
) : (
|
|
70
|
+
<span role='none' className='grow' />
|
|
71
|
+
)}
|
|
72
|
+
{isCancellable ? (
|
|
73
|
+
<Button variant='ghost' compact className='flex gap-1' onClick={cancel} data-testid='cancel-invitation'>
|
|
74
|
+
<span className='sr-only'>{t('cancel invitation label')}</span>
|
|
75
|
+
<ProhibitInset className={getSize(4)} weight='bold' />
|
|
76
|
+
</Button>
|
|
77
|
+
) : (
|
|
78
|
+
<Button
|
|
79
|
+
variant='ghost'
|
|
80
|
+
compact
|
|
81
|
+
className='flex gap-1'
|
|
82
|
+
onClick={handleClickRemove}
|
|
83
|
+
data-testid='remove-invitation'
|
|
84
|
+
>
|
|
85
|
+
<span className='sr-only'>{t('remove invitation label')}</span>
|
|
86
|
+
<X className={getSize(4)} weight='bold' />
|
|
87
|
+
</Button>
|
|
88
|
+
)}
|
|
89
|
+
</AccordionPrimitive.Header>
|
|
90
|
+
{showShare && invitationUrl && (
|
|
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>
|
|
102
|
+
</AccordionPrimitive.Content>
|
|
103
|
+
)}
|
|
104
|
+
</AccordionPrimitive.Item>
|
|
105
|
+
);
|
|
106
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
import { StoryFn } from '@storybook/react';
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
import { Invitation } from '@dxos/client';
|
|
8
|
+
import { mx, ThemeContext } from '@dxos/react-components';
|
|
9
|
+
|
|
10
|
+
import { defaultSurface } from '../../styles';
|
|
11
|
+
import { InvitationStatusAvatar } from './InvitationStatusAvatar';
|
|
12
|
+
|
|
13
|
+
const haltedAts = [null, Invitation.State.CONNECTED, Invitation.State.AUTHENTICATING, Invitation.State.SUCCESS];
|
|
14
|
+
|
|
15
|
+
const statuses = [
|
|
16
|
+
Invitation.State.INIT,
|
|
17
|
+
Invitation.State.CONNECTING,
|
|
18
|
+
Invitation.State.CONNECTED,
|
|
19
|
+
Invitation.State.AUTHENTICATING,
|
|
20
|
+
Invitation.State.SUCCESS,
|
|
21
|
+
Invitation.State.CANCELLED,
|
|
22
|
+
Invitation.State.TIMEOUT,
|
|
23
|
+
Invitation.State.ERROR
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
export default {
|
|
27
|
+
component: InvitationStatusAvatar
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const Default = {
|
|
31
|
+
decorators: [
|
|
32
|
+
(Story: StoryFn) => {
|
|
33
|
+
return (
|
|
34
|
+
<ThemeContext.Provider value={{ themeVariant: 'os' }}>
|
|
35
|
+
<div
|
|
36
|
+
className={mx(defaultSurface, 'max-is-md mli-auto rounded-md p-2 backdrop-blur-md flex flex-wrap gap-4')}
|
|
37
|
+
>
|
|
38
|
+
{haltedAts.map((haltedAt) => (
|
|
39
|
+
<>
|
|
40
|
+
{statuses.map((status) => {
|
|
41
|
+
return (
|
|
42
|
+
<Story key={`${status}__${haltedAt}`} args={{ status, ...(haltedAt !== null && { haltedAt }) }} />
|
|
43
|
+
);
|
|
44
|
+
})}
|
|
45
|
+
</>
|
|
46
|
+
))}
|
|
47
|
+
</div>
|
|
48
|
+
</ThemeContext.Provider>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
]
|
|
52
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React, { ComponentPropsWithoutRef } from 'react';
|
|
6
|
+
|
|
7
|
+
import { Invitation } from '@dxos/client';
|
|
8
|
+
import { getSize, mx, Size } from '@dxos/react-components';
|
|
9
|
+
|
|
10
|
+
import { inactiveStrokeColor, activeStrokeColor, resolvedStrokeColor } from '../../styles';
|
|
11
|
+
import { invitationStatusValue } from '../../util';
|
|
12
|
+
|
|
13
|
+
export interface InvitationStatusAvatarSlots {
|
|
14
|
+
svg?: ComponentPropsWithoutRef<'svg'>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface InvitationStatusAvatarProps {
|
|
18
|
+
size?: Size;
|
|
19
|
+
status: Invitation.State;
|
|
20
|
+
haltedAt?: Invitation.State;
|
|
21
|
+
slots?: InvitationStatusAvatarSlots;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const svgSize = 32;
|
|
25
|
+
const strokeWidth = 5;
|
|
26
|
+
const radius = (svgSize - strokeWidth) / 2;
|
|
27
|
+
const circumference = Math.PI * 2 * radius;
|
|
28
|
+
const gap = circumference / 12;
|
|
29
|
+
const nSegments = 3;
|
|
30
|
+
const segment = circumference / nSegments;
|
|
31
|
+
const baseOffset = (2 * circumference) / 3 - (segment - gap) / 2;
|
|
32
|
+
|
|
33
|
+
const circleProps: ComponentPropsWithoutRef<'circle'> = {
|
|
34
|
+
fill: 'none',
|
|
35
|
+
strokeLinecap: 'round',
|
|
36
|
+
cx: svgSize / 2,
|
|
37
|
+
cy: svgSize / 2,
|
|
38
|
+
r: radius,
|
|
39
|
+
strokeWidth,
|
|
40
|
+
strokeDasharray: `${segment - gap} ${2 * segment + gap}`
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const InvitationStatusAvatar = ({ size = 10, status, haltedAt, slots = {} }: InvitationStatusAvatarProps) => {
|
|
44
|
+
const resolvedColor = resolvedStrokeColor(status);
|
|
45
|
+
const halted =
|
|
46
|
+
status === Invitation.State.CANCELLED || status === Invitation.State.TIMEOUT || status === Invitation.State.ERROR;
|
|
47
|
+
const cursor = invitationStatusValue.get(halted ? haltedAt! : status)!;
|
|
48
|
+
return (
|
|
49
|
+
<svg {...slots.svg} viewBox={`0 0 ${svgSize} ${svgSize}`} className={mx(getSize(size), slots.svg?.className)}>
|
|
50
|
+
{[...Array(nSegments)].map((_, index) => (
|
|
51
|
+
<circle
|
|
52
|
+
key={index}
|
|
53
|
+
{...circleProps}
|
|
54
|
+
strokeDashoffset={index * segment + baseOffset}
|
|
55
|
+
className={
|
|
56
|
+
index === 0
|
|
57
|
+
? cursor === 1
|
|
58
|
+
? halted
|
|
59
|
+
? resolvedColor
|
|
60
|
+
: activeStrokeColor
|
|
61
|
+
: cursor > 1
|
|
62
|
+
? resolvedColor
|
|
63
|
+
: inactiveStrokeColor
|
|
64
|
+
: index === 1
|
|
65
|
+
? cursor === 3
|
|
66
|
+
? halted
|
|
67
|
+
? resolvedColor
|
|
68
|
+
: activeStrokeColor
|
|
69
|
+
: cursor > 3
|
|
70
|
+
? resolvedColor
|
|
71
|
+
: inactiveStrokeColor
|
|
72
|
+
: cursor > 3
|
|
73
|
+
? resolvedColor
|
|
74
|
+
: inactiveStrokeColor
|
|
75
|
+
}
|
|
76
|
+
/>
|
|
77
|
+
))}
|
|
78
|
+
</svg>
|
|
79
|
+
);
|
|
80
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React, { ComponentProps } from 'react';
|
|
6
|
+
|
|
7
|
+
import { mx } from '@dxos/react-components';
|
|
8
|
+
|
|
9
|
+
export const PanelSeparator = ({ className, ...props }: ComponentProps<'span'>) => {
|
|
10
|
+
return (
|
|
11
|
+
<span
|
|
12
|
+
role='none'
|
|
13
|
+
{...props}
|
|
14
|
+
className={mx('block bs-px mlb-1 bg-neutral-800/10 dark:bg-neutral-200/10', className)}
|
|
15
|
+
/>
|
|
16
|
+
);
|
|
17
|
+
};
|
|
@@ -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
|
+
);
|
|
@@ -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
|
+
};
|