-
Notifications
You must be signed in to change notification settings - Fork 0
/
field.cljc
197 lines (180 loc) · 8.08 KB
/
field.cljc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
(ns ez-form.field
(:require #?(:clj [clojure.edn :refer [read-string]])
[clojure.string :as str]
[ez-form.common :refer [get-first]]
[ez-form.decorate :refer [add-decor
add-error-decor
add-help-decor
add-label-decor
add-text-decor]]
[ez-form.keywordize :refer [kw->string]]
#?@(:cljs [[cljs.reader :refer [read-string]]
[reagent.core :as r]])))
(defmulti ->transform (fn [field data] (:transform field)))
(defmethod ->transform :edn [_ data]
(read-string data))
(defmethod ->transform :default [_ data]
data)
(defn value-of [field element]
(->transform field (-> element .-target .-value)))
(defn errors
"Send in a field from a map and get back a list of error messages"
[{:keys [errors] :as field}]
;; for clojure errors are always run per display
#?(:clj (if errors
(map #(add-error-decor field %) errors)))
;; for clojurescript always decorate errors, we just
;; do not show them when they're not present
#?(:cljs (add-decor :error field)))
(defn label [field]
(add-label-decor field))
(defn text [field]
(add-text-decor field))
(defn help [field]
(add-help-decor field))
(defn option [selected-value opt]
(let [[value text] (if (sequential? opt)
[(first opt) (second opt)]
[opt opt])
;; have selected-value as a set
;; so that we can have multiple options
;; set as true
selected-value (if (sequential? selected-value)
(set selected-value)
(set [selected-value]))
opts
#?(:clj (if (some selected-value [value])
{:value value :selected true}
{:value value}))
#?(:cljs {:value (str value)})]
^{:key text} [:option opts text]))
(defn get-opts [field keys form-options]
(merge
(if (contains? (get-in form-options [:css :field]) :all)
{:class (get-in form-options [:css :field :all])})
(if (contains? (get-in form-options [:css :field]) (:type field))
{:class (get-in form-options [:css :field (:type field)])})
(:opts field)
(into {} (map (fn [[k v]]
(if (nil? v)
[k v]
[k (cond
(keyword? v) (kw->string v)
(fn? v) (v field)
:else v)])) (select-keys field keys)))))
(defmulti field (fn [field form-options] (:type field)))
(defmethod field :checkbox [field form-options]
#?(:clj
(let [id (get-first field :id :name)
value-added (:value-added field)
checked? (:checked? field)
options (:options field)
opts (get-opts field [:class :name :value :type] form-options)]
(if options
(let [value-added (cond (string? value-added) (str/split value-added #",")
:else value-added)]
(map (fn [option]
(let [[value label] (if (sequential? option)
option
[option option])
id (str (kw->string id) "-" value)]
[:div
[:input (merge {:value value}
opts
{:id id}
(if (or
;; default checked, but only if
;; value-added is empty
(and checked?
(nil? value-added))
;; check if value equals value-added
(some #(= value %) value-added))
{:checked true}))]
[:label {:for id} label]]))
options))
[:input (merge {:value value-added}
opts
{:id id}
(if (or
;; default checked, but only if value-added is empty
(and checked?
(nil? value-added))
;; checked if value-added is non-nil and non-false
(and (not (nil? value-added))
(not (false? value-added))
(= (:value field) value-added)))
{:checked true}))])))
;; leave multiple checkboxes for now
#?(:cljs
(let [id (get-first field :id :name)
c (:cursor field)
opts (get-opts field [:class :name :type] form-options)]
[:input (merge {:on-change #(reset! c (not (true? @c)))
:checked (true? @c)}
opts
{:id id})])))
(defmethod field :boolean [f form-options]
(field (assoc f :type :checkbox) form-options))
(defmethod field :radio [field form-options]
(let [id (get-first field :id :name)
checked? (:checked field)
opts (get-opts field [:class :name :value :type] form-options)
value (or (:value-added field) (:value field))
#?@(:cljs [c (:cursor field)])]
#?(:clj [:input (merge {:value value} opts {:id id} (if (or checked?
(= value (:value opts)))
{:checked true}))])
#?(:cljs [:input (merge {:value @c
:checked (= @c (:value opts))
:on-change #(reset! c (value-of field %))} opts {:id id})])))
(defmethod field :html [field form-options]
(if-let [f (:fn field)]
(f field form-options)))
(defmethod field :textarea [field form-options]
(let [id (get-first field :id :name)
opts (get-opts field [:class :name] form-options)
#?@(:clj [value (or (:value field) (:value-added field))])
#?@(:cljs [c (:cursor field)])]
#?(:clj
[:textarea (merge opts {:id id}) (or value "")])
#?(:cljs
[:textarea (merge opts {:id id :on-change #(reset! c (value-of field %)) :value (or @c "")})])))
#?(:cljs
(defn ->options-checkup [args]
(reduce (fn [out v]
(let [[value text] (if (sequential? v) v [v v])]
(assoc out (str value) value)))
{} args)))
(defmethod field :dropdown [field form-options]
(let [id (get-first field :id :name)
opts (get-opts field [:class :name] form-options)
options (:options field)
#?@(:clj [value (or (:value field) (:value-added field) "")])
#?@(:cljs [c (:cursor field)
options-checkup (->options-checkup (if (fn? options)
(options field form-options)
options))
cljs-opts {:value (or (str @c) "")
:on-change #(reset! c (get options-checkup (value-of field %)))}])]
[:select (merge opts
{:type :select
:id id}
#?(:cljs cljs-opts))
#?(:cljs
(if (fn? options)
(map #(option @c %) (options field form-options))
(map #(option @c %) options)))
#?(:clj
(if (fn? options)
(map #(option value %) (options field form-options))
(map #(option value %) options)))]))
(defmethod field :default [field form-options]
(let [id (get-first field :id :name)
opts (get-opts field [:placeholder :class :name :type] form-options)
#?@(:clj [value (or (:value field) (:value-added field))])
#?@(:cljs [c (:cursor field)])]
[:input (merge {:type :text
#?@(:clj [:value (or value "")])
#?@(:cljs [:value (or @c "")
:on-change #(reset! c (value-of field %))])
:id id} opts)]))