@dxos/react-ui 0.1.23 → 0.1.24
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 +1781 -57
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/types/src/components/{IdentityListItem → IdentityList}/IdentityListItem.d.ts +0 -0
- package/dist/types/src/components/IdentityList/IdentityListItem.d.ts.map +1 -0
- package/dist/types/src/components/{IdentityListItem → IdentityList}/IdentityListItem.stories.d.ts +0 -0
- package/dist/types/src/components/IdentityList/IdentityListItem.stories.d.ts.map +1 -0
- package/dist/types/src/components/IdentityList/SpaceMemberList.d.ts +6 -0
- package/dist/types/src/components/IdentityList/SpaceMemberList.d.ts.map +1 -0
- package/dist/types/src/components/IdentityList/SpaceMemberListContainer.d.ts +7 -0
- package/dist/types/src/components/IdentityList/SpaceMemberListContainer.d.ts.map +1 -0
- package/dist/types/src/components/IdentityList/index.d.ts +4 -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 +9 -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/index.d.ts +4 -0
- package/dist/types/src/components/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/index.d.ts +1 -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/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 +1 -0
- package/dist/types/src/layouts/index.d.ts.map +1 -1
- package/dist/types/src/panels/JoinPanel/JoinHeading.d.ts +10 -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 +53 -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 +7 -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/index.d.ts +1 -0
- package/dist/types/src/panels/index.d.ts.map +1 -1
- 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/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 +41 -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 +18 -11
- package/src/components/{IdentityListItem → IdentityList}/IdentityListItem.stories.tsx +0 -0
- package/src/components/{IdentityListItem → IdentityList}/IdentityListItem.tsx +4 -3
- package/src/components/IdentityList/SpaceMemberList.tsx +31 -0
- package/src/components/IdentityList/SpaceMemberListContainer.tsx +27 -0
- package/src/components/IdentityList/index.ts +7 -0
- package/src/components/InvitationList/InvitationList.stories.tsx +65 -0
- package/src/components/InvitationList/InvitationList.tsx +24 -0
- package/src/components/InvitationList/InvitationListContainer.tsx +27 -0
- package/src/components/InvitationList/InvitationListItem.tsx +95 -0
- package/src/components/InvitationList/InvitationListProps.ts +7 -0
- package/src/components/InvitationList/InvitationStatusAvatar.stories.tsx +52 -0
- package/src/components/InvitationList/InvitationStatusAvatar.tsx +75 -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/index.ts +7 -0
- package/src/composites/JoinDialog/JoinDialog.stories.tsx +80 -0
- package/src/composites/JoinDialog/JoinDialog.tsx +31 -0
- package/src/composites/JoinDialog/index.ts +5 -0
- package/src/composites/index.ts +1 -0
- package/src/index.ts +1 -0
- package/src/layouts/PanelAlertDialog/PanelAlertDialog.tsx +44 -0
- package/src/layouts/PanelAlertDialog/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 +1 -0
- package/src/panels/JoinPanel/JoinHeading.tsx +57 -0
- package/src/panels/JoinPanel/JoinPanel.tsx +303 -0
- package/src/panels/JoinPanel/JoinPanelProps.ts +94 -0
- package/src/panels/JoinPanel/index.ts +6 -0
- package/src/panels/JoinPanel/view-states/AdditionMethodSelector.tsx +69 -0
- package/src/panels/JoinPanel/view-states/IdentityAdded.tsx +60 -0
- package/src/panels/JoinPanel/view-states/IdentityInput.tsx +72 -0
- package/src/panels/JoinPanel/view-states/IdentitySelector.tsx +98 -0
- package/src/panels/JoinPanel/view-states/InvitationAccepted.tsx +69 -0
- package/src/panels/JoinPanel/view-states/InvitationAuthenticator.tsx +139 -0
- package/src/panels/JoinPanel/view-states/InvitationInput.tsx +68 -0
- package/src/panels/JoinPanel/view-states/InvitationRescuer.tsx +95 -0
- package/src/panels/JoinPanel/view-states/ViewState.tsx +204 -0
- package/src/panels/JoinPanel/view-states/index.ts +12 -0
- package/src/panels/index.ts +1 -0
- package/src/styles/index.ts +1 -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 +42 -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.map +0 -1
- package/dist/types/src/components/IdentityListItem/IdentityListItem.stories.d.ts.map +0 -1
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2022 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import '@dxosTheme';
|
|
6
|
+
|
|
7
|
+
import { StoryFn } from '@storybook/react';
|
|
8
|
+
import React, { useCallback, useMemo, useState } from 'react';
|
|
9
|
+
|
|
10
|
+
import { Client, Invitation, InvitationEncoder } 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 } from '@dxos/react-components';
|
|
16
|
+
|
|
17
|
+
import { JoinDialog } from './JoinDialog';
|
|
18
|
+
|
|
19
|
+
export default {
|
|
20
|
+
component: JoinDialog
|
|
21
|
+
};
|
|
22
|
+
export const Default = {
|
|
23
|
+
decorators: [
|
|
24
|
+
(Story: StoryFn) => {
|
|
25
|
+
const n = 2;
|
|
26
|
+
const testBuilder = new TestBuilder();
|
|
27
|
+
const clients = useMemo(() => {
|
|
28
|
+
return [...Array(n)].map(() => new Client({ services: testBuilder.createClientServicesHost() }));
|
|
29
|
+
}, []);
|
|
30
|
+
|
|
31
|
+
const [invitationCode, setInvitationCode] = useState<string>();
|
|
32
|
+
|
|
33
|
+
const onInvitationEvent = useCallback((invitation: Invitation) => {
|
|
34
|
+
if (!invitationCode) {
|
|
35
|
+
const nextInvitationCode = InvitationEncoder.encode(invitation);
|
|
36
|
+
log.info('[next invitation code]', { nextInvitationCode });
|
|
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
|
+
);
|
|
78
|
+
}
|
|
79
|
+
]
|
|
80
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Action, Cancel } from '@radix-ui/react-alert-dialog';
|
|
6
|
+
import React from 'react';
|
|
7
|
+
|
|
8
|
+
import { ThemeContext, useId } from '@dxos/react-components';
|
|
9
|
+
|
|
10
|
+
import { PanelAlertDialog, PanelAlertDialogProps } from '../../layouts';
|
|
11
|
+
import { JoinPanel, JoinPanelProps } from '../../panels';
|
|
12
|
+
|
|
13
|
+
export interface JoinDialogProps extends Omit<PanelAlertDialogProps, 'titleId' | 'children'>, JoinPanelProps {}
|
|
14
|
+
|
|
15
|
+
export const JoinDialog = ({ slots, ...joinPanelProps }: JoinDialogProps) => {
|
|
16
|
+
const titleId = useId('joinDialog__title');
|
|
17
|
+
return (
|
|
18
|
+
<PanelAlertDialog {...{ slots, titleId }}>
|
|
19
|
+
<ThemeContext.Provider value={{ themeVariant: 'os' }}>
|
|
20
|
+
<JoinPanel
|
|
21
|
+
{...{
|
|
22
|
+
...joinPanelProps,
|
|
23
|
+
titleId,
|
|
24
|
+
exitActionParent: <Cancel asChild />,
|
|
25
|
+
doneActionParent: <Action asChild />
|
|
26
|
+
}}
|
|
27
|
+
/>
|
|
28
|
+
</ThemeContext.Provider>
|
|
29
|
+
</PanelAlertDialog>
|
|
30
|
+
);
|
|
31
|
+
};
|
package/src/composites/index.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as AlertDialog from '@radix-ui/react-alert-dialog';
|
|
6
|
+
import React, { ComponentPropsWithoutRef, PropsWithChildren } from 'react';
|
|
7
|
+
|
|
8
|
+
import { mx } from '@dxos/react-components';
|
|
9
|
+
|
|
10
|
+
export interface PanelAlertDialogSlots {
|
|
11
|
+
root: ComponentPropsWithoutRef<typeof AlertDialog.Root>;
|
|
12
|
+
overlay: ComponentPropsWithoutRef<typeof AlertDialog.Overlay>;
|
|
13
|
+
content: ComponentPropsWithoutRef<typeof AlertDialog.Content>;
|
|
14
|
+
trigger: ComponentPropsWithoutRef<typeof AlertDialog.Trigger>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type PanelAlertDialogProps = PropsWithChildren<{
|
|
18
|
+
titleId: string;
|
|
19
|
+
slots?: Partial<PanelAlertDialogSlots>;
|
|
20
|
+
}>;
|
|
21
|
+
|
|
22
|
+
export const PanelAlertDialog = ({ titleId, slots = {}, children }: PanelAlertDialogProps) => {
|
|
23
|
+
return (
|
|
24
|
+
<AlertDialog.Root defaultOpen {...slots.root}>
|
|
25
|
+
{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
|
+
>
|
|
33
|
+
<AlertDialog.Content
|
|
34
|
+
onEscapeKeyDown={(event) => event.preventDefault()}
|
|
35
|
+
{...slots.content}
|
|
36
|
+
aria-labelledby={titleId}
|
|
37
|
+
className={mx('is-full min-is-[260px] max-is-[320px] shadow-md backdrop-blur-md', slots.content?.className)}
|
|
38
|
+
>
|
|
39
|
+
{children}
|
|
40
|
+
</AlertDialog.Content>
|
|
41
|
+
</AlertDialog.Overlay>
|
|
42
|
+
</AlertDialog.Root>
|
|
43
|
+
);
|
|
44
|
+
};
|
|
@@ -6,7 +6,7 @@ import React, { ComponentProps } from 'react';
|
|
|
6
6
|
|
|
7
7
|
import { mx, Popover } from '@dxos/react-components';
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import { defaultSurface, defaultArrow } from '../../styles';
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* This component essentially sets default styles for popovers which have panels as their content
|
|
@@ -24,7 +24,7 @@ export const PanelPopover = ({ slots, children, ...popoverProps }: ComponentProp
|
|
|
24
24
|
collisionPadding: 8,
|
|
25
25
|
sideOffset: 4,
|
|
26
26
|
...slots?.content,
|
|
27
|
-
className: mx(
|
|
27
|
+
className: mx(defaultSurface, slots?.content?.className)
|
|
28
28
|
},
|
|
29
29
|
trigger: {
|
|
30
30
|
...slots?.trigger,
|
|
@@ -37,8 +37,7 @@ export const useTogglePanelSidebar = () => {
|
|
|
37
37
|
|
|
38
38
|
export interface PanelSidebarProviderSlots {
|
|
39
39
|
content?: ComponentProps<typeof DialogPrimitive.Content>;
|
|
40
|
-
|
|
41
|
-
fixedBlockEnd?: ComponentProps<'div'>;
|
|
40
|
+
main?: ComponentProps<'div'>;
|
|
42
41
|
}
|
|
43
42
|
|
|
44
43
|
export interface PanelSidebarProviderProps {
|
|
@@ -46,13 +45,9 @@ export interface PanelSidebarProviderProps {
|
|
|
46
45
|
slots?: PanelSidebarProviderSlots;
|
|
47
46
|
}
|
|
48
47
|
|
|
49
|
-
export const PanelSidebarProvider = ({
|
|
50
|
-
children,
|
|
51
|
-
inlineStart,
|
|
52
|
-
slots
|
|
53
|
-
}: PropsWithChildren<PanelSidebarProviderProps>) => {
|
|
48
|
+
export const PanelSidebarProvider = ({ children, slots }: PropsWithChildren<PanelSidebarProviderProps>) => {
|
|
54
49
|
const { t } = useTranslation('os');
|
|
55
|
-
const [isLg] = useMediaQuery('lg');
|
|
50
|
+
const [isLg] = useMediaQuery('lg', { ssr: false });
|
|
56
51
|
const [displayState, setInternalDisplayState] = useState<PanelSidebarState>(isLg ? 'show' : 'hide');
|
|
57
52
|
const isOpen = displayState === 'show';
|
|
58
53
|
const [transitionShow, setTransitionShow] = useState(isOpen);
|
|
@@ -81,30 +76,23 @@ export const PanelSidebarProvider = ({
|
|
|
81
76
|
return (
|
|
82
77
|
<PanelSidebarContext.Provider value={{ setDisplayState, displayState }}>
|
|
83
78
|
<DialogPrimitive.Root open={domShow} modal={!isLg}>
|
|
79
|
+
{/* Sidebar. */}
|
|
84
80
|
<DialogPrimitive.Content
|
|
81
|
+
onOpenAutoFocus={(event) => isLg && event.preventDefault()}
|
|
82
|
+
onCloseAutoFocus={(event) => isLg && event.preventDefault()}
|
|
85
83
|
{...slots?.content}
|
|
86
84
|
className={mx(
|
|
87
|
-
'fixed block-start-0 block-end-0 is-
|
|
88
|
-
|
|
85
|
+
'fixed block-start-0 block-end-0 is-sidebar z-50 overscroll-contain overflow-x-hidden overflow-y-auto',
|
|
86
|
+
'transition-[inset-inline-start,inset-inline-end] duration-200 ease-in-out',
|
|
87
|
+
transitionShow ? 'inline-start-0' : '-inline-start-sidebar',
|
|
89
88
|
slots?.content?.className
|
|
90
89
|
)}
|
|
91
90
|
>
|
|
92
91
|
<DialogPrimitive.Title className='sr-only'>{t('sidebar label')}</DialogPrimitive.Title>
|
|
93
92
|
{slots?.content?.children}
|
|
94
93
|
</DialogPrimitive.Content>
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
role='none'
|
|
98
|
-
{...slots?.fixedBlockStart}
|
|
99
|
-
className={mx(
|
|
100
|
-
'fixed inline-end-0 block-start-0 z-[49] transition-[inset-inline-start,inset-inline-end] duration-200 ease-in-out',
|
|
101
|
-
transitionShow ? 'inline-start-[272px]' : 'inline-start-0',
|
|
102
|
-
slots?.fixedBlockStart?.className
|
|
103
|
-
)}
|
|
104
|
-
>
|
|
105
|
-
{slots?.fixedBlockStart?.children}
|
|
106
|
-
</div>
|
|
107
|
-
)}
|
|
94
|
+
|
|
95
|
+
{/* TODO(burdon): Simple comment required. */}
|
|
108
96
|
{!isLg && (
|
|
109
97
|
<DialogPrimitive.Overlay
|
|
110
98
|
className={mx(
|
|
@@ -115,11 +103,15 @@ export const PanelSidebarProvider = ({
|
|
|
115
103
|
onClick={internalHide}
|
|
116
104
|
/>
|
|
117
105
|
)}
|
|
106
|
+
|
|
107
|
+
{/* Main content. */}
|
|
118
108
|
<div
|
|
119
109
|
role='none'
|
|
110
|
+
{...slots?.main}
|
|
120
111
|
className={mx(
|
|
121
|
-
'
|
|
122
|
-
isLg && isOpen ? 'pis-
|
|
112
|
+
'transition-[padding-inline-start] duration-200 ease-in-out',
|
|
113
|
+
isLg && isOpen ? 'pis-sidebar' : 'pis-0',
|
|
114
|
+
slots?.main?.className
|
|
123
115
|
)}
|
|
124
116
|
>
|
|
125
117
|
{children}
|
package/src/layouts/index.ts
CHANGED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
import { ProhibitInset } from 'phosphor-react';
|
|
5
|
+
import React, { cloneElement, ForwardedRef, forwardRef } from 'react';
|
|
6
|
+
|
|
7
|
+
import { AuthenticatingInvitationObservable } from '@dxos/client';
|
|
8
|
+
import { useSpace } from '@dxos/react-client';
|
|
9
|
+
import { Avatar, Button, getSize, Heading, mx, Trans, useId, useTranslation } from '@dxos/react-components';
|
|
10
|
+
|
|
11
|
+
import { subduedSurface } from '../../styles';
|
|
12
|
+
|
|
13
|
+
export interface JoinSpaceHeadingProps {
|
|
14
|
+
titleId: string;
|
|
15
|
+
invitation?: AuthenticatingInvitationObservable;
|
|
16
|
+
onExit?: () => void;
|
|
17
|
+
exitActionParent?: Parameters<typeof cloneElement>[0];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const JoinHeading = forwardRef(
|
|
21
|
+
({ titleId, invitation, onExit, exitActionParent }: JoinSpaceHeadingProps, ref: ForwardedRef<HTMLDivElement>) => {
|
|
22
|
+
const { t } = useTranslation('os');
|
|
23
|
+
|
|
24
|
+
const space = useSpace(invitation?.invitation?.spaceKey);
|
|
25
|
+
const spaceTitle = space?.properties.name ?? '(Space not available)';
|
|
26
|
+
|
|
27
|
+
const nameId = useId('spaceDisplayName');
|
|
28
|
+
|
|
29
|
+
const exitButton = (
|
|
30
|
+
<Button compact variant='ghost' {...(onExit && { onClick: onExit })} className='grow-0 shrink-0'>
|
|
31
|
+
<ProhibitInset className={getSize(5)} />
|
|
32
|
+
<span className='sr-only'>{t('exit label')}</span>
|
|
33
|
+
</Button>
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<div role='none' className={mx(subduedSurface, 'p-2 rounded-bs-md')} ref={ref}>
|
|
38
|
+
<div role='group' className='flex items-center gap-2'>
|
|
39
|
+
<Avatar fallbackValue={invitation?.invitation?.spaceKey?.toHex() ?? ''} labelId={nameId} />
|
|
40
|
+
<Heading level={1} className='font-body font-normal text-base grow' id={titleId}>
|
|
41
|
+
<Trans
|
|
42
|
+
{...{
|
|
43
|
+
defaults: t('join space heading'),
|
|
44
|
+
components: {
|
|
45
|
+
small: <span className='block leading-none mbe-1 font-system-medium text-sm' />,
|
|
46
|
+
large: <span className='block leading-none' id={nameId} />
|
|
47
|
+
},
|
|
48
|
+
values: { spaceTitle }
|
|
49
|
+
}}
|
|
50
|
+
/>
|
|
51
|
+
</Heading>
|
|
52
|
+
{exitActionParent ? cloneElement(exitActionParent, {}, exitButton) : exitButton}
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
);
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
import React, { useEffect, useReducer } from 'react';
|
|
5
|
+
|
|
6
|
+
import { InvitationEncoder } from '@dxos/client';
|
|
7
|
+
import { log } from '@dxos/log';
|
|
8
|
+
import { useClient, useIdentity } from '@dxos/react-client';
|
|
9
|
+
import { useId } from '@dxos/react-components';
|
|
10
|
+
|
|
11
|
+
import { JoinHeading } from './JoinHeading';
|
|
12
|
+
import { JoinAction, JoinPanelProps, JoinState } from './JoinPanelProps';
|
|
13
|
+
import {
|
|
14
|
+
AdditionMethodSelector,
|
|
15
|
+
IdentitySelector,
|
|
16
|
+
IdentityInput,
|
|
17
|
+
IdentityAdded,
|
|
18
|
+
InvitationAuthenticator,
|
|
19
|
+
InvitationRescuer,
|
|
20
|
+
InvitationInput,
|
|
21
|
+
InvitationAccepted
|
|
22
|
+
} from './view-states';
|
|
23
|
+
|
|
24
|
+
export const JoinPanel = ({
|
|
25
|
+
initialInvitationCode,
|
|
26
|
+
titleId: propsTitleId,
|
|
27
|
+
exitActionParent,
|
|
28
|
+
onExit,
|
|
29
|
+
doneActionParent,
|
|
30
|
+
onDone
|
|
31
|
+
}: JoinPanelProps) => {
|
|
32
|
+
const client = useClient();
|
|
33
|
+
const internalTitleId = useId('joinPanel__title');
|
|
34
|
+
const titleId = propsTitleId ?? internalTitleId;
|
|
35
|
+
const identity = useIdentity();
|
|
36
|
+
|
|
37
|
+
const availableIdentities = identity ? [identity] : [];
|
|
38
|
+
|
|
39
|
+
const reducer = (state: JoinState, action: JoinAction) => {
|
|
40
|
+
const nextState = { ...state };
|
|
41
|
+
switch (action.type) {
|
|
42
|
+
case 'added identity':
|
|
43
|
+
nextState.activeView = 'identity added';
|
|
44
|
+
nextState.selectedIdentity = action.identity;
|
|
45
|
+
break;
|
|
46
|
+
case 'add identity':
|
|
47
|
+
nextState.activeView = 'addition method selector';
|
|
48
|
+
break;
|
|
49
|
+
case 'select addition method':
|
|
50
|
+
if (action.method === 'accept device invitation') {
|
|
51
|
+
nextState.activeView = 'halo invitation acceptor';
|
|
52
|
+
if (state.unredeemedHaloInvitationCode) {
|
|
53
|
+
nextState.haloInvitation = client.echo.acceptInvitation(
|
|
54
|
+
InvitationEncoder.decode(state.unredeemedHaloInvitationCode)
|
|
55
|
+
);
|
|
56
|
+
nextState.unredeemedHaloInvitationCode = undefined;
|
|
57
|
+
nextState.haloViewState = 'invitation authenticator';
|
|
58
|
+
} else {
|
|
59
|
+
nextState.haloViewState = 'invitation input';
|
|
60
|
+
}
|
|
61
|
+
} else {
|
|
62
|
+
nextState.activeView = 'identity input';
|
|
63
|
+
nextState.additionMethod = action.method;
|
|
64
|
+
}
|
|
65
|
+
break;
|
|
66
|
+
case 'select identity':
|
|
67
|
+
nextState.selectedIdentity = action.identity;
|
|
68
|
+
nextState.activeView = 'space invitation acceptor';
|
|
69
|
+
if (state.unredeemedSpaceInvitationCode) {
|
|
70
|
+
nextState.spaceInvitation = client.echo.acceptInvitation(
|
|
71
|
+
InvitationEncoder.decode(state.unredeemedSpaceInvitationCode)
|
|
72
|
+
);
|
|
73
|
+
nextState.unredeemedSpaceInvitationCode = undefined;
|
|
74
|
+
nextState.spaceViewState = 'invitation authenticator';
|
|
75
|
+
} else {
|
|
76
|
+
nextState.spaceViewState = 'invitation input';
|
|
77
|
+
}
|
|
78
|
+
break;
|
|
79
|
+
case 'deselect identity':
|
|
80
|
+
nextState.selectedIdentity = undefined;
|
|
81
|
+
nextState.activeView = 'identity selector';
|
|
82
|
+
break;
|
|
83
|
+
case 'cancel invitation':
|
|
84
|
+
if (action.from === 'space' && state.spaceInvitation?.invitation) {
|
|
85
|
+
void state.spaceInvitation?.cancel();
|
|
86
|
+
}
|
|
87
|
+
if (action.from === 'halo' && state.haloInvitation?.invitation) {
|
|
88
|
+
void state.haloInvitation?.cancel();
|
|
89
|
+
}
|
|
90
|
+
break;
|
|
91
|
+
case 'authenticating invitation':
|
|
92
|
+
nextState[action.from === 'halo' ? 'haloInvitationAnnotation' : 'spaceInvitationAnnotation'] = 'authenticating';
|
|
93
|
+
break;
|
|
94
|
+
case 'connecting invitation':
|
|
95
|
+
nextState[action.from === 'halo' ? 'haloInvitation' : 'spaceInvitation'] = action.invitation;
|
|
96
|
+
// eslint-disable-next-line no-fallthrough
|
|
97
|
+
case 'cancelled invitation':
|
|
98
|
+
case 'fail invitation':
|
|
99
|
+
case 'timeout invitation':
|
|
100
|
+
nextState[action.from === 'halo' ? 'haloViewState' : 'spaceViewState'] = 'invitation rescuer';
|
|
101
|
+
break;
|
|
102
|
+
case 'connect invitation':
|
|
103
|
+
case 'authenticate invitation':
|
|
104
|
+
if (action.from === 'halo' && state.haloInvitationAnnotation === 'authenticating') {
|
|
105
|
+
nextState.haloInvitationAnnotation = 'authentication failed';
|
|
106
|
+
}
|
|
107
|
+
if (action.from === 'space' && state.spaceInvitationAnnotation === 'authenticating') {
|
|
108
|
+
nextState.spaceInvitationAnnotation = 'authentication failed';
|
|
109
|
+
}
|
|
110
|
+
nextState[action.from === 'halo' ? 'haloViewState' : 'spaceViewState'] = 'invitation authenticator';
|
|
111
|
+
break;
|
|
112
|
+
case 'accepted invitation':
|
|
113
|
+
nextState[action.from === 'halo' ? 'haloViewState' : 'spaceViewState'] = 'invitation accepted';
|
|
114
|
+
break;
|
|
115
|
+
case 'reset invitation':
|
|
116
|
+
nextState[action.from === 'halo' ? 'haloInvitation' : 'spaceInvitation'] = undefined;
|
|
117
|
+
nextState[action.from === 'halo' ? 'haloInvitationAnnotation' : 'spaceInvitationAnnotation'] = undefined;
|
|
118
|
+
nextState[action.from === 'halo' ? 'unredeemedHaloInvitationCode' : 'unredeemedSpaceInvitationCode'] =
|
|
119
|
+
undefined;
|
|
120
|
+
nextState[action.from === 'halo' ? 'haloViewState' : 'spaceViewState'] = 'invitation input';
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
log.debug('[join panel reducer]', { action, nextState });
|
|
124
|
+
return nextState;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const [joinState, dispatch] = useReducer(reducer, {
|
|
128
|
+
unredeemedHaloInvitationCode: undefined,
|
|
129
|
+
unredeemedSpaceInvitationCode: initialInvitationCode,
|
|
130
|
+
spaceInvitation: undefined,
|
|
131
|
+
haloInvitation: undefined,
|
|
132
|
+
activeView: availableIdentities.length > 0 ? 'identity selector' : 'addition method selector',
|
|
133
|
+
selectedIdentity: undefined,
|
|
134
|
+
additionMethod: undefined,
|
|
135
|
+
spaceViewState: 'invitation input',
|
|
136
|
+
haloViewState: 'invitation input'
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
useEffect(() => {
|
|
140
|
+
// TODO (thure): Validate if this is sufficiently synchronous for iOS to move focus. It might not be!
|
|
141
|
+
const attrValue =
|
|
142
|
+
joinState.activeView === 'identity input'
|
|
143
|
+
? `${joinState.activeView}; ${joinState.additionMethod}`
|
|
144
|
+
: joinState.activeView === 'space invitation acceptor'
|
|
145
|
+
? `${joinState.activeView}; ${joinState.spaceViewState}`
|
|
146
|
+
: joinState.activeView === 'halo invitation acceptor'
|
|
147
|
+
? `${joinState.activeView}; ${joinState.haloViewState}`
|
|
148
|
+
: joinState.activeView;
|
|
149
|
+
log.debug('[autofocus value]', { attrValue });
|
|
150
|
+
const $nextAutofocus: HTMLElement | null = document.querySelector(`[data-autofocus="${attrValue}"]`);
|
|
151
|
+
if ($nextAutofocus) {
|
|
152
|
+
$nextAutofocus.focus();
|
|
153
|
+
}
|
|
154
|
+
}, [joinState.activeView, joinState.spaceViewState, joinState.haloViewState]);
|
|
155
|
+
|
|
156
|
+
useEffect(() => {
|
|
157
|
+
return joinState.spaceInvitation?.subscribe({
|
|
158
|
+
onAuthenticating: () => dispatch({ type: 'authenticate invitation', from: 'space' }),
|
|
159
|
+
onCancelled: () => dispatch({ type: 'cancelled invitation', from: 'space' }),
|
|
160
|
+
onConnected: () => dispatch({ type: 'connect invitation', from: 'space' }),
|
|
161
|
+
onError: () => dispatch({ type: 'fail invitation', from: 'space' }),
|
|
162
|
+
onSuccess: () => dispatch({ type: 'accepted invitation', from: 'space' }),
|
|
163
|
+
onTimeout: () => dispatch({ type: 'timeout invitation', from: 'space' })
|
|
164
|
+
});
|
|
165
|
+
}, [joinState.spaceInvitation]);
|
|
166
|
+
|
|
167
|
+
useEffect(() => {
|
|
168
|
+
return joinState.haloInvitation?.subscribe({
|
|
169
|
+
onAuthenticating: () => dispatch({ type: 'authenticate invitation', from: 'halo' }),
|
|
170
|
+
onCancelled: () => dispatch({ type: 'cancelled invitation', from: 'halo' }),
|
|
171
|
+
onConnected: () => dispatch({ type: 'connect invitation', from: 'halo' }),
|
|
172
|
+
onError: () => dispatch({ type: 'fail invitation', from: 'halo' }),
|
|
173
|
+
onSuccess: () => dispatch({ type: 'accepted invitation', from: 'halo' }),
|
|
174
|
+
onTimeout: () => dispatch({ type: 'timeout invitation', from: 'halo' })
|
|
175
|
+
});
|
|
176
|
+
}, [joinState.haloInvitation]);
|
|
177
|
+
|
|
178
|
+
return (
|
|
179
|
+
<>
|
|
180
|
+
<JoinHeading {...{ titleId, invitation: joinState.spaceInvitation, onExit, exitActionParent }} />
|
|
181
|
+
<div role='none' className='is-full overflow-hidden'>
|
|
182
|
+
<div role='none' className='flex is-[1300%]' aria-live='polite'>
|
|
183
|
+
<IdentitySelector
|
|
184
|
+
{...{ dispatch, availableIdentities, active: joinState.activeView === 'identity selector' }}
|
|
185
|
+
/>
|
|
186
|
+
<AdditionMethodSelector
|
|
187
|
+
{...{
|
|
188
|
+
dispatch,
|
|
189
|
+
availableIdentities,
|
|
190
|
+
active: joinState.activeView === 'addition method selector'
|
|
191
|
+
}}
|
|
192
|
+
/>
|
|
193
|
+
<IdentityInput
|
|
194
|
+
{...{
|
|
195
|
+
dispatch,
|
|
196
|
+
active: joinState.activeView === 'identity input' && joinState.additionMethod === 'create identity',
|
|
197
|
+
method: 'create identity'
|
|
198
|
+
}}
|
|
199
|
+
/>
|
|
200
|
+
<IdentityInput
|
|
201
|
+
{...{
|
|
202
|
+
dispatch,
|
|
203
|
+
active: joinState.activeView === 'identity input' && joinState.additionMethod === 'recover identity',
|
|
204
|
+
method: 'recover identity'
|
|
205
|
+
}}
|
|
206
|
+
/>
|
|
207
|
+
<InvitationInput
|
|
208
|
+
{...{
|
|
209
|
+
dispatch,
|
|
210
|
+
activeInvitation: joinState.haloInvitation || true,
|
|
211
|
+
active:
|
|
212
|
+
joinState.activeView === 'halo invitation acceptor' && joinState.haloViewState === 'invitation input',
|
|
213
|
+
invitationType: 'halo'
|
|
214
|
+
}}
|
|
215
|
+
/>
|
|
216
|
+
<InvitationRescuer
|
|
217
|
+
{...{
|
|
218
|
+
dispatch,
|
|
219
|
+
activeInvitation: joinState.haloInvitation || true,
|
|
220
|
+
active:
|
|
221
|
+
joinState.activeView === 'halo invitation acceptor' && joinState.haloViewState === 'invitation rescuer',
|
|
222
|
+
invitationType: 'halo'
|
|
223
|
+
}}
|
|
224
|
+
/>
|
|
225
|
+
<InvitationAuthenticator
|
|
226
|
+
{...{
|
|
227
|
+
dispatch,
|
|
228
|
+
activeInvitation: joinState.haloInvitation || true,
|
|
229
|
+
active:
|
|
230
|
+
joinState.activeView === 'halo invitation acceptor' &&
|
|
231
|
+
joinState.haloViewState === 'invitation authenticator',
|
|
232
|
+
invitationType: 'halo',
|
|
233
|
+
...(joinState.spaceInvitationAnnotation === 'authentication failed' && { failed: true })
|
|
234
|
+
}}
|
|
235
|
+
/>
|
|
236
|
+
<InvitationAccepted
|
|
237
|
+
{...{
|
|
238
|
+
dispatch,
|
|
239
|
+
activeInvitation: joinState.haloInvitation || true,
|
|
240
|
+
active:
|
|
241
|
+
joinState.activeView === 'halo invitation acceptor' &&
|
|
242
|
+
joinState.haloViewState === 'invitation accepted',
|
|
243
|
+
invitationType: 'halo'
|
|
244
|
+
}}
|
|
245
|
+
/>
|
|
246
|
+
<IdentityAdded
|
|
247
|
+
{...{
|
|
248
|
+
dispatch,
|
|
249
|
+
addedIdentity: joinState.selectedIdentity,
|
|
250
|
+
active: joinState.activeView === 'identity added'
|
|
251
|
+
}}
|
|
252
|
+
/>
|
|
253
|
+
<InvitationInput
|
|
254
|
+
{...{
|
|
255
|
+
dispatch,
|
|
256
|
+
activeInvitation: joinState.spaceInvitation || true,
|
|
257
|
+
selectedIdentity: joinState.selectedIdentity,
|
|
258
|
+
active:
|
|
259
|
+
joinState.activeView === 'space invitation acceptor' && joinState.spaceViewState === 'invitation input',
|
|
260
|
+
invitationType: 'space'
|
|
261
|
+
}}
|
|
262
|
+
/>
|
|
263
|
+
<InvitationRescuer
|
|
264
|
+
{...{
|
|
265
|
+
dispatch,
|
|
266
|
+
activeInvitation: joinState.spaceInvitation || true,
|
|
267
|
+
selectedIdentity: joinState.selectedIdentity,
|
|
268
|
+
active:
|
|
269
|
+
joinState.activeView === 'space invitation acceptor' &&
|
|
270
|
+
joinState.spaceViewState === 'invitation rescuer',
|
|
271
|
+
invitationType: 'space'
|
|
272
|
+
}}
|
|
273
|
+
/>
|
|
274
|
+
<InvitationAuthenticator
|
|
275
|
+
{...{
|
|
276
|
+
dispatch,
|
|
277
|
+
activeInvitation: joinState.spaceInvitation || true,
|
|
278
|
+
selectedIdentity: joinState.selectedIdentity,
|
|
279
|
+
active:
|
|
280
|
+
joinState.activeView === 'space invitation acceptor' &&
|
|
281
|
+
joinState.spaceViewState === 'invitation authenticator',
|
|
282
|
+
invitationType: 'space',
|
|
283
|
+
...(joinState.spaceInvitationAnnotation === 'authentication failed' && { failed: true })
|
|
284
|
+
}}
|
|
285
|
+
/>
|
|
286
|
+
<InvitationAccepted
|
|
287
|
+
{...{
|
|
288
|
+
dispatch,
|
|
289
|
+
activeInvitation: joinState.spaceInvitation || true,
|
|
290
|
+
selectedIdentity: joinState.selectedIdentity,
|
|
291
|
+
active:
|
|
292
|
+
joinState.activeView === 'space invitation acceptor' &&
|
|
293
|
+
joinState.spaceViewState === 'invitation accepted',
|
|
294
|
+
invitationType: 'space',
|
|
295
|
+
doneActionParent,
|
|
296
|
+
onDone
|
|
297
|
+
}}
|
|
298
|
+
/>
|
|
299
|
+
</div>
|
|
300
|
+
</div>
|
|
301
|
+
</>
|
|
302
|
+
);
|
|
303
|
+
};
|