Skip to content

Instantly share code, notes, and snippets.

@Conaws
Created October 5, 2018 19:08
Show Gist options
  • Save Conaws/d42029554d791d9494b1b6ad9a6672f8 to your computer and use it in GitHub Desktop.
Save Conaws/d42029554d791d9494b1b6ad9a6672f8 to your computer and use it in GitHub Desktop.
(ns undead.cards.multi
(:require
[posh.core :as posh :refer [posh!]]
[cljs.pprint :refer [pprint]]
[re-com.core :as rc :refer [v-box box input-text h-box]]
[datascript.core :as d]
[undead.util :refer [deref-or-value]]
[clojure.string :as str]
[reagent-forms.core :refer [bind-fields]]
[reagent.core :as r]
[re-frame.core :refer [subscribe dispatch]])
(:require-macros
[cljs.test :refer [testing is]]
[devcards.core
:as dc
:refer [defcard defcard-doc defcard-rg deftest]]))
(defn value-of [event]
(-> event .-target .-value))
(defn friend-source2 [text]
(filter
#(-> % (.toLowerCase %) (.indexOf text) (> -1))
["Alice" "Alan" "Bob" "Beth" "Jim" "Jane" "Kim" "Rob" "Zoe"]))
#_(defcard-rg re-com-test
[rc/typeahead :data-source friend-source2])
(defn multi [{:keys [highlight-class
placeholder
item-class
list-class
options
save! selections on-delete]}]
(let [a (r/atom "")
selected-index (r/atom -1)
typeahead-hidden? (r/atom false)
mouse-on-list? (r/atom false)
]
(fn []
(let [options (if (clojure.string/blank? @a)
[]
(filter
#(-> % (.toLowerCase %) (.indexOf (.toLowerCase @a)) (> -1))
options))
matching-options (filter (comp not (set @selections)) options)
choose-selected #(if (and (not-empty matching-options)
(> @selected-index -1))
(let [choice (nth matching-options @selected-index)]
(save! choice)
(reset! selected-index 0)
(reset! a ""))
(when (not (str/blank? @a))
(do
(save! @a)
(reset! selected-index 0)
(reset! a ""))))
]
[:div.tags.flex
[:div.tags-output
(when @selections
(for [x @selections]
^{:key x}[:button {:on-click #(swap! selections (fn [y] (remove #{x} y)))}(str x)]
))
[:input.tags-input
{:value @a
:placeholder placeholder
;; (if (empty? @selections ) placeholder nil)
:on-change #(reset! a (-> % .-target .-value))
:on-key-down #(do
(case (.-which %)
38 (do
(.preventDefault %)
(when-not (= @selected-index -1)
(swap! selected-index dec)))
40 (do
(.preventDefault %)
(when-not (= @selected-index (dec (count matching-options)))
(swap! selected-index inc)))
9 (choose-selected)
13 (choose-selected)
8 (when (clojure.string/blank? @a)
(on-delete))
27 (do #_(reset! typeahead-hidden? true)
(reset! selected-index -1))
"default"))}]
[:ul {:style
{:display (if (or (empty? matching-options) @typeahead-hidden?) :none :block) }
:class list-class
:on-mouse-enter #(reset! mouse-on-list? true)
:on-mouse-leave #(reset! mouse-on-list? false)}
(doall
(map-indexed
(fn [index result]
[:li {:tab-index index
:key index
:class (if (= @selected-index index) highlight-class item-class)
:on-mouse-over #(do
(reset! selected-index (js/parseInt (.getAttribute (.-target %) "tabIndex"))))
:on-click #(do
(reset! a "")
(save! result)
)}
result])
matching-options))]]])
)))
(defcard-rg tags-example
(let [selections (r/atom ["A" "B" "Conor Rules"])]
[multi {:highlight-class "highlight"
:selections selections
:on-delete #(swap! selections pop)
:save! #(swap! selections conj %)
:options ["Reagent""Re-frame""Re-com""Reaction"]}])
)
(def schema {:node/title {:db/unique :db.unique/identity}
:node/prototype-of {:db/valueType :db.type/ref
:db/cardinality :db.cardinality/many}
:node/similar-to {:db/valueType :db.type/ref
:db/cardinality :db.cardinality/many}
:set/attributes {:db/cardinality :db.cardinality/many}
:set/members {:db/valueType :db.type/ref
:db/cardinality :db.cardinality/many}})
(defonce lconn2 (d/create-conn schema))
(posh! lconn2)
(d/transact! lconn2
[{:node/title "A"
:set/members [{:node/title "A1"}
{:node/title "A2"}
{:node/title "A3"}
{:node/title "A4"}
{:node/title "A5"}
{:node/title "A6"}
]}
{:node/title "B"
:set/members [{:node/title "B1"}
{:node/title "B2"}
{:node/title "A3"}
{:node/title "A4"}
{:node/title "A5"}
{:node/title "A6"}
]}])
(defn multi-drop1 [nodes]
(let [nodes nodes
selections (r/atom [])
selection-id (r/atom nil)]
(fn []
(let [new-nodes (keep (fn [[e t ty]]
(if (not ((set @selections)
e))
{:id e :label t
:group ty}
)) @nodes)
sorted-nodes (sort-by :group new-nodes)]
[rc/v-box
:children [[rc/h-box
:children [(map (fn [e]
[:button
{:on-click #(reset! selections
(vec (remove #{e} @selections ))
)}
e
]
) @selections)
[rc/single-dropdown
:choices sorted-nodes
:placeholder "If this then that"
:filter-box? true
:width "200px"
:model selection-id
:on-change #(do
(reset! selection-id nil)
(swap! selections conj %))]]]]
]))))
(defn select-text-nodes []
[multi-drop1
(posh/q lconn2 '[:find ?e ?text ?pt
:where [?e :node/title ?text]
[?p :set/members ?e]
[?p :node/title ?pt]])])
(defcard-rg stest
[select-text-nodes])
(defn friend-source [text]
(filter
#(-> % (.toLowerCase %) (.indexOf text) (> -1))
["Alice" "Alan" "Bob" "Beth" "Jim" "Jane" "Kim" "Rob" "Zoe"]))
#_(defn typeahead
[{:keys [id
data-source
input-class
list-class
item-class
highlight-class
input-placeholder
result-fn
choice-fn
clear-on-focus?]
:as attrs
:or {result-fn identity
choice-fn identity
clear-on-focus? true}}
{:keys [save!]}]
(let [input-val (r/atom "")
typeahead-hidden? (r/atom true)
mouse-on-list? (r/atom false)
selected-index (r/atom -1)
selections (r/atom [])
choose-selected #(when (and (not-empty @selections) (> @selected-index -1))
(let [choice (nth @selections @selected-index)]
(save! id choice)
(choice-fn choice)
(reset! typeahead-hidden? true)))]
(fn []
[:div.bblack
[:input {:type :text
:placeholder input-placeholder
:class input-class
:value @input-val
:on-focus #(when clear-on-focus? (save! id nil))
:on-blur #(when-not @mouse-on-list?
(reset! typeahead-hidden? true)
(reset! selected-index -1))
:on-change #(reset! input-val (-> % .-target .-value))
;; (when-let [value (str/trim (value-of %))]
;; (reset! selections (data-source (.toLowerCase value)))
;; (reset! typeahead-hidden? false)
;; (reset! selected-index -1))
:on-key-down #(do
(case (.-which %)
38 (do
(.preventDefault %)
(when-not (= @selected-index 0)
(swap! selected-index dec)))
40 (do
(.preventDefault %)
(when-not (= @selected-index (dec (count @selections)))
(save! id (value-of %))
(swap! selected-index inc)))
9 (choose-selected)
13 (choose-selected)
27 (do (reset! typeahead-hidden? true)
(reset! selected-index 0))
"default"))}]
[:h1
(pr-str @input-val)
(pr-str @selected-index)
(pr-str @selections)]
])))
;; (js/console.log (not (#{1 2} 1)))
(defn multi-complete [{:keys [highlight-class
placeholder
item-class
list-class
container-class
suggestions
filter-fn
save!
selections
selection-class
on-delete]
:or {container-class "tags"
selection-class "tags-output-item"
filter-fn (fn [typeahead-atom x]
(-> (.toLowerCase x)
(.indexOf (.toLowerCase @typeahead-atom))
(> -1)))}
:as props}]
(let [a (r/atom "")
selected-index (r/atom -1)
typeahead-hidden? (r/atom false)
mouse-on-list? (r/atom false)
reset-internals! #(do
(reset! selected-index 0)
(reset! a ""))]
(fn [props]
(let [suggestions (deref-or-value suggestions)
options (if (clojure.string/blank? @a)
[]
(filter (partial filter-fn a) suggestions))
matching-options (filter (comp not (set @selections)) options)
choose-selected #(if (and (not-empty matching-options)
(> @selected-index -1))
(let [choice (nth matching-options @selected-index)]
(save! choice)
(reset-internals!))
(when (and (not (str/blank? @a))
;; might be nice to return a warning to user
;; idea is not to allow duplicate items
(not ((set @selections) @a)))
(do
(save! @a)
(reset-internals!))))]
[:div.tags.flex
[:div.tags-output
(when @selections
(for [x @selections]
^{:key x}
[:span
{:class selection-class
:on-click #(swap! selections (fn [y] (remove #{x} y)))}
(str x)]
))
[:input.tags-input
{:value @a
:placeholder placeholder
:on-change #(reset! a (-> % .-target .-value))
:on-key-down #(do
(case (.-which %)
38 (do
(.preventDefault %)
(when-not (= @selected-index -1)
(swap! selected-index dec)))
40 (do
(.preventDefault %)
(when-not (= @selected-index (dec (count matching-options)))
(swap! selected-index inc)))
9 (choose-selected)
13 (choose-selected)
8 (when (clojure.string/blank? @a)
(on-delete))
27 (do #_(reset! typeahead-hidden? true)
(reset! selected-index -1))
"default"))}]
[:ul {:style
{:display (if (or (empty? matching-options) @typeahead-hidden?) :none :block) }
:class list-class
:on-mouse-enter #(reset! mouse-on-list? true)
:on-mouse-leave #(reset! mouse-on-list? false)}
(doall
(map-indexed
(fn [index result]
[:li {:tab-index index
:key index
:class (if (= @selected-index index) highlight-class item-class)
:on-mouse-over #(do
(reset! selected-index (js/parseInt (.getAttribute (.-target %) "tabIndex"))))
:on-click #(do
(reset! a "")
(save! result)
)}
result])
matching-options))]]])
)))
(defcard-rg tags-example2
(let [selections (r/atom [])]
[multi-complete {:highlight-class "highlight"
:selections selections
:on-delete #(swap! selections pop)
:save! #(swap! selections conj %)
:suggestions ["Reagent""Re-frame""Re-com""Reaction"]}])
)
#_(defmethod init-field :typeahead
[[type {:keys [id data-source input-class list-class item-class highlight-class input-placeholder result-fn choice-fn clear-on-focus?]
:as attrs
:or {result-fn identity
choice-fn identity
clear-on-focus? true}}] {:keys [doc get save!]}]
(let [typeahead-hidden? (atom true)
mouse-on-list? (atom false)
selected-index (atom -1)
selections (atom [])
choose-selected #(when (and (not-empty @selections) (> @selected-index -1))
(let [choice (nth @selections @selected-index)]
(save! id choice)
(choice-fn choice)
(reset! typeahead-hidden? true)))]
(render-element attrs doc
[type
[:input {:type :text
:placeholder input-placeholder
:class input-class
:value (let [v (get id)]
(if-not (iterable? v)
v (first v)))
:on-focus #(when clear-on-focus? (save! id nil))
:on-blur #(when-not @mouse-on-list?
(reset! typeahead-hidden? true)
(reset! selected-index -1))
:on-change #(when-let [value (trim (value-of %))]
(reset! selections (data-source (.toLowerCase value)))
(save! id (value-of %))
(reset! typeahead-hidden? false)
(reset! selected-index -1))
:on-key-down #(do
(case (.-which %)
38 (do
(.preventDefault %)
(when-not (= @selected-index 0)
(swap! selected-index dec)))
40 (do
(.preventDefault %)
(when-not (= @selected-index (dec (count @selections)))
(save! id (value-of %))
(swap! selected-index inc)))
9 (choose-selected)
13 (choose-selected)
27 (do (reset! typeahead-hidden? true)
(reset! selected-index 0))
"default"))}]
[:ul {:style {:display (if (or (empty? @selections) @typeahead-hidden?) :none :block) }
:class list-class
:on-mouse-enter #(reset! mouse-on-list? true)
:on-mouse-leave #(reset! mouse-on-list? false)}
(doall
(map-indexed
(fn [index result]
[:li {:tab-index index
:key index
:class (if (= @selected-index index) highlight-class item-class)
:on-mouse-over #(do
(reset! selected-index (js/parseInt (.getAttribute (.-target %) "tabIndex"))))
:on-click #(do
(reset! typeahead-hidden? true)
(save! id result)
(choice-fn result))}
(result-fn result)])
@selections))]])))
(defcard-rg draw-button
(fn []
[:button#draw "Hey"]))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment