Form prototype
This commit is contained in:
146
src/Form/containers/Drawer/SuperFormDrawer.tsx
Normal file
146
src/Form/containers/Drawer/SuperFormDrawer.tsx
Normal file
@@ -0,0 +1,146 @@
|
||||
import React, {
|
||||
forwardRef,
|
||||
useCallback,
|
||||
useImperativeHandle,
|
||||
useRef,
|
||||
useState,
|
||||
type ReactElement,
|
||||
type Ref,
|
||||
} from 'react'
|
||||
import { IconX } from '@tabler/icons-react'
|
||||
import type { FieldValues } from 'react-hook-form'
|
||||
import { ActionIcon, Drawer } from '@mantine/core'
|
||||
import type { SuperFormDrawerProps, SuperFormDrawerRef, SuperFormRef } from '../../types'
|
||||
import SuperForm from '../../components/SuperForm'
|
||||
import { openConfirmModal } from '../../utils/openConfirmModal'
|
||||
|
||||
const SuperFormDrawer = <T extends FieldValues>(
|
||||
{ drawerProps, noCloseOnSubmit, ...formProps }: SuperFormDrawerProps<T>,
|
||||
ref: Ref<SuperFormDrawerRef<T>>
|
||||
) => {
|
||||
// Component Refs
|
||||
const formRef = useRef<SuperFormRef<T>>(null)
|
||||
const drawerRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
// Component store State
|
||||
// Tell drawer that form layout mounted to fix refs
|
||||
const [layoutMounted, setLayoutMounted] = useState(false)
|
||||
|
||||
// Component Callback Functions
|
||||
const onSubmit = (data: T, request, formData, form, closeForm: boolean = true) => {
|
||||
formProps?.onSubmit?.(data, request, formData, form, closeForm)
|
||||
|
||||
if (request === 'delete') {
|
||||
drawerProps?.onClose()
|
||||
}
|
||||
|
||||
if (!noCloseOnSubmit) {
|
||||
if (closeForm) {
|
||||
drawerProps?.onClose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const onCancel = (request) => {
|
||||
if (formRef?.current?.getFormState().isDirty) {
|
||||
openConfirmModal(() => {
|
||||
drawerProps?.onClose()
|
||||
formProps?.onCancel?.(request)
|
||||
})
|
||||
} else {
|
||||
drawerProps?.onClose()
|
||||
formProps?.onCancel?.(request)
|
||||
}
|
||||
}
|
||||
|
||||
const onLayoutMounted = useCallback(() => {
|
||||
setLayoutMounted(true)
|
||||
formProps?.onLayoutMounted?.()
|
||||
}, [formProps?.onLayoutMounted])
|
||||
|
||||
const onLayoutUnMounted = useCallback(() => {
|
||||
setLayoutMounted(false)
|
||||
formProps?.onLayoutUnMounted?.()
|
||||
}, [formProps?.onLayoutUnMounted])
|
||||
|
||||
// Component use Effects
|
||||
useImperativeHandle<SuperFormDrawerRef<T>, SuperFormDrawerRef<T>>(
|
||||
ref,
|
||||
() => ({
|
||||
...formRef.current,
|
||||
drawer: drawerRef.current,
|
||||
} as SuperFormDrawerRef<T>),
|
||||
[layoutMounted]
|
||||
)
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
ref={drawerRef}
|
||||
onClose={onCancel}
|
||||
closeOnClickOutside={false}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Escape' && drawerProps.closeOnEscape !== false) {
|
||||
e.stopPropagation()
|
||||
onCancel(formProps.request)
|
||||
}
|
||||
}}
|
||||
overlayProps={{ backgroundOpacity: 0.5, blur: 0.5 }}
|
||||
padding={6}
|
||||
position='right'
|
||||
transitionProps={{
|
||||
transition: 'slide-left',
|
||||
duration: 150,
|
||||
timingFunction: 'linear',
|
||||
}}
|
||||
size={500}
|
||||
styles={{
|
||||
content: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'stretch',
|
||||
},
|
||||
body: {
|
||||
minHeight: '100px',
|
||||
flexGrow: 1,
|
||||
},
|
||||
}}
|
||||
keepMounted={false}
|
||||
{...drawerProps}
|
||||
closeOnEscape={false}
|
||||
withCloseButton={false}
|
||||
title={null}
|
||||
>
|
||||
<SuperForm<T>
|
||||
{...formProps}
|
||||
onCancel={onCancel}
|
||||
onSubmit={onSubmit}
|
||||
onLayoutMounted={onLayoutMounted}
|
||||
onLayoutUnMounted={onLayoutUnMounted}
|
||||
ref={formRef}
|
||||
layoutProps={{
|
||||
...formProps?.layoutProps,
|
||||
rightSection: (
|
||||
<ActionIcon
|
||||
size='xs'
|
||||
onClick={() => {
|
||||
onCancel(formProps?.request)
|
||||
}}
|
||||
>
|
||||
<IconX size={18} />
|
||||
</ActionIcon>
|
||||
),
|
||||
title:
|
||||
(drawerProps.title as string) ??
|
||||
formProps?.layoutProps?.title ??
|
||||
(formProps?.request as string),
|
||||
}}
|
||||
/>
|
||||
</Drawer>
|
||||
)
|
||||
}
|
||||
|
||||
const FRSuperFormDrawer = forwardRef(SuperFormDrawer) as <T extends FieldValues>(
|
||||
props: SuperFormDrawerProps<T> & { ref?: Ref<SuperFormDrawerRef<T>> }
|
||||
) => ReactElement
|
||||
|
||||
export default FRSuperFormDrawer
|
||||
105
src/Form/containers/Modal/SuperFormModal.tsx
Normal file
105
src/Form/containers/Modal/SuperFormModal.tsx
Normal file
@@ -0,0 +1,105 @@
|
||||
import React, {
|
||||
forwardRef,
|
||||
useCallback,
|
||||
useImperativeHandle,
|
||||
useRef,
|
||||
useState,
|
||||
type ReactElement,
|
||||
type Ref,
|
||||
} from 'react'
|
||||
import type { FieldValues } from 'react-hook-form'
|
||||
import { Modal, ScrollArea } from '@mantine/core'
|
||||
import type { SuperFormModalProps, SuperFormModalRef, SuperFormRef } from '../../types'
|
||||
import SuperForm from '../../components/SuperForm'
|
||||
import { openConfirmModal } from '../../utils/openConfirmModal'
|
||||
|
||||
const SuperFormModal = <T extends FieldValues>(
|
||||
{ modalProps, noCloseOnSubmit, ...formProps }: SuperFormModalProps<T>,
|
||||
ref: Ref<SuperFormModalRef<T>>
|
||||
) => {
|
||||
// Component Refs
|
||||
const modalRef = useRef<HTMLDivElement>(null)
|
||||
const formRef = useRef<SuperFormRef<T>>(null)
|
||||
|
||||
// Component store State
|
||||
// Tell drawer that form layout mounted to fix refs
|
||||
const [layoutMounted, setLayoutMounted] = useState(false)
|
||||
|
||||
// Component Callback Functions
|
||||
const onSubmit = (data: T, request, formData, form, closeForm: boolean = true) => {
|
||||
formProps?.onSubmit?.(data, request, formData, form, closeForm)
|
||||
|
||||
if (request === 'delete') {
|
||||
modalProps?.onClose()
|
||||
}
|
||||
|
||||
if (!noCloseOnSubmit) {
|
||||
if (closeForm) {
|
||||
modalProps?.onClose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const onCancel = (request) => {
|
||||
if (formRef?.current?.getFormState().isDirty) {
|
||||
openConfirmModal(() => {
|
||||
modalProps?.onClose()
|
||||
formProps?.onCancel?.(request)
|
||||
})
|
||||
} else {
|
||||
modalProps?.onClose()
|
||||
formProps?.onCancel?.(request)
|
||||
}
|
||||
}
|
||||
|
||||
const onLayoutMounted = useCallback(() => {
|
||||
setLayoutMounted(true)
|
||||
formProps?.onLayoutMounted?.()
|
||||
}, [formProps?.onLayoutMounted])
|
||||
|
||||
const onLayoutUnMounted = useCallback(() => {
|
||||
setLayoutMounted(false)
|
||||
formProps?.onLayoutUnMounted?.()
|
||||
}, [formProps?.onLayoutUnMounted])
|
||||
|
||||
// Component use Effects
|
||||
useImperativeHandle<SuperFormModalRef<T>, SuperFormModalRef<T>>(
|
||||
ref,
|
||||
() => ({
|
||||
...formRef.current,
|
||||
modal: modalRef.current,
|
||||
} as SuperFormModalRef<T>),
|
||||
[layoutMounted]
|
||||
)
|
||||
|
||||
return (
|
||||
<Modal
|
||||
ref={modalRef}
|
||||
closeOnClickOutside={false}
|
||||
overlayProps={{
|
||||
backgroundOpacity: 0.5,
|
||||
blur: 4,
|
||||
}}
|
||||
padding='sm'
|
||||
scrollAreaComponent={ScrollArea.Autosize}
|
||||
size={500}
|
||||
keepMounted={false}
|
||||
{...modalProps}
|
||||
>
|
||||
<SuperForm<T>
|
||||
{...formProps}
|
||||
onCancel={onCancel}
|
||||
onSubmit={onSubmit}
|
||||
onLayoutMounted={onLayoutMounted}
|
||||
onLayoutUnMounted={onLayoutUnMounted}
|
||||
ref={formRef}
|
||||
/>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
const FRSuperFormModal = forwardRef(SuperFormModal) as <T extends FieldValues>(
|
||||
props: SuperFormModalProps<T> & { ref?: Ref<SuperFormModalRef<T>> }
|
||||
) => ReactElement
|
||||
|
||||
export default FRSuperFormModal
|
||||
116
src/Form/containers/Popover/SuperFormPopover.tsx
Normal file
116
src/Form/containers/Popover/SuperFormPopover.tsx
Normal file
@@ -0,0 +1,116 @@
|
||||
import React, {
|
||||
forwardRef,
|
||||
useCallback,
|
||||
useImperativeHandle,
|
||||
useRef,
|
||||
useState,
|
||||
type ReactElement,
|
||||
type Ref,
|
||||
} from 'react'
|
||||
import type { FieldValues } from 'react-hook-form'
|
||||
import { Box, Popover } from '@mantine/core'
|
||||
import { useUncontrolled } from '@mantine/hooks'
|
||||
import type { SuperFormPopoverProps, SuperFormPopoverRef, SuperFormRef } from '../../types'
|
||||
import SuperForm from '../../components/SuperForm'
|
||||
import { openConfirmModal } from '../../utils/openConfirmModal'
|
||||
|
||||
const SuperFormPopover = <T extends FieldValues>(
|
||||
{ popoverProps, target, noCloseOnSubmit, ...formProps }: SuperFormPopoverProps<T>,
|
||||
ref: Ref<SuperFormPopoverRef<T>>
|
||||
) => {
|
||||
// Component Refs
|
||||
const popoverRef = useRef<HTMLDivElement>(null)
|
||||
const formRef = useRef<SuperFormRef<T>>(null)
|
||||
|
||||
// Component store State
|
||||
// Tell drawer that form layout mounted to fix refs
|
||||
const [layoutMounted, setLayoutMounted] = useState(false)
|
||||
|
||||
// Component Hooks
|
||||
const [_value, _onChange] = useUncontrolled({
|
||||
value: popoverProps?.opened,
|
||||
onChange: popoverProps?.onChange,
|
||||
})
|
||||
|
||||
// Component Callback Functions
|
||||
const onSubmit = (data: T, request, formData, form, closeForm: boolean = true) => {
|
||||
formProps?.onSubmit?.(data, request, formData, form, closeForm)
|
||||
|
||||
if (request === 'delete') {
|
||||
_onChange(false)
|
||||
}
|
||||
|
||||
if (!noCloseOnSubmit) {
|
||||
if (closeForm) {
|
||||
_onChange(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const onCancel = (request) => {
|
||||
if (formRef?.current?.getFormState().isDirty) {
|
||||
openConfirmModal(() => {
|
||||
_onChange(false)
|
||||
formProps?.onCancel?.(request)
|
||||
})
|
||||
} else {
|
||||
_onChange(false)
|
||||
formProps?.onCancel?.(request)
|
||||
}
|
||||
}
|
||||
|
||||
const onLayoutMounted = useCallback(() => {
|
||||
setLayoutMounted(true)
|
||||
formProps?.onLayoutMounted?.()
|
||||
}, [formProps?.onLayoutMounted])
|
||||
|
||||
const onLayoutUnMounted = useCallback(() => {
|
||||
setLayoutMounted(false)
|
||||
formProps?.onLayoutUnMounted?.()
|
||||
}, [formProps?.onLayoutUnMounted])
|
||||
|
||||
// Component use Effects
|
||||
useImperativeHandle<SuperFormPopoverRef<T>, SuperFormPopoverRef<T>>(
|
||||
ref,
|
||||
() => ({
|
||||
...formRef.current,
|
||||
popover: popoverRef.current,
|
||||
} as SuperFormPopoverRef<T>),
|
||||
[layoutMounted]
|
||||
)
|
||||
|
||||
return (
|
||||
<Popover
|
||||
closeOnClickOutside={false}
|
||||
onClose={() => _onChange(false)}
|
||||
opened={_value}
|
||||
position='left'
|
||||
radius='md'
|
||||
withArrow
|
||||
withinPortal
|
||||
zIndex={200}
|
||||
keepMounted={false}
|
||||
{...popoverProps}
|
||||
>
|
||||
<Popover.Target>
|
||||
<Box onClick={() => _onChange(true)}>{target}</Box>
|
||||
</Popover.Target>
|
||||
<Popover.Dropdown p={0} m={0} ref={popoverRef}>
|
||||
<SuperForm
|
||||
{...formProps}
|
||||
onCancel={onCancel}
|
||||
onSubmit={onSubmit}
|
||||
onLayoutMounted={onLayoutMounted}
|
||||
onLayoutUnMounted={onLayoutUnMounted}
|
||||
ref={formRef}
|
||||
/>
|
||||
</Popover.Dropdown>
|
||||
</Popover>
|
||||
)
|
||||
}
|
||||
|
||||
const FRSuperFormPopover = forwardRef(SuperFormPopover) as <T extends FieldValues>(
|
||||
props: SuperFormPopoverProps<T> & { ref?: Ref<SuperFormPopoverRef<T>> }
|
||||
) => ReactElement
|
||||
|
||||
export default FRSuperFormPopover
|
||||
Reference in New Issue
Block a user