File

projects/UILibrary/src/app/components/chat-box/chat-box.component.ts

Implements

AfterViewChecked

Metadata

Index

Properties
Methods
Inputs
Outputs

Constructor

constructor()

Inputs

isTyping
Type : boolean
Default value : false
messages
Type : IChatMessage[]
settings
Type : IChatSettings

Outputs

isAgentTyping
Type : EventEmitter
newMessage
Type : EventEmitter

Methods

getColor
getColor(message: IChatMessage)
Parameters :
Name Type Optional
message IChatMessage No
Returns : any
ngAfterViewChecked
ngAfterViewChecked()
Returns : void
onKeyup
onKeyup()
Returns : void
onNewMessageFocus
onNewMessageFocus(event: boolean)
Parameters :
Name Type Optional
event boolean No
Returns : void
scrollToBottom
scrollToBottom()
Returns : void
sendNewMessage
sendNewMessage()
Returns : void
shouldAlignRight
shouldAlignRight(message: IChatMessage)
Parameters :
Name Type Optional
message IChatMessage No
Returns : boolean

Properties

_isAgentTyping
Default value : false
agentColor
Type : string
Default value : '#29b6f6'
colors
Type : []
Default value : [ '#81c784', '#ffab91', '#f48fb1', '#29b6f6', '#e1bee7', '#9ccc65', '#ffc107', '#26c6da', '#cddc39', ]
colorsIndex
Type : number
Default value : 0
Private lastCount
Default value : -1
messagesContainer
Type : ElementRef
Decorators :
@ViewChild('MessagesContainer', {static: true})
newMessageText
Type : string
Default value : ''
usernameToColor
Type : literal type
Default value : {}
import { Component, Input, Output, EventEmitter, ViewChild, ElementRef, AfterViewChecked } from '@angular/core';
import { IChatMessage, IChatMessageType } from '../../models/chat.message';
import { IChatSettings } from '../../models/chat.settings';

@Component({
  selector: 'app-chat-box',
  templateUrl: './chat-box.component.html',
  styleUrls: ['./chat-box.component.scss'],
})
export class ChatBoxComponent implements AfterViewChecked {
  @Input() settings: IChatSettings;
  @Input() isTyping = false;

  @Input() messages: IChatMessage[];
  private lastCount = -1;

  _isAgentTyping = false;
  @Output() isAgentTyping = new EventEmitter<boolean>();

  @Output() newMessage = new EventEmitter<string>();
  newMessageText = '';

  @ViewChild('MessagesContainer', { static: true }) messagesContainer: ElementRef;

  usernameToColor: { [username: string]: string } = {};
  colors = [
    '#81c784',
    '#ffab91',
    '#f48fb1',
    '#29b6f6',
    '#e1bee7',
    '#9ccc65',
    '#ffc107',
    '#26c6da',
    '#cddc39',
  ];
  colorsIndex = 0;
  agentColor = '#29b6f6';

  constructor() { }

  ngAfterViewChecked() {
    if (this.lastCount !== this.messages.length) {
      this.lastCount = this.messages.length;
      this.scrollToBottom();
    }
  }

  getColor(message: IChatMessage) {
    if (message.type && message.type === IChatMessageType.AGENT) {
      return this.agentColor;
    } else if (message.username) {
      if (!this.usernameToColor[message.username]) {
        this.usernameToColor[message.username] = this.colors[this.colorsIndex];
        this.colorsIndex = (this.colorsIndex + 1) % this.colors.length;
      }
      return this.usernameToColor[message.username];
    } else {
      return this.colors[0];
    }
  }

  shouldAlignRight(message: IChatMessage) {
    return message.type && message.type === IChatMessageType.AGENT;
  }

  sendNewMessage() {
    if (this.newMessageText) {
      this.newMessage.emit(this.newMessageText);
      this.newMessageText = '';
    }
  }

  onNewMessageFocus(event: boolean) {
    const isTyping = event && this.newMessageText.length > 0;
    if (isTyping !== this._isAgentTyping) {
      this._isAgentTyping = isTyping;
      this.isAgentTyping.emit(isTyping);
    }
  }

  onKeyup() {
    const isTyping = this.newMessageText.length > 0;
    if (isTyping !== this._isAgentTyping) {
      this._isAgentTyping = isTyping;
      this.isAgentTyping.emit(isTyping);
    }
  }

  scrollToBottom() {
    try {
      this.messagesContainer.nativeElement.scrollTop = this.messagesContainer.nativeElement.scrollHeight;
    }
    catch (e) { }
  }
}
<div class="chat-box">
  <div #MessagesContainer class="messages" [style.max-height]='settings.maxHeight'>
    <ng-container *ngFor="let message of messages">
      <app-chat-message [alignRight]="shouldAlignRight(message)" [message]="message.text" [username]="message.username"
        [timestamp]="message.timestamp" [image]="message.userIcon" [fallbackImage]="settings.fallbackUserIcon" [color]="getColor(message)">
      </app-chat-message>
    </ng-container>

    <div *ngIf="isTyping" class="typing-indicator">
      Someone is typing
      <span></span>
      <span></span>
      <span></span>
    </div>
  </div>

  <div class="new-message">
    <input #NewMessageText [(ngModel)]="newMessageText" type="text" placeholder="Type a message" (focus)="onNewMessageFocus(true)"
      (blur)="onNewMessageFocus(false)" (keydown.enter)="sendNewMessage()" (keyup)='onKeyup()' [disabled]="settings.disableSendMessage">
    <input type="image" [src]="settings.sendImage" (click)="sendNewMessage()" [disabled]="settings.disableSendMessage">
  </div>
</div>

./chat-box.component.scss

$background-color: #eceff1;

.chat-box{
  background-color: $background-color;
}

.messages {
  padding: 5px;
  overflow: auto;
  min-height: 50px;
}

.new-message{
  border-top: solid black 1px;
  display: flex;
  align-content: center;
  height: 25px;
  input[type=text]{
      flex: 1 1 0;
      background-color: $background-color;
      border: none;
      min-width: 0;
  }
  input[type=image]{
    position: relative;
    top: 50%;
    transform: translateY(-50%);
    height: 20px;
    cursor: pointer;
  }
  input[type=image]:disabled
  {
    opacity:0.5;
    cursor:auto;
  }
  input:focus{
    outline: none;
  }
}

.typing-indicator{
  font-size: 1em;
  font-style: italic;

  $size: 10px;
  span {
    display: inline-block;

    height: $size;
    width: $size;
    border-radius: 50%;
    background-color: #9e9e9e;
    margin: 0 1px;
    opacity: .2;
    @for $i from 1 through 3 {
      &:nth-of-type(#{$i}) {
        animation: 1.5s blink infinite ($i * .33333s);
      }
    }
  }
  @keyframes blink {
    50% {
      opacity: 1;
    }
  }
}
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""