-
-
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"
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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")) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;; ============================================================================= | |
;; 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