diff --git a/rsconcept/frontend/src/app/ApplicationLayout.tsx b/rsconcept/frontend/src/app/ApplicationLayout.tsx index 87407967..9385b2fa 100644 --- a/rsconcept/frontend/src/app/ApplicationLayout.tsx +++ b/rsconcept/frontend/src/app/ApplicationLayout.tsx @@ -5,7 +5,7 @@ import ConceptToaster from '@/app/ConceptToaster'; import Footer from '@/app/Footer'; import Navigation from '@/app/Navigation'; import Loader from '@/components/ui/Loader'; -import ModalLoader from '@/components/ui/ModalLoader'; +import { ModalLoader } from '@/components/ui/Modal'; import { useAppLayoutStore, useMainHeight, useViewportHeight } from '@/stores/appLayout'; import { globals } from '@/utils/constants'; diff --git a/rsconcept/frontend/src/components/ui/Modal/ModalBackdrop.tsx b/rsconcept/frontend/src/components/ui/Modal/ModalBackdrop.tsx new file mode 100644 index 00000000..1fa5f8e6 --- /dev/null +++ b/rsconcept/frontend/src/components/ui/Modal/ModalBackdrop.tsx @@ -0,0 +1,19 @@ +'use client'; + +import clsx from 'clsx'; + +interface ModalBackdropProps { + onHide: () => void; +} + +export function ModalBackdrop({ onHide }: ModalBackdropProps) { + return ( + <> +
+
+ + ); +} diff --git a/rsconcept/frontend/src/components/ui/Modal.tsx b/rsconcept/frontend/src/components/ui/Modal/ModalForm.tsx similarity index 64% rename from rsconcept/frontend/src/components/ui/Modal.tsx rename to rsconcept/frontend/src/components/ui/Modal/ModalForm.tsx index 74181bb7..ae76b9a9 100644 --- a/rsconcept/frontend/src/components/ui/Modal.tsx +++ b/rsconcept/frontend/src/components/ui/Modal/ModalForm.tsx @@ -5,46 +5,24 @@ import clsx from 'clsx'; import { IconClose } from '@/components/Icons'; import BadgeHelp from '@/components/info/BadgeHelp'; import { CProps } from '@/components/props'; +import Button from '@/components/ui/Button'; +import MiniButton from '@/components/ui/MiniButton'; import useEscapeKey from '@/hooks/useEscapeKey'; import { HelpTopic } from '@/models/miscellaneous'; import { useDialogsStore } from '@/stores/dialogs'; import { PARAMETER } from '@/utils/constants'; import { prepareTooltip } from '@/utils/labels'; -import Button from './Button'; -import MiniButton from './MiniButton'; +import SubmitButton from '../SubmitButton'; +import { ModalBackdrop } from './ModalBackdrop'; export interface ModalProps extends CProps.Styling { /** Title of the modal window. */ header?: string; - /** Text of the submit button. */ - submitText?: string; - - /** Tooltip for the submit button when the form is invalid. */ - submitInvalidTooltip?: string; - - /** Indicates that form is readonly. */ - readonly?: boolean; - - /** Indicates that submit button is enabled. */ - canSubmit?: boolean; - /** Indicates that the modal window should be scrollable. */ overflowVisible?: boolean; - /** ID of the form to be submitted. */ - formID?: string; - - /** Callback to be called before submit. */ - beforeSubmit?: () => boolean; - - /** Callback to be called after submit. */ - onSubmit?: () => boolean; - - /** Callback to be called after cancel. */ - onCancel?: () => void; - /** Help topic to be displayed in the modal window. */ helpTopic?: HelpTopic; @@ -52,67 +30,64 @@ export interface ModalProps extends CProps.Styling { hideHelpWhen?: () => boolean; } +interface ModalFormProps extends ModalProps { + /** Text of the submit button. */ + submitText?: string; + + /** Tooltip for the submit button when the form is invalid. */ + submitInvalidTooltip?: string; + + /** Indicates that submit button is enabled. */ + canSubmit?: boolean; + + /** Callback to be called before submit. */ + beforeSubmit?: () => boolean; + + /** Callback to be called after submit. */ + onSubmit: (event: React.FormEvent) => void; +} + /** - * Displays a customizable modal window. + * Displays a customizable modal window with submit form. */ -function Modal({ +export function ModalForm({ children, + className, header, - submitText = 'Продолжить', - submitInvalidTooltip, - - readonly, - canSubmit, overflowVisible, + canSubmit, + submitText = 'Продолжить', + submitInvalidTooltip, beforeSubmit, - formID, onSubmit, - onCancel, - className, helpTopic, hideHelpWhen, ...restProps -}: React.PropsWithChildren) { +}: React.PropsWithChildren) { const hideDialog = useDialogsStore(state => state.hideDialog); useEscapeKey(hideDialog); - const handleCancel = () => { - hideDialog(); - onCancel?.(); - }; - - const handleSubmit = () => { + function handleSubmit(event: React.FormEvent) { if (beforeSubmit && !beforeSubmit()) { return; } - if (onSubmit && !onSubmit()) { - return; - } - if (formID) { - const element = document.getElementById(formID) as HTMLFormElement; - if (element) { - element.requestSubmit(); - } - } + onSubmit(event); hideDialog(); - }; + } return (
-
-
-
+
{helpTopic && !hideHelpWhen?.() ? (
@@ -125,7 +100,7 @@ function Modal({ titleHtml={prepareTooltip('Закрыть диалоговое окно', 'ESC')} icon={} className='float-right mt-2 mr-2' - onClick={handleCancel} + onClick={hideDialog} /> {header ?

{header}

: null} @@ -145,22 +120,16 @@ function Modal({
- {!readonly ? ( -
-
+
); } - -export default Modal; diff --git a/rsconcept/frontend/src/components/ui/ModalLoader.tsx b/rsconcept/frontend/src/components/ui/Modal/ModalLoader.tsx similarity index 88% rename from rsconcept/frontend/src/components/ui/ModalLoader.tsx rename to rsconcept/frontend/src/components/ui/Modal/ModalLoader.tsx index 8929420d..634196e1 100644 --- a/rsconcept/frontend/src/components/ui/ModalLoader.tsx +++ b/rsconcept/frontend/src/components/ui/Modal/ModalLoader.tsx @@ -2,9 +2,9 @@ import clsx from 'clsx'; -import Loader from './Loader'; +import Loader from '@/components/ui/Loader'; -function ModalLoader() { +export function ModalLoader() { return (
@@ -21,5 +21,3 @@ function ModalLoader() {
); } - -export default ModalLoader; diff --git a/rsconcept/frontend/src/components/ui/Modal/ModalView.tsx b/rsconcept/frontend/src/components/ui/Modal/ModalView.tsx new file mode 100644 index 00000000..05f71e8d --- /dev/null +++ b/rsconcept/frontend/src/components/ui/Modal/ModalView.tsx @@ -0,0 +1,80 @@ +'use client'; + +import clsx from 'clsx'; + +import { IconClose } from '@/components/Icons'; +import BadgeHelp from '@/components/info/BadgeHelp'; +import Button from '@/components/ui/Button'; +import MiniButton from '@/components/ui/MiniButton'; +import useEscapeKey from '@/hooks/useEscapeKey'; +import { useDialogsStore } from '@/stores/dialogs'; +import { PARAMETER } from '@/utils/constants'; +import { prepareTooltip } from '@/utils/labels'; + +import { ModalBackdrop } from './ModalBackdrop'; +import { ModalProps } from './ModalForm'; + +interface ModalViewProps extends ModalProps {} + +/** + * Displays a customizable modal window with submit form. + */ +export function ModalView({ + children, + className, + header, + overflowVisible, + helpTopic, + hideHelpWhen, + ...restProps +}: React.PropsWithChildren) { + const hideDialog = useDialogsStore(state => state.hideDialog); + useEscapeKey(hideDialog); + + return ( +
+ +
+ {helpTopic && !hideHelpWhen?.() ? ( +
+ +
+ ) : null} + + } + className='float-right mt-2 mr-2' + onClick={hideDialog} + /> + + {header ?

{header}

: null} + +
+ {children} +
+ +
+
+
+
+ ); +} diff --git a/rsconcept/frontend/src/components/ui/Modal/index.tsx b/rsconcept/frontend/src/components/ui/Modal/index.tsx new file mode 100644 index 00000000..c9ddb230 --- /dev/null +++ b/rsconcept/frontend/src/components/ui/Modal/index.tsx @@ -0,0 +1,3 @@ +export { ModalForm } from './ModalForm'; +export { ModalLoader } from './ModalLoader'; +export { ModalView } from './ModalView'; diff --git a/rsconcept/frontend/src/dialogs/DlgChangeInputSchema.tsx b/rsconcept/frontend/src/dialogs/DlgChangeInputSchema.tsx index f2240c06..9733a8f2 100644 --- a/rsconcept/frontend/src/dialogs/DlgChangeInputSchema.tsx +++ b/rsconcept/frontend/src/dialogs/DlgChangeInputSchema.tsx @@ -8,7 +8,7 @@ import { IconReset } from '@/components/Icons'; import PickSchema from '@/components/select/PickSchema'; import Label from '@/components/ui/Label'; import MiniButton from '@/components/ui/MiniButton'; -import Modal from '@/components/ui/Modal'; +import { ModalForm } from '@/components/ui/Modal'; import { ILibraryItem, LibraryItemID, LibraryItemType } from '@/models/library'; import { IOperation, IOperationSchema, OperationID } from '@/models/oss'; import { sortItemsForOSS } from '@/models/ossAPI'; @@ -41,7 +41,7 @@ function DlgChangeInputSchema() { } return ( - - + ); } diff --git a/rsconcept/frontend/src/dialogs/DlgChangeLocation.tsx b/rsconcept/frontend/src/dialogs/DlgChangeLocation.tsx index 7b5a530b..95079566 100644 --- a/rsconcept/frontend/src/dialogs/DlgChangeLocation.tsx +++ b/rsconcept/frontend/src/dialogs/DlgChangeLocation.tsx @@ -7,7 +7,7 @@ import { useAuthSuspense } from '@/backend/auth/useAuth'; import SelectLocationContext from '@/components/select/SelectLocationContext'; import SelectLocationHead from '@/components/select/SelectLocationHead'; import Label from '@/components/ui/Label'; -import Modal from '@/components/ui/Modal'; +import { ModalForm } from '@/components/ui/Modal'; import TextArea from '@/components/ui/TextArea'; import { LocationHead } from '@/models/library'; import { combineLocation, validateLocation } from '@/models/libraryAPI'; @@ -39,7 +39,7 @@ function DlgChangeLocation() { } return ( -