diff --git a/src/FormHelper/autoFocus.tsx b/src/FormHelper/autoFocus.tsx new file mode 100644 index 0000000..a807fc8 --- /dev/null +++ b/src/FormHelper/autoFocus.tsx @@ -0,0 +1,40 @@ +import React, { MutableRefObject, useState } from 'react'; + +type RefOrDomOrId = HTMLElement | string | React.Ref; + +type F = any; + +const getDom = (refOrDomOrId: RefOrDomOrId) => + typeof refOrDomOrId === 'string' + ? document.querySelector(`#${refOrDomOrId}`) + : (refOrDomOrId as MutableRefObject)?.current || refOrDomOrId; + +// https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/input#input_%E7%B1%BB%E5%9E%8B +const disabled = ':not([disabled]):not([readonly])'; +const queryInputTypes = + ['text', 'password', 'search', 'tel', 'url', 'number', 'email', ''] + .map(item => `input[type="${item}"]${disabled}`) + .join(', ') + `, input:not([type])${disabled}, textarea${disabled}`; +const setAutoFocus = (refOrDomOrId: RefOrDomOrId) => { + const dom = getDom(refOrDomOrId); + if (!dom) return; + const input = dom.querySelector?.(queryInputTypes); + if (!input?.focus) return; + input.focus(); + return true; +}; + +/** + * Passing a ref, id, or DOM element to obtain and set the focus state of the first non-disabled and non-readonly input or textarea. + * @param {RefOrDomOrId} refOrDomOrId - 支持类型 HTMLElement | string | React.Ref + * @returns void + */ +export const useAutoFocus = (refOrDomOrId?: RefOrDomOrId) => { + const [focused, setFocused] = useState(false); + React.useEffect(() => { + if (focused || !refOrDomOrId) return; + const setRes = setAutoFocus(refOrDomOrId); + if (!setRes) return; + setFocused(true); + }, [refOrDomOrId, focused, setFocused]); +}; diff --git a/src/FormHelper/demos/DomUseAutoFocus.tsx b/src/FormHelper/demos/DomUseAutoFocus.tsx new file mode 100644 index 0000000..fa52d1f --- /dev/null +++ b/src/FormHelper/demos/DomUseAutoFocus.tsx @@ -0,0 +1,26 @@ +import { useAutoFocus } from '@yuntijs/ui'; +import React, { useEffect, useState } from 'react'; + +import FormContent from './components/FormContent'; +import RenderContainer from './components/RenderContainer'; + +type IDomUseAutoFocus = Record; + +const FormContentWrapper = () => { + const [fomDom, setFormDom] = useState(null); + useEffect(() => { + //
in FormContent + setFormDom(document.querySelector('#form123') as HTMLElement); + }, []); + useAutoFocus(fomDom); + return ; +}; + +const DomUseAutoFocus: React.FC = () => { + return ( + + + + ); +}; +export default DomUseAutoFocus; diff --git a/src/FormHelper/demos/FormHelperDemo.tsx b/src/FormHelper/demos/FormHelperDemo.tsx new file mode 100644 index 0000000..89493e5 --- /dev/null +++ b/src/FormHelper/demos/FormHelperDemo.tsx @@ -0,0 +1,22 @@ +import { FormHelper } from '@yuntijs/ui'; +import { Button, Form, Input } from 'antd'; +import React, { useState } from 'react'; + +const Test = () => { + const [open, setOpen] = useState(false); + return ( + <> + + {open && ( + + + + + + + + )} + + ); +}; +export default Test; diff --git a/src/FormHelper/demos/HocFormHelper.tsx b/src/FormHelper/demos/HocFormHelper.tsx new file mode 100644 index 0000000..dea00c5 --- /dev/null +++ b/src/FormHelper/demos/HocFormHelper.tsx @@ -0,0 +1,15 @@ +import { withFormHelper } from '@yuntijs/ui'; +import React from 'react'; + +import FormContent from './components/FormContent'; +import RenderContainer from './components/RenderContainer'; + +const HocFormHelper: React.FC> = () => { + const FormContentWrapper = withFormHelper()(FormContent); + return ( + + + + ); +}; +export default HocFormHelper; diff --git a/src/FormHelper/demos/IdUseAutoFocus.tsx b/src/FormHelper/demos/IdUseAutoFocus.tsx new file mode 100644 index 0000000..4088fe3 --- /dev/null +++ b/src/FormHelper/demos/IdUseAutoFocus.tsx @@ -0,0 +1,20 @@ +import { useAutoFocus } from '@yuntijs/ui'; +import React from 'react'; + +import FormContent from './components/FormContent'; +import RenderContainer from './components/RenderContainer'; + +const FormContentWrapper = () => { + //
in FormContent + useAutoFocus('form123'); + return ; +}; + +const IdUseAutoFocus: React.FC> = () => { + return ( + + + + ); +}; +export default IdUseAutoFocus; diff --git a/src/FormHelper/demos/RefUseAutoFocus.tsx b/src/FormHelper/demos/RefUseAutoFocus.tsx new file mode 100644 index 0000000..0c21027 --- /dev/null +++ b/src/FormHelper/demos/RefUseAutoFocus.tsx @@ -0,0 +1,27 @@ +import { useAutoFocus } from '@yuntijs/ui'; +import React, { useRef } from 'react'; + +import FormContent from './components/FormContent'; +import RenderContainer from './components/RenderContainer'; + +interface IRefUseAutoFocus {} + +const FormContentWrapper = () => { + const divRef = useRef(null); + useAutoFocus(divRef); + // ⚠️注意 The form instance obtained through the ref of the Ant Design's Form component does not take effect. You need to pass the ref of native HTML tags like div or span. + return ( +
+ +
+ ); +}; + +const RefUseAutoFocus: React.FC = props => { + return ( + + + + ); +}; +export default RefUseAutoFocus; diff --git a/src/FormHelper/demos/components/FormContent.tsx b/src/FormHelper/demos/components/FormContent.tsx new file mode 100644 index 0000000..69037f4 --- /dev/null +++ b/src/FormHelper/demos/components/FormContent.tsx @@ -0,0 +1,36 @@ +import { Form, Input, InputNumber, Radio, Select, Switch } from 'antd'; +import React from 'react'; + +interface IFormContent {} + +const FormContent: React.FC = props => { + return ( + +
+ + +
+ + + + + + + + 123 + + + + + + + + + + + + ); +}; +export default FormContent; diff --git a/src/FormHelper/demos/components/RenderContainer.tsx b/src/FormHelper/demos/components/RenderContainer.tsx new file mode 100644 index 0000000..bf1f09a --- /dev/null +++ b/src/FormHelper/demos/components/RenderContainer.tsx @@ -0,0 +1,19 @@ +import { Button } from 'antd'; +import React, { useState } from 'react'; + +interface IRenderContainer { + children: React.ReactNode; +} + +const RenderContainer: React.FC = props => { + const [open, setOpen] = useState(false); + return ( + <> + +
+
+ {open && props.children} + + ); +}; +export default RenderContainer; diff --git a/src/FormHelper/index.md b/src/FormHelper/index.md new file mode 100644 index 0000000..c9a4585 --- /dev/null +++ b/src/FormHelper/index.md @@ -0,0 +1,53 @@ +--- +nav: Components +group: Utils +title: FormHelper +description: Enhanced component for Form +--- + +# FormHelper + +Form enhancement component, currently supporting: + +- autoFocus: automatically selecting the first non-disabled and non-readonly input or textarea of the form when focused. + +## Usage + +### React component FormHelper + +Wrap the Form component with FormHelper. Supported attributes include: + +| props | description | default | required | +| --------- | -------------------------------------------------------------------------------------------------------------- | ------- | -------- | +| autoFocus | Automatically select the focus state of the first non-disabled and non-readonly input or textarea in the form. | true | false | +| id | The id of the outer wrapping div. | - | false | +| className | The className of the outer wrapping div. | - | false | +| style | The style of the outer wrapping div. | - | false | + +#### usage + + + +### withFormHelper - Higher-Order Component (HOC) + +As a higher-order component, withFormHelper supports the same parameters as the React component FormHelper. + +#### usage + + + +### useAutoFocus - hooks + +Supports passing a ref, id, or DOM element as a parameter. + +#### Example of a ref parameter: + + + +#### Example of a id parameter: + + + +#### Example of a dom parameter: + + diff --git a/src/FormHelper/index.tsx b/src/FormHelper/index.tsx new file mode 100644 index 0000000..50a03f1 --- /dev/null +++ b/src/FormHelper/index.tsx @@ -0,0 +1,50 @@ +import React, { ComponentType, FC, useRef } from 'react'; + +import { useAutoFocus } from './autoFocus'; + +export { useAutoFocus } from './autoFocus'; +// Component +export type FormHelperProps = { + children: React.ReactNode; + autoFocus?: boolean; + id?: string; + className?: string; + style?: React.CSSProperties; +}; + +const FormHelper: React.FC = props => { + const { autoFocus = true } = props; + + const ref = useRef(null); + useAutoFocus(autoFocus ? ref : undefined); + return ( +
+ {props.children} +
+ ); +}; + +// HOC +type FormHelperConfig = Omit; + +export const withFormHelper = + (formHelperConfig?: FormHelperConfig) => +

>(WrappedComponent: ComponentType

): FC

=> { + const HocComponent: FC

= props => { + return ( + + + + ); + }; + const displayName = WrappedComponent.displayName || WrappedComponent.name || 'Component'; + HocComponent.displayName = `withFormHelper(${displayName})`; + return HocComponent; + }; + +export { FormHelper }; diff --git a/src/index.ts b/src/index.ts index 766968b..81d1924 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,6 +11,7 @@ export * from './Card'; export * from './Descriptions'; export * from './Divider'; export * from './Drawer'; +export * from './FormHelper'; export * from './Modal'; // ~ antd @@ -64,7 +65,7 @@ export { type FormRule, Grid, Image, - ImageProps, + type ImageProps, Input, InputNumber, type InputNumberProps,