@particle-network/ui-react 0.5.1-beta.4 → 0.5.1-beta.5

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 (32) hide show
  1. package/dist/components/ProgressWrapper/index.d.ts +2 -2
  2. package/dist/components/UXChip/chip.extend.d.ts +2 -1
  3. package/dist/components/UXChip/index.d.ts +1 -1
  4. package/dist/components/UXColorPicker/color-picker.js +1 -1
  5. package/dist/components/UXColorPicker/types.d.ts +1 -1
  6. package/dist/components/UXThemeSwitch/constants.d.ts +9 -0
  7. package/dist/components/UXThemeSwitch/constants.js +3 -0
  8. package/dist/components/UXThemeSwitch/custom-theme-config.js +52 -15
  9. package/dist/components/UXThemeSwitch/theme-item.js +47 -11
  10. package/dist/components/UXThemeSwitch/theme-switch.js +6 -5
  11. package/dist/components/UXThemeSwitch/use-theme-color.d.ts +1 -22
  12. package/dist/components/UXThemeSwitch/use-theme-color.js +1 -9
  13. package/dist/components/UXThemeSwitch/use-theme-store.d.ts +5 -0
  14. package/dist/components/UXThemeSwitch/use-theme-store.js +9 -4
  15. package/dist/components/UXThemeSwitch/use-theme.d.ts +2 -0
  16. package/dist/components/UXThemeSwitch/use-theme.js +5 -76
  17. package/dist/components/UXThemeSwitch/utils.d.ts +28 -0
  18. package/dist/components/UXThemeSwitch/utils.js +202 -0
  19. package/dist/components/typography/Text.type.d.ts +2 -2
  20. package/dist/components/typography/Text.type.js +0 -1
  21. package/dist/heroui/constants.d.ts +18 -0
  22. package/dist/heroui/constants.js +98 -0
  23. package/dist/heroui/types.d.ts +91 -0
  24. package/dist/heroui/types.js +0 -0
  25. package/dist/heroui/utils/colors.d.ts +34 -0
  26. package/dist/heroui/utils/colors.js +121 -0
  27. package/dist/heroui/utils/object.d.ts +1 -0
  28. package/dist/heroui/utils/object.js +17 -0
  29. package/dist/hooks/useI18n.d.ts +54 -38
  30. package/dist/hooks/useI18n.js +54 -38
  31. package/package.json +4 -3
  32. package/tailwind-preset.js +22 -69
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { type UXForegroundColor, type UXRadius } from '@particle-network/ui-shared';
2
+ import { type HexColor, type UXForegroundColor, type UXRadius } from '@particle-network/ui-shared';
3
3
  export interface ProgressWrapperProps extends React.HTMLAttributes<HTMLDivElement> {
4
4
  /**
5
5
  * 进度 (%)
@@ -24,7 +24,7 @@ export interface ProgressWrapperProps extends React.HTMLAttributes<HTMLDivElemen
24
24
  /**
25
25
  * 进度条颜色
26
26
  */
27
- color?: UXForegroundColor | 'transparent' | `#${string}`;
27
+ color?: Exclude<UXForegroundColor, 'default' | 'white'> | 'transparent' | HexColor;
28
28
  /**
29
29
  * 进度条 SVG 样式
30
30
  */
@@ -1,7 +1,8 @@
1
1
  import type React from 'react';
2
2
  import { type ChipProps } from '@heroui/chip';
3
+ import type { HexColor } from '@particle-network/ui-shared';
3
4
  type ExtendedChipProps = Omit<ChipProps, 'color'> & {
4
- color?: 'default' | 'primary' | 'secondary' | 'success' | 'warning' | 'alert' | 'danger' | 'bullish' | 'bearish' | `#${string}`;
5
+ color?: 'default' | 'primary' | 'secondary' | 'success' | 'warning' | 'alert' | 'danger' | 'bullish' | 'bearish' | HexColor;
5
6
  };
6
7
  declare const ExtendedChip: React.ForwardRefExoticComponent<ExtendedChipProps>;
7
8
  export default ExtendedChip;
@@ -2,5 +2,5 @@ import React from 'react';
2
2
  import ExtendedChip from './chip.extend';
3
3
  export type UXChipProps = React.ComponentPropsWithRef<typeof ExtendedChip>;
4
4
  export declare const UXChip: import("@heroui/system-rsc").InternalForwardRefRenderFunction<"div", Omit<import("@heroui/chip").ChipProps, "color"> & {
5
- color?: "default" | "primary" | "secondary" | "success" | "warning" | "alert" | "danger" | "bullish" | "bearish" | `#${string}`;
5
+ color?: "default" | "primary" | "secondary" | "success" | "warning" | "alert" | "danger" | "bullish" | "bearish" | import("@particle-network/ui-shared").HexColor;
6
6
  }, never>;
@@ -201,7 +201,7 @@ const UXColorPicker = ({ className, isDisabled, placement = 'bottom-start', valu
201
201
  }),
202
202
  /*#__PURE__*/ jsx(ColorInput, {
203
203
  isDisabled: isDisabled,
204
- inputClassName: "focus:ring-0 px-0 text-left flex-1 relative h-7",
204
+ inputClassName: "focus:ring-0 px-0 text-left flex-1 relative bg-transparent",
205
205
  value: value,
206
206
  defaultValue: !isControlled && pickerKey > 0 ? internalColor : defaultValue,
207
207
  onChange: handleColorFieldsChange,
@@ -27,7 +27,7 @@ export interface UXColorPickerProps {
27
27
  */
28
28
  onValueChangeEnd?: (color: string) => void;
29
29
  /**
30
- * 是否已改变
30
+ * 是否已改变(展示 reset 按钮)
31
31
  */
32
32
  isChanged?: boolean;
33
33
  /**
@@ -0,0 +1,9 @@
1
+ /**
2
+ * 默认字体
3
+ */
4
+ export declare const DEFAULT_FONT_FAMILY = "Inter,system-ui,sans-serif,\"Microsoft YaHei\"";
5
+ /**
6
+ * 预加载所有主题字体的 Google Fonts CSS
7
+ * 包含所有可能用到的字体,一次性加载,避免切换主题时重复加载
8
+ */
9
+ export declare 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";
@@ -0,0 +1,3 @@
1
+ const DEFAULT_FONT_FAMILY = 'Inter,system-ui,sans-serif,"Microsoft YaHei"';
2
+ 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';
3
+ export { DEFAULT_FONT_FAMILY, PRELOAD_FONTS_URL };
@@ -5,6 +5,7 @@ import { produce } from "immer";
5
5
  import { useI18n, useLang } from "../../hooks/index.js";
6
6
  import { HStack, VStack } from "../layout/index.js";
7
7
  import { Text } from "../typography/Text.js";
8
+ import { UXButton } from "../UXButton/index.js";
8
9
  import { UXColorPicker } from "../UXColorPicker/index.js";
9
10
  import { UXSelect, UXSelectItem } from "../UXSelect/index.js";
10
11
  import { UXTab, UXTabs } from "../UXTabs/index.js";
@@ -28,17 +29,25 @@ const COLOR_CATEGORIES = {
28
29
  'secondary',
29
30
  'tertiary'
30
31
  ],
32
+ candle: [
33
+ 'candleUp',
34
+ 'candleBorderUp',
35
+ 'candleWickUp',
36
+ 'candleDown',
37
+ 'candleBorderDown',
38
+ 'candleWickDown'
39
+ ],
31
40
  signal: [
32
41
  'success',
33
42
  'danger',
34
- 'alert',
35
- 'gold'
43
+ 'warning',
44
+ 'alert'
36
45
  ]
37
46
  };
38
47
  const CustomThemeConfig = ()=>{
39
48
  const { lang } = useLang();
40
49
  const i18n = useI18n();
41
- const { theme, setTheme } = useTheme();
50
+ const { theme, setTheme, setCustomTheme } = useTheme();
42
51
  const baseThemes = useMemo(()=>themeData.filter((t)=>'custom' !== t.id), []);
43
52
  const baseThemeId = theme.baseThemeId || DEFAULT_THEME_ID;
44
53
  const baseTheme = useMemo(()=>themeData.find((t)=>t.id === baseThemeId) || themeData.find((t)=>t.id === DEFAULT_THEME_ID), [
@@ -46,18 +55,38 @@ const CustomThemeConfig = ()=>{
46
55
  ]);
47
56
  const handleBaseThemeChange = (themeId)=>{
48
57
  const selectedBaseTheme = themeData.find((t)=>t.id === themeId);
49
- if (selectedBaseTheme) setTheme({
50
- ...theme,
51
- baseThemeId: themeId,
52
- colorScheme: selectedBaseTheme.colorScheme,
53
- colorVariables: selectedBaseTheme.colorVariables
54
- });
58
+ if (selectedBaseTheme) {
59
+ const updatedTheme = {
60
+ ...theme,
61
+ baseThemeId: themeId,
62
+ colorScheme: selectedBaseTheme.colorScheme,
63
+ colorVariables: selectedBaseTheme.colorVariables
64
+ };
65
+ setTheme(updatedTheme);
66
+ setCustomTheme(updatedTheme);
67
+ }
55
68
  };
56
69
  const handleColorChange = (color, value)=>{
57
70
  const updatedTheme = produce(theme, (draft)=>{
58
71
  draft.colorVariables[color] = value;
59
72
  });
60
73
  setTheme(updatedTheme);
74
+ setCustomTheme(updatedTheme);
75
+ };
76
+ const handleSwapUpDown = ()=>{
77
+ const updatedTheme = produce(theme, (draft)=>{
78
+ const { bullish, bearish, candleUp, candleDown, candleWickUp, candleWickDown, candleBorderUp, candleBorderDown } = draft.colorVariables;
79
+ draft.colorVariables.bullish = bearish;
80
+ draft.colorVariables.bearish = bullish;
81
+ draft.colorVariables.candleUp = candleDown;
82
+ draft.colorVariables.candleDown = candleUp;
83
+ draft.colorVariables.candleWickUp = candleWickDown;
84
+ draft.colorVariables.candleWickDown = candleWickUp;
85
+ draft.colorVariables.candleBorderUp = candleBorderDown;
86
+ draft.colorVariables.candleBorderDown = candleBorderUp;
87
+ });
88
+ setTheme(updatedTheme);
89
+ setCustomTheme(updatedTheme);
61
90
  };
62
91
  const getColorValue = (color)=>theme.colorVariables[color] || baseTheme.colorVariables[color] || '#000000';
63
92
  const renderColorItem = (color, label)=>{
@@ -82,12 +111,20 @@ const CustomThemeConfig = ()=>{
82
111
  return /*#__PURE__*/ jsxs(VStack, {
83
112
  gap: "lg",
84
113
  children: [
85
- /*#__PURE__*/ jsx(HStack, {
114
+ /*#__PURE__*/ jsxs(HStack, {
86
115
  justify: "between",
87
- children: /*#__PURE__*/ jsx(Text, {
88
- body1Bold: true,
89
- children: i18n.theme.custom.title
90
- })
116
+ children: [
117
+ /*#__PURE__*/ jsx(Text, {
118
+ body1Bold: true,
119
+ children: i18n.theme.custom.title
120
+ }),
121
+ /*#__PURE__*/ jsx(UXButton, {
122
+ variant: "text",
123
+ color: "primary",
124
+ onPress: handleSwapUpDown,
125
+ children: i18n.theme.custom.swapUpDown
126
+ })
127
+ ]
91
128
  }),
92
129
  /*#__PURE__*/ jsxs(HStack, {
93
130
  justify: "between",
@@ -120,7 +157,7 @@ const CustomThemeConfig = ()=>{
120
157
  gap: "md",
121
158
  className: "pt-md",
122
159
  children: colors.map((color)=>{
123
- const label = i18n.theme.custom.colors[color] || color;
160
+ const label = i18n.colors[color] || color;
124
161
  return renderColorItem(color, label);
125
162
  })
126
163
  })
@@ -4,8 +4,11 @@ import { cn } from "@heroui/theme";
4
4
  import { useLang } from "../../hooks/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
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 transition-all duration-300 hover:scale-105', 'h-[59px] w-[100px] md:h-[71px] md:w-[120px]', 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['bg-default'] : 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['bg-default'] : 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,7 +154,10 @@ 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
162
  'custom' === id ? /*#__PURE__*/ jsxs(Fragment, {
127
163
  children: [
@@ -34,14 +34,15 @@ const FONT_EXAMPLES = [
34
34
  ];
35
35
  const UXThemeSwitchModal = ({ as = 'modal', omitThemes = [], backdrop, isOpen, onClose, onOpenChange })=>{
36
36
  const i18n = useI18n();
37
- const { theme: selectedTheme, setTheme, fontUrl, setFontUrl, fontName, fontLoadStatus, clearFontUrl } = useTheme();
37
+ const { theme: selectedTheme, customTheme: savedCustomTheme, setTheme, fontUrl, setFontUrl, fontName, fontLoadStatus, clearFontUrl } = useTheme();
38
38
  const [isFontExampleOpen, setIsFontExampleOpen] = useState(false);
39
39
  const Component = 'modal' === as ? UXModal : UXDrawer;
40
40
  const themes = useMemo(()=>themeData.filter((theme)=>!omitThemes.includes(theme.id)), [
41
41
  omitThemes
42
42
  ]);
43
43
  const handleThemeSelect = (theme)=>{
44
- if ('custom' === theme.id && 'custom' !== selectedTheme.id) {
44
+ if ('custom' === theme.id) if (savedCustomTheme) setTheme(savedCustomTheme);
45
+ else {
45
46
  const baseTheme = themeData.find((t)=>t.id === DEFAULT_THEME_ID) || themeData["0"];
46
47
  const customTheme = {
47
48
  ...theme,
@@ -50,7 +51,8 @@ const UXThemeSwitchModal = ({ as = 'modal', omitThemes = [], backdrop, isOpen, o
50
51
  colorVariables: baseTheme.colorVariables
51
52
  };
52
53
  setTheme(customTheme);
53
- } else setTheme(theme);
54
+ }
55
+ else setTheme(theme);
54
56
  };
55
57
  return /*#__PURE__*/ jsx(Component, {
56
58
  isOpen: isOpen,
@@ -81,8 +83,7 @@ const UXThemeSwitchModal = ({ as = 'modal', omitThemes = [], backdrop, isOpen, o
81
83
  'custom' === selectedTheme.id && /*#__PURE__*/ jsxs(Fragment, {
82
84
  children: [
83
85
  /*#__PURE__*/ jsx(UXDivider, {}),
84
- /*#__PURE__*/ jsx(CustomThemeConfig, {}),
85
- /*#__PURE__*/ jsx(UXDivider, {})
86
+ /*#__PURE__*/ jsx(CustomThemeConfig, {})
86
87
  ]
87
88
  }),
88
89
  /*#__PURE__*/ jsx(UXDivider, {}),
@@ -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,14 +1,6 @@
1
- import { useMemo } from "react";
2
1
  import { useThemeStore } from "./use-theme-store.js";
3
2
  const useThemeColor = ()=>{
4
3
  const { colorVariables } = useThemeStore((state)=>state.theme);
5
- const themeColor = useMemo(()=>({
6
- ...colorVariables,
7
- transparent: 'transparent',
8
- white: '#FFFFFF'
9
- }), [
10
- colorVariables
11
- ]);
12
- return themeColor;
4
+ return colorVariables;
13
5
  };
14
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,11 +23,12 @@ const useThemeStore = create()(persist((set)=>({
19
23
  fontName
20
24
  })
21
25
  }), {
22
- name: 'ux-preferences-theme',
23
- version: 2,
26
+ name: 'ux-preference-theme',
27
+ version: 0,
24
28
  storage: createJSONStorage(()=>'undefined' != typeof window ? window.localStorage : {}),
25
29
  partialize: (state)=>({
26
30
  theme: state.theme,
31
+ customTheme: state.customTheme,
27
32
  fontName: state.fontName,
28
33
  fontUrl: state.fontUrl
29
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,88 +1,15 @@
1
1
  import { useEffect } from "react";
2
- import { colorToCSSVariable, hexColorToHSLValue, 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 applyCustomThemeColors = (theme)=>{
18
- if ('undefined' == typeof window) return;
19
- const root = document.documentElement;
20
- Object.entries(theme.colorVariables).forEach(([color, value])=>{
21
- const cssVariable = colorToCSSVariable[color]?.self;
22
- if (cssVariable) cssVariable.forEach((variable)=>{
23
- root.style.setProperty(variable, hexColorToHSLValue(value));
24
- });
25
- });
26
- };
27
- const clearCustomThemeColors = ()=>{
28
- if ('undefined' == typeof window) return;
29
- const root = document.documentElement;
30
- Object.values(colorToCSSVariable).forEach((cssVar)=>{
31
- cssVar.self?.forEach((variable)=>{
32
- root.style.removeProperty(variable);
33
- });
34
- });
35
- };
36
- const applyTheme = (theme)=>{
37
- if ('undefined' == typeof window) return;
38
- const root = document.documentElement;
39
- const isClassListCorrect = root.classList.contains(theme.id);
40
- const isDataThemeCorrect = root.getAttribute('data-theme') === theme.colorScheme;
41
- const isDataPrefersColorCorrect = root.getAttribute('data-prefers-color') === theme.colorScheme;
42
- if ('custom' === theme.id) {
43
- clearCustomThemeColors();
44
- applyCustomThemeColors(theme);
45
- } else clearCustomThemeColors();
46
- if (isClassListCorrect && isDataThemeCorrect && isDataPrefersColorCorrect && 'custom' !== theme.id) return;
47
- if (!isDataThemeCorrect) root.setAttribute('data-theme', theme.colorScheme);
48
- if (!isDataPrefersColorCorrect) root.setAttribute('data-prefers-color', theme.colorScheme);
49
- root.classList.remove('dark');
50
- root.classList.remove('light');
51
- if (!isClassListCorrect) {
52
- themeKeys.forEach((key)=>{
53
- root.classList.remove(key);
54
- });
55
- root.classList.add('custom' === theme.id ? theme.baseThemeId : theme.id);
56
- }
57
- };
58
- const extractFontFamilyFromLink = (link)=>{
59
- if (!link) return null;
60
- try {
61
- const url = new URL(link);
62
- const familyParam = url.searchParams.get('family');
63
- if (!familyParam) return null;
64
- const firstFamily = familyParam.split('&family=')[0].split(':')[0].replace(/\+/g, ' ');
65
- return firstFamily;
66
- } catch {
67
- return null;
68
- }
69
- };
70
- const applyFont = (fontName)=>{
71
- if ('undefined' == typeof window) return;
72
- if (fontName) {
73
- document.documentElement.style.setProperty('--ux-font-family', `"${fontName}", ${DEFAULT_FONT_FAMILY}`);
74
- document.body.style.fontFamily = `"${fontName}", ${DEFAULT_FONT_FAMILY}`;
75
- } else {
76
- document.documentElement.style.setProperty('--ux-font-family', DEFAULT_FONT_FAMILY);
77
- document.body.style.fontFamily = DEFAULT_FONT_FAMILY;
78
- }
79
- };
4
+ import { applyFont, applyTheme, extractFontFamilyFromLink, preloadFonts } from "./utils.js";
80
5
  const useTheme = ()=>{
81
6
  const theme = useThemeStore((state)=>state.theme);
7
+ const customTheme = useThemeStore((state)=>state.customTheme);
82
8
  const fontUrl = useThemeStore((state)=>state.fontUrl);
83
9
  const fontName = useThemeStore((state)=>state.fontName);
84
10
  const fontLoadStatus = useThemeStore((state)=>state.fontLoadStatus);
85
11
  const storeSetTheme = useThemeStore((state)=>state.setTheme);
12
+ const storeSetCustomTheme = useThemeStore((state)=>state.setCustomTheme);
86
13
  const storeSetFontUrl = useThemeStore((state)=>state.setFontUrl);
87
14
  const storeSetFontLoadStatus = useThemeStore((state)=>state.setFontLoadStatus);
88
15
  const storeSetFontName = useThemeStore((state)=>state.setFontName);
@@ -142,7 +69,9 @@ const useTheme = ()=>{
142
69
  };
143
70
  return {
144
71
  theme,
72
+ customTheme,
145
73
  setTheme,
74
+ setCustomTheme: storeSetCustomTheme,
146
75
  fontUrl,
147
76
  setFontUrl,
148
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;