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
				
			
		| 
						 | 
					@ -1,8 +1,8 @@
 | 
				
			||||||
import React, {useState} from "react";
 | 
					import React, {useState} from "react";
 | 
				
			||||||
import "../index";
 | 
					import "../index";
 | 
				
			||||||
import {Checkbox} from "../src/Components/Forms/Checkbox";
 | 
					import {Checkbox} from "../src/Components/Forms/Checkbox";
 | 
				
			||||||
import { Radio } from "../src/Components/Forms/Radio";
 | 
					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 {Card} from "../src/Components/Card";
 | 
				
			||||||
import {PasswordInput} from "../src/Components/Forms/PasswordInput";
 | 
					import {PasswordInput} from "../src/Components/Forms/PasswordInput";
 | 
				
			||||||
import {RequiredField} from "../src/Components/Forms/RequiredField";
 | 
					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 {Subapp, useCallableSubapp, useSubapps} from "../src/Components/Subapps/Subapps";
 | 
				
			||||||
import {DemoSubapp} from "./DemoSubapp";
 | 
					import {DemoSubapp} from "./DemoSubapp";
 | 
				
			||||||
import {DemoCurtain} from "./DemoCurtain";
 | 
					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()
 | 
					export function DemoApp()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -41,6 +44,9 @@ export function DemoApp()
 | 
				
			||||||
	// Easy subapp.
 | 
						// Easy subapp.
 | 
				
			||||||
	const easySubapp = useCallableSubapp(<DemoSubapp />);
 | 
						const easySubapp = useCallableSubapp(<DemoSubapp />);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Easy modal.
 | 
				
			||||||
 | 
						const easyModal = useCallableModal((type: ModalType = ModalType.NONE) => <DemoModal type={type} />);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const [datetime, setDatetime] = useState(null);
 | 
						const [datetime, setDatetime] = useState(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const [selected, setSelected] = useState(null);
 | 
						const [selected, setSelected] = useState(null);
 | 
				
			||||||
| 
						 | 
					@ -489,7 +495,9 @@ export function DemoApp()
 | 
				
			||||||
			<h3>Modals</h3>
 | 
								<h3>Modals</h3>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			<Card>
 | 
								<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>
 | 
								</Card>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			<h2>Notifications</h2>
 | 
								<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/_list";
 | 
				
			||||||
@import "components/_loaders";
 | 
					@import "components/_loaders";
 | 
				
			||||||
@import "components/_menus";
 | 
					@import "components/_menus";
 | 
				
			||||||
 | 
					@import "components/_modal";
 | 
				
			||||||
@import "components/_pagination";
 | 
					@import "components/_pagination";
 | 
				
			||||||
@import "components/_select";
 | 
					@import "components/_select";
 | 
				
			||||||
@import "components/_steps";
 | 
					@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…
	
	Add table
		
		Reference in a new issue