Created
October 5, 2018 19:08
-
-
Save Conaws/d42029554d791d9494b1b6ad9a6672f8 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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