From 2d6e30953470242bc0ee2b4042524c828dff0bf8 Mon Sep 17 00:00:00 2001 From: Tsu-ba-me Date: Mon, 14 Nov 2022 20:42:53 -0500 Subject: [PATCH] fix(striker-ui): add GateForm --- striker-ui/components/GateForm.tsx | 199 +++++++++++++++++++++++++++++ striker-ui/types/GateForm.d.ts | 41 ++++++ 2 files changed, 240 insertions(+) create mode 100644 striker-ui/components/GateForm.tsx create mode 100644 striker-ui/types/GateForm.d.ts diff --git a/striker-ui/components/GateForm.tsx b/striker-ui/components/GateForm.tsx new file mode 100644 index 00000000..2d1ea860 --- /dev/null +++ b/striker-ui/components/GateForm.tsx @@ -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 = { width: '100%' }; +const MESSAGE_KEY: GateFormMessageKey = { accessError: 'accessError' }; + +const GateForm = forwardRef( + ( + { + 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>({}); + const inputPassphraseRef = useRef>({}); + const messageGroupRef = useRef({}); + + const [isSubmitting, setIsSubmitting] = useState(false); + const [isShowMessageGroup, setIsShowMessageGroup] = + useState(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 ? ( + + ) : ( + + + {submitLabel} + + + ), + [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 ( + + } + ref={inputIdentifierRef} + /> + ), + }, + 'credential-passphrase': { + children: ( + + } + ref={inputPassphraseRef} + /> + ), + }, + 'credential-submit': submitGrid, + 'credential-message-group': { + children: ( + { + setIsShowMessageGroup(length > 0); + }} + ref={messageGroupRef} + /> + ), + sm: 2, + sx: { display: messageGroupSxDisplay }, + }, + }} + spacing={gridSpacing} + {...restGridProps} + /> + ); + }, +); + +GateForm.displayName = 'GateForm'; + +export default GateForm; diff --git a/striker-ui/types/GateForm.d.ts b/striker-ui/types/GateForm.d.ts new file mode 100644 index 00000000..3d576908 --- /dev/null +++ b/striker-ui/types/GateForm.d.ts @@ -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 +) => void; + +type GateFormOptionalProps = { + allowSubmit?: boolean; + gridProps?: Partial; + 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; +};