mirror of
https://github.com/sergeyyarkov/yarkov.tech.git
synced 2026-10-11 10:29:21 +03:00
refactor: moved some elements into components
This commit is contained in:
parent
d0e299e194
commit
85e854cc71
@ -1,5 +1,5 @@
|
||||
{
|
||||
"printWidth": 150,
|
||||
"printWidth": 100,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
|
||||
@ -8,7 +8,7 @@ Personal website created with Astro.
|
||||
### Todo
|
||||
|
||||
- [x] HTML layout
|
||||
- [ ] Adaptive & Responsive layout
|
||||
- [x] Adaptive & Responsive layout
|
||||
- [ ] Dark/Light Theme
|
||||
- [ ] Multilang web-site
|
||||
- [ ] Form handling via Netlify Forms
|
||||
|
||||
@ -10,10 +10,10 @@
|
||||
},
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"dev": "astro dev --host",
|
||||
"start": "astro dev",
|
||||
"build": "astro build",
|
||||
"preview": "astro preview"
|
||||
"preview": "astro preview --host"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@astrojs/solid-js": "^0.2.1",
|
||||
|
||||
5
src/components/ArticleContent/ArticleContent.astro
Normal file
5
src/components/ArticleContent/ArticleContent.astro
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
import './ArticleContent.scss';
|
||||
---
|
||||
|
||||
<div class='article-content '><slot></slot></div>
|
||||
84
src/components/ArticleContent/ArticleContent.scss
Normal file
84
src/components/ArticleContent/ArticleContent.scss
Normal file
@ -0,0 +1,84 @@
|
||||
.article-content {
|
||||
& p,
|
||||
& em,
|
||||
& q {
|
||||
line-height: var(--line-height-base);
|
||||
font-size: var(--font-size-lg);
|
||||
}
|
||||
|
||||
& ul {
|
||||
padding-left: 1.5rem;
|
||||
line-height: var(--line-height-base);
|
||||
margin-bottom: 1.5rem;
|
||||
margin-top: 0;
|
||||
& li {
|
||||
font-size: var(--font-size-lg);
|
||||
line-height: var(--line-height-base);
|
||||
margin-bottom: 0.5rem;
|
||||
padding-left: 0.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
& q {
|
||||
font-style: italic;
|
||||
quotes: '«' '»';
|
||||
}
|
||||
|
||||
& h1,
|
||||
& h2,
|
||||
& h3,
|
||||
& h4 {
|
||||
margin-top: 3rem;
|
||||
margin-bottom: 1rem;
|
||||
padding-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
& h1:not(:first-child),
|
||||
& h2:not(:first-child),
|
||||
& h3:not(:first-child),
|
||||
& h4:not(:first-child) {
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
& h2 {
|
||||
font-size: var(--font-size-2xl);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
& h3 {
|
||||
font-size: var(--font-size-xl);
|
||||
}
|
||||
|
||||
& h4 {
|
||||
font-size: var(--font-size-lg);
|
||||
}
|
||||
|
||||
& a {
|
||||
color: var(--color-blue-500);
|
||||
text-underline-offset: 2.5px;
|
||||
text-decoration-color: var(--color-blue-500);
|
||||
font-weight: 500;
|
||||
&:hover {
|
||||
color: var(--color-blue-500);
|
||||
text-decoration-thickness: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
& figure {
|
||||
margin: 0 0 1em;
|
||||
}
|
||||
|
||||
& figcaption {
|
||||
font-style: italic;
|
||||
font-size: 1em;
|
||||
text-align: center;
|
||||
margin: 0.7rem 0;
|
||||
}
|
||||
|
||||
& img {
|
||||
display: block;
|
||||
border-radius: var(--border-radius-md);
|
||||
max-width: 100%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
1
src/components/ArticleContent/index.ts
Normal file
1
src/components/ArticleContent/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './ArticleContent.astro';
|
||||
@ -6,7 +6,6 @@
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
column-gap: 10px;
|
||||
row-gap: 5px;
|
||||
color: var(--font-color);
|
||||
padding: 8px 15px;
|
||||
& h3 {
|
||||
@ -22,6 +21,7 @@
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
@ -34,6 +34,7 @@
|
||||
|
||||
@media screen and (max-width: 560px) {
|
||||
.article-item {
|
||||
margin-bottom: 10px;
|
||||
& a {
|
||||
flex-direction: column-reverse;
|
||||
align-items: flex-start;
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import type { IArticle } from '@root/interfaces';
|
||||
import type { ArticleType } from '@root/types';
|
||||
|
||||
import './ArticleItem.scss';
|
||||
import { Component } from 'solid-js';
|
||||
import { DateFormatter } from '@utils/date';
|
||||
|
||||
export type ArticleItemProps = {
|
||||
data: IArticle;
|
||||
data: ArticleType;
|
||||
};
|
||||
|
||||
export const ArticleItem: Component<ArticleItemProps> = (props) => {
|
||||
|
||||
@ -1,19 +1,20 @@
|
||||
import "./ArticlesList.scss";
|
||||
import { Component, createSignal, Show, For } from "solid-js";
|
||||
import { PageHeading } from "@components/PageHeading";
|
||||
import type { IArticle } from "@root/interfaces";
|
||||
import * as utils from "@root/utils/articles";
|
||||
import { ArticleItem } from "../ArticleItem";
|
||||
import './ArticlesList.scss';
|
||||
import { Component, createSignal, Show, For } from 'solid-js';
|
||||
import type { ArticleType } from '@root/types';
|
||||
|
||||
import PageHeading from '@components/PageHeading';
|
||||
import * as utils from '@root/utils/articles';
|
||||
import { ArticleItem } from '../ArticleItem';
|
||||
|
||||
export type ArticlesListProps = {
|
||||
data: IArticle[];
|
||||
data: ArticleType[];
|
||||
tags: string[];
|
||||
};
|
||||
|
||||
export const ArticlesList: Component<ArticlesListProps> = (props) => {
|
||||
const [articles, setArticles] = createSignal(utils.filterArticlesByYear(props.data));
|
||||
const [selectedTags, setSelectedTags] = createSignal<string[]>([]);
|
||||
const [filteredArticles, setFilteredArticles] = createSignal<IArticle[]>([]);
|
||||
const [filteredArticles, setFilteredArticles] = createSignal<ArticleType[]>([]);
|
||||
|
||||
return (
|
||||
<section class="articles container">
|
||||
@ -23,38 +24,24 @@ export const ArticlesList: Component<ArticlesListProps> = (props) => {
|
||||
связаны с IT тематикой находятся здесь."
|
||||
/>
|
||||
<div class="articles-search">
|
||||
<svg
|
||||
width="25"
|
||||
height="24"
|
||||
viewBox="0 0 25 24"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M10.6026 3.60001C11.8798 3.59993 13.1316 3.94283 14.2172 4.5901C15.3028 5.23736 16.179 6.16331 16.7472 7.26368C17.3154 8.36405 17.5531 9.59517 17.4334 10.8184C17.3137 12.0417 16.8415 13.2085 16.0699 14.1876L21.2427 19.164C21.4085 19.3248 21.5053 19.5399 21.5135 19.7662C21.5218 19.9926 21.4411 20.2137 21.2874 20.3853C21.1337 20.5569 20.9184 20.6665 20.6845 20.6921C20.4505 20.7178 20.2151 20.6576 20.0253 20.5236L19.9205 20.436L14.7476 15.4596C13.8802 16.0922 12.8687 16.5172 11.7968 16.6994C10.7248 16.8815 9.62324 16.8157 8.58298 16.5074C7.54272 16.199 6.59366 15.6569 5.81422 14.9259C5.03478 14.1949 4.44733 13.296 4.10042 12.3034C3.7535 11.3108 3.65708 10.2531 3.81911 9.21757C3.98114 8.18204 4.39698 7.19847 5.03226 6.34812C5.66755 5.49778 6.50404 4.80506 7.47263 4.32721C8.44122 3.84936 9.51411 3.60009 10.6026 3.60001V3.60001ZM10.6026 5.40001C9.27934 5.40001 8.01025 5.90572 7.07454 6.80589C6.13884 7.70607 5.61316 8.92697 5.61316 10.2C5.61316 11.473 6.13884 12.6939 7.07454 13.5941C8.01025 14.4943 9.27934 15 10.6026 15C11.9259 15 13.195 14.4943 14.1307 13.5941C15.0664 12.6939 15.5921 11.473 15.5921 10.2C15.5921 8.92697 15.0664 7.70607 14.1307 6.80589C13.195 5.90572 11.9259 5.40001 10.6026 5.40001Z"></path>
|
||||
<svg width="25" height="24" viewBox="0 0 25 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.6026 3.60001C11.8798 3.59993 13.1316 3.94283 14.2172 4.5901C15.3028 5.23736 16.179 6.16331 16.7472 7.26368C17.3154 8.36405 17.5531 9.59517 17.4334 10.8184C17.3137 12.0417 16.8415 13.2085 16.0699 14.1876L21.2427 19.164C21.4085 19.3248 21.5053 19.5399 21.5135 19.7662C21.5218 19.9926 21.4411 20.2137 21.2874 20.3853C21.1337 20.5569 20.9184 20.6665 20.6845 20.6921C20.4505 20.7178 20.2151 20.6576 20.0253 20.5236L19.9205 20.436L14.7476 15.4596C13.8802 16.0922 12.8687 16.5172 11.7968 16.6994C10.7248 16.8815 9.62324 16.8157 8.58298 16.5074C7.54272 16.199 6.59366 15.6569 5.81422 14.9259C5.03478 14.1949 4.44733 13.296 4.10042 12.3034C3.7535 11.3108 3.65708 10.2531 3.81911 9.21757C3.98114 8.18204 4.39698 7.19847 5.03226 6.34812C5.66755 5.49778 6.50404 4.80506 7.47263 4.32721C8.44122 3.84936 9.51411 3.60009 10.6026 3.60001V3.60001ZM10.6026 5.40001C9.27934 5.40001 8.01025 5.90572 7.07454 6.80589C6.13884 7.70607 5.61316 8.92697 5.61316 10.2C5.61316 11.473 6.13884 12.6939 7.07454 13.5941C8.01025 14.4943 9.27934 15 10.6026 15C11.9259 15 13.195 14.4943 14.1307 13.5941C15.0664 12.6939 15.5921 11.473 15.5921 10.2C15.5921 8.92697 15.0664 7.70607 14.1307 6.80589C13.195 5.90572 11.9259 5.40001 10.6026 5.40001Z"></path>
|
||||
</svg>
|
||||
<input type="text" placeholder="поиск по названию или описанию..." />
|
||||
<span>{props.data.length}</span>
|
||||
</div>
|
||||
<div class="articles-tags">
|
||||
<nav>
|
||||
<ul class='list-links'>
|
||||
<ul class="list-links">
|
||||
<For each={props.tags}>
|
||||
{(tag) => (
|
||||
<li
|
||||
style={
|
||||
selectedTags().includes(tag) &&
|
||||
"border-color: var(--border-color-hover);"
|
||||
}
|
||||
style={selectedTags().includes(tag) && 'border-color: var(--border-color-hover);'}
|
||||
class="btn-link"
|
||||
onClick={() => {
|
||||
if (selectedTags().includes(tag)) {
|
||||
setSelectedTags(
|
||||
selectedTags().filter(
|
||||
(selectedTag) => selectedTag !== tag
|
||||
)
|
||||
);
|
||||
setFilteredArticles(
|
||||
filteredArticles().filter((a) => !a.tags.includes(tag))
|
||||
);
|
||||
setSelectedTags(selectedTags().filter((selectedTag) => selectedTag !== tag));
|
||||
setFilteredArticles(filteredArticles().filter((a) => !a.tags.includes(tag)));
|
||||
return;
|
||||
}
|
||||
setSelectedTags([...selectedTags(), tag]);
|
||||
@ -64,7 +51,8 @@ export const ArticlesList: Component<ArticlesListProps> = (props) => {
|
||||
}
|
||||
});
|
||||
}}
|
||||
data-tag={tag.toLocaleLowerCase()}>
|
||||
data-tag={tag.toLocaleLowerCase()}
|
||||
>
|
||||
{tag}
|
||||
</li>
|
||||
)}
|
||||
|
||||
@ -19,4 +19,4 @@ const { title = SITE_METADATA.title, description } = Astro.props as Props;
|
||||
{title}
|
||||
</title>
|
||||
<meta name='title' content={title} />
|
||||
{description && <meta name="description" content={description} />}
|
||||
<meta name="description" content={description || SITE_METADATA.description} />
|
||||
|
||||
@ -3,3 +3,9 @@
|
||||
width: 100%;
|
||||
margin-top: 5rem;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.site-content {
|
||||
margin-top: 3rem;
|
||||
}
|
||||
}
|
||||
|
||||
23
src/components/ContactForm/ContactForm.scss
Normal file
23
src/components/ContactForm/ContactForm.scss
Normal file
@ -0,0 +1,23 @@
|
||||
.contact-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 7px;
|
||||
margin-bottom: 3.125rem;
|
||||
|
||||
& div:first-of-type {
|
||||
display: flex;
|
||||
column-gap: 10px;
|
||||
}
|
||||
|
||||
& div:last-of-type {
|
||||
text-align: center;
|
||||
margin-top: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 560px) {
|
||||
.contact-form div:first-of-type {
|
||||
flex-direction: column;
|
||||
row-gap: 7px;
|
||||
}
|
||||
}
|
||||
27
src/components/ContactForm/ContactForm.tsx
Normal file
27
src/components/ContactForm/ContactForm.tsx
Normal file
@ -0,0 +1,27 @@
|
||||
import type { Component } from 'solid-js';
|
||||
import './ContactForm.scss';
|
||||
|
||||
type InputFieldsType = {
|
||||
name: string;
|
||||
email: string;
|
||||
subject: string;
|
||||
message: string;
|
||||
};
|
||||
|
||||
const ContactForm: Component = () => {
|
||||
return (
|
||||
<form class="contact-form" name="contact" method="post" data-netlify="true">
|
||||
<div>
|
||||
<input name="name" placeholder="Ваше имя" type="text" />
|
||||
<input name="email" placeholder="example@email.com" type="text" />
|
||||
</div>
|
||||
<input name="subject" placeholder="Тема сообщения" type="text" />
|
||||
<textarea name="message" placeholder="Ваше сообщение..." id="message" cols={30} rows={10} />
|
||||
<div>
|
||||
<button type="submit">Отправить</button>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export default ContactForm;
|
||||
1
src/components/ContactForm/index.ts
Normal file
1
src/components/ContactForm/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './ContactForm';
|
||||
24
src/components/ContactsList/ContactsList.astro
Normal file
24
src/components/ContactsList/ContactsList.astro
Normal file
@ -0,0 +1,24 @@
|
||||
---
|
||||
import { SITE_METADATA } from '@root/config';
|
||||
import { Icon } from 'astro-icon';
|
||||
import './ContactsList.scss';
|
||||
|
||||
const {
|
||||
contacts: { email, githubUrl, telegramUrl },
|
||||
} = SITE_METADATA;
|
||||
---
|
||||
|
||||
<div class='contacts-list'>
|
||||
<a href={'mailto:' + email}
|
||||
>Email
|
||||
<Icon name='arrow-up-right' width='15' height='15' />
|
||||
</a>
|
||||
<a href={githubUrl} target='_blank'
|
||||
>Github
|
||||
<Icon name='arrow-up-right' width='15' height='15' />
|
||||
</a>
|
||||
<a href={telegramUrl} target='_blank'
|
||||
>Telegram
|
||||
<Icon name='arrow-up-right' width='15' height='15' />
|
||||
</a>
|
||||
</div>
|
||||
11
src/components/ContactsList/ContactsList.scss
Normal file
11
src/components/ContactsList/ContactsList.scss
Normal file
@ -0,0 +1,11 @@
|
||||
.contacts-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: 40px;
|
||||
row-gap: 30px;
|
||||
|
||||
& a {
|
||||
display: flex;
|
||||
column-gap: 5px;
|
||||
}
|
||||
}
|
||||
1
src/components/ContactsList/index.ts
Normal file
1
src/components/ContactsList/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './ContactsList.astro';
|
||||
@ -135,7 +135,10 @@
|
||||
z-index: 9999;
|
||||
& .container {
|
||||
flex-direction: column;
|
||||
row-gap: 2.5rem;
|
||||
row-gap: 3rem;
|
||||
}
|
||||
&__logo {
|
||||
align-self: flex-start;
|
||||
}
|
||||
&__nav {
|
||||
display: block;
|
||||
@ -144,9 +147,9 @@
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
& li {
|
||||
margin-bottom: 1.2rem;
|
||||
margin-bottom: 30px;
|
||||
& a {
|
||||
font-size: var(--font-size-lg);
|
||||
font-size: var(--font-size-xl);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -164,7 +167,7 @@
|
||||
display: block;
|
||||
position: absolute;
|
||||
bottom: 45px;
|
||||
right: var(--content-padding);
|
||||
right: 2.5rem;
|
||||
& svg {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
|
||||
@ -18,7 +18,7 @@ export const NavToggle: Component = () => {
|
||||
});
|
||||
|
||||
return (
|
||||
<button id="nav-toggle" class="header__button" onClick={toggleNav} type="button" aria-pressed={isNavShown() ? 'true' : 'false'}>
|
||||
<button id="nav-toggle" class="header__button" onClick={toggleNav} type="button" aria-pressed={isNavShown() ? 'true' : 'false'} aria-label="Menu">
|
||||
<span />
|
||||
<span />
|
||||
<span />
|
||||
|
||||
17
src/components/PageHeading/PageHeading.astro
Normal file
17
src/components/PageHeading/PageHeading.astro
Normal file
@ -0,0 +1,17 @@
|
||||
---
|
||||
import './PageHeading.scss';
|
||||
|
||||
export interface Props {
|
||||
heading: string;
|
||||
description?: string | undefined;
|
||||
}
|
||||
|
||||
const { heading, description } = Astro.props as Props;
|
||||
---
|
||||
|
||||
<header class='page-heading'>
|
||||
<h1 style={!description && 'margin-bottom: 2.5rem;'}>
|
||||
{heading}
|
||||
</h1>
|
||||
{description && <p>{description}</p>}
|
||||
</header>
|
||||
@ -1,13 +0,0 @@
|
||||
.PageHeading {
|
||||
font-size: var(--font-size-4xl);
|
||||
text-align: center;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.PageDescription {
|
||||
font-size: var(--font-size-lg);
|
||||
text-align: center;
|
||||
max-width: 650px;
|
||||
margin: 0 auto;
|
||||
margin-bottom: 2.5rem;
|
||||
}
|
||||
14
src/components/PageHeading/PageHeading.scss
Normal file
14
src/components/PageHeading/PageHeading.scss
Normal file
@ -0,0 +1,14 @@
|
||||
.page-heading {
|
||||
& h1 {
|
||||
font-size: var(--font-size-4xl);
|
||||
text-align: center;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
& p {
|
||||
font-size: var(--font-size-lg);
|
||||
text-align: center;
|
||||
max-width: 650px;
|
||||
margin: 0 auto;
|
||||
margin-bottom: 2.5rem;
|
||||
}
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
import { Component } from 'solid-js';
|
||||
import styles from './PageHeading.module.scss';
|
||||
|
||||
export type PageHeadingProps = {
|
||||
heading: string;
|
||||
description?: string | undefined;
|
||||
};
|
||||
|
||||
export const PageHeading: Component<PageHeadingProps> = (props) => {
|
||||
return (
|
||||
<header>
|
||||
<h1
|
||||
class={styles.PageHeading}
|
||||
style={!props.description && 'margin-bottom: 2.5rem;'}>
|
||||
{props.heading}
|
||||
</h1>
|
||||
{props.description && <p class={styles.PageDescription}>{props.description}</p>}
|
||||
</header>
|
||||
);
|
||||
};
|
||||
@ -1 +1 @@
|
||||
export * from './PageHeading';
|
||||
export { default } from './PageHeading.astro';
|
||||
|
||||
45
src/components/ProjectItem/ProjectItem.astro
Normal file
45
src/components/ProjectItem/ProjectItem.astro
Normal file
@ -0,0 +1,45 @@
|
||||
---
|
||||
import type { ProjectType } from '@root/types';
|
||||
|
||||
import './ProjectItem.scss';
|
||||
import { Icon } from 'astro-icon';
|
||||
|
||||
export interface Props {
|
||||
data: ProjectType;
|
||||
}
|
||||
|
||||
const { data } = Astro.props as Props;
|
||||
const { icon, name, year, description, articleUrl, sourceUrl, demoUrl } = data;
|
||||
---
|
||||
|
||||
<div class='project-item'>
|
||||
<div class='project-item__heading'>
|
||||
<h2>
|
||||
<Icon name={icon} width='20' height='20' />
|
||||
{name}
|
||||
</h2>
|
||||
<span>{year}</span>
|
||||
</div>
|
||||
<div class='project-item__description'>
|
||||
<p>{description}</p>
|
||||
</div>
|
||||
<div class='project-item__links'>
|
||||
{articleUrl && (
|
||||
<a class="btn-link" href={articleUrl} target="_blank">
|
||||
Статья
|
||||
</a>
|
||||
)}
|
||||
{sourceUrl && (
|
||||
<a class="btn-link" href={sourceUrl} target="_blank">
|
||||
Исходник
|
||||
<Icon name="link-out" width="15" height="15" />
|
||||
</a>
|
||||
)}
|
||||
{demoUrl && (
|
||||
<a class="btn-link" href={demoUrl} target="_blank">
|
||||
Демо
|
||||
<Icon name="link-out" width="15" height="15" />
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
36
src/components/ProjectItem/ProjectItem.scss
Normal file
36
src/components/ProjectItem/ProjectItem.scss
Normal file
@ -0,0 +1,36 @@
|
||||
.project-item {
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--border-radius-lg);
|
||||
padding: 15px;
|
||||
|
||||
&__heading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
|
||||
& h2 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 1.2rem;
|
||||
font-weight: 600;
|
||||
column-gap: 10px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
& span {
|
||||
font-size: var(--font-size-sm);
|
||||
color: var(--font-color-gray);
|
||||
}
|
||||
}
|
||||
|
||||
&__description {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
&__links {
|
||||
display: flex;
|
||||
column-gap: 5px;
|
||||
}
|
||||
}
|
||||
1
src/components/ProjectItem/index.ts
Normal file
1
src/components/ProjectItem/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './ProjectItem.astro';
|
||||
7
src/components/ProjectsList/ProjectsList.astro
Normal file
7
src/components/ProjectsList/ProjectsList.astro
Normal file
@ -0,0 +1,7 @@
|
||||
---
|
||||
import './ProjectsList.scss';
|
||||
---
|
||||
|
||||
<div class='projects-list'>
|
||||
<slot></slot>
|
||||
</div>
|
||||
5
src/components/ProjectsList/ProjectsList.scss
Normal file
5
src/components/ProjectsList/ProjectsList.scss
Normal file
@ -0,0 +1,5 @@
|
||||
.projects-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
1
src/components/ProjectsList/index.ts
Normal file
1
src/components/ProjectsList/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './ProjectsList.astro';
|
||||
@ -1,12 +1,12 @@
|
||||
---
|
||||
import type { IArticle } from '@root/interfaces';
|
||||
import type { ArticleType } from '@root/types';
|
||||
|
||||
import './RecentArticles.scss';
|
||||
import { ArticleItem } from '@components/ArticleItem';
|
||||
import { sortArtcilesByDate } from '@utils/articles';
|
||||
|
||||
export interface Props {
|
||||
articles: IArticle[];
|
||||
articles: ArticleType[];
|
||||
length?: number | undefined;
|
||||
}
|
||||
|
||||
|
||||
30
src/components/SkillsBlock/SkillsBlock.astro
Normal file
30
src/components/SkillsBlock/SkillsBlock.astro
Normal file
@ -0,0 +1,30 @@
|
||||
---
|
||||
import type { SkillsType } from '@root/types';
|
||||
|
||||
import { Icon } from 'astro-icon';
|
||||
import './SkillsBlock.scss';
|
||||
|
||||
export interface Props {
|
||||
data: SkillsType;
|
||||
}
|
||||
|
||||
const {
|
||||
data: { title, list },
|
||||
} = Astro.props as Props;
|
||||
---
|
||||
|
||||
<div class='skills-block'>
|
||||
<div>
|
||||
<h3>{title}</h3>
|
||||
</div>
|
||||
<div>
|
||||
<ul>
|
||||
{list.map((entry) => (
|
||||
<li>
|
||||
{entry.icon && <Icon name={entry.icon} width="20" height="20" />}
|
||||
{entry.name}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
36
src/components/SkillsBlock/SkillsBlock.scss
Normal file
36
src/components/SkillsBlock/SkillsBlock.scss
Normal file
@ -0,0 +1,36 @@
|
||||
.skills-block {
|
||||
& h3 {
|
||||
font-size: var(--font-size-md);
|
||||
color: var(--font-color-grayer);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
& ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
|
||||
& li {
|
||||
font-weight: 400;
|
||||
margin-bottom: 10px;
|
||||
font-size: var(--font-size-sm);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
column-gap: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 500px) {
|
||||
.skills-block {
|
||||
& h3 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
& ul {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
1
src/components/SkillsBlock/index.ts
Normal file
1
src/components/SkillsBlock/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './SkillsBlock.astro';
|
||||
5
src/components/SkillsList/SkillsList.astro
Normal file
5
src/components/SkillsList/SkillsList.astro
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
import './SkillsList.scss';
|
||||
---
|
||||
|
||||
<div class='skills-list'><slot></slot></div>
|
||||
14
src/components/SkillsList/SkillsList.scss
Normal file
14
src/components/SkillsList/SkillsList.scss
Normal file
@ -0,0 +1,14 @@
|
||||
.skills-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
column-gap: 40px;
|
||||
row-gap: 30px;
|
||||
margin-top: 5rem;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 500px) {
|
||||
.skills-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
1
src/components/SkillsList/index.ts
Normal file
1
src/components/SkillsList/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './SkillsList.astro';
|
||||
@ -2,6 +2,8 @@ export const SITE_METADATA = {
|
||||
title: 'Sergey Yarkov',
|
||||
name: 'yarkov.tech',
|
||||
author: 'Sergey Yarkov',
|
||||
description:
|
||||
'Здесь я публикую статьи и туториалы на различные IT темы, а также рассказываю о своих проектах которые я сделал за последние несколько лет.',
|
||||
contacts: {
|
||||
email: 'serzh.yarkov@gmail.com',
|
||||
githubUrl: 'https://github.com/sergeyyarkov',
|
||||
@ -13,6 +15,75 @@ export const SITE_METADATA = {
|
||||
{ name: 'Обо мне', url: '/about' },
|
||||
{ name: 'Контакты', url: '/contacts' },
|
||||
],
|
||||
projects: [],
|
||||
skills: [],
|
||||
projects: [
|
||||
{
|
||||
icon: 'nodejs',
|
||||
name: 'yarkov.tech',
|
||||
year: '2022',
|
||||
description:
|
||||
'Мой персональный веб-сайт где находятся информация обо мне и список различных статей, туториалов...',
|
||||
sourceUrl: 'https://github.com/sergeyyarkov/yarkov.tech',
|
||||
demoUrl: 'https://yarkov.tech',
|
||||
articleUrl: undefined,
|
||||
},
|
||||
{
|
||||
icon: 'triangle_filled',
|
||||
name: 'devthread',
|
||||
year: '2021',
|
||||
description:
|
||||
'Статический веб-сайт для публикации различных статей, разработанный при помощи Gatsby.js и NetlifyCMS',
|
||||
sourceUrl: 'https://github.com/sergeyyarkov/devthread.ru',
|
||||
demoUrl: 'https://devthread.netlify.app',
|
||||
articleUrl: undefined,
|
||||
},
|
||||
],
|
||||
skills: [
|
||||
{
|
||||
title: 'Программирование',
|
||||
list: [
|
||||
{ icon: 'nodejs', name: 'Javascript/Typescript' },
|
||||
{ icon: 'language-c', name: 'C Language' },
|
||||
{ icon: 'mysql', name: 'SQL' },
|
||||
{ icon: 'graphql', name: 'GraphQL' },
|
||||
{ icon: 'avr', name: 'AVR instruction set' },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Фреймворки',
|
||||
list: [
|
||||
{ icon: 'adonisjs', name: 'Adonis.js' },
|
||||
{ icon: 'expressjs', name: 'Express.js' },
|
||||
{ icon: 'socket-io', name: 'Socket.io' },
|
||||
{ icon: 'astro_filled', name: 'Astro' },
|
||||
{ icon: 'reactjs', name: 'React.js' },
|
||||
{ icon: 'gatsbyjs', name: 'Gatsby.js' },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Инструменты',
|
||||
list: [
|
||||
{ icon: 'nodejs', name: 'Node.js' },
|
||||
{ icon: 'docker', name: 'Docker' },
|
||||
{ icon: 'git-branch', name: 'Git/gitflow' },
|
||||
{ icon: 'sass', name: 'SASS' },
|
||||
{ icon: 'webpack', name: 'Webpack' },
|
||||
{ icon: 'vite', name: 'Vite.js' },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Базы данных',
|
||||
list: [
|
||||
{ icon: 'postgresql', name: 'PostgreSQL' },
|
||||
{ icon: 'mongodb', name: 'MongoDB' },
|
||||
{ icon: 'redis', name: 'Redis' },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Языки',
|
||||
list: [
|
||||
{ icon: undefined, name: 'Русский (родной)' },
|
||||
{ icon: undefined, name: 'Английский (B1)' },
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@ -1,6 +0,0 @@
|
||||
export interface IArticle {
|
||||
title: string;
|
||||
author: string;
|
||||
tags: Array<string>;
|
||||
published_at: number;
|
||||
}
|
||||
@ -2,6 +2,7 @@
|
||||
import '../styles/prism-one-light.css';
|
||||
import PageLayout from '@root/layouts/default.astro';
|
||||
import { Markdown } from 'astro/components';
|
||||
import ArticleContent from '@components/ArticleContent';
|
||||
|
||||
export function getStaticPaths() {
|
||||
return [{ params: { article: 'article' } }];
|
||||
@ -37,7 +38,7 @@ export function getStaticPaths() {
|
||||
</figure>
|
||||
</div>
|
||||
</header>
|
||||
<div class='article-content'>
|
||||
<ArticleContent>
|
||||
<Markdown>
|
||||
## Introduction
|
||||
|
||||
@ -99,7 +100,7 @@ export function getStaticPaths() {
|
||||
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>
|
||||
</div>
|
||||
</ArticleContent>
|
||||
<footer>
|
||||
<h2>Comments</h2>
|
||||
</footer>
|
||||
@ -112,8 +113,6 @@ export function getStaticPaths() {
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
column-gap: 5px;
|
||||
row-gap: 1.2rem;
|
||||
|
||||
& ul {
|
||||
max-width: 350px;
|
||||
}
|
||||
@ -122,6 +121,7 @@ export function getStaticPaths() {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
@ -172,6 +172,7 @@ export function getStaticPaths() {
|
||||
|
||||
& div {
|
||||
align-self: flex-end;
|
||||
margin-bottom: 1.2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,200 +1,28 @@
|
||||
---
|
||||
import PageLayout from '@layouts/default.astro';
|
||||
import { SITE_METADATA } from '@root/config';
|
||||
import { Markdown } from 'astro/components';
|
||||
import { PageHeading } from '@root/components/PageHeading';
|
||||
import { Icon } from 'astro-icon'
|
||||
import PageLayout from '@layouts/default.astro';
|
||||
import PageHeading from '@components/PageHeading';
|
||||
import SkillsList from '@components/SkillsList';
|
||||
import SkillsBlock from '@components/SkillsBlock';
|
||||
|
||||
// TODO: import content based on user ui language
|
||||
import * as aboutContent from '../../content/about/ru/index.md';
|
||||
import ArticleContent from '@components/ArticleContent';
|
||||
|
||||
const { skills } = SITE_METADATA;
|
||||
---
|
||||
|
||||
<PageLayout title='Обо мне | Sergey Yarkov'>
|
||||
<section class='about container'>
|
||||
<section class='container'>
|
||||
<PageHeading heading='Обо мне' />
|
||||
<div class='article-content'>
|
||||
<Markdown content={aboutContent.rawContent()} />
|
||||
</div>
|
||||
<div class='about-skills'>
|
||||
<div class='skills-list'>
|
||||
<div class='skills-list__heading'>
|
||||
<h3>Программирование</h3>
|
||||
</div>
|
||||
<div class='skills-list__content'>
|
||||
<ul>
|
||||
<li>
|
||||
<Icon name='nodejs' width='20' height='20' />
|
||||
Javascript/Typescript
|
||||
</li>
|
||||
<li>
|
||||
<Icon name='language-c' width='20' height='20' />
|
||||
C Language
|
||||
</li>
|
||||
<li>
|
||||
<Icon name='mysql' width='20' height='20' />
|
||||
SQL
|
||||
</li>
|
||||
<li>
|
||||
<Icon name='graphql' width='20' height='20' />
|
||||
GraphQL
|
||||
</li>
|
||||
<li>
|
||||
<Icon name='avr' width='20' height='20' />
|
||||
AVR instruction set
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class='skills-list'>
|
||||
<div class='skills-list__heading'>
|
||||
<h3>Фреймворки</h3>
|
||||
</div>
|
||||
<div class='skills-list__content'>
|
||||
<ul>
|
||||
<li>
|
||||
<Icon name='adonisjs' width='20' height='20' />
|
||||
Adonis.js
|
||||
</li>
|
||||
<li>
|
||||
<Icon name='expressjs' width='20' height='20' />
|
||||
Express.js
|
||||
</li>
|
||||
<li>
|
||||
<Icon name='socket-io' width='20' height='20' />
|
||||
Socket.io
|
||||
</li>
|
||||
<li>
|
||||
<Icon name='reactjs' width='20' height='20' />
|
||||
React.js
|
||||
</li>
|
||||
<li>
|
||||
<Icon name='astro_filled' width='20' height='20' />
|
||||
Astro.js
|
||||
</li>
|
||||
<li>
|
||||
<Icon name='gatsbyjs' width='20' height='20' />
|
||||
Gastby.js
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class='skills-list'>
|
||||
<div class='skills-list__heading'>
|
||||
<h3>Инструменты</h3>
|
||||
</div>
|
||||
<div class='skills-list__content'>
|
||||
<ul>
|
||||
<li>
|
||||
<Icon name='nodejs' width='20' height='20' />
|
||||
Node.js
|
||||
</li>
|
||||
<li>
|
||||
<Icon name='docker' width='20' height='20' />
|
||||
Docker
|
||||
</li>
|
||||
<li>
|
||||
<Icon name='git-branch' width='20' height='20' />
|
||||
Git/gitflow
|
||||
</li>
|
||||
<li>
|
||||
<Icon name='sass' width='20' height='20' />
|
||||
SASS
|
||||
</li>
|
||||
<li>
|
||||
<Icon name='vite' width='20' height='20' />
|
||||
Vite.js
|
||||
</li>
|
||||
<li>
|
||||
<Icon name='webpack' width='20' height='20' />
|
||||
Webpack
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class='skills-list'>
|
||||
<div class='skills-list__heading'>
|
||||
<h3>Базы данных</h3>
|
||||
</div>
|
||||
<div class='skills-list__content'>
|
||||
<ul>
|
||||
<li>
|
||||
<Icon name='postgresql' width='20' height='20' />
|
||||
PostgreSQL
|
||||
</li>
|
||||
<li>
|
||||
<Icon name='mongodb' width='20' height='20' />
|
||||
MongoDB
|
||||
</li>
|
||||
<li>
|
||||
<Icon name='redis' width='20' height='20' />
|
||||
Redis
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class='skills-list'>
|
||||
<div class='skills-list__heading'>
|
||||
<h3>Языки</h3>
|
||||
</div>
|
||||
<div class='skills-list__content'>
|
||||
<ul>
|
||||
<li>Русский (родной)</li>
|
||||
<li>Английский (B1)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ArticleContent>
|
||||
<Markdown content={aboutContent.rawContent()}>
|
||||
|
||||
</Markdown>
|
||||
</ArticleContent>
|
||||
<SkillsList>
|
||||
{skills.map((block) => <SkillsBlock data={block} />)}
|
||||
</SkillsList>
|
||||
</section>
|
||||
<style lang='scss'>
|
||||
.about {
|
||||
&-skills {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
column-gap: 40px;
|
||||
row-gap: 30px;
|
||||
margin-top: 5rem;
|
||||
|
||||
& .skills-list {
|
||||
& h3 {
|
||||
font-size: var(--font-size-md);
|
||||
color: var(--font-color-grayer);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
& ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
|
||||
& li {
|
||||
font-weight: 400;
|
||||
margin-bottom: 10px;
|
||||
font-size: var(--font-size-sm);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
column-gap: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 500px) {
|
||||
.about-skills {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
& .skills-list {
|
||||
& h3 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
& ul {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</PageLayout>
|
||||
@ -1,15 +1,13 @@
|
||||
---
|
||||
import PageLayout from '@layouts/default.astro';
|
||||
import { ArticlesList } from '@components/ArticlesList';
|
||||
import { IArticle } from '@root/interfaces';
|
||||
import { ArticleType } from '@root/types';
|
||||
import * as utils from '@utils/index';
|
||||
|
||||
const articles = await Astro.glob<IArticle>('../../content/articles/**/*.md');
|
||||
const tags = new Set(
|
||||
articles.map((article) => article.frontmatter.tags).flat()
|
||||
);
|
||||
const articles = await Astro.glob<ArticleType>('../../content/articles/**/*.md');
|
||||
const tags = new Set(articles.map((article) => article.frontmatter.tags).flat());
|
||||
---
|
||||
|
||||
<PageLayout title='Статьи | Sergey Yarkov'>
|
||||
<ArticlesList client:load data={utils.transformArticles(articles)} tags={[...tags]} />
|
||||
<!-- <ArticlesList client:load data={utils.transformArticles(articles)} tags={[...tags]} /> -->
|
||||
</PageLayout>
|
||||
@ -1,78 +1,14 @@
|
||||
---
|
||||
import PageLayout from '@layouts/default.astro';
|
||||
import { PageHeading } from '@root/components/PageHeading';
|
||||
import { Icon } from 'astro-icon';
|
||||
import { SITE_METADATA } from '@root/config';
|
||||
import PageHeading from '@components/PageHeading';
|
||||
import ContactForm from '@components/ContactForm';
|
||||
import ContactsList from '@components/ContactsList';
|
||||
---
|
||||
|
||||
<PageLayout title='Контакты | Sergey Yarkov'>
|
||||
<section class='contacts container'>
|
||||
<section class='container'>
|
||||
<PageHeading heading='Контакты' description='Воспользуйтесь формой обратной связи или напишите мне в социальные сети.' />
|
||||
<div class='contacts-form'>
|
||||
<form name='contact' method='POST' data-netlify='true'>
|
||||
<div>
|
||||
<input name='name' placeholder='Ваше имя' type='text' />
|
||||
<input name='email' placeholder='example@email.com' type='text' />
|
||||
</div>
|
||||
<input name='subject' placeholder='Тема сообщения' type='text' />
|
||||
<textarea name='message' placeholder='Ваше сообщение...' id='message' cols={30} rows={10}></textarea>
|
||||
<div>
|
||||
<button type='submit'>Отправить</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class='contacts-link'>
|
||||
<a href={'mailto:' + SITE_METADATA.contacts.email}
|
||||
>Email
|
||||
<Icon name='arrow-up-right' width='15' height='15' />
|
||||
</a>
|
||||
<a href={SITE_METADATA.contacts.githubUrl} target='_blank'
|
||||
>Github
|
||||
<Icon name='arrow-up-right' width='15' height='15' />
|
||||
</a>
|
||||
<a href={SITE_METADATA.contacts.telegramUrl} target='_blank'
|
||||
>Telegram
|
||||
<Icon name='arrow-up-right' width='15' height='15' />
|
||||
</a>
|
||||
</div>
|
||||
<ContactForm client:idle />
|
||||
<ContactsList />
|
||||
</section>
|
||||
<style lang='scss'>
|
||||
.contacts {
|
||||
& form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 7px;
|
||||
margin-bottom: 3.125rem;
|
||||
|
||||
& div:first-of-type {
|
||||
display: flex;
|
||||
column-gap: 10px;
|
||||
}
|
||||
|
||||
& div:last-of-type {
|
||||
text-align: center;
|
||||
margin-top: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
&-link {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: 40px;
|
||||
row-gap: 30px;
|
||||
|
||||
& a {
|
||||
display: flex;
|
||||
column-gap: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 500px) {
|
||||
.contacts form div:first-of-type {
|
||||
flex-direction: column;
|
||||
row-gap: 7px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</PageLayout>
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
---
|
||||
import type { IArticle } from '@root/interfaces';
|
||||
import type { ArticleType } from '@root/types';
|
||||
|
||||
import PageLayout from '@layouts/default.astro';
|
||||
import { Icon } from 'astro-icon';
|
||||
import * as utils from '@utils/index';
|
||||
import RecentArticles from '@root/components/RecentArticles';
|
||||
import PageLayout from '@layouts/default.astro';
|
||||
import RecentArticles from '@components/RecentArticles';
|
||||
|
||||
const data = await Astro.glob<IArticle>('../../content/articles/**/*.md');
|
||||
const data = await Astro.glob<ArticleType>('../../content/articles/**/*.md');
|
||||
const articles = utils.transformArticles(data);
|
||||
---
|
||||
|
||||
@ -18,7 +18,8 @@ const articles = utils.transformArticles(data);
|
||||
<h2>Fullstack <b>web-developer</b> & <b>freelancer</b></h2>
|
||||
</hgroup>
|
||||
<p class='welcome-heading__description'>
|
||||
Я занимаюсь разработкой веб-сайтов и веб-приложений разной сложности, а также публикую различные статьи и туториалы на разные IT темы.
|
||||
Я занимаюсь разработкой веб-сайтов и веб-приложений разной сложности, а также публикую
|
||||
различные статьи и туториалы на разные IT темы.
|
||||
</p>
|
||||
</header>
|
||||
<nav class='welcome-links'>
|
||||
|
||||
@ -1,116 +1,18 @@
|
||||
---
|
||||
import PageLayout from '@layouts/default.astro';
|
||||
import { PageHeading } from '@root/components/PageHeading';
|
||||
import { SITE_METADATA } from '@root/config';
|
||||
import { Icon } from 'astro-icon'
|
||||
import PageLayout from '@layouts/default.astro';
|
||||
import PageHeading from '@root/components/PageHeading';
|
||||
import ProjectsList from '@root/components/ProjectsList';
|
||||
import ProjectItem from '@root/components/ProjectItem';
|
||||
|
||||
const { projects } = SITE_METADATA;
|
||||
---
|
||||
|
||||
<PageLayout title='Проекты | Sergey Yarkov'>
|
||||
<section class='projects container'>
|
||||
<PageHeading heading='Проекты'
|
||||
description='Несколько моих основных проектов которые были разработаны и выложены в открытый доступ.' />
|
||||
<div class='projects-heading'>
|
||||
<a href={SITE_METADATA.contacts.githubUrl + '?tab=repositories' } target='_blank'>
|
||||
смотреть все
|
||||
</a>
|
||||
</div>
|
||||
<div class='projects-list'>
|
||||
<div class='project-item'>
|
||||
<div class='project-item__heading'>
|
||||
<h2>
|
||||
<Icon name='nodejs' width='20' height='20' />
|
||||
yarkov.tech
|
||||
</h2>
|
||||
<span>2022</span>
|
||||
</div>
|
||||
<div class='project-item__description'>
|
||||
<p>
|
||||
Мой персональный веб-сайт где находятся информация обо мне и список
|
||||
различных статей, туториалов...
|
||||
</p>
|
||||
</div>
|
||||
<div class='project-item__links'>
|
||||
<a class='btn-link' href='#'>
|
||||
Исходник
|
||||
<Icon name='link-out' width='15' height='15' />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class='project-item'>
|
||||
<div class='project-item__heading'>
|
||||
<h2>
|
||||
<Icon name='triangle_filled' width='20' height='20' />
|
||||
devthread
|
||||
</h2>
|
||||
<span>2021</span>
|
||||
</div>
|
||||
<div class='project-item__description'>
|
||||
<p>
|
||||
Статический веб-сайт для публикации различных статей, разработанный
|
||||
при помощи Gatsby.js и NetlifyCMS
|
||||
</p>
|
||||
</div>
|
||||
<div class='project-item__links'>
|
||||
<a class='btn-link' href='#'>
|
||||
Статья
|
||||
</a>
|
||||
<a class='btn-link' href='#'>
|
||||
Исходник
|
||||
<Icon name='link-out' width='15' height='15' />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<section class='container'>
|
||||
<PageHeading heading='Проекты' description='Несколько моих основных проектов которые были разработаны и выложены в открытый доступ.' />
|
||||
<ProjectsList>
|
||||
{projects.map((project) => <ProjectItem data={project} />)}
|
||||
</ProjectsList>
|
||||
</section>
|
||||
<style lang='scss'>
|
||||
.projects {
|
||||
&-heading {
|
||||
text-align: right;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
& .projects-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: 10px;
|
||||
|
||||
& .project-item {
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--border-radius-lg);
|
||||
padding: 15px;
|
||||
|
||||
&__heading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
|
||||
& h2 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 1.2rem;
|
||||
font-weight: 600;
|
||||
column-gap: 10px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
& span {
|
||||
font-size: var(--font-size-sm);
|
||||
color: var(--font-color-gray);
|
||||
}
|
||||
}
|
||||
|
||||
&__description {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
&__links {
|
||||
display: flex;
|
||||
column-gap: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</PageLayout>
|
||||
@ -177,6 +177,7 @@ body {
|
||||
color: var(--font-color);
|
||||
font-size: var(--font-size-sm);
|
||||
font-weight: 400;
|
||||
white-space: nowrap;
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
@ -184,91 +185,6 @@ body {
|
||||
}
|
||||
}
|
||||
|
||||
.article-content {
|
||||
& p,
|
||||
& em,
|
||||
& q {
|
||||
line-height: var(--line-height-base);
|
||||
font-size: var(--font-size-lg);
|
||||
}
|
||||
|
||||
& ul {
|
||||
padding-left: 1.5rem;
|
||||
line-height: var(--line-height-base);
|
||||
margin-bottom: 1.5rem;
|
||||
margin-top: 0;
|
||||
& li {
|
||||
font-size: var(--font-size-lg);
|
||||
line-height: var(--line-height-base);
|
||||
margin-bottom: 0.5rem;
|
||||
padding-left: 0.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
& q {
|
||||
font-style: italic;
|
||||
quotes: '«' '»';
|
||||
}
|
||||
|
||||
& h1,
|
||||
& h2,
|
||||
& h3,
|
||||
& h4 {
|
||||
margin-top: 3rem;
|
||||
margin-bottom: 1rem;
|
||||
padding-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
& h1:not(:first-child),
|
||||
& h2:not(:first-child),
|
||||
& h3:not(:first-child),
|
||||
& h4:not(:first-child) {
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
& h2 {
|
||||
font-size: var(--font-size-3xl);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
& h3 {
|
||||
font-size: var(--font-size-2xl);
|
||||
}
|
||||
|
||||
& h4 {
|
||||
font-size: var(--font-size-xl);
|
||||
}
|
||||
|
||||
& a {
|
||||
color: var(--color-blue-500);
|
||||
text-underline-offset: 2.5px;
|
||||
text-decoration-color: var(--color-blue-500);
|
||||
font-weight: 500;
|
||||
&:hover {
|
||||
color: var(--color-blue-500);
|
||||
text-decoration-thickness: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
& figure {
|
||||
margin: 0 0 1em;
|
||||
}
|
||||
|
||||
& figcaption {
|
||||
font-style: italic;
|
||||
font-size: 1em;
|
||||
text-align: center;
|
||||
margin: 0.7rem 0;
|
||||
}
|
||||
|
||||
& img {
|
||||
display: block;
|
||||
border-radius: var(--border-radius-md);
|
||||
max-width: 100%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
li::marker {
|
||||
color: var(--color-blue-500);
|
||||
font-size: 1.3rem;
|
||||
@ -298,6 +214,7 @@ h5 {
|
||||
|
||||
input,
|
||||
textarea {
|
||||
-webkit-appearance: none;
|
||||
font-family: var(--font-family-base);
|
||||
padding: 8px 15px;
|
||||
border: 1px solid var(--border-color);
|
||||
|
||||
21
src/types/index.ts
Normal file
21
src/types/index.ts
Normal file
@ -0,0 +1,21 @@
|
||||
export type ArticleType = {
|
||||
title: string;
|
||||
author: string;
|
||||
tags: Array<string>;
|
||||
published_at: number;
|
||||
};
|
||||
|
||||
export type ProjectType = {
|
||||
icon: string;
|
||||
name: string;
|
||||
year: string;
|
||||
description: string;
|
||||
sourceUrl?: string | undefined;
|
||||
articleUrl?: string | undefined;
|
||||
demoUrl?: string | undefined;
|
||||
};
|
||||
|
||||
export type SkillsType = {
|
||||
title: string;
|
||||
list: Array<{ icon?: string | undefined; name: string }>;
|
||||
};
|
||||
@ -1,8 +1,8 @@
|
||||
import type { IArticle } from '@root/interfaces';
|
||||
import type { ArticleType } from '@root/types';
|
||||
|
||||
import { MarkdownInstance } from 'astro';
|
||||
|
||||
export const transformArticles = (articles: MarkdownInstance<IArticle>[]): IArticle[] =>
|
||||
export const transformArticles = (articles: MarkdownInstance<ArticleType>[]): ArticleType[] =>
|
||||
articles.map((article) => ({
|
||||
title: article.frontmatter.title,
|
||||
author: article.frontmatter.author,
|
||||
@ -10,11 +10,11 @@ export const transformArticles = (articles: MarkdownInstance<IArticle>[]): IArti
|
||||
published_at: article.frontmatter.published_at,
|
||||
}));
|
||||
|
||||
export const sortArtcilesByDate = (articles: IArticle[]): IArticle[] => {
|
||||
export const sortArtcilesByDate = (articles: ArticleType[]): ArticleType[] => {
|
||||
return articles.sort((a, b) => new Date(b.published_at).valueOf() - new Date(a.published_at).valueOf());
|
||||
};
|
||||
|
||||
export const filterArticlesByYear = (articles: IArticle[]): { [year: number]: IArticle[] } => {
|
||||
export const filterArticlesByYear = (articles: ArticleType[]): { [year: number]: ArticleType[] } => {
|
||||
const filtered = {};
|
||||
|
||||
for (const article of articles) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user