@arcblock/ux 2.6.9 → 2.7.1

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 (164) hide show
  1. package/babel.config.es.js +12 -0
  2. package/es/ActionButton/index.js +99 -0
  3. package/es/ActivityIndicator/index.js +180 -0
  4. package/es/Address/compact-text.js +105 -0
  5. package/es/Address/did-address.js +211 -0
  6. package/es/Address/index.js +23 -0
  7. package/es/Address/responsive-did-address.js +88 -0
  8. package/es/Alert/index.js +134 -0
  9. package/es/AnimationWaiter/default-animation.json +1 -0
  10. package/es/AnimationWaiter/index.js +239 -0
  11. package/es/Async/index.js +38 -0
  12. package/es/Avatar/did-motif.js +64 -0
  13. package/es/Avatar/etherscan-blockies.js +83 -0
  14. package/es/Avatar/index.js +176 -0
  15. package/es/Badge/index.js +98 -0
  16. package/es/Blocklet/blocklet.js +298 -0
  17. package/es/Blocklet/index.js +4 -0
  18. package/es/Blocklet/utils.js +52 -0
  19. package/es/BlockletNFT/index.js +412 -0
  20. package/es/Button/index.js +8 -0
  21. package/es/Button/wrap.js +140 -0
  22. package/es/ButtonGroup/index.js +6 -0
  23. package/es/CardSelector/index.js +131 -0
  24. package/es/Center/index.js +41 -0
  25. package/es/ClickToCopy/copy-button.js +72 -0
  26. package/es/ClickToCopy/hook.js +39 -0
  27. package/es/ClickToCopy/index.js +93 -0
  28. package/es/CodeBlock/LightBox.js +85 -0
  29. package/es/CodeBlock/index.js +226 -0
  30. package/es/Colors/index.js +2 -0
  31. package/es/Colors/themes/default.js +78 -0
  32. package/es/ContactForm/index.js +230 -0
  33. package/es/CookieConsent/index.js +113 -0
  34. package/es/CountDown/index.js +178 -0
  35. package/es/DID/index.js +105 -0
  36. package/es/Datatable/CustomToolbar.js +396 -0
  37. package/es/Datatable/DatatableContext.js +34 -0
  38. package/es/Datatable/TableSearch.js +165 -0
  39. package/es/Datatable/index.js +627 -0
  40. package/es/Datatable/utils.js +132 -0
  41. package/es/Dialog/confirm.js +90 -0
  42. package/es/Dialog/dialog.js +192 -0
  43. package/es/Dialog/index.js +3 -0
  44. package/es/DidLogo/index.js +31 -0
  45. package/es/DriftBot/index.js +70 -0
  46. package/es/Earth/countries.json +8057 -0
  47. package/es/Earth/index.js +521 -0
  48. package/es/Earth/util.js +51 -0
  49. package/es/Empty/index.js +64 -0
  50. package/es/ErrorBoundary/fallback.js +73 -0
  51. package/es/ErrorBoundary/index.js +1 -0
  52. package/es/Footer/index.js +172 -0
  53. package/es/Header/auto-hidden.js +35 -0
  54. package/es/Header/header.js +211 -0
  55. package/es/Header/index.js +2 -0
  56. package/es/Header/responsive-header.js +111 -0
  57. package/es/Icon/image.js +65 -0
  58. package/es/Icon/index.js +84 -0
  59. package/es/Img/index.js +217 -0
  60. package/es/InfoRow/index.js +87 -0
  61. package/es/Layout/dashboard/external-link.js +58 -0
  62. package/es/Layout/dashboard/full-page.js +53 -0
  63. package/es/Layout/dashboard/index.js +275 -0
  64. package/es/Layout/dashboard/sidebar.js +209 -0
  65. package/es/Layout/dashboard-legacy/header.js +174 -0
  66. package/es/Layout/dashboard-legacy/index.js +149 -0
  67. package/es/Layout/dashboard-legacy/sidebar.js +129 -0
  68. package/es/Layout/index.js +335 -0
  69. package/es/Locale/browser-lang.js +52 -0
  70. package/es/Locale/context.js +114 -0
  71. package/es/Locale/languages.js +60 -0
  72. package/es/Locale/selector.js +180 -0
  73. package/es/Locale/util.js +13 -0
  74. package/es/Logo/images/logo-dark-text.svg +3 -0
  75. package/es/Logo/images/logo-dark-top.svg +6 -0
  76. package/es/Logo/images/logo-light-text.svg +3 -0
  77. package/es/Logo/images/logo-light-top.svg +6 -0
  78. package/es/Logo/index.js +136 -0
  79. package/es/Metric/index.js +132 -0
  80. package/es/NFTDisplay/README.md +59 -0
  81. package/es/NFTDisplay/aspect-ratio-container.js +39 -0
  82. package/es/NFTDisplay/broken.js +18 -0
  83. package/es/NFTDisplay/demo/data/asset-state-display-url.json +7 -0
  84. package/es/NFTDisplay/demo/data/asset-state-gzipped-svg-1-1.json +10 -0
  85. package/es/NFTDisplay/demo/data/asset-state-gzipped-svg-374-130.json +10 -0
  86. package/es/NFTDisplay/demo/data/asset-state-gzipped-svg-with-foreign-object.json +20 -0
  87. package/es/NFTDisplay/demo/data/asset-state-svg.json +29 -0
  88. package/es/NFTDisplay/demo/data/asset-state-url.json +10 -0
  89. package/es/NFTDisplay/index.js +323 -0
  90. package/es/NFTDisplay/loading.js +18 -0
  91. package/es/NFTDisplay/svg-embedder/img.js +45 -0
  92. package/es/NFTDisplay/svg-embedder/inline-svg.js +39 -0
  93. package/es/NavMenu/index.js +2 -0
  94. package/es/NavMenu/nav-menu.js +286 -0
  95. package/es/NavMenu/style.js +176 -0
  96. package/es/PageScroller/index.js +286 -0
  97. package/es/PageScroller/story/FifthComponent.js +9 -0
  98. package/es/PageScroller/story/FirstComponent.js +9 -0
  99. package/es/PageScroller/story/FourthComponent.js +12 -0
  100. package/es/PageScroller/story/FullPage.js +47 -0
  101. package/es/PageScroller/story/PageContain.js +59 -0
  102. package/es/PageScroller/story/SecondComponent.js +9 -0
  103. package/es/PageScroller/story/ThirdComponent.js +9 -0
  104. package/es/PageScroller/story/index.css +115 -0
  105. package/es/PageScroller/usePrevValue.js +9 -0
  106. package/es/PricingTable/PricingPlan.js +124 -0
  107. package/es/PricingTable/index.js +53 -0
  108. package/es/QRCode/index.js +72 -0
  109. package/es/RelativeTime/index.js +98 -0
  110. package/es/Result/common.js +140 -0
  111. package/es/Result/demo/fixtures/result-image-404.svg +1 -0
  112. package/es/Result/index.js +33 -0
  113. package/es/Result/result.js +59 -0
  114. package/es/Result/translations.js +54 -0
  115. package/es/Screenshot/BaseScreenshot/index.js +91 -0
  116. package/es/Screenshot/BaseScreenshot/shells/Macbook.js +51 -0
  117. package/es/Screenshot/BaseScreenshot/shells/Phone.js +36 -0
  118. package/es/Screenshot/demo/images/bg-00.jpg +0 -0
  119. package/es/Screenshot/demo/images/bg-01.jpg +0 -0
  120. package/es/Screenshot/demo/images/bg-02.jpg +0 -0
  121. package/es/Screenshot/demo/images/bg-03.jpg +0 -0
  122. package/es/Screenshot/demo/images/bg-04.jpg +0 -0
  123. package/es/Screenshot/demo/images/bg-05.jpg +0 -0
  124. package/es/Screenshot/demo/images/bg-06.jpg +0 -0
  125. package/es/Screenshot/demo/images/bg-07.jpg +0 -0
  126. package/es/Screenshot/demo/images/bg-08.jpg +0 -0
  127. package/es/Screenshot/demo/images/bg-09.jpg +0 -0
  128. package/es/Screenshot/devices.css +1366 -0
  129. package/es/Screenshot/index.js +299 -0
  130. package/es/SessionManager/federated-login-detecter.js +166 -0
  131. package/es/SessionManager/index.js +468 -0
  132. package/es/SessionManager/user-popper.js +132 -0
  133. package/es/Sparkline/index.js +193 -0
  134. package/es/Spinner/index.js +28 -0
  135. package/es/SplitButton/index.js +144 -0
  136. package/es/Switch/index.js +96 -0
  137. package/es/Tabs/index.js +48 -0
  138. package/es/Tag/index.js +108 -0
  139. package/es/TextCollapse/index.js +92 -0
  140. package/es/Theme/index.js +16 -0
  141. package/es/Theme/theme-provider.js +39 -0
  142. package/es/Theme/theme.js +133 -0
  143. package/es/Toast/index.js +95 -0
  144. package/es/Util/deprecate.js +28 -0
  145. package/es/Util/index.js +298 -0
  146. package/es/Util/wallet.js +32 -0
  147. package/es/Video/index.js +89 -0
  148. package/es/Wallet/Action.js +119 -0
  149. package/es/Wallet/Download.js +331 -0
  150. package/es/Wallet/Open.js +45 -0
  151. package/es/Wallet/images/abtwallet.png +0 -0
  152. package/es/Wallet/images/android_download.svg +23 -0
  153. package/es/Wallet/images/app-store.svg +20 -0
  154. package/es/Wallet/images/google-play.svg +70 -0
  155. package/es/WebWalletSWKeeper/index.js +115 -0
  156. package/es/WechatPrompt/images/android.png +0 -0
  157. package/es/WechatPrompt/images/ios.png +0 -0
  158. package/es/WechatPrompt/index.js +88 -0
  159. package/es/index.js +38 -0
  160. package/es/withTheme/index.js +69 -0
  161. package/es/withTracker/README.md +34 -0
  162. package/es/withTracker/error_boundary.js +34 -0
  163. package/es/withTracker/index.js +56 -0
  164. package/package.json +272 -5
