Merge pull request #787 from yashsriv/feat/emoji-autocomplete
Add autocomplete for emoji, users, chans, and commands
This commit is contained in:
commit
6c8d1616fd
@ -1338,8 +1338,7 @@ kbd {
|
|||||||
|
|
||||||
#help .help-item .subject {
|
#help .help-item .subject {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
padding-right: 10px;
|
padding-right: 15px;
|
||||||
min-width: 150px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#help .help-item .description p {
|
#help .help-item .description p {
|
||||||
@ -1486,7 +1485,8 @@ kbd {
|
|||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
#context-menu {
|
#context-menu,
|
||||||
|
.textcomplete-menu {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@ -1505,7 +1505,8 @@ kbd {
|
|||||||
background-color: rgba(0, 0, 0, .1);
|
background-color: rgba(0, 0, 0, .1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.context-menu-item {
|
.context-menu-item,
|
||||||
|
.textcomplete-item {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: block;
|
display: block;
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
@ -1514,15 +1515,33 @@ kbd {
|
|||||||
margin-bottom: 6px;
|
margin-bottom: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.context-menu-item:hover {
|
.context-menu-item:hover,
|
||||||
|
.textcomplete-item:hover,
|
||||||
|
.textcomplete-menu .active {
|
||||||
background-color: #f6f6f6;
|
background-color: #f6f6f6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.context-menu-item:before {
|
.context-menu-item:before,
|
||||||
|
.textcomplete-item:before {
|
||||||
width: 20px;
|
width: 20px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.textcomplete-item a {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.textcomplete-item a:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.textcomplete-item .emoji {
|
||||||
|
margin-right: 8px;
|
||||||
|
width: 16px;
|
||||||
|
text-align: center;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tooltips v0.5.3
|
* Tooltips v0.5.3
|
||||||
* See http://primercss.io/tooltips/
|
* See http://primercss.io/tooltips/
|
||||||
|
@ -532,6 +532,49 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<h2>Autocompletion</h2>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Start typing the following characters followed by any letter to
|
||||||
|
trigger the autocompletion dropdown:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="help-item">
|
||||||
|
<div class="subject">
|
||||||
|
<code>@</code>
|
||||||
|
</div>
|
||||||
|
<div class="description">
|
||||||
|
<p>Nickname</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="help-item">
|
||||||
|
<div class="subject">
|
||||||
|
<code>#</code>
|
||||||
|
</div>
|
||||||
|
<div class="description">
|
||||||
|
<p>Channel</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="help-item">
|
||||||
|
<div class="subject">
|
||||||
|
<code>/</code>
|
||||||
|
</div>
|
||||||
|
<div class="description">
|
||||||
|
<p>Commands (see list of commands below)</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="help-item">
|
||||||
|
<div class="subject">
|
||||||
|
<code>:</code>
|
||||||
|
</div>
|
||||||
|
<div class="description">
|
||||||
|
<p>Emoji</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<h2>Commands</h2>
|
<h2>Commands</h2>
|
||||||
|
|
||||||
<p>All commands can be autocompleted with <kbd>tab</kbd>.</p>
|
<p>All commands can be autocompleted with <kbd>tab</kbd>.</p>
|
||||||
|
4
client/js/libs/jquery/inputhistory.js
vendored
4
client/js/libs/jquery/inputhistory.js
vendored
@ -34,7 +34,7 @@ import jQuery from "jquery";
|
|||||||
var key = e.which;
|
var key = e.which;
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case 13: // Enter
|
case 13: // Enter
|
||||||
if (e.shiftKey) {
|
if (e.shiftKey || self.data("autocompleting")) {
|
||||||
return; // multiline input
|
return; // multiline input
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ import jQuery from "jquery";
|
|||||||
case 38: // Up
|
case 38: // Up
|
||||||
case 40: // Down
|
case 40: // Down
|
||||||
// NOTICE: This is specific to The Lounge.
|
// NOTICE: This is specific to The Lounge.
|
||||||
if (e.ctrlKey || e.metaKey) {
|
if (e.ctrlKey || e.metaKey || self.data("autocompleting")) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
1434
client/js/libs/simplemap.json
Normal file
1434
client/js/libs/simplemap.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -2,12 +2,14 @@
|
|||||||
|
|
||||||
// vendor libraries
|
// vendor libraries
|
||||||
require("jquery-ui/ui/widgets/sortable");
|
require("jquery-ui/ui/widgets/sortable");
|
||||||
|
require("jquery-textcomplete");
|
||||||
const $ = require("jquery");
|
const $ = require("jquery");
|
||||||
const moment = require("moment");
|
const moment = require("moment");
|
||||||
const Mousetrap = require("mousetrap");
|
const Mousetrap = require("mousetrap");
|
||||||
const URI = require("urijs");
|
const URI = require("urijs");
|
||||||
|
|
||||||
// our libraries
|
// our libraries
|
||||||
|
const emojiMap = require("./libs/simplemap.json");
|
||||||
require("./libs/jquery/inputhistory");
|
require("./libs/jquery/inputhistory");
|
||||||
require("./libs/jquery/stickyscroll");
|
require("./libs/jquery/stickyscroll");
|
||||||
require("./libs/jquery/tabcomplete");
|
require("./libs/jquery/tabcomplete");
|
||||||
@ -41,6 +43,73 @@ $(function() {
|
|||||||
|
|
||||||
var favicon = $("#favicon");
|
var favicon = $("#favicon");
|
||||||
|
|
||||||
|
// Autocompletion Strategies
|
||||||
|
|
||||||
|
const emojiStrategy = {
|
||||||
|
id: "emoji",
|
||||||
|
match: /\B:([-+\w]*)$/,
|
||||||
|
search(term, callback) {
|
||||||
|
callback(Object.keys(emojiMap).filter(name => name.indexOf(term) === 0));
|
||||||
|
},
|
||||||
|
template(value) {
|
||||||
|
return `<span class="emoji">${emojiMap[value]}</span> ${value}`;
|
||||||
|
},
|
||||||
|
replace(value) {
|
||||||
|
return emojiMap[value];
|
||||||
|
},
|
||||||
|
index: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
const nicksStrategy = {
|
||||||
|
id: "nicks",
|
||||||
|
match: /\B(@([a-zA-Z_[\]\\^{}|`@][a-zA-Z0-9_[\]\\^{}|`-]*)?)$/,
|
||||||
|
search(term, callback) {
|
||||||
|
term = term.slice(1);
|
||||||
|
if (term[0] === "@") {
|
||||||
|
callback(completeNicks(term.slice(1)).map(val => "@" + val));
|
||||||
|
} else {
|
||||||
|
callback(completeNicks(term));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template(value) {
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
replace(value) {
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
index: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
const chanStrategy = {
|
||||||
|
id: "chans",
|
||||||
|
match: /\B((#|\+|&|![A-Z0-9]{5})([^\x00\x0A\x0D\x20\x2C\x3A]+(:[^\x00\x0A\x0D\x20\x2C\x3A]*)?)?)$/,
|
||||||
|
search(term, callback, match) {
|
||||||
|
callback(completeChans(match[0]));
|
||||||
|
},
|
||||||
|
template(value) {
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
replace(value) {
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
index: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
const commandStrategy = {
|
||||||
|
id: "commands",
|
||||||
|
match: /^\/(\w*)$/,
|
||||||
|
search(term, callback) {
|
||||||
|
callback(completeCommands("/" + term));
|
||||||
|
},
|
||||||
|
template(value) {
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
replace(value) {
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
index: 1
|
||||||
|
};
|
||||||
|
|
||||||
socket.on("auth", function(data) {
|
socket.on("auth", function(data) {
|
||||||
var login = $("#sign-in");
|
var login = $("#sign-in");
|
||||||
var token;
|
var token;
|
||||||
@ -638,7 +707,18 @@ $(function() {
|
|||||||
|
|
||||||
chat.find(".chan.active .chat").trigger("msg.sticky"); // fix growing
|
chat.find(".chan.active .chat").trigger("msg.sticky"); // fix growing
|
||||||
})
|
})
|
||||||
.tab(complete, {hint: false});
|
.tab(completeNicks, {hint: false})
|
||||||
|
.textcomplete([emojiStrategy, nicksStrategy, chanStrategy, commandStrategy], {
|
||||||
|
dropdownClassName: "textcomplete-menu",
|
||||||
|
placement: "top"
|
||||||
|
}).on({
|
||||||
|
"textComplete:show": function() {
|
||||||
|
$(this).data("autocompleting", true);
|
||||||
|
},
|
||||||
|
"textComplete:hide": function() {
|
||||||
|
$(this).data("autocompleting", false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
var focus = $.noop;
|
var focus = $.noop;
|
||||||
if (!("ontouchstart" in window || navigator.maxTouchPoints > 0)) {
|
if (!("ontouchstart" in window || navigator.maxTouchPoints > 0)) {
|
||||||
@ -1272,18 +1352,31 @@ $(function() {
|
|||||||
.find(".messages .msg, .date-marker").remove();
|
.find(".messages .msg, .date-marker").remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
function complete(word) {
|
function completeNicks(word) {
|
||||||
var words = constants.commands.slice();
|
const users = chat.find(".active").find(".users");
|
||||||
var users = chat.find(".active").find(".users");
|
const words = users.data("nicks");
|
||||||
var nicks = users.data("nicks");
|
|
||||||
|
|
||||||
for (var i in nicks) {
|
return $.grep(
|
||||||
words.push(nicks[i]);
|
words,
|
||||||
|
w => !w.toLowerCase().indexOf(word.toLowerCase())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function completeCommands(word) {
|
||||||
|
const words = constants.commands.slice();
|
||||||
|
|
||||||
|
return $.grep(
|
||||||
|
words,
|
||||||
|
w => !w.toLowerCase().indexOf(word.toLowerCase())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function completeChans(word) {
|
||||||
|
const words = [];
|
||||||
|
|
||||||
sidebar.find(".chan")
|
sidebar.find(".chan")
|
||||||
.each(function() {
|
.each(function() {
|
||||||
var self = $(this);
|
const self = $(this);
|
||||||
if (!self.hasClass("lobby")) {
|
if (!self.hasClass("lobby")) {
|
||||||
words.push(self.data("title"));
|
words.push(self.data("title"));
|
||||||
}
|
}
|
||||||
@ -1291,9 +1384,7 @@ $(function() {
|
|||||||
|
|
||||||
return $.grep(
|
return $.grep(
|
||||||
words,
|
words,
|
||||||
function(w) {
|
w => !w.toLowerCase().indexOf(word.toLowerCase())
|
||||||
return !w.toLowerCase().indexOf(word.toLowerCase());
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,6 +68,7 @@
|
|||||||
"handlebars": "4.0.6",
|
"handlebars": "4.0.6",
|
||||||
"handlebars-loader": "1.5.0",
|
"handlebars-loader": "1.5.0",
|
||||||
"jquery": "3.2.1",
|
"jquery": "3.2.1",
|
||||||
|
"jquery-textcomplete": "1.8.0",
|
||||||
"jquery-ui": "1.12.1",
|
"jquery-ui": "1.12.1",
|
||||||
"mocha": "3.3.0",
|
"mocha": "3.3.0",
|
||||||
"mousetrap": "1.6.1",
|
"mousetrap": "1.6.1",
|
||||||
|
@ -13,6 +13,7 @@ let config = {
|
|||||||
"js/bundle.vendor.js": [
|
"js/bundle.vendor.js": [
|
||||||
"handlebars/runtime",
|
"handlebars/runtime",
|
||||||
"jquery",
|
"jquery",
|
||||||
|
"jquery-textcomplete",
|
||||||
"jquery-ui/ui/widgets/sortable",
|
"jquery-ui/ui/widgets/sortable",
|
||||||
"moment",
|
"moment",
|
||||||
"mousetrap",
|
"mousetrap",
|
||||||
|
Loading…
Reference in New Issue
Block a user