|
1 | | -import React, { useEffect, useState } from "react"; |
| 1 | +import React, { useEffect, useState, useMemo } from "react"; |
| 2 | +import { graphql, useStaticQuery } from "gatsby"; |
2 | 3 |
|
3 | | -import { content } from "./content"; |
4 | 4 | import Button from "../../reusecore/Button"; |
5 | 5 | import PaginationWrapper from "./pagination.style"; |
6 | 6 |
|
| 7 | +const STABLE_ROUTES = [ |
| 8 | + // Getting Started |
| 9 | + { link: "/projects/sistent/getting-started/about", text: "About" }, |
| 10 | + { link: "/projects/sistent/getting-started/installation", text: "Installation" }, |
| 11 | + { link: "/projects/sistent/getting-started/usage", text: "Usage" }, |
| 12 | + { link: "/projects/sistent/getting-started/tokens", text: "Tokens" }, |
| 13 | + |
| 14 | + // Identity |
| 15 | + { link: "/projects/sistent/identity/color", text: "Colors" }, |
| 16 | + { link: "/projects/sistent/identity/color/guidance", text: "Colors" }, |
| 17 | + { link: "/projects/sistent/identity/color/code", text: "Colors" }, |
| 18 | + { link: "/projects/sistent/identity/spacing", text: "Spacing" }, |
| 19 | + { link: "/projects/sistent/identity/spacing/guidance", text: "Spacing" }, |
| 20 | + { link: "/projects/sistent/identity/spacing/code", text: "Spacing" }, |
| 21 | + { link: "/projects/sistent/identity/typography", text: "Typography" }, |
| 22 | + { link: "/projects/sistent/identity/typography/guidance", text: "Typography" }, |
| 23 | + { link: "/projects/sistent/identity/typography/code", text: "Typography" }, |
| 24 | +]; |
| 25 | + |
| 26 | +const PAGE_TYPE_ORDER = { |
| 27 | + overview: 1, |
| 28 | + guidance: 2, |
| 29 | + code: 3, |
| 30 | +}; |
| 31 | + |
7 | 32 | const SistentPagination = () => { |
8 | 33 | const [currentPage, setCurrentPage] = useState(0); |
9 | 34 |
|
| 35 | + const data = useStaticQuery(graphql` |
| 36 | + query SistentPaginationNav { |
| 37 | + allMdx( |
| 38 | + filter: { |
| 39 | + fields: { collection: { eq: "sistent" } } |
| 40 | + } |
| 41 | + ) { |
| 42 | + nodes { |
| 43 | + frontmatter { |
| 44 | + name |
| 45 | + component |
| 46 | + published |
| 47 | + } |
| 48 | + fields { |
| 49 | + slug |
| 50 | + pageType |
| 51 | + } |
| 52 | + } |
| 53 | + } |
| 54 | + } |
| 55 | + `); |
| 56 | + |
| 57 | + // Published components based on overview pages |
| 58 | + const publishedComponents = useMemo(() => { |
| 59 | + const published = new Set(); |
| 60 | + data.allMdx.nodes.forEach((node) => { |
| 61 | + if (node.fields.pageType === "overview" && node.frontmatter.published === true) { |
| 62 | + published.add(node.frontmatter.component); |
| 63 | + } |
| 64 | + }); |
| 65 | + return published; |
| 66 | + }, [data.allMdx.nodes]); |
| 67 | + |
| 68 | + // Map, filter, and sort dynamic routes |
| 69 | + const dynamicRoutes = useMemo(() => { |
| 70 | + return data.allMdx.nodes |
| 71 | + .map((node) => ({ |
| 72 | + componentSlug: node.frontmatter.component, |
| 73 | + text: node.frontmatter.name || node.frontmatter.component, |
| 74 | + link: node.fields.slug, |
| 75 | + pageType: node.fields.pageType, |
| 76 | + })) |
| 77 | + .filter((node) => publishedComponents.has(node.componentSlug)) |
| 78 | + .sort((a, b) => { |
| 79 | + if (a.componentSlug !== b.componentSlug) { |
| 80 | + return (a.componentSlug || "").localeCompare(b.componentSlug || ""); |
| 81 | + } |
| 82 | + return ( |
| 83 | + (PAGE_TYPE_ORDER[a.pageType] || 99) - (PAGE_TYPE_ORDER[b.pageType] || 99) |
| 84 | + ); |
| 85 | + }); |
| 86 | + }, [data.allMdx.nodes, publishedComponents]); |
| 87 | + |
| 88 | + // Combine and de-duplicate routes by link |
| 89 | + const fullContentArray = useMemo(() => { |
| 90 | + const combined = [...STABLE_ROUTES, ...dynamicRoutes]; |
| 91 | + const seenLinks = new Set(); |
| 92 | + return combined.filter((route) => { |
| 93 | + if (seenLinks.has(route.link)) return false; |
| 94 | + seenLinks.add(route.link); |
| 95 | + return true; |
| 96 | + }); |
| 97 | + }, [dynamicRoutes]); |
| 98 | + |
10 | 99 | useEffect(() => { |
11 | 100 | const path = window.location.pathname; |
12 | | - const index = content.findIndex((x) => x.link === path); |
| 101 | + // Handle trailing slashes |
| 102 | + const cleanPath = path.endsWith("/") && path.length > 1 ? path.slice(0, -1) : path; |
| 103 | + const index = fullContentArray.findIndex((x) => x.link === cleanPath); |
13 | 104 | setCurrentPage(index); |
14 | | - }, []); |
| 105 | + }, [fullContentArray]); |
15 | 106 |
|
16 | 107 | return ( |
17 | 108 | <PaginationWrapper> |
18 | 109 | {currentPage > 0 ? ( |
19 | | - <Button $secondary $url={content[currentPage - 1]?.link}> |
| 110 | + <Button $secondary $url={fullContentArray[currentPage - 1]?.link}> |
20 | 111 | ← Previous |
21 | 112 | </Button> |
22 | 113 | ) : null} |
23 | 114 |
|
24 | | - {currentPage < content.length - 1 ? ( |
25 | | - <Button $primary $url={content[currentPage + 1]?.link}> |
| 115 | + {currentPage !== -1 && currentPage < fullContentArray.length - 1 ? ( |
| 116 | + <Button $primary $url={fullContentArray[currentPage + 1]?.link}> |
26 | 117 | Next → |
27 | 118 | </Button> |
28 | 119 | ) : null} |
|
0 commit comments