From 434c214908e2b2b053016ee9d5903764a8a67478 Mon Sep 17 00:00:00 2001 From: matthieu42morin Date: Sat, 11 Nov 2023 16:33:14 +0100 Subject: [PATCH] Blog layouts and test posts --- src/content/blog.ts | 5 +- src/content/blog/first-post.md | 20 +- src/content/utils.ts | 184 ++++++++++++++++++ src/lib/assets/prism-nord.css | 124 ++++++++++++ src/lib/components/blog/BlogLayout.svelte | 11 ++ src/lib/components/blog/CategoryFilter.svelte | 38 ++++ src/lib/components/blog/PostLayout.svelte | 58 +++--- src/lib/components/blog/PostPreview.svelte | 16 +- src/lib/types/blog.d.ts | 14 +- src/lib/utils/helpers.ts | 123 ++++++++++++ src/routes/blog/+page.svelte | 2 +- 11 files changed, 549 insertions(+), 46 deletions(-) create mode 100644 src/content/utils.ts create mode 100644 src/lib/assets/prism-nord.css create mode 100644 src/lib/components/blog/BlogLayout.svelte create mode 100644 src/lib/components/blog/CategoryFilter.svelte create mode 100644 src/lib/utils/helpers.ts diff --git a/src/content/blog.ts b/src/content/blog.ts index 15e01d6..85991b3 100644 --- a/src/content/blog.ts +++ b/src/content/blog.ts @@ -1,10 +1,9 @@ import type { BlogPost } from '$lib/types/blog'; -import type { MarkdownMetadata } from '$lib/contents/types'; +import type { MarkdownMetadata } from '$content/types'; import type { MdsvexImport } from './types'; -import { parseReadContent } from '../../content/utils'; +import { parseReadContent } from '$content/utils'; import { error } from '@sveltejs/kit'; - export function listBlogPosts() { const posts = import.meta.glob('./blog/*.md', { eager: true, diff --git a/src/content/blog/first-post.md b/src/content/blog/first-post.md index 47a7172..37266bf 100644 --- a/src/content/blog/first-post.md +++ b/src/content/blog/first-post.md @@ -7,9 +7,27 @@ tags: - post published: true image: Feature.jpg - --- ## Svelte Media inside the **Svelte** folder is served from the `static` folder. + +```python + +input_text = ''' "yahooapis.com", + "hotmail.com", + "gfx.ms", + "afx.ms", + "live.com", +''' +# and so on... + +lines = input_text.split('\n') + +formatted_lines = ['* ' + line.strip()[1:-2] + ' * block' for line in lines if line] + +output_text = '\n'.join(formatted_lines) +print(output_text) + +``` diff --git a/src/content/utils.ts b/src/content/utils.ts new file mode 100644 index 0000000..4acc7f0 --- /dev/null +++ b/src/content/utils.ts @@ -0,0 +1,184 @@ +import type { create_ssr_component } from 'svelte/internal'; +type DateStyle = Intl.DateTimeFormatOptions['dateStyle']; + +/** + * Sorts an array of objects by their `date` property in descending order. + * @param a - The first object to compare. + * @param b - The second object to compare. + * @returns A number that represents the difference between the parsed dates of the two objects. + */ +export function dateSort(a: T, b: T): number { + return Date.parse(b.date) - Date.parse(a.date); +} + +export function renderMdsvexComponent(component: any): string { + if (typeof component['render'] != 'function') { + throw new Error( + "Unable to render something that isn't a mdsvex component", + ); + } + + return (component as ReturnType).render().html; +} + +export function mdPathToSlug(path: string) { + return path.split('/').at(-1).slice(0, -3); +} + +export function parseReadContent( + data: Record, +): T[] { + return Object.entries(data) + .map(([file, data]) => ({ + slug: mdPathToSlug(file), + ...data, + })) + .sort(dateSort); +} + +// Old utils +// /** +// * Formats a date string using the specified date style and locale. +// * @param date - The date string to format. +// * @param dateStyle - The style to use when formatting the date. Defaults to 'medium'. +// * @param locales - The locale to use when formatting the date. Defaults to 'en'. +// * @returns The formatted date string. +// */ +// // export function formatDate(date: string, dateStyle: DateStyle = 'medium', locales = 'en') { +// // const formatter = new Intl.DateTimeFormat(locales, { dateStyle }); +// // return formatter.format(new Date(date)); +// // } + +// /** +// * Renders an mdsvex component and returns the resulting HTML string. +// * @param component - The mdsvex component to render. +// * @returns The HTML string that was generated by rendering the component. +// * @throws An error if the `render` property of the component is not a function. +// */ +// export function renderMdsvexComponent(component: ReturnType): string { +// if (typeof component['render'] !== 'function') { +// throw new Error("Unable to render something that isn't a mdsvex component"); +// } + +// return component.render().html; +// } + +// /** +// * Converts an md file path to a slug by removing the last segment of the path and the `.md` extension. +// * @param path - The path of the md file. +// * @returns The slug of the md file. +// */ +// export function mdPathToSlug(path: string): string { +// return (path.split('/').at(-1) || '')?.slice(0, -3) || ''; +// } + +// /** +// * Parses an object of data that has string keys and values of type `T` that have an optional `date` property. +// * @param data - The object of data to parse. +// * @param dateSort - A function that sorts an array of objects by their `date` property in descending order. +// * @param mdPathToSlug - A function that converts an md file path to a slug. +// * @returns An array of objects that have a `slug` property and the properties of the original data objects. +// */ +// export function parseReadContent(data: Record): T[] { +// return Object.entries(data) +// .map( +// ([file, data]) => +// ({ +// slug: mdPathToSlug(file), +// ...data +// } as { slug: string } & T & { date: string }) +// ) +// .sort(dateSort); +// } + + + + +import { readable } from 'svelte/store'; + +export const isEurope = () => { + const offset = new Date().getTimezoneOffset(); + return offset <= 0 && offset >= -180; +}; + +export const stringToBeautifiedFragment = (str: string = '') => + (str || '') + .toLocaleLowerCase() + .replace(/\s/g, '-') + .replace(/\?/g, '') + .replace(/,/g, ''); + +export const showHideOverflowY = (bool: boolean) => { + const html = document.querySelector('html'); + if (bool) { + html.classList.add('overflow-y-hidden'); + } else { + html.classList.remove('overflow-y-hidden'); + } +}; + +export const formatDate = (date) => { + try { + const d = new Date(date); + return `${d.toLocaleString('default', { + month: 'long', + })} ${d.getDate()}, ${d.getFullYear()}`; + } catch (e) { + return ''; + } +}; + +export const scrollToElement = async ( + element: HTMLElement, + selector: string, +) => { + const firstElement: HTMLElement = element.querySelector(selector); + if (!firstElement) { + return; + } + firstElement.scrollIntoView({ + behavior: 'smooth', + }); +}; + +export const removeTrailingSlash = (site: string) => { + return site.replace(/\/$/, ''); +}; + + +export const useMediaQuery = (mediaQueryString: string) => { + const matches = readable(null, (set) => { + if (typeof globalThis['window'] === 'undefined') return; + + const match = window.matchMedia(mediaQueryString); + set(match.matches); + const element = (event: MediaQueryListEvent) => set(event.matches); + match.addEventListener('change', element); + return () => { + match.removeEventListener('change', element); + }; + }); + return matches; +}; + +export const scrollIntoView = (selector: string) => { + const scrollToElement = document.querySelector(selector); + + if (!scrollToElement) return; + + const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)'); + + scrollToElement.scrollIntoView({ + block: 'nearest', + inline: 'start', + behavior: mediaQuery.matches ? 'auto' : 'smooth', + }); +}; +export const getVariantFromStatus = (status: string) => { + if (status === 'soon' || status === 'Early Access') { + return 'pink'; + } else { + return 'orange'; + } +}; + diff --git a/src/lib/assets/prism-nord.css b/src/lib/assets/prism-nord.css new file mode 100644 index 0000000..23b8ac9 --- /dev/null +++ b/src/lib/assets/prism-nord.css @@ -0,0 +1,124 @@ +/** + * Nord Theme Originally by Arctic Ice Studio + * https://nordtheme.com + * + * Ported for PrismJS by Zane Hitchcoxc (@zwhitchcox) and Gabriel Ramos (@gabrieluizramos) + */ + +code[class*="language-"], +pre[class*="language-"] { + color: #f8f8f2; + background: none; + font-family: "Fira Code", Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + line-height: 1.5; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; +} + +/* Code blocks */ +pre[class*="language-"] { + padding: 1em; + margin: .5em 0; + overflow: auto; + border-radius: 0.3em; +} + +:not(pre) > code[class*="language-"], +pre[class*="language-"] { + background: #2E3440; +} + +/* Inline code */ +:not(pre) > code[class*="language-"] { + padding: .1em; + border-radius: .3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: #636f88; +} + +.token.punctuation { + color: #81A1C1; +} + +.namespace { + opacity: .7; +} + +.token.property, +.token.tag, +.token.constant, +.token.symbol, +.token.deleted { + color: #81A1C1; +} + +.token.number { + color: #B48EAD; +} + +.token.boolean { + color: #81A1C1; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #A3BE8C; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string, +.token.variable { + color: #81A1C1; +} + +.token.atrule, +.token.attr-value, +.token.function, +.token.class-name { + color: #88C0D0; +} + +.token.keyword { + color: #81A1C1; +} + +.token.regex, +.token.important { + color: #EBCB8B; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} diff --git a/src/lib/components/blog/BlogLayout.svelte b/src/lib/components/blog/BlogLayout.svelte new file mode 100644 index 0000000..d40f867 --- /dev/null +++ b/src/lib/components/blog/BlogLayout.svelte @@ -0,0 +1,11 @@ + + + + + diff --git a/src/lib/components/blog/CategoryFilter.svelte b/src/lib/components/blog/CategoryFilter.svelte new file mode 100644 index 0000000..48cfa72 --- /dev/null +++ b/src/lib/components/blog/CategoryFilter.svelte @@ -0,0 +1,38 @@ + + +
+

Sort by category

+
    + {#each options as option} +
  • + +
  • + {/each} +
+
diff --git a/src/lib/components/blog/PostLayout.svelte b/src/lib/components/blog/PostLayout.svelte index 0f9fb4c..def37aa 100644 --- a/src/lib/components/blog/PostLayout.svelte +++ b/src/lib/components/blog/PostLayout.svelte @@ -1,48 +1,52 @@
- {`${title}`} -
-

- {formatDate(date)} -

- {#if tags && tags.length > 0} -
- {#each tags as tag} - - {tag} - - {/each} -
- {/if} - +
+ {`${title}`} +
+
+

{title}

+
+ +
+
+
+
+ {#if tags && tags.length > 0} +
+ tags: {#each tags as tag} + + {tag} + + {/each} +
+ {/if} + On {formatDate(date)} +
+