@@ -0,0 +1,217 @@
1
+ import { useEffect, useMemo, useState } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import SvgIcon from '@mui/material/SvgIcon';
4
+ import { useInView } from 'react-intersection-observer';
5
+ import Alert from 'mdi-material-ui/Alert';
6
+ import ImageIcon from 'mdi-material-ui/Image';
7
+ import { styled } from '../Theme';
8
+
9
+ /**
10
+ * @typedef {Object} ImgExProps
11
+ * @property {string} src - required
12
+ * @property {string} [alt]
13
+ * @property {string} [size='cover']
14
+ * @property {string} [position='top center']
15
+ * @property {object} [style]
16
+ * @property {number} [ratio=1]
17
+ * @property {string} [repeat='no-repeat']
18
+ * @property {number} [width]
19
+ * @property {number} [height]
20
+ * @property {boolean} [lazy=true]
21
+ * @property {string} [placeholder]
22
+ * @property {string} [fallback]
23
+ * @property {string} [className='']
24
+ * @property {function} [onError=() => {}]
25
+ * @property {function} [onSuccess=() => {}]
26
+ */
27
+
28
+ /**
29
+ * @typedef {ImgExProps & import('react').ComponentPropsWithoutRef<"div">} ImgProps
30
+ */
31
+ import { jsx as _jsx } from "react/jsx-runtime";
32
+ import { jsxs as _jsxs } from "react/jsx-runtime";
33
+ const PREFIX = 'Img';
34
+ const classes = {
35
+ root: `${PREFIX}-root`
36
+ };
37
+
38
+ /** @type {import('@emotion/styled').StyledComponent<import('react').ComponentPropsWithoutRef<"div">, {}, { ref?: React.Ref<any> | undefined;}>} */
39
+ const Root = styled('div')(() => ({
40
+ [`& .${classes.root}`]: {
41
+ position: 'relative',
42
+ overflow: 'hidden',
43
+ '& .image--state, & .image--img': {
44
+ minWidth: '100%',
45
+ minHeight: '100%',
46
+ position: 'absolute',
47
+ top: 0,
48
+ left: '50%',
49
+ transform: 'translateX(-50%)'
50
+ },
51
+ '& .image--state': {
52
+ display: 'flex',
53
+ flexDirection: 'column',
54
+ justifyContent: 'center',
55
+ alignItems: 'center',
56
+ color: '#999',
57
+ background: '#eee',
58
+ width: '100%',
59
+ height: '100%'
60
+ },
61
+ '& .image--img': {
62
+ opacity: 0,
63
+ visibility: 'hidden'
64
+ },
65
+ '& .image--icon': {
66
+ fontSize: 30,
67
+ maxWidth: '80%',
68
+ maxHeight: '80%'
69
+ }
70
+ }
71
+ }));
72
+
73
+ /**
74
+ *
75
+ * @param {ImgProps} props
76
+ * @returns {React.ReactComponentElement}
77
+ */
78
+ function Img({
79
+ lazy,
80
+ width,
81
+ height,
82
+ repeat,
83
+ ratio,
84
+ alt,
85
+ size,
86
+ position,
87
+ src,
88
+ placeholder,
89
+ fallback,
90
+ style,
91
+ className,
92
+ onError,
93
+ onSuccess,
94
+ ...rest
95
+ }) {
96
+ // eslint-disable-next-line react-hooks/rules-of-hooks
97
+ const [ref, inView] = lazy ? useInView({
98
+ threshold: 0,
99
+ triggerOnce: true
100
+ }) : [null, true];
101
+ const [imgState, setImgState] = useState('init');
102
+ const actualSrc = useMemo(() => {
103
+ switch (imgState) {
104
+ case 'init':
105
+ case 'loading':
106
+ return placeholder;
107
+ case 'error':
108
+ return fallback;
109
+ case 'loaded':
110
+ return src;
111
+ default:
112
+ return null;
113
+ }
114
+ }, [placeholder, fallback, src, imgState]);
115
+ const actualRatio = width && height ? 100 * height / width : ratio * 100;
116
+ const mergedStyle = useMemo(() => ({
117
+ backgroundImage: actualSrc ? `url(${actualSrc})` : '',
118
+ backgroundPosition: position,
119
+ backgroundSize: size,
120
+ backgroundRepeat: repeat,
121
+ paddingTop: `${actualRatio}%`
122
+ }),
123
+ // eslint-disable-next-line react-hooks/exhaustive-deps
124
+ [actualSrc, position, size, imgState]);
125
+
126
+ /**
127
+ * @type {CSSStyleDeclaration}
128
+ */
129
+ const outerStyle = {
130
+ ...style,
131
+ display: 'inline-block',
132
+ width: width ? `${width}px` : '100%'
133
+ };
134
+ function loadImg() {
135
+ const img = new Image();
136
+ img.src = src;
137
+ setImgState('loading');
138
+ img.onload = () => {
139
+ setImgState('loaded');
140
+ onSuccess();
141
+ };
142
+ img.onerror = err => {
143
+ setImgState('error');
144
+ onError(err);
145
+ };
146
+ }
147
+ useEffect(() => {
148
+ if (inView) loadImg();
149
+ // eslint-disable-next-line react-hooks/exhaustive-deps
150
+ }, [inView]);
151
+ return (
152
+ /*#__PURE__*/
153
+ // paddingTop 要求元素本身的宽度为 100%,所以只能加一个外层元素去限制宽度
154
+ _jsx(Root, {
155
+ ref: ref,
156
+ style: outerStyle,
157
+ ...rest,
158
+ children: /*#__PURE__*/_jsxs("div", {
159
+ className: `image ${className} ${classes.root}`,
160
+ style: mergedStyle,
161
+ children: [!fallback && imgState === 'error' && /*#__PURE__*/_jsx("div", {
162
+ className: "image--state",
163
+ title: "loading image",
164
+ children: /*#__PURE__*/_jsx(SvgIcon, {
165
+ component: Alert,
166
+ className: "image--icon"
167
+ })
168
+ }), !placeholder && imgState === 'loading' && /*#__PURE__*/_jsx("div", {
169
+ className: "image--state",
170
+ title: "Image load error",
171
+ children: /*#__PURE__*/_jsx(SvgIcon, {
172
+ component: ImageIcon,
173
+ className: "image--icon"
174
+ })
175
+ }), imgState === 'loaded' && /*#__PURE__*/_jsx("img", {
176
+ className: "image--img",
177
+ src: src,
178
+ alt: alt
179
+ })]
180
+ })
181
+ })
182
+ );
183
+ }
184
+ Img.propTypes = {
185
+ src: PropTypes.string.isRequired,
186
+ alt: PropTypes.string,
187
+ size: PropTypes.string,
188
+ position: PropTypes.string,
189
+ style: PropTypes.object,
190
+ ratio: PropTypes.number,
191
+ repeat: PropTypes.string,
192
+ width: PropTypes.number,
193
+ height: PropTypes.number,
194
+ lazy: PropTypes.bool,
195
+ placeholder: PropTypes.string,
196
+ fallback: PropTypes.string,
197
+ className: PropTypes.string,
198
+ onError: PropTypes.func,
199
+ onSuccess: PropTypes.func
200
+ };
201
+ Img.defaultProps = {
202
+ alt: null,
203
+ size: 'cover',
204
+ position: 'top center',
205
+ repeat: 'no-repeat',
206
+ style: null,
207
+ ratio: 1,
208
+ width: null,
209
+ height: null,
210
+ lazy: true,
211
+ placeholder: null,
212
+ fallback: null,
213
+ className: '',
214
+ onError: () => {},
215
+ onSuccess: () => {}
216
+ };
217
+ export default Img;
@@ -0,0 +1,87 @@
1
+ import PropTypes from 'prop-types';
2
+ import camelCase from 'lodash/camelCase';
3
+ import upperFirst from 'lodash/upperFirst';
4
+ import Typography from '@mui/material/Typography';
5
+ import { styled } from '../Theme';
6
+ import { jsx as _jsx } from "react/jsx-runtime";
7
+ import { jsxs as _jsxs } from "react/jsx-runtime";
8
+ function InfoRow({
9
+ name,
10
+ nameFormatter,
11
+ layout,
12
+ children,
13
+ valueComponent,
14
+ nameWidth,
15
+ ...rest
16
+ }) {
17
+ return /*#__PURE__*/_jsxs(Container, {
18
+ layout: layout,
19
+ width: nameWidth,
20
+ ...rest,
21
+ children: [/*#__PURE__*/_jsx(Typography, {
22
+ color: "textSecondary",
23
+ className: "info-row__name",
24
+ children: nameFormatter(name)
25
+ }), children && /*#__PURE__*/_jsx(Typography, {
26
+ color: "textPrimary",
27
+ component: valueComponent,
28
+ className: "info-row__value",
29
+ children: children
30
+ })]
31
+ });
32
+ }
33
+ InfoRow.propTypes = {
34
+ name: PropTypes.any.isRequired,
35
+ layout: PropTypes.oneOf(['horizontal', 'vertical']),
36
+ children: PropTypes.any,
37
+ nameFormatter: PropTypes.func,
38
+ valueComponent: PropTypes.string,
39
+ nameWidth: PropTypes.number
40
+ };
41
+ InfoRow.defaultProps = {
42
+ children: null,
43
+ nameWidth: 90,
44
+ nameFormatter: name => {
45
+ const resetName = name.split(' ').map(x => upperFirst(camelCase(x))).join(' ');
46
+ return typeof name === 'string' ? resetName : name;
47
+ },
48
+ valueComponent: 'div',
49
+ layout: 'horizontal'
50
+ };
51
+ const Container = styled('div')`
52
+ display: flex;
53
+ flex-direction: ${props => props.layout === 'vertical' ? 'column' : 'row'};
54
+ justify-content: flex-start;
55
+ align-items: ${props => props.layout === 'vertical' ? 'flex-start' : 'center'};
56
+ margin-bottom: 16px;
57
+
58
+ .info-row__name {
59
+ width: ${props => props.width}px;
60
+ margin-right: ${props => props.layout === 'vertical' ? '0' : '8px'};
61
+ margin-bottom: ${props => props.layout === 'vertical' ? '8px' : '0'};
62
+ text-transform: capitalize;
63
+ }
64
+
65
+ .info-row__value {
66
+ flex: 1;
67
+ overflow: auto;
68
+ word-wrap: break-word;
69
+ font-weight: 500;
70
+ }
71
+
72
+ @media (max-width: ${props => props.theme.breakpoints.values.md}px) {
73
+ flex-direction: column;
74
+ justify-content: flex-start;
75
+ align-items: flex-start;
76
+ .info-row__name {
77
+ font-weight: bold;
78
+ }
79
+
80
+ .info-row__value {
81
+ width: 100%;
82
+ font-size: 12px;
83
+ margin-top: 8px;
84
+ }
85
+ }
86
+ `;
87
+ export default InfoRow;
@@ -0,0 +1,58 @@
1
+ import PropTypes from 'prop-types';
2
+ import { Link as RouterLink, NavLink as RouterNavLink } from 'react-router-dom';
3
+
4
+ // 包裹 router Link/NavLink 组件, 支持 external link (external 为 true 时渲染为 a 标签)
5
+ import { jsx as _jsx } from "react/jsx-runtime";
6
+ function ExternalLink({
7
+ children,
8
+ routerLinkComponent: RouterLinkComponent,
9
+ to,
10
+ external,
11
+ ...rest
12
+ }) {
13
+ if (external) {
14
+ return /*#__PURE__*/_jsx("a", {
15
+ href: to,
16
+ ...rest,
17
+ children: children
18
+ });
19
+ }
20
+ return /*#__PURE__*/_jsx(RouterLinkComponent, {
21
+ to: to,
22
+ ...rest,
23
+ children: children
24
+ });
25
+ }
26
+ ExternalLink.propTypes = {
27
+ children: PropTypes.any.isRequired,
28
+ routerLinkComponent: PropTypes.elementType.isRequired,
29
+ external: PropTypes.bool,
30
+ to: PropTypes.oneOfType([PropTypes.string, PropTypes.object]).isRequired
31
+ };
32
+ ExternalLink.defaultProps = {
33
+ external: false
34
+ };
35
+ export function Link(props) {
36
+ return /*#__PURE__*/_jsx(ExternalLink, {
37
+ routerLinkComponent: RouterLink,
38
+ ...props
39
+ });
40
+ }
41
+ export function NavLink({
42
+ className,
43
+ ...rest
44
+ }) {
45
+ // NavLink#className 支持 function
46
+ const classes = rest.external && typeof className === 'function' ? className({}) : className;
47
+ return /*#__PURE__*/_jsx(ExternalLink, {
48
+ routerLinkComponent: RouterNavLink,
49
+ className: classes,
50
+ ...rest
51
+ });
52
+ }
53
+ NavLink.propTypes = {
54
+ className: PropTypes.oneOfType([PropTypes.string, PropTypes.func])
55
+ };
56
+ NavLink.defaultProps = {
57
+ className: ''
58
+ };
@@ -0,0 +1,53 @@
1
+ import { useState, useContext, createContext, useMemo, useLayoutEffect } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { jsx as _jsx } from "react/jsx-runtime";
4
+ export const FullPageContext = /*#__PURE__*/createContext();
5
+ export const useFullPage = initialState => {
6
+ const ctx = useContext(FullPageContext);
7
+ useLayoutEffect(() => {
8
+ if (initialState) {
9
+ ctx.configure(initialState);
10
+ }
11
+ // eslint-disable-next-line react-hooks/exhaustive-deps
12
+ }, []);
13
+ return ctx;
14
+ };
15
+ export function FullPageProvider({
16
+ children,
17
+ ...rest
18
+ }) {
19
+ const [state, setState] = useState({
20
+ inFullPage: false,
21
+ showToggleButton: false,
22
+ headerVisibleInFullPage: true,
23
+ footerVisibleInFullPage: false,
24
+ sidebarVisibleInFullPage: false
25
+ });
26
+ const toggleFullPage = () => {
27
+ setState(prev => ({
28
+ ...prev,
29
+ inFullPage: !prev.inFullPage
30
+ }));
31
+ };
32
+ const value = useMemo(() => {
33
+ return {
34
+ ...state,
35
+ headerVisible: !state.inFullPage || state.headerVisibleInFullPage,
36
+ footerVisible: !state.inFullPage || state.footerVisibleInFullPage,
37
+ sidebarVisible: !state.inFullPage || state.sidebarVisibleInFullPage,
38
+ toggleFullPage,
39
+ configure: params => setState(prev => ({
40
+ ...prev,
41
+ ...params
42
+ }))
43
+ };
44
+ }, [state]);
45
+ return /*#__PURE__*/_jsx(FullPageContext.Provider, {
46
+ value: value,
47
+ ...rest,
48
+ children: children
49
+ });
50
+ }
51
+ FullPageProvider.propTypes = {
52
+ children: PropTypes.node.isRequired
53
+ };
@@ -0,0 +1,275 @@
1
+ import { useEffect, useMemo } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { useLocation, matchPath } from 'react-router-dom';
4
+ import Helmet from 'react-helmet';
5
+ import Container from '@mui/material/Container';
6
+ import Hidden from '@mui/material/Hidden';
7
+ import Box from '@mui/material/Box';
8
+ import clsx from 'clsx';
9
+ import OpenInFullIcon from '@mui/icons-material/OpenInFull';
10
+ import CloseFullscreenIcon from '@mui/icons-material/CloseFullscreen';
11
+ import DashboardLegacy from '../dashboard-legacy';
12
+ import { ResponsiveHeader } from '../../Header';
13
+ import NavMenu from '../../NavMenu';
14
+ import Footer from '../../Footer';
15
+ import Sidebar from './sidebar';
16
+ import { styled, useTheme } from '../../Theme';
17
+ import { Link } from './external-link';
18
+ import { FullPageProvider, useFullPage } from './full-page';
19
+
20
+ // 监听 location 变化并关闭 header 中的 menu (确保 drawer - disablePortal:false, keepMounted:true)
21
+ // 直接监听 menu 中 link 点击来触发 closeMenu 会有问题 (Suspense & lazy)
22
+ // eslint-disable-next-line react/prop-types
23
+ import { jsx as _jsx } from "react/jsx-runtime";
24
+ import { jsxs as _jsxs } from "react/jsx-runtime";
25
+ function NavMenuWrapper({
26
+ closeMenu,
27
+ ...rest
28
+ }) {
29
+ const location = useLocation();
30
+ useEffect(() => {
31
+ closeMenu();
32
+ // eslint-disable-next-line react-hooks/exhaustive-deps
33
+ }, [location]);
34
+ return /*#__PURE__*/_jsx(NavMenu, {
35
+ ...rest
36
+ });
37
+ }
38
+ function formatLinks(links, location) {
39
+ return links.map(link => {
40
+ if (link.children?.length) {
41
+ return {
42
+ ...link,
43
+ label: link.title,
44
+ children: formatLinks(link.children, location)
45
+ };
46
+ }
47
+ return {
48
+ ...link,
49
+ label: /*#__PURE__*/_jsx(Link, {
50
+ to: link.url,
51
+ external: link.external,
52
+ children: link.title
53
+ }),
54
+ // external = true 时 active 状态由传入 links 的调用方决定 (适用于 blocklet ui dashboard 的情况)
55
+ active: link.external ? link.active : !!matchPath({
56
+ path: link.url,
57
+ end: false
58
+ }, location.pathname)
59
+ };
60
+ });
61
+ }
62
+ function Dashboard({
63
+ children,
64
+ title,
65
+ headerProps,
66
+ links = [],
67
+ fullWidth,
68
+ dense,
69
+ footerProps,
70
+ ...rest
71
+ }) {
72
+ const theme = useTheme();
73
+ const {
74
+ inFullPage,
75
+ showToggleButton,
76
+ headerVisible,
77
+ footerVisible,
78
+ sidebarVisible,
79
+ toggleFullPage
80
+ } = useFullPage();
81
+ const location = useLocation();
82
+ const navItems = useMemo(() => formatLinks(links, location), [location, links]);
83
+ const isGroupedMode = navItems.some(item => !!item.children?.length);
84
+ // 一级菜单数量 > 8 或都分组模式, 都启用 dense 布局
85
+ const _dense = dense === 'auto' ? navItems.length >= 8 || isGroupedMode : dense;
86
+ const classes = clsx('dashboard', {
87
+ 'dashboard-dense': _dense
88
+ }, rest.className);
89
+ const defaultHomeLink = content => /*#__PURE__*/_jsx(Link, {
90
+ to: window.blocklet?.prefix || '/',
91
+ children: content
92
+ });
93
+ const _headerProps = {
94
+ homeLink: defaultHomeLink,
95
+ ...headerProps
96
+ };
97
+ return /*#__PURE__*/_jsxs(Wrapper, {
98
+ ...rest,
99
+ className: classes,
100
+ children: [/*#__PURE__*/_jsx(Helmet, {
101
+ title: title
102
+ }, title), headerVisible && /*#__PURE__*/_jsx(StyledUxHeader, {
103
+ ..._headerProps,
104
+ className: "dashboard-header",
105
+ children: links?.length ? ({
106
+ isMobile,
107
+ closeMenu
108
+ }) => {
109
+ if (isMobile) {
110
+ return /*#__PURE__*/_jsx(NavMenuWrapper, {
111
+ mode: "inline",
112
+ items: navItems,
113
+ closeMenu: closeMenu,
114
+ bgColor: "transparent",
115
+ activeTextColor: theme.palette.primary.main
116
+ });
117
+ }
118
+ return null;
119
+ } : null
120
+ }), /*#__PURE__*/_jsxs(Box, {
121
+ display: "flex",
122
+ className: "dashboard-body",
123
+ children: [/*#__PURE__*/_jsx(Hidden, {
124
+ mdDown: true,
125
+ children: !!links?.length && sidebarVisible && /*#__PURE__*/_jsx(Box, {
126
+ className: "dashboard-sidebar",
127
+ children: /*#__PURE__*/_jsx(Sidebar, {
128
+ links: links,
129
+ dense: _dense
130
+ })
131
+ })
132
+ }), /*#__PURE__*/_jsxs(Box, {
133
+ className: "dashboard-main",
134
+ children: [showToggleButton && /*#__PURE__*/_jsx(Box, {
135
+ sx: {
136
+ position: 'absolute',
137
+ top: 4,
138
+ right: 4,
139
+ display: 'flex',
140
+ justifyContent: 'center',
141
+ alignItems: 'center',
142
+ width: 24,
143
+ height: 24,
144
+ color: '#fff',
145
+ bgcolor: 'rgba(158,158,158, 0.6);',
146
+ borderRadius: 1,
147
+ cursor: 'pointer'
148
+ },
149
+ onClick: toggleFullPage,
150
+ children: inFullPage ? /*#__PURE__*/_jsx(CloseFullscreenIcon, {
151
+ style: {
152
+ fontSize: 18
153
+ }
154
+ }) : /*#__PURE__*/_jsx(OpenInFullIcon, {
155
+ style: {
156
+ fontSize: 18
157
+ }
158
+ })
159
+ }), /*#__PURE__*/_jsx(Container, {
160
+ className: "dashboard-content",
161
+ ...(fullWidth && {
162
+ maxWidth: false
163
+ }),
164
+ children: children
165
+ }), footerVisible && /*#__PURE__*/_jsx(Footer, {
166
+ ...footerProps
167
+ })]
168
+ })]
169
+ })]
170
+ });
171
+ }
172
+ Dashboard.propTypes = {
173
+ children: PropTypes.any.isRequired,
174
+ title: PropTypes.string,
175
+ // 支持分组, links item 如果是数组, 则视为一个 group
176
+ links: PropTypes.array.isRequired,
177
+ headerProps: PropTypes.object,
178
+ fullWidth: PropTypes.bool,
179
+ sidebarWidth: PropTypes.number,
180
+ // sidenav 稠密一些的布局, 纵向空间占用较少, 默认为 auto, 当 links 个数 > 8 时自动启用
181
+ dense: PropTypes.oneOf([true, false, 'auto'])
182
+ };
183
+ Dashboard.defaultProps = {
184
+ title: 'Home',
185
+ headerProps: {},
186
+ fullWidth: false,
187
+ sidebarWidth: 120,
188
+ dense: 'auto'
189
+ };
190
+ const Wrapper = styled('div', {
191
+ shouldForwardProp: prop => prop !== 'sidebarWidth'
192
+ })`
193
+ &.dashboard {
194
+ display: flex;
195
+ flex-direction: column;
196
+ height: 100vh;
197
+ }
198
+ .dashboard-body {
199
+ overflow: hidden;
200
+ flex: 1;
201
+ }
202
+ .dashboard-sidebar {
203
+ box-sizing: border-box;
204
+ flex: 0 0 auto;
205
+ width: ${props => props.sidebarWidth}px;
206
+ overflow: hidden;
207
+ &:hover {
208
+ overflow-y: auto;
209
+ }
210
+ }
211
+ .dashboard-main {
212
+ display: flex;
213
+ flex-direction: column;
214
+ overflow: auto;
215
+ flex: 1;
216
+ position: relative;
217
+ }
218
+ .dashboard-content {
219
+ flex: 1;
220
+ }
221
+ &.dashboard-dense {
222
+ .dashboard-header {
223
+ border-bottom: 1px solid #eee;
224
+ }
225
+ .dashboard-sidebar {
226
+ width: 256px;
227
+ border-right: 1px solid #eee;
228
+ }
229
+ }
230
+ ${props => props.theme.breakpoints.up('md')} {
231
+ .header-logo {
232
+ display: flex;
233
+ justify-content: center;
234
+ /* logo 与 sidebar 中的 icon 垂直对齐, sidebarWidth - 24 * 2 */
235
+ width: ${props => props.sidebarWidth - 24 * 2}px;
236
+ }
237
+ &.dashboard-dense {
238
+ .header-logo {
239
+ /* dense = true 时 logo 与 sidenav icons 不需要对齐 */
240
+ width: auto;
241
+ }
242
+ }
243
+ }
244
+ `;
245
+ const StyledUxHeader = styled(ResponsiveHeader)`
246
+ .header-container {
247
+ max-width: 100%;
248
+ }
249
+ `;
250
+
251
+ // 兼容旧版 dashboard
252
+ export default function DashboardWrapper({
253
+ legacy,
254
+ ...rest
255
+ }) {
256
+ if (legacy) {
257
+ return /*#__PURE__*/_jsx(DashboardLegacy, {
258
+ ...rest
259
+ });
260
+ }
261
+ return /*#__PURE__*/_jsx(FullPageProvider, {
262
+ children: /*#__PURE__*/_jsx(Dashboard, {
263
+ ...rest
264
+ })
265
+ });
266
+ }
267
+ DashboardWrapper.propTypes = {
268
+ ...Dashboard.propTypes,
269
+ legacy: PropTypes.bool,
270
+ footerProps: PropTypes.shape(Footer.propTypes)
271
+ };
272
+ DashboardWrapper.defaultProps = {
273
+ ...Dashboard.defaultProps,
274
+ legacy: true
275
+ };