docs(docs-infra): Support linking to individual playground templates

This commit is contained in:
Matthieu Riegler 2025-10-23 18:57:06 +02:00 committed by Kristiyan Kostadinov
parent a3639e2258
commit 6e7b3c2aa6
5 changed files with 29 additions and 9 deletions

View file

@ -151,7 +151,7 @@ export type PlaygroundRouteData = {
starterTemplate?: PlaygroundTemplate;
};
export type PlaygroundTemplate = Required<Pick<NavigationItem, 'path' | 'label'>>;
export type PlaygroundTemplate = Required<Pick<NavigationItem, 'path' | 'label'>> & {id: string};
// Note: only the fields being used are defined in this type
export interface PackageJson {

View file

@ -19,6 +19,7 @@ export async function generatePlaygroundRoutes(
const templates = Object.entries(configs).map(([path, config]) => ({
path: `playground/${path}`,
label: config.title,
id: path,
}));
return {

View file

@ -12,17 +12,17 @@
</div>
@if (embeddedEditorComponent) {
<ng-container *ngComponentOutlet="embeddedEditorComponent" />
<ng-container *ngComponentOutlet="embeddedEditorComponent" />
}
<ng-template #templatesMenu>
<ul class="adev-template-dropdown" cdkMenu>
@for (template of templates; track template.path) {
<li>
<button cdkMenuItem type="button" (click)="changeTemplate(template)">
<span>{{ template.label }}</span>
</button>
</li>
<li>
<button cdkMenuItem type="button" (click)="changeTemplate(template)">
<span>{{ template.label }}</span>
</button>
</li>
}
</ul>
</ng-template>

View file

@ -13,6 +13,7 @@ import {EmbeddedTutorialManager} from '../../editor';
import {NodeRuntimeSandbox} from '../../editor/node-runtime-sandbox.service';
import TutorialPlayground from './playground.component';
import {provideRouter} from '@angular/router';
describe('TutorialPlayground', () => {
let component: TutorialPlayground;
@ -28,6 +29,7 @@ describe('TutorialPlayground', () => {
TestBed.configureTestingModule({
imports: [TutorialPlayground],
providers: [
provideRouter([]),
{
provide: WINDOW,
useValue: fakeWindow,

View file

@ -15,7 +15,9 @@ import {
EnvironmentInjector,
PLATFORM_ID,
Type,
effect,
inject,
input,
} from '@angular/core';
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
import {CdkMenu, CdkMenuItem, CdkMenuTrigger} from '@angular/cdk/menu';
@ -27,6 +29,7 @@ import {injectNodeRuntimeSandbox} from '../../editor/index';
import type {NodeRuntimeSandbox} from '../../editor/node-runtime-sandbox.service';
import PLAYGROUND_ROUTE_DATA_JSON from '../../../../src/assets/tutorials/playground/routes.json';
import {ActivatedRoute, Router} from '@angular/router';
@Component({
selector: 'adev-playground',
@ -36,12 +39,16 @@ import PLAYGROUND_ROUTE_DATA_JSON from '../../../../src/assets/tutorials/playgro
changeDetection: ChangeDetectionStrategy.OnPush,
})
export default class PlaygroundComponent {
readonly templateId = input<string | undefined>();
private readonly changeDetectorRef = inject(ChangeDetectorRef);
private readonly environmentInjector = inject(EnvironmentInjector);
private readonly destroyRef = inject(DestroyRef);
private readonly isServer = isPlatformServer(inject(PLATFORM_ID));
private readonly router = inject(Router);
private readonly route = inject(ActivatedRoute);
readonly templates = PLAYGROUND_ROUTE_DATA_JSON.templates;
readonly templates: PlaygroundTemplate[] = PLAYGROUND_ROUTE_DATA_JSON.templates;
readonly defaultTemplate = PLAYGROUND_ROUTE_DATA_JSON.defaultTemplate;
readonly starterTemplate = PLAYGROUND_ROUTE_DATA_JSON.starterTemplate;
@ -54,6 +61,11 @@ export default class PlaygroundComponent {
return;
}
effect(() => {
const foundTemplate = this.templates.find((t) => t.id === this.templateId());
this.changeTemplate(foundTemplate ?? this.defaultTemplate);
});
// If using `async-await`, `this` will be captured until the function is executed
// and completed, which can lead to a memory leak if the user navigates away from
// the playground component to another page.
@ -66,7 +78,7 @@ export default class PlaygroundComponent {
this.nodeRuntimeSandbox = nodeRuntimeSandbox;
this.embeddedEditorComponent = embeddedEditorComponent;
}),
switchMap(() => this.loadTemplate(this.defaultTemplate.path)),
switchMap(() => this.loadTemplate(this.selectedTemplate.path)),
takeUntilDestroyed(this.destroyRef),
)
.subscribe(() => {
@ -80,6 +92,11 @@ export default class PlaygroundComponent {
}
async changeTemplate(template: PlaygroundTemplate): Promise<void> {
this.router.navigate([], {
relativeTo: this.route,
queryParams: {templateId: template.id},
replaceUrl: true,
});
this.selectedTemplate = template;
await this.loadTemplate(template.path);
await this.nodeRuntimeSandbox!.reset();