Skip to content

docs(www): enhance code blocks #4916

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 28 additions & 2 deletions projects/www/src/app/components/docs/code-example.component.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
import { Component, Input } from '@angular/core';
import { Component, inject, Input, PLATFORM_ID, signal } from '@angular/core';
import { isPlatformServer } from '@angular/common';
import { CodeHighlightPipe } from './code-highlight.pipe';
import { ExamplesService } from '@ngrx-io/app/examples/examples.service';

@Component({
selector: 'ngrx-code-example',
standalone: true,
imports: [CodeHighlightPipe],
template: `
<div class="header">{{ header }}</div>
<div class="body">
<ng-content></ng-content>
@if(path) {
<div [innerHTML]="codeContent() | ngrxCodeHighlight"></div>
}@else{
<ng-content />
}
</div>
`,
styles: [
Expand Down Expand Up @@ -35,4 +43,22 @@ import { Component, Input } from '@angular/core';
})
export class CodeExampleComponent {
@Input() header: string = '';
@Input() path: string = '';
@Input() region: string = '';
@Input() language: string = 'typescript';

private exampleService = inject(ExamplesService);
private platformId = inject(PLATFORM_ID);
protected codeContent = signal('');

async ngAfterViewInit(): Promise<void> {
if (isPlatformServer(this.platformId)) return;
if (!this.path) return;

const content = await this.exampleService.extractSnippet(
this.path,
this.region
);
this.codeContent.set(content);
}
}
31 changes: 24 additions & 7 deletions projects/www/src/app/components/docs/code-highlight.pipe.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,33 @@
import { Pipe, PipeTransform } from '@angular/core';
import hljs from 'highlight.js/lib/core';
import typescript from 'highlight.js/lib/languages/typescript';
import { inject, Pipe, PipeTransform } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { ngrxTheme } from '@ngrx-io/shared/ngrx-shiki-theme';
import {
BundledLanguage,
BundledTheme,
getHighlighter,
HighlighterGeneric,
} from 'shiki';

hljs.registerLanguage('typescript', typescript);
let highlighter: HighlighterGeneric<BundledLanguage, BundledTheme>;
getHighlighter({
langs: ['typescript'],
themes: [ngrxTheme],
}).then((h) => (highlighter = h));

@Pipe({
name: 'ngrxCodeHighlight',
pure: true,
standalone: true,
pure: true,
})
export class CodeHighlightPipe implements PipeTransform {
transform(code: string): string {
return hljs.highlight(code, { language: 'typescript' }).value;
private sanitizer = inject(DomSanitizer);

transform(code: string): SafeHtml {
const html = highlighter?.codeToHtml(code, {
lang: 'typescript',
theme: 'ngrx-theme',
});

return this.sanitizer.bypassSecurityTrustHtml(html);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Component } from '@angular/core';

@Component({
selector: 'app-root',
template: `
<paginator
[length]="100"
[pageSize]="10"
[pageSizeOptions]="[5, 10, 25, 100]"
(page)="log($event)"
>
</paginator>
`,
})
export class AppComponent {
log(obj: unknown) {
console.log(obj);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { NgModule } from '@angular/core';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ReactiveFormsModule } from '@angular/forms';
import { MatNativeDateModule } from '@angular/material/core';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatSelectModule } from '@angular/material/select';
import { MatTooltipModule } from '@angular/material/tooltip';

import { AppComponent } from './app.component';
import { PaginatorComponent } from './paginator.component';

@NgModule({
imports: [
BrowserAnimationsModule,
MatNativeDateModule,
ReactiveFormsModule,
MatFormFieldModule,
MatSelectModule,
MatTooltipModule,
],
declarations: [AppComponent, PaginatorComponent],
bootstrap: [AppComponent],
})
export class AppModule {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import {
Component,
Input,
ChangeDetectionStrategy,
Output,
ViewEncapsulation,
} from '@angular/core';
import { PaginatorStore } from './paginator.store';

@Component({
selector: 'paginator',
templateUrl: 'paginator.html',
host: {
class: 'mat-paginator',
},
styleUrls: ['./paginator.scss'],
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [PaginatorStore],
})
export class PaginatorComponent {
// #docregion inputs
@Input() set pageIndex(value: string | number) {
this.paginatorStore.setPageIndex(value);
}

@Input() set length(value: string | number) {
this.paginatorStore.setLength(value);
}

@Input() set pageSize(value: string | number) {
this.paginatorStore.setPageSize(value);
}

@Input() set pageSizeOptions(value: readonly number[]) {
this.paginatorStore.setPageSizeOptions(value);
}
// #enddocregion inputs

// #docregion selectors
// Outputing the event directly from the page$ Observable<PageEvent> property.
/** Event emitted when the paginator changes the page size or page index. */
@Output() readonly page = this.paginatorStore.page$;

// ViewModel for the PaginatorComponent
readonly vm$ = this.paginatorStore.vm$;
// #enddocregion selectors

constructor(private readonly paginatorStore: PaginatorStore) {}

// #docregion updating-state
changePageSize(newPageSize: number) {
this.paginatorStore.changePageSize(newPageSize);
}
nextPage() {
this.paginatorStore.nextPage();
}
firstPage() {
this.paginatorStore.firstPage();
}
previousPage() {
this.paginatorStore.previousPage();
}
lastPage() {
this.paginatorStore.lastPage();
}
// #enddocregion updating-state
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<div class="mat-paginator-outer-container" *ngIf="vm$ | async as vm">
<div class="mat-paginator-container">
<div class="mat-paginator-page-size">
<div class="mat-paginator-page-size-label">
Items per page
</div>

<mat-form-field
*ngIf="vm.pageSizeOptions.length > 1"
class="mat-paginator-page-size-select">
<mat-select
[value]="vm.pageSize"
(selectionChange)="changePageSize($any($event).value)">
<mat-option *ngFor="let pageSizeOption of vm.pageSizeOptions" [value]="pageSizeOption">
{{pageSizeOption}}
</mat-option>
</mat-select>
</mat-form-field>

<div
class="mat-paginator-page-size-value"
*ngIf="vm.pageSizeOptions.length <= 1">{{vm.pageSize}}</div>
</div>

<div class="mat-paginator-range-actions">
<div class="mat-paginator-range-label">
{{vm.rangeLabel}}
</div>

<button mat-icon-button type="button"
class="mat-paginator-navigation-first"
(click)="firstPage()"
matTooltip="First page"
[matTooltipDisabled]="!vm.hasPreviousPage"
[matTooltipPosition]="'above'"
[disabled]="!vm.hasPreviousPage">
<svg class="mat-paginator-icon" viewBox="0 0 24 24" focusable="false">
<path d="M18.41 16.59L13.82 12l4.59-4.59L17 6l-6 6 6 6zM6 6h2v12H6z"/>
</svg>
</button>
<button mat-icon-button type="button"
class="mat-paginator-navigation-previous"
(click)="previousPage()"
[attr.aria-label]="'Previous Page'"
[matTooltip]="'Previous Page'"
[matTooltipDisabled]="!vm.hasPreviousPage"
[matTooltipPosition]="'above'"
[disabled]="!vm.hasPreviousPage">
<svg class="mat-paginator-icon" viewBox="0 0 24 24" focusable="false">
<path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/>
</svg>
</button>
<button mat-icon-button type="button"
class="mat-paginator-navigation-next"
(click)="nextPage()"
[attr.aria-label]="'Next Page'"
[matTooltip]="'Next Page'"
[matTooltipDisabled]="!vm.hasNextPage"
[matTooltipPosition]="'above'"
[disabled]="!vm.hasNextPage">
<svg class="mat-paginator-icon" viewBox="0 0 24 24" focusable="false">
<path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/>
</svg>
</button>
<button mat-icon-button type="button"
class="mat-paginator-navigation-last"
(click)="lastPage()"
[attr.aria-label]="'Last Page'"
[matTooltip]="'Last Page'"
[matTooltipDisabled]="!vm.hasNextPage"
[matTooltipPosition]="'above'"
[disabled]="!vm.hasNextPage">
<svg class="mat-paginator-icon" viewBox="0 0 24 24" focusable="false">
<path d="M5.59 7.41L10.18 12l-4.59 4.59L7 18l6-6-6-6zM16 6h2v12h-2z"/>
</svg>
</button>
</div>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
@import '~@angular/material/prebuilt-themes/indigo-pink.css';
/* Add application styles & imports to this file! */


$mat-paginator-padding: 0 8px;
$mat-paginator-page-size-margin-right: 8px;

$mat-paginator-items-per-page-label-margin: 0 4px;
$mat-paginator-selector-margin: 6px 4px 0 4px;
$mat-paginator-selector-trigger-width: 56px;
$mat-paginator-selector-trigger-outline-width: 64px;
$mat-paginator-selector-trigger-fill-width: 64px;

$mat-paginator-range-label-margin: 0 32px 0 24px;
$mat-paginator-button-icon-size: 28px;

.mat-paginator {
display: block;
}

// Note: this wrapper element is only used to get the flexbox vertical centering to work
// with the `min-height` on IE11. It can be removed if we drop support for IE.
.mat-paginator-outer-container {
display: flex;
}

.mat-paginator-container {
display: flex;
align-items: center;
justify-content: flex-end;
padding: $mat-paginator-padding;
flex-wrap: wrap-reverse;
width: 100%;
}

.mat-paginator-page-size {
display: flex;
align-items: baseline;
margin-right: $mat-paginator-page-size-margin-right;

[dir='rtl'] & {
margin-right: 0;
margin-left: $mat-paginator-page-size-margin-right;
}
}

.mat-paginator-page-size-label {
margin: $mat-paginator-items-per-page-label-margin;
}

.mat-paginator-page-size-select {
margin: $mat-paginator-selector-margin;
width: $mat-paginator-selector-trigger-width;

&.mat-form-field-appearance-outline {
width: $mat-paginator-selector-trigger-outline-width;
}

&.mat-form-field-appearance-fill {
width: $mat-paginator-selector-trigger-fill-width;
}
}

.mat-paginator-range-label {
margin: $mat-paginator-range-label-margin;
}

.mat-paginator-range-actions {
display: flex;
align-items: center;
}

.mat-paginator-icon {
width: $mat-paginator-button-icon-size;
fill: currentColor;

[dir='rtl'] & {
transform: rotate(180deg);
}
}
Loading