@particle-network/ui-react 0.4.0-beta.8 → 0.4.0

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 (53) hide show
  1. package/dist/components/UXAutocomplete/index.d.ts +5 -0
  2. package/dist/components/UXAutocomplete/index.js +72 -0
  3. package/dist/components/UXButton/button-theme.js +15 -23
  4. package/dist/components/UXButton/use-button.js +2 -1
  5. package/dist/components/UXCheckbox/checkbox.extend.js +3 -3
  6. package/dist/components/UXDatePicker/index.js +1 -1
  7. package/dist/components/UXDateRangePicker/index.js +1 -1
  8. package/dist/components/UXDivider/divider.extend.d.ts +2 -2
  9. package/dist/components/UXDrawer/index.d.ts +9 -0
  10. package/dist/components/UXDrawer/index.js +89 -0
  11. package/dist/components/UXEmpty/index.d.ts +2 -1
  12. package/dist/components/UXEmpty/index.js +8 -2
  13. package/dist/components/UXHint/index.js +1 -1
  14. package/dist/components/UXInput/index.d.ts +33 -33
  15. package/dist/components/UXInput/input.extend.d.ts +33 -33
  16. package/dist/components/UXModal/index.d.ts +1 -1
  17. package/dist/components/UXModal/index.js +7 -6
  18. package/dist/components/UXSelect/index.js +2 -2
  19. package/dist/components/UXSwitch/index.d.ts +22 -22
  20. package/dist/components/UXSwitch/switch.extend.d.ts +22 -22
  21. package/dist/components/UXTable/index.d.ts +19 -19
  22. package/dist/components/UXTable/table.extend.d.ts +19 -19
  23. package/dist/components/UXThemeSwitch/index.d.ts +0 -1
  24. package/dist/components/UXThemeSwitch/index.js +0 -1
  25. package/dist/components/UXThemeSwitch/theme-item.d.ts +1 -1
  26. package/dist/components/UXThemeSwitch/theme-item.js +4 -5
  27. package/dist/components/UXThemeSwitch/theme-switch.d.ts +3 -3
  28. package/dist/components/UXThemeSwitch/theme-switch.js +133 -100
  29. package/dist/components/UXThemeSwitch/use-color-scheme.d.ts +1 -1
  30. package/dist/components/UXThemeSwitch/use-color-scheme.js +3 -3
  31. package/dist/components/UXThemeSwitch/use-theme-color.js +3 -3
  32. package/dist/components/UXThemeSwitch/use-theme-store.d.ts +5 -20
  33. package/dist/components/UXThemeSwitch/use-theme-store.js +14 -28
  34. package/dist/components/UXThemeSwitch/use-theme.d.ts +7 -12
  35. package/dist/components/UXThemeSwitch/use-theme.js +71 -80
  36. package/dist/components/UXToast/index.d.ts +7 -4
  37. package/dist/components/UXToast/index.js +21 -17
  38. package/dist/components/UXTooltip/tooltip.extend.d.ts +21 -21
  39. package/dist/components/UXTooltip/tooltip.extend.js +2 -1
  40. package/dist/components/index.d.ts +2 -0
  41. package/dist/components/index.js +2 -0
  42. package/dist/hooks/useI18n.d.ts +6 -2
  43. package/dist/hooks/useI18n.js +15 -7
  44. package/dist/utils/detect.js +1 -2
  45. package/dist/utils/input-classes.d.ts +3 -0
  46. package/dist/utils/input-classes.js +3 -0
  47. package/dist/utils/variants.js +16 -16
  48. package/package.json +5 -6
  49. package/tailwind-preset.js +205 -109
  50. package/dist/components/UXThemeSwitch/theme-data.d.ts +0 -12
  51. package/dist/components/UXThemeSwitch/theme-data.js +0 -340
  52. package/dist/icons/index.d.ts +0 -14
  53. package/dist/icons/index.js +0 -120
@@ -1,124 +1,157 @@
1
1
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
- import "react";
3
- import { Drawer, DrawerBody, DrawerContent, DrawerFooter, DrawerHeader } from "@heroui/drawer";
2
+ import { useState } from "react";
3
+ import { cn } from "@heroui/theme";
4
4
  import { useDisclosure } from "@heroui/use-disclosure";
