commit
f9b68c52be
@ -1,6 +1,8 @@
|
||||
---
|
||||
layout: blog
|
||||
title: How I've built my website
|
||||
segments:
|
||||
- blog
|
||||
published: true
|
||||
date: 2020-02-28T16:00:55.791Z
|
||||
thumbnail: /images/uploads/DSC01202.jpg
|
||||
|
@ -1,6 +1,8 @@
|
||||
---
|
||||
layout: blog
|
||||
title: Custom Redox keyboard assembly
|
||||
segments:
|
||||
- blog
|
||||
published: true
|
||||
date: 2020-04-10T15:16:54.820Z
|
||||
thumbnail: /images/uploads/img_20200301_171735.jpg
|
||||
|
@ -1,6 +1,8 @@
|
||||
---
|
||||
layout: blog
|
||||
title: Transition to Colemak keyboard layout
|
||||
segments:
|
||||
- blog
|
||||
published: true
|
||||
date: 2020-05-11T05:38:18.797Z
|
||||
tags:
|
||||
|
@ -1,6 +1,8 @@
|
||||
---
|
||||
layout: blog
|
||||
title: Samsung Galaxy Buds+ Review
|
||||
segments:
|
||||
- blog
|
||||
published: true
|
||||
date: 2020-06-07T22:58:18.797Z # TODO executable line for generating date
|
||||
tags:
|
||||
|
@ -1,6 +1,8 @@
|
||||
---
|
||||
layout: blog
|
||||
title: I've made an RSS feed
|
||||
segments:
|
||||
- blog
|
||||
published: true
|
||||
date: 2020-06-15T15:48:18.797Z # TODO executable line for generating date
|
||||
tags:
|
||||
|
@ -1,6 +1,8 @@
|
||||
---
|
||||
layout: blog
|
||||
title: WebAssembly briefing
|
||||
segments:
|
||||
- blog
|
||||
published: true
|
||||
date: 2020-08-05T15:38:18.797Z # TODO executable line for generating date
|
||||
tags:
|
||||
|
@ -1,6 +1,8 @@
|
||||
---
|
||||
layout: blog
|
||||
title: How to deal with technical debt
|
||||
segments:
|
||||
- blog
|
||||
published: true
|
||||
date: 2020-11-08T15:57:18.797Z
|
||||
tags:
|
||||
|
@ -1,6 +1,8 @@
|
||||
---
|
||||
layout: blog
|
||||
title: I've moved my site to Netlify
|
||||
segments:
|
||||
- blog
|
||||
published: true
|
||||
date: 2020-11-20T20:35:05.230Z
|
||||
tags:
|
||||
|
@ -1,6 +1,8 @@
|
||||
---
|
||||
layout: blog
|
||||
title: Guide on error handling
|
||||
segments:
|
||||
- blog
|
||||
published: true
|
||||
date: 2020-12-09T14:44:11.948Z
|
||||
tags:
|
||||
|
@ -1,6 +1,8 @@
|
||||
---
|
||||
layout: blog
|
||||
title: Logging recommendations
|
||||
segments:
|
||||
- blog
|
||||
published: true
|
||||
date: 2020-12-18T14:39:53.533Z
|
||||
tags:
|
||||
|
@ -1,6 +1,8 @@
|
||||
---
|
||||
layout: blog
|
||||
title: Podcast episode about Agile development
|
||||
segments:
|
||||
- blog
|
||||
published: true
|
||||
date: 2021-02-16T09:09:57.102Z
|
||||
tags:
|
||||
|
@ -1,6 +1,8 @@
|
||||
---
|
||||
layout: blog
|
||||
title: Introduction to JavaScript Application testing
|
||||
segments:
|
||||
- blog
|
||||
published: true
|
||||
date: 2021-05-07T14:44:57.102Z # update date accordingly
|
||||
tags:
|
||||
|
@ -1,6 +1,8 @@
|
||||
---
|
||||
layout: blog
|
||||
title: How we handle the rapid growth of the project team
|
||||
segments:
|
||||
- blog
|
||||
published: true
|
||||
date: 2021-12-16T11:02:10.123Z
|
||||
tags:
|
||||
|
@ -1,6 +1,8 @@
|
||||
---
|
||||
layout: blog
|
||||
title: First Weekly
|
||||
segments:
|
||||
- blog
|
||||
published: true
|
||||
date: 2022-02-06T17:13:02.611Z
|
||||
tags:
|
||||
|
@ -1,6 +1,8 @@
|
||||
---
|
||||
layout: blog
|
||||
title: "I've started streaming - Weekly #06-2022 "
|
||||
segments:
|
||||
- blog
|
||||
published: true
|
||||
date: 2022-02-13T19:39:33.578Z
|
||||
tags:
|
||||
|
@ -1,6 +1,8 @@
|
||||
---
|
||||
layout: blog
|
||||
title: "Laptop battery got fat - Weekly #07-2022"
|
||||
segments:
|
||||
- blog
|
||||
published: true
|
||||
date: 2022-02-20T17:50:56.214Z
|
||||
tags:
|
||||
|
@ -1,6 +1,8 @@
|
||||
---
|
||||
layout: blog
|
||||
title: Error handling with Either<Type>
|
||||
segments:
|
||||
- blog
|
||||
published: true
|
||||
date: 2022-02-28T11:30:54.195Z
|
||||
tags:
|
||||
|
@ -1,6 +1,8 @@
|
||||
---
|
||||
layout: blog
|
||||
title: "Second attempt @ Weekly #08-2022"
|
||||
segments:
|
||||
- blog
|
||||
published: true
|
||||
date: 2022-02-28T11:49:53.914Z
|
||||
tags:
|
||||
|
18
_posts/blog/2022-03-15-devbreak-1-state-of-js-2021.md
Normal file
18
_posts/blog/2022-03-15-devbreak-1-state-of-js-2021.md
Normal file
@ -0,0 +1,18 @@
|
||||
---
|
||||
layout: blog
|
||||
title: "DevBreak #1 State of JS 2021 review with Lukáš Orgován"
|
||||
segments:
|
||||
- broadcasts
|
||||
published: true
|
||||
date: 2022-03-15T20:22:21.191Z
|
||||
tags:
|
||||
- DevBreak
|
||||
---
|
||||
The first episode of the [DevBreak talk show](/broadcasts/tags/DevBreak).
|
||||
I've invited my close friend and a tech lead [Lukáš Orgován](https://www.linkedin.com/in/lukasorgovan/) to talk about the opinions on the results of the [State of JS 2021 survey](https://2021.stateofjs.com/en-us/).
|
||||
|
||||
This episode aired on the 15th of March 2022 and is in the Slovak language.
|
||||
|
||||
<div class="video-embed">
|
||||
<iframe src="https://player.twitch.tv/?video=1427225407&parent=localhost&parent=michalvanko.dev" frameborder="0" allowfullscreen="true" scrolling="no" height="100%" width="100%" class="embed"></iframe>
|
||||
</div>
|
@ -1,6 +1,8 @@
|
||||
---
|
||||
layout: blog
|
||||
title: "Coming from DevBreak - Weekly #11-2022"
|
||||
segments:
|
||||
- blog
|
||||
published: true
|
||||
date: 2022-03-19T21:22:35.128Z
|
||||
tags:
|
||||
|
@ -1,6 +1,8 @@
|
||||
---
|
||||
layout: blog
|
||||
title: "Happy Easter - Weekly #15-2022"
|
||||
segments:
|
||||
- blog
|
||||
published: true
|
||||
date: 2022-04-17T08:05:05.489Z
|
||||
tags:
|
||||
|
@ -0,0 +1,17 @@
|
||||
---
|
||||
layout: blog
|
||||
title: "DevBreak #2 Stories and impressions from Hack Kosice 2022"
|
||||
segments:
|
||||
- broadcasts
|
||||
published: true
|
||||
date: 2022-04-26T20:22:21.191Z
|
||||
tags:
|
||||
- DevBreak
|
||||
---
|
||||
|
||||
At the last possible moment, I have been able to invite **Daniela, Samuel, and Filip** over to talk about stories and their impressions of the _Hack Kosice 2022_ event that took place last weekend.
|
||||
This episode aired on the 26th of April 2022 and is in the Slovak language.
|
||||
|
||||
<div class="video-embed">
|
||||
<iframe src="https://player.twitch.tv/?video=1468441353&parent=localhost&parent=michalvanko.dev" frameborder="0" allowfullscreen="true" scrolling="no" height="100%" width="100%" class="embed"></iframe>
|
||||
</div>
|
@ -1,6 +1,8 @@
|
||||
---
|
||||
layout: blog
|
||||
title: 'Treasure hunt - Weekly #18-2022'
|
||||
segments:
|
||||
- blog
|
||||
published: true
|
||||
date: 2022-05-07T08:35:51.818Z
|
||||
tags:
|
||||
|
17
_posts/blog/2022-06-09-devbreak-3-full-stack-development.md
Normal file
17
_posts/blog/2022-06-09-devbreak-3-full-stack-development.md
Normal file
@ -0,0 +1,17 @@
|
||||
---
|
||||
layout: blog
|
||||
title: "DevBreak #3 Full-stack development with Dominik Štefan"
|
||||
segments:
|
||||
- broadcasts
|
||||
published: true
|
||||
date: 2022-06-09T20:22:21.191Z
|
||||
tags:
|
||||
- DevBreak
|
||||
---
|
||||
|
||||
My friend and former colleague [Dominik Štefan](https://www.linkedin.com/in/dominik-%C5%A1tefan-167266180/) came by to talk about his experience working on full-stack web application development. We talked about many different topics but most importantly we had a lot of fun.
|
||||
This episode aired on the 9th of June 2022 and is in the Slovak language.
|
||||
|
||||
<div class="video-embed">
|
||||
<iframe src="https://player.twitch.tv/?video=1503339615&parent=localhost&parent=michalvanko.dev" frameborder="0" allowfullscreen="true" scrolling="no" height="100%" width="100%" class="embed"></iframe>
|
||||
</div>
|
@ -1,6 +1,8 @@
|
||||
---
|
||||
layout: blog
|
||||
title: "Our attempt at Rusty game jam - Weekly #25-2022"
|
||||
segments:
|
||||
- blog
|
||||
published: true
|
||||
date: 2022-06-26T20:02:47.419Z
|
||||
tags:
|
||||
|
@ -1,6 +1,8 @@
|
||||
---
|
||||
layout: blog
|
||||
title: Lovely London Trip
|
||||
segments:
|
||||
- blog
|
||||
published: true
|
||||
date: 2022-11-27T19:49:09.204Z
|
||||
tags:
|
||||
|
17
_posts/blog/2023-01-21-devbreak-4-state-of-js-2022.md
Normal file
17
_posts/blog/2023-01-21-devbreak-4-state-of-js-2022.md
Normal file
@ -0,0 +1,17 @@
|
||||
---
|
||||
layout: blog
|
||||
title: "DevBreak #4 State of JS 2022 review with Filip Seman"
|
||||
segments:
|
||||
- broadcasts
|
||||
published: true
|
||||
date: 2023-01-21T20:22:21.191Z
|
||||
tags:
|
||||
- DevBreak
|
||||
---
|
||||
After another year, the results of the [State of JS](https://2022.stateofjs.com/en-us/) survey have been published.
|
||||
I've invited my #1 fan and friend [Filip Seman](https://www.linkedin.com/in/xseman/).
|
||||
This episode aired on the 21st of January 2023 and is in the Slovak language.
|
||||
|
||||
<div class="video-embed">
|
||||
<iframe src="https://player.twitch.tv/?video=1715138585&parent=localhost&parent=michalvanko.dev" frameborder="0" allowfullscreen="true" scrolling="no" height="100%" width="100%" class="embed"></iframe>
|
||||
</div>
|
@ -0,0 +1,16 @@
|
||||
---
|
||||
layout: blog
|
||||
title: "DevBreak #5 Joys and concerns of Engineering manager with Pavol Dudrík"
|
||||
segments:
|
||||
- broadcasts
|
||||
published: true
|
||||
date: 2023-02-04T20:22:21.191Z
|
||||
tags:
|
||||
- DevBreak
|
||||
---
|
||||
In this episode, I've invited my friend [Pavol Dudrík](https://www.linkedin.com/in/pavol-dudr%C3%ADk-043100168/) to talk about what motivates us and makes us happy about the work of the Engineering manager. This job combines two views, and the transition from engineer to manager is often full of pitfalls. We want to share our experience and dispel misconceptions often associated with managerial positions in IT.
|
||||
This episode aired on the 4th of February 2023 and is in the Slovak language.
|
||||
|
||||
<div class="video-embed">
|
||||
<iframe src="https://player.twitch.tv/?video=1728904048&parent=localhost&parent=michalvanko.dev" frameborder="0" allowfullscreen="true" scrolling="no" height="100%" width="100%" class="embed"></iframe>
|
||||
</div>
|
@ -1,6 +1,10 @@
|
||||
---
|
||||
layout: blog
|
||||
title: dev - Ide to ? copy
|
||||
segments:
|
||||
- blog
|
||||
- broadcasts
|
||||
- cookbook
|
||||
published: true
|
||||
date: 2020-01-09T17:24:13.481Z
|
||||
thumbnail: /images/uploads/screenshot.gif
|
||||
|
@ -1,6 +1,10 @@
|
||||
---
|
||||
layout: blog
|
||||
title: dev - Ide to ?
|
||||
segments:
|
||||
- blog
|
||||
- broadcasts
|
||||
- cookbook
|
||||
published: true
|
||||
date: 2019-08-09T17:24:13.481Z
|
||||
thumbnail: /images/uploads/screenshot.gif
|
||||
|
@ -1,6 +1,10 @@
|
||||
---
|
||||
layout: blog
|
||||
title: dev - Anothert one
|
||||
segments:
|
||||
- blog
|
||||
- broadcasts
|
||||
- cookbook
|
||||
published: true
|
||||
date: 2019-11-03T11:01:32.621Z
|
||||
thumbnail: /images/uploads/responzio.png
|
||||
|
@ -1,6 +1,10 @@
|
||||
---
|
||||
layout: blog
|
||||
title: New Title
|
||||
segments:
|
||||
- blog
|
||||
- broadcasts
|
||||
- cookbook
|
||||
published: true
|
||||
date: 2020-05-11T05:38:18.797Z # TODO executable line for generating date
|
||||
tags:
|
||||
|
@ -1,29 +0,0 @@
|
||||
|
||||
<section id="personal">
|
||||
<h3>Personal Information</h3>
|
||||
<p>I was born on 26th of May in Košice, Slovakia and I still live here.</p>
|
||||
<h4>Hobbies:</h4>
|
||||
<p>
|
||||
I enjoy playing basketball with my friends. I also like to play other team sports like football and hockey.
|
||||
I also play squash and table tennis. Once I've won a competition in squash at my university.
|
||||
During summer I love water skiing and swimming in a nearby lake.
|
||||
<br />
|
||||
I am very passionate about music. I've also tried some software for composing music but I am not really hooked into that yet.
|
||||
From time to time I enjoy playing board games with my friends.
|
||||
</p>
|
||||
<h4>Interests:</h4>
|
||||
<p>
|
||||
I like to explore new technologies and I'm passionate about <em>Open Source movement</em>,
|
||||
<em>Internet of Things</em> applications and <em>Linux desktop evolution</em>.
|
||||
<br />
|
||||
I am interested in modern software architecture and <em>reactive programming</em>.
|
||||
<br />
|
||||
I've attended various <strong>tech conferences and hackathons</strong>. I like them for all of the fascinating ideas that might be invented.
|
||||
<br />
|
||||
I've given presentations on various topics related to <em>web development</em>. You can <a href='#presentations'>take a look at some of them here</a>.
|
||||
<br />
|
||||
I enjoy <strong>teaching and explaining</strong> how various technologies and techniques work to my colleagues for their better understanding.
|
||||
<br />
|
||||
I take advantage of <strong>test driven development</strong>.
|
||||
</p>
|
||||
</section><!--/personal-->
|
38
src/lib/articleContent/articleContent.ts
Normal file
38
src/lib/articleContent/articleContent.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { error } from '@sveltejs/kit'
|
||||
import fm from 'front-matter'
|
||||
import { readFile } from 'fs'
|
||||
import { parseField } from '$lib/markdown/parse-markdown'
|
||||
import { promisify } from 'util'
|
||||
|
||||
export interface ArticleAttributes {
|
||||
slug: string
|
||||
layout: string
|
||||
segments: string[]
|
||||
title: string
|
||||
published: boolean
|
||||
date: string
|
||||
thumbnail: string
|
||||
tags: string[]
|
||||
body: string
|
||||
}
|
||||
|
||||
export async function getArticleContent(slug: string) {
|
||||
let postSource: string
|
||||
try {
|
||||
postSource = await promisify(readFile)(`_posts/blog/${slug}.md`, 'utf-8')
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (e: any) {
|
||||
if (e.code === 'ENOENT') {
|
||||
throw error(404, 'Post not found \n' + e.toString())
|
||||
}
|
||||
throw e
|
||||
}
|
||||
|
||||
const parsedPost = fm<ArticleAttributes>(postSource)
|
||||
|
||||
const post = parseField<ArticleAttributes>('body')({
|
||||
...parsedPost.attributes,
|
||||
body: parsedPost.body,
|
||||
})
|
||||
return post
|
||||
}
|
@ -8,24 +8,13 @@ import {
|
||||
filterAndCount,
|
||||
type PaginationQuery,
|
||||
} from '$lib/pagination/pagination'
|
||||
import type { ArticleAttributes } from './articleContent'
|
||||
|
||||
export interface ArticlePreviewAttributes extends ArticleAttributes {
|
||||
preview: string
|
||||
}
|
||||
|
||||
const { NODE_ENV } = process.env
|
||||
|
||||
export interface PostAttributes {
|
||||
layout: string
|
||||
title: string
|
||||
published: boolean
|
||||
date: string
|
||||
thumbnail: string
|
||||
tags: string[]
|
||||
}
|
||||
|
||||
export interface PostContent extends PostAttributes {
|
||||
preview: string
|
||||
slug: string
|
||||
published: boolean
|
||||
}
|
||||
|
||||
export async function getBlogListing(paginationQuery: PaginationQuery) {
|
||||
const files = await promisify(readdir)(`_posts/blog/`, 'utf-8')
|
||||
const filteredFiles = filterDevelopmentFiles(files)
|
||||
@ -36,7 +25,7 @@ export async function getBlogListing(paginationQuery: PaginationQuery) {
|
||||
`_posts/blog/${file}`,
|
||||
'utf-8'
|
||||
)
|
||||
const parsedAttributes = fm<PostAttributes>(fileContent)
|
||||
const parsedAttributes = fm<ArticleAttributes>(fileContent)
|
||||
|
||||
const lineOfTextRegExp = /^(?:\w|\[).+/gm
|
||||
const lines = parsedAttributes.body
|
||||
@ -53,7 +42,7 @@ export async function getBlogListing(paginationQuery: PaginationQuery) {
|
||||
})
|
||||
)
|
||||
const filteredContents = pipe(
|
||||
sortBy<PostContent>(prop('date')),
|
||||
sortBy<ArticlePreviewAttributes>(prop('date')),
|
||||
(items) => reverse(items),
|
||||
filter<(typeof contents)[0]>((article) => article.published),
|
||||
filterAndCount(paginationQuery)
|
11
src/lib/articleContent/onMountScripts.ts
Normal file
11
src/lib/articleContent/onMountScripts.ts
Normal file
@ -0,0 +1,11 @@
|
||||
declare global {
|
||||
interface Window {
|
||||
onMountScripts?: Array<() => void>
|
||||
}
|
||||
}
|
||||
export function runOnMountScripts() {
|
||||
window.onMountScripts?.forEach((fn) => {
|
||||
fn()
|
||||
})
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { format } from 'date-fns'
|
||||
import type { PostContent } from 'src/routes/blog/content'
|
||||
import type { ArticlePreviewAttributes } from '$lib/articleContent/articleContentListing'
|
||||
import SvgIcon from './SvgIcon.svelte'
|
||||
import {
|
||||
boldClass,
|
||||
@ -22,7 +22,7 @@
|
||||
licenceText,
|
||||
} from './Footer.css'
|
||||
|
||||
export let latestPosts: PostContent[]
|
||||
export let latestPosts: ArticlePreviewAttributes[]
|
||||
</script>
|
||||
|
||||
<footer class="site-footer navigation-theme {siteFooterClass}">
|
||||
@ -61,7 +61,7 @@
|
||||
<ul class={listUlClass}>
|
||||
{#each latestPosts as post}
|
||||
<li class={listLiClass}>
|
||||
<a rel="prefetch" href="/blog/{post.slug}">
|
||||
<a rel="prefetch" href="/{post.segments[0]}/{post.slug}">
|
||||
<span>{post.title}</span>
|
||||
<time class="date {dateClass}" datetime={post.date}>
|
||||
- {format(new Date(post.date), 'do MMM, yyyy')}
|
@ -12,40 +12,48 @@
|
||||
portfolioPageNavigationLinksClass,
|
||||
selectedClass,
|
||||
} from './Nav.css'
|
||||
import { page } from "$app/stores"
|
||||
import { page } from '$app/stores'
|
||||
|
||||
$: segment = $page.url.pathname
|
||||
|
||||
let links = [
|
||||
{
|
||||
label: 'Introduction',
|
||||
url: '/',
|
||||
},
|
||||
{
|
||||
label: 'Blog',
|
||||
url: '/blog',
|
||||
},
|
||||
{
|
||||
label: 'Broadcasts',
|
||||
url: '/broadcasts',
|
||||
},
|
||||
// {
|
||||
// label: "Dev's Cookery",
|
||||
// url: '/cookery',
|
||||
// },
|
||||
{
|
||||
label: 'Portfolio',
|
||||
url: '/portfolio',
|
||||
},
|
||||
]
|
||||
</script>
|
||||
|
||||
<nav class={navigationClass}>
|
||||
<section class={navigationContentClass}>
|
||||
<ul class={navigationLinksClass}>
|
||||
<li>
|
||||
<a class={classNames({ [selectedClass]: segment === '/' })} href="/">
|
||||
Introduction
|
||||
</a>
|
||||
</li>
|
||||
<!-- for the blog link, we're using rel=prefetch so that Sapper prefetches
|
||||
the blog data when we hover over the link or tap it on a touchscreen -->
|
||||
<li>
|
||||
<a
|
||||
rel="prefetch"
|
||||
class={classNames({ [selectedClass]: segment.startsWith('/blog') })}
|
||||
href="/blog"
|
||||
>
|
||||
Blog
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
class={classNames({
|
||||
[selectedClass]: segment.startsWith('/portfolio'),
|
||||
})}
|
||||
href="/portfolio"
|
||||
>
|
||||
Portfolio
|
||||
</a>
|
||||
</li>
|
||||
{#each links as link}
|
||||
<li>
|
||||
<a
|
||||
rel="prefetch"
|
||||
class={classNames({ [selectedClass]: segment === link.url })}
|
||||
href={link.url}
|
||||
>
|
||||
{link.label}
|
||||
</a>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
|
||||
<aside class="logo-section {logoSectionClass}">
|
@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import svgSprite from '../svg/build/icons-sprite.svg'
|
||||
import svgSprite from '../../svg/build/icons-sprite.svg'
|
||||
export let className: string
|
||||
export let name: string
|
||||
</script>
|
@ -0,0 +1,18 @@
|
||||
<script lang="ts">
|
||||
interface ArticleDetails {
|
||||
title: string
|
||||
slug: string
|
||||
preview: string
|
||||
}
|
||||
export let segment: string
|
||||
export let article: ArticleDetails
|
||||
</script>
|
||||
|
||||
<article>
|
||||
<header>
|
||||
<h2>
|
||||
<a rel="prefetch" href={`/${segment}/${article.slug}`}>{article.title}</a>
|
||||
</h2>
|
||||
</header>
|
||||
{@html article.preview}
|
||||
</article>
|
@ -2,26 +2,27 @@
|
||||
import { horizontalBorderTopClass } from '$lib/styles/scoops.css'
|
||||
|
||||
import { format } from 'date-fns'
|
||||
import type { PostContent } from '../../routes/blog/content'
|
||||
import type { ArticleContent } from '$lib/content/articleContentListing'
|
||||
import {
|
||||
footerClass,
|
||||
publishedClass,
|
||||
publishedLabelClass,
|
||||
tagsListClass,
|
||||
tagsListLiClass,
|
||||
} from './ArticleFooter.css'
|
||||
} from './ArticlePreviewFooter.css'
|
||||
|
||||
export let post: PostContent
|
||||
export let segment: string
|
||||
export let article: ArticleContent
|
||||
</script>
|
||||
|
||||
<footer class="{footerClass} {horizontalBorderTopClass}">
|
||||
<div class="article-tags">
|
||||
{#if post.tags.length > 0}
|
||||
{#if article.tags.length > 0}
|
||||
<span class="lighten">Tags:</span>
|
||||
<ul class={tagsListClass}>
|
||||
{#each post.tags as tag}
|
||||
{#each article.tags as tag}
|
||||
<li class={tagsListLiClass}>
|
||||
<a href="/blog/tags/{tag}">{tag}</a>
|
||||
<a href="/{segment}/tags/{tag}">{tag}</a>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
@ -29,8 +30,8 @@
|
||||
</div>
|
||||
<div class="created-at">
|
||||
<span class={publishedLabelClass}>Published on</span>
|
||||
<time datetime={post.date} class={publishedClass}>
|
||||
{format(new Date(post.date), "do MMMM',' y")}
|
||||
<time datetime={article.date} class={publishedClass}>
|
||||
{format(new Date(article.date), "do MMMM',' y")}
|
||||
</time>
|
||||
</div>
|
||||
</footer>
|
@ -0,0 +1,41 @@
|
||||
<script lang="ts">
|
||||
import ArticleFooter from '$lib/components/articles/ArticlePreviewFooter/ArticlePreviewFooter.svelte'
|
||||
import Paginator from '$lib/components/paginator/Paginator.svelte'
|
||||
import { postListClass } from './ArticlePreviewList.css'
|
||||
import ArticlePreviewCard from '$lib/components/articles/ArticlePreviewCard/ArticlePreviewCard.svelte'
|
||||
import type { PaginationResult } from '$lib/pagination/pagination'
|
||||
import type { ArticleContent } from '$lib/content/articleContentListing'
|
||||
|
||||
export let page: number
|
||||
export let pageSize: number
|
||||
export let filters: Record<string, string>
|
||||
export let posts: PaginationResult<ArticleContent>
|
||||
export let segment: string
|
||||
</script>
|
||||
|
||||
<header>
|
||||
<Paginator
|
||||
{segment}
|
||||
{page}
|
||||
{pageSize}
|
||||
{filters}
|
||||
totalCount={posts.totalCount}
|
||||
/>
|
||||
</header>
|
||||
<ul class="post-list {postListClass}">
|
||||
{#each posts.items as article (article.slug)}
|
||||
<li>
|
||||
<ArticlePreviewCard {article} {segment} />
|
||||
<ArticleFooter {article} {segment} />
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
<footer>
|
||||
<Paginator
|
||||
{segment}
|
||||
{page}
|
||||
{pageSize}
|
||||
{filters}
|
||||
totalCount={posts.totalCount}
|
||||
/>
|
||||
</footer>
|
@ -10,12 +10,11 @@
|
||||
|
||||
export const Divider = 'divider'
|
||||
|
||||
export let href: string
|
||||
export let segment: string
|
||||
export let page: number
|
||||
export let pageSize: number
|
||||
export let totalCount: number
|
||||
export let filters: Record<string, string>
|
||||
let paginatorPages: (number | typeof Divider)[]
|
||||
|
||||
$: paginatorPages = getPaginatorPages({ page, pageSize, totalCount })
|
||||
</script>
|
||||
@ -23,7 +22,7 @@
|
||||
<ul class={listClass}>
|
||||
{#if page !== 1}
|
||||
<li class="{listItemClass} ">
|
||||
<a class={pageLinkClass} href={createHref(href, filters, page - 1)}
|
||||
<a class={pageLinkClass} href={createHref(segment, filters, page - 1)}
|
||||
><</a
|
||||
>
|
||||
</li>
|
||||
@ -35,7 +34,7 @@
|
||||
<li class="{listItemClass} {activePage}">{pageNumber}</li>
|
||||
{:else}
|
||||
<li class="{listItemClass} ">
|
||||
<a class={pageLinkClass} href={createHref(href, filters, pageNumber)}
|
||||
<a class={pageLinkClass} href={createHref(segment, filters, pageNumber)}
|
||||
>{pageNumber}</a
|
||||
>
|
||||
</li>
|
||||
@ -43,7 +42,7 @@
|
||||
{/each}
|
||||
{#if page !== paginatorPages.length}
|
||||
<li class="{listItemClass} ">
|
||||
<a class={pageLinkClass} href={createHref(href, filters, page + 1)}
|
||||
<a class={pageLinkClass} href={createHref(segment, filters, page + 1)}
|
||||
>></a
|
||||
>
|
||||
</li>
|
@ -11,11 +11,13 @@ export function getPaginatorPages({
|
||||
page: number
|
||||
pageSize: number
|
||||
totalCount: number
|
||||
}) {
|
||||
}): (number | typeof Divider)[] {
|
||||
const maxLinksLength = 7
|
||||
const linksAroundActive = 2
|
||||
const totalPages = Math.ceil(totalCount / pageSize)
|
||||
const daco = range(1, totalPages + 1).reduce((acc, link) => {
|
||||
const shownPages = range(1, totalPages + 1).reduce<
|
||||
(number | typeof Divider)[]
|
||||
>((acc, link) => {
|
||||
const isFirst = link === 1
|
||||
const isLast = link === totalPages
|
||||
const isPageOnStart = page <= 3 && link < maxLinksLength
|
||||
@ -36,7 +38,7 @@ export function getPaginatorPages({
|
||||
return [...acc, link]
|
||||
}, [])
|
||||
|
||||
return daco
|
||||
return shownPages
|
||||
}
|
||||
|
||||
export function createHref(
|
@ -1,9 +1,9 @@
|
||||
import { splitEvery } from 'ramda'
|
||||
import { init, splitEvery } from 'ramda'
|
||||
|
||||
export function parseParams(params: string) {
|
||||
const splittedParams = params.split('/')
|
||||
let splittedParams = params.split('/')
|
||||
if (splittedParams.length % 2 !== 0) {
|
||||
return []
|
||||
splittedParams = init(splittedParams)
|
||||
}
|
||||
const splits = splitEvery(2, splittedParams)
|
||||
return Object.fromEntries(splits)
|
||||
|
@ -17,7 +17,10 @@ export function dropAndTake<Item>({ offset = 0, limit = Infinity }) {
|
||||
) => Item[]
|
||||
}
|
||||
|
||||
export function filterByPropContains<Item>(filters: Record<string, string>) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function filterByPropContains<Item extends Record<string, any>>(
|
||||
filters: Record<string, string>
|
||||
) {
|
||||
return function (items: Item[]) {
|
||||
return items.filter((item) => {
|
||||
return Object.entries(filters).every(([fieldName, value]) =>
|
||||
@ -27,7 +30,8 @@ export function filterByPropContains<Item>(filters: Record<string, string>) {
|
||||
}
|
||||
}
|
||||
|
||||
export function filterAndCount<Item>({
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function filterAndCount<Item extends Record<string, any>>({
|
||||
filters,
|
||||
...dropTakeParams
|
||||
}: PaginationQuery) {
|
||||
|
@ -19,3 +19,9 @@ globalStyle(`${contentClass} img:only-child`, {
|
||||
display: 'block',
|
||||
margin: '0 auto',
|
||||
})
|
||||
|
||||
globalStyle(`${contentClass} .video-embed`, {
|
||||
margin: '0 auto',
|
||||
maxWidth: vars.width.image,
|
||||
aspectRatio: vars.aspectRatio.monitor,
|
||||
})
|
@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import type { LayoutData } from "./$types"
|
||||
import Nav from '../components/Nav.svelte'
|
||||
import Footer from '../components/Footer.svelte'
|
||||
import type { LayoutData } from './$types'
|
||||
import Nav from '$lib/components/Nav.svelte'
|
||||
import Footer from '$lib/components/Footer.svelte'
|
||||
import 'modern-normalize/modern-normalize.css'
|
||||
import '$lib/styles/global.css'
|
||||
import { mainContentClass } from './layout.css'
|
||||
|
@ -2,7 +2,7 @@ import type { LayoutLoad } from './$types'
|
||||
export const prerender = true
|
||||
|
||||
export const load = (async ({ fetch }) => {
|
||||
const blogPostsResponse = await fetch(`/blog/articles/pageSize/5.json`)
|
||||
const blogPostsResponse = await fetch(`/articles/pageSize/5.json`)
|
||||
const blogPostsContent = await blogPostsResponse.json()
|
||||
|
||||
return {
|
||||
|
@ -3,7 +3,7 @@ import {
|
||||
parseParams,
|
||||
} from '$lib/pagination/dropTakeParams'
|
||||
import { json } from '@sveltejs/kit'
|
||||
import { getBlogListing } from '../../content'
|
||||
import { getBlogListing } from '$lib/articleContent/articleContentListing'
|
||||
import type { RequestHandler } from './$types'
|
||||
|
||||
export const prerender = true
|
@ -1,12 +1,10 @@
|
||||
<script lang="ts">
|
||||
import ArticleFooter from '../../../components/blog/ArticleFooter.svelte'
|
||||
import Paginator from '../../../components/paginator/Paginator.svelte'
|
||||
import { postListClass, seeAllClass } from './page.css'
|
||||
import type { PageData } from './$types'
|
||||
import ArticlePreviewList from '$lib/components/articles/ArticlePreviewList/ArticlePreviewList.svelte'
|
||||
import { seeAllClass } from '$lib/components/articles/ArticlePreviewList/ArticlePreviewList.css'
|
||||
|
||||
export let data: PageData
|
||||
let { posts, filters, page, pageSize } = data
|
||||
$: ({ posts, filters, page, pageSize } = data)
|
||||
$: ({ posts, filters } = data)
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
@ -30,36 +28,5 @@
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
<header>
|
||||
<Paginator
|
||||
href="blog"
|
||||
{page}
|
||||
{pageSize}
|
||||
{filters}
|
||||
totalCount={posts.totalCount}
|
||||
/>
|
||||
</header>
|
||||
<ul class="post-list {postListClass}">
|
||||
{#each posts.items as post (post.slug)}
|
||||
<li>
|
||||
<article>
|
||||
<header>
|
||||
<h2>
|
||||
<a rel="prefetch" href="/blog/{post.slug}">{post.title}</a>
|
||||
</h2>
|
||||
</header>
|
||||
{@html post.preview}
|
||||
</article>
|
||||
<ArticleFooter {post} />
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
<footer>
|
||||
<Paginator
|
||||
href="blog"
|
||||
{page}
|
||||
{pageSize}
|
||||
{filters}
|
||||
totalCount={posts.totalCount}
|
||||
/>
|
||||
</footer>
|
||||
|
||||
<ArticlePreviewList {...data} segment="blog" />
|
||||
|
@ -1,16 +1,15 @@
|
||||
import { parseParams } from '$lib/pagination/dropTakeParams'
|
||||
import type { PageLoad } from './$types'
|
||||
import type { PostContent } from './../content'
|
||||
import type { ArticlePreviewAttributes } from '$lib/articleContent/articleContentListing'
|
||||
import type { PaginationResult } from '$lib/pagination/pagination'
|
||||
|
||||
export const load = (async ({ fetch, params }) => {
|
||||
const { page = 1, pageSize = 7, ...filters } = parseParams(params.params)
|
||||
const articleResponse = await fetch(
|
||||
`/blog/articles/${params.params ? params.params : 'index'}.json`
|
||||
`/articles/segments/blog${params.params ? `/${params.params}` : ''}.json`
|
||||
).then((r) => r.json())
|
||||
|
||||
return {
|
||||
posts: articleResponse.posts as PaginationResult<PostContent>,
|
||||
posts: articleResponse.posts as PaginationResult<ArticlePreviewAttributes >,
|
||||
page: Number(page),
|
||||
pageSize,
|
||||
filters,
|
||||
|
@ -1,35 +1,9 @@
|
||||
import { readFile } from 'fs'
|
||||
import { promisify } from 'util'
|
||||
import fm from 'front-matter'
|
||||
import { parseField } from '../../../markdown/parse-markdown'
|
||||
import { error, json } from '@sveltejs/kit'
|
||||
import type { PostAttributes } from '../content'
|
||||
import type { PageServerLoad } from './$types'
|
||||
import { getArticleContent } from '$lib/articleContent/articleContent'
|
||||
|
||||
export const prerender = true
|
||||
|
||||
export interface SinglePost {
|
||||
body: string
|
||||
}
|
||||
|
||||
export const load = (async ({ params: { slug } }) => {
|
||||
let postSource: string
|
||||
try {
|
||||
postSource = await promisify(readFile)(`_posts/blog/${slug}.md`, 'utf-8')
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (e: any) {
|
||||
if (e.code === 'ENOENT') {
|
||||
throw error(404, 'Post not found \n' + e.toString())
|
||||
}
|
||||
throw e
|
||||
}
|
||||
|
||||
const parsedPost = fm<PostAttributes>(postSource)
|
||||
|
||||
const post = parseField<SinglePost>('body')({
|
||||
...parsedPost.attributes,
|
||||
body: parsedPost.body,
|
||||
})
|
||||
|
||||
const post = await getArticleContent(slug);
|
||||
return post
|
||||
}) satisfies PageServerLoad
|
||||
|
@ -1,9 +1,15 @@
|
||||
<script lang="ts">
|
||||
import ArticleFooter from '../../../components/blog/ArticleFooter.svelte'
|
||||
import ArticleFooter from '$lib/components/articles/ArticlePreviewFooter/ArticlePreviewFooter.svelte'
|
||||
import type { PageData } from './$types'
|
||||
import { contentClass } from './page.css'
|
||||
import { contentClass } from '$lib/styles/article/article.css'
|
||||
import { onMount } from 'svelte'
|
||||
import { runOnMountScripts } from '$lib/articleContent/onMountScripts'
|
||||
|
||||
export let data: PageData
|
||||
|
||||
onMount(() => {
|
||||
runOnMountScripts()
|
||||
})
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
@ -12,7 +18,7 @@
|
||||
|
||||
<h1>{data.title}</h1>
|
||||
|
||||
<div class="content {contentClass}">
|
||||
<article class="content {contentClass}">
|
||||
{@html data.body}
|
||||
</div>
|
||||
<ArticleFooter post={data} />
|
||||
</article>
|
||||
<ArticleFooter article={data} segment="blog" />
|
||||
|
30
src/routes/broadcasts/[...params]/+page.svelte
Normal file
30
src/routes/broadcasts/[...params]/+page.svelte
Normal file
@ -0,0 +1,30 @@
|
||||
<script lang="ts">
|
||||
import type { PageData } from './$types'
|
||||
import ArticlePreviewList from '$lib/components/articles/ArticlePreviewList/ArticlePreviewList.svelte'
|
||||
import { seeAllClass } from '$lib/components/articles/ArticlePreviewList/ArticlePreviewList.css'
|
||||
|
||||
export let data: PageData
|
||||
$: ({ posts, filters } = data)
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Broadcasts @michalvankodev</title>
|
||||
</svelte:head>
|
||||
|
||||
{#if posts.items.length === 0}
|
||||
<p class="no-posts">You've found void in the space.</p>
|
||||
{:else}
|
||||
<h1>
|
||||
{#if filters.tags}
|
||||
<em>{filters.tags}</em>
|
||||
{/if}
|
||||
Broadcasts
|
||||
</h1>
|
||||
{#if filters.tags}
|
||||
<div class={seeAllClass}>
|
||||
<a href="/broadcasts">See all broadcasts</a>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
<ArticlePreviewList {...data} segment="broadcasts" />
|
18
src/routes/broadcasts/[...params]/+page.ts
Normal file
18
src/routes/broadcasts/[...params]/+page.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { parseParams } from '$lib/pagination/dropTakeParams'
|
||||
import type { PageLoad } from './$types'
|
||||
import type { ArticlePreviewAttributes } from '$lib/articleContent/articleContentListing'
|
||||
import type { PaginationResult } from '$lib/pagination/pagination'
|
||||
|
||||
export const load = (async ({ fetch, params }) => {
|
||||
const { page = 1, pageSize = 7, ...filters } = parseParams(params.params)
|
||||
const articleResponse = await fetch(
|
||||
`/articles/segments/broadcasts${params.params ? `/${params.params}` : ''}.json`
|
||||
).then((r) => r.json())
|
||||
|
||||
return {
|
||||
posts: articleResponse.posts as PaginationResult<ArticlePreviewAttributes>,
|
||||
page: Number(page),
|
||||
pageSize,
|
||||
filters,
|
||||
}
|
||||
}) satisfies PageLoad
|
9
src/routes/broadcasts/[slug=blogPage]/+page.server.ts
Normal file
9
src/routes/broadcasts/[slug=blogPage]/+page.server.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import type { PageServerLoad } from './$types'
|
||||
import { getArticleContent } from '$lib/articleContent/articleContent'
|
||||
|
||||
export const prerender = true
|
||||
|
||||
export const load = (async ({ params: { slug } }) => {
|
||||
const post = await getArticleContent(slug);
|
||||
return post
|
||||
}) satisfies PageServerLoad
|
24
src/routes/broadcasts/[slug=blogPage]/+page.svelte
Normal file
24
src/routes/broadcasts/[slug=blogPage]/+page.svelte
Normal file
@ -0,0 +1,24 @@
|
||||
<script lang="ts">
|
||||
import ArticleFooter from '$lib/components/articles/ArticlePreviewFooter/ArticlePreviewFooter.svelte'
|
||||
import type { PageData } from './$types'
|
||||
import { contentClass } from '$lib/styles/article/article.css'
|
||||
import { onMount } from 'svelte'
|
||||
import { runOnMountScripts } from '$lib/articleContent/onMountScripts'
|
||||
|
||||
export let data: PageData
|
||||
|
||||
onMount(() => {
|
||||
runOnMountScripts()
|
||||
})
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>{data.title}</title>
|
||||
</svelte:head>
|
||||
|
||||
<h1>{data.title}</h1>
|
||||
|
||||
<article class="content {contentClass}">
|
||||
{@html data.body}
|
||||
</article>
|
||||
<ArticleFooter article={data} segment="broadcasts" />
|
@ -1,5 +1,5 @@
|
||||
import { getBlogListing } from '$lib/articleContent/articleContentListing'
|
||||
import { Feed } from 'feed'
|
||||
import { getBlogListing } from './blog/content'
|
||||
|
||||
export async function getFeed() {
|
||||
const feed = new Feed({
|
||||
|
@ -3,7 +3,7 @@ import { promisify } from 'util'
|
||||
import fm from 'front-matter'
|
||||
// TODO Switch marked for unified
|
||||
import marked from 'marked'
|
||||
import { parseField } from '../../markdown/parse-markdown'
|
||||
import { parseField } from '$lib/markdown/parse-markdown'
|
||||
import type { PageServerLoad } from './$types'
|
||||
|
||||
export const prerender = true
|
||||
|
@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import Work from '../../components/portfolio/work.svelte'
|
||||
import Project from '../../components/portfolio/project.svelte'
|
||||
import Presentation from '../../components/portfolio/presentation.svelte'
|
||||
import Work from './components/work.svelte'
|
||||
import Project from './components/project.svelte'
|
||||
import Presentation from './components/presentation.svelte'
|
||||
import type { PageData } from './$types'
|
||||
|
||||
import { listClass, listItemClass, nameTagClass } from './page.css'
|
||||
|
39
src/routes/portfolio/components/personal.svelte
Normal file
39
src/routes/portfolio/components/personal.svelte
Normal file
@ -0,0 +1,39 @@
|
||||
<section id="personal">
|
||||
<h3>Personal Information</h3>
|
||||
<p>I was born on 26th of May in Košice, Slovakia and I still live here.</p>
|
||||
<h4>Hobbies:</h4>
|
||||
<p>
|
||||
I enjoy playing basketball with my friends. I also like to play other team
|
||||
sports like football and hockey. I also play squash and table tennis. Once
|
||||
I've won a competition in squash at my university. During summer I love
|
||||
water skiing and swimming in a nearby lake.
|
||||
<br />
|
||||
I am very passionate about music. I've also tried some software for composing
|
||||
music but I am not really hooked into that yet. From time to time I enjoy playing
|
||||
board games with my friends.
|
||||
</p>
|
||||
<h4>Interests:</h4>
|
||||
<p>
|
||||
I like to explore new technologies and I'm passionate about <em
|
||||
>Open Source movement</em
|
||||
>,
|
||||
<em>Internet of Things</em> applications and
|
||||
<em>Linux desktop evolution</em>.
|
||||
<br />
|
||||
I am interested in modern software architecture and
|
||||
<em>reactive programming</em>.
|
||||
<br />
|
||||
I've attended various <strong>tech conferences and hackathons</strong>. I
|
||||
like them for all of the fascinating ideas that might be invented.
|
||||
<br />
|
||||
I've given presentations on various topics related to
|
||||
<em>web development</em>. You can
|
||||
<a href="#presentations">take a look at some of them here</a>.
|
||||
<br />
|
||||
I enjoy <strong>teaching and explaining</strong> how various technologies
|
||||
and techniques work to my colleagues for their better understanding.
|
||||
<br />
|
||||
I take advantage of <strong>test driven development</strong>.
|
||||
</p>
|
||||
</section>
|
||||
<!--/personal-->
|
@ -19,6 +19,15 @@ collections:
|
||||
fields: # The fields for each document, usually in front matter
|
||||
- { label: 'Layout', name: 'layout', widget: 'hidden', default: 'blog' }
|
||||
- { label: 'Title', name: 'title', widget: 'string' }
|
||||
- label: 'Segments'
|
||||
name: 'segments'
|
||||
widget: 'select'
|
||||
multiple: true
|
||||
options:
|
||||
- { label: 'Blog', value: 'blog' }
|
||||
- { label: 'Broadcasts', value: 'broadcasts' }
|
||||
- { label: 'Cookbook', value: 'cookbook' }
|
||||
default: ['blog']
|
||||
- {
|
||||
label: 'Published',
|
||||
name: 'published',
|
||||
|
Loading…
Reference in New Issue
Block a user