@particle-network/ui-react 0.4.0-beta.0 → 0.4.0-beta.2
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/dist/components/UXThemeSwitch/index.d.ts +2 -0
- package/dist/components/UXThemeSwitch/index.js +2 -0
- package/dist/components/UXThemeSwitch/theme-data.d.ts +5 -4
- package/dist/components/UXThemeSwitch/theme-data.js +13 -13
- package/dist/components/UXThemeSwitch/theme-item.d.ts +1 -1
- package/dist/components/UXThemeSwitch/theme-item.js +26 -17
- package/dist/components/UXThemeSwitch/theme-switch.d.ts +3 -0
- package/dist/components/UXThemeSwitch/theme-switch.js +104 -76
- package/dist/components/UXThemeSwitch/use-color-scheme.d.ts +5 -0
- package/dist/components/UXThemeSwitch/use-color-scheme.js +11 -0
- package/dist/components/UXThemeSwitch/use-theme-color.d.ts +19 -0
- package/dist/components/UXThemeSwitch/use-theme-color.js +6 -0
- package/dist/components/UXThemeSwitch/use-theme-store.d.ts +16 -0
- package/dist/components/UXThemeSwitch/use-theme-store.js +20 -1
- package/dist/components/UXThemeSwitch/use-theme.d.ts +4 -0
- package/dist/components/UXThemeSwitch/use-theme.js +47 -11
- package/dist/hooks/useI18n.d.ts +3 -0
- package/dist/hooks/useI18n.js +8 -2
- package/package.json +3 -3
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
export type
|
|
1
|
+
export type ThemeId = 'ux-purple-gold-dark' | 'ux-purple-gold-light' | 'ux-green-red-dark' | 'ux-green-red-light' | 'ux-green-red-soft-dark' | 'ux-green-red-soft-light' | 'gmgn-dark' | 'axiom-dark' | 'hyperliquid-dark' | 'phantom-dark' | 'binance-dark' | 'bullx-dark';
|
|
2
|
+
export type ColorScheme = 'dark' | 'light';
|
|
2
3
|
export interface ThemeItemType {
|
|
3
|
-
|
|
4
|
+
id: ThemeId;
|
|
4
5
|
zhName: string;
|
|
5
6
|
enName: string;
|
|
6
|
-
colorScheme:
|
|
7
|
+
colorScheme: ColorScheme;
|
|
7
8
|
colorVariables: {
|
|
8
9
|
foreground: string;
|
|
9
10
|
secondary: string;
|
|
@@ -25,4 +26,4 @@ export interface ThemeItemType {
|
|
|
25
26
|
};
|
|
26
27
|
}
|
|
27
28
|
export declare const themeData: ThemeItemType[];
|
|
28
|
-
export declare const themeKeys:
|
|
29
|
+
export declare const themeKeys: ThemeId[];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const themeData = [
|
|
2
2
|
{
|
|
3
|
-
|
|
3
|
+
id: 'ux-purple-gold-dark',
|
|
4
4
|
zhName: 'UX 紫金 Dark',
|
|
5
5
|
enName: 'UX Purple/Gold Dark',
|
|
6
6
|
colorScheme: 'dark',
|
|
@@ -25,7 +25,7 @@ const themeData = [
|
|
|
25
25
|
}
|
|
26
26
|
},
|
|
27
27
|
{
|
|
28
|
-
|
|
28
|
+
id: 'ux-purple-gold-light',
|
|
29
29
|
zhName: 'UX 紫金 Light',
|
|
30
30
|
enName: 'UX Purple/Gold Light',
|
|
31
31
|
colorScheme: 'light',
|
|
@@ -50,7 +50,7 @@ const themeData = [
|
|
|
50
50
|
}
|
|
51
51
|
},
|
|
52
52
|
{
|
|
53
|
-
|
|
53
|
+
id: 'ux-green-red-dark',
|
|
54
54
|
zhName: 'UX 绿红 Dark',
|
|
55
55
|
enName: 'UX Green/Red Dark',
|
|
56
56
|
colorScheme: 'dark',
|
|
@@ -75,7 +75,7 @@ const themeData = [
|
|
|
75
75
|
}
|
|
76
76
|
},
|
|
77
77
|
{
|
|
78
|
-
|
|
78
|
+
id: 'ux-green-red-light',
|
|
79
79
|
zhName: 'UX 绿红 Light',
|
|
80
80
|
enName: 'UX Green/Red Light',
|
|
81
81
|
colorScheme: 'light',
|
|
@@ -100,7 +100,7 @@ const themeData = [
|
|
|
100
100
|
}
|
|
101
101
|
},
|
|
102
102
|
{
|
|
103
|
-
|
|
103
|
+
id: 'ux-green-red-soft-dark',
|
|
104
104
|
zhName: 'UX 柔和 Dark',
|
|
105
105
|
enName: 'UX Soft Dark',
|
|
106
106
|
colorScheme: 'dark',
|
|
@@ -125,7 +125,7 @@ const themeData = [
|
|
|
125
125
|
}
|
|
126
126
|
},
|
|
127
127
|
{
|
|
128
|
-
|
|
128
|
+
id: 'ux-green-red-soft-light',
|
|
129
129
|
zhName: 'UX 柔和 Light',
|
|
130
130
|
enName: 'UX Soft Light',
|
|
131
131
|
colorScheme: 'light',
|
|
@@ -150,7 +150,7 @@ const themeData = [
|
|
|
150
150
|
}
|
|
151
151
|
},
|
|
152
152
|
{
|
|
153
|
-
|
|
153
|
+
id: 'gmgn-dark',
|
|
154
154
|
zhName: 'GMGN',
|
|
155
155
|
enName: 'GMGN',
|
|
156
156
|
colorScheme: 'dark',
|
|
@@ -175,7 +175,7 @@ const themeData = [
|
|
|
175
175
|
}
|
|
176
176
|
},
|
|
177
177
|
{
|
|
178
|
-
|
|
178
|
+
id: 'axiom-dark',
|
|
179
179
|
zhName: 'AXIOM',
|
|
180
180
|
enName: 'AXIOM',
|
|
181
181
|
colorScheme: 'dark',
|
|
@@ -200,7 +200,7 @@ const themeData = [
|
|
|
200
200
|
}
|
|
201
201
|
},
|
|
202
202
|
{
|
|
203
|
-
|
|
203
|
+
id: 'hyperliquid-dark',
|
|
204
204
|
zhName: 'Hyperliquid',
|
|
205
205
|
enName: 'Hyperliquid',
|
|
206
206
|
colorScheme: 'dark',
|
|
@@ -225,7 +225,7 @@ const themeData = [
|
|
|
225
225
|
}
|
|
226
226
|
},
|
|
227
227
|
{
|
|
228
|
-
|
|
228
|
+
id: 'phantom-dark',
|
|
229
229
|
zhName: 'Phantom',
|
|
230
230
|
enName: 'Phantom',
|
|
231
231
|
colorScheme: 'dark',
|
|
@@ -250,7 +250,7 @@ const themeData = [
|
|
|
250
250
|
}
|
|
251
251
|
},
|
|
252
252
|
{
|
|
253
|
-
|
|
253
|
+
id: 'binance-dark',
|
|
254
254
|
zhName: 'Binance',
|
|
255
255
|
enName: 'Binance',
|
|
256
256
|
colorScheme: 'dark',
|
|
@@ -275,7 +275,7 @@ const themeData = [
|
|
|
275
275
|
}
|
|
276
276
|
},
|
|
277
277
|
{
|
|
278
|
-
|
|
278
|
+
id: 'bullx-dark',
|
|
279
279
|
zhName: "Bullx's",
|
|
280
280
|
enName: "Bullx's",
|
|
281
281
|
colorScheme: 'dark',
|
|
@@ -300,5 +300,5 @@ const themeData = [
|
|
|
300
300
|
}
|
|
301
301
|
}
|
|
302
302
|
];
|
|
303
|
-
const themeKeys = themeData.map((theme)=>theme.
|
|
303
|
+
const themeKeys = themeData.map((theme)=>theme.id);
|
|
304
304
|
export { themeData, themeKeys };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { ThemeItemType } from './theme-data';
|
|
3
|
-
export interface ThemeItemProps extends
|
|
3
|
+
export interface ThemeItemProps extends ThemeItemType {
|
|
4
4
|
isSelected: boolean;
|
|
5
5
|
onClick: () => void;
|
|
6
6
|
}
|
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import "react";
|
|
3
|
+
import { cn } from "@heroui/theme";
|
|
3
4
|
import { useLang } from "../../hooks/index.js";
|
|
4
5
|
import { VStack } from "../layout/index.js";
|
|
5
6
|
import { Text } from "../typography/Text.js";
|
|
6
|
-
const ThemeItem = ({ zhName, enName,
|
|
7
|
+
const ThemeItem = ({ id, zhName, enName, isSelected, onClick })=>{
|
|
7
8
|
const lang = useLang();
|
|
8
9
|
return /*#__PURE__*/ jsxs(VStack, {
|
|
9
10
|
center: true,
|
|
10
|
-
className:
|
|
11
|
+
className: cn('cursor-pointer hover:scale-105 transition-all duration-300', id),
|
|
11
12
|
onClick: onClick,
|
|
12
13
|
children: [
|
|
13
14
|
/*#__PURE__*/ jsx("div", {
|
|
14
|
-
className:
|
|
15
|
-
style: {
|
|
16
|
-
borderColor: isSelected ? colorVariables.primary : 'transparent'
|
|
17
|
-
},
|
|
15
|
+
className: cn('rounded-small border-1.5', isSelected ? 'border-primary' : 'border-transparent'),
|
|
18
16
|
children: /*#__PURE__*/ jsxs("svg", {
|
|
19
17
|
xmlns: "http://www.w3.org/2000/svg",
|
|
20
18
|
width: "180",
|
|
@@ -29,7 +27,8 @@ const ThemeItem = ({ zhName, enName, colorVariables, isSelected, onClick })=>{
|
|
|
29
27
|
width: "116.19",
|
|
30
28
|
height: "67",
|
|
31
29
|
rx: "5",
|
|
32
|
-
fill:
|
|
30
|
+
fill: "currentColor",
|
|
31
|
+
className: "text-primary"
|
|
33
32
|
}),
|
|
34
33
|
/*#__PURE__*/ jsx("mask", {
|
|
35
34
|
id: "mask0_40928_218196",
|
|
@@ -47,7 +46,8 @@ const ThemeItem = ({ zhName, enName, colorVariables, isSelected, onClick })=>{
|
|
|
47
46
|
width: "116.19",
|
|
48
47
|
height: "67",
|
|
49
48
|
rx: "5",
|
|
50
|
-
fill:
|
|
49
|
+
fill: "currentColor",
|
|
50
|
+
className: "text-background"
|
|
51
51
|
})
|
|
52
52
|
}),
|
|
53
53
|
/*#__PURE__*/ jsx("g", {
|
|
@@ -58,7 +58,8 @@ const ThemeItem = ({ zhName, enName, colorVariables, isSelected, onClick })=>{
|
|
|
58
58
|
width: "116.19",
|
|
59
59
|
height: "67",
|
|
60
60
|
rx: "6",
|
|
61
|
-
fill:
|
|
61
|
+
fill: "currentColor",
|
|
62
|
+
className: "text-background"
|
|
62
63
|
})
|
|
63
64
|
}),
|
|
64
65
|
/*#__PURE__*/ jsx("rect", {
|
|
@@ -66,7 +67,8 @@ const ThemeItem = ({ zhName, enName, colorVariables, isSelected, onClick })=>{
|
|
|
66
67
|
y: "31",
|
|
67
68
|
width: "100",
|
|
68
69
|
height: "1",
|
|
69
|
-
fill:
|
|
70
|
+
fill: "currentColor",
|
|
71
|
+
className: "text-divider"
|
|
70
72
|
}),
|
|
71
73
|
/*#__PURE__*/ jsx("rect", {
|
|
72
74
|
x: "54.082",
|
|
@@ -74,7 +76,8 @@ const ThemeItem = ({ zhName, enName, colorVariables, isSelected, onClick })=>{
|
|
|
74
76
|
width: "30.0917",
|
|
75
77
|
height: "6.01835",
|
|
76
78
|
rx: "3.00917",
|
|
77
|
-
fill:
|
|
79
|
+
fill: "currentColor",
|
|
80
|
+
className: "text-foreground"
|
|
78
81
|
}),
|
|
79
82
|
/*#__PURE__*/ jsx("rect", {
|
|
80
83
|
x: "54.082",
|
|
@@ -82,7 +85,8 @@ const ThemeItem = ({ zhName, enName, colorVariables, isSelected, onClick })=>{
|
|
|
82
85
|
width: "19.5596",
|
|
83
86
|
height: "6.01835",
|
|
84
87
|
rx: "3.00917",
|
|
85
|
-
fill:
|
|
88
|
+
fill: "currentColor",
|
|
89
|
+
className: "text-bullish"
|
|
86
90
|
}),
|
|
87
91
|
/*#__PURE__*/ jsx("rect", {
|
|
88
92
|
x: "75.8994",
|
|
@@ -90,7 +94,8 @@ const ThemeItem = ({ zhName, enName, colorVariables, isSelected, onClick })=>{
|
|
|
90
94
|
width: "19.5596",
|
|
91
95
|
height: "6.01835",
|
|
92
96
|
rx: "3.00917",
|
|
93
|
-
fill:
|
|
97
|
+
fill: "currentColor",
|
|
98
|
+
className: "text-bearish"
|
|
94
99
|
}),
|
|
95
100
|
/*#__PURE__*/ jsx("rect", {
|
|
96
101
|
x: "86.4316",
|
|
@@ -98,7 +103,8 @@ const ThemeItem = ({ zhName, enName, colorVariables, isSelected, onClick })=>{
|
|
|
98
103
|
width: "6.01835",
|
|
99
104
|
height: "6.01835",
|
|
100
105
|
rx: "3.00917",
|
|
101
|
-
fill:
|
|
106
|
+
fill: "currentColor",
|
|
107
|
+
className: "text-secondary"
|
|
102
108
|
}),
|
|
103
109
|
/*#__PURE__*/ jsx("rect", {
|
|
104
110
|
x: "94.707",
|
|
@@ -106,7 +112,8 @@ const ThemeItem = ({ zhName, enName, colorVariables, isSelected, onClick })=>{
|
|
|
106
112
|
width: "6.01835",
|
|
107
113
|
height: "6.01835",
|
|
108
114
|
rx: "3.00917",
|
|
109
|
-
fill:
|
|
115
|
+
fill: "currentColor",
|
|
116
|
+
className: "text-secondary"
|
|
110
117
|
}),
|
|
111
118
|
/*#__PURE__*/ jsx("rect", {
|
|
112
119
|
x: "102.981",
|
|
@@ -114,7 +121,8 @@ const ThemeItem = ({ zhName, enName, colorVariables, isSelected, onClick })=>{
|
|
|
114
121
|
width: "6.01835",
|
|
115
122
|
height: "6.01835",
|
|
116
123
|
rx: "3.00917",
|
|
117
|
-
fill:
|
|
124
|
+
fill: "currentColor",
|
|
125
|
+
className: "text-secondary"
|
|
118
126
|
}),
|
|
119
127
|
/*#__PURE__*/ jsx("rect", {
|
|
120
128
|
x: "27",
|
|
@@ -122,7 +130,8 @@ const ThemeItem = ({ zhName, enName, colorVariables, isSelected, onClick })=>{
|
|
|
122
130
|
width: "22.5688",
|
|
123
131
|
height: "22.5688",
|
|
124
132
|
rx: "11.2844",
|
|
125
|
-
fill:
|
|
133
|
+
fill: "currentColor",
|
|
134
|
+
className: "text-primary"
|
|
126
135
|
})
|
|
127
136
|
]
|
|
128
137
|
})
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import type { DrawerProps } from '@heroui/drawer';
|
|
3
|
+
export type UXThemeSwitchDrawerProps = Omit<DrawerProps, 'children'>;
|
|
4
|
+
export declare const UXThemeSwitchDrawer: React.FC<UXThemeSwitchDrawerProps>;
|
|
2
5
|
export interface UXThemeSwitchProps {
|
|
3
6
|
children?: (onOpen: () => void) => React.ReactNode;
|
|
4
7
|
}
|
|
@@ -2,22 +2,22 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import "react";
|
|
3
3
|
import { Drawer, DrawerBody, DrawerContent, DrawerFooter, DrawerHeader } from "@heroui/drawer";
|
|
4
4
|
import { useDisclosure } from "@heroui/use-disclosure";
|
|
5
|
-
import ThemeSwitchIcon from "@particle-network/icons/web
|
|
5
|
+
import { ThemeSwitchIcon } from "@particle-network/icons/web";
|
|
6
6
|
import { useI18n } from "../../hooks/index.js";
|
|
7
7
|
import { Flex, HStack, VStack } from "../layout/index.js";
|
|
8
8
|
import { Text } from "../typography/Text.js";
|
|
9
9
|
import { UXButton } from "../UXButton/index.js";
|
|
10
10
|
import { UXDivider } from "../UXDivider/index.js";
|
|
11
11
|
import { UXInput } from "../UXInput/index.js";
|
|
12
|
+
import { UXSpinner } from "../UXSpinner/index.js";
|
|
12
13
|
import { themeData } from "./theme-data.js";
|
|
13
14
|
import { ThemeItem } from "./theme-item.js";
|
|
14
15
|
import { useTheme } from "./use-theme.js";
|
|
15
|
-
const
|
|
16
|
+
const UXThemeSwitchDrawer = ({ isOpen, onClose, onOpenChange, ...props })=>{
|
|
16
17
|
const i18n = useI18n();
|
|
17
|
-
const {
|
|
18
|
-
const { selectedTheme, setSelectedTheme, savedTheme, setSavedTheme, selectedFontLink, setSelectedFontLink, savedFontLink, setSavedFontLink } = useTheme();
|
|
18
|
+
const { selectedTheme, setSelectedTheme, savedTheme, setSavedTheme, selectedFontLink, setSelectedFontLink, savedFontLink, setSavedFontLink, fontName, fontLoadStatus, fontLoadError } = useTheme();
|
|
19
19
|
const handleOpenChange = (_isOpen)=>{
|
|
20
|
-
onOpenChange();
|
|
20
|
+
onOpenChange?.(_isOpen);
|
|
21
21
|
if (!_isOpen) {
|
|
22
22
|
setSelectedTheme(savedTheme);
|
|
23
23
|
setSelectedFontLink(savedFontLink);
|
|
@@ -26,12 +26,106 @@ const UXThemeSwitch = ({ children })=>{
|
|
|
26
26
|
const handleApplyTheme = ()=>{
|
|
27
27
|
setSavedTheme(selectedTheme);
|
|
28
28
|
setSavedFontLink(selectedFontLink);
|
|
29
|
-
onClose();
|
|
29
|
+
onClose?.();
|
|
30
30
|
};
|
|
31
31
|
const handleReset = ()=>{
|
|
32
32
|
setSelectedTheme(savedTheme);
|
|
33
33
|
setSelectedFontLink(savedFontLink);
|
|
34
34
|
};
|
|
35
|
+
return /*#__PURE__*/ jsx(Drawer, {
|
|
36
|
+
isOpen: isOpen,
|
|
37
|
+
backdrop: "transparent",
|
|
38
|
+
onOpenChange: handleOpenChange,
|
|
39
|
+
...props,
|
|
40
|
+
children: /*#__PURE__*/ jsxs(DrawerContent, {
|
|
41
|
+
children: [
|
|
42
|
+
/*#__PURE__*/ jsx(DrawerHeader, {
|
|
43
|
+
className: "flex flex-col gap-1",
|
|
44
|
+
children: i18n.theme.title
|
|
45
|
+
}),
|
|
46
|
+
/*#__PURE__*/ jsxs(DrawerBody, {
|
|
47
|
+
children: [
|
|
48
|
+
/*#__PURE__*/ jsx(Flex, {
|
|
49
|
+
wrap: true,
|
|
50
|
+
justify: "between",
|
|
51
|
+
className: "gap-y-5",
|
|
52
|
+
children: themeData.map((theme)=>/*#__PURE__*/ jsx(ThemeItem, {
|
|
53
|
+
isSelected: selectedTheme.id === theme.id,
|
|
54
|
+
onClick: ()=>setSelectedTheme(theme),
|
|
55
|
+
...theme
|
|
56
|
+
}, theme.id))
|
|
57
|
+
}),
|
|
58
|
+
/*#__PURE__*/ jsx(UXDivider, {
|
|
59
|
+
className: "my-lg"
|
|
60
|
+
}),
|
|
61
|
+
/*#__PURE__*/ jsxs(VStack, {
|
|
62
|
+
gap: "md",
|
|
63
|
+
children: [
|
|
64
|
+
/*#__PURE__*/ jsx(Text, {
|
|
65
|
+
body1Bold: true,
|
|
66
|
+
children: i18n.theme.font.title
|
|
67
|
+
}),
|
|
68
|
+
/*#__PURE__*/ jsx(UXInput, {
|
|
69
|
+
isClearable: true,
|
|
70
|
+
startContent: 'loading' === fontLoadStatus && /*#__PURE__*/ jsx(UXSpinner, {
|
|
71
|
+
size: 18
|
|
72
|
+
}),
|
|
73
|
+
isInvalid: 'error' === fontLoadStatus,
|
|
74
|
+
className: "w-full",
|
|
75
|
+
placeholder: i18n.theme.font.placeholder,
|
|
76
|
+
value: selectedFontLink,
|
|
77
|
+
description: /*#__PURE__*/ jsxs(Fragment, {
|
|
78
|
+
children: [
|
|
79
|
+
'success' === fontLoadStatus && /*#__PURE__*/ jsxs(Text, {
|
|
80
|
+
body3Bold: true,
|
|
81
|
+
color: "success",
|
|
82
|
+
children: [
|
|
83
|
+
i18n.theme.font.success,
|
|
84
|
+
fontName
|
|
85
|
+
]
|
|
86
|
+
}),
|
|
87
|
+
'error' === fontLoadStatus && fontLoadError && /*#__PURE__*/ jsx(Text, {
|
|
88
|
+
body3Bold: true,
|
|
89
|
+
color: "danger",
|
|
90
|
+
children: fontLoadError
|
|
91
|
+
})
|
|
92
|
+
]
|
|
93
|
+
}),
|
|
94
|
+
onValueChange: setSelectedFontLink
|
|
95
|
+
})
|
|
96
|
+
]
|
|
97
|
+
})
|
|
98
|
+
]
|
|
99
|
+
}),
|
|
100
|
+
/*#__PURE__*/ jsx(DrawerFooter, {
|
|
101
|
+
className: "mb-8",
|
|
102
|
+
children: /*#__PURE__*/ jsxs(HStack, {
|
|
103
|
+
fullWidth: true,
|
|
104
|
+
gap: "lg",
|
|
105
|
+
children: [
|
|
106
|
+
/*#__PURE__*/ jsx(UXButton, {
|
|
107
|
+
fullWidth: true,
|
|
108
|
+
size: "lg",
|
|
109
|
+
onPress: handleReset,
|
|
110
|
+
children: i18n.theme.reset
|
|
111
|
+
}),
|
|
112
|
+
/*#__PURE__*/ jsx(UXButton, {
|
|
113
|
+
fullWidth: true,
|
|
114
|
+
isDisabled: 'error' === fontLoadStatus,
|
|
115
|
+
size: "lg",
|
|
116
|
+
color: "primary",
|
|
117
|
+
onPress: handleApplyTheme,
|
|
118
|
+
children: i18n.theme.apply
|
|
119
|
+
})
|
|
120
|
+
]
|
|
121
|
+
})
|
|
122
|
+
})
|
|
123
|
+
]
|
|
124
|
+
})
|
|
125
|
+
});
|
|
126
|
+
};
|
|
127
|
+
const UXThemeSwitch = ({ children })=>{
|
|
128
|
+
const { isOpen, onOpen, onClose, onOpenChange } = useDisclosure();
|
|
35
129
|
const renderChildren = ()=>{
|
|
36
130
|
if (children) return children(onOpen);
|
|
37
131
|
return /*#__PURE__*/ jsx(UXButton, {
|
|
@@ -44,78 +138,12 @@ const UXThemeSwitch = ({ children })=>{
|
|
|
44
138
|
return /*#__PURE__*/ jsxs(Fragment, {
|
|
45
139
|
children: [
|
|
46
140
|
renderChildren(),
|
|
47
|
-
/*#__PURE__*/ jsx(
|
|
141
|
+
/*#__PURE__*/ jsx(UXThemeSwitchDrawer, {
|
|
48
142
|
isOpen: isOpen,
|
|
49
|
-
|
|
50
|
-
onOpenChange:
|
|
51
|
-
children: /*#__PURE__*/ jsxs(DrawerContent, {
|
|
52
|
-
children: [
|
|
53
|
-
/*#__PURE__*/ jsx(DrawerHeader, {
|
|
54
|
-
className: "flex flex-col gap-1",
|
|
55
|
-
children: i18n.theme.title
|
|
56
|
-
}),
|
|
57
|
-
/*#__PURE__*/ jsxs(DrawerBody, {
|
|
58
|
-
children: [
|
|
59
|
-
/*#__PURE__*/ jsx(Flex, {
|
|
60
|
-
wrap: true,
|
|
61
|
-
justify: "between",
|
|
62
|
-
className: "gap-y-5",
|
|
63
|
-
children: themeData.map((theme)=>/*#__PURE__*/ jsx(ThemeItem, {
|
|
64
|
-
isSelected: selectedTheme.key === theme.key,
|
|
65
|
-
zhName: theme.zhName,
|
|
66
|
-
enName: theme.enName,
|
|
67
|
-
colorVariables: theme.colorVariables,
|
|
68
|
-
onClick: ()=>setSelectedTheme(theme)
|
|
69
|
-
}, theme.key))
|
|
70
|
-
}),
|
|
71
|
-
/*#__PURE__*/ jsx(UXDivider, {
|
|
72
|
-
className: "my-lg"
|
|
73
|
-
}),
|
|
74
|
-
/*#__PURE__*/ jsxs(VStack, {
|
|
75
|
-
gap: "md",
|
|
76
|
-
className: "w-full",
|
|
77
|
-
children: [
|
|
78
|
-
/*#__PURE__*/ jsx(Text, {
|
|
79
|
-
className: "text-sm font-medium",
|
|
80
|
-
children: i18n.theme.font.title
|
|
81
|
-
}),
|
|
82
|
-
/*#__PURE__*/ jsx(UXInput, {
|
|
83
|
-
isClearable: true,
|
|
84
|
-
className: "w-full",
|
|
85
|
-
placeholder: i18n.theme.font.placeholder,
|
|
86
|
-
value: selectedFontLink,
|
|
87
|
-
onValueChange: setSelectedFontLink
|
|
88
|
-
})
|
|
89
|
-
]
|
|
90
|
-
})
|
|
91
|
-
]
|
|
92
|
-
}),
|
|
93
|
-
/*#__PURE__*/ jsx(DrawerFooter, {
|
|
94
|
-
className: "mb-8",
|
|
95
|
-
children: /*#__PURE__*/ jsxs(HStack, {
|
|
96
|
-
fullWidth: true,
|
|
97
|
-
gap: "lg",
|
|
98
|
-
children: [
|
|
99
|
-
/*#__PURE__*/ jsx(UXButton, {
|
|
100
|
-
fullWidth: true,
|
|
101
|
-
size: "lg",
|
|
102
|
-
onPress: handleReset,
|
|
103
|
-
children: i18n.theme.reset
|
|
104
|
-
}),
|
|
105
|
-
/*#__PURE__*/ jsx(UXButton, {
|
|
106
|
-
fullWidth: true,
|
|
107
|
-
size: "lg",
|
|
108
|
-
color: "primary",
|
|
109
|
-
onPress: handleApplyTheme,
|
|
110
|
-
children: i18n.theme.apply
|
|
111
|
-
})
|
|
112
|
-
]
|
|
113
|
-
})
|
|
114
|
-
})
|
|
115
|
-
]
|
|
116
|
-
})
|
|
143
|
+
onClose: onClose,
|
|
144
|
+
onOpenChange: onOpenChange
|
|
117
145
|
})
|
|
118
146
|
]
|
|
119
147
|
});
|
|
120
148
|
};
|
|
121
|
-
export { UXThemeSwitch };
|
|
149
|
+
export { UXThemeSwitch, UXThemeSwitchDrawer };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { useThemeStore } from "./use-theme-store.js";
|
|
2
|
+
const useColorScheme = ()=>{
|
|
3
|
+
const { selectedTheme } = useThemeStore();
|
|
4
|
+
const { colorScheme } = selectedTheme;
|
|
5
|
+
return {
|
|
6
|
+
colorScheme,
|
|
7
|
+
isDark: 'dark' === colorScheme,
|
|
8
|
+
isLight: 'light' === colorScheme
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
export { useColorScheme };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare const useThemeColor: () => {
|
|
2
|
+
foreground: string;
|
|
3
|
+
secondary: string;
|
|
4
|
+
tertiary: string;
|
|
5
|
+
primary: string;
|
|
6
|
+
success: string;
|
|
7
|
+
danger: string;
|
|
8
|
+
alert: string;
|
|
9
|
+
warning: string;
|
|
10
|
+
gold: string;
|
|
11
|
+
background: string;
|
|
12
|
+
'background-200': string;
|
|
13
|
+
'background-300': string;
|
|
14
|
+
'background-400': string;
|
|
15
|
+
overlay: string;
|
|
16
|
+
divider: string;
|
|
17
|
+
bullish: string;
|
|
18
|
+
bearish: string;
|
|
19
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ThemeItemType } from './theme-data';
|
|
2
|
+
export type FontLoadStatus = 'idle' | 'loading' | 'success' | 'error';
|
|
2
3
|
interface State {
|
|
3
4
|
/**
|
|
4
5
|
* 临时选中的 theme(用于预览)
|
|
@@ -16,12 +17,27 @@ interface State {
|
|
|
16
17
|
* 保存的字体链接
|
|
17
18
|
*/
|
|
18
19
|
savedFontLink: string;
|
|
20
|
+
/**
|
|
21
|
+
* 应用的字体名称
|
|
22
|
+
*/
|
|
23
|
+
fontName: string;
|
|
24
|
+
/**
|
|
25
|
+
* 字体加载状态
|
|
26
|
+
*/
|
|
27
|
+
fontLoadStatus: FontLoadStatus;
|
|
28
|
+
/**
|
|
29
|
+
* 字体加载错误信息
|
|
30
|
+
*/
|
|
31
|
+
fontLoadError: string | null;
|
|
19
32
|
}
|
|
20
33
|
interface Actions {
|
|
21
34
|
setSelectedTheme: (theme: ThemeItemType) => void;
|
|
22
35
|
setSavedTheme: (theme: ThemeItemType) => void;
|
|
23
36
|
setSelectedFontLink: (link: string) => void;
|
|
24
37
|
setSavedFontLink: (link: string) => void;
|
|
38
|
+
setFontLoadStatus: (status: FontLoadStatus) => void;
|
|
39
|
+
setFontLoadError: (error: string | null) => void;
|
|
40
|
+
setFontName: (name: string) => void;
|
|
25
41
|
}
|
|
26
42
|
type ThemeStore = State & Actions;
|
|
27
43
|
export declare const useThemeStore: import("zustand").UseBoundStore<Omit<import("zustand").StoreApi<ThemeStore>, "setState" | "persist"> & {
|
|
@@ -6,6 +6,9 @@ const useThemeStore = create()(persist((set)=>({
|
|
|
6
6
|
savedTheme: themeData["0"],
|
|
7
7
|
selectedFontLink: '',
|
|
8
8
|
savedFontLink: '',
|
|
9
|
+
fontName: '',
|
|
10
|
+
fontLoadStatus: 'idle',
|
|
11
|
+
fontLoadError: null,
|
|
9
12
|
setSelectedTheme: (theme)=>set({
|
|
10
13
|
selectedTheme: theme
|
|
11
14
|
}),
|
|
@@ -17,9 +20,25 @@ const useThemeStore = create()(persist((set)=>({
|
|
|
17
20
|
}),
|
|
18
21
|
setSavedFontLink: (link)=>set({
|
|
19
22
|
savedFontLink: link
|
|
23
|
+
}),
|
|
24
|
+
setFontLoadStatus: (status)=>set({
|
|
25
|
+
fontLoadStatus: status
|
|
26
|
+
}),
|
|
27
|
+
setFontLoadError: (error)=>set({
|
|
28
|
+
fontLoadError: error
|
|
29
|
+
}),
|
|
30
|
+
setFontName: (name)=>set({
|
|
31
|
+
fontName: name
|
|
20
32
|
})
|
|
21
33
|
}), {
|
|
22
34
|
name: 'ux-theme',
|
|
23
|
-
storage: createJSONStorage(()=>'undefined' != typeof window ? window.localStorage : {})
|
|
35
|
+
storage: createJSONStorage(()=>'undefined' != typeof window ? window.localStorage : {}),
|
|
36
|
+
partialize: (state)=>({
|
|
37
|
+
selectedTheme: state.selectedTheme,
|
|
38
|
+
savedTheme: state.savedTheme,
|
|
39
|
+
fontName: state.fontName,
|
|
40
|
+
selectedFontLink: state.selectedFontLink,
|
|
41
|
+
savedFontLink: state.savedFontLink
|
|
42
|
+
})
|
|
24
43
|
}));
|
|
25
44
|
export { useThemeStore };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ThemeItemType } from './theme-data';
|
|
2
|
+
import { type FontLoadStatus } from './use-theme-store';
|
|
2
3
|
/**
|
|
3
4
|
* UX 主题管理 Hook
|
|
4
5
|
*/
|
|
@@ -11,4 +12,7 @@ export declare const useTheme: () => {
|
|
|
11
12
|
setSelectedFontLink: (link: string) => void;
|
|
12
13
|
savedFontLink: string;
|
|
13
14
|
setSavedFontLink: (link: string) => void;
|
|
15
|
+
fontLoadStatus: FontLoadStatus;
|
|
16
|
+
fontLoadError: string | null;
|
|
17
|
+
fontName: string;
|
|
14
18
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useEffect } from "react";
|
|
2
|
+
import { useDebounceFn } from "ahooks";
|
|
2
3
|
import { themeKeys } from "./theme-data.js";
|
|
3
4
|
import { useThemeStore } from "./use-theme-store.js";
|
|
4
5
|
const DEFAULT_FONT_FAMILY = 'ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji';
|
|
@@ -10,7 +11,7 @@ const applyTheme = (theme)=>{
|
|
|
10
11
|
themeKeys.forEach((key)=>{
|
|
11
12
|
root.classList.remove(key);
|
|
12
13
|
});
|
|
13
|
-
root.classList.add(theme.
|
|
14
|
+
root.classList.add(theme.id);
|
|
14
15
|
};
|
|
15
16
|
const extractFontFamilyFromLink = (link)=>{
|
|
16
17
|
if (!link) return null;
|
|
@@ -24,38 +25,70 @@ const extractFontFamilyFromLink = (link)=>{
|
|
|
24
25
|
return null;
|
|
25
26
|
}
|
|
26
27
|
};
|
|
27
|
-
const loadGoogleFont = (link)=>{
|
|
28
|
+
const loadGoogleFont = (link, options)=>{
|
|
29
|
+
const { setFontLoadStatus, setFontLoadError, setFontName } = options;
|
|
28
30
|
if ('undefined' == typeof window) return;
|
|
29
31
|
const existingLink = document.getElementById('ux-google-font-link');
|
|
30
32
|
if (existingLink) existingLink.remove();
|
|
31
33
|
if (!link?.trim()) {
|
|
32
34
|
document.documentElement.style.setProperty('--ux-font-family', DEFAULT_FONT_FAMILY);
|
|
33
35
|
document.body.style.fontFamily = DEFAULT_FONT_FAMILY;
|
|
36
|
+
setFontLoadStatus('idle');
|
|
37
|
+
setFontLoadError(null);
|
|
38
|
+
setFontName('');
|
|
34
39
|
return;
|
|
35
40
|
}
|
|
41
|
+
setFontLoadStatus('loading');
|
|
42
|
+
setFontLoadError(null);
|
|
36
43
|
const linkElement = document.createElement('link');
|
|
37
44
|
linkElement.id = 'ux-google-font-link';
|
|
38
45
|
linkElement.rel = 'stylesheet';
|
|
39
46
|
linkElement.href = link;
|
|
47
|
+
linkElement.onload = ()=>{
|
|
48
|
+
setFontLoadStatus('success');
|
|
49
|
+
setFontLoadError(null);
|
|
50
|
+
const fontFamily = extractFontFamilyFromLink(link);
|
|
51
|
+
if (fontFamily) {
|
|
52
|
+
document.documentElement.style.setProperty('--ux-font-family', `"${fontFamily}", ${DEFAULT_FONT_FAMILY}`);
|
|
53
|
+
document.body.style.fontFamily = `"${fontFamily}", ${DEFAULT_FONT_FAMILY}`;
|
|
54
|
+
setFontName(fontFamily);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
linkElement.onerror = ()=>{
|
|
58
|
+
const errorMessage = '字体加载失败,请检查链接是否正确';
|
|
59
|
+
setFontLoadStatus('error');
|
|
60
|
+
setFontLoadError(errorMessage);
|
|
61
|
+
setFontName('');
|
|
62
|
+
document.documentElement.style.setProperty('--ux-font-family', DEFAULT_FONT_FAMILY);
|
|
63
|
+
document.body.style.fontFamily = DEFAULT_FONT_FAMILY;
|
|
64
|
+
};
|
|
40
65
|
document.head.appendChild(linkElement);
|
|
41
|
-
const fontFamily = extractFontFamilyFromLink(link);
|
|
42
|
-
if (fontFamily) {
|
|
43
|
-
document.documentElement.style.setProperty('--ux-font-family', `"${fontFamily}", ${DEFAULT_FONT_FAMILY}`);
|
|
44
|
-
document.body.style.fontFamily = `"${fontFamily}", ${DEFAULT_FONT_FAMILY}`;
|
|
45
|
-
}
|
|
46
66
|
};
|
|
47
67
|
const useTheme = ()=>{
|
|
48
68
|
const selectedTheme = useThemeStore((state)=>state.selectedTheme);
|
|
49
69
|
const savedTheme = useThemeStore((state)=>state.savedTheme);
|
|
50
70
|
const selectedFontLink = useThemeStore((state)=>state.selectedFontLink);
|
|
51
71
|
const savedFontLink = useThemeStore((state)=>state.savedFontLink);
|
|
72
|
+
const fontName = useThemeStore((state)=>state.fontName);
|
|
73
|
+
const fontLoadStatus = useThemeStore((state)=>state.fontLoadStatus);
|
|
74
|
+
const fontLoadError = useThemeStore((state)=>state.fontLoadError);
|
|
52
75
|
const storeSetSelectedTheme = useThemeStore((state)=>state.setSelectedTheme);
|
|
53
76
|
const storeSetSavedTheme = useThemeStore((state)=>state.setSavedTheme);
|
|
54
77
|
const storeSetSelectedFontLink = useThemeStore((state)=>state.setSelectedFontLink);
|
|
55
78
|
const storeSetSavedFontLink = useThemeStore((state)=>state.setSavedFontLink);
|
|
79
|
+
const storeSetFontLoadStatus = useThemeStore((state)=>state.setFontLoadStatus);
|
|
80
|
+
const storeSetFontLoadError = useThemeStore((state)=>state.setFontLoadError);
|
|
81
|
+
const storeSetFontName = useThemeStore((state)=>state.setFontName);
|
|
82
|
+
const { run: debouncedLoadGoogleFont } = useDebounceFn((link)=>loadGoogleFont(link, {
|
|
83
|
+
setFontLoadStatus: storeSetFontLoadStatus,
|
|
84
|
+
setFontLoadError: storeSetFontLoadError,
|
|
85
|
+
setFontName: storeSetFontName
|
|
86
|
+
}), {
|
|
87
|
+
wait: 500
|
|
88
|
+
});
|
|
56
89
|
useEffect(()=>{
|
|
57
90
|
applyTheme(savedTheme);
|
|
58
|
-
|
|
91
|
+
debouncedLoadGoogleFont(savedFontLink);
|
|
59
92
|
}, [
|
|
60
93
|
savedTheme,
|
|
61
94
|
savedFontLink
|
|
@@ -70,11 +103,11 @@ const useTheme = ()=>{
|
|
|
70
103
|
};
|
|
71
104
|
const setSelectedFontLink = (link)=>{
|
|
72
105
|
storeSetSelectedFontLink(link);
|
|
73
|
-
|
|
106
|
+
debouncedLoadGoogleFont(link);
|
|
74
107
|
};
|
|
75
108
|
const setSavedFontLink = (link)=>{
|
|
76
109
|
storeSetSavedFontLink(link);
|
|
77
|
-
|
|
110
|
+
debouncedLoadGoogleFont(link);
|
|
78
111
|
};
|
|
79
112
|
return {
|
|
80
113
|
selectedTheme,
|
|
@@ -84,7 +117,10 @@ const useTheme = ()=>{
|
|
|
84
117
|
selectedFontLink,
|
|
85
118
|
setSelectedFontLink,
|
|
86
119
|
savedFontLink,
|
|
87
|
-
setSavedFontLink
|
|
120
|
+
setSavedFontLink,
|
|
121
|
+
fontLoadStatus,
|
|
122
|
+
fontLoadError,
|
|
123
|
+
fontName
|
|
88
124
|
};
|
|
89
125
|
};
|
|
90
126
|
export { useTheme };
|
package/dist/hooks/useI18n.d.ts
CHANGED
package/dist/hooks/useI18n.js
CHANGED
|
@@ -15,7 +15,10 @@ const en = {
|
|
|
15
15
|
apply: 'Apply Theme',
|
|
16
16
|
font: {
|
|
17
17
|
title: 'Custom Font',
|
|
18
|
-
placeholder: 'Enter Google Fonts
|
|
18
|
+
placeholder: 'Enter Google Fonts URL or custom font URL',
|
|
19
|
+
loading: 'Loading font...',
|
|
20
|
+
success: 'Font loaded: ',
|
|
21
|
+
error: 'Failed to load font'
|
|
19
22
|
}
|
|
20
23
|
}
|
|
21
24
|
};
|
|
@@ -35,7 +38,10 @@ const zh = {
|
|
|
35
38
|
apply: '应用主题',
|
|
36
39
|
font: {
|
|
37
40
|
title: '自定义字体',
|
|
38
|
-
placeholder: '输入 Google Fonts
|
|
41
|
+
placeholder: '输入 Google Fonts 或自定义字体链接',
|
|
42
|
+
loading: '正在加载字体...',
|
|
43
|
+
success: '字体加载成功:',
|
|
44
|
+
error: '字体加载失败'
|
|
39
45
|
}
|
|
40
46
|
}
|
|
41
47
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@particle-network/ui-react",
|
|
3
|
-
"version": "0.4.0-beta.
|
|
3
|
+
"version": "0.4.0-beta.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -50,8 +50,8 @@
|
|
|
50
50
|
"ahooks": "^3.9.4",
|
|
51
51
|
"copy-to-clipboard": "^3.3.3",
|
|
52
52
|
"zustand": "^5.0.8",
|
|
53
|
-
"@particle-network/icons": "0.
|
|
54
|
-
"@particle-network/ui-shared": "0.
|
|
53
|
+
"@particle-network/icons": "0.4.0-beta.0",
|
|
54
|
+
"@particle-network/ui-shared": "0.3.0-beta.0"
|
|
55
55
|
},
|
|
56
56
|
"scripts": {
|
|
57
57
|
"build": "rslib build",
|