@arcblock/ux 2.9.13 → 2.9.14

Sign up to get free protection for your applications and to get access to all the features.
Files changed (149) hide show
  1. package/es/Address/did-address.js +18 -13
  2. package/es/Colors/themes/default.js +0 -1
  3. package/es/Colors/themes/temp.js +10 -1
  4. package/es/ContactForm/index.js +2 -2
  5. package/es/DID/index.js +0 -1
  6. package/es/Dialog/confirm.js +1 -99
  7. package/es/Dialog/dialog.js +4 -7
  8. package/es/Dialog/index.js +1 -1
  9. package/es/Dialog/use-confirm.js +105 -0
  10. package/es/Locale/util.js +10 -5
  11. package/es/NFTDisplay/index.js +0 -1
  12. package/es/Passport/index.js +2 -0
  13. package/es/Passport/passport.js +103 -0
  14. package/es/SessionUser/components/logged-in.js +1 -1
  15. package/es/SessionUser/components/user-info.js +1 -1
  16. package/es/SessionUser/index.js +2 -2
  17. package/es/Tabs/index.js +88 -2
  18. package/es/Util/passport.js +62 -0
  19. package/lib/ActionButton/index.js +6 -7
  20. package/lib/ActivityIndicator/index.js +4 -4
  21. package/lib/Address/compact-text.js +6 -6
  22. package/lib/Address/did-address.js +25 -21
  23. package/lib/Address/index.js +4 -4
  24. package/lib/Address/responsive-did-address.js +4 -4
  25. package/lib/Alert/index.js +5 -6
  26. package/lib/AnimationWaiter/index.js +4 -4
  27. package/lib/Async/index.js +6 -6
  28. package/lib/Avatar/did-motif.js +5 -6
  29. package/lib/Avatar/etherscan-blockies.js +2 -3
  30. package/lib/Avatar/index.js +4 -4
  31. package/lib/Badge/index.js +5 -6
  32. package/lib/Blocklet/blocklet.js +4 -4
  33. package/lib/Blocklet/index.js +3 -4
  34. package/lib/Blocklet/utils.js +3 -5
  35. package/lib/BlockletContext/index.js +1 -2
  36. package/lib/BlockletNFT/index.js +4 -4
  37. package/lib/Button/index.js +1 -2
  38. package/lib/Button/wrap.js +4 -4
  39. package/lib/ButtonGroup/index.js +1 -2
  40. package/lib/ClickToCopy/copy-button.js +4 -4
  41. package/lib/ClickToCopy/index.js +6 -6
  42. package/lib/CodeBlock/LightBox.js +1 -2
  43. package/lib/CodeBlock/index.js +4 -4
  44. package/lib/Colors/themes/default.js +1 -2
  45. package/lib/Colors/themes/temp.js +11 -3
  46. package/lib/ContactForm/index.js +2 -2
  47. package/lib/CookieConsent/index.js +6 -6
  48. package/lib/CountDown/index.js +4 -4
  49. package/lib/DID/index.js +7 -9
  50. package/lib/Datatable/CustomToolbar.js +2 -2
  51. package/lib/Datatable/index.js +49 -49
  52. package/lib/Datatable/utils.js +2 -3
  53. package/lib/Dialog/confirm.js +5 -113
  54. package/lib/Dialog/dialog.js +14 -18
  55. package/lib/Dialog/index.js +3 -4
  56. package/lib/Dialog/use-confirm.js +126 -0
  57. package/lib/Earth/index.js +7 -8
  58. package/lib/Empty/index.js +5 -6
  59. package/lib/Header/auto-hidden.js +5 -6
  60. package/lib/Header/header.js +5 -6
  61. package/lib/Header/responsive-header.js +5 -6
  62. package/lib/Icon/image.js +4 -4
  63. package/lib/Icon/index.js +6 -7
  64. package/lib/Img/index.js +5 -6
  65. package/lib/InfoRow/index.js +5 -6
  66. package/lib/Layout/dashboard/external-link.js +4 -4
  67. package/lib/Layout/dashboard/full-page.js +5 -6
  68. package/lib/Layout/dashboard/index.js +4 -4
  69. package/lib/Layout/dashboard/sidebar.js +5 -6
  70. package/lib/Layout/dashboard-legacy/header.js +4 -4
  71. package/lib/Layout/dashboard-legacy/index.js +4 -4
  72. package/lib/Layout/dashboard-legacy/sidebar.js +4 -4
  73. package/lib/Layout/index.js +4 -4
  74. package/lib/Locale/browser-lang.js +1 -2
  75. package/lib/Locale/context.js +6 -8
  76. package/lib/Locale/languages.js +7 -9
  77. package/lib/Locale/selector.js +4 -4
  78. package/lib/Locale/util.js +12 -11
  79. package/lib/Logo/index.js +4 -4
  80. package/lib/NFTDisplay/aspect-ratio-container.js +5 -6
  81. package/lib/NFTDisplay/broken.js +4 -4
  82. package/lib/NFTDisplay/index.js +5 -7
  83. package/lib/NFTDisplay/svg-embedder/img.js +5 -6
  84. package/lib/NFTDisplay/svg-embedder/inline-svg.js +5 -6
  85. package/lib/NavMenu/nav-menu.js +5 -6
  86. package/lib/NavMenu/style.js +2 -4
  87. package/lib/PageScroller/index.js +6 -7
  88. package/lib/Passport/index.js +9 -0
  89. package/lib/Passport/passport.js +116 -0
  90. package/lib/PoweredByArcBlock/index.js +4 -4
  91. package/lib/PricingTable/PricingPlan.js +1 -2
  92. package/lib/PricingTable/index.js +1 -2
  93. package/lib/QRCode/index.js +5 -6
  94. package/lib/RelativeTime/index.js +4 -4
  95. package/lib/Result/common.js +4 -4
  96. package/lib/Result/index.js +7 -8
  97. package/lib/Result/result.js +5 -6
  98. package/lib/Result/translations.js +2 -3
  99. package/lib/Screenshot/BaseScreenshot/index.js +5 -6
  100. package/lib/Screenshot/BaseScreenshot/shells/Macbook.js +10 -15
  101. package/lib/Screenshot/BaseScreenshot/shells/Phone.js +10 -15
  102. package/lib/Screenshot/index.js +5 -6
  103. package/lib/SessionBlocklet/index.js +4 -4
  104. package/lib/SessionManager/index.js +1 -2
  105. package/lib/SessionUser/components/logged-in.js +5 -5
  106. package/lib/SessionUser/components/session-user-item.js +5 -6
  107. package/lib/SessionUser/components/session-user-switch.js +4 -4
  108. package/lib/SessionUser/components/user-info.js +1 -1
  109. package/lib/SessionUser/index.js +2 -2
  110. package/lib/SessionUser/libs/translation.js +2 -3
  111. package/lib/Spinner/index.js +13 -14
  112. package/lib/SplitButton/index.js +4 -4
  113. package/lib/Switch/index.js +5 -6
  114. package/lib/Tabs/index.js +98 -11
  115. package/lib/Tag/index.js +5 -6
  116. package/lib/TextCollapse/index.js +5 -6
  117. package/lib/Theme/index.js +6 -7
  118. package/lib/Theme/theme.js +6 -8
  119. package/lib/Toast/index.js +12 -13
  120. package/lib/Typography/index.js +4 -4
  121. package/lib/Util/constant.js +10 -20
  122. package/lib/Util/deprecate.js +4 -4
  123. package/lib/Util/index.js +2 -4
  124. package/lib/Util/passport.js +72 -0
  125. package/lib/Util/wallet.js +1 -2
  126. package/lib/Video/index.js +4 -4
  127. package/lib/Wallet/Action.js +4 -4
  128. package/lib/Wallet/Download.js +4 -4
  129. package/lib/WebWalletSWKeeper/index.js +5 -6
  130. package/lib/withTheme/index.js +5 -6
  131. package/lib/withTracker/error_boundary.js +2 -2
  132. package/lib/withTracker/index.js +8 -9
  133. package/package.json +9 -4
  134. package/src/Address/did-address.jsx +15 -12
  135. package/src/Colors/themes/temp.js +9 -0
  136. package/src/Dialog/confirm.jsx +94 -0
  137. package/src/Dialog/{dialog.js → dialog.jsx} +14 -7
  138. package/src/Dialog/index.js +1 -1
  139. package/src/Dialog/use-confirm.jsx +114 -0
  140. package/src/Locale/util.js +12 -5
  141. package/src/Passport/index.jsx +3 -0
  142. package/src/Passport/passport.jsx +99 -0
  143. package/src/SessionUser/components/logged-in.jsx +1 -1
  144. package/src/SessionUser/components/user-info.jsx +1 -1
  145. package/src/SessionUser/index.jsx +1 -1
  146. package/src/Tabs/index.jsx +124 -0
  147. package/src/Util/passport.js +75 -0
  148. package/src/Dialog/confirm.js +0 -201
  149. package/src/Tabs/index.js +0 -45
