refactor(ssr): compose the bulk-code form entirely in Selmer
Each bulk-code route now ends in a single sel/render call; all composition
(modal chrome, body, account grid, rows, footer, errors) happens in the
templates via {% extends %}/{% block %}/{% include %}/{% with %}, reading one
nested view-model (form-ctx). No HTML is stitched together in Clojure.
- Add components/modal-card.html: a base chrome with head/body/footer blocks;
bulk-code/card.html extends it. (Transaction Edit keeps its string-slot
edit-modal.html for now.)
- New top-level templates: open.html, form.html, card.html, body.html; rework
account-grid/account-row/footer/head to pull the shared component partials in
via {% include %}+{% with %} instead of hardcoding class strings or receiving
pre-rendered HTML strings.
- render-form / open-handler collapse to one sel/render of form.html / open.html.
bulk-code-body*, footer*, form-errors-html, account-grid*, the *errors* dynamic
var and ferr are gone; field errors are read straight from :form-errors.
- Extract sc/{select,button,a-button,a-icon-button}-ctx so templates can include
those partials with computed context (the render wrappers now call the -ctx fns).
Verified: rendered output is DOM-identical to the prior version across empty /
populated / error scenarios (whitespace-normalized token compare), plus browser
QA (open, vendor auto-populate, add/remove row, typeahead, per-row location swap,
percentage validation, submit); no JS errors.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -86,23 +86,27 @@
|
||||
(defn money-input [params]
|
||||
(render "templates/components/money-input.html" {:attrs (money-input-attrs params)}))
|
||||
|
||||
(defn select
|
||||
"Generic <select> rendered from a Selmer partial (the location-select.html shape,
|
||||
generalized). options = [[value label] ...]; `value` (string or keyword) marks the
|
||||
selected option. Class defaults to the standard input classes, like com/select. Extra
|
||||
attrs (hx-*, x-*) ride through onto the element."
|
||||
(defn select-ctx
|
||||
"Plain-data context for templates/components/select.html. options = [[value label] ...];
|
||||
`value` (string or keyword) marks the selected option. Split out so a template can
|
||||
{% include %} the partial via {% with %} without re-deriving classes/selection."
|
||||
[{:keys [name value options class] :as params}]
|
||||
(let [classes (-> ""
|
||||
(hh/add-class inputs/default-input-classes)
|
||||
(hh/add-class (or class "")))
|
||||
sel (cond-> value (keyword? value) clojure.core/name)
|
||||
attrs (dissoc params :name :value :options :class)]
|
||||
(render "templates/components/select.html"
|
||||
{:name name
|
||||
:classes classes
|
||||
:attrs (attrs->str attrs)
|
||||
:options (for [[v label] options]
|
||||
{:value v :label label :selected (= (str v) (str sel))})})))
|
||||
{:name name
|
||||
:classes classes
|
||||
:attrs (attrs->str attrs)
|
||||
:options (for [[v label] options]
|
||||
{:value v :label label :selected (= (str v) (str sel))})}))
|
||||
|
||||
(defn select
|
||||
"Generic <select> rendered from a Selmer partial (the location-select.html shape,
|
||||
generalized). See select-ctx."
|
||||
[params]
|
||||
(render "templates/components/select.html" (select-ctx params)))
|
||||
|
||||
;; --- field wrapper ---------------------------------------------------------------
|
||||
|
||||
@@ -153,7 +157,9 @@
|
||||
:attrs (attrs->str (dissoc params :class))
|
||||
:body (body->html children)}))
|
||||
|
||||
(defn button [{:keys [color disabled minimal-loading?] :as params} & children]
|
||||
(defn button-ctx
|
||||
"Plain-data context for templates/components/button.html (classes/attrs/loading_label/body)."
|
||||
[{:keys [color disabled minimal-loading?] :as params} & children]
|
||||
(let [classes (cond-> (:class params)
|
||||
true (str " focus:ring-4 font-bold rounded-lg text-xs p-3 text-center mr-2 inline-flex items-center relative justify-center disabled:opacity-50"
|
||||
(btn/bg-colors color disabled))
|
||||
@@ -161,13 +167,17 @@
|
||||
disabled (str " cursor-not-allowed")
|
||||
(some? color) (str " text-white ")
|
||||
(nil? color) (str " bg-white dark:bg-gray-600 border-gray-300 dark:border-gray-700 text-gray-500 hover:text-gray-800 dark:text-gray-400 dark:hover:text-gray-100 font-medium border border-gray-300 dark:border-gray-700"))]
|
||||
(render "templates/components/button.html"
|
||||
{:classes classes
|
||||
:attrs (attrs->str (dissoc params :class))
|
||||
:loading_label (not minimal-loading?)
|
||||
:body (body->html children)})))
|
||||
{:classes classes
|
||||
:attrs (attrs->str (dissoc params :class))
|
||||
:loading_label (not minimal-loading?)
|
||||
:body (body->html children)}))
|
||||
|
||||
(defn a-button [{:keys [color disabled] :as params} & children]
|
||||
(defn button [params & children]
|
||||
(render "templates/components/button.html" (apply button-ctx params children)))
|
||||
|
||||
(defn a-button-ctx
|
||||
"Plain-data context for templates/components/a-button.html (classes/attrs/indicator/body)."
|
||||
[{:keys [color disabled] :as params} & children]
|
||||
(let [indicator? (:indicator? params true)
|
||||
classes (cond-> (:class params)
|
||||
true (str " focus:ring-4 font-bold rounded-lg text-xs p-3 text-center mr-2 inline-flex items-center hover:scale-105 transition duration-100 justify-center")
|
||||
@@ -176,23 +186,29 @@
|
||||
(= :secondary-light color) (str " text-blue-800 bg-white-200 border-gray-100 border hover:bg-blue-100 focus:ring-blue-100 dark:bg-blue-400 dark:hover:bg-blue-800 ")
|
||||
(some? color) (str " text-white " (btn/bg-colors color disabled))
|
||||
(nil? color) (str " bg-white dark:bg-gray-600 border-gray-300 dark:border-gray-700 text-gray-500 hover:text-gray-800 dark:text-gray-400 dark:hover:text-gray-100 font-medium border border-gray-300 dark:border-gray-700"))]
|
||||
(render "templates/components/a-button.html"
|
||||
{:classes classes
|
||||
:attrs (attrs->str (-> (dissoc params :class)
|
||||
(assoc :tabindex 0 :href (:href params "#"))))
|
||||
:indicator indicator?
|
||||
:body (body->html children)})))
|
||||
{:classes classes
|
||||
:attrs (attrs->str (-> (dissoc params :class)
|
||||
(assoc :tabindex 0 :href (:href params "#"))))
|
||||
:indicator indicator?
|
||||
:body (body->html children)}))
|
||||
|
||||
(defn a-icon-button [{:keys [class] :as params} & children]
|
||||
(defn a-button [params & children]
|
||||
(render "templates/components/a-button.html" (apply a-button-ctx params children)))
|
||||
|
||||
(defn a-icon-button-ctx
|
||||
"Plain-data context for templates/components/a-icon-button.html (classes/attrs/body)."
|
||||
[{:keys [class] :as params} & children]
|
||||
(let [class-str (or class "")
|
||||
has-padding? (re-find #"\bp[xy]?-\d+(\.\d+)?\b" class-str)
|
||||
classes (str class-str (if has-padding? "" " p-3")
|
||||
" inline-flex items-center justify-center bg-white dark:bg-gray-600 items-center text-sm font-medium border border-gray-300 dark:border-gray-700 text-center text-gray-500 hover:text-gray-800 rounded-lg dark:text-gray-400 dark:hover:text-gray-100")]
|
||||
(render "templates/components/a-icon-button.html"
|
||||
{:classes classes
|
||||
:attrs (attrs->str (-> (dissoc params :class)
|
||||
(assoc :href (or (:href params) ""))))
|
||||
:body (body->html children)})))
|
||||
{:classes classes
|
||||
:attrs (attrs->str (-> (dissoc params :class)
|
||||
(assoc :href (or (:href params) ""))))
|
||||
:body (body->html children)}))
|
||||
|
||||
(defn a-icon-button [params & children]
|
||||
(render "templates/components/a-icon-button.html" (apply a-icon-button-ctx params children)))
|
||||
|
||||
(defn button-group-button [{:keys [size] :or {size :normal} :as params} & children]
|
||||
(let [classes (cond-> (:class params)
|
||||
|
||||
Reference in New Issue
Block a user