готовая 4лаба

This commit is contained in:
Никита Потапов 2023-12-08 15:37:06 +04:00
parent ad972b3004
commit 1e0a05314a
31 changed files with 619 additions and 39 deletions

13
favicon.svg Normal file
View File

@ -0,0 +1,13 @@
<svg width="142" height="72" viewBox="0 0 142 72" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0.955078 72V65.8477L8.91406 64.4805V8.47461L0.955078 7.10742V0.90625H26.4922V7.10742L18.5332 8.47461V33.0352H52.5176V8.47461L44.5586 7.10742V0.90625H52.5176H62.1367H70.0957V7.10742L62.1367 8.47461V64.4805L70.0957 65.8477V72H44.5586V65.8477L52.5176 64.4805V40.6035H18.5332V64.4805L26.4922 65.8477V72H0.955078ZM77.5176 72V65.8477L85.4766 64.4805V8.47461L77.5176 7.10742V0.90625H103.055V7.10742L95.0957 8.47461V32.9375H102.859L118.924 9.79297C121.561 6.14714 123.855 3.67318 125.809 2.37109C127.762 1.03646 130.024 0.369141 132.596 0.369141C134.126 0.369141 135.411 0.564453 136.453 0.955078C137.527 1.31315 138.65 1.85026 139.822 2.56641L138.406 9.35352C137.527 9.19076 136.746 9.06055 136.062 8.96289C135.411 8.83268 134.76 8.76758 134.109 8.76758C132.579 8.76758 131.277 9.17448 130.203 9.98828C129.161 10.8021 127.941 12.1855 126.541 14.1387L111.258 35.4277L134.891 64.627L141.58 65.8477V72H118.24V65.8477L123.66 65.1641L123.465 64.9199L104.275 41.043H95.0957V64.4805L103.055 65.8477V72H77.5176Z"/>
<style>
path {
fill: black;
}
@media (prefers-color-scheme: dark) {
path {
fill: white;
}
}
</style>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -3,6 +3,7 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="shortcut icon" href="favicon.svg" type="image/svg+xml" />
</head>
<body>
<div id="root" class="min-vh-100 d-flex flex-column"></div>

14
jsconfig.json Normal file
View File

@ -0,0 +1,14 @@
{
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "Node",
"target": "ES2020",
"jsx": "react",
"strictNullChecks": true,
"strictFunctionTypes": true
},
"exclude": [
"node_modules",
"**/node_modules/*"
]
}

View File

@ -1,15 +1,20 @@
import PropTypes from 'prop-types'
import Header from './components/header/header'
import Footer from './components/footer/footer'
import { Outlet } from 'react-router-dom';
import { Container } from 'react-bootstrap'
import BottomMenu from './components/bottommenu/bottommenu';
import LeftMenu from './components/leftmenu/leftmenu';
const App = ({ routes }) => {
return (
<>
<Header routes={routes}></Header>
<Container as="main" className="px-1 px-sm-2 px-lg-4 pt-2 pt-sm-4 d-flex position-relative flex-fill">
Привет
<LeftMenu routes={routes} />
<Outlet />
</Container>
<BottomMenu routes={routes} />
<Footer></Footer>
</>
);

BIN
src/assets/gachi.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
src/assets/korgi.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

View File

@ -0,0 +1,11 @@
.bottom-menu {
background-color: #235D70;
}
.bottom-menu * {
color: white;
}
.bottom-menu span {
font-size: smaller;
}

View File

@ -0,0 +1,26 @@
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
import './bottommenu.css';
const BottomMenu = ({ routes }) => {
const pages = routes.filter((route) => route.showInBottomMenu);
return (
<>
<div className="bottom-menu d-flex d-sm-none justify-content-around sticky-bottom">
{
pages.map((route) =>
<Link to={route.path} key={route.path} className="bottom-menu-item d-flex flex-column rounded-2 align-items-center py-1">
{route.menuIcon}
<span>{route.title}</span>
</Link>)
}
</div>
</>
);
};
BottomMenu.propTypes = {
routes: PropTypes.array,
};
export default BottomMenu;

View File

@ -0,0 +1,63 @@
.center {
width: 70%;
z-index: 0;
}
.user-message-trucated-text {
max-width: 60vw;
}
@media screen and (min-width: 992px) {
.user-message-trucated-text {
max-width: 30vw;
}
.center {
width: 40%;
}
}
@media screen and (max-width: 576px) {
.user-message-trucated-text {
max-width: 70vw;
}
.center {
width: 100%;
}
}
#post-editor {
min-height: calc(1em + 26px);
}
#title-image-preview {
width: 100%;
min-height: 100px;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
font-size: 50px;
cursor: pointer;
border: solid;
border-width: 1px;
}
#title-image-preview i {
opacity: 0.3;
transition: opacity .3s;
}
#title-image-preview:hover i {
opacity: 1.0;
}
#input-title-image {
display: none;
}
#selected-title-image {
width: 100%;
display: none;
}

