96 lines
5.0 KiB
Plaintext
96 lines
5.0 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.
|
|
|
|
Three 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.
|
|
an anchor a link to GEN#v3_16 should open with the picker reading
|
|
3:16, and the fragment never reaches the worker: a browser
|
|
asks for /GEN and keeps the rest. So a picker that arrived
|
|
with the page asks once, on load, if there is an anchor to
|
|
ask about.
|
|
|
|
None of them carries the book or the anchor. The request does, in
|
|
HX-Current-URL, which htmx sets from location.href -- the address the page
|
|
is actually showing, fragment and all. Baking either into these attributes
|
|
is the one thing here that can go stale, because the picker outlives the
|
|
page it was rendered for.
|
|
|
|
settled is what stops the load trigger looping: the answer to it is a
|
|
picker already standing in the right place, and it says so by leaving the
|
|
trigger out of the copy it sends back.
|
|
|
|
!!location.hash, and not location.hash, because htmx keeps an event only
|
|
when the filter returns boolean true exactly -- it tests the result with
|
|
!== true -- and a hash is a string. Written without the coercion the
|
|
trigger is dead: every link carrying an anchor lands with the picker still
|
|
reading 1:1, which is where the worker rendered it, because a browser
|
|
keeps the fragment to itself and the request that would correct it is
|
|
never sent. #}
|
|
<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="{% if reference and not settled %}load[!!location.hash], {% endif %}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.
|
|
|
|
Which is why both selects post under the same name. ch3 and v3_16 are
|
|
told apart by their shape, so a second name saying which select it came
|
|
from would only be repeating what the value already says.
|
|
|
|
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="at"
|
|
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="at"
|
|
hx-get="api/reference" hx-target="this" hx-swap="innerHTML">
|
|
{% include "verses.njk" %}
|
|
</select>
|
|
{%- endif %}
|
|
</div>
|