Add modal windows system and typed modals.
This commit is contained in:
parent
4a83e6a0fb
commit
5dee63db7b
6 changed files with 219 additions and 3 deletions
|
@ -2,7 +2,7 @@ import React, {useState} from "react";
|
|||
import "../index";
|
||||
import {Checkbox} from "../src/Components/Forms/Checkbox";
|
||||
import {Radio} from "../src/Components/Forms/Radio";
|
||||
import {AirTrafficControl, Basket, FloppyDisk, House, TrashSimple, X, XCircle} from "@phosphor-icons/react";
|
||||
import {AirTrafficControl, Basket, FloppyDisk, House, TrashSimple, XCircle} from "@phosphor-icons/react";
|
||||
import {Card} from "../src/Components/Card";
|
||||
import {PasswordInput} from "../src/Components/Forms/PasswordInput";
|
||||
import {RequiredField} from "../src/Components/Forms/RequiredField";
|
||||
|
@ -29,6 +29,9 @@ import {useCallableCurtain, useCurtains} from "../src/Components/Curtains/Curtai
|
|||
import {Subapp, useCallableSubapp, useSubapps} from "../src/Components/Subapps/Subapps";
|
||||
import {DemoSubapp} from "./DemoSubapp";
|
||||
import {DemoCurtain} from "./DemoCurtain";
|
||||
import {DemoModal} from "./DemoModal";
|
||||
import {useCallableModal} from "../src/Components/Modals/Modals";
|
||||
import {ModalType} from "../src/Components/Modals/ModalsTypes";
|
||||
|
||||
export function DemoApp()
|
||||
{
|
||||
|
@ -41,6 +44,9 @@ export function DemoApp()
|
|||
// Easy subapp.
|
||||
const easySubapp = useCallableSubapp(<DemoSubapp />);
|
||||
|
||||
// Easy modal.
|
||||
const easyModal = useCallableModal((type: ModalType = ModalType.NONE) => <DemoModal type={type} />);
|
||||
|
||||
const [datetime, setDatetime] = useState(null);
|
||||
|
||||
const [selected, setSelected] = useState(null);
|
||||
|
@ -489,7 +495,9 @@ export function DemoApp()
|
|||
<h3>Modals</h3>
|
||||
|
||||
<Card>
|
||||
<button>Open a modal</button>
|
||||
<button onClick={() => easyModal(ModalType.INFO)}>Open an info modal</button>
|
||||
<button className={"warning"} onClick={() => easyModal(ModalType.WARNING)}>Open a warning modal</button>
|
||||
<button className={"flat"} onClick={() => easyModal()}>Open a simple modal</button>
|
||||
</Card>
|
||||
|
||||
<h2>Notifications</h2>
|
||||
|
|
16
demo/DemoModal.tsx
Normal file
16
demo/DemoModal.tsx
Normal file
|
@ -0,0 +1,16 @@
|
|||
import React from "react";
|
||||
import {Modal, useModal} from "../src/Components/Modals/Modals";
|
||||
import {ModalType} from "../src/Components/Modals/ModalsTypes";
|
||||
|
||||
export function DemoModal({type}: { type: ModalType; })
|
||||
{
|
||||
const {close} = useModal();
|
||||
|
||||
return (
|
||||
<Modal type={type} title={"Modal title"}>
|
||||
Modal test content
|
||||
|
||||
<button onClick={close}>OK</button>
|
||||
</Modal>
|
||||
);
|
||||
}
|
67
src/Components/Modals/Modals.tsx
Normal file
67
src/Components/Modals/Modals.tsx
Normal file
|
@ -0,0 +1,67 @@
|
|||
import React from "react";
|
||||
import {X} from "@phosphor-icons/react";
|
||||
import {useCurtains, useCurtain, useCallableCurtain} from "../Curtains/Curtains";
|
||||
import {classes, Modify} from "../../Utils";
|
||||
import {ModalType, ModalTypeIcon} from "./ModalsTypes";
|
||||
|
||||
/**
|
||||
* More natural name of useCurtains for modals.
|
||||
* @see useCurtains
|
||||
*/
|
||||
export const useModals = useCurtains;
|
||||
|
||||
/**
|
||||
* More natural name of useCurtain for modals.
|
||||
* @see useCurtain
|
||||
*/
|
||||
export const useModal = useCurtain;
|
||||
|
||||
/**
|
||||
* More natural name of useCallableCurtain for modals.
|
||||
* @see useCallableCurtain
|
||||
*/
|
||||
export const useCallableModal = useCallableCurtain;
|
||||
|
||||
/**
|
||||
* Modal main component.
|
||||
*/
|
||||
export function Modal({className, title, closable, type, children, ...props}: React.PropsWithChildren<Modify<React.HTMLAttributes<HTMLDivElement>, {
|
||||
/**
|
||||
* Modal title.
|
||||
*/
|
||||
title?: string;
|
||||
|
||||
/**
|
||||
* Can disable close button.
|
||||
*/
|
||||
closable?: boolean;
|
||||
|
||||
/**
|
||||
* Modal type. None by default.
|
||||
*/
|
||||
type?: ModalType;
|
||||
}>>)
|
||||
{
|
||||
// Modal is closable by default.
|
||||
closable = closable !== undefined ? closable : true;
|
||||
|
||||
// Modal type is NONE by default.
|
||||
type = type !== undefined ? type : ModalType.NONE;
|
||||
|
||||
// Modal state.
|
||||
const {close} = useModal();
|
||||
|
||||
return (
|
||||
<div className={classes("modal", type, className)} {...props}>
|
||||
<header>
|
||||
<h1>{ModalTypeIcon[type]} {title ?? ""}</h1>
|
||||
|
||||
<button className={"icon-only close"} onClick={close} disabled={!closable}><X size={20} /></button>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
{children}
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
25
src/Components/Modals/ModalsTypes.tsx
Normal file
25
src/Components/Modals/ModalsTypes.tsx
Normal file
|
@ -0,0 +1,25 @@
|
|||
import React from "react";
|
||||
import {CheckCircle, Info, Record, Warning, WarningDiamond} from "@phosphor-icons/react";
|
||||
|
||||
/**
|
||||
* Modal types enumeration.
|
||||
*/
|
||||
export enum ModalType
|
||||
{
|
||||
INFO = "info",
|
||||
SUCCESS = "success",
|
||||
WARNING = "warning",
|
||||
ERROR = "error",
|
||||
NONE = "none",
|
||||
}
|
||||
|
||||
/**
|
||||
* Icon for each modal type.
|
||||
*/
|
||||
export const ModalTypeIcon: Record<ModalType, React.ReactElement> = {
|
||||
[ModalType.INFO]: <Info size={24} weight={"duotone"} />,
|
||||
[ModalType.SUCCESS]: <CheckCircle size={24} weight={"duotone"} />,
|
||||
[ModalType.WARNING]: <Warning size={24} weight={"duotone"} />,
|
||||
[ModalType.ERROR]: <WarningDiamond size={24} weight={"duotone"} />,
|
||||
[ModalType.NONE]: null,
|
||||
};
|
|
@ -11,6 +11,7 @@
|
|||
@import "components/_list";
|
||||
@import "components/_loaders";
|
||||
@import "components/_menus";
|
||||
@import "components/_modal";
|
||||
@import "components/_pagination";
|
||||
@import "components/_select";
|
||||
@import "components/_steps";
|
||||
|
|
99
src/styles/components/_modal.less
Normal file
99
src/styles/components/_modal.less
Normal file
|
@ -0,0 +1,99 @@
|
|||
.curtain > .modal
|
||||
{
|
||||
position: absolute;
|
||||
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
|
||||
margin: auto;
|
||||
width: 45em; max-width: 95%;
|
||||
height: fit-content; max-height: 95%;
|
||||
border-radius: 0.25em;
|
||||
box-sizing: border-box;
|
||||
|
||||
background: var(--background);
|
||||
|
||||
overflow: auto;
|
||||
|
||||
> header
|
||||
{
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
> h1
|
||||
{
|
||||
flex: 1;
|
||||
margin: auto;
|
||||
padding: 1em 1em;
|
||||
|
||||
font-size: 1.33em;
|
||||
font-weight: 650;
|
||||
|
||||
vertical-align: middle;
|
||||
|
||||
svg
|
||||
{
|
||||
margin-top: -0.2em;
|
||||
margin-right: 0.1em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
> .close
|
||||
{
|
||||
transition: opacity 0.2s ease;
|
||||
|
||||
width: 2em;
|
||||
height: 2em;
|
||||
margin: auto 1em;
|
||||
padding: 0;
|
||||
border-radius: 2em;
|
||||
|
||||
box-shadow: 0 0 0 0 transparent;
|
||||
outline: none;
|
||||
border: none;
|
||||
background: none;
|
||||
color: var(--foreground-lightest);
|
||||
|
||||
opacity: 0.6;
|
||||
|
||||
&:hover
|
||||
{
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&:disabled
|
||||
{
|
||||
opacity: 0.33;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> main
|
||||
{
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
&.info
|
||||
{
|
||||
> header > h1 { color: var(--primary); }
|
||||
}
|
||||
|
||||
&.success
|
||||
{
|
||||
> header > h1 { color: var(--green); }
|
||||
}
|
||||
|
||||
&.warning
|
||||
{
|
||||
> header > h1 { color: var(--orange); }
|
||||
}
|
||||
|
||||
&.error
|
||||
{
|
||||
> header > h1 { color: var(--red); }
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue