finally sunset old binding

This commit is contained in:
2022-07-23 10:17:41 -07:00
parent 03b5846d82
commit 3628f1e018
12 changed files with 159 additions and 484 deletions

View File

@@ -220,7 +220,7 @@ nav.navbar .navbar-item.is-active {
margin: 0 -50px; margin: 0 -50px;
padding-left: 50px; padding-left: 50px;
} }
.aside .main .icon { .aside .main .menu-item .icon {
font-size: 19px; font-size: 19px;
padding-right: 30px; padding-right: 30px;
color: #A0A0A0; color: #A0A0A0;

View File

@@ -201,7 +201,7 @@
" is-danger" " is-danger"
value value
"is-success" " is-success"
:else :else
"")))))))))))))) ""))))))))))))))

View File

@@ -1,55 +1,51 @@
(ns auto-ap.views.components.date-range-filter (ns auto-ap.views.components.date-range-filter
(:require (:require
[auto-ap.views.utils :refer [bind-field date-picker date->str local-now standard]] [auto-ap.views.utils :refer [date-picker date->str local-now standard]]
[cljs-time.core :as t] [cljs-time.core :as t]
[re-frame.core :as re-frame])) [re-frame.core :as re-frame]
[auto-ap.forms.builder :as form-builder]))
(defn dispatch-change [on-change-event start end]
(fn [_] (defn set-value [on-change-event v]
(re-frame/dispatch (into on-change-event [[:start] start]) ) (re-frame/dispatch (conj on-change-event v)))
(re-frame/dispatch (into on-change-event [[:end] end]))))
(defn date-range-filter [{:keys [value on-change-event]}] (defn date-range-filter [{:keys [value on-change-event]}]
[:div [form-builder/virtual-builder {:value (or value {})
[:div.field.has-addons :on-change (fn [v]
[:p.control [:a.button.is-small {:on-click (set-value on-change-event v))}
(dispatch-change on-change-event
(date->str (t/minus (local-now) (t/period :days 7)) standard) [:div
(date->str (local-now) standard))} [:div.field.has-addons
"Week" ]] [:p.control [:a.button.is-small {:on-click
[:p.control [:a.button.is-small {:on-click #(set-value on-change-event
(dispatch-change on-change-event {:start (date->str (t/minus (local-now) (t/period :days 7)) standard)
(date->str (t/minus (local-now) (t/period :months 1)) standard) :end (date->str (local-now) standard)})}
(date->str (local-now) standard))} "Week" ]]
"Month" ]] [:p.control [:a.button.is-small {:on-click
[:p.control [:a.button.is-small {:on-click #(set-value on-change-event
(dispatch-change on-change-event {:start (date->str (t/minus (local-now) (t/period :months 1)) standard)
(date->str (t/minus (local-now) (t/period :years 1)) standard) :end (date->str (local-now) standard)})}
(date->str (local-now) standard))} "Month" ]]
"Year"]] [:p.control [:a.button.is-small {:on-click
[:p.control [:a.button.is-small {:on-click
(dispatch-change on-change-event #(set-value on-change-event
nil {:start (date->str (t/minus (local-now) (t/period :years 1)) standard)
nil)} :end (date->str (local-now) standard)})}
"All"]]] "Year"]]
[:div.field.has-addons [:p.control [:a.button.is-small {:on-click
[:div.control #(set-value on-change-event nil)}
[bind-field "All"]]]
[date-picker [:div.field.has-addons
{:event on-change-event [:div.control
:type "date"
:placeholder "Start" [form-builder/raw-field-v2 {:field :start}
:class "is-small" [date-picker
:field [:start] {:placeholder "Start"
:subscription value :class "is-small"
:output :text}]]] :output :text}]]]
[:div.control [:div.control
[bind-field [form-builder/raw-field-v2 {:field :end}
[date-picker [date-picker
{:event on-change-event {:class "is-small"
:type "date" :placeholder "End"
:class "is-small" :output :text}]]]]]])
:placeholder "End"
:field [:end]
:subscription value
:output :text}]]]]])

View File

