Revamp header, add icons, improve search

dev
gravel 7 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 {
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;

@ -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; }

@ -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 {

@ -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;
}

@ -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);
}

@ -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
})
});

@ -1,49 +1,50 @@
<?php
$sample_searches_tags_reserved = ["new", "we're here"];
$sample_searches_plain = ["language", "Australia"];
$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-container"
title="Search requires JavaScript"
>
<div id="search">
<?php
// Phantom element for alignnment
?>
<div></div>
<input id="search-bar" autocomplete="off" type="text" placeholder="Search for Communities">
<div id="search-actions">
<span id="btn-clear-search" class="btn-clear-search anchorstyle clickable enter-clicks" tabindex="0" title="Clear search">×</span>
<span id="btn-share-search" class="anchorstyle clickable enter-clicks" tabindex="0">Share</span>
<input
id="search-bar"
disabled
autocomplete="off"
type="text"
placeholder="Search for Communities"
tabindex="0"
>
<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 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>

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

@ -72,45 +72,13 @@
<body>
<input type="checkbox" id="toggle-theme-switch">
<div id="theming-root">
<header>
<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>
<?php include "+components/index-header.php" ?>
<h1 id="headline">List of Session Communities</h1>
<?php include "+components/issue-banner.php" ?>
<?php include "+components/communities-search.php" ?>
<?php include "+components/communities-search.php"; ?>
<?php include "+components/qr-modals.php" ?>

Loading…
Cancel
Save