Add quote length filter (#2705) Bruception

* Add quote length filter

* Clear filters when closing search

* Add select2 style selectors

* updated placeholders

* fixed styling

Co-authored-by: Miodec <bartnikjack@gmail.com>
This commit is contained in:
Bruce Berrios 2022-03-15 16:14:17 -04:00 committed by GitHub
parent ab27950bcc
commit eb79ffbc00
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 216 additions and 61 deletions

View file

@ -63,6 +63,7 @@ class QuotesController {
length: quote.length,
id: quote.id,
language: language,
group: 0,
};
this.quoteCollection.quotes.push(monkeyTypeQuote);

View file

@ -54,6 +54,70 @@ function highlightMatches(text: string, matchedText: string[]): string {
return normalizedWords.join("");
}
function applyQuoteLengthFilter(
quotes: MonkeyTypes.Quote[]
): MonkeyTypes.Quote[] {
const quoteLengthFilterValue = $(
"#quoteSearchPopup .quoteLengthFilter"
).val() as string[];
if (quoteLengthFilterValue.length === 0) {
return quotes;
}
const quoteLengthFilter = quoteLengthFilterValue.map((filterValue) =>
parseInt(filterValue, 10)
);
const filteredQuotes = quotes.filter((quote) =>
quoteLengthFilter.includes(quote.group)
);
return filteredQuotes;
}
function buildQuoteSearchResult(
quote: MonkeyTypes.Quote,
matchedSearchTerms: string[]
): string {
let lengthDesc;
if (quote.length < 101) {
lengthDesc = "short";
} else if (quote.length < 301) {
lengthDesc = "medium";
} else if (quote.length < 601) {
lengthDesc = "long";
} else {
lengthDesc = "thicc";
}
const isNotAuthed = !firebase.auth().currentUser;
return `
<div class="searchResult" id="${quote.id}">
<div class="text">
${highlightMatches(quote.text, matchedSearchTerms)}
</div>
<div class="id">
<div class="sub">id</div>
<span class="quote-id">
${highlightMatches(quote.id.toString(), matchedSearchTerms)}
</span>
</div>
<div class="length">
<div class="sub">length</div>
${lengthDesc}
</div>
<div class="source">
<div class="sub">source</div>
${highlightMatches(quote.source, matchedSearchTerms)}</div>
<div class="icon-button report ${
isNotAuthed && "hidden"
}" aria-label="Report quote" data-balloon-pos="left">
<i class="fas fa-flag report"></i>
</div>
</div>
`;
}
async function updateResults(searchText: string): Promise<void> {
const { quotes } = await QuotesController.getQuotes(Config.language);
@ -67,58 +131,25 @@ async function updateResults(searchText: string): Promise<void> {
const { results: matches, matchedQueryTerms } =
quoteSearchService.query(searchText);
$("#quoteSearchResults").remove();
$("#quoteSearchPopup").append(
'<div class="quoteSearchResults" id="quoteSearchResults"></div>'
const quotesToShow = applyQuoteLengthFilter(
searchText === "" ? quotes : matches
);
const resultsList = $("#quoteSearchResults");
const isNotAuthed = !firebase.auth().currentUser;
const quotesToShow = searchText === "" ? quotes : matches;
resultsList.empty();
quotesToShow.slice(0, 100).forEach((quote) => {
let lengthDesc;
if (quote.length < 101) {
lengthDesc = "short";
} else if (quote.length < 301) {
lengthDesc = "medium";
} else if (quote.length < 601) {
lengthDesc = "long";
} else {
lengthDesc = "thicc";
}
resultsList.append(`
<div class="searchResult" id="${quote.id}">
<div class="text">${highlightMatches(
quote.text,
matchedQueryTerms
)}</div>
<div class="id"><div class="sub">id</div><span class="quote-id">${highlightMatches(
quote.id.toString(),
matchedQueryTerms
)}</span></div>
<div class="length"><div class="sub">length</div>${lengthDesc}</div>
<div class="source"><div class="sub">source</div>${highlightMatches(
quote.source,
matchedQueryTerms
)}</div>
<div class="icon-button report ${
isNotAuthed && "hidden"
}" aria-label="Report quote" data-balloon-pos="left">
<i class="fas fa-flag report"></i>
</div>
</div>
`);
const quoteSearchResult = buildQuoteSearchResult(quote, matchedQueryTerms);
resultsList.append(quoteSearchResult);
});
if (quotesToShow.length > 100) {
$("#extraResults").html(
quotesToShow.length +
" results <span style='opacity: 0.5'>(only showing 100)</span>"
);
} else {
$("#extraResults").html(quotesToShow.length + " results");
}
const resultsExceededText =
quotesToShow.length > 100
? "<span style='opacity: 0.5'>(only showing 100)</span>"
: "";
$("#extraResults").html(
`${quotesToShow.length} results ${resultsExceededText}`
);
}
export async function show(clearText = true): Promise<void> {
@ -141,6 +172,35 @@ export async function show(clearText = true): Promise<void> {
$("#quoteSearchPopup #goToApproveQuotes").addClass("hidden");
}
$("#quoteSearchPopup .quoteLengthFilter").select2({
placeholder: "Filter by length",
maximumSelectionLength: Infinity,
multiple: true,
width: "100%",
data: [
{
id: 0,
text: "short",
selected: false,
},
{
id: 1,
text: "medium",
selected: false,
},
{
id: 2,
text: "long",
selected: false,
},
{
id: 3,
text: "thicc",
selected: false,
},
],
});
$("#quoteSearchPopupWrapper")
.stop(true, true)
.css("opacity", 0)
@ -166,8 +226,11 @@ export function hide(noAnim = false, focusWords = true): void {
noAnim ? 0 : 100,
() => {
$("#quoteSearchPopupWrapper").addClass("hidden");
if (focusWords) {
TestUI.focusWords();
$("#quoteSearchPopup .quoteLengthFilter").val([]);
$("#quoteSearchPopup .quoteLengthFilter").trigger("change");
}
}
);
@ -194,16 +257,19 @@ export function apply(val: number): boolean {
return ret;
}
const debouncedSearch = debounce(updateResults);
const searchForQuotes = debounce((): void => {
const searchText = (<HTMLInputElement>document.getElementById("searchBox"))
.value;
updateResults(searchText);
});
$("#quoteSearchPopup .searchBox").on("keyup", (e) => {
if (e.code === "Escape") return;
const searchText = (<HTMLInputElement>document.getElementById("searchBox"))
.value;
debouncedSearch(searchText);
searchForQuotes();
});
$("#quoteSearchPopup .quoteLengthFilter").on("change", searchForQuotes);
$("#quoteSearchPopupWrapper").on("click", (e) => {
if ($(e.target).attr("id") === "quoteSearchPopupWrapper") {
hide();
@ -237,7 +303,6 @@ $(document).on("click", "#quoteSearchPopup .report", async (e) => {
$(document).on("click", "#top .config .quoteLength .text-button", (e) => {
const len = $(e.currentTarget).attr("quoteLength") ?? (0 as number);
if (len == -2) {
// UpdateConfig.setQuoteLength(-2, false, e.shiftKey);
show();
}
});

View file

@ -591,7 +591,7 @@ declare namespace MonkeyTypes {
source: string;
length: number;
id: number;
group?: number;
group: number;
language: string;
textSplit?: string[];
}

View file

@ -182,3 +182,73 @@ input[type="number"] {
.select2-container--default .select2-results__group {
color: var(--text-color);
}
.select2-container--default .select2-selection--multiple {
background: rgba(0, 0, 0, 0.1);
border-radius: var(--roundness);
color: var(--text-color);
outline: none;
border: none;
}
.select2-selection__choice__display {
color: var(--bg-color);
}
.select2-selection__choice__remove {
color: var(--main-color) !important;
}
.select2-container .select2-search--inline .select2-search__field {
margin: 0;
border-radius: 0;
font-family: var(--font);
line-height: 1rem;
}
.select2-container--default.select2-container--focus
.select2-selection--multiple {
border: none;
}
.select2-container--default .select2-selection--multiple {
border: none;
padding: 0.5rem;
font-family: var(--font);
}
.select2-container--default
.select2-selection--multiple
.select2-selection__choice {
background-color: var(--main-color);
border: none;
border-radius: calc(var(--roundness) / 2);
box-sizing: border-box;
display: inline-block;
margin-left: 0;
margin-top: 0;
margin-right: 0.5rem;
padding: 0;
padding-left: 1.25rem;
position: relative;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
vertical-align: bottom;
white-space: nowrap;
}
.select2-container--default
.select2-selection--multiple
.select2-selection__choice__remove {
line-height: 1.5rem;
border-right: 1px solid var(--bg-color);
color: var(--bg-color) !important;
transition: 0.125s;
}
.select2-container--default
.select2-selection--multiple
.select2-selection__choice__remove:hover {
background-color: var(--text-color);
}

View file

@ -446,6 +446,15 @@
}
}
#quoteSearchControlsWrapper {
display: grid;
grid-template-columns: 1.5fr 1fr;
gap: 1rem;
#searchBox {
width: 100%;
}
}
#extraResults {
text-align: center;
color: var(--sub-color);
@ -455,6 +464,7 @@
gap: 0.5rem;
height: auto;
overflow-y: scroll;
align-content: baseline;
.searchResult {
display: grid;

View file

@ -32,13 +32,18 @@
}
}
}
#quoteSearchPopupWrapper #quoteSearchPopup #quoteSearchControlsWrapper {
grid-template-columns: 1fr 1fr;
}
}
@media only screen and (max-width: 1050px) {
.pageSettings .section.fullWidth .buttons {
grid-template-columns: 1fr 1fr 1fr;
}
#quoteSearchPopupWrapper #quoteSearchPopup #quoteSearchControlsWrapper {
grid-template-columns: 1fr;
}
#result .morestats {
gap: 1rem;
grid-template-rows: 1fr 1fr;

View file

@ -517,13 +517,17 @@
</div>
</div>
</div>
<input
id="searchBox"
class="searchBox"
type="text"
maxlength="200"
autocomplete="off"
/>
<div id="quoteSearchControlsWrapper">
<input
id="searchBox"
class="searchBox"
type="text"
maxlength="200"
autocomplete="off"
placeholder="Filter by text"
/>
<select class="quoteLengthFilter"></select>
</div>
<div id="extraResults">No search results</div>
<div id="quoteSearchResults" class="quoteSearchResults"></div>
</div>