files: made file filter an active input instead of a button.

This commit is contained in:
Marcin Kuzminski 2019-05-22 12:46:46 +02:00
parent eddc784e19
commit ab5d354f1b
5 changed files with 287 additions and 248 deletions

View file

@ -1951,7 +1951,7 @@ def get_visual_attr(tmpl_context_var, attr_name):
def get_last_path_part(file_node):
if not file_node.path:
return u''
return u'/'
path = safe_unicode(file_node.path.split('/')[-1])
return u'../' + path

View file

@ -2221,25 +2221,41 @@ h3.files_location{
margin: -25px 0px 5px 0px;
}
.node-filter {
font-size: @repo-title-fontsize;
padding: 4px 0px 0px 0px;
.node-filter-path {
float: left;
color: @grey4;
}
.node-filter-input {
float: left;
margin: -2px 0px 0px 2px;
input {
padding: 2px;
border: none;
font-size: @repo-title-fontsize;
}
}
.files-quick-filter {
float: right;
margin: 0 25px;
}
.files-filter-box {
display: flex;
padding: 0px;
border-radius: 3px;
border: 1px solid @grey3;
a {
border: none !important;
}
li {
list-style-type: none
}
}
.files-filter-box-path {
line-height: 28px;
padding: 0 5px;
}
.files-filter-box-input {
background-color: @grey3 !important;
margin-right: 0;
input {
border: none
}
}
.browser-result{
td a{

View file

@ -19,234 +19,254 @@
/**
* Search file list
*/
// global reference to file-node filter
var _NODEFILTER = {};
var fileBrowserListeners = function(node_list_url, url_base){
var n_filter = $('#node_filter').get(0);
var NodeFilter = {};
_NODEFILTER.filterTimeout = null;
var nodes = null;
var fileBrowserListeners = function (node_list_url, url_base) {
var $filterInput = $('#node_filter');
var n_filter = $filterInput.get(0);
_NODEFILTER.fetchNodes = function(callback) {
$.ajax({url: node_list_url, headers: {'X-PARTIAL-XHR': true}})
.done(function(data){
nodes = data.nodes;
if (callback) {
callback();
NodeFilter.filterTimeout = null;
var nodes = null;
NodeFilter.focus = function () {
$filterInput.focus()
};
NodeFilter.fetchNodes = function (callback) {
$.ajax(
{url: node_list_url, headers: {'X-PARTIAL-XHR': true}})
.done(function (data) {
nodes = data.nodes;
if (callback) {
callback();
}
})
.fail(function (data) {
console.log('failed to load');
});
};
NodeFilter.initFilter = function (e) {
if ($filterInput.hasClass('loading')) {
return
}
})
.fail(function(data){
console.log('failed to load');
});
};
_NODEFILTER.fetchNodesCallback = function() {
$('#node_filter_box_loading').hide();
$('#node_filter_box').removeClass('hidden').show();
n_filter.focus();
if ($('#node_filter').hasClass('init')){
n_filter.value = '';
$('#node_filter').removeClass('init');
}
};
_NODEFILTER.initFilter = function(){
$('#node_filter_box_loading').removeClass('hidden').show();
$('#search_activate_id').hide();
$('#search_deactivate_id').removeClass('hidden').show();
$('#add_node_id').hide();
_NODEFILTER.fetchNodes(_NODEFILTER.fetchNodesCallback);
};
_NODEFILTER.resetFilter = function(){
$('#node_filter_box_loading').hide();
$('#node_filter_box').hide();
$('#search_activate_id').show();
$('#search_deactivate_id').hide();
$('#add_node_id').show();
$('#tbody').show();
$('#tbody_filtered').hide();
$('#node_filter').val('');
};
_NODEFILTER.fuzzy_match = function(filepath, query) {
var highlight = [];
var order = 0;
for (var i = 0; i < query.length; i++) {
var match_position = filepath.indexOf(query[i]);
if (match_position !== -1) {
var prev_match_position = highlight[highlight.length-1];
if (prev_match_position === undefined) {
highlight.push(match_position);
} else {
var current_match_position = prev_match_position + match_position + 1;
highlight.push(current_match_position);
order = order + current_match_position - prev_match_position;
// in case we are already loaded, do nothing
if (!$filterInput.hasClass('init')) {
return NodeFilter.handleKey(e);
}
filepath = filepath.substring(match_position+1);
} else {
return false;
}
}
return {'order': order,
'highlight': highlight};
};
_NODEFILTER.sortPredicate = function(a, b) {
if (a.order < b.order) return -1;
if (a.order > b.order) return 1;
if (a.filepath < b.filepath) return -1;
if (a.filepath > b.filepath) return 1;
return 0;
};
var org = $('.files-filter-box-path .tag').html();
$('.files-filter-box-path .tag').html('loading...');
$filterInput.addClass('loading');
var callback = function (org) {
return function () {
if ($filterInput.hasClass('init')) {
$filterInput.removeClass('init');
$filterInput.removeClass('loading');
}
$('.files-filter-box-path .tag').html(org);
// auto re-filter if we filled in the input
if (n_filter.value !== "") {
NodeFilter.updateFilter(n_filter, e)()
}
_NODEFILTER.updateFilter = function(elem, e) {
return function(){
// Reset timeout
_NODEFILTER.filterTimeout = null;
var query = elem.value.toLowerCase();
var match = [];
var matches_max = 20;
if (query !== ""){
var results = [];
for(var k=0;k<nodes.length;k++){
var result = _NODEFILTER.fuzzy_match(
nodes[k].name.toLowerCase(), query);
if (result) {
result.type = nodes[k].type;
result.filepath = nodes[k].name;
results.push(result);
}
}
results = results.sort(_NODEFILTER.sortPredicate);
var limit = matches_max;
if (results.length < matches_max) {
limit = results.length;
}
for (var i=0; i<limit; i++){
if(query && results.length > 0){
var n = results[i].filepath;
var t = results[i].type;
var n_hl = n.split("");
var pos = results[i].highlight;
for (var j = 0; j < pos.length; j++) {
n_hl[pos[j]] = "<em>" + n_hl[pos[j]] + "</em>";
}
n_hl = n_hl.join("");
var new_url = url_base.replace('__FPATH__',n);
var typeObj = {
dir: 'icon-directory browser-dir',
file: 'icon-file-text browser-file'
};
var typeIcon = '<i class="{0}"></i>'.format(typeObj[t]);
match.push('<tr class="browser-result"><td><a class="match-link" href="{0}">{1}{2}</a></td><td colspan="5"></td></tr>'.format(new_url,typeIcon, n_hl));
}
}
if(results.length > limit){
var truncated_count = results.length - matches_max;
if (truncated_count === 1) {
match.push('<tr><td>{0} {1}</td><td colspan="5"></td></tr>'.format(truncated_count, _gettext('truncated result')));
} else {
match.push('<tr><td>{0} {1}</td><td colspan="5"></td></tr>'.format(truncated_count, _gettext('truncated results')));
}
}
}
if (query !== ""){
$('#tbody').hide();
$('#tbody_filtered').show();
if (match.length === 0){
match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_gettext('No matching files')));
}
$('#tbody_filtered').html(match.join(""));
}
else{
$('#tbody').show();
$('#tbody_filtered').hide();
}
};
// load node data
NodeFilter.fetchNodes(callback(org));
};
};
var scrollDown = function(element){
var elementBottom = element.offset().top + $(element).outerHeight();
var windowBottom = window.innerHeight + $(window).scrollTop();
if (elementBottom > windowBottom) {
var offset = elementBottom - window.innerHeight;
$('html,body').scrollTop(offset);
return false;
}
return true;
};
NodeFilter.resetFilter = function () {
$('#tbody').show();
$('#tbody_filtered').hide();
$filterInput.val('');
};
var scrollUp = function(element){
if (element.offset().top < $(window).scrollTop()) {
$('html,body').scrollTop(element.offset().top);
return false;
}
return true;
};
NodeFilter.handleKey = function (e) {
var scrollDown = function (element) {
var elementBottom = element.offset().top + $(element).outerHeight();
var windowBottom = window.innerHeight + $(window).scrollTop();
if (elementBottom > windowBottom) {
var offset = elementBottom - window.innerHeight;
$('html,body').scrollTop(offset);
return false;
}
return true;
};
$('#filter_activate').click(function() {
_NODEFILTER.initFilter();
});
var scrollUp = function (element) {
if (element.offset().top < $(window).scrollTop()) {
$('html,body').scrollTop(element.offset().top);
return false;
}
return true;
};
var $hlElem = $('.browser-highlight');
$('#filter_deactivate').click(function() {
_NODEFILTER.resetFilter();
});
if (e.keyCode === 40) { // Down
if ($hlElem.length === 0) {
$('.browser-result').first().addClass('browser-highlight');
} else {
var next = $hlElem.next();
if (next.length !== 0) {
$hlElem.removeClass('browser-highlight');
next.addClass('browser-highlight');
}
}
$(n_filter).click(function() {
if ($('#node_filter').hasClass('init')){
n_filter.value = '';
$('#node_filter').removeClass('init');
}
});
$(n_filter).keydown(function(e) {
if (e.keyCode === 40){ // Down
if ($('.browser-highlight').length === 0){
$('.browser-result').first().addClass('browser-highlight');
} else {
var next = $('.browser-highlight').next();
if (next.length !== 0) {
$('.browser-highlight').removeClass('browser-highlight');
next.addClass('browser-highlight');
if ($hlElem.get(0) !== undefined){
scrollDown($hlElem);
}
}
}
scrollDown($('.browser-highlight'));
}
if (e.keyCode === 38){ // Up
e.preventDefault();
if ($('.browser-highlight').length !== 0){
var next = $('.browser-highlight').prev();
if (next.length !== 0) {
$('.browser-highlight').removeClass('browser-highlight');
next.addClass('browser-highlight');
if (e.keyCode === 38) { // Up
e.preventDefault();
if ($hlElem.length !== 0) {
var next = $hlElem.prev();
if (next.length !== 0) {
$('.browser-highlight').removeClass('browser-highlight');
next.addClass('browser-highlight');
}
}
if ($hlElem.get(0) !== undefined){
scrollUp($hlElem);
}
}
}
scrollUp($('.browser-highlight'));
}
if (e.keyCode === 13){ // Enter
if ($('.browser-highlight').length !== 0){
var url = $('.browser-highlight').find('.match-link').attr('href');
window.location = url;
}
}
if (e.keyCode === 27){ // Esc
_NODEFILTER.resetFilter();
$('html,body').scrollTop(0);
}
});
var capture_keys = [40, 38, 39, 37, 13, 27];
$(n_filter).keyup(function(e) {
if ($.inArray(e.keyCode, capture_keys) === -1){
clearTimeout(_NODEFILTER.filterTimeout);
_NODEFILTER.filterTimeout = setTimeout(_NODEFILTER.updateFilter(n_filter, e),200);
}
});
if (e.keyCode === 13) { // Enter
if ($('.browser-highlight').length !== 0) {
var url = $('.browser-highlight').find('.match-link').attr('href');
window.location = url;
}
}
if (e.keyCode === 27) { // Esc
NodeFilter.resetFilter();
$('html,body').scrollTop(0);
}
var capture_keys = [
40, // ArrowDown
38, // ArrowUp
39, // ArrowRight
37, // ArrowLeft
13, // Enter
27 // Esc
];
if ($.inArray(e.keyCode, capture_keys) === -1) {
clearTimeout(NodeFilter.filterTimeout);
NodeFilter.filterTimeout = setTimeout(NodeFilter.updateFilter(n_filter, e), 200);
}
};
NodeFilter.fuzzy_match = function (filepath, query) {
var highlight = [];
var order = 0;
for (var i = 0; i < query.length; i++) {
var match_position = filepath.indexOf(query[i]);
if (match_position !== -1) {
var prev_match_position = highlight[highlight.length - 1];
if (prev_match_position === undefined) {
highlight.push(match_position);
} else {
var current_match_position = prev_match_position + match_position + 1;
highlight.push(current_match_position);
order = order + current_match_position - prev_match_position;
}
filepath = filepath.substring(match_position + 1);
} else {
return false;
}
}
return {
'order': order,
'highlight': highlight
};
};
NodeFilter.sortPredicate = function (a, b) {
if (a.order < b.order) return -1;
if (a.order > b.order) return 1;
if (a.filepath < b.filepath) return -1;
if (a.filepath > b.filepath) return 1;
return 0;
};
NodeFilter.updateFilter = function (elem, e) {
return function () {
// Reset timeout
NodeFilter.filterTimeout = null;
var query = elem.value.toLowerCase();
var match = [];
var matches_max = 20;
if (query !== "") {
var results = [];
for (var k = 0; k < nodes.length; k++) {
var result = NodeFilter.fuzzy_match(
nodes[k].name.toLowerCase(), query);
if (result) {
result.type = nodes[k].type;
result.filepath = nodes[k].name;
results.push(result);
}
}
results = results.sort(NodeFilter.sortPredicate);
var limit = matches_max;
if (results.length < matches_max) {
limit = results.length;
}
for (var i = 0; i < limit; i++) {
if (query && results.length > 0) {
var n = results[i].filepath;
var t = results[i].type;
var n_hl = n.split("");
var pos = results[i].highlight;
for (var j = 0; j < pos.length; j++) {
n_hl[pos[j]] = "<em>" + n_hl[pos[j]] + "</em>";
}
n_hl = n_hl.join("");
var new_url = url_base.replace('__FPATH__', n);
var typeObj = {
dir: 'icon-directory browser-dir',
file: 'icon-file-text browser-file'
};
var typeIcon = '<i class="{0}"></i>'.format(typeObj[t]);
match.push('<tr class="browser-result"><td><a class="match-link" href="{0}">{1}{2}</a></td><td colspan="5"></td></tr>'.format(new_url, typeIcon, n_hl));
}
}
if (results.length > limit) {
var truncated_count = results.length - matches_max;
if (truncated_count === 1) {
match.push('<tr><td>{0} {1}</td><td colspan="5"></td></tr>'.format(truncated_count, _gettext('truncated result')));
} else {
match.push('<tr><td>{0} {1}</td><td colspan="5"></td></tr>'.format(truncated_count, _gettext('truncated results')));
}
}
}
if (query !== "") {
$('#tbody').hide();
$('#tbody_filtered').show();
if (match.length === 0) {
match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_gettext('No matching files')));
}
$('#tbody_filtered').html(match.join(""));
} else {
$('#tbody').show();
$('#tbody_filtered').hide();
}
};
};
};
var getIdentNode = function(n){

View file

@ -383,7 +383,8 @@
callbacks();
var search_GET = "${request.GET.get('search','')}";
if (search_GET === "1") {
_NODEFILTER.initFilter();
NodeFilter.initFilter();
NodeFilter.focus();
}
});

View file

@ -3,25 +3,37 @@
<div class="browser-header">
<div class="browser-nav">
<div class="files-quick-filter">
<ul class="files-filter-box">
<li class="files-filter-box-path">
<span class="tag">
${h.get_last_path_part(c.file)}
</span>
</li>
<li class="files-filter-box-input">
<input onkeydown="NodeFilter.initFilter(event)" class="init" type="text" name="filter" size="25" id="node_filter" autocomplete="off">
</li>
</ul>
</div>
<div class="info_box">
<div class="info_box_elem previous">
<a id="prev_commit_link" data-commit-id="${c.prev_commit.raw_id}" class=" ${'disabled' if c.url_prev == '#' else ''}" href="${c.url_prev}" title="${_('Previous commit')}"><i class="icon-left"></i></a>
<a id="prev_commit_link" data-commit-id="${c.prev_commit.raw_id}" class=" ${('disabled' if c.url_prev == '#' else '')}" href="${c.url_prev}" title="${_('Previous commit')}"><i class="icon-left"></i></a>
</div>
${h.hidden('refs_filter')}
<div class="info_box_elem next">
<a id="next_commit_link" data-commit-id="${c.next_commit.raw_id}" class=" ${'disabled' if c.url_next == '#' else ''}" href="${c.url_next}" title="${_('Next commit')}"><i class="icon-right"></i></a>
<a id="next_commit_link" data-commit-id="${c.next_commit.raw_id}" class=" ${('disabled' if c.url_next == '#' else '')}" href="${c.url_next}" title="${_('Next commit')}"><i class="icon-right"></i></a>
</div>
</div>
<div id="search_activate_id" class="search_activate">
<a class="btn btn-default" id="filter_activate" href="javascript:void(0)">${_('Search File List')}</a>
</div>
<div id="search_deactivate_id" class="search_activate hidden">
<a class="btn btn-default" id="filter_deactivate" href="javascript:void(0)">${_('Close File List')}</a>
</div>
% if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
<div title="${_('Add New File')}" class="btn btn-primary new-file">
<a href="${h.route_path('repo_files_add_file',repo_name=c.repo_name,commit_id=c.commit.raw_id,f_path=c.f_path, _anchor='edit')}">
@ -36,19 +48,9 @@
</a>
</div>
% endif
</div>
<div class="browser-search">
<div class="node-filter">
<div class="node_filter_box hidden" id="node_filter_box_loading" >${_('Loading file list...')}</div>
<div class="node_filter_box hidden" id="node_filter_box" >
<div class="node-filter-path">${h.get_last_path_part(c.file)}/</div>
<div class="node-filter-input">
<input class="init" type="text" name="filter" size="25" id="node_filter" autocomplete="off">
</div>
</div>
</div>
</div>
</div>
## file tree is computed from caches, and filled in
<div id="file-tree">