mirror of
https://github.com/rr-/szurubooru.git
synced 2025-07-17 08:26:24 +00:00
Merge 96f5a028b8
into 376f687c38
This commit is contained in:
@ -369,6 +369,7 @@ input[type=file]:focus+.file-dropper,
|
||||
a
|
||||
display: block
|
||||
padding: 0.1em 0.5em
|
||||
transition: none
|
||||
&.active a, a:hover
|
||||
background: $button-enabled-background-color
|
||||
color: $button-enabled-text-color
|
||||
|
@ -78,7 +78,8 @@ class TagListController {
|
||||
this._ctx.parameters.query,
|
||||
offset,
|
||||
limit,
|
||||
fields
|
||||
fields,
|
||||
{}
|
||||
);
|
||||
},
|
||||
pageRenderer: (pageCtx) => {
|
||||
|
@ -70,11 +70,18 @@ class AutoCompleteControl {
|
||||
prefix = this._sourceInputNode.value.substring(0, index + 1);
|
||||
middle = this._sourceInputNode.value.substring(index + 1);
|
||||
}
|
||||
suffix = spaceIndex < commaIndex ? suffix.replace(/^[^,]+/, "") : suffix.replace(/^\S+/, "");
|
||||
suffix = suffix.trimLeft();
|
||||
this._sourceInputNode.value =
|
||||
prefix + result.toString() + delimiter + suffix.trimLeft();
|
||||
prefix + result.toString() + delimiter + suffix;
|
||||
if (!addSpace) {
|
||||
this._sourceInputNode.value = this._sourceInputNode.value.trim();
|
||||
this._sourceInputNode.value = this._sourceInputNode.value.trimLeft();
|
||||
}
|
||||
const selection = this._sourceInputNode.value.length - suffix.length;
|
||||
if (!addSpace) {
|
||||
this._sourceInputNode.value = this._sourceInputNode.value.trimRight();
|
||||
}
|
||||
this._sourceInputNode.setSelectionRange(selection, selection);
|
||||
this._sourceInputNode.focus();
|
||||
}
|
||||
|
||||
@ -228,6 +235,13 @@ class AutoCompleteControl {
|
||||
}
|
||||
|
||||
_updateResults(textToFind) {
|
||||
if (this._options.isNegationAllowed && textToFind == "-") {
|
||||
this._results = [];
|
||||
this._activeResult = -1;
|
||||
this._refreshList();
|
||||
return;
|
||||
}
|
||||
|
||||
this._options.getMatches(textToFind).then((matches) => {
|
||||
const oldResults = this._results.slice();
|
||||
this._results = matches.slice(0, this._options.maxResults);
|
||||
|
@ -15,7 +15,7 @@ function _poolListToMatches(pools, options) {
|
||||
'<span class="' +
|
||||
cssName +
|
||||
'">' +
|
||||
misc.escapeHtml(pool.names[0] + " (" + pool.postCount + ")") +
|
||||
misc.escapeHtml(pool.matchingNames[0] + " (" + pool.postCount + ")") +
|
||||
"</span>";
|
||||
return {
|
||||
caption: caption,
|
||||
@ -54,6 +54,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,7 +5,7 @@ const views = require("../util/views.js");
|
||||
const TagList = require("../models/tag_list.js");
|
||||
const AutoCompleteControl = require("./auto_complete_control.js");
|
||||
|
||||
function _tagListToMatches(tags, options) {
|
||||
function _tagListToMatches(tags, options, negated) {
|
||||
return [...tags]
|
||||
.sort((tag1, tag2) => {
|
||||
return tag2.usages - tag1.usages;
|
||||
@ -15,11 +15,15 @@ function _tagListToMatches(tags, options) {
|
||||
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,
|
||||
@ -35,14 +39,24 @@ class TagAutoCompleteControl extends AutoCompleteControl {
|
||||
options = Object.assign(
|
||||
{
|
||||
isTaggedWith: (tag) => false,
|
||||
isNegationAllowed: false,
|
||||
},
|
||||
options
|
||||
);
|
||||
|
||||
options.getMatches = (text) => {
|
||||
const negated = options.isNegationAllowed && text[0] == "-";
|
||||
if (negated) text = text.substring(1);
|
||||
if (!text) {
|
||||
return new Promise((resolve, reject) => {
|
||||
(response) => resolve(null),
|
||||
reject
|
||||
});
|
||||
}
|
||||
|
||||
const term = misc.escapeSearchTerm(text);
|
||||
const query =
|
||||
(text.length < minLengthForPartialSearch
|
||||
(text.length < minLengthForPartialSearch && text[0] != "-"
|
||||
? term + "*"
|
||||
: "*" + term + "*") + " sort:usages";
|
||||
|
||||
@ -51,10 +65,11 @@ class TagAutoCompleteControl extends AutoCompleteControl {
|
||||
"names",
|
||||
"category",
|
||||
"usages",
|
||||
]).then(
|
||||
],
|
||||
{ noProgress: true }).then(
|
||||
(response) =>
|
||||
resolve(
|
||||
_tagListToMatches(response.results, this._options)
|
||||
_tagListToMatches(response.results, this._options, negated)
|
||||
),
|
||||
reject
|
||||
);
|
||||
@ -63,6 +78,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;
|
||||
|
@ -114,6 +114,7 @@ class TagInputControl extends events.EventTarget {
|
||||
},
|
||||
verticalShift: -2,
|
||||
isTaggedWith: (tagName) => this.tags.isTaggedWith(tagName),
|
||||
isNegationAllowed: false,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -6,7 +6,7 @@ const AbstractList = require("./abstract_list.js");
|
||||
const Tag = require("./tag.js");
|
||||
|
||||
class TagList extends AbstractList {
|
||||
static search(text, offset, limit, fields) {
|
||||
static search(text, offset, limit, fields, options) {
|
||||
return api
|
||||
.get(
|
||||
uri.formatApiLink("tags", {
|
||||
@ -14,7 +14,8 @@ class TagList extends AbstractList {
|
||||
offset: offset,
|
||||
limit: limit,
|
||||
fields: fields.join(","),
|
||||
})
|
||||
}),
|
||||
options
|
||||
)
|
||||
.then((response) => {
|
||||
return Promise.resolve(
|
||||
|
@ -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,9 +27,10 @@ class HomeView {
|
||||
{
|
||||
confirm: (tag) =>
|
||||
this._autoCompleteControl.replaceSelectedText(
|
||||
misc.escapeSearchTerm(tag.names[0]),
|
||||
misc.escapeSearchTerm(tag.matchingNames[0]),
|
||||
true
|
||||
),
|
||||
isNegationAllowed: true,
|
||||
}
|
||||
);
|
||||
this._formNode.addEventListener("submit", (e) =>
|
||||
|
@ -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,9 +183,10 @@ class PostsHeaderView extends events.EventTarget {
|
||||
{
|
||||
confirm: (tag) =>
|
||||
this._autoCompleteControl.replaceSelectedText(
|
||||
misc.escapeSearchTerm(tag.names[0]),
|
||||
misc.escapeSearchTerm(tag.matchingNames[0]),
|
||||
true
|
||||
),
|
||||
isNegationAllowed: true,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -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