Skip to content

Commit

Permalink
feat: fix more things
Browse files Browse the repository at this point in the history
  • Loading branch information
flornkm committed Nov 24, 2023
1 parent bffadac commit 0ed980d
Show file tree
Hide file tree
Showing 10 changed files with 297 additions and 62 deletions.
2 changes: 1 addition & 1 deletion interface/components/Footer.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { getLocale } from "#hooks/getLocale"
import { usePageContext } from "../../renderer/usePageContext"
import Bulb from "~icons/eva/bulb-fill"
import LanguagePicker from "./LanguagePicker"
import { LanguagePicker } from "./Picker"
import * as m from "#lang/paraglide/messages"
import { languageTag, sourceLanguageTag } from "#lang/paraglide/runtime"

Expand Down
172 changes: 172 additions & 0 deletions interface/components/Picker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import { useState } from "preact/hooks"
import useOuterClick from "#hooks/useOuterClick"
import { JSX } from "preact/jsx-runtime"
import { languageTag, sourceLanguageTag } from "#lang/paraglide/runtime"
import Check from "~icons/eva/checkmark-outline"
import { navigate } from "vike/client/router"

type functionOption = {
label: string
function: () => void
}

type linkOption = {
label: string
link: string
}

export default function Picker(props: {
position: "top" | "bottom"
align: "right" | "left"
options: (functionOption | linkOption)[]
children: JSX.Element
}) {
const [open, setOpen] = useState(false)
const elementsList = useOuterClick(() => {
if (open) setOpen(false)
})

return (
<div class="flex items-center relative">
<button
onClick={() => (open ? setOpen(false) : setOpen(true))}
class={
"text-xl p-1 rounded-md transition-colors hover:text-black hover:bg-zinc-100 before:opacity-0 dark:hover:text-white dark:hover:bg-zinc-900 " +
(open
? "text-black dark:text-white bg-zinc-100 dark:bg-zinc-900"
: "text-zinc-400")
}
>
{props.children}
</button>
<div
// @ts-ignore
ref={elementsList}
class={
"absolute z-10 bg-zinc-50 border border-zinc-200 rounded-md flex flex-col transition-all overflow-hidden dark:bg-zinc-950 dark:border-zinc-800 " +
(props.align === "left" ? "left-0 " : "right-0 ") +
(open
? "opacity-100 " +
(props.position === "top" ? "bottom-9 " : "top-9 ")
: "opacity-0 pointer-events-none " +
(props.position === "top" ? "bottom-4 " : "top-4 "))
}
>
{props.options.map((option) => {
return (
<a
class={
"px-3 py-1.5 cursor-pointer transition-colors hover:bg-zinc-100 flex items-center gap-1.5 justify-start dark:hover:bg-zinc-900 " +
(props.options.length - 1 === props.options.indexOf(option)
? ""
: " border-b border-b-zinc-200 dark:border-b-zinc-800")
}
href={"link" in option ? option.link : undefined}
onClick={"function" in option ? option.function : undefined}
>
<p class="w-auto truncate">{option.label}</p>
</a>
)
})}
</div>
</div>
)
}

type Language = {
name: string
languageTag: string
link: string
}

export function LanguagePicker(props: {
position: "top" | "bottom"
align: "right" | "left"
}) {
const [open, setOpen] = useState(false)
const elementsList = useOuterClick(() => {
if (open) setOpen(false)
})

const languages = [
{
name: "English",
languageTag: sourceLanguageTag,
link: "/",
},
{
name: "Chinese",
languageTag: "zh",
link: "/zh",
},
] as Language[]

return (
<div class="flex items-center relative">
<button
onClick={() => (open ? setOpen(false) : setOpen(true))}
class={
"text-xl p-1 rounded-md transition-colors hover:text-black hover:bg-zinc-100 before:opacity-0 dark:hover:text-white dark:hover:bg-zinc-900 " +
(open
? "text-black dark:text-white bg-zinc-100 dark:bg-zinc-900"
: "text-zinc-400")
}
>
<LanguageIcon />
</button>
<div
// @ts-ignore
ref={elementsList}
class={
"absolute z-10 bg-zinc-50 border border-zinc-200 rounded-md flex flex-col transition-all overflow-hidden dark:bg-zinc-950 dark:border-zinc-800 " +
// (props.position === "top" ? "bottom-8 " : "top-8 ") +
(props.align === "left" ? "left-0 " : "right-0 ") +
(open
? "opacity-100 " +
(props.position === "top" ? "bottom-9 " : "top-9 ")
: "opacity-0 pointer-events-none " +
(props.position === "top" ? "bottom-4 " : "top-4 "))
}
>
{languages.map((locale) => {
return (
<a
class={
"px-3 py-1.5 transition-colors hover:bg-zinc-100 flex items-center gap-1.5 justify-end dark:hover:bg-zinc-900 " +
(languages.length - 1 === languages.indexOf(locale)
? ""
: " border-b border-b-zinc-200 dark:border-b-zinc-800")
}
href={locale.link}
onClick={(e) => {
e.preventDefault()
navigate(locale.link).then(() =>
window.open(locale.link, "_self")
)
}}
>
{locale.languageTag === languageTag() && <Check />}
<p class="w-16">{locale.name}</p>
</a>
)
})}
</div>
</div>
)
}

