@arcblock/ux 2.13.6 → 2.13.8

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 (45) hide show
  1. package/lib/Colors/index.d.ts +3 -1
  2. package/lib/Colors/index.js +4 -2
  3. package/lib/Config/config-provider.d.ts +7 -7
  4. package/lib/Config/config-provider.js +1 -1
  5. package/lib/Theme/theme.d.ts +7 -41
  6. package/lib/Theme/theme.js +50 -132
  7. package/lib/UserCard/Cards/avatar-only.d.ts +3 -2
  8. package/lib/UserCard/Cards/basic-info.d.ts +3 -2
  9. package/lib/UserCard/Cards/basic-info.js +12 -6
  10. package/lib/UserCard/Cards/index.d.ts +3 -2
  11. package/lib/UserCard/Cards/name-only.d.ts +4 -2
  12. package/lib/UserCard/Cards/name-only.js +3 -2
  13. package/lib/UserCard/Container/dialog.js +1 -1
  14. package/lib/UserCard/Content/basic.d.ts +2 -1
  15. package/lib/UserCard/Content/basic.js +195 -67
  16. package/lib/UserCard/Content/minimal.d.ts +2 -2
  17. package/lib/UserCard/Content/minimal.js +2 -0
  18. package/lib/UserCard/Content/tooltip-avatar.d.ts +4 -4
  19. package/lib/UserCard/Content/tooltip-avatar.js +4 -3
  20. package/lib/UserCard/components.d.ts +2 -2
  21. package/lib/UserCard/components.js +9 -3
  22. package/lib/UserCard/index.js +36 -4
  23. package/lib/UserCard/types.d.ts +7 -4
  24. package/lib/UserCard/utils.d.ts +2 -0
  25. package/lib/UserCard/utils.js +33 -0
  26. package/lib/type.d.ts +2 -3
  27. package/lib/withTheme/index.d.ts +2 -3
  28. package/package.json +6 -5
  29. package/src/Colors/index.ts +7 -2
  30. package/src/Config/config-provider.tsx +9 -9
  31. package/src/Theme/theme.ts +55 -166
  32. package/src/UserCard/Cards/avatar-only.tsx +3 -2
  33. package/src/UserCard/Cards/basic-info.tsx +17 -5
  34. package/src/UserCard/Cards/index.tsx +3 -2
  35. package/src/UserCard/Cards/name-only.tsx +4 -4
  36. package/src/UserCard/Container/dialog.tsx +1 -1
  37. package/src/UserCard/Content/basic.tsx +191 -57
  38. package/src/UserCard/Content/minimal.tsx +4 -3
  39. package/src/UserCard/Content/tooltip-avatar.tsx +10 -5
  40. package/src/UserCard/components.tsx +17 -7
  41. package/src/UserCard/index.tsx +41 -3
  42. package/src/UserCard/types.ts +11 -4
  43. package/src/UserCard/utils.ts +33 -0
  44. package/src/type.d.ts +2 -3
  45. package/src/withTheme/index.tsx +2 -3
@@ -1,4 +1,5 @@
1
1
  import { createContext, useContext, ReactNode, useMemo, useState, useCallback, useEffect } from 'react';
2
+ import { type PaletteMode } from '@mui/material';
2
3
  import type { ThemeOptions } from '@mui/material/styles';
3
4
  import useMediaQuery from '@mui/material/useMediaQuery';
4
5
  import set from 'lodash/set';
@@ -6,10 +7,9 @@ import { ThemeProvider as EmotionThemeProvider } from '@emotion/react';
6
7
  import { LocaleProvider, LocaleProviderProps, useLocaleContext } from '../Locale/context';
7
8
  import ThemeProvider, { ThemeProviderProps } from '../Theme/theme-provider';
8
9
  import { createTheme, useTheme } from '../Theme';
9
- import { ThemeMode } from '../type';
10
10
 
11
11
  export interface ConfigContextType {
12
- mode: ThemeMode;
12
+ mode: PaletteMode;
13
13
  themeOptions: ThemeOptions;
14
14
  toggleMode: () => void;
15
15
  }
@@ -18,8 +18,8 @@ const ConfigContext = createContext<ConfigContextType>({} as ConfigContextType);
18
18
  const preferThemeModeKey = 'blocklet_theme_prefer';
19
19
 
20
20
  export function isThemeRecord(
21
- theme: ThemeOptions | Record<ThemeMode, ThemeOptions>
22
- ): theme is Record<ThemeMode, ThemeOptions> {
21
+ theme: ThemeOptions | Record<PaletteMode, ThemeOptions>
22
+ ): theme is Record<PaletteMode, ThemeOptions> {
23
23
  return 'light' in theme || 'dark' in theme;
24
24
  }
