@dxos/react-ui 0.1.22 → 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.
Files changed (199) hide show
  1. package/dist/lib/browser/index.mjs +2434 -77
  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/IdentityList/DeviceList.d.ts +7 -0
  5. package/dist/types/src/components/IdentityList/DeviceList.d.ts.map +1 -0
  6. package/dist/types/src/components/IdentityList/IdentityListItem.d.ts +7 -0
  7. package/dist/types/src/components/IdentityList/IdentityListItem.d.ts.map +1 -0
  8. package/dist/types/src/components/{IdentityListItem → IdentityList}/IdentityListItem.stories.d.ts +2 -1
  9. package/dist/types/src/components/IdentityList/IdentityListItem.stories.d.ts.map +1 -0
  10. package/dist/types/src/components/IdentityList/SpaceMemberList.d.ts +7 -0
  11. package/dist/types/src/components/IdentityList/SpaceMemberList.d.ts.map +1 -0
  12. package/dist/types/src/components/IdentityList/SpaceMemberListContainer.d.ts +9 -0
  13. package/dist/types/src/components/IdentityList/SpaceMemberListContainer.d.ts.map +1 -0
  14. package/dist/types/src/components/IdentityList/index.d.ts +5 -0
  15. package/dist/types/src/components/IdentityList/index.d.ts.map +1 -0
  16. package/dist/types/src/components/InvitationList/InvitationList.d.ts +7 -0
  17. package/dist/types/src/components/InvitationList/InvitationList.d.ts.map +1 -0
  18. package/dist/types/src/components/InvitationList/InvitationList.stories.d.ts +10 -0
  19. package/dist/types/src/components/InvitationList/InvitationList.stories.d.ts.map +1 -0
  20. package/dist/types/src/components/InvitationList/InvitationListContainer.d.ts +7 -0
  21. package/dist/types/src/components/InvitationList/InvitationListContainer.d.ts.map +1 -0
  22. package/dist/types/src/components/InvitationList/InvitationListItem.d.ts +9 -0
  23. package/dist/types/src/components/InvitationList/InvitationListItem.d.ts.map +1 -0
  24. package/dist/types/src/components/InvitationList/InvitationListProps.d.ts +4 -0
  25. package/dist/types/src/components/InvitationList/InvitationListProps.d.ts.map +1 -0
  26. package/dist/types/src/components/InvitationList/InvitationStatusAvatar.d.ts +14 -0
  27. package/dist/types/src/components/InvitationList/InvitationStatusAvatar.d.ts.map +1 -0
  28. package/dist/types/src/components/InvitationList/InvitationStatusAvatar.stories.d.ts +9 -0
  29. package/dist/types/src/components/InvitationList/InvitationStatusAvatar.stories.d.ts.map +1 -0
  30. package/dist/types/src/components/InvitationList/index.d.ts +5 -0
  31. package/dist/types/src/components/InvitationList/index.d.ts.map +1 -0
  32. package/dist/types/src/components/PanelSeparator/PanelSeparator.d.ts +3 -0
  33. package/dist/types/src/components/PanelSeparator/PanelSeparator.d.ts.map +1 -0
  34. package/dist/types/src/components/PanelSeparator/index.d.ts +2 -0
  35. package/dist/types/src/components/PanelSeparator/index.d.ts.map +1 -0
  36. package/dist/types/src/components/SpaceList/SpaceListItem.d.ts +7 -0
  37. package/dist/types/src/components/SpaceList/SpaceListItem.d.ts.map +1 -0
  38. package/dist/types/src/components/SpaceList/index.d.ts +2 -0
  39. package/dist/types/src/components/SpaceList/index.d.ts.map +1 -0
  40. package/dist/types/src/components/index.d.ts +5 -0
  41. package/dist/types/src/components/index.d.ts.map +1 -0
  42. package/dist/types/src/composites/DevicesDialog/DevicesDialog.d.ts +6 -0
  43. package/dist/types/src/composites/DevicesDialog/DevicesDialog.d.ts.map +1 -0
  44. package/dist/types/src/composites/DevicesDialog/index.d.ts +2 -0
  45. package/dist/types/src/composites/DevicesDialog/index.d.ts.map +1 -0
  46. package/dist/types/src/composites/JoinDialog/JoinDialog.d.ts +6 -0
  47. package/dist/types/src/composites/JoinDialog/JoinDialog.d.ts.map +1 -0
  48. package/dist/types/src/composites/JoinDialog/JoinDialog.stories.d.ts +10 -0
  49. package/dist/types/src/composites/JoinDialog/JoinDialog.stories.d.ts.map +1 -0
  50. package/dist/types/src/composites/JoinDialog/index.d.ts +2 -0
  51. package/dist/types/src/composites/JoinDialog/index.d.ts.map +1 -0
  52. package/dist/types/src/composites/Shell/Shell.d.ts +6 -0
  53. package/dist/types/src/composites/Shell/Shell.d.ts.map +1 -0
  54. package/dist/types/src/composites/Shell/index.d.ts +2 -0
  55. package/dist/types/src/composites/Shell/index.d.ts.map +1 -0
  56. package/dist/types/src/composites/SpaceDialog/SpaceDialog.d.ts +6 -0
  57. package/dist/types/src/composites/SpaceDialog/SpaceDialog.d.ts.map +1 -0
  58. package/dist/types/src/composites/SpaceDialog/index.d.ts +2 -0
  59. package/dist/types/src/composites/SpaceDialog/index.d.ts.map +1 -0
  60. package/dist/types/src/composites/index.d.ts +3 -0
  61. package/dist/types/src/composites/index.d.ts.map +1 -1
  62. package/dist/types/src/index.d.ts +1 -0
  63. package/dist/types/src/index.d.ts.map +1 -1
  64. package/dist/types/src/layouts/PanelAlertDialog/PanelAlertDialog.d.ts +14 -0
  65. package/dist/types/src/layouts/PanelAlertDialog/PanelAlertDialog.d.ts.map +1 -0
  66. package/dist/types/src/layouts/PanelAlertDialog/index.d.ts +2 -0
  67. package/dist/types/src/layouts/PanelAlertDialog/index.d.ts.map +1 -0
  68. package/dist/types/src/layouts/PanelDialog/PanelDialog.d.ts +14 -0
  69. package/dist/types/src/layouts/PanelDialog/PanelDialog.d.ts.map +1 -0
  70. package/dist/types/src/layouts/PanelDialog/index.d.ts +2 -0
  71. package/dist/types/src/layouts/PanelDialog/index.d.ts.map +1 -0
  72. package/dist/types/src/layouts/PanelSidebar/PanelSidebar.d.ts +4 -5
  73. package/dist/types/src/layouts/PanelSidebar/PanelSidebar.d.ts.map +1 -1
  74. package/dist/types/src/layouts/index.d.ts +2 -0
  75. package/dist/types/src/layouts/index.d.ts.map +1 -1
  76. package/dist/types/src/panels/DevicesPanel/DevicesPanel.d.ts +10 -0
  77. package/dist/types/src/panels/DevicesPanel/DevicesPanel.d.ts.map +1 -0
  78. package/dist/types/src/panels/DevicesPanel/index.d.ts +2 -0
  79. package/dist/types/src/panels/DevicesPanel/index.d.ts.map +1 -0
  80. package/dist/types/src/panels/JoinPanel/JoinHeading.d.ts +13 -0
  81. package/dist/types/src/panels/JoinPanel/JoinHeading.d.ts.map +1 -0
  82. package/dist/types/src/panels/JoinPanel/JoinPanel.d.ts +3 -0
  83. package/dist/types/src/panels/JoinPanel/JoinPanel.d.ts.map +1 -0
  84. package/dist/types/src/panels/JoinPanel/JoinPanelProps.d.ts +54 -0
  85. package/dist/types/src/panels/JoinPanel/JoinPanelProps.d.ts.map +1 -0
  86. package/dist/types/src/panels/JoinPanel/index.d.ts +3 -0
  87. package/dist/types/src/panels/JoinPanel/index.d.ts.map +1 -0
  88. package/dist/types/src/panels/JoinPanel/view-states/AdditionMethodSelector.d.ts +7 -0
  89. package/dist/types/src/panels/JoinPanel/view-states/AdditionMethodSelector.d.ts.map +1 -0
  90. package/dist/types/src/panels/JoinPanel/view-states/IdentityAdded.d.ts +15 -0
  91. package/dist/types/src/panels/JoinPanel/view-states/IdentityAdded.d.ts.map +1 -0
  92. package/dist/types/src/panels/JoinPanel/view-states/IdentityInput.d.ts +6 -0
  93. package/dist/types/src/panels/JoinPanel/view-states/IdentityInput.d.ts.map +1 -0
  94. package/dist/types/src/panels/JoinPanel/view-states/IdentitySelector.d.ts +7 -0
  95. package/dist/types/src/panels/JoinPanel/view-states/IdentitySelector.d.ts.map +1 -0
  96. package/dist/types/src/panels/JoinPanel/view-states/InvitationAccepted.d.ts +10 -0
  97. package/dist/types/src/panels/JoinPanel/view-states/InvitationAccepted.d.ts.map +1 -0
  98. package/dist/types/src/panels/JoinPanel/view-states/InvitationAuthenticator.d.ts +7 -0
  99. package/dist/types/src/panels/JoinPanel/view-states/InvitationAuthenticator.d.ts.map +1 -0
  100. package/dist/types/src/panels/JoinPanel/view-states/InvitationInput.d.ts +6 -0
  101. package/dist/types/src/panels/JoinPanel/view-states/InvitationInput.d.ts.map +1 -0
  102. package/dist/types/src/panels/JoinPanel/view-states/InvitationRescuer.d.ts +6 -0
  103. package/dist/types/src/panels/JoinPanel/view-states/InvitationRescuer.d.ts.map +1 -0
  104. package/dist/types/src/panels/JoinPanel/view-states/ViewState.d.ts +12 -0
  105. package/dist/types/src/panels/JoinPanel/view-states/ViewState.d.ts.map +1 -0
  106. package/dist/types/src/panels/JoinPanel/view-states/index.d.ts +9 -0
  107. package/dist/types/src/panels/JoinPanel/view-states/index.d.ts.map +1 -0
  108. package/dist/types/src/panels/SpacePanel/SpacePanel.d.ts +18 -0
  109. package/dist/types/src/panels/SpacePanel/SpacePanel.d.ts.map +1 -0
  110. package/dist/types/src/panels/SpacePanel/SpacePanel.stories.d.ts +9 -0
  111. package/dist/types/src/panels/SpacePanel/SpacePanel.stories.d.ts.map +1 -0
  112. package/dist/types/src/panels/SpacePanel/index.d.ts +2 -0
  113. package/dist/types/src/panels/SpacePanel/index.d.ts.map +1 -0
  114. package/dist/types/src/panels/index.d.ts +3 -0
  115. package/dist/types/src/panels/index.d.ts.map +1 -1
  116. package/dist/types/src/styles/dialogStyles.d.ts +3 -0
  117. package/dist/types/src/styles/dialogStyles.d.ts.map +1 -0
  118. package/dist/types/src/styles/index.d.ts +2 -0
  119. package/dist/types/src/styles/index.d.ts.map +1 -1
  120. package/dist/types/src/styles/invitationStatusStyles.d.ts +20 -0
  121. package/dist/types/src/styles/invitationStatusStyles.d.ts.map +1 -0
  122. package/dist/types/src/styles/panelStyles.d.ts +2 -1
  123. package/dist/types/src/styles/panelStyles.d.ts.map +1 -1
  124. package/dist/types/src/translations/locales/en-US.d.ts +52 -0
  125. package/dist/types/src/translations/locales/en-US.d.ts.map +1 -1
  126. package/dist/types/src/util/index.d.ts +2 -0
  127. package/dist/types/src/util/index.d.ts.map +1 -0
  128. package/dist/types/src/util/invitationStatusValue.d.ts +8 -0
  129. package/dist/types/src/util/invitationStatusValue.d.ts.map +1 -0
  130. package/package.json +19 -11
  131. package/src/components/IdentityList/DeviceList.tsx +31 -0
  132. package/src/components/{IdentityListItem → IdentityList}/IdentityListItem.stories.tsx +0 -0
  133. package/src/components/{IdentityListItem → IdentityList}/IdentityListItem.tsx +18 -5
  134. package/src/components/IdentityList/SpaceMemberList.tsx +33 -0
  135. package/src/components/IdentityList/SpaceMemberListContainer.tsx +28 -0
  136. package/src/components/IdentityList/index.ts +8 -0
  137. package/src/components/InvitationList/InvitationList.stories.tsx +65 -0
  138. package/src/components/InvitationList/InvitationList.tsx +28 -0
  139. package/src/components/InvitationList/InvitationListContainer.tsx +27 -0
  140. package/src/components/InvitationList/InvitationListItem.tsx +106 -0
  141. package/src/components/InvitationList/InvitationListProps.ts +7 -0
  142. package/src/components/InvitationList/InvitationStatusAvatar.stories.tsx +52 -0
  143. package/src/components/InvitationList/InvitationStatusAvatar.tsx +80 -0
  144. package/src/components/InvitationList/index.ts +8 -0
  145. package/src/components/PanelSeparator/PanelSeparator.tsx +17 -0
  146. package/src/components/PanelSeparator/index.ts +5 -0
  147. package/src/components/SpaceList/SpaceListItem.tsx +32 -0
  148. package/src/components/SpaceList/index.ts +5 -0
  149. package/src/components/index.ts +8 -0
  150. package/src/composites/DevicesDialog/DevicesDialog.tsx +38 -0
  151. package/src/composites/DevicesDialog/index.ts +5 -0
  152. package/src/composites/JoinDialog/JoinDialog.stories.tsx +81 -0
  153. package/src/composites/JoinDialog/JoinDialog.tsx +33 -0
  154. package/src/composites/JoinDialog/index.ts +5 -0
  155. package/src/composites/Shell/Shell.tsx +92 -0
  156. package/src/composites/Shell/index.ts +5 -0
  157. package/src/composites/SpaceDialog/SpaceDialog.tsx +38 -0
  158. package/src/composites/SpaceDialog/index.ts +5 -0
  159. package/src/composites/index.ts +4 -0
  160. package/src/index.ts +1 -0
  161. package/src/layouts/PanelAlertDialog/PanelAlertDialog.tsx +40 -0
  162. package/src/layouts/PanelAlertDialog/index.ts +5 -0
  163. package/src/layouts/PanelDialog/PanelDialog.tsx +39 -0
  164. package/src/layouts/PanelDialog/index.ts +5 -0
  165. package/src/layouts/PanelPopover/PanelPopover.tsx +2 -2
  166. package/src/layouts/PanelSidebar/PanelSidebar.tsx +27 -31
  167. package/src/layouts/index.ts +2 -0
  168. package/src/panels/DevicesPanel/DevicesPanel.tsx +98 -0
  169. package/src/panels/DevicesPanel/index.ts +5 -0
  170. package/src/panels/JoinPanel/JoinHeading.tsx +85 -0
  171. package/src/panels/JoinPanel/JoinPanel.tsx +319 -0
  172. package/src/panels/JoinPanel/JoinPanelProps.ts +90 -0
  173. package/src/panels/JoinPanel/index.ts +6 -0
  174. package/src/panels/JoinPanel/view-states/AdditionMethodSelector.tsx +73 -0
  175. package/src/panels/JoinPanel/view-states/IdentityAdded.tsx +99 -0
  176. package/src/panels/JoinPanel/view-states/IdentityInput.tsx +79 -0
  177. package/src/panels/JoinPanel/view-states/IdentitySelector.tsx +101 -0
  178. package/src/panels/JoinPanel/view-states/InvitationAccepted.tsx +70 -0
  179. package/src/panels/JoinPanel/view-states/InvitationAuthenticator.tsx +142 -0
  180. package/src/panels/JoinPanel/view-states/InvitationInput.tsx +75 -0
  181. package/src/panels/JoinPanel/view-states/InvitationRescuer.tsx +98 -0
  182. package/src/panels/JoinPanel/view-states/ViewState.tsx +204 -0
  183. package/src/panels/JoinPanel/view-states/index.ts +12 -0
  184. package/src/panels/SpacePanel/SpacePanel.stories.tsx +53 -0
  185. package/src/panels/SpacePanel/SpacePanel.tsx +174 -0
  186. package/src/panels/SpacePanel/index.ts +5 -0
  187. package/src/panels/index.ts +3 -0
  188. package/src/styles/dialogStyles.ts +8 -0
  189. package/src/styles/index.ts +2 -0
  190. package/src/styles/invitationStatusStyles.ts +42 -0
  191. package/src/styles/panelStyles.ts +2 -1
  192. package/src/testing/ClientProvider.tsx +1 -1
  193. package/src/translations/locales/en-US.ts +53 -1
  194. package/src/util/index.ts +5 -0
  195. package/src/util/invitationStatusValue.ts +21 -0
  196. package/theme-extensions.js +11 -0
  197. package/dist/types/src/components/IdentityListItem/IdentityListItem.d.ts +0 -6
  198. package/dist/types/src/components/IdentityListItem/IdentityListItem.d.ts.map +0 -1
  199. package/dist/types/src/components/IdentityListItem/IdentityListItem.stories.d.ts.map +0 -1