View File

@ -0,0 +1,18 @@
import PropTypes from 'prop-types';
import './center.css';
const Center = ({ children }) => {
return (
<>
<div className="center d-flex flex-column">
{children}
</div>
</>
);
}
Center.propTypes = {
children: PropTypes.node,
};
export default Center;

View File

View File

@ -0,0 +1,24 @@
import './chatrow.css';
const ChatRow = () => {
return (
<>
<a className="people-row border-bottom border-dark p-2 d-flex justify-content-between align-items-center">
<div className="d-flex justify-content-start align-items-center" style={{ fontSize: "small" }}>
<img src="/src/assets/gachi.jpg" className="user-avatar avatar-small img-fluid rounded-circle" />
<div className="user-info ms-2 d-flex flex-column justify-content-center w-100">
<div className="user-name">Валерий Никифоров</div>
<div className="user-message-trucated-text px-2 py-1 rounded-3 text-truncate" style={{ fontSize: "smaller" }}>
Табличку по баллам сведу к понедельнику.
Больше никакие лабы присылать нельзя, литкод
можно. Плюс поверьте, что у вас 7 ответов по
лекциям
</div>
</div>
</div>
</a >
</>
);
};
export default ChatRow;

View File

View File

@ -0,0 +1,24 @@
import './friendrow.css'
const FriendRow = () => {
return (
<>
<div className="people-row border-bottom border-dark p-2 d-flex justify-content-between align-items-center">
<a className="d-flex justify-content-start align-items-center" style={{ fontSize: "small" }}>
<img src="/src/assets/gachi.jpg" className="user-avatar avatar-small img-fluid rounded-circle" />
<div className="user-info ms-2 d-flex flex-column justify-content-center">
<div className="user-name">
Валентина Лаврентьева
</div>
<div className="user-meta">
г. Калининград, 20 лет
</div>
</div>
</a>
<button className="btn btn-close" title="Удалить из друзей"></button>
</div>
</>
);
};
export default FriendRow;

View File

