New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement collection diff updates #12

Merged
merged 6 commits into from Feb 5, 2017
Copy path View file
@@ -6,10 +6,10 @@
(defprotocol IDom
(create-component! [this type])
(delete-component! [this node])
(set-child! [this parent id child])
(set-indexed-child! [this parent k idx child])
(delete-indexed-child! [this parent k idx child])
(replace-indexed-child! [this parent k idx child])
(set-property! [this node property value]))
(defquasitype Component [type dom-node props])
@@ -57,7 +57,7 @@
;; TODO: Unmount?
Created (set-indexed-child! dom parent-node k idx node)
Deleted (delete-indexed-child! dom parent-node k idx node)
Updated nil
Updated (replace-indexed-child! dom parent-node k idx node)
Noop nil)))))
(defn diff-component [dom dom-node spec-a spec-b]
@@ -67,7 +67,8 @@
(if (sequential? vb)
(diff-child-list dom dom-node k va vb)
(let [result (diff dom va vb)]
(if (instance? Created result)
(if (or (instance? Created result)
(instance? Updated result))
(set-property! dom dom-node k (:node result)))))))
nil
spec-a)
@@ -78,48 +79,49 @@
(if (sequential? vb)
(diff-child-list dom dom-node k nil vb)
(let [result (diff dom nil vb)]
(if (instance? Created result)
(if (or (instance? Created result)
(instance? Updated result))
(set-property! dom dom-node k (:node result)))))))
nil
spec-b))
(defn diff [dom a b]
(condp = [(val-type a) (val-type b)]
[:nil :comp] (let [node (create-component! dom (:type b))]
(assert node "No Node returned by create-component!")
(set-once! b :dom-node node)
(diff-component dom node nil (:props b))
(->Created node))
(defn diff
[dom a b]
(let [refresh-node (fn [node compo-a compo-b]
(set-once! compo-b :dom-node node)
(diff-component dom node (:props compo-a) (:props compo-b))
node)
new-node (fn [compo]
(let [node (create-component! dom (:type compo))]
(assert node "No Node returned by create-component!")
(refresh-node node nil compo)))]
(condp = [(val-type a) (val-type b)]
[:nil :comp] (->Created (new-node b))
[:val :val] (if (= a b)
(->Noop b)
(->Created b))
[:val :val] (if (= a b)
(->Noop b)
(->Updated b))
[:nil :val] (->Created b)
[:nil :val] (->Created b)
[:nil :ucomp] (diff dom nil (render-user-component b))
[:nil :ucomp] (diff dom nil (render-user-component b))
[:ucomp :nil] (diff dom (render-user-component a) nil)
[:ucomp :nil] (diff dom (render-user-component a) nil)
[:ucomp :ucomp] (if (needs-update? a b)
(diff dom (render-user-component a) (render-user-component b))
(->Noop (:dom-node (:render-result b))))
[:ucomp :ucomp] (if (needs-update? a b)
(diff dom (render-user-component a) (render-user-component b))
(->Noop (:dom-node (:render-result b))))
[:comp :comp] (if (= (:type a) (:type b))
(let [spec-a (:props a)
spec-b (:props b)
dom-node (:dom-node a)]
(assert dom-node (str "No DOM Node" (pr-str a)))
(set-once! b :dom-node dom-node)
[:comp :comp] (-> (if (= (:type a) (:type b))
(doto (:dom-node a)
(assert (str "No DOM Node" (pr-str a)))
(refresh-node a b))
(new-node b))
->Updated)
(diff-component dom dom-node spec-a spec-b)
[:comp :nil] (->Deleted (:dom-node a))
(->Updated dom-node))
(do (delete-component! dom (:dom-node a))
(diff dom nil b)))
[:comp :nil] (->Deleted (:dom-node a))))
[:val :nil] (->Deleted a))))
(defn component
Copy path View file
@@ -29,20 +29,20 @@
(set-indexed-child! [this parent k idx child]
(run-later
(let [^List lst (render-core/get-property (unwrap-promise parent) k)]
(assert (= idx (count lst)) "TODO: Implement this")
(.add lst (unwrap-promise child)))))
(let [^List lst (render-core/get-property (unwrap-promise parent) k)]
(.add lst idx (unwrap-promise child)))))
(delete-indexed-child! [this parent k idx child]
(run-later
(let [^List lst (render-core/get-property (unwrap-promise parent) k)]
(assert (= idx (dec (count lst))) "TODO: Implement this")
(.remove lst ^int idx))))
(delete-component! [this node]
nil))
(let [^List lst (render-core/get-property (unwrap-promise parent) k)]
(.remove lst (unwrap-promise child)))))
(replace-indexed-child! [this parent k idx child]
(run-later
(let [^List lst (render-core/get-property (unwrap-promise parent) k)]
(doto lst
(.remove ^int idx)
(.add ^int idx (unwrap-promise child)))))))
(defrecord App [prev-state dom root handler-fn])
@@ -51,7 +51,7 @@
(defn app
([init-state]
(app init-state default-handler-fn))
(app init-state default-handler-fn))
([init-state default-handler-fn]
(let [dom (->FXDom default-handler-fn)
root (:node (diff dom nil init-state))]
@@ -61,5 +61,3 @@
(defn update-app [{:keys [prev-state dom root handler-fn]} new-state]
(let [new-node (:node (time (diff dom prev-state new-state)))]
(->App new-state dom new-node handler-fn)))
Copy path View file
@@ -24,7 +24,9 @@
(delete-indexed-child! [this parent k idx child]
(swap! this update-in [:log] conj [:delete-indexed-child parent k idx child]))
)
(replace-indexed-child! [this parent k idx child]
(swap! this update-in [:log] conj [:replace-indexed-child parent k idx child])))
(defn log []
@@ -90,8 +92,11 @@
[:create 2 :button]
[:set-property 2 :text "Hello World"]
[:set-property 1 :root 2]]}))))
)
(testing "from value to nil"
(let [log (log)
value :x]
(is (= (->Deleted value) (diff log value nil))))))
(deftest component-diffing
(testing "can change spec values"
@@ -111,12 +116,11 @@
component-a (component :button {:text "test1"})
component-b (component :text {:text "test2"})]
(is (= (->Created 1) (diff log nil component-a)))
(is (= (->Created 2) (diff log component-a component-b)))
(is (= (->Updated 2) (diff log component-a component-b)))
(is (= @log {:id 2
:log [[:create 1 :button]
[:set-property 1 :text "test1"]
[:delete 1]
[:create 2 :text]
[:set-property 2 :text "test2"]]}))))
@@ -183,6 +187,35 @@
[:set-indexed-child 1 :children 0 2]
[:delete-indexed-child 1 :children 0 2]]}))))
(testing "can replace child in list"
(let [log (log)
component-a (component :list {:children [(component :text {:text "Hey"})]})
component-b (component :list {:children [(component :text {:text "Bye"})]})
component-c (component :list {:children [(component :button {:text "Click me"})]})]
(is (= (->Created 1) (diff log nil component-a)))
(is (= (->Updated 1) (diff log component-a component-b)))
(is (= @log {:id 2
:log [[:create 1 :list]
[:create 2 :text]
[:set-property 2 :text "Hey"]
[:set-indexed-child 1 :children 0 2]
[:set-property 2 :text "Bye"]
[:replace-indexed-child 1 :children 0 2]]}))
(is (= (->Updated 1) (diff log component-b component-c)))
(is (= @log {:id 3
:log [[:create 1 :list]
[:create 2 :text]
[:set-property 2 :text "Hey"]
[:set-indexed-child 1 :children 0 2]
[:set-property 2 :text "Bye"]
[:replace-indexed-child 1 :children 0 2]
[:create 3 :button]
[:set-property 3 :text "Click me"]
[:replace-indexed-child 1 :children 0 3]]}))))
(testing "complex nesting"
(let [log (log)
ProTip! Use n and p to navigate between commits in a pull request.