diff --git a/package.json b/package.json index b3ece2a..4443e08 100644 --- a/package.json +++ b/package.json @@ -117,6 +117,7 @@ }, "dependencies": { "browser-encrypt-attachment": "^0.3.0", + "emoji-picker-react": "^3.1.3", "isomorphic-fetch": "^2.2.1", "linkifyjs": "^2.1.9", "matrix-js-sdk": "^4.0.0", @@ -125,6 +126,7 @@ "prop-types": "^15.6.2", "react": "^16.8.6", "react-dom": "^16.8.6", + "react-onclickoutside": "^6.9.0", "react-test-renderer": "^16.13.0", "react-transition-group": "^4.0.0", "uuidv4": "^6.0.2" diff --git a/src/components/_chat.scss b/src/components/_chat.scss index 8dbff05..c3bac66 100644 --- a/src/components/_chat.scss +++ b/src/components/_chat.scss @@ -195,7 +195,7 @@ } &:focus { - background: $theme-light-color; + background-color: $theme-light-color; outline: none; } } @@ -366,36 +366,6 @@ margin-bottom: 0; } - input[placeholder] { - text-overflow: ellipsis; - overflow: hidden; - } - - input[type="text"] { - font-size: 1rem; - padding: 0.5rem; - border: none; - display: flex; - flex: 1 1 auto; - border: 1px solid $dark-color; - background: $white; - color: $dark-color; - font-family: $theme-font; - margin-right: 0.2rem; - transition: all 0.2s ease-in-out; - border-radius: 10px; - - &:hover { - box-shadow: inset 0px 0px 0px 1px $dark-color; - } - - &:focus { - outline: none; - box-shadow: inset 0px 0px 0px 1px $dark-color; - background: $theme-light-color; - } - } - input[type="submit"] { background-color: $theme-color; height: 100%; @@ -423,12 +393,122 @@ background-color: $theme-highlight-color; } } + + .message-input-container { + display: flex; + flex: 1 1 auto; + position: relative; + + input[type="text"] { + font-size: 1rem; + padding: 0.5rem; + padding-right: 32px; + border: none; + display: flex; + flex: 1 1 auto; + background: $white; + color: $dark-color; + font-family: $theme-font; + margin-right: 0.2rem; + transition: all 0.2s ease-in-out; + border-radius: 10px; + border: 1px solid $dark-color; + + &:hover { + box-shadow: inset 0px 0px 0px 1px $dark-color; + } + + &:focus { + outline: none; + box-shadow: inset 0px 0px 0px 1px $dark-color; + background: $theme-light-color; + } + } + + .emoji-button-container { + position: absolute; + right: 6px; + top: 0; + bottom: 0; + height: 100%; + display: flex; + align-items: center; + justify-content: flex-start; + + + button { + transition: all 0.2s ease-in-out; + + &:hover { + box-shadow: none; + } + + &:focus { + outline: none; + } + + emoji-button { + background: transparent; + border: none; + padding: 0; + margin-right: 3px; + transition: all 0.2s ease-in-out; + + &:hover { + svg path#icon { + fill: $theme-color; + } + } + + &:focus { + svg path#icon { + fill: $theme-highlight-color; + } + } + } + } + } + + .emoji-picker { + animation-duration: 0.2s; + animation-fill-mode: forwards; + position: absolute; + bottom: 32px; + right: -4px; + + &-entering { + animation-name: slideInUp; + opacity: 0.5; + } + &-entered { + display: inherit; + visibility: visible; + opacity: 1; + } + &-exiting { + animation-name: slideOutDown; + opacity: 0.5; + } + &-exited { + display: none; + visibility: hidden; + opacity: 0; + } + } + } } .highlight-text { color: $theme-color; } + .pos-relative { + position: relative; + } +} + +.hidden { + display: none; } @media screen and (max-width: 420px){ @@ -448,6 +528,11 @@ } } -.hidden { - display: none; +@media screen and (max-width: 360px){ + #ocrcc-chatbox .input-window .message-input-container .emoji-picker { + position: fixed; + left: 5px; + right: 5px; + bottom: 42px; + } } diff --git a/src/components/chatbox.jsx b/src/components/chatbox.jsx index b2047ab..d1ebbbd 100644 --- a/src/components/chatbox.jsx +++ b/src/components/chatbox.jsx @@ -15,6 +15,7 @@ import {uuid} from "uuidv4" import Message from "./message"; import Dock from "./dock"; import Header from "./header"; +import EmojiSelector from './emoji-selector'; import './styles.scss'; @@ -54,6 +55,7 @@ class ChatBox extends React.Component { roomId: null, typingStatus: null, awaitingAgreement: true, + emojiSelectorOpen: false, } this.state = this.initialState this.chatboxInput = React.createRef(); @@ -74,6 +76,14 @@ class ChatBox extends React.Component { }); } + toggleEmojiSelector = () => { + this.setState({ emojiSelectorOpen: !this.state.emojiSelectorOpen }) + } + + closeEmojiSelector = () => { + this.setState({ emojiSelectorOpen: false }, () => this.chatboxInput.current.focus()) + } + handleWidgetExit = () => { this.setState({ showDock: true, @@ -357,9 +367,13 @@ class ChatBox extends React.Component { } - handleEscape = (e) => { - if (e.keyCode === 27 && this.state.opened) { - this.handleToggleOpen() + handleKeyDown = (e) => { + if (e.keyCode === 27) { + if (this.state.emojiSelectorOpen) { + this.closeEmojiSelector() + } else if (this.state.opened) { + this.handleToggleOpen() + } } } @@ -413,12 +427,12 @@ class ChatBox extends React.Component { } componentDidMount() { - document.addEventListener("keydown", this.handleEscape, false); + document.addEventListener("keydown", this.handleKeyDown, false); window.addEventListener('beforeunload', this.exitChat) } componentWillUnmount() { - document.removeEventListener("keydown", this.handleEscape, false); + document.removeEventListener("keydown", this.handleKeyDown, false); window.removeEventListener('beforeunload', this.exitChat) this.exitChat(); } @@ -449,8 +463,16 @@ class ChatBox extends React.Component { } } + onEmojiClick = (event, emojiObject) => { + const { emoji } = emojiObject; + this.setState({ + inputValue: this.state.inputValue.concat(emoji), + emojiSelectorOpen: false, + }, () => this.chatboxInput.current.focus()) + } + render() { - const { ready, messages, inputValue, userId, roomId, typingStatus, opened, showDock } = this.state; + const { ready, messages, inputValue, userId, roomId, typingStatus, opened, showDock, emojiSelectorOpen } = this.state; const inputLabel = 'Send a message...' return ( @@ -501,16 +523,25 @@ class ChatBox extends React.Component {