@dxos/react-ui 0.1.32-next.86f5578 → 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 (47) hide show
  1. package/dist/lib/browser/index.mjs +629 -534
  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/composites/DevicesDialog/DevicesDialog.d.ts.map +1 -1
  22. package/dist/types/src/composites/JoinDialog/JoinDialog.d.ts.map +1 -1
  23. package/dist/types/src/composites/SpaceDialog/SpaceDialog.d.ts.map +1 -1
  24. package/dist/types/src/panels/IdentityPanel/IdentityPanel.d.ts.map +1 -1
  25. package/dist/types/src/panels/JoinPanel/JoinHeading.d.ts.map +1 -1
  26. package/dist/types/src/panels/JoinPanel/JoinPanel.d.ts.map +1 -1
  27. package/dist/types/src/panels/JoinPanel/view-states/InvitationAuthenticator.d.ts.map +1 -1
  28. package/dist/types/src/translations/locales/en-US.d.ts +1 -0
  29. package/dist/types/src/translations/locales/en-US.d.ts.map +1 -1
  30. package/package.json +13 -13
  31. package/src/components/Clipboard/ClipboardProvider.tsx +26 -0
  32. package/src/components/Clipboard/CopyButton.tsx +38 -0
  33. package/src/components/Clipboard/index.ts +6 -0
  34. package/src/components/InvitationEmoji/InvitationEmoji.tsx +32 -0
  35. package/src/components/InvitationEmoji/index.ts +5 -0
  36. package/src/components/InvitationList/InvitationList.tsx +7 -4
  37. package/src/components/InvitationList/InvitationListItem.tsx +6 -11
  38. package/src/components/InvitationList/InvitationStatusAvatar.tsx +37 -26
  39. package/src/components/index.ts +2 -0
  40. package/src/composites/DevicesDialog/DevicesDialog.tsx +3 -2
  41. package/src/composites/JoinDialog/JoinDialog.tsx +4 -2
  42. package/src/composites/SpaceDialog/SpaceDialog.tsx +3 -2
  43. package/src/panels/IdentityPanel/IdentityPanel.tsx +4 -2
  44. package/src/panels/JoinPanel/JoinHeading.tsx +3 -12
  45. package/src/panels/JoinPanel/JoinPanel.tsx +4 -3
  46. package/src/panels/JoinPanel/view-states/InvitationAuthenticator.tsx +1 -0
  47. package/src/translations/locales/en-US.ts +1 -0
@@ -0,0 +1,26 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import React, { createContext, PropsWithChildren, useCallback, useContext, useState } from 'react';
6
+
7
+ export type ClipboardContextValue = {
8
+ textValue: string;
9
+ setTextValue: (nextValue: string) => Promise<void>;
10
+ };
11
+
12
+ export const ClipboardContext = createContext<ClipboardContextValue>({
13
+ textValue: '',
14
+ setTextValue: async (_) => {}
15
+ });
16
+
17
+ export const useClipboardContext = () => useContext(ClipboardContext);
18
+
19
+ export const ClipboardProvider = ({ children }: PropsWithChildren<{}>) => {
20
+ const [textValue, setInternalTextValue] = useState('');
21
+ const setTextValue = useCallback(async (nextValue: string) => {
22
+ await navigator.clipboard.writeText(nextValue);
23
+ return setInternalTextValue(nextValue);
24
+ }, []);
25
+ return <ClipboardContext.Provider value={{ textValue, setTextValue }}>{children}</ClipboardContext.Provider>;
26
+ };
@@ -0,0 +1,38 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { Check, Copy } from '@phosphor-icons/react';
6
+ import React from 'react';
7
+
8
+ import { Button, getSize, mx, useTranslation } from '@dxos/react-components';
9
+
10
+ import { useClipboardContext } from './ClipboardProvider';
11
+
12
+ export type CopyButtonProps = {
13
+ value: string;
14
+ };
15
+
16
+ const inactiveLabelStyles = 'invisible bs-px -mbe-px';
17
+
18
+ export const CopyButton = ({ value }: CopyButtonProps) => {
19
+ const { t } = useTranslation('os');
20
+ const { textValue, setTextValue } = useClipboardContext();
21
+ const isCopied = textValue === value;
22
+ return (
23
+ <Button
24
+ className='inline-flex flex-col justify-center'
25
+ onClick={() => setTextValue(value)}
26
+ data-testid='copy-invitation'
27
+ >
28
+ <div role='none' className={mx('flex gap-1', isCopied && inactiveLabelStyles)}>
29
+ <span className='pli-1'>{t('copy invitation code label')}</span>
30
+ <Copy className={getSize(4)} weight='bold' />
31
+ </div>
32
+ <div role='none' className={mx('flex gap-1', !isCopied && inactiveLabelStyles)}>
33
+ <span className='pli-1'>{t('copy invitation code success label')}</span>
34
+ <Check className={getSize(4)} weight='bold' />
35
+ </div>
36
+ </Button>
37
+ );
38
+ };
@@ -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';
@@ -5,7 +5,7 @@
5
5
  import { Close } from '@radix-ui/react-dialog';
