Send all responses as sexp wire format with client-side rendering
- Server sends sexp source text, client (sexp.js) renders everything - SexpExpr marker class for nested sexp composition in serialize() - sexp_page() HTML shell with data-mount="body" for full page loads - sexp_response() returns text/sexp for OOB/partial responses - ~app-body layout component replaces ~app-layout (no raw!) - ~rich-text is the only component using raw! (for CMS HTML content) - Fragment endpoints return text/sexp, auto-wrapped in SexpExpr - All _*_html() helpers converted to _*_sexp() returning sexp source - Head auto-hoist: sexp.js moves meta/title/link/script[ld+json] from rendered body to document.head automatically - Unknown components render warning box instead of crashing page - Component kwargs preserve AST for lazy rendering (fixes <> in kwargs) - Fix unterminated paren in events/sexp/tickets.sexpr Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
;; Events page-level components (slots, ticket types, buy form, cart, posts nav)
|
||||
|
||||
(defcomp ~events-slot-days-pills (&key days-inner-html)
|
||||
(div :class "flex flex-wrap gap-1" (raw! days-inner-html)))
|
||||
(defcomp ~events-slot-days-pills (&key days-inner)
|
||||
(div :class "flex flex-wrap gap-1" days-inner))
|
||||
|
||||
(defcomp ~events-slot-day-pill (&key day)
|
||||
(span :class "px-2 py-0.5 rounded-full text-xs bg-slate-200" day))
|
||||
@@ -9,11 +9,11 @@
|
||||
(defcomp ~events-slot-no-days ()
|
||||
(span :class "text-xs text-slate-400" "No days"))
|
||||
|
||||
(defcomp ~events-slot-panel (&key slot-id list-container days-html flexible time-str cost-str pre-action edit-url)
|
||||
(defcomp ~events-slot-panel (&key slot-id list-container days flexible time-str cost-str pre-action edit-url)
|
||||
(section :id (str "slot-" slot-id) :class list-container
|
||||
(div :class "flex flex-col"
|
||||
(div :class "text-xs font-semibold uppercase tracking-wide text-stone-500" "Days")
|
||||
(div :class "mt-1" (raw! days-html)))
|
||||
(div :class "mt-1" days))
|
||||
(div :class "flex flex-col"
|
||||
(div :class "text-xs font-semibold uppercase tracking-wide text-stone-500" "Flexible")
|
||||
(div :class "mt-1" flexible))
|
||||
@@ -24,11 +24,11 @@
|
||||
(div :class "flex flex-col"
|
||||
(div :class "text-xs font-semibold uppercase tracking-wide text-stone-500" "Cost")
|
||||
(div :class "mt-1" cost-str)))
|
||||
(button :type "button" :class pre-action :hx-get edit-url
|
||||
:hx-target (str "#slot-" slot-id) :hx-swap "outerHTML" "Edit")))
|
||||
(button :type "button" :class pre-action :sx-get edit-url
|
||||
:sx-target (str "#slot-" slot-id) :sx-swap "outerHTML" "Edit")))
|
||||
|
||||
(defcomp ~events-slot-description-oob (&key description)
|
||||
(div :id "slot-description-title" :hx-swap-oob "outerHTML"
|
||||
(div :id "slot-description-title" :sx-swap-oob "outerHTML"
|
||||
:class "text-base font-normal break-words whitespace-normal min-w-0 break-all w-full text-center block"
|
||||
description))
|
||||
|
||||
@@ -36,15 +36,15 @@
|
||||
(tr (td :colspan "5" :class "p-3 text-stone-500" "No slots yet.")))
|
||||
|
||||
(defcomp ~events-slots-row (&key tr-cls slot-href pill-cls hx-select slot-name description
|
||||
flexible days-html time-str cost-str action-btn del-url csrf-hdr)
|
||||
flexible days time-str cost-str action-btn del-url csrf-hdr)
|
||||
(tr :class tr-cls
|
||||
(td :class "p-2 align-top w-1/6"
|
||||
(div :class "font-medium"
|
||||
(a :href slot-href :class pill-cls :hx-get slot-href :hx-target "#main-panel"
|
||||
:hx-select hx-select :hx-swap "outerHTML" :hx-push-url "true" slot-name))
|
||||
(a :href slot-href :class pill-cls :sx-get slot-href :sx-target "#main-panel"
|
||||
:sx-select hx-select :sx-swap "outerHTML" :sx-push-url "true" slot-name))
|
||||
(p :class "text-stone-500 whitespace-pre-line break-all w-full" description))
|
||||
(td :class "p-2 align-top w-1/6" flexible)
|
||||
(td :class "p-2 align-top w-1/6" (raw! days-html))
|
||||
(td :class "p-2 align-top w-1/6" days)
|
||||
(td :class "p-2 align-top w-1/6" time-str)
|
||||
(td :class "p-2 align-top w-1/6" cost-str)
|
||||
(td :class "p-2 align-top w-1/6"
|
||||
@@ -53,11 +53,11 @@
|
||||
:data-confirm-text "This action cannot be undone."
|
||||
:data-confirm-icon "warning" :data-confirm-confirm-text "Yes, delete it"
|
||||
:data-confirm-cancel-text "Cancel" :data-confirm-event "confirmed"
|
||||
:hx-delete del-url :hx-target "#slots-table" :hx-select "#slots-table"
|
||||
:hx-swap "outerHTML" :hx-headers csrf-hdr :hx-trigger "confirmed"
|
||||
:sx-delete del-url :sx-target "#slots-table" :sx-select "#slots-table"
|
||||
:sx-swap "outerHTML" :sx-headers csrf-hdr :sx-trigger "confirmed"
|
||||
(i :class "fa-solid fa-trash")))))
|
||||
|
||||
(defcomp ~events-slots-table (&key list-container rows-html pre-action add-url)
|
||||
(defcomp ~events-slots-table (&key list-container rows pre-action add-url)
|
||||
(section :id "slots-table" :class list-container
|
||||
(table :class "w-full text-sm border table-fixed"
|
||||
(thead :class "bg-stone-100"
|
||||
@@ -67,10 +67,10 @@
|
||||
(th :class "text-left p-2 w-1/6" "Time")
|
||||
(th :class "text-left p-2 w-1/6" "Cost")
|
||||
(th :class "text-left p-2 w-1/6" "Actions")))
|
||||
(tbody (raw! rows-html)))
|
||||
(tbody rows))
|
||||
(div :id "slot-add-container" :class "mt-4"
|
||||
(button :type "button" :class pre-action
|
||||
:hx-get add-url :hx-target "#slot-add-container" :hx-swap "innerHTML"
|
||||
:sx-get add-url :sx-target "#slot-add-container" :sx-swap "innerHTML"
|
||||
"+ Add slot"))))
|
||||
|
||||
(defcomp ~events-ticket-type-col (&key label value)
|
||||
@@ -81,9 +81,9 @@
|
||||
(defcomp ~events-ticket-type-panel (&key ticket-id list-container c1 c2 c3 pre-action edit-url)
|
||||
(section :id (str "ticket-" ticket-id) :class list-container
|
||||
(div :class "grid grid-cols-1 sm:grid-cols-3 gap-4 text-sm"
|
||||
(raw! c1) (raw! c2) (raw! c3))
|
||||
(button :type "button" :class pre-action :hx-get edit-url
|
||||
:hx-target (str "#ticket-" ticket-id) :hx-swap "outerHTML" "Edit")))
|
||||
c1 c2 c3)
|
||||
(button :type "button" :class pre-action :sx-get edit-url
|
||||
:sx-target (str "#ticket-" ticket-id) :sx-swap "outerHTML" "Edit")))
|
||||
|
||||
(defcomp ~events-ticket-types-empty-row ()
|
||||
(tr (td :colspan "4" :class "p-3 text-stone-500" "No ticket types yet.")))
|
||||
@@ -93,8 +93,8 @@
|
||||
(tr :class tr-cls
|
||||
(td :class "p-2 align-top w-1/3"
|
||||
(div :class "font-medium"
|
||||
(a :href tt-href :class pill-cls :hx-get tt-href :hx-target "#main-panel"
|
||||
:hx-select hx-select :hx-swap "outerHTML" :hx-push-url "true" tt-name)))
|
||||
(a :href tt-href :class pill-cls :sx-get tt-href :sx-target "#main-panel"
|
||||
:sx-select hx-select :sx-swap "outerHTML" :sx-push-url "true" tt-name)))
|
||||
(td :class "p-2 align-top w-1/4" cost-str)
|
||||
(td :class "p-2 align-top w-1/4" count)
|
||||
(td :class "p-2 align-top w-1/6"
|
||||
@@ -103,11 +103,11 @@
|
||||
:data-confirm-text "This action cannot be undone."
|
||||
:data-confirm-icon "warning" :data-confirm-confirm-text "Yes, delete it"
|
||||
:data-confirm-cancel-text "Cancel" :data-confirm-event "confirmed"
|
||||
:hx-delete del-url :hx-target "#tickets-table" :hx-select "#tickets-table"
|
||||
:hx-swap "outerHTML" :hx-headers csrf-hdr :hx-trigger "confirmed"
|
||||
:sx-delete del-url :sx-target "#tickets-table" :sx-select "#tickets-table"
|
||||
:sx-swap "outerHTML" :sx-headers csrf-hdr :sx-trigger "confirmed"
|
||||
(i :class "fa-solid fa-trash")))))
|
||||
|
||||
(defcomp ~events-ticket-types-table (&key list-container rows-html action-btn add-url)
|
||||
(defcomp ~events-ticket-types-table (&key list-container rows action-btn add-url)
|
||||
(section :id "tickets-table" :class list-container
|
||||
(table :class "w-full text-sm border table-fixed"
|
||||
(thead :class "bg-stone-100"
|
||||
@@ -115,16 +115,16 @@
|
||||
(th :class "text-left p-2 w-1/4" "Cost")
|
||||
(th :class "text-left p-2 w-1/4" "Count")
|
||||
(th :class "text-left p-2 w-1/6" "Actions")))
|
||||
(tbody (raw! rows-html)))
|
||||
(tbody rows))
|
||||
(div :id "ticket-add-container" :class "mt-4"
|
||||
(button :class action-btn :hx-get add-url :hx-target "#ticket-add-container" :hx-swap "innerHTML"
|
||||
(button :class action-btn :sx-get add-url :sx-target "#ticket-add-container" :sx-swap "innerHTML"
|
||||
(i :class "fa fa-plus") " Add ticket type"))))
|
||||
|
||||
(defcomp ~events-ticket-config-display (&key price-str count-str show-js)
|
||||
(div :class "space-y-2"
|
||||
(div :class "flex items-center gap-2"
|
||||
(span :class "text-sm font-medium text-stone-700" "Price:")
|
||||
(span :class "font-medium text-green-600" (raw! price-str)))
|
||||
(span :class "font-medium text-green-600" price-str))
|
||||
(div :class "flex items-center gap-2"
|
||||
(span :class "text-sm font-medium text-stone-700" "Available:")
|
||||
(span :class "font-medium text-blue-600" count-str))
|
||||
@@ -139,10 +139,10 @@
|
||||
|
||||
(defcomp ~events-ticket-config-form (&key entry-id hidden-cls update-url csrf price-val count-val hide-js)
|
||||
(form :id (str "ticket-form-" entry-id) :class (str hidden-cls " space-y-3 mt-2 p-3 border rounded bg-stone-50")
|
||||
:hx-post update-url :hx-target (str "#entry-tickets-" entry-id) :hx-swap "innerHTML"
|
||||
:sx-post update-url :sx-target (str "#entry-tickets-" entry-id) :sx-swap "innerHTML"
|
||||
(input :type "hidden" :name "csrf_token" :value csrf)
|
||||
(div (label :for (str "ticket-price-" entry-id) :class "block text-sm font-medium text-stone-700 mb-1"
|
||||
(raw! "Ticket Price (£)"))
|
||||
"Ticket Price (£)")
|
||||
(input :type "number" :id (str "ticket-price-" entry-id) :name "ticket_price"
|
||||
:step "0.01" :min "0" :value price-val
|
||||
:class "w-full px-3 py-2 border rounded focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
@@ -174,37 +174,37 @@
|
||||
(i :class "fa fa-shopping-cart text-[0.6rem]" :aria-hidden "true")
|
||||
(str " " count " in basket")))
|
||||
|
||||
(defcomp ~events-buy-info-bar (&key items-html)
|
||||
(div :class "flex items-center gap-3 mb-3 text-xs text-stone-500" (raw! items-html)))
|
||||
(defcomp ~events-buy-info-bar (&key items)
|
||||
(div :class "flex items-center gap-3 mb-3 text-xs text-stone-500" items))
|
||||
|
||||
(defcomp ~events-buy-type-item (&key type-name cost-str adjust-controls-html)
|
||||
(defcomp ~events-buy-type-item (&key type-name cost-str adjust-controls)
|
||||
(div :class "flex items-center justify-between p-3 rounded-lg bg-stone-50 border border-stone-100"
|
||||
(div (div :class "font-medium text-sm" type-name)
|
||||
(div :class "text-xs text-stone-500" cost-str))
|
||||
(raw! adjust-controls-html)))
|
||||
adjust-controls))
|
||||
|
||||
(defcomp ~events-buy-types-wrapper (&key items-html)
|
||||
(div :class "space-y-2" (raw! items-html)))
|
||||
(defcomp ~events-buy-types-wrapper (&key items)
|
||||
(div :class "space-y-2" items))
|
||||
|
||||
(defcomp ~events-buy-default (&key price-str adjust-controls-html)
|
||||
(defcomp ~events-buy-default (&key price-str adjust-controls)
|
||||
(<> (div :class "flex items-center justify-between mb-4"
|
||||
(div (span :class "font-medium text-green-600" price-str)
|
||||
(span :class "text-sm text-stone-500 ml-2" "per ticket")))
|
||||
(raw! adjust-controls-html)))
|
||||
adjust-controls))
|
||||
|
||||
(defcomp ~events-buy-panel (&key entry-id info-html body-html)
|
||||
(defcomp ~events-buy-panel (&key entry-id info body)
|
||||
(div :id (str "ticket-buy-" entry-id) :class "rounded-xl border border-stone-200 bg-white p-4"
|
||||
(h3 :class "text-sm font-semibold text-stone-700 mb-3"
|
||||
(i :class "fa fa-ticket mr-1" :aria-hidden "true") "Tickets")
|
||||
(raw! info-html) (raw! body-html)))
|
||||
info body))
|
||||
|
||||
(defcomp ~events-adjust-form (&key adjust-url target extra-cls csrf entry-id tt-html count-val btn-html)
|
||||
(form :hx-post adjust-url :hx-target target :hx-swap "outerHTML" :class extra-cls
|
||||
(defcomp ~events-adjust-form (&key adjust-url target extra-cls csrf entry-id tt count-val btn)
|
||||
(form :sx-post adjust-url :sx-target target :sx-swap "outerHTML" :class extra-cls
|
||||
(input :type "hidden" :name "csrf_token" :value csrf)
|
||||
(input :type "hidden" :name "entry_id" :value entry-id)
|
||||
(raw! tt-html)
|
||||
tt
|
||||
(input :type "hidden" :name "count" :value count-val)
|
||||
(raw! btn-html)))
|
||||
btn))
|
||||
|
||||
(defcomp ~events-adjust-tt-hidden (&key ticket-type-id)
|
||||
(input :type "hidden" :name "ticket_type_id" :value ticket-type-id))
|
||||
@@ -231,16 +231,16 @@
|
||||
(span :class "absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 pointer-events-none"
|
||||
(span :class "flex items-center justify-center bg-black text-white rounded-full w-4 h-4 text-xs font-bold" count)))))
|
||||
|
||||
(defcomp ~events-adjust-controls (&key minus-html cart-icon-html plus-html)
|
||||
(div :class "flex items-center gap-2" (raw! minus-html) (raw! cart-icon-html) (raw! plus-html)))
|
||||
(defcomp ~events-adjust-controls (&key minus cart-icon plus)
|
||||
(div :class "flex items-center gap-2" minus cart-icon plus))
|
||||
|
||||
(defcomp ~events-buy-result (&key entry-id count-label tickets-html remaining-html my-tickets-href)
|
||||
(defcomp ~events-buy-result (&key entry-id count-label tickets remaining my-tickets-href)
|
||||
(div :id (str "ticket-buy-" entry-id) :class "rounded-xl border border-emerald-200 bg-emerald-50 p-4"
|
||||
(div :class "flex items-center gap-2 mb-3"
|
||||
(i :class "fa fa-check-circle text-emerald-600" :aria-hidden "true")
|
||||
(span :class "font-semibold text-emerald-800" count-label))
|
||||
(div :class "space-y-2 mb-4" (raw! tickets-html))
|
||||
(raw! remaining-html)
|
||||
(div :class "space-y-2 mb-4" tickets)
|
||||
remaining
|
||||
(div :class "mt-3 flex gap-2"
|
||||
(a :href my-tickets-href :class "text-sm text-emerald-700 hover:text-emerald-900 underline"
|
||||
"View all my tickets"))))
|
||||
@@ -256,25 +256,25 @@
|
||||
(p :class "text-xs text-stone-500" text))
|
||||
|
||||
(defcomp ~events-cart-icon-logo (&key blog-href logo)
|
||||
(div :id "cart-mini" :hx-swap-oob "true"
|
||||
(div :id "cart-mini" :sx-swap-oob "true"
|
||||
(div :class "h-12 w-12 rounded-full overflow-hidden border border-stone-300 flex-shrink-0"
|
||||
(a :href blog-href :class "h-full w-full font-bold text-5xl flex-shrink-0 flex flex-row items-center gap-1"
|
||||
(img :src logo :class "h-full w-full rounded-full object-cover border border-stone-300 flex-shrink-0")))))
|
||||
|
||||
(defcomp ~events-cart-icon-badge (&key cart-href count)
|
||||
(div :id "cart-mini" :hx-swap-oob "true"
|
||||
(div :id "cart-mini" :sx-swap-oob "true"
|
||||
(a :href cart-href :class "relative inline-flex items-center justify-center text-stone-700 hover:text-emerald-700"
|
||||
(i :class "fa fa-shopping-cart text-5xl" :aria-hidden "true")
|
||||
(span :class "absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 inline-flex items-center justify-center rounded-full bg-emerald-600 text-white text-sm w-5 h-5"
|
||||
count))))
|
||||
|
||||
;; Inline ticket widget (for all-events/page-summary cards)
|
||||
(defcomp ~events-tw-form (&key ticket-url target csrf entry-id count-val btn-html)
|
||||
(form :action ticket-url :method "post" :hx-post ticket-url :hx-target target :hx-swap "outerHTML"
|
||||
(defcomp ~events-tw-form (&key ticket-url target csrf entry-id count-val btn)
|
||||
(form :action ticket-url :method "post" :sx-post ticket-url :sx-target target :sx-swap "outerHTML"
|
||||
(input :type "hidden" :name "csrf_token" :value csrf)
|
||||
(input :type "hidden" :name "entry_id" :value entry-id)
|
||||
(input :type "hidden" :name "count" :value count-val)
|
||||
(raw! btn-html)))
|
||||
btn))
|
||||
|
||||
(defcomp ~events-tw-cart-plus ()
|
||||
(button :type "submit" :class "relative inline-flex items-center justify-center text-stone-500 hover:bg-emerald-50 rounded p-1"
|
||||
@@ -293,40 +293,40 @@
|
||||
(span :class "absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 pointer-events-none"
|
||||
(span :class "flex items-center justify-center bg-black text-white rounded-full w-4 h-4 text-xs font-bold" qty)))))
|
||||
|
||||
(defcomp ~events-tw-widget (&key entry-id price-html inner-html)
|
||||
(defcomp ~events-tw-widget (&key entry-id price inner)
|
||||
(div :id (str "page-ticket-" entry-id) :class "flex items-center gap-2"
|
||||
(span :class "text-green-600 font-medium text-sm" (raw! price-html))
|
||||
(raw! inner-html)))
|
||||
(span :class "text-green-600 font-medium text-sm" price)
|
||||
inner))
|
||||
|
||||
;; Entry posts panel
|
||||
(defcomp ~events-entry-posts-panel (&key posts-html search-url entry-id)
|
||||
(defcomp ~events-entry-posts-panel (&key posts search-url entry-id)
|
||||
(div :class "space-y-2"
|
||||
(raw! posts-html)
|
||||
posts
|
||||
(div :class "mt-3 pt-3 border-t"
|
||||
(label :class "block text-xs font-medium text-stone-700 mb-1" "Add Post")
|
||||
(input :type "text" :placeholder "Search posts..."
|
||||
:class "w-full px-3 py-2 border rounded text-sm"
|
||||
:hx-get search-url :hx-trigger "keyup changed delay:300ms, load"
|
||||
:hx-target (str "#post-search-results-" entry-id) :hx-swap "innerHTML" :name "q")
|
||||
:sx-get search-url :sx-trigger "keyup changed delay:300ms, load"
|
||||
:sx-target (str "#post-search-results-" entry-id) :sx-swap "innerHTML" :name "q")
|
||||
(div :id (str "post-search-results-" entry-id) :class "mt-2 max-h-96 overflow-y-auto border rounded"))))
|
||||
|
||||
(defcomp ~events-entry-posts-list (&key items-html)
|
||||
(div :class "space-y-2" (raw! items-html)))
|
||||
(defcomp ~events-entry-posts-list (&key items)
|
||||
(div :class "space-y-2" items))
|
||||
|
||||
(defcomp ~events-entry-posts-none ()
|
||||
(p :class "text-sm text-stone-400" "No posts associated"))
|
||||
|
||||
(defcomp ~events-entry-post-item (&key img-html title del-url entry-id csrf-hdr)
|
||||
(defcomp ~events-entry-post-item (&key img title del-url entry-id csrf-hdr)
|
||||
(div :class "flex items-center justify-between gap-3 p-2 bg-stone-50 rounded border"
|
||||
(raw! img-html) (span :class "text-sm flex-1" title)
|
||||
img (span :class "text-sm flex-1" title)
|
||||
(button :type "button" :class "text-xs text-red-600 hover:text-red-800 flex-shrink-0"
|
||||
:data-confirm "true" :data-confirm-title "Remove post?"
|
||||
:data-confirm-text (str "This will remove " title " from this entry")
|
||||
:data-confirm-icon "warning" :data-confirm-confirm-text "Yes, remove it"
|
||||
:data-confirm-cancel-text "Cancel" :data-confirm-event "confirmed"
|
||||
:hx-delete del-url :hx-trigger "confirmed"
|
||||
:hx-target (str "#entry-posts-" entry-id) :hx-swap "innerHTML"
|
||||
:hx-headers csrf-hdr
|
||||
:sx-delete del-url :sx-trigger "confirmed"
|
||||
:sx-target (str "#entry-posts-" entry-id) :sx-swap "innerHTML"
|
||||
:sx-headers csrf-hdr
|
||||
(i :class "fa fa-times") " Remove")))
|
||||
|
||||
(defcomp ~events-post-img (&key src alt)
|
||||
@@ -337,19 +337,19 @@
|
||||
|
||||
;; Entry posts nav OOB
|
||||
(defcomp ~events-entry-posts-nav-oob-empty ()
|
||||
(div :id "entry-posts-nav-wrapper" :hx-swap-oob "true"))
|
||||
(div :id "entry-posts-nav-wrapper" :sx-swap-oob "true"))
|
||||
|
||||
(defcomp ~events-entry-posts-nav-oob (&key items-html)
|
||||
(defcomp ~events-entry-posts-nav-oob (&key items)
|
||||
(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 "entry-posts-nav-wrapper" :hx-swap-oob "true"
|
||||
(div :class "flex overflow-x-auto gap-1 scrollbar-thin" (raw! items-html))))
|
||||
:id "entry-posts-nav-wrapper" :sx-swap-oob "true"
|
||||
(div :class "flex overflow-x-auto gap-1 scrollbar-thin" items)))
|
||||
|
||||
(defcomp ~events-entry-nav-post (&key href nav-btn img-html title)
|
||||
(a :href href :class nav-btn (raw! img-html) (div :class "flex-1 min-w-0" (div :class "font-medium truncate" title))))
|
||||
(defcomp ~events-entry-nav-post (&key href nav-btn img title)
|
||||
(a :href href :class nav-btn img (div :class "flex-1 min-w-0" (div :class "font-medium truncate" title))))
|
||||
|
||||
;; Post nav entries OOB
|
||||
(defcomp ~events-post-nav-oob-empty ()
|
||||
(div :id "entries-calendars-nav-wrapper" :hx-swap-oob "true"))
|
||||
(div :id "entries-calendars-nav-wrapper" :sx-swap-oob "true"))
|
||||
|
||||
(defcomp ~events-post-nav-entry (&key href nav-btn name time-str)
|
||||
(a :href href :class nav-btn
|
||||
@@ -363,9 +363,9 @@
|
||||
(i :class "fa fa-calendar" :aria-hidden "true")
|
||||
(div name)))
|
||||
|
||||
(defcomp ~events-post-nav-wrapper (&key items-html hyperscript)
|
||||
(defcomp ~events-post-nav-wrapper (&key items hyperscript)
|
||||
(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"
|
||||
:id "entries-calendars-nav-wrapper" :sx-swap-oob "true"
|
||||
(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"
|
||||
@@ -373,7 +373,7 @@
|
||||
(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;" :_ hyperscript
|
||||
(div :class "flex flex-col sm:flex-row gap-1" (raw! items-html)))
|
||||
(div :class "flex flex-col sm:flex-row gap-1" items))
|
||||
(style ".scrollbar-hide::-webkit-scrollbar { display: none; } .scrollbar-hide { -ms-overflow-style: none; scrollbar-width: none; }")
|
||||
(button :class "entries-nav-arrow hidden flex-shrink-0 p-2 hover:bg-stone-200 rounded"
|
||||
:aria-label "Scroll right"
|
||||
@@ -381,6 +381,6 @@
|
||||
(i :class "fa fa-chevron-right"))))
|
||||
|
||||
;; Entry nav post link (with image)
|
||||
(defcomp ~events-entry-nav-post-link (&key href img-html title)
|
||||
(defcomp ~events-entry-nav-post-link (&key href img title)
|
||||
(a :href href :class "flex items-center gap-2 px-3 py-2 hover:bg-stone-100 rounded transition text-sm border sm:whitespace-nowrap sm:flex-shrink-0"
|
||||
(raw! img-html) (div :class "flex-1 min-w-0" (div :class "font-medium truncate" title))))
|
||||
img (div :class "flex-1 min-w-0" (div :class "font-medium truncate" title))))
|
||||
|
||||
Reference in New Issue
Block a user