Skip to content

Commit 9ffa0db

Browse files
committed
Add reflectionPreview template, rendering improvements
Resolves #2449
1 parent fbe939a commit 9ffa0db

File tree

8 files changed

+108
-28
lines changed

8 files changed

+108
-28
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# Unreleased
22

3+
### Features
4+
5+
- Added support for TypeScript 5.3, #2446.
6+
- TypeDoc will now render interfaces as code at the top of the page describing interfaces, #2449.
7+
This can be controlled through the new `DefaultThemeRenderContext.reflectionPreview` helper.
8+
- Improved type rendering to highlight keywords differently than symbols.
9+
310
### Bug Fixes
411

512
- Fixed automatic declaration file resolution on Windows, #2416.
@@ -11,6 +18,7 @@
1118
- `@example` tag titles will now be rendered in the example heading, #2440.
1219
- Correctly handle transient symbols in `@namespace`-created namespaces, #2444.
1320
- TypeDoc no longer displays the "Hierarchy" section if there is no inheritance hierarchy to display.
21+
- Direct links to individual signatures no longer results in the signature being partially scrolled off the screen.
1422

1523
### Thanks!
1624

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
"doc:c": "node bin/typedoc --tsconfig src/test/converter/tsconfig.json",
6767
"doc:c2": "node bin/typedoc --tsconfig src/test/converter2/tsconfig.json",
6868
"doc:c2d": "node --inspect-brk bin/typedoc --tsconfig src/test/converter2/tsconfig.json",
69+
"example": "cd example && node ../bin/typedoc",
6970
"test:full": "c8 mocha --config .config/mocha.full.json",
7071
"test:visual": "ts-node ./src/test/capture-screenshots.ts && ./scripts/compare_screenshots.sh",
7172
"test:visual:accept": "node scripts/accept_visual_regression.js",

