mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
docs(docs-infra): prefer/avoid code block styling
fixes #65866
(cherry picked from commit 3411638f65)
This commit is contained in:
parent
5880fbc73c
commit
182116582e
9 changed files with 111 additions and 22 deletions
|
|
@ -205,6 +205,7 @@ export class DocViewer {
|
|||
const preview = Boolean(placeholder.getAttribute('preview'));
|
||||
const hideCode = Boolean(placeholder.getAttribute('hideCode'));
|
||||
const title = placeholder.getAttribute('header') ?? undefined;
|
||||
const style = placeholder.getAttribute('style') ?? undefined;
|
||||
const firstCodeSnippetTitle =
|
||||
snippets.length > 0 ? (snippets[0].title ?? snippets[0].name) : undefined;
|
||||
const exampleRef = this.viewContainer.createComponent(ExampleViewer);
|
||||
|
|
@ -217,6 +218,7 @@ export class DocViewer {
|
|||
preview,
|
||||
hideCode,
|
||||
id: this.countOfExamples,
|
||||
style,
|
||||
});
|
||||
|
||||
exampleRef.setInput('githubUrl', `${GITHUB_CONTENT_URL}/${snippets[0].name}`);
|
||||
|
|
|
|||
|
|
@ -347,6 +347,7 @@ const getMetadata = (value: Partial<ExampleMetadata> = {}): ExampleMetadata => {
|
|||
preview: false,
|
||||
hideCode: false,
|
||||
...value,
|
||||
style: undefined,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -42,4 +42,6 @@ export interface ExampleMetadata {
|
|||
preview: boolean;
|
||||
/** Whether to hide code example by default. */
|
||||
hideCode: boolean;
|
||||
/** Visual style for the code example */
|
||||
style?: 'prefer' | 'avoid';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ export interface DocsCodeBlock extends CodeToken {
|
|||
code: string;
|
||||
// Code language
|
||||
language: string | undefined;
|
||||
style: 'prefer' | 'avoid' | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -40,6 +41,7 @@ export const docsCodeBlockExtension = {
|
|||
const headerRule = /header\s*:\s*(['"`])([^'"`]+)\1/; // The 2nd capture matters here
|
||||
const highlightRule = /highlight\s*:\s*(.*)([^,])/;
|
||||
const hideCopyRule = /hideCopy/;
|
||||
const preferRule = /(prefer|avoid)/;
|
||||
|
||||
const token: DocsCodeBlock = {
|
||||
raw: match[0],
|
||||
|
|
@ -49,6 +51,7 @@ export const docsCodeBlockExtension = {
|
|||
header: headerRule.exec(metadataStr)?.[2],
|
||||
highlight: highlightRule.exec(metadataStr)?.[1],
|
||||
hideCopy: hideCopyRule.test(metadataStr),
|
||||
style: preferRule.exec(metadataStr)?.[1] as 'prefer' | 'avoid' | undefined,
|
||||
};
|
||||
return token;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ const visibleLinesRule = /visibleLines="([^"]*)"/;
|
|||
const regionRule = /region="([^"]*)"/;
|
||||
const previewRule = /preview/;
|
||||
const hideCodeRule = /hideCode/;
|
||||
const preferRule = /(prefer|avoid)/;
|
||||
|
||||
export const docsCodeExtension = {
|
||||
name: 'docs-code',
|
||||
|
|
@ -54,6 +55,7 @@ export const docsCodeExtension = {
|
|||
const preview = previewRule.exec(attr) ? true : false;
|
||||
const hideCode = hideCodeRule.exec(attr) ? true : false;
|
||||
const classes = classRule.exec(attr);
|
||||
const style = preferRule.exec(attr);
|
||||
|
||||
let code = match[2]?.trim() ?? '';
|
||||
if (path && path[1]) {
|
||||
|
|
@ -76,6 +78,7 @@ export const docsCodeExtension = {
|
|||
region: region?.[1],
|
||||
preview: preview,
|
||||
hideCode,
|
||||
style: style?.[1] as 'prefer' | 'avoid' | undefined,
|
||||
classes: classes?.[1]?.split(' '),
|
||||
};
|
||||
return token;
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ export function formatCode(token: CodeToken, context: RendererContext): string {
|
|||
highlightCode(context.highlighter, token);
|
||||
|
||||
const containerEl = JSDOM.fragment(`
|
||||
<div class="docs-code">
|
||||
<div class="docs-code${token.style ? ' docs-code-' + token.style : ''}">
|
||||
${buildHeaderElement(token)}
|
||||
${token.code}
|
||||
</div>
|
||||
|
|
@ -95,7 +95,18 @@ export function processForApiLinks(fragment: Element, apiEntries: ApiEntries): v
|
|||
|
||||
/** Build the header element if a header is provided in the token. */
|
||||
function buildHeaderElement(token: CodeToken) {
|
||||
return token.header ? `<div class="docs-code-header"><h3>${token.header}</h3></div>` : '';
|
||||
let header = '';
|
||||
if (token.style) {
|
||||
header += `<span class="docs-code-header-style ">${token.style === 'prefer' ? 'Prefer' : 'Avoid'}</span>`;
|
||||
}
|
||||
|
||||
if (token.header) {
|
||||
header += `<h3>${token.header}</h3>`;
|
||||
}
|
||||
|
||||
if (!header) return '';
|
||||
|
||||
return `<div class="docs-code-header">${header}</div>`;
|
||||
}
|
||||
|
||||
function applyContainerAttributesAndClasses(el: Element, token: CodeToken) {
|
||||
|
|
|
|||
|
|
@ -166,6 +166,18 @@ $code-font-size: 0.875rem;
|
|||
border-width: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
&.docs-code-avoid {
|
||||
--style-color: var(--hot-red);
|
||||
}
|
||||
|
||||
&.docs-code-prefer {
|
||||
--style-color: var(--symbolic-green);
|
||||
}
|
||||
&.docs-code-avoid,
|
||||
&.docs-code-prefer {
|
||||
border-color: color-mix(in srgb, var(--style-color) 40%, var(--senary-contrast));
|
||||
}
|
||||
}
|
||||
|
||||
.shiki {
|
||||
|
|
@ -278,18 +290,51 @@ $code-font-size: 0.875rem;
|
|||
}
|
||||
|
||||
.docs-code-header {
|
||||
display: flex;
|
||||
padding: 0.75rem;
|
||||
|
||||
// docs header background
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: var(--subtle-purple);
|
||||
border-radius: 0.25rem 0.25rem 0 0;
|
||||
transition: background 0.3s ease;
|
||||
background: var(--subtle-purple);
|
||||
border-radius: 0.25rem 0.25rem 0 0;
|
||||
transition: background 0.3s ease;
|
||||
|
||||
.docs-code-avoid &,
|
||||
.docs-code-prefer & {
|
||||
background: color-mix(in srgb, var(--style-color) 17%, var(--page-background));
|
||||
color: color-mix(in srgb, var(--style-color) 60%, var(--primary-contrast));
|
||||
h3 {
|
||||
color: color-mix(in srgb, var(--style-color) 60%, var(--primary-contrast));
|
||||
}
|
||||
}
|
||||
|
||||
.docs-code-header-style {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 0.8rem;
|
||||
|
||||
&::before {
|
||||
font-family: var(--icons);
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
&:not(:last-child)::after {
|
||||
content: '-';
|
||||
margin: 0 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.docs-code-header-style {
|
||||
.docs-code-prefer & {
|
||||
&::before {
|
||||
content: 'check';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.docs-code-header-style {
|
||||
.docs-code-avoid & {
|
||||
&::before {
|
||||
content: 'dangerous';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -219,16 +219,15 @@ export class UserProfile {
|
|||
|
||||
Prefer `class` and `style` bindings over using the [`NgClass`](/api/common/NgClass) and [`NgStyle`](/api/common/NgStyle) directives.
|
||||
|
||||
```html
|
||||
<!-- PREFER -->
|
||||
```html {prefer}
|
||||
<div [class.admin]="isAdmin" [class.dense]="density === 'high'">
|
||||
<div [style.color]="textColor" [style.background-color]="backgroundColor">
|
||||
<!-- OR -->
|
||||
<div [class]="{admin: isAdmin, dense: density === 'high'}">
|
||||
<div [style]="{'color': textColor, 'background-color': backgroundColor}">
|
||||
```
|
||||
|
||||
|
||||
<!-- AVOID -->
|
||||
```html {avoid}
|
||||
<div [ngClass]="{admin: isAdmin, dense: density === 'high'}">
|
||||
<div [ngStyle]="{'color': textColor, 'background-color': backgroundColor}">
|
||||
```
|
||||
|
|
@ -246,11 +245,11 @@ For more details, refer to the [bindings guide](/guide/templates/binding#css-cla
|
|||
|
||||
Prefer naming event handlers for the action they perform rather than for the triggering event:
|
||||
|
||||
```html
|
||||
<!-- PREFER -->
|
||||
```html {prefer}
|
||||
<button (click)="saveUserData()">Save</button>
|
||||
```
|
||||
|
||||
<!-- AVOID -->
|
||||
```html {avoid}
|
||||
<button (click)="handleClick()">Save</button>
|
||||
```
|
||||
|
||||
|
|
@ -291,14 +290,14 @@ well-named methods to contain that logic and then _call those methods_ in your l
|
|||
Lifecycle hook names describe _when_ they run, meaning that the code inside doesn't have a
|
||||
meaningful name that describes what the code inside is doing.
|
||||
|
||||
```typescript
|
||||
// PREFER
|
||||
```ts {prefer}
|
||||
ngOnInit() {
|
||||
this.startLogging();
|
||||
this.runBackgroundTask();
|
||||
}
|
||||
```
|
||||
|
||||
// AVOID
|
||||
```ts {avoid}
|
||||
ngOnInit() {
|
||||
this.logger.setMode('info');
|
||||
this.logger.monitorErrors();
|
||||
|
|
|
|||
|
|
@ -364,3 +364,26 @@ This can be used to separate page sections, like we're about to do below. These
|
|||
<hr/>
|
||||
|
||||
The end!
|
||||
|
||||
## Prefer / Avoid
|
||||
|
||||
```ts {prefer}
|
||||
const foo= 'bar';
|
||||
```
|
||||
|
||||
```ts {avoid}
|
||||
const bar = 'foo';
|
||||
```
|
||||
|
||||
```ts {avoid, header: 'with a header'}
|
||||
const baz = 42;
|
||||
```
|
||||
|
||||
<docs-code
|
||||
path="adev/src/content/examples/hello-world/src/app/app.component-old.ts"
|
||||
header="A styled code example"
|
||||
language='ts'
|
||||
linenums
|
||||
highlight="[[3,7], 9]"
|
||||
prefer>
|
||||
</docs-code>
|
||||
|
|
|
|||
Loading…
Reference in a new issue