Skip to content

Commit

Permalink
feat(snippets): ✨ add snippets page (#105)
Browse files Browse the repository at this point in the history
  • Loading branch information
sozonome authored Jul 22, 2022
1 parent 916a7b1 commit f0c3e50
Show file tree
Hide file tree
Showing 18 changed files with 392 additions and 5 deletions.
1 change: 1 addition & 0 deletions commitlint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const CommitLintConfiguration = {
"projects",
"seo",
"services",
"snippets",
"static",
"theme",
"utils",
Expand Down
40 changes: 40 additions & 0 deletions content/snippets/custom-scroll-bar.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
title: Custom Scrollbar
description: define your own custom scroll bar
published: false
date: 2022-07-22
stacks:
- css
- chakra-ui
---

## CSS

```css
::-webkit-scrollbar {
width: 0.75rem;
height: 0.75rem;
background-color: blue;
}

::-webkit-scrollbar-thumb {
border-radius: 20px;
background-color: gray;
}

/** firefox **/
html {
scrollbar-width: thin;
scrollbar-color: blue;
}
```

## References

- MDN
- [https://developer.mozilla.org/en-US/docs/Web/CSS/::-webkit-scrollbar#browser_compatibility](https://developer.mozilla.org/en-US/docs/Web/CSS/::-webkit-scrollbar#browser_compatibility)
- [https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Scrollbars#browser_compatibility](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Scrollbars#browser_compatibility)
- W3Schools: [https://www.w3schools.com/howto/howto_css_custom_scrollbar.asp](https://www.w3schools.com/howto/howto_css_custom_scrollbar.asp)
- [sznm.dev](http://sznm.dev) - chakra-ui implementation
- [https://github.com/sozonome/sznm.dev/commit/f967221e40c7d680eb25ca4944ede4f5def2b628](https://github.com/sozonome/sznm.dev/commit/f967221e40c7d680eb25ca4944ede4f5def2b628)
- [https://github.com/sozonome/sznm.dev/commit/76c3ce6895b6de5b0a0d4195378cf68cde269054](https://github.com/sozonome/sznm.dev/commit/76c3ce6895b6de5b0a0d4195378cf68cde269054)
34 changes: 34 additions & 0 deletions content/snippets/overflow-scroll-without-scrollbar.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
title: Overflow Scroll without Scrollbar
description: for sleek overflow scroll in mobile viewport
published: false
date: 2022-07-22
stacks:
- css
- chakra-ui
---

## CSS

```css
.some-component {
overflow-x: scroll; /* or overflow-y */
}

.some-component::-webkit-scrollbar {
display: none;
}
```

## Chakra-UI

```jsx
<Flex overflow="scroll" css={{ "&::-webkit-scrollbar": { display: "none" } }}>
...some children
</Flex>
```

## References

- [https://stackoverflow.com/questions/65042380/how-to-add-webkit-scrollbar-pseudo-element-in-chakra-ui-element-react](https://stackoverflow.com/questions/65042380/how-to-add-webkit-scrollbar-pseudo-element-in-chakra-ui-element-react)
- [https://react.geist-ui.dev/en-us/components/tabs](https://react.geist-ui.dev/en-us/components/tabs)
22 changes: 21 additions & 1 deletion contentlayer.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,29 @@ const Project = defineDocumentType(() => ({
},
}));

const Snippet = defineDocumentType(() => ({
name: "Snippet",
filePathPattern: "snippets/*.md",
fields: {
title: { type: "string", required: true },
description: { type: "string", required: true },
published: { type: "boolean" },
date: { type: "string" },
stacks: { type: "list", of: { type: "string" } },
},
computedFields: {
id: {
type: "string",
resolve: (snippet) =>
// eslint-disable-next-line no-underscore-dangle
snippet._raw.sourceFileName.replace(/\.md$|\.mdx$/, ""),
},
},
}));

const contentLayerConfig = makeSource({
contentDirPath: "content",
documentTypes: [Blog, Project],
documentTypes: [Blog, Project, Snippet],
markdown: {
remarkPlugins: [remarkHtml],
rehypePlugins: [rehypeRaw],
Expand Down
2 changes: 0 additions & 2 deletions src/lib/components/blog/renderers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,3 @@ export const renderers: Options["components"] = {
h5: ({ children }) => <HeadingLink as="h5">{String(children)}</HeadingLink>,
h6: ({ children }) => <HeadingLink as="h6">{String(children)}</HeadingLink>,
};

export default renderers;
56 changes: 56 additions & 0 deletions src/lib/components/snippets/SnippetCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { Flex, Grid, Heading, Text } from "@chakra-ui/react";
import type { Snippet } from "contentlayer/generated";
import Link from "next/link";

import { trackEventToUmami } from "lib/utils/trackEvent";

type SnippetCardProps = {
data: Snippet;
};

const SnippetCard = ({ data }: SnippetCardProps) => {
const handleClickSnippet = () => {
trackEventToUmami({
eventValue: `Snippet: ${data.title}`,
eventType: "navigate",
});
};

return (
<Link href={`/snippets/${data.id}`} passHref>
<Flex
direction="column"
as="a"
gap={4}
padding={8}
height="full"
borderWidth={2}
borderRadius={24}
onClick={handleClickSnippet}
>
<Grid gap={2}>
<Heading size="md">{data.title}</Heading>
<Text fontSize="sm">{data.description}</Text>
</Grid>

<Flex gap={2}>
{data.stacks?.map((stack) => (
<Text
borderWidth={1}
paddingY={0.5}
paddingX={2}
borderRadius={12}
fontSize="xs"
color="teal.500"
key={stack}
>
{stack}
</Text>
))}
</Flex>
</Flex>
</Link>
);
};

export default SnippetCard;
19 changes: 19 additions & 0 deletions src/lib/components/snippets/detail/Head.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Grid, Heading, Text } from "@chakra-ui/react";
import type { Snippet } from "contentlayer/generated";

type SnippetDetailHeadProps = {
data: Snippet;
};

const SnippetDetailHead = ({ data }: SnippetDetailHeadProps) => {
return (
<Grid gap={2}>
<Heading as="h1" size={{ base: "2xl", sm: "3xl", md: "4xl" }}>
{data.title}
</Heading>
<Text color="gray.500">{data.description}</Text>
</Grid>
);
};

export default SnippetDetailHead;
35 changes: 35 additions & 0 deletions src/lib/components/snippets/detail/Meta.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import type { Snippet } from "contentlayer/generated";
import { NextSeo } from "next-seo";

import { baseUrl } from "lib/constants/baseUrl";
import { sznmOgImage } from "lib/utils/sznmOgImage";

type SnippetDetailMetaProps = {
data: Snippet;
};

const SnippetDetailMeta = ({ data }: SnippetDetailMetaProps) => {
const ogImage = sznmOgImage({
heading: data.title,
text: "Snippets | https://sznm.dev",
});
const pageUrl = `${baseUrl}/snippets/${data.id}`;

return (
<NextSeo
title={data.title}
canonical={pageUrl}
openGraph={{
title: `sozonome | ${data.title}`,
images: [
{
url: ogImage,
alt: `${data.title} og-image`,
},
],
}}
/>
);
};

export default SnippetDetailMeta;
9 changes: 7 additions & 2 deletions src/lib/layout/Navigation.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { IconButton } from "@chakra-ui/react";
import Link from "next/link";
import type { IconType } from "react-icons";
import { FaFeatherAlt, FaHome, FaRocket, FaUser } from "react-icons/fa";
import { FaCode, FaFeatherAlt, FaHome, FaRocket, FaUser } from "react-icons/fa";

import { trackEventToUmami } from "lib/utils/trackEvent";

Expand All @@ -26,7 +26,7 @@ const NavItem = ({ href, label, icon }: NavItemProps) => {
aria-label={label}
variant="ghost"
flexBasis="25%"
fontSize={["2xl", "md"]}
fontSize={["xl", "md"]}
padding={0}
onClick={handleClickNavigation}
>
Expand All @@ -52,6 +52,11 @@ const navigations: NavItemProps[] = [
label: "Blog",
icon: FaFeatherAlt,
},
{
href: "/snippets",
label: "Snippets",
icon: FaCode,
},
{
href: "/about",
label: "About",
Expand Down
26 changes: 26 additions & 0 deletions src/lib/pages/snippets/detail/Snippet.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
.content {
p {
margin: 0.5rem 0 2rem;
}
a {
font-weight: 550;
text-decoration: underline;
}
img {
border-radius: 1rem;
margin: 1rem 0;
}
ul,
ol {
margin: 0 1.5rem;
li {
margin: 0.6rem 0;
}
}
code {
background-color: rgb(200, 200, 200);
border-radius: 0.25rem;
padding: 0 0.25rem;
color: black;
}
}
46 changes: 46 additions & 0 deletions src/lib/pages/snippets/detail/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Box, Spacer, useColorModeValue } from "@chakra-ui/react";
import type { GiscusProps } from "@giscus/react";
import Giscus from "@giscus/react";
import ReactMarkdown from "react-markdown";
import rehypeRaw from "rehype-raw";

import { renderers } from "lib/components/blog/renderers";
import SnippetDetailHead from "lib/components/snippets/detail/Head";
import SnippetDetailMeta from "lib/components/snippets/detail/Meta";

import styles from "./Snippet.module.scss";
import type { SnippetDetailProps } from "./types";

const SnippetDetail = ({ data }: SnippetDetailProps) => {
const giscusTheme: GiscusProps["theme"] = useColorModeValue("light", "dark");

return (
<Box as="article">
<SnippetDetailMeta data={data} />
<SnippetDetailHead data={data} />
<Spacer height={16} />
<ReactMarkdown
className={styles.content}
rehypePlugins={[rehypeRaw]}
components={renderers}
>
{data.body.raw}
</ReactMarkdown>

<Box marginY={12}>
<Giscus
repo="sozonome/sznm.dev"
repoId="MDEwOlJlcG9zaXRvcnkyNjY2Njk3MDg="
mapping="pathname"
category="Snippets"
categoryId="DIC_kwDOD-UOjM4CQX_7"
reactionsEnabled="1"
theme={giscusTheme}
emitMetadata="0"
/>
</Box>
</Box>
);
};

export default SnippetDetail;
30 changes: 30 additions & 0 deletions src/lib/pages/snippets/detail/loader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import type { Snippet } from "contentlayer/generated";
import { allSnippets } from "contentlayer/generated";
import type { GetStaticProps } from "next";

import type { SnippetDetailParams, SnippetDetailProps } from "./types";

export const getStaticPaths = async () => {
const paths = allSnippets.map((project) => ({
params: {
id: project.id,
},
}));
return {
paths,
fallback: false,
};
};

export const getStaticProps: GetStaticProps<
SnippetDetailProps,
SnippetDetailParams
> = async ({ params }) => {
const data = allSnippets.find(
({ id }) => id === (params?.id as string)
) as Snippet;

return {
props: { data },
};
};
9 changes: 9 additions & 0 deletions src/lib/pages/snippets/detail/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { Snippet } from "contentlayer/generated";

export type SnippetDetailParams = {
id: string;
};

export type SnippetDetailProps = {
data: Snippet;
};
Loading

1 comment on commit f0c3e50

@vercel
Copy link

@vercel vercel bot commented on f0c3e50 Jul 22, 2022

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.