From f47bf798d37ab7bff169a097e8e20fd65f6b0790 Mon Sep 17 00:00:00 2001 From: Arik Chakma Date: Thu, 7 Nov 2024 18:01:18 +0600 Subject: [PATCH] feat: related guides sidebar (#7682) * feat: related guides sidebar * fix: hide related guides on mobile --- .gitignore | 1 + src/components/Guide/GuideContent.astro | 11 +++- src/components/Guide/RelatedGuides.tsx | 74 ++++++++++++++++++++++++ src/lib/guide.ts | 8 +++ src/lib/markdown.ts | 2 +- src/pages/frontend/job-description.astro | 8 +-- 6 files changed, 98 insertions(+), 6 deletions(-) create mode 100644 src/components/Guide/RelatedGuides.tsx diff --git a/.gitignore b/.gitignore index af5f0247e..81d7bf8f8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .idea .temp +.astro # build output dist/ diff --git a/src/components/Guide/GuideContent.astro b/src/components/Guide/GuideContent.astro index 3eeb4db1e..ed51c19a8 100644 --- a/src/components/Guide/GuideContent.astro +++ b/src/components/Guide/GuideContent.astro @@ -3,6 +3,7 @@ import { getGuideTableOfContent, type GuideFileType } from '../../lib/guide'; import MarkdownFile from '../MarkdownFile.astro'; import { TableOfContent } from '../TableOfContent/TableOfContent'; import { replaceVariables } from '../../lib/markdown'; +import { RelatedGuides } from './RelatedGuides'; interface Props { guide: GuideFileType; @@ -14,13 +15,21 @@ const allHeadings = guide.getHeadings(); const tableOfContent = getGuideTableOfContent(allHeadings); const showTableOfContent = tableOfContent.length > 0; +const showRelatedGuides = + guide?.frontmatter?.relatedGuides && + Object.keys(guide?.frontmatter?.relatedGuides).length > 0; const { frontmatter: guideFrontmatter, author } = guide; ---
{ - showTableOfContent && ( + (showTableOfContent || showRelatedGuides) && (
+
) diff --git a/src/components/Guide/RelatedGuides.tsx b/src/components/Guide/RelatedGuides.tsx new file mode 100644 index 000000000..72dc858b6 --- /dev/null +++ b/src/components/Guide/RelatedGuides.tsx @@ -0,0 +1,74 @@ +import { useState } from 'react'; +import { cn } from '../../lib/classname'; +import { ChevronDown } from 'lucide-react'; + +type RelatedGuidesProps = { + relatedTitle?: string; + relatedGuides: Record; +}; + +export function RelatedGuides(props: RelatedGuidesProps) { + const { relatedTitle = 'Other Guides', relatedGuides } = props; + + const [isOpen, setIsOpen] = useState(false); + + const relatedGuidesArray = Object.entries(relatedGuides).map( + ([title, url]) => ({ + title, + url, + }), + ); + + if (relatedGuidesArray.length === 0) { + return null; + } + + return ( +
+

{relatedTitle}

+ + +
    + {relatedGuidesArray.map((relatedGuide) => ( +
  1. + { + if (!isOpen) { + return; + } + + setIsOpen(false); + }} + > + {relatedGuide.title} + +
  2. + ))} +
+
+ ); +} diff --git a/src/lib/guide.ts b/src/lib/guide.ts index a83b1aa84..809b02950 100644 --- a/src/lib/guide.ts +++ b/src/lib/guide.ts @@ -20,6 +20,8 @@ export interface GuideFrontmatter { priority: number; changefreq: 'daily' | 'weekly' | 'monthly' | 'yearly'; }; + relatedTitle?: string; + relatedGuides?: Record; tags: string[]; } @@ -116,5 +118,11 @@ export function getGuideTableOfContent(headings: HeadingType[]) { } }); + if (tableOfContents.length > 5) { + tableOfContents.forEach((group) => { + group.children = []; + }); + } + return tableOfContents; } diff --git a/src/lib/markdown.ts b/src/lib/markdown.ts index af0a1c2ae..3cc2a7f84 100644 --- a/src/lib/markdown.ts +++ b/src/lib/markdown.ts @@ -11,7 +11,7 @@ export function replaceVariables( currentYear: new Date().getFullYear().toString(), }; - return markdown.replace(/@([^@]+)@/g, (match, p1) => { + return markdown?.replace(/@([^@]+)@/g, (match, p1) => { return allVariables[p1] || match; }); } diff --git a/src/pages/frontend/job-description.astro b/src/pages/frontend/job-description.astro index c4ab5fb27..7022e25a5 100644 --- a/src/pages/frontend/job-description.astro +++ b/src/pages/frontend/job-description.astro @@ -12,7 +12,7 @@ const guide = await getGuideById(guideId); const { frontmatter: guideData } = guide!; const ogImageUrl = - guideData.seo.ogImageUrl || + guideData.seo?.ogImageUrl || getOpenGraphImageUrl({ group: 'guide', resourceId: guideId, @@ -20,9 +20,9 @@ const ogImageUrl = ---