-
Notifications
You must be signed in to change notification settings - Fork 1
/
combobox.cljs
93 lines (84 loc) · 4.27 KB
/
combobox.cljs
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
(ns om-widgets.combobox
(:require [om.core :as om :include-macros true]
[om.dom :as dom :include-macros true]
[schema.core :as s :include-macros true]
[sablono.core :refer-macros [html]]
[om-widgets.utils :as utils]
[cljs.reader :as reader]
[om-widgets.utils :as u]
[pallet.thread-expr :as th]))
(defn- option
[[k v] owner]
(reify
om/IRenderState
(render-state [_ {:keys [selected]}]
(let [value (if (map? k)
(into (sorted-map) k)
k)
opts (->> {:value (pr-str {:value value})}
(merge (when (= value selected) {:selected 1})))]
(html
(if (map? v)
(u/make-childs [:optgroup {:label value}] (om/build-all option v))
(dom/option (clj->js opts) v)))))))
(defn- combo
[app owner]
(reify
om/IRenderState
(render-state [this {:keys [options path] :as state}]
(let [flatten-opts (reduce (fn [acc [k v]] (conj acc (if (coll? v) v {k v}))) {} options)
value (when (get flatten-opts (utils/om-get app path)) (utils/om-get app path))
opts (->> {:onChange (fn [e]
(let [value (reader/read-string (.. e -target -value))]
(utils/om-update! app
(:path state)
(:value value))
(when (utils/atom? app)
(om/refresh! owner))
(when (:onChange state)
((:onChange state) (:value value)))
(.preventDefault e)))
:className (clojure.string/join " " ["om-widgets-combobox" (:class-name state)
(when (and (not (:disabled state)) (:read-only state))
"om-widgets-combobox-readonly")])
:disabled (or (:disabled state) (if (:read-only state) true false))
:onBlur (:onBlur state)
:value (cond
(nil? value) (pr-str {:value nil})
(map? value) (pr-str {:value (into (sorted-map) value)})
:else (pr-str {:value value}))
:id (:id state)}
(merge (when (:tabIndex state)) {:tabIndex (:tabIndex state)})
(merge (when (:read-only state) {:readOnly true})))]
(apply dom/select (clj->js opts)
;; create an empty value to override <select> default behaviour of always selecting the first item
;; this plays well with required values or form validation
(apply conj [(dom/option (clj->js (-> {:value (pr-str {:value nil})
:disabled true}
(merge (when (nil? value)
{:selected 1})))))]
(om/build-all option options {:state {:selected value}})))))))
;; ---------------------------------------------------------------------
;; Schema
(def ComboboxSchema
{:options [s/Any s/Any]
(s/optional-key :id) s/Str
(s/optional-key :class-name) s/Str
(s/optional-key :onChange) (s/pred fn?)
(s/optional-key :read-only) s/Bool
(s/optional-key :disabled) s/Bool
(s/optional-key :tabIndex) s/Int})
;; ---------------------------------------------------------------------
;; Public
(defn combobox
[app path {:keys [options class-name id read-only disabled onBlur onChange tabIndex]
:or {class-name ""}}]
(om/build combo app {:state {:options options
:class-name class-name
:read-only read-only
:disabled disabled
:tabIndex tabIndex
:id id
:onBlur onBlur
:onChange onChange
:path path}}))