@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.
Files changed (147) hide show
  1. package/dist/lib/browser/index.mjs +1781 -57
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/types/src/components/{IdentityListItem → IdentityList}/IdentityListItem.d.ts +0 -0
  5. package/dist/types/src/components/IdentityList/IdentityListItem.d.ts.map +1 -0
  6. package/dist/types/src/components/{IdentityListItem → IdentityList}/IdentityListItem.stories.d.ts +0 -0
  7. package/dist/types/src/components/IdentityList/IdentityListItem.stories.d.ts.map +1 -0
  8. package/dist/types/src/components/IdentityList/SpaceMemberList.d.ts +6 -0
  9. package/dist/types/src/components/IdentityList/SpaceMemberList.d.ts.map +1 -0
  10. package/dist/types/src/components/IdentityList/SpaceMemberListContainer.d.ts +7 -0
  11. package/dist/types/src/components/IdentityList/SpaceMemberListContainer.d.ts.map +1 -0
  12. package/dist/types/src/components/IdentityList/index.d.ts +4 -0
  13. package/dist/types/src/components/IdentityList/index.d.ts.map +1 -0
  14. package/dist/types/src/components/InvitationList/InvitationList.d.ts +7 -0
  15. package/dist/types/src/components/InvitationList/InvitationList.d.ts.map +1 -0
  16. package/dist/types/src/components/InvitationList/InvitationList.stories.d.ts +10 -0
  17. package/dist/types/src/components/InvitationList/InvitationList.stories.d.ts.map +1 -0
  18. package/dist/types/src/components/InvitationList/InvitationListContainer.d.ts +7 -0
  19. package/dist/types/src/components/InvitationList/InvitationListContainer.d.ts.map +1 -0
  20. package/dist/types/src/components/InvitationList/InvitationListItem.d.ts +9 -0
  21. package/dist/types/src/components/InvitationList/InvitationListItem.d.ts.map +1 -0
  22. package/dist/types/src/components/InvitationList/InvitationListProps.d.ts +4 -0
  23. package/dist/types/src/components/InvitationList/InvitationListProps.d.ts.map +1 -0
  24. package/dist/types/src/components/InvitationList/InvitationStatusAvatar.d.ts +9 -0
  25. package/dist/types/src/components/InvitationList/InvitationStatusAvatar.d.ts.map +1 -0
  26. package/dist/types/src/components/InvitationList/InvitationStatusAvatar.stories.d.ts +9 -0
  27. package/dist/types/src/components/InvitationList/InvitationStatusAvatar.stories.d.ts.map +1 -0
  28. package/dist/types/src/components/InvitationList/index.d.ts +5 -0
  29. package/dist/types/src/components/InvitationList/index.d.ts.map +1 -0
  30. package/dist/types/src/components/PanelSeparator/PanelSeparator.d.ts +3 -0
  31. package/dist/types/src/components/PanelSeparator/PanelSeparator.d.ts.map +1 -0
  32. package/dist/types/src/components/PanelSeparator/index.d.ts +2 -0
  33. package/dist/types/src/components/PanelSeparator/index.d.ts.map +1 -0
  34. package/dist/types/src/components/index.d.ts +4 -0
  35. package/dist/types/src/components/index.d.ts.map +1 -0
  36. package/dist/types/src/composites/JoinDialog/JoinDialog.d.ts +6 -0
  37. package/dist/types/src/composites/JoinDialog/JoinDialog.d.ts.map +1 -0
  38. package/dist/types/src/composites/JoinDialog/JoinDialog.stories.d.ts +10 -0
  39. package/dist/types/src/composites/JoinDialog/JoinDialog.stories.d.ts.map +1 -0
  40. package/dist/types/src/composites/JoinDialog/index.d.ts +2 -0
  41. package/dist/types/src/composites/JoinDialog/index.d.ts.map +1 -0
  42. package/dist/types/src/composites/index.d.ts +1 -0
  43. package/dist/types/src/composites/index.d.ts.map +1 -1
  44. package/dist/types/src/index.d.ts +1 -0
  45. package/dist/types/src/index.d.ts.map +1 -1
  46. package/dist/types/src/layouts/PanelAlertDialog/PanelAlertDialog.d.ts +14 -0
  47. package/dist/types/src/layouts/PanelAlertDialog/PanelAlertDialog.d.ts.map +1 -0
  48. package/dist/types/src/layouts/PanelAlertDialog/index.d.ts +2 -0
  49. package/dist/types/src/layouts/PanelAlertDialog/index.d.ts.map +1 -0
  50. package/dist/types/src/layouts/PanelSidebar/PanelSidebar.d.ts +2 -3
  51. package/dist/types/src/layouts/PanelSidebar/PanelSidebar.d.ts.map +1 -1
  52. package/dist/types/src/layouts/index.d.ts +1 -0
  53. package/dist/types/src/layouts/index.d.ts.map +1 -1
  54. package/dist/types/src/panels/JoinPanel/JoinHeading.d.ts +10 -0
  55. package/dist/types/src/panels/JoinPanel/JoinHeading.d.ts.map +1 -0
  56. package/dist/types/src/panels/JoinPanel/JoinPanel.d.ts +3 -0
  57. package/dist/types/src/panels/JoinPanel/JoinPanel.d.ts.map +1 -0
  58. package/dist/types/src/panels/JoinPanel/JoinPanelProps.d.ts +53 -0
  59. package/dist/types/src/panels/JoinPanel/JoinPanelProps.d.ts.map +1 -0
  60. package/dist/types/src/panels/JoinPanel/index.d.ts +3 -0
  61. package/dist/types/src/panels/JoinPanel/index.d.ts.map +1 -0
  62. package/dist/types/src/panels/JoinPanel/view-states/AdditionMethodSelector.d.ts +7 -0
  63. package/dist/types/src/panels/JoinPanel/view-states/AdditionMethodSelector.d.ts.map +1 -0
  64. package/dist/types/src/panels/JoinPanel/view-states/IdentityAdded.d.ts +7 -0
  65. package/dist/types/src/panels/JoinPanel/view-states/IdentityAdded.d.ts.map +1 -0
  66. package/dist/types/src/panels/JoinPanel/view-states/IdentityInput.d.ts +6 -0
  67. package/dist/types/src/panels/JoinPanel/view-states/IdentityInput.d.ts.map +1 -0
  68. package/dist/types/src/panels/JoinPanel/view-states/IdentitySelector.d.ts +7 -0
  69. package/dist/types/src/panels/JoinPanel/view-states/IdentitySelector.d.ts.map +1 -0
  70. package/dist/types/src/panels/JoinPanel/view-states/InvitationAccepted.d.ts +10 -0
  71. package/dist/types/src/panels/JoinPanel/view-states/InvitationAccepted.d.ts.map +1 -0
  72. package/dist/types/src/panels/JoinPanel/view-states/InvitationAuthenticator.d.ts +7 -0
  73. package/dist/types/src/panels/JoinPanel/view-states/InvitationAuthenticator.d.ts.map +1 -0
  74. package/dist/types/src/panels/JoinPanel/view-states/InvitationInput.d.ts +6 -0
  75. package/dist/types/src/panels/JoinPanel/view-states/InvitationInput.d.ts.map +1 -0
  76. package/dist/types/src/panels/JoinPanel/view-states/InvitationRescuer.d.ts +6 -0
  77. package/dist/types/src/panels/JoinPanel/view-states/InvitationRescuer.d.ts.map +1 -0
  78. package/dist/types/src/panels/JoinPanel/view-states/ViewState.d.ts +12 -0
  79. package/dist/types/src/panels/JoinPanel/view-states/ViewState.d.ts.map +1 -0
  80. package/dist/types/src/panels/JoinPanel/view-states/index.d.ts +9 -0
  81. package/dist/types/src/panels/JoinPanel/view-states/index.d.ts.map +1 -0
  82. package/dist/types/src/panels/index.d.ts +1 -0
  83. package/dist/types/src/panels/index.d.ts.map +1 -1
  84. package/dist/types/src/styles/index.d.ts +1 -0
  85. package/dist/types/src/styles/index.d.ts.map +1 -1
  86. package/dist/types/src/styles/invitationStatusStyles.d.ts +20 -0
  87. package/dist/types/src/styles/invitationStatusStyles.d.ts.map +1 -0
  88. package/dist/types/src/styles/panelStyles.d.ts +2 -1
  89. package/dist/types/src/styles/panelStyles.d.ts.map +1 -1
  90. package/dist/types/src/translations/locales/en-US.d.ts +41 -0
  91. package/dist/types/src/translations/locales/en-US.d.ts.map +1 -1
  92. package/dist/types/src/util/index.d.ts +2 -0
  93. package/dist/types/src/util/index.d.ts.map +1 -0
  94. package/dist/types/src/util/invitationStatusValue.d.ts +8 -0
  95. package/dist/types/src/util/invitationStatusValue.d.ts.map +1 -0
  96. package/package.json +18 -11
  97. package/src/components/{IdentityListItem → IdentityList}/IdentityListItem.stories.tsx +0 -0
  98. package/src/components/{IdentityListItem → IdentityList}/IdentityListItem.tsx +4 -3
  99. package/src/components/IdentityList/SpaceMemberList.tsx +31 -0
  100. package/src/components/IdentityList/SpaceMemberListContainer.tsx +27 -0
  101. package/src/components/IdentityList/index.ts +7 -0
  102. package/src/components/InvitationList/InvitationList.stories.tsx +65 -0
  103. package/src/components/InvitationList/InvitationList.tsx +24 -0
  104. package/src/components/InvitationList/InvitationListContainer.tsx +27 -0
  105. package/src/components/InvitationList/InvitationListItem.tsx +95 -0
  106. package/src/components/InvitationList/InvitationListProps.ts +7 -0
  107. package/src/components/InvitationList/InvitationStatusAvatar.stories.tsx +52 -0
  108. package/src/components/InvitationList/InvitationStatusAvatar.tsx +75 -0
  109. package/src/components/InvitationList/index.ts +8 -0
  110. package/src/components/PanelSeparator/PanelSeparator.tsx +17 -0
  111. package/src/components/PanelSeparator/index.ts +5 -0
  112. package/src/components/index.ts +7 -0
  113. package/src/composites/JoinDialog/JoinDialog.stories.tsx +80 -0
  114. package/src/composites/JoinDialog/JoinDialog.tsx +31 -0
  115. package/src/composites/JoinDialog/index.ts +5 -0
  116. package/src/composites/index.ts +1 -0
  117. package/src/index.ts +1 -0
  118. package/src/layouts/PanelAlertDialog/PanelAlertDialog.tsx +44 -0
  119. package/src/layouts/PanelAlertDialog/index.ts +5 -0
  120. package/src/layouts/PanelPopover/PanelPopover.tsx +2 -2
  121. package/src/layouts/PanelSidebar/PanelSidebar.tsx +17 -25
  122. package/src/layouts/index.ts +1 -0
  123. package/src/panels/JoinPanel/JoinHeading.tsx +57 -0
  124. package/src/panels/JoinPanel/JoinPanel.tsx +303 -0
  125. package/src/panels/JoinPanel/JoinPanelProps.ts +94 -0
  126. package/src/panels/JoinPanel/index.ts +6 -0
  127. package/src/panels/JoinPanel/view-states/AdditionMethodSelector.tsx +69 -0
  128. package/src/panels/JoinPanel/view-states/IdentityAdded.tsx +60 -0
  129. package/src/panels/JoinPanel/view-states/IdentityInput.tsx +72 -0
  130. package/src/panels/JoinPanel/view-states/IdentitySelector.tsx +98 -0
  131. package/src/panels/JoinPanel/view-states/InvitationAccepted.tsx +69 -0
  132. package/src/panels/JoinPanel/view-states/InvitationAuthenticator.tsx +139 -0
  133. package/src/panels/JoinPanel/view-states/InvitationInput.tsx +68 -0
  134. package/src/panels/JoinPanel/view-states/InvitationRescuer.tsx +95 -0
  135. package/src/panels/JoinPanel/view-states/ViewState.tsx +204 -0
  136. package/src/panels/JoinPanel/view-states/index.ts +12 -0
  137. package/src/panels/index.ts +1 -0
  138. package/src/styles/index.ts +1 -0
  139. package/src/styles/invitationStatusStyles.ts +42 -0
  140. package/src/styles/panelStyles.ts +2 -1
  141. package/src/testing/ClientProvider.tsx +1 -1
  142. package/src/translations/locales/en-US.ts +42 -1
  143. package/src/util/index.ts +5 -0
  144. package/src/util/invitationStatusValue.ts +21 -0
  145. package/theme-extensions.js +11 -0
  146. package/dist/types/src/components/IdentityListItem/IdentityListItem.d.ts.map +0 -1
  147. package/dist/types/src/components/IdentityListItem/IdentityListItem.stories.d.ts.map +0 -1
