Compare commits
7 commits
dfd6d09c97
...
2e99b04261
| Author | SHA1 | Date | |
|---|---|---|---|
| 2e99b04261 | |||
| 3c55a37ed8 | |||
| e8532af1de | |||
| 07325e57dd | |||
| 90f14bc652 | |||
| be6615ff48 | |||
| 224187422a |
9 changed files with 270 additions and 108 deletions
143
src/lib/components/BlogCard.svelte
Normal file
143
src/lib/components/BlogCard.svelte
Normal file
|
|
@ -0,0 +1,143 @@
|
||||||
|
<script lang="ts" module>
|
||||||
|
export enum BlogCardSize {
|
||||||
|
Short,
|
||||||
|
Long,
|
||||||
|
Both
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { BLOG_POST_FRESHNESS_MILLIS } from '$lib/util/Blogs';
|
||||||
|
import DateWidget from '$lib/components/DateWidget.svelte';
|
||||||
|
|
||||||
|
export let post: App.BlogPost;
|
||||||
|
export let size: BlogCardSize = BlogCardSize.Both;
|
||||||
|
|
||||||
|
const dateNow = new Date().valueOf();
|
||||||
|
const isPostNew = dateNow - new Date(post.date!).valueOf() <= BLOG_POST_FRESHNESS_MILLIS;
|
||||||
|
const isPostUpdated =
|
||||||
|
post.dateChanged != null &&
|
||||||
|
dateNow - new Date(post.dateChanged).valueOf() <= BLOG_POST_FRESHNESS_MILLIS;
|
||||||
|
const isPostFresh = isPostNew || isPostUpdated;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rndtrash: пришлось дублировать классы с модификатором и без, потому что Tailwind просто не понимает,
|
||||||
|
* когда его классы склеивают из нескольких частей
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Возвращает список классов для полноразмерной плашки.
|
||||||
|
*
|
||||||
|
* Возвращает пустую строку, если плашка может быть только короткой
|
||||||
|
* @param classes
|
||||||
|
* @param modClasses
|
||||||
|
*/
|
||||||
|
function fullClass(classes: string, modClasses: string): string {
|
||||||
|
if (size === BlogCardSize.Short) return '';
|
||||||
|
return size === BlogCardSize.Both ? modClasses : classes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Возвращает список классов для короткой карточки.
|
||||||
|
*
|
||||||
|
* Возвращает пустую строку, если плашка может быть только полноразмерной
|
||||||
|
* @param classes
|
||||||
|
* @param modClasses
|
||||||
|
*/
|
||||||
|
function shortClass(classes: string, modClasses?: string): string {
|
||||||
|
if (size === BlogCardSize.Long) return '';
|
||||||
|
return size === BlogCardSize.Both && modClasses ? modClasses : classes;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<a
|
||||||
|
href="/blog/{post.slug}"
|
||||||
|
class="blog-card
|
||||||
|
{isPostUpdated ? 'updated' : isPostNew ? 'new' : ''}
|
||||||
|
{shortClass('flex-col justify-baseline')}
|
||||||
|
{fullClass('flex-row justify-stretch', 'sm:flex-row sm:justify-stretch')}"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="relative w-full basis-auto overflow-hidden
|
||||||
|
{shortClass('h-32')}
|
||||||
|
{fullClass('h-auto basis-1/3', 'sm:h-auto sm:basis-1/3')}"
|
||||||
|
>
|
||||||
|
{#if post.thumbnail}
|
||||||
|
<img
|
||||||
|
class="thumbnail"
|
||||||
|
src={`/blog/${post.slug}/${post.thumbnail}`}
|
||||||
|
alt={post.thumbnailAlt ?? 'Миниатюра поста'}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
{#if isPostFresh}
|
||||||
|
<div class="toast {fullClass('hidden', 'sm:hidden')}">
|
||||||
|
{#if isPostUpdated}
|
||||||
|
ОБНОВЛЕНО
|
||||||
|
{:else}
|
||||||
|
НОВОЕ
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<div class="flex w-full flex-col justify-center p-4 break-words md:p-8">
|
||||||
|
<div class="flex flex-row flex-wrap justify-start gap-4 pb-2">
|
||||||
|
<DateWidget
|
||||||
|
class={post.dateChanged ? shortClass('hidden', 'not-sm:hidden') : ''}
|
||||||
|
dateString={post.date}
|
||||||
|
type="published"
|
||||||
|
highlight={isPostNew && !isPostUpdated}
|
||||||
|
/>
|
||||||
|
{#if post.dateChanged}
|
||||||
|
<DateWidget
|
||||||
|
dateString={post.dateChanged}
|
||||||
|
type="updated"
|
||||||
|
highlight={isPostUpdated}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2 class="text-3xl font-bold">{post.title}</h2>
|
||||||
|
|
||||||
|
{#if post.description}
|
||||||
|
<p>{post.description}</p>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
@import '$src/app.css';
|
||||||
|
|
||||||
|
.blog-card {
|
||||||
|
@apply flex w-full max-w-5xl overflow-hidden rounded-lg bg-slate-100 text-slate-950 drop-shadow-xl transition-all hover:drop-shadow-2xl;
|
||||||
|
|
||||||
|
.toast {
|
||||||
|
@apply absolute top-0 right-0 rounded-bl-lg p-2 font-bold text-slate-50;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.updated {
|
||||||
|
box-shadow: 0 0 0 4px var(--color-purple-600);
|
||||||
|
|
||||||
|
.toast {
|
||||||
|
@apply bg-purple-600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.new {
|
||||||
|
box-shadow: 0 0 0 4px var(--color-amber-600);
|
||||||
|
|
||||||
|
.toast {
|
||||||
|
@apply bg-amber-600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
img.thumbnail {
|
||||||
|
@apply absolute h-full w-full object-cover transition-transform;
|
||||||
|
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover img.thumbnail {
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
32
src/lib/components/DateWidget.svelte
Normal file
32
src/lib/components/DateWidget.svelte
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import Icon from '@iconify/svelte';
|
||||||
|
|
||||||
|
let className: string = '';
|
||||||
|
export { className as class };
|
||||||
|
|
||||||
|
export let dateString: string | undefined;
|
||||||
|
export let type: 'published' | 'updated' = 'published';
|
||||||
|
export let highlight = false;
|
||||||
|
|
||||||
|
const icon =
|
||||||
|
type == 'published' ? 'material-symbols:calendar-today' : 'material-symbols:update';
|
||||||
|
|
||||||
|
const highlightClasses = (classes: string) => (highlight ? classes : '');
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="flex items-center gap-2 p-1 text-lg font-bold {className} rounded-lg
|
||||||
|
{highlightClasses(type == 'published' ? 'bg-amber-600' : 'bg-purple-600')}
|
||||||
|
{highlightClasses('text-slate-50')}"
|
||||||
|
>
|
||||||
|
<Icon {icon} width={28} height={28} />
|
||||||
|
<span>
|
||||||
|
{dateString
|
||||||
|
? new Date(dateString).toLocaleString(undefined, {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric'
|
||||||
|
})
|
||||||
|
: 'Не опубликован!'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
@ -1,16 +1,29 @@
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
export const THUMBNAIL_DEFAULT = "https://teasanctuary.ru/common/background-day.webp";
|
export const THUMBNAIL_DEFAULT = "https://teasanctuary.ru/common/background-day.webp";
|
||||||
|
export const BLOG_POST_FRESHNESS_MILLIS = 3 * 24 * 60 * 60 * 1000; // 3 дня
|
||||||
|
|
||||||
export async function fetchPostsSorted() {
|
export type PostComparer = (a: App.BlogPost, b: App.BlogPost) => number;
|
||||||
|
|
||||||
|
export const sortPostsByPostDate: PostComparer = (a, b) => new Date(b.date!).valueOf() - new Date(a.date!).valueOf();
|
||||||
|
|
||||||
|
function laterDate(a: string, b?: string): number {
|
||||||
|
const dateA = new Date(a).valueOf();
|
||||||
|
if (!b) return dateA;
|
||||||
|
|
||||||
|
const dateB = new Date(b).valueOf();
|
||||||
|
return Math.max(dateA, dateB);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const sortPostsByPostAndUpdateDate: PostComparer = (a, b) => laterDate(b.date!, b.dateChanged) - laterDate(a.date!, a.dateChanged);
|
||||||
|
|
||||||
|
export async function fetchPostsSorted(postComparer?: PostComparer) {
|
||||||
const allPosts = await fetchPosts();
|
const allPosts = await fetchPosts();
|
||||||
|
|
||||||
const sortedPosts = allPosts
|
const sortedPosts = allPosts
|
||||||
// Для списка постов оставляем только те, у которых объявлена дата публикации
|
// Для списка постов оставляем только те, у которых объявлена дата публикации
|
||||||
.filter((a) => !!a.date)
|
.filter((a) => !!a.date)
|
||||||
.sort((a, b) => {
|
.sort(postComparer ?? sortPostsByPostDate);
|
||||||
return new Date(b.date!).valueOf() - new Date(a.date!).valueOf();
|
|
||||||
});
|
|
||||||
|
|
||||||
return sortedPosts;
|
return sortedPosts;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
7
src/routes/+page.server.ts
Normal file
7
src/routes/+page.server.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { fetchPostsSorted, sortPostsByPostAndUpdateDate } from "$src/lib/util/Blogs";
|
||||||
|
|
||||||
|
const LATEST_POSTS_COUNT = 3;
|
||||||
|
|
||||||
|
export async function load() {
|
||||||
|
return { posts: (await fetchPostsSorted(sortPostsByPostAndUpdateDate)).slice(0, LATEST_POSTS_COUNT) };
|
||||||
|
}
|
||||||
|
|
@ -2,15 +2,15 @@
|
||||||
import SocialButton from '$lib/components/SocialButton.svelte';
|
import SocialButton from '$lib/components/SocialButton.svelte';
|
||||||
import SocialHyperlink from '$lib/components/SocialHyperlink.svelte';
|
import SocialHyperlink from '$lib/components/SocialHyperlink.svelte';
|
||||||
import { PUBLIC_TS_DISCORD } from '$env/static/public';
|
import { PUBLIC_TS_DISCORD } from '$env/static/public';
|
||||||
|
import BlogCard, { BlogCardSize } from '$src/lib/components/BlogCard.svelte';
|
||||||
|
import { page } from '$app/state';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>Tea Sanctuary</title>
|
<title>Tea Sanctuary</title>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<section
|
<section class="hero flex shrink-0 flex-col items-center justify-center gap-5 overflow-hidden p-4">
|
||||||
class="flex shrink-0 flex-col items-center justify-center gap-5 overflow-hidden p-4 hero"
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
class="flex flex-col flex-nowrap items-center justify-center gap-3 lg:flex-row lg:flex-nowrap"
|
class="flex flex-col flex-nowrap items-center justify-center gap-3 lg:flex-row lg:flex-nowrap"
|
||||||
>
|
>
|
||||||
|
|
@ -57,19 +57,12 @@
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div class="text-center font-bold lg:text-left">
|
||||||
class="text-center font-bold lg:text-left"
|
|
||||||
>
|
|
||||||
<h1>TEA</h1>
|
<h1>TEA</h1>
|
||||||
<h1>SANCTUARY</h1>
|
<h1>SANCTUARY</h1>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-center justify-start gap-4">
|
<div class="flex flex-col items-center justify-start gap-4">
|
||||||
<!--
|
|
||||||
<div class="flex flex-row gap-2 rounded-2xl bg-amber-950 p-2 text-center text-amber-50 ring-2 shadow">
|
|
||||||
Сайт находится в разработке. Если эта плашка висит после 1.06.2025 - пинайте Ивана!
|
|
||||||
</div>
|
|
||||||
-->
|
|
||||||
<div class="flex flex-row flex-wrap items-start justify-center gap-4">
|
<div class="flex flex-row flex-wrap items-start justify-center gap-4">
|
||||||
<SocialButton class="w-60 shrink-0" href={PUBLIC_TS_DISCORD}>Сообщество</SocialButton>
|
<SocialButton class="w-60 shrink-0" href={PUBLIC_TS_DISCORD}>Сообщество</SocialButton>
|
||||||
<SocialButton class="w-60 shrink-0" href="https://github.com/TeaSanctuary/">
|
<SocialButton class="w-60 shrink-0" href="https://github.com/TeaSanctuary/">
|
||||||
|
|
@ -87,6 +80,19 @@
|
||||||
</SocialButton>
|
</SocialButton>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section class="flex flex-col items-stretch bg-blue-900 pt-4 text-slate-50">
|
||||||
|
<h1 class="text-center">ПОСЛЕДНИЕ ПОСТЫ</h1>
|
||||||
|
|
||||||
|
<div class="flex flex-row items-stretch justify-evenly gap-4 overflow-x-auto p-4">
|
||||||
|
{#each page.data.posts as post, i}
|
||||||
|
<div class="aspect-3/2 w-80 shrink-0">
|
||||||
|
<BlogCard {post} size={BlogCardSize.Short} />
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section class="flex justify-center bg-slate-50 text-slate-950">
|
<section class="flex justify-center bg-slate-50 text-slate-950">
|
||||||
<div
|
<div
|
||||||
class="flex w-5xl max-w-screen flex-col flex-nowrap gap-12 p-2 px-2 pt-12 pb-12 text-base sm:text-xl"
|
class="flex w-5xl max-w-screen flex-col flex-nowrap gap-12 p-2 px-2 pt-12 pb-12 text-base sm:text-xl"
|
||||||
|
|
@ -94,67 +100,87 @@
|
||||||
<section id="who-are-we">
|
<section id="who-are-we">
|
||||||
<h1>Кто мы?</h1>
|
<h1>Кто мы?</h1>
|
||||||
<div class="text-justify">
|
<div class="text-justify">
|
||||||
<b>Tea Sanctuary</b> — это в первую очередь коллектив друзей, разрабатывающих проекты
|
<b>Tea Sanctuary</b> — это в первую очередь коллектив друзей, разрабатывающих
|
||||||
для души, для всеобщего пользования и даже на заказ. С <b>8 июля 2017 года</b> мы ведём публичную
|
проекты для души, для всеобщего пользования и даже на заказ. С
|
||||||
деятельность в сфере разработки ПО и развлечений.
|
<b>8 июля 2017 года</b> мы ведём публичную деятельность в сфере разработки ПО и развлечений.
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<div class="text-justify">
|
<div class="text-justify">
|
||||||
<b>Tea Sanctuary</b> — это также и сообщество единомышленников. Любовь к добротным видеоиграм
|
<b>Tea Sanctuary</b> — это также и сообщество единомышленников. Любовь к добротным
|
||||||
и пассивная агрессия к вычислительной технике у нас в крови. Когда-то сообщество было закрытым
|
видеоиграм и пассивная агрессия к вычислительной технике у нас в крови. Когда-то сообщество
|
||||||
и насчитывало около 50 участников, но впоследствии мы решили его расширить. Станьте частью коллектива!
|
было закрытым и насчитывало около 50 участников, но впоследствии мы решили его расширить.
|
||||||
|
Станьте частью коллектива!
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section id="what-are-we-doing">
|
<section id="what-are-we-doing">
|
||||||
<h1>Что делаем?</h1>
|
<h1>Что делаем?</h1>
|
||||||
<div class="text-justify">
|
<div class="text-justify">
|
||||||
Наша главная страсть — это, конечно, видеоигры. Мы часто участвуем в так называемых
|
Наша главная страсть — это, конечно, видеоигры. Мы часто участвуем в так
|
||||||
"гейм джемах" — конкурсах на разработку игр. Наши игры вы можете оценить здесь:
|
называемых "гейм джемах" — конкурсах на разработку игр. Наши игры вы можете
|
||||||
|
оценить здесь:
|
||||||
<SocialHyperlink href="https://randomtrash.itch.io">RandomTrash</SocialHyperlink>
|
<SocialHyperlink href="https://randomtrash.itch.io">RandomTrash</SocialHyperlink>
|
||||||
<SocialHyperlink href="https://friendlywithmeat.itch.io/">FriendlyWithMeat</SocialHyperlink>.
|
<SocialHyperlink href="https://friendlywithmeat.itch.io/">
|
||||||
Также мы ведём работу над нашим первым полноценным игровым проектом.
|
FriendlyWithMeat
|
||||||
Следите за новостями в нашем
|
</SocialHyperlink>. Также мы ведём работу над нашим первым полноценным игровым
|
||||||
|
проектом. Следите за новостями в нашем
|
||||||
<SocialHyperlink href={PUBLIC_TS_DISCORD}>сообществе</SocialHyperlink>!
|
<SocialHyperlink href={PUBLIC_TS_DISCORD}>сообществе</SocialHyperlink>!
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<div class="text-justify">
|
<div class="text-justify">
|
||||||
Отдельные участники нашего коллектива занимаются модификацией существующих игр, добавляя в
|
Отдельные участники нашего коллектива занимаются модификацией существующих игр,
|
||||||
них новый контент. Например, <b>MegaZerg</b> создаёт оригинальные карты для такой
|
добавляя в них новый контент. Например, <b>MegaZerg</b> создаёт оригинальные карты
|
||||||
бессмертной классики, как Counter-Strike 1.6, и выкладывает их на ресурс GameBanana:
|
для такой бессмертной классики, как Counter-Strike 1.6, и выкладывает их на ресурс
|
||||||
<SocialHyperlink href="https://gamebanana.com/members/2971042">kemist</SocialHyperlink>
|
GameBanana:
|
||||||
|
<SocialHyperlink href="https://gamebanana.com/members/2971042">
|
||||||
|
kemist
|
||||||
|
</SocialHyperlink>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<div class="text-justify">
|
<div class="text-justify">
|
||||||
Мы размещаем игровые сервера, как постоянные, так и временные для различных событий.
|
Мы размещаем игровые сервера, как постоянные, так и временные для различных событий.
|
||||||
Например, у нас есть сервер
|
Например, у нас есть сервер
|
||||||
<SocialHyperlink href="https://hl.teasanctuary.ru">Tea Sanctuary HLDM</SocialHyperlink>, где
|
<SocialHyperlink href="https://hl.teasanctuary.ru">
|
||||||
вы можете ознакомиться с новыми картами от всего сообщества Half-Life.
|
Tea Sanctuary HLDM
|
||||||
|
</SocialHyperlink>, где вы можете ознакомиться с новыми картами от всего сообщества
|
||||||
|
Half-Life.
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<div class="text-justify">
|
<div class="text-justify">
|
||||||
Не одними играми едины, за нашими плечами есть несколько прикладных программ, созданных под
|
Не одними играми едины, за нашими плечами есть несколько прикладных программ,
|
||||||
заказ. Про них ничего особо рассказать не можем, но если вам надо что-нибудь сделать —
|
созданных под заказ. Про них ничего особо рассказать не можем, но если вам надо
|
||||||
пишите нам!
|
что-нибудь сделать — пишите нам!
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section id="how-can-you-contact-us">
|
<section id="how-can-you-contact-us">
|
||||||
<h1>Как с вами связаться?</h1>
|
<h1>Как с вами связаться?</h1>
|
||||||
<div class="text-justify">
|
<div class="text-justify">
|
||||||
Общие вопросы можно задавать в <SocialHyperlink href={PUBLIC_TS_DISCORD}>сообществе Tea Sanctuary</SocialHyperlink>.
|
Общие вопросы можно задавать в
|
||||||
Там же можете написать личное сообщение администраторам.
|
<SocialHyperlink href={PUBLIC_TS_DISCORD}>
|
||||||
|
сообществе Tea Sanctuary
|
||||||
|
</SocialHyperlink>. Там же можете написать личное сообщение администраторам.
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<div class="text-justify">
|
<div class="text-justify">
|
||||||
Наши соцсети и почту для более важных обращений можно найти на странице <SocialHyperlink href="/contact">Контакты</SocialHyperlink>.
|
Наши соцсети и почту для более важных обращений можно найти на странице
|
||||||
|
<SocialHyperlink href="/contact">Контакты</SocialHyperlink>.
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@import "$src/app.css";
|
@import '$src/app.css';
|
||||||
|
|
||||||
|
section > h1,
|
||||||
|
section > h2 {
|
||||||
|
@apply font-disket mb-4 font-bold;
|
||||||
|
}
|
||||||
|
|
||||||
section > h1 {
|
section > h1 {
|
||||||
@apply font-disket mb-4 text-2xl font-bold sm:text-4xl;
|
@apply text-2xl sm:text-4xl;
|
||||||
}
|
}
|
||||||
</style>
|
|
||||||
|
section > h2 {
|
||||||
|
@apply text-xl sm:text-3xl;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
export async function load() {
|
|
||||||
return { title: undefined };
|
|
||||||
}
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { page } from '$app/state';
|
import { page } from '$app/state';
|
||||||
import Icon from '@iconify/svelte';
|
|
||||||
import SocialHyperlink from '$src/lib/components/SocialHyperlink.svelte';
|
import SocialHyperlink from '$src/lib/components/SocialHyperlink.svelte';
|
||||||
import InfoBlock from '$src/lib/components/InfoBlock.svelte';
|
import InfoBlock from '$src/lib/components/InfoBlock.svelte';
|
||||||
|
import BlogCard from '$src/lib/components/BlogCard.svelte';
|
||||||
|
|
||||||
function groupPostsByMonthYear(posts: App.BlogPost[]) {
|
function groupPostsByMonthYear(posts: App.BlogPost[]) {
|
||||||
const groupedPosts = new Map<string, App.BlogPost[]>();
|
const groupedPosts = new Map<string, App.BlogPost[]>();
|
||||||
|
|
@ -48,44 +48,7 @@
|
||||||
</h1>
|
</h1>
|
||||||
<div class="flex flex-col flex-wrap items-center gap-4">
|
<div class="flex flex-col flex-wrap items-center gap-4">
|
||||||
{#each postsInMonthYear as post, i}
|
{#each postsInMonthYear as post, i}
|
||||||
<a
|
<BlogCard {post} />
|
||||||
href="/blog/{post.slug}"
|
|
||||||
class="flex w-full max-w-5xl flex-col justify-baseline overflow-hidden rounded-lg bg-slate-100 text-slate-950 drop-shadow-xl transition-all hover:drop-shadow-2xl sm:flex-row sm:justify-stretch"
|
|
||||||
>
|
|
||||||
<div class="relative h-32 w-full basis-auto sm:h-auto sm:basis-1/3">
|
|
||||||
{#if post.thumbnail}
|
|
||||||
<img
|
|
||||||
class="absolute h-full w-full object-cover"
|
|
||||||
src={`/blog/${post.slug}/${post.thumbnail}`}
|
|
||||||
alt={post.thumbnailAlt ?? 'Миниатюра поста'}
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
<div class="flex w-full flex-col justify-center p-4 break-words md:p-8">
|
|
||||||
<div class="flex flex-row flex-wrap justify-between gap-4 pb-4">
|
|
||||||
<div class="flex items-center text-lg font-bold">
|
|
||||||
<Icon
|
|
||||||
icon="material-symbols:calendar-today"
|
|
||||||
class="mr-3"
|
|
||||||
style="transform: scale( 1.3 )"
|
|
||||||
/>
|
|
||||||
<p>
|
|
||||||
{new Date(post.date!).toLocaleString('default', {
|
|
||||||
month: 'short',
|
|
||||||
day: 'numeric',
|
|
||||||
year: 'numeric'
|
|
||||||
})}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2 class="text-3xl font-bold">{post.title}</h2>
|
|
||||||
|
|
||||||
{#if post.description}
|
|
||||||
<p>{post.description}</p>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { page } from '$app/state';
|
import { page } from '$app/state';
|
||||||
|
import DateWidget from '$src/lib/components/DateWidget.svelte';
|
||||||
import InfoBlock from '$src/lib/components/InfoBlock.svelte';
|
import InfoBlock from '$src/lib/components/InfoBlock.svelte';
|
||||||
import type { PageData } from './$types';
|
import type { PageData } from './$types';
|
||||||
import Icon from '@iconify/svelte';
|
import Icon from '@iconify/svelte';
|
||||||
|
|
@ -33,29 +34,9 @@
|
||||||
? 'bg-amber-50 text-slate-950'
|
? 'bg-amber-50 text-slate-950'
|
||||||
: 'bg-red-500 text-slate-50'} sm:flex-row sm:flex-nowrap sm:gap-5"
|
: 'bg-red-500 text-slate-50'} sm:flex-row sm:flex-nowrap sm:gap-5"
|
||||||
>
|
>
|
||||||
<div class="flex items-center">
|
<DateWidget dateString={data.blogPost.date} type="published" />
|
||||||
<Icon icon="material-symbols:calendar-today" class="mr-3" width={24} height={24} />
|
|
||||||
<p>
|
|
||||||
{data.blogPost.date
|
|
||||||
? new Date(data.blogPost.date).toLocaleString(undefined, {
|
|
||||||
month: 'short',
|
|
||||||
day: 'numeric',
|
|
||||||
year: 'numeric'
|
|
||||||
})
|
|
||||||
: 'Не опубликован!'}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
{#if data.blogPost.dateChanged}
|
{#if data.blogPost.dateChanged}
|
||||||
<div class="flex items-center font-bold">
|
<DateWidget dateString={data.blogPost.dateChanged} type="updated" />
|
||||||
<Icon icon="material-symbols:update" class="mr-3" width={24} height={24} />
|
|
||||||
<p>
|
|
||||||
{new Date(data.blogPost.dateChanged).toLocaleString(undefined, {
|
|
||||||
month: 'short',
|
|
||||||
day: 'numeric',
|
|
||||||
year: 'numeric'
|
|
||||||
})}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
{/if}
|
{/if}
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
@ -80,8 +61,8 @@
|
||||||
prose-headings:mb-4
|
prose-headings:mb-4
|
||||||
prose-headings:font-bold prose-headings:text-slate-950 prose-h1:text-2xl
|
prose-headings:font-bold prose-headings:text-slate-950 prose-h1:text-2xl
|
||||||
prose-h1:sm:text-4xl prose-h2:text-xl
|
prose-h1:sm:text-4xl prose-h2:text-xl
|
||||||
prose-h2:sm:text-3xl mx-auto
|
prose-h2:sm:text-3xl prose-p:text-justify
|
||||||
prose-p:text-justify
|
mx-auto
|
||||||
w-5xl
|
w-5xl
|
||||||
p-4
|
p-4
|
||||||
text-base
|
text-base
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ const config = {
|
||||||
},
|
},
|
||||||
extensions: ['.svelte', '.md'],
|
extensions: ['.svelte', '.md'],
|
||||||
preprocess: [
|
preprocess: [
|
||||||
vitePreprocess(),
|
vitePreprocess({ script: true }),
|
||||||
mdsvex({
|
mdsvex({
|
||||||
extensions: ['.md'],
|
extensions: ['.md'],
|
||||||
layout: join(__dirname, "./src/lib/components/MdsvexLayout.svelte")
|
layout: join(__dirname, "./src/lib/components/MdsvexLayout.svelte")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue