Liking cljdoc? Tell your friends :D

dev.zeko.stube.render

Hiccup → HTML rendering and the small DSL for Datastar attributes.

Two responsibilities live here, deliberately kept apart from the kernel:

  1. Serialise hiccup to HTML with Chassis. The kernel works with hiccup data structures all the way through; they are only stringified at the very edge, just before patch-elements! writes to the wire. This keeps everything before the wire pure, diff-able, and REPL-inspectable.

  2. Generate Datastar attribute fragmentson, bind — that tag a piece of UI with the wiring that lets the client post events back to the right conversation and instance.

The cid only exists at request time, so the helpers consult a dynamic var bound by the http layer for the duration of a render.

Hiccup → HTML rendering and the small DSL for Datastar attributes.

Two responsibilities live here, deliberately kept apart from the kernel:

1. **Serialise hiccup to HTML** with [Chassis](https://github.com/onionpancakes/chassis).
   The kernel works with hiccup data structures all the way through;
   they are only stringified at the very edge, just before
   `patch-elements!` writes to the wire.  This keeps everything before
   the wire pure, diff-able, and REPL-inspectable.

2. **Generate Datastar attribute fragments** — `on`, `bind` — that
   tag a piece of UI with the wiring that lets the client post events
   back to the right conversation and instance.

The cid only exists at request time, so the helpers consult a dynamic
var bound by the http layer for the duration of a render.
raw docstring

*cid*clj

The conversation id of the request currently being served. Bound by the http layer around every render so attribute helpers can build URLs pointing at the right SSE endpoint.

The conversation id of the request currently being served.  Bound by
the http layer around every render so attribute helpers can build URLs
pointing at the right SSE endpoint.
sourceraw docstring

*conv*clj

The conversation being rendered, bound by the kernel for the duration of one render-frame call. render-slot consults it to look up embedded children by id.

Two-way bindings (s/bind) and event hooks (s/on) only need the cid; only slot rendering needs the conversation, hence the separate vars.

The conversation being rendered, bound by the kernel for the duration
of one `render-frame` call.  [[render-slot]] consults it to look up
embedded children by id.

Two-way bindings (`s/bind`) and event hooks (`s/on`) only need the
cid; only slot rendering needs the conversation, hence the separate
vars.
sourceraw docstring

back-buttonclj

(back-button label)
(back-button label attrs)

Return a small Hiccup button wired to the conversation-level [:back] effect.

(s/back-button "Back")

This intentionally does not take self: browser history rewind is a conversation operation, not a component-local event. For wizard-style Back buttons that answer a parent with a sentinel, keep using (s/on self :click :as ...) from that component.

Return a small Hiccup button wired to the conversation-level `[:back]`
effect.

    (s/back-button "Back")

This intentionally does not take `self`: browser history rewind is a
conversation operation, not a component-local event.  For wizard-style
Back buttons that answer a parent with a sentinel, keep using
`(s/on self :click :as ...)` from that component.
sourceraw docstring

back-urlclj

(back-url)

URL the browser POSTs to for the conversation-level Back action.

URL the browser POSTs to for the conversation-level Back action.
sourceraw docstring

bindclj

(bind signal)

Return an attribute map that two-way binds the named signal to the current element.

[:input (merge {:name "answer"} (s/bind :answer))]

Datastar's signal-defining attributes use the colon form (data-bind:foo); the dash form would not be recognised.

Datastar 1.0 camel-cases data-bind:<key> by default. Clojure code conventionally names signals with kebab-case keywords and reads the POSTed signals back by the same keyword, so force Datastar's no-op kebab case modifier to keep the wire key unchanged.

Return an attribute map that two-way binds the named signal to the
current element.

    [:input (merge {:name "answer"} (s/bind :answer))]

Datastar's signal-defining attributes use the colon form
(`data-bind:foo`); the dash form would not be recognised.

Datastar 1.0 camel-cases `data-bind:<key>` by default.  Clojure code
conventionally names signals with kebab-case keywords and reads the
POSTed signals back by the same keyword, so force Datastar's no-op
kebab case modifier to keep the wire key unchanged.
sourceraw docstring

event-urlclj

(event-url iid route-event)

URL the browser POSTs to for an event. Public so user code can build custom Datastar expressions that target the same endpoint.

route-event is either a keyword (:save) or a structured event vector ([:pick-day day]). The path always contains the logical event name; structured payloads ride in a small EDN query parameter so the server can reconstruct {:event :pick-day :payload day} without teaching Datastar about stube metadata.

URL the browser POSTs to for an event.  Public so user code can build
custom Datastar expressions that target the same endpoint.

`route-event` is either a keyword (`:save`) or a structured event
vector (`[:pick-day day]`).  The path always contains the logical
event name; structured payloads ride in a small EDN query parameter so
the server can reconstruct `{:event :pick-day :payload day}` without
teaching Datastar about stube metadata.
sourceraw docstring

htmlclj

(html tree)

Render hiccup tree to an HTML string.

Render hiccup `tree` to an HTML string.
sourceraw docstring

local-bindclj

(local-bind self signal)

Like bind, but scopes logical signal to this component instance.

:keep #{:answer}
[:input (s/local-bind self :answer)]

The browser sends :answer-<iid>; the conversation layer lifts that value back onto :answer before the handler runs.

Like [[bind]], but scopes logical `signal` to this component instance.

    :keep #{:answer}
    [:input (s/local-bind self :answer)]

The browser sends `:answer-<iid>`; the conversation layer lifts that
value back onto `:answer` before the handler runs.
sourceraw docstring

local-signalclj

See [[dev.zeko.stube.conversation/local-signal]].
sourceraw docstring

onclj

(on self dom-event)
(on self dom-event as-kw route-event)

Return an attribute map that wires a real DOM event on the surrounding element to a server-side stube event.

Two arities:

(on self :submit)            ;; DOM `submit`  → POST .../submit
(on self :click :as :inc)    ;; DOM `click`   → POST .../inc
(on self :click :as [:pick item-id])
                              ;; handler sees :event :pick,
                              ;; :payload item-id

The first form is the common case where the DOM event name and the route name happen to be the same (most often :submit on a form, :input on a text field). The second form is the right one for any click-triggered action: a button has no inc event of its own, only click, so we need to listen on click and route to inc separately.

Datastar registers listeners under the colon form (data-on:<event>); the dash form data-on-<event> is reserved for built-in pseudo-events (data-on-intersect, …) and would be silently ignored. data-on:submit automatically calls preventDefault, so forms never trigger a full-page reload.

The instance id and route event live in the URL path itself — Datastar still ships every other signal as the request body, so two-way bindings (s/bind) keep working unchanged.

Usage:

[:form   (s/on self :submit) …]
[:button (s/on self :click :as :inc) "+"]
[:button (s/on self :click :as :dec) "−"]
Return an attribute map that wires a real DOM event on the
surrounding element to a server-side stube event.

Two arities:

    (on self :submit)            ;; DOM `submit`  → POST .../submit
    (on self :click :as :inc)    ;; DOM `click`   → POST .../inc
    (on self :click :as [:pick item-id])
                                  ;; handler sees :event :pick,
                                  ;; :payload item-id

The first form is the common case where the DOM event name and the
route name happen to be the same (most often `:submit` on a form,
`:input` on a text field).  The second form is the right one for any
click-triggered action: a button has no `inc` event of its own, only
`click`, so we need to listen on `click` and route to `inc`
separately.

Datastar registers listeners under the colon form
(`data-on:<event>`); the dash form `data-on-<event>` is reserved for
built-in pseudo-events (`data-on-intersect`, …) and would be silently
ignored.  `data-on:submit` automatically calls `preventDefault`, so
forms never trigger a full-page reload.

The instance id and route event live in the URL path itself —
Datastar still ships every other signal as the request body, so
two-way bindings (`s/bind`) keep working unchanged.

Usage:

    [:form   (s/on self :submit) …]
    [:button (s/on self :click :as :inc) "+"]
    [:button (s/on self :click :as :dec) "−"]
sourceraw docstring

payload-query-paramclj

Query-string key used by event-url for structured event payloads.

Query-string key used by [[event-url]] for structured event payloads.
sourceraw docstring

render-slotclj

(render-slot self slot-key)
(render-slot self slot-key lookup!)

Inline the hiccup of an embedded child. Inside a parent's :render,

[:section (s/render-slot self :slot/header)]

expands to whatever the :ui/site-header instance currently renders, and Chassis serialises both layers in one pass. No HTML escaping is needed because we hand back hiccup, not a pre-rendered string.

The lookup arrow is:

slot-key  →  (:instance/children self)
          →  child instance id
          →  (instance *conv* child-iid)
          →  child component definition's `:render`

Throws if *conv* is unbound or the slot is unknown. Returns the default hidden placeholder when the child component has no :render of its own.

Inline the hiccup of an embedded child.  Inside a parent's `:render`,

    [:section (s/render-slot self :slot/header)]

expands to whatever the `:ui/site-header` instance currently renders,
and Chassis serialises both layers in one pass.  No HTML escaping is
needed because we hand back hiccup, not a pre-rendered string.

The lookup arrow is:

    slot-key  →  (:instance/children self)
              →  child instance id
              →  (instance *conv* child-iid)
              →  child component definition's `:render`

Throws if `*conv*` is unbound or the slot is unknown.  Returns the
default hidden placeholder when the child component has no `:render`
of its own.
sourceraw docstring

root-attrsclj

(root-attrs self & attr-maps)

Return an attribute map carrying self's instance id plus any other attribute maps merged in. Replaces the recurring boilerplate

(merge {:id (:instance/id self)} (s/on self :submit) {:class "x"})

with

(s/root-attrs self (s/on self :submit) {:class "x"})

The id has to be on the root element of every component so Datastar's morph-by-id can locate the frame on subsequent renders.

Return an attribute map carrying `self`'s instance id plus any other
attribute maps merged in.  Replaces the recurring boilerplate

    (merge {:id (:instance/id self)} (s/on self :submit) {:class "x"})

with

    (s/root-attrs self (s/on self :submit) {:class "x"})

The id has to be on the root element of every component so Datastar's
morph-by-id can locate the frame on subsequent renders.
sourceraw docstring

upload-attrsclj

(upload-attrs self)

Return form attributes for a zero-JS multipart upload.

[:form (s/upload-attrs self)
 [:input {:type "file" :name "file"}]
 [:button "Upload"]]
(s/upload-frame self)

The hidden iframe target prevents the browser from navigating away from the Datastar shell while the server handles the multipart POST.

Return form attributes for a zero-JS multipart upload.

    [:form (s/upload-attrs self)
     [:input {:type "file" :name "file"}]
     [:button "Upload"]]
    (s/upload-frame self)

The hidden iframe target prevents the browser from navigating away from
the Datastar shell while the server handles the multipart POST.
sourceraw docstring

upload-frameclj

(upload-frame self)

Hidden iframe target used by upload-attrs.

Hidden iframe target used by [[upload-attrs]].
sourceraw docstring

upload-targetclj

(upload-target self)

Stable hidden iframe target name for upload forms owned by self.

Stable hidden iframe target name for upload forms owned by `self`.
sourceraw docstring

upload-urlclj

(upload-url self)

URL a multipart upload form POSTs to for self.

Uploads intentionally do not use Datastar's signal POST body: browser file inputs need a normal multipart/form-data request. The HTTP layer turns that request back into a regular :upload-received event for this instance and pushes any resulting fragments over the already open SSE stream.

URL a multipart upload form POSTs to for `self`.

Uploads intentionally do not use Datastar's signal POST body: browser
file inputs need a normal `multipart/form-data` request.  The HTTP
layer turns that request back into a regular `:upload-received` event
for this instance and pushes any resulting fragments over the already
open SSE stream.
sourceraw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close