@dxos/shell 0.4.10-main.fd4f2a3 → 0.4.10-main.fe71b4c
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundle/index-BjPIerZ_.mjs +110 -0
- package/dist/bundle/index-BjPIerZ_.mjs.map +1 -0
- package/dist/bundle/index-BnojJ-lm.mjs +96615 -0
- package/dist/bundle/index-BnojJ-lm.mjs.map +1 -0
- package/dist/bundle/shell.mjs +9 -81827
- package/dist/bundle/shell.mjs.map +1 -1
- package/dist/bundle/shell.umd.js +152 -89
- package/dist/bundle/shell.umd.js.map +1 -1
- package/dist/bundle/style.css +1 -1
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/react.mjs +862 -821
- package/dist/lib/browser/react.mjs.map +4 -4
- package/dist/lib/browser/testing/index.mjs +4 -2
- package/dist/lib/browser/testing/index.mjs.map +2 -2
- package/dist/types/src/components/AgentConfig.d.ts +3 -0
- package/dist/types/src/components/AgentConfig.d.ts.map +1 -0
- package/dist/types/src/components/AgentConfig.js +37 -0
- package/dist/types/src/components/AgentConfig.js.map +1 -0
- package/dist/types/src/components/DeviceList/DeviceList.d.ts +3 -0
- package/dist/types/src/components/DeviceList/DeviceList.d.ts.map +1 -0
- package/dist/types/src/components/DeviceList/DeviceList.js +29 -0
- package/dist/types/src/components/DeviceList/DeviceList.js.map +1 -0
- package/dist/types/src/components/DeviceList/DeviceListItem.d.ts +6 -0
- package/dist/types/src/components/DeviceList/DeviceListItem.d.ts.map +1 -0
- package/dist/types/src/components/DeviceList/DeviceListItem.js +72 -0
- package/dist/types/src/components/DeviceList/DeviceListItem.js.map +1 -0
- package/dist/types/src/components/DeviceList/DeviceListProps.d.ts +27 -0
- package/dist/types/src/components/DeviceList/DeviceListProps.d.ts.map +1 -0
- package/dist/types/src/components/DeviceList/DeviceListProps.js +6 -0
- package/dist/types/src/components/DeviceList/DeviceListProps.js.map +1 -0
- package/dist/types/src/components/DeviceList/index.d.ts +3 -0
- package/dist/types/src/components/DeviceList/index.d.ts.map +1 -0
- package/dist/types/src/components/DeviceList/index.js +22 -0
- package/dist/types/src/components/DeviceList/index.js.map +1 -0
- package/dist/types/src/components/EmojiPicker.d.ts +9 -2
- package/dist/types/src/components/EmojiPicker.d.ts.map +1 -1
- package/dist/types/src/components/EmojiPicker.js +57 -3
- package/dist/types/src/components/EmojiPicker.js.map +1 -1
- package/dist/types/src/components/HuePicker.d.ts +9 -2
- package/dist/types/src/components/HuePicker.d.ts.map +1 -1
- package/dist/types/src/components/HuePicker.js +75 -6
- package/dist/types/src/components/HuePicker.js.map +1 -1
- package/dist/types/src/components/IdentityList/index.d.ts +0 -1
- package/dist/types/src/components/IdentityList/index.d.ts.map +1 -1
- package/dist/types/src/components/IdentityList/index.js +0 -1
- package/dist/types/src/components/IdentityList/index.js.map +1 -1
- package/dist/types/src/components/InvitationList/InvitationListItem.js +5 -5
- package/dist/types/src/components/InvitationList/InvitationListItem.js.map +1 -1
- package/dist/types/src/components/Panel/Action.d.ts +2 -2
- package/dist/types/src/components/Viewport/Viewport.d.ts +1 -1
- package/dist/types/src/components/index.d.ts +2 -0
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/components/index.js +2 -0
- package/dist/types/src/components/index.js.map +1 -1
- package/dist/types/src/composites/Shell/Shell.d.ts.map +1 -1
- package/dist/types/src/composites/Shell/Shell.js +1 -5
- package/dist/types/src/composites/Shell/Shell.js.map +1 -1
- package/dist/types/src/panels/IdentityPanel/IdentityPanel.d.ts.map +1 -1
- package/dist/types/src/panels/IdentityPanel/IdentityPanel.js +80 -40
- package/dist/types/src/panels/IdentityPanel/IdentityPanel.js.map +1 -1
- package/dist/types/src/panels/IdentityPanel/IdentityPanel.stories.d.ts +0 -3
- package/dist/types/src/panels/IdentityPanel/IdentityPanel.stories.d.ts.map +1 -1
- package/dist/types/src/panels/IdentityPanel/IdentityPanel.stories.js +1 -43
- package/dist/types/src/panels/IdentityPanel/IdentityPanel.stories.js.map +1 -1
- package/dist/types/src/panels/IdentityPanel/IdentityPanelProps.d.ts +8 -11
- package/dist/types/src/panels/IdentityPanel/IdentityPanelProps.d.ts.map +1 -1
- package/dist/types/src/panels/IdentityPanel/identityMachine.d.ts +1 -1
- package/dist/types/src/panels/IdentityPanel/identityMachine.d.ts.map +1 -1
- package/dist/types/src/panels/IdentityPanel/identityMachine.js +0 -24
- package/dist/types/src/panels/IdentityPanel/identityMachine.js.map +1 -1
- package/dist/types/src/panels/IdentityPanel/steps/IdentityActionChooser.d.ts +4 -4
- package/dist/types/src/panels/IdentityPanel/steps/IdentityActionChooser.d.ts.map +1 -1
- package/dist/types/src/panels/IdentityPanel/steps/IdentityActionChooser.js +9 -43
- package/dist/types/src/panels/IdentityPanel/steps/IdentityActionChooser.js.map +1 -1
- package/dist/types/src/panels/IdentityPanel/steps/ProfileForm.d.ts +1 -1
- package/dist/types/src/panels/IdentityPanel/steps/ProfileForm.d.ts.map +1 -1
- package/dist/types/src/panels/IdentityPanel/steps/ProfileForm.js +2 -2
- package/dist/types/src/panels/IdentityPanel/steps/ProfileForm.js.map +1 -1
- package/dist/types/src/panels/IdentityPanel/steps/index.d.ts +0 -2
- package/dist/types/src/panels/IdentityPanel/steps/index.d.ts.map +1 -1
- package/dist/types/src/panels/IdentityPanel/steps/index.js +0 -2
- package/dist/types/src/panels/IdentityPanel/steps/index.js.map +1 -1
- package/dist/types/src/panels/IdentityPanel/useAgentHandlers.d.ts.map +1 -1
- package/dist/types/src/panels/IdentityPanel/useAgentHandlers.js +5 -1
- package/dist/types/src/panels/IdentityPanel/useAgentHandlers.js.map +1 -1
- package/dist/types/src/panels/SpacePanel/steps/SpaceManager.d.ts.map +1 -1
- package/dist/types/src/panels/SpacePanel/steps/SpaceManager.js +3 -1
- package/dist/types/src/panels/SpacePanel/steps/SpaceManager.js.map +1 -1
- package/dist/types/src/steps/InvitationManager.d.ts +1 -1
- package/dist/types/src/steps/InvitationManager.d.ts.map +1 -1
- package/dist/types/src/steps/InvitationManager.js +3 -4
- package/dist/types/src/steps/InvitationManager.js.map +1 -1
- package/dist/types/src/stories/Invitations.e2e-stories.d.ts.map +1 -1
- package/dist/types/src/stories/Invitations.e2e-stories.js +2 -2
- package/dist/types/src/stories/Invitations.e2e-stories.js.map +1 -1
- package/dist/types/src/testing/shell-manager.d.ts.map +1 -1
- package/dist/types/src/testing/shell-manager.js +4 -2
- package/dist/types/src/testing/shell-manager.js.map +1 -1
- package/dist/types/src/translations/locales/en-US.d.ts +12 -2
- package/dist/types/src/translations/locales/en-US.d.ts.map +1 -1
- package/dist/types/src/translations/locales/en-US.js +12 -2
- package/dist/types/src/translations/locales/en-US.js.map +1 -1
- package/package.json +22 -21
- package/src/components/AgentConfig.tsx +66 -0
- package/src/components/DeviceList/DeviceList.tsx +54 -0
- package/src/components/DeviceList/DeviceListItem.tsx +120 -0
- package/src/components/DeviceList/DeviceListProps.ts +33 -0
- package/src/components/DeviceList/index.ts +6 -0
- package/src/components/EmojiPicker.tsx +101 -4
- package/src/components/HuePicker.tsx +99 -5
- package/src/components/IdentityList/index.ts +0 -1
- package/src/components/InvitationList/InvitationListItem.tsx +5 -5
- package/src/components/index.ts +2 -0
- package/src/composites/Shell/Shell.tsx +1 -7
- package/src/panels/IdentityPanel/IdentityPanel.stories.tsx +0 -63
- package/src/panels/IdentityPanel/IdentityPanel.tsx +123 -34
- package/src/panels/IdentityPanel/IdentityPanelProps.ts +10 -14
- package/src/panels/IdentityPanel/identityMachine.ts +1 -31
- package/src/panels/IdentityPanel/steps/IdentityActionChooser.tsx +23 -76
- package/src/panels/IdentityPanel/steps/ProfileForm.tsx +12 -4
- package/src/panels/IdentityPanel/steps/index.ts +0 -2
- package/src/panels/IdentityPanel/useAgentHandlers.ts +5 -1
- package/src/panels/SpacePanel/steps/SpaceManager.tsx +3 -1
- package/src/steps/InvitationManager.tsx +4 -4
- package/src/stories/Invitations.e2e-stories.tsx +3 -3
- package/src/testing/shell-manager.ts +4 -2
- package/src/translations/locales/en-US.ts +13 -2
- package/dist/types/src/components/IdentityList/DeviceList.d.ts +0 -7
- package/dist/types/src/components/IdentityList/DeviceList.d.ts.map +0 -1
- package/dist/types/src/components/IdentityList/DeviceList.js +0 -34
- package/dist/types/src/components/IdentityList/DeviceList.js.map +0 -1
- package/dist/types/src/panels/IdentityPanel/steps/AgentForm.d.ts +0 -3
- package/dist/types/src/panels/IdentityPanel/steps/AgentForm.d.ts.map +0 -1
- package/dist/types/src/panels/IdentityPanel/steps/AgentForm.js +0 -26
- package/dist/types/src/panels/IdentityPanel/steps/AgentForm.js.map +0 -1
- package/dist/types/src/panels/IdentityPanel/steps/DeviceManager.d.ts +0 -10
- package/dist/types/src/panels/IdentityPanel/steps/DeviceManager.d.ts.map +0 -1
- package/dist/types/src/panels/IdentityPanel/steps/DeviceManager.js +0 -23
- package/dist/types/src/panels/IdentityPanel/steps/DeviceManager.js.map +0 -1
- package/src/components/IdentityList/DeviceList.tsx +0 -49
- package/src/panels/IdentityPanel/steps/AgentForm.tsx +0 -61
- package/src/panels/IdentityPanel/steps/DeviceManager.tsx +0 -41
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { CaretRight, Plus } from '@phosphor-icons/react';
|
|
6
|
+
import React from 'react';
|
|
7
|
+
|
|
8
|
+
import { type Device } from '@dxos/react-client/halo';
|
|
9
|
+
import { Button, List, useTranslation } from '@dxos/react-ui';
|
|
10
|
+
import { descriptionText, getSize, mx } from '@dxos/react-ui-theme';
|
|
11
|
+
|
|
12
|
+
import { DeviceListItem } from './DeviceListItem';
|
|
13
|
+
import { type DeviceListProps } from './DeviceListProps';
|
|
14
|
+
|
|
15
|
+
export const DeviceList = ({
|
|
16
|
+
devices,
|
|
17
|
+
onClickAdd,
|
|
18
|
+
onClickEdit,
|
|
19
|
+
onClickReset,
|
|
20
|
+
onClickJoinExisting,
|
|
21
|
+
}: DeviceListProps) => {
|
|
22
|
+
const { t } = useTranslation('os');
|
|
23
|
+
return (
|
|
24
|
+
<>
|
|
25
|
+
<h2 className={mx(descriptionText, 'text-center mbs-4')}>{t('devices heading')}</h2>
|
|
26
|
+
{devices.length > 0 && (
|
|
27
|
+
<List>
|
|
28
|
+
{devices.map((device: Device) => {
|
|
29
|
+
return (
|
|
30
|
+
<DeviceListItem
|
|
31
|
+
key={device.deviceKey.toHex()}
|
|
32
|
+
device={device}
|
|
33
|
+
onClickEdit={() => onClickEdit?.(device)}
|
|
34
|
+
{...{ onClickReset, onClickJoinExisting }}
|
|
35
|
+
/>
|
|
36
|
+
);
|
|
37
|
+
})}
|
|
38
|
+
</List>
|
|
39
|
+
)}
|
|
40
|
+
<Button
|
|
41
|
+
variant='ghost'
|
|
42
|
+
classNames='justify-start gap-2 !pis-0 !pie-3'
|
|
43
|
+
data-testid='devices-panel.create-invitation'
|
|
44
|
+
onClick={onClickAdd}
|
|
45
|
+
>
|
|
46
|
+
<div role='img' className={mx(getSize(8), 'm-1 rounded-sm surface-input grid place-items-center')}>
|
|
47
|
+
<Plus weight='light' className={getSize(6)} />
|
|
48
|
+
</div>
|
|
49
|
+
<span className='grow font-medium text-start'>{t('choose add device label')}</span>
|
|
50
|
+
<CaretRight weight='bold' className={getSize(4)} />
|
|
51
|
+
</Button>
|
|
52
|
+
</>
|
|
53
|
+
);
|
|
54
|
+
};
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
Compass,
|
|
7
|
+
Desktop,
|
|
8
|
+
DeviceMobile,
|
|
9
|
+
Devices,
|
|
10
|
+
DotsThree,
|
|
11
|
+
type IconProps,
|
|
12
|
+
Intersect,
|
|
13
|
+
Power,
|
|
14
|
+
Robot,
|
|
15
|
+
} from '@phosphor-icons/react';
|
|
16
|
+
import React, { type ComponentPropsWithoutRef, forwardRef } from 'react';
|
|
17
|
+
|
|
18
|
+
import { generateName } from '@dxos/display-name';
|
|
19
|
+
import { Device, DeviceType, DeviceKind } from '@dxos/react-client/halo';
|
|
20
|
+
import {
|
|
21
|
+
ListItem,
|
|
22
|
+
Avatar,
|
|
23
|
+
useId,
|
|
24
|
+
type ThemedClassName,
|
|
25
|
+
Tag,
|
|
26
|
+
useTranslation,
|
|
27
|
+
DropdownMenu,
|
|
28
|
+
Button,
|
|
29
|
+
} from '@dxos/react-ui';
|
|
30
|
+
import { getSize } from '@dxos/react-ui-theme';
|
|
31
|
+
import { keyToFallback } from '@dxos/util';
|
|
32
|
+
|
|
33
|
+
import { type DeviceListItemProps } from './DeviceListProps';
|
|
34
|
+
|
|
35
|
+
const iconProps: IconProps = {
|
|
36
|
+
weight: 'duotone',
|
|
37
|
+
width: 24,
|
|
38
|
+
height: 24,
|
|
39
|
+
x: 8,
|
|
40
|
+
y: 8,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const DeviceListItem = forwardRef<
|
|
44
|
+
HTMLLIElement,
|
|
45
|
+
ThemedClassName<ComponentPropsWithoutRef<'li'>> & DeviceListItemProps
|
|
46
|
+
>(({ device, onClickAdd, onClickEdit, onClickReset, onClickJoinExisting, classNames, ...props }, forwardedRef) => {
|
|
47
|
+
const { t } = useTranslation('os');
|
|
48
|
+
const fallbackValue = keyToFallback(device.deviceKey);
|
|
49
|
+
const labelId = useId('identityListItem__label');
|
|
50
|
+
const displayName = device.profile
|
|
51
|
+
? t('device name placeholder', { os: device.profile.os, platform: device.profile.platform })
|
|
52
|
+
: generateName(device.deviceKey.toHex());
|
|
53
|
+
const isCurrent = device.kind === DeviceKind.CURRENT;
|
|
54
|
+
return (
|
|
55
|
+
<ListItem.Root
|
|
56
|
+
{...props}
|
|
57
|
+
classNames={['flex gap-2 items-center mlb-2', classNames]}
|
|
58
|
+
data-testid={`device-list-item${isCurrent ? '-current' : ''}`}
|
|
59
|
+
labelId={labelId}
|
|
60
|
+
ref={forwardedRef}
|
|
61
|
+
>
|
|
62
|
+
<Avatar.Root
|
|
63
|
+
status={device.presence === Device.PresenceState.ONLINE ? 'active' : 'inactive'}
|
|
64
|
+
labelId={labelId}
|
|
65
|
+
hue={fallbackValue.hue}
|
|
66
|
+
variant='square'
|
|
67
|
+
>
|
|
68
|
+
<Avatar.Frame classNames='place-self-center'>
|
|
69
|
+
{device.profile?.type ? (
|
|
70
|
+
device.profile.type === DeviceType.BROWSER ? (
|
|
71
|
+
<Compass {...iconProps} />
|
|
72
|
+
) : device.profile.type === DeviceType.NATIVE ? (
|
|
73
|
+
<Desktop {...iconProps} />
|
|
74
|
+
) : [DeviceType.AGENT, DeviceType.AGENT_MANAGED].includes(device.profile.type) ? (
|
|
75
|
+
<Robot {...iconProps} />
|
|
76
|
+
) : device.profile.type === DeviceType.MOBILE ? (
|
|
77
|
+
<DeviceMobile {...iconProps} />
|
|
78
|
+
) : (
|
|
79
|
+
<Devices {...iconProps} />
|
|
80
|
+
)
|
|
81
|
+
) : (
|
|
82
|
+
<Avatar.Fallback text={fallbackValue.emoji} />
|
|
83
|
+
)}
|
|
84
|
+
</Avatar.Frame>
|
|
85
|
+
{isCurrent && <Tag color='primary'>{t('current device tag label')}</Tag>}
|
|
86
|
+
<Avatar.Label classNames='flex-1 text-sm truncate'>{displayName}</Avatar.Label>
|
|
87
|
+
{device.kind === DeviceKind.CURRENT && (
|
|
88
|
+
<DropdownMenu.Root>
|
|
89
|
+
<DropdownMenu.Trigger asChild>
|
|
90
|
+
<Button
|
|
91
|
+
variant='ghost'
|
|
92
|
+
classNames='pli-0 is-[--rail-action] bs-[--rail-action]'
|
|
93
|
+
data-testid={`device-list-item${isCurrent ? '-current' : ''}.options`}
|
|
94
|
+
>
|
|
95
|
+
<span className='sr-only'>{t('more options label')}</span>
|
|
96
|
+
<DotsThree className={getSize(5)} />
|
|
97
|
+
</Button>
|
|
98
|
+
</DropdownMenu.Trigger>
|
|
99
|
+
<DropdownMenu.Content>
|
|
100
|
+
<DropdownMenu.Viewport>
|
|
101
|
+
{/* <DropdownMenu.Item disabled onClick={onClickEdit}> */}
|
|
102
|
+
{/* <PencilSimpleLine className={getSize(5)} /> */}
|
|
103
|
+
{/* {t('edit device label')} */}
|
|
104
|
+
{/* </DropdownMenu.Item> */}
|
|
105
|
+
<DropdownMenu.Item data-testid='device-list-item-current.join-existing' onClick={onClickJoinExisting}>
|
|
106
|
+
<Intersect className={getSize(5)} />
|
|
107
|
+
{t('choose join new identity label')}
|
|
108
|
+
</DropdownMenu.Item>
|
|
109
|
+
<DropdownMenu.Item data-testid='device-list-item-current.reset' onClick={onClickReset}>
|
|
110
|
+
<Power className={getSize(5)} />
|
|
111
|
+
{t('reset device label')}
|
|
112
|
+
</DropdownMenu.Item>
|
|
113
|
+
</DropdownMenu.Viewport>
|
|
114
|
+
</DropdownMenu.Content>
|
|
115
|
+
</DropdownMenu.Root>
|
|
116
|
+
)}
|
|
117
|
+
</Avatar.Root>
|
|
118
|
+
</ListItem.Root>
|
|
119
|
+
);
|
|
120
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type SpaceMember } from '@dxos/react-client/echo';
|
|
6
|
+
import { type Device } from '@dxos/react-client/halo';
|
|
7
|
+
|
|
8
|
+
export type DeviceListProps = {
|
|
9
|
+
devices: Device[];
|
|
10
|
+
onClickAdd?: () => void;
|
|
11
|
+
onClickEdit?: (device: Device) => void;
|
|
12
|
+
onClickReset?: () => void;
|
|
13
|
+
onClickJoinExisting?: () => void;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type AgentFormProps = {
|
|
17
|
+
onAgentCreate?: () => Promise<void>;
|
|
18
|
+
onAgentDestroy?: () => Promise<void>;
|
|
19
|
+
agentStatus?: string;
|
|
20
|
+
agentActive?: boolean;
|
|
21
|
+
agentProviderDisabled?: boolean;
|
|
22
|
+
validationMessage?: string;
|
|
23
|
+
agentHostingEnabled?: boolean;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export type DeviceListItemProps = {
|
|
27
|
+
device: Device;
|
|
28
|
+
presence?: SpaceMember['presence'];
|
|
29
|
+
onClickAdd?: () => void;
|
|
30
|
+
onClickEdit?: () => void;
|
|
31
|
+
onClickReset?: () => void;
|
|
32
|
+
onClickJoinExisting?: () => void;
|
|
33
|
+
};
|
|
@@ -3,11 +3,20 @@
|
|
|
3
3
|
//
|
|
4
4
|
import emojiData from '@emoji-mart/data';
|
|
5
5
|
import EmojiMart from '@emoji-mart/react';
|
|
6
|
-
import { ArrowCounterClockwise, CaretDown } from '@phosphor-icons/react';
|
|
6
|
+
import { ArrowCounterClockwise, CaretDown, ImageSquare } from '@phosphor-icons/react';
|
|
7
7
|
import { useControllableState } from '@radix-ui/react-use-controllable-state';
|
|
8
|
-
import React, { type Dispatch, type SetStateAction, useState } from 'react';
|
|
8
|
+
import React, { type Dispatch, type SetStateAction, useRef, useState } from 'react';
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
Button,
|
|
12
|
+
Popover,
|
|
13
|
+
useTranslation,
|
|
14
|
+
Tooltip,
|
|
15
|
+
type ButtonProps,
|
|
16
|
+
Toolbar,
|
|
17
|
+
useMediaQuery,
|
|
18
|
+
type ThemedClassName,
|
|
19
|
+
} from '@dxos/react-ui';
|
|
11
20
|
import { getSize } from '@dxos/react-ui-theme';
|
|
12
21
|
|
|
13
22
|
export type EmojiPickerProps = {
|
|
@@ -18,7 +27,95 @@ export type EmojiPickerProps = {
|
|
|
18
27
|
onClickClear?: ButtonProps['onClick'];
|
|
19
28
|
};
|
|
20
29
|
|
|
21
|
-
|
|
30
|
+
/**
|
|
31
|
+
* A toolbar button for picking an emoji. Use only in `role=toolbar` elements. Unable to unset the value.
|
|
32
|
+
*/
|
|
33
|
+
export const EmojiPickerToolbarButton = ({
|
|
34
|
+
disabled,
|
|
35
|
+
defaultEmoji,
|
|
36
|
+
emoji,
|
|
37
|
+
onChangeEmoji,
|
|
38
|
+
classNames,
|
|
39
|
+
}: ThemedClassName<Omit<EmojiPickerProps, 'onClickClear'>>) => {
|
|
40
|
+
const { t } = useTranslation('os');
|
|
41
|
+
|
|
42
|
+
const [_emojiValue, setEmojiValue] = useControllableState<string>({
|
|
43
|
+
prop: emoji,
|
|
44
|
+
onChange: onChangeEmoji,
|
|
45
|
+
defaultProp: defaultEmoji,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const [emojiPickerOpen, setEmojiPickerOpen] = useState<boolean>(false);
|
|
49
|
+
|
|
50
|
+
const suppressNextTooltip = useRef<boolean>(false);
|
|
51
|
+
const [triggerTooltipOpen, setTriggerTooltipOpen] = useState(false);
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<Tooltip.Root
|
|
55
|
+
open={triggerTooltipOpen}
|
|
56
|
+
onOpenChange={(nextOpen) => {
|
|
57
|
+
if (suppressNextTooltip.current) {
|
|
58
|
+
setTriggerTooltipOpen(false);
|
|
59
|
+
suppressNextTooltip.current = false;
|
|
60
|
+
} else {
|
|
61
|
+
setTriggerTooltipOpen(nextOpen);
|
|
62
|
+
}
|
|
63
|
+
}}
|
|
64
|
+
>
|
|
65
|
+
<Popover.Root
|
|
66
|
+
open={emojiPickerOpen}
|
|
67
|
+
onOpenChange={(nextOpen) => {
|
|
68
|
+
setEmojiPickerOpen(nextOpen);
|
|
69
|
+
suppressNextTooltip.current = true;
|
|
70
|
+
}}
|
|
71
|
+
>
|
|
72
|
+
<Tooltip.Trigger asChild>
|
|
73
|
+
<Popover.Trigger asChild>
|
|
74
|
+
<Toolbar.Button classNames={['gap-2 text-2xl plb-1', classNames]} disabled={disabled}>
|
|
75
|
+
<span className='sr-only'>{t('select emoji label')}</span>
|
|
76
|
+
<ImageSquare className={getSize(5)} />
|
|
77
|
+
</Toolbar.Button>
|
|
78
|
+
</Popover.Trigger>
|
|
79
|
+
</Tooltip.Trigger>
|
|
80
|
+
<Tooltip.Portal>
|
|
81
|
+
<Tooltip.Content side='bottom' classNames='z-50'>
|
|
82
|
+
{t('select emoji label')}
|
|
83
|
+
<Tooltip.Arrow />
|
|
84
|
+
</Tooltip.Content>
|
|
85
|
+
</Tooltip.Portal>
|
|
86
|
+
<Popover.Content
|
|
87
|
+
side='bottom'
|
|
88
|
+
onKeyDownCapture={(event) => {
|
|
89
|
+
if (event.key === 'Escape') {
|
|
90
|
+
event.stopPropagation();
|
|
91
|
+
setEmojiPickerOpen(false);
|
|
92
|
+
suppressNextTooltip.current = true;
|
|
93
|
+
}
|
|
94
|
+
}}
|
|
95
|
+
>
|
|
96
|
+
<EmojiMart
|
|
97
|
+
data={emojiData}
|
|
98
|
+
onEmojiSelect={({ native }: { native?: string }) => {
|
|
99
|
+
if (native) {
|
|
100
|
+
setEmojiValue(native);
|
|
101
|
+
setEmojiPickerOpen(false);
|
|
102
|
+
}
|
|
103
|
+
}}
|
|
104
|
+
autoFocus={true}
|
|
105
|
+
maxFrequentRows={0}
|
|
106
|
+
noCountryFlags={true}
|
|
107
|
+
/>
|
|
108
|
+
<Popover.Arrow />
|
|
109
|
+
</Popover.Content>
|
|
110
|
+
</Popover.Root>
|
|
111
|
+
</Tooltip.Root>
|
|
112
|
+
);
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* A button for picking an emoji alongside a button for unsetting it.
|
|
117
|
+
*/
|
|
118
|
+
export const EmojiPickerBlock = ({ disabled, defaultEmoji, emoji, onChangeEmoji, onClickClear }: EmojiPickerProps) => {
|
|
22
119
|
const { t } = useTranslation('os');
|
|
23
120
|
const [isMd] = useMediaQuery('md', { ssr: false });
|
|
24
121
|
|
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
//
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
|
-
import { ArrowCounterClockwise, CaretDown, Check } from '@phosphor-icons/react';
|
|
4
|
+
import { ArrowCounterClockwise, CaretDown, Check, Palette } from '@phosphor-icons/react';
|
|
5
5
|
import { useControllableState } from '@radix-ui/react-use-controllable-state';
|
|
6
|
-
import React, { type Dispatch, type SetStateAction } from 'react';
|
|
6
|
+
import React, { type Dispatch, type SetStateAction, useRef, useState } from 'react';
|
|
7
7
|
|
|
8
|
-
import {
|
|
9
|
-
|
|
8
|
+
import {
|
|
9
|
+
Button,
|
|
10
|
+
useTranslation,
|
|
11
|
+
Tooltip,
|
|
12
|
+
DropdownMenu,
|
|
13
|
+
useThemeContext,
|
|
14
|
+
type ButtonProps,
|
|
15
|
+
Toolbar,
|
|
16
|
+
type ThemedClassName,
|
|
17
|
+
} from '@dxos/react-ui';
|
|
18
|
+
import { getSize, hueTokenThemes, mx } from '@dxos/react-ui-theme';
|
|
10
19
|
|
|
11
20
|
const HuePreview = ({ hue }: { hue: string }) => {
|
|
12
21
|
const { tx } = useThemeContext();
|
|
@@ -25,7 +34,92 @@ export type HuePickerProps = {
|
|
|
25
34
|
onClickClear?: ButtonProps['onClick'];
|
|
26
35
|
};
|
|
27
36
|
|
|
28
|
-
|
|
37
|
+
/**
|
|
38
|
+
* A toolbar button for picking hue. Use only in `role=toolbar` elements. Unable to unset the value.
|
|
39
|
+
*/
|
|
40
|
+
export const HuePickerToolbarButton = ({
|
|
41
|
+
disabled,
|
|
42
|
+
hue,
|
|
43
|
+
onChangeHue,
|
|
44
|
+
classNames,
|
|
45
|
+
defaultHue,
|
|
46
|
+
}: ThemedClassName<Omit<HuePickerProps, 'onClickClear'>>) => {
|
|
47
|
+
const { t } = useTranslation('os');
|
|
48
|
+
|
|
49
|
+
const [hueValue, setHueValue] = useControllableState<string>({
|
|
50
|
+
prop: hue,
|
|
51
|
+
onChange: onChangeHue,
|
|
52
|
+
defaultProp: defaultHue,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const [huePickerOpen, setHuePickerOpen] = useState<boolean>(false);
|
|
56
|
+
|
|
57
|
+
const suppressNextTooltip = useRef<boolean>(false);
|
|
58
|
+
const [triggerTooltipOpen, setTriggerTooltipOpen] = useState(false);
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<Tooltip.Root
|
|
62
|
+
open={triggerTooltipOpen}
|
|
63
|
+
onOpenChange={(nextOpen) => {
|
|
64
|
+
if (suppressNextTooltip.current) {
|
|
65
|
+
setTriggerTooltipOpen(false);
|
|
66
|
+
suppressNextTooltip.current = false;
|
|
67
|
+
} else {
|
|
68
|
+
setTriggerTooltipOpen(nextOpen);
|
|
69
|
+
}
|
|
70
|
+
}}
|
|
71
|
+
>
|
|
72
|
+
<DropdownMenu.Root
|
|
73
|
+
modal={false}
|
|
74
|
+
open={huePickerOpen}
|
|
75
|
+
onOpenChange={(nextOpen) => {
|
|
76
|
+
setHuePickerOpen(nextOpen);
|
|
77
|
+
suppressNextTooltip.current = true;
|
|
78
|
+
}}
|
|
79
|
+
>
|
|
80
|
+
<Tooltip.Trigger asChild>
|
|
81
|
+
<DropdownMenu.Trigger asChild>
|
|
82
|
+
<Toolbar.Button classNames={mx('gap-2 plb-1', classNames)} disabled={disabled}>
|
|
83
|
+
<span className='sr-only'>{t('select hue label')}</span>
|
|
84
|
+
<Palette className={getSize(5)} />
|
|
85
|
+
</Toolbar.Button>
|
|
86
|
+
</DropdownMenu.Trigger>
|
|
87
|
+
</Tooltip.Trigger>
|
|
88
|
+
<Tooltip.Portal>
|
|
89
|
+
<Tooltip.Content side='bottom' classNames='z-50'>
|
|
90
|
+
{t('select hue label')}
|
|
91
|
+
<Tooltip.Arrow />
|
|
92
|
+
</Tooltip.Content>
|
|
93
|
+
</Tooltip.Portal>
|
|
94
|
+
<DropdownMenu.Content side='bottom'>
|
|
95
|
+
<DropdownMenu.Viewport>
|
|
96
|
+
{Object.keys(hueTokenThemes).map((hue) => {
|
|
97
|
+
return (
|
|
98
|
+
<DropdownMenu.CheckboxItem
|
|
99
|
+
key={hue}
|
|
100
|
+
checked={hue === hueValue}
|
|
101
|
+
onCheckedChange={() => setHueValue(hue)}
|
|
102
|
+
>
|
|
103
|
+
<HuePreview hue={hue} />
|
|
104
|
+
<span className='grow'>{t(`${hue} label`)}</span>
|
|
105
|
+
<DropdownMenu.ItemIndicator>
|
|
106
|
+
<Check />
|
|
107
|
+
</DropdownMenu.ItemIndicator>
|
|
108
|
+
</DropdownMenu.CheckboxItem>
|
|
109
|
+
);
|
|
110
|
+
})}
|
|
111
|
+
</DropdownMenu.Viewport>
|
|
112
|
+
<DropdownMenu.Arrow />
|
|
113
|
+
</DropdownMenu.Content>
|
|
114
|
+
</DropdownMenu.Root>
|
|
115
|
+
</Tooltip.Root>
|
|
116
|
+
);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* A button for picking hue alongside a button for unsetting it.
|
|
121
|
+
*/
|
|
122
|
+
export const HuePickerBlock = ({ disabled, hue, onChangeHue, defaultHue, onClickClear }: HuePickerProps) => {
|
|
29
123
|
const { t } = useTranslation('os');
|
|
30
124
|
|
|
31
125
|
const [hueValue, setHueValue] = useControllableState<string>({
|
|
@@ -96,14 +96,14 @@ export const InvitationListItemImpl = ({
|
|
|
96
96
|
...props
|
|
97
97
|
}: InvitationListItemImplProps) => {
|
|
98
98
|
const { t } = useTranslation('os');
|
|
99
|
-
const { cancel, status: invitationStatus, invitationCode, authCode,
|
|
99
|
+
const { cancel, status: invitationStatus, invitationCode, authCode, multiUse } = propsInvitationStatus;
|
|
100
100
|
|
|
101
101
|
const isCancellable = !(
|
|
102
102
|
[Invitation.State.ERROR, Invitation.State.TIMEOUT, Invitation.State.CANCELLED].indexOf(invitationStatus) >= 0
|
|
103
103
|
);
|
|
104
104
|
|
|
105
105
|
const showShare =
|
|
106
|
-
|
|
106
|
+
multiUse ||
|
|
107
107
|
[Invitation.State.INIT, Invitation.State.CONNECTING, Invitation.State.CONNECTED].indexOf(invitationStatus) >= 0;
|
|
108
108
|
|
|
109
109
|
const showAuthCode = invitationStatus === Invitation.State.READY_FOR_AUTHENTICATION;
|
|
@@ -147,9 +147,9 @@ export const InvitationListItemImpl = ({
|
|
|
147
147
|
classNames={['flex gap-2 pis-3 pie-1 items-center relative', props.classNames]}
|
|
148
148
|
>
|
|
149
149
|
<ListItem.Heading classNames='sr-only'>
|
|
150
|
-
{t(
|
|
150
|
+
{t(multiUse ? 'invite many list item label' : 'invite one list item label')}
|
|
151
151
|
</ListItem.Heading>
|
|
152
|
-
{
|
|
152
|
+
{multiUse && (
|
|
153
153
|
<AvatarStackEffect status={avatarStatus} animation={avatarAnimation} reverseEffects={reverseEffects} />
|
|
154
154
|
)}
|
|
155
155
|
<Tooltip.Root>
|
|
@@ -162,7 +162,7 @@ export const InvitationListItemImpl = ({
|
|
|
162
162
|
</Avatar.Root>
|
|
163
163
|
<Tooltip.Portal>
|
|
164
164
|
<Tooltip.Content side='left' classNames='z-[70]'>
|
|
165
|
-
{t(
|
|
165
|
+
{t(multiUse ? 'invite many qr label' : 'invite one qr label')}
|
|
166
166
|
<Tooltip.Arrow />
|
|
167
167
|
</Tooltip.Content>
|
|
168
168
|
</Tooltip.Portal>
|
package/src/components/index.ts
CHANGED
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
export * from './AgentConfig';
|
|
5
6
|
export * from './Clipboard';
|
|
6
7
|
export * from './CompoundButton';
|
|
7
8
|
export * from './EmojiPicker';
|
|
8
9
|
export * from './HuePicker';
|
|
9
10
|
export * from './IdentityList';
|
|
11
|
+
export * from './DeviceList';
|
|
10
12
|
export * from './InvitationList';
|
|
11
13
|
export * from './Panel';
|
|
12
14
|
export * from './Viewport';
|
|
@@ -81,13 +81,7 @@ export const Shell = ({ runtime, origin }: { runtime: ShellRuntime; origin: stri
|
|
|
81
81
|
await runtime.setAppContext({ display: ShellDisplay.NONE });
|
|
82
82
|
runtime.setLayout({ layout: ShellLayout.DEFAULT });
|
|
83
83
|
}}
|
|
84
|
-
initialDisposition={
|
|
85
|
-
layout === ShellLayout.SHARE_IDENTITY
|
|
86
|
-
? 'manage-device-invitation'
|
|
87
|
-
: layout === ShellLayout.EDIT_PROFILE
|
|
88
|
-
? 'manage-profile'
|
|
89
|
-
: 'default'
|
|
90
|
-
}
|
|
84
|
+
initialDisposition={layout === ShellLayout.SHARE_IDENTITY ? 'manage-device-invitation' : 'default'}
|
|
91
85
|
/>
|
|
92
86
|
);
|
|
93
87
|
|
|
@@ -8,7 +8,6 @@ import React from 'react';
|
|
|
8
8
|
|
|
9
9
|
import { faker } from '@dxos/random';
|
|
10
10
|
import { PublicKey } from '@dxos/react-client';
|
|
11
|
-
import { Device, DeviceKind } from '@dxos/react-client/halo';
|
|
12
11
|
import { Invitation } from '@dxos/react-client/invitations';
|
|
13
12
|
import { withTheme } from '@dxos/storybook-utils';
|
|
14
13
|
|
|
@@ -53,58 +52,6 @@ export const IdentityActionChooser = () => {
|
|
|
53
52
|
);
|
|
54
53
|
};
|
|
55
54
|
|
|
56
|
-
export const UpdateProfileForm = () => {
|
|
57
|
-
return (
|
|
58
|
-
<StorybookDialog inOverlayLayout>
|
|
59
|
-
<IdentityPanelImpl
|
|
60
|
-
{...noOpProps}
|
|
61
|
-
activeView='update profile form'
|
|
62
|
-
IdentityActionChooser={IdentityActionChooserImpl}
|
|
63
|
-
/>
|
|
64
|
-
</StorybookDialog>
|
|
65
|
-
);
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
export const DeviceManager = () => {
|
|
69
|
-
return (
|
|
70
|
-
<StorybookDialog inOverlayLayout>
|
|
71
|
-
<IdentityPanelImpl
|
|
72
|
-
{...{
|
|
73
|
-
...noOpProps,
|
|
74
|
-
// TODO(nf): update
|
|
75
|
-
devices: [
|
|
76
|
-
{
|
|
77
|
-
deviceKey: PublicKey.random(),
|
|
78
|
-
kind: DeviceKind.CURRENT,
|
|
79
|
-
presence: Device.PresenceState.ONLINE,
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
deviceKey: PublicKey.random(),
|
|
83
|
-
kind: DeviceKind.TRUSTED,
|
|
84
|
-
presence: Device.PresenceState.ONLINE,
|
|
85
|
-
},
|
|
86
|
-
{
|
|
87
|
-
deviceKey: PublicKey.random(),
|
|
88
|
-
kind: DeviceKind.TRUSTED,
|
|
89
|
-
presence: Device.PresenceState.OFFLINE,
|
|
90
|
-
},
|
|
91
|
-
],
|
|
92
|
-
}}
|
|
93
|
-
activeView='device manager'
|
|
94
|
-
IdentityActionChooser={IdentityActionChooserImpl}
|
|
95
|
-
/>
|
|
96
|
-
</StorybookDialog>
|
|
97
|
-
);
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
export const DeviceManagerEmpty = () => {
|
|
101
|
-
return (
|
|
102
|
-
<StorybookDialog inOverlayLayout>
|
|
103
|
-
<IdentityPanelImpl {...noOpProps} activeView='device manager' IdentityActionChooser={IdentityActionChooserImpl} />
|
|
104
|
-
</StorybookDialog>
|
|
105
|
-
);
|
|
106
|
-
};
|
|
107
|
-
|
|
108
55
|
const DeviceInvitationManagerWithState = (extraProps: InvitationManagerProps) => (
|
|
109
56
|
<StorybookDialog inOverlayLayout>
|
|
110
57
|
<IdentityPanelImpl
|
|
@@ -156,13 +103,3 @@ export const DeviceInvitationManagerCancelled = () =>
|
|
|
156
103
|
DeviceInvitationManagerWithState({
|
|
157
104
|
status: Invitation.State.CANCELLED,
|
|
158
105
|
});
|
|
159
|
-
|
|
160
|
-
// export const IdentityActionChooser = {
|
|
161
|
-
// decorators: [ClientDecorator()],
|
|
162
|
-
// args: { activeView: 'identity action chooser' },
|
|
163
|
-
// };
|
|
164
|
-
|
|
165
|
-
// export const DeviceManager = {
|
|
166
|
-
// decorators: [ClientDecorator()],
|
|
167
|
-
// args: { activeView: 'device manager' },
|
|
168
|
-
// };
|