Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | 1x 1x 96x 96x 96x 96x 96x 96x 96x 96x 96x 96x 96x 96x 96x 96x 96x 96x 96x 96x 96x 96x 96x 66x 96x 96x 2x 2x 2x 2x 2x 2x 2x 96x 96x 96x 9x 9x 9x 9x 96x 96x 96x 96x 66x 96x 96x 96x 96x 96x 96x 96x 96x 96x 96x 96x 96x 96x 96x | import { FCWithChildren } from '@/shared/@types'
import { ReactElement, useEffect, useRef, useState } from 'react'
import { useTranslate } from '@/shared/lib'
import { motion } from 'framer-motion'
import { useFormErrors } from '@/shared/hooks'
import { useFormContext } from 'react-hook-form'
import cn from 'classnames'
import { Button } from '@tmk/ui-kit'
export interface FormBlockWrapperProps {
title?: ReactElement
defaultOpen?: boolean
fields?: string[]
childrenClassName?: string
getDisclosureButtonText?: (isOpen: boolean) => string
position?: Position
buttonClassName?: string
withMarginTop?: boolean
}
type Position = 'top' | 'bottom'
export const FormBlockWrapper: FCWithChildren<FormBlockWrapperProps> = ({
title,
className,
children,
defaultOpen = true,
fields,
childrenClassName,
getDisclosureButtonText,
position = 'top',
buttonClassName,
withMarginTop = true,
...rest
}) => {
const { t } = useTranslate(['common'])
const ref = useRef<HTMLDivElement>(null)
const { formState } = useFormContext() || { formState: {} }
const { errors } = formState
const { fieldsHasError } = useFormErrors(errors) || {}
const [open, setOpen] = useState(defaultOpen)
const isNeedMarginTop = withMarginTop && (title || fields) && children
const scrollHeight = ref.current?.scrollHeight
useEffect(() => {
if (fieldsHasError?.(fields)) {
setOpen(true)
}
}, [fields, fieldsHasError])
const DisclosureButton = () => (
<Button
className={cn('flex-shrink-0', buttonClassName)}
variant='text'
color='secondary'
onClick={() => setOpen(prev => !prev)}
>
<h3>{getDisclosureButtonText?.(open) || t(open ? 'Rollup' : 'Open')}</h3>
</Button>
)
return (
<div className={cn('bg-gray-tertiary w-full p-4 flex flex-col rounded-base', className)} {...rest}>
{(title || fields) && (
<div className={'flex items-center justify-between'}>
{title && title}
{fields && position === 'top' && <DisclosureButton />}
</div>
)}
<motion.div
ref={ref}
initial={
defaultOpen
? { height: scrollHeight, marginTop: isNeedMarginTop ? '20px' : 0 }
: { height: 0, overflow: 'hidden', marginTop: 0 }
}
animate={{
height: open ? scrollHeight : 0,
...(!open && { overflow: 'hidden' }),
marginTop: open && isNeedMarginTop ? '20px' : 0,
transitionEnd: open ? { overflow: 'visible', height: 'max-content' } : { overflow: 'hidden', height: 0 },
}}
exit={{ overflow: 'visible' }}
transition={{ type: 'just' }}
className={childrenClassName}
>
{children}
</motion.div>
{fields && position === 'bottom' && <DisclosureButton />}
</div>
)
}
|