diff --git a/common/src/app/common/types/tokens_lib.cljc b/common/src/app/common/types/tokens_lib.cljc index 8bf9a9ff20..011aae0892 100644 --- a/common/src/app/common/types/tokens_lib.cljc +++ b/common/src/app/common/types/tokens_lib.cljc @@ -44,17 +44,13 @@ (defn group-item "Add a group to the item name, in the form group.name." [item group-name separator] - (dm/assert! - "expected groupable item" - (valid-groupable-item? item)) - (update item :name #(str group-name separator %))) + (assert (valid-groupable-item? item) "expected groupable item") + (update item :name #(dm/str group-name separator %))) (defn ungroup-item "Remove the first group from the item name." [item separator] - (dm/assert! - "expected groupable item" - (valid-groupable-item? item)) + (assert (valid-groupable-item? item) "expected groupable item") (update item :name #(-> % (split-path separator) (rest) @@ -63,9 +59,7 @@ (defn get-path "Get the groups part of the name as a vector. E.g. group.subgroup.name -> ['group' 'subgroup']" [item separator] - (dm/assert! - "expected groupable item" - (valid-groupable-item? item)) + (assert (valid-groupable-item? item) "expected groupable item") (split-path (:name item) separator)) (defn get-groups-str @@ -78,9 +72,7 @@ (defn get-final-name "Get the final part of the name. E.g. group.subgroup.name -> name" [item separator] - (dm/assert! - "expected groupable item" - (valid-groupable-item? item)) + (assert (valid-groupable-item? item) "expected groupable item") (-> (:name item) (split-path separator) (last))) @@ -94,9 +86,7 @@ "Get all children of a group of a grouping tree. Each child is a tuple [name item], where item " [group] - (dm/assert! - "expected group node" - (group? group)) + (assert (group? group) "expected group node") (seq group)) ;; === Token @@ -114,36 +104,37 @@ (defrecord Token [name type value description modified-at]) +(defn token? + [o] + (instance? Token o)) + +(def schema:token-attrs + [:map {:title "Token"} + [:name cto/token-name-ref] + [:type [::sm/one-of cto/token-types]] + [:value :any] + [:description [:maybe :string]] + [:modified-at ::sm/inst]]) + (def schema:token [:and - [:map {:title "Token"} - [:name cto/token-name-ref] - [:type [::sm/one-of cto/token-types]] - [:value :any] - [:description [:maybe :string]] - [:modified-at ::sm/inst]] - [:fn (partial instance? Token)]]) + schema:token-attrs + [:fn token?]]) -(sm/register! ::token schema:token) +(def check-token + (sm/check-fn schema:token :hint "expected valid token")) -(def valid-token? - (sm/validator schema:token)) - -(def check-token! - (sm/check-fn ::token)) +(def ^:private check-token-attrs + (sm/check-fn schema:token-attrs :hint "expected valid params for token")) (defn make-token - [& {:keys [] :as params}] - (let [params (-> params - (dissoc :id) ;; we will remove this when old data structures are removed - (update :modified-at #(or % (dt/now)))) - token (map->Token params)] - - (dm/assert! - "expected valid token" - (check-token! token)) - - token)) + [& {:as attrs}] + (-> attrs + (dissoc :id) ;; we will remove this when old data structures are removed + (update :modified-at #(or % (dt/now))) + (update :description d/nilv "") + (check-token-attrs) + (map->Token))) (defn find-token-value-references "Returns set of token references found in `token-value`. @@ -336,17 +327,16 @@ tokens)) (add-token [_ token] - (dm/assert! "expected valid token" (check-token! token)) - (TokenSet. name - description - (dt/now) - (assoc tokens (:name token) token))) + (let [token (check-token token)] + (TokenSet. name + description + (dt/now) + (assoc tokens (:name token) token)))) (update-token [this token-name f] (if-let [token (get tokens token-name)] (let [token' (-> (make-token (f token)) (assoc :modified-at (dt/now)))] - (check-token! token') (TokenSet. name description (dt/now) @@ -382,36 +372,44 @@ "$type" (cto/token-type->dtcg-token-type (:type token))} (:description token) (assoc "$description" (:description token))))))) +(defn token-set? + [o] + (instance? TokenSet o)) + +(def schema:token-set-attrs + [:map {:title "TokenSet"} + [:name :string] + [:description [:maybe :string]] + [:modified-at ::sm/inst] + [:tokens [:and + [:map-of {:gen/max 5} :string schema:token] + [:fn d/ordered-map?]]]]) + (def schema:token-set - [:and [:map {:title "TokenSet"} - [:name :string] - [:description [:maybe :string]] - [:modified-at ::sm/inst] - [:tokens [:and [:map-of {:gen/max 5} :string ::token] - [:fn d/ordered-map?]]]] - [:fn (partial instance? TokenSet)]]) + [:and + schema:token-set-attrs + [:fn token-set?]]) (sm/register! ::token-set schema:token-set) (def valid-token-set? (sm/validator schema:token-set)) -(def check-token-set! - (sm/check-fn ::token-set)) +(def check-token-set + (sm/check-fn schema:token-set :hint "expected valid token set")) + +(def ^:private check-token-set-attrs + (sm/check-fn schema:token-set-attrs :hint "expected valid params for token-set")) (defn make-token-set - [& {:keys [] :as params}] - (let [params (-> params - (dissoc :id) - (update :modified-at #(or % (dt/now))) - (update :tokens #(into (d/ordered-map) %))) - token-set (map->TokenSet params)] - - (dm/assert! - "expected valid token set" - (check-token-set! token-set)) - - token-set)) + [& {:as attrs}] + (-> attrs + (dissoc :id) + (update :modified-at #(or % (dt/now))) + (update :tokens #(into (d/ordered-map) %)) + (update :description d/nilv "") + (check-token-set-attrs) + (map->TokenSet))) ;; === TokenSets (collection) @@ -455,22 +453,16 @@ [:fn d/ordered-map?]]]}} [:ref ::node]]) -(sm/register! ::token-set-node schema:token-set-node) - (def schema:token-sets [:and [:map-of {:title "TokenSets"} - :string ::token-set-node] + :string + schema:token-set-node] [:fn d/ordered-map?]]) -(sm/register! ::token-sets schema:token-sets) - (def valid-token-sets? (sm/validator schema:token-sets)) -(def check-token-sets! - (sm/check-fn ::token-sets)) - ;; === TokenTheme (def theme-separator "/") @@ -549,23 +541,34 @@ (hidden-temporary-theme? [this] (theme-matches-group-name this hidden-token-theme-group hidden-token-theme-name))) +(defn token-theme? + [o] + (instance? TokenTheme o)) + +(def schema:token-theme-attrs + [:map {:title "TokenTheme"} + [:name :string] + [:group :string] + [:description [:maybe :string]] + [:is-source [:maybe :boolean]] + [:modified-at ::sm/inst] + [:sets [:set {:gen/max 5} :string]]]) + (def schema:token-theme - [:and [:map {:title "TokenTheme"} - [:name :string] - [:group :string] - [:description [:maybe :string]] - [:is-source [:maybe :boolean]] - [:modified-at ::sm/inst] - [:sets [:set {:gen/max 5} :string]]] - [:fn (partial instance? TokenTheme)]]) + [:and + schema:token-theme-attrs + [:fn token-theme?]]) (sm/register! ::token-theme schema:token-theme) (def valid-token-theme? (sm/validator schema:token-theme)) -(def check-token-theme! - (sm/check-fn ::token-theme)) +(def check-token-theme + (sm/check-fn schema:token-theme :hint "expected a valid token-theme")) + +(def ^:private check-token-theme-attrs + (sm/check-fn schema:token-theme-attrs :hint "expected valid params for token-theme")) (def top-level-theme-group-name "Top level theme groups have an empty string as the theme group." @@ -575,26 +578,23 @@ (= group top-level-theme-group-name)) (defn make-token-theme - [& {:keys [] :as params}] - (let [params (-> params - (dissoc :id) - (update :group #(or % top-level-theme-group-name)) - (update :is-source #(or % false)) - (update :modified-at #(or % (dt/now))) - (update :sets #(into #{} %))) - token-theme (map->TokenTheme params)] - - (dm/assert! - "expected valid token theme" - (check-token-theme! token-theme)) - - token-theme)) + [& {:as attrs}] + (-> attrs + (dissoc :id) + (update :group d/nilv top-level-theme-group-name) + (update :is-source d/nilv false) + (update :modified-at #(or % (dt/now))) + (update :sets set) + (update :description d/nilv "") + (check-token-theme-attrs) + (map->TokenTheme))) (defn make-hidden-token-theme - [& {:keys [] :as params}] - (make-token-theme (assoc params - :group hidden-token-theme-group - :name hidden-token-theme-name))) + [& {:as attrs}] + (-> attrs + (assoc :group hidden-token-theme-group) + (assoc :name hidden-token-theme-name) + (make-token-theme))) ;; === TokenThemes (collection) @@ -606,8 +606,7 @@ (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-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") @@ -620,23 +619,18 @@ used for managing active sets without a user created theme.") (def schema:token-themes [:and [:map-of {:title "TokenThemes"} - :string [:and [:map-of :string ::token-theme] + :string [:and [:map-of :string schema:token-theme] [:fn d/ordered-map?]]] [:fn d/ordered-map?]]) -(sm/register! ::token-themes schema:token-themes) - (def valid-token-themes? (sm/validator schema:token-themes)) -(def check-token-themes! - (sm/check-fn ::token-themes)) - -(def schema:active-token-themes - [:set string?]) +(def ^:private schema:active-themes + [:set :string]) (def valid-active-token-themes? - (sm/validator schema:active-token-themes)) + (sm/validator schema:active-themes)) ;; === Import / Export from DTCG format @@ -869,17 +863,14 @@ Will return a value that matches this schema: ITokenSets (add-set [_ token-set] - (dm/assert! "expected valid token set" (check-token-set! token-set)) - (let [path (get-token-set-prefixed-path token-set)] + (let [path (get-token-set-prefixed-path token-set) + token-set (check-token-set token-set)] (TokensLib. (d/oassoc-in sets path token-set) themes active-themes))) (add-sets [this token-sets] - (reduce - (fn [lib set] - (add-set lib set)) - this token-sets)) + (reduce add-set this token-sets)) (update-set [this set-name f] (let [prefixed-full-path (set-name->prefixed-full-path set-name) @@ -889,7 +880,6 @@ Will return a value that matches this schema: (assoc :modified-at (dt/now))) prefixed-full-path' (get-token-set-prefixed-path set') name-changed? (not= (:name set) (:name set'))] - (check-token-set! set') (if name-changed? (TokensLib. (-> sets (d/oassoc-in-before prefixed-full-path prefixed-full-path' set') @@ -1061,10 +1051,10 @@ Will return a value that matches this schema: ITokenThemes (add-theme [_ token-theme] - (dm/assert! "expected valid token theme" (check-token-theme! token-theme)) - (TokensLib. sets - (update themes (:group token-theme) d/oassoc (:name token-theme) token-theme) - active-themes)) + (let [token-theme (check-token-theme token-theme)] + (TokensLib. sets + (update themes (:group token-theme) d/oassoc (:name token-theme) token-theme) + active-themes))) (update-theme [this group name f] (let [theme (dm/get-in themes [group name])] @@ -1076,7 +1066,6 @@ Will return a value that matches this schema: same-group? (= group group') same-name? (= name name') same-path? (and same-group? same-name?)] - (check-token-theme! theme') (TokensLib. sets (if same-path? (update themes group' assoc name' theme') @@ -1164,7 +1153,6 @@ Will return a value that matches this schema: (some? (get-in sets (set-group-path->set-group-prefixed-path set-path)))) (add-token-in-set [this set-name token] - (dm/assert! "expected valid token instance" (check-token! token)) (update-set this set-name #(add-token % token))) (get-token-in-set [this set-name token-name] @@ -1304,6 +1292,7 @@ Will return a value that matches this schema: (into tokens' (map (fn [x] [(:name x) x]) (get-tokens set)))) {} (get-sets this))) + ;; FIXME: revisit if this still necessary (validate [_] (and (valid-token-sets? sets) (valid-token-themes? themes) @@ -1314,11 +1303,14 @@ Will return a value that matches this schema: (and (instance? TokensLib o) (validate o))) -(defn check-tokens-lib! - [lib] - (dm/assert! - "expected valid tokens lib" - (valid-tokens-lib? lib))) +(def ^:private check-token-sets + (sm/check-fn schema:token-sets :hint "expected valid token sets")) + +(def ^:private check-token-themes + (sm/check-fn schema:token-themes :hint "expected valid token themes")) + +(def ^:private check-active-themes + (sm/check-fn schema:active-themes :hint "expected valid active themes")) (defn make-tokens-lib "Create an empty or prepopulated tokens library." @@ -1333,13 +1325,11 @@ Will return a value that matches this schema: :active-themes #{})) ([& {:keys [sets themes active-themes]}] - (let [tokens-lib (TokensLib. sets themes (or active-themes #{}))] - - (dm/assert! - "expected valid tokens lib" - (valid-tokens-lib? tokens-lib)) - - tokens-lib))) + (let [active-themes (d/nilv active-themes #{})] + (TokensLib. + (check-token-sets sets) + (check-token-themes themes) + (check-active-themes active-themes))))) (defn ensure-tokens-lib [tokens-lib] @@ -1353,10 +1343,11 @@ Will return a value that matches this schema: (def type:tokens-lib {:type ::tokens-lib :pred valid-tokens-lib? - :type-properties {:encode/json encode-dtcg - :decode/json decode-dtcg}}) + :type-properties + {:encode/json encode-dtcg + :decode/json decode-dtcg}}) -(sm/register! ::tokens-lib type:tokens-lib) +(sm/register! type:tokens-lib) ;; === Serialization handlers for RPC API (transit) and database (fressian) @@ -1369,17 +1360,17 @@ Will return a value that matches this schema: {:id "penpot/token-set" :class TokenSet :wfn #(into {} %) - :rfn #(make-token-set %)} + :rfn #(map->TokenSet %)} {:id "penpot/token-theme" :class TokenTheme :wfn #(into {} %) - :rfn #(make-token-theme %)} + :rfn #(map->TokenTheme %)} {:id "penpot/token" :class Token :wfn #(into {} %) - :rfn #(make-token %)}) + :rfn #(map->Token %)}) #?(:clj (fres/add-handlers! diff --git a/common/test/common_tests/types/tokens_lib_test.cljc b/common/test/common_tests/types/tokens_lib_test.cljc index a7ba89d47a..e6b60cfbbf 100644 --- a/common/test/common_tests/types/tokens_lib_test.cljc +++ b/common/test/common_tests/types/tokens_lib_test.cljc @@ -37,23 +37,21 @@ (t/is (= (:name token1) "test-token-1")) (t/is (= (:type token1) :boolean)) (t/is (= (:value token1) true)) - (t/is (nil? (:description token1))) + (t/is (= (:description token1) "")) (t/is (some? (:modified-at token1))) - (t/is (ctob/valid-token? token1)) + (t/is (ctob/check-token token1)) (t/is (= (:name token2) "test-token-2")) (t/is (= (:type token2) :numeric)) (t/is (= (:value token2) 66)) (t/is (= (:description token2) "test description")) (t/is (= (:modified-at token2) now)) - (t/is (ctob/valid-token? token2)))) + (t/is (ctob/check-token token2)))) (t/testing "invalid-tokens" - (let [args {:name 777 - :type :invalid}] - (t/is (thrown-with-msg? #?(:cljs js/Error :clj Exception) #"expected valid token" - (apply ctob/make-token args))) - (t/is (false? (ctob/valid-token? {}))))) + (let [params {:name 777 :type :invalid}] + (t/is (thrown-with-msg? #?(:cljs js/Error :clj Exception) #"expected valid params for token" + (ctob/make-token params))))) (t/testing "find-token-value-references" (t/testing "finds references inside curly braces in a string" @@ -75,7 +73,7 @@ :tokens [])] (t/is (= (:name token-set1) "test-token-set-1")) - (t/is (nil? (:description token-set1))) + (t/is (= (:description token-set1) "")) (t/is (some? (:modified-at token-set1))) (t/is (empty? (:tokens token-set1))) @@ -85,10 +83,9 @@ (t/is (empty? (:tokens token-set2))))) (t/testing "invalid-token-set" - (let [args {:name 777 - :description 999}] - (t/is (thrown-with-msg? #?(:cljs js/Error :clj Exception) #"expected valid token set" - (apply ctob/make-token-set args))))) + (let [params {:name 777 :description 999}] + (t/is (thrown-with-msg? #?(:cljs js/Error :clj Exception) #"expected valid params for token-set" + (ctob/make-token-set params))))) (t/testing "move-token-set" (t/testing "flat" @@ -192,7 +189,7 @@ (t/is (= (:name token-theme1) "test-token-theme-1")) (t/is (= (:group token-theme1) "")) - (t/is (nil? (:description token-theme1))) + (t/is (= (:description token-theme1) "")) (t/is (false? (:is-source token-theme1))) (t/is (some? (:modified-at token-theme1))) (t/is (empty? (:sets token-theme1))) @@ -205,12 +202,12 @@ (t/is (empty? (:sets token-theme2))))) (t/testing "invalid-token-theme" - (let [args {:name 777 - :group nil - :description 999 - :is-source 42}] - (t/is (thrown-with-msg? #?(:cljs js/Error :clj Exception) #"expected valid token theme" - (apply ctob/make-token-theme args)))))) + (let [params {:name 777 + :group nil + :description 999 + :is-source 42}] + (t/is (thrown-with-msg? #?(:cljs js/Error :clj Exception) #"expected valid params for token-theme" + (ctob/make-token-theme params)))))) (t/deftest tokens-lib @@ -219,10 +216,9 @@ (t/is (= (ctob/set-count tokens-lib) 0)))) (t/testing "invalid-tokens-lib" - (let [args {:sets nil - :themes nil}] - (t/is (thrown-with-msg? #?(:cljs js/Error :clj Exception) #"expected valid tokens lib" - (apply ctob/make-tokens-lib args)))))) + (let [params {:sets nil :themes nil}] + (t/is (thrown-with-msg? #?(:cljs js/Error :clj Exception) #"expected valid token sets" + (ctob/make-tokens-lib params)))))) (t/testing "token-set in a lib" @@ -413,7 +409,7 @@ (t/is (= (count (:tokens token-set')) 2)) (t/is (= (d/index-of (keys (:tokens token-set')) "updated-name") 0)) (t/is (= (:name token') "updated-name")) - (t/is (= (:description token') nil)) + (t/is (= (:description token') "")) (t/is (= (:value token') true)) (t/is (dt/is-after? (:modified-at token-set') (:modified-at token-set))) (t/is (dt/is-after? (:modified-at token') (:modified-at token))))) @@ -757,7 +753,7 @@ (t/is (= (ctob/set-count tokens-lib') 1)) (t/is (= (:name token') "group1.updated-name")) - (t/is (= (:description token') nil)) + (t/is (= (:description token') "")) (t/is (= (:value token') true)) (t/is (dt/is-after? (:modified-at token-set') (:modified-at token-set))) (t/is (dt/is-after? (:modified-at token') (:modified-at token))))) @@ -792,7 +788,7 @@ (t/is (= (ctob/set-count tokens-lib') 1)) (t/is (= (d/index-of (keys (:tokens token-set')) "group2.updated-name") 1)) (t/is (= (:name token') "group2.updated-name")) - (t/is (= (:description token') nil)) + (t/is (= (:description token') "")) (t/is (= (:value token') true)) (t/is (dt/is-after? (:modified-at token-set') (:modified-at token-set))) (t/is (dt/is-after? (:modified-at token') (:modified-at token))))) @@ -930,7 +926,7 @@ (t/is (= (count group1') 3)) (t/is (= (d/index-of (keys group1') "S-updated-name") 0)) (t/is (= (:name token-set') "group1/updated-name")) - (t/is (= (:description token-set') nil)) + (t/is (= (:description token-set') "")) (t/is (dt/is-after? (:modified-at token-set') (:modified-at token-set))) sets-tree')) @@ -961,7 +957,7 @@ (t/is (= (count group2') 1)) (t/is (nil? (get group1' "S-updated-name"))) (t/is (= (:name token-set') "group2/updated-name")) - (t/is (= (:description token-set') nil)) + (t/is (= (:description token-set') "")) (t/is (dt/is-after? (:modified-at token-set') (:modified-at token-set))))) (t/testing "delete-set-in-group" @@ -1097,7 +1093,7 @@ (t/is (= (d/index-of (keys group1') "updated-name") 0)) (t/is (= (:name token-theme') "updated-name")) (t/is (= (:group token-theme') "group1")) - (t/is (= (:description token-theme') nil)) + (t/is (= (:description token-theme') "")) (t/is (dt/is-after? (:modified-at token-theme') (:modified-at token-theme))))) (t/testing "move-theme-of-group" @@ -1127,7 +1123,7 @@ (t/is (= (d/index-of (keys group2') "updated-name") 0)) (t/is (= (:name token-theme') "updated-name")) (t/is (= (:group token-theme') "group2")) - (t/is (= (:description token-theme') nil)) + (t/is (= (:description token-theme') "")) (t/is (dt/is-after? (:modified-at token-theme') (:modified-at token-theme))))) (t/testing "delete-theme-in-group" @@ -1166,7 +1162,7 @@ {:name "colors.red.600" :type :color :value "#e53e3e" - :description nil})) + :description ""})) (t/is (= (get-set-token "core" "spacing.multi-value") {:name "spacing.multi-value" :type :spacing @@ -1176,7 +1172,7 @@ {:name "button.primary.background" :type :color :value "{accent.default}" - :description nil}))) + :description ""}))) (t/testing "invalid tokens got discarded" (t/is (nil? (get-set-token "typography" "H1.Bold")))))))) @@ -1200,7 +1196,7 @@ {:name "colors.red.600" :type :color :value "#e53e3e" - :description nil})) + :description ""})) (t/is (tht/token-data-eq? (get-set-token "core" "spacing.multi-value") {:name "spacing.multi-value" :type :spacing @@ -1210,7 +1206,7 @@ {:name "button.primary.background" :type :color :value "{accent.default}" - :description nil}))) + :description ""}))) (t/testing "invalid tokens got discarded" (t/is (nil? (get-set-token "typography" "H1.Bold")))))) @@ -1238,26 +1234,29 @@ :group "group-1" :modified-at now :sets #{"core"}))) - expected (ctob/encode-dtcg tokens-lib)] - (t/is (= {"$themes" [{"description" nil - "group" "group-1" - "is-source" false - "modified-at" now - "name" "theme-1" - "selectedTokenSets" {"core" "enabled"}}] - "$metadata" {"tokenSetOrder" ["core"]} - "core" - {"colors" {"red" {"600" {"$value" "#e53e3e" - "$type" "color"}}} - "spacing" - {"multi-value" - {"$value" "{dimension.sm} {dimension.xl}" - "$type" "spacing" - "$description" "You can have multiple values in a single spacing token"}} - "button" - {"primary" {"background" {"$value" "{accent.default}" - "$type" "color"}}}}} - expected)))) + result (ctob/encode-dtcg tokens-lib) + expected {"$themes" [{"description" "" + "group" "group-1" + "is-source" false + "modified-at" now + "name" "theme-1" + "selectedTokenSets" {"core" "enabled"}}] + "$metadata" {"tokenSetOrder" ["core"]} + "core" + {"colors" {"red" {"600" {"$value" "#e53e3e" + "$type" "color" + "$description" ""}}} + "spacing" + {"multi-value" + {"$value" "{dimension.sm} {dimension.xl}" + "$type" "spacing" + "$description" "You can have multiple values in a single spacing token"}} + "button" + {"primary" {"background" {"$value" "{accent.default}" + "$type" "color" + "$description" ""}}}}}] + + (t/is (= expected result)))) (t/testing "encode-decode-dtcg-json" (with-redefs [dt/now (constantly #inst "2024-10-16T12:01:20.257840055-00:00")] diff --git a/frontend/src/app/main/data/tokens.cljs b/frontend/src/app/main/data/tokens.cljs index 576a3eb94b..9d78af382d 100644 --- a/frontend/src/app/main/data/tokens.cljs +++ b/frontend/src/app/main/data/tokens.cljs @@ -18,7 +18,6 @@ [app.main.data.helpers :as dsh] [app.main.data.notifications :as ntf] [app.main.data.workspace.shapes :as dwsh] - [app.main.data.workspace.tokens.selected-set :as dwts] [app.main.ui.workspace.tokens.update :as wtu] [app.util.i18n :refer [tr]] [beicon.v2.core :as rx] @@ -38,6 +37,14 @@ (-> (dsh/lookup-file-data state) (get :tokens-lib))) +(defn lookup-token-set + ([state] + (when-let [selected (dm/get-in state [:workspace-tokens :selected-token-set-name])] + (lookup-token-set state selected))) + ([state name] + (some-> (get-tokens-lib state) + (ctob/get-set name)))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -233,49 +240,72 @@ (rx/of (drop-error (ex-data e)))))))) -;; FIXME: the the name is very confusing -(defn update-create-token - [{:keys [token prev-token-name]}] - (ptk/reify ::update-create-token +(defn- create-token-with-set + "A special case when a first token is created and no set exists" + [token] + (ptk/reify ::create-token-and-set + ptk/WatchEvent + (watch [_ _ _] + (let [set-name "Global" + + token-set + (-> (ctob/make-token-set :name set-name) + (ctob/add-token token)) + + hidden-theme + (ctob/make-hidden-token-theme :sets [set-name]) + + changes + (pcb/add-token-set (pcb/empty-changes) token-set) + + changes + (-> changes + (pcb/add-token-theme hidden-theme) + (pcb/update-active-token-themes #{ctob/hidden-token-theme-path} #{}))] + + (rx/of (set-selected-token-set-name set-name) + (dch/commit-changes changes)))))) + +(defn create-token + [params] + (let [token (ctob/make-token params)] + (ptk/reify ::create-token + ptk/WatchEvent + (watch [it state _] + (if-let [token-set (lookup-token-set state)] + (let [data (dsh/lookup-file-data state) + changes (-> (pcb/empty-changes it) + (pcb/with-library-data data) + (pcb/set-token (:name token-set) + (:name token) + token))] + + (rx/of (dch/commit-changes changes) + (ptk/data-event ::ev/event {::ev/name "create-token"}))) + + (rx/of (create-token-with-set token))))))) + +(defn update-token + [name params] + (assert (string? name) "expected string for `name`") + + (ptk/reify ::update-token ptk/WatchEvent (watch [it state _] - (let [data (dsh/lookup-file-data state) - selected (dm/get-in state [:workspace-tokens :selected-token-set-name]) + (let [token-set (lookup-token-set state) + data (dsh/lookup-file-data state) + token (ctob/get-token token-set name) + token' (->> (merge token params) + (into {}) + (ctob/make-token)) - tokens-lib (get-tokens-lib state) - token-set (if selected - (some-> tokens-lib (ctob/get-set selected)) - (some-> tokens-lib (ctob/get-sets) (first))) + changes (-> (pcb/empty-changes it) + (pcb/with-library-data data) + (pcb/set-token (:name token-set) + (:name token) + token'))] - set-name (or (:name token-set) "Global") - changes (if (not token-set) - ;; No set created add a global set - (let [token-set (ctob/make-token-set :name set-name :tokens {(:name token) token}) - hidden-theme (ctob/make-hidden-token-theme :sets [set-name]) - active-theme-paths (some-> tokens-lib ctob/get-active-theme-paths) - add-to-hidden-theme? (= active-theme-paths #{ctob/hidden-token-theme-path}) - base-changes (pcb/add-token-set (pcb/empty-changes) token-set)] - (cond - (not tokens-lib) - (-> base-changes - (pcb/add-token-theme hidden-theme) - (pcb/update-active-token-themes #{ctob/hidden-token-theme-path} #{})) - - add-to-hidden-theme? - (let [prev-hidden-theme (ctob/get-theme tokens-lib ctob/hidden-token-theme-group ctob/hidden-token-theme-name)] - (-> base-changes - (pcb/update-token-theme (ctob/toggle-set prev-hidden-theme ctob/hidden-token-theme-path) prev-hidden-theme))) - - :else base-changes)) - (-> (pcb/empty-changes it) - (pcb/with-library-data data) - (pcb/set-token set-name (or prev-token-name (:name token)) token)))] - - (rx/of - (set-selected-token-set-name set-name) - (when-not prev-token-name - (ptk/event ::ev/event {::ev/name "create-tokens"})) - (dch/commit-changes changes)))))) + (rx/of (dch/commit-changes changes)))))) (defn delete-token [set-name token-name] @@ -296,21 +326,23 @@ (ptk/reify ::duplicate-token ptk/WatchEvent (watch [_ state _] - (let [token-set (dwts/get-selected-token-set state) - token (some-> token-set (ctob/get-token token-name)) - tokens (some-> token-set (ctob/get-tokens)) - suffix-fn (fn [copy-count] - (let [suffix (tr "workspace.token.duplicate-suffix")] - (str/concat "-" - suffix - (when (> copy-count 1) - (str "-" copy-count))))) - unames (map :name tokens) - copy-name (cfh/generate-unique-name token-name unames :suffix-fn suffix-fn)] - (when token - (rx/of - (update-create-token - {:token (assoc token :name copy-name)}))))))) + (when-let [token-set (lookup-token-set state)] + (when-let [token (ctob/get-token token-set token-name)] + (let [tokens (ctob/get-tokens token-set) + unames (map :name tokens) + + suffix-fn + (fn [copy-count] + (let [suffix (tr "workspace.token.duplicate-suffix")] + (str/concat "-" + suffix + (when (> copy-count 1) + (str "-" copy-count))))) + + copy-name + (cfh/generate-unique-name token-name unames :suffix-fn suffix-fn)] + + (rx/of (create-token (assoc token :name copy-name))))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; TOKEN UI OPS diff --git a/frontend/src/app/main/ui/workspace/tokens/form.cljs b/frontend/src/app/main/ui/workspace/tokens/form.cljs index 85c8530c3a..42558070c1 100644 --- a/frontend/src/app/main/ui/workspace/tokens/form.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/form.cljs @@ -397,13 +397,20 @@ ;; The result should be a vector of all resolved validations ;; We do not handle the error case as it will be handled by the components validations (when (and (seq result) (not err)) - (st/emit! (dt/update-create-token {:token (ctob/make-token :name final-name - :type (or (:type token) token-type) - :value final-value - :description final-description) - :prev-token-name (:name token)})) - (st/emit! (wtu/update-workspace-tokens)) - (modal/hide!)))))))) + (st/emit! + (if (ctob/token? token) + (dt/update-token (:name token) + {:name final-name + :value final-value + :description final-description}) + + (dt/create-token {:name final-name + :type token-type + :value final-value + :description final-description})) + (wtu/update-workspace-tokens) + (modal/hide))))))))) + on-delete-token (mf/use-fn (mf/deps selected-token-set-name) diff --git a/frontend/test/frontend_tests/logic/components_and_tokens.cljs b/frontend/test/frontend_tests/logic/components_and_tokens.cljs index 1d8cf116d0..e441f51d8f 100644 --- a/frontend/test/frontend_tests/logic/components_and_tokens.cljs +++ b/frontend/test/frontend_tests/logic/components_and_tokens.cljs @@ -203,10 +203,11 @@ store (ths/setup-store file) ;; ==== Action - events [(dt/update-create-token {:token (ctob/make-token :name "test-token-1" - :type :border-radius - :value 66) - :prev-token-name "test-token-1"})] + events [(dt/set-selected-token-set-name "test-token-set") + (dt/update-token "test-token-1" + {:name "test-token-1" + :type :border-radius + :value 66})] step2 (fn [_] (let [events2 [(wtu/update-workspace-tokens) @@ -358,30 +359,25 @@ store (ths/setup-store file) ;; ==== Action - events [(dt/update-create-token {:token (ctob/make-token :name "token-radius" - :type :border-radius - :value 30) - :prev-token-name "token-radius"}) - (dt/update-create-token {:token (ctob/make-token :name "token-rotation" - :type :rotation - :value 45) - :prev-token-name "token-rotation"}) - (dt/update-create-token {:token (ctob/make-token :name "token-opacity" - :type :opacity - :value 0.9) - :prev-token-name "token-opacity"}) - (dt/update-create-token {:token (ctob/make-token :name "token-stroke-width" - :type :stroke-width - :value 8) - :prev-token-name "token-stroke-width"}) - (dt/update-create-token {:token (ctob/make-token :name "token-color" - :type :color - :value "#ff0000") - :prev-token-name "token-color"}) - (dt/update-create-token {:token (ctob/make-token :name "token-dimensions" - :type :dimensions - :value 200) - :prev-token-name "token-dimensions"})] + events [(dt/set-selected-token-set-name "test-token-set") + (dt/update-token "token-radius" + {:name "token-radius" + :value 30}) + (dt/update-token "token-rotation" + {:name "token-rotation" + :value 45}) + (dt/update-token "token-opacity" + {:name "token-opacity" + :value 0.9}) + (dt/update-token "token-stroke-width" + {:name "token-stroke-width" + :value 8}) + (dt/update-token "token-color" + {:name "token-color" + :value "#ff0000"}) + (dt/update-token "token-dimensions" + {:name "token-dimensions" + :value 200})] step2 (fn [_] (let [events2 [(wtu/update-workspace-tokens) @@ -391,7 +387,7 @@ (fn [new-state] (let [;; ==== Get file' (ths/get-file-from-state new-state) - frame1' (cths/get-shape file' :frame1) + frame1' (cths/get-shape file' :frame1) c-frame1' (cths/get-shape file' :c-frame1) tokens-frame1' (:applied-tokens c-frame1')]