start work on better masonry layout
This commit is contained in:
parent
9a5859c30b
commit
9e5d6c6f89
|
@ -24,6 +24,8 @@
|
|||
"@angular/platform-browser": "^14.0.0-next.15",
|
||||
"@angular/platform-browser-dynamic": "^14.0.0-next.15",
|
||||
"@angular/router": "^14.0.0-next.15",
|
||||
"@ng-web-apis/common": "^2.0.0",
|
||||
"@ng-web-apis/resize-observer": "^1.0.3",
|
||||
"@ngui/common": "^1.0.0",
|
||||
"bootstrap": "^5.1.3",
|
||||
"fuse.js": "^6.5.3",
|
||||
|
@ -47,6 +49,7 @@
|
|||
"@fontsource/material-icons-outlined": "^4.5.4",
|
||||
"@fontsource/roboto": "^4.5.5",
|
||||
"@types/node": "^17.0.30",
|
||||
"@types/resize-observer-browser": "^0.1.7",
|
||||
"@types/validator": "^13.7.2",
|
||||
"typescript": "4.6.4"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
import { Directive, ElementRef, Inject } from '@angular/core';
|
||||
import {
|
||||
boxExtractor,
|
||||
ResizeObserverDirective,
|
||||
ResizeObserverService,
|
||||
RESIZE_OPTION_BOX,
|
||||
} from '@ng-web-apis/resize-observer';
|
||||
import { AutoUnsubscribe } from 'ngx-auto-unsubscribe-decorator';
|
||||
import { map, Observable } from 'rxjs';
|
||||
|
||||
@Directive({
|
||||
selector: '[masonry-item]',
|
||||
providers: [
|
||||
ResizeObserverService,
|
||||
{
|
||||
provide: RESIZE_OPTION_BOX,
|
||||
deps: [ElementRef],
|
||||
useFactory: boxExtractor,
|
||||
},
|
||||
],
|
||||
})
|
||||
export class MasonryItemDirective {
|
||||
private lastEntry: ResizeObserverEntry | null = null;
|
||||
|
||||
private resizeObserver: Observable<ResizeObserverEntry>;
|
||||
|
||||
constructor(
|
||||
private element: ElementRef<HTMLElement>,
|
||||
@Inject(ResizeObserverService)
|
||||
resize: Observable<ResizeObserverEntry[]>
|
||||
) {
|
||||
this.resizeObserver = resize.pipe(map((entries) => entries[0]));
|
||||
this.subscribeResize();
|
||||
}
|
||||
|
||||
@AutoUnsubscribe()
|
||||
private subscribeResize() {
|
||||
return this.resizeObserver.subscribe((value) => {
|
||||
this.lastEntry = value;
|
||||
});
|
||||
}
|
||||
|
||||
public getElement() {
|
||||
return this.element.nativeElement;
|
||||
}
|
||||
|
||||
public getSize() {
|
||||
return this.resizeObserver;
|
||||
}
|
||||
|
||||
public getLastSize() {
|
||||
return this.lastEntry;
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
<ng-content></ng-content>
|
34
frontend/src/app/components/masonry/masonry.component.ts
Normal file
34
frontend/src/app/components/masonry/masonry.component.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
import {
|
||||
AfterViewInit,
|
||||
Component,
|
||||
ContentChildren,
|
||||
ElementRef,
|
||||
OnChanges,
|
||||
OnInit,
|
||||
QueryList,
|
||||
SimpleChanges,
|
||||
ViewChild,
|
||||
} from '@angular/core';
|
||||
import { AutoUnsubscribe } from 'ngx-auto-unsubscribe-decorator';
|
||||
import { MasonryItemDirective } from './masonry-item.directive';
|
||||
|
||||
@Component({
|
||||
selector: 'masonry',
|
||||
templateUrl: './masonry.component.html',
|
||||
styleUrls: ['./masonry.component.scss'],
|
||||
})
|
||||
export class MasonryComponent implements AfterViewInit {
|
||||
@ContentChildren(MasonryItemDirective)
|
||||
content: QueryList<MasonryItemDirective>;
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
this.subscribeContent();
|
||||
}
|
||||
|
||||
@AutoUnsubscribe()
|
||||
private subscribeContent() {
|
||||
return this.content.changes.subscribe((items) => {
|
||||
console.log('a', items.toArray());
|
||||
});
|
||||
}
|
||||
}
|
11
frontend/src/app/components/masonry/masonry.module.ts
Normal file
11
frontend/src/app/components/masonry/masonry.module.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { MasonryComponent } from './masonry.component';
|
||||
import { MasonryItemDirective } from './masonry-item.directive';
|
||||
|
||||
@NgModule({
|
||||
declarations: [MasonryComponent, MasonryItemDirective],
|
||||
imports: [CommonModule],
|
||||
exports: [MasonryComponent, MasonryItemDirective],
|
||||
})
|
||||
export class MasonryModule {}
|
|
@ -1,4 +1,6 @@
|
|||
import {
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
ElementRef,
|
||||
Input,
|
||||
|
@ -25,6 +27,7 @@ enum PicsurImgState {
|
|||
selector: 'picsur-img',
|
||||
templateUrl: './picsur-img.component.html',
|
||||
styleUrls: ['./picsur-img.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class PicsurImgComponent implements OnChanges {
|
||||
private readonly logger = new Logger('ZodImgComponent');
|
||||
|
@ -39,7 +42,8 @@ export class PicsurImgComponent implements OnChanges {
|
|||
|
||||
constructor(
|
||||
private qoiWorker: QoiWorkerService,
|
||||
private apiService: ApiService
|
||||
private apiService: ApiService,
|
||||
private changeDetector: ChangeDetectorRef
|
||||
) {}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
|
@ -50,6 +54,7 @@ export class PicsurImgComponent implements OnChanges {
|
|||
let url = this.imageURL ?? '';
|
||||
if (!URLRegex.test(url)) {
|
||||
this.state = PicsurImgState.Loading;
|
||||
this.changeDetector.markForCheck();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -61,6 +66,8 @@ export class PicsurImgComponent implements OnChanges {
|
|||
}
|
||||
})
|
||||
.catch((e) => this.logger.error);
|
||||
|
||||
|
||||
}
|
||||
|
||||
private async update(url: string): AsyncFailable<void> {
|
||||
|
@ -80,6 +87,7 @@ export class PicsurImgComponent implements OnChanges {
|
|||
} else {
|
||||
this.state = PicsurImgState.Image;
|
||||
}
|
||||
this.changeDetector.markForCheck();
|
||||
}
|
||||
|
||||
private async getMime(url: string): AsyncFailable<FullMime> {
|
||||
|
|
|
@ -1,9 +1,33 @@
|
|||
<masonry>
|
||||
<div *ngFor="let image of sourceImages" class="m-2" masonry-item>
|
||||
<mat-card>
|
||||
<mat-card-header>
|
||||
<mat-card-title>Image by you</mat-card-title>
|
||||
<mat-card-subtitle>
|
||||
Uploaded {{ image.created | amTimeAgo }}
|
||||
</mat-card-subtitle>
|
||||
</mat-card-header>
|
||||
<picsur-img
|
||||
mat-card-image
|
||||
[src]="getThumbnailUrl(image)"
|
||||
alt="Image uploaded by you"
|
||||
>
|
||||
</picsur-img>
|
||||
<mat-card-actions>
|
||||
<button mat-stroked-button (click)="viewImage(image)">View</button>
|
||||
<button mat-button color="warn">Delete</button>
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
||||
</div>
|
||||
</masonry>
|
||||
|
||||
<div class="column-wrapper" *ngIf="images !== null">
|
||||
<div
|
||||
*ngFor="let dummy of [].constructor(columns); let i = index"
|
||||
*ngFor="let column of images"
|
||||
class="column"
|
||||
#column
|
||||
>
|
||||
<div *ngFor="let image of images | masonry : columns : i" class="m-2">
|
||||
<div *ngFor="let image of column" class="m-2" #imgdiv (waResizeObserver)="onResize($event, image, imgdiv)">
|
||||
<mat-card>
|
||||
<mat-card-header>
|
||||
<mat-card-title>Image by you</mat-card-title>
|
||||
|
@ -25,3 +49,30 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- <div class="paginator">
|
||||
<button class="mat-stroked-button" mat-icon-button>
|
||||
<mat-icon>first_page</mat-icon>
|
||||
</button>
|
||||
<button class="mat-stroked-button" mat-icon-button>
|
||||
<mat-icon>chevron_left</mat-icon>
|
||||
</button>
|
||||
<button class="mat-stroked-button" mat-icon-button>
|
||||
<span>1</span>
|
||||
</button>
|
||||
<button class="mat-stroked-button" mat-icon-button>
|
||||
<span>2</span>
|
||||
</button>
|
||||
<button class="mat-stroked-button" color="accent" mat-icon-button>
|
||||
<span>3</span>
|
||||
</button>
|
||||
<button class="mat-stroked-button" mat-icon-button>
|
||||
<span>4</span>
|
||||
</button>
|
||||
<button class="mat-stroked-button" mat-icon-button>
|
||||
<mat-icon>chevron_right</mat-icon>
|
||||
</button>
|
||||
<button class="mat-stroked-button" mat-icon-button>
|
||||
<mat-icon>last_page</mat-icon>
|
||||
</button>
|
||||
</div> -->
|
||||
|
|
|
@ -9,3 +9,13 @@
|
|||
flex-basis: 0;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.paginator {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
|
||||
button {
|
||||
margin-inline: .5em;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,11 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import {
|
||||
Component,
|
||||
ElementRef,
|
||||
OnInit,
|
||||
QueryList,
|
||||
ViewChildren,
|
||||
ViewRef,
|
||||
} from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { AutoUnsubscribe } from 'ngx-auto-unsubscribe-decorator';
|
||||
import { SupportedMime } from 'picsur-shared/dist/dto/mimes.dto';
|
||||
|
@ -19,8 +26,15 @@ import { UtilService } from 'src/app/util/util-module/util.service';
|
|||
export class ImagesComponent implements OnInit {
|
||||
private readonly logger: Logger = new Logger('ImagesComponent');
|
||||
|
||||
images: EImage[] | null = null;
|
||||
columns = 3;
|
||||
@ViewChildren('column')
|
||||
columnsChild: QueryList<ElementRef<HTMLDivElement>>;
|
||||
|
||||
sourceImages: EImage[] | null = null;
|
||||
private elementSizes: { [key: string]: number } = {};
|
||||
private elements: { [key: string]: HTMLElement } = {};
|
||||
private desiredColumns = 1;
|
||||
|
||||
images: EImage[][] | null = null;
|
||||
|
||||
constructor(
|
||||
private readonly route: ActivatedRoute,
|
||||
|
@ -42,22 +56,90 @@ export class ImagesComponent implements OnInit {
|
|||
return this.logger.error(result.getReason());
|
||||
}
|
||||
|
||||
this.images = result.images;
|
||||
this.sourceImages = result.images;
|
||||
this.sortMasonryRender();
|
||||
}
|
||||
|
||||
@AutoUnsubscribe()
|
||||
private subscribeMobile() {
|
||||
return this.bootstrapService.screenSize().subscribe((size) => {
|
||||
if (size <= BSScreenSize.sm) {
|
||||
this.columns = 1;
|
||||
this.desiredColumns = 1;
|
||||
} else if (size <= BSScreenSize.lg) {
|
||||
this.columns = 2;
|
||||
this.desiredColumns = 2;
|
||||
} else {
|
||||
this.columns = 3;
|
||||
this.desiredColumns = 3;
|
||||
}
|
||||
this.sortMasonryRender();
|
||||
});
|
||||
}
|
||||
|
||||
private sortMasonryRender() {
|
||||
if (!this.sourceImages) {
|
||||
this.images = null;
|
||||
return;
|
||||
}
|
||||
|
||||
this.elements = {};
|
||||
this.elementSizes = {};
|
||||
|
||||
const columnSizes: number[] = [];
|
||||
const columns: EImage[][] = [];
|
||||
for (let i = 0; i < this.desiredColumns; i++) {
|
||||
columnSizes.push(0);
|
||||
columns.push([]);
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.sourceImages.length; i++) {
|
||||
columns[i % this.desiredColumns].push(this.sourceImages[i]);
|
||||
}
|
||||
|
||||
this.images = columns;
|
||||
}
|
||||
|
||||
private sortMasonry() {
|
||||
if (!this.sourceImages) {
|
||||
this.images = null;
|
||||
return;
|
||||
}
|
||||
|
||||
const elementImages = this.sourceImages.map((img) => ({
|
||||
element: this.elements[img.id],
|
||||
height: this.elementSizes[img.id],
|
||||
}));
|
||||
if (
|
||||
elementImages.find(
|
||||
(test) => test.element === undefined || test.height === undefined
|
||||
) !== undefined
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let { element } of elementImages) {
|
||||
element.parentElement?.removeChild(element);
|
||||
}
|
||||
|
||||
const columnSizes: number[] = this.columnsChild.map((column) => 0);
|
||||
for (let i = 0; i < elementImages.length; i++) {
|
||||
const { element, height } = elementImages[i];
|
||||
|
||||
let minColumn = 0;
|
||||
let minColumnSize = columnSizes[0];
|
||||
for (let j = 0; j < columnSizes.length; j++) {
|
||||
const distributed_j = (j + i) % columnSizes.length;
|
||||
|
||||
const columnSize = columnSizes[distributed_j];
|
||||
if (columnSize <= minColumnSize) {
|
||||
minColumn = distributed_j;
|
||||
minColumnSize = columnSize;
|
||||
}
|
||||
}
|
||||
|
||||
this.columnsChild.toArray()[minColumn].nativeElement.appendChild(element);
|
||||
columnSizes[minColumn] += height;
|
||||
}
|
||||
}
|
||||
|
||||
getThumbnailUrl(image: EImage) {
|
||||
return (
|
||||
this.imageService.GetImageURL(image.id, SupportedMime.QOI) + '?height=480'
|
||||
|
@ -67,4 +149,17 @@ export class ImagesComponent implements OnInit {
|
|||
viewImage(image: EImage) {
|
||||
this.router.navigate(['/view', image.id]);
|
||||
}
|
||||
|
||||
onResize(
|
||||
[event]: ResizeObserverEntry[],
|
||||
image: EImage,
|
||||
element: HTMLElement
|
||||
) {
|
||||
this.elements[image.id] = element;
|
||||
|
||||
if (this.elementSizes[image.id] !== event.contentRect.height) {
|
||||
this.elementSizes[image.id] = event.contentRect.height;
|
||||
this.sortMasonry();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,10 @@ import { ImagesComponent } from './images.component';
|
|||
import { ImagesRoutingModule } from './images.routing.module';
|
||||
import { MasonryPipe } from './masonry.pipe';
|
||||
import { MomentModule } from 'ngx-moment';
|
||||
import { MatPaginatorModule } from '@angular/material/paginator';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { ResizeObserverModule } from '@ng-web-apis/resize-observer';
|
||||
import { MasonryModule } from 'src/app/components/masonry/masonry.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [ImagesComponent, MasonryPipe],
|
||||
|
@ -15,6 +19,10 @@ import { MomentModule } from 'ngx-moment';
|
|||
ImagesRoutingModule,
|
||||
MatCardModule,
|
||||
MatButtonModule,
|
||||
MatPaginatorModule,
|
||||
MatIconModule,
|
||||
ResizeObserverModule,
|
||||
MasonryModule,
|
||||
PicsurImgModule,
|
||||
MomentModule,
|
||||
],
|
||||
|
|
|
@ -74,7 +74,7 @@
|
|||
class="mat-elevation-z2"
|
||||
[pageSizeOptions]="pageSizeOptions"
|
||||
[pageSize]="startingPageSize"
|
||||
[showFirstLastButtons]="bootstrapService.isDesktop() | async"
|
||||
[showFirstLastButtons]="bootstrapService.isNotMobile() | async"
|
||||
[hidePageSize]="bootstrapService.isMobile() | async"
|
||||
aria-label="Select page of roles"
|
||||
>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { Inject, Injectable } from '@angular/core';
|
||||
import { ApiResponseSchema } from 'picsur-shared/dist/dto/api/api.dto';
|
||||
import { Mime2Ext } from 'picsur-shared/dist/dto/mimes.dto';
|
||||
import { AsyncFailable, Fail, HasFailed } from 'picsur-shared/dist/types';
|
||||
|
@ -10,6 +10,7 @@ import { z } from 'zod';
|
|||
import { MultiPartRequest } from '../../models/dto/multi-part-request.dto';
|
||||
import { Logger } from '../logger/logger.service';
|
||||
import { KeyService } from '../storage/key.service';
|
||||
import { WINDOW } from '@ng-web-apis/common';
|
||||
|
||||
/*
|
||||
Proud of this, it works so smoooth
|
||||
|
@ -27,7 +28,10 @@ export class ApiService {
|
|||
return this.errorSubject.asObservable();
|
||||
}
|
||||
|
||||
constructor(private keyService: KeyService) {}
|
||||
constructor(
|
||||
private readonly keyService: KeyService,
|
||||
@Inject(WINDOW) readonly windowRef: Window
|
||||
) {}
|
||||
|
||||
public async get<T extends z.AnyZodObject>(
|
||||
type: ZodDtoStatic<T>,
|
||||
|
@ -176,7 +180,7 @@ export class ApiService {
|
|||
if (isJSON) headers['Content-Type'] = 'application/json';
|
||||
options.headers = headers;
|
||||
|
||||
return await window.fetch(url, options);
|
||||
return await this.windowRef.fetch(url, options);
|
||||
} catch (e) {
|
||||
this.errorSubject.next({
|
||||
error: e,
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { Inject, Injectable } from '@angular/core';
|
||||
import { LOCATION, WINDOW } from '@ng-web-apis/common';
|
||||
import {
|
||||
ImageDeleteRequest,
|
||||
ImageDeleteResponse,
|
||||
ImageListRequest,
|
||||
ImageListResponse,
|
||||
ImageUploadResponse
|
||||
ImageUploadResponse,
|
||||
} from 'picsur-shared/dist/dto/api/image-manage.dto';
|
||||
import { ImageMetaResponse } from 'picsur-shared/dist/dto/api/image.dto';
|
||||
import { ImageLinks } from 'picsur-shared/dist/dto/image-links.dto';
|
||||
|
@ -19,7 +20,10 @@ import { ApiService } from './api.service';
|
|||
providedIn: 'root',
|
||||
})
|
||||
export class ImageService {
|
||||
constructor(private api: ApiService) {}
|
||||
constructor(
|
||||
private api: ApiService,
|
||||
@Inject(LOCATION) readonly location: Location,
|
||||
) {}
|
||||
|
||||
public async UploadImage(image: File): AsyncFailable<string> {
|
||||
const result = await this.api.postForm(
|
||||
|
@ -81,7 +85,7 @@ export class ImageService {
|
|||
// Non api calls
|
||||
|
||||
public GetImageURL(image: string, mime: string | null): string {
|
||||
const baseURL = window.location.protocol + '//' + window.location.host;
|
||||
const baseURL = this.location.protocol + '//' + this.location.host;
|
||||
const extension = mime !== null ? Mime2Ext(mime) ?? 'error' : null;
|
||||
|
||||
return `${baseURL}/i/${image}${extension !== null ? '.' + extension : ''}`;
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { Inject, Injectable } from '@angular/core';
|
||||
import { HISTORY } from '@ng-web-apis/common';
|
||||
import { InfoResponse } from 'picsur-shared/dist/dto/api/info.dto';
|
||||
import {
|
||||
AsyncFailable,
|
||||
Fail, HasFailed
|
||||
} from 'picsur-shared/dist/types';
|
||||
import { AsyncFailable, Fail, HasFailed } from 'picsur-shared/dist/types';
|
||||
import { SemVerRegex } from 'picsur-shared/dist/util/common-regex';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { SnackBarType } from 'src/app/models/dto/snack-bar-type.dto';
|
||||
|
@ -25,7 +23,11 @@ export class InfoService {
|
|||
|
||||
private infoSubject = new BehaviorSubject<ServerInfo>(new ServerInfo());
|
||||
|
||||
constructor(private api: ApiService, private utilService: UtilService) {
|
||||
constructor(
|
||||
private readonly api: ApiService,
|
||||
private readonly utilService: UtilService,
|
||||
@Inject(HISTORY) private readonly history: History
|
||||
) {
|
||||
this.checkCompatibility().catch(this.logger.error);
|
||||
}
|
||||
|
||||
|
@ -104,7 +106,7 @@ export class InfoService {
|
|||
} else {
|
||||
this.checkCompatibility();
|
||||
// Go to previous page
|
||||
window.history.back();
|
||||
this.history.back();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { Inject, Injectable } from '@angular/core';
|
||||
import { SESSION_STORAGE } from '@ng-web-apis/common';
|
||||
import { AsyncFailable, Failable, HasFailed } from 'picsur-shared/dist/types';
|
||||
|
||||
interface dataWrapper<T> {
|
||||
|
@ -12,15 +13,7 @@ interface dataWrapper<T> {
|
|||
export class CacheService {
|
||||
private readonly cacheExpiresMS = 1000 * 60 * 60;
|
||||
|
||||
private storage: Storage;
|
||||
|
||||
constructor() {
|
||||
if (window.sessionStorage) {
|
||||
this.storage = window.sessionStorage;
|
||||
} else {
|
||||
throw new Error('Session storage is not supported');
|
||||
}
|
||||
}
|
||||
constructor(@Inject(SESSION_STORAGE) private readonly storage: Storage) {}
|
||||
|
||||
public set<T>(key: string, value: T): void {
|
||||
const data: dataWrapper<T> = {
|
||||
|
|
|
@ -48,6 +48,10 @@ router-outlet ~ * {
|
|||
width: initial !important;
|
||||
}
|
||||
|
||||
button.mat-icon-button {
|
||||
line-height: unset;
|
||||
}
|
||||
|
||||
.row {
|
||||
width: 100%;
|
||||
}
|
||||
|
|
|
@ -72,6 +72,7 @@
|
|||
},
|
||||
"settings": {
|
||||
"vsicons.presets.angular": true,
|
||||
"skipRefreshExplorerOnWindowFocus": true
|
||||
"skipRefreshExplorerOnWindowFocus": true,
|
||||
"angular.log": "verbose"
|
||||
}
|
||||
}
|
||||
|
|
52
yarn.lock
52
yarn.lock
|
@ -1373,7 +1373,8 @@
|
|||
secure-json-parse "^2.4.0"
|
||||
stream-wormhole "^1.1.0"
|
||||
|
||||
"@fastify/static@^5.0.0", fastify-static@^4.7.0, "fastify-static@npm:@fastify/static":
|
||||
"@fastify/static@^5.0.0", "fastify-static@npm:@fastify/static":
|
||||
name fastify-static
|
||||
version "5.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@fastify/static/-/static-5.0.2.tgz#46cee887393b422f4b10a46a14e970a64dd086d4"
|
||||
integrity sha512-HvyXZ5a7hUHoSBRq9jKUuKIUCkHMkCDcmiAeEmixXlGOx8pEWx3NYOIaiivcjWa6/NLvfdUT+t/jzfVQ2PA7Gw==
|
||||
|
@ -1607,6 +1608,20 @@
|
|||
dependencies:
|
||||
uuid "8.3.2"
|
||||
|
||||
"@ng-web-apis/common@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@ng-web-apis/common/-/common-2.0.0.tgz#6a90f2eb575a595e6fee36f7f57ec38ca901aa17"
|
||||
integrity sha512-2Vnp4WTEqKZArhbKLgD1JIKjsDa3hWCa67OWaRWRH5sgX5xneVVaIAvC8gVpiCfl2p1Roen2kxfyYngx7G64SQ==
|
||||
dependencies:
|
||||
tslib "^2.2.0"
|
||||
|
||||
"@ng-web-apis/resize-observer@^1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@ng-web-apis/resize-observer/-/resize-observer-1.0.3.tgz#7f594f588d6706bfdeab514dec6249b5afc44534"
|
||||
integrity sha512-ddmhxlca4knmN7BicgPTBScYXNTEKKF3z2WXPgmhOTxhVOyg/HHRDtq5nDljJt1eEaut2gcnhgDm4/6eGfedWw==
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
|
||||
"@ngtools/webpack@14.0.0-next.12":
|
||||
version "14.0.0-next.12"
|
||||
resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-14.0.0-next.12.tgz#6920a8a54abd2a5bc41f671ed4e18d8b3be0502b"
|
||||
|
@ -1942,6 +1957,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc"
|
||||
integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==
|
||||
|
||||
"@types/resize-observer-browser@^0.1.7":
|
||||
version "0.1.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/resize-observer-browser/-/resize-observer-browser-0.1.7.tgz#294aaadf24ac6580b8fbd1fe3ab7b59fe85f9ef3"
|
||||
integrity sha512-G9eN0Sn0ii9PWQ3Vl72jDPgeJwRWhv2Qk/nQkJuWmRmOB4HX3/BhD5SE1dZs/hzPZL/WKnvF0RHdTSG54QJFyg==
|
||||
|
||||
"@types/retry@0.12.0":
|
||||
version "0.12.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d"
|
||||
|
@ -4006,6 +4026,27 @@ fastify-plugin@^3.0.0:
|
|||
resolved "https://registry.yarnpkg.com/fastify-plugin/-/fastify-plugin-3.0.1.tgz#79e84c29f401020f38b524f59f2402103fd21ed2"
|
||||
integrity sha512-qKcDXmuZadJqdTm6vlCqioEbyewF60b/0LOFCcYN1B6BIZGlYJumWWOYs70SFYLDAH4YqdE1cxH/RKMG7rFxgA==
|
||||
|
||||
"fastify-static-deprecated@npm:fastify-static@4.6.1":
|
||||
version "4.6.1"
|
||||
resolved "https://registry.yarnpkg.com/fastify-static/-/fastify-static-4.6.1.tgz#687131da76f1d4391fb8b47f71ea2118cdc85803"
|
||||
integrity sha512-vy7N28U4AMhuOim12ZZWHulEE6OQKtzZbHgiB8Zj4llUuUQXPka0WHAQI3njm1jTCx4W6fixUHfpITxweMtAIA==
|
||||
dependencies:
|
||||
content-disposition "^0.5.3"
|
||||
encoding-negotiator "^2.0.1"
|
||||
fastify-plugin "^3.0.0"
|
||||
glob "^7.1.4"
|
||||
p-limit "^3.1.0"
|
||||
readable-stream "^3.4.0"
|
||||
send "^0.17.1"
|
||||
|
||||
fastify-static@^4.7.0:
|
||||
version "4.7.0"
|
||||
resolved "https://registry.yarnpkg.com/fastify-static/-/fastify-static-4.7.0.tgz#e802658d69c1dcddb380b9afc2456d467a3494be"
|
||||
integrity sha512-zZhCfJv/hkmud2qhWqpU3K9XVAuy3+IV8Tp9BC5J5U+GyA2XwoB6h8lh9GqpEIqdXOw01WyWQllV7dOWVyAlXg==
|
||||
dependencies:
|
||||
fastify-static-deprecated "npm:fastify-static@4.6.1"
|
||||
process-warning "^1.0.0"
|
||||
|
||||
fastify@3.28.0:
|
||||
version "3.28.0"
|
||||
resolved "https://registry.yarnpkg.com/fastify/-/fastify-3.28.0.tgz#14d939a2f176b82af1094de7abcb0b2d83bcff8f"
|
||||
|
@ -5404,7 +5445,12 @@ minimatch@^3.0.4, minimatch@^3.1.2:
|
|||
dependencies:
|
||||
brace-expansion "^1.1.7"
|
||||
|
||||
minimist@1.2.6, minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.6, "minimist@npm:minimist-lite":
|
||||
minimist@1.2.6, minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.6:
|
||||
version "1.2.6"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
|
||||
integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
|
||||
|
||||
"minimist@npm:minimist-lite":
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/minimist-lite/-/minimist-lite-2.2.1.tgz#abb71db2c9b454d7cf4496868c03e9802de9934d"
|
||||
integrity sha512-RSrWIRWGYoM2TDe102s7aIyeSipXMIXKb1fSHYx1tAbxAV0z4g2xR6ra3oPzkTqFb0EIUz1H3A/qvYYeDd+/qQ==
|
||||
|
@ -7866,7 +7912,7 @@ tslib@^1.8.1, tslib@^1.9.0:
|
|||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
|
||||
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
|
||||
|
||||
tslib@^2.0.0, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.4.0:
|
||||
tslib@^2.0.0, tslib@^2.1.0, tslib@^2.2.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.4.0:
|
||||
version "2.4.0"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
|
||||
integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==
|
||||
|
|
Loading…
Reference in a new issue