angular/adev/shared-docs/pipeline/api-gen/rendering/templates/header-api.tsx
Joey Perrott 5f1c08d75f build: migrate adev shared-docs package to use ts_project (#61193)
Migrate the build rules for shared docs to use ts_project

PR Close #61193
2025-05-09 16:30:05 +00:00

159 lines
4.7 KiB
TypeScript

/*!
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import {Fragment, h} from 'preact';
import {EntryType, isDocEntryWithSourceInfo, PipeEntry} from '../entities.mjs';
import {DocEntryRenderable, PipeEntryRenderable} from '../entities/renderables.mjs';
import {
HEADER_CLASS_NAME,
HEADER_ENTRY_CATEGORY,
HEADER_ENTRY_LABEL,
HEADER_ENTRY_TITLE,
} from '../styling/css-classes.mjs';
import {DocsPillRow} from './docs-pill-row';
/** Component to render a header of the API page. */
export function HeaderApi(props: {
entry: DocEntryRenderable | PipeEntryRenderable;
showFullDescription?: boolean;
}) {
const entry = props.entry;
// TODO: This link point to the main branch.
// When ADEV is not deployed on the main branch branch anymore,
// We should update it to point to the tag of the released version which ADEV runs on.
const sourceUrl = sourceUrlForEntry(entry);
return (
<header className={HEADER_CLASS_NAME}>
<span className={HEADER_ENTRY_CATEGORY}>{entry.moduleName}</span>
<div className={HEADER_ENTRY_TITLE}>
<div>
<h1>{entry.name}</h1>
<div className={`${HEADER_ENTRY_LABEL} type-${entry.entryType.toLowerCase()} full`}>
{getEntryTypeDisplayName(entry.entryType)}
</div>
{statusTag(entry)}
{entry.entryType === EntryType.Pipe && !(entry as PipeEntry).isPure && (
<div className={`${HEADER_ENTRY_LABEL} type-impure-pipe full`}>Impure</div>
)}
</div>
{sourceUrl && (
<a
class="docs-github-links"
target="_blank"
href={sourceUrl}
title="View source"
aria-label="View source"
>
<i role="presentation" aria-hidden="true" class="material-symbols-outlined">
code
</i>
</a>
)}
</div>
<section
className={'docs-reference-description'}
dangerouslySetInnerHTML={{
__html: props.showFullDescription ? entry.htmlDescription : entry.shortHtmlDescription,
}}
></section>
<DocsPillRow links={entry.additionalLinks} />
</header>
);
}
function statusTag(entry: DocEntryRenderable) {
let tag: h.JSX.HTMLAttributes<HTMLDivElement> | null = null;
// Cascading Deprecated > Stable > Developer Preview > Experimental
if (entry.deprecated) {
tag = (
<div className={`${HEADER_ENTRY_LABEL} type-stable full`}>
{tagInVersionString('deprecated', entry.deprecated)}
</div>
);
} else if (entry.stable) {
tag = (
<div className={`${HEADER_ENTRY_LABEL} type-stable full`}>
{tagInVersionString('stable', entry.stable)}
</div>
);
} else if (entry.developerPreview) {
tag = (
<div className={`${HEADER_ENTRY_LABEL} type-developer_preview full`}>
<a href="/reference/releases#developer-preview">
{tagInVersionString('developer preview', entry.developerPreview)}
</a>
</div>
);
} else if (entry.experimental) {
tag = (
<div className={`${HEADER_ENTRY_LABEL} type-experimental full`}>
<a href="/reference/releases#experimental">
{tagInVersionString('experimental', entry.experimental)}
</a>
</div>
);
}
return tag;
}
function tagInVersionString(label: string, tag: {version: string | undefined} | undefined) {
if (tag?.version) {
return (
<>
<span className="status-label">{label}</span>
<span className="status-version">since v{tag.version}</span>
</>
);
}
return <>{label}</>;
}
function tagInVersionTooltip(
label: string,
tag: {version: string | undefined} | undefined,
): string {
return tag?.version ? `${label} since ${tag.version}` : label;
}
function getEntryTypeDisplayName(entryType: EntryType | string): string {
switch (entryType) {
case EntryType.NgModule:
return 'NgModule';
case EntryType.TypeAlias:
return 'Type Alias';
case EntryType.UndecoratedClass:
return 'Class';
case EntryType.InitializerApiFunction:
return 'Initializer API';
}
return entryType;
}
function sourceUrlForEntry(entry: DocEntryRenderable): string | null {
if (!isDocEntryWithSourceInfo(entry)) {
return null;
}
if (entry.source.filePath.includes('node_modules')) {
// We don't know the source path in external repos link the CLI
return null;
} else {
const filePath = entry.source.filePath.replace(/^\//, '');
return `https://github.com/${entry.repo}/blob/main/${filePath}#L${entry.source.startLine}-L${entry.source.endLine}`;
}
}