Merge remote-tracking branch 'upstream/master' into feature/edge-alarm-comment-support

This commit is contained in:
Andrii Landiak 2024-01-10 10:47:25 +02:00
commit 92c23aa0cb
9 changed files with 191 additions and 89 deletions

View File

@ -66,12 +66,11 @@
</button>
</div>
<div *ngIf="linkType === ImageLinkType.none && !disabled" class="tb-image-select-buttons-container">
<button #browseGalleryButton
mat-stroked-button
<button mat-stroked-button
type="button"
color="primary"
class="tb-image-select-button"
(click)="toggleGallery($event, browseGalleryButton)">
(click)="openGallery($event)">
<tb-icon matButtonIcon>filter</tb-icon>
<span translate>image.browse-from-gallery</span>
</button>

View File

@ -14,16 +14,7 @@
/// limitations under the License.
///
import {
ChangeDetectorRef,
Component,
forwardRef,
Input,
OnDestroy,
OnInit,
Renderer2,
ViewContainerRef
} from '@angular/core';
import { ChangeDetectorRef, Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core';
import { PageComponent } from '@shared/components/page.component';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
@ -33,12 +24,13 @@ import {
extractParamsFromImageResourceUrl,
ImageResourceInfo,
isBase64DataImageUrl,
isImageResourceUrl, prependTbImagePrefix, removeTbImagePrefix
isImageResourceUrl,
prependTbImagePrefix,
removeTbImagePrefix
} from '@shared/models/resource.models';
import { ImageService } from '@core/http/image.service';
import { MatButton } from '@angular/material/button';
import { TbPopoverService } from '@shared/components/popover.service';
import { ImageGalleryComponent } from '@shared/components/image/image-gallery.component';
import { MatDialog } from '@angular/material/dialog';
import { ImageGalleryDialogComponent } from '@shared/components/image/image-gallery-dialog.component';
export enum ImageLinkType {
none = 'none',
@ -87,10 +79,8 @@ export class GalleryImageInputComponent extends PageComponent implements OnInit,
constructor(protected store: Store<AppState>,
private imageService: ImageService,
private cd: ChangeDetectorRef,
private renderer: Renderer2,
private viewContainerRef: ViewContainerRef,
private popoverService: TbPopoverService) {
private dialog: MatDialog,
private cd: ChangeDetectorRef) {
super(store);
}
@ -194,32 +184,22 @@ export class GalleryImageInputComponent extends PageComponent implements OnInit,
this.linkType = ImageLinkType.external;
}
toggleGallery($event: Event, browseGalleryButton: MatButton) {
openGallery($event: Event): void {
if ($event) {
$event.stopPropagation();
}
const trigger = browseGalleryButton._elementRef.nativeElement;
if (this.popoverService.hasPopover(trigger)) {
this.popoverService.hidePopover(trigger);
} else {
const ctx: any = {
pageMode: false,
popoverMode: true,
mode: 'grid',
selectionMode: true
};
const imageGalleryPopover = this.popoverService.displayPopover(trigger, this.renderer,
this.viewContainerRef, ImageGalleryComponent, 'top', true, null,
ctx,
{},
{}, {}, true);
imageGalleryPopover.tbComponentRef.instance.imageSelected.subscribe((image) => {
imageGalleryPopover.hide();
this.dialog.open<ImageGalleryDialogComponent, any,
ImageResourceInfo>(ImageGalleryDialogComponent, {
autoFocus: false,
disableClose: false,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog']
}).afterClosed().subscribe((image) => {
if (image) {
this.linkType = ImageLinkType.resource;
this.imageResource = image;
this.updateModel(image.link);
});
}
}
});
}
}

View File

@ -0,0 +1,32 @@
<!--
Copyright © 2016-2024 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<div class="tb-image-gallery-dialog">
<button class="tb-image-gallery-close tb-mat-20"
mat-icon-button
(click)="cancel()"
type="button">
<mat-icon>close</mat-icon>
</button>
<tb-image-gallery
pageMode="false"
dialogMode
mode="grid"
selectionMode
(imageSelected)="imageSelected($event)">
</tb-image-gallery>
</div>

View File

@ -0,0 +1,47 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@import '../../../../scss/constants';
:host {
.tb-image-gallery-dialog {
position: relative;
padding: 24px 32px 16px 32px;
width: 100vw;
height: 100vh;
max-height: 100vh;
@media #{$mat-gt-xs} {
width: 80vw;
height: 80vh;
max-height: 80vh;
}
@media #{$mat-gt-sm} {
width: 700px;
}
@media #{$mat-gt-md} {
width: 900px;
}
@media #{$mat-gt-xl} {
width: 900px;
}
.tb-image-gallery-close {
position: absolute;
top: 12px;
right: 12px;
z-index: 1;
color: rgba(0, 0, 0, 0.38);
}
}
}

View File

@ -0,0 +1,62 @@
///
/// Copyright © 2016-2024 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Component, Inject, OnInit, SkipSelf } from '@angular/core';
import { ErrorStateMatcher } from '@angular/material/core';
import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
import { DialogComponent } from '@shared/components/dialog.component';
import { Router } from '@angular/router';
import { ImageService } from '@core/http/image.service';
import { ImageResourceInfo, imageResourceType } from '@shared/models/resource.models';
import {
UploadImageDialogComponent,
UploadImageDialogData
} from '@shared/components/image/upload-image-dialog.component';
import { UrlHolder } from '@shared/pipe/image.pipe';
import { ImportExportService } from '@shared/import-export/import-export.service';
import { EmbedImageDialogComponent, EmbedImageDialogData } from '@shared/components/image/embed-image-dialog.component';
@Component({
selector: 'tb-image-gallery-dialog',
templateUrl: './image-gallery-dialog.component.html',
styleUrls: ['./image-gallery-dialog.component.scss']
})
export class ImageGalleryDialogComponent extends
DialogComponent<ImageGalleryDialogComponent, ImageResourceInfo> implements OnInit {
constructor(protected store: Store<AppState>,
protected router: Router,
private imageService: ImageService,
private dialog: MatDialog,
public dialogRef: MatDialogRef<ImageGalleryDialogComponent, ImageResourceInfo>) {
super(store, router, dialogRef);
}
ngOnInit(): void {
}
cancel(): void {
this.dialogRef.close(null);
}
imageSelected(image: ImageResourceInfo): void {
this.dialogRef.close(image);
}
}

View File

@ -15,11 +15,11 @@
limitations under the License.
-->
<div class="tb-images tb-absolute-fill" [ngClass]="{'tb-popover-mode': popoverMode, 'mat-padding': !popoverMode}">
<div class="tb-images tb-absolute-fill" [ngClass]="{'tb-dialog-mode': dialogMode, 'mat-padding': !dialogMode}">
<div fxFlex fxLayout="column" class="tb-images-content" [ngClass]="{'tb-outlined-border': pageMode}">
<mat-toolbar class="mat-mdc-table-toolbar"
fxLayout.lt-md="column" fxLayoutAlign.lt-md="start stretch"
[fxShow]="!textSearchMode && (mode === 'grid' || dataSource?.selection.isEmpty())" [ngClass.lt-md]="{'multi-row': !isSysAdmin}">
fxLayout.lt-lg="column" fxLayoutAlign.lt-lg="start stretch"
[fxShow]="!textSearchMode && (mode === 'grid' || dataSource?.selection.isEmpty())" [ngClass.lt-lg]="{'multi-row': !isSysAdmin}">
<div fxFlex fxLayout="row" fxLayoutAlign="start center">
<div class="mat-toolbar-tools">
<span fxHide fxShow.gt-sm class="tb-images-title" translate>image.gallery</span>
@ -44,11 +44,11 @@
</div>
</div>
<mat-slide-toggle *ngIf="!isSysAdmin"
fxHide fxShow.gt-sm
fxHide fxShow.gt-md
[ngModel]="includeSystemImages"
(ngModelChange)="includeSystemImagesChanged($event)">{{ 'image.include-system-images' | translate }}</mat-slide-toggle>
</div>
<section fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px">
<section fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap.gt-md="16px">
<span fxFlex></span>
<section fxLayout="row" fxLayoutAlign="start center">
<button [disabled]="isLoading$ | async"
@ -79,7 +79,7 @@
(click)="uploadImage()"
matTooltip="{{'image.upload-image' | translate }}"
matTooltipPosition="above">
<mat-icon>upload</mat-icon>
<mat-icon>add</mat-icon>
</button>
</section>
<button fxHide fxShow.gt-md
@ -91,7 +91,7 @@
</button>
</section>
</div>
<mat-slide-toggle *ngIf="!isSysAdmin" fxHide fxShow.lt-md
<mat-slide-toggle *ngIf="!isSysAdmin" fxHide fxShow.lt-lg
[ngModel]="includeSystemImages"
(ngModelChange)="includeSystemImagesChanged($event)">{{ 'image.include-system-images' | translate }}</mat-slide-toggle>
</mat-toolbar>
@ -293,7 +293,7 @@
[hidePageSize]="hidePageSize"
showFirstLastButtons></mat-paginator>
</div>
<div fxFlex class="mat-padding" *ngIf="mode === 'grid'" fxLayout="column">
<div *ngIf="mode === 'grid'" fxFlex [ngClass]="{'mat-padding': !dialogMode}" fxLayout="column">
<tb-scroll-grid fxFlex
[columns]="gridColumns"
[itemSize]="gridImagesItemSizeStrategy"

