@dxos/react-ui 0.1.32-next.77d513e → 0.1.33

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 (35) hide show
  1. package/dist/lib/browser/index.mjs +611 -527
  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/Clipboard/ClipboardProvider.d.ts +9 -0
  5. package/dist/types/src/components/Clipboard/ClipboardProvider.d.ts.map +1 -0
  6. package/dist/types/src/components/Clipboard/CopyButton.d.ts +5 -0
  7. package/dist/types/src/components/Clipboard/CopyButton.d.ts.map +1 -0
  8. package/dist/types/src/components/Clipboard/index.d.ts +3 -0
  9. package/dist/types/src/components/Clipboard/index.d.ts.map +1 -0
  10. package/dist/types/src/components/InvitationEmoji/InvitationEmoji.d.ts +7 -0
  11. package/dist/types/src/components/InvitationEmoji/InvitationEmoji.d.ts.map +1 -0
  12. package/dist/types/src/components/InvitationEmoji/index.d.ts +2 -0
  13. package/dist/types/src/components/InvitationEmoji/index.d.ts.map +1 -0
  14. package/dist/types/src/components/InvitationList/InvitationList.d.ts.map +1 -1
  15. package/dist/types/src/components/InvitationList/InvitationListItem.d.ts.map +1 -1
  16. package/dist/types/src/components/InvitationList/InvitationStatusAvatar.d.ts +2 -1
  17. package/dist/types/src/components/InvitationList/InvitationStatusAvatar.d.ts.map +1 -1
  18. package/dist/types/src/components/InvitationList/InvitationStatusAvatar.stories.d.ts +1 -1
  19. package/dist/types/src/components/index.d.ts +2 -0
  20. package/dist/types/src/components/index.d.ts.map +1 -1
  21. package/dist/types/src/panels/JoinPanel/JoinHeading.d.ts.map +1 -1
  22. package/dist/types/src/translations/locales/en-US.d.ts +1 -0
  23. package/dist/types/src/translations/locales/en-US.d.ts.map +1 -1
  24. package/package.json +13 -13
  25. package/src/components/Clipboard/ClipboardProvider.tsx +26 -0
  26. package/src/components/Clipboard/CopyButton.tsx +38 -0
  27. package/src/components/Clipboard/index.ts +6 -0
  28. package/src/components/InvitationEmoji/InvitationEmoji.tsx +32 -0
  29. package/src/components/InvitationEmoji/index.ts +5 -0
  30. package/src/components/InvitationList/InvitationList.tsx +7 -4
  31. package/src/components/InvitationList/InvitationListItem.tsx +6 -11
  32. package/src/components/InvitationList/InvitationStatusAvatar.tsx +37 -26
  33. package/src/components/index.ts +2 -0
  34. package/src/panels/JoinPanel/JoinHeading.tsx +3 -12
  35. package/src/translations/locales/en-US.ts +1 -0
@@ -0,0 +1,6 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ export * from './CopyButton';
6
+ export * from './ClipboardProvider';
@@ -0,0 +1,32 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import React, { ComponentPropsWithoutRef } from 'react';
6
+
7
+ import { getSize, mx } from '@dxos/react-components';
8
+
9
+ import { toEmoji } from '../../util';
10
+
11
+ export type InvitationEmojiProps = {
12
+ invitationId?: string;
13
+ size?: 'lg' | 'sm';
14
+ } & ComponentPropsWithoutRef<'div'>;
15
+
16
+ export const InvitationEmoji = ({ invitationId, size = 'lg', ...rootProps }: InvitationEmojiProps) => {
17
+ const invitationEmoji = invitationId && toEmoji(invitationId);
18
+ return (
19
+ <div
20
+ role='none'
21
+ {...rootProps}
22
+ className={mx(
23
+ getSize(size === 'sm' ? 8 : 12),
24
+ 'inline-flex items-center justify-center leading-none',
25
+ size === 'sm' ? 'text-2xl' : 'text-5xl bg-neutral-300 dark:bg-neutral-700 rounded-full',
26
+ rootProps.className
27
+ )}
28
+ >
29
+ <span role='none'>{invitationEmoji}</span>
30
+ </div>
31
+ );
32
+ };
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ export * from './InvitationEmoji';
@@ -7,6 +7,7 @@ import React from 'react';
7
7
  import type { CancellableInvitationObservable } from '@dxos/client';
8
8
  import { defaultDescription, DensityProvider, mx, useTranslation } from '@dxos/react-components';
9
9
 
10
+ import { ClipboardProvider } from '../Clipboard';
10
11
  import { InvitationListItem, InvitationListItemProps } from './InvitationListItem';
11
12
 
