модалка - на ревью

This commit is contained in:
Nikolai 2022-09-14 13:24:02 +03:00
parent 27c1a782ed
commit 072fe6f848
9 changed files with 202 additions and 425 deletions

@ -1,185 +0,0 @@
import React from "react";
import {
BrowserRouter as Router,
Switch,
Route,
Link,
useHistory,
useLocation,
useParams
} from "react-router-dom";
// This example shows how to render two different screens
// (or the same screen in a different context) at the same URL,
// depending on how you got there.
//
// Click the "featured images" and see them full screen. Then
// "visit the gallery" and click on the colors. Note the URL and
// the component are the same as before but now we see them
// inside a modal on top of the gallery screen.
export default function ModalGalleryExample() {
return (
<Router>
<ModalSwitch />
</Router>
);
}
function ModalSwitch() {
let location = useLocation();
// This piece of state is set when one of the
// gallery links is clicked. The `background` state
// is the location that we were at when one of
// the gallery links was clicked. If it's there,
// use it as the location for the <Switch> so
// we show the gallery in the background, behind
// the modal.
let background = location.state && location.state.background;
return (
<div>
<Switch location={background || location}>
<Route exact path="/" children={<Home />} />
<Route path="/gallery" children={<Gallery />} />
<Route path="/img/:id" children={<ImageView />} />
</Switch>
{/* Show the modal when a background page is set */}
{background && <Route path="/img/:id" children={<Modal />} />}
</div>
);
}
const IMAGES = [
{ id: 0, title: "Dark Orchid", color: "DarkOrchid" },
{ id: 1, title: "Lime Green", color: "LimeGreen" },
{ id: 2, title: "Tomato", color: "Tomato" },
{ id: 3, title: "Seven Ate Nine", color: "#789" },
{ id: 4, title: "Crimson", color: "Crimson" }
];
function Thumbnail({ color }) {
return (
<div
style={{
width: 50,
height: 50,
background: color
}}
/>
);
}
function Image({ color }) {
return (
<div
style={{
width: "100%",
height: 400,
background: color
}}
/>
);
}
function Home() {
return (
<div>
<Link to="/gallery">Visit the Gallery</Link>
<h2>Featured Images</h2>
<ul>
<li>
<Link to="/img/2">Tomato</Link>
</li>
<li>
<Link to="/img/4">Crimson</Link>
</li>
</ul>
</div>
);
}
function Gallery() {
let location = useLocation();
return (
<div>
{IMAGES.map(i => (
<Link
key={i.id}
to={{
pathname: `/img/${i.id}`,
// This is the trick! This link sets
// the `background` in location state.
state: { background: location }
}}
>
<Thumbnail color={i.color} />
<p>{i.title}</p>
</Link>
))}
</div>
);
}
function ImageView() {
let { id } = useParams();
let image = IMAGES[parseInt(id, 10)];
if (!image) return <div>Image not found</div>;
return (
<div>
<h1>{image.title}</h1>
<Image color={image.color} />
</div>
);
}
function Modal() {
let history = useHistory();
let { id } = useParams();
let image = IMAGES[parseInt(id, 10)];
if (!image) return null;
let back = e => {
e.stopPropagation();
history.goBack();
};
return (
<div
onClick={back}
style={{
position: "absolute",
top: 0,
left: 0,
bottom: 0,
right: 0,
background: "rgba(0, 0, 0, 0.15)"
}}
>
<div
className="modal"
style={{
position: "absolute",
background: "#fff",
top: 25,
left: "10%",
right: "10%",
padding: 15,
border: "2px solid #444"
}}
>
<h1>{image.title}</h1>
<Image color={image.color} />
<button type="button" onClick={back}>
Close
</button>
</div>
</div>
);
}

48
package-lock.json generated

