From e3738fb017545c2a169d2df6fc9a4adf3420a4e7 Mon Sep 17 00:00:00 2001 From: Dom Kiva-Meyer Date: Thu, 12 Jun 2014 13:59:25 -0700 Subject: [PATCH] refactor and add doc strings --- src/omelette/view.cljs | 185 ++++++++++++++++++++++++++--------------- 1 file changed, 116 insertions(+), 69 deletions(-) diff --git a/src/omelette/view.cljs b/src/omelette/view.cljs index 88e6e99..62aa1cf 100644 --- a/src/omelette/view.cljs +++ b/src/omelette/view.cljs @@ -11,113 +11,160 @@ [sablono.core :as html :refer-macros [html]]) (:require-macros [cljs.core.async.macros :as csp])) +; Print using Nashorn's `print` function if `console` is undefined. (if (exists? js/console) (enable-console-print!) (set-print-fn! js/print)) -(defn not-found-view [] - (om/component - (html - (html/image "/assets/images/404.gif")))) +(defn- search-view-query [owner query] + [:div + {:on-change #(->> (-> % + .-target + .-value + (str/replace #"\W|\d|_" "") + str/lower-case) + (om/set-state! owner :query))} + (html/search-field "query" query)]) -(defn about-view [data] - (om/component - (html - (if-let [markdown (:markdown data)] - (->> markdown - md/mdToHtml - (hash-map :__html) - (hash-map :dangerouslySetInnerHTML) - (vector :div)) - [:div - [:h3 "Loading..."] - (html/image "/assets/images/loading.gif")])))) +(defn- search-view-options [owner options] + [:div + (for [[option label] {:prefix "starts with" + :infix "includes" + :postfix "ends with"} + :let [handler (fn [event] + (let [new-opts ((if (-> event + .-target + .-checked) + conj + disj) + options + option)] + (when (seq new-opts) + (om/set-state! owner :options new-opts))))]] + [:div + {:on-change handler} + (html/check-box (name option) (-> options option boolean)) + (html/label (name option) label)])]) (defn search-view [data owner] (reify om/IInitState - (init-state [_] (dissoc (om/value data) :results)) + (init-state + [_] + (dissoc (om/value data) :results)) + om/IWillUpdate + (will-update + [_ _ new-state] + ; Update data if the current and new states are different. + (when-not (= (om/get-render-state owner) + new-state) + ; Tag it with `:nav` so the router receives it. + (om/update! data [] new-state :nav))) om/IRenderState (render-state [_ {:keys [query options]}] (html [:div - [:form {:on-change #(om/update! data [] (om/get-state owner) :nav) - :on-submit #(.preventDefault %)} - [:input {:type "search" - :value query - :on-change #(om/set-state! owner :query (-> % .-target .-value (str/replace #"\W|\d|_" "") str/lower-case))}] - [:br] - [:div {:on-change #(om/set-state! owner :options ((if (-> % .-target .-checked) - conj - disj) - options - (-> % .-target .-name keyword)))} - (html/check-box "prefix" (-> options :prefix boolean)) - (html/label "prefix" "starts with") - [:br] - (html/check-box "infix" (-> options :infix boolean)) - (html/label "infix" "includes") - [:br] - (html/check-box "postfix" (-> options :postfix boolean)) - (html/label "postfix" "ends with")]] + [:form {:on-submit #(.preventDefault %)} + (search-view-query owner query) + (search-view-options owner options)] [:div (if-let [results (:results data)] (if (seq results) (html/unordered-list results) - [:em "no results"]) - [:div - [:h3 "Loading..."] - (html/image "/assets/images/loading.gif")])]])))) + [:h3 "No Results"]) + [:h3 "Loading..."])]])))) + +(defn about-view [data] + (om/component + (html + [:div + (when-let [markdown (:markdown data)] + (->> markdown + md/mdToHtml + (hash-map :__html) + (hash-map :dangerouslySetInnerHTML) + (vector :div)))]))) + +(defn not-found-view [] + (om/component + (html + (html/image "/assets/images/404.gif")))) + +(defn- app-view-nav [data owner] + [:nav.navbar.navbar-default + [:ul.nav.navbar-nav + (for [[href content] {"/" "Search" + "/about" "About" + "/not-found" "Not Found"} + :let [active? (= (-> content + str/lower-case + (str/replace #" " "-")) + (-> data + first + name)) + handler (fn [event] + (.preventDefault event) + (csp/put! (om/get-shared owner :nav-tokens) href))]] + [:li (when active? {:class "active"}) + [:a {:href href + :on-click handler} + content]])]]) (defn app-view [data owner] (om/component - (letfn [(nav-link-to - [href content] - [:a {:href href - :on-click (fn [e] - (.preventDefault e) - (csp/put! (om/get-shared owner :nav-tokens) href))} - content])] - (html - [:div - [:div - [:nav - (nav-link-to "/" "Search") - (nav-link-to "/about" "About") - (nav-link-to "/not-found" "Not Found")] - [:h1 (route/state->title data)] - (om/build route/router - data - {:opts {:page-views {"about" about-view - "search" search-view - "not-found" not-found-view}}})] - [:div - (om/build ankha/inspector data)]])))) + (html + [:div.container-fluid + (app-view-nav data owner) + [:div.row + [:div.col-xs-6 + [:h1 (route/state->title data)] + (om/build route/router + data + {:opts {:page-views {"about" about-view + "search" search-view + "not-found" not-found-view}}})] + [:div.col-xs-6 + [:h2 "App State"] + (om/build ankha/inspector data)]]]))) -(def app-state (atom nil)) +(declare app-container + app-state) -(defn render [] +(defn render + "Renders the app to the DOM. + Can safely be called repeatedly to rerender the app."[] (let [transactions (csp/chan) transactions-pub (csp/pub transactions :tag)] (om/root app-view app-state - {:target (goog.dom/getElement "omelette-app") + {:target app-container :tx-listen #(csp/put! transactions %) :shared {:nav-tokens (csp/chan) :transactions transactions :transactions-pub transactions-pub}}))) -(defn ^:export render-to-string [state-edn] +(defn ^:export render-to-string + "Takes an app state as EDN and returns the HTML for that state. + It can be invoked from JS as `omelette.view.render_to_string(edn)`." + [state-edn] (->> state-edn edn/read-string (om/build app-view) dom/render-to-str)) -(defn ^:export init [id] - (->> id +(defn ^:export init + "Initializes the app. + Should only be called once on page load. + It can be invoked from JS as `omelette.view.init(appElementId, stateElementId)`." + [app-id state-id] + (->> state-id goog.dom/getElement .-textContent edn/read-string - (reset! app-state)) + atom + (set! app-state)) + (->> app-id + goog.dom/getElement + (set! app-container)) (render))