@arcblock/ux 2.13.46 → 2.13.47

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.
@@ -7,6 +7,7 @@ import StyledEngineProvider from '@mui/material/StyledEngineProvider';
7
7
  import CssBaseline from '@mui/material/CssBaseline';
8
8
  import set from 'lodash/set';
9
9
  import { BLOCKLET_THEME_PREFER_KEY } from '@blocklet/theme';
10
+ import useLocationState from '../hooks/use-location-state';
10
11
  import { createTheme, getDefaultThemePrefer, isTheme, isUxTheme, lazyCreateDefaultTheme } from './theme';
11
12
  const defaultTheme = createTheme();
12
13
 
@@ -19,9 +20,9 @@ export function useColorScheme() {
19
20
 
20
21
  /** 根据偏好获取颜色模式 */
21
22
  const resolveMode = prefer => {
23
+ // 允许组件的 prefer 属性覆盖 blocklet theme 中配置的 prefer
22
24
  if (prefer) {
23
25
  if (prefer === 'system') {
24
- // 取系统默认
25
26
  return getDefaultThemePrefer({
26
27
  theme: {
27
28
  prefer: 'system'
@@ -123,6 +124,7 @@ function ColorSchemeProvider({
123
124
  }) {
124
125
  const [mode, setMode] = useState(() => resolveMode(prefer));
125
126
  const parentTheme = useTheme();
127
+ const location = useLocationState();
126
128
  const _themeInput = useMemo(() => {
127
129
  let result = {};
128
130
  const createBaseTheme = lazyCreateDefaultTheme(mode);
@@ -177,11 +179,11 @@ function ColorSchemeProvider({
177
179
  changeMode,
178
180
  prefer
179
181
  }), [mode, prefer, toggleMode, changeMode]);
182
+
183
+ // 监听 prefer 或者 url.search 变化
180
184
  useEffect(() => {
181
- if (prefer) {
182
- setMode(resolveMode(prefer));
183
- }
184
- }, [prefer, setMode]);
185
+ setMode(resolveMode(prefer));
186
+ }, [prefer, setMode, location.search]);
185
187
  return /*#__PURE__*/_jsx(ColorSchemeContext.Provider, {
186
188
  value: colorSchemeValue,
187
189
  children: /*#__PURE__*/_jsx(BaseThemeProvider, {
@@ -73,9 +73,15 @@ export function loadFonts(fonts) {
73
73
 
74
74
  // 获取默认主题偏好
75
75
  export function getDefaultThemePrefer(meta) {
76
+ // 跟随 url theme 参数
77
+ const urlParams = new URLSearchParams(window.location.search);
78
+ const urlPrefer = urlParams.get('theme');
79
+ if (urlPrefer === 'light' || urlPrefer === 'dark') {
80
+ return urlPrefer;
81
+ }
76
82
  const prefer = Object.assign({}, window.blocklet, meta).theme?.prefer;
77
83
  if (prefer === 'system') {
78
- // 本地缓存
84
+ // 跟随本地缓存
79
85
  const localPrefer = localStorage.getItem(BLOCKLET_THEME_PREFER_KEY);
80
86
  if (localPrefer && (localPrefer === 'light' || localPrefer === 'dark')) {
81
87
  return localPrefer;
@@ -84,11 +90,12 @@ export function getDefaultThemePrefer(meta) {
84
90
  // 跟随系统
85
91
  return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
86
92
  }
93
+ // 跟随 blocklet theme mode
87
94
  if (prefer === 'light' || prefer === 'dark') {
88
95
  return prefer;
89
96
  }
90
97
 
91
- // 未设置偏好
98
+ // fallback
92
99
  return 'light';
93
100
  }
94
101
 
@@ -0,0 +1,16 @@
1
+ export interface LocationLike {
2
+ href: string;
3
+ origin: string;
4
+ host: string;
5
+ hostname: string;
6
+ port: string;
7
+ protocol: string;
8
+ pathname: string;
9
+ search: string;
10
+ hash: string;
11
+ }
12
+ /**
13
+ * 监听 location 变化,返回当前 location 状态
14
+ */
15
+ export declare function useLocationState(): LocationLike;
16
+ export default useLocationState;
@@ -0,0 +1,89 @@
1
+ /* eslint-disable no-restricted-globals */
2
+ import { useEffect, useState } from 'react';
3
+ const subscribers = new Set();
4
+ let patched = false;
5
+ let lastHref = '';
6
+
7
+ // 检查是否在浏览器环境中
8
+ const isBrowser = typeof window !== 'undefined';
9
+ function notifyIfChanged() {
10
+ if (!isBrowser) return;
11
+ const currentHref = window.location.href;
12
+ if (currentHref !== lastHref) {
13
+ lastHref = currentHref;
14
+ subscribers.forEach(cb => cb(window.location));
15
+ }
16
+ }
17
+ function patchHistoryOnce() {
18
+ if (!isBrowser || patched) return;
19
+ patched = true;
20
+ const originalPushState = history.pushState;
21
+ history.pushState = function patchedPushState(...args) {
22
+ const result = originalPushState.apply(this, args);
23
+ notifyIfChanged();
24
+ return result;
25
+ };
26
+ const originalReplaceState = history.replaceState;
27
+ history.replaceState = function patchedReplaceState(...args) {
28
+ const result = originalReplaceState.apply(this, args);
29
+ notifyIfChanged();
30
+ return result;
31
+ };
32
+ window.addEventListener('popstate', notifyIfChanged);
33
+ window.addEventListener('hashchange', notifyIfChanged);
34
+ }
35
+ function subscribeUrlChange(callback) {
36
+ if (isBrowser) {
37
+ patchHistoryOnce();
38
+ subscribers.add(callback);
39
+ }
40
+ return () => {
41
+ subscribers.delete(callback);
42
+ };
43
+ }
44
+ function extractLocation(location) {
45
+ return {
46
+ href: location.href,
47
+ origin: location.origin,
48
+ host: location.host,
49
+ hostname: location.hostname,
50
+ pathname: location.pathname,
51
+ port: location.port,
52
+ protocol: location.protocol,
53
+ search: location.search,
54
+ hash: location.hash
55
+ };
56
+ }
57
+ const defaultLocation = {
58
+ href: '',
59
+ origin: '',
60
+ host: '',
61
+ hostname: '',
62
+ port: '',
63
+ protocol: '',
64
+ pathname: '',
65
+ search: '',
66
+ hash: ''
67
+ };
68
+
69
+ /**
70
+ * 监听 location 变化,返回当前 location 状态
71
+ */
72
+ export function useLocationState() {
73
+ const [location, setLocation] = useState(() => {
74
+ if (!isBrowser) {
75
+ return defaultLocation;
76
+ }
77
+ return extractLocation(window.location);
78
+ });
79
+ useEffect(() => {
80
+ if (!isBrowser) {
81
+ return undefined;
82
+ }
83
+ return subscribeUrlChange(newLocation => {
84
+ setLocation(extractLocation(newLocation));
85
+ });
86
+ }, []);
87
+ return location;
88
+ }
89
+ export default useLocationState;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arcblock/ux",
3
- "version": "2.13.46",
3
+ "version": "2.13.47",
4
4
  "description": "Common used react components for arcblock products",
5
5
  "keywords": [
6
6
  "react",
@@ -71,14 +71,14 @@
71
71
  "react": ">=18.2.0",
72
72
  "react-router-dom": ">=6.22.3"
73
73
  },
74
- "gitHead": "1e351b068cb3856aa4c8b1bf9f0067d95e8aec17",
74
+ "gitHead": "8236383669dc761248ad704198602e3b5af3bed2",
75
75
  "dependencies": {
76
76
  "@arcblock/did-motif": "^1.1.13",
77
- "@arcblock/icons": "^2.13.46",
78
- "@arcblock/nft-display": "^2.13.46",
79
- "@arcblock/react-hooks": "^2.13.46",
77
+ "@arcblock/icons": "^2.13.47",
78
+ "@arcblock/nft-display": "^2.13.47",
79
+ "@arcblock/react-hooks": "^2.13.47",
80
80
  "@babel/plugin-syntax-dynamic-import": "^7.8.3",
81
- "@blocklet/theme": "^2.13.46",
81
+ "@blocklet/theme": "^2.13.47",
82
82
  "@fontsource/roboto": "~5.1.1",
83
83
  "@fontsource/ubuntu-mono": "^5.0.18",
84
84
  "@iconify-icons/logos": "^1.2.36",
@@ -7,6 +7,8 @@ import CssBaseline from '@mui/material/CssBaseline';
7
7
  import set from 'lodash/set';
8
8
  import { BLOCKLET_THEME_PREFER_KEY } from '@blocklet/theme';
9
9
 
10
+ import useLocationState from '../hooks/use-location-state';
11
+
10
12
  import {
11
13
  createTheme,
12
14
  getDefaultThemePrefer,
@@ -33,9 +35,9 @@ export function useColorScheme() {
33
35
 
34
36
  /** 根据偏好获取颜色模式 */
35
37
  const resolveMode = (prefer?: Prefer): PaletteMode => {
38
+ // 允许组件的 prefer 属性覆盖 blocklet theme 中配置的 prefer
36
39
  if (prefer) {
37
40
  if (prefer === 'system') {
38
- // 取系统默认
39
41
  return getDefaultThemePrefer({ theme: { prefer: 'system' } });
40
42
  }
41
43
  return prefer;
@@ -166,6 +168,7 @@ function ColorSchemeProvider({
166
168
  }: ThemeProviderProps) {
167
169
  const [mode, setMode] = useState<PaletteMode>(() => resolveMode(prefer));
168
170
  const parentTheme = useTheme();
171
+ const location = useLocationState();
169
172
 
170
173
  const _themeInput = useMemo(() => {
171
174
  let result: UxThemeOptions = {};
@@ -222,11 +225,10 @@ function ColorSchemeProvider({
222
225
  [mode, prefer, toggleMode, changeMode]
223
226
  );
224
227
 
228
+ // 监听 prefer 或者 url.search 变化
225
229
  useEffect(() => {
226
- if (prefer) {
227
- setMode(resolveMode(prefer));
228
- }
229
- }, [prefer, setMode]);
230
+ setMode(resolveMode(prefer));
231
+ }, [prefer, setMode, location.search]);
230
232
 
231
233
  return (
232
234
  <ColorSchemeContext.Provider value={colorSchemeValue}>
@@ -82,10 +82,16 @@ export function loadFonts(fonts: string[]) {
82
82
 
83
83
  // 获取默认主题偏好
84
84
  export function getDefaultThemePrefer(meta?: { theme: { prefer: 'light' | 'dark' | 'system' } }): PaletteMode {
85
- const prefer = Object.assign({}, window.blocklet, meta).theme?.prefer;
85
+ // 跟随 url theme 参数
86
+ const urlParams = new URLSearchParams(window.location.search);
87
+ const urlPrefer = urlParams.get('theme');
88
+ if (urlPrefer === 'light' || urlPrefer === 'dark') {
89
+ return urlPrefer;
90
+ }
86
91
 
92
+ const prefer = Object.assign({}, window.blocklet, meta).theme?.prefer;
87
93
  if (prefer === 'system') {
88
- // 本地缓存
94
+ // 跟随本地缓存
89
95
  const localPrefer = localStorage.getItem(BLOCKLET_THEME_PREFER_KEY) as PaletteMode;
90
96
  if (localPrefer && (localPrefer === 'light' || localPrefer === 'dark')) {
91
97
  return localPrefer;
@@ -94,12 +100,12 @@ export function getDefaultThemePrefer(meta?: { theme: { prefer: 'light' | 'dark'
94
100
  // 跟随系统
95
101
  return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
96
102
  }
97
-
103
+ // 跟随 blocklet theme mode
98
104
  if (prefer === 'light' || prefer === 'dark') {
99
105
  return prefer;
100
106
  }
101
107
 
102
- // 未设置偏好
108
+ // fallback
103
109
  return 'light';
104
110
  }
105
111
 
@@ -0,0 +1,117 @@
1
+ /* eslint-disable no-restricted-globals */
2
+ import { useEffect, useState } from 'react';
3
+
4
+ type UrlChangeCallback = (location: Location) => void;
5
+
6
+ const subscribers = new Set<UrlChangeCallback>();
7
+ let patched = false;
8
+ let lastHref = '';
9
+
10
+ // 检查是否在浏览器环境中
11
+ const isBrowser = typeof window !== 'undefined';
12
+
13
+ function notifyIfChanged() {
14
+ if (!isBrowser) return;
15
+
16
+ const currentHref = window.location.href;
17
+ if (currentHref !== lastHref) {
18
+ lastHref = currentHref;
19
+ subscribers.forEach((cb) => cb(window.location));
20
+ }
21
+ }
22
+
23
+ function patchHistoryOnce() {
24
+ if (!isBrowser || patched) return;
25
+ patched = true;
26
+
27
+ const originalPushState = history.pushState;
28
+ history.pushState = function patchedPushState(...args) {
29
+ const result = originalPushState.apply(this, args);
30
+ notifyIfChanged();
31
+ return result;
32
+ };
33
+
34
+ const originalReplaceState = history.replaceState;
35
+ history.replaceState = function patchedReplaceState(...args) {
36
+ const result = originalReplaceState.apply(this, args);
37
+ notifyIfChanged();
38
+ return result;
39
+ };
40
+
41
+ window.addEventListener('popstate', notifyIfChanged);
42
+ window.addEventListener('hashchange', notifyIfChanged);
43
+ }
44
+
45
+ function subscribeUrlChange(callback: UrlChangeCallback): () => void {
46
+ if (isBrowser) {
47
+ patchHistoryOnce();
48
+ subscribers.add(callback);
49
+ }
50
+ return () => {
51
+ subscribers.delete(callback);
52
+ };
53
+ }
54
+
55
+ export interface LocationLike {
56
+ href: string;
57
+ origin: string;
58
+ host: string;
59
+ hostname: string;
60
+ port: string;
61
+ protocol: string;
62
+ pathname: string;
63
+ search: string;
64
+ hash: string;
65
+ }
66
+
67
+ function extractLocation(location: Location): LocationLike {
68
+ return {
69
+ href: location.href,
70
+ origin: location.origin,
71
+ host: location.host,
72
+ hostname: location.hostname,
73
+ pathname: location.pathname,
74
+ port: location.port,
75
+ protocol: location.protocol,
76
+ search: location.search,
77
+ hash: location.hash,
78
+ };
79
+ }
80
+
81
+ const defaultLocation: LocationLike = {
82
+ href: '',
83
+ origin: '',
84
+ host: '',
85
+ hostname: '',
86
+ port: '',
87
+ protocol: '',
88
+ pathname: '',
89
+ search: '',
90
+ hash: '',
91
+ };
92
+
93
+ /**
94
+ * 监听 location 变化,返回当前 location 状态
95
+ */
96
+ export function useLocationState(): LocationLike {
97
+ const [location, setLocation] = useState<LocationLike>(() => {
98
+ if (!isBrowser) {
99
+ return defaultLocation;
100
+ }
101
+ return extractLocation(window.location);
102
+ });
103
+
104
+ useEffect(() => {
105
+ if (!isBrowser) {
106
+ return undefined;
107
+ }
108
+
109
+ return subscribeUrlChange((newLocation) => {
110
+ setLocation(extractLocation(newLocation));
111
+ });
112
+ }, []);
113
+
114
+ return location;
115
+ }
116
+
117
+ export default useLocationState;