diff --git a/docs/app/app.module.ts b/docs/app/app.module.ts
index 1ee5428360..5e2756bb09 100644
--- a/docs/app/app.module.ts
+++ b/docs/app/app.module.ts
@@ -8,6 +8,7 @@ import { InjectionToken, NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { RouterModule } from '@angular/router';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import {
NbThemeModule,
NbSidebarModule,
@@ -29,6 +30,7 @@ const docs = require('../output.json');
@NgModule({
imports: [
BrowserModule,
+ BrowserAnimationsModule,
FormsModule,
HttpClientModule,
NbSidebarModule,
diff --git a/docs/assets/images/components/chat-ui.svg b/docs/assets/images/components/chat-ui.svg
new file mode 100644
index 0000000000..d92b67b76c
--- /dev/null
+++ b/docs/assets/images/components/chat-ui.svg
@@ -0,0 +1,26 @@
+
+
\ No newline at end of file
diff --git a/docs/structure.ts b/docs/structure.ts
index 7bd7852e05..0a66ef0b88 100644
--- a/docs/structure.ts
+++ b/docs/structure.ts
@@ -214,6 +214,16 @@ export const structure = [
'NbRouteTabsetComponent',
],
},
+ {
+ type: 'tabs',
+ name: 'Chat UI',
+ icon: 'chat-ui.svg',
+ source: [
+ 'NbChatComponent',
+ 'NbChatMessageComponent',
+ 'NbChatFormComponent',
+ ],
+ },
{
type: 'tabs',
name: 'Actions',
diff --git a/e2e/chat.e2e-spec.ts b/e2e/chat.e2e-spec.ts
new file mode 100644
index 0000000000..9313f559b5
--- /dev/null
+++ b/e2e/chat.e2e-spec.ts
@@ -0,0 +1,77 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ */
+
+import { browser, element, by } from 'protractor';
+import { colors, chatSizes as sizes } from './component-shared';
+import { waitFor } from './e2e-helper';
+
+let chats: any[] = [];
+
+function prepareChats() {
+ const result: any[] = [];
+
+ let elementNumber: number = 1;
+ for (const { colorKey, color } of colors) {
+ for (const { sizeKey, height } of sizes) {
+ result.push({
+ size: sizeKey,
+ height: height,
+ colorKey,
+ color,
+ elementNumber,
+ });
+ elementNumber++;
+ }
+ }
+
+ return result;
+}
+
+describe('nb-chat', () => {
+
+ chats = prepareChats();
+
+ beforeEach((done) => {
+ browser.get('#/chat/chat-test.component').then(() => done());
+ });
+
+ chats.forEach(c => {
+
+ it(`should display ${c.colorKey} chat with ${c.size} size`, () => {
+ waitFor(`nb-chat:nth-child(${c.elementNumber})`);
+
+ element(by.css(`nb-chat:nth-child(${c.elementNumber})`)).getCssValue('height').then(height => {
+ expect(height).toEqual(c.height);
+ });
+
+ element(by.css(`nb-chat:nth-child(${c.elementNumber}) .header`))
+ .getCssValue('background-color').then(bgColor => {
+ expect(bgColor).toEqual(c.color);
+ });
+ });
+ });
+
+ it('should add on message', () => {
+ const all: any = element.all(by.css('nb-chat:nth-child(1) nb-chat-message'));
+ all.count().then(allCount => {
+ element(by.css('nb-chat:nth-child(1) nb-chat-form input')).sendKeys('akveo');
+ element(by.css('nb-chat:nth-child(1) nb-chat-form .btn')).click();
+ expect(all.count()).toEqual(allCount + 1);
+ });
+ });
+
+ it('should not add on an empty message', () => {
+ const all: any = element.all(by.css('nb-chat:nth-child(1) nb-chat-message'));
+ all.count().then(allCount => {
+ element(by.css('nb-chat:nth-child(1) nb-chat-form .btn')).click();
+ expect(all.count()).toEqual(allCount);
+
+ element(by.css('nb-chat:nth-child(1) nb-chat-form input')).sendKeys(' ');
+ element(by.css('nb-chat:nth-child(1) nb-chat-form .btn')).click();
+ expect(all.count()).toEqual(allCount);
+ });
+ });
+});
diff --git a/e2e/component-shared.ts b/e2e/component-shared.ts
index d4219ed931..b6cffb41d9 100644
--- a/e2e/component-shared.ts
+++ b/e2e/component-shared.ts
@@ -29,3 +29,5 @@ export const colors = [
{ colorKey: 'default', color: hexToRgbA('#a4abb3') },
{ colorKey: 'disabled', color: 'rgba(255, 255, 255, 0.4)' },
];
+
+export const chatSizes = cardSizes;
diff --git a/package-lock.json b/package-lock.json
index 92cc0f7649..c3f9e8d899 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6878,12 +6878,14 @@
"balanced-match": {
"version": "1.0.0",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
"dev": true,
+ "optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -6898,17 +6900,20 @@
"code-point-at": {
"version": "1.1.0",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"concat-map": {
"version": "0.0.1",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"core-util-is": {
"version": "1.0.2",
@@ -7025,7 +7030,8 @@
"inherits": {
"version": "2.0.3",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"ini": {
"version": "1.3.5",
@@ -7037,6 +7043,7 @@
"version": "1.0.0",
"bundled": true,
"dev": true,
+ "optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@@ -7051,6 +7058,7 @@
"version": "3.0.4",
"bundled": true,
"dev": true,
+ "optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
@@ -7058,12 +7066,14 @@
"minimist": {
"version": "0.0.8",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"minipass": {
"version": "2.2.4",
"bundled": true,
"dev": true,
+ "optional": true,
"requires": {
"safe-buffer": "^5.1.1",
"yallist": "^3.0.0"
@@ -7082,6 +7092,7 @@
"version": "0.5.1",
"bundled": true,
"dev": true,
+ "optional": true,
"requires": {
"minimist": "0.0.8"
}
@@ -7162,7 +7173,8 @@
"number-is-nan": {
"version": "1.0.1",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"object-assign": {
"version": "4.1.1",
@@ -7174,6 +7186,7 @@
"version": "1.4.0",
"bundled": true,
"dev": true,
+ "optional": true,
"requires": {
"wrappy": "1"
}
@@ -7295,6 +7308,7 @@
"version": "1.0.2",
"bundled": true,
"dev": true,
+ "optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 0157f2a77a..adb1ed18b0 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -9,15 +9,19 @@ import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { HttpClientModule } from '@angular/common/http';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NbThemeModule } from '@nebular/theme';
import { NbAppComponent } from './app.component';
import { NbLayoutDirectionToggleComponent } from './layout-direction-toggle/layout-direction-toggle.component';
import { NbDynamicToAddComponent } from '../playground/shared/dynamic.component';
import { NbPlaygroundSharedModule } from '../playground/shared/shared.module';
+
+
@NgModule({
imports: [
BrowserModule,
+ BrowserAnimationsModule,
FormsModule,
HttpClientModule,
RouterModule.forRoot([
diff --git a/src/framework/theme/components/chat/_chat.component.theme.scss b/src/framework/theme/components/chat/_chat.component.theme.scss
new file mode 100644
index 0000000000..99b305b5ab
--- /dev/null
+++ b/src/framework/theme/components/chat/_chat.component.theme.scss
@@ -0,0 +1,402 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ */
+
+@mixin nb-chat-theme() {
+
+ nb-chat {
+ font-size: nb-theme(chat-font-size);
+ background: nb-theme(chat-bg);
+ border-radius: nb-theme(chat-border-radius);
+ box-shadow: nb-theme(chat-shadow);
+
+ .header {
+ color: nb-theme(chat-fg-text);
+ padding: nb-theme(chat-padding);
+ border-bottom: 1px solid nb-theme(chat-separator);
+ border-top-left-radius: nb-theme(chat-border-radius);
+ border-top-right-radius: nb-theme(chat-border-radius);
+ font-weight: nb-theme(font-weight-bolder);
+ }
+
+ .scrollable {
+ overflow: auto;
+ flex: 1;
+ @include nb-scrollbars(
+ nb-theme(scrollbar-fg),
+ nb-theme(scrollbar-bg),
+ nb-theme(scrollbar-width));
+ }
+
+ .messages {
+ padding: nb-theme(chat-padding);
+ overflow-y: auto;
+ overflow-x: hidden;
+ display: flex;
+ flex-shrink: 0;
+ flex-direction: column;
+ }
+
+ .no-messages {
+ font-size: 0.875rem;
+ text-align: center;
+ }
+
+ nb-chat-message {
+ margin-bottom: 1.5rem;
+ display: flex;
+ flex-direction: row;
+
+ .message {
+ flex: 1;
+ }
+
+ .avatar {
+ border-radius: 50%;
+ flex-shrink: 0;
+ background: nb-theme(chat-message-avatar-bg);
+ background-position: center;
+ background-size: 3.4rem 2.6rem;
+ background-repeat: no-repeat;
+ width: 2.5rem;
+ height: 2.5rem;
+ text-align: center;
+ line-height: 2.5rem;
+ font-size: 0.875rem;
+ color: white;
+ }
+
+ nb-chat-message-text {
+
+ display: flex;
+ flex-direction: column;
+
+ .sender {
+ font-size: 0.875rem;
+ color: nb-theme(chat-message-sender-fg);
+ margin-bottom: 0.5rem;
+ }
+
+ p {
+ word-wrap: break-word;
+ word-break: break-all;
+ max-width: 100%;
+ margin-bottom: 0;
+ }
+
+ .text {
+ padding: 1rem;
+ border-radius: 0.5rem;
+ }
+ }
+
+ nb-chat-message-file {
+ display: flex;
+ flex-direction: column;
+
+ a {
+ color: nb-theme(chat-message-file-fg);
+ background: nb-theme(chat-message-file-bg);
+ font-size: 4rem;
+ text-align: center;
+ border: 1px solid nb-theme(chat-message-file-fg);
+ width: 10rem;
+ height: 10rem;
+ overflow: hidden;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-shrink: 0;
+ border-radius: 0.5rem;
+ &:hover, &:focus {
+ text-decoration: none;
+ color: nb-theme(chat-message-file-fg);
+ }
+ div {
+ background-size: cover;
+ width: 100%;
+ height: 100%;
+ }
+ }
+
+ nb-chat-message-text {
+ display: block;
+ margin-bottom: 0.5rem;
+ }
+
+ .message-content-group {
+ display: flex;
+ flex-direction: row;
+ justify-content: flex-end;
+ flex-wrap: wrap;
+
+ a {
+ @include nb-ltr(margin-right, 1rem);
+ @include nb-rtl(margin-left, 1rem);
+ margin-bottom: 1rem;
+ width: 5rem;
+ height: 5rem;
+ }
+ }
+ }
+
+ nb-chat-message-quote {
+
+ p.quote {
+ font-style: italic;
+ font-size: 0.875rem;
+ background: nb-theme(chat-message-quote-bg);
+ color: nb-theme(chat-message-quote-fg);
+ padding: 1rem;
+ border-radius: 0.5rem;
+ margin-bottom: 0.5rem;
+ }
+
+ .sender {
+ font-size: 0.875rem;
+ color: nb-theme(chat-message-sender-fg);
+ margin-bottom: 0.5rem;
+ }
+ }
+
+ &.not-reply {
+ .message {
+ @include nb-ltr(margin-left, 0.5rem);
+ @include nb-rtl(margin-right, 0.5rem);
+
+ @include nb-ltr(margin-right, 3rem);
+ @include nb-rtl(margin-left, 3rem);
+ }
+
+ nb-chat-message-text {
+ align-items: flex-start;
+ .text {
+ @include nb-ltr(border-top-left-radius, 0);
+ @include nb-rtl(border-top-right-radius, 0);
+ background: nb-theme(chat-message-bg);
+ color: nb-theme(chat-message-fg);
+ }
+ }
+
+ nb-chat-message-file {
+ align-items: flex-start;
+ }
+ }
+
+ &.reply {
+ flex-direction: row-reverse;
+
+ .message {
+ margin-left: 0;
+
+ @include nb-ltr(margin-right, 0.5rem);
+ @include nb-rtl(margin-left, 0.5rem);
+
+ @include nb-ltr(margin-left, 3rem);
+ @include nb-rtl(margin-right, 3rem);
+ }
+
+ nb-chat-message-text {
+ align-items: flex-end;
+ .sender {
+ @include nb-ltr(text-align, right);
+ @include nb-rtl(text-align, left);
+ }
+
+ .text {
+ @include nb-ltr(border-top-right-radius, 0);
+ @include nb-rtl(border-top-left-radius, 0);
+ background: nb-theme(chat-message-reply-bg);
+ color: nb-theme(chat-message-reply-fg);
+ }
+ }
+
+ nb-chat-message-file {
+ align-items: flex-end;
+ }
+ }
+ }
+
+ nb-chat-form {
+ display: flex;
+ flex-direction: column;
+ padding: nb-theme(chat-padding);
+ border-top: 1px solid nb-theme(chat-separator);
+
+ .message-row {
+ flex-direction: row;
+ display: flex;
+ }
+
+ input {
+ flex: 1;
+ padding: 1.25rem 1.5rem;
+ border-radius: 2rem;
+ border: 1px solid nb-theme(chat-form-border);
+ background: nb-theme(chat-form-bg);
+ color: nb-theme(chat-form-fg);
+ outline: none;
+ box-sizing: border-box;
+
+ &.with-button {
+ border-bottom-right-radius: 0;
+ border-top-right-radius: 0;
+ @include nb-ltr(border-bottom-right-radius, 0);
+ @include nb-ltr(border-top-right-radius, 0);
+ @include nb-rtl(border-bottom-left-radius, 0);
+ @include nb-rtl(border-top-left-radius, 0);
+ }
+
+ &::placeholder {
+ color: nb-theme(chat-form-placeholder-fg);
+ }
+ }
+
+ button.btn {
+ border-radius: 3rem;
+ @include nb-ltr(border-bottom-left-radius, 0);
+ @include nb-ltr(border-top-left-radius, 0);
+ @include nb-rtl(border-bottom-right-radius, 0);
+ @include nb-rtl(border-top-right-radius, 0);
+ padding: 0 1.5rem;
+
+ &.with-icon {
+ font-size: 3rem;
+ line-height: 1;
+ padding: 0 1.25rem 0 0.875rem;
+ text-align: center;
+ }
+ }
+
+ &.file-over input {
+ border: 1px dashed nb-theme(chat-form-active-border);
+ box-shadow: 0 0 0 4px nb-theme(chat-form-bg);
+ &::placeholder {
+ color: nb-theme(chat-form-fg);
+ }
+ }
+
+ .dropped-files {
+ display: flex;
+ flex-direction: row;
+ margin-bottom: 0.5rem;
+ flex-wrap: wrap;
+ div {
+ background-size: cover;
+ width: 3rem;
+ height: 3rem;
+ border-radius: 0.5rem;
+ @include nb-ltr(margin-right, 0.5rem);
+ @include nb-rtl(margin-left, 0.5rem);
+ margin-bottom: 0.5rem;
+ border: 1px solid nb-theme(chat-form-fg);
+ text-align: center;
+ line-height: 3rem;
+ font-size: 2rem;
+ color: nb-theme(chat-form-fg);
+ position: relative;
+
+ .remove {
+ position: absolute;
+ right: -0.5rem;
+ top: -0.875rem;
+ font-size: 0.875rem;
+ line-height: 1;
+ cursor: pointer;
+ }
+ }
+ }
+ }
+
+ &.xxsmall-chat {
+ height: nb-theme(chat-height-xxsmall);
+ }
+ &.xsmall-chat {
+ height: nb-theme(chat-height-xsmall);
+ }
+ &.small-chat {
+ height: nb-theme(chat-height-small);
+ }
+ &.medium-chat {
+ height: nb-theme(chat-height-medium);
+ }
+ &.large-chat {
+ height: nb-theme(chat-height-large);
+ }
+ &.xlarge-chat {
+ height: nb-theme(chat-height-xlarge);
+ }
+ &.xxlarge-chat {
+ height: nb-theme(chat-height-xxlarge);
+ }
+
+ &.active-chat {
+ .header {
+ background-color: nb-theme(chat-active-bg);
+ color: nb-theme(chat-fg);
+ }
+ nb-chat-form button.btn {
+ background-color: nb-theme(chat-active-bg);
+ }
+ }
+ &.disabled-chat {
+ .header {
+ background-color: nb-theme(chat-disabled-bg);
+ color: nb-theme(chat-disabled-fg);
+ }
+ nb-chat-form button.btn {
+ background-color: nb-theme(chat-disabled-bg);
+ border: 1px solid nb-theme(chat-form-border);
+ color: nb-theme(chat-disabled-fg);
+ }
+ }
+ &.primary-chat {
+ .header {
+ background-color: nb-theme(chat-primary-bg);
+ color: nb-theme(chat-fg);
+ }
+ nb-chat-form button.btn {
+ background-color: nb-theme(chat-primary-bg);
+ }
+ }
+ &.info-chat {
+ .header {
+ background-color: nb-theme(chat-info-bg);
+ color: nb-theme(chat-fg);
+ }
+ nb-chat-form button.btn {
+ background-color: nb-theme(chat-info-bg);
+ }
+ }
+ &.success-chat {
+ .header {
+ background-color: nb-theme(chat-success-bg);
+ color: nb-theme(chat-fg);
+ }
+ nb-chat-form button.btn {
+ background-color: nb-theme(chat-success-bg);
+ }
+ }
+ &.warning-chat {
+ .header {
+ background-color: nb-theme(chat-warning-bg);
+ color: nb-theme(chat-fg);
+ }
+ nb-chat-form button.btn {
+ background-color: nb-theme(chat-warning-bg);
+ }
+ }
+ &.danger-chat {
+ .header {
+ background-color: nb-theme(chat-danger-bg);
+ color: nb-theme(chat-fg);
+ }
+ nb-chat-form button.btn {
+ background-color: nb-theme(chat-danger-bg);
+ }
+ }
+ }
+}
+
diff --git a/src/framework/theme/components/chat/chat-form.component.ts b/src/framework/theme/components/chat/chat-form.component.ts
new file mode 100644
index 0000000000..1399b240e2
--- /dev/null
+++ b/src/framework/theme/components/chat/chat-form.component.ts
@@ -0,0 +1,184 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ */
+
+import {
+ ChangeDetectionStrategy,
+ ChangeDetectorRef,
+ Component,
+ EventEmitter,
+ HostBinding,
+ HostListener,
+ Input,
+ Output,
+} from '@angular/core';
+import { DomSanitizer } from '@angular/platform-browser';
+
+/**
+ * Chat form component.
+ *
+ * Show a message form with a send message button.
+ *
+ * ```ts
+ *
+ *
+ * ```
+ *
+ * When `[dropFiles]="true"` handles files drag&drop with a file preview.
+ *
+ * Drag & drop available for files and images:
+ * @stacked-example(Drag & Drop Chat, chat/chat-drop.component)
+ *
+ * New message could be tracked outside by using `(send)` output.
+ *
+ * ```ts
+ *
+ *
+ *
+ * // ...
+ *
+ * onNewMessage({ message: string, files: any[] }) {
+ * this.service.sendToServer(message, files);
+ * }
+ * ```
+ *
+ * @styles
+ *
+ * chat-form-bg:
+ * chat-form-fg:
+ * chat-form-border:
+ * chat-form-active-border:
+ *
+ */
+@Component({
+ selector: 'nb-chat-form',
+ template: `
+
+
+
+
+
+ `,
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class NbChatFormComponent {
+
+ droppedFiles: any[] = [];
+ imgDropTypes = ['image/png', 'image/jpeg', 'image/gif'];
+
+ /**
+ * Predefined message text
+ * @type {string}
+ */
+ @Input() message: string = '';
+
+ /**
+ * Send button title
+ * @type {string}
+ */
+ @Input() buttonTitle: string = '';
+
+ /**
+ * Send button icon, shown if `buttonTitle` is empty
+ * @type {string}
+ */
+ @Input() buttonIcon: string = 'nb-paper-plane';
+
+ /**
+ * Show send button
+ * @type {boolean}
+ */
+ @Input() showButton: boolean = true;
+
+ /**
+ * Show send button
+ * @type {boolean}
+ */
+ @Input() dropFiles: boolean = false;
+
+ /**
+ *
+ * @type {EventEmitter<{ message: string, files: File[] }>}
+ */
+ @Output() send = new EventEmitter<{ message: string, files: File[] }>();
+
+ @HostBinding('class.file-over') fileOver = false;
+
+ constructor(private cd: ChangeDetectorRef, private domSanitizer: DomSanitizer) {
+ }
+
+ @HostListener('drop', ['$event'])
+ onDrop(event: any) {
+ if (this.dropFiles) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ this.fileOver = false;
+ if (event.dataTransfer && event.dataTransfer.files) {
+
+ // tslint:disable-next-line
+ for (let file of event.dataTransfer.files) {
+ const res = file;
+
+ if (this.imgDropTypes.includes(file.type)) {
+ const fr = new FileReader();
+ fr.onload = (e: any) => {
+ res.src = e.target.result;
+ res.urlStyle = this.domSanitizer.bypassSecurityTrustStyle(`url(${res.src})`);
+ this.cd.detectChanges();
+ };
+
+ fr.readAsDataURL(file);
+ }
+ this.droppedFiles.push(res);
+ }
+ }
+ }
+ }
+
+ removeFile(file) {
+ const index = this.droppedFiles.indexOf(file);
+ if (index >= 0) {
+ this.droppedFiles.splice(index, 1);
+ }
+ }
+
+ @HostListener('dragover')
+ onDragOver() {
+ if (this.dropFiles) {
+ this.fileOver = true;
+ }
+ }
+
+ @HostListener('dragleave')
+ onDragLeave() {
+ if (this.dropFiles) {
+ this.fileOver = false;
+ }
+ }
+
+ sendMessage() {
+ if (this.droppedFiles.length || String(this.message).trim().length) {
+ this.send.emit({ message: this.message, files: this.droppedFiles });
+ this.message = '';
+ this.droppedFiles = [];
+ }
+ }
+}
diff --git a/src/framework/theme/components/chat/chat-message-file.component.ts b/src/framework/theme/components/chat/chat-message-file.component.ts
new file mode 100644
index 0000000000..6d8cdd1f67
--- /dev/null
+++ b/src/framework/theme/components/chat/chat-message-file.component.ts
@@ -0,0 +1,87 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ */
+
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input } from '@angular/core';
+import { DomSanitizer } from '@angular/platform-browser';
+
+/**
+ * Chat message component.
+ *
+ * @styles
+ *
+ */
+@Component({
+ selector: 'nb-chat-message-file',
+ template: `
+
+ {{ message }}
+
+
+ 1">
+
+
+
+
+
+
+
+
+
+ `,
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class NbChatMessageFileComponent {
+
+ readyFiles: any[];
+
+ /**
+ * Message sender
+ * @type {string}
+ */
+ @Input() message: string;
+
+ /**
+ * Message sender
+ * @type {string}
+ */
+ @Input() sender: string;
+
+ /**
+ * Message send date
+ * @type {Date}
+ */
+ @Input() date: Date;
+
+ /**
+ * Message file path
+ * @type {Date}
+ */
+ @Input()
+ set files(files: any[]) {
+ this.readyFiles = (files || []).map((file: any) => {
+ const isImage = this.isImage(file);
+ return {
+ ...file,
+ urlStyle: isImage && this.domSanitizer.bypassSecurityTrustStyle(`url(${file.url})`),
+ isImage: isImage,
+ };
+ });
+ this.cd.detectChanges();
+ }
+
+ constructor(private cd: ChangeDetectorRef, private domSanitizer: DomSanitizer) {
+ }
+
+
+ isImage(file: any): boolean {
+ return ['image/png', 'image/jpeg', 'image/gif'].includes(file.type);
+ }
+}
diff --git a/src/framework/theme/components/chat/chat-message-map.component.ts b/src/framework/theme/components/chat/chat-message-map.component.ts
new file mode 100644
index 0000000000..e55ee24e0c
--- /dev/null
+++ b/src/framework/theme/components/chat/chat-message-map.component.ts
@@ -0,0 +1,69 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ */
+
+import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
+import { NbChatOptions } from './chat.options';
+
+/**
+ * Chat message component.
+ *
+ * @styles
+ *
+ */
+@Component({
+ selector: 'nb-chat-message-map',
+ template: `
+
+ `,
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class NbChatMessageMapComponent {
+
+ /**
+ * Message sender
+ * @type {string}
+ */
+ @Input() message: string;
+
+ /**
+ * Message sender
+ * @type {string}
+ */
+ @Input() sender: string;
+
+ /**
+ * Message send date
+ * @type {Date}
+ */
+ @Input() date: Date;
+
+ /**
+ * Map latitude
+ * @type {number}
+ */
+ @Input() latitude: number;
+
+ /**
+ * Map longitude
+ * @type {number}
+ */
+ @Input() longitude: number;
+
+ get file() {
+ return {
+ // tslint:disable-next-line
+ url: `https://maps.googleapis.com/maps/api/staticmap?center=${this.latitude},${this.longitude}&zoom=12&size=400x400&key=${this.mapKey}`,
+ type: 'image/png',
+ icon: 'nb-location',
+ };
+ }
+
+ mapKey: string;
+
+ constructor(options: NbChatOptions) {
+ this.mapKey = options.messageGoogleMapKey;
+ }
+}
diff --git a/src/framework/theme/components/chat/chat-message-quote.component.ts b/src/framework/theme/components/chat/chat-message-quote.component.ts
new file mode 100644
index 0000000000..610f9a24eb
--- /dev/null
+++ b/src/framework/theme/components/chat/chat-message-quote.component.ts
@@ -0,0 +1,54 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ */
+
+import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
+
+/**
+ * Chat message component.
+ *
+ * @styles
+ *
+ */
+@Component({
+ selector: 'nb-chat-message-quote',
+ template: `
+ {{ sender }}
+
+ {{ quote }}
+
+
+ {{ message }}
+
+ `,
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class NbChatMessageQuoteComponent {
+
+ /**
+ * Message sender
+ * @type {string}
+ */
+ @Input() message: string;
+
+ /**
+ * Message sender
+ * @type {string}
+ */
+ @Input() sender: string;
+
+ /**
+ * Message send date
+ * @type {Date}
+ */
+ @Input() date: Date;
+
+ /**
+ * Quoted message
+ * @type {Date}
+ */
+ @Input() quote: string;
+
+}
diff --git a/src/framework/theme/components/chat/chat-message-text.component.ts b/src/framework/theme/components/chat/chat-message-text.component.ts
new file mode 100644
index 0000000000..a290948784
--- /dev/null
+++ b/src/framework/theme/components/chat/chat-message-text.component.ts
@@ -0,0 +1,43 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ */
+
+import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
+
+/**
+ * Chat message component.
+ *
+ * @styles
+ *
+ */
+@Component({
+ selector: 'nb-chat-message-text',
+ template: `
+ {{ sender }}
+ {{ message }}
+ `,
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class NbChatMessageTextComponent {
+
+ /**
+ * Message sender
+ * @type {string}
+ */
+ @Input() sender: string;
+
+ /**
+ * Message sender
+ * @type {string}
+ */
+ @Input() message: string;
+
+ /**
+ * Message send date
+ * @type {Date}
+ */
+ @Input() date: Date;
+
+}
diff --git a/src/framework/theme/components/chat/chat-message.component.ts b/src/framework/theme/components/chat/chat-message.component.ts
new file mode 100644
index 0000000000..a7a61b5a5c
--- /dev/null
+++ b/src/framework/theme/components/chat/chat-message.component.ts
@@ -0,0 +1,194 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ */
+
+import { ChangeDetectionStrategy, Component, HostBinding, Input } from '@angular/core';
+import { convertToBoolProperty } from '../helpers';
+import { DomSanitizer, SafeStyle } from '@angular/platform-browser';
+import { animate, state, style, transition, trigger } from '@angular/animations';
+
+/**
+ * Chat message component.
+ *
+ * Multiple message types are available through a `type` property, such as
+ * - text - simple text message
+ * - file - could be a file preview or a file icon
+ * if multiple files are provided grouped files are shown
+ * - quote - quotes a message with specific quote styles
+ * - map - shows a google map picture by provided [latitude] and [longitude] properties
+ *
+ * @stacked-example(Available Types, chat/chat-message-types-showcase.component)
+ *
+ * Message with attached files:
+ * ```html
+ *
+ *
+ * ```
+ *
+ * Map message:
+ * ```html
+ *
+ *
+ * ```
+ *
+ * @styles
+ *
+ * chat-message-fg:
+ * chat-message-bg:
+ * chat-message-reply-bg:
+ * chat-message-reply-fg:
+ * chat-message-avatar-bg:
+ * chat-message-sender-fg:
+ * chat-message-quote-fg:
+ * chat-message-quote-bg:
+ * chat-message-file-fg:
+ * chat-message-file-bg:
+ */
+@Component({
+ selector: 'nb-chat-message',
+ template: `
+
+
+ {{ getInitials() }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `,
+ animations: [
+ trigger('flyInOut', [
+ state('in', style({ transform: 'translateX(0)' })),
+ transition('void => *', [
+ style({ transform: 'translateX(-100%)' }),
+ animate(80),
+ ]),
+ transition('* => void', [
+ animate(80, style({ transform: 'translateX(100%)' })),
+ ]),
+ ]),
+ ],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class NbChatMessageComponent {
+
+
+ @HostBinding('@flyInOut')
+ get flyInOut() {
+ return true;
+ }
+
+ @HostBinding('class.reply')
+ replyValue: boolean = false;
+
+ @HostBinding('class.not-reply')
+ get notReply() {
+ return !this.replyValue;
+ }
+
+ avatarStyle: SafeStyle;
+
+ /**
+ * Determines if a message is a reply
+ */
+ @Input()
+ set reply(val: boolean) {
+ this.replyValue = convertToBoolProperty(val);
+ }
+
+ /**
+ * Message sender
+ * @type {string}
+ */
+ @Input() message: string;
+
+ /**
+ * Message sender
+ * @type {string}
+ */
+ @Input() sender: string;
+
+ /**
+ * Message send date
+ * @type {Date}
+ */
+ @Input() date: Date;
+
+ /**
+ * Array of files `{ url: 'file url', icon: 'file icon class' }`
+ * @type {string}
+ */
+ @Input() files: { url: string, icon: string }[];
+
+ /**
+ * Quoted message text
+ * @type {string}
+ */
+ @Input() quote: string;
+
+ /**
+ * Map latitude
+ * @type {number}
+ */
+ @Input() latitude: number;
+
+ /**
+ * Map longitude
+ * @type {number}
+ */
+ @Input() longitude: number;
+
+ /**
+ * Message send avatar
+ * @type {string}
+ */
+ @Input()
+ set avatar(value: string) {
+ this.avatarStyle = value ? this.domSanitizer.bypassSecurityTrustStyle(`url(${value})`) : null;
+ }
+
+ /**
+ * Message type, available options `text|file|map|quote`
+ * @type {string}
+ */
+ @Input() type: string;
+
+ constructor(private domSanitizer: DomSanitizer) { }
+
+ getInitials(): string {
+ if (this.sender) {
+ const names = this.sender.split(' ');
+
+ return names.map(n => n.charAt(0)).splice(0, 2).join('').toUpperCase();
+ }
+
+ return '';
+ }
+}
diff --git a/src/framework/theme/components/chat/chat.component.scss b/src/framework/theme/components/chat/chat.component.scss
new file mode 100644
index 0000000000..e34264ce7d
--- /dev/null
+++ b/src/framework/theme/components/chat/chat.component.scss
@@ -0,0 +1,12 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ */
+
+:host {
+ display: flex;
+ flex-direction: column;
+ position: relative;
+ height: 100%;
+}
diff --git a/src/framework/theme/components/chat/chat.component.ts b/src/framework/theme/components/chat/chat.component.ts
new file mode 100644
index 0000000000..15d0f87bd9
--- /dev/null
+++ b/src/framework/theme/components/chat/chat.component.ts
@@ -0,0 +1,249 @@
+/**
+ * @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,
+ AfterViewChecked,
+ ContentChildren,
+ QueryList, AfterViewInit,
+} from '@angular/core';
+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
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * ```
+ *
+ * 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-font-size:
+ * chat-fg:
+ * chat-bg:
+ * chat-border-radius:
+ * chat-fg-text:
+ * chat-height-xxsmall:
+ * chat-height-xsmall:
+ * chat-height-small:
+ * chat-height-medium:
+ * chat-height-large:
+ * chat-height-xlarge:
+ * chat-height-xxlarge:
+ * chat-border:
+ * chat-padding:
+ * chat-shadow:
+ * chat-separator:
+ * chat-active-bg:
+ * chat-disabled-bg:
+ * chat-disabled-fg:
+ * chat-primary-bg:
+ * chat-info-bg:
+ * chat-success-bg:
+ * chat-warning-bg:
+ * chat-danger-bg:
+ */
+@Component({
+ selector: 'nb-chat',
+ styleUrls: ['./chat.component.scss'],
+ template: `
+
+
+
+
+
+ `,
+})
+export class NbChatComponent implements AfterViewChecked, AfterViewInit {
+
+ static readonly SIZE_XXSMALL = 'xxsmall';
+ static readonly SIZE_XSMALL = 'xsmall';
+ static readonly SIZE_SMALL = 'small';
+ static readonly SIZE_MEDIUM = 'medium';
+ static readonly SIZE_LARGE = 'large';
+ static readonly SIZE_XLARGE = 'xlarge';
+ static readonly SIZE_XXLARGE = 'xxlarge';
+
+ static readonly STATUS_ACTIVE = 'active';
+ static readonly STATUS_DISABLED = 'disabled';
+ static readonly STATUS_PRIMARY = 'primary';
+ static readonly STATUS_INFO = 'info';
+ static readonly STATUS_SUCCESS = 'success';
+ static readonly STATUS_WARNING = 'warning';
+ static readonly STATUS_DANGER = 'danger';
+
+ size: string;
+ status: string;
+ accent: string;
+
+ @Input() title: string;
+
+ @HostBinding('class.xxsmall-chat')
+ get xxsmall() {
+ return this.size === NbChatComponent.SIZE_XXSMALL;
+ }
+
+ @HostBinding('class.xsmall-chat')
+ get xsmall() {
+ return this.size === NbChatComponent.SIZE_XSMALL;
+ }
+
+ @HostBinding('class.small-chat')
+ get small() {
+ return this.size === NbChatComponent.SIZE_SMALL;
+ }
+
+ @HostBinding('class.medium-chat')
+ get medium() {
+ return this.size === NbChatComponent.SIZE_MEDIUM;
+ }
+
+ @HostBinding('class.large-chat')
+ get large() {
+ return this.size === NbChatComponent.SIZE_LARGE;
+ }
+
+ @HostBinding('class.xlarge-chat')
+ get xlarge() {
+ return this.size === NbChatComponent.SIZE_XLARGE;
+ }
+
+ @HostBinding('class.xxlarge-chat')
+ get xxlarge() {
+ return this.size === NbChatComponent.SIZE_XXLARGE;
+ }
+
+ @HostBinding('class.active-chat')
+ get active() {
+ return this.status === NbChatComponent.STATUS_ACTIVE;
+ }
+
+ @HostBinding('class.disabled-chat')
+ get disabled() {
+ return this.status === NbChatComponent.STATUS_DISABLED;
+ }
+
+ @HostBinding('class.primary-chat')
+ get primary() {
+ return this.status === NbChatComponent.STATUS_PRIMARY;
+ }
+
+ @HostBinding('class.info-chat')
+ get info() {
+ return this.status === NbChatComponent.STATUS_INFO;
+ }
+
+ @HostBinding('class.success-chat')
+ get success() {
+ return this.status === NbChatComponent.STATUS_SUCCESS;
+ }
+
+ @HostBinding('class.warning-chat')
+ get warning() {
+ return this.status === NbChatComponent.STATUS_WARNING;
+ }
+
+ @HostBinding('class.danger-chat')
+ get danger() {
+ return this.status === NbChatComponent.STATUS_DANGER;
+ }
+
+ @HostBinding('class.accent')
+ get hasAccent() {
+ return this.accent;
+ }
+
+ /**
+ * Chat size, available sizes:
+ * xxsmall, xsmall, small, medium, large, xlarge, xxlarge
+ * @param {string} val
+ */
+ @Input('size')
+ private set setSize(val: string) {
+ this.size = val;
+ }
+
+ /**
+ * Chat status color (adds specific styles):
+ * active, disabled, primary, info, success, warning, danger
+ * @param {string} val
+ */
+ @Input('status')
+ private set setStatus(val: string) {
+ this.status = val;
+ }
+
+ @ViewChild('scrollable') scrollable: ElementRef;
+ @ContentChildren(NbChatMessageComponent) messages: QueryList;
+
+ ngAfterViewChecked() {
+ this.scrollable.nativeElement.scrollTop = this.scrollable.nativeElement.scrollHeight;
+ }
+
+ ngAfterViewInit() {
+ this.messages.changes
+ .subscribe((messages) => this.messages = messages);
+ }
+}
diff --git a/src/framework/theme/components/chat/chat.module.ts b/src/framework/theme/components/chat/chat.module.ts
new file mode 100644
index 0000000000..25e4d9be06
--- /dev/null
+++ b/src/framework/theme/components/chat/chat.module.ts
@@ -0,0 +1,60 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ */
+
+import { ModuleWithProviders, NgModule } from '@angular/core';
+
+import { NbSharedModule } from '../shared/shared.module';
+
+import { NbChatComponent } from './chat.component';
+import { NbChatMessageComponent } from './chat-message.component';
+import { NbChatFormComponent } from './chat-form.component';
+import { NbChatMessageTextComponent } from './chat-message-text.component';
+import { NbChatMessageFileComponent } from './chat-message-file.component';
+import { NbChatMessageQuoteComponent } from './chat-message-quote.component';
+import { NbChatMessageMapComponent } from './chat-message-map.component';
+import { NbChatOptions } from './chat.options';
+
+const NB_CHAT_COMPONENTS = [
+ NbChatComponent,
+ NbChatMessageComponent,
+ NbChatFormComponent,
+ NbChatMessageTextComponent,
+ NbChatMessageFileComponent,
+ NbChatMessageQuoteComponent,
+ NbChatMessageMapComponent,
+];
+
+@NgModule({
+ imports: [
+ NbSharedModule,
+ ],
+ declarations: [
+ ...NB_CHAT_COMPONENTS,
+ ],
+ exports: [
+ ...NB_CHAT_COMPONENTS,
+ ],
+})
+export class NbChatModule {
+
+ static forRoot(options?: NbChatOptions) {
+ return {
+ ngModule: NbChatModule,
+ providers: [
+ { provide: NbChatOptions, useValue: options },
+ ],
+ };
+ }
+
+ static forChild(options?: NbChatOptions) {
+ return {
+ ngModule: NbChatModule,
+ providers: [
+ { provide: NbChatOptions, useValue: options },
+ ],
+ };
+ }
+}
diff --git a/src/framework/theme/components/chat/chat.options.ts b/src/framework/theme/components/chat/chat.options.ts
new file mode 100644
index 0000000000..2042cfe9dd
--- /dev/null
+++ b/src/framework/theme/components/chat/chat.options.ts
@@ -0,0 +1,9 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ */
+
+export class NbChatOptions {
+ messageGoogleMapKey?: string;
+}
diff --git a/src/framework/theme/index.ts b/src/framework/theme/index.ts
index d328206cda..134850a450 100644
--- a/src/framework/theme/index.ts
+++ b/src/framework/theme/index.ts
@@ -35,3 +35,11 @@ export * from './components/progress-bar/progress-bar.component';
export * from './components/progress-bar/progress-bar.module';
export * from './components/alert/alert.component';
export * from './components/alert/alert.module';
+export * from './components/chat/chat.component';
+export * from './components/chat/chat-message.component';
+export * from './components/chat/chat-message-map.component';
+export * from './components/chat/chat-message-file.component';
+export * from './components/chat/chat-message-quote.component';
+export * from './components/chat/chat-message-text.component';
+export * from './components/chat/chat-form.component';
+export * from './components/chat/chat.module';
diff --git a/src/framework/theme/styles/global/_components.scss b/src/framework/theme/styles/global/_components.scss
index 9ecc359fe4..6cca0ac345 100644
--- a/src/framework/theme/styles/global/_components.scss
+++ b/src/framework/theme/styles/global/_components.scss
@@ -21,6 +21,7 @@
@import '../../components/popover/popover.component.theme';
@import '../../components/context-menu/context-menu.component.theme';
@import '../../components/alert/alert.component.theme';
+@import '../../components/chat/chat.component.theme';
@mixin nb-theme-components() {
@@ -41,4 +42,5 @@
@include nb-popover-theme();
@include nb-context-menu-theme();
@include nb-alert-theme();
+ @include nb-chat-theme();
}
diff --git a/src/framework/theme/styles/themes/_default.scss b/src/framework/theme/styles/themes/_default.scss
index 25cd928059..84c6d0b365 100644
--- a/src/framework/theme/styles/themes/_default.scss
+++ b/src/framework/theme/styles/themes/_default.scss
@@ -492,6 +492,46 @@ $theme: (
alert-closable-padding: 3rem,
alert-button-padding: 3rem,
alert-margin: margin,
+
+ chat-font-size: font-size,
+ chat-fg: color-white,
+ chat-bg: color-bg,
+ chat-border-radius: radius,
+ chat-fg-text: color-fg-text,
+ chat-height-xxsmall: 96px,
+ chat-height-xsmall: 216px,
+ chat-height-small: 336px,
+ chat-height-medium: 456px,
+ chat-height-large: 576px,
+ chat-height-xlarge: 696px,
+ chat-height-xxlarge: 816px,
+ chat-border: border,
+ chat-padding: padding,
+ chat-shadow: shadow,
+ chat-separator: separator,
+ chat-message-fg: color-white,
+ chat-message-bg: linear-gradient(to right, #4ca6ff, #59bfff),
+ chat-message-reply-bg: color-bg-active,
+ chat-message-reply-fg: color-fg-text,
+ chat-message-avatar-bg: color-fg,
+ chat-message-sender-fg: color-fg,
+ chat-message-quote-fg: color-fg,
+ chat-message-quote-bg: color-bg-active,
+ chat-message-file-fg: color-fg,
+ chat-message-file-bg: transparent,
+ chat-form-bg: transparent,
+ chat-form-fg: color-fg-heading,
+ chat-form-border: separator,
+ chat-form-placeholder-fg: color-fg,
+ chat-form-active-border: color-fg,
+ chat-active-bg: color-fg,
+ chat-disabled-bg: color-disabled,
+ chat-disabled-fg: color-fg,
+ chat-primary-bg: color-primary,
+ chat-info-bg: color-info,
+ chat-success-bg: color-success,
+ chat-warning-bg: color-warning,
+ chat-danger-bg: color-danger,
);
// register the theme
diff --git a/src/playground/chat/bot-replies.ts b/src/playground/chat/bot-replies.ts
new file mode 100644
index 0000000000..23d8cd1765
--- /dev/null
+++ b/src/playground/chat/bot-replies.ts
@@ -0,0 +1,190 @@
+const botAvatar: string = 'https://i.ytimg.com/vi/Erqi5ckVoEo/hqdefault.jpg';
+
+export const gifsLinks: string[] = [
+ 'https://media.tenor.com/images/ac287fd06319e47b1533737662d5bfe8/tenor.gif',
+ 'https://i.gifer.com/no.gif',
+ 'https://techcrunch.com/wp-content/uploads/2015/08/safe_image.gif',
+ 'http://www.reactiongifs.com/r/wnd1.gif',
+];
+export const imageLinks: string[] = [
+ 'https://picsum.photos/320/240/?image=357',
+ 'https://picsum.photos/320/240/?image=556',
+ 'https://picsum.photos/320/240/?image=339',
+ 'https://picsum.photos/320/240/?image=387',
+ 'https://picsum.photos/320/240/?image=30',
+ 'https://picsum.photos/320/240/?image=271',
+];
+const fileLink: string = 'http://google.com';
+
+export const botReplies = [
+ {
+ regExp: /([H,h]ey)|([H,h]i)/g,
+ answerArray: ['Hello!', 'Yes?', 'Yes, milord?', 'What can I do for you?'],
+ type: 'text',
+ reply: {
+ text: '',
+ reply: false,
+ date: new Date(),
+ user: {
+ name: 'Bot',
+ avatar: botAvatar,
+ },
+ },
+ },
+ {
+ regExp: /([H,h]elp)/g,
+ answerArray: [`No problem! Try sending a message containing word "hey", "image",
+ "gif", "file", "map", "quote", "file group" to see different message components`],
+ type: 'text',
+ reply: {
+ text: '',
+ reply: false,
+ date: new Date(),
+ user: {
+ name: 'Bot',
+ avatar: botAvatar,
+ },
+ },
+ },
+ {
+ regExp: /([I,i]mage)|(IMAGE)|([P,p]ic)|(Picture)/g,
+ answerArray: ['Hey look at this!', 'Ready to work', 'Yes, master.'],
+ type: 'pic',
+ reply: {
+ text: '',
+ reply: false,
+ date: new Date(),
+ type: 'file',
+ files: [
+ {
+ url: '',
+ type: 'image/jpeg',
+ },
+ ],
+ user: {
+ name: 'Bot',
+ avatar: botAvatar,
+ },
+ },
+ },
+ {
+ regExp: /([G,g]if)|(GIF)/g,
+ type: 'gif',
+ answerArray: ['No problem', 'Well done', 'You got it man'],
+ reply: {
+ text: '',
+ reply: false,
+ date: new Date(),
+ type: 'file',
+ files: [
+ {
+ url: '',
+ type: 'image/gif',
+ },
+ ],
+ user: {
+ name: 'Bot',
+ avatar: botAvatar,
+ },
+ },
+ },
+ {
+ regExp: /([F,f]ile group)|(FILE)/g,
+ type: 'group',
+ answerArray: ['Take it!', 'Job Done.', 'As you wish'],
+ reply: {
+ text: '',
+ reply: false,
+ date: new Date(),
+ type: 'file',
+ files: [
+ {
+ url: fileLink,
+ icon: 'nb-compose',
+ },
+ {
+ url: '',
+ type: 'image/gif',
+ },
+ {
+ url: '',
+ type: 'image/jpeg',
+ },
+ ],
+ icon: 'nb-compose',
+ user: {
+ name: 'Bot',
+ avatar: botAvatar,
+ },
+ },
+ },
+ {
+ regExp: /([F,f]ile)|(FILE)/g,
+ type: 'file',
+ answerArray: ['Take it!', 'Job Done.', 'As you wish'],
+ reply: {
+ text: '',
+ reply: false,
+ date: new Date(),
+ type: 'file',
+ files: [
+ {
+ url: fileLink,
+ icon: 'nb-compose',
+ },
+ ],
+ icon: 'nb-compose',
+ user: {
+ name: 'Bot',
+ avatar: botAvatar,
+ },
+ },
+ },
+ {
+ regExp: /([M,m]ap)|(MAP)/g,
+ type: 'map',
+ answerArray: ['Done.', 'My sight is yours.', 'I shall be your eyes.'],
+ reply: {
+ text: '',
+ reply: false,
+ date: new Date(),
+ type: 'map',
+ latitude: 53.914321,
+ longitude: 27.5998355,
+ user: {
+ name: 'Bot',
+ avatar: botAvatar,
+ },
+ },
+ },
+ {
+ regExp: /([Q,q]uote)|(QUOTE)/g,
+ type: 'quote',
+ answerArray: ['Quoted!', 'Say no more.', 'I gladly obey.'],
+ reply: {
+ text: '',
+ reply: false,
+ date: new Date(),
+ type: 'quote',
+ quote: '',
+ user: {
+ name: 'Bot',
+ avatar: botAvatar,
+ },
+ },
+ },
+ {
+ regExp: /(.*)/g,
+ answerArray: ['Hello there! Try typing "help"'],
+ type: 'text',
+ reply: {
+ text: '',
+ reply: false,
+ date: new Date(),
+ user: {
+ name: 'Bot',
+ avatar: botAvatar,
+ },
+ },
+ },
+];
diff --git a/src/playground/chat/chat-colors.component.html b/src/playground/chat/chat-colors.component.html
new file mode 100644
index 0000000000..e87d9f8e1f
--- /dev/null
+++ b/src/playground/chat/chat-colors.component.html
@@ -0,0 +1,12 @@
+
+
+
+
+
diff --git a/src/playground/chat/chat-colors.component.ts b/src/playground/chat/chat-colors.component.ts
new file mode 100644
index 0000000000..8d8f0cbdbb
--- /dev/null
+++ b/src/playground/chat/chat-colors.component.ts
@@ -0,0 +1,138 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'nb-chat-colors',
+ templateUrl: './chat-colors.component.html',
+ styles: [`
+ ::ng-deep nb-layout-column {
+ justify-content: center;
+ display: flex;
+ }
+ nb-chat {
+ width: 500px;
+ margin: 0.5rem 0 2rem 2rem;
+ }
+ `],
+})
+
+export class NbChatColorsComponent {
+ chats: any[] = [
+ {
+ status: 'success',
+ title: 'Nebular Conversational UI Success',
+ messages: [
+ {
+ text: 'Success!',
+ date: new Date(),
+ reply: false,
+ user: {
+ name: 'Bot',
+ avatar: 'https://i.ytimg.com/vi/Erqi5ckVoEo/hqdefault.jpg',
+ },
+ },
+ ],
+ },
+ {
+ status: 'danger',
+ title: 'Nebular Conversational UI Danger',
+ messages: [
+ {
+ text: 'Danger!',
+ date: new Date(),
+ reply: false,
+ user: {
+ name: 'Bot',
+ avatar: 'https://i.ytimg.com/vi/Erqi5ckVoEo/hqdefault.jpg',
+ },
+ },
+ ],
+ },
+ {
+ status: 'primary',
+ title: 'Nebular Conversational UI Primary',
+ messages: [
+ {
+ text: 'Primary!',
+ date: new Date(),
+ reply: false,
+ user: {
+ name: 'Bot',
+ avatar: 'https://i.ytimg.com/vi/Erqi5ckVoEo/hqdefault.jpg',
+ },
+ },
+ ],
+ },
+ {
+ status: 'info',
+ title: 'Nebular Conversational UI Info',
+ messages: [
+ {
+ text: 'Info!',
+ date: new Date(),
+ reply: false,
+ user: {
+ name: 'Bot',
+ avatar: 'https://i.ytimg.com/vi/Erqi5ckVoEo/hqdefault.jpg',
+ },
+ },
+ ],
+ },
+ {
+ status: 'warning',
+ title: 'Nebular Conversational UI Warning',
+ messages: [
+ {
+ text: 'Warning!',
+ date: new Date(),
+ reply: false,
+ user: {
+ name: 'Bot',
+ avatar: 'https://i.ytimg.com/vi/Erqi5ckVoEo/hqdefault.jpg',
+ },
+ },
+ ],
+ },
+ {
+ status: 'active',
+ title: 'Nebular Conversational UI Active',
+ messages: [
+ {
+ text: 'Active!',
+ date: new Date(),
+ reply: false,
+ user: {
+ name: 'Bot',
+ avatar: 'https://i.ytimg.com/vi/Erqi5ckVoEo/hqdefault.jpg',
+ },
+ },
+ ],
+ },
+ {
+ status: 'disabled',
+ title: 'Nebular Conversational UI Disabled',
+ messages: [
+ {
+ text: 'Disabled!',
+ date: new Date(),
+ reply: false,
+ user: {
+ name: 'Bot',
+ avatar: 'https://i.ytimg.com/vi/Erqi5ckVoEo/hqdefault.jpg',
+ },
+ },
+ ],
+ },
+ ];
+
+ sendMessage(messages, event) {
+ messages.push({
+ text: event.message,
+ date: new Date(),
+ reply: true,
+ user: {
+ name: 'Jonh Doe',
+ avatar: 'https://techcrunch.com/wp-content/uploads/2015/08/safe_image.gif',
+ },
+ });
+ }
+}
diff --git a/src/playground/chat/chat-conversation-showcase.component.html b/src/playground/chat/chat-conversation-showcase.component.html
new file mode 100644
index 0000000000..2969ca262d
--- /dev/null
+++ b/src/playground/chat/chat-conversation-showcase.component.html
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/playground/chat/chat-conversation-showcase.component.ts b/src/playground/chat/chat-conversation-showcase.component.ts
new file mode 100644
index 0000000000..a5ef2ac06a
--- /dev/null
+++ b/src/playground/chat/chat-conversation-showcase.component.ts
@@ -0,0 +1,46 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'nb-chat-conversation-showcase',
+ styles: [`
+ ::ng-deep nb-layout-column {
+ display: flex;
+ justify-content: center;
+ }
+ :host {
+ display: flex;
+ }
+ nb-chat {
+ width: 300px;
+ margin: 1rem;
+ }
+ `],
+ templateUrl: './chat-conversation-showcase.component.html',
+})
+
+export class NbChatConversationShowcaseComponent {
+
+ messages: any[] = [];
+
+ sendMessage(event: any, userName: string, avatar: string, reply: boolean) {
+ const files = !event.files ? [] : event.files.map((file) => {
+ return {
+ url: file.src,
+ type: file.type,
+ icon: 'nb-compose',
+ };
+ });
+
+ this.messages.push({
+ text: event.message,
+ date: new Date(),
+ reply: reply,
+ type: files.length ? 'file' : 'text',
+ files: files,
+ user: {
+ name: userName,
+ avatar: avatar,
+ },
+ });
+ }
+}
diff --git a/src/playground/chat/chat-drop.component.html b/src/playground/chat/chat-drop.component.html
new file mode 100644
index 0000000000..ab61dd905c
--- /dev/null
+++ b/src/playground/chat/chat-drop.component.html
@@ -0,0 +1,12 @@
+
+
+
+
+
diff --git a/src/playground/chat/chat-drop.component.ts b/src/playground/chat/chat-drop.component.ts
new file mode 100644
index 0000000000..5b7b8a5a9a
--- /dev/null
+++ b/src/playground/chat/chat-drop.component.ts
@@ -0,0 +1,53 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'nb-chat-drop',
+ styles: [`
+ ::ng-deep nb-layout-column {
+ justify-content: center;
+ display: flex;
+ }
+ nb-chat {
+ width: 500px;
+ height: 80vw;
+ }
+ `],
+ templateUrl: './chat-drop.component.html',
+})
+
+export class NbChatDropComponent {
+
+ messages: any[] = [
+ {
+ text: 'Drag & drop a file or a group of files.',
+ date: new Date(),
+ reply: true,
+ user: {
+ name: 'Bot',
+ avatar: 'https://i.gifer.com/no.gif',
+ },
+ },
+ ];
+
+ sendMessage(event) {
+ const files = !event.files ? [] : event.files.map((file) => {
+ return {
+ url: file.src,
+ type: file.type,
+ icon: 'nb-compose',
+ };
+ });
+
+ this.messages.push({
+ text: event.message,
+ date: new Date(),
+ files: files,
+ type: files.length ? 'file' : 'text',
+ reply: true,
+ user: {
+ name: 'Jonh Doe',
+ avatar: 'https://i.gifer.com/no.gif',
+ },
+ });
+ }
+}
diff --git a/src/playground/chat/chat-message-types-showcase.component.ts b/src/playground/chat/chat-message-types-showcase.component.ts
new file mode 100644
index 0000000000..c5b37d4404
--- /dev/null
+++ b/src/playground/chat/chat-message-types-showcase.component.ts
@@ -0,0 +1,87 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'nb-chat-message-type-showcase',
+ styles: [`
+ nb-card-body {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+ align-items: center;
+ }
+ nb-chat {
+ width: 500px;
+ }
+ `],
+ template: `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `,
+})
+
+export class NbChatMessageTypesShowcaseComponent {
+ date = new Date();
+}
diff --git a/src/playground/chat/chat-showcase.component.html b/src/playground/chat/chat-showcase.component.html
new file mode 100644
index 0000000000..d2a1a99163
--- /dev/null
+++ b/src/playground/chat/chat-showcase.component.html
@@ -0,0 +1,16 @@
+
+
+
+
+
+
diff --git a/src/playground/chat/chat-showcase.component.ts b/src/playground/chat/chat-showcase.component.ts
new file mode 100644
index 0000000000..2569f3f1aa
--- /dev/null
+++ b/src/playground/chat/chat-showcase.component.ts
@@ -0,0 +1,57 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ */
+
+import { Component } from '@angular/core';
+import { NbChatShowcaseService } from './chat-showcase.service';
+
+@Component({
+ selector: 'nb-chat-showcase',
+ templateUrl: './chat-showcase.component.html',
+ providers: [ NbChatShowcaseService ],
+ styles: [`
+ ::ng-deep nb-layout-column {
+ justify-content: center;
+ display: flex;
+ }
+ nb-chat {
+ width: 500px;
+ }
+ `],
+})
+export class NbChatShowcaseComponent {
+
+ messages: any[];
+
+ constructor(protected chatShowcaseService: NbChatShowcaseService) {
+ this.messages = this.chatShowcaseService.loadMessages();
+ }
+
+ sendMessage(event: any) {
+ const files = !event.files ? [] : event.files.map((file) => {
+ return {
+ url: file.src,
+ type: file.type,
+ icon: 'nb-compose',
+ };
+ });
+
+ this.messages.push({
+ text: event.message,
+ date: new Date(),
+ reply: true,
+ type: files.length ? 'file' : 'text',
+ files: files,
+ user: {
+ name: 'Jonh Doe',
+ avatar: 'https://i.gifer.com/no.gif',
+ },
+ });
+ const botReply = this.chatShowcaseService.reply(event.message);
+ if (botReply) {
+ setTimeout(() => { this.messages.push(botReply) }, 500);
+ }
+ }
+}
diff --git a/src/playground/chat/chat-showcase.service.ts b/src/playground/chat/chat-showcase.service.ts
new file mode 100644
index 0000000000..ccbc7d6d51
--- /dev/null
+++ b/src/playground/chat/chat-showcase.service.ts
@@ -0,0 +1,42 @@
+import { Injectable } from '@angular/core';
+
+import { messages } from './messages';
+import { botReplies, gifsLinks, imageLinks } from './bot-replies';
+
+@Injectable()
+export class NbChatShowcaseService {
+
+
+ loadMessages() {
+ return messages;
+ }
+
+ loadBotReplies() {
+ return botReplies;
+ }
+
+ reply(message: string) {
+ const botReply: any = this.loadBotReplies()
+ .find((reply: any) => message.search(reply.regExp) !== -1);
+
+ if (botReply.reply.type === 'quote') {
+ botReply.reply.quote = message;
+ }
+
+ if (botReply.type === 'gif') {
+ botReply.reply.files[0].url = gifsLinks[Math.floor(Math.random() * gifsLinks.length)];
+ }
+
+ if (botReply.type === 'pic') {
+ botReply.reply.files[0].url = imageLinks[Math.floor(Math.random() * imageLinks.length)];
+ }
+
+ if (botReply.type === 'group') {
+ botReply.reply.files[1].url = gifsLinks[Math.floor(Math.random() * gifsLinks.length)];
+ botReply.reply.files[2].url = imageLinks[Math.floor(Math.random() * imageLinks.length)];
+ }
+
+ botReply.reply.text = botReply.answerArray[Math.floor(Math.random() * botReply.answerArray.length)];
+ return { ...botReply.reply };
+ }
+}
diff --git a/src/playground/chat/chat-size.component.html b/src/playground/chat/chat-size.component.html
new file mode 100644
index 0000000000..e9e5db0b17
--- /dev/null
+++ b/src/playground/chat/chat-size.component.html
@@ -0,0 +1,12 @@
+
+
+
+
+
diff --git a/src/playground/chat/chat-sizes.component.ts b/src/playground/chat/chat-sizes.component.ts
new file mode 100644
index 0000000000..b9734027ba
--- /dev/null
+++ b/src/playground/chat/chat-sizes.component.ts
@@ -0,0 +1,62 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'nb-chat-sizes',
+ styles: [`
+ ::ng-deep nb-layout-column {
+ justify-content: center;
+ display: flex;
+ }
+ nb-chat {
+ width: 500px;
+ margin: 0.5rem 0 2rem 2rem;
+ }`],
+ templateUrl: './chat-size.component.html',
+})
+
+export class NbChatSizesComponent {
+ chats: any[] = [
+ {
+ title: 'Nebular Conversational UI Small',
+ messages: [
+ {
+ text: 'Small!',
+ date: new Date(),
+ reply: true,
+ user: {
+ name: 'Bot',
+ avatar: 'https://i.gifer.com/no.gif',
+ },
+ },
+ ],
+ size: 'small',
+ },
+ {
+ title: 'Nebular Conversational UI Medium',
+ messages: [
+ {
+ text: 'Medium!',
+ date: new Date(),
+ reply: true,
+ user: {
+ name: 'Bot',
+ avatar: 'https://i.ytimg.com/vi/Erqi5ckVoEo/hqdefault.jpg',
+ },
+ },
+ ],
+ size: 'large',
+ },
+ ];
+
+ sendMessage(messages, event) {
+ messages.push({
+ text: event.message,
+ date: new Date(),
+ reply: true,
+ user: {
+ name: 'Jonh Doe',
+ avatar: 'https://techcrunch.com/wp-content/uploads/2015/08/safe_image.gif',
+ },
+ });
+ }
+}
diff --git a/src/playground/chat/chat-test.component.ts b/src/playground/chat/chat-test.component.ts
new file mode 100644
index 0000000000..e57af130de
--- /dev/null
+++ b/src/playground/chat/chat-test.component.ts
@@ -0,0 +1,71 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ */
+
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'nb-chat-test',
+ template: `
+
+
+
+
+
+
+
+ `,
+})
+export class NbChatTestComponent {
+ messages = [];
+ sizes = ['xxsmall', 'xsmall', 'small', 'medium', 'large', 'xlarge', 'xxlarge'];
+ statuses = ['primary', 'success', 'info', 'warning', 'danger', 'active', 'disabled'];
+
+ chats: any[];
+
+ constructor() {
+ this.chats = this.prepareChats();
+ }
+
+ private prepareChats(): any[] {
+ const result = [];
+
+ this.statuses.forEach(status => {
+ this.sizes.forEach(size => {
+ result.push({
+ size,
+ status,
+ });
+ });
+ });
+
+ return result;
+ }
+
+ sendMessage(event) {
+ this.messages.push({
+ text: event.message,
+ date: new Date(),
+ reply: true,
+ user: {
+ name: 'Jonh Doe',
+ avatar: 'https://techcrunch.com/wp-content/uploads/2015/08/safe_image.gif',
+ },
+ });
+ }
+}
diff --git a/src/playground/chat/messages.ts b/src/playground/chat/messages.ts
new file mode 100644
index 0000000000..14f9cd0888
--- /dev/null
+++ b/src/playground/chat/messages.ts
@@ -0,0 +1,85 @@
+export const messages = [
+ {
+ text: 'Hello, how are you? This should be a very long message so that we can test how it fit into the screen.',
+ reply: false,
+ date: new Date(),
+ user: {
+ name: 'John Doe',
+ avatar: 'https://i.gifer.com/no.gif',
+ },
+ },
+ {
+ text: 'Hello, how are you? This should be a very long message so that we can test how it fit into the screen.',
+ reply: true,
+ date: new Date(),
+ user: {
+ name: 'John Doe',
+ avatar: 'https://i.gifer.com/no.gif',
+ },
+ },
+ {
+ text: 'Hello, how are you?',
+ reply: false,
+ date: new Date(),
+ user: {
+ name: 'John Doe',
+ avatar: '',
+ },
+ },
+ {
+ text: 'Hey looks at that pic I just found!',
+ reply: false,
+ date: new Date(),
+ type: 'file',
+ files: [
+ {
+ url: 'https://i.gifer.com/no.gif',
+ type: 'image/jpeg',
+ icon: false,
+ },
+ ],
+ user: {
+ name: 'John Doe',
+ avatar: '',
+ },
+ },
+ {
+ text: 'What do you mean by that?',
+ reply: false,
+ date: new Date(),
+ type: 'quote',
+ quote: 'Hello, how are you? This should be a very long message so that we can test how it fit into the screen.',
+ user: {
+ name: 'John Doe',
+ avatar: '',
+ },
+ },
+ {
+ text: 'Attached is an archive I mentioned',
+ reply: true,
+ date: new Date(),
+ type: 'file',
+ files: [
+ {
+ url: 'https://i.gifer.com/no.gif',
+ icon: 'nb-compose',
+ },
+ ],
+ user: {
+ name: 'John Doe',
+ avatar: '',
+ },
+ },
+ {
+ text: 'Meet me there',
+ reply: false,
+ date: new Date(),
+ type: 'map',
+ latitude: 40.714728,
+ longitude: -73.998672,
+ user: {
+ name: 'John Doe',
+ avatar: '',
+ },
+ },
+];
diff --git a/src/playground/playground-routing.module.ts b/src/playground/playground-routing.module.ts
index 1dc741cbde..ec6b66366a 100644
--- a/src/playground/playground-routing.module.ts
+++ b/src/playground/playground-routing.module.ts
@@ -103,6 +103,13 @@ import { NbAlertShowcaseComponent } from './alert/alert-showcase.component';
import { NbAlertColorsComponent } from './alert/alert-colors.component';
import { NbAlertAccentsComponent } from './alert/alert-accents.component';
import { NbAlertSizesComponent } from './alert/alert-sizes.component';
+import { NbChatShowcaseComponent } from './chat/chat-showcase.component';
+import { NbChatColorsComponent } from './chat/chat-colors.component';
+import { NbChatSizesComponent } from './chat/chat-sizes.component';
+import { NbChatDropComponent } from './chat/chat-drop.component';
+import { NbChatMessageTypesShowcaseComponent } from './chat/chat-message-types-showcase.component';
+import { NbChatConversationShowcaseComponent } from './chat/chat-conversation-showcase.component';
+import { NbChatTestComponent } from './chat/chat-test.component';
export const routes: Routes = [
{
@@ -396,6 +403,39 @@ export const routes: Routes = [
},
],
},
+ {
+ path: 'chat',
+ children: [
+ {
+ path: 'chat-showcase.component',
+ component: NbChatShowcaseComponent,
+ },
+ {
+ path: 'chat-colors.component',
+ component: NbChatColorsComponent,
+ },
+ {
+ path: 'chat-sizes.component',
+ component: NbChatSizesComponent,
+ },
+ {
+ path: 'chat-drop.component',
+ component: NbChatDropComponent,
+ },
+ {
+ path: 'chat-message-types-showcase.component',
+ component: NbChatMessageTypesShowcaseComponent,
+ },
+ {
+ path: 'chat-conversation-showcase.component',
+ component: NbChatConversationShowcaseComponent,
+ },
+ {
+ path: 'chat-test.component',
+ component: NbChatTestComponent,
+ },
+ ],
+ },
],
},
{
diff --git a/src/playground/playground.module.ts b/src/playground/playground.module.ts
index 36c78d47be..484ed2879d 100644
--- a/src/playground/playground.module.ts
+++ b/src/playground/playground.module.ts
@@ -24,6 +24,7 @@ import {
NbRouteTabsetModule,
NbProgressBarModule,
NbAlertModule,
+ NbChatModule,
} from '@nebular/theme';
import { NbPlaygroundRoutingModule } from './playground-routing.module';
@@ -126,6 +127,13 @@ import { NbAlertColorsComponent } from './alert/alert-colors.component';
import { NbAlertAccentsComponent } from './alert/alert-accents.component';
import { NbAlertSizesComponent } from './alert/alert-sizes.component';
import { NbAlertTestComponent } from './alert/alert-test.component';
+import { NbChatShowcaseComponent } from './chat/chat-showcase.component';
+import { NbChatColorsComponent } from './chat/chat-colors.component';
+import { NbChatSizesComponent } from './chat/chat-sizes.component';
+import { NbChatDropComponent } from './chat/chat-drop.component';
+import { NbChatMessageTypesShowcaseComponent } from './chat/chat-message-types-showcase.component';
+import { NbChatConversationShowcaseComponent } from './chat/chat-conversation-showcase.component';
+import { NbChatTestComponent } from './chat/chat-test.component';
export const NB_MODULES = [
NbCardModule,
@@ -147,6 +155,9 @@ export const NB_MODULES = [
NbAlertModule,
NbPlaygroundSharedModule,
NbProgressBarModule,
+ NbChatModule.forChild({
+ messageGoogleMapKey: 'AIzaSyA_wNuCzia92MAmdLRzmqitRGvCF7wCZPY',
+ }),
];
export const NB_EXAMPLE_COMPONENTS = [
@@ -241,6 +252,13 @@ export const NB_EXAMPLE_COMPONENTS = [
NbAlertAccentsComponent,
NbAlertSizesComponent,
NbAlertTestComponent,
+ NbChatShowcaseComponent,
+ NbChatColorsComponent,
+ NbChatSizesComponent,
+ NbChatDropComponent,
+ NbChatMessageTypesShowcaseComponent,
+ NbChatConversationShowcaseComponent,
+ NbChatTestComponent,
];