6
6
  import React from 'react';
7
7
 
8
- import { ThemeContext, useId } from '@dxos/react-components';
8
+ import { ThemeContext, useId, useThemeContext } from '@dxos/react-components';
9
9
 
10
10
  import { PanelDialog, PanelDialogProps } from '../../layouts';
11
11
  import { DevicesPanel, DevicesPanelProps } from '../../panels';
@@ -16,6 +16,7 @@ export interface DevicesDialogProps
16
16
 
17
17
  export const DevicesDialog = ({ slots, ...devicesDialogProps }: DevicesDialogProps) => {
18
18
  const titleId = useId('spaceDialog__title');
19
+ const themeContextValue = useThemeContext();
19
20
 
20
21
  return (
21
22
  <PanelDialog
@@ -24,7 +25,7 @@ export const DevicesDialog = ({ slots, ...devicesDialogProps }: DevicesDialogPro
24
25
  titleId
25
26
  }}
26
27
  >
27
- <ThemeContext.Provider value={{ themeVariant: 'os' }}>
28
+ <ThemeContext.Provider value={{ ...themeContextValue, themeVariant: 'os' }}>
28
29
  <DevicesPanel
29
30
  {...{
30
31
  ...devicesDialogProps,
@@ -5,7 +5,7 @@
5
5
  import { Action, Cancel } from '@radix-ui/react-alert-dialog';
6
6
  import React from 'react';
7
7
 
8
- import { ThemeContext, useId } from '@dxos/react-components';
8
+ import { ThemeContext, useId, useThemeContext } from '@dxos/react-components';
9
9
 
10
10
  import { PanelAlertDialog, PanelAlertDialogProps } from '../../layouts';
11
11
  import { JoinPanel, JoinPanelProps } from '../../panels';
@@ -16,9 +16,11 @@ export interface JoinDialogProps
16
16
 
17
17
  export const JoinDialog = ({ slots, ...joinPanelProps }: JoinDialogProps) => {
18
18
  const titleId = useId('joinDialog__title');
19
+ const themeContextValue = useThemeContext();
20
+
19
21
  return (
20
22
  <PanelAlertDialog {...{ slots, titleId }}>
21
- <ThemeContext.Provider value={{ themeVariant: 'os' }}>
23
+ <ThemeContext.Provider value={{ ...themeContextValue, themeVariant: 'os' }}>
22
24
  <JoinPanel
23
25
  {...{
24
26
  ...joinPanelProps,
@@ -5,7 +5,7 @@
5
5
  import { Close } from '@radix-ui/react-dialog';
6
6
  import React from 'react';
7
7
 
8
- import { ThemeContext, useId } from '@dxos/react-components';
8
+ import { ThemeContext, useId, useThemeContext } from '@dxos/react-components';
9
9
 
10
10
  import { PanelDialog, PanelDialogProps } from '../../layouts';
11
11
  import { SpacePanel, SpacePanelProps } from '../../panels';
@@ -16,6 +16,7 @@ export interface SpaceDialogProps
16
16
 
17
17
  export const SpaceDialog = ({ slots, ...spacePanelProps }: SpaceDialogProps) => {
18
18
  const titleId = useId('spaceDialog__title');
19
+ const themeContextValue = useThemeContext();
19
20
 
20
21
  return (
21
22
  <PanelDialog
@@ -24,7 +25,7 @@ export const SpaceDialog = ({ slots, ...spacePanelProps }: SpaceDialogProps) =>
24
25
  titleId
25
26
  }}
26
27
  >
27
- <ThemeContext.Provider value={{ themeVariant: 'os' }}>
28
+ <ThemeContext.Provider value={{ ...themeContextValue, themeVariant: 'os' }}>
28
29
  <SpacePanel
29
30
  {...{
30
31
  ...spacePanelProps,
@@ -5,7 +5,7 @@ import React from 'react';
5
5
 
6
6
  import type { Identity } from '@dxos/client';
7
7
  import { useClient } from '@dxos/react-client';
8
- import { Avatar, Button, DensityProvider, ThemeContext, useTranslation } from '@dxos/react-components';
8
+ import { Avatar, Button, DensityProvider, ThemeContext, useThemeContext, useTranslation } from '@dxos/react-components';
9
9
 
10
10
  export const IdentityPanel = ({
11
11
  identity,
@@ -16,13 +16,15 @@ export const IdentityPanel = ({
16
16
  }) => {
17
17
  const { t } = useTranslation('os');
18
18
  const client = useClient();
19
+ const themeContextValue = useThemeContext();
20
+
19
21
  const defaultManageProfile = () => {
20
22
  const remoteSource = new URL(client?.config.get('runtime.client.remoteSource') || 'https://halo.dxos.org');
21
23
  const tab = window.open(remoteSource.origin, '_blank');
22
24
  tab?.focus();
23
25
  };
24
26
  return (
25
- <ThemeContext.Provider value={{ themeVariant: 'os' }}>
27
+ <ThemeContext.Provider value={{ ...themeContextValue, themeVariant: 'os' }}>
26
28
  <DensityProvider density='fine'>
27
29
  <div className='flex flex-col gap-2 justify-center items-center'>
28
30
  <Avatar
@@ -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>
@@ -5,7 +5,7 @@ import React, { useEffect } from 'react';
5
5
 
6
6
  import { log } from '@dxos/log';
7
7
  import { useClient, useIdentity } from '@dxos/react-client';
8
- import { DensityProvider, useId } from '@dxos/react-components';
8
+ import { DensityProvider, useId, useThemeContext } from '@dxos/react-components';
9
9
 
10
10
  import { JoinHeading } from './JoinHeading';
11
11
  import { JoinPanelProps } from './JoinPanelProps';
@@ -32,6 +32,7 @@ export const JoinPanel = ({
32
32
  const client = useClient();
33
33
  const identity = useIdentity();
34
34
  const titleId = useId('joinPanel__title');
35
+ const { hasIosKeyboard } = useThemeContext();
35
36
 
36
37
  const [joinState, joinSend, joinService] = useJoinMachine(client, {
37
38
  context: {
@@ -57,10 +58,10 @@ export const JoinPanel = ({
57
58
  const innermostState = stateStack[stateStack.length - 1];
58
59
  const autoFocusValue = innermostState === 'finishingJoining' ? 'successSpaceInvitation' : innermostState;
59
60
  const $nextAutofocus: HTMLElement | null = document.querySelector(`[data-autofocus~="${autoFocusValue}"]`);
60
- if ($nextAutofocus) {
61
+ if ($nextAutofocus && !(hasIosKeyboard && $nextAutofocus.hasAttribute('data-prevent-ios-autofocus'))) {
61
62
  $nextAutofocus.focus();
62
63
  }
63
- }, [joinState.value]);
64
+ }, [joinState.value, hasIosKeyboard]);
64
65
 
65
66
  return (
66
67
  <DensityProvider density='fine'>
@@ -55,6 +55,7 @@ const PureInvitationAuthenticatorContent = ({
55
55
  autoComplete: 'off',
56
56
  pattern: '\\d*',
57
57
  'data-autofocus': `connecting${Domain}Invitation inputting${Domain}VerificationCode authenticationFailing${Domain}VerificationCode authenticating${Domain}VerificationCode`,
58
+ 'data-prevent-ios-autofocus': true,
58
59
  'data-testid': `${invitationType}-auth-code-input`,
59
60
  'data-1p-ignore': true
60
61
  } as ComponentPropsWithoutRef<'input'>
@@ -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',