@ -8,8 +8,8 @@ const Header = ({ routes }) => {
const indexPageLink = routes.filter((route) => route.index === false).shift();
const currentPage = routes.filter((route) => route.path === location.pathname)[0];
return (
<header>
<Navbar className='sticky-top'>
<header className='sticky-top'>
<Navbar>
<Container fluid className='d-flex justify-content-between position-relative'>
<Navbar.Brand as={Link} to={indexPageLink?.path ?? '/'}>
<span className='logo-small d-sm-none'>

View File

@ -0,0 +1,11 @@
#left-menu {
--bs-border-color: var(--alternative-font-color);
}
.left-menu-item {
transition: .1s;
}
.left-menu-item:hover {
background-color: #e8e8e8;
}

View File

@ -0,0 +1,28 @@
import PropTypes from "prop-types";
import { Link } from 'react-router-dom';
import './leftmenu.css';
const LeftMenu = ({ routes }) => {
return (
<>
<div
id="left-menu"
className="p-3 d-none d-sm-flex flex-column justify-content-center align-content-around border rounded-2 position-fixed"
>
{
routes.map((route) =>
<Link to={route.path} key={route.path} className="left-menu-item d-flex flex-row align-items-center rounded-2 py-1 px-2">
{route.menuIcon}
<span className="ms-2 d-none d-lg-block">{route.title}</span>
</Link>)
}
</div>
</>
);
};
LeftMenu.propTypes = {
routes: PropTypes.array,
};
export default LeftMenu;

View File

@ -0,0 +1,45 @@
.post {
background-color: var(--alternative-backgrond-color);
--bs-border-color: var(--alternative-font-color);
}
.post-meta {
font-size: small;
}
.counter-block {
transition: .1s;
cursor: pointer;
}
.counter-block:hover {
background-color: #e8e8e8;
}
.post-body-img {
width: 100%;
}
.post-body a {
color: #4a9cb7;
text-decoration: none;
background-image: linear-gradient(currentColor, currentColor);
background-position: 0% 100%;
background-repeat: no-repeat;
background-size: 0% 2px;
transition: background-size .3s;
}
.post-body a:hover {
background-size: 100% 2px;
}
.post-body-text {
white-space: pre-wrap;
}
.avatar-small {
width: 35px;
height: 35px;
object-fit: cover;
}

View File

@ -0,0 +1,111 @@
import './post.css';
const Post = () => {
return (
<>
<div className="post mb-2 mb-sm-4 w-100 d-flex flex-column border rounded-2">
<div className="post-header py-1 px-2 d-flex border-bottom justify-content-between align-items-center">
<a className="d-flex justify-content-start align-items-center">
<div className="post-author-avatar-wrapper">
<img
src="/src/assets/gachi.jpg" className="post-author-avatar avatar-small rounded-circle" />
</div>
<div className="post-meta ms-2 d-flex flex-column justify-content-center">
<div className="post-author-name">
Алексей Смирнов
</div>
<div className="post-publication-datetime">
15 сентября, 15:22
</div>
</div>
</a>
<a>
<i className="bi bi-three-dots fs-4"></i>
</a>
</div>
<div className="post-body">
<div className="post-body-text m-2">
<p>
Корги королевская порода с дружелюбным
характером
</p>
<p>
Привет, друзья! Сегодня я хочу
рассказать вам о замечательной породе
собак корги. Эти милые создания с
короткими лапами и длинными ушами
покорили сердца многих людей, и не зря!
Корги это небольшие собаки, которые
произошли из Уэльса. Они были выведены
для охоты на зайцев и других мелких
животных.
</p>
</div>
<img src="/src/assets/korgi.jpg" className="post-body-img img-fluid" />
</div>
<div className="post-footer py-1 px-2 border-top d-flex">
<div className="counter-block likes-block px-2 me-1 d-flex align-items-center rounded-4">
<svg
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<g fill="none" fillRule="evenodd">
<path d="M0 0h24v24H0z"></path>
<path
d="M16 4a5.95 5.95 0 0 0-3.89 1.7l-.12.11-.12-.11A5.96 5.96 0 0 0 7.73 4 5.73 5.73 0 0 0 2 9.72c0 3.08 1.13 4.55 6.18 8.54l2.69 2.1c.66.52 1.6.52 2.26 0l2.36-1.84.94-.74c4.53-3.64 5.57-5.1 5.57-8.06A5.73 5.73 0 0 0 16.27 4zm.27 1.8a3.93 3.93 0 0 1 3.93 3.92v.3c-.08 2.15-1.07 3.33-5.51 6.84l-2.67 2.08a.04.04 0 0 1-.04 0L9.6 17.1l-.87-.7C4.6 13.1 3.8 11.98 3.8 9.73A3.93 3.93 0 0 1 7.73 5.8c1.34 0 2.51.62 3.57 1.92a.9.9 0 0 0 1.4-.01c1.04-1.3 2.2-1.91 3.57-1.91z"
fill="currentColor"
fillRule="nonzero"
></path>
</g>
</svg>
<span className="count ms-1">734</span>
</div>
<div
className="counter-block comments-block px-2 me-1 d-flex align-items-center rounded-4"
>
<svg
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<g fill="none" fillRule="evenodd">
<path d="M0 0h24v24H0z"></path>
<path
d="M16.9 4H7.1c-1.15 0-1.73.11-2.35.44-.56.3-1 .75-1.31 1.31C3.11 6.37 3 6.95 3 8.1v5.8c0 1.15.11 1.73.44 2.35.3.56.75 1 1.31 1.31l.15.07c.51.25 1.04.35 1.95.37h.25v2.21c0 .44.17.85.47 1.16l.12.1c.64.55 1.6.52 2.21-.08L13.37 18h3.53c1.15 0 1.73-.11 2.35-.44.56-.3 1-.75 1.31-1.31.33-.62.44-1.2.44-2.35V8.1c0-1.15-.11-1.73-.44-2.35a3.17 3.17 0 0 0-1.31-1.31A4.51 4.51 0 0 0 16.9 4zM6.9 5.8h9.99c.88 0 1.18.06 1.5.23.25.13.44.32.57.57.17.32.23.62.23 1.5v6.16c-.02.61-.09.87-.23 1.14-.13.25-.32.44-.57.57-.32.17-.62.23-1.5.23h-4.02a.9.9 0 0 0-.51.26l-3.47 3.4V17.1c0-.5-.4-.9-.9-.9H6.74a2.3 2.3 0 0 1-1.14-.23 1.37 1.37 0 0 1-.57-.57c-.17-.32-.23-.62-.23-1.5V7.74c.02-.61.09-.87.23-1.14.13-.25.32-.44.57-.57.3-.16.58-.22 1.31-.23z"
fill="currentColor"
fillRule="nonzero"
></path>
</g>
</svg>
<span className="count ms-1">344</span>
</div>
<div
className="counter-block replies-block px-2 d-flex align-items-center rounded-4"
>
<svg
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<g fill="none" fillRule="evenodd">
<path d="M0 0h24v24H0z"></path>
<path
d="M12 3.73c-1.12.07-2 1-2 2.14v2.12h-.02a9.9 9.9 0 0 0-7.83 10.72.9.9 0 0 0 1.61.46l.19-.24a9.08 9.08 0 0 1 5.84-3.26l.2-.03.01 2.5a2.15 2.15 0 0 0 3.48 1.69l7.82-6.14a2.15 2.15 0 0 0 0-3.38l-7.82-6.13c-.38-.3-.85-.46-1.33-.46zm.15 1.79c.08 0 .15.03.22.07l7.82 6.14a.35.35 0 0 1 0 .55l-7.82 6.13a.35.35 0 0 1-.57-.28V14.7a.9.9 0 0 0-.92-.9h-.23l-.34.02c-2.28.14-4.4.98-6.12 2.36l-.17.15.02-.14a8.1 8.1 0 0 1 6.97-6.53.9.9 0 0 0 .79-.9V5.87c0-.2.16-.35.35-.35z"
fill="currentColor"
fillRule="nonzero"
></path>
</g>
</svg>
<span className="count ms-1">1</span>
</div>
</div>
</div>
</>
);
};
export default Post;

View File

@ -0,0 +1,7 @@
#post-editor {
--bs-border-color: var(--alternative-font-color);
}
#title-image-block * {
--bs-border-color: var(--alternative-font-color);
}