@@ -0,0 +1,204 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { CheckCircle, HourglassSimple, X } from 'phosphor-react';
6
+ import React, { ComponentProps, ComponentPropsWithoutRef, ReactNode, useMemo } from 'react';
7
+
8
+ import type { AuthenticatingInvitationObservable, Profile } from '@dxos/client';
9
+ import { Invitation } from '@dxos/client';
10
+ import { useInvitationStatus } from '@dxos/react-client';
11
+ import { mx, useTranslation, Trans, Avatar, useId, getSize, strongShimmer } from '@dxos/react-components';
12
+
13
+ import { defaultSurface, subduedSurface, resolvedBgColor, activeBgColor, inactiveBgColor } from '../../../styles';
14
+ import { invitationStatusValue } from '../../../util';
15
+ import { JoinDispatch } from '../JoinPanelProps';
16
+
17
+ export interface ViewStateProps extends ComponentProps<'div'> {
18
+ active: boolean;
19
+ dispatch: JoinDispatch;
20
+ selectedIdentity?: true | Profile;
21
+ activeInvitation?: true | AuthenticatingInvitationObservable;
22
+ }
23
+
24
+ const stripe = mx('rounded-full grow', getSize(3));
25
+
26
+ export const ViewStateHeading = ({ children, className, ...props }: ComponentPropsWithoutRef<'h2'>) => {
27
+ return (
28
+ <h2 {...props} className={mx('font-system-medium text-sm md:text-base mbe-1 mli-1 text-center', className)}>
29
+ {children}
30
+ </h2>
31
+ );
32
+ };
33
+
34
+ const PureViewStateInvitation = ({
35
+ halted,
36
+ cursor,
37
+ label,
38
+ resolvedColor
39
+ }: {
40
+ halted?: boolean;
41
+ cursor: number;
42
+ label: ReactNode;
43
+ resolvedColor: string;
44
+ }) => {
45
+ const labelId = useId('invitationState');
46
+ return (
47
+ <div role='none' className={mx(defaultSurface, 'pli-2 pbs-2')}>
48
+ <div role='status' aria-labelledby={labelId} className='flex gap-2 items-center mlb-1'>
49
+ <div
50
+ role='none'
51
+ className={mx(
52
+ stripe,
53
+ !halted && cursor === 1 && strongShimmer,
54
+ cursor === 2 ? (halted ? resolvedColor : activeBgColor) : cursor > 1 ? resolvedColor : inactiveBgColor
55
+ )}
56
+ />
57
+ <div
58
+ role='none'
59
+ className={mx(
60
+ stripe,
61
+ !halted && cursor === 3 && strongShimmer,
62
+ cursor === 3 ? (halted ? resolvedColor : activeBgColor) : cursor > 3 ? resolvedColor : inactiveBgColor
63
+ )}
64
+ />
65
+ <div
66
+ role='none'
67
+ className={mx(stripe, cursor > 3 ? (halted ? resolvedColor : resolvedColor) : inactiveBgColor)}
68
+ />
69
+ </div>
70
+ <ViewStateHeading id={labelId} className='mbs-2 flex justify-center items-center gap-2'>
71
+ {label}
72
+ </ViewStateHeading>
73
+ </div>
74
+ );
75
+ };
76
+
77
+ const ViewStateInvitationStatus = ({ activeInvitation }: { activeInvitation: AuthenticatingInvitationObservable }) => {
78
+ const { t } = useTranslation('os');
79
+ const { status, haltedAt } = useInvitationStatus(activeInvitation);
80
+
81
+ const halted =
82
+ status === Invitation.State.CANCELLED || status === Invitation.State.TIMEOUT || status === Invitation.State.ERROR;
83
+
84
+ const cursor = invitationStatusValue.get(halted ? haltedAt! : status)!;
85
+
86
+ const resolvedColor = resolvedBgColor(status);
87
+
88
+ const statusLabelMap = useMemo(
89
+ () =>
90
+ new Map<Invitation.State, ReactNode>([
91
+ [
92
+ Invitation.State.ERROR,
93
+ <>
94
+ <X weight='bold' className={mx(getSize(4), 'text-error-600 dark:text-error-400')} />
95
+ <span>{t('error status label')}</span>
96
+ </>
97
+ ],
98
+ [
99
+ Invitation.State.TIMEOUT,
100
+ <>
101
+ <HourglassSimple weight='fill' className={mx(getSize(4), 'text-warning-600 dark:text-warning-400')} />
102
+ <span>{t('timeout status label')}</span>
103
+ </>
104
+ ],
105
+ [
106
+ Invitation.State.CANCELLED,
107
+ <>
108
+ <X weight='bold' className={mx(getSize(4), 'text-warning-600 dark:text-warning-400')} />
109
+ <span>{t('cancelled status label')}</span>
110
+ </>
111
+ ],
112
+ [Invitation.State.INIT, t('init status label')],
113
+ [Invitation.State.CONNECTING, t('connecting status label')],
114
+ [Invitation.State.CONNECTED, t('connected status label')],
115
+ [Invitation.State.AUTHENTICATING, t('authenticating status label')],
116
+ [
117
+ Invitation.State.SUCCESS,
118
+ <>
119
+ <CheckCircle weight='fill' className={mx(getSize(4), 'text-success-600 dark:text-success-400')} />
120
+ <span>{t('success status label')}</span>
121
+ </>
122
+ ]
123
+ ]),
124
+ [t]
125
+ );
126
+
127
+ return (
128
+ <PureViewStateInvitation
129
+ {...{
130
+ label: statusLabelMap.get(status)!,
131
+ resolvedColor,
132
+ cursor,
133
+ halted
134
+ }}
135
+ />
136
+ );
137
+ };
138
+
139
+ export const ViewState = ({
140
+ active,
141
+ children,
142
+ className,
143
+ dispatch,
144
+ selectedIdentity,
145
+ activeInvitation,
146
+ ...props
147
+ }: ViewStateProps) => {
148
+ // note (thure): reserve `order-1` and `order-3` for outgoing steps in different directions
149
+ const { t } = useTranslation('os');
150
+ const identityLabel = useId('selectedIdentityLabel');
151
+
152
+ return (
153
+ <div
154
+ role='none'
155
+ {...props}
156
+ {...(!active && { 'aria-hidden': true })}
157
+ className={mx('is-[50%] flex flex-col', active ? 'order-2' : 'order-4', className)}
158
+ >
159
+ {selectedIdentity && (
160
+ <div role='none' className={mx(subduedSurface, 'flex-none flex items-center gap-1 pli-2 pbe-1.5')}>
161
+ <Trans
162
+ {...{
163
+ defaults: t('join space as identity heading'),
164
+ components: {
165
+ icon: (
166
+ <Avatar
167
+ size={4}
168
+ fallbackValue={selectedIdentity === true ? '' : selectedIdentity.identityKey.toHex()}
169
+ labelId={identityLabel}
170
+ />
171
+ ),
172
+ label: <span id={identityLabel} />,
173
+ part: <span role='none' className='flex items-center gap-1 leading-none' />
174
+ },
175
+ values: {
176
+ labelValue:
177
+ selectedIdentity === true
178
+ ? ' '
179
+ : selectedIdentity.displayName ?? selectedIdentity.identityKey.truncate()
180
+ }
181
+ }}
182
+ />
183
+ </div>
184
+ )}
185
+ {activeInvitation &&
186
+ (activeInvitation === true ? (
187
+ <PureViewStateInvitation
188
+ {...{
189
+ halted: false,
190
+ status: Invitation.State.INIT,
191
+ label: t('invitation input label'),
192
+ cursor: 0,
193
+ resolvedColor: inactiveBgColor
194
+ }}
195
+ />
196
+ ) : (
197
+ <ViewStateInvitationStatus {...{ activeInvitation }} />
198
+ ))}
199
+ <div role='region' className={mx(defaultSurface, 'rounded-be-md grow shrink-0 flex flex-col gap-1 p-2')}>
200
+ {children}
201
+ </div>
202
+ </div>
203
+ );
204
+ };
@@ -0,0 +1,12 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ export * from './AdditionMethodSelector';
6
+ export * from './IdentityAdded';
7
+ export * from './IdentitySelector';
8
+ export * from './IdentityInput';
9
+ export * from './InvitationAccepted';
10
+ export * from './InvitationInput';
11
+ export * from './InvitationRescuer';
12
+ export * from './InvitationAuthenticator';
@@ -0,0 +1,53 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { StoryContext, StoryFn } from '@storybook/react';
6
+ import React, { useEffect, useState } from 'react';
7
+
8
+ import { Client, Space } from '@dxos/client';
9
+ import { TestBuilder } from '@dxos/client/testing';
10
+ import { ClientContext } from '@dxos/react-client';
11
+ import { Loading } from '@dxos/react-components';
12
+
13
+ import { SpacePanel } from './SpacePanel';
14
+
15
+ export default {
16
+ component: SpacePanel
17
+ };
18
+
19
+ const SpaceDecorator = (Story: StoryFn, { args }: StoryContext) => {
20
+ const testBuilder = new TestBuilder();
21
+ const [client, setClient] = useState<Client>();
22
+ const [space, setSpace] = useState<Space>();
23
+
24
+ useEffect(() => {
25
+ const timeout = setTimeout(async () => {
26
+ const client = new Client({ services: testBuilder.createClientServicesHost() });
27
+ await client.initialize();
28
+ setClient(client);
29
+
30
+ await client.halo.createProfile();
31
+ const space = await client.echo.createSpace();
32
+ space.properties.name = 'Q3 2022 Planning';
33
+
34
+ setSpace(space);
35
+ });
36
+
37
+ return () => clearTimeout(timeout);
38
+ }, []);
39
+
40
+ if (!client || !space) {
41
+ return <Loading label='Setting things up…' />;
42
+ }
43
+
44
+ return (
45
+ <ClientContext.Provider value={{ client }}>
46
+ <Story args={{ space, ...args }} />
47
+ </ClientContext.Provider>
48
+ );
49
+ };
50
+
51
+ export const Default = {
52
+ decorators: [SpaceDecorator]
53
+ };
@@ -0,0 +1,174 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { CaretLeft, CaretRight, Intersect, PlusCircle, UserPlus } from 'phosphor-react';
6
+ import React, { cloneElement, useReducer } from 'react';
7
+
8
+ import { Space, SpaceMember } from '@dxos/client';
9
+ import { useClient, useSpaceInvitations, useSpaces } from '@dxos/react-client';
10
+ import { Button, getSize, mx, Tooltip, useTranslation } from '@dxos/react-components';
11
+
12
+ import { InvitationList, PanelSeparator, SpaceListItem, SpaceMemberListContainer } from '../../components';
13
+ import { defaultSurface, subduedSurface } from '../../styles';
14
+
15
+ export type SpacePanelProps = {
16
+ titleId?: string;
17
+ space?: Space;
18
+ initialState?: SpaceView;
19
+ createInvitationUrl: (invitationCode: string) => string;
20
+ onClickJoinSpace?: () => void;
21
+ onClickMember?: (member: SpaceMember) => void;
22
+ doneActionParent?: Parameters<typeof cloneElement>[0];
23
+ onDone?: (result: Space | null) => void;
24
+ };
25
+
26
+ export type SpaceView = 'current space' | 'space list';
27
+
28
+ export type SpaceState = {
29
+ activeView: SpaceView;
30
+ };
31
+
32
+ const CurrentSpaceView = ({
33
+ space,
34
+ createInvitationUrl,
35
+ onClickMember,
36
+ onShowAll,
37
+ titleId
38
+ }: { onShowAll: () => void } & SpacePanelProps) => {
39
+ const { t } = useTranslation('os');
40
+ const invitations = useSpaceInvitations(space?.key);
41
+ const name = space?.properties.name;
42
+
43
+ if (!space) {
44
+ return null;
45
+ }
46
+
47
+ return (
48
+ <div role='none' className='flex flex-col'>
49
+ <div role='none' className={mx(subduedSurface, 'rounded-bs-md flex items-center p-2 gap-2')}>
50
+ <Tooltip content={t('show all spaces label')} zIndex='z-50'>
51
+ <Button compact variant='ghost' onClick={onShowAll} data-testid='show-all-spaces'>
52
+ <CaretLeft className={getSize(4)} weight='bold' />
53
+ </Button>
54
+ </Tooltip>
55
+ {/* TODO(wittjosiah): Label this as the space panel. */}
56
+ <h2 id={titleId} className={mx('grow font-system-medium', !name && 'font-mono')}>
57
+ {name ?? space.key.truncate()}
58
+ </h2>
59
+ </div>
60
+ <div role='region' className={mx(defaultSurface, 'rounded-be-md p-2')}>
61
+ <InvitationList
62
+ invitations={invitations}
63
+ onClickRemove={({ invitation }) => invitation && space?.removeInvitation(invitation.invitationId!)}
64
+ createInvitationUrl={createInvitationUrl}
65
+ />
66
+ <Button
67
+ className='is-full flex gap-2 mbs-2'
68
+ compact
69
+ onClick={() => space?.createInvitation()}
70
+ data-testid='create-space-invitation'
71
+ >
72
+ <span>{t('create space invitation label')}</span>
73
+ <UserPlus className={getSize(4)} weight='bold' />
74
+ </Button>
75
+ <PanelSeparator />
76
+ <SpaceMemberListContainer spaceKey={space.key} includeSelf onSelect={onClickMember} />
77
+ </div>
78
+ </div>
79
+ );
80
+ };
81
+
82
+ const SpaceListView = ({
83
+ doneActionParent,
84
+ onDone,
85
+ onClickJoinSpace,
86
+ onShowCurrent,
87
+ titleId
88
+ }: { onShowCurrent: () => void } & SpacePanelProps) => {
89
+ const { t } = useTranslation('os');
90
+ const client = useClient();
91
+ const spaces = useSpaces();
92
+
93
+ const handleCreateSpace = async () => {
94
+ const space = await client.echo.createSpace();
95
+ onDone?.(space);
96
+ };
97
+
98
+ return (
99
+ <div role='none' className='flex flex-col'>
100
+ <div role='none' className={mx(subduedSurface, 'rounded-bs-md flex items-center p-2 gap-2')}>
101
+ <h2 id={titleId} className='grow font-system-medium'>
102
+ {t('all spaces label')}
103
+ </h2>
104
+ <Tooltip content={t('show current space label')} zIndex='z-50'>
105
+ <Button compact variant='ghost' onClick={onShowCurrent} data-testid='show-current-space'>
106
+ <CaretRight className={getSize(4)} weight='bold' />
107
+ </Button>
108
+ </Tooltip>
109
+ </div>
110
+ <div role='region' className={mx(defaultSurface, 'rounded-be-md p-2')}>
111
+ <ul className='flex flex-col gap-2'>
112
+ {spaces.map((space) => {
113
+ const key = space.key.toHex();
114
+ const listItem = <SpaceListItem key={key} space={space} onClick={() => onDone?.(space)} />;
115
+ return doneActionParent ? cloneElement(doneActionParent, { key }, listItem) : listItem;
116
+ })}
117
+ </ul>
118
+ <Button className='is-full flex gap-2 mbs-2' compact onClick={handleCreateSpace} data-testid='create-space'>
119
+ <span>{t('create space label')}</span>
120
+ <PlusCircle className={getSize(4)} weight='bold' />
121
+ </Button>
122
+ {onClickJoinSpace && (
123
+ <Button
124
+ className='is-full flex gap-2 mbs-2'
125
+ compact
126
+ onClick={() => onClickJoinSpace?.()}
127
+ data-testid='join-space'
128
+ >
129
+ <span>{t('join space label')}</span>
130
+ <Intersect className={getSize(4)} weight='bold' />
131
+ </Button>
132
+ )}
133
+ </div>
134
+ </div>
135
+ );
136
+ };
137
+
138
+ interface SpacePanelState {
139
+ activeView: SpaceView;
140
+ }
141
+
142
+ interface SpacePanelAction {
143
+ type: 'deselect space' | 'select space';
144
+ }
145
+
146
+ export const SpacePanel = (props: SpacePanelProps) => {
147
+ const reducer = (state: SpacePanelState, action: SpacePanelAction) => {
148
+ const nextState = { ...state };
149
+ switch (action.type) {
150
+ case 'deselect space':
151
+ nextState.activeView = 'space list';
152
+ break;
153
+ case 'select space':
154
+ nextState.activeView = 'current space';
155
+ break;
156
+ }
157
+ return nextState;
158
+ };
159
+
160
+ const [panelState, dispatch] = useReducer(reducer, {
161
+ activeView: props.initialState ?? 'current space'
162
+ });
163
+
164
+ // TODO(wittjosiah): Use ViewState or similar.
165
+ return (
166
+ <>
167
+ {panelState.activeView === 'current space' ? (
168
+ <CurrentSpaceView {...props} onShowAll={() => dispatch({ type: 'deselect space' })} />
169
+ ) : (
170
+ <SpaceListView {...props} onShowCurrent={() => dispatch({ type: 'select space' })} />
171
+ )}
172
+ </>
173
+ );
174
+ };
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ export * from './SpacePanel';
@@ -2,4 +2,7 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
+ export * from './DevicesPanel';
5
6
  export * from './IdentityPanel';
