Implement pagination, update the logo again
This commit is contained in:
parent
cf751dd397
commit
5e8eb94d6b
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"useTabs": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "none",
|
||||
"trailingComma": "all",
|
||||
"printWidth": 100,
|
||||
"plugins": ["prettier-plugin-svelte"],
|
||||
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
|
||||
|
|
16
src/app.css
16
src/app.css
|
@ -1,9 +1,9 @@
|
|||
html {
|
||||
--color-primary-h: 49;
|
||||
--color-primary-s: 79%;
|
||||
--color-primary-l: 53%;
|
||||
--color-primary-h: 218;
|
||||
--color-primary-s: 81%;
|
||||
--color-primary-l: 24%;
|
||||
--color-primary: hsl(var(--color-primary-h), var(--color-primary-s), var(--color-primary-l));
|
||||
--color-secondary-h: 218;
|
||||
--color-secondary-h: 100;
|
||||
--color-secondary-s: 81%;
|
||||
--color-secondary-l: 24%;
|
||||
--color-secondary: hsl(
|
||||
|
@ -59,6 +59,9 @@ html {
|
|||
|
||||
--z-index-modal: 100;
|
||||
|
||||
--clip-path: polygon(0% 0%, 75% 0%, 100% 100%, 25% 100%);
|
||||
--clip-path-button: polygon(0px 0px, calc(100% - 10px) 0px, 100% 40px, 10px 40px);
|
||||
|
||||
background-color: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
@ -104,6 +107,11 @@ svg {
|
|||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
button {
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
input,
|
||||
button,
|
||||
textarea,
|
||||
|
|
37
src/lib/Button.svelte
Normal file
37
src/lib/Button.svelte
Normal file
|
@ -0,0 +1,37 @@
|
|||
<script lang="ts">
|
||||
export let disabled = false;
|
||||
export let href: string | undefined;
|
||||
let className = '';
|
||||
export { className as class };
|
||||
</script>
|
||||
|
||||
{#if disabled}
|
||||
<button class={className} disabled><slot /></button>
|
||||
{:else}
|
||||
<a class={className} {href}><slot /></a>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
a,
|
||||
button {
|
||||
display: inline-block;
|
||||
color: hsl(var(--color-text-h), var(--color-text-s), calc(100% - var(--color-text-l)));
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
opacity: 0.8;
|
||||
background-color: var(--color-primary);
|
||||
padding: 0.5rem 1rem;
|
||||
clip-path: var(--clip-path-button);
|
||||
transition: all var(--animation-speed) var(--animation-type);
|
||||
user-select: none;
|
||||
}
|
||||
button {
|
||||
opacity: 0.6;
|
||||
cursor: unset;
|
||||
}
|
||||
a:hover,
|
||||
a:focus-visible {
|
||||
opacity: 1;
|
||||
text-decoration-color: unset;
|
||||
}
|
||||
</style>
|
5
src/params/integer.ts
Normal file
5
src/params/integer.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import type { ParamMatcher } from '@sveltejs/kit';
|
||||
|
||||
export const match: ParamMatcher = (param) => {
|
||||
return /^\d+$/.test(param);
|
||||
};
|
|
@ -24,16 +24,25 @@ React, SvelteKit, TailwindCSS, Rust, Docker, SQL, and AWS technologies
|
|||
|
||||
I'm an active contributor to the open-source world: check out
|
||||
my [Github profile](https://github.com/SeriousBug)! For a closer look at my work,
|
||||
visit my [portfolio](/portfolio) where I
|
||||
visit my [portfolio](/portfolio/) where I
|
||||
highlight my favorite projects. I sometimes write about the tools
|
||||
I use or challenges I've solved on my [blog](/posts). I'm always open to feedback, reach out to me through my socials linked below.
|
||||
I use or challenges I've solved on my [blog](/posts/). I'm always open to feedback, reach out to me through my socials linked below.
|
||||
|
||||
<div class="socials">
|
||||
<a target="_blank" rel="noopener" href="mailto:kaan@bgenc.net">Email</a>
|
||||
<a target="_blank" rel="noopener me" href="https://github.com/SeriousBug">Github</a>
|
||||
<a target="_blank" rel="noopener me" href="https://fosstodon.org/@kaan">Mastodon</a>
|
||||
<a target="_blank" rel="noopener me" href="https://www.linkedin.com/in/kaan-barmore-genc">LinkedIn</a>
|
||||
<a target="_blank" rel="noopener me" href="/static/cv.pdf">CV</a>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
picture {
|
||||
|
||||
max-width: 320px;
|
||||
max-height: 320px;
|
||||
margin: 0 auto;
|
||||
clip-path: polygon(0% 0%, 75% 0%, 100% 100%, 25% 100%);
|
||||
clip-path: var(--clip-path);
|
||||
}
|
||||
|
||||
h1 {
|
||||
|
|
|
@ -45,12 +45,6 @@
|
|||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
opacity: 0.6;
|
||||
transition: opacity var(--animation-speed) var(--animation-type);
|
||||
}
|
||||
.row:hover,
|
||||
.row:focus-within {
|
||||
opacity: 1;
|
||||
}
|
||||
.row > * {
|
||||
flex-grow: 1;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<script>
|
||||
import Button from '$lib/Button.svelte';
|
||||
import Spacer from '$lib/Spacer.svelte';
|
||||
|
||||
import Avif from './logo.avif';
|
||||
|
@ -19,13 +20,12 @@
|
|||
</picture>
|
||||
</a>
|
||||
<Spacer />
|
||||
<a href="/posts">Blog</a>
|
||||
<a href="/portfolio">Portfolio</a>
|
||||
<Button href="/posts/">Blog</Button>
|
||||
<Button href="/portfolio/">Portfolio</Button>
|
||||
</header>
|
||||
|
||||
<style>
|
||||
header {
|
||||
max-width: calc(var(--size-container) * 2);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
export async function load({ params }) {
|
||||
console.log('params', params);
|
||||
const post = await import(`../posts/${params.slug}.md`);
|
||||
const { title, date } = post.metadata;
|
||||
const content = post.default;
|
||||
|
|
BIN
src/routes/logo.avif
(Stored with Git LFS)
BIN
src/routes/logo.avif
(Stored with Git LFS)
Binary file not shown.
|
@ -1,59 +0,0 @@
|
|||
<script lang="ts">
|
||||
import { format } from 'date-fns';
|
||||
|
||||
export let data;
|
||||
function groupPosts(posts: any[]) {
|
||||
const postsByYear = new Map<string, any[]>();
|
||||
posts.forEach((post) => {
|
||||
const year = format(new Date(post.meta.date), 'yyyy');
|
||||
if (!postsByYear.has(year)) {
|
||||
postsByYear.set(year, []);
|
||||
}
|
||||
postsByYear.get(year)!.push(post);
|
||||
});
|
||||
return postsByYear;
|
||||
}
|
||||
</script>
|
||||
|
||||
<ul class="years">
|
||||
{#each groupPosts(data.posts).entries() as [year, posts]}
|
||||
<li>
|
||||
<h2>{year}</h2>
|
||||
<ul class="posts">
|
||||
{#each posts as post}
|
||||
<a href={`/${post.path}`}>
|
||||
<li>
|
||||
<div>
|
||||
{post.meta.title}
|
||||
</div>
|
||||
<time datetime={post.meta.date}>
|
||||
{format(new Date(post.meta.date), 'MMMM d')}
|
||||
</time>
|
||||
</li>
|
||||
</a>
|
||||
{/each}
|
||||
</ul>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
|
||||
<style>
|
||||
time {
|
||||
width: 8rem;
|
||||
}
|
||||
|
||||
li {
|
||||
list-style-type: none;
|
||||
}
|
||||
.years,
|
||||
.posts {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.years {
|
||||
gap: 4rem;
|
||||
}
|
||||
.posts {
|
||||
gap: 1.5rem;
|
||||
}
|
||||
</style>
|
115
src/routes/posts/[[page=integer]]/+page.svelte
Normal file
115
src/routes/posts/[[page=integer]]/+page.svelte
Normal file
|
@ -0,0 +1,115 @@
|
|||
<script lang="ts">
|
||||
import Button from '$lib/Button.svelte';
|
||||
import { format } from 'date-fns';
|
||||
|
||||
export let data;
|
||||
|
||||
function groupPosts(posts: any[]) {
|
||||
const postsByYear = new Map<string, any[]>();
|
||||
posts.forEach((post) => {
|
||||
const year = format(new Date(post.meta.date), 'yyyy');
|
||||
if (!postsByYear.has(year)) {
|
||||
postsByYear.set(year, []);
|
||||
}
|
||||
postsByYear.get(year)!.push(post);
|
||||
});
|
||||
return postsByYear;
|
||||
}
|
||||
</script>
|
||||
|
||||
<ul class="years">
|
||||
{#each groupPosts(data.posts).entries() as [year, posts]}
|
||||
<li>
|
||||
<h2>{year}</h2>
|
||||
<ol class="posts">
|
||||
{#each posts as post}
|
||||
<a href={`/${post.path}`}>
|
||||
<li>
|
||||
<h5 class="post-title">
|
||||
{post.meta.title}
|
||||
</h5>
|
||||
<time datetime={post.meta.date}>
|
||||
{format(new Date(post.meta.date), 'MMMM d')}
|
||||
</time>
|
||||
</li>
|
||||
</a>
|
||||
{/each}
|
||||
</ol>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
|
||||
<div class="pagination-controls">
|
||||
<Button disabled={data.page < 3} href={'/posts/'}>{data.page < 3 ? '_' : data.page - 1}</Button>
|
||||
<Button disabled={data.page === 1} href={data.page === 2 ? '/posts/' : `/posts/${data.page - 1}`}
|
||||
>{data.page === 1 ? '_' : data.page - 1}</Button
|
||||
>
|
||||
<span class="current-page">{data.page}</span>
|
||||
<Button disabled={!data.hasMore} href="/posts/{data.page + 1}"
|
||||
>{data.hasMore ? data.page + 1 : '_'}</Button
|
||||
>
|
||||
<Button disabled={data.page > data.pageCount - 2} href="/posts/{data.pageCount}"
|
||||
>{data.page > data.pageCount - 2 ? '_' : data.pageCount}</Button
|
||||
>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
time {
|
||||
width: 8rem;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.pagination-controls {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 0.2rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
:global(.pagination-controls > :first-child) {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
:global(.pagination-controls > :last-child) {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
.current-page {
|
||||
display: inline-block;
|
||||
color: hsl(var(--color-text-h), var(--color-text-s), calc(100% - var(--color-text-l)));
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
opacity: 0.6;
|
||||
cursor: unset;
|
||||
user-select: none;
|
||||
background-color: var(--color-primary);
|
||||
padding: 0.5rem 1rem;
|
||||
clip-path: var(--clip-path-button);
|
||||
transition: all var(--animation-speed) var(--animation-type);
|
||||
}
|
||||
.post-title {
|
||||
margin-bottom: 0.3rem;
|
||||
text-decoration: underline;
|
||||
text-decoration-color: transparent;
|
||||
transition: all var(--animation-speed) var(--animation-type);
|
||||
}
|
||||
a:hover .post-title {
|
||||
text-decoration-color: unset;
|
||||
}
|
||||
.posts a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
li {
|
||||
list-style-type: none;
|
||||
}
|
||||
.years,
|
||||
.posts {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.years {
|
||||
gap: 4rem;
|
||||
}
|
||||
.posts {
|
||||
gap: 1.5rem;
|
||||
}
|
||||
</style>
|
|
@ -1,6 +1,8 @@
|
|||
import _ from 'lodash';
|
||||
|
||||
export async function load() {
|
||||
const PAGE_SIZE = 10;
|
||||
|
||||
export async function load({ params }) {
|
||||
const allPostFiles = import.meta.glob<SvelteAllProps>('/src/routes/posts/*.md');
|
||||
const iterablePostFiles = Object.entries(allPostFiles);
|
||||
|
||||
|
@ -13,10 +15,21 @@ export async function load() {
|
|||
|
||||
return {
|
||||
meta: metadata,
|
||||
path: slug
|
||||
path: slug,
|
||||
};
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
return { posts: _.reverse(_.sortBy(posts, ({ meta }) => meta.date)) };
|
||||
// Get the page number, converting it to a 0-based index
|
||||
const page = (params.page ? parseInt(params.page, 10) : 1) - 1;
|
||||
|
||||
return {
|
||||
posts: _.reverse(_.sortBy(posts, ({ meta }) => meta.date)).slice(
|
||||
page * PAGE_SIZE,
|
||||
(page + 1) * PAGE_SIZE,
|
||||
),
|
||||
hasMore: posts.length > (page + 1) * PAGE_SIZE,
|
||||
page: page + 1,
|
||||
pageCount: Math.ceil(posts.length / PAGE_SIZE),
|
||||
};
|
||||
}
|
|
@ -17,7 +17,7 @@ h6 {
|
|||
font-size: 1.125rem;
|
||||
}
|
||||
a {
|
||||
color: var(--color-secondary);
|
||||
color: var(--color-primary);
|
||||
font-weight: 600;
|
||||
text-decoration-color: transparent;
|
||||
opacity: 0.8;
|
||||
|
|
BIN
static/android-chrome-192x192.png
(Stored with Git LFS)
BIN
static/android-chrome-192x192.png
(Stored with Git LFS)
Binary file not shown.
BIN
static/android-chrome-512x512.png
(Stored with Git LFS)
BIN
static/android-chrome-512x512.png
(Stored with Git LFS)
Binary file not shown.
BIN
static/apple-touch-icon.png
(Stored with Git LFS)
BIN
static/apple-touch-icon.png
(Stored with Git LFS)
Binary file not shown.
BIN
static/favicon-16x16.png
(Stored with Git LFS)
BIN
static/favicon-16x16.png
(Stored with Git LFS)
Binary file not shown.
BIN
static/favicon-32x32.png
(Stored with Git LFS)
BIN
static/favicon-32x32.png
(Stored with Git LFS)
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
BIN
static/favicon.png
(Stored with Git LFS)
BIN
static/favicon.png
(Stored with Git LFS)
Binary file not shown.
Loading…
Reference in a new issue