10 Commits
Author SHA1 Message Date
Emil Shanaty 612c4584ec . 2025-08-02 22:43:51 +03:00
Emil Shanaty 9eaf768b19 Add local images use functionality. 2025-08-02 22:29:40 +03:00
Emil Shanaty 0d1b256669 enchance design. Overhaul 2025-07-29 19:10:04 +03:00
Emil Shanaty da0291d940 Clear README.md 2024-10-13 10:40:28 +03:00
Emil Shanaty 067406719c Create README.md 2024-10-13 10:38:52 +03:00
emil28092005 d8172a721c nvmd 2024-08-31 09:13:29 +03:00
emil28092005 d91f43748b add header description ,some adjustments 2024-08-28 20:24:43 +03:00
emil28092005 0bf8afb865 MVP2 2024-08-28 19:42:25 +03:00
emil28092005 6ddf7543ad MVP2 2024-08-28 19:36:49 +03:00
emil28092005 3d838767d9 first commit 2024-08-27 19:48:14 +03:00
15 changed files with 1415 additions and 323 deletions
+1 -1
View File
@@ -2,7 +2,7 @@
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/Pereslavl_dacha.iml" filepath="$PROJECT_DIR$/.idea/Pereslavl_dacha.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/emil28092005.github.io.iml" filepath="$PROJECT_DIR$/.idea/emil28092005.github.io.iml" />
</modules>
</component>
</project>
Generated
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
<mapping directory="" vcs="Git" />
</component>
</project>
View File
+14 -14
View File
@@ -1,15 +1,15 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 24.1.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" baseProfile="basic" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
y="0px" viewBox="0 0 32 32" xml:space="preserve">
<g id="Layer_2" style="display:none;">
</g>
<g id="Layer_1">
<circle style="fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:10;" cx="16" cy="16" r="12"/>
<path style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;" d="M21.786,11
c-4.989-0.508-11.286,2.682-13.244,5.627c1.169,1.409,3.046,1.476,5.819,1.014c0.762,2.633,3.469,4.307,5.072,4.27
C21.27,20.308,22.521,13.439,21.786,11z"/>
<line style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;" x1="16.755" y1="15.434" x2="14.361" y2="17.641"/>
</g>
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 24.1.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" baseProfile="basic" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
y="0px" viewBox="0 0 32 32" xml:space="preserve">
<g id="Layer_2" style="display:none;">
</g>
<g id="Layer_1">
<circle style="fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:10;" cx="16" cy="16" r="12"/>
<path style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;" d="M21.786,11
c-4.989-0.508-11.286,2.682-13.244,5.627c1.169,1.409,3.046,1.476,5.819,1.014c0.762,2.633,3.469,4.307,5.072,4.27
C21.27,20.308,22.521,13.439,21.786,11z"/>
<line style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;" x1="16.755" y1="15.434" x2="14.361" y2="17.641"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 928 B

After

Width:  |  Height:  |  Size: 914 B

View File
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 MiB

+246
View File
@@ -0,0 +1,246 @@
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'));
+64 -60
View File
File diff suppressed because one or more lines are too long
-60
View File
File diff suppressed because one or more lines are too long
+1089
View File
File diff suppressed because it is too large Load Diff
-92
View File
@@ -1,92 +0,0 @@
*{
box-sizing: border-box;
}
body ,header, section, footer{
margin: 0;
}
h1,h2,h3,h4,h5,h6,p{
margin: 0;
color: #e4c373;
font-family: 'MesloLGS NF', sans-serif;
}
h1 {
text-align: center;
font-weight: 600;
margin: 0;
}
p {
font-weight: lighter;
}
.header {
text-align: center;
background-color: saddlebrown;
width: 100%;
}
.header-name{
color: #bdad60;
}
.header-image {
display: inline-block;
width: 100%;
}
.section {
width: 100%;
display: inline-block;
text-align: center;
justify-content: space-evenly;
background: #bdad60;
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 1px #2a1508;
}
.card-description {
margin: 0;
text-align: center;
}
.footer {
background-color: saddlebrown;
text-align: center;
height: 20vh;
width: 100%;
}
.fancy-external-border{
border: solid 5px #2a1508;
box-shadow: 10px 10px 20px #2a1508;
}
.telegram-icon {
}
-95
View File
@@ -1,95 +0,0 @@
*{
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: 22vh;
width: 100%;
}
.card--framed{
border: solid 5px #2a1508;
box-shadow: 10px 10px 20px #2a1508;
}