Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 16 additions & 86 deletions src/components/LibraryLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,11 @@ import {
getLibraryLayoutVersion,
getLibraryTabLinkOptions,
getMenuGroupInitialOpenState,
isChartsCatalogTarget,
} from './library-layout-navigation'
import {
customToolTabs,
isCustomToolTarget,
} from '~/libraries/custom-tool-tabs'

// Number of days a doc page is flagged as "New"/"Updated" in the sidebar.
const RECENCY_WINDOW_DAYS = 7
Expand Down Expand Up @@ -675,18 +678,6 @@ const useMenuConfig = ({
const currentFramework = useCurrentFramework(frameworks)
const statsAvailable =
getLibrary(libraryId as LibraryId).statsAvailable !== false
const chartsExamplesMenuItems: MenuItem['children'] = [
{
label: 'Examples',
to: '/charts/catalog',
tab: 'home',
},
{
label: 'Examples',
to: '/charts/catalog',
tab: 'examples',
},
]

const localMenu: MenuItem = {
label: 'Menu',
Expand All @@ -695,7 +686,6 @@ const useMenuConfig = ({
label: 'Home',
to: '..',
},
...(libraryId === 'charts' ? chartsExamplesMenuItems : []),
{
label: 'Blog',
to: '/$libraryId/$version/docs/blog',
Expand Down Expand Up @@ -830,17 +820,7 @@ export function LibraryLayout({
const menuConfig = useMenuConfig({ config, frameworks, repo, libraryId })
const libraryGroupColor = categoryTextColor[categoryOf(libraryId)]

const isExample = matches.some(
(d) =>
d.pathname.includes('/examples/') ||
d.routeId.startsWith('/_library/charts/catalog'),
)

const isThemeEditor = matches.some((d) =>
d.routeId.startsWith('/_library/highlight/$version/theme-editor'),
)

const isNpmStats = matches.some((d) => d.pathname.includes('/docs/npm-stats'))
const docsTab = matches.find((d) => d.staticData.docsTab)?.staticData.docsTab

// The library blog already lists posts, so the "Latest Posts" rail widget is
// redundant there — hide it on the blog while keeping it on other docs pages.
Expand Down Expand Up @@ -872,60 +852,17 @@ export function LibraryLayout({
}, [closeMobileMenu, mobileMenuOpen])

const tabbedMenuConfig = React.useMemo(() => {
const tabs = getTabbedMenuConfig(menuConfig)

if (libraryId === 'charts') {
return tabs.map((tab) =>
tab.id === 'examples'
? {
...tab,
firstItem: {
label: 'Examples',
to: '/charts/catalog',
tab: 'examples',
},
}
: tab,
)
}

if (libraryId === 'highlight') {
return [
...tabs,
{
id: 'theme-editor' as const,
label: 'Theme Editor',
groups: [],
firstItem: {
label: 'Theme Editor',
to: '/highlight/$version/theme-editor',
tab: 'theme-editor' as const,
},
},
]
}

return tabs
return getTabbedMenuConfig(menuConfig, customToolTabs[libraryId])
}, [libraryId, menuConfig])

const activeTabId = React.useMemo(() => {
if (isThemeEditor) {
return 'theme-editor' as const
}

return getActiveDocsNavTabId({
isExample,
tabIdOverride: docsTab?.id,
menuConfig,
pathname: lastMatch.pathname,
relativePathname,
})
}, [
isExample,
isThemeEditor,
lastMatch.pathname,
menuConfig,
relativePathname,
])
}, [docsTab?.id, lastMatch.pathname, menuConfig, relativePathname])

const visibleMenuConfig = React.useMemo(() => {
return (
Expand Down Expand Up @@ -964,11 +901,12 @@ export function LibraryLayout({

const groupInitialOpenState = React.useMemo(() => {
return getMenuGroupInitialOpenState(
libraryId,
visibleMenuConfig,
_splat,
lastMatch.pathname,
)
}, [lastMatch.pathname, visibleMenuConfig, _splat])
}, [libraryId, lastMatch.pathname, visibleMenuConfig, _splat])

const [openGroups, setOpenGroups] = React.useState(groupInitialOpenState)

Expand Down Expand Up @@ -1042,7 +980,7 @@ export function LibraryLayout({
? ({ libraryId, version } as never)
: undefined
const isHomeLink = child.to === '..'
const isChartsExamplesLink = isChartsCatalogTarget(child.to)
const isCustomToolLink = isCustomToolTarget(libraryId, child.to)
const frameworkDocsTarget = getFrameworkDocsLinkTarget(child.to)

const recency = getDocRecency(child.addedAt, child.updatedAt)
Expand Down Expand Up @@ -1130,14 +1068,12 @@ export function LibraryLayout({
) : (
<Link
from={
isChartsExamplesLink
? undefined
: '/$libraryId/$version/docs'
isCustomToolLink ? undefined : '/$libraryId/$version/docs'
}
to={child.to}
params={linkParams}
onClick={closeMobileMenu}
preload={isChartsExamplesLink ? false : 'intent'}
preload={isCustomToolLink ? false : 'intent'}
activeOptions={{
exact: true,
includeHash: false,
Expand Down Expand Up @@ -1244,8 +1180,7 @@ export function LibraryLayout({
params={linkOptions.params as never}
onClick={closeMobileMenu}
preload={
isChartsCatalogTarget(target.to) ||
target.to === '/highlight/$version/theme-editor'
isCustomToolTarget(libraryId, target.to)
? false
: 'intent'
}
Expand Down Expand Up @@ -1421,10 +1356,7 @@ export function LibraryLayout({
to={linkOptions.to as never}
params={linkOptions.params as never}
preload={
isChartsCatalogTarget(target.to) ||
target.to === '/highlight/$version/theme-editor'
? false
: 'intent'
isCustomToolTarget(libraryId, target.to) ? false : 'intent'
}
activeOptions={{
exact: true,
Expand Down Expand Up @@ -1545,9 +1477,7 @@ export function LibraryLayout({
!isLandingPage && 'px-4 md:px-8',

!isLandingPage &&
!isExample &&
!isNpmStats &&
!isThemeEditor &&
!docsTab?.fullBleed &&
'mx-auto w-[var(--docs-content-width,900px)]',
)}
>
Expand Down
12 changes: 8 additions & 4 deletions src/components/library-layout-navigation.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { isCustomToolTarget } from '~/libraries/custom-tool-tabs'
import type { LibraryId } from '~/libraries/ids'
import type { MenuItem } from '~/utils/config'

export function isChartsCatalogTarget(to: string) {
Expand All @@ -22,7 +24,7 @@ export function getLibraryTabLinkOptions({
version,
to,
}: {
libraryId: string
libraryId: LibraryId
version: string
to: string
}) {
Expand All @@ -34,7 +36,7 @@ export function getLibraryTabLinkOptions({

return {
from:
isHomeTarget || isChartsCatalogTarget(to)
isHomeTarget || isCustomToolTarget(libraryId, to)
? undefined
: '/$libraryId/$version/docs',
to: isHomeTarget ? `/${libraryId}/${version}` : to,
Expand All @@ -50,6 +52,7 @@ function normalizeMenuPath(path: string) {
}

export function isMenuTargetActive(
libraryId: LibraryId,
to: string,
relativePathname: string | undefined,
pathname: string,
Expand All @@ -59,12 +62,13 @@ export function isMenuTargetActive(
}

return (
isChartsCatalogTarget(to) &&
isCustomToolTarget(libraryId, to) &&
normalizeMenuPath(to) === normalizeMenuPath(pathname)
)
}

export function getMenuGroupInitialOpenState(
libraryId: LibraryId,
groups: MenuItem[],
relativePathname: string | undefined,
pathname: string,
Expand All @@ -73,7 +77,7 @@ export function getMenuGroupInitialOpenState(

groups.forEach((group, index) => {
const isChildActive = group.children.some((child) =>
isMenuTargetActive(child.to, relativePathname, pathname),
isMenuTargetActive(libraryId, child.to, relativePathname, pathname),
)
const key = `${index}:${String(group.label)}`

Expand Down
29 changes: 29 additions & 0 deletions src/libraries/custom-tool-tabs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type { DocsNavTabId } from '~/utils/docsNavTabs'
import type { LibraryId } from './ids'
import type { FileRouteTypes } from '~/routeTree.gen'

// A "custom tool tab" is a page bolted onto a library's docs nav bar that isn't sourced from the library's docs/config.json
export type CustomToolTabId = DocsNavTabId

export type CustomToolTab = {
id: CustomToolTabId
label: string
to: FileRouteTypes['fullPaths']
}

export const customToolTabs: Partial<Record<LibraryId, CustomToolTab[]>> = {
charts: [{ id: 'examples', label: 'Examples', to: '/charts/catalog' }],
highlight: [
{
id: 'theme-editor',
label: 'Theme Editor',
to: '/highlight/$version/theme-editor',
},
],
}

export function isCustomToolTarget(libraryId: LibraryId, to: string) {
return (customToolTabs[libraryId] ?? []).some(
(tab) => to === tab.to || to.startsWith(`${tab.to}/`),
)
}
9 changes: 9 additions & 0 deletions src/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import {
consumeStaleAppRecovery,
} from './utils/stale-app-reload'
import { redactByokRequestHeaders } from './utils/sentry-redaction'
import type { DocsNavTabId } from './utils/docsNavTabs'
import type { CustomToolTabId } from './libraries/custom-tool-tabs'

if (typeof document !== 'undefined') {
installStaleAppReloadHandlers()
Expand Down Expand Up @@ -97,6 +99,13 @@ declare module '@tanstack/react-router' {
showNavbar?: boolean
includeSearchInCanonical?: boolean
ownsCanonicalLink?: boolean

// allows for the setting of custom routes to show up in the page tab
docsTab?: {
id: DocsNavTabId | CustomToolTabId
// config to disable clamping of page
fullBleed?: boolean
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ export const Route = createFileRoute(
// /latest equivalent), so the root route must not emit its own.
staticData: {
ownsCanonicalLink: true,
docsTab: { id: 'examples', fullBleed: true },
},
validateSearch: v.object({
path: v.optional(v.string()),
Expand Down
1 change: 1 addition & 0 deletions src/routes/_library/$libraryId/$version.docs.npm-stats.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ export const Route = createFileRoute(
component: RouteComponent,
staticData: {
includeSearchInCanonical: true,
docsTab: { id: 'home', fullBleed: true },
},
})

Expand Down
3 changes: 3 additions & 0 deletions src/routes/_library/charts.catalog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ export const Route = createFileRoute('/_library/charts/catalog')({
version: 'latest',
}),
component: ChartsCatalogLayout,
staticData: {
docsTab: { id: 'examples', fullBleed: true },
},
})

function ChartsCatalogLayout() {
Expand Down
1 change: 1 addition & 0 deletions src/routes/_library/highlight.$version.theme-editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@ export const Route = createFileRoute(
}
},
component: ThemeEditorPage,
staticData: { docsTab: { id: 'theme-editor', fullBleed: true } },
ssr: false,
})
28 changes: 21 additions & 7 deletions src/utils/docsNavTabs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,11 @@ function isDocsTabTarget(to: string) {
return true
}

export function getTabbedMenuConfig(menuConfig: MenuItem[]) {
return docsNavTabs
export function getTabbedMenuConfig(
menuConfig: MenuItem[],
tools: Array<{ id: DocsNavTabId; label: string; to: string }> = [],
) {
const tabs = docsNavTabs
.map((tab) => {
const groups = menuConfig
.map((group) => {
Expand Down Expand Up @@ -148,6 +151,17 @@ export function getTabbedMenuConfig(menuConfig: MenuItem[]) {
}
})
.filter((tab) => tab.groups.length)

for (const tool of tools) {
const existing = tabs.find((tab) => tab.id === tool.id)
const firstItem = { label: tool.label, to: tool.to }
if (existing) {
existing.firstItem = firstItem
} else {
tabs.push({ id: tool.id, label: tool.label, groups: [], firstItem })
}
}
return tabs
}

// Matches a menu child's `to` against the current pathname, handling the
Expand Down Expand Up @@ -184,19 +198,19 @@ function isChildPathMatch({
return false
}

export function getActiveDocsNavTabId({
isExample,
export function getActiveDocsNavTabId<TTabIdOverride>({
tabIdOverride,
menuConfig,
pathname,
relativePathname,
}: {
isExample: boolean
tabIdOverride?: TTabIdOverride
menuConfig: MenuItem[]
pathname: string
relativePathname: string
}) {
if (isExample) {
return 'examples'
if (tabIdOverride) {
return tabIdOverride
}

const activeGroup = menuConfig.find((group) =>
Expand Down
Loading
Loading