parent
50452c49c0
commit
2d6e309534
2 changed files with 240 additions and 0 deletions
@ -0,0 +1,199 @@ |
||||
import { SxProps, Theme } from '@mui/material'; |
||||
import { |
||||
forwardRef, |
||||
useCallback, |
||||
useImperativeHandle, |
||||
useMemo, |
||||
useRef, |
||||
useState, |
||||
} from 'react'; |
||||
|
||||
import INPUT_TYPES from '../lib/consts/INPUT_TYPES'; |
||||
|
||||
import ContainedButton from './ContainedButton'; |
||||
import FlexBox from './FlexBox'; |
||||
import Grid from './Grid'; |
||||
import InputWithRef, { InputForwardedRefContent } from './InputWithRef'; |
||||
import MessageGroup, { MessageGroupForwardedRefContent } from './MessageGroup'; |
||||
import OutlinedInputWithLabel from './OutlinedInputWithLabel'; |
||||
import Spinner from './Spinner'; |
||||
|
||||
const INPUT_ROOT_SX: SxProps<Theme> = { width: '100%' }; |
||||
const MESSAGE_KEY: GateFormMessageKey = { accessError: 'accessError' }; |
||||
|
||||
const GateForm = forwardRef<GateFormForwardedRefContent, GateFormProps>( |
||||
( |
||||
{ |
||||
allowSubmit: isAllowSubmit = true, |
||||
gridProps: { |
||||
columns: gridColumns = { xs: 1, sm: 2 }, |
||||
layout, |
||||
spacing: gridSpacing = '1em', |
||||
...restGridProps |
||||
} = {}, |
||||
identifierLabel, |
||||
identifierOutlinedInputWithLabelProps: { |
||||
formControlProps: identifierFormControlProps = {}, |
||||
...restIdentifierOutlinedInputWithLabelProps |
||||
} = {}, |
||||
onSubmit, |
||||
onSubmitAppend, |
||||
passphraseLabel, |
||||
passphraseOutlinedInputWithLabelProps: { |
||||
formControlProps: passphraseFormControlProps = {}, |
||||
inputProps: passphraseInputProps, |
||||
...restPassphraseOutlinedInputWithLabelProps |
||||
} = {}, |
||||
submitLabel, |
||||
}, |
||||
ref, |
||||
) => { |
||||
const { sx: identifierSx, ...restIdentifierFormControlProps } = |
||||
identifierFormControlProps; |
||||
const { sx: passphraseSx, ...restPassphraseFormControlProps } = |
||||
passphraseFormControlProps; |
||||
|
||||
const inputIdentifierRef = useRef<InputForwardedRefContent<'string'>>({}); |
||||
const inputPassphraseRef = useRef<InputForwardedRefContent<'string'>>({}); |
||||
const messageGroupRef = useRef<MessageGroupForwardedRefContent>({}); |
||||
|
||||
const [isSubmitting, setIsSubmitting] = useState<boolean>(false); |
||||
const [isShowMessageGroup, setIsShowMessageGroup] = |
||||
useState<boolean>(false); |
||||
|
||||
const setMessage: GateFormMessageSetter = useCallback( |
||||
(message?, key = 'accessError') => { |
||||
messageGroupRef.current.setMessage?.call( |
||||
null, |
||||
MESSAGE_KEY[key], |
||||
message, |
||||
); |
||||
}, |
||||
[], |
||||
); |
||||
|
||||
const messageGroupSxDisplay = useMemo( |
||||
() => (isShowMessageGroup ? undefined : 'none'), |
||||
[isShowMessageGroup], |
||||
); |
||||
const submitHandler: ContainedButtonProps['onClick'] = useMemo( |
||||
() => |
||||
onSubmit ?? |
||||
((...args) => { |
||||
setMessage(); |
||||
setIsSubmitting(true); |
||||
onSubmitAppend?.call( |
||||
null, |
||||
inputIdentifierRef.current, |
||||
inputPassphraseRef.current, |
||||
setMessage, |
||||
setIsSubmitting, |
||||
messageGroupRef.current, |
||||
...args, |
||||
); |
||||
}), |
||||
[onSubmit, onSubmitAppend, setMessage], |
||||
); |
||||
const submitElement = useMemo( |
||||
() => |
||||
isSubmitting ? ( |
||||
<Spinner sx={{ marginTop: 0 }} /> |
||||
) : ( |
||||
<FlexBox row sx={{ justifyContent: 'flex-end' }}> |
||||
<ContainedButton onClick={submitHandler}> |
||||
{submitLabel} |
||||
</ContainedButton> |
||||
</FlexBox> |
||||
), |
||||
[isSubmitting, submitHandler, submitLabel], |
||||
); |
||||
const submitGrid = useMemo( |
||||
() => |
||||
isAllowSubmit |
||||
? { |
||||
children: submitElement, |
||||
sm: 2, |
||||
} |
||||
: undefined, |
||||
[isAllowSubmit, submitElement], |
||||
); |
||||
|
||||
useImperativeHandle(ref, () => ({ |
||||
get: () => ({ |
||||
identifier: inputIdentifierRef.current.getValue?.call(null) ?? '', |
||||
passphrase: inputPassphraseRef.current.getValue?.call(null) ?? '', |
||||
}), |
||||
messageGroup: { |
||||
...messageGroupRef.current, |
||||
}, |
||||
setIsSubmitting: (value) => { |
||||
setIsSubmitting(value); |
||||
}, |
||||
})); |
||||
|
||||
return ( |
||||
<Grid |
||||
columns={gridColumns} |
||||
layout={{ |
||||
'credential-identifier': { |
||||
children: ( |
||||
<InputWithRef |
||||
input={ |
||||
<OutlinedInputWithLabel |
||||
formControlProps={{ |
||||
...restIdentifierFormControlProps, |
||||
sx: { ...INPUT_ROOT_SX, ...identifierSx }, |
||||
}} |
||||
label={identifierLabel} |
||||
{...restIdentifierOutlinedInputWithLabelProps} |
||||
/> |
||||
} |
||||
ref={inputIdentifierRef} |
||||
/> |
||||
), |
||||
}, |
||||
'credential-passphrase': { |
||||
children: ( |
||||
<InputWithRef |
||||
input={ |
||||
<OutlinedInputWithLabel |
||||
formControlProps={{ |
||||
...restPassphraseFormControlProps, |
||||
sx: { ...INPUT_ROOT_SX, ...passphraseSx }, |
||||
}} |
||||
inputProps={{ |
||||
type: INPUT_TYPES.password, |
||||
...passphraseInputProps, |
||||
}} |
||||
label={passphraseLabel} |
||||
{...restPassphraseOutlinedInputWithLabelProps} |
||||
/> |
||||
} |
||||
ref={inputPassphraseRef} |
||||
/> |
||||
), |
||||
}, |
||||
'credential-submit': submitGrid, |
||||
'credential-message-group': { |
||||
children: ( |
||||
<MessageGroup |
||||
onSet={(length) => { |
||||
setIsShowMessageGroup(length > 0); |
||||
}} |
||||
ref={messageGroupRef} |
||||
/> |
||||
), |
||||
sm: 2, |
||||
sx: { display: messageGroupSxDisplay }, |
||||
}, |
||||
}} |
||||
spacing={gridSpacing} |
||||
{...restGridProps} |
||||
/> |
||||
); |
||||
}, |
||||
); |
||||
|
||||
GateForm.displayName = 'GateForm'; |
||||
|
||||
export default GateForm; |
@ -0,0 +1,41 @@ |
||||
type GateFormMessageKey = { accessError: string }; |
||||
|
||||
type GateFormMessageSetter = ( |
||||
message?: import('../components/MessageBox').Message, |
||||
key?: keyof GateFormMessageKey, |
||||
) => void; |
||||
type GateFormSubmittingSetter = (value: boolean) => void; |
||||
|
||||
type GateFormSubmitHandler = ( |
||||
identifierContent: import('../components/InputWithRef').InputForwardedRefContent<'string'>, |
||||
passphraseContent: import('../components/InputWithRef').InputForwardedRefContent<'string'>, |
||||
setMessage: GateFormMessageSetter, |
||||
setIsSubmitting: GateFormSubmittingSetter, |
||||
messageGroupContent: import('../components/MessageGroup').MessageGroupForwardedRefContent, |
||||
...args: Parameters<ContainedButtonProps['onClick']> |
||||
) => void; |
||||
|
||||
type GateFormOptionalProps = { |
||||
allowSubmit?: boolean; |
||||
gridProps?: Partial<GridProps>; |
||||
identifierOutlinedInputWithLabelProps?: Partial< |
||||
import('../components/OutlinedInputWithLabel').OutlinedInputWithLabelProps |
||||
>; |
||||
onSubmit?: ContainedButtonProps['onClick']; |
||||
onSubmitAppend?: GateFormSubmitHandler; |
||||
passphraseOutlinedInputWithLabelProps?: Partial< |
||||
import('../components/OutlinedInputWithLabel').OutlinedInputWithLabelProps |
||||
>; |
||||
}; |
||||
|
||||
type GateFormProps = GateFormOptionalProps & { |
||||
identifierLabel: import('../components/OutlinedInputWithLabel').OutlinedInputWithLabelProps['label']; |
||||
passphraseLabel: import('../components/OutlinedInputWithLabel').OutlinedInputWithLabelProps['label']; |
||||
submitLabel: import('react').ReactNode; |
||||
}; |
||||
|
||||
type GateFormForwardedRefContent = { |
||||
get?: () => { identifier: string; passphrase: string }; |
||||
messageGroup?: import('../components/MessageGroup').MessageGroupForwardedRefContent; |
||||
setIsSubmitting?: GateFormSubmittingSetter; |
||||
}; |
Loading…
Reference in new issue