thelounge/client/js/userlist.js
dgw e5a6417a82 Scroll to newly joined/activated channels
Add a new utility function for scrolling elements into view with the
same, consistent options, and use it for both the new channel scrolling
behavior and the existing userlist scroll code.

Implements & resolves #2062
2018-03-08 10:30:07 -06:00

114 lines
3.1 KiB
JavaScript

"use strict";
const $ = require("jquery");
const fuzzy = require("fuzzy");
const Mousetrap = require("mousetrap");
const templates = require("../views");
const utils = require("./utils");
const chat = $("#chat");
chat.on("input", ".userlist .search", function() {
const value = $(this).val();
const parent = $(this).closest(".userlist");
const names = parent.find(".names-original");
const container = parent.find(".names-filtered");
// Input content has changed, reset the potential selection
parent.find(".user").removeClass("active");
if (!value.length) {
container.hide();
names.show();
return;
}
const fuzzyOptions = {
pre: "<b>",
post: "</b>",
extract: (el) => $(el).text(),
};
const result = fuzzy.filter(
value,
names.find(".user").toArray(),
fuzzyOptions
);
names.hide();
container.html(templates.user_filtered({matches: result})).show();
// Mark the first result as active for convenience
container.find(".user").first().addClass("active");
});
chat.on("mouseenter", ".userlist .user", function() {
// Reset any potential selection, this is required in cas there is already a
// nick previously selected by keyboard
$(this).parent().find(".user.active").removeClass("active");
$(this).addClass("active");
});
chat.on("mouseleave", ".userlist .user", function() {
// Reset any potential selection
$(this).parent().find(".user.active").removeClass("active");
});
exports.handleKeybinds = function(input) {
Mousetrap(input.get(0)).bind(["up", "down"], (e, key) => {
e.preventDefault();
const userlists = input.closest(".userlist");
let userlist;
// If input field has content, use the filtered list instead
if (input.val().length) {
userlist = userlists.find(".names-filtered");
} else {
userlist = userlists.find(".names-original");
}
const users = userlist.find(".user");
if (users.length === 0) {
return;
}
// Find which item in the array of users is currently selected, if any.
// Returns -1 if none.
const activeIndex = users.toArray()
.findIndex((user) => user.classList.contains("active"));
// Now that we know which user is active, reset any selection
userlist.find(".user.active").removeClass("active");
// Mark next/previous user as active.
if (key === "down") {
// If no users or last user were marked as active, mark the first one.
users.eq((activeIndex + 1) % users.length).addClass("active");
} else {
// If no users or first user was marked as active, mark the last one.
users.eq(Math.max(activeIndex, 0) - 1).addClass("active");
}
// Adjust scroll when active item is outside of the visible area
utils.scrollIntoViewNicely(userlist.find(".user.active")[0]);
});
// When pressing Enter, open the context menu (emit a click) on the active
// user
Mousetrap(input.get(0)).bind("enter", () => {
const user = input.closest(".userlist").find(".user.active");
if (user.length) {
const clickEvent = new $.Event("click");
const userOffset = user.offset();
clickEvent.pageX = userOffset.left;
clickEvent.pageY = userOffset.top + user.height();
user.trigger(clickEvent);
}
});
};