7
+ export * from './JoinPanel';
8
+ export * from './SpacePanel';
@@ -0,0 +1,8 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ export const defaultOverlay =
6
+ 'fixed inset-0 backdrop-blur z-50 overflow-auto grid place-items-center p-2 md:p-4 lg:p-8';
7
+
8
+ export const defaultDialogContent = 'is-full min-is-[260px] max-is-[320px] rounded-md shadow-md backdrop-blur-md';
@@ -2,4 +2,6 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
+ export * from './dialogStyles';
5
6
  export * from './panelStyles';
7
+ export * from './invitationStatusStyles';
@@ -0,0 +1,42 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { Invitation } from '@dxos/client';
6
+
7
+ export const inactiveBgColor = 'bg-neutral-100 dark:bg-neutral-600';
8
+ export const activeBgColor = 'bg-primary-500 dark:bg-primary-400';
9
+ export const successBgColor = 'bg-success-500 dark:bg-success-400';
10
+ export const errorBgColor = 'bg-error-500 dark:bg-error-400';
11
+ export const cancelledBgColor = 'bg-warning-500 dark:bg-warning-400';
12
+
13
+ export const inactiveTextColor = 'text-neutral-100 dark:text-neutral-600';
14
+ export const activeTextColor = 'text-primary-400 dark:text-primary-500';
15
+ export const successTextColor = 'text-success-400 dark:text-success-500';
16
+ export const errorTextColor = 'text-error-400 dark:text-error-500';
17
+ export const cancelledTextColor = 'text-warning-400 dark:text-warning-500';
18
+
19
+ export const inactiveStrokeColor = 'stroke-neutral-100 dark:stroke-neutral-700';
20
+ export const activeStrokeColor = 'stroke-primary-500 dark:stroke-primary-150';
21
+ export const successStrokeColor = 'stroke-success-400 dark:stroke-primary-400';
22
+ export const errorStrokeColor = 'stroke-error-400 dark:stroke-error-500';
23
+ export const cancelledStrokeColor = 'stroke-warning-400 dark:stroke-warning-500';
24
+
25
+ export const resolvedStrokeColor = (status: Invitation.State) =>
26
+ status === Invitation.State.ERROR
27
+ ? errorStrokeColor
28
+ : status === Invitation.State.CANCELLED || status === Invitation.State.TIMEOUT
29
+ ? cancelledStrokeColor
30
+ : successStrokeColor;
31
+ export const resolvedBgColor = (status: Invitation.State) =>
32
+ status === Invitation.State.ERROR
33
+ ? errorBgColor
34
+ : status === Invitation.State.CANCELLED || status === Invitation.State.TIMEOUT
35
+ ? cancelledBgColor
36
+ : successBgColor;
37
+ export const resolvedTextColor = (status: Invitation.State) =>
38
+ status === Invitation.State.ERROR
39
+ ? errorTextColor
40
+ : status === Invitation.State.CANCELLED || status === Invitation.State.TIMEOUT
41
+ ? cancelledTextColor
42
+ : successTextColor;
@@ -2,5 +2,6 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- export const defaultPanel = 'p-2 backdrop-blur-md bg-neutral-50/[.66] dark:bg-neutral-950/[.66]';
5
+ export const defaultSurface = 'bg-neutral-50/[.72] dark:bg-neutral-950/[.72]';
6
+ export const subduedSurface = 'bg-neutral-100/[.66] dark:bg-neutral-900/[.66]';
6
7
  export const defaultArrow = 'text-neutral-50/[.66] dark:text-neutral-950/[.66]';