25
25
 
@@ -27,8 +27,8 @@ export interface ConfigProviderProps
27
27
  extends Omit<LocaleProviderProps, 'translations'>,
28
28
  Omit<ThemeProviderProps, 'theme'> {
29
29
  children: ReactNode;
30
- prefer?: ThemeMode;
31
- theme?: ThemeOptions | Record<ThemeMode, ThemeOptions>;
30
+ prefer?: PaletteMode;
31
+ theme?: ThemeOptions | Record<PaletteMode, ThemeOptions>;
32
32
  disableBlockletTheme?: boolean;
33
33
  translations?: Record<string, any>;
34
34
  }
@@ -51,18 +51,18 @@ export function ConfigProvider({
51
51
  onLoadingTranslation,
52
52
  }: ConfigProviderProps) {
53
53
  const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)');
54
- const [mode, setMode] = useState<ThemeMode>(() => {
54
+ const [mode, setMode] = useState<PaletteMode>(() => {
55
55
  if (prefer) {
56
56
  return prefer;
57
57
  }
58
58
 
59
59
  // 未启用暗色主题
60
- if (!window.blocklet?.USE_DARK_THEME) {
60
+ if (['1', 'true'].includes(window.blocklet?.USE_DARK_THEME) === false) {
61
61
  return 'light';
62
62
  }
63
63
 
64
64
  // 本地缓存
65
- const localPrefer = localStorage.getItem(preferThemeModeKey) as ThemeMode;
65
+ const localPrefer = localStorage.getItem(preferThemeModeKey) as PaletteMode;
66
66
  if (localPrefer && (localPrefer === 'light' || localPrefer === 'dark')) {
67
67
  return localPrefer;
68
68
  }
@@ -1,12 +1,11 @@
1
1
  /* eslint-disable no-shadow */
2
2
  // https://app.zeplin.io/styleguide/5d1436f1e97c2156f49c0725/colors
3
- import { createTheme as _createTheme, Components, responsiveFontSizes, type ThemeOptions } from '@mui/material/styles';
3
+ import type { PaletteMode } from '@mui/material';
4
+ import { createTheme as _createTheme, responsiveFontSizes, type ThemeOptions } from '@mui/material/styles';
4
5
  import { deepmerge } from '@mui/utils';
5
- import type { Typography } from '@mui/material/styles/createTypography';
6
6
  import webfontloader from 'webfontloader';
7
+ import { BLOCKLET_THEME_LIGHT, BLOCKLET_THEME_DARK, DEFAULT_FONTS } from '@blocklet/theme';
7
8
 
8
- import colors from '../Colors';
9
- import { ThemeMode } from '../type';
10
9
  import { cleanedObj } from '../Util';
11
10
 
12
11
  // 默认只加载最基本的 roboto latin 字体
@@ -17,44 +16,6 @@ import '@fontsource/roboto/latin-ext-400.css';
17
16
  import '@fontsource/roboto/latin-ext-500.css';
18
17
  import '@fontsource/roboto/latin-ext-700.css';
19
18
 
20
- // 扩展 Theme
21
- declare module '@mui/material/styles' {
22
- interface Theme {
23
- mode?: ThemeMode;
24
- themeName?: string;
25
- pageWidth?: string;
26
- colors?: Record<string, string>;
27
- typography: Typography & {
28
- useNextVariants: boolean;
29
- color: Record<string, string>;
30
- button: {
31
- fontWeight?: number;
32
- };
33
- };
34
- }
35
- interface ThemeOptions {
36
- themeName?: string;
37
- mode?: ThemeMode;
38
- pageWidth?: string;
39
- colors?: Record<string, string>;
40
- /** @deprecated 请使用 components */
41
- overrides?: Components<Omit<Theme, 'components'>>;
42
- }
43
- interface TypeText {
44
- hint: string;
45
- }
46
- }
47
- // 扩展 TypographyOptions
48
- declare module '@mui/material/styles/createTypography' {
49
- interface TypographyOptions {
50
- useNextVariants?: boolean;
51
- color?: Record<string, string>;
52
- }
53
- }
54
-
55
- // 默认深色主题
56
- const defaultDarkTheme = _createTheme({ palette: { mode: 'dark' } });
57
-
58
19
  // 收集字体配置
59
20
  export function collectFontFamilies(obj?: { fontFamily?: string }, fontSet: Set<string> = new Set()): Set<string> {
60
21
  if (!obj || typeof obj !== 'object') return fontSet;
@@ -78,8 +39,7 @@ export function collectFontFamilies(obj?: { fontFamily?: string }, fontSet: Set<
78
39
  }
79
40
 
80
41
  // 动态加载字体
81
- const defaultFonts = ['Roboto', 'Helvetica', 'Arial', 'sans-serif']; // 后三者是系统字体,无需动态加载
82
- const prevFonts = new Set<string>(defaultFonts.concat('inherit')); // inherit 属于 MUI 特殊值,无需动态加载
42
+ const prevFonts = new Set<string>(DEFAULT_FONTS.concat('inherit')); // inherit 属于 MUI 特殊值,无需动态加载
83
43
  export function loadFonts(fonts: string[]) {
84
44
  // 过滤出未加载的字体
85
45
  const unloadedFonts = fonts.filter((font) => !prevFonts.has(font));
@@ -112,105 +72,22 @@ export function loadFonts(fonts: string[]) {
112
72
  }
113
73
 
114
74
  // 创建默认主题配置
115
- export function createDefaultThemeOptions(mode: ThemeMode = 'light'): ThemeOptions {
116
- const result: ThemeOptions = {
117
- palette: {
118
- mode,
119
- ...colors,
120
- background: {
121
- paper: colors.common.white,
122
- default: colors.background.default,
123
- },
124
- },
125
- typography: {
126
- fontFamily: defaultFonts.join(','),
127
- useNextVariants: true,
128
- color: {
129
- // 此处 #222222 必须硬编码, layout/sidebar.js -> Icon/image 加载图片时 color 会影响加载路径
130
- // TODO: 此处硬编码的色值后面需要改为 colors.grey[900],
131
- // 或者如果可以的话直接删掉 typography#color, 文本颜色建议使用 theme.palette.text 中的色值?
132
- // layout 组件建议重构, sidebar 中建议使用 icon 替换 img (#366)
133
- main: mode === 'light' ? '#222222' : colors.common.white,
134
- gray: mode === 'light' ? colors.grey[500] : colors.grey[300],
135
- },
136
- // button 默认使用粗体
137
- button: {
138
- fontWeight: 700,
139
- },
140
- },
141
- components: {
142
- MuiButton: {
143
- styleOverrides: {
144
- root: {
145
- boxShadow: 'none',
146
- },
147
- },
148
- },
149
- MuiButtonGroup: {
150
- styleOverrides: {
151
- root: {
152
- boxShadow: 'none',
153
- },
154
- },
155
- },
156
- MuiTableCell: {
157
- styleOverrides: {
158
- root: ({ ownerState }) => ({
159
- ...(ownerState.size === 'small'
160
- ? {
161
- borderBottomWidth: '0',
162
- paddingTop: '8px',
163
- paddingBottom: '8px',
164
- paddingLeft: 0,
165
- paddingRight: '20px',
166
- }
167
- : {
168
- borderBottomWidth: '0',
169
- paddingTop: '14px',
170
- paddingBottom: '14px',
171
- paddingLeft: 0,
172
- paddingRight: '30px',
173
- }),
174
- }),
175
- head: {
176
- textTransform: 'uppercase',
177
- color: mode === 'light' ? colors.grey[900] : colors.grey[300],
178
- },
179
- body: {
180
- color: mode === 'light' ? colors.grey[900] : colors.grey[300],
181
- },
182
- },
183
- },
184
- },
185
- };
186
-
187
- // 深色主题
75
+ export function createDefaultThemeOptions(mode: PaletteMode = 'light') {
188
76
  if (mode === 'dark') {
189
- result.palette = {
190
- ...defaultDarkTheme.palette,
191
- background: {
192
- paper: colors.grey[900],
193
- default: colors.grey[900],
194
- },
195
- };
77
+ return BLOCKLET_THEME_DARK;
196
78
  }
197
79
 
198
- return result;
80
+ return BLOCKLET_THEME_LIGHT;
199
81
  }
200
82
 
201
- // https://material-ui.com/customization/default-theme/
202
- export const create = ({
203
- disableBlockletTheme = false,
204
- mode = 'light',
205
- pageWidth = 'md',
206
- overrides,
207
- // original theme options
208
- palette,
209
- components,
210
- ...rest
211
- }: ThemeOptions & { disableBlockletTheme?: boolean } = {}) => {
212
- const userThemeOptions: ThemeOptions = {
213
- themeName: 'ArcBlock',
83
+ export interface UserThemeOptions extends ThemeOptions {
84
+ disableBlockletTheme?: boolean;
85
+ }
86
+
87
+ // 主要处理 mode 和 overrides
88
+ const normalizeUserThemeOptions = ({ mode = 'light', palette, components, overrides, ...rest }: UserThemeOptions) => {
89
+ return {
90
+ mode,
214
91
  palette: {
215
92
  ...palette,
216
93
  mode,
@@ -219,43 +96,55 @@ export const create = ({
219
96
  ...overrides,
220
97
  ...components,
221
98
  },
222
- // @TODO 考虑使用 theme.shape.pageWidth
223
- pageWidth,
224
- // @TODO 考虑使用 theme.palette.common
225
- colors: {
226
- white: '#FFFFFF',
227
- dark: '#4A707C',
228
- gray: '#222222',
229
- minor: '#9B9B9B',
230
- darkText: '#DCDCDC',
231
- background: '#F7F8F8',
232
- yellow: '#FFCF71',
233
- green: '#44cdc6',
234
- red: '#D0021B',
235
- blue: '#4E6AF6',
236
- primary: '#222222',
237
- black: '#222222',
238
- secondary: '#44cdc6',
239
- mint: '#44cdc6',
240
- textSecondary: '#4A4A4A',
241
- active: '#5b9025',
242
- danger: '#D0021B',
243
- lightGrey: '#BCBCBC',
244
- },
245
- // @deprecated use theme.palette.mode
246
- mode,
247
99
  ...rest,
248
100
  };
249
- // Blocklet Server 后台配置的全局主题
101
+ };
102
+
103
+ const defaultUserThemeOptions: UserThemeOptions = {
104
+ themeName: 'ArcBlock',
105
+ pageWidth: 'md',
106
+ disableBlockletTheme: false,
107
+ // @deprecated use theme.palette
108
+ colors: {
109
+ white: '#FFFFFF',
110
+ dark: '#4A707C',
111
+ gray: '#222222',
112
+ minor: '#9B9B9B',
113
+ darkText: '#DCDCDC',
114
+ background: '#F7F8F8',
115
+ yellow: '#FFCF71',
116
+ green: '#44cdc6',
117
+ red: '#D0021B',
118
+ blue: '#4E6AF6',
119
+ primary: '#222222',
120
+ black: '#222222',
121
+ secondary: '#44cdc6',
122
+ mint: '#44cdc6',
123
+ textSecondary: '#4A4A4A',
124
+ active: '#5b9025',
125
+ danger: '#D0021B',
126
+ lightGrey: '#BCBCBC',
127
+ },
128
+ };
129
+
130
+ // https://material-ui.com/customization/default-theme/
131
+ export const create = (...args: UserThemeOptions[]) => {
132
+ const userThemeOptions = args.reduce(
133
+ (acc, curr) => deepmerge(acc, normalizeUserThemeOptions(curr)),
134
+ normalizeUserThemeOptions(defaultUserThemeOptions)
135
+ );
136
+ const { mode, disableBlockletTheme } = userThemeOptions;
250
137
  const blockletThemeOptions = window.blocklet?.theme?.[mode] ?? {};
251
- // UX Theme 默认配置
252
138
  const defaultThemeOptions = createDefaultThemeOptions(mode);
139
+
253
140
  // 合并配置
254
141
  let mergedThemeOptions = defaultThemeOptions;
255
142
  if (!disableBlockletTheme) {
256
143
  mergedThemeOptions = deepmerge(defaultThemeOptions, cleanedObj(blockletThemeOptions));
257
144
  }
258
145
  mergedThemeOptions = deepmerge(mergedThemeOptions, cleanedObj(userThemeOptions));
146
+
147
+ // 创建主题
259
148
  const theme = _createTheme(mergedThemeOptions);
260
149
 
261
150
  // 异步加载字体
@@ -263,7 +152,7 @@ export const create = ({
263
152
  loadFonts(Array.from(fonts));
264
153
 
265
154
  /**
266
- * 响应式字体,配置后,theme.typography 会变为下面的结构
155
+ * 支持响应式字体,theme.typography.$variant 中会添加 @media 分支,比如
267
156
  * {
268
157
  * "h1": {
269
158
  * "fontSize": "3rem",
@@ -1,8 +1,9 @@
1
1
  import React from 'react';
2
- import { UserCardProps, InfoType } from '../types';
2
+ import { UserCardProps, InfoType, User } from '../types';
3
3
  import TooltipAvatar from '../Content/tooltip-avatar';
4
4
 
5
- interface AvatarOnlyCardProps extends UserCardProps {
5
+ interface AvatarOnlyCardProps extends Omit<UserCardProps, 'user'> {
6
+ user: User;
6
7
  renderCardContent: () => React.ReactNode;
7
8
  shouldShowHoverCard: boolean;
8
9
  }
@@ -1,10 +1,11 @@
1
1
  import React from 'react';
2
2
  import { Box } from '@mui/material';
3
- import { InfoType, UserCardProps } from '../types';
3
+ import { InfoType, UserCardProps, User } from '../types';
4
4
  import MinimalContent from '../Content/minimal';
5
5
  import BasicContent from '../Content/basic';
6
6
 
7
- interface BasicCardProps extends UserCardProps {
7
+ interface BasicCardProps extends Omit<UserCardProps, 'user'> {
8
+ user: User;
8
9
  shouldShowHoverCard: boolean;
9
10
  renderCardContent?: () => React.ReactNode | null;
10
11
  isFull?: boolean;
@@ -12,14 +13,25 @@ interface BasicCardProps extends UserCardProps {
12
13
 
13
14
  // 详细卡片模式下的Basic渲染组件
14
15
  function BasicCard(props: BasicCardProps) {
15
- const { user, avatarSize = 40, renderCustomContent, isFull = true, infoType = InfoType.Minimal, ...rest } = props;
16
+ const {
17
+ user,
18
+ avatarSize = 40,
19
+ renderCustomContent,
20
+ isFull = true,
21
+ infoType = InfoType.Minimal,
22
+ renderFields,
23
+ popupRenderFields,
24
+ ...rest
25
+ } = props;
16
26
 
17
27
  return (
18
28
  <Box display="flex" flexDirection="column" width="100%" sx={{ flex: 1, minWidth: 0 }}>
19
29
  <MinimalContent user={user} avatarSize={avatarSize} {...rest} />
20
30
 
21
- {infoType === InfoType.Basic && <BasicContent user={user} isFull={isFull} />}
22
- {renderCustomContent && <Box sx={{ mt: 1.5 }}>{renderCustomContent()}</Box>}
31
+ {infoType === InfoType.Basic && <BasicContent user={user} isFull={isFull} renderFields={renderFields} />}
32
+ <Box className="user-card__footer">
33
+ {renderCustomContent && <Box sx={{ mt: 1.5 }}>{renderCustomContent()}</Box>}
34
+ </Box>
23
35
  </Box>
24
36
  );
25
37
  }
@@ -1,9 +1,10 @@
1
1
  import React from 'react';
2
- import { UserCardProps, InfoType } from '../types';
2
+ import { UserCardProps, InfoType, User } from '../types';
3
3
  import NameOnlyCard from './name-only';
4
4
  import BasicCard from './basic-info';
5
5
 
6
- interface DetailedCardProps extends UserCardProps {
6
+ interface DetailedCardProps extends Omit<UserCardProps, 'user'> {
7
+ user: User;
7
8
  shouldShowHoverCard: boolean;
8
9
  renderCardContent?: () => React.ReactNode | null;
9
10
  }
@@ -1,14 +1,14 @@
1
1
  import { Typography } from '@mui/material';
2
- import { UserCardProps } from '../types';
2
+ import { UserCardProps, User } from '../types';
3
3
  import { renderAvatar } from '../components';
4
4
 
5
5
  // 详细卡片模式下的NameOnly渲染组件
6
- function NameOnlyCard(props: UserCardProps) {
7
- const { user, avatarSize = 48 } = props;
6
+ function NameOnlyCard(props: Omit<UserCardProps, 'user'> & { user: User }) {
7
+ const { user, avatarSize = 48, onAvatarClick } = props;
8
8
 
9
9
  return (
10
10
  <>
11
- {renderAvatar(user, avatarSize, props.avatarProps)}
11
+ {renderAvatar(user, avatarSize, props.avatarProps, onAvatarClick)}
12
12
  <Typography variant="body1">{user.fullName || user.email || user.did}</Typography>
13
13
  </>
14
14
  );
@@ -23,7 +23,7 @@ function DialogContainer({ children, sx }: DialogContainerProps) {
23
23
  border: '1px solid',
24
24
  borderColor: 'divider',
25
25
  borderRadius: 2,
26
- maxWidth: 400,
26
+ maxWidth: 500,
27
27
  minWidth: 320,
28
28
  display: 'flex',
29
29
  },