Skip to content

Commit ae54201

Browse files
committed
Break navigation lines
Resolves #2940
1 parent f31aa35 commit ae54201

File tree

4 files changed

+57
-14
lines changed

4 files changed

+57
-14
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ title: Changelog
66

77
### Features
88

9+
- The navigation in the default theme will now attempt to break long names onto multiple lines, #2940.
910
- Added German (de) localization, #2941.
1011

1112
### Bug Fixes

src/lib/output/themes/default/assets/typedoc/Navigation.ts

+20-2
Original file line numberDiff line numberDiff line change
@@ -110,12 +110,30 @@ function addNavText(
110110
el.icon || el.kind
111111
}"></use></svg>`;
112112
}
113-
a.appendChild(document.createElement("span")).textContent = el.text;
113+
a.appendChild(wbr(el.text, document.createElement("span")));
114114
} else {
115115
const span = parent.appendChild(document.createElement("span"));
116116
const label = window.translations.folder.replaceAll('"', "&quot;");
117117
span.innerHTML =
118118
`<svg width="20" height="20" viewBox="0 0 24 24" fill="none" class="tsd-kind-icon" aria-label="${label}"><use href="#icon-folder"></use></svg>`;
119-
span.appendChild(document.createElement("span")).textContent = el.text;
119+
span.appendChild(wbr(el.text, document.createElement("span")));
120120
}
121121
}
122+
123+
function wbr(str: string, element: HTMLElement) {
124+
// Keep this in sync with the same helper in lib.tsx
125+
// We use lookahead/lookbehind to indicate where the string should
126+
// be split without consuming a character.
127+
// (?<=[^A-Z])(?=[A-Z]) -- regular camel cased text
128+
// (?<=[A-Z])(?=[A-Z][a-z]) -- acronym
129+
// (?<=[_-])(?=[^_-]) -- snake
130+
const parts = str.split(/(?<=[^A-Z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|(?<=[_-])(?=[^_-])/);
131+
for (let i = 0; i < parts.length; ++i) {
132+
if (i !== 0) {
133+
element.appendChild(document.createElement("wbr"));
134+
}
135+
element.appendChild(document.createTextNode(parts[i]));
136+
}
137+
138+
return element;
139+
}

src/lib/output/themes/lib.tsx

+8-12
Original file line numberDiff line numberDiff line change
@@ -49,18 +49,14 @@ export function getKindClass(refl: Reflection): string {
4949
* @return The original string containing ``<wbr>`` tags where possible.
5050
*/
5151
export function wbr(str: string): (string | JSX.Element)[] {
52-
// TODO surely there is a better way to do this, but I'm tired.
53-
const ret: (string | JSX.Element)[] = [];
54-
const re = /[\s\S]*?(?:[^_-][_-](?=[^_-])|[^A-Z](?=[A-Z][^A-Z]))/g;
55-
let match: RegExpExecArray | null;
56-
let i = 0;
57-
while ((match = re.exec(str))) {
58-
ret.push(match[0], <wbr />);
59-
i += match[0].length;
60-
}
61-
ret.push(str.slice(i));
62-
63-
return ret;
52+
// Keep this in sync with the same helper in Navigation.ts
53+
// We use lookahead/lookbehind to indicate where the string should
54+
// be split without consuming a character.
55+
// (?<=[^A-Z])(?=[A-Z]) -- regular camel cased text
56+
// (?<=[A-Z])(?=[A-Z][a-z]) -- acronym
57+
// (?<=[_-])(?=[^_-]) -- snake
58+
const parts = str.split(/(?<=[^A-Z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|(?<=[_-])(?=[^_-])/);
59+
return parts.flatMap(p => [p, <wbr />]).slice(0, -1);
6460
}
6561

6662
export function join<T>(joiner: JSX.Children, list: readonly T[], cb: (x: T) => JSX.Children) {

src/test/output/lib.test.tsx

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { deepStrictEqual as equal } from "node:assert";
2+
import { wbr } from "../../lib/output/themes/lib.js";
3+
import { JSX } from "#utils";
4+
5+
describe("wbr", () => {
6+
it("No breaks", () => {
7+
equal(wbr("hello"), ["hello"]);
8+
});
9+
10+
it("Adds <wbr> to camelCased text", () => {
11+
equal(wbr("helloWorld"), ["hello", <wbr />, "World"]);
12+
equal(wbr("helloWorldMulti"), ["hello", <wbr />, "World", <wbr />, "Multi"]);
13+
});
14+
15+
it("Adds <wbr> to snake_cased text", () => {
16+
equal(wbr("snake_case_text"), ["snake_", <wbr />, "case_", <wbr />, "text"]);
17+
equal(wbr("snake__case__text"), ["snake__", <wbr />, "case__", <wbr />, "text"]);
18+
});
19+
20+
it("Adds <wbr> to dashed-text", () => {
21+
equal(wbr("dashed-text"), ["dashed-", <wbr />, "text"]);
22+
});
23+
24+
it("Adds <wbr> appropriately with acronyms", () => {
25+
equal(wbr("HTMLImageElement"), ["HTML", <wbr />, "Image", <wbr />, "Element"]);
26+
equal(wbr("theHTMLImageElement"), ["the", <wbr />, "HTML", <wbr />, "Image", <wbr />, "Element"]);
27+
});
28+
});

0 commit comments

Comments
 (0)