@@ -1,25 +1,21 @@
(ns auto-ap.views.components.number-filter (ns auto-ap.views.components.number-filter
(:require (:require
[auto-ap.views.utils :refer [bind-field]] [re-frame.core :as re-frame]
[re-frame.core :as re-frame])) [auto-ap.forms.builder :as form-builder]
[auto-ap.views.components :as com]))
(defn number-filter [{:keys [value on-change-event]}] (defn number-filter [{:keys [value on-change-event]}]
[:div.field [form-builder/virtual-builder {:value (or value {})
[:div.control :on-change (fn [v]
[:div.columns (re-frame/dispatch (conj on-change-event v)))}
[:div.column
[bind-field [:div.columns
[:input.input {:type "number" [:div.column
:placeholder ">=" [:div.control
:field [:amount-gte] [form-builder/raw-field-v2 {:field :amount-gte}
:step "0.01" [com/money-input {:placeholder ">="}]]]]
:event on-change-event [:div.column
:subscription value}]]] [:div.control
[:div.column [form-builder/raw-field-v2 {:field :amount-lte}
[bind-field [com/money-input {:placeholder "<="}]]]]]]
[:input.input {:type "number" )
:placeholder "<="
:field [:amount-lte]
:event on-change-event
:step "0.01"
:subscription value}]]]]]])

View File

@@ -1,29 +1,25 @@
(ns auto-ap.views.components.vendor-dialog (ns auto-ap.views.components.vendor-dialog
(:require (:require
[auto-ap.entities.contact :as contact]
[auto-ap.entities.vendors :as entity]
[auto-ap.forms :as forms] [auto-ap.forms :as forms]
[auto-ap.forms.builder :as form-builder] [auto-ap.forms.builder :as form-builder]
[auto-ap.schema :as schema]
[auto-ap.status :as status] [auto-ap.status :as status]
[auto-ap.views.components.level :refer [left-stack]]
[auto-ap.subs :as subs] [auto-ap.subs :as subs]
[auto-ap.views.components :as com] [auto-ap.views.components :as com]
[auto-ap.views.components.address :refer [address2-field]] [auto-ap.views.components.address :refer [address2-field]]
[auto-ap.views.components.level :refer [left-stack]]
[auto-ap.views.components.modal :as modal] [auto-ap.views.components.modal :as modal]
[auto-ap.views.components.multi :refer [multi-field-v2]]
[auto-ap.views.components.number :refer [number-input]] [auto-ap.views.components.number :refer [number-input]]
[auto-ap.views.components.typeahead :refer [typeahead-v3]] [auto-ap.views.components.typeahead :refer [typeahead-v3]]
[auto-ap.views.components.typeahead.vendor [auto-ap.views.components.typeahead.vendor
:refer [search-backed-typeahead]] :refer [search-backed-typeahead]]
[auto-ap.views.pages.admin.vendors.common :as common] [auto-ap.views.pages.admin.vendors.common :as common]
[auto-ap.views.components.multi :refer [multi-field-v2]]
[auto-ap.views.utils [auto-ap.views.utils
:refer [dispatch-event multi-field str->int with-is-admin? with-user]] :refer [dispatch-event str->int with-is-admin? with-user]]
[clojure.spec.alpha :as s]
[re-frame.core :as re-frame]
[reagent.core :as r]
[malli.core :as m] [malli.core :as m]
[auto-ap.schema :as schema] [re-frame.core :as re-frame]
[malli.error :as me])) [reagent.core :as r]))
;; Remaining cleanup todos: ;; Remaining cleanup todos:
;; test minification ;; test minification

View File