View File

@ -19,36 +19,12 @@ $tb-button-selected-color: rgb(255, 110, 64) !default;
.tb-images {
&.tb-popover-mode {
padding-top: 8px;
&.tb-dialog-mode {
position: relative;
width: 300px;
height: 500px;
max-width: calc(100vw - 50px);
max-height: calc(100vh - 100px);
@media #{$mat-gt-xs} {
width: 500px;
height: 600px;
}
@media #{$mat-gt-sm} {
width: 700px;
height: 600px;
}
@media #{$mat-gt-md} {
width: 1000px;
height: 700px;
}
@media #{$mat-gt-xmd} {
width: 1300px;
height: 700px;
}
@media #{$mat-gt-xl} {
width: 1600px;
height: 800px;
}
@media screen and (min-width: 2320px) {
width: 2000px;
height: 800px;
width: 100%;
height: 100%;
.mat-toolbar.mat-mdc-table-toolbar {
padding: 0;
}
}

View File

@ -88,15 +88,11 @@ const pageGridColumns: ScrollGridColumns = {
}
};
const popoverGridColumns: ScrollGridColumns = {
columns: 1,
const dialogGridColumns: ScrollGridColumns = {
columns: 2,
breakpoints: {
'screen and (min-width: 2320px)': 8,
'gt-lg': 6,
'screen and (min-width: 1600px)': 5,
'gt-md': 4,
'gt-sm': 3,
'gt-xs': 2
'gt-xs': 3
}
};
@ -111,18 +107,25 @@ export class ImageGalleryComponent extends PageComponent implements OnInit, OnDe
@HostBinding('style.display')
private display = 'block';
@HostBinding('style.width')
private width = '100%';
@HostBinding('style.height')
private height = '100%';
@Input()
@coerceBoolean()
pageMode = true;
@Input()
@coerceBoolean()
popoverMode = false;
dialogMode = false;
@Input()
mode: 'list' | 'grid' = 'list';
@Input()
@coerceBoolean()
selectionMode = false;
@Output()
@ -194,7 +197,7 @@ export class ImageGalleryComponent extends PageComponent implements OnInit, OnDe
}
ngOnInit(): void {
this.gridColumns = this.popoverMode ? popoverGridColumns : pageGridColumns;
this.gridColumns = this.dialogMode ? dialogGridColumns : pageGridColumns;
this.displayedColumns = this.computeDisplayedColumns();
let sortOrder: SortOrder = this.defaultSortOrder;
this.pageSizeOptions = [this.defaultPageSize, this.defaultPageSize * 2, this.defaultPageSize * 3];

View File

@ -215,6 +215,7 @@ import { ImagesInUseDialogComponent } from '@shared/components/image/images-in-u
import { GalleryImageInputComponent } from '@shared/components/image/gallery-image-input.component';
import { MultipleGalleryImageInputComponent } from '@shared/components/image/multiple-gallery-image-input.component';
import { EmbedImageDialogComponent } from '@shared/components/image/embed-image-dialog.component';
import { ImageGalleryDialogComponent } from '@shared/components/image/image-gallery-dialog.component';
export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) {
return markedOptionsService;
@ -410,7 +411,8 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService)
ImagesInUseDialogComponent,
GalleryImageInputComponent,
MultipleGalleryImageInputComponent,
EmbedImageDialogComponent
EmbedImageDialogComponent,
ImageGalleryDialogComponent
],
imports: [
CommonModule,
@ -660,7 +662,8 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService)
ImagesInUseDialogComponent,
GalleryImageInputComponent,
MultipleGalleryImageInputComponent,
EmbedImageDialogComponent
EmbedImageDialogComponent,
ImageGalleryDialogComponent
]
})
export class SharedModule { }