Merge pull request #2150 from thelounge/xpaw/chat-flexbox
Cleanup chat/userlist to use flexbox, fix a couple of bugs
This commit is contained in:
commit
85efebc6be
@ -498,8 +498,8 @@ kbd {
|
|||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
#viewport.rt #chat .sidebar {
|
#viewport.rt #chat .userlist {
|
||||||
right: -180px;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#sidebar {
|
#sidebar {
|
||||||
@ -954,7 +954,8 @@ button.collapse-network:first-child:nth-last-child(3) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#chat .chan.active {
|
#chat .chan.active {
|
||||||
display: block;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
#chat .condensed {
|
#chat .condensed {
|
||||||
@ -1006,48 +1007,40 @@ button.collapse-network:first-child:nth-last-child(3) {
|
|||||||
|
|
||||||
#windows #form .input,
|
#windows #form .input,
|
||||||
.messages .msg,
|
.messages .msg,
|
||||||
.sidebar {
|
.userlist {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
}
|
}
|
||||||
|
|
||||||
#windows #chat .header {
|
#windows #chat .header {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#chat .chat,
|
#chat .chat-content {
|
||||||
#chat .sidebar {
|
display: flex;
|
||||||
top: 48px;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#chat .chat {
|
#chat .chat {
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
will-change: transform, scroll-position;
|
|
||||||
-webkit-overflow-scrolling: touch;
|
|
||||||
position: absolute;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-grow: 1;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
-webkit-overflow-scrolling: touch;
|
||||||
|
|
||||||
#chat .channel .chat {
|
|
||||||
right: 180px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#viewport.rt .chat {
|
#viewport.rt .chat {
|
||||||
right: 0;
|
right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#chat .sidebar {
|
#chat .userlist {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-left: 1px solid #e7e7e7;
|
border-left: 1px solid #e7e7e7;
|
||||||
bottom: 0;
|
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
width: 180px;
|
width: 180px;
|
||||||
transition: right 0.4s;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-shrink: 0;
|
||||||
touch-action: pan-y;
|
touch-action: pan-y;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1456,16 +1449,13 @@ part/quit messages where we don't load previews (adds a blank line otherwise) */
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#chat .count {
|
#chat .userlist .count {
|
||||||
background: #fafafa;
|
background: #fafafa;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
left: 0;
|
flex-shrink: 0;
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#chat .search {
|
#chat .userlist .search {
|
||||||
color: #222;
|
color: #222;
|
||||||
border: 0;
|
border: 0;
|
||||||
background: none;
|
background: none;
|
||||||
@ -1476,17 +1466,14 @@ part/quit messages where we don't load previews (adds a blank line otherwise) */
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#chat .names {
|
#chat .userlist .names {
|
||||||
bottom: 0;
|
flex-grow: 1;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
will-change: transform, scroll-position;
|
|
||||||
-webkit-overflow-scrolling: touch;
|
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
position: absolute;
|
|
||||||
top: 48px;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
touch-action: pan-y;
|
touch-action: pan-y;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
}
|
}
|
||||||
|
|
||||||
#chat .names-filtered {
|
#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,
|
.context-menu-item:hover,
|
||||||
.textcomplete-item:hover,
|
.textcomplete-item:hover,
|
||||||
.textcomplete-menu .active,
|
.textcomplete-menu .active,
|
||||||
#chat .users .user.active {
|
#chat .userlist .user.active {
|
||||||
background-color: #f6f6f6;
|
background-color: #f6f6f6;
|
||||||
transition: none;
|
transition: none;
|
||||||
}
|
}
|
||||||
@ -2320,10 +2307,6 @@ part/quit messages where we don't load previews (adds a blank line otherwise) */
|
|||||||
left: 0;
|
left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#chat .chat {
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#viewport .lt,
|
#viewport .lt,
|
||||||
#viewport .channel .rt {
|
#viewport .channel .rt {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -2333,16 +2316,17 @@ part/quit messages where we don't load previews (adds a blank line otherwise) */
|
|||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
#chat .channel .chat {
|
#chat .userlist {
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
transform: translateX(180px);
|
||||||
|
transition: transform 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
#chat .sidebar {
|
#viewport.rt #chat .userlist {
|
||||||
right: -180px;
|
display: flex;
|
||||||
}
|
transform: translateX(0);
|
||||||
|
|
||||||
#viewport.rt #chat .sidebar {
|
|
||||||
right: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#chat .header .title {
|
#chat .header .title {
|
||||||
|
@ -263,7 +263,7 @@ function fuzzyGrep(term, array) {
|
|||||||
|
|
||||||
function rawNicks() {
|
function rawNicks() {
|
||||||
const chan = chat.find(".active");
|
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 this channel has a list of nicks, just return it
|
||||||
if (users.length > 0) {
|
if (users.length > 0) {
|
||||||
|
@ -41,11 +41,12 @@ $(function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
viewport.on("click", ".rt", function(e) {
|
viewport.on("click", ".rt", function() {
|
||||||
const self = $(this);
|
const self = $(this);
|
||||||
viewport.toggleClass(self.prop("class"));
|
viewport.toggleClass(self.prop("class"));
|
||||||
e.stopPropagation();
|
chat.find(".chan.active .chat").trigger("keepToBottom");
|
||||||
chat.find(".chan.active .chat").trigger("msg.sticky");
|
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
function positionContextMenu(that, e) {
|
function positionContextMenu(that, e) {
|
||||||
@ -212,7 +213,7 @@ $(function() {
|
|||||||
+ Math.round(parseFloat(style.borderBottomWidth) || 0)
|
+ Math.round(parseFloat(style.borderBottomWidth) || 0)
|
||||||
) + "px";
|
) + "px";
|
||||||
|
|
||||||
chat.find(".chan.active .chat").trigger("msg.sticky"); // fix growing
|
chat.find(".chan.active .chat").trigger("keepToBottom"); // fix growing
|
||||||
});
|
});
|
||||||
|
|
||||||
let focus = $.noop;
|
let focus = $.noop;
|
||||||
@ -553,7 +554,7 @@ $(function() {
|
|||||||
text: "/whois " + itemData,
|
text: "/whois " + itemData,
|
||||||
});
|
});
|
||||||
|
|
||||||
$(`.channel.active .users .user[data-name="${itemData}"]`).trigger("click");
|
$(`.channel.active .userlist .user[data-name="${itemData}"]`).trigger("click");
|
||||||
},
|
},
|
||||||
query: function(itemData) {
|
query: function(itemData) {
|
||||||
const chan = utils.findCurrentNetworkChan(itemData);
|
const chan = utils.findCurrentNetworkChan(itemData);
|
||||||
|
@ -158,7 +158,7 @@ function renderUnreadMarker(template, firstUnread, channel) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderChannelUsers(data) {
|
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
|
const nicks = data.users
|
||||||
.concat() // Make a copy of the user list, sort is applied in-place
|
.concat() // Make a copy of the user list, sort is applied in-place
|
||||||
.sort((a, b) => b.lastMessage - a.lastMessage)
|
.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
|
// Before re-rendering the list of names, there might have been an entry
|
||||||
// marked as active (i.e. that was highlighted by keyboard navigation).
|
// marked as active (i.e. that was highlighted by keyboard navigation).
|
||||||
// It is `undefined` if there was none.
|
// It is `undefined` if there was none.
|
||||||
const previouslyActive = users.find(".active").data("name");
|
const previouslyActive = users.find(".active");
|
||||||
|
|
||||||
const search = users
|
const search = users
|
||||||
.find(".search")
|
.find(".search")
|
||||||
@ -185,11 +185,11 @@ function renderChannelUsers(data) {
|
|||||||
|
|
||||||
// If a nick was highlighted before re-rendering the lists, re-highlight it in
|
// If a nick was highlighted before re-rendering the lists, re-highlight it in
|
||||||
// the newly-rendered list.
|
// the newly-rendered list.
|
||||||
if (previouslyActive) {
|
if (previouslyActive.length > 0) {
|
||||||
// We need to un-highlight everything first because triggering `input` with
|
// We need to un-highlight everything first because triggering `input` with
|
||||||
// a value highlights the first entry.
|
// a value highlights the first entry.
|
||||||
users.find(".user").removeClass("active");
|
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;
|
return users;
|
||||||
|
@ -102,7 +102,7 @@ function processReceivedMessage(data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((data.msg.type === "message" || data.msg.type === "action") && channel.hasClass("channel")) {
|
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) {
|
if (nicks) {
|
||||||
const find = nicks.indexOf(data.msg.from.nick);
|
const find = nicks.indexOf(data.msg.from.nick);
|
||||||
|
@ -8,9 +8,9 @@ const templates = require("../views");
|
|||||||
|
|
||||||
const chat = $("#chat");
|
const chat = $("#chat");
|
||||||
|
|
||||||
chat.on("input", ".users .search", function() {
|
chat.on("input", ".userlist .search", function() {
|
||||||
const value = $(this).val();
|
const value = $(this).val();
|
||||||
const parent = $(this).closest(".users");
|
const parent = $(this).closest(".userlist");
|
||||||
const names = parent.find(".names-original");
|
const names = parent.find(".names-original");
|
||||||
const container = parent.find(".names-filtered");
|
const container = parent.find(".names-filtered");
|
||||||
|
|
||||||
@ -42,22 +42,24 @@ chat.on("input", ".users .search", function() {
|
|||||||
container.find(".user").first().addClass("active");
|
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
|
// Reset any potential selection, this is required in cas there is already a
|
||||||
// nick previously selected by keyboard
|
// nick previously selected by keyboard
|
||||||
$(".users .user").removeClass("active");
|
$(this).parent().find(".user.active").removeClass("active");
|
||||||
|
|
||||||
$(this).addClass("active");
|
$(this).addClass("active");
|
||||||
});
|
});
|
||||||
|
|
||||||
chat.on("mouseleave", ".users .user", function() {
|
chat.on("mouseleave", ".userlist .user", function() {
|
||||||
// Reset any potential selection
|
// Reset any potential selection
|
||||||
$(".users .user").removeClass("active");
|
$(this).parent().find(".user.active").removeClass("active");
|
||||||
});
|
});
|
||||||
|
|
||||||
exports.handleKeybinds = function(input) {
|
exports.handleKeybinds = function(input) {
|
||||||
Mousetrap(input.get(0)).bind(["up", "down"], (_e, key) => {
|
Mousetrap(input.get(0)).bind(["up", "down"], (e, key) => {
|
||||||
const userlists = input.closest(".users");
|
e.preventDefault();
|
||||||
|
|
||||||
|
const userlists = input.closest(".userlist");
|
||||||
let userlist;
|
let userlist;
|
||||||
|
|
||||||
// If input field has content, use the filtered list instead
|
// If input field has content, use the filtered list instead
|
||||||
@ -69,13 +71,17 @@ exports.handleKeybinds = function(input) {
|
|||||||
|
|
||||||
const users = userlist.find(".user");
|
const users = userlist.find(".user");
|
||||||
|
|
||||||
|
if (users.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Find which item in the array of users is currently selected, if any.
|
// Find which item in the array of users is currently selected, if any.
|
||||||
// Returns -1 if none.
|
// Returns -1 if none.
|
||||||
const activeIndex = users.toArray()
|
const activeIndex = users.toArray()
|
||||||
.findIndex((user) => user.classList.contains("active"));
|
.findIndex((user) => user.classList.contains("active"));
|
||||||
|
|
||||||
// Now that we know which user is active, reset any selection
|
// 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.
|
// Mark next/previous user as active.
|
||||||
if (key === "down") {
|
if (key === "down") {
|
||||||
@ -87,23 +93,13 @@ exports.handleKeybinds = function(input) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Adjust scroll when active item is outside of the visible area
|
// Adjust scroll when active item is outside of the visible area
|
||||||
const userlistHeight = userlist.height();
|
userlist.find(".user.active")[0].scrollIntoView(false);
|
||||||
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);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// When pressing Enter, open the context menu (emit a click) on the active
|
// When pressing Enter, open the context menu (emit a click) on the active
|
||||||
// user
|
// user
|
||||||
Mousetrap(input.get(0)).bind("enter", () => {
|
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) {
|
if (user.length) {
|
||||||
const clickEvent = new $.Event("click");
|
const clickEvent = new $.Event("click");
|
||||||
|
@ -48,7 +48,7 @@ function hasRoleInChannel(channel, roles) {
|
|||||||
const channelID = channel.data("id");
|
const channelID = channel.data("id");
|
||||||
const network = $("#sidebar .network").has(`.chan[data-id="${channelID}"]`);
|
const network = $("#sidebar .network").has(`.chan[data-id="${channelID}"]`);
|
||||||
const ownNick = network.data("nick");
|
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(", ."));
|
return user.parent().is("." + roles.join(", ."));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ a:hover,
|
|||||||
|
|
||||||
#windows .header .topic,
|
#windows .header .topic,
|
||||||
.messages .msg,
|
.messages .msg,
|
||||||
.sidebar {
|
.userlist {
|
||||||
line-height: 1.8;
|
line-height: 1.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#main,
|
#main,
|
||||||
#chat .sidebar,
|
#chat .userlist,
|
||||||
#windows .chan,
|
#windows .chan,
|
||||||
#windows .window {
|
#windows .window {
|
||||||
background: #333c4a;
|
background: #333c4a;
|
||||||
@ -50,7 +50,7 @@ body {
|
|||||||
#chat .content,
|
#chat .content,
|
||||||
#windows .header,
|
#windows .header,
|
||||||
#chat .user-mode::before,
|
#chat .user-mode::before,
|
||||||
#chat .sidebar {
|
#chat .userlist {
|
||||||
border-color: #2a323d;
|
border-color: #2a323d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#main,
|
#main,
|
||||||
#chat .sidebar,
|
#chat .userlist,
|
||||||
#windows .chan,
|
#windows .chan,
|
||||||
#windows .window {
|
#windows .window {
|
||||||
background: #3f3f3f;
|
background: #3f3f3f;
|
||||||
@ -76,7 +76,7 @@ body {
|
|||||||
#chat .content,
|
#chat .content,
|
||||||
#windows .header,
|
#windows .header,
|
||||||
#chat .user-mode::before,
|
#chat .user-mode::before,
|
||||||
#chat .sidebar {
|
#chat .userlist {
|
||||||
border-color: #333;
|
border-color: #333;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,22 +18,22 @@
|
|||||||
</span>
|
</span>
|
||||||
{{/equal}}
|
{{/equal}}
|
||||||
</div>
|
</div>
|
||||||
<div class="chat">
|
<div class="chat-content">
|
||||||
<div class="show-more{{#if messages.length}} show{{/if}}">
|
<div class="chat">
|
||||||
<button class="show-more-button" data-id="{{id}}">Show older messages</button>
|
<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>
|
||||||
<div class="messages" role="log" aria-live="polite" aria-relevant="additions"></div>
|
{{#equal type "channel"}}
|
||||||
</div>
|
<aside class="userlist">
|
||||||
{{#equal type "channel"}}
|
|
||||||
<aside class="sidebar">
|
|
||||||
<div class="users">
|
|
||||||
<div class="count">
|
<div class="count">
|
||||||
<input type="search" class="search" aria-label="Search among the user list" tabindex="-1">
|
<input type="search" class="search" aria-label="Search among the user list" tabindex="-1">
|
||||||
</div>
|
</div>
|
||||||
<div class="names names-filtered"></div>
|
<div class="names names-filtered"></div>
|
||||||
<div class="names names-original"></div>
|
<div class="names names-original"></div>
|
||||||
</div>
|
</aside>
|
||||||
</aside>
|
{{/equal}}
|
||||||
{{/equal}}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
Loading…
Reference in New Issue
Block a user