@@ -2,5 +2,46 @@ export declare const os: {
2
2
  'manage profile label': string;
3
3
  'identity offline description': string;
4
4
  'sidebar label': string;
5
+ 'copy invitation code label': string;
6
+ 'open share panel label': string;
7
+ 'join space heading': string;
8
+ 'exit label': string;
9
+ 'identity selector title': string;
10
+ 'identity radio group title': string;
11
+ 'add identity label': string;
12
+ 'continue label': string;
13
+ 'back label': string;
14
+ 'next label': string;
15
+ 'auth choices label': string;
16
+ 'create identity label': string;
17
+ 'create identity description': string;
18
+ 'recover identity label': string;
19
+ 'recover identity description': string;
20
+ 'join identity label': string;
21
+ 'join identity description': string;
22
+ 'deselect identity label': string;
23
+ 'addition method selector title': string;
24
+ 'new identity input label': string;
25
+ 'recover identity input label': string;
26
+ 'failed to create identity message': string;
27
+ 'failed to recover identity message': string;
28
+ 'failed to authenticate message': string;
29
+ 'identity added label': string;
30
+ 'join space as identity heading': string;
31
+ 'error status label': string;
32
+ 'timeout status label': string;
33
+ 'cancelled status label': string;
34
+ 'init status label': string;
35
+ 'connecting status label': string;
36
+ 'connected status label': string;
37
+ 'authenticating status label': string;
38
+ 'success status label': string;
39
+ 'connect label': string;
40
+ 'reconnect label': string;
41
+ 'cancel label': string;
42
+ 'done label': string;
43
+ 'reset label': string;
44
+ 'pin input label': string;
45
+ 'invitation input label': string;
5
46
  };