@@ -92,20 +92,12 @@ const DidAddress = forwardRef(
92
92
  return (
93
93
  <Root as={component} size={size} {...rest} ref={ref}>
94
94
  {prepend}
95
+ <Box sx={{ display: 'none' }} ref={textRef}>
96
+ {children}
97
+ </Box>
95
98
  {/* 注意: 该元素必须渲染(可以隐藏), 以便 compact 模式下复制的文本是完整的 */}
96
99
  <Tooltip title={copyable ? '' : translations[locale].copied} placement="bottom" arrow open={copied}>
97
- <Box
98
- ref={textRef}
99
- component="span"
100
- className="did-address-text did-address-truncate"
101
- sx={{
102
- display: compact ? 'none' : 'inline',
103
- cursor: copyable ? 'text' : 'pointer',
104
- }}
105
- onDoubleClick={copyable ? noop : onCopy}>
106
- {children}
107
- </Box>
108
- {compact && (
100
+ {compact ? (
109
101
  <Box
110
102
  component="span"
111
103
  className="did-address-text"
@@ -120,6 +112,17 @@ const DidAddress = forwardRef(
120
112
  {children}
121
113
  </CompactText>
122
114
  </Box>
115
+ ) : (
116
+ <Box
117
+ component="span"
118
+ className="did-address-text did-address-truncate"
119
+ sx={{
120
+ display: compact ? 'none' : 'inline',
121
+ cursor: copyable ? 'text' : 'pointer',
122
+ }}
123
+ onDoubleClick={copyable ? noop : onCopy}>
124
+ {children}
125
+ </Box>
123
126
  )}
124
127
  </Tooltip>
125
128
  {copyElement}
@@ -13,6 +13,15 @@ const colors = {
13
13
  primaryBase: 'rgba(19, 125, 250, 1)',
14
14
  primary100: 'rgba(19, 125, 250, 1)',
15
15
  gray6: 'rgba(17, 22, 24, 0.06)',
16
+ backgroundsBgSubtitle: 'rgba(249, 250, 251, 1)',
17
+ backgroundsBgComponent: 'rgba(241, 243, 245, 1)',
18
+ backgroundsBgField: 'rgba(249, 250, 251, 1)',
19
+ foregroundsFgBase: 'rgba(3, 7, 18, 1)',
20
+ foregroundsFgSubtile: 'rgba(75, 85, 99, 1)',
21
+ strokeBorderBase: 'rgba(229, 231, 235, 1)',
22
+ strokeBorderStrong: 'rgba(209, 213, 219, 1)',
23
+ buttonsButtonNeutral: 'rgba(255, 255, 255, 1)',
24
+ buttonsButtonNeutralHover: 'rgba(243, 244, 246, 1)',
16
25
  };
17
26
 
18
27
  export default colors;
@@ -0,0 +1,94 @@
1
+ import PropTypes from 'prop-types';
2
+
3
+ import Button from '../Button';
4
+ import Dialog from './dialog';
5
+
6
+ /**
7
+ @typedef {Object} ConfirmProps
8
+ @property {boolean} open
9
+ @property {React.ReactNode} title
10
+ @property {React.ReactNode} children
11
+ @property {() => void | Promise<void>} onConfirm
12
+ @property {() => void | Promise<void>} onCancel
13
+ @property {boolean} [showCancelButton=true]
14
+ @property {{text: string, props?: import('../Button/wrap').ButtonProps}} [confirmButton={text: 'Confirm'}]
15
+ @property {{text: string, props?: import('../Button/wrap').ButtonProps}} [cancelButton={text: 'Cancel'}]
16
+ @property {import('@mui/material').PaperProps} [PaperProps={}]
17
+ */
18
+
19
+ // 注意排在 {...rest} 之后的 props 优先级更高
20
+ /**
21
+ *
22
+ * @param {ConfirmProps} props
23
+ * @returns {import('react').ReactComponentElement}
24
+ */
25
+ export default function Confirm({
26
+ title,
27
+ children,
28
+ onConfirm,
29
+ onCancel,
30
+ showCancelButton,
31
+ confirmButton,
32
+ cancelButton,
33
+ PaperProps,
34
+ ...rest
35
+ }) {
36
+ // 去除 dialog 默认的 300 最小高度
37
+ PaperProps.style = Object.assign(
38
+ {
39
+ minHeight: 0,
40
+ },
41
+ PaperProps.style
42
+ );
43
+
44
+ return (
45
+ <Dialog
46
+ title={title}
47
+ PaperProps={PaperProps}
48
+ {...rest}
49
+ onClose={() => onCancel()}
50
+ actions={
51
+ <>
52
+ {showCancelButton && (
53
+ <Button onClick={() => onCancel()} color="primary" {...cancelButton.props}>
54
+ {cancelButton.text}
55
+ </Button>
56
+ )}
57
+ <Button onClick={() => onConfirm()} color="primary" autoFocus {...confirmButton.props}>
58
+ {confirmButton.text}
59
+ </Button>
60
+ </>
61
+ }>
62
+ {children}
63
+ </Dialog>
64
+ );
65
+ }
66
+
67
+ Confirm.propTypes = {
68
+ title: PropTypes.node.isRequired,
69
+ children: PropTypes.node.isRequired,
70
+ onConfirm: PropTypes.func.isRequired,
71
+ onCancel: PropTypes.func.isRequired,
72
+ showCancelButton: PropTypes.bool,
73
+ // 可以传入 {text: ..., props: ...}
74
+ confirmButton: PropTypes.shape({
75
+ text: PropTypes.string.isRequired,
76
+ props: PropTypes.object,
77
+ }),
78
+ cancelButton: PropTypes.shape({
79
+ text: PropTypes.string.isRequired,
80
+ props: PropTypes.object,
81
+ }),
82
+ PaperProps: PropTypes.object,
83
+ };
84
+
85
+ Confirm.defaultProps = {
86
+ showCancelButton: true,
87
+ confirmButton: {
88
+ text: 'Confirm',
89
+ },
90
+ cancelButton: {
91
+ text: 'Cancel',
92
+ },
93
+ PaperProps: {},
94
+ };
@@ -1,10 +1,13 @@
1
1
  import PropTypes from 'prop-types';
2
- import MuiDialog from '@mui/material/Dialog';
3
- import MuiDialogContent from '@mui/material/DialogContent';
4
- import DialogActions from '@mui/material/DialogActions';
5
- import IconButton from '@mui/material/IconButton';
6
- import useMediaQuery from '@mui/material/useMediaQuery';
7
2
  import CloseIcon from '@mui/icons-material/Close';
3
+ import {
4
+ Typography,
5
+ Dialog as MuiDialog,
6
+ DialogContent as MuiDialogContent,
7
+ DialogActions,
8
+ IconButton,
9
+ useMediaQuery,
10
+ } from '@mui/material';
8
11
 
9
12
  import { styled, useTheme } from '../Theme';
10
13
 
@@ -25,7 +28,7 @@ import { styled, useTheme } from '../Theme';
25
28
  @property {boolean} [showCloseButton=true] - Whether or not to show the close button.
26
29
  @property {'left'|'center'|'right'} [actionsPosition='right'] - The position of the actions toolbar.
27
30
  @property {PaperStyle} [PaperProps] - Props to be passed down to the dialog paper.
28
- @property {(event: React.SyntheticEvent, reason: string) => void} [onClose] - Callback function fired when the dialog is closed.
31
+ @property {(event: React.SyntheticEvent, reason: string) => void} [onClose] - Callback function fired when the dialog is closed.
29
32
  */
30
33
 
31
34
  /**
@@ -74,7 +77,11 @@ function Dialog({ children, title, prepend, toolbar, actions, showCloseButton, a
74
77
  <div className="ux-dialog_left">
75
78
  {showCloseButton && isMobile && closeButton}
76
79
  {prepend && <div className="ux-dialog_prepend">{prepend}</div>}
77
- {title && <h6 className="ux-dialog_title">{title}</h6>}
80
+ {title && (
81
+ <Typography variant="h6" className="ux-dialog_title">
82
+ {title}
83
+ </Typography>
84
+ )}
78
85
  </div>
79
86
 
80
87
  <div className="ux-dialog_right">
@@ -1,4 +1,4 @@
1
1
  // eslint-disable-next-line no-restricted-exports
2
2
  export { default } from './dialog';
3
3
  export { default as Confirm } from './confirm';
4
- export { useConfirm } from './confirm';
4
+ export { default as useConfirm } from './use-confirm';
@@ -0,0 +1,114 @@
1
+ import { forwardRef, useImperativeHandle, useRef, useState } from 'react';
2
+ import { useMemoizedFn, useReactive } from 'ahooks';
3
+ import noop from 'lodash/noop';
4
+
5
+ import Confirm from './confirm';
6
+
7
+ const ConfirmHolder = forwardRef((props, ref) => {
8
+ // HACK: 这里默认值不使用 null,来避免开发环境中的字段必填警告
9
+ const [title, setTitle] = useState('');
10
+ const [content, setContent] = useState('');
11
+ const state = useReactive({
12
+ show: false,
13
+ onConfirm: noop,
14
+ onCancel: noop,
15
+ loading: false,
16
+ confirmButtonText: 'Confirm',
17
+ cancelButtonText: 'Cancel',
18
+ });
19
+ const open = useMemoizedFn((params = {}) => {
20
+ setTitle(params.title);
21
+ setContent(params.content);
22
+ state.onConfirm = params.onConfirm || noop;
23
+ state.onCancel = params.onCancel || noop;
24
+ state.showCancelButton = params.showCancelButton ?? true;
25
+ if (params.confirmButtonText) state.confirmButtonText = params.confirmButtonText;
26
+ if (params.cancelButtonText) state.cancelButtonText = params.cancelButtonText;
27
+
28
+ state.loading = false;
29
+ state.show = true;
30
+ });
31
+ const reset = useMemoizedFn(() => {
32
+ setTitle('');
33
+ setContent('');
34
+ state.onConfirm = noop;
35
+ state.onCancel = noop;
36
+ state.confirmButtonText = 'Confirm';
37
+ state.cancelButtonText = 'Cancel';
38
+ });
39
+ const close = useMemoizedFn(() => {
40
+ state.show = false;
41
+ setTimeout(() => {
42
+ reset();
43
+ }, 300);
44
+ });
45
+ const onCancel = useMemoizedFn(() => {
46
+ close();
47
+ state?.onCancel();
48
+ }, []);
49
+ const onConfirm = useMemoizedFn(async () => {
50
+ state.loading = true;
51
+ try {
52
+ await state?.onConfirm(close);
53
+ } finally {
54
+ state.loading = false;
55
+ }
56
+ }, []);
57
+ useImperativeHandle(
58
+ ref,
59
+ () => {
60
+ return {
61
+ open,
62
+ close,
63
+ };
64
+ },
65
+ [open, close]
66
+ );
67
+
68
+ return (
69
+ <Confirm
70
+ {...props}
71
+ open={state.show}
72
+ title={title}
73
+ onConfirm={onConfirm}
74
+ onCancel={onCancel}
75
+ confirmButton={{
76
+ text: state.confirmButtonText,
77
+ props: {
78
+ variant: 'contained',
79
+ color: 'primary',
80
+ loading: state.loading,
81
+ },
82
+ }}
83
+ showCancelButton={state.showCancelButton}
84
+ cancelButton={{
85
+ text: state.cancelButtonText,
86
+ props: {
87
+ variant: 'outlined',
88
+ color: 'primary',
89
+ },
90
+ }}>
91
+ {content}
92
+ </Confirm>
93
+ );
94
+ });
95
+
96
+ export default function useConfirm(props = {}) {
97
+ const confirmRef = useRef(null);
98
+
99
+ const open = useMemoizedFn((...args) => {
100
+ confirmRef.current?.open(...args);
101
+ });
102
+ const close = useMemoizedFn((...args) => {
103
+ confirmRef.current?.close(...args);
104
+ });
105
+ const confirmApi = {
106
+ open,
107
+ close,
108
+ };
109
+
110
+ return {
111
+ confirmHolder: <ConfirmHolder {...props} ref={confirmRef} />,
112
+ confirmApi,
113
+ };
114
+ }
@@ -1,19 +1,26 @@
1
+ import get from 'lodash/get';
2
+
1
3
  /* eslint-disable no-prototype-builtins */
2
4
  export const replace = (template, data) =>
3
5
  template.replace(/{(\w*)}/g, (m, key) => (data.hasOwnProperty(key) ? data[key] : ''));
4
6
 
5
7
  export const translate = (translations, key, locale, fallbackLocale = 'en', data = {}) => {
6
- if (!translations[locale] || !translations[locale][key]) {
8
+ const translation = translations[locale];
9
+ const translationValue = get(translation, key);
10
+ const fallbackValue = get(translations[fallbackLocale], key);
11
+
12
+ if (!translation || !translationValue) {
7
13
  console.warn(`Warning: no ${key} translation of ${locale}`);
8
- if (fallbackLocale && translations[fallbackLocale]?.[key]) {
9
- return replace(translations[fallbackLocale]?.[key], data);
14
+ if (fallbackLocale && fallbackValue) {
15
+ return replace(fallbackValue, data);
10
16
  }
11
17
  return key;
12
18
  }
19
+
13
20
  if (typeof translations[locale] === 'function' || translations[locale].then) {
14
- return replace(translations[fallbackLocale][key], data);
21
+ return replace(fallbackValue, data);
15
22
  }
16
- return replace(translations[locale][key], data);
23
+ return replace(translationValue, data);
17
24
  };
18
25
 
19
26
  export const t = translate;
@@ -0,0 +1,3 @@
1
+ import PassportItem from './passport';
2
+
3
+ export default PassportItem;
@@ -0,0 +1,99 @@
1
+ import PropTypes from 'prop-types';
2
+ import upperFirst from 'lodash/upperFirst';
3
+ import { Box } from '@mui/material';
4
+ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
5
+ import RevokeIcon from '@arcblock/icons/lib/RevokeIcon';
6
+
7
+ export default function Passport({ passport, user, color, width, icon, children, createPassportSvg, ...rest }) {
8
+ const { t } = useLocaleContext();
9
+ return (
10
+ <Box
11
+ {...rest}
12
+ sx={{
13
+ display: 'flex',
14
+ alignItems: 'center',
15
+ ...rest?.sx,
16
+ }}>
17
+ <Box
18
+ className="passport-item__display"
19
+ // eslint-disable-next-line react/no-danger
20
+ dangerouslySetInnerHTML={{
21
+ __html: createPassportSvg({
22
+ title: passport.title || passport.name,
23
+ issuer: passport.issuer && passport.issuer.name,
24
+ issuerDid: passport.issuer && passport.issuer.id,
25
+ ownerName: user.fullName,
26
+ ownerDid: user.did,
27
+ ownerAvatarUrl: user.avatar,
28
+ revoked: passport.revoked,
29
+ preferredColor: color,
30
+ width,
31
+ }),
32
+ }}
33
+ />
34
+ <Box
35
+ className="passport-item__body"
36
+ sx={{
37
+ p: 0,
38
+ ml: 3,
39
+ mt: 0,
40
+ }}>
41
+ <Box
42
+ className="passport-item__title"
43
+ sx={{
44
+ display: 'flex',
45
+ alignItems: 'center',
46
+ fontWeight: 400,
47
+ fontSize: '16px',
48
+ lineHeight: 1.1875,
49
+ color: '#222222',
50
+ }}>
51
+ {upperFirst(passport.title)}
52
+ <Box
53
+ className="passport-item__status-icon"
54
+ sx={{
55
+ svg: {
56
+ fill: '#bfbfbf',
57
+ height: '1.2em',
58
+ marginLeft: '0.4em',
59
+ },
60
+ }}>
61
+ {icon}
62
+ {passport.revoked && <RevokeIcon />}
63
+ </Box>
64
+ </Box>
65
+ {passport.expirationDate && (
66
+ <Box
67
+ className="passport-item__title"
68
+ sx={{
69
+ display: 'flex',
70
+ alignItems: 'center',
71
+ fontWeight: 400,
72
+ fontSize: '16px',
73
+ lineHeight: 1.1875,
74
+ color: '#222222',
75
+ }}>
76
+ {t('team.passport.validUntil', { date: passport.expirationDate })}
77
+ </Box>
78
+ )}
79
+ {children}
80
+ </Box>
81
+ </Box>
82
+ );
83
+ }
84
+
85
+ Passport.propTypes = {
86
+ passport: PropTypes.any.isRequired,
87
+ user: PropTypes.any.isRequired,
88
+ color: PropTypes.string.isRequired,
89
+ createPassportSvg: PropTypes.func.isRequired,
90
+ icon: PropTypes.any,
91
+ children: PropTypes.any,
92
+ width: PropTypes.number,
93
+ };
94
+
95
+ Passport.defaultProps = {
96
+ icon: null,
97
+ children: null,
98
+ width: 150,
99
+ };
@@ -189,6 +189,6 @@ function SessionMenuItem({ icon, title, ...rest }) {
189
189
  }
190
190
 
191
191
  SessionMenuItem.propTypes = {
192
- icon: PropTypes.string.isRequired,
192
+ icon: PropTypes.any.isRequired,
193
193
  title: PropTypes.string.isRequired,
194
194
  };
@@ -98,7 +98,7 @@ export default function UserInfo({
98
98
  />
99
99
  </Box>
100
100
 
101
- <Typography variant="h6" sx={{ wordBreak: 'break-all' }}>
101
+ <Typography variant="h6" sx={{ wordBreak: 'break-word' }}>
102
102
  {session.user.fullName}
103
103
  </Typography>
104
104
  </Box>
@@ -22,8 +22,8 @@ SessionUser.propTypes = {
22
22
  user: PropTypes.shape({
23
23
  did: PropTypes.string.isRequired,
24
24
  fullName: PropTypes.string.isRequired,
25
- email: PropTypes.string.isRequired,
26
25
  avatar: PropTypes.string.isRequired,
26
+ email: PropTypes.string,
27
27
  }),
28
28
  }).isRequired,
29
29
  onBindWallet: PropTypes.func,
@@ -0,0 +1,124 @@
1
+ import PropTypes from 'prop-types';
2
+ import { Tabs as MuiTabs, Tab as MuiTab } from '@mui/material';
3
+
4
+ import { temp as colors } from '../Colors';
5
+ import { styled } from '../Theme';
6
+
7
+ const PREFIX = 'index';
8
+
9
+ const classes = {
10
+ tabs: `${PREFIX}-tabs`,
11
+ tab: `${PREFIX}-tab`,
12
+ };
13
+
14
+ const StyledMuiTabs = styled(MuiTabs)(({ theme }) => ({
15
+ [`& .${classes.tabs}`]: {},
16
+
17
+ [`& .${classes.tab}`]: {
18
+ fontSize: '0.875rem',
19
+ [theme.breakpoints.up('md')]: {
20
+ fontSize: '1rem',
21
+ },
22
+ },
23
+ }));
24
+
25
+ function CardTabs({ tabs, current, onChange, ...rest }) {
26
+ return (
27
+ <MuiTabs
28
+ scrollButtons="auto"
29
+ value={current}
30
+ onChange={(_, newValue) => onChange(newValue)}
31
+ {...rest}
32
+ variant="scrollable"
33
+ sx={{
34
+ minHeight: 'auto',
35
+ '.MuiTabs-scrollButtons.Mui-disabled': {
36
+ opacity: 0.3,
37
+ cursor: 'not-allowed',
38
+ pointerEvents: 'auto',
39
+ },
40
+ '.MuiTabs-scroller': {
41
+ borderRadius: '100vw',
42
+ },
43
+ '.MuiTabs-flexContainer': {
44
+ borderRadius: '100vw',
45
+ backgroundColor: colors.backgroundsBgComponent,
46
+ padding: 0.5,
47
+ display: 'inline-flex',
48
+ columnGap: 0.25,
49
+ },
50
+ '.MuiTab-root': {
51
+ borderRadius: '100vw',
52
+ border: '1px solid transparent',
53
+ minHeight: 'auto',
54
+ lineHeight: 1,
55
+ py: 1,
56
+ color: colors.foregroundsFgSubtile,
57
+ fontSize: '13px',
58
+ fontWeight: 'normal',
59
+ textTransform: 'capitalize',
60
+ transition: 'background-color 0.2s ease',
61
+ '&.Mui-selected, &:hover': {
62
+ backgroundColor: 'white',
63
+ borderColor: colors.lineBorderBase,
64
+ color: colors.foregroundsFgBase,
65
+ },
66
+ },
67
+ '.MuiTabs-indicator': {
68
+ display: 'none',
69
+ },
70
+ ...rest.sx,
71
+ }}>
72
+ {tabs.map((x) => (
73
+ <MuiTab className={classes.tab} key={x.value} value={x.value} label={x.label} icon={x.icon || null} />
74
+ ))}
75
+ </MuiTabs>
76
+ );
77
+ }
78
+
79
+ CardTabs.propTypes = {
80
+ tabs: PropTypes.array.isRequired,
81
+ current: PropTypes.string.isRequired,
82
+ onChange: PropTypes.func.isRequired,
83
+ };
84
+
85
+ /**
86
+ * @typedef {import('@mui/material').TabsProps & {
87
+ * tabs: string[];
88
+ * onChange: (value) => {};
89
+ * variant: 'card' | 'fullWidth' | 'scrollable' | 'standard'
90
+ * }} TabsProps
91
+ */
92
+
93
+ /**
94
+ * @description
95
+ * @param { TabsProps} props
96
+ * @return {import('react').ReactNode}
97
+ */
98
+
99
+ export default function Tabs({ tabs, current, onChange, ...rest }) {
100
+ if (rest.variant === 'card') {
101
+ return <CardTabs tabs={tabs} current={current} onChange={onChange} />;
102
+ }
103
+
104
+ return (
105
+ <StyledMuiTabs
106
+ scrollButtons="auto"
107
+ variant="scrollable"
108
+ value={current}
109
+ onChange={(_, newValue) => onChange(newValue)}
110
+ indicatorColor="primary"
111
+ {...rest}
112
+ className={[classes.tabs, rest.className || ''].join(' ')}>
113
+ {tabs.map((x) => (
114
+ <MuiTab className={classes.tab} key={x.value} value={x.value} label={x.label} icon={x.icon || null} />
115
+ ))}
116
+ </StyledMuiTabs>
117
+ );
118
+ }
119
+
120
+ Tabs.propTypes = {
121
+ tabs: PropTypes.array.isRequired,
122
+ current: PropTypes.string.isRequired,
123
+ onChange: PropTypes.func.isRequired,
124
+ };
@@ -0,0 +1,75 @@
1
+ import { getNftBGColor, DEFAULT_COLORS, getNftBGColorFromDid, getSvg } from '@arcblock/nft-display';
2
+
3
+ export const getPassportColor = (preferredColor, did) => {
4
+ let color;
5
+ if (preferredColor === 'default') {
6
+ color = DEFAULT_COLORS['app-passport'];
7
+ } else if (preferredColor === 'auto') {
8
+ color = getNftBGColorFromDid(did);
9
+ } else {
10
+ color = getNftBGColor(preferredColor);
11
+ }
12
+
13
+ return color;
14
+ };
15
+
16
+ export const getTextColor = (background) => {
17
+ const r = parseInt(background.slice(1, 3), 16);
18
+ const g = parseInt(background.slice(3, 5), 16);
19
+ const b = parseInt(background.slice(5, 7), 16);
20
+ const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
21
+ return luminance > 0.5 ? '#111' : '#EEE';
22
+ };
23
+
24
+ export const createPassportSvg = ({
25
+ issuer = '',
26
+ title = '',
27
+ issuerDid = '',
28
+ issuerAvatarUrl = '',
29
+ ownerDid = '',
30
+ ownerName = '',
31
+ ownerAvatarUrl = '',
32
+ preferredColor = 'default',
33
+ revoked = false,
34
+ isDataUrl = false,
35
+ width = '100%',
36
+ height = '100%',
37
+ }) => {
38
+ const color = getPassportColor(preferredColor, issuerDid);
39
+
40
+ const svgXML = getSvg({
41
+ width,
42
+ height,
43
+
44
+ tag: revoked ? 'revoked' : '',
45
+ tagVariant: revoked ? 'error' : 'info',
46
+
47
+ color,
48
+
49
+ did: ownerDid,
50
+ variant: 'app-passport' || ownerName,
51
+ verifiable: true,
52
+
53
+ issuer: {
54
+ name: issuer,
55
+ icon: issuerAvatarUrl,
56
+ },
57
+
58
+ header: {
59
+ name: title,
60
+ icon: ownerAvatarUrl,
61
+ },
62
+
63
+ // FIXME: @wangshijun this should be dynamic
64
+ extra: {
65
+ key: 'Exp',
66
+ value: '2123-01-01',
67
+ },
68
+ });
69
+
70
+ if (isDataUrl) {
71
+ return `data:image/svg+xml;utf8,${encodeURIComponent(svgXML)}`;
72
+ }
73
+
74
+ return svgXML;
75
+ };