@huibo-ui/react-antd 1.0.0 → 1.0.2

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.
@@ -1,31 +1,33 @@
1
1
  import React from 'react';
2
2
  /**
3
- * antd Form 兼容层。
3
+ * antd Form 兼容层(同步化版)。
4
4
  *
5
- * useForm() 返回一个实例,其方法转发到内部 <hb-form> 自定义元素的 @Method
6
- * getFieldsValue / setFieldsValue / setFieldValue / getFieldValue /
7
- * validateFields / validate / resetFields)。
8
- * 转发通过 ref:useForm 创建一个 ref setter,<HbForm ref={form.__setRef}> 挂载后
9
- * CE 元素存进闭包,之后实例方法即直接调 CE method。
10
- * 在 CE 挂载前调用方法会排队等待——与 antd useForm 行为一致。
5
+ * 核心改造:hb-form @Method 是异步(Promise),但 antd FormInstance 的
6
+ * getFieldsValue / resetFields / setFieldsValue 是同步。scrm 等业务大量
7
+ * `formRef.current.getFieldsValue()` 同步取值,异步会拿到 Promise 而崩。
8
+ *
9
+ * 解法:instance 内部维护同步 `values` 缓存,hbChange(onValuesChange) 实时更新;
10
+ * - 读方法同步返回缓存
11
+ * - 写方法同步更新缓存 + 异步调 hb-form @Method 落到子控件
12
+ * - validateFields / validate 保持异步(antd 本身也返回 Promise)
13
+ *
14
+ * Form 用 forwardRef:`<Form ref={formRef}>` 拿到同步 FormInstance。
15
+ * useForm 与 ref 共享同一份 createFormInstance 逻辑。
11
16
  */
12
17
  export interface FormInstance {
13
- /** 内部:供 <HbForm ref> 绑定 CE 元素 */
14
- __setRef: (el: any) => void;
15
- getFieldValue: (name: string) => Promise<any>;
16
- getFieldsValue: (nameList?: string[]) => Promise<Record<string, any>>;
17
- setFieldValue: (name: string, value: any) => Promise<void>;
18
- setFieldsValue: (values: Record<string, any>) => Promise<void>;
19
- /** 对齐 antd:通过 resolve(values)/reject(errorFields) */
18
+ getFieldValue: (name: string) => any;
19
+ getFieldsValue: (nameList?: string[]) => Record<string, any>;
20
+ setFieldValue: (name: string, value: any) => void;
21
+ setFieldsValue: (values: Record<string, any>) => void;
20
22
  validateFields: (nameList?: string[]) => Promise<Record<string, any>>;
21
- /** 返回布尔,对齐早期用法 */
22
23
  validate: (nameList?: string[]) => Promise<boolean>;
23
- resetFields: (nameList?: string[]) => Promise<void>;
24
- /** 兼容 antd 上的其它常用方法(简易实现,保持不崩溃) */
24
+ resetFields: (nameList?: string[]) => void;
25
25
  submit: () => void;
26
- scrollToField: (_name: string) => void;
26
+ scrollToField: (name: string) => void;
27
27
  getFieldsError: () => any[];
28
- getFieldError: (_name: string) => string[];
28
+ getFieldError: (name: string) => string[];
29
+ /** scrm 兼容:handleReset = resetFields */
30
+ handleReset?: () => void;
29
31
  }
30
32
  export interface FormProps {
31
33
  form?: FormInstance;
@@ -47,15 +49,9 @@ export interface FormProps {
47
49
  children?: React.ReactNode;
48
50
  style?: React.CSSProperties;
49
51
  className?: string;
52
+ [key: string]: any;
50
53
  }
51
- export declare function Form(props: FormProps): React.JSX.Element;
52
- export declare namespace Form {
53
- var Item: typeof FormItem;
54
- var useForm: any;
55
- var List: any;
56
- var Provider: any;
57
- var useWatch: any;
58
- }
54
+ export declare const Form: React.ForwardRefExoticComponent<Omit<FormProps, "ref"> & React.RefAttributes<FormInstance>>;
59
55
  export interface FormItemProps {
60
56
  name?: string | string[];
61
57
  label?: React.ReactNode;
@@ -72,8 +68,11 @@ export interface FormItemProps {
72
68
  };
73
69
  colon?: boolean;
74
70
  tooltip?: React.ReactNode;
71
+ /** antd noStyle:不额外包裹。hb-form-item 无对应概念,这里仅消费该 prop 不报错 */
72
+ noStyle?: boolean;
75
73
  style?: React.CSSProperties;
76
74
  className?: string;
77
75
  children?: React.ReactNode;
76
+ [key: string]: any;
78
77
  }
79
78
  export declare function FormItem(props: FormItemProps): React.JSX.Element;
@@ -1,44 +1,15 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import React from 'react';
3
3
  import { HbForm, HbFormItem } from '@huibo-ui/react';
4
- export function Form(props) {
5
- const { form, initialValues, onFinish, onFinishFailed, onValuesChange, layout, labelCol, children, style, className, } = props;
6
- const labelPosition = layout === 'vertical' ? 'top' : layout === 'inline' ? 'left' : 'right';
7
- // 延迟绑定:ref 回调把 CE 元素交给 form 实例
8
- const refCallback = (el) => {
9
- if (form)
10
- form.__setRef(el);
11
- };
12
- return (_jsx(HbForm, { ref: refCallback, initialValues: initialValues, onFinish: onFinish, onFinishFailed: onFinishFailed ? (errors) => onFinishFailed({ errorFields: errors, values: {} }) : undefined, onValuesChange: onValuesChange
13
- ? (info) => {
14
- const prop = info?.prop;
15
- if (!prop)
16
- return;
17
- onValuesChange({ [prop]: info.value }, {});
18
- }
19
- : undefined, labelPosition: labelPosition, labelWidth: labelCol?.span ? `${(labelCol.span / 24) * 100}%` : '80px', style: style, className: className, children: children }));
20
- }
21
- export function FormItem(props) {
22
- const { name, label, rules, required, labelCol, children, style, className } = props;
23
- // name 可能是路径数组 ['a','b'],hb-form-item 的 prop 取路径字符串
24
- const propName = Array.isArray(name) ? name.join('.') : name;
25
- return (_jsx(HbFormItem, { prop: propName || '', label: typeof label === 'string' ? label : '', rules: rules || [], required: required, labelCol: labelCol?.span, style: style, className: className, children: children }));
26
- }
27
- Form.Item = FormItem;
28
- /**
29
- * useForm —— 返回 antd 风格的 form 实例。
30
- * 实例方法转发到 <hb-form> CE 的 @Method。CE 尚未挂载时,方法调用会被队列等待。
31
- */
32
- Form.useForm = function useForm() {
4
+ function createFormInstance() {
33
5
  let ceEl = null;
34
6
  const pending = [];
7
+ const values = {};
35
8
  const bind = (el) => {
36
9
  ceEl = el;
37
- if (el) {
10
+ if (el)
38
11
  pending.splice(0).forEach(fn => fn());
39
- }
40
12
  };
41
- /** CE 挂载前排队,挂载后立即执行;返回 promise 以兼容 await */
42
13
  const run = (fn) => {
43
14
  if (ceEl)
44
15
  return fn(ceEl);
@@ -46,52 +17,93 @@ Form.useForm = function useForm() {
46
17
  pending.push(() => fn(ceEl).then(resolve, reject));
47
18
  });
48
19
  };
49
- const instance = {
50
- __setRef: bind,
51
- getFieldValue: (name) => run(el => el.getFieldValue(name)),
52
- getFieldsValue: (nameList) => run(el => el.getFieldsValue(nameList)),
53
- setFieldValue: (name, value) => run(el => el.setFieldValueMethod(name, value)),
54
- setFieldsValue: (values) => run(el => el.setFieldsValue(values)),
55
- validateFields: (nameList) => run(el => el.validateFields(nameList)),
56
- validate: (nameList) => run(el => (nameList ? el.validate(nameList) : el.validate())),
57
- resetFields: (nameList) => run(el => el.resetFields(nameList)),
58
- submit: () => {
59
- // 提交由表单内 type=submit 按钮触发;此处做兜底校验
60
- run(async (el) => {
61
- try {
62
- await el.validateFields();
63
- }
64
- catch {
65
- /* 校验失败错误已由 FormItem 展示 */
66
- }
67
- });
20
+ const inst = {
21
+ getFieldValue: (name) => values[name],
22
+ getFieldsValue: (nameList) => {
23
+ if (nameList && nameList.length > 0) {
24
+ const picked = {};
25
+ nameList.forEach(n => { if (n in values)
26
+ picked[n] = values[n]; });
27
+ return picked;
28
+ }
29
+ return { ...values };
68
30
  },
31
+ setFieldValue: (name, value) => {
32
+ values[name] = value;
33
+ void run(el => el.setFieldValueMethod(name, value));
34
+ },
35
+ setFieldsValue: (vals) => {
36
+ if (vals)
37
+ Object.assign(values, vals);
38
+ void run(el => el.setFieldsValue(vals));
39
+ },
40
+ resetFields: (nameList) => {
41
+ if (nameList && nameList.length > 0) {
42
+ nameList.forEach(n => { delete values[n]; });
43
+ }
44
+ else {
45
+ Object.keys(values).forEach(k => { delete values[k]; });
46
+ }
47
+ void run(el => el.resetFields(nameList));
48
+ },
49
+ validateFields: (nameList) => run(el => el.validateFields(nameList)).then(() => ({ ...values })),
50
+ validate: (nameList) => run(el => (nameList ? el.validate(nameList) : el.validate())),
51
+ submit: () => { void run(async (el) => { try {
52
+ await el.validateFields();
53
+ }
54
+ catch { /* 校验失败由 FormItem 展示 */ } }); },
69
55
  scrollToField: () => { },
70
56
  getFieldsError: () => [],
71
57
  getFieldError: () => [],
58
+ handleReset: () => inst.resetFields(),
72
59
  };
73
- return [instance];
60
+ // 内部钩子:Form 组件挂
61
+ inst.__setRef = bind;
62
+ inst.__syncValue = (prop, value) => { if (prop)
63
+ values[prop] = value; };
64
+ return inst;
65
+ }
66
+ export const Form = React.forwardRef(function Form(props, ref) {
67
+ const { form: formProp, initialValues, onFinish, onFinishFailed, onValuesChange, layout, labelCol, children, style, className, } = props;
68
+ const [innerForm] = React.useState(() => formProp || createFormInstance());
69
+ const form = formProp || innerForm;
70
+ React.useImperativeHandle(ref, () => form, [form]);
71
+ // initialValues 落到同步缓存 + hb-form(子控件)
72
+ React.useEffect(() => {
73
+ if (initialValues) {
74
+ void form.setFieldsValue(initialValues);
75
+ }
76
+ // eslint-disable-next-line react-hooks/exhaustive-deps
77
+ }, []);
78
+ const labelPosition = layout === 'vertical' ? 'top' : layout === 'inline' ? 'left' : 'right';
79
+ const refCallback = (el) => { form.__setRef?.(el); };
80
+ return (_jsx(HbForm, { ref: refCallback, initialValues: initialValues, onFinish: onFinish, onFinishFailed: onFinishFailed ? (errors) => onFinishFailed({ errorFields: errors, values: {} }) : undefined, onValuesChange: (info) => {
81
+ const prop = info?.prop;
82
+ if (prop)
83
+ form.__syncValue?.(prop, info.value);
84
+ if (onValuesChange)
85
+ onValuesChange({ [prop]: info?.value }, {});
86
+ }, labelPosition: labelPosition, labelWidth: labelCol?.span ? `${(labelCol.span / 24) * 100}%` : '80px', style: style, className: className, children: children }));
87
+ });
88
+ Form.useForm = function useForm() {
89
+ const [f] = React.useState(createFormInstance);
90
+ return [f];
74
91
  };
75
- Form.List = (({ children }) => children || null);
76
- Form.Provider = (({ children }) => children);
77
- /**
78
- * Form.useWatch —— 监听某字段值(简易实现:挂载时从 CE 取一次值)。
79
- * 用于「读初始/回填值」场景;实时联动需要外层 <Form onValuesChange> 驱动,当前项目未用到联动。
80
- */
81
92
  Form.useWatch = function useWatch(namePath, form) {
82
93
  const name = Array.isArray(namePath) ? namePath.join('.') : namePath;
83
94
  const [val, setVal] = React.useState(undefined);
84
95
  React.useEffect(() => {
85
96
  if (!form)
86
97
  return;
87
- let active = true;
88
- form.getFieldsValue().then((all) => {
89
- if (active && all && name in all)
90
- setVal(all[name]);
91
- });
92
- return () => {
93
- active = false;
94
- };
98
+ setVal(form.getFieldValue?.(name));
95
99
  }, [form, name]);
96
100
  return val;
97
101
  };
102
+ Form.List = (({ children }) => children || null);
103
+ Form.Provider = (({ children }) => children || null);
104
+ export function FormItem(props) {
105
+ const { name, label, rules, required, labelCol, noStyle, children, style, className } = props;
106
+ const propName = Array.isArray(name) ? name.join('.') : name;
107
+ return (_jsx(HbFormItem, { prop: propName || '', label: typeof label === 'string' ? label : '', rules: rules || [], required: required, labelCol: labelCol?.span, style: style, className: className, children: children }));
108
+ }
109
+ Form.Item = FormItem;
@@ -1,11 +1,14 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
+ import React from 'react';
2
3
  export function Layout(props) {
3
4
  const { children, style, className, hasSider } = props;
4
- // 默认 flex 行布局(sider + 右侧);若 className 含 flex-col 则尊重它(垂直)
5
- const isVertical = className && /flex-col|flex-direction:\s*column/.test(className);
5
+ // antd 语义:含 Sider 直接子→水平(row);否则→垂直(column, Header 顶部)。
6
+ // 之前硬编码 row 导致 scrm 外层 Layout(Header+下层)横排,Header 跑到左侧。
7
+ const childrenArr = React.Children.toArray(children);
8
+ const siderDetected = hasSider || childrenArr.some(c => c?.type === Layout.Sider);
6
9
  const flexStyle = {
7
10
  display: 'flex',
8
- flexDirection: isVertical ? 'column' : 'row',
11
+ flexDirection: siderDetected ? 'row' : 'column',
9
12
  flex: 1,
10
13
  minHeight: 0,
11
14
  width: '100%',
package/lib/index.d.ts CHANGED
@@ -217,3 +217,40 @@ export { Breadcrumb } from './components/Breadcrumb';
217
217
  export { Typography } from './components/Typography';
218
218
  export { QRCode } from './components/QRCode';
219
219
  export { TimePicker } from './components/TimePicker';
220
+ export type { FormInstance, FormProps } from './components/Form';
221
+ export type { ColumnType as ColumnGroupType } from './components/Table';
222
+ export type TablePaginationConfig = {
223
+ current?: number;
224
+ pageSize?: number;
225
+ total?: number;
226
+ showSizeChanger?: boolean;
227
+ pageSizeOptions?: string[];
228
+ [k: string]: any;
229
+ };
230
+ export type MenuItemType = {
231
+ key: React.Key;
232
+ label?: React.ReactNode;
233
+ children?: MenuItemType[];
234
+ disabled?: boolean;
235
+ type?: string;
236
+ [k: string]: any;
237
+ };
238
+ export type DescriptionsProps = {
239
+ title?: React.ReactNode;
240
+ bordered?: boolean;
241
+ column?: number | object;
242
+ size?: 'default' | 'middle' | 'small';
243
+ layout?: 'horizontal' | 'vertical';
244
+ colon?: boolean;
245
+ [k: string]: any;
246
+ };
247
+ /**
248
+ * antd theme 命名空间兼容。
249
+ * react-antd 用 CSS 变量驱动主题,无 token 算法;useToken 返回空 token 兜底,
250
+ * 避免业务代码 `const { token } = theme.useToken()` 崩溃(取到的 token.* 为 undefined)。
251
+ */
252
+ export declare const theme: {
253
+ useToken: () => {
254
+ token: Record<string, any>;
255
+ };
256
+ };
package/lib/index.js CHANGED
@@ -121,3 +121,11 @@ export { Breadcrumb } from './components/Breadcrumb';
121
121
  export { Typography } from './components/Typography';
122
122
  export { QRCode } from './components/QRCode';
123
123
  export { TimePicker } from './components/TimePicker';
124
+ /**
125
+ * antd theme 命名空间兼容。
126
+ * react-antd 用 CSS 变量驱动主题,无 token 算法;useToken 返回空 token 兜底,
127
+ * 避免业务代码 `const { token } = theme.useToken()` 崩溃(取到的 token.* 为 undefined)。
128
+ */
129
+ export const theme = {
130
+ useToken: () => ({ token: {} }),
131
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@huibo-ui/react-antd",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "antd 兼容层 — 接收 antd 原生 props,内部转换为 huibo-ui,实现丝滑平替",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -30,4 +30,4 @@
30
30
  "publishConfig": {
31
31
  "access": "public"
32
32
  }
33
- }
33
+ }