/**
* @license
* Copyright Akveo. All Rights Reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*/
import {
Component,
Input,
HostBinding,
ViewChild,
ElementRef,
ContentChildren,
QueryList,
AfterViewInit,
ContentChild,
SimpleChanges,
AfterContentInit,
OnChanges,
} from '@angular/core';
import { NbStatusService } from '../../services/status.service';
import { NbComponentSize } from '../component-size';
import { NbComponentOrCustomStatus } from '../component-status';
import { convertToBoolProperty, NbBooleanInput } from '../helpers';
import { NbChatFormComponent } from './chat-form.component';
import { NbChatMessageComponent } from './chat-message.component';
/**
* Conversational UI collection - a set of components for chat-like UI construction.
*
* Main features:
* - different message types support (text, image, file, file group, map, etc)
* - drag & drop for images and files with preview
* - different UI styles
* - custom action buttons (coming soon)
*
* Here's a complete example build in a bot-like app. Type `help` to be able to receive different message types.
* Enjoy the conversation and the beautiful UI.
* @stacked-example(Showcase, chat/chat-showcase.component)
*
* Basic chat configuration and usage:
* ```ts
*
*
*
*
*
*
*
* ```
* ### Installation
*
* Import `NbChatModule` to your feature module.
* ```ts
* @NgModule({
* imports: [
* // ...
* NbChatModule,
* ],
* })
* export class PageModule { }
* ```
*
* If you need to provide an API key for a `map` message type (which is required by Google Maps)
* you may use `NbChatModule.forRoot({ ... })` call if this is a global app configuration
* or `NbChatModule.forChild({ ... })` for a feature module configuration:
*
* ```ts
* @NgModule({
* imports: [
* // ...
* NbChatModule.forRoot({ messageGoogleMapKey: 'MAP_KEY' }),
* ],
* })
* export class AppModule { }
* ```
*
* ### Usage
*
* There are three main components:
* ```ts
*
* // chat container
*
*
* // chat form with drag&drop files feature
*
*
* // chat message, available multiple types
* ```
*
* Two users conversation showcase:
* @stacked-example(Conversation, chat/chat-conversation-showcase.component)
*
* Chat UI is also available in different colors by specifying a `[status]` input:
*
* @stacked-example(Colored Chat, chat/chat-colors.component)
*
* Also it is possible to configure sizes through `[size]` input:
*
* @stacked-example(Chat Sizes, chat/chat-sizes.component)
*
* @styles
*
* chat-background-color:
* chat-border:
* chat-border-radius:
* chat-shadow:
* chat-padding:
* chat-scrollbar-color:
* chat-scrollbar-background-color:
* chat-scrollbar-width:
* chat-text-color:
* chat-text-font-family:
* chat-text-font-size:
* chat-text-font-weight:
* chat-text-line-height:
* chat-header-text-font-family:
* chat-header-text-font-size:
* chat-header-text-font-weight:
* chat-header-text-line-height:
* chat-tiny-height:
* chat-small-height:
* chat-medium-height:
* chat-large-height:
* chat-giant-height:
* chat-basic-background-color:
* chat-basic-text-color:
* chat-primary-background-color:
* chat-primary-text-color:
* chat-success-background-color:
* chat-success-text-color:
* chat-info-background-color:
* chat-info-text-color:
* chat-warning-background-color:
* chat-warning-text-color:
* chat-danger-background-color:
* chat-danger-text-color:
* chat-control-background-color:
* chat-control-text-color:
* chat-divider-color:
* chat-divider-style:
* chat-divider-width:
* chat-message-background:
* chat-message-text-color:
* chat-message-reply-background-color:
* chat-message-reply-text-color:
* chat-message-avatar-background-color:
* chat-message-sender-text-color:
* chat-message-quote-background-color:
* chat-message-quote-text-color:
* chat-message-file-text-color:
* chat-message-file-background-color:
*/
@Component({
selector: 'nb-chat',
styleUrls: ['./chat.component.scss'],
template: `
`,
})
export class NbChatComponent implements OnChanges, AfterContentInit, AfterViewInit {
@Input() title: string;
/**
* Chat size, available sizes:
* `tiny`, `small`, `medium`, `large`, `giant`
*/
@Input() size: NbComponentSize;
/**
* Chat status color (adds specific styles):
* `basic` (default), `primary`, `success`, `info`, `warning`, `danger`, `control`.
*/
@Input() status: NbComponentOrCustomStatus = 'basic';
@Input() noMessagesPlaceholder: string = 'No messages yet.';
/**
* Scroll chat to the bottom of the list when a new message arrives
*/
@Input()
get scrollBottom(): boolean {
return this._scrollBottom
}
set scrollBottom(value: boolean) {
this._scrollBottom = convertToBoolProperty(value);
}
protected _scrollBottom: boolean = true;
static ngAcceptInputType_scrollBottom: NbBooleanInput;
@ViewChild('scrollable') scrollable: ElementRef;
@ContentChildren(NbChatMessageComponent) messages: QueryList;
@ContentChild(NbChatFormComponent) chatForm: NbChatFormComponent;
constructor(protected statusService: NbStatusService) {
}
ngOnChanges(changes: SimpleChanges) {
if ('status' in changes) {
this.updateFormStatus();
}
}
ngAfterContentInit() {
this.updateFormStatus();
}
ngAfterViewInit() {
this.messages.changes
.subscribe((messages) => {
this.messages = messages;
this.updateView();
});
this.updateView();
}
updateView() {
if (this.scrollBottom) {
this.scrollListBottom();
}
}
scrollListBottom() {
this.scrollable.nativeElement.scrollTop = this.scrollable.nativeElement.scrollHeight;
}
protected updateFormStatus(): void {
if (this.chatForm) {
this.chatForm.setStatus(this.status);
}
}
@HostBinding('class.size-tiny')
get tiny(): boolean {
return this.size === 'tiny';
}
@HostBinding('class.size-small')
get small(): boolean {
return this.size === 'small';
}
@HostBinding('class.size-medium')
get medium(): boolean {
return this.size === 'medium';
}
@HostBinding('class.size-large')
get large(): boolean {
return this.size === 'large';
}
@HostBinding('class.size-giant')
get giant(): boolean {
return this.size === 'giant';
}
@HostBinding('class.status-primary')
get primary(): boolean {
return this.status === 'primary';
}
@HostBinding('class.status-success')
get success(): boolean {
return this.status === 'success';
}
@HostBinding('class.status-info')
get info(): boolean {
return this.status === 'info';
}
@HostBinding('class.status-warning')
get warning(): boolean {
return this.status === 'warning';
}
@HostBinding('class.status-danger')
get danger(): boolean {
return this.status === 'danger';
}
@HostBinding('class.status-basic')
get basic(): boolean {
return this.status === 'basic';
}
@HostBinding('class.status-control')
get control(): boolean {
return this.status === 'control';
}
@HostBinding('class')
get additionalClasses(): string[] {
if (this.statusService.isCustomStatus(this.status)) {
return [this.statusService.getStatusClass(this.status)];
}
return [];
}
}