rework front page to have shuffle and lazy infinite scroll
This commit is contained in:
parent
9729f45842
commit
b75bad995a
|
@ -315,6 +315,7 @@ def generate(args):
|
||||||
list_template = jenv.get_template("list.html")
|
list_template = jenv.get_template("list.html")
|
||||||
categorization_template = jenv.get_template("categorization.html")
|
categorization_template = jenv.get_template("categorization.html")
|
||||||
work_template = jenv.get_template("work.html")
|
work_template = jenv.get_template("work.html")
|
||||||
|
index_template = jenv.get_template("index.html")
|
||||||
|
|
||||||
con = sqlite3.connect(args.destdir / 'meta.db')
|
con = sqlite3.connect(args.destdir / 'meta.db')
|
||||||
cur = con.cursor()
|
cur = con.cursor()
|
||||||
|
@ -408,7 +409,7 @@ def generate(args):
|
||||||
copy_recursive(r / 'static', args.destdir / 'site' / 'static')
|
copy_recursive(r / 'static', args.destdir / 'site' / 'static')
|
||||||
|
|
||||||
with open(args.destdir / 'site' / 'index.html', 'w') as f:
|
with open(args.destdir / 'site' / 'index.html', 'w') as f:
|
||||||
f.write(list_template.render(depth=0, works=works))
|
f.write(index_template.render(depth=0, works=works))
|
||||||
|
|
||||||
con.close()
|
con.close()
|
||||||
|
|
||||||
|
|
|
@ -5,9 +5,56 @@ body {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* index stuff */
|
||||||
|
|
||||||
|
#top {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: end;
|
||||||
|
gap: 40px;
|
||||||
|
margin-bottom: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#top .nav {
|
||||||
|
margin-bottom: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#top-padding, #controls {
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-basis: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media all and (max-width: 600px) {
|
||||||
|
#top {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0;
|
||||||
|
}
|
||||||
|
#top .nav {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#controls button {
|
||||||
|
position: relative;
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
margin: 5px 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#controls button img {
|
||||||
|
height: 40px;
|
||||||
|
width: 40px;
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
margin: -20px 0 0 -20px;
|
||||||
|
}
|
||||||
|
|
||||||
/* listing stuff */
|
/* listing stuff */
|
||||||
|
|
||||||
#title, nav {
|
#title, .nav {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +67,7 @@ body {
|
||||||
margin-bottom: 25px;
|
margin-bottom: 25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-listing {
|
#card-listing {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
12
dlibrary/static/icons/shuffle.svg
Normal file
12
dlibrary/static/icons/shuffle.svg
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='180' height='180' viewBox='-40 -40 180 180'>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="crossing">
|
||||||
|
<path d='M -30 40 L -10 40 C 65 40 25 140 100 140 L 120 140 L -30 140 Z'/>
|
||||||
|
<path d='M -20 -40 L -10 -40 C 65 -40 25 60 100 60 L 130 60 L 130 -40 Z'/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
<path d='M -20 0 L -10 0 C 65 0 25 100 100 100 L 120 100' fill='none' stroke='#ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10' stroke-width='10'/>
|
||||||
|
<path d='M -20 100 L -10 100 C 65 100 25 0 100 0 L 120 0' fill='none' clip-path='url(#crossing)' stroke='#ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10' stroke-width='10'/>
|
||||||
|
<path d='M 105 85 L 120 100 L 105 115' fill='none' stroke='#ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10' stroke-width='10'/>
|
||||||
|
<path d='M 105 -15 L 120 0 L 105 15' fill='none' stroke='#ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10' stroke-width='10'/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
6
dlibrary/static/icons/sort.svg
Normal file
6
dlibrary/static/icons/sort.svg
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='180' height='180' viewBox='-20 -20 180 180'>
|
||||||
|
<path d='M 30 0 L 30 140' fill='none' stroke='#ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10' stroke-width='10'/>
|
||||||
|
<path d='M 0 110 L 30 140 L 60 110' fill='none' stroke='#ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10' stroke-width='10'/>
|
||||||
|
<path d='M 110 0 L 110 140' fill='none' stroke='#ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10' stroke-width='10'/>
|
||||||
|
<path d='M 80 30 L 110 0 L 140 30' fill='none' stroke='#ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10' stroke-width='10'/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 765 B |
117
dlibrary/static/index.js
Normal file
117
dlibrary/static/index.js
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
const LCG_M = Math.pow(2, 32);
|
||||||
|
const LCG_A = 0xd9f5;
|
||||||
|
const LCG_C = 69;
|
||||||
|
|
||||||
|
function lcg(seed) {
|
||||||
|
let value = seed % LCG_M;
|
||||||
|
|
||||||
|
return (n) => {
|
||||||
|
value = (LCG_A * value + LCG_C) % LCG_M;
|
||||||
|
return Math.floor(n * value / LCG_M);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function seedableShuffledCopy(list, seed) {
|
||||||
|
const gen = lcg(seed);
|
||||||
|
const l = [...list];
|
||||||
|
|
||||||
|
for (let i = 0; i < l.length - 1; ++i) {
|
||||||
|
j = i + gen(l.length - i);
|
||||||
|
const tmp = l[i];
|
||||||
|
l[i] = l[j];
|
||||||
|
l[j] = tmp;
|
||||||
|
}
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
function newSeed() {
|
||||||
|
return Math.floor(Math.random() * LCG_M);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const shuffleButton = document.getElementById('shuffle');
|
||||||
|
const sortButton = document.getElementById('sort');
|
||||||
|
const listContainer = document.getElementById('card-listing');
|
||||||
|
let ordering = localStorage.getItem('indexOrdering') || 'dateDesc';
|
||||||
|
|
||||||
|
let orderedWorks;
|
||||||
|
|
||||||
|
function scrollHandler() {
|
||||||
|
while (orderedWorks.length > 0 && listContainer.clientHeight - window.scrollY < 5000) {
|
||||||
|
const work = orderedWorks.shift();
|
||||||
|
|
||||||
|
const card = document.createElement('div');
|
||||||
|
card.className = 'card';
|
||||||
|
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = `${ROOT}/works/${work.id}/${INDEX}`;
|
||||||
|
card.appendChild(link);
|
||||||
|
|
||||||
|
const thumb = document.createElement('img');
|
||||||
|
thumb.src = `${ROOT}/thumbnails/${work.id}.jpg`;
|
||||||
|
link.appendChild(thumb);
|
||||||
|
|
||||||
|
const creators = document.createElement('div');
|
||||||
|
creators.className = 'card-creators';
|
||||||
|
let creatorsInfo = `[${work.circle || ''}`;
|
||||||
|
if (work.authors) {
|
||||||
|
let authorList = work.authors[0];
|
||||||
|
for (let i = 1; i < work.authors.length; ++i) {
|
||||||
|
authorList += `, ${work.authors[i]}`;
|
||||||
|
}
|
||||||
|
creatorsInfo += (work.circle ? ` (${authorList})]` : `${authorList}]`);
|
||||||
|
}
|
||||||
|
creators.textContent = creatorsInfo;
|
||||||
|
link.appendChild(creators);
|
||||||
|
|
||||||
|
const title = document.createElement('div');
|
||||||
|
title.className = 'card-title';
|
||||||
|
title.textContent = work.title;
|
||||||
|
link.appendChild(title);
|
||||||
|
|
||||||
|
listContainer.appendChild(card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyOrdering() {
|
||||||
|
listContainer.replaceChildren();
|
||||||
|
scrollHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (ordering) {
|
||||||
|
case 'shuffle':
|
||||||
|
let seed = parseInt(localStorage.getItem('shuffleSeed')) || newSeed();
|
||||||
|
orderedWorks = seedableShuffledCopy(WORKS, seed);
|
||||||
|
break;
|
||||||
|
case 'dateAsc':
|
||||||
|
orderedWorks = WORKS.toReversed();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
orderedWorks = [...WORKS];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
applyOrdering();
|
||||||
|
|
||||||
|
window.addEventListener('scroll', scrollHandler);
|
||||||
|
|
||||||
|
document.getElementById('shuffle').onclick = () => {
|
||||||
|
let seed = newSeed();
|
||||||
|
localStorage.setItem('shuffleSeed', seed);
|
||||||
|
ordering = 'shuffle';
|
||||||
|
localStorage.setItem('indexOrdering', ordering);
|
||||||
|
|
||||||
|
orderedWorks = seedableShuffledCopy(WORKS, seed);
|
||||||
|
applyOrdering();
|
||||||
|
};
|
||||||
|
document.getElementById('sort').onclick = () => {
|
||||||
|
if (ordering === 'dateDesc') {
|
||||||
|
ordering = 'dateAsc';
|
||||||
|
orderedWorks = WORKS.toReversed();
|
||||||
|
} else {
|
||||||
|
ordering = 'dateDesc';
|
||||||
|
orderedWorks = [...WORKS];
|
||||||
|
}
|
||||||
|
localStorage.setItem('indexOrdering', ordering);
|
||||||
|
applyOrdering();
|
||||||
|
};
|
||||||
|
});
|
|
@ -4,7 +4,7 @@
|
||||||
{% from 'utils.html' import urlcat, index, root with context %}
|
{% from 'utils.html' import urlcat, index, root with context %}
|
||||||
<h1 id="title"><a href="{{ root() }}/{{ index() }}">DLibrary</a> > {{ categorization.capitalize() }}</h1>
|
<h1 id="title"><a href="{{ root() }}/{{ index() }}">DLibrary</a> > {{ categorization.capitalize() }}</h1>
|
||||||
{% include 'nav.html' %}
|
{% include 'nav.html' %}
|
||||||
<div class="card-listing">
|
<div id="card-listing">
|
||||||
{% for cat in categories %}
|
{% for cat in categories %}
|
||||||
<div class="card {% if not work_style_cards %}category{% endif %}">
|
<div class="card {% if not work_style_cards %}category{% endif %}">
|
||||||
<a href="{{ urlcat(cat) }}/{{ index() }}">
|
<a href="{{ urlcat(cat) }}/{{ index() }}">
|
||||||
|
|
25
dlibrary/templates/index.html
Normal file
25
dlibrary/templates/index.html
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% from 'utils.html' import index, root with context %}
|
||||||
|
{% block head %}
|
||||||
|
<script>
|
||||||
|
const ROOT = "{{ root() }}";
|
||||||
|
const INDEX = "{{ index() }}";
|
||||||
|
const WORKS = {{ works | tojson }};
|
||||||
|
</script>
|
||||||
|
<script src="{{ root() }}/static/index.js"></script>
|
||||||
|
{% endblock %}
|
||||||
|
{% block body %}
|
||||||
|
<div id="top">
|
||||||
|
<div id="top-padding"></div>
|
||||||
|
<div id="header">
|
||||||
|
<h1 id="title"><a href="{{ root() }}/{{ index() }}">DLibrary</a></h1>
|
||||||
|
{% include 'nav.html' %}
|
||||||
|
</div>
|
||||||
|
<div id="controls">
|
||||||
|
<button id="shuffle" name="Shuffle"><img src="{{ root() }}/static/icons/shuffle.svg"/></button>
|
||||||
|
<button id="sort" name="Sort"><img src="{{ root() }}/static/icons/sort.svg"/></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="card-listing">
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -3,12 +3,12 @@
|
||||||
{% from 'utils.html' import index, root with context %}
|
{% from 'utils.html' import index, root with context %}
|
||||||
<h1 id="title"><a href="{{ root() }}/{{ index() }}">DLibrary</a>{% if categorization %} > <a href="{{ root() }}/{{ categorization }}/{{ index() }}">{{ categorization.capitalize() }}</a>{% endif %}{% if title %} > {{ title }}{% endif %}</h1>
|
<h1 id="title"><a href="{{ root() }}/{{ index() }}">DLibrary</a>{% if categorization %} > <a href="{{ root() }}/{{ categorization }}/{{ index() }}">{{ categorization.capitalize() }}</a>{% endif %}{% if title %} > {{ title }}{% endif %}</h1>
|
||||||
{% include 'nav.html' %}
|
{% include 'nav.html' %}
|
||||||
<div class="card-listing">
|
<div id="card-listing">
|
||||||
{% for work in works %}
|
{% for work in works %}
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<a href="{{ root() }}/works/{{ work['id'] }}/{{ index() }}">
|
<a href="{{ root() }}/works/{{ work['id'] }}/{{ index() }}">
|
||||||
<img src="{{ root() }}/thumbnails/{{ work['id'] }}.jpg">
|
<img src="{{ root() }}/thumbnails/{{ work['id'] }}.jpg">
|
||||||
<div class="card-authors">
|
<div class="card-creators">
|
||||||
[{% if work['circle'] %}{{ work['circle'] }}{% endif %}{% if work['circle'] and work['authors'] %} ({% endif %}{{ ', '.join(work['authors']) }}{% if work['circle'] and work['authors'] %}){% endif %}]
|
[{% if work['circle'] %}{{ work['circle'] }}{% endif %}{% if work['circle'] and work['authors'] %} ({% endif %}{{ ', '.join(work['authors']) }}{% if work['circle'] and work['authors'] %}){% endif %}]
|
||||||
</div>
|
</div>
|
||||||
<div class="card-title">
|
<div class="card-title">
|
||||||
|
|
Loading…
Reference in a new issue