@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.
- package/lib/Theme/theme-provider.js +7 -5
- package/lib/Theme/theme.js +9 -2
- package/lib/hooks/use-location-state.d.ts +16 -0
- package/lib/hooks/use-location-state.js +89 -0
- package/package.json +6 -6
- package/src/Theme/theme-provider.tsx +7 -5
- package/src/Theme/theme.ts +10 -4
- package/src/hooks/use-location-state.tsx +117 -0
@@ -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
|
-
|
182
|
-
|
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, {
|
package/lib/Theme/theme.js
CHANGED
@@ -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.
|
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": "
|
74
|
+
"gitHead": "8236383669dc761248ad704198602e3b5af3bed2",
|
75
75
|
"dependencies": {
|
76
76
|
"@arcblock/did-motif": "^1.1.13",
|
77
|
-
"@arcblock/icons": "^2.13.
|
78
|
-
"@arcblock/nft-display": "^2.13.
|
79
|
-
"@arcblock/react-hooks": "^2.13.
|
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.
|
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
|
-
|
227
|
-
|
228
|
-
}
|
229
|
-
}, [prefer, setMode]);
|
230
|
+
setMode(resolveMode(prefer));
|
231
|
+
}, [prefer, setMode, location.search]);
|
230
232
|
|
231
233
|
return (
|
232
234
|
<ColorSchemeContext.Provider value={colorSchemeValue}>
|
package/src/Theme/theme.ts
CHANGED
@@ -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
|
-
|
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;
|