diff --git a/src/components/Archive.astro b/src/components/Archive.astro new file mode 100644 index 0000000..e69de29 diff --git a/src/components/BurgerMenu.astro b/src/components/BurgerMenu.astro new file mode 100644 index 0000000..6c63b8b --- /dev/null +++ b/src/components/BurgerMenu.astro @@ -0,0 +1,453 @@ +--- +// BurgerMenu.astro +import { fetchMenu } from '@api/menu'; + +interface Props { + colorMenuId: number; + standardMenuId: number; + submenuId: number; +} + +const { colorMenuId, standardMenuId, submenuId } = Astro.props; + +// Получаем все меню +const colorMenu = await fetchMenu({ id: colorMenuId }); +const standardMenu = await fetchMenu({ id: standardMenuId }); +const submenu = await fetchMenu({ id: submenuId }); +--- + +
+
+
+ + +
+ {colorMenu && ( +
+
    + {colorMenu.menuItems.nodes.map(item => { + const colorClass = item.menuItemColor ? `color-${item.menuItemColor}` : 'color-black'; + return ( +
  • + + {item.label} + +
  • + ); + })} + +
  • + +
  • +
+
+ )} + +
+ + {standardMenu && ( +
+ +
+ )} +
+
+ + +
+ + + + + + + diff --git a/src/components/ContentGrid.astro b/src/components/ContentGrid.astro index 1d3f9ab..aaad6d4 100644 --- a/src/components/ContentGrid.astro +++ b/src/components/ContentGrid.astro @@ -58,12 +58,11 @@ function shouldBeLarge(index: number, columns: number): boolean { ---
-

- {showCount && items.length > 0 && ( - ({items.length}) - )} -

+ {showCount && items.length > 0 && ( +

({items.length})

+ )} +
{ + const name = coauthor?.node?.name || coauthor?.name; + const nickname = coauthor?.node?.nickname || coauthor?.nickname; + + return name; // Возвращаем только имя, ссылки будут в шаблоне + }) + .filter(Boolean) + .join(', '); +} + +function shouldBeLarge(index: number): boolean { + if (index < 8) return false; + return (index - 8) % 11 === 0; +} +--- + +
+

+ {showCount && items.length > 0 && ( + ({items.length}) + )} +

+ +
+ {items.map((item, index) => { + const postUrl = item.uri || `/blog/${item.databaseId}`; + const postDate = new Date(item.date); + const isLarge = shouldBeLarge(index); + + return ( +
+
+ {item.featuredImage?.node?.sourceUrl ? ( + {item.featuredImage.node.altText + ) : ( +
+ )} + + + {item.categories?.nodes?.[0] && ( + + )} + +
+ + + + + {item.coauthors && item.coauthors.length > 0 && ( + + )} +
+
+ +
+

+ +

+ +
+
+ ); + })} +
+ + + + + + + + + {pageInfo.hasNextPage && ( +
+ )} +
+ + \ No newline at end of file diff --git a/src/components/ContentGrid____.astro b/src/components/ContentGrid____.astro new file mode 100644 index 0000000..3f24894 --- /dev/null +++ b/src/components/ContentGrid____.astro @@ -0,0 +1,679 @@ +--- +import CoauthorsInline from '@components/Coauthors.astro'; + +export interface Props { + items: any[]; + showCount?: boolean; +} + +const { + items = [], + showCount = false, +} = Astro.props; + +// Функция для извлечения класса цвета из строки +function extractColorClass(colorString: string): string { + if (!colorString) return 'bg-blue'; + + if (colorString.includes('фон меню:')) { + const parts = colorString.split(':'); + const color = parts[1]?.trim(); + + const validColors = [ + 'black', 'yellow', 'blue', 'green', 'red', 'orange', 'gray', + 'indigo', 'purple', 'pink', 'teal', 'cyan', 'white', + 'gray-dark', 'light', 'dark' + ]; + + if (color && validColors.includes(color)) { + return `bg-${color}`; + } + } + + if (colorString.startsWith('bg-')) { + return colorString; + } + + const simpleColor = colorString.toLowerCase(); + switch(simpleColor) { + case 'black': case 'yellow': case 'blue': case 'green': + case 'red': case 'orange': case 'gray': case 'indigo': + case 'purple': case 'pink': case 'teal': case 'cyan': + case 'white': case 'dark': case 'light': + return `bg-${simpleColor}`; + case 'gray-dark': return 'bg-gray-dark'; + default: return 'bg-blue'; + } +} +--- + +
+

+ {showCount && items.length > 0 && ( + ({items.length}) + )} +

+ +
+ {items.map((item, index) => { + const postUrl = item.uri || `/blog/${item.databaseId}`; + const postDate = new Date(item.date); + const coauthors = item.coauthors || []; + + // Получаем цвет категории и преобразуем в CSS класс + const rawColor = item.categories?.nodes?.[0]?.color || ''; + const categoryBgClass = extractColorClass(rawColor); + + // Логика для больших плиток на десктопе + let isLarge = false; + let largePosition = ''; + + const rowNumber = Math.floor(index / 4) + 1; + const positionInRow = index % 4; + + if (index >= 8) { + const largeRowStart = (rowNumber - 3) % 3 === 0 && rowNumber >= 3; + + if (largeRowStart && positionInRow === 0) { + isLarge = true; + largePosition = 'first'; + } + } + + return ( +
+ +
+ {item.featuredImage?.node?.sourceUrl ? ( + {item.featuredImage.node.altText + ) : ( +
+ )} + + {/* Рубрика в верхнем правом углу с цветом */} + {item.categories?.nodes?.[0]?.name && ( +
+ {item.categories.nodes[0].name} +
+ )} + + {/* Оверлей с контентом - ФИКСИРОВАННАЯ ВЫСОТА */} +
+
+ {/* Дата */} + + + {/* Заголовок */} +

+ {item.title} +

+ + {/* Соавторы - всегда внизу */} + {coauthors.length > 0 && ( +
+ +
+ )} +
+
+
+
+ + {/* Скринридеру и SEO */} +
+

+ +

+ + {coauthors.length > 0 && ( +
+ {coauthors.map((coauthor, idx) => { + const authorUrl = coauthor?.url || coauthor?.uri || '#'; + return ( + + {coauthor.name}{idx < coauthors.length - 1 ? ', ' : ''} + + ); + })} +
+ )} +
+
+ ); + })} +
+
+ + \ No newline at end of file diff --git a/src/components/ContentGrid_json.astro b/src/components/ContentGrid_json.astro new file mode 100644 index 0000000..a0bd300 --- /dev/null +++ b/src/components/ContentGrid_json.astro @@ -0,0 +1,494 @@ +--- + +import CategoryBadge from './CategoryBadge.astro'; // цветная плитка рубрик +import Author from '@components/AuthorDisplay.astro'; //вывод соавторов + + +export interface Props { + items: any[]; + showCount?: boolean; + slug?: string; + pageInfo?: { + hasNextPage: boolean; + endCursor: string | null; + }; + loadMoreConfig?: { + type: 'latest' | 'category' | 'author' | 'tag'; + slug?: string; + first?: number; + }; +} + +const { + items = [], + showCount = false, + pageInfo = { hasNextPage: false, endCursor: null }, + loadMoreConfig = { type: 'latest', first: 11 } +} = Astro.props; + + +function getCoauthorsNames(coauthors: any[]): string { + if (!coauthors || coauthors.length === 0) return ''; + + return coauthors + .map((coauthor: any) => coauthor?.node?.name || coauthor?.name) + .filter(Boolean) + .join(' '); +} + +// ✅ ИСПРАВЛЕННАЯ функция для определения больших карточек +// Большие карточки на индексах: 8, 19, 30, 41, 52... +// Формула: (index - 8) % 11 === 0 +function shouldBeLarge(index: number): boolean { + if (index < 8) return false; + return (index - 8) % 11 === 0; +} +--- + +
+

+ {showCount && items.length > 0 && ( + ({items.length}) + )} +

+ +
+ {items.map((item, index) => { + const postUrl = item.uri || `/blog/${item.databaseId}`; + const postDate = new Date(item.date); + const coauthors = item.coauthors || []; + const coauthorsNames = getCoauthorsNames(coauthors); + + + // ✅ ИСПРАВЛЕННАЯ логика + const isLarge = shouldBeLarge(index); + const largePosition = isLarge ? 'first' : ''; + + return ( + + ); + })} +
+ + + + + + + + + {pageInfo.hasNextPage && ( +
+ )} +
+ + diff --git a/src/components/ContentGrid_no_infinity.astro b/src/components/ContentGrid_no_infinity.astro new file mode 100644 index 0000000..f9d50f0 --- /dev/null +++ b/src/components/ContentGrid_no_infinity.astro @@ -0,0 +1,163 @@ +--- +export interface Props { + items: any[]; + showCount?: boolean; +} + +const { + items = [], + showCount = false, +} = Astro.props; + +// Функция для извлечения класса цвета из строки +function extractColorClass(colorString: string): string { + if (!colorString) return 'bg-blue'; + + if (colorString.includes('фон меню:')) { + const parts = colorString.split(':'); + const color = parts[1]?.trim(); + + const validColors = [ + 'black', 'yellow', 'blue', 'green', 'red', 'orange', 'gray', + 'indigo', 'purple', 'pink', 'teal', 'cyan', 'white', + 'gray-dark', 'light', 'dark' + ]; + + if (color && validColors.includes(color)) { + return `bg-${color}`; + } + } + + if (colorString.startsWith('bg-')) { + return colorString; + } + + const simpleColor = colorString.toLowerCase(); + switch(simpleColor) { + case 'black': case 'yellow': case 'blue': case 'green': + case 'red': case 'orange': case 'gray': case 'indigo': + case 'purple': case 'pink': case 'teal': case 'cyan': + case 'white': case 'dark': case 'light': + return `bg-${simpleColor}`; + case 'gray-dark': return 'bg-gray-dark'; + default: return 'bg-blue'; + } +} + +// Функция для получения списка имен соавторов +function getCoauthorsNames(coauthors: any[]): string { + if (!coauthors || coauthors.length === 0) return ''; + + return coauthors + .map((coauthor: any) => coauthor?.node?.name || coauthor?.name) + .filter(Boolean) + .join(' '); +} +--- + +
+

+ {showCount && items.length > 0 && ( + ({items.length}) + )} +

+ +
+ {items.map((item, index) => { + const postUrl = item.uri || `/blog/${item.databaseId}`; + const postDate = new Date(item.date); + const coauthors = item.coauthors || []; + const coauthorsNames = getCoauthorsNames(coauthors); + + const rawColor = item.categories?.nodes?.[0]?.color || ''; + const categoryBgClass = extractColorClass(rawColor); + + let isLarge = false; + let largePosition = ''; + + const rowNumber = Math.floor(index / 4) + 1; + const positionInRow = index % 4; + + if (index >= 8) { + const largeRowStart = (rowNumber - 3) % 3 === 0 && rowNumber >= 3; + + if (largeRowStart && positionInRow === 0) { + isLarge = true; + largePosition = 'first'; + } + } + + return ( + + ); + })} +
+
diff --git a/src/components/Header/Header_lite.astro b/src/components/Header/Header_lite.astro new file mode 100644 index 0000000..ed5523d --- /dev/null +++ b/src/components/Header/Header_lite.astro @@ -0,0 +1,33 @@ +--- + + import Stores from './LazyStores.astro'; + import MainMenu from '@components/MainMenu.astro'; + + const MENU_ID = 3340; // 103246 (бургер 1). 103247 (бургер 2 ) + let menuItems = []; + +--- + +
+ +
+ Профиль +
+ + + +
+ + + \ No newline at end of file diff --git a/src/components/HomeNews.astro b/src/components/HomeNews.astro new file mode 100644 index 0000000..4841640 --- /dev/null +++ b/src/components/HomeNews.astro @@ -0,0 +1,47 @@ +--- +import { fetchWPRestGet } from "@/lib/api/wp-rest-get-client"; + +type PostItem = { id: number; title: string; link: string; date?: string; type?: string }; +type ApiResp = { + news?: { items?: PostItem[] }; + top?: { items?: PostItem[] }; +}; + +let data: ApiResp | null = null; + +try { + data = await fetchWPRestGet("my/v1/news-top", { per_page: 20 }); +} catch (e) { + data = null; +} + +const top = data?.top?.items ?? []; +const news = data?.news?.items ?? []; +const shouldRender = top.length > 0 || news.length > 0; +--- + +{shouldRender && ( +
+ {top.length > 0 && ( + <> +

Top

+ + + )} + + {news.length > 0 && ( + <> +

News

+ + + )} +
+)} diff --git a/src/components/MainLine.astro b/src/components/MainLine.astro new file mode 100644 index 0000000..d4bea1a --- /dev/null +++ b/src/components/MainLine.astro @@ -0,0 +1,128 @@ +--- +// src/components/MainLine.astro +import EndnewsList from '@components/EndnewsList.astro'; +import MainPostWidget from '@components/MainPostWidget.astro'; +import ColonPost from '@components/ColonPost.astro'; +--- + +
+
+
+
+
+
+
Правая колонка (260px)
+
+ + \ No newline at end of file diff --git a/src/components/ShareButtons.astro b/src/components/ShareButtons.astro new file mode 100644 index 0000000..123ab8e --- /dev/null +++ b/src/components/ShareButtons.astro @@ -0,0 +1,106 @@ +--- +interface Props { + url: string; + title: string; +} + +const { url, title } = Astro.props; + +// Формируем полный URL +const fullUrl = url.startsWith('http') ? url : `${Astro.site}${url}`; +const encodedUrl = encodeURIComponent(fullUrl); +const encodedTitle = encodeURIComponent(title); + +// Ссылки для шеринга +const telegramUrl = `https://t.me/share/url?url=${encodedUrl}&text=${encodedTitle}`; +const vkUrl = `https://vk.com/share.php?url=${encodedUrl}&title=${encodedTitle}`; +const okUrl = `https://connect.ok.ru/offer?url=${encodedUrl}&title=${encodedTitle}`; +--- + + + + diff --git a/src/components/___ArchivePagination.astro b/src/components/___ArchivePagination.astro new file mode 100644 index 0000000..ea15d6b --- /dev/null +++ b/src/components/___ArchivePagination.astro @@ -0,0 +1,217 @@ +--- + + +interface Props { + baseUrl: string; + page: number; + hasNextPage: boolean; + window?: number; // сколько страниц вокруг текущей +} + +const { + baseUrl, + page, + hasNextPage, + window = 2, +} = Astro.props; + +const pageUrl = (p: number) => + p === 1 ? baseUrl : `${baseUrl}/page/${p}`; + +// рассчитываем диапазон +const start = Math.max(1, page - window); +const end = page + window; +--- + + + + diff --git a/src/layouts/ContentLayout.astro b/src/layouts/ContentLayout.astro index 41f6596..c228da6 100644 --- a/src/layouts/ContentLayout.astro +++ b/src/layouts/ContentLayout.astro @@ -48,54 +48,4 @@ import '../styles/global.css'; - - - diff --git a/src/styles/components/ContentGrid.css b/src/styles/components/ContentGrid.css index e119aa6..30dc8eb 100644 --- a/src/styles/components/ContentGrid.css +++ b/src/styles/components/ContentGrid.css @@ -12,9 +12,7 @@ /* Секция постов */ .posts-section { - max-width: 1400px; - margin: 0 auto; - padding: 0 20px 20px; + } .posts-section h2 { diff --git a/src/styles/global.css b/src/styles/global.css index 0b76be3..3f713e9 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -1,4 +1,5 @@ @import './reset.css'; +@import './ContentLayout.css'; @import './components/ContentGrid.css'; @import './components/theme-colors.css'; @@ -52,7 +53,6 @@ main { } - @media (max-width: 767px) { .container{