A replication of Googles Messenger app with some basic functionality. Using Jade, SCSS, and ES6 via Tracuer and Animate.css for animations.
Forked from Blake Tarter's Pen Material Design Chat.
A Pen by Captain Anonymous on CodePen.
h1.title Material Design Chat | |
.wrapper | |
nav#nav.nav | |
.default-nav | |
.main-nav | |
.toggle | |
.main-nav-item | |
a.main-nav-item-link(href="#") Blake | |
.options | |
.inner#inner | |
.content#content | |
.bottom#bottom | |
textarea.input#input | |
.send#send | |
A replication of Googles Messenger app with some basic functionality. Using Jade, SCSS, and ES6 via Tracuer and Animate.css for animations.
Forked from Blake Tarter's Pen Material Design Chat.
A Pen by Captain Anonymous on CodePen.
class Messenger { | |
constructor() { | |
this.messageList = []; | |
this.deletedList = []; | |
this.me = 1; // completely arbitrary id | |
this.them = 5; // and another one | |
this.onRecieve = (message) => console.log('Recieved: ' + message.text); | |
this.onSend = (message) => console.log('Sent: ' + message.text); | |
this.onDelete = (message) => console.log('Deleted: ' + message.text); | |
} | |
send(text = '') { | |
text = this.filter(text); | |
if (this.validate(text)) { | |
let message = { | |
user: this.me, | |
text: text, | |
time: new Date().getTime() | |
}; | |
this.messageList.push(message); | |
this.onSend(message); | |
} | |
} | |
recieve(text = '') { | |
text = this.filter(text); | |
if (this.validate(text)) { | |
let message = { | |
user: this.them, | |
text: text, | |
time: new Date().getTime() | |
}; | |
this.messageList.push(message); | |
this.onRecieve(message); | |
} | |
} | |
delete(index) { | |
index = index || (this.messageLength - 1); | |
let deleted = this.messageLength.pop(); | |
this.deletedList.push(deleted); | |
this.onDelete(deleted); | |
} | |
filter(input) { | |
let output = input.replace('bad input', 'good output'); // such amazing filter there right? | |
return output; | |
} | |
validate(input) { | |
return !!input.length; // an amazing example of validation I swear. | |
} | |
} | |
class BuildHTML { | |
constructor() { | |
this.messageWrapper = 'message-wrapper'; | |
this.circleWrapper = 'circle-wrapper'; | |
this.textWrapper = 'text-wrapper'; | |
this.meClass = 'me'; | |
this.themClass = 'them'; | |
} | |
_build(text, who) { | |
return `<div class="${this.messageWrapper} ${this[who + 'Class']}"> | |
<div class="${this.circleWrapper} animated bounceIn"></div> | |
<div class="${this.textWrapper}">...</div> | |
</div>`; | |
} | |
me(text) { | |
return this._build(text, 'me'); | |
} | |
them(text) { | |
return this._build(text, 'them'); | |
} | |
} | |
$(document).ready(function() { | |
let messenger = new Messenger(); | |
let buildHTML = new BuildHTML(); | |
let $input = $('#input'); | |
let $send = $('#send'); | |
let $content = $('#content'); | |
let $inner = $('#inner'); | |
function safeText(text) { | |
$content.find('.message-wrapper').last().find('.text-wrapper').text(text); | |
} | |
function animateText() { | |
setTimeout(() => { | |
$content.find('.message-wrapper').last().find('.text-wrapper').addClass('animated fadeIn'); | |
}, 350) | |
} | |
function scrollBottom() { | |
$($inner).animate({ | |
scrollTop: $($content).offset().top + $($content).outerHeight(true) | |
}, { | |
queue: false, | |
duration: 'ease' | |
}); | |
} | |
function buildSent(message) { | |
console.log('sending: ', message.text); | |
$content.append(buildHTML.me(message.text)); | |
safeText(message.text); | |
animateText(); | |
scrollBottom(); | |
} | |
function buildRecieved(message) { | |
console.log('recieving: ', message.text); | |
$content.append(buildHTML.them(message.text)); | |
safeText(message.text); | |
animateText(); | |
scrollBottom(); | |
} | |
function sendMessage() { | |
let text = $input.val(); | |
messenger.send(text); | |
$input.val(''); | |
$input.focus(); | |
} | |
messenger.onSend = buildSent; | |
messenger.onRecieve = buildRecieved; | |
setTimeout(() => { | |
messenger.recieve('Hello there!'); | |
}, 1500); | |
setTimeout(() => { | |
messenger.recieve('Do you like this? If so check out more on my page...'); | |
}, 5000); | |
setTimeout(() => { | |
messenger.recieve('Or maybe just give it a like!'); | |
}, 7500); | |
$input.focus(); | |
$send.on('click', function(e) { | |
sendMessage(); | |
}); | |
$input.on('keydown', function(e) { | |
let key = e.which || e.keyCode; | |
if (key === 13) { // enter key | |
e.preventDefault(); | |
sendMessage(); | |
} | |
}); | |
}); |
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script> |
$blue: #2196F3; | |
$dingy: #495B6A; | |
$white: #ffffff; | |
$purple: #673AB7; | |
$yellow: #FFEB3B; | |
$red: #F44336; | |
$orange: #FF5722; | |
$black: #333333; | |
$nav: 100; | |
$navHeight: 64px; | |
$trans: 0.3s ease; | |
* { | |
box-sizing: border-box; | |
} | |
body { | |
/* position: relative; */ | |
background-color: $orange; | |
} | |
.title { | |
color: $white; | |
text-align: center; | |
font-weight: 100; | |
} | |
.wrapper { | |
height: 520px; | |
width: 320px; | |
overflow: hidden; | |
background-color: white; | |
position: fixed; | |
top: 100px; | |
left: 50%; | |
transform: translateX(-50%); | |
box-shadow: 0px 3px 3px 0px rgba(50, 50, 50, 0.5); | |
.inner { | |
overflow: scroll; | |
height: 520px; | |
padding-top: $navHeight; | |
background: darken($white, 5%); | |
-ms-overflow-style: none; | |
overflow: -moz-scrollbars-none; | |
//gotta hide windows scrollbars | |
&::-webkit-scrollbar { | |
width: 0 !important | |
} | |
.content { | |
padding: ($navHeight - ($navHeight/1.5)) / 2; | |
position: relative; | |
margin-bottom: $navHeight/2; | |
} | |
} | |
transition: $trans; | |
} | |
.nav { | |
position: fixed; | |
top: 0; | |
left: 0; | |
right: 0; | |
height: $navHeight; | |
z-index: $nav; | |
.default-nav { | |
height: $navHeight; | |
width: 100%; | |
position: absolute; | |
left: 0; | |
top: 0; | |
z-index: $nav + 10; | |
background-color: $red; | |
border-bottom: 3px solid darken($red, 10%);; | |
color: $white; | |
-webkit-box-shadow: 0px 3px 3px 0px rgba(50, 50, 50, 0.1); | |
-moz-box-shadow: 0px 3px 3px 0px rgba(50, 50, 50, 0.1); | |
box-shadow: 0px 3px 3px 0px rgba(50, 50, 50, 0.1); | |
.main-nav { | |
position: absolute; | |
left: 0; | |
width: 100%; | |
height: $navHeight; | |
top: 0; | |
margin: 0; | |
padding: 0; | |
list-style: none; | |
.toggle { | |
height: 32px; | |
width: 32px; | |
background: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/104946/ic_arrow_back_white_48dp.png); | |
background-size: contain; | |
margin: 16px; | |
float: left; | |
&:hover { | |
cursor: pointer; | |
} | |
} | |
.options { | |
height: 32px; | |
width: 32px; | |
background: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/104946/ic_more_vert_white_48dp.png); | |
background-size: contain; | |
margin: 16px; | |
position: absolute; | |
right: 0; | |
&:hover { | |
cursor: pointer; | |
} | |
} | |
.main-nav-item { | |
float: left; | |
height: $navHeight; | |
margin-right: 50px; | |
position: relative; | |
// text-align: center; | |
line-height: $navHeight; | |
.main-nav-item-link { | |
display: block; | |
position: relative; | |
height: $navHeight; | |
width: 100%; | |
text-align: center; | |
line-height: $navHeight; | |
text-decoration: none; | |
color: inherit; | |
transition: $trans; | |
} | |
transition: $trans; | |
} | |
transition: $trans; | |
} | |
transition: $trans; | |
} | |
transition: $trans; | |
} | |
.bottom { | |
position: fixed; | |
bottom: 0; left: 0; right: 0; | |
height: $navHeight; | |
background: $white; | |
/* box-shadow: 0px -3px 3px 0px rgba(50, 50, 50, 0.1); */ | |
.input { | |
height: $navHeight; | |
background: $white; | |
border: none; | |
width: calc(100% - #{$navHeight}); | |
position: absolute; | |
left: 0; | |
top: 0; | |
padding: 0 5%; | |
resize: none; | |
overflow: scroll; | |
padding-top: ($navHeight/2) - 8; | |
font-weight: 300; | |
&:focus { | |
outline: none; | |
} | |
-ms-overflow-style: none; | |
overflow: -moz-scrollbars-none; | |
//gotta hide windows scrollbars | |
&::-webkit-scrollbar { | |
width: 0 !important | |
} | |
} | |
.send { | |
position: fixed; | |
height: $navHeight/1.5; | |
width: $navHeight/1.5; | |
border-radius: 50%; | |
border: 0; | |
background: $red; | |
color: $white; | |
bottom: ($navHeight - ($navHeight/1.5)) / 2; | |
right: ($navHeight - ($navHeight/1.5)) / 2; | |
&:before { | |
content: ''; | |
background: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/104946/ic_send_white_48dp.png) no-repeat center center; | |
background-size: $navHeight/2.5; | |
position: absolute; | |
top: 0; left: 0; right: 0; bottom: 0; | |
} | |
&:focus { | |
outline: none; | |
} | |
&:hover { | |
cursor: pointer; | |
} | |
} | |
} | |
.message-wrapper { | |
position: relative; | |
overflow: hidden; | |
width: 100%; | |
margin: (($navHeight - ($navHeight/1.5)) / 2) 0; | |
padding: (($navHeight - ($navHeight/1.5)) / 2) 0; | |
.circle-wrapper { | |
height: $navHeight/1.5; | |
width: $navHeight/1.5; | |
border-radius: 50%; | |
} | |
.text-wrapper { | |
padding: ($navHeight - ($navHeight/1.5)) / 2; | |
min-height: $navHeight/1.5; | |
width: 60%; | |
margin: 0 ($navHeight - ($navHeight/1.5)) / 2; | |
box-shadow: 0px 1px 0px 0px rgba(50, 50, 50, 0.3); | |
border-radius: 2px; | |
font-weight: 300; | |
position: relative; | |
/* word-break: break-all; */ | |
opacity: 0; | |
&:before { | |
content: ''; | |
width: 0; | |
height: 0; | |
border-style: solid; | |
} | |
} | |
&.them { | |
.circle-wrapper, .text-wrapper { | |
background: $red; | |
float: left; | |
color: $white; | |
} | |
.text-wrapper { | |
&:before { | |
border-width: 0 10px 10px 0; | |
border-color: transparent $red transparent transparent; | |
position: absolute; | |
top: 0; | |
left: -9px; | |
} | |
} | |
} | |
&.me { | |
.circle-wrapper, .text-wrapper { | |
background: $orange; | |
float: right; | |
color: $black; | |
} | |
.text-wrapper { | |
background: $white; | |
&:before { | |
border-width: 10px 10px 0 0; | |
border-color: $white transparent transparent transparent; | |
position: absolute; | |
top: 0; | |
right: -9px; | |
} | |
} | |
} | |
} | |
@media (max-width: 560px) { | |
.wrapper { | |
width: 100%; | |
height: 100%; | |
height: 100vh; | |
top: 0; | |
left: 0; | |
transform: translateX(0); | |
.inner { | |
height: 100%; | |
height: 100vh; | |
} | |
} | |
} |
<link href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/104946/animate.min.css" rel="stylesheet" /> |