format all src
This commit is contained in:
parent
eff108c7d0
commit
4cb2cddcef
@ -1,59 +1,58 @@
|
||||
.md * {
|
||||
@apply py-2
|
||||
@apply py-2;
|
||||
}
|
||||
|
||||
.md ol {
|
||||
@apply list-decimal list-inside
|
||||
@apply list-decimal list-inside;
|
||||
}
|
||||
.md ul {
|
||||
@apply list-disc list-inside
|
||||
@apply list-disc list-inside;
|
||||
}
|
||||
|
||||
.md hr {
|
||||
@apply m-4
|
||||
@apply m-4;
|
||||
}
|
||||
|
||||
.md blockquote {
|
||||
@apply border-l-neutral-500 border-l-4 p-2
|
||||
@apply border-l-neutral-500 border-l-4 p-2;
|
||||
}
|
||||
|
||||
.md blockquote p {
|
||||
@apply before:content-['"'] after:content-['"']
|
||||
@apply before:content-['"'] after:content-['"'];
|
||||
}
|
||||
|
||||
.md details {
|
||||
@apply p-20
|
||||
@apply p-20;
|
||||
}
|
||||
|
||||
.md code {
|
||||
@apply rounded-lg
|
||||
@apply rounded-lg;
|
||||
}
|
||||
|
||||
.md h1 {
|
||||
@apply text-6xl
|
||||
@apply text-6xl;
|
||||
}
|
||||
|
||||
.md h2 {
|
||||
@apply text-5xl
|
||||
@apply text-5xl;
|
||||
}
|
||||
|
||||
.md h3 {
|
||||
@apply text-4xl
|
||||
@apply text-4xl;
|
||||
}
|
||||
|
||||
.md h4 {
|
||||
@apply text-3xl
|
||||
@apply text-3xl;
|
||||
}
|
||||
|
||||
.md h5 {
|
||||
@apply text-2xl
|
||||
@apply text-2xl;
|
||||
}
|
||||
|
||||
.md h6 {
|
||||
@apply text-xl
|
||||
@apply text-xl;
|
||||
}
|
||||
|
||||
.md a {
|
||||
@apply underline text-teal-200 hover:text-cyan-500
|
||||
@apply underline text-teal-200 hover:text-cyan-500;
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import styles from "./content.module.css"
|
||||
import styles from "./content.module.css";
|
||||
import { getAllPaths, getContent } from "$lib/content";
|
||||
import Markdown from "react-markdown";
|
||||
import remarkGfm from "remark-gfm";
|
||||
@ -9,19 +9,19 @@ import { notFound } from "next/navigation";
|
||||
import Link from "next/link";
|
||||
import CopyButton from "$components/copy-button";
|
||||
import { getText } from "$lib/react";
|
||||
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"
|
||||
import codeStyle from 'react-syntax-highlighter/dist/esm/styles/prism/coldark-dark'
|
||||
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
||||
import codeStyle from "react-syntax-highlighter/dist/esm/styles/prism/coldark-dark";
|
||||
|
||||
type Params = {
|
||||
slug: string[]
|
||||
}
|
||||
slug: string[];
|
||||
};
|
||||
|
||||
type Props = {
|
||||
params: Params
|
||||
}
|
||||
params: Params;
|
||||
};
|
||||
|
||||
export async function generateStaticParams(): Promise<Params[]> {
|
||||
return getAllPaths().map(p => ({ slug: p.split("/") }))
|
||||
return getAllPaths().map(p => ({ slug: p.split("/") }));
|
||||
}
|
||||
|
||||
export default function Content({ params }: Props) {
|
||||
@ -29,34 +29,34 @@ export default function Content({ params }: Props) {
|
||||
const { data, content } = getContent(params.slug);
|
||||
|
||||
if (data.draft) {
|
||||
notFound()
|
||||
notFound();
|
||||
}
|
||||
|
||||
const title = () => <span className="text-3xl">{data.title}</span>
|
||||
const title = () => <span className="text-3xl">{data.title}</span>;
|
||||
|
||||
const goal = () =>
|
||||
data.goal ?
|
||||
(
|
||||
<div>
|
||||
<h2>The goal</h2>
|
||||
{data.goal}
|
||||
</div>
|
||||
) :
|
||||
"";
|
||||
data.goal ? (
|
||||
<div>
|
||||
<h2>The goal</h2>
|
||||
{data.goal}
|
||||
</div>
|
||||
) : (
|
||||
""
|
||||
);
|
||||
|
||||
const role = () =>
|
||||
data.role ?
|
||||
(
|
||||
<div>
|
||||
<h2>My role</h2>
|
||||
{data.role}
|
||||
</div>
|
||||
) :
|
||||
"";
|
||||
data.role ? (
|
||||
<div>
|
||||
<h2>My role</h2>
|
||||
{data.role}
|
||||
</div>
|
||||
) : (
|
||||
""
|
||||
);
|
||||
|
||||
const date = () => data.date ? (<span>{data.date}</span>) : ""
|
||||
const date = () => (data.date ? <span>{data.date}</span> : "");
|
||||
|
||||
const ctnt = () =>
|
||||
const ctnt = () => (
|
||||
<Markdown
|
||||
className={styles.md}
|
||||
remarkPlugins={[remarkGfm, remarkFrontmatter]}
|
||||
@ -65,14 +65,21 @@ export default function Content({ params }: Props) {
|
||||
img({ height, width, src, alt, className }) {
|
||||
return (
|
||||
<span className="w-full h-max p-20">
|
||||
<Image className={`w-full h-full border-2 px-2 ${className || ""}`} alt={alt!} height={Number(height) || imgSize} width={Number(width) || imgSize} src={`${data.slug}${src}`}></Image>
|
||||
<Image
|
||||
className={`w-full h-full border-2 px-2 ${className || ""}`}
|
||||
alt={alt!}
|
||||
height={Number(height) || imgSize}
|
||||
width={Number(width) || imgSize}
|
||||
src={`${data.slug}${src}`}></Image>
|
||||
</span>
|
||||
)
|
||||
);
|
||||
},
|
||||
a({ href, children, className }) {
|
||||
return (
|
||||
<Link className={className || ""} aria-label={getText(children)} href={href!} target="_blank">{children}</Link>
|
||||
)
|
||||
<Link className={className || ""} aria-label={getText(children)} href={href!} target="_blank">
|
||||
{children}
|
||||
</Link>
|
||||
);
|
||||
},
|
||||
pre({ children, className }) {
|
||||
return (
|
||||
@ -80,26 +87,24 @@ export default function Content({ params }: Props) {
|
||||
<CopyButton text={getText(children)} />
|
||||
<pre className={`${className || ""}`}>{children}</pre>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
},
|
||||
code({ children, ref, className, node, ...rest }) {
|
||||
const match = /language-(\w+)/.exec(className || '')
|
||||
const match = /language-(\w+)/.exec(className || "");
|
||||
return match ? (
|
||||
<SyntaxHighlighter
|
||||
{...rest}
|
||||
language={match[1]}
|
||||
style={codeStyle}
|
||||
>{getText(children)}</SyntaxHighlighter>
|
||||
<SyntaxHighlighter {...rest} language={match[1]} style={codeStyle}>
|
||||
{getText(children)}
|
||||
</SyntaxHighlighter>
|
||||
) : (
|
||||
<code {...rest} className={`${className} text-orange-400 font-black font-mono`}>
|
||||
{children}
|
||||
</code>
|
||||
)
|
||||
}
|
||||
}}
|
||||
>
|
||||
);
|
||||
},
|
||||
}}>
|
||||
{content}
|
||||
</Markdown>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="w-full h-full p-4 lg:p-20 overflow-x-hidden overflow-scroll">
|
||||
@ -109,9 +114,7 @@ export default function Content({ params }: Props) {
|
||||
{role()}
|
||||
{date()}
|
||||
</div>
|
||||
<div className="w-full m-auto lg:w-3/4 mt-10">
|
||||
{ctnt()}
|
||||
</div>
|
||||
<div className="w-full m-auto lg:w-3/4 mt-10">{ctnt()}</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -2,12 +2,15 @@ import { faEnvelope } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
const email = "ivan@idimitrov.dev";
|
||||
const mailto = `mailto:${email}`
|
||||
const Contact = () =>
|
||||
const mailto = `mailto:${email}`;
|
||||
const Contact = () => (
|
||||
<div className="w-full h-full p-2 grid place-content-center">
|
||||
<div className="flex flex-row gap-4">
|
||||
<a aria-label={mailto} href={mailto}><FontAwesomeIcon className="w-14 h-14" icon={faEnvelope} /></a>
|
||||
<a aria-label={mailto} href={mailto}>
|
||||
<FontAwesomeIcon className="w-14 h-14" icon={faEnvelope} />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default Contact
|
||||
export default Contact;
|
||||
|
@ -4,29 +4,28 @@
|
||||
|
||||
@layer components {
|
||||
.btn {
|
||||
@apply bg-gray-900 transition ease-in duration-200 text-gray-300 hover:bg-gray-700/60 rounded-md px-3 py-2 text-sm
|
||||
@apply bg-gray-900 transition ease-in duration-200 text-gray-300 hover:bg-gray-700/60 rounded-md px-3 py-2 text-sm;
|
||||
}
|
||||
}
|
||||
|
||||
@layer utilities {
|
||||
.gradient {
|
||||
@apply bg-gradient-to-br from-slate-950 via-red-700 to-yellow-400
|
||||
@apply bg-gradient-to-br from-slate-950 via-red-700 to-yellow-400;
|
||||
}
|
||||
}
|
||||
|
||||
html * {
|
||||
@apply scrollbar scrollbar-thin scrollbar-thumb-sky-400
|
||||
@apply scrollbar scrollbar-thin scrollbar-thumb-sky-400;
|
||||
}
|
||||
|
||||
body {
|
||||
@apply gradient text-slate-50 w-screen h-screen overflow-hidden
|
||||
@apply gradient text-slate-50 w-screen h-screen overflow-hidden;
|
||||
}
|
||||
|
||||
main {
|
||||
@apply flex flex-col w-full h-full bg-slate-950/95
|
||||
@apply flex flex-col w-full h-full bg-slate-950/95;
|
||||
}
|
||||
|
||||
svg {
|
||||
@apply hover:text-sky-400 transition duration-200 ease-in
|
||||
@apply hover:text-sky-400 transition duration-200 ease-in;
|
||||
}
|
||||
|
||||
|
@ -1,17 +1,13 @@
|
||||
import './globals.css'
|
||||
import Navbar from '$components/navbar'
|
||||
import type { Metadata } from 'next'
|
||||
import "./globals.css";
|
||||
import Navbar from "$components/navbar";
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Ivan Dimitrov",
|
||||
description: "Freelance Software Developer",
|
||||
}
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body>
|
||||
@ -21,5 +17,5 @@ export default function RootLayout({
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -5,18 +5,10 @@ import Link from "next/link";
|
||||
const Home = () => (
|
||||
<div className="grid w-full h-full place-content-center">
|
||||
<div className={"grid grid-cols-2 gap-4 place-content-center"}>
|
||||
<Link
|
||||
aria-label="GitHub"
|
||||
href={process.env.NEXT_PUBLIC_GITHUB_URL!}
|
||||
target="_blank"
|
||||
>
|
||||
<Link aria-label="GitHub" href={process.env.NEXT_PUBLIC_GITHUB_URL!} target="_blank">
|
||||
<FontAwesomeIcon className="w-14 h-14" icon={faGithub} />
|
||||
</Link>
|
||||
<Link
|
||||
aria-label="GitLab"
|
||||
href={process.env.NEXT_PUBLIC_GITLAB_URL!}
|
||||
target="_blank"
|
||||
>
|
||||
<Link aria-label="GitLab" href={process.env.NEXT_PUBLIC_GITLAB_URL!} target="_blank">
|
||||
<FontAwesomeIcon className="w-14 h-14" icon={faGitlab} />
|
||||
</Link>
|
||||
</div>
|
||||
|
@ -1,28 +1,29 @@
|
||||
"use client"
|
||||
import { faCheck, faCopy } from "@fortawesome/free-solid-svg-icons"
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
|
||||
import { useState } from "react"
|
||||
"use client";
|
||||
import { faCheck, faCopy } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { useState } from "react";
|
||||
|
||||
type Props = {
|
||||
text: string
|
||||
}
|
||||
text: string;
|
||||
};
|
||||
|
||||
const CopyButton = ({ text }: Props) => {
|
||||
const [visible, setVisible] = useState("invisible")
|
||||
const [visible, setVisible] = useState("invisible");
|
||||
return (
|
||||
<button
|
||||
className="absolute top-5 right-5"
|
||||
aria-label="copy"
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(text)
|
||||
setVisible("visible")
|
||||
setTimeout(() => setVisible("invisible"), 1000)
|
||||
}}
|
||||
>
|
||||
<span className={`${visible} absolute bottom-5 left-5`}><FontAwesomeIcon className="text-green-400" icon={faCheck} /></span>
|
||||
navigator.clipboard.writeText(text);
|
||||
setVisible("visible");
|
||||
setTimeout(() => setVisible("invisible"), 1000);
|
||||
}}>
|
||||
<span className={`${visible} absolute bottom-5 left-5`}>
|
||||
<FontAwesomeIcon className="text-green-400" icon={faCheck} />
|
||||
</span>
|
||||
<FontAwesomeIcon icon={faCopy} />
|
||||
</button>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export default CopyButton;
|
||||
|
@ -1,9 +1,9 @@
|
||||
"use client"
|
||||
"use client";
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
|
||||
const Navbar = () => {
|
||||
const path = usePathname()
|
||||
const path = usePathname();
|
||||
const link = (text: string, href: string) => {
|
||||
return (
|
||||
<Link className="gradient w-full h-max rounded-md border-2" aria-label={text} href={href}>
|
||||
@ -11,8 +11,8 @@ const Navbar = () => {
|
||||
{text}
|
||||
</div>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
return (
|
||||
<div className="w-max h-max px-6 py-2 mx-auto rounded-full bg-slate-900 grid place-content-center">
|
||||
<div className="flex flex-row gap-6">
|
||||
@ -21,8 +21,7 @@ const Navbar = () => {
|
||||
{link("Contact", "/contact")}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
export default Navbar
|
||||
export default Navbar;
|
||||
|
@ -2,18 +2,18 @@ import fs from "fs";
|
||||
import matter, { GrayMatterFile } from "gray-matter";
|
||||
import path from "path";
|
||||
|
||||
export const baseDir = "./_content/"
|
||||
export const baseDir = "./_content/";
|
||||
|
||||
export const getContent = (slug: string[]): GrayMatterFile<string> => {
|
||||
let p = path.join(baseDir)
|
||||
let p = path.join(baseDir);
|
||||
slug.forEach(s => {
|
||||
p = path.join(p, s)
|
||||
})
|
||||
const file = fs.readFileSync(p, "utf8")
|
||||
p = path.join(p, s);
|
||||
});
|
||||
const file = fs.readFileSync(p, "utf8");
|
||||
const m = matter(file);
|
||||
m.data.slug = `/c/${slug.join("/")}`
|
||||
return m
|
||||
}
|
||||
m.data.slug = `/c/${slug.join("/")}`;
|
||||
return m;
|
||||
};
|
||||
|
||||
const getAllPathsRecursive = (base = baseDir): string[] => {
|
||||
let results = [] as string[];
|
||||
@ -23,16 +23,21 @@ const getAllPathsRecursive = (base = baseDir): string[] => {
|
||||
const stat = fs.statSync(filePath);
|
||||
if (stat.isDirectory()) {
|
||||
results = results.concat(getAllPathsRecursive(filePath));
|
||||
} else if (path.extname(filePath) === '.md') {
|
||||
} else if (path.extname(filePath) === ".md") {
|
||||
results.push(filePath);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
};
|
||||
|
||||
export const getAllPaths = (base = baseDir): string[] => getAllPathsRecursive(base).map(p => p.substring(9))
|
||||
export const getAllPaths = (base = baseDir): string[] => getAllPathsRecursive(base).map(p => p.substring(9));
|
||||
|
||||
export const getCases = (): GrayMatterFile<string>[] => getAllPaths(`${baseDir}cases/`).map(s => s.split("/")).map(getContent)
|
||||
|
||||
export const getAllContent = (): GrayMatterFile<string>[] => getAllPaths().map(s => s.split("/")).map(getContent)
|
||||
export const getCases = (): GrayMatterFile<string>[] =>
|
||||
getAllPaths(`${baseDir}cases/`)
|
||||
.map(s => s.split("/"))
|
||||
.map(getContent);
|
||||
|
||||
export const getAllContent = (): GrayMatterFile<string>[] =>
|
||||
getAllPaths()
|
||||
.map(s => s.split("/"))
|
||||
.map(getContent);
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { ReactNode } from "react"
|
||||
import { ReactNode } from "react";
|
||||
|
||||
export const getText = (node: ReactNode | any) => {
|
||||
const props = node.props
|
||||
const props = node.props;
|
||||
if (!props) {
|
||||
return node
|
||||
return node;
|
||||
}
|
||||
const c = props.children || ""
|
||||
return typeof c === "string" ? c : c.map(getText).join("")
|
||||
}
|
||||
const c = props.children || "";
|
||||
return typeof c === "string" ? c : c.map(getText).join("");
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user