function LanguageIcon() {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
width="1em"
height="1em"
viewBox="0 0 512 512"
>
<title>ionicons-v5-l</title>
<path d="M478.33,433.6l-90-218a22,22,0,0,0-40.67,0l-90,218a22,22,0,1,0,40.67,16.79L316.66,406H419.33l18.33,44.39A22,22,0,0,0,458,464a22,22,0,0,0,20.32-30.4ZM334.83,362,368,281.65,401.17,362Z" />
<path d="M267.84,342.92a22,22,0,0,0-4.89-30.7c-.2-.15-15-11.13-36.49-34.73,39.65-53.68,62.11-114.75,71.27-143.49H330a22,22,0,0,0,0-44H214V70a22,22,0,0,0-44,0V90H54a22,22,0,0,0,0,44H251.25c-9.52,26.95-27.05,69.5-53.79,108.36-31.41-41.68-43.08-68.65-43.17-68.87a22,22,0,0,0-40.58,17c.58,1.38,14.55,34.23,52.86,83.93.92,1.19,1.83,2.35,2.74,3.51-39.24,44.35-77.74,71.86-93.85,80.74a22,22,0,1,0,21.07,38.63c2.16-1.18,48.6-26.89,101.63-85.59,22.52,24.08,38,35.44,38.93,36.1a22,22,0,0,0,30.75-4.9Z" />
</svg>
)
}
4 changes: 2 additions & 2 deletions interface/components/Tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export default function Tooltip(props: {
return (
<span
class={
"opacity-0 font-normal group-hover:opacity-100 delay-75 scale-90 group-hover:scale-100 pointer-events-none transition-all duration-300 ease-out absolute text-sm px-2 py-1 rounded-full bg-black z-[99] text-white dark:bg-white dark:text-black " +
"opacity-0 font-normal group-hover:opacity-100 delay-75 scale-90 group-hover:scale-100 pointer-events-none transition-all duration-150 ease-out absolute text-sm px-2 py-1 rounded-full bg-black z-[99] text-white dark:bg-white dark:text-black " +
(props.position === "top"
? "-top-5 group-hover:-top-6 left-[50%] translate-x-[-50%]"
: "") +
Expand All @@ -26,7 +26,7 @@ export default function Tooltip(props: {
>
<span
class={
"w-2.5 h-2.5 rounded-sm bg-black absolute scale-75 group-hover:scale-100 transition-transform duration-300 z-30 transform rotate-45 dark:bg-white " +
"w-2.5 h-2.5 rounded-sm bg-black absolute scale-75 group-hover:scale-100 transition-transform duration-200 z-30 transform rotate-45 dark:bg-white " +
(props.position === "top"
? "-bottom-1 left-[50%] translate-x-[-50%]"
: "") +
Expand Down
29 changes: 29 additions & 0 deletions markdown/Markdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
export default function Markdown(props: { class?: string; content: string }) {
// Regular expression pattern to capture video ID and image source
const pattern =
/<a\s+href="([A-Za-z0-9_-]{11})"\s*><img\s+src="([^"]+)"[^>]+><\/a>/g

// Replace the matches with the cover image and add click event
const replacedContent = props.content.replace(
pattern,
(match, videoId, imgSrc) => `<div
class="video-container"
onclick="this.innerHTML = '<iframe width=&quot;100%&quot; height=&quot;100%&quot; style=&quot;aspect-ratio: 16/9&quot; src=&quot;https://www.youtube.com/embed/${videoId}?autoplay=1&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; frameborder=&quot;0&quot; allowfullscreen autoplay></iframe>';"
style="cursor: pointer; width: 100%; height: auto;">
<div class="video-wrapper">
<img
src="${imgSrc}"
class="video-overlay"
alt="Video cover"
style="width: 100%; height: auto;"
/>
</div>
</div>`
)

return (
<article className={props.class}>
<div dangerouslySetInnerHTML={{ __html: replacedContent }}></div>
</article>
)
}
26 changes: 3 additions & 23 deletions markdown/convert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,9 @@ export async function convertMarkdownToHtml(
let markdown = await readFile(`${contentRoot}${url}.md`, "utf-8")

const convertedHTML = marked(
convertVideo(
deleteInfo(
markdown + '\n <base target="_blank">',
markdown.match(/---(.*?)---/s)![1].split("\n").length
)
deleteInfo(
markdown + '\n <base target="_blank">',
markdown.match(/---(.*?)---/s)![1].split("\n").length
)
)

Expand Down Expand Up @@ -62,21 +60,3 @@ export async function returnContent(category: "work" | "archive" | "feed") {
function deleteInfo(string: string, n: number) {
return string.replace(new RegExp(`(?:.*?\n){${n - 1}}(?:.*?\n)`), "")
}

function convertVideo(string: string) {
return string.replace(
/\[!\[(.*?)\]\((.*?)\)\]\((.*?)\)/g,
`<div class="video-container">
<div class="video-wrapper" onclick="loadVideo(this, 'https://www.youtube.com/embed/$3?autoplay=1', '$1', '$2')">
<img class="video-overlay" src="$2" alt="$1" class="video-thumbnail">
</div>
<script>
function loadVideo(wrapper, src, title, thumbnail) {
if (typeof window === 'undefined') return;
var container = wrapper.parentElement;
container.innerHTML = '<iframe width="100%" height="100%" src="' + src + '" title="' + title + '" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>';
}
</script>
</div>`
)
}
5 changes: 2 additions & 3 deletions pages/archive/@slug/+Page.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import "#design-system/markdown.css"
import Markdown from "#markdown/Markdown"

export default function Page(props: Record<string, string>) {
return (
<>
<article class="lg:py-16 pb-16">
<div dangerouslySetInnerHTML={{ __html: props.content }}></div>
</article>
<Markdown class="lg:py-16 pb-16" content={props.content} />
</>
)
}
36 changes: 33 additions & 3 deletions pages/feed/@slug/+Page.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,41 @@
import Picker from "#components/Picker"
import "#design-system/feed.css"
import Markdown from "#markdown/Markdown"
import { useState } from "preact/hooks"
import Share from "~icons/eva/share-outline"

export default function Page(props: Record<string, string>) {
const [copyLabel, setCopyLabel] = useState("Copy link")
return (
<>
<article class="lg:py-16 pb-16 max-w-lg mx-auto">
<div dangerouslySetInnerHTML={{ __html: props.content }}></div>
</article>
<div>
<Markdown class="lg:mt-16 max-w-lg mx-auto" content={props.content} />
</div>
<div class="flex items-center justify-end mb-16 mx-auto max-w-lg">
<Picker
options={[
{
label: copyLabel,
function: () => {
// copy to clipboard the current page's url
navigator.clipboard.writeText(
typeof window !== "undefined"
? window.location.href.replace(/\/$/, "")
: ""
)
setCopyLabel("Copied!")
setTimeout(() => {
setCopyLabel("Copy link")
}, 1000)
},
},
]}
position="top"
align="right"
>
<Share />
</Picker>
</div>
</>
)
}
38 changes: 32 additions & 6 deletions pages/feed/index/+Page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import "#design-system/feed.css"
import { Post, PostContent } from "../types"
import * as m from "#lang/paraglide/messages"
import Share from "~icons/eva/share-outline"
import Picker from "#components/Picker"
import { useState } from "preact/hooks"
import Markdown from "#markdown/Markdown"

export default function Page({
posts,
Expand All @@ -9,6 +13,7 @@ export default function Page({
posts: Post[]
content: PostContent
}) {
const [copyLabel, setCopyLabel] = useState("Copy link")
return (
<div class="w-full">
<section class="w-full lg:pt-16">
Expand Down Expand Up @@ -39,12 +44,33 @@ export default function Page({
})}{" "}
{date.getDate()}, {date.getFullYear()}
</p>
<article>
<div
class="post"
dangerouslySetInnerHTML={{ __html: content[post.slug] }}
/>
</article>
<Markdown content={content[post.slug]} />
<div class="flex items-center justify-end">
<Picker
options={[
{
label: copyLabel,
function: () => {
navigator.clipboard.writeText(
"https://floriankiem.com" + post.url
)
setCopyLabel("Copied!")
setTimeout(() => {
setCopyLabel("Copy link")
}, 1000)
},
},
{
label: "Share on X",
link: `https://x.com/intent/tweet?text=${post.title} from Florian&url=https://floriankiem.com${post.url}`,
},
]}
position="top"
align="right"
>
<Share />
</Picker>
</div>
</div>
)
})}
Expand Down
Loading

1 comment on commit 0ed980d

@vercel
Copy link

@vercel vercel bot commented on 0ed980d Nov 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.