From a873ce50ce27f584e7eb8c2ef489679b9d0d1813 Mon Sep 17 00:00:00 2001 From: Arek Nawo Date: Mon, 3 Jun 2024 08:49:35 +0200 Subject: [PATCH] feat: Vrite blog --- apps/landing-page/src/pages/blog/[slug].astro | 54 ++- apps/landing-page/src/pages/blog/index.astro | 156 ++++---- apps/landing-page/src/styles/base.css | 51 +++ apps/landing-page/src/styles/blog.scss | 352 ++++++++++++++++++ 4 files changed, 521 insertions(+), 92 deletions(-) create mode 100644 apps/landing-page/src/styles/blog.scss diff --git a/apps/landing-page/src/pages/blog/[slug].astro b/apps/landing-page/src/pages/blog/[slug].astro index a5ab2300..bff9ea2e 100644 --- a/apps/landing-page/src/pages/blog/[slug].astro +++ b/apps/landing-page/src/pages/blog/[slug].astro @@ -1,20 +1,19 @@ --- import { Header, BaseHead, Footer } from "#components/fragments"; -import { IconButton, Card, Button } from "#components/primitives"; -import { mdiCalendar } from "@mdi/js"; import { format } from "date-fns"; import { Content, ContentPiece, getStaticPaths } from "virtual:vrite"; import { convert } from "html-to-text"; +import "#styles/blog.scss"; type Props = ContentPiece; -const { title, description, date, coverUrl, members } = Astro.props; +const { title, description, date, coverUrl } = Astro.props; export const prerender = true; export { getStaticPaths }; --- - + - +
-
- - + +
+
+
+ Blog post cover +
+
- - { - members.map((member) => { - return ( - - ); - }) - } + Arek Nawo +
+ + {format(date ? new Date(date) : new Date(), "dd MMM yyyy")} +
-
- {title} +
+

{title}

+
diff --git a/apps/landing-page/src/pages/blog/index.astro b/apps/landing-page/src/pages/blog/index.astro index bec9316e..1c7bdb7f 100644 --- a/apps/landing-page/src/pages/blog/index.astro +++ b/apps/landing-page/src/pages/blog/index.astro @@ -6,13 +6,22 @@ import "@fontsource/nunito/600.css"; import "@fontsource/nunito/700.css"; import "@fontsource/nunito/800.css"; import "@fontsource/nunito/900.css"; -import { Button, Card, IconButton } from "#components/primitives"; -import { mdiCalendar } from "@mdi/js"; -import { format } from "date-fns"; import { BaseHead, Header, Footer } from "#components/fragments"; -import { getContentPieces } from "virtual:vrite"; +import { getContentPieces, client } from "virtual:vrite"; +import { format } from "date-fns"; const contentPieces = await getContentPieces({ limit: "all" }); +const tags = await client.tags.list({ perPage: 100 }); +const workspace = await client.workspace.get(); +const contentPiecesByMonth = contentPieces.reduce((acc, contentPiece) => { + const date = new Date(contentPiece.date); + const month = format(date, "MMMM yyyy"); + if (!acc[month]) { + acc[month] = []; + } + acc[month].push(contentPiece); + return acc; +}, {}); const title = "Vrite - developer content platform"; const description = "Open-Source, collaborative developer content platform for documentation, technical blogs, and more."; @@ -20,76 +29,97 @@ const description = export const prerender = true; --- - +
+
+
+
-
-
-

Blog

-
- -
- { - contentPieces.map((contentPiece) => ( - - -
- - - -
-
- <> - - -
-

- {contentPiece.title} -

-
- {contentPiece.members.map((member) => { - return ( - - ); - })} -
-
-
-
-
- )) - } +
+
+
+

+ {workspace.name} +

+

+ {workspace.description} +

