Skip to content

Commit

Permalink
add initial color mode from system preferences and fix hero button hrefs
Browse files Browse the repository at this point in the history
  • Loading branch information
rezrah committed Dec 13, 2023
1 parent b49adef commit f4adfb5
Show file tree
Hide file tree
Showing 8 changed files with 157 additions and 114 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ out/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
*.tgz
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ const ColorModeProvider = ({children}) => {
const savedMode = window.localStorage.getItem('doctocat-active-color-mode')
if (savedMode && (savedMode === 'light' || savedMode === 'dark')) {
setColorMode(savedMode)
} else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
setColorMode('dark')
}
}
}, [])
Expand Down
4 changes: 4 additions & 0 deletions packages/theme/components/layout/header/Header.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@
}

@media (min-width: 768px) {
.Header {
padding: var(--base-size-20) var(--base-size-12) var(--base-size-20) var(--base-size-24);
}

.Header__searchArea {
width: 100%;
max-width: 350px;
Expand Down
47 changes: 25 additions & 22 deletions packages/theme/components/layout/header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import clsx from 'clsx'
import {MdxFile, PageMapItem} from 'nextra'
import type {PageItem} from 'nextra/normalize-pages'
import React, {useEffect, useMemo} from 'react'
import {debounce} from 'lodash'

import Link from 'next/link'
import styles from './Header.module.css'
Expand Down Expand Up @@ -67,6 +68,8 @@ export function Header({pageMap, docsDirectories, siteTitle}: HeaderProps) {
document.documentElement.setAttribute('data-color-mode', colorMode)
}, [colorMode])

const setSearchResultsDebounced = debounce(data => setSearchResults(data), 1000)

const searchData = useMemo(
() =>
pageMap
Expand Down Expand Up @@ -102,34 +105,34 @@ export function Header({pageMap, docsDirectories, siteTitle}: HeaderProps) {
}
if (inputRef.current.value.length > 2) {
const curSearchTerm = inputRef.current.value.toLowerCase()
const results: SearchResults[] = searchData

// filters the frontMatter title and descriptions against the search term
const filteredData = searchData
.filter((data): data is SearchResults => data !== null)
.filter(data => {
if (!data.title) return false
const title = data.title.toLowerCase()
const description = data.description.toLowerCase()
let searchIndex = 0
for (let i = 0; i < title.length; i++) {
if (title[i] === curSearchTerm[searchIndex]) {
searchIndex++
if (searchIndex === curSearchTerm.length) {
return true
}
}
}
searchIndex = 0
for (let i = 0; i < description.length; i++) {
if (description[i] === curSearchTerm[searchIndex]) {
searchIndex++
if (searchIndex === curSearchTerm.length) {
return true
}
}
}
return false
return title.includes(curSearchTerm) || description.includes(curSearchTerm)
})
.filter(Boolean)
setTimeout(() => setSearchResults(results), 1000)

// sorts the data to show hits in title first, description second
const sortedData = filteredData.sort((a, b) => {
const aTitle = a.title.toLowerCase()
const bTitle = b.title.toLowerCase()
const aIncludes = aTitle.includes(curSearchTerm)
const bIncludes = bTitle.includes(curSearchTerm)

if (aIncludes && !bIncludes) {
return -1
} else if (!aIncludes && bIncludes) {
return 1
} else {
return 0
}
})

setSearchResultsDebounced(sortedData)

setSearchTerm(inputRef.current.value)
setIsSearchResultOpen(true)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.IndexCards {
--brand-Grid-spacing-margin: 0;
--brand-Card-maxWidth: 100%;
--brand-Grid-spacing-row: var(--brand-Grid-spacing-column-gap);
}
9 changes: 5 additions & 4 deletions packages/theme/components/layout/root-layout/Theme.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,7 @@ export function Theme({children, pageOpts}: NextraThemeLayoutProps) {

const {siteTitle} = publicRuntimeConfig
const isHomePage = route === '/'
const isIndexPage = filePath.endsWith('index.mdx') && !isHomePage && !frontMatter['show-tabs']

const isIndexPage = /index\.mdx?$/.test(filePath) && !isHomePage && !frontMatter['show-tabs']
const data = !isHomePage && activePath[activePath.length - 2]
const filteredTabData: MdxFile[] =
data && data.kind === 'Folder'
Expand Down Expand Up @@ -143,9 +142,11 @@ export function Theme({children, pageOpts}: NextraThemeLayoutProps) {
{frontMatter['action-1-text'] && (
<Box paddingBlockStart={16}>
<ButtonGroup>
<Button as="a">{frontMatter['action-1-text']}</Button>
<Button as="a" href={frontMatter['action-1-link']}>
{frontMatter['action-1-text']}
</Button>
{frontMatter['action-2-text'] && (
<Button as="a" variant="secondary">
<Button as="a" variant="secondary" href={frontMatter['action-2-link']}>
{frontMatter['action-2-text']}
</Button>
)}
Expand Down
6 changes: 6 additions & 0 deletions packages/theme/components/layout/sidebar/Sidebar.module.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
@media (min-width: 768px) {
.Sidebar {
padding-inline-start: var(--base-size-8);
}
}

.NavList h3 {
font-weight: normal;
text-transform: uppercase;
Expand Down
200 changes: 112 additions & 88 deletions packages/theme/components/layout/sidebar/Sidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react'
import React, {useMemo} from 'react'
import {NavList} from '@primer/react'
import {Folder, MdxFile, PageMapItem} from 'nextra'
import {useRouter} from 'next/router'
Expand Down Expand Up @@ -64,102 +64,126 @@ export function Sidebar({pageMap}: SidebarProps) {

const {sidebarLinks}: ThemeConfig = publicRuntimeConfig

return (
<NavList className={styles.NavList}>
{pageMap.map(item => {
if (item.kind === 'MdxPage' && item.route === '/') return null

if (item.kind === 'MdxPage') {
return (
<NavList.Item key={item.name} href={`${basePath}${item.route}`} sx={{textTransform: 'capitalize'}}>
{item.frontMatter?.title ?? item.name}
</NavList.Item>
)
/**
* Sorts the incoming data so that folders with a menu-position frontmatter value
* are sorted to the top of the list. If a folder does not have a menu-position
* value, it is sorted to the bottom of the list.
*/
const reorderedPageMap = useMemo(
() =>
[...pageMap].sort((a, b) => {
if (a.kind === 'Folder' && a.children) {
const aIndex = a.children.find(child => child.name === 'index' && child.kind === 'MdxPage')
const aPosition = (aIndex as MdxFile | undefined)?.frontMatter?.['menu-position'] ?? Infinity
if (b.kind === 'Folder' && b.children) {
const bIndex = b.children.find(child => child.name === 'index' && child.kind === 'MdxPage')
const bPosition = (bIndex as MdxFile | undefined)?.frontMatter?.['menu-position'] ?? Infinity
return aPosition - bPosition
}
}
return 0
}),
[pageMap],
)

return (
<div className={styles.Sidebar}>
<NavList className={styles.NavList}>
{reorderedPageMap.map(item => {
if (item.kind === 'MdxPage' && item.route === '/') return null

// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (item.kind === 'Folder') {
const indexPage = (item as Folder).children.find(
child => child.kind === 'MdxPage' && child.name === 'index',
) as MdxFile
const subNavName = indexPage.frontMatter?.title ?? ''
const shouldShowTabs = indexPage.frontMatter?.['show-tabs'] ?? false
if (shouldShowTabs) {
if (item.kind === 'MdxPage') {
return (
<NavList.Item key={indexPage.name} href={`${basePath}${indexPage.route}`}>
{(indexPage as MdxFile).frontMatter?.title || item.name}
<NavList.Item key={item.name} href={`${basePath}${item.route}`} sx={{textTransform: 'capitalize'}}>
{item.frontMatter?.title ?? item.name}
</NavList.Item>
)
}

return (
<NavList.Group title={subNavName} key={item.name} sx={{mb: 24}}>
{item.children &&
item.children
.sort((a, b) => (a.name === 'index' ? -1 : b.name === 'index' ? 1 : 0)) // puts index page first
.filter(
child =>
(child as DocsItem).name !== 'index' ||
((child.name === 'index' && (child as MdxFile).frontMatter?.['show-tabs']) ?? false),
) // only show index page if it has show-tabs
.map((child: DocsItem) => {
if (child.kind === 'MdxPage') {
return (
<NavList.Item
key={child.name}
href={`${basePath}${child.route}`}
aria-current={child.route === router.pathname ? 'page' : undefined}
>
{(child as MdxFile).frontMatter?.title || item.name}
</NavList.Item>
)
}

if ((child as DocsItem).kind === 'Folder') {
const landingPageItem: PageMapItem | undefined = (child as Folder).children.find(
innerChild => innerChild.kind === 'MdxPage' && innerChild.name === 'index',
)
return (
<NavList.Item
key={`${(landingPageItem as MdxFile).route}`}
href={`${basePath}${(landingPageItem as MdxFile).route}`}
sx={{textTransform: 'capitalize'}}
aria-current={(landingPageItem as MdxFile).route === router.pathname ? 'page' : undefined}
>
{(landingPageItem as MdxFile).frontMatter?.title || item.name}
</NavList.Item>
)
}

return null
})}
</NavList.Group>
)
}

return null
})}
{sidebarLinks && sidebarLinks.length > 0 && (
<NavList.Group title="" sx={{mb: 24}}>
{sidebarLinks.map(link => {
const {leadingIcon} = link
const isExternalUrl = link.href.startsWith('http')
const LeadingIcon = getOcticonForType(leadingIcon)
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (item.kind === 'Folder') {
const indexPage = (item as Folder).children.find(
child => child.kind === 'MdxPage' && child.name === 'index',
) as MdxFile
const subNavName = indexPage.frontMatter?.title ?? ''
const shouldShowTabs = indexPage.frontMatter?.['show-tabs'] ?? false
if (shouldShowTabs) {
return (
<NavList.Item key={indexPage.name} href={`${basePath}${indexPage.route}`}>
{(indexPage as MdxFile).frontMatter?.title || item.name}
</NavList.Item>
)
}

return (
<NavList.Item key={link.title} href={link.href} target={isExternalUrl ? '_blank' : undefined}>
<NavList.LeadingVisual>{LeadingIcon && <LeadingIcon />}</NavList.LeadingVisual>
{link.title}
{isExternalUrl && (
<NavList.TrailingVisual>
<LinkExternalIcon />
</NavList.TrailingVisual>
)}
</NavList.Item>
<NavList.Group title={subNavName} key={item.name} sx={{mb: 24}}>
{item.children &&
item.children
.sort((a, b) => (a.name === 'index' ? -1 : b.name === 'index' ? 1 : 0)) // puts index page first
.filter(
child =>
(child as DocsItem).name !== 'index' ||
((child.name === 'index' && (child as MdxFile).frontMatter?.['show-tabs']) ?? false),
) // only show index page if it has show-tabs
.map((child: DocsItem) => {
if (child.kind === 'MdxPage') {
return (
<NavList.Item
key={child.name}
href={`${basePath}${child.route}`}
aria-current={child.route === router.pathname ? 'page' : undefined}
>
{(child as MdxFile).frontMatter?.title || item.name}
</NavList.Item>
)
}

if ((child as DocsItem).kind === 'Folder') {
const landingPageItem: PageMapItem | undefined = (child as Folder).children.find(
innerChild => innerChild.kind === 'MdxPage' && innerChild.name === 'index',
)
return (
<NavList.Item
key={`${(landingPageItem as MdxFile).route}`}
href={`${basePath}${(landingPageItem as MdxFile).route}`}
sx={{textTransform: 'capitalize'}}
aria-current={(landingPageItem as MdxFile).route === router.pathname ? 'page' : undefined}
>
{(landingPageItem as MdxFile).frontMatter?.title || item.name}
</NavList.Item>
)
}

return null
})}
</NavList.Group>
)
})}
</NavList.Group>
)}
</NavList>
}

return null
})}
{sidebarLinks && sidebarLinks.length > 0 && (
<NavList.Group title="" sx={{mb: 24}}>
{sidebarLinks.map(link => {
const {leadingIcon} = link
const isExternalUrl = link.href.startsWith('http')
const LeadingIcon = getOcticonForType(leadingIcon)

return (
<NavList.Item key={link.title} href={link.href} target={isExternalUrl ? '_blank' : undefined}>
<NavList.LeadingVisual>{LeadingIcon && <LeadingIcon />}</NavList.LeadingVisual>
{link.title}
{isExternalUrl && (
<NavList.TrailingVisual>
<LinkExternalIcon />
</NavList.TrailingVisual>
)}
</NavList.Item>
)
})}
</NavList.Group>
)}
</NavList>
</div>
)
}

0 comments on commit f4adfb5

Please sign in to comment.