ConceptPortal-public/rsconcept/frontend/src/components/ui/Modal.tsx

141 lines
3.7 KiB
TypeScript
Raw Normal View History

'use client';
import clsx from 'clsx';
import { motion } from 'framer-motion';
2023-07-25 20:27:29 +03:00
import { useRef } from 'react';
import useEscapeKey from '@/hooks/useEscapeKey';
2024-10-29 12:06:43 +03:00
import { HelpTopic } from '@/models/miscellaneous';
import { animateModal } from '@/styling/animations';
2024-10-29 12:06:43 +03:00
import { PARAMETER } from '@/utils/constants';
2024-03-09 16:40:10 +03:00
import { prepareTooltip } from '@/utils/labels';
import { IconClose } from '../Icons';
2024-10-29 12:06:43 +03:00
import BadgeHelp from '../info/BadgeHelp';
import { CProps } from '../props';
2023-07-25 20:27:29 +03:00
import Button from './Button';
2023-12-04 14:19:54 +03:00
import MiniButton from './MiniButton';
import Overlay from './Overlay';
2023-07-22 12:24:14 +03:00
2023-12-28 14:04:44 +03:00
export interface ModalProps extends CProps.Styling {
header?: string;
submitText?: string;
submitInvalidTooltip?: string;
2023-12-28 14:04:44 +03:00
readonly?: boolean;
canSubmit?: boolean;
2024-06-05 14:43:52 +03:00
overflowVisible?: boolean;
2023-12-28 14:04:44 +03:00
hideWindow: () => void;
2024-08-30 20:18:21 +03:00
beforeSubmit?: () => boolean;
2023-12-28 14:04:44 +03:00
onSubmit?: () => void;
onCancel?: () => void;
2024-10-29 12:06:43 +03:00
helpTopic?: HelpTopic;
hideHelpWhen?: () => boolean;
2023-07-22 12:24:14 +03:00
}
2023-12-28 14:04:44 +03:00
function Modal({
2024-10-29 12:06:43 +03:00
children,
2023-12-28 14:04:44 +03:00
header,
2024-10-29 12:06:43 +03:00
submitText = 'Продолжить',
submitInvalidTooltip,
readonly,
canSubmit,
overflowVisible,
2023-12-28 14:04:44 +03:00
hideWindow,
2024-08-30 20:18:21 +03:00
beforeSubmit,
2023-12-28 14:04:44 +03:00
onSubmit,
onCancel,
className,
2024-10-29 12:06:43 +03:00
helpTopic,
hideHelpWhen,
...restProps
2024-09-19 17:49:25 +03:00
}: React.PropsWithChildren<ModalProps>) {
2023-07-22 12:24:14 +03:00
const ref = useRef(null);
2023-07-25 22:29:33 +03:00
useEscapeKey(hideWindow);
2023-07-22 12:24:14 +03:00
const handleCancel = () => {
2023-07-25 22:29:33 +03:00
hideWindow();
2023-07-25 20:27:29 +03:00
if (onCancel) onCancel();
2023-07-22 12:24:14 +03:00
};
const handleSubmit = () => {
2024-08-30 20:18:21 +03:00
if (beforeSubmit && !beforeSubmit()) {
return;
}
2023-08-08 23:04:21 +03:00
if (onSubmit) onSubmit();
2024-08-30 20:18:21 +03:00
hideWindow();
};
2023-12-04 14:19:54 +03:00
return (
2024-10-29 12:06:43 +03:00
<div className='fixed top-0 left-0 w-full h-full z-modal cursor-default'>
2024-04-03 21:53:11 +03:00
<div className={clsx('z-navigation', 'fixed top-0 left-0', 'w-full h-full', 'cc-modal-blur')} />
<div className={clsx('z-navigation', 'fixed top-0 left-0', 'w-full h-full', 'cc-modal-backdrop')} />
2023-12-28 14:04:44 +03:00
<motion.div
ref={ref}
2024-05-10 15:28:33 +03:00
className={clsx(
'z-modal',
'absolute bottom-1/2 left-1/2 -translate-x-1/2 translate-y-1/2',
'border rounded-xl',
2024-05-10 15:28:33 +03:00
'clr-app'
)}
2023-12-28 14:04:44 +03:00
initial={{ ...animateModal.initial }}
animate={{ ...animateModal.animate }}
exit={{ ...animateModal.exit }}
{...restProps}
2023-12-07 01:21:27 +03:00
>
<Overlay position='right-2 top-2'>
2024-03-09 16:40:10 +03:00
<MiniButton
noPadding
2024-03-09 16:40:10 +03:00
titleHtml={prepareTooltip('Закрыть диалоговое окно', 'ESC')}
icon={<IconClose size='1.25rem' />}
2024-03-09 16:40:10 +03:00
onClick={handleCancel}
/>
2023-12-28 14:04:44 +03:00
</Overlay>
2024-10-29 12:06:43 +03:00
{helpTopic && !hideHelpWhen?.() ? (
<Overlay position='left-2 top-2'>
<BadgeHelp topic={helpTopic} className={clsx(PARAMETER.TOOLTIP_WIDTH, 'sm:max-w-[40rem]')} padding='p-0' />
</Overlay>
) : null}
2023-12-28 14:04:44 +03:00
{header ? <h1 className='px-12 py-2 select-none'>{header}</h1> : null}
<div
className={clsx(
'overscroll-contain max-h-[calc(100svh-8rem)] max-w-[100svw] xs:max-w-[calc(100svw-2rem)]',
{
'overflow-auto': !overflowVisible,
'overflow-visible': overflowVisible
},
className
)}
2023-12-28 14:04:44 +03:00
>
{children}
</div>
2023-12-04 14:19:54 +03:00
<div className='z-modalControls my-2 flex gap-12 justify-center text-sm'>
2023-12-28 14:04:44 +03:00
{!readonly ? (
<Button
autoFocus
text={submitText}
title={!canSubmit ? submitInvalidTooltip : ''}
className='min-w-[7rem]'
2023-12-28 14:04:44 +03:00
colors='clr-btn-primary'
disabled={!canSubmit}
onClick={handleSubmit}
/>
) : null}
<Button text={readonly ? 'Закрыть' : 'Отмена'} className='min-w-[7rem]' onClick={handleCancel} />
2023-12-28 14:04:44 +03:00
</div>
</motion.div>
2024-05-10 15:28:33 +03:00
</div>
2023-12-28 14:04:44 +03:00
);
2023-07-22 12:24:14 +03:00
}
2023-12-28 14:04:44 +03:00
export default Modal;