Add demo-app (angular.dev tutorial)

This commit is contained in:
tsi 2025-05-06 18:54:53 +02:00
commit 1497598ace
Signed by: tosu
SSH Key Fingerprint: SHA256:12cSlRVb42jJ+Mk5F9Moq2uhJHVmIuaBoTyJTcoDS2s
28 changed files with 17538 additions and 0 deletions

42
demo-app/.gitignore vendored Normal file
View File

@ -0,0 +1,42 @@
# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files.
# Compiled output
/dist
/tmp
/out-tsc
/bazel-out
# Node
/node_modules
npm-debug.log
yarn-error.log
# IDEs and editors
.idea/
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# Visual Studio Code
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# Miscellaneous
/.angular/cache
.sass-cache/
/connect.lock
/coverage
/libpeerconnection.log
testem.log
/typings
# System files
.DS_Store
Thumbs.db

102
demo-app/angular.json Normal file
View File

@ -0,0 +1,102 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"first-app": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"inlineTemplate": true,
"inlineStyle": true,
"style": "scss",
"skipTests": true
},
"@schematics/angular:class": {
"skipTests": true
},
"@schematics/angular:directive": {
"skipTests": true
},
"@schematics/angular:guard": {
"skipTests": true
},
"@schematics/angular:interceptor": {
"skipTests": true
},
"@schematics/angular:pipe": {
"skipTests": true
},
"@schematics/angular:resolver": {
"skipTests": true
},
"@schematics/angular:service": {
"skipTests": true
}
},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular/build:application",
"options": {
"outputPath": "dist/first-app",
"index": "src/index.html",
"browser": "src/main.ts",
"polyfills": ["zone.js"],
"tsConfig": "tsconfig.app.json",
"inlineStyleLanguage": "scss",
"assets": ["src/favicon.ico", "src/assets"],
"styles": ["src/styles.css"],
"scripts": []
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"outputHashing": "all"
},
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular/build:dev-server",
"configurations": {
"production": {
"buildTarget": "first-app:build:production"
},
"development": {
"buildTarget": "first-app:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular/build:extract-i18n",
"options": {
"buildTarget": "first-app:build"
}
}
}
}
},
"cli": {
"analytics": false
}
}

104
demo-app/db.json Normal file
View File

@ -0,0 +1,104 @@
{
"locations": [
{
"id": 0,
"name": "Acme Fresh Start Housing",
"city": "Chicago",
"state": "IL",
"photo": "https://angular.dev/assets/images/tutorials/common/bernard-hermant-CLKGGwIBTaY-unsplash.jpg",
"availableUnits": 4,
"wifi": true,
"laundry": true
},
{
"id": 1,
"name": "A113 Transitional Housing",
"city": "Santa Monica",
"state": "CA",
"photo": "https://angular.dev/assets/images/tutorials/common/brandon-griggs-wR11KBaB86U-unsplash.jpg",
"availableUnits": 0,
"wifi": false,
"laundry": true
},
{
"id": 2,
"name": "Warm Beds Housing Support",
"city": "Juneau",
"state": "AK",
"photo": "https://angular.dev/assets/images/tutorials/common/i-do-nothing-but-love-lAyXdl1-Wmc-unsplash.jpg",
"availableUnits": 1,
"wifi": false,
"laundry": false
},
{
"id": 3,
"name": "Homesteady Housing",
"city": "Chicago",
"state": "IL",
"photo": "https://angular.dev/assets/images/tutorials/common/ian-macdonald-W8z6aiwfi1E-unsplash.jpg",
"availableUnits": 1,
"wifi": true,
"laundry": false
},
{
"id": 4,
"name": "Happy Homes Group",
"city": "Gary",
"state": "IN",
"photo": "https://angular.dev/assets/images/tutorials/common/krzysztof-hepner-978RAXoXnH4-unsplash.jpg",
"availableUnits": 1,
"wifi": true,
"laundry": false
},
{
"id": 5,
"name": "Hopeful Apartment Group",
"city": "Oakland",
"state": "CA",
"photo": "https://angular.dev/assets/images/tutorials/common/r-architecture-JvQ0Q5IkeMM-unsplash.jpg",
"availableUnits": 2,
"wifi": true,
"laundry": true
},
{
"id": 6,
"name": "Seriously Safe Towns",
"city": "Oakland",
"state": "CA",
"photo": "https://angular.dev/assets/images/tutorials/common/phil-hearing-IYfp2Ixe9nM-unsplash.jpg",
"availableUnits": 5,
"wifi": true,
"laundry": true
},
{
"id": 7,
"name": "Hopeful Housing Solutions",
"city": "Oakland",
"state": "CA",
"photo": "https://angular.dev/assets/images/tutorials/common/r-architecture-GGupkreKwxA-unsplash.jpg",
"availableUnits": 2,
"wifi": true,
"laundry": true
},
{
"id": 8,
"name": "Seriously Safe Towns",
"city": "Oakland",
"state": "CA",
"photo": "https://angular.dev/assets/images/tutorials/common/saru-robert-9rP3mxf8qWI-unsplash.jpg",
"availableUnits": 10,
"wifi": false,
"laundry": false
},
{
"id": 9,
"name": "Capital Safe Towns",
"city": "Portland",
"state": "OR",
"photo": "https://angular.dev/assets/images/tutorials/common/webaliser-_TPTXZd9mOo-unsplash.jpg",
"availableUnits": 6,
"wifi": true,
"laundry": true
}
]
}

