navbar: Improve structure & styling for top navbar.

This updates the logged-in top navbar to display the stream/message
name, number of users, and description. It also replaces the search
bar with a search icon that expands into a full-width search bar.

Co-authored-by: Max Nussenbaum <max@maxnuss.com>

Fixes: #164.
Fixes: #5198.
This commit is contained in:
YashRE42 2020-02-03 21:31:11 +05:30 committed by Tim Abbott
parent 5fdb8989e5
commit eb4a2b9d4e
11 changed files with 379 additions and 318 deletions

View File

@ -251,9 +251,9 @@ expect_stream_subject();
casper.then(check_narrow_title('frontend test - Zulip Dev - Zulip')); casper.then(check_narrow_title('frontend test - Zulip Dev - Zulip'));
casper.then(function () { casper.then(function () {
// This time, un-narrow by hitting the search 'x' // Un-narrow by clicking "Zulip"
casper.test.info('Un-narrowing'); casper.test.info('Un-narrowing');
casper.click('#search_exit'); casper.click('.brand');
}); });
expect_home(); expect_home();

View File

@ -5,6 +5,7 @@ zrequire('search');
zrequire('search_pill'); zrequire('search_pill');
zrequire('Filter', 'js/filter'); zrequire('Filter', 'js/filter');
zrequire('search_pill_widget'); zrequire('search_pill_widget');
zrequire('tab_bar');
const noop = () => {}; const noop = () => {};
const return_true = () => true; const return_true = () => true;
@ -38,13 +39,6 @@ run_test('update_button_visibility', () => {
const search_query = $('#search_query'); const search_query = $('#search_query');
const search_button = $('.search_button'); const search_button = $('.search_button');
search_query.is = return_false;
search_query.val('');
narrow_state.active = return_false;
search_button.prop('disabled', false);
search.update_button_visibility();
assert(search_button.prop('disabled'));
search_query.is = return_true; search_query.is = return_true;
search_query.val(''); search_query.val('');
narrow_state.active = return_false; narrow_state.active = return_false;
@ -237,7 +231,6 @@ run_test('initizalize', () => {
search_query_box.is = return_true; search_query_box.is = return_true;
func(ev); func(ev);
assert(is_blurred); assert(is_blurred);
assert(search_button.prop('disabled'));
operators = [{ operators = [{
negated: false, negated: false,
@ -268,7 +261,7 @@ run_test('initizalize', () => {
search_query_box.val("test string"); search_query_box.val("test string");
narrow_state.search_string = () => 'ver'; narrow_state.search_string = () => 'ver';
callback(); callback();
assert.equal(search_query_box.val(), 'ver'); assert.equal(search_query_box.val(), 'test string');
} }
}; };
@ -284,17 +277,7 @@ run_test('initizalize', () => {
} }
}; };
let is_deactivated;
narrow.deactivate = () => {
is_deactivated = true;
};
search.initialize(); search.initialize();
const search_exit_callback = $('#search_exit').get_on_handler('click');
search_exit_callback();
assert(is_deactivated);
}); });
run_test('initiate_search', () => { run_test('initiate_search', () => {

View File

@ -2,6 +2,7 @@ set_global('page_params', {
search_pills_enabled: false, search_pills_enabled: false,
}); });
zrequire('search'); zrequire('search');
zrequire('tab_bar');
const noop = () => {}; const noop = () => {};
const return_true = () => true; const return_true = () => true;
@ -26,8 +27,6 @@ run_test('update_button_visibility', () => {
search_query.val(''); search_query.val('');
narrow_state.active = return_false; narrow_state.active = return_false;
search_button.prop('disabled', false); search_button.prop('disabled', false);
search.update_button_visibility();
assert(search_button.prop('disabled'));
search_query.is = return_true; search_query.is = return_true;
search_query.val(''); search_query.val('');
@ -213,7 +212,6 @@ run_test('initialize', () => {
search_query_box.is = return_true; search_query_box.is = return_true;
func(ev); func(ev);
assert(is_blurred); assert(is_blurred);
assert(search_button.prop('disabled'));
_setup('ver'); _setup('ver');
search.is_using_input_method = true; search.is_using_input_method = true;
@ -239,7 +237,7 @@ run_test('initialize', () => {
search_query_box.val("test string"); search_query_box.val("test string");
narrow_state.search_string = () => 'ver'; narrow_state.search_string = () => 'ver';
callback(); callback();
assert.equal(search_query_box.val(), 'ver'); assert.equal(search_query_box.val(), 'test string');
} }
}; };

View File

@ -231,9 +231,14 @@ exports.process_escape_key = function (e) {
return true; return true;
} }
if (page_params.search_pills_enabled && $('#searchbox').has(':focus')) { if ($('#searchbox').has(':focus')) {
$('#searchbox .pill').blur(); $("input:focus,textarea:focus").blur();
$('#searchbox #search_query').blur(); if (page_params.search_pills_enabled) {
$('#searchbox .pill').blur();
$('#searchbox #search_query').blur();
} else {
tab_bar.exit_search();
}
return true; return true;
} }

View File

@ -45,8 +45,6 @@ function update_buttons_with_focus(focused) {
|| search_query_box.val() || search_query_box.val()
|| narrow_state.active()) { || narrow_state.active()) {
$('.search_button').prop('disabled', false); $('.search_button').prop('disabled', false);
} else {
$('.search_button').prop('disabled', true);
} }
} }
@ -156,7 +154,6 @@ exports.initialize = function () {
// Some of these functions don't actually need to be exported, // Some of these functions don't actually need to be exported,
// but the code was moved here from elsewhere, and it would be // but the code was moved here from elsewhere, and it would be
// more work to re-order everything and make them private. // more work to re-order everything and make them private.
$('#search_exit').on('click', narrow.deactivate);
search_query_box.on('focus', exports.focus_search); search_query_box.on('focus', exports.focus_search);
search_query_box.on('blur', function () { search_query_box.on('blur', function () {
@ -176,8 +173,6 @@ exports.initialize = function () {
// really it would be OK if they did). // really it would be OK if they did).
setTimeout(function () { setTimeout(function () {
const search_string = narrow_state.search_string();
search_query_box.val(search_string);
exports.update_button_visibility(); exports.update_button_visibility();
}, 100); }, 100);
}); });
@ -204,6 +199,7 @@ exports.initiate_search = function () {
if (page_params.search_pills_enabled) { if (page_params.search_pills_enabled) {
$('#search_query').focus(); $('#search_query').focus();
} else { } else {
tab_bar.open_search_bar_and_close_narrow_description();
$('#search_query').select(); $('#search_query').select();
} }
}; };

View File

@ -140,6 +140,12 @@ exports.update_stream_name = function (sub, new_name) {
if (compose_state.stream_name() === old_name) { if (compose_state.stream_name() === old_name) {
compose_state.stream_name(new_name); compose_state.stream_name(new_name);
} }
// Update navbar stream name if needed
const filter = narrow_state.filter();
if (filter && filter.operands("stream")[0] === old_name) {
tab_bar.update_stream_name(new_name);
}
}; };
exports.update_stream_description = function (sub, description, rendered_description) { exports.update_stream_description = function (sub, description, rendered_description) {
@ -152,6 +158,12 @@ exports.update_stream_description = function (sub, description, rendered_descrip
// Update stream settings // Update stream settings
stream_edit.update_stream_description(sub); stream_edit.update_stream_description(sub);
// Update navbar stream description if needed
const filter = narrow_state.filter();
if (filter && filter.operands("stream")[0] === sub.name) {
tab_bar.update_stream_description(sub.rendered_description);
}
}; };
exports.update_stream_privacy = function (sub, values) { exports.update_stream_privacy = function (sub, values) {

View File

@ -1,172 +1,138 @@
const render_tab_bar = require('../templates/tab_bar.hbs'); const render_tab_bar = require('../templates/tab_bar.hbs');
function make_tab(title, hash, data, extra_class, home) { function get_sub_count(current_stream) {
return {active: "inactive", const sub_count = current_stream.subscriber_count;
cls: extra_class || "", return sub_count;
title: title,
hash: hash,
data: data,
home: home || false };
} }
function make_tab_data() { function get_formatted_sub_count(current_stream) {
const tabs = []; let sub_count = get_sub_count(current_stream);
const filter = narrow_state.filter(); if (sub_count >= 1000) {
// parseInt() is used to floor the value of division to an integer
function filtered_to_non_home_view_stream() { sub_count = parseInt(sub_count / 1000, 10) + "k";
if (!filter.has_operator('stream')) {
return false;
}
const stream_name = filter.operands('stream')[0];
const stream_id = stream_data.get_stream_id(stream_name);
if (!stream_id) {
return true;
}
return stream_data.is_muted(stream_id);
} }
return sub_count;
}
function in_all() { function make_tab_data(filter) {
return filter !== undefined && const tab_data = {};
(filtered_to_non_home_view_stream() || if (filter === undefined) {
filter.has_operand("in", "all")); return {
title: 'All messages',
icon: 'home',
};
} }
tab_data.title = filter.get_title();
if (in_all()) { tab_data.icon = filter.get_icon();
tabs.push(make_tab("All Messages", "#narrow/in/all", undefined, "root")); if (tab_data.icon === 'hashtag' || tab_data.icon === 'lock') {
} else if (page_params.narrow !== undefined) { const stream = filter.operands("stream")[0];
tabs.push(make_tab("Stream " + page_params.narrow_stream, const current_stream = stream_data.get_sub_by_name(stream);
hash_util.operators_to_hash([page_params.narrow[0]]), if (current_stream) {
page_params.narrow_stream, 'stream')); tab_data.rendered_narrow_description = current_stream.rendered_description;
if (page_params.narrow_topic !== undefined) { tab_data.sub_count = get_sub_count(current_stream);
tabs.push(make_tab("Topic " + page_params.narrow_topic, tab_data.formatted_sub_count = get_formatted_sub_count(current_stream);
hash_util.operators_to_hash(page_params.narrow), tab_data.stream_settings_link = "#streams/" + current_stream.stream_id + "/" + current_stream.name;
null)); } else {
tab_data.title = 'Unknown Stream';
tab_data.sub_count = '0';
tab_data.formatted_sub_count = '0';
tab_data.rendered_narrow_description = "This stream does not exist or is private.";
} }
} }
return tab_data;
if (narrow_state.active() && narrow_state.operators().length > 0) {
let stream;
const ops = narrow_state.operators();
// Second breadcrumb item
let hashed = hash_util.operators_to_hash(ops.slice(0, 1));
if (filter.has_operator("stream")) {
stream = filter.operands("stream")[0];
tabs.push(make_tab(stream, hashed, stream, 'stream'));
} else if (filter.has_operator("pm-with") ||
filter.has_operand("is", "private")) {
tabs.push(make_tab("Private Messages", '#narrow/is/private',
undefined, 'private_message '));
if (filter.has_operator("pm-with")) {
const emails = filter.operands("pm-with")[0].split(',');
const names = emails.map(email => {
if (!people.get_by_email(email)) {
return email;
}
return people.get_by_email(email).full_name;
});
tabs.push(make_tab(names.join(', '), hashed));
}
} else if (filter.has_operator("group-pm-with")) {
tabs.push(make_tab("Group Private", '#narrow/group-pm-with',
undefined, 'private_message '));
} else if (filter.has_operand("is", "starred")) {
tabs.push(make_tab("Starred", hashed));
} else if (filter.has_operand("streams", "public")) {
tabs.push(make_tab("Public Streams", hashed));
} else if (filter.has_operator("near")) {
tabs.push(make_tab("Near " + filter.operands("near")[0], hashed));
} else if (filter.has_operator("id")) {
tabs.push(make_tab("ID " + filter.operands("id")[0], hashed));
} else if (filter.has_operand("is", "mentioned")) {
tabs.push(make_tab("Mentions", hashed));
} else if (filter.has_operator("sender")) {
let sender = filter.operands("sender")[0];
if (people.get_by_email(sender)) {
sender = people.get_by_email(sender).full_name;
}
tabs.push(make_tab("Sent by " + sender, hashed));
} else if (filter.has_operator("search")) {
// Search is not a clickable link, since we don't have
// a search narrow
tabs.push(make_tab("Search results", false));
}
// Third breadcrumb item for stream-topic naarrows
if (filter.has_operator("stream") &&
filter.has_operator("topic")) {
const topic = filter.operands("topic")[0];
hashed = hash_util.operators_to_hash(ops.slice(0, 2));
tabs.push(make_tab(topic, hashed, null));
}
}
if (tabs.length === 0) {
tabs.push(make_tab('All messages', "#", "home", "root", true));
}
// Last tab is not a link
tabs[tabs.length - 1].hash = null;
return tabs;
} }
exports.colorize_tab_bar = function () { exports.colorize_tab_bar = function () {
const stream_tab = $('#tab_list .stream'); const filter = narrow_state.filter();
if (stream_tab.length > 0) { if (filter === undefined || !filter.has_operator('stream')) {return;}
let stream_name = stream_tab.data('name'); const color_for_stream = stream_data.get_color(filter.operands("stream")[0]);
if (stream_name === undefined) { const stream_light = colorspace.getHexColor(colorspace.getDecimalColor(color_for_stream));
return; $("#tab_list .fa-hashtag").css('color', stream_light);
} $("#tab_list .fa-lock").css('color', stream_light);
stream_name = stream_name.toString();
const color_for_stream = stream_data.get_color(stream_name);
const stream_dark = stream_color.get_color_class(color_for_stream);
const stream_light = colorspace.getHexColor(
colorspace.getLighterColor(
colorspace.getDecimalColor(color_for_stream), 0.2));
if (stream_tab.hasClass("stream")) {
stream_tab.css('background-color', color_for_stream);
if (stream_tab.hasClass("inactive")) {
stream_tab.hover (
function () {
$(this).css('background-color', stream_light);
}, function () {
$(this).css('background-color', color_for_stream);
}
);
}
stream_tab.removeClass(stream_color.color_classes);
stream_tab.addClass(stream_dark);
}
}
}; };
function build_tab_bar() { function display_tab_bar(tab_bar_data) {
const tabs = make_tab_data();
const tab_bar = $("#tab_bar"); const tab_bar = $("#tab_bar");
tab_bar.empty(); tab_bar.empty();
const rendered = render_tab_bar(tab_bar_data);
tabs[tabs.length - 1].active = "active";
const rendered = render_tab_bar({tabs: tabs});
tab_bar.append(rendered); tab_bar.append(rendered);
exports.colorize_tab_bar(); if (tab_bar_data.stream_settings_link) {
exports.colorize_tab_bar();
}
tab_bar.removeClass('notdisplayed'); tab_bar.removeClass('notdisplayed');
} }
function build_tab_bar(filter) {
// This makes sure we don't waste time appending tab_bar on a template where it's never used
if (filter && !filter.is_common_narrow()) {
exports.open_search_bar_and_close_narrow_description();
} else {
const tab_bar_data = make_tab_data(filter);
display_tab_bar(tab_bar_data);
$(".search_closed").on("click", function (e) {
exports.open_search_bar_and_close_narrow_description();
$('#search_query').select();
e.preventDefault();
e.stopPropagation();
});
exports.close_search_bar_and_open_narrow_description();
}
}
exports.exit_search = function () {
const filter = narrow_state.filter();
if (!filter || filter.is_common_narrow()) {
// for common narrows, we change the UI (and don't redirect)
exports.close_search_bar_and_open_narrow_description();
} else {
// for "searching narrows", we redirect
window.location.replace(filter.generate_redirect_url());
}
};
exports.update_stream_name = function (new_name) {
const stream_name = $(".stream a");
if (stream_name !== undefined) {
stream_name.text(new_name);
}
};
exports.update_stream_description = function () {
// TODO: Implement this properly. Really, this and update_stream
// name should just do a full rerender of the tab_tab component;
// they're rare events and that rendering is cheap.
// TODO: Do similar rerenders for stream privacy or subscriber
// count changes.
return;
};
exports.initialize = function () { exports.initialize = function () {
build_tab_bar(); const filter = narrow_state.filter();
build_tab_bar(filter);
// register navbar click handlers
$('#search_exit').on("click", function (e) {
tab_bar.exit_search();
e.preventDefault();
e.stopPropagation();
});
$(".search_open").on("click", function (e) {
$('#search_query').typeahead('lookup').focus();
e.preventDefault();
e.stopPropagation();
});
};
exports.open_search_bar_and_close_narrow_description = function () {
$(".navbar-search").addClass("expanded");
$("#tab_list").addClass("hidden");
};
exports.close_search_bar_and_open_narrow_description = function () {
$(".navbar-search").removeClass("expanded");
$("#tab_list").removeClass("hidden");
}; };
window.tab_bar = exports; window.tab_bar = exports;

View File

@ -83,9 +83,8 @@ body.night-mode {
/* do not turn the .message_header .stream_label text dark on hover because they're /* do not turn the .message_header .stream_label text dark on hover because they're
on a dark background, and don't change the dark labels dark either. */ on a dark background, and don't change the dark labels dark either. */
.message_header:not(.dark_background) a.stream_label:not(.dark_background):hover, .message_header:not(.dark_background) a.stream_label:not(.dark_background):hover {
#tab_list li.stream:not(.dark_background) { color: hsl(212, 28%, 18%);
color: hsl(212, 28%, 18%) !important;
} }
/* these are converting grey things to "new grey" */ /* these are converting grey things to "new grey" */
@ -204,8 +203,7 @@ on a dark background, and don't change the dark labels dark either. */
} }
.message-header-contents, .message-header-contents,
.message_header_private_message .message-header-contents, .message_header_private_message .message-header-contents {
#tab_bar #tab_list li.active {
background-color: hsla(0, 0%, 0%, 0.2); background-color: hsla(0, 0%, 0%, 0.2);
} }
@ -226,6 +224,19 @@ on a dark background, and don't change the dark labels dark either. */
.top-navbar-border { .top-navbar-border {
border-color: hsla(0, 0%, 0%, 0.6); border-color: hsla(0, 0%, 0%, 0.6);
} }
#tab_bar #tab_list li.sub_count::before {
background: hsla(0, 0%, 100%, 0.5);
}
#tab_bar #tab_list li.sub_count::after {
background: hsla(0, 0%, 100%, 0.5);
}
#searchbox_legacy {
#tab_bar #tab_list li.sub_count,
#tab_bar #tab_list li.narrow_description {
color: hsla(0, 0%, 90%, 1);
}
}
.overlay, .overlay,
#subscription_overlay #stream-creation #stream_creation_form #stream_creating_indicator:not(:empty), #subscription_overlay #stream-creation #stream_creation_form #stream_creating_indicator:not(:empty),
@ -354,8 +365,6 @@ on a dark background, and don't change the dark labels dark either. */
} }
thead, thead,
#searchbox,
#searchbox_legacy,
.drafts-container .drafts-header, .drafts-container .drafts-header,
.nav > li > a:hover, .nav > li > a:hover,
.subscriptions-container .subscriptions-header, .subscriptions-container .subscriptions-header,

