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'; import { buildPeacefulStringTestBatch, createTestInputFunction, } from '../lib/test_input'; const INPUT_ROOT_SX: SxProps = { width: '100%' }; const IT_IDS = { identifier: 'identifier', passphrase: 'passphrase', }; const MESSAGE_KEY: GateFormMessageKey = { accessError: 'accessError', identifierInputError: 'identifierInputError', passphraseInputError: 'passphraseInputError', }; const GateForm = forwardRef( ( { allowSubmit: isAllowSubmit = true, gridProps: { columns: gridColumns = { xs: 1, sm: 2 }, layout, spacing: gridSpacing = '1em', ...restGridProps } = {}, identifierLabel, identifierOutlinedInputWithLabelProps: { formControlProps: identifierFormControlProps = {}, inputProps: identifierInputProps, ...restIdentifierOutlinedInputWithLabelProps } = {}, identifierInputTestBatchBuilder: overwriteIdentifierInputTestBatch, onIdentifierBlurAppend, onSubmit, onSubmitAppend, passphraseLabel, passphraseOutlinedInputWithLabelProps: { formControlProps: passphraseFormControlProps = {}, inputProps: passphraseInputProps, ...restPassphraseOutlinedInputWithLabelProps } = {}, submitLabel, }, ref, ) => { const { sx: identifierSx, ...restIdentifierFormControlProps } = identifierFormControlProps; const { sx: passphraseSx, ...restPassphraseFormControlProps } = passphraseFormControlProps; const inputIdentifierRef = useRef>({}); const inputPassphraseRef = useRef>({}); const messageGroupRef = useRef({}); const [isInputIdentifierValid, setIsInputIdentifierValid] = useState(false); const [isInputPassphraseValid, setIsInputPassphraseValid] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false); const setAccessErrorMessage: GateFormMessageSetter = useCallback( (message?) => { messageGroupRef.current.setMessage?.call( null, MESSAGE_KEY.accessError, message, ); }, [], ); const setIdentifierInputErrorMessage: GateFormMessageSetter = useCallback( (message?) => { messageGroupRef.current.setMessage?.call( null, MESSAGE_KEY.identifierInputError, message, ); }, [], ); const setPassphraseInputErrorMessage: GateFormMessageSetter = useCallback( (message?) => { messageGroupRef.current.setMessage?.call( null, MESSAGE_KEY.passphraseInputError, message, ); }, [], ); const messagesGroupSxDisplay = useMemo( () => (isAllowSubmit ? undefined : 'none'), [isAllowSubmit], ); const identifierInputTestBatch = useMemo( () => overwriteIdentifierInputTestBatch?.call( null, setIdentifierInputErrorMessage, inputIdentifierRef.current, ) ?? buildPeacefulStringTestBatch( identifierLabel, () => { setIdentifierInputErrorMessage(); }, { getValue: inputIdentifierRef.current.getValue }, (message) => { setIdentifierInputErrorMessage({ children: message, type: 'warning', }); }, ), [ identifierLabel, overwriteIdentifierInputTestBatch, setIdentifierInputErrorMessage, ], ); const inputTests: InputTestBatches = useMemo( () => ({ [IT_IDS.identifier]: identifierInputTestBatch, [IT_IDS.passphrase]: buildPeacefulStringTestBatch( passphraseLabel, () => { setPassphraseInputErrorMessage(); }, { getValue: inputPassphraseRef.current.getValue }, (message) => { setPassphraseInputErrorMessage({ children: message, type: 'warning', }); }, ), }), [ identifierInputTestBatch, passphraseLabel, setPassphraseInputErrorMessage, ], ); const submitHandler: ContainedButtonProps['onClick'] = useMemo( () => onSubmit ?? ((...args) => { setAccessErrorMessage(); setIsSubmitting(true); onSubmitAppend?.call( null, inputIdentifierRef.current, inputPassphraseRef.current, setAccessErrorMessage, setIsSubmitting, messageGroupRef.current, ...args, ); }), [onSubmit, onSubmitAppend, setAccessErrorMessage], ); const submitElement = useMemo( () => isSubmitting ? ( ) : ( {submitLabel} ), [ isInputIdentifierValid, isInputPassphraseValid, isSubmitting, submitHandler, submitLabel, ], ); const submitGrid = useMemo( () => isAllowSubmit ? { children: submitElement, sm: 2, } : undefined, [isAllowSubmit, submitElement], ); const testInput = useMemo( () => createTestInputFunction(inputTests), [inputTests], ); useImperativeHandle(ref, () => ({ get: () => ({ identifier: inputIdentifierRef.current.getValue?.call(null) ?? '', passphrase: inputPassphraseRef.current.getValue?.call(null) ?? '', }), messageGroup: { ...messageGroupRef.current, }, setIsSubmitting: (value) => { setIsSubmitting(value); }, })); return ( { const { target: { value }, } = event; const valid = testInput({ inputs: { [IT_IDS.identifier]: { value } }, }); setIsInputIdentifierValid(valid); onIdentifierBlurAppend?.call(null, event); }, onFocus: () => { setIdentifierInputErrorMessage(); }, ...identifierInputProps, }} label={identifierLabel} {...restIdentifierOutlinedInputWithLabelProps} /> } ref={inputIdentifierRef} /> ), }, 'credential-passphrase': { children: ( { const valid = testInput({ inputs: { [IT_IDS.passphrase]: { value } }, }); setIsInputPassphraseValid(valid); }, onFocus: () => { setPassphraseInputErrorMessage(); }, type: INPUT_TYPES.password, ...passphraseInputProps, }} label={passphraseLabel} {...restPassphraseOutlinedInputWithLabelProps} /> } ref={inputPassphraseRef} /> ), }, 'credential-message-group': { children: , sm: 2, sx: { display: messagesGroupSxDisplay }, }, 'credential-submit': submitGrid, }} spacing={gridSpacing} {...restGridProps} /> ); }, ); GateForm.displayName = 'GateForm'; export default GateForm;