diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-side.component.ts b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-side.component.ts
index f3bf9f58..b031b7af 100644
--- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-side.component.ts
+++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-side.component.ts
@@ -221,6 +221,8 @@ export class LeftSideComponent implements OnInit, OnDestroy {
>(MessageWriteDialogComponent, {
width: '600px',
height: '600px',
+ disableClose: true,
+ hasBackdrop: false,
data: {
loginRes: this.loginRes,
environmentsInfo: this.environmentsInfo
diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/message/message-write.dialog.component.html b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/message/message-write.dialog.component.html
index 8a9c93cd..16eed9f2 100644
--- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/message/message-write.dialog.component.html
+++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/message/message-write.dialog.component.html
@@ -9,6 +9,7 @@
#messageWrite
(send)="onSend($event)"
(selectReceiver)="onSelectReceiver($event)"
+ (cancel)="onCancel()"
>
diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/message/message-write.dialog.component.ts b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/message/message-write.dialog.component.ts
index d24ada79..24238923 100644
--- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/message/message-write.dialog.component.ts
+++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/message/message-write.dialog.component.ts
@@ -98,14 +98,34 @@ export class MessageWriteDialogComponent implements OnInit {
.pipe(take(1))
.subscribe(
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 => {
- this.logger.debug('onSend', error);
+ this.snackBarService.open(`쪽지를 전송에 실패 하였습니다.`, '', {
+ duration: 3000,
+ verticalPosition: 'bottom'
+ });
+ this.dialogRef.close({});
}
);
}
+ onCancel() {
+ this.dialogRef.close({});
+ }
+
getBtnValid() {
return true;
}
diff --git a/projects/ucap-webmessenger-app/src/assets/fonts/materialdesignicons/scss/_extras.scss b/projects/ucap-webmessenger-app/src/assets/fonts/materialdesignicons/scss/_extras.scss
index bebf66ff..2e178c90 100644
--- a/projects/ucap-webmessenger-app/src/assets/fonts/materialdesignicons/scss/_extras.scss
+++ b/projects/ucap-webmessenger-app/src/assets/fonts/materialdesignicons/scss/_extras.scss
@@ -1,4 +1,4 @@
-$mdi-sizes: 18 24 36 48;
+$mdi-sizes: 18 20 24 36 48;
@each $mdi-size in $mdi-sizes {
.#{$mdi-css-prefix}-#{$mdi-size}px {
&.#{$mdi-css-prefix}-set,
diff --git a/projects/ucap-webmessenger-app/src/assets/scss/global/_default.scss b/projects/ucap-webmessenger-app/src/assets/scss/global/_default.scss
index 0dc5dba1..46c33237 100644
--- a/projects/ucap-webmessenger-app/src/assets/scss/global/_default.scss
+++ b/projects/ucap-webmessenger-app/src/assets/scss/global/_default.scss
@@ -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 {
overflow: hidden;
padding: 0px;
diff --git a/projects/ucap-webmessenger-ui-message/src/lib/components/write.component.html b/projects/ucap-webmessenger-ui-message/src/lib/components/write.component.html
index f17172ea..2199f21f 100644
--- a/projects/ucap-webmessenger-ui-message/src/lib/components/write.component.html
+++ b/projects/ucap-webmessenger-ui-message/src/lib/components/write.component.html
@@ -52,17 +52,6 @@
-
-
-
-
-
-
-
-
-
+
diff --git a/projects/ucap-webmessenger-ui-message/src/lib/components/write.component.ts b/projects/ucap-webmessenger-ui-message/src/lib/components/write.component.ts
index d4d8ddbb..05b17b8c 100644
--- a/projects/ucap-webmessenger-ui-message/src/lib/components/write.component.ts
+++ b/projects/ucap-webmessenger-ui-message/src/lib/components/write.component.ts
@@ -10,9 +10,12 @@ import {
ElementRef
} from '@angular/core';
-import { ucapAnimations } from '@ucap-webmessenger/ui';
+import { ucapAnimations, DialogService } from '@ucap-webmessenger/ui';
import { NGXLogger } from 'ngx-logger';
+
+import moment from 'moment';
+
import { FileUtil } from '@ucap-webmessenger/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import {
@@ -22,6 +25,11 @@ import {
} from '@ucap-webmessenger/api-message';
import { FileUploadItem } from '@ucap-webmessenger/api';
import { UserInfo } from '@ucap-webmessenger/protocol-sync';
+import {
+ ScheduleSendDialogComponent,
+ ScheduleSendDialogData,
+ ScheduleSendDialogResult
+} from '../dialogs/schedule-send.dialog.component';
const ATTR_FILE = 'UCAP_ATTR_FILE';
@@ -53,6 +61,9 @@ export class WriteComponent implements OnInit, OnDestroy, AfterViewInit {
@Output()
send = new EventEmitter();
+ @Output()
+ cancel = new EventEmitter();
+
@Output()
selectReceiver = new EventEmitter();
@@ -69,6 +80,7 @@ export class WriteComponent implements OnInit, OnDestroy, AfterViewInit {
constructor(
private formBuilder: FormBuilder,
+ private dialogService: DialogService,
private changeDetectorRef: ChangeDetectorRef,
private logger: NGXLogger
) {}
@@ -147,6 +159,31 @@ export class WriteComponent implements OnInit, OnDestroy, AfterViewInit {
}
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();
if (!contentList || 0 === contentList.length) {
return;
@@ -184,18 +221,19 @@ export class WriteComponent implements OnInit, OnDestroy, AfterViewInit {
this.send.emit({
category: CategoryType.General,
- type: MessageType.Send,
+ type: !!reservationDate ? MessageType.Reservation : MessageType.Send,
title,
listOrder,
textContent,
recvUserList,
+ reservationTime: !!reservationDate
+ ? `${reservationDate.format('YYYY-MM-DD HH:mm')}:00`
+ : undefined,
files,
fileUploadItem: this.fileUploadItem
});
}
- onClickCancel() {}
-
private generateContent(): Content[] {
const contentList: Content[] = [];
diff --git a/projects/ucap-webmessenger-ui-message/src/lib/dialogs/schedule-send.dialog.component.html b/projects/ucap-webmessenger-ui-message/src/lib/dialogs/schedule-send.dialog.component.html
new file mode 100644
index 00000000..29cafcd3
--- /dev/null
+++ b/projects/ucap-webmessenger-ui-message/src/lib/dialogs/schedule-send.dialog.component.html
@@ -0,0 +1,114 @@
+
+
+
+ 예약 발송
+
+
+
+
+
+
+
+
+
+ {{ selectedDate.format('MMM DD YYYY, HH:mm') }}
+
+
+
+
+
+
+ 1시간 뒤
+
+
+
+
+ 2시간 뒤
+
+
+
+
+ 내일 아침
+
+
+
+
+ 내일 오후
+
+
+
+
+ 일주일 뒤
+
+
+
+
+ 한달 뒤
+
+
+
+
+
+
+
+
+
+
+
+
+ 취소
+
+
+ 예약 발송
+
+
+
+
diff --git a/projects/ucap-webmessenger-ui-message/src/lib/dialogs/schedule-send.dialog.component.scss b/projects/ucap-webmessenger-ui-message/src/lib/dialogs/schedule-send.dialog.component.scss
new file mode 100644
index 00000000..b5636aec
--- /dev/null
+++ b/projects/ucap-webmessenger-ui-message/src/lib/dialogs/schedule-send.dialog.component.scss
@@ -0,0 +1,3 @@
+.preset-button {
+ width: 100%;
+}
diff --git a/projects/ucap-webmessenger-ui-message/src/lib/dialogs/schedule-send.dialog.component.spec.ts b/projects/ucap-webmessenger-ui-message/src/lib/dialogs/schedule-send.dialog.component.spec.ts
new file mode 100644
index 00000000..d852b16b
--- /dev/null
+++ b/projects/ucap-webmessenger-ui-message/src/lib/dialogs/schedule-send.dialog.component.spec.ts
@@ -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;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ScheduleSendDialogComponent]
+ }).compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(ScheduleSendDialogComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/projects/ucap-webmessenger-ui-message/src/lib/dialogs/schedule-send.dialog.component.ts b/projects/ucap-webmessenger-ui-message/src/lib/dialogs/schedule-send.dialog.component.ts
new file mode 100644
index 00000000..accde7e4
--- /dev/null
+++ b/projects/ucap-webmessenger-ui-message/src/lib/dialogs/schedule-send.dialog.component.ts
@@ -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({});
+ }
+}
diff --git a/projects/ucap-webmessenger-ui-message/src/lib/ucap-ui-message.module.ts b/projects/ucap-webmessenger-ui-message/src/lib/ucap-ui-message.module.ts
index 98148b38..4aede558 100644
--- a/projects/ucap-webmessenger-ui-message/src/lib/ucap-ui-message.module.ts
+++ b/projects/ucap-webmessenger-ui-message/src/lib/ucap-ui-message.module.ts
@@ -18,6 +18,9 @@ import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
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 { PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
@@ -27,8 +30,10 @@ import { UCapUiModule } from '@ucap-webmessenger/ui';
import { ListItemComponent } from './components/list-item.component';
import { WriteComponent } from './components/write.component';
+import { ScheduleSendDialogComponent } from './dialogs/schedule-send.dialog.component';
+
const COMPONENTS = [ListItemComponent, WriteComponent];
-const DIALOGS = [];
+const DIALOGS = [ScheduleSendDialogComponent];
const DIRECTIVES = [];
const SERVICES = [];
@@ -53,6 +58,9 @@ const SERVICES = [];
MatListModule,
MatMomentDateModule,
+ MatMenuModule,
+ MatButtonToggleModule,
+
PerfectScrollbarModule,
UCapUiModule
diff --git a/projects/ucap-webmessenger-ui-message/src/public-api.ts b/projects/ucap-webmessenger-ui-message/src/public-api.ts
index a458dd76..fbb8ef71 100644
--- a/projects/ucap-webmessenger-ui-message/src/public-api.ts
+++ b/projects/ucap-webmessenger-ui-message/src/public-api.ts
@@ -2,6 +2,8 @@
* Public API Surface of ucap-webmessenger-ui-message
*/
+export * from './lib/components/list-item.component';
export * from './lib/components/write.component';
+export * from './lib/dialogs/schedule-send.dialog.component';
export * from './lib/ucap-ui-message.module';
diff --git a/projects/ucap-webmessenger-ui/src/lib/components/message-editor.component.html b/projects/ucap-webmessenger-ui/src/lib/components/message-editor.component.html
deleted file mode 100644
index 6c3813ff..00000000
--- a/projects/ucap-webmessenger-ui/src/lib/components/message-editor.component.html
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
-
-
- test
-
-
-
diff --git a/projects/ucap-webmessenger-ui/src/lib/components/message-editor.component.scss b/projects/ucap-webmessenger-ui/src/lib/components/message-editor.component.scss
deleted file mode 100644
index cb4a5c84..00000000
--- a/projects/ucap-webmessenger-ui/src/lib/components/message-editor.component.scss
+++ /dev/null
@@ -1,9 +0,0 @@
-.container {
- height: 500px;
- overflow: auto;
-
- .editor {
- height: 100%;
- width: 100%;
- }
-}
diff --git a/projects/ucap-webmessenger-ui/src/lib/components/message-editor.component.spec.ts b/projects/ucap-webmessenger-ui/src/lib/components/message-editor.component.spec.ts
deleted file mode 100644
index cafed007..00000000
--- a/projects/ucap-webmessenger-ui/src/lib/components/message-editor.component.spec.ts
+++ /dev/null
@@ -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;
-
- beforeEach(async(() => {
- TestBed.configureTestingModule({
- declarations: [ MessageEditorComponent ]
- })
- .compileComponents();
- }));
-
- beforeEach(() => {
- fixture = TestBed.createComponent(MessageEditorComponent);
- component = fixture.componentInstance;
- fixture.detectChanges();
- });
-
- it('should create', () => {
- expect(component).toBeTruthy();
- });
-});
diff --git a/projects/ucap-webmessenger-ui/src/lib/components/message-editor.component.ts b/projects/ucap-webmessenger-ui/src/lib/components/message-editor.component.ts
deleted file mode 100644
index ec1baf54..00000000
--- a/projects/ucap-webmessenger-ui/src/lib/components/message-editor.component.ts
+++ /dev/null
@@ -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;
-
- @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 === '
'
- // ) {
- // const div = document.createElement('div');
- // div.innerHTML = '
';
- // document.querySelector('#contentArea').innerHTML = '';
- // this.attachElementNextFocused(div);
- // }
- // });
- // // init..
- // if (document.querySelector('#contentArea').innerHTML === '') {
- // const div = document.createElement('div');
- // div.innerHTML = '
';
- // 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(/\n/g, '
');
- }
- convertEntitytoHtml(str: string): string {
- return str
- .replace(/ /g, ' ')
- .replace(/</g, '<')
- .replace(/>/g, '>')
- .replace(/
/g, ' \n');
- }
-}
diff --git a/projects/ucap-webmessenger-ui/src/lib/components/pick-date.component.html b/projects/ucap-webmessenger-ui/src/lib/components/pick-date.component.html
new file mode 100644
index 00000000..6deec282
--- /dev/null
+++ b/projects/ucap-webmessenger-ui/src/lib/components/pick-date.component.html
@@ -0,0 +1,10 @@
+
+
+
+
diff --git a/projects/ucap-webmessenger-ui/src/lib/components/pick-date.component.scss b/projects/ucap-webmessenger-ui/src/lib/components/pick-date.component.scss
new file mode 100644
index 00000000..5143d639
--- /dev/null
+++ b/projects/ucap-webmessenger-ui/src/lib/components/pick-date.component.scss
@@ -0,0 +1,4 @@
+.ucap-pick-date-container {
+ width: 100%;
+ height: 100%;
+}
diff --git a/projects/ucap-webmessenger-ui/src/lib/components/pick-date.component.spec.ts b/projects/ucap-webmessenger-ui/src/lib/components/pick-date.component.spec.ts
new file mode 100644
index 00000000..47d0b420
--- /dev/null
+++ b/projects/ucap-webmessenger-ui/src/lib/components/pick-date.component.spec.ts
@@ -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;
+ let fixture: ComponentFixture>;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [PickDateComponent]
+ }).compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PickDateComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/projects/ucap-webmessenger-ui/src/lib/components/pick-date.component.ts b/projects/ucap-webmessenger-ui/src/lib/components/pick-date.component.ts
new file mode 100644
index 00000000..c936a745
--- /dev/null
+++ b/projects/ucap-webmessenger-ui/src/lib/components/pick-date.component.ts
@@ -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 implements OnInit {
+ @Input()
+ initSelected: D;
+
+ @Output()
+ readonly selected: EventEmitter = new EventEmitter();
+
+ @Output()
+ readonly yearSelected: EventEmitter = new EventEmitter();
+
+ @Output()
+ readonly monthSelected: EventEmitter = new EventEmitter();
+
+ @ViewChild('calendar', { static: true })
+ calendar: MatCalendar;
+
+ // tslint:disable-next-line: variable-name
+ constructor(@Optional() private _dateAdapter: DateAdapter) {}
+
+ 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);
+ }
+}
diff --git a/projects/ucap-webmessenger-ui/src/lib/components/pick-time.component.html b/projects/ucap-webmessenger-ui/src/lib/components/pick-time.component.html
new file mode 100644
index 00000000..92f24120
--- /dev/null
+++ b/projects/ucap-webmessenger-ui/src/lib/components/pick-time.component.html
@@ -0,0 +1,25 @@
+
+
+
+
+ :
+
+
+
diff --git a/projects/ucap-webmessenger-ui/src/lib/components/pick-time.component.scss b/projects/ucap-webmessenger-ui/src/lib/components/pick-time.component.scss
new file mode 100644
index 00000000..260c3809
--- /dev/null
+++ b/projects/ucap-webmessenger-ui/src/lib/components/pick-time.component.scss
@@ -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;
+ }
+}
diff --git a/projects/ucap-webmessenger-ui/src/lib/components/pick-time.component.spec.ts b/projects/ucap-webmessenger-ui/src/lib/components/pick-time.component.spec.ts
new file mode 100644
index 00000000..9e3404a1
--- /dev/null
+++ b/projects/ucap-webmessenger-ui/src/lib/components/pick-time.component.spec.ts
@@ -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;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [PickTimeComponent]
+ }).compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PickTimeComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/projects/ucap-webmessenger-ui/src/lib/components/pick-time.component.ts b/projects/ucap-webmessenger-ui/src/lib/components/pick-time.component.ts
new file mode 100644
index 00000000..f6ac40c5
--- /dev/null
+++ b/projects/ucap-webmessenger-ui/src/lib/components/pick-time.component.ts
@@ -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 = new EventEmitter();
+
+ @Output()
+ readonly minuteSelected: EventEmitter = new EventEmitter();
+
+ constructor() {}
+
+ ngOnInit() {}
+
+ onChangedHour(hour: number) {
+ this.hourSelected.emit(hour);
+ }
+ onChangedMinute(minute: number) {
+ this.minuteSelected.emit(minute);
+ }
+}
diff --git a/projects/ucap-webmessenger-ui/src/lib/components/split-button.component.html b/projects/ucap-webmessenger-ui/src/lib/components/split-button.component.html
new file mode 100644
index 00000000..3f57c581
--- /dev/null
+++ b/projects/ucap-webmessenger-ui/src/lib/components/split-button.component.html
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
diff --git a/projects/ucap-webmessenger-ui/src/lib/components/split-button.component.scss b/projects/ucap-webmessenger-ui/src/lib/components/split-button.component.scss
new file mode 100644
index 00000000..a1624ec3
--- /dev/null
+++ b/projects/ucap-webmessenger-ui/src/lib/components/split-button.component.scss
@@ -0,0 +1,5 @@
+.ucap-split-button-container {
+ .mat-button-toggle-label-content {
+ height: 36px !important;
+ }
+}
diff --git a/projects/ucap-webmessenger-ui/src/lib/components/split-button.component.spec.ts b/projects/ucap-webmessenger-ui/src/lib/components/split-button.component.spec.ts
new file mode 100644
index 00000000..820d0ab1
--- /dev/null
+++ b/projects/ucap-webmessenger-ui/src/lib/components/split-button.component.spec.ts
@@ -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;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [SplitButtonComponent]
+ }).compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(SplitButtonComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/projects/ucap-webmessenger-ui/src/lib/components/split-button.component.ts b/projects/ucap-webmessenger-ui/src/lib/components/split-button.component.ts
new file mode 100644
index 00000000..259498ad
--- /dev/null
+++ b/projects/ucap-webmessenger-ui/src/lib/components/split-button.component.ts
@@ -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();
+
+ @Output()
+ splitClick = new EventEmitter();
+
+ @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,
+ 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);
+ }
+}
diff --git a/projects/ucap-webmessenger-ui/src/lib/components/step-input.component.html b/projects/ucap-webmessenger-ui/src/lib/components/step-input.component.html
new file mode 100644
index 00000000..823f0ece
--- /dev/null
+++ b/projects/ucap-webmessenger-ui/src/lib/components/step-input.component.html
@@ -0,0 +1,20 @@
+
diff --git a/projects/ucap-webmessenger-ui/src/lib/components/step-input.component.scss b/projects/ucap-webmessenger-ui/src/lib/components/step-input.component.scss
new file mode 100644
index 00000000..bac472c4
--- /dev/null
+++ b/projects/ucap-webmessenger-ui/src/lib/components/step-input.component.scss
@@ -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);
+ }
+ }
+}
diff --git a/projects/ucap-webmessenger-ui/src/lib/components/step-input.component.spec.ts b/projects/ucap-webmessenger-ui/src/lib/components/step-input.component.spec.ts
new file mode 100644
index 00000000..944ff807
--- /dev/null
+++ b/projects/ucap-webmessenger-ui/src/lib/components/step-input.component.spec.ts
@@ -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;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [StepInputComponent]
+ }).compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(StepInputComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/projects/ucap-webmessenger-ui/src/lib/components/step-input.component.ts b/projects/ucap-webmessenger-ui/src/lib/components/step-input.component.ts
new file mode 100644
index 00000000..05ecaab6
--- /dev/null
+++ b/projects/ucap-webmessenger-ui/src/lib/components/step-input.component.ts
@@ -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();
+
+ @ViewChild('input', { static: true })
+ input: ElementRef;
+
+ 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);
+ }
+}
diff --git a/projects/ucap-webmessenger-ui/src/lib/ucap-ui.module.ts b/projects/ucap-webmessenger-ui/src/lib/ucap-ui.module.ts
index 8ebef4f7..f6855bce 100644
--- a/projects/ucap-webmessenger-ui/src/lib/ucap-ui.module.ts
+++ b/projects/ucap-webmessenger-ui/src/lib/ucap-ui.module.ts
@@ -16,6 +16,9 @@ import { MatSnackBarModule } from '@angular/material/snack-bar';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatInputModule } from '@angular/material/input';
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';
@@ -23,6 +26,11 @@ import { FileUploadQueueComponent } from './components/file-upload-queue.compone
import { FloatActionButtonComponent } from './components/float-action-button.component';
import { FileViewerComponent } from './components/file-viewer.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 { DocumentViewerComponent } from './components/file-viewer/document-viewer.component';
@@ -53,8 +61,7 @@ import {
} from './pipes/dates.pipe';
import { SecondsToMinutesPipe } from './pipes/seconds-to-minutes.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';
const COMPONENTS = [
@@ -63,7 +70,10 @@ const COMPONENTS = [
FileViewerComponent,
ExpansionPanelComponent,
StickerSelectorComponent,
- MessageEditorComponent,
+ SplitButtonComponent,
+ PickDateComponent,
+ PickTimeComponent,
+ StepInputComponent,
BinaryViewerComponent,
DocumentViewerComponent,
@@ -113,6 +123,9 @@ const SERVICES = [
MatTooltipModule,
MatTabsModule,
MatFormFieldModule,
+ MatButtonToggleModule,
+ MatMenuModule,
+ MatDatepickerModule,
DragDropModule
],
exports: [...COMPONENTS, ...DIRECTIVES, ...PIPES],
diff --git a/projects/ucap-webmessenger-ui/src/public-api.ts b/projects/ucap-webmessenger-ui/src/public-api.ts
index 81892d49..db78e22a 100644
--- a/projects/ucap-webmessenger-ui/src/public-api.ts
+++ b/projects/ucap-webmessenger-ui/src/public-api.ts
@@ -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/video-viewer.component';
+export * from './lib/components/expansion-panel.component';
export * from './lib/components/file-upload-queue.component';
export * from './lib/components/file-viewer.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';