5
- import { ChartColorSwitchIcon } from "@particle-network/icons/web";
5
+ import { ChartColorSwitchIcon, CopyIcon } from "@particle-network/icons/web";
6
+ import { themeData } from "@particle-network/ui-shared";
6
7
  import { useI18n } from "../../hooks/index.js";
7
- import { Flex, HStack, VStack } from "../layout/index.js";
8
+ import { UXCopy, UXTooltip } from "../index.js";
9
+ import { HStack, VStack } from "../layout/index.js";
8
10
  import { Text } from "../typography/Text.js";
9
11
  import { UXButton } from "../UXButton/index.js";
10
12
  import { UXDivider } from "../UXDivider/index.js";
11
13
  import { UXInput } from "../UXInput/index.js";
14
+ import { UXModal } from "../UXModal/index.js";
12
15
  import { UXSpinner } from "../UXSpinner/index.js";
13
- import { themeData } from "./theme-data.js";
14
16
  import { ThemeItem } from "./theme-item.js";
15
17
  import { useTheme } from "./use-theme.js";
16
- const UXThemeSwitchDrawer = ({ isOpen, onClose, onOpenChange, ...props })=>{
18
+ const FONT_EXAMPLES = [
19
+ {
20
+ title: 'Manrope',
21
+ url: 'https://fonts.googleapis.com/css2?family=Manrope:wght@200..800&display=swap'
22
+ },
23
+ {
24
+ title: 'Poppins',
25
+ url: 'https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap'
26
+ },
27
+ {
28
+ title: 'Roboto',
29
+ url: 'https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100..900;1,100..900&display=swap'
30
+ }
31
+ ];
32
+ const UXThemeSwitchModal = ({ isOpen, onClose, onOpenChange, ...props })=>{
17
33
  const i18n = useI18n();
18
- const { selectedTheme, setSelectedTheme, savedTheme, setSavedTheme, selectedFontLink, setSelectedFontLink, savedFontLink, setSavedFontLink, fontName, fontLoadStatus, fontLoadError } = useTheme();
19
- const handleOpenChange = (_isOpen)=>{
20
- onOpenChange?.(_isOpen);
21
- if (!_isOpen) {
22
- setSelectedTheme(savedTheme);
23
- setSelectedFontLink(savedFontLink);
24
- }
25
- };
26
- const handleApplyTheme = ()=>{
27
- setSavedTheme(selectedTheme);
28
- setSavedFontLink(selectedFontLink);
29
- onClose?.();
30
- };
31
- const handleReset = ()=>{
32
- setSelectedTheme(savedTheme);
33
- setSelectedFontLink(savedFontLink);
34
- };
35
- return /*#__PURE__*/ jsx(Drawer, {
34
+ const { theme: selectedTheme, setTheme, fontUrl, setFontUrl, fontName, fontLoadStatus, clearFontUrl } = useTheme();
35
+ const [isFontExampleOpen, setIsFontExampleOpen] = useState(false);
36
+ return /*#__PURE__*/ jsx(UXModal, {
36
37
  isOpen: isOpen,
37
- backdrop: "transparent",
38
- onOpenChange: handleOpenChange,
38
+ classNames: {
39
+ base: 'md:max-h-[800px]'
40
+ },
41
+ title: i18n.theme.title,
42
+ footer: /*#__PURE__*/ jsx(UXButton, {
43
+ fullWidth: true,
44
+ size: "lg",
45
+ color: "primary",
46
+ onPress: onClose,
47
+ children: i18n.theme.done
48
+ }),
49
+ onOpenChange: onOpenChange,
39
50
  ...props,
40
- children: /*#__PURE__*/ jsxs(DrawerContent, {
51
+ children: /*#__PURE__*/ jsxs(VStack, {
52
+ gap: "lg",
41
53
  children: [
42
- /*#__PURE__*/ jsx(DrawerHeader, {
43
- className: "flex flex-col gap-1",
44
- children: i18n.theme.title
54
+ /*#__PURE__*/ jsx("div", {
55
+ className: "grid grid-cols-3 gap-y-5 pt-1",
56
+ children: themeData.map((theme)=>/*#__PURE__*/ jsx(ThemeItem, {
57
+ isSelected: selectedTheme.id === theme.id,
58
+ onClick: ()=>setTheme(theme),
59
+ ...theme
60
+ }, theme.id))
45
61
  }),
46
- /*#__PURE__*/ jsxs(DrawerBody, {
62
+ /*#__PURE__*/ jsx(UXDivider, {}),
63
+ /*#__PURE__*/ jsxs(VStack, {
64
+ gap: "md",
47
65
  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))
66
+ /*#__PURE__*/ jsx(Text, {
67
+ body1Bold: true,
68
+ children: i18n.theme.font.title
57
69
  }),
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, {
70
+ /*#__PURE__*/ jsx(UXInput, {
71
+ isClearable: true,
72
+ startContent: 'loading' === fontLoadStatus && /*#__PURE__*/ jsx(UXSpinner, {
73
+ size: 18
74
+ }),
75
+ isInvalid: 'error' === fontLoadStatus,
76
+ className: "w-full",
77
+ placeholder: i18n.theme.font.placeholder,
78
+ value: fontUrl,
79
+ description: /*#__PURE__*/ jsxs(Fragment, {
80
+ children: [
81
+ 'success' === fontLoadStatus && /*#__PURE__*/ jsxs(Text, {
82
+ body3Bold: true,
83
+ color: "success",
78
84
  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
- })
85
+ i18n.theme.font.success,
86
+ fontName
92
87
  ]
93
88
  }),
94
- onValueChange: setSelectedFontLink
95
- })
96
- ]
89
+ 'error' === fontLoadStatus && /*#__PURE__*/ jsx(Text, {
90
+ body3Bold: true,
91
+ color: "danger",
92
+ children: i18n.theme.font.error
93
+ })
94
+ ]
95
+ }),
96
+ onValueChange: setFontUrl,
97
+ onClear: clearFontUrl
97
98
  })
98
99
  ]
99
100
  }),
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
- })
101
+ /*#__PURE__*/ jsxs(Text, {
102
+ color: "secondary",
103
+ children: [
104
+ i18n.theme.font.hint,
105
+ /*#__PURE__*/ jsx("a", {
106
+ href: "https://fonts.google.com/",
107
+ target: "_blank",
108
+ rel: "noopener noreferrer",
109
+ className: "hover:underline",
110
+ children: "https://fonts.google.com/"
111
+ })
112
+ ]
113
+ }),
114
+ /*#__PURE__*/ jsx(UXButton, {
115
+ className: "self-start",
116
+ variant: "text",
117
+ color: "primary",
118
+ onPress: ()=>{
119
+ setIsFontExampleOpen((prev)=>!prev);
120
+ },
121
+ children: i18n.theme.font.example[isFontExampleOpen ? 'hide' : 'show']
122
+ }),
123
+ /*#__PURE__*/ jsx(VStack, {
124
+ gap: "sm",
125
+ className: cn(isFontExampleOpen ? 'flex' : 'hidden'),
126
+ children: FONT_EXAMPLES.map((example)=>/*#__PURE__*/ jsxs(HStack, {
127
+ gap: "lg",
128
+ children: [
129
+ /*#__PURE__*/ jsx(UXTooltip, {
130
+ content: example.url,
131
+ placement: "bottom",
132
+ children: /*#__PURE__*/ jsxs(Text, {
133
+ color: "secondary",
134
+ className: "flex-1 truncate cursor-pointer",
135
+ children: [
136
+ example.title,
137
+ ": ",
138
+ example.url
139
+ ]
140
+ })
141
+ }),
142
+ /*#__PURE__*/ jsx(UXCopy, {
143
+ copyText: example.url,
144
+ children: /*#__PURE__*/ jsx(UXButton, {
145
+ isIconOnly: true,
146
+ size: "sm",
147
+ variant: "light",
148
+ children: /*#__PURE__*/ jsx(CopyIcon, {
149
+ color: "secondary"
150
+ })
151
+ })
152
+ })
153
+ ]
154
+ }, example.title))
122
155
  })
123
156
  ]
124
157
  })
@@ -138,7 +171,7 @@ const UXThemeSwitch = ({ children })=>{
138
171
  return /*#__PURE__*/ jsxs(Fragment, {
139
172
  children: [
140
173
  renderChildren(),
141
- /*#__PURE__*/ jsx(UXThemeSwitchDrawer, {
174
+ /*#__PURE__*/ jsx(UXThemeSwitchModal, {
142
175
  isOpen: isOpen,
143
176
  onClose: onClose,
144
177
  onOpenChange: onOpenChange
@@ -146,4 +179,4 @@ const UXThemeSwitch = ({ children })=>{
146
179
  ]
147
180
  });
148
181
  };
149
- export { UXThemeSwitch, UXThemeSwitchDrawer };
182
+ export { UXThemeSwitch, UXThemeSwitchModal };
@@ -1,5 +1,5 @@
1
1
  export declare const useColorScheme: () => {
2
- colorScheme: import("./theme-data").ColorScheme;
2
+ colorScheme: import("@particle-network/ui-shared").ColorScheme;
3
3
  isDark: boolean;
4
4
  isLight: boolean;
5
5
  };
@@ -1,7 +1,7 @@
1
- import { useThemeStore } from "./use-theme-store.js";
1
+ import { useTheme } from "./use-theme.js";
2
2
  const useColorScheme = ()=>{
3
- const { selectedTheme } = useThemeStore();
4
- const { colorScheme } = selectedTheme;
3
+ const { theme } = useTheme();
4
+ const { colorScheme } = theme;
5
5
  return {
6
6
  colorScheme,
7
7
  isDark: 'dark' === colorScheme,
@@ -1,6 +1,6 @@
1
- import { useThemeStore } from "./use-theme-store.js";
1
+ import { useTheme } from "./use-theme.js";
2
2
  const useThemeColor = ()=>{
3
- const { selectedTheme } = useThemeStore();
4
- return selectedTheme.colorVariables;
3
+ const { theme } = useTheme();
4
+ return theme.colorVariables;
5
5
  };
6
6
  export { useThemeColor };
@@ -1,22 +1,14 @@
1
- import { type ThemeItemType } from './theme-data';
1
+ import { type ThemeItemType } from '@particle-network/ui-shared';
2
2
  export type FontLoadStatus = 'idle' | 'loading' | 'success' | 'error';
3
3
  interface State {
4
- /**
5
- * 临时选中的 theme(用于预览)
6
- */
7
- selectedTheme: ThemeItemType;
8
4
  /**
9
5
  * 保存的主题
10
6
  */
11
- savedTheme: ThemeItemType;
12
- /**
13
- * 临时输入的字体链接(用于预览)
14
- */
15
- selectedFontLink: string;
7
+ theme: ThemeItemType;
16
8
  /**
17
9
  * 保存的字体链接
18
10
  */
19
- savedFontLink: string;
11
+ fontUrl: string;
20
12
  /**
21
13
  * 应用的字体名称
22
14
  */
@@ -25,18 +17,11 @@ interface State {
25
17
  * 字体加载状态
26
18
  */
27
19
  fontLoadStatus: FontLoadStatus;
28
- /**
29
- * 字体加载错误信息
30
- */
31
- fontLoadError: string | null;
32
20
  }
33
21
  interface Actions {
34
- setSelectedTheme: (theme: ThemeItemType) => void;
35
- setSavedTheme: (theme: ThemeItemType) => void;
36
- setSelectedFontLink: (link: string) => void;
37
- setSavedFontLink: (link: string) => void;
22
+ setTheme: (theme: ThemeItemType) => void;
23
+ setFontUrl: (fontUrl: string) => void;
38
24
  setFontLoadStatus: (status: FontLoadStatus) => void;
39
- setFontLoadError: (error: string | null) => void;
40
25
  setFontName: (name: string) => void;
41
26
  }
42
27
  type ThemeStore = State & Actions;
@@ -1,44 +1,30 @@
1
+ import { themeData } from "@particle-network/ui-shared";
1
2
  import { create } from "zustand";
2
3
  import { createJSONStorage, persist } from "zustand/middleware";
3
- import { themeData } from "./theme-data.js";
4
4
  const useThemeStore = create()(persist((set)=>({
5
- selectedTheme: themeData["0"],
6
- savedTheme: themeData["0"],
7
- selectedFontLink: '',
8
- savedFontLink: '',
5
+ theme: themeData["0"],
6
+ fontUrl: '',
9
7
  fontName: '',
10
8
  fontLoadStatus: 'idle',
11
- fontLoadError: null,
12
- setSelectedTheme: (theme)=>set({
13
- selectedTheme: theme
9
+ setTheme: (theme)=>set({
10
+ theme
14
11
  }),
15
- setSavedTheme: (theme)=>set({
16
- savedTheme: theme
12
+ setFontUrl: (fontUrl)=>set({
13
+ fontUrl
17
14
  }),
18
- setSelectedFontLink: (link)=>set({
19
- selectedFontLink: link
15
+ setFontLoadStatus: (fontLoadStatus)=>set({
16
+ fontLoadStatus
20
17
  }),
21
- setSavedFontLink: (link)=>set({
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
18
+ setFontName: (fontName)=>set({
19
+ fontName
32
20
  })
33
21
  }), {
34
- name: 'ux-theme',
22
+ name: 'ux-theme-3',
35
23
  storage: createJSONStorage(()=>'undefined' != typeof window ? window.localStorage : {}),
36
24
  partialize: (state)=>({
37
- selectedTheme: state.selectedTheme,
38
- savedTheme: state.savedTheme,
25
+ theme: state.theme,
39
26
  fontName: state.fontName,
40
- selectedFontLink: state.selectedFontLink,
41
- savedFontLink: state.savedFontLink
27
+ fontUrl: state.fontUrl
42
28
  })
43
29
  }));
44
30
  export { useThemeStore };
@@ -1,18 +1,13 @@
1
- import { type ThemeItemType } from './theme-data';
2
- import { type FontLoadStatus } from './use-theme-store';
1
+ import { type ThemeItemType } from '@particle-network/ui-shared';
3
2
  /**
4
3
  * UX 主题管理 Hook
5
4
  */
6
5
  export declare const useTheme: () => {
7
- selectedTheme: ThemeItemType;
8
- setSelectedTheme: (theme: ThemeItemType) => void;
9
- savedTheme: ThemeItemType;
10
- setSavedTheme: (theme: ThemeItemType) => void;
11
- selectedFontLink: string;
12
- setSelectedFontLink: (link: string) => void;
13
- savedFontLink: string;
14
- setSavedFontLink: (link: string) => void;
15
- fontLoadStatus: FontLoadStatus;
16
- fontLoadError: string | null;
6
+ theme: ThemeItemType;
7
+ setTheme: (theme: ThemeItemType) => void;
8
+ fontUrl: string;
9
+ setFontUrl: (link: string) => void;
10
+ clearFontUrl: () => void;
11
+ fontLoadStatus: import("./use-theme-store").FontLoadStatus;
17
12
  fontName: string;
18
13
  };
@@ -1,8 +1,19 @@
1
1
  import { useEffect } from "react";
2
+ import { themeKeys } from "@particle-network/ui-shared";
2
3
  import { useDebounceFn } from "ahooks";
3
- import { themeKeys } from "./theme-data.js";
4
4
  import { useThemeStore } from "./use-theme-store.js";
5
- const DEFAULT_FONT_FAMILY = 'ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji';
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
+ };
6
17
  const applyTheme = (theme)=>{
7
18
  if ('undefined' == typeof window) return;
8
19
  const root = document.documentElement;
@@ -21,107 +32,87 @@ const extractFontFamilyFromLink = (link)=>{
21
32
  const url = new URL(link);
22
33
  const familyParam = url.searchParams.get('family');
23
34
  if (!familyParam) return null;
24
- const fontName = familyParam.split(':')[0].replace(/\+/g, ' ');
25
- return fontName;
35
+ const firstFamily = familyParam.split('&family=')[0].split(':')[0].replace(/\+/g, ' ');
36
+ return firstFamily;
26
37
  } catch {
27
38
  return null;
28
39
  }
29
40
  };
30
- const loadGoogleFont = (link, options)=>{
31
- const { setFontLoadStatus, setFontLoadError, setFontName } = options;
41
+ const applyFont = (fontName)=>{
32
42
  if ('undefined' == typeof window) return;
33
- const existingLink = document.getElementById('ux-google-font-link');
34
- if (existingLink) existingLink.remove();
35
- if (!link?.trim()) {
43
+ if (fontName) {
44
+ document.documentElement.style.setProperty('--ux-font-family', `"${fontName}", ${DEFAULT_FONT_FAMILY}`);
45
+ document.body.style.fontFamily = `"${fontName}", ${DEFAULT_FONT_FAMILY}`;
46
+ } else {
36
47
  document.documentElement.style.setProperty('--ux-font-family', DEFAULT_FONT_FAMILY);
37
48
  document.body.style.fontFamily = DEFAULT_FONT_FAMILY;
38
- setFontLoadStatus('idle');
39
- setFontLoadError(null);
40
- setFontName('');
41
- return;
42
49
  }
43
- setFontLoadStatus('loading');
44
- setFontLoadError(null);
45
- const linkElement = document.createElement('link');
46
- linkElement.id = 'ux-google-font-link';
47
- linkElement.rel = 'stylesheet';
48
- linkElement.href = link;
49
- linkElement.onload = ()=>{
50
- setFontLoadStatus('success');
51
- setFontLoadError(null);
52
- const fontFamily = extractFontFamilyFromLink(link);
53
- if (fontFamily) {
54
- document.documentElement.style.setProperty('--ux-font-family', `"${fontFamily}", ${DEFAULT_FONT_FAMILY}`);
55
- document.body.style.fontFamily = `"${fontFamily}", ${DEFAULT_FONT_FAMILY}`;
56
- setFontName(fontFamily);
57
- }
58
- };
59
- linkElement.onerror = ()=>{
60
- const errorMessage = '字体加载失败,请检查链接是否正确';
61
- setFontLoadStatus('error');
62
- setFontLoadError(errorMessage);
63
- setFontName('');
64
- document.documentElement.style.setProperty('--ux-font-family', DEFAULT_FONT_FAMILY);
65
- document.body.style.fontFamily = DEFAULT_FONT_FAMILY;
66
- };
67
- document.head.appendChild(linkElement);
68
50
  };
69
51
  const useTheme = ()=>{
70
- const selectedTheme = useThemeStore((state)=>state.selectedTheme);
71
- const savedTheme = useThemeStore((state)=>state.savedTheme);
72
- const selectedFontLink = useThemeStore((state)=>state.selectedFontLink);
73
- const savedFontLink = useThemeStore((state)=>state.savedFontLink);
52
+ const theme = useThemeStore((state)=>state.theme);
53
+ const fontUrl = useThemeStore((state)=>state.fontUrl);
74
54
  const fontName = useThemeStore((state)=>state.fontName);
75
55
  const fontLoadStatus = useThemeStore((state)=>state.fontLoadStatus);
76
- const fontLoadError = useThemeStore((state)=>state.fontLoadError);
77
- const storeSetSelectedTheme = useThemeStore((state)=>state.setSelectedTheme);
78
- const storeSetSavedTheme = useThemeStore((state)=>state.setSavedTheme);
79
- const storeSetSelectedFontLink = useThemeStore((state)=>state.setSelectedFontLink);
80
- const storeSetSavedFontLink = useThemeStore((state)=>state.setSavedFontLink);
56
+ const storeSetTheme = useThemeStore((state)=>state.setTheme);
57
+ const storeSetFontUrl = useThemeStore((state)=>state.setFontUrl);
81
58
  const storeSetFontLoadStatus = useThemeStore((state)=>state.setFontLoadStatus);
82
- const storeSetFontLoadError = useThemeStore((state)=>state.setFontLoadError);
83
59
  const storeSetFontName = useThemeStore((state)=>state.setFontName);
84
- const { run: debouncedLoadGoogleFont } = useDebounceFn((link)=>loadGoogleFont(link, {
85
- setFontLoadStatus: storeSetFontLoadStatus,
86
- setFontLoadError: storeSetFontLoadError,
87
- setFontName: storeSetFontName
88
- }), {
89
- wait: 500
60
+ const applyFontWithStatus = (customFontUrl, themeFontName)=>{
61
+ if ('undefined' == typeof window) return;
62
+ const url = customFontUrl?.trim();
63
+ if (!url) return void applyFont(themeFontName);
64
+ const existingLink = document.getElementById('ux-google-font-link');
65
+ if (existingLink) existingLink.remove();
66
+ storeSetFontLoadStatus('loading');
67
+ const linkElement = document.createElement('link');
68
+ linkElement.id = 'ux-google-font-link';
69
+ linkElement.rel = 'stylesheet';
70
+ linkElement.href = url;
71
+ linkElement.onload = ()=>{
72
+ storeSetFontLoadStatus('success');
73
+ const fontFamily = extractFontFamilyFromLink(url);
74
+ if (fontFamily) {
75
+ applyFont(fontFamily);
76
+ storeSetFontName(fontFamily);
77
+ }
78
+ };
79
+ linkElement.onerror = ()=>{
80
+ storeSetFontLoadStatus('error');
81
+ storeSetFontName('');
82
+ applyFont(themeFontName);
83
+ };
84
+ document.head.appendChild(linkElement);
85
+ };
86
+ const { run: debouncedApplyFont } = useDebounceFn((customFontLink, themeFontName)=>applyFontWithStatus(customFontLink, themeFontName), {
87
+ wait: 300
90
88
  });
91
89
  useEffect(()=>{
92
- applyTheme(savedTheme);
93
- debouncedLoadGoogleFont(savedFontLink);
94
- }, [
95
- savedTheme,
96
- savedFontLink
97
- ]);
98
- const setSelectedTheme = (theme)=>{
99
- storeSetSelectedTheme(theme);
90
+ preloadFonts();
100
91
  applyTheme(theme);
101
- };
102
- const setSavedTheme = (theme)=>{
103
- storeSetSavedTheme(theme);
92
+ debouncedApplyFont(fontUrl, theme.fontName);
93
+ }, []);
94
+ const setTheme = (theme)=>{
95
+ storeSetTheme(theme);
104
96
  applyTheme(theme);
97
+ debouncedApplyFont(fontUrl, theme.fontName);
105
98
  };
106
- const setSelectedFontLink = (link)=>{
107
- storeSetSelectedFontLink(link);
108
- debouncedLoadGoogleFont(link);
99
+ const setFontUrl = (link)=>{
100
+ storeSetFontUrl(link);
101
+ debouncedApplyFont(link, theme.fontName);
109
102
  };
110
- const setSavedFontLink = (link)=>{
111
- storeSetSavedFontLink(link);
112
- debouncedLoadGoogleFont(link);
103
+ const clearFontUrl = ()=>{
104
+ storeSetFontUrl('');
105
+ storeSetFontName('');
106
+ storeSetFontLoadStatus('idle');
107
+ debouncedApplyFont('', theme.fontName);
113
108
  };
114
109
  return {
115
- selectedTheme,
116
- setSelectedTheme,
117
- savedTheme,
118
- setSavedTheme,
119
- selectedFontLink,
120
- setSelectedFontLink,
121
- savedFontLink,
122
- setSavedFontLink,
110
+ theme,
111
+ setTheme,
112
+ fontUrl,
113
+ setFontUrl,
114
+ clearFontUrl,
123
115
  fontLoadStatus,
124
- fontLoadError,
125
116
  fontName
126
117
  };
127
118
  };
@@ -1,13 +1,16 @@
1
+ import type { ReactNode } from 'react';
1
2
  import type { ToastProps } from '@heroui/toast';
2
- export type UXToastType = 'success' | 'error' | 'loading';
3
+ export { addToast, closeAll, closeToast, getToastQueue, isToastClosing } from '@heroui/toast';
4
+ export type UXToastType = 'success' | 'error' | 'loading' | 'info';
3
5
  export type UXToastProps = Partial<ToastProps> & {
4
6
  type: UXToastType;
5
7
  };
6
8
  export declare const UXToastProvider: () => import("react/jsx-runtime").JSX.Element;
7
9
  export declare const toast: {
8
- success: (message: string, props?: Partial<ToastProps>) => string | null;
9
- error: (message: string, props?: Partial<ToastProps>) => string | null;
10
- loading: (message: string, props?: Partial<ToastProps>) => string | null;
10
+ info: (message: ReactNode, props?: Partial<ToastProps>) => string | null;
11
+ success: (message: ReactNode, props?: Partial<ToastProps>) => string | null;
12
+ error: (message: ReactNode, props?: Partial<ToastProps>) => string | null;
13
+ loading: (message: ReactNode, props?: Partial<ToastProps>) => string | null;
11
14
  show: (props?: Partial<ToastProps> & {
12
15
  type: UXToastType;
13
16
  }) => string | null;