@ -26,7 +26,8 @@
"react-scripts": "5.0.1",
"styled-components": "^5.3.5",
"typescript": "^4.8.2",
"web-vitals": "^2.1.4"
"web-vitals": "^2.1.4",
"zustand": "^4.1.1"
}
},
"node_modules/@adobe/css-tools": {
@ -16240,6 +16241,14 @@
"requires-port": "^1.0.0"
}
},
"node_modules/use-sync-external-store": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
"integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@ -17219,6 +17228,29 @@
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/zustand": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/zustand/-/zustand-4.1.1.tgz",
"integrity": "sha512-h4F3WMqsZgvvaE0n3lThx4MM81Ls9xebjvrABNzf5+jb3/03YjNTSgZXeyrvXDArMeV9untvWXRw1tY+ntPYbA==",
"dependencies": {
"use-sync-external-store": "1.2.0"
},
"engines": {
"node": ">=12.7.0"
},
"peerDependencies": {
"immer": ">=9.0",
"react": ">=16.8"
},
"peerDependenciesMeta": {
"immer": {
"optional": true
},
"react": {
"optional": true
}
}
}
},
"dependencies": {
@ -28616,6 +28648,12 @@
"requires-port": "^1.0.0"
}
},
"use-sync-external-store": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
"integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
"requires": {}
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@ -29379,6 +29417,14 @@
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="
},
"zustand": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/zustand/-/zustand-4.1.1.tgz",
"integrity": "sha512-h4F3WMqsZgvvaE0n3lThx4MM81Ls9xebjvrABNzf5+jb3/03YjNTSgZXeyrvXDArMeV9untvWXRw1tY+ntPYbA==",
"requires": {
"use-sync-external-store": "1.2.0"
}
}
}
}

@ -21,7 +21,8 @@
"react-scripts": "5.0.1",
"styled-components": "^5.3.5",
"typescript": "^4.8.2",
"web-vitals": "^2.1.4"
"web-vitals": "^2.1.4",
"zustand": "^4.1.1"
},
"scripts": {
"start": "react-scripts start",

@ -1,14 +1,25 @@
import * as React from "react";
import { Box } from "@mui/material";
import { ThemeProvider } from "@mui/material";
import theme from "../../theme";
import CssBaseline from '@mui/material/CssBaseline';
import Menu from "../Menu";
import Header from "../Header";
import Content from "../Content";
import ModalWindow from "../ModalWindow";
import theme from "../../theme";
import { useMatch } from "react-router-dom";
import useStore from "../../store";
const LoggedIn: React.FC = () => {
const handleOpen = useStore((state) => state.handleOpen);
const handleClose = useStore((state) => state.handleClose);
const match = useMatch('/modal');
match ? handleOpen() : handleClose()
const CenterBox: React.FC = () => {
return (
<React.Fragment>
<ThemeProvider theme={theme}>
@ -37,12 +48,14 @@ const CenterBox: React.FC = () => {
<Content />
</Box>
</Box>
</Box>
<ModalWindow />
</ThemeProvider>
</React.Fragment>
);
}
export default CenterBox;
export default LoggedIn;

@ -1,132 +0,0 @@
import * as React from "react";
import { Box, Modal, Fade, Backdrop, Typography, Button } from "@mui/material";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import theme from "../../theme";
const ModalWindow: React.FC = () => {
const [open, setOpen] = React.useState(false);
const handleOpen = () => setOpen(true);
const handleClose = () => setOpen(false);
return (
<React.Fragment>
<Button onClick={handleOpen}>Open modal</Button>
<Modal
aria-labelledby="transition-modal-title"
aria-describedby="transition-modal-description"
open={open}
onClose={handleClose}
closeAfterTransition
BackdropComponent={Backdrop}
BackdropProps={{
timeout: 500,
}}
>
<Fade in={open}>
<Box sx={{
position: "absolute" as "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
width: "90%",
height: "90%",
bgcolor: theme.palette.menu.main,
border: "2px solid #000",
boxShadow: 24,
color: theme.palette.secondary.main,
display: "flex",
flexDirection: "column",
alignItems: "center",
}}>
<Typography id="transition-modal-title" variant="caption">
Проект
</Typography>
<Box sx={{
width: "100%",
marginTop: "15px",
display: "flex"
}}>
<Box sx={{
backgroundColor: theme.palette.grayMedium.main,
width: "155px"
}}>
<Typography variant="h4" sx={{
backgroundColor: theme.palette.grayMedium.main,
width: "100%",
height: "55px", //205px
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
cursor: "pointer",
}}>
СТАТИСТИКА
</Typography>
<Typography variant="h4" sx={{
backgroundColor: theme.palette.grayMedium.main,
color: theme.palette.grayDisabled.main,
width: "100%",
height: "55px", //205px
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
cursor: "pointer",
textAlign: "center"
}}>
ТРЕКЕРЫ УСТРОЙСТВ
</Typography>
<Typography variant="h4" sx={{
backgroundColor: theme.palette.grayMedium.main,
color: theme.palette.grayDisabled.main,
width: "100%",
height: "55px", //205px
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
cursor: "pointer",
textAlign: "center"
}}>
ВВОДЫ
</Typography>
<Typography variant="h4" sx={{
backgroundColor: theme.palette.grayMedium.main,
color: theme.palette.grayDisabled.main,
width: "100%",
height: "55px", //205px
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
cursor: "pointer",
textAlign: "center"
}}>
ВЫВОДЫ
</Typography>
</Box>
<Box sx={{
backgroundColor: theme.palette.grayMedium.main,
width: "calc(100% - 155px)",
height: "55px",
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center"
}}>
Long text Long text Long text Long text Long text
Long text Long text Long text Long text Long text
Long text Long text Long text
</Box>
</Box>
</Box>
</Fade>
</Modal>
</React.Fragment>
);
}
export default ModalWindow;

@ -1,13 +1,29 @@
import * as React from "react";
import { Box, Typography } from "@mui/material";
import { ThemeProvider } from "@mui/material";
import { useNavigate } from "react-router-dom";
import { Box, Modal, Fade, Backdrop, Typography } from "@mui/material";
import theme from "../../theme";
import useStore from "../../store";
const ModalWindow: React.FC = () => {
const open = useStore((state) => state.open);
// const handleClose = useStore((state) => state.handleClose);
const navigate = useNavigate();
return (
<React.Fragment>
<ThemeProvider theme={theme}>
<Modal
aria-labelledby="transition-modal-title"
aria-describedby="transition-modal-description"
open={ open }
onClose={ () => navigate(-1) }
closeAfterTransition
BackdropComponent={Backdrop}
BackdropProps={{
timeout: 500,
}}
>
<Fade in={open}>
<Box sx={{
position: "absolute" as "absolute",
top: "50%",
@ -16,7 +32,6 @@ const ModalWindow: React.FC = () => {
width: "90%",
height: "90%",
bgcolor: theme.palette.menu.main,
border: "2px solid #000",
boxShadow: 24,
color: theme.palette.secondary.main,
display: "flex",
@ -106,7 +121,8 @@ const ModalWindow: React.FC = () => {
</Box>
</Box>
</Box>
</ThemeProvider>
</Fade>
</Modal>
</React.Fragment>
);
}

@ -1,6 +1,6 @@
import * as React from "react";
import { useNavigate } from "react-router-dom";
import { Box, Typography, TextField, Button } from "@mui/material";
import { Link } from "react-router-dom";
import Table from '@mui/material/Table';
import TableHead from '@mui/material/TableHead';
import TableBody from '@mui/material/TableBody';
@ -12,19 +12,17 @@ import theme from "../../theme";
const Users: React.FC = () => {
const [selectedValue, setSelectedValue] = React.useState('a');
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setSelectedValue(event.target.value);
};
const navigate = useNavigate();
return (
<React.Fragment>
<Link to={{
pathname: "/modal",
state: { modal: true }
}}>
<Button
variant="text"
onClick={ () => navigate("/modal") }
sx={{
width: "90%",
height: "60px",
@ -35,11 +33,13 @@ const Users: React.FC = () => {
border: "2px solid",
fontWeight: "normal",
borderColor: theme.palette.golden.main,
color: theme.palette.secondary.main
}} >
color: theme.palette.secondary.main,
"&:hover": {
backgroundColor: theme.palette.menu.main
}
}}>
ИНФОРМАЦИЯ О ПРОЕКТЕ
</Button>
</Link>
<Table sx={{

@ -4,7 +4,6 @@ import { BrowserRouter, Routes, Route } from "react-router-dom";
import Authorization from "./Components/Authorization";
import Sections from "./Components/Sections";
import LoggedIn from "./Components/LoggedIn";
import ModalWindow from "./Components/ModalWindow";
import Error404 from "./Components/Error404";
@ -17,7 +16,7 @@ root.render(
<Route path="/" element={ <Authorization /> } />
<Route path="/dispatch" element={ <Sections /> } />
<Route path="/users" element={ <LoggedIn /> } />
<Route path="/modal" element={ <ModalWindow /> } />
<Route path="/modal" element={ <LoggedIn /> } />
<Route
path="*"
element={ <Error404 /> }

19
src/store.ts Normal file

@ -0,0 +1,19 @@
import create from "zustand";
const useStore = create<StoreState>((set) => ({
open: false,
handleOpen: () => set({ open: true }),
handleClose: () => set({ open: false }),
openModal: () => { }
}))
interface StoreState {
open: boolean
handleOpen: () => void
handleClose: () => void
}
export default useStore;