diff --git a/_pages/portfolio.md b/_pages/portfolio.md
index e6d2d44..b633151 100644
--- a/_pages/portfolio.md
+++ b/_pages/portfolio.md
@@ -90,7 +90,7 @@ work_history:
displayed: true
address:
name: eSOLUTIONS s.r.o.
- location: Hroncová 2
+ location: Gorkého 8
zipcode: 040 01
city: Košice
country: Slovakia
@@ -151,7 +151,7 @@ projects:
This project is built with modern web technologies including: **CycleJS**, **Reactive Streams**, **D3**, **Jest**, **Webpack**.
displayed: true
image:
- image_description: " responzIO main controller"
+ image_description: ' responzIO main controller'
source: /images/uploads/responzio.png
name: responzIO
- description: >-
@@ -300,14 +300,16 @@ presentations:
affect the future of the world.
- displayed: true
name: Spreading the web
- description: A presentation about the rising number of use cases for utilizing
+ description:
+ A presentation about the rising number of use cases for utilizing
web technologies outside of the web platform such as native mobile
applications and robotics.
link: https://michalvankodev.github.io/spreading-the-web/#/
- displayed: true
name: Docker
link: http://michalvankodev.github.io/dockerpresentation/#/
- description: An introduction to Docker containerization technology and how it
+ description:
+ An introduction to Docker containerization technology and how it
differs from virtualization.
education:
- description: |-
diff --git a/package-lock.json b/package-lock.json
index fdf0596..217c488 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,7 +8,6 @@
"name": "michalvankodev",
"version": "0.0.1",
"dependencies": {
- "@mobily/ts-belt": "^3.10.0",
"@vanilla-extract/css": "^1.6.8",
"@vanilla-extract/sprinkles": "^1.4.0",
"@vanilla-extract/vite-plugin": "^3.1.4",
@@ -42,7 +41,8 @@
"tslib": "^2.3.1",
"typescript": "^4.6.2",
"vite": "^2.8.6",
- "vitest": "^0.7.10"
+ "vitest": "^0.7.13",
+ "vitest-svelte-kit": "^0.0.6"
}
},
"node_modules/@babel/code-frame": {
@@ -447,14 +447,6 @@
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
"dev": true
},
- "node_modules/@mobily/ts-belt": {
- "version": "3.10.0",
- "resolved": "https://registry.npmjs.org/@mobily/ts-belt/-/ts-belt-3.10.0.tgz",
- "integrity": "sha512-F3XLU3zMDzJOf9KlKgnNOz5rdAtMG/UBxEDU4UNA4ewKFRd5DsbIIJmeAifLudNwcXmoIgtZ39KwVjPaL/CjgA==",
- "engines": {
- "node": ">= 10.*"
- }
- },
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -4025,9 +4017,9 @@
}
},
"node_modules/vitest": {
- "version": "0.7.10",
- "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.7.10.tgz",
- "integrity": "sha512-We5a7cnY2aUpX4tAO+w2KRhJiJ4FznfWjYKkqWoAqs4x4pKgyRsMJNZ7OSY/lFHOoRz3yv0mgwfVlZiRc0/mmA==",
+ "version": "0.7.13",
+ "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.7.13.tgz",
+ "integrity": "sha512-UCHeJEOK+qCBa/e4UtkCfv0wIZ125T4Nf2R0J/46v/Wnv6bt9zGfAyKAI6siYFhvLvg20MgDIreROtVgedHFWw==",
"dev": true,
"dependencies": {
"@types/chai": "^4.3.0",
@@ -4042,7 +4034,7 @@
"vitest": "vitest.mjs"
},
"engines": {
- "node": ">=v14.16.0"
+ "node": ">=v14.19.1"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
@@ -4068,6 +4060,12 @@
}
}
},
+ "node_modules/vitest-svelte-kit": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/vitest-svelte-kit/-/vitest-svelte-kit-0.0.6.tgz",
+ "integrity": "sha512-bQ1GcCAk600YV1xOiJBhltGE/HO/j6FozNY2BFq2GP1mHh3pj0KrGZlyx0kVlXx+BSKDXQHuYZtwlHwNlvv0fQ==",
+ "dev": true
+ },
"node_modules/word-wrap": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
@@ -4498,11 +4496,6 @@
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
"dev": true
},
- "@mobily/ts-belt": {
- "version": "3.10.0",
- "resolved": "https://registry.npmjs.org/@mobily/ts-belt/-/ts-belt-3.10.0.tgz",
- "integrity": "sha512-F3XLU3zMDzJOf9KlKgnNOz5rdAtMG/UBxEDU4UNA4ewKFRd5DsbIIJmeAifLudNwcXmoIgtZ39KwVjPaL/CjgA=="
- },
"@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -6943,9 +6936,9 @@
}
},
"vitest": {
- "version": "0.7.10",
- "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.7.10.tgz",
- "integrity": "sha512-We5a7cnY2aUpX4tAO+w2KRhJiJ4FznfWjYKkqWoAqs4x4pKgyRsMJNZ7OSY/lFHOoRz3yv0mgwfVlZiRc0/mmA==",
+ "version": "0.7.13",
+ "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.7.13.tgz",
+ "integrity": "sha512-UCHeJEOK+qCBa/e4UtkCfv0wIZ125T4Nf2R0J/46v/Wnv6bt9zGfAyKAI6siYFhvLvg20MgDIreROtVgedHFWw==",
"dev": true,
"requires": {
"@types/chai": "^4.3.0",
@@ -6957,6 +6950,12 @@
"vite": "^2.8.6"
}
},
+ "vitest-svelte-kit": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/vitest-svelte-kit/-/vitest-svelte-kit-0.0.6.tgz",
+ "integrity": "sha512-bQ1GcCAk600YV1xOiJBhltGE/HO/j6FozNY2BFq2GP1mHh3pj0KrGZlyx0kVlXx+BSKDXQHuYZtwlHwNlvv0fQ==",
+ "dev": true
+ },
"word-wrap": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
diff --git a/package.json b/package.json
index a858cf3..f145de6 100644
--- a/package.json
+++ b/package.json
@@ -16,7 +16,6 @@
"svgstore": "svgstore -o static/build/icons-sprite.svg src/svg/**.svg"
},
"dependencies": {
- "@mobily/ts-belt": "^3.10.0",
"@vanilla-extract/css": "^1.6.8",
"@vanilla-extract/sprinkles": "^1.4.0",
"@vanilla-extract/vite-plugin": "^3.1.4",
@@ -50,6 +49,7 @@
"tslib": "^2.3.1",
"typescript": "^4.6.2",
"vite": "^2.8.6",
- "vitest": "^0.7.10"
+ "vitest": "^0.7.13",
+ "vitest-svelte-kit": "^0.0.6"
}
}
diff --git a/src/components/blog/ArticleFooter.svelte b/src/components/blog/ArticleFooter.svelte
index d4d4950..bf21c1e 100644
--- a/src/components/blog/ArticleFooter.svelte
+++ b/src/components/blog/ArticleFooter.svelte
@@ -21,7 +21,7 @@
diff --git a/src/components/paginator/Paginator.css.ts b/src/components/paginator/Paginator.css.ts
new file mode 100644
index 0000000..398b93f
--- /dev/null
+++ b/src/components/paginator/Paginator.css.ts
@@ -0,0 +1,21 @@
+import { sprinkles } from '$lib/styles/sprinkles.css'
+
+export const listClass = sprinkles({
+ listStyle: 'none',
+ display: 'flex',
+ justifyContent: 'center',
+})
+
+export const listItemClass = sprinkles({
+ paddingX: '1x',
+})
+
+export const activePage = sprinkles({
+ //fontStyle: 'italic',
+ fontWeight: 'bold',
+ paddingX: '2x',
+})
+
+export const pageLinkClass = sprinkles({
+ paddingX: '1x',
+})
diff --git a/src/components/paginator/Paginator.svelte b/src/components/paginator/Paginator.svelte
new file mode 100644
index 0000000..485130c
--- /dev/null
+++ b/src/components/paginator/Paginator.svelte
@@ -0,0 +1,52 @@
+
+
+
+ {#if page !== 1}
+ -
+ <
+
+ {/if}
+ {#each paginatorPages as pageNumber}
+ {#if pageNumber === Divider}
+ - ...
+ {:else if page === pageNumber}
+ - {pageNumber}
+ {:else}
+ -
+ {pageNumber}
+
+ {/if}
+ {/each}
+ {#if page !== paginatorPages.length}
+ -
+ >
+
+ {/if}
+
diff --git a/src/components/paginator/paginatorUtils.test.ts b/src/components/paginator/paginatorUtils.test.ts
new file mode 100644
index 0000000..0a38cd0
--- /dev/null
+++ b/src/components/paginator/paginatorUtils.test.ts
@@ -0,0 +1,52 @@
+import { describe, expect, test } from 'vitest'
+import { Divider, getPaginatorPages } from './paginatorUtils'
+
+describe('Paginator component', () => {
+ describe('Paginator generates feasable pages to display', () => {
+ test('Page: 1/5', () => {
+ expect(
+ getPaginatorPages({ page: 1, totalCount: 5, pageSize: 1 })
+ ).toEqual([1, 2, 3, 4, 5])
+ })
+ test('Page: 4/7', () => {
+ expect(
+ getPaginatorPages({ page: 4, totalCount: 7, pageSize: 1 })
+ ).toEqual([1, 2, 3, 4, 5, 6, 7])
+ })
+ test('Page: 4/8', () => {
+ expect(
+ getPaginatorPages({ page: 4, totalCount: 8, pageSize: 1 })
+ ).toEqual([1, 2, 3, 4, 5, 6, Divider, 8])
+ })
+ test('Page: 1/10', () => {
+ expect(
+ getPaginatorPages({ page: 1, totalCount: 10, pageSize: 1 })
+ ).toEqual([1, 2, 3, 4, 5, 6, Divider, 10])
+ })
+ test('Page: 2/10', () => {
+ expect(
+ getPaginatorPages({ page: 2, totalCount: 10, pageSize: 1 })
+ ).toEqual([1, 2, 3, 4, 5, 6, Divider, 10])
+ })
+ test('Page: 5/10', () => {
+ expect(
+ getPaginatorPages({ page: 5, totalCount: 10, pageSize: 1 })
+ ).toEqual([1, Divider, 3, 4, 5, 6, 7, Divider, 10])
+ })
+ test('Page: 7/10', () => {
+ expect(
+ getPaginatorPages({ page: 7, totalCount: 10, pageSize: 1 })
+ ).toEqual([1, Divider, 5, 6, 7, 8, 9, 10])
+ })
+ test('Page: 8/10', () => {
+ expect(
+ getPaginatorPages({ page: 8, totalCount: 10, pageSize: 1 })
+ ).toEqual([1, Divider, 5, 6, 7, 8, 9, 10])
+ })
+ test('Page: 10/10', () => {
+ expect(
+ getPaginatorPages({ page: 10, totalCount: 10, pageSize: 1 })
+ ).toEqual([1, Divider, 5, 6, 7, 8, 9, 10])
+ })
+ })
+})
diff --git a/src/components/paginator/paginatorUtils.ts b/src/components/paginator/paginatorUtils.ts
new file mode 100644
index 0000000..c35b5f5
--- /dev/null
+++ b/src/components/paginator/paginatorUtils.ts
@@ -0,0 +1,50 @@
+import { toParams } from '$lib/pagination/searchParams'
+import { last, range } from 'ramda'
+
+export const Divider = 'divider'
+
+export function getPaginatorPages({
+ page,
+ pageSize,
+ totalCount,
+}: {
+ page: number
+ pageSize: number
+ totalCount: number
+}) {
+ const maxLinksLength = 7
+ const linksAroundActive = 2
+ const totalPages = Math.ceil(totalCount / pageSize)
+ const daco = range(1, totalPages + 1).reduce((acc, link) => {
+ const isFirst = link === 1
+ const isLast = link === totalPages
+ const isPageOnStart = page <= 3 && link < maxLinksLength
+ const isPageOnEnd =
+ page >= totalPages - 3 && link > totalPages - maxLinksLength + 1
+
+ if ([isFirst, isLast, isPageOnStart, isPageOnEnd].some((value) => value)) {
+ return [...acc, link]
+ }
+
+ if (link < page - linksAroundActive || link > page + linksAroundActive) {
+ if (last(acc) === Divider) {
+ return acc
+ }
+ return [...acc, Divider]
+ }
+
+ return [...acc, link]
+ }, [])
+
+ return daco
+}
+
+export function createHref(
+ href: string,
+ filters: Record,
+ pageNumber: number
+) {
+ const filtersPath = toParams(filters)
+ console.log(filtersPath, filters)
+ return `/${href}/${filtersPath ? filtersPath + '/' : ''}page/${pageNumber}`
+}
diff --git a/src/lib/pagination/pagination.ts b/src/lib/pagination/pagination.ts
index 3dabaf0..64e2a72 100644
--- a/src/lib/pagination/pagination.ts
+++ b/src/lib/pagination/pagination.ts
@@ -1,6 +1,4 @@
-import { identity } from 'ramda'
-import { flow, A } from '@mobily/ts-belt'
-const { drop, take } = A
+import { identity, drop, take, pipe } from 'ramda'
export interface PaginationQuery {
offset?: number
@@ -14,7 +12,9 @@ export interface PaginationResult {
}
export function dropAndTake- ({ offset = 0, limit = Infinity }) {
- return flow(drop
- (offset), take
- (limit))
+ return pipe(drop
- (offset), take
- (limit)) as (
+ items: Item[]
+ ) => Item[]
}
export function filterByPropContains
- (filters: Record) {
diff --git a/src/lib/pagination/searchParams.test.ts b/src/lib/pagination/searchParams.test.ts
index 8928369..721ff1c 100644
--- a/src/lib/pagination/searchParams.test.ts
+++ b/src/lib/pagination/searchParams.test.ts
@@ -42,22 +42,37 @@ describe('get search params', () => {
})
test('should parse values into searchParams for first page', () => {
- const params = 'tags/News/page/1'
- expect(getPaginationSearchParams(7, params).toString()).toEqual(
+ const params = {
+ pageSize: 7,
+ page: 1,
+ filters: {
+ tags: 'News',
+ },
+ }
+ expect(getPaginationSearchParams(params).toString()).toEqual(
'limit=7&offset=0&tags=News'
)
})
test('should parse values into searchParams for third page', () => {
- const params = 'tags/News/page/3'
- expect(getPaginationSearchParams(7, params).toString()).toEqual(
+ const params = {
+ pageSize: 7,
+ page: 3,
+ filters: {
+ tags: 'News',
+ },
+ }
+ expect(getPaginationSearchParams(params).toString()).toEqual(
'limit=7&offset=14&tags=News'
)
})
test('should return first page without any params specified', () => {
- const params = ''
- expect(getPaginationSearchParams(7, params).toString()).toEqual(
+ const params = {
+ pageSize: 7,
+ page: 1,
+ }
+ expect(getPaginationSearchParams(params).toString()).toEqual(
'limit=7&offset=0'
)
})
diff --git a/src/lib/pagination/searchParams.ts b/src/lib/pagination/searchParams.ts
index 1cc4a51..a8e89fa 100644
--- a/src/lib/pagination/searchParams.ts
+++ b/src/lib/pagination/searchParams.ts
@@ -34,12 +34,26 @@ export function parseParams(params: string) {
return Object.fromEntries(splits)
}
+export function toParams(records: Record) {
+ return Object.entries(records)
+ .map(([key, value]) => `${key}/${value}`)
+ .join('/')
+}
+
+export interface PaginationSearchParams {
+ pageSize: number
+ page: number
+ filters?: Record
+}
+
/**
* Convert svelte `load` params into a `URLSearchParams` so they can be used to fetch endpoints with pagination queries
*/
-export function getPaginationSearchParams(pageSize: number, params: string) {
- const { page = 1, ...filters } = parseParams(params)
-
+export function getPaginationSearchParams({
+ pageSize,
+ page,
+ filters,
+}: PaginationSearchParams) {
const offset = pageSize * (page - 1)
const limit = pageSize
return new URLSearchParams({ limit, offset, ...filters })
diff --git a/src/routes/blog/[...params].svelte b/src/routes/blog/[...params].svelte
index b089d14..7fd6928 100644
--- a/src/routes/blog/[...params].svelte
+++ b/src/routes/blog/[...params].svelte
@@ -1,28 +1,47 @@
@@ -33,18 +52,28 @@
You've found void in the space.
{:else}
- Recent
- {#if tagQuery}
- {tagQuery}
+ {#if filters.tags}
+ {filters.tags}
+ {:else}
+ Blog
{/if}
posts
- {#if tagQuery}
+ {#if filters.tags}
{/if}
{/if}
+
{#each posts.items as post}
-
@@ -60,3 +89,12 @@
{/each}
+
diff --git a/src/routes/feed/_feed.ts b/src/routes/feed/_feed.ts
index 737f045..37dc423 100644
--- a/src/routes/feed/_feed.ts
+++ b/src/routes/feed/_feed.ts
@@ -24,8 +24,8 @@ export async function getFeed() {
},
})
- const blogListing = await getBlogListing()
- blogListing.forEach((post) => {
+ const blogListing = await getBlogListing({})
+ blogListing.items.forEach((post) => {
feed.addItem({
title: post.title,
id: `https://michalvanko.dev/blog/${post.slug}`,
diff --git a/svelte.config.js b/svelte.config.js
index 9bd3f3c..5546daf 100644
--- a/svelte.config.js
+++ b/svelte.config.js
@@ -10,6 +10,7 @@ const config = {
kit: {
adapter: adapterStatic(),
vite: { plugins: [vanillaExtractPlugin()] },
+ prerender: { default: true }
},
preprocess: preprocess({
sourceMap: dev,
diff --git a/vitest.config.js b/vitest.config.js
new file mode 100644
index 0000000..a24f56c
--- /dev/null
+++ b/vitest.config.js
@@ -0,0 +1,3 @@
+import { extractFromSvelteConfig } from "vitest-svelte-kit"
+
+export default extractFromSvelteConfig()