@@ -11,7 +11,7 @@ const configProvider = async () => new Config(await Dynamics(), Defaults());
11
11
 
12
12
  export const ClientProvider = ({ children }: PropsWithChildren<{}>) => {
13
13
  return (
14
- <NaturalClientProvider config={configProvider} fallback={<span>Starting client…</span>}>
14
+ <NaturalClientProvider config={configProvider} fallback={() => <span>Starting client…</span>}>
15
15
  {children}
16
16
  </NaturalClientProvider>
17
17
  );
@@ -5,5 +5,57 @@
5
5
  export const os = {
6
6
  'manage profile label': 'Manage identity',
7
7
  'identity offline description': 'Offline',
8
- 'sidebar label': 'DXOS sidebar'
8
+ 'sidebar label': 'DXOS sidebar',
9
+ 'copy invitation code label': 'Copy',
10
+ 'open share panel label': 'Share',
11
+ 'joining heading': '<small>Joining</small><large>{{name}}</large>',
12
+ 'join space heading': 'Join a space',
13
+ 'halo heading': 'Initialize device identity',
14
+ 'exit label': 'Exit',
15
+ 'identity selector title': 'Join as',
16
+ 'identity radio group title': 'Select an identity you’ve associated with this device already',
17
+ 'add identity label': 'Add',
18
+ 'continue label': 'Continue',
19
+ 'back label': 'Back',
20
+ 'next label': 'Next',
21
+ 'close label': 'Close',
22
+ 'auth choices label': 'Choose an identity',
23
+ 'create identity label': 'Create an identity',
24
+ 'create identity description': 'Create a new identity.',
25
+ 'recover identity label': 'Use a seed phrase',
26
+ 'recover identity description': 'Enter your seed phrase to log in manually.',
27
+ 'join identity label': 'Use an authed device',
28
+ 'join identity description': 'Add this device to an identity you’re already logged into on another device.',
29
+ 'deselect identity label': 'Back to identities',
30
+ 'addition method selector title': 'Add an identity to this device',
31
+ 'new identity input label': 'Add a display name',
32
+ 'recover identity input label': 'Type or paste your seed phrase',
33
+ 'failed to create identity message': 'Failed to create an identity.',
34
+ 'failed to recover identity message': 'Failed to recover an identity from the provided seed phrase.',
35
+ 'failed to authenticate message': 'Incorrect PIN.',
36
+ 'identity added label': 'Created identity & added to this device:',
37
+ 'join space as identity heading': '<part>as </part><part><icon/><label>{{labelValue}}</label></part>',
38
+ 'error status label': 'Failed',
39
+ 'timeout status label': 'Timed out',
40
+ 'cancelled status label': 'Cancelled',
41
+ 'init status label': 'Ready to connect',
42
+ 'connecting status label': 'Connecting…',
43
+ 'connected status label': 'Enter confirmation PIN',
44
+ 'authenticating status label': 'Enter confirmation PIN',
45
+ 'success status label': 'Success',
46
+ 'connect label': 'Connect',
47
+ 'reconnect label': 'Reconnect',
48
+ 'cancel label': 'Cancel',
49
+ 'done label': 'Done',
50
+ 'reset label': 'Start over',
51
+ 'auth code input label': 'Auth code provided by the invitation sender',
52
+ 'invitation input label': 'Paste an invitation code or URL',
53
+ 'create space invitation label': 'Invite',
54
+ 'qr label': 'Scan to accept invitation',
55
+ 'empty invitations message': 'No pending invitations',
56
+ 'show all spaces label': 'All spaces',
57
+ 'all spaces label': 'All spaces',
58
+ 'create space label': 'Create a new space',
59
+ 'join space label': 'Join a space',
60
+ 'show current space label': 'Current space'
9
61
  };
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ export * from './invitationStatusValue';
@@ -0,0 +1,21 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { Invitation } from '@dxos/client';
6
+
7
+ /**
8
+ * This map assigns a numeric value to invitation states to facilitate
9
+ * more terse code which uses combinations of `>`, `<`, and `=` instead of
10
+ * longer sequences of `===` and `||` predicates.
11
+ */
12
+ export const invitationStatusValue = new Map<Invitation.State, number>([
13
+ [Invitation.State.ERROR, -3],
14
+ [Invitation.State.TIMEOUT, -2],
15
+ [Invitation.State.CANCELLED, -1],
16
+ [Invitation.State.INIT, 0],
17
+ [Invitation.State.CONNECTING, 1],
18
+ [Invitation.State.CONNECTED, 2],
19
+ [Invitation.State.AUTHENTICATING, 3],
20
+ [Invitation.State.SUCCESS, 4]
21
+ ]);
@@ -0,0 +1,11 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ module.exports = {
6
+ osThemeExtension: {
7
+ spacing: {
8
+ sidebar: '272px'
9
+ }
10
+ }
11
+ };
@@ -1,6 +0,0 @@
1
- import { Profile, SpaceMember } from '@dxos/client';
2
- export declare const IdentityListItem: ({ identity, presence }: {
3
- identity: Profile;
4
- presence?: SpaceMember.PresenceState | undefined;
5
- }) => JSX.Element;
6
- //# sourceMappingURL=IdentityListItem.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"IdentityListItem.d.ts","sourceRoot":"","sources":["../../../../../src/components/IdentityListItem/IdentityListItem.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGpD,eAAO,MAAM,gBAAgB;cAAwC,OAAO;;iBAuB3E,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"IdentityListItem.stories.d.ts","sourceRoot":"","sources":["../../../../../src/components/IdentityListItem/IdentityListItem.stories.tsx"],"names":[],"mappings":"AAIA,OAAO,YAAY,CAAC;;;;;;;AAIpB,wBAEE;AAEF,eAAO,MAAM,OAAO;;;;;;;;;;CAUnB,CAAC"}