src/lib/output/themes/default/DefaultThemeRenderContext.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import {
3939
sidebarLinks,
4040
} from "./partials/navigation";
4141
import { parameter } from "./partials/parameter";
42+
import { reflectionPreview } from "./partials/reflectionPreview";
4243
import { toolbar } from "./partials/toolbar";
4344
import { type } from "./partials/type";
4445
import { typeAndParent } from "./partials/typeAndParent";
@@ -117,6 +118,16 @@ export class DefaultThemeRenderContext {
117118
indexTemplate = bind(indexTemplate, this);
118119
defaultLayout = bind(defaultLayout, this);
119120

121+
/**
122+
* Rendered just after the description for a reflection.
123+
* This can be used to render a shortened type display of a reflection that the
124+
* rest of the page expands on.
125+
*
126+
* Note: Will not be called for variables/type aliases, as they are summarized
127+
* by their type declaration, which is already rendered by {@link DefaultThemeRenderContext.memberDeclaration}
128+
*/
129+
reflectionPreview = bind(reflectionPreview, this);
130+
120131
analytics = bind(analytics, this);
121132
breadcrumb = bind(breadcrumb, this);
122133
commentSummary = bind(commentSummary, this);

src/lib/output/themes/default/partials/member.signatures.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ export const memberSignatures = (context: DefaultThemeRenderContext, props: Decl
99
<ul class={classNames({ "tsd-signatures": true }, context.getReflectionClasses(props))}>
1010
{props.signatures?.map((item) => (
1111
<>
12-
<li class="tsd-signature tsd-anchor-link" id={item.anchor}>
12+
<li class="tsd-signature tsd-anchor-link">
13+
<a id={item.anchor} class="tsd-anchor"></a>
1314
{context.memberSignatureTitle(item)}
1415
{anchorIcon(context, item.anchor)}
1516
</li>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { DeclarationReflection, ReflectionKind, type Reflection, ReflectionType } from "../../../../models";
2+
import { JSX } from "../../../../utils";
3+
import { getKindClass } from "../../lib";
4+
import type { DefaultThemeRenderContext } from "../DefaultThemeRenderContext";
5+
6+
export function reflectionPreview(context: DefaultThemeRenderContext, props: Reflection) {
7+
if (!(props instanceof DeclarationReflection)) return;
8+
9+
// Each property of the interface will have a member rendered later on the page describing it, so generate
10+
// a type-like object with links to each member.
11+
if (props.kindOf(ReflectionKind.Interface)) {
12+
return (
13+
<div class="tsd-signature">
14+
<span class="tsd-signature-keyword">interface </span>
15+
<span class={getKindClass(props)}>{props.name} </span>
16+
{context.type(new ReflectionType(props), { topLevelLinks: true })}
17+
</div>
18+
);
19+
}
20+
}

src/lib/output/themes/default/partials/type.tsx

Lines changed: 52 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,11 @@ export function validateStateIsClean(page: string) {
8686
// 1 | 2[] !== (1 | 2)[]
8787
// () => 1 | 2 !== (() => 1) | 2
8888
const typeRenderers: {
89-
[K in keyof TypeKindMap]: (context: DefaultThemeRenderContext, type: TypeKindMap[K]) => JSX.Element;
89+
[K in keyof TypeKindMap]: (
90+
context: DefaultThemeRenderContext,
91+
type: TypeKindMap[K],
92+
options: { topLevelLinks: boolean },
93+
) => JSX.Element;
9094
} = {
9195
array(context, type) {
9296
return (
@@ -100,7 +104,7 @@ const typeRenderers: {
100104
indentationDepth++;
101105
const parts: JSX.Element[] = [
102106
renderType(context, type.checkType, TypeContext.conditionalCheck),
103-
<span class="tsd-signature-symbol"> extends </span>,
107+
<span class="tsd-signature-keyword"> extends </span>,
104108
renderType(context, type.extendsType, TypeContext.conditionalExtends),
105109
<br />,
106110
includeIndentation(),
@@ -142,11 +146,11 @@ const typeRenderers: {
142146
inferred(context, type) {
143147
return (
144148
<>
145-
<span class="tsd-signature-symbol">infer </span>{" "}
149+
<span class="tsd-signature-keyword">infer </span>{" "}
146150
<span class="tsd-kind-type-parameter">{type.name}</span>
147151
{type.constraint && (
148152
<>
149-
<span class="tsd-signature-symbol"> extends </span>
153+
<span class="tsd-signature-keyword"> extends </span>
150154
{renderType(context, type.constraint, TypeContext.inferredConstraint)}
151155
</>
152156
)}
@@ -170,23 +174,28 @@ const typeRenderers: {
170174

171175
switch (type.readonlyModifier) {
172176
case "+":
173-
parts.push(<span class="tsd-signature-symbol">readonly </span>);
177+
parts.push(<span class="tsd-signature-keyword">readonly </span>);
174178
break;
175179
case "-":
176-
parts.push(<span class="tsd-signature-symbol">-readonly </span>);
180+
parts.push(
181+
<>
182+
<span class="tsd-signature-symbol">-</span>
183+
<span class="tsd-signature-keyword">readonly </span>
184+
</>,
185+
);
177186
break;
178187
}
179188

180189
parts.push(
181190
<span class="tsd-signature-symbol">[</span>,
182191
<span class="tsd-kind-type-parameter">{type.parameter}</span>,
183-
<span class="tsd-signature-symbol"> in </span>,
192+
<span class="tsd-signature-keyword"> in </span>,
184193
renderType(context, type.parameterType, TypeContext.mappedParameter),
185194
);
186195

187196
if (type.nameType) {
188197
parts.push(
189-
<span class="tsd-signature-symbol"> as </span>,
198+
<span class="tsd-signature-keyword"> as </span>,
190199
renderType(context, type.nameType, TypeContext.mappedName),
191200
);
192201
}
@@ -241,11 +250,11 @@ const typeRenderers: {
241250
predicate(context, type) {
242251
return (
243252
<>
244-
{!!type.asserts && <span class="tsd-signature-symbol">asserts </span>}
253+
{!!type.asserts && <span class="tsd-signature-keyword">asserts </span>}
245254
<span class="tsd-kind-parameter">{type.name}</span>
246255
{!!type.targetType && (
247256
<>
248-
<span class="tsd-signature-symbol"> is </span>
257+
<span class="tsd-signature-keyword"> is </span>
249258
{renderType(context, type.targetType, TypeContext.predicateTarget)}
250259
</>
251260
)}
@@ -255,7 +264,7 @@ const typeRenderers: {
255264
query(context, type) {
256265
return (
257266
<>
258-
<span class="tsd-signature-symbol">typeof </span>
267+
<span class="tsd-signature-keyword">typeof </span>
259268
{renderType(context, type.queryType, TypeContext.queryTypeTarget)}
260269
</>
261270
);
@@ -299,17 +308,26 @@ const typeRenderers: {
299308

300309
return name;
301310
},
302-
reflection(context, type) {
311+
reflection(context, type, { topLevelLinks }) {
303312
const members: JSX.Element[] = [];
304313
const children: DeclarationReflection[] = type.declaration.children || [];
305314

306315
indentationDepth++;
307316

317+
const renderName = (named: Reflection) =>
318+
topLevelLinks ? (
319+
<a class={getKindClass(named)} href={context.urlTo(named)}>
320+
{named.name}
321+
</a>
322+
) : (
323+
<span class={getKindClass(named)}>{named.name}</span>
324+
);
325+
308326
for (const item of children) {
309327
if (item.getSignature && item.setSignature) {
310328
members.push(
311329
<>
312-
<span class={getKindClass(item)}>{item.name}</span>
330+
{renderName(item)}
313331
<span class="tsd-signature-symbol">: </span>
314332
{renderType(context, item.getSignature.type, TypeContext.none)}
315333
</>,
@@ -320,8 +338,8 @@ const typeRenderers: {
320338
if (item.getSignature) {
321339
members.push(
322340
<>
323-
<span class="tsd-signature-symbol">get </span>
324-
<span class={getKindClass(item.getSignature)}>{item.name}</span>
341+
<span class="tsd-signature-keyword">get </span>
342+
{renderName(item.getSignature)}
325343
<span class="tsd-signature-symbol">(): </span>
326344
{renderType(context, item.getSignature.type, TypeContext.none)}
327345
</>,
@@ -332,8 +350,8 @@ const typeRenderers: {
332350
if (item.setSignature) {
333351
members.push(
334352
<>
335-
<span class="tsd-signature-symbol">set </span>
336-
<span class={getKindClass(item.setSignature)}>{item.name}</span>
353+
<span class="tsd-signature-keyword">set </span>
354+
{renderName(item.setSignature)}
337355
<span class="tsd-signature-symbol">(</span>
338356
{item.setSignature.parameters?.map((item) => (
339357
<>
@@ -352,11 +370,11 @@ const typeRenderers: {
352370
for (const sig of item.signatures) {
353371
members.push(
354372
<>
355-
<span class={getKindClass(sig)}>{item.name}</span>
373+
{renderName(sig)}
356374
{item.flags.isOptional && <span class="tsd-signature-symbol">?</span>}
357375
{context.memberSignatureTitle(sig, {
358376
hideName: true,
359-
arrowStyle: true,
377+
arrowStyle: false,
360378
})}
361379
</>,
362380
);
@@ -366,7 +384,7 @@ const typeRenderers: {
366384

367385
members.push(
368386
<>
369-
<span class={getKindClass(item)}>{item.name}</span>
387+
{renderName(item)}
370388
<span class="tsd-signature-symbol">{item.flags.isOptional ? "?: " : ": "}</span>
371389
{renderType(context, item.type, TypeContext.none)}
372390
</>,
@@ -468,7 +486,7 @@ const typeRenderers: {
468486
typeOperator(context, type) {
469487
return (
470488
<>
471-
<span class="tsd-signature-symbol">{type.operator} </span>
489+
<span class="tsd-signature-keyword">{type.operator} </span>
472490
{renderType(context, type.target, TypeContext.typeOperatorTarget)}
473491
</>
474492
);
@@ -483,13 +501,18 @@ const typeRenderers: {
483501
},
484502
};
485503

486-
function renderType(context: DefaultThemeRenderContext, type: Type | undefined, where: TypeContext) {
504+
function renderType(
505+
context: DefaultThemeRenderContext,
506+
type: Type | undefined,
507+
where: TypeContext,
508+
options: { topLevelLinks: boolean } = { topLevelLinks: false },
509+
) {
487510
if (!type) {
488511
return <span class="tsd-signature-type">any</span>;
489512
}
490513

491514
const renderFn = typeRenderers[type.type];
492-
const rendered = renderFn(context, type as never);
515+
const rendered = renderFn(context, type as never, options);
493516

494517
if (type.needsParenthesis(where)) {
495518
return (
@@ -504,6 +527,10 @@ function renderType(context: DefaultThemeRenderContext, type: Type | undefined,
504527
return rendered;
505528
}
506529

507-
export function type(context: DefaultThemeRenderContext, type: Type | undefined) {
508-
return renderType(context, type, TypeContext.none);
530+
export function type(
531+
context: DefaultThemeRenderContext,
532+
type: Type | undefined,
533+
options: { topLevelLinks: boolean } = { topLevelLinks: false },
534+
) {
535+
return renderType(context, type, TypeContext.none, options);
509536
}

src/lib/output/themes/default/templates/reflection.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { JSX, Raw } from "../../../../utils";
66

77
export function reflectionTemplate(context: DefaultThemeRenderContext, props: PageEvent<ContainerReflection>) {
88
if (
9-
[ReflectionKind.TypeAlias, ReflectionKind.Variable].includes(props.model.kind) &&
9+
props.model.kindOf(ReflectionKind.TypeAlias | ReflectionKind.Variable) &&
1010
props.model instanceof DeclarationReflection
1111
) {
1212
return context.memberDeclaration(props.model);
@@ -20,7 +20,6 @@ export function reflectionTemplate(context: DefaultThemeRenderContext, props: Pa
2020
{context.commentTags(props.model)}
2121
</section>
2222
)}
23-
2423
{props.model instanceof DeclarationReflection &&
2524
props.model.kind === ReflectionKind.Module &&
2625
props.model.readme?.length && (
@@ -29,6 +28,8 @@ export function reflectionTemplate(context: DefaultThemeRenderContext, props: Pa
2928
</section>
3029
)}
3130

31+
{context.reflectionPreview(props.model)}
32+
3233
{hasTypeParameters(props.model) && <> {context.typeParameters(props.model.typeParameters)} </>}
3334
{props.model instanceof DeclarationReflection && (
3435
<>

static/style.css

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
--light-color-text-aside: #6e6e6e;
1212
--light-color-link: #1f70c2;
1313

14+
--light-color-ts-keyword: #056bd6;
1415
--light-color-ts-project: #b111c9;
1516
--light-color-ts-module: var(--light-color-ts-project);
1617
--light-color-ts-namespace: var(--light-color-ts-project);
@@ -50,6 +51,7 @@
5051
--dark-color-text-aside: #dddddd;
5152
--dark-color-link: #00aff4;
5253

54+
--dark-color-ts-keyword: #3399ff;
5355
--dark-color-ts-project: #e358ff;
5456
--dark-color-ts-module: var(--dark-color-ts-project);
5557
--dark-color-ts-namespace: var(--dark-color-ts-project);
@@ -91,6 +93,7 @@
9193
--color-text-aside: var(--light-color-text-aside);
9294
--color-link: var(--light-color-link);
9395

96+
--color-ts-keyword: var(--light-color-ts-keyword);
9497
--color-ts-module: var(--light-color-ts-module);
9598
--color-ts-namespace: var(--light-color-ts-namespace);
9699
--color-ts-enum: var(--light-color-ts-enum);
@@ -132,6 +135,7 @@
132135
--color-text-aside: var(--dark-color-text-aside);
133136
--color-link: var(--dark-color-link);
134137

138+
--color-ts-keyword: var(--dark-color-ts-keyword);
135139
--color-ts-module: var(--dark-color-ts-module);
136140
--color-ts-namespace: var(--dark-color-ts-namespace);
137141
--color-ts-enum: var(--dark-color-ts-enum);
@@ -180,6 +184,7 @@ body {
180184
--color-text-aside: var(--light-color-text-aside);
181185
--color-link: var(--light-color-link);
182186

187+
--color-ts-keyword: var(--light-color-ts-keyword);
183188
--color-ts-module: var(--light-color-ts-module);
184189
--color-ts-namespace: var(--light-color-ts-namespace);
185190
--color-ts-enum: var(--light-color-ts-enum);
@@ -219,6 +224,7 @@ body {
219224
--color-text-aside: var(--dark-color-text-aside);
220225
--color-link: var(--dark-color-link);
221226

227+
--color-ts-keyword: var(--dark-color-ts-keyword);
222228
--color-ts-module: var(--dark-color-ts-module);
223229
--color-ts-namespace: var(--dark-color-ts-namespace);
224230
--color-ts-enum: var(--dark-color-ts-enum);
@@ -984,6 +990,11 @@ a.tsd-index-link {
984990
overflow-x: auto;
985991
}
986992

993+
.tsd-signature-keyword {
994+
color: var(--color-ts-keyword);
995+
font-weight: normal;
996+
}
997+
987998
.tsd-signature-symbol {
988999
color: var(--color-text-aside);
9891000
font-weight: normal;

0 commit comments

Comments
 (0)