Binary file not shown.

27
demo-app/learnings.md Normal file
View File

@ -0,0 +1,27 @@
# Learnings 02.05.2025
- main.ts --populates-> index.html
- app.component.ts == root component -> may also handle routing
- component hierarchy: components talk to subcomponents via "Inputs" ("inheritance")
- used with property bindings (`[bla]="value"`)
- or interpolation/mustache syntax (`{{ value }}`)
- components have html template, css style (scss, css, etc.), and ts (ts, class, decorators)
- all can be in separate files
- angular directives for "template metaprogramming"
- ngFor, ngForOf, etc.
- per route: render a different component (SPA) (handled by @angular/router)
- Route parameters (:id) enable to pass data to the components from the URL
- services (i.e. return json asynchronously, etc.) **inject** into other components
to make (dynamic) data available and reduce coupling
-> dependency injection \^^/
- Angular Forms: event listeners: (event), (submit), etc.
- HTTP communication via fetch (not Angular specific)

16750
demo-app/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

42
demo-app/package.json Normal file
View File

@ -0,0 +1,42 @@
{
"name": "angular.dev",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"watch": "ng build --watch --configuration development"
},
"private": true,
"dependencies": {
"@angular/animations": "^19.0.0",
"@angular/common": "^19.0.0",
"@angular/compiler": "^19.0.0",
"@angular/core": "^19.0.0",
"@angular/forms": "^19.0.0",
"@angular/platform-browser": "^19.0.0",
"@angular/router": "^19.0.0",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.15.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "^19.0.0",
"@angular/cli": "^19.0.0",
"@angular/compiler-cli": "^19.0.0",
"@types/jasmine": "~5.1.0",
"@types/node": "^16.11.35",
"copyfiles": "^2.4.1",
"jasmine-core": "~5.6.0",
"jasmine-marbles": "~0.9.2",
"jasmine-spec-reporter": "~7.0.0",
"karma": "~6.4.0",
"karma-chrome-launcher": "~3.2.0",
"karma-coverage": "~2.2.0",
"karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.1.0",
"protractor": "~7.0.0",
"ts-node": "~10.9.0",
"typescript": "~5.8.0"
}
}

View File

@ -0,0 +1,12 @@
:host {
--content-padding: 10px;
}
header {
display: block;
height: 60px;
padding: var(--content-padding);
box-shadow: 0px 5px 25px var(--shadow-color);
}
.content {
padding: var(--content-padding);
}

View File

@ -0,0 +1,23 @@
import {Component} from '@angular/core';
import {RouterModule} from '@angular/router';
@Component({
selector: 'app-root',
imports: [RouterModule],
template: `
<main>
<a [routerLink]="['/']">
<header class="brand-name">
<img class="brand-logo" src="/assets/logo.svg" alt="logo" aria-hidden="true" />
</header>
</a>
<section class="content">
<router-outlet></router-outlet>
</section>
</main>
`,
styleUrls: ['./app.component.css'],
})
export class AppComponent {
title = 'homes';
}

View File

@ -0,0 +1,13 @@
/*!
* @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 {ApplicationConfig} from '@angular/core';
export const appConfig: ApplicationConfig = {
providers: [],
};

View File

@ -0,0 +1,62 @@
.listing-photo {
height: 600px;
width: 50%;
object-fit: cover;
border-radius: 30px;
float: right;
}
.listing-heading {
font-size: 48pt;
font-weight: bold;
margin-bottom: 15px;
}
.listing-location::before {
content: url('/assets/location-pin.svg') / '';
}
.listing-location {
font-size: 24pt;
margin-bottom: 15px;
}
.listing-features > .section-heading {
color: var(--secondary-color);
font-size: 24pt;
margin-bottom: 15px;
}
.listing-features {
margin-bottom: 20px;
}
.listing-features li {
font-size: 14pt;
}
li {
list-style-type: '\1F44D';
}
.listing-apply .section-heading {
font-size: 18pt;
margin-bottom: 15px;
}
label, input {
display: block;
}
label {
color: var(--secondary-color);
font-weight: bold;
text-transform: uppercase;
font-size: 12pt;
}
input {
font-size: 16pt;
margin-bottom: 15px;
padding: 10px;
width: 400px;
border-top: none;
border-right: none;
border-left: none;
border-bottom: solid .3px;
}
@media (max-width: 1024px) {
.listing-photo {
width: 100%;
height: 400px;
}
}

View File

@ -0,0 +1,69 @@
import { Component, inject } from '@angular/core';
import {ActivatedRoute} from '@angular/router';
import {HousingService} from '../housing.service';
import {HousingLocation} from '../housinglocation';
import {FormControl, FormGroup, ReactiveFormsModule} from '@angular/forms';
@Component({
selector: 'app-details',
imports: [ReactiveFormsModule],
template: `
<article>
<img
class="listing-photo"
[src]="housingLocation?.photo"
alt="Exterior photo of {{ housingLocation?.name }}"
crossorigin
/>
<section>
<h2 class="listing-heading">{{ housingLocation?.name }}</h2>
<p class="listing-location">{{ housingLocation?.city }}, {{ housingLocation?.state }}</p>
</section>
<section class="listing-features">
<h2 class="section-heading">About this housing location</h2>
<ul>
<li>Units available: {{ housingLocation?.availableUnits }}</li>
<li>Does this location have wifi: {{ housingLocation?.wifi }}</li>
<li>Does this location have laundry: {{ housingLocation?.laundry }}</li>
</ul>
</section>
<section class="listing-apply">
<h2 class="section-heading">Apply now to live here</h2>
<form [formGroup]="applyForm" (submit)="submitApplication()">
<label for="first-name">First Name</label>
<input id="first-name" type="text" formControlName="firstName" />
<label for="last-name">Last Name</label>
<input id="last-name" type="text" formControlName="lastName" />
<label for="email">Email</label>
<input id="email" type="email" formControlName="email" />
<button type="submit" class="primary">Apply now</button>
</form>
</section>
</article>
`,
styleUrls: ['./details.component.css'],
})
export class DetailsComponent {
route: ActivatedRoute = inject(ActivatedRoute);
housingLocationId = -1;
housingService = inject(HousingService);
housingLocation: HousingLocation | undefined;
applyForm = new FormGroup({
firstName: new FormControl(''),
lastName: new FormControl(''),
email: new FormControl(''),
});
constructor() {
this.housingLocationId = Number(this.route.snapshot.params['id']);
this.housingService.getHousingLocationById(this.housingLocationId).then((housingLocation) => {
this.housingLocation = housingLocation;
});
}
submitApplication() {
this.housingService.submitApplication(
this.applyForm.value.firstName ?? '',
this.applyForm.value.lastName ?? '',
this.applyForm.value.email ?? '',
);
}
}

View File

@ -0,0 +1,37 @@
.results {
display: grid;
column-gap: 14px;
row-gap: 14px;
grid-template-columns: repeat(auto-fill, minmax(400px, 400px));
margin-top: 50px;
justify-content: space-around;
}
input[type="text"] {
border: solid 1px var(--primary-color);
padding: 10px;
border-radius: 10px;
margin-right: 4px;
display: inline-block;
width: 70%;
}
button {
padding: 10px;
border: solid 1px var(--primary-color);
background: var(--primary-color);
color: white;
border-radius: 8px;
}
@media (min-width: 500px) and (max-width: 768px) {
.results {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 499px) {
.results {
grid-template-columns: 1fr;
}
}

View File

@ -0,0 +1,47 @@
import {Component, inject} from '@angular/core';
import {CommonModule} from '@angular/common';
import {HousingLocationComponent} from '../housing-location/housing-location.component';
import {HousingService} from '../housing.service';
import {HousingLocation} from '../housinglocation';
@Component({
selector: 'app-home',
imports: [CommonModule, HousingLocationComponent],
template: `
<section>
<form>
<input type="text" placeholder="Filter by city" #filter />
<button class="primary" type="button" (click)="filterResults(filter.value)">Search</button>
</form>
</section>
<section class="results">
<app-housing-location
*ngFor="let housingLocation of filteredLocationList"
[housingLocation]="housingLocation"
></app-housing-location>
</section>
`,
styleUrls: ['./home.component.css'],
})
export class HomeComponent {
housingLocationList: HousingLocation[] = [];
housingService: HousingService = inject(HousingService);
filteredLocationList: HousingLocation[] = [];
constructor() {
this.housingService.getAllHousingLocations().then((housingLocationList: HousingLocation[]) => {
this.housingLocationList = housingLocationList;
this.filteredLocationList = this.housingLocationList;
});
}
filterResults(text: string) {
if (!text) {
this.filteredLocationList = this.housingLocationList;
return;
}
this.filteredLocationList = this.housingLocationList.filter((housingLocation) =>
housingLocation?.city.toLowerCase().includes(text.toLowerCase())
);
}
}

View File

@ -0,0 +1,30 @@
.listing {
background: var(--accent-color);
border-radius: 30px;
padding-bottom: 30px;
}
.listing-heading {
color: var(--primary-color);
padding: 10px 20px 0 20px;
}
.listing-photo {
height: 250px;
width: 100%;
object-fit: cover;
border-radius: 30px 30px 0 0;
}
.listing-location {
padding: 10px 20px 20px 20px;
}
.listing-location::before {
content: url("/assets/location-pin.svg") / "";
}
section.listing a {
padding-left: 20px;
text-decoration: none;
color: var(--primary-color);
}
section.listing a::after {
content: "\203A";
margin-left: 5px;
}

View File

@ -0,0 +1,25 @@
import { Component, Input } from '@angular/core';
import { RouterModule } from '@angular/router';
import {HousingLocation} from '../housinglocation';
@Component({
selector: 'app-housing-location',
imports: [RouterModule],
template: `
<section class="listing">
<img
class="listing-photo"
[src]="housingLocation.photo"
alt="Exterior photo of {{ housingLocation.name }}"
crossorigin
/>
<h2 class="listing-heading">{{ housingLocation.name }}</h2>
<p class="listing-location">{{ housingLocation.city }}, {{ housingLocation.state }}</p>
<a [routerLink]="['/details', housingLocation.id]">Learn More</a>
</section>
`,
styleUrls: ['./housing-location.component.css'],
})
export class HousingLocationComponent {
@Input() housingLocation!: HousingLocation;
}

View File

@ -0,0 +1,23 @@
import { Injectable } from '@angular/core';
import { HousingLocation } from './housinglocation';
@Injectable({
providedIn: 'root'
})
export class HousingService {
readonly baseUrl = 'https://angular.dev/assets/images/tutorials/common';
url = 'http://localhost:3000/locations';
constructor() { }
async getAllHousingLocations(): Promise<HousingLocation[]> {
const data = await fetch(this.url);
return (await data.json()) ?? [];
}
async getHousingLocationById(id: number): Promise<HousingLocation | undefined> {
const data = await fetch(`${this.url}?id=${id}`);
const locationJson = await data.json();
return locationJson[0] ?? {};
}
submitApplication(firstName: string, lastName: string, email: string) {
console.log(`Homes application received: firstName: ${firstName}, lastName: ${lastName}, email: ${email}.`);
}
}

View File

@ -0,0 +1,10 @@
export interface HousingLocation {
id: number;
name: string;
city: string;
state: string;
photo: string;
availableUnits: number;
wifi: boolean;
laundry: boolean;
}

View File

@ -0,0 +1,18 @@
import { Routes } from "@angular/router";
import { HomeComponent } from "./home/home.component";
import { DetailsComponent } from "./details/details.component";
const routeConfig: Routes = [
{
path: '',
component: HomeComponent,
title: 'Home page',
},
{
path: 'details/:id',
component: DetailsComponent,
title: 'Home details',
},
];
export default routeConfig;

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 223 236"><g clip-path="url(#a)"><path fill="url(#b)" d="m222.077 39.192-8.019 125.923L137.387 0l84.69 39.192Zm-53.105 162.825-57.933 33.056-57.934-33.056 11.783-28.556h92.301l11.783 28.556ZM111.039 62.675l30.357 73.803H80.681l30.358-73.803ZM7.937 165.115 0 39.192 84.69 0 7.937 165.115Z"/><path fill="url(#c)" d="m222.077 39.192-8.019 125.923L137.387 0l84.69 39.192Zm-53.105 162.825-57.933 33.056-57.934-33.056 11.783-28.556h92.301l11.783 28.556ZM111.039 62.675l30.357 73.803H80.681l30.358-73.803ZM7.937 165.115 0 39.192 84.69 0 7.937 165.115Z"/></g><defs><linearGradient id="b" x1="49.009" x2="225.829" y1="213.75" y2="129.722" gradientUnits="userSpaceOnUse"><stop stop-color="#E40035"/><stop offset=".24" stop-color="#F60A48"/><stop offset=".352" stop-color="#F20755"/><stop offset=".494" stop-color="#DC087D"/><stop offset=".745" stop-color="#9717E7"/><stop offset="1" stop-color="#6C00F5"/></linearGradient><linearGradient id="c" x1="41.025" x2="156.741" y1="28.344" y2="160.344" gradientUnits="userSpaceOnUse"><stop stop-color="#FF31D9"/><stop offset="1" stop-color="#FF5BE1" stop-opacity="0"/></linearGradient><clipPath id="a"><path fill="#fff" d="M0 0h223v236H0z"/></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="20pt" height="20pt" version="1.1" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<path d="m50 11.602c-15.398 0-27.898 12.5-27.898 27.898 0 5 1.3008 9.6992 3.6016 13.801l2.8984 4.1992 21.398 30.898 21.801-31.398 2.1992-3.1992c2.5-4.1992 3.8984-9 3.8984-14.199 0-15.5-12.5-28-27.898-28zm0 35.598c-4.1992 0-7.6992-3.3984-7.6992-7.6992s3.3984-7.6992 7.6992-7.6992 7.6992 3.3984 7.6992 7.6992-3.5 7.6992-7.6992 7.6992z" fill="#605cc8"/>
</svg>

After

Width:  |  Height:  |  Size: 502 B

View File

@ -0,0 +1,7 @@
<svg width="151" height="44" viewBox="0 0 151 44" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.15532 33.2946C5.25369 33.2946 4.58006 32.5707 4.58006 31.6701V24.0152C4.58006 19.5469 4.35206 20.6276 7.59585 17.4236C21.7317 4.06732 16.2425 4.24744 29.6149 18.054C32.0434 20.7627 31.9985 19.4534 31.9985 23.8316V31.6667C31.9985 32.5707 31.28 33.2912 30.3749 33.2912H6.15532V33.2946Z" fill="#605DC8"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M30.896 35.1547L5.73676 35.2586C4.80059 35.2621 3.75042 35.8232 3.37388 36.5229L2.90061 37.4027L0.537726 41.7878L0.140457 42.529C-0.236085 43.2253 0.164639 43.7829 1.10081 43.7795L26.26 43.6756C27.1962 43.6721 28.2464 43.111 28.6229 42.4113L29.0962 41.5315L31.4591 37.1463L31.8563 36.4051C32.2329 35.7089 31.8321 35.1512 30.896 35.1547Z" fill="#605DC8"/>
<path d="M2.54861 18.7156C2.23079 18.7156 1.90952 18.629 1.6228 18.4454C0.821352 17.9363 0.5899 16.859 1.09771 16.0554C1.46389 15.4701 2.70061 14.2231 5.50912 11.4659L6.39693 10.5931C8.73218 8.38318 10.5147 6.56817 11.9449 5.10646C15.1679 1.81587 16.7985 0.153263 18.9023 0.222538C20.9888 0.277958 22.4362 1.92325 25.3138 5.19651C26.7267 6.8037 28.4816 8.8023 30.8514 11.2477C30.8652 11.265 30.8825 11.2789 30.8963 11.2962C31.228 11.6634 31.715 12.2003 32.2401 12.7752C33.9743 14.6734 34.8206 15.6086 35.0763 15.9688C35.629 16.7413 35.4494 17.8185 34.679 18.3727C33.9087 18.9234 32.8343 18.7468 32.2816 17.9744C32.0639 17.6869 30.5889 16.0693 29.708 15.1064C29.1863 14.5348 28.6993 14.0014 28.3642 13.6308C25.9495 11.1369 24.1704 9.11057 22.7368 7.4826C21.0233 5.5325 19.4032 3.68977 18.809 3.67245H18.8021C18.1699 3.67245 16.3356 5.54289 14.3976 7.52417C12.9467 9.00666 11.14 10.8494 8.78054 13.0766L7.91001 13.9321C6.74929 15.0717 4.33804 17.4375 3.97877 17.9293C3.65405 18.4454 3.10824 18.7225 2.54515 18.7225L2.54861 18.7156Z" fill="#605DC8"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M22.0976 33.2115V26.0588C22.0976 25.4665 21.6174 24.985 21.0267 24.985H15.1955C14.6047 24.985 14.1246 25.4665 14.1246 26.0588V33.2115C14.1246 33.2704 14.1142 33.3293 14.0969 33.3847H22.1252C22.1079 33.3293 22.0976 33.2739 22.0976 33.2115Z" fill="white"/>
<path d="M58.9784 10.0512V32.2205H54.5319V22.7874H45.0353V32.2205H40.5887V10.0512H45.0353V19.1666H54.5319V10.0512H58.9784ZM70.8506 32.5063C69.1567 32.5063 67.6322 32.1358 66.277 31.3947C64.9219 30.6324 63.8526 29.5631 63.0692 28.1868C62.3069 26.8105 61.9258 25.2224 61.9258 23.4226C61.9258 21.6228 62.3175 20.0348 63.1009 18.6585C63.9055 17.2821 64.996 16.2234 66.3723 15.4823C67.7486 14.7201 69.2838 14.3389 70.9777 14.3389C72.6716 14.3389 74.2067 14.7201 75.5831 15.4823C76.9594 16.2234 78.0393 17.2821 78.8227 18.6585C79.6273 20.0348 80.0296 21.6228 80.0296 23.4226C80.0296 25.2224 79.6167 26.8105 78.7909 28.1868C77.9863 29.5631 76.8853 30.6324 75.4878 31.3947C74.1115 32.1358 72.5657 32.5063 70.8506 32.5063ZM70.8506 28.6315C71.6553 28.6315 72.4069 28.4409 73.1057 28.0597C73.8256 27.6574 74.3973 27.0646 74.8208 26.2811C75.2443 25.4977 75.456 24.5449 75.456 23.4226C75.456 21.7499 75.0114 20.4688 74.122 19.5795C73.2539 18.669 72.1846 18.2138 70.9142 18.2138C69.6437 18.2138 68.5744 18.669 67.7063 19.5795C66.8593 20.4688 66.4358 21.7499 66.4358 23.4226C66.4358 25.0954 66.8487 26.387 67.6745 27.2975C68.5215 28.1868 69.5802 28.6315 70.8506 28.6315ZM104.825 14.3707C106.985 14.3707 108.721 15.0377 110.034 16.3717C111.368 17.6844 112.035 19.5266 112.035 21.8981V32.2205H107.589V22.5016C107.589 21.1252 107.239 20.0771 106.54 19.3572C105.842 18.6161 104.889 18.2456 103.682 18.2456C102.475 18.2456 101.512 18.6161 100.792 19.3572C100.093 20.0771 99.7436 21.1252 99.7436 22.5016V32.2205H95.297V22.5016C95.297 21.1252 94.9477 20.0771 94.2489 19.3572C93.5502 18.6161 92.5974 18.2456 91.3904 18.2456C90.1623 18.2456 89.1883 18.6161 88.4684 19.3572C87.7697 20.0771 87.4203 21.1252 87.4203 22.5016V32.2205H82.9737V14.6248H87.4203V16.7528C87.992 16.0117 88.7225 15.4294 89.6118 15.0059C90.5223 14.5824 91.5175 14.3707 92.5974 14.3707C93.9737 14.3707 95.2018 14.6671 96.2816 15.26C97.3615 15.8317 98.1979 16.6575 98.7908 17.7374C99.3625 16.721 100.188 15.9058 101.268 15.2918C102.369 14.6777 103.555 14.3707 104.825 14.3707ZM132.294 23.0415C132.294 23.6767 132.252 24.2484 132.167 24.7566H119.304C119.41 26.027 119.854 27.0222 120.638 27.7421C121.421 28.4621 122.385 28.822 123.528 28.822C125.18 28.822 126.355 28.1127 127.053 26.694H131.849C131.341 28.3879 130.367 29.7854 128.927 30.8865C127.488 31.9664 125.72 32.5063 123.623 32.5063C121.929 32.5063 120.405 32.1358 119.05 31.3947C117.716 30.6324 116.668 29.5631 115.905 28.1868C115.164 26.8105 114.794 25.2224 114.794 23.4226C114.794 21.6017 115.164 20.003 115.905 18.6267C116.646 17.2504 117.684 16.1917 119.018 15.4506C120.352 14.7095 121.887 14.3389 123.623 14.3389C125.296 14.3389 126.789 14.6989 128.102 15.4188C129.436 16.1387 130.462 17.1657 131.182 18.4996C131.924 19.8124 132.294 21.3264 132.294 23.0415ZM127.689 21.771C127.668 20.6276 127.255 19.7172 126.45 19.0396C125.645 18.3408 124.661 17.9915 123.496 17.9915C122.395 17.9915 121.464 18.3303 120.701 19.0078C119.96 19.6642 119.505 20.5853 119.336 21.771H127.689ZM141.955 32.5063C140.516 32.5063 139.224 32.2522 138.081 31.744C136.937 31.2147 136.027 30.5054 135.349 29.616C134.693 28.7267 134.333 27.7421 134.269 26.6623H138.748C138.832 27.3398 139.16 27.9009 139.732 28.3456C140.325 28.7903 141.055 29.0126 141.924 29.0126C142.771 29.0126 143.427 28.8432 143.893 28.5044C144.38 28.1656 144.623 27.7316 144.623 27.2022C144.623 26.6305 144.327 26.207 143.734 25.9318C143.162 25.6353 142.241 25.3177 140.971 24.9789C139.658 24.6613 138.578 24.3331 137.731 23.9943C136.905 23.6555 136.185 23.1368 135.571 22.438C134.979 21.7393 134.682 20.797 134.682 19.6113C134.682 18.6373 134.957 17.748 135.508 16.9434C136.08 16.1387 136.884 15.5035 137.922 15.0377C138.98 14.5719 140.219 14.3389 141.638 14.3389C143.734 14.3389 145.407 14.8683 146.656 15.927C147.905 16.9645 148.593 18.3726 148.721 20.1512H144.465C144.401 19.4525 144.105 18.902 143.575 18.4996C143.067 18.0762 142.379 17.8644 141.511 17.8644C140.706 17.8644 140.081 18.0126 139.637 18.3091C139.213 18.6055 139.002 19.0184 139.002 19.5478C139.002 20.1406 139.298 20.5959 139.891 20.9135C140.484 21.2099 141.405 21.517 142.654 21.8346C143.925 22.1522 144.973 22.4804 145.798 22.8192C146.624 23.1579 147.334 23.6873 147.926 24.4072C148.541 25.106 148.858 26.0376 148.879 27.2022C148.879 28.2186 148.593 29.129 148.022 29.9337C147.471 30.7383 146.667 31.3735 145.608 31.8393C144.57 32.284 143.353 32.5063 141.955 32.5063Z" fill="#605DC8"/>
</svg>

After

Width:  |  Height:  |  Size: 6.5 KiB

BIN
demo-app/src/favicon.ico Normal file

Binary file not shown.

14
demo-app/src/index.html Normal file
View File

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Homes</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link href="https://fonts.googleapis.com/css2?family=Be+Vietnam+Pro:ital,wght@0,400;0,700;1,400;1,700&display=swap" rel="stylesheet">
</head>
<body>
<app-root></app-root>
</body>
</html>

12
demo-app/src/main.ts Normal file
View File

@ -0,0 +1,12 @@
/*
* Protractor support is deprecated in Angular.
* Protractor is used in this example for compatibility with Angular documentation tools.
*/
import {bootstrapApplication, provideProtractorTestingSupport} from '@angular/platform-browser';
import {AppComponent} from './app/app.component';
import {provideRouter} from '@angular/router';
import routeConfig from './app/routes';
bootstrapApplication(AppComponent, {providers: [provideProtractorTestingSupport(), provideRouter(routeConfig)]}).catch((err) =>
console.error(err),
);

23
demo-app/src/styles.css Normal file
View File

@ -0,0 +1,23 @@
/* You can add global styles to this file, and also import other style files */
* {
margin: 0;
padding: 0;
}
body {
font-family: 'Be Vietnam Pro', sans-serif;
}
:root {
--primary-color: #605DC8;
--secondary-color: #8B89E6;
--accent-color: #e8e7fa;
--shadow-color: #E8E8E8;
}
button.primary {
padding: 10px;
border: solid 1px var(--primary-color);
background: var(--primary-color);
color: white;
border-radius: 8px;
}

View File

@ -0,0 +1,10 @@
/* To learn more about this file see: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/app",
"types": []
},
"files": ["src/main.ts"],
"include": ["src/**/*.d.ts"]
}

31
demo-app/tsconfig.json Normal file
View File

@ -0,0 +1,31 @@
/* To learn more about this file see: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"sourceMap": true,
"declaration": false,
"downlevelIteration": true,
"experimentalDecorators": true,
"moduleResolution": "node",
"importHelpers": true,
"target": "ES2022",
"module": "ES2022",
"useDefineForClassFields": false,
"lib": ["ES2022", "dom"]
},
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false,
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"strictTemplates": true,
"_enabledBlockTypes": ["if", "for"]
}
}