Add ability to filter by tags in kiwix serve

This change introduces filtering by tags.
To filter, the user can click on the tag name and it will filter it.
A label is added (clickable) to show the tag filter, it can be clicked to remove the filter
This commit is contained in:
Nikhil Tanwar 2022-02-11 21:59:26 +05:30 committed by Veloman Yunkan
parent 93f2686a94
commit 43ab6dfb6a
4 changed files with 92 additions and 10 deletions

File diff suppressed because one or more lines are too long

View File

@ -77,6 +77,13 @@
return queryNode != null ? queryNode.innerHTML : "";
}
function generateTagLink(tagValue) {
tagValue = tagValue.toLowerCase();
const humanFriendlyTagValue = humanFriendlyTitle(tagValue);
const tagMessage = `Filter by tag "${humanFriendlyTagValue}"`;
return `<span class='tag__link' aria-label='${tagMessage}' title='${tagMessage}' data-tag=${tagValue}>${humanFriendlyTagValue}</span>`
}
function generateBookHtml(book, sort = false) {
const link = book.querySelector('link[type="text/html"]').getAttribute('href');
let iconUrl;
@ -91,9 +98,9 @@
const langCode = getInnerHtml(book, 'language');
const language = languages[langCode];
const tags = getInnerHtml(book, 'tags');
let tagHtml = tags.split(';').filter(tag => {return !(tag.split(':')[0].startsWith('_'))})
.map((tag) => {return tag.charAt(0).toUpperCase() + tag.slice(1)})
.join(' | ').replace(/_/g, ' ');
const tagList = tags.split(';').filter(tag => {return !(tag.startsWith('_'))});
const tagFilterLinks = tagList.map((tagValue) => generateTagLink(tagValue));
const tagHtml = tagFilterLinks.join(' | ');
let downloadLink;
let zimSize = 0;
try {
@ -113,17 +120,21 @@
}
const faviconAttr = iconUrl != undefined ? `style="background-image: url('${iconUrl}')"` : '';
const languageAttr = langCode != '' ? `title="${language}" aria-label="${language}"` : 'style="background-color: transparent"';
divTag.innerHTML = `<a class="book__link" href="${link}" data-hover="Preview">
divTag.innerHTML = `
<div class="book__wrapper">
<a class="book__link" href="${link}" data-hover="Preview">
<div class="book__link__wrapper">
<div class="book__icon" ${faviconAttr}></div>
<div class="book__header">
<div id="book__title">${title}</div>
${downloadLink ? `<div class="book__download"><span data-link="${downloadLink}">Download ${humanFriendlyZimSize ? ` - ${humanFriendlyZimSize}</span></div>`: ''}` : ''}
</div>
<div class="book__description" title="${description}">${description}</div>
</div>
</a>
<div class="book__languageTag" ${languageAttr}>${getLanguageCodeToDisplay(langCode)}</div>
<div class="book__tags"><div class="book__tags--wrapper">${tagHtml}</div></div>
</div></div></a>`;
</div></div>`;
return divTag;
}
@ -333,6 +344,7 @@
insertModal(downloadButton);
}
});
refreshTagLinks();
}
async function resetAndFilter(filterType = '', filterValue = '') {
@ -376,10 +388,45 @@
}
});
}
function addTagElement(tagValue, resetFilter) {
const tagElement = document.getElementsByClassName('tagFilterLabel')[0];
tagElement.style.display = 'inline-block';
const humanFriendlyTagValue = humanFriendlyTitle(tagValue);
tagElement.innerHTML = `${humanFriendlyTagValue}`;
const tagMessage = `Stop filtering by tag "${humanFriendlyTagValue}"`;
tagElement.setAttribute('aria-label', tagMessage);
tagElement.setAttribute('title', tagMessage);
if (resetFilter)
resetAndFilter('tag', tagValue);
}
function refreshTagLinks() {
const tagLinks = document.getElementsByClassName('tag__link');
[...tagLinks].forEach(elem => {
if (!elem.getAttribute('click-listener')) {
elem.addEventListener('click', () => addTagElement(elem.dataset.tag, true));
elem.setAttribute('click-listener', 'true');
}
});
}
function removeTagElement(resetFilter) {
const tagElement = document.getElementsByClassName('tagFilterLabel')[0];
tagElement.style.display = 'none';
if (resetFilter)
resetAndFilter('tag', '');
}
function updateVisibleParams() {
document.querySelectorAll('.filter').forEach(filter => {filter.value = params.get(filter.name) || ''});
updateFilterColors();
const tagKey = params.get('tag');
if (tagKey !== null && tagKey.trim() !== '') {
addTagElement(tagKey, false);
} else {
removeTagElement(false);
}
}
window.addEventListener('resize', (event) => {
@ -417,6 +464,8 @@
document.querySelectorAll('.filter').forEach(filter => {
filter.addEventListener('change', () => {resetAndFilter(filter.name, filter.value)});
});
const tagElement = document.getElementsByClassName('tagFilterLabel')[0];
tagElement.addEventListener('click', () => removeTagElement(true));
if (filters) {
const currentLink = window.location.search;
const newLink = `?${params.toString()}`;

View File

@ -58,6 +58,7 @@
</div>
<form id='kiwixSearchForm' class='kiwixNav__SearchForm'>
<input type="text" name="q" placeholder="Search" id="searchFilter" class='kiwixSearch filter'>
<span class="kiwixButton tagFilterLabel"></span>
<input type="submit" class="kiwixButton kiwixButtonHover" value="Search"/>
</form>
</div>

View File

@ -179,12 +179,12 @@ R"EXPECTEDRESULT( src="/ROOT/skin/jquery-ui/external/jquery/jquery.js?cache
src="/ROOT/skin/jquery-ui/jquery-ui.min.js?cacheid=d927c2ff"
href="/ROOT/skin/jquery-ui/jquery-ui.min.css?cacheid=e1de77b3"
href="/ROOT/skin/jquery-ui/jquery-ui.theme.min.css?cacheid=2a5841f9"
href="/ROOT/skin/index.css?cacheid=1aca980a"
href="/ROOT/skin/index.css?cacheid=a1acc52f"
src: url("/ROOT/skin/fonts/Poppins.ttf?cacheid=af705837") format("truetype");
src: url("/ROOT/skin/fonts/Roboto.ttf?cacheid=84d10248") format("truetype");
<script src="/ROOT/skin/isotope.pkgd.min.js?cacheid=2e48d392" defer></script>
<script src="/ROOT/skin/iso6391To3.js?cacheid=ecde2bb3"></script>
<script type="text/javascript" src="/ROOT/skin/index.js?cacheid=5f0f7683" defer></script>
<script type="text/javascript" src="/ROOT/skin/index.js?cacheid=e99ed2dd" defer></script>
)EXPECTEDRESULT"
},
{