+
+ { + Object.entries(contentPiecesByMonth).map(([month, contentPieces]) => { + return ( + <> +
diff --git a/apps/landing-page/src/styles/base.css b/apps/landing-page/src/styles/base.css index 4d51efd4..50755aa4 100644 --- a/apps/landing-page/src/styles/base.css +++ b/apps/landing-page/src/styles/base.css @@ -157,6 +157,21 @@ h2 { mask-image: radial-gradient(ellipse at center, rgba(0, 0, 0, 1), transparent 90%); animation: uncover 1s ease-out forwards; } +.grid-background-3 { + position: absolute; + width: 175%; + height: 120%; + top: -10%; + left: -65%; + background-image: linear-gradient(#e5e7eb 2px, transparent 0), + linear-gradient(to right, #e5e7eb 2px, transparent 0); + background-size: 24px 24px; + background-position: center center; + mask-position: center center; + mask-repeat: no-repeat; + mask-image: radial-gradient(ellipse at center, rgba(0, 0, 0, 1), transparent 75%); + animation: uncover 1s ease-out forwards; +} @media (prefers-color-scheme: dark) { .grid-background { @@ -167,7 +182,12 @@ h2 { background-image: linear-gradient(#4b5563 2px, transparent 0), linear-gradient(to right, #4b5563 2px, transparent 0); } + .grid-background-3 { + background-image: linear-gradient(#374151 2px, transparent 0), + linear-gradient(to right, #374151 2px, transparent 0); + } } + @keyframes uncover { from { opacity: 0; @@ -235,3 +255,34 @@ h2 { ); } } + +.astro-code { + @apply !rounded-2xl !bg-gray-800 !dark:bg-gray-900 px-4 py-3 leading-6; +} +.astro-code::-webkit-scrollbar { + @apply w-2 h-4 rounded-b-2xl bg-gray-800 dark:bg-gray-900 border; +} +.astro-code::-webkit-scrollbar-thumb { + @apply rounded-2xl bg-gray-700 border-4 border-gray-800 dark:border-gray-900 border-solid; +} +.astro-code::-webkit-scrollbar-track { + @apply mx-2; +} +.prose :where(code):not(:where(pre *, .not-prose, .not-prose *)) { + @apply bg-gray-200 fill-current dark:bg-gray-900 rounded-md px-1 font-mono font-medium py-0.5 text-wrap; +} +.prose :where(code):not(:where(pre *, .not-prose, .not-prose *))::after { + content: ""; +} +.prose :where(code):not(:where(pre *, .not-prose, .not-prose *))::before { + content: ""; +} +.prose :where(img, video):not(:where(.not-prose, .not-prose *)) { + @apply rounded-2xl; +} +.prose :where(figcaption) { + @apply text-center text-gray-500 dark:text-gray-400 text-sm mt-1; +} +.prose img { + margin-bottom: 1em; +} diff --git a/apps/landing-page/src/styles/blog.scss b/apps/landing-page/src/styles/blog.scss new file mode 100644 index 00000000..f8aed1d7 --- /dev/null +++ b/apps/landing-page/src/styles/blog.scss @@ -0,0 +1,352 @@ +@mixin checkbox { + @apply bg-gray-50 border-gray-200 dark:(bg-gray-900 border-gray-700) border-2 rounded-lg h-6 w-6 focus:(ring-0 border-gray-200 dark:border-gray-700) hover:border-primary cursor-pointer disabled:opacity-70 disabled:hover:border-gray-200 disabled:hover:dark:border-gray-700 disabled:cursor-default; + &:checked { + background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e"), + linear-gradient(to top right, var(--un-gradient-stops)); + @apply border-transparent dark:border-transparent; + } +} + +.prose { + p, + ul, + ol, + blockquote { + @apply relative; + } + :where(p, ul, ol, blockquote):hover > .ProseMirror-widget[data-widget="draggableText"] { + @apply opacity-100; + } + // Drag handle + .ProseMirror-widget[data-widget="draggableText"] { + @apply absolute h-full flex justify-center items-start w-10 top-0 -left-10 opacity-0; + padding-top: 5px; + + > svg { + @apply w-full cursor-pointer rounded-full text-gray-500 dark:text-gray-400 h-6 fill-current; + } + } + + > * > .ProseMirror { + @apply pb-48; + } + .tableWrapper { + @apply overflow-x-scroll pb-2 mb-3; + &::-webkit-scrollbar { + @apply w-2 h-2 rounded-lg bg-gray-100 dark:bg-gray-800; + } + &::-webkit-scrollbar-thumb { + @apply bg-gray-200 rounded-lg dark:bg-gray-900; + } + } + table { + overflow: visible; + border-collapse: collapse; + border-spacing: 0; + margin: 0; + table-layout: fixed; + width: 100%; + + tbody { + width: 100%; + } + + tr { + @apply border-2 border-gray-300 dark:border-gray-700; + } + td, + th { + @apply text-left font-500 border-2 border-gray-300 dark:border-gray-700 relative p-2 vertical-top; + min-width: 100px; + border-style: none solid solid none; + word-break: break-all; + } + th { + @apply bg-gray-50 dark:bg-gray-900; + } + tr:first-child { + td, + th { + border-top-style: solid; + } + } + tr { + td, + th { + &:first-child { + border-left-style: solid; + } + } + } + p { + @apply p-0 m-0; + } + .selectedCell:after { + @apply bg-primary; + opacity: 0.1; + content: ""; + left: 0; + right: 0; + top: 0; + bottom: 0; + pointer-events: none; + position: absolute; + z-index: 2; + } + + .column-resize-handle { + @apply bg-primary absolute w-1 -bottom-0.5 -top-0.5 -right-0.75 z-10 pointer-events-none; + } + &.resizing { + .code-block-editor { + @apply hidden; + } + .code-block-placeholder { + @apply flex; + } + } + } + + font-kerning: none; + :where(h1, h2, h3, h4, h5, h6):not(:where(.not-prose, .not-prose *)) { + &::before, + &::before, + &::before, + &::before, + &::before, + &::before, + .open-slash-menu > button { + @apply font-bold dark:bg-gray-900 dark:border-gray-700; + font-family: "JetBrainsMonoVariable", monospace; + position: absolute; + left: -2.5rem; + background: #f9fafb; + height: 2rem; + width: 2rem; + justify-content: center; + display: flex; + align-items: center; + color: #6b7280; + border-radius: 25%; + font-size: 1rem; + line-height: 1rem; + text-align: center; + top: 50%; + transform: translateY(-50%); + //border: 2px solid #e5e7eb; + @apply border-2; + opacity: 1; + } + } + + :where(ul p, ol p):not(:where(.not-prose, .not-prose *)) { + @apply m-0; + } + :where(ul:not([data-type="taskList"]) li > * + *, ol li > * + *):not( + :where(.not-prose, .not-prose *) + ) { + // @apply my-5; + } + :where(li > ul, li > ol, li > div > ul, li > div > ol):not(:where(.not-prose, .not-prose *)) { + //@apply m-0; + } + :where(ul[data-type="taskList"]):not(:where(.not-prose, .not-prose *)) { + padding-left: 0rem; + > li { + padding-left: 0.5rem; + display: flex !important; + justify-content: start; + align-items: start; + min-height: 1.75em; + min-width: 1rem; + label { + display: flex; + align-items: center; + justify-content: center; + margin-right: 0.5rem; + min-height: 1.75em; + input { + @include checkbox; + } + } + > div { + min-width: 1rem; + } + } + } + :where(hr):not(:where(.not-prose, .not-prose *)) { + @apply rounded-full border border-gray-200 dark:border-gray-700; + margin: 1.25rem 0; + &.ProseMirror-selectednode { + @apply border border-primary; + } + } + :where(u) { + text-decoration: underline wavy; + } +} +.prose { + p { + @apply relative; + } + .ProseMirror:not(:where(.not-prose, .not-prose *)) { + > * + div:not(.tableWrapper) { + @apply mb-5; + } + + padding-bottom: 8rem; + } + .tableWrapper { + @apply overflow-x-scroll pb-2 mb-3; + &::-webkit-scrollbar { + @apply w-2 h-2 rounded-lg bg-gray-100 dark:bg-gray-800; + } + &::-webkit-scrollbar-thumb { + @apply bg-gray-200 rounded-lg dark:bg-gray-900; + } + } + + img { + @apply rounded-2xl; + } + table { + overflow: visible; + border-collapse: collapse; + border-spacing: 0; + margin: 0; + table-layout: fixed; + width: 100%; + + tbody { + width: 100%; + } + + tr { + @apply border-2 border-gray-200 dark:border-gray-700; + } + td, + th { + @apply text-left font-500 border-2 border-gray-200 dark:border-gray-700 relative p-2 vertical-top; + min-width: 100px; + border-style: none solid solid none; + word-break: break-all; + } + th { + @apply bg-gray-50 dark:bg-gray-900; + } + tr:first-child { + td, + th { + border-top-style: solid; + } + } + tr { + td, + th { + &:first-child { + border-left-style: solid; + } + } + } + p { + @apply p-0 m-0; + } + .selectedCell:after { + @apply bg-primary; + opacity: 0.1; + content: ""; + left: 0; + right: 0; + top: 0; + bottom: 0; + pointer-events: none; + position: absolute; + z-index: 2; + } + + .column-resize-handle { + @apply bg-primary absolute w-1 -bottom-0.5 -top-0.5 -right-0.75 z-10 pointer-events-none; + } + } + + font-kerning: none; + :where(h1, h2, h3, h4, h5, h6):not(:where(.not-prose, .not-prose *)) { + @apply relative; + margin: 0 !important; + } + + h3:not(:where(.not-prose, .not-prose *))::before { + @apply pr-5 -left-5; + } + + :where(ul p, ol p):not(:where(.not-prose, .not-prose *)) { + @apply m-0; + } + :where(ul:not([data-type="taskList"]) li > * + *, ol li > * + *):not( + :where(.not-prose, .not-prose *) + ) { + @apply my-5; + } + :where(li > ul, li > ol, li > div > ul, li > div > ol):not(:where(.not-prose, .not-prose *)) { + @apply m-0; + } + :where(ul[data-type="taskList"]):not(:where(.not-prose, .not-prose *)) { + padding-left: 0rem; + li { + padding-left: 0.5rem; + display: flex !important; + justify-content: start; + align-items: start; + min-height: 1.75em; + min-width: 1rem; + label { + display: flex; + align-items: center; + justify-content: center; + margin-right: 0.5rem; + min-height: 1.75em; + input { + @include checkbox; + } + } + > div { + min-width: 1rem; + } + } + } + :where(hr):not(:where(.not-prose, .not-prose *)) { + @apply rounded-full border border-gray-200 dark:border-gray-700; + margin: 1.25rem 0; + &.ProseMirror-selectednode { + @apply border border-primary; + } + } +} +.prose { + --un-prose-lists: #6b7280; + --un-prose-hr: #6b7280; + --un-prose-invert-lists: #9ca3af; + --un-prose-invert-hr: #9ca3af; + + > * + * { + margin-top: 0; + } + :where(blockquote):not(:where(.not-prose, .not-prose *)) { + @apply border-l-3 pl-5.5 border-solid border-gray-200 dark:border-gray-700 not-italic text-gray-500 dark:text-gray-400; + } + + :where(mark) { + @apply text-gray-700; + } + :where(span[data-comment]) { + @apply text-primary relative; + + &::after { + content: ""; + @apply bg-primary opacity-20 absolute w-full h-full top-0 left-0 pointer-events-none; + } + } + :where(figure > img) { + @apply w-full; + } +}