Cleanup chat/userlist to use flexbox, fix a couple of bugs

This commit is contained in:
Pavel Djundik 2018-03-04 22:03:11 +02:00
parent cbf82a1bc7
commit e719e4ff81
11 changed files with 76 additions and 95 deletions

View file

@ -498,8 +498,8 @@ kbd {
float: right;
}
#viewport.rt #chat .sidebar {
right: -180px;
#viewport.rt #chat .userlist {
display: none;
}
#sidebar {
@ -954,7 +954,8 @@ button.collapse-network:first-child:nth-last-child(3) {
}
#chat .chan.active {
display: block;
display: flex;
flex-direction: column;
}
#chat .condensed {
@ -1006,48 +1007,40 @@ button.collapse-network:first-child:nth-last-child(3) {
#windows #form .input,
.messages .msg,
.sidebar {
.userlist {
font-size: 14px;
line-height: 1.4;
}
#windows #chat .header {
display: flex;
flex-shrink: 0;
}
#chat .chat,
#chat .sidebar {
top: 48px;
#chat .chat-content {
display: flex;
flex-grow: 1;
}
#chat .chat {
bottom: 0;
left: 0;
right: 0;
overflow: auto;
will-change: transform, scroll-position;
-webkit-overflow-scrolling: touch;
position: absolute;
display: flex;
flex-grow: 1;
flex-direction: column;
}
#chat .channel .chat {
right: 180px;
-webkit-overflow-scrolling: touch;
}
#viewport.rt .chat {
right: 0;
}
#chat .sidebar {
#chat .userlist {
background: #fff;
border-left: 1px solid #e7e7e7;
bottom: 0;
position: absolute;
right: 0;
width: 180px;
transition: right 0.4s;
display: flex;
flex-direction: column;
flex-shrink: 0;
touch-action: pan-y;
}
@ -1456,16 +1449,13 @@ part/quit messages where we don't load previews (adds a blank line otherwise) */
display: none;
}
#chat .count {
#chat .userlist .count {
background: #fafafa;
height: 48px;
left: 0;
position: absolute;
right: 0;
top: 0;
flex-shrink: 0;
}
#chat .search {
#chat .userlist .search {
color: #222;
border: 0;
background: none;
@ -1476,17 +1466,14 @@ part/quit messages where we don't load previews (adds a blank line otherwise) */
width: 100%;
}
#chat .names {
bottom: 0;
#chat .userlist .names {
flex-grow: 1;
overflow: auto;
overflow-x: hidden;
will-change: transform, scroll-position;
-webkit-overflow-scrolling: touch;
padding-bottom: 10px;
position: absolute;
top: 48px;
width: 100%;
touch-action: pan-y;
-webkit-overflow-scrolling: touch;
}
#chat .names-filtered {
@ -1973,7 +1960,7 @@ part/quit messages where we don't load previews (adds a blank line otherwise) */
.context-menu-item:hover,
.textcomplete-item:hover,
.textcomplete-menu .active,
#chat .users .user.active {
#chat .userlist .user.active {
background-color: #f6f6f6;
transition: none;
}
@ -2320,10 +2307,6 @@ part/quit messages where we don't load previews (adds a blank line otherwise) */
left: 0;
}
#chat .chat {
right: 0;
}
#viewport .lt,
#viewport .channel .rt {
display: flex;
@ -2333,16 +2316,17 @@ part/quit messages where we don't load previews (adds a blank line otherwise) */
display: block;
}
#chat .channel .chat {
#chat .userlist {
height: 100%;
position: absolute;
right: 0;
transform: translateX(180px);
transition: transform 0.2s;
}
#chat .sidebar {
right: -180px;
}
#viewport.rt #chat .sidebar {
right: 0;
#viewport.rt #chat .userlist {
display: flex;
transform: translateX(0);
}
#chat .header .title {

View file

