Commit b6f4f05b authored by JC Brand's avatar JC Brand

Add converse-autocomplete and use that in the chat textarea

parent f0ad326e
......@@ -19,7 +19,7 @@
"rules": {
"lodash/prefer-lodash-method": [2, {
"ignoreMethods": [
"find", "endsWith", "startsWith", "filter", "reduce",
"find", "endsWith", "startsWith", "filter", "reduce", "isArray", "create",
"map", "replace", "toLower", "split", "trim", "forEach", "toUpperCase", "includes"
]
}],
......@@ -215,10 +215,7 @@
"one-var": "off",
"one-var-declaration-per-line": "off",
"operator-assignment": "off",
"operator-linebreak": [
"error",
"after"
],
"operator-linebreak": "off",
"padded-blocks": "off",
"prefer-arrow-callback": "off",
"prefer-const": "error",
......
......@@ -7748,6 +7748,8 @@ body.reset {
line-height: 27px; }
#conversejs.converse-fullscreen .chatbox .sendXMPPMessage ul {
width: 100%; }
#conversejs.converse-fullscreen .chatbox .sendXMPPMessage .suggestion-box__results:after {
display: none; }
#conversejs.converse-fullscreen .chatbox .sendXMPPMessage .toggle-smiley ul.emoji-toolbar .emoji-category-picker {
margin-right: 5em; }
#conversejs.converse-fullscreen .chatbox .sendXMPPMessage .toggle-smiley ul.emoji-toolbar .emoji-category {
......@@ -8681,8 +8683,7 @@ body.reset {
color: #E77051; }
#conversejs.converse-embedded .chatroom .sendXMPPMessage .chat-textarea,
#conversejs .chatroom .sendXMPPMessage .chat-textarea {
border-bottom-right-radius: 0;
resize: none; }
border-bottom-right-radius: 0; }
#conversejs.converse-embedded .chatroom .sendXMPPMessage .chat-textarea.correcting,
#conversejs .chatroom .sendXMPPMessage .chat-textarea.correcting {
background-color: #fadfd7; }
......@@ -9040,20 +9041,26 @@ body.reset {
#conversejs .visually-hidden {
position: absolute;
clip: rect(0, 0, 0, 0); }
#conversejs .form-group .suggestion-box,
#conversejs .form-group .awesomplete {
width: 100%; }
#conversejs div.awesomplete {
display: inline-block;
#conversejs .suggestion-box,
#conversejs .awesomplete {
position: relative; }
#conversejs div.awesomplete mark {
#conversejs .suggestion-box mark,
#conversejs .awesomplete mark {
background: #FFB9A7; }
#conversejs div.awesomplete > input {
#conversejs .suggestion-box > input,
#conversejs .awesomplete > input {
display: block; }
#conversejs div.awesomplete > ul {
#conversejs .suggestion-box .suggestion-box__results,
#conversejs .suggestion-box > ul,
#conversejs .awesomplete .suggestion-box__results,
#conversejs .awesomplete > ul {
position: absolute;
left: 0;
right: 0;
z-index: 1;
z-index: 2;
min-width: 100%;
box-sizing: border-box;
list-style: none;
......@@ -9061,51 +9068,91 @@ body.reset {
border-radius: .3em;
margin: .2em 0 0;
background: rgba(255, 255, 255, 0.9);
background: linear-gradient(to bottom right, white, rgba(255, 255, 255, 0.8));
background: linear-gradient(to bottom right, white, rgba(255, 255, 255, 0.9));
border: 1px solid rgba(0, 0, 0, 0.3);
box-shadow: 0.05em 0.2em 0.6em rgba(0, 0, 0, 0.2);
box-shadow: 0.05em 0.2em 0.6em rgba(0, 0, 0, 0.1);
text-shadow: none; }
#conversejs div.awesomplete > ul:before {
#conversejs .suggestion-box .suggestion-box__results:before,
#conversejs .suggestion-box > ul:before,
#conversejs .awesomplete .suggestion-box__results:before,
#conversejs .awesomplete > ul:before {
content: "";
position: absolute;
top: -.43em;
left: 1em;
width: 0;
height: 0;
padding: .4em;
background: white;
border: inherit;
border-right: 0;
border-bottom: 0;
-webkit-transform: rotate(45deg);
transform: rotate(45deg); }
#conversejs div.awesomplete > ul > li {
transform: rotate(45deg);
z-index: 1; }
#conversejs .suggestion-box .suggestion-box__results > li,
#conversejs .suggestion-box > ul > li,
#conversejs .awesomplete .suggestion-box__results > li,
#conversejs .awesomplete > ul > li {
text-overflow: ellipsis;
overflow-x: hidden;
position: relative;
cursor: pointer;
padding: 1em; }
#conversejs .suggestion-box .suggestion-box__results--above,
#conversejs .awesomplete .suggestion-box__results--above {
bottom: 4.5em; }
#conversejs .suggestion-box .suggestion-box__results--above:before,
#conversejs .awesomplete .suggestion-box__results--above:before {
display: none; }
#conversejs .suggestion-box .suggestion-box__results--above:after,
#conversejs .awesomplete .suggestion-box__results--above:after {
z-index: 1;
content: "";
position: absolute;
bottom: -.43em;
left: 1em;
width: 0;
height: 0;
padding: .4em;
background: white;
border: inherit;
border-left: 0;
border-top: 0;
-webkit-transform: rotate(45deg);
transform: rotate(45deg); }
#conversejs .suggestion-box > ul[hidden],
#conversejs .suggestion-box > ul:empty,
#conversejs div.awesomplete > ul[hidden],
#conversejs div.awesomplete > ul:empty {
display: none; }
@supports (transform: scale(0)) {
#conversejs .suggestion-box > ul,
#conversejs div.awesomplete > ul {
transition: 0.3s cubic-bezier(0.4, 0.2, 0.5, 1.4);
transform-origin: 1.43em -.43em; }
#conversejs .suggestion-box > ul[hidden],
#conversejs .suggestion-box > ul:empty,
#conversejs div.awesomplete > ul[hidden],
#conversejs div.awesomplete > ul:empty {
opacity: 0;
transform: scale(0);
display: block;
transition-timing-function: ease; } }
#conversejs .suggestion-box > ul > li:hover,
#conversejs div.awesomplete > ul > li:hover {
z-index: 2;
background: #E77051;
color: white; }
#conversejs .suggestion-box > ul > li[aria-selected="true"],
#conversejs div.awesomplete > ul > li[aria-selected="true"] {
background: #3d6d8f;
color: white; }
#conversejs .suggestion-box li:hover mark,
#conversejs div.awesomplete li:hover mark {
background: #A53214;
color: white; }
#conversejs .suggestion-box li[aria-selected="true"] mark,
#conversejs div.awesomplete li[aria-selected="true"] mark {
background: #3d6b00;
color: inherit; }
......
This diff is collapsed.
......@@ -7,13 +7,14 @@
}
.form-group {
.suggestion-box,
.awesomplete {
width: 100%;
}
}
div.awesomplete {
display: inline-block;
.suggestion-box,
.awesomplete {
position: relative;
mark {
background: $lightest-red;
......@@ -23,6 +24,7 @@
display: block;
}
.suggestion-box__results,
> ul {
&:before {
content: "";
......@@ -30,18 +32,19 @@
top: -.43em;
left: 1em;
width: 0; height: 0;
padding: .4em;
background: white;
border: inherit;
border-right: 0;
border-bottom: 0;
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
z-index: 1;
}
position: absolute;
left: 0;
right: 0;
z-index: 1;
z-index: 2;
min-width: 100%;
box-sizing: border-box;
list-style: none;
......@@ -49,9 +52,9 @@
border-radius: .3em;
margin: .2em 0 0;
background: hsla(0,0%,100%,.9);
background: linear-gradient(to bottom right, white, hsla(0,0%,100%,.8));
background: linear-gradient(to bottom right, white, hsla(0,0%,100%,.9));
border: 1px solid rgba(0,0,0,.3);
box-shadow: .05em .2em .6em rgba(0,0,0,.2);
box-shadow: .05em .2em .6em rgba(0,0,0,.1);
text-shadow: none;
> li {
......@@ -62,19 +65,45 @@
padding: 1em;
}
}
.suggestion-box__results--above {
bottom: 4.5em;
&:before {
display: none;
}
&:after {
z-index: 1;
content: "";
position: absolute;
bottom: -.43em;
left: 1em;
width: 0; height: 0;
padding: .4em;
background: white;
border: inherit;
border-left: 0;
border-top: 0;
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
}
}
}
.suggestion-box > ul[hidden],
.suggestion-box > ul:empty,
div.awesomplete > ul[hidden],
div.awesomplete > ul:empty {
display: none;
}
@supports (transform: scale(0)) {
.suggestion-box > ul,
div.awesomplete > ul {
transition: .3s cubic-bezier(.4,.2,.5,1.4);
transform-origin: 1.43em -.43em;
}
.suggestion-box > ul[hidden],
.suggestion-box > ul:empty,
div.awesomplete > ul[hidden],
div.awesomplete > ul:empty {
opacity: 0;
......@@ -84,21 +113,26 @@
}
}
.suggestion-box > ul > li:hover,
div.awesomplete > ul > li:hover {
z-index: 2;
background: $red;
color: $inverse-link-color;
}
.suggestion-box > ul > li[aria-selected="true"],
div.awesomplete > ul > li[aria-selected="true"] {
background: hsl(205, 40%, 40%);
color: white;
}
.suggestion-box li:hover mark,
div.awesomplete li:hover mark {
background: $darkest-red;
color: $inverse-link-color;
}
.suggestion-box li[aria-selected="true"] mark,
div.awesomplete li[aria-selected="true"] mark {
background: hsl(86, 100%, 21%);
color: inherit;
......
......@@ -605,6 +605,11 @@
ul {
width: 100%;
}
.suggestion-box__results {
&:after {
display: none;
}
}
.toggle-smiley {
ul {
&.emoji-toolbar {
......
......@@ -271,7 +271,6 @@
}
.chat-textarea {
border-bottom-right-radius: 0;
resize: none;
&.correcting {
background-color: lighten($chatroom-head-color, 30%);
}
......
This diff is collapsed.
......@@ -50,18 +50,6 @@
"use strict";
const { $msg, Backbone, Promise, Strophe, _, b64_sha1, f, sizzle, moment } = converse.env;
const u = converse.env.utils;
const KEY = {
ENTER: 13,
SHIFT: 17,
CTRL: 17,
ALT: 18,
ESCAPE: 27,
UP_ARROW: 38,
DOWN_ARROW: 40,
FORWARD_SLASH: 47,
META: 91,
META_RIGHT: 93
};
converse.plugins.add('converse-chatview', {
/* Plugin dependencies are other plugins which might be
......@@ -926,20 +914,26 @@
return;
}
if (!ev.shiftKey && !ev.altKey) {
if (ev.keyCode === KEY.FORWARD_SLASH) {
if (ev.keyCode === _converse.keycodes.FORWARD_SLASH) {
// Forward slash is used to run commands. Nothing to do here.
return;
} else if (ev.keyCode === KEY.ESCAPE) {
} else if (ev.keyCode === _converse.keycodes.ESCAPE) {
return this.onEscapePressed(ev);
} else if (ev.keyCode === KEY.ENTER) {
} else if (ev.keyCode === _converse.keycodes.ENTER) {
return this.onFormSubmitted(ev);
} else if (ev.keyCode === KEY.UP_ARROW && !ev.target.selectionEnd) {
} else if (ev.keyCode === _converse.keycodes.UP_ARROW && !ev.target.selectionEnd) {
return this.editEarlierMessage();
} else if (ev.keyCode === KEY.DOWN_ARROW && ev.target.selectionEnd === ev.target.value.length) {
} else if (ev.keyCode === _converse.keycodes.DOWN_ARROW && ev.target.selectionEnd === ev.target.value.length) {
return this.editLaterMessage();
}
}
if (_.includes([KEY.SHIFT, KEY.META, KEY.META_RIGHT, KEY.ESCAPE, KEY.ALT], ev.keyCode)) {
if (_.includes([
_converse.keycodes.SHIFT,
_converse.keycodes.META,
_converse.keycodes.META_RIGHT,
_converse.keycodes.ESCAPE,
_converse.keycodes.ALT]
, ev.keyCode)) {
return;
}
if (this.model.get('chat_state') !== _converse.COMPOSING) {
......
......@@ -67,6 +67,7 @@
// Core plugins are whitelisted automatically
_converse.core_plugins = [
'converse-autocomplete',
'converse-bookmarks',
'converse-caps',
'converse-chatboxes',
......@@ -106,6 +107,21 @@
// Make converse pluggable
pluggable.enable(_converse, '_converse', 'pluggable');
_converse.keycodes = {
TAB: 9,
ENTER: 13,
SHIFT: 16,
CTRL: 17,
ALT: 18,
ESCAPE: 27,
UP_ARROW: 38,
DOWN_ARROW: 40,
FORWARD_SLASH: 47,
META: 91,
META_RIGHT: 93
};
// Module-level constants
_converse.STATUS_WEIGHTS = {
'offline': 6,
......
// Converse.js
// http://conversejs.org
//
// Copyright (c) 2012-2018, the Converse.js developers
// Copyright (c) 2013-2018, the Converse.js developers
// Licensed under the Mozilla Public License (MPLv2)
(function (root, factory) {
......@@ -93,7 +93,7 @@
* If the setting "strict_plugin_dependencies" is set to true,
* an error will be raised if the plugin is not found.
*/
dependencies: ["converse-modal", "converse-controlbox", "converse-chatview"],
dependencies: ["converse-autocomplete", "converse-modal", "converse-controlbox", "converse-chatview"],
overrides: {
......@@ -584,6 +584,7 @@
this.renderHeading();
this.renderChatArea();
this.renderMessageForm();
this.initAutoComplete();
if (this.model.get('connection_status') !== converse.ROOMSTATUS.ENTERED) {
this.showSpinner();
}
......@@ -610,6 +611,23 @@
return this;
},
initAutoComplete () {
this.auto_complete = new _converse.AutoComplete(this.el, {
'auto_evaluate': false,
'min_chars': 1,
'match_current_word': true,
'match_on_tab': true,
'list': this.model.occupants.map(o => ({'label': o.getDisplayName(), 'value': o.get('jid')})),
'filter': _converse.FILTER_STARTSWITH
});
this.auto_complete.on('suggestion-box-selectcomplete', () => (this.auto_completing = false));
},
keyPressed (ev) {
this.auto_complete.keyPressed(ev);
return _converse.ChatBoxView.prototype.keyPressed.apply(this, arguments);
},
showRoomDetailsModal (ev) {
ev.preventDefault();
if (_.isUndefined(this.model.room_details_modal)) {
......@@ -834,8 +852,7 @@
},
parseMessageForCommands (text) {
const _super_ = _converse.ChatBoxView.prototype;
if (_super_.parseMessageForCommands.apply(this, arguments)) {
if (_converse.ChatBoxView.prototype.parseMessageForCommands.apply(this, arguments)) {
return true;
}
if (_converse.muc_disable_moderator_commands) {
......
......@@ -7,6 +7,7 @@ if (typeof define !== 'undefined') {
* --------------------
* Any of the following components may be removed if they're not needed.
*/
"converse-autocomplete",
"converse-bookmarks", // XEP-0048 Bookmarks
"converse-caps", // XEP-0115 Entity Capabilities
"converse-chatview", // Renders standalone chat boxes for single user chat
......
......@@ -6,14 +6,22 @@
{[ } ]}
<input type="text" placeholder="{{o.label_spoiler_hint}}" value="{{ o.hint_value }}"
class="{[ if (!o.composing_spoiler) { ]} hidden {[ } ]} spoiler-hint"/>
<textarea
type="text"
class="chat-textarea
{[ if (o.show_send_button) { ]} chat-textarea-send-button {[ } ]}
{[ if (o.composing_spoiler) { ]} spoiler {[ } ]}"
placeholder="{{{o.label_personal_message}}}">{{ o.message_value }}</textarea>
{[ if (o.show_send_button) { ]}
<button type="submit" class="pure-button send-button">{{{ o.label_send }}}</button>
{[ } ]}
<div class="suggestion-box">
<ul class="suggestion-box__results suggestion-box__results--above" hidden></ul>
<textarea
type="text"
class="chat-textarea suggestion-box__input
{[ if (o.show_send_button) { ]} chat-textarea-send-button {[ } ]}
{[ if (o.composing_spoiler) { ]} spoiler {[ } ]}"
placeholder="{{{o.label_personal_message}}}">{{ o.message_value }}</textarea>
<span class="suggestion-box__additions visually-hidden" role="status" aria-live="assertive" aria-relevant="additions"></span>
{[ if (o.show_send_button) { ]}
<button type="submit" class="pure-button send-button">{{{ o.label_send }}}</button>
{[ } ]}
</div>
</form>
</div>
......@@ -808,7 +808,18 @@
} else {
model.set(attributes);
}
}
};
u.siblingIndex = function (el) {
/* eslint-disable no-cond-assign */
for (var i = 0; el = el.previousElementSibling; i++);
return i;
};
u.getCurrentWord = function (input) {
const cursor = input.selectionEnd || undefined;
return _.last(input.value.slice(0, cursor).split(' '));
};
u.isVisible = function (el) {
if (u.hasClass('hidden', el)) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment