mirror of
https://github.com/sergeyyarkov/yarkov.tech.git
synced 2026-10-11 02:19:08 +03:00
feat: added dynamin article page
This commit is contained in:
parent
73ee879952
commit
d935483507
@ -1,5 +1,4 @@
|
|||||||
import { defineConfig } from 'astro/config';
|
import { defineConfig } from 'astro/config';
|
||||||
|
|
||||||
import solid from '@astrojs/solid-js';
|
import solid from '@astrojs/solid-js';
|
||||||
|
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
if (window.theme) {
|
if (window.theme) {
|
||||||
/* the {theme} in the path will be dynamically changed */
|
/* the {theme} in the path will be dynamically changed */
|
||||||
const PATH = `assets/css/code-highlight-{theme}.min.css`;
|
const PATH = `/assets/css/code-highlight-{theme}.min.css`;
|
||||||
const $head = document.getElementsByTagName('head')[0];
|
const $head = document.getElementsByTagName('head')[0];
|
||||||
const getPath = (theme) => PATH.replace(`{theme}`, theme);
|
const getPath = (theme) => PATH.replace(`{theme}`, theme);
|
||||||
const createLink = (theme, media) => {
|
const createLink = (theme, media) => {
|
||||||
|
|||||||
@ -9,7 +9,7 @@ export type ArticleItemProps = {
|
|||||||
export const ArticleItem: Component<ArticleItemProps> = (props) => {
|
export const ArticleItem: Component<ArticleItemProps> = (props) => {
|
||||||
return (
|
return (
|
||||||
<article class="article-item">
|
<article class="article-item">
|
||||||
<a href="#">
|
<a href={`/${new Date(props.data.published_at).toLocaleDateString('en-CA')}/${props.data.slug}`}>
|
||||||
<h3>{props.data.title}</h3>
|
<h3>{props.data.title}</h3>
|
||||||
<p>
|
<p>
|
||||||
<time datetime={props.data.published_at.toString()}>{DateFormatter.format(new Date(props.data.published_at))}</time>
|
<time datetime={props.data.published_at.toString()}>{DateFormatter.format(new Date(props.data.published_at))}</time>
|
||||||
|
|||||||
@ -16,7 +16,7 @@ const { title, tags, author, published_at } = data;
|
|||||||
<ul class='list-links'>
|
<ul class='list-links'>
|
||||||
{tags.map((tag) => (
|
{tags.map((tag) => (
|
||||||
<li>
|
<li>
|
||||||
<a class="btn-link" href={`/articles?tags=${tag}`}>
|
<a class="btn-link" href={`/articles?tags=${tag.toLocaleLowerCase()}`}>
|
||||||
{tag}
|
{tag}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
@ -32,10 +32,7 @@ const { title, tags, author, published_at } = data;
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<figure>
|
<figure>
|
||||||
<img
|
<img src='https://picsum.photos/1920/1080' alt='Article Image' />
|
||||||
src='https://img.freepik.com/premium-photo/beautiful-emerald-lake-yoho-national-park-british-columbia-canada_131985-177.jpg?w=1920'
|
|
||||||
alt='Article Image'
|
|
||||||
/>
|
|
||||||
</figure>
|
</figure>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
@ -1,18 +1,17 @@
|
|||||||
---
|
---
|
||||||
import { SITE_METADATA } from '@root/config';
|
import { SITE_METADATA } from '@root/config';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
title?: string | undefined;
|
title?: string | undefined;
|
||||||
description?: string | undefined;
|
description?: string | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { title = SITE_METADATA.title, description } = Astro.props as Props;
|
const { title = SITE_METADATA.title, description } = Astro.props as Props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<link rel='preconnect' href='https://fonts.googleapis.com' />
|
<link rel='preconnect' href='https://fonts.googleapis.com' />
|
||||||
<link rel='preconnect' href='https://fonts.gstatic.com' crossorigin />
|
<link rel='preconnect' href='https://fonts.gstatic.com' crossorigin />
|
||||||
<link href='https://fonts.googleapis.com/css2?family=Rubik:ital,wght@0,400;0,500;0,600;0,700;1,400&display=swap'
|
<link href='https://fonts.googleapis.com/css2?family=Rubik:ital,wght@0,400;0,500;0,600;0,700;1,400&display=swap' rel='stylesheet' />
|
||||||
rel='stylesheet' />
|
|
||||||
|
|
||||||
<meta charset='utf-8' />
|
<meta charset='utf-8' />
|
||||||
<meta name='viewport' content='width=device-width, initial-scale=1' />
|
<meta name='viewport' content='width=device-width, initial-scale=1' />
|
||||||
@ -21,5 +20,5 @@
|
|||||||
</title>
|
</title>
|
||||||
<meta name='title' content={title} />
|
<meta name='title' content={title} />
|
||||||
<meta name='description' content={description || SITE_METADATA.description} />
|
<meta name='description' content={description || SITE_METADATA.description} />
|
||||||
<script is:inline src='assets/js/detect-theme.min.js' />
|
<script is:inline src='/assets/js/detect-theme.min.js' />
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
|
|||||||
17
src/components/Counter/Counter.tsx
Normal file
17
src/components/Counter/Counter.tsx
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import type { Component } from 'solid-js';
|
||||||
|
|
||||||
|
import { createSignal, createEffect } from 'solid-js';
|
||||||
|
|
||||||
|
const Counter: Component = () => {
|
||||||
|
const [count, setCount] = createSignal<number>(0);
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
setInterval(() => {
|
||||||
|
setCount(count() + 1);
|
||||||
|
}, 500);
|
||||||
|
});
|
||||||
|
|
||||||
|
return <div>{count()}</div>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Counter;
|
||||||
1
src/components/Counter/index.ts
Normal file
1
src/components/Counter/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from './Counter';
|
||||||
@ -18,9 +18,7 @@ const { lang = 'ru', content } = Astro.props as Props;
|
|||||||
<html lang={lang}>
|
<html lang={lang}>
|
||||||
<head>
|
<head>
|
||||||
<BaseHead title={content.title} description={content.description}>
|
<BaseHead title={content.title} description={content.description}>
|
||||||
<script is:inline defer src='assets/js/load-code-highlight.js'>
|
<script is:inline defer src='/assets/js/load-code-highlight.js' />
|
||||||
|
|
||||||
</script>
|
|
||||||
</BaseHead>
|
</BaseHead>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|||||||
22
src/pages/[...slug].astro
Normal file
22
src/pages/[...slug].astro
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
import type { MarkdownInstance } from 'astro';
|
||||||
|
|
||||||
|
import ArticleLayout from '@root/layouts/article.astro';
|
||||||
|
|
||||||
|
// TODO: generate pages based on user language
|
||||||
|
export async function getStaticPaths() {
|
||||||
|
const data = await Astro.glob<ArticleType>('../../content/articles/**/*.md');
|
||||||
|
return data.map((article) => {
|
||||||
|
const published_at = new Date(article.frontmatter.published_at).toLocaleDateString('en-CA');
|
||||||
|
const slug = `${published_at}/${article.frontmatter.slug}`;
|
||||||
|
const props = { Content: article.Content, frontmatter: article.frontmatter };
|
||||||
|
return { params: { slug }, props };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const { Content, frontmatter: data } = Astro.props as MarkdownInstance<ArticleType>;
|
||||||
|
---
|
||||||
|
|
||||||
|
<ArticleLayout content={data}>
|
||||||
|
<Content />
|
||||||
|
</ArticleLayout>
|
||||||
@ -1,80 +0,0 @@
|
|||||||
---
|
|
||||||
import ArticleLayout from '@root/layouts/article.astro';
|
|
||||||
import { Markdown } from 'astro/components';
|
|
||||||
|
|
||||||
export function getStaticPaths() {
|
|
||||||
return [{ params: { article: 'article' } }];
|
|
||||||
}
|
|
||||||
---
|
|
||||||
|
|
||||||
<ArticleLayout
|
|
||||||
content={{
|
|
||||||
title: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
|
|
||||||
description: 'description1',
|
|
||||||
author: 'Sergey Yarkov',
|
|
||||||
tags: ['AVR', 'JavaScript', 'NodeJS'],
|
|
||||||
published_at: new Date().toISOString(),
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Markdown>
|
|
||||||
## Introduction
|
|
||||||
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam eu turpis molestie, dictum est a, mattis tellus.
|
|
||||||
Sed dignissim, metus nec fringilla accumsan, risus sem sollicitudin lacus, ut interdum tellus elit sed risus.
|
|
||||||
Maecenas eget condimentum velit, sit amet feugiat lectus. Class aptent taciti sociosqu ad litora torquent per
|
|
||||||
conubia nostra, per inceptos himenaeos. [Praesent auctor](#) purus luctus enim egestas, ac scelerisque ante
|
|
||||||
pulvinar.
|
|
||||||
Donec ut rhoncus ex. Suspendisse ac rhoncus nisl, eu tempor urna. Curabitur vel bibendum lorem. Morbi convallis
|
|
||||||
convallis diam sit amet [lacinia](#). Aliquam in elementum
|
|
||||||
|
|
||||||
Curabitur tempor quis eros tempus lacinia. Nam bibendum pellentesque quam a convallis. Sed ut vulputate nisi.
|
|
||||||
Integer in felis sed leo vestibulum venenatis. Suspendisse quis arcu sem. Aenean feugiat ex eu vestibulum
|
|
||||||
vestibulum. Morbi a eleifend magna. Nam metus lacus, porttitor eu mauris a, blandit ultrices nibh. Mauris sit
|
|
||||||
amet magna non ligula vestibulum eleifend. Nulla varius volutpat turpis sed lacinia. Nam eget mi in purus
|
|
||||||
lobortis eleifend. Sed nec ante dictum sem condimentum ullamcorper quis venenatis nisi. Proin vitae facilisis
|
|
||||||
nisi, ac posuere leo.
|
|
||||||
|
|
||||||
### Another Heading
|
|
||||||
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam eu turpis molestie, dictum est a, mattis tellus.
|
|
||||||
Sed dignissim, metus nec fringilla accumsan, risus sem sollicitudin lacus, ut interdum tellus elit sed risus.
|
|
||||||
|
|
||||||
- `Nam metus lacus`: porttitor eu mauris a, [blandit ultrices](#) nibh.
|
|
||||||
- `Class aptent`: taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.
|
|
||||||
- `Sed nec ante`: dictum sem condimentum [ullamcorper quis](#) venenatis nisi.
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import type { APIContext } from 'astro';
|
|
||||||
|
|
||||||
export async function get({ params, request }: APIContext) {
|
|
||||||
return {
|
|
||||||
body: JSON.stringify({
|
|
||||||
path: new URL(request.url).pathname,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam eu turpis molestie, dictum est a, mattis tellus.
|
|
||||||
Sed dignissim.
|
|
||||||
|
|
||||||
<figure>
|
|
||||||
<img src="https://wallpaperaccess.com/full/138733.jpg" alt="test" />
|
|
||||||
<figcaption>This is a test image!</figcaption>
|
|
||||||
</figure>
|
|
||||||
|
|
||||||
<blockquote>
|
|
||||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam eu turpis molestie, dictum est a, mattis
|
|
||||||
tellus.
|
|
||||||
Sed dignissim, metus nec fringilla accumsan, risus sem sollicitudin lacus, ut interdum tellus elit sed
|
|
||||||
risus.
|
|
||||||
</p>
|
|
||||||
<p>— <a href="#">Metus Nec</a></p>
|
|
||||||
</blockquote>
|
|
||||||
|
|
||||||
## Conclusion
|
|
||||||
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam eu turpis molestie, dictum est a, mattis tellus.
|
|
||||||
Sed dignissim, metus nec fringilla accumsan, risus sem sollicitudin lacus, ut interdum tellus elit sed risus.
|
|
||||||
</Markdown>
|
|
||||||
</ArticleLayout>
|
|
||||||
@ -1,16 +1,24 @@
|
|||||||
---
|
---
|
||||||
import PageLayout from '@layouts/default.astro';
|
import PageLayout from '@layouts/default.astro';
|
||||||
import ArticlesList from '@components/ArticlesList';
|
import ArticlesList from '@components/ArticlesList';
|
||||||
import { transformArticles, formatToArticleBlocks } from '@utils/articles';
|
import { transformArticles, formatToArticleBlocks } from '@utils/articles';
|
||||||
|
|
||||||
const articles = await Astro.glob<ArticleType>('../../content/articles/**/*.md');
|
const articles = await Astro.glob<ArticleType>('../../content/articles/**/*.md');
|
||||||
const tags = new Set(articles.map((article) => article.frontmatter.tags).flat());
|
const tags = new Set(articles.map((article) => article.frontmatter.tags).flat());
|
||||||
---
|
---
|
||||||
|
|
||||||
<PageLayout title='Статьи'
|
<PageLayout
|
||||||
description='Технические статьи, туториалы и различные справочные материалы которые связаны с IT тематикой находятся здесь.'>
|
title='Статьи'
|
||||||
|
description='Технические статьи, туториалы и различные справочные материалы которые связаны с IT тематикой находятся здесь.'
|
||||||
|
>
|
||||||
<section>
|
<section>
|
||||||
<ArticlesList client:load data={{ articles: formatToArticleBlocks(transformArticles(articles)), tags: [...tags], }}
|
<ArticlesList
|
||||||
emptyPlaceholder='Не найдено ни одной статьи...' />
|
client:load
|
||||||
|
data={{
|
||||||
|
articles: formatToArticleBlocks(transformArticles(articles)),
|
||||||
|
tags: [...tags],
|
||||||
|
}}
|
||||||
|
emptyPlaceholder='Не найдено ни одной статьи...'
|
||||||
|
/>
|
||||||
</section>
|
</section>
|
||||||
</PageLayout>
|
</PageLayout>
|
||||||
|
|||||||
1
src/types/main.d.ts
vendored
1
src/types/main.d.ts
vendored
@ -11,6 +11,7 @@ declare global {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ArticleType = {
|
type ArticleType = {
|
||||||
|
slug: string;
|
||||||
title: string;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
author: string;
|
author: string;
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { MarkdownInstance } from 'astro';
|
|||||||
|
|
||||||
export const transformArticles = (articles: MarkdownInstance<ArticleType>[]): ArticleType[] =>
|
export const transformArticles = (articles: MarkdownInstance<ArticleType>[]): ArticleType[] =>
|
||||||
articles.map((article) => ({
|
articles.map((article) => ({
|
||||||
|
slug: article.frontmatter.slug,
|
||||||
title: article.frontmatter.title,
|
title: article.frontmatter.title,
|
||||||
description: article.frontmatter.description,
|
description: article.frontmatter.description,
|
||||||
author: article.frontmatter.author,
|
author: article.frontmatter.author,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user