From 14fcbcc33bc3dd46817deedb352f10d72a04df18 Mon Sep 17 00:00:00 2001 From: Konstantinos Kaloutas Date: Fri, 8 Nov 2024 10:52:14 +0200 Subject: [PATCH] add tests and refactor --- .../metosin/malli-types-cljs/config.edn | 603 ++++++++---------- src/renderer/element/handlers.cljs | 50 +- test/element_test.cljs | 57 ++ 3 files changed, 348 insertions(+), 362 deletions(-) diff --git a/.clj-kondo/metosin/malli-types-cljs/config.edn b/.clj-kondo/metosin/malli-types-cljs/config.edn index 5edee888..5dcd6e9a 100644 --- a/.clj-kondo/metosin/malli-types-cljs/config.edn +++ b/.clj-kondo/metosin/malli-types-cljs/config.edn @@ -2563,13 +2563,13 @@ ->unit {:arities {2 {:args [:number :string], :ret :number}}}, unit->px {:arities {1 {:args [:any], :ret :number}}}}, - renderer.utils.path {manipulate {:arities {2 {:args [:string :keyword], + renderer.utils.path {get-d {:arities {1 {:args [:any], :ret :string}}}, + manipulate {:arities {2 {:args [:string :keyword], :ret :string}}}, boolean-operation {:arities {3 {:args [:string :string :keyword], - :ret :string}}}, - get-d {:arities {1 {:args [:any], :ret :string}}}}, + :ret :string}}}}, renderer.document.handlers {search-by-path {:arities {2 {:args [{:op :keys, :opt {:system-fonts :vector, :copied-bounds :seqable, @@ -4956,6 +4956,162 @@ :req {:tasks :map}}, :pen-mode :boolean}}], :ret :vector}}}, + collapse-all {:arities {1 {:args [{:op :keys, + :opt {:system-fonts :vector, + :copied-bounds :seqable, + :active-document :any, + :adjusted-pointer-offset :seqable, + :re-pressed.core/keydown :map, + :pivot-point :seqable, + :viewbox-kdtree :any, + :copied-elements :seqable, + :kdtree :any, + :pointer-offset :seqable, + :nearest-neighbors :sequential, + :event-time :number, + :drag :boolean, + :version :string, + :nearest-neighbor {:op :keys, + :req {:point :seqable, + :base-point :seqable, + :dist-squared :number}, + :nilable true}, + :dom-rect {:op :keys, + :req {:x :number, + :y :number, + :width :number, + :height :number, + :top :number, + :right :number, + :bottom :number, + :left :number}}, + :nearest-neighbor-offset :nilable/seqable, + :clicked-element :any, + :primary-tool :any}, + :req {:double-click-delta :any, + :dialogs :vector, + :repl-mode :keyword, + :snap {:op :keys, + :req {:active :boolean, + :threshold :number, + :options :set}}, + :grid :boolean, + :tool :any, + :ruler {:op :keys, + :req {:visible :boolean, + :locked :boolean, + :size :number}}, + :cursor :string, + :pointer-pos :seqable, + :state :keyword, + :window {:op :keys, + :req {:maximized :boolean, + :minimized :boolean, + :fullscreen :boolean, + :focused :boolean}}, + :theme {:op :keys, + :opt {:native-mode :keyword}, + :req {:mode :keyword}}, + :lang :any, + :document-tabs :vector, + :documents :map, + :notifications :seqable, + :recent :vector, + :zoom-sensitivity :any, + :debug-info :boolean, + :adjusted-pointer-pos :seqable, + :timeline {:op :keys, + :req {:time :number, + :speed :number, + :replay? :boolean, + :grid-snap? :boolean, + :guide-snap? :boolean, + :paused? :boolean}}, + :backdrop :boolean, + :drag-threshold :number, + :fx :vector, + :panels :map, + :worker {:op :keys, + :req {:tasks :map}}, + :pen-mode :boolean}}], + :ret {:op :keys, + :opt {:system-fonts :vector, + :copied-bounds :seqable, + :active-document :any, + :adjusted-pointer-offset :seqable, + :re-pressed.core/keydown :map, + :pivot-point :seqable, + :viewbox-kdtree :any, + :copied-elements :seqable, + :kdtree :any, + :pointer-offset :seqable, + :nearest-neighbors :sequential, + :event-time :number, + :drag :boolean, + :version :string, + :nearest-neighbor {:op :keys, + :req {:point :seqable, + :base-point :seqable, + :dist-squared :number}, + :nilable true}, + :dom-rect {:op :keys, + :req {:x :number, + :y :number, + :width :number, + :height :number, + :top :number, + :right :number, + :bottom :number, + :left :number}}, + :nearest-neighbor-offset :nilable/seqable, + :clicked-element :any, + :primary-tool :any}, + :req {:double-click-delta :any, + :dialogs :vector, + :repl-mode :keyword, + :snap {:op :keys, + :req {:active :boolean, + :threshold :number, + :options :set}}, + :grid :boolean, + :tool :any, + :ruler {:op :keys, + :req {:visible :boolean, + :locked :boolean, + :size :number}}, + :cursor :string, + :pointer-pos :seqable, + :state :keyword, + :window {:op :keys, + :req {:maximized :boolean, + :minimized :boolean, + :fullscreen :boolean, + :focused :boolean}}, + :theme {:op :keys, + :opt {:native-mode :keyword}, + :req {:mode :keyword}}, + :lang :any, + :document-tabs :vector, + :documents :map, + :notifications :seqable, + :recent :vector, + :zoom-sensitivity :any, + :debug-info :boolean, + :adjusted-pointer-pos :seqable, + :timeline {:op :keys, + :req {:time :number, + :speed :number, + :replay? :boolean, + :grid-snap? :boolean, + :guide-snap? :boolean, + :paused? :boolean}}, + :backdrop :boolean, + :drag-threshold :number, + :fx :vector, + :panels :map, + :worker {:op :keys, + :req {:tasks :map}}, + :pen-mode :boolean}}}}}, assoc-prop {:arities {3 {:args [{:op :keys, :opt {:system-fonts :vector, :copied-bounds :seqable, @@ -6313,186 +6469,30 @@ :replay? :boolean, :grid-snap? :boolean, :guide-snap? :boolean, - :paused? :boolean}}, - :backdrop :boolean, - :drag-threshold :number, - :fx :vector, - :panels :map, - :worker {:op :keys, - :req {:tasks :map}}, - :pen-mode :boolean}} - :any], - :ret {:op :keys, - :opt {:children :vector, - :selected :boolean, - :parent :any, - :content :string, - :type :any, - :bounds :seqable, - :locked :boolean, - :label :string, - :id :any, - :attrs :map, - :visible :boolean}, - :req {:tag :any}, - :nilable true}}}}, - expand {:arities {1 {:args [{:op :keys, - :opt {:system-fonts :vector, - :copied-bounds :seqable, - :active-document :any, - :adjusted-pointer-offset :seqable, - :re-pressed.core/keydown :map, - :pivot-point :seqable, - :viewbox-kdtree :any, - :copied-elements :seqable, - :kdtree :any, - :pointer-offset :seqable, - :nearest-neighbors :sequential, - :event-time :number, - :drag :boolean, - :version :string, - :nearest-neighbor {:op :keys, - :req {:point :seqable, - :base-point :seqable, - :dist-squared :number}, - :nilable true}, - :dom-rect {:op :keys, - :req {:x :number, - :y :number, - :width :number, - :height :number, - :top :number, - :right :number, - :bottom :number, - :left :number}}, - :nearest-neighbor-offset :nilable/seqable, - :clicked-element :any, - :primary-tool :any}, - :req {:double-click-delta :any, - :dialogs :vector, - :repl-mode :keyword, - :snap {:op :keys, - :req {:active :boolean, - :threshold :number, - :options :set}}, - :grid :boolean, - :tool :any, - :ruler {:op :keys, - :req {:visible :boolean, - :locked :boolean, - :size :number}}, - :cursor :string, - :pointer-pos :seqable, - :state :keyword, - :window {:op :keys, - :req {:maximized :boolean, - :minimized :boolean, - :fullscreen :boolean, - :focused :boolean}}, - :theme {:op :keys, - :opt {:native-mode :keyword}, - :req {:mode :keyword}}, - :lang :any, - :document-tabs :vector, - :documents :map, - :notifications :seqable, - :recent :vector, - :zoom-sensitivity :any, - :debug-info :boolean, - :adjusted-pointer-pos :seqable, - :timeline {:op :keys, - :req {:time :number, - :speed :number, - :replay? :boolean, - :grid-snap? :boolean, - :guide-snap? :boolean, - :paused? :boolean}}, - :backdrop :boolean, - :drag-threshold :number, - :fx :vector, - :panels :map, - :worker {:op :keys, - :req {:tasks :map}}, - :pen-mode :boolean}}], - :ret {:op :keys, - :opt {:system-fonts :vector, - :copied-bounds :seqable, - :active-document :any, - :adjusted-pointer-offset :seqable, - :re-pressed.core/keydown :map, - :pivot-point :seqable, - :viewbox-kdtree :any, - :copied-elements :seqable, - :kdtree :any, - :pointer-offset :seqable, - :nearest-neighbors :sequential, - :event-time :number, - :drag :boolean, - :version :string, - :nearest-neighbor {:op :keys, - :req {:point :seqable, - :base-point :seqable, - :dist-squared :number}, - :nilable true}, - :dom-rect {:op :keys, - :req {:x :number, - :y :number, - :width :number, - :height :number, - :top :number, - :right :number, - :bottom :number, - :left :number}}, - :nearest-neighbor-offset :nilable/seqable, - :clicked-element :any, - :primary-tool :any}, - :req {:double-click-delta :any, - :dialogs :vector, - :repl-mode :keyword, - :snap {:op :keys, - :req {:active :boolean, - :threshold :number, - :options :set}}, - :grid :boolean, - :tool :any, - :ruler {:op :keys, - :req {:visible :boolean, - :locked :boolean, - :size :number}}, - :cursor :string, - :pointer-pos :seqable, - :state :keyword, - :window {:op :keys, - :req {:maximized :boolean, - :minimized :boolean, - :fullscreen :boolean, - :focused :boolean}}, - :theme {:op :keys, - :opt {:native-mode :keyword}, - :req {:mode :keyword}}, - :lang :any, - :document-tabs :vector, - :documents :map, - :notifications :seqable, - :recent :vector, - :zoom-sensitivity :any, - :debug-info :boolean, - :adjusted-pointer-pos :seqable, - :timeline {:op :keys, - :req {:time :number, - :speed :number, - :replay? :boolean, - :grid-snap? :boolean, - :guide-snap? :boolean, - :paused? :boolean}}, - :backdrop :boolean, - :drag-threshold :number, - :fx :vector, - :panels :map, - :worker {:op :keys, - :req {:tasks :map}}, - :pen-mode :boolean}}}, - 2 {:args [{:op :keys, + :paused? :boolean}}, + :backdrop :boolean, + :drag-threshold :number, + :fx :vector, + :panels :map, + :worker {:op :keys, + :req {:tasks :map}}, + :pen-mode :boolean}} + :any], + :ret {:op :keys, + :opt {:children :vector, + :selected :boolean, + :parent :any, + :content :string, + :type :any, + :bounds :seqable, + :locked :boolean, + :label :string, + :id :any, + :attrs :map, + :visible :boolean}, + :req {:tag :any}, + :nilable true}}}}, + expand {:arities {2 {:args [{:op :keys, :opt {:system-fonts :vector, :copied-bounds :seqable, :active-document :any, @@ -12472,6 +12472,85 @@ :worker {:op :keys, :req {:tasks :map}}, :pen-mode :boolean}}}}}, + parent-ids {:arities {1 {:args [{:op :keys, + :opt {:system-fonts :vector, + :copied-bounds :seqable, + :active-document :any, + :adjusted-pointer-offset :seqable, + :re-pressed.core/keydown :map, + :pivot-point :seqable, + :viewbox-kdtree :any, + :copied-elements :seqable, + :kdtree :any, + :pointer-offset :seqable, + :nearest-neighbors :sequential, + :event-time :number, + :drag :boolean, + :version :string, + :nearest-neighbor {:op :keys, + :req {:point :seqable, + :base-point :seqable, + :dist-squared :number}, + :nilable true}, + :dom-rect {:op :keys, + :req {:x :number, + :y :number, + :width :number, + :height :number, + :top :number, + :right :number, + :bottom :number, + :left :number}}, + :nearest-neighbor-offset :nilable/seqable, + :clicked-element :any, + :primary-tool :any}, + :req {:double-click-delta :any, + :dialogs :vector, + :repl-mode :keyword, + :snap {:op :keys, + :req {:active :boolean, + :threshold :number, + :options :set}}, + :grid :boolean, + :tool :any, + :ruler {:op :keys, + :req {:visible :boolean, + :locked :boolean, + :size :number}}, + :cursor :string, + :pointer-pos :seqable, + :state :keyword, + :window {:op :keys, + :req {:maximized :boolean, + :minimized :boolean, + :fullscreen :boolean, + :focused :boolean}}, + :theme {:op :keys, + :opt {:native-mode :keyword}, + :req {:mode :keyword}}, + :lang :any, + :document-tabs :vector, + :documents :map, + :notifications :seqable, + :recent :vector, + :zoom-sensitivity :any, + :debug-info :boolean, + :adjusted-pointer-pos :seqable, + :timeline {:op :keys, + :req {:time :number, + :speed :number, + :replay? :boolean, + :grid-snap? :boolean, + :guide-snap? :boolean, + :paused? :boolean}}, + :backdrop :boolean, + :drag-threshold :number, + :fx :vector, + :panels :map, + :worker {:op :keys, + :req {:tasks :map}}, + :pen-mode :boolean}}], + :ret :set}}}, scale {:arities {4 {:args [{:op :keys, :opt {:system-fonts :vector, :copied-bounds :seqable, @@ -17875,163 +17954,7 @@ :worker {:op :keys, :req {:tasks :map}}, :pen-mode :boolean}}}}}, - collapse {:arities {1 {:args [{:op :keys, - :opt {:system-fonts :vector, - :copied-bounds :seqable, - :active-document :any, - :adjusted-pointer-offset :seqable, - :re-pressed.core/keydown :map, - :pivot-point :seqable, - :viewbox-kdtree :any, - :copied-elements :seqable, - :kdtree :any, - :pointer-offset :seqable, - :nearest-neighbors :sequential, - :event-time :number, - :drag :boolean, - :version :string, - :nearest-neighbor {:op :keys, - :req {:point :seqable, - :base-point :seqable, - :dist-squared :number}, - :nilable true}, - :dom-rect {:op :keys, - :req {:x :number, - :y :number, - :width :number, - :height :number, - :top :number, - :right :number, - :bottom :number, - :left :number}}, - :nearest-neighbor-offset :nilable/seqable, - :clicked-element :any, - :primary-tool :any}, - :req {:double-click-delta :any, - :dialogs :vector, - :repl-mode :keyword, - :snap {:op :keys, - :req {:active :boolean, - :threshold :number, - :options :set}}, - :grid :boolean, - :tool :any, - :ruler {:op :keys, - :req {:visible :boolean, - :locked :boolean, - :size :number}}, - :cursor :string, - :pointer-pos :seqable, - :state :keyword, - :window {:op :keys, - :req {:maximized :boolean, - :minimized :boolean, - :fullscreen :boolean, - :focused :boolean}}, - :theme {:op :keys, - :opt {:native-mode :keyword}, - :req {:mode :keyword}}, - :lang :any, - :document-tabs :vector, - :documents :map, - :notifications :seqable, - :recent :vector, - :zoom-sensitivity :any, - :debug-info :boolean, - :adjusted-pointer-pos :seqable, - :timeline {:op :keys, - :req {:time :number, - :speed :number, - :replay? :boolean, - :grid-snap? :boolean, - :guide-snap? :boolean, - :paused? :boolean}}, - :backdrop :boolean, - :drag-threshold :number, - :fx :vector, - :panels :map, - :worker {:op :keys, - :req {:tasks :map}}, - :pen-mode :boolean}}], - :ret {:op :keys, - :opt {:system-fonts :vector, - :copied-bounds :seqable, - :active-document :any, - :adjusted-pointer-offset :seqable, - :re-pressed.core/keydown :map, - :pivot-point :seqable, - :viewbox-kdtree :any, - :copied-elements :seqable, - :kdtree :any, - :pointer-offset :seqable, - :nearest-neighbors :sequential, - :event-time :number, - :drag :boolean, - :version :string, - :nearest-neighbor {:op :keys, - :req {:point :seqable, - :base-point :seqable, - :dist-squared :number}, - :nilable true}, - :dom-rect {:op :keys, - :req {:x :number, - :y :number, - :width :number, - :height :number, - :top :number, - :right :number, - :bottom :number, - :left :number}}, - :nearest-neighbor-offset :nilable/seqable, - :clicked-element :any, - :primary-tool :any}, - :req {:double-click-delta :any, - :dialogs :vector, - :repl-mode :keyword, - :snap {:op :keys, - :req {:active :boolean, - :threshold :number, - :options :set}}, - :grid :boolean, - :tool :any, - :ruler {:op :keys, - :req {:visible :boolean, - :locked :boolean, - :size :number}}, - :cursor :string, - :pointer-pos :seqable, - :state :keyword, - :window {:op :keys, - :req {:maximized :boolean, - :minimized :boolean, - :fullscreen :boolean, - :focused :boolean}}, - :theme {:op :keys, - :opt {:native-mode :keyword}, - :req {:mode :keyword}}, - :lang :any, - :document-tabs :vector, - :documents :map, - :notifications :seqable, - :recent :vector, - :zoom-sensitivity :any, - :debug-info :boolean, - :adjusted-pointer-pos :seqable, - :timeline {:op :keys, - :req {:time :number, - :speed :number, - :replay? :boolean, - :grid-snap? :boolean, - :guide-snap? :boolean, - :paused? :boolean}}, - :backdrop :boolean, - :drag-threshold :number, - :fx :vector, - :panels :map, - :worker {:op :keys, - :req {:tasks :map}}, - :pen-mode :boolean}}}, - 2 {:args [{:op :keys, + collapse {:arities {2 {:args [{:op :keys, :opt {:system-fonts :vector, :copied-bounds :seqable, :active-document :any, diff --git a/src/renderer/element/handlers.cljs b/src/renderer/element/handlers.cljs index d38f1b69..5960dcfa 100644 --- a/src/renderer/element/handlers.cljs +++ b/src/renderer/element/handlers.cljs @@ -71,14 +71,23 @@ [db id] (:children (entity db id))) +(m/=> parent-ids [:-> App [:set uuid?]]) +(defn parent-ids + [db] + (->> (selected db) + (map :parent) + (remove nil?) + (set))) + (m/=> parent [:function [:-> App [:maybe Element]] [:-> App [:maybe uuid?] [:maybe Element]]]) (defn parent ([db] - (let [selected-ks (selected-ids db)] - (or (parent db (first selected-ks)) - (root db)))) + (let [ids (parent-ids db)] + (if (= (count ids) 1) + (entity db (first ids)) + (root db)))) ([db id] (when-let [parent-id (:parent (entity db id))] (entity db parent-id)))) @@ -160,12 +169,12 @@ (reduce #(concat %1 (ancestor-ids db %2)) [] (selected-ids db))) ([db id] (loop [parent-id (:parent (entity db id)) - parent-ids []] + ids []] (if parent-id (recur (:parent (entity db parent-id)) - (conj parent-ids parent-id)) - parent-ids)))) + (conj ids parent-id)) + ids)))) (m/=> index [:-> App uuid? [:maybe int?]]) (defn index @@ -289,27 +298,24 @@ [:-> App uuid? App]]) (defn deselect ([db] - (reduce deselect db (keys (entities db)))) + (reduce deselect db (selected-ids db))) ([db id] (assoc-prop db id :selected false))) -(m/=> collapse [:function - [:-> App App] - [:-> App uuid? App]]) +(m/=> collapse [:-> App uuid? App]) (defn collapse - ([db] - (reduce collapse db (keys (entities db)))) - ([db id] - (update-in db [:documents (:active-document db) :collapsed-ids] conj id))) + [db id] + (update-in db [:documents (:active-document db) :collapsed-ids] conj id)) -(m/=> expand [:function - [:-> App App] - [:-> App uuid? App]]) +(m/=> collapse-all [:-> App App]) +(defn collapse-all + [db] + (reduce collapse db (keys (entities db)))) + +(m/=> expand [:-> App uuid? App]) (defn expand - ([db] - (reduce expand db (keys (entities db)))) - ([db id] - (update-in db [:documents (:active-document db) :collapsed-ids] disj id))) + [db id] + (update-in db [:documents (:active-document db) :collapsed-ids] disj id)) (m/=> expand-ancestors [:-> App uuid? App]) (defn expand-ancestors @@ -797,7 +803,7 @@ (assoc-in [:attrs :x] x) (assoc-in [:attrs :y] y))] (-> (add db svg) - (collapse)))) + (collapse-all)))) (m/=> snapping-points [:-> App [:maybe [:sequential Element]] [:vector Vec2D]]) (defn snapping-points diff --git a/test/element_test.cljs b/test/element_test.cljs index 25034328..f47f1ea4 100644 --- a/test/element_test.cljs +++ b/test/element_test.cljs @@ -54,6 +54,46 @@ (rf/dispatch [::e/select-same-tags]) (is (= (count @selected) 2)))))) +(deftest visibility + (rf-test/run-test-sync + (rf/dispatch [::app.e/initialize-db]) + (rf/dispatch [::document.e/init]) + + (let [selected (rf/subscribe [::s/selected])] + (rf/dispatch [::e/add {:tag :rect + :attrs {:width 100 + :height 100}}]) + (testing "default state" + (is (-> @selected first :visible))) + + (testing "toggle visibility" + (rf/dispatch [::e/toggle-prop (-> @selected first :id) :visible]) + (is (not (-> @selected first :visible)))) + + (testing "revert visibility" + (rf/dispatch [::e/toggle-prop (-> @selected first :id) :visible]) + (is (-> @selected first :visible)))))) + +(deftest label + (rf-test/run-test-sync + (rf/dispatch [::app.e/initialize-db]) + (rf/dispatch [::document.e/init]) + + (let [selected (rf/subscribe [::s/selected])] + (rf/dispatch [::e/add {:tag :rect + :attrs {:width 100 + :height 100}}]) + (testing "default state" + (is (not (-> @selected first :label)))) + + (testing "set label" + (rf/dispatch [::e/set-prop (-> @selected first :id) :label "rect"]) + (is (= (-> @selected first :label) "rect"))) + + (testing "clear label" + (rf/dispatch [::e/set-prop (-> @selected first :id) :label ""]) + (is (not (-> @selected first :label))))))) + (deftest lock (rf-test/run-test-sync (rf/dispatch [::app.e/initialize-db]) @@ -98,6 +138,23 @@ (rf/dispatch [::e/remove-attr :fill]) (is (not (-> @selected first :attrs :fill))))))) +(deftest delete + (rf-test/run-test-sync + (rf/dispatch [::app.e/initialize-db]) + (rf/dispatch [::document.e/init]) + + (let [selected (rf/subscribe [::s/selected])] + (rf/dispatch [::e/add {:tag :rect + :attrs {:width 100 + :height 100}}]) + + (testing "delete selection" + (let [id (-> @selected first :id)] + (is (uuid? id)) + (rf/dispatch [::e/delete]) + (is (empty? @selected)) + (is (not @(rf/subscribe [::s/entity id])))))))) + (deftest scale (rf-test/run-test-sync (rf/dispatch [::app.e/initialize-db])