2024-04-08 14:04:08 +08:00
|
|
|
import * as React from 'react';
|
2022-05-07 14:31:54 +08:00
|
|
|
import type { FormInstance as RcFormInstance } from 'rc-field-form';
|
|
|
|
import { useForm as useRcForm } from 'rc-field-form';
|
2020-06-05 18:06:52 +08:00
|
|
|
import scrollIntoView from 'scroll-into-view-if-needed';
|
2024-05-20 19:41:30 +08:00
|
|
|
import { getDOM } from 'rc-util/lib/Dom/findDOMNode';
|
2024-04-08 14:04:08 +08:00
|
|
|
|
2022-06-22 14:57:09 +08:00
|
|
|
import type { InternalNamePath, NamePath, ScrollOptions } from '../interface';
|
|
|
|
import { getFieldId, toArray } from '../util';
|
2020-06-05 18:06:52 +08:00
|
|
|
|
2020-07-31 16:53:39 +08:00
|
|
|
export interface FormInstance<Values = any> extends RcFormInstance<Values> {
|
2020-06-05 18:06:52 +08:00
|
|
|
scrollToField: (name: NamePath, options?: ScrollOptions) => void;
|
2022-09-30 14:26:41 +08:00
|
|
|
/** @internal: This is an internal usage. Do not use in your prod */
|
2020-06-05 18:06:52 +08:00
|
|
|
__INTERNAL__: {
|
|
|
|
/** No! Do not use this in your code! */
|
|
|
|
name?: string;
|
|
|
|
/** No! Do not use this in your code! */
|
|
|
|
itemRef: (name: InternalNamePath) => (node: React.ReactElement) => void;
|
|
|
|
};
|
|
|
|
getFieldInstance: (name: NamePath) => any;
|
|
|
|
}
|
|
|
|
|
|
|
|
function toNamePathStr(name: NamePath) {
|
|
|
|
const namePath = toArray(name);
|
|
|
|
return namePath.join('_');
|
|
|
|
}
|
|
|
|
|
2024-04-17 17:56:29 +08:00
|
|
|
function getFieldDOMNode(name: NamePath, wrapForm: FormInstance) {
|
|
|
|
const field = wrapForm.getFieldInstance(name);
|
2024-05-20 19:41:30 +08:00
|
|
|
const fieldDom = getDOM(field);
|
2024-04-17 17:56:29 +08:00
|
|
|
|
2024-05-20 19:41:30 +08:00
|
|
|
if (fieldDom) {
|
|
|
|
return fieldDom;
|
2024-04-17 17:56:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
const fieldId = getFieldId(toArray(name), wrapForm.__INTERNAL__.name);
|
|
|
|
if (fieldId) {
|
|
|
|
return document.getElementById(fieldId);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-31 16:53:39 +08:00
|
|
|
export default function useForm<Values = any>(form?: FormInstance<Values>): [FormInstance<Values>] {
|
2020-06-05 18:06:52 +08:00
|
|
|
const [rcForm] = useRcForm();
|
2020-09-16 11:43:55 +08:00
|
|
|
const itemsRef = React.useRef<Record<string, React.ReactElement>>({});
|
2020-06-05 18:06:52 +08:00
|
|
|
|
2020-09-16 11:43:55 +08:00
|
|
|
const wrapForm: FormInstance<Values> = React.useMemo(
|
2020-06-05 18:06:52 +08:00
|
|
|
() =>
|
2021-06-03 09:36:04 +08:00
|
|
|
form ?? {
|
2020-06-05 18:06:52 +08:00
|
|
|
...rcForm,
|
|
|
|
__INTERNAL__: {
|
|
|
|
itemRef: (name: InternalNamePath) => (node: React.ReactElement) => {
|
|
|
|
const namePathStr = toNamePathStr(name);
|
|
|
|
if (node) {
|
|
|
|
itemsRef.current[namePathStr] = node;
|
|
|
|
} else {
|
|
|
|
delete itemsRef.current[namePathStr];
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
2021-06-03 09:36:04 +08:00
|
|
|
scrollToField: (name: NamePath, options: ScrollOptions = {}) => {
|
2024-04-17 17:56:29 +08:00
|
|
|
const node = getFieldDOMNode(name, wrapForm);
|
2020-06-05 18:06:52 +08:00
|
|
|
|
|
|
|
if (node) {
|
|
|
|
scrollIntoView(node, {
|
|
|
|
scrollMode: 'if-needed',
|
|
|
|
block: 'nearest',
|
|
|
|
...options,
|
2022-12-07 13:56:18 +08:00
|
|
|
} as any);
|
2020-06-05 18:06:52 +08:00
|
|
|
}
|
|
|
|
},
|
2021-06-03 09:36:04 +08:00
|
|
|
getFieldInstance: (name: NamePath) => {
|
2020-06-05 18:06:52 +08:00
|
|
|
const namePathStr = toNamePathStr(name);
|
|
|
|
return itemsRef.current[namePathStr];
|
|
|
|
},
|
|
|
|
},
|
|
|
|
[form, rcForm],
|
|
|
|
);
|
|
|
|
|
|
|
|
return [wrapForm];
|
|
|
|
}
|