From 5a358e3d0cfa43efb3772846b0ad8d3b9175f8c3 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Fri, 5 Jul 2024 14:13:14 +0200 Subject: [PATCH 01/77] Extract singular token applied predicate --- .../app/main/ui/workspace/tokens/token.cljs | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/token.cljs b/frontend/src/app/main/ui/workspace/tokens/token.cljs index 79fbb0e2d..3cb8e1ff1 100644 --- a/frontend/src/app/main/ui/workspace/tokens/token.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/token.cljs @@ -18,17 +18,24 @@ applied-tokens) (into {})))) +(defn token-attribute-applied? + "Test if `token` is applied to a `shape` on single `token-attribute`." + [token shape token-attribute] + (when-let [id (get-in shape [:applied-tokens token-attribute])] + (= (:id token) id))) + (defn token-applied? - "Test if `token` is applied to a `shape` with the given `token-attributes`." + "Test if `token` is applied to a `shape` with at least one of the one of the given `token-attributes`." [token shape token-attributes] - (let [{:keys [id]} token - applied-tokens (get shape :applied-tokens {})] - (some (fn [attr] - (= (get applied-tokens attr) id)) - token-attributes))) + (some #(token-attribute-applied? token shape %) token-attributes)) (defn shapes-token-applied? - "Test if `token` is applied to to any of `shapes` with the given `token-attributes`." + "Test if `token` is applied to to any of `shapes` with at least one of the one of the given `token-attributes`." + [token shapes token-attributes] + (some #(token-applied? token % token-attributes) shapes)) + +(defn shapes-token-applied-all? + "Test if `token` is applied to to any of `shapes` with at least one of the one of the given `token-attributes`." [token shapes token-attributes] (some #(token-applied? token % token-attributes) shapes)) From 3c67872d3cd3873c6c5110b4919e0eaa6982878b Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Fri, 5 Jul 2024 14:44:57 +0200 Subject: [PATCH 02/77] Add future test cases for providing a toggle all/attributes --- frontend/test/token_tests/token_test.cljs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/frontend/test/token_tests/token_test.cljs b/frontend/test/token_tests/token_test.cljs index 255adf28e..d78233056 100644 --- a/frontend/test/token_tests/token_test.cljs +++ b/frontend/test/token_tests/token_test.cljs @@ -25,6 +25,29 @@ (t/testing "doesn't match passed `:token-attributes`" (t/is (nil? (wtt/token-applied? {:id :a} {:applied-tokens {:x :a}} #{:y}))))) +(t/deftest token-applied-attributes + (t/is (= #{:x} (wtt/token-applied-attributes {:id :a} + {:applied-tokens {:x :a :y :b}} + #{:x :missing})))) +(comment + (let [test-fn (fn [& args])] + (t/deftest token-context-menu + (let [shapes [{:applied-tokens {:x :a + :y :a}} + {:applied-tokens {:x :a + :y :a}}]] + (t/is (= :all (test-fn {:id :a} shapes #{:x :y})))) + (let [shapes [{:applied-tokens {:x :a + :y :_}} + {:applied-tokens {:x :_ + :y :a}}]] + (t/is (= #{:x :y} (test-fn {:id :a} shapes #{:x :y})))) + (let [shapes [{:applied-tokens {:x :a + :y :_}} + {:applied-tokens {:x :a + :y :_}}]] + (t/is (= #{:x} (test-fn {:id :a} shapes #{:x}))))))) + (t/deftest tokens-applied-test (t/testing "is true when single shape matches the token and attributes" (t/is (true? (wtt/shapes-token-applied? {:id :a} [{:applied-tokens {:x :a}} From 62ecee2cf80a9ba6d111dd85319942f4fab405f9 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Mon, 8 Jul 2024 10:24:23 +0200 Subject: [PATCH 03/77] Add grouping function by type --- .../app/main/ui/workspace/tokens/token.cljs | 20 +++++++++ frontend/test/token_tests/token_test.cljs | 45 +++++++++++-------- 2 files changed, 47 insertions(+), 18 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/token.cljs b/frontend/src/app/main/ui/workspace/tokens/token.cljs index 3cb8e1ff1..66f251db0 100644 --- a/frontend/src/app/main/ui/workspace/tokens/token.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/token.cljs @@ -29,6 +29,12 @@ [token shape token-attributes] (some #(token-attribute-applied? token shape %) token-attributes)) +(defn token-applied-attributes + "Return a set of which `token-attributes` are applied with `token`." + [token shape token-attributes] + (-> (filter #(token-attribute-applied? token shape %) token-attributes) + (set))) + (defn shapes-token-applied? "Test if `token` is applied to to any of `shapes` with at least one of the one of the given `token-attributes`." [token shapes token-attributes] @@ -39,6 +45,20 @@ [token shapes token-attributes] (some #(token-applied? token % token-attributes) shapes)) +(defn group-shapes-by-all-applied + "" + [token shapes token-attributes] + (reduce + (fn [acc cur-shape] + (let [applied-attrs (token-applied-attributes token cur-shape token-attributes)] + (cond + (empty? applied-attrs) (update acc :none (fnil conj []) cur-shape) + (= applied-attrs token-attributes) (update acc :all (fnil conj []) cur-shape) + :else (reduce (fn [acc' cur'] + (update-in acc' [:some cur'] (fnil conj []) cur-shape)) + acc applied-attrs)))) + {} shapes)) + (defn token-name->path "Splits token-name into a path vector split by `.` characters. diff --git a/frontend/test/token_tests/token_test.cljs b/frontend/test/token_tests/token_test.cljs index d78233056..9b0462955 100644 --- a/frontend/test/token_tests/token_test.cljs +++ b/frontend/test/token_tests/token_test.cljs @@ -29,24 +29,33 @@ (t/is (= #{:x} (wtt/token-applied-attributes {:id :a} {:applied-tokens {:x :a :y :b}} #{:x :missing})))) -(comment - (let [test-fn (fn [& args])] - (t/deftest token-context-menu - (let [shapes [{:applied-tokens {:x :a - :y :a}} - {:applied-tokens {:x :a - :y :a}}]] - (t/is (= :all (test-fn {:id :a} shapes #{:x :y})))) - (let [shapes [{:applied-tokens {:x :a - :y :_}} - {:applied-tokens {:x :_ - :y :a}}]] - (t/is (= #{:x :y} (test-fn {:id :a} shapes #{:x :y})))) - (let [shapes [{:applied-tokens {:x :a - :y :_}} - {:applied-tokens {:x :a - :y :_}}]] - (t/is (= #{:x} (test-fn {:id :a} shapes #{:x}))))))) +(t/deftest token-context-menu + (t/testing "Returns :all when token is applied to every shape" + (let [shapes [{:applied-tokens {:x 1 :y 1}} + {:applied-tokens {:x 1 :y 1}}] + expected (wtt/group-shapes-by-all-applied {:id 1} shapes #{:x :y})] + (t/is (= (:all expected) shapes)) + (t/is (empty? (:other expected))) + (t/is (empty? (:some expected))))) + + (t/testing "Returns set of matched attributes that fit the applied token" + (let [attributes #{:x :y :z} + shape-applied-x {:applied-tokens {:x 1}} + shape-applied-y {:applied-tokens {:y 1}} + shape-applied-x-y {:applied-tokens {:x 1 :y 1}} + shape-applied-none {:applied-tokens {}} + shape-applied-all {:applied-tokens {:x 1 :y 1 :z 1}} + shapes [shape-applied-x + shape-applied-y + shape-applied-x-y + shape-applied-all + shape-applied-none] + expected (wtt/group-shapes-by-all-applied {:id 1} shapes attributes)] + (t/is (= (:all expected) [shape-applied-all])) + (t/is (= (:none expected) [shape-applied-none])) + (t/is (= (get-in expected [:some :x]) [shape-applied-x shape-applied-x-y])) + (t/is (= (get-in expected [:some :y]) [shape-applied-y shape-applied-x-y])) + (t/is (nil? (get-in expected [:some :z])))))) (t/deftest tokens-applied-test (t/testing "is true when single shape matches the token and attributes" From e75f9a7c7fe638183c8002ac993eda187c249b67 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Mon, 8 Jul 2024 11:06:57 +0200 Subject: [PATCH 04/77] Add predicate for all test --- frontend/src/app/main/ui/workspace/tokens/token.cljs | 5 +++++ frontend/test/token_tests/token_test.cljs | 1 + 2 files changed, 6 insertions(+) diff --git a/frontend/src/app/main/ui/workspace/tokens/token.cljs b/frontend/src/app/main/ui/workspace/tokens/token.cljs index 66f251db0..74df395c0 100644 --- a/frontend/src/app/main/ui/workspace/tokens/token.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/token.cljs @@ -59,6 +59,11 @@ acc applied-attrs)))) {} shapes)) +(defn group-shapes-by-all-applied-all? [grouped-shapes] + (and (seq (:all grouped-shapes)) + (empty? (:other grouped-shapes)) + (empty? (:some grouped-shapes)))) + (defn token-name->path "Splits token-name into a path vector split by `.` characters. diff --git a/frontend/test/token_tests/token_test.cljs b/frontend/test/token_tests/token_test.cljs index 9b0462955..dcf0b69ef 100644 --- a/frontend/test/token_tests/token_test.cljs +++ b/frontend/test/token_tests/token_test.cljs @@ -34,6 +34,7 @@ (let [shapes [{:applied-tokens {:x 1 :y 1}} {:applied-tokens {:x 1 :y 1}}] expected (wtt/group-shapes-by-all-applied {:id 1} shapes #{:x :y})] + (t/is (true? (wtt/group-shapes-by-all-applied-all? expected))) (t/is (= (:all expected) shapes)) (t/is (empty? (:other expected))) (t/is (empty? (:some expected))))) From db7391e4cb679c56610cb199c724dcaefc9dc688 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Mon, 8 Jul 2024 11:40:05 +0200 Subject: [PATCH 05/77] Separate entries --- .../ui/workspace/tokens/context_menu.cljs | 62 +++++++++++-------- .../ui/workspace/tokens/context_menu.scss | 6 +- 2 files changed, 41 insertions(+), 27 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index 352268480..37ef003cd 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -213,10 +213,13 @@ (defn additional-actions [{:keys [token-id token-type selected-shapes] :as context-data}] (let [attributes->actions (fn [update-fn coll] (for [{:keys [attributes] :as item} coll] - (let [selected? (wtt/shapes-token-applied? {:id token-id} selected-shapes attributes)] - (assoc item - :action #(update-fn context-data attributes) - :selected? selected?))))] + (cond + (= :separator item) item + :else + (let [selected? (wtt/shapes-token-applied? {:id token-id} selected-shapes attributes)] + (assoc item + :action #(update-fn context-data attributes) + :selected? selected?)))))] (case token-type :border-radius (attributes->actions apply-border-radius-token @@ -228,14 +231,15 @@ :spacing (attributes->actions apply-spacing-token [{:title "All" :attributes #{:p1 :p2 :p3 :p4}} - {:title "Column Gap" :attributes #{:column-gap}} - {:title "Vertical padding" :attributes #{:p1 :p3}} - {:title "Horizontal padding" :attributes #{:p2 :p4}} - {:title "Row Gap" :attributes #{:row-gap}} {:title "Top" :attributes #{:p1}} {:title "Right" :attributes #{:p2}} {:title "Bottom" :attributes #{:p3}} - {:title "Left" :attributes #{:p4}}]) + {:title "Left" :attributes #{:p4}} + :separator + {:title "Column Gap" :attributes #{:column-gap}} + {:title "Vertical padding" :attributes #{:p1 :p3}} + {:title "Horizontal padding" :attributes #{:p2 :p4}} + {:title "Row Gap" :attributes #{:row-gap}}]) :sizing (attributes->actions apply-sizing-token @@ -292,23 +296,29 @@ (mf/defc token-pill-context-menu [context-data] (let [menu-entries (generate-menu-entries context-data)] - (for [[index {:keys [title action selected? children submenu]}] (d/enumerate menu-entries)] - [:& menu-entry (cond-> {:key index - :title title} - (not submenu) (assoc :on-click action - ;; TODO: Allow selected items wihtout an icon for the context menu - :icon (mf/html [:div {:class (stl/css-case :empty-icon true - :hidden-icon (not selected?))}]) - :selected? selected?)) - (when submenu - (let [submenu-entries (additional-actions (assoc context-data :token-type submenu))] - (for [[index {:keys [title action selected?]}] (d/enumerate submenu-entries)] - [:& menu-entry {:key index - :title title - :on-click action - :icon (mf/html [:div {:class (stl/css-case :empty-icon true - :hidden-icon (not selected?))}]) - :selected? selected?}])))]))) + (for [[index {:keys [title action selected? children submenu] :as entry}] (d/enumerate menu-entries)] + (cond + (= :separator entry) [:& menu-separator] + :else + [:& menu-entry (cond-> {:key index + :title title} + (not submenu) (assoc :on-click action + ;; TODO: Allow selected items wihtout an icon for the context menu + :icon (mf/html [:div {:class (stl/css-case :empty-icon true + :hidden-icon (not selected?))}]) + :selected? selected?)) + (when submenu + (let [submenu-entries (additional-actions (assoc context-data :token-type submenu))] + (for [[index {:keys [title action selected?] :as sub-entry}] (d/enumerate submenu-entries)] + (cond + (= :separator sub-entry) [:& menu-separator] + :else + [:& menu-entry {:key index + :title title + :on-click action + :icon (mf/html [:div {:class (stl/css-case :empty-icon true + :hidden-icon (not selected?))}]) + :selected? selected?}]))))])))) (mf/defc token-context-menu [] diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.scss b/frontend/src/app/main/ui/workspace/tokens/context_menu.scss index 3062483bc..1ca475903 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.scss +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.scss @@ -38,7 +38,11 @@ } .separator { - height: $s-12; + @include bodySmallTypography; + margin: $s-6; + display: flex; + align-items: center; + border-block-start: $s-1 solid var(--panel-border-color); } .context-menu-item { From 90618ec89a5bccf2867ac63103da64f9966034f0 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Mon, 8 Jul 2024 11:41:25 +0200 Subject: [PATCH 06/77] Add separator between default actions and attribute actions --- frontend/src/app/main/ui/workspace/tokens/context_menu.cljs | 2 +- frontend/src/app/main/ui/workspace/tokens/context_menu.scss | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index 37ef003cd..a751559e2 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -290,7 +290,7 @@ :fields fields :token token})))}] specific-actions (additional-actions context-data) - all-actions (concat specific-actions default-actions)] + all-actions (concat specific-actions [:separator] default-actions)] all-actions)) (mf/defc token-pill-context-menu diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.scss b/frontend/src/app/main/ui/workspace/tokens/context_menu.scss index 1ca475903..f2798ea7a 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.scss +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.scss @@ -40,8 +40,6 @@ .separator { @include bodySmallTypography; margin: $s-6; - display: flex; - align-items: center; border-block-start: $s-1 solid var(--panel-border-color); } From da0389e304fad1da20a8321b7ab3b59f95f4e0f7 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Mon, 8 Jul 2024 14:38:19 +0200 Subject: [PATCH 07/77] Improved logic to run once for all shapes --- .../app/main/ui/workspace/tokens/token.cljs | 17 +++++++- frontend/test/token_tests/token_test.cljs | 43 ++++++++++--------- 2 files changed, 37 insertions(+), 23 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/token.cljs b/frontend/src/app/main/ui/workspace/tokens/token.cljs index 74df395c0..4df33b285 100644 --- a/frontend/src/app/main/ui/workspace/tokens/token.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/token.cljs @@ -1,6 +1,7 @@ (ns app.main.ui.workspace.tokens.token (:require - [cuerdas.core :as str])) + [cuerdas.core :as str] + [clojure.set :as set])) (defn attributes-map "Creats an attributes map using collection of `attributes` for `id`." @@ -45,8 +46,20 @@ [token shapes token-attributes] (some #(token-applied? token % token-attributes) shapes)) +(defn shapes-ids-by-applied-attributes [token shapes token-attributes] + (reduce (fn [acc shape] + (let [applied-ids-by-attribute (->> (map #(when (token-attribute-applied? token shape %) + [% #{(:id shape)}]) + token-attributes) + (filter some?) + (into {}))] + (merge-with into acc applied-ids-by-attribute))) + {} shapes)) + +(defn shapes-applied-all? [ids-by-attributes shape-ids attributes] + (every? #(set/superset? (get ids-by-attributes %) shape-ids) attributes)) + (defn group-shapes-by-all-applied - "" [token shapes token-attributes] (reduce (fn [acc cur-shape] diff --git a/frontend/test/token_tests/token_test.cljs b/frontend/test/token_tests/token_test.cljs index dcf0b69ef..bcf86fcc9 100644 --- a/frontend/test/token_tests/token_test.cljs +++ b/frontend/test/token_tests/token_test.cljs @@ -29,34 +29,35 @@ (t/is (= #{:x} (wtt/token-applied-attributes {:id :a} {:applied-tokens {:x :a :y :b}} #{:x :missing})))) -(t/deftest token-context-menu - (t/testing "Returns :all when token is applied to every shape" - (let [shapes [{:applied-tokens {:x 1 :y 1}} - {:applied-tokens {:x 1 :y 1}}] - expected (wtt/group-shapes-by-all-applied {:id 1} shapes #{:x :y})] - (t/is (true? (wtt/group-shapes-by-all-applied-all? expected))) - (t/is (= (:all expected) shapes)) - (t/is (empty? (:other expected))) - (t/is (empty? (:some expected))))) - +(t/deftest shapes-ids-by-applied-attributes (t/testing "Returns set of matched attributes that fit the applied token" (let [attributes #{:x :y :z} - shape-applied-x {:applied-tokens {:x 1}} - shape-applied-y {:applied-tokens {:y 1}} - shape-applied-x-y {:applied-tokens {:x 1 :y 1}} - shape-applied-none {:applied-tokens {}} - shape-applied-all {:applied-tokens {:x 1 :y 1 :z 1}} + shape-applied-x {:id :shape-applied-x + :applied-tokens {:x 1}} + shape-applied-y {:id :shape-applied-y + :applied-tokens {:y 1}} + shape-applied-x-y {:id :shape-applied-x-y + :applied-tokens {:x 1 :y 1}} + shape-applied-none {:id :shape-applied-none + :applied-tokens {}} + shape-applied-all {:id :shape-applied-all + :applied-tokens {:x 1 :y 1 :z 1}} + ids-set (fn [& xs] (into #{} (map :id xs))) shapes [shape-applied-x shape-applied-y shape-applied-x-y shape-applied-all shape-applied-none] - expected (wtt/group-shapes-by-all-applied {:id 1} shapes attributes)] - (t/is (= (:all expected) [shape-applied-all])) - (t/is (= (:none expected) [shape-applied-none])) - (t/is (= (get-in expected [:some :x]) [shape-applied-x shape-applied-x-y])) - (t/is (= (get-in expected [:some :y]) [shape-applied-y shape-applied-x-y])) - (t/is (nil? (get-in expected [:some :z])))))) + expected (wtt/shapes-ids-by-applied-attributes {:id 1} shapes attributes)] + (t/is (= (:x expected) (ids-set shape-applied-x + shape-applied-x-y + shape-applied-all))) + (t/is (= (:y expected) (ids-set shape-applied-y + shape-applied-x-y + shape-applied-all))) + (t/is (= (:z expected) (ids-set shape-applied-all))) + (t/is (true? (wtt/shapes-applied-all? expected (ids-set shape-applied-all) attributes))) + (t/is (false? (wtt/shapes-applied-all? expected (apply ids-set shapes) attributes)))))) (t/deftest tokens-applied-test (t/testing "is true when single shape matches the token and attributes" From 82b44e65692f6a8328831fa7ab8ae29627bdfc47 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Mon, 8 Jul 2024 14:40:07 +0200 Subject: [PATCH 08/77] Inline concat --- .../src/app/main/ui/workspace/tokens/context_menu.cljs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index a751559e2..92ea3e133 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -277,6 +277,7 @@ (defn generate-menu-entries [{:keys [token-id token-type-props token-type selected-shapes] :as context-data}] (let [{:keys [modal]} token-type-props + attribute-actions (additional-actions context-data) default-actions [{:title "Delete Token" :action #(st/emit! (dt/delete-token token-id))} {:title "Duplicate Token" :action #(st/emit! (dt/duplicate-token token-id))} {:title "Edit Token" :action (fn [event] @@ -288,10 +289,11 @@ :y (.-clientY ^js event) :position :right :fields fields - :token token})))}] - specific-actions (additional-actions context-data) - all-actions (concat specific-actions [:separator] default-actions)] - all-actions)) + :token token})))}]] + (concat + attribute-actions + [:separator] + default-actions))) (mf/defc token-pill-context-menu [context-data] From 3ad009b515caaa30193f1efddc6e670ce4086f06 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Mon, 8 Jul 2024 14:40:35 +0200 Subject: [PATCH 09/77] Fix lint --- frontend/src/app/main/ui/workspace/tokens/context_menu.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index 92ea3e133..2e0611d38 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -275,7 +275,7 @@ []))) -(defn generate-menu-entries [{:keys [token-id token-type-props token-type selected-shapes] :as context-data}] +(defn generate-menu-entries [{:keys [token-id token-type-props _token-type _selected-shapes] :as context-data}] (let [{:keys [modal]} token-type-props attribute-actions (additional-actions context-data) default-actions [{:title "Delete Token" :action #(st/emit! (dt/delete-token token-id))} From bf994fcd56fe6d3afb0eb05750746061de7400ef Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Mon, 8 Jul 2024 14:40:57 +0200 Subject: [PATCH 10/77] Rename --- frontend/src/app/main/ui/workspace/tokens/context_menu.cljs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index 2e0611d38..85c0c91a8 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -210,7 +210,7 @@ :token-type-props token-type-props :selected-shapes selected-shapes}))) -(defn additional-actions [{:keys [token-id token-type selected-shapes] :as context-data}] +(defn shape-attribute-actions [{:keys [token-id token-type selected-shapes] :as context-data}] (let [attributes->actions (fn [update-fn coll] (for [{:keys [attributes] :as item} coll] (cond @@ -277,7 +277,7 @@ (defn generate-menu-entries [{:keys [token-id token-type-props _token-type _selected-shapes] :as context-data}] (let [{:keys [modal]} token-type-props - attribute-actions (additional-actions context-data) + attribute-actions (shape-attribute-actions context-data) default-actions [{:title "Delete Token" :action #(st/emit! (dt/delete-token token-id))} {:title "Duplicate Token" :action #(st/emit! (dt/duplicate-token token-id))} {:title "Edit Token" :action (fn [event] @@ -310,7 +310,7 @@ :hidden-icon (not selected?))}]) :selected? selected?)) (when submenu - (let [submenu-entries (additional-actions (assoc context-data :token-type submenu))] + (let [submenu-entries (shape-attribute-actions (assoc context-data :token-type submenu))] (for [[index {:keys [title action selected?] :as sub-entry}] (d/enumerate submenu-entries)] (cond (= :separator sub-entry) [:& menu-separator] From 7b2d11019c5df9bac53877c6bdbaae8780e28132 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Mon, 8 Jul 2024 15:10:01 +0200 Subject: [PATCH 11/77] Only show atrribute actions when shapes are selected --- frontend/src/app/main/ui/workspace/tokens/context_menu.cljs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index 85c0c91a8..ee43e98d9 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -277,7 +277,8 @@ (defn generate-menu-entries [{:keys [token-id token-type-props _token-type _selected-shapes] :as context-data}] (let [{:keys [modal]} token-type-props - attribute-actions (shape-attribute-actions context-data) + attribute-actions (when (seq selected-shapes) + (shape-attribute-actions context-data)) default-actions [{:title "Delete Token" :action #(st/emit! (dt/delete-token token-id))} {:title "Duplicate Token" :action #(st/emit! (dt/duplicate-token token-id))} {:title "Edit Token" :action (fn [event] @@ -292,7 +293,7 @@ :token token})))}]] (concat attribute-actions - [:separator] + (when attribute-actions [:separator]) default-actions))) (mf/defc token-pill-context-menu From 77fe4d556fb995ab8fe764b92131d08d474999a8 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Mon, 8 Jul 2024 15:10:51 +0200 Subject: [PATCH 12/77] Convert border-radius to new UX --- .../ui/workspace/tokens/context_menu.cljs | 34 ++++++++++++++----- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index ee43e98d9..e7216161b 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -210,6 +210,30 @@ :token-type-props token-type-props :selected-shapes selected-shapes}))) +(defn border-radius-attribute-actions [{:keys [token-id selected-shapes] :as _props}] + (let [all-attributes #{:r1 :r2 :r3 :r4} + ids-by-attributes (wtt/shapes-ids-by-applied-attributes {:id token-id} selected-shapes all-attributes) + shape-ids (into #{} (map :id selected-shapes)) + all? (wtt/shapes-applied-all? ids-by-attributes shape-ids all-attributes) + selected? #(and + (not all?) + (seq (% ids-by-attributes)))] + [{:title "All" + :selected? all? + :action (when all? #(js/console.log "all"))} + {:title "Top Left" + :selected? (selected? :r1) + :action (when all? #(js/console.log "all"))} + {:title "Top Right" + :selected? (selected? :r2) + :action (when all? #(js/console.log "all"))} + {:title "Bottom Right" + :selected? (selected? :r3) + :action (when all? #(js/console.log "all"))} + {:title "Bottom Left" + :selected? (selected? :r4) + :action (when all? #(js/console.log "all"))}])) + (defn shape-attribute-actions [{:keys [token-id token-type selected-shapes] :as context-data}] (let [attributes->actions (fn [update-fn coll] (for [{:keys [attributes] :as item} coll] @@ -221,13 +245,7 @@ :action #(update-fn context-data attributes) :selected? selected?)))))] (case token-type - :border-radius (attributes->actions - apply-border-radius-token - [{:title "All" :attributes #{:r1 :r2 :r3 :r4}} - {:title "Top Left" :attributes #{:r1}} - {:title "Top Right" :attributes #{:r2}} - {:title "Bottom Right" :attributes #{:r3}} - {:title "Bottom Left" :attributes #{:r4}}]) + :border-radius (border-radius-attribute-actions context-data) :spacing (attributes->actions apply-spacing-token [{:title "All" :attributes #{:p1 :p2 :p3 :p4}} @@ -275,7 +293,7 @@ []))) -(defn generate-menu-entries [{:keys [token-id token-type-props _token-type _selected-shapes] :as context-data}] +(defn generate-menu-entries [{:keys [token-id token-type-props _token-type selected-shapes] :as context-data}] (let [{:keys [modal]} token-type-props attribute-actions (when (seq selected-shapes) (shape-attribute-actions context-data)) From 9bf763efb35802e79579152f43977561cacc5d04 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Mon, 8 Jul 2024 15:19:48 +0200 Subject: [PATCH 13/77] Add all action --- .../src/app/main/ui/workspace/tokens/context_menu.cljs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index e7216161b..ebe3e0483 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -220,7 +220,14 @@ (seq (% ids-by-attributes)))] [{:title "All" :selected? all? - :action (when all? #(js/console.log "all"))} + :action #(if all? + (st/emit! (wtc/unapply-token {:token {:id token-id} + :attributes all-attributes + :shape-ids shape-ids})) + (st/emit! (wtc/apply-token {:token {:id token-id} + :attributes all-attributes + :on-update-shape wtc/update-shape-radius + :shape-ids shape-ids})))} {:title "Top Left" :selected? (selected? :r1) :action (when all? #(js/console.log "all"))} From ae2da534e962aec9277acf1c4acd5271bfd29f07 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Mon, 8 Jul 2024 15:40:18 +0200 Subject: [PATCH 14/77] Move radius updating to core --- frontend/src/app/main/ui/workspace/tokens/core.cljs | 12 ++++++++++-- .../test/token_tests/logic/token_actions_test.cljs | 8 ++++---- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/core.cljs b/frontend/src/app/main/ui/workspace/tokens/core.cljs index cb03a7a27..1bc6e45ca 100644 --- a/frontend/src/app/main/ui/workspace/tokens/core.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/core.cljs @@ -50,7 +50,15 @@ ;; Shape Update Functions ------------------------------------------------------ -(defn update-shape-radius [value shape-ids] +(defn update-shape-radius-single-corner [value shape-ids attribute] + (dch/update-shapes shape-ids + (fn [shape] + (when (ctsr/has-radius? shape) + (ctsr/set-radius-4 shape (first attribute) value))) + {:reg-objects? true + :attrs [:rx :ry :r1 :r2 :r3 :r4]})) + +(defn update-shape-radius-all [value shape-ids] (dch/update-shapes shape-ids (fn [shape] (when (ctsr/has-radius? shape) @@ -203,7 +211,7 @@ [:border-radius {:title "Border Radius" :attributes ctt/border-radius-keys - :on-update-shape update-shape-radius + :on-update-shape update-shape-radius-all :modal {:key :tokens/border-radius :fields [{:label "Border Radius" :key :border-radius}]}}] diff --git a/frontend/test/token_tests/logic/token_actions_test.cljs b/frontend/test/token_tests/logic/token_actions_test.cljs index 3fadf0cdd..585e19d53 100644 --- a/frontend/test/token_tests/logic/token_actions_test.cljs +++ b/frontend/test/token_tests/logic/token_actions_test.cljs @@ -36,11 +36,11 @@ events [(wtc/apply-token {:shape-ids [(:id rect-1)] :attributes #{:rx :ry} :token (toht/get-token file :token-1) - :on-update-shape wtc/update-shape-radius}) + :on-update-shape wtc/update-shape-radius-all}) (wtc/apply-token {:shape-ids [(:id rect-1)] :attributes #{:rx :ry} :token (toht/get-token file :token-2) - :on-update-shape wtc/update-shape-radius})]] + :on-update-shape wtc/update-shape-radius-all})]] (tohs/run-store-async store done events (fn [new-state] @@ -63,7 +63,7 @@ events [(wtc/apply-token {:shape-ids [(:id rect-1)] :attributes #{:rx :ry} :token (toht/get-token file :token-2) - :on-update-shape wtc/update-shape-radius})]] + :on-update-shape wtc/update-shape-radius-all})]] (tohs/run-store-async store done events (fn [new-state] @@ -187,7 +187,7 @@ rect-2 (cths/get-shape file :rect-2) events [(wtc/toggle-token {:shapes [rect-1 rect-2] :token-type-props {:attributes #{:rx :ry} - :on-update-shape wtc/update-shape-radius} + :on-update-shape wtc/update-shape-radius-all} :token (toht/get-token file :token-2)})]] (tohs/run-store-async store done events From 359ec592fbb913821908eef232f4d8dc5cc75c8a Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Mon, 8 Jul 2024 15:40:33 +0200 Subject: [PATCH 15/77] Single attribute context menu --- .../ui/workspace/tokens/context_menu.cljs | 61 +++++++++++-------- 1 file changed, 34 insertions(+), 27 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index ebe3e0483..44e1f6a84 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -211,35 +211,42 @@ :selected-shapes selected-shapes}))) (defn border-radius-attribute-actions [{:keys [token-id selected-shapes] :as _props}] - (let [all-attributes #{:r1 :r2 :r3 :r4} - ids-by-attributes (wtt/shapes-ids-by-applied-attributes {:id token-id} selected-shapes all-attributes) + (let [token {:id token-id} + all-attributes #{:r1 :r2 :r3 :r4} + ids-by-attributes (wtt/shapes-ids-by-applied-attributes token selected-shapes all-attributes) shape-ids (into #{} (map :id selected-shapes)) all? (wtt/shapes-applied-all? ids-by-attributes shape-ids all-attributes) - selected? #(and - (not all?) - (seq (% ids-by-attributes)))] - [{:title "All" - :selected? all? - :action #(if all? - (st/emit! (wtc/unapply-token {:token {:id token-id} - :attributes all-attributes - :shape-ids shape-ids})) - (st/emit! (wtc/apply-token {:token {:id token-id} - :attributes all-attributes - :on-update-shape wtc/update-shape-radius - :shape-ids shape-ids})))} - {:title "Top Left" - :selected? (selected? :r1) - :action (when all? #(js/console.log "all"))} - {:title "Top Right" - :selected? (selected? :r2) - :action (when all? #(js/console.log "all"))} - {:title "Bottom Right" - :selected? (selected? :r3) - :action (when all? #(js/console.log "all"))} - {:title "Bottom Left" - :selected? (selected? :r4) - :action (when all? #(js/console.log "all"))}])) + selected-pred #(and + (not all?) + (seq (% ids-by-attributes))) + single-attributes (->> {:r1 "Top Left" + :r2 "Top Right" + :r3 "Bottom Left" + :r4 "Bottom Right"} + (map (fn [[attr title]] + (let [selected? (selected-pred attr)] + {:title title + :selected? selected? + :action (if selected? + (st/emit! (wtc/unapply-token {:token token + :attributes #{attr} + :shape-ids shape-ids})) + (st/emit! (wtc/apply-token {:token token + :attributes #{attr} + :on-update-shape wtc/update-shape-radius-single-corner + :shape-ids shape-ids})))}))))] + (concat + [{:title "All" + :selected? all? + :action #(if all? + (st/emit! (wtc/unapply-token {:token token + :attributes all-attributes + :shape-ids shape-ids})) + (st/emit! (wtc/apply-token {:token token + :attributes all-attributes + :on-update-shape wtc/update-shape-radius-all + :shape-ids shape-ids})))}] + single-attributes))) (defn shape-attribute-actions [{:keys [token-id token-type selected-shapes] :as context-data}] (let [attributes->actions (fn [update-fn coll] From 91033d6dea263003d4c0cc4e31472cad3d6a8df5 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Mon, 22 Jul 2024 11:30:27 +0200 Subject: [PATCH 16/77] Fix indent --- .../token_tests/logic/token_actions_test.cljs | 278 +++++++++--------- 1 file changed, 139 insertions(+), 139 deletions(-) diff --git a/frontend/test/token_tests/logic/token_actions_test.cljs b/frontend/test/token_tests/logic/token_actions_test.cljs index 585e19d53..a29d9045a 100644 --- a/frontend/test/token_tests/logic/token_actions_test.cljs +++ b/frontend/test/token_tests/logic/token_actions_test.cljs @@ -27,155 +27,155 @@ :type :border-radius}))) (t/deftest test-apply-token - (t/testing "applying a token twice with the same attributes will override") - (t/async - done - (let [file (setup-file) - store (ths/setup-store file) - rect-1 (cths/get-shape file :rect-1) - events [(wtc/apply-token {:shape-ids [(:id rect-1)] - :attributes #{:rx :ry} - :token (toht/get-token file :token-1) - :on-update-shape wtc/update-shape-radius-all}) - (wtc/apply-token {:shape-ids [(:id rect-1)] - :attributes #{:rx :ry} - :token (toht/get-token file :token-2) - :on-update-shape wtc/update-shape-radius-all})]] - (tohs/run-store-async - store done events - (fn [new-state] - (let [file' (ths/get-file-from-store new-state) - token-2' (toht/get-token file' :token-2) - rect-1' (cths/get-shape file' :rect-1)] - (t/is (some? (:applied-tokens rect-1'))) - (t/is (= (:rx (:applied-tokens rect-1')) (:id token-2'))) - (t/is (= (:ry (:applied-tokens rect-1')) (:id token-2'))) - (t/is (= (:rx rect-1') 24)) - (t/is (= (:ry rect-1') 24)))))))) + (t/testing "applying a token twice with the same attributes will override" + (t/async + done + (let [file (setup-file) + store (ths/setup-store file) + rect-1 (cths/get-shape file :rect-1) + events [(wtc/apply-token {:shape-ids [(:id rect-1)] + :attributes #{:rx :ry} + :token (toht/get-token file :token-1) + :on-update-shape wtc/update-shape-radius-all}) + (wtc/apply-token {:shape-ids [(:id rect-1)] + :attributes #{:rx :ry} + :token (toht/get-token file :token-2) + :on-update-shape wtc/update-shape-radius-all})]] + (tohs/run-store-async + store done events + (fn [new-state] + (let [file' (ths/get-file-from-store new-state) + token-2' (toht/get-token file' :token-2) + rect-1' (cths/get-shape file' :rect-1)] + (t/is (some? (:applied-tokens rect-1'))) + (t/is (= (:rx (:applied-tokens rect-1')) (:id token-2'))) + (t/is (= (:ry (:applied-tokens rect-1')) (:id token-2'))) + (t/is (= (:rx rect-1') 24)) + (t/is (= (:ry rect-1') 24))))))))) (t/deftest test-apply-border-radius - (t/testing "applies radius token and updates the shapes radius") - (t/async - done - (let [file (setup-file) - store (ths/setup-store file) - rect-1 (cths/get-shape file :rect-1) - events [(wtc/apply-token {:shape-ids [(:id rect-1)] - :attributes #{:rx :ry} - :token (toht/get-token file :token-2) - :on-update-shape wtc/update-shape-radius-all})]] - (tohs/run-store-async - store done events - (fn [new-state] - (let [file' (ths/get-file-from-store new-state) - token-2' (toht/get-token file' :token-2) - rect-1' (cths/get-shape file' :rect-1)] - (t/is (some? (:applied-tokens rect-1'))) - (t/is (= (:rx (:applied-tokens rect-1')) (:id token-2'))) - (t/is (= (:ry (:applied-tokens rect-1')) (:id token-2'))) - (t/is (= (:rx rect-1') 24)) - (t/is (= (:ry rect-1') 24)))))))) + (t/testing "applies radius token and updates the shapes radius" + (t/async + done + (let [file (setup-file) + store (ths/setup-store file) + rect-1 (cths/get-shape file :rect-1) + events [(wtc/apply-token {:shape-ids [(:id rect-1)] + :attributes #{:rx :ry} + :token (toht/get-token file :token-2) + :on-update-shape wtc/update-shape-radius-all})]] + (tohs/run-store-async + store done events + (fn [new-state] + (let [file' (ths/get-file-from-store new-state) + token-2' (toht/get-token file' :token-2) + rect-1' (cths/get-shape file' :rect-1)] + (t/is (some? (:applied-tokens rect-1'))) + (t/is (= (:rx (:applied-tokens rect-1')) (:id token-2'))) + (t/is (= (:ry (:applied-tokens rect-1')) (:id token-2'))) + (t/is (= (:rx rect-1') 24)) + (t/is (= (:ry rect-1') 24))))))))) (t/deftest test-apply-dimensions - (t/testing "applies dimensions token and updates the shapes width and height") - (t/async - done - (let [file (-> (setup-file) - (toht/add-token :token-target {:value "100" - :name "dimensions.sm" - :type :dimensions})) - store (ths/setup-store file) - rect-1 (cths/get-shape file :rect-1) - events [(wtc/apply-token {:shape-ids [(:id rect-1)] - :attributes #{:width :height} - :token (toht/get-token file :token-target) - :on-update-shape wtc/update-shape-dimensions})]] - (tohs/run-store-async - store done events - (fn [new-state] - (let [file' (ths/get-file-from-store new-state) - token-target' (toht/get-token file' :token-target) - rect-1' (cths/get-shape file' :rect-1)] - (t/is (some? (:applied-tokens rect-1'))) - (t/is (= (:width (:applied-tokens rect-1')) (:id token-target'))) - (t/is (= (:height (:applied-tokens rect-1')) (:id token-target'))) - (t/is (= (:width rect-1') 100)) - (t/is (= (:height rect-1') 100)))))))) + (t/testing "applies dimensions token and updates the shapes width and height" + (t/async + done + (let [file (-> (setup-file) + (toht/add-token :token-target {:value "100" + :name "dimensions.sm" + :type :dimensions})) + store (ths/setup-store file) + rect-1 (cths/get-shape file :rect-1) + events [(wtc/apply-token {:shape-ids [(:id rect-1)] + :attributes #{:width :height} + :token (toht/get-token file :token-target) + :on-update-shape wtc/update-shape-dimensions})]] + (tohs/run-store-async + store done events + (fn [new-state] + (let [file' (ths/get-file-from-store new-state) + token-target' (toht/get-token file' :token-target) + rect-1' (cths/get-shape file' :rect-1)] + (t/is (some? (:applied-tokens rect-1'))) + (t/is (= (:width (:applied-tokens rect-1')) (:id token-target'))) + (t/is (= (:height (:applied-tokens rect-1')) (:id token-target'))) + (t/is (= (:width rect-1') 100)) + (t/is (= (:height rect-1') 100))))))))) (t/deftest test-apply-sizing - (t/testing "applies sizing token and updates the shapes width and height") - (t/async - done - (let [file (-> (setup-file) - (toht/add-token :token-target {:value "100" - :name "sizing.sm" - :type :sizing})) - store (ths/setup-store file) - rect-1 (cths/get-shape file :rect-1) - events [(wtc/apply-token {:shape-ids [(:id rect-1)] - :attributes #{:width :height} - :token (toht/get-token file :token-target) - :on-update-shape wtc/update-shape-dimensions})]] - (tohs/run-store-async - store done events - (fn [new-state] - (let [file' (ths/get-file-from-store new-state) - token-target' (toht/get-token file' :token-target) - rect-1' (cths/get-shape file' :rect-1)] - (t/is (some? (:applied-tokens rect-1'))) - (t/is (= (:width (:applied-tokens rect-1')) (:id token-target'))) - (t/is (= (:height (:applied-tokens rect-1')) (:id token-target'))) - (t/is (= (:width rect-1') 100)) - (t/is (= (:height rect-1') 100)))))))) + (t/testing "applies sizing token and updates the shapes width and height" + (t/async + done + (let [file (-> (setup-file) + (toht/add-token :token-target {:value "100" + :name "sizing.sm" + :type :sizing})) + store (ths/setup-store file) + rect-1 (cths/get-shape file :rect-1) + events [(wtc/apply-token {:shape-ids [(:id rect-1)] + :attributes #{:width :height} + :token (toht/get-token file :token-target) + :on-update-shape wtc/update-shape-dimensions})]] + (tohs/run-store-async + store done events + (fn [new-state] + (let [file' (ths/get-file-from-store new-state) + token-target' (toht/get-token file' :token-target) + rect-1' (cths/get-shape file' :rect-1)] + (t/is (some? (:applied-tokens rect-1'))) + (t/is (= (:width (:applied-tokens rect-1')) (:id token-target'))) + (t/is (= (:height (:applied-tokens rect-1')) (:id token-target'))) + (t/is (= (:width rect-1') 100)) + (t/is (= (:height rect-1') 100))))))))) (t/deftest test-apply-opacity - (t/testing "applies opacity token and updates the shapes opacity") - (t/async - done - (let [file (-> (setup-file) - (toht/add-token :token-target {:value "0.5" - :name "opacity.medium" - :type :opacity})) - store (ths/setup-store file) - rect-1 (cths/get-shape file :rect-1) - events [(wtc/apply-token {:shape-ids [(:id rect-1)] - :attributes #{:opacity} - :token (toht/get-token file :token-target) - :on-update-shape wtc/update-opacity})]] - (tohs/run-store-async - store done events - (fn [new-state] - (let [file' (ths/get-file-from-store new-state) - token-target' (toht/get-token file' :token-target) - rect-1' (cths/get-shape file' :rect-1)] - (t/is (some? (:applied-tokens rect-1'))) - (t/is (= (:opacity (:applied-tokens rect-1')) (:id token-target'))) - ;; TODO Fix opacity shape update not working? - #_(t/is (= (:opacity rect-1') 0.5)))))))) + (t/testing "applies opacity token and updates the shapes opacity" + (t/async + done + (let [file (-> (setup-file) + (toht/add-token :token-target {:value "0.5" + :name "opacity.medium" + :type :opacity})) + store (ths/setup-store file) + rect-1 (cths/get-shape file :rect-1) + events [(wtc/apply-token {:shape-ids [(:id rect-1)] + :attributes #{:opacity} + :token (toht/get-token file :token-target) + :on-update-shape wtc/update-opacity})]] + (tohs/run-store-async + store done events + (fn [new-state] + (let [file' (ths/get-file-from-store new-state) + token-target' (toht/get-token file' :token-target) + rect-1' (cths/get-shape file' :rect-1)] + (t/is (some? (:applied-tokens rect-1'))) + (t/is (= (:opacity (:applied-tokens rect-1')) (:id token-target'))) + ;; TODO Fix opacity shape update not working? + #_(t/is (= (:opacity rect-1') 0.5))))))))) (t/deftest test-apply-rotation - (t/testing "applies rotation token and updates the shapes rotation") - (t/async - done - (let [file (-> (setup-file) - (toht/add-token :token-target {:value "120" - :name "rotation.medium" - :type :rotation})) - store (ths/setup-store file) - rect-1 (cths/get-shape file :rect-1) - events [(wtc/apply-token {:shape-ids [(:id rect-1)] - :attributes #{:rotation} - :token (toht/get-token file :token-target) - :on-update-shape wtc/update-rotation})]] - (tohs/run-store-async - store done events - (fn [new-state] - (let [file' (ths/get-file-from-store new-state) - token-target' (toht/get-token file' :token-target) - rect-1' (cths/get-shape file' :rect-1)] - (t/is (some? (:applied-tokens rect-1'))) - (t/is (= (:rotation (:applied-tokens rect-1')) (:id token-target'))) - (t/is (= (:rotation rect-1') 120)))))))) + (t/testing "applies rotation token and updates the shapes rotation" + (t/async + done + (let [file (-> (setup-file) + (toht/add-token :token-target {:value "120" + :name "rotation.medium" + :type :rotation})) + store (ths/setup-store file) + rect-1 (cths/get-shape file :rect-1) + events [(wtc/apply-token {:shape-ids [(:id rect-1)] + :attributes #{:rotation} + :token (toht/get-token file :token-target) + :on-update-shape wtc/update-rotation})]] + (tohs/run-store-async + store done events + (fn [new-state] + (let [file' (ths/get-file-from-store new-state) + token-target' (toht/get-token file' :token-target) + rect-1' (cths/get-shape file' :rect-1)] + (t/is (some? (:applied-tokens rect-1'))) + (t/is (= (:rotation (:applied-tokens rect-1')) (:id token-target'))) + (t/is (= (:rotation rect-1') 120))))))))) (t/deftest test-toggle-token-none (t/testing "should apply token to all selected items, where no item has the token applied" From f731a30f81ccf4fb0781e19fa5c67bbcada29306 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Mon, 22 Jul 2024 13:54:36 +0200 Subject: [PATCH 17/77] Allow removing other attributes with apply-token function --- .../app/main/ui/workspace/tokens/core.cljs | 14 ++++++-- .../token_tests/logic/token_actions_test.cljs | 36 ++++++++++++++++++- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/core.cljs b/frontend/src/app/main/ui/workspace/tokens/core.cljs index 1bc6e45ca..55f18bdf2 100644 --- a/frontend/src/app/main/ui/workspace/tokens/core.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/core.cljs @@ -24,7 +24,8 @@ [beicon.v2.core :as rx] [cuerdas.core :as str] [potok.v2.core :as ptk] - [promesa.core :as p])) + [promesa.core :as p] + [cljs.pprint :as pprint])) ;; Helpers --------------------------------------------------------------------- @@ -107,7 +108,12 @@ ;; Events ---------------------------------------------------------------------- (defn apply-token - [{:keys [attributes shape-ids token on-update-shape] :as _props}] + "Apply `attributes` that match `token` for `shape-ids`. + + Optionally remove attributes from `attributes-to-remove`, + this is useful for applying a single attribute from an attributes set + while removing other applied tokens from this set." + [{:keys [attributes attributes-to-remove token shape-ids on-update-shape] :as _props}] (ptk/reify ::apply-token ptk/WatchEvent (watch [_ state _] @@ -121,7 +127,9 @@ (rx/of (dwu/start-undo-transaction undo-id) (dch/update-shapes shape-ids (fn [shape] - (update shape :applied-tokens merge tokenized-attributes))) + (cond-> shape + attributes-to-remove (update :applied-tokens #(apply (partial dissoc %) attributes-to-remove)) + :always (update :applied-tokens merge tokenized-attributes)))) (when on-update-shape (on-update-shape resolved-value shape-ids attributes)) (dwu/commit-undo-transaction undo-id))))))))) diff --git a/frontend/test/token_tests/logic/token_actions_test.cljs b/frontend/test/token_tests/logic/token_actions_test.cljs index a29d9045a..8546b0771 100644 --- a/frontend/test/token_tests/logic/token_actions_test.cljs +++ b/frontend/test/token_tests/logic/token_actions_test.cljs @@ -27,7 +27,7 @@ :type :border-radius}))) (t/deftest test-apply-token - (t/testing "applying a token twice with the same attributes will override" + (t/testing "applying a token twice with the same attributes will override the previous applied token" (t/async done (let [file (setup-file) @@ -53,6 +53,40 @@ (t/is (= (:rx rect-1') 24)) (t/is (= (:ry rect-1') 24))))))))) +(t/deftest test-apply-token-overwrite + (t/testing "removes old token attributes and applies only single attribute" + (t/async + done + (let [file (setup-file) + store (ths/setup-store file) + rect-1 (cths/get-shape file :rect-1) + events [;; Apply `:token-1` to all border radius attributes + (wtc/apply-token {:attributes #{:rx :ry :r1 :r2 :r3 :r4} + :token (toht/get-token file :token-1) + :shape-ids [(:id rect-1)] + :on-update-shape wtc/update-shape-radius-all}) + ;; Apply single `:r1` attribute to same shape + ;; while removing other attributes from the border-radius set + ;; but keep `:r4` for testing purposes + (wtc/apply-token {:attributes #{:r1} + :attributes-to-remove #{:rx :ry :r1 :r2 :r3} + :token (toht/get-token file :token-2) + :shape-ids [(:id rect-1)] + :on-update-shape wtc/update-shape-radius-all})]] + (tohs/run-store-async + store done events + (fn [new-state] + (let [file' (ths/get-file-from-store new-state) + token-1' (toht/get-token file' :token-1) + token-2' (toht/get-token file' :token-2) + rect-1' (cths/get-shape file' :rect-1)] + (t/testing "other border-radius attributes got removed" + (t/is (nil? (:rx (:applied-tokens rect-1'))))) + (t/testing "r1 got applied with :token-2" + (t/is (= (:r1 (:applied-tokens rect-1')) (:id token-2')))) + (t/testing "while :r4 was kept" + (t/is (= (:r4 (:applied-tokens rect-1')) (:id token-1')))))))))));))))))))))) + (t/deftest test-apply-border-radius (t/testing "applies radius token and updates the shapes radius" (t/async From 2836ff269359e31cd9ce29bc8ab5a2cdb4877f7a Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Mon, 22 Jul 2024 17:07:42 +0200 Subject: [PATCH 18/77] Fix actions --- .../ui/workspace/tokens/context_menu.cljs | 48 +++++++++---------- 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index 44e1f6a84..f66d67cb5 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -215,38 +215,34 @@ all-attributes #{:r1 :r2 :r3 :r4} ids-by-attributes (wtt/shapes-ids-by-applied-attributes token selected-shapes all-attributes) shape-ids (into #{} (map :id selected-shapes)) - all? (wtt/shapes-applied-all? ids-by-attributes shape-ids all-attributes) - selected-pred #(and - (not all?) - (seq (% ids-by-attributes))) + all-selected? (wtt/shapes-applied-all? ids-by-attributes shape-ids all-attributes) single-attributes (->> {:r1 "Top Left" :r2 "Top Right" :r3 "Bottom Left" :r4 "Bottom Right"} (map (fn [[attr title]] - (let [selected? (selected-pred attr)] + (let [selected? (seq (attr ids-by-attributes))] {:title title - :selected? selected? - :action (if selected? - (st/emit! (wtc/unapply-token {:token token - :attributes #{attr} - :shape-ids shape-ids})) - (st/emit! (wtc/apply-token {:token token - :attributes #{attr} - :on-update-shape wtc/update-shape-radius-single-corner - :shape-ids shape-ids})))}))))] - (concat - [{:title "All" - :selected? all? - :action #(if all? - (st/emit! (wtc/unapply-token {:token token - :attributes all-attributes - :shape-ids shape-ids})) - (st/emit! (wtc/apply-token {:token token - :attributes all-attributes - :on-update-shape wtc/update-shape-radius-all - :shape-ids shape-ids})))}] - single-attributes))) + :selected? (and (not all-selected?) selected?) + :action #(let [props {:attributes #{attr} + :token token + :shape-ids shape-ids} + event (cond + all-selected? (-> (assoc props :attributes-to-remove #{:r1 :r2 :r3 :r4 :rx :ry}) + (wtc/apply-token)) + selected? (wtc/unapply-token props) + :else (-> (assoc props :on-update-shape wtc/update-shape-radius-single-corner) + (wtc/apply-token)))] + (st/emit! event))})))) + all-attribute (let [props {:attributes all-attributes + :token token + :shape-ids shape-ids}] + {:title "All" + :selected? all-selected? + :action #(if all-selected? + (st/emit! (wtc/unapply-token props)) + (st/emit! (wtc/apply-token (assoc props :on-update-shape wtc/update-shape-radius-all))))})] + (concat [all-attribute] single-attributes))) (defn shape-attribute-actions [{:keys [token-id token-type selected-shapes] :as context-data}] (let [attributes->actions (fn [update-fn coll] From fd2f5537cf0cb0cbf2d953dea63fd0918a435c63 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Mon, 22 Jul 2024 18:07:16 +0200 Subject: [PATCH 19/77] Extract common logic --- .../ui/workspace/tokens/context_menu.cljs | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index f66d67cb5..4ad335732 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -210,9 +210,48 @@ :token-type-props token-type-props :selected-shapes selected-shapes}))) +(defn attribute-actions [token selected-shapes attributes] + (let [ids-by-attributes (wtt/shapes-ids-by-applied-attributes token selected-shapes attributes) + shape-ids (into #{} (map :id selected-shapes))] + {:all-selected? (wtt/shapes-applied-all? ids-by-attributes shape-ids attributes) + :shape-ids shape-ids + :selected-pred #(seq (% ids-by-attributes))})) + (defn border-radius-attribute-actions [{:keys [token-id selected-shapes] :as _props}] (let [token {:id token-id} all-attributes #{:r1 :r2 :r3 :r4} + {:keys [all-selected? selected-pred shape-ids]} (attribute-actions token selected-shapes #{:r1 :r2 :r3 :r4}) + single-attributes (->> {:r1 "Top Left" + :r2 "Top Right" + :r3 "Bottom Left" + :r4 "Bottom Right"} + (map (fn [[attr title]] + (let [selected? (selected-pred attr)] + {:title title + :selected? (and (not all-selected?) selected?) + :action #(let [props {:attributes #{attr} + :token token + :shape-ids shape-ids} + event (cond + all-selected? (-> (assoc props :attributes-to-remove #{:r1 :r2 :r3 :r4 :rx :ry}) + (wtc/apply-token)) + selected? (wtc/unapply-token props) + :else (-> (assoc props :on-update-shape wtc/update-shape-radius-single-corner) + (wtc/apply-token)))] + (st/emit! event))})))) + all-attribute (let [props {:attributes all-attributes + :token token + :shape-ids shape-ids}] + {:title "All" + :selected? all-selected? + :action #(if all-selected? + (st/emit! (wtc/unapply-token props)) + (st/emit! (wtc/apply-token (assoc props :on-update-shape wtc/update-shape-radius-all))))})] + (concat [all-attribute] single-attributes))) + +(defn spacing-attribute-actions [{:keys [token-id selected-shapes] :as _props}] + (let [token {:id token-id} + all-attributes #{:p1 :p2 :p3 :p4} ids-by-attributes (wtt/shapes-ids-by-applied-attributes token selected-shapes all-attributes) shape-ids (into #{} (map :id selected-shapes)) all-selected? (wtt/shapes-applied-all? ids-by-attributes shape-ids all-attributes) From c00023319a8fb190ad4b616734a609cb406ac580 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Mon, 22 Jul 2024 18:13:37 +0200 Subject: [PATCH 20/77] Extract data --- .../ui/workspace/tokens/context_menu.cljs | 55 +++++++++++-------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index 4ad335732..1029a00ef 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -220,7 +220,7 @@ (defn border-radius-attribute-actions [{:keys [token-id selected-shapes] :as _props}] (let [token {:id token-id} all-attributes #{:r1 :r2 :r3 :r4} - {:keys [all-selected? selected-pred shape-ids]} (attribute-actions token selected-shapes #{:r1 :r2 :r3 :r4}) + {:keys [all-selected? selected-pred shape-ids]} (attribute-actions token selected-shapes all-attributes) single-attributes (->> {:r1 "Top Left" :r2 "Top Right" :r3 "Bottom Left" @@ -249,18 +249,20 @@ (st/emit! (wtc/apply-token (assoc props :on-update-shape wtc/update-shape-radius-all))))})] (concat [all-attribute] single-attributes))) +(def spacing + {:padding {:p1 "Top" + :p2 "Right" + :p3 "Bottom" + :p4 "Left"}}) + (defn spacing-attribute-actions [{:keys [token-id selected-shapes] :as _props}] (let [token {:id token-id} - all-attributes #{:p1 :p2 :p3 :p4} - ids-by-attributes (wtt/shapes-ids-by-applied-attributes token selected-shapes all-attributes) - shape-ids (into #{} (map :id selected-shapes)) - all-selected? (wtt/shapes-applied-all? ids-by-attributes shape-ids all-attributes) - single-attributes (->> {:r1 "Top Left" - :r2 "Top Right" - :r3 "Bottom Left" - :r4 "Bottom Right"} + padding-attrs (:padding spacing) + all-padding-attrs (into #{} (keys padding-attrs)) + {:keys [all-selected? selected-pred shape-ids]} (attribute-actions token selected-shapes all-padding-attrs) + single-attributes (->> padding-attrs (map (fn [[attr title]] - (let [selected? (seq (attr ids-by-attributes))] + (let [selected? (selected-pred attr)] {:title title :selected? (and (not all-selected?) selected?) :action #(let [props {:attributes #{attr} @@ -273,7 +275,7 @@ :else (-> (assoc props :on-update-shape wtc/update-shape-radius-single-corner) (wtc/apply-token)))] (st/emit! event))})))) - all-attribute (let [props {:attributes all-attributes + all-attribute (let [props {:attributes all-padding-attrs :token token :shape-ids shape-ids}] {:title "All" @@ -283,6 +285,23 @@ (st/emit! (wtc/apply-token (assoc props :on-update-shape wtc/update-shape-radius-all))))})] (concat [all-attribute] single-attributes))) +(comment + (comment + apply-spacing-token + [{:title "All" :attributes #{:p1 :p2 :p3 :p4}} + {:title "Top" :attributes #{:p1}} + {:title "Right" :attributes #{:p2}} + {:title "Bottom" :attributes #{:p3}} + {:title "Left" :attributes #{:p4}} + :separator + {:title "Column Gap" :attributes #{:column-gap}} + {:title "Row Gap" :attributes #{:row-gap}} + {:title "Vertical padding" :attributes #{:p1 :p3}} + {:title "Horizontal padding" :attributes #{:p2 :p4}}]) + + nil) + + (defn shape-attribute-actions [{:keys [token-id token-type selected-shapes] :as context-data}] (let [attributes->actions (fn [update-fn coll] (for [{:keys [attributes] :as item} coll] @@ -295,19 +314,7 @@ :selected? selected?)))))] (case token-type :border-radius (border-radius-attribute-actions context-data) - :spacing (attributes->actions - apply-spacing-token - [{:title "All" :attributes #{:p1 :p2 :p3 :p4}} - {:title "Top" :attributes #{:p1}} - {:title "Right" :attributes #{:p2}} - {:title "Bottom" :attributes #{:p3}} - {:title "Left" :attributes #{:p4}} - :separator - {:title "Column Gap" :attributes #{:column-gap}} - {:title "Vertical padding" :attributes #{:p1 :p3}} - {:title "Horizontal padding" :attributes #{:p2 :p4}} - {:title "Row Gap" :attributes #{:row-gap}}]) - + :spacing (spacing-attribute-actions context-data) :sizing (attributes->actions apply-sizing-token [{:title "All" :attributes #{:width :height :layout-item-min-w :layout-item-max-w :layout-item-min-h :layout-item-max-h}} From 2411eeb644b195189196233810e70602e57c4698 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Mon, 22 Jul 2024 18:23:12 +0200 Subject: [PATCH 21/77] Add separate gap --- .../ui/workspace/tokens/context_menu.cljs | 84 +++++++++++++------ 1 file changed, 59 insertions(+), 25 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index 1029a00ef..8f9fcfcb2 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -253,37 +253,71 @@ {:padding {:p1 "Top" :p2 "Right" :p3 "Bottom" - :p4 "Left"}}) + :p4 "Left"} + :gap {:column-gap "Column Gap" + :row-gap "Row Gap"}}) (defn spacing-attribute-actions [{:keys [token-id selected-shapes] :as _props}] (let [token {:id token-id} padding-attrs (:padding spacing) all-padding-attrs (into #{} (keys padding-attrs)) {:keys [all-selected? selected-pred shape-ids]} (attribute-actions token selected-shapes all-padding-attrs) - single-attributes (->> padding-attrs - (map (fn [[attr title]] - (let [selected? (selected-pred attr)] - {:title title - :selected? (and (not all-selected?) selected?) - :action #(let [props {:attributes #{attr} - :token token - :shape-ids shape-ids} - event (cond - all-selected? (-> (assoc props :attributes-to-remove #{:r1 :r2 :r3 :r4 :rx :ry}) - (wtc/apply-token)) - selected? (wtc/unapply-token props) - :else (-> (assoc props :on-update-shape wtc/update-shape-radius-single-corner) - (wtc/apply-token)))] - (st/emit! event))})))) - all-attribute (let [props {:attributes all-padding-attrs - :token token - :shape-ids shape-ids}] - {:title "All" - :selected? all-selected? - :action #(if all-selected? - (st/emit! (wtc/unapply-token props)) - (st/emit! (wtc/apply-token (assoc props :on-update-shape wtc/update-shape-radius-all))))})] - (concat [all-attribute] single-attributes))) + single-padding (->> padding-attrs + (map (fn [[attr title]] + (let [selected? (selected-pred attr)] + {:title title + :selected? (and (not all-selected?) selected?) + :action #(let [props {:attributes #{attr} + :token token + :shape-ids shape-ids} + event (cond + all-selected? (-> (assoc props :attributes-to-remove #{:r1 :r2 :r3 :r4 :rx :ry}) + (wtc/apply-token)) + selected? (wtc/unapply-token props) + :else (-> (assoc props :on-update-shape wtc/update-shape-radius-single-corner) + (wtc/apply-token)))] + (st/emit! event))}))) + (into)) + all-padding (let [props {:attributes all-padding-attrs + :token token + :shape-ids shape-ids}] + {:title "All" + :selected? all-selected? + :action #(if all-selected? + (st/emit! (wtc/unapply-token props)) + (st/emit! (wtc/apply-token (assoc props :on-update-shape wtc/update-shape-radius-all))))}) + gap-attrs (:gap spacing) + all-gap-attrs (into #{} (keys gap-attrs)) + {:keys [all-selected? selected-pred shape-ids]} (attribute-actions token selected-shapes all-gap-attrs) + single-gap (->> gap-attrs + (map (fn [[attr title]] + (let [selected? (selected-pred attr)] + {:title title + :selected? (and (not all-selected?) selected?) + :action #(let [props {:attributes #{attr} + :token token + :shape-ids shape-ids} + event (cond + all-selected? (-> (assoc props :attributes-to-remove #{:r1 :r2 :r3 :r4 :rx :ry}) + (wtc/apply-token)) + selected? (wtc/unapply-token props) + :else (-> (assoc props :on-update-shape wtc/update-shape-radius-single-corner) + (wtc/apply-token)))] + (st/emit! event))}))) + (into)) + all-gap (let [props {:attributes all-gap-attrs + :token token + :shape-ids shape-ids}] + {:title "All" + :selected? all-selected? + :action #(if all-selected? + (st/emit! (wtc/unapply-token props)) + (st/emit! (wtc/apply-token (assoc props :on-update-shape wtc/update-shape-radius-all))))})] + (concat [all-padding] + single-padding + [:separator] + [all-gap] + single-gap))) (comment (comment From 0e858d880df629ab6c9a4ddc437e0cee2fb6b711 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Tue, 23 Jul 2024 10:37:51 +0200 Subject: [PATCH 22/77] Add horizontal/vertical padding toggle --- .../ui/workspace/tokens/context_menu.cljs | 94 ++++++++++++++----- 1 file changed, 68 insertions(+), 26 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index 8f9fcfcb2..6dc357840 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -259,33 +259,76 @@ (defn spacing-attribute-actions [{:keys [token-id selected-shapes] :as _props}] (let [token {:id token-id} + on-update-shape (fn [resolved-value shape-ids attrs] + (dwsl/update-layout shape-ids {:layout-padding (zipmap attrs (repeat resolved-value))})) padding-attrs (:padding spacing) all-padding-attrs (into #{} (keys padding-attrs)) {:keys [all-selected? selected-pred shape-ids]} (attribute-actions token selected-shapes all-padding-attrs) - single-padding (->> padding-attrs - (map (fn [[attr title]] - (let [selected? (selected-pred attr)] - {:title title - :selected? (and (not all-selected?) selected?) - :action #(let [props {:attributes #{attr} - :token token - :shape-ids shape-ids} - event (cond - all-selected? (-> (assoc props :attributes-to-remove #{:r1 :r2 :r3 :r4 :rx :ry}) - (wtc/apply-token)) - selected? (wtc/unapply-token props) - :else (-> (assoc props :on-update-shape wtc/update-shape-radius-single-corner) - (wtc/apply-token)))] - (st/emit! event))}))) - (into)) - all-padding (let [props {:attributes all-padding-attrs - :token token - :shape-ids shape-ids}] - {:title "All" - :selected? all-selected? - :action #(if all-selected? - (st/emit! (wtc/unapply-token props)) - (st/emit! (wtc/apply-token (assoc props :on-update-shape wtc/update-shape-radius-all))))}) + horizontal-attributes #{:p1 :p3} + horizontal-padding-selected? (and + (not all-selected?) + (every? selected-pred horizontal-attributes)) + vertical-attributes #{:p2 :p4} + vertical-padding-selected? (and + (not all-selected?) + (every? selected-pred vertical-attributes)) + padding-items [{:title "All" + :selected? all-selected? + :action (fn [] + (let [props {:attributes all-padding-attrs + :token token + :shape-ids shape-ids}] + (if all-selected? + (st/emit! (wtc/unapply-token props)) + (st/emit! (wtc/apply-token (assoc props :on-update-shape on-update-shape))))))} + {:title "Horizontal" + :selected? horizontal-padding-selected? + :action (fn [] + (let [props {:token token + :shape-ids shape-ids} + event (cond + all-selected? (wtc/apply-token (assoc props :attributes-to-remove vertical-attributes)) + horizontal-padding-selected? (wtc/apply-token (assoc props :attributes-to-remove horizontal-attributes)) + :else (wtc/apply-token (assoc props + :attributes horizontal-attributes + :on-update-shape on-update-shape)))] + (st/emit! event)))} + {:title "Vertical" + :selected? vertical-padding-selected? + :action (fn [] + (let [props {:token token + :shape-ids shape-ids} + event (cond + all-selected? (wtc/apply-token (assoc props :attributes-to-remove vertical-attributes)) + vertical-padding-selected? (wtc/apply-token (assoc props :attributes-to-remove vertical-attributes)) + :else (wtc/apply-token (assoc props + :attributes vertical-attributes + :on-update-shape on-update-shape)))] + (st/emit! event)))}] + ;; single-padding (->> padding-attrs + ;; (map (fn [[attr title]] + ;; (let [selected? (selected-pred attr)] + ;; {:title title + ;; :selected? (and (not all-selected?) selected?) + ;; :action #(let [props {:attributes #{attr} + ;; :token token + ;; :shape-ids shape-ids} + ;; event (cond + ;; all-selected? (-> (assoc props :attributes-to-remove all-padding-attrs) + ;; (wtc/apply-token)) + ;; selected? (wtc/unapply-token props) + ;; :else (-> (assoc props :on-update-shape wtc/update-shape-radius-single-corner) + ;; (wtc/apply-token)))] + ;; (st/emit! event))}))) + ;; (into)) + ;; all-padding (let [props {:attributes all-padding-attrs + ;; :token token + ;; :shape-ids shape-ids}] + ;; {:title "All" + ;; :selected? all-selected? + ;; :action #(if all-selected? + ;; (st/emit! (wtc/unapply-token props)) + ;; (st/emit! (wtc/apply-token (assoc props :on-update-shape wtc/update-shape-radius-all))))}) gap-attrs (:gap spacing) all-gap-attrs (into #{} (keys gap-attrs)) {:keys [all-selected? selected-pred shape-ids]} (attribute-actions token selected-shapes all-gap-attrs) @@ -313,8 +356,7 @@ :action #(if all-selected? (st/emit! (wtc/unapply-token props)) (st/emit! (wtc/apply-token (assoc props :on-update-shape wtc/update-shape-radius-all))))})] - (concat [all-padding] - single-padding + (concat padding-items [:separator] [all-gap] single-gap))) From 39822a3b3140bd4e96493abf31d921aaab8fa0da Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Tue, 23 Jul 2024 10:49:19 +0200 Subject: [PATCH 23/77] Add single padding --- .../ui/workspace/tokens/context_menu.cljs | 47 +++++++++---------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index 6dc357840..6822582d4 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -305,30 +305,28 @@ :attributes vertical-attributes :on-update-shape on-update-shape)))] (st/emit! event)))}] - ;; single-padding (->> padding-attrs - ;; (map (fn [[attr title]] - ;; (let [selected? (selected-pred attr)] - ;; {:title title - ;; :selected? (and (not all-selected?) selected?) - ;; :action #(let [props {:attributes #{attr} - ;; :token token - ;; :shape-ids shape-ids} - ;; event (cond - ;; all-selected? (-> (assoc props :attributes-to-remove all-padding-attrs) - ;; (wtc/apply-token)) - ;; selected? (wtc/unapply-token props) - ;; :else (-> (assoc props :on-update-shape wtc/update-shape-radius-single-corner) - ;; (wtc/apply-token)))] - ;; (st/emit! event))}))) - ;; (into)) - ;; all-padding (let [props {:attributes all-padding-attrs - ;; :token token - ;; :shape-ids shape-ids}] - ;; {:title "All" - ;; :selected? all-selected? - ;; :action #(if all-selected? - ;; (st/emit! (wtc/unapply-token props)) - ;; (st/emit! (wtc/apply-token (assoc props :on-update-shape wtc/update-shape-radius-all))))}) + single-padding-items (->> padding-attrs + (map (fn [[attr title]] + (let [same-axis-selected? (cond + (get horizontal-attributes attr) horizontal-padding-selected? + (get vertical-attributes attr) vertical-padding-selected? + :else true) + selected? (and + (not all-selected?) + (not same-axis-selected?) + (selected-pred attr))] + {:title title + :selected? selected? + :action #(let [props {:attributes #{attr} + :token token + :shape-ids shape-ids} + event (cond + all-selected? (-> (assoc props :attributes-to-remove all-padding-attrs) + (wtc/apply-token)) + selected? (wtc/unapply-token props) + :else (-> (assoc props :on-update-shape on-update-shape) + (wtc/apply-token)))] + (st/emit! event))})))) gap-attrs (:gap spacing) all-gap-attrs (into #{} (keys gap-attrs)) {:keys [all-selected? selected-pred shape-ids]} (attribute-actions token selected-shapes all-gap-attrs) @@ -357,6 +355,7 @@ (st/emit! (wtc/unapply-token props)) (st/emit! (wtc/apply-token (assoc props :on-update-shape wtc/update-shape-radius-all))))})] (concat padding-items + single-padding-items [:separator] [all-gap] single-gap))) From 4cf8b2c1438e3b03e3144ddd78c6c7f0551135fe Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Tue, 23 Jul 2024 10:54:23 +0200 Subject: [PATCH 24/77] Extract gap as extra function --- .../ui/workspace/tokens/context_menu.cljs | 64 ++++++++++--------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index 6822582d4..3e45a38c1 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -257,7 +257,38 @@ :gap {:column-gap "Column Gap" :row-gap "Row Gap"}}) -(defn spacing-attribute-actions [{:keys [token-id selected-shapes] :as _props}] +(defn gap-attribute-actions [{:keys [token-id selected-shapes] :as _props}] + (let [token {:id token-id} + gap-attrs (:gap spacing) + all-gap-attrs (into #{} (keys gap-attrs)) + {:keys [all-selected? selected-pred shape-ids]} (attribute-actions token selected-shapes all-gap-attrs) + all-gap (let [props {:attributes all-gap-attrs + :token token + :shape-ids shape-ids}] + [{:title "All" + :selected? all-selected? + :action #(if all-selected? + (st/emit! (wtc/unapply-token props)) + (st/emit! (wtc/apply-token (assoc props :on-update-shape wtc/update-shape-radius-all))))}]) + single-gap (->> gap-attrs + (map (fn [[attr title]] + (let [selected? (selected-pred attr)] + {:title title + :selected? (and (not all-selected?) selected?) + :action #(let [props {:attributes #{attr} + :token token + :shape-ids shape-ids} + event (cond + all-selected? (-> (assoc props :attributes-to-remove #{:r1 :r2 :r3 :r4 :rx :ry}) + (wtc/apply-token)) + selected? (wtc/unapply-token props) + :else (-> (assoc props :on-update-shape wtc/update-shape-radius-single-corner) + (wtc/apply-token)))] + (st/emit! event))}))) + (into))] + (concat all-gap single-gap))) + +(defn spacing-attribute-actions [{:keys [token-id selected-shapes] :as props}] (let [token {:id token-id} on-update-shape (fn [resolved-value shape-ids attrs] (dwsl/update-layout shape-ids {:layout-padding (zipmap attrs (repeat resolved-value))})) @@ -327,38 +358,11 @@ :else (-> (assoc props :on-update-shape on-update-shape) (wtc/apply-token)))] (st/emit! event))})))) - gap-attrs (:gap spacing) - all-gap-attrs (into #{} (keys gap-attrs)) - {:keys [all-selected? selected-pred shape-ids]} (attribute-actions token selected-shapes all-gap-attrs) - single-gap (->> gap-attrs - (map (fn [[attr title]] - (let [selected? (selected-pred attr)] - {:title title - :selected? (and (not all-selected?) selected?) - :action #(let [props {:attributes #{attr} - :token token - :shape-ids shape-ids} - event (cond - all-selected? (-> (assoc props :attributes-to-remove #{:r1 :r2 :r3 :r4 :rx :ry}) - (wtc/apply-token)) - selected? (wtc/unapply-token props) - :else (-> (assoc props :on-update-shape wtc/update-shape-radius-single-corner) - (wtc/apply-token)))] - (st/emit! event))}))) - (into)) - all-gap (let [props {:attributes all-gap-attrs - :token token - :shape-ids shape-ids}] - {:title "All" - :selected? all-selected? - :action #(if all-selected? - (st/emit! (wtc/unapply-token props)) - (st/emit! (wtc/apply-token (assoc props :on-update-shape wtc/update-shape-radius-all))))})] + gap-items (gap-attribute-actions props)] (concat padding-items single-padding-items [:separator] - [all-gap] - single-gap))) + gap-items))) (comment (comment From bad9056d5424d0028f5fd386865ef584d0765381 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Tue, 23 Jul 2024 11:35:07 +0200 Subject: [PATCH 25/77] Update gap --- .../main/ui/workspace/tokens/context_menu.cljs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index 3e45a38c1..ae5759e91 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -25,7 +25,7 @@ [app.main.ui.workspace.tokens.token :as wtt] [app.util.dom :as dom] [app.util.timers :as timers] - [clojure.set :as set] + [clojure.set :as set :refer [rename-keys]] [okulary.core :as l] [rumext.v2 :as mf])) @@ -140,11 +140,10 @@ (defn update-layout-spacing [value shape-ids attributes] (if-let [layout-gap (cond - (:row-gap attributes) {:row-gap value} - (:column-gap attributes) {:column-gap value})] - (st/emit! (dwsl/update-layout shape-ids {:layout-gap layout-gap})) - (st/emit! (dwsl/update-layout shape-ids {:layout-padding (zipmap attributes (repeat value))})))) - + (:row-gap attributes) {:row-gap value} + (:column-gap attributes) {:column-gap value})] + (dwsl/update-layout shape-ids {:layout-gap layout-gap}) + (dwsl/update-layout shape-ids {:layout-padding (zipmap attributes (repeat value))}))) (defn apply-spacing-token [{:keys [token-id token-type-props selected-shapes]} attributes] (let [token (dt/get-token-data-from-token-id token-id) @@ -259,6 +258,7 @@ (defn gap-attribute-actions [{:keys [token-id selected-shapes] :as _props}] (let [token {:id token-id} + on-update-shape update-layout-spacing gap-attrs (:gap spacing) all-gap-attrs (into #{} (keys gap-attrs)) {:keys [all-selected? selected-pred shape-ids]} (attribute-actions token selected-shapes all-gap-attrs) @@ -269,7 +269,7 @@ :selected? all-selected? :action #(if all-selected? (st/emit! (wtc/unapply-token props)) - (st/emit! (wtc/apply-token (assoc props :on-update-shape wtc/update-shape-radius-all))))}]) + (st/emit! (wtc/apply-token (assoc props :on-update-shape on-update-shape))))}]) single-gap (->> gap-attrs (map (fn [[attr title]] (let [selected? (selected-pred attr)] @@ -279,10 +279,10 @@ :token token :shape-ids shape-ids} event (cond - all-selected? (-> (assoc props :attributes-to-remove #{:r1 :r2 :r3 :r4 :rx :ry}) + all-selected? (-> (assoc props :attributes-to-remove #{:row-gap :column-gap}) (wtc/apply-token)) selected? (wtc/unapply-token props) - :else (-> (assoc props :on-update-shape wtc/update-shape-radius-single-corner) + :else (-> (assoc props :on-update-shape on-update-shape) (wtc/apply-token)))] (st/emit! event))}))) (into))] From cabc3d3f362326a9554793113ff7895a8327c16d Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Tue, 23 Jul 2024 14:55:14 +0200 Subject: [PATCH 26/77] Simplify passed prop date --- .../ui/workspace/tokens/context_menu.cljs | 66 ++++++++----------- .../app/main/ui/workspace/tokens/sidebar.cljs | 2 - 2 files changed, 27 insertions(+), 41 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index ae5759e91..32a500c9c 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -216,9 +216,8 @@ :shape-ids shape-ids :selected-pred #(seq (% ids-by-attributes))})) -(defn border-radius-attribute-actions [{:keys [token-id selected-shapes] :as _props}] - (let [token {:id token-id} - all-attributes #{:r1 :r2 :r3 :r4} +(defn border-radius-attribute-actions [{:keys [token selected-shapes]}] + (let [all-attributes #{:r1 :r2 :r3 :r4} {:keys [all-selected? selected-pred shape-ids]} (attribute-actions token selected-shapes all-attributes) single-attributes (->> {:r1 "Top Left" :r2 "Top Right" @@ -256,9 +255,8 @@ :gap {:column-gap "Column Gap" :row-gap "Row Gap"}}) -(defn gap-attribute-actions [{:keys [token-id selected-shapes] :as _props}] - (let [token {:id token-id} - on-update-shape update-layout-spacing +(defn gap-attribute-actions [{:keys [token selected-shapes]}] + (let [on-update-shape update-layout-spacing gap-attrs (:gap spacing) all-gap-attrs (into #{} (keys gap-attrs)) {:keys [all-selected? selected-pred shape-ids]} (attribute-actions token selected-shapes all-gap-attrs) @@ -288,9 +286,8 @@ (into))] (concat all-gap single-gap))) -(defn spacing-attribute-actions [{:keys [token-id selected-shapes] :as props}] - (let [token {:id token-id} - on-update-shape (fn [resolved-value shape-ids attrs] +(defn spacing-attribute-actions [{:keys [token selected-shapes] :as context-data}] + (let [on-update-shape (fn [resolved-value shape-ids attrs] (dwsl/update-layout shape-ids {:layout-padding (zipmap attrs (repeat resolved-value))})) padding-attrs (:padding spacing) all-padding-attrs (into #{} (keys padding-attrs)) @@ -358,40 +355,32 @@ :else (-> (assoc props :on-update-shape on-update-shape) (wtc/apply-token)))] (st/emit! event))})))) - gap-items (gap-attribute-actions props)] + gap-items (gap-attribute-actions context-data)] (concat padding-items single-padding-items [:separator] gap-items))) -(comment - (comment - apply-spacing-token - [{:title "All" :attributes #{:p1 :p2 :p3 :p4}} - {:title "Top" :attributes #{:p1}} - {:title "Right" :attributes #{:p2}} - {:title "Bottom" :attributes #{:p3}} - {:title "Left" :attributes #{:p4}} - :separator - {:title "Column Gap" :attributes #{:column-gap}} - {:title "Row Gap" :attributes #{:row-gap}} - {:title "Vertical padding" :attributes #{:p1 :p3}} - {:title "Horizontal padding" :attributes #{:p2 :p4}}]) +(def shape-attribute-actions-map + {:border-radius border-radius-attribute-actions + :spacing spacing-attribute-actions + :sizing nil}) - nil) +(defn shape-attribute-actions [{:keys [token] :as context-data}] + (when-let [with-actions (get shape-attribute-actions-map (:type token))] + (with-actions context-data))) - -(defn shape-attribute-actions [{:keys [token-id token-type selected-shapes] :as context-data}] +(defn shape-attribute-actions* [{:keys [token selected-shapes] :as context-data}] (let [attributes->actions (fn [update-fn coll] (for [{:keys [attributes] :as item} coll] (cond (= :separator item) item :else - (let [selected? (wtt/shapes-token-applied? {:id token-id} selected-shapes attributes)] + (let [selected? (wtt/shapes-token-applied? {:id token} selected-shapes attributes)] (assoc item :action #(update-fn context-data attributes) :selected? selected?)))))] - (case token-type + (case (:type token) :border-radius (border-radius-attribute-actions context-data) :spacing (spacing-attribute-actions context-data) :sizing (attributes->actions @@ -428,15 +417,15 @@ []))) -(defn generate-menu-entries [{:keys [token-id token-type-props _token-type selected-shapes] :as context-data}] - (let [{:keys [modal]} token-type-props +(defn generate-menu-entries [{:keys [token selected-shapes] :as context-data}] + (let [{:keys [modal]} (get wtc/token-types (:type token)) attribute-actions (when (seq selected-shapes) (shape-attribute-actions context-data)) - default-actions [{:title "Delete Token" :action #(st/emit! (dt/delete-token token-id))} - {:title "Duplicate Token" :action #(st/emit! (dt/duplicate-token token-id))} + default-actions [{:title "Delete Token" :action #(st/emit! (dt/delete-token (:id token)))} + {:title "Duplicate Token" :action #(st/emit! (dt/duplicate-token (:id token)))} {:title "Edit Token" :action (fn [event] (let [{:keys [key fields]} modal - token (dt/get-token-data-from-token-id token-id)] + token (dt/get-token-data-from-token-id (:id token))] (st/emit! dt/hide-token-context-menu) (dom/stop-propagation event) (modal/show! key {:x (.-clientX ^js event) @@ -452,7 +441,7 @@ (mf/defc token-pill-context-menu [context-data] (let [menu-entries (generate-menu-entries context-data)] - (for [[index {:keys [title action selected? children submenu] :as entry}] (d/enumerate menu-entries)] + (for [[index {:keys [title action selected? submenu] :as entry}] (d/enumerate menu-entries)] (cond (= :separator entry) [:& menu-separator] :else @@ -484,8 +473,9 @@ dropdown-ref (mf/use-ref) objects (mf/deref refs/workspace-page-objects) selected (mf/deref refs/selected-shapes) - selected-shapes (into [] (keep (d/getf objects)) selected)] - + selected-shapes (into [] (keep (d/getf objects)) selected) + token-id (:token-id mdata) + token (get (mf/deref refs/workspace-tokens) token-id)] (mf/use-effect (mf/deps mdata) #(let [dropdown (mf/ref-val dropdown-ref)] @@ -506,7 +496,5 @@ :on-context-menu prevent-default} (when (= :token (:type mdata)) [:ul {:class (stl/css :context-list)} - [:& token-pill-context-menu {:token-id (:token-id mdata) - :token-type-props (:token-type-props mdata) - :token-type (:token-type mdata) + [:& token-pill-context-menu {:token token :selected-shapes selected-shapes}]])]])) diff --git a/frontend/src/app/main/ui/workspace/tokens/sidebar.cljs b/frontend/src/app/main/ui/workspace/tokens/sidebar.cljs index 4edabdfcc..d3f43d039 100644 --- a/frontend/src/app/main/ui/workspace/tokens/sidebar.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/sidebar.cljs @@ -78,8 +78,6 @@ (dom/stop-propagation event) (st/emit! (dt/show-token-context-menu {:type :token :position (dom/get-client-position event) - :token-type-props token-type-props - :token-type type :token-id (:id token)})))) on-toggle-open-click (mf/use-fn From da3f2f820cb34029ddb3d1e86f2ccd700a7321b2 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Tue, 23 Jul 2024 15:28:13 +0200 Subject: [PATCH 27/77] Add generic context menu actions --- .../ui/workspace/tokens/context_menu.cljs | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index 32a500c9c..9a0affd8c 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -361,10 +361,28 @@ [:separator] gap-items))) +(defn generic-attribute-actions [attributes title {:keys [token selected-shapes]}] + (let [{:keys [on-update-shape] :as p} (get wtc/token-types (:type token)) + {:keys [selected-pred shape-ids]} (attribute-actions token selected-shapes attributes)] + (map (fn [attribute] + (let [selected? (selected-pred attribute) + props {:attributes #{attribute} + :token token + :shape-ids shape-ids}] + + {:title title + :selected? selected? + :action #(if selected? + (st/emit! (wtc/unapply-token props)) + (st/emit! (wtc/apply-token (assoc props :on-update-shape on-update-shape))))})) + attributes))) + (def shape-attribute-actions-map {:border-radius border-radius-attribute-actions :spacing spacing-attribute-actions - :sizing nil}) + :rotation (partial generic-attribute-actions #{:rotation} "Rotation") + :opacity (partial generic-attribute-actions #{:opacity} "Opacity") + :stroke-width (partial generic-attribute-actions #{:stroke-width} "Stroke Width")}) (defn shape-attribute-actions [{:keys [token] :as context-data}] (when-let [with-actions (get shape-attribute-actions-map (:type token))] @@ -381,8 +399,6 @@ :action #(update-fn context-data attributes) :selected? selected?)))))] (case (:type token) - :border-radius (border-radius-attribute-actions context-data) - :spacing (spacing-attribute-actions context-data) :sizing (attributes->actions apply-sizing-token [{:title "All" :attributes #{:width :height :layout-item-min-w :layout-item-max-w :layout-item-min-h :layout-item-max-h}} @@ -403,17 +419,7 @@ {:title "y" :attributes #{:y}}]) ;;TODO: Background blur {:title "Background blur" :attributes #{:width}}]) - :opacity (attributes->actions - apply-rotation-opacity-stroke-token - [{:title "opacity" :attributes #{:opacity}}]) - :rotation (attributes->actions - apply-rotation-opacity-stroke-token - [{:title "rotation" :attributes #{:rotation}}]) - - :stroke-width (attributes->actions - apply-rotation-opacity-stroke-token - [{:title "stroke width" :attributes #{:stroke-width}}]) []))) From 214a3236820b5a17b1288795d767b73cf1726182 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Tue, 23 Jul 2024 16:01:56 +0200 Subject: [PATCH 28/77] Add abstract method for a all or seperate actions --- .../ui/workspace/tokens/context_menu.cljs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index 9a0affd8c..e2d0842d1 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -361,6 +361,33 @@ [:separator] gap-items))) +(defn all-or-sepearate-actions [attribute-labels on-update-shape {:keys [token selected-shapes]}] + (let [attributes (set (keys attribute-labels)) + {:keys [all-selected? selected-pred shape-ids]} (attribute-actions token selected-shapes attributes) + all-action (let [props {:attributes attributes + :token token + :shape-ids shape-ids}] + {:title "All" + :selected? all-selected? + :action #(if all-selected? + (st/emit! (wtc/unapply-token props)) + (st/emit! (wtc/apply-token (assoc props :on-update-shape on-update-shape))))}) + single-actions (map (fn [[attr title]] + (let [selected? (selected-pred attr)] + {:title title + :selected? (and (not all-selected?) selected?) + :action #(let [props {:attributes #{attr} + :token token + :shape-ids shape-ids} + event (cond + all-selected? (-> (assoc props :attributes-to-remove attributes) + (wtc/apply-token)) + selected? (wtc/unapply-token props) + :else (-> (assoc props :on-update-shape on-update-shape) + (wtc/apply-token)))] + (st/emit! event))})) + attribute-labels)] + (concat [all-action] single-actions))) (defn generic-attribute-actions [attributes title {:keys [token selected-shapes]}] (let [{:keys [on-update-shape] :as p} (get wtc/token-types (:type token)) {:keys [selected-pred shape-ids]} (attribute-actions token selected-shapes attributes)] From 65dbafafb811bb5d7fd452862287fccf786fa0f8 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Tue, 23 Jul 2024 16:02:14 +0200 Subject: [PATCH 29/77] Add width/height applying --- .../ui/workspace/tokens/context_menu.cljs | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index e2d0842d1..bcf49eea0 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -25,8 +25,10 @@ [app.main.ui.workspace.tokens.token :as wtt] [app.util.dom :as dom] [app.util.timers :as timers] - [clojure.set :as set :refer [rename-keys]] + [beicon.v2.core :as rx] + [clojure.set :as set] [okulary.core :as l] + [potok.v2.core :as ptk] [rumext.v2 :as mf])) (def tokens-menu-ref @@ -179,14 +181,18 @@ :selected-shapes selected-shapes}))) (defn update-shape-dimensions [value shape-ids attributes] - (st/emit! (dwt/update-dimensions shape-ids (first attributes) value))) + (ptk/reify ::update-shape-dimensions + ptk/WatchEvent + (watch [_ _ _] + (rx/of + (when (:width attributes) (dwt/update-dimensions shape-ids :width value)) + (when (:height attributes) (dwt/update-dimensions shape-ids :height value)))))) (defn update-layout-sizing-limits [value shape-ids attributes] (st/emit! (dwsl/update-layout-child shape-ids {(first attributes) value}))) -(defn apply-sizing-token [{:keys [token-id token-type-props selected-shapes]} attributes] - (let [token (dt/get-token-data-from-token-id token-id) - updated-token-type-props (cond +(defn apply-sizing-token [{:keys [token token-type-props selected-shapes]} attributes] + (let [updated-token-type-props (cond (set/superset? #{:width :height} attributes) (assoc token-type-props :on-update-shape update-shape-dimensions @@ -388,6 +394,12 @@ (st/emit! event))})) attribute-labels)] (concat [all-action] single-actions))) + +(defn sizing-attribute-actions [context-data] + (concat + (all-or-sepearate-actions {:width "Width" :height "Height"} update-shape-dimensions context-data) + [:separator])) + (defn generic-attribute-actions [attributes title {:keys [token selected-shapes]}] (let [{:keys [on-update-shape] :as p} (get wtc/token-types (:type token)) {:keys [selected-pred shape-ids]} (attribute-actions token selected-shapes attributes)] @@ -407,6 +419,7 @@ (def shape-attribute-actions-map {:border-radius border-radius-attribute-actions :spacing spacing-attribute-actions + :sizing sizing-attribute-actions :rotation (partial generic-attribute-actions #{:rotation} "Rotation") :opacity (partial generic-attribute-actions #{:opacity} "Opacity") :stroke-width (partial generic-attribute-actions #{:stroke-width} "Stroke Width")}) From 62f7f8a74f2dea749004bfc941cdd82341c536bd Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Tue, 23 Jul 2024 16:35:07 +0200 Subject: [PATCH 30/77] Add sizing --- .../ui/workspace/tokens/context_menu.cljs | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index bcf49eea0..e935dd29d 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -189,7 +189,12 @@ (when (:height attributes) (dwt/update-dimensions shape-ids :height value)))))) (defn update-layout-sizing-limits [value shape-ids attributes] - (st/emit! (dwsl/update-layout-child shape-ids {(first attributes) value}))) + (let [props (-> {:layout-item-min-w value + :layout-item-min-h value + :layout-item-max-w value + :layout-item-max-h value} + (select-keys attributes))] + (dwsl/update-layout-child shape-ids props))) (defn apply-sizing-token [{:keys [token token-type-props selected-shapes]} attributes] (let [updated-token-type-props (cond @@ -397,8 +402,17 @@ (defn sizing-attribute-actions [context-data] (concat - (all-or-sepearate-actions {:width "Width" :height "Height"} update-shape-dimensions context-data) - [:separator])) + (all-or-sepearate-actions {:width "Width" + :height "Height"} + update-shape-dimensions context-data) + [:separator] + (all-or-sepearate-actions {:layout-item-min-w "Min Width" + :layout-item-min-h "Min Height"} + update-layout-sizing-limits context-data) + [:separator] + (all-or-sepearate-actions {:layout-item-max-w "Max Width" + :layout-item-max-h "Max Height"} + update-layout-sizing-limits context-data))) (defn generic-attribute-actions [attributes title {:keys [token selected-shapes]}] (let [{:keys [on-update-shape] :as p} (get wtc/token-types (:type token)) From aa75f308585ac93faf475693a954a1b1e5d9b3be Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Wed, 24 Jul 2024 09:19:31 +0200 Subject: [PATCH 31/77] Allow type overriding via prop --- .../app/main/ui/workspace/tokens/context_menu.cljs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index e935dd29d..5379591e7 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -436,10 +436,12 @@ :sizing sizing-attribute-actions :rotation (partial generic-attribute-actions #{:rotation} "Rotation") :opacity (partial generic-attribute-actions #{:opacity} "Opacity") - :stroke-width (partial generic-attribute-actions #{:stroke-width} "Stroke Width")}) + :stroke-width (partial generic-attribute-actions #{:stroke-width} "Stroke Width") + :dimensions (fn [_] + [{:title "Spacing" :submenu :spacing}])}) -(defn shape-attribute-actions [{:keys [token] :as context-data}] - (when-let [with-actions (get shape-attribute-actions-map (:type token))] +(defn shape-attribute-actions [{:keys [type token] :as context-data}] + (when-let [with-actions (get shape-attribute-actions-map (or type (:type token)))] (with-actions context-data))) (defn shape-attribute-actions* [{:keys [token selected-shapes] :as context-data}] @@ -513,7 +515,8 @@ :hidden-icon (not selected?))}]) :selected? selected?)) (when submenu - (let [submenu-entries (shape-attribute-actions (assoc context-data :token-type submenu))] + (let [submenu-entries (-> (assoc context-data :type submenu) + (generate-menu-entries))] (for [[index {:keys [title action selected?] :as sub-entry}] (d/enumerate submenu-entries)] (cond (= :separator sub-entry) [:& menu-separator] From 1776591fec331f989e7495568fd1066489d9b333 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Wed, 24 Jul 2024 09:42:09 +0200 Subject: [PATCH 32/77] Fix react index warning --- .../ui/workspace/tokens/context_menu.cljs | 47 ++++++++++--------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index 5379591e7..7ac6ec139 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -504,29 +504,30 @@ [context-data] (let [menu-entries (generate-menu-entries context-data)] (for [[index {:keys [title action selected? submenu] :as entry}] (d/enumerate menu-entries)] - (cond - (= :separator entry) [:& menu-separator] - :else - [:& menu-entry (cond-> {:key index - :title title} - (not submenu) (assoc :on-click action - ;; TODO: Allow selected items wihtout an icon for the context menu - :icon (mf/html [:div {:class (stl/css-case :empty-icon true - :hidden-icon (not selected?))}]) - :selected? selected?)) - (when submenu - (let [submenu-entries (-> (assoc context-data :type submenu) - (generate-menu-entries))] - (for [[index {:keys [title action selected?] :as sub-entry}] (d/enumerate submenu-entries)] - (cond - (= :separator sub-entry) [:& menu-separator] - :else - [:& menu-entry {:key index - :title title - :on-click action - :icon (mf/html [:div {:class (stl/css-case :empty-icon true - :hidden-icon (not selected?))}]) - :selected? selected?}]))))])))) + [:* {:key (str title " " index)} + (cond + (= :separator entry) [:& menu-separator] + :else + [:& menu-entry (cond-> {:title title} + (not submenu) (assoc :on-click action + ;; TODO: Allow selected items wihtout an icon for the context menu + :icon (mf/html [:div {:class (stl/css-case :empty-icon true + :hidden-icon (not selected?))}]) + :selected? selected?)) + (when submenu + (let [submenu-entries (-> (assoc context-data :type submenu) + (generate-menu-entries))] + (for [[index {:keys [title action selected?] :as sub-entry}] (d/enumerate submenu-entries)] + [:* {:key (str title " " index)} + (cond + (= :separator sub-entry) [:& menu-separator] + :else + [:& menu-entry {:key index + :title title + :on-click action + :icon (mf/html [:div {:class (stl/css-case :empty-icon true + :hidden-icon (not selected?))}]) + :selected? selected?}])])))])]))) (mf/defc token-context-menu [] From f20313e7f84e8c7a52d9758c8c466aeedc89d48a Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Wed, 24 Jul 2024 09:43:28 +0200 Subject: [PATCH 33/77] Add dimensions sub-menu --- .../ui/workspace/tokens/context_menu.cljs | 60 ++++++------------- 1 file changed, 17 insertions(+), 43 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index 7ac6ec139..df68326d1 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -431,54 +431,28 @@ attributes))) (def shape-attribute-actions-map - {:border-radius border-radius-attribute-actions - :spacing spacing-attribute-actions - :sizing sizing-attribute-actions - :rotation (partial generic-attribute-actions #{:rotation} "Rotation") - :opacity (partial generic-attribute-actions #{:opacity} "Opacity") - :stroke-width (partial generic-attribute-actions #{:stroke-width} "Stroke Width") - :dimensions (fn [_] - [{:title "Spacing" :submenu :spacing}])}) + (let [stroke-width (partial generic-attribute-actions #{:stroke-width} "Stroke Width")] + {:border-radius border-radius-attribute-actions + :spacing spacing-attribute-actions + :sizing sizing-attribute-actions + :rotation (partial generic-attribute-actions #{:rotation} "Rotation") + :opacity (partial generic-attribute-actions #{:opacity} "Opacity") + :stroke-width stroke-width + :dimensions (fn [context-data] + (concat + [{:title "Spacing" :submenu :spacing} + {:title "Sizing" :submenu :sizing} + :separator + {:title "Border Radius" :submenu :border-radius}] + (stroke-width context-data) + [:separator] + (generic-attribute-actions #{:x} "X" context-data) + (generic-attribute-actions #{:y} "Y" context-data)))})) (defn shape-attribute-actions [{:keys [type token] :as context-data}] (when-let [with-actions (get shape-attribute-actions-map (or type (:type token)))] (with-actions context-data))) -(defn shape-attribute-actions* [{:keys [token selected-shapes] :as context-data}] - (let [attributes->actions (fn [update-fn coll] - (for [{:keys [attributes] :as item} coll] - (cond - (= :separator item) item - :else - (let [selected? (wtt/shapes-token-applied? {:id token} selected-shapes attributes)] - (assoc item - :action #(update-fn context-data attributes) - :selected? selected?)))))] - (case (:type token) - :sizing (attributes->actions - apply-sizing-token - [{:title "All" :attributes #{:width :height :layout-item-min-w :layout-item-max-w :layout-item-min-h :layout-item-max-h}} - {:title "Width" :attributes #{:width}} - {:title "Height" :attributes #{:height}} - {:title "Min width" :attributes #{:layout-item-min-w}} - {:title "Max width" :attributes #{:layout-item-max-w}} - {:title "Min height" :attributes #{:layout-item-min-h}} - {:title "Max height" :attributes #{:layout-item-max-h}}]) - - :dimensions (attributes->actions - apply-dimensions-token - [{:title "Spacing" :submenu :spacing} - {:title "Sizing" :submenu :sizing} - {:title "Border Radius" :submenu :border-radius} - {:title "Border Width" :attributes #{:stroke-width}} - {:title "x" :attributes #{:x}} - {:title "y" :attributes #{:y}}]) - ;;TODO: Background blur {:title "Background blur" :attributes #{:width}}]) - - - - []))) - (defn generate-menu-entries [{:keys [token selected-shapes] :as context-data}] (let [{:keys [modal]} (get wtc/token-types (:type token)) attribute-actions (when (seq selected-shapes) From 81c83f9dd49a6c2369251ea120c4ce1019a3fc9e Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Wed, 24 Jul 2024 10:55:22 +0200 Subject: [PATCH 34/77] Recurse tree instead of repetition --- .../ui/workspace/tokens/context_menu.cljs | 56 +++++++------------ 1 file changed, 21 insertions(+), 35 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index df68326d1..ccbf352df 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -106,15 +106,14 @@ [:span {:key (dm/str shortcut "-" idx) :class (stl/css :shortcut-key)} sc])]) - (when (> (count children) 1) - [:span {:class (stl/css :submenu-icon)} i/arrow]) - - (when (> (count children) 1) - [:ul {:class (stl/css :token-context-submenu) - :ref submenu-ref - :style {:display "none" :left 235} - :on-context-menu prevent-default} - children])]))) + (when children + [:* + [:span {:class (stl/css :submenu-icon)} i/arrow] + [:ul {:class (stl/css :token-context-submenu) + :ref submenu-ref + :style {:display "none" :left 235} + :on-context-menu prevent-default} + children]])]))) (mf/defc menu-separator [] @@ -474,34 +473,21 @@ (when attribute-actions [:separator]) default-actions))) -(mf/defc token-pill-context-menu +(mf/defc context-menu-tree [context-data] - (let [menu-entries (generate-menu-entries context-data)] - (for [[index {:keys [title action selected? submenu] :as entry}] (d/enumerate menu-entries)] + (let [entries (generate-menu-entries context-data)] + (for [[index {:keys [title action selected? submenu] :as entry}] (d/enumerate entries)] [:* {:key (str title " " index)} (cond (= :separator entry) [:& menu-separator] - :else - [:& menu-entry (cond-> {:title title} - (not submenu) (assoc :on-click action - ;; TODO: Allow selected items wihtout an icon for the context menu - :icon (mf/html [:div {:class (stl/css-case :empty-icon true - :hidden-icon (not selected?))}]) - :selected? selected?)) - (when submenu - (let [submenu-entries (-> (assoc context-data :type submenu) - (generate-menu-entries))] - (for [[index {:keys [title action selected?] :as sub-entry}] (d/enumerate submenu-entries)] - [:* {:key (str title " " index)} - (cond - (= :separator sub-entry) [:& menu-separator] - :else - [:& menu-entry {:key index - :title title - :on-click action - :icon (mf/html [:div {:class (stl/css-case :empty-icon true - :hidden-icon (not selected?))}]) - :selected? selected?}])])))])]))) + submenu [:& menu-entry {:title title} + [:& context-menu-tree (assoc context-data :type submenu)]] + :else [:& menu-entry + {:title title + :on-click action + :icon (mf/html [:div {:class (stl/css-case :empty-icon true + :hidden-icon (not selected?))}]) + :selected? selected?}])]))) (mf/defc token-context-menu [] @@ -534,5 +520,5 @@ :on-context-menu prevent-default} (when (= :token (:type mdata)) [:ul {:class (stl/css :context-list)} - [:& token-pill-context-menu {:token token - :selected-shapes selected-shapes}]])]])) + [:& context-menu-tree {:token token + :selected-shapes selected-shapes}]])]])) From 08cc777096ea6e396cea34b04d5666d9f8c78e22 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Wed, 24 Jul 2024 10:58:34 +0200 Subject: [PATCH 35/77] Restructure --- .../ui/workspace/tokens/context_menu.cljs | 180 +++++++++--------- 1 file changed, 93 insertions(+), 87 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index ccbf352df..b691a2d2c 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -31,93 +31,7 @@ [potok.v2.core :as ptk] [rumext.v2 :as mf])) -(def tokens-menu-ref - (l/derived :token-context-menu refs/workspace-local)) - -(defn- prevent-default - [event] - (dom/prevent-default event) - (dom/stop-propagation event)) - -(mf/defc menu-entry - {::mf/props :obj} - [{:keys [title shortcut on-click on-pointer-enter on-pointer-leave - on-unmount children selected? icon disabled value]}] - (let [submenu-ref (mf/use-ref nil) - hovering? (mf/use-ref false) - on-pointer-enter - (mf/use-callback - (fn [] - (mf/set-ref-val! hovering? true) - (let [submenu-node (mf/ref-val submenu-ref)] - (when (some? submenu-node) - (dom/set-css-property! submenu-node "display" "block"))) - (when on-pointer-enter (on-pointer-enter)))) - - on-pointer-leave - (mf/use-callback - (fn [] - (mf/set-ref-val! hovering? false) - (let [submenu-node (mf/ref-val submenu-ref)] - (when (some? submenu-node) - (timers/schedule - 50 - #(when-not (mf/ref-val hovering?) - (dom/set-css-property! submenu-node "display" "none"))))) - (when on-pointer-leave (on-pointer-leave)))) - - set-dom-node - (mf/use-callback - (fn [dom] - (let [submenu-node (mf/ref-val submenu-ref)] - (when (and (some? dom) (some? submenu-node)) - (dom/set-css-property! submenu-node "top" (str (.-offsetTop dom) "px"))))))] - - (mf/use-effect - (mf/deps on-unmount) - (constantly on-unmount)) - - (if icon - [:li {:class (stl/css :icon-menu-item) - :disabled disabled - :data-value value - :ref set-dom-node - :on-click on-click - :on-pointer-enter on-pointer-enter - :on-pointer-leave on-pointer-leave} - [:span - {:class (stl/css :icon-wrapper)} - (if selected? [:span {:class (stl/css :selected-icon)} - i/tick] - [:span {:class (stl/css :selected-icon)}]) - [:span {:class (stl/css :shape-icon)} icon]] - [:span {:class (stl/css :title)} title]] - [:li {:class (stl/css :context-menu-item) - :disabled disabled - :ref set-dom-node - :data-value value - :on-click on-click - :on-pointer-enter on-pointer-enter - :on-pointer-leave on-pointer-leave} - [:span {:class (stl/css :title)} title] - (when shortcut - [:span {:class (stl/css :shortcut)} - (for [[idx sc] (d/enumerate (scd/split-sc shortcut))] - [:span {:key (dm/str shortcut "-" idx) - :class (stl/css :shortcut-key)} sc])]) - - (when children - [:* - [:span {:class (stl/css :submenu-icon)} i/arrow] - [:ul {:class (stl/css :token-context-submenu) - :ref submenu-ref - :style {:display "none" :left 235} - :on-context-menu prevent-default} - children]])]))) - -(mf/defc menu-separator - [] - [:li {:class (stl/css :separator)}]) +;; Events ---------------------------------------------------------------------- (defn update-shape-radius-single-corner [value shape-ids attribute] (st/emit! @@ -219,6 +133,8 @@ :token-type-props token-type-props :selected-shapes selected-shapes}))) +;; Actions --------------------------------------------------------------------- + (defn attribute-actions [token selected-shapes attributes] (let [ids-by-attributes (wtt/shapes-ids-by-applied-attributes token selected-shapes attributes) shape-ids (into #{} (map :id selected-shapes))] @@ -473,6 +389,96 @@ (when attribute-actions [:separator]) default-actions))) +;; Components ------------------------------------------------------------------ + +(def tokens-menu-ref + (l/derived :token-context-menu refs/workspace-local)) + +(defn- prevent-default + [event] + (dom/prevent-default event) + (dom/stop-propagation event)) + +(mf/defc menu-entry + {::mf/props :obj} + [{:keys [title shortcut on-click on-pointer-enter on-pointer-leave + on-unmount children selected? icon disabled value]}] + (let [submenu-ref (mf/use-ref nil) + hovering? (mf/use-ref false) + on-pointer-enter + (mf/use-callback + (fn [] + (mf/set-ref-val! hovering? true) + (let [submenu-node (mf/ref-val submenu-ref)] + (when (some? submenu-node) + (dom/set-css-property! submenu-node "display" "block"))) + (when on-pointer-enter (on-pointer-enter)))) + + on-pointer-leave + (mf/use-callback + (fn [] + (mf/set-ref-val! hovering? false) + (let [submenu-node (mf/ref-val submenu-ref)] + (when (some? submenu-node) + (timers/schedule + 50 + #(when-not (mf/ref-val hovering?) + (dom/set-css-property! submenu-node "display" "none"))))) + (when on-pointer-leave (on-pointer-leave)))) + + set-dom-node + (mf/use-callback + (fn [dom] + (let [submenu-node (mf/ref-val submenu-ref)] + (when (and (some? dom) (some? submenu-node)) + (dom/set-css-property! submenu-node "top" (str (.-offsetTop dom) "px"))))))] + + (mf/use-effect + (mf/deps on-unmount) + (constantly on-unmount)) + + (if icon + [:li {:class (stl/css :icon-menu-item) + :disabled disabled + :data-value value + :ref set-dom-node + :on-click on-click + :on-pointer-enter on-pointer-enter + :on-pointer-leave on-pointer-leave} + [:span + {:class (stl/css :icon-wrapper)} + (if selected? [:span {:class (stl/css :selected-icon)} + i/tick] + [:span {:class (stl/css :selected-icon)}]) + [:span {:class (stl/css :shape-icon)} icon]] + [:span {:class (stl/css :title)} title]] + [:li {:class (stl/css :context-menu-item) + :disabled disabled + :ref set-dom-node + :data-value value + :on-click on-click + :on-pointer-enter on-pointer-enter + :on-pointer-leave on-pointer-leave} + [:span {:class (stl/css :title)} title] + (when shortcut + [:span {:class (stl/css :shortcut)} + (for [[idx sc] (d/enumerate (scd/split-sc shortcut))] + [:span {:key (dm/str shortcut "-" idx) + :class (stl/css :shortcut-key)} sc])]) + + (when children + [:* + [:span {:class (stl/css :submenu-icon)} i/arrow] + [:ul {:class (stl/css :token-context-submenu) + :ref submenu-ref + :style {:display "none" :left 235} + :on-context-menu prevent-default} + children]])]))) + +(mf/defc menu-separator + [] + [:li {:class (stl/css :separator)}]) + (mf/defc context-menu-tree [context-data] (let [entries (generate-menu-entries context-data)] From fbd2ab833d76856e2e51a7dc1fbbb36caba381ed Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Wed, 24 Jul 2024 10:59:26 +0200 Subject: [PATCH 36/77] Inline separator --- frontend/src/app/main/ui/workspace/tokens/context_menu.cljs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index b691a2d2c..8349b2acf 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -475,17 +475,13 @@ :on-context-menu prevent-default} children]])]))) -(mf/defc menu-separator - [] - [:li {:class (stl/css :separator)}]) - (mf/defc context-menu-tree [context-data] (let [entries (generate-menu-entries context-data)] (for [[index {:keys [title action selected? submenu] :as entry}] (d/enumerate entries)] [:* {:key (str title " " index)} (cond - (= :separator entry) [:& menu-separator] + (= :separator entry) [:li {:class (stl/css :separator)}] submenu [:& menu-entry {:title title} [:& context-menu-tree (assoc context-data :type submenu)]] :else [:& menu-entry From 113fc9891ba7fd14d0861279b91e789465f6a266 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Wed, 24 Jul 2024 13:29:26 +0200 Subject: [PATCH 37/77] Simplify component & css --- .../ui/workspace/tokens/context_menu.cljs | 111 +++++++----------- .../ui/workspace/tokens/context_menu.scss | 90 ++++---------- 2 files changed, 65 insertions(+), 136 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index 8349b2acf..022da6dd5 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -372,18 +372,21 @@ (let [{:keys [modal]} (get wtc/token-types (:type token)) attribute-actions (when (seq selected-shapes) (shape-attribute-actions context-data)) - default-actions [{:title "Delete Token" :action #(st/emit! (dt/delete-token (:id token)))} - {:title "Duplicate Token" :action #(st/emit! (dt/duplicate-token (:id token)))} - {:title "Edit Token" :action (fn [event] - (let [{:keys [key fields]} modal - token (dt/get-token-data-from-token-id (:id token))] - (st/emit! dt/hide-token-context-menu) - (dom/stop-propagation event) - (modal/show! key {:x (.-clientX ^js event) - :y (.-clientY ^js event) - :position :right - :fields fields - :token token})))}]] + default-actions [{:title "Delete Token" + :action #(st/emit! (dt/delete-token (:id token)))} + {:title "Duplicate Token" + :action #(st/emit! (dt/duplicate-token (:id token)))} + {:title "Edit Token" + :action (fn [event] + (let [{:keys [key fields]} modal + token (dt/get-token-data-from-token-id (:id token))] + (st/emit! dt/hide-token-context-menu) + (dom/stop-propagation event) + (modal/show! key {:x (.-clientX ^js event) + :y (.-clientY ^js event) + :position :right + :fields fields + :token token})))}]] (concat attribute-actions (when attribute-actions [:separator]) @@ -401,79 +404,47 @@ (mf/defc menu-entry {::mf/props :obj} - [{:keys [title shortcut on-click on-pointer-enter on-pointer-leave - on-unmount children selected? icon disabled value]}] + [{:keys [title value on-click selected? children]}] (let [submenu-ref (mf/use-ref nil) hovering? (mf/use-ref false) on-pointer-enter (mf/use-callback (fn [] (mf/set-ref-val! hovering? true) - (let [submenu-node (mf/ref-val submenu-ref)] - (when (some? submenu-node) - (dom/set-css-property! submenu-node "display" "block"))) - (when on-pointer-enter (on-pointer-enter)))) - + (when-let [submenu-node (mf/ref-val submenu-ref)] + (dom/set-css-property! submenu-node "display" "block")))) on-pointer-leave (mf/use-callback (fn [] (mf/set-ref-val! hovering? false) - (let [submenu-node (mf/ref-val submenu-ref)] - (when (some? submenu-node) - (timers/schedule - 50 - #(when-not (mf/ref-val hovering?) - (dom/set-css-property! submenu-node "display" "none"))))) - (when on-pointer-leave (on-pointer-leave)))) - + (when-let [submenu-node (mf/ref-val submenu-ref)] + (timers/schedule 50 #(when-not (mf/ref-val hovering?) + (dom/set-css-property! submenu-node "display" "none")))))) set-dom-node (mf/use-callback (fn [dom] (let [submenu-node (mf/ref-val submenu-ref)] (when (and (some? dom) (some? submenu-node)) (dom/set-css-property! submenu-node "top" (str (.-offsetTop dom) "px"))))))] - - (mf/use-effect - (mf/deps on-unmount) - (constantly on-unmount)) - - (if icon - [:li {:class (stl/css :icon-menu-item) - :disabled disabled - :data-value value - :ref set-dom-node - :on-click on-click - :on-pointer-enter on-pointer-enter - :on-pointer-leave on-pointer-leave} - [:span - {:class (stl/css :icon-wrapper)} - (if selected? [:span {:class (stl/css :selected-icon)} - i/tick] - [:span {:class (stl/css :selected-icon)}]) - [:span {:class (stl/css :shape-icon)} icon]] - [:span {:class (stl/css :title)} title]] - [:li {:class (stl/css :context-menu-item) - :disabled disabled - :ref set-dom-node - :data-value value - :on-click on-click - :on-pointer-enter on-pointer-enter - :on-pointer-leave on-pointer-leave} - [:span {:class (stl/css :title)} title] - (when shortcut - [:span {:class (stl/css :shortcut)} - (for [[idx sc] (d/enumerate (scd/split-sc shortcut))] - [:span {:key (dm/str shortcut "-" idx) - :class (stl/css :shortcut-key)} sc])]) - - (when children - [:* - [:span {:class (stl/css :submenu-icon)} i/arrow] - [:ul {:class (stl/css :token-context-submenu) - :ref submenu-ref - :style {:display "none" :left 235} - :on-context-menu prevent-default} - children]])]))) + [:li + {:class (stl/css :context-menu-item) + :ref set-dom-node + :data-value value + :on-click on-click + :on-pointer-enter on-pointer-enter + :on-pointer-leave on-pointer-leave} + (when selected? + [:span {:class (stl/css :icon-wrapper)} + [:span {:class (stl/css :selected-icon)} i/tick]]) + [:span {:class (stl/css :title)} title] + (when children + [:* + [:span {:class (stl/css :submenu-icon)} i/arrow] + [:ul {:class (stl/css :token-context-submenu) + :ref submenu-ref + :style {:display "none" :left 235} + :on-context-menu prevent-default} + children]])])) (mf/defc context-menu-tree [context-data] @@ -487,8 +458,6 @@ :else [:& menu-entry {:title title :on-click action - :icon (mf/html [:div {:class (stl/css-case :empty-icon true - :hidden-icon (not selected?))}]) :selected? selected?}])]))) (mf/defc token-context-menu diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.scss b/frontend/src/app/main/ui/workspace/tokens/context_menu.scss index f2798ea7a..28e3527af 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.scss +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.scss @@ -46,7 +46,6 @@ .context-menu-item { display: flex; align-items: center; - justify-content: space-between; height: $s-28; width: 100%; padding: $s-6; @@ -54,27 +53,34 @@ cursor: pointer; .title { + flex-grow: 1; @include bodySmallTypography; color: var(--menu-foreground-color); margin-left: calc(($s-32 + $s-28) / 2); } - .shortcut { - @include flexCenter; - gap: $s-2; - color: var(--menu-shortcut-foreground-color); - .shortcut-key { - @include bodySmallTypography; - @include flexCenter; - height: $s-20; - padding: $s-2 $s-6; - border-radius: $br-6; - background-color: var(--menu-shortcut-background-color); + + .icon-wrapper { + display: grid; + grid-template-columns: 1fr 1fr; + } + + .icon-wrapper + .title { + margin-left: $s-6; + } + + .selected-icon { + svg { + @extend .button-icon-small; + stroke: var(--menu-foreground-color); } } - .submenu-icon svg { - @extend .button-icon-small; - stroke: var(--menu-foreground-color); + .submenu-icon { + margin-left: $s-2; + svg { + @extend .button-icon-small; + stroke: var(--menu-foreground-color); + } } &:hover { @@ -86,60 +92,14 @@ color: var(--menu-shortcut-foreground-color-hover); } } + &:focus { border: 1px solid var(--menu-border-color-focus); background-color: var(--menu-background-color-focus); } -} -.icon-menu-item { - display: flex; - justify-content: flex-start; - align-items: center; - height: $s-28; - padding: $s-6; - border-radius: $br-8; - &:hover { - background-color: var(--menu-background-color-hover); - } - - span.title { - margin-left: $s-6; - } - - .selected-icon { - svg { - @extend .button-icon-small; - stroke: var(--menu-foreground-color); - } - } - - .shape-icon { - margin-left: $s-2; - svg { - @extend .button-icon-small; - stroke: var(--menu-foreground-color); - } - } - - .icon-wrapper { - display: grid; - grid-template-columns: 1fr 1fr; - margin: 0; + &[disabled] { + pointer-events: none; + opacity: 0.6; } } - -.icon-menu-item[disabled], -.context-menu-item[disabled] { - pointer-events: none; - opacity: 0.6; -} - -// TODO: Allow selected items wihtout an icon for the context menu -.empty-icon { - width: 0; - height: 0; -} -.hidden-icon { - width: 11px; -} From cbd5d42069cd441d61d0b5f3d47e0b4785f106e5 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Wed, 24 Jul 2024 13:36:38 +0200 Subject: [PATCH 38/77] Simplify --- .../app/main/ui/workspace/tokens/context_menu.cljs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index 022da6dd5..7c2028160 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -364,14 +364,11 @@ (generic-attribute-actions #{:x} "X" context-data) (generic-attribute-actions #{:y} "Y" context-data)))})) -(defn shape-attribute-actions [{:keys [type token] :as context-data}] - (when-let [with-actions (get shape-attribute-actions-map (or type (:type token)))] - (with-actions context-data))) - -(defn generate-menu-entries [{:keys [token selected-shapes] :as context-data}] +(defn generate-menu-entries [{:keys [type token] :as context-data}] (let [{:keys [modal]} (get wtc/token-types (:type token)) - attribute-actions (when (seq selected-shapes) - (shape-attribute-actions context-data)) + with-actions (get shape-attribute-actions-map (or type (:type token))) + attribute-actions (when with-actions + (with-actions context-data)) default-actions [{:title "Delete Token" :action #(st/emit! (dt/delete-token (:id token)))} {:title "Duplicate Token" From 335808bf0333d946b3c08caf8524447588c4110d Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Wed, 24 Jul 2024 13:41:00 +0200 Subject: [PATCH 39/77] Remove unneeded actions --- .../ui/workspace/tokens/context_menu.cljs | 44 ------------------- 1 file changed, 44 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index 7c2028160..f55489f80 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -33,26 +33,6 @@ ;; Events ---------------------------------------------------------------------- -(defn update-shape-radius-single-corner [value shape-ids attribute] - (st/emit! - (dch/update-shapes shape-ids - (fn [shape] - (when (ctsr/has-radius? shape) - (ctsr/set-radius-4 shape (first attribute) value))) - {:reg-objects? true - :attrs [:rx :ry :r1 :r2 :r3 :r4]}))) - -(defn apply-border-radius-token [{:keys [token-id token-type-props selected-shapes]} attributes] - (let [token (dt/get-token-data-from-token-id token-id) - updated-token-type-props (if (set/superset? #{:r1 :r2 :r3 :r4} attributes) - (assoc token-type-props - :on-update-shape update-shape-radius-single-corner - :attributes attributes) - token-type-props)] - (wtc/on-apply-token {:token token - :token-type-props updated-token-type-props - :selected-shapes selected-shapes}))) - (defn update-layout-spacing [value shape-ids attributes] (if-let [layout-gap (cond (:row-gap attributes) {:row-gap value} @@ -109,30 +89,6 @@ (select-keys attributes))] (dwsl/update-layout-child shape-ids props))) -(defn apply-sizing-token [{:keys [token token-type-props selected-shapes]} attributes] - (let [updated-token-type-props (cond - (set/superset? #{:width :height} attributes) - (assoc token-type-props - :on-update-shape update-shape-dimensions - :attributes attributes) - - (set/superset? #{:layout-item-min-w :layout-item-max-w - :layout-item-min-h :layout-item-max-h} attributes) - (assoc token-type-props - :on-update-shape update-layout-sizing-limits - :attributes attributes) - - :else token-type-props)] - (wtc/on-apply-token {:token token - :token-type-props updated-token-type-props - :selected-shapes selected-shapes}))) - -(defn apply-rotation-opacity-stroke-token [{:keys [token-id token-type-props selected-shapes]} attributes] - (let [token (dt/get-token-data-from-token-id token-id)] - (wtc/on-apply-token {:token token - :token-type-props token-type-props - :selected-shapes selected-shapes}))) - ;; Actions --------------------------------------------------------------------- (defn attribute-actions [token selected-shapes attributes] From c11c1e0c03ba895a57f28e196fa53dcf5e069e0c Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Wed, 24 Jul 2024 13:41:08 +0200 Subject: [PATCH 40/77] Use all-or-sepearate-actions for gap --- .../ui/workspace/tokens/context_menu.cljs | 89 +++++++------------ 1 file changed, 30 insertions(+), 59 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index f55489f80..121a8674c 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -137,36 +137,33 @@ :gap {:column-gap "Column Gap" :row-gap "Row Gap"}}) -(defn gap-attribute-actions [{:keys [token selected-shapes]}] - (let [on-update-shape update-layout-spacing - gap-attrs (:gap spacing) - all-gap-attrs (into #{} (keys gap-attrs)) - {:keys [all-selected? selected-pred shape-ids]} (attribute-actions token selected-shapes all-gap-attrs) - all-gap (let [props {:attributes all-gap-attrs - :token token - :shape-ids shape-ids}] - [{:title "All" - :selected? all-selected? - :action #(if all-selected? - (st/emit! (wtc/unapply-token props)) - (st/emit! (wtc/apply-token (assoc props :on-update-shape on-update-shape))))}]) - single-gap (->> gap-attrs - (map (fn [[attr title]] - (let [selected? (selected-pred attr)] - {:title title - :selected? (and (not all-selected?) selected?) - :action #(let [props {:attributes #{attr} - :token token - :shape-ids shape-ids} - event (cond - all-selected? (-> (assoc props :attributes-to-remove #{:row-gap :column-gap}) - (wtc/apply-token)) - selected? (wtc/unapply-token props) - :else (-> (assoc props :on-update-shape on-update-shape) - (wtc/apply-token)))] - (st/emit! event))}))) - (into))] - (concat all-gap single-gap))) +(defn all-or-sepearate-actions [attribute-labels on-update-shape {:keys [token selected-shapes]}] + (let [attributes (set (keys attribute-labels)) + {:keys [all-selected? selected-pred shape-ids]} (attribute-actions token selected-shapes attributes) + all-action (let [props {:attributes attributes + :token token + :shape-ids shape-ids}] + {:title "All" + :selected? all-selected? + :action #(if all-selected? + (st/emit! (wtc/unapply-token props)) + (st/emit! (wtc/apply-token (assoc props :on-update-shape on-update-shape))))}) + single-actions (map (fn [[attr title]] + (let [selected? (selected-pred attr)] + {:title title + :selected? (and (not all-selected?) selected?) + :action #(let [props {:attributes #{attr} + :token token + :shape-ids shape-ids} + event (cond + all-selected? (-> (assoc props :attributes-to-remove attributes) + (wtc/apply-token)) + selected? (wtc/unapply-token props) + :else (-> (assoc props :on-update-shape on-update-shape) + (wtc/apply-token)))] + (st/emit! event))})) + attribute-labels)] + (concat [all-action] single-actions))) (defn spacing-attribute-actions [{:keys [token selected-shapes] :as context-data}] (let [on-update-shape (fn [resolved-value shape-ids attrs] @@ -237,40 +234,14 @@ :else (-> (assoc props :on-update-shape on-update-shape) (wtc/apply-token)))] (st/emit! event))})))) - gap-items (gap-attribute-actions context-data)] + gap-items (all-or-sepearate-actions {:column-gap "Column Gap" + :row-gap "Row Gap"} + update-layout-spacing context-data)] (concat padding-items single-padding-items [:separator] gap-items))) -(defn all-or-sepearate-actions [attribute-labels on-update-shape {:keys [token selected-shapes]}] - (let [attributes (set (keys attribute-labels)) - {:keys [all-selected? selected-pred shape-ids]} (attribute-actions token selected-shapes attributes) - all-action (let [props {:attributes attributes - :token token - :shape-ids shape-ids}] - {:title "All" - :selected? all-selected? - :action #(if all-selected? - (st/emit! (wtc/unapply-token props)) - (st/emit! (wtc/apply-token (assoc props :on-update-shape on-update-shape))))}) - single-actions (map (fn [[attr title]] - (let [selected? (selected-pred attr)] - {:title title - :selected? (and (not all-selected?) selected?) - :action #(let [props {:attributes #{attr} - :token token - :shape-ids shape-ids} - event (cond - all-selected? (-> (assoc props :attributes-to-remove attributes) - (wtc/apply-token)) - selected? (wtc/unapply-token props) - :else (-> (assoc props :on-update-shape on-update-shape) - (wtc/apply-token)))] - (st/emit! event))})) - attribute-labels)] - (concat [all-action] single-actions))) - (defn sizing-attribute-actions [context-data] (concat (all-or-sepearate-actions {:width "Width" From e6889fc92e48e01c3127bd676c7d8d12d9893d9a Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Wed, 24 Jul 2024 13:41:32 +0200 Subject: [PATCH 41/77] Fix typo --- frontend/src/app/main/ui/workspace/tokens/context_menu.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index 121a8674c..cca998045 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -52,7 +52,7 @@ (defn update-shape-position [value shape-ids attributes] (doseq [shape-id shape-ids] - (st/emit! (dw/update-position shape-id {(first attributes) value})))) + (st/emit! (dwt/update-position shape-id {(first attributes) value})))) (defn apply-dimensions-token [{:keys [token-id token-type-props selected-shapes]} attributes] (let [token (dt/get-token-data-from-token-id token-id) From 893e533afee381720b5a2602c50684dca75ac58e Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Wed, 24 Jul 2024 14:01:56 +0200 Subject: [PATCH 42/77] Cleanup --- frontend/src/app/main/ui/workspace/tokens/context_menu.cljs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index cca998045..ffe9edf53 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -8,13 +8,8 @@ (:require-macros [app.main.style :as stl]) (:require [app.common.data :as d] - [app.common.data.macros :as dm] - [app.common.types.shape.radius :as ctsr] [app.main.data.modal :as modal] - [app.main.data.shortcuts :as scd] [app.main.data.tokens :as dt] - [app.main.data.workspace :as dw] - [app.main.data.workspace.changes :as dch] [app.main.data.workspace.shape-layout :as dwsl] [app.main.data.workspace.transforms :as dwt] [app.main.refs :as refs] From 38499e2f1f8ca7e52adcb73e963e8922b826476e Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Wed, 24 Jul 2024 14:07:29 +0200 Subject: [PATCH 43/77] Fix properties --- frontend/src/app/main/ui/workspace/tokens/context_menu.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index ffe9edf53..b7cd23d8a 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -98,8 +98,8 @@ {:keys [all-selected? selected-pred shape-ids]} (attribute-actions token selected-shapes all-attributes) single-attributes (->> {:r1 "Top Left" :r2 "Top Right" - :r3 "Bottom Left" - :r4 "Bottom Right"} + :r4 "Bottom Left" + :r3 "Bottom Right"} (map (fn [[attr title]] (let [selected? (selected-pred attr)] {:title title From 871402bd84218f1b325c61461fb4b87d2213654f Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Wed, 24 Jul 2024 14:07:42 +0200 Subject: [PATCH 44/77] Fix overriding of existing radius-4 --- frontend/src/app/main/ui/workspace/tokens/core.cljs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/core.cljs b/frontend/src/app/main/ui/workspace/tokens/core.cljs index 55f18bdf2..f92d73a67 100644 --- a/frontend/src/app/main/ui/workspace/tokens/core.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/core.cljs @@ -24,8 +24,7 @@ [beicon.v2.core :as rx] [cuerdas.core :as str] [potok.v2.core :as ptk] - [promesa.core :as p] - [cljs.pprint :as pprint])) + [promesa.core :as p])) ;; Helpers --------------------------------------------------------------------- @@ -51,11 +50,13 @@ ;; Shape Update Functions ------------------------------------------------------ -(defn update-shape-radius-single-corner [value shape-ids attribute] +(defn update-shape-radius-single-corner [value shape-ids attributes] (dch/update-shapes shape-ids (fn [shape] (when (ctsr/has-radius? shape) - (ctsr/set-radius-4 shape (first attribute) value))) + (cond-> shape + (:rx shape) (ctsr/switch-to-radius-4) + :always (ctsr/set-radius-4 (first attributes) value)))) {:reg-objects? true :attrs [:rx :ry :r1 :r2 :r3 :r4]})) From 46250003d3c2849852e9752001ae2fd398a176eb Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Wed, 24 Jul 2024 14:17:43 +0200 Subject: [PATCH 45/77] Reuse all-or-sepearate-actions for border-radius --- .../ui/workspace/tokens/context_menu.cljs | 73 +++++++------------ 1 file changed, 26 insertions(+), 47 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index b7cd23d8a..b5db426b6 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -93,37 +93,6 @@ :shape-ids shape-ids :selected-pred #(seq (% ids-by-attributes))})) -(defn border-radius-attribute-actions [{:keys [token selected-shapes]}] - (let [all-attributes #{:r1 :r2 :r3 :r4} - {:keys [all-selected? selected-pred shape-ids]} (attribute-actions token selected-shapes all-attributes) - single-attributes (->> {:r1 "Top Left" - :r2 "Top Right" - :r4 "Bottom Left" - :r3 "Bottom Right"} - (map (fn [[attr title]] - (let [selected? (selected-pred attr)] - {:title title - :selected? (and (not all-selected?) selected?) - :action #(let [props {:attributes #{attr} - :token token - :shape-ids shape-ids} - event (cond - all-selected? (-> (assoc props :attributes-to-remove #{:r1 :r2 :r3 :r4 :rx :ry}) - (wtc/apply-token)) - selected? (wtc/unapply-token props) - :else (-> (assoc props :on-update-shape wtc/update-shape-radius-single-corner) - (wtc/apply-token)))] - (st/emit! event))})))) - all-attribute (let [props {:attributes all-attributes - :token token - :shape-ids shape-ids}] - {:title "All" - :selected? all-selected? - :action #(if all-selected? - (st/emit! (wtc/unapply-token props)) - (st/emit! (wtc/apply-token (assoc props :on-update-shape wtc/update-shape-radius-all))))})] - (concat [all-attribute] single-attributes))) - (def spacing {:padding {:p1 "Top" :p2 "Right" @@ -132,7 +101,8 @@ :gap {:column-gap "Column Gap" :row-gap "Row Gap"}}) -(defn all-or-sepearate-actions [attribute-labels on-update-shape {:keys [token selected-shapes]}] +(defn all-or-sepearate-actions [{:keys [attribute-labels on-update-shape-all on-update-shape]} + {:keys [token selected-shapes]}] (let [attributes (set (keys attribute-labels)) {:keys [all-selected? selected-pred shape-ids]} (attribute-actions token selected-shapes attributes) all-action (let [props {:attributes attributes @@ -142,7 +112,7 @@ :selected? all-selected? :action #(if all-selected? (st/emit! (wtc/unapply-token props)) - (st/emit! (wtc/apply-token (assoc props :on-update-shape on-update-shape))))}) + (st/emit! (wtc/apply-token (assoc props :on-update-shape (or on-update-shape-all on-update-shape)))))}) single-actions (map (fn [[attr title]] (let [selected? (selected-pred attr)] {:title title @@ -229,9 +199,10 @@ :else (-> (assoc props :on-update-shape on-update-shape) (wtc/apply-token)))] (st/emit! event))})))) - gap-items (all-or-sepearate-actions {:column-gap "Column Gap" - :row-gap "Row Gap"} - update-layout-spacing context-data)] + gap-items (all-or-sepearate-actions {:attribute-labels {:column-gap "Column Gap" + :row-gap "Row Gap"} + :on-update-shape update-layout-spacing} + context-data)] (concat padding-items single-padding-items [:separator] @@ -239,20 +210,23 @@ (defn sizing-attribute-actions [context-data] (concat - (all-or-sepearate-actions {:width "Width" - :height "Height"} - update-shape-dimensions context-data) + (all-or-sepearate-actions {:attribute-labels {:width "Width" + :height "Height"} + :on-update-shape update-shape-dimensions} + context-data) [:separator] - (all-or-sepearate-actions {:layout-item-min-w "Min Width" - :layout-item-min-h "Min Height"} - update-layout-sizing-limits context-data) + (all-or-sepearate-actions {:attribute-labels {:layout-item-min-w "Min Width" + :layout-item-min-h "Min Height"} + :on-update-shape update-layout-sizing-limits} + context-data) [:separator] - (all-or-sepearate-actions {:layout-item-max-w "Max Width" - :layout-item-max-h "Max Height"} - update-layout-sizing-limits context-data))) + (all-or-sepearate-actions {:attribute-labels {:layout-item-max-w "Max Width" + :layout-item-max-h "Max Height"} + :on-update-shape update-layout-sizing-limits} + context-data))) (defn generic-attribute-actions [attributes title {:keys [token selected-shapes]}] - (let [{:keys [on-update-shape] :as p} (get wtc/token-types (:type token)) + (let [{:keys [on-update-shape]} (get wtc/token-types (:type token)) {:keys [selected-pred shape-ids]} (attribute-actions token selected-shapes attributes)] (map (fn [attribute] (let [selected? (selected-pred attribute) @@ -269,7 +243,12 @@ (def shape-attribute-actions-map (let [stroke-width (partial generic-attribute-actions #{:stroke-width} "Stroke Width")] - {:border-radius border-radius-attribute-actions + {:border-radius (partial all-or-sepearate-actions {:attribute-labels {:r1 "Top Left" + :r2 "Top Right" + :r4 "Bottom Left" + :r3 "Bottom Right"} + :on-update-shape-all wtc/update-shape-radius-all + :on-update-shape wtc/update-shape-radius-single-corner}) :spacing spacing-attribute-actions :sizing sizing-attribute-actions :rotation (partial generic-attribute-actions #{:rotation} "Rotation") From 310033fd755b341c3579bf43bca89f603ffa0db2 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Wed, 24 Jul 2024 14:20:08 +0200 Subject: [PATCH 46/77] Inline attributes --- .../app/main/ui/workspace/tokens/context_menu.cljs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index b5db426b6..4fa186df2 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -93,14 +93,6 @@ :shape-ids shape-ids :selected-pred #(seq (% ids-by-attributes))})) -(def spacing - {:padding {:p1 "Top" - :p2 "Right" - :p3 "Bottom" - :p4 "Left"} - :gap {:column-gap "Column Gap" - :row-gap "Row Gap"}}) - (defn all-or-sepearate-actions [{:keys [attribute-labels on-update-shape-all on-update-shape]} {:keys [token selected-shapes]}] (let [attributes (set (keys attribute-labels)) @@ -133,7 +125,10 @@ (defn spacing-attribute-actions [{:keys [token selected-shapes] :as context-data}] (let [on-update-shape (fn [resolved-value shape-ids attrs] (dwsl/update-layout shape-ids {:layout-padding (zipmap attrs (repeat resolved-value))})) - padding-attrs (:padding spacing) + padding-attrs {:p1 "Top" + :p2 "Right" + :p3 "Bottom" + :p4 "Left"} all-padding-attrs (into #{} (keys padding-attrs)) {:keys [all-selected? selected-pred shape-ids]} (attribute-actions token selected-shapes all-padding-attrs) horizontal-attributes #{:p1 :p3} From 5e911d814c089697c5e5ac2f5906bfcff3c74777 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Wed, 24 Jul 2024 14:49:39 +0200 Subject: [PATCH 47/77] Show attribute actions only when something is selected --- .../ui/workspace/tokens/context_menu.cljs | 58 ++++++++++--------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index 4fa186df2..1c77e04d3 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -260,30 +260,31 @@ (generic-attribute-actions #{:x} "X" context-data) (generic-attribute-actions #{:y} "Y" context-data)))})) -(defn generate-menu-entries [{:keys [type token] :as context-data}] - (let [{:keys [modal]} (get wtc/token-types (:type token)) - with-actions (get shape-attribute-actions-map (or type (:type token))) - attribute-actions (when with-actions - (with-actions context-data)) - default-actions [{:title "Delete Token" - :action #(st/emit! (dt/delete-token (:id token)))} - {:title "Duplicate Token" - :action #(st/emit! (dt/duplicate-token (:id token)))} - {:title "Edit Token" - :action (fn [event] - (let [{:keys [key fields]} modal - token (dt/get-token-data-from-token-id (:id token))] - (st/emit! dt/hide-token-context-menu) - (dom/stop-propagation event) - (modal/show! key {:x (.-clientX ^js event) - :y (.-clientY ^js event) - :position :right - :fields fields - :token token})))}]] +(defn default-actions [{:keys [token]}] + (let [{:keys [modal]} (get wtc/token-types token)] + [{:title "Delete Token" + :action #(st/emit! (dt/delete-token (:id token)))} + {:title "Duplicate Token" + :action #(st/emit! (dt/duplicate-token (:id token)))} + {:title "Edit Token" + :action (fn [event] + (let [{:keys [key fields]} modal + token (dt/get-token-data-from-token-id (:id token))] + (st/emit! dt/hide-token-context-menu) + (dom/stop-propagation event) + (modal/show! key {:x (.-clientX ^js event) + :y (.-clientY ^js event) + :position :right + :fields fields + :token token})))}])) + +(defn selection-actions [{:keys [type token] :as context-data}] + (let [with-actions (get shape-attribute-actions-map (or type (:type token))) + attribute-actions (with-actions context-data)] (concat attribute-actions - (when attribute-actions [:separator]) - default-actions))) + [:separator] + (default-actions context-data)))) ;; Components ------------------------------------------------------------------ @@ -340,8 +341,10 @@ children]])])) (mf/defc context-menu-tree - [context-data] - (let [entries (generate-menu-entries context-data)] + [{:keys [selected-shapes] :as context-data}] + (let [entries (if (seq selected-shapes) + (selection-actions context-data) + (default-actions context-data))] (for [[index {:keys [title action selected? submenu] :as entry}] (d/enumerate entries)] [:* {:key (str title " " index)} (cond @@ -382,7 +385,6 @@ :ref dropdown-ref :style {:top top :left left} :on-context-menu prevent-default} - (when (= :token (:type mdata)) - [:ul {:class (stl/css :context-list)} - [:& context-menu-tree {:token token - :selected-shapes selected-shapes}]])]])) + [:ul {:class (stl/css :context-list)} + [:& context-menu-tree {:token token + :selected-shapes selected-shapes}]]]])) From 386a4c94bab1408fc5058a421823b33934519633 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Wed, 24 Jul 2024 14:51:59 +0200 Subject: [PATCH 48/77] Disallow clicking pill when nothing is selected --- frontend/src/app/main/ui/workspace/tokens/sidebar.cljs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/sidebar.cljs b/frontend/src/app/main/ui/workspace/tokens/sidebar.cljs index d3f43d039..13c74a91b 100644 --- a/frontend/src/app/main/ui/workspace/tokens/sidebar.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/sidebar.cljs @@ -97,10 +97,11 @@ (mf/deps selected-shapes token-type-props) (fn [event token] (dom/stop-propagation event) - (st/emit! - (wtc/toggle-token {:token token - :shapes selected-shapes - :token-type-props token-type-props})))) + (when (seq selected-shapes) + (st/emit! + (wtc/toggle-token {:token token + :shapes selected-shapes + :token-type-props token-type-props}))))) tokens-count (count tokens)] [:div {:on-click on-toggle-open-click} [:& cmm/asset-section {:icon (mf/fnc icon-wrapper [_] From d5a03e154bec2857cfbe1771e79b8ccb8f42e79c Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Wed, 24 Jul 2024 14:57:33 +0200 Subject: [PATCH 49/77] Cleanup --- .../src/app/main/ui/workspace/tokens/context_menu.cljs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index 1c77e04d3..424fd0329 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -340,7 +340,7 @@ :on-context-menu prevent-default} children]])])) -(mf/defc context-menu-tree +(mf/defc menu-tree [{:keys [selected-shapes] :as context-data}] (let [entries (if (seq selected-shapes) (selection-actions context-data) @@ -350,7 +350,7 @@ (cond (= :separator entry) [:li {:class (stl/css :separator)}] submenu [:& menu-entry {:title title} - [:& context-menu-tree (assoc context-data :type submenu)]] + [:& menu-tree (assoc context-data :type submenu)]] :else [:& menu-entry {:title title :on-click action @@ -386,5 +386,5 @@ :style {:top top :left left} :on-context-menu prevent-default} [:ul {:class (stl/css :context-list)} - [:& context-menu-tree {:token token - :selected-shapes selected-shapes}]]]])) + [:& menu-tree {:token token + :selected-shapes selected-shapes}]]]])) From 03370c267d1e77c9631a9cce4a1b6411e4e391ce Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Wed, 24 Jul 2024 15:02:10 +0200 Subject: [PATCH 50/77] Cleanup --- .../ui/workspace/tokens/context_menu.cljs | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index 424fd0329..b34615dad 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -93,6 +93,22 @@ :shape-ids shape-ids :selected-pred #(seq (% ids-by-attributes))})) +(defn generic-attribute-actions [attributes title {:keys [token selected-shapes]}] + (let [{:keys [on-update-shape]} (get wtc/token-types (:type token)) + {:keys [selected-pred shape-ids]} (attribute-actions token selected-shapes attributes)] + (map (fn [attribute] + (let [selected? (selected-pred attribute) + props {:attributes #{attribute} + :token token + :shape-ids shape-ids}] + + {:title title + :selected? selected? + :action #(if selected? + (st/emit! (wtc/unapply-token props)) + (st/emit! (wtc/apply-token (assoc props :on-update-shape on-update-shape))))})) + attributes))) + (defn all-or-sepearate-actions [{:keys [attribute-labels on-update-shape-all on-update-shape]} {:keys [token selected-shapes]}] (let [attributes (set (keys attribute-labels)) @@ -220,22 +236,6 @@ :on-update-shape update-layout-sizing-limits} context-data))) -(defn generic-attribute-actions [attributes title {:keys [token selected-shapes]}] - (let [{:keys [on-update-shape]} (get wtc/token-types (:type token)) - {:keys [selected-pred shape-ids]} (attribute-actions token selected-shapes attributes)] - (map (fn [attribute] - (let [selected? (selected-pred attribute) - props {:attributes #{attribute} - :token token - :shape-ids shape-ids}] - - {:title title - :selected? selected? - :action #(if selected? - (st/emit! (wtc/unapply-token props)) - (st/emit! (wtc/apply-token (assoc props :on-update-shape on-update-shape))))})) - attributes))) - (def shape-attribute-actions-map (let [stroke-width (partial generic-attribute-actions #{:stroke-width} "Stroke Width")] {:border-radius (partial all-or-sepearate-actions {:attribute-labels {:r1 "Top Left" From 56e72b5247cc7bfced46628fcebc4ccae215529d Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Wed, 24 Jul 2024 15:29:40 +0200 Subject: [PATCH 51/77] Refactor - Separate core into namespaces: changes, token_types --- .../options/menus/layout_container.cljs | 11 +- .../sidebar/options/menus/measures.cljs | 11 +- .../app/main/ui/workspace/tokens/changes.cljs | 141 +++++++++++++++ .../ui/workspace/tokens/context_menu.cljs | 80 ++------- .../app/main/ui/workspace/tokens/core.cljs | 161 +----------------- .../main/ui/workspace/tokens/core_test.cljs | 0 .../app/main/ui/workspace/tokens/sidebar.cljs | 3 +- .../app/main/ui/workspace/tokens/token.cljs | 10 +- .../main/ui/workspace/tokens/token_types.cljs | 104 +++++++++++ .../token_tests/logic/token_actions_test.cljs | 21 +-- 10 files changed, 288 insertions(+), 254 deletions(-) create mode 100644 frontend/src/app/main/ui/workspace/tokens/changes.cljs create mode 100644 frontend/src/app/main/ui/workspace/tokens/core_test.cljs create mode 100644 frontend/src/app/main/ui/workspace/tokens/token_types.cljs diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs index 47e7904c1..b2f71ae23 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs @@ -14,10 +14,8 @@ [app.config :as cf] [app.main.data.events :as-alias ev] [app.main.data.workspace :as udw] - [app.main.data.workspace.changes :as dch] [app.main.data.workspace.grid-layout.editor :as dwge] [app.main.data.workspace.shape-layout :as dwsl] - [app.main.data.workspace.undo :as dwu] [app.main.features :as features] [app.main.refs :as refs] [app.main.store :as st] @@ -31,6 +29,7 @@ [app.main.ui.icons :as i] [app.main.ui.workspace.tokens.core :as wtc] [app.main.ui.workspace.tokens.editable-select :refer [editable-select]] + [app.main.ui.workspace.tokens.token-types :as wtty] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [app.util.keyboard :as kbd] @@ -864,7 +863,7 @@ (wtc/tokens-name-map->select-options {:shape shape :tokens spacing-tokens - :attributes (wtc/token-attributes :spacing) + :attributes (wtty/token-attributes :spacing) :selected-attributes #{:spacing-column}}))) spacing-row-options (mf/use-memo @@ -873,7 +872,7 @@ (wtc/tokens-name-map->select-options {:shape shape :tokens spacing-tokens - :attributes (wtc/token-attributes :spacing) + :attributes (wtty/token-attributes :spacing) :selected-attributes #{:spacing-row}}))) padding-x-options (mf/use-memo @@ -882,7 +881,7 @@ (wtc/tokens-name-map->select-options {:shape shape :tokens spacing-tokens - :attributes (wtc/token-attributes :spacing) + :attributes (wtty/token-attributes :spacing) :selected-attributes #{:padding-p1 :padding-p3}}))) padding-y-options (mf/use-memo @@ -891,7 +890,7 @@ (wtc/tokens-name-map->select-options {:shape shape :tokens spacing-tokens - :attributes (wtc/token-attributes :spacing) + :attributes (wtty/token-attributes :spacing) :selected-attributes #{:padding-p2 :padding-p4}}))) on-add-layout diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs index ef8eb85e3..63d4c6c77 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs @@ -27,6 +27,7 @@ [app.main.ui.workspace.tokens.core :as wtc] [app.main.ui.workspace.tokens.editable-select :refer [editable-select]] [app.main.ui.workspace.tokens.style-dictionary :as sd] + [app.main.ui.workspace.tokens.token-types :as wtty] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [clojure.set :refer [rename-keys union]] @@ -109,21 +110,21 @@ #(wtc/tokens-name-map->select-options {:shape shape :tokens border-radius-tokens - :attributes (wtc/token-attributes :border-radius)})) + :attributes (wtty/token-attributes :border-radius)})) sizing-tokens (:sizing tokens-by-type) width-options (mf/use-memo (mf/deps shape sizing-tokens) #(wtc/tokens-name-map->select-options {:shape shape :tokens sizing-tokens - :attributes (wtc/token-attributes :sizing) + :attributes (wtty/token-attributes :sizing) :selected-attributes #{:width}})) height-options (mf/use-memo (mf/deps shape sizing-tokens) #(wtc/tokens-name-map->select-options {:shape shape :tokens sizing-tokens - :attributes (wtc/token-attributes :sizing) + :attributes (wtty/token-attributes :sizing) :selected-attributes #{:height}})) flex-child? (->> selection-parents (some ctl/flex-layout?)) @@ -330,7 +331,7 @@ (let [token-value (wtc/maybe-resolve-token-value token)] (st/emit! (change-radius (fn [shape] - (-> (dt/unapply-token-id shape (wtc/token-attributes :border-radius)) + (-> (dt/unapply-token-id shape (wtty/token-attributes :border-radius)) (ctsr/set-radius-1 token-value)))))))) on-radius-1-change @@ -342,7 +343,7 @@ (change-radius (fn [shape] (-> (dt/maybe-apply-token-to-shape {:token (when token-value value) :shape shape - :attributes (wtc/token-attributes :border-radius)}) + :attributes (wtty/token-attributes :border-radius)}) (ctsr/set-radius-1 (or token-value value))))))))) on-radius-multi-change diff --git a/frontend/src/app/main/ui/workspace/tokens/changes.cljs b/frontend/src/app/main/ui/workspace/tokens/changes.cljs new file mode 100644 index 000000000..802903c08 --- /dev/null +++ b/frontend/src/app/main/ui/workspace/tokens/changes.cljs @@ -0,0 +1,141 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS INC + +(ns app.main.ui.workspace.tokens.changes + (:require + [app.common.types.token :as ctt] + [app.common.types.shape.radius :as ctsr] + [app.main.data.tokens :as dt] + [app.main.data.workspace.changes :as dch] + [app.main.data.workspace :as udw] + [app.main.data.workspace.shape-layout :as dwsl] + [app.main.data.workspace.transforms :as dwt] + [app.main.store :as st] + [app.main.ui.workspace.tokens.core :as wtc] + [app.main.ui.workspace.tokens.style-dictionary :as sd] + [app.main.ui.workspace.tokens.token :as wtt] + [beicon.v2.core :as rx] + [clojure.set :as set] + [potok.v2.core :as ptk] + [promesa.core :as p])) + +;; Token Updates --------------------------------------------------------------- + +(defn on-apply-token [{:keys [token token-type-props selected-shapes] :as _props}] + (let [{:keys [attributes on-apply on-update-shape] + :or {on-apply dt/update-token-from-attributes}} token-type-props + shape-ids (->> selected-shapes + (eduction + (remove #(wtt/shapes-token-applied? token % attributes)) + (map :id)))] + + (p/let [sd-tokens (sd/resolve-workspace-tokens+ {:debug? true})] + (let [resolved-token (get sd-tokens (:id token)) + resolved-token-value (wtt/resolve-token-value resolved-token)] + (doseq [shape selected-shapes] + (st/emit! (on-apply {:token-id (:id token) + :shape-id (:id shape) + :attributes attributes})) + (on-update-shape resolved-token-value shape-ids attributes)))))) + +;; Shape Updates --------------------------------------------------------------- + +(defn update-shape-radius-all [value shape-ids] + (dch/update-shapes shape-ids + (fn [shape] + (when (ctsr/has-radius? shape) + (ctsr/set-radius-1 shape value))) + {:reg-objects? true + :attrs ctt/border-radius-keys})) + +(defn update-opacity [value shape-ids] + (dch/update-shapes shape-ids #(assoc % :opacity value))) + +(defn update-rotation [value shape-ids] + (ptk/reify ::update-shape-dimensions + ptk/WatchEvent + (watch [_ _ _] + (rx/of + (udw/trigger-bounding-box-cloaking shape-ids) + (udw/increase-rotation shape-ids value))))) + +(defn update-shape-radius-single-corner [value shape-ids attributes] + (dch/update-shapes shape-ids + (fn [shape] + (when (ctsr/has-radius? shape) + (cond-> shape + (:rx shape) (ctsr/switch-to-radius-4) + :always (ctsr/set-radius-4 (first attributes) value)))) + {:reg-objects? true + :attrs [:rx :ry :r1 :r2 :r3 :r4]})) + +(defn update-stroke-width + [value shape-ids] + (dch/update-shapes shape-ids (fn [shape] + (when (seq (:strokes shape)) + (assoc-in shape [:strokes 0 :stroke-width] value))))) + +(defn update-layout-spacing [value shape-ids attributes] + (if-let [layout-gap (cond + (:row-gap attributes) {:row-gap value} + (:column-gap attributes) {:column-gap value})] + (dwsl/update-layout shape-ids {:layout-gap layout-gap}) + (dwsl/update-layout shape-ids {:layout-padding (zipmap attributes (repeat value))}))) + +(defn update-shape-dimensions [value shape-ids attributes] + (ptk/reify ::update-shape-dimensions + ptk/WatchEvent + (watch [_ _ _] + (rx/of + (when (:width attributes) (dwt/update-dimensions shape-ids :width value)) + (when (:height attributes) (dwt/update-dimensions shape-ids :height value)))))) + +(defn update-layout-spacing-column [value shape-ids] + (ptk/reify ::update-layout-spacing-column + ptk/WatchEvent + (watch [_ state _] + (rx/concat + (for [shape-id shape-ids] + (let [shape (dt/get-shape-from-state shape-id state) + layout-direction (:layout-flex-dir shape) + layout-update (if (or (= layout-direction :row-reverse) (= layout-direction :row)) + {:layout-gap {:column-gap value}} + {:layout-gap {:row-gap value}})] + (dwsl/update-layout [shape-id] layout-update))))))) + +(defn update-shape-position [value shape-ids attributes] + (doseq [shape-id shape-ids] + (st/emit! (dwt/update-position shape-id {(first attributes) value})))) + +(defn apply-dimensions-token [{:keys [token-id token-type-props selected-shapes]} attributes] + (let [token (dt/get-token-data-from-token-id token-id) + attributes (set attributes) + updated-token-type-props (cond + (set/superset? #{:x :y} attributes) + (assoc token-type-props + :on-update-shape update-shape-position + :attributes attributes) + + (set/superset? #{:stroke-width} attributes) + (assoc token-type-props + :on-update-shape update-stroke-width + :attributes attributes) + + :else token-type-props)] + (wtc/on-apply-token {:token token + :token-type-props updated-token-type-props + :selected-shapes selected-shapes}))) + +(defn update-layout-sizing-limits [value shape-ids attributes] + (ptk/reify ::update-layout-sizing-limits + ptk/WatchEvent + (watch [_ _ _] + (let [props (-> {:layout-item-min-w value + :layout-item-min-h value + :layout-item-max-w value + :layout-item-max-h value} + (select-keys attributes))] + (dwsl/update-layout-child shape-ids props))))) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index b34615dad..c3e85ba0c 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -11,79 +11,19 @@ [app.main.data.modal :as modal] [app.main.data.tokens :as dt] [app.main.data.workspace.shape-layout :as dwsl] - [app.main.data.workspace.transforms :as dwt] [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.dropdown :refer [dropdown]] [app.main.ui.icons :as i] + [app.main.ui.workspace.tokens.changes :as wtch] [app.main.ui.workspace.tokens.core :as wtc] [app.main.ui.workspace.tokens.token :as wtt] + [app.main.ui.workspace.tokens.token-types :as wtty] [app.util.dom :as dom] [app.util.timers :as timers] - [beicon.v2.core :as rx] - [clojure.set :as set] [okulary.core :as l] - [potok.v2.core :as ptk] [rumext.v2 :as mf])) -;; Events ---------------------------------------------------------------------- - -(defn update-layout-spacing [value shape-ids attributes] - (if-let [layout-gap (cond - (:row-gap attributes) {:row-gap value} - (:column-gap attributes) {:column-gap value})] - (dwsl/update-layout shape-ids {:layout-gap layout-gap}) - (dwsl/update-layout shape-ids {:layout-padding (zipmap attributes (repeat value))}))) - -(defn apply-spacing-token [{:keys [token-id token-type-props selected-shapes]} attributes] - (let [token (dt/get-token-data-from-token-id token-id) - attributes (set attributes) - updated-token-type-props (assoc token-type-props - :on-update-shape update-layout-spacing - :attributes attributes)] - (wtc/on-apply-token {:token token - :token-type-props updated-token-type-props - :selected-shapes selected-shapes}))) - -(defn update-shape-position [value shape-ids attributes] - (doseq [shape-id shape-ids] - (st/emit! (dwt/update-position shape-id {(first attributes) value})))) - -(defn apply-dimensions-token [{:keys [token-id token-type-props selected-shapes]} attributes] - (let [token (dt/get-token-data-from-token-id token-id) - attributes (set attributes) - updated-token-type-props (cond - (set/superset? #{:x :y} attributes) - (assoc token-type-props - :on-update-shape update-shape-position - :attributes attributes) - - (set/superset? #{:stroke-width} attributes) - (assoc token-type-props - :on-update-shape wtc/update-stroke-width - :attributes attributes) - - :else token-type-props)] - (wtc/on-apply-token {:token token - :token-type-props updated-token-type-props - :selected-shapes selected-shapes}))) - -(defn update-shape-dimensions [value shape-ids attributes] - (ptk/reify ::update-shape-dimensions - ptk/WatchEvent - (watch [_ _ _] - (rx/of - (when (:width attributes) (dwt/update-dimensions shape-ids :width value)) - (when (:height attributes) (dwt/update-dimensions shape-ids :height value)))))) - -(defn update-layout-sizing-limits [value shape-ids attributes] - (let [props (-> {:layout-item-min-w value - :layout-item-min-h value - :layout-item-max-w value - :layout-item-max-h value} - (select-keys attributes))] - (dwsl/update-layout-child shape-ids props))) - ;; Actions --------------------------------------------------------------------- (defn attribute-actions [token selected-shapes attributes] @@ -94,7 +34,7 @@ :selected-pred #(seq (% ids-by-attributes))})) (defn generic-attribute-actions [attributes title {:keys [token selected-shapes]}] - (let [{:keys [on-update-shape]} (get wtc/token-types (:type token)) + (let [{:keys [on-update-shape]} (get wtty/token-types (:type token)) {:keys [selected-pred shape-ids]} (attribute-actions token selected-shapes attributes)] (map (fn [attribute] (let [selected? (selected-pred attribute) @@ -212,7 +152,7 @@ (st/emit! event))})))) gap-items (all-or-sepearate-actions {:attribute-labels {:column-gap "Column Gap" :row-gap "Row Gap"} - :on-update-shape update-layout-spacing} + :on-update-shape wtch/update-layout-spacing} context-data)] (concat padding-items single-padding-items @@ -223,17 +163,17 @@ (concat (all-or-sepearate-actions {:attribute-labels {:width "Width" :height "Height"} - :on-update-shape update-shape-dimensions} + :on-update-shape wtch/update-shape-dimensions} context-data) [:separator] (all-or-sepearate-actions {:attribute-labels {:layout-item-min-w "Min Width" :layout-item-min-h "Min Height"} - :on-update-shape update-layout-sizing-limits} + :on-update-shape wtch/update-layout-sizing-limits} context-data) [:separator] (all-or-sepearate-actions {:attribute-labels {:layout-item-max-w "Max Width" :layout-item-max-h "Max Height"} - :on-update-shape update-layout-sizing-limits} + :on-update-shape wtch/update-layout-sizing-limits} context-data))) (def shape-attribute-actions-map @@ -242,8 +182,8 @@ :r2 "Top Right" :r4 "Bottom Left" :r3 "Bottom Right"} - :on-update-shape-all wtc/update-shape-radius-all - :on-update-shape wtc/update-shape-radius-single-corner}) + :on-update-shape-all wtch/update-shape-radius-all + :on-update-shape wtch/update-shape-radius-single-corner}) :spacing spacing-attribute-actions :sizing sizing-attribute-actions :rotation (partial generic-attribute-actions #{:rotation} "Rotation") @@ -261,7 +201,7 @@ (generic-attribute-actions #{:y} "Y" context-data)))})) (defn default-actions [{:keys [token]}] - (let [{:keys [modal]} (get wtc/token-types token)] + (let [{:keys [modal]} (get wtty/token-types token)] [{:title "Delete Token" :action #(st/emit! (dt/delete-token (:id token)))} {:title "Duplicate Token" diff --git a/frontend/src/app/main/ui/workspace/tokens/core.cljs b/frontend/src/app/main/ui/workspace/tokens/core.cljs index f92d73a67..b6dfb8000 100644 --- a/frontend/src/app/main/ui/workspace/tokens/core.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/core.cljs @@ -28,7 +28,7 @@ ;; Helpers --------------------------------------------------------------------- -(defn resolve-token-value [{:keys [value resolved-value] :as token}] +(defn resolve-token-value [{:keys [value resolved-value] :as _token}] (or resolved-value (d/parse-double value))) @@ -48,64 +48,6 @@ (cond-> (assoc item :label name) (wtt/token-applied? item shape (or selected-attributes attributes)) (assoc :selected? true)))))) -;; Shape Update Functions ------------------------------------------------------ - -(defn update-shape-radius-single-corner [value shape-ids attributes] - (dch/update-shapes shape-ids - (fn [shape] - (when (ctsr/has-radius? shape) - (cond-> shape - (:rx shape) (ctsr/switch-to-radius-4) - :always (ctsr/set-radius-4 (first attributes) value)))) - {:reg-objects? true - :attrs [:rx :ry :r1 :r2 :r3 :r4]})) - -(defn update-shape-radius-all [value shape-ids] - (dch/update-shapes shape-ids - (fn [shape] - (when (ctsr/has-radius? shape) - (ctsr/set-radius-1 shape value))) - {:reg-objects? true - :attrs ctt/border-radius-keys})) - -(defn update-shape-dimensions [value shape-ids] - (ptk/reify ::update-shape-dimensions - ptk/WatchEvent - (watch [_ _ _] - (rx/of - (dwt/update-dimensions shape-ids :width value) - (dwt/update-dimensions shape-ids :height value))))) - -(defn update-opacity [value shape-ids] - (dch/update-shapes shape-ids #(assoc % :opacity value))) - -(defn update-stroke-width - [value shape-ids] - (dch/update-shapes shape-ids (fn [shape] - (when (seq (:strokes shape)) - (assoc-in shape [:strokes 0 :stroke-width] value))))) - -(defn update-rotation [value shape-ids] - (ptk/reify ::update-shape-dimensions - ptk/WatchEvent - (watch [_ _ _] - (rx/of - (udw/trigger-bounding-box-cloaking shape-ids) - (udw/increase-rotation shape-ids value))))) - -(defn update-layout-spacing-column [value shape-ids] - (ptk/reify ::update-layout-spacing-column - ptk/WatchEvent - (watch [_ state _] - (rx/concat - (for [shape-id shape-ids] - (let [shape (dt/get-shape-from-state shape-id state) - layout-direction (:layout-flex-dir shape) - layout-update (if (or (= layout-direction :row-reverse) (= layout-direction :row)) - {:layout-gap {:column-gap value}} - {:layout-gap {:row-gap value}})] - (dwsl/update-layout [shape-id] layout-update))))))) - ;; Events ---------------------------------------------------------------------- (defn apply-token @@ -212,104 +154,3 @@ (let [all-tokens (deref refs/workspace-tokens) transformed-tokens-json (transform-tokens-into-json-format all-tokens)] (export-tokens-file transformed-tokens-json))) - -;; Token types ----------------------------------------------------------------- - -(def token-types - (ordered-map - [:border-radius - {:title "Border Radius" - :attributes ctt/border-radius-keys - :on-update-shape update-shape-radius-all - :modal {:key :tokens/border-radius - :fields [{:label "Border Radius" - :key :border-radius}]}}] - [:stroke-width - {:title "Stroke Width" - :attributes ctt/stroke-width-keys - :on-update-shape update-stroke-width - :modal {:key :tokens/stroke-width - :fields [{:label "Stroke Width" - :key :stroke-width}]}}] - - [:sizing - {:title "Sizing" - :attributes #{:width :height} - :on-update-shape update-shape-dimensions - :modal {:key :tokens/sizing - :fields [{:label "Sizing" - :key :sizing}]}}] - [:dimensions - {:title "Dimensions" - :attributes #{:width :height} - :on-update-shape update-shape-dimensions - :modal {:key :tokens/dimensions - :fields [{:label "Dimensions" - :key :dimensions}]}}] - - [:opacity - {:title "Opacity" - :attributes ctt/opacity-keys - :on-update-shape update-opacity - :modal {:key :tokens/opacity - :fields [{:label "Opacity" - :key :opacity}]}}] - - [:rotation - {:title "Rotation" - :attributes ctt/rotation-keys - :on-update-shape update-rotation - :modal {:key :tokens/rotation - :fields [{:label "Rotation" - :key :rotation}]}}] - [:spacing - {:title "Spacing" - :attributes ctt/spacing-keys - :on-update-shape update-layout-spacing-column - :modal {:key :tokens/spacing - :fields [{:label "Spacing" - :key :spacing}]}}] - (comment - [:boolean - {:title "Boolean" - :modal {:key :tokens/boolean - :fields [{:label "Boolean"}]}}] - - [:box-shadow - {:title "Box Shadow" - :modal {:key :tokens/box-shadow - :fields [{:label "Box shadows" - :key :box-shadow - :type :box-shadow}]}}] - - [:numeric - {:title "Numeric" - :modal {:key :tokens/numeric - :fields [{:label "Numeric" - :key :numeric}]}}] - - [:other - {:title "Other" - :modal {:key :tokens/other - :fields [{:label "Other" - :key :other}]}}] - [:string - {:title "String" - :modal {:key :tokens/string - :fields [{:label "String" - :key :string}]}}] - [:typography - {:title "Typography" - :modal {:key :tokens/typography - :fields [{:label "Font" :key :font-family} - {:label "Weight" :key :weight} - {:label "Font Size" :key :font-size} - {:label "Line Height" :key :line-height} - {:label "Letter Spacing" :key :letter-spacing} - {:label "Paragraph Spacing" :key :paragraph-spacing} - {:label "Paragraph Indent" :key :paragraph-indent} - {:label "Text Decoration" :key :text-decoration} - {:label "Text Case" :key :text-case}]}}]))) - -(defn token-attributes [token-type] - (get-in token-types [token-type :attributes])) diff --git a/frontend/src/app/main/ui/workspace/tokens/core_test.cljs b/frontend/src/app/main/ui/workspace/tokens/core_test.cljs new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/main/ui/workspace/tokens/sidebar.cljs b/frontend/src/app/main/ui/workspace/tokens/sidebar.cljs index 13c74a91b..0dee240ab 100644 --- a/frontend/src/app/main/ui/workspace/tokens/sidebar.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/sidebar.cljs @@ -17,6 +17,7 @@ [app.main.ui.workspace.tokens.core :as wtc] [app.main.ui.workspace.tokens.style-dictionary :as sd] [app.main.ui.workspace.tokens.token :as wtt] + [app.main.ui.workspace.tokens.token-types :as wtty] [app.util.dom :as dom] [cuerdas.core :as str] [okulary.core :as l] @@ -131,7 +132,7 @@ Sort each group alphabetically (by their `:token-key`)." [tokens] (let [tokens-by-type (wtc/group-tokens-by-type tokens) - {:keys [empty filled]} (->> wtc/token-types + {:keys [empty filled]} (->> wtty/token-types (map (fn [[token-key token-type-props]] {:token-key token-key :token-type-props token-type-props diff --git a/frontend/src/app/main/ui/workspace/tokens/token.cljs b/frontend/src/app/main/ui/workspace/tokens/token.cljs index 4df33b285..84a994a99 100644 --- a/frontend/src/app/main/ui/workspace/tokens/token.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/token.cljs @@ -1,7 +1,13 @@ (ns app.main.ui.workspace.tokens.token (:require - [cuerdas.core :as str] - [clojure.set :as set])) + [app.common.data :as d] + [clojure.set :as set] + [cuerdas.core :as str])) + +(defn resolve-token-value [{:keys [value resolved-value] :as token}] + (or + resolved-value + (d/parse-double value))) (defn attributes-map "Creats an attributes map using collection of `attributes` for `id`." diff --git a/frontend/src/app/main/ui/workspace/tokens/token_types.cljs b/frontend/src/app/main/ui/workspace/tokens/token_types.cljs new file mode 100644 index 000000000..04ae55aea --- /dev/null +++ b/frontend/src/app/main/ui/workspace/tokens/token_types.cljs @@ -0,0 +1,104 @@ +(ns app.main.ui.workspace.tokens.token-types + (:require + [app.common.types.token :as ctt] + [app.main.ui.workspace.tokens.changes :as wtch] + [app.common.data :as d :refer [ordered-map]])) + +(def token-types + (ordered-map + [:border-radius + {:title "Border Radius" + :attributes ctt/border-radius-keys + :on-update-shape wtch/update-shape-radius-all + :modal {:key :tokens/border-radius + :fields [{:label "Border Radius" + :key :border-radius}]}}] + [:stroke-width + {:title "Stroke Width" + :attributes ctt/stroke-width-keys + :on-update-shape wtch/update-stroke-width + :modal {:key :tokens/stroke-width + :fields [{:label "Stroke Width" + :key :stroke-width}]}}] + + [:sizing + {:title "Sizing" + :attributes #{:width :height} + :on-update-shape wtch/update-shape-dimensions + :modal {:key :tokens/sizing + :fields [{:label "Sizing" + :key :sizing}]}}] + [:dimensions + {:title "Dimensions" + :attributes #{:width :height} + :on-update-shape wtch/update-shape-dimensions + :modal {:key :tokens/dimensions + :fields [{:label "Dimensions" + :key :dimensions}]}}] + + [:opacity + {:title "Opacity" + :attributes ctt/opacity-keys + :on-update-shape wtch/update-opacity + :modal {:key :tokens/opacity + :fields [{:label "Opacity" + :key :opacity}]}}] + + [:rotation + {:title "Rotation" + :attributes ctt/rotation-keys + :on-update-shape wtch/update-rotation + :modal {:key :tokens/rotation + :fields [{:label "Rotation" + :key :rotation}]}}] + [:spacing + {:title "Spacing" + :attributes ctt/spacing-keys + :on-update-shape wtch/update-layout-spacing-column + :modal {:key :tokens/spacing + :fields [{:label "Spacing" + :key :spacing}]}}] + (comment + [:boolean + {:title "Boolean" + :modal {:key :tokens/boolean + :fields [{:label "Boolean"}]}}] + + [:box-shadow + {:title "Box Shadow" + :modal {:key :tokens/box-shadow + :fields [{:label "Box shadows" + :key :box-shadow + :type :box-shadow}]}}] + + [:numeric + {:title "Numeric" + :modal {:key :tokens/numeric + :fields [{:label "Numeric" + :key :numeric}]}}] + + [:other + {:title "Other" + :modal {:key :tokens/other + :fields [{:label "Other" + :key :other}]}}] + [:string + {:title "String" + :modal {:key :tokens/string + :fields [{:label "String" + :key :string}]}}] + [:typography + {:title "Typography" + :modal {:key :tokens/typography + :fields [{:label "Font" :key :font-family} + {:label "Weight" :key :weight} + {:label "Font Size" :key :font-size} + {:label "Line Height" :key :line-height} + {:label "Letter Spacing" :key :letter-spacing} + {:label "Paragraph Spacing" :key :paragraph-spacing} + {:label "Paragraph Indent" :key :paragraph-indent} + {:label "Text Decoration" :key :text-decoration} + {:label "Text Case" :key :text-case}]}}]))) + +(defn token-attributes [token-type] + (get-in token-types [token-type :attributes])) diff --git a/frontend/test/token_tests/logic/token_actions_test.cljs b/frontend/test/token_tests/logic/token_actions_test.cljs index 8546b0771..5c1ca4c06 100644 --- a/frontend/test/token_tests/logic/token_actions_test.cljs +++ b/frontend/test/token_tests/logic/token_actions_test.cljs @@ -3,6 +3,7 @@ [app.common.test-helpers.compositions :as ctho] [app.common.test-helpers.files :as cthf] [app.common.test-helpers.shapes :as cths] + [app.main.ui.workspace.tokens.changes :as wtch] [app.main.ui.workspace.tokens.core :as wtc] [cljs.test :as t :include-macros true] [frontend-tests.helpers.pages :as thp] @@ -36,11 +37,11 @@ events [(wtc/apply-token {:shape-ids [(:id rect-1)] :attributes #{:rx :ry} :token (toht/get-token file :token-1) - :on-update-shape wtc/update-shape-radius-all}) + :on-update-shape wtch/update-shape-radius-all}) (wtc/apply-token {:shape-ids [(:id rect-1)] :attributes #{:rx :ry} :token (toht/get-token file :token-2) - :on-update-shape wtc/update-shape-radius-all})]] + :on-update-shape wtch/update-shape-radius-all})]] (tohs/run-store-async store done events (fn [new-state] @@ -64,7 +65,7 @@ (wtc/apply-token {:attributes #{:rx :ry :r1 :r2 :r3 :r4} :token (toht/get-token file :token-1) :shape-ids [(:id rect-1)] - :on-update-shape wtc/update-shape-radius-all}) + :on-update-shape wtch/update-shape-radius-all}) ;; Apply single `:r1` attribute to same shape ;; while removing other attributes from the border-radius set ;; but keep `:r4` for testing purposes @@ -72,7 +73,7 @@ :attributes-to-remove #{:rx :ry :r1 :r2 :r3} :token (toht/get-token file :token-2) :shape-ids [(:id rect-1)] - :on-update-shape wtc/update-shape-radius-all})]] + :on-update-shape wtch/update-shape-radius-all})]] (tohs/run-store-async store done events (fn [new-state] @@ -97,7 +98,7 @@ events [(wtc/apply-token {:shape-ids [(:id rect-1)] :attributes #{:rx :ry} :token (toht/get-token file :token-2) - :on-update-shape wtc/update-shape-radius-all})]] + :on-update-shape wtch/update-shape-radius-all})]] (tohs/run-store-async store done events (fn [new-state] @@ -123,7 +124,7 @@ events [(wtc/apply-token {:shape-ids [(:id rect-1)] :attributes #{:width :height} :token (toht/get-token file :token-target) - :on-update-shape wtc/update-shape-dimensions})]] + :on-update-shape wtch/update-shape-dimensions})]] (tohs/run-store-async store done events (fn [new-state] @@ -149,7 +150,7 @@ events [(wtc/apply-token {:shape-ids [(:id rect-1)] :attributes #{:width :height} :token (toht/get-token file :token-target) - :on-update-shape wtc/update-shape-dimensions})]] + :on-update-shape wtch/update-shape-dimensions})]] (tohs/run-store-async store done events (fn [new-state] @@ -175,7 +176,7 @@ events [(wtc/apply-token {:shape-ids [(:id rect-1)] :attributes #{:opacity} :token (toht/get-token file :token-target) - :on-update-shape wtc/update-opacity})]] + :on-update-shape wtch/update-opacity})]] (tohs/run-store-async store done events (fn [new-state] @@ -200,7 +201,7 @@ events [(wtc/apply-token {:shape-ids [(:id rect-1)] :attributes #{:rotation} :token (toht/get-token file :token-target) - :on-update-shape wtc/update-rotation})]] + :on-update-shape wtch/update-rotation})]] (tohs/run-store-async store done events (fn [new-state] @@ -221,7 +222,7 @@ rect-2 (cths/get-shape file :rect-2) events [(wtc/toggle-token {:shapes [rect-1 rect-2] :token-type-props {:attributes #{:rx :ry} - :on-update-shape wtc/update-shape-radius-all} + :on-update-shape wtch/update-shape-radius-all} :token (toht/get-token file :token-2)})]] (tohs/run-store-async store done events From b392c3ba65cf61a1a617a80e69e8b41472fd8c1c Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Wed, 24 Jul 2024 15:46:14 +0200 Subject: [PATCH 52/77] Move token change events to changes ns --- .../app/main/ui/workspace/tokens/changes.cljs | 72 +++++++++++++- .../ui/workspace/tokens/context_menu.cljs | 45 +++++---- .../app/main/ui/workspace/tokens/core.cljs | 97 +------------------ .../app/main/ui/workspace/tokens/sidebar.cljs | 7 +- .../token_tests/logic/token_actions_test.cljs | 94 +++++++++--------- 5 files changed, 142 insertions(+), 173 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/changes.cljs b/frontend/src/app/main/ui/workspace/tokens/changes.cljs index 802903c08..3680e3a2c 100644 --- a/frontend/src/app/main/ui/workspace/tokens/changes.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/changes.cljs @@ -6,13 +6,14 @@ (ns app.main.ui.workspace.tokens.changes (:require - [app.common.types.token :as ctt] [app.common.types.shape.radius :as ctsr] + [app.common.types.token :as ctt] [app.main.data.tokens :as dt] - [app.main.data.workspace.changes :as dch] [app.main.data.workspace :as udw] + [app.main.data.workspace.changes :as dch] [app.main.data.workspace.shape-layout :as dwsl] [app.main.data.workspace.transforms :as dwt] + [app.main.data.workspace.undo :as dwu] [app.main.store :as st] [app.main.ui.workspace.tokens.core :as wtc] [app.main.ui.workspace.tokens.style-dictionary :as sd] @@ -24,6 +25,67 @@ ;; Token Updates --------------------------------------------------------------- +(defn apply-token + "Apply `attributes` that match `token` for `shape-ids`. + + Optionally remove attributes from `attributes-to-remove`, + this is useful for applying a single attribute from an attributes set + while removing other applied tokens from this set." + [{:keys [attributes attributes-to-remove token shape-ids on-update-shape] :as _props}] + (ptk/reify ::apply-token + ptk/WatchEvent + (watch [_ state _] + (->> (rx/from (sd/resolve-tokens+ (get-in state [:workspace-data :tokens]))) + (rx/mapcat + (fn [sd-tokens] + (let [undo-id (js/Symbol) + resolved-value (-> (get sd-tokens (:id token)) + (wtt/resolve-token-value)) + tokenized-attributes (wtt/attributes-map attributes (:id token))] + (rx/of + (dwu/start-undo-transaction undo-id) + (dch/update-shapes shape-ids (fn [shape] + (cond-> shape + attributes-to-remove (update :applied-tokens #(apply (partial dissoc %) attributes-to-remove)) + :always (update :applied-tokens merge tokenized-attributes)))) + (when on-update-shape + (on-update-shape resolved-value shape-ids attributes)) + (dwu/commit-undo-transaction undo-id))))))))) + +(defn unapply-token + "Removes `attributes` that match `token` for `shape-ids`. + + Doesn't update shape attributes." + [{:keys [attributes token shape-ids] :as _props}] + (ptk/reify ::unapply-token + ptk/WatchEvent + (watch [_ _ _] + (rx/of + (let [remove-token #(when % (wtt/remove-attributes-for-token-id attributes (:id token) %))] + (dch/update-shapes + shape-ids + (fn [shape] + (update shape :applied-tokens remove-token)))))))) + +(defn toggle-token + [{:keys [token-type-props token shapes] :as _props}] + (ptk/reify ::on-toggle-token + ptk/WatchEvent + (watch [_ _ _] + (let [{:keys [attributes on-update-shape]} token-type-props + unapply-tokens? (wtt/shapes-token-applied? token shapes (:attributes token-type-props)) + shape-ids (map :id shapes)] + (if unapply-tokens? + (rx/of + (unapply-token {:attributes attributes + :token token + :shape-ids shape-ids})) + (rx/of + (apply-token {:attributes attributes + :token token + :shape-ids shape-ids + :on-update-shape on-update-shape}))))))) + (defn on-apply-token [{:keys [token token-type-props selected-shapes] :as _props}] (let [{:keys [attributes on-apply on-update-shape] :or {on-apply dt/update-token-from-attributes}} token-type-props @@ -125,9 +187,9 @@ :attributes attributes) :else token-type-props)] - (wtc/on-apply-token {:token token - :token-type-props updated-token-type-props - :selected-shapes selected-shapes}))) + (on-apply-token {:token token + :token-type-props updated-token-type-props + :selected-shapes selected-shapes}))) (defn update-layout-sizing-limits [value shape-ids attributes] (ptk/reify ::update-layout-sizing-limits diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index c3e85ba0c..60729d406 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -16,7 +16,6 @@ [app.main.ui.components.dropdown :refer [dropdown]] [app.main.ui.icons :as i] [app.main.ui.workspace.tokens.changes :as wtch] - [app.main.ui.workspace.tokens.core :as wtc] [app.main.ui.workspace.tokens.token :as wtt] [app.main.ui.workspace.tokens.token-types :as wtty] [app.util.dom :as dom] @@ -45,8 +44,8 @@ {:title title :selected? selected? :action #(if selected? - (st/emit! (wtc/unapply-token props)) - (st/emit! (wtc/apply-token (assoc props :on-update-shape on-update-shape))))})) + (st/emit! (wtch/unapply-token props)) + (st/emit! (wtch/apply-token (assoc props :on-update-shape on-update-shape))))})) attributes))) (defn all-or-sepearate-actions [{:keys [attribute-labels on-update-shape-all on-update-shape]} @@ -59,8 +58,8 @@ {:title "All" :selected? all-selected? :action #(if all-selected? - (st/emit! (wtc/unapply-token props)) - (st/emit! (wtc/apply-token (assoc props :on-update-shape (or on-update-shape-all on-update-shape)))))}) + (st/emit! (wtch/unapply-token props)) + (st/emit! (wtch/apply-token (assoc props :on-update-shape (or on-update-shape-all on-update-shape)))))}) single-actions (map (fn [[attr title]] (let [selected? (selected-pred attr)] {:title title @@ -70,10 +69,10 @@ :shape-ids shape-ids} event (cond all-selected? (-> (assoc props :attributes-to-remove attributes) - (wtc/apply-token)) - selected? (wtc/unapply-token props) + (wtch/apply-token)) + selected? (wtch/unapply-token props) :else (-> (assoc props :on-update-shape on-update-shape) - (wtc/apply-token)))] + (wtch/apply-token)))] (st/emit! event))})) attribute-labels)] (concat [all-action] single-actions))) @@ -102,19 +101,19 @@ :token token :shape-ids shape-ids}] (if all-selected? - (st/emit! (wtc/unapply-token props)) - (st/emit! (wtc/apply-token (assoc props :on-update-shape on-update-shape))))))} + (st/emit! (wtch/unapply-token props)) + (st/emit! (wtch/apply-token (assoc props :on-update-shape on-update-shape))))))} {:title "Horizontal" :selected? horizontal-padding-selected? :action (fn [] (let [props {:token token :shape-ids shape-ids} event (cond - all-selected? (wtc/apply-token (assoc props :attributes-to-remove vertical-attributes)) - horizontal-padding-selected? (wtc/apply-token (assoc props :attributes-to-remove horizontal-attributes)) - :else (wtc/apply-token (assoc props - :attributes horizontal-attributes - :on-update-shape on-update-shape)))] + all-selected? (wtch/apply-token (assoc props :attributes-to-remove vertical-attributes)) + horizontal-padding-selected? (wtch/apply-token (assoc props :attributes-to-remove horizontal-attributes)) + :else (wtch/apply-token (assoc props + :attributes horizontal-attributes + :on-update-shape on-update-shape)))] (st/emit! event)))} {:title "Vertical" :selected? vertical-padding-selected? @@ -122,11 +121,11 @@ (let [props {:token token :shape-ids shape-ids} event (cond - all-selected? (wtc/apply-token (assoc props :attributes-to-remove vertical-attributes)) - vertical-padding-selected? (wtc/apply-token (assoc props :attributes-to-remove vertical-attributes)) - :else (wtc/apply-token (assoc props - :attributes vertical-attributes - :on-update-shape on-update-shape)))] + all-selected? (wtch/apply-token (assoc props :attributes-to-remove vertical-attributes)) + vertical-padding-selected? (wtch/apply-token (assoc props :attributes-to-remove vertical-attributes)) + :else (wtch/apply-token (assoc props + :attributes vertical-attributes + :on-update-shape on-update-shape)))] (st/emit! event)))}] single-padding-items (->> padding-attrs (map (fn [[attr title]] @@ -145,10 +144,10 @@ :shape-ids shape-ids} event (cond all-selected? (-> (assoc props :attributes-to-remove all-padding-attrs) - (wtc/apply-token)) - selected? (wtc/unapply-token props) + (wtch/apply-token)) + selected? (wtch/unapply-token props) :else (-> (assoc props :on-update-shape on-update-shape) - (wtc/apply-token)))] + (wtch/apply-token)))] (st/emit! event))})))) gap-items (all-or-sepearate-actions {:attribute-labels {:column-gap "Column Gap" :row-gap "Row Gap"} diff --git a/frontend/src/app/main/ui/workspace/tokens/core.cljs b/frontend/src/app/main/ui/workspace/tokens/core.cljs index b6dfb8000..a68d7e046 100644 --- a/frontend/src/app/main/ui/workspace/tokens/core.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/core.cljs @@ -6,25 +6,12 @@ (ns app.main.ui.workspace.tokens.core (:require - [app.common.data :as d :refer [ordered-map]] - [app.common.types.shape.radius :as ctsr] - [app.common.types.token :as ctt] - [app.main.data.tokens :as dt] - [app.main.data.workspace :as udw] - [app.main.data.workspace.changes :as dch] - [app.main.data.workspace.shape-layout :as dwsl] - [app.main.data.workspace.transforms :as dwt] - [app.main.data.workspace.undo :as dwu] + [app.common.data :as d] [app.main.refs :as refs] - [app.main.store :as st] - [app.main.ui.workspace.tokens.style-dictionary :as sd] [app.main.ui.workspace.tokens.token :as wtt] [app.util.dom :as dom] [app.util.webapi :as wapi] - [beicon.v2.core :as rx] - [cuerdas.core :as str] - [potok.v2.core :as ptk] - [promesa.core :as p])) + [cuerdas.core :as str])) ;; Helpers --------------------------------------------------------------------- @@ -48,86 +35,6 @@ (cond-> (assoc item :label name) (wtt/token-applied? item shape (or selected-attributes attributes)) (assoc :selected? true)))))) -;; Events ---------------------------------------------------------------------- - -(defn apply-token - "Apply `attributes` that match `token` for `shape-ids`. - - Optionally remove attributes from `attributes-to-remove`, - this is useful for applying a single attribute from an attributes set - while removing other applied tokens from this set." - [{:keys [attributes attributes-to-remove token shape-ids on-update-shape] :as _props}] - (ptk/reify ::apply-token - ptk/WatchEvent - (watch [_ state _] - (->> (rx/from (sd/resolve-tokens+ (get-in state [:workspace-data :tokens]))) - (rx/mapcat - (fn [sd-tokens] - (let [undo-id (js/Symbol) - resolved-value (-> (get sd-tokens (:id token)) - (resolve-token-value)) - tokenized-attributes (wtt/attributes-map attributes (:id token))] - (rx/of - (dwu/start-undo-transaction undo-id) - (dch/update-shapes shape-ids (fn [shape] - (cond-> shape - attributes-to-remove (update :applied-tokens #(apply (partial dissoc %) attributes-to-remove)) - :always (update :applied-tokens merge tokenized-attributes)))) - (when on-update-shape - (on-update-shape resolved-value shape-ids attributes)) - (dwu/commit-undo-transaction undo-id))))))))) - -(defn unapply-token - "Removes `attributes` that match `token` for `shape-ids`. - - Doesn't update shape attributes." - [{:keys [attributes token shape-ids] :as _props}] - (ptk/reify ::unapply-token - ptk/WatchEvent - (watch [_ _ _] - (rx/of - (let [remove-token #(when % (wtt/remove-attributes-for-token-id attributes (:id token) %))] - (dch/update-shapes - shape-ids - (fn [shape] - (update shape :applied-tokens remove-token)))))))) - -(defn toggle-token - [{:keys [token-type-props token shapes] :as _props}] - (ptk/reify ::on-toggle-token - ptk/WatchEvent - (watch [_ _ _] - (let [{:keys [attributes on-update-shape]} token-type-props - unapply-tokens? (wtt/shapes-token-applied? token shapes (:attributes token-type-props)) - shape-ids (map :id shapes)] - (if unapply-tokens? - (rx/of - (unapply-token {:attributes attributes - :token token - :shape-ids shape-ids})) - (rx/of - (apply-token {:attributes attributes - :token token - :shape-ids shape-ids - :on-update-shape on-update-shape}))))))) - -(defn on-apply-token [{:keys [token token-type-props selected-shapes] :as _props}] - (let [{:keys [attributes on-apply on-update-shape] - :or {on-apply dt/update-token-from-attributes}} token-type-props - shape-ids (->> selected-shapes - (eduction - (remove #(wtt/shapes-token-applied? token % attributes)) - (map :id)))] - - (p/let [sd-tokens (sd/resolve-workspace-tokens+ {:debug? true})] - (let [resolved-token (get sd-tokens (:id token)) - resolved-token-value (resolve-token-value resolved-token)] - (doseq [shape selected-shapes] - (st/emit! (on-apply {:token-id (:id token) - :shape-id (:id shape) - :attributes attributes})) - (on-update-shape resolved-token-value shape-ids attributes)))))) - ;; JSON export functions ------------------------------------------------------- (defn encode-tokens diff --git a/frontend/src/app/main/ui/workspace/tokens/sidebar.cljs b/frontend/src/app/main/ui/workspace/tokens/sidebar.cljs index 0dee240ab..9c2218716 100644 --- a/frontend/src/app/main/ui/workspace/tokens/sidebar.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/sidebar.cljs @@ -14,6 +14,7 @@ [app.main.store :as st] [app.main.ui.icons :as i] [app.main.ui.workspace.sidebar.assets.common :as cmm] + [app.main.ui.workspace.tokens.changes :as wtch] [app.main.ui.workspace.tokens.core :as wtc] [app.main.ui.workspace.tokens.style-dictionary :as sd] [app.main.ui.workspace.tokens.token :as wtt] @@ -100,9 +101,9 @@ (dom/stop-propagation event) (when (seq selected-shapes) (st/emit! - (wtc/toggle-token {:token token - :shapes selected-shapes - :token-type-props token-type-props}))))) + (wtch/toggle-token {:token token + :shapes selected-shapes + :token-type-props token-type-props}))))) tokens-count (count tokens)] [:div {:on-click on-toggle-open-click} [:& cmm/asset-section {:icon (mf/fnc icon-wrapper [_] diff --git a/frontend/test/token_tests/logic/token_actions_test.cljs b/frontend/test/token_tests/logic/token_actions_test.cljs index 5c1ca4c06..1b6565d00 100644 --- a/frontend/test/token_tests/logic/token_actions_test.cljs +++ b/frontend/test/token_tests/logic/token_actions_test.cljs @@ -34,14 +34,14 @@ (let [file (setup-file) store (ths/setup-store file) rect-1 (cths/get-shape file :rect-1) - events [(wtc/apply-token {:shape-ids [(:id rect-1)] - :attributes #{:rx :ry} - :token (toht/get-token file :token-1) - :on-update-shape wtch/update-shape-radius-all}) - (wtc/apply-token {:shape-ids [(:id rect-1)] - :attributes #{:rx :ry} - :token (toht/get-token file :token-2) - :on-update-shape wtch/update-shape-radius-all})]] + events [(wtch/apply-token {:shape-ids [(:id rect-1)] + :attributes #{:rx :ry} + :token (toht/get-token file :token-1) + :on-update-shape wtch/update-shape-radius-all}) + (wtch/apply-token {:shape-ids [(:id rect-1)] + :attributes #{:rx :ry} + :token (toht/get-token file :token-2) + :on-update-shape wtch/update-shape-radius-all})]] (tohs/run-store-async store done events (fn [new-state] @@ -62,18 +62,18 @@ store (ths/setup-store file) rect-1 (cths/get-shape file :rect-1) events [;; Apply `:token-1` to all border radius attributes - (wtc/apply-token {:attributes #{:rx :ry :r1 :r2 :r3 :r4} - :token (toht/get-token file :token-1) - :shape-ids [(:id rect-1)] - :on-update-shape wtch/update-shape-radius-all}) + (wtch/apply-token {:attributes #{:rx :ry :r1 :r2 :r3 :r4} + :token (toht/get-token file :token-1) + :shape-ids [(:id rect-1)] + :on-update-shape wtch/update-shape-radius-all}) ;; Apply single `:r1` attribute to same shape ;; while removing other attributes from the border-radius set ;; but keep `:r4` for testing purposes - (wtc/apply-token {:attributes #{:r1} - :attributes-to-remove #{:rx :ry :r1 :r2 :r3} - :token (toht/get-token file :token-2) - :shape-ids [(:id rect-1)] - :on-update-shape wtch/update-shape-radius-all})]] + (wtch/apply-token {:attributes #{:r1} + :attributes-to-remove #{:rx :ry :r1 :r2 :r3} + :token (toht/get-token file :token-2) + :shape-ids [(:id rect-1)] + :on-update-shape wtch/update-shape-radius-all})]] (tohs/run-store-async store done events (fn [new-state] @@ -95,10 +95,10 @@ (let [file (setup-file) store (ths/setup-store file) rect-1 (cths/get-shape file :rect-1) - events [(wtc/apply-token {:shape-ids [(:id rect-1)] - :attributes #{:rx :ry} - :token (toht/get-token file :token-2) - :on-update-shape wtch/update-shape-radius-all})]] + events [(wtch/apply-token {:shape-ids [(:id rect-1)] + :attributes #{:rx :ry} + :token (toht/get-token file :token-2) + :on-update-shape wtch/update-shape-radius-all})]] (tohs/run-store-async store done events (fn [new-state] @@ -121,10 +121,10 @@ :type :dimensions})) store (ths/setup-store file) rect-1 (cths/get-shape file :rect-1) - events [(wtc/apply-token {:shape-ids [(:id rect-1)] - :attributes #{:width :height} - :token (toht/get-token file :token-target) - :on-update-shape wtch/update-shape-dimensions})]] + events [(wtch/apply-token {:shape-ids [(:id rect-1)] + :attributes #{:width :height} + :token (toht/get-token file :token-target) + :on-update-shape wtch/update-shape-dimensions})]] (tohs/run-store-async store done events (fn [new-state] @@ -147,10 +147,10 @@ :type :sizing})) store (ths/setup-store file) rect-1 (cths/get-shape file :rect-1) - events [(wtc/apply-token {:shape-ids [(:id rect-1)] - :attributes #{:width :height} - :token (toht/get-token file :token-target) - :on-update-shape wtch/update-shape-dimensions})]] + events [(wtch/apply-token {:shape-ids [(:id rect-1)] + :attributes #{:width :height} + :token (toht/get-token file :token-target) + :on-update-shape wtch/update-shape-dimensions})]] (tohs/run-store-async store done events (fn [new-state] @@ -173,10 +173,10 @@ :type :opacity})) store (ths/setup-store file) rect-1 (cths/get-shape file :rect-1) - events [(wtc/apply-token {:shape-ids [(:id rect-1)] - :attributes #{:opacity} - :token (toht/get-token file :token-target) - :on-update-shape wtch/update-opacity})]] + events [(wtch/apply-token {:shape-ids [(:id rect-1)] + :attributes #{:opacity} + :token (toht/get-token file :token-target) + :on-update-shape wtch/update-opacity})]] (tohs/run-store-async store done events (fn [new-state] @@ -198,10 +198,10 @@ :type :rotation})) store (ths/setup-store file) rect-1 (cths/get-shape file :rect-1) - events [(wtc/apply-token {:shape-ids [(:id rect-1)] - :attributes #{:rotation} - :token (toht/get-token file :token-target) - :on-update-shape wtch/update-rotation})]] + events [(wtch/apply-token {:shape-ids [(:id rect-1)] + :attributes #{:rotation} + :token (toht/get-token file :token-target) + :on-update-shape wtch/update-rotation})]] (tohs/run-store-async store done events (fn [new-state] @@ -220,10 +220,10 @@ store (ths/setup-store file) rect-1 (cths/get-shape file :rect-1) rect-2 (cths/get-shape file :rect-2) - events [(wtc/toggle-token {:shapes [rect-1 rect-2] - :token-type-props {:attributes #{:rx :ry} - :on-update-shape wtch/update-shape-radius-all} - :token (toht/get-token file :token-2)})]] + events [(wtch/toggle-token {:shapes [rect-1 rect-2] + :token-type-props {:attributes #{:rx :ry} + :on-update-shape wtch/update-shape-radius-all} + :token (toht/get-token file :token-2)})]] (tohs/run-store-async store done events (fn [new-state] @@ -253,9 +253,9 @@ rect-without-token (cths/get-shape file :rect-2) rect-with-other-token (cths/get-shape file :rect-3) - events [(wtc/toggle-token {:shapes [rect-with-token rect-without-token rect-with-other-token] - :token (toht/get-token file :token-1) - :token-type-props {:attributes #{:rx :ry}}})]] + events [(wtch/toggle-token {:shapes [rect-with-token rect-without-token rect-with-other-token] + :token (toht/get-token file :token-1) + :token-type-props {:attributes #{:rx :ry}}})]] (tohs/run-store-async store done events (fn [new-state] @@ -287,9 +287,9 @@ rect-without-token (cths/get-shape file :rect-2) rect-with-other-token-2 (cths/get-shape file :rect-3) - events [(wtc/toggle-token {:shapes [rect-with-other-token-1 rect-without-token rect-with-other-token-2] - :token (toht/get-token file :token-1) - :token-type-props {:attributes #{:rx :ry}}})]] + events [(wtch/toggle-token {:shapes [rect-with-other-token-1 rect-without-token rect-with-other-token-2] + :token (toht/get-token file :token-1) + :token-type-props {:attributes #{:rx :ry}}})]] (tohs/run-store-async store done events (fn [new-state] From 37bef1e2ea7e38e4c5d757c66ff2c77535289237 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Wed, 24 Jul 2024 16:05:29 +0200 Subject: [PATCH 53/77] Cleanup --- frontend/src/app/main/ui/workspace/tokens/changes.cljs | 1 - frontend/src/app/main/ui/workspace/tokens/token.cljs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/changes.cljs b/frontend/src/app/main/ui/workspace/tokens/changes.cljs index 3680e3a2c..5f1711675 100644 --- a/frontend/src/app/main/ui/workspace/tokens/changes.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/changes.cljs @@ -15,7 +15,6 @@ [app.main.data.workspace.transforms :as dwt] [app.main.data.workspace.undo :as dwu] [app.main.store :as st] - [app.main.ui.workspace.tokens.core :as wtc] [app.main.ui.workspace.tokens.style-dictionary :as sd] [app.main.ui.workspace.tokens.token :as wtt] [beicon.v2.core :as rx] diff --git a/frontend/src/app/main/ui/workspace/tokens/token.cljs b/frontend/src/app/main/ui/workspace/tokens/token.cljs index 84a994a99..b0d12d220 100644 --- a/frontend/src/app/main/ui/workspace/tokens/token.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/token.cljs @@ -4,7 +4,7 @@ [clojure.set :as set] [cuerdas.core :as str])) -(defn resolve-token-value [{:keys [value resolved-value] :as token}] +(defn resolve-token-value [{:keys [value resolved-value] :as _token}] (or resolved-value (d/parse-double value))) From cb942996a9f31474190b1d73c9689ea53001663e Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Wed, 24 Jul 2024 16:05:43 +0200 Subject: [PATCH 54/77] Fix render-loop after token was deleted --- .../src/app/main/ui/workspace/tokens/context_menu.cljs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index 60729d406..14e1c1935 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -324,6 +324,7 @@ :ref dropdown-ref :style {:top top :left left} :on-context-menu prevent-default} - [:ul {:class (stl/css :context-list)} - [:& menu-tree {:token token - :selected-shapes selected-shapes}]]]])) + (when token + [:ul {:class (stl/css :context-list)} + [:& menu-tree {:token token + :selected-shapes selected-shapes}]])]])) From b9b4abf1e0b9a51db51daa8db203324a7f0e338d Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Wed, 24 Jul 2024 16:10:41 +0200 Subject: [PATCH 55/77] Fix edit modal not opening --- frontend/src/app/main/ui/workspace/tokens/context_menu.cljs | 4 ++-- frontend/src/app/main/ui/workspace/tokens/token_types.cljs | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index 14e1c1935..fcf3d95a7 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -33,7 +33,7 @@ :selected-pred #(seq (% ids-by-attributes))})) (defn generic-attribute-actions [attributes title {:keys [token selected-shapes]}] - (let [{:keys [on-update-shape]} (get wtty/token-types (:type token)) + (let [{:keys [on-update-shape]} (wtty/get-token-properties token) {:keys [selected-pred shape-ids]} (attribute-actions token selected-shapes attributes)] (map (fn [attribute] (let [selected? (selected-pred attribute) @@ -200,7 +200,7 @@ (generic-attribute-actions #{:y} "Y" context-data)))})) (defn default-actions [{:keys [token]}] - (let [{:keys [modal]} (get wtty/token-types token)] + (let [{:keys [modal]} (wtty/get-token-properties token)] [{:title "Delete Token" :action #(st/emit! (dt/delete-token (:id token)))} {:title "Duplicate Token" diff --git a/frontend/src/app/main/ui/workspace/tokens/token_types.cljs b/frontend/src/app/main/ui/workspace/tokens/token_types.cljs index 04ae55aea..d3bb2c0e5 100644 --- a/frontend/src/app/main/ui/workspace/tokens/token_types.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/token_types.cljs @@ -100,5 +100,8 @@ {:label "Text Decoration" :key :text-decoration} {:label "Text Case" :key :text-case}]}}]))) +(defn get-token-properties [token] + (get token-types (:type token))) + (defn token-attributes [token-type] (get-in token-types [token-type :attributes])) From 957ad0dae32d3260923f2913bcfe8f77e8406fd0 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Wed, 24 Jul 2024 16:21:48 +0200 Subject: [PATCH 56/77] Always highlight if one of the attributes is active, but only apply minimal set on pill click --- frontend/src/app/main/ui/workspace/tokens/changes.cljs | 6 +++--- frontend/src/app/main/ui/workspace/tokens/sidebar.cljs | 4 ++-- frontend/src/app/main/ui/workspace/tokens/token_types.cljs | 1 + 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/changes.cljs b/frontend/src/app/main/ui/workspace/tokens/changes.cljs index 5f1711675..b8c610d0a 100644 --- a/frontend/src/app/main/ui/workspace/tokens/changes.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/changes.cljs @@ -71,12 +71,12 @@ (ptk/reify ::on-toggle-token ptk/WatchEvent (watch [_ _ _] - (let [{:keys [attributes on-update-shape]} token-type-props - unapply-tokens? (wtt/shapes-token-applied? token shapes (:attributes token-type-props)) + (let [{:keys [attributes all-attributes on-update-shape]} token-type-props + unapply-tokens? (wtt/shapes-token-applied? token shapes (or all-attributes attributes)) shape-ids (map :id shapes)] (if unapply-tokens? (rx/of - (unapply-token {:attributes attributes + (unapply-token {:attributes (or all-attributes attributes) :token token :shape-ids shape-ids})) (rx/of diff --git a/frontend/src/app/main/ui/workspace/tokens/sidebar.cljs b/frontend/src/app/main/ui/workspace/tokens/sidebar.cljs index 9c2218716..b15f37028 100644 --- a/frontend/src/app/main/ui/workspace/tokens/sidebar.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/sidebar.cljs @@ -72,7 +72,7 @@ [{:keys [type tokens selected-shapes token-type-props]}] (let [open? (mf/deref (-> (l/key type) (l/derived lens:token-type-open-status))) - {:keys [modal attributes title]} token-type-props + {:keys [modal attributes all-attributes title]} token-type-props on-context-menu (mf/use-fn (fn [event token] @@ -124,7 +124,7 @@ [:& token-pill {:key (:id token) :token token - :highlighted? (wtt/shapes-token-applied? token selected-shapes attributes) + :highlighted? (wtt/shapes-token-applied? token selected-shapes (or all-attributes attributes)) :on-click #(on-token-pill-click % token) :on-context-menu #(on-context-menu % token)}])]])]])) diff --git a/frontend/src/app/main/ui/workspace/tokens/token_types.cljs b/frontend/src/app/main/ui/workspace/tokens/token_types.cljs index d3bb2c0e5..01c4c528d 100644 --- a/frontend/src/app/main/ui/workspace/tokens/token_types.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/token_types.cljs @@ -24,6 +24,7 @@ [:sizing {:title "Sizing" :attributes #{:width :height} + :all-attributes ctt/sizing-keys :on-update-shape wtch/update-shape-dimensions :modal {:key :tokens/sizing :fields [{:label "Sizing" From f094654837fd3340884dbed3395a3dbc4d2719fa Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Thu, 25 Jul 2024 08:56:02 +0200 Subject: [PATCH 57/77] Fix only shape ids being applied --- .../app/main/ui/workspace/tokens/changes.cljs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/changes.cljs b/frontend/src/app/main/ui/workspace/tokens/changes.cljs index b8c610d0a..3e76d90e1 100644 --- a/frontend/src/app/main/ui/workspace/tokens/changes.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/changes.cljs @@ -12,6 +12,7 @@ [app.main.data.workspace :as udw] [app.main.data.workspace.changes :as dch] [app.main.data.workspace.shape-layout :as dwsl] + [app.main.data.workspace.state-helpers :as wsh] [app.main.data.workspace.transforms :as dwt] [app.main.data.workspace.undo :as dwu] [app.main.store :as st] @@ -140,11 +141,18 @@ (assoc-in shape [:strokes 0 :stroke-width] value))))) (defn update-layout-spacing [value shape-ids attributes] - (if-let [layout-gap (cond - (:row-gap attributes) {:row-gap value} - (:column-gap attributes) {:column-gap value})] - (dwsl/update-layout shape-ids {:layout-gap layout-gap}) - (dwsl/update-layout shape-ids {:layout-padding (zipmap attributes (repeat value))}))) + (ptk/reify ::update-layout-spacing + ptk/WatchEvent + (watch [_ state _] + (let [layout-shape-ids (-> (wsh/lookup-shapes state shape-ids) + (comp (filter :layout) + (map :id)))] + (rx/of + (if-let [layout-gap (cond + (:row-gap attributes) {:row-gap value} + (:column-gap attributes) {:column-gap value})] + (dwsl/update-layout layout-shape-ids {:layout-gap layout-gap}) + (dwsl/update-layout layout-shape-ids {:layout-padding (zipmap attributes (repeat value))}))))))) (defn update-shape-dimensions [value shape-ids attributes] (ptk/reify ::update-shape-dimensions From a3a48838755b3d56b09457f583a8eb303c29df6b Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Thu, 25 Jul 2024 08:58:26 +0200 Subject: [PATCH 58/77] Cleanup --- frontend/src/app/main/ui/workspace/tokens/core_test.cljs | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 frontend/src/app/main/ui/workspace/tokens/core_test.cljs diff --git a/frontend/src/app/main/ui/workspace/tokens/core_test.cljs b/frontend/src/app/main/ui/workspace/tokens/core_test.cljs deleted file mode 100644 index e69de29bb..000000000 From cc41a42dfae7b682a1b0db0bfbf251c15a6deb30 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Thu, 25 Jul 2024 09:11:03 +0200 Subject: [PATCH 59/77] Update CHANGELOG --- .../app/main/ui/workspace/tokens/CHANGELOG.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/frontend/src/app/main/ui/workspace/tokens/CHANGELOG.md b/frontend/src/app/main/ui/workspace/tokens/CHANGELOG.md index 5574e332c..7347888c4 100644 --- a/frontend/src/app/main/ui/workspace/tokens/CHANGELOG.md +++ b/frontend/src/app/main/ui/workspace/tokens/CHANGELOG.md @@ -18,6 +18,25 @@ If possible add video here from PR as well ## Changes +### 2024-07-25 - UX Improvements for the context menu + +[Link to PR](https://github.com/tokens-studio/tokens-studio-for-penpot/pull/224) + +Changes context menu behavior according to [Specs](https://github.com/tokens-studio/obsidian-docs/blob/31f0d7f98ff5ac922970f3009fe877cc02d6d0cd/Products/TS%20for%20Penpot/Specs/Token%20State%20Specs.md) + +- Removing a token wont update the shape +- Mixed selection (shapes with applied, shapes without applied) will always unapply token +- Multi selection of shapes without token will apply the token to all +- Every shape change and token applying should be one undo step now +- Prevent token applying when nothign is selected +- `All` is a toggle instead of a checkbox if all tokens have been applied + - For instance with border radius the context menu can `:r1 :r2 :r3 :r4` which will highlight `All` + - If one attribute is missing it will check the single attributes + - Clicking a single attribute after clicking `All` will remove the other attributes +- Fixed some issues for switching between split and uniform border radius +- Clicking a token wont apply all attributes anymore. We apply only a select collection of attributes, which makes most sense. For instance on `sizing` we only apply `width` and `height` instead of all (`max-width`, `max-height`, `min-heigt`, `min-width`) + + ### 2024-07-05 - UX Improvements when applying tokens [Link to PR](https://github.com/tokens-studio/tokens-studio-for-penpot/compare/token-studio-develop...ux-improvements?body=&expand=1) From 1e481412e8706f1ccbab4b39ce5602a1e1c588c8 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Thu, 25 Jul 2024 09:12:23 +0200 Subject: [PATCH 60/77] Remove old token applying events --- .../app/main/ui/workspace/tokens/changes.cljs | 36 ------------------- 1 file changed, 36 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/changes.cljs b/frontend/src/app/main/ui/workspace/tokens/changes.cljs index 3e76d90e1..23cf3fc07 100644 --- a/frontend/src/app/main/ui/workspace/tokens/changes.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/changes.cljs @@ -86,23 +86,6 @@ :shape-ids shape-ids :on-update-shape on-update-shape}))))))) -(defn on-apply-token [{:keys [token token-type-props selected-shapes] :as _props}] - (let [{:keys [attributes on-apply on-update-shape] - :or {on-apply dt/update-token-from-attributes}} token-type-props - shape-ids (->> selected-shapes - (eduction - (remove #(wtt/shapes-token-applied? token % attributes)) - (map :id)))] - - (p/let [sd-tokens (sd/resolve-workspace-tokens+ {:debug? true})] - (let [resolved-token (get sd-tokens (:id token)) - resolved-token-value (wtt/resolve-token-value resolved-token)] - (doseq [shape selected-shapes] - (st/emit! (on-apply {:token-id (:id token) - :shape-id (:id shape) - :attributes attributes})) - (on-update-shape resolved-token-value shape-ids attributes)))))) - ;; Shape Updates --------------------------------------------------------------- (defn update-shape-radius-all [value shape-ids] @@ -179,25 +162,6 @@ (doseq [shape-id shape-ids] (st/emit! (dwt/update-position shape-id {(first attributes) value})))) -(defn apply-dimensions-token [{:keys [token-id token-type-props selected-shapes]} attributes] - (let [token (dt/get-token-data-from-token-id token-id) - attributes (set attributes) - updated-token-type-props (cond - (set/superset? #{:x :y} attributes) - (assoc token-type-props - :on-update-shape update-shape-position - :attributes attributes) - - (set/superset? #{:stroke-width} attributes) - (assoc token-type-props - :on-update-shape update-stroke-width - :attributes attributes) - - :else token-type-props)] - (on-apply-token {:token token - :token-type-props updated-token-type-props - :selected-shapes selected-shapes}))) - (defn update-layout-sizing-limits [value shape-ids attributes] (ptk/reify ::update-layout-sizing-limits ptk/WatchEvent From 9bec2509c9be64f68804b7cdd59e6ba599a7af56 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Thu, 25 Jul 2024 10:11:36 +0200 Subject: [PATCH 61/77] Better context-menu position, remove hardcoded value --- .../ui/workspace/tokens/context_menu.cljs | 39 +++++++++---------- .../ui/workspace/tokens/context_menu.scss | 2 - 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index fcf3d95a7..b3768960f 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -237,7 +237,7 @@ (mf/defc menu-entry {::mf/props :obj} - [{:keys [title value on-click selected? children]}] + [{:keys [title value on-click selected? children submenu-offset]}] (let [submenu-ref (mf/use-ref nil) hovering? (mf/use-ref false) on-pointer-enter @@ -275,7 +275,9 @@ [:span {:class (stl/css :submenu-icon)} i/arrow] [:ul {:class (stl/css :token-context-submenu) :ref submenu-ref - :style {:display "none" :left 235} + :style {:display "none" + :top 0 + :left (str submenu-offset "px")} :on-context-menu prevent-default} children]])])) @@ -288,7 +290,8 @@ [:* {:key (str title " " index)} (cond (= :separator entry) [:li {:class (stl/css :separator)}] - submenu [:& menu-entry {:title title} + submenu [:& menu-entry {:title title + :submenu-offset (:submenu-offset context-data)} [:& menu-tree (assoc context-data :type submenu)]] :else [:& menu-entry {:title title @@ -297,27 +300,22 @@ (mf/defc token-context-menu [] - (let [mdata (mf/deref tokens-menu-ref) - top (- (get-in mdata [:position :y]) 20) - left (get-in mdata [:position :x]) - dropdown-ref (mf/use-ref) + (let [mdata (mf/deref tokens-menu-ref) + top (+ (get-in mdata [:position :y]) 5) + left (+ (get-in mdata [:position :x]) 5) + width (mf/use-state 0) + dropdown-ref (mf/use-ref) objects (mf/deref refs/workspace-page-objects) selected (mf/deref refs/selected-shapes) selected-shapes (into [] (keep (d/getf objects)) selected) token-id (:token-id mdata) token (get (mf/deref refs/workspace-tokens) token-id)] - (mf/use-effect - (mf/deps mdata) - #(let [dropdown (mf/ref-val dropdown-ref)] - (when dropdown - (let [bounding-rect (dom/get-bounding-rect dropdown) - window-size (dom/get-window-size) - delta-x (max (- (+ (:right bounding-rect) 250) (:width window-size)) 0) - delta-y (max (- (:bottom bounding-rect) (:height window-size)) 0) - new-style (str "top: " (- top delta-y) "px; " - "left: " (- left delta-x) "px;")] - (when (or (> delta-x 0) (> delta-y 0)) - (.setAttribute ^js dropdown "style" new-style)))))) + + (mf/use-effect + (fn [] + (when-let [node (mf/ref-val dropdown-ref)] + (reset! width (.-offsetWidth node))))) + [:& dropdown {:show (boolean mdata) :on-close #(st/emit! dt/hide-token-context-menu)} [:div {:class (stl/css :token-context-menu) @@ -326,5 +324,6 @@ :on-context-menu prevent-default} (when token [:ul {:class (stl/css :context-list)} - [:& menu-tree {:token token + [:& menu-tree {:submenu-offset @width + :token token :selected-shapes selected-shapes}]])]])) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.scss b/frontend/src/app/main/ui/workspace/tokens/context_menu.scss index 28e3527af..c1d6cc573 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.scss +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.scss @@ -8,8 +8,6 @@ .token-context-menu { position: absolute; - top: $s-40; - left: $s-736; z-index: $z-index-4; } From 1633f8035e496040b28708dea3089b2ba1e23823 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Mon, 29 Jul 2024 13:51:17 +0200 Subject: [PATCH 62/77] Indent --- .../src/app/main/ui/workspace/tokens/context_menu.cljs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index b3768960f..f4b239781 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -310,12 +310,10 @@ selected-shapes (into [] (keep (d/getf objects)) selected) token-id (:token-id mdata) token (get (mf/deref refs/workspace-tokens) token-id)] - - (mf/use-effect - (fn [] - (when-let [node (mf/ref-val dropdown-ref)] - (reset! width (.-offsetWidth node))))) - + (mf/use-effect + (fn [] + (when-let [node (mf/ref-val dropdown-ref)] + (reset! width (.-offsetWidth node))))) [:& dropdown {:show (boolean mdata) :on-close #(st/emit! dt/hide-token-context-menu)} [:div {:class (stl/css :token-context-menu) From 6fc370bb301476013d494d54158411a59892001a Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Mon, 29 Jul 2024 14:22:07 +0200 Subject: [PATCH 63/77] Fix token position wrong, component gets rendered on user mount --- frontend/src/app/main/ui/workspace/tokens/context_menu.cljs | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index f4b239781..344b8b8d2 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -311,6 +311,7 @@ token-id (:token-id mdata) token (get (mf/deref refs/workspace-tokens) token-id)] (mf/use-effect + (mf/deps mdata) (fn [] (when-let [node (mf/ref-val dropdown-ref)] (reset! width (.-offsetWidth node))))) From 55ed79d9680014f8baef150aa268478fdf930dd5 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Mon, 29 Jul 2024 14:22:37 +0200 Subject: [PATCH 64/77] Move to sidebar, should not be rendered in root --- frontend/src/app/main/ui/workspace.cljs | 2 -- frontend/src/app/main/ui/workspace/tokens/sidebar.cljs | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/main/ui/workspace.cljs b/frontend/src/app/main/ui/workspace.cljs index f1a90d8b0..3cfc57b7b 100644 --- a/frontend/src/app/main/ui/workspace.cljs +++ b/frontend/src/app/main/ui/workspace.cljs @@ -30,7 +30,6 @@ [app.main.ui.workspace.sidebar :refer [left-sidebar right-sidebar]] [app.main.ui.workspace.sidebar.collapsable-button :refer [collapsed-button]] [app.main.ui.workspace.sidebar.history :refer [history-toolbox]] - [app.main.ui.workspace.tokens.context-menu :refer [token-context-menu]] [app.main.ui.workspace.tokens.modals] [app.main.ui.workspace.viewport :refer [viewport]] [app.util.debug :as dbg] @@ -206,7 +205,6 @@ :style {:background-color background-color :touch-action "none"}} [:& context-menu] - [:& token-context-menu] (if ^boolean file-ready? [:& workspace-page {:page-id page-id diff --git a/frontend/src/app/main/ui/workspace/tokens/sidebar.cljs b/frontend/src/app/main/ui/workspace/tokens/sidebar.cljs index b15f37028..7cab3ea9a 100644 --- a/frontend/src/app/main/ui/workspace/tokens/sidebar.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/sidebar.cljs @@ -15,6 +15,7 @@ [app.main.ui.icons :as i] [app.main.ui.workspace.sidebar.assets.common :as cmm] [app.main.ui.workspace.tokens.changes :as wtch] + [app.main.ui.workspace.tokens.context-menu :refer [token-context-menu]] [app.main.ui.workspace.tokens.core :as wtc] [app.main.ui.workspace.tokens.style-dictionary :as sd] [app.main.ui.workspace.tokens.token :as wtt] @@ -155,6 +156,7 @@ token-groups (mf/with-memo [tokens] (sorted-token-groups tokens))] [:article + [:& token-context-menu] [:div.assets-bar (for [{:keys [token-key token-type-props tokens]} (concat (:filled token-groups) (:empty token-groups))] From 1eea55ad43bb6686c930f65a346a9612eff3494e Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Mon, 29 Jul 2024 15:09:35 +0200 Subject: [PATCH 65/77] Test --- frontend/src/app/main/ui/workspace/tokens/context_menu.cljs | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index 344b8b8d2..b8a084dd0 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -313,6 +313,7 @@ (mf/use-effect (mf/deps mdata) (fn [] + (js/console.log "(mf/ref-val dropdown-ref)" (mf/ref-val dropdown-ref)) (when-let [node (mf/ref-val dropdown-ref)] (reset! width (.-offsetWidth node))))) [:& dropdown {:show (boolean mdata) From 596d662ca831e68167764850b0c06bb99e88c717 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Mon, 29 Jul 2024 15:24:17 +0200 Subject: [PATCH 66/77] Cleanup --- frontend/src/app/main/ui/workspace/tokens/context_menu.cljs | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index b8a084dd0..344b8b8d2 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -313,7 +313,6 @@ (mf/use-effect (mf/deps mdata) (fn [] - (js/console.log "(mf/ref-val dropdown-ref)" (mf/ref-val dropdown-ref)) (when-let [node (mf/ref-val dropdown-ref)] (reset! width (.-offsetWidth node))))) [:& dropdown {:show (boolean mdata) From 3f14af9e038ab7f07a5df9494cce6a862bb6c164 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Mon, 29 Jul 2024 16:00:46 +0200 Subject: [PATCH 67/77] Fix highlighting for dimensions token --- .../src/app/main/ui/workspace/tokens/token_types.cljs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/token_types.cljs b/frontend/src/app/main/ui/workspace/tokens/token_types.cljs index 01c4c528d..f7df4ba11 100644 --- a/frontend/src/app/main/ui/workspace/tokens/token_types.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/token_types.cljs @@ -1,8 +1,9 @@ (ns app.main.ui.workspace.tokens.token-types (:require + [app.common.data :as d :refer [ordered-map]] [app.common.types.token :as ctt] [app.main.ui.workspace.tokens.changes :as wtch] - [app.common.data :as d :refer [ordered-map]])) + [clojure.set :as set])) (def token-types (ordered-map @@ -32,6 +33,11 @@ [:dimensions {:title "Dimensions" :attributes #{:width :height} + :all-attributes (set/union + ctt/spacing-keys + ctt/sizing-keys + ctt/border-radius-keys + ctt/stroke-width-keys) :on-update-shape wtch/update-shape-dimensions :modal {:key :tokens/dimensions :fields [{:label "Dimensions" From a893a66ec83a7fafaf464f272ba3ebce998456a0 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Mon, 29 Jul 2024 16:07:33 +0200 Subject: [PATCH 68/77] Fix crash on applying col/row gap --- frontend/src/app/main/ui/workspace/tokens/changes.cljs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/changes.cljs b/frontend/src/app/main/ui/workspace/tokens/changes.cljs index 23cf3fc07..05e27b94f 100644 --- a/frontend/src/app/main/ui/workspace/tokens/changes.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/changes.cljs @@ -127,9 +127,10 @@ (ptk/reify ::update-layout-spacing ptk/WatchEvent (watch [_ state _] - (let [layout-shape-ids (-> (wsh/lookup-shapes state shape-ids) - (comp (filter :layout) - (map :id)))] + (let [layout-shape-ids (->> (wsh/lookup-shapes state shape-ids) + (eduction + (filter :layout) + (map :id)))] (rx/of (if-let [layout-gap (cond (:row-gap attributes) {:row-gap value} From c92decedeb9ac7e0c45e1cf7549f730d5947dfa7 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Mon, 29 Jul 2024 16:21:42 +0200 Subject: [PATCH 69/77] Trigger Build From 9ba4776c8edf587b9c2d7fc457eccb89fd25048a Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Mon, 29 Jul 2024 16:34:26 +0200 Subject: [PATCH 70/77] Trigger Build From 70904dbc64dba5f13c8d5a8057a30e0e7894e9cc Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Mon, 29 Jul 2024 16:38:40 +0200 Subject: [PATCH 71/77] Trigger Build From b423a9c782419c5e51cca5ee43ad6b4d0dae526d Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Mon, 29 Jul 2024 17:43:32 +0200 Subject: [PATCH 72/77] Always update all layout-gap on token pill click --- .../app/main/ui/workspace/tokens/changes.cljs | 40 +++++++++---------- .../main/ui/workspace/tokens/token_types.cljs | 2 +- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/changes.cljs b/frontend/src/app/main/ui/workspace/tokens/changes.cljs index 05e27b94f..03901f54c 100644 --- a/frontend/src/app/main/ui/workspace/tokens/changes.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/changes.cljs @@ -123,21 +123,6 @@ (when (seq (:strokes shape)) (assoc-in shape [:strokes 0 :stroke-width] value))))) -(defn update-layout-spacing [value shape-ids attributes] - (ptk/reify ::update-layout-spacing - ptk/WatchEvent - (watch [_ state _] - (let [layout-shape-ids (->> (wsh/lookup-shapes state shape-ids) - (eduction - (filter :layout) - (map :id)))] - (rx/of - (if-let [layout-gap (cond - (:row-gap attributes) {:row-gap value} - (:column-gap attributes) {:column-gap value})] - (dwsl/update-layout layout-shape-ids {:layout-gap layout-gap}) - (dwsl/update-layout layout-shape-ids {:layout-padding (zipmap attributes (repeat value))}))))))) - (defn update-shape-dimensions [value shape-ids attributes] (ptk/reify ::update-shape-dimensions ptk/WatchEvent @@ -146,17 +131,30 @@ (when (:width attributes) (dwt/update-dimensions shape-ids :width value)) (when (:height attributes) (dwt/update-dimensions shape-ids :height value)))))) +(defn- attributes->layout-gap [attributes value] + (let [layout-gap (-> (set/intersection attributes #{:column-gap :row-gap}) + (zipmap (repeat value)))] + {:layout-gap layout-gap})) + +(defn update-layout-spacing [value shape-ids attributes] + (ptk/reify ::update-layout-spacing + ptk/WatchEvent + (watch [_ state _] + (let [layout-shape-ids (->> (wsh/lookup-shapes state shape-ids) + (eduction + (filter :layout) + (map :id))) + layout-attributes (attributes->layout-gap attributes value)] + (rx/of + (dwsl/update-layout layout-shape-ids layout-attributes)))))) + (defn update-layout-spacing-column [value shape-ids] (ptk/reify ::update-layout-spacing-column ptk/WatchEvent - (watch [_ state _] + (watch [_ _ _] (rx/concat (for [shape-id shape-ids] - (let [shape (dt/get-shape-from-state shape-id state) - layout-direction (:layout-flex-dir shape) - layout-update (if (or (= layout-direction :row-reverse) (= layout-direction :row)) - {:layout-gap {:column-gap value}} - {:layout-gap {:row-gap value}})] + (let [layout-update {:layout-gap {:column-gap value :row-gap value}}] (dwsl/update-layout [shape-id] layout-update))))))) (defn update-shape-position [value shape-ids attributes] diff --git a/frontend/src/app/main/ui/workspace/tokens/token_types.cljs b/frontend/src/app/main/ui/workspace/tokens/token_types.cljs index f7df4ba11..436eb00cb 100644 --- a/frontend/src/app/main/ui/workspace/tokens/token_types.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/token_types.cljs @@ -61,7 +61,7 @@ [:spacing {:title "Spacing" :attributes ctt/spacing-keys - :on-update-shape wtch/update-layout-spacing-column + :on-update-shape wtch/update-layout-spacing :modal {:key :tokens/spacing :fields [{:label "Spacing" :key :spacing}]}}] From ededd23849daaffe36c0f094853b88ca802d3e87 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Mon, 29 Jul 2024 17:48:30 +0200 Subject: [PATCH 73/77] Trigger Build From 9340ba9cc05a381912c6da0d1f853b2ebcb2b094 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Mon, 29 Jul 2024 18:30:59 +0200 Subject: [PATCH 74/77] Allow passing custom on-update-shape function --- .../app/main/ui/workspace/tokens/context_menu.cljs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index 344b8b8d2..1afbcad9d 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -32,8 +32,10 @@ :shape-ids shape-ids :selected-pred #(seq (% ids-by-attributes))})) -(defn generic-attribute-actions [attributes title {:keys [token selected-shapes]}] - (let [{:keys [on-update-shape]} (wtty/get-token-properties token) +(defn generic-attribute-actions [attributes title {:keys [token selected-shapes on-update-shape]}] + (let [on-update-shape-fn (or on-update-shape + (-> (wtty/get-token-properties token) + (:on-update-shape))) {:keys [selected-pred shape-ids]} (attribute-actions token selected-shapes attributes)] (map (fn [attribute] (let [selected? (selected-pred attribute) @@ -43,9 +45,9 @@ {:title title :selected? selected? - :action #(if selected? - (st/emit! (wtch/unapply-token props)) - (st/emit! (wtch/apply-token (assoc props :on-update-shape on-update-shape))))})) + :action (if selected? + (st/emit! (wtch/unapply-token props)) + (st/emit! (wtch/apply-token (assoc props :on-update-shape on-update-shape-fn))))})) attributes))) (defn all-or-sepearate-actions [{:keys [attribute-labels on-update-shape-all on-update-shape]} From 5e33eab7d0ef048b73b272f05c416f6603819613 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Mon, 29 Jul 2024 18:31:11 +0200 Subject: [PATCH 75/77] Fix position updating --- frontend/src/app/main/ui/workspace/tokens/changes.cljs | 7 +++++-- .../src/app/main/ui/workspace/tokens/context_menu.cljs | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/changes.cljs b/frontend/src/app/main/ui/workspace/tokens/changes.cljs index 03901f54c..915216bef 100644 --- a/frontend/src/app/main/ui/workspace/tokens/changes.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/changes.cljs @@ -158,8 +158,11 @@ (dwsl/update-layout [shape-id] layout-update))))))) (defn update-shape-position [value shape-ids attributes] - (doseq [shape-id shape-ids] - (st/emit! (dwt/update-position shape-id {(first attributes) value})))) + (ptk/reify ::update-shape-position + ptk/WatchEvent + (watch [_ _ _] + (rx/concat + (map #(dwt/update-position % (zipmap attributes (repeat value))) shape-ids))))) (defn update-layout-sizing-limits [value shape-ids attributes] (ptk/reify ::update-layout-sizing-limits diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index 1afbcad9d..34a6df684 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -198,8 +198,8 @@ {:title "Border Radius" :submenu :border-radius}] (stroke-width context-data) [:separator] - (generic-attribute-actions #{:x} "X" context-data) - (generic-attribute-actions #{:y} "Y" context-data)))})) + (generic-attribute-actions #{:x} "X" (assoc context-data :on-update-shape wtch/update-shape-position)) + (generic-attribute-actions #{:y} "Y" (assoc context-data :on-update-shape wtch/update-shape-position))))})) (defn default-actions [{:keys [token]}] (let [{:keys [modal]} (wtty/get-token-properties token)] From f69db7ce9e7c88030be181d6de3b0a239aea36a6 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Mon, 29 Jul 2024 18:43:47 +0200 Subject: [PATCH 76/77] Cleanup --- frontend/src/app/main/ui/workspace/tokens/token_types.cljs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/frontend/src/app/main/ui/workspace/tokens/token_types.cljs b/frontend/src/app/main/ui/workspace/tokens/token_types.cljs index 436eb00cb..6b6c16916 100644 --- a/frontend/src/app/main/ui/workspace/tokens/token_types.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/token_types.cljs @@ -1,3 +1,9 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS INC + (ns app.main.ui.workspace.tokens.token-types (:require [app.common.data :as d :refer [ordered-map]] From 1d4b4175012e10621bced361333b4a1d82525f8a Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Tue, 30 Jul 2024 06:57:21 +0200 Subject: [PATCH 77/77] Fix missing function shorthand --- .../src/app/main/ui/workspace/tokens/context_menu.cljs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs index 34a6df684..615f758d4 100644 --- a/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/context_menu.cljs @@ -45,9 +45,10 @@ {:title title :selected? selected? - :action (if selected? - (st/emit! (wtch/unapply-token props)) - (st/emit! (wtch/apply-token (assoc props :on-update-shape on-update-shape-fn))))})) + :action (fn [] + (if selected? + (st/emit! (wtch/unapply-token props)) + (st/emit! (wtch/apply-token (assoc props :on-update-shape on-update-shape-fn)))))})) attributes))) (defn all-or-sepearate-actions [{:keys [attribute-labels on-update-shape-all on-update-shape]}