From 43ab6dfb6acc94b1cc3e85569e1ff24d66ecad79 Mon Sep 17 00:00:00 2001 From: Nikhil Tanwar <2002nikhiltanwar@gmail.com> Date: Fri, 11 Feb 2022 21:59:26 +0530 Subject: [PATCH] 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 --- static/skin/index.css | 38 ++++++++++++++++++++++-- static/skin/index.js | 59 +++++++++++++++++++++++++++++++++---- static/templates/index.html | 1 + test/server.cpp | 4 +-- 4 files changed, 92 insertions(+), 10 deletions(-) diff --git a/static/skin/index.css b/static/skin/index.css index 800aca210..8443feacb 100644 --- a/static/skin/index.css +++ b/static/skin/index.css @@ -134,6 +134,27 @@ body { position: relative; } +.tagFilterLabel { + width: max-content; + padding: 10px; + font-family: roboto; + font-size: 12px; + margin: 0 0 0 17px; + text-align: center; + background-color: transparent; + color: #909090; + display: none; +} + +.tag__link { + cursor: pointer; + color: #909090; +} + +.tag__link:hover { + font-weight: bolder; +} + .book__list { position: relative; } @@ -144,6 +165,8 @@ body { .book__link { text-decoration: none; + grid-column: 1 / 3; + grid-row: 1 / 3; } .book__wrapper { @@ -165,6 +188,12 @@ body { transform: scale(1.07); } +.book__link__wrapper { + display: grid; + grid-template-columns: 65px 1fr; + grid-template-rows: 70px 120px 1fr 1fr; +} + .book__icon { background-image: url('data:image/png;base64,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'); background-repeat: no-repeat; @@ -249,7 +278,6 @@ body { text-align: end; font-size: 1.1rem; justify-content: flex-end; - color: #909090; font-family: roboto; margin-right: 10px; margin-top: 10px; @@ -424,7 +452,7 @@ body { @media screen and (max-width: 590px) { .kiwixNav { - height: 257px; + height: 285px; } .kiwixHomeBody { @@ -436,10 +464,14 @@ body { } .kiwixButton { - margin: 19px 0; + margin: 15px 0; width: 229px; } + .tagFilterLabel { + margin: 15px 0 0 0; + } + .kiwixNav__filters { grid-template-columns: 1fr; } diff --git a/static/skin/index.js b/static/skin/index.js index a6bcdd744..83ad0e8c8 100644 --- a/static/skin/index.js +++ b/static/skin/index.js @@ -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 `${humanFriendlyTagValue}` + } + 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 = ` + divTag.innerHTML = `
+ + +
${getLanguageCodeToDisplay(langCode)}
${tagHtml}
-
`; + `; 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()}`; diff --git a/static/templates/index.html b/static/templates/index.html index a62185fa8..0dabc45b7 100644 --- a/static/templates/index.html +++ b/static/templates/index.html @@ -58,6 +58,7 @@
+
diff --git a/test/server.cpp b/test/server.cpp index 145f59226..3e31d1f6d 100644 --- a/test/server.cpp +++ b/test/server.cpp @@ -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"); - + )EXPECTEDRESULT" }, {