Skip to content

Commit 12a51a1

Browse files
authored
add ellipsis to long breadcrumbs (#235)
1 parent f7a3062 commit 12a51a1

File tree

4 files changed

+160
-0
lines changed

4 files changed

+160
-0
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import {type ReactNode} from 'react';
2+
import Link from '@docusaurus/Link';
3+
import useBaseUrl from '@docusaurus/useBaseUrl';
4+
import {translate} from '@docusaurus/Translate';
5+
import IconHome from '@theme/Icon/Home';
6+
7+
import styles from './styles.module.css';
8+
9+
export default function HomeBreadcrumbItem(): ReactNode {
10+
const homeHref = useBaseUrl('/');
11+
12+
return (
13+
<li className="breadcrumbs__item">
14+
<Link
15+
aria-label={translate({
16+
id: 'theme.docs.breadcrumbs.home',
17+
message: 'Home page',
18+
description: 'The ARIA label for the home page in the breadcrumbs',
19+
})}
20+
className="breadcrumbs__link"
21+
href={homeHref}>
22+
<IconHome className={styles.breadcrumbHomeIcon} />
23+
</Link>
24+
</li>
25+
);
26+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.breadcrumbHomeIcon {
2+
position: relative;
3+
top: 1px;
4+
vertical-align: top;
5+
height: 1.1rem;
6+
width: 1.1rem;
7+
}

src/theme/DocBreadcrumbs/index.tsx

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import {type ReactNode} from 'react';
2+
import clsx from 'clsx';
3+
import {ThemeClassNames} from '@docusaurus/theme-common';
4+
import {useSidebarBreadcrumbs} from '@docusaurus/plugin-content-docs/client';
5+
import {useHomePageRoute} from '@docusaurus/theme-common/internal';
6+
import Link from '@docusaurus/Link';
7+
import {translate} from '@docusaurus/Translate';
8+
import HomeBreadcrumbItem from '@theme/DocBreadcrumbs/Items/Home';
9+
10+
import styles from './styles.module.css';
11+
12+
function truncateMiddle(text: string, maxLength: number): string {
13+
if (text.length <= maxLength) return text;
14+
const charsToShow = maxLength - 3;
15+
const front = Math.ceil(charsToShow / 2);
16+
const back = Math.floor(charsToShow / 2);
17+
return text.substring(0, front) + '...' + text.substring(text.length - back);
18+
}
19+
20+
// TODO move to design system folder
21+
function BreadcrumbsItemLink({
22+
children,
23+
href,
24+
isLast,
25+
}: {
26+
children: ReactNode;
27+
href: string | undefined;
28+
isLast: boolean;
29+
}): ReactNode {
30+
const className = 'breadcrumbs__link';
31+
if (isLast) {
32+
return (
33+
<span className={className} itemProp="name">
34+
{children}
35+
</span>
36+
);
37+
}
38+
return href ? (
39+
<Link className={className} href={href} itemProp="item">
40+
<span itemProp="name">{children}</span>
41+
</Link>
42+
) : (
43+
// TODO Google search console doesn't like breadcrumb items without href.
44+
// The schema doesn't seem to require `id` for each `item`, although Google
45+
// insist to infer one, even if it's invalid. Removing `itemProp="item
46+
// name"` for now, since I don't know how to properly fix it.
47+
// See https://github.com/facebook/docusaurus/issues/7241
48+
<span className={className}>{children}</span>
49+
);
50+
}
51+
52+
// TODO move to design system folder
53+
function BreadcrumbsItem({
54+
children,
55+
active,
56+
index,
57+
addMicrodata,
58+
}: {
59+
children: ReactNode;
60+
active?: boolean;
61+
index: number;
62+
addMicrodata: boolean;
63+
}): ReactNode {
64+
return (
65+
<li
66+
{...(addMicrodata && {
67+
itemScope: true,
68+
itemProp: 'itemListElement',
69+
itemType: 'https://schema.org/ListItem',
70+
})}
71+
className={clsx('breadcrumbs__item', {
72+
'breadcrumbs__item--active': active,
73+
})}>
74+
{children}
75+
<meta itemProp="position" content={String(index + 1)} />
76+
</li>
77+
);
78+
}
79+
80+
export default function DocBreadcrumbs(): ReactNode {
81+
const breadcrumbs = useSidebarBreadcrumbs();
82+
const homePageRoute = useHomePageRoute();
83+
84+
if (!breadcrumbs) {
85+
return null;
86+
}
87+
88+
return (
89+
<nav
90+
className={clsx(
91+
ThemeClassNames.docs.docBreadcrumbs,
92+
styles.breadcrumbsContainer,
93+
)}
94+
aria-label={translate({
95+
id: 'theme.docs.breadcrumbs.navAriaLabel',
96+
message: 'Breadcrumbs',
97+
description: 'The ARIA label for the breadcrumbs',
98+
})}>
99+
<ul
100+
className="breadcrumbs"
101+
itemScope
102+
itemType="https://schema.org/BreadcrumbList">
103+
{homePageRoute && <HomeBreadcrumbItem />}
104+
{breadcrumbs.map((item, idx) => {
105+
const isLast = idx === breadcrumbs.length - 1;
106+
const href =
107+
item.type === 'category' && item.linkUnlisted
108+
? undefined
109+
: item.href;
110+
const label = !isLast ? truncateMiddle(item.label, 25) : truncateMiddle(item.label, 80)
111+
112+
return (
113+
<BreadcrumbsItem key={idx} active={isLast} index={idx} addMicrodata={!!href}>
114+
<BreadcrumbsItemLink href={href} isLast={isLast}>
115+
{label}
116+
</BreadcrumbsItemLink>
117+
</BreadcrumbsItem>
118+
);
119+
})}
120+
</ul>
121+
</nav>
122+
);
123+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.breadcrumbsContainer {
2+
--ifm-breadcrumb-size-multiplier: 0.8;
3+
margin-bottom: 0.8rem;
4+
}

0 commit comments

Comments
 (0)