From 87afe61860996cacd9bcce790b29e78633aa63cd Mon Sep 17 00:00:00 2001 From: Brock Whittaker Date: Wed, 21 Sep 2016 17:05:24 -0700 Subject: [PATCH] Add overlay lightbox for displaying inline image previews. This adds an event listener (by way of delegation) to the .message_inline_image elements that pops up the overlay and hides it when the overlay exit is clicked. Fixes #654. --- static/js/click_handlers.js | 19 +++++ static/js/hotkey.js | 7 +- static/js/ui.js | 23 ++++++ static/styles/overlay.css | 116 ++++++++++++++++++++++++++++ static/styles/zulip.css | 6 ++ templates/zerver/image-overlay.html | 17 ++++ templates/zerver/index.html | 1 + tools/lib/find_add_class.py | 1 + zproject/settings.py | 2 + 9 files changed, 191 insertions(+), 1 deletion(-) create mode 100644 static/styles/overlay.css create mode 100644 templates/zerver/image-overlay.html diff --git a/static/js/click_handlers.js b/static/js/click_handlers.js index f2eb49c7cb..85e29d332e 100644 --- a/static/js/click_handlers.js +++ b/static/js/click_handlers.js @@ -368,6 +368,25 @@ $(function () { .text("Bringing you to your latest messages…")); }); + (function () { + $("#main_div").on("click", ".message_inline_image a", function (e) { + var img = e.target, + row = rows.id($(img).closest(".message_row")), + user = current_msg_list.get(row).sender_full_name; + + // prevent the link from opening in a new page. + e.preventDefault(); + // prevent the message compose dialog from happening. + e.stopPropagation(); + + ui.lightbox_photo(img, user); + }); + + $("#overlay .exit").click(function (e) { + ui.exit_lightbox_photo(); + }); + }()); + // MAIN CLICK HANDLER $(document).on('click', function (e) { diff --git a/static/js/hotkey.js b/static/js/hotkey.js index 0c50cd8a86..b8c0f2ce95 100644 --- a/static/js/hotkey.js +++ b/static/js/hotkey.js @@ -246,6 +246,10 @@ function process_hotkey(e) { } } + if (event_name === "escape" && $("#overlay").hasClass("show")) { + ui.exit_lightbox_photo(); + } + // If we're on a button or a link and have pressed enter, let the // browser handle the keypress // @@ -361,7 +365,8 @@ function process_hotkey(e) { $(document).keydown(function (e) { // Restrict to non-alphanumeric keys - if (48 > e.which || 90 < e.which) { + // check if 27 (esc) because it doesn't register under .keypress() + if (48 > e.which || 90 < e.which || e.which === 27) { if (process_hotkey(e)) { e.preventDefault(); } diff --git a/static/js/ui.js b/static/js/ui.js index 6b8ef37d99..7e37f5b708 100644 --- a/static/js/ui.js +++ b/static/js/ui.js @@ -281,6 +281,29 @@ exports.small_avatar_url = function (message) { } }; +exports.lightbox_photo = function (image, user) { + // image should be an Image Object in JavaScript. + var url = $(image).attr("src"), + title = $(image).parent().attr("title"); + + $("#overlay .image-preview") + .css("background-image", "url(" + url + ")"); + + $("#overlay").addClass("show"); + + $(".right-sidebar, .column-middle-inner, .left-sidebar").addClass("visual-blur"); + + $(".title").text(title || "N/A"); + $(".user").text(user); + + $(".image-actions .open, .image-actions .download").attr("href", url); +}; + +exports.exit_lightbox_photo = function (image) { + $("#overlay").removeClass("show"); + $(".right-sidebar, .column-middle-inner, .left-sidebar").removeClass("visual-blur"); +}; + var loading_more_messages_indicator_showing = false; exports.show_loading_more_messages_indicator = function () { if (! loading_more_messages_indicator_showing) { diff --git a/static/styles/overlay.css b/static/styles/overlay.css new file mode 100644 index 0000000000..60aef73634 --- /dev/null +++ b/static/styles/overlay.css @@ -0,0 +1,116 @@ +#overlay { + position: fixed; + top: 0px; + left: 0px; + + width: 100vw; + height: 100vh; + + pointer-events: none; + opacity: 0; + + background-color: rgba(255,255,255,0.4); + box-shadow: 0 0 20px rgba(0,0,0,0.2); + z-index: 101; + + transition: all 0.2s ease; +} + +#overlay.show { + pointer-events: auto; + opacity: 1; +} + +#overlay .image-preview { + position: relative; + width: 100%; + height: calc(100% - 80px); + + background-color: #19203a; + box-shadow: 0 0 20px rgba(0,0,0,0.2); + + background-size: contain; + background-repeat: no-repeat; + background-position: center center; +} + +#overlay .image-preview .exit { + position: absolute; + top: 20px; + right: 20px; + + color: rgba(255,255,255,0.8); + font-size: 2rem; + font-weight: 200; + + opacity: 0; + pointer-events: none; + cursor: pointer; + transition: all 0.2s ease; +} + +#overlay .image-preview:hover .exit { + pointer-events: auto; + opacity: 1; +} + +#overlay .image-description, +#overlay .image-actions { + float: left; + margin: 10px 20px; +} + +#overlay .image-actions { + float: right; +} + +#overlay .image-actions .icon { + font-size: 1.1rem; + display: inline-block; + margin-top: 5px; +} + +#overlay .image-actions .icon .text { + font-size: 0.9rem; + margin-right: 5px; +} + +#overlay .image-actions span:nth-of-type(2) { + position: relative; + top: 1px; +} + +#overlay .image-actions .open span:nth-of-type(2) { + top: 2px; +} + +#overlay .image-description { + /* approx width of screen minus action buttons on the side. */ + width: calc(100% - 290px); +} + +#overlay .image-description .title { + font-size: 2rem; + font-weight: 300; + line-height: normal; + max-width: calc(100%); + + /* Required for text-overflow */ + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +#overlay .image-description .user { + font-size: 0.8rem; + font-weight: 600; + color: #888; +} + +.image-actions a.button { + text-decoration: none; +} + +.button .download { + top: 1px; +} diff --git a/static/styles/zulip.css b/static/styles/zulip.css index 331cece35f..171c46f06b 100644 --- a/static/styles/zulip.css +++ b/static/styles/zulip.css @@ -26,6 +26,12 @@ body, transition: background-color 200ms linear; } +.visual-blur { + filter: blur(15px); + -moz-filter: blur(15px); + -webkit-filter: blur(15px); +} + input, button, select, diff --git a/templates/zerver/image-overlay.html b/templates/zerver/image-overlay.html new file mode 100644 index 0000000000..16dbde7f46 --- /dev/null +++ b/templates/zerver/image-overlay.html @@ -0,0 +1,17 @@ +
+
+
x
+
+
+
+
+
+ +
diff --git a/templates/zerver/index.html b/templates/zerver/index.html index 2ae4e6b936..83e3ebd6c3 100644 --- a/templates/zerver/index.html +++ b/templates/zerver/index.html @@ -119,6 +119,7 @@ var page_params = {{ page_params }};
{% include "zerver/right-sidebar.html" %}
+ {% include "zerver/image-overlay.html" %} {% include "zerver/keyboard_shortcuts.html" %} {% include "zerver/search_operators.html" %} diff --git a/tools/lib/find_add_class.py b/tools/lib/find_add_class.py index 7f2b0c6140..956b73b93b 100644 --- a/tools/lib/find_add_class.py +++ b/tools/lib/find_add_class.py @@ -15,6 +15,7 @@ GENERIC_KEYWORDS = [ 'error', 'expanded', 'hide', + 'show', 'notdisplayed', 'popover', 'success', diff --git a/zproject/settings.py b/zproject/settings.py index cbcacfe868..cdde7af269 100644 --- a/zproject/settings.py +++ b/zproject/settings.py @@ -650,6 +650,7 @@ PIPELINE = { 'styles/zulip.css', 'styles/media.css', 'styles/settings.css', + 'styles/overlay.css', 'styles/pygments.css', 'styles/thirdparty-fonts.css', # We don't want fonts.css on QtWebKit, so its omitted here @@ -665,6 +666,7 @@ PIPELINE = { 'styles/zulip.css', 'styles/media.css', 'styles/settings.css', + 'styles/overlay.css', 'styles/pygments.css', 'styles/thirdparty-fonts.css', 'styles/fonts.css',