Merge branch 'master' of http://10.81.13.221:6990/Web/next-ucap-messenger
This commit is contained in:
commit
cc7908875a
|
@ -221,6 +221,8 @@ export class LeftSideComponent implements OnInit, OnDestroy {
|
||||||
>(MessageWriteDialogComponent, {
|
>(MessageWriteDialogComponent, {
|
||||||
width: '600px',
|
width: '600px',
|
||||||
height: '600px',
|
height: '600px',
|
||||||
|
disableClose: true,
|
||||||
|
hasBackdrop: false,
|
||||||
data: {
|
data: {
|
||||||
loginRes: this.loginRes,
|
loginRes: this.loginRes,
|
||||||
environmentsInfo: this.environmentsInfo
|
environmentsInfo: this.environmentsInfo
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#messageWrite
|
#messageWrite
|
||||||
(send)="onSend($event)"
|
(send)="onSend($event)"
|
||||||
(selectReceiver)="onSelectReceiver($event)"
|
(selectReceiver)="onSelectReceiver($event)"
|
||||||
|
(cancel)="onCancel()"
|
||||||
></ucap-message-write>
|
></ucap-message-write>
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
|
|
|
@ -98,14 +98,34 @@ export class MessageWriteDialogComponent implements OnInit {
|
||||||
.pipe(take(1))
|
.pipe(take(1))
|
||||||
.subscribe(
|
.subscribe(
|
||||||
res => {
|
res => {
|
||||||
this.logger.debug('onSend', res);
|
let msg = '';
|
||||||
|
if (!!message.reservationTime) {
|
||||||
|
msg = `쪽지 전송을 예약 하였습니다.`;
|
||||||
|
} else {
|
||||||
|
msg = `쪽지를 전송하였습니다.`;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.snackBarService.open(msg, '', {
|
||||||
|
duration: 3000,
|
||||||
|
verticalPosition: 'bottom'
|
||||||
|
});
|
||||||
|
|
||||||
|
this.dialogRef.close({});
|
||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
this.logger.debug('onSend', error);
|
this.snackBarService.open(`쪽지를 전송에 실패 하였습니다.`, '', {
|
||||||
|
duration: 3000,
|
||||||
|
verticalPosition: 'bottom'
|
||||||
|
});
|
||||||
|
this.dialogRef.close({});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onCancel() {
|
||||||
|
this.dialogRef.close({});
|
||||||
|
}
|
||||||
|
|
||||||
getBtnValid() {
|
getBtnValid() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
$mdi-sizes: 18 24 36 48;
|
$mdi-sizes: 18 20 24 36 48;
|
||||||
@each $mdi-size in $mdi-sizes {
|
@each $mdi-size in $mdi-sizes {
|
||||||
.#{$mdi-css-prefix}-#{$mdi-size}px {
|
.#{$mdi-css-prefix}-#{$mdi-size}px {
|
||||||
&.#{$mdi-css-prefix}-set,
|
&.#{$mdi-css-prefix}-set,
|
||||||
|
|
|
@ -294,6 +294,12 @@ $daesang-grey: (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mat-button-toggle-appearance-standard .mat-button-toggle-label-content {
|
||||||
|
height: 34px;
|
||||||
|
line-height: 34px;
|
||||||
|
padding: 0 12px;
|
||||||
|
}
|
||||||
|
|
||||||
.app-dialog-full .mat-dialog-container {
|
.app-dialog-full .mat-dialog-container {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
|
|
|
@ -52,17 +52,6 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="editor-actions-spacer"></div>
|
<div class="editor-actions-spacer"></div>
|
||||||
<div>
|
|
||||||
<mat-form-field>
|
|
||||||
<input matInput [matDatepicker]="picker" placeholder="발송일자" />
|
|
||||||
<mat-datepicker-toggle
|
|
||||||
matSuffix
|
|
||||||
[for]="picker"
|
|
||||||
></mat-datepicker-toggle>
|
|
||||||
<mat-datepicker #picker></mat-datepicker>
|
|
||||||
</mat-form-field>
|
|
||||||
</div>
|
|
||||||
<div class="editor-actions-spacer"></div>
|
|
||||||
<div class="editor-actions">
|
<div class="editor-actions">
|
||||||
<button
|
<button
|
||||||
mat-stroked-button
|
mat-stroked-button
|
||||||
|
@ -71,14 +60,17 @@
|
||||||
>
|
>
|
||||||
취소
|
취소
|
||||||
</button>
|
</button>
|
||||||
<button
|
|
||||||
mat-flat-button
|
<mat-menu #appMenu="matMenu" yPosition="above">
|
||||||
(click)="onClickSend()"
|
<button mat-menu-item (click)="onClickSendSchedule()">
|
||||||
class="mat-primary"
|
<span class="mdi mdi-send-clock"></span><span>예약 보내기</span>
|
||||||
style="margin-left: 3px;"
|
|
||||||
>
|
|
||||||
보내기
|
|
||||||
</button>
|
</button>
|
||||||
|
</mat-menu>
|
||||||
|
|
||||||
|
<ucap-split-button [menu]="appMenu" (buttonClick)="onClickSend()"
|
||||||
|
>보내기</ucap-split-button
|
||||||
|
>
|
||||||
|
<!-- [disabled]="messageWriteForm.invalid" -->
|
||||||
</div>
|
</div>
|
||||||
</mat-card-actions>
|
</mat-card-actions>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -10,9 +10,12 @@ import {
|
||||||
ElementRef
|
ElementRef
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
|
||||||
import { ucapAnimations } from '@ucap-webmessenger/ui';
|
import { ucapAnimations, DialogService } from '@ucap-webmessenger/ui';
|
||||||
|
|
||||||
import { NGXLogger } from 'ngx-logger';
|
import { NGXLogger } from 'ngx-logger';
|
||||||
|
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
import { FileUtil } from '@ucap-webmessenger/core';
|
import { FileUtil } from '@ucap-webmessenger/core';
|
||||||
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
|
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
|
||||||
import {
|
import {
|
||||||
|
@ -22,6 +25,11 @@ import {
|
||||||
} from '@ucap-webmessenger/api-message';
|
} from '@ucap-webmessenger/api-message';
|
||||||
import { FileUploadItem } from '@ucap-webmessenger/api';
|
import { FileUploadItem } from '@ucap-webmessenger/api';
|
||||||
import { UserInfo } from '@ucap-webmessenger/protocol-sync';
|
import { UserInfo } from '@ucap-webmessenger/protocol-sync';
|
||||||
|
import {
|
||||||
|
ScheduleSendDialogComponent,
|
||||||
|
ScheduleSendDialogData,
|
||||||
|
ScheduleSendDialogResult
|
||||||
|
} from '../dialogs/schedule-send.dialog.component';
|
||||||
|
|
||||||
const ATTR_FILE = 'UCAP_ATTR_FILE';
|
const ATTR_FILE = 'UCAP_ATTR_FILE';
|
||||||
|
|
||||||
|
@ -53,6 +61,9 @@ export class WriteComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||||
@Output()
|
@Output()
|
||||||
send = new EventEmitter<Message>();
|
send = new EventEmitter<Message>();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
cancel = new EventEmitter<void>();
|
||||||
|
|
||||||
@Output()
|
@Output()
|
||||||
selectReceiver = new EventEmitter<UserInfo[]>();
|
selectReceiver = new EventEmitter<UserInfo[]>();
|
||||||
|
|
||||||
|
@ -69,6 +80,7 @@ export class WriteComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private formBuilder: FormBuilder,
|
private formBuilder: FormBuilder,
|
||||||
|
private dialogService: DialogService,
|
||||||
private changeDetectorRef: ChangeDetectorRef,
|
private changeDetectorRef: ChangeDetectorRef,
|
||||||
private logger: NGXLogger
|
private logger: NGXLogger
|
||||||
) {}
|
) {}
|
||||||
|
@ -147,6 +159,31 @@ export class WriteComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
onClickSend() {
|
onClickSend() {
|
||||||
|
this.sendMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickCancel() {
|
||||||
|
this.cancel.emit();
|
||||||
|
}
|
||||||
|
|
||||||
|
async onClickSendSchedule() {
|
||||||
|
const result = await this.dialogService.open<
|
||||||
|
ScheduleSendDialogComponent,
|
||||||
|
ScheduleSendDialogData,
|
||||||
|
ScheduleSendDialogResult
|
||||||
|
>(ScheduleSendDialogComponent, {
|
||||||
|
width: '600px',
|
||||||
|
height: '600px',
|
||||||
|
disableClose: true,
|
||||||
|
data: {}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!!result && !!result.scheduleSendDate) {
|
||||||
|
this.sendMessage(result.scheduleSendDate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private sendMessage(reservationDate?: moment.Moment) {
|
||||||
const contentList: Content[] = this.generateContent();
|
const contentList: Content[] = this.generateContent();
|
||||||
if (!contentList || 0 === contentList.length) {
|
if (!contentList || 0 === contentList.length) {
|
||||||
return;
|
return;
|
||||||
|
@ -184,18 +221,19 @@ export class WriteComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||||
|
|
||||||
this.send.emit({
|
this.send.emit({
|
||||||
category: CategoryType.General,
|
category: CategoryType.General,
|
||||||
type: MessageType.Send,
|
type: !!reservationDate ? MessageType.Reservation : MessageType.Send,
|
||||||
title,
|
title,
|
||||||
listOrder,
|
listOrder,
|
||||||
textContent,
|
textContent,
|
||||||
recvUserList,
|
recvUserList,
|
||||||
|
reservationTime: !!reservationDate
|
||||||
|
? `${reservationDate.format('YYYY-MM-DD HH:mm')}:00`
|
||||||
|
: undefined,
|
||||||
files,
|
files,
|
||||||
fileUploadItem: this.fileUploadItem
|
fileUploadItem: this.fileUploadItem
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onClickCancel() {}
|
|
||||||
|
|
||||||
private generateContent(): Content[] {
|
private generateContent(): Content[] {
|
||||||
const contentList: Content[] = [];
|
const contentList: Content[] = [];
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,114 @@
|
||||||
|
<mat-card class="confirm-card mat-elevation-z">
|
||||||
|
<mat-card-header>
|
||||||
|
<mat-card-title>
|
||||||
|
예약 발송
|
||||||
|
</mat-card-title>
|
||||||
|
</mat-card-header>
|
||||||
|
<mat-card-content>
|
||||||
|
<div fxLayout="row">
|
||||||
|
<div fxFlex="60%">
|
||||||
|
<div fxLayout="column">
|
||||||
|
<div fxFlexFill>
|
||||||
|
<ucap-pick-date
|
||||||
|
[initSelected]="selectedDate"
|
||||||
|
(selected)="onSelectedDate($event)"
|
||||||
|
></ucap-pick-date>
|
||||||
|
</div>
|
||||||
|
<div fxFlex="30px" style="margin-top: 30px;">
|
||||||
|
<span flFlex="1 1 auto"></span>
|
||||||
|
<span style="width: 100px;">
|
||||||
|
<ucap-pick-time
|
||||||
|
[hour]="selectedDate.hour()"
|
||||||
|
[hourStep]="hourStep"
|
||||||
|
[minute]="selectedDate.minute()"
|
||||||
|
[minuteStep]="minuteStep"
|
||||||
|
(hourSelected)="onHourSelected($event)"
|
||||||
|
(minuteSelected)="onMinuteSelected($event)"
|
||||||
|
>
|
||||||
|
</ucap-pick-time>
|
||||||
|
</span>
|
||||||
|
<span flFlex="1 1 auto"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div fxFlex="40%" style="margin-left: 30px;">
|
||||||
|
<div fxLayout="column">
|
||||||
|
<div fxFlex="40px" style="margin-top: 30px;">
|
||||||
|
<span flFlexFill style="font-size: 22px;">
|
||||||
|
{{ selectedDate.format('MMM DD YYYY, HH:mm') }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div fxFlexFill>
|
||||||
|
<mat-list role="list">
|
||||||
|
<mat-list-item role="listitem">
|
||||||
|
<button
|
||||||
|
mat-stroked-button
|
||||||
|
class="preset-button"
|
||||||
|
(click)="onClickPresetHour(1)"
|
||||||
|
>
|
||||||
|
1시간 뒤
|
||||||
|
</button>
|
||||||
|
</mat-list-item>
|
||||||
|
<mat-list-item role="listitem">
|
||||||
|
<button
|
||||||
|
mat-stroked-button
|
||||||
|
class="preset-button"
|
||||||
|
(click)="onClickPresetHour(2)"
|
||||||
|
>
|
||||||
|
2시간 뒤
|
||||||
|
</button>
|
||||||
|
</mat-list-item>
|
||||||
|
<mat-list-item role="listitem">
|
||||||
|
<button
|
||||||
|
mat-stroked-button
|
||||||
|
class="preset-button"
|
||||||
|
(click)="onClickPresetTommorowMorning()"
|
||||||
|
>
|
||||||
|
내일 아침
|
||||||
|
</button>
|
||||||
|
</mat-list-item>
|
||||||
|
<mat-list-item role="listitem">
|
||||||
|
<button
|
||||||
|
mat-stroked-button
|
||||||
|
class="preset-button"
|
||||||
|
(click)="onClickPresetTommorowAfternoon()"
|
||||||
|
>
|
||||||
|
내일 오후
|
||||||
|
</button>
|
||||||
|
</mat-list-item>
|
||||||
|
<mat-list-item role="listitem">
|
||||||
|
<button
|
||||||
|
mat-stroked-button
|
||||||
|
class="preset-button"
|
||||||
|
(click)="onClickPresetDay(7)"
|
||||||
|
>
|
||||||
|
일주일 뒤
|
||||||
|
</button>
|
||||||
|
</mat-list-item>
|
||||||
|
<mat-list-item role="listitem">
|
||||||
|
<button
|
||||||
|
mat-stroked-button
|
||||||
|
class="preset-button"
|
||||||
|
(click)="onClickPresetDay(30)"
|
||||||
|
>
|
||||||
|
한달 뒤
|
||||||
|
</button>
|
||||||
|
</mat-list-item>
|
||||||
|
</mat-list>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mat-card-content>
|
||||||
|
<mat-card-actions>
|
||||||
|
<div class="editor-actions-spacer"></div>
|
||||||
|
<div class="editor-actions">
|
||||||
|
<button mat-stroked-button (click)="onClickCancel()" class="mat-primary">
|
||||||
|
취소
|
||||||
|
</button>
|
||||||
|
<button mat-stroked-button (click)="onClickSend()" class="mat-primary">
|
||||||
|
예약 발송
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</mat-card-actions>
|
||||||
|
</mat-card>
|
|
@ -0,0 +1,3 @@
|
||||||
|
.preset-button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ScheduleSendDialogComponent } from './schedule-send.dialog.component';
|
||||||
|
|
||||||
|
describe('message::ScheduleSendDialogComponent', () => {
|
||||||
|
let component: ScheduleSendDialogComponent;
|
||||||
|
let fixture: ComponentFixture<ScheduleSendDialogComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ScheduleSendDialogComponent]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ScheduleSendDialogComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,99 @@
|
||||||
|
import { Component, OnInit, Inject } from '@angular/core';
|
||||||
|
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
|
||||||
|
|
||||||
|
import { NGXLogger } from 'ngx-logger';
|
||||||
|
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
|
// tslint:disable-next-line: no-empty-interface
|
||||||
|
export interface ScheduleSendDialogData {}
|
||||||
|
|
||||||
|
export interface ScheduleSendDialogResult {
|
||||||
|
scheduleSendDate?: moment.Moment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ucap-message-schedule-send',
|
||||||
|
templateUrl: './schedule-send.dialog.component.html',
|
||||||
|
styleUrls: ['./schedule-send.dialog.component.scss']
|
||||||
|
})
|
||||||
|
export class ScheduleSendDialogComponent implements OnInit {
|
||||||
|
scheduleSendForm: FormGroup;
|
||||||
|
|
||||||
|
hourStep = 1;
|
||||||
|
minuteStep = 10;
|
||||||
|
|
||||||
|
get selectedDate() {
|
||||||
|
return this._selectedDate;
|
||||||
|
}
|
||||||
|
set selectedDate(v: moment.Moment) {
|
||||||
|
v.hour(this.hourStep * Math.round(v.hour() / this.hourStep)).minute(
|
||||||
|
this.minuteStep * Math.round(v.minute() / this.minuteStep)
|
||||||
|
);
|
||||||
|
this._selectedDate = v;
|
||||||
|
}
|
||||||
|
// tslint:disable-next-line: variable-name
|
||||||
|
private _selectedDate: moment.Moment;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public dialogRef: MatDialogRef<
|
||||||
|
ScheduleSendDialogData,
|
||||||
|
ScheduleSendDialogResult
|
||||||
|
>,
|
||||||
|
@Inject(MAT_DIALOG_DATA) public data: ScheduleSendDialogData,
|
||||||
|
private formBuilder: FormBuilder,
|
||||||
|
private logger: NGXLogger
|
||||||
|
) {
|
||||||
|
this.selectedDate = moment().add(1, 'hours');
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.scheduleSendForm = this.formBuilder.group({
|
||||||
|
scheduleDate: ['', [Validators.required]],
|
||||||
|
scheduleTime: ['', [Validators.required]]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onSelectedDate(date: moment.Moment) {
|
||||||
|
this.selectedDate = date;
|
||||||
|
}
|
||||||
|
|
||||||
|
onHourSelected(hour: number) {
|
||||||
|
this.selectedDate.hour(hour);
|
||||||
|
}
|
||||||
|
onMinuteSelected(minute: number) {
|
||||||
|
this.selectedDate.minute(minute);
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickPresetTommorowMorning() {
|
||||||
|
this.selectedDate = moment()
|
||||||
|
.add(1, 'days')
|
||||||
|
.hour(10)
|
||||||
|
.minute(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickPresetHour(interval: number) {
|
||||||
|
this.selectedDate = moment().add(interval, 'hours');
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickPresetDay(interval: number) {
|
||||||
|
this.selectedDate = moment().add(interval, 'days');
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickPresetTommorowAfternoon() {
|
||||||
|
this.selectedDate = moment()
|
||||||
|
.add(1, 'days')
|
||||||
|
.hour(15)
|
||||||
|
.minute(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickSend() {
|
||||||
|
this.dialogRef.close({
|
||||||
|
scheduleSendDate: this.selectedDate
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickCancel() {
|
||||||
|
this.dialogRef.close({});
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,6 +18,9 @@ import { MatIconModule } from '@angular/material/icon';
|
||||||
import { MatInputModule } from '@angular/material/input';
|
import { MatInputModule } from '@angular/material/input';
|
||||||
import { MatListModule } from '@angular/material/list';
|
import { MatListModule } from '@angular/material/list';
|
||||||
|
|
||||||
|
import { MatMenuModule } from '@angular/material/menu';
|
||||||
|
import { MatButtonToggleModule } from '@angular/material/button-toggle';
|
||||||
|
|
||||||
import { MatMomentDateModule } from '@angular/material-moment-adapter';
|
import { MatMomentDateModule } from '@angular/material-moment-adapter';
|
||||||
|
|
||||||
import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
|
import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
|
||||||
|
@ -27,8 +30,10 @@ import { UCapUiModule } from '@ucap-webmessenger/ui';
|
||||||
import { ListItemComponent } from './components/list-item.component';
|
import { ListItemComponent } from './components/list-item.component';
|
||||||
import { WriteComponent } from './components/write.component';
|
import { WriteComponent } from './components/write.component';
|
||||||
|
|
||||||
|
import { ScheduleSendDialogComponent } from './dialogs/schedule-send.dialog.component';
|
||||||
|
|
||||||
const COMPONENTS = [ListItemComponent, WriteComponent];
|
const COMPONENTS = [ListItemComponent, WriteComponent];
|
||||||
const DIALOGS = [];
|
const DIALOGS = [ScheduleSendDialogComponent];
|
||||||
const DIRECTIVES = [];
|
const DIRECTIVES = [];
|
||||||
const SERVICES = [];
|
const SERVICES = [];
|
||||||
|
|
||||||
|
@ -53,6 +58,9 @@ const SERVICES = [];
|
||||||
MatListModule,
|
MatListModule,
|
||||||
MatMomentDateModule,
|
MatMomentDateModule,
|
||||||
|
|
||||||
|
MatMenuModule,
|
||||||
|
MatButtonToggleModule,
|
||||||
|
|
||||||
PerfectScrollbarModule,
|
PerfectScrollbarModule,
|
||||||
|
|
||||||
UCapUiModule
|
UCapUiModule
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
* Public API Surface of ucap-webmessenger-ui-message
|
* Public API Surface of ucap-webmessenger-ui-message
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
export * from './lib/components/list-item.component';
|
||||||
export * from './lib/components/write.component';
|
export * from './lib/components/write.component';
|
||||||
|
export * from './lib/dialogs/schedule-send.dialog.component';
|
||||||
|
|
||||||
export * from './lib/ucap-ui-message.module';
|
export * from './lib/ucap-ui-message.module';
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
<div class="toolbar">
|
|
||||||
<button mat-stroked-button (click)="onClickFileInput('attach')">
|
|
||||||
<span class="mdi mdi-attachment"></span>
|
|
||||||
</button>
|
|
||||||
<button mat-stroked-button (click)="onClickFileInput('image')">
|
|
||||||
<span class="mdi mdi-image"></span>
|
|
||||||
</button>
|
|
||||||
<input
|
|
||||||
type="file"
|
|
||||||
#fileInput
|
|
||||||
style="display: none"
|
|
||||||
(change)="onChangeFileInput()"
|
|
||||||
/>
|
|
||||||
<button mat-stroked-button (click)="test()">
|
|
||||||
test
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="container">
|
|
||||||
<div
|
|
||||||
#contentArea
|
|
||||||
id="contentArea"
|
|
||||||
contentEditable="true"
|
|
||||||
class="editor"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
|
@ -1,9 +0,0 @@
|
||||||
.container {
|
|
||||||
height: 500px;
|
|
||||||
overflow: auto;
|
|
||||||
|
|
||||||
.editor {
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { MessageEditorComponent } from './message-editor.component';
|
|
||||||
|
|
||||||
describe('MessageEditorComponent', () => {
|
|
||||||
let component: MessageEditorComponent;
|
|
||||||
let fixture: ComponentFixture<MessageEditorComponent>;
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [ MessageEditorComponent ]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(MessageEditorComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,192 +0,0 @@
|
||||||
import {
|
|
||||||
Component,
|
|
||||||
OnInit,
|
|
||||||
ElementRef,
|
|
||||||
ViewChild,
|
|
||||||
AfterViewInit,
|
|
||||||
HostListener
|
|
||||||
} from '@angular/core';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'ucap-message-editor',
|
|
||||||
templateUrl: './message-editor.component.html',
|
|
||||||
styleUrls: ['./message-editor.component.scss']
|
|
||||||
})
|
|
||||||
export class MessageEditorComponent implements OnInit, AfterViewInit {
|
|
||||||
@ViewChild('fileInput', { static: false })
|
|
||||||
fileInput: ElementRef<HTMLInputElement>;
|
|
||||||
|
|
||||||
@ViewChild('contentArea', { static: true }) contentArea: ElementRef;
|
|
||||||
|
|
||||||
attachFileType: string;
|
|
||||||
|
|
||||||
fileIndex = 0;
|
|
||||||
attachFiles: { file: File; idx: number }[] = [];
|
|
||||||
imageFiles: { file: File; idx: number }[] = [];
|
|
||||||
|
|
||||||
constructor() {}
|
|
||||||
|
|
||||||
ngOnInit() {}
|
|
||||||
|
|
||||||
ngAfterViewInit(): void {
|
|
||||||
// setTimeout(() => {
|
|
||||||
// this.contentArea.nativeElement.focus();
|
|
||||||
// document
|
|
||||||
// .querySelector('#contentArea')
|
|
||||||
// .addEventListener('paste', (event: ClipboardEvent) => {
|
|
||||||
// event.stopPropagation();
|
|
||||||
// event.preventDefault();
|
|
||||||
// const clipboardData = event.clipboardData;
|
|
||||||
// let pastedData = clipboardData.getData('Text');
|
|
||||||
// pastedData = this.convertHtmltoEntity(pastedData);
|
|
||||||
// clipboardData.setData('Text', pastedData);
|
|
||||||
// const div = document.createElement('div');
|
|
||||||
// div.innerHTML = pastedData;
|
|
||||||
// this.attachElementNextFocused(div);
|
|
||||||
// });
|
|
||||||
// document
|
|
||||||
// .querySelector('#contentArea')
|
|
||||||
// .addEventListener('keyup', event => {
|
|
||||||
// if (
|
|
||||||
// document.querySelector('#contentArea').innerHTML === '' ||
|
|
||||||
// document.querySelector('#contentArea').innerHTML === '<br>'
|
|
||||||
// ) {
|
|
||||||
// const div = document.createElement('div');
|
|
||||||
// div.innerHTML = ' <br>';
|
|
||||||
// document.querySelector('#contentArea').innerHTML = '';
|
|
||||||
// this.attachElementNextFocused(div);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// // init..
|
|
||||||
// if (document.querySelector('#contentArea').innerHTML === '') {
|
|
||||||
// const div = document.createElement('div');
|
|
||||||
// div.innerHTML = ' <br>';
|
|
||||||
// this.attachElementNextFocused(div);
|
|
||||||
// }
|
|
||||||
// }, 700);
|
|
||||||
}
|
|
||||||
|
|
||||||
onClickFileInput(type: string) {
|
|
||||||
this.attachFileType = type;
|
|
||||||
if (type === 'attach') {
|
|
||||||
this.fileInput.nativeElement.setAttribute('multiple', 'true');
|
|
||||||
this.fileInput.nativeElement.setAttribute('accept', '*.*');
|
|
||||||
} else {
|
|
||||||
this.fileInput.nativeElement.removeAttribute('multiple');
|
|
||||||
this.fileInput.nativeElement.setAttribute('accept', 'image/*');
|
|
||||||
}
|
|
||||||
this.fileInput.nativeElement.click();
|
|
||||||
}
|
|
||||||
|
|
||||||
onChangeFileInput() {
|
|
||||||
const files: FileList = this.fileInput.nativeElement.files;
|
|
||||||
|
|
||||||
// tslint:disable-next-line: prefer-for-of
|
|
||||||
for (let i = 0; i < files.length; i++) {
|
|
||||||
if (this.attachFileType === 'attach') {
|
|
||||||
this.attachFiles.push({ file: files[i], idx: this.fileIndex });
|
|
||||||
} else {
|
|
||||||
this.imageFiles.push({ file: files[i], idx: this.fileIndex });
|
|
||||||
// this.addImageInInputField(files[i], this.fileIndex);
|
|
||||||
}
|
|
||||||
this.fileIndex++;
|
|
||||||
}
|
|
||||||
this.fileInput.nativeElement.value = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
getContents(): string {
|
|
||||||
return 'this is contents';
|
|
||||||
}
|
|
||||||
|
|
||||||
// addImageInInputField(image: File, index: number) {
|
|
||||||
// const reader = new FileReader();
|
|
||||||
// reader.readAsDataURL(image);
|
|
||||||
// reader.onloadend = () => {
|
|
||||||
// const img = document.createElement('img');
|
|
||||||
// img.setAttribute('src', reader.result.toString());
|
|
||||||
// img.setAttribute('data-seq', index.toString());
|
|
||||||
|
|
||||||
// this.attachElementNextFocused(img);
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
|
|
||||||
// attachElementNextFocused(el: HTMLElement) {
|
|
||||||
// if (window.getSelection) {
|
|
||||||
// const sel = window.getSelection();
|
|
||||||
// const selAnchorNode: Node = sel.anchorNode;
|
|
||||||
// const focusedEl = selAnchorNode.parentElement;
|
|
||||||
|
|
||||||
// if (this.isParentIdCheck(focusedEl, 'contentArea')) {
|
|
||||||
// if (sel.rangeCount) {
|
|
||||||
// const range = sel.getRangeAt(0);
|
|
||||||
|
|
||||||
// if (selAnchorNode.nodeType === Node.TEXT_NODE) {
|
|
||||||
// const content: string = focusedEl.textContent;
|
|
||||||
|
|
||||||
// focusedEl.innerText =
|
|
||||||
// content.substr(0, range.startOffset) +
|
|
||||||
// el.textContent +
|
|
||||||
// content.substr(range.endOffset);
|
|
||||||
// } else if (
|
|
||||||
// selAnchorNode.nodeType === Node.ELEMENT_NODE &&
|
|
||||||
// focusedEl.id === 'contentArea'
|
|
||||||
// ) {
|
|
||||||
// const selEl = selAnchorNode as HTMLElement;
|
|
||||||
// const content: string = selEl.textContent;
|
|
||||||
|
|
||||||
// selEl.innerText =
|
|
||||||
// content.substr(0, range.startOffset) +
|
|
||||||
// el.textContent +
|
|
||||||
// content.substr(range.endOffset);
|
|
||||||
// } else {
|
|
||||||
// focusedEl.append(el);
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// focusedEl.append(el);
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// this.contentArea.nativeElement.appendChild(el);
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// this.contentArea.nativeElement.appendChild(el);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// isParentIdCheck(el: HTMLElement, id: string): boolean {
|
|
||||||
// if (el.id === id) {
|
|
||||||
// return true;
|
|
||||||
// } else {
|
|
||||||
// if (el.tagName === 'BODY' || el.tagName === 'body') {
|
|
||||||
// return false;
|
|
||||||
// } else {
|
|
||||||
// return this.isParentIdCheck(el.parentElement, id);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
test(): void {
|
|
||||||
if (!!this.contentArea) {
|
|
||||||
const els: HTMLCollection = this.contentArea.nativeElement.children;
|
|
||||||
|
|
||||||
// tslint:disable-next-line: prefer-for-of
|
|
||||||
for (let i = 0; i < els.length; i++) {
|
|
||||||
console.log(this.convertEntitytoHtml(els[i].textContent));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
convertHtmltoEntity(str: string): string {
|
|
||||||
return str
|
|
||||||
.replace(/ /g, ' ')
|
|
||||||
.replace(/</g, '<')
|
|
||||||
.replace(/>/g, '>')
|
|
||||||
.replace(/\n/g, '<br>');
|
|
||||||
}
|
|
||||||
convertEntitytoHtml(str: string): string {
|
|
||||||
return str
|
|
||||||
.replace(/ /g, ' ')
|
|
||||||
.replace(/</g, '<')
|
|
||||||
.replace(/>/g, '>')
|
|
||||||
.replace(/<br>/g, ' \n');
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
<div class="ucap-pick-date-container">
|
||||||
|
<mat-calendar
|
||||||
|
#calendar
|
||||||
|
[selected]="initSelected"
|
||||||
|
(selectedChange)="select($event)"
|
||||||
|
(yearSelected)="_selectYear($event)"
|
||||||
|
(monthSelected)="_selectMonth($event)"
|
||||||
|
>
|
||||||
|
</mat-calendar>
|
||||||
|
</div>
|
|
@ -0,0 +1,4 @@
|
||||||
|
.ucap-pick-date-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
/* tslint:disable:no-unused-variable */
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { DebugElement } from '@angular/core';
|
||||||
|
|
||||||
|
import { PickDateComponent } from './pick-date.component';
|
||||||
|
|
||||||
|
describe('PickDateComponent', () => {
|
||||||
|
let component: PickDateComponent<any>;
|
||||||
|
let fixture: ComponentFixture<PickDateComponent<any>>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [PickDateComponent]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(PickDateComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,59 @@
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
Input,
|
||||||
|
Output,
|
||||||
|
EventEmitter,
|
||||||
|
ViewChild,
|
||||||
|
Optional
|
||||||
|
} from '@angular/core';
|
||||||
|
import { ucapAnimations } from '../animations';
|
||||||
|
import { MatCalendar, DateAdapter } from '@angular/material';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ucap-pick-date',
|
||||||
|
templateUrl: './pick-date.component.html',
|
||||||
|
styleUrls: ['./pick-date.component.scss'],
|
||||||
|
animations: ucapAnimations
|
||||||
|
})
|
||||||
|
export class PickDateComponent<D> implements OnInit {
|
||||||
|
@Input()
|
||||||
|
initSelected: D;
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
readonly selected: EventEmitter<D> = new EventEmitter<D>();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
readonly yearSelected: EventEmitter<D> = new EventEmitter<D>();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
readonly monthSelected: EventEmitter<D> = new EventEmitter<D>();
|
||||||
|
|
||||||
|
@ViewChild('calendar', { static: true })
|
||||||
|
calendar: MatCalendar<D>;
|
||||||
|
|
||||||
|
// tslint:disable-next-line: variable-name
|
||||||
|
constructor(@Optional() private _dateAdapter: DateAdapter<D>) {}
|
||||||
|
|
||||||
|
ngOnInit() {}
|
||||||
|
|
||||||
|
/** Selects the given date */
|
||||||
|
select(date: D): void {
|
||||||
|
const oldValue = this.initSelected;
|
||||||
|
this.initSelected = date;
|
||||||
|
if (!this._dateAdapter.sameDate(oldValue, this.initSelected)) {
|
||||||
|
this.selected.emit(date);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Emits the selected year in multiyear view */
|
||||||
|
_selectYear(normalizedYear: D): void {
|
||||||
|
this.yearSelected.emit(normalizedYear);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Emits selected month in year view */
|
||||||
|
_selectMonth(normalizedMonth: D): void {
|
||||||
|
this.monthSelected.emit(normalizedMonth);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
<div class="ucap-pick-time-container">
|
||||||
|
<span class="hour-input">
|
||||||
|
<ucap-step-input
|
||||||
|
[value]="hour"
|
||||||
|
[min]="0"
|
||||||
|
[max]="23"
|
||||||
|
[step]="hourStep"
|
||||||
|
[paddingZero]="true"
|
||||||
|
[placeholder]="'HH'"
|
||||||
|
(changed)="onChangedHour($event)"
|
||||||
|
></ucap-step-input>
|
||||||
|
</span>
|
||||||
|
<span>:</span>
|
||||||
|
<span class="minute-input">
|
||||||
|
<ucap-step-input
|
||||||
|
[value]="minute"
|
||||||
|
[min]="0"
|
||||||
|
[max]="59"
|
||||||
|
[step]="minuteStep"
|
||||||
|
[paddingZero]="true"
|
||||||
|
[placeholder]="'MM'"
|
||||||
|
(changed)="onChangedMinute($event)"
|
||||||
|
></ucap-step-input
|
||||||
|
></span>
|
||||||
|
</div>
|
|
@ -0,0 +1,15 @@
|
||||||
|
.ucap-pick-time-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
|
||||||
|
|
||||||
|
.hour-input {
|
||||||
|
width: 40px;
|
||||||
|
}
|
||||||
|
.minute-input {
|
||||||
|
width: 40px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
/* tslint:disable:no-unused-variable */
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { DebugElement } from '@angular/core';
|
||||||
|
|
||||||
|
import { PickTimeComponent } from './pick-time.component';
|
||||||
|
|
||||||
|
describe('PickTimeComponent', () => {
|
||||||
|
let component: PickTimeComponent;
|
||||||
|
let fixture: ComponentFixture<PickTimeComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [PickTimeComponent]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(PickTimeComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,47 @@
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
Input,
|
||||||
|
Output,
|
||||||
|
EventEmitter,
|
||||||
|
ViewChild,
|
||||||
|
Optional
|
||||||
|
} from '@angular/core';
|
||||||
|
import { ucapAnimations } from '../animations';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ucap-pick-time',
|
||||||
|
templateUrl: './pick-time.component.html',
|
||||||
|
styleUrls: ['./pick-time.component.scss'],
|
||||||
|
animations: ucapAnimations
|
||||||
|
})
|
||||||
|
export class PickTimeComponent implements OnInit {
|
||||||
|
@Input()
|
||||||
|
hour: number;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
hourStep = 1;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
minute: number;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
minuteStep = 1;
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
readonly hourSelected: EventEmitter<number> = new EventEmitter<number>();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
readonly minuteSelected: EventEmitter<number> = new EventEmitter<number>();
|
||||||
|
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
ngOnInit() {}
|
||||||
|
|
||||||
|
onChangedHour(hour: number) {
|
||||||
|
this.hourSelected.emit(hour);
|
||||||
|
}
|
||||||
|
onChangedMinute(minute: number) {
|
||||||
|
this.minuteSelected.emit(minute);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
<mat-button-toggle-group
|
||||||
|
class="ucap-split-button-container"
|
||||||
|
[matMenuTriggerFor]="menu"
|
||||||
|
[disabled]="disabled"
|
||||||
|
#matMenuTrigger="matMenuTrigger"
|
||||||
|
>
|
||||||
|
<mat-button-toggle color="primary" (click)="onClick($event)">
|
||||||
|
<ng-content></ng-content>
|
||||||
|
</mat-button-toggle>
|
||||||
|
|
||||||
|
<mat-button-toggle color="primary" (click)="onClickSplit($event)">
|
||||||
|
<span
|
||||||
|
class="mdi mdi-24px"
|
||||||
|
[class.mdi-menu-up]="splitOpened"
|
||||||
|
[class.mdi-menu-down]="!splitOpened"
|
||||||
|
></span>
|
||||||
|
</mat-button-toggle>
|
||||||
|
</mat-button-toggle-group>
|
|
@ -0,0 +1,5 @@
|
||||||
|
.ucap-split-button-container {
|
||||||
|
.mat-button-toggle-label-content {
|
||||||
|
height: 36px !important;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
/* tslint:disable:no-unused-variable */
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { DebugElement } from '@angular/core';
|
||||||
|
|
||||||
|
import { SplitButtonComponent } from './split-button.component';
|
||||||
|
|
||||||
|
describe('SplitButtonComponent', () => {
|
||||||
|
let component: SplitButtonComponent;
|
||||||
|
let fixture: ComponentFixture<SplitButtonComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [SplitButtonComponent]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(SplitButtonComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,71 @@
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
Input,
|
||||||
|
Output,
|
||||||
|
EventEmitter,
|
||||||
|
ViewChild,
|
||||||
|
OnDestroy,
|
||||||
|
ElementRef,
|
||||||
|
Renderer2
|
||||||
|
} from '@angular/core';
|
||||||
|
import { MatMenuPanel, MatMenuTrigger, MatButton } from '@angular/material';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
import { NGXLogger } from 'ngx-logger';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ucap-split-button',
|
||||||
|
templateUrl: './split-button.component.html',
|
||||||
|
styleUrls: ['./split-button.component.scss']
|
||||||
|
})
|
||||||
|
export class SplitButtonComponent implements OnInit, OnDestroy {
|
||||||
|
@Input()
|
||||||
|
menu: MatMenuPanel;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
disabled: boolean;
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
buttonClick = new EventEmitter<MouseEvent>();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
splitClick = new EventEmitter<MouseEvent>();
|
||||||
|
|
||||||
|
@ViewChild('matMenuTrigger', { static: true })
|
||||||
|
private matMenuTrigger: MatMenuTrigger;
|
||||||
|
|
||||||
|
// tslint:disable-next-line: variable-name
|
||||||
|
private _handleClick: (event: MouseEvent) => void;
|
||||||
|
private menuCloseSubscription: Subscription;
|
||||||
|
private splitOpened = false;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private elementRef: ElementRef<HTMLElement>,
|
||||||
|
private logger: NGXLogger
|
||||||
|
) {}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.menuCloseSubscription = this.menu.close.subscribe(() => {
|
||||||
|
this.matMenuTrigger._handleClick = e => {};
|
||||||
|
this.splitOpened = false;
|
||||||
|
});
|
||||||
|
this._handleClick = this.matMenuTrigger._handleClick;
|
||||||
|
this.matMenuTrigger._handleClick = e => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
if (!!this.menuCloseSubscription) {
|
||||||
|
this.menuCloseSubscription.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onClick(event: MouseEvent) {
|
||||||
|
this.buttonClick.emit(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickSplit(event: MouseEvent) {
|
||||||
|
this.matMenuTrigger._handleClick = this._handleClick;
|
||||||
|
this.splitOpened = true;
|
||||||
|
this.splitClick.emit(event);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
<div
|
||||||
|
class="ucap-step-input-container"
|
||||||
|
[class.ucap-step-input-container--active]="isFocused"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
matInput
|
||||||
|
#input
|
||||||
|
class="stepper-input"
|
||||||
|
[attr.maxlength]="maxLength"
|
||||||
|
[attr.placeholder]="placeholder"
|
||||||
|
[attr.disabled]="disabled"
|
||||||
|
(keydown)="onKeydown($event)"
|
||||||
|
(focus)="onFocus()"
|
||||||
|
(blur)="onBlur()"
|
||||||
|
/>
|
||||||
|
<div class="steppers">
|
||||||
|
<span class="stepper" role="button" (click)="increase()">▲</span>
|
||||||
|
<span class="stepper" role="button" (click)="decrease()">▼</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,56 @@
|
||||||
|
.ucap-step-input-container {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
height: 30px;
|
||||||
|
padding: 0 5px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
&--active {
|
||||||
|
&:after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: -2px;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 1px;
|
||||||
|
background-color: deepskyblue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.stepper-input {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 0 10px 0 0;
|
||||||
|
border: 0;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: inherit;
|
||||||
|
outline: none;
|
||||||
|
text-align: end;
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.steppers {
|
||||||
|
position: absolute;
|
||||||
|
right: 3px;
|
||||||
|
top: -2px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stepper {
|
||||||
|
height: 15px;
|
||||||
|
font-size: 11px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: rgba(0, 0, 0, 0.4);
|
||||||
|
transition: color 0.2s;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: rgba(0, 0, 0, 0.9);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
/* tslint:disable:no-unused-variable */
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { DebugElement } from '@angular/core';
|
||||||
|
|
||||||
|
import { StepInputComponent } from './step-input.component';
|
||||||
|
|
||||||
|
describe('StepInputComponent', () => {
|
||||||
|
let component: StepInputComponent;
|
||||||
|
let fixture: ComponentFixture<StepInputComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [StepInputComponent]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(StepInputComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,189 @@
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
Input,
|
||||||
|
Output,
|
||||||
|
EventEmitter,
|
||||||
|
ViewChild,
|
||||||
|
ElementRef,
|
||||||
|
ChangeDetectorRef,
|
||||||
|
OnChanges,
|
||||||
|
SimpleChanges
|
||||||
|
} from '@angular/core';
|
||||||
|
import { ucapAnimations } from '../animations';
|
||||||
|
import { StringUtil } from '@ucap-webmessenger/core';
|
||||||
|
|
||||||
|
const ChangesVALUE = 'value';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ucap-step-input',
|
||||||
|
templateUrl: './step-input.component.html',
|
||||||
|
styleUrls: ['./step-input.component.scss'],
|
||||||
|
animations: ucapAnimations
|
||||||
|
})
|
||||||
|
export class StepInputComponent implements OnInit, OnChanges {
|
||||||
|
@Input()
|
||||||
|
value = 0;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
readonly maxLength = 2;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
readonly min: number;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
readonly max: number;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
placeholder = '';
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
readonly step = 1;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
disabled: boolean;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
paddingZero: boolean;
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
changed = new EventEmitter<number>();
|
||||||
|
|
||||||
|
@ViewChild('input', { static: true })
|
||||||
|
input: ElementRef<HTMLInputElement>;
|
||||||
|
|
||||||
|
isFocused: boolean;
|
||||||
|
|
||||||
|
private get displayValue() {
|
||||||
|
return this._displayValue;
|
||||||
|
}
|
||||||
|
private set displayValue(v: string) {
|
||||||
|
this._displayValue = v;
|
||||||
|
this.input.nativeElement.value = v;
|
||||||
|
}
|
||||||
|
// tslint:disable-next-line: variable-name
|
||||||
|
private _displayValue: string;
|
||||||
|
|
||||||
|
constructor(private changeDetectorRef: ChangeDetectorRef) {}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.changeValue(this.value, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
|
const valueChanges = changes[ChangesVALUE];
|
||||||
|
|
||||||
|
if (!!valueChanges && !valueChanges.firstChange) {
|
||||||
|
this.changeValue(valueChanges.currentValue, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
increase() {
|
||||||
|
if (!this.disabled) {
|
||||||
|
this.changeValue(this.value + this.step);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
decrease() {
|
||||||
|
if (!this.disabled) {
|
||||||
|
this.changeValue(this.value - this.step);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onKeydown(event: KeyboardEvent) {
|
||||||
|
if (!this.permittedKey(event)) {
|
||||||
|
event.preventDefault();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/^[0-9]$/i.test(event.key)) {
|
||||||
|
this.changeValue(Number(`${String(this.displayValue)}${event.key}`));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (event.key) {
|
||||||
|
case 'ArrowUp':
|
||||||
|
this.increase();
|
||||||
|
return;
|
||||||
|
case 'ArrowDown':
|
||||||
|
this.decrease();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onFocus() {
|
||||||
|
this.isFocused = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
onBlur() {
|
||||||
|
this.isFocused = false;
|
||||||
|
|
||||||
|
const dv = Number(this.input.nativeElement.value);
|
||||||
|
|
||||||
|
if (isNaN(dv)) {
|
||||||
|
this.changeValue(this.min);
|
||||||
|
} else {
|
||||||
|
this.changeValue(dv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private changeValue(v: number, force: boolean = false) {
|
||||||
|
if (!isNaN(v)) {
|
||||||
|
if (this.max < v) {
|
||||||
|
v = this.min;
|
||||||
|
} else if (this.min > v) {
|
||||||
|
v = this.max;
|
||||||
|
}
|
||||||
|
|
||||||
|
v = this.step * Math.round(v / this.step);
|
||||||
|
|
||||||
|
let dv: string;
|
||||||
|
if (this.paddingZero) {
|
||||||
|
dv = StringUtil.zeroFill(v, this.maxLength);
|
||||||
|
} else {
|
||||||
|
dv = String(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (force || v !== this.value) {
|
||||||
|
this.value = v;
|
||||||
|
this.changed.emit(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.input.nativeElement.value !== dv) {
|
||||||
|
this.displayValue = dv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private permittedKey(event: KeyboardEvent): boolean {
|
||||||
|
if (
|
||||||
|
['.', 'Backspace', 'Tab', 'Escape', 'Esc'].some(n => n === event.key) ||
|
||||||
|
// Allow: Ctrl/cmd+A
|
||||||
|
('A' === event.key &&
|
||||||
|
(true === event.ctrlKey || true === event.metaKey)) ||
|
||||||
|
// Allow: Ctrl/cmd+C
|
||||||
|
('C' === event.key &&
|
||||||
|
(true === event.ctrlKey || true === event.metaKey)) ||
|
||||||
|
// Allow: Ctrl/cmd+X
|
||||||
|
('X' === event.key &&
|
||||||
|
(true === event.ctrlKey || true === event.metaKey)) ||
|
||||||
|
// Allow: home, end, left, right, up, down
|
||||||
|
[
|
||||||
|
'Home',
|
||||||
|
'End',
|
||||||
|
'ArrowDown',
|
||||||
|
'Down',
|
||||||
|
'ArrowLeft',
|
||||||
|
'Left',
|
||||||
|
'ArrowRight',
|
||||||
|
'Right',
|
||||||
|
'ArrowUp',
|
||||||
|
'Up'
|
||||||
|
].some(n => n === event.key)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return /^[0-9]$/i.test(event.key);
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,6 +16,9 @@ import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||||
import { MatInputModule } from '@angular/material/input';
|
import { MatInputModule } from '@angular/material/input';
|
||||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
|
import { MatButtonToggleModule } from '@angular/material/button-toggle';
|
||||||
|
import { MatMenuModule } from '@angular/material/menu';
|
||||||
|
import { MatDatepickerModule } from '@angular/material/datepicker';
|
||||||
|
|
||||||
import { DragDropModule } from '@angular/cdk/drag-drop';
|
import { DragDropModule } from '@angular/cdk/drag-drop';
|
||||||
|
|
||||||
|
@ -23,6 +26,11 @@ import { FileUploadQueueComponent } from './components/file-upload-queue.compone
|
||||||
import { FloatActionButtonComponent } from './components/float-action-button.component';
|
import { FloatActionButtonComponent } from './components/float-action-button.component';
|
||||||
import { FileViewerComponent } from './components/file-viewer.component';
|
import { FileViewerComponent } from './components/file-viewer.component';
|
||||||
import { ExpansionPanelComponent } from './components/expansion-panel.component';
|
import { ExpansionPanelComponent } from './components/expansion-panel.component';
|
||||||
|
import { SplitButtonComponent } from './components/split-button.component';
|
||||||
|
import { PickDateComponent } from './components/pick-date.component';
|
||||||
|
import { PickTimeComponent } from './components/pick-time.component';
|
||||||
|
import { StepInputComponent } from './components/step-input.component';
|
||||||
|
import { StickerSelectorComponent } from './components/sticker-selector.component';
|
||||||
|
|
||||||
import { BinaryViewerComponent } from './components/file-viewer/binary-viewer.component';
|
import { BinaryViewerComponent } from './components/file-viewer/binary-viewer.component';
|
||||||
import { DocumentViewerComponent } from './components/file-viewer/document-viewer.component';
|
import { DocumentViewerComponent } from './components/file-viewer/document-viewer.component';
|
||||||
|
@ -53,8 +61,7 @@ import {
|
||||||
} from './pipes/dates.pipe';
|
} from './pipes/dates.pipe';
|
||||||
import { SecondsToMinutesPipe } from './pipes/seconds-to-minutes.pipe';
|
import { SecondsToMinutesPipe } from './pipes/seconds-to-minutes.pipe';
|
||||||
import { LinkyPipe } from './pipes/linky.pipe';
|
import { LinkyPipe } from './pipes/linky.pipe';
|
||||||
import { StickerSelectorComponent } from './components/sticker-selector.component';
|
|
||||||
import { MessageEditorComponent } from './components/message-editor.component';
|
|
||||||
import { MatTabsModule } from '@angular/material';
|
import { MatTabsModule } from '@angular/material';
|
||||||
|
|
||||||
const COMPONENTS = [
|
const COMPONENTS = [
|
||||||
|
@ -63,7 +70,10 @@ const COMPONENTS = [
|
||||||
FileViewerComponent,
|
FileViewerComponent,
|
||||||
ExpansionPanelComponent,
|
ExpansionPanelComponent,
|
||||||
StickerSelectorComponent,
|
StickerSelectorComponent,
|
||||||
MessageEditorComponent,
|
SplitButtonComponent,
|
||||||
|
PickDateComponent,
|
||||||
|
PickTimeComponent,
|
||||||
|
StepInputComponent,
|
||||||
|
|
||||||
BinaryViewerComponent,
|
BinaryViewerComponent,
|
||||||
DocumentViewerComponent,
|
DocumentViewerComponent,
|
||||||
|
@ -113,6 +123,9 @@ const SERVICES = [
|
||||||
MatTooltipModule,
|
MatTooltipModule,
|
||||||
MatTabsModule,
|
MatTabsModule,
|
||||||
MatFormFieldModule,
|
MatFormFieldModule,
|
||||||
|
MatButtonToggleModule,
|
||||||
|
MatMenuModule,
|
||||||
|
MatDatepickerModule,
|
||||||
DragDropModule
|
DragDropModule
|
||||||
],
|
],
|
||||||
exports: [...COMPONENTS, ...DIRECTIVES, ...PIPES],
|
exports: [...COMPONENTS, ...DIRECTIVES, ...PIPES],
|
||||||
|
|
|
@ -9,9 +9,15 @@ export * from './lib/components/file-viewer/image-viewer.component';
|
||||||
export * from './lib/components/file-viewer/sound-viewer.component';
|
export * from './lib/components/file-viewer/sound-viewer.component';
|
||||||
export * from './lib/components/file-viewer/video-viewer.component';
|
export * from './lib/components/file-viewer/video-viewer.component';
|
||||||
|
|
||||||
|
export * from './lib/components/expansion-panel.component';
|
||||||
export * from './lib/components/file-upload-queue.component';
|
export * from './lib/components/file-upload-queue.component';
|
||||||
export * from './lib/components/file-viewer.component';
|
export * from './lib/components/file-viewer.component';
|
||||||
export * from './lib/components/float-action-button.component';
|
export * from './lib/components/float-action-button.component';
|
||||||
|
export * from './lib/components/pick-date.component';
|
||||||
|
export * from './lib/components/pick-time.component';
|
||||||
|
export * from './lib/components/step-input.component';
|
||||||
|
export * from './lib/components/split-button.component';
|
||||||
|
export * from './lib/components/sticker-selector.component';
|
||||||
|
|
||||||
export * from './lib/data-source/virtual-scroll-tree-flat.data-source';
|
export * from './lib/data-source/virtual-scroll-tree-flat.data-source';
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user