@fe-free/core 6.0.8 → 6.0.10

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/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @fe-free/core
2
2
 
3
+ ## 6.0.10
4
+
5
+ ### Patch Changes
6
+
7
+ - feat: i18n
8
+ - @fe-free/icons@6.0.10
9
+ - @fe-free/tool@6.0.10
10
+
11
+ ## 6.0.9
12
+
13
+ ### Patch Changes
14
+
15
+ - fix: tabs
16
+ - @fe-free/icons@6.0.9
17
+ - @fe-free/tool@6.0.9
18
+
3
19
  ## 6.0.8
4
20
 
5
21
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fe-free/core",
3
- "version": "6.0.8",
3
+ "version": "6.0.10",
4
4
  "description": "React 业务核心组件库:CRUD、ProForm 扩展、布局、路由、树、上传等(Antd + ProComponents)",
5
5
  "license": "ISC",
6
6
  "author": "",
@@ -43,8 +43,8 @@
43
43
  "i18next-icu": "^2.4.1",
44
44
  "react": "^19.2.0",
45
45
  "react-i18next": "^16.4.0",
46
- "@fe-free/icons": "6.0.8",
47
- "@fe-free/tool": "6.0.8"
46
+ "@fe-free/icons": "6.0.10",
47
+ "@fe-free/tool": "6.0.10"
48
48
  },
49
49
  "scripts": {
50
50
  "i18n-extract": "rm -rf ./src/locales/zh-CN && npx i18next-cli extract"
@@ -8,6 +8,7 @@ import { merge } from 'lodash-es';
8
8
  import { useEffect, useMemo } from 'react';
9
9
  import { useTranslation } from 'react-i18next';
10
10
  import { BrowserRouter as Router, useNavigate } from 'react-router-dom';
11
+
11
12
  import { EnumLanguage, I18nProvider } from '../i18n';
12
13
  import { routeTool } from '../route';
13
14
  import { customValueTypeMap } from '../value_type_map';
@@ -86,7 +87,7 @@ function CheckUpdate({ basename }: { basename: string }) {
86
87
  return () => {
87
88
  clearInterval(timer);
88
89
  };
89
- }, [t]);
90
+ }, [t, basename, modal]);
90
91
 
91
92
  return null;
92
93
  }
@@ -139,7 +140,7 @@ function CoreAppBase(props: CoreAppProps) {
139
140
  if (window.location.pathname === '/' && basename !== '/' && basename !== '') {
140
141
  window.location.href = basename;
141
142
  }
142
- }, []);
143
+ }, [basename]);
143
144
 
144
145
  const theme = useMemo(() => {
145
146
  return merge(themeConfig, configProviderProps?.theme);
@@ -3,8 +3,10 @@ import { Input } from 'antd';
3
3
  import classNames from 'classnames';
4
4
  import { useCallback, useEffect, useMemo, useState } from 'react';
5
5
  import { useTranslation } from 'react-i18next';
6
+
6
7
  import type { CRUDProps } from '../crud';
7
8
  import { CRUD } from '../crud';
9
+
8
10
  import './style.scss';
9
11
 
10
12
  // eslint-disable-next-line @typescript-eslint/no-empty-object-type
@@ -35,7 +37,7 @@ function useTips(props) {
35
37
  ),
36
38
  );
37
39
  }
38
- }, [t]);
40
+ }, [t]); // eslint-disable-line react-hooks/exhaustive-deps
39
41
  }
40
42
 
41
43
  function SearchRender(props: {
@@ -91,7 +93,7 @@ function CRUDOfList(props: CRUDOfListProps) {
91
93
 
92
94
  const newParams = useMemo(() => {
93
95
  return {
94
- ...(tableProps.params || {}),
96
+ ...tableProps.params,
95
97
  [searchDataIndex]: debouncedSearchValue,
96
98
  };
97
99
  }, [debouncedSearchValue, searchDataIndex, tableProps.params]);
@@ -17,7 +17,7 @@ function EditorJSON({ value, onChange, readonly, mode, mainMenuBar }: EditorJSON
17
17
  const refOnChange = useRef(onChange);
18
18
 
19
19
  useEffect(() => {
20
- refEditor.current?.update({
20
+ void refEditor.current?.update({
21
21
  text: value || '',
22
22
  });
23
23
  }, [value]);
package/src/i18n.tsx CHANGED
@@ -29,40 +29,60 @@ const listLanguage = Object.keys(valueEnumLanguage).map((key) => {
29
29
  };
30
30
  });
31
31
 
32
- function initI18n({ enTranslation }) {
33
- const cacheLng = localStorage.getItem('i18nextLng') || EnumLanguage.ZH_CN;
32
+ let hasSetupPlugins = false;
33
+
34
+ function getDefaultLng() {
35
+ if (typeof window === 'undefined') {
36
+ return EnumLanguage.ZH_CN;
37
+ }
38
+
39
+ const cacheLng = window.localStorage.getItem('i18nextLng') || EnumLanguage.ZH_CN;
34
40
  const lng = listLanguage.find((item) => item.value === cacheLng) ? cacheLng : EnumLanguage.ZH_CN;
35
41
 
36
- console.log('initI18n', 'cacheLng', cacheLng, 'lng', lng);
37
-
38
- i18n
39
- .use(LanguageDetector)
40
- .use(initReactI18next)
41
- .use(ICU)
42
- .init({
43
- resources: {
44
- [EnumLanguage.ZH_CN]: {
45
- translation: {},
46
- },
47
- [EnumLanguage.EN_US]: {
48
- translation: {
49
- ...enTranslation,
50
- },
51
- },
42
+ return lng;
43
+ }
44
+
45
+ function initI18n({ enTranslation = {} }: { enTranslation?: Record<string, unknown> } = {}) {
46
+ if (i18n.isInitialized) {
47
+ i18n.addResourceBundle(EnumLanguage.EN_US, 'translation', enTranslation, true, true);
48
+ return;
49
+ }
50
+
51
+ if (!hasSetupPlugins) {
52
+ i18n.use(LanguageDetector).use(initReactI18next).use(ICU);
53
+ hasSetupPlugins = true;
54
+ }
55
+
56
+ const lng = getDefaultLng();
57
+ console.log('initI18n', lng);
58
+
59
+ void i18n.init({
60
+ resources: {
61
+ [EnumLanguage.ZH_CN]: {
62
+ translation: {},
52
63
  },
53
- lng,
54
- fallbackLng: EnumLanguage.ZH_CN,
55
- interpolation: {
56
- escapeValue: false,
64
+ [EnumLanguage.EN_US]: {
65
+ translation: enTranslation,
57
66
  },
58
- });
67
+ },
68
+ lng,
69
+ fallbackLng: EnumLanguage.ZH_CN,
70
+ interpolation: {
71
+ escapeValue: false,
72
+ },
73
+ });
59
74
  }
60
75
 
61
76
  function I18nProvider({ children }: { children: React.ReactNode }) {
77
+ initI18n();
62
78
  return <I18nextProvider i18n={i18n}>{children}</I18nextProvider>;
63
79
  }
64
80
 
65
- // @ts-ignore
66
- window._i18n = i18n;
81
+ initI18n();
82
+
83
+ if (typeof window !== 'undefined') {
84
+ // @ts-ignore
85
+ window._i18n = i18n;
86
+ }
67
87
 
68
88
  export { EnumLanguage, I18nProvider, initI18n, listLanguage, valueEnumLanguage };
@@ -1,3 +1,4 @@
1
+ import type { TabsProps as AntdTabsProps } from 'antd';
1
2
  import classNames from 'classnames';
2
3
  import { Fragment, useMemo } from 'react';
3
4
  import { useSearchParams } from 'react-router-dom';
@@ -94,47 +95,55 @@ function PageLayout({
94
95
  }
95
96
 
96
97
  interface PageLayoutTabsProps extends PageLayoutProps {
97
- tabsProps: Omit<TabsProps, 'items' | 'withSearchParams'> & {
98
- items: {
99
- key: string;
100
- label: React.ReactNode;
101
- children: React.ReactNode;
102
- }[];
98
+ tabsProps: Omit<TabsProps, 'withSearchParams' | 'tabsProps'> & {
99
+ tabsProps: Omit<AntdTabsProps, 'items'> & {
100
+ items: {
101
+ key: string;
102
+ label: React.ReactNode;
103
+ children: React.ReactNode;
104
+ }[];
105
+ };
103
106
  };
104
107
  }
105
108
 
106
109
  function PageLayoutTabs(props: PageLayoutTabsProps) {
107
110
  const { tabsProps, ...rest } = props;
111
+ const { tabsProps: antdTabsProps, ...tabsPropsRest } = tabsProps;
112
+ const { items: antdItems, ...antdTabsPropsRest } = antdTabsProps;
108
113
 
109
114
  const [searchParams] = useSearchParams();
110
115
  const tab = searchParams.get(tabsProps.tabKey || 'tab') || undefined;
111
116
 
112
117
  const { children, newItems } = useMemo(() => {
113
- const items = props.tabsProps.items;
118
+ const items = antdItems ?? [];
114
119
 
115
- const item = items.find((item) => item.key === tab);
116
- const children = item?.children || items[0]?.children;
120
+ const item = items.find((v) => v.key === tab);
121
+ const child = item?.children || items[0]?.children;
117
122
 
118
- const newItems = items.map((item) => ({
119
- ...item,
120
- children: undefined,
121
- }));
122
-
123
- return { children, newItems };
124
- }, [props.tabsProps.items, tab]);
123
+ return {
124
+ children: child,
125
+ newItems: items.map((v) => ({
126
+ ...v,
127
+ children: undefined,
128
+ })),
129
+ };
130
+ }, [antdItems, tab]);
125
131
 
126
132
  return (
127
133
  <PageLayout
128
134
  direction="vertical"
129
135
  start={
130
136
  <Tabs
131
- tabBarExtraContent={{
132
- left: <div className="w-4" />,
133
- right: <div className="w-4" />,
137
+ tabsProps={{
138
+ tabBarExtraContent: {
139
+ left: <div className="w-4" />,
140
+ right: <div className="w-4" />,
141
+ },
142
+ ...antdTabsPropsRest,
143
+ items: newItems,
134
144
  }}
135
- {...tabsProps}
136
- items={newItems}
137
145
  withSearchParams
146
+ {...tabsPropsRest}
138
147
  />
139
148
  }
140
149
  {...rest}
@@ -0,0 +1,17 @@
1
+ import { PageLayoutTabs } from '@fe-free/core';
2
+ import type { Meta, StoryObj } from '@storybook/react-vite';
3
+
4
+ const meta: Meta<typeof PageLayoutTabs> = {
5
+ title: '@fe-free/core/PageLayoutTabs',
6
+ component: PageLayoutTabs,
7
+ tags: ['autodocs'],
8
+ };
9
+
10
+ export default meta;
11
+ type Story = StoryObj<typeof PageLayoutTabs>;
12
+
13
+ export const Default: Story = {
14
+ render: () => {
15
+ return <div>依赖 react-router-dom 的 Route 组件,暂不提供 demo</div>;
16
+ },
17
+ };
@@ -12,15 +12,15 @@ interface TabsProps {
12
12
  withSearchParams?: boolean;
13
13
  /** 设置 searchParams 的类型,默认 set */
14
14
  searchParamsType?: 'set' | 'change';
15
- /** 默认 tab */
15
+ /** url 上字段的名字,默认 tab */
16
16
  tabKey?: string;
17
-
18
- tabProps?: AntdTabsProps;
17
+ /** antd Tabs 的 props */
18
+ tabsProps?: AntdTabsProps;
19
19
  }
20
20
 
21
21
  function Tabs(props: TabsProps) {
22
- const { withSearchParams, searchParamsType = 'set', tabKey = 'tab', tabProps } = props;
23
- const { className, activeKey, onChange, ...rest } = tabProps || {};
22
+ const { withSearchParams, searchParamsType = 'set', tabKey = 'tab', tabsProps } = props;
23
+ const { className, activeKey, onChange, ...rest } = tabsProps || {};
24
24
 
25
25
  const [searchParams] = useSearchParams();
26
26
  const tab = searchParams.get(tabKey) || undefined;