Skip to content

Commit 6e62a30

Browse files
committed
feat: API Page Styling
- Add API Summary section at top of page - Fix dark mode on learn page - Add all API doc sections - Fix API object typings
1 parent ed1e003 commit 6e62a30

File tree

5 files changed

+230
-117
lines changed

5 files changed

+230
-117
lines changed

src/components/header.tsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,15 @@ const Header = () => (
2323
</Link>
2424
</li>
2525
<li className="nav__tabs">
26-
<a href="/learn">Learn</a>
26+
<Link to="/learn">Learn</Link>
2727
</li>
2828
<li className="nav__tabs">
29-
<a href="/docs">Documentation</a>
29+
<Link to="/docs">Documentation</Link>
3030
</li>
3131
<li className="nav__tabs">
32-
<a href="/download">Download</a>
32+
<Link to="/download">Download</Link>
3333
</li>
34-
<li style={{ flexGrow: 1 }}></li>
34+
<li style={{ flexGrow: 1 }} />
3535
<li className="nav__tabs nav__tabs--right">
3636
<button
3737
className="dark-mode-toggle"
@@ -46,7 +46,12 @@ const Header = () => (
4646
<li className="nav__tabs">
4747
<a target="_blank" href="https://github.com/nodejs/nodejs.dev">
4848
<span className="sr-only">GitHub</span>
49-
<svg width="2.4rem" height="2.4rem" viewBox="0 0 438.549 438.549">
49+
<svg
50+
width="2.4rem"
51+
height="2.4rem"
52+
viewBox="0 0 438.549 438.549"
53+
style={{ fill: 'var(--color-text-accent)' }}
54+
>
5055
<path d="M409.132,114.573c-19.608-33.596-46.205-60.194-79.798-79.8C295.736,15.166,259.057,5.365,219.271,5.365 c-39.781,0-76.472,9.804-110.063,29.408c-33.596,19.605-60.192,46.204-79.8,79.8C9.803,148.168,0,184.854,0,224.63 c0,47.78,13.94,90.745,41.827,128.906c27.884,38.164,63.906,64.572,108.063,79.227c5.14,0.954,8.945,0.283,11.419-1.996 c2.475-2.282,3.711-5.14,3.711-8.562c0-0.571-0.049-5.708-0.144-15.417c-0.098-9.709-0.144-18.179-0.144-25.406l-6.567,1.136 c-4.187,0.767-9.469,1.092-15.846,1c-6.374-0.089-12.991-0.757-19.842-1.999c-6.854-1.231-13.229-4.086-19.13-8.559 c-5.898-4.473-10.085-10.328-12.56-17.556l-2.855-6.57c-1.903-4.374-4.899-9.233-8.992-14.559 c-4.093-5.331-8.232-8.945-12.419-10.848l-1.999-1.431c-1.332-0.951-2.568-2.098-3.711-3.429c-1.142-1.331-1.997-2.663-2.568-3.997 c-0.572-1.335-0.098-2.43,1.427-3.289c1.525-0.859,4.281-1.276,8.28-1.276l5.708,0.853c3.807,0.763,8.516,3.042,14.133,6.851 c5.614,3.806,10.229,8.754,13.846,14.842c4.38,7.806,9.657,13.754,15.846,17.847c6.184,4.093,12.419,6.136,18.699,6.136 c6.28,0,11.704-0.476,16.274-1.423c4.565-0.952,8.848-2.383,12.847-4.285c1.713-12.758,6.377-22.559,13.988-29.41 c-10.848-1.14-20.601-2.857-29.264-5.14c-8.658-2.286-17.605-5.996-26.835-11.14c-9.235-5.137-16.896-11.516-22.985-19.126 c-6.09-7.614-11.088-17.61-14.987-29.979c-3.901-12.374-5.852-26.648-5.852-42.826c0-23.035,7.52-42.637,22.557-58.817 c-7.044-17.318-6.379-36.732,1.997-58.24c5.52-1.715,13.706-0.428,24.554,3.853c10.85,4.283,18.794,7.952,23.84,10.994 c5.046,3.041,9.089,5.618,12.135,7.708c17.705-4.947,35.976-7.421,54.818-7.421s37.117,2.474,54.823,7.421l10.849-6.849 c7.419-4.57,16.18-8.758,26.262-12.565c10.088-3.805,17.802-4.853,23.134-3.138c8.562,21.509,9.325,40.922,2.279,58.24 c15.036,16.18,22.559,35.787,22.559,58.817c0,16.178-1.958,30.497-5.853,42.966c-3.9,12.471-8.941,22.457-15.125,29.979 c-6.191,7.521-13.901,13.85-23.131,18.986c-9.232,5.14-18.182,8.85-26.84,11.136c-8.662,2.286-18.415,4.004-29.263,5.146 c9.894,8.562,14.842,22.077,14.842,40.539v60.237c0,3.422,1.19,6.279,3.572,8.562c2.379,2.279,6.136,2.95,11.276,1.995 c44.163-14.653,80.185-41.062,108.068-79.226c27.88-38.161,41.825-81.126,41.825-128.906 C438.536,184.851,428.728,148.168,409.132,114.573z" />
5156
</svg>
5257
</a>

src/components/layout.tsx

Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import '../styles/layout.css';
77
import '../styles/mobile.css';
88
import SEO from './seo';
99
import { isMobileScreen } from '../util/isScreenWithinWidth';
10-
import { notifyWhenStickyHeadersChange } from '../util/notifyWhenStickyHeadersChange';
1110
import { StickyChange, SentinelObserverSetupOptions } from '../types';
1211
import {
1312
addFocusOutlineListeners,
@@ -24,49 +23,6 @@ type Props = {
2423
const Layout = ({ children, title, description, img }: Props) => {
2524
const prevOffset = useRef<number>(-1);
2625

27-
useEffect(() => {
28-
if (window.document && 'documentElement' in window.document) {
29-
addFocusOutlineListeners();
30-
}
31-
if ('IntersectionObserver' in window) {
32-
setupObserver();
33-
} else {
34-
// Use polyfill for browsers without IntersectionObserver support
35-
import('intersection-observer')
36-
.then(setupObserver)
37-
// Fallback for browsers without IntersectionObserver support
38-
.catch(magicHeroNumber);
39-
}
40-
41-
return cleanUp;
42-
});
43-
44-
const setupObserver = (): void => {
45-
const container: HTMLElement = document.querySelector(
46-
'.side-nav'
47-
) as HTMLElement;
48-
const stickyElementsClassName: string = 'side-nav__title';
49-
const root: HTMLElement | null = isMobileScreen() ? null : container;
50-
const headerRootMargin: string = '-93px 0px 0px 0px';
51-
const setupOptions: SentinelObserverSetupOptions = {
52-
container,
53-
stickyElementsClassName,
54-
root,
55-
headerRootMargin,
56-
};
57-
if (container) {
58-
notifyWhenStickyHeadersChange(setupOptions);
59-
}
60-
61-
document.addEventListener('stickychange', (({
62-
detail,
63-
}: CustomEvent<StickyChange>) => {
64-
const { target, stuck }: StickyChange = detail;
65-
// Update sticking header color
66-
target.style.color = stuck ? '#fff' : '#000';
67-
}) as EventListener);
68-
};
69-
7026
const magicHeroNumber = (): void => {
7127
if (typeof window === 'undefined') {
7228
// Guard for SSR
@@ -90,10 +46,6 @@ const Layout = ({ children, title, description, img }: Props) => {
9046
window.requestAnimationFrame(magicHeroNumber);
9147
};
9248

93-
const cleanUp = (): void => {
94-
return removeFocusOutlineListeners();
95-
};
96-
9749
return (
9850
<>
9951
<SEO title={title} description={description} img={img} />

src/hooks/useApiDocs.tsx

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,28 +11,47 @@ export interface ApiDocsBase {
1111
desc?: string;
1212
meta?: ApiDocsMeta;
1313
introduced_in?: string;
14+
modules?: ApiDocsModule[];
15+
events?: ApiDocsEvent[];
16+
methods?: ApiDocsMethod[];
17+
classes?: ApiDocsClass[];
18+
properties?: ApiDocsProp[];
19+
miscs?: ApiDocsMiscs[];
1420
}
1521

1622
export interface ApiDocsMiscs extends ApiDocsBase {
1723
type: 'misc';
18-
miscs?: ApiDocsMiscs[];
1924
}
2025

2126
export interface ApiDocsMethod extends ApiDocsBase {
2227
type: 'method';
2328
signatures: ApiDocsSignature[];
2429
}
30+
31+
export function isMethodObj(obj: ApiDocsObj): obj is ApiDocsMethod {
32+
return obj.type === 'method';
33+
}
34+
2535
export interface ApiDocsSignature extends ApiDocsBase {
2636
params: ApiDocsProp[];
2737
}
2838

39+
export interface ApiDocsEvent extends ApiDocsBase {
40+
type: 'event';
41+
}
42+
export function isEventObj(obj: ApiDocsObj): obj is ApiDocsClass {
43+
return obj.type === 'event';
44+
}
45+
2946
export interface ApiDocsClass extends ApiDocsBase {
3047
type: 'class';
31-
methods: ApiDocsMethod[];
32-
properties: ApiDocsProp[];
3348
signatures: ApiDocsSignature[];
3449
}
3550

51+
export function isClassObj(obj: ApiDocsObj): obj is ApiDocsClass {
52+
return obj.type === 'class';
53+
}
54+
3655
export interface ApiDocsProp extends ApiDocsBase {
3756
type: string;
3857
}
@@ -41,11 +60,10 @@ export interface ApiDocsModule extends ApiDocsBase {
4160
type: 'module';
4261
stability: number;
4362
stabilityText: string;
44-
modules?: ApiDocsModule[];
45-
methods?: ApiDocsMethod[];
46-
classes?: ApiDocsClass[];
47-
properties?: ApiDocsProp[];
48-
miscs?: ApiDocsMiscs[];
63+
}
64+
65+
export function isModuleObj(obj: ApiDocsObj): obj is ApiDocsModule {
66+
return obj.type === 'module';
4967
}
5068

5169
export interface APIResponse {

src/pages/docs.tsx

Lines changed: 65 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,68 @@
11
import React, { useState } from 'react';
22
import { useApiData, useReleaseHistory } from '../hooks';
3-
import { ApiDocsObj, APIResponse } from '../hooks/useApiDocs';
3+
import { ApiDocsObj, APIResponse, isModuleObj } from '../hooks/useApiDocs';
44
import Layout from '../components/layout';
55

6+
function renderArticleOverview(obj: ApiDocsObj, overview: JSX.Element[] = []) {
7+
const children: JSX.Element[] = [];
8+
if (obj.events) {
9+
obj.events.map(evt => renderArticleOverview(evt, children));
10+
}
11+
if (obj.methods) {
12+
obj.methods.map(method => renderArticleOverview(method, children));
13+
}
14+
if (obj.properties) {
15+
obj.properties
16+
.filter(o => o.type !== 'Object')
17+
.map(prop => renderArticleOverview(prop, children));
18+
19+
obj.properties
20+
.filter(o => o.type === 'Object')
21+
.map(prop => renderArticleOverview(prop, children));
22+
}
23+
if (obj.classes) {
24+
obj.classes.map(klass => renderArticleOverview(klass, children));
25+
}
26+
27+
overview.push(
28+
<li
29+
className={`api-key__item api-key__item--${obj.type} ${
30+
children.length ? 'api-key__item--has-children' : ''
31+
}`}
32+
key={obj.name}
33+
>
34+
<a href={`#${obj.name}`} className="t-body1">
35+
{obj.displayName || obj.name}
36+
</a>
37+
{children.length ? (
38+
<ul className="api-key__section">{children}</ul>
39+
) : (
40+
undefined
41+
)}
42+
</li>
43+
);
44+
45+
return overview;
46+
}
47+
48+
function renderArticle(page: ApiDocsObj | null) {
49+
if (!page) {
50+
return <article>No Page Found</article>;
51+
}
52+
53+
return (
54+
<article style={{ width: '100%' }} className="article-reader">
55+
<h1>{page.displayName || page.name}</h1>
56+
<ul className="api-key">
57+
{renderArticleOverview(page)}
58+
{page.modules &&
59+
page.modules.map(mod => renderArticleOverview(mod, []))}
60+
</ul>
61+
{page.desc && <p dangerouslySetInnerHTML={{ __html: page.desc }} />}
62+
</article>
63+
);
64+
}
65+
666
function sideBarSection(
767
title: string,
868
section: keyof APIResponse,
@@ -59,14 +119,13 @@ export default () => {
59119
</select>
60120
</li>
61121
{sideBarSection('Globals', 'globals', apiData, setPage)}
122+
{sideBarSection('Methods', 'methods', apiData, setPage)}
123+
{sideBarSection('Misc', 'miscs', apiData, setPage)}
62124
{sideBarSection('Modules', 'modules', apiData, setPage)}
125+
{sideBarSection('Classes', 'classes', apiData, setPage)}
63126
</ul>
64127
</nav>
65-
<article style={{ width: '100%' }} className="article-reader">
66-
{page && page.desc && (
67-
<p dangerouslySetInnerHTML={{ __html: page.desc }} />
68-
)}
69-
</article>
128+
{renderArticle(page)}
70129
</Layout>
71130
);
72131
};

0 commit comments

Comments
 (0)