From 791cb7e5fea22f79d5e3b081587ca872476ce580 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Thu, 2 Jan 2025 09:09:11 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Implement=20set=20group=20toggling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/src/app/common/logic/tokens.cljc | 42 +++++++ common/src/app/common/test_helpers/files.cljc | 6 + .../src/app/common/test_helpers/tokens.cljc | 5 +- common/src/app/common/types/tokens_lib.cljc | 35 ++++-- .../test/common_tests/logic/token_test.cljc | 117 ++++++++++++++++++ frontend/src/app/main/data/tokens.cljs | 28 ++--- .../app/main/ui/workspace/tokens/sets.cljs | 31 +++-- 7 files changed, 230 insertions(+), 34 deletions(-) create mode 100644 common/src/app/common/logic/tokens.cljc create mode 100644 common/test/common_tests/logic/token_test.cljc diff --git a/common/src/app/common/logic/tokens.cljc b/common/src/app/common/logic/tokens.cljc new file mode 100644 index 0000000000..d1e7a49d8a --- /dev/null +++ b/common/src/app/common/logic/tokens.cljc @@ -0,0 +1,42 @@ +(ns app.common.logic.tokens + (:require + [app.common.files.changes-builder :as pcb] + [app.common.types.tokens-lib :as ctob])) + +(defn generate-update-active-sets + "Copy the active sets from the currently active themes and move them to the hidden token theme and update the theme with `update-hidden-theme-fn`. + + Use this for managing sets active state without having to modify a user created theme (\"no themes selected\" state in the ui)." + [changes tokens-lib update-hidden-theme-fn] + (let [prev-active-token-themes (ctob/get-active-theme-paths tokens-lib) + active-token-set-names (ctob/get-active-themes-set-names tokens-lib) + + prev-hidden-token-theme (ctob/get-hidden-theme tokens-lib) + hidden-token-theme (-> (or (some-> prev-hidden-token-theme (ctob/set-sets active-token-set-names)) + (ctob/make-hidden-token-theme :sets active-token-set-names)) + (update-hidden-theme-fn)) + + changes (-> changes + (pcb/update-active-token-themes #{ctob/hidden-token-theme-path} prev-active-token-themes)) + + changes (if prev-hidden-token-theme + (pcb/update-token-theme changes hidden-token-theme prev-hidden-token-theme) + (pcb/add-token-theme changes hidden-token-theme))] + changes)) + +(defn generate-toggle-token-set + "Toggle a token set at `set-name` in `tokens-lib` without modifying a user theme." + [changes tokens-lib set-name] + (generate-update-active-sets changes tokens-lib #(ctob/toggle-set % set-name))) + +(defn generate-toggle-token-set-group + "Toggle a token set group at `prefixed-set-path-str` in `tokens-lib` without modifying a user theme." + [changes tokens-lib prefixed-set-path-str] + (let [deactivate? (contains? #{:all :partial} (ctob/sets-at-path-all-active? tokens-lib prefixed-set-path-str)) + sets-names (->> (ctob/get-sets-at-prefix-path tokens-lib prefixed-set-path-str) + (map :name) + (into #{})) + update-fn (if deactivate? + #(ctob/disable-sets % sets-names) + #(ctob/enable-sets % sets-names))] + (generate-update-active-sets changes tokens-lib update-fn))) diff --git a/common/src/app/common/test_helpers/files.cljc b/common/src/app/common/test_helpers/files.cljc index 7d384b0efb..d7c18dcd93 100644 --- a/common/src/app/common/test_helpers/files.cljc +++ b/common/src/app/common/test_helpers/files.cljc @@ -58,6 +58,12 @@ (validate-file! file') file')) +(defn apply-undo-changes + [file changes] + (let [file' (ctf/update-file-data file #(cfc/process-changes % (:undo-changes changes) true))] + (validate-file! file') + file')) + ;; ----- Pages (defn sample-page diff --git a/common/src/app/common/test_helpers/tokens.cljc b/common/src/app/common/test_helpers/tokens.cljc index cc008c70d8..c344f32b7b 100644 --- a/common/src/app/common/test_helpers/tokens.cljc +++ b/common/src/app/common/test_helpers/tokens.cljc @@ -15,6 +15,10 @@ [app.common.types.token :as cto] [app.common.types.tokens-lib :as ctob])) +(defn get-tokens-lib + [file] + (:tokens-lib (ctf/file-data file))) + (defn add-tokens-lib [file] (ctf/update-file-data file #(update % :tokens-lib ctob/ensure-tokens-lib))) @@ -87,4 +91,3 @@ (ctpl/update-page file-data (:id page) #(ctst/set-shape % shape')))))) - diff --git a/common/src/app/common/types/tokens_lib.cljc b/common/src/app/common/types/tokens_lib.cljc index 79f8578dbb..76f653351d 100644 --- a/common/src/app/common/types/tokens_lib.cljc +++ b/common/src/app/common/types/tokens_lib.cljc @@ -455,7 +455,7 @@ When `before-set-name` is nil, move set to bottom") (get-in-set-tree [_ path] "get `path` in nested tree of all sets in the library") (get-sets [_] "get an ordered sequence of all sets in the library") (get-path-sets [_ path] "get an ordered sequence of sets at `path` in the library") - (get-sets-at-prefix-path [_ prefixed-path] "get an ordered sequence of sets at `prefixed-path` in the library") + (get-sets-at-prefix-path [_ prefixed-path-str] "get an ordered sequence of sets at `prefixed-path-str` in the library") (get-sets-at-path [_ path-str] "get an ordered sequence of sets at `path` in the library") (rename-set-group [_ from-path-str to-path-str] "renames set groups and all child set names from `from-path-str` to `to-path-str`") (get-ordered-set-names [_] "get an ordered sequence of all sets names in the library") @@ -504,12 +504,13 @@ When `before-set-name` is nil, move set to bottom") (def hidden-token-theme-path (token-theme-path hidden-token-theme-group hidden-token-theme-name)) - (defprotocol ITokenTheme (set-sets [_ set-names] "set the active token sets") + (enable-set [_ set-name] "enable set in theme") + (enable-sets [_ set-names] "enable sets in theme") (disable-set [_ set-name] "disable set in theme") + (disable-sets [_ set-names] "disable sets in theme") (toggle-set [_ set-name] "toggle a set enabled / disabled in the theme") - (update-set-name [_ prev-set-name set-name] "update set-name from `prev-set-name` to `set-name` when it exists") (theme-path [_] "get `token-theme-path` from theme") (theme-matches-group-name [_ group name] "if a theme matches the given group & name") @@ -525,13 +526,22 @@ When `before-set-name` is nil, move set to bottom") (dt/now) set-names)) + (enable-set [this set-name] + (set-sets this (conj sets set-name))) + + (enable-sets [this set-names] + (set-sets this (set/union sets set-names))) + (disable-set [this set-name] (set-sets this (disj sets set-name))) + (disable-sets [this set-names] + (set-sets this (or (set/difference sets set-names) #{}))) + (toggle-set [this set-name] - (set-sets this (if (sets set-name) - (disj sets set-name) - (conj sets set-name)))) + (if (sets set-name) + (disable-set this set-name) + (enable-set this set-name))) (update-set-name [this prev-set-name set-name] (if (get sets prev-set-name) @@ -610,6 +620,8 @@ When `before-set-name` is nil, move set to bottom") (get-theme-tree [_] "get a nested tree of all themes in the library") (get-themes [_] "get an ordered sequence of all themes in the library") (get-theme [_ group name] "get one theme looking for name") + (get-hidden-theme [_] "get the theme hidden from the user , +used for managing active sets without a user created theme.") (get-theme-groups [_] "get a sequence of group names by order") (get-active-theme-paths [_] "get the active theme paths") (get-active-themes [_] "get an ordered sequence of active themes in the library") @@ -791,8 +803,8 @@ Will return a value that matches this schema: (tree-seq d/ordered-map? vals) (filter (partial instance? TokenSet)))) - (get-sets-at-prefix-path [_ prefixed-path] - (some->> (get-in sets (split-token-set-path prefixed-path)) + (get-sets-at-prefix-path [_ prefixed-path-str] + (some->> (get-in sets (split-token-set-path prefixed-path-str)) (tree-seq d/ordered-map? vals) (filter (partial instance? TokenSet)))) @@ -881,6 +893,9 @@ Will return a value that matches this schema: (get-theme [_ group name] (dm/get-in themes [group name])) + (get-hidden-theme [this] + (get-theme this hidden-token-theme-group hidden-token-theme-name)) + (set-active-themes [_ active-themes] (TokensLib. sets themes @@ -946,10 +961,10 @@ Will return a value that matches this schema: (mapcat :sets) (get-active-themes this))) - (sets-at-path-all-active? [this prefixed-path] + (sets-at-path-all-active? [this prefixed-path-str] (let [active-set-names (get-active-themes-set-names this)] (if (seq active-set-names) - (let [path-active-set-names (->> (get-sets-at-prefix-path this prefixed-path) + (let [path-active-set-names (->> (get-sets-at-prefix-path this prefixed-path-str) (map :name) (into #{})) difference (set/difference path-active-set-names active-set-names)] diff --git a/common/test/common_tests/logic/token_test.cljc b/common/test/common_tests/logic/token_test.cljc new file mode 100644 index 0000000000..c91235d8f6 --- /dev/null +++ b/common/test/common_tests/logic/token_test.cljc @@ -0,0 +1,117 @@ +(ns common-tests.logic.token-test + (:require + [app.common.files.changes-builder :as pcb] + [app.common.logic.tokens :as clt] + [app.common.test-helpers.files :as thf] + [app.common.test-helpers.ids-map :as thi] + [app.common.test-helpers.tokens :as tht] + [app.common.types.tokens-lib :as ctob] + [clojure.test :as t])) + +(t/use-fixtures :each thi/test-fixture) + +(defn- setup-file [lib-fn] + (-> (thf/sample-file :file1) + (tht/add-tokens-lib) + (tht/update-tokens-lib lib-fn))) + +(t/deftest generate-toggle-token-set-test + (t/testing "toggling an active set will switch to hidden theme without user sets" + (let [file (setup-file #(-> % + (ctob/add-set (ctob/make-token-set :name "foo/bar")) + (ctob/add-theme (ctob/make-token-theme :name "theme" + :sets #{"foo/bar"})) + (ctob/set-active-themes #{"/theme"}))) + changes (clt/generate-toggle-token-set (pcb/empty-changes) (tht/get-tokens-lib file) "foo/bar") + + redo (thf/apply-changes file changes) + redo-lib (tht/get-tokens-lib redo) + undo (thf/apply-undo-changes redo changes) + undo-lib (tht/get-tokens-lib undo)] + (t/is (= #{ctob/hidden-token-theme-path} (ctob/get-active-theme-paths redo-lib))) + (t/is (= #{} (:sets (ctob/get-hidden-theme redo-lib)))) + + ;; Undo + (t/is (nil? (ctob/get-hidden-theme undo-lib))) + (t/is (= #{"/theme"} (ctob/get-active-theme-paths undo-lib))))) + + (t/testing "toggling an inactive set will switch to hidden theme without user sets" + (let [file (setup-file #(-> % + (ctob/add-set (ctob/make-token-set :name "foo/bar")) + (ctob/add-theme (ctob/make-token-theme :name "theme" + :sets #{"foo/bar"})) + (ctob/set-active-themes #{"/theme"}))) + changes (clt/generate-toggle-token-set (pcb/empty-changes) (tht/get-tokens-lib file) "foo/bar") + + redo (thf/apply-changes file changes) + redo-lib (tht/get-tokens-lib redo) + undo (thf/apply-undo-changes redo changes) + undo-lib (tht/get-tokens-lib undo)] + (t/is (= #{ctob/hidden-token-theme-path} (ctob/get-active-theme-paths redo-lib))) + (t/is (= #{} (:sets (ctob/get-hidden-theme redo-lib)))) + + ;; Undo + (t/is (nil? (ctob/get-hidden-theme undo-lib))) + (t/is (= #{"/theme"} (ctob/get-active-theme-paths undo-lib))))) + + (t/testing "toggling an set with hidden theme already active will toggle set in hidden theme" + (let [file (setup-file #(-> % + (ctob/add-set (ctob/make-token-set :name "foo/bar")) + (ctob/add-theme (ctob/make-hidden-token-theme)) + (ctob/set-active-themes #{ctob/hidden-token-theme-path}))) + + changes (clt/generate-toggle-token-set-group (pcb/empty-changes) (tht/get-tokens-lib file) "G-foo/S-bar") + + redo (thf/apply-changes file changes) + redo-lib (tht/get-tokens-lib redo) + undo (thf/apply-undo-changes redo changes) + undo-lib (tht/get-tokens-lib undo)] + (t/is (= (ctob/get-active-theme-paths redo-lib) (ctob/get-active-theme-paths undo-lib))) + + (t/is (= #{"foo/bar"} (:sets (ctob/get-hidden-theme redo-lib)))) + + ;; Undo + (t/is (some? (ctob/get-hidden-theme undo-lib)))))) + +(t/deftest generate-toggle-token-set-group-test + (t/testing "toggling set group with no active sets inside will activate all child sets" + (let [file (setup-file #(-> % + (ctob/add-set (ctob/make-token-set :name "foo/bar")) + (ctob/add-set (ctob/make-token-set :name "foo/bar/baz")) + (ctob/add-set (ctob/make-token-set :name "foo/bar/baz/baz-child")) + (ctob/add-theme (ctob/make-token-theme :name "theme")) + (ctob/set-active-themes #{"/theme"}))) + changes (clt/generate-toggle-token-set-group (pcb/empty-changes) (tht/get-tokens-lib file) "G-foo/G-bar") + + redo (thf/apply-changes file changes) + redo-lib (tht/get-tokens-lib redo) + undo (thf/apply-undo-changes redo changes) + undo-lib (tht/get-tokens-lib undo)] + (t/is (= #{ctob/hidden-token-theme-path} (ctob/get-active-theme-paths redo-lib))) + (t/is (= #{"foo/bar/baz" "foo/bar/baz/baz-child"} (:sets (ctob/get-hidden-theme redo-lib)))) + + ;; Undo + (t/is (nil? (ctob/get-hidden-theme undo-lib))) + (t/is (= #{"/theme"} (ctob/get-active-theme-paths undo-lib))))) + + (t/testing "toggling set group with partially active sets inside will deactivate all child sets" + (let [file (setup-file #(-> % + (ctob/add-set (ctob/make-token-set :name "foo/bar")) + (ctob/add-set (ctob/make-token-set :name "foo/bar/baz")) + (ctob/add-set (ctob/make-token-set :name "foo/bar/baz/baz-child")) + (ctob/add-theme (ctob/make-token-theme :name "theme" + :sets #{"foo/bar/baz"})) + (ctob/set-active-themes #{"/theme"}))) + + changes (clt/generate-toggle-token-set-group (pcb/empty-changes) (tht/get-tokens-lib file) "G-foo/G-bar") + + redo (thf/apply-changes file changes) + redo-lib (tht/get-tokens-lib redo) + undo (thf/apply-undo-changes redo changes) + undo-lib (tht/get-tokens-lib undo)] + (t/is (= #{} (:sets (ctob/get-hidden-theme redo-lib)))) + (t/is (= #{ctob/hidden-token-theme-path} (ctob/get-active-theme-paths redo-lib))) + + ;; Undo + (t/is (nil? (ctob/get-hidden-theme undo-lib))) + (t/is (= #{"/theme"} (ctob/get-active-theme-paths undo-lib)))))) diff --git a/frontend/src/app/main/data/tokens.cljs b/frontend/src/app/main/data/tokens.cljs index d0bbc890b5..e70f557bf8 100644 --- a/frontend/src/app/main/data/tokens.cljs +++ b/frontend/src/app/main/data/tokens.cljs @@ -9,6 +9,7 @@ [app.common.data.macros :as dm] [app.common.files.changes-builder :as pcb] [app.common.geom.point :as gpt] + [app.common.logic.tokens :as clt] [app.common.types.shape :as cts] [app.common.types.tokens-lib :as ctob] [app.main.data.changes :as dch] @@ -158,22 +159,19 @@ (defn toggle-token-set [{:keys [token-set-name]}] (ptk/reify ::toggle-token-set ptk/WatchEvent - (watch [it state _] - (let [tokens-lib (get-tokens-lib state) - prev-theme (ctob/get-theme tokens-lib ctob/hidden-token-theme-group ctob/hidden-token-theme-name) - active-token-set-names (ctob/get-active-themes-set-names tokens-lib) - theme (-> (or (some-> prev-theme - (ctob/set-sets active-token-set-names)) - (ctob/make-hidden-token-theme :sets active-token-set-names)) - (ctob/toggle-set token-set-name)) - prev-active-token-themes (ctob/get-active-theme-paths tokens-lib) - changes (-> (pcb/empty-changes it) - (pcb/update-active-token-themes #{(ctob/token-theme-path ctob/hidden-token-theme-group ctob/hidden-token-theme-name)} prev-active-token-themes)) - changes' (if prev-theme - (pcb/update-token-theme changes theme prev-theme) - (pcb/add-token-theme changes theme))] + (watch [_ state _] + (let [changes (clt/generate-toggle-token-set (pcb/empty-changes) (get-tokens-lib state) token-set-name)] (rx/of - (dch/commit-changes changes') + (dch/commit-changes changes) + (wtu/update-workspace-tokens)))))) + +(defn toggle-token-set-group [{:keys [prefixed-path-str]}] + (ptk/reify ::toggle-token-set-group + ptk/WatchEvent + (watch [_ state _] + (let [changes (clt/generate-toggle-token-set-group (pcb/empty-changes) (get-tokens-lib state) prefixed-path-str)] + (rx/of + (dch/commit-changes changes) (wtu/update-workspace-tokens)))))) (defn import-tokens-lib [lib] diff --git a/frontend/src/app/main/ui/workspace/tokens/sets.cljs b/frontend/src/app/main/ui/workspace/tokens/sets.cljs index c9b7b1b356..cc6de31eb2 100644 --- a/frontend/src/app/main/ui/workspace/tokens/sets.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/sets.cljs @@ -25,6 +25,9 @@ (defn on-toggle-token-set-click [token-set-name] (st/emit! (wdt/toggle-token-set {:token-set-name token-set-name}))) +(defn on-toggle-token-set-group-click [prefixed-path-str] + (st/emit! (wdt/toggle-token-set-group {:prefixed-path-str prefixed-path-str}))) + (defn on-select-token-set-click [tree-path] (st/emit! (wdt/set-selected-token-set-path tree-path))) @@ -88,7 +91,7 @@ :id (if mixed? ic/remove ic/tick)}])])) (mf/defc sets-tree-set-group - [{:keys [label tree-depth tree-path active? selected? collapsed? editing? on-edit on-edit-reset on-edit-submit]}] + [{:keys [label tree-depth tree-path active? selected? collapsed? editing? on-toggle on-edit on-edit-reset on-edit-submit]}] (let [editing?' (editing? tree-path) active?' (active? tree-path) on-context-menu @@ -102,7 +105,7 @@ (wdt/show-token-set-context-menu {:position (dom/get-client-position event) :prefixed-set-path tree-path}))))) - on-click + on-collapse-click (mf/use-fn (fn [event] (dom/stop-propagation event) @@ -111,6 +114,10 @@ (mf/use-fn (mf/deps tree-path) #(on-edit tree-path)) + on-checkbox-click + (mf/use-fn + (mf/deps on-toggle tree-path) + #(on-toggle tree-path)) on-edit-submit' (mf/use-fn (mf/deps tree-path on-edit-submit) @@ -124,7 +131,7 @@ :on-context-menu on-context-menu} [:> icon-button* {:class (stl/css :set-item-group-collapse-button) - :on-click on-click + :on-click on-collapse-click :aria-label (tr "labels.collapse") :icon (if @collapsed? "arrow-right" "arrow-down") :variant "action"}] @@ -139,7 +146,8 @@ :on-double-click on-double-click} label] [:& checkbox - {:checked (case active?' + {:on-click on-checkbox-click + :checked (case active?' :all true :partial "mixed" :none false) @@ -172,6 +180,7 @@ (mf/deps tree-path) #(on-edit tree-path)) on-checkbox-click (mf/use-fn + (mf/deps set-name) (fn [event] (dom/stop-propagation event) (on-toggle set-name))) @@ -214,7 +223,8 @@ on-edit-submit-set on-edit-submit-group on-select - on-toggle + on-toggle-set + on-toggle-set-group selected? set-node set-path @@ -243,7 +253,7 @@ :tree-path (or tree-path set-path) :tree-depth tree-depth :editing? editing? - :on-toggle on-toggle + :on-toggle on-toggle-set :on-edit on-edit :on-edit-reset on-edit-reset :on-edit-submit on-edit-submit-set}] @@ -257,6 +267,7 @@ :tree-path (or tree-path set-path) :tree-depth tree-depth :editing? editing? + :on-toggle on-toggle-set-group :on-edit on-edit :on-edit-reset on-edit-reset :on-edit-submit on-edit-submit-group}]) @@ -271,7 +282,8 @@ :tree-path tree-path' :on-select on-select :selected? selected? - :on-toggle on-toggle + :on-toggle-set on-toggle-set + :on-toggle-set-group on-toggle-set-group :active? active? :group-active? group-active? :editing? editing? @@ -289,6 +301,7 @@ token-set-group-active? on-create-token-set on-toggle-token-set + on-toggle-token-set-group origin on-select context] @@ -310,7 +323,8 @@ :on-select on-select :active? token-set-active? :group-active? token-set-group-active? - :on-toggle on-toggle-token-set + :on-toggle-set on-toggle-token-set + :on-toggle-set-group on-toggle-token-set-group :editing? editing? :on-edit on-edit :on-edit-reset on-reset @@ -352,6 +366,7 @@ :on-select on-select-token-set-click :origin "set-panel" :on-toggle-token-set on-toggle-token-set-click + :on-toggle-token-set-group on-toggle-token-set-group-click :on-update-token-set on-update-token-set :on-update-token-set-group on-update-token-set-group :on-create-token-set on-create-token-set}]))