Edit grubs
This commit is contained in:
parent
80566eed03
commit
30d2d8a3b1
6 changed files with 255 additions and 172 deletions
|
@ -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 []
|
||||
|
|
|
@ -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]]))
|
||||
|
|
|
@ -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))
|
44
src/cljs/grub/view/app.cljs
Normal file
44
src/cljs/grub/view/app.cljs
Normal 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))
|
152
src/cljs/grub/view/grub.cljs
Normal file
152
src/cljs/grub/view/grub.cljs
Normal 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"]])))))
|
57
src/cljs/grub/view/recipe.cljs
Normal file
57
src/cljs/grub/view/recipe.cljs
Normal 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))]]))))
|
Loading…
Reference in a new issue