Form builder as a way to simplify some things

This commit is contained in:
2022-07-14 22:08:03 -07:00
parent 99fe24ddae
commit 586978823e
6 changed files with 760 additions and 640 deletions

View File

@@ -0,0 +1,106 @@
(ns auto-ap.forms.builder
(:require
[auto-ap.views.utils :refer [bind-field]]
[re-frame.core :as re-frame]
[react :as react]
[reagent.core :as r]
[auto-ap.forms :as forms]
[auto-ap.status :as status]))
(defonce ^js/React.Context form-context (react/createContext "default"))
(def ^js/React.Provider Provider (. form-context -Provider))
(def ^js/React.Consumer Consumer (. form-context -Consumer))
(defonce ^js/React.Context form-scope-context (react/createContext []))
(def ^js/React.Provider FormScopeProvider (. form-scope-context -Provider))
(def ^js/React.Consumer FormScopeConsumer (. form-scope-context -Consumer))
(defn builder [{:keys [can-submit data-sub change-event submit-event id fullwidth?] :as z}]
(let [data-sub (or data-sub [::forms/form id])
change-event (or change-event [::forms/change id])
{:keys [data error]} @(re-frame/subscribe data-sub)]
(r/create-element Provider #js {:value #js {:can-submit @(re-frame/subscribe can-submit)
:change-event change-event
:submit-event submit-event
:error error
:status @(re-frame/subscribe [::status/single id])
:id id
:data data
:fullwidth? fullwidth?}}
(r/as-element
(into [:form {:on-submit (fn [e]
(when (.-stopPropagation e)
(.stopPropagation e)
(.preventDefault e))
(when can-submit
(re-frame/dispatch-sync (vec (conj submit-event {})))))}]
(r/children (r/current-component)))))))
(defn raw-field []
(let [[child] (r/children (r/current-component))]
[:> Consumer {}
(fn [consume-form]
(r/as-element
[:> FormScopeConsumer {}
(fn [form-scope]
(r/as-element
[bind-field (-> child
(update-in [1 :field] (fn [f]
(cond
(sequential? f)
(into form-scope f)
f
(conj form-scope f)
:else
nil)))
(assoc-in [1 :subscription] (aget consume-form "data"))
(assoc-in [1 :event] (aget consume-form "change-event")))]))]))]))
(defn with-scope [{:keys [scope]}]
(r/create-element FormScopeProvider #js {:value scope}
(r/as-element (into [:<>]
(r/children (r/current-component))))))
(defn field []
(let [[label child] (r/children (r/current-component))]
[:> Consumer {}
(fn [consume]
(r/as-element
[:div.field
(when label (if (aget consume "fullwidth?") [:p.help label]
[:label.label label]))
[:div.control [raw-field {} child]]]))]))
(defn section [{:keys [title]}]
[:<>
[:h4.is-4.title title]
[:hr]
(into [:div {:style {:margin-bottom "5em"}}]
(r/children (r/current-component)))])
(defn submit-button []
(let [[child] (r/children (r/current-component))]
[:> Consumer {}
(fn [consume]
(let [status (aget consume "status")
can-submit (aget consume "can-submit")
fullwidth? (aget consume "fullwidth?")]
(r/as-element
[:button.button.is-medium.is-primary {:disabled (or (status/disabled-for status)
(not can-submit))
:class (cond-> (status/class-for status)
fullwidth? (conj "is-fullwidth")) }
child])))]))
(defn error-notification []
(let [[child] (r/children (r/current-component))]
[:> Consumer {}
(fn [consume]
(r/as-element
(when-let [error (aget consume "error")]
^{:key error}
[:div.has-text-danger.animated.fadeInUp {} error])))]))