Revamp header, add icons, improve search

dev
gravel 4 months ago
parent 62dffe293f
commit 6ce6744bca
Signed by: gravel
GPG Key ID: C0538F3C906B308F

3
.gitmodules vendored

@ -0,0 +1,3 @@
[submodule "feather"]
path = feather
url = https://github.com/feathericons/feather

@ -0,0 +1 @@
../../feather/icons

@ -1,6 +1,8 @@
.banner { .banner {
position: relative; 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; border-left: var(--primary-color) 1rem solid;
padding-inline: 1rem; padding-inline: 1rem;
text-align: center; text-align: center;

@ -18,4 +18,20 @@
--color-https: hsl(195, 53%, 79%); --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; } :root { text-underline-offset: 0.2em; }

@ -32,8 +32,8 @@ footer:first-of-type {
white-space: nowrap; white-space: nowrap;
} }
#more-sites-info-button { footer#author {
font-size: 1.25em; align-self: stretch;
} }
#footer__author { #footer__author {

@ -83,6 +83,7 @@ body {
margin: 0; margin: 0;
background-color: var(--secondary-color); background-color: var(--secondary-color);
color: var(--primary-color); color: var(--primary-color);
align-items: center;
} }
a, .anchorstyle { a, .anchorstyle {
@ -94,20 +95,28 @@ a, .anchorstyle {
cursor: pointer; cursor: pointer;
} }
/* Enumerate elements with transitions to improve performance on Chromium. */ .feather-icon {
/* width: var(--icon-size);
#theming-root, th, tr, .protocol-indicator, .copy_button, height: var(--icon-size);
.td_description::after, transition: opacity 0.2s;
#details-modal, #details-modal .themed-button, }
a, .anchorstyle {
transition: color 2.5s linear, background-color 2s linear; .feather-icon.clickable:not(:hover),
:is(a, .clickable ):not(:hover) > .feather-icon {
opacity: 0.6;
} }
*/
html.js .noscript, .hidden { html.js .noscript, .hidden {
display: none; display: none;
} }
.themed-button {
color: var(--primary-color);
background-color: var(--secondary-color);
border-radius: 10%;
padding: var(--cell-padding-small);
}
.badge { .badge {
display: inline-block; display: inline-block;
border-radius: 1em; border-radius: 1em;
@ -120,13 +129,14 @@ html.js .noscript, .hidden {
} }
.highlight { .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 { @keyframes highlight {
0% { } 0% { }
50% { scale: 2; filter: brightness(125%); } 50% { translate: 0 -0.5rem; }
100% { } 100% { }
} }
.tag, .sample-search { .tag, .sample-search {
@ -168,11 +178,18 @@ gap {
header { header {
display: flex; display: flex;
direction: row; align-self: stretch;
flex-direction: row;
/* Push items as far apart as possible */ /* Push items as far apart as possible */
justify-content: space-between; justify-content: space-between;
align-items: flex-start;
padding-top: var(--body-margin); padding-top: var(--body-margin);
padding-inline: 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 { #header-start, #header-end {
@ -180,77 +197,102 @@ header {
gap: 0.5em; gap: 0.5em;
} }
#headline { html:not(.js) :is(#search-container, #search-container *) {
text-align: center; cursor: not-allowed !important;
flex-grow: 1;
} }
#search-container { #search-container {
display: flex; display: flex;
flex-direction: column; flex-direction: row;
max-height: 10rem; justify-items: space-between;
justify-content: center; align-items: stretch;
margin-block-end: 1rem;
column-gap: 1rem;
}
#search {
display: flex;
flex-direction: row;
box-sizing: border-box; box-sizing: border-box;
overflow: hidden; justify-content: center;
transition: max-height 0.5s; align-items: center;
border-bottom: 2px var(--primary-color) solid;
padding-bottom: 0.1rem;
} }
#search-container > * { #search-action-pre { order: 0; }
margin-bottom: 1rem; #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 { #search:has(#search-bar:focus) {
max-height: 0 !important; border-bottom-color: lightgreen;
} }
#search-container.collapsed > * { #search:has(#search-bar.search-no-results) {
display: none !important; border-bottom-color: red;
} }
#search { #search-bar:focus:placeholder-shown::placeholder {
display: flex; opacity: 0;
justify-content: center;
align-items: baseline;
margin-inline: 1rem;
} }
#search > div { div.search-actions {
width: 50%; display: flex;
flex-direction: row;
align-items: center;
position: relative;
} }
#search-bar { div.search-actions > .feather-icon {
height: 1.25rem; position: absolute;
color: var(--primary-color);
background-color: var(--secondary-color);
border: 1px var(--primary-color) solid;
text-align: right;
padding: 0.1rem 0.25rem;
} }
#search-bar.search-no-results { div.search-actions, div.search-actions > .feather-icon {
color: red; /* To prevent layout shifts */
height: 2rem;
aspect-ratio: 1;
} }
#search-actions > * { div.search-actions {
display: inline-block; --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) { #search-bar:is(:focus, :not(:placeholder-shown)) ~ div.search-actions {
visibility: hidden; --display-when-active: block;
--display-when-inactive: none;
} }
.btn-clear-search { #search-bar:not(:placeholder-shown) ~ div.search-actions {
font-size: 1.5rem; --display-when-empty: none;
padding: 0.25rem 1rem; --display-when-nonempty: block;
} }
#sample-searches { #btn-random-search { display: var(--display-when-empty); }
display: flex; #btn-clear-search { display: var(--display-when-nonempty); }
flex-direction: row; #btn-search { display: var(--display-when-empty); }
flex-wrap: wrap; #btn-share-search { display: var(--display-when-nonempty); }
margin-inline: 1rem;
justify-content: center;
row-gap: 0.25rem; #headline {
text-align: center;
flex-grow: 1;
} }
/* --- Table --- */ /* --- Table --- */
@ -664,13 +706,6 @@ footer, aside {
row-gap: .5em; 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 { #details-modal-qr-code-label-name {
display: block; display: block;
} }

