Files
mono/events/sx/handlers/container-nav.sx
giles 278ae3e8f6 Make SxExpr a str subclass, sx_call/render functions return SxExpr
SxExpr is now a str subclass so it works everywhere a plain string
does (join, isinstance, f-strings) while serialize() still emits it
unquoted. sx_call() and all internal render functions (_render_to_sx,
async_eval_to_sx, etc.) return SxExpr, eliminating the "forgot to
wrap" bug class that caused the sx_content leak and list serialization
bugs.

- Phase 0: SxExpr(str) with .source property, __add__/__radd__
- Phase 1: sx_call returns SxExpr (drop-in, all 200+ sites unchanged)
- Phase 2: async_eval_to_sx, async_eval_slot_to_sx, _render_to_sx,
  mobile_menu_sx return SxExpr; remove isinstance(str) workaround
- Phase 3: Remove ~150 redundant SxExpr() wrappings across 45 files
- Phase 4: serialize() docstring, handler return docs, ;; returns: sx

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 21:47:00 +00:00

83 lines
3.6 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
;; Events container-nav fragment handler
;; returns: sx
;;
;; Renders calendar entry nav items + calendar link nav items
;; for the scrollable navigation panel on blog post pages.
;;
;; Params (from request.args):
;; container_type — "page" (default)
;; container_id — int
;; post_slug — string
;; paginate_url — base URL for infinite scroll
;; page — current page (default 1)
;; exclude — comma-separated exclusion prefixes
;; current_calendar — currently selected calendar slug
(defhandler container-nav
(&key container_type container_id post_slug paginate_url page exclude current_calendar)
(let ((ct (or container_type "page"))
(cid (parse-int (or container_id "0")))
(slug (or post_slug ""))
(purl (or paginate_url ""))
(pg (parse-int (or page "1")))
(excl-raw (or exclude ""))
(cur-cal (or current_calendar ""))
(excludes (filter (fn (e) (not (empty? e)))
(map trim (split excl-raw ","))))
(has-cal-excl (not (empty? (filter (fn (e) (starts-with? e "calendar"))
excludes))))
(styles (or (jinja-global "styles") (dict)))
(nav-class (or (get styles "nav_button") ""))
(sel-colours (or (jinja-global "select_colours") "")))
;; Only render if no calendar-* exclusion
(when (not has-cal-excl)
(let ((result (service "calendar" "associated-entries"
:content-type ct :content-id cid :page pg))
(entries (first result))
(has-more (nth result 1))
(calendars (service "calendar" "calendars-for-container"
:container-type ct :container-id cid)))
(<>
;; Calendar entry nav items
(map (fn (entry)
(let ((entry-path (str "/" slug "/"
(get entry "calendar_slug") "/"
(get entry "start_at_year") "/"
(get entry "start_at_month") "/"
(get entry "start_at_day")
"/entries/" (get entry "id") "/"))
(date-str (str (format-date (get entry "start_at") "%b %d, %Y at %H:%M")
(if (get entry "end_at")
(str " " (format-date (get entry "end_at") "%H:%M"))
""))))
(~calendar-entry-nav
:href (app-url "events" entry-path)
:name (get entry "name")
:date-str date-str
:nav-class nav-class))) entries)
;; Infinite scroll sentinel
(when (and has-more (not (empty? purl)))
(~htmx-sentinel
:id (str "entries-load-sentinel-" pg)
:hx-get (str purl "?page=" (+ pg 1))
:hx-trigger "intersect once"
:hx-swap "beforebegin"
:class "flex-shrink-0 w-1"))
;; Calendar link nav items
(map (fn (cal)
(let ((href (app-url "events" (str "/" slug "/" (get cal "slug") "/")))
(is-selected (if (not (empty? cur-cal))
(= (get cal "slug") cur-cal)
false)))
(~calendar-link-nav
:href href
:name (get cal "name")
:nav-class nav-class
:is-selected is-selected
:select-colours sel-colours))) calendars))))))