@arcblock/ux 2.11.50 → 2.12.0

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.
@@ -0,0 +1,5 @@
1
+ import type { SxProps } from '@mui/material';
2
+ export default function CloseButton({ onClose, sx }: {
3
+ onClose?: () => void;
4
+ sx?: SxProps;
5
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,29 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Box } from '@mui/material';
3
+ import { Icon } from '@iconify/react';
4
+ import CloseRoundedIcon from '@iconify-icons/material-symbols/close-rounded';
5
+ import noop from 'lodash/noop';
6
+ import { temp as colors } from '../Colors';
7
+ import { mergeSx } from '../Util/style';
8
+ export default function CloseButton({
9
+ onClose = noop,
10
+ sx
11
+ }) {
12
+ return /*#__PURE__*/_jsx(Box, {
13
+ onClick: onClose,
14
+ sx: mergeSx({
15
+ fontSize: 0,
16
+ cursor: 'pointer',
17
+ color: colors.foregroundsFgSubtile,
18
+ transition: 'transform 0.25s ease-in-out 0s',
19
+ transformOrigin: 'center',
20
+ '&:hover': {
21
+ transform: 'rotate(90deg)'
22
+ }
23
+ }, sx),
24
+ children: /*#__PURE__*/_jsx(Icon, {
25
+ icon: CloseRoundedIcon,
26
+ fontSize: 20
27
+ })
28
+ });
29
+ }
@@ -1,2 +1,3 @@
1
1
  export { default } from './themes/default';
2
2
  export { default as temp } from './themes/temp';
3
+ export { default as didConnectColors } from './themes/did-connect';
@@ -1,3 +1,4 @@
1
1
  // eslint-disable-next-line no-restricted-exports
2
2
  export { default } from './themes/default';
3
- export { default as temp } from './themes/temp';
3
+ export { default as temp } from './themes/temp';
4
+ export { default as didConnectColors } from './themes/did-connect';
@@ -0,0 +1,55 @@
1
+ declare const colors: {
2
+ background: {
3
+ overlay: string;
4
+ default: string;
5
+ paper0: string;
6
+ paper1: string;
7
+ paper2: string;
8
+ };
9
+ text: {
10
+ primary: string;
11
+ secondary: string;
12
+ disable: string;
13
+ hint: string;
14
+ };
15
+ state: {
16
+ hover: string;
17
+ selected: string;
18
+ item: string;
19
+ itemSelected: string;
20
+ };
21
+ primary: {
22
+ main: string;
23
+ light: string;
24
+ dark: string;
25
+ };
26
+ secondary: {
27
+ main: string;
28
+ light: string;
29
+ dark: string;
30
+ };
31
+ error: {
32
+ main: string;
33
+ light: string;
34
+ dark: string;
35
+ };
36
+ warning: {
37
+ main: string;
38
+ light: string;
39
+ dark: string;
40
+ };
41
+ info: {
42
+ main: string;
43
+ light: string;
44
+ dark: string;
45
+ };
46
+ success: {
47
+ main: string;
48
+ light: string;
49
+ dark: string;
50
+ };
51
+ divider: {
52
+ main: string;
53
+ };
54
+ };
55
+ export default colors;
@@ -0,0 +1,55 @@
1
+ const colors = {
2
+ background: {
3
+ overlay: 'rgba(15, 23, 42, 0.5)',
4
+ default: 'white',
5
+ paper0: 'white',
6
+ paper1: '#FAFAFA',
7
+ paper2: '#F4F4F5'
8
+ },
9
+ text: {
10
+ primary: '#18181B',
11
+ secondary: '#71717B',
12
+ disable: '#D4D4D8',
13
+ hint: '#D4D4D8'
14
+ },
15
+ state: {
16
+ hover: '#FAFAFA',
17
+ selected: '#F4F4F5',
18
+ item: '#F4F4F5',
19
+ itemSelected: '#E4E4E7'
20
+ },
21
+ primary: {
22
+ main: '#2B7FFF',
23
+ light: '#51A2FF',
24
+ dark: '#155DFC'
25
+ },
26
+ secondary: {
27
+ main: '#615FFF',
28
+ light: '#7C86FF',
29
+ dark: '#4F39F6'
30
+ },
31
+ error: {
32
+ main: '#FB2C36',
33
+ light: '#FF6467',
34
+ dark: '#E7000B'
35
+ },
36
+ warning: {
37
+ main: '#FF6900',
38
+ light: '#FF8904',
39
+ dark: '#F54900'
40
+ },
41
+ info: {
42
+ main: '#2B7FFF',
43
+ light: '#51A2FF',
44
+ dark: '#155DFC'
45
+ },
46
+ success: {
47
+ main: '#00C950',
48
+ light: '#05DF72',
49
+ dark: '#00A63E'
50
+ },
51
+ divider: {
52
+ main: '#F4F4F5'
53
+ }
54
+ };
55
+ export default colors;
@@ -31,6 +31,7 @@ dayjs.updateLocale('zh-cn', {
31
31
  yy: '%d 年'
32
32
  }
33
33
  });
34
+ // FIXME: @@ 此处不能真正的将 relativeTime 设置为支持中文
34
35
  setDateTool(dayjs);