@ -263,7 +263,7 @@ function fuzzyGrep(term, array) {
function rawNicks() {
const chan = chat.find(".active");
const users = chan.find(".users");
const users = chan.find(".userlist");
// If this channel has a list of nicks, just return it
if (users.length > 0) {

View file

@ -41,11 +41,12 @@ $(function() {
}
});
viewport.on("click", ".rt", function(e) {
viewport.on("click", ".rt", function() {
const self = $(this);
viewport.toggleClass(self.prop("class"));
e.stopPropagation();
chat.find(".chan.active .chat").trigger("msg.sticky");
chat.find(".chan.active .chat").trigger("keepToBottom");
return false;
});
function positionContextMenu(that, e) {
@ -212,7 +213,7 @@ $(function() {
+ Math.round(parseFloat(style.borderBottomWidth) || 0)
) + "px";
chat.find(".chan.active .chat").trigger("msg.sticky"); // fix growing
chat.find(".chan.active .chat").trigger("keepToBottom"); // fix growing
});
let focus = $.noop;
@ -553,7 +554,7 @@ $(function() {
text: "/whois " + itemData,
});
$(`.channel.active .users .user[data-name="${itemData}"]`).trigger("click");
$(`.channel.active .userlist .user[data-name="${itemData}"]`).trigger("click");
},
query: function(itemData) {
const chan = utils.findCurrentNetworkChan(itemData);

View file

@ -158,7 +158,7 @@ function renderUnreadMarker(template, firstUnread, channel) {
}
function renderChannelUsers(data) {
const users = chat.find("#chan-" + data.id).find(".users");
const users = chat.find("#chan-" + data.id).find(".userlist");
const nicks = data.users
.concat() // Make a copy of the user list, sort is applied in-place
.sort((a, b) => b.lastMessage - a.lastMessage)
@ -167,7 +167,7 @@ function renderChannelUsers(data) {
// Before re-rendering the list of names, there might have been an entry
// marked as active (i.e. that was highlighted by keyboard navigation).
// It is `undefined` if there was none.
const previouslyActive = users.find(".active").data("name");
const previouslyActive = users.find(".active");
const search = users
.find(".search")
@ -185,11 +185,11 @@ function renderChannelUsers(data) {
// If a nick was highlighted before re-rendering the lists, re-highlight it in
// the newly-rendered list.
if (previouslyActive) {
if (previouslyActive.length > 0) {
// We need to un-highlight everything first because triggering `input` with
// a value highlights the first entry.
users.find(".user").removeClass("active");
users.find(`.user[data-name="${previouslyActive}"]`).addClass("active");
users.find(`.user[data-name="${previouslyActive.data("name")}"]`).addClass("active");
}
return users;

View file

@ -102,7 +102,7 @@ function processReceivedMessage(data) {
}
if ((data.msg.type === "message" || data.msg.type === "action") && channel.hasClass("channel")) {
const nicks = channel.find(".users").data("nicks");
const nicks = channel.find(".userlist").data("nicks");
if (nicks) {
const find = nicks.indexOf(data.msg.from.nick);

View file

@ -8,9 +8,9 @@ const templates = require("../views");
const chat = $("#chat");
chat.on("input", ".users .search", function() {
chat.on("input", ".userlist .search", function() {
const value = $(this).val();
const parent = $(this).closest(".users");
const parent = $(this).closest(".userlist");
const names = parent.find(".names-original");
const container = parent.find(".names-filtered");
@ -42,22 +42,24 @@ chat.on("input", ".users .search", function() {
container.find(".user").first().addClass("active");
});
chat.on("mouseenter", ".users .user", function() {
chat.on("mouseenter", ".userlist .user", function() {
// Reset any potential selection, this is required in cas there is already a
// nick previously selected by keyboard
$(".users .user").removeClass("active");
$(this).parent().find(".user.active").removeClass("active");
$(this).addClass("active");
});
chat.on("mouseleave", ".users .user", function() {
chat.on("mouseleave", ".userlist .user", function() {
// Reset any potential selection
$(".users .user").removeClass("active");
$(this).parent().find(".user.active").removeClass("active");
});
exports.handleKeybinds = function(input) {
Mousetrap(input.get(0)).bind(["up", "down"], (_e, key) => {
const userlists = input.closest(".users");
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
@ -69,13 +71,17 @@ exports.handleKeybinds = function(input) {
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
userlists.find(".user").removeClass("active");
userlist.find(".user.active").removeClass("active");
// Mark next/previous user as active.
if (key === "down") {
@ -87,23 +93,13 @@ exports.handleKeybinds = function(input) {
}
// Adjust scroll when active item is outside of the visible area
const userlistHeight = userlist.height();
const userlistScroll = userlist.scrollTop();
const active = $(".user.active");
const activeTop = active.position().top;
const activeHeight = active.height();
if (activeTop > userlistHeight - activeHeight) {
userlist.scrollTop(userlistScroll + activeTop - userlistHeight + activeHeight);
} else if (activeTop < 0) {
userlist.scrollTop(userlistScroll + activeTop - activeHeight);
}
userlist.find(".user.active")[0].scrollIntoView(false);
});
// When pressing Enter, open the context menu (emit a click) on the active
// user
Mousetrap(input.get(0)).bind("enter", () => {
const user = input.closest(".users").find(".user.active");
const user = input.closest(".userlist").find(".user.active");
if (user.length) {
const clickEvent = new $.Event("click");

View file

@ -48,7 +48,7 @@ function hasRoleInChannel(channel, roles) {
const channelID = channel.data("id");
const network = $("#sidebar .network").has(`.chan[data-id="${channelID}"]`);
const ownNick = network.data("nick");
const user = channel.find(`.users .user[data-name="${escape(ownNick)}"]`).first();
const user = channel.find(`.names-original .user[data-name="${escape(ownNick)}"]`).first();
return user.parent().is("." + roles.join(", ."));
}

View file

@ -91,7 +91,7 @@ a:hover,
#windows .header .topic,
.messages .msg,
.sidebar {
.userlist {
line-height: 1.8;
}

View file

@ -31,7 +31,7 @@ body {
}
#main,
#chat .sidebar,
#chat .userlist,
#windows .chan,
#windows .window {
background: #333c4a;
@ -50,7 +50,7 @@ body {
#chat .content,
#windows .header,
#chat .user-mode::before,
#chat .sidebar {
#chat .userlist {
border-color: #2a323d;
}

View file

@ -32,7 +32,7 @@ body {
}
#main,
#chat .sidebar,
#chat .userlist,
#windows .chan,
#windows .window {
background: #3f3f3f;
@ -76,7 +76,7 @@ body {
#chat .content,
#windows .header,
#chat .user-mode::before,
#chat .sidebar {
#chat .userlist {
border-color: #333;
}

View file

@ -18,22 +18,22 @@
</span>
{{/equal}}
</div>
<div class="chat">
<div class="show-more{{#if messages.length}} show{{/if}}">
<button class="show-more-button" data-id="{{id}}">Show older messages</button>
<div class="chat-content">
<div class="chat">
<div class="show-more{{#if messages.length}} show{{/if}}">
<button class="show-more-button" data-id="{{id}}">Show older messages</button>
</div>
<div class="messages" role="log" aria-live="polite" aria-relevant="additions"></div>
</div>
<div class="messages" role="log" aria-live="polite" aria-relevant="additions"></div>
</div>
{{#equal type "channel"}}
<aside class="sidebar">
<div class="users">
{{#equal type "channel"}}
<aside class="userlist">
<div class="count">
<input type="search" class="search" aria-label="Search among the user list" tabindex="-1">
</div>
<div class="names names-filtered"></div>
<div class="names names-original"></div>
</div>
</aside>
{{/equal}}
</aside>
{{/equal}}
</div>
</div>
{{/each}}