Whole-form hx-select swaps with zero out-of-band swaps
Replace the section-swap + OOB approach with uniform whole-form swaps, eliminating both out-of-band swaps: - Discrete edits (vendor, account, location, mode, add/remove row) now swap all of #wizard-form via hx-select. The active action/tab already round-trips (:action is in edit-form-schema and the tab x-data inits from it), so a whole-form swap re-creates the tab state from the server value and the active tab is preserved -- no #wizard-snapshot OOB needed, since the snapshot hidden field rides along inside the form. - Move the totals into their own <tbody id="account-totals"> (new optional :footer-tbody param on data-grid-) so the amount field updates them with a plain targeted swap instead of an OOB swap of #total,#balance. The totals tbody is a sibling of the input rows, so the amount input is never replaced. - Memo unchanged (hx-swap=none). Net: 0 hx-select-oob, 0 morph. The focus invariant is unchanged -- the typed field is never inside a region it swaps. Tab clicks stay Alpine (instant); only the action value round-trips. Revert the now-unneeded #wizard-snapshot id. Full e2e suite: 27 passed / 2 failed (same pre-existing, unrelated failures). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -233,9 +233,8 @@
|
||||
:x-dispatch:changed "simpleAccountId"
|
||||
:hx-trigger "changed"
|
||||
:hx-post (bidi/path-for ssr-routes/only-routes ::route/edit-form-changed)
|
||||
:hx-target "#manual-coding-section"
|
||||
:hx-select "#manual-coding-section"
|
||||
:hx-select-oob "#wizard-snapshot"
|
||||
:hx-target "#wizard-form"
|
||||
:hx-select "#wizard-form"
|
||||
:hx-swap "outerHTML"
|
||||
:hx-include "closest form"}
|
||||
(location-select*
|
||||
@@ -250,9 +249,8 @@
|
||||
[:a.text-sm.text-blue-600.hover:underline.cursor-pointer
|
||||
{:hx-post (bidi/path-for ssr-routes/only-routes ::route/edit-wizard-toggle-mode)
|
||||
:hx-include "closest form"
|
||||
:hx-target "#manual-coding-section"
|
||||
:hx-select "#manual-coding-section"
|
||||
:hx-select-oob "#wizard-snapshot"
|
||||
:hx-target "#wizard-form"
|
||||
:hx-select "#wizard-form"
|
||||
:hx-swap "outerHTML"}
|
||||
"Switch to advanced mode"]]]))
|
||||
|
||||
@@ -296,9 +294,8 @@
|
||||
:x-dispatch:changed "accountId"
|
||||
:hx-trigger "changed"
|
||||
:hx-post (bidi/path-for ssr-routes/only-routes ::route/edit-form-changed)
|
||||
:hx-target "#manual-coding-section"
|
||||
:hx-select "#manual-coding-section"
|
||||
:hx-select-oob "#wizard-snapshot"
|
||||
:hx-target "#wizard-form"
|
||||
:hx-select "#wizard-form"
|
||||
:hx-swap "outerHTML"
|
||||
:hx-include "closest form"}
|
||||
(location-select* {:name (fc/field-name)
|
||||
@@ -317,12 +314,12 @@
|
||||
:class "w-16 account-amount-field"
|
||||
:value (fc/field-value)
|
||||
:hx-post (bidi/path-for ssr-routes/only-routes ::route/edit-form-changed)
|
||||
;; Typing an amount posts the whole form but swaps NOTHING into the field
|
||||
;; itself (hx-swap=none). Only the TOTAL and BALANCE cells are pulled out of
|
||||
;; the response and applied out-of-band, so the amount input is never replaced
|
||||
;; and the user's focus + caret survive with no morph involved.
|
||||
:hx-select-oob "#total,#balance"
|
||||
:hx-swap "none"
|
||||
;; Typing an amount posts the whole form but swaps back only the
|
||||
;; #account-totals tbody -- a sibling of the input-bearing rows, so
|
||||
;; the amount input is never replaced and the caret survives.
|
||||
:hx-target "#account-totals"
|
||||
:hx-select "#account-totals"
|
||||
:hx-swap "outerHTML"
|
||||
:hx-trigger "keyup changed delay:300ms"
|
||||
:hx-include "closest form"}]
|
||||
(if (= "%" amount-mode)
|
||||
@@ -331,9 +328,8 @@
|
||||
(com/data-grid-cell {:class "align-top"}
|
||||
(com/a-icon-button {:hx-post (bidi/path-for ssr-routes/only-routes ::route/edit-wizard-remove-account)
|
||||
:hx-vals (hx/json {:row-index (or index 0)})
|
||||
:hx-target "#manual-coding-section"
|
||||
:hx-select "#manual-coding-section"
|
||||
:hx-select-oob "#wizard-snapshot"
|
||||
:hx-target "#wizard-form"
|
||||
:hx-select "#wizard-form"
|
||||
:hx-swap "outerHTML"
|
||||
:hx-include "closest form"
|
||||
:class "account-remove-action"} svg/x))))
|
||||
@@ -475,12 +471,36 @@
|
||||
:name "step-params[amount-mode]"
|
||||
:orientation :horizontal
|
||||
:hx-post (bidi/path-for ssr-routes/only-routes ::route/toggle-amount-mode)
|
||||
:hx-target "#manual-coding-section"
|
||||
:hx-select "#manual-coding-section"
|
||||
:hx-select-oob "#wizard-snapshot"
|
||||
:hx-target "#wizard-form"
|
||||
:hx-select "#wizard-form"
|
||||
:hx-swap "outerHTML"
|
||||
:hx-include "closest form"}))
|
||||
(com/data-grid-header {:class "w-16"})]}
|
||||
(com/data-grid-header {:class "w-16"})]
|
||||
;; Totals live in their own <tbody id="account-totals"> so the amount
|
||||
;; field refreshes them with a plain targeted swap, never swapping the
|
||||
;; input-bearing rows above (which would drop the caret).
|
||||
:footer-tbody
|
||||
[:tbody {:id "account-totals"}
|
||||
(com/data-grid-row {:class "account-total-row"}
|
||||
(com/data-grid-cell {})
|
||||
(com/data-grid-cell {:class "text-right"} [:span.font-bold.text-right "TOTAL"])
|
||||
(com/data-grid-cell {:id "total"
|
||||
:class "text-right"}
|
||||
(account-total* request))
|
||||
(com/data-grid-cell {}))
|
||||
(com/data-grid-row {:class "account-balance-row"}
|
||||
(com/data-grid-cell {})
|
||||
(com/data-grid-cell {:class "text-right"} [:span.font-bold.text-right "BALANCE"])
|
||||
(com/data-grid-cell {:id "balance"
|
||||
:class "text-right"}
|
||||
(account-balance* request))
|
||||
(com/data-grid-cell {}))
|
||||
(com/data-grid-row {:class "account-grand-total-row"}
|
||||
(com/data-grid-cell {})
|
||||
(com/data-grid-cell {:class "text-right"} [:span.font-bold.text-right "TRANSACTION TOTAL"])
|
||||
(com/data-grid-cell {:class "text-right"}
|
||||
(format "$%,.2f" total))
|
||||
(com/data-grid-cell {}))]}
|
||||
(fc/cursor-map (fn [cursor]
|
||||
(transaction-account-row* {:value cursor
|
||||
:client-id (-> request :entity :transaction/client :db/id)
|
||||
@@ -491,35 +511,12 @@
|
||||
(com/data-grid-row {:class "new-row"}
|
||||
(com/data-grid-cell {:colspan 4}
|
||||
(com/a-button {:hx-post (bidi/path-for ssr-routes/only-routes ::route/edit-wizard-new-account)
|
||||
:hx-target "#manual-coding-section"
|
||||
:hx-select "#manual-coding-section"
|
||||
:hx-select-oob "#wizard-snapshot"
|
||||
:hx-target "#wizard-form"
|
||||
:hx-select "#wizard-form"
|
||||
:hx-swap "outerHTML"
|
||||
:hx-include "closest form"
|
||||
:color :secondary}
|
||||
"New account")))
|
||||
(com/data-grid-row {:class "account-total-row"}
|
||||
(com/data-grid-cell {})
|
||||
(com/data-grid-cell {:class "text-right"} [:span.font-bold.text-right "TOTAL"])
|
||||
(com/data-grid-cell {:id "total"
|
||||
:class "text-right"}
|
||||
(account-total* request))
|
||||
(com/data-grid-cell {}))
|
||||
|
||||
(com/data-grid-row {:class "account-balance-row"}
|
||||
(com/data-grid-cell {})
|
||||
(com/data-grid-cell {:class "text-right"} [:span.font-bold.text-right "BALANCE"])
|
||||
(com/data-grid-cell {:id "balance"
|
||||
:class "text-right"}
|
||||
(account-balance* request))
|
||||
(com/data-grid-cell {}))
|
||||
|
||||
(com/data-grid-row {:class "account-grand-total-row"}
|
||||
(com/data-grid-cell {})
|
||||
(com/data-grid-cell {:class "text-right"} [:span.font-bold.text-right "TRANSACTION TOTAL"])
|
||||
(com/data-grid-cell {:class "text-right"}
|
||||
(format "$%,.2f" total))
|
||||
(com/data-grid-cell {})))))
|
||||
"New account"))))))
|
||||
|
||||
(defn manual-coding-section*
|
||||
"Renders the vendor field + account/location section for the manual tab.
|
||||
@@ -536,9 +533,8 @@
|
||||
(com/hidden {:name "step-params[mode]" :value (name mode)})
|
||||
[:div {:hx-trigger "change"
|
||||
:hx-post (bidi/path-for ssr-routes/only-routes ::route/edit-vendor-changed)
|
||||
:hx-target "#manual-coding-section"
|
||||
:hx-select "#manual-coding-section"
|
||||
:hx-select-oob "#wizard-snapshot"
|
||||
:hx-target "#wizard-form"
|
||||
:hx-select "#wizard-form"
|
||||
:hx-swap "outerHTML"
|
||||
:hx-sync "this:replace"
|
||||
:hx-include "closest form"}
|
||||
@@ -564,9 +560,8 @@
|
||||
[:a.text-sm.text-blue-600.hover:underline.cursor-pointer
|
||||
{:hx-post (bidi/path-for ssr-routes/only-routes ::route/edit-wizard-toggle-mode)
|
||||
:hx-include "closest form"
|
||||
:hx-target "#manual-coding-section"
|
||||
:hx-select "#manual-coding-section"
|
||||
:hx-select-oob "#wizard-snapshot"
|
||||
:hx-target "#wizard-form"
|
||||
:hx-select "#wizard-form"
|
||||
:hx-swap "outerHTML"}
|
||||
"Switch to simple mode"]])
|
||||
(fc/with-field :transaction/accounts
|
||||
|
||||
Reference in New Issue
Block a user