View File

@ -0,0 +1,31 @@
import './postinputform.css';
import { Camera } from 'react-bootstrap-icons';
const PostInputForm = () => {
return (
<>
<textarea placeholder="Что нового?" id="post-editor" className="border rounded-2 mb-2 p-2"></textarea>
<div id="title-image-block">
<input id="check-title-image" type="checkbox" style={{ display: "none" }} />
<input id="input-title-image" className='border' name="titleImage" accept="image/*" type="file" />
<label id="title-image-preview" htmlFor="input-title-image" title="Добавить изображение" className="border rounded-2 mb-2">
<Camera id="empty-title-image" />
<img id="selected-title-image" className="rounded-2" />
</label>
</div>
<button className="btn btn-primary mb-2" id="post-publication-button">
Опубликовать
</button>
<div className="d-none d-flex mb-2 w-100" id="edit-block">
<button className="btn btn-danger me-2 w-100" id="edit-post-button-cancel">
Отмена
</button>
<button className="btn btn-success w-100" id="edit-post-button-accept">
Применить
</button>
</div >
</>
);
};
export default PostInputForm;

View File

@ -0,0 +1,35 @@
import './userprofileblock.css';
import { People, Calendar as Calendar, GeoAlt } from 'react-bootstrap-icons';
const UserProfileBlock = () => {
return (
<>
<div className="user-profile p-3 mb-2 mb-sm-4 w-100 d-flex flex-column flex-sm-row border rounded-2 border-dark">
<div className='d-flex align-items-center justify-content-center me-0 me-sm-3 mb-3 mb-sm-0'>
<img src="/src/assets/gachi.jpg" className="user-photo rounded-2 img-fluid" />
</div>
<div className="user-meta-wrapper d-flex flex-column">
<span className="fs-5 mb-2">Алексей Смирнов</span>
<span style={{ fontSize: "small" }} className="mb-2 fst-italic">
Прежде чем задать вопрос, подумай, хочешь ли ты
знать на него правдивый ответ...
</span>
<span style={{ fontSize: "small" }} className='d-flex align-items-center'>
<GeoAlt className='me-1' />
Ульяновск
</span>
<span style={{ fontSize: "small" }} className='d-flex align-items-center'>
<Calendar className='me-1' />
23.04.1998г.
</span>
<span style={{ fontSize: "small" }} className='d-flex align-items-center'>
<People className='me-1' />
234 друга
</span>
</div>
</div >
</>
);
};
export default UserProfileBlock;

