|
|
@ -4,19 +4,8 @@
|
|
4
|
4
|
[om.core :as om :include-macros true]
|
|
5
|
5
|
[om.dom :as dom :include-macros true]))
|
|
6
|
6
|
|
|
7
|
|
(enable-console-print!)
|
|
8
|
|
|
|
9
|
|
(extend-type js/Boolean
|
|
10
|
|
ICloneable
|
|
11
|
|
(-clone [b] b))
|
|
12
|
7
|
|
|
13
|
|
(extend-type number
|
|
14
|
|
ICloneable
|
|
15
|
|
(-clone [n] (js/Number. n)))
|
|
16
|
|
|
|
17
|
|
(extend-type string
|
|
18
|
|
ICloneable
|
|
19
|
|
(-clone [s] (js/String. s)))
|
|
|
8
|
(enable-console-print!)
|
|
20
|
9
|
|
|
21
|
10
|
; data for one node: type and optionally data
|
|
22
|
11
|
; if data is present, fill the node with the data
|
|
|
@ -75,35 +64,42 @@
|
|
75
|
64
|
(map? type) (:type type)
|
|
76
|
65
|
:else type)))
|
|
77
|
66
|
|
|
78
|
|
(defmethod make-typed-input 'Boolean [bool owner]
|
|
|
67
|
(defmethod make-typed-input 'Boolean [m owner {:keys [type key val]}]
|
|
79
|
68
|
(om/component
|
|
80
|
69
|
(dom/input #js {:type "checkbox"
|
|
81
|
|
:checked (.valueOf bool)
|
|
82
|
|
:onChange #(om/update! bool (fn [_ n] n) (js/Boolean. (.. % -target -checked)))})))
|
|
|
70
|
:checked (.valueOf (or val (empty-value type)))
|
|
|
71
|
:onChange #(om/transact! m key (fn [_ n] n) (js/Boolean. (.. % -target -checked)))})))
|
|
83
|
72
|
|
|
84
|
|
(defmethod make-typed-input 'Number [number owner]
|
|
|
73
|
(defmethod make-typed-input 'Number [m owner {:keys [key val]}]
|
|
85
|
74
|
(om/component
|
|
86
|
75
|
(dom/input #js {:type "number"
|
|
87
|
|
:value (om/value number)
|
|
88
|
|
:onChange #(om/update! number (fn [_ n] n) (js/parseFloat (.. % -target -value)))})))
|
|
|
76
|
:value val
|
|
|
77
|
:onChange (update-on-change! m key js/parseFloat)})))
|
|
89
|
78
|
|
|
90
|
79
|
(def keyword-pattern "^:(\\w+|\\w+(\\.\\w+)*\\/\\w+)$")
|
|
91
|
80
|
|
|
92
|
|
(defmethod make-typed-input 'Keyword [kw owner]
|
|
|
81
|
(defmethod make-typed-input 'Keyword [m owner {:keys [key val]}]
|
|
93
|
82
|
(om/component
|
|
94
|
83
|
(dom/input #js {:type "text"
|
|
95
|
|
:value (om/value kw)
|
|
|
84
|
:value val
|
|
96
|
85
|
:pattern keyword-pattern
|
|
97
|
|
:onChange (fn [ev]
|
|
98
|
|
(when (valid? (.-target ev))
|
|
99
|
|
(om/update! kw (fn [o n] n) (or (read-keyword (.. ev -target -value))
|
|
100
|
|
(empty-value 'Keyword)))))})))
|
|
101
|
|
|
|
102
|
|
(defmethod make-typed-input 'String [string owner]
|
|
|
86
|
:onChange (update-on-change! m key #(or (read-keyword %) (empty-value type)))})))
|
|
|
87
|
|
|
|
88
|
(defn update-on-change!
|
|
|
89
|
([m k] (update-on-change! m k identity))
|
|
|
90
|
([m k transform-fn] (update-on-change! m k transform-fn false))
|
|
|
91
|
([m k transform-fn optional?]
|
|
|
92
|
(fn [ev]
|
|
|
93
|
(let [new-val (transform-fn (.. ev -target -value))]
|
|
|
94
|
(if (and optional? (empty? new-val))
|
|
|
95
|
(om/update! m dissoc k)
|
|
|
96
|
(om/transact! m k (fn [_ n] n) new-val))))))
|
|
|
97
|
|
|
|
98
|
(defmethod make-typed-input 'String [m owner {:keys [type key val optional?]}]
|
|
103
|
99
|
(om/component
|
|
104
|
100
|
(dom/input #js {:type "text"
|
|
105
|
|
:value (om/value string)
|
|
106
|
|
:onChange #(om/update! string (fn [_ n] n) (.. % -target -value))})))
|
|
|
101
|
:value val
|
|
|
102
|
:onChange (update-on-change! m key identity optional?)})))
|
|
107
|
103
|
|
|
108
|
104
|
(defmethod make-typed-input 'Value [value owner]
|
|
109
|
105
|
(om/component
|
|
|
@ -116,28 +112,32 @@
|
|
116
|
112
|
(keyword? value) {:type "text", :pattern keyword-pattern}
|
|
117
|
113
|
:else {:type "text"}))))))
|
|
118
|
114
|
|
|
119
|
|
(defmethod make-typed-input 'U [value owner {type :type}]
|
|
|
115
|
(defmethod make-typed-input 'U [m owner {:keys [type key val]}]
|
|
120
|
116
|
(om/component
|
|
121
|
|
(dom/select #js {:onChange #(om/update! value (fn [_ n] n) (r/read-string (.. % -target -value)))}
|
|
|
117
|
(dom/select #js {:onChange (update-on-change! m key r/read-string)}
|
|
122
|
118
|
(into-array
|
|
123
|
119
|
(map (fn [[_ v]]
|
|
124
|
120
|
(dom/option nil (str v)))
|
|
125
|
121
|
(rest type))))))
|
|
126
|
122
|
|
|
127
|
123
|
(defmethod make-typed-input 'HMap [m owner {type :type}]
|
|
128
|
|
(om/component
|
|
129
|
|
(dom/div nil
|
|
130
|
|
(dom/span nil "{")
|
|
131
|
|
(into-array
|
|
132
|
|
(map (fn [[k v]]
|
|
133
|
|
(dom/div #js {:className "field"}
|
|
134
|
|
(dom/label nil (str k))
|
|
135
|
|
(om/build make-typed-input v {:opts {:type (k (nth type 2))}})))
|
|
136
|
|
m))
|
|
137
|
|
(dom/span nil "}"))))
|
|
|
124
|
(let [hmap (apply hash-map (rest type))
|
|
|
125
|
required (:mandatory hmap)
|
|
|
126
|
optional (:optional hmap)]
|
|
|
127
|
(om/component
|
|
|
128
|
(dom/div nil
|
|
|
129
|
(dom/span nil "{")
|
|
|
130
|
(into-array
|
|
|
131
|
(map (fn [[k t]]
|
|
|
132
|
(dom/div #js {:className "field"}
|
|
|
133
|
(dom/label nil (str k))
|
|
|
134
|
(om/build make-typed-input m {:opts {:type t, :key k, :val (k m)
|
|
|
135
|
:optional? (contains? optional k)}})))
|
|
|
136
|
(merge required optional)))
|
|
|
137
|
(dom/span nil "}")))))
|
|
138
|
138
|
|
|
139
|
139
|
(def app-state
|
|
140
|
|
(let [type '(HMap :mandatory
|
|
|
140
|
(let [type '[HMap :mandatory
|
|
141
|
141
|
{:name {:type String :default "Paul"},
|
|
142
|
142
|
:age {:type Number, :default 10},
|
|
143
|
143
|
:language (U (Value :en)
|
|
|
@ -145,7 +145,9 @@
|
|
145
|
145
|
(Value :fr)
|
|
146
|
146
|
(Value :jp))
|
|
147
|
147
|
:fun Boolean
|
|
148
|
|
:gender Keyword})]
|
|
|
148
|
:gender Keyword}
|
|
|
149
|
:optional
|
|
|
150
|
{:secret-skill String}]]
|
|
149
|
151
|
(atom
|
|
150
|
152
|
{:type type
|
|
151
|
153
|
:data (empty-value type)})))
|