@particle-network/ui-react 0.5.1-beta.1 → 0.5.1-beta.11

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 (89) hide show
  1. package/dist/components/ProgressWrapper/index.d.ts +2 -2
  2. package/dist/components/ProgressWrapper/index.js +1 -1
  3. package/dist/components/UXAutocomplete/index.js +1 -2
  4. package/dist/components/UXButton/button-theme.js +38 -2
  5. package/dist/components/UXButton/button.js +1 -1
  6. package/dist/components/UXCheckbox/checkbox.extend.js +16 -13
  7. package/dist/components/UXChip/chip.extend.d.ts +2 -1
  8. package/dist/components/UXChip/index.d.ts +1 -1
  9. package/dist/components/UXColorPicker/color-fields.d.ts +3 -0
  10. package/dist/components/UXColorPicker/color-fields.js +142 -0
  11. package/dist/components/UXColorPicker/color-input.d.ts +7 -0
  12. package/dist/components/UXColorPicker/color-input.js +12 -0
  13. package/dist/components/UXColorPicker/color-picker.d.ts +3 -0
  14. package/dist/components/UXColorPicker/color-picker.js +228 -0
  15. package/dist/components/UXColorPicker/index.d.ts +5 -0
  16. package/dist/components/UXColorPicker/index.js +3 -0
  17. package/dist/components/UXColorPicker/types.d.ts +51 -0
  18. package/dist/components/UXColorPicker/types.js +0 -0
  19. package/dist/components/UXColorPicker/utils.d.ts +7 -0
  20. package/dist/components/UXColorPicker/utils.js +6 -0
  21. package/dist/components/UXCopy/index.js +2 -2
  22. package/dist/components/UXEmpty/index.js +2 -2
  23. package/dist/components/UXHint/index.js +1 -1
  24. package/dist/components/UXInput/index.d.ts +6 -6
  25. package/dist/components/UXInput/input.extend.d.ts +6 -6
  26. package/dist/components/UXRadio/radio.extend.js +3 -3
  27. package/dist/components/UXSlider/use-slider.d.ts +1 -1
  28. package/dist/components/UXSlider/use-slider.js +1 -1
  29. package/dist/components/UXSpinner/spinner.d.ts +1 -5
  30. package/dist/components/UXSpinner/spinner.js +3 -4
  31. package/dist/components/UXSwitch/index.d.ts +2 -2
  32. package/dist/components/UXSwitch/switch.extend.d.ts +2 -2
  33. package/dist/components/UXSwitch/switch.extend.js +6 -6
  34. package/dist/components/UXTabs/tabs.classes.js +4 -4
  35. package/dist/components/UXThemeSwitch/constants.d.ts +9 -0
  36. package/dist/components/UXThemeSwitch/constants.js +3 -0
  37. package/dist/components/UXThemeSwitch/custom-theme-config.d.ts +2 -0
  38. package/dist/components/UXThemeSwitch/custom-theme-config.js +171 -0
  39. package/dist/components/UXThemeSwitch/theme-item.js +94 -15
  40. package/dist/components/UXThemeSwitch/theme-switch.js +26 -5
  41. package/dist/components/UXThemeSwitch/use-color-scheme.js +11 -8
  42. package/dist/components/UXThemeSwitch/use-theme-color.d.ts +1 -22
  43. package/dist/components/UXThemeSwitch/use-theme-color.js +3 -7
  44. package/dist/components/UXThemeSwitch/use-theme-store.d.ts +5 -0
  45. package/dist/components/UXThemeSwitch/use-theme-store.js +9 -3
  46. package/dist/components/UXThemeSwitch/use-theme.d.ts +2 -0
  47. package/dist/components/UXThemeSwitch/use-theme.js +10 -53
  48. package/dist/components/UXThemeSwitch/utils.d.ts +28 -0
  49. package/dist/components/UXThemeSwitch/utils.js +222 -0
  50. package/dist/components/UXToast/index.js +3 -3
  51. package/dist/components/index.d.ts +1 -0
  52. package/dist/components/index.js +1 -0
  53. package/dist/components/layout/Box/box-theme.d.ts +2213 -0
  54. package/dist/components/layout/Box/box-theme.js +344 -0
  55. package/dist/components/layout/Box/box.d.ts +14 -0
  56. package/dist/components/layout/Box/box.js +99 -0
  57. package/dist/components/layout/Circle.js +2 -3
  58. package/dist/components/layout/Flex.d.ts +3 -27
  59. package/dist/components/layout/Flex.js +6 -19
  60. package/dist/components/layout/HStack.d.ts +1 -1
  61. package/dist/components/layout/Square.js +3 -3
  62. package/dist/components/layout/VStack.d.ts +1 -1
  63. package/dist/components/layout/VStack.js +2 -2
  64. package/dist/components/layout/index.d.ts +1 -0
  65. package/dist/components/layout/index.js +1 -0
  66. package/dist/components/typography/Text.js +22 -7
  67. package/dist/components/typography/Text.type.d.ts +3 -26
  68. package/dist/components/typography/Text.type.js +0 -47
  69. package/dist/components/typography/text-theme.d.ts +178 -0
  70. package/dist/components/typography/text-theme.js +79 -0
  71. package/dist/heroui/constants.d.ts +18 -0
  72. package/dist/heroui/constants.js +98 -0
  73. package/dist/heroui/types.d.ts +91 -0
  74. package/dist/heroui/types.js +0 -0
  75. package/dist/heroui/utils/colors.d.ts +34 -0
  76. package/dist/heroui/utils/colors.js +121 -0
  77. package/dist/heroui/utils/object.d.ts +1 -0
  78. package/dist/heroui/utils/object.js +17 -0
  79. package/dist/hooks/useI18n.d.ts +133 -25
  80. package/dist/hooks/useI18n.js +84 -2
  81. package/dist/hooks/useLang.d.ts +5 -1
  82. package/dist/hooks/useLang.js +13 -1
  83. package/dist/utils/cn.d.ts +2 -0
  84. package/dist/utils/cn.js +258 -0
  85. package/dist/utils/index.d.ts +1 -0
  86. package/dist/utils/index.js +2 -1
  87. package/dist/utils/input-classes.js +2 -2
  88. package/package.json +6 -3
  89. package/tailwind-preset.js +84 -160