@ -173,11 +173,11 @@ export const dom = {
snackbar: () => document.getElementById("copy-snackbar"), snackbar: () => document.getElementById("copy-snackbar"),
qr_code_buttons: () => document.querySelectorAll('.td_qr_code > a'), qr_code_buttons: () => document.querySelectorAll('.td_qr_code > a'),
/** @return {HTMLInputElement | null} */ /** @return {HTMLInputElement | null} */
btn_toggle_search: () => document.querySelector('#btn-toggle-search'),
/** @return {HTMLInputElement | null} */
search_bar: () => document.querySelector('#search-bar'), search_bar: () => document.querySelector('#search-bar'),
btn_clear_search: () => document.querySelector("#btn-clear-search"), btn_clear_search: () => document.querySelector("#btn-clear-search"),
btn_share_search: () => document.querySelector("#btn-share-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"), search_container: () => document.querySelector("#search-container"),
sample_searches: () => document.querySelectorAll(".sample-search") 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 unreachable = (error = "") => { throw new Error(error || "Unreachable"); };
export const workOnMainThread = () => new Promise(resolve => setTimeout(resolve, 0)); export const workOnMainThread = () => new Promise(resolve => setTimeout(resolve, 0));
export const onInteractive = (func) => {
document.addEventListener("DOMContentLoaded", func);
}

@ -17,7 +17,7 @@
import { import {
dom, COLUMN, COLUMN_LITERAL, COMPARISON, ATTRIBUTES, dom, COLUMN, COLUMN_LITERAL, COMPARISON, ATTRIBUTES,
columnAscendingByDefault, columnIsSortable, COLUMN_TRANSFORMATION, 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'; } from './js/util.js';
// Hidden communities for transparency. // Hidden communities for transparency.
@ -101,13 +101,11 @@ function reactToURLParameters() {
if (hash.startsWith("q=")) { if (hash.startsWith("q=")) {
useSearchTerm(decodeURIComponent(hash.slice(2)), true); useSearchTerm(decodeURIComponent(hash.slice(2)), true);
toggleSearchBarVisibility();
return; return;
} }
if (!hash.includes("+") && !document.querySelector(`#${hash}`)) { if (!hash.includes("+") && !document.querySelector(`#${hash}`)) {
useSearchTerm(`#${hash}`, true); useSearchTerm(`#${hash}`, true);
toggleSearchBarVisibility();
return; return;
} }
@ -539,10 +537,9 @@ function toggleSearchBarVisibility(setShown = null) {
} }
function addSearchInteractions() { function addSearchInteractions() {
dom.btn_toggle_search()?.addEventListener('click', function (ev) { // Remove JS notice
location.hash="#"; dom.search_container()?.removeAttribute("title");
toggleSearchBarVisibility(); dom.search_bar()?.removeAttribute("disabled");
})
dom.search_bar()?.addEventListener('keydown', function () { dom.search_bar()?.addEventListener('keydown', function () {
setTimeout(() => useSearchTerm(this.value), 0); setTimeout(() => useSearchTerm(this.value), 0);
@ -552,9 +549,11 @@ function addSearchInteractions() {
if (ev.key === "Enter") { if (ev.key === "Enter") {
this.blur(); this.blur();
} }
if (this.value === "") { setTimeout(() => useSearchTerm(this.value), 0);
useSearchTerm(""); })
}
dom.btn_search()?.addEventListener('click', function() {
dom.search_bar()?.focus();
}) })
dom.btn_clear_search()?.addEventListener('click', function () { dom.btn_clear_search()?.addEventListener('click', function () {
@ -562,6 +561,26 @@ function addSearchInteractions() {
dom.search_bar()?.focus(); 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() { dom.btn_share_search()?.addEventListener('click', function() {
const searchTerm = dom.search_bar()?.value; const searchTerm = dom.search_bar()?.value;
if (!searchTerm) return; if (!searchTerm) return;
@ -571,14 +590,6 @@ function addSearchInteractions() {
newLocation.hash = hash; newLocation.hash = hash;
shareOrCopyToClipboard(newLocation.href, "Share link copied to clipboard"); 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() identifier: row.getAttribute(ATTRIBUTES.ROW.IDENTIFIER) ?? unreachable()
}))); })));
} }
let lastSearchTerm = null;
/** /**
* *
* @param {string} rawTerm * @param {string} rawTerm
*/ */
async function useSearchTerm(rawTerm, fillSearchBarWithTerm = false) { async function useSearchTerm(rawTerm, fillSearchBarWithTerm = false) {
if (rawTerm === lastSearchTerm) return;
lastSearchTerm = rawTerm;
const searchBar = dom.search_bar(); const searchBar = dom.search_bar();
if (searchBar === undefined || !(searchBar instanceof HTMLInputElement)) { 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); dom.search_bar()?.classList.remove(CLASSES.SEARCH.NO_RESULTS);
} else { } else {
location.hash = `q=${rawTerm}`; 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 termTags = Array.from(rawTerm.matchAll(/#[^#\s]+/g)).map(match => match[0].slice(1).toLowerCase());
const termLanguage = rawTerm.match(/lang:(\S+)/)?.[1]; const termLanguage = rawTerm.match(/lang:(\S+)/)?.[1];
/** /**
@ -827,14 +842,13 @@ function sortTable(column) {
// `html.js` selector for styling purposes // `html.js` selector for styling purposes
document.documentElement.classList.add("js"); document.documentElement.classList.add("js");
document.addEventListener('DOMContentLoaded', () => onLoad()); onInteractive(onLoad)
document.addEventListener('DOMContentLoaded', () => { onInteractive(() => {
document.querySelector("#banner-sample-search")?.addEventListener('click', function() { document.querySelector("#banner-sample-search")?.addEventListener('click', function() {
useSearchTerm(this.getAttribute('data-search-target'), true); useSearchTerm(this.getAttribute('data-search-target'), true);
const changed = toggleSearchBarVisibility(true);
if (!changed) return;
dom.btn_share_search()?.classList.add('highlight'); dom.btn_share_search()?.classList.add('highlight');
setTimeout(() => dom.btn_share_search()?.classList.remove('highlight'), 2000); // Do not remove class
// Animation should not retrigger
}) })
}); });

@ -1,49 +1,50 @@
<?php <div
$sample_searches_tags_reserved = ["new", "we're here"]; id="search-container"
$sample_searches_plain = ["language", "Australia"]; title="Search requires JavaScript"
$sample_searches_tags = ["chat", "official", "privacy", "android", "crypto"]; >
$sample_searches_languages = [["en", "🇬🇧"], ["zh", "🇨🇳"]];
$sample_searches = [];
foreach ($sample_searches_tags_reserved as $tag) {
$search = str_replace(" ", "-", $tag);
$sample_searches[] = array("type" => "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]);
}
?>
<div id="search-container" class="collapsed">
<div id="search"> <div id="search">
<?php <input
// Phantom element for alignnment id="search-bar"
?> disabled
<div></div> autocomplete="off"
<input id="search-bar" autocomplete="off" type="text" placeholder="Search for Communities"> type="text"
<div id="search-actions"> placeholder="Search for Communities"
<span id="btn-clear-search" class="btn-clear-search anchorstyle clickable enter-clicks" tabindex="0" title="Clear search">×</span> tabindex="0"
<span id="btn-share-search" class="anchorstyle clickable enter-clicks" tabindex="0">Share</span> >
<div class="search-actions" id="search-action-pre">
<img
id="btn-clear-search"
class="feather-icon clickable enter-clicks"
src="/assets/icons/x.svg"
alt="x"
title="Clear search"
tabindex="0"
>
<img
id="btn-random-search"
class="feather-icon clickable enter-clicks"
src="/assets/icons/hash.svg"
alt="⚂"
title="Try a random search"
tabindex="0"
>
</div>
<div class="search-actions" id="search-action-post">
<img
id="btn-search"
class="feather-icon search-action-inactive clickable"
src="/assets/icons/search.svg"
alt="🔍"
title="Search"
>
<img
id="btn-share-search"
class="feather-icon search-action-active clickable enter-clicks"
src="/assets/icons/share-2.svg"
alt="Share"
title="Share search"
tabindex="0"
>
</div> </div>
</div>
<div id="sample-searches">
<?php foreach ($sample_searches as $search): ?>
<span
class="badge clickable enter-clicks sample-search sample-search-<?=$search['type']?>"
title="Try searching for <?=$search['search']?>"
data-search="<?=$search['search']?>"
tabindex=0
><?=
$search['text']
?></span>
<?php endforeach; ?>
</div> </div>
</div> </div>

@ -96,7 +96,11 @@ if (basename($_SERVER['SCRIPT_FILENAME']) == "index.php"):
class="clickable enter-clicks footer__nav-target" class="clickable enter-clicks footer__nav-target"
title="Read more about sites hosting additional groups." title="Read more about sites hosting additional groups."
tabindex="0" tabindex="0"
>()</span><?php endif; ?> ><img
class="feather-icon"
src="/assets/icons/info.svg"
alt="()"
></span><?php endif; ?>
</nav> </nav>
<nav id="about-session"> <nav id="about-session">
<a <a

@ -0,0 +1,31 @@
<header>
<div id="header-start">
<a
class="icon-container"
target="_blank"
rel="help"
title="Multi-language guide to using the site."
href="/instructions/"
>Instructions</a>
<a
href="#footer"
title="About"
>Jump to footer</a>
</div>
<div id="header-end">
<label
for="toggle-theme-switch"
class="icon-container anchorstyle clickable enter-clicks"
title="Switch color theme"
tabindex="0"
><img
class="feather-icon dark-theme-only"
src="/assets/icons/moon.svg"
alt="🌜"
><img
class="feather-icon light-theme-only"
src="/assets/icons/sun.svg"
alt="☀️"
></label>
</div>
</header>

@ -6,7 +6,7 @@
<p <p
class="banner-subtitle js-only" class="banner-subtitle js-only"
>Show someone the way by clicking 'Share' from <a id="banner-sample-search" href="#" data-search-target="#privacy">your favorite search</a>!</p> >Show someone the way by clicking 'Share' from <a id="banner-sample-search" href="#q=#privacy" data-search-target="#privacy">your favorite search</a>!</p>
<noscript><p <noscript><p
class="banner-subtitle" class="banner-subtitle"

@ -72,45 +72,13 @@
<body> <body>
<input type="checkbox" id="toggle-theme-switch"> <input type="checkbox" id="toggle-theme-switch">
<div id="theming-root"> <div id="theming-root">
<header> <?php include "+components/index-header.php" ?>
<div id="header-start"></div>
<div id="header-end">
<a
id="link-about"
href="#footer"
title="See more links to resources about sessioncommunities.online."
>About</a>
<span
id="btn-toggle-search"
class="anchorstyle clickable enter-clicks js-only"
title="Open search bar."
tabindex="0"
>Search</span>
<a
id="link-more-sites"
href="#more-sites"
title="Find other Session groups."
>More groups</a>
<label
for="toggle-theme-switch"
class="anchorstyle clickable enter-clicks"
title="Switch color theme"
tabindex="0"
>Switch theme</label>
<a
id="link-instructions"
target="_blank"
rel="help"
title="Multi-language guide to using the site."
href="/instructions/"
>Instructions</a>
</div>
</header>
<h1 id="headline">List of Session Communities</h1> <h1 id="headline">List of Session Communities</h1>
<?php include "+components/issue-banner.php" ?> <?php include "+components/issue-banner.php" ?>
<?php include "+components/communities-search.php" ?> <?php include "+components/communities-search.php"; ?>
<?php include "+components/qr-modals.php" ?> <?php include "+components/qr-modals.php" ?>

Loading…
Cancel
Save