mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
docs(docs-infra): add open in StackBlitz button to code editor (#57018)
PR Close #57018
This commit is contained in:
parent
d3e14b35d3
commit
edbb403171
7 changed files with 105 additions and 3 deletions
|
|
@ -78,6 +78,7 @@ APPLICATION_DEPS = [
|
|||
"@npm//@lezer/highlight",
|
||||
"@npm//@lezer/javascript",
|
||||
"@npm//@lezer/common",
|
||||
"@npm//@stackblitz/sdk",
|
||||
"@npm//@xterm/xterm",
|
||||
"@npm//@xterm/addon-fit",
|
||||
"@npm//algoliasearch",
|
||||
|
|
|
|||
|
|
@ -65,6 +65,14 @@
|
|||
</button>
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="adev-editor-download-button"
|
||||
type="button"
|
||||
(click)="openCurrentCodeInStackBlitz()"
|
||||
aria-label="Open current code in editor in StackBlitz"
|
||||
>
|
||||
<docs-icon>launch</docs-icon>
|
||||
</button>
|
||||
<button
|
||||
class="adev-editor-download-button"
|
||||
type="button"
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
* found in the LICENSE file at https://angular.dev/license
|
||||
*/
|
||||
|
||||
import {NgFor, NgIf} from '@angular/common';
|
||||
import {Location, NgFor, NgIf} from '@angular/common';
|
||||
import {
|
||||
AfterViewInit,
|
||||
ChangeDetectionStrategy,
|
||||
|
|
@ -20,6 +20,7 @@ import {
|
|||
} from '@angular/core';
|
||||
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||
import {MatTabGroup, MatTabsModule} from '@angular/material/tabs';
|
||||
import {Title} from '@angular/platform-browser';
|
||||
import {debounceTime, map} from 'rxjs';
|
||||
|
||||
import {TerminalType} from '../terminal/terminal-handler.service';
|
||||
|
|
@ -28,6 +29,7 @@ import {EmbeddedTutorialManager} from '../embedded-tutorial-manager.service';
|
|||
import {CodeMirrorEditor} from './code-mirror-editor.service';
|
||||
import {DiagnosticWithLocation, DiagnosticsState} from './services/diagnostics-state.service';
|
||||
import {DownloadManager} from '../download-manager.service';
|
||||
import {StackBlitzOpener} from '../stackblitz-opener.service';
|
||||
import {ClickOutside, IconComponent} from '@angular/docs';
|
||||
|
||||
export const REQUIRED_FILES = new Set([
|
||||
|
|
@ -36,6 +38,8 @@ export const REQUIRED_FILES = new Set([
|
|||
'src/app/app.component.ts',
|
||||
]);
|
||||
|
||||
const ANGULAR_DEV = 'https://angular.dev';
|
||||
|
||||
@Component({
|
||||
selector: 'docs-tutorial-code-editor',
|
||||
standalone: true,
|
||||
|
|
@ -73,6 +77,9 @@ export class CodeEditor implements AfterViewInit, OnDestroy {
|
|||
private readonly codeMirrorEditor = inject(CodeMirrorEditor);
|
||||
private readonly diagnosticsState = inject(DiagnosticsState);
|
||||
private readonly downloadManager = inject(DownloadManager);
|
||||
private readonly stackblitzOpener = inject(StackBlitzOpener);
|
||||
private readonly title = inject(Title);
|
||||
private readonly location = inject(Location);
|
||||
private readonly embeddedTutorialManager = inject(EmbeddedTutorialManager);
|
||||
|
||||
private readonly errors$ = this.diagnosticsState.diagnostics$.pipe(
|
||||
|
|
@ -110,6 +117,16 @@ export class CodeEditor implements AfterViewInit, OnDestroy {
|
|||
this.codeMirrorEditor.disable();
|
||||
}
|
||||
|
||||
async openCurrentCodeInStackBlitz(): Promise<void> {
|
||||
const title = this.title.getTitle();
|
||||
|
||||
const path = this.location.path();
|
||||
const editorUrl = `${ANGULAR_DEV}${path}`;
|
||||
const description = `Angular.dev example generated from [${editorUrl}](${editorUrl})`;
|
||||
|
||||
await this.stackblitzOpener.openCurrentSolutionInStackBlitz({title, description});
|
||||
}
|
||||
|
||||
async downloadCurrentCodeEditorState(): Promise<void> {
|
||||
const name = this.embeddedTutorialManager.tutorialId();
|
||||
await this.downloadManager.downloadCurrentStateOfTheSolution(name);
|
||||
|
|
|
|||
24
adev/src/app/editor/stackblitz-opener.service.spec.ts
Normal file
24
adev/src/app/editor/stackblitz-opener.service.spec.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
/*!
|
||||
* @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 {TestBed} from '@angular/core/testing';
|
||||
|
||||
import {StackBlitzOpener} from './stackblitz-opener.service';
|
||||
|
||||
describe('StackBlitzOpener', () => {
|
||||
let service: StackBlitzOpener;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(StackBlitzOpener);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
46
adev/src/app/editor/stackblitz-opener.service.ts
Normal file
46
adev/src/app/editor/stackblitz-opener.service.ts
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/*!
|
||||
* @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 {EnvironmentInjector, Injectable, inject} from '@angular/core';
|
||||
import sdk, {Project, ProjectFiles} from '@stackblitz/sdk';
|
||||
import {injectAsync} from '../core/services/inject-async';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class StackBlitzOpener {
|
||||
private readonly environmentInjector = inject(EnvironmentInjector);
|
||||
|
||||
/**
|
||||
* Generate a StackBlitz project from the current state of the solution in the EmbeddedEditor
|
||||
*/
|
||||
async openCurrentSolutionInStackBlitz(
|
||||
projectMetadata: Pick<Project, 'title' | 'description'>,
|
||||
): Promise<void> {
|
||||
const nodeRuntimeSandbox = await injectAsync(this.environmentInjector, () =>
|
||||
import('./node-runtime-sandbox.service').then((c) => c.NodeRuntimeSandbox),
|
||||
);
|
||||
|
||||
const runtimeFiles = await nodeRuntimeSandbox.getSolutionFiles();
|
||||
|
||||
const stackblitzProjectFiles: ProjectFiles = {};
|
||||
runtimeFiles.forEach((file) => {
|
||||
// Leading slashes are incompatible with StackBlitz SDK they are removed
|
||||
const path = file.path.replace(/^\//, '');
|
||||
|
||||
stackblitzProjectFiles[path] =
|
||||
typeof file.content !== 'string' ? new TextDecoder().decode(file.content) : file.content;
|
||||
});
|
||||
|
||||
sdk.openProject({
|
||||
...projectMetadata,
|
||||
template: 'node',
|
||||
files: stackblitzProjectFiles,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -48,10 +48,10 @@
|
|||
},
|
||||
"// 1": "dependencies are used locally and by bazel",
|
||||
"dependencies": {
|
||||
"@angular/build": "18.1.0-next.1",
|
||||
"@angular-devkit/build-angular": "18.1.0-next.1",
|
||||
"@angular-devkit/core": "18.1.0-next.1",
|
||||
"@angular-devkit/schematics": "18.1.0-next.1",
|
||||
"@angular/build": "18.1.0-next.1",
|
||||
"@angular/cdk": "18.1.0",
|
||||
"@angular/cli": "18.1.0-next.1",
|
||||
"@angular/material": "18.1.0",
|
||||
|
|
@ -72,6 +72,7 @@
|
|||
"@rollup/plugin-commonjs": "^26.0.0",
|
||||
"@rollup/plugin-node-resolve": "^13.0.4",
|
||||
"@schematics/angular": "18.1.0-next.1",
|
||||
"@stackblitz/sdk": "^1.11.0",
|
||||
"@types/angular": "^1.6.47",
|
||||
"@types/babel__core": "7.20.5",
|
||||
"@types/babel__generator": "7.6.8",
|
||||
|
|
@ -91,8 +92,8 @@
|
|||
"@types/shelljs": "^0.8.6",
|
||||
"@types/systemjs": "6.13.5",
|
||||
"@types/yargs": "^17.0.3",
|
||||
"@xterm/xterm": "^5.5.0",
|
||||
"@xterm/addon-fit": "^0.10.0",
|
||||
"@xterm/xterm": "^5.5.0",
|
||||
"algoliasearch": "^4.23.3",
|
||||
"angular-1.5": "npm:angular@1.5",
|
||||
"angular-1.6": "npm:angular@1.6",
|
||||
|
|
|
|||
|
|
@ -4117,6 +4117,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz#821f8442f4175d8f0467b9daf26e3a18e2d02af2"
|
||||
integrity sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==
|
||||
|
||||
"@stackblitz/sdk@^1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@stackblitz/sdk/-/sdk-1.11.0.tgz#ba30c837decca221ce8d605ff768a774c0f92f89"
|
||||
integrity sha512-DFQGANNkEZRzFk1/rDP6TcFdM82ycHE+zfl9C/M/jXlH68jiqHWHFMQURLELoD8koxvu/eW5uhg94NSAZlYrUQ==
|
||||
|
||||
"@szmarczak/http-timer@^4.0.5":
|
||||
version "4.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807"
|
||||
|
|
|
|||
Loading…
Reference in a new issue