gallery/script/gallery.ts

232 lines
5.9 KiB
TypeScript
Raw Normal View History

2023-09-06 19:17:21 -04:00
type Boxes = Set<HTMLInputElement>;
let reqBoxes: Boxes;
let excBoxes: Boxes;
let allBoxes: Boxes;
let tags: Map<HTMLElement, string[]>;
let itemsByYear: Map<string, Set<HTMLElement>>;
let showSingles = false;
2024-07-07 14:08:29 -04:00
function fillSets(): [Set<string>, Set<string>] {
2023-09-06 19:17:21 -04:00
function checkedValues(boxes: Boxes) {
return new Set([...boxes].filter(b => b.checked).map(b => b.value));
2023-09-06 19:17:21 -04:00
}
return [checkedValues(reqBoxes), checkedValues(excBoxes)];
}
function updateItems() {
2024-07-07 14:44:05 -04:00
const [reqTags, excTags] = fillSets();
const anyReq = reqTags.size > 0;
2023-09-06 19:17:21 -04:00
2024-07-07 14:44:05 -04:00
for (const [year, items] of itemsByYear) {
2024-07-07 14:45:02 -04:00
let hideMarker = true;
2023-09-06 19:17:21 -04:00
2024-07-07 14:44:05 -04:00
for (const item of items) {
const req = tags.get(item)?.some(x => reqTags.has(x)) ?? false;
const exc = tags.get(item)?.some(x => excTags.has(x)) ?? false;
const hidden = exc || (anyReq && !req);
2023-09-06 19:17:21 -04:00
item.hidden = hidden;
2024-07-07 14:45:02 -04:00
hideMarker &&= hidden;
2023-09-06 19:17:21 -04:00
}
2024-07-07 14:44:05 -04:00
const marker = document.getElementById(`marker-${year}`);
2024-07-07 14:45:02 -04:00
if (marker !== null) marker.hidden = hideMarker;
2023-09-06 19:17:21 -04:00
}
2024-07-07 14:21:12 -04:00
function disp(pfx: string, tags: Iterable<string>) {
return [...tags].map(x => pfx + x).join('\u2003'); // em space
2023-09-06 19:17:21 -04:00
}
2024-07-07 14:44:05 -04:00
const plus = disp('+\u2009', reqTags); // thin space
const minus = disp('-\u2009', excTags);
2023-09-06 19:17:21 -04:00
document.getElementById('filters-details')!.dataset.filters =
`${plus}\u2003${minus}`.trim();
}
function update() {
updateItems();
history.pushState(null, "", makeFragment());
}
function converseId(id: string) {
if (id.match(/^require/)) {
return id.replace('require', 'exclude');
} else {
return id.replace('exclude', 'require');
}
}
function toggle(checkbox: HTMLInputElement) {
if (checkbox.checked) {
2024-07-07 14:44:05 -04:00
const converse = document.getElementById(converseId(checkbox.id)) as HTMLInputElement;
2023-09-06 19:17:21 -04:00
converse.checked = false;
}
update();
}
function clearForm() {
allBoxes.forEach(b => b.checked = b.defaultChecked);
}
function clear(e: Event) {
clearForm();
update();
e.preventDefault();
}
function toggleSingles(e: Event) {
showSingles = !showSingles;
2024-07-07 14:44:05 -04:00
const elems = Array.from(document.querySelectorAll('.filterlist li')) as HTMLElement[];
for (const li of elems) {
const countStr = li.querySelector('label')?.dataset.count;
const count = countStr ? +countStr : 0;
2023-09-06 19:17:21 -04:00
if (count <= 1 && li instanceof HTMLElement) {
li.hidden = !showSingles;
}
}
2024-07-07 15:40:24 -04:00
e.preventDefault();
2023-09-06 19:17:21 -04:00
}
function makeFragment() {
2024-07-07 14:44:05 -04:00
const allBoxesArr = Array.from(allBoxes);
const ids = allBoxesArr.filter(b => b.checked).map(b => b.id);
2023-09-06 19:17:21 -04:00
if (ids.length == 0) {
return '#all';
} else if (allBoxesArr.every(b => b.checked == b.defaultChecked)) {
return '#';
} else {
return '#' + ids.join(';');
}
}
type Shortcuts = { [short: string]: Set<string> };
const shortcuts: Shortcuts = {
'summary': new Set('require_artsummary'),
'colourexamples': new Set('require_colourexample'),
'flatexamples': new Set('require_flatexample'),
'sketchexamples': new Set('require_sketchexample'),
'iconexamples': new Set('require_iconexample'),
'curated': new Set('require_curated')
};
2023-09-06 19:17:21 -04:00
function useFragment() {
2024-07-07 14:44:05 -04:00
const frag = decodeURIComponent(location.hash).replace(/^#/, '');
const details = document.getElementById('filters-details') as HTMLDetailsElement;
const fromShortcut = shortcuts[frag];
2023-09-06 19:17:21 -04:00
if (!frag) {
clearForm();
} else if (frag == 'all') {
allBoxes.forEach(b => b.checked = false);
details.open = false;
} else if (fromShortcut) {
allBoxes.forEach(b => b.checked = fromShortcut.has(b.id));
2023-09-06 19:17:21 -04:00
} else {
const pieces = frag.split(';');
2024-07-07 16:05:08 -04:00
const set = new Set(pieces);
2024-07-07 14:44:05 -04:00
const re = /^(require|exclude)_|hide_filters/;
2024-07-07 16:05:08 -04:00
if (pieces.every(x => re.test(x))) {
2023-09-06 19:17:21 -04:00
allBoxes.forEach(b => b.checked = set.has(b.id));
}
}
updateItems();
}
function sortFilters(cmp: (a: Node, b: Node) => number) {
function sort1(id: string) {
2024-07-07 14:44:05 -04:00
const elt = document.getElementById(id);
2023-09-06 19:17:21 -04:00
if (elt === null) return;
2024-07-07 14:44:05 -04:00
const children = Array.from(elt.childNodes);
2023-09-06 19:17:21 -04:00
children.sort(cmp);
2024-07-07 14:44:05 -04:00
for (const c of children) {
2023-09-06 19:17:21 -04:00
elt.removeChild(c);
elt.appendChild(c);
}
}
sort1('require');
sort1('exclude');
}
function sortFiltersAlpha(e: Event) {
2024-07-07 15:41:25 -04:00
function getName(node: Node): string {
if (node instanceof Element) {
return node.getElementsByTagName('input')[0]?.value ?? '';
2023-09-06 19:17:21 -04:00
} else {
return '';
}
}
sortFilters((a, b) => getName(a).localeCompare(getName(b)));
e.preventDefault();
}
function sortFiltersUses(e: Event) {
function getUses(node: Node): number {
2024-07-07 15:41:25 -04:00
if (node instanceof Element) {
const countStr = node.getElementsByTagName('label')[0]?.dataset.count;
2023-09-06 19:17:21 -04:00
return countStr ? +countStr : 0;
} else {
return 0;
}
}
sortFilters((a, b) => getUses(b) - getUses(a));
e.preventDefault();
}
function setup() {
2024-07-07 14:19:24 -04:00
function inputs(id: string): Boxes {
2024-07-07 14:44:05 -04:00
const iter = document.getElementById(id)!.getElementsByTagName('input');
2023-09-06 19:17:21 -04:00
return new Set(Array.from(iter));
}
2024-07-07 14:44:05 -04:00
const items = Array.from(document.getElementsByClassName('post')) as HTMLElement[];
2023-09-06 19:17:21 -04:00
itemsByYear = new Map;
2024-07-07 14:44:05 -04:00
for (const item of items) {
const year = item.dataset.year;
2023-09-06 19:17:21 -04:00
if (year !== undefined) {
if (!itemsByYear.has(year)) {
itemsByYear.set(year, new Set([item]));
} else {
itemsByYear.get(year)?.add(item);
}
}
}
reqBoxes = inputs('require');
excBoxes = inputs('exclude');
allBoxes = new Set([...reqBoxes, ...excBoxes]);
tags = new Map(items.map(item => [item, item.dataset.tags?.split(';') ?? []]));
allBoxes.forEach(b => b.addEventListener('change', () => toggle(b)));
function addClick(id: string, f: (e: Event) => void) {
document.getElementById(id)!.addEventListener('click', f);
}
addClick('clear', clear);
addClick('sortalpha', sortFiltersAlpha);
addClick('sortuses', sortFiltersUses);
addClick('singles', toggleSingles);
window.addEventListener('popstate', useFragment);
useFragment();
}
window.addEventListener('DOMContentLoaded', setup);
export {};