created a typeahead. neat.
This commit is contained in:
@@ -92,6 +92,8 @@
|
||||
:jar true
|
||||
:compiler {:main auto-ap.core
|
||||
:output-to "resources/public/js/compiled/app.js"
|
||||
:npm-deps {:react-autocomplete "1.8.1"}
|
||||
:install-deps true
|
||||
:optimizations :advanced
|
||||
:closure-defines {goog.DEBUG false}
|
||||
:pretty-print false}}
|
||||
|
||||
@@ -247,6 +247,53 @@
|
||||
background-color:#F5F5F5;
|
||||
}
|
||||
.table { table-layout: fixed }
|
||||
|
||||
.typeahead {
|
||||
position:relative;
|
||||
}
|
||||
|
||||
.typeahead-menu {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
float: left;
|
||||
min-width: 160px;
|
||||
padding: 5px 0;
|
||||
margin: 2px 0 0;
|
||||
list-style: none;
|
||||
font-size: 14px;
|
||||
text-align: left;
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #cccccc;
|
||||
border: 1px solid rgba(0, 0, 0, 0.15);
|
||||
border-radius: 4px;
|
||||
-webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
|
||||
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
.typeahead-suggestion {
|
||||
display: block;
|
||||
padding: 3px 20px;
|
||||
clear: both;
|
||||
font-weight: normal;
|
||||
line-height: 1.42857143;
|
||||
color: #333333;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.typeahead-suggestion:hover,
|
||||
.typeahead-suggestion:focus,
|
||||
.typeahead-menu:not(:hover) .typeahead-highlighted
|
||||
{
|
||||
color: #ffffff;
|
||||
text-decoration: none;
|
||||
outline: 0;
|
||||
background-color: #00d1b2;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
[auto-ap.subs :as subs]
|
||||
[auto-ap.routes :as routes]
|
||||
[auto-ap.effects :as effects]
|
||||
[auto-ap.utils :refer [by]]
|
||||
[venia.core :as v]
|
||||
[bidi.bidi :as bidi]
|
||||
[goog.crypt.base64 :as b64]
|
||||
@@ -35,9 +36,11 @@
|
||||
:user token)
|
||||
:graphql {:token token
|
||||
:query-obj {:venia/queries [[:company
|
||||
[:id :name [:bank-accounts [:id :number :check-number :name]]]]]}
|
||||
[:id :name [:bank-accounts [:id :number :check-number :name]]]]
|
||||
[:vendor
|
||||
[:id :name]]]}
|
||||
|
||||
:on-success [::received-companies]}}))))
|
||||
:on-success [::received-initial]}}))))
|
||||
|
||||
(re-frame/reg-event-db
|
||||
::toggle-menu
|
||||
@@ -49,19 +52,19 @@
|
||||
(fn [{:keys [db]} [_ token user]]
|
||||
{:graphql {:token token
|
||||
:query-obj {:venia/queries [[:company
|
||||
[:id :name]]]}
|
||||
[:id :name [:bank-accounts [:id :number :check-number :name]]]]
|
||||
[:vendor
|
||||
[:id :name]]]}
|
||||
|
||||
:on-success [::received-companies]}
|
||||
:on-success [::received-initial]}
|
||||
:db (assoc db :user (assoc user :token token))}))
|
||||
|
||||
(re-frame/reg-event-db
|
||||
::received-companies
|
||||
(fn [db [_ {companies :company}]]
|
||||
|
||||
(assoc db :companies (reduce (fn [companies company]
|
||||
(assoc companies (:id company) company))
|
||||
{}
|
||||
companies))))
|
||||
::received-initial
|
||||
(fn [db [_ {companies :company vendors :vendor :as x}]]
|
||||
(-> db
|
||||
(assoc :companies (by :id companies) )
|
||||
(assoc :vendors (by :id vendors) ))))
|
||||
|
||||
(re-frame/reg-event-db
|
||||
::swap-company
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
(ns auto-ap.views.pages.unpaid-invoices
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[reagent.core :as r]
|
||||
[clojure.string :as str]
|
||||
[auto-ap.entities.companies :as company]
|
||||
[auto-ap.entities.vendors :as vendor]
|
||||
[auto-ap.views.utils :refer [dispatch-event bind-field horizontal-field]]
|
||||
@@ -238,9 +240,60 @@
|
||||
:max outstanding-balance
|
||||
:step "0.01"}]]]]]])]]])))
|
||||
|
||||
(defn typeahead [{:keys [matches on-change field value]}]
|
||||
(let [text (r/atom (or (second (first (filter #(= (first %) value) matches))) ""))
|
||||
highlighted (r/atom 0)
|
||||
selected (r/atom (first (first (filter #(= (first %) value) matches))))
|
||||
select (fn [[id t]]
|
||||
(reset! selected id)
|
||||
(reset! text t)
|
||||
(println [id t])
|
||||
(when on-change
|
||||
(on-change id)))]
|
||||
(fn [{:keys [matches on-change field value]}]
|
||||
(let [valid-matches (take 5 (for [[[id t :as match] i] (map vector matches (range))
|
||||
:when (str/includes? (.toLowerCase t) (.toLowerCase @text))]
|
||||
match))]
|
||||
[:div.typeahead
|
||||
[:input.input {:type "text"
|
||||
:field [:vendor]
|
||||
:value @text
|
||||
:on-blur (fn [e]
|
||||
(cond @selected
|
||||
nil
|
||||
|
||||
(seq valid-matches)
|
||||
(do (select (first valid-matches))
|
||||
true)
|
||||
|
||||
:else
|
||||
(do (select [nil ""])
|
||||
true))
|
||||
)
|
||||
:on-key-up (fn [e]
|
||||
(if (= 13 (.-keyCode e))
|
||||
(do
|
||||
(select (first valid-matches))
|
||||
false)
|
||||
true))
|
||||
:on-change (fn [e]
|
||||
(reset! highlighted (ffirst valid-matches))
|
||||
(select [nil (.. e -target -value)]))}]
|
||||
(when (and (seq @text)
|
||||
(not @selected)
|
||||
(seq valid-matches))
|
||||
[:div.typeahead-menu
|
||||
[:ul
|
||||
(for [[id t :as match] valid-matches]
|
||||
|
||||
[:li.typeahead-suggestion {:class (if (= id @highlighted)
|
||||
"typeahead-highlighted")
|
||||
:on-mouse-down #(do (println "MATCH" match) (select match))} t])]])]))))
|
||||
|
||||
(defn new-invoice-modal []
|
||||
(let [data @(re-frame/subscribe [::new-invoice])
|
||||
change-event [::events/change-form [::new-invoice]]]
|
||||
(println data)
|
||||
[action-modal {:id ::new-invoice
|
||||
:title "New Invoice"
|
||||
:action-text "Create"
|
||||
@@ -248,10 +301,11 @@
|
||||
[horizontal-field
|
||||
[:label.label "Vendor"]
|
||||
[bind-field
|
||||
[:input.input {:type "text"
|
||||
:field [:vendor]
|
||||
:event change-event
|
||||
:subscription data}]]]
|
||||
[typeahead {:matches (map (fn [x] [(:id x) (:name x)]) @(re-frame/subscribe [::subs/vendors]))
|
||||
:type "typeahead"
|
||||
:field [:vendor-id]
|
||||
:event change-event
|
||||
:subscription data}]]]
|
||||
[horizontal-field
|
||||
[:label.label "Date"]
|
||||
[bind-field
|
||||
@@ -263,10 +317,11 @@
|
||||
[horizontal-field
|
||||
[:label.label "Company"]
|
||||
[bind-field
|
||||
[:input.input {:type "text"
|
||||
:field [:company]
|
||||
:event change-event
|
||||
:subscription data}]]]
|
||||
[typeahead {:matches (map (fn [x] [(:id x) (:name x)]) @(re-frame/subscribe [::subs/companies]))
|
||||
:type "typeahead"
|
||||
:field [:company-id]
|
||||
:event change-event
|
||||
:subscription data}]]]
|
||||
|
||||
[horizontal-field
|
||||
[:label.label "Invoice #"]
|
||||
|
||||
@@ -68,11 +68,22 @@
|
||||
keys (dissoc keys :field :subscription :event :spec)]
|
||||
(into [dom keys] (with-keys rest))))
|
||||
|
||||
(defmethod do-bind "typeahead" [dom {:keys [field event subscription class spec] :as keys} & rest]
|
||||
(let [field (if (keyword? field) [field] field)
|
||||
event (if (keyword? event) [event] event)
|
||||
keys (assoc keys
|
||||
:on-change (fn [selected]
|
||||
(re-frame/dispatch (conj (conj event field) selected)))
|
||||
:value (get-in subscription field)
|
||||
:class (str class
|
||||
(when (and spec (not (s/valid? spec (get-in subscription field))))
|
||||
" is-danger")))
|
||||
keys (dissoc keys :field :subscription :event :spec)]
|
||||
(into [dom keys] (with-keys rest))))
|
||||
|
||||
(defmethod do-bind :default [dom {:keys [field event subscription class spec] :as keys} & rest]
|
||||
(let [field (if (keyword? field) [field] field)
|
||||
event (if (keyword? event) [event] event)
|
||||
_ (println field event dom rest)
|
||||
keys (assoc keys
|
||||
:on-change (dispatch-value-change (conj event field))
|
||||
:value (get-in subscription field)
|
||||
|
||||
Reference in New Issue
Block a user