mirror of
https://github.com/Safe-Support-Chat/ocrcc-chatbox
synced 2024-11-25 04:04:53 +00:00
add emoji selector
This commit is contained in:
parent
f0b9578d66
commit
b4e4356cb7
@ -117,6 +117,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"browser-encrypt-attachment": "^0.3.0",
|
"browser-encrypt-attachment": "^0.3.0",
|
||||||
|
"emoji-picker-react": "^3.1.3",
|
||||||
"isomorphic-fetch": "^2.2.1",
|
"isomorphic-fetch": "^2.2.1",
|
||||||
"linkifyjs": "^2.1.9",
|
"linkifyjs": "^2.1.9",
|
||||||
"matrix-js-sdk": "^4.0.0",
|
"matrix-js-sdk": "^4.0.0",
|
||||||
@ -125,6 +126,7 @@
|
|||||||
"prop-types": "^15.6.2",
|
"prop-types": "^15.6.2",
|
||||||
"react": "^16.8.6",
|
"react": "^16.8.6",
|
||||||
"react-dom": "^16.8.6",
|
"react-dom": "^16.8.6",
|
||||||
|
"react-onclickoutside": "^6.9.0",
|
||||||
"react-test-renderer": "^16.13.0",
|
"react-test-renderer": "^16.13.0",
|
||||||
"react-transition-group": "^4.0.0",
|
"react-transition-group": "^4.0.0",
|
||||||
"uuidv4": "^6.0.2"
|
"uuidv4": "^6.0.2"
|
||||||
|
@ -195,7 +195,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
background: $theme-light-color;
|
background-color: $theme-light-color;
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -366,36 +366,6 @@
|
|||||||
margin-bottom: 0;
|
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"] {
|
input[type="submit"] {
|
||||||
background-color: $theme-color;
|
background-color: $theme-color;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@ -423,12 +393,122 @@
|
|||||||
background-color: $theme-highlight-color;
|
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 {
|
.highlight-text {
|
||||||
color: $theme-color;
|
color: $theme-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pos-relative {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 420px){
|
@media screen and (max-width: 420px){
|
||||||
@ -448,6 +528,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.hidden {
|
@media screen and (max-width: 360px){
|
||||||
display: none;
|
#ocrcc-chatbox .input-window .message-input-container .emoji-picker {
|
||||||
|
position: fixed;
|
||||||
|
left: 5px;
|
||||||
|
right: 5px;
|
||||||
|
bottom: 42px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ import {uuid} from "uuidv4"
|
|||||||
import Message from "./message";
|
import Message from "./message";
|
||||||
import Dock from "./dock";
|
import Dock from "./dock";
|
||||||
import Header from "./header";
|
import Header from "./header";
|
||||||
|
import EmojiSelector from './emoji-selector';
|
||||||
|
|
||||||
import './styles.scss';
|
import './styles.scss';
|
||||||
|
|
||||||
@ -54,6 +55,7 @@ class ChatBox extends React.Component {
|
|||||||
roomId: null,
|
roomId: null,
|
||||||
typingStatus: null,
|
typingStatus: null,
|
||||||
awaitingAgreement: true,
|
awaitingAgreement: true,
|
||||||
|
emojiSelectorOpen: false,
|
||||||
}
|
}
|
||||||
this.state = this.initialState
|
this.state = this.initialState
|
||||||
this.chatboxInput = React.createRef();
|
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 = () => {
|
handleWidgetExit = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
showDock: true,
|
showDock: true,
|
||||||
@ -357,9 +367,13 @@ class ChatBox extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
handleEscape = (e) => {
|
handleKeyDown = (e) => {
|
||||||
if (e.keyCode === 27 && this.state.opened) {
|
if (e.keyCode === 27) {
|
||||||
this.handleToggleOpen()
|
if (this.state.emojiSelectorOpen) {
|
||||||
|
this.closeEmojiSelector()
|
||||||
|
} else if (this.state.opened) {
|
||||||
|
this.handleToggleOpen()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -413,12 +427,12 @@ class ChatBox extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
document.addEventListener("keydown", this.handleEscape, false);
|
document.addEventListener("keydown", this.handleKeyDown, false);
|
||||||
window.addEventListener('beforeunload', this.exitChat)
|
window.addEventListener('beforeunload', this.exitChat)
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
document.removeEventListener("keydown", this.handleEscape, false);
|
document.removeEventListener("keydown", this.handleKeyDown, false);
|
||||||
window.removeEventListener('beforeunload', this.exitChat)
|
window.removeEventListener('beforeunload', this.exitChat)
|
||||||
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() {
|
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...'
|
const inputLabel = 'Send a message...'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -501,16 +523,25 @@ class ChatBox extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
<div className="input-window">
|
<div className="input-window">
|
||||||
<form onSubmit={this.handleSubmit}>
|
<form onSubmit={this.handleSubmit}>
|
||||||
<input
|
<div className="message-input-container">
|
||||||
id="message-input"
|
<input
|
||||||
type="text"
|
id="message-input"
|
||||||
onChange={this.handleInputChange}
|
type="text"
|
||||||
value={inputValue}
|
onChange={this.handleInputChange}
|
||||||
aria-label={inputLabel}
|
value={inputValue}
|
||||||
placeholder={inputLabel}
|
aria-label={inputLabel}
|
||||||
autoFocus={true}
|
placeholder={inputLabel}
|
||||||
ref={this.chatboxInput}
|
autoFocus={true}
|
||||||
/>
|
onFocus={(e) => this.setState({ inputValue: this.state.inputValue })}
|
||||||
|
ref={this.chatboxInput}
|
||||||
|
/>
|
||||||
|
<EmojiSelector
|
||||||
|
onEmojiClick={this.onEmojiClick}
|
||||||
|
emojiSelectorOpen={emojiSelectorOpen}
|
||||||
|
toggleEmojiSelector={this.toggleEmojiSelector}
|
||||||
|
closeEmojiSelector={this.closeEmojiSelector}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<input type="submit" value="Send" id="submit" />
|
<input type="submit" value="Send" id="submit" />
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
47
src/components/emoji-selector.jsx
Normal file
47
src/components/emoji-selector.jsx
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import React from "react"
|
||||||
|
import PropTypes from "prop-types"
|
||||||
|
import { Transition } from 'react-transition-group';
|
||||||
|
import EmojiPicker from 'emoji-picker-react';
|
||||||
|
import onClickOutside from "react-onclickoutside";
|
||||||
|
|
||||||
|
|
||||||
|
const SVG = () => <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path id="icon" fill="#828282" d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm3.5-9c.83 0 1.5-.67 1.5-1.5S16.33 8 15.5 8 14 8.67 14 9.5s.67 1.5 1.5 1.5zm-7 0c.83 0 1.5-.67 1.5-1.5S9.33 8 8.5 8 7 8.67 7 9.5 7.67 11 8.5 11zm3.5 6.5c2.33 0 4.31-1.46 5.11-3.5H6.89c.8 2.04 2.78 3.5 5.11 3.5z"/></svg>
|
||||||
|
|
||||||
|
class EmojiSelector extends React.Component {
|
||||||
|
constructor(props){
|
||||||
|
super(props)
|
||||||
|
}
|
||||||
|
|
||||||
|
handleClickOutside = e => {
|
||||||
|
this.props.closeEmojiSelector()
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { onEmojiClick, emojiSelectorOpen, toggleEmojiSelector } = this.props;
|
||||||
|
|
||||||
|
return(
|
||||||
|
<div className="emoji-button-container">
|
||||||
|
<div className="pos-relative">
|
||||||
|
<Transition in={emojiSelectorOpen} timeout={250}>
|
||||||
|
{
|
||||||
|
status => {
|
||||||
|
return(
|
||||||
|
<div className={`emoji-picker emoji-picker-${status}`} aria-hidden={!emojiSelectorOpen}>
|
||||||
|
<EmojiPicker onEmojiClick={onEmojiClick}/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</Transition>
|
||||||
|
<button id="emoji-button" onClick={toggleEmojiSelector} aria-label="Emoji picker">
|
||||||
|
<SVG />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default onClickOutside(EmojiSelector)
|
||||||
|
|
9
src/components/emoji_icon.svg
Normal file
9
src/components/emoji_icon.svg
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 23.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
|
||||||
|
<path style="fill:none;" d="M0,0h24v24H0V0z"/>
|
||||||
|
<path style="fill:#828282;" d="M12,2C6.5,2,2,6.5,2,12s4.5,10,10,10c5.5,0,10-4.5,10-10S17.5,2,12,2z M12,20c-4.4,0-8-3.6-8-8
|
||||||
|
s3.6-8,8-8s8,3.6,8,8S16.4,20,12,20z M15.5,11c0.8,0,1.5-0.7,1.5-1.5S16.3,8,15.5,8S14,8.7,14,9.5S14.7,11,15.5,11z M8.5,11
|
||||||
|
c0.8,0,1.5-0.7,1.5-1.5S9.3,8,8.5,8S7,8.7,7,9.5S7.7,11,8.5,11z M12,17.5c2.3,0,4.3-1.5,5.1-3.5H6.9C7.7,16,9.7,17.5,12,17.5z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 769 B |
10
yarn.lock
10
yarn.lock
@ -4765,6 +4765,11 @@ email-addresses@^3.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/email-addresses/-/email-addresses-3.1.0.tgz#cabf7e085cbdb63008a70319a74e6136188812fb"
|
resolved "https://registry.yarnpkg.com/email-addresses/-/email-addresses-3.1.0.tgz#cabf7e085cbdb63008a70319a74e6136188812fb"
|
||||||
integrity sha512-k0/r7GrWVL32kZlGwfPNgB2Y/mMXVTq/decgLczm/j34whdaspNrZO8CnXPf1laaHxI6ptUlsnAxN+UAPw+fzg==
|
integrity sha512-k0/r7GrWVL32kZlGwfPNgB2Y/mMXVTq/decgLczm/j34whdaspNrZO8CnXPf1laaHxI6ptUlsnAxN+UAPw+fzg==
|
||||||
|
|
||||||
|
emoji-picker-react@^3.1.3:
|
||||||
|
version "3.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/emoji-picker-react/-/emoji-picker-react-3.1.3.tgz#eaa48d6980701c00d24bdbfa53f44d39aa9b2380"
|
||||||
|
integrity sha512-3VfxTeR6mUsdtXM3mEp9olUSbZsIatRsJhzULujhIz4yO9YanXELwrzWN3ApK7TAniNPMcUM0Z4CzUEbh2IlvQ==
|
||||||
|
|
||||||
emoji-regex@^7.0.1, emoji-regex@^7.0.2:
|
emoji-regex@^7.0.1, emoji-regex@^7.0.2:
|
||||||
version "7.0.3"
|
version "7.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156"
|
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156"
|
||||||
@ -10082,6 +10087,11 @@ react-lifecycles-compat@^3.0.4:
|
|||||||
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
|
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
|
||||||
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
|
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
|
||||||
|
|
||||||
|
react-onclickoutside@^6.9.0:
|
||||||
|
version "6.9.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-onclickoutside/-/react-onclickoutside-6.9.0.tgz#a54bc317ae8cf6131a5d78acea55a11067f37a1f"
|
||||||
|
integrity sha512-8ltIY3bC7oGhj2nPAvWOGi+xGFybPNhJM0V1H8hY/whNcXgmDeaeoCMPPd8VatrpTsUWjb/vGzrmu6SrXVty3A==
|
||||||
|
|
||||||
react-popper-tooltip@^2.8.3:
|
react-popper-tooltip@^2.8.3:
|
||||||
version "2.10.1"
|
version "2.10.1"
|
||||||
resolved "https://registry.yarnpkg.com/react-popper-tooltip/-/react-popper-tooltip-2.10.1.tgz#e10875f31916297c694d64a677d6f8fa0a48b4d1"
|
resolved "https://registry.yarnpkg.com/react-popper-tooltip/-/react-popper-tooltip-2.10.1.tgz#e10875f31916297c694d64a677d6f8fa0a48b4d1"
|
||||||
|
Loading…
Reference in New Issue
Block a user