From 6ce6744bcabd13207eac8440a04462c9770d6406 Mon Sep 17 00:00:00 2001 From: gravel Date: Tue, 9 Jan 2024 22:04:31 +0000 Subject: [PATCH] Revamp header, add icons, improve search --- .gitmodules | 3 + output/assets/icons | 1 + output/css/banner.css | 4 +- output/css/common.css | 16 +++ output/css/footer.css | 4 +- output/index.css | 161 ++++++++++++++--------- output/js/util.js | 8 +- output/main.js | 62 +++++---- sites/+components/communities-search.php | 91 ++++++------- sites/+components/footer.php | 6 +- sites/+components/index-header.php | 31 +++++ sites/+components/issue-banner.php | 2 +- sites/index.php | 38 +----- 13 files changed, 253 insertions(+), 174 deletions(-) create mode 100644 .gitmodules create mode 120000 output/assets/icons create mode 100644 sites/+components/index-header.php diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..9bb8f0a --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "feather"] + path = feather + url = https://github.com/feathericons/feather diff --git a/output/assets/icons b/output/assets/icons new file mode 120000 index 0000000..a10c49a --- /dev/null +++ b/output/assets/icons @@ -0,0 +1 @@ +../../feather/icons \ No newline at end of file diff --git a/output/css/banner.css b/output/css/banner.css index 6c8b59d..a503866 100644 --- a/output/css/banner.css +++ b/output/css/banner.css @@ -1,6 +1,8 @@ .banner { position: relative; - margin: 0 1rem 1rem; + box-sizing: border-box; + align-self: stretch; + margin: 0 1rem 0.5rem; border-left: var(--primary-color) 1rem solid; padding-inline: 1rem; text-align: center; diff --git a/output/css/common.css b/output/css/common.css index 9cbde99..c3a86b7 100644 --- a/output/css/common.css +++ b/output/css/common.css @@ -18,4 +18,20 @@ --color-https: hsl(195, 53%, 79%); } +.feather-icon { + filter: invert(); +} + +#toggle-theme-switch:checked ~ #theming-root .feather-icon { + filter: none; +} + +:not(#toggle-theme-switch:checked ~ #theming-root *).light-theme-only { + display: none; +} + +#toggle-theme-switch:checked ~ #theming-root .dark-theme-only { + display: none; +} + :root { text-underline-offset: 0.2em; } diff --git a/output/css/footer.css b/output/css/footer.css index c57dc08..88a0d8b 100644 --- a/output/css/footer.css +++ b/output/css/footer.css @@ -32,8 +32,8 @@ footer:first-of-type { white-space: nowrap; } -#more-sites-info-button { - font-size: 1.25em; +footer#author { + align-self: stretch; } #footer__author { diff --git a/output/index.css b/output/index.css index 1cf0ffb..585d201 100644 --- a/output/index.css +++ b/output/index.css @@ -83,6 +83,7 @@ body { margin: 0; background-color: var(--secondary-color); color: var(--primary-color); + align-items: center; } a, .anchorstyle { @@ -94,20 +95,28 @@ a, .anchorstyle { cursor: pointer; } -/* Enumerate elements with transitions to improve performance on Chromium. */ -/* -#theming-root, th, tr, .protocol-indicator, .copy_button, -.td_description::after, -#details-modal, #details-modal .themed-button, -a, .anchorstyle { - transition: color 2.5s linear, background-color 2s linear; +.feather-icon { + width: var(--icon-size); + height: var(--icon-size); + transition: opacity 0.2s; +} + +.feather-icon.clickable:not(:hover), +:is(a, .clickable ):not(:hover) > .feather-icon { + opacity: 0.6; } -*/ html.js .noscript, .hidden { display: none; } +.themed-button { + color: var(--primary-color); + background-color: var(--secondary-color); + border-radius: 10%; + padding: var(--cell-padding-small); +} + .badge { display: inline-block; border-radius: 1em; @@ -120,13 +129,14 @@ html.js .noscript, .hidden { } .highlight { - animation: highlight 1s ease-in-out 0.1s 1 forwards; + animation: highlight 0.66s 0.1s 1 forwards; + animation-timing-function: cubic-bezier(0,.89,.43,1.84); } @keyframes highlight { 0% { } - 50% { scale: 2; filter: brightness(125%); } - 100% { } + 50% { translate: 0 -0.5rem; } + 100% { } } .tag, .sample-search { @@ -168,11 +178,18 @@ gap { header { display: flex; - direction: row; + align-self: stretch; + flex-direction: row; /* Push items as far apart as possible */ justify-content: space-between; + align-items: flex-start; padding-top: var(--body-margin); padding-inline: var(--body-margin); + --icon-size: 2.5rem; +} + +header .feather-icon, #search .feather-icon { + font-size: var(--icon-size); } #header-start, #header-end { @@ -180,77 +197,102 @@ header { gap: 0.5em; } -#headline { - text-align: center; - flex-grow: 1; +html:not(.js) :is(#search-container, #search-container *) { + cursor: not-allowed !important; } #search-container { display: flex; - flex-direction: column; - max-height: 10rem; - justify-content: center; + flex-direction: row; + justify-items: space-between; + align-items: stretch; + margin-block-end: 1rem; + column-gap: 1rem; +} + +#search { + display: flex; + flex-direction: row; box-sizing: border-box; - overflow: hidden; - transition: max-height 0.5s; + justify-content: center; + align-items: center; + border-bottom: 2px var(--primary-color) solid; + padding-bottom: 0.1rem; } -#search-container > * { - margin-bottom: 1rem; +#search-action-pre { order: 0; } +#search-bar { order: 1; } +#search-action-post { order: 2; } + +#search-bar { + --icon-size: 2rem; + height: var(--icon-size); + width: 15rem; + color: var(--primary-color); + background-color: var(--secondary-color); + border: none; + outline: none; + text-align: left; + padding: 0.3rem 0.1rem; + font-size: 1rem; + margin-inline: 0.5rem; } -.collapsed { - max-height: 0 !important; +#search:has(#search-bar:focus) { + border-bottom-color: lightgreen; } -#search-container.collapsed > * { - display: none !important; +#search:has(#search-bar.search-no-results) { + border-bottom-color: red; } -#search { - display: flex; - justify-content: center; - align-items: baseline; - margin-inline: 1rem; +#search-bar:focus:placeholder-shown::placeholder { + opacity: 0; } -#search > div { - width: 50%; +div.search-actions { + display: flex; + flex-direction: row; + align-items: center; + position: relative; } -#search-bar { - height: 1.25rem; - color: var(--primary-color); - background-color: var(--secondary-color); - border: 1px var(--primary-color) solid; - text-align: right; - padding: 0.1rem 0.25rem; +div.search-actions > .feather-icon { + position: absolute; } -#search-bar.search-no-results { - color: red; +div.search-actions, div.search-actions > .feather-icon { + /* To prevent layout shifts */ + height: 2rem; + aspect-ratio: 1; } -#search-actions > * { - display: inline-block; +div.search-actions { + --display-when-active: none; + --display-when-inactive: block; + --display-when-empty: block; + --display-when-nonempty: none; } -#search-bar:placeholder-shown + #search-actions > :is(#btn-clear-search, #btn-share-search) { - visibility: hidden; +#search-bar:is(:focus, :not(:placeholder-shown)) ~ div.search-actions { + --display-when-active: block; + --display-when-inactive: none; } -.btn-clear-search { - font-size: 1.5rem; - padding: 0.25rem 1rem; +#search-bar:not(:placeholder-shown) ~ div.search-actions { + --display-when-empty: none; + --display-when-nonempty: block; } -#sample-searches { - display: flex; - flex-direction: row; - flex-wrap: wrap; - margin-inline: 1rem; - justify-content: center; - row-gap: 0.25rem; +#btn-random-search { display: var(--display-when-empty); } +#btn-clear-search { display: var(--display-when-nonempty); } +#btn-search { display: var(--display-when-empty); } +#btn-share-search { display: var(--display-when-nonempty); } + + +#headline { + text-align: center; + flex-grow: 1; } /* --- Table --- */ @@ -664,13 +706,6 @@ footer, aside { row-gap: .5em; } -/*#details-modal*/ .themed-button { - color: var(--primary-color); - background-color: var(--secondary-color); - border-radius: 10%; - padding: var(--cell-padding-small); -} - #details-modal-qr-code-label-name { display: block; } diff --git a/output/js/util.js b/output/js/util.js index f8b4de2..4d89ad6 100644 --- a/output/js/util.js +++ b/output/js/util.js @@ -173,11 +173,11 @@ export const dom = { snackbar: () => document.getElementById("copy-snackbar"), qr_code_buttons: () => document.querySelectorAll('.td_qr_code > a'), /** @return {HTMLInputElement | null} */ - btn_toggle_search: () => document.querySelector('#btn-toggle-search'), - /** @return {HTMLInputElement | null} */ search_bar: () => document.querySelector('#search-bar'), btn_clear_search: () => document.querySelector("#btn-clear-search"), btn_share_search: () => document.querySelector("#btn-share-search"), + btn_search: () => document.querySelector("#btn-search"), + btn_random_search: () => document.querySelector("#btn-random-search"), search_container: () => document.querySelector("#search-container"), sample_searches: () => document.querySelectorAll(".sample-search") } @@ -323,3 +323,7 @@ export const element = new Proxy({}, { export const unreachable = (error = "") => { throw new Error(error || "Unreachable"); }; export const workOnMainThread = () => new Promise(resolve => setTimeout(resolve, 0)); + +export const onInteractive = (func) => { + document.addEventListener("DOMContentLoaded", func); +} diff --git a/output/main.js b/output/main.js index ad64a47..ee46090 100644 --- a/output/main.js +++ b/output/main.js @@ -17,7 +17,7 @@ import { dom, COLUMN, COLUMN_LITERAL, COMPARISON, ATTRIBUTES, columnAscendingByDefault, columnIsSortable, COLUMN_TRANSFORMATION, - element, JOIN_URL_PASTE, communityQRCodeURL, STAFF_ID_PASTE, IDENTIFIER_PASTE, DETAILS_LINK_PASTE, CLASSES, flagToLanguageAscii, RoomInfo, unreachable, workOnMainThread + element, JOIN_URL_PASTE, communityQRCodeURL, STAFF_ID_PASTE, IDENTIFIER_PASTE, DETAILS_LINK_PASTE, CLASSES, flagToLanguageAscii, RoomInfo, unreachable, workOnMainThread, onInteractive } from './js/util.js'; // Hidden communities for transparency. @@ -101,13 +101,11 @@ function reactToURLParameters() { if (hash.startsWith("q=")) { useSearchTerm(decodeURIComponent(hash.slice(2)), true); - toggleSearchBarVisibility(); return; } if (!hash.includes("+") && !document.querySelector(`#${hash}`)) { useSearchTerm(`#${hash}`, true); - toggleSearchBarVisibility(); return; } @@ -539,10 +537,9 @@ function toggleSearchBarVisibility(setShown = null) { } function addSearchInteractions() { - dom.btn_toggle_search()?.addEventListener('click', function (ev) { - location.hash="#"; - toggleSearchBarVisibility(); - }) + // Remove JS notice + dom.search_container()?.removeAttribute("title"); + dom.search_bar()?.removeAttribute("disabled"); dom.search_bar()?.addEventListener('keydown', function () { setTimeout(() => useSearchTerm(this.value), 0); @@ -552,9 +549,11 @@ function addSearchInteractions() { if (ev.key === "Enter") { this.blur(); } - if (this.value === "") { - useSearchTerm(""); - } + setTimeout(() => useSearchTerm(this.value), 0); + }) + + dom.btn_search()?.addEventListener('click', function() { + dom.search_bar()?.focus(); }) dom.btn_clear_search()?.addEventListener('click', function () { @@ -562,6 +561,26 @@ function addSearchInteractions() { dom.search_bar()?.focus(); }) + dom.btn_random_search()?.addEventListener('click', function() { + const searchBar = dom.search_bar() ?? unreachable(); + const currentSearchTerm = searchBar.value; + const randomSearches = [ + "#new", + "#we're here", + "language", + "Australia", + "#chat", + "#official", + "#privacy", + "#android", + "#crypto", + "lang:en", + "lang:zh", + ].filter(term => term != currentSearchTerm); + const randomSearch = randomSearches[~~(Math.random() * randomSearches.length)]; + useSearchTerm(randomSearch, true); + }) + dom.btn_share_search()?.addEventListener('click', function() { const searchTerm = dom.search_bar()?.value; if (!searchTerm) return; @@ -571,14 +590,6 @@ function addSearchInteractions() { newLocation.hash = hash; shareOrCopyToClipboard(newLocation.href, "Share link copied to clipboard"); }); - - Array.from(dom.sample_searches()).forEach(button => button.addEventListener('click', function() { - const targetSearch = button.getAttribute(ATTRIBUTES.SEARCH.TARGET_SEARCH); - if (targetSearch === null) { - throw new Error("Sample search was null"); - } - useSearchTerm(targetSearch, true); - })) } /** @@ -720,11 +731,15 @@ function initializeSearch() { identifier: row.getAttribute(ATTRIBUTES.ROW.IDENTIFIER) ?? unreachable() }))); } + +let lastSearchTerm = null; /** * * @param {string} rawTerm */ async function useSearchTerm(rawTerm, fillSearchBarWithTerm = false) { + if (rawTerm === lastSearchTerm) return; + lastSearchTerm = rawTerm; const searchBar = dom.search_bar(); if (searchBar === undefined || !(searchBar instanceof HTMLInputElement)) { @@ -737,7 +752,7 @@ async function useSearchTerm(rawTerm, fillSearchBarWithTerm = false) { dom.search_bar()?.classList.remove(CLASSES.SEARCH.NO_RESULTS); } else { location.hash = `q=${rawTerm}`; - const term = rawTerm.toLowerCase().replace(/lang:(\S+)/g, "").trim(); + const term = rawTerm.toLowerCase().replace(/lang:(\S+)|#$/g, "").trim(); const termTags = Array.from(rawTerm.matchAll(/#[^#\s]+/g)).map(match => match[0].slice(1).toLowerCase()); const termLanguage = rawTerm.match(/lang:(\S+)/)?.[1]; /** @@ -827,14 +842,13 @@ function sortTable(column) { // `html.js` selector for styling purposes document.documentElement.classList.add("js"); -document.addEventListener('DOMContentLoaded', () => onLoad()); +onInteractive(onLoad) -document.addEventListener('DOMContentLoaded', () => { +onInteractive(() => { document.querySelector("#banner-sample-search")?.addEventListener('click', function() { useSearchTerm(this.getAttribute('data-search-target'), true); - const changed = toggleSearchBarVisibility(true); - if (!changed) return; dom.btn_share_search()?.classList.add('highlight'); - setTimeout(() => dom.btn_share_search()?.classList.remove('highlight'), 2000); + // Do not remove class + // Animation should not retrigger }) }); diff --git a/sites/+components/communities-search.php b/sites/+components/communities-search.php index c7b653b..525dbde 100644 --- a/sites/+components/communities-search.php +++ b/sites/+components/communities-search.php @@ -1,49 +1,50 @@ - "tag-reserved", "text" => "#$tag", "search" => "#$search"); - } - foreach ($sample_searches_tags as $tag) { - $search = str_replace(" ", "-", $tag); - $sample_searches[] = array("type" => "tag", "text" => "#$tag", "search" => "#$search"); - } - foreach ($sample_searches_plain as $text) { - $sample_searches[] = array("type" => "plain", "text" => $text, "search" => $text); - } - foreach ($sample_searches_languages as $language_array) { - $sample_searches[] = array("type" => "language", "text" => $language_array[1], "search" => "lang:" . $language_array[0]); - } -?> - -