diff --git a/.nvmrc b/.nvmrc
index 0828ab794..209e3ef4b 100644
--- a/.nvmrc
+++ b/.nvmrc
@@ -1 +1 @@
-v18
\ No newline at end of file
+20
diff --git a/Dockerfile b/Dockerfile
index f715addb1..c73bacf1c 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM node:18-alpine
+FROM node:20-alpine
COPY . /app
diff --git a/package.json b/package.json
index f6fba8f59..c7603525d 100644
--- a/package.json
+++ b/package.json
@@ -28,7 +28,7 @@
"typescript": "^5.2.2"
},
"engines": {
- "node": ">=18.0.0",
+ "node": ">=20.0.0",
"pnpm": ">=8.0.0"
}
}
diff --git a/packages/blog-starter-kit/themes/enterprise/components/post-toc.tsx b/packages/blog-starter-kit/themes/enterprise/components/post-toc.tsx
index 6bc23b40d..d4fd21422 100644
--- a/packages/blog-starter-kit/themes/enterprise/components/post-toc.tsx
+++ b/packages/blog-starter-kit/themes/enterprise/components/post-toc.tsx
@@ -1,6 +1,12 @@
+import { useState } from 'react';
import Link from 'next/link';
+import { List } from 'react-feather';
+import * as DialogPrimitive from '@radix-ui/react-dialog';
import { PostFullFragment } from '../generated/graphql';
import { useAppContext } from './contexts/appContext';
+import { Button } from './button';
+import CloseSVG from './icons/svgs/CloseSVG';
+import CustomScrollArea from './scroll-area';
type TableOfContentsItem = PostFullFragment['features']['tableOfContents']['items'][number];
@@ -29,9 +35,11 @@ const mapTableOfContentItems = (toc: TableOfContentsItem[]) => {
const Toc = ({
data,
parentId,
+ onNavigate,
}: {
data: TableOfContentsItem[];
parentId: TableOfContentsItem['parentId'];
+ onNavigate?: () => void;
}) => {
const children = data.filter((item) => item.parentId === parentId);
if (children.length === 0) return null;
@@ -41,21 +49,21 @@ const Toc = ({
{item.title}
-
+
))}
);
};
-export const PostTOC = () => {
+const TagsList = () => {
const { post } = useAppContext();
-
if (!post) return null;
const tagsList = post.tags?.map((tag) => (
@@ -69,15 +77,81 @@ export const PostTOC = () => {
));
+ return (
+
+ );
+};
+
+export const PostTOC = () => {
+ const { post } = useAppContext();
+
+ if (!post) return null;
+
return (
);
};
+
+export const PostTOCMobile = () => {
+ const { post } = useAppContext();
+ const [isOpen, setIsOpen] = useState(false);
+
+ if (!post) return null;
+
+ const closeModal = () => setIsOpen(false);
+
+ return (
+
+
}
+ onClick={() => setIsOpen(true)}
+ />
+ {isOpen && (
+
+
+
+
+
+ On this page
+
+
+ }
+ className="rounded-xl !border-transparent !px-3 !py-2 hover:bg-neutral-800 dark:text-white"
+ onClick={closeModal}
+ />
+
+
+
+
+
+
+
+
+
+ )}
+
+ );
+};
diff --git a/packages/blog-starter-kit/themes/enterprise/pages/[slug].tsx b/packages/blog-starter-kit/themes/enterprise/pages/[slug].tsx
index 39b68da84..7133d245d 100644
--- a/packages/blog-starter-kit/themes/enterprise/pages/[slug].tsx
+++ b/packages/blog-starter-kit/themes/enterprise/pages/[slug].tsx
@@ -29,7 +29,7 @@ import { useEffect, useState } from 'react';
import { resizeImage } from '@starter-kit/utils/image';
import { triggerCustomWidgetEmbed } from '@starter-kit/utils/trigger-custom-widget-embed';
import { CoverImage } from '../components/cover-image';
-import { PostTOC } from '../components/post-toc';
+import { PostTOC, PostTOCMobile } from '../components/post-toc';
const AboutAuthor = dynamic(() => import('../components/about-author'), { ssr: false });
const Subscribe = dynamic(() => import('../components/subscribe').then((mod) => mod.Subscribe));
@@ -136,6 +136,12 @@ const Post = ({ publication, post }: PostProps) => {
author={post.author}
readTimeInMinutes={post.readTimeInMinutes}
/>
+ {post.features.tableOfContents.isEnabled &&
+ post.features?.tableOfContents?.items?.length > 0 && (
+
+ )}