12
13
  export interface InvitationListProps extends Omit<InvitationListItemProps, 'invitation' | 'value'> {
@@ -18,10 +19,12 @@ export const InvitationList = ({ invitations, ...invitationProps }: InvitationLi
18
19
  return invitations.length ? (
19
20
  <AccordionRoot type='single' collapsible className='flex flex-col gap-1'>
20
21
  <DensityProvider density='fine'>
21
- {invitations.map((invitation, index) => {
22
- const value = invitation.invitation?.invitationId ?? `inv_${index}`;
23
- return <InvitationListItem key={value} value={value} invitation={invitation} {...invitationProps} />;
24
- })}
22
+ <ClipboardProvider>
23
+ {invitations.map((invitation, index) => {
24
+ const value = invitation.invitation?.invitationId ?? `inv_${index}`;
25
+ return <InvitationListItem key={value} value={value} invitation={invitation} {...invitationProps} />;
26
+ })}
27
+ </ClipboardProvider>
25
28
  </DensityProvider>
26
29
  </AccordionRoot>
27
30
  ) : (
@@ -1,7 +1,7 @@
1
1
  //
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
- import { Copy, ProhibitInset, QrCode, X } from '@phosphor-icons/react';
4
+ import { ProhibitInset, QrCode, X } from '@phosphor-icons/react';
5
5
  import * as AccordionPrimitive from '@radix-ui/react-accordion';
6
6
  import { QRCodeSVG } from 'qrcode.react';
7
7
  import React, { useCallback } from 'react';
@@ -11,6 +11,7 @@ import { useInvitationStatus } from '@dxos/react-client';
11
11
  import { Button, getSize, mx, useId, useTranslation } from '@dxos/react-components';
12
12
 
13
13
  import { invitationStatusValue } from '../../util';
14
+ import { CopyButton } from '../Clipboard';
14
15
  import { SharedInvitationListProps } from './InvitationListProps';
15
16
  import { InvitationStatusAvatar } from './InvitationStatusAvatar';
16
17
 
@@ -39,16 +40,13 @@ export const InvitationListItem = ({
39
40
  const handleClickRemove = useCallback(() => onClickRemove(invitation), [invitation, onClickRemove]);
40
41
 
41
42
  const invitationUrl = invitationCode && createInvitationUrl(invitationCode);
42
- const handleClickCopy = useCallback(() => {
43
- if (invitationUrl) {
44
- void navigator.clipboard.writeText(invitationUrl);
45
- }
46
- }, [invitationUrl]);
47
43
 
48
44
  return (
49
45
  <AccordionPrimitive.Item value={value}>
50
46
  <AccordionPrimitive.Header className='flex gap-2 items-center'>
51
- <InvitationStatusAvatar {...{ status, haltedAt, size: 8 }} />
47
+ <InvitationStatusAvatar
48
+ {...{ status, haltedAt, size: 8, invitationId: invitation?.invitation?.invitationId }}
49
+ />
52
50
  {showShare && invitationUrl ? (
53
51
  <>
54
52
  <AccordionPrimitive.Trigger asChild>
@@ -57,10 +55,7 @@ export const InvitationListItem = ({
57
55
  <QrCode className={getSize(4)} weight='bold' />
58
56
  </Button>
59
57
  </AccordionPrimitive.Trigger>
60
- <Button className='flex gap-1' onClick={handleClickCopy} data-testid='copy-invitation'>
61
- <span className='pli-1'>{t('copy invitation code label')}</span>
62
- <Copy className={getSize(4)} weight='bold' />
63
- </Button>
58
+ <CopyButton value={invitationUrl} />
64
59
  </>
65
60
  ) : showAuthCode ? (
66
61
  <p className='grow text-xl text-center text-success-500 dark:text-success-300 font-mono'>
@@ -9,6 +9,7 @@ import { getSize, mx, Size } from '@dxos/react-components';
9
9
 
10
10
  import { inactiveStrokeColor, activeStrokeColor, resolvedStrokeColor } from '../../styles';
11
11
  import { invitationStatusValue } from '../../util';
12
+ import { InvitationEmoji } from '../InvitationEmoji';
12
13
 
13
14
  export interface InvitationStatusAvatarSlots {
14
15
  svg?: ComponentPropsWithoutRef<'svg'>;
@@ -19,6 +20,7 @@ export interface InvitationStatusAvatarProps {
19
20
  status: Invitation.State;
20
21
  haltedAt?: Invitation.State;
21
22
  slots?: InvitationStatusAvatarSlots;
23
+ invitationId?: string;
22
24
  }
23
25
 
24
26
  const svgSize = 32;
@@ -40,41 +42,50 @@ const circleProps: ComponentPropsWithoutRef<'circle'> = {
40
42
  strokeDasharray: `${segment - gap} ${2 * segment + gap}`
41
43
  };
42
44
 
43
- export const InvitationStatusAvatar = ({ size = 10, status, haltedAt, slots = {} }: InvitationStatusAvatarProps) => {
45
+ export const InvitationStatusAvatar = ({
46
+ size = 10,
47
+ status,
48
+ haltedAt,
49
+ slots = {},
50
+ invitationId
51
+ }: InvitationStatusAvatarProps) => {
44
52
  const resolvedColor = resolvedStrokeColor(status);
45
53
  const halted =
46
54
  status === Invitation.State.CANCELLED || status === Invitation.State.TIMEOUT || status === Invitation.State.ERROR;
47
55
  const cursor = invitationStatusValue.get(halted ? haltedAt! : status)!;
48
56
  return (
49
- <svg {...slots.svg} viewBox={`0 0 ${svgSize} ${svgSize}`} className={mx(getSize(size), slots.svg?.className)}>
50
- {[...Array(nSegments)].map((_, index) => (
51
- <circle
52
- key={index}
53
- {...circleProps}
54
- strokeDashoffset={index * segment + baseOffset}
55
- className={
56
- index === 0
57
- ? cursor === 1
58
- ? halted
57
+ <div className='inline-block relative'>
58
+ <InvitationEmoji {...{ invitationId }} size='sm' className='absolute inset-0 text-base' />
59
+ <svg {...slots.svg} viewBox={`0 0 ${svgSize} ${svgSize}`} className={mx(getSize(size), slots.svg?.className)}>
60
+ {[...Array(nSegments)].map((_, index) => (
61
+ <circle
62
+ key={index}
63
+ {...circleProps}
64
+ strokeDashoffset={index * segment + baseOffset}
65
+ className={
66
+ index === 0
67
+ ? cursor === 1
68
+ ? halted
69
+ ? resolvedColor
70
+ : activeStrokeColor
71
+ : cursor > 1
59
72
  ? resolvedColor
60
- : activeStrokeColor
61
- : cursor > 1
62
- ? resolvedColor
63
- : inactiveStrokeColor
64
- : index === 1
65
- ? cursor === 3
66
- ? halted
73
+ : inactiveStrokeColor
74
+ : index === 1
75
+ ? cursor === 3
76
+ ? halted
77
+ ? resolvedColor
78
+ : activeStrokeColor
79
+ : cursor > 3
67
80
  ? resolvedColor
68
- : activeStrokeColor
81
+ : inactiveStrokeColor
69
82
  : cursor > 3
70
83
  ? resolvedColor
71
84
  : inactiveStrokeColor
72
- : cursor > 3
73
- ? resolvedColor
74
- : inactiveStrokeColor
75
- }
76
- />
77
- ))}
78
- </svg>
85
+ }
86
+ />
87
+ ))}
88
+ </svg>
89
+ </div>
79
90
  );
80
91
  };
@@ -2,7 +2,9 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
+ export * from './Clipboard';
5
6
  export * from './IdentityList';
7
+ export * from './InvitationEmoji';
6
8
  export * from './InvitationList';
7
9
  export * from './PanelSeparator';
8
10
  export * from './SpaceList';
@@ -7,8 +7,8 @@ import React, { cloneElement, ForwardedRef, forwardRef } from 'react';
7
7
  import { useSpace } from '@dxos/react-client';
8
8
  import { Button, defaultDescription, getSize, Heading, mx, useId, useTranslation } from '@dxos/react-components';
9
9
 
10
+ import { InvitationEmoji } from '../../components';
10
11
  import { defaultSurface } from '../../styles';
11
- import { toEmoji } from '../../util';
12
12
  import { JoinPanelMode } from './JoinPanelProps';
13
13
  import { JoinState } from './joinMachine';
14
14
 
@@ -33,8 +33,7 @@ export const JoinHeading = forwardRef(
33
33
  const name = space?.properties.name;
34
34
  const nameId = useId(mode === 'halo-only' ? 'identityDisplayName' : 'spaceDisplayName');
35
35
 
36
- const invitationKey = joinState?.context[mode === 'halo-only' ? 'halo' : 'space'].invitation?.invitationId;
37
- const invitationEmoji = invitationKey && toEmoji(invitationKey);
36
+ const invitationId = joinState?.context[mode === 'halo-only' ? 'halo' : 'space'].invitation?.invitationId;
38
37
 
39
38
  const exitButton = (
40
39
  <Button
@@ -65,15 +64,7 @@ export const JoinHeading = forwardRef(
65
64
  {t(mode === 'halo-only' ? 'selecting identity heading' : 'joining space heading')}
66
65
  </Heading>
67
66
  <div role='group' className='flex items-center justify-center gap-2'>
68
- <span
69
- role='none'
70
- className={mx(
71
- getSize(12),
72
- 'bg-neutral-300 dark:bg-neutral-700 rounded-full text-center text-4xl leading-[3rem]'
73
- )}
74
- >
75
- {invitationEmoji}
76
- </span>
67
+ <InvitationEmoji {...{ invitationId }} />
77
68
  {name && <p id={nameId}>{name}</p>}
78
69
  </div>
79
70
  </div>
@@ -7,6 +7,7 @@ export const os = {
7
7
  'identity offline description': 'Offline',
8
8
  'sidebar label': 'DXOS sidebar',
9
9
  'copy invitation code label': 'Copy',
10
+ 'copy invitation code success label': 'Copied',
10
11
  'open share panel label': 'Share',
11
12
  'joining space heading': 'Joining space',
12
13
  'join space heading': 'Join a space',