@@ -1,18 +1,17 @@
(ns auto-ap.views.pages.admin.accounts.form (ns auto-ap.views.pages.admin.accounts.form
(:require [auto-ap.entities.account :as entity] (:require
[auto-ap.forms :as forms] [auto-ap.forms :as forms]
[auto-ap.subs :as subs] [auto-ap.forms.builder :as form-builder]
[auto-ap.views.components.layouts :refer [side-bar]] [auto-ap.schema :as schema]
[auto-ap.views.components.typeahead :refer [typeahead-v3]] [auto-ap.subs :as subs]
[auto-ap.views.utils :refer [dispatch-event multi-field with-user]] [auto-ap.views.components :as com]
[clojure.spec.alpha :as s] [auto-ap.views.components.layouts :refer [side-bar]]
[clojure.string :as str] [auto-ap.views.components.typeahead :refer [typeahead-v3]]
[re-frame.core :as re-frame] [auto-ap.views.utils :refer [dispatch-event with-user]]
[auto-ap.forms.builder :as form-builder] [clojure.string :as str]
[vimsical.re-frame.cofx.inject :as inject] [malli.core :as m]
[auto-ap.views.components :as com] [re-frame.core :as re-frame]
[malli.core :as m] [vimsical.re-frame.cofx.inject :as inject]))
[auto-ap.schema :as schema]))
(def types [:dividend :expense :asset :liability :equity :revenue]) (def types [:dividend :expense :asset :liability :equity :revenue])
(def applicabilities [:global :optional :customized]) (def applicabilities [:global :optional :customized])

View File

@@ -15,13 +15,10 @@
:refer [search-backed-typeahead]] :refer [search-backed-typeahead]]
[auto-ap.views.utils [auto-ap.views.utils
:refer [date-picker :refer [date-picker
dispatch-event dispatch-event]]
horizontal-field
multi-field]]
[bidi.bidi :as bidi] [bidi.bidi :as bidi]
[cljs-time.coerce :as coerce] [cljs-time.coerce :as coerce]
[cljs-time.core :as t] [cljs-time.core :as t]
[clojure.string :as str]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[reagent.core :as r] [reagent.core :as r]
[react-signature-canvas] [react-signature-canvas]

View File

