mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
[HIMA-67] - Replaced infinite scroll with virtual scroll implementation
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "~7.2.0",
|
||||
"@angular/cdk": "7.0.0",
|
||||
"@angular/common": "~7.2.0",
|
||||
"@angular/compiler": "~7.2.0",
|
||||
"@angular/core": "~7.2.0",
|
||||
|
||||
@@ -34,7 +34,16 @@ import { FilterState } from './core/store/state/filter.state';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { SharedModule } from './shared/shared.module';
|
||||
import { AutocompleteState } from './core/store/state/autocomplete.state';
|
||||
const states = [FeedState, ProcessState, BreadcrumbsState, FilterState, AutocompleteState];
|
||||
|
||||
import { ScrollingModule } from '@angular/cdk/scrolling';
|
||||
|
||||
const states = [
|
||||
FeedState,
|
||||
ProcessState,
|
||||
BreadcrumbsState,
|
||||
FilterState,
|
||||
AutocompleteState
|
||||
];
|
||||
|
||||
export function _configInitializer(conf: ConfigService) {
|
||||
// load config from /assets/config.json
|
||||
@@ -75,7 +84,8 @@ export function _feedServiceEndpointProviderFactory(conf: ConfigService) {
|
||||
FeedServiceModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
SharedModule
|
||||
SharedModule,
|
||||
ScrollingModule
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
@@ -104,7 +114,7 @@ export function _feedServiceEndpointProviderFactory(conf: ConfigService) {
|
||||
provide: FEED_SERVICE_ENDPOINT,
|
||||
useFactory: _feedServiceEndpointProviderFactory,
|
||||
deps: [ConfigService]
|
||||
},
|
||||
}
|
||||
// { provide: CatSearchService, useClass: CatSearchMockService }, // Uncomment if u want to use the CatSearchMockService
|
||||
// { provide: FeedService, useClass: FeedMockService } // Uncomment if u want to use the FeedMockService
|
||||
],
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
@import '../../../assets/scss/variables';
|
||||
|
||||
.card-container {
|
||||
display: grid;
|
||||
grid-template-columns: min-content auto;
|
||||
grid-gap: 7vh;
|
||||
background-color: white;
|
||||
padding: 24px;
|
||||
margin-top: 10px;
|
||||
border-radius: 5px;
|
||||
padding: 24px;
|
||||
min-height: 134px;
|
||||
}
|
||||
|
||||
.recommanded {
|
||||
|
||||
@@ -1,130 +1,130 @@
|
||||
@import "../../../assets/scss/variables";
|
||||
@import '../../../assets/scss/variables';
|
||||
|
||||
.card-container {
|
||||
display: grid;
|
||||
grid-template-columns: min-content auto;
|
||||
grid-gap: 7vh;
|
||||
background-color: white;
|
||||
padding: 24px;
|
||||
margin-top: 10px;
|
||||
border-radius: 5px;
|
||||
display: grid;
|
||||
grid-template-columns: min-content auto;
|
||||
grid-gap: 7vh;
|
||||
background-color: white;
|
||||
border-radius: 5px;
|
||||
padding: 24px;
|
||||
min-height: 134px;
|
||||
}
|
||||
|
||||
.recommanded {
|
||||
background-color: $important-notification;
|
||||
color: white;
|
||||
background-color: $important-notification;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.product-icon {
|
||||
width: 69px;
|
||||
width: 69px;
|
||||
}
|
||||
|
||||
.content-container {
|
||||
display: grid;
|
||||
grid-template-columns: auto;
|
||||
display: grid;
|
||||
grid-template-columns: auto;
|
||||
}
|
||||
|
||||
.author span {
|
||||
font-size: 16px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.title-price {
|
||||
display: grid;
|
||||
grid-template-columns: auto max-content;
|
||||
grid-gap: 8vh;
|
||||
margin-top: 10px;
|
||||
display: grid;
|
||||
grid-template-columns: auto max-content;
|
||||
grid-gap: 8vh;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.title span {
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.price span {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.title-grid {
|
||||
display: grid;
|
||||
grid-template-columns: min-content auto;
|
||||
grid-gap: 2vh;
|
||||
display: grid;
|
||||
grid-template-columns: min-content auto;
|
||||
grid-gap: 2vh;
|
||||
}
|
||||
|
||||
.rec-icon-container {
|
||||
padding-top: 4px;
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
.currency {
|
||||
margin-left: 5px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.type-stock {
|
||||
display: grid;
|
||||
grid-template-columns: auto max-content;
|
||||
margin-top: 10px;
|
||||
display: grid;
|
||||
grid-template-columns: auto max-content;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.type {
|
||||
display: grid;
|
||||
grid-template-columns: min-content auto;
|
||||
grid-gap: 2vh;
|
||||
display: grid;
|
||||
grid-template-columns: min-content auto;
|
||||
grid-gap: 2vh;
|
||||
}
|
||||
|
||||
.type-text {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.available-stock {
|
||||
display: grid;
|
||||
grid-template-columns: auto auto auto auto;
|
||||
grid-gap: 2vh;
|
||||
display: grid;
|
||||
grid-template-columns: auto auto auto auto;
|
||||
grid-gap: 2vh;
|
||||
}
|
||||
|
||||
.available-icon-container {
|
||||
padding-top: 2px;
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
.stock-icon {
|
||||
width: 15px;
|
||||
width: 15px;
|
||||
}
|
||||
|
||||
.available-text span {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.stock span {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.publisher-order {
|
||||
display: grid;
|
||||
grid-template-columns: auto max-content;
|
||||
margin-top: 10px;
|
||||
display: grid;
|
||||
grid-template-columns: auto max-content;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.publisher-serial {
|
||||
display: grid;
|
||||
grid-template-columns: max-content min-content auto;
|
||||
grid-gap: 2vh;
|
||||
display: grid;
|
||||
grid-template-columns: max-content min-content auto;
|
||||
grid-gap: 2vh;
|
||||
}
|
||||
|
||||
.publisher-serial span {
|
||||
font-size: 16px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.order span {
|
||||
font-size: 16px;
|
||||
color: #a7b9cb;
|
||||
font-size: 16px;
|
||||
color: #a7b9cb;
|
||||
}
|
||||
|
||||
.type-icon-container {
|
||||
padding-top: 3px;
|
||||
padding-top: 3px;
|
||||
}
|
||||
|
||||
.publisher {
|
||||
max-width: 300px;
|
||||
}
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
@@ -1,27 +1,22 @@
|
||||
<div class="result-container">
|
||||
<app-filter></app-filter>
|
||||
<div
|
||||
id="start"
|
||||
infiniteScroll
|
||||
[infiniteScrollDistance]="1"
|
||||
[infiniteScrollThrottle]="50"
|
||||
(scrolled)="onScroll()"
|
||||
[scrollWindow]="true"
|
||||
>
|
||||
<app-product-card *ngFor="let product of products" [product]="product">
|
||||
</app-product-card>
|
||||
</div>
|
||||
<div *ngIf="(process$ | async)?.loading">
|
||||
<div [@stagger]="'yes'">
|
||||
<div *ngFor="let dummy of dummies">
|
||||
<app-product-card-loading> </app-product-card-loading>
|
||||
</div>
|
||||
<cdk-virtual-scroll-viewport itemSize="180" class="viewport">
|
||||
<div *cdkVirtualFor="let product of ds" class="product-item">
|
||||
<app-product-card
|
||||
[product]="product"
|
||||
*ngIf="product != null; else loadingComponent"
|
||||
>
|
||||
</app-product-card>
|
||||
<ng-template #loadingComponent>
|
||||
<app-product-card-loading></app-product-card-loading>
|
||||
</ng-template>
|
||||
</div>
|
||||
<app-loading
|
||||
[style.marginTop.px]="60"
|
||||
[style.marginBottom.px]="60"
|
||||
loading="true"
|
||||
text="Inhalte werden geladen"
|
||||
></app-loading>
|
||||
</div>
|
||||
</cdk-virtual-scroll-viewport>
|
||||
<app-loading
|
||||
*ngIf="(process$ | async)?.loading"
|
||||
[style.marginTop.px]="60"
|
||||
[style.marginBottom.px]="60"
|
||||
loading="true"
|
||||
text="Inhalte werden geladen"
|
||||
></app-loading>
|
||||
</div>
|
||||
|
||||
@@ -3,3 +3,20 @@ app-product-card-loading {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
app-filter {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.result-container {
|
||||
height: calc(100% - 100px);
|
||||
}
|
||||
.viewport {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.product-item {
|
||||
height: 180px;
|
||||
overflow: hidden;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { ProductService } from './../../core/services/product.service';
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Search } from 'src/app/core/models/search.model';
|
||||
import { Process } from 'src/app/core/models/process.model';
|
||||
import { Product } from 'src/app/core/models/product.model';
|
||||
import { Router } from '@angular/router';
|
||||
import { map, filter } from 'rxjs/operators';
|
||||
import { map, filter, take } from 'rxjs/operators';
|
||||
import { Select, Store } from '@ngxs/store';
|
||||
import { ItemDTO } from 'dist/cat-service/lib/dtos';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Observable, BehaviorSubject, Subscription } from 'rxjs';
|
||||
import { GetProducts } from 'src/app/core/store/actions/product.actions';
|
||||
import { ProcessState } from 'src/app/core/store/state/process.state';
|
||||
import {
|
||||
@@ -15,6 +16,7 @@ import {
|
||||
} from 'src/app/core/store/actions/process.actions';
|
||||
import { ProductMapping } from 'src/app/core/mappings/product.mapping';
|
||||
import { staggerAnimation } from './stagger.animation';
|
||||
import { DataSource, CollectionViewer } from '@angular/cdk/collections';
|
||||
|
||||
@Component({
|
||||
selector: 'app-search-results',
|
||||
@@ -26,16 +28,17 @@ export class SearchResultsComponent implements OnInit {
|
||||
@Select(ProcessState.getCurrentProcess) process$;
|
||||
currentSearch: Search;
|
||||
products: Product[];
|
||||
loading = true;
|
||||
@Select(ProcessState.getProducts) products$: Observable<ItemDTO[]>;
|
||||
skip = 0;
|
||||
|
||||
dummies = [1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
ds: SearchDataSource;
|
||||
|
||||
constructor(
|
||||
private store: Store,
|
||||
private router: Router,
|
||||
private productMapping: ProductMapping
|
||||
private productMapping: ProductMapping,
|
||||
private productService: ProductService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
@@ -44,8 +47,12 @@ export class SearchResultsComponent implements OnInit {
|
||||
this.router.navigate(['dashboard']);
|
||||
return;
|
||||
}
|
||||
this.store.dispatch(new GetProducts(this.currentSearch));
|
||||
this.loadProducts();
|
||||
this.ds = new SearchDataSource(
|
||||
this.productService,
|
||||
this.currentSearch.query
|
||||
);
|
||||
// this.store.dispatch(new GetProducts(this.currentSearch));
|
||||
// this.loadProducts();
|
||||
}
|
||||
|
||||
loadCurrentSearch() {
|
||||
@@ -73,6 +80,73 @@ export class SearchResultsComponent implements OnInit {
|
||||
filter(f => Array.isArray(f)),
|
||||
map(items => items.map(item => this.productMapping.fromItemDTO(item)))
|
||||
)
|
||||
.subscribe(data => (this.products = data), () => (this.loading = false));
|
||||
.subscribe(data => (this.products = data));
|
||||
}
|
||||
}
|
||||
|
||||
export class SearchDataSource extends DataSource<Product | undefined> {
|
||||
private pageSize = 10;
|
||||
private cachedData = Array.from<Product>({ length: 0 });
|
||||
private fetchedPages = new Set<number>();
|
||||
private dataStream = new BehaviorSubject<(Product | undefined)[]>(
|
||||
this.cachedData
|
||||
);
|
||||
private subscription = new Subscription();
|
||||
|
||||
private productMapping = new ProductMapping();
|
||||
|
||||
constructor(private searchService: ProductService, private search: string) {
|
||||
super();
|
||||
}
|
||||
connect(
|
||||
collectionViewer: CollectionViewer
|
||||
): Observable<(Product | undefined)[]> {
|
||||
this.subscription.add(
|
||||
collectionViewer.viewChange.subscribe(range => {
|
||||
const startPage = this.getPageForIndex(range.start);
|
||||
const endPage = this.getPageForIndex(range.end - 1);
|
||||
for (let i = startPage; i <= endPage; i++) {
|
||||
this.fetchPage(i);
|
||||
}
|
||||
})
|
||||
);
|
||||
this.fetchPage(0);
|
||||
|
||||
return this.dataStream;
|
||||
}
|
||||
|
||||
disconnect(): void {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
|
||||
private getPageForIndex(index: number): number {
|
||||
return Math.floor(index / this.pageSize);
|
||||
}
|
||||
|
||||
private fetchPage(page: number) {
|
||||
if (this.fetchedPages.has(page)) {
|
||||
return;
|
||||
}
|
||||
this.fetchedPages.add(page);
|
||||
|
||||
this.searchService
|
||||
.searchItemsWithPagination(
|
||||
this.search,
|
||||
page * this.pageSize,
|
||||
this.pageSize,
|
||||
[]
|
||||
)
|
||||
.pipe(take(1))
|
||||
.subscribe(data => {
|
||||
if (page === 0) {
|
||||
this.cachedData = Array.from<Product>({ length: data.hits });
|
||||
}
|
||||
this.cachedData.splice(
|
||||
page * this.pageSize,
|
||||
this.pageSize,
|
||||
...data.result.map(item => this.productMapping.fromItemDTO(item))
|
||||
);
|
||||
this.dataStream.next(this.cachedData);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,47 +14,54 @@ import { map } from 'rxjs/operators';
|
||||
import { Search } from '../models/search.model';
|
||||
import { FilterMapping } from '../mappings/filter.mapping';
|
||||
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ProductService {
|
||||
|
||||
searchResponse$: Observable<PagedApiResponse<ItemDTO>>;
|
||||
|
||||
constructor(
|
||||
private searchService: CatSearchService,
|
||||
private filterMapping: FilterMapping
|
||||
) { }
|
||||
) {}
|
||||
|
||||
persistLastSearchToLocalStorage(param: string) {
|
||||
// get recent searches from local storage
|
||||
const recentSearches: RecentArticleSearch[] = JSON.parse(localStorage.getItem('recent_searches'));
|
||||
const recentSearches: RecentArticleSearch[] = JSON.parse(
|
||||
localStorage.getItem('recent_searches')
|
||||
);
|
||||
/*
|
||||
* check if there are search items in local storage, if there are not add search to local storage
|
||||
* else check if current search already exists in loacl storage, if exist delete it (deletion is made becouse
|
||||
* we want every search to display in LIFO order).
|
||||
* finaly push the new search at the end of the local storage array
|
||||
*/
|
||||
* check if there are search items in local storage, if there are not add search to local storage
|
||||
* else check if current search already exists in loacl storage, if exist delete it (deletion is made becouse
|
||||
* we want every search to display in LIFO order).
|
||||
* finaly push the new search at the end of the local storage array
|
||||
*/
|
||||
if (recentSearches) {
|
||||
const searches = [...recentSearches.filter((data) => {
|
||||
return data.name !== param;
|
||||
}), <RecentArticleSearch>{
|
||||
id: recentSearches[recentSearches.length - 1].id + 1,
|
||||
name: param
|
||||
}];
|
||||
const searches = [
|
||||
...recentSearches.filter(data => {
|
||||
return data.name !== param;
|
||||
}),
|
||||
<RecentArticleSearch>{
|
||||
id: recentSearches[recentSearches.length - 1].id + 1,
|
||||
name: param
|
||||
}
|
||||
];
|
||||
localStorage.setItem('recent_searches', JSON.stringify(searches));
|
||||
} else {
|
||||
const searches = [<RecentArticleSearch>{
|
||||
id: 1,
|
||||
name: param
|
||||
}];
|
||||
const searches = [
|
||||
<RecentArticleSearch>{
|
||||
id: 1,
|
||||
name: param
|
||||
}
|
||||
];
|
||||
localStorage.setItem('recent_searches', JSON.stringify(searches));
|
||||
}
|
||||
}
|
||||
|
||||
getRecentSearches(): Observable<RecentArticleSearch[]> {
|
||||
const recentSearches: RecentArticleSearch[] = JSON.parse(localStorage.getItem('recent_searches'));
|
||||
const recentSearches: RecentArticleSearch[] = JSON.parse(
|
||||
localStorage.getItem('recent_searches')
|
||||
);
|
||||
return of(recentSearches);
|
||||
}
|
||||
// placeholder service method for calling product search API
|
||||
@@ -66,13 +73,16 @@ export class ProductService {
|
||||
// service method for calling product search API
|
||||
searchItems(search: Search): Observable<ItemDTO[]> {
|
||||
this.persistLastSearchToLocalStorage(search.query);
|
||||
const queryToken = <QueryTokenDTO> {
|
||||
input: {qs: search.query},
|
||||
const queryToken = <QueryTokenDTO>{
|
||||
input: { qs: search.query },
|
||||
skip: search.skip,
|
||||
take: search.take
|
||||
};
|
||||
|
||||
const queryWithFilters = this.filterMapping.toQueryTokenDto(queryToken, search.fitlers);
|
||||
const queryWithFilters = this.filterMapping.toQueryTokenDto(
|
||||
queryToken,
|
||||
search.fitlers
|
||||
);
|
||||
return this.searchService.search(queryWithFilters).pipe(
|
||||
map(response => {
|
||||
if (response.error) {
|
||||
@@ -84,6 +94,31 @@ export class ProductService {
|
||||
);
|
||||
}
|
||||
|
||||
searchItemsWithPagination(
|
||||
query,
|
||||
skip,
|
||||
size,
|
||||
filters
|
||||
): Observable<PagedApiResponse<ItemDTO>> {
|
||||
const queryToken = <QueryTokenDTO>{
|
||||
input: { qs: query },
|
||||
skip: skip,
|
||||
take: size
|
||||
};
|
||||
const queryWithFilters = this.filterMapping.toQueryTokenDto(
|
||||
queryToken,
|
||||
filters
|
||||
);
|
||||
return this.searchService.search(queryWithFilters).pipe(
|
||||
map(response => {
|
||||
if (response.error) {
|
||||
throw new Error(response.message);
|
||||
}
|
||||
return response;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
getItemById(id: number): Observable<ItemDTO> {
|
||||
return this.searchService.getById(id).pipe(
|
||||
map(response => {
|
||||
|
||||
@@ -25,6 +25,7 @@ import { CheckoutComponent } from '../components/checkout/checkout.component';
|
||||
import { SharedModule } from '../shared/shared.module';
|
||||
import { ProcessModule } from './process/process.module';
|
||||
import { ProductCardLoadingComponent } from '../components/product-card-loading/product-card-loading.component';
|
||||
import { ScrollingModule } from '@angular/cdk/scrolling';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@@ -43,6 +44,7 @@ import { ProductCardLoadingComponent } from '../components/product-card-loading/
|
||||
imports: [
|
||||
CommonModule,
|
||||
AppRoutingModule,
|
||||
ScrollingModule,
|
||||
FormsModule,
|
||||
SharedModule,
|
||||
NewsletterSignupModule,
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
@import "../../../assets/scss/variables";
|
||||
@import '../../../assets/scss/variables';
|
||||
|
||||
.content-body {
|
||||
height:100%;
|
||||
background-color: $hima-content-color;
|
||||
padding-top: 150px;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
background-color: $hima-content-color;
|
||||
padding-top: 150px;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
position: absolute;
|
||||
width: calc(100% - 30px);
|
||||
height: calc(100% - 230px);
|
||||
top: 0;
|
||||
}
|
||||
|
||||
14
yarn.lock
14
yarn.lock
@@ -128,6 +128,14 @@
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
|
||||
"@angular/cdk@7.0.0":
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@angular/cdk/-/cdk-7.0.0.tgz#b98d7378e84fed1af30c460aa91af4ada2cf252b"
|
||||
dependencies:
|
||||
tslib "^1.7.1"
|
||||
optionalDependencies:
|
||||
parse5 "^5.0.0"
|
||||
|
||||
"@angular/cli@~7.2.1":
|
||||
version "7.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-7.2.2.tgz#408dc7cd69931301c108ee2637836f0e9e7e3f02"
|
||||
@@ -4958,6 +4966,10 @@ parse5@4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608"
|
||||
|
||||
parse5@^5.0.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.0.tgz#c59341c9723f414c452975564c7c00a68d58acd2"
|
||||
|
||||
parseqs@0.0.5:
|
||||
version "0.0.5"
|
||||
resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d"
|
||||
@@ -6658,7 +6670,7 @@ tsickle@>=0.34.0:
|
||||
mkdirp "^0.5.1"
|
||||
source-map "^0.7.3"
|
||||
|
||||
tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3:
|
||||
tslib@^1.7.1, tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3:
|
||||
version "1.9.3"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user