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