Move market composition from Python to .sx defcomps (Phase 3)

Python sxc/pages/ functions no longer build nested sx_call chains or
reference leaf component names. Instead they extract data (URLs, prices,
CSRF, cart state) and call a single top-level composition defcomp with
pure data values. The .sx defcomps handle all component-to-component
wiring, iteration (map), and conditional rendering.

New .sx composition defcomps:
- headers.sx: ~market-header-from-data, ~market-desktop-nav-from-data,
  ~market-product-header-from-data, ~market-product-admin-header-from-data
- prices.sx: ~market-prices-header-from-data, ~market-card-price-from-data
- navigation.sx: ~market-mobile-nav-from-data
- cards.sx: ~market-product-cards-content, ~market-card-from-data,
  ~market-cards-content, ~market-landing-from-data
- detail.sx: ~market-product-detail-from-data, ~market-detail-gallery-from-data,
  ~market-detail-info-from-data
- meta.sx: ~market-product-meta-from-data
- filters.sx: ~market-desktop-filter-from-data, ~market-mobile-chips-from-data,
  ~market-mobile-filter-content-from-data, plus 6 sub-composition defcomps

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-05 01:11:57 +00:00
parent 36a0bd8577
commit e81d77437e
12 changed files with 961 additions and 781 deletions

View File

@@ -95,3 +95,63 @@
(if desc-content desc-content (when desc desc)))
(if badge-content badge-content (when badge badge))))
;; ---------------------------------------------------------------------------
;; Composition defcomps — receive data lists, compose component trees
;; ---------------------------------------------------------------------------
;; Product cards grid with infinite scroll sentinels
(defcomp ~market-product-cards-content (&key products page total-pages next-url
mobile-sentinel-hs desktop-sentinel-hs)
(<>
(map (lambda (p)
(~market-product-card
:href (get p "href") :hx-select (get p "hx-select") :slug (get p "slug")
:image (get p "image") :brand (get p "brand") :brand-highlight (get p "brand-highlight")
:special-price (get p "special-price") :regular-price (get p "regular-price")
:cart-action (get p "cart-action") :quantity (get p "quantity")
:cart-href (get p "cart-href") :csrf (get p "csrf")
:title (get p "title") :has-like (get p "has-like")
:liked (get p "liked") :like-action (get p "like-action")
:labels (get p "labels") :stickers (get p "stickers")
:has-highlight (get p "has-highlight")
:search-pre (get p "search-pre") :search-mid (get p "search-mid")
:search-post (get p "search-post")))
products)
(if (< page total-pages)
(<> (~sentinel-mobile :id (str "sentinel-" page "-m") :next-url next-url
:hyperscript mobile-sentinel-hs)
(~sentinel-desktop :id (str "sentinel-" page "-d") :next-url next-url
:hyperscript desktop-sentinel-hs))
(~end-of-results))))
;; Single market card from data (handles conditional title/desc/badge)
(defcomp ~market-card-from-data (&key name description href show-badge badge-href badge-title)
(~market-market-card
:title-content (if href
(~market-market-card-title-link :href href :name name)
(~market-market-card-title :name name))
:desc-content (when description
(~market-market-card-desc :description description))
:badge-content (when (and show-badge badge-title)
(~market-market-card-badge :href badge-href :title badge-title))))
;; Market cards list with infinite scroll sentinel
(defcomp ~market-cards-content (&key markets page has-more next-url)
(<>
(map (lambda (m)
(~market-card-from-data
:name (get m "name") :description (get m "description")
:href (get m "href") :show-badge (get m "show-badge")
:badge-href (get m "badge-href") :badge-title (get m "badge-title")))
markets)
(when has-more
(~sentinel-simple :id (str "sentinel-" page) :next-url next-url))))
;; Market landing page content from data
(defcomp ~market-landing-from-data (&key excerpt feature-image html)
(~market-landing-content :inner
(<> (when excerpt (~market-landing-excerpt :text excerpt))
(when feature-image (~market-landing-image :src feature-image))
(when html (~market-landing-html :html html)))))