View File

@ -0,0 +1,3 @@
.wrapper {
width: 100%;
}

View File

@ -0,0 +1,18 @@
import PropTypes from 'prop-types';
import './wrapper.css';
const Wrapper = ({ children }) => {
return (
<>
<div className="wrapper d-flex justify-content-center">
{children}
</div>
</>
);
}
Wrapper.propTypes = {
children: PropTypes.node,
};
export default Wrapper;

View File

@ -25,6 +25,7 @@
--dark-font-color: var(--color-white);
--light-font-color: var(--color-black);
--alternative-font-color: var(--color-black);
--alternative-backgrond-color: var(--light-background-color);
}
* {
@ -48,5 +49,6 @@ a {
--light-font-color: var(--color-white);
--dark-font-color: var(--color-white);
--alternative-font-color: var(--color-gray);
--alternative-backgrond-color: var(--color-black);
}
}

View File

@ -4,8 +4,12 @@ import ReactDOM from 'react-dom/client'
import { RouterProvider, createBrowserRouter } from 'react-router-dom'
import ErrorPage from './pages/ErrorPage.jsx'
import FeedPage from './pages/FeedPage.jsx'
import UserProfilePage from './pages/UserProfilePage.jsx'
import ChatPage from './pages/ChatPage.jsx'
import FriendsPage from './pages/FriendsPage.jsx'
import App from './App.jsx'
import './index.css'
import { Person, LayoutTextSidebarReverse, ChatText, People } from 'react-bootstrap-icons';
const routes = [
{
@ -13,6 +17,29 @@ const routes = [
path: '/',
element: <FeedPage />,
title: 'Новости',
menuIcon: <LayoutTextSidebarReverse />,
showInBottomMenu: true
},
{
path: '/me',
element: <UserProfilePage />,
title: 'Моя страница',
menuIcon: <Person />,
showInBottomMenu: false
},
{
path: '/chat',
element: <ChatPage />,
title: 'Сообщения',
menuIcon: <ChatText />,
showInBottomMenu: true
},
{
path: '/friends',
element: <FriendsPage />,
title: 'Друзья',
menuIcon: <People />,
showInBottomMenu: true
}
];

23
src/pages/ChatPage.jsx Normal file
View File

@ -0,0 +1,23 @@
import Wrapper from "../components/wrapper/wrapper";
import Center from "../components/center/center";
import ChatRow from "../components/chatrow/chatrow";
const ChatPage = () => {
return (
<>
<Wrapper>
<Center>
<ChatRow />
<ChatRow />
<ChatRow />
<ChatRow />
<ChatRow />
<ChatRow />
<ChatRow />
</Center>
</Wrapper>
</>
);
};
export default ChatPage;

View File

@ -1,43 +1,26 @@
import { Table } from 'react-bootstrap';
// import ulstuLogo from '../assets/logo.png';
import Wrapper from "../components/wrapper/wrapper";
import Center from "../components/center/center";
import PostInputForm from "../components/postinputform/postinputform";
import Post from "../components/post/post";
const Page2 = () => {
const FeedPage = () => {
return (
<>
<p className="text-center">
Вторая страница содержит пример рисунка (рис. 1) и таблицы (таб. 1).
</p>
<div className="text-center">
{/* <img src={ulstuLogo} alt="logo" width="128" /> */}
<br />
Рис. 1. Пример рисунка.
</div>
<div className="mt-3 row justify-content-center">
<Table className="w-50" bordered>
<caption>Таблица 1. Пример таблицы.</caption>
<thead>
<tr>
<th className="w-25">Номер</th>
<th>ФИО</th>
<th className="w-25">Телефон</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Иванов</td>
<td>89999999999</td>
</tr>
<tr>
<td>2</td>
<td>Петров</td>
<td>89999999991</td>
</tr>
</tbody>
</Table>
<Wrapper>
<Center>
<PostInputForm />
<div className="posts-wrapper">
<Post />
<Post />
<Post />
<Post />
<Post />
</div>
</Center>
</Wrapper>
</>
);
};
export default Page2;
export default FeedPage;

30
src/pages/FriendsPage.jsx Normal file
View File

@ -0,0 +1,30 @@
import Wrapper from "../components/wrapper/wrapper";
import Center from "../components/center/center";
import { Search } from "react-bootstrap-icons";
import FriendRow from "../components/friendrow/friendrow";
const FriendsPage = () => {
return (
<>
<Wrapper>
<Center>
<div className="input-group mb-3">
<input type="text" className="form-control" placeholder="Найти друзей" />
<button className="btn btn-outline-secondary" type="button" id="button-addon">
<Search />
</button>
</div>
<FriendRow />
<FriendRow />
<FriendRow />
<FriendRow />
<FriendRow />
<FriendRow />
<FriendRow />
</Center>
</Wrapper>
</>
);
};
export default FriendsPage;

View File

@ -0,0 +1,27 @@
import Wrapper from "../components/wrapper/wrapper";
import Center from "../components/center/center";
import PostInputForm from "../components/postinputform/postinputform";
import Post from "../components/post/post";
import UserProfileBlock from "../components/userprofileblock/userprofileblock";
const UserProfilePage = () => {
return (
<>
<Wrapper>
<Center>
<UserProfileBlock />
<PostInputForm />
<div className="posts-wrapper">
<Post />
<Post />
<Post />
<Post />
<Post />
</div>
</Center>
</Wrapper>
</>
);
};
export default UserProfilePage;