Skip to content

Instantly share code, notes, and snippets.

@bendlas
Forked from anmonteiro/composite.cljs
Created May 31, 2016 13:46
Show Gist options
  • Save bendlas/1a16b216fb0fbe0e02ef446711f18c4a to your computer and use it in GitHub Desktop.
Save bendlas/1a16b216fb0fbe0e02ef446711f18c4a to your computer and use it in GitHub Desktop.
Code for the series of posts "An exploration of object recursion design patterns in Om Next recursive queries"
(ns rec
(:require
[webnf.base.logging :as log]
[om.next :as om :refer-macros [defui]]
[goog.dom :as gdom]
[om-tools.dom :as dom]
[clojure.string :as str]))
(def composite-data
{:composite/item {:id 0
:width 400
:height 400
:color "#428BCA"
:children [{:id 1
:width 200
:height 200
:color "#9ACD32"
:children [{:id 3
:width 100
:height 100
:color "#CD329A"}
{:id 4
:width 100
:height 100
:color "#32CD65"}]}
{:id 2
:width 200
:height 200
:color "#39DBBE"}]}})
(declare component)
(defn display-id [id]
(dom/div #js {:style #js {:position "absolute"
:textAlign "right"
:bottom 0
:zIndex 1
:right 5}}
(dom/span nil
(str id))))
(defn common-div [props & children]
(let [{:keys [id width height color]} props]
(dom/div #js {:className (str id)
:style
#js {:position "relative"
:float "left"
:width width
:height height
:zIndex 2
:textAlign "center"
:backgroundColor color}}
children
(display-id id))))
(defui Composite
static om/IQuery
(query [this]
'[:id :width :height :color {:children ...}])
Object
(render [this]
(let [{:keys [children] :as props} (om/props this)]
(dom/div {:on-click #(om/transact! this '[(composite!)])}
(common-div props (map component children))))))
(def composite (om/factory Composite))
(defui Leaf
static om/IQuery
(query [this]
'[:id :width :height :color])
Object
(render [this]
(dom/div {:on-click #(om/transact! this '[(leaf!)])}
(common-div (om/props this)))))
(def leaf (om/factory Leaf))
(defui Component
static om/Ident
(ident [this {:keys [id children]}]
(if-not (nil? children)
[:composite id]
[:leaf id]))
static om/IQuery
(query [this]
{:leaf (om/get-query Leaf)
:composite (om/get-query Composite)})
Object
(render [this]
(let [{:keys [id] :as props} (om/props this)
[type id] (om/get-ident this)]
(({:composite composite
:leaf leaf} type) props))))
(def component (om/factory Component))
(defui CompositeApp
static om/IQuery
(query [this]
[{:composite/item (om/get-query Component)}])
Object
(render [this]
(let [{:keys [composite/item]} (om/props this)]
(dom/div #js {:style #js {:margin "0 auto"
:display "table"}}
(component item)
(dom/div #js {:style #js {:clear "both"}})))))
(defmulti composite-read om/dispatch)
(defmethod composite-read :default
[{:keys [data] :as env} k _]
{:value (get data k)})
(defmethod composite-read :children
[{:keys [parser data union-query state] :as env} k _]
(let [st @state
f #(parser (assoc env :data (get-in st %)) ((first %) union-query))]
{:value (into [] (map f) (:children data))}))
(defmethod composite-read :composite/item
[{:keys [state parser query ast] :as env} k _]
(let [st @state
[type id :as entry] (get st k)
data (get-in st entry)
new-env (assoc env :data data :union-query query)]
{:value (parser new-env (type query))}))
(def composite-reconciler
(om/reconciler {:state composite-data
:parser (om/parser {:read composite-read
:mutate (fn [{:keys [state]} k p]
{:action #(do (log/info "MUTATE" (str k) (str p))
(swap! state update :mutations conj k))})})}))
(om/add-root! composite-reconciler CompositeApp (js/document.getElementById "app-main"))
;; =============================================================================
;; Decorator
(def undecorated-data
{:decorator/app {:id 0
:width 300
:height 300
:color "#DBD639"}})
(def decorated-data
{:decorator/app {:id 1
:decorator/text "I am a text message"
:next {:id 2
:decorator/image "/resources/public/devcards/cljs.svg"
:image/max-width 200
:next (:decorator/app undecorated-data)}}})
(defn display-id [id]
(dom/div #js {:style #js {:position "absolute"
:textAlign "right"
:bottom 0
:zIndex 1
:right 5}}
(dom/span nil
(str id))))
(defn common-div [props & children]
(let [{:keys [id width height color]} props]
(dom/div #js {:className (str id)
:style
#js {:position "relative"
:float "left"
:width width
:height height
:zIndex 2
:textAlign "center"
:backgroundColor color}}
children
(display-id id))))
(declare component)
(defui ImageDecorator
static om/IQuery
(query [this]
'[:id :decorator/image :image/max-width {:next ...}])
Object
(render [this]
(let [{:keys [id decorator/image image/max-width next] :as props} (om/props this)]
(component next
(om/children this)
(dom/img #js {:src image
:style #js {:maxWidth max-width
:display "block"
:margin "15px auto"}})))))
(def image-decorator (om/factory ImageDecorator))
(defui TextDecorator
static om/IQuery
(query [this]
'[:id :decorator/text {:next ...}])
Object
(render [this]
(let [{:keys [id decorator/text next] :as props} (om/props this)]
(component next
(om/children this)
(dom/p #js {:style #js {:margin "15px 0"
:textAlign "center"}} text)))))
(def text-decorator (om/factory TextDecorator))
(defui ConcreteComponent
static om/IQuery
(query [this]
'[:id :width :height :color])
Object
(render [this]
(common-div (om/props this)
(om/children this))))
(def concrete-component (om/factory ConcreteComponent))
(defui Component
static om/Ident
(ident [this {:keys [id decorator/text decorator/image]}]
(cond
(not (nil? text)) [:text id]
(not (nil? image)) [:image id]
:else [:component id]))
static om/IQuery
(query [this]
{:text (om/get-query TextDecorator)
:image (om/get-query ImageDecorator)
:component (om/get-query ConcreteComponent)})
Object
(render [this]
(let [{:keys [id] :as props} (om/props this)
[type id] (om/get-ident this)]
(({:text text-decorator
:image image-decorator
:component concrete-component} type) props
(om/children this)))))
(def component (om/factory Component))
(defui DecoratorApp
static om/IQuery
(query [this]
[{:decorator/app (om/get-query Component)}])
Object
(render [this]
(let [{:keys [decorator/app]} (om/props this)]
(dom/div #js {:style #js {:margin "0 auto"
:display "table"}}
(component app)
(dom/div #js {:style #js {:clear "both"}})))))
(defmulti decorator-read om/dispatch)
(defmethod decorator-read :default
[{:keys [data] :as env} k _]
{:value (get data k)})
(defmethod decorator-read :next
[{:keys [parser data union-query state] :as env} k _]
(let [st @state
f #(parser (assoc env :data (get-in st %)) ((first %) union-query))]
{:value (f (:next data))}))
(defmethod decorator-read :decorator/app
[{:keys [state parser query ast] :as env} k _]
(let [st @state
[type id :as entry] (get st k)
data (get-in st entry)
new-env (assoc env :data data :union-query query)]
{:value (parser new-env (type query))}))
(def undecorated-reconciler
(om/reconciler {:state undecorated-data
:parser (om/parser {:read decorator-read})}))
(def decorator-reconciler
(om/reconciler {:state decorated-data
:parser (om/parser {:read decorator-read})}))
(om/add-root! decorator-reconciler DecoratorApp (js/document.getElementById "app"))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment