Change Theme, Animations, and Loading Spinners

This commit is contained in:
Charles Showalter 2022-05-17 11:59:02 -07:00
parent 3f0a50d2bd
commit f3ef7e326e
21 changed files with 238 additions and 73 deletions

View File

@ -30,7 +30,9 @@
"styles": [
"./node_modules/font-awesome/css/font-awesome.css",
"./node_modules/ngx-toastr/toastr.css",
"src/styles.scss"
"src/styles.scss",
"./node_modules/ngx-spinner/animations/ball-clip-rotate-multiple.css",
"./node_modules/bootswatch/dist/cyborg/bootstrap.min.css"
],
"scripts": []
},

View File

@ -17,8 +17,10 @@
"@angular/platform-browser-dynamic": "~13.3.0",
"@angular/router": "~13.3.0",
"bootstrap": "^5.1.3",
"bootswatch": "^5.1.3",
"font-awesome": "^4.7.0",
"ngx-bootstrap": "^8.0.0",
"ngx-spinner": "^13.1.1",
"ngx-toastr": "^14.3.0",
"rxjs": "~7.5.0",
"tslib": "^2.3.0",
@ -3511,6 +3513,11 @@
"@popperjs/core": "^2.10.2"
}
},
"node_modules/bootswatch": {
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/bootswatch/-/bootswatch-5.1.3.tgz",
"integrity": "sha512-NmZFN6rOCoXWQ/PkzmD8FFWDe24kocX9OXWHNVaLxVVnpqpAzEbMFsf8bAfKwVtpNXibasZCzv09B5fLieAh2g=="
},
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@ -7976,6 +7983,18 @@
"rxjs": "^6.5.3 || ^7.4.0"
}
},
"node_modules/ngx-spinner": {
"version": "13.1.1",
"resolved": "https://registry.npmjs.org/ngx-spinner/-/ngx-spinner-13.1.1.tgz",
"integrity": "sha512-6aJz4KxIsBrlQckJxcM3CEvMcYbZoSDnQZPu0F/ZYAYunbBOTb9iydw0Gjczg9moum1VURWjX5dTVKmFo85c2Q==",
"dependencies": {
"tslib": "^2.3.0"
},
"peerDependencies": {
"@angular/common": "^13.0.0",
"@angular/core": "^13.0.0"
}
},
"node_modules/ngx-toastr": {
"version": "14.3.0",
"resolved": "https://registry.npmjs.org/ngx-toastr/-/ngx-toastr-14.3.0.tgz",
@ -14317,6 +14336,11 @@
"integrity": "sha512-fcQztozJ8jToQWXxVuEyXWW+dSo8AiXWKwiSSrKWsRB/Qt+Ewwza+JWoLKiTuQLaEPhdNAJ7+Dosc9DOIqNy7Q==",
"requires": {}
},
"bootswatch": {
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/bootswatch/-/bootswatch-5.1.3.tgz",
"integrity": "sha512-NmZFN6rOCoXWQ/PkzmD8FFWDe24kocX9OXWHNVaLxVVnpqpAzEbMFsf8bAfKwVtpNXibasZCzv09B5fLieAh2g=="
},
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@ -17564,6 +17588,14 @@
"tslib": "^2.0.0"
}
},
"ngx-spinner": {
"version": "13.1.1",
"resolved": "https://registry.npmjs.org/ngx-spinner/-/ngx-spinner-13.1.1.tgz",
"integrity": "sha512-6aJz4KxIsBrlQckJxcM3CEvMcYbZoSDnQZPu0F/ZYAYunbBOTb9iydw0Gjczg9moum1VURWjX5dTVKmFo85c2Q==",
"requires": {
"tslib": "^2.3.0"
}
},
"ngx-toastr": {
"version": "14.3.0",
"resolved": "https://registry.npmjs.org/ngx-toastr/-/ngx-toastr-14.3.0.tgz",

View File

@ -19,8 +19,10 @@
"@angular/platform-browser-dynamic": "~13.3.0",
"@angular/router": "~13.3.0",
"bootstrap": "^5.1.3",
"bootswatch": "^5.1.3",
"font-awesome": "^4.7.0",
"ngx-bootstrap": "^8.0.0",
"ngx-spinner": "^13.1.1",
"ngx-toastr": "^14.3.0",
"rxjs": "~7.5.0",
"tslib": "^2.3.0",

View File

@ -1,5 +1,6 @@
<ngx-spinner>
<h3>Loading...</h3>
</ngx-spinner>
<app-nav-bar></app-nav-bar>
<app-section-header></app-section-header>
<div class="container">
<router-outlet></router-outlet>
</div>
<router-outlet></router-outlet>

View File

@ -9,6 +9,8 @@ import { CoreModule } from './core/core.module';
import { ShopModule } from './shop/shop.module';
import { HomeModule } from './home/home.module';
import { ErrorInterceptor } from './core/interceptors/error.interceptor';
import { NgxSpinnerModule } from 'ngx-spinner';
import { LoadingInterceptor } from './core/interceptors/loading.interceptor';
@NgModule({
declarations: [
@ -20,10 +22,12 @@ import { ErrorInterceptor } from './core/interceptors/error.interceptor';
BrowserAnimationsModule,
HttpClientModule,
CoreModule,
HomeModule
HomeModule,
NgxSpinnerModule
],
providers: [
{provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true}
{provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true},
{provide: HTTP_INTERCEPTORS, useClass: LoadingInterceptor, multi: true}
],
bootstrap: [AppComponent]
})

View File

@ -5,7 +5,7 @@ import {
HttpEvent,
HttpInterceptor
} from '@angular/common/http';
import { catchError, Observable, throwError } from 'rxjs';
import { catchError, delay, Observable, throwError } from 'rxjs';
import { NavigationExtras, Router } from '@angular/router';
import { ToastrService } from 'ngx-toastr';

View File

@ -0,0 +1,20 @@
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from "@angular/common/http";
import { Injectable } from "@angular/core";
import { delay, finalize, Observable } from "rxjs";
import { BusyService } from "../services/busy.service";
@Injectable()
export class LoadingInterceptor implements HttpInterceptor {
constructor(private busyService: BusyService) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
this.busyService.busy();
return next.handle(req).pipe(
delay(1000),
finalize(()=> {
this.busyService.idle();
})
);
}
}

View File

@ -1,4 +1,4 @@
<div class="d-flex flex-column flex-md-row align-items-center justify-content-between p-3 px-md-4 mb-3 bg-white border-bottom shadown-sm fixed-top">
<div class="d-flex flex-column flex-md-row align-items-center justify-content-between p-3 px-md-4 mb-3 border-bottom border-dark fixed-top" style="background-color: #0d0d0e;">
<img class="logo" src="/assets/images/logo.png" style="max-height: 70px;" alt="logo" routerLink="/">
<nav class="me-3 my-md-0 mr-md-3 text-uppercase" style="font-size: larger;">
<a class="me-3 py-2" [routerLink]="['/']" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}">Home</a>

View File

@ -1,9 +1,9 @@
<ng-container *ngIf="(breadcrumb$ | async) as breadcrumbs">
<section *ngIf="breadcrumbs.length > 0 && breadcrumbs[breadcrumbs.length-1].label !== 'Home'" class="py-5" style="margin-top: 105px; background-color:#f5f5f5;">
<section *ngIf="breadcrumbs.length > 0 && breadcrumbs[breadcrumbs.length-1].label !== 'Home'" class="py-5" style="margin-top: 105px;">
<div class="container">
<div class="row d-flex align-item-center">
<div class="col-9">
<h1>{{breadcrumbs.length > 0 && breadcrumbs[breadcrumbs.length-1].label | titlecase}}</h1>
<h1 class="text-dark">{{breadcrumbs.length > 0 && breadcrumbs[breadcrumbs.length-1].label | titlecase}}</h1>
</div>
<div class="col-3">
<xng-breadcrumb></xng-breadcrumb>

View File

@ -0,0 +1,30 @@
import { Injectable } from '@angular/core';
import { NgxSpinnerService } from 'ngx-spinner';
@Injectable({
providedIn: 'root'
})
export class BusyService {
busyRequestCount = 0;
constructor(private spinnerService: NgxSpinnerService) { }
busy(){
this.busyRequestCount++;
this.spinnerService.show(undefined, {
type: 'ball-clip-rotate-multiple',
size: 'large',
bdColor: 'rgba(0, 0, 0, 0.8)',
color: '#dddddd'
});
}
idle(){
this.busyRequestCount--;
if (this.busyRequestCount <= 0){
this.busyRequestCount = 0;
this.spinnerService.hide();
}
}
}

View File

@ -1 +1,16 @@
<p>home works!</p>
<carousel>
<slide>
<img src="assets/images/hero1.jpg" alt="first slide" style="display: block; width: 100%">
</slide>
<slide>
<img src="assets/images/hero2.jpg" alt="first slide" style="display: block; width: 100%">
</slide>
<slide>
<img src="assets/images/hero3.jpg" alt="first slide" style="display: block; width: 100%">
</slide>
</carousel>
<section class="featured">
<div class="d-flex justify-content-center pt-5">
<h1>Welcome to our Shop!</h1>
</div>
</section>

