@primer/doctocat-nextjs 0.5.0-rc.d795ae7 → 0.5.1-rc.6f568d0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/components/layout/index-cards/IndexCards.tsx +13 -4
- package/components/layout/root-layout/Theme.tsx +33 -29
- package/components/layout/sidebar/Sidebar.tsx +19 -6
- package/components/layout/underline-nav/UnderlineNav.tsx +4 -3
- package/css/prose.module.css +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @primer/doctocat-nextjs
|
|
2
2
|
|
|
3
|
+
## 0.5.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#35](https://github.com/primer/doctocat-nextjs/pull/35) [`d0c5bd4`](https://github.com/primer/doctocat-nextjs/commit/d0c5bd40829c417b610682049e4b5bd59b3f87f0) Thanks [@rezrah](https://github.com/rezrah)! - Enable support for `trailingSlash: true` in `next.config.js`
|
|
8
|
+
|
|
3
9
|
## 0.5.0
|
|
4
10
|
|
|
5
11
|
### Minor Changes
|
|
@@ -12,12 +12,21 @@ type IndexCardsProps = {
|
|
|
12
12
|
|
|
13
13
|
export function IndexCards({route, folderData}: IndexCardsProps) {
|
|
14
14
|
// We don't want to show children of these pages. E.g. tabbed pages
|
|
15
|
-
const onlyDirectChildren = folderData.filter(
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
const onlyDirectChildren = folderData.filter(item => {
|
|
16
|
+
// Normalize paths regardless of trailing slash enablement
|
|
17
|
+
const normalizedRoute = route.endsWith('/') ? route : `${route}/`
|
|
18
|
+
const normalizedItemRoute = item.route.endsWith('/') ? item.route : `${item.route}/`
|
|
19
|
+
|
|
20
|
+
const isChild = normalizedItemRoute.startsWith(normalizedRoute)
|
|
21
|
+
if (!isChild) return false
|
|
22
|
+
|
|
23
|
+
const routeSegments = normalizedRoute.split('/').filter(Boolean).length
|
|
24
|
+
const itemSegments = normalizedItemRoute.split('/').filter(Boolean).length
|
|
18
25
|
|
|
19
|
-
|
|
26
|
+
return itemSegments === routeSegments + 1
|
|
27
|
+
})
|
|
20
28
|
|
|
29
|
+
const filteredData = onlyDirectChildren.filter(item => item.type === 'doc')
|
|
21
30
|
return (
|
|
22
31
|
<Stack direction="vertical" padding="none" gap="spacious">
|
|
23
32
|
{filteredData.map((item: DocsItem) => {
|
|
@@ -53,16 +53,13 @@ export type ThemeProps = PropsWithChildren<{
|
|
|
53
53
|
|
|
54
54
|
export function Theme({pageMap, children}: ThemeProps) {
|
|
55
55
|
const pathname = usePathname()
|
|
56
|
+
const pathHasTrailingSlash = pathname.endsWith('/')
|
|
56
57
|
|
|
57
58
|
const normalizedPages = normalizePages({
|
|
58
59
|
list: pageMap,
|
|
59
60
|
route: pathname,
|
|
60
61
|
})
|
|
61
62
|
|
|
62
|
-
const {activeMetadata} = normalizedPages
|
|
63
|
-
|
|
64
|
-
const {filePath = '', title = ''} = activeMetadata || {}
|
|
65
|
-
|
|
66
63
|
const route = usePathname()
|
|
67
64
|
|
|
68
65
|
const fsPath = useFSRoute()
|
|
@@ -79,33 +76,42 @@ export function Theme({pageMap, children}: ThemeProps) {
|
|
|
79
76
|
// eslint-disable-next-line i18n-text/no-en
|
|
80
77
|
const siteTitle = process.env.NEXT_PUBLIC_SITE_TITLE || 'Example Site'
|
|
81
78
|
const isHomePage = route === '/'
|
|
82
|
-
|
|
83
|
-
|
|
79
|
+
|
|
80
|
+
const activeFile = isHomePage
|
|
81
|
+
? undefined
|
|
82
|
+
: (normalizedPages.flatDocsDirectories.find(
|
|
83
|
+
item => `${item.route}${pathHasTrailingSlash ? '/' : ''}` === pathname,
|
|
84
|
+
) as MdxFile)
|
|
85
|
+
|
|
86
|
+
const activeMetadata = activeFile?.frontMatter || {}
|
|
87
|
+
const filePath = activeMetadata.filePath || ''
|
|
88
|
+
const title = activeMetadata.title || ''
|
|
89
|
+
|
|
90
|
+
const isIndexPage = /index\.mdx?$/.test(filePath) && !isHomePage && activeMetadata['show-tabs'] === undefined
|
|
84
91
|
const data = !isHomePage && activePath[activePath.length - 2]
|
|
85
92
|
const filteredTabData: MdxFile[] = data && hasChildren(data) ? ((data as Folder).children as MdxFile[]) : []
|
|
86
93
|
|
|
87
94
|
const relatedLinks = getRelatedPages(route, activeMetadata, flatDocsDirectories)
|
|
88
|
-
const disablePageAnimation = activeMetadata
|
|
95
|
+
const disablePageAnimation = activeMetadata.options?.disablePageAnimation || false
|
|
96
|
+
|
|
89
97
|
return (
|
|
90
98
|
<>
|
|
91
99
|
<BrandThemeProvider dir="ltr" colorMode={colorMode}>
|
|
92
100
|
<ThemeProvider colorMode={colorMode}>
|
|
93
101
|
<BaseStyles>
|
|
94
|
-
|
|
95
|
-
<
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
</Head>
|
|
108
|
-
)}
|
|
102
|
+
<Head>
|
|
103
|
+
<title>{title}</title>
|
|
104
|
+
{activeMetadata.description && <meta name="description" content={activeMetadata.description} />}
|
|
105
|
+
<meta property="og:type" content="website" />
|
|
106
|
+
<meta property="og:title" content={title} />
|
|
107
|
+
{activeMetadata.description && <meta property="og:description" content={activeMetadata.description} />}
|
|
108
|
+
<meta property="og:image" content={activeMetadata.image || '/og-image.png'} />
|
|
109
|
+
{/* X (Twitter) OG */}
|
|
110
|
+
<meta name="twitter:card" content="summary_large_image" />
|
|
111
|
+
<meta name="twitter:title" content={title} />
|
|
112
|
+
{activeMetadata.description && <meta name="twitter:description" content={activeMetadata.description} />}
|
|
113
|
+
<meta name="twitter:image" content={activeMetadata.image || '/og-image.png'} />
|
|
114
|
+
</Head>
|
|
109
115
|
|
|
110
116
|
<ContentWrapper disableAnimations={disablePageAnimation}>
|
|
111
117
|
<PRCBox
|
|
@@ -159,22 +165,22 @@ export function Theme({pageMap, children}: ThemeProps) {
|
|
|
159
165
|
|
|
160
166
|
<Box>
|
|
161
167
|
<Stack direction="vertical" padding="none" gap={12} alignItems="flex-start">
|
|
162
|
-
{activeMetadata
|
|
168
|
+
{activeMetadata.title && (
|
|
163
169
|
<Heading as="h1" size="3">
|
|
164
170
|
{activeMetadata.title}
|
|
165
171
|
</Heading>
|
|
166
172
|
)}
|
|
167
|
-
{activeMetadata
|
|
173
|
+
{activeMetadata.description && (
|
|
168
174
|
<Text as="p" variant="muted" size="300">
|
|
169
175
|
{activeMetadata.description}
|
|
170
176
|
</Text>
|
|
171
177
|
)}
|
|
172
|
-
{activeMetadata
|
|
178
|
+
{activeMetadata.image && (
|
|
173
179
|
<Box paddingBlockStart={16} style={{width: '100%'}}>
|
|
174
180
|
<Hero.Image src={activeMetadata.image} alt={activeMetadata['image-alt']} />
|
|
175
181
|
</Box>
|
|
176
182
|
)}
|
|
177
|
-
{activeMetadata
|
|
183
|
+
{activeMetadata['action-1-text'] && (
|
|
178
184
|
<Box paddingBlockStart={16}>
|
|
179
185
|
<ButtonGroup>
|
|
180
186
|
<Button as="a" href={activeMetadata['action-1-link']}>
|
|
@@ -190,9 +196,7 @@ export function Theme({pageMap, children}: ThemeProps) {
|
|
|
190
196
|
)}
|
|
191
197
|
</Stack>
|
|
192
198
|
</Box>
|
|
193
|
-
{activeMetadata
|
|
194
|
-
<UnderlineNav tabData={filteredTabData} />
|
|
195
|
-
)}
|
|
199
|
+
{activeMetadata['show-tabs'] && <UnderlineNav tabData={filteredTabData} />}
|
|
196
200
|
</>
|
|
197
201
|
)}
|
|
198
202
|
<article>
|
|
@@ -63,6 +63,11 @@ export function Sidebar({pageMap}: SidebarProps) {
|
|
|
63
63
|
)
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
const normalizePath = (path: string) => {
|
|
67
|
+
// Remove trailing slash unless it's the root path
|
|
68
|
+
return path === '/' ? path : path.replace(/\/+$/, '')
|
|
69
|
+
}
|
|
70
|
+
|
|
66
71
|
return (
|
|
67
72
|
<NavList.Group title={subNavName} key={item.name} sx={{mb: 24}}>
|
|
68
73
|
{item.children
|
|
@@ -73,12 +78,13 @@ export function Sidebar({pageMap}: SidebarProps) {
|
|
|
73
78
|
if (!hasChildren(child)) {
|
|
74
79
|
const {name, route} = child as MdxFile
|
|
75
80
|
|
|
81
|
+
const cleanPathname = normalizePath(pathname)
|
|
76
82
|
return (
|
|
77
83
|
<NavList.Item
|
|
78
84
|
as={NextLink}
|
|
79
85
|
key={name}
|
|
80
86
|
href={route}
|
|
81
|
-
aria-current={route ===
|
|
87
|
+
aria-current={route === cleanPathname ? 'page' : undefined}
|
|
82
88
|
>
|
|
83
89
|
{(child as MdxFile).frontMatter?.title || name}
|
|
84
90
|
</NavList.Item>
|
|
@@ -88,17 +94,24 @@ export function Sidebar({pageMap}: SidebarProps) {
|
|
|
88
94
|
if (hasChildren(child)) {
|
|
89
95
|
const landingPageItem = (child as Folder).children.find(
|
|
90
96
|
innerChild => (innerChild as DocsItem).name === 'index',
|
|
91
|
-
)
|
|
97
|
+
) as MdxFile
|
|
98
|
+
|
|
99
|
+
// Then inside your component where you need to compare paths:
|
|
100
|
+
const cleanPathname = normalizePath(pathname)
|
|
101
|
+
const cleanRoute = normalizePath(landingPageItem.route)
|
|
102
|
+
|
|
103
|
+
// For checking if current path is this route or a direct child:
|
|
104
|
+
const isCurrentOrChild = cleanPathname === cleanRoute || cleanPathname.startsWith(`${cleanRoute}/`)
|
|
92
105
|
|
|
93
106
|
return (
|
|
94
107
|
<NavList.Item
|
|
95
108
|
as={NextLink}
|
|
96
|
-
key={
|
|
97
|
-
href={
|
|
109
|
+
key={landingPageItem.route}
|
|
110
|
+
href={landingPageItem.route}
|
|
98
111
|
sx={{textTransform: 'capitalize'}}
|
|
99
|
-
aria-current={
|
|
112
|
+
aria-current={isCurrentOrChild ? 'page' : undefined}
|
|
100
113
|
>
|
|
101
|
-
{
|
|
114
|
+
{landingPageItem.frontMatter?.title || item.name}
|
|
102
115
|
</NavList.Item>
|
|
103
116
|
)
|
|
104
117
|
}
|
|
@@ -12,8 +12,7 @@ type UnderlineNavProps = {
|
|
|
12
12
|
export function UnderlineNav({tabData}: UnderlineNavProps) {
|
|
13
13
|
const [isClient, setIsClient] = useState(false)
|
|
14
14
|
const pathname = usePathname()
|
|
15
|
-
|
|
16
|
-
const currentRoute = pathname
|
|
15
|
+
const pathHasTrailingSlash = pathname.endsWith('/')
|
|
17
16
|
|
|
18
17
|
// Reorders tabData so the tab with name === index is always first
|
|
19
18
|
if (tabData.length > 1) {
|
|
@@ -34,12 +33,14 @@ export function UnderlineNav({tabData}: UnderlineNavProps) {
|
|
|
34
33
|
<PrimerUnderlineNav aria-label="Sibling pages">
|
|
35
34
|
{tabData.length > 1 &&
|
|
36
35
|
tabData.reverse().map(item => {
|
|
36
|
+
const cleanPathname = pathname === '/' ? '/' : pathHasTrailingSlash ? pathname.slice(0, -1) : pathname
|
|
37
|
+
|
|
37
38
|
return (
|
|
38
39
|
<PrimerUnderlineNav.Item
|
|
39
40
|
as={Link}
|
|
40
41
|
key={item.name}
|
|
41
42
|
href={`${item.route}`}
|
|
42
|
-
aria-current={
|
|
43
|
+
aria-current={cleanPathname === item.route ? 'page' : undefined}
|
|
43
44
|
>
|
|
44
45
|
{(item.frontMatter && (item.frontMatter['tab-label'] || item.frontMatter.title)) || item.name}
|
|
45
46
|
</PrimerUnderlineNav.Item>
|
package/css/prose.module.css
CHANGED
|
@@ -150,6 +150,7 @@
|
|
|
150
150
|
.Prose ul:not(:global(.custom-component) ul) {
|
|
151
151
|
list-style-type: image;
|
|
152
152
|
list-style-image: var(--brand-Prose-unorderedList-imageUrl);
|
|
153
|
+
margin-block-end: var(--base-size-24) !important;
|
|
153
154
|
}
|
|
154
155
|
|
|
155
156
|
.Prose li:not(:global(.custom-component) li) {
|