├── .gitignore
├── es
├── assets
│ ├── close-icon.png
│ ├── logo-no-bg.svg
│ ├── chat-icon.svg
│ ├── close.svg
│ └── file.svg
├── index.js
├── styles
│ ├── index.js
│ ├── header.css
│ ├── index.css
│ ├── emojiPicker.css
│ ├── launcher.css
│ ├── message.css
│ └── userInput.css
├── components
│ ├── Messages
│ │ ├── EmojiMessage.js
│ │ ├── TextMessage.js
│ │ ├── FileMessage.js
│ │ └── index.js
│ ├── Header.js
│ ├── MessageList.js
│ ├── icons
│ │ ├── SendIcon.js
│ │ ├── FileIcon.js
│ │ └── EmojiIcon.js
│ ├── ChatWindow.js
│ ├── emoji-picker
│ │ ├── EmojiPicker.js
│ │ └── emojiData.js
│ ├── Launcher.js
│ └── UserInput.js
├── messageTypes.js
└── services
│ └── messageBroker.js
├── lib
├── assets
│ ├── close-icon.png
│ ├── logo-no-bg.svg
│ ├── chat-icon.svg
│ ├── close.svg
│ └── file.svg
├── styles
│ ├── index.js
│ ├── header.css
│ ├── index.css
│ ├── emojiPicker.css
│ ├── launcher.css
│ ├── message.css
│ └── userInput.css
├── messageTypes.js
├── index.js
├── components
│ ├── Messages
│ │ ├── EmojiMessage.js
│ │ ├── TextMessage.js
│ │ ├── FileMessage.js
│ │ └── index.js
│ ├── MessageList.js
│ ├── Header.js
│ ├── icons
│ │ ├── SendIcon.js
│ │ ├── FileIcon.js
│ │ └── EmojiIcon.js
│ ├── ChatWindow.js
│ ├── emoji-picker
│ │ ├── EmojiPicker.js
│ │ └── emojiData.js
│ ├── Launcher.js
│ └── UserInput.js
└── services
│ └── messageBroker.js
├── src
├── assets
│ ├── close-icon.png
│ ├── logo-no-bg.svg
│ ├── chat-icon.svg
│ ├── close.svg
│ └── file.svg
├── index.js
├── styles
│ ├── index.js
│ ├── header.css
│ ├── index.css
│ ├── emojiPicker.css
│ ├── launcher.css
│ ├── message.css
│ └── userInput.css
├── components
│ ├── Messages
│ │ ├── EmojiMessage.js
│ │ ├── TextMessage.js
│ │ ├── index.js
│ │ └── FileMessage.js
│ ├── MessageList.js
│ ├── Header.js
│ ├── icons
│ │ ├── SendIcon.js
│ │ ├── FileIcon.js
│ │ └── EmojiIcon.js
│ ├── ChatWindow.js
│ ├── emoji-picker
│ │ └── EmojiPicker.js
│ ├── Launcher.js
│ └── UserInput.js
├── messageTypes.js
└── services
│ └── messageBroker.js
├── umd
├── close-icon.c30463a5.png
├── main.2176b2e1.css.map
├── logo-no-bg.7718b3e3.svg
├── chat-icon.e0d2b748.svg
├── close.c4c396d3.svg
└── file.037acab7.svg
├── demo
├── dist
│ ├── demo.a318e8d5.css.map
│ ├── close-icon.c30463a5.png
│ ├── logo-no-bg.7718b3e3.svg
│ ├── chat-icon.e0d2b748.svg
│ ├── close.c4c396d3.svg
│ ├── manifest.4a1ee271.js
│ ├── index.html
│ └── file.037acab7.svg
├── assets
│ └── styles
│ │ ├── index.js
│ │ ├── footer.css
│ │ ├── header.css
│ │ ├── base.css
│ │ └── test-area.css
└── src
│ ├── Header.js
│ ├── Footer.js
│ ├── TestArea.js
│ ├── messageHistory.js
│ └── index.js
├── nwb.config.js
├── .travis.yml
├── CONTRIBUTING.md
├── package.json
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | /coverage
2 | /node_modules
3 | npm-debug.log*
4 | .DS_Store
5 |
--------------------------------------------------------------------------------
/es/assets/close-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mattmezza/react-beautiful-chat/HEAD/es/assets/close-icon.png
--------------------------------------------------------------------------------
/lib/assets/close-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mattmezza/react-beautiful-chat/HEAD/lib/assets/close-icon.png
--------------------------------------------------------------------------------
/src/assets/close-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mattmezza/react-beautiful-chat/HEAD/src/assets/close-icon.png
--------------------------------------------------------------------------------
/umd/close-icon.c30463a5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mattmezza/react-beautiful-chat/HEAD/umd/close-icon.c30463a5.png
--------------------------------------------------------------------------------
/umd/main.2176b2e1.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":[],"names":[],"mappings":"","file":"main.2176b2e1.css","sourceRoot":""}
--------------------------------------------------------------------------------
/demo/dist/demo.a318e8d5.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":[],"names":[],"mappings":"","file":"demo.a318e8d5.css","sourceRoot":""}
--------------------------------------------------------------------------------
/demo/assets/styles/index.js:
--------------------------------------------------------------------------------
1 | import "./base.css";
2 | import "./header.css";
3 | import "./test-area.css";
4 | import "./footer.css";
--------------------------------------------------------------------------------
/demo/dist/close-icon.c30463a5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mattmezza/react-beautiful-chat/HEAD/demo/dist/close-icon.c30463a5.png
--------------------------------------------------------------------------------
/es/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import './styles';
3 | import Launcher from './components/Launcher';
4 |
5 | export { Launcher };
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import './styles';
3 | import Launcher from './components/Launcher';
4 |
5 |
6 | export { Launcher };
7 |
--------------------------------------------------------------------------------
/es/styles/index.js:
--------------------------------------------------------------------------------
1 | import './emojiPicker.css';
2 | import './index.css';
3 | import './launcher.css';
4 | import './header.css';
5 | import './message.css';
6 | import './userInput.css';
--------------------------------------------------------------------------------
/src/styles/index.js:
--------------------------------------------------------------------------------
1 | import './emojiPicker.css';
2 | import './index.css';
3 | import './launcher.css';
4 | import './header.css';
5 | import './message.css';
6 | import './userInput.css';
--------------------------------------------------------------------------------
/src/components/Messages/EmojiMessage.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 |
3 |
4 | const EmojiMessage = (props) => {
5 | return
{props.data.emoji}
6 | }
7 |
8 | export default EmojiMessage
--------------------------------------------------------------------------------
/nwb.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | type: 'react-component',
3 | npm: {
4 | esModules: true,
5 | umd: {
6 | global: 'liveChat',
7 | externals: {
8 | react: 'React'
9 | }
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/lib/styles/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | require('./emojiPicker.css');
4 |
5 | require('./index.css');
6 |
7 | require('./launcher.css');
8 |
9 | require('./header.css');
10 |
11 | require('./message.css');
12 |
13 | require('./userInput.css');
--------------------------------------------------------------------------------
/demo/assets/styles/footer.css:
--------------------------------------------------------------------------------
1 | .demo-footer {
2 | width: 1100px;
3 | display: flex;
4 | margin: auto;
5 | justify-content: space-between;
6 | padding: 20px 0px;
7 | border-top: solid 2px #F0F4FA;
8 | color: #8694AB;
9 | font-weight: 100;
10 | font-size: 14px;
11 | }
--------------------------------------------------------------------------------
/es/components/Messages/EmojiMessage.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | var EmojiMessage = function EmojiMessage(props) {
4 | return React.createElement(
5 | "div",
6 | { className: "sc-message--emoji" },
7 | props.data.emoji
8 | );
9 | };
10 |
11 | export default EmojiMessage;
--------------------------------------------------------------------------------
/es/messageTypes.js:
--------------------------------------------------------------------------------
1 | var MESSAGE_TYPES = {
2 | CLIENT: {
3 | NEW_VISITOR: 'client.new_visitor',
4 | MESSAGE: 'client.message',
5 | RETURNING_VISITOR: 'client.returning_visitor'
6 | },
7 | BROKER: {
8 | VISITOR_ID: 'broker.visitor_id',
9 | MESSAGE: 'broker.message'
10 | }
11 | };
12 |
13 | module.exports = MESSAGE_TYPES;
--------------------------------------------------------------------------------
/src/messageTypes.js:
--------------------------------------------------------------------------------
1 | const MESSAGE_TYPES = {
2 | CLIENT: {
3 | NEW_VISITOR: 'client.new_visitor',
4 | MESSAGE: 'client.message',
5 | RETURNING_VISITOR: 'client.returning_visitor',
6 | },
7 | BROKER: {
8 | VISITOR_ID: 'broker.visitor_id',
9 | MESSAGE: 'broker.message',
10 | },
11 | };
12 |
13 | module.exports = MESSAGE_TYPES;
14 |
--------------------------------------------------------------------------------
/lib/messageTypes.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var MESSAGE_TYPES = {
4 | CLIENT: {
5 | NEW_VISITOR: 'client.new_visitor',
6 | MESSAGE: 'client.message',
7 | RETURNING_VISITOR: 'client.returning_visitor'
8 | },
9 | BROKER: {
10 | VISITOR_ID: 'broker.visitor_id',
11 | MESSAGE: 'broker.message'
12 | }
13 | };
14 |
15 | module.exports = MESSAGE_TYPES;
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 |
3 | language: node_js
4 | node_js:
5 | - 6
6 |
7 | before_install:
8 | - npm install codecov.io coveralls
9 |
10 | after_success:
11 | - cat ./coverage/lcov.info | ./node_modules/codecov.io/bin/codecov.io.js
12 | - cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js
13 |
14 | branches:
15 | only:
16 | - master
17 |
--------------------------------------------------------------------------------
/demo/src/Header.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 |
3 | class Header extends Component {
4 | render () {
5 | return (
6 |
7 |
react-beautiful-chat
8 |
11 |
12 | )
13 | }
14 | }
15 |
16 | export default Header
--------------------------------------------------------------------------------
/lib/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | exports.__esModule = true;
4 | exports.Launcher = undefined;
5 |
6 | var _react = require('react');
7 |
8 | var _react2 = _interopRequireDefault(_react);
9 |
10 | require('./styles');
11 |
12 | var _Launcher = require('./components/Launcher');
13 |
14 | var _Launcher2 = _interopRequireDefault(_Launcher);
15 |
16 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
17 |
18 | exports.Launcher = _Launcher2.default;
--------------------------------------------------------------------------------
/lib/components/Messages/EmojiMessage.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | exports.__esModule = true;
4 |
5 | var _react = require("react");
6 |
7 | var _react2 = _interopRequireDefault(_react);
8 |
9 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
10 |
11 | var EmojiMessage = function EmojiMessage(props) {
12 | return _react2.default.createElement(
13 | "div",
14 | { className: "sc-message--emoji" },
15 | props.data.emoji
16 | );
17 | };
18 |
19 | exports.default = EmojiMessage;
20 | module.exports = exports["default"];
--------------------------------------------------------------------------------
/src/components/MessageList.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import Message from './Messages'
3 |
4 | class MessageList extends Component {
5 |
6 | componentDidUpdate(prevProps, prevState) {
7 | this.scrollList.scrollTop = this.scrollList.scrollHeight;
8 | }
9 |
10 | render () {
11 | return (
12 | this.scrollList = el}>
13 | {this.props.messages.map((message, i) => {
14 | return
15 | })}
16 |
)
17 | }
18 | }
19 |
20 | export default MessageList
--------------------------------------------------------------------------------
/src/components/Header.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import closeIcon from './../assets/close-icon.png';
3 |
4 |
5 | class Header extends Component {
6 |
7 | render() {
8 | return (
9 |
10 |
11 |
{this.props.teamName}
12 |
13 |
14 |
15 |
16 | );
17 | }
18 | }
19 |
20 | export default Header;
21 |
--------------------------------------------------------------------------------
/demo/assets/styles/header.css:
--------------------------------------------------------------------------------
1 | .demo-header {
2 | width: 100%;
3 | color: #4e8cff;
4 | display: flex;
5 | justify-content: space-between;
6 | max-width: 1100px;
7 | margin: auto;
8 | padding: 40px 0px;
9 | }
10 |
11 | .demo-header--title {
12 | font-size: 24px;
13 | font-weight: 600;
14 | }
15 |
16 | .demo-header--links {
17 | width: 200px;
18 | display: flex;
19 | justify-content: space-between;
20 | font-size: 18px;
21 | font-weight: 500;
22 | }
23 |
24 | .demo-header--links a {
25 | color: #4e8cff;
26 | text-decoration: none;
27 | cursor: pointer;
28 | }
29 |
30 | .demo-header--links a:hover {
31 | color: #4983ee;
32 | }
--------------------------------------------------------------------------------
/demo/src/Footer.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 |
3 | class Footer extends Component {
4 | render () {
5 | return (
6 |
7 |
8 |
© {new Date().getFullYear()} mattmezza/react-beautiful-chat
9 |
10 |
11 |
12 |
Improved version of react-chat-window
13 |
14 |
15 | )
16 | }
17 | }
18 |
19 | export default Footer
--------------------------------------------------------------------------------
/src/components/Messages/TextMessage.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import chatIconUrl from './../../assets/chat-icon.svg';
3 |
4 | const TextMessage = (props) => {
5 | const meta = props.message.data.meta || null
6 | const text = props.message.data.text || ''
7 | const author = props.message.author
8 | return (
9 |
10 | {
11 | props.message &&
12 | author === "me" &&
13 | props.onDelete &&
14 |
props.onDelete(props.message)}>
15 | x
16 |
17 | }
18 | {text}
19 | {meta &&
{meta}
}
20 |
21 | )
22 | }
23 |
24 | export default TextMessage
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ## Prerequisites
2 |
3 | [Node.js](http://nodejs.org/) >= v4 must be installed.
4 |
5 | ## Installation
6 |
7 | - Running `npm install` in the components's root directory will install everything you need for development.
8 |
9 | ## Demo Development Server
10 |
11 | - `npm start` will run a development server with the component's demo app at [http://localhost:3000](http://localhost:3000) with hot module reloading.
12 |
13 | ## Running Tests
14 |
15 | - `npm test` will run the tests once.
16 |
17 | - `npm run test:coverage` will run the tests and produce a coverage report in `coverage/`.
18 |
19 | - `npm run test:watch` will run the tests on every change.
20 |
21 | ## Building
22 |
23 | - `npm run build` will build the component for publishing to npm and also bundle the demo app.
24 |
25 | - `npm run clean` will delete built resources.
26 |
--------------------------------------------------------------------------------
/es/components/Messages/TextMessage.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import chatIconUrl from './../../assets/chat-icon.svg';
3 |
4 | var TextMessage = function TextMessage(props) {
5 | var meta = props.message.data.meta || null;
6 | var text = props.message.data.text || '';
7 | var author = props.message.author;
8 | return React.createElement(
9 | 'div',
10 | { className: 'sc-message--text' },
11 | props.message && author === "me" && props.onDelete && React.createElement(
12 | 'button',
13 | { className: 'delete-message', onClick: function onClick() {
14 | return props.onDelete(props.message);
15 | } },
16 | 'x'
17 | ),
18 | text,
19 | meta && React.createElement(
20 | 'p',
21 | { className: 'sc-message--meta' },
22 | meta
23 | )
24 | );
25 | };
26 |
27 | export default TextMessage;
--------------------------------------------------------------------------------
/demo/assets/styles/base.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding: 0px;
3 | margin: 0px;
4 | }
5 |
6 | * {
7 | font-family: Avenir Next, Helvetica Neue, Helvetica,sans-serif;
8 | }
9 |
10 | .demo-description {
11 | max-width: 500px;
12 | }
13 |
14 | .demo-description img {
15 | max-width: 500px;
16 | }
17 |
18 | .demo-test-area {
19 | width: 300px;
20 | box-sizing: border-box;
21 | }
22 |
23 | .demo-test-area--text {
24 | box-sizing: border-box;
25 | width: 100%;
26 | margin: 0px;
27 | padding: 0px;
28 | resize: none;
29 | font-family: Avenir Next, Helvetica Neue, Helvetica,sans-serif;
30 | background: #fafbfc;
31 | color: #8da2b5;
32 | border: 1px solid #dde5ed;
33 | font-size: 16px;
34 | padding: 16px 15px 14px;
35 | margin: 0;
36 | border-radius: 6px;
37 | outline: none;
38 | height: 150px;
39 | margin-bottom: 10px;
40 | }
41 |
42 | .demo-monster-img {
43 | width: 400px;
44 | display: block;
45 | margin: 60px auto;
46 | }
--------------------------------------------------------------------------------
/es/assets/logo-no-bg.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lib/assets/logo-no-bg.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/logo-no-bg.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/umd/logo-no-bg.7718b3e3.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/demo/dist/logo-no-bg.7718b3e3.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/es/assets/chat-icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lib/assets/chat-icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/chat-icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/umd/chat-icon.e0d2b748.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/demo/dist/chat-icon.e0d2b748.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/es/assets/close.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/lib/assets/close.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/src/assets/close.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/umd/close.c4c396d3.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/demo/dist/close.c4c396d3.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/es/styles/header.css:
--------------------------------------------------------------------------------
1 | .sc-header {
2 | background: #4e8cff;
3 | min-height: 75px;
4 | border-top-left-radius: 9px;
5 | border-top-right-radius: 9px;
6 | color: white;
7 | padding: 10px;
8 | box-shadow: 0 1px 4px rgba(0,0,0,.2);
9 | position: relative;
10 | box-sizing: border-box;
11 | display: flex;
12 | }
13 |
14 | .sc-header--img {
15 | border-radius: 50%;
16 | align-self: center;
17 | padding: 10px;
18 | }
19 |
20 | .sc-header--team-name {
21 | align-self: center;
22 | padding: 10px;
23 | flex: 1;
24 | user-select: none;
25 | cursor: pointer;
26 | border-radius: 5px;
27 | }
28 |
29 | .sc-header--team-name:hover {
30 | background: #4882ed;
31 | }
32 |
33 | .sc-header--close-button {
34 | width: 40px;
35 | align-self: center;
36 | height: 40px;
37 | margin-right: 10px;
38 | box-sizing: border-box;
39 | cursor: pointer;
40 | border-radius: 5px;
41 | }
42 |
43 | .sc-header--close-button:hover {
44 | background: #4882ed;
45 | }
46 |
47 | .sc-header--close-button img {
48 | width: 100%;
49 | height: 100%;
50 | padding: 13px;
51 | box-sizing: border-box;
52 | }
53 |
54 | @media (max-width: 450px) {
55 | .sc-header {
56 | border-radius: 0px;
57 | }
58 | }
--------------------------------------------------------------------------------
/lib/styles/header.css:
--------------------------------------------------------------------------------
1 | .sc-header {
2 | background: #4e8cff;
3 | min-height: 75px;
4 | border-top-left-radius: 9px;
5 | border-top-right-radius: 9px;
6 | color: white;
7 | padding: 10px;
8 | box-shadow: 0 1px 4px rgba(0,0,0,.2);
9 | position: relative;
10 | box-sizing: border-box;
11 | display: flex;
12 | }
13 |
14 | .sc-header--img {
15 | border-radius: 50%;
16 | align-self: center;
17 | padding: 10px;
18 | }
19 |
20 | .sc-header--team-name {
21 | align-self: center;
22 | padding: 10px;
23 | flex: 1;
24 | user-select: none;
25 | cursor: pointer;
26 | border-radius: 5px;
27 | }
28 |
29 | .sc-header--team-name:hover {
30 | background: #4882ed;
31 | }
32 |
33 | .sc-header--close-button {
34 | width: 40px;
35 | align-self: center;
36 | height: 40px;
37 | margin-right: 10px;
38 | box-sizing: border-box;
39 | cursor: pointer;
40 | border-radius: 5px;
41 | }
42 |
43 | .sc-header--close-button:hover {
44 | background: #4882ed;
45 | }
46 |
47 | .sc-header--close-button img {
48 | width: 100%;
49 | height: 100%;
50 | padding: 13px;
51 | box-sizing: border-box;
52 | }
53 |
54 | @media (max-width: 450px) {
55 | .sc-header {
56 | border-radius: 0px;
57 | }
58 | }
--------------------------------------------------------------------------------
/src/styles/header.css:
--------------------------------------------------------------------------------
1 | .sc-header {
2 | background: #4e8cff;
3 | min-height: 75px;
4 | border-top-left-radius: 9px;
5 | border-top-right-radius: 9px;
6 | color: white;
7 | padding: 10px;
8 | box-shadow: 0 1px 4px rgba(0,0,0,.2);
9 | position: relative;
10 | box-sizing: border-box;
11 | display: flex;
12 | }
13 |
14 | .sc-header--img {
15 | border-radius: 50%;
16 | align-self: center;
17 | padding: 10px;
18 | }
19 |
20 | .sc-header--team-name {
21 | align-self: center;
22 | padding: 10px;
23 | flex: 1;
24 | user-select: none;
25 | cursor: pointer;
26 | border-radius: 5px;
27 | }
28 |
29 | .sc-header--team-name:hover {
30 | background: #4882ed;
31 | }
32 |
33 | .sc-header--close-button {
34 | width: 40px;
35 | align-self: center;
36 | height: 40px;
37 | margin-right: 10px;
38 | box-sizing: border-box;
39 | cursor: pointer;
40 | border-radius: 5px;
41 | }
42 |
43 | .sc-header--close-button:hover {
44 | background: #4882ed;
45 | }
46 |
47 | .sc-header--close-button img {
48 | width: 100%;
49 | height: 100%;
50 | padding: 13px;
51 | box-sizing: border-box;
52 | }
53 |
54 | @media (max-width: 450px) {
55 | .sc-header {
56 | border-radius: 0px;
57 | }
58 | }
--------------------------------------------------------------------------------
/lib/components/Messages/TextMessage.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | exports.__esModule = true;
4 |
5 | var _react = require('react');
6 |
7 | var _react2 = _interopRequireDefault(_react);
8 |
9 | var _chatIcon = require('./../../assets/chat-icon.svg');
10 |
11 | var _chatIcon2 = _interopRequireDefault(_chatIcon);
12 |
13 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
14 |
15 | var TextMessage = function TextMessage(props) {
16 | var meta = props.message.data.meta || null;
17 | var text = props.message.data.text || '';
18 | var author = props.message.author;
19 | return _react2.default.createElement(
20 | 'div',
21 | { className: 'sc-message--text' },
22 | props.message && author === "me" && props.onDelete && _react2.default.createElement(
23 | 'button',
24 | { className: 'delete-message', onClick: function onClick() {
25 | return props.onDelete(props.message);
26 | } },
27 | 'x'
28 | ),
29 | text,
30 | meta && _react2.default.createElement(
31 | 'p',
32 | { className: 'sc-message--meta' },
33 | meta
34 | )
35 | );
36 | };
37 |
38 | exports.default = TextMessage;
39 | module.exports = exports['default'];
--------------------------------------------------------------------------------
/es/styles/index.css:
--------------------------------------------------------------------------------
1 | .sc-chat-window {
2 | width: 370px;
3 | height: calc(100% - 120px);
4 | max-height: 590px;
5 | position: fixed;
6 | right: 25px;
7 | bottom: 100px;
8 | box-sizing: border-box;
9 | box-shadow: 0px 7px 40px 2px rgba(148, 149, 150, 0.3);
10 | background: white;
11 | display: flex;
12 | flex-direction: column;
13 | justify-content: space-between;
14 | transition: 0.3s ease-in-out;
15 | border-radius: 10px;
16 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
17 | }
18 |
19 | .sc-chat-window.closed {
20 | opacity: 0;
21 | visibility: hidden;
22 | bottom: 90px;
23 | }
24 |
25 | .sc-message-list {
26 | height: 80%;
27 | overflow-y: auto;
28 | background-color: white;
29 | background-size: 100%;
30 | padding: 40px 0px;
31 | }
32 |
33 | .sc-message--me {
34 | text-align: right;
35 | }
36 | .sc-message--them {
37 | text-align: left;
38 | }
39 |
40 | @media (max-width: 450px) {
41 | .sc-chat-window {
42 | width: 100%;
43 | height: 100%;
44 | max-height: 100%;
45 | right: 0px;
46 | bottom: 0px;
47 | border-radius: 0px;
48 | }
49 | .sc-chat-window {
50 | transition: 0.1s ease-in-out;
51 | }
52 | .sc-chat-window.closed {
53 | bottom: 0px;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/lib/styles/index.css:
--------------------------------------------------------------------------------
1 | .sc-chat-window {
2 | width: 370px;
3 | height: calc(100% - 120px);
4 | max-height: 590px;
5 | position: fixed;
6 | right: 25px;
7 | bottom: 100px;
8 | box-sizing: border-box;
9 | box-shadow: 0px 7px 40px 2px rgba(148, 149, 150, 0.3);
10 | background: white;
11 | display: flex;
12 | flex-direction: column;
13 | justify-content: space-between;
14 | transition: 0.3s ease-in-out;
15 | border-radius: 10px;
16 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
17 | }
18 |
19 | .sc-chat-window.closed {
20 | opacity: 0;
21 | visibility: hidden;
22 | bottom: 90px;
23 | }
24 |
25 | .sc-message-list {
26 | height: 80%;
27 | overflow-y: auto;
28 | background-color: white;
29 | background-size: 100%;
30 | padding: 40px 0px;
31 | }
32 |
33 | .sc-message--me {
34 | text-align: right;
35 | }
36 | .sc-message--them {
37 | text-align: left;
38 | }
39 |
40 | @media (max-width: 450px) {
41 | .sc-chat-window {
42 | width: 100%;
43 | height: 100%;
44 | max-height: 100%;
45 | right: 0px;
46 | bottom: 0px;
47 | border-radius: 0px;
48 | }
49 | .sc-chat-window {
50 | transition: 0.1s ease-in-out;
51 | }
52 | .sc-chat-window.closed {
53 | bottom: 0px;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/styles/index.css:
--------------------------------------------------------------------------------
1 | .sc-chat-window {
2 | width: 370px;
3 | height: calc(100% - 120px);
4 | max-height: 590px;
5 | position: fixed;
6 | right: 25px;
7 | bottom: 100px;
8 | box-sizing: border-box;
9 | box-shadow: 0px 7px 40px 2px rgba(148, 149, 150, 0.3);
10 | background: white;
11 | display: flex;
12 | flex-direction: column;
13 | justify-content: space-between;
14 | transition: 0.3s ease-in-out;
15 | border-radius: 10px;
16 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
17 | }
18 |
19 | .sc-chat-window.closed {
20 | opacity: 0;
21 | visibility: hidden;
22 | bottom: 90px;
23 | }
24 |
25 | .sc-message-list {
26 | height: 80%;
27 | overflow-y: auto;
28 | background-color: white;
29 | background-size: 100%;
30 | padding: 40px 0px;
31 | }
32 |
33 | .sc-message--me {
34 | text-align: right;
35 | }
36 | .sc-message--them {
37 | text-align: left;
38 | }
39 |
40 | @media (max-width: 450px) {
41 | .sc-chat-window {
42 | width: 100%;
43 | height: 100%;
44 | max-height: 100%;
45 | right: 0px;
46 | bottom: 0px;
47 | border-radius: 0px;
48 | }
49 | .sc-chat-window {
50 | transition: 0.1s ease-in-out;
51 | }
52 | .sc-chat-window.closed {
53 | bottom: 0px;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/components/Messages/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import TextMessage from './TextMessage'
3 | import EmojiMessage from './EmojiMessage'
4 | import FileMessage from './FileMessage'
5 | import chatIconUrl from './../../assets/chat-icon.svg'
6 |
7 |
8 | class Message extends Component {
9 |
10 | _renderMessageOfType(type) {
11 | switch (type) {
12 | case 'text':
13 | return
14 | case 'emoji':
15 | return
16 | case 'file':
17 | return
18 | }
19 | }
20 |
21 | render() {
22 | let contentClassList = [
23 | "sc-message--content",
24 | (this.props.message.author === "me" ? "sent" : "received")
25 | ];
26 | return (
27 |
28 |
29 |
32 | {this._renderMessageOfType(this.props.message.type)}
33 |
34 |
)
35 | }
36 | }
37 |
38 | export default Message
--------------------------------------------------------------------------------
/src/components/Messages/FileMessage.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import genericFileIcon from '../../assets/file.svg';
3 | import closeIcon from '../../assets/close.svg'
4 |
5 | const FileMessage = (props) => {
6 | const meta = props.message.data.meta || null
7 | const text = props.message.data.text || ''
8 | const file = props.message.data.file
9 | const author = props.message.author
10 | return (
11 |
12 | {
13 | props.message &&
14 | author === "me" &&
15 | props.onDelete &&
16 |
props.onDelete(props.message)}>
17 | x
18 |
19 | }
20 |
25 |
28 |
29 | {text}
30 |
31 | {meta &&
{meta}
}
32 |
33 | )
34 | }
35 |
36 | export default FileMessage
--------------------------------------------------------------------------------
/src/components/icons/SendIcon.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | class SendIcon extends Component {
4 |
5 | render() {
6 | return (
7 | { e.preventDefault(); this.props.onClick(e); }}
11 | className="sc-user-input--send-icon-wrapper"
12 | >
13 |
23 |
24 |
28 |
29 |
30 |
31 | );
32 | }
33 | }
34 |
35 | export default SendIcon;
36 |
--------------------------------------------------------------------------------
/es/styles/emojiPicker.css:
--------------------------------------------------------------------------------
1 | .sc-emoji-picker {
2 | position: absolute;
3 | bottom: 50px;
4 | right: 0px;
5 | width: 330px;
6 | max-height: 215px;
7 | box-shadow: 0px 7px 40px 2px rgba(148, 149, 150, 0.3);
8 | background: white;
9 | border-radius: 10px;
10 | outline: none;
11 | }
12 |
13 | .sc-emoji-picker:after {
14 | content: "";
15 | width: 14px;
16 | height: 14px;
17 | background: white;
18 | position: absolute;
19 | bottom: -6px;
20 | right: 30px;
21 | transform: rotate(45deg);
22 | border-radius: 2px;
23 | }
24 |
25 | .sc-emoji-picker--content {
26 | padding: 10px;
27 | overflow: auto;
28 | width: 100%;
29 | max-height: 195px;
30 | margin-top: 7px;
31 | box-sizing: border-box;
32 | }
33 |
34 | .sc-emoji-picker--category {
35 | display: flex;
36 | flex-direction: row;
37 | flex-wrap: wrap;
38 | }
39 |
40 | .sc-emoji-picker--category-title {
41 | min-width: 100%;
42 | color: #b8c3ca;
43 | font-weight: 200;
44 | font-size: 13px;
45 | margin: 5px;
46 | letter-spacing: 1px;
47 | }
48 |
49 | .sc-emoji-picker--emoji {
50 | margin: 5px;
51 | width: 30px;
52 | line-height: 30px;
53 | text-align: center;
54 | cursor: pointer;
55 | vertical-align: middle;
56 | font-size: 28px;
57 | transition: transform 60ms ease-out,-webkit-transform 60ms ease-out;
58 | }
59 |
60 | .sc-emoji-picker--emoji:hover {
61 | transform: scale(1.4);
62 | }
--------------------------------------------------------------------------------
/lib/styles/emojiPicker.css:
--------------------------------------------------------------------------------
1 | .sc-emoji-picker {
2 | position: absolute;
3 | bottom: 50px;
4 | right: 0px;
5 | width: 330px;
6 | max-height: 215px;
7 | box-shadow: 0px 7px 40px 2px rgba(148, 149, 150, 0.3);
8 | background: white;
9 | border-radius: 10px;
10 | outline: none;
11 | }
12 |
13 | .sc-emoji-picker:after {
14 | content: "";
15 | width: 14px;
16 | height: 14px;
17 | background: white;
18 | position: absolute;
19 | bottom: -6px;
20 | right: 30px;
21 | transform: rotate(45deg);
22 | border-radius: 2px;
23 | }
24 |
25 | .sc-emoji-picker--content {
26 | padding: 10px;
27 | overflow: auto;
28 | width: 100%;
29 | max-height: 195px;
30 | margin-top: 7px;
31 | box-sizing: border-box;
32 | }
33 |
34 | .sc-emoji-picker--category {
35 | display: flex;
36 | flex-direction: row;
37 | flex-wrap: wrap;
38 | }
39 |
40 | .sc-emoji-picker--category-title {
41 | min-width: 100%;
42 | color: #b8c3ca;
43 | font-weight: 200;
44 | font-size: 13px;
45 | margin: 5px;
46 | letter-spacing: 1px;
47 | }
48 |
49 | .sc-emoji-picker--emoji {
50 | margin: 5px;
51 | width: 30px;
52 | line-height: 30px;
53 | text-align: center;
54 | cursor: pointer;
55 | vertical-align: middle;
56 | font-size: 28px;
57 | transition: transform 60ms ease-out,-webkit-transform 60ms ease-out;
58 | }
59 |
60 | .sc-emoji-picker--emoji:hover {
61 | transform: scale(1.4);
62 | }
--------------------------------------------------------------------------------
/src/styles/emojiPicker.css:
--------------------------------------------------------------------------------
1 | .sc-emoji-picker {
2 | position: absolute;
3 | bottom: 50px;
4 | right: 0px;
5 | width: 330px;
6 | max-height: 215px;
7 | box-shadow: 0px 7px 40px 2px rgba(148, 149, 150, 0.3);
8 | background: white;
9 | border-radius: 10px;
10 | outline: none;
11 | }
12 |
13 | .sc-emoji-picker:after {
14 | content: "";
15 | width: 14px;
16 | height: 14px;
17 | background: white;
18 | position: absolute;
19 | bottom: -6px;
20 | right: 30px;
21 | transform: rotate(45deg);
22 | border-radius: 2px;
23 | }
24 |
25 | .sc-emoji-picker--content {
26 | padding: 10px;
27 | overflow: auto;
28 | width: 100%;
29 | max-height: 195px;
30 | margin-top: 7px;
31 | box-sizing: border-box;
32 | }
33 |
34 | .sc-emoji-picker--category {
35 | display: flex;
36 | flex-direction: row;
37 | flex-wrap: wrap;
38 | }
39 |
40 | .sc-emoji-picker--category-title {
41 | min-width: 100%;
42 | color: #b8c3ca;
43 | font-weight: 200;
44 | font-size: 13px;
45 | margin: 5px;
46 | letter-spacing: 1px;
47 | }
48 |
49 | .sc-emoji-picker--emoji {
50 | margin: 5px;
51 | width: 30px;
52 | line-height: 30px;
53 | text-align: center;
54 | cursor: pointer;
55 | vertical-align: middle;
56 | font-size: 28px;
57 | transition: transform 60ms ease-out,-webkit-transform 60ms ease-out;
58 | }
59 |
60 | .sc-emoji-picker--emoji:hover {
61 | transform: scale(1.4);
62 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-beautiful-chat",
3 | "version": "1.1.0",
4 | "description": "A simple and beautiful React chat component backend agnostic and with Emoji and File support.",
5 | "main": "lib/index.js",
6 | "module": "es/index.js",
7 | "files": [
8 | "css",
9 | "es",
10 | "lib",
11 | "umd"
12 | ],
13 | "scripts": {
14 | "build": "nwb build-react-component --copy-files",
15 | "clean": "nwb clean-module && nwb clean-demo",
16 | "start": "nwb serve-react-demo",
17 | "test": "nwb test-react",
18 | "test:coverage": "nwb test-react --coverage",
19 | "test:watch": "nwb test-react --server",
20 | "gh:publish": "nwb build-demo && gh-pages -d demo/dist"
21 | },
22 | "dependencies": {
23 | "emoji-js": "3.2.2",
24 | "gh-pages": "^1.1.0",
25 | "lodash": "^4.17.5",
26 | "prop-types": "15.5.10",
27 | "react-highlight.js": "1.0.5",
28 | "socket.io-client": "2.0.3"
29 | },
30 | "peerDependencies": {
31 | "react": "15.x"
32 | },
33 | "devDependencies": {
34 | "nwb": "0.17.x",
35 | "react": "15.6.1",
36 | "react-dom": "15.6.1"
37 | },
38 | "author": "",
39 | "homepage": "https://mattmezza.github.io/react-beautiful-chat/",
40 | "license": "MIT",
41 | "repository": {
42 | "type": "git",
43 | "url": "https://github.com/mattmezza/react-beautiful-chat.git"
44 | },
45 | "keywords": [
46 | "react-component"
47 | ]
48 | }
49 |
--------------------------------------------------------------------------------
/demo/dist/manifest.4a1ee271.js:
--------------------------------------------------------------------------------
1 | !function(e){function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}var r=window.webpackJsonp;window.webpackJsonp=function(t,c,u){for(var i,a,f,l=0,s=[];l handle(msg));
32 | },
33 |
34 | onMessageReceived(handler) {
35 | this.messageRecievedHandlers.push(handler);
36 | },
37 |
38 | getVisitorId() {
39 | return 1;
40 | },
41 |
42 | getTeamId() {
43 | return 1;
44 | },
45 |
46 | getTeamName() {
47 | return 1;
48 | },
49 |
50 | getImageUrl() {
51 | return 1;
52 | },
53 |
54 | setVisitorId(data) {
55 | localStorage.setItem('SLACKCHAT.VISITOR_ID', data.visitorId);
56 | }
57 | };
58 |
59 | export default messageBroker;
--------------------------------------------------------------------------------
/src/components/ChatWindow.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React, { Component } from 'react'
3 | import MessageList from './MessageList'
4 | import UserInput from './UserInput'
5 | import Header from './Header'
6 |
7 |
8 | class ChatWindow extends Component {
9 | constructor(props) {
10 | super(props)
11 | }
12 |
13 | onUserInputSubmit = (message) => {
14 | this.props.onUserInputSubmit(message)
15 | }
16 |
17 | onMessageReceived(message) {
18 | this.setState({ messages: [...this.state.messages, message] })
19 | }
20 |
21 | render() {
22 | let messageList = this.props.messageList || []
23 | let classList = [
24 | "sc-chat-window",
25 | (this.props.isOpen ? "opened" : "closed")
26 | ]
27 | return (
28 |
29 |
34 |
39 |
44 |
45 | )
46 | }
47 | }
48 |
49 | ChatWindow.propTypes = {
50 | showEmoji: PropTypes.bool,
51 | showFile: PropTypes.bool,
52 | onKeyPress: PropTypes.func
53 | }
54 |
55 | export default ChatWindow
56 |
--------------------------------------------------------------------------------
/es/components/Messages/FileMessage.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import genericFileIcon from '../../assets/file.svg';
3 | import closeIcon from '../../assets/close.svg';
4 |
5 | var FileMessage = function FileMessage(props) {
6 | var meta = props.message.data.meta || null;
7 | var text = props.message.data.text || '';
8 | var file = props.message.data.file;
9 | var author = props.message.author;
10 | return React.createElement(
11 | 'div',
12 | { className: 'sc-message--file' },
13 | props.message && author === "me" && props.onDelete && React.createElement(
14 | 'button',
15 | { className: 'delete-message', onClick: function onClick() {
16 | return props.onDelete(props.message);
17 | } },
18 | 'x'
19 | ),
20 | React.createElement(
21 | 'div',
22 | { className: 'sc-message--file-icon' },
23 | React.createElement(
24 | 'a',
25 | { href: file.url || '#', target: '_blank' },
26 | React.createElement('img', { src: genericFileIcon, alt: 'generic file icon', height: 60 })
27 | )
28 | ),
29 | React.createElement(
30 | 'div',
31 | { className: 'sc-message--file-name' },
32 | React.createElement(
33 | 'a',
34 | { href: file.url ? file.url : '#', target: '_blank' },
35 | file.name
36 | )
37 | ),
38 | React.createElement(
39 | 'div',
40 | { className: 'sc-message--file-text' },
41 | text
42 | ),
43 | meta && React.createElement(
44 | 'p',
45 | { className: 'sc-message--meta' },
46 | meta
47 | )
48 | );
49 | };
50 |
51 | export default FileMessage;
--------------------------------------------------------------------------------
/src/components/icons/FileIcon.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | class FileIcon extends Component {
4 |
5 | render() {
6 | return (
7 |
8 |
14 |
25 |
26 |
27 |
28 | this.props.onChange(e.target.files[0])} onClick={(e) => (e.target.value = null)} />
29 |
30 |
31 | );
32 | }
33 | }
34 |
35 | export default FileIcon;
36 |
--------------------------------------------------------------------------------
/es/services/messageBroker.js:
--------------------------------------------------------------------------------
1 | import MESSAGE_TYPES from './../messageTypes';
2 | var CLIENT = MESSAGE_TYPES.CLIENT;
3 | var BROKER = MESSAGE_TYPES.BROKER;
4 | var SOCKET_URL = process.env.SC_SOCKET_URL;
5 | var io = require('socket.io-client');
6 |
7 | var messageBroker = {
8 | init: function init() {
9 | var socket = io(SOCKET_URL);
10 | this.socket = socket;
11 | this.messageRecievedHandlers = [];
12 | socket.on(BROKER.VISITOR_ID, this.setVisitorId);
13 | socket.on(BROKER.MESSAGE, this.handleIncomingMessage.bind(this));
14 | var visitorId = this.getVisitorId();
15 | var teamId = this.getTeamId();
16 |
17 | if (!visitorId) {
18 | socket.emit(CLIENT.NEW_VISITOR, { teamId: teamId });
19 | } else {
20 | socket.emit(CLIENT.RETURNING_VISITOR, { visitorId: visitorId, teamId: teamId });
21 | }
22 | },
23 | sendMessage: function sendMessage(msg) {
24 | msg.visitorId = this.getVisitorId();
25 | msg.teamId = this.getTeamId();
26 | this.socket.emit(CLIENT.MESSAGE, msg);
27 | },
28 | handleIncomingMessage: function handleIncomingMessage(msg) {
29 | this.messageRecievedHandlers.forEach(function (handle) {
30 | return handle(msg);
31 | });
32 | },
33 | onMessageReceived: function onMessageReceived(handler) {
34 | this.messageRecievedHandlers.push(handler);
35 | },
36 | getVisitorId: function getVisitorId() {
37 | return 1;
38 | },
39 | getTeamId: function getTeamId() {
40 | return 1;
41 | },
42 | getTeamName: function getTeamName() {
43 | return 1;
44 | },
45 | getImageUrl: function getImageUrl() {
46 | return 1;
47 | },
48 | setVisitorId: function setVisitorId(data) {
49 | localStorage.setItem('SLACKCHAT.VISITOR_ID', data.visitorId);
50 | }
51 | };
52 |
53 | export default messageBroker;
--------------------------------------------------------------------------------
/src/components/emoji-picker/EmojiPicker.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import EmojiConvertor from 'emoji-js';
3 | import emojiData from './emojiData';
4 |
5 |
6 | class EmojiPicker extends Component {
7 |
8 | constructor() {
9 | super();
10 | this.emojiConvertor = new EmojiConvertor();
11 | this.emojiConvertor.init_env();
12 | }
13 |
14 | componentDidMount() {
15 | const elem = this.domNode;
16 | elem.style.opacity = 0;
17 | window.requestAnimationFrame(() => {
18 | elem.style.transition = 'opacity 350ms';
19 | elem.style.opacity = 1;
20 | });
21 | this.domNode.focus();
22 | }
23 |
24 | render() {
25 | return (
26 | { this.domNode = e; }}
31 | >
32 |
33 | {emojiData.map((category) => {
34 | return (
35 |
36 |
{category.name}
37 | {category.emojis.map((emoji) => {
38 | return (
39 |
{
43 | this.props.onEmojiPicked(emoji);
44 | this.domNode.blur();
45 | }}
46 | >
47 | {emoji}
48 |
49 | );
50 | })}
51 |
52 | );
53 | })}
54 |
55 |
56 | );
57 | }
58 | }
59 |
60 | export default EmojiPicker;
61 |
--------------------------------------------------------------------------------
/demo/src/TestArea.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 |
3 | class TestArea extends Component {
4 | render () {
5 | return (
6 |
7 |
8 |
react-beautiful-chat demo
9 |
10 |
11 |
24 |
25 | react-beautiful-chat is a chat window that allows you to build and add custom live chat to your sites. It includes only the react chat widget. There is no backend, and no communication system baked in.
26 |
27 | It is based on react-chat-window and adds some features to it, like optional emoji and file messages.
28 |
29 |
30 | For instructions on how to use it click here .
31 |
32 |
33 | )
34 | }
35 | }
36 |
37 | export default TestArea
--------------------------------------------------------------------------------
/es/styles/launcher.css:
--------------------------------------------------------------------------------
1 | .sc-launcher {
2 | width: 60px;
3 | height: 60px;
4 | background-color: #4e8cff;
5 | background-position: center;
6 | background-repeat: no-repeat;
7 | position: fixed;
8 | right: 25px;
9 | bottom: 25px;
10 | border-radius: 50%;
11 | box-shadow: none;
12 | transition: box-shadow 0.2s ease-in-out;
13 | }
14 |
15 | .sc-launcher:before {
16 | content: '';
17 | position: relative;
18 | display: block;
19 | width: 60px;
20 | height: 60px;
21 | border-radius: 50%;
22 | transition: box-shadow 0.2s ease-in-out;
23 | }
24 |
25 | .sc-launcher .sc-open-icon,
26 | .sc-launcher .sc-closed-icon {
27 | width: 60px;
28 | height: 60px;
29 | position: fixed;
30 | right: 25px;
31 | bottom: 25px;
32 | transition: opacity 100ms ease-in-out, transform 100ms ease-in-out;
33 | }
34 |
35 | .sc-launcher .sc-closed-icon {
36 | transition: opacity 100ms ease-in-out, transform 100ms ease-in-out;
37 | width: 60px;
38 | height: 60px;
39 | }
40 |
41 | .sc-launcher .sc-open-icon {
42 | padding: 20px;
43 | box-sizing: border-box;
44 | opacity: 0;
45 | }
46 |
47 | .sc-launcher.opened .sc-open-icon {
48 | transform: rotate(-90deg);
49 | opacity: 1;
50 | }
51 |
52 | .sc-launcher.opened .sc-closed-icon {
53 | transform: rotate(-90deg);
54 | opacity: 0;
55 | }
56 |
57 | .sc-launcher.opened:before {
58 | box-shadow: 0px 0px 400px 250px rgba(148, 149, 150, 0.2);
59 | }
60 |
61 | .sc-launcher:hover {
62 | box-shadow: 0 0px 27px 1.5px rgba(0,0,0,0.2);
63 | }
64 |
65 | .sc-new-messsages-count {
66 | position: absolute;
67 | top: -3px;
68 | left: 41px;
69 | display: flex;
70 | justify-content: center;
71 | flex-direction: column;
72 | border-radius: 50%;
73 | width: 22px;
74 | height: 22px;
75 | background: #ff4646;
76 | color: white;
77 | text-align: center;
78 | margin: auto;
79 | font-size: 12px;
80 | font-weight: 500;
81 | }
--------------------------------------------------------------------------------
/demo/dist/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | react-beautiful-chat 1.0.0 Demo
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/lib/styles/launcher.css:
--------------------------------------------------------------------------------
1 | .sc-launcher {
2 | width: 60px;
3 | height: 60px;
4 | background-color: #4e8cff;
5 | background-position: center;
6 | background-repeat: no-repeat;
7 | position: fixed;
8 | right: 25px;
9 | bottom: 25px;
10 | border-radius: 50%;
11 | box-shadow: none;
12 | transition: box-shadow 0.2s ease-in-out;
13 | }
14 |
15 | .sc-launcher:before {
16 | content: '';
17 | position: relative;
18 | display: block;
19 | width: 60px;
20 | height: 60px;
21 | border-radius: 50%;
22 | transition: box-shadow 0.2s ease-in-out;
23 | }
24 |
25 | .sc-launcher .sc-open-icon,
26 | .sc-launcher .sc-closed-icon {
27 | width: 60px;
28 | height: 60px;
29 | position: fixed;
30 | right: 25px;
31 | bottom: 25px;
32 | transition: opacity 100ms ease-in-out, transform 100ms ease-in-out;
33 | }
34 |
35 | .sc-launcher .sc-closed-icon {
36 | transition: opacity 100ms ease-in-out, transform 100ms ease-in-out;
37 | width: 60px;
38 | height: 60px;
39 | }
40 |
41 | .sc-launcher .sc-open-icon {
42 | padding: 20px;
43 | box-sizing: border-box;
44 | opacity: 0;
45 | }
46 |
47 | .sc-launcher.opened .sc-open-icon {
48 | transform: rotate(-90deg);
49 | opacity: 1;
50 | }
51 |
52 | .sc-launcher.opened .sc-closed-icon {
53 | transform: rotate(-90deg);
54 | opacity: 0;
55 | }
56 |
57 | .sc-launcher.opened:before {
58 | box-shadow: 0px 0px 400px 250px rgba(148, 149, 150, 0.2);
59 | }
60 |
61 | .sc-launcher:hover {
62 | box-shadow: 0 0px 27px 1.5px rgba(0,0,0,0.2);
63 | }
64 |
65 | .sc-new-messsages-count {
66 | position: absolute;
67 | top: -3px;
68 | left: 41px;
69 | display: flex;
70 | justify-content: center;
71 | flex-direction: column;
72 | border-radius: 50%;
73 | width: 22px;
74 | height: 22px;
75 | background: #ff4646;
76 | color: white;
77 | text-align: center;
78 | margin: auto;
79 | font-size: 12px;
80 | font-weight: 500;
81 | }
--------------------------------------------------------------------------------
/src/styles/launcher.css:
--------------------------------------------------------------------------------
1 | .sc-launcher {
2 | width: 60px;
3 | height: 60px;
4 | background-color: #4e8cff;
5 | background-position: center;
6 | background-repeat: no-repeat;
7 | position: fixed;
8 | right: 25px;
9 | bottom: 25px;
10 | border-radius: 50%;
11 | box-shadow: none;
12 | transition: box-shadow 0.2s ease-in-out;
13 | }
14 |
15 | .sc-launcher:before {
16 | content: '';
17 | position: relative;
18 | display: block;
19 | width: 60px;
20 | height: 60px;
21 | border-radius: 50%;
22 | transition: box-shadow 0.2s ease-in-out;
23 | }
24 |
25 | .sc-launcher .sc-open-icon,
26 | .sc-launcher .sc-closed-icon {
27 | width: 60px;
28 | height: 60px;
29 | position: fixed;
30 | right: 25px;
31 | bottom: 25px;
32 | transition: opacity 100ms ease-in-out, transform 100ms ease-in-out;
33 | }
34 |
35 | .sc-launcher .sc-closed-icon {
36 | transition: opacity 100ms ease-in-out, transform 100ms ease-in-out;
37 | width: 60px;
38 | height: 60px;
39 | }
40 |
41 | .sc-launcher .sc-open-icon {
42 | padding: 20px;
43 | box-sizing: border-box;
44 | opacity: 0;
45 | }
46 |
47 | .sc-launcher.opened .sc-open-icon {
48 | transform: rotate(-90deg);
49 | opacity: 1;
50 | }
51 |
52 | .sc-launcher.opened .sc-closed-icon {
53 | transform: rotate(-90deg);
54 | opacity: 0;
55 | }
56 |
57 | .sc-launcher.opened:before {
58 | box-shadow: 0px 0px 400px 250px rgba(148, 149, 150, 0.2);
59 | }
60 |
61 | .sc-launcher:hover {
62 | box-shadow: 0 0px 27px 1.5px rgba(0,0,0,0.2);
63 | }
64 |
65 | .sc-new-messsages-count {
66 | position: absolute;
67 | top: -3px;
68 | left: 41px;
69 | display: flex;
70 | justify-content: center;
71 | flex-direction: column;
72 | border-radius: 50%;
73 | width: 22px;
74 | height: 22px;
75 | background: #ff4646;
76 | color: white;
77 | text-align: center;
78 | margin: auto;
79 | font-size: 12px;
80 | font-weight: 500;
81 | }
--------------------------------------------------------------------------------
/es/components/Header.js:
--------------------------------------------------------------------------------
1 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
2 |
3 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
4 |
5 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
6 |
7 | import React, { Component } from 'react';
8 | import closeIcon from './../assets/close-icon.png';
9 |
10 | var Header = function (_Component) {
11 | _inherits(Header, _Component);
12 |
13 | function Header() {
14 | _classCallCheck(this, Header);
15 |
16 | return _possibleConstructorReturn(this, _Component.apply(this, arguments));
17 | }
18 |
19 | Header.prototype.render = function render() {
20 | return React.createElement(
21 | 'div',
22 | { className: 'sc-header' },
23 | React.createElement('img', { className: 'sc-header--img', src: this.props.imageUrl, alt: '' }),
24 | React.createElement(
25 | 'div',
26 | { className: 'sc-header--team-name' },
27 | ' ',
28 | this.props.teamName,
29 | ' '
30 | ),
31 | React.createElement(
32 | 'div',
33 | { className: 'sc-header--close-button', onClick: this.props.onClose },
34 | React.createElement('img', { src: closeIcon, alt: '' })
35 | )
36 | );
37 | };
38 |
39 | return Header;
40 | }(Component);
41 |
42 | export default Header;
--------------------------------------------------------------------------------
/es/components/MessageList.js:
--------------------------------------------------------------------------------
1 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
2 |
3 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
4 |
5 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
6 |
7 | import React, { Component } from 'react';
8 | import Message from './Messages';
9 |
10 | var MessageList = function (_Component) {
11 | _inherits(MessageList, _Component);
12 |
13 | function MessageList() {
14 | _classCallCheck(this, MessageList);
15 |
16 | return _possibleConstructorReturn(this, _Component.apply(this, arguments));
17 | }
18 |
19 | MessageList.prototype.componentDidUpdate = function componentDidUpdate(prevProps, prevState) {
20 | this.scrollList.scrollTop = this.scrollList.scrollHeight;
21 | };
22 |
23 | MessageList.prototype.render = function render() {
24 | var _this2 = this;
25 |
26 | return React.createElement(
27 | 'div',
28 | { className: 'sc-message-list', ref: function ref(el) {
29 | return _this2.scrollList = el;
30 | } },
31 | this.props.messages.map(function (message, i) {
32 | return React.createElement(Message, { message: message, key: i, onDelete: _this2.props.onDelete });
33 | })
34 | );
35 | };
36 |
37 | return MessageList;
38 | }(Component);
39 |
40 | export default MessageList;
--------------------------------------------------------------------------------
/lib/services/messageBroker.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | exports.__esModule = true;
4 |
5 | var _messageTypes = require('./../messageTypes');
6 |
7 | var _messageTypes2 = _interopRequireDefault(_messageTypes);
8 |
9 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
10 |
11 | var CLIENT = _messageTypes2.default.CLIENT;
12 | var BROKER = _messageTypes2.default.BROKER;
13 | var SOCKET_URL = process.env.SC_SOCKET_URL;
14 | var io = require('socket.io-client');
15 |
16 | var messageBroker = {
17 | init: function init() {
18 | var socket = io(SOCKET_URL);
19 | this.socket = socket;
20 | this.messageRecievedHandlers = [];
21 | socket.on(BROKER.VISITOR_ID, this.setVisitorId);
22 | socket.on(BROKER.MESSAGE, this.handleIncomingMessage.bind(this));
23 | var visitorId = this.getVisitorId();
24 | var teamId = this.getTeamId();
25 |
26 | if (!visitorId) {
27 | socket.emit(CLIENT.NEW_VISITOR, { teamId: teamId });
28 | } else {
29 | socket.emit(CLIENT.RETURNING_VISITOR, { visitorId: visitorId, teamId: teamId });
30 | }
31 | },
32 | sendMessage: function sendMessage(msg) {
33 | msg.visitorId = this.getVisitorId();
34 | msg.teamId = this.getTeamId();
35 | this.socket.emit(CLIENT.MESSAGE, msg);
36 | },
37 | handleIncomingMessage: function handleIncomingMessage(msg) {
38 | this.messageRecievedHandlers.forEach(function (handle) {
39 | return handle(msg);
40 | });
41 | },
42 | onMessageReceived: function onMessageReceived(handler) {
43 | this.messageRecievedHandlers.push(handler);
44 | },
45 | getVisitorId: function getVisitorId() {
46 | return 1;
47 | },
48 | getTeamId: function getTeamId() {
49 | return 1;
50 | },
51 | getTeamName: function getTeamName() {
52 | return 1;
53 | },
54 | getImageUrl: function getImageUrl() {
55 | return 1;
56 | },
57 | setVisitorId: function setVisitorId(data) {
58 | localStorage.setItem('SLACKCHAT.VISITOR_ID', data.visitorId);
59 | }
60 | };
61 |
62 | exports.default = messageBroker;
63 | module.exports = exports['default'];
--------------------------------------------------------------------------------
/demo/src/messageHistory.js:
--------------------------------------------------------------------------------
1 | export default [
2 | { id: 1, type: 'text', author: "me", data: { text: "Why don't they have salsa on the table?" } },
3 | { id: 2, type: 'text', author: "them", data: { text: "What do you need salsa for?" } },
4 | { id: 3, type: 'text', author: "me", data: { text: "Salsa is now the number one condiment in America." } },
5 | { id: 4, type: 'text', author: "them", data: { text: "You know why? Because people like to say 'salsa.' 'Excuse me, do you have salsa?' 'We need more salsa.' 'Where is the salsa? No salsa?'" } },
6 | { id: 5, type: 'text', author: "me", data: { text: "You know it must be impossible for a Spanish person to order seltzer and not get salsa. 'I wanted seltzer, not salsa.'" } },
7 | { id: 6, type: 'text', author: "them", data: { text: "Don't you know the difference between seltzer and salsa?? You have the seltezer after the salsa!" } },
8 | { id: 7, type: 'text', author: "me", data: { text: "See, this should be a show. This is the show. " } },
9 | { id: 8, type: 'text', author: "them", data: { text: "What?" } },
10 | { id: 9, type: 'text', author: "me", data: { text: "This. Just talking." } },
11 | { id: 10, type: 'text', author: "them", data: { text: "Yeah, right." } },
12 | { id: 11, type: 'text', author: "me", data: { text: "I'm really serious. I think that's a good idea. " } },
13 | { id: 12, type: 'text', author: "them", data: { text: "Just talking? Well what's the show about?" } },
14 | { id: 13, type: 'text', author: "me", data: { text: "It's about nothing." } },
15 | { id: 14, type: 'text', author: "them", data: { text: "No story?" } },
16 | { id: 15, type: 'file', author: "them", data: { text: "No forget the story. ", file: { name: 'file.mp3', url: '#' } } },
17 | { id: 16, type: 'file', author: "me", data: { text: "What about this one instead?? ", file: { name: 'song.mp3', url: '#' } } },
18 | { id: 17, type: 'text', author: "them", data: { text: "Open up the dev tools and go to the console section to check some cool events attached to the component", meta: 'Read' } },
19 | { id: 18, type: 'emoji', author: "me", data: { emoji: "😋" } },
20 | ]
--------------------------------------------------------------------------------
/lib/components/Messages/FileMessage.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | exports.__esModule = true;
4 |
5 | var _react = require('react');
6 |
7 | var _react2 = _interopRequireDefault(_react);
8 |
9 | var _file = require('../../assets/file.svg');
10 |
11 | var _file2 = _interopRequireDefault(_file);
12 |
13 | var _close = require('../../assets/close.svg');
14 |
15 | var _close2 = _interopRequireDefault(_close);
16 |
17 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
18 |
19 | var FileMessage = function FileMessage(props) {
20 | var meta = props.message.data.meta || null;
21 | var text = props.message.data.text || '';
22 | var file = props.message.data.file;
23 | var author = props.message.author;
24 | return _react2.default.createElement(
25 | 'div',
26 | { className: 'sc-message--file' },
27 | props.message && author === "me" && props.onDelete && _react2.default.createElement(
28 | 'button',
29 | { className: 'delete-message', onClick: function onClick() {
30 | return props.onDelete(props.message);
31 | } },
32 | 'x'
33 | ),
34 | _react2.default.createElement(
35 | 'div',
36 | { className: 'sc-message--file-icon' },
37 | _react2.default.createElement(
38 | 'a',
39 | { href: file.url || '#', target: '_blank' },
40 | _react2.default.createElement('img', { src: _file2.default, alt: 'generic file icon', height: 60 })
41 | )
42 | ),
43 | _react2.default.createElement(
44 | 'div',
45 | { className: 'sc-message--file-name' },
46 | _react2.default.createElement(
47 | 'a',
48 | { href: file.url ? file.url : '#', target: '_blank' },
49 | file.name
50 | )
51 | ),
52 | _react2.default.createElement(
53 | 'div',
54 | { className: 'sc-message--file-text' },
55 | text
56 | ),
57 | meta && _react2.default.createElement(
58 | 'p',
59 | { className: 'sc-message--meta' },
60 | meta
61 | )
62 | );
63 | };
64 |
65 | exports.default = FileMessage;
66 | module.exports = exports['default'];
--------------------------------------------------------------------------------
/lib/components/MessageList.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | exports.__esModule = true;
4 |
5 | var _react = require('react');
6 |
7 | var _react2 = _interopRequireDefault(_react);
8 |
9 | var _Messages = require('./Messages');
10 |
11 | var _Messages2 = _interopRequireDefault(_Messages);
12 |
13 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
14 |
15 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
16 |
17 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
18 |
19 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
20 |
21 | var MessageList = function (_Component) {
22 | _inherits(MessageList, _Component);
23 |
24 | function MessageList() {
25 | _classCallCheck(this, MessageList);
26 |
27 | return _possibleConstructorReturn(this, _Component.apply(this, arguments));
28 | }
29 |
30 | MessageList.prototype.componentDidUpdate = function componentDidUpdate(prevProps, prevState) {
31 | this.scrollList.scrollTop = this.scrollList.scrollHeight;
32 | };
33 |
34 | MessageList.prototype.render = function render() {
35 | var _this2 = this;
36 |
37 | return _react2.default.createElement(
38 | 'div',
39 | { className: 'sc-message-list', ref: function ref(el) {
40 | return _this2.scrollList = el;
41 | } },
42 | this.props.messages.map(function (message, i) {
43 | return _react2.default.createElement(_Messages2.default, { message: message, key: i, onDelete: _this2.props.onDelete });
44 | })
45 | );
46 | };
47 |
48 | return MessageList;
49 | }(_react.Component);
50 |
51 | exports.default = MessageList;
52 | module.exports = exports['default'];
--------------------------------------------------------------------------------
/lib/components/Header.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | exports.__esModule = true;
4 |
5 | var _react = require('react');
6 |
7 | var _react2 = _interopRequireDefault(_react);
8 |
9 | var _closeIcon = require('./../assets/close-icon.png');
10 |
11 | var _closeIcon2 = _interopRequireDefault(_closeIcon);
12 |
13 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
14 |
15 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
16 |
17 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
18 |
19 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
20 |
21 | var Header = function (_Component) {
22 | _inherits(Header, _Component);
23 |
24 | function Header() {
25 | _classCallCheck(this, Header);
26 |
27 | return _possibleConstructorReturn(this, _Component.apply(this, arguments));
28 | }
29 |
30 | Header.prototype.render = function render() {
31 | return _react2.default.createElement(
32 | 'div',
33 | { className: 'sc-header' },
34 | _react2.default.createElement('img', { className: 'sc-header--img', src: this.props.imageUrl, alt: '' }),
35 | _react2.default.createElement(
36 | 'div',
37 | { className: 'sc-header--team-name' },
38 | ' ',
39 | this.props.teamName,
40 | ' '
41 | ),
42 | _react2.default.createElement(
43 | 'div',
44 | { className: 'sc-header--close-button', onClick: this.props.onClose },
45 | _react2.default.createElement('img', { src: _closeIcon2.default, alt: '' })
46 | )
47 | );
48 | };
49 |
50 | return Header;
51 | }(_react.Component);
52 |
53 | exports.default = Header;
54 | module.exports = exports['default'];
--------------------------------------------------------------------------------
/src/components/icons/EmojiIcon.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import React, { Component } from 'react';
3 | import EmojiPicker from './../emoji-picker/EmojiPicker';
4 |
5 |
6 | class EmojiIcon extends Component {
7 |
8 | constructor() {
9 | super();
10 | this.state = {
11 | isActive: false
12 | };
13 | }
14 |
15 | _handlePickerBlur() {
16 | this.setState({
17 | isActive: false
18 | });
19 | }
20 |
21 | _openPicker(e) {
22 | e.preventDefault();
23 | this.setState({
24 | isActive: !this.state.isActive
25 | });
26 | }
27 |
28 | render() {
29 | return (
30 |
31 | { this.state.isActive &&
32 |
36 | }
37 |
41 |
53 |
54 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
70 |
71 |
72 |
73 |
74 | )}
75 | };
76 |
77 |
78 | export default EmojiIcon;
79 |
--------------------------------------------------------------------------------
/src/components/Launcher.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import React, { Component } from 'react';
3 | import ChatWindow from './ChatWindow';
4 | import launcherIcon from './../assets/logo-no-bg.svg';
5 | import launcherIconActive from './../assets/close-icon.png';
6 |
7 | class Launcher extends Component {
8 |
9 | constructor() {
10 | super();
11 | this.state = {
12 | launcherIcon,
13 | isOpen: false
14 | };
15 | }
16 |
17 | handleClick() {
18 | if (this.props.handleClick !== undefined) {
19 | this.props.handleClick();
20 | } else {
21 | this.setState({
22 | isOpen: !this.state.isOpen,
23 | });
24 | }
25 | }
26 |
27 | render() {
28 | const isOpen = this.props.hasOwnProperty('isOpen') ? this.props.isOpen : this.state.isOpen;
29 | const classList = [
30 | 'sc-launcher',
31 | (isOpen ? 'opened' : ''),
32 | ];
33 | return (
34 |
35 |
36 |
37 |
38 |
39 |
40 |
52 |
53 | );
54 | }
55 | }
56 |
57 | const MessageCount = (props) => {
58 | if (props.count === 0 || props.isOpen === true) { return null }
59 | return (
60 |
61 | {props.count}
62 |
63 | )
64 | }
65 |
66 | Launcher.propTypes = {
67 | onMessageWasReceived: PropTypes.func,
68 | onMessageWasSent: PropTypes.func,
69 | newMessagesCount: PropTypes.number,
70 | isOpen: PropTypes.bool,
71 | handleClick: PropTypes.func,
72 | messageList: PropTypes.arrayOf(PropTypes.object),
73 | showEmoji: PropTypes.bool,
74 | showFile: PropTypes.bool,
75 | onKeyPress: PropTypes.func,
76 | onDelete: PropTypes.func
77 | };
78 |
79 | Launcher.defaultProps = {
80 | newMessagesCount: 0
81 | }
82 |
83 | export default Launcher;
84 |
--------------------------------------------------------------------------------
/es/components/Messages/index.js:
--------------------------------------------------------------------------------
1 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
2 |
3 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
4 |
5 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
6 |
7 | import React, { Component } from 'react';
8 | import TextMessage from './TextMessage';
9 | import EmojiMessage from './EmojiMessage';
10 | import FileMessage from './FileMessage';
11 | import chatIconUrl from './../../assets/chat-icon.svg';
12 |
13 | var Message = function (_Component) {
14 | _inherits(Message, _Component);
15 |
16 | function Message() {
17 | _classCallCheck(this, Message);
18 |
19 | return _possibleConstructorReturn(this, _Component.apply(this, arguments));
20 | }
21 |
22 | Message.prototype._renderMessageOfType = function _renderMessageOfType(type) {
23 | switch (type) {
24 | case 'text':
25 | return React.createElement(TextMessage, { message: this.props.message, onDelete: this.props.onDelete });
26 | case 'emoji':
27 | return React.createElement(EmojiMessage, this.props.message);
28 | case 'file':
29 | return React.createElement(FileMessage, { onDelete: this.props.onDelete, message: this.props.message });
30 | }
31 | };
32 |
33 | Message.prototype.render = function render() {
34 | var contentClassList = ["sc-message--content", this.props.message.author === "me" ? "sent" : "received"];
35 | return React.createElement(
36 | 'div',
37 | { className: 'sc-message' },
38 | React.createElement(
39 | 'div',
40 | { className: contentClassList.join(" ") },
41 | React.createElement('div', { className: 'sc-message--avatar', style: {
42 | backgroundImage: 'url(' + chatIconUrl + ')'
43 | } }),
44 | this._renderMessageOfType(this.props.message.type)
45 | )
46 | );
47 | };
48 |
49 | return Message;
50 | }(Component);
51 |
52 | export default Message;
--------------------------------------------------------------------------------
/demo/src/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { render } from 'react-dom'
3 | import { Launcher } from '../../src'
4 | import messageHistory from './messageHistory';
5 | import TestArea from './TestArea';
6 | import Header from './Header';
7 | import Footer from './Footer';
8 | import Highlight from "react-highlight.js";
9 | import './../assets/styles'
10 |
11 |
12 |
13 | class Demo extends Component {
14 |
15 | constructor() {
16 | super();
17 | this.state = {
18 | messageList: messageHistory,
19 | newMessagesCount: 0,
20 | isOpen: false
21 | };
22 | this.lastId = messageHistory[messageHistory.length - 1].id
23 | }
24 |
25 | _onMessageWasSent(message) {
26 | this.setState({
27 | messageList: [...this.state.messageList, {id: this.lastId + 1, ...message}]
28 | })
29 | this.lastId += 1
30 | }
31 |
32 | _sendMessage(text) {
33 | if (text.length > 0) {
34 | const newMessagesCount = this.state.isOpen ? this.state.newMessagesCount : this.state.newMessagesCount + 1
35 | this.setState({
36 | newMessagesCount: newMessagesCount,
37 | messageList: [...this.state.messageList, {
38 | id: this.lastId + 1,
39 | author: 'them',
40 | type: 'text',
41 | data: { text }
42 | }]
43 | })
44 | this.lastId += 1
45 | }
46 | }
47 |
48 | _handleClick() {
49 | this.setState({
50 | isOpen: !this.state.isOpen,
51 | newMessagesCount: 0
52 | })
53 | }
54 |
55 | onKeyPress = (userInput) => {
56 | console.log(userInput)
57 | }
58 |
59 | onDelete = (msg) => {
60 | this.setState({messageList: this.state.messageList.filter(({id}) => id!==msg.id)})
61 | }
62 |
63 | render() {
64 | return
65 |
66 |
69 |
84 |
85 |
86 |
87 | }
88 | }
89 |
90 | render( , document.querySelector('#demo'))
91 |
--------------------------------------------------------------------------------
/es/components/icons/SendIcon.js:
--------------------------------------------------------------------------------
1 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
2 |
3 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
4 |
5 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
6 |
7 | import React, { Component } from 'react';
8 |
9 | var SendIcon = function (_Component) {
10 | _inherits(SendIcon, _Component);
11 |
12 | function SendIcon() {
13 | _classCallCheck(this, SendIcon);
14 |
15 | return _possibleConstructorReturn(this, _Component.apply(this, arguments));
16 | }
17 |
18 | SendIcon.prototype.render = function render() {
19 | var _this2 = this;
20 |
21 | return React.createElement(
22 | 'button',
23 | {
24 | onFocus: this.props.onFocus,
25 | onBlur: this.props.onBlur,
26 | onClick: function onClick(e) {
27 | e.preventDefault();_this2.props.onClick(e);
28 | },
29 | className: 'sc-user-input--send-icon-wrapper'
30 | },
31 | React.createElement(
32 | 'svg',
33 | {
34 | version: '1.1',
35 | className: 'sc-user-input--send-icon',
36 | xmlns: 'http://www.w3.org/2000/svg',
37 | x: '0px',
38 | y: '0px',
39 | width: '37.393px',
40 | height: '37.393px',
41 | viewBox: '0 0 37.393 37.393',
42 | enableBackground: 'new 0 0 37.393 37.393' },
43 | React.createElement(
44 | 'g',
45 | { id: 'Layer_2' },
46 | React.createElement('path', { d: 'M36.511,17.594L2.371,2.932c-0.374-0.161-0.81-0.079-1.1,0.21C0.982,3.43,0.896,3.865,1.055,4.241l5.613,13.263 L2.082,32.295c-0.115,0.372-0.004,0.777,0.285,1.038c0.188,0.169,0.427,0.258,0.67,0.258c0.132,0,0.266-0.026,0.392-0.08 l33.079-14.078c0.368-0.157,0.607-0.519,0.608-0.919S36.879,17.752,36.511,17.594z M4.632,30.825L8.469,18.45h8.061 c0.552,0,1-0.448,1-1s-0.448-1-1-1H8.395L3.866,5.751l29.706,12.757L4.632,30.825z' })
47 | )
48 | )
49 | );
50 | };
51 |
52 | return SendIcon;
53 | }(Component);
54 |
55 | export default SendIcon;
--------------------------------------------------------------------------------
/es/components/ChatWindow.js:
--------------------------------------------------------------------------------
1 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
2 |
3 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
4 |
5 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
6 |
7 | import PropTypes from 'prop-types';
8 | import React, { Component } from 'react';
9 | import MessageList from './MessageList';
10 | import UserInput from './UserInput';
11 | import Header from './Header';
12 |
13 | var ChatWindow = function (_Component) {
14 | _inherits(ChatWindow, _Component);
15 |
16 | function ChatWindow(props) {
17 | _classCallCheck(this, ChatWindow);
18 |
19 | var _this = _possibleConstructorReturn(this, _Component.call(this, props));
20 |
21 | _this.onUserInputSubmit = function (message) {
22 | _this.props.onUserInputSubmit(message);
23 | };
24 |
25 | return _this;
26 | }
27 |
28 | ChatWindow.prototype.onMessageReceived = function onMessageReceived(message) {
29 | this.setState({ messages: [].concat(this.state.messages, [message]) });
30 | };
31 |
32 | ChatWindow.prototype.render = function render() {
33 | var messageList = this.props.messageList || [];
34 | var classList = ["sc-chat-window", this.props.isOpen ? "opened" : "closed"];
35 | return React.createElement(
36 | 'div',
37 | { className: classList.join(' ') },
38 | React.createElement(Header, {
39 | teamName: this.props.agentProfile.teamName,
40 | imageUrl: this.props.agentProfile.imageUrl,
41 | onClose: this.props.onClose
42 | }),
43 | React.createElement(MessageList, {
44 | messages: messageList,
45 | imageUrl: this.props.agentProfile.imageUrl,
46 | onDelete: this.props.onDelete
47 | }),
48 | React.createElement(UserInput, {
49 | showEmoji: this.props.showEmoji,
50 | onSubmit: this.onUserInputSubmit,
51 | showFile: this.props.showFile,
52 | onKeyPress: this.props.onKeyPress })
53 | );
54 | };
55 |
56 | return ChatWindow;
57 | }(Component);
58 |
59 | ChatWindow.propTypes = process.env.NODE_ENV !== "production" ? {
60 | showEmoji: PropTypes.bool,
61 | showFile: PropTypes.bool,
62 | onKeyPress: PropTypes.func
63 | } : {};
64 |
65 | export default ChatWindow;
--------------------------------------------------------------------------------
/lib/components/icons/SendIcon.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | exports.__esModule = true;
4 |
5 | var _react = require('react');
6 |
7 | var _react2 = _interopRequireDefault(_react);
8 |
9 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
10 |
11 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
12 |
13 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
14 |
15 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
16 |
17 | var SendIcon = function (_Component) {
18 | _inherits(SendIcon, _Component);
19 |
20 | function SendIcon() {
21 | _classCallCheck(this, SendIcon);
22 |
23 | return _possibleConstructorReturn(this, _Component.apply(this, arguments));
24 | }
25 |
26 | SendIcon.prototype.render = function render() {
27 | var _this2 = this;
28 |
29 | return _react2.default.createElement(
30 | 'button',
31 | {
32 | onFocus: this.props.onFocus,
33 | onBlur: this.props.onBlur,
34 | onClick: function onClick(e) {
35 | e.preventDefault();_this2.props.onClick(e);
36 | },
37 | className: 'sc-user-input--send-icon-wrapper'
38 | },
39 | _react2.default.createElement(
40 | 'svg',
41 | {
42 | version: '1.1',
43 | className: 'sc-user-input--send-icon',
44 | xmlns: 'http://www.w3.org/2000/svg',
45 | x: '0px',
46 | y: '0px',
47 | width: '37.393px',
48 | height: '37.393px',
49 | viewBox: '0 0 37.393 37.393',
50 | enableBackground: 'new 0 0 37.393 37.393' },
51 | _react2.default.createElement(
52 | 'g',
53 | { id: 'Layer_2' },
54 | _react2.default.createElement('path', { d: 'M36.511,17.594L2.371,2.932c-0.374-0.161-0.81-0.079-1.1,0.21C0.982,3.43,0.896,3.865,1.055,4.241l5.613,13.263 L2.082,32.295c-0.115,0.372-0.004,0.777,0.285,1.038c0.188,0.169,0.427,0.258,0.67,0.258c0.132,0,0.266-0.026,0.392-0.08 l33.079-14.078c0.368-0.157,0.607-0.519,0.608-0.919S36.879,17.752,36.511,17.594z M4.632,30.825L8.469,18.45h8.061 c0.552,0,1-0.448,1-1s-0.448-1-1-1H8.395L3.866,5.751l29.706,12.757L4.632,30.825z' })
55 | )
56 | )
57 | );
58 | };
59 |
60 | return SendIcon;
61 | }(_react.Component);
62 |
63 | exports.default = SendIcon;
64 | module.exports = exports['default'];
--------------------------------------------------------------------------------
/es/components/icons/FileIcon.js:
--------------------------------------------------------------------------------
1 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
2 |
3 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
4 |
5 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
6 |
7 | import React, { Component } from 'react';
8 |
9 | var FileIcon = function (_Component) {
10 | _inherits(FileIcon, _Component);
11 |
12 | function FileIcon() {
13 | _classCallCheck(this, FileIcon);
14 |
15 | return _possibleConstructorReturn(this, _Component.apply(this, arguments));
16 | }
17 |
18 | FileIcon.prototype.render = function render() {
19 | var _this2 = this;
20 |
21 | return React.createElement(
22 | 'label',
23 | { htmlFor: 'file-input' },
24 | React.createElement(
25 | 'button',
26 | {
27 | onFocus: this.props.onFocus,
28 | onBlur: this.props.onBlur,
29 | className: 'sc-user-input--file-icon-wrapper',
30 | type: 'button'
31 | },
32 | React.createElement(
33 | 'svg',
34 | {
35 | version: '1.1',
36 | xmlns: 'http://www.w3.org/2000/svg',
37 | className: 'sc-user-input--file-icon',
38 | x: '0px',
39 | y: '0px',
40 | width: '24px',
41 | height: '24px',
42 | viewBox: '0 0 37.393 37.393',
43 | enableBackground: 'new 0 0 37.393 37.393'
44 | },
45 | React.createElement('path', { d: 'M20.807 10.22l-2.030-2.029-10.15 10.148c-1.682 1.681-1.682 4.408 0 6.089s4.408 1.681 6.090 0l12.18-12.178c2.804-2.802 2.804-7.346 0-10.148-2.802-2.803-7.347-2.803-10.149 0l-12.788 12.787c-0.009 0.009-0.019 0.018-0.027 0.026-3.909 3.909-3.909 10.245 0 14.153 3.908 3.908 10.246 3.908 14.156 0 0.009-0.009 0.016-0.018 0.026-0.027l0.001 0.001 8.729-8.728-2.031-2.029-8.729 8.727c-0.009 0.008-0.018 0.018-0.026 0.026-2.784 2.783-7.312 2.783-10.096 0-2.783-2.783-2.783-7.31 0-10.093 0.010-0.009 0.019-0.018 0.028-0.026l-0.001-0.002 12.79-12.786c1.678-1.679 4.411-1.679 6.090 0s1.678 4.411 0 6.089l-12.18 12.178c-0.56 0.56-1.47 0.56-2.030 0-0.559-0.559-0.559-1.47 0-2.029l10.15-10.149z' })
46 | )
47 | ),
48 | React.createElement('input', { type: 'file', id: 'file-input', onChange: function onChange(e) {
49 | return _this2.props.onChange(e.target.files[0]);
50 | }, onClick: function onClick(e) {
51 | return e.target.value = null;
52 | } })
53 | );
54 | };
55 |
56 | return FileIcon;
57 | }(Component);
58 |
59 | export default FileIcon;
--------------------------------------------------------------------------------
/lib/components/Messages/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | exports.__esModule = true;
4 |
5 | var _react = require('react');
6 |
7 | var _react2 = _interopRequireDefault(_react);
8 |
9 | var _TextMessage = require('./TextMessage');
10 |
11 | var _TextMessage2 = _interopRequireDefault(_TextMessage);
12 |
13 | var _EmojiMessage = require('./EmojiMessage');
14 |
15 | var _EmojiMessage2 = _interopRequireDefault(_EmojiMessage);
16 |
17 | var _FileMessage = require('./FileMessage');
18 |
19 | var _FileMessage2 = _interopRequireDefault(_FileMessage);
20 |
21 | var _chatIcon = require('./../../assets/chat-icon.svg');
22 |
23 | var _chatIcon2 = _interopRequireDefault(_chatIcon);
24 |
25 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
26 |
27 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
28 |
29 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
30 |
31 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
32 |
33 | var Message = function (_Component) {
34 | _inherits(Message, _Component);
35 |
36 | function Message() {
37 | _classCallCheck(this, Message);
38 |
39 | return _possibleConstructorReturn(this, _Component.apply(this, arguments));
40 | }
41 |
42 | Message.prototype._renderMessageOfType = function _renderMessageOfType(type) {
43 | switch (type) {
44 | case 'text':
45 | return _react2.default.createElement(_TextMessage2.default, { message: this.props.message, onDelete: this.props.onDelete });
46 | case 'emoji':
47 | return _react2.default.createElement(_EmojiMessage2.default, this.props.message);
48 | case 'file':
49 | return _react2.default.createElement(_FileMessage2.default, { onDelete: this.props.onDelete, message: this.props.message });
50 | }
51 | };
52 |
53 | Message.prototype.render = function render() {
54 | var contentClassList = ["sc-message--content", this.props.message.author === "me" ? "sent" : "received"];
55 | return _react2.default.createElement(
56 | 'div',
57 | { className: 'sc-message' },
58 | _react2.default.createElement(
59 | 'div',
60 | { className: contentClassList.join(" ") },
61 | _react2.default.createElement('div', { className: 'sc-message--avatar', style: {
62 | backgroundImage: 'url(' + _chatIcon2.default + ')'
63 | } }),
64 | this._renderMessageOfType(this.props.message.type)
65 | )
66 | );
67 | };
68 |
69 | return Message;
70 | }(_react.Component);
71 |
72 | exports.default = Message;
73 | module.exports = exports['default'];
--------------------------------------------------------------------------------
/es/components/emoji-picker/EmojiPicker.js:
--------------------------------------------------------------------------------
1 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
2 |
3 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
4 |
5 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
6 |
7 | import React, { Component } from 'react';
8 | import EmojiConvertor from 'emoji-js';
9 | import emojiData from './emojiData';
10 |
11 | var EmojiPicker = function (_Component) {
12 | _inherits(EmojiPicker, _Component);
13 |
14 | function EmojiPicker() {
15 | _classCallCheck(this, EmojiPicker);
16 |
17 | var _this = _possibleConstructorReturn(this, _Component.call(this));
18 |
19 | _this.emojiConvertor = new EmojiConvertor();
20 | _this.emojiConvertor.init_env();
21 | return _this;
22 | }
23 |
24 | EmojiPicker.prototype.componentDidMount = function componentDidMount() {
25 | var elem = this.domNode;
26 | elem.style.opacity = 0;
27 | window.requestAnimationFrame(function () {
28 | elem.style.transition = 'opacity 350ms';
29 | elem.style.opacity = 1;
30 | });
31 | this.domNode.focus();
32 | };
33 |
34 | EmojiPicker.prototype.render = function render() {
35 | var _this2 = this;
36 |
37 | return React.createElement(
38 | 'div',
39 | {
40 | tabIndex: '0',
41 | onBlur: this.props.onBlur,
42 | className: 'sc-emoji-picker',
43 | ref: function ref(e) {
44 | _this2.domNode = e;
45 | }
46 | },
47 | React.createElement(
48 | 'div',
49 | { className: 'sc-emoji-picker--content' },
50 | emojiData.map(function (category) {
51 | return React.createElement(
52 | 'div',
53 | { className: 'sc-emoji-picker--category', key: category.name },
54 | React.createElement(
55 | 'div',
56 | { className: 'sc-emoji-picker--category-title' },
57 | category.name
58 | ),
59 | category.emojis.map(function (emoji) {
60 | return React.createElement(
61 | 'span',
62 | {
63 | key: emoji,
64 | className: 'sc-emoji-picker--emoji',
65 | onClick: function onClick() {
66 | _this2.props.onEmojiPicked(emoji);
67 | _this2.domNode.blur();
68 | }
69 | },
70 | emoji
71 | );
72 | })
73 | );
74 | })
75 | )
76 | );
77 | };
78 |
79 | return EmojiPicker;
80 | }(Component);
81 |
82 | export default EmojiPicker;
--------------------------------------------------------------------------------
/es/assets/file.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/lib/assets/file.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/src/assets/file.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/umd/file.037acab7.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/demo/dist/file.037acab7.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/lib/components/icons/FileIcon.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | exports.__esModule = true;
4 |
5 | var _react = require('react');
6 |
7 | var _react2 = _interopRequireDefault(_react);
8 |
9 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
10 |
11 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
12 |
13 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
14 |
15 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
16 |
17 | var FileIcon = function (_Component) {
18 | _inherits(FileIcon, _Component);
19 |
20 | function FileIcon() {
21 | _classCallCheck(this, FileIcon);
22 |
23 | return _possibleConstructorReturn(this, _Component.apply(this, arguments));
24 | }
25 |
26 | FileIcon.prototype.render = function render() {
27 | var _this2 = this;
28 |
29 | return _react2.default.createElement(
30 | 'label',
31 | { htmlFor: 'file-input' },
32 | _react2.default.createElement(
33 | 'button',
34 | {
35 | onFocus: this.props.onFocus,
36 | onBlur: this.props.onBlur,
37 | className: 'sc-user-input--file-icon-wrapper',
38 | type: 'button'
39 | },
40 | _react2.default.createElement(
41 | 'svg',
42 | {
43 | version: '1.1',
44 | xmlns: 'http://www.w3.org/2000/svg',
45 | className: 'sc-user-input--file-icon',
46 | x: '0px',
47 | y: '0px',
48 | width: '24px',
49 | height: '24px',
50 | viewBox: '0 0 37.393 37.393',
51 | enableBackground: 'new 0 0 37.393 37.393'
52 | },
53 | _react2.default.createElement('path', { d: 'M20.807 10.22l-2.030-2.029-10.15 10.148c-1.682 1.681-1.682 4.408 0 6.089s4.408 1.681 6.090 0l12.18-12.178c2.804-2.802 2.804-7.346 0-10.148-2.802-2.803-7.347-2.803-10.149 0l-12.788 12.787c-0.009 0.009-0.019 0.018-0.027 0.026-3.909 3.909-3.909 10.245 0 14.153 3.908 3.908 10.246 3.908 14.156 0 0.009-0.009 0.016-0.018 0.026-0.027l0.001 0.001 8.729-8.728-2.031-2.029-8.729 8.727c-0.009 0.008-0.018 0.018-0.026 0.026-2.784 2.783-7.312 2.783-10.096 0-2.783-2.783-2.783-7.31 0-10.093 0.010-0.009 0.019-0.018 0.028-0.026l-0.001-0.002 12.79-12.786c1.678-1.679 4.411-1.679 6.090 0s1.678 4.411 0 6.089l-12.18 12.178c-0.56 0.56-1.47 0.56-2.030 0-0.559-0.559-0.559-1.47 0-2.029l10.15-10.149z' })
54 | )
55 | ),
56 | _react2.default.createElement('input', { type: 'file', id: 'file-input', onChange: function onChange(e) {
57 | return _this2.props.onChange(e.target.files[0]);
58 | }, onClick: function onClick(e) {
59 | return e.target.value = null;
60 | } })
61 | );
62 | };
63 |
64 | return FileIcon;
65 | }(_react.Component);
66 |
67 | exports.default = FileIcon;
68 | module.exports = exports['default'];
--------------------------------------------------------------------------------
/lib/components/ChatWindow.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | exports.__esModule = true;
4 |
5 | var _propTypes = require('prop-types');
6 |
7 | var _propTypes2 = _interopRequireDefault(_propTypes);
8 |
9 | var _react = require('react');
10 |
11 | var _react2 = _interopRequireDefault(_react);
12 |
13 | var _MessageList = require('./MessageList');
14 |
15 | var _MessageList2 = _interopRequireDefault(_MessageList);
16 |
17 | var _UserInput = require('./UserInput');
18 |
19 | var _UserInput2 = _interopRequireDefault(_UserInput);
20 |
21 | var _Header = require('./Header');
22 |
23 | var _Header2 = _interopRequireDefault(_Header);
24 |
25 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
26 |
27 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
28 |
29 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
30 |
31 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
32 |
33 | var ChatWindow = function (_Component) {
34 | _inherits(ChatWindow, _Component);
35 |
36 | function ChatWindow(props) {
37 | _classCallCheck(this, ChatWindow);
38 |
39 | var _this = _possibleConstructorReturn(this, _Component.call(this, props));
40 |
41 | _this.onUserInputSubmit = function (message) {
42 | _this.props.onUserInputSubmit(message);
43 | };
44 |
45 | return _this;
46 | }
47 |
48 | ChatWindow.prototype.onMessageReceived = function onMessageReceived(message) {
49 | this.setState({ messages: [].concat(this.state.messages, [message]) });
50 | };
51 |
52 | ChatWindow.prototype.render = function render() {
53 | var messageList = this.props.messageList || [];
54 | var classList = ["sc-chat-window", this.props.isOpen ? "opened" : "closed"];
55 | return _react2.default.createElement(
56 | 'div',
57 | { className: classList.join(' ') },
58 | _react2.default.createElement(_Header2.default, {
59 | teamName: this.props.agentProfile.teamName,
60 | imageUrl: this.props.agentProfile.imageUrl,
61 | onClose: this.props.onClose
62 | }),
63 | _react2.default.createElement(_MessageList2.default, {
64 | messages: messageList,
65 | imageUrl: this.props.agentProfile.imageUrl,
66 | onDelete: this.props.onDelete
67 | }),
68 | _react2.default.createElement(_UserInput2.default, {
69 | showEmoji: this.props.showEmoji,
70 | onSubmit: this.onUserInputSubmit,
71 | showFile: this.props.showFile,
72 | onKeyPress: this.props.onKeyPress })
73 | );
74 | };
75 |
76 | return ChatWindow;
77 | }(_react.Component);
78 |
79 | ChatWindow.propTypes = process.env.NODE_ENV !== "production" ? {
80 | showEmoji: _propTypes2.default.bool,
81 | showFile: _propTypes2.default.bool,
82 | onKeyPress: _propTypes2.default.func
83 | } : {};
84 |
85 | exports.default = ChatWindow;
86 | module.exports = exports['default'];
--------------------------------------------------------------------------------
/es/styles/message.css:
--------------------------------------------------------------------------------
1 | .sc-message {
2 | width: 300px;
3 | margin: auto;
4 | padding-bottom: 10px;
5 | display: flex;
6 | }
7 |
8 | .sc-message--content {
9 | width: 100%;
10 | display: flex;
11 | }
12 |
13 | .sc-message--content.sent {
14 | justify-content: flex-end;
15 | }
16 |
17 | .sc-message--content.sent .sc-message--avatar {
18 | display: none;
19 | }
20 |
21 | .sc-message--avatar {
22 | background-image: url(https://d13yacurqjgara.cloudfront.net/assets/avatar-default-aa2eab7684294781f93bc99ad394a0eb3249c5768c21390163c9f55ea8ef83a4.gif);
23 | background-repeat: no-repeat;
24 | background-size: 100%;
25 | background-position: center;
26 | min-width: 30px;
27 | min-height: 30px;
28 | border-radius: 50%;
29 | align-self: center;
30 | margin-right: 15px;
31 | }
32 |
33 | .sc-message--text {
34 | padding: 17px 20px;
35 | border-radius: 6px;
36 | font-weight: 300;
37 | font-size: 14px;
38 | line-height: 1.4;
39 | white-space: pre-wrap;
40 | -webkit-font-smoothing: subpixel-antialiased;
41 | position: relative;
42 | }
43 |
44 | .sc-message--content.sent .sc-message--text {
45 | color: white;
46 | background-color: #4e8cff;
47 | max-width: calc(100% - 120px);
48 | word-wrap: break-word;
49 | }
50 |
51 | .sc-message--content.received .sc-message--text {
52 | color: #263238;
53 | background-color: #f4f7f9;
54 | margin-right: 40px;
55 | }
56 |
57 | .sc-message--file {
58 | border-radius: 6px;
59 | background-color: #4e8cff;
60 | font-weight: 300;
61 | font-size: 14px;
62 | line-height: 1.4;
63 | white-space: pre-wrap;
64 | -webkit-font-smoothing: subpixel-antialiased;
65 | position: relative;
66 | }
67 |
68 | .sc-message--content.sent .sc-message--file {
69 | color: white;
70 | background-color: #4e8cff;
71 | word-wrap: break-word;
72 | }
73 |
74 | .sc-message--file-icon {
75 | width: 60px;
76 | text-align: center;
77 | margin-left: auto;
78 | margin-right: auto;
79 | margin-top: 15px;
80 | margin-bottom: 0px;
81 | }
82 |
83 | .sc-message--file-icon:hover {
84 | opacity: 0.7;
85 | }
86 |
87 | .sc-message--file-text {
88 | padding: 12px 15px;
89 | border-bottom-left-radius: 6px;
90 | border-bottom-right-radius: 6px;
91 | }
92 |
93 | .sc-message--file-name {
94 | color: white;
95 | padding-left: 15px;
96 | padding-right: 15px;
97 | padding-top: 0;
98 | padding-bottom: 12px;
99 | font-size: x-small;
100 | text-align: center;
101 | }
102 |
103 | .sc-message--file-name a {
104 | text-decoration: none;
105 | color: #ece7e7;
106 | }
107 |
108 | .sc-message--file-name a:hover {
109 | color: white;
110 | }
111 |
112 | .sc-message--content.sent .sc-message--file-text {
113 | color: white;
114 | background-color: #4e8cff;
115 | word-wrap: break-word;
116 | }
117 |
118 | .sc-message--content.received .sc-message--file {
119 | color: #263238;
120 | background-color: #f4f7f9;
121 | margin-right: 40px;
122 | }
123 |
124 | .sc-message--content.received .sc-message--file-name {
125 | color: #000;
126 | }
127 |
128 | .sc-message--content.received .sc-message--file a {
129 | color: rgba(43, 40, 40, 0.7);
130 | }
131 |
132 | .sc-message--content.received .sc-message--file a:hover {
133 | color: #0c0c0c;
134 | }
135 |
136 | .sc-message--emoji {
137 | font-size: 40px;
138 | }
139 |
140 | .sc-message--meta {
141 | font-size: xx-small;
142 | margin-bottom: -10px;
143 | color: white;
144 | text-align: center;
145 | }
146 |
147 | @media (max-width: 450px) {
148 | .sc-message {
149 | width: 80%;
150 | }
151 | }
--------------------------------------------------------------------------------
/lib/styles/message.css:
--------------------------------------------------------------------------------
1 | .sc-message {
2 | width: 300px;
3 | margin: auto;
4 | padding-bottom: 10px;
5 | display: flex;
6 | }
7 |
8 | .sc-message--content {
9 | width: 100%;
10 | display: flex;
11 | }
12 |
13 | .sc-message--content.sent {
14 | justify-content: flex-end;
15 | }
16 |
17 | .sc-message--content.sent .sc-message--avatar {
18 | display: none;
19 | }
20 |
21 | .sc-message--avatar {
22 | background-image: url(https://d13yacurqjgara.cloudfront.net/assets/avatar-default-aa2eab7684294781f93bc99ad394a0eb3249c5768c21390163c9f55ea8ef83a4.gif);
23 | background-repeat: no-repeat;
24 | background-size: 100%;
25 | background-position: center;
26 | min-width: 30px;
27 | min-height: 30px;
28 | border-radius: 50%;
29 | align-self: center;
30 | margin-right: 15px;
31 | }
32 |
33 | .sc-message--text {
34 | padding: 17px 20px;
35 | border-radius: 6px;
36 | font-weight: 300;
37 | font-size: 14px;
38 | line-height: 1.4;
39 | white-space: pre-wrap;
40 | -webkit-font-smoothing: subpixel-antialiased;
41 | position: relative;
42 | }
43 |
44 | .sc-message--content.sent .sc-message--text {
45 | color: white;
46 | background-color: #4e8cff;
47 | max-width: calc(100% - 120px);
48 | word-wrap: break-word;
49 | }
50 |
51 | .sc-message--content.received .sc-message--text {
52 | color: #263238;
53 | background-color: #f4f7f9;
54 | margin-right: 40px;
55 | }
56 |
57 | .sc-message--file {
58 | border-radius: 6px;
59 | background-color: #4e8cff;
60 | font-weight: 300;
61 | font-size: 14px;
62 | line-height: 1.4;
63 | white-space: pre-wrap;
64 | -webkit-font-smoothing: subpixel-antialiased;
65 | position: relative;
66 | }
67 |
68 | .sc-message--content.sent .sc-message--file {
69 | color: white;
70 | background-color: #4e8cff;
71 | word-wrap: break-word;
72 | }
73 |
74 | .sc-message--file-icon {
75 | width: 60px;
76 | text-align: center;
77 | margin-left: auto;
78 | margin-right: auto;
79 | margin-top: 15px;
80 | margin-bottom: 0px;
81 | }
82 |
83 | .sc-message--file-icon:hover {
84 | opacity: 0.7;
85 | }
86 |
87 | .sc-message--file-text {
88 | padding: 12px 15px;
89 | border-bottom-left-radius: 6px;
90 | border-bottom-right-radius: 6px;
91 | }
92 |
93 | .sc-message--file-name {
94 | color: white;
95 | padding-left: 15px;
96 | padding-right: 15px;
97 | padding-top: 0;
98 | padding-bottom: 12px;
99 | font-size: x-small;
100 | text-align: center;
101 | }
102 |
103 | .sc-message--file-name a {
104 | text-decoration: none;
105 | color: #ece7e7;
106 | }
107 |
108 | .sc-message--file-name a:hover {
109 | color: white;
110 | }
111 |
112 | .sc-message--content.sent .sc-message--file-text {
113 | color: white;
114 | background-color: #4e8cff;
115 | word-wrap: break-word;
116 | }
117 |
118 | .sc-message--content.received .sc-message--file {
119 | color: #263238;
120 | background-color: #f4f7f9;
121 | margin-right: 40px;
122 | }
123 |
124 | .sc-message--content.received .sc-message--file-name {
125 | color: #000;
126 | }
127 |
128 | .sc-message--content.received .sc-message--file a {
129 | color: rgba(43, 40, 40, 0.7);
130 | }
131 |
132 | .sc-message--content.received .sc-message--file a:hover {
133 | color: #0c0c0c;
134 | }
135 |
136 | .sc-message--emoji {
137 | font-size: 40px;
138 | }
139 |
140 | .sc-message--meta {
141 | font-size: xx-small;
142 | margin-bottom: -10px;
143 | color: white;
144 | text-align: center;
145 | }
146 |
147 | @media (max-width: 450px) {
148 | .sc-message {
149 | width: 80%;
150 | }
151 | }
--------------------------------------------------------------------------------
/src/styles/message.css:
--------------------------------------------------------------------------------
1 | .sc-message {
2 | width: 300px;
3 | margin: auto;
4 | padding-bottom: 10px;
5 | display: flex;
6 | }
7 |
8 | .sc-message--content {
9 | width: 100%;
10 | display: flex;
11 | }
12 |
13 | .sc-message--content.sent {
14 | justify-content: flex-end;
15 | }
16 |
17 | .sc-message--content.sent .sc-message--avatar {
18 | display: none;
19 | }
20 |
21 | .sc-message--avatar {
22 | background-image: url(https://d13yacurqjgara.cloudfront.net/assets/avatar-default-aa2eab7684294781f93bc99ad394a0eb3249c5768c21390163c9f55ea8ef83a4.gif);
23 | background-repeat: no-repeat;
24 | background-size: 100%;
25 | background-position: center;
26 | min-width: 30px;
27 | min-height: 30px;
28 | border-radius: 50%;
29 | align-self: center;
30 | margin-right: 15px;
31 | }
32 |
33 | .sc-message--text {
34 | padding: 17px 20px;
35 | border-radius: 6px;
36 | font-weight: 300;
37 | font-size: 14px;
38 | line-height: 1.4;
39 | white-space: pre-wrap;
40 | -webkit-font-smoothing: subpixel-antialiased;
41 | position: relative;
42 | }
43 |
44 | .sc-message--content.sent .sc-message--text {
45 | color: white;
46 | background-color: #4e8cff;
47 | max-width: calc(100% - 120px);
48 | word-wrap: break-word;
49 | }
50 |
51 | .sc-message--content.received .sc-message--text {
52 | color: #263238;
53 | background-color: #f4f7f9;
54 | margin-right: 40px;
55 | }
56 |
57 | .sc-message--file {
58 | border-radius: 6px;
59 | background-color: #4e8cff;
60 | font-weight: 300;
61 | font-size: 14px;
62 | line-height: 1.4;
63 | white-space: pre-wrap;
64 | -webkit-font-smoothing: subpixel-antialiased;
65 | position: relative;
66 | }
67 |
68 | .sc-message--content.sent .sc-message--file {
69 | color: white;
70 | background-color: #4e8cff;
71 | word-wrap: break-word;
72 | }
73 |
74 | .sc-message--file-icon {
75 | width: 60px;
76 | text-align: center;
77 | margin-left: auto;
78 | margin-right: auto;
79 | margin-top: 15px;
80 | margin-bottom: 0px;
81 | }
82 |
83 | .sc-message--file-icon:hover {
84 | opacity: 0.7;
85 | }
86 |
87 | .sc-message--file-text {
88 | padding: 12px 15px;
89 | border-bottom-left-radius: 6px;
90 | border-bottom-right-radius: 6px;
91 | }
92 |
93 | .sc-message--file-name {
94 | color: white;
95 | padding-left: 15px;
96 | padding-right: 15px;
97 | padding-top: 0;
98 | padding-bottom: 12px;
99 | font-size: x-small;
100 | text-align: center;
101 | }
102 |
103 | .sc-message--file-name a {
104 | text-decoration: none;
105 | color: #ece7e7;
106 | }
107 |
108 | .sc-message--file-name a:hover {
109 | color: white;
110 | }
111 |
112 | .sc-message--content.sent .sc-message--file-text {
113 | color: white;
114 | background-color: #4e8cff;
115 | word-wrap: break-word;
116 | }
117 |
118 | .sc-message--content.received .sc-message--file {
119 | color: #263238;
120 | background-color: #f4f7f9;
121 | margin-right: 40px;
122 | }
123 |
124 | .sc-message--content.received .sc-message--file-name {
125 | color: #000;
126 | }
127 |
128 | .sc-message--content.received .sc-message--file a {
129 | color: rgba(43, 40, 40, 0.7);
130 | }
131 |
132 | .sc-message--content.received .sc-message--file a:hover {
133 | color: #0c0c0c;
134 | }
135 |
136 | .sc-message--emoji {
137 | font-size: 40px;
138 | }
139 |
140 | .sc-message--meta {
141 | font-size: xx-small;
142 | margin-bottom: -10px;
143 | color: white;
144 | text-align: center;
145 | }
146 |
147 | @media (max-width: 450px) {
148 | .sc-message {
149 | width: 80%;
150 | }
151 | }
--------------------------------------------------------------------------------
/lib/components/emoji-picker/EmojiPicker.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | exports.__esModule = true;
4 |
5 | var _react = require('react');
6 |
7 | var _react2 = _interopRequireDefault(_react);
8 |
9 | var _emojiJs = require('emoji-js');
10 |
11 | var _emojiJs2 = _interopRequireDefault(_emojiJs);
12 |
13 | var _emojiData = require('./emojiData');
14 |
15 | var _emojiData2 = _interopRequireDefault(_emojiData);
16 |
17 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
18 |
19 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
20 |
21 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
22 |
23 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
24 |
25 | var EmojiPicker = function (_Component) {
26 | _inherits(EmojiPicker, _Component);
27 |
28 | function EmojiPicker() {
29 | _classCallCheck(this, EmojiPicker);
30 |
31 | var _this = _possibleConstructorReturn(this, _Component.call(this));
32 |
33 | _this.emojiConvertor = new _emojiJs2.default();
34 | _this.emojiConvertor.init_env();
35 | return _this;
36 | }
37 |
38 | EmojiPicker.prototype.componentDidMount = function componentDidMount() {
39 | var elem = this.domNode;
40 | elem.style.opacity = 0;
41 | window.requestAnimationFrame(function () {
42 | elem.style.transition = 'opacity 350ms';
43 | elem.style.opacity = 1;
44 | });
45 | this.domNode.focus();
46 | };
47 |
48 | EmojiPicker.prototype.render = function render() {
49 | var _this2 = this;
50 |
51 | return _react2.default.createElement(
52 | 'div',
53 | {
54 | tabIndex: '0',
55 | onBlur: this.props.onBlur,
56 | className: 'sc-emoji-picker',
57 | ref: function ref(e) {
58 | _this2.domNode = e;
59 | }
60 | },
61 | _react2.default.createElement(
62 | 'div',
63 | { className: 'sc-emoji-picker--content' },
64 | _emojiData2.default.map(function (category) {
65 | return _react2.default.createElement(
66 | 'div',
67 | { className: 'sc-emoji-picker--category', key: category.name },
68 | _react2.default.createElement(
69 | 'div',
70 | { className: 'sc-emoji-picker--category-title' },
71 | category.name
72 | ),
73 | category.emojis.map(function (emoji) {
74 | return _react2.default.createElement(
75 | 'span',
76 | {
77 | key: emoji,
78 | className: 'sc-emoji-picker--emoji',
79 | onClick: function onClick() {
80 | _this2.props.onEmojiPicked(emoji);
81 | _this2.domNode.blur();
82 | }
83 | },
84 | emoji
85 | );
86 | })
87 | );
88 | })
89 | )
90 | );
91 | };
92 |
93 | return EmojiPicker;
94 | }(_react.Component);
95 |
96 | exports.default = EmojiPicker;
97 | module.exports = exports['default'];
--------------------------------------------------------------------------------
/es/components/emoji-picker/emojiData.js:
--------------------------------------------------------------------------------
1 | export default [{
2 | name: 'People',
3 | emojis: ['😄', '😃', '😀', '😊', '😉', '😍', '😘', '😚', '😗', '😙', '😜', '😝', '😛', '😳', '😁', '😔', '😌', '😒', '😞', '😣', '😢', '😂', '😭', '😪', '😥', '😰', '😅', '😓', '😩', '😫', '😨', '😱', '😠', '😡', '😤', '😖', '😆', '😋', '😷', '😎', '😴', '😵', '😲', '😟', '😦', '😧', '👿', '😮', '😬', '😐', '😕', '😯', '😏', '😑', '👲', '👳', '👮', '👷', '💂', '👶', '👦', '👧', '👨', '👩', '👴', '👵', '👱', '👼', '👸', '😺', '😸', '😻', '😽', '😼', '🙀', '😿', '😹', '😾', '👹', '👺', '🙈', '🙉', '🙊', '💀', '👽', '💩', '🔥', '✨', '🌟', '💫', '💥', '💢', '💦', '💧', '💤', '💨', '👂', '👀', '👃', '👅', '👄', '👍', '👎', '👌', '👊', '✊', '👋', '✋', '👐', '👆', '👇', '👉', '👈', '🙌', '🙏', '👏', '💪', '🚶', '🏃', '💃', '👫', '👪', '💏', '💑', '👯', '🙆', '🙅', '💁', '🙋', '💆', '💇', '💅', '👰', '🙎', '🙍', '🙇', '🎩', '👑', '👒', '👟', '👞', '👡', '👠', '👢', '👕', '👔', '👚', '👗', '🎽', '👖', '👘', '👙', '💼', '👜', '👝', '👛', '👓', '🎀', '🌂', '💄', '💛', '💙', '💜', '💚', '💔', '💗', '💓', '💕', '💖', '💞', '💘', '💌', '💋', '💍', '💎', '👤', '💬', '👣']
4 | }, {
5 | name: 'Nature',
6 | emojis: ['🐶', '🐺', '🐱', '🐭', '🐹', '🐰', '🐸', '🐯', '🐨', '🐻', '🐷', '🐽', '🐮', '🐗', '🐵', '🐒', '🐴', '🐑', '🐘', '🐼', '🐧', '🐦', '🐤', '🐥', '🐣', '🐔', '🐍', '🐢', '🐛', '🐝', '🐜', '🐞', '🐌', '🐙', '🐚', '🐠', '🐟', '🐬', '🐳', '🐎', '🐲', '🐡', '🐫', '🐩', '🐾', '💐', '🌸', '🌷', '🍀', '🌹', '🌻', '🌺', '🍁', '🍃', '🍂', '🌿', '🌾', '🍄', '🌵', '🌴', '🌰', '🌱', '🌼', '🌑', '🌓', '🌔', '🌕', '🌛', '🌙', '🌏', '🌋', '🌌', '🌠', '⛅', '⛄', '🌀', '🌁', '🌈', '🌊']
7 | }, {
8 | name: 'Objects',
9 | emojis: ['🎍', '💝', '🎎', '🎒', '🎓', '🎏', '🎆', '🎇', '🎐', '🎑', '🎃', '👻', '🎅', '🎄', '🎁', '🎋', '🎉', '🎊', '🎈', '🎌', '🔮', '🎥', '📷', '📹', '📼', '💿', '📀', '💽', '💾', '💻', '📱', '📞', '📟', '📠', '📡', '📺', '📻', '🔊', '🔔', '📢', '📣', '⏳', '⌛', '⏰', '⌚', '🔓', '🔒', '🔏', '🔐', '🔑', '🔎', '💡', '🔦', '🔌', '🔋', '🔍', '🛀', '🚽', '🔧', '🔩', '🔨', '🚪', '🚬', '💣', '🔫', '🔪', '💊', '💉', '💰', '💴', '💵', '💳', '💸', '📲', '📧', '📥', '📤', '📩', '📨', '📫', '📪', '📮', '📦', '📝', '📄', '📃', '📑', '📊', '📈', '📉', '📜', '📋', '📅', '📆', '📇', '📁', '📂', '📌', '📎', '📏', '📐', '📕', '📗', '📘', '📙', '📓', '📔', '📒', '📚', '📖', '🔖', '📛', '📰', '🎨', '🎬', '🎤', '🎧', '🎼', '🎵', '🎶', '🎹', '🎻', '🎺', '🎷', '🎸', '👾', '🎮', '🃏', '🎴', '🀄', '🎲', '🎯', '🏈', '🏀', '⚽', '⚾', '🎾', '🎱', '🎳', '⛳', '🏁', '🏆', '🎿', '🏂', '🏊', '🏄', '🎣', '🍵', '🍶', '🍺', '🍻', '🍸', '🍹', '🍷', '🍴', '🍕', '🍔', '🍟', '🍗', '🍖', '🍝', '🍛', '🍤', '🍱', '🍣', '🍥', '🍙', '🍘', '🍚', '🍜', '🍲', '🍢', '🍡', '🍳', '🍞', '🍩', '🍮', '🍦', '🍨', '🍧', '🎂', '🍰', '🍪', '🍫', '🍬', '🍭', '🍯', '🍎', '🍏', '🍊', '🍒', '🍇', '🍉', '🍓', '🍑', '🍈', '🍌', '🍍', '🍠', '🍆', '🍅', '🌽']
10 | }, {
11 | name: 'Places',
12 | emojis: ['🏠', '🏡', '🏫', '🏢', '🏣', '🏥', '🏦', '🏪', '🏩', '🏨', '💒', '⛪', '🏬', '🌇', '🌆', '🏯', '🏰', '⛺', '🏭', '🗼', '🗾', '🗻', '🌄', '🌅', '🌃', '🗽', '🌉', '🎠', '🎡', '⛲', '🎢', '🚢', '⛵', '🚤', '🚀', '💺', '🚉', '🚄', '🚅', '🚇', '🚃', '🚌', '🚙', '🚗', '🚕', '🚚', '🚨', '🚓', '🚒', '🚑', '🚲', '💈', '🚏', '🎫', '🚥', '🚧', '🔰', '⛽', '🏮', '🎰', '🗿', '🎪', '🎭', '📍', '🚩']
13 | }, {
14 | name: 'Symbols',
15 | emojis: ['🔟', '🔢', '🔣', '🔠', '🔡', '🔤', '🔼', '🔽', '⏪', '⏩', '⏫', '⏬', '🆗', '🆕', '🆙', '🆒', '🆓', '🆖', '📶', '🎦', '🈁', '🈯', '🈳', '🈵', '🈴', '🈲', '🉐', '🈹', '🈺', '🈶', '🈚', '🚻', '🚹', '🚺', '🚼', '🚾', '🚭', '🈸', '🉑', '🆑', '🆘', '🆔', '🚫', '🔞', '⛔', '❎', '✅', '💟', '🆚', '📳', '📴', '🆎', '💠', '⛎', '🔯', '🏧', '💹', '💲', '💱', '❌', '❗', '❓', '❕', '❔', '⭕', '🔝', '🔚', '🔙', '🔛', '🔜', '🔃', '🕛', '🕐', '🕑', '🕒', '🕓', '🕔', '🕕', '🕖', '🕗', '🕘', '🕙', '🕚', '➕', '➖', '➗', '💮', '💯', '🔘', '🔗', '➰', '🔱', '🔺', '🔲', '🔳', '🔴', '🔵', '🔻', '⬜', '⬛', '🔶', '🔷', '🔸', '🔹']
16 | }];
--------------------------------------------------------------------------------
/es/components/Launcher.js:
--------------------------------------------------------------------------------
1 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
2 |
3 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
4 |
5 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
6 |
7 | import PropTypes from 'prop-types';
8 | import React, { Component } from 'react';
9 | import ChatWindow from './ChatWindow';
10 | import launcherIcon from './../assets/logo-no-bg.svg';
11 | import launcherIconActive from './../assets/close-icon.png';
12 |
13 | var Launcher = function (_Component) {
14 | _inherits(Launcher, _Component);
15 |
16 | function Launcher() {
17 | _classCallCheck(this, Launcher);
18 |
19 | var _this = _possibleConstructorReturn(this, _Component.call(this));
20 |
21 | _this.state = {
22 | launcherIcon: launcherIcon,
23 | isOpen: false
24 | };
25 | return _this;
26 | }
27 |
28 | Launcher.prototype.handleClick = function handleClick() {
29 | if (this.props.handleClick !== undefined) {
30 | this.props.handleClick();
31 | } else {
32 | this.setState({
33 | isOpen: !this.state.isOpen
34 | });
35 | }
36 | };
37 |
38 | Launcher.prototype.render = function render() {
39 | var isOpen = this.props.hasOwnProperty('isOpen') ? this.props.isOpen : this.state.isOpen;
40 | var classList = ['sc-launcher', isOpen ? 'opened' : ''];
41 | return React.createElement(
42 | 'div',
43 | null,
44 | React.createElement(
45 | 'div',
46 | { className: classList.join(' '), onClick: this.handleClick.bind(this) },
47 | React.createElement(MessageCount, { count: this.props.newMessagesCount, isOpen: isOpen }),
48 | React.createElement('img', { className: "sc-open-icon", src: launcherIconActive }),
49 | React.createElement('img', { className: "sc-closed-icon", src: launcherIcon })
50 | ),
51 | React.createElement(ChatWindow, {
52 | messageList: this.props.messageList,
53 | onUserInputSubmit: this.props.onMessageWasSent,
54 | agentProfile: this.props.agentProfile,
55 | isOpen: isOpen,
56 | onClose: this.handleClick.bind(this),
57 | showEmoji: this.props.showEmoji,
58 | showFile: this.props.showFile,
59 | onKeyPress: this.props.onKeyPress,
60 | onKeyPressDebounce: this.props.onKeyPressDebounce,
61 | onDelete: this.props.onDelete
62 | })
63 | );
64 | };
65 |
66 | return Launcher;
67 | }(Component);
68 |
69 | var MessageCount = function MessageCount(props) {
70 | if (props.count === 0 || props.isOpen === true) {
71 | return null;
72 | }
73 | return React.createElement(
74 | 'div',
75 | { className: "sc-new-messsages-count" },
76 | props.count
77 | );
78 | };
79 |
80 | Launcher.propTypes = process.env.NODE_ENV !== "production" ? {
81 | onMessageWasReceived: PropTypes.func,
82 | onMessageWasSent: PropTypes.func,
83 | newMessagesCount: PropTypes.number,
84 | isOpen: PropTypes.bool,
85 | handleClick: PropTypes.func,
86 | messageList: PropTypes.arrayOf(PropTypes.object),
87 | showEmoji: PropTypes.bool,
88 | showFile: PropTypes.bool,
89 | onKeyPress: PropTypes.func,
90 | onDelete: PropTypes.func
91 | } : {};
92 |
93 | Launcher.defaultProps = {
94 | newMessagesCount: 0
95 | };
96 |
97 | export default Launcher;
--------------------------------------------------------------------------------
/lib/components/emoji-picker/emojiData.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | exports.__esModule = true;
4 | exports.default = [{
5 | name: 'People',
6 | emojis: ['😄', '😃', '😀', '😊', '😉', '😍', '😘', '😚', '😗', '😙', '😜', '😝', '😛', '😳', '😁', '😔', '😌', '😒', '😞', '😣', '😢', '😂', '😭', '😪', '😥', '😰', '😅', '😓', '😩', '😫', '😨', '😱', '😠', '😡', '😤', '😖', '😆', '😋', '😷', '😎', '😴', '😵', '😲', '😟', '😦', '😧', '👿', '😮', '😬', '😐', '😕', '😯', '😏', '😑', '👲', '👳', '👮', '👷', '💂', '👶', '👦', '👧', '👨', '👩', '👴', '👵', '👱', '👼', '👸', '😺', '😸', '😻', '😽', '😼', '🙀', '😿', '😹', '😾', '👹', '👺', '🙈', '🙉', '🙊', '💀', '👽', '💩', '🔥', '✨', '🌟', '💫', '💥', '💢', '💦', '💧', '💤', '💨', '👂', '👀', '👃', '👅', '👄', '👍', '👎', '👌', '👊', '✊', '👋', '✋', '👐', '👆', '👇', '👉', '👈', '🙌', '🙏', '👏', '💪', '🚶', '🏃', '💃', '👫', '👪', '💏', '💑', '👯', '🙆', '🙅', '💁', '🙋', '💆', '💇', '💅', '👰', '🙎', '🙍', '🙇', '🎩', '👑', '👒', '👟', '👞', '👡', '👠', '👢', '👕', '👔', '👚', '👗', '🎽', '👖', '👘', '👙', '💼', '👜', '👝', '👛', '👓', '🎀', '🌂', '💄', '💛', '💙', '💜', '💚', '💔', '💗', '💓', '💕', '💖', '💞', '💘', '💌', '💋', '💍', '💎', '👤', '💬', '👣']
7 | }, {
8 | name: 'Nature',
9 | emojis: ['🐶', '🐺', '🐱', '🐭', '🐹', '🐰', '🐸', '🐯', '🐨', '🐻', '🐷', '🐽', '🐮', '🐗', '🐵', '🐒', '🐴', '🐑', '🐘', '🐼', '🐧', '🐦', '🐤', '🐥', '🐣', '🐔', '🐍', '🐢', '🐛', '🐝', '🐜', '🐞', '🐌', '🐙', '🐚', '🐠', '🐟', '🐬', '🐳', '🐎', '🐲', '🐡', '🐫', '🐩', '🐾', '💐', '🌸', '🌷', '🍀', '🌹', '🌻', '🌺', '🍁', '🍃', '🍂', '🌿', '🌾', '🍄', '🌵', '🌴', '🌰', '🌱', '🌼', '🌑', '🌓', '🌔', '🌕', '🌛', '🌙', '🌏', '🌋', '🌌', '🌠', '⛅', '⛄', '🌀', '🌁', '🌈', '🌊']
10 | }, {
11 | name: 'Objects',
12 | emojis: ['🎍', '💝', '🎎', '🎒', '🎓', '🎏', '🎆', '🎇', '🎐', '🎑', '🎃', '👻', '🎅', '🎄', '🎁', '🎋', '🎉', '🎊', '🎈', '🎌', '🔮', '🎥', '📷', '📹', '📼', '💿', '📀', '💽', '💾', '💻', '📱', '📞', '📟', '📠', '📡', '📺', '📻', '🔊', '🔔', '📢', '📣', '⏳', '⌛', '⏰', '⌚', '🔓', '🔒', '🔏', '🔐', '🔑', '🔎', '💡', '🔦', '🔌', '🔋', '🔍', '🛀', '🚽', '🔧', '🔩', '🔨', '🚪', '🚬', '💣', '🔫', '🔪', '💊', '💉', '💰', '💴', '💵', '💳', '💸', '📲', '📧', '📥', '📤', '📩', '📨', '📫', '📪', '📮', '📦', '📝', '📄', '📃', '📑', '📊', '📈', '📉', '📜', '📋', '📅', '📆', '📇', '📁', '📂', '📌', '📎', '📏', '📐', '📕', '📗', '📘', '📙', '📓', '📔', '📒', '📚', '📖', '🔖', '📛', '📰', '🎨', '🎬', '🎤', '🎧', '🎼', '🎵', '🎶', '🎹', '🎻', '🎺', '🎷', '🎸', '👾', '🎮', '🃏', '🎴', '🀄', '🎲', '🎯', '🏈', '🏀', '⚽', '⚾', '🎾', '🎱', '🎳', '⛳', '🏁', '🏆', '🎿', '🏂', '🏊', '🏄', '🎣', '🍵', '🍶', '🍺', '🍻', '🍸', '🍹', '🍷', '🍴', '🍕', '🍔', '🍟', '🍗', '🍖', '🍝', '🍛', '🍤', '🍱', '🍣', '🍥', '🍙', '🍘', '🍚', '🍜', '🍲', '🍢', '🍡', '🍳', '🍞', '🍩', '🍮', '🍦', '🍨', '🍧', '🎂', '🍰', '🍪', '🍫', '🍬', '🍭', '🍯', '🍎', '🍏', '🍊', '🍒', '🍇', '🍉', '🍓', '🍑', '🍈', '🍌', '🍍', '🍠', '🍆', '🍅', '🌽']
13 | }, {
14 | name: 'Places',
15 | emojis: ['🏠', '🏡', '🏫', '🏢', '🏣', '🏥', '🏦', '🏪', '🏩', '🏨', '💒', '⛪', '🏬', '🌇', '🌆', '🏯', '🏰', '⛺', '🏭', '🗼', '🗾', '🗻', '🌄', '🌅', '🌃', '🗽', '🌉', '🎠', '🎡', '⛲', '🎢', '🚢', '⛵', '🚤', '🚀', '💺', '🚉', '🚄', '🚅', '🚇', '🚃', '🚌', '🚙', '🚗', '🚕', '🚚', '🚨', '🚓', '🚒', '🚑', '🚲', '💈', '🚏', '🎫', '🚥', '🚧', '🔰', '⛽', '🏮', '🎰', '🗿', '🎪', '🎭', '📍', '🚩']
16 | }, {
17 | name: 'Symbols',
18 | emojis: ['🔟', '🔢', '🔣', '🔠', '🔡', '🔤', '🔼', '🔽', '⏪', '⏩', '⏫', '⏬', '🆗', '🆕', '🆙', '🆒', '🆓', '🆖', '📶', '🎦', '🈁', '🈯', '🈳', '🈵', '🈴', '🈲', '🉐', '🈹', '🈺', '🈶', '🈚', '🚻', '🚹', '🚺', '🚼', '🚾', '🚭', '🈸', '🉑', '🆑', '🆘', '🆔', '🚫', '🔞', '⛔', '❎', '✅', '💟', '🆚', '📳', '📴', '🆎', '💠', '⛎', '🔯', '🏧', '💹', '💲', '💱', '❌', '❗', '❓', '❕', '❔', '⭕', '🔝', '🔚', '🔙', '🔛', '🔜', '🔃', '🕛', '🕐', '🕑', '🕒', '🕓', '🕔', '🕕', '🕖', '🕗', '🕘', '🕙', '🕚', '➕', '➖', '➗', '💮', '💯', '🔘', '🔗', '➰', '🔱', '🔺', '🔲', '🔳', '🔴', '🔵', '🔻', '⬜', '⬛', '🔶', '🔷', '🔸', '🔹']
19 | }];
20 | module.exports = exports['default'];
--------------------------------------------------------------------------------
/es/components/icons/EmojiIcon.js:
--------------------------------------------------------------------------------
1 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
2 |
3 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
4 |
5 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
6 |
7 | import PropTypes from 'prop-types';
8 | import React, { Component } from 'react';
9 | import EmojiPicker from './../emoji-picker/EmojiPicker';
10 |
11 | var EmojiIcon = function (_Component) {
12 | _inherits(EmojiIcon, _Component);
13 |
14 | function EmojiIcon() {
15 | _classCallCheck(this, EmojiIcon);
16 |
17 | var _this = _possibleConstructorReturn(this, _Component.call(this));
18 |
19 | _this.state = {
20 | isActive: false
21 | };
22 | return _this;
23 | }
24 |
25 | EmojiIcon.prototype._handlePickerBlur = function _handlePickerBlur() {
26 | this.setState({
27 | isActive: false
28 | });
29 | };
30 |
31 | EmojiIcon.prototype._openPicker = function _openPicker(e) {
32 | e.preventDefault();
33 | this.setState({
34 | isActive: !this.state.isActive
35 | });
36 | };
37 |
38 | EmojiIcon.prototype.render = function render() {
39 | return React.createElement(
40 | 'div',
41 | { className: 'sc-user-input--picker-wrapper' },
42 | this.state.isActive && React.createElement(EmojiPicker, {
43 | onEmojiPicked: this.props.onEmojiPicked,
44 | onBlur: this._handlePickerBlur.bind(this)
45 | }),
46 | React.createElement(
47 | 'button',
48 | {
49 | onClick: this._openPicker.bind(this),
50 | className: 'sc-user-input--emoji-icon-wrapper'
51 | },
52 | React.createElement(
53 | 'svg',
54 | {
55 | className: 'sc-user-input--emoji-icon ' + (this.props.isActive ? 'active' : ''),
56 | version: '1.1',
57 | id: 'Layer_2',
58 | xmlns: 'http://www.w3.org/2000/svg',
59 | x: '0px',
60 | y: '0px',
61 | width: '37.393px',
62 | height: '37.393px',
63 | viewBox: '0 0 37.393 37.393',
64 | enableBackground: 'new 0 0 37.393 37.393'
65 | },
66 | React.createElement(
67 | 'g',
68 | null,
69 | React.createElement('path', { d: 'M18.696,37.393C8.387,37.393,0,29.006,0,18.696C0,8.387,8.387,0,18.696,0c10.31,0,18.696,8.387,18.696,18.696 C37.393,29.006,29.006,37.393,18.696,37.393z M18.696,2C9.49,2,2,9.49,2,18.696c0,9.206,7.49,16.696,16.696,16.696 c9.206,0,16.696-7.49,16.696-16.696C35.393,9.49,27.902,2,18.696,2z'
70 | })
71 | ),
72 | React.createElement(
73 | 'g',
74 | null,
75 | React.createElement('circle', { cx: '12.379', cy: '14.359', r: '1.938' })
76 | ),
77 | React.createElement(
78 | 'g',
79 | null,
80 | React.createElement('circle', { cx: '24.371', cy: '14.414', r: '1.992' })
81 | ),
82 | React.createElement(
83 | 'g',
84 | null,
85 | React.createElement('path', { d: 'M18.035,27.453c-5.748,0-8.342-4.18-8.449-4.357c-0.286-0.473-0.135-1.087,0.338-1.373 c0.471-0.286,1.084-0.136,1.372,0.335c0.094,0.151,2.161,3.396,6.74,3.396c4.713,0,7.518-3.462,7.545-3.497 c0.343-0.432,0.973-0.504,1.405-0.161c0.433,0.344,0.505,0.973,0.161,1.405C27.009,23.374,23.703,27.453,18.035,27.453z'
86 | })
87 | )
88 | )
89 | )
90 | );
91 | };
92 |
93 | return EmojiIcon;
94 | }(Component);
95 |
96 | ;
97 |
98 | export default EmojiIcon;
--------------------------------------------------------------------------------
/src/components/UserInput.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React, { Component } from 'react'
3 | import { render } from 'react-dom'
4 | import SendIcon from './icons/SendIcon'
5 | import EmojiIcon from './icons/EmojiIcon'
6 | import EmojiPicker from './emoji-picker/EmojiPicker'
7 | import FileIcons from './icons/FileIcon'
8 | import closeIcon from '../assets/close.svg'
9 | import genericFileIcon from '../assets/file.svg'
10 | import _ from 'lodash'
11 |
12 | class UserInput extends Component {
13 |
14 | constructor() {
15 | super()
16 | this.state = {
17 | inputActive: false,
18 | file: null
19 | }
20 | }
21 |
22 | handleKey = (event) => {
23 | if (event.keyCode === 13 && !event.shiftKey) {
24 | this._submitText(event)
25 | }
26 | }
27 |
28 | handleKeyPress = _.debounce(() => {
29 | this.props.onKeyPress(this.userInput.textContent)
30 | }, 300, {trailing: true})
31 |
32 | _submitText(event) {
33 | event.preventDefault()
34 | const text = this.userInput.textContent
35 | const file = this.state.file
36 | if (file) {
37 | if (text && text.length > 0) {
38 | this.props.onSubmit({
39 | author: 'me',
40 | type: 'file',
41 | data: { text, file }
42 | })
43 | this.setState({ file: null })
44 | this.userInput.innerHTML = ''
45 | } else {
46 | this.props.onSubmit({
47 | author: 'me',
48 | type: 'file',
49 | data: { file }
50 | })
51 | this.setState({ file: null })
52 | }
53 | } else {
54 | if (text && text.length > 0) {
55 | this.props.onSubmit({
56 | author: 'me',
57 | type: 'text',
58 | data: { text }
59 | })
60 | this.userInput.innerHTML = ''
61 | }
62 | }
63 | }
64 |
65 | _handleEmojiPicked(emoji) {
66 | this.props.onSubmit({
67 | author: 'me',
68 | type: 'emoji',
69 | data: { emoji }
70 | })
71 | }
72 |
73 | _handleFileSubmit(file) {
74 | this.setState({ file })
75 | }
76 |
77 | render() {
78 | return (
79 |
80 | {
81 | this.state.file &&
82 |
83 |
84 | {this.state.file && this.state.file.name}
85 |
this.setState({ file: null })} >
86 |
87 | }
88 |
89 | { this.setState({ inputActive: true }) }}
93 | onBlur={() => { this.setState({ inputActive: false }) }}
94 | ref={(e) => { this.userInput = e }}
95 | onKeyDown={this.handleKey}
96 | onKeyPress={this.handleKeyPress}
97 | contentEditable="true"
98 | placeholder="Write a reply..."
99 | className="sc-user-input--text"
100 | >
101 |
102 |
103 |
104 |
105 | {this.props.showEmoji && }
106 |
107 | {this.props.showFile &&
108 |
109 | this._handleFileSubmit(file)} />
110 |
111 | }
112 |
113 |
114 |
115 |
116 |
117 |
118 | )
119 | }
120 | }
121 |
122 | UserInput.propTypes = {
123 | onSubmit: PropTypes.func.isRequired,
124 | showEmoji: PropTypes.bool,
125 | showFile: PropTypes.bool,
126 | onKeyPress: PropTypes.func
127 | }
128 |
129 | UserInput.defaultProps = {
130 | showEmoji: true,
131 | showFile: true
132 | }
133 |
134 | export default UserInput
135 |
--------------------------------------------------------------------------------
/es/styles/userInput.css:
--------------------------------------------------------------------------------
1 | .sc-user-input {
2 | min-height: 55px;
3 | margin: 0px;
4 | position: relative;
5 | bottom: 0;
6 | display: flex;
7 | background-color: #f4f7f9;
8 | border-bottom-left-radius: 10px;
9 | border-bottom-right-radius: 10px;
10 | transition: background-color .2s ease,box-shadow .2s ease;
11 | }
12 |
13 |
14 | .sc-user-input--text {
15 | width: 300px;
16 | resize: none;
17 | border: none;
18 | outline: none;
19 | border-bottom-left-radius: 10px;
20 | box-sizing: border-box;
21 | padding: 18px;
22 | font-size: 15px;
23 | font-weight: 400;
24 | line-height: 1.33;
25 | white-space: pre-wrap;
26 | word-wrap: break-word;
27 | color: #565867;
28 | -webkit-font-smoothing: antialiased;
29 | max-height: 200px;
30 | overflow: scroll;
31 | bottom: 0;
32 | overflow-x: hidden;
33 | overflow-y: auto;
34 | }
35 |
36 | .sc-user-input--text:empty:before {
37 | content: attr(placeholder);
38 | display: block; /* For Firefox */
39 | color: rgba(86, 88, 103, 0.3);
40 | outline: none;
41 | }
42 |
43 | .sc-user-input--buttons {
44 | width: 100px;
45 | position: absolute;
46 | right: 30px;
47 | height: 100%;
48 | display: flex;
49 | }
50 |
51 | .sc-user-input--button:first-of-type {
52 | width: 40px;
53 | }
54 |
55 | .sc-user-input--button {
56 | width: 30px;
57 | height: 55px;
58 | padding-left: 3px;
59 | padding-right: 3px;
60 | display: flex;
61 | flex-direction: column;
62 | justify-content: center;
63 | }
64 |
65 | .sc-user-input.active {
66 | box-shadow: none;
67 | background-color: white;
68 | box-shadow: 0px -5px 20px 0px rgba(150, 165, 190, 0.2);
69 | }
70 |
71 | .sc-user-input--send-icon, .sc-user-input--file-icon {
72 | height: 20px;
73 | width: 20px;
74 | cursor: pointer;
75 | align-self: center;
76 | outline: none;
77 | }
78 |
79 | .sc-user-input--button label {
80 | position: relative;
81 | height: 24px;
82 | padding-left: 3px;
83 | cursor: pointer;
84 | }
85 |
86 | .sc-user-input--button label:hover path {
87 | fill: rgba(86, 88, 103, 1);
88 | }
89 |
90 | .sc-user-input--button input {
91 | position: absolute;
92 | left: 0;
93 | width: 100%;
94 | z-index: 99999;
95 | height: 100%;
96 | opacity: 0;
97 | cursor: pointer;
98 | overflow: hidden;
99 | }
100 |
101 | .file-container {
102 | background-color: #f4f7f9;
103 | border-top-left-radius: 10px;
104 | padding: 5px 20px;
105 | color: #565867;
106 | }
107 |
108 | .delete-file-message {
109 | font-style: normal;
110 | float: right;
111 | cursor: pointer;
112 | color: #c8cad0;
113 | }
114 |
115 | .delete-file-message:hover {
116 | color: #5d5e6d;
117 | }
118 |
119 | .delete-message {
120 | font-style: normal;
121 | font-size: 10px;
122 | line-height: 16px;
123 | position: absolute;
124 | top: -5px;
125 | width: 16px;
126 | height: 16px;
127 | padding: 0;
128 | margin: 0;
129 | right: -5px;
130 | cursor: pointer;
131 | color: #fff;
132 | background: #4e8cff;
133 | border: 0;
134 | border-radius: 50%;
135 | }
136 |
137 | .delete-message:hover {
138 | color: #c8cad0;
139 | }
140 |
141 | .delete-message:focus, .delete-message:active {
142 | border: 0;
143 | outline: 0;
144 | }
145 |
146 | .icon-file-message {
147 | margin-right: 5px;
148 | }
149 |
150 | .sc-user-input--send-icon path, .sc-user-input--file-icon path {
151 | fill: rgba(86, 88, 103, 0.3);
152 | }
153 |
154 | .sc-user-input--send-icon:hover path, .sc-user-input--file-icon:hover path {
155 | fill: rgba(86, 88, 103, 1);
156 | }
157 |
158 | .sc-user-input--emoji-icon-wrapper,
159 | .sc-user-input--send-icon-wrapper,
160 | .sc-user-input--file-icon-wrapper {
161 | background: none;
162 | border: none;
163 | padding: 0px;
164 | margin: 0px;
165 | outline: none;
166 | }
167 |
168 | .sc-user-input--emoji-icon-wrapper:focus {
169 | outline: none;
170 | }
171 |
172 | .sc-user-input--emoji-icon {
173 | height: 18px;
174 | cursor: pointer;
175 | align-self: center;
176 | }
177 |
178 | .sc-user-input--emoji-icon path, .sc-user-input--emoji-icon circle {
179 | fill: rgba(86, 88, 103, 0.3);
180 | }
181 |
182 | .sc-user-input--emoji-icon-wrapper:focus .sc-user-input--emoji-icon path,
183 | .sc-user-input--emoji-icon-wrapper:focus .sc-user-input--emoji-icon circle,
184 | .sc-user-input--emoji-icon.active path,
185 | .sc-user-input--emoji-icon.active circle,
186 | .sc-user-input--emoji-icon:hover path,
187 | .sc-user-input--emoji-icon:hover circle {
188 | fill: rgba(86, 88, 103, 1);
189 | }
--------------------------------------------------------------------------------
/lib/styles/userInput.css:
--------------------------------------------------------------------------------
1 | .sc-user-input {
2 | min-height: 55px;
3 | margin: 0px;
4 | position: relative;
5 | bottom: 0;
6 | display: flex;
7 | background-color: #f4f7f9;
8 | border-bottom-left-radius: 10px;
9 | border-bottom-right-radius: 10px;
10 | transition: background-color .2s ease,box-shadow .2s ease;
11 | }
12 |
13 |
14 | .sc-user-input--text {
15 | width: 300px;
16 | resize: none;
17 | border: none;
18 | outline: none;
19 | border-bottom-left-radius: 10px;
20 | box-sizing: border-box;
21 | padding: 18px;
22 | font-size: 15px;
23 | font-weight: 400;
24 | line-height: 1.33;
25 | white-space: pre-wrap;
26 | word-wrap: break-word;
27 | color: #565867;
28 | -webkit-font-smoothing: antialiased;
29 | max-height: 200px;
30 | overflow: scroll;
31 | bottom: 0;
32 | overflow-x: hidden;
33 | overflow-y: auto;
34 | }
35 |
36 | .sc-user-input--text:empty:before {
37 | content: attr(placeholder);
38 | display: block; /* For Firefox */
39 | color: rgba(86, 88, 103, 0.3);
40 | outline: none;
41 | }
42 |
43 | .sc-user-input--buttons {
44 | width: 100px;
45 | position: absolute;
46 | right: 30px;
47 | height: 100%;
48 | display: flex;
49 | }
50 |
51 | .sc-user-input--button:first-of-type {
52 | width: 40px;
53 | }
54 |
55 | .sc-user-input--button {
56 | width: 30px;
57 | height: 55px;
58 | padding-left: 3px;
59 | padding-right: 3px;
60 | display: flex;
61 | flex-direction: column;
62 | justify-content: center;
63 | }
64 |
65 | .sc-user-input.active {
66 | box-shadow: none;
67 | background-color: white;
68 | box-shadow: 0px -5px 20px 0px rgba(150, 165, 190, 0.2);
69 | }
70 |
71 | .sc-user-input--send-icon, .sc-user-input--file-icon {
72 | height: 20px;
73 | width: 20px;
74 | cursor: pointer;
75 | align-self: center;
76 | outline: none;
77 | }
78 |
79 | .sc-user-input--button label {
80 | position: relative;
81 | height: 24px;
82 | padding-left: 3px;
83 | cursor: pointer;
84 | }
85 |
86 | .sc-user-input--button label:hover path {
87 | fill: rgba(86, 88, 103, 1);
88 | }
89 |
90 | .sc-user-input--button input {
91 | position: absolute;
92 | left: 0;
93 | width: 100%;
94 | z-index: 99999;
95 | height: 100%;
96 | opacity: 0;
97 | cursor: pointer;
98 | overflow: hidden;
99 | }
100 |
101 | .file-container {
102 | background-color: #f4f7f9;
103 | border-top-left-radius: 10px;
104 | padding: 5px 20px;
105 | color: #565867;
106 | }
107 |
108 | .delete-file-message {
109 | font-style: normal;
110 | float: right;
111 | cursor: pointer;
112 | color: #c8cad0;
113 | }
114 |
115 | .delete-file-message:hover {
116 | color: #5d5e6d;
117 | }
118 |
119 | .delete-message {
120 | font-style: normal;
121 | font-size: 10px;
122 | line-height: 16px;
123 | position: absolute;
124 | top: -5px;
125 | width: 16px;
126 | height: 16px;
127 | padding: 0;
128 | margin: 0;
129 | right: -5px;
130 | cursor: pointer;
131 | color: #fff;
132 | background: #4e8cff;
133 | border: 0;
134 | border-radius: 50%;
135 | }
136 |
137 | .delete-message:hover {
138 | color: #c8cad0;
139 | }
140 |
141 | .delete-message:focus, .delete-message:active {
142 | border: 0;
143 | outline: 0;
144 | }
145 |
146 | .icon-file-message {
147 | margin-right: 5px;
148 | }
149 |
150 | .sc-user-input--send-icon path, .sc-user-input--file-icon path {
151 | fill: rgba(86, 88, 103, 0.3);
152 | }
153 |
154 | .sc-user-input--send-icon:hover path, .sc-user-input--file-icon:hover path {
155 | fill: rgba(86, 88, 103, 1);
156 | }
157 |
158 | .sc-user-input--emoji-icon-wrapper,
159 | .sc-user-input--send-icon-wrapper,
160 | .sc-user-input--file-icon-wrapper {
161 | background: none;
162 | border: none;
163 | padding: 0px;
164 | margin: 0px;
165 | outline: none;
166 | }
167 |
168 | .sc-user-input--emoji-icon-wrapper:focus {
169 | outline: none;
170 | }
171 |
172 | .sc-user-input--emoji-icon {
173 | height: 18px;
174 | cursor: pointer;
175 | align-self: center;
176 | }
177 |
178 | .sc-user-input--emoji-icon path, .sc-user-input--emoji-icon circle {
179 | fill: rgba(86, 88, 103, 0.3);
180 | }
181 |
182 | .sc-user-input--emoji-icon-wrapper:focus .sc-user-input--emoji-icon path,
183 | .sc-user-input--emoji-icon-wrapper:focus .sc-user-input--emoji-icon circle,
184 | .sc-user-input--emoji-icon.active path,
185 | .sc-user-input--emoji-icon.active circle,
186 | .sc-user-input--emoji-icon:hover path,
187 | .sc-user-input--emoji-icon:hover circle {
188 | fill: rgba(86, 88, 103, 1);
189 | }
--------------------------------------------------------------------------------
/src/styles/userInput.css:
--------------------------------------------------------------------------------
1 | .sc-user-input {
2 | min-height: 55px;
3 | margin: 0px;
4 | position: relative;
5 | bottom: 0;
6 | display: flex;
7 | background-color: #f4f7f9;
8 | border-bottom-left-radius: 10px;
9 | border-bottom-right-radius: 10px;
10 | transition: background-color .2s ease,box-shadow .2s ease;
11 | }
12 |
13 |
14 | .sc-user-input--text {
15 | width: 300px;
16 | resize: none;
17 | border: none;
18 | outline: none;
19 | border-bottom-left-radius: 10px;
20 | box-sizing: border-box;
21 | padding: 18px;
22 | font-size: 15px;
23 | font-weight: 400;
24 | line-height: 1.33;
25 | white-space: pre-wrap;
26 | word-wrap: break-word;
27 | color: #565867;
28 | -webkit-font-smoothing: antialiased;
29 | max-height: 200px;
30 | overflow: scroll;
31 | bottom: 0;
32 | overflow-x: hidden;
33 | overflow-y: auto;
34 | }
35 |
36 | .sc-user-input--text:empty:before {
37 | content: attr(placeholder);
38 | display: block; /* For Firefox */
39 | color: rgba(86, 88, 103, 0.3);
40 | outline: none;
41 | }
42 |
43 | .sc-user-input--buttons {
44 | width: 100px;
45 | position: absolute;
46 | right: 30px;
47 | height: 100%;
48 | display: flex;
49 | }
50 |
51 | .sc-user-input--button:first-of-type {
52 | width: 40px;
53 | }
54 |
55 | .sc-user-input--button {
56 | width: 30px;
57 | height: 55px;
58 | padding-left: 3px;
59 | padding-right: 3px;
60 | display: flex;
61 | flex-direction: column;
62 | justify-content: center;
63 | }
64 |
65 | .sc-user-input.active {
66 | box-shadow: none;
67 | background-color: white;
68 | box-shadow: 0px -5px 20px 0px rgba(150, 165, 190, 0.2);
69 | }
70 |
71 | .sc-user-input--send-icon, .sc-user-input--file-icon {
72 | height: 20px;
73 | width: 20px;
74 | cursor: pointer;
75 | align-self: center;
76 | outline: none;
77 | }
78 |
79 | .sc-user-input--button label {
80 | position: relative;
81 | height: 24px;
82 | padding-left: 3px;
83 | cursor: pointer;
84 | }
85 |
86 | .sc-user-input--button label:hover path {
87 | fill: rgba(86, 88, 103, 1);
88 | }
89 |
90 | .sc-user-input--button input {
91 | position: absolute;
92 | left: 0;
93 | width: 100%;
94 | z-index: 99999;
95 | height: 100%;
96 | opacity: 0;
97 | cursor: pointer;
98 | overflow: hidden;
99 | }
100 |
101 | .file-container {
102 | background-color: #f4f7f9;
103 | border-top-left-radius: 10px;
104 | padding: 5px 20px;
105 | color: #565867;
106 | }
107 |
108 | .delete-file-message {
109 | font-style: normal;
110 | float: right;
111 | cursor: pointer;
112 | color: #c8cad0;
113 | }
114 |
115 | .delete-file-message:hover {
116 | color: #5d5e6d;
117 | }
118 |
119 | .delete-message {
120 | font-style: normal;
121 | font-size: 10px;
122 | line-height: 16px;
123 | position: absolute;
124 | top: -5px;
125 | width: 16px;
126 | height: 16px;
127 | padding: 0;
128 | margin: 0;
129 | right: -5px;
130 | cursor: pointer;
131 | color: #fff;
132 | background: #4e8cff;
133 | border: 0;
134 | border-radius: 50%;
135 | }
136 |
137 | .delete-message:hover {
138 | color: #c8cad0;
139 | }
140 |
141 | .delete-message:focus, .delete-message:active {
142 | border: 0;
143 | outline: 0;
144 | }
145 |
146 | .icon-file-message {
147 | margin-right: 5px;
148 | }
149 |
150 | .sc-user-input--send-icon path, .sc-user-input--file-icon path {
151 | fill: rgba(86, 88, 103, 0.3);
152 | }
153 |
154 | .sc-user-input--send-icon:hover path, .sc-user-input--file-icon:hover path {
155 | fill: rgba(86, 88, 103, 1);
156 | }
157 |
158 | .sc-user-input--emoji-icon-wrapper,
159 | .sc-user-input--send-icon-wrapper,
160 | .sc-user-input--file-icon-wrapper {
161 | background: none;
162 | border: none;
163 | padding: 0px;
164 | margin: 0px;
165 | outline: none;
166 | }
167 |
168 | .sc-user-input--emoji-icon-wrapper:focus {
169 | outline: none;
170 | }
171 |
172 | .sc-user-input--emoji-icon {
173 | height: 18px;
174 | cursor: pointer;
175 | align-self: center;
176 | }
177 |
178 | .sc-user-input--emoji-icon path, .sc-user-input--emoji-icon circle {
179 | fill: rgba(86, 88, 103, 0.3);
180 | }
181 |
182 | .sc-user-input--emoji-icon-wrapper:focus .sc-user-input--emoji-icon path,
183 | .sc-user-input--emoji-icon-wrapper:focus .sc-user-input--emoji-icon circle,
184 | .sc-user-input--emoji-icon.active path,
185 | .sc-user-input--emoji-icon.active circle,
186 | .sc-user-input--emoji-icon:hover path,
187 | .sc-user-input--emoji-icon:hover circle {
188 | fill: rgba(86, 88, 103, 1);
189 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-beautiful-chat
2 |
3 | `react-beautiful-chat` provides an intercom-like chat window that can be included easily in any project for free. It provides no messaging facilities, only the view component.
4 |
5 | `react-beautiful-chat` is an improved version of `react-chat-window` (which you can find [here](https://github.com/kingofthestack/react-live-chat))
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | 
15 |
16 | ## Features
17 |
18 | - Customizeable
19 | - Backend agnostic
20 | - Free
21 |
22 | ## [Demo](https://mattmezza.github.io/react-beautiful-chat/)
23 |
24 | ## Table of Contents
25 | - [Installation](#installation)
26 | - [Example](#example)
27 | - [Components](#api)
28 |
29 | ## Installation
30 |
31 | ```
32 | $ npm install react-beautiful-chat
33 | ```
34 |
35 | ## Example
36 |
37 | ``` javascript
38 | import React, {Component} from 'react'
39 | import {render} from 'react-dom'
40 | import {Launcher} from '../../src'
41 |
42 | class Demo extends Component {
43 |
44 | constructor() {
45 | super();
46 | this.state = {
47 | messageList: messageHistory
48 | };
49 | }
50 |
51 | _onMessageWasSent(message) {
52 | this.setState({
53 | messageList: [...this.state.messageList, message]
54 | })
55 | }
56 |
57 | _sendMessage(text) {
58 | if (text.length > 0) {
59 | this.setState({
60 | messageList: [...this.state.messageList, {
61 | author: 'them',
62 | type: 'text',
63 | data: { text }
64 | }]
65 | })
66 | }
67 | }
68 |
69 | render() {
70 | return (
71 |
80 |
)
81 | }
82 | }
83 | ```
84 |
85 | For more detailed examples see the demo folder.
86 |
87 | ## Components
88 |
89 | # Launcher
90 |
91 | `Launcher` is the only component needed to use react-beautiful-chat. It will react dynamically to changes in messages. All new messages must be added via a change in props as shown in the example.
92 |
93 | Launcher props:
94 |
95 | |prop | type | description |
96 | |-----|--------|---------------|
97 | | *agentProfile | object | Represents your product or service's customer service agent. Fields: teamName, imageUrl|
98 | | onMessageWasSent | function(message) | Called when a message a message is sent with a message object as an argument. |
99 | | messageList | [message] | An array of message objects to be rendered as a conversation. |
100 | | showEmoji | bool | A bool indicating whether or not to show the emoji button
101 | | showFile | bool | A bool indicating whether or not to show the file chooser button
102 | | onKeyPress | func | A function `(userInput) => console.log(userInput)` used to do something with the user input. The function is invoked debounced at 300ms
103 | | onDelete | func | A function `(msg) => console.log(msg)` used to delete a sent message. If this props is set, a delete button will be shown in the top right corner of each message sent by the user to a partner. You can set any property on the message object (an `id` property for instance) and then use this property to call some backend api to delete the message.
104 |
105 |
106 | ### Message Objects
107 |
108 | Message objects are rendered differently depending on their type. Currently, only text and emoji types are supported. Each message object has an `author` field which can have the value 'me' or 'them'.
109 |
110 | ``` javascript
111 | {
112 | author: 'them',
113 | type: 'text',
114 | data: {
115 | text: 'some text'
116 | }
117 | }
118 |
119 | {
120 | author: 'me',
121 | type: 'emoji',
122 | data: {
123 | code: 'someCode'
124 | }
125 | }
126 |
127 | {
128 | author: 'me',
129 | type: 'file',
130 | data: {
131 | name: 'file.mp3',
132 | url: 'https:123.rf/file.mp3'
133 | }
134 | }
135 |
136 | ```
137 |
138 |
--------------------------------------------------------------------------------
/lib/components/Launcher.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | exports.__esModule = true;
4 |
5 | var _propTypes = require('prop-types');
6 |
7 | var _propTypes2 = _interopRequireDefault(_propTypes);
8 |
9 | var _react = require('react');
10 |
11 | var _react2 = _interopRequireDefault(_react);
12 |
13 | var _ChatWindow = require('./ChatWindow');
14 |
15 | var _ChatWindow2 = _interopRequireDefault(_ChatWindow);
16 |
17 | var _logoNoBg = require('./../assets/logo-no-bg.svg');
18 |
19 | var _logoNoBg2 = _interopRequireDefault(_logoNoBg);
20 |
21 | var _closeIcon = require('./../assets/close-icon.png');
22 |
23 | var _closeIcon2 = _interopRequireDefault(_closeIcon);
24 |
25 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
26 |
27 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
28 |
29 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
30 |
31 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
32 |
33 | var Launcher = function (_Component) {
34 | _inherits(Launcher, _Component);
35 |
36 | function Launcher() {
37 | _classCallCheck(this, Launcher);
38 |
39 | var _this = _possibleConstructorReturn(this, _Component.call(this));
40 |
41 | _this.state = {
42 | launcherIcon: _logoNoBg2.default,
43 | isOpen: false
44 | };
45 | return _this;
46 | }
47 |
48 | Launcher.prototype.handleClick = function handleClick() {
49 | if (this.props.handleClick !== undefined) {
50 | this.props.handleClick();
51 | } else {
52 | this.setState({
53 | isOpen: !this.state.isOpen
54 | });
55 | }
56 | };
57 |
58 | Launcher.prototype.render = function render() {
59 | var isOpen = this.props.hasOwnProperty('isOpen') ? this.props.isOpen : this.state.isOpen;
60 | var classList = ['sc-launcher', isOpen ? 'opened' : ''];
61 | return _react2.default.createElement(
62 | 'div',
63 | null,
64 | _react2.default.createElement(
65 | 'div',
66 | { className: classList.join(' '), onClick: this.handleClick.bind(this) },
67 | _react2.default.createElement(MessageCount, { count: this.props.newMessagesCount, isOpen: isOpen }),
68 | _react2.default.createElement('img', { className: "sc-open-icon", src: _closeIcon2.default }),
69 | _react2.default.createElement('img', { className: "sc-closed-icon", src: _logoNoBg2.default })
70 | ),
71 | _react2.default.createElement(_ChatWindow2.default, {
72 | messageList: this.props.messageList,
73 | onUserInputSubmit: this.props.onMessageWasSent,
74 | agentProfile: this.props.agentProfile,
75 | isOpen: isOpen,
76 | onClose: this.handleClick.bind(this),
77 | showEmoji: this.props.showEmoji,
78 | showFile: this.props.showFile,
79 | onKeyPress: this.props.onKeyPress,
80 | onKeyPressDebounce: this.props.onKeyPressDebounce,
81 | onDelete: this.props.onDelete
82 | })
83 | );
84 | };
85 |
86 | return Launcher;
87 | }(_react.Component);
88 |
89 | var MessageCount = function MessageCount(props) {
90 | if (props.count === 0 || props.isOpen === true) {
91 | return null;
92 | }
93 | return _react2.default.createElement(
94 | 'div',
95 | { className: "sc-new-messsages-count" },
96 | props.count
97 | );
98 | };
99 |
100 | Launcher.propTypes = process.env.NODE_ENV !== "production" ? {
101 | onMessageWasReceived: _propTypes2.default.func,
102 | onMessageWasSent: _propTypes2.default.func,
103 | newMessagesCount: _propTypes2.default.number,
104 | isOpen: _propTypes2.default.bool,
105 | handleClick: _propTypes2.default.func,
106 | messageList: _propTypes2.default.arrayOf(_propTypes2.default.object),
107 | showEmoji: _propTypes2.default.bool,
108 | showFile: _propTypes2.default.bool,
109 | onKeyPress: _propTypes2.default.func,
110 | onDelete: _propTypes2.default.func
111 | } : {};
112 |
113 | Launcher.defaultProps = {
114 | newMessagesCount: 0
115 | };
116 |
117 | exports.default = Launcher;
118 | module.exports = exports['default'];
--------------------------------------------------------------------------------
/lib/components/icons/EmojiIcon.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | exports.__esModule = true;
4 |
5 | var _propTypes = require('prop-types');
6 |
7 | var _propTypes2 = _interopRequireDefault(_propTypes);
8 |
9 | var _react = require('react');
10 |
11 | var _react2 = _interopRequireDefault(_react);
12 |
13 | var _EmojiPicker = require('./../emoji-picker/EmojiPicker');
14 |
15 | var _EmojiPicker2 = _interopRequireDefault(_EmojiPicker);
16 |
17 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
18 |
19 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
20 |
21 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
22 |
23 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
24 |
25 | var EmojiIcon = function (_Component) {
26 | _inherits(EmojiIcon, _Component);
27 |
28 | function EmojiIcon() {
29 | _classCallCheck(this, EmojiIcon);
30 |
31 | var _this = _possibleConstructorReturn(this, _Component.call(this));
32 |
33 | _this.state = {
34 | isActive: false
35 | };
36 | return _this;
37 | }
38 |
39 | EmojiIcon.prototype._handlePickerBlur = function _handlePickerBlur() {
40 | this.setState({
41 | isActive: false
42 | });
43 | };
44 |
45 | EmojiIcon.prototype._openPicker = function _openPicker(e) {
46 | e.preventDefault();
47 | this.setState({
48 | isActive: !this.state.isActive
49 | });
50 | };
51 |
52 | EmojiIcon.prototype.render = function render() {
53 | return _react2.default.createElement(
54 | 'div',
55 | { className: 'sc-user-input--picker-wrapper' },
56 | this.state.isActive && _react2.default.createElement(_EmojiPicker2.default, {
57 | onEmojiPicked: this.props.onEmojiPicked,
58 | onBlur: this._handlePickerBlur.bind(this)
59 | }),
60 | _react2.default.createElement(
61 | 'button',
62 | {
63 | onClick: this._openPicker.bind(this),
64 | className: 'sc-user-input--emoji-icon-wrapper'
65 | },
66 | _react2.default.createElement(
67 | 'svg',
68 | {
69 | className: 'sc-user-input--emoji-icon ' + (this.props.isActive ? 'active' : ''),
70 | version: '1.1',
71 | id: 'Layer_2',
72 | xmlns: 'http://www.w3.org/2000/svg',
73 | x: '0px',
74 | y: '0px',
75 | width: '37.393px',
76 | height: '37.393px',
77 | viewBox: '0 0 37.393 37.393',
78 | enableBackground: 'new 0 0 37.393 37.393'
79 | },
80 | _react2.default.createElement(
81 | 'g',
82 | null,
83 | _react2.default.createElement('path', { d: 'M18.696,37.393C8.387,37.393,0,29.006,0,18.696C0,8.387,8.387,0,18.696,0c10.31,0,18.696,8.387,18.696,18.696 C37.393,29.006,29.006,37.393,18.696,37.393z M18.696,2C9.49,2,2,9.49,2,18.696c0,9.206,7.49,16.696,16.696,16.696 c9.206,0,16.696-7.49,16.696-16.696C35.393,9.49,27.902,2,18.696,2z'
84 | })
85 | ),
86 | _react2.default.createElement(
87 | 'g',
88 | null,
89 | _react2.default.createElement('circle', { cx: '12.379', cy: '14.359', r: '1.938' })
90 | ),
91 | _react2.default.createElement(
92 | 'g',
93 | null,
94 | _react2.default.createElement('circle', { cx: '24.371', cy: '14.414', r: '1.992' })
95 | ),
96 | _react2.default.createElement(
97 | 'g',
98 | null,
99 | _react2.default.createElement('path', { d: 'M18.035,27.453c-5.748,0-8.342-4.18-8.449-4.357c-0.286-0.473-0.135-1.087,0.338-1.373 c0.471-0.286,1.084-0.136,1.372,0.335c0.094,0.151,2.161,3.396,6.74,3.396c4.713,0,7.518-3.462,7.545-3.497 c0.343-0.432,0.973-0.504,1.405-0.161c0.433,0.344,0.505,0.973,0.161,1.405C27.009,23.374,23.703,27.453,18.035,27.453z'
100 | })
101 | )
102 | )
103 | )
104 | );
105 | };
106 |
107 | return EmojiIcon;
108 | }(_react.Component);
109 |
110 | ;
111 |
112 | exports.default = EmojiIcon;
113 | module.exports = exports['default'];
--------------------------------------------------------------------------------
/es/components/UserInput.js:
--------------------------------------------------------------------------------
1 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
2 |
3 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
4 |
5 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
6 |
7 | import PropTypes from 'prop-types';
8 | import React, { Component } from 'react';
9 | import { render } from 'react-dom';
10 | import SendIcon from './icons/SendIcon';
11 | import EmojiIcon from './icons/EmojiIcon';
12 | import EmojiPicker from './emoji-picker/EmojiPicker';
13 | import FileIcons from './icons/FileIcon';
14 | import closeIcon from '../assets/close.svg';
15 | import genericFileIcon from '../assets/file.svg';
16 | import _ from 'lodash';
17 |
18 | var UserInput = function (_Component) {
19 | _inherits(UserInput, _Component);
20 |
21 | function UserInput() {
22 | _classCallCheck(this, UserInput);
23 |
24 | var _this = _possibleConstructorReturn(this, _Component.call(this));
25 |
26 | _this.handleKey = function (event) {
27 | if (event.keyCode === 13 && !event.shiftKey) {
28 | _this._submitText(event);
29 | }
30 | };
31 |
32 | _this.handleKeyPress = _.debounce(function () {
33 | _this.props.onKeyPress(_this.userInput.textContent);
34 | }, 300, { trailing: true });
35 |
36 | _this.state = {
37 | inputActive: false,
38 | file: null
39 | };
40 | return _this;
41 | }
42 |
43 | UserInput.prototype._submitText = function _submitText(event) {
44 | event.preventDefault();
45 | var text = this.userInput.textContent;
46 | var file = this.state.file;
47 | if (file) {
48 | if (text && text.length > 0) {
49 | this.props.onSubmit({
50 | author: 'me',
51 | type: 'file',
52 | data: { text: text, file: file }
53 | });
54 | this.setState({ file: null });
55 | this.userInput.innerHTML = '';
56 | } else {
57 | this.props.onSubmit({
58 | author: 'me',
59 | type: 'file',
60 | data: { file: file }
61 | });
62 | this.setState({ file: null });
63 | }
64 | } else {
65 | if (text && text.length > 0) {
66 | this.props.onSubmit({
67 | author: 'me',
68 | type: 'text',
69 | data: { text: text }
70 | });
71 | this.userInput.innerHTML = '';
72 | }
73 | }
74 | };
75 |
76 | UserInput.prototype._handleEmojiPicked = function _handleEmojiPicked(emoji) {
77 | this.props.onSubmit({
78 | author: 'me',
79 | type: 'emoji',
80 | data: { emoji: emoji }
81 | });
82 | };
83 |
84 | UserInput.prototype._handleFileSubmit = function _handleFileSubmit(file) {
85 | this.setState({ file: file });
86 | };
87 |
88 | UserInput.prototype.render = function render() {
89 | var _this2 = this;
90 |
91 | return React.createElement(
92 | 'div',
93 | null,
94 | this.state.file && React.createElement(
95 | 'div',
96 | { className: 'file-container' },
97 | React.createElement(
98 | 'span',
99 | { className: 'icon-file-message' },
100 | React.createElement('img', { src: genericFileIcon, alt: 'genericFileIcon', height: 15 })
101 | ),
102 | this.state.file && this.state.file.name,
103 | React.createElement(
104 | 'span',
105 | { className: 'delete-file-message', onClick: function onClick() {
106 | return _this2.setState({ file: null });
107 | } },
108 | React.createElement('img', { src: closeIcon, alt: 'close icon', height: 10, title: 'Remove the file' })
109 | )
110 | ),
111 | React.createElement(
112 | 'form',
113 | { className: 'sc-user-input ' + (this.state.inputActive ? 'active' : '') },
114 | React.createElement('div', {
115 | role: 'button',
116 | tabIndex: '0',
117 | onFocus: function onFocus() {
118 | _this2.setState({ inputActive: true });
119 | },
120 | onBlur: function onBlur() {
121 | _this2.setState({ inputActive: false });
122 | },
123 | ref: function ref(e) {
124 | _this2.userInput = e;
125 | },
126 | onKeyDown: this.handleKey,
127 | onKeyPress: this.handleKeyPress,
128 | contentEditable: 'true',
129 | placeholder: 'Write a reply...',
130 | className: 'sc-user-input--text'
131 | }),
132 | React.createElement(
133 | 'div',
134 | { className: 'sc-user-input--buttons' },
135 | React.createElement('div', { className: 'sc-user-input--button' }),
136 | React.createElement(
137 | 'div',
138 | { className: 'sc-user-input--button' },
139 | this.props.showEmoji && React.createElement(EmojiIcon, { onEmojiPicked: this._handleEmojiPicked.bind(this) })
140 | ),
141 | this.props.showFile && React.createElement(
142 | 'div',
143 | { className: 'sc-user-input--button' },
144 | React.createElement(FileIcons, { onChange: function onChange(file) {
145 | return _this2._handleFileSubmit(file);
146 | } })
147 | ),
148 | React.createElement(
149 | 'div',
150 | { className: 'sc-user-input--button' },
151 | React.createElement(SendIcon, { onClick: this._submitText.bind(this) })
152 | )
153 | )
154 | )
155 | );
156 | };
157 |
158 | return UserInput;
159 | }(Component);
160 |
161 | UserInput.propTypes = process.env.NODE_ENV !== "production" ? {
162 | onSubmit: PropTypes.func.isRequired,
163 | showEmoji: PropTypes.bool,
164 | showFile: PropTypes.bool,
165 | onKeyPress: PropTypes.func
166 | } : {};
167 |
168 | UserInput.defaultProps = {
169 | showEmoji: true,
170 | showFile: true
171 | };
172 |
173 | export default UserInput;
--------------------------------------------------------------------------------
/lib/components/UserInput.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | exports.__esModule = true;
4 |
5 | var _propTypes = require('prop-types');
6 |
7 | var _propTypes2 = _interopRequireDefault(_propTypes);
8 |
9 | var _react = require('react');
10 |
11 | var _react2 = _interopRequireDefault(_react);
12 |
13 | var _reactDom = require('react-dom');
14 |
15 | var _SendIcon = require('./icons/SendIcon');
16 |
17 | var _SendIcon2 = _interopRequireDefault(_SendIcon);
18 |
19 | var _EmojiIcon = require('./icons/EmojiIcon');
20 |
21 | var _EmojiIcon2 = _interopRequireDefault(_EmojiIcon);
22 |
23 | var _EmojiPicker = require('./emoji-picker/EmojiPicker');
24 |
25 | var _EmojiPicker2 = _interopRequireDefault(_EmojiPicker);
26 |
27 | var _FileIcon = require('./icons/FileIcon');
28 |
29 | var _FileIcon2 = _interopRequireDefault(_FileIcon);
30 |
31 | var _close = require('../assets/close.svg');
32 |
33 | var _close2 = _interopRequireDefault(_close);
34 |
35 | var _file = require('../assets/file.svg');
36 |
37 | var _file2 = _interopRequireDefault(_file);
38 |
39 | var _lodash = require('lodash');
40 |
41 | var _lodash2 = _interopRequireDefault(_lodash);
42 |
43 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
44 |
45 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
46 |
47 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
48 |
49 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
50 |
51 | var UserInput = function (_Component) {
52 | _inherits(UserInput, _Component);
53 |
54 | function UserInput() {
55 | _classCallCheck(this, UserInput);
56 |
57 | var _this = _possibleConstructorReturn(this, _Component.call(this));
58 |
59 | _this.handleKey = function (event) {
60 | if (event.keyCode === 13 && !event.shiftKey) {
61 | _this._submitText(event);
62 | }
63 | };
64 |
65 | _this.handleKeyPress = _lodash2.default.debounce(function () {
66 | _this.props.onKeyPress(_this.userInput.textContent);
67 | }, 300, { trailing: true });
68 |
69 | _this.state = {
70 | inputActive: false,
71 | file: null
72 | };
73 | return _this;
74 | }
75 |
76 | UserInput.prototype._submitText = function _submitText(event) {
77 | event.preventDefault();
78 | var text = this.userInput.textContent;
79 | var file = this.state.file;
80 | if (file) {
81 | if (text && text.length > 0) {
82 | this.props.onSubmit({
83 | author: 'me',
84 | type: 'file',
85 | data: { text: text, file: file }
86 | });
87 | this.setState({ file: null });
88 | this.userInput.innerHTML = '';
89 | } else {
90 | this.props.onSubmit({
91 | author: 'me',
92 | type: 'file',
93 | data: { file: file }
94 | });
95 | this.setState({ file: null });
96 | }
97 | } else {
98 | if (text && text.length > 0) {
99 | this.props.onSubmit({
100 | author: 'me',
101 | type: 'text',
102 | data: { text: text }
103 | });
104 | this.userInput.innerHTML = '';
105 | }
106 | }
107 | };
108 |
109 | UserInput.prototype._handleEmojiPicked = function _handleEmojiPicked(emoji) {
110 | this.props.onSubmit({
111 | author: 'me',
112 | type: 'emoji',
113 | data: { emoji: emoji }
114 | });
115 | };
116 |
117 | UserInput.prototype._handleFileSubmit = function _handleFileSubmit(file) {
118 | this.setState({ file: file });
119 | };
120 |
121 | UserInput.prototype.render = function render() {
122 | var _this2 = this;
123 |
124 | return _react2.default.createElement(
125 | 'div',
126 | null,
127 | this.state.file && _react2.default.createElement(
128 | 'div',
129 | { className: 'file-container' },
130 | _react2.default.createElement(
131 | 'span',
132 | { className: 'icon-file-message' },
133 | _react2.default.createElement('img', { src: _file2.default, alt: 'genericFileIcon', height: 15 })
134 | ),
135 | this.state.file && this.state.file.name,
136 | _react2.default.createElement(
137 | 'span',
138 | { className: 'delete-file-message', onClick: function onClick() {
139 | return _this2.setState({ file: null });
140 | } },
141 | _react2.default.createElement('img', { src: _close2.default, alt: 'close icon', height: 10, title: 'Remove the file' })
142 | )
143 | ),
144 | _react2.default.createElement(
145 | 'form',
146 | { className: 'sc-user-input ' + (this.state.inputActive ? 'active' : '') },
147 | _react2.default.createElement('div', {
148 | role: 'button',
149 | tabIndex: '0',
150 | onFocus: function onFocus() {
151 | _this2.setState({ inputActive: true });
152 | },
153 | onBlur: function onBlur() {
154 | _this2.setState({ inputActive: false });
155 | },
156 | ref: function ref(e) {
157 | _this2.userInput = e;
158 | },
159 | onKeyDown: this.handleKey,
160 | onKeyPress: this.handleKeyPress,
161 | contentEditable: 'true',
162 | placeholder: 'Write a reply...',
163 | className: 'sc-user-input--text'
164 | }),
165 | _react2.default.createElement(
166 | 'div',
167 | { className: 'sc-user-input--buttons' },
168 | _react2.default.createElement('div', { className: 'sc-user-input--button' }),
169 | _react2.default.createElement(
170 | 'div',
171 | { className: 'sc-user-input--button' },
172 | this.props.showEmoji && _react2.default.createElement(_EmojiIcon2.default, { onEmojiPicked: this._handleEmojiPicked.bind(this) })
173 | ),
174 | this.props.showFile && _react2.default.createElement(
175 | 'div',
176 | { className: 'sc-user-input--button' },
177 | _react2.default.createElement(_FileIcon2.default, { onChange: function onChange(file) {
178 | return _this2._handleFileSubmit(file);
179 | } })
180 | ),
181 | _react2.default.createElement(
182 | 'div',
183 | { className: 'sc-user-input--button' },
184 | _react2.default.createElement(_SendIcon2.default, { onClick: this._submitText.bind(this) })
185 | )
186 | )
187 | )
188 | );
189 | };
190 |
191 | return UserInput;
192 | }(_react.Component);
193 |
194 | UserInput.propTypes = process.env.NODE_ENV !== "production" ? {
195 | onSubmit: _propTypes2.default.func.isRequired,
196 | showEmoji: _propTypes2.default.bool,
197 | showFile: _propTypes2.default.bool,
198 | onKeyPress: _propTypes2.default.func
199 | } : {};
200 |
201 | UserInput.defaultProps = {
202 | showEmoji: true,
203 | showFile: true
204 | };
205 |
206 | exports.default = UserInput;
207 | module.exports = exports['default'];
--------------------------------------------------------------------------------