file upload zone is added
This commit is contained in:
parent
f3f8059564
commit
1147fe830d
|
@ -53,7 +53,16 @@
|
||||||
mode="indeterminate"
|
mode="indeterminate"
|
||||||
></mat-progress-bar>
|
></mat-progress-bar>
|
||||||
<!-- CHAT CONTENT -->
|
<!-- CHAT CONTENT -->
|
||||||
<div fxFlex="1 1 auto" class="chat-content" #messageBoxContainer>
|
<div
|
||||||
|
fxFlex="1 1 auto"
|
||||||
|
class="chat-content"
|
||||||
|
#messageBoxContainer
|
||||||
|
ucapUiFileUploadFor
|
||||||
|
(fileSelected)="onFileSelected($event)"
|
||||||
|
(fileDragEnter)="onFileDragEnter()"
|
||||||
|
(fileDragOver)="onFileDragOver()"
|
||||||
|
(fileDragLeave)="onFileDragLeave()"
|
||||||
|
>
|
||||||
<!-- Timer Room Info -->
|
<!-- Timer Room Info -->
|
||||||
<span *ngIf="roomInfo && roomInfo.isTimeRoom">비밀 대화방입니다.</span>
|
<span *ngIf="roomInfo && roomInfo.isTimeRoom">비밀 대화방입니다.</span>
|
||||||
<!-- Timer Room Info -->
|
<!-- Timer Room Info -->
|
||||||
|
@ -71,6 +80,17 @@
|
||||||
>
|
>
|
||||||
</ucap-chat-messages>
|
</ucap-chat-messages>
|
||||||
<!-- CHAT MESSAGES -->
|
<!-- CHAT MESSAGES -->
|
||||||
|
|
||||||
|
<div
|
||||||
|
*ngIf="fileDragOver || (files && 0 < files.length)"
|
||||||
|
class="file-drop-zone-container"
|
||||||
|
>
|
||||||
|
<div class="file-drop-zone">
|
||||||
|
<ucap-ui-file-upload-queue
|
||||||
|
[(files)]="files"
|
||||||
|
></ucap-ui-file-upload-queue>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- / CHAT CONTENT -->
|
<!-- / CHAT CONTENT -->
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,22 @@
|
||||||
background: transparent;
|
background: transparent;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
-webkit-overflow-scrolling: touch;
|
-webkit-overflow-scrolling: touch;
|
||||||
|
|
||||||
|
.file-drop-zone-container {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.file-drop-zone {
|
||||||
|
position: absolute;
|
||||||
|
border: 2px solid gray;
|
||||||
|
background-color: white;
|
||||||
|
width: 600px;
|
||||||
|
height: 150px;
|
||||||
|
bottom: 0px;
|
||||||
|
margin: auto;
|
||||||
|
left: 0px;
|
||||||
|
right: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,6 +88,9 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewChecked {
|
||||||
isCopyableMessage = isCopyable;
|
isCopyableMessage = isCopyable;
|
||||||
isRecallableMessage = isRecallable;
|
isRecallableMessage = isRecallable;
|
||||||
|
|
||||||
|
fileDragOver = false;
|
||||||
|
files: File[];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private store: Store<any>,
|
private store: Store<any>,
|
||||||
private sessionStorageService: SessionStorageService,
|
private sessionStorageService: SessionStorageService,
|
||||||
|
@ -198,6 +201,30 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewChecked {
|
||||||
this.logger.debug('fileSave', value);
|
this.logger.debug('fileSave', value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onFileDragEnter() {
|
||||||
|
this.logger.debug('onFileDragEnter');
|
||||||
|
this.fileDragOver = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
onFileDragOver() {
|
||||||
|
this.logger.debug('onFileDragOver');
|
||||||
|
}
|
||||||
|
|
||||||
|
onFileDragLeave() {
|
||||||
|
this.logger.debug('onFileDragLeave');
|
||||||
|
this.fileDragOver = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
onFileSelected(files: File[]) {
|
||||||
|
this.logger.debug('onFileSelected', files);
|
||||||
|
if (!this.files) {
|
||||||
|
this.files = [];
|
||||||
|
}
|
||||||
|
this.files.push(...files);
|
||||||
|
|
||||||
|
this.fileDragOver = false;
|
||||||
|
}
|
||||||
|
|
||||||
onContextMenuMessage(params: { event: MouseEvent; message: Info }) {
|
onContextMenuMessage(params: { event: MouseEvent; message: Info }) {
|
||||||
params.event.preventDefault();
|
params.event.preventDefault();
|
||||||
params.event.stopPropagation();
|
params.event.stopPropagation();
|
||||||
|
|
|
@ -17,6 +17,7 @@ import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
import { MatInputModule } from '@angular/material/input';
|
import { MatInputModule } from '@angular/material/input';
|
||||||
|
|
||||||
import { DragDropModule } from '@angular/cdk/drag-drop';
|
import { DragDropModule } from '@angular/cdk/drag-drop';
|
||||||
|
import { OverlayModule } from '@angular/cdk/overlay';
|
||||||
|
|
||||||
import { UCapUiModule } from '@ucap-webmessenger/ui';
|
import { UCapUiModule } from '@ucap-webmessenger/ui';
|
||||||
import { UCapUiChatModule } from '@ucap-webmessenger/ui-chat';
|
import { UCapUiChatModule } from '@ucap-webmessenger/ui-chat';
|
||||||
|
@ -35,6 +36,7 @@ import { ReactiveFormsModule } from '@angular/forms';
|
||||||
FlexLayoutModule,
|
FlexLayoutModule,
|
||||||
DragDropModule,
|
DragDropModule,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
|
OverlayModule,
|
||||||
MatFormFieldModule,
|
MatFormFieldModule,
|
||||||
MatInputModule,
|
MatInputModule,
|
||||||
MatBadgeModule,
|
MatBadgeModule,
|
||||||
|
|
|
@ -2,3 +2,7 @@
|
||||||
|
|
||||||
// Import app.theme.scss
|
// Import app.theme.scss
|
||||||
@import 'app/app.theme';
|
@import 'app/app.theme';
|
||||||
|
|
||||||
|
.file-upload-backdrop {
|
||||||
|
background-color: aqua;
|
||||||
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ import {
|
||||||
UserInfoF,
|
UserInfoF,
|
||||||
UserInfoDN
|
UserInfoDN
|
||||||
} from '@ucap-webmessenger/protocol-query';
|
} from '@ucap-webmessenger/protocol-query';
|
||||||
|
import { NGXLogger } from 'ngx-logger';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ucap-group-expansion-panel',
|
selector: 'ucap-group-expansion-panel',
|
||||||
|
@ -57,7 +58,7 @@ export class ExpansionPanelComponent implements OnInit {
|
||||||
|
|
||||||
@ViewChild('groupAccordion', { static: true }) groupAccordion: MatAccordion;
|
@ViewChild('groupAccordion', { static: true }) groupAccordion: MatAccordion;
|
||||||
|
|
||||||
constructor() {}
|
constructor(private logger: NGXLogger) {}
|
||||||
|
|
||||||
ngOnInit() {}
|
ngOnInit() {}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
<div
|
||||||
|
fxLayout="row wrap"
|
||||||
|
fxFlex="100"
|
||||||
|
class="ucap-ui-file-upload-queue-container"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
fxLayout="row"
|
||||||
|
fxFlex="100"
|
||||||
|
fxFlex.gt-xs="50"
|
||||||
|
fxFlex.gt-md="25"
|
||||||
|
*ngFor="let file of uploadFiles"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<mat-icon>image</mat-icon>
|
||||||
|
</div>
|
||||||
|
<div>{{ file.name }}</div>
|
||||||
|
<div (click)="onClickClear(file)">
|
||||||
|
<mat-icon>clear</mat-icon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,4 @@
|
||||||
|
.ucap-ui-file-upload-queue-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
||||||
|
|
||||||
|
import { NGXLogger } from 'ngx-logger';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ucap-ui-file-upload-queue',
|
||||||
|
templateUrl: './file-upload-queue.component.html',
|
||||||
|
styleUrls: ['./file-upload-queue.component.scss']
|
||||||
|
})
|
||||||
|
export class FileUploadQueueComponent implements OnInit {
|
||||||
|
@Output()
|
||||||
|
filesChange = new EventEmitter<File[]>();
|
||||||
|
@Input() set files(files: File[]) {
|
||||||
|
this.uploadFiles = files;
|
||||||
|
}
|
||||||
|
|
||||||
|
uploadFiles: File[];
|
||||||
|
|
||||||
|
constructor(private logger: NGXLogger) {}
|
||||||
|
|
||||||
|
ngOnInit() {}
|
||||||
|
|
||||||
|
onClickClear(file: File) {
|
||||||
|
this.uploadFiles = this.uploadFiles.filter(f => {
|
||||||
|
return f.name !== file.name && f.path !== file.path;
|
||||||
|
});
|
||||||
|
this.filesChange.emit(this.uploadFiles);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
import {
|
||||||
|
Directive,
|
||||||
|
ElementRef,
|
||||||
|
EventEmitter,
|
||||||
|
HostListener,
|
||||||
|
Output
|
||||||
|
} from '@angular/core';
|
||||||
|
|
||||||
|
import { NGXLogger } from 'ngx-logger';
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: 'input[ucapUiFileUploadFor], div[ucapUiFileUploadFor]'
|
||||||
|
})
|
||||||
|
export class FileUploadForDirective {
|
||||||
|
@Output()
|
||||||
|
public fileDragEnter: EventEmitter<any> = new EventEmitter();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
public fileDragOver: EventEmitter<any> = new EventEmitter();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
public fileDragLeave: EventEmitter<any> = new EventEmitter();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
public fileSelected: EventEmitter<File[]> = new EventEmitter<File[]>();
|
||||||
|
|
||||||
|
dragOver = false;
|
||||||
|
|
||||||
|
constructor(private elementRef: ElementRef, private logger: NGXLogger) {}
|
||||||
|
|
||||||
|
@HostListener('window:dragenter', ['$event'])
|
||||||
|
public onDragEnter(event: any): any {
|
||||||
|
const files = event.dataTransfer.files;
|
||||||
|
if (!this.dragOver) {
|
||||||
|
this.fileDragEnter.emit(files);
|
||||||
|
this.dragOver = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@HostListener('window:dragover', ['$event'])
|
||||||
|
public onDragOver(event: any): any {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
@HostListener('window:dragleave', ['$event'])
|
||||||
|
public onDragLeave(event: any): any {
|
||||||
|
if (event && event.pageX === 0 && event.pageY === 0) {
|
||||||
|
this.fileDragLeave.emit();
|
||||||
|
this.dragOver = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@HostListener('change')
|
||||||
|
public onChange(): any {
|
||||||
|
const files = this.elementRef.nativeElement.files;
|
||||||
|
this.fileSelected.emit(files);
|
||||||
|
this.elementRef.nativeElement.value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
@HostListener('window:drop', ['$event'])
|
||||||
|
public onDrop(event: any): any {
|
||||||
|
const files = event.dataTransfer.files;
|
||||||
|
this.fileSelected.emit(files);
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
this.elementRef.nativeElement.value = '';
|
||||||
|
this.dragOver = false;
|
||||||
|
}
|
||||||
|
}
|
20
projects/ucap-webmessenger-ui/src/lib/pipes/bytes.pipe.ts
Normal file
20
projects/ucap-webmessenger-ui/src/lib/pipes/bytes.pipe.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import { Pipe, PipeTransform } from '@angular/core';
|
||||||
|
|
||||||
|
const units = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB'];
|
||||||
|
|
||||||
|
@Pipe({ name: 'ucapBytes' })
|
||||||
|
export class BytesPipe implements PipeTransform {
|
||||||
|
public transform(bytes: number): string {
|
||||||
|
if (isNaN(parseFloat('' + bytes)) || !isFinite(bytes)) {
|
||||||
|
return '-';
|
||||||
|
}
|
||||||
|
if (bytes <= 0) {
|
||||||
|
return '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
const num = Math.floor(Math.log(bytes) / Math.log(1024));
|
||||||
|
return (
|
||||||
|
(bytes / Math.pow(1024, Math.floor(num))).toFixed(1) + ' ' + units[num]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,76 +0,0 @@
|
||||||
import { PipeTransform, Pipe } from '@angular/core';
|
|
||||||
import { of, Observable } from 'rxjs';
|
|
||||||
import { NGXLogger } from 'ngx-logger';
|
|
||||||
import { HttpClient } from '@angular/common/http';
|
|
||||||
import { take, map, catchError, switchMap, tap } from 'rxjs/operators';
|
|
||||||
|
|
||||||
export interface ImagePipeParameter {
|
|
||||||
path?: string;
|
|
||||||
validation?: boolean;
|
|
||||||
default?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Pipe({ name: 'ucapUiImaage' })
|
|
||||||
export class ImagePipe implements PipeTransform {
|
|
||||||
constructor(private httpClient: HttpClient, private logger: NGXLogger) {}
|
|
||||||
|
|
||||||
transform(
|
|
||||||
base: string,
|
|
||||||
params?: ImagePipeParameter
|
|
||||||
): Observable<string | ArrayBuffer> {
|
|
||||||
params = !!params ? params : {};
|
|
||||||
params.validation = !!params.validation ? params.validation : true;
|
|
||||||
|
|
||||||
if (params.validation) {
|
|
||||||
if (
|
|
||||||
!base ||
|
|
||||||
'' === base.trim() ||
|
|
||||||
!params.path ||
|
|
||||||
'' === params.path.trim()
|
|
||||||
) {
|
|
||||||
return of(params.default);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const imageUrl = `${base}${params.path}`;
|
|
||||||
|
|
||||||
return new Observable<string | ArrayBuffer>(subscriber => {
|
|
||||||
subscriber.next(params.default);
|
|
||||||
|
|
||||||
this.httpClient
|
|
||||||
.get(imageUrl, { responseType: 'blob' })
|
|
||||||
.pipe(
|
|
||||||
take(1),
|
|
||||||
tap(blob => {
|
|
||||||
const reader = new FileReader();
|
|
||||||
reader.onloadend = ev => {
|
|
||||||
subscriber.next(reader.result);
|
|
||||||
};
|
|
||||||
reader.readAsDataURL(blob);
|
|
||||||
}),
|
|
||||||
catchError(err => {
|
|
||||||
this.logger.error(err);
|
|
||||||
return params.default;
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.subscribe();
|
|
||||||
});
|
|
||||||
|
|
||||||
return this.httpClient.get(imageUrl, { responseType: 'blob' }).pipe(
|
|
||||||
take(1),
|
|
||||||
switchMap(blob => {
|
|
||||||
return new Observable<string | ArrayBuffer>(subscriber => {
|
|
||||||
const reader = new FileReader();
|
|
||||||
reader.onloadend = ev => {
|
|
||||||
subscriber.next(reader.result);
|
|
||||||
};
|
|
||||||
reader.readAsDataURL(blob);
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
catchError(err => {
|
|
||||||
this.logger.error(err);
|
|
||||||
return params.default;
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +1,19 @@
|
||||||
import { NgModule, ModuleWithProviders } from '@angular/core';
|
import { NgModule, ModuleWithProviders } from '@angular/core';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
|
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||||
|
|
||||||
import { MatButtonModule } from '@angular/material/button';
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
import { MatCardModule } from '@angular/material/card';
|
import { MatCardModule } from '@angular/material/card';
|
||||||
import { MatDialogModule } from '@angular/material/dialog';
|
import { MatDialogModule } from '@angular/material/dialog';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||||
|
|
||||||
import { DragDropModule } from '@angular/cdk/drag-drop';
|
import { DragDropModule } from '@angular/cdk/drag-drop';
|
||||||
|
|
||||||
|
import { FileUploadQueueComponent } from './components/file-upload-queue.component';
|
||||||
import { ImageComponent } from './components/image.component';
|
import { ImageComponent } from './components/image.component';
|
||||||
const COMPONENTS = [ImageComponent];
|
const COMPONENTS = [ImageComponent, FileUploadQueueComponent];
|
||||||
|
|
||||||
import { BottomSheetService } from './services/bottom-sheet.service';
|
import { BottomSheetService } from './services/bottom-sheet.service';
|
||||||
import { ClipboardService } from './services/clipboard.service';
|
import { ClipboardService } from './services/clipboard.service';
|
||||||
|
@ -22,24 +26,29 @@ const SERVICES = [
|
||||||
SnackBarService
|
SnackBarService
|
||||||
];
|
];
|
||||||
|
|
||||||
|
import { FileUploadForDirective } from './directives/file-upload-for.directive';
|
||||||
|
const DIRECTIVES = [FileUploadForDirective];
|
||||||
|
|
||||||
import { AlertDialogComponent } from './dialogs/alert.dialog.component';
|
import { AlertDialogComponent } from './dialogs/alert.dialog.component';
|
||||||
import { ConfirmDialogComponent } from './dialogs/confirm.dialog.component';
|
import { ConfirmDialogComponent } from './dialogs/confirm.dialog.component';
|
||||||
const DIALOGS = [AlertDialogComponent, ConfirmDialogComponent];
|
const DIALOGS = [AlertDialogComponent, ConfirmDialogComponent];
|
||||||
|
|
||||||
import { ImagePipe } from './pipes/image.pipe';
|
import { BytesPipe } from './pipes/bytes.pipe';
|
||||||
const PIPES = [ImagePipe];
|
const PIPES = [BytesPipe];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
|
FlexLayoutModule,
|
||||||
MatButtonModule,
|
MatButtonModule,
|
||||||
MatCardModule,
|
MatCardModule,
|
||||||
MatDialogModule,
|
MatDialogModule,
|
||||||
|
MatIconModule,
|
||||||
MatSnackBarModule,
|
MatSnackBarModule,
|
||||||
DragDropModule
|
DragDropModule
|
||||||
],
|
],
|
||||||
exports: [...COMPONENTS, ...PIPES],
|
exports: [...COMPONENTS, ...DIRECTIVES, ...PIPES],
|
||||||
declarations: [...COMPONENTS, ...DIALOGS, ...PIPES],
|
declarations: [...COMPONENTS, ...DIALOGS, ...DIRECTIVES, ...PIPES],
|
||||||
entryComponents: [...DIALOGS]
|
entryComponents: [...DIALOGS]
|
||||||
})
|
})
|
||||||
export class UCapUiModule {
|
export class UCapUiModule {
|
||||||
|
|
|
@ -7,7 +7,7 @@ export * from './lib/animations';
|
||||||
export * from './lib/dialogs/alert.dialog.component';
|
export * from './lib/dialogs/alert.dialog.component';
|
||||||
export * from './lib/dialogs/confirm.dialog.component';
|
export * from './lib/dialogs/confirm.dialog.component';
|
||||||
|
|
||||||
export * from './lib/pipes/image.pipe';
|
export * from './lib/directives/file-upload-for.directive';
|
||||||
|
|
||||||
export * from './lib/services/bottom-sheet.service';
|
export * from './lib/services/bottom-sheet.service';
|
||||||
export * from './lib/services/clipboard.service';
|
export * from './lib/services/clipboard.service';
|
||||||
|
|
Loading…
Reference in New Issue
Block a user