Files
bible/assets/templates/reference.njk
T
2026-09-07 08:05:45 -04:00

75 lines
3.8 KiB
Plaintext

{# The navbar's reference picker: which book you are in, and where in it to go.
Context: reference (or null), oob.
reference is built by sw.js from bible/verses.json, which compile_bible.py
writes from the same tree it renders the books from -- so an option can
never name an anchor that is not in the page it lands on.
It used to be built in the browser, by app.js, reading the chapters and
verses off the book already on screen. That was free, and it only worked
where there was a document to read: this shell is composed in a service
worker, which has no DOM, before any document exists. What the picker needs
is a fact on disk now, and everything below is markup and htmx.
Outside the collapse deliberately -- it is the main way of moving around a
book now that the chapter list is gone from the page, and a chapter picker
behind a hamburger is no use on a phone.
d-none for the empty state rather than the hidden attribute: Bootstrap
declares .d-flex after Reboot's [hidden] rule, both !important and at the
same specificity, so the utility would win.
Two things bring it back into step with the page, because the navbar is
never the swap target -- every boosted link lands in #content:
a navigation sw.js appends this template to the fragment with oob set,
the same way a bookmark write carries the modal's status
line, so one response moves both.
a restore htmx swaps the history element straight in and processes
no out-of-band elements at all, so the picker asks for
itself instead. worker-ready is in the same trigger for
the empty state below.
Neither carries the book: the request does, in HX-Current-URL, which is the
address the page is actually showing. An address baked into this attribute
is the one thing here that can go stale, because the picker outlives the
page it was rendered for. #}
<div class="reference navbar-text {% if reference %}d-flex{% else %}d-none{% endif %} align-items-center gap-1 ms-3"
id="reference"
{%- if oob %} hx-swap-oob="true"{% endif %}
hx-get="api/reference"
hx-trigger="worker-ready from:body, htmx:historyRestore from:body"
hx-target="this" hx-swap="outerHTML">
{%- if reference %}
<span class="reference-book text-light fw-semibold"
id="reference-book">{{ reference.book }}</span>
<label class="visually-hidden" for="reference-chapter">Chapter</label>
{# The value is the id of the element to land on, so nothing downstream has
to know how a verse anchor is spelled -- including the worker, which
reads the chapter back out of the id it sent.
hx-target is spelled out on both selects because the one on the div above
is "this", and an inherited "this" would resolve to the select. #}
<select class="reference-chapter form-select form-select-sm"
id="reference-chapter" name="chapter"
hx-get="api/reference" hx-target="#reference-verse"
hx-swap="innerHTML">
{%- for chapter in reference.chapters %}
<option value="{{ chapter.id }}"
{%- if chapter.id == reference.chapter %} selected{% endif %}>{{ chapter.label }}</option>
{%- endfor %}
</select>
<span class="reference-sep text-light">:</span>
<label class="visually-hidden" for="reference-verse">Verse</label>
{# Answers with its own options again rather than nothing, so the response
has something to hang the scroll on -- and so the option the reader chose
comes back marked selected instead of being reset by the swap. #}
<select class="reference-verse form-select form-select-sm"
id="reference-verse" name="verse"
hx-get="api/reference" hx-target="this" hx-swap="innerHTML">
{% include "verses.njk" %}
</select>
{%- endif %}
</div>