You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
65 lines
2.1 KiB
65 lines
2.1 KiB
4 years ago
|
import { useRef, useMemo } from 'react';
|
||
|
import { useForm as useRcForm, FormInstance as RcFormInstance } from 'rc-field-form';
|
||
|
import scrollIntoView from 'scroll-into-view-if-needed';
|
||
|
import { ScrollOptions, NamePath, InternalNamePath } from '../interface';
|
||
|
import { toArray, getFieldId } from '../util';
|
||
|
|
||
|
export interface FormInstance extends RcFormInstance {
|
||
|
scrollToField: (name: NamePath, options?: ScrollOptions) => void;
|
||
|
/** This is an internal usage. Do not use in your prod */
|
||
|
__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('_');
|
||
|
}
|
||
|
|
||
|
export default function useForm(form?: FormInstance): [FormInstance] {
|
||
|
const [rcForm] = useRcForm();
|
||
|
const itemsRef = useRef<Record<string, React.ReactElement>>({});
|
||
|
|
||
|
const wrapForm: FormInstance = useMemo(
|
||
|
() =>
|
||
|
form || {
|
||
|
...rcForm,
|
||
|
__INTERNAL__: {
|
||
|
itemRef: (name: InternalNamePath) => (node: React.ReactElement) => {
|
||
|
const namePathStr = toNamePathStr(name);
|
||
|
if (node) {
|
||
|
itemsRef.current[namePathStr] = node;
|
||
|
} else {
|
||
|
delete itemsRef.current[namePathStr];
|
||
|
}
|
||
|
},
|
||
|
},
|
||
|
scrollToField: (name: string, options: ScrollOptions = {}) => {
|
||
|
const namePath = toArray(name);
|
||
|
const fieldId = getFieldId(namePath, wrapForm.__INTERNAL__.name);
|
||
|
const node: HTMLElement | null = fieldId ? document.getElementById(fieldId) : null;
|
||
|
|
||
|
if (node) {
|
||
|
scrollIntoView(node, {
|
||
|
scrollMode: 'if-needed',
|
||
|
block: 'nearest',
|
||
|
...options,
|
||
|
});
|
||
|
}
|
||
|
},
|
||
|
getFieldInstance: (name: string) => {
|
||
|
const namePathStr = toNamePathStr(name);
|
||
|
return itemsRef.current[namePathStr];
|
||
|
},
|
||
|
},
|
||
|
[form, rcForm],
|
||
|
);
|
||
|
|
||
|
return [wrapForm];
|
||
|
}
|