Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1232ac13ac | ||
|
|
8090456baa | ||
|
|
c596e4764a | ||
|
|
482029ed76 | ||
|
|
3c4917138e |
Generated
+1
-1
@@ -2,7 +2,7 @@
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/emil28092005.github.io.iml" filepath="$PROJECT_DIR$/.idea/emil28092005.github.io.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/Pereslavl_dacha.iml" filepath="$PROJECT_DIR$/.idea/Pereslavl_dacha.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.9 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 7.1 MiB |
@@ -1,246 +0,0 @@
|
||||
const { useState, useEffect } = React;
|
||||
|
||||
// Функция для получения настроек сайта из HTML
|
||||
function getSiteConfigFromHTML() {
|
||||
const configElement = document.querySelector('#site-config site-header');
|
||||
if (!configElement) {
|
||||
return {
|
||||
bgImage: 'https://picsum.photos/1920/1080?random=1',
|
||||
title: 'Станция Сосновая',
|
||||
subtitle: 'База отдыха'
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
bgImage: configElement.getAttribute('data-bg-image') || 'https://picsum.photos/1920/1080?random=1',
|
||||
title: configElement.getAttribute('data-title') || 'Станция Сосновая',
|
||||
subtitle: configElement.getAttribute('data-subtitle') || 'База отдыха'
|
||||
};
|
||||
}
|
||||
|
||||
// Функция для получения данных карточек из HTML
|
||||
function getCardDataFromHTML() {
|
||||
const cardElements = document.querySelectorAll('#cards-data card-data');
|
||||
return Array.from(cardElements).map(element => {
|
||||
const images = element.getAttribute('data-images');
|
||||
return {
|
||||
title: element.getAttribute('data-title'),
|
||||
short: element.getAttribute('data-short'),
|
||||
images: images ? images.split(',').map(img => img.trim()) : [],
|
||||
long: element.getAttribute('data-long')
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// Функция для установки фонового изображения
|
||||
function setHeaderBackground(bgImage) {
|
||||
useEffect(() => {
|
||||
const headerBg = document.querySelector('.header-bg');
|
||||
if (headerBg && bgImage) {
|
||||
headerBg.style.backgroundImage = `url('${bgImage}')`;
|
||||
}
|
||||
}, [bgImage]);
|
||||
}
|
||||
|
||||
function Header() {
|
||||
const [siteConfig, setSiteConfig] = useState({
|
||||
bgImage: '',
|
||||
title: 'Станция Сосновая',
|
||||
subtitle: 'База отдыха'
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const config = getSiteConfigFromHTML();
|
||||
setSiteConfig(config);
|
||||
}, []);
|
||||
|
||||
// Устанавливаем фоновое изображение
|
||||
useEffect(() => {
|
||||
const headerBg = document.querySelector('.header-bg');
|
||||
if (headerBg && siteConfig.bgImage) {
|
||||
headerBg.style.backgroundImage = `url('${siteConfig.bgImage}')`;
|
||||
}
|
||||
}, [siteConfig.bgImage]);
|
||||
|
||||
return (
|
||||
<header className="header">
|
||||
<div className="header-bg"></div>
|
||||
<div className="header-overlay"></div>
|
||||
<div className="header-content">
|
||||
<h1>{siteConfig.title}</h1>
|
||||
<h2>{siteConfig.subtitle}</h2>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
function SafeImage({ src, alt, className, fallback = '/images/placeholder.jpg' }) {
|
||||
const [imgSrc, setImgSrc] = useState(src);
|
||||
|
||||
const handleError = () => {
|
||||
console.warn(`Не удалось загрузить изображение: ${src}`);
|
||||
setImgSrc(fallback);
|
||||
};
|
||||
|
||||
return (
|
||||
<img
|
||||
src={imgSrc}
|
||||
alt={alt}
|
||||
className={className}
|
||||
onError={handleError}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function Card({ card, onCardClick }) {
|
||||
return (
|
||||
<div className="card" onClick={() => onCardClick(card)}>
|
||||
<SafeImage
|
||||
src={card.images[0]}
|
||||
alt={card.title}
|
||||
className="card-image"
|
||||
/>
|
||||
<div className="card-content">
|
||||
<h3 className="card-title">{card.title}</h3>
|
||||
<p className="card-description">{card.short}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Modal({ card, isOpen, onClose }) {
|
||||
const [currentImageIndex, setCurrentImageIndex] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
setCurrentImageIndex(0);
|
||||
document.body.style.overflow = 'hidden';
|
||||
} else {
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
return () => (document.body.style.overflow = '');
|
||||
}, [isOpen, card]);
|
||||
|
||||
if (!isOpen || !card) return null;
|
||||
|
||||
const next = (e) => {
|
||||
e.stopPropagation();
|
||||
setCurrentImageIndex((i) => (i + 1) % card.images.length);
|
||||
};
|
||||
|
||||
const prev = (e) => {
|
||||
e.stopPropagation();
|
||||
setCurrentImageIndex((i) => (i - 1 + card.images.length) % card.images.length);
|
||||
};
|
||||
|
||||
const modalContent = (
|
||||
<div className="modal" onClick={(e) => e.target === e.currentTarget && onClose()}>
|
||||
<div className="modal-content">
|
||||
<div className="modal-header">
|
||||
<h2 className="modal-title">{card.title}</h2>
|
||||
<button className="close-button" onClick={onClose}>×</button>
|
||||
</div>
|
||||
<div className="carousel-container">
|
||||
<SafeImage
|
||||
src={card.images[currentImageIndex]}
|
||||
className="carousel-image"
|
||||
alt={card.title}
|
||||
/>
|
||||
{card.images.length > 1 && (
|
||||
<>
|
||||
<button className="carousel-button prev" onClick={prev}>‹</button>
|
||||
<button className="carousel-button next" onClick={next}>›</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<p className="modal-description">{card.long}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return ReactDOM.createPortal(modalContent, document.body);
|
||||
}
|
||||
|
||||
function CardsSection() {
|
||||
const [selected, setSelected] = useState(null);
|
||||
const [cardData, setCardData] = useState([]);
|
||||
const isOpen = Boolean(selected);
|
||||
|
||||
// Загружаем данные карточек из HTML при монтировании компонента
|
||||
useEffect(() => {
|
||||
const data = getCardDataFromHTML();
|
||||
setCardData(data);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<section className="cards-section">
|
||||
<div className="container">
|
||||
<h2 className="section-title">Что есть на участке</h2>
|
||||
<div className="cards-grid">
|
||||
{cardData.map((c, i) => (
|
||||
<Card key={i} card={c} onCardClick={setSelected} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<Modal card={selected} isOpen={isOpen} onClose={() => setSelected(null)} />
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function Footer() {
|
||||
const ContactLink = ({ href, children }) => (
|
||||
<a href={href} target="_blank" rel="noopener noreferrer" className="contact-item">
|
||||
{children}
|
||||
</a>
|
||||
);
|
||||
|
||||
const TelegramIcon = (
|
||||
<svg className="contact-icon" viewBox="0 0 24 24">
|
||||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm4.64 6.8c-.15 1.58-.8 5.42-1.13 7.19-.14.75-.42 1-.68 1.03-.58.05-1.02-.38-1.58-.75-.88-.58-1.38-.94-2.23-1.5-.99-.65-.35-1.01.22-1.59.15-.15 2.71-2.48 2.76-2.69a.2.2 0 0 0-.05-.18c-.06-.05-.14-.03-.21-.02-.09.02-1.49.95-4.22 2.79-.4.27-.76.41-1.08.4-.36-.01-1.04-.2-1.55-.37-.63-.2-1.12-.31-1.08-.66.02-.18.27-.36.74-.55 2.92-1.27 4.86-2.11 5.83-2.51 2.78-1.16 3.35-1.36 3.73-1.36.08 0 .27.02.39.12.1.08.13.19.14.27-.01.06.01.24 0 .38z"/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
const WhatsAppIcon = (
|
||||
<svg className="contact-icon" viewBox="0 0 24 24">
|
||||
<path d="M12.04 2c-5.46 0-9.91 4.45-9.91 9.91 0 1.75.46 3.45 1.32 4.95L2.05 22l5.25-1.38c1.45.79 3.08 1.21 4.74 1.21 5.46 0 9.91-4.45 9.91-9.91S17.5 2 12.04 2zm4.52 7.01c.2-.07.4-.02.4.24 0 .41-.12 2.28-.17 2.64-.05.36-.3.42-.6.26-.32-.18-1.37-.6-1.37-.6s-.72-.45-.94-.77c-.24-.32-.39-.66-.44-.87-.04-.2.07-.35.18-.46.1-.1.23-.15.23-.15s.18-.12.81.19c.47.23 1.17.8 1.17.8s.72.35.73.46c.01.11-.07.24-.07.24z"/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
const PhoneIcon = (
|
||||
<svg className="contact-icon" viewBox="0 0 24 24">
|
||||
<path d="M20.01 15.38c-1.23 0-2.42-.2-3.53-.56-.35-.12-.74-.03-1.01.24l-1.57 1.97c-2.83-1.35-5.48-3.9-6.89-6.83l1.95-1.66c.27-.28.35-.67.24-1.02-.37-1.11-.56-2.3-.56-3.53 0-.54-.45-.99-.99-.99H4.19C3.65 3 3 3.24 3 3.99 3 13.28 10.73 21 20.01 21c.71 0 .99-.63.99-1.18v-3.45c0-.54-.45-.99-.99-.99z"/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
return (
|
||||
<footer className="footer">
|
||||
<div className="container">
|
||||
<div className="footer-content">
|
||||
<ContactLink href="tel:+79123456789">
|
||||
{PhoneIcon} +7 (912) 345-67-89
|
||||
</ContactLink>
|
||||
<ContactLink href="https://t.me/stanciya_sosnovaya">
|
||||
{TelegramIcon} Telegram
|
||||
</ContactLink>
|
||||
<ContactLink href="https://wa.me/79123456789">
|
||||
{WhatsAppIcon} WhatsApp
|
||||
</ContactLink>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<CardsSection />
|
||||
<Footer />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
ReactDOM.render(<App />, document.getElementById('root'));
|
||||
@@ -0,0 +1,7 @@
|
||||
let cards = document.querySelectorAll('.card');
|
||||
|
||||
let phrase = "ABOBA";
|
||||
cards.addEventListener("click", function() {foo()})
|
||||
function foo(){
|
||||
document.querySelectorAll(".card__name").textContent = phrase;
|
||||
}
|
||||
-72
@@ -1,72 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Станция Сосновая - База отдыха</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<!-- React 18 CDN -->
|
||||
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
|
||||
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
|
||||
<!-- Babel for JSX transformation -->
|
||||
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<body>
|
||||
<!-- Настройки сайта - РЕДАКТИРОВАТЬ ЗДЕСЬ -->
|
||||
<div id="site-config" style="display: none;">
|
||||
<site-header
|
||||
data-bg-image="Images/cards/card1/20230928_224522.jpg"
|
||||
data-title="Станция Сосновая"
|
||||
data-subtitle="База отдыха">
|
||||
</site-header>
|
||||
</div>
|
||||
|
||||
<!-- Данные карточек - РЕДАКТИРОВАТЬ ЗДЕСЬ ДЛЯ ДОБАВЛЕНИЯ НОВЫХ КАРТОЧЕК -->
|
||||
<div id="cards-data" style="display: none;">
|
||||
<card-data
|
||||
data-title="Уютная беседка"
|
||||
data-short="Просторная беседка для отдыха на свежем воздухе"
|
||||
data-main-image="Images/cards/card1/20230928_224522.jpg"
|
||||
data-images="Images/cards/card1/20230928_224522.jpg,Images/cards/card1/360_0725_Stitch_XHC.jpg,Images/cards/20230928_224522.jpg"
|
||||
data-long="Просторная деревянная беседка на 10-12 человек, оборудованная столами и скамейками. Идеальное место для семейных обедов, игр и вечерних посиделок. Беседка защищена от дождя и ветра, имеет хорошее освещение. Рядом расположена зона для барбекю и мангала. В беседке есть розетки для подключения электроприборов.">
|
||||
</card-data>
|
||||
|
||||
<card-data
|
||||
data-title="Зона барбекю"
|
||||
data-short="Оборудованная площадка для приготовления шашлыков"
|
||||
data-main-image="/images/barbeku/barbeku1.jpg"
|
||||
data-images="/images/barbeku/barbeku1.jpg,/images/barbeku/barbeku2.jpg"
|
||||
data-long="Специально оборудованная зона для приготовления пищи на открытом огне. В наличии: стационарный мангал, решетки для гриля, столы для разделки продуктов. Рядом расположены мойка с проточной водой и место для хранения дров. Зона безопасна и соответствует всем противопожарным требованиям.">
|
||||
</card-data>
|
||||
|
||||
<card-data
|
||||
data-title="Детская площадка"
|
||||
data-short="Безопасная игровая зона для детей разных возрастов"
|
||||
data-main-image="/images/detskaya/detskaya1.jpg"
|
||||
data-images="/images/detskaya/detskaya1.jpg,/images/detskaya/detskaya2.jpg,/images/detskaya/detskaya3.jpg"
|
||||
data-long="Современная детская площадка с качелями, горками, песочницей и спортивными снарядами. Все оборудование изготовлено из экологически чистых материалов и регулярно проходит проверку безопасности. Площадка огорожена и имеет мягкое покрытие. Рядом установлены скамейки для родителей.">
|
||||
</card-data>
|
||||
|
||||
<card-data
|
||||
data-title="Парковка"
|
||||
data-short="Удобная парковочная зона для автомобилей гостей"
|
||||
data-main-image="/images/parkovka/parkovka1.jpg"
|
||||
data-images="/images/parkovka/parkovka1.jpg,/images/parkovka/parkovka2.jpg"
|
||||
data-long="Просторная парковочная зона на 8-10 автомобилей. Парковка имеет твердое покрытие, хорошо освещается в темное время суток. Въезд и выезд удобные, есть место для разворота. Парковочные места размечены, территория охраняется. Рядом с парковкой расположена основная тропинка к зоне отдыха.">
|
||||
</card-data>
|
||||
</div>
|
||||
|
||||
<div id="root"></div>
|
||||
|
||||
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
|
||||
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
|
||||
<script src="app.js"></script>
|
||||
<div id="root"></div>
|
||||
<!-- Adding cache-busting query param to ensure the latest JS is fetched -->
|
||||
<script type="text/babel" src="app.js?v=3"></script>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,82 @@
|
||||
*{
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
p{
|
||||
margin: 0;
|
||||
font-weight: normal;
|
||||
background-color: red;
|
||||
font-family: 'Sans', sans-serif;
|
||||
|
||||
}
|
||||
|
||||
div, header, main, footer {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
p {
|
||||
|
||||
}
|
||||
|
||||
.main-preview {
|
||||
text-align: center;
|
||||
background-color: blue;
|
||||
width: 100%;
|
||||
height: 80vh;
|
||||
}
|
||||
|
||||
.main-preview-image {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
section{
|
||||
background-color: darkred;
|
||||
margin: auto;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
.card {
|
||||
display: block;
|
||||
background-color: saddlebrown;
|
||||
margin: 20px 0 20px 0;
|
||||
padding: 20px;
|
||||
height: 600px;
|
||||
width: 80%;
|
||||
border: 1px solid lightgray;
|
||||
}
|
||||
|
||||
.snap-left{
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.snap-right{
|
||||
margin-left: auto;
|
||||
|
||||
}
|
||||
|
||||
.card-description {
|
||||
|
||||
}
|
||||
.card-head-block{
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
background-color: orange;
|
||||
width: 30%;
|
||||
}
|
||||
.card-description-box {
|
||||
height: 80%;
|
||||
width: 30%;
|
||||
display: inline-block;
|
||||
background-color: white;
|
||||
margin: 0 auto 0 auto;
|
||||
}
|
||||
|
||||
.card-image{
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
*{
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body ,header, section, footer{
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
h1,h2,h3,h4,h5,h6,p{
|
||||
text-shadow: black 4px 4px 3px;
|
||||
margin: 0;
|
||||
color: #e4c373;
|
||||
font-family: 'MesloLGS NF', sans-serif;
|
||||
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
font-weight: 600;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
p {
|
||||
font-weight: 550;
|
||||
}
|
||||
|
||||
.header {
|
||||
|
||||
text-align: center;
|
||||
background-color: saddlebrown;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.header__name {
|
||||
color: #bdad60;
|
||||
}
|
||||
|
||||
.header__description {
|
||||
color: #bdad60;
|
||||
line-height: 1;
|
||||
margin-bottom: 1vh;
|
||||
}
|
||||
|
||||
.header__image {
|
||||
display: inline-block;
|
||||
width: 95%;
|
||||
|
||||
}
|
||||
|
||||
.section {
|
||||
width: 100%;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
padding: 25px;
|
||||
justify-content: space-evenly;
|
||||
background: radial-gradient(circle, rgba(164,126,10,1) 0%, rgba(139,69,19,1) 100%);
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.card {
|
||||
display: inline-block;
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
margin: 10px;
|
||||
background-color: saddlebrown;
|
||||
}
|
||||
|
||||
.card__name {
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.card__image {
|
||||
display: block;
|
||||
margin: 0 auto ;
|
||||
width: 90%;
|
||||
border: solid 3px #2a1508;
|
||||
}
|
||||
|
||||
.card__description {
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.footer {
|
||||
background-color: saddlebrown;
|
||||
text-align: center;
|
||||
height: 25vh;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.header__image--framed{
|
||||
border: solid 5px #2a1508;
|
||||
box-shadow: 10px 10px 20px #2a1508;
|
||||
}
|
||||
.card--framed{
|
||||
border: solid 5px #2a1508;
|
||||
box-shadow: 10px 10px 20px #2a1508;
|
||||
}
|
||||
.footer--framed{
|
||||
border: solid 5px #2a1508;
|
||||
box-shadow: 10px 10px 20px #2a1508;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user