refactor(ssr): shared component partials own their CSS classes
Bake the Tailwind class base into the shared Selmer component partials so the partials own their markup and callers pass only data + a small variant (width / size / color). Applies across all four modals that share them (bulk-code, invoices, sales-summaries, transaction-edit). - typeahead / select / location-select / money-input / validated-field / button / a-button / a-icon-button: the class base, the validated-field has-error toggle, and the button color ladders now live in the .html. The sc/*-ctx fns pass width / variant / extra / color plus the non-class attrs (computed exactly as before, so every non-class attribute is unchanged). - bulk-code templates updated to the new partial contracts; account-row pulls money-input and a-icon-button in via includes. Verified: every component's class SET is identical to before across all variants (14/14 oracle match -- buttons reorder/dedupe classes, CSS is order-independent); bulk-code full render is DOM-equivalent to the pre-sweep baseline (class-set + attr-order normalized) for empty / populated / error; browser QA of bulk-code (full flow) and transaction-edit (open + render) clean, no JS errors; invoices + sales-summaries compile and render through the same sc/* fns. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -11,7 +11,6 @@
|
||||
components byte-for-byte modulo Tailwind class ordering (verify by string-match +
|
||||
e2e, never byte-parity -- see selmer-conventions.md)."
|
||||
(:require
|
||||
[auto-ap.ssr.components.buttons :as btn]
|
||||
[auto-ap.ssr.components.inputs :as inputs]
|
||||
[auto-ap.ssr.hiccup-helper :as hh]
|
||||
[auto-ap.ssr.hx :as hx]
|
||||
@@ -72,32 +71,26 @@
|
||||
(update :class #(str % (inputs/use-size size))))]
|
||||
(render "templates/components/text-input.html" {:attrs (attrs->str attrs)})))
|
||||
|
||||
(defn money-input-attrs
|
||||
"The serialized attribute string for a money input. Split out so a template-driven
|
||||
grid can inline `<input {{ attrs|safe }}>` without re-deriving the class logic."
|
||||
[{:keys [size] :as params}]
|
||||
(attrs->str (-> params
|
||||
(dissoc :size)
|
||||
(update :class (fnil hh/add-class "") inputs/default-input-classes)
|
||||
(update :class hh/add-class "appearance-none text-right")
|
||||
(update :class #(str % (inputs/use-size size)))
|
||||
(assoc :type "number" :step "0.01"))))
|
||||
(defn money-input-ctx
|
||||
"Plain-data context for templates/components/money-input.html. The class base is owned
|
||||
by the template; this passes the non-class attributes (name/value/...) and the variant
|
||||
class (caller width + size). Split out so a template can include the partial directly."
|
||||
[{:keys [size class] :as params}]
|
||||
{:variant (str (or class "") (inputs/use-size size))
|
||||
:attrs (attrs->str (dissoc params :class :size))})
|
||||
|
||||
(defn money-input [params]
|
||||
(render "templates/components/money-input.html" {:attrs (money-input-attrs params)}))
|
||||
(render "templates/components/money-input.html" (money-input-ctx params)))
|
||||
|
||||
(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)
|
||||
(let [sel (cond-> value (keyword? value) clojure.core/name)
|
||||
attrs (dissoc params :name :value :options :class)]
|
||||
{:name name
|
||||
:classes classes
|
||||
:variant (or class "")
|
||||
:attrs (attrs->str attrs)
|
||||
:options (for [[v label] options]
|
||||
{:value v :label label :selected (= (str v) (str sel))})}))
|
||||
@@ -130,11 +123,12 @@
|
||||
"Selmer port of com/validated-field (the errors- variant of field-): label + body +
|
||||
an always-present error <p>. Pass-through attrs land on the wrapping div (the account
|
||||
row's location cell hangs its swap wiring here)."
|
||||
[{:keys [label errors] :as params} & body]
|
||||
[{:keys [label errors class] :as params} & body]
|
||||
(let [attrs (dissoc params :label :errors :error-source :error-key :class)]
|
||||
(render "templates/components/validated-field.html"
|
||||
{:label label
|
||||
:classes (validated-field-classes params)
|
||||
:has_error (sequential? errors)
|
||||
:extra (or class "")
|
||||
:attrs (attrs->str attrs)
|
||||
:body (body->html body)
|
||||
:errors_str (errors-str errors)})))
|
||||
@@ -158,51 +152,42 @@
|
||||
:body (body->html 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))
|
||||
(not disabled) (str " hover:scale-105 transition duration-100")
|
||||
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"))]
|
||||
{:classes classes
|
||||
:attrs (attrs->str (dissoc params :class))
|
||||
:loading_label (not minimal-loading?)
|
||||
:body (body->html children)}))
|
||||
"Plain-data context for templates/components/button.html. The class base + color ladder
|
||||
are owned by the template; this passes the color (name), the caller's extra class, the
|
||||
non-class attrs, loading_label and body. NB: Selmer button callers only pass static
|
||||
colors (primary); dynamic colors go through the Hiccup com/button."
|
||||
[{:keys [color minimal-loading?] :as params} & children]
|
||||
{:color (some-> color name)
|
||||
:extra (or (:class params) "")
|
||||
:attrs (attrs->str (dissoc params :class))
|
||||
:loading_label (not minimal-loading?)
|
||||
:body (body->html 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")
|
||||
(= :secondary color) (str " text-white bg-blue-500 hover:bg-blue-600 focus:ring-blue-300 dark:bg-blue-600 dark:hover:bg-blue-700")
|
||||
(= :primary color) (str " text-white bg-green-500 hover:bg-green-600 focus:ring-green-300 dark:bg-green-600 dark:hover:bg-green-700 ")
|
||||
(= :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"))]
|
||||
{:classes classes
|
||||
:attrs (attrs->str (-> (dissoc params :class)
|
||||
(assoc :tabindex 0 :href (:href params "#"))))
|
||||
:indicator indicator?
|
||||
:body (body->html children)}))
|
||||
"Plain-data context for templates/components/a-button.html. The class base + color
|
||||
ladder (secondary/primary/red/default) are owned by the template; this passes the
|
||||
color (name), the caller's extra class, the non-class attrs, indicator and body."
|
||||
[{:keys [color] :as params} & children]
|
||||
{:color (some-> color name)
|
||||
:extra (or (:class params) "")
|
||||
:attrs (attrs->str (-> (dissoc params :class)
|
||||
(assoc :tabindex 0 :href (:href params "#"))))
|
||||
:indicator (:indicator? params true)
|
||||
:body (body->html 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)."
|
||||
"Plain-data context for templates/components/a-icon-button.html. The fixed class base is
|
||||
owned by the template; `extra` is the caller class plus the conditional p-3 padding."
|
||||
[{: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")]
|
||||
{:classes classes
|
||||
has-padding? (re-find #"\bp[xy]?-\d+(\.\d+)?\b" class-str)]
|
||||
{:extra (str class-str (if has-padding? "" " p-3"))
|
||||
:attrs (attrs->str (-> (dissoc params :class)
|
||||
(assoc :href (or (:href params) ""))))
|
||||
:body (body->html children)}))
|
||||
@@ -322,12 +307,7 @@
|
||||
:value {:value vval :label vlabel}
|
||||
:tippy nil :search "" :active -1
|
||||
:elements (if vval [{:value vval :label vlabel}] [])})
|
||||
a-class (-> (hh/add-class (or class "") inputs/default-input-classes)
|
||||
(hh/add-class "cursor-pointer"))
|
||||
a-xinit (str "$nextTick(() => tippy = $el.__x_tippy); " x-init)
|
||||
search-class (-> (or class "")
|
||||
(hh/add-class inputs/default-input-classes)
|
||||
(hh/replace-wildcard ["rounded" "border"] "border-bottom bg-gray-100 rounded-t-lg w-full"))
|
||||
hidden-attrs (-> params
|
||||
(dissoc :class :value-fn :content-fn :placeholder :x-model)
|
||||
(assoc "x-ref" "hidden" :type "hidden" ":value" "value.value"
|
||||
@@ -336,9 +316,8 @@
|
||||
:x_model x-model
|
||||
:key (when id (str id "--" vval))
|
||||
:disabled disabled
|
||||
:a_class a-class
|
||||
:width (or class "")
|
||||
:a_xinit a-xinit
|
||||
:search_class search-class
|
||||
:placeholder placeholder
|
||||
:hidden_attrs (attrs->str hidden-attrs)}))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user