mirror of https://github.com/kiwix/libkiwix.git
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:
parent
93f2686a94
commit
43ab6dfb6a
File diff suppressed because one or more lines are too long
|
@ -77,6 +77,13 @@
|
||||||
return queryNode != null ? queryNode.innerHTML : "";
|
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) {
|
function generateBookHtml(book, sort = false) {
|
||||||
const link = book.querySelector('link[type="text/html"]').getAttribute('href');
|
const link = book.querySelector('link[type="text/html"]').getAttribute('href');
|
||||||
let iconUrl;
|
let iconUrl;
|
||||||
|
@ -91,9 +98,9 @@
|
||||||
const langCode = getInnerHtml(book, 'language');
|
const langCode = getInnerHtml(book, 'language');
|
||||||
const language = languages[langCode];
|
const language = languages[langCode];
|
||||||
const tags = getInnerHtml(book, 'tags');
|
const tags = getInnerHtml(book, 'tags');
|
||||||
let tagHtml = tags.split(';').filter(tag => {return !(tag.split(':')[0].startsWith('_'))})
|
const tagList = tags.split(';').filter(tag => {return !(tag.startsWith('_'))});
|
||||||
.map((tag) => {return tag.charAt(0).toUpperCase() + tag.slice(1)})
|
const tagFilterLinks = tagList.map((tagValue) => generateTagLink(tagValue));
|
||||||
.join(' | ').replace(/_/g, ' ');
|
const tagHtml = tagFilterLinks.join(' | ');
|
||||||
let downloadLink;
|
let downloadLink;
|
||||||
let zimSize = 0;
|
let zimSize = 0;
|
||||||
try {
|
try {
|
||||||
|
@ -113,17 +120,21 @@
|
||||||
}
|
}
|
||||||
const faviconAttr = iconUrl != undefined ? `style="background-image: url('${iconUrl}')"` : '';
|
const faviconAttr = iconUrl != undefined ? `style="background-image: url('${iconUrl}')"` : '';
|
||||||
const languageAttr = langCode != '' ? `title="${language}" aria-label="${language}"` : 'style="background-color: transparent"';
|
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">
|
<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__icon" ${faviconAttr}></div>
|
||||||
<div class="book__header">
|
<div class="book__header">
|
||||||
<div id="book__title">${title}</div>
|
<div id="book__title">${title}</div>
|
||||||
${downloadLink ? `<div class="book__download"><span data-link="${downloadLink}">Download ${humanFriendlyZimSize ? ` - ${humanFriendlyZimSize}</span></div>`: ''}` : ''}
|
${downloadLink ? `<div class="book__download"><span data-link="${downloadLink}">Download ${humanFriendlyZimSize ? ` - ${humanFriendlyZimSize}</span></div>`: ''}` : ''}
|
||||||
</div>
|
</div>
|
||||||
<div class="book__description" title="${description}">${description}</div>
|
<div class="book__description" title="${description}">${description}</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
<div class="book__languageTag" ${languageAttr}>${getLanguageCodeToDisplay(langCode)}</div>
|
<div class="book__languageTag" ${languageAttr}>${getLanguageCodeToDisplay(langCode)}</div>
|
||||||
<div class="book__tags"><div class="book__tags--wrapper">${tagHtml}</div></div>
|
<div class="book__tags"><div class="book__tags--wrapper">${tagHtml}</div></div>
|
||||||
</div></div></a>`;
|
</div></div>`;
|
||||||
return divTag;
|
return divTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,6 +344,7 @@
|
||||||
insertModal(downloadButton);
|
insertModal(downloadButton);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
refreshTagLinks();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function resetAndFilter(filterType = '', filterValue = '') {
|
async function resetAndFilter(filterType = '', filterValue = '') {
|
||||||
|
@ -377,9 +389,44 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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() {
|
function updateVisibleParams() {
|
||||||
document.querySelectorAll('.filter').forEach(filter => {filter.value = params.get(filter.name) || ''});
|
document.querySelectorAll('.filter').forEach(filter => {filter.value = params.get(filter.name) || ''});
|
||||||
updateFilterColors();
|
updateFilterColors();
|
||||||
|
const tagKey = params.get('tag');
|
||||||
|
if (tagKey !== null && tagKey.trim() !== '') {
|
||||||
|
addTagElement(tagKey, false);
|
||||||
|
} else {
|
||||||
|
removeTagElement(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('resize', (event) => {
|
window.addEventListener('resize', (event) => {
|
||||||
|
@ -417,6 +464,8 @@
|
||||||
document.querySelectorAll('.filter').forEach(filter => {
|
document.querySelectorAll('.filter').forEach(filter => {
|
||||||
filter.addEventListener('change', () => {resetAndFilter(filter.name, filter.value)});
|
filter.addEventListener('change', () => {resetAndFilter(filter.name, filter.value)});
|
||||||
});
|
});
|
||||||
|
const tagElement = document.getElementsByClassName('tagFilterLabel')[0];
|
||||||
|
tagElement.addEventListener('click', () => removeTagElement(true));
|
||||||
if (filters) {
|
if (filters) {
|
||||||
const currentLink = window.location.search;
|
const currentLink = window.location.search;
|
||||||
const newLink = `?${params.toString()}`;
|
const newLink = `?${params.toString()}`;
|
||||||
|
|
|
@ -58,6 +58,7 @@
|
||||||
</div>
|
</div>
|
||||||
<form id='kiwixSearchForm' class='kiwixNav__SearchForm'>
|
<form id='kiwixSearchForm' class='kiwixNav__SearchForm'>
|
||||||
<input type="text" name="q" placeholder="Search" id="searchFilter" class='kiwixSearch filter'>
|
<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"/>
|
<input type="submit" class="kiwixButton kiwixButtonHover" value="Search"/>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -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"
|
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.min.css?cacheid=e1de77b3"
|
||||||
href="/ROOT/skin/jquery-ui/jquery-ui.theme.min.css?cacheid=2a5841f9"
|
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/Poppins.ttf?cacheid=af705837") format("truetype");
|
||||||
src: url("/ROOT/skin/fonts/Roboto.ttf?cacheid=84d10248") 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/isotope.pkgd.min.js?cacheid=2e48d392" defer></script>
|
||||||
<script src="/ROOT/skin/iso6391To3.js?cacheid=ecde2bb3"></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"
|
)EXPECTEDRESULT"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue