feat: use search bar for tags too
This commit is contained in:
parent
b7196f124c
commit
abb477a1c0
5 changed files with 181 additions and 122 deletions
|
|
@ -4,8 +4,7 @@
|
|||
"doctype": "DocType",
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"description",
|
||||
"count"
|
||||
"description"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
|
|
@ -13,15 +12,9 @@
|
|||
"fieldtype": "Small Text",
|
||||
"in_list_view": 1,
|
||||
"label": "Description"
|
||||
},
|
||||
{
|
||||
"fieldname": "count",
|
||||
"fieldtype": "Int",
|
||||
"label": "Count",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"modified": "2019-09-24 00:53:13.488147",
|
||||
"modified": "2019-09-25 17:47:41.712237",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Desk",
|
||||
"name": "Tag",
|
||||
|
|
|
|||
|
|
@ -2,109 +2,14 @@
|
|||
// MIT License. See license.txt
|
||||
|
||||
frappe.provide("frappe.global_tags");
|
||||
frappe.provide("locals.global_tags");
|
||||
|
||||
frappe.global_tags.GlobalTagsDialog = class GlobalTags {
|
||||
constructor(opts) {
|
||||
$.extend(this, opts);
|
||||
this.show();
|
||||
}
|
||||
|
||||
show() {
|
||||
if (!this.dialog) {
|
||||
this.make_dialog();
|
||||
}
|
||||
|
||||
$(this.dialog.body).html(
|
||||
`<div class="text-muted text-center" style="padding: 30px 0px">
|
||||
${__("Loading")}...
|
||||
</div>`);
|
||||
|
||||
this.dialog.show();
|
||||
}
|
||||
|
||||
make_dialog() {
|
||||
let title = __("Tag {0}", ["#".concat(this.tag)]);
|
||||
|
||||
this.dialog = new frappe.ui.Dialog({
|
||||
hide_on_page_refresh: true,
|
||||
minimizable: true,
|
||||
title: title
|
||||
});
|
||||
|
||||
this.dialog.on_page_show = () => {
|
||||
this.get_documents_for_tag()
|
||||
.then(() => this.make_html());
|
||||
};
|
||||
}
|
||||
|
||||
make_html() {
|
||||
const results = this.results;
|
||||
let html = '';
|
||||
|
||||
const linked_doctypes = Object.keys(results);
|
||||
|
||||
if (linked_doctypes.length === 0) {
|
||||
html = __("Not Linked to any record");
|
||||
} else {
|
||||
html = linked_doctypes.map(doctype => {
|
||||
const docs = results[doctype];
|
||||
return `
|
||||
<div class="list-item-table margin-bottom">
|
||||
${this.make_doc_head(doctype)}
|
||||
${docs.map(doc => this.make_doc_row(doc.dn, doctype, doc.title)).join('')}
|
||||
</div>
|
||||
`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
$(this.dialog.body).html(html);
|
||||
}
|
||||
|
||||
get_documents_for_tag() {
|
||||
return new Promise((resolve) => {
|
||||
frappe.call({
|
||||
method: "frappe.utils.global_tags.get_documents_for_tag",
|
||||
args: {
|
||||
tag: this.tag
|
||||
},
|
||||
callback: (r) => {
|
||||
this.results = r.message;
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
make_doc_head(heading) {
|
||||
return `
|
||||
<header class="level list-row list-row-head text-muted small">
|
||||
<div>${__(heading)}</div>
|
||||
</header>
|
||||
`;
|
||||
}
|
||||
|
||||
make_doc_row(docname, doctype, title) {
|
||||
return `<div class="list-row-container">
|
||||
<div class="level list-row small">
|
||||
<div class="level-left bold">
|
||||
<a href="#Form/${doctype}/${docname}">${docname}</a>
|
||||
</div>
|
||||
<div class="level-left">
|
||||
<p>${title}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
};
|
||||
|
||||
frappe.global_tags.utils = {
|
||||
get_tags: function(txt) {
|
||||
txt = txt.slice(1);
|
||||
let out = [];
|
||||
|
||||
for (let i in locals.global_tags) {
|
||||
let tag = locals.global_tags[i];
|
||||
for (let i in frappe.global_tags.tags) {
|
||||
let tag = frappe.global_tags.tags[i];
|
||||
let level = frappe.search.utils.fuzzy_search(txt, tag);
|
||||
if (level) {
|
||||
out.push({
|
||||
|
|
@ -113,8 +18,9 @@ frappe.global_tags.utils = {
|
|||
value: __("#{0}", [__(tag)]),
|
||||
index: 1 + level,
|
||||
match: tag,
|
||||
onclick: function() {
|
||||
new frappe.global_tags.GlobalTagsDialog({"tag": tag});
|
||||
onclick() {
|
||||
// Use Global Search Dialog for tag search too.
|
||||
frappe.searchdialog.search.init_search("#".concat(tag), "global_tag")
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -123,14 +29,130 @@ frappe.global_tags.utils = {
|
|||
return out;
|
||||
},
|
||||
|
||||
set_tags: function() {
|
||||
set_tags() {
|
||||
frappe.call({
|
||||
method: "frappe.utils.global_tags.get_tags_list_for_awesomebar",
|
||||
callback: function(r) {
|
||||
if (r && r.message) {
|
||||
locals.global_tags = $.extend([], r.message);
|
||||
frappe.global_tags.tags = $.extend([], r.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
get_tag_results: function(tag) {
|
||||
var me = this;
|
||||
function get_results_sets(data) {
|
||||
var results_sets = [], result, set;
|
||||
function get_existing_set(doctype) {
|
||||
return results_sets.find(function(set) {
|
||||
return set.title === doctype;
|
||||
});
|
||||
}
|
||||
|
||||
function make_description(content, doc_name) {
|
||||
var parts = content.split(" ||| ");
|
||||
var result_max_length = 300;
|
||||
var field_length = 120;
|
||||
var fields = [];
|
||||
var result_current_length = 0;
|
||||
var field_text = "";
|
||||
for(var i = 0; i < parts.length; i++) {
|
||||
var part = parts[i];
|
||||
if(part.toLowerCase().indexOf(tag) !== -1) {
|
||||
// If the field contains the keyword
|
||||
if(part.indexOf(' &&& ') !== -1) {
|
||||
var colon_index = part.indexOf(' &&& ');
|
||||
var field_value = part.slice(colon_index + 5);
|
||||
} else {
|
||||
var colon_index = part.indexOf(' : ');
|
||||
var field_value = part.slice(colon_index + 3);
|
||||
}
|
||||
if(field_value.length > field_length) {
|
||||
// If field value exceeds field_length, find the keyword in it
|
||||
// and trim field value by half the field_length at both sides
|
||||
// ellipsify if necessary
|
||||
var field_data = "";
|
||||
var index = field_value.indexOf(tag);
|
||||
field_data += index < field_length/2 ? field_value.slice(0, index)
|
||||
: '...' + field_value.slice(index - field_length/2, index);
|
||||
field_data += field_value.slice(index, index + field_length/2);
|
||||
field_data += index + field_length/2 < field_value.length ? "..." : "";
|
||||
field_value = field_data;
|
||||
}
|
||||
var field_name = part.slice(0, colon_index);
|
||||
|
||||
// Find remaining result_length and add field length to result_current_length
|
||||
var remaining_length = result_max_length - result_current_length;
|
||||
result_current_length += field_name.length + field_value.length + 2;
|
||||
if(result_current_length < result_max_length) {
|
||||
// We have room, push the entire field
|
||||
field_text = '<span class="field-name text-muted">' +
|
||||
me.bolden_match_part(field_name, tag) + ': </span> ' +
|
||||
me.bolden_match_part(field_value, tag);
|
||||
if(fields.indexOf(field_text) === -1 && doc_name !== field_value) {
|
||||
fields.push(field_text);
|
||||
}
|
||||
} else {
|
||||
// Not enough room
|
||||
if(field_name.length < remaining_length){
|
||||
// Ellipsify (trim at word end) and push
|
||||
remaining_length -= field_name.length;
|
||||
field_text = '<span class="field-name text-muted">' +
|
||||
me.bolden_match_part(field_name, tag) + ': </span> ';
|
||||
field_value = field_value.slice(0, remaining_length);
|
||||
field_value = field_value.slice(0, field_value.lastIndexOf(' ')) + ' ...';
|
||||
field_text += me.bolden_match_part(field_value, tag);
|
||||
fields.push(field_text);
|
||||
} else {
|
||||
// No room for even the field name, skip
|
||||
fields.push('...');
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return fields.join(', ');
|
||||
}
|
||||
|
||||
data.forEach(function(d) {
|
||||
// more properties
|
||||
result = {
|
||||
label: d.name,
|
||||
value: d.name,
|
||||
description: make_description(d.content, d.name),
|
||||
route: ['Form', d.doctype, d.name],
|
||||
|
||||
};
|
||||
set = get_existing_set(d.doctype);
|
||||
if(set) {
|
||||
set.results.push(result);
|
||||
} else {
|
||||
set = {
|
||||
title: d.doctype,
|
||||
results: [result],
|
||||
fetch_type: "Global"
|
||||
};
|
||||
results_sets.push(set);
|
||||
}
|
||||
|
||||
});
|
||||
return results_sets;
|
||||
}
|
||||
return new Promise(function(resolve, reject) {
|
||||
frappe.call({
|
||||
method: "frappe.utils.global_tags.get_documents_for_tag",
|
||||
args: {
|
||||
tag: tag
|
||||
},
|
||||
callback: function(r) {
|
||||
if(r.message) {
|
||||
resolve(get_results_sets(r.message));
|
||||
} else {
|
||||
resolve([]);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,6 +124,10 @@ frappe.search.SearchDialog = Class.extend({
|
|||
// Help results
|
||||
// this.$modal_body.on('click', 'a[data-path]', frappe.help.show_results);
|
||||
this.bind_keyboard_events();
|
||||
|
||||
// Setup Minimizable functionality
|
||||
this.search_dialog.minimizable = true;
|
||||
this.search_dialog.$wrapper.find('.btn-modal-minimize').click(() => this.toggle_minimize());
|
||||
},
|
||||
|
||||
bind_keyboard_events: function() {
|
||||
|
|
@ -308,7 +312,7 @@ frappe.search.SearchDialog = Class.extend({
|
|||
frappe.route_options = result.route_options;
|
||||
}
|
||||
$result.on('click', (e) => {
|
||||
this.search_dialog.hide();
|
||||
this.toggle_minimize();
|
||||
if(result.onclick) {
|
||||
result.onclick(result.match);
|
||||
} else {
|
||||
|
|
@ -353,6 +357,19 @@ frappe.search.SearchDialog = Class.extend({
|
|||
this.$modal_body.find('.more-results.last').slideDown(200, function() {});
|
||||
},
|
||||
|
||||
get_minimize_btn: function() {
|
||||
return this.search_dialog.$wrapper.find(".modal-header .btn-modal-minimize");
|
||||
},
|
||||
|
||||
toggle_minimize: function() {
|
||||
let modal = this.search_dialog.$wrapper.closest('.modal').toggleClass('modal-minimize');
|
||||
modal.attr('tabindex') ? modal.removeAttr('tabindex') : modal.attr('tabindex', -1);
|
||||
this.get_minimize_btn().find('i').toggleClass('octicon-chevron-down').toggleClass('octicon-chevron-up');
|
||||
this.search_dialog.is_minimized = !this.search_dialog.is_minimized;
|
||||
this.on_minimize_toggle && this.on_minimize_toggle(this.search_dialog.is_minimized);
|
||||
this.search_dialog.header.find('.modal-title').toggleClass('cursor-pointer');
|
||||
},
|
||||
|
||||
// Search objects
|
||||
searches: {
|
||||
global_search: {
|
||||
|
|
@ -372,6 +389,22 @@ frappe.search.SearchDialog = Class.extend({
|
|||
});
|
||||
}
|
||||
},
|
||||
global_tag: {
|
||||
input_placeholder: __("Global Tags"),
|
||||
empty_state_text: __("Search for Tags"),
|
||||
no_results_status: (keyword) => __("<p>No results found for '" + keyword + "' in Global Tags</p>"),
|
||||
|
||||
get_results: function(keywords, callback) {
|
||||
var results = frappe.search.utils.get_nav_results(keywords);
|
||||
frappe.global_tags.utils.get_tag_results(keywords)
|
||||
.then(function(global_results) {
|
||||
results = results.concat(global_results);
|
||||
callback(results, keywords);
|
||||
}, function (err) {
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
});
|
||||
|
|
@ -1,6 +1,13 @@
|
|||
<div class="search-header">
|
||||
<i class="octicon octicon-search"></i>
|
||||
<input type="text" class="form-control search-input" style="padding-left: 15px">
|
||||
<p class="loading-state hide" style="margin: 0px 20px; color:#d4d9dd">{%= __("Searching")%} ...</p>
|
||||
<a type="button" class="close" data-dismiss="modal" aria-hidden="true">×</a>
|
||||
<i class="octicon octicon-search"></i>
|
||||
<input type="text" class="form-control search-input" style="padding-left: 15px">
|
||||
<p class="loading-state hide" style="margin: 0px 20px; color:#d4d9dd">{%= __("Searching")%} ...</p>
|
||||
<!--a type="button" class="close" data-dismiss="modal" aria-hidden="true">×</a -->
|
||||
<a type="button" class="btn btn-default btn-sm btn-modal-minimize" style="margin-right: 2px;">
|
||||
<i class="octicon octicon-chevron-down" style="padding: 1px 0px;"></i>
|
||||
</a>
|
||||
<a type="button" class="btn btn-default btn-sm btn-modal-close" data-dismiss="modal" aria-hidden="true">
|
||||
<i class="octicon octicon-x visible-xs" style="padding: 1px 0px;"></i>
|
||||
<span class="hidden-xs">Close</span>
|
||||
</a>
|
||||
</div>
|
||||
|
|
@ -48,7 +48,10 @@ def get_documents_for_tag(tag):
|
|||
:param tag: tag to be searched
|
||||
"""
|
||||
# remove hastag `#` from tag
|
||||
results = {}
|
||||
tag = tag[1:]
|
||||
|
||||
results = []
|
||||
|
||||
tag = frappe.db.escape('%{0}%'.format(tag.lower()), False)
|
||||
|
||||
result = frappe.db.sql('''
|
||||
|
|
@ -58,10 +61,11 @@ def get_documents_for_tag(tag):
|
|||
'''.format(tag), as_dict=True)
|
||||
|
||||
for res in result:
|
||||
if res.dt in results.keys():
|
||||
results[res.dt].append(res)
|
||||
else:
|
||||
results[res.dt] = [res]
|
||||
results.append({
|
||||
"doctype": res.dt,
|
||||
"name": res.dn,
|
||||
"content": res.title
|
||||
})
|
||||
|
||||
return results
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue