parent
e1e03ba7f9
commit
0711d8a63a
2 changed files with 159 additions and 0 deletions
@ -0,0 +1,114 @@ |
||||
import { |
||||
List as MUIList, |
||||
ListItem as MUIListItem, |
||||
capitalize, |
||||
} from '@mui/material'; |
||||
import { FC, ReactElement, createElement } from 'react'; |
||||
|
||||
import FlexBox from './FlexBox'; |
||||
import { BodyText, MonoText, SensitiveText } from './Text'; |
||||
|
||||
const renderEntryValueWithPassword: RenderFormValueFunction = (key, entry) => { |
||||
const textElement = /passw/i.test(key) ? SensitiveText : MonoText; |
||||
|
||||
return createElement(textElement, { monospaced: true }, String(entry)); |
||||
}; |
||||
|
||||
const buildEntryList = ({ |
||||
depth = 0, |
||||
entries, |
||||
getEntryLabel, |
||||
getListProps, |
||||
getListItemProps, |
||||
listKey, |
||||
maxDepth, |
||||
renderEntry, |
||||
renderEntryValue, |
||||
}: { |
||||
depth?: number; |
||||
entries: FormEntries; |
||||
getEntryLabel: GetFormEntryLabelFunction; |
||||
getListProps?: GetFormEntriesPropsFunction; |
||||
getListItemProps?: GetFormEntryPropsFunction; |
||||
listKey?: string; |
||||
maxDepth: number; |
||||
renderEntry: RenderFormEntryFunction; |
||||
renderEntryValue: RenderFormValueFunction; |
||||
}): ReactElement => { |
||||
const result: ReactElement[] = []; |
||||
|
||||
Object.entries(entries).forEach(([itemKey, entry]) => { |
||||
const itemId = `form-summary-entry-${itemKey}`; |
||||
|
||||
result.push( |
||||
<MUIListItem |
||||
key={itemId} |
||||
sx={{ paddingLeft: `.${depth * 2}em` }} |
||||
{...getListItemProps?.call(null, itemKey, null, depth)} |
||||
> |
||||
{renderEntry(itemKey, null, getEntryLabel, renderEntryValue)} |
||||
</MUIListItem>, |
||||
); |
||||
|
||||
if (entry !== null && typeof entry === 'object' && depth < maxDepth) { |
||||
result.push( |
||||
buildEntryList({ |
||||
depth: depth + 1, |
||||
entries: entry, |
||||
getEntryLabel, |
||||
listKey: itemKey, |
||||
maxDepth, |
||||
renderEntry, |
||||
renderEntryValue, |
||||
}), |
||||
); |
||||
} |
||||
}); |
||||
|
||||
const listId = `form-summary-list-${listKey ?? 'root'}`; |
||||
|
||||
return ( |
||||
<MUIList |
||||
dense |
||||
key={listId} |
||||
{...getListProps?.call(null, listKey, entries, depth)} |
||||
> |
||||
{result} |
||||
</MUIList> |
||||
); |
||||
}; |
||||
|
||||
const FormSummary = <T extends FormEntries>({ |
||||
entries, |
||||
getEntryLabel = (key) => { |
||||
const spaced = key.replace(/([a-z\d])([A-Z])/g, '$1 $2'); |
||||
const lcased = spaced.toLowerCase(); |
||||
|
||||
return capitalize(lcased); |
||||
}, |
||||
getListProps, |
||||
getListItemProps, |
||||
hasPassword, |
||||
maxDepth = 3, |
||||
renderEntry = (key, entry, getLabel, renderValue) => ( |
||||
<FlexBox fullWidth growFirst row> |
||||
<BodyText>{getLabel(key, entry)}</BodyText> |
||||
{renderValue(key, entry)} |
||||
</FlexBox> |
||||
), |
||||
// Prop(s) that rely on other(s).
|
||||
renderEntryValue = hasPassword |
||||
? renderEntryValueWithPassword |
||||
: (key, entry) => <MonoText>{String(entry)}</MonoText>, |
||||
}: FormSummaryProps<T>): ReturnType<FC<FormSummaryProps<T>>> => |
||||
buildEntryList({ |
||||
entries, |
||||
getEntryLabel, |
||||
getListProps, |
||||
getListItemProps, |
||||
maxDepth, |
||||
renderEntry, |
||||
renderEntryValue, |
||||
}); |
||||
|
||||
export default FormSummary; |
@ -0,0 +1,45 @@ |
||||
type FormEntry = boolean | null | number | string; |
||||
|
||||
type FormEntries = { |
||||
[key: string]: FormEntries | FormEntry; |
||||
}; |
||||
|
||||
type GetFormEntryLabelFunction = (key: string, entry: FormEntry) => string; |
||||
|
||||
type GetFormEntryPropsFunction = ( |
||||
key: string, |
||||
entry: FormEntry, |
||||
depth: number, |
||||
) => import('@mui/material').ListItemProps; |
||||
|
||||
type GetFormEntriesPropsFunction = ( |
||||
key: string | undefined, |
||||
entries: FormEntries, |
||||
depth: number, |
||||
) => import('@mui/material').ListProps; |
||||
|
||||
type RenderFormValueFunction = ( |
||||
key: string, |
||||
entry: FormEntry, |
||||
) => import('react').ReactElement; |
||||
|
||||
type RenderFormEntryFunction = ( |
||||
key: string, |
||||
entry: FormEntry, |
||||
getLabel: GetFormEntryLabelFunction, |
||||
renderValue: RenderFormValueFunction, |
||||
) => import('react').ReactElement; |
||||
|
||||
type FormSummaryOptionalProps = { |
||||
getEntryLabel?: GetFormEntryLabelFunction; |
||||
getListProps?: GetFormEntriesPropsFunction; |
||||
getListItemProps?: GetFormEntryPropsFunction; |
||||
hasPassword?: boolean; |
||||
maxDepth?: number; |
||||
renderEntry?: RenderFormEntryFunction; |
||||
renderEntryValue?: RenderFormValueFunction; |
||||
}; |
||||
|
||||
type FormSummaryProps<T extends FormEntries> = FormSummaryOptionalProps & { |
||||
entries: T; |
||||
}; |
Loading…
Reference in new issue