View File

@ -0,0 +1,9 @@
.featured {
height: 250px;
}
img {
object-fit: cover;
height: 600px;
width: 100%;
}

View File

@ -1,6 +1,7 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HomeComponent } from './home.component';
import { SharedModule } from '../shared/shared.module';
@ -9,7 +10,8 @@ import { HomeComponent } from './home.component';
HomeComponent
],
imports: [
CommonModule
CommonModule,
SharedModule
],
exports: [HomeComponent]
})

View File

@ -1,6 +1,7 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { PaginationModule } from 'ngx-bootstrap/pagination';
import { CarouselModule } from 'ngx-bootstrap/carousel';
import { PagingHeaderComponent } from './components/paging-header/paging-header.component';
import { PagerComponent } from './components/pager/pager.component';
@ -13,12 +14,14 @@ import { PagerComponent } from './components/pager/pager.component';
],
imports: [
CommonModule,
PaginationModule.forRoot()
PaginationModule.forRoot(),
CarouselModule.forRoot()
],
exports: [
PaginationModule,
PagingHeaderComponent,
PagerComponent
PagerComponent,
CarouselModule
]
})
export class SharedModule { }

View File

@ -1,22 +1,23 @@
<div class="row" *ngIf="product">
<div class="col-6">
<img src="{{product.pictureUrl}}" alt="{{product.name}}" class="img-fluid w-100">
</div>
<div class="col-6">
<h3>{{product.name}}</h3>
<p>{{product.price | currency}}</p>
<div class="d-flex justify-content-start align-items-center">
<i class="fa fa-minus-circle text-warning me-2" style="cursor: pointer; font-size: 2em"></i>
<span class="font-weight-bold" style="font-size: 1.5em;">2</span>
<i class="fa fa-plus-circle text-warning mx-2" style="cursor: pointer; font-size: 2em"></i>
<button class="btn btn-outline-secondary btn-lg ms-4">Ad to Cart</button>
<div class="container mt-5">
<div class="row" *ngIf="product">
<div class="col-6">
<img src="{{product.pictureUrl}}" alt="{{product.name}}" class="img-fluid w-100">
</div>
</div>
<div class="row mt-5">
<div class="col-12 ms-3">
<h4>Description</h4>
<p>{{product.description}}</p>
<div class="col-6">
<h3>{{product.name}}</h3>
<p style="font-size: 2em;">{{product.price | currency}}</p>
<div class="d-flex justify-content-start align-items-center">
<i class="fa fa-minus-circle text-warning me-2" style="cursor: pointer; font-size: 2em"></i>
<span class="font-weight-bold" style="font-size: 1.5em;">2</span>
<i class="fa fa-plus-circle text-warning mx-2" style="cursor: pointer; font-size: 2em"></i>
<button class="btn btn-outline-secondary btn-lg ms-4">Ad to Cart</button>
</div>
</div>
<div class="row mt-5">
<div class="col-12 ms-3">
<h4>Description</h4>
<p>{{product.description}}</p>
</div>
</div>
</div>
</div>

View File

@ -12,7 +12,9 @@ import { ShopService } from '../shop.service';
export class ProductDetailsComponent implements OnInit {
product: IProduct;
constructor(private shopService: ShopService, private activatedRoute: ActivatedRoute, private bcService: BreadcrumbService) { }
constructor(private shopService: ShopService, private activatedRoute: ActivatedRoute, private bcService: BreadcrumbService) {
this.bcService.set('@productDetails', ' ');
}
ngOnInit(): void {
this.loadProduct();

View File

@ -1,13 +1,15 @@
<div class="card h-100 shadow-sm">
<img src="{{product.pictureUrl}}" alt="{{product.name}}" class="img-fluid bg-light">
<div class="d-flex align-items-center justify-content-center hover-overlay">
<button type="button" class="btn btn-sm btn-primary fa fa-shopping-cart me-2"></button>
<button routerLink="/shop/{{product.id}}" type="button" class="btn btn-sm btn-primary">View</button>
</div>
<div class="card-body d-flex flex-column">
<a routerLink="/shop/{{product.id}}">
<h6 class="text-uppercase">{{product.name}}</h6>
</a>
<span class="mb-2">{{product.price | currency}}</span>
</div>
<div class="image position-relative" style="cursor: pointer;">
<img src="{{product.pictureUrl}}" alt="{{product.name}}" class="img-fluid bg-light">
<div class="d-flex align-items-center justify-content-center hover-overlay">
<button type="button" class="btn btn-sm btn-primary fa fa-shopping-cart me-2"></button>
<button routerLink="/shop/{{product.id}}" type="button" class="btn btn-sm btn-primary">View</button>
</div>
</div>
<div class="card-body d-flex flex-column">
<a routerLink="/shop/{{product.id}}">
<h6 class="text-uppercase">{{product.name}}</h6>
</a>
<span class="mb-2">{{product.price | currency}}</span>
</div>
</div>

View File

@ -2,3 +2,36 @@
width: 30%;
height: 40px;
}
.image :hover {
opacity: 1;
& button {
transform: none;
opacity: 1;
}
}
.hover-overlay {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
background-color: rgba($color: rgb(0, 0, 0), $alpha: 0.5);
opacity: 0;
transition: all 0.5s;
& button {
z-index: 1000;
transition: all 0.5s;
}
& button:first-of-type {
transform: translateX(-20px);
}
& button:last-of-type {
transform: translateX(20px);
}
}

View File

@ -1,32 +1,34 @@
<div class="container">
<div class="row mt-5">
<section class="col-3">
<h5 class="text-warning ml-3">Sort</h5>
<select class="form-select mb-3" style="width: 100%;" (change)="onSortSelected($event.target.value)">
<option *ngFor="let sort of sortOptions"
[value]="sort.value"
>
{{sort.name}}
</option>
</select>
<h5 class="text-warning ml-3">Brands</h5>
<ul class="list-group my-3">
<li class="list-group-item"
*ngFor="let brand of brands"
[class.active]="brand.id === this.shopParams.brandId"
[value]="brand.id"
(click)="onBrandSelected(brand.id)"
>{{brand.name}}</li>
</ul>
<h5 class="text-warning ml-3">Type</h5>
<ul class="list-group my-3">
<li class="list-group-item"
*ngFor="let type of productType"
[class.active]="type.id === this.shopParams.typeId"
[value]="type.id"
(click)="onTypeSelected(type.id)"
>{{type.name}}</li>
</ul>
<ng-container *ngIf="productType && brands">
<h5 class="text-warning ml-3">Sort</h5>
<select class="form-select mb-3" style="width: 100%;" (change)="onSortSelected($event.target.value)">
<option *ngFor="let sort of sortOptions"
[value]="sort.value"
>
{{sort.name}}
</option>
</select>
<h5 class="text-warning ml-3">Brands</h5>
<ul class="list-group my-3">
<li class="list-group-item" style="cursor: pointer;"
*ngFor="let brand of brands"
[class.active]="brand.id === this.shopParams.brandId"
[value]="brand.id"
(click)="onBrandSelected(brand.id)"
>{{brand.name}}</li>
</ul>
<h5 class="text-warning ml-3">Type</h5>
<ul class="list-group my-3">
<li class="list-group-item" style="cursor: pointer;"
*ngFor="let type of productType"
[class.active]="type.id === this.shopParams.typeId"
[value]="type.id"
(click)="onTypeSelected(type.id)"
>{{type.name}}</li>
</ul>
</ng-container>
</section>
<section class="col-9">
<div class="d-flex justify-content-between align-items-center pb-2">
@ -35,7 +37,7 @@
[pageSize]="this.shopParams.pageSize"
[pageNumber]="this.shopParams.pageNumber"
></app-paging-header>
<div class="row g-2 align-items-center">
<div class="row g-2 align-items-center" *ngIf="products">
<div class="col-sm">
<input (keyup.enter)="onSearch()" style="width:300px" #search placeholder="Search" type="text" class="form-control">
</div>

View File

@ -11,7 +11,7 @@ import { ShopService } from './shop.service';
styleUrls: ['./shop.component.scss']
})
export class ShopComponent implements OnInit {
@ViewChild('search', {static: true}) searchTerm: ElementRef;
@ViewChild('search', {static: false}) searchTerm: ElementRef;
products: IProduct[];
brands: IBrand[];
productType: IType[];

View File

@ -17,6 +17,11 @@ label.xng-breadcrumb-trail {
color: orange;
}
xng-breadcrumb-separator {
.xng-breadcrumb-separator {
padding: 0 0;
color: #343a40;
}
.xng-breadcrumb-link, a.xng-breadcrumb-link {
color: #343a40;
}