Edit grubs

This commit is contained in:
Nicholas Kariniemi 2014-07-03 23:45:55 +03:00
parent 80566eed03
commit 30d2d8a3b1
6 changed files with 255 additions and 172 deletions

View file

@ -2,7 +2,7 @@
(:require [grub.state :as state]
[grub.websocket :as ws]
[cljs.core.async :as a :refer [<! >! chan]])
(:require-macros [grub.macros :refer [log logs go-loop]]
(:require-macros [grub.macros :refer [log logs]]
[cljs.core.async.macros :refer [go]]))
(defn wire-channels-together []

View file

@ -1,5 +1,5 @@
(ns grub.state
(:require [grub.view :as view]
(:require [grub.view.app :as view]
[cljs.core.async :as a :refer [<! >! chan]])
(:require-macros [grub.macros :refer [log logs]]
[cljs.core.async.macros :refer [go go-loop]]))

View file

@ -1,170 +0,0 @@
(ns grub.view
(:require [om.core :as om :include-macros true]
[sablono.core :as html :refer-macros [html]]
[cljs.core.async :as a :refer [<! put! chan]])
(:require-macros [grub.macros :refer [log logs]]
[cljs.core.async.macros :refer [go go-loop]]))
(defn recipe-view [recipe owner]
(reify
om/IRender
(render [this]
(let [{:keys [id name grubs]} recipe]
(html
[:div.panel.panel-default.recipe-panel
{:id id}
[:div.panel-heading.recipe-header
[:input.form-control.recipe-header-input
{:id "recipe-name"
:type "text"
:placeholder "Grub pie"
:value name}]
[:button.btn.btn-primary.btn-sm.recipe-add-grubs-btn
{:type "button"}
"Add Grubs"]]
[:div.panel-body.recipe-grubs.hidden
[:textarea.form-control.recipe-grubs-input
{:id "recipe-grubs"
:rows 3
:placeholder "2 grubs"}
grubs]
[:button.btn.btn-primary.hidden.pull-right.recipe-btn.recipe-done-btn
{:type "button"} "Done"]]])))))
(defn recipes-view [recipes owner]
(reify
om/IRender
(render [this]
(html
[:div
[:h3.recipes-title "Recipes"]
[:div.panel.panel-default.recipe-panel
[:div.panel-heading.recipe-header
[:input.form-control.recipe-header-input
{:id "recipe-name"
:type "text"
:placeholder "Grub pie"}]
[:button.btn.btn-primary.btn-sm.recipe-add-grubs-btn {:type "button"} "Add Grubs"]]
[:div.panel-body.recipe-grubs.hidden
[:textarea.form-control.recipe-grubs-input
{:id "recipe-grubs"
:rows 3
:placeholder "2 grubs"}]
[:button.btn.btn-primary.hidden.pull-right.recipe-btn.recipe-done-btn
{:type "button"} "Done"]]]
[:ul#recipe-list.list-group.recipe-list
(for [recipe (vals recipes)]
(om/build recipe-view recipe))]]))))
(defn complete-event [{:keys [id completed]}]
{:event :update-grub
:id id
:completed (not completed)})
(defn grub-view [grub-state owner]
(reify
om/IRender
(render [this]
(let [{:keys [id grub completed]} grub-state]
(html
[:li.list-group-item.grub-item
{:id id
:class (when completed "completed")
:on-click #(put! (:update (om/get-shared owner)) (complete-event @grub-state))}
[:span.grub-static
(if completed
[:span.glyphicon.glyphicon-check]
[:span.glyphicon.glyphicon-unchecked])
[:span.grub-text grub]]
[:input.grub-input {:type "text" :value grub}]])))))
(defn get-grub-ingredient [grub]
(when-not (nil? (:grub grub))
(let [text (clojure.string/lower-case (:grub grub))
match (re-find #"[a-z]{3}.*$" text)]
match)))
(defn sort-grubs [grubs]
(sort-by (juxt :completed get-grub-ingredient :grub) (vals grubs)))
(defn add-grub-event [grub]
{:event :add-grub
:id (str "grub-" (.now js/Date))
:grub grub
:completed false})
(defn add-grub [add {:keys [new-grub]} owner]
(when (not (empty? new-grub))
(let [new-grub-event (add-grub-event new-grub)]
(om/set-state! owner :new-grub "")
(put! add (add-grub-event new-grub)))))
(defn enter-pressed? [event]
(let [enter-keycode 13]
(= (.-which event) enter-keycode)))
(defn add-grub-on-enter [event add state owner]
(when (enter-pressed? event)
(add-grub add state owner)))
(defn handle-new-grub-change [event owner]
(om/set-state! owner :new-grub (.. event -target -value)))
(defn grubs-view [grubs owner]
(reify
om/IInitState
(init-state [_]
{:new-grub ""})
om/IRenderState
(render-state [this {:keys [new-grub] :as state}]
(let [add (:add (om/get-shared owner))]
(html
[:div
[:h3 "Grub List"]
[:div.input-group.add-grub-input-form
[:span.input-group-btn
[:input.form-control#add-grub-input
{:type "text"
:placeholder "2 grubs"
:value new-grub
:on-key-up #(add-grub-on-enter % add state owner)
:on-change #(handle-new-grub-change % owner)}]]
[:button.btn.btn-primary
{:id "add-grub-btn"
:type "button"
:on-click #(add-grub (:add (om/get-shared owner)) new-grub owner)}
"Add"]]
[:ul#grub-list.list-group
(for [grub (sort-grubs grubs)]
(om/build grub-view grub))]
[:button.btn.pull-right
{:id "clear-all-btn"
:class (when (empty? grubs) "hidden")
:type "button"
:on-click #(put! (:clear-all (om/get-shared owner)) {:event :clear-all-grubs})}
"Clear all"]])))))
(defn app-view [state owner]
(reify
om/IRender
(render [this]
(html
[:div.container
[:div.row
[:div.col-sm-6.leftmost-column
(om/build grubs-view (:grubs state))]
[:div.col-sm-6
(om/build recipes-view (:recipes state))]]]))))
(defn render-app [state]
(let [add (chan)
update (chan)
clear-all (chan)
out (a/merge [add update clear-all])]
(om/root app-view
state
{:target (.getElementById js/document "container")
:shared {:add add
:update update
:clear-all clear-all}})
out))

View file

@ -0,0 +1,44 @@
(ns grub.view.app
(:require [om.core :as om :include-macros true]
[sablono.core :as html :refer-macros [html]]
[cljs.core.async :as a :refer [<! put! chan]]
[grub.view.grub :as grub]
[grub.view.recipe :as recipe])
(:require-macros [grub.macros :refer [log logs]]
[cljs.core.async.macros :refer [go go-loop]]))
(defn app-view [state owner]
(reify
om/IRender
(render [this]
(html
[:div.container
[:div.row
[:div.col-sm-6.leftmost-column
(om/build grub/grubs-view (:grubs state))]
[:div.col-sm-6
(om/build recipe/recipes-view (:recipes state))]]]))
om/IWillMount
(will-mount [_]
(let [body-elem (aget (.getElementsByTagName js/document "body") 0)]
(log "body:" body-elem)
(.addEventListener body-elem "mousedown"
#(put! (om/get-shared owner :events-in) {:type :body-mousedown
:event %}))))))
(defn render-app [state]
(let [add (chan)
update (chan)
clear-all (chan)
out (a/merge [add update clear-all])
events-in (chan)
events (a/pub events-in :type)]
(om/root app-view
state
{:target (.getElementById js/document "container")
:shared {:add add
:update update
:clear-all clear-all
:events events
:events-in events-in}})
out))

View file

@ -0,0 +1,152 @@
(ns grub.view.grub
(:require [om.core :as om :include-macros true]
[sablono.core :as html :refer-macros [html]]
[cljs.core.async :as a :refer [<! put! chan]])
(:require-macros [grub.macros :refer [log logs]]
[cljs.core.async.macros :refer [go go-loop]]))
(defn enter-pressed? [event]
(let [enter-keycode 13]
(= (.-which event) enter-keycode)))
(defn complete-event [{:keys [id completed]}]
{:event :update-grub
:id id
:completed (not completed)})
(defn click-on-self? [{:keys [event]} node]
(or (= (.-target event) node) (.contains node (.-target event))))
(defn edit-event [id grub]
{:event :update-grub
:id id
:grub grub})
(defn grub-view [{:keys [id grub completed] :as grub-state} owner]
(reify
om/IInitState
(init-state [_]
(let [publisher (chan)]
{:edit-state :waiting
:events-in publisher
:events (a/pub publisher identity)
:grub grub}))
om/IRenderState
(render-state [_ {:keys [edit-state events-in events pressed] :as state}]
(logs id edit-state)
(html
[:li.list-group-item.grub-item
{:id id
:class [(when completed "completed")
(when (= edit-state :pressed) "grub-active")
(when (= edit-state :editing) "edit")]
:on-mouse-down #(put! events-in :mouse-down)
:on-mouse-up #(put! events-in :mouse-up)
:on-mouse-leave #(put! events-in :mouse-leave)
:on-click #(when (#{:waiting :pressed} edit-state)
(put! (om/get-shared owner :update) (complete-event @grub-state)))}
[:span.grub-static
(if completed
[:span.glyphicon.glyphicon-check]
[:span.glyphicon.glyphicon-unchecked])
[:span.grub-text grub]]
[:input.grub-input
{:type "text"
:value (:grub state)
:on-change #(om/set-state! owner :grub (.. % -target -value))
:on-key-up #(when (enter-pressed? %) (put! events-in :enter))}]]))
om/IWillMount
(will-mount [_]
(let [local-events (om/get-state owner :events)
events (om/get-shared owner :events)
subscriber (chan)]
(go-loop []
(om/set-state! owner :edit-state :waiting)
(a/sub local-events :mouse-down subscriber)
(<! subscriber)
(a/unsub local-events :mouse-down subscriber)
(om/set-state! owner :edit-state :pressed)
(a/sub local-events :mouse-leave subscriber)
(a/sub local-events :mouse-up subscriber)
(let [timeout (a/timeout 500)
[event c] (a/alts! [timeout subscriber])]
(a/unsub local-events :mouse-leave subscriber)
(a/unsub local-events :mouse-up subscriber)
(if (= c timeout)
(do (om/set-state! owner :edit-state :editing)
(a/sub events :body-mousedown subscriber)
(a/sub local-events :enter subscriber)
(loop []
(let [event (<! subscriber)]
(when (and (= (:type event) :body-mousedown)
(click-on-self? event (om/get-node owner)))
(recur))))
(a/unsub events :body-mousedown subscriber)
(a/unsub local-events :enter subscriber)
(put! (om/get-shared owner :update) (edit-event id (om/get-state owner :grub))))
(om/set-state! owner :edit-state :waiting)))
(recur))))))
(defn get-grub-ingredient [grub]
(when-not (nil? (:grub grub))
(let [text (clojure.string/lower-case (:grub grub))
match (re-find #"[a-z]{3}.*$" text)]
match)))
(defn sort-grubs [grubs]
(sort-by (juxt :completed get-grub-ingredient :grub) (vals grubs)))
(defn add-grub-event [grub]
{:event :add-grub
:id (str "grub-" (.now js/Date))
:grub grub
:completed false})
(defn add-grub [add {:keys [new-grub]} owner]
(when (not (empty? new-grub))
(let [new-grub-event (add-grub-event new-grub)]
(om/set-state! owner :new-grub "")
(put! add (add-grub-event new-grub)))))
(defn add-grub-on-enter [event add state owner]
(when (enter-pressed? event)
(add-grub add state owner)))
(defn handle-new-grub-change [event owner]
(om/set-state! owner :new-grub (.. event -target -value)))
(defn grubs-view [grubs owner]
(reify
om/IInitState
(init-state [_]
{:new-grub ""})
om/IRenderState
(render-state [this {:keys [new-grub] :as state}]
(let [add (om/get-shared owner :add)]
(html
[:div
[:h3 "Grub List"]
[:div.input-group.add-grub-input-form
[:span.input-group-btn
[:input.form-control#add-grub-input
{:type "text"
:placeholder "2 grubs"
:value new-grub
:on-key-up #(add-grub-on-enter % add state owner)
:on-change #(handle-new-grub-change % owner)}]]
[:button.btn.btn-primary
{:id "add-grub-btn"
:type "button"
:on-click #(add-grub (om/get-shared owner :add) new-grub owner)}
"Add"]]
[:ul#grub-list.list-group
(for [grub (sort-grubs grubs)]
(om/build grub-view grub))]
[:button.btn.pull-right
{:id "clear-all-btn"
:class (when (empty? grubs) "hidden")
:type "button"
:on-click #(put! (om/get-shared owner :clear-all) {:event :clear-all-grubs})}
"Clear all"]])))))

View file

@ -0,0 +1,57 @@
(ns grub.view.recipe
(:require [om.core :as om :include-macros true]
[sablono.core :as html :refer-macros [html]]
[cljs.core.async :as a :refer [<! put! chan]])
(:require-macros [grub.macros :refer [log logs]]
[cljs.core.async.macros :refer [go go-loop]]))
(defn recipe-view [recipe owner]
(reify
om/IRender
(render [this]
(let [{:keys [id name grubs]} recipe]
(html
[:div.panel.panel-default.recipe-panel
{:id id}
[:div.panel-heading.recipe-header
[:input.form-control.recipe-header-input
{:id "recipe-name"
:type "text"
:placeholder "Grub pie"
:value name}]
[:button.btn.btn-primary.btn-sm.recipe-add-grubs-btn
{:type "button"}
"Add Grubs"]]
[:div.panel-body.recipe-grubs.hidden
[:textarea.form-control.recipe-grubs-input
{:id "recipe-grubs"
:rows 3
:placeholder "2 grubs"}
grubs]
[:button.btn.btn-primary.hidden.pull-right.recipe-btn.recipe-done-btn
{:type "button"} "Done"]]])))))
(defn recipes-view [recipes owner]
(reify
om/IRender
(render [this]
(html
[:div
[:h3.recipes-title "Recipes"]
[:div.panel.panel-default.recipe-panel
[:div.panel-heading.recipe-header
[:input.form-control.recipe-header-input
{:id "recipe-name"
:type "text"
:placeholder "Grub pie"}]
[:button.btn.btn-primary.btn-sm.recipe-add-grubs-btn {:type "button"} "Add Grubs"]]
[:div.panel-body.recipe-grubs.hidden
[:textarea.form-control.recipe-grubs-input
{:id "recipe-grubs"
:rows 3
:placeholder "2 grubs"}]
[:button.btn.btn-primary.hidden.pull-right.recipe-btn.recipe-done-btn
{:type "button"} "Done"]]]
[:ul#recipe-list.list-group.recipe-list
(for [recipe (vals recipes)]
(om/build recipe-view recipe))]]))))