Skip to content

Instantly share code, notes, and snippets.

@mpereira
Created February 3, 2014 14:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mpereira/03bd0a1c943a00cb30db to your computer and use it in GitHub Desktop.
Save mpereira/03bd0a1c943a00cb30db to your computer and use it in GitHub Desktop.
(ns om-laboratory.core
(:require [om.core :as om :include-macros true]
[om.dom :as dom :include-macros true]
[clojure.string :as string]))
(enable-console-print!)
(defn index-of [coll val]
(first (keep-indexed #(if (= %2 val) %1) coll)))
(def application-state
(atom {:tasks [{:text "buy milk" :done true}
{:text "pay bills" :done true}
{:text "do statistics" :done false}
{:text "learn clojure properly" :done false}]}))
(defn task-text-form-view [props owner]
(defn class-names []
(string/join " " (cons "task-text-form" (om/get-state owner :class-names))))
(defn handle-on-submit! [event]
(.preventDefault event)
(let [input-node (om/get-node owner "task-text-input")]
(when-let [task-text (.-value input-node)]
(set! (.-value input-node) nil)
((om/get-state owner :on-submit!) {:text task-text
:done (get @props :done false)}))))
(reify
om/IRenderState
(render-state [this state]
(dom/form
#js {:className (class-names) :onSubmit handle-on-submit!}
(dom/input #js {:className "task-text-input"
:ref "task-text-input"
:name "task[text]"
:placeholder (get-in state [:input :placeholder])
:required (get-in state [:input :required])
:defaultValue (:text props)})))))
(defn task-view [props owner]
(defn class-names []
(string/join " " ["task" (if (:done props) "done")]))
(defn toggle-being-edited! [& [event]]
(if event (.preventDefault event))
(om/set-state! owner :being-edited? (not (om/get-state owner :being-edited?))))
(defn on-task-text-form-submit [task-delta]
(toggle-being-edited!)
((om/get-state owner :update-task!) @props task-delta))
(defn update-task-done [event]
(.preventDefault event)
((om/get-state owner :update-task!) @props {:done (.-checked (.-target event))}))
(reify
om/IInitState
(init-state [this]
{:being-edited? false})
om/IRenderState
(render-state [this {:keys [remove-task! being-edited?]}]
(apply
dom/li #js {:className (class-names)}
(cond
being-edited? [(om/build task-text-form-view
props
{:init-state
{:class-names ["update-task-text-form"]
:on-submit! on-task-text-form-submit}})]
:else [(dom/input #js {:className "task-done-input"
:onChange update-task-done
:type "checkbox"
:name "task[done]"
:checked (:done @props)})
(dom/span #js {:className "text"
:onDoubleClick toggle-being-edited!}
(:text props))
(dom/button #js {:className "delete"
:onClick #(remove-task! @props)}
"Delete")])))))
(defn tasks-view [props owner]
(reify
om/IRenderState
(render-state [this state]
(apply
dom/ul #js {:className "tasks"}
(om/build-all
task-view
(:tasks props)
{:init-state (select-keys state [:remove-task! :update-task!])})))))
(defn todo-application [props owner]
(defn remove-task! [task]
(om/transact! props :tasks #(into [] (remove (partial identical? task) %))))
(defn create-task! [task]
(om/transact! props :tasks #(into [] (conj % task))))
(defn update-task! [task task-delta]
(om/transact!
props
:tasks
#(into [] (assoc % (index-of % task) (merge task task-delta)))))
(reify
om/IRender
(render [this]
(dom/div
#js {:className "todo-application"}
(dom/h1 #js {:className "title"} "Todo")
(om/build task-text-form-view
props
{:init-state
{:class-names ["create-task-text-form"]
:on-submit! create-task!
:input {:placeholder "What needs to be done?"
:required true}}})
(om/build tasks-view
props
{:init-state {:remove-task! remove-task!
:create-task! create-task!
:update-task! update-task!}})))))
(om/root
application-state
todo-application
(. js/document (getElementById "application")))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment