@hsu-react/ui 0.0.18 → 0.0.20
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/es/components/Chart/Bar/index.js +21 -6
- package/es/components/Chart/Line/index.js +21 -6
- package/es/components/Chart/_utils/autoScrollLegend.d.ts +5 -0
- package/es/components/Chart/_utils/autoScrollLegend.js +16 -6
- package/es/components/FilePreview/_components/XlsxPreviewPane/index.d.ts +16 -0
- package/es/components/FilePreview/_components/XlsxPreviewPane/index.js +27 -0
- package/es/components/FilePreview/index.js +52 -23
- package/es/components/FormItem/FormInput/FormPasswordStrength/index.js +43 -4
- package/es/components/FormItem/index.d.ts +2 -2
- package/es/components/FormItem/index.js +46 -5
- package/lib/components/Chart/Bar/index.js +18 -3
- package/lib/components/Chart/Line/index.js +18 -3
- package/lib/components/Chart/_utils/autoScrollLegend.d.ts +5 -0
- package/lib/components/Chart/_utils/autoScrollLegend.js +14 -6
- package/lib/components/FilePreview/_components/XlsxPreviewPane/index.d.ts +16 -0
- package/lib/components/FilePreview/_components/XlsxPreviewPane/index.js +35 -0
- package/lib/components/FilePreview/index.js +47 -23
- package/lib/components/FormItem/FormInput/FormPasswordStrength/index.js +37 -5
- package/lib/components/FormItem/index.d.ts +2 -2
- package/lib/components/FormItem/index.js +43 -7
- package/package.json +6 -5
- package/src/components/Chart/Bar/index.tsx +26 -6
- package/src/components/Chart/Line/index.tsx +26 -6
- package/src/components/Chart/_utils/autoScrollLegend.ts +17 -3
- package/src/components/FilePreview/_components/XlsxPreviewPane/index.tsx +33 -0
- package/src/components/FilePreview/index.md +11 -0
- package/src/components/FilePreview/index.tsx +46 -19
- package/src/components/FormItem/FormInput/FormPasswordStrength/index.tsx +43 -4
- package/src/components/FormItem/index.md +19 -0
- package/src/components/FormItem/index.tsx +44 -5
|
@@ -1,16 +1,33 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import PdfPreview from "./_components/PdfPreview";
|
|
1
|
+
import React, { Suspense, lazy } from "react";
|
|
3
2
|
import VideoPreview from "./_components/VideoPreview";
|
|
4
3
|
import TextPreview from "./_components/TextPreview";
|
|
5
|
-
import MarkdownPreview from "./_components/MarkdownPreview";
|
|
6
4
|
import ImagePreview from "./_components/ImagePreview";
|
|
7
|
-
import XlsxPreview from "./_components/XlsxPreview";
|
|
8
|
-
import { useXlsxData } from "./_hooks";
|
|
9
5
|
import { FilePreviewType, FilePreviewTypeArr } from "./_utils";
|
|
10
6
|
|
|
11
7
|
export type { FilePreviewType };
|
|
12
8
|
export { FilePreviewTypeArr };
|
|
13
9
|
|
|
10
|
+
/**
|
|
11
|
+
* 重型格式按需加载。
|
|
12
|
+
*
|
|
13
|
+
* FilePreview 是个按 fileType 分发的 dispatcher,而它处在 Upload / FormItem 的静态
|
|
14
|
+
* 依赖图上(FormItem → FormUpload → Upload → FilePreview),业务侧只要用到任何一种
|
|
15
|
+
* 表单项,下面这些只服务于特定格式的库就会被打进首屏:
|
|
16
|
+
*
|
|
17
|
+
* pdf → PdfPreview → hsu-utils 的 RenderPDF → pdfjs-dist 约 374 KB
|
|
18
|
+
* xlsx → XlsxPreviewPane → xlsx + x-data-spreadsheet
|
|
19
|
+
* md → MarkdownPreview → Markdown → katex 等
|
|
20
|
+
*
|
|
21
|
+
* 修 dispatcher 而不是逐个改它的引用方:这里改一次,所有静态 import FilePreview 的
|
|
22
|
+
* 地方(Upload、UploadedItem、FormImage…)都受益,也不会因为将来多一个引用方而回归。
|
|
23
|
+
*
|
|
24
|
+
* 图片/文本/视频三种是轻量实现,保持静态——它们也是最常命中的格式,不值得为其
|
|
25
|
+
* 付一次异步往返。
|
|
26
|
+
*/
|
|
27
|
+
const PdfPreview = lazy(() => import("./_components/PdfPreview"));
|
|
28
|
+
const MarkdownPreview = lazy(() => import("./_components/MarkdownPreview"));
|
|
29
|
+
const XlsxPreviewPane = lazy(() => import("./_components/XlsxPreviewPane"));
|
|
30
|
+
|
|
14
31
|
interface FilePreviewProps {
|
|
15
32
|
fileUrl?: string;
|
|
16
33
|
fileType?: FilePreviewType;
|
|
@@ -25,7 +42,6 @@ interface FilePreviewProps {
|
|
|
25
42
|
const FilePreview: React.FC<FilePreviewProps> = (props) => {
|
|
26
43
|
const { fileUrl, fileType, open, onClose, text, className, pagination } =
|
|
27
44
|
props;
|
|
28
|
-
const xlsxData = useXlsxData({ fileType, fileUrl });
|
|
29
45
|
|
|
30
46
|
if (!open) {
|
|
31
47
|
return null;
|
|
@@ -43,13 +59,15 @@ const FilePreview: React.FC<FilePreviewProps> = (props) => {
|
|
|
43
59
|
}
|
|
44
60
|
case "pdf": {
|
|
45
61
|
return (
|
|
46
|
-
<
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
62
|
+
<Suspense fallback={null}>
|
|
63
|
+
<PdfPreview
|
|
64
|
+
fileUrl={fileUrl}
|
|
65
|
+
open={open}
|
|
66
|
+
onClose={onClose}
|
|
67
|
+
className={className}
|
|
68
|
+
pagination={pagination}
|
|
69
|
+
/>
|
|
70
|
+
</Suspense>
|
|
53
71
|
);
|
|
54
72
|
}
|
|
55
73
|
case "jpg":
|
|
@@ -71,16 +89,25 @@ const FilePreview: React.FC<FilePreviewProps> = (props) => {
|
|
|
71
89
|
}
|
|
72
90
|
case "md": {
|
|
73
91
|
return (
|
|
74
|
-
<
|
|
92
|
+
<Suspense fallback={null}>
|
|
93
|
+
<MarkdownPreview
|
|
94
|
+
text={text}
|
|
95
|
+
onClose={onClose}
|
|
96
|
+
className={className}
|
|
97
|
+
/>
|
|
98
|
+
</Suspense>
|
|
75
99
|
);
|
|
76
100
|
}
|
|
77
101
|
case "xlsx": {
|
|
78
102
|
return (
|
|
79
|
-
<
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
103
|
+
<Suspense fallback={null}>
|
|
104
|
+
<XlsxPreviewPane
|
|
105
|
+
fileUrl={fileUrl}
|
|
106
|
+
fileType={fileType}
|
|
107
|
+
onClose={onClose}
|
|
108
|
+
className={className}
|
|
109
|
+
/>
|
|
110
|
+
</Suspense>
|
|
84
111
|
);
|
|
85
112
|
}
|
|
86
113
|
default: {
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import Input, { InputProps as BasicInputProps } from "../../../Input";
|
|
2
2
|
import ItemContainer, { ItemContainerProps } from "../../ItemContainer";
|
|
3
|
-
import React, { useState } from "react";
|
|
3
|
+
import React, { useEffect, useRef, useState } from "react";
|
|
4
4
|
|
|
5
5
|
import styles from "./index.module.scss";
|
|
6
|
-
import zxcvbn from "zxcvbn";
|
|
7
6
|
|
|
8
7
|
interface InputProps extends BasicInputProps {}
|
|
9
8
|
|
|
@@ -11,9 +10,43 @@ export interface FormPasswordStrengthProps extends ItemContainerProps {
|
|
|
11
10
|
componentProps?: InputProps;
|
|
12
11
|
}
|
|
13
12
|
|
|
13
|
+
/**
|
|
14
|
+
* zxcvbn 约 793 KB(未压缩),却只在这一个字段的 onChange 里用来算强度分。
|
|
15
|
+
* 静态 import 会让它跟着 FormInput → FormItem 进入每个引入过表单项的页面的
|
|
16
|
+
* 首屏 chunk。改成动态 import:只有真的渲染了 PASSWORDSTRENGTH 字段才去拉。
|
|
17
|
+
*
|
|
18
|
+
* 模块级缓存 Promise,多个密码框共用一次加载;失败时清掉缓存,下次重试。
|
|
19
|
+
*/
|
|
20
|
+
type Zxcvbn = typeof import("zxcvbn");
|
|
21
|
+
|
|
22
|
+
let zxcvbnPromise: Promise<Zxcvbn> | undefined;
|
|
23
|
+
const loadZxcvbn = (): Promise<Zxcvbn> => {
|
|
24
|
+
if (!zxcvbnPromise) {
|
|
25
|
+
zxcvbnPromise = import("zxcvbn")
|
|
26
|
+
.then((mod) => {
|
|
27
|
+
// zxcvbn 是 `export =` 的 CJS 模块:不同打包器/interop 下动态 import 拿到的
|
|
28
|
+
// 可能是函数本身,也可能是 { default: 函数 },两种都要兼容
|
|
29
|
+
const m = mod as Zxcvbn & { default?: Zxcvbn };
|
|
30
|
+
return m.default ?? m;
|
|
31
|
+
})
|
|
32
|
+
.catch((err) => {
|
|
33
|
+
zxcvbnPromise = undefined;
|
|
34
|
+
throw err;
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
return zxcvbnPromise;
|
|
38
|
+
};
|
|
39
|
+
|
|
14
40
|
const PasswordStrength: React.FC<InputProps> = (props) => {
|
|
15
41
|
const { placeholder, onChange, ...inputConfig } = props;
|
|
16
42
|
const [fraction, setFraction] = useState<number>(0);
|
|
43
|
+
// 挂载即预取:chunk 仍是独立的,但用户敲第一个字符时通常已经就绪
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
loadZxcvbn().catch(() => undefined);
|
|
46
|
+
}, []);
|
|
47
|
+
|
|
48
|
+
// 打字比加载快时会有多次并发计算,迟到的结果不能盖掉最新一次
|
|
49
|
+
const seqRef = useRef(0);
|
|
17
50
|
|
|
18
51
|
return (
|
|
19
52
|
<div className={styles.passwordStrength}>
|
|
@@ -23,8 +56,14 @@ const PasswordStrength: React.FC<InputProps> = (props) => {
|
|
|
23
56
|
onChange={(value) => {
|
|
24
57
|
onChange && onChange(value);
|
|
25
58
|
|
|
26
|
-
const
|
|
27
|
-
|
|
59
|
+
const seq = ++seqRef.current;
|
|
60
|
+
loadZxcvbn()
|
|
61
|
+
.then((zxcvbn) => {
|
|
62
|
+
if (seq !== seqRef.current) return;
|
|
63
|
+
setFraction(zxcvbn(value).guesses_log10);
|
|
64
|
+
})
|
|
65
|
+
// 强度条只是辅助提示,拉不到就不显示,不打断用户输入
|
|
66
|
+
.catch(() => undefined);
|
|
28
67
|
}}
|
|
29
68
|
allowClear
|
|
30
69
|
autoComplete="new-password"
|
|
@@ -528,3 +528,22 @@ export default () => (
|
|
|
528
528
|
- 上传类:`FILE`、`IMAGEFILE`
|
|
529
529
|
|
|
530
530
|
> 同时导出 `FormItemContainer`(即 `ItemContainer`)及类型 `FormItemType`、`FormItemProps`、`PlaceholderDict` / `PlaceholderDictEn` 占位文案字典。
|
|
531
|
+
|
|
532
|
+
### 重型字段按需加载
|
|
533
|
+
|
|
534
|
+
`EDITOR`、`CODEMIRROR`、`PASSWORDSTRENGTH` 三类字段的实现依赖体积很大的三方库
|
|
535
|
+
(`@wangeditor/editor` 约 814 KB、`@codemirror/*` 约 380 KB、`zxcvbn` 约 793 KB,均为未压缩)。
|
|
536
|
+
`FormItem` 几乎会被每个业务页面引入,若静态引入这些渲染器,那么**只要用到任何一种表单项**,
|
|
537
|
+
上述库就会被一并打进消费方的首屏产物。
|
|
538
|
+
|
|
539
|
+
因此这三类字段改为按需加载:只有 `type` 真的命中时才去拉对应 chunk。对使用方而言:
|
|
540
|
+
|
|
541
|
+
- **用法不变**,仍然是 `<FormItem type="EDITOR" ... />`,无需自行包 `Suspense`。
|
|
542
|
+
- 首次渲染这类字段时,该字段会晚一拍出现(组件内部已用 `Suspense fallback={null}` 兜住,
|
|
543
|
+
不会让表单布局跳动)。antd `Form` 的值存在 store 里,字段挂载晚于 `setFieldsValue`
|
|
544
|
+
也能正确回填,**不影响编辑态回显**。
|
|
545
|
+
- `PASSWORDSTRENGTH` 在挂载时即预取 `zxcvbn`,正常输入速度下感知不到延迟;强度条在
|
|
546
|
+
加载完成前保持 0,加载失败则不显示,不阻断输入。
|
|
547
|
+
|
|
548
|
+
若消费方希望这些库进首屏(例如整站以富文本编辑为主),照常在自己的入口静态
|
|
549
|
+
`import Editor from "@hsu-react/ui/es/components/Editor"` 即可,两者不冲突。
|
|
@@ -8,7 +8,7 @@ import FormDatePicker, {
|
|
|
8
8
|
FormRangePickerProps,
|
|
9
9
|
FormStepPickerProps,
|
|
10
10
|
} from "./FormDatePicker";
|
|
11
|
-
import
|
|
11
|
+
import type { FormEditorProps } from "./FormEditor";
|
|
12
12
|
import FormInput, {
|
|
13
13
|
FormInputNumberProps,
|
|
14
14
|
FormInputProps,
|
|
@@ -31,9 +31,40 @@ import FormTree, { FormTreeProps } from "./FormTree";
|
|
|
31
31
|
import FormUpload, { FormImageProps, FormUploadProps } from "./FormUpload";
|
|
32
32
|
import ItemContainer, { ItemContainerProps } from "./ItemContainer";
|
|
33
33
|
import FormText, { FormTextProps } from "./FormText";
|
|
34
|
-
import
|
|
34
|
+
import type { FormCodeMirrorProps } from "./FormCodeMirror";
|
|
35
35
|
|
|
36
|
-
import React from "react";
|
|
36
|
+
import React, { Suspense, lazy } from "react";
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 重型字段按需加载。
|
|
40
|
+
*
|
|
41
|
+
* FormItem 是个静态分发器,几乎每个业务页面都会引入它。若在这里静态 import
|
|
42
|
+
* 全部渲染器,那么**只要用到任何一种表单项**,富文本、代码编辑器这些巨型依赖
|
|
43
|
+
* 就会被一并拉进消费方的首屏 chunk:
|
|
44
|
+
*
|
|
45
|
+
* FormItem → FormEditor → Editor → @wangeditor/editor (约 814 KB)
|
|
46
|
+
* FormItem → FormCodeMirror → CodeMirror → @codemirror/* (约 380 KB)
|
|
47
|
+
*
|
|
48
|
+
* 消费方实测:某后台项目仅因入口图里存在 FormItem,首屏就多背了约 2.5 MB
|
|
49
|
+
* 未压缩的三方库;改为按需加载后首屏从 3.72 MB 降到 965 KB(gzip)。
|
|
50
|
+
*
|
|
51
|
+
* 只有 type 真的命中 EDITOR / CODEMIRROR 时才会去拉对应 chunk。类型仍走静态
|
|
52
|
+
* `import type`(编译期擦除,不产生运行时依赖),所以 FormItemMap 不受影响。
|
|
53
|
+
* 同一目录下的 CodeMirror 早就用 `import()` 按需加载语言包/校验器,这里是同一思路。
|
|
54
|
+
*/
|
|
55
|
+
const FormEditor = lazy(() => import("./FormEditor"));
|
|
56
|
+
const FormCodeMirror = lazy(() => import("./FormCodeMirror"));
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* 重型字段的 Suspense 包装。
|
|
60
|
+
*
|
|
61
|
+
* fallback 用 null 而不是骨架:这些字段都由 ItemContainer 渲染 antd 的
|
|
62
|
+
* Form.Item,加载期间不占位反而不会让表单布局跳动;antd Form 的值存在 store 里,
|
|
63
|
+
* 字段挂载晚于 setFieldsValue 也能正确回填,不影响编辑态回显。
|
|
64
|
+
*/
|
|
65
|
+
const LazyField: React.FC<{ children: React.ReactNode }> = ({ children }) => (
|
|
66
|
+
<Suspense fallback={null}>{children}</Suspense>
|
|
67
|
+
);
|
|
37
68
|
|
|
38
69
|
export { ItemContainer as FormItemContainer };
|
|
39
70
|
export type { ItemContainerProps as FormItemContainerProps };
|
|
@@ -374,9 +405,17 @@ const FormItem: React.FC<FormItemProps> = (props) => {
|
|
|
374
405
|
case "SLIDER":
|
|
375
406
|
return <FormSlider {...props} />;
|
|
376
407
|
case "EDITOR":
|
|
377
|
-
return
|
|
408
|
+
return (
|
|
409
|
+
<LazyField>
|
|
410
|
+
<FormEditor {...props} />
|
|
411
|
+
</LazyField>
|
|
412
|
+
);
|
|
378
413
|
case "CODEMIRROR":
|
|
379
|
-
return
|
|
414
|
+
return (
|
|
415
|
+
<LazyField>
|
|
416
|
+
<FormCodeMirror {...props} />
|
|
417
|
+
</LazyField>
|
|
418
|
+
);
|
|
380
419
|
case "TEXT":
|
|
381
420
|
return <FormText {...props} />;
|
|
382
421
|
default:
|