mirror of
https://github.com/KazooTTT/kazoottt-blog.git
synced 2025-06-24 03:01:31 +08:00
Built resume template
This commit is contained in:
17
src/utils/date.ts
Normal file
17
src/utils/date.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { siteConfig } from '@/site-config'
|
||||
|
||||
const dateFormat = new Intl.DateTimeFormat(siteConfig.date.locale, siteConfig.date.options)
|
||||
|
||||
export function getFormattedDate(
|
||||
date: string | number | Date,
|
||||
options?: Intl.DateTimeFormatOptions
|
||||
) {
|
||||
if (typeof options !== 'undefined') {
|
||||
return new Date(date).toLocaleDateString(siteConfig.date.locale, {
|
||||
...(siteConfig.date.options as Intl.DateTimeFormatOptions),
|
||||
...options
|
||||
})
|
||||
}
|
||||
|
||||
return dateFormat.format(new Date(date))
|
||||
}
|
11
src/utils/domElement.ts
Normal file
11
src/utils/domElement.ts
Normal file
@ -0,0 +1,11 @@
|
||||
export function toggleClass(element: HTMLElement, className: string) {
|
||||
element.classList.toggle(className)
|
||||
}
|
||||
|
||||
export function elementHasClass(element: HTMLElement, className: string) {
|
||||
return element.classList.contains(className)
|
||||
}
|
||||
|
||||
export function rootInDarkMode() {
|
||||
return document.documentElement.getAttribute('data-theme') === 'dark'
|
||||
}
|
41
src/utils/generateToc.ts
Normal file
41
src/utils/generateToc.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import type { MarkdownHeading } from 'astro'
|
||||
|
||||
export interface TocItem extends MarkdownHeading {
|
||||
subheadings: Array<TocItem>
|
||||
}
|
||||
|
||||
function diveChildren(item: TocItem, depth: number): Array<TocItem> {
|
||||
if (depth === 1 || !item.subheadings.length) {
|
||||
return item.subheadings
|
||||
} else {
|
||||
// e.g., 2
|
||||
return diveChildren(item.subheadings[item.subheadings.length - 1] as TocItem, depth - 1)
|
||||
}
|
||||
}
|
||||
|
||||
export function generateToc(headings: ReadonlyArray<MarkdownHeading>) {
|
||||
// this ignores/filters out h1 element(s)
|
||||
const bodyHeadings = [...headings.filter(({ depth }) => depth > 1)]
|
||||
const toc: Array<TocItem> = []
|
||||
|
||||
bodyHeadings.forEach((h) => {
|
||||
const heading: TocItem = { ...h, subheadings: [] }
|
||||
|
||||
// add h2 elements into the top level
|
||||
if (heading.depth === 2) {
|
||||
toc.push(heading)
|
||||
} else {
|
||||
const lastItemInToc = toc[toc.length - 1]!
|
||||
if (heading.depth < lastItemInToc.depth) {
|
||||
throw new Error(`Orphan heading found: ${heading.text}.`)
|
||||
}
|
||||
|
||||
// higher depth
|
||||
// push into children, or children's children
|
||||
const gap = heading.depth - lastItemInToc.depth
|
||||
const target = diveChildren(lastItemInToc, gap)
|
||||
target.push(heading)
|
||||
}
|
||||
})
|
||||
return toc
|
||||
}
|
6
src/utils/index.ts
Normal file
6
src/utils/index.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export { cn } from './tailwind'
|
||||
export { getAllPosts, sortMDByDate, getUniqueTags, getUniqueTagsWithCount } from './post'
|
||||
export { getFormattedDate } from './date'
|
||||
export { generateToc } from './generateToc'
|
||||
export type { TocItem } from './generateToc'
|
||||
export { elementHasClass, toggleClass, rootInDarkMode } from './domElement'
|
39
src/utils/post.ts
Normal file
39
src/utils/post.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import type { CollectionEntry } from 'astro:content'
|
||||
import { getCollection } from 'astro:content'
|
||||
|
||||
/** Note: this function filters out draft posts based on the environment */
|
||||
export async function getAllPosts() {
|
||||
return await getCollection('post', ({ data }) => {
|
||||
return import.meta.env.PROD ? data.draft !== true : true
|
||||
})
|
||||
}
|
||||
|
||||
export function sortMDByDate(posts: Array<CollectionEntry<'post'>>) {
|
||||
return posts.sort((a, b) => {
|
||||
const aDate = new Date(a.data.updatedDate ?? a.data.publishDate).valueOf()
|
||||
const bDate = new Date(b.data.updatedDate ?? b.data.publishDate).valueOf()
|
||||
return bDate - aDate
|
||||
})
|
||||
}
|
||||
|
||||
/** Note: This function doesn't filter draft posts, pass it the result of getAllPosts above to do so. */
|
||||
export function getAllTags(posts: Array<CollectionEntry<'post'>>) {
|
||||
return posts.flatMap((post) => [...post.data.tags])
|
||||
}
|
||||
|
||||
/** Note: This function doesn't filter draft posts, pass it the result of getAllPosts above to do so. */
|
||||
export function getUniqueTags(posts: Array<CollectionEntry<'post'>>) {
|
||||
return [...new Set(getAllTags(posts))]
|
||||
}
|
||||
|
||||
/** Note: This function doesn't filter draft posts, pass it the result of getAllPosts above to do so. */
|
||||
export function getUniqueTagsWithCount(
|
||||
posts: Array<CollectionEntry<'post'>>
|
||||
): Array<[string, number]> {
|
||||
return [
|
||||
...getAllTags(posts).reduce(
|
||||
(acc, t) => acc.set(t, (acc.get(t) || 0) + 1),
|
||||
new Map<string, number>()
|
||||
)
|
||||
].sort((a, b) => b[1] - a[1])
|
||||
}
|
13
src/utils/remarkReadingTime.ts
Normal file
13
src/utils/remarkReadingTime.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import getReadingTime from 'reading-time'
|
||||
import { toString } from 'mdast-util-to-string'
|
||||
|
||||
export function remarkReadingTime() {
|
||||
// @ts-expect-error:next-line
|
||||
return function (tree, { data }) {
|
||||
const textOnPage = toString(tree)
|
||||
const readingTime = getReadingTime(textOnPage)
|
||||
// readingTime.text will give us minutes read as a friendly string,
|
||||
// i.e. "3 min read"
|
||||
data.astro.frontmatter.minutesRead = readingTime.text
|
||||
}
|
||||
}
|
6
src/utils/tailwind.ts
Normal file
6
src/utils/tailwind.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { clsx, type ClassValue } from 'clsx'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
}
|
Reference in New Issue
Block a user