add shared buttons in post
This commit is contained in:
@@ -3,183 +3,189 @@ import { getLatestColonPost } from '@lib/api/colon-posts';
|
||||
import Author from '@components/AuthorDisplay.astro';
|
||||
|
||||
const colonPost = await getLatestColonPost();
|
||||
|
||||
---
|
||||
|
||||
{colonPost && (
|
||||
<div class="colon-post-card">
|
||||
<div class="colon-post-content">
|
||||
<div class="colon-post-left">
|
||||
{colonPost.featuredImage?.node?.sourceUrl && (
|
||||
<a href={colonPost.uri}>
|
||||
<div class="split-flex">
|
||||
<!-- ЛЕВЫЙ БЛОК: 30% ширины, фото впритык -->
|
||||
<div class="left-photo">
|
||||
{colonPost.featuredImage?.node?.sourceUrl ? (
|
||||
<a href={colonPost.uri} class="photo-link">
|
||||
<img
|
||||
src={colonPost.featuredImage.node.sourceUrl}
|
||||
alt={colonPost.featuredImage.node.altText || colonPost.secondaryTitle || colonPost.title}
|
||||
loading="lazy"
|
||||
class="colon-post-image"
|
||||
class="photo-img"
|
||||
/>
|
||||
</a>
|
||||
) : (
|
||||
<div class="photo-placeholder"></div>
|
||||
)}
|
||||
|
||||
<div class="colon-post-meta">
|
||||
<span>{new Date(colonPost.date).toLocaleDateString('ru-RU')}</span>
|
||||
<div><Author post={colonPost} separator=", " /></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="colon-post-right">
|
||||
<a href={colonPost.uri}>
|
||||
<h3>{colonPost.secondaryTitle || colonPost.title}</h3>
|
||||
</a>
|
||||
|
||||
<!-- ПРАВЫЙ БЛОК: 70% ширины, заголовок сверху, дата|автор снизу -->
|
||||
<div class="right-content">
|
||||
<div class="content-wrapper">
|
||||
<!-- Заголовок жирным сверху -->
|
||||
<a href={colonPost.uri} class="title-link">
|
||||
<h3 class="bold-title">{colonPost.secondaryTitle || colonPost.title}</h3>
|
||||
</a>
|
||||
|
||||
<!-- Мета-информация внизу: дата | автор -->
|
||||
<div class="meta-line">
|
||||
<span class="date">{new Date(colonPost.date).toLocaleDateString('ru-RU')}</span>
|
||||
<span class="separator">|</span>
|
||||
<span class="author"><Author post={colonPost} separator=", " /></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<style>
|
||||
/* Основная карточка */
|
||||
.colon-post-card {
|
||||
background: white;
|
||||
background: #ececec;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||
width: 100%;
|
||||
max-width: 800px; /* опционально, для демо */
|
||||
height: 200px; /* фиксированная высота карточки */
|
||||
}
|
||||
|
||||
|
||||
.colon-post-card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.colon-post-content {
|
||||
|
||||
/* Flex-контейнер: две части */
|
||||
.split-flex {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
padding: 20px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.colon-post-left {
|
||||
flex: 0 0 200px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.colon-post-image-link {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.colon-post-image {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
object-fit: cover;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.colon-post-image:hover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.colon-post-image-placeholder {
|
||||
|
||||
/* ЛЕВЫЙ БЛОК: ровно 30% */
|
||||
.left-photo {
|
||||
flex: 0 0 34%; /* ширина 30%, не растягивается */
|
||||
height: 100%;
|
||||
background: #d4d4d4; /* фон, если нет фото */
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.photo-link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
||||
border-radius: 6px;
|
||||
margin-bottom: 10px;
|
||||
height: 100%;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.placeholder-text {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
|
||||
.photo-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover; /* заполняет контейнер, сохраняя пропорции и обрезаясь */
|
||||
display: block;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.colon-post-meta {
|
||||
|
||||
.photo-img:hover {
|
||||
transform: scale(1.05); /* легкий эффект при наведении */
|
||||
}
|
||||
|
||||
.photo-placeholder {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
||||
}
|
||||
|
||||
/* ПРАВЫЙ БЛОК: 70% */
|
||||
.right-content {
|
||||
flex: 1; /* занимает оставшееся место (70%) */
|
||||
height: 100%;
|
||||
padding: 16px 20px; /* внутренние отступы */
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* Обёртка для контента, чтобы занять всю высоту и распределить пространство */
|
||||
.content-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Заголовок жирным */
|
||||
.bold-title {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 700;
|
||||
line-height: 1.4;
|
||||
color: #2c3e50;
|
||||
margin: 0 0 8px 0;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.colon-post-date {
|
||||
white-space: nowrap;
|
||||
|
||||
.title-link {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.meta-separator {
|
||||
margin: 0 5px;
|
||||
color: #ccc;
|
||||
|
||||
.title-link:hover .bold-title {
|
||||
color: #3498db;
|
||||
}
|
||||
|
||||
.colon-post-author {
|
||||
white-space: nowrap;
|
||||
|
||||
/* Мета-строка: прижимаем к низу */
|
||||
.meta-line {
|
||||
font-size: 0.9rem;
|
||||
color: #666;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-top: auto; /* это прижимает мету к низу */
|
||||
}
|
||||
|
||||
.author-link {
|
||||
|
||||
.separator {
|
||||
color: #aaa;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.author :global(a) {
|
||||
color: #2c3e50;
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.author-link:hover {
|
||||
|
||||
.author :global(a:hover) {
|
||||
color: #3498db;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.colon-post-right {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.colon-post-title-link {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.colon-post-title {
|
||||
margin: 0 0 10px 0;
|
||||
font-size: 20px;
|
||||
line-height: 1.4;
|
||||
color: #2c3e50;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.colon-post-title:hover {
|
||||
color: #3498db;
|
||||
}
|
||||
|
||||
.colon-post-secondary-title {
|
||||
font-size: 16px;
|
||||
color: #666;
|
||||
line-height: 1.5;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
|
||||
/* Адаптивность */
|
||||
@media (max-width: 768px) {
|
||||
.colon-post-content {
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
@media (max-width: 600px) {
|
||||
.colon-post-card {
|
||||
height: auto;
|
||||
min-height: 180px;
|
||||
}
|
||||
|
||||
.colon-post-left {
|
||||
flex: 0 0 auto;
|
||||
width: 100%;
|
||||
|
||||
.left-photo {
|
||||
flex: 0 0 30%;
|
||||
aspect-ratio: 1 / 1; /* сохраняем квадрат на мобильных */
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.colon-post-image,
|
||||
.colon-post-image-placeholder {
|
||||
height: 200px;
|
||||
|
||||
.right-content {
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
.colon-post-title {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.colon-post-secondary-title {
|
||||
font-size: 15px;
|
||||
|
||||
.bold-title {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
/* Если нужно точное соответствие макету, можно добавить медиа-запросы под свои нужды */
|
||||
</style>
|
||||
@@ -2,7 +2,6 @@
|
||||
import { getLatestAnews } from '../lib/api/posts.js';
|
||||
import { fetchWPRestGet } from "@/lib/api/wp-rest-get-client";
|
||||
|
||||
|
||||
// Даты/время
|
||||
function formatDate(dateString: string): string {
|
||||
const date = new Date(dateString);
|
||||
@@ -51,118 +50,129 @@ const hasNews = newsPosts.length > 0;
|
||||
const hasTop = topPosts.length > 0;
|
||||
|
||||
// По умолчанию открываем вкладку, где есть данные
|
||||
const defaultTab: "news" | "top" = hasTop ? "top" : "news";
|
||||
const defaultTab: "news" | "top" = hasNews ? "news" : "top";
|
||||
---
|
||||
|
||||
{(hasNews || hasTop) && (
|
||||
<div class="endnews-container">
|
||||
<div class="endnews-header">
|
||||
<h4 class="endnews-title">Новости</h4>
|
||||
</div>
|
||||
|
||||
{/* Radio-кнопки ВНЕ .endnews-tabs для работы CSS-селекторов */}
|
||||
<input
|
||||
class="endnews-tab-input"
|
||||
type="radio"
|
||||
name="endnews-tab"
|
||||
id="endnews-tab-news"
|
||||
checked={defaultTab === "news"}
|
||||
/>
|
||||
<input
|
||||
class="endnews-tab-input"
|
||||
type="radio"
|
||||
name="endnews-tab"
|
||||
id="endnews-tab-top"
|
||||
checked={defaultTab === "top"}
|
||||
/>
|
||||
|
||||
{/* Только labels в блоке табов */}
|
||||
<div class="endnews-container" id="endnews-container">
|
||||
{/* Табы на всю ширину, каждый по 50% - теперь div'ы */}
|
||||
<div class="endnews-tabs">
|
||||
<label class="endnews-tab-label" for="endnews-tab-news">Новости</label>
|
||||
<label class="endnews-tab-label" for="endnews-tab-top">Топ‑10</label>
|
||||
<div class={`endnews-tab-label ${defaultTab === 'news' ? 'active' : ''}`} data-tab="news">Новости</div>
|
||||
<div class={`endnews-tab-label ${defaultTab === 'top' ? 'active' : ''}`} data-tab="top">Топ‑10</div>
|
||||
</div>
|
||||
|
||||
{/* Контент: две панели, показываем нужную через :checked */}
|
||||
{/* Контент: две панели */}
|
||||
<div class="latestnews-list">
|
||||
<section class="endnews-panel endnews-panel--news" aria-label="Новости">
|
||||
<section class={`endnews-panel endnews-panel--news ${defaultTab === 'news' ? 'active' : ''}`} aria-label="Новости">
|
||||
{Object.entries(groupedNews).map(([dateKey, datePosts]) => (
|
||||
<div class="latestnews-date-group" key={dateKey}>
|
||||
<div class="latestnews-date">{formatDate(dateKey + 'T00:00:00')}</div>
|
||||
|
||||
{datePosts.map((post) => (
|
||||
<article class="lastnews-item" key={post.uri}>
|
||||
<div class="lastnews-time">{formatTime(post.date)}</div>
|
||||
<div class="lastnews-content">
|
||||
<a href={post.uri || '#'} class="endnews-link">
|
||||
{post.title}
|
||||
</a>
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
|
||||
{/* Переделано в ul > li */}
|
||||
<ul class="latestnews-items">
|
||||
{datePosts.map((post) => (
|
||||
<li class="lastnews-item" key={post.uri}>
|
||||
<div class="lastnews-time">{formatTime(post.date)}</div>
|
||||
<div class="lastnews-content">
|
||||
<a href={post.uri || '#'} class="endnews-link">
|
||||
{post.title}
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
))}
|
||||
</section>
|
||||
|
||||
<section class="endnews-panel endnews-panel--top" aria-label="Топ-10">
|
||||
{topPosts.map((post, i) => (
|
||||
<article class="lastnews-item" key={(post.uri ?? post.link ?? post.title) + i}>
|
||||
<div class="ltopnews-content">
|
||||
<a href={post.uri ?? post.link ?? '#'} class="endnews-link">
|
||||
{post.title}
|
||||
</a>
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
|
||||
{!hasTop && <div class="endnews-empty">Топ пока пуст</div>}
|
||||
<section class={`endnews-panel endnews-panel--top ${defaultTab === 'top' ? 'active' : ''}`} aria-label="Топ-10">
|
||||
{topPosts.length > 0 ? (
|
||||
<ul class="latestnews-items top-items">
|
||||
{topPosts.map((post, i) => (
|
||||
<li class="lastnews-item top-item" key={(post.uri ?? post.link ?? post.title) + i}>
|
||||
<div class="lastnews-content">
|
||||
<a href={post.uri ?? post.link ?? '#'} class="endnews-link">
|
||||
{post.title}
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
<div class="endnews-empty">Топ пока пуст</div>
|
||||
)}
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const container = document.getElementById('endnews-container');
|
||||
if (!container) return;
|
||||
|
||||
const tabs = container.querySelectorAll('.endnews-tab-label');
|
||||
const panels = {
|
||||
news: container.querySelector('.endnews-panel--news'),
|
||||
top: container.querySelector('.endnews-panel--top')
|
||||
};
|
||||
|
||||
tabs.forEach(tab => {
|
||||
tab.addEventListener('click', () => {
|
||||
const tabName = tab.dataset.tab;
|
||||
|
||||
// Обновляем классы на табах
|
||||
tabs.forEach(t => t.classList.remove('active'));
|
||||
tab.classList.add('active');
|
||||
|
||||
// Обновляем классы на панелях
|
||||
Object.values(panels).forEach(panel => {
|
||||
if (panel) panel.classList.remove('active');
|
||||
});
|
||||
if (panels[tabName]) panels[tabName].classList.add('active');
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.endnews-container { width:100%; }
|
||||
|
||||
.endnews-header{
|
||||
padding:12px 16px;
|
||||
background:#B61D1D;
|
||||
border-radius:6px 6px 0 0;
|
||||
}
|
||||
.endnews-title{ color:#fff; margin:0; font-size:1.1rem; font-weight:600; }
|
||||
|
||||
/* Скрываем radio-кнопки */
|
||||
.endnews-tab-input{
|
||||
position:absolute;
|
||||
opacity:0;
|
||||
pointer-events:none;
|
||||
}
|
||||
|
||||
/* Tabs */
|
||||
/* Tabs на всю ширину */
|
||||
.endnews-tabs{
|
||||
display:flex;
|
||||
gap:8px;
|
||||
padding:10px 12px;
|
||||
border:1px solid #ECECEC;
|
||||
border-top:none;
|
||||
background:#fff;
|
||||
}
|
||||
.endnews-tab-label{
|
||||
cursor:pointer;
|
||||
user-select:none;
|
||||
font-size:0.85rem;
|
||||
font-weight:700;
|
||||
padding:8px 10px;
|
||||
border-radius:6px;
|
||||
background:#f5f5f5;
|
||||
color:#505258;
|
||||
transition: background 0.2s, color 0.2s;
|
||||
width:100%;
|
||||
background: #ECECEC;
|
||||
border-radius: 6px 6px 0 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Активный таб - используем общий сиблинг-селектор */
|
||||
#endnews-tab-news:checked ~ .endnews-tabs label[for="endnews-tab-news"],
|
||||
#endnews-tab-top:checked ~ .endnews-tabs label[for="endnews-tab-top"]{
|
||||
background:#B61D1D;
|
||||
color:#fff;
|
||||
.endnews-tab-label{
|
||||
flex: 1 1 0;
|
||||
width: 50%;
|
||||
min-width: 50%;
|
||||
max-width: 50%;
|
||||
cursor:pointer;
|
||||
user-select:none;
|
||||
font-size:0.95rem;
|
||||
font-weight:400;
|
||||
padding:12px 10px;
|
||||
text-align: center;
|
||||
background: #ECECEC;
|
||||
color: #BFBFBF;
|
||||
transition: background 0.2s, color 0.2s, font-weight 0.2s, border 0.2s;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid transparent;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
/* Активный таб - черный жирный на белом фоне */
|
||||
.endnews-tab-label.active{
|
||||
background: #FFFFFF;
|
||||
color: #000000;
|
||||
font-weight: 700;
|
||||
border: 1px solid #ECECEC;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
/* list container */
|
||||
@@ -171,17 +181,40 @@ const defaultTab: "news" | "top" = hasTop ? "top" : "news";
|
||||
border-top:none;
|
||||
border-radius:0 0 6px 6px;
|
||||
background:#fff;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
|
||||
/* Скрываем скроллбар */
|
||||
.latestnews-list::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Панели скрыты по умолчанию */
|
||||
.endnews-panel{ display:none; }
|
||||
|
||||
/* Показываем нужную панель по выбранному radio */
|
||||
#endnews-tab-news:checked ~ .latestnews-list .endnews-panel--news{ display:block; }
|
||||
#endnews-tab-top:checked ~ .latestnews-list .endnews-panel--top { display:block; }
|
||||
/* Показываем активную панель */
|
||||
.endnews-panel.active{ display:block; }
|
||||
|
||||
/* Существующий стиль списка */
|
||||
.latestnews-date-group{ padding:12px 16px; border-bottom:1px solid #f0f0f0; }
|
||||
/* Стили для списка */
|
||||
.latestnews-items {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.latestnews-items li{
|
||||
font-size: 0.8125rem;
|
||||
line-height: 1.2;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.latestnews-date-group{
|
||||
padding:12px 16px;
|
||||
border-bottom:1px solid #f0f0f0;
|
||||
}
|
||||
.latestnews-date-group:last-child{ border-bottom:none; }
|
||||
|
||||
.latestnews-date{
|
||||
@@ -212,6 +245,46 @@ const defaultTab: "news" | "top" = hasTop ? "top" : "news";
|
||||
margin-top:1px;
|
||||
}
|
||||
|
||||
/* Стили для топ-новостей */
|
||||
.top-items {
|
||||
padding: 8px 0 0 0;
|
||||
}
|
||||
|
||||
.top-item {
|
||||
position: relative;
|
||||
padding: 10px 0 10px 16px;
|
||||
margin: 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.top-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.top-item::before {
|
||||
content: '';
|
||||
background: #b51d1d;
|
||||
width: 3px;
|
||||
height: 20px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
-webkit-transform: translate(0, -50%);
|
||||
-ms-transform: translate(0,-50%);
|
||||
transform: translate(0, -50%);
|
||||
}
|
||||
|
||||
.top-item .lastnews-content {
|
||||
margin-left: 0;
|
||||
padding-right: 16px;
|
||||
}
|
||||
|
||||
.lastnews-time-top{
|
||||
min-width:2.5rem;
|
||||
flex-shrink:0;
|
||||
}
|
||||
|
||||
.lastnews-content{
|
||||
line-height:1.3;
|
||||
font-weight:500;
|
||||
@@ -221,18 +294,6 @@ const defaultTab: "news" | "top" = hasTop ? "top" : "news";
|
||||
flex:1;
|
||||
}
|
||||
|
||||
.topnews-content{
|
||||
|
||||
line-height:1.3;
|
||||
font-weight:500;
|
||||
font-size:0.9em;
|
||||
color:#000;
|
||||
transition:color 0.2s;
|
||||
margin-left: 4px;
|
||||
|
||||
}
|
||||
|
||||
|
||||
.endnews-link{ color:inherit; text-decoration:none; display:block; }
|
||||
.endnews-link:hover{ color:#B61D1D; }
|
||||
|
||||
@@ -244,14 +305,36 @@ const defaultTab: "news" | "top" = hasTop ? "top" : "news";
|
||||
}
|
||||
.endnews-empty{ padding:12px 16px; color:#6b6d72; font-size:0.85rem; }
|
||||
|
||||
/* Десктоп скролл */
|
||||
/* Десктоп версия */
|
||||
@media (min-width:1024px){
|
||||
.endnews-container{ height:500px; display:flex; flex-direction:column; }
|
||||
.latestnews-list{ flex:1; overflow-y:auto; overflow-x:hidden; }
|
||||
.latestnews-list{ flex:1; }
|
||||
}
|
||||
|
||||
/* Мобильная версия */
|
||||
@media (max-width:1023px){
|
||||
.endnews-container{ height:auto; }
|
||||
.latestnews-list{ overflow:visible; }
|
||||
|
||||
/* Скрываем вкладку Топ-10 на мобильных */
|
||||
.endnews-tab-label[data-tab="top"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Оставляем только вкладку Новости на всю ширину */
|
||||
.endnews-tab-label[data-tab="news"] {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
/* На мобильных всегда показываем панель новостей */
|
||||
.endnews-panel--top {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.endnews-panel--news {
|
||||
display: block !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
</style>
|
||||
@@ -17,24 +17,41 @@ if (!menu) {
|
||||
---
|
||||
|
||||
<nav class="primary-nav" aria-label="Main navigation">
|
||||
<div class="primary-nav__content">
|
||||
<button class="primary-nav__burger" aria-label="Toggle menu"></button>
|
||||
<ul class="primary-nav__list">
|
||||
{menu.menuItems.nodes.map(item => {
|
||||
const colorClass = item.menuItemColor ? `color-${item.menuItemColor}` : 'color-black';
|
||||
return (
|
||||
<li class="primary-nav__item" key={item.id}>
|
||||
<a
|
||||
href={item.url}
|
||||
class={`primary-nav__link ${colorClass}`}
|
||||
target={item.target || '_self'}
|
||||
>
|
||||
{item.label}
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
<div class="primary-nav__wrapper">
|
||||
<div class="primary-nav__content">
|
||||
|
||||
<button class="primary-nav__burger" aria-label="Toggle menu"></button>
|
||||
|
||||
<!-- Логотип для фиксированного меню -->
|
||||
<div class="primary-nav__logo-scroll">
|
||||
<a href="/" aria-label="На главную">
|
||||
<img
|
||||
alt="Профиль"
|
||||
width="120"
|
||||
height="27"
|
||||
src="https://cdn.profile.ru/wp-content/themes/profile/assets/img/profile-logo-delovoy.svg"
|
||||
class="primary-nav__logo-image"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<ul class="primary-nav__list">
|
||||
{menu.menuItems.nodes.map(item => {
|
||||
const colorClass = item.menuItemColor ? `color-${item.menuItemColor}` : 'color-black';
|
||||
return (
|
||||
<li class="primary-nav__item" key={item.id}>
|
||||
<a
|
||||
href={item.url}
|
||||
class={`primary-nav__link ${colorClass}`}
|
||||
target={item.target || '_self'}
|
||||
>
|
||||
{item.label}
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
@@ -43,16 +60,88 @@ if (!menu) {
|
||||
<style>
|
||||
.primary-nav {
|
||||
margin: 12px 0;
|
||||
position: relative;
|
||||
border-top: 1px solid black;
|
||||
border-bottom: 1px solid black;
|
||||
background-color: white;
|
||||
width: 100%;
|
||||
transition: all 0.3s ease;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
|
||||
/* Стили только для десктопа */
|
||||
@media (min-width: 768px) {
|
||||
.primary-nav.fixed {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: 0;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
/* Добавляем отступ для контента, когда меню фиксированное */
|
||||
.primary-nav.fixed + * {
|
||||
margin-top: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Обертка для центрирования контента */
|
||||
.primary-nav__wrapper {
|
||||
width: 100%;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
/* При фиксированном меню обертка тоже фиксируется */
|
||||
@media (min-width: 768px) {
|
||||
.primary-nav.fixed .primary-nav__wrapper {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
padding: 0 20px; /* Добавляем отступы по бокам */
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
||||
.primary-nav__content {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
position: relative;
|
||||
min-height: 48px;
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* Для десктопа добавляем ограничение ширины */
|
||||
@media (min-width: 768px) {
|
||||
.primary-nav__content {
|
||||
max-width: 1200px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Стили для логотипа, появляющегося при скролле */
|
||||
.primary-nav__logo-scroll {
|
||||
display: none;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
margin-right: 20px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* Показываем логотип только когда меню фиксированное и на десктопе */
|
||||
@media (min-width: 768px) {
|
||||
.primary-nav.fixed .primary-nav__logo-scroll {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
opacity: 1;
|
||||
margin-left: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.primary-nav__logo-image {
|
||||
display: block;
|
||||
width: auto;
|
||||
height: 27px;
|
||||
}
|
||||
|
||||
.primary-nav__burger {
|
||||
@@ -112,3 +201,89 @@ if (!menu) {
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const primaryNav = document.querySelector('.primary-nav');
|
||||
const header = document.querySelector('.header');
|
||||
|
||||
if (!primaryNav || !header) return;
|
||||
|
||||
// Проверяем, что это десктоп
|
||||
let isDesktop = window.innerWidth >= 768;
|
||||
|
||||
// Убираем ранний return, чтобы эффект мог включаться/выключаться при ресайзе
|
||||
|
||||
// Создаем плейсхолдер для сохранения места при фиксированном меню
|
||||
const placeholder = document.createElement('div');
|
||||
placeholder.className = 'primary-nav-placeholder';
|
||||
placeholder.style.display = 'none';
|
||||
placeholder.style.height = primaryNav.offsetHeight + 'px';
|
||||
placeholder.style.width = '100%';
|
||||
placeholder.style.backgroundColor = 'transparent';
|
||||
|
||||
// Вставляем плейсхолдер после меню
|
||||
if (primaryNav.parentNode) {
|
||||
primaryNav.parentNode.insertBefore(placeholder, primaryNav.nextSibling);
|
||||
}
|
||||
|
||||
function handleScroll() {
|
||||
// Применяем эффект только на десктопе
|
||||
if (!isDesktop) return;
|
||||
|
||||
const topBar = document.querySelector('.top-bar');
|
||||
if (!topBar) return;
|
||||
|
||||
const topBarHeight = topBar.offsetHeight;
|
||||
const scrollPosition = window.scrollY;
|
||||
|
||||
if (scrollPosition > topBarHeight) {
|
||||
if (!primaryNav.classList.contains('fixed')) {
|
||||
primaryNav.classList.add('fixed');
|
||||
if (placeholder.parentNode) {
|
||||
placeholder.style.display = 'block';
|
||||
// Обновляем высоту плейсхолдера
|
||||
placeholder.style.height = primaryNav.offsetHeight + 'px';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (primaryNav.classList.contains('fixed')) {
|
||||
primaryNav.classList.remove('fixed');
|
||||
if (placeholder.parentNode) {
|
||||
placeholder.style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('scroll', handleScroll);
|
||||
|
||||
// Вызываем сразу для правильного начального состояния
|
||||
handleScroll();
|
||||
|
||||
// Обновляем при изменении размера окна
|
||||
window.addEventListener('resize', function() {
|
||||
const newIsDesktop = window.innerWidth >= 768;
|
||||
|
||||
if (newIsDesktop !== isDesktop) {
|
||||
isDesktop = newIsDesktop;
|
||||
if (!isDesktop) {
|
||||
// Стали мобильным - убираем эффект
|
||||
primaryNav.classList.remove('fixed');
|
||||
if (placeholder.parentNode) {
|
||||
placeholder.style.display = 'none';
|
||||
}
|
||||
} else {
|
||||
// Стали десктопом - проверяем скролл
|
||||
handleScroll();
|
||||
}
|
||||
}
|
||||
|
||||
if (primaryNav.classList.contains('fixed')) {
|
||||
placeholder.style.height = primaryNav.offsetHeight + 'px';
|
||||
}
|
||||
});
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
@@ -136,7 +136,7 @@ const formattedDate = postDate.toLocaleDateString('ru-RU', {
|
||||
left: 16px;
|
||||
padding: 6px 12px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.75rem;
|
||||
font-size: 0.625rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
@@ -164,7 +164,7 @@ const formattedDate = postDate.toLocaleDateString('ru-RU', {
|
||||
|
||||
.title-overlay {
|
||||
margin: 0 0 12px 0;
|
||||
font-size: 1.5rem;
|
||||
font-size: 1.4rem;
|
||||
font-weight: 700;
|
||||
line-height: 1.3;
|
||||
}
|
||||
@@ -182,7 +182,6 @@ const formattedDate = postDate.toLocaleDateString('ru-RU', {
|
||||
.author-overlay {
|
||||
font-size: 0.875rem;
|
||||
opacity: 0.9;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Адаптивность */
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
---
|
||||
import { stripHtml } from '@/utils/htmlhelpers';
|
||||
import Author from '@components/AuthorDisplay.astro';
|
||||
import Subscribe from '@components/SubscribePost.astro';
|
||||
import ShareButtons from '@components/ShareButtons.astro';
|
||||
|
||||
interface Props {
|
||||
post: any;
|
||||
@@ -6,18 +10,114 @@ interface Props {
|
||||
}
|
||||
|
||||
const { post, pageInfo } = Astro.props;
|
||||
|
||||
---
|
||||
|
||||
{post ? (
|
||||
<article class="news-single">
|
||||
<h1>{post.title}</h1>
|
||||
<div class="meta">
|
||||
{post.date && <time>{new Date(post.date).toLocaleDateString('ru-RU')}</time>}
|
||||
</div>
|
||||
{post.content && <div set:html={post.content} />}
|
||||
</article>
|
||||
<div class="article-wrapper">
|
||||
<article class="news-single">
|
||||
<div class="article_info">
|
||||
<div class="publication__data">{post.date && <time>{new Date(post.date).toLocaleDateString('ru-RU')}</time>}</div>
|
||||
<span class="author"><Author post={post} separator=", " /></span>
|
||||
</div>
|
||||
<h1>{post.title}</h1>
|
||||
{post.secondaryTitle && <p class="secondary-title">{post.secondaryTitle}</p>}
|
||||
|
||||
{post.featuredImage?.node?.sourceUrl && (
|
||||
<figure class="featured-image">
|
||||
<img
|
||||
src={post.featuredImage.node.sourceUrl}
|
||||
alt={post.featuredImage.node.altText || post.title}
|
||||
width={post.featuredImage.node.mediaDetails?.width || 1200}
|
||||
height={post.featuredImage.node.mediaDetails?.height || 675}
|
||||
loading="eager"
|
||||
class="post-image"
|
||||
/>
|
||||
{(post.featuredImage.node.description || post.featuredImage.node.caption) && (
|
||||
<figcaption class="image-caption">
|
||||
{post.featuredImage.node.description && (
|
||||
<p>{stripHtml(post.featuredImage.node.description)}</p>
|
||||
)}
|
||||
{post.featuredImage.node.caption && (
|
||||
<span>©{stripHtml(post.featuredImage.node.caption)}</span>
|
||||
)}
|
||||
</figcaption>
|
||||
)}
|
||||
</figure>
|
||||
)}
|
||||
|
||||
{post.content && <div set:html={post.content} />}
|
||||
</article>
|
||||
|
||||
<ShareButtons url={post.uri} title={post.title} />
|
||||
|
||||
<Subscribe />
|
||||
</div>
|
||||
) : (
|
||||
<div>Новость не найдена</div>
|
||||
)}
|
||||
|
||||
<style>
|
||||
.article-wrapper {
|
||||
position: relative;
|
||||
max-width: 75%;
|
||||
padding-bottom: 2rem;
|
||||
}
|
||||
|
||||
.news-single h1{
|
||||
font-size: 2.375rem;
|
||||
line-height: 1.2;
|
||||
margin: 0 0 0.625rem;
|
||||
}
|
||||
|
||||
.secondary-title {
|
||||
font-size: 1.25rem;
|
||||
line-height: 1.4;
|
||||
color: #666;
|
||||
margin: 0.5rem 0 1rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.article_info{
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.publication__data::after{
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
height: 12px;
|
||||
margin-top: -6px;
|
||||
border-left: 1px solid grey;
|
||||
}
|
||||
|
||||
.featured-image {
|
||||
margin: 1.5rem 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.post-image {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
border-radius: 8px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.image-caption {
|
||||
font-size: 0.875rem;
|
||||
color: #666;
|
||||
margin-top: 0.5rem;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.image-caption p {
|
||||
margin: 0 0 0.25rem 0;
|
||||
}
|
||||
|
||||
.image-caption span {
|
||||
font-style: italic;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
|
||||
10
src/components/SubscribePost.astro
Normal file
10
src/components/SubscribePost.astro
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
|
||||
---
|
||||
<div class="clearfix">
|
||||
|
||||
<p>
|
||||
Читайте на смартфоне наши Telegram-каналы: <a href="https://t.me/profile_newzzz/" target="_blank">Профиль-News</a>, и <a href="https://t.me/profilejournal" target="_blank">журнал Профиль</a>. Скачивайте полностью <a href="https://profile.ru/mobilnye-prilozheniya/" target="_blank">бесплатное мобильное</a> приложение журнала "Профиль".
|
||||
</p>
|
||||
|
||||
</div>
|
||||
@@ -3,6 +3,7 @@
|
||||
const { title, description } = Astro.props;
|
||||
|
||||
import Header from '../components/Header/Header.astro';
|
||||
import Header_lite from '../components/Header/Header_lite.astro';
|
||||
import CurrentDate from '../components/Header/CurrentDate.astro';
|
||||
import Footer from '../components/Footer.astro';
|
||||
|
||||
@@ -34,7 +35,7 @@ import '../styles/global.css';
|
||||
</div>
|
||||
|
||||
<Footer
|
||||
publicationName="ТехноВестник"
|
||||
publicationName="Профиль"
|
||||
organization="Учредитель: ИДР. Все права защищены."
|
||||
menuItems={[
|
||||
{ text: "О нас", url: "/about" },
|
||||
@@ -47,11 +48,3 @@ import '../styles/global.css';
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<style>
|
||||
|
||||
main {
|
||||
width:100%;
|
||||
max-width: 1024px;
|
||||
margin:auto;
|
||||
}
|
||||
</style>
|
||||
@@ -137,6 +137,7 @@ export async function getProfileArticleById(databaseId) {
|
||||
id
|
||||
databaseId
|
||||
title
|
||||
secondaryTitle
|
||||
content
|
||||
excerpt
|
||||
uri
|
||||
@@ -150,6 +151,7 @@ export async function getProfileArticleById(databaseId) {
|
||||
sourceUrl
|
||||
altText
|
||||
caption
|
||||
description
|
||||
mediaDetails {
|
||||
width
|
||||
height
|
||||
@@ -169,6 +171,15 @@ export async function getProfileArticleById(databaseId) {
|
||||
uri
|
||||
}
|
||||
}
|
||||
# Соавторы как массив
|
||||
coauthors {
|
||||
id
|
||||
name
|
||||
firstName
|
||||
lastName
|
||||
url
|
||||
description
|
||||
}
|
||||
categories {
|
||||
nodes {
|
||||
id
|
||||
@@ -195,7 +206,6 @@ export async function getProfileArticleById(databaseId) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Получить Anews пост по databaseId
|
||||
*/
|
||||
|
||||
@@ -40,6 +40,7 @@ if (pageInfo.type === 'single') { //одиночная статья
|
||||
description="Информационное агентство Деловой журнал Профиль"
|
||||
>
|
||||
|
||||
|
||||
{pageInfo.type === 'single' && article ? (
|
||||
<NewsSingle post={article} pageInfo={pageInfo} />
|
||||
) : (
|
||||
|
||||
@@ -47,6 +47,10 @@ a {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
main {
|
||||
width:100%;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@media (max-width: 767px) {
|
||||
|
||||
30
src/utils/htmlhelpers.ts
Normal file
30
src/utils/htmlhelpers.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Удаляет HTML-теги и декодирует HTML-сущности
|
||||
*/
|
||||
export function stripHtml(html: string): string {
|
||||
if (!html) return '';
|
||||
return html
|
||||
.replace(/<[^>]*>/g, '')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, "'")
|
||||
.replace(/'/g, "'")
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/&/g, '&')
|
||||
.replace(/ /g, ' ');
|
||||
}
|
||||
|
||||
/**
|
||||
* Только декодирует HTML-сущности, не удаляет теги
|
||||
*/
|
||||
export function decodeHtmlEntities(text: string): string {
|
||||
if (!text) return '';
|
||||
return text
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, "'")
|
||||
.replace(/'/g, "'")
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/&/g, '&')
|
||||
.replace(/ /g, ' ');
|
||||
}
|
||||
Reference in New Issue
Block a user