mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
feat(devtools): create demo application that uses standalone APIs and standalone components/directives/pipes (#48533)
The existing DevTools demo app that is used for developing on DevTools is exclusively an NgModule application. This commit creates a copy of the old demo app but with no NgModules and only standalone APIs/Components/Directives/Pipes PR Close #48533
This commit is contained in:
committed by
Pawel Kozlowski
parent
b1aed48993
commit
dbadfea67f
@@ -0,0 +1 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
@@ -0,0 +1,90 @@
|
||||
load("//devtools/tools:ng_module.bzl", "ng_module")
|
||||
load("@io_bazel_rules_sass//:defs.bzl", "sass_binary")
|
||||
load("@build_bazel_rules_nodejs//:index.bzl", "pkg_web")
|
||||
load("//tools:defaults.bzl", "esbuild", "http_server")
|
||||
load("//devtools/tools/esbuild:index.bzl", "LINKER_PROCESSED_FW_PACKAGES")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
sass_binary(
|
||||
name = "demo_styles",
|
||||
src = "styles.scss",
|
||||
include_paths = ["external/npm/node_modules"],
|
||||
sourcemap = False,
|
||||
deps = ["//devtools:material_sass_deps"],
|
||||
)
|
||||
|
||||
sass_binary(
|
||||
name = "firefox_styles",
|
||||
src = "styles/firefox_styles.scss",
|
||||
)
|
||||
|
||||
sass_binary(
|
||||
name = "chrome_styles",
|
||||
src = "styles/chrome_styles.scss",
|
||||
)
|
||||
|
||||
ng_module(
|
||||
name = "demo",
|
||||
srcs = ["main.ts"],
|
||||
deps = [
|
||||
"//devtools/projects/demo-standalone/src/app",
|
||||
"//devtools/src:demo_application_environment",
|
||||
"//devtools/src:demo_application_operations",
|
||||
"//packages/common",
|
||||
"//packages/common/http",
|
||||
"//packages/core",
|
||||
"//packages/core/src/util",
|
||||
"//packages/platform-browser",
|
||||
"@npm//@types",
|
||||
"@npm//rxjs",
|
||||
],
|
||||
)
|
||||
|
||||
esbuild(
|
||||
name = "bundle",
|
||||
config = "//devtools/tools/esbuild:esbuild_config_esm",
|
||||
entry_points = [":main.ts"],
|
||||
platform = "browser",
|
||||
splitting = True,
|
||||
target = "es2016",
|
||||
deps = LINKER_PROCESSED_FW_PACKAGES + [":demo"],
|
||||
)
|
||||
|
||||
exports_files(["index.html"])
|
||||
|
||||
filegroup(
|
||||
name = "dev_app_static_files",
|
||||
srcs = [
|
||||
":chrome_styles",
|
||||
":demo_styles",
|
||||
":firefox_styles",
|
||||
":index.html",
|
||||
"//packages/zone.js/bundles:zone.umd.js",
|
||||
],
|
||||
)
|
||||
|
||||
pkg_web(
|
||||
name = "devapp",
|
||||
srcs = [":dev_app_static_files"] + [
|
||||
":bundle",
|
||||
"@npm//:node_modules/tslib/tslib.js",
|
||||
],
|
||||
# Currently, ibazel doesn't allow passing in flags to bazel run.
|
||||
# This means we are not able to use --config snapshot-build
|
||||
# to access the current commit SHA.
|
||||
# Since we still want to be able to use ibazel to speed up
|
||||
# local development, we supply an empty string for the SHA substitution.
|
||||
# This does not effect production builds.
|
||||
substitutions = {"BUILD_SCM_COMMIT_SHA": ""},
|
||||
)
|
||||
|
||||
http_server(
|
||||
name = "devserver",
|
||||
srcs = [":dev_app_static_files"],
|
||||
additional_root_paths = ["angular/devtools/projects/demo-standalone/src/devapp"],
|
||||
tags = ["manual"],
|
||||
deps = [
|
||||
":devapp",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,19 @@
|
||||
load("//devtools/tools:ng_module.bzl", "ng_module")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
ng_module(
|
||||
name = "app",
|
||||
srcs = [
|
||||
"app.component.ts",
|
||||
],
|
||||
deps = [
|
||||
"//devtools/projects/demo-standalone/src/app/demo-app",
|
||||
"//devtools/projects/demo-standalone/src/app/devtools-app",
|
||||
"//devtools/projects/ng-devtools",
|
||||
"//packages/core",
|
||||
"//packages/platform-browser",
|
||||
"//packages/platform-browser/animations",
|
||||
"//packages/router",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @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.io/license
|
||||
*/
|
||||
|
||||
import {Component} from '@angular/core';
|
||||
import {Router, RouterOutlet} from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
template: `<router-outlet></router-outlet>`,
|
||||
standalone: true,
|
||||
imports: [RouterOutlet]
|
||||
})
|
||||
export class AppComponent {
|
||||
constructor(public router: Router) {}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
load("//devtools/tools:ng_module.bzl", "ng_module")
|
||||
load("@io_bazel_rules_sass//:defs.bzl", "sass_binary", "sass_library")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
sass_library(
|
||||
name = "todo_mvc",
|
||||
srcs = [
|
||||
"@npm//:node_modules/todomvc-app-css/index.css",
|
||||
"@npm//:node_modules/todomvc-common/base.css",
|
||||
],
|
||||
)
|
||||
|
||||
sass_binary(
|
||||
name = "demo_app_component_styles",
|
||||
src = "demo-app.component.scss",
|
||||
deps = [
|
||||
":todo_mvc",
|
||||
],
|
||||
)
|
||||
|
||||
ng_module(
|
||||
name = "demo-app",
|
||||
srcs = [
|
||||
"demo-app.component.ts",
|
||||
"heavy.component.ts",
|
||||
"zippy.component.ts",
|
||||
],
|
||||
angular_assets = [
|
||||
"demo-app.component.html",
|
||||
":demo_app_component_styles",
|
||||
],
|
||||
deps = [
|
||||
"//devtools/projects/demo-standalone/src/app/demo-app/todo",
|
||||
"//devtools/projects/ng-devtools-backend",
|
||||
"//devtools/src:zone-unaware-iframe_message_bus",
|
||||
"//packages/core",
|
||||
"//packages/elements",
|
||||
"//packages/router",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,4 @@
|
||||
<router-outlet></router-outlet>
|
||||
<app-zippy [title]="getTitle()">This is my content</app-zippy>
|
||||
<app-heavy></app-heavy>
|
||||
<div #elementReference>HTMLElement</div>
|
||||
@@ -0,0 +1,2 @@
|
||||
@use 'external/npm/node_modules/todomvc-app-css/index.css';
|
||||
@use 'external/npm/node_modules/todomvc-common/base.css';
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* @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.io/license
|
||||
*/
|
||||
|
||||
import {Component, CUSTOM_ELEMENTS_SCHEMA, ElementRef, EventEmitter, inject, Injector, Input, Output, ViewChild, ViewEncapsulation} from '@angular/core';
|
||||
import {createCustomElement} from '@angular/elements';
|
||||
import {RouterOutlet} from '@angular/router';
|
||||
import {initializeMessageBus} from 'ng-devtools-backend';
|
||||
|
||||
import {ZoneUnawareIFrameMessageBus} from '../../../../../src/zone-unaware-iframe-message-bus';
|
||||
|
||||
import {HeavyComponent} from './heavy.component';
|
||||
import {ZippyComponent} from './zippy.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-demo-component',
|
||||
templateUrl: './demo-app.component.html',
|
||||
styleUrls: ['./demo-app.component.scss'],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
standalone: true,
|
||||
imports: [HeavyComponent, RouterOutlet],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
})
|
||||
export class DemoAppComponent {
|
||||
@ViewChild(ZippyComponent) zippy: ZippyComponent;
|
||||
@ViewChild('elementReference') elementRef: ElementRef;
|
||||
|
||||
@Input('input_one') inputOne = 'input one';
|
||||
@Input() inputTwo = 'input two';
|
||||
|
||||
@Output() outputOne = new EventEmitter();
|
||||
@Output('output_two') outputTwo = new EventEmitter();
|
||||
|
||||
constructor() {
|
||||
const el = createCustomElement(ZippyComponent, {injector: inject(Injector)});
|
||||
customElements.define('app-zippy', el as any);
|
||||
}
|
||||
|
||||
getTitle(): '► Click to expand'|'▼ Click to collapse' {
|
||||
if (!this.zippy || !this.zippy.visible) {
|
||||
return '► Click to expand';
|
||||
}
|
||||
return '▼ Click to collapse';
|
||||
}
|
||||
}
|
||||
|
||||
export const ROUTES = [
|
||||
{
|
||||
path: '',
|
||||
component: DemoAppComponent,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
loadChildren: () => import('./todo/todo-app.component').then((m) => m.ROUTES),
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
initializeMessageBus(new ZoneUnawareIFrameMessageBus(
|
||||
'angular-devtools-backend', 'angular-devtools', () => window.parent));
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* @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.io/license
|
||||
*/
|
||||
|
||||
import {Component, Input} from '@angular/core';
|
||||
|
||||
const fib = (n: number) => {
|
||||
if (n === 1 || n === 2) {
|
||||
return 1;
|
||||
}
|
||||
return fib(n - 1) + fib(n - 2);
|
||||
};
|
||||
|
||||
@Component({selector: 'app-heavy', template: `<h1>{{ calculate() }}</h1>`, standalone: true})
|
||||
export class HeavyComponent {
|
||||
@Input()
|
||||
set foo(_: any) {
|
||||
}
|
||||
|
||||
state = {
|
||||
nested: {
|
||||
props: {
|
||||
foo: 1,
|
||||
bar: 2,
|
||||
},
|
||||
[Symbol(3)]():
|
||||
number {
|
||||
return 1.618;
|
||||
},
|
||||
get foo():
|
||||
number {
|
||||
return 42;
|
||||
},
|
||||
},
|
||||
};
|
||||
calculate(): number {
|
||||
return fib(15);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
load("//devtools/tools:ng_module.bzl", "ng_module")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
ng_module(
|
||||
name = "todo",
|
||||
srcs = [
|
||||
"dialog.component.ts",
|
||||
"todo-app.component.ts",
|
||||
],
|
||||
deps = [
|
||||
"//devtools/projects/demo-standalone/src/app/demo-app/todo/about",
|
||||
"//devtools/projects/demo-standalone/src/app/demo-app/todo/home",
|
||||
"//packages/common",
|
||||
"//packages/core",
|
||||
"//packages/forms",
|
||||
"//packages/router",
|
||||
"@npm//@angular/material",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,12 @@
|
||||
load("//devtools/tools:ng_module.bzl", "ng_module")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
ng_module(
|
||||
name = "about",
|
||||
srcs = ["about.component.ts"],
|
||||
deps = [
|
||||
"//packages/core",
|
||||
"//packages/router",
|
||||
],
|
||||
)
|
||||
@@ -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.io/license
|
||||
*/
|
||||
|
||||
import {Component} from '@angular/core';
|
||||
import {RouterLink, RouterOutlet} from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-about',
|
||||
standalone: true,
|
||||
imports: [RouterOutlet, RouterLink],
|
||||
template: `
|
||||
About component
|
||||
<a [routerLink]="">Home</a>
|
||||
<a [routerLink]="">Home</a>
|
||||
<a [routerLink]="">Home</a>
|
||||
`
|
||||
})
|
||||
export class AboutComponent {
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* @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.io/license
|
||||
*/
|
||||
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {Component, Inject} from '@angular/core';
|
||||
import {FormsModule} from '@angular/forms';
|
||||
import {MAT_LEGACY_DIALOG_DATA as MAT_DIALOG_DATA, MatLegacyDialogModule as MatDialogModule, MatLegacyDialogRef as MatDialogRef} from '@angular/material/legacy-dialog';
|
||||
import {MatLegacyFormFieldModule as MatFormFieldModule} from '@angular/material/legacy-form-field';
|
||||
|
||||
|
||||
export interface DialogData {
|
||||
animal: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-dialog',
|
||||
standalone: true,
|
||||
imports: [MatDialogModule, MatFormFieldModule, FormsModule, CommonModule],
|
||||
template: `
|
||||
<h1 mat-dialog-title>Hi {{ data.name }}</h1>
|
||||
<div mat-dialog-content>
|
||||
<p>What's your favorite animal?</p>
|
||||
<mat-form-field>
|
||||
<mat-label>Favorite Animal</mat-label>
|
||||
<input matInput [(ngModel)]="data.animal" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div mat-dialog-actions>
|
||||
<button mat-button (click)="onNoClick()">No Thanks</button>
|
||||
<button mat-button [mat-dialog-close]="data.animal" cdkFocusInitial>Ok</button>
|
||||
</div>
|
||||
`,
|
||||
})
|
||||
export class DialogComponent {
|
||||
constructor(
|
||||
public dialogRef: MatDialogRef<DialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: DialogData) {}
|
||||
|
||||
onNoClick(): void {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
load("//devtools/tools:ng_module.bzl", "ng_module")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
ng_module(
|
||||
name = "home",
|
||||
srcs = [
|
||||
"sample.pipe.ts",
|
||||
"todo.component.ts",
|
||||
"todos.component.ts",
|
||||
"tooltip.directive.ts",
|
||||
],
|
||||
deps = [
|
||||
"//packages/common",
|
||||
"//packages/core",
|
||||
"//packages/router",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* @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.io/license
|
||||
*/
|
||||
|
||||
import {OnDestroy, Pipe, PipeTransform} from '@angular/core';
|
||||
|
||||
@Pipe({name: 'sample', pure: false, standalone: true})
|
||||
export class SamplePipe implements PipeTransform, OnDestroy {
|
||||
transform(val: any): void {
|
||||
return val;
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.log('Destroying');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* @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.io/license
|
||||
*/
|
||||
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {ChangeDetectionStrategy, Component, EventEmitter, Input, Output} from '@angular/core';
|
||||
|
||||
import {TooltipDirective} from './tooltip.directive';
|
||||
|
||||
export interface Todo {
|
||||
id: string;
|
||||
completed: boolean;
|
||||
label: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-todo',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
imports: [CommonModule, TooltipDirective],
|
||||
styles: [`
|
||||
.destroy {
|
||||
cursor: pointer;
|
||||
display: unset !important;
|
||||
}
|
||||
`],
|
||||
template: `
|
||||
<li [class.completed]="todo.completed">
|
||||
<div class="view" appTooltip>
|
||||
<input class="toggle" type="checkbox" [checked]="todo.completed" (change)="toggle()" />
|
||||
<label (dblclick)="enableEditMode()" [style.display]="editMode ? 'none' : 'block'">{{ todo.label }}</label>
|
||||
<button class="destroy" (click)="delete.emit(todo)"></button>
|
||||
</div>
|
||||
<input
|
||||
class="edit"
|
||||
[value]="todo.label"
|
||||
[style.display]="editMode ? 'block' : 'none'"
|
||||
(keydown.enter)="completeEdit($any($event.target).value)"
|
||||
/>
|
||||
</li>
|
||||
`
|
||||
})
|
||||
export class TodoComponent {
|
||||
@Input() todo: Todo;
|
||||
@Output() update = new EventEmitter();
|
||||
@Output() delete = new EventEmitter();
|
||||
|
||||
editMode = false;
|
||||
|
||||
toggle(): void {
|
||||
this.todo.completed = !this.todo.completed;
|
||||
this.update.emit(this.todo);
|
||||
}
|
||||
|
||||
completeEdit(label: string): void {
|
||||
this.todo.label = label;
|
||||
this.editMode = false;
|
||||
this.update.emit(this.todo);
|
||||
}
|
||||
|
||||
enableEditMode(): void {
|
||||
this.editMode = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
/**
|
||||
* @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.io/license
|
||||
*/
|
||||
|
||||
import {NgForOf} from '@angular/common';
|
||||
import {ChangeDetectorRef, Component, EventEmitter, OnDestroy, OnInit, Output, Pipe, PipeTransform} from '@angular/core';
|
||||
import {RouterLink} from '@angular/router';
|
||||
|
||||
import {SamplePipe} from './sample.pipe';
|
||||
import {Todo, TodoComponent} from './todo.component';
|
||||
import {TooltipDirective} from './tooltip.directive';
|
||||
|
||||
export const enum TodoFilter {
|
||||
All = 'all',
|
||||
Completed = 'completed',
|
||||
Active = 'active',
|
||||
}
|
||||
|
||||
@Pipe({pure: false, name: 'todosFilter', standalone: true})
|
||||
export class TodosFilter implements PipeTransform {
|
||||
transform(todos: Todo[], filter: TodoFilter): Todo[] {
|
||||
return (todos || []).filter((t) => {
|
||||
if (filter === TodoFilter.All) {
|
||||
return true;
|
||||
}
|
||||
if (filter === TodoFilter.Active && !t.completed) {
|
||||
return true;
|
||||
}
|
||||
if (filter === TodoFilter.Completed && t.completed) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const fib = (n: number) => {
|
||||
if (n === 1 || n === 2) {
|
||||
return 1;
|
||||
}
|
||||
return fib(n - 1) + fib(n - 2);
|
||||
};
|
||||
|
||||
@Component({
|
||||
selector: 'app-todos',
|
||||
imports: [NgForOf, RouterLink, TodoComponent, SamplePipe, TodosFilter, TooltipDirective],
|
||||
standalone: true,
|
||||
template: `
|
||||
<a [routerLink]="">Home</a>
|
||||
<a [routerLink]="">Home</a>
|
||||
<a [routerLink]="">Home</a>
|
||||
|
||||
<p>{{ 'Sample text processed by a pipe' | sample }}</p>
|
||||
|
||||
<section class="todoapp">
|
||||
<header class="header">
|
||||
<h1>todos</h1>
|
||||
<input (keydown.enter)="addTodo(input)" #input class="new-todo" placeholder="What needs to be done?" autofocus />
|
||||
</header>
|
||||
<section class="main">
|
||||
<input id="toggle-all" class="toggle-all" type="checkbox" />
|
||||
<label for="toggle-all">Mark all as complete</label>
|
||||
<ul class="todo-list">
|
||||
<app-todo
|
||||
appTooltip
|
||||
*ngFor="let todo of todos | todosFilter: filterValue"
|
||||
[todo]="todo"
|
||||
(delete)="onDelete($event)"
|
||||
(update)="onChange($event)"
|
||||
></app-todo>
|
||||
</ul>
|
||||
</section>
|
||||
<footer class="footer">
|
||||
<span class="todo-count">
|
||||
<strong>{{ itemsLeft }}</strong> item left
|
||||
</span>
|
||||
<button class="clear-completed" (click)="clearCompleted()">Clear completed</button>
|
||||
</footer>
|
||||
</section>
|
||||
`
|
||||
})
|
||||
export class TodosComponent implements OnInit, OnDestroy {
|
||||
todos: Todo[] = [
|
||||
{
|
||||
label: 'Buy milk',
|
||||
completed: false,
|
||||
id: '42',
|
||||
},
|
||||
{
|
||||
label: 'Build something fun!',
|
||||
completed: false,
|
||||
id: '43',
|
||||
},
|
||||
];
|
||||
|
||||
@Output() update = new EventEmitter();
|
||||
@Output() delete = new EventEmitter();
|
||||
@Output() add = new EventEmitter();
|
||||
|
||||
private hashListener: EventListenerOrEventListenerObject;
|
||||
|
||||
constructor(private cdRef: ChangeDetectorRef) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
if (typeof window !== 'undefined') {
|
||||
window.addEventListener('hashchange', (this.hashListener = () => this.cdRef.markForCheck()));
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
fib(40);
|
||||
if (typeof window !== 'undefined') {
|
||||
window.removeEventListener('hashchange', this.hashListener);
|
||||
}
|
||||
}
|
||||
|
||||
get filterValue(): TodoFilter {
|
||||
if (typeof window !== 'undefined') {
|
||||
return (window.location.hash.replace(/^#\//, '') as TodoFilter) || TodoFilter.All;
|
||||
}
|
||||
return TodoFilter.All;
|
||||
}
|
||||
|
||||
get itemsLeft(): number {
|
||||
return (this.todos || []).filter((t) => !t.completed).length;
|
||||
}
|
||||
|
||||
clearCompleted(): void {
|
||||
(this.todos || []).filter((t) => t.completed).forEach((t) => this.delete.emit(t));
|
||||
}
|
||||
|
||||
addTodo(input: HTMLInputElement): void {
|
||||
const todo = {
|
||||
completed: false,
|
||||
label: input.value,
|
||||
};
|
||||
const result: Todo = {...todo, id: Math.random().toString()};
|
||||
this.todos.push(result);
|
||||
input.value = '';
|
||||
}
|
||||
|
||||
onChange(todo: Todo): void {
|
||||
if (!todo.id) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
onDelete(todo: Todo): void {
|
||||
if (!todo.id) {
|
||||
return;
|
||||
}
|
||||
const idx = this.todos.findIndex((t) => t.id === todo.id);
|
||||
if (idx < 0) {
|
||||
return;
|
||||
}
|
||||
// tslint:disable-next-line:no-console
|
||||
console.log('Deleting', idx);
|
||||
|
||||
this.todos.splice(idx, 1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* @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.io/license
|
||||
*/
|
||||
|
||||
import {Directive, HostListener} from '@angular/core';
|
||||
|
||||
@Directive({selector: '[appTooltip]', standalone: true})
|
||||
export class TooltipDirective {
|
||||
visible = false;
|
||||
nested = {
|
||||
child: {
|
||||
grandchild: {
|
||||
prop: 1,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
constructor() {
|
||||
// setInterval(() => this.nested.child.grandchild.prop++, 500);
|
||||
}
|
||||
|
||||
@HostListener('click')
|
||||
handleClick(): void {
|
||||
this.visible = !this.visible;
|
||||
if (this.visible) {
|
||||
(this as any).extraProp = true;
|
||||
} else {
|
||||
delete (this as any).extraProp;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* @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.io/license
|
||||
*/
|
||||
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {Component} from '@angular/core';
|
||||
import {FormsModule} from '@angular/forms';
|
||||
import {MatLegacyDialog as MatDialog, MatLegacyDialogModule as MatDialogModule} from '@angular/material/legacy-dialog';
|
||||
import {MatLegacyFormFieldModule as MatFormFieldModule} from '@angular/material/legacy-form-field';
|
||||
import {MatLegacyInputModule as MatInputModule} from '@angular/material/legacy-input';
|
||||
import {RouterLink, RouterOutlet} from '@angular/router';
|
||||
|
||||
import {DialogComponent} from './dialog.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-todo-demo',
|
||||
standalone: true,
|
||||
imports: [
|
||||
RouterLink, RouterOutlet, MatDialogModule, MatFormFieldModule, MatInputModule, FormsModule,
|
||||
CommonModule
|
||||
],
|
||||
styles: [`
|
||||
nav {
|
||||
padding-top: 20px;
|
||||
padding-bottom: 10px;
|
||||
|
||||
a {
|
||||
margin-right: 15px;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.dialog-open-button {
|
||||
border: 1px solid #ccc;
|
||||
padding: 10px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
`],
|
||||
template: `
|
||||
<nav>
|
||||
<a routerLink="/demo-app/todos/app">Todos</a>
|
||||
<a routerLink="/demo-app/todos/about">About</a>
|
||||
</nav>
|
||||
|
||||
<button class="dialog-open-button" (click)="openDialog()">Open dialog</button>
|
||||
|
||||
<router-outlet></router-outlet>
|
||||
`
|
||||
})
|
||||
export class TodoAppComponent {
|
||||
name: string;
|
||||
animal: string;
|
||||
|
||||
constructor(public dialog: MatDialog) {}
|
||||
|
||||
openDialog(): void {
|
||||
const dialogRef = this.dialog.open(DialogComponent, {
|
||||
width: '250px',
|
||||
data: {name: this.name, animal: this.animal},
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe((result) => {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.log('The dialog was closed');
|
||||
|
||||
this.animal = result;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const ROUTES = [
|
||||
{
|
||||
path: 'todos',
|
||||
component: TodoAppComponent,
|
||||
children: [
|
||||
{
|
||||
path: 'app',
|
||||
loadComponent: () => import('./home/todos.component').then((m) => m.TodosComponent),
|
||||
},
|
||||
{
|
||||
path: 'about',
|
||||
loadComponent: () => import('./about/about.component').then((m) => m.AboutComponent),
|
||||
},
|
||||
{
|
||||
path: '**',
|
||||
redirectTo: 'app',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '**',
|
||||
redirectTo: 'todos',
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* @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.io/license
|
||||
*/
|
||||
|
||||
import {Component, Input} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-zippy',
|
||||
standalone: true,
|
||||
styles: [`
|
||||
:host {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
header {
|
||||
max-width: 120px;
|
||||
border: 1px solid #ccc;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
div {
|
||||
max-width: 120px;
|
||||
border-left: 1px solid #ccc;
|
||||
border-right: 1px solid #ccc;
|
||||
border-bottom: 1px solid #ccc;
|
||||
padding: 10px;
|
||||
}
|
||||
`],
|
||||
template: `
|
||||
<section>
|
||||
<header (click)="visible = !visible">{{ title }}</header>
|
||||
<div [hidden]="!visible">
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
</section>
|
||||
`
|
||||
})
|
||||
export class ZippyComponent {
|
||||
@Input() title: string;
|
||||
visible = false;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
load("//devtools/tools:ng_module.bzl", "ng_module")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
ng_module(
|
||||
name = "devtools-app",
|
||||
srcs = ["devtools-app.component.ts"],
|
||||
deps = [
|
||||
"//devtools/projects/ng-devtools",
|
||||
"//devtools/projects/protocol",
|
||||
"//devtools/src:iframe_message_bus",
|
||||
"//packages/common",
|
||||
"//packages/core",
|
||||
"//packages/router",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* @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.io/license
|
||||
*/
|
||||
|
||||
import {Component, ElementRef, ViewChild} from '@angular/core';
|
||||
import {DevToolsModule as NgDevToolsModule} from 'ng-devtools';
|
||||
import {Events, MessageBus, PriorityAwareMessageBus} from 'protocol';
|
||||
|
||||
import {IFrameMessageBus} from '../../../../../src/iframe-message-bus';
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
imports: [NgDevToolsModule],
|
||||
providers: [
|
||||
{
|
||||
provide: MessageBus,
|
||||
useFactory(): MessageBus<Events> {
|
||||
return new PriorityAwareMessageBus(new IFrameMessageBus(
|
||||
'angular-devtools', 'angular-devtools-backend',
|
||||
// tslint:disable-next-line: no-non-null-assertion
|
||||
() => (document.querySelector('#sample-app') as HTMLIFrameElement).contentWindow!));
|
||||
},
|
||||
},
|
||||
],
|
||||
styles: [`
|
||||
iframe {
|
||||
height: 340px;
|
||||
width: 100%;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.devtools-wrapper {
|
||||
height: calc(100vh - 345px);
|
||||
}
|
||||
`],
|
||||
template: `
|
||||
<iframe #ref src="demo-app/todos/app" id="sample-app"></iframe>
|
||||
<br />
|
||||
<div class="devtools-wrapper">
|
||||
<ng-devtools></ng-devtools>
|
||||
</div>
|
||||
`
|
||||
})
|
||||
export class DevToolsComponent {
|
||||
messageBus: IFrameMessageBus|null = null;
|
||||
@ViewChild('ref') iframe: ElementRef;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
load("//devtools/tools:typescript.bzl", "ts_library")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
ts_library(
|
||||
name = "environments",
|
||||
srcs = [
|
||||
"environment.ts",
|
||||
],
|
||||
deps = [
|
||||
"//packages/platform-browser",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* @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.io/license
|
||||
*/
|
||||
|
||||
export const environment = {
|
||||
production: false,
|
||||
LATEST_SHA: 'BUILD_SCM_COMMIT_SHA', // Stamped at build time by bazel
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>AngularDevtools</title>
|
||||
<base href="/" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="icon" type="image/x-icon" href="assets/icon16.png" />
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
||||
|
||||
<link rel="stylesheet" href="./styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<app-root></app-root>
|
||||
</div>
|
||||
|
||||
<script src="./angular/packages/zone.js/bundles/zone.umd.js"></script>
|
||||
<script type="module" src="./bundle/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* @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.io/license
|
||||
*/
|
||||
|
||||
import {bootstrapApplication} from '@angular/platform-browser';
|
||||
import {provideAnimations} from '@angular/platform-browser/animations';
|
||||
import {provideRouter} from '@angular/router';
|
||||
import {ApplicationEnvironment, ApplicationOperations} from 'ng-devtools';
|
||||
|
||||
import {DemoApplicationEnvironment} from '../../../src/demo-application-environment';
|
||||
import {DemoApplicationOperations} from '../../../src/demo-application-operations';
|
||||
|
||||
import {AppComponent} from './app/app.component';
|
||||
|
||||
|
||||
bootstrapApplication(AppComponent, {
|
||||
providers: [
|
||||
provideAnimations(),
|
||||
provideRouter([
|
||||
{
|
||||
path: '',
|
||||
loadComponent: () =>
|
||||
import('./app/devtools-app/devtools-app.component').then((m) => m.DevToolsComponent),
|
||||
pathMatch: 'full',
|
||||
},
|
||||
{
|
||||
path: 'demo-app',
|
||||
loadChildren: () => import('./app/demo-app/demo-app.component').then((m) => m.ROUTES),
|
||||
},
|
||||
]),
|
||||
{
|
||||
provide: ApplicationOperations,
|
||||
useClass: DemoApplicationOperations,
|
||||
},
|
||||
{
|
||||
provide: ApplicationEnvironment,
|
||||
useClass: DemoApplicationEnvironment,
|
||||
},
|
||||
]
|
||||
});
|
||||
@@ -0,0 +1,48 @@
|
||||
@use 'sass:map';
|
||||
@use 'external/npm/node_modules/@angular/material/index' as mat;
|
||||
@include mat.all-legacy-component-typographies();
|
||||
@include mat.legacy-core();
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
// Light theme
|
||||
$light-primary: mat.define-palette(mat.$grey-palette, 700, 200);
|
||||
$light-accent: mat.define-palette(mat.$blue-palette, 800);
|
||||
$light-theme: mat.define-light-theme($light-primary, $light-accent);
|
||||
|
||||
// Dark theme
|
||||
$dark-primary: mat.define-palette(mat.$blue-grey-palette, 50);
|
||||
$dark-accent: mat.define-palette(mat.$blue-palette, 200);
|
||||
$dark-theme: map.deep-merge(
|
||||
mat.define-dark-theme($dark-primary, $dark-accent),
|
||||
(
|
||||
'color': (
|
||||
'background': (
|
||||
background: #202124,
|
||||
card: #202124,
|
||||
),
|
||||
'foreground': (
|
||||
text: #bcc5ce,
|
||||
),
|
||||
),
|
||||
'background': (
|
||||
background: #202124,
|
||||
card: #202124,
|
||||
),
|
||||
'foreground': (
|
||||
'text': #bcc5ce,
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
.light-theme {
|
||||
@include mat.all-legacy-component-themes($light-theme);
|
||||
}
|
||||
|
||||
.dark-theme {
|
||||
@include mat.all-legacy-component-themes($dark-theme);
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
/** Specific style for Chrome browser */
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
/** Specific style for Firefox browser */
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
<link rel="icon" type="image/x-icon" href="assets/icon16.png" />
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
||||
|
||||
<!-- <script src="https://unpkg.com/@webcomponents/webcomponentsjs@2.0.3/custom-elements-es5-adapter.js"></script> -->
|
||||
<link rel="stylesheet" href="./styles.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
"ts-circular-deps:approve": "yarn -s ng-dev ts-circular-deps approve --config ./packages/circular-deps-test.conf.js",
|
||||
"check-tooling-setup": "yarn tsc --project .ng-dev/tsconfig.json && yarn tsc --project scripts/tsconfig.json",
|
||||
"devtools:devserver": "ibazel run //devtools/src:devserver",
|
||||
"devtools:devserver:demo-standalone": "ibazel run //devtools/projects/demo-standalone/src:devserver",
|
||||
"devtools:build:chrome": "bazelisk build --config snapshot-build --//devtools/projects/shell-browser/src:flag_browser=chrome -- devtools/projects/shell-browser/src:prodapp",
|
||||
"devtools:build:firefox": "bazelisk build --config snapshot-build --//devtools/projects/shell-browser/src:flag_browser=firefox -- devtools/projects/shell-browser/src:prodapp",
|
||||
"devtools:test": "bazelisk test --config snapshot-build --//devtools/projects/shell-browser/src:flag_browser=chrome -- //devtools/..."
|
||||
|
||||
Reference in New Issue
Block a user