@@ -15,8 +15,7 @@
::save ::save
[ with-user (forms/in-form ::form)] [ with-user (forms/in-form ::form)]
(fn [{:keys [db user]}] (fn [{:keys [db user]}]
{ {:http {:token user
:http {:token user
:method :post :method :post
:body (pr-str {:excel-rows (:excel-rows (:data db))}) :body (pr-str {:excel-rows (:excel-rows (:data db))})
:headers {"Content-Type" "application/edn"} :headers {"Content-Type" "application/edn"}

View File

@@ -2,37 +2,28 @@
(:require (:require
[auto-ap.events :as events] [auto-ap.events :as events]
[auto-ap.forms :as forms] [auto-ap.forms :as forms]
[auto-ap.forms.builder :as form-builder]
[auto-ap.status :as status] [auto-ap.status :as status]
[auto-ap.subs :as subs] [auto-ap.views.components :as com]
[auto-ap.views.components.dropdown [auto-ap.views.components.dropdown
:refer [drop-down drop-down-contents]] :refer [drop-down drop-down-contents]]
[auto-ap.views.components.layouts :refer [side-bar-layout]] [auto-ap.views.components.layouts :refer [side-bar-layout]]
[auto-ap.views.pages.ledger.side-bar :refer [ledger-side-bar]] [auto-ap.views.pages.ledger.side-bar :refer [ledger-side-bar]]
[auto-ap.views.utils :refer [bind-field dispatch-event]] [auto-ap.views.utils :refer [dispatch-event]]
[clojure.string :as str] [clojure.string :as str]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[reagent.core :as r])) [reagent.core :as r]))
(re-frame/reg-sub (defn line->id [{:keys [source id client-code]}]
::loading
(fn [db]
(-> db ::loading)))
(re-frame/reg-sub
::can-submit
(fn [db]
true))
(defn line->id [{:keys [source id client-code date vendor-name] :as line}]
(str client-code "-" source "-" id)) (str client-code "-" source "-" id))
(re-frame/reg-sub (re-frame/reg-sub
::request ::request
:<- [::forms/form ::form] :<- [::forms/form ::form]
(fn [{{lines :line-items :as d} :data :as g}] (fn [{{lines :line-items} :data}]
(into [] (into []
(for [[external-id lines] (group-by line->id lines) (for [[_ lines] (group-by line->id lines)
:let [{:keys [source id client-code date vendor-name note cleared-against] :as line} (first lines)]] :let [{:keys [source client-code date vendor-name note cleared-against] :as line} (first lines)]]
{:source source {:source source
:external-id (line->id line) :external-id (line->id line)
:client-code client-code :client-code client-code
@@ -139,6 +130,8 @@
[:form.form [:form.form
(if value (if value
[:div [:div
[:a.button {:on-click #(on-change nil)}
"reset"]
[:table.table {:style {:width "100%"}} [:table.table {:style {:width "100%"}}
[:thead [:thead
[:tr [:tr
@@ -190,68 +183,72 @@
(def balance-sheet-content (def balance-sheet-content
(with-meta (with-meta
(fn [] (fn []
(let [current-client @(re-frame/subscribe [::subs/client]) (let [status @(re-frame/subscribe [::status/single ::import])
user @(re-frame/subscribe [::subs/user]) {:keys [data result]} @(re-frame/subscribe [::forms/form ::form]) ]
status @(re-frame/subscribe [::status/single ::import]) [form-builder/builder {:id ::form
{:keys [data result active? error id]} @(re-frame/subscribe [::forms/form ::form]) ] :submit-event [::importing]}
[:div [:div
[:div.level [:div.level
[:div.level-left [:div.level-left
[:h1.title "Eternal Import"]] [:h1.title "Eternal Import"]]
[:div.level-right [:div.level-right
[:button.button.is-primary.is-pulled-right.is-large {:disabled (or (not data) [form-builder/submit-button "Import"]]]
(= :loading (:state status ))) [status/status-notification {:statuses [[::status/single ::import]]} ]
:on-click (dispatch-event [::importing])} "Import"]]] (when result
[status/status-notification {:statuses [[::status/single ::import]]} ] [:div.notification
(when result "Imported with "
[:div.notification (count (:errors result)) " errors, "
"Imported with " (count (:ignored result)) " ignored, "
(count (:errors result)) " errors, " (count (:success result)) " successful."])
(count (:ignored result)) " ignored, " (if (= :loading (:state status ))
(count (:success result)) " successful."]) [status/big-loader status]
(if (= :loading (:state status ))
[status/big-loader status]
[:div
[:div.is-clearfix
[:div.is-pulled-right
[:label.checkbox
[bind-field
[:input {:type "checkbox"
:event [::forms/change ::form]
:subscription data
:field [:only-show-errors?]}]]
"Only show errors"]]]
[:div [:div
[bind-field [:div.is-clearfix
[textarea->table {:type "textarea->table" [:div.is-pulled-right
:field [:line-items] [form-builder/raw-field-v2 {:field :only-show-errors?}
:headings [["Id" :id] [com/checkbox {:label "Only show errors"}]]]]
["Client" :client-code] [:div
["Source" :source] [form-builder/raw-field-v2 {:field :line-items}
["Vendor" :vendor-name] [textarea->table {:headings [["Id" :id]
["Date" :date] ["Client" :client-code]
["Account" :account-identifier] ["Source" :source]
["Location" :location] ["Vendor" :vendor-name]
["Debit" :debit] ["Date" :date]
["Credit" :credit] ["Account" :account-identifier]
["Note" :note] ["Location" :location]
["Cleared against" :cleared-against]] ["Debit" :debit]
:read-only-headings ["Credit" :credit]
[["status" :status]] ["Note" :note]
["Cleared against" :cleared-against]]
:read-only-headings
[["status" :status]]
:row-filter :row-filter
(fn [{:keys [status-category]}] (fn [{:keys [status-category]}]
(if (:only-show-errors? data) (if (:only-show-errors? data)
(= :error status-category) (= :error status-category)
true)) true))}]]]])]]))
:event [::forms/change ::form]
:subscription data}
]]]])]))
{})) {}))
(defn external-import-page [] (re-frame/reg-event-fx
::mounted
(fn [_ _]
{:dispatch [::forms/start-form ::form]}))
(re-frame/reg-event-fx
::unmounted
(fn [_ _]
{:dispatch [::forms/form-closing ::form]}))
(defn external-import-page-internal []
[side-bar-layout [side-bar-layout
{:side-bar [ledger-side-bar] {:side-bar [ledger-side-bar]
:main [balance-sheet-content]}]) :main [balance-sheet-content]}])
(defn external-import-page []
(r/create-class
{:display-name "external-import-page"
:component-will-unmount #(re-frame/dispatch-sync [::unmounted])
:component-did-mount #(re-frame/dispatch [::mounted])
:reagent-render external-import-page-internal}))

View File

@@ -10,7 +10,6 @@
:refer [appearing-side-bar side-bar-layout]] :refer [appearing-side-bar side-bar-layout]]
[auto-ap.views.components.modal :as modal] [auto-ap.views.components.modal :as modal]
[auto-ap.views.components.switch-field :refer [switch-field]] [auto-ap.views.components.switch-field :refer [switch-field]]
[auto-ap.views.components.typeahead :refer [typeahead-v3]]
[auto-ap.views.pages.data-page :as data-page] [auto-ap.views.pages.data-page :as data-page]
[auto-ap.views.pages.ledger.side-bar :refer [ledger-side-bar]] [auto-ap.views.pages.ledger.side-bar :refer [ledger-side-bar]]
[auto-ap.views.pages.ledger.table :as ledger-table] [auto-ap.views.pages.ledger.table :as ledger-table]
@@ -19,7 +18,6 @@
date-picker date-picker
dispatch-event dispatch-event
local-today local-today
multi-field
query-params query-params
standard standard
str->date str->date

View File

@@ -51,7 +51,8 @@
(re-frame/reg-event-fx (re-frame/reg-event-fx
::unmounted ::unmounted
(fn [{:keys [db]} _] (fn [{:keys [db]} _]
{:dispatch [::data-page/dispose :invoices] {:dispatch-n [[::data-page/dispose :invoices]
[::forms/form-closing ::form/form]]
::forward/dispose [{:id ::updated} ::forward/dispose [{:id ::updated}
{:id ::checks-printed}] {:id ::checks-printed}]
::track/dispose [{:id ::params}]})) ::track/dispose [{:id ::params}]}))

View File

@@ -106,26 +106,6 @@
(when d (when d
(format/parse f d))) (format/parse f d)))
(defn dispatch-date-change [event]
(fn [e]
(re-frame/dispatch (conj event
(if (str/blank? e)
e
(date->str (t/from-default-time-zone (c/from-date e)) standard))))))
(defn dispatch-cljs-date-change [event]
(fn [e]
(re-frame/dispatch (conj event
(if (str/blank? e)
e
(c/to-local-date e))))))
;; TODO inline on-changes causes each field to be rerendered each time. When we fix this
;; let's make sure that we find away not to trigger a re-render for every component any time any form field
;; changes
(defmulti do-bind (fn [_ {:keys [type]}]
type))
(defn with-keys [children] (defn with-keys [children]
(map-indexed (fn [i c] ^{:key i} c) children)) (map-indexed (fn [i c] ^{:key i} c) children))
@@ -178,290 +158,6 @@
0.0)}} 0.0)}}
child])))])]))) child])))])])))
(defn multi-field [{:keys [value]} ]
(let [value-repr (reagent/atom (mapv
(fn [x]
(assoc x :key (random-uuid) :new? false))
value))]
(fn [{:keys [template on-change allow-change? disable-new? disable-remove?]} ]
(let [value @value-repr
already-has-new-row? (= [:key :new?] (keys (last value)))
value (if (or already-has-new-row? disable-new?)
value
(conj value {:key (random-uuid)
:new? true}))]
[:div {:style {:margin-bottom "0.25em"}}
(for [[i override] (map vector (range) value)
:let [is-disabled? (if (= false allow-change?)
(not (boolean (:new? override)))
nil)]
]
^{:key (:key override)}
[:div.level {:style {:margin-bottom "0.25em"}}
[:div.level-left {:style {:padding "0.5em 1em"}
:class (cond
(and (= i (dec (count value)))
(:new? override))
"has-background-light"
(:new? override)
"has-background-info-light"
:else
"")}
(let [template (if (fn? template)
(template override)
template)]
[:<> (for [[idx template] (map vector (range ) template)]
^{:key idx}
[:div.level-item
(update template 1 assoc
:value (let [value (get-in override (get-in template [1 :field])) ;; TODO this is really ugly to support maps or strings
value (if (map? value)
(dissoc value :key :new?)
value)]
(if (= value {})
nil
value))
:disabled (or is-disabled? (get-in template [1 :disabled]))
:on-change (fn [e]
(reset! value-repr
(into []
(filter (fn [r]
(not= [:key :new?] (keys r)))
(assoc-in value
(into [i] (get-in template [1 :field]))
(let [this-value (if (and e (.. e -target))
(.. e -target -value )
e)]
(if (map? this-value)
(update this-value :key (fnil identity (random-uuid)))
this-value))))))
(on-change (mapv
(fn [v]
(dissoc v :new? :key))
@value-repr))))])
])
(when-not disable-remove?
[:div.level-item
[:a.button.level-item
{:disabled is-disabled?
:on-click (fn []
(when-not is-disabled?
(reset! value-repr (into []
(filter (fn [{:keys [key ]}]
(not= key (:key override)))
(filter (fn [r]
(not= [:key :new?] (keys r)))
value))))
(on-change (mapv
(fn [v]
(dissoc v :new? :key))
@value-repr))))}
[:span.icon [:span.icon-remove]]]])
]])]))))
(defmethod do-bind "select" [dom {:keys [field allow-nil? subscription event class spec] :as keys} & rest]
(let [field (if (keyword? field) [field] field)
event (if (keyword? event) [event] event)
keys (assoc keys
:on-change (dispatch-value-change (conj event field))
:value (or (get-in subscription field) "")
:class (str class
(when (and spec (not (s/valid? spec (get-in subscription field))))
" is-danger")))
keys (dissoc keys :field :subscription :event :spec)
options (if allow-nil?
(with-keys (conj rest [:option {:value nil}]))
(with-keys rest))]
(into [dom (dissoc keys :allow-nil?)] options)))
(defmethod do-bind "radio" [dom {:keys [field subscription event class value spec] :as keys} & rest]
(let [field (if (keyword? field) [field] field)
event (if (keyword? event) [event] event)
keys (assoc keys
:on-change (dispatch-value-change (conj event field))
:checked (= (get-in subscription field) value)
:class (str class
(when (and spec (not (s/valid? spec (get-in subscription field ))))
" is-danger")))
keys (dissoc keys :field :subscription :event :spec)]
(into [dom keys] (with-keys rest))))
(defmethod do-bind "checkbox" [dom {:keys [field subscription event class spec] :as keys} & rest]
(let [field (if (keyword? field) [field] field)
event (if (keyword? event) [event] event)
keys (assoc keys
:on-change (dispatch-event (-> event
(conj field)
(conj (not (get-in subscription field)))))
:checked (boolean (get-in subscription field))
:class (str class
(when (and spec (not (s/valid? spec (get-in subscription field ))))
" is-danger")))
keys (dissoc keys :field :subscription :event :spec)]
(into [dom keys] (with-keys rest))))
(defmethod do-bind "multi-field" [dom {:keys [field event subscription class spec] :as keys} & rest]
(let [field (if (keyword? field) [field] field)
event (if (keyword? event) [event] event)
keys (assoc keys
:on-change (fn [value]
(re-frame/dispatch (conj (conj event field) value)))
:value (get-in subscription field)
:class (str class
(when (and spec (not (s/valid? spec (get-in subscription field))))
" is-danger")))
keys (dissoc keys :field :subscription :event :spec)]
(into [dom keys] (with-keys rest))))
(defmethod do-bind "typeahead-v3" [dom {:keys [field event subscription class spec] :as keys} & rest]
(let [field (if (keyword? field) [field] field)
event (if (keyword? event) [event] event)
keys (assoc keys
:on-change (fn [selected]
(re-frame/dispatch (conj (conj event field) selected))
#_(when text-field
(re-frame/dispatch (conj (conj (or text-event event) text-field) text-value))))
:value (get-in subscription field)
:class (str class
(when (and spec (not (s/valid? spec (get-in subscription field))))
" is-danger")))
keys (dissoc keys :field :subscription :event :spec)]
(into [dom keys] (with-keys rest))))
(defmethod do-bind "date" [dom {:keys [field event subscription class spec] :as keys} & rest]
(let [field (if (keyword? field) [field] field)
event (if (keyword? event) [event] event)
selected (get-in subscription field)
keys (assoc keys
:on-change (fn [v]
(re-frame/dispatch (-> event (conj field) (conj v))))
:value selected
:class (str class
(when (and spec (not (s/valid? spec (get-in subscription field))))
" is-danger")))
keys (dissoc keys :field :subscription :event :spec)]
(into [dom keys] (with-keys rest))))
(defmethod do-bind "expense-accounts" [dom {:keys [field event subscription class spec] :as keys} & rest]
(let [field (if (keyword? field) [field] field)
event (if (keyword? event) [event] event)
keys (assoc keys
:value (get-in subscription field)
:event (conj event field)
:class (str class
(when (and spec (not (s/valid? spec (get-in subscription field))))
" is-danger")))
keys (dissoc keys :field :subscription :spec)]
(into [dom keys] (with-keys rest))))
(defmethod do-bind "button-radio" [dom {:keys [field event subscription class spec] :as keys} & rest]
(let [field (if (keyword? field) [field] field)
event (if (keyword? event) [event] event)
keys (assoc keys
:value (get-in subscription field)
:on-change (fn [v]
(re-frame/dispatch (-> event (conj field) (conj v))))
:class (str class
(when (and spec (not (s/valid? spec (get-in subscription field))))
" is-danger")))
keys (dissoc keys :field :event :subscription :spec)]
(into [dom keys] (with-keys rest))))
(defmethod do-bind "number" [dom {:keys [field event subscription class spec] :as keys} & rest]
(let [field (if (keyword? field) [field] field)
event (if (keyword? event) [event] event)
keys (assoc keys
:on-change (fn [e]
(.preventDefault e)
(re-frame/dispatch (-> event
(conj field)
(conj (let [val (.. e -target -value)]
(cond (and val
(re-matches #"[\-]?(\d+)(\.\d{2})?" val))
(js/parseFloat val)
(str/blank? val )
nil
:else
val))))))
:value (get-in subscription field)
:class (str class
(when (and spec (not (s/valid? spec (get-in subscription field))))
" is-danger")))
keys (dissoc keys :field :subscription :event :spec)]
(into [dom keys] (with-keys rest))))
(defmethod do-bind "textarea->table" [dom {:keys [field event subscription class spec] :as keys} & rest]
(let [field (if (keyword? field) [field] field)
event (if (keyword? event) [event] event)
keys (assoc keys
:on-change (fn [x]
(re-frame/dispatch (-> event
(conj field)
(conj x))))
:value (get-in subscription field)
:class (str class
(when (and spec (not (s/valid? spec (get-in subscription field))))
" is-danger")))
keys (dissoc keys :field :subscription :event :spec)]
(into [dom keys] (with-keys rest))))
(defmethod do-bind "money" [dom {:keys [field event subscription class spec] :as keys} & rest]
(let [field (if (keyword? field) [field] field)
event (if (keyword? event) [event] event)
keys (assoc keys
:on-change (fn [x]
(re-frame/dispatch (-> event
(conj field)
(conj x))))
:value (get-in subscription field)
:class (str class
(when (and spec (not (s/valid? spec (get-in subscription field))))
" is-danger")))
keys (dissoc keys :field :subscription :event :spec)]
(into [dom keys] (with-keys rest))))
(defmethod do-bind :default [dom {:keys [field event subscription class spec] :as keys} & rest]
(let [field (if (keyword? field) [field] field)
event (if (keyword? event) [event] event)
keys (assoc keys
:on-change (dispatch-value-change (conj event field))
:value (get-in subscription field)
:class (str class
(when (and spec (not (s/valid? spec (get-in subscription field))))
" is-danger")))
keys (dissoc keys :field :subscription :event :spec)]
(into [dom keys] (with-keys rest))))
(defn bind-field [all]
(apply do-bind all))
(defn horizontal-field [label & controls]
[:div.field.is-horizontal
(when label
[:div.field-label
label
])
(into
[:div.field-body]
(with-keys (map (fn [x] [:div.field x]) controls)))])
(defn coerce-date [d] (defn coerce-date [d]
(cond (and (string? d) (cond (and (string? d)
(some->> (re-find #"^(\d{4})" d) (some->> (re-find #"^(\d{4})" d)