🎨 Add site and rename app

This commit is contained in:
brunosimon
2017-12-27 15:52:29 +01:00
parent 0e491b0660
commit 657fa4546d
224 changed files with 206 additions and 42 deletions
+3
View File
@@ -0,0 +1,3 @@
<template src="./template.html"></template>
<script src="./script.js"></script>
<style src="./style.styl" lang="stylus" scoped></style>
+223
View File
@@ -0,0 +1,223 @@
import Tooltip from '@/components/tooltip'
export default
{
name: 'chat',
components:
{
Tooltip
},
data()
{
return {
userName: '',
messageText: ''
}
},
computed:
{
open()
{
return this.$store.state.chat.open
},
question()
{
return this.$store.state.chat.question
},
messages()
{
return this.$store.state.chat.messages
},
user()
{
return this.$store.state.chat.user
},
scrollbarWidth()
{
return this.$store.state.scrollbarWidth
},
unreadCount()
{
return this.$store.state.chat.unreadCount
}
},
watch:
{
user(value)
{
this.userName = value.name
},
messages()
{
window.requestAnimationFrame(() =>
{
if(this.atBottom)
{
this.scrollToBottom()
}
})
},
open(value)
{
if(value)
{
window.requestAnimationFrame(() =>
{
// Focus on textearea
this.$textarea.focus()
})
}
},
question(value)
{
if(value)
{
window.requestAnimationFrame(() =>
{
// Focus on textearea
this.$textarea.focus()
// Scroll to bottom
this.scrollToBottom()
})
}
}
},
created()
{
this.atBottom = true
},
mounted()
{
this.$messages = this.$el.querySelector('.messages')
this.$textarea = this.$el.querySelector('.textarea')
this.$innerMessages = this.$messages.querySelector('.inner-messages')
},
methods:
{
onHeaderClick()
{
this.$store.commit('toggleChat')
},
onUserNameChange()
{
this.$store.commit('setPendingUser', { name: this.userName })
},
onUserNameKeyDown(event)
{
// Blur if enter pressed
if(event.keyCode === 13)
{
// Focus on textearea
this.$textarea.focus()
}
},
onMessageTextKeyDown(event)
{
// Enter key pressed but without shift key
if(event.keyCode === 13 && !event.shiftKey)
{
event.preventDefault()
const text = this.messageText.trim()
if(text)
{
// Create and send message
const message = {}
message.text = text
if(this.question)
{
message.file = this.question.file
message.line = this.question.line
message.version = this.question.version
}
this.$store.commit('setPendingMessage', message)
this.messageText = ''
// Reset question
this.$store.commit('setQuestion', null)
// Scroll to bottom
this.scrollToBottom()
}
}
},
onMessagesScroll()
{
this.atBottom = this.$messages.scrollTop + this.$messages.offsetHeight >= this.$innerMessages.offsetHeight - 15
},
onQuestionRemoveClick()
{
// Reset question
this.$store.commit('setQuestion', null)
// Focus on textearea
this.$textarea.focus()
},
onFileClick(file, version, line)
{
// Set file, line and version
this.$store.commit('setFile', file)
this.$store.commit('setLine', line)
window.requestAnimationFrame(() =>
{
this.$store.commit('setVersion', version)
})
// Focus on textearea
this.$textarea.focus()
// Scroll to bottom
this.scrollToBottom()
},
scrollToBottom()
{
this.$messages.scrollTop = this.$innerMessages.offsetHeight - this.$messages.offsetHeight + 15
},
formatMinutes(time)
{
const date = new Date(time)
let minutes = '' + date.getMinutes()
if(minutes.length === 1)
{
minutes = `0${minutes}`
}
let hours = '' + date.getHours()
if(hours.length === 1)
{
hours = `0${hours}`
}
return `${hours}:${minutes}`
}
}
}
+201
View File
@@ -0,0 +1,201 @@
.chat
position fixed
bottom 0
right 123px
color #fff
.trigger
position absolute
bottom 0
right 0
height 40px
padding-left 14px
padding-right 18px
line-height 40px
background #24263A
font-size 14px
white-space nowrap
user-select none
.stroke
position absolute
top 0
left 0
right 0
height 1px
background #4BD1C5
display none
.icon
margin-left 4px
.unread
display inline-block
position absolute
top 7px
left calc(100% - 25px)
width 20px
text-align center
font-size 9px
line-height 9px
color #4BD1C5
&:hover
.stroke
display block
.content
position relative
background #24263A
height 450px
width 300px
overflow hidden
.header
position absolute
top 0
left 0
width 100%
height 40px
padding-left 14px
line-height 40px
font-size 14px
user-select none
.stroke
position absolute
top 0
left 0
right 0
height 1px
background #4BD1C5
display none
.dash
position absolute
top calc(50% - 1px)
right 14px
height 2px
width 12px
background #fff
&:hover
.stroke
display block
.messages
position absolute
top 40px
left 0
right 0
height 330px
padding-top 4px
overflow-y scroll
background #393855
font-size 12px
.message
position relative
padding-left 21px
margin-bottom 10px
.stroke
position absolute
top 0px
left 10px
width 2px
bottom 2px
.message-user
padding-bottom 3px
.message-date
display inline-block
opacity 0
padding-left 4px
padding-bottom 3px
.message-file
display block
padding-bottom 3px
opacity 0.7
&:hover
opacity 1
color #4BD1C5
&:hover
.message-date
opacity 0.4
.user
position absolute
top 374px
left 0
font-size 12px
padding-left 8px
.user-label
opacity 0.7
pointer-events none
user-select none
.user-name
width 185px
padding-left 1px
outline none
border none
&:hover
background rgba(255, 255, 255, 0.075)
.question
position absolute
bottom 0
right 0
padding 4px
font-size 12px
.question-file
position absolute
bottom 5px
right 22px
opacity 0.7
white-space nowrap
pointer-events none
.remove
position absolute
bottom 0
right 0
width 20px
height 20px
padding-top 4px
padding-left 3px
.icon
&.default
display block
&.hover
display none
&:hover
.icon
&.default
display none
&.hover
display block
textarea
position absolute
bottom 0
left 0
font-size 12px
width 100%
height 58px
padding-left 8px
resize none
// opacity 0.8
+45
View File
@@ -0,0 +1,45 @@
<div class="chat">
<a href="#" v-show="!open" class="trigger" @click="onHeaderClick">
<span class="stroke"></span>
Discussions
<img v-if="unreadCount === 0" class="icon default" src="../../../static/svg/chat-star.svg" alt="">
<img v-if="unreadCount > 0" class="icon hover" src="../../../static/svg/chat-star-hover.svg" alt="">
<span v-if="unreadCount > 0" class="unread">{{ unreadCount > 99 ? '99+' : unreadCount }}</span>
<tooltip position="top-left">Chat with other person on the project</tooltip>
</a>
<div class="content" v-show="open">
<a href="#" class="header" @click="onHeaderClick">
<span class="stroke"></span>
<div class="dash"></div>
Discussions
</a>
<div class="messages" :style="`right: -${scrollbarWidth}px;`" @scroll="onMessagesScroll">
<div class="inner-messages">
<div class="message" v-for="message of messages" :key="message.date">
<div class="stroke" :style="`background: ${message.user.color};`"></div>
<span class="message-user" :style="`color: ${message.user.color};`">
{{ message.user.name }}
</span>
<span class="message-date">
{{ formatMinutes(message.time) }}
</span>
<a href="#" class="message-file" v-if="message.file && message.line" @click.prevent="onFileClick(message.file, message.version, message.line)">
{{ message.file }}:{{ message.line }}
</a>
<div class="message-text" v-html="message.text.replace(/\n/g, '<br>')"></div>
</div>
</div>
</div>
<div class="user" v-if="user">
<span class="user-label">Your nickname is</span> <input maxlength="22" class="user-name" :style="`color: ${user.color};`" v-model="userName" @change="onUserNameChange($event)" @keydown="onUserNameKeyDown($event)"/>
</div>
<textarea class="textarea" :style="`width:calc(100% + ${scrollbarWidth}px);padding-right:${scrollbarWidth}px;`" v-model="messageText" @keydown="onMessageTextKeyDown($event)" placeholder="Type you message here..."></textarea>
<div class="question" v-if="question">
<span class="question-file">{{ question.file }}:{{ question.line }}</span>
<a href="#" class="remove" @click.prevent="onQuestionRemoveClick">
<img class="icon default" src="../../../static/svg/cross.svg" alt="">
<img class="icon hover" src="../../../static/svg/cross-hover.svg" alt="">
</a>
</div>
</div>
</div>