35
36
  export default function RelativeTime({
36
37
  value,
@@ -0,0 +1,19 @@
1
+ export default function QuickLoginItem({ userSession, locale, onClick, }: {
2
+ onClick?: () => void;
3
+ locale?: string;
4
+ userSession: {
5
+ user: {
6
+ avatar: string;
7
+ did: string;
8
+ fullName: string;
9
+ role: string;
10
+ roleTitle: string;
11
+ email: string;
12
+ };
13
+ extra: {
14
+ walletOS: string;
15
+ provider: string;
16
+ };
17
+ updatedAt: string;
18
+ };
19
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,122 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import DID from '@arcblock/ux/lib/DID';
3
+ import RelativeTime from '@arcblock/ux/lib/RelativeTime';
4
+ import WalletOSIcon from '@arcblock/ux/lib/WalletOSIcon';
5
+ import { Box, Chip, Typography } from '@mui/material';
6
+ import { temp as colors } from '@arcblock/ux/lib/Colors';
7
+ import noop from 'lodash/noop';
8
+ import Avatar from '../../Avatar';
9
+ import { getSourceProvider } from '../libs/utils';
10
+ export default function QuickLoginItem({
11
+ userSession,
12
+ locale = 'en',
13
+ onClick = noop
14
+ }) {
15
+ const isRawWalletAccount = getSourceProvider(userSession?.user) === 'wallet';
16
+ return /*#__PURE__*/_jsxs(Box, {
17
+ sx: {
18
+ borderRadius: 2,
19
+ p: 2,
20
+ transition: 'background-color 0.5s',
21
+ '&:hover, &:active': {
22
+ backgroundColor: colors.backgroundsBgSubtileHover
23
+ },
24
+ display: 'flex',
25
+ justifyContent: 'space-between',
26
+ alignItems: 'center',
27
+ cursor: 'pointer',
28
+ '&:hover': {
29
+ backgroundColor: colors.surfacePrimarySubtitle
30
+ },
31
+ width: '100%',
32
+ backgroundColor: 'white'
33
+ },
34
+ onClick: onClick,
35
+ children: [/*#__PURE__*/_jsxs(Box, {
36
+ sx: {
37
+ display: 'flex',
38
+ alignItems: 'center',
39
+ gap: 1.5,
40
+ flex: 1,
41
+ overflow: 'hidden'
42
+ },
43
+ children: [/*#__PURE__*/_jsx(Avatar, {
44
+ src: userSession.user.avatar,
45
+ did: userSession.user.did,
46
+ size: 44,
47
+ variant: "circle",
48
+ shape: "circle"
49
+ }), /*#__PURE__*/_jsxs(Box, {
50
+ sx: {
51
+ flex: 1,
52
+ overflow: 'hidden'
53
+ },
54
+ children: [/*#__PURE__*/_jsxs(Typography, {
55
+ sx: {
56
+ fontSize: '16px',
57
+ fontWeight: 500,
58
+ display: 'flex',
59
+ alignItems: 'center',
60
+ gap: 1,
61
+ color: colors.textBase
62
+ },
63
+ children: [userSession.user.fullName, /*#__PURE__*/_jsx(Chip, {
64
+ label: userSession.user?.roleTitle || userSession.user?.role,
65
+ size: "small",
66
+ variant: "outlined",
67
+ sx: () => ({
68
+ height: 'auto',
69
+ fontWeight: 'bold',
70
+ fontSize: '12px',
71
+ borderColor: colors.lineBorderStrong,
72
+ color: colors.textSubtitle
73
+ })
74
+ })]
75
+ }), isRawWalletAccount ? /*#__PURE__*/_jsxs(Box, {
76
+ sx: {
77
+ display: 'flex',
78
+ alignItems: 'center',
79
+ gap: 0.5
80
+ },
81
+ children: [/*#__PURE__*/_jsx(WalletOSIcon, {
82
+ provider: userSession?.extra?.provider,
83
+ walletOS: userSession?.extra?.walletOS,
84
+ color: colors.textSubtitle
85
+ }), /*#__PURE__*/_jsx(DID
86
+ // @ts-ignore
87
+ , {
88
+ locale: locale,
89
+ did: userSession.user.did,
90
+ size: 12,
91
+ sx: {
92
+ lineHeight: 1,
93
+ fontSize: '12px',
94
+ mt: 0.5,
95
+ '& span.arc-avatar-did-motif': {
96
+ display: 'none !important'
97
+ }
98
+ },
99
+ compact: true
100
+ })]
101
+ }) : /*#__PURE__*/_jsx(Typography, {
102
+ sx: {
103
+ fontSize: '12px',
104
+ color: colors.textSubtitle
105
+ },
106
+ children: userSession.user.email
107
+ })]
108
+ })]
109
+ }), userSession.updatedAt && /*#__PURE__*/_jsx(Box, {
110
+ component: "span",
111
+ sx: {
112
+ color: colors.textMuted,
113
+ fontSize: '12px',
114
+ flexShrink: 0
115
+ },
116
+ children: /*#__PURE__*/_jsx(RelativeTime, {
117
+ value: userSession.updatedAt,
118
+ locale: locale
119
+ })
120
+ })]
121
+ });
122
+ }
@@ -4,5 +4,6 @@ export interface UnLoginProps {
4
4
  onLogin?: () => void;
5
5
  size?: number;
6
6
  dark?: false | true;
7
+ locale?: string;
7
8
  }
8
- export default function UnLogin({ session, onLogin, size, dark }: UnLoginProps): import("react/jsx-runtime").JSX.Element;
9
+ export default function UnLogin({ session, onLogin, size, dark, locale }: UnLoginProps): import("react/jsx-runtime").JSX.Element;
@@ -1,53 +1,196 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { Box, CircularProgress, IconButton } from '@mui/material';
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { Box, CircularProgress, Divider, Fade, IconButton, LinearProgress, MenuItem, MenuList, Paper, Popper } from '@mui/material';
3
3
  import { Icon } from '@iconify/react';
4
4
  import UserIcon from '@iconify-icons/tabler/user';
5
- import { useRef } from 'react';
6
- import { useMemoizedFn } from 'ahooks';
5
+ import { useEffect, useRef } from 'react';
6
+ import { useMemoizedFn, useReactive } from 'ahooks';
7
7
  import noop from 'lodash/noop';
8
+ import DIDWalletIcon from '@arcblock/icons/lib/DidWallet';
9
+ import { temp as colors, didConnectColors } from '../../Colors';
10
+ import CloseButton from '../../CloseButton';
11
+ import { translate } from '../../Locale/util';
12
+ import { translations } from '../libs/translation';
13
+ import Typography from '../../Typography';
14
+ import QuickLoginItem from './quick-login-item';
8
15
  export default function UnLogin({
9
16
  session,
10
17
  onLogin = noop,
11
18
  size = 24,
12
- dark = false
19
+ dark = false,
20
+ locale = 'en'
13
21
  }) {
22
+ const t = useMemoizedFn((key, data = {}) => {
23
+ return translate(translations, key, locale, 'en', data);
24
+ });
14
25
  const isFirstLoading = false;
15
26
  const userAnchorRef = useRef(null);
27
+ const currentState = useReactive({
28
+ open: false,
29
+ userSessions: [],
30
+ loadingSessionId: null
31
+ });
32
+ const onTogglePopper = useMemoizedFn((value = !currentState.open) => {
33
+ currentState.open = value;
34
+ });
16
35
  const _onLogin = useMemoizedFn(() => {
17
36
  if (isFirstLoading) {
18
37
  return;
19
38
  }
20
39
  session.login(onLogin);
21
40
  });
22
- return /*#__PURE__*/_jsx(Box, {
23
- children: /*#__PURE__*/_jsx(IconButton, {
24
- ref: userAnchorRef,
25
- onClick: _onLogin,
26
- "data-cy": "sessionManager-login",
27
- className: "arc-session-user-unlogin",
28
- size: "medium",
29
- "aria-label": "Login button",
30
- children: isFirstLoading ? /*#__PURE__*/_jsx(Box, {
31
- width: size,
32
- height: size,
33
- display: "flex",
34
- justifyContent: "center",
35
- alignItems: "center",
36
- children: /*#__PURE__*/_jsx(CircularProgress, {
41
+ useEffect(() => {
42
+ const timer = setTimeout(async () => {
43
+ currentState.userSessions = await session.getUserSessions();
44
+ if (currentState.userSessions.length > 0) {
45
+ currentState.open = true;
46
+ }
47
+ }, 1500);
48
+ return () => {
49
+ clearTimeout(timer);
50
+ };
51
+ // eslint-disable-next-line react-hooks/exhaustive-deps
52
+ }, []);
53
+ const handleLogin = useMemoizedFn(async userSession => {
54
+ currentState.loadingSessionId = userSession.id;
55
+ try {
56
+ await session.loginUserSession({
57
+ ...userSession,
58
+ appPid: blocklet?.appPid
59
+ });
60
+ } catch (err) {
61
+ console.error('session.loginUserSession.error', err);
62
+ } finally {
63
+ currentState.loadingSessionId = null;
64
+ }
65
+ });
66
+ return /*#__PURE__*/_jsxs(_Fragment, {
67
+ children: [/*#__PURE__*/_jsx(Box, {
68
+ children: /*#__PURE__*/_jsx(IconButton, {
69
+ ref: userAnchorRef,
70
+ onClick: _onLogin,
71
+ "data-cy": "sessionManager-login",
72
+ className: "arc-session-user-unlogin",
73
+ size: "medium",
74
+ "aria-label": "Login button",
75
+ children: isFirstLoading ? /*#__PURE__*/_jsx(Box, {
76
+ width: size,
77
+ height: size,
78
+ display: "flex",
79
+ justifyContent: "center",
80
+ alignItems: "center",
81
+ children: /*#__PURE__*/_jsx(CircularProgress, {
82
+ style: {
83
+ width: size - 4,
84
+ height: size - 4,
85
+ color: dark ? '#fff' : ''
86
+ }
87
+ })
88
+ }) : /*#__PURE__*/_jsx(Icon, {
89
+ icon: UserIcon,
90
+ fontSize: size,
91
+ color: dark ? '#fff' : 'inherit',
37
92
  style: {
38
- width: size - 4,
39
- height: size - 4,
40
- color: dark ? '#fff' : ''
93
+ transform: 'scale(1.25)'
41
94
  }
42
95
  })
43
- }) : /*#__PURE__*/_jsx(Icon, {
44
- icon: UserIcon,
45
- fontSize: size,
46
- color: dark ? '#fff' : 'inherit',
47
- style: {
48
- transform: 'scale(1.25)'
96
+ })
97
+ }), /*#__PURE__*/_jsx(Popper, {
98
+ open: currentState.open,
99
+ anchorEl: userAnchorRef.current
100
+ // @ts-expect-error
101
+ ,
102
+ dark: dark,
103
+ transition: true,
104
+ placement: "bottom-end",
105
+ sx: {
106
+ zIndex: 1100
107
+ },
108
+ modifiers: [{
109
+ name: 'offset',
110
+ options: {
111
+ offset: [0, 10] // [水平偏移, 垂直偏移]
49
112
  }
113
+ }],
114
+ children: ({
115
+ TransitionProps
116
+ }) => /*#__PURE__*/_jsx(Fade, {
117
+ ...TransitionProps,
118
+ children: /*#__PURE__*/_jsxs(Paper, {
119
+ variant: "outlined",
120
+ sx: {
121
+ borderRadius: 2,
122
+ width: 400,
123
+ maxWidth: '90vw',
124
+ borderColor: colors.lineStep,
125
+ border: '0 !important',
126
+ boxShadow: `0px 8px 16px 0px ${colors.gray6}, 0px 0px 0px 1px ${colors.gray6}`
127
+ },
128
+ children: [/*#__PURE__*/_jsxs(Box, {
129
+ sx: {
130
+ display: 'flex',
131
+ alignItems: 'center',
132
+ gap: 2,
133
+ p: 2,
134
+ borderBottom: '1px solid #eee'
135
+ },
136
+ children: [/*#__PURE__*/_jsx(DIDWalletIcon, {
137
+ width: 24,
138
+ height: 24
139
+ }), /*#__PURE__*/_jsx(Typography, {
140
+ fontSize: "auto",
141
+ sx: {
142
+ fontSize: '16',
143
+ flex: 1,
144
+ color: didConnectColors.text.secondary
145
+ },
146
+ children: t('useQuickLogin')
147
+ }), /*#__PURE__*/_jsx(CloseButton, {
148
+ onClose: () => onTogglePopper(false)
149
+ })]
150
+ }), /*#__PURE__*/_jsx(MenuList, {
151
+ sx: {
152
+ p: 0,
153
+ overflow: 'hidden'
154
+ },
155
+ children: currentState.userSessions.map((userSessionItem, index) => {
156
+ const isLoading = currentState.loadingSessionId === userSessionItem.id;
157
+ return /*#__PURE__*/_jsxs(_Fragment, {
158
+ children: [/*#__PURE__*/_jsxs(MenuItem, {
159
+ sx: {
160
+ overflow: 'hidden',
161
+ position: 'relative',
162
+ p: 0,
163
+ '&:hover': {
164
+ backgroundColor: `${colors.surfacePrimarySubtitle} !important`
165
+ }
166
+ },
167
+ onClick: () => {
168
+ handleLogin(userSessionItem);
169
+ },
170
+ children: [isLoading && /*#__PURE__*/_jsx(LinearProgress, {
171
+ sx: {
172
+ height: '2px',
173
+ position: 'absolute',
174
+ top: 0,
175
+ left: 0,
176
+ right: 0
177
+ }
178
+ }), /*#__PURE__*/_jsx(QuickLoginItem, {
179
+ userSession: userSessionItem,
180
+ locale: locale
181
+ })]
182
+ }, userSessionItem.id), index < currentState.userSessions.length - 1 ? /*#__PURE__*/_jsx(Divider, {
183
+ sx: {
184
+ mx: 2,
185
+ my: '0px !important',
186
+ borderColor: '#e4e4e7'
187
+ }
188
+ }) : null]
189
+ });
190
+ })
191
+ })]
192
+ })
50
193
  })
51
- })
194
+ })]
52
195
  });
53
196
  }
@@ -23,6 +23,7 @@ export default function SessionUser({
23
23
  }
24
24
  return /*#__PURE__*/_jsx(UnLogin, {
25
25
  session: session,
26
+ locale: locale,
26
27
  size: size
27
28
  });
28
29
  }
@@ -10,7 +10,8 @@ export const translations = {
10
10
  dashboard: 'Dashboard',
11
11
  logout: 'Sign Out',
12
12
  addAnotherAccount: 'Add another account',
13
- disconnected: 'Disconnected'
13
+ disconnected: 'Disconnected',
14
+ useQuickLogin: 'Use DID Connect account to login quickly'
14
15
  },
15
16
  zh: {
16
17
  connectDIDWallet: '连接你的 DID Wallet 获得更高的安全性',
@@ -21,6 +22,7 @@ export const translations = {
21
22
  dashboard: '控制台',
22
23
  logout: '退出登录',
23
24
  addAnotherAccount: '添加账户',
24
- disconnected: '未连接'
25
+ disconnected: '未连接',
26
+ useQuickLogin: '使用 DID Connect 账号快速登录'
25
27
  }
26
28
  };
@@ -95,7 +95,9 @@ export default function Typography({
95
95
  ...rest,
96
96
  sx: {
97
97
  ...sx,
98
- fontSize: `${state.fontSize}px !important`,
98
+ ...(state.fontSize ? {
99
+ fontSize: `${state.fontSize}px !important`
100
+ } : {}),
99
101
  position: 'fixed',
100
102
  top: -1000,
101
103
  left: -1000
@@ -107,7 +109,9 @@ export default function Typography({
107
109
  ...rest,
108
110
  sx: {
109
111
  ...sx,
110
- fontSize: `${state.fontSize}px !important`
112
+ ...(state.fontSize ? {
113
+ fontSize: `${state.fontSize}px !important`
114
+ } : {})
111
115
  },
112
116
  children: children
113
117
  });
@@ -0,0 +1,2 @@
1
+ import type { SxProps } from '@mui/material';
2
+ export declare const mergeSx: (...sxList: Array<SxProps | undefined | null>) => SxProps;
@@ -0,0 +1,14 @@
1
+ /* eslint-disable import/prefer-default-export */
2
+
3
+ export const mergeSx = (...sxList) => {
4
+ const mergedSx = sxList.reduce((acc, sx) => {
5
+ if (!sx) {
6
+ return acc;
7
+ }
8
+ if (Array.isArray(sx)) {
9
+ return acc.concat(sx);
10
+ }
11
+ return acc.concat([sx]);
12
+ }, []);
13
+ return mergedSx;
14
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arcblock/ux",
3
- "version": "2.11.50",
3
+ "version": "2.12.0",
4
4
  "description": "Common used react components for arcblock products",
5
5
  "keywords": [
6
6
  "react",
@@ -68,12 +68,12 @@
68
68
  "react": ">=18.2.0",
69
69
  "react-router-dom": ">=6.22.3"
70
70
  },
71
- "gitHead": "f969739a55ab3a6c178b15b8e2aa736ef4956fd0",
71
+ "gitHead": "ce9042b3a31a06b60ab4d3b923e8b5903bbbf106",
72
72
  "dependencies": {
73
73
  "@arcblock/did-motif": "^1.1.13",
74
- "@arcblock/icons": "^2.11.50",
75
- "@arcblock/nft-display": "^2.11.50",
76
- "@arcblock/react-hooks": "^2.11.50",
74
+ "@arcblock/icons": "^2.12.0",
75
+ "@arcblock/nft-display": "^2.12.0",
76
+ "@arcblock/react-hooks": "^2.12.0",
77
77
  "@babel/plugin-syntax-dynamic-import": "^7.8.3",
78
78
  "@fontsource/inter": "^5.0.16",
79
79
  "@fontsource/ubuntu-mono": "^5.0.18",
@@ -0,0 +1,30 @@
1
+ import { Box } from '@mui/material';
2
+ import type { SxProps } from '@mui/material';
3
+ import { Icon } from '@iconify/react';
4
+ import CloseRoundedIcon from '@iconify-icons/material-symbols/close-rounded';
5
+ import noop from 'lodash/noop';
6
+
7
+ import { temp as colors } from '../Colors';
8
+ import { mergeSx } from '../Util/style';
9
+
10
+ export default function CloseButton({ onClose = noop, sx }: { onClose?: () => void; sx?: SxProps }) {
11
+ return (
12
+ <Box
13
+ onClick={onClose}
14
+ sx={mergeSx(
15
+ {
16
+ fontSize: 0,
17
+ cursor: 'pointer',
18
+ color: colors.foregroundsFgSubtile,
19
+ transition: 'transform 0.25s ease-in-out 0s',
20
+ transformOrigin: 'center',
21
+ '&:hover': {
22
+ transform: 'rotate(90deg)',
23
+ },
24
+ },
25
+ sx
26
+ )}>
27
+ <Icon icon={CloseRoundedIcon} fontSize={20} />
28
+ </Box>
29
+ );
30
+ }
@@ -1,3 +1,4 @@
1
1
  // eslint-disable-next-line no-restricted-exports
2
2
  export { default } from './themes/default';
3
3
  export { default as temp } from './themes/temp';
4
+ export { default as didConnectColors } from './themes/did-connect';
@@ -0,0 +1,56 @@
1
+ const colors = {
2
+ background: {
3
+ overlay: 'rgba(15, 23, 42, 0.5)',
4
+ default: 'white',
5
+ paper0: 'white',
6
+ paper1: '#FAFAFA',
7
+ paper2: '#F4F4F5',
8
+ },
9
+ text: {
10
+ primary: '#18181B',
11
+ secondary: '#71717B',
12
+ disable: '#D4D4D8',
13
+ hint: '#D4D4D8',
14
+ },
15
+ state: {
16
+ hover: '#FAFAFA',
17
+ selected: '#F4F4F5',
18
+ item: '#F4F4F5',
19
+ itemSelected: '#E4E4E7',
20
+ },
21
+ primary: {
22
+ main: '#2B7FFF',
23
+ light: '#51A2FF',
24
+ dark: '#155DFC',
25
+ },
26
+ secondary: {
27
+ main: '#615FFF',
28
+ light: '#7C86FF',
29
+ dark: '#4F39F6',
30
+ },
31
+ error: {
32
+ main: '#FB2C36',
33
+ light: '#FF6467',
34
+ dark: '#E7000B',
35
+ },
36
+ warning: {
37
+ main: '#FF6900',
38
+ light: '#FF8904',
39
+ dark: '#F54900',
40
+ },
41
+ info: {
42
+ main: '#2B7FFF',
43
+ light: '#51A2FF',
44
+ dark: '#155DFC',
45
+ },
46
+ success: {
47
+ main: '#00C950',
48
+ light: '#05DF72',
49
+ dark: '#00A63E',
50
+ },
51
+ divider: {
52
+ main: '#F4F4F5',
53
+ },
54
+ };
55
+
56
+ export default colors;
@@ -32,6 +32,7 @@ dayjs.updateLocale('zh-cn', {
32
32
  yy: '%d 年',
33
33
  },
34
34
  });
35
+ // FIXME: @@ 此处不能真正的将 relativeTime 设置为支持中文
35
36
  setDateTool(dayjs);
36
37
 
37
38
  export interface RelativeTimeProps {
@@ -0,0 +1,131 @@
1
+ import PropTypes from 'prop-types';
2
+ import DID from '@arcblock/ux/lib/DID';
3
+ import RelativeTime from '@arcblock/ux/lib/RelativeTime';
4
+ import WalletOSIcon from '@arcblock/ux/lib/WalletOSIcon';
5
+ import { Box, Chip, Typography } from '@mui/material';
6
+ import { temp as colors } from '@arcblock/ux/lib/Colors';
7
+ import noop from 'lodash/noop';
8
+
9
+ import Avatar from '../../Avatar';
10
+ import { getSourceProvider } from '../libs/utils';
11
+
12
+ export default function QuickLoginItem({
13
+ userSession,
14
+ locale = 'en',
15
+ onClick = noop,
16
+ }: {
17
+ onClick?: () => void;
18
+ locale?: string;
19
+ userSession: {
20
+ user: {
21
+ avatar: string;
22
+ did: string;
23
+ fullName: string;
24
+ role: string;
25
+ roleTitle: string;
26
+ email: string;
27
+ };
28
+ extra: {
29
+ walletOS: string;
30
+ provider: string;
31
+ };
32
+ updatedAt: string;
33
+ };
34
+ }) {
35
+ const isRawWalletAccount = getSourceProvider(userSession?.user) === 'wallet';
36
+
37
+ return (
38
+ <Box
39
+ sx={{
40
+ borderRadius: 2,
41
+ p: 2,
42
+ transition: 'background-color 0.5s',
43
+ '&:hover, &:active': {
44
+ backgroundColor: colors.backgroundsBgSubtileHover,
45
+ },
46
+ display: 'flex',
47
+ justifyContent: 'space-between',
48
+ alignItems: 'center',
49
+ cursor: 'pointer',
50
+ '&:hover': {
51
+ backgroundColor: colors.surfacePrimarySubtitle,
52
+ },
53
+ width: '100%',
54
+ backgroundColor: 'white',
55
+ }}
56
+ onClick={onClick}>
57
+ <Box
58
+ sx={{
59
+ display: 'flex',
60
+ alignItems: 'center',
61
+ gap: 1.5,
62
+ flex: 1,
63
+ overflow: 'hidden',
64
+ }}>
65
+ <Avatar src={userSession.user.avatar} did={userSession.user.did} size={44} variant="circle" shape="circle" />
66
+ <Box sx={{ flex: 1, overflow: 'hidden' }}>
67
+ <Typography
68
+ sx={{
69
+ fontSize: '16px',
70
+ fontWeight: 500,
71
+ display: 'flex',
72
+ alignItems: 'center',
73
+ gap: 1,
74
+ color: colors.textBase,
75
+ }}>
76
+ {userSession.user.fullName}
77
+ <Chip
78
+ label={userSession.user?.roleTitle || userSession.user?.role}
79
+ size="small"
80
+ variant="outlined"
81
+ sx={() => ({
82
+ height: 'auto',
83
+ fontWeight: 'bold',
84
+ fontSize: '12px',
85
+ borderColor: colors.lineBorderStrong,
86
+ color: colors.textSubtitle,
87
+ })}
88
+ />
89
+ </Typography>
90
+ {isRawWalletAccount ? (
91
+ <Box
92
+ sx={{
93
+ display: 'flex',
94
+ alignItems: 'center',
95
+ gap: 0.5,
96
+ }}>
97
+ <WalletOSIcon
98
+ provider={userSession?.extra?.provider}
99
+ walletOS={userSession?.extra?.walletOS}
100
+ color={colors.textSubtitle}
101
+ />
102
+ <DID
103
+ // @ts-ignore
104
+ locale={locale}
105
+ did={userSession.user.did}
106
+ size={12}
107
+ sx={{
108
+ lineHeight: 1,
109
+ fontSize: '12px',
110
+ mt: 0.5,
111
+ '& span.arc-avatar-did-motif': {
112
+ display: 'none !important',
113
+ },
114
+ }}
115
+ compact
116
+ />
117
+ </Box>
118
+ ) : (
119
+ <Typography sx={{ fontSize: '12px', color: colors.textSubtitle }}>{userSession.user.email}</Typography>
120
+ )}
121
+ </Box>
122
+ </Box>
123
+ {userSession.updatedAt && (
124
+ <Box component="span" sx={{ color: colors.textMuted, fontSize: '12px', flexShrink: 0 }}>
125
+ {/* @ts-ignore */}
126
+ <RelativeTime value={userSession.updatedAt} locale={locale} />
127
+ </Box>
128
+ )}
129
+ </Box>
130
+ );
131
+ }
@@ -1,22 +1,52 @@
1
- import { Box, CircularProgress, IconButton } from '@mui/material';
1
+ import {
2
+ Box,
3
+ CircularProgress,
4
+ Divider,
5
+ Fade,
6
+ IconButton,
7
+ LinearProgress,
8
+ MenuItem,
9
+ MenuList,
10
+ Paper,
11
+ Popper,
12
+ } from '@mui/material';
2
13
  import { Icon } from '@iconify/react';
3
14
  import UserIcon from '@iconify-icons/tabler/user';
4
- import { useRef } from 'react';
5
- import { useMemoizedFn } from 'ahooks';
15
+ import { useEffect, useRef } from 'react';
16
+ import { useMemoizedFn, useReactive } from 'ahooks';
6
17
  import noop from 'lodash/noop';
18
+ import DIDWalletIcon from '@arcblock/icons/lib/DidWallet';
7
19
 
8
20
  import { Session } from '../../type';
21
+ import { temp as colors, didConnectColors } from '../../Colors';
22
+ import CloseButton from '../../CloseButton';
23
+ import { translate } from '../../Locale/util';
24
+ import { translations } from '../libs/translation';
25
+ import Typography from '../../Typography';
26
+ import QuickLoginItem from './quick-login-item';
9
27
 
10
28
  export interface UnLoginProps {
11
29
  session: Session;
12
30
  onLogin?: () => void;
13
31
  size?: number;
14
32
  dark?: false | true;
33
+ locale?: string;
15
34
  }
16
35
 
17
- export default function UnLogin({ session, onLogin = noop, size = 24, dark = false }: UnLoginProps) {
36
+ export default function UnLogin({ session, onLogin = noop, size = 24, dark = false, locale = 'en' }: UnLoginProps) {
37
+ const t = useMemoizedFn((key, data = {}) => {
38
+ return translate(translations, key, locale, 'en', data);
39
+ });
18
40
  const isFirstLoading = false;
19
41
  const userAnchorRef = useRef(null);
42
+ const currentState = useReactive({
43
+ open: false,
44
+ userSessions: [] as Session[],
45
+ loadingSessionId: null,
46
+ });
47
+ const onTogglePopper = useMemoizedFn((value = !currentState.open) => {
48
+ currentState.open = value;
49
+ });
20
50
  const _onLogin = useMemoizedFn(() => {
21
51
  if (isFirstLoading) {
22
52
  return;
@@ -24,28 +54,152 @@ export default function UnLogin({ session, onLogin = noop, size = 24, dark = fal
24
54
  session.login(onLogin);
25
55
  });
26
56
 
57
+ useEffect(() => {
58
+ const timer = setTimeout(async () => {
59
+ currentState.userSessions = await session.getUserSessions();
60
+ if (currentState.userSessions.length > 0) {
61
+ currentState.open = true;
62
+ }
63
+ }, 1500);
64
+
65
+ return () => {
66
+ clearTimeout(timer);
67
+ };
68
+ // eslint-disable-next-line react-hooks/exhaustive-deps
69
+ }, []);
70
+
71
+ const handleLogin = useMemoizedFn(async (userSession) => {
72
+ currentState.loadingSessionId = userSession.id;
73
+ try {
74
+ await session.loginUserSession({ ...userSession, appPid: blocklet?.appPid });
75
+ } catch (err) {
76
+ console.error('session.loginUserSession.error', err);
77
+ } finally {
78
+ currentState.loadingSessionId = null;
79
+ }
80
+ });
81
+
27
82
  return (
28
- <Box>
29
- <IconButton
30
- ref={userAnchorRef}
31
- onClick={_onLogin}
32
- data-cy="sessionManager-login"
33
- className="arc-session-user-unlogin"
34
- size="medium"
35
- aria-label="Login button">
36
- {isFirstLoading ? (
37
- <Box width={size} height={size} display="flex" justifyContent="center" alignItems="center">
38
- <CircularProgress style={{ width: size - 4, height: size - 4, color: dark ? '#fff' : '' }} />
39
- </Box>
40
- ) : (
41
- <Icon
42
- icon={UserIcon}
43
- fontSize={size}
44
- color={dark ? '#fff' : 'inherit'}
45
- style={{ transform: 'scale(1.25)' }}
46
- />
83
+ <>
84
+ <Box>
85
+ <IconButton
86
+ ref={userAnchorRef}
87
+ onClick={_onLogin}
88
+ data-cy="sessionManager-login"
89
+ className="arc-session-user-unlogin"
90
+ size="medium"
91
+ aria-label="Login button">
92
+ {isFirstLoading ? (
93
+ <Box width={size} height={size} display="flex" justifyContent="center" alignItems="center">
94
+ <CircularProgress style={{ width: size - 4, height: size - 4, color: dark ? '#fff' : '' }} />
95
+ </Box>
96
+ ) : (
97
+ <Icon
98
+ icon={UserIcon}
99
+ fontSize={size}
100
+ color={dark ? '#fff' : 'inherit'}
101
+ style={{ transform: 'scale(1.25)' }}
102
+ />
103
+ )}
104
+ </IconButton>
105
+ </Box>
106
+ <Popper
107
+ open={currentState.open}
108
+ anchorEl={userAnchorRef.current}
109
+ // @ts-expect-error
110
+ dark={dark}
111
+ transition
112
+ placement="bottom-end"
113
+ sx={{ zIndex: 1100 }}
114
+ modifiers={[
115
+ {
116
+ name: 'offset',
117
+ options: {
118
+ offset: [0, 10], // [水平偏移, 垂直偏移]
119
+ },
120
+ },
121
+ ]}>
122
+ {({ TransitionProps }) => (
123
+ <Fade {...TransitionProps}>
124
+ <Paper
125
+ variant="outlined"
126
+ sx={{
127
+ borderRadius: 2,
128
+ width: 400,
129
+ maxWidth: '90vw',
130
+ borderColor: colors.lineStep,
131
+ border: '0 !important',
132
+ boxShadow: `0px 8px 16px 0px ${colors.gray6}, 0px 0px 0px 1px ${colors.gray6}`,
133
+ }}>
134
+ <Box
135
+ sx={{
136
+ display: 'flex',
137
+ alignItems: 'center',
138
+ gap: 2,
139
+ p: 2,
140
+ borderBottom: '1px solid #eee',
141
+ }}>
142
+ <DIDWalletIcon width={24} height={24} />
143
+ <Typography
144
+ fontSize="auto"
145
+ sx={{
146
+ fontSize: '16',
147
+ flex: 1,
148
+ color: didConnectColors.text.secondary,
149
+ }}>
150
+ {t('useQuickLogin')}
151
+ </Typography>
152
+ <CloseButton onClose={() => onTogglePopper(false)} />
153
+ </Box>
154
+ <MenuList sx={{ p: 0, overflow: 'hidden' }}>
155
+ {currentState.userSessions.map((userSessionItem, index) => {
156
+ const isLoading = currentState.loadingSessionId === userSessionItem.id;
157
+
158
+ return (
159
+ <>
160
+ <MenuItem
161
+ key={userSessionItem.id}
162
+ sx={{
163
+ overflow: 'hidden',
164
+ position: 'relative',
165
+ p: 0,
166
+ '&:hover': {
167
+ backgroundColor: `${colors.surfacePrimarySubtitle} !important`,
168
+ },
169
+ }}
170
+ onClick={() => {
171
+ handleLogin(userSessionItem);
172
+ }}>
173
+ {isLoading && (
174
+ <LinearProgress
175
+ sx={{
176
+ height: '2px',
177
+ position: 'absolute',
178
+ top: 0,
179
+ left: 0,
180
+ right: 0,
181
+ }}
182
+ />
183
+ )}
184
+ <QuickLoginItem userSession={userSessionItem as unknown as any} locale={locale} />
185
+ </MenuItem>
186
+ {index < currentState.userSessions.length - 1 ? (
187
+ <Divider
188
+ sx={{
189
+ mx: 2,
190
+ my: '0px !important',
191
+ borderColor: '#e4e4e7',
192
+ }}
193
+ />
194
+ ) : null}
195
+ </>
196
+ );
197
+ })}
198
+ </MenuList>
199
+ </Paper>
200
+ </Fade>
47
201
  )}
48
- </IconButton>
49
- </Box>
202
+ </Popper>
203
+ </>
50
204
  );
51
205
  }
@@ -22,5 +22,5 @@ export default function SessionUser({ session, onBindWallet = noop, locale = 'en
22
22
  <LoggedIn isBlocklet={isBlocklet} session={session} onBindWallet={onBindWallet} locale={locale} size={size} />
23
23
  );
24
24
  }
25
- return <UnLogin session={session} size={size} />;
25
+ return <UnLogin session={session} locale={locale} size={size} />;
26
26
  }
@@ -13,6 +13,7 @@ export const translations: Translations = {
13
13
  logout: 'Sign Out',
14
14
  addAnotherAccount: 'Add another account',
15
15
  disconnected: 'Disconnected',
16
+ useQuickLogin: 'Use DID Connect account to login quickly',
16
17
  },
17
18
  zh: {
18
19
  connectDIDWallet: '连接你的 DID Wallet 获得更高的安全性',
@@ -24,5 +25,6 @@ export const translations: Translations = {
24
25
  logout: '退出登录',
25
26
  addAnotherAccount: '添加账户',
26
27
  disconnected: '未连接',
28
+ useQuickLogin: '使用 DID Connect 账号快速登录',
27
29
  },
28
30
  };
@@ -98,7 +98,7 @@ export default function Typography({
98
98
  {...rest}
99
99
  sx={{
100
100
  ...sx,
101
- fontSize: `${state.fontSize}px !important`,
101
+ ...(state.fontSize ? { fontSize: `${state.fontSize}px !important` } : {}),
102
102
  position: 'fixed',
103
103
  top: -1000,
104
104
  left: -1000,
@@ -112,7 +112,7 @@ export default function Typography({
112
112
  {...rest}
113
113
  sx={{
114
114
  ...sx,
115
- fontSize: `${state.fontSize}px !important`,
115
+ ...(state.fontSize ? { fontSize: `${state.fontSize}px !important` } : {}),
116
116
  }}>
117
117
  {children}
118
118
  </MuiTypography>
@@ -0,0 +1,17 @@
1
+ /* eslint-disable import/prefer-default-export */
2
+ import type { SxProps } from '@mui/material';
3
+
4
+ export const mergeSx = (...sxList: Array<SxProps | undefined | null>) => {
5
+ const mergedSx = sxList.reduce((acc, sx) => {
6
+ if (!sx) {
7
+ return acc;
8
+ }
9
+
10
+ if (Array.isArray(sx)) {
11
+ return acc.concat(sx);
12
+ }
13
+
14
+ return acc.concat([sx]);
15
+ }, [] as SxProps[]);
16
+ return mergedSx as SxProps;
17
+ };