6
47
  //# sourceMappingURL=en-US.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"en-US.d.ts","sourceRoot":"","sources":["../../../../../src/translations/locales/en-US.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,EAAE;;;;CAId,CAAC"}
1
+ {"version":3,"file":"en-US.d.ts","sourceRoot":"","sources":["../../../../../src/translations/locales/en-US.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6Cd,CAAC"}
@@ -0,0 +1,2 @@
1
+ export * from './invitationStatusValue';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/util/index.ts"],"names":[],"mappings":"AAIA,cAAc,yBAAyB,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { Invitation } from '@dxos/client';
2
+ /**
3
+ * This map assigns a numeric value to invitation states to facilitate
4
+ * more terse code which uses combinations of `>`, `<`, and `=` instead of
5
+ * longer sequences of `===` and `||` predicates.
6
+ */
7
+ export declare const invitationStatusValue: Map<Invitation.State, number>;
8
+ //# sourceMappingURL=invitationStatusValue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invitationStatusValue.d.ts","sourceRoot":"","sources":["../../../../src/util/invitationStatusValue.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,+BAShC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/react-ui",
3
- "version": "0.1.23",
3
+ "version": "0.1.24",
4
4
  "description": "A React component library for DXOS’s OS experiences",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -10,20 +10,25 @@
10
10
  "types": "dist/types/src/index.d.ts",
11
11
  "files": [
12
12
  "dist",
13
- "src"
13
+ "src",
14
+ "theme-extensions.js"
14
15
  ],
15
16
  "dependencies": {
17
+ "@radix-ui/react-accordion": "^1.0.1",
18
+ "@radix-ui/react-alert-dialog": "^1.0.2",
16
19
  "@radix-ui/react-dialog": "^1.0.0",
20
+ "@radix-ui/react-radio-group": "^1.1.1",
17
21
  "@react-hook/async": "^3.1.1",
18
22
  "debug": "^4.3.3",
23
+ "qrcode.react": "^3.1.0",
19
24
  "url-join": "^5.0.0",
20
- "@dxos/client": "0.1.23",
21
- "@dxos/config": "0.1.23",
22
- "@dxos/debug": "0.1.23",
23
- "@dxos/keys": "0.1.23",
24
- "@dxos/react-client": "0.1.23",
25
- "@dxos/react-components": "0.1.23",
26
- "@dxos/util": "0.1.23"
25
+ "@dxos/client": "0.1.24",
26
+ "@dxos/config": "0.1.24",
27
+ "@dxos/debug": "0.1.24",
28
+ "@dxos/keys": "0.1.24",
29
+ "@dxos/react-client": "0.1.24",
30
+ "@dxos/react-components": "0.1.24",
31
+ "@dxos/util": "0.1.24"
27
32
  },
28
33
  "devDependencies": {
29
34
  "@types/debug": "^4.1.7",
@@ -33,8 +38,10 @@
33
38
  "phosphor-react": "^1.4.1",
34
39
  "react": "^18.2.0",
35
40
  "react-dom": "^18.2.0",
36
- "storybook-dark-mode": "^1.1.2",
37
- "vite": "4.0.4"
41
+ "vite": "4.0.4",
42
+ "@dxos/async": "0.1.24",
43
+ "@dxos/log": "0.1.24",
44
+ "@dxos/react-async": "0.1.24"
38
45
  },
39
46
  "peerDependencies": {
40
47
  "phosphor-react": "^1.4.1",
@@ -9,12 +9,13 @@ import { Avatar, useTranslation } from '@dxos/react-components';
9
9
  export const IdentityListItem = ({ identity, presence }: { identity: Profile; presence?: SpaceMember['presence'] }) => {
10
10
  const { t } = useTranslation('os');
11
11
  return (
12
- <li className='flex gap-2'>
12
+ <li className='flex gap-2 items-center'>
13
13
  <Avatar
14
14
  {...{
15
+ variant: 'circle',
15
16
  size: 9,
16
17
  fallbackValue: identity.identityKey.toHex(),
17
- label: <p className='text-sm'>{identity.displayName ?? identity.identityKey.truncate()}</p>,
18
+ label: <p className='text-sm truncate'>{identity.displayName ?? identity.identityKey.truncate()}</p>,
18
19
  ...(presence === SpaceMember.PresenceState.OFFLINE && {
19
20
  status: 'inactive',
20
21
  description: (
@@ -24,7 +25,7 @@ export const IdentityListItem = ({ identity, presence }: { identity: Profile; pr
24
25
  )
25
26
  }),
26
27
  ...(presence === SpaceMember.PresenceState.ONLINE && { status: 'active' }),
27
- slots: { labels: { className: 'block' } }
28
+ slots: { labels: { className: 'block shrink overflow-hidden' }, root: { className: 'shrink-0' } }
28
29
  }}
29
30
  />
30
31
  </li>
@@ -0,0 +1,31 @@
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
+ spaceMembers: SpaceMember[];
13
+ }
14
+
15
+ export const SpaceMemberList = ({ spaceMembers }: SpaceMemberListProps) => {
16
+ return (
17
+ <ul className='flex flex-col gap-2'>
18
+ {spaceMembers
19
+ .filter((spaceMember) => spaceMember.profile)
20
+ .map((spaceMember) => {
21
+ return (
22
+ <IdentityListItem
23
+ key={spaceMember.identityKey.toHex()}
24
+ identity={spaceMember.profile!}
25
+ presence={spaceMember.presence}
26
+ />
27
+ );
28
+ })}
29
+ </ul>
30
+ );
31
+ };
@@ -0,0 +1,27 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+ import React, { useMemo } from 'react';
5
+
6
+ import { PublicKey } from '@dxos/keys';
7
+ import { useClient, useMembers } from '@dxos/react-client';
8
+
9
+ import { SpaceMemberList } from './SpaceMemberList';
10
+
11
+ export interface SpaceMemberListContainerProps {
12
+ spaceKey: PublicKey;
13
+ includeSelf?: boolean;
14
+ }
15
+
16
+ export const SpaceMemberListContainer = ({ spaceKey, includeSelf }: SpaceMemberListContainerProps) => {
17
+ const client = useClient();
18
+ const allUnsortedMembers = useMembers(spaceKey);
19
+ const spaceMembers = useMemo(
20
+ () =>
21
+ includeSelf
22
+ ? allUnsortedMembers.sort((a) => (a.identityKey.equals(client.halo.profile!.identityKey) ? -1 : 1))
23
+ : allUnsortedMembers.filter((member) => !member.identityKey.equals(client.halo.profile!.identityKey)),
24
+ [allUnsortedMembers]
25
+ );
26
+ return <SpaceMemberList spaceMembers={spaceMembers} />;
27
+ };
@@ -0,0 +1,7 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ export * from './IdentityListItem';
6
+ export * from './SpaceMemberList';
7
+ export * from './SpaceMemberListContainer';
@@ -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,24 @@
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
+
9
+ import { InvitationListItem, InvitationListItemProps } from './InvitationListItem';
10
+
11
+ export interface InvitationListProps extends Omit<InvitationListItemProps, 'invitation' | 'value'> {
12
+ invitations: CancellableInvitationObservable[];
13
+ }
14
+
15
+ export const InvitationList = ({ invitations, ...invitationProps }: InvitationListProps) => {
16
+ return (
17
+ <AccordionRoot type='single' collapsible>
18
+ {invitations.map((invitation, index) => {
19
+ const value = invitation.invitation?.invitationId ?? `inv_${index}`;
20
+ return <InvitationListItem key={value} value={value} invitation={invitation} {...invitationProps} />;
21
+ })}
22
+ </AccordionRoot>
23
+ );
24
+ };
@@ -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,95 @@
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, 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
+
31
+ const { cancel, status, haltedAt, invitationCode, authenticationCode } = useInvitationStatus(invitation);
32
+ const statusValue = invitationStatusValue.get(status) ?? 0;
33
+
34
+ const isCancellable = statusValue < 4 && statusValue >= 0;
35
+ const showShare = statusValue < 3 && statusValue >= 0;
36
+ const showPin = statusValue === 3;
37
+
38
+ const handleClickRemove = useCallback(() => onClickRemove(invitation), [invitation, onClickRemove]);
39
+
40
+ const invitationUrl = invitationCode && createInvitationUrl(invitationCode);
41
+ const handleCLickCopy = useCallback(() => {
42
+ if (invitationUrl) {
43
+ void navigator.clipboard.writeText(invitationUrl);
44
+ }
45
+ }, [invitationUrl]);
46
+
47
+ return (
48
+ <AccordionPrimitive.Item value={value}>
49
+ <AccordionPrimitive.Header className='flex gap-2 items-center'>
50
+ <InvitationStatusAvatar {...{ status, haltedAt }} />
51
+ {showShare && invitationUrl ? (
52
+ <AccordionPrimitive.Trigger asChild>
53
+ <Button className='grow flex gap-1'>
54
+ <span>{t('open share panel label')}</span>
55
+ <QrCode className={getSize(4)} weight='bold' />
56
+ </Button>
57
+ </AccordionPrimitive.Trigger>
58
+ ) : showPin ? (
59
+ <p className='grow text-xl text-center text-success-500 dark:text-success-300 font-mono'>
60
+ {authenticationCode}
61
+ </p>
62
+ ) : (
63
+ <span role='none' className='grow' />
64
+ )}
65
+ {isCancellable ? (
66
+ <Button variant='ghost' compact className='flex gap-1' onClick={cancel}>
67
+ <span className='sr-only'>{t('cancel invitation label')}</span>
68
+ <ProhibitInset className={getSize(4)} weight='bold' />
69
+ </Button>
70
+ ) : (
71
+ <Button variant='ghost' compact className='flex gap-1' onClick={handleClickRemove}>
72
+ <span className='sr-only'>{t('remove invitation label')}</span>
73
+ <X className={getSize(4)} weight='bold' />
74
+ </Button>
75
+ )}
76
+ </AccordionPrimitive.Header>
77
+ {showShare && invitationUrl && (
78
+ <AccordionPrimitive.Content>
79
+ <Button className='flex gap-2 is-full p-2' onClick={handleCLickCopy}>
80
+ <QRCodeSVG
81
+ bgColor='transparent'
82
+ fgColor='currentColor'
83
+ value={invitationUrl}
84
+ className={mx('grow aspect-square is-24 bs-auto')}
85
+ />
86
+ <span className='pli-1'>
87
+ {t('copy invitation code label')}
88
+ <Copy className={mx(getSize(4), 'inline mis-1')} weight='bold' />
89
+ </span>
90
+ </Button>
91
+ </AccordionPrimitive.Content>
92
+ )}
93
+ </AccordionPrimitive.Item>
94
+ );
95
+ };
@@ -0,0 +1,7 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ export interface SharedInvitationListProps {
6
+ createInvitationUrl: (invitationCode: string) => string;
7
+ }
@@ -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,75 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import React, { ComponentPropsWithoutRef } from 'react';
6
+
7
+ import { Invitation } from '@dxos/client';
8
+ import { getSize, Size } from '@dxos/react-components';
9
+
10
+ import { inactiveStrokeColor, activeStrokeColor, resolvedStrokeColor } from '../../styles';
11
+ import { invitationStatusValue } from '../../util';
12
+
13
+ export interface InvitationStatusAvatarProps {
14
+ size?: Size;
15
+ status: Invitation.State;
16
+ haltedAt?: Invitation.State;
17
+ }
18
+
19
+ const svgSize = 32;
20
+ const strokeWidth = 4;
21
+ const radius = (svgSize - strokeWidth) / 2;
22
+ const circumference = Math.PI * 2 * radius;
23
+ const gap = circumference / 12;
24
+ const nSegments = 3;
25
+ const segment = circumference / nSegments;
26
+ const baseOffset = (2 * circumference) / 3 - (segment - gap) / 2;
27
+
28
+ const circleProps: ComponentPropsWithoutRef<'circle'> = {
29
+ fill: 'none',
30
+ strokeLinecap: 'round',
31
+ cx: svgSize / 2,
32
+ cy: svgSize / 2,
33
+ r: radius,
34
+ strokeWidth: 4,
35
+ strokeDasharray: `${segment - gap} ${2 * segment + gap}`
36
+ };
37
+
38
+ export const InvitationStatusAvatar = ({ size = 10, status, haltedAt }: InvitationStatusAvatarProps) => {
39
+ const resolvedColor = resolvedStrokeColor(status);
40
+ const halted =
41
+ status === Invitation.State.CANCELLED || status === Invitation.State.TIMEOUT || status === Invitation.State.ERROR;
42
+ const cursor = invitationStatusValue.get(halted ? haltedAt! : status)!;
43
+ return (
44
+ <svg viewBox={`0 0 ${svgSize} ${svgSize}`} className={getSize(size)}>
45
+ {[...Array(nSegments)].map((_, index) => (
46
+ <circle
47
+ key={index}
48
+ {...circleProps}
49
+ strokeDashoffset={index * segment + baseOffset}
50
+ className={
51
+ index === 0
52
+ ? cursor === 1
53
+ ? halted
54
+ ? resolvedColor
55
+ : activeStrokeColor
56
+ : cursor > 1
57
+ ? resolvedColor
58
+ : inactiveStrokeColor
59
+ : index === 1
60
+ ? cursor === 3
61
+ ? halted
62
+ ? resolvedColor
63
+ : activeStrokeColor
64
+ : cursor > 3
65
+ ? resolvedColor
66
+ : inactiveStrokeColor
67
+ : cursor > 3
68
+ ? resolvedColor
69
+ : inactiveStrokeColor
70
+ }
71
+ />
72
+ ))}
73
+ </svg>
74
+ );
75
+ };
@@ -0,0 +1,8 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ export * from './InvitationStatusAvatar';
6
+ export * from './InvitationListItem';
7
+ export * from './InvitationListContainer';
8
+ export * from './InvitationList';
@@ -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,5 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ export * from './PanelSeparator';
@@ -0,0 +1,7 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ export * from './IdentityList';
6
+ export * from './InvitationList';
7
+ export * from './PanelSeparator';