-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4 from kettei-sproutty/feature/blog-section
feat(blog): create blog page
- Loading branch information
Showing
32 changed files
with
773 additions
and
46 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
<script lang="ts"> | ||
import { page } from '$app/stores' | ||
import { cn } from '$lib' | ||
import type { BlogPost } from '$lib/blog/post' | ||
type BlogLinkProps = { | ||
article: BlogPost | ||
} | ||
let { article }: BlogLinkProps = $props() | ||
</script> | ||
|
||
<a | ||
aria-current={$page.params.slug === article.slug ? "page" : undefined} | ||
href="/blog/{article.slug}" | ||
class={cn( | ||
"flex justify-between hover:bg-primary-600 transition-colors duration-200 ease-in-out px-2", | ||
"focus:bg-primary-500", | ||
{ | ||
"bg-primary-700": $page.params.slug === article.slug, | ||
}, | ||
)} | ||
> | ||
<span>{article.metadata.title}</span> | ||
<span class="text-primary-400"> | ||
{Intl.DateTimeFormat("en", { | ||
year: "2-digit", | ||
month: "short", | ||
day: "numeric", | ||
}).format(new Date(article.metadata.date))} | ||
</span> | ||
</a> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
<script lang="ts"> | ||
import Button from '$components/ui/button.svelte' | ||
import Input from '$components/ui/input.svelte' | ||
import { cn } from '$lib' | ||
import BlogSection from './blog-section.svelte' | ||
type BlogModalProps = { | ||
showModal: boolean | ||
handleFilter: (value: string) => void | ||
closeModal: () => void | ||
search: string | ||
} | ||
let { showModal, handleFilter, closeModal, search }: BlogModalProps = $props() | ||
let value = $state(search) | ||
const handleClose = (event: MouseEvent | KeyboardEvent) => { | ||
if (!showModal) return | ||
if (event instanceof KeyboardEvent) { | ||
if (event.key === ' ' || event.key === 'Escape') { | ||
event.preventDefault() | ||
closeModal() | ||
} | ||
return | ||
} | ||
event.preventDefault() | ||
closeModal() | ||
} | ||
const handleSearch = (event: MouseEvent | KeyboardEvent) => { | ||
if (!showModal) return | ||
if (event instanceof KeyboardEvent) { | ||
if (event.key === ' ' || event.key === 'Enter') { | ||
handleFilter(value) | ||
closeModal() | ||
} | ||
return | ||
} | ||
handleFilter(value) | ||
closeModal() | ||
} | ||
$effect(() => { | ||
const handleKeyUp = (event: KeyboardEvent) => { | ||
switch (event.key) { | ||
case 'Escape': | ||
handleClose(event) | ||
break | ||
case 'Enter': | ||
handleSearch(event) | ||
break | ||
} | ||
} | ||
window.addEventListener('keyup', handleKeyUp) | ||
return () => window.removeEventListener('keyup', handleKeyUp) | ||
}) | ||
</script> | ||
|
||
<dialog open={showModal}> | ||
<div | ||
class={cn( | ||
"fixed inset-0 bg-black bg-opacity-50 z-50 w-full h-full", | ||
showModal ? "flex" : "hidden", | ||
)} | ||
></div> | ||
{#if showModal} | ||
<div | ||
class={cn( | ||
"fixed inset-0 flex justify-center items-center z-50", | ||
showModal ? "flex" : "hidden", | ||
)} | ||
> | ||
<BlogSection class="text-primary-300 bg-primary-900 p-4 w-80" label="Search" id="search"> | ||
<Input | ||
autofocus | ||
showLabel={false} | ||
label="search" | ||
value={value} | ||
oninput={(e) => e.target instanceof HTMLInputElement && (value = e.target.value)} | ||
error={null} | ||
placeholder={"Search for an article"} | ||
/> | ||
<div class="flex gap-2"> | ||
<Button onkeypress={closeModal} onclick={closeModal} class="w-1/2 flex items-center justify-center bg-primary-900" type="button"> | ||
<span class="text-center text-sm">Close</span> | ||
<kbd class="text-primary-300 font-thin text-xs">(Esc)</kbd> | ||
</Button> | ||
<Button onkeypress={handleSearch} onclick={handleSearch} class="w-1/2 flex items-center justify-center bg-primary-900" type="button"> | ||
<span class="text-center text-sm">Search</span> | ||
<kbd class="text-primary-300 font-thin text-xs">(Enter)</kbd> | ||
</Button> | ||
</div></BlogSection | ||
> | ||
</div> | ||
{/if} | ||
</dialog> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
<script lang="ts"> | ||
import { cn } from '$lib' | ||
import type { HTMLAttributes } from 'svelte/elements' | ||
type BlogSectionProps = HTMLAttributes<HTMLDivElement> & { | ||
label: string | ||
isAside?: boolean | ||
id: string | ||
} | ||
const { label, isAside = false, id, ...props }: BlogSectionProps = $props() | ||
</script> | ||
|
||
{#if isAside} | ||
<aside id={id} class={cn("blog-section", props.class)}> | ||
<h2 class="blog-section__title">{label}</h2> | ||
<div class="h-2"></div> | ||
{@render props.children?.()} | ||
</aside> | ||
{:else} | ||
<section id={id} class={cn("blog-section", props.class)}> | ||
<h2 class="blog-section__title">{label}</h2> | ||
<div class="h-2"></div> | ||
{@render props.children?.()} | ||
</section> | ||
{/if} | ||
|
||
<style lang="postcss"> | ||
.blog-section { | ||
@apply border border-primary-500 rounded-md relative; | ||
} | ||
.blog-section__title { | ||
@apply text-sm font-sans bg-primary-900 inline-block -top-3 absolute left-2 px-2 font-bold; | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export { default as BlogLink } from './blog-link.svelte' | ||
export { default as BlogModal } from './blog-modal.svelte' | ||
export { default as BlogSection } from './blog-section.svelte' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
<script lang="ts"> | ||
import { cn } from '$lib' | ||
import type { HTMLAttributes, HTMLButtonAttributes } from 'svelte/elements' | ||
type ButtonProps = Omit<HTMLButtonAttributes, 'type'> & { | ||
hasIcon?: boolean | ||
fullWidth?: boolean | ||
type: Exclude<HTMLButtonAttributes['type'], undefined | null> | ||
} | ||
const { children, type, fullWidth = false, class: className, ...props }: ButtonProps = $props() | ||
</script> | ||
|
||
<button | ||
type={type} | ||
class={cn( | ||
"border border-primary-700 bg-primary-700 transition-colors duration-200 ease-in-out text-primary-200 px-2 py-1 rounded-md flex items-center gap-2", | ||
"hover:bg-primary-600 focus:bg-primary-700 focus:outline-none active:bg-primary-800 active:text-primary-100", | ||
{ | ||
"flex gap-2 items-center": props.hasIcon, | ||
"w-full flex items-center justify-center": fullWidth, | ||
}, | ||
className, | ||
)} | ||
{...props} | ||
> | ||
{@render children?.()} | ||
</button> |
Oops, something went wrong.