Replace container nav/card widgets with fragment composition (Phase 4)

Templates now consume container_nav_html and card_widgets_html from
fragment fetches instead of iterating widgets.container_nav / container_cards.
OOB nav template renders entry/calendar links directly from data.
Calendar and market widget registrations removed (account widgets remain).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
giles
2026-02-24 13:32:48 +00:00
parent ab674ada31
commit d2e07e047e
7 changed files with 127 additions and 95 deletions

View File

@@ -69,10 +69,11 @@
{% endif %}
</a>
{# Widget-driven card decorations #}
{% for w in widgets.container_cards %}
{% include w.template with context %}
{% endfor %}
{# Card decorations — via fragments #}
{% if card_widgets_html %}
{% set _card_html = card_widgets_html.get(post.id|string, "") %}
{% if _card_html %}{{ _card_html | safe }}{% endif %}
{% endif %}
{% include '_types/blog/_card/at_bar.html' %}

View File

@@ -25,13 +25,9 @@
{% endcall %}
</div>
{# Container nav widgets (market links, etc.) #}
{% if container_nav_widgets %}
{% for wdata in container_nav_widgets %}
{% with ctx=wdata.ctx %}
{% include wdata.widget.template with context %}
{% endwith %}
{% endfor %}
{# Container nav (market links, etc.) — via fragments #}
{% if container_nav_html %}
{{ container_nav_html | safe }}
{% endif %}
{# Admin link #}

View File

@@ -22,12 +22,9 @@
{% endcall %}
</div>
{% if container_nav_widgets %}
{% for wdata in container_nav_widgets %}
{% with ctx=wdata.ctx %}
{% include wdata.widget.template with context %}
{% endwith %}
{% endfor %}
{# Container nav (market links, etc.) — via fragments #}
{% if container_nav_html %}
{{ container_nav_html | safe }}
{% endif %}
{# Admin link #}

View File

@@ -1,8 +1,42 @@
{% import 'macros/links.html' as links %}
{# Widget-driven container nav — entries, calendars, markets #}
{% if container_nav_widgets %}
{# Container nav — entries, calendars, markets (via fragments) #}
{% if container_nav_html %}
<div class="flex flex-col sm:flex-row sm:items-center gap-2 border-r border-stone-200 mr-2 sm:max-w-2xl"
id="entries-calendars-nav-wrapper">
{% include '_types/post/admin/_nav_entries.html' %}
{# Left scroll arrow - desktop only #}
<button
class="entries-nav-arrow hidden flex-shrink-0 p-2 hover:bg-stone-200 rounded"
aria-label="Scroll left"
_="on click
set #associated-items-container.scrollLeft to #associated-items-container.scrollLeft - 200">
<i class="fa fa-chevron-left"></i>
</button>
<div id="associated-items-container"
class="overflow-y-auto sm:overflow-x-auto sm:overflow-y-visible scrollbar-hide max-h-[50vh] sm:max-h-none"
style="scroll-behavior: smooth;"
_="on load or scroll
if window.innerWidth >= 640 and my.scrollWidth > my.clientWidth
remove .hidden from .entries-nav-arrow
add .flex to .entries-nav-arrow
else
add .hidden to .entries-nav-arrow
remove .flex from .entries-nav-arrow
end">
<div class="flex flex-col sm:flex-row gap-1">
{{ container_nav_html | safe }}
</div>
</div>
<style>
.scrollbar-hide::-webkit-scrollbar { display: none; }
.scrollbar-hide { -ms-overflow-style: none; scrollbar-width: none; }
</style>
{# Right scroll arrow - desktop only #}
<button
class="entries-nav-arrow hidden flex-shrink-0 p-2 hover:bg-stone-200 rounded"
aria-label="Scroll right"
_="on click
set #associated-items-container.scrollLeft to #associated-items-container.scrollLeft + 200">
<i class="fa fa-chevron-right"></i>
</button>
</div>
{% endif %}

View File

@@ -6,7 +6,73 @@
<div class="flex flex-col sm:flex-row sm:items-center gap-2 border-r border-stone-200 mr-2 sm:max-w-2xl"
id="entries-calendars-nav-wrapper"
hx-swap-oob="true">
{% include '_types/post/admin/_nav_entries.html' %}
{# Left scroll arrow - desktop only #}
<button
class="entries-nav-arrow hidden flex-shrink-0 p-2 hover:bg-stone-200 rounded"
aria-label="Scroll left"
_="on click
set #associated-items-container.scrollLeft to #associated-items-container.scrollLeft - 200">
<i class="fa fa-chevron-left"></i>
</button>
<div id="associated-items-container"
class="overflow-y-auto sm:overflow-x-auto sm:overflow-y-visible scrollbar-hide max-h-[50vh] sm:max-h-none"
style="scroll-behavior: smooth;"
_="on load or scroll
if window.innerWidth >= 640 and my.scrollWidth > my.clientWidth
remove .hidden from .entries-nav-arrow
add .flex to .entries-nav-arrow
else
add .hidden to .entries-nav-arrow
remove .flex from .entries-nav-arrow
end">
<div class="flex flex-col sm:flex-row gap-1">
{# Calendar entries #}
{% if associated_entries and associated_entries.entries %}
{% for entry in associated_entries.entries %}
{% set _entry_path = '/' + post.slug + '/calendars/' + entry.calendar_slug + '/' + entry.start_at.year|string + '/' + entry.start_at.month|string + '/' + entry.start_at.day|string + '/entries/' + entry.id|string + '/' %}
<a
href="{{ events_url(_entry_path) }}"
class="{{styles.nav_button_less_pad}}">
<div class="w-8 h-8 rounded bg-stone-200 flex-shrink-0"></div>
<div class="flex-1 min-w-0">
<div class="font-medium truncate">{{ entry.name }}</div>
<div class="text-xs text-stone-600 truncate">
{{ entry.start_at.strftime('%b %d, %Y at %H:%M') }}
{% if entry.end_at %} {{ entry.end_at.strftime('%H:%M') }}{% endif %}
</div>
</div>
</a>
{% endfor %}
{% endif %}
{# Calendar links #}
{% if calendars %}
{% for calendar in calendars %}
{% set local_href=events_url('/' + post.slug + '/calendars/' + calendar.slug + '/') %}
<a
href="{{ local_href }}"
class="{{styles.nav_button_less_pad}}">
<i class="fa fa-calendar" aria-hidden="true"></i>
<div>{{calendar.name}}</div>
</a>
{% endfor %}
{% endif %}
</div>
</div>
<style>
.scrollbar-hide::-webkit-scrollbar { display: none; }
.scrollbar-hide { -ms-overflow-style: none; scrollbar-width: none; }
</style>
{# Right scroll arrow - desktop only #}
<button
class="entries-nav-arrow hidden flex-shrink-0 p-2 hover:bg-stone-200 rounded"
aria-label="Scroll right"
_="on click
set #associated-items-container.scrollLeft to #associated-items-container.scrollLeft + 200">
<i class="fa fa-chevron-right"></i>
</button>
</div>
{% else %}
{# Empty placeholder to remove nav items when all are disassociated/deleted #}