import { Box, BoxProps, SxProps, Theme } from '@mui/material'; import { forwardRef, useImperativeHandle, useMemo, useRef } 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 } from '../lib/test_input'; import useFormUtils from '../hooks/useFormUtils'; import useProtectedState from '../hooks/useProtectedState'; const INPUT_ROOT_SX: SxProps = { width: '100%' }; const INPUT_ID_PREFIX_GATE = 'gate-input'; const INPUT_ID_GATE_ID = `${INPUT_ID_PREFIX_GATE}-credential-id`; const INPUT_ID_GATE_PASSPHRASE = `${INPUT_ID_PREFIX_GATE}-credential-passphrase`; const MSG_ID_GATE_ACCESS = 'access'; const GateForm = forwardRef( ( { formContainer: isFormContainer = true, gridProps: { columns: gridColumns = { xs: 1, sm: 2 }, layout, spacing: gridSpacing = '1em', ...restGridProps } = {}, identifierId = INPUT_ID_GATE_ID, identifierInputTestBatchBuilder: buildIdentifierInputTestBatch = buildPeacefulStringTestBatch, identifierLabel, identifierOutlinedInputWithLabelProps: { formControlProps: identifierFormControlProps = {}, inputProps: identifierInputProps, ...restIdentifierOutlinedInputWithLabelProps } = {}, onIdentifierBlurAppend, onSubmit, onSubmitAppend, passphraseId = INPUT_ID_GATE_PASSPHRASE, passphraseLabel, passphraseOutlinedInputWithLabelProps: { formControlProps: passphraseFormControlProps = {}, inputProps: passphraseInputProps, ...restPassphraseOutlinedInputWithLabelProps } = {}, submitLabel, // Props that depend on others. allowSubmit: isAllowSubmit = isFormContainer, }, ref, ) => { const { sx: identifierSx, ...restIdentifierFormControlProps } = identifierFormControlProps; const { sx: passphraseSx, ...restPassphraseFormControlProps } = passphraseFormControlProps; const inputIdentifierRef = useRef>({}); const inputPassphraseRef = useRef>({}); const messageGroupRef = useRef({}); const [isSubmitting, setIsSubmitting] = useProtectedState(false); const formUtils = useFormUtils( [INPUT_ID_GATE_ID, INPUT_ID_GATE_PASSPHRASE], messageGroupRef, ); const { buildFinishInputTestBatchFunction, buildInputFirstRenderFunction, buildInputUnmountFunction, isFormInvalid, setMessage, } = formUtils; const submitHandler: DivFormEventHandler = useMemo( () => onSubmit ?? ((...args) => { const { 0: event } = args; event.preventDefault(); setMessage(MSG_ID_GATE_ACCESS); setIsSubmitting(true); const { target } = event; const { elements } = target as HTMLFormElement; const { value: identifierValue } = elements.namedItem( INPUT_ID_GATE_ID, ) as HTMLInputElement; const { value: passphraseValue } = elements.namedItem( INPUT_ID_GATE_PASSPHRASE, ) as HTMLInputElement; onSubmitAppend?.call( null, identifierValue, passphraseValue, (message?) => { setMessage(MSG_ID_GATE_ACCESS, message); }, setIsSubmitting, ...args, ); }), [onSubmit, onSubmitAppend, setIsSubmitting, setMessage], ); const submitElement = useMemo( () => isSubmitting ? ( ) : ( {submitLabel} ), [isFormInvalid, isSubmitting, submitLabel], ); const submitAreaGridLayout = useMemo(() => { const result: GridLayout = {}; if (isAllowSubmit) { result['gate-cell-message-group'] = { children: ( ), sm: 2, }; result['gate-cell-submit'] = { children: submitElement, sm: 2 }; } return result; }, [isAllowSubmit, submitElement]); const containerProps = useMemo(() => { const result: BoxProps = {}; if (isFormContainer) { result.component = 'form'; result.onSubmit = submitHandler; } return result; }, [isFormContainer, submitHandler]); useImperativeHandle(ref, () => ({ get: () => ({ identifier: inputIdentifierRef.current.getValue?.call(null) ?? '', passphrase: inputPassphraseRef.current.getValue?.call(null) ?? '', }), messageGroup: { ...messageGroupRef.current, }, setIsSubmitting: (value) => { setIsSubmitting(value); }, })); return ( } inputTestBatch={buildIdentifierInputTestBatch( identifierLabel, () => { setMessage(identifierId); }, { onFinishBatch: buildFinishInputTestBatchFunction(identifierId), }, (message) => { setMessage(identifierId, { children: message }); }, )} onBlurAppend={(...args) => { onIdentifierBlurAppend?.call(null, ...args); }} onFirstRender={buildInputFirstRenderFunction(identifierId)} onUnmount={buildInputUnmountFunction(identifierId)} ref={inputIdentifierRef} required /> ), }, 'gate-input-cell-credential-passphrase': { children: ( } inputTestBatch={buildPeacefulStringTestBatch( passphraseLabel, () => { setMessage(passphraseId); }, { onFinishBatch: buildFinishInputTestBatchFunction(passphraseId), }, (message) => { setMessage(passphraseId, { children: message, }); }, )} onFirstRender={buildInputFirstRenderFunction(passphraseId)} onUnmount={buildInputUnmountFunction(passphraseId)} ref={inputPassphraseRef} required /> ), }, ...submitAreaGridLayout, }} spacing={gridSpacing} {...restGridProps} /> ); }, ); GateForm.displayName = 'GateForm'; export { INPUT_ID_GATE_ID, INPUT_ID_GATE_PASSPHRASE }; export default GateForm;