Improve UI of previews

- Remove margins around thumbnails and images, increase radius, etc. for a fresher look overall
- Increase preview body contrast just enough to pass AA standards
- Add a `More`/`Less` button to expand previews that take more than one line. Button is added only when necessary
- Use a zoom-in cursor on hover for thumbnails
- Improve preview message errors
This commit is contained in:
Jérémie Astori 2018-03-22 01:17:44 -04:00
parent ff5a231ea0
commit 7355db94d6
No known key found for this signature in database
GPG key ID: B9A4F245CD67BDE8
5 changed files with 164 additions and 40 deletions

View file

@ -238,6 +238,7 @@ kbd {
#chat .nick .from::before, #chat .nick .from::before,
#chat .action .from::before, #chat .action .from::before,
#chat .toggle-button::after, #chat .toggle-button::after,
#chat .toggle-content .more-caret::before,
#version-checker::before, #version-checker::before,
.context-menu-item::before, .context-menu-item::before,
#help .website-link::before, #help .website-link::before,
@ -1332,75 +1333,112 @@ kbd {
color: #f00; color: #f00;
} }
#chat .toggle-content.opened .more-caret, /* Expand/Collapse link previews */
#chat .toggle-button.opened, /* Thumbnail toggle */ #chat .toggle-button.opened, /* Thumbnail toggle */
#chat .msg.condensed:not(.closed) .toggle-button { /* Expanded status message toggle */ #chat .msg.condensed:not(.closed) .toggle-button { /* Expanded status message toggle */
transform: rotate(90deg); transform: rotate(90deg);
} }
#chat .toggle-content { #chat .toggle-content {
background: #f5f5f5; background: #f6f6f6;
border-radius: 2px; border-radius: 5px;
display: none; display: none;
color: #222;
max-width: 100%; max-width: 100%;
margin: 0; margin: 0;
margin-top: 6px; margin-top: 6px;
overflow: hidden; overflow: hidden;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
contain: content; /* This fixes a display bug where border-radius stopped working on image link hover */
} }
#chat .toggle-type-error { /* This applies to images of preview-type-image and thumbnails of preview-type-link */
background: transparent;
}
#chat .toggle-content img { #chat .toggle-content img {
max-width: 100%; max-width: 100%;
max-height: 128px; max-height: 128px;
display: block; display: block;
cursor: zoom-in;
} }
#chat .prefetch-error { #chat .toggle-content pre.prefetch-error {
padding: 0;
margin: 0;
color: inherit;
background-color: transparent;
}
#chat .toggle-content .prefetch-error {
display: none; display: none;
} }
#chat .toggle-content.opened .prefetch-error {
display: inline;
}
/* This applies to thumbnails of preview-type-link only */
#chat .toggle-content .thumb { #chat .toggle-content .thumb {
max-width: 48px; max-height: 54px;
max-height: 38px; max-width: 96px;
} }
#chat .toggle-thumbnail { #chat .toggle-type-error,
padding: 6px; #chat .toggle-content .toggle-text {
padding: 8px 10px;
} }
#chat .toggle-text { #chat .toggle-content .toggle-text {
padding: 6px;
min-width: 0;
display: flex;
flex-direction: column;
white-space: nowrap; white-space: nowrap;
color: inherit;
}
#chat .toggle-text:not(:first-child) {
padding-left: 0;
}
#chat .toggle-content .head,
#chat .toggle-content .body {
text-overflow: ellipsis;
overflow: hidden; overflow: hidden;
color: inherit; }
#chat .toggle-content.opened .toggle-text {
white-space: normal;
} }
#chat .toggle-content .head { #chat .toggle-content .head {
display: flex;
align-items: flex-start;
font-weight: bold; font-weight: bold;
color: #222;
} }
#chat .toggle-content .body { #chat .toggle-type-error,
color: #999; #chat .toggle-text .body {
color: #717171;
}
#chat .toggle-text a {
color: inherit;
}
#chat .toggle-text .overflowable {
text-overflow: ellipsis;
overflow: hidden;
flex-grow: 1;
}
#chat .toggle-content .more {
color: #50a656;
font-weight: normal;
margin-left: 10px;
flex-shrink: 0;
}
#chat .toggle-content .more::after {
content: attr(aria-label);
}
#chat .toggle-content .more-caret {
display: inline-block;
transition: transform 0.2s;
}
#chat .toggle-content .more-caret::before {
content: "\f0da"; /* https://fontawesome.com/icons/caret-right?style=solid */
} }
#chat .toggle-content.show { #chat .toggle-content.show {
display: inline-flex !important; display: inline-flex !important;
align-items: flex-start;
} }
#chat audio { #chat audio {
@ -2322,6 +2360,11 @@ part/quit messages where we don't load previews (adds a blank line otherwise) */
#chat .header .title { #chat .header .title {
padding-left: 6px; padding-left: 6px;
} }
#chat .toggle-content .thumb {
max-height: 58px;
max-width: 104px;
}
} }
@media (max-width: 479px) { @media (max-width: 479px) {

View file

@ -49,6 +49,7 @@ $(function() {
slideoutMenu.toggle(isOpen); slideoutMenu.toggle(isOpen);
storeSidebarVisibility("thelounge.state.sidebar", isOpen); storeSidebarVisibility("thelounge.state.sidebar", isOpen);
utils.togglePreviewMoreButtonsIfNeeded();
}); });
viewport.on("click", ".rt", function() { viewport.on("click", ".rt", function() {
@ -57,6 +58,7 @@ $(function() {
viewport.toggleClass("rt", isOpen); viewport.toggleClass("rt", isOpen);
chat.find(".chan.active .chat").trigger("keepToBottom"); chat.find(".chan.active .chat").trigger("keepToBottom");
storeSidebarVisibility("thelounge.state.userlist", isOpen); storeSidebarVisibility("thelounge.state.userlist", isOpen);
utils.togglePreviewMoreButtonsIfNeeded();
return false; return false;
}); });
@ -252,6 +254,8 @@ $(function() {
if (!keepSidebarOpen && $(window).outerWidth() < utils.mobileViewportPixels) { if (!keepSidebarOpen && $(window).outerWidth() < utils.mobileViewportPixels) {
slideoutMenu.toggle(false); slideoutMenu.toggle(false);
} }
utils.togglePreviewMoreButtonsIfNeeded();
} }
const lastActive = $("#windows > .active"); const lastActive = $("#windows > .active");

View file

@ -66,6 +66,26 @@ function appendPreview(preview, msg, template) {
previewContainer.append(template); previewContainer.append(template);
const moreBtn = previewContainer.find(".more");
const previewContent = previewContainer.find(".toggle-content")[0];
const showMoreIfNeeded = () => {
if (preview.type === "link") {
const isVisible = moreBtn.is(":visible");
const shouldShow = previewContent.offsetWidth >= previewContainer[0].offsetWidth;
if (!isVisible && shouldShow) {
moreBtn.show();
} else if (isVisible && !shouldShow) {
togglePreviewMore(moreBtn, false);
moreBtn.hide();
}
}
};
$(window).on("resize", showMoreIfNeeded);
window.requestAnimationFrame(showMoreIfNeeded);
if (activeChannelId === channelId) { if (activeChannelId === channelId) {
container.trigger("keepToBottom"); container.trigger("keepToBottom");
} }
@ -97,6 +117,24 @@ $("#chat").on("click", ".text .toggle-button", function() {
} }
}); });
$("#chat").on("click", ".toggle-content .more", function() {
togglePreviewMore($(this));
return false;
});
function togglePreviewMore(moreBtn, state = undefined) {
moreBtn.closest(".toggle-content").toggleClass("opened", state);
const isExpanded = moreBtn.closest(".toggle-content").hasClass("opened");
moreBtn.attr("aria-expanded", isExpanded);
if (isExpanded) {
moreBtn.attr("aria-label", moreBtn.data("opened-text"));
} else {
moreBtn.attr("aria-label", moreBtn.data("closed-text"));
}
}
/* Image viewer */ /* Image viewer */
const imageViewer = $("#image-viewer"); const imageViewer = $("#image-viewer");

View file

@ -23,6 +23,7 @@ module.exports = {
toggleNickEditor, toggleNickEditor,
toggleNotificationMarkers, toggleNotificationMarkers,
requestIdleCallback, requestIdleCallback,
togglePreviewMoreButtonsIfNeeded,
}; };
function findCurrentNetworkChan(name) { function findCurrentNetworkChan(name) {
@ -141,3 +142,8 @@ function requestIdleCallback(callback, timeout) {
callback(); callback();
} }
} }
// Force handling preview display
function togglePreviewMoreButtonsIfNeeded() {
window.requestAnimationFrame(() => $(window).trigger("resize"));
}

View file

@ -23,10 +23,31 @@
<img src="{{thumb}}" decoding="async" alt="" class="thumb"> <img src="{{thumb}}" decoding="async" alt="" class="thumb">
</a> </a>
{{/if}} {{/if}}
<a class="toggle-text" href="{{link}}" target="_blank" rel="noopener"> <div class="toggle-text">
<div class="head" title="{{head}}">{{head}}</div> <div class="head">
<div class="body" title="{{body}}">{{body}}</div> <div class="overflowable">
</a> <a href="{{link}}" target="_blank" rel="noopener" title="{{head}}">
{{head}}
</a>
</div>
<button class="more"
aria-expanded="false"
aria-label="More"
data-closed-text="More"
data-opened-text="Less"
>
<span class="more-caret"></span>
</button>
</div>
<div class="body overflowable">
<a href="{{link}}" target="_blank" rel="noopener" title="{{body}}">
{{body}}
</a>
</div>
</a>
</div>
{{/equal}} {{/equal}}
{{#equal type "error"}} {{#equal type "error"}}
{{#equal error "image-too-big"}} {{#equal error "image-too-big"}}
@ -38,12 +59,24 @@
</em> </em>
{{/equal}} {{/equal}}
{{#equal error "message"}} {{#equal error "message"}}
<em> <div>
There was an error loading preview for this link. <em>
<a href="{{link}}" target="_blank" rel="noopener">Click here</a> A preview could not be loaded.
to open it in a new window. <a href="{{link}}" target="_blank" rel="noopener">Click here</a>
<small class="prefetch-error">({{message}})</small> to open it in a new window.
</em> </em>
<br>
<pre class="prefetch-error">{{message}}</pre>
</div>
<button class="more"
aria-expanded="false"
aria-label="More"
data-closed-text="More"
data-opened-text="Less"
>
<span class="more-caret"></span>
</button>
{{/equal}} {{/equal}}
{{/equal}} {{/equal}}
</div> </div>