mirror of
https://github.com/rr-/szurubooru.git
synced 2025-07-17 08:26:24 +00:00
client/search: autocomplete tag alias that the user searched for
Instead of always using the first alias. "Tag input" fields (edit page, bulk tagging, etc.) will display the matched name in suggestions, and use the first alias upon selection.
This commit is contained in:
@ -4,18 +4,20 @@ const misc = require("../util/misc.js");
|
||||
const PoolList = require("../models/pool_list.js");
|
||||
const AutoCompleteControl = require("./auto_complete_control.js");
|
||||
|
||||
function _poolListToMatches(pools, options) {
|
||||
function _poolListToMatches(text, pools, options) {
|
||||
return [...pools]
|
||||
.sort((pool1, pool2) => {
|
||||
return pool2.postCount - pool1.postCount;
|
||||
})
|
||||
.map((pool) => {
|
||||
pool.matchingNames = pool.names.filter((name) => misc.wildcardMatch(text + "*", name, false));
|
||||
pool.matchingNames = pool.matchingNames.length ? pool.matchingNames : pool.names;
|
||||
let cssName = misc.makeCssName(pool.category, "pool");
|
||||
const caption =
|
||||
'<span class="' +
|
||||
cssName +
|
||||
'">' +
|
||||
misc.escapeHtml(pool.names[0] + " (" + pool.postCount + ")") +
|
||||
misc.escapeHtml(pool.matchingNames[0] + " (" + pool.postCount + ")") +
|
||||
"</span>";
|
||||
return {
|
||||
caption: caption,
|
||||
@ -46,7 +48,7 @@ class PoolAutoCompleteControl extends AutoCompleteControl {
|
||||
{ noProgress: true }).then(
|
||||
(response) =>
|
||||
resolve(
|
||||
_poolListToMatches(response.results, this._options)
|
||||
_poolListToMatches(text, response.results, this._options)
|
||||
),
|
||||
reject
|
||||
);
|
||||
@ -55,6 +57,17 @@ class PoolAutoCompleteControl extends AutoCompleteControl {
|
||||
|
||||
super(input, options);
|
||||
}
|
||||
|
||||
_getActiveSuggestion() {
|
||||
if (this._activeResult === -1) {
|
||||
return null;
|
||||
}
|
||||
const result = this._results[this._activeResult].value;
|
||||
const textToFind = this._options.getTextToFind();
|
||||
result.matchingNames = result.names.filter((name) => misc.wildcardMatch(textToFind + "*", name, false));
|
||||
result.matchingNames = result.matchingNames.length ? result.matchingNames : result.names;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = PoolAutoCompleteControl;
|
||||
|
@ -5,24 +5,27 @@ const views = require("../util/views.js");
|
||||
const TagList = require("../models/tag_list.js");
|
||||
const AutoCompleteControl = require("./auto_complete_control.js");
|
||||
|
||||
function _tagListToMatches(tags, options, negated) {
|
||||
function _tagListToMatches(text, tags, options, negated) {
|
||||
return [...tags]
|
||||
.sort((tag1, tag2) => {
|
||||
return tag2.usages - tag1.usages;
|
||||
})
|
||||
.map((tag) => {
|
||||
tag.matchingNames = tag.names.filter((name) => misc.wildcardMatch(text + "*", name, false));
|
||||
tag.matchingNames = tag.matchingNames.length ? tag.matchingNames : tag.names;
|
||||
let cssName = misc.makeCssName(tag.category, "tag");
|
||||
if (options.isTaggedWith(tag.names[0])) {
|
||||
cssName += " disabled";
|
||||
}
|
||||
if (negated) {
|
||||
tag.names = tag.names.map((tagName) => "-"+tagName);
|
||||
tag.matchingNames = tag.matchingNames.map((tagName) => "-"+tagName);
|
||||
}
|
||||
const caption =
|
||||
'<span class="' +
|
||||
cssName +
|
||||
'">' +
|
||||
misc.escapeHtml(tag.names[0] + " (" + tag.postCount + ")") +
|
||||
misc.escapeHtml(tag.matchingNames[0] + " (" + tag.postCount + ")") +
|
||||
"</span>";
|
||||
return {
|
||||
caption: caption,
|
||||
@ -68,7 +71,7 @@ class TagAutoCompleteControl extends AutoCompleteControl {
|
||||
{ noProgress: true }).then(
|
||||
(response) =>
|
||||
resolve(
|
||||
_tagListToMatches(response.results, this._options, negated)
|
||||
_tagListToMatches(text, response.results, this._options, negated)
|
||||
),
|
||||
reject
|
||||
);
|
||||
@ -77,6 +80,17 @@ class TagAutoCompleteControl extends AutoCompleteControl {
|
||||
|
||||
super(input, options);
|
||||
}
|
||||
|
||||
_getActiveSuggestion() {
|
||||
if (this._activeResult === -1) {
|
||||
return null;
|
||||
}
|
||||
const result = this._results[this._activeResult].value;
|
||||
const textToFind = this._options.getTextToFind();
|
||||
result.matchingNames = result.names.filter((name) => misc.wildcardMatch(textToFind + "*", name, false));
|
||||
result.matchingNames = result.matchingNames.length ? result.matchingNames : result.names;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TagAutoCompleteControl;
|
||||
|
@ -211,6 +211,12 @@ function getPrettyName(tag) {
|
||||
return tag;
|
||||
}
|
||||
|
||||
function wildcardMatch(pattern, str, sensitive = false) {
|
||||
let w = pattern.replace(/[.+^${}()|[\]\\?]/g, "\\$&");
|
||||
const re = new RegExp(`^${w.replace(/\(--wildcard--\)|\*/g, ".*")}$`, sensitive ? "" : "i");
|
||||
return re.test(str);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
range: range,
|
||||
formatRelativeTime: formatRelativeTime,
|
||||
@ -229,4 +235,5 @@ module.exports = {
|
||||
escapeSearchTerm: escapeSearchTerm,
|
||||
dataURItoBlob: dataURItoBlob,
|
||||
getPrettyName: getPrettyName,
|
||||
wildcardMatch: wildcardMatch,
|
||||
};
|
||||
|
@ -27,7 +27,7 @@ class HomeView {
|
||||
{
|
||||
confirm: (tag) =>
|
||||
this._autoCompleteControl.replaceSelectedText(
|
||||
misc.escapeSearchTerm(tag.names[0]),
|
||||
misc.escapeSearchTerm(tag.matchingNames[0]),
|
||||
true
|
||||
),
|
||||
isNegationAllowed: true,
|
||||
|
@ -25,7 +25,7 @@ class PoolMergeView extends events.EventTarget {
|
||||
confirm: (pool) => {
|
||||
this._targetPoolId = pool.id;
|
||||
this._autoCompleteControl.replaceSelectedText(
|
||||
pool.names[0],
|
||||
pool.matchingNames[0],
|
||||
false
|
||||
);
|
||||
},
|
||||
|
@ -21,7 +21,7 @@ class PoolsHeaderView extends events.EventTarget {
|
||||
{
|
||||
confirm: (pool) =>
|
||||
this._autoCompleteControl.replaceSelectedText(
|
||||
misc.escapeSearchTerm(pool.names[0]),
|
||||
misc.escapeSearchTerm(pool.matchingNames[0]),
|
||||
true
|
||||
),
|
||||
}
|
||||
|
@ -183,7 +183,7 @@ class PostsHeaderView extends events.EventTarget {
|
||||
{
|
||||
confirm: (tag) =>
|
||||
this._autoCompleteControl.replaceSelectedText(
|
||||
misc.escapeSearchTerm(tag.names[0]),
|
||||
misc.escapeSearchTerm(tag.matchingNames[0]),
|
||||
true
|
||||
),
|
||||
isNegationAllowed: true,
|
||||
|
@ -23,7 +23,7 @@ class TagMergeView extends events.EventTarget {
|
||||
{
|
||||
confirm: (tag) =>
|
||||
this._autoCompleteControl.replaceSelectedText(
|
||||
tag.names[0],
|
||||
tag.matchingNames[0],
|
||||
false
|
||||
),
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ class TagsHeaderView extends events.EventTarget {
|
||||
{
|
||||
confirm: (tag) =>
|
||||
this._autoCompleteControl.replaceSelectedText(
|
||||
misc.escapeSearchTerm(tag.names[0]),
|
||||
misc.escapeSearchTerm(tag.matchingNames[0]),
|
||||
true
|
||||
),
|
||||
}
|
||||
|
Reference in New Issue
Block a user