Render grub list with om
This commit is contained in:
parent
594408ede2
commit
24632d99ce
9 changed files with 17237 additions and 226 deletions
|
@ -1,161 +1,165 @@
|
|||
html, body {
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
width: 100%;
|
||||
min-width: 100%;
|
||||
margin: 0px;
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
width: 100%;
|
||||
min-width: 100%;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-top: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.recipes-title {
|
||||
clear: right;
|
||||
clear: right;
|
||||
}
|
||||
|
||||
.recipe-list {
|
||||
margin-top: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
#recipe-grubs {
|
||||
width: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.recipe-add-grubs-btn {
|
||||
float: right;
|
||||
clear: both;
|
||||
margin: 2px;
|
||||
float: right;
|
||||
clear: both;
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.leftmost-column {
|
||||
margin-bottom: 45px;
|
||||
margin-bottom: 45px;
|
||||
}
|
||||
|
||||
.grub-close {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
tr:hover .grub-close {
|
||||
display: block;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#grub-list {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.add-grub-input-form .input-group-btn {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.grub-item {
|
||||
cursor: default;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
cursor: default;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.grub-item.grub-active {
|
||||
color: #ffffff;
|
||||
background-color: rgb(71, 73, 73);
|
||||
color: #ffffff;
|
||||
background-color: rgb(71, 73, 73);
|
||||
}
|
||||
|
||||
.grub-item .input-span {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.grub-item .grub-static {
|
||||
display: inline;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.grub-item .grub-input {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.grub-item.edit .grub-static {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.grub-item.edit .grub-input {
|
||||
display: inline;
|
||||
width: 100%;
|
||||
display: inline;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.grub-item .glyphicon {
|
||||
margin-right: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.completed {
|
||||
text-decoration: line-through;
|
||||
color: #a9a9a9;
|
||||
text-decoration: line-through;
|
||||
color: #a9a9a9;
|
||||
}
|
||||
|
||||
.completed.edit {
|
||||
text-decoration: none;
|
||||
color: default;
|
||||
text-decoration: none;
|
||||
color: default;
|
||||
}
|
||||
|
||||
.panel-body {
|
||||
padding: 10px;
|
||||
padding-top: 0px;
|
||||
padding: 10px;
|
||||
padding-top: 0px;
|
||||
}
|
||||
|
||||
.panel-heading {
|
||||
background-color: #ffffff;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.recipe-panel {
|
||||
padding: 0px;
|
||||
margin-bottom: -1px;
|
||||
padding: 0px;
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
|
||||
.recipe-panel > .recipe-header {
|
||||
background-color: #ffffff;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.recipe-header {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
border-bottom: none;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.recipe-header-input {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
transition: none;
|
||||
border-bottom: none;
|
||||
width: inherit;
|
||||
display: inline;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
transition: none;
|
||||
border-bottom: none;
|
||||
width: inherit;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.recipe-header-input:focus {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
transition: none;
|
||||
outline: none;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
transition: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.recipe-btn {
|
||||
margin-top: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.recipe-grubs {
|
||||
}
|
||||
|
||||
.recipe-grubs-input {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
transition: none;
|
||||
border-bottom: none;
|
||||
resize: none;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
transition: none;
|
||||
border-bottom: none;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.recipe-grubs-input:focus {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
transition: none;
|
||||
border-bottom: none;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
transition: none;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
|
16644
public/js/react-0.9.0.js
Normal file
16644
public/js/react-0.9.0.js
Normal file
File diff suppressed because it is too large
Load diff
21
public/js/react-0.9.0.min.js
vendored
Normal file
21
public/js/react-0.9.0.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -25,7 +25,8 @@
|
|||
(html5
|
||||
index-page-header
|
||||
[:body
|
||||
(include-js "http://fb.me/react-0.9.0.js")
|
||||
[:div#container]
|
||||
(include-js "/js/react-0.9.0.min.js")
|
||||
(include-js "/js/jquery.js")
|
||||
(include-js "/js/bootstrap.js")
|
||||
(include-js "/js/grub.js")]))
|
||||
|
@ -34,7 +35,8 @@
|
|||
(html5
|
||||
index-page-header
|
||||
[:body
|
||||
(include-js "http://fb.me/react-0.9.0.js")
|
||||
[:div#container]
|
||||
(include-js "/js/react-0.9.0.js")
|
||||
(include-js "/js/out/goog/base.js")
|
||||
(include-js "/js/jquery.js")
|
||||
(include-js "/js/bootstrap.js")
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
(ns grub.core
|
||||
(:require [grub.view :as view]
|
||||
(: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]]
|
||||
|
@ -7,10 +7,10 @@
|
|||
|
||||
(defn wire-channels-together []
|
||||
(let [to-remote (chan)
|
||||
to-view (chan)
|
||||
to-state (chan)
|
||||
from-remote (ws/get-remote-chan to-remote)
|
||||
from-view (view/setup-and-get-view-events to-view)]
|
||||
(a/pipe from-remote to-view)
|
||||
(a/pipe from-view to-remote)))
|
||||
from-state (state/update-state-and-render to-state)]
|
||||
(a/pipe from-remote to-state)
|
||||
(a/pipe from-state to-remote)))
|
||||
|
||||
(wire-channels-together)
|
||||
|
|
59
src/cljs/grub/state.cljs
Normal file
59
src/cljs/grub/state.cljs
Normal file
|
@ -0,0 +1,59 @@
|
|||
(ns grub.state
|
||||
(:require [grub.view :as view]
|
||||
[cljs.core.async :as a :refer [<! >! chan]])
|
||||
(:require-macros [grub.macros :refer [log logs]]
|
||||
[cljs.core.async.macros :refer [go go-loop]]))
|
||||
|
||||
(def default-app-state {:grubs {}
|
||||
:recipes {}})
|
||||
|
||||
(defmulti handle-event (fn [event state] (:event event))
|
||||
:default :unknown-event)
|
||||
|
||||
(defmethod handle-event :unknown-event [event grubs]
|
||||
(logs "Cannot handle unknown event:" event)
|
||||
grubs)
|
||||
|
||||
(defn new-grub [id grub completed]
|
||||
{:id id :grub grub :completed completed})
|
||||
|
||||
(defmethod handle-event :add-grub [event grubs]
|
||||
(let [grub (new-grub (:id event) (:grub event) (:completed event))]
|
||||
(assoc grubs (:id grub) grub)))
|
||||
|
||||
(defn assoc-new-grub [current new]
|
||||
(assoc current (:id new)
|
||||
(new-grub (:id new) (:grub new) (:completed new))))
|
||||
|
||||
(defn make-add-grubs-map [grub-events]
|
||||
(reduce assoc-new-grub {} grub-events))
|
||||
|
||||
(defmethod handle-event :add-grub-list [event grubs]
|
||||
(let [add-grub-events (:grubs event)
|
||||
add-grubs (make-add-grubs-map add-grub-events)]
|
||||
(merge grubs add-grubs)))
|
||||
|
||||
(defmethod handle-event :complete-grub [event grubs]
|
||||
(assoc-in grubs [(:id event) :completed] true))
|
||||
|
||||
(defmethod handle-event :uncomplete-grub [event grubs]
|
||||
(assoc-in grubs [(:id event) :completed] false))
|
||||
|
||||
(defmethod handle-event :update-grub [event grubs]
|
||||
(assoc-in grubs [(:id event) :grub] (:grub event)))
|
||||
|
||||
(defmethod handle-event :clear-all-grubs [event grubs]
|
||||
{})
|
||||
|
||||
(defn update-state-and-render [remote-chan]
|
||||
(let [out (chan)]
|
||||
(go-loop [state default-app-state]
|
||||
(let [event (<! remote-chan)
|
||||
new-grubs (handle-event event (:grubs state))
|
||||
new-state (assoc state :grubs new-grubs)]
|
||||
(logs "event:" event)
|
||||
(logs "new-state")
|
||||
(logs new-state)
|
||||
(view/render-body new-state)
|
||||
(recur new-state)))
|
||||
out))
|
|
@ -1,24 +1,364 @@
|
|||
(ns grub.view
|
||||
(:require [grub.view.dom :as dom]
|
||||
[grub.view.grub :as grub-view]
|
||||
[grub.view.recipe :as recipe-view]
|
||||
[dommy.core :as dommy]
|
||||
[cljs.core.async :as a :refer [<! >! chan]])
|
||||
(:require-macros [grub.macros :refer [log logs and-let]]
|
||||
[dommy.macros :refer [deftemplate sel1 node]]
|
||||
[cljs.core.async.macros :refer [go go-loop]]))
|
||||
(:require [dommy.core :as dommy]
|
||||
[cljs.core.async :as a :refer [<! >! chan]]
|
||||
[om.core :as om :include-macros true]
|
||||
[om.dom :as dom :include-macros true]
|
||||
[sablono.core :as html :refer-macros [html]])
|
||||
(:require-macros [grub.macros :refer [log logs]]
|
||||
[dommy.macros :refer [sel1]]
|
||||
[cljs.core.async.macros :refer [go]]))
|
||||
|
||||
(defn setup-and-get-view-events [remote-channel]
|
||||
(dom/render-body)
|
||||
(let [out (chan)
|
||||
remote (a/mult remote-channel)
|
||||
to-grubs (chan)
|
||||
to-recipes (chan)
|
||||
from-grubs (grub-view/handle-grubs to-grubs)
|
||||
from-recipes (a/mult (recipe-view/handle-recipes to-recipes))]
|
||||
(a/tap remote to-grubs)
|
||||
(a/tap remote to-recipes)
|
||||
(a/tap from-recipes to-grubs)
|
||||
(a/tap from-recipes out)
|
||||
(a/pipe from-grubs out)
|
||||
out))
|
||||
(defn listen
|
||||
([el type] (listen el type nil))
|
||||
([el type f] (listen el type f (chan)))
|
||||
([el type f out]
|
||||
(let [push-fn (fn [e] (when f (f e)) (go (>! out e)))
|
||||
unlisten #(do (dommy/unlisten! el type push-fn)
|
||||
(a/close! out))]
|
||||
(dommy/listen! el type push-fn)
|
||||
{:chan out :unlisten unlisten})))
|
||||
|
||||
(def ENTER-KEYCODE 13)
|
||||
|
||||
(defn listen-once
|
||||
([el type] (listen el type nil))
|
||||
([el type f] (listen el type f (chan)))
|
||||
([el type f out]
|
||||
(let [push-fn (fn [e] (when f (f e)) (go (>! out e)))
|
||||
unlisten #(do (dommy/unlisten! el type push-fn)
|
||||
(a/close! out))]
|
||||
(dommy/listen-once! el type push-fn)
|
||||
{:chan out :unlisten unlisten})))
|
||||
|
||||
(defn get-away-clicks [elem]
|
||||
(let [{c :chan unlisten :unlisten} (listen (sel1 :body) :click)
|
||||
filtered-chan (a/filter< #(not (dommy/descendant? (.-target %) elem)) c)]
|
||||
{:unlisten unlisten :chan filtered-chan}))
|
||||
|
||||
(defn get-clicks [elem]
|
||||
(listen elem :click))
|
||||
|
||||
(defn get-enters [elem]
|
||||
(let [{c :chan unlisten :unlisten} (listen elem :keyup)
|
||||
filtered-chan (a/filter< #(= (.-which %) ENTER-KEYCODE) c)]
|
||||
{:unlisten unlisten
|
||||
:chan filtered-chan}))
|
||||
|
||||
(defn get-ctrl-enters []
|
||||
(let [{c :chan unlisten :unlisten} (listen (sel1 :body) :keyup)
|
||||
filtered-chan (a/filter< #(and (= (.-which %) ENTER-KEYCODE)
|
||||
(.-ctrlKey %))
|
||||
c)]
|
||||
{:chan filtered-chan :unlisten unlisten}))
|
||||
|
||||
(defn get-body-enters []
|
||||
(get-enters (sel1 :body)))
|
||||
|
||||
(def add-grub-btn
|
||||
[:button.btn.btn-primary {:id "add-grub-btn" :type "button"} "Add"])
|
||||
|
||||
(def clear-all-btn
|
||||
[:button.btn.hidden.pull-right
|
||||
{:id "clear-all-btn" :type "button"}
|
||||
"Clear all"])
|
||||
|
||||
(defn clear-grubs! []
|
||||
(dommy/set-html! (sel1 :#grub-list) ""))
|
||||
|
||||
(defn get-grub-completed-glyph [completed]
|
||||
(if completed
|
||||
[:span.glyphicon.glyphicon-check]
|
||||
[:span.glyphicon.glyphicon-unchecked]))
|
||||
|
||||
(defn make-grub-node [id grub completed]
|
||||
[:li.list-group-item.grub-item
|
||||
{:id id
|
||||
:class (when completed "completed")}
|
||||
[:span.grub-static
|
||||
(get-grub-completed-glyph completed)
|
||||
[:span.grub-text grub]]
|
||||
[:input.grub-input {:type "text" :value grub}]])
|
||||
|
||||
(defn grubs-selector []
|
||||
[(sel1 :#grub-list) :.grub-item])
|
||||
|
||||
(defn make-recipe-node
|
||||
([id name grubs] (make-recipe-node id name grubs false))
|
||||
([id name grubs new-recipe]
|
||||
[: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}]
|
||||
(when-not new-recipe
|
||||
[: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"]]]))
|
||||
|
||||
(def new-recipe (make-recipe-node "new-recipe" "" "" true))
|
||||
|
||||
(def new-recipe-done-btn
|
||||
(sel1 new-recipe ".recipe-done-btn"))
|
||||
|
||||
(defn recipes-selector []
|
||||
[(sel1 :#recipe-list) :.recipe-panel])
|
||||
|
||||
(defn recipe-done-btns-selector []
|
||||
[(sel1 :body) :.recipe-done-btn])
|
||||
|
||||
(defn recipe-done-btn-selector [recipe-elem]
|
||||
(sel1 recipe-elem :.recipe-done-btn))
|
||||
|
||||
(defn recipe-add-grubs-btns-selector []
|
||||
[(sel1 :body) :.recipe-add-grubs-btn])
|
||||
|
||||
(defn grub-list-template [grubs]
|
||||
(for [grub grubs]
|
||||
(make-grub-node (:id grub) (:grub grub) (:completed grub))))
|
||||
|
||||
(defn render-grub-list [grubs]
|
||||
(let [grub-list (sel1 :#grub-list)]
|
||||
(aset grub-list "innerHTML" "")
|
||||
(dommy/replace-contents! grub-list (grub-list-template grubs))))
|
||||
|
||||
(defn get-add-grub-text []
|
||||
(dommy/value (sel1 :#add-grub-input)))
|
||||
|
||||
(defn clear-add-grub-text []
|
||||
(dommy/set-value! (sel1 :#add-grub-input) ""))
|
||||
|
||||
(defn get-recipe-add-grubs-clicks []
|
||||
(->> (:chan (listen (recipe-add-grubs-btns-selector) :click))
|
||||
(a/map< #(dommy/closest (.-selectedTarget %) :.recipe-panel))))
|
||||
|
||||
(defn get-edit-recipe-input-click []
|
||||
(->> (:chan (listen-once (recipes-selector) :click))
|
||||
(a/filter< #(not (dommy/has-class? (.-selectedTarget %) :btn)))
|
||||
(a/map< #(.-selectedTarget %))))
|
||||
|
||||
(defprotocol IHideable
|
||||
(-hide! [this])
|
||||
(-show! [this]))
|
||||
|
||||
(defprotocol IGrub
|
||||
(-activate! [this])
|
||||
(-deactivate! [this])
|
||||
|
||||
(-id [this])
|
||||
(-grub-text [this])
|
||||
|
||||
(-complete! [this])
|
||||
(-uncomplete! [this])
|
||||
(-completed? [this])
|
||||
|
||||
(-set-editing! [this])
|
||||
(-unset-editing! [this])
|
||||
(-editing? [this])
|
||||
(-update-grub! [this grub]))
|
||||
|
||||
(defprotocol IRecipe
|
||||
(-expand! [this])
|
||||
(-unexpand! [this])
|
||||
|
||||
(-update-recipe! [this])
|
||||
|
||||
(-get-name [this])
|
||||
(-get-grubs-str [this])
|
||||
(-get-grubs [this]))
|
||||
|
||||
(defprotocol IClearable
|
||||
(-clear! [this]))
|
||||
|
||||
(extend-type js/HTMLElement
|
||||
IHideable
|
||||
(-hide! [this]
|
||||
(dommy/add-class! this :hidden))
|
||||
(-show! [this]
|
||||
(dommy/remove-class! this :hidden)))
|
||||
|
||||
|
||||
(extend-type js/HTMLElement
|
||||
IGrub
|
||||
(-activate! [this]
|
||||
(dommy/add-class! this :grub-active))
|
||||
(-deactivate! [this]
|
||||
(dommy/remove-class! this :grub-active))
|
||||
|
||||
(-id [this]
|
||||
(.-id this))
|
||||
(-grub-text [this]
|
||||
(.-value (sel1 this :.grub-input)))
|
||||
|
||||
(-complete! [this]
|
||||
(dommy/add-class! this :completed)
|
||||
(dommy/replace! (sel1 this ".glyphicon")
|
||||
(get-grub-completed-glyph true)))
|
||||
(-uncomplete! [this]
|
||||
(dommy/remove-class! this :completed)
|
||||
(dommy/replace! (sel1 this ".glyphicon")
|
||||
(get-grub-completed-glyph false)))
|
||||
(-completed? [this]
|
||||
(dommy/has-class? this :completed))
|
||||
|
||||
(-set-editing! [this]
|
||||
(-deactivate! this)
|
||||
(dommy/add-class! this :edit)
|
||||
(.focus (sel1 this :input)))
|
||||
(-unset-editing! [this]
|
||||
(dommy/remove-class! this :edit))
|
||||
(-editing? [this]
|
||||
(dommy/has-class? this :edit)))
|
||||
|
||||
(defrecord Grub [elem id grub completed]
|
||||
dommy.template/PElement
|
||||
(-elem [this] elem)
|
||||
|
||||
IGrub
|
||||
(-set-editing! [this] (-set-editing! elem))
|
||||
(-unset-editing! [this] (-unset-editing! elem))
|
||||
(-editing? [this] (-editing? elem))
|
||||
|
||||
(-complete! [this] (-complete! elem))
|
||||
(-uncomplete! [this] (-uncomplete! elem))
|
||||
(-completed? [this] (-completed? elem))
|
||||
|
||||
(-set-editing! [this] (-set-editing! elem))
|
||||
(-unset-editing! [this] (-unset-editing! elem))
|
||||
(-editing? [this] (-editing? elem))
|
||||
|
||||
(-update-grub! [this grub]
|
||||
(dommy/set-text! (sel1 elem ".grub-text") grub)
|
||||
(dommy/set-value! (sel1 elem ".grub-input") grub)))
|
||||
|
||||
(defn make-new-grub [id grub completed]
|
||||
(let [node (make-grub-node id grub completed)
|
||||
grub (Grub. node id grub completed)
|
||||
grub-list (sel1 :#grub-list)]
|
||||
grub))
|
||||
|
||||
(defn clear-new-grub-input! []
|
||||
(dommy/set-value! (sel1 :#add-grub-input) ""))
|
||||
|
||||
(defn focus-new-grub-input! []
|
||||
(.focus (sel1 :#add-grub-input)))
|
||||
|
||||
(extend-type js/HTMLDivElement
|
||||
IRecipe
|
||||
(-expand! [this]
|
||||
(dommy/remove-class! (sel1 this ".recipe-grubs") :hidden)
|
||||
(dommy/remove-class! (sel1 this ".recipe-done-btn") :hidden))
|
||||
(-unexpand! [this]
|
||||
(dommy/add-class! (sel1 this ".recipe-grubs") :hidden)
|
||||
(dommy/add-class! (sel1 this ".recipe-done-btn") :hidden))
|
||||
|
||||
(-get-name [this]
|
||||
(dommy/value (sel1 this :#recipe-name)))
|
||||
(-get-grubs-str [this]
|
||||
(dommy/value (sel1 this :#recipe-grubs)))
|
||||
(-get-grubs [this]
|
||||
(let [split-grubs (clojure.string/split-lines (-get-grubs-str this))]
|
||||
(when split-grubs (into [] split-grubs)))))
|
||||
|
||||
|
||||
(extend-type js/HTMLDivElement
|
||||
IClearable
|
||||
(-clear! [this]
|
||||
(dommy/set-value! (sel1 this "#recipe-name") "")
|
||||
(dommy/set-value! (sel1 this "#recipe-grubs") "")))
|
||||
|
||||
(defrecord Recipe [elem id name grubs]
|
||||
dommy.template/PElement
|
||||
(-elem [this] elem)
|
||||
|
||||
IRecipe
|
||||
(-expand! [this] (-expand! elem))
|
||||
(-unexpand! [this] (-unexpand! elem))
|
||||
|
||||
(-clear! [this] (-clear! elem))
|
||||
|
||||
(-update-recipe! [this]
|
||||
(dommy/set-value! (sel1 this :#recipe-name) name)
|
||||
(dommy/set-text! (sel1 this :#recipe-grubs) grubs)))
|
||||
|
||||
(defn add-new-recipe! [id name grubs]
|
||||
(let [node (make-recipe-node id name grubs)
|
||||
recipe (Recipe. node id name grubs)
|
||||
recipe-list (sel1 :#recipe-list)]
|
||||
(dommy/append! recipe-list recipe)
|
||||
recipe))
|
||||
|
||||
(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")}
|
||||
[:span.grub-static
|
||||
(get-grub-completed-glyph completed)
|
||||
[:span.grub-text grub]]
|
||||
[:input.grub-input {:type "text" :value grub}]])))))
|
||||
|
||||
(defn get-grub-ingredient [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) (vals grubs)))
|
||||
|
||||
(defn app-view [state owner]
|
||||
(reify
|
||||
om/IRender
|
||||
(render [this]
|
||||
(let [sorted-grubs (sort-grubs (:grubs state))]
|
||||
(logs "render app-view state:" state)
|
||||
(logs "render app-view owner:" owner)
|
||||
(logs "render grubs:" sorted-grubs)
|
||||
(html
|
||||
[:div.container
|
||||
[:div.row
|
||||
[:div.col-sm-6.leftmost-column
|
||||
[: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"}]]
|
||||
[:button.btn.btn-primary {:id "add-grub-btn" :type "button"} "Add"]]
|
||||
[:ul#grub-list.list-group
|
||||
(for [grub (vals (:grubs state))]
|
||||
(om/build grub-view grub))]
|
||||
[:button.btn.hidden.pull-right
|
||||
{:id "clear-all-btn" :type "button"}
|
||||
"Clear all"]]
|
||||
[:div.col-sm-6
|
||||
[: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]]]])))))
|
||||
|
||||
(defn render-body [state]
|
||||
(logs state)
|
||||
(om/root app-view state {:target (.getElementById js/document "container")}))
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
[om.core :as om :include-macros true]
|
||||
[om.dom :as dom :include-macros true]
|
||||
[sablono.core :as html :refer-macros [html]])
|
||||
(:require-macros [grub.macros :refer [log logs go-loop]]
|
||||
[dommy.macros :refer [deftemplate sel1 node]]
|
||||
(:require-macros [grub.macros :refer [log logs]]
|
||||
[dommy.macros :refer [sel1]]
|
||||
[cljs.core.async.macros :refer [go]]))
|
||||
|
||||
(defn listen
|
||||
|
@ -54,33 +54,30 @@
|
|||
(defn get-body-enters []
|
||||
(get-enters (sel1 :body)))
|
||||
|
||||
(def add-grub-text
|
||||
[:input.form-control {:id "add-grub-input" :type "text" :placeholder "2 grubs"}])
|
||||
|
||||
(def add-grub-btn
|
||||
[:button.btn.btn-primary {:id "add-grub-btn" :type "button"} "Add"])
|
||||
|
||||
(def clear-all-btn
|
||||
(node [:button.btn.hidden.pull-right
|
||||
{:id "clear-all-btn" :type "button"}
|
||||
"Clear all"]))
|
||||
[:button.btn.hidden.pull-right
|
||||
{:id "clear-all-btn" :type "button"}
|
||||
"Clear all"])
|
||||
|
||||
(defn clear-grubs! []
|
||||
(dommy/set-html! (sel1 :#grub-list) ""))
|
||||
|
||||
(defn get-grub-completed-glyph [completed]
|
||||
(node (if completed
|
||||
(if completed
|
||||
[:span.glyphicon.glyphicon-check]
|
||||
[:span.glyphicon.glyphicon-unchecked])))
|
||||
[:span.glyphicon.glyphicon-unchecked]))
|
||||
|
||||
(defn make-grub-node [id grub completed]
|
||||
(node [:li.list-group-item.grub-item
|
||||
{:id id
|
||||
:class (when completed "completed")}
|
||||
[:span.grub-static
|
||||
(get-grub-completed-glyph completed)
|
||||
[:span.grub-text grub]]
|
||||
[:input.grub-input {:type "text" :value grub}]]))
|
||||
[:li.list-group-item.grub-item
|
||||
{:id id
|
||||
:class (when completed "completed")}
|
||||
[:span.grub-static
|
||||
(get-grub-completed-glyph completed)
|
||||
[:span.grub-text grub]]
|
||||
[:input.grub-input {:type "text" :value grub}]])
|
||||
|
||||
(defn grubs-selector []
|
||||
[(sel1 :#grub-list) :.grub-item])
|
||||
|
@ -88,24 +85,24 @@
|
|||
(defn make-recipe-node
|
||||
([id name grubs] (make-recipe-node id name grubs false))
|
||||
([id name grubs new-recipe]
|
||||
(node [: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}]
|
||||
(when-not new-recipe
|
||||
[: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"]]])))
|
||||
[: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}]
|
||||
(when-not new-recipe
|
||||
[: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"]]]))
|
||||
|
||||
(def new-recipe (make-recipe-node "new-recipe" "" "" true))
|
||||
|
||||
|
@ -124,36 +121,9 @@
|
|||
(defn recipe-add-grubs-btns-selector []
|
||||
[(sel1 :body) :.recipe-add-grubs-btn])
|
||||
|
||||
(deftemplate main-template []
|
||||
[:div.container
|
||||
[:div.row
|
||||
[:div.col-sm-6.leftmost-column
|
||||
[:h3 "Grub List"]
|
||||
[:div#addGrubInput]
|
||||
[:ul#grub-list.list-group]
|
||||
clear-all-btn]
|
||||
[:div.col-sm-6
|
||||
[:h3.recipes-title "Recipes"]
|
||||
new-recipe
|
||||
[:ul#recipe-list.list-group.recipe-list]]]])
|
||||
|
||||
(deftemplate grub-list-template [grubs]
|
||||
(node (for [grub grubs]
|
||||
(make-grub-node (:id grub) (:grub grub) (:completed grub)))))
|
||||
|
||||
(defn add-grub-input [data]
|
||||
(om/component
|
||||
(html [:div.input-group
|
||||
add-grub-text
|
||||
[:span.input-group-btn
|
||||
add-grub-btn]])))
|
||||
|
||||
(defn render-om []
|
||||
(om/root add-grub-input {} {:target (.getElementById js/document "addGrubInput")}))
|
||||
|
||||
(defn render-body []
|
||||
(dommy/prepend! (sel1 :body) (main-template))
|
||||
(render-om))
|
||||
(defn grub-list-template [grubs]
|
||||
(for [grub grubs]
|
||||
(make-grub-node (:id grub) (:grub grub) (:completed grub))))
|
||||
|
||||
(defn render-grub-list [grubs]
|
||||
(let [grub-list (sel1 :#grub-list)]
|
||||
|
@ -325,4 +295,40 @@
|
|||
recipe-list (sel1 :#recipe-list)]
|
||||
(dommy/append! recipe-list recipe)
|
||||
recipe))
|
||||
|
||||
(defn grub-view []
|
||||
(om/component
|
||||
(html
|
||||
[:div.container
|
||||
[:div.row
|
||||
[:div.col-sm-6.leftmost-column
|
||||
[: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"}]]
|
||||
[:button.btn.btn-primary {:id "add-grub-btn" :type "button"} "Add"]]
|
||||
[:ul#grub-list.list-group]
|
||||
[:button.btn.hidden.pull-right
|
||||
{:id "clear-all-btn" :type "button"}
|
||||
"Clear all"]]
|
||||
[:div.col-sm-6
|
||||
[: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]]]])))
|
||||
|
||||
(defn render-body [state]
|
||||
(logs state)
|
||||
(om/root grub-view state {:target (.getElementById js/document "container")}))
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
(a/map< parse-complete-event)))
|
||||
|
||||
(defn get-clear-all-events []
|
||||
(->> (:chan (dom/listen dom/clear-all-btn :click))
|
||||
(->> (:chan (dom/listen (sel1 :#clear-all-btn) :click))
|
||||
(a/map< (fn [e] {:event :clear-all-grubs}))))
|
||||
|
||||
(defn get-grub-mousedown-events []
|
||||
|
@ -127,68 +127,3 @@
|
|||
(defn sort-and-render-grub-list! [grubs]
|
||||
(let [sorted-grubs (sort-by (juxt :completed get-grub-ingredient) (vals grubs))]
|
||||
(dom/render-grub-list sorted-grubs)))
|
||||
|
||||
(defmulti handle-event (fn [event grubs] (:event event))
|
||||
:default :unknown-event)
|
||||
|
||||
(defmethod handle-event :unknown-event [event grubs]
|
||||
;(logs "Cannot handle unknown event:" event)
|
||||
grubs)
|
||||
|
||||
(defmethod handle-event :add-grub [event grubs]
|
||||
(let [grub (dom/make-new-grub (:id event) (:grub event) (:completed event))
|
||||
new-grubs (assoc grubs (:id grub) grub)]
|
||||
(dom/-show! dom/clear-all-btn)
|
||||
(sort-and-render-grub-list! new-grubs)
|
||||
(dom/clear-new-grub-input!)
|
||||
(dom/focus-new-grub-input!)
|
||||
new-grubs))
|
||||
|
||||
(defn assoc-new-grub [current new]
|
||||
(assoc current (:id new)
|
||||
(dom/make-new-grub (:id new) (:grub new) (:completed new))))
|
||||
|
||||
(defn make-add-grubs-map [grub-events]
|
||||
(reduce assoc-new-grub {} grub-events))
|
||||
|
||||
(defmethod handle-event :add-grub-list [event grubs]
|
||||
(let [add-grub-events (:grubs event)
|
||||
add-grubs (make-add-grubs-map add-grub-events)
|
||||
new-grubs (merge grubs add-grubs)]
|
||||
(dom/-show! dom/clear-all-btn)
|
||||
(sort-and-render-grub-list! new-grubs)
|
||||
new-grubs))
|
||||
|
||||
(defmethod handle-event :complete-grub [event grubs]
|
||||
(let [grub (get grubs (:id event))
|
||||
new-grubs (assoc-in grubs [(:id event) :completed] true)]
|
||||
(sort-and-render-grub-list! new-grubs)
|
||||
new-grubs))
|
||||
|
||||
(defmethod handle-event :uncomplete-grub [event grubs]
|
||||
(let [new-grubs (assoc-in grubs [(:id event) :completed] false)]
|
||||
(sort-and-render-grub-list! new-grubs)
|
||||
new-grubs))
|
||||
|
||||
(defmethod handle-event :update-grub [event grubs]
|
||||
(let [new-grubs (assoc-in grubs [(:id event) :grub] (:grub event))]
|
||||
(sort-and-render-grub-list! new-grubs)
|
||||
new-grubs))
|
||||
|
||||
(defmethod handle-event :clear-all-grubs [event grubs]
|
||||
(dom/-hide! dom/clear-all-btn)
|
||||
(dom/clear-grubs!)
|
||||
{})
|
||||
|
||||
(defn handle-grubs [remote-events]
|
||||
(let [out (chan)
|
||||
local-events [(get-create-events)
|
||||
(get-complete-events)
|
||||
(get-clear-all-events)
|
||||
(get-update-events)]]
|
||||
(go-loop [grubs {}]
|
||||
(let [[event c] (a/alts! (conj local-events remote-events))]
|
||||
(when-not (= c remote-events)
|
||||
(>! out event))
|
||||
(recur (handle-event event grubs))))
|
||||
out))
|
||||
|
|
Loading…
Reference in a new issue