View File

@ -1372,6 +1372,7 @@ div.focused_table {
} }
#tab_bar { #tab_bar {
width: 100%;
z-index: 2; z-index: 2;
padding-top: 0px; padding-top: 0px;
overflow: hidden; overflow: hidden;
@ -1379,97 +1380,161 @@ div.focused_table {
float: left; float: left;
letter-spacing: normal; letter-spacing: normal;
height: 40px; height: 40px;
#tab_list { #tab_list {
list-style: none; display: flex;
align-content: flex-start;
flex-wrap: nowrap;
margin: 0px 0px 0px 0px; margin: 0px 0px 0px 0px;
height: 40px; height: 40px;
line-height: 40px; line-height: 40px;
font-size: 16px; font-size: 16px;
border: none; border: none;
white-space: nowrap; white-space: nowrap;
width: calc(100% - 1px);
li { cursor: default;
.hidden {
display: none;
}
span {
white-space: nowrap; white-space: nowrap;
list-style-type: none; list-style-type: none;
display: inline-block; display: inline-block;
vertical-align: top;
position: relative; position: relative;
font-weight: 300; font-weight: 600;
background-color: hsl(0, 0%, 98%); font-size: 16px;
margin: 0px; line-height: 16px;
padding: 0px; margin: 0 -4px 0 0;
text-overflow: ellipsis; padding: 12px 6px;
padding: 0 5px;
}
li.private_message a { @media (max-width: 500px) {
color: hsl(0, 0%, 100%); padding: 7px 3.5px; // based on having ~41.66% decrease
} }
&:not(.search_closed):not(.sub_count):not(.narrow_description) {
a { flex-shrink: 1;
text-decoration: none; overflow: hidden;
color: inherit; }
border-color: inherit; &:not(.stream) {
width: 100%; text-overflow: ellipsis;
display: inline-block; }
padding: 0px 5px; i {
max-width: 150px; margin-right: 3px;
white-space: nowrap; }
overflow: hidden; .fa {
text-overflow: ellipsis; margin: 0px 3px 0px 5px;
} .fa-envelope {
font-size: 14px;
li.active { margin: 0px 5px 0px 5px;
background-color: hsl(0, 0%, 88%); }
max-width: 150px; .fa-hashtag {
white-space: nowrap; font-size: 1.2rem;
overflow: hidden; // font-weight: 800;
text-overflow: ellipsis; margin: 0px 2px 0px 5px;
}
li.private_message {
border-top-color: hsla(0, 0%, 0%, 0.0);
border-right-color: hsla(0, 0%, 0%, 0.0);
border-bottom-color: hsla(0, 0%, 0%, 0.0);
background-color: hsl(0, 0%, 27%);
border-left-color: hsl(0, 0%, 27%);
color: hsl(0, 0%, 100%);
border-width: 0px;
}
.root {
border-color: hsl(0, 0%, 88%);
background-color: hsl(0, 0%, 88%);
margin: 0px;
a {
color: hsl(0, 0%, 52%);
padding-right: 2px;
&:hover {
color: hsl(0, 0%, 0%);
} }
} }
} }
li.inactive { .stream {
border-width: 0px; text-overflow: clip;
margin-right: -4px; overflow: hidden;
font-size: 14px; a {
&::before { color: inherit;
left: 100%; text-decoration: none;
top: 50%; padding-right: 2px;
content: " ";
height: 0px;
width: 0px;
position: absolute;
pointer-events: none;
z-index: 1;
transform: scale(.9999);
} }
} }
li.active.root { .sub_count,
padding: 0px 10px; .narrow_description {
background: none;
font-size: 14px;
color: hsl(0, 0%, 40%);
font-weight: 400;
line-height: 20px;
} }
.sub_count {
margin-left: 10px;
margin-right: 10px;
.fa.fa-user-o {
margin-left: 0px;
}
&::before {
content: "";
position: absolute;
left: -5px;
top: 25%;
width: 1px;
height: 50%;
background: hsl(0, 0%, 88%);
@media (max-width: 500px) {
top: 10%;
}
}
&::after {
content: "";
position: absolute;
right: -5px;
top: 25%;
width: 1px;
height: 50%;
background: hsl(0, 0%, 88%);
@media (max-width: 500px) {
top: 10%;
}
}
}
.narrow_description {
// the actual value of flex shrink does not matter, it is the
// ratio of this value to other flex items that determines the
// behavior while shrinking, here the other item has the .stream
// class and a flex of 1, so the value here *is* the ratio, and
// is chosen such that the narrow description shrinks to close
// before the stream name must begin to shrink
flex-shrink: 100;
overflow: hidden;
white-space: nowrap;
margin: 0;
.emoji {
margin: 0;
padding: 0;
}
padding: 12px 0px;
padding-left: 2px;
@media (max-width: 500px) {
padding: 7px 0;
padding-left: 2px;
}
}
.search_closed {
flex: 0; // makes sure search icon is always visible
margin-left: auto; // aligns search icon to right end of box
margin-right: 40px; // 27 = 15 (gets icon inside border) + 25 (margin against border)
cursor: pointer;
font-size: 20px;
padding: 12px 0px 0px 0px;
@media (max-width: 500px) {
padding: 5px 0px 0px 0px;
}
}
}
}
.search_icon {
color: hsl(0, 0%, 80%);
text-decoration: none;
width: 0;
height: 0;
padding-top: 12px;
padding-left: 50px;
&:hover {
color: hsl(0, 0%, 0%);
text-decoration: none;
} }
} }
@ -1580,12 +1645,20 @@ div.focused_table {
width: 100%; width: 100%;
height: 40px; height: 40px;
.navbar-search { .navbar-search:not(.expanded) {
margin-top: 0px; display: none;
width: auto; }
float: none;
.navbar-search.expanded {
overflow: hidden; overflow: hidden;
height: 40px; margin-top: 0px;
right: 2;
width: calc(100% - 2px);
position: absolute;
.search_button {
display: inline;
margin-right: 16px;
}
} }
.input-append { .input-append {
@ -1600,6 +1673,10 @@ div.focused_table {
top: 10px; top: 10px;
z-index: 5; z-index: 5;
} }
.fa-search:not(.deactivated) {
cursor: pointer;
}
} }
#search_query { #search_query {
@ -1608,7 +1685,7 @@ div.focused_table {
height: 40px; height: 40px;
padding: 0px; padding: 0px;
padding-left: 35px; padding-left: 35px;
padding-right: 20px; padding-right: 40px;
border: none; border: none;
border-radius: 0px; border-radius: 0px;
font-family: 'Source Sans Pro'; font-family: 'Source Sans Pro';
@ -1646,19 +1723,6 @@ div.focused_table {
visibility: hidden; visibility: hidden;
} }
.search_icon {
color: hsl(0, 0%, 80%);
text-decoration: none;
display: block;
width: 1px;
height: 1px;
}
.search_icon:hover {
color: hsl(0, 0%, 0%);
text-decoration: none;
}
#search_arrows { #search_arrows {
/* Bootstrap wants font-size: 0 to eliminate space between /* Bootstrap wants font-size: 0 to eliminate space between
the buttons. We need to inherit the font size, so we the buttons. We need to inherit the font size, so we
@ -1701,7 +1765,7 @@ div.focused_table {
left: 0px; left: 0px;
text-align: center; text-align: center;
vertical-align: middle; vertical-align: middle;
border-right: 2px solid hsl(204, 20%, 74%); // border-right: 2px solid hsl(204, 20%, 74%);
} }
#streamlist-toggle-button { #streamlist-toggle-button {
@ -2515,17 +2579,10 @@ div.topic_edit_spinner .loading_indicator_spinner {
width: 30px; width: 30px;
} }
#top_navbar.rightside-userlist .navbar-search {
margin-right: 100px;
}
#top_navbar.rightside-userlist #navbar-buttons { #top_navbar.rightside-userlist #navbar-buttons {
margin-right: 41px; margin-right: 41px;
} }
.navbar-search {
margin-right: 60px;
}
.nav .dropdown-menu { .nav .dropdown-menu {
min-width: 180px; min-width: 180px;
@ -2539,6 +2596,25 @@ div.topic_edit_spinner .loading_indicator_spinner {
.column-middle { .column-middle {
margin-right: 7px; margin-right: 7px;
} }
.top-navbar-container,
#searchbox_legacy .navbar-search.expanded {
width: calc(100% - 91px);
}
.search_closed .fa-search {
right: 115px;
}
#top_navbar:not(.rightside-userlist) {
.search_closed .fa-search {
right: 72px;
}
.top-navbar-border,
#searchbox_legacy .navbar-search.expanded {
width: calc(100% - 50px);
}
}
} }
@media (max-width: 775px) { @media (max-width: 775px) {
@ -2604,17 +2680,34 @@ div.topic_edit_spinner .loading_indicator_spinner {
display: block; display: block;
} }
#searchbox, .top-navbar-border {
#searchbox_legacy { margin-left: 40px;
margin-left: 42px;
} }
#top_navbar.rightside-userlist .navbar-search { .top-navbar-border {
margin-right: 127px; width: calc(100% - 116px);
}
// todo: Figure out why this has to be different
// from above at this width and resolve it
// #searchbox_legacy .navbar-search.expanded,
#searchbox_legacy .navbar-search.expanded {
width: calc(100% - 131px);
} }
.navbar-search { .search_closed .fa-search {
margin-right: 81px; right: 115px;
}
#top_navbar:not(.rightside-userlist) {
// .search_closed .fa-search {
// right: 115px;
// }
.top-navbar-border {
width: calc(100% - 75px);
}
#searchbox_legacy .navbar-search.expanded {
width: calc(100% - 90px);
}
} }
} }
@ -2636,6 +2729,10 @@ div.topic_edit_spinner .loading_indicator_spinner {
margin-top: 10px; margin-top: 10px;
} }
.search_closed .fa-search {
top: 5px;
}
#streamlist-toggle, #streamlist-toggle,
#navbar-buttons, #navbar-buttons,
.navbar-search, .navbar-search,
@ -2665,10 +2762,6 @@ div.topic_edit_spinner .loading_indicator_spinner {
top: -5px; top: -5px;
} }
#top_navbar.rightside-userlist .navbar-search {
margin-right: 115px;
}
#searchbox, #searchbox,
#searchbox_legacy { #searchbox_legacy {
.input-append .fa-search { .input-append .fa-search {
@ -2677,7 +2770,7 @@ div.topic_edit_spinner .loading_indicator_spinner {
.search_button, .search_button,
.search_button[disabled]:hover { .search_button[disabled]:hover {
top: 2px; top: 0px;
} }
} }

View File

@ -1,19 +1,19 @@
<ul id="tab_list"> <span id="tab_list">
{{#each tabs}} <span {{#if stream_settings_link}}class="stream"{{/if}} {{#if data}}data-name="{{data}}"{{/if}}>
<li class="{{active}} {{cls}}" {{#if data}}data-name="{{data}}"{{/if}}>
{{#if icon}} {{#if icon}}
<i class="fa fa-angle-right" aria-hidden="true"></i> <i class="fa fa-{{icon}}" aria-hidden="true"></i>
{{/if}} {{/if}}
{{! Most tabs are links, but some are not since we don't have a narrow for them (e.g. Search) }} {{#if stream_settings_link}}
{{#if hash}} <a href="{{stream_settings_link}}">{{title}}</a>
<a href="{{hash}}">{{title}}</a>
{{else}} {{else}}
{{#if home}} {{title}}
<i class="fa fa-home" aria-hidden="true"></i>
{{else}}
{{title}}
{{/if}}
{{/if}} {{/if}}
</li> </span>
{{/each}} {{#if sub_count}}
</ul> <span class="sub_count" data-toggle="tooltip" title="{{sub_count}} users are subscribed to #{{title}}"><i class="fa fa-user-o"></i>{{formatted_sub_count}}</span>
{{/if}}
{{#if rendered_narrow_description}}
<span class="narrow_description rendered_markdown" data-toggle="tooltip" title="{{rendered_narrow_description}}">{{rendered_markdown rendered_narrow_description}}</span>
{{/if}}
<span class="search_icon search_closed" ><i class="fa fa-search" aria-hidden="true"></i></span>
</span>

View File

@ -12,12 +12,12 @@
<span id="streamlist-toggle-unreadcount">0</span> <span id="streamlist-toggle-unreadcount">0</span>
</a> </a>
</div> </div>
<div class="top-navbar-border"> <div class="top-navbar-border top-navbar-container">
{% if search_pills_enabled %} {% if search_pills_enabled %}
<div id="searchbox"> <div id="searchbox">
<div id="tab_bar" class="notdisplayed"> <div id="tab_bar" class="notdisplayed">
</div> </div>
<a class="search_icon" href="#search-operators" data-overlay-trigger="search-operators" title="{{ _('Search help') }}"><i class="fa fa-search" aria-hidden="true"></i></a> <span class="search_icon" ><i class="fa fa-search" aria-hidden="true"></i></span>
<form id="searchbox_form" class="form-search navbar-search"> <form id="searchbox_form" class="form-search navbar-search">
<div id="search_arrows" class="pill-container input-append"> <div id="search_arrows" class="pill-container input-append">
<div contenteditable="true" class="input search-query input-block-level" id="search_query" type="text" placeholder="{{ _('Search') }}" <div contenteditable="true" class="input search-query input-block-level" id="search_query" type="text" placeholder="{{ _('Search') }}"
@ -35,9 +35,8 @@
<div id="search_arrows" class="input-append"> <div id="search_arrows" class="input-append">
<input class="search-query input-block-level" id="search_query" type="text" placeholder="{{ _('Search') }}" <input class="search-query input-block-level" id="search_query" type="text" placeholder="{{ _('Search') }}"
autocomplete="off" aria-label="{{ _('Search') }}" title="{{ _('Search') }} (/)"/> autocomplete="off" aria-label="{{ _('Search') }}" title="{{ _('Search') }} (/)"/>
{# Start the button off disabled since there is no active search #} <button class="btn search_button" type="button" id="search_exit" aria-label="{{ _('Exit search') }}"><i class="fa fa-remove" aria-hidden="true"></i></button>
<button class="btn search_button" type="button" id="search_exit" disabled="disabled" aria-label="{{ _('Exit search') }}"><i class="fa fa-remove" aria-hidden="true"></i></button> <span class="search_icon search_open" ><i class="fa fa-search" aria-hidden="true"></i></span>
<a class="search_icon" href="#search-operators" data-overlay-trigger="search-operators" title="{{ _('Search help') }}"><i class="fa fa-search" aria-hidden="true"></i></a>
</div> </div>
</form> </form>
</div> </div>