@@ -1,11 +1,14 @@
1
- import { jsx, jsxs } from "react/jsx-runtime";
1
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
2
  import "react";
3
- import { cn } from "@heroui/theme";
4
3
  import { useLang } from "../../hooks/index.js";
4
+ import { cn } from "../../utils/index.js";
5
5
  import { VStack } from "../layout/index.js";
6
6
  import { Text } from "../typography/Text.js";
7
+ import { useTheme } from "./use-theme.js";
7
8
  const ThemeItem = ({ id, zhName, enName, isSelected, onClick })=>{
8
- const lang = useLang();
9
+ const { lang } = useLang();
10
+ const { customTheme } = useTheme();
11
+ const customColors = customTheme.colorVariables;
9
12
  return /*#__PURE__*/ jsxs(VStack, {
10
13
  gap: 2,
11
14
  items: "center",
@@ -13,7 +16,10 @@ const ThemeItem = ({ id, zhName, enName, isSelected, onClick })=>{
13
16
  onClick: onClick,
14
17
  children: [
15
18
  /*#__PURE__*/ jsx("div", {
16
- className: cn('rounded-medium border-2 hover:scale-105 transition-all duration-300', 'w-[100px] h-[59px] md:w-[120px] md:h-[71px]', isSelected ? 'border-primary' : 'border-transparent'),
19
+ className: cn('rounded-medium border-2 border-transparent transition-all duration-300 hover:scale-105', 'h-[59px] w-[100px] md:h-[71px] md:w-[120px]', isSelected && 'border-primary'),
20
+ style: {
21
+ borderColor: isSelected && 'custom' === id ? customColors.primary : void 0
22
+ },
17
23
  children: /*#__PURE__*/ jsxs("svg", {
18
24
  xmlns: "http://www.w3.org/2000/svg",
19
25
  viewBox: "0 0 120 71",
@@ -27,7 +33,10 @@ const ThemeItem = ({ id, zhName, enName, isSelected, onClick })=>{
27
33
  height: "67",
28
34
  rx: "5",
29
35
  fill: "currentColor",
30
- className: "text-primary"
36
+ className: "text-primary",
37
+ style: {
38
+ color: 'custom' === id ? customColors.primary : void 0
39
+ }
31
40
  }),
32
41
  /*#__PURE__*/ jsx("mask", {
33
42
  id: "mask0_40928_218196",
@@ -46,7 +55,10 @@ const ThemeItem = ({ id, zhName, enName, isSelected, onClick })=>{
46
55
  height: "67",
47
56
  rx: "5",
48
57
  fill: "currentColor",
49
- className: "text-background"
58
+ className: "text-background",
59
+ style: {
60
+ color: 'custom' === id ? customColors.bgDefault : void 0
61
+ }
50
62
  })
51
63
  }),
52
64
  /*#__PURE__*/ jsx("g", {
@@ -58,7 +70,10 @@ const ThemeItem = ({ id, zhName, enName, isSelected, onClick })=>{
58
70
  height: "67",
59
71
  rx: "6",
60
72
  fill: "currentColor",
61
- className: "text-background"
73
+ className: "text-background",
74
+ style: {
75
+ color: 'custom' === id ? customColors.bgDefault : void 0
76
+ }
62
77
  })
63
78
  }),
64
79
  /*#__PURE__*/ jsx("rect", {
@@ -67,7 +82,10 @@ const ThemeItem = ({ id, zhName, enName, isSelected, onClick })=>{
67
82
  width: "100",
68
83
  height: "1",
69
84
  fill: "currentColor",
70
- className: "text-divider"
85
+ className: "text-divider",
86
+ style: {
87
+ color: 'custom' === id ? customColors.divider : void 0
88
+ }
71
89
  }),
72
90
  /*#__PURE__*/ jsx("rect", {
73
91
  x: "54.082",
@@ -76,7 +94,10 @@ const ThemeItem = ({ id, zhName, enName, isSelected, onClick })=>{
76
94
  height: "6.01835",
77
95
  rx: "3.00917",
78
96
  fill: "currentColor",
79
- className: "text-foreground"
97
+ className: "text-foreground",
98
+ style: {
99
+ color: 'custom' === id ? customColors.foreground : void 0
100
+ }
80
101
  }),
81
102
  /*#__PURE__*/ jsx("rect", {
82
103
  x: "54.082",
@@ -85,7 +106,10 @@ const ThemeItem = ({ id, zhName, enName, isSelected, onClick })=>{
85
106
  height: "6.01835",
86
107
  rx: "3.00917",
87
108
  fill: "currentColor",
88
- className: "text-bullish"
109
+ className: "text-bullish",
110
+ style: {
111
+ color: 'custom' === id ? customColors.bullish : void 0
112
+ }
89
113
  }),
90
114
  /*#__PURE__*/ jsx("rect", {
91
115
  x: "75.8994",
@@ -94,7 +118,10 @@ const ThemeItem = ({ id, zhName, enName, isSelected, onClick })=>{
94
118
  height: "6.01835",
95
119
  rx: "3.00917",
96
120
  fill: "currentColor",
97
- className: "text-bearish"
121
+ className: "text-bearish",
122
+ style: {
123
+ color: 'custom' === id ? customColors.bearish : void 0
124
+ }
98
125
  }),
99
126
  /*#__PURE__*/ jsx("rect", {
100
127
  x: "86.4316",
@@ -103,7 +130,10 @@ const ThemeItem = ({ id, zhName, enName, isSelected, onClick })=>{
103
130
  height: "6.01835",
104
131
  rx: "3.00917",
105
132
  fill: "currentColor",
106
- className: "text-secondary"
133
+ className: "text-secondary",
134
+ style: {
135
+ color: 'custom' === id ? customColors.secondary : void 0
136
+ }
107
137
  }),
108
138
  /*#__PURE__*/ jsx("rect", {
109
139
  x: "94.707",
@@ -112,7 +142,10 @@ const ThemeItem = ({ id, zhName, enName, isSelected, onClick })=>{
112
142
  height: "6.01835",
113
143
  rx: "3.00917",
114
144
  fill: "currentColor",
115
- className: "text-secondary"
145
+ className: "text-secondary",
146
+ style: {
147
+ color: 'custom' === id ? customColors.secondary : void 0
148
+ }
116
149
  }),
117
150
  /*#__PURE__*/ jsx("rect", {
118
151
  x: "102.981",
@@ -121,9 +154,55 @@ const ThemeItem = ({ id, zhName, enName, isSelected, onClick })=>{
121
154
  height: "6.01835",
122
155
  rx: "3.00917",
123
156
  fill: "currentColor",
124
- className: "text-secondary"
157
+ className: "text-secondary",
158
+ style: {
159
+ color: 'custom' === id ? customColors.secondary : void 0
160
+ }
125
161
  }),
126
- /*#__PURE__*/ jsx("rect", {
162
+ 'custom' === id ? /*#__PURE__*/ jsxs(Fragment, {
163
+ children: [
164
+ /*#__PURE__*/ jsx("g", {
165
+ "clip-path": "url(#paint0_angular_48986_280686_clip_path)",
166
+ "data-figma-skip-parse": "true",
167
+ children: /*#__PURE__*/ jsx("g", {
168
+ transform: "matrix(0 0.011 -0.011 0 39 51)",
169
+ children: /*#__PURE__*/ jsx("foreignObject", {
170
+ x: "-1043.94",
171
+ y: "-1043.94",
172
+ width: "2087.87",
173
+ height: "2087.87",
174
+ children: /*#__PURE__*/ jsx("div", {
175
+ style: {
176
+ background: 'conic-gradient(from 90deg,rgba(255, 0, 0, 1) 0deg,rgba(255, 133, 0, 1) 54deg,rgba(190, 255, 0, 1) 117deg,rgba(7, 225, 255, 1) 199.8deg,rgba(35, 0, 255, 1) 282.6deg,rgba(173, 0, 255, 1) 360deg)',
177
+ height: '100%',
178
+ width: '100%',
179
+ opacity: 1
180
+ }
181
+ })
182
+ })
183
+ })
184
+ }),
185
+ /*#__PURE__*/ jsx("circle", {
186
+ cx: "39",
187
+ cy: "51",
188
+ r: "10.7583",
189
+ "data-figma-gradient-fill": '{"type":"GRADIENT_ANGULAR","stops":[{"color":{"r":1.0,"g":0.0,"b":0.0,"a":1.0},"position":0.0},{"color":{"r":1.0,"g":0.52295231819152832,"b":0.0,"a":1.0},"position":0.15000000596046448},{"color":{"r":0.74555355310440063,"g":1.0,"b":0.0,"a":1.0},"position":0.32499998807907104},{"color":{"r":0.029410807415843010,"g":0.88352936506271362,"b":1.0,"a":1.0},"position":0.55500000715255737},{"color":{"r":0.14000000059604645,"g":0.0,"b":1.0,"a":1.0},"position":0.78500002622604370},{"color":{"r":0.67999994754791260,"g":0.0,"b":1.0,"a":1.0},"position":1.0}],"stopsVar":[{"color":{"r":1.0,"g":0.0,"b":0.0,"a":1.0},"position":0.0},{"color":{"r":1.0,"g":0.52295231819152832,"b":0.0,"a":1.0},"position":0.15000000596046448},{"color":{"r":0.74555355310440063,"g":1.0,"b":0.0,"a":1.0},"position":0.32499998807907104},{"color":{"r":0.029410807415843010,"g":0.88352936506271362,"b":1.0,"a":1.0},"position":0.55500000715255737},{"color":{"r":0.14000000059604645,"g":0.0,"b":1.0,"a":1.0},"position":0.78500002622604370},{"color":{"r":0.67999994754791260,"g":0.0,"b":1.0,"a":1.0},"position":1.0}],"transform":{"m00":1.3471115643134642e-15,"m01":-22.0,"m02":50.0,"m10":22.0,"m11":1.3471115643134642e-15,"m12":40.0},"opacity":1.0,"blendMode":"NORMAL","visible":true}',
190
+ stroke: "#8B8EA1",
191
+ "stroke-width": "0.483303"
192
+ }),
193
+ /*#__PURE__*/ jsx("defs", {
194
+ children: /*#__PURE__*/ jsx("clipPath", {
195
+ id: "paint0_angular_48986_280686_clip_path",
196
+ children: /*#__PURE__*/ jsx("circle", {
197
+ cx: "39",
198
+ cy: "51",
199
+ r: "10.7583",
200
+ "stroke-width": "0.483303"
201
+ })
202
+ })
203
+ })
204
+ ]
205
+ }) : /*#__PURE__*/ jsx("rect", {
127
206
  x: "27",
128
207
  y: "39",
129
208
  width: "22.5688",
@@ -1,10 +1,10 @@
1
1
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
2
  import { useMemo, useState } from "react";
3
- import { cn } from "@heroui/theme";
4
3
  import { useDisclosure } from "@heroui/use-disclosure";
5
4
  import { ChartColorSwitchIcon, CopyIcon } from "@particle-network/icons/web";
6
- import { themeData } from "@particle-network/ui-shared";
5
+ import { DEFAULT_THEME_ID, themeData } from "@particle-network/ui-shared";
7
6
  import { useI18n } from "../../hooks/index.js";
7
+ import { cn } from "../../utils/index.js";
8
8
  import { HStack, VStack } from "../layout/index.js";
9
9
  import { Text } from "../typography/Text.js";
10
10
  import { UXButton } from "../UXButton/index.js";
@@ -15,6 +15,7 @@ import { UXInput } from "../UXInput/index.js";
15
15
  import { UXModal } from "../UXModal/index.js";
16
16
  import { UXSpinner } from "../UXSpinner/index.js";
17
17
  import { UXTooltip } from "../UXTooltip/index.js";
18
+ import { CustomThemeConfig } from "./custom-theme-config.js";
18
19
  import { ThemeItem } from "./theme-item.js";
19
20
  import { useTheme } from "./use-theme.js";
20
21
  const FONT_EXAMPLES = [
@@ -33,12 +34,26 @@ const FONT_EXAMPLES = [
33
34
  ];
34
35
  const UXThemeSwitchModal = ({ as = 'modal', omitThemes = [], backdrop, isOpen, onClose, onOpenChange })=>{
35
36
  const i18n = useI18n();
36
- const { theme: selectedTheme, setTheme, fontUrl, setFontUrl, fontName, fontLoadStatus, clearFontUrl } = useTheme();
37
+ const { theme: selectedTheme, customTheme: savedCustomTheme, setTheme, fontUrl, setFontUrl, fontName, fontLoadStatus, clearFontUrl } = useTheme();
37
38
  const [isFontExampleOpen, setIsFontExampleOpen] = useState(false);
38
39
  const Component = 'modal' === as ? UXModal : UXDrawer;
39
40
  const themes = useMemo(()=>themeData.filter((theme)=>!omitThemes.includes(theme.id)), [
40
41
  omitThemes
41
42
  ]);
43
+ const handleThemeSelect = (theme)=>{
44
+ if ('custom' === theme.id) if (savedCustomTheme) setTheme(savedCustomTheme);
45
+ else {
46
+ const baseTheme = themeData.find((t)=>t.id === DEFAULT_THEME_ID) || themeData["0"];
47
+ const customTheme = {
48
+ ...theme,
49
+ baseThemeId: DEFAULT_THEME_ID,
50
+ colorScheme: baseTheme.colorScheme,
51
+ colorVariables: baseTheme.colorVariables
52
+ };
53
+ setTheme(customTheme);
54
+ }
55
+ else setTheme(theme);
56
+ };
42
57
  return /*#__PURE__*/ jsx(Component, {
43
58
  isOpen: isOpen,
44
59
  classNames: {
@@ -61,10 +76,16 @@ const UXThemeSwitchModal = ({ as = 'modal', omitThemes = [], backdrop, isOpen, o
61
76
  className: "grid grid-cols-3 gap-y-5 pt-1",
62
77
  children: themes.map((theme)=>/*#__PURE__*/ jsx(ThemeItem, {
63
78
  isSelected: selectedTheme.id === theme.id,
64
- onClick: ()=>setTheme(theme),
79
+ onClick: ()=>handleThemeSelect(theme),
65
80
  ...theme
66
81
  }, theme.id))
67
82
  }),
83
+ 'custom' === selectedTheme.id && /*#__PURE__*/ jsxs(Fragment, {
84
+ children: [
85
+ /*#__PURE__*/ jsx(UXDivider, {}),
86
+ /*#__PURE__*/ jsx(CustomThemeConfig, {})
87
+ ]
88
+ }),
68
89
  /*#__PURE__*/ jsx(UXDivider, {}),
69
90
  /*#__PURE__*/ jsxs(VStack, {
70
91
  gap: "md",
@@ -137,7 +158,7 @@ const UXThemeSwitchModal = ({ as = 'modal', omitThemes = [], backdrop, isOpen, o
137
158
  placement: "bottom",
138
159
  children: /*#__PURE__*/ jsxs(Text, {
139
160
  color: "secondary",
140
- className: "flex-1 truncate cursor-pointer",
161
+ className: "flex-1 cursor-pointer truncate",
141
162
  children: [
142
163
  example.title,
143
164
  ": ",
@@ -1,11 +1,14 @@
1
- import { useTheme } from "./use-theme.js";
1
+ import { useMemo } from "react";
2
+ import { useThemeStore } from "./use-theme-store.js";
2
3
  const useColorScheme = ()=>{
3
- const { theme } = useTheme();
4
- const { colorScheme } = theme;
5
- return {
6
- colorScheme,
7
- isDark: 'dark' === colorScheme,
8
- isLight: 'light' === colorScheme
9
- };
4
+ const { colorScheme } = useThemeStore((state)=>state.theme);
5
+ const scheme = useMemo(()=>({
6
+ colorScheme,
7
+ isDark: 'dark' === colorScheme,
8
+ isLight: 'light' === colorScheme
9
+ }), [
10
+ colorScheme
11
+ ]);
12
+ return scheme;
10
13
  };
11
14
  export { useColorScheme };
@@ -1,22 +1 @@
1
- export declare const useThemeColor: () => {
2
- transparent: string;
3
- white: string;
4
- default: string;
5
- alert: string;
6
- success: string;
7
- foreground: string;
8
- secondary: string;
9
- tertiary: string;
10
- primary: string;
11
- danger: string;
12
- warning: string;
13
- gold: string;
14
- bullish: string;
15
- bearish: string;
16
- "bg-default": string;
17
- "bg-300": string;
18
- "bg-200": string;
19
- "bg-400": string;
20
- divider: string;
21
- overlay: string;
22
- };
1
+ export declare const useThemeColor: () => Record<import("@particle-network/ui-shared").ThemeColorVariable, string>;
@@ -1,10 +1,6 @@
1
- import { useTheme } from "./use-theme.js";
1
+ import { useThemeStore } from "./use-theme-store.js";
2
2
  const useThemeColor = ()=>{
3
- const { theme } = useTheme();
4
- return {
5
- ...theme.colorVariables,
6
- transparent: 'transparent',
7
- white: '#FFFFFF'
8
- };
3
+ const { colorVariables } = useThemeStore((state)=>state.theme);
4
+ return colorVariables;
9
5
  };
10
6
  export { useThemeColor };
@@ -5,6 +5,10 @@ interface State {
5
5
  * 保存的主题
6
6
  */
7
7
  theme: ThemeItemType;
8
+ /**
9
+ * 保存的自定义主题配置(当切换到其他主题时保存,切回 custom 时恢复)
10
+ */
11
+ customTheme: ThemeItemType;
8
12
  /**
9
13
  * 保存的字体链接
10
14
  */
@@ -20,6 +24,7 @@ interface State {
20
24
  }
21
25
  interface Actions {
22
26
  setTheme: (theme: ThemeItemType) => void;
27
+ setCustomTheme: (customTheme: ThemeItemType) => void;
23
28
  setFontUrl: (fontUrl: string) => void;
24
29
  setFontLoadStatus: (status: FontLoadStatus) => void;
25
30
  setFontName: (name: string) => void;
@@ -1,14 +1,18 @@
1
- import { themeData } from "@particle-network/ui-shared";
1
+ import { CustomTheme, UXDarkTheme } from "@particle-network/ui-shared";
2
2
  import { create } from "zustand";
3
3
  import { createJSONStorage, persist } from "zustand/middleware";
4
4
  const useThemeStore = create()(persist((set)=>({
5
- theme: themeData["0"],
5
+ theme: UXDarkTheme,
6
+ customTheme: CustomTheme,
6
7
  fontUrl: '',
7
8
  fontName: '',
8
9
  fontLoadStatus: 'idle',
9
10
  setTheme: (theme)=>set({
10
11
  theme
11
12
  }),
13
+ setCustomTheme: (customTheme)=>set({
14
+ customTheme
15
+ }),
12
16
  setFontUrl: (fontUrl)=>set({
13
17
  fontUrl
14
18
  }),
@@ -19,10 +23,12 @@ const useThemeStore = create()(persist((set)=>({
19
23
  fontName
20
24
  })
21
25
  }), {
22
- name: 'ux-theme-3',
26
+ name: 'ux-preference-theme',
27
+ version: 2,
23
28
  storage: createJSONStorage(()=>'undefined' != typeof window ? window.localStorage : {}),
24
29
  partialize: (state)=>({
25
30
  theme: state.theme,
31
+ customTheme: state.customTheme,
26
32
  fontName: state.fontName,
27
33
  fontUrl: state.fontUrl
28
34
  })
@@ -4,7 +4,9 @@ import { type ThemeItemType } from '@particle-network/ui-shared';
4
4
  */
5
5
  export declare const useTheme: () => {
6
6
  theme: ThemeItemType;
7
+ customTheme: ThemeItemType;
7
8
  setTheme: (theme: ThemeItemType) => void;
9
+ setCustomTheme: (customTheme: ThemeItemType) => void;
8
10
  fontUrl: string;
9
11
  setFontUrl: (link: string) => void;
10
12
  clearFontUrl: () => void;
@@ -1,65 +1,15 @@
1
1
  import { useEffect } from "react";
2
- import { themeKeys } from "@particle-network/ui-shared";
3
2
  import { useDebounceFn } from "ahooks";
4
3
  import { useThemeStore } from "./use-theme-store.js";
5
- const DEFAULT_FONT_FAMILY = 'Inter,system-ui,sans-serif,"Microsoft YaHei"';
6
- const PRELOAD_FONTS_URL = 'https://fonts.googleapis.com/css2?family=Geist:wght@100..900&family=IBM+Plex+Sans:ital,wght@0,100..700;1,100..700&display=swap';
7
- const preloadFonts = ()=>{
8
- if ('undefined' == typeof window) return;
9
- const existingLink = document.getElementById('ux-preload-fonts-link');
10
- if (existingLink) return;
11
- const linkElement = document.createElement('link');
12
- linkElement.id = 'ux-preload-fonts-link';
13
- linkElement.rel = 'stylesheet';
14
- linkElement.href = PRELOAD_FONTS_URL;
15
- document.head.appendChild(linkElement);
16
- };
17
- const applyTheme = (theme)=>{
18
- if ('undefined' == typeof window) return;
19
- const root = document.documentElement;
20
- const isClassListCorrect = root.classList.contains(theme.id);
21
- const isDataThemeCorrect = root.getAttribute('data-theme') === theme.colorScheme;
22
- const isDataPrefersColorCorrect = root.getAttribute('data-prefers-color') === theme.colorScheme;
23
- if (isClassListCorrect && isDataThemeCorrect && isDataPrefersColorCorrect) return;
24
- if (!isDataThemeCorrect) root.setAttribute('data-theme', theme.colorScheme);
25
- if (!isDataPrefersColorCorrect) root.setAttribute('data-prefers-color', theme.colorScheme);
26
- root.classList.remove('dark');
27
- root.classList.remove('light');
28
- if (!isClassListCorrect) {
29
- themeKeys.forEach((key)=>{
30
- root.classList.remove(key);
31
- });
32
- root.classList.add(theme.id);
33
- }
34
- };
35
- const extractFontFamilyFromLink = (link)=>{
36
- if (!link) return null;
37
- try {
38
- const url = new URL(link);
39
- const familyParam = url.searchParams.get('family');
40
- if (!familyParam) return null;
41
- const firstFamily = familyParam.split('&family=')[0].split(':')[0].replace(/\+/g, ' ');
42
- return firstFamily;
43
- } catch {
44
- return null;
45
- }
46
- };
47
- const applyFont = (fontName)=>{
48
- if ('undefined' == typeof window) return;
49
- if (fontName) {
50
- document.documentElement.style.setProperty('--ux-font-family', `"${fontName}", ${DEFAULT_FONT_FAMILY}`);
51
- document.body.style.fontFamily = `"${fontName}", ${DEFAULT_FONT_FAMILY}`;
52
- } else {
53
- document.documentElement.style.setProperty('--ux-font-family', DEFAULT_FONT_FAMILY);
54
- document.body.style.fontFamily = DEFAULT_FONT_FAMILY;
55
- }
56
- };
4
+ import { applyFont, applyTheme, extractFontFamilyFromLink, preloadFonts } from "./utils.js";
57
5
  const useTheme = ()=>{
58
6
  const theme = useThemeStore((state)=>state.theme);
7
+ const customTheme = useThemeStore((state)=>state.customTheme);
59
8
  const fontUrl = useThemeStore((state)=>state.fontUrl);
60
9
  const fontName = useThemeStore((state)=>state.fontName);
61
10
  const fontLoadStatus = useThemeStore((state)=>state.fontLoadStatus);
62
11
  const storeSetTheme = useThemeStore((state)=>state.setTheme);
12
+ const storeSetCustomTheme = useThemeStore((state)=>state.setCustomTheme);
63
13
  const storeSetFontUrl = useThemeStore((state)=>state.setFontUrl);
64
14
  const storeSetFontLoadStatus = useThemeStore((state)=>state.setFontLoadStatus);
65
15
  const storeSetFontName = useThemeStore((state)=>state.setFontName);
@@ -97,6 +47,11 @@ const useTheme = ()=>{
97
47
  applyTheme(theme);
98
48
  debouncedApplyFont(fontUrl, theme.fontName);
99
49
  }, []);
50
+ useEffect(()=>{
51
+ applyTheme(theme);
52
+ }, [
53
+ theme
54
+ ]);
100
55
  const setTheme = (theme)=>{
101
56
  storeSetTheme(theme);
102
57
  applyTheme(theme);
@@ -114,7 +69,9 @@ const useTheme = ()=>{
114
69
  };
115
70
  return {
116
71
  theme,
72
+ customTheme,
117
73
  setTheme,
74
+ setCustomTheme: storeSetCustomTheme,
118
75
  fontUrl,
119
76
  setFontUrl,
120
77
  clearFontUrl,
@@ -0,0 +1,28 @@
1
+ import { type ThemeItemType } from '@particle-network/ui-shared';
2
+ /**
3
+ * 初始化并预加载字体 CSS(仅执行一次)
4
+ */
5
+ export declare const preloadFonts: () => void;
6
+ export declare const setCSSProperty: (property: string, value: string) => void;
7
+ /**
8
+ * 应用自定义主题的颜色到 CSS 变量
9
+ */
10
+ export declare const applyCustomThemeColors: (theme: ThemeItemType) => void;
11
+ /**
12
+ * 清除自定义主题的 CSS 变量(切换到其他主题时)
13
+ */
14
+ export declare const clearCustomThemeColors: () => void;
15
+ /**
16
+ * 应用主题到文档根元素
17
+ */
18
+ export declare const applyTheme: (theme: ThemeItemType) => void;
19
+ /**
20
+ * 从 Google Fonts 链接中提取字体名称
21
+ * 例如: https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap
22
+ * 提取: Roboto
23
+ */
24
+ export declare const extractFontFamilyFromLink: (link: string) => string | null;
25
+ /**
26
+ * 应用字体到 DOM
27
+ */
28
+ export declare const applyFont: (fontName: string | undefined) => void;