Merge pull request #290 from tokens-studio/refactor-themes-sets

Refactor themes sets
This commit is contained in:
Florian Schrödl 2024-10-03 13:26:27 +02:00 committed by GitHub
commit d58932c2e5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
34 changed files with 1028 additions and 1074 deletions

View file

@ -258,7 +258,7 @@
[:update-active-token-themes [:update-active-token-themes
[:map {:title "UpdateActiveTokenThemes"} [:map {:title "UpdateActiveTokenThemes"}
[:type [:= :update-active-token-themes]] [:type [:= :update-active-token-themes]]
[:theme-ids [:set ::sm/uuid]]]] [:theme-ids [:set :string]]]]
[:delete-temporary-token-theme [:delete-temporary-token-theme
[:map {:title "DeleteTemporaryTokenThemeChange"} [:map {:title "DeleteTemporaryTokenThemeChange"}
@ -274,14 +274,14 @@
[:mod-token-theme [:mod-token-theme
[:map {:title "ModTokenThemeChange"} [:map {:title "ModTokenThemeChange"}
[:type [:= :mod-token-theme]] [:type [:= :mod-token-theme]]
[:id ::sm/uuid] [:group :string]
[:name :string] [:name :string]
[:token-theme ::ctot/token-theme]]] [:token-theme ::ctot/token-theme]]]
[:del-token-theme [:del-token-theme
[:map {:title "DelTokenThemeChange"} [:map {:title "DelTokenThemeChange"}
[:type [:= :del-token-theme]] [:type [:= :del-token-theme]]
[:id ::sm/uuid] [:group :string]
[:name :string]]] [:name :string]]]
[:add-token-set [:add-token-set
@ -292,29 +292,24 @@
[:mod-token-set [:mod-token-set
[:map {:title "ModTokenSetChange"} [:map {:title "ModTokenSetChange"}
[:type [:= :mod-token-set]] [:type [:= :mod-token-set]]
[:id ::sm/uuid]
[:name :string] [:name :string]
[:token-set ::ctot/token-set]]] [:token-set ::ctot/token-set]]]
[:del-token-set [:del-token-set
[:map {:title "DelTokenSetChange"} [:map {:title "DelTokenSetChange"}
[:type [:= :del-token-set]] [:type [:= :del-token-set]]
[:id ::sm/uuid]
[:name :string]]] [:name :string]]]
[:add-token [:add-token
[:map {:title "AddTokenChange"} [:map {:title "AddTokenChange"}
[:type [:= :add-token]] [:type [:= :add-token]]
[:set-id ::sm/uuid]
[:set-name :string] [:set-name :string]
[:token ::cto/token]]] [:token ::cto/token]]]
[:mod-token [:mod-token
[:map {:title "ModTokenChange"} [:map {:title "ModTokenChange"}
[:type [:= :mod-token]] [:type [:= :mod-token]]
[:set-id ::sm/uuid]
[:set-name :string] [:set-name :string]
[:id ::sm/uuid]
[:name :string] [:name :string]
[:token ::cto/token]]] [:token ::cto/token]]]
@ -322,7 +317,6 @@
[:map {:title "DelTokenChange"} [:map {:title "DelTokenChange"}
[:type [:= :del-token]] [:type [:= :del-token]]
[:set-name :string] [:set-name :string]
[:id ::sm/uuid]
[:name :string]]]]]) [:name :string]]]]])
(sm/register! ::changes (sm/register! ::changes
@ -793,131 +787,89 @@
;; -- Tokens ;; -- Tokens
(defmethod process-change :add-token (defmethod process-change :add-token
[data {:keys [set-id set-name token]}] [data {:keys [set-name token]}]
(-> data (update data :tokens-lib #(-> %
(ctol/add-token set-id token) (ctob/ensure-tokens-lib)
(update :tokens-lib (ctob/add-token-in-set set-name (ctob/make-token token)))))
#(-> %
(ctob/ensure-tokens-lib)
(ctob/add-token-in-set set-name (ctob/make-token token))))))
(defmethod process-change :mod-token (defmethod process-change :mod-token
[data {:keys [set-name id name token]}] [data {:keys [set-name name token]}]
(-> data (update data :tokens-lib #(-> %
(ctol/update-token id merge token) (ctob/ensure-tokens-lib)
(update :tokens-lib (ctob/update-token-in-set
#(-> % set-name
(ctob/ensure-tokens-lib) name
(ctob/update-token-in-set (fn [old-token]
set-name (ctob/make-token (merge old-token token)))))))
name
(fn [old-token]
(ctob/make-token (merge old-token token))))))))
(defmethod process-change :del-token (defmethod process-change :del-token
[data {:keys [set-name id name]}] [data {:keys [set-name name]}]
(-> data (update data :tokens-lib #(-> %
(ctol/delete-token id) (ctob/ensure-tokens-lib)
(update :tokens-lib (ctob/delete-token-from-set
#(-> % set-name
(ctob/ensure-tokens-lib) name))))
(ctob/delete-token-from-set
set-name
name)))))
(defn- set-ids->names
[data sets]
(let [lib-sets (:token-sets-index data)
set-id->name
(fn [set-id]
(dm/get-in lib-sets [set-id :name]))]
(map set-id->name sets)))
(defmethod process-change :add-temporary-token-theme (defmethod process-change :add-temporary-token-theme
[data {:keys [token-theme]}] [data {:keys [token-theme]}]
(-> data (update data :tokens-lib #(-> %
(ctotl/add-temporary-token-theme token-theme) (ctob/ensure-tokens-lib)
(update :tokens-lib (ctob/add-theme (ctob/make-token-theme token-theme)))))
#(-> %
(ctob/ensure-tokens-lib)
(ctob/add-theme (-> token-theme
(update :sets (partial set-ids->names data))
(ctob/make-token-theme)))))))
(defmethod process-change :update-active-token-themes (defmethod process-change :update-active-token-themes
[data {:keys [theme-ids]}] [data {:keys [theme-ids]}]
(ctotl/assoc-active-token-themes data theme-ids)) (update data :tokens-lib #(-> % (ctob/ensure-tokens-lib)
(ctob/set-active-themes theme-ids))))
(defmethod process-change :delete-temporary-token-theme (defmethod process-change :delete-temporary-token-theme
[data {:keys [id group name]}] [data {:keys [group name]}]
(-> data (update data :tokens-lib #(-> %
(ctotl/delete-temporary-token-theme id) (ctob/ensure-tokens-lib)
(update :tokens-lib (ctob/delete-theme group name))))
#(-> %
(ctob/ensure-tokens-lib)
(ctob/delete-theme group name)))))
(defmethod process-change :add-token-theme (defmethod process-change :add-token-theme
[data {:keys [token-theme]}] [data {:keys [token-theme]}]
(-> data (update data :tokens-lib #(-> %
(ctotl/add-token-theme token-theme) (ctob/ensure-tokens-lib)
(update :tokens-lib (ctob/add-theme (-> token-theme
#(-> % (ctob/make-token-theme))))))
(ctob/ensure-tokens-lib)
(ctob/add-theme (-> token-theme
(update :sets (partial set-ids->names data))
(ctob/make-token-theme)))))))
(defmethod process-change :mod-token-theme (defmethod process-change :mod-token-theme
[data {:keys [id name group token-theme]}] [data {:keys [name group token-theme]}]
(-> data (update data :tokens-lib #(-> %
(ctotl/update-token-theme id merge token-theme) (ctob/ensure-tokens-lib)
(update :tokens-lib (ctob/update-theme group name
#(-> % (fn [prev-theme]
(ctob/ensure-tokens-lib) (merge prev-theme token-theme))))))
(ctob/update-theme name group
(fn [prev-theme]
(merge prev-theme
(-> token-theme
(update :sets (partial set-ids->names data))))))))))
(defmethod process-change :del-token-theme (defmethod process-change :del-token-theme
[data {:keys [id group name]}] [data {:keys [group name]}]
(-> data (update data :tokens-lib #(-> %
(ctotl/delete-token-theme id) (ctob/ensure-tokens-lib)
(update :tokens-lib (ctob/delete-theme group name))))
#(-> %
(ctob/ensure-tokens-lib)
(ctob/delete-theme group name)))))
(defmethod process-change :add-token-set (defmethod process-change :add-token-set
[data {:keys [token-set]}] [data {:keys [token-set]}]
(-> data (update data :tokens-lib #(-> %
(ctotl/add-token-set token-set) (ctob/ensure-tokens-lib)
(update :tokens-lib (ctob/add-set (ctob/make-token-set token-set)))))
#(-> %
(ctob/ensure-tokens-lib)
(ctob/add-set (ctob/make-token-set token-set))))))
(defmethod process-change :mod-token-set (defmethod process-change :mod-token-set
[data {:keys [id name token-set]}] [data {:keys [name token-set]}]
(-> data (update data :tokens-lib (fn [lib]
(ctotl/update-token-set id merge token-set) (let [path-changed? (not= name (:name token-set))
(update :tokens-lib lib' (-> lib
#(-> % (ctob/ensure-tokens-lib)
(ctob/ensure-tokens-lib) (ctob/update-set name (fn [prev-set]
(ctob/update-set name (fn [prev-set] (merge prev-set (dissoc token-set :tokens)))))]
(merge prev-set (cond-> lib'
(dissoc token-set :tokens)))))))) path-changed? (ctob/update-set-name name (:name token-set)))))))
(defmethod process-change :del-token-set (defmethod process-change :del-token-set
[data {:keys [id name]}] [data {:keys [name]}]
(-> data (update data :tokens-lib #(-> %
(ctotl/delete-token-set id) (ctob/ensure-tokens-lib)
(update :tokens-lib (ctob/delete-set name))))
#(-> %
(ctob/ensure-tokens-lib)
(ctob/delete-set name)))))
;; === Operations ;; === Operations
(defmethod process-operation :set (defmethod process-operation :set

View file

@ -20,7 +20,8 @@
[app.common.types.component :as ctk] [app.common.types.component :as ctk]
[app.common.types.file :as ctf] [app.common.types.file :as ctf]
[app.common.types.shape.layout :as ctl] [app.common.types.shape.layout :as ctl]
[app.common.uuid :as uuid])) [app.common.uuid :as uuid]
[app.common.types.tokens-lib :as ctob]))
;; Auxiliary functions to help create a set of changes (undo + redo) ;; Auxiliary functions to help create a set of changes (undo + redo)
@ -713,23 +714,28 @@
[changes token-theme] [changes token-theme]
(-> changes (-> changes
(update :redo-changes conj {:type :add-token-theme :token-theme token-theme}) (update :redo-changes conj {:type :add-token-theme :token-theme token-theme})
(update :undo-changes conj {:type :del-token-theme :id (:id token-theme) :name (:name token-theme)}) (update :undo-changes conj {:type :del-token-theme :group (:group token-theme) :name (:name token-theme)})
(apply-changes-local))) (apply-changes-local)))
(defn update-token-theme (defn update-token-theme
[changes token-theme prev-token-theme] [changes token-theme prev-token-theme]
(-> changes (let [name (or (:name prev-token-theme)
(update :redo-changes conj {:type :mod-token-theme :id (:id token-theme) :name (:name prev-token-theme) :token-theme token-theme}) (:name token-theme))
(update :undo-changes conj {:type :mod-token-theme :id (:id token-theme) :name (:name token-theme) :token-theme (or prev-token-theme token-theme)}) group (or (:group prev-token-theme)
(apply-changes-local))) (:group token-theme))]
(-> changes
(update :redo-changes conj {:type :mod-token-theme :group group :name name :token-theme token-theme})
(update :undo-changes conj {:type :mod-token-theme :group group :name name :token-theme (or prev-token-theme token-theme)})
(apply-changes-local))))
(defn delete-token-theme (defn delete-token-theme
[changes token-theme-id] [changes group name]
(assert-library! changes) (assert-library! changes)
(let [library-data (::library-data (meta changes)) (let [library-data (::library-data (meta changes))
prev-token-theme (get-in library-data [:token-themes-index token-theme-id])] prev-token-theme (some-> (get library-data :tokens-lib)
(ctob/get-theme group name))]
(-> changes (-> changes
(update :redo-changes conj {:type :del-token-theme :id token-theme-id :name (:name prev-token-theme)}) (update :redo-changes conj {:type :del-token-theme :group group :name name})
(update :undo-changes conj {:type :add-token-theme :token-theme prev-token-theme}) (update :undo-changes conj {:type :add-token-theme :token-theme prev-token-theme})
(apply-changes-local)))) (apply-changes-local))))
@ -737,48 +743,51 @@
[changes token-set] [changes token-set]
(-> changes (-> changes
(update :redo-changes conj {:type :add-token-set :token-set token-set}) (update :redo-changes conj {:type :add-token-set :token-set token-set})
(update :undo-changes conj {:type :del-token-set :id (:id token-set) :name (:name token-set)}) (update :undo-changes conj {:type :del-token-set :name (:name token-set)})
(apply-changes-local))) (apply-changes-local)))
(defn update-token-set (defn update-token-set
[changes token-set prev-token-set] [changes token-set prev-token-set]
(-> changes (-> changes
(update :redo-changes conj {:type :mod-token-set :id (:id token-set) :name (:name prev-token-set) :token-set token-set}) (update :redo-changes conj {:type :mod-token-set :name (:name prev-token-set) :token-set token-set})
(update :undo-changes conj {:type :mod-token-set :id (:id token-set) :name (:name prev-token-set) :token-set (or prev-token-set token-set)}) (update :undo-changes conj {:type :mod-token-set :name (:name token-set) :token-set (or prev-token-set token-set)})
(apply-changes-local))) (apply-changes-local)))
(defn delete-token-set (defn delete-token-set
[changes token-set-id token-set-name] [changes token-set-name]
(assert-library! changes) (assert-library! changes)
(let [library-data (::library-data (meta changes)) (let [library-data (::library-data (meta changes))
prev-token-set (get-in library-data [:token-sets-index token-set-id])] prev-token-theme (some-> (get library-data :tokens-lib)
(ctob/get-set token-set-name))]
(-> changes (-> changes
(update :redo-changes conj {:type :del-token-set :id token-set-id :name token-set-name}) (update :redo-changes conj {:type :del-token-set :name token-set-name})
(update :undo-changes conj {:type :add-token-set :token-set prev-token-set}) (update :undo-changes conj {:type :add-token-set :token-set prev-token-theme})
(apply-changes-local)))) (apply-changes-local))))
(defn add-token (defn add-token
[changes set-id set-name token] [changes set-name token]
(-> changes (-> changes
(update :redo-changes conj {:type :add-token :set-id set-id :set-name set-name :token token}) (update :redo-changes conj {:type :add-token :set-name set-name :token token})
(update :undo-changes conj {:type :del-token :set-name set-name :id (:id token) :name (:name token)}) (update :undo-changes conj {:type :del-token :set-name set-name :name (:name token)})
(apply-changes-local))) (apply-changes-local)))
(defn update-token (defn update-token
[changes set-id set-name {:keys [id name] :as token} {prev-name :name :as prev-token}] [changes set-name token prev-token]
(-> changes (-> changes
(update :redo-changes conj {:type :mod-token :set-id set-id :set-name set-name :id id :name prev-name :token token}) (update :redo-changes conj {:type :mod-token :set-name set-name :name (:name prev-token) :token token})
(update :undo-changes conj {:type :mod-token :set-id set-id :set-name set-name :id id :name name :token (or prev-token token)}) (update :undo-changes conj {:type :mod-token :set-name set-name :name (:name token) :token (or prev-token token)})
(apply-changes-local))) (apply-changes-local)))
(defn delete-token (defn delete-token
[changes set-name token-id token-name] [changes set-name token-name]
(assert-library! changes) (assert-library! changes)
(let [library-data (::library-data (meta changes)) (let [library-data (::library-data (meta changes))
prev-token (get-in library-data [:tokens token-id])] prev-token (some-> (get library-data :tokens-lib)
(ctob/get-set set-name)
(ctob/get-token token-name))]
(-> changes (-> changes
(update :redo-changes conj {:type :del-token :set-name set-name :id token-id :name token-name}) (update :redo-changes conj {:type :del-token :set-name set-name :name token-name})
(update :undo-changes conj {:type :add-token :set-id uuid/zero :set-name set-name :token prev-token}) (update :undo-changes conj {:type :add-token :set-name set-name :token prev-token})
(apply-changes-local)))) (apply-changes-local))))
(defn add-component (defn add-component

View file

@ -64,22 +64,6 @@
[:map-of {:gen/max 5} ::sm/uuid ::media-object]] [:map-of {:gen/max 5} ::sm/uuid ::media-object]]
[:plugin-data {:optional true} [:plugin-data {:optional true}
[:map-of {:gen/max 5} :keyword ::ctpg/plugin-data]] [:map-of {:gen/max 5} :keyword ::ctpg/plugin-data]]
[:token-theme-temporary-id {:optional true}
::sm/uuid]
[:token-active-themes {:optional true :default #{}}
[:set ::sm/uuid]]
[:token-themes {:optional true}
[:vector ::sm/uuid]]
[:token-themes-index {:optional true}
[:map-of {:gen/max 5} ::sm/uuid ::ctt/token-theme]]
[:token-set-groups {:optional true}
[:vector ::sm/uuid]]
[:token-set-groups-index {:optional true}
[:map-of {:gen/max 10} ::sm/uuid ::ctt/token-set-group]]
[:token-sets-index {:optional true}
[:map-of {:gen/max 10} ::sm/uuid ::ctt/token-set]]
[:tokens {:optional true}
[:map-of {:gen/max 100} ::sm/uuid ::cto/token]]
[:tokens-lib {:optional true} ::ctl/tokens-lib]]) [:tokens-lib {:optional true} ::ctl/tokens-lib]])
(def check-file-data! (def check-file-data!

View file

@ -58,13 +58,13 @@
[n] [n]
(string? n)) (string? n))
;; TODO Move this to tokens-lib
(sm/register! ::token (sm/register! ::token
[:map {:title "Token"} [:map {:title "Token"}
[:id ::sm/uuid]
[:name token-name-ref] [:name token-name-ref]
[:type [::sm/one-of token-types]] [:type [::sm/one-of token-types]]
[:value :any] [:value :any]
[:description {:optional true} :string] [:description {:optional true} [:maybe :string]]
[:modified-at {:optional true} ::sm/inst]]) [:modified-at {:optional true} ::sm/inst]])
(sm/register! ::color (sm/register! ::color

View file

@ -10,35 +10,16 @@
(sm/register! ::token-theme (sm/register! ::token-theme
[:map {:title "TokenTheme"} [:map {:title "TokenTheme"}
[:id ::sm/uuid]
[:name :string] [:name :string]
[:group {:optional true} :string] [:group :string]
[:source? {:optional true} :boolean] [:description [:maybe :string]]
[:description {:optional true} :string] [:is-source :boolean]
[:modified-at {:optional true} ::sm/inst] [:modified-at {:optional true} ::sm/inst]
[:sets [:set {:gen/max 10 :gen/min 1} ::sm/uuid]]]) [:sets :any]])
(sm/register! ::token-set-group-ref
[:map
[:id ::sm/uuid]
[:type [:= :group]]])
(sm/register! ::token-set-ref
[:map
[:id ::sm/uuid]
[:type [:= :set]]])
(sm/register! ::token-set-group
[:map {:title "TokenSetGroup"}
[:id ::sm/uuid]
[:name :string]
[:items [:vector {:gen/max 10 :gen/min 1}
[:or ::token-set-group-ref ::token-set-ref]]]])
(sm/register! ::token-set (sm/register! ::token-set
[:map {:title "TokenSet"} [:map {:title "TokenSet"}
[:id ::sm/uuid]
[:name :string] [:name :string]
[:description {:optional true} :string] [:description {:optional true} [:maybe :string]]
[:modified-at {:optional true} ::sm/inst] [:modified-at {:optional true} ::sm/inst]
[:tokens [:vector {:gen/max 10 :gen/min 1} ::sm/uuid]]]) [:tokens :any]])

View file

@ -6,14 +6,16 @@
(ns app.common.types.tokens-lib (ns app.common.types.tokens-lib
(:require (:require
#?(:clj [app.common.fressian :as fres])
[app.common.data :as d] [app.common.data :as d]
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.schema :as sm] [app.common.schema :as sm]
[app.common.time :as dt] [app.common.time :as dt]
[app.common.transit :as t] [app.common.transit :as t]
[app.common.types.token :as cto] [app.common.types.token :as cto]
[cuerdas.core :as str] [clojure.set :as set]
#?(:clj [app.common.fressian :as fres]))) [clojure.walk :as walk]
[cuerdas.core :as str]))
;; === Groups handling ;; === Groups handling
@ -102,10 +104,10 @@
(def schema:token (def schema:token
[:and [:and
[:map {:title "Token"} [:map {:title "Token"}
[:name cto/token-name-ref] ;; not necessary to have uuid [:name cto/token-name-ref]
[:type [::sm/one-of cto/token-types]] [:type [::sm/one-of cto/token-types]]
[:value :any] [:value :any]
[:description [:maybe :string]] ;; defrecord always have the attributes, even with nil value [:description [:maybe :string]]
[:modified-at ::sm/inst]] [:modified-at ::sm/inst]]
[:fn (partial instance? Token)]]) [:fn (partial instance? Token)]])
@ -130,12 +132,27 @@
token)) token))
(defn group-by-type [tokens]
(let [tokens' (if (or (map? tokens)
(d/ordered-map? tokens))
(vals tokens)
tokens)]
(group-by :type tokens')))
(defn filter-by-type [token-type tokens]
(let [token-type? #(= token-type (:type %))]
(cond
(d/ordered-map? tokens) (into (d/ordered-map) (filter (comp token-type? val) tokens))
(map? tokens) (into {} (filter (comp token-type? val) tokens))
:else (filter token-type? tokens))))
;; === Token Set ;; === Token Set
(defprotocol ITokenSet (defprotocol ITokenSet
(add-token [_ token] "add a token at the end of the list") (add-token [_ token] "add a token at the end of the list")
(update-token [_ token-name f] "update a token in the list") (update-token [_ token-name f] "update a token in the list")
(delete-token [_ token-name] "delete a token from the list") (delete-token [_ token-name] "delete a token from the list")
(get-token [_ token-name] "return token by token-name")
(get-tokens [_] "return an ordered sequence of all tokens in the set")) (get-tokens [_] "return an ordered sequence of all tokens in the set"))
(defrecord TokenSet [name description modified-at tokens] (defrecord TokenSet [name description modified-at tokens]
@ -168,6 +185,9 @@
(dt/now) (dt/now)
(dissoc tokens token-name))) (dissoc tokens token-name)))
(get-token [_ token-name]
(get tokens token-name))
(get-tokens [_] (get-tokens [_]
(vals tokens))) (vals tokens)))
@ -221,6 +241,7 @@
(set-count [_] "get the total number if sets in the library") (set-count [_] "get the total number if sets in the library")
(get-set-tree [_] "get a nested tree of all sets in the library") (get-set-tree [_] "get a nested tree of all sets in the library")
(get-sets [_] "get an ordered sequence of all sets in the library") (get-sets [_] "get an ordered sequence of all sets in the library")
(get-ordered-set-names [_] "get an ordered sequence of all sets names in the library")
(get-set [_ set-name] "get one set looking for name") (get-set [_ set-name] "get one set looking for name")
(get-set-group [_ set-group-path] "get the attributes of a set group")) (get-set-group [_ set-group-path] "get the attributes of a set group"))
@ -249,20 +270,55 @@
;; === TokenTheme ;; === TokenTheme
(def theme-separator "/")
(defn token-theme-path [group name]
(join-path [group name] theme-separator))
(defn split-token-theme-path [path]
(split-path path theme-separator))
(def hidden-token-theme-group
"")
(def hidden-token-theme-name
"__PENPOT__HIDDEN__TOKEN__THEME__")
(def hidden-token-theme-path
(token-theme-path hidden-token-theme-group hidden-token-theme-name))
(defprotocol ITokenTheme (defprotocol ITokenTheme
(toggle-set [_ set-name] "togle a set used / not used in the theme")) (set-sets [_ set-names] "set the active token sets")
(toggle-set [_ set-name] "togle a set used / not used in the theme")
(theme-path [_] "get `token-theme-path` from theme")
(theme-matches-group-name [_ group name] "if a theme matches the given group & name")
(hidden-temporary-theme? [_] "if a theme is the (from the user ui) hidden temporary theme"))
(defrecord TokenTheme [name group description is-source modified-at sets] (defrecord TokenTheme [name group description is-source modified-at sets]
ITokenTheme ITokenTheme
(toggle-set [_ set-name] (set-sets [_ set-names]
(TokenTheme. name (TokenTheme. name
group group
description description
is-source is-source
(dt/now) (dt/now)
(if (sets set-name) set-names))
(disj sets set-name)
(conj sets set-name))))) (toggle-set [this set-name]
(set-sets this (if (sets set-name)
(disj sets set-name)
(conj sets set-name))))
(theme-path [_]
(token-theme-path group name))
(theme-matches-group-name [this group name]
(and (= (:group this) group)
(= (:name this) name)))
(hidden-temporary-theme? [this]
(theme-matches-group-name this hidden-token-theme-group hidden-token-theme-name)))
(def schema:token-theme (def schema:token-theme
[:and [:map {:title "TokenTheme"} [:and [:map {:title "TokenTheme"}
@ -271,8 +327,7 @@
[:description [:maybe :string]] [:description [:maybe :string]]
[:is-source :boolean] [:is-source :boolean]
[:modified-at ::sm/inst] [:modified-at ::sm/inst]
[:sets [:and [:set {:gen/max 5} :string] [:sets [:set {:gen/max 5} :string]]]
[:fn d/ordered-set?]]]]
[:fn (partial instance? TokenTheme)]]) [:fn (partial instance? TokenTheme)]])
(sm/register! ::token-theme schema:token-theme) (sm/register! ::token-theme schema:token-theme)
@ -297,7 +352,7 @@
(update :group #(or % top-level-theme-group-name)) (update :group #(or % top-level-theme-group-name))
(update :is-source #(or % false)) (update :is-source #(or % false))
(update :modified-at #(or % (dt/now))) (update :modified-at #(or % (dt/now)))
(update :sets #(into (d/ordered-set) %))) (update :sets #(into #{} %)))
token-theme (map->TokenTheme params)] token-theme (map->TokenTheme params)]
(dm/assert! (dm/assert!
@ -306,6 +361,12 @@
token-theme)) token-theme))
(defn make-hidden-token-theme
[& {:keys [] :as params}]
(make-token-theme (assoc params
:group hidden-token-theme-group
:name hidden-token-theme-name)))
;; === TokenThemes (collection) ;; === TokenThemes (collection)
(defprotocol ITokenThemes (defprotocol ITokenThemes
@ -316,7 +377,14 @@
(get-theme-tree [_] "get a nested tree of all themes in the library") (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-themes [_] "get an ordered sequence of all themes in the library")
(get-theme [_ group name] "get one theme looking for name") (get-theme [_ group name] "get one theme looking for name")
(get-theme-groups [_] "get a sequence of group names by order")) (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")
(set-active-themes [_ active-themes] "set active themes in library")
(theme-active? [_ group name] "predicate if token theme is active")
(activate-theme [_ group name] "adds theme from the active-themes")
(deactivate-theme [_ group name] "removes theme from the active-themes")
(toggle-theme-active? [_ group name] "toggles theme in the active-themes"))
(def schema:token-themes (def schema:token-themes
[:and [:and
@ -333,6 +401,12 @@
(def check-token-themes! (def check-token-themes!
(sm/check-fn ::token-themes)) (sm/check-fn ::token-themes))
(def schema:active-token-themes
[:set string?])
(def valid-active-token-themes?
(sm/validator schema:active-token-themes))
;; === Tokens Lib ;; === Tokens Lib
(defprotocol ITokensLib (defprotocol ITokensLib
@ -341,20 +415,30 @@
(update-token-in-set [_ set-name token-name f] "update a token in a set") (update-token-in-set [_ set-name token-name f] "update a token in a set")
(delete-token-from-set [_ set-name token-name] "delete a token from a set") (delete-token-from-set [_ set-name token-name] "delete a token from a set")
(toggle-set-in-theme [_ group-name theme-name set-name] "toggle a set used / not used in a theme") (toggle-set-in-theme [_ group-name theme-name set-name] "toggle a set used / not used in a theme")
(get-active-themes-set-names [_] "set of set names that are active in the the active themes")
(get-active-themes-set-tokens [_] "set of set names that are active in the the active themes")
(update-set-name [_ old-set-name new-set-name] "updates set name in themes")
(validate [_])) (validate [_]))
(deftype TokensLib [sets set-groups themes] (deftype TokensLib [sets set-groups themes active-themes]
;; NOTE: This is only for debug purposes, pending to properly ;; NOTE: This is only for debug purposes, pending to properly
;; implement the toString and alternative printing. ;; implement the toString and alternative printing.
#?@(:clj [clojure.lang.IDeref #?@(:clj [clojure.lang.IDeref
(deref [_] {:sets sets :set-groups set-groups :themes themes})] (deref [_] {:sets sets
:set-groups set-groups
:themes themes
:active-themes active-themes})]
:cljs [cljs.core/IDeref :cljs [cljs.core/IDeref
(-deref [_] {:sets sets :set-groups set-groups :themes themes})]) (-deref [_] {:sets sets
:set-groups set-groups
:themes themes
:active-themes active-themes})])
#?@(:cljs [cljs.core/IEncodeJS #?@(:cljs [cljs.core/IEncodeJS
(-clj->js [_] (js-obj "sets" (clj->js sets) (-clj->js [_] (js-obj "sets" (clj->js sets)
"set-groups" (clj->js set-groups) "set-groups" (clj->js set-groups)
"themes" (clj->js themes)))]) "themes" (clj->js themes)
"active-themes" (clj->js active-themes)))])
ITokenSets ITokenSets
(add-set [_ token-set] (add-set [_ token-set]
@ -365,7 +449,8 @@
(cond-> set-groups (cond-> set-groups
(not (str/empty? groups-str)) (not (str/empty? groups-str))
(assoc groups-str (make-token-set-group))) (assoc groups-str (make-token-set-group)))
themes))) themes
active-themes)))
(update-set [this set-name f] (update-set [this set-name f]
(let [path (split-path set-name "/") (let [path (split-path set-name "/")
@ -381,14 +466,16 @@
(d/oassoc-in-before path path' set') (d/oassoc-in-before path path' set')
(d/dissoc-in path))) (d/dissoc-in path)))
set-groups ;; TODO update set-groups as needed set-groups ;; TODO update set-groups as needed
themes)) themes
active-themes))
this))) this)))
(delete-set [_ set-name] (delete-set [_ set-name]
(let [path (split-path set-name "/")] (let [path (split-path set-name "/")]
(TokensLib. (d/dissoc-in sets path) (TokensLib. (d/dissoc-in sets path)
set-groups ;; TODO remove set-group if needed set-groups ;; TODO remove set-group if needed
themes))) themes
active-themes)))
(get-set-tree [_] (get-set-tree [_]
sets) sets)
@ -397,6 +484,9 @@
(->> (tree-seq d/ordered-map? vals sets) (->> (tree-seq d/ordered-map? vals sets)
(filter (partial instance? TokenSet)))) (filter (partial instance? TokenSet))))
(get-ordered-set-names [this]
(map :name (get-sets this)))
(set-count [this] (set-count [this]
(count (get-sets this))) (count (get-sets this)))
@ -412,7 +502,8 @@
(dm/assert! "expected valid token theme" (check-token-theme! token-theme)) (dm/assert! "expected valid token theme" (check-token-theme! token-theme))
(TokensLib. sets (TokensLib. sets
set-groups set-groups
(update themes (:group token-theme) d/oassoc (:name token-theme) token-theme))) (update themes (:group token-theme) d/oassoc (:name token-theme) token-theme)
active-themes))
(update-theme [this group name f] (update-theme [this group name f]
(let [theme (dm/get-in themes [group name])] (let [theme (dm/get-in themes [group name])]
@ -420,21 +511,28 @@
(let [theme' (-> (make-token-theme (f theme)) (let [theme' (-> (make-token-theme (f theme))
(assoc :modified-at (dt/now))) (assoc :modified-at (dt/now)))
group' (:group theme') group' (:group theme')
name' (:name theme')] name' (:name theme')
same-group? (= group group')
same-name? (= name name')
same-path? (and same-group? same-name?)]
(check-token-theme! theme') (check-token-theme! theme')
(TokensLib. sets (TokensLib. sets
set-groups set-groups
(if (and (= group group') (= name name')) (if same-path?
(update themes group' assoc name' theme') (update themes group' assoc name' theme')
(-> themes (-> themes
(d/oassoc-in-before [group name] [group' name'] theme') (d/oassoc-in-before [group name] [group' name'] theme')
(d/dissoc-in [group name]))))) (d/dissoc-in [group name])))
(if same-path?
active-themes
(disj active-themes (token-theme-path group name)))))
this))) this)))
(delete-theme [_ group name] (delete-theme [_ group name]
(TokensLib. sets (TokensLib. sets
set-groups set-groups
(d/dissoc-in themes [group name]))) (d/dissoc-in themes [group name])
(disj active-themes (token-theme-path group name))))
(get-theme-tree [_] (get-theme-tree [_]
themes) themes)
@ -455,13 +553,58 @@
(get-theme [_ group name] (get-theme [_ group name]
(dm/get-in themes [group name])) (dm/get-in themes [group name]))
(set-active-themes [_ active-themes]
(TokensLib. sets
set-groups
themes
active-themes))
(activate-theme [this group name]
(if-let [theme (get-theme this group name)]
(let [group-themes (->> (get themes group)
(map (comp theme-path val))
(into #{}))
active-themes' (-> (set/difference active-themes group-themes)
(conj (theme-path theme)))]
(TokensLib. sets
set-groups
themes
active-themes'))
this))
(deactivate-theme [_ group name]
(TokensLib. sets
set-groups
themes
(disj active-themes (token-theme-path group name))))
(theme-active? [_ group name]
(contains? active-themes (token-theme-path group name)))
(toggle-theme-active? [this group name]
(if (theme-active? this group name)
(deactivate-theme this group name)
(activate-theme this group name)))
(get-active-theme-paths [_]
active-themes)
(get-active-themes [this]
(into
(list)
(comp
(filter (partial instance? TokenTheme))
(filter #(theme-active? this (:group %) (:name %))))
(tree-seq d/ordered-map? vals themes)))
ITokensLib ITokensLib
(add-token-in-set [this set-name token] (add-token-in-set [this set-name token]
(dm/assert! "expected valid token instance" (check-token! token)) (dm/assert! "expected valid token instance" (check-token! token))
(if (contains? sets set-name) (if (contains? sets set-name)
(TokensLib. (update sets set-name add-token token) (TokensLib. (update sets set-name add-token token)
set-groups set-groups
themes) themes
active-themes)
this)) this))
(update-token-in-set [this set-name token-name f] (update-token-in-set [this set-name token-name f]
@ -469,7 +612,8 @@
(TokensLib. (update sets set-name (TokensLib. (update sets set-name
#(update-token % token-name f)) #(update-token % token-name f))
set-groups set-groups
themes) themes
active-themes)
this)) this))
(delete-token-from-set [this set-name token-name] (delete-token-from-set [this set-name token-name]
@ -477,7 +621,8 @@
(TokensLib. (update sets set-name (TokensLib. (update sets set-name
#(delete-token % token-name)) #(delete-token % token-name))
set-groups set-groups
themes) themes
active-themes)
this)) this))
(toggle-set-in-theme [this theme-group theme-name set-name] (toggle-set-in-theme [this theme-group theme-name set-name]
@ -485,12 +630,46 @@
(TokensLib. sets (TokensLib. sets
set-groups set-groups
(d/oupdate-in themes [theme-group theme-name] (d/oupdate-in themes [theme-group theme-name]
#(toggle-set % set-name))) #(toggle-set % set-name))
active-themes)
this)) this))
(get-active-themes-set-names [this]
(into #{}
(mapcat :sets)
(get-active-themes this)))
(get-active-themes-set-tokens [this]
(let [sets-order (get-ordered-set-names this)
active-themes (get-active-themes this)
order-theme-set (fn [theme]
(filter #(contains? (set (:sets theme)) %) sets-order))]
(reduce
(fn [tokens theme]
(reduce
(fn [tokens' cur]
(merge tokens' (:tokens (get-set this cur))))
tokens (order-theme-set theme)))
(d/ordered-map) active-themes)))
;; TODO Move to `update-set`
(update-set-name [_ old-set-name new-set-name]
(TokensLib. sets
set-groups
(walk/postwalk
(fn [form]
(if (instance? TokenTheme form)
(-> form
(update :sets disj old-set-name)
(update :sets conj new-set-name))
form))
themes)
active-themes))
(validate [_] (validate [_]
(and (valid-token-sets? sets) ;; TODO: validate set-groups (and (valid-token-sets? sets) ;; TODO: validate set-groups
(valid-token-themes? themes)))) (valid-token-themes? themes)
(valid-active-token-themes? active-themes))))
(defn valid-tokens-lib? (defn valid-tokens-lib?
[o] [o]
@ -513,10 +692,11 @@
;; with pages and pages-index. ;; with pages and pages-index.
(make-tokens-lib :sets (d/ordered-map) (make-tokens-lib :sets (d/ordered-map)
:set-groups {} :set-groups {}
:themes (d/ordered-map))) :themes (d/ordered-map)
:active-themes #{}))
([& {:keys [sets set-groups themes]}] ([& {:keys [sets set-groups themes active-themes]}]
(let [tokens-lib (TokensLib. sets set-groups themes)] (let [tokens-lib (TokensLib. sets set-groups themes (or active-themes #{}))]
(dm/assert! (dm/assert!
"expected valid tokens lib" "expected valid tokens lib"
@ -592,9 +772,11 @@
(fres/write-tag! w n 3) (fres/write-tag! w n 3)
(fres/write-object! w (.-sets o)) (fres/write-object! w (.-sets o))
(fres/write-object! w (.-set-groups o)) (fres/write-object! w (.-set-groups o))
(fres/write-object! w (.-themes o))) (fres/write-object! w (.-themes o))
(fres/write-object! w (.-active-themes o)))
:rfn (fn [r] :rfn (fn [r]
(let [sets (fres/read-object! r) (let [sets (fres/read-object! r)
set-groups (fres/read-object! r) set-groups (fres/read-object! r)
themes (fres/read-object! r)] themes (fres/read-object! r)
(->TokensLib sets set-groups themes)))})) active-themes (fres/read-object! r)]
(->TokensLib sets set-groups themes active-themes)))}))

View file

@ -6,8 +6,8 @@
(ns common-tests.types.tokens-lib-test (ns common-tests.types.tokens-lib-test
(:require (:require
#?(:clj [app.common.fressian :as fres])
[app.common.data :as d] [app.common.data :as d]
[app.common.fressian :as fres]
[app.common.time :as dt] [app.common.time :as dt]
[app.common.transit :as tr] [app.common.transit :as tr]
[app.common.types.tokens-lib :as ctob] [app.common.types.tokens-lib :as ctob]
@ -182,6 +182,19 @@
(t/is (dt/is-after? (:modified-at token-set') (:modified-at token-set))))) (t/is (dt/is-after? (:modified-at token-set') (:modified-at token-set)))))
(t/deftest delete-token-set (t/deftest delete-token-set
(let [tokens-lib (-> (ctob/make-tokens-lib)
(ctob/add-set (ctob/make-token-set :name "test-token-set")))
tokens-lib' (-> tokens-lib
(ctob/delete-set "test-token-set")
(ctob/delete-set "not-existing-set"))
token-set' (ctob/get-set tokens-lib' "updated-name")]
(t/is (= (ctob/set-count tokens-lib') 0))
(t/is (nil? token-set'))))
(t/deftest active-themes-set-names
(let [tokens-lib (-> (ctob/make-tokens-lib) (let [tokens-lib (-> (ctob/make-tokens-lib)
(ctob/add-set (ctob/make-token-set :name "test-token-set"))) (ctob/add-set (ctob/make-token-set :name "test-token-set")))
@ -307,7 +320,35 @@
(t/is (= (ctob/set-count tokens-lib') 1)) (t/is (= (ctob/set-count tokens-lib') 1))
(t/is (= (count (:tokens token-set')) 0)) (t/is (= (count (:tokens token-set')) 0))
(t/is (nil? token')) (t/is (nil? token'))
(t/is (dt/is-after? (:modified-at token-set') (:modified-at token-set)))))) (t/is (dt/is-after? (:modified-at token-set') (:modified-at token-set)))))
(t/deftest list-active-themes-tokens-in-order
(let [tokens-lib (-> (ctob/make-tokens-lib)
(ctob/add-theme (ctob/make-token-theme :name "out-of-order-theme"
;; Out of order sets in theme
:sets ["unknown-set" "set-b" "set-a"]))
(ctob/set-active-themes #{"/out-of-order-theme"})
(ctob/add-set (ctob/make-token-set :name "set-a"))
(ctob/add-token-in-set "set-a" (ctob/make-token :name "set-a-token"
:type :boolean
:value true))
(ctob/add-set (ctob/make-token-set :name "set-b"))
(ctob/add-token-in-set "set-b" (ctob/make-token :name "set-b-token"
:type :boolean
:value true))
;; Ignore this set
(ctob/add-set (ctob/make-token-set :name "inactive-set"))
(ctob/add-token-in-set "inactive-set" (ctob/make-token :name "inactive-set-token"
:type :boolean
:value true)))
expected-order (ctob/get-ordered-set-names tokens-lib)
expected-tokens (ctob/get-active-themes-set-tokens tokens-lib)
expected-token-names (mapv key expected-tokens)]
(t/is (= '("set-a" "set-b" "inactive-set") expected-order))
(t/is (= ["set-a-token" "set-b-token"] expected-token-names)))))
(t/testing "token-theme in a lib" (t/testing "token-theme in a lib"

View file

@ -0,0 +1,7 @@
(ns preload
(:require
[devtools.core :as devtools]))
;; Silence shadow-cljs devtools (ns reloading)
(devtools/set-pref! :dont-display-banner true)
(devtools/set-pref! :min-expandable-sequable-count-for-well-known-types 0)

View file

@ -8,7 +8,9 @@
{:target :browser {:target :browser
:output-dir "resources/public/js/" :output-dir "resources/public/js/"
:asset-path "/js" :asset-path "/js"
:devtools {:browser-inject :main :devtools {:preloads [preload devtools.preload]
:log false
:browser-inject :main
:watch-dir "resources/public" :watch-dir "resources/public"
:reload-strategy :full} :reload-strategy :full}
:build-options {:manifest-name "manifest.json"} :build-options {:manifest-name "manifest.json"}
@ -65,7 +67,8 @@
:compiler-options :compiler-options
{:output-feature-set :es2020 {:output-feature-set :es2020
:output-wrapper false :output-wrapper false
:warnings {:fn-deprecated false}} :warnings {:fn-deprecated false}
:closure-defines {shadow.debug.LogLevel :warning}}
:release :release
{:closure-defines {goog.DEBUG false {:closure-defines {goog.DEBUG false

View file

@ -11,7 +11,7 @@
[app.common.files.changes-builder :as pcb] [app.common.files.changes-builder :as pcb]
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.common.types.shape :as cts] [app.common.types.shape :as cts]
[app.common.uuid :as uuid] [app.common.types.tokens-lib :as ctob]
[app.main.data.changes :as dch] [app.main.data.changes :as dch]
[app.main.data.workspace.shapes :as dwsh] [app.main.data.workspace.shapes :as dwsh]
[app.main.refs :as refs] [app.main.refs :as refs]
@ -40,6 +40,13 @@
(watch [_ _ _] (watch [_ _ _]
(rx/of (dwsh/update-shapes [id] #(merge % attrs)))))) (rx/of (dwsh/update-shapes [id] #(merge % attrs))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TOKENS Getters
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn get-tokens-lib [state]
(get-in state [:workspace-data :tokens-lib]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TOKENS Actions ;; TOKENS Actions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -81,11 +88,6 @@
(let [workspace-data (deref refs/workspace-data)] (let [workspace-data (deref refs/workspace-data)]
(get (:tokens workspace-data) id))) (get (:tokens workspace-data) id)))
(defn get-token-set-data-from-token-set-id
[id]
(let [workspace-data (deref refs/workspace-data)]
(get (:token-sets-index workspace-data) id)))
(defn set-selected-token-set-id (defn set-selected-token-set-id
[id] [id]
(ptk/reify ::set-selected-token-set-id (ptk/reify ::set-selected-token-set-id
@ -93,16 +95,8 @@
(update [_ state] (update [_ state]
(wtts/assoc-selected-token-set-id state id)))) (wtts/assoc-selected-token-set-id state id))))
(defn get-token-set-tokens
[token-set file]
(map #(get-in file [:tokens %]) (:tokens token-set)))
(defn create-token-theme [token-theme] (defn create-token-theme [token-theme]
(let [new-token-theme (merge (let [new-token-theme token-theme]
{:id (uuid/next)
:sets #{}
:selected :enabled}
token-theme)]
(ptk/reify ::create-token-theme (ptk/reify ::create-token-theme
ptk/WatchEvent ptk/WatchEvent
(watch [it _ _] (watch [it _ _]
@ -111,199 +105,162 @@
(rx/of (rx/of
(dch/commit-changes changes))))))) (dch/commit-changes changes)))))))
(defn update-token-theme [token-theme] (defn update-token-theme [[group name] token-theme]
(ptk/reify ::update-token-theme (ptk/reify ::update-token-theme
ptk/WatchEvent ptk/WatchEvent
(watch [it state _] (watch [it state _]
(let [prev-token-theme (wtts/get-workspace-token-theme (:id token-theme) state) (let [tokens-lib (get-tokens-lib state)
changes (-> (pcb/empty-changes it) prev-token-theme (some-> tokens-lib (ctob/get-theme group name))
(pcb/update-token-theme token-theme prev-token-theme))] changes (pcb/update-token-theme (pcb/empty-changes it) token-theme prev-token-theme)]
(rx/of (rx/of
(dch/commit-changes changes)))))) (dch/commit-changes changes))))))
(defn ensure-token-theme-changes [changes state {:keys [id new-set?]}] (defn toggle-token-theme-active? [group name]
(let [theme-id (wtts/update-theme-id state) (ptk/reify ::toggle-token-theme-active?
theme (some-> theme-id (wtts/get-workspace-token-theme state))]
(cond
(not theme-id) (-> changes
(pcb/add-temporary-token-theme
{:id (uuid/next)
:name "Test theme"
:sets #{id}}))
new-set? (-> changes
(pcb/update-token-theme
(wtts/add-token-set-to-token-theme id theme)
theme))
:else changes)))
(defn toggle-token-theme [token-theme-id]
(ptk/reify ::toggle-token-theme
ptk/WatchEvent ptk/WatchEvent
(watch [it state _] (watch [it state _]
(let [themes (wtts/get-active-theme-ids state) (let [tokens-lib (get-tokens-lib state)
new-themes (wtts/toggle-active-theme-id token-theme-id state) prev-active-token-themes (some-> tokens-lib
(ctob/get-active-theme-paths))
active-token-themes (some-> tokens-lib
(ctob/toggle-theme-active? group name)
(ctob/get-active-theme-paths))
active-token-themes' (if (= active-token-themes #{ctob/hidden-token-theme-path})
active-token-themes
(disj active-token-themes ctob/hidden-token-theme-path))
changes (-> (pcb/empty-changes it) changes (-> (pcb/empty-changes it)
(pcb/update-active-token-themes new-themes themes))] (pcb/update-active-token-themes active-token-themes' prev-active-token-themes))]
(rx/of (rx/of
(dch/commit-changes changes) (dch/commit-changes changes)
(wtu/update-workspace-tokens)))))) (wtu/update-workspace-tokens))))))
(defn delete-token-theme [token-theme-id] (defn delete-token-theme [group name]
(ptk/reify ::delete-token-theme (ptk/reify ::delete-token-theme
ptk/WatchEvent ptk/WatchEvent
(watch [it state _] (watch [it state _]
(let [data (get state :workspace-data) (let [data (get state :workspace-data)
changes (-> (pcb/empty-changes it) changes (-> (pcb/empty-changes it)
(pcb/with-library-data data) (pcb/with-library-data data)
(pcb/delete-token-theme token-theme-id))] (pcb/delete-token-theme group name))]
(rx/of (rx/of
(dch/commit-changes changes) (dch/commit-changes changes)
(wtu/update-workspace-tokens)))))) (wtu/update-workspace-tokens))))))
(defn create-token-set [token-set] (defn create-token-set [token-set]
(let [new-token-set (merge (let [new-token-set (merge
{:id (uuid/next) {:name "Token Set"
:name "Token Set"
:tokens []} :tokens []}
token-set)] token-set)]
(ptk/reify ::create-token-set (ptk/reify ::create-token-set
ptk/WatchEvent ptk/WatchEvent
(watch [it state _] (watch [it state _]
(let [changes (-> (pcb/empty-changes it) (let [changes (-> (pcb/empty-changes it)
(pcb/add-token-set new-token-set) (pcb/add-token-set new-token-set))]
(ensure-token-theme-changes state {:id (:id new-token-set)
:new-set? true}))]
(rx/of (rx/of
(set-selected-token-set-id (:id new-token-set)) (set-selected-token-set-id (:name new-token-set))
(dch/commit-changes changes))))))) (dch/commit-changes changes)))))))
(defn update-token-set [token-set] (defn update-token-set [set-name token-set]
(ptk/reify ::update-token-set (ptk/reify ::update-token-set
ptk/WatchEvent ptk/WatchEvent
(watch [it state _] (watch [it state _]
(let [prev-token-set (wtts/get-token-set (:id token-set) state) (let [prev-token-set (some-> (get-tokens-lib state)
(ctob/get-set set-name))
changes (-> (pcb/empty-changes it) changes (-> (pcb/empty-changes it)
(pcb/update-token-set token-set prev-token-set))] (pcb/update-token-set token-set prev-token-set))]
(rx/of (rx/of
(dch/commit-changes changes)))))) (dch/commit-changes changes))))))
(defn toggle-token-set [{:keys [token-set-id]}] (defn toggle-token-set [{:keys [token-set-name]}]
(ptk/reify ::toggle-token-set (ptk/reify ::toggle-token-set
ptk/WatchEvent ptk/WatchEvent
(watch [it state _] (watch [it state _]
(let [target-theme-id (wtts/get-temp-theme-id state) (let [tokens-lib (get-tokens-lib state)
active-set-ids (wtts/get-active-set-ids state) prev-theme (ctob/get-theme tokens-lib ctob/hidden-token-theme-group ctob/hidden-token-theme-name)
theme (-> (wtts/get-workspace-token-theme target-theme-id state) active-token-set-names (ctob/get-active-themes-set-names tokens-lib)
(assoc :sets active-set-ids)) 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) changes (-> (pcb/empty-changes it)
(pcb/update-token-theme (pcb/update-active-token-themes #{(ctob/token-theme-path ctob/hidden-token-theme-group ctob/hidden-token-theme-name)} prev-active-token-themes))
(wtts/toggle-token-set-to-token-theme token-set-id theme) changes' (if prev-theme
theme) (pcb/update-token-theme changes theme prev-theme)
(pcb/update-active-token-themes #{target-theme-id} (wtts/get-active-theme-ids state)))] (pcb/add-token-theme changes theme))]
(rx/of (rx/of
(dch/commit-changes changes) (dch/commit-changes changes')
(wtu/update-workspace-tokens)))))) (wtu/update-workspace-tokens))))))
(defn delete-token-set [token-set-id token-set-name] (defn delete-token-set [token-set-name]
(ptk/reify ::delete-token-set (ptk/reify ::delete-token-set
ptk/WatchEvent ptk/WatchEvent
(watch [it state _] (watch [it state _]
(let [data (get state :workspace-data) (let [data (get state :workspace-data)
changes (-> (pcb/empty-changes it) changes (-> (pcb/empty-changes it)
(pcb/with-library-data data) (pcb/with-library-data data)
(pcb/delete-token-set token-set-id token-set-name))] (pcb/delete-token-set token-set-name))]
(rx/of (rx/of
(dch/commit-changes changes) (dch/commit-changes changes)
(wtu/update-workspace-tokens)))))) (wtu/update-workspace-tokens))))))
(defn update-create-token (defn update-create-token
[token] [{:keys [token prev-token-name]}]
(let [token (update token :id #(or % (uuid/next)))] (ptk/reify ::update-create-token
(ptk/reify ::update-create-token ptk/WatchEvent
ptk/WatchEvent (watch [_ state _]
(watch [it state _] (let [token-set (wtts/get-selected-token-set state)
(let [token-set (wtts/get-selected-token-set state) token-set-name (or (:name token-set) "Global")
create-set? (not token-set) changes (if (not token-set)
token-set (or token-set ;; No set created add a global set
{:id (uuid/next) (let [tokens-lib (get-tokens-lib state)
:name "Global" token-set (ctob/make-token-set :name token-set-name :tokens {(:name token) token})
:tokens []}) hidden-theme (ctob/make-hidden-token-theme :sets [token-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} #{}))
changes (cond-> (pcb/empty-changes it) add-to-hidden-theme? (let [prev-hidden-theme (ctob/get-theme tokens-lib ctob/hidden-token-theme-group ctob/hidden-token-theme-name)]
create-set? (-> base-changes
(pcb/add-token-set token-set)) (pcb/update-token-theme (ctob/toggle-set prev-hidden-theme ctob/hidden-token-theme-path) prev-hidden-theme)))
prev-token-id (d/seek #(= % (:id token)) (:tokens token-set)) :else base-changes))
prev-token (get-token-data-from-token-id prev-token-id) ;; Either update or add token to existing set
create-token? (not prev-token) (if-let [prev-token (ctob/get-token token-set (or prev-token-name (:name token)))]
(pcb/update-token (pcb/empty-changes) (:name token-set) token prev-token)
changes (if create-token? (pcb/add-token (pcb/empty-changes) (:name token-set) token)))]
(pcb/add-token changes (:id token-set) (:name token-set) token) (rx/of
(pcb/update-token changes (:id token-set) (:name token-set) token prev-token)) (set-selected-token-set-id token-set-name)
(dch/commit-changes changes))))))
changes (-> changes
(ensure-token-theme-changes state {:new-set? create-set?
:id (:id token-set)}))]
(rx/of
(set-selected-token-set-id (:id token-set))
(dch/commit-changes changes)))))))
(defn delete-token (defn delete-token
[set-name id name] [set-name token-name]
(dm/assert! (string? set-name)) (dm/assert! (string? set-name))
(dm/assert! (uuid? id)) (dm/assert! (string? token-name))
(dm/assert! (string? name))
(ptk/reify ::delete-token (ptk/reify ::delete-token
ptk/WatchEvent ptk/WatchEvent
(watch [it state _] (watch [it state _]
(let [data (get state :workspace-data) (let [data (get state :workspace-data)
changes (-> (pcb/empty-changes it) changes (-> (pcb/empty-changes it)
(pcb/with-library-data data) (pcb/with-library-data data)
(pcb/delete-token set-name id name))] (pcb/delete-token set-name token-name))]
(rx/of (dch/commit-changes changes)))))) (rx/of (dch/commit-changes changes))))))
(defn duplicate-token (defn duplicate-token
[id] [token-name]
(let [new-token (-> (get-token-data-from-token-id id) (dm/assert! (string? token-name))
(dissoc :id) (ptk/reify ::duplicate-token
(update :name #(str/concat % "-copy")))] ptk/WatchEvent
(update-create-token new-token))) (watch [_ state _]
(when-let [token (some-> (wtts/get-selected-token-set state)
(ctob/get-token token-name)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (update :name #(str/concat % "-copy")))]
;; TEMP (Move to test) (rx/of
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (update-create-token {:token token}))))))
(comment
(def shape-1 {:r3 3})
(def token-1 {:rx 1
:ry 1})
(def shape-after-token-1-is-applied {:rx 1
:ry 1
:r3 3})
(def token-2 {:r3 1})
(def shape-after-token-2-is-applied {:rx 1
:ry 1
:r3 1})
(def token-3 {:r3 1})
(def shape-after-token-3-is-applied {:rx 1
:ry 1})
(= (toggle-or-apply-token shape-1 token-1)
shape-after-token-1-is-applied)
(= (toggle-or-apply-token shape-after-token-1-is-applied token-2)
shape-after-token-2-is-applied)
(= (toggle-or-apply-token shape-after-token-2-is-applied token-3)
shape-after-token-3-is-applied)
nil)
(defn set-token-type-section-open (defn set-token-type-section-open
[token-type open?] [token-type open?]
@ -315,7 +272,7 @@
;; Token Context Menu Functions ------------------------------------------------- ;; Token Context Menu Functions -------------------------------------------------
(defn show-token-context-menu (defn show-token-context-menu
[{:keys [position _token-id] :as params}] [{:keys [position _token-name] :as params}]
(dm/assert! (gpt/point? position)) (dm/assert! (gpt/point? position))
(ptk/reify ::show-token-context-menu (ptk/reify ::show-token-context-menu
ptk/UpdateEvent ptk/UpdateEvent
@ -329,7 +286,7 @@
(assoc-in state [:workspace-local :token-context-menu] nil)))) (assoc-in state [:workspace-local :token-context-menu] nil))))
(defn show-token-set-context-menu (defn show-token-set-context-menu
[{:keys [position _token-set-id] :as params}] [{:keys [position _token-set-name] :as params}]
(dm/assert! (gpt/point? position)) (dm/assert! (gpt/point? position))
(ptk/reify ::show-token-set-context-menu (ptk/reify ::show-token-set-context-menu
ptk/UpdateEvent ptk/UpdateEvent

View file

@ -12,11 +12,11 @@
[app.common.files.helpers :as cph] [app.common.files.helpers :as cph]
[app.common.types.shape-tree :as ctt] [app.common.types.shape-tree :as ctt]
[app.common.types.shape.layout :as ctl] [app.common.types.shape.layout :as ctl]
[app.common.types.tokens-lib :as ctob]
[app.main.data.workspace.state-helpers :as wsh] [app.main.data.workspace.state-helpers :as wsh]
[app.main.store :as st] [app.main.store :as st]
[app.main.ui.workspace.tokens.token-set :as wtts] [app.main.ui.workspace.tokens.token-set :as wtts]
[okulary.core :as l] [okulary.core :as l]))
[app.common.types.tokens-lib :as ctob]))
;; ---- Global refs ;; ---- Global refs
@ -235,59 +235,6 @@
(def workspace-data (def workspace-data
(l/derived :workspace-data st/state)) (l/derived :workspace-data st/state))
(def workspace-selected-token-set-id
(l/derived
wtts/get-selected-token-set-id
st/state
=))
;; ---- Tokens
(def tokens-lib
(l/derived :tokens-lib workspace-data))
(def workspace-token-theme-groups
(l/derived #(some-> % ctob/get-theme-groups) tokens-lib))
(defn workspace-token-theme
[id]
(l/derived #(wtts/get-workspace-theme id %) st/state))
(def workspace-active-theme-ids
(l/derived wtts/get-active-theme-ids st/state))
(def workspace-temp-theme-id
(l/derived wtts/get-temp-theme-id st/state))
(def workspace-active-set-ids
(l/derived wtts/get-active-set-ids st/state))
(def workspace-token-themes
(l/derived wtts/get-workspace-themes-index st/state))
(def workspace-ordered-token-themes
(l/derived wtts/get-workspace-ordered-themes st/state))
(def workspace-ordered-token-sets
(l/derived
(fn [data]
(or (wtts/get-workspace-ordered-sets data) {}))
st/state
=))
(def workspace-active-theme-sets-tokens
(l/derived wtts/get-active-theme-sets-tokens-names-map st/state =))
(def workspace-ordered-token-sets-tokens
(l/derived wtts/get-workspace-ordered-sets-tokens st/state =))
(def workspace-selected-token-set-tokens
(l/derived
(fn [data]
(or (wtts/get-selected-token-set-tokens data) {}))
st/state
=))
(def workspace-file-colors (def workspace-file-colors
(l/derived (fn [data] (l/derived (fn [data]
(when data (when data
@ -496,6 +443,65 @@
ids))) ids)))
st/state =)) st/state =))
;; ---- Token refs
(def tokens-lib
(l/derived :tokens-lib workspace-data))
(def workspace-token-theme-groups
(l/derived (d/nilf ctob/get-theme-groups) tokens-lib))
(defn workspace-token-theme
[group name]
(l/derived
(fn [lib]
(when lib
(ctob/get-theme lib group name)))
tokens-lib))
(def workspace-token-theme-tree-no-hidden
(l/derived (fn [lib]
(or
(some-> lib
(ctob/delete-theme ctob/hidden-token-theme-group ctob/hidden-token-theme-name)
(ctob/get-theme-tree))
[]))
tokens-lib))
(def workspace-token-themes
(l/derived #(or (some-> % ctob/get-themes) []) tokens-lib))
(def workspace-token-themes-no-hidden
(l/derived #(remove ctob/hidden-temporary-theme? %) workspace-token-themes))
(def workspace-selected-token-set-id
(l/derived wtts/get-selected-token-set-id st/state))
(def workspace-ordered-token-sets
(l/derived #(or (some-> % ctob/get-sets) []) tokens-lib))
(def workspace-active-theme-paths
(l/derived (d/nilf ctob/get-active-theme-paths) tokens-lib))
(def workspace-active-theme-paths-no-hidden
(l/derived #(disj % ctob/hidden-token-theme-path) workspace-active-theme-paths))
(def workspace-active-set-names
(l/derived (d/nilf ctob/get-active-themes-set-names) tokens-lib))
(def workspace-active-theme-sets-tokens
(l/derived #(or (some-> % ctob/get-active-themes-set-tokens) {}) tokens-lib))
(def workspace-selected-token-set-token
(fn [token-name]
(l/derived
#(some-> (wtts/get-selected-token-set %)
(ctob/get-token token-name))
st/state)))
(def workspace-selected-token-set-tokens
(l/derived #(or (wtts/get-selected-token-set-tokens %) {}) st/state))
;; ---- Viewer refs ;; ---- Viewer refs
(defn lookup-viewer-objects-by-id (defn lookup-viewer-objects-by-id

View file

@ -16,6 +16,8 @@
[cuerdas.core :as str] [cuerdas.core :as str]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
(set! *warn-on-infer* false)
(mf/defc tab-element (mf/defc tab-element
{::mf/wrap-props false} {::mf/wrap-props false}
[{:keys [children]}] [{:keys [children]}]

View file

@ -11,6 +11,7 @@
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.math :as mth] [app.common.math :as mth]
[app.common.types.shape.layout :as ctl] [app.common.types.shape.layout :as ctl]
[app.common.types.tokens-lib :as ctob]
[app.config :as cf] [app.config :as cf]
[app.main.data.events :as-alias ev] [app.main.data.events :as-alias ev]
[app.main.data.workspace :as udw] [app.main.data.workspace :as udw]
@ -27,10 +28,11 @@
[app.main.ui.formats :as fmt] [app.main.ui.formats :as fmt]
[app.main.ui.hooks :as h] [app.main.ui.hooks :as h]
[app.main.ui.icons :as i] [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.core :as wtc]
[app.main.ui.workspace.tokens.editable-select :refer [editable-select]] [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 :as wtt] [app.main.ui.workspace.tokens.token :as wtt]
[app.main.ui.workspace.tokens.changes :as wtch]
[app.main.ui.workspace.tokens.token-types :as wtty] [app.main.ui.workspace.tokens.token-types :as wtty]
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]] [app.util.i18n :as i18n :refer [tr]]
@ -856,8 +858,10 @@
shape (when-not multiple shape (when-not multiple
(first (deref (refs/objects-by-id ids)))) (first (deref (refs/objects-by-id ids))))
tokens (mf/deref refs/workspace-selected-token-set-tokens) tokens (sd/use-active-theme-sets-tokens)
spacing-tokens (mf/use-memo (mf/deps tokens) #(:spacing (wtc/group-tokens-by-type tokens))) spacing-tokens (mf/use-memo
(mf/deps tokens)
#(ctob/filter-by-type :spacing tokens))
spacing-column-options (mf/use-memo spacing-column-options (mf/use-memo
(mf/deps shape spacing-tokens) (mf/deps shape spacing-tokens)

View file

@ -32,7 +32,8 @@
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]] [app.util.i18n :as i18n :refer [tr]]
[clojure.set :refer [rename-keys union]] [clojure.set :refer [rename-keys union]]
[rumext.v2 :as mf])) [rumext.v2 :as mf]
[app.common.types.tokens-lib :as ctob]))
(def measure-attrs (def measure-attrs
[:proportion-lock [:proportion-lock
@ -101,28 +102,29 @@
selection-parents-ref (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids)) selection-parents-ref (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids))
selection-parents (mf/deref selection-parents-ref) selection-parents (mf/deref selection-parents-ref)
tokens (-> (mf/deref refs/workspace-active-theme-sets-tokens) tokens (sd/use-active-theme-sets-tokens)
(sd/use-resolved-tokens)) tokens-by-type (mf/use-memo
tokens-by-type (mf/use-memo (mf/deps tokens) #(wtc/group-tokens-by-type tokens)) (mf/deps tokens)
#(ctob/group-by-type tokens))
border-radius-tokens (:border-radius tokens-by-type) border-radius-tokens (:border-radius tokens-by-type)
border-radius-options (mf/use-memo border-radius-options (mf/use-memo
(mf/deps shape border-radius-tokens) (mf/deps shape border-radius-tokens)
#(wtc/tokens-name-map->select-options #(wtc/tokens->select-options
{:shape shape {:shape shape
:tokens border-radius-tokens :tokens border-radius-tokens
:attributes (wtty/token-attributes :border-radius)})) :attributes (wtty/token-attributes :border-radius)}))
sizing-tokens (:sizing tokens-by-type) sizing-tokens (:sizing tokens-by-type)
width-options (mf/use-memo width-options (mf/use-memo
(mf/deps shape sizing-tokens) (mf/deps shape sizing-tokens)
#(wtc/tokens-name-map->select-options #(wtc/tokens->select-options
{:shape shape {:shape shape
:tokens sizing-tokens :tokens sizing-tokens
:attributes (wtty/token-attributes :sizing) :attributes (wtty/token-attributes :sizing)
:selected-attributes #{:width}})) :selected-attributes #{:width}}))
height-options (mf/use-memo height-options (mf/use-memo
(mf/deps shape sizing-tokens) (mf/deps shape sizing-tokens)
#(wtc/tokens-name-map->select-options #(wtc/tokens->select-options
{:shape shape {:shape shape
:tokens sizing-tokens :tokens sizing-tokens
:attributes (wtty/token-attributes :sizing) :attributes (wtty/token-attributes :sizing)

View file

@ -8,19 +8,20 @@
(:require (:require
[app.common.types.shape.radius :as ctsr] [app.common.types.shape.radius :as ctsr]
[app.common.types.token :as ctt] [app.common.types.token :as ctt]
[app.main.data.workspace.colors :as wdc] [app.common.types.tokens-lib :as ctob]
[app.main.data.workspace :as udw] [app.main.data.workspace :as udw]
[app.main.data.workspace.colors :as wdc]
[app.main.data.workspace.shape-layout :as dwsl] [app.main.data.workspace.shape-layout :as dwsl]
[app.main.data.workspace.shapes :as dwsh] [app.main.data.workspace.shapes :as dwsh]
[app.main.data.workspace.state-helpers :as wsh] [app.main.data.workspace.state-helpers :as wsh]
[app.main.data.workspace.transforms :as dwt] [app.main.data.workspace.transforms :as dwt]
[app.main.data.workspace.undo :as dwu] [app.main.data.workspace.undo :as dwu]
[app.main.ui.workspace.tokens.style-dictionary :as sd] [app.main.ui.workspace.tokens.style-dictionary :as sd]
[app.main.ui.workspace.tokens.tinycolor :as tinycolor]
[app.main.ui.workspace.tokens.token :as wtt] [app.main.ui.workspace.tokens.token :as wtt]
[beicon.v2.core :as rx] [beicon.v2.core :as rx]
[clojure.set :as set] [clojure.set :as set]
[potok.v2.core :as ptk] [potok.v2.core :as ptk]))
[app.main.ui.workspace.tokens.tinycolor :as tinycolor]))
;; Token Updates --------------------------------------------------------------- ;; Token Updates ---------------------------------------------------------------
@ -34,21 +35,23 @@
(ptk/reify ::apply-token (ptk/reify ::apply-token
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(->> (rx/from (sd/resolve-tokens+ (get-in state [:workspace-data :tokens]))) (when-let [tokens (some-> (get-in state [:workspace-data :tokens-lib])
(rx/mapcat (ctob/get-active-themes-set-tokens))]
(fn [resolved-tokens] (->> (rx/from (sd/resolve-tokens+ tokens))
(let [undo-id (js/Symbol) (rx/mapcat
resolved-value (get-in resolved-tokens [(wtt/token-identifier token) :resolved-value]) (fn [resolved-tokens]
tokenized-attributes (wtt/attributes-map attributes token)] (let [undo-id (js/Symbol)
(rx/of resolved-value (get-in resolved-tokens [(wtt/token-identifier token) :resolved-value])
(dwu/start-undo-transaction undo-id) tokenized-attributes (wtt/attributes-map attributes token)]
(dwsh/update-shapes shape-ids (fn [shape] (rx/of
(cond-> shape (dwu/start-undo-transaction undo-id)
attributes-to-remove (update :applied-tokens #(apply (partial dissoc %) attributes-to-remove)) (dwsh/update-shapes shape-ids (fn [shape]
:always (update :applied-tokens merge tokenized-attributes)))) (cond-> shape
(when on-update-shape attributes-to-remove (update :applied-tokens #(apply (partial dissoc %) attributes-to-remove))
(on-update-shape resolved-value shape-ids attributes)) :always (update :applied-tokens merge tokenized-attributes))))
(dwu/commit-undo-transaction undo-id))))))))) (when on-update-shape
(on-update-shape resolved-value shape-ids attributes))
(dwu/commit-undo-transaction undo-id))))))))))
(defn unapply-token (defn unapply-token
"Removes `attributes` that match `token` for `shape-ids`. "Removes `attributes` that match `token` for `shape-ids`.

View file

@ -19,20 +19,10 @@
[cuerdas.core :as str] [cuerdas.core :as str]
[goog.events :as events] [goog.events :as events]
[rumext.v2 :as mf]) [rumext.v2 :as mf])
(:import (:import goog.events.EventType))
goog.events.EventType))
;; Helpers --------------------------------------------------------------------- ;; Helpers ---------------------------------------------------------------------
(defn workspace-shapes [workspace page-id shape-ids]
(-> (get-in workspace [:pages-index page-id :objects])
(keep shape-ids)))
(defn vec-remove
"remove elem in coll"
[pos coll]
(into (subvec coll 0 pos) (subvec coll (inc pos))))
(defn camel-keys [m] (defn camel-keys [m]
(->> m (->> m
(d/deep-mapm (d/deep-mapm

View file

@ -202,16 +202,14 @@
(generic-attribute-actions #{:y} "Y" (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 selected-token-set-id]}] (defn default-actions [{:keys [token selected-token-set-id]}]
(let [{:keys [modal]} (wtty/get-token-properties token) (let [{:keys [modal]} (wtty/get-token-properties token)]
selected-token-set (dt/get-token-set-data-from-token-set-id selected-token-set-id)]
[{:title "Delete Token" [{:title "Delete Token"
:action #(st/emit! (dt/delete-token (:name selected-token-set) (:id token) (:name token)))} :action #(st/emit! (dt/delete-token selected-token-set-id (:name token)))}
{:title "Duplicate Token" {:title "Duplicate Token"
:action #(st/emit! (dt/duplicate-token (:id token)))} :action #(st/emit! (dt/duplicate-token (:name token)))}
{:title "Edit Token" {:title "Edit Token"
:action (fn [event] :action (fn [event]
(let [{:keys [key fields]} modal (let [{:keys [key fields]} modal]
token (dt/get-token-data-from-token-id (:id token))]
(st/emit! dt/hide-token-context-menu) (st/emit! dt/hide-token-context-menu)
(dom/stop-propagation event) (dom/stop-propagation event)
(modal/show! key {:x (.-clientX ^js event) (modal/show! key {:x (.-clientX ^js event)
@ -301,19 +299,27 @@
:on-click action :on-click action
:selected? selected?}])]))) :selected? selected?}])])))
(mf/defc token-context-menu-tree
[{:keys [width] :as mdata}]
(let [objects (mf/deref refs/workspace-page-objects)
selected (mf/deref refs/selected-shapes)
selected-shapes (into [] (keep (d/getf objects)) selected)
token-name (:token-name mdata)
token (mf/deref (refs/workspace-selected-token-set-token token-name))
selected-token-set-id (mf/deref refs/workspace-selected-token-set-id)]
[:ul {:class (stl/css :context-list)}
[:& menu-tree {:submenu-offset width
:token token
:selected-token-set-id selected-token-set-id
:selected-shapes selected-shapes}]]))
(mf/defc token-context-menu (mf/defc token-context-menu
[] []
(let [mdata (mf/deref tokens-menu-ref) (let [mdata (mf/deref tokens-menu-ref)
top (+ (get-in mdata [:position :y]) 5) top (+ (get-in mdata [:position :y]) 5)
left (+ (get-in mdata [:position :x]) 5) left (+ (get-in mdata [:position :x]) 5)
width (mf/use-state 0) width (mf/use-state 0)
dropdown-ref (mf/use-ref) 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-selected-token-set-tokens) token-id)
selected-token-set-id (mf/deref refs/workspace-selected-token-set-id)]
(mf/use-effect (mf/use-effect
(mf/deps mdata) (mf/deps mdata)
(fn [] (fn []
@ -325,9 +331,5 @@
:ref dropdown-ref :ref dropdown-ref
:style {:top top :left left} :style {:top top :left left}
:on-context-menu prevent-default} :on-context-menu prevent-default}
(when token (when mdata
[:ul {:class (stl/css :context-list)} [:& token-context-menu-tree (assoc mdata :offset @width)])]]))
[:& menu-tree {:submenu-offset @width
:token token
:selected-token-set-id selected-token-set-id
:selected-shapes selected-shapes}]])]]))

View file

@ -23,17 +23,19 @@
(defn maybe-resolve-token-value [{:keys [value] :as token}] (defn maybe-resolve-token-value [{:keys [value] :as token}]
(when value (resolve-token-value token))) (when value (resolve-token-value token)))
(defn group-tokens-by-type (defn tokens->select-options [{:keys [shape tokens attributes selected-attributes]}]
"Groups tokens by their `:type` property." (map
[tokens] (fn [{:keys [name] :as token}]
(->> (vals tokens) (cond-> (assoc token :label name)
(group-by :type))) (wtt/token-applied? token shape (or selected-attributes attributes)) (assoc :selected? true)))
tokens))
(defn tokens-name-map->select-options [{:keys [shape tokens attributes selected-attributes]}] (defn tokens-name-map->select-options [{:keys [shape tokens attributes selected-attributes]}]
(->> (wtt/token-names-map tokens) (map
(map (fn [[_k {:keys [name] :as item}]] (fn [[_k {:keys [name] :as token}]]
(cond-> (assoc item :label name) (cond-> (assoc token :label name)
(wtt/token-applied? item shape (or selected-attributes attributes)) (assoc :selected? true)))))) (wtt/token-applied? token shape (or selected-attributes attributes)) (assoc :selected? true)))
tokens))
;; JSON export functions ------------------------------------------------------- ;; JSON export functions -------------------------------------------------------
@ -46,18 +48,18 @@
(defn export-tokens-file [tokens-json] (defn export-tokens-file [tokens-json]
(let [file-name "tokens.json" (let [file-name "tokens.json"
file-content (encode-tokens tokens-json) file-content (encode-tokens tokens-json)
blob (wapi/create-blob (clj->js file-content) "application/json")] blob (wapi/create-blob file-content "application/json")]
(dom/trigger-download file-name blob))) (dom/trigger-download file-name blob)))
(defn transform-tokens-into-json-format [tokens] (defn tokens->dtcg-map [tokens]
(let [global (reduce (let [global (reduce
(fn [acc [_ {:keys [name value type]}]] (fn [acc [_ {:keys [name value type]}]]
(assoc acc name {:$value value (assoc acc name {"$value" value
:$type (str/camel type)})) "$type" (str/camel type)}))
(sorted-map) tokens)] (d/ordered-map) tokens)]
{:global global})) {:global global}))
(defn download-tokens-as-json [] (defn download-tokens-as-json []
(let [all-tokens (deref refs/workspace-selected-token-set-tokens) (let [tokens (deref refs/workspace-active-theme-sets-tokens)
transformed-tokens-json (transform-tokens-into-json-format all-tokens)] dtcg-format-tokens-map (tokens->dtcg-map tokens)]
(export-tokens-file transformed-tokens-json))) (export-tokens-file dtcg-format-tokens-map)))

View file

@ -10,6 +10,7 @@
["lodash.debounce" :as debounce] ["lodash.debounce" :as debounce]
[app.common.colors :as c] [app.common.colors :as c]
[app.common.data :as d] [app.common.data :as d]
[app.common.types.tokens-lib :as ctob]
[app.main.data.modal :as modal] [app.main.data.modal :as modal]
[app.main.data.tokens :as dt] [app.main.data.tokens :as dt]
[app.main.refs :as refs] [app.main.refs :as refs]
@ -99,30 +100,28 @@ Token names should only contain letters and digits separated by . characters.")}
(defn validate-token-value+ (defn validate-token-value+
"Validates token value by resolving the value `input` using `StyleDictionary`. "Validates token value by resolving the value `input` using `StyleDictionary`.
Returns a promise of either resolved tokens or rejects with an error state." Returns a promise of either resolved tokens or rejects with an error state."
[{:keys [input name-value token tokens]}] [{:keys [value name-value token tokens]}]
(let [ ;; When creating a new token we dont have a token name yet, (let [;; When creating a new token we dont have a token name yet,
;; so we use a temporary token name that hopefully doesn't clash with any of the users token names ;; so we use a temporary token name that hopefully doesn't clash with any of the users token names
token-name (if (str/empty? name-value) "__TOKEN_STUDIO_SYSTEM.TEMP" name-value)] token-name (if (str/empty? name-value) "__TOKEN_STUDIO_SYSTEM.TEMP" name-value)]
(cond (cond
(empty? (str/trim input)) (empty? (str/trim value))
(p/rejected {:errors [{:error/code :error/empty-input}]}) (p/rejected {:errors [{:error/code :error/empty-input}]})
(token-self-reference? token-name input) (token-self-reference? token-name value)
(p/rejected {:errors [(wte/get-error-code :error.token/direct-self-reference)]}) (p/rejected {:errors [(wte/get-error-code :error.token/direct-self-reference)]})
:else :else
(let [token-id (or (:id token) (random-uuid)) (-> (update tokens token-name merge {:value value
new-tokens (update tokens token-name merge {:id token-id :name token-name
:value input :type (:type token)})
:name token-name (sd/resolve-tokens+ {:names-map? true})
:type (:type token)})] (p/then
(-> (sd/resolve-tokens+ new-tokens {:names-map? true}) (fn [resolved-tokens]
(p/then (let [{:keys [errors resolved-value] :as resolved-token} (get resolved-tokens token-name)]
(fn [resolved-tokens] (cond
(let [{:keys [errors resolved-value] :as resolved-token} (get resolved-tokens token-name)] resolved-value (p/resolved resolved-token)
(cond :else (p/rejected {:errors (or errors (wte/get-error-code :error/unknown-error))})))))))))
resolved-value (p/resolved resolved-token)
:else (p/rejected {:errors (or errors (wte/get-error-code :error/unknown-error))}))))))))))
(defn use-debonced-resolve-callback (defn use-debonced-resolve-callback
"Resolves a token values using `StyleDictionary`. "Resolves a token values using `StyleDictionary`.
@ -141,7 +140,7 @@ Token names should only contain letters and digits separated by . characters.")}
(js/setTimeout (js/setTimeout
(fn [] (fn []
(when (not (timeout-outdated-cb?)) (when (not (timeout-outdated-cb?))
(-> (validate-token-value+ {:input value (-> (validate-token-value+ {:value value
:name-value @name-ref :name-value @name-ref
:token token :token token
:tokens tokens}) :tokens tokens})
@ -203,8 +202,9 @@ Token names should only contain letters and digits separated by . characters.")}
color? (wtt/color-token? token) color? (wtt/color-token? token)
selected-set-tokens (mf/deref refs/workspace-selected-token-set-tokens) selected-set-tokens (mf/deref refs/workspace-selected-token-set-tokens)
active-theme-tokens (mf/deref refs/workspace-active-theme-sets-tokens) active-theme-tokens (mf/deref refs/workspace-active-theme-sets-tokens)
resolved-tokens (sd/use-resolved-tokens active-theme-tokens {:names-map? true resolved-tokens (sd/use-resolved-tokens active-theme-tokens
:cache-atom form-token-cache-atom}) {:names-map? true
:cache-atom form-token-cache-atom})
token-path (mf/use-memo token-path (mf/use-memo
(mf/deps (:name token)) (mf/deps (:name token))
#(wtt/token-name->path (:name token))) #(wtt/token-name->path (:name token)))
@ -309,7 +309,7 @@ Token names should only contain letters and digits separated by . characters.")}
valid-description?+ (some-> final-description validate-descripion schema-validation->promise)] valid-description?+ (some-> final-description validate-descripion schema-validation->promise)]
(-> (p/all [valid-name?+ (-> (p/all [valid-name?+
valid-description?+ valid-description?+
(validate-token-value+ {:input final-value (validate-token-value+ {:value final-value
:name-value final-name :name-value final-name
:token token :token token
:tokens resolved-tokens})]) :tokens resolved-tokens})])
@ -317,14 +317,13 @@ Token names should only contain letters and digits separated by . characters.")}
;; The result should be a vector of all resolved validations ;; 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 ;; We do not handle the error case as it will be handled by the components validations
(when (and (seq result) (not err)) (when (and (seq result) (not err))
(let [new-token (cond-> {:name final-name (st/emit! (dt/update-create-token {:token (ctob/make-token :name final-name
:type (or (:type token) token-type) :type (or (:type token) token-type)
:value final-value} :value final-value
final-description (assoc :description final-description) :description final-description)
(:id token) (assoc :id (:id token)))] :prev-token-name (:name token)}))
(st/emit! (dt/update-create-token new-token)) (st/emit! (wtu/update-workspace-tokens))
(st/emit! (wtu/update-workspace-tokens)) (modal/hide!))))))))]
(modal/hide!)))))))))]
[:form [:form
{:class (stl/css :form-wrapper) {:class (stl/css :form-wrapper)
:on-submit on-submit} :on-submit on-submit}

View file

@ -7,6 +7,7 @@
(ns app.main.ui.workspace.tokens.modals.themes (ns app.main.ui.workspace.tokens.modals.themes
(:require-macros [app.main.style :as stl]) (:require-macros [app.main.style :as stl])
(:require (:require
[app.common.types.tokens-lib :as ctob]
[app.main.data.modal :as modal] [app.main.data.modal :as modal]
[app.main.data.tokens :as wdt] [app.main.data.tokens :as wdt]
[app.main.refs :as refs] [app.main.refs :as refs]
@ -15,12 +16,10 @@
[app.main.ui.icons :as i] [app.main.ui.icons :as i]
[app.main.ui.workspace.tokens.common :refer [labeled-input] :as wtco] [app.main.ui.workspace.tokens.common :refer [labeled-input] :as wtco]
[app.main.ui.workspace.tokens.sets :as wts] [app.main.ui.workspace.tokens.sets :as wts]
[app.main.ui.workspace.tokens.token-set :as wtts]
[app.util.dom :as dom]
[rumext.v2 :as mf]
[cuerdas.core :as str]
[app.main.ui.workspace.tokens.sets-context :as sets-context] [app.main.ui.workspace.tokens.sets-context :as sets-context]
[app.main.ui.shapes.group :as group])) [app.util.dom :as dom]
[cuerdas.core :as str]
[rumext.v2 :as mf]))
(def ^:private chevron-icon (def ^:private chevron-icon
(i/icon-xref :arrow (stl/css :chevron-icon))) (i/icon-xref :arrow (stl/css :chevron-icon)))
@ -56,29 +55,30 @@
(mf/defc themes-overview (mf/defc themes-overview
[{:keys [set-state]}] [{:keys [set-state]}]
(let [active-theme-ids (mf/deref refs/workspace-active-theme-ids) (let [active-theme-ids (mf/deref refs/workspace-active-theme-paths)
themes (mf/deref refs/workspace-ordered-token-themes) themes-groups (mf/deref refs/workspace-token-theme-tree-no-hidden)
on-edit-theme (fn [theme e] on-edit-theme (fn [theme e]
(dom/prevent-default e) (dom/prevent-default e)
(dom/stop-propagation e) (dom/stop-propagation e)
(set-state (fn [_] {:type :edit-theme (set-state (fn [_] {:type :edit-theme
:theme-id (:id theme)})))] :theme-path [(:id theme) (:group theme) (:name theme)]})))]
[:div [:div
[:ul {:class (stl/css :theme-group-wrapper)} [:ul {:class (stl/css :theme-group-wrapper)}
(for [[group themes] themes] (for [[group themes] themes-groups]
[:li {:key (str "token-theme-group" group)} [:li {:key (str "token-theme-group" group)}
(when (seq group) (when (seq group)
[:span {:class (stl/css :theme-group-label)} group]) [:span {:class (stl/css :theme-group-label)} group])
[:ul {:class (stl/css :theme-group-rows-wrapper)} [:ul {:class (stl/css :theme-group-rows-wrapper)}
(for [{:keys [id name] :as theme} themes (for [[_ {:keys [group name] :as theme}] themes
:let [selected? (some? (get active-theme-ids id))]] :let [theme-id (ctob/theme-path theme)
[:li {:key (str "token-theme-" id) selected? (some? (get active-theme-ids theme-id))]]
[:li {:key theme-id
:class (stl/css :theme-row)} :class (stl/css :theme-row)}
[:div {:class (stl/css :theme-row-left)} [:div {:class (stl/css :theme-row-left)}
[:div {:on-click (fn [e] [:div {:on-click (fn [e]
(dom/prevent-default e) (dom/prevent-default e)
(dom/stop-propagation e) (dom/stop-propagation e)
(st/emit! (wdt/toggle-token-theme id)))} (st/emit! (wdt/toggle-token-theme-active? group name)))}
[:& switch {:name (str "Theme" name) [:& switch {:name (str "Theme" name)
:on-change (constantly nil) :on-change (constantly nil)
:selected? selected?}]] :selected? selected?}]]
@ -97,7 +97,7 @@
[:button {:on-click (fn [e] [:button {:on-click (fn [e]
(dom/prevent-default e) (dom/prevent-default e)
(dom/stop-propagation e) (dom/stop-propagation e)
(st/emit! (wdt/delete-token-theme id)))} (st/emit! (wdt/delete-token-theme group name)))}
i/delete]]]])]])] i/delete]]]])]])]
[:div {:class (stl/css :button-footer)} [:div {:class (stl/css :button-footer)}
[:button {:class (stl/css :create-theme-button) [:button {:class (stl/css :create-theme-button)
@ -109,43 +109,35 @@
"Create theme"]]])) "Create theme"]]]))
(mf/defc edit-theme (mf/defc edit-theme
[{:keys [token-sets theme theme-groups on-back on-submit]}] [{:keys [edit? token-sets theme theme-groups on-back on-submit]}]
(let [{:keys [dropdown-open? on-open-dropdown on-close-dropdown on-toggle-dropdown]} (wtco/use-dropdown-open-state) (let [{:keys [dropdown-open? on-open-dropdown on-close-dropdown on-toggle-dropdown]} (wtco/use-dropdown-open-state)
theme-state (mf/use-state theme)
edit? (some? (:id theme)) disabled? (-> (:name @theme-state)
theme-state (mf/use-state {:token-sets token-sets
:theme theme})
disabled? (-> (get-in @theme-state [:theme :name])
(str/trim) (str/trim)
(str/empty?)) (str/empty?))
token-set-active? (mf/use-callback token-set-active? (mf/use-callback
(mf/deps theme-state) (mf/deps theme-state)
(fn [id] (fn [set-name]
(get-in @theme-state [:theme :sets id]))) (get-in @theme-state [:sets set-name])))
on-toggle-token-set (mf/use-callback on-toggle-token-set (mf/use-callback
(mf/deps theme-state) (mf/deps theme-state)
(fn [token-set-id] (fn [set-name]
(swap! theme-state (fn [st] (swap! theme-state #(ctob/toggle-set % set-name))))
(update st :theme #(wtts/toggle-token-set-to-token-theme token-set-id %)))))) on-change-field (fn [field value]
on-change-field (fn [field] (swap! theme-state #(assoc % field value)))
(fn [e]
(swap! theme-state (fn [st] (assoc-in st field (dom/get-target-val e))))))
group-input-ref (mf/use-ref) group-input-ref (mf/use-ref)
on-update-group (on-change-field [:theme :group]) on-update-group (partial on-change-field :group)
on-update-name (on-change-field [:theme :name]) on-update-name (partial on-change-field :name)
on-save-form (mf/use-callback on-save-form (mf/use-callback
(mf/deps theme-state on-submit) (mf/deps theme-state on-submit)
(fn [e] (fn [e]
(dom/prevent-default e) (dom/prevent-default e)
(let [theme (:theme @theme-state) (let [theme (-> @theme-state
final-name (str/trim (:name theme)) (update :name str/trim)
final-group (-> (:group theme) (update :group str/trim)
(str/trim) (update :description str/trim))]
(str/lower))] (when-not (str/empty? (:name theme))
(when-not (str/empty? final-name) (on-submit theme)))
(cond-> theme
(empty final-group) (dissoc :group)
:always on-submit)))
(on-back)))] (on-back)))]
[:form {:on-submit on-save-form} [:form {:on-submit on-save-form}
[:div {:class (stl/css :edit-theme-wrapper)} [:div {:class (stl/css :edit-theme-wrapper)}
@ -165,23 +157,23 @@
theme-groups) theme-groups)
:on-select (fn [{:keys [value]}] :on-select (fn [{:keys [value]}]
(set! (.-value (mf/ref-val group-input-ref)) value) (set! (.-value (mf/ref-val group-input-ref)) value)
(swap! theme-state assoc-in [:theme :group] value)) (on-update-group value))
:on-close on-close-dropdown}]) :on-close on-close-dropdown}])
[:& labeled-input {:label "Group" [:& labeled-input {:label "Group"
:input-props {:ref group-input-ref :input-props {:ref group-input-ref
:default-value (:group theme) :default-value (:group theme)
:on-change on-update-group} :on-change (comp on-update-group dom/get-target-val)}
:render-right (when (seq theme-groups) :render-right (when (seq theme-groups)
(mf/fnc [] (mf/fnc []
[:button {:class (stl/css :group-drop-down-button) [:button {:class (stl/css :group-drop-down-button)
:type "button" :type "button"
:on-click (fn [e] :on-click (fn [e]
(dom/stop-propagation e) (dom/stop-propagation e)
(on-toggle-dropdown))} (on-toggle-dropdown))}
i/arrow]))}]] i/arrow]))}]]
[:& labeled-input {:label "Theme" [:& labeled-input {:label "Theme"
:input-props {:default-value (:name theme) :input-props {:default-value (:name theme)
:on-change on-update-name}}]] :on-change (comp on-update-name dom/get-target-val)}}]]
[:div {:class (stl/css :sets-list-wrapper)} [:div {:class (stl/css :sets-list-wrapper)}
[:& wts/controlled-sets-list [:& wts/controlled-sets-list
{:token-sets token-sets {:token-sets token-sets
@ -195,7 +187,7 @@
[:button {:class (stl/css :button-secondary) [:button {:class (stl/css :button-secondary)
:type "button" :type "button"
:on-click (fn [] :on-click (fn []
(st/emit! (wdt/delete-token-theme (:id theme))) (st/emit! (wdt/delete-token-theme (:group theme) (:name theme)))
(on-back))} (on-back))}
"Delete"] "Delete"]
[:div]) [:div])
@ -212,32 +204,35 @@
(mf/defc controlled-edit-theme (mf/defc controlled-edit-theme
[{:keys [state set-state]}] [{:keys [state set-state]}]
(let [{:keys [theme-id]} @state (let [{:keys [theme-path]} @state
[_ theme-group theme-name] theme-path
token-sets (mf/deref refs/workspace-ordered-token-sets) token-sets (mf/deref refs/workspace-ordered-token-sets)
theme (mf/deref (refs/workspace-token-theme theme-id)) theme (mf/deref (refs/workspace-token-theme theme-group theme-name))
theme-groups (mf/deref refs/workspace-token-theme-groups)] theme-groups (mf/deref refs/workspace-token-theme-groups)]
[:& edit-theme [:& edit-theme
{:token-sets token-sets {:edit? true
:token-sets token-sets
:theme theme :theme theme
:theme-groups theme-groups :theme-groups theme-groups
:on-back #(set-state (constantly {:type :themes-overview})) :on-back #(set-state (constantly {:type :themes-overview}))
:on-submit #(st/emit! (wdt/update-token-theme %))}])) :on-submit #(st/emit! (wdt/update-token-theme [(:group theme) (:name theme)] %))}]))
(mf/defc create-theme (mf/defc create-theme
[{:keys [set-state]}] [{:keys [set-state]}]
(let [token-sets (mf/deref refs/workspace-ordered-token-sets) (let [token-sets (mf/deref refs/workspace-ordered-token-sets)
theme {:name "" :sets #{}} theme (ctob/make-token-theme :name "")
theme-groups (mf/deref refs/workspace-token-theme-groups)] theme-groups (mf/deref refs/workspace-token-theme-groups)]
[:& edit-theme [:& edit-theme
{:token-sets token-sets {:edit? false
:token-sets token-sets
:theme theme :theme theme
:theme-groups theme-groups :theme-groups theme-groups
:on-back #(set-state (constantly {:type :themes-overview})) :on-back #(set-state (constantly {:type :themes-overview}))
:on-submit #(st/emit! (wdt/create-token-theme %))}])) :on-submit #(st/emit! (wdt/create-token-theme %))}]))
(mf/defc themes (mf/defc themes
[{:keys [] :as _args}] [_]
(let [themes (mf/deref refs/workspace-ordered-token-themes) (let [themes (mf/deref refs/workspace-token-themes-no-hidden)
state (mf/use-state (if (empty? themes) state (mf/use-state (if (empty? themes)
{:type :create-theme} {:type :create-theme}
{:type :themes-overview})) {:type :themes-overview}))
@ -258,7 +253,7 @@
(mf/defc modal (mf/defc modal
{::mf/wrap-props false} {::mf/wrap-props false}
[{:keys [] :as _args}] [_]
(let [handle-close-dialog (mf/use-callback #(st/emit! (modal/hide)))] (let [handle-close-dialog (mf/use-callback #(st/emit! (modal/hide)))]
[:div {:class (stl/css :modal-overlay)} [:div {:class (stl/css :modal-overlay)}
[:div {:class (stl/css :modal-dialog)} [:div {:class (stl/css :modal-dialog)}

View file

@ -7,6 +7,7 @@
(ns app.main.ui.workspace.tokens.sets (ns app.main.ui.workspace.tokens.sets
(:require-macros [app.main.style :as stl]) (:require-macros [app.main.style :as stl])
(:require (:require
[app.main.data.messages :as msg]
[app.main.data.tokens :as wdt] [app.main.data.tokens :as wdt]
[app.main.refs :as refs] [app.main.refs :as refs]
[app.main.store :as st] [app.main.store :as st]
@ -20,18 +21,18 @@
(def ^:private chevron-icon (def ^:private chevron-icon
(i/icon-xref :arrow (stl/css :chevron-icon))) (i/icon-xref :arrow (stl/css :chevron-icon)))
(defn on-toggle-token-set-click [token-set-id] (defn on-toggle-token-set-click [token-set-name]
(st/emit! (wdt/toggle-token-set {:token-set-id token-set-id}))) (st/emit! (wdt/toggle-token-set {:token-set-name token-set-name})))
(defn on-select-token-set-click [id] (defn on-select-token-set-click [name]
(st/emit! (wdt/set-selected-token-set-id id))) (st/emit! (wdt/set-selected-token-set-id name)))
(defn on-delete-token-set-click [id name event] (defn on-delete-token-set-click [name event]
(dom/stop-propagation event) (dom/stop-propagation event)
(st/emit! (wdt/delete-token-set id name))) (st/emit! (wdt/delete-token-set name)))
(defn on-update-token-set [token-set] (defn on-update-token-set [set-name token-set]
(st/emit! (wdt/update-token-set token-set))) (st/emit! (wdt/update-token-set set-name token-set)))
(defn on-create-token-set [token-set] (defn on-create-token-set [token-set]
(st/emit! (wdt/create-token-set token-set))) (st/emit! (wdt/create-token-set token-set)))
@ -71,21 +72,21 @@
on-submit on-submit
on-cancel] on-cancel]
:as _props}] :as _props}]
(let [{:keys [id name _children]} token-set (let [{:keys [name _children]} token-set
selected? (and set? (token-set-selected? id)) selected? (and set? (token-set-selected? name))
visible? (token-set-active? id) visible? (token-set-active? name)
collapsed? (mf/use-state false) collapsed? (mf/use-state false)
set? true #_(= type :set) set? true #_(= type :set)
group? false #_(= type :group) group? false #_(= type :group)
editing-node? (editing? id) editing-node? (editing? name)
on-select (mf/use-callback on-select (mf/use-callback
(mf/deps editing-node?) (mf/deps editing-node?)
(fn [event] (fn [event]
(dom/stop-propagation event) (dom/stop-propagation event)
(when-not editing-node? (when-not editing-node?
(on-select id)))) (on-select name))))
on-context-menu (mf/use-callback on-context-menu (mf/use-callback
(mf/deps editing-node? id) (mf/deps editing-node? name)
(fn [event] (fn [event]
(dom/prevent-default event) (dom/prevent-default event)
(dom/stop-propagation event) (dom/stop-propagation event)
@ -93,11 +94,10 @@
(st/emit! (st/emit!
(wdt/show-token-set-context-menu (wdt/show-token-set-context-menu
{:position (dom/get-client-position event) {:position (dom/get-client-position event)
:token-set-id id
:token-set-name name})))))] :token-set-name name})))))]
[:div {:class (stl/css :set-item-container) [:div {:class (stl/css :set-item-container)
:on-click on-select :on-click on-select
:on-double-click #(on-edit id) :on-double-click #(on-edit name)
:on-context-menu on-context-menu} :on-context-menu on-context-menu}
[:div {:class (stl/css-case :set-item-group group? [:div {:class (stl/css-case :set-item-group group?
:set-item-set set? :set-item-set set?
@ -116,14 +116,14 @@
[:* [:*
[:div {:class (stl/css :set-name)} name] [:div {:class (stl/css :set-name)} name]
[:div {:class (stl/css :delete-set)} [:div {:class (stl/css :delete-set)}
[:button {:on-click #(on-delete-token-set-click id name %) [:button {:on-click #(on-delete-token-set-click name %)
:type "button"} :type "button"}
i/delete]] i/delete]]
(if set? (if set?
[:span {:class (stl/css :action-btn) [:span {:class (stl/css :action-btn)
:on-click (fn [event] :on-click (fn [event]
(dom/stop-propagation event) (dom/stop-propagation event)
(on-toggle id))} (on-toggle name))}
(if visible? i/shown i/hide)] (if visible? i/shown i/hide)]
nil nil
#_(when (and children (not @collapsed?)) #_(when (and children (not @collapsed?))
@ -134,6 +134,12 @@
:set-id child-id :set-id child-id
:selected-set-id selected-token-set-id)])]))])]])) :selected-set-id selected-token-set-id)])]))])]]))
(defn warn-on-try-create-token-set-group! []
(st/emit! (msg/show {:content "Token Set grouping is not supported yet."
:notification-type :toast
:type :warning
:timeout 3000})))
(mf/defc controlled-sets-list (mf/defc controlled-sets-list
[{:keys [token-sets [{:keys [token-sets
on-update-token-set on-update-token-set
@ -144,22 +150,27 @@
on-select on-select
context] context]
:as _props}] :as _props}]
(let [{:keys [editing? new? on-edit on-create on-reset] :as ctx} (or context (sets-context/use-context))] (let [{:keys [editing? new? on-edit on-create on-reset] :as ctx} (or context (sets-context/use-context))
avoid-token-set-grouping #(str/replace % "/" "-")]
[:ul {:class (stl/css :sets-list)} [:ul {:class (stl/css :sets-list)}
(for [[id token-set] token-sets] (for [token-set token-sets]
(when token-set (when token-set
[:& sets-tree {:key id [:& sets-tree
:token-set token-set {:key (:name token-set)
:token-set-selected? (if new? (constantly false) token-set-selected?) :token-set token-set
:token-set-active? token-set-active? :token-set-selected? (if new? (constantly false) token-set-selected?)
:editing? editing? :token-set-active? token-set-active?
:on-select on-select :editing? editing?
:on-edit on-edit :on-select on-select
:on-toggle on-toggle-token-set :on-edit on-edit
:on-submit #(do :on-toggle on-toggle-token-set
(on-update-token-set %) :on-submit #(do
(on-reset)) ;; TODO: We don't support set grouping for now so we rename sets for now
:on-cancel on-reset}])) (when (str/includes? (:name %) "/")
(warn-on-try-create-token-set-group!))
(on-update-token-set (avoid-token-set-grouping (:name token-set)) (update % :name avoid-token-set-grouping))
(on-reset))
:on-cancel on-reset}]))
(when new? (when new?
[:& sets-tree {:token-set {:name ""} [:& sets-tree {:token-set {:name ""}
:token-set-selected? (constantly true) :token-set-selected? (constantly true)
@ -168,7 +179,10 @@
:on-select (constantly nil) :on-select (constantly nil)
:on-edit on-create :on-edit on-create
:on-submit #(do :on-submit #(do
(on-create-token-set %) ;; TODO: We don't support set grouping for now so we rename sets for now
(when (str/includes? (:name %) "/")
(warn-on-try-create-token-set-group!))
(on-create-token-set (update % :name avoid-token-set-grouping))
(on-reset)) (on-reset))
:on-cancel on-reset}])])) :on-cancel on-reset}])]))
@ -178,9 +192,9 @@
selected-token-set-id (mf/deref refs/workspace-selected-token-set-id) selected-token-set-id (mf/deref refs/workspace-selected-token-set-id)
token-set-selected? (mf/use-callback token-set-selected? (mf/use-callback
(mf/deps selected-token-set-id) (mf/deps selected-token-set-id)
(fn [id] (fn [set-name]
(= id selected-token-set-id))) (= set-name selected-token-set-id)))
active-token-set-ids (mf/deref refs/workspace-active-set-ids) active-token-set-ids (mf/deref refs/workspace-active-set-names)
token-set-active? (mf/use-callback token-set-active? (mf/use-callback
(mf/deps active-token-set-ids) (mf/deps active-token-set-ids)
(fn [id] (fn [id]

View file

@ -28,11 +28,11 @@
[:span {:class (stl/css :title)} title]]) [:span {:class (stl/css :title)} title]])
(mf/defc menu (mf/defc menu
[{:keys [token-set-id token-set-name]}] [{:keys [token-set-name]}]
(let [{:keys [on-edit]} (sets-context/use-context)] (let [{:keys [on-edit]} (sets-context/use-context)]
[:ul {:class (stl/css :context-list)} [:ul {:class (stl/css :context-list)}
[:& menu-entry {:title "Rename" :on-click #(on-edit token-set-id)}] [:& menu-entry {:title "Rename" :on-click #(on-edit token-set-name)}]
[:& menu-entry {:title "Delete" :on-click #(st/emit! (wdt/delete-token-set token-set-id token-set-name))}]])) [:& menu-entry {:title "Delete" :on-click #(st/emit! (wdt/delete-token-set token-set-name))}]]))
(mf/defc sets-context-menu (mf/defc sets-context-menu
[] []
@ -41,7 +41,6 @@
left (+ (get-in mdata [:position :x]) 5) left (+ (get-in mdata [:position :x]) 5)
width (mf/use-state 0) width (mf/use-state 0)
dropdown-ref (mf/use-ref) dropdown-ref (mf/use-ref)
token-set-id (:token-set-id mdata)
token-set-name (:token-set-name mdata)] token-set-name (:token-set-name mdata)]
(mf/use-effect (mf/use-effect
(mf/deps mdata) (mf/deps mdata)
@ -54,5 +53,4 @@
:ref dropdown-ref :ref dropdown-ref
:style {:top top :left left} :style {:top top :left left}
:on-context-menu prevent-default} :on-context-menu prevent-default}
[:& menu {:token-set-id token-set-id [:& menu {:token-set-name token-set-name}]]]))
:token-set-name token-set-name}]]]))

View file

@ -10,7 +10,6 @@
[app.common.data :as d] [app.common.data :as d]
[app.main.data.modal :as modal] [app.main.data.modal :as modal]
[app.main.data.tokens :as dt] [app.main.data.tokens :as dt]
[app.main.data.tokens :as wdt]
[app.main.refs :as refs] [app.main.refs :as refs]
[app.main.store :as st] [app.main.store :as st]
[app.main.ui.components.color-bullet :refer [color-bullet]] [app.main.ui.components.color-bullet :refer [color-bullet]]
@ -19,7 +18,6 @@
[app.main.ui.icons :as i] [app.main.ui.icons :as i]
[app.main.ui.workspace.sidebar.assets.common :as cmm] [app.main.ui.workspace.sidebar.assets.common :as cmm]
[app.main.ui.workspace.tokens.changes :as wtch] [app.main.ui.workspace.tokens.changes :as wtch]
[app.main.ui.workspace.tokens.common :refer [labeled-input]]
[app.main.ui.workspace.tokens.context-menu :refer [token-context-menu]] [app.main.ui.workspace.tokens.context-menu :refer [token-context-menu]]
[app.main.ui.workspace.tokens.core :as wtc] [app.main.ui.workspace.tokens.core :as wtc]
[app.main.ui.workspace.tokens.sets :refer [sets-list]] [app.main.ui.workspace.tokens.sets :refer [sets-list]]
@ -34,7 +32,8 @@
[cuerdas.core :as str] [cuerdas.core :as str]
[okulary.core :as l] [okulary.core :as l]
[rumext.v2 :as mf] [rumext.v2 :as mf]
[shadow.resource])) [shadow.resource]
[app.common.types.tokens-lib :as ctob]))
(def lens:token-type-open-status (def lens:token-type-open-status
(l/derived (l/in [:workspace-tokens :open-status]) st/state)) (l/derived (l/in [:workspace-tokens :open-status]) st/state))
@ -42,15 +41,6 @@
(def ^:private download-icon (def ^:private download-icon
(i/icon-xref :download (stl/css :download-icon))) (i/icon-xref :download (stl/css :download-icon)))
(def selected-set-id
(l/derived :selected-set-id st/state))
;; Event Functions -------------------------------------------------------------
(defn on-set-add-click [_event]
(when-let [set-name (js/window.prompt "Set name")]
(st/emit! (wdt/create-token-set {:name set-name}))))
;; Components ------------------------------------------------------------------ ;; Components ------------------------------------------------------------------
(mf/defc token-pill (mf/defc token-pill
@ -107,7 +97,7 @@
(dom/stop-propagation event) (dom/stop-propagation event)
(st/emit! (dt/show-token-context-menu {:type :token (st/emit! (dt/show-token-context-menu {:type :token
:position (dom/get-client-position event) :position (dom/get-client-position event)
:token-id (:id token)})))) :token-name (:name token)}))))
on-toggle-open-click (mf/use-fn on-toggle-open-click (mf/use-fn
(mf/deps open? tokens) (mf/deps open? tokens)
@ -117,7 +107,6 @@
(let [{:keys [key fields]} modal] (let [{:keys [key fields]} modal]
(dom/stop-propagation event) (dom/stop-propagation event)
(st/emit! (dt/set-token-type-section-open type true)) (st/emit! (dt/set-token-type-section-open type true))
(js/console.log "key" key)
(modal/show! key {:x (.-clientX ^js event) (modal/show! key {:x (.-clientX ^js event)
:y (.-clientY ^js event) :y (.-clientY ^js event)
:position :right :position :right
@ -151,7 +140,7 @@
(for [token (sort-by :modified-at tokens)] (for [token (sort-by :modified-at tokens)]
(let [theme-token (get active-theme-tokens (wtt/token-identifier token))] (let [theme-token (get active-theme-tokens (wtt/token-identifier token))]
[:& token-pill [:& token-pill
{:key (:id token) {:key (:name token)
:token token :token token
:theme-token theme-token :theme-token theme-token
:highlighted? (wtt/shapes-token-applied? token selected-shapes (or all-attributes attributes)) :highlighted? (wtt/shapes-token-applied? token selected-shapes (or all-attributes attributes))
@ -162,7 +151,7 @@
"Separate token-types into groups of `:empty` or `:filled` depending if tokens exist for that type. "Separate token-types into groups of `:empty` or `:filled` depending if tokens exist for that type.
Sort each group alphabetically (by their `:token-key`)." Sort each group alphabetically (by their `:token-key`)."
[tokens] [tokens]
(let [tokens-by-type (wtc/group-tokens-by-type tokens) (let [tokens-by-type (ctob/group-by-type tokens)
{:keys [empty filled]} (->> wtty/token-types {:keys [empty filled]} (->> wtty/token-types
(map (fn [[token-key token-type-props]] (map (fn [[token-key token-type-props]]
{:token-key token-key {:token-key token-key
@ -173,23 +162,6 @@
{:empty (sort-by :token-key empty) {:empty (sort-by :token-key empty)
:filled (sort-by :token-key filled)})) :filled (sort-by :token-key filled)}))
(mf/defc tokene-theme-create
[_props]
(let [group (mf/use-state "")
name (mf/use-state "")]
[:div {:style {:display "flex"
:flex-direction "column"
:gap "10px"}}
[:& labeled-input {:label "Group name"
:input-props {:value @group
:on-change #(reset! group (dom/event->value %))}}]
[:& labeled-input {:label "Theme name"
:input-props {:value @name
:on-change #(reset! name (dom/event->value %))}}]
[:button {:on-click #(st/emit! (wdt/create-token-theme {:group @group
:name @name}))}
"Create"]]))
(mf/defc edit-button (mf/defc edit-button
[{:keys [create?]}] [{:keys [create?]}]
[:button {:class (stl/css :themes-button) [:button {:class (stl/css :themes-button)
@ -200,7 +172,7 @@
(mf/defc themes-sidebar (mf/defc themes-sidebar
[_props] [_props]
(let [ordered-themes (mf/deref refs/workspace-ordered-token-themes)] (let [ordered-themes (mf/deref refs/workspace-token-themes-no-hidden)]
[:div {:class (stl/css :theme-sidebar)} [:div {:class (stl/css :theme-sidebar)}
[:span {:class (stl/css :themes-header)} "Themes"] [:span {:class (stl/css :themes-header)} "Themes"]
[:div {:class (stl/css :theme-select-wrapper)} [:div {:class (stl/css :theme-select-wrapper)}
@ -241,7 +213,6 @@
selected (mf/deref refs/selected-shapes) selected (mf/deref refs/selected-shapes)
selected-shapes (into [] (keep (d/getf objects)) selected) selected-shapes (into [] (keep (d/getf objects)) selected)
active-theme-tokens (sd/use-active-theme-sets-tokens) active-theme-tokens (sd/use-active-theme-sets-tokens)
tokens (sd/use-resolved-workspace-tokens) tokens (sd/use-resolved-workspace-tokens)

View file

@ -72,34 +72,33 @@
(defn resolve-tokens+ (defn resolve-tokens+
[tokens & {:keys [names-map?] :as config}] [tokens & {:keys [names-map?] :as config}]
(p/let [sd-tokens (-> (wtt/token-names-tree tokens) (let [{:keys [tree ids-map]} (wtt/token-names-tree-id-map tokens)]
(resolve-sd-tokens+))] (p/let [sd-tokens (resolve-sd-tokens+ tree)]
(let [resolved-tokens (reduce (let [resolved-tokens (reduce
(fn [acc ^js cur] (fn [acc ^js cur]
(let [identifier (if names-map? (let [{:keys [type] :as origin-token} (if names-map?
(.. cur -original -name) (get tokens (.. cur -original -name))
(uuid (.-uuid (.-id cur)))) (get ids-map (uuid (.-uuid (.-id cur)))))
{:keys [type] :as origin-token} (get tokens identifier) value (.-value cur)
value (.-value cur) token-or-err (case type
token-or-err (case type :color (if-let [tc (tinycolor/valid-color value)]
:color (if-let [tc (tinycolor/valid-color value)] {:value value :unit (tinycolor/color-format tc)}
{:value value :unit (tinycolor/color-format tc)} {:errors [(wte/error-with-value :error.token/invalid-color value)]})
{:errors [(wte/error-with-value :error.token/invalid-color value)]}) (or (wtt/parse-token-value value)
(or (wtt/parse-token-value value) (if-let [references (-> (wtt/find-token-references value)
(if-let [references (-> (wtt/find-token-references value) (seq))]
(seq))] {:errors [(wte/error-with-value :error.style-dictionary/missing-reference references)]
{:errors [(wte/error-with-value :error.style-dictionary/missing-reference references)] :references references}
:references references} {:errors [(wte/error-with-value :error.style-dictionary/invalid-token-value value)]})))
{:errors [(wte/error-with-value :error.style-dictionary/invalid-token-value value)]}))) output-token (if (:errors token-or-err)
output-token (if (:errors token-or-err) (merge origin-token token-or-err)
(merge origin-token token-or-err) (assoc origin-token
(assoc origin-token :resolved-value (:value token-or-err)
:resolved-value (:value token-or-err) :unit (:unit token-or-err)))]
:unit (:unit token-or-err)))] (assoc acc (wtt/token-identifier output-token) output-token)))
(assoc acc (wtt/token-identifier output-token) output-token))) {} sd-tokens)]
{} sd-tokens)] (l/debug :hint "Resolved tokens" :js/tokens resolved-tokens)
(l/debug :hint "Resolved tokens" :js/tokens resolved-tokens) resolved-tokens))))
resolved-tokens)))
;; Hooks ----------------------------------------------------------------------- ;; Hooks -----------------------------------------------------------------------
@ -115,7 +114,7 @@
This hook will return the unresolved tokens as state until they are processed, This hook will return the unresolved tokens as state until they are processed,
then the state will be updated with the resolved tokens." then the state will be updated with the resolved tokens."
[tokens & {:keys [cache-atom _names-map?] [tokens & {:keys [cache-atom names-map?]
:or {cache-atom !tokens-cache} :or {cache-atom !tokens-cache}
:as config}] :as config}]
(let [tokens-state (mf/use-state (get @cache-atom tokens))] (let [tokens-state (mf/use-state (get @cache-atom tokens))]
@ -124,8 +123,11 @@
(fn [] (fn []
(let [cached (get @cache-atom tokens)] (let [cached (get @cache-atom tokens)]
(cond (cond
(nil? tokens) (if names-map? {} [])
;; The tokens are already processing somewhere ;; The tokens are already processing somewhere
(p/promise? cached) (p/then cached #(reset! tokens-state %)) (p/promise? cached) (-> cached
(p/then #(reset! tokens-state %))
#_(p/catch js/console.error))
;; Get the cached entry ;; Get the cached entry
(some? cached) (reset! tokens-state cached) (some? cached) (reset! tokens-state cached)
;; No cached entry, start processing ;; No cached entry, start processing

View file

@ -7,6 +7,7 @@
(ns app.main.ui.workspace.tokens.theme-select (ns app.main.ui.workspace.tokens.theme-select
(:require-macros [app.main.style :as stl]) (:require-macros [app.main.style :as stl])
(:require (:require
[app.common.types.tokens-lib :as ctob]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.main.data.modal :as modal] [app.main.data.modal :as modal]
[app.main.data.tokens :as wdt] [app.main.data.tokens :as wdt]
@ -15,63 +16,58 @@
[app.main.ui.components.dropdown :refer [dropdown]] [app.main.ui.components.dropdown :refer [dropdown]]
[app.main.ui.icons :as i] [app.main.ui.icons :as i]
[app.util.dom :as dom] [app.util.dom :as dom]
[cuerdas.core :as str]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
(mf/defc themes-list (mf/defc themes-list
[{:keys [themes active-theme-ids on-close grouped?]}] [{:keys [themes active-theme-paths on-close grouped?]}]
(when (seq themes) (when (seq themes)
[:ul [:ul
(for [{:keys [id name]} themes (for [[_ {:keys [group name] :as theme}] themes
:let [selected? (get active-theme-ids id)]] :let [theme-id (ctob/theme-path theme)
[:li {:key id selected? (get active-theme-paths theme-id)]]
[:li {:key theme-id
:class (stl/css-case :class (stl/css-case
:checked-element true :checked-element true
:sub-item grouped? :sub-item grouped?
:is-selected selected?) :is-selected selected?)
:on-click (fn [e] :on-click (fn [e]
(dom/stop-propagation e) (dom/stop-propagation e)
(st/emit! (wdt/toggle-token-theme id)) (st/emit! (wdt/toggle-token-theme-active? group name))
(on-close))} (on-close))}
[:span {:class (stl/css :label)} name] [:span {:class (stl/css :label)} name]
[:span {:class (stl/css :check-icon)} i/tick]])])) [:span {:class (stl/css :check-icon)} i/tick]])]))
(mf/defc theme-options (mf/defc theme-options
[{:keys [on-close]}] [{:keys [active-theme-paths themes on-close]}]
(let [active-theme-ids (mf/deref refs/workspace-active-theme-ids) [:ul
ordered-themes (mf/deref refs/workspace-ordered-token-themes) (for [[group themes] themes]
grouped-themes (dissoc ordered-themes nil) [:li {:key group}
ungrouped-themes (get ordered-themes nil)] (when (seq group)
[:ul [:span {:class (stl/css :group)} group])
[:& themes-list {:themes ungrouped-themes [:& themes-list {:themes themes
:active-theme-ids active-theme-ids :active-theme-paths active-theme-paths
:on-close on-close}] :on-close on-close
(for [[group themes] grouped-themes] :grouped? true}]])
[:li {:key group} [:li {:class (stl/css-case :checked-element true
(when group :checked-element-button true)
[:span {:class (stl/css :group)} group]) :on-click #(modal/show! :tokens/themes {})}
[:& themes-list {:themes themes [:span "Edit themes"]
:active-theme-ids active-theme-ids [:span {:class (stl/css :icon)} i/arrow]]])
:on-close on-close
:grouped? true}]])
[:li {:class (stl/css-case :checked-element true
:checked-element-button true)
:on-click #(modal/show! :tokens/themes {})}
[:span "Edit themes"]
[:span {:class (stl/css :icon)} i/arrow]]]))
(mf/defc theme-select (mf/defc theme-select
[{:keys []}] [{:keys []}]
(let [;; Store (let [;; Store
temp-theme-id (mf/deref refs/workspace-temp-theme-id) active-theme-paths (mf/deref refs/workspace-active-theme-paths-no-hidden)
active-theme-ids (-> (mf/deref refs/workspace-active-theme-ids) active-themes-count (count active-theme-paths)
(disj temp-theme-id)) themes (mf/deref refs/workspace-token-theme-tree-no-hidden)
active-themes-count (count active-theme-ids)
themes (mf/deref refs/workspace-token-themes)
;; Data ;; Data
current-label (cond current-label (cond
(> active-themes-count 1) (str active-themes-count " themes active") (> active-themes-count 1) (str active-themes-count " themes active")
(pos? active-themes-count) (get-in themes [(first active-theme-ids) :name]) (= active-themes-count 1) (some->> (first active-theme-paths)
(ctob/split-token-theme-path)
(str/join " / "))
:else "No theme active") :else "No theme active")
;; State ;; State
@ -92,4 +88,6 @@
[:& dropdown {:show is-open? :on-close on-close-dropdown} [:& dropdown {:show is-open? :on-close on-close-dropdown}
[:div {:ref dropdown-element* [:div {:ref dropdown-element*
:class (stl/css :custom-select-dropdown)} :class (stl/css :custom-select-dropdown)}
[:& theme-options {:on-close on-close-dropdown}]]]])) [:& theme-options {:active-theme-paths active-theme-paths
:themes themes
:on-close on-close-dropdown}]]]]))

View file

@ -6,18 +6,18 @@
(:require (:require
["tinycolor2" :as tinycolor])) ["tinycolor2" :as tinycolor]))
(defn tinycolor? [x] (defn tinycolor? [^js x]
(and (instance? tinycolor x) (.isValid x))) (and (instance? tinycolor x) (.isValid x)))
(defn valid-color [color-str] (defn valid-color [color-str]
(let [tc (tinycolor color-str)] (let [tc (tinycolor color-str)]
(when (.isValid tc) tc))) (when (.isValid tc) tc)))
(defn ->hex [tc] (defn ->hex [^js tc]
(assert (tinycolor? tc)) (assert (tinycolor? tc))
(.toHex tc)) (.toHex tc))
(defn color-format [tc] (defn color-format [^js tc]
(assert (tinycolor? tc)) (assert (tinycolor? tc))
(.getFormat tc)) (.getFormat tc))

View file

@ -1,17 +1,10 @@
(ns app.main.ui.workspace.tokens.token (ns app.main.ui.workspace.tokens.token
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.data.macros :as dm]
[app.main.ui.workspace.tokens.tinycolor :as tinycolor]
[clojure.set :as set] [clojure.set :as set]
[cuerdas.core :as str] [cuerdas.core :as str]))
[app.main.ui.workspace.tokens.tinycolor :as tinycolor]))
(defn get-workspace-tokens
[state]
(get-in state [:workspace-data :tokens] {}))
(defn get-workspace-token
[token-id state]
(get-in state [:workspace-data :tokens token-id]))
(def parseable-token-value-regexp (def parseable-token-value-regexp
"Regexp that can be used to parse a number value out of resolved token value. "Regexp that can be used to parse a number value out of resolved token value.
@ -66,12 +59,6 @@
[token shape token-attributes] [token shape token-attributes]
(some #(token-attribute-applied? 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? (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`." "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] [token shapes token-attributes]
@ -117,6 +104,19 @@
(->> (map (fn [{:keys [name] :as token}] [name token]) tokens) (->> (map (fn [{:keys [name] :as token}] [name token]) tokens)
(into {}))) (into {})))
(defn token-names-tree-id-map [tokens]
(reduce
(fn [acc [_ {:keys [name] :as token}]]
(when (string? name)
(let [temp-id (random-uuid)
token (assoc token :temp/id temp-id)]
(-> acc
(assoc-in (concat [:tree] (token-name->path name)) token)
(assoc-in [:ids-map temp-id] token)))))
{:tree {}
:ids-map {}}
tokens))
(defn token-names-tree (defn token-names-tree
"Convert tokens into a nested tree with their `:name` as the path." "Convert tokens into a nested tree with their `:name` as the path."
[tokens] [tokens]

View file

@ -1,100 +1,18 @@
(ns app.main.ui.workspace.tokens.token-set (ns app.main.ui.workspace.tokens.token-set
(:require (:require
[app.common.data :refer [ordered-map]] [app.common.types.tokens-lib :as ctob]))
[app.main.ui.workspace.tokens.token :as wtt]
[clojure.set :as set])) (defn get-workspace-tokens-lib [state]
(get-in state [:workspace-data :tokens-lib]))
;; Themes ---------------------------------------------------------------------- ;; Themes ----------------------------------------------------------------------
(defn get-workspace-themes [state]
(get-in state [:workspace-data :token-themes] []))
(defn get-workspace-theme [id state]
(get-in state [:workspace-data :token-themes-index id]))
(defn get-workspace-themes-index [state]
(get-in state [:workspace-data :token-themes-index] {}))
(defn get-workspace-theme-groups [state]
(reduce
(fn [acc {:keys [group]}]
(if group
(conj acc group)
acc))
#{} (vals (get-workspace-themes-index state))))
(defn get-workspace-token-set-groups [state]
(get-in state [:workspace-data :token-set-groups]))
(defn get-workspace-ordered-themes [state]
(let [themes (get-workspace-themes state)
themes-index (get-workspace-themes-index state)]
(->> (map #(get themes-index %) themes)
(group-by :group))))
(defn get-active-theme-ids [state] (defn get-active-theme-ids [state]
(get-in state [:workspace-data :token-active-themes] #{})) (get-in state [:workspace-data :token-active-themes] #{}))
(defn get-temp-theme-id [state] (defn get-temp-theme-id [state]
(get-in state [:workspace-data :token-theme-temporary-id])) (get-in state [:workspace-data :token-theme-temporary-id]))
(defn get-active-theme-ids-or-fallback [state]
(let [active-theme-ids (get-active-theme-ids state)
temp-theme-id (get-temp-theme-id state)]
(cond
(seq active-theme-ids) active-theme-ids
temp-theme-id #{temp-theme-id})))
(defn get-active-set-ids [state]
(let [active-theme-ids (get-active-theme-ids-or-fallback state)
themes-index (get-workspace-themes-index state)
active-set-ids (reduce
(fn [acc cur]
(if-let [sets (get-in themes-index [cur :sets])]
(set/union acc sets)
acc))
#{} active-theme-ids)]
active-set-ids))
(defn get-ordered-active-set-ids [state]
(let [active-set-ids (get-active-set-ids state)
token-set-groups (get-workspace-token-set-groups state)]
(filter active-set-ids token-set-groups)))
(defn theme-ids-with-group
"Returns set of theme-ids that share the same `:group` property as the theme with `theme-id`.
Will also return matching theme-ids without a `:group` property."
[theme-id state]
(let [themes (get-workspace-themes-index state)
theme-group (get-in themes [theme-id :group])
same-group-theme-ids (->> themes
(eduction
(map val)
(filter #(= (:group %) theme-group))
(map :id))
(into #{}))]
same-group-theme-ids))
(defn toggle-active-theme-id
"Toggle a `theme-id` by checking `:token-active-themes`.
Deactivate all theme-ids that have the same group as `theme-id` when activating `theme-id`.
Ensures that the temporary theme id is selected when the resulting set is empty."
[theme-id state]
(let [temp-theme-id-set (some->> (get-temp-theme-id state) (conj #{}))
active-theme-ids (get-active-theme-ids state)
add? (not (get active-theme-ids theme-id))
;; Deactivate themes with the same group when activating a theme
same-group-ids (when add? (theme-ids-with-group theme-id state))
theme-ids-without-same-group (set/difference active-theme-ids
same-group-ids
temp-theme-id-set)
new-themes (if add?
(conj theme-ids-without-same-group theme-id)
(disj theme-ids-without-same-group theme-id))]
(if (empty? new-themes)
(or temp-theme-id-set #{})
new-themes)))
(defn update-theme-id (defn update-theme-id
[state] [state]
(let [active-themes (get-active-theme-ids state) (let [active-themes (get-active-theme-ids state)
@ -110,67 +28,29 @@
(defn add-token-set-to-token-theme [token-set-id token-theme] (defn add-token-set-to-token-theme [token-set-id token-theme]
(update token-theme :sets conj token-set-id)) (update token-theme :sets conj token-set-id))
(defn toggle-token-set-to-token-theme [token-set-id token-theme]
(update token-theme :sets #(if (get % token-set-id)
(disj % token-set-id)
(conj % token-set-id))))
;; Sets ------------------------------------------------------------------------ ;; Sets ------------------------------------------------------------------------
(defn get-workspace-sets [state] (defn get-active-theme-sets-tokens-names-map [state]
(get-in state [:workspace-data :token-sets-index])) (when-let [lib (get-workspace-tokens-lib state)]
(ctob/get-active-themes-set-tokens lib)))
(defn get-workspace-ordered-sets [state] ;; === Set selection
;; TODO Include groups
(let [top-level-set-ids (get-in state [:workspace-data :token-set-groups])
token-sets (get-workspace-sets state)]
(->> (map (fn [id] [id (get token-sets id)]) top-level-set-ids)
(into (ordered-map)))))
(defn get-workspace-ordered-sets-tokens [state]
(let [sets (get-workspace-ordered-sets state)]
(reduce
(fn [acc [_ {:keys [tokens] :as sets}]]
(reduce (fn [acc' token-id]
(if-let [token (wtt/get-workspace-token token-id state)]
(assoc acc' (wtt/token-identifier token) token)
acc'))
acc tokens))
{} sets)))
(defn get-token-set [set-id state]
(some-> (get-workspace-sets state)
(get set-id)))
(defn get-workspace-token-set-tokens [set-id state]
(-> (get-token-set set-id state)
:tokens))
(defn get-selected-token-set-id [state] (defn get-selected-token-set-id [state]
(or (get-in state [:workspace-local :selected-token-set-id]) (or (get-in state [:workspace-local :selected-token-set-id])
(get-in state [:workspace-data :token-set-groups 0]))) (some-> (get-workspace-tokens-lib state)
(ctob/get-sets)
(first)
(:name))))
(defn get-selected-token-set [state] (defn get-selected-token-set [state]
(when-let [id (get-selected-token-set-id state)] (when-let [id (get-selected-token-set-id state)]
(get-token-set id state))) (some-> (get-workspace-tokens-lib state)
(ctob/get-set id))))
(defn get-selected-token-set-tokens [state] (defn get-selected-token-set-tokens [state]
(when-let [token-set (get-selected-token-set state)] (some-> (get-selected-token-set state)
(let [tokens (or (wtt/get-workspace-tokens state) {})] :tokens))
(select-keys tokens (:tokens token-set)))))
(defn assoc-selected-token-set-id [state id] (defn assoc-selected-token-set-id [state id]
(assoc-in state [:workspace-local :selected-token-set-id] id)) (assoc-in state [:workspace-local :selected-token-set-id] id))
(defn get-active-theme-sets-tokens-names-map [state]
(let [active-set-ids (get-ordered-active-set-ids state)]
(reduce
(fn [names-map-acc set-id]
(let [token-ids (get-workspace-token-set-tokens set-id state)]
(reduce
(fn [acc token-id]
(if-let [token (wtt/get-workspace-token token-id state)]
(assoc acc (wtt/token-identifier token) token)
acc))
names-map-acc token-ids)))
(ordered-map) active-set-ids)))

View file

@ -1,5 +1,6 @@
(ns token-tests.helpers.state (ns token-tests.helpers.state
(:require (:require
[app.common.types.tokens-lib :as ctob]
[app.main.ui.workspace.tokens.style-dictionary :as sd] [app.main.ui.workspace.tokens.style-dictionary :as sd]
[beicon.v2.core :as rx] [beicon.v2.core :as rx]
[potok.v2.core :as ptk])) [potok.v2.core :as ptk]))
@ -21,11 +22,10 @@
(ptk/reify ::end+ (ptk/reify ::end+
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(->> (rx/from (sd/resolve-tokens+ (get-in state [:workspace-data :tokens]))) (->> (rx/from (-> (get-in state [:workspace-data :tokens-lib])
(rx/mapcat (ctob/get-active-themes-set-tokens)
(fn [_] (sd/resolve-tokens+ {:names-map? true})))
(rx/of (rx/mapcat #(rx/of (end)))))))
(end))))))))
(defn stop-on (defn stop-on
"Helper function to be used with async version of run-store. "Helper function to be used with async version of run-store.

View file

@ -1,16 +1,18 @@
(ns token-tests.helpers.tokens (ns token-tests.helpers.tokens
(:require (:require
[app.common.test-helpers.ids-map :as thi] [app.common.test-helpers.ids-map :as thi]
[app.main.ui.workspace.tokens.token :as wtt])) [app.main.ui.workspace.tokens.token :as wtt]
[app.common.types.tokens-lib :as ctob]))
(defn add-token [state label params] (defn add-token [state label params]
(let [id (thi/new-id! label) (let [id (thi/new-id! label)
token (assoc params :id id)] token (assoc params :id id)]
(update-in state [:data :tokens] assoc id token))) (update-in state [:data :tokens] assoc id token)))
(defn get-token [file label] (defn get-token [file name]
(let [id (thi/id label)] (some-> (get-in file [:data :tokens-lib])
(get-in file [:data :tokens id]))) (ctob/get-active-themes-set-tokens)
(get name)))
(defn apply-token-to-shape [file shape-label token-label attributes] (defn apply-token-to-shape [file shape-label token-label attributes]
(let [first-page-id (get-in file [:data :pages 0]) (let [first-page-id (get-in file [:data :pages 0])

View file

@ -1,14 +1,11 @@
(ns token-tests.logic.token-actions-test (ns token-tests.logic.token-actions-test
(:require (:require
[app.common.pprint :refer [pprint]]
[app.common.logging :as log] [app.common.logging :as log]
[app.common.test-helpers.compositions :as ctho] [app.common.test-helpers.compositions :as ctho]
[app.common.test-helpers.files :as cthf] [app.common.test-helpers.files :as cthf]
[app.common.test-helpers.shapes :as cths] [app.common.test-helpers.shapes :as cths]
[app.main.data.tokens :as wdt] [app.common.types.tokens-lib :as ctob]
[app.main.ui.workspace.tokens.changes :as wtch] [app.main.ui.workspace.tokens.changes :as wtch]
[app.main.ui.workspace.tokens.token :as wtt]
[app.main.ui.workspace.tokens.token-set :as wtts]
[cljs.test :as t :include-macros true] [cljs.test :as t :include-macros true]
[frontend-tests.helpers.pages :as thp] [frontend-tests.helpers.pages :as thp]
[frontend-tests.helpers.state :as ths] [frontend-tests.helpers.state :as ths]
@ -25,13 +22,13 @@
(cthf/sample-file :file-1 :page-label :page-1)) (cthf/sample-file :file-1 :page-label :page-1))
(def border-radius-token (def border-radius-token
{:value "12" {:name "borderRadius.sm"
:name "borderRadius.sm" :value "12"
:type :border-radius}) :type :border-radius})
(def ^:private reference-border-radius-token (def reference-border-radius-token
{:value "{borderRadius.sm} * 2" {:name "borderRadius.md"
:name "borderRadius.md" :value "{borderRadius.sm} * 2"
:type :border-radius}) :type :border-radius})
(defn setup-file-with-tokens (defn setup-file-with-tokens
@ -40,45 +37,13 @@
(ctho/add-rect :rect-1 rect-1) (ctho/add-rect :rect-1 rect-1)
(ctho/add-rect :rect-2 rect-2) (ctho/add-rect :rect-2 rect-2)
(ctho/add-rect :rect-3 rect-3) (ctho/add-rect :rect-3 rect-3)
(toht/add-token :token-1 border-radius-token) (assoc-in [:data :tokens-lib]
(toht/add-token :token-2 reference-border-radius-token))) (-> (ctob/make-tokens-lib)
(ctob/add-theme (ctob/make-token-theme :name "Theme A" :sets #{"Set A"}))
(t/deftest test-create-token (ctob/set-active-themes #{"/Theme A"})
(t/testing "creates token in new token set" (ctob/add-set (ctob/make-token-set :name "Set A"))
(t/async (ctob/add-token-in-set "Set A" (ctob/make-token border-radius-token))
done (ctob/add-token-in-set "Set A" (ctob/make-token reference-border-radius-token))))))
(let [file (setup-file)
store (ths/setup-store file)
events [(wdt/update-create-token border-radius-token)]]
(tohs/run-store-async
store done events
(fn [new-state]
(let [set-id (wtts/get-selected-token-set-id new-state)
token-set (wtts/get-token-set set-id new-state)
set-tokens (wtts/get-active-theme-sets-tokens-names-map new-state)]
(t/testing "selects created workspace set and adds token to it"
(t/is (some? token-set))
(t/is (= 1 (count set-tokens)))
(t/is (= (list border-radius-token) (->> (vals set-tokens)
(map #(dissoc % :id :modified-at)))))))))))))
(t/deftest test-create-multiple-tokens
(t/testing "uses selected tokens set when creating multiple tokens"
(t/async
done
(let [file (setup-file)
store (ths/setup-store file)
events [(wdt/update-create-token border-radius-token)
(wdt/update-create-token reference-border-radius-token)]]
(tohs/run-store-async
store done events
(fn [new-state]
(let [set-tokens (wtts/get-active-theme-sets-tokens-names-map new-state)]
(t/testing "selects created workspace set and adds token to it"
(t/is (= 2 (count set-tokens)))
(t/is (= (list border-radius-token reference-border-radius-token)
(->> (vals set-tokens)
(map #(dissoc % :id :modified-at)))))))))))))
(t/deftest test-apply-token (t/deftest test-apply-token
(t/testing "applies token to shape and updates shape attributes to resolved value" (t/testing "applies token to shape and updates shape attributes to resolved value"
@ -89,18 +54,18 @@
rect-1 (cths/get-shape file :rect-1) rect-1 (cths/get-shape file :rect-1)
events [(wtch/apply-token {:shape-ids [(:id rect-1)] events [(wtch/apply-token {:shape-ids [(:id rect-1)]
:attributes #{:rx :ry} :attributes #{:rx :ry}
:token (toht/get-token file :token-2) :token (toht/get-token file "borderRadius.md")
:on-update-shape wtch/update-shape-radius-all})]] :on-update-shape wtch/update-shape-radius-all})]]
(tohs/run-store-async (tohs/run-store-async
store done events store done events
(fn [new-state] (fn [new-state]
(let [file' (ths/get-file-from-store new-state) (let [file' (ths/get-file-from-store new-state)
token-2' (toht/get-token file' :token-2) token (toht/get-token file' "borderRadius.md")
rect-1' (cths/get-shape file' :rect-1)] rect-1' (cths/get-shape file' :rect-1)]
(t/testing "shape `:applied-tokens` got updated" (t/testing "shape `:applied-tokens` got updated"
(t/is (some? (:applied-tokens rect-1'))) (t/is (some? (:applied-tokens rect-1')))
(t/is (= (:rx (:applied-tokens rect-1')) (wtt/token-identifier token-2'))) (t/is (= (:rx (:applied-tokens rect-1')) (:name token)))
(t/is (= (:ry (:applied-tokens rect-1')) (wtt/token-identifier token-2')))) (t/is (= (:ry (:applied-tokens rect-1')) (:name token))))
(t/testing "shape radius got update to the resolved token value." (t/testing "shape radius got update to the resolved token value."
(t/is (= (:rx rect-1') 24)) (t/is (= (:rx rect-1') 24))
(t/is (= (:ry rect-1') 24)))))))))) (t/is (= (:ry rect-1') 24))))))))))
@ -114,22 +79,22 @@
rect-1 (cths/get-shape file :rect-1) rect-1 (cths/get-shape file :rect-1)
events [(wtch/apply-token {:shape-ids [(:id rect-1)] events [(wtch/apply-token {:shape-ids [(:id rect-1)]
:attributes #{:rx :ry} :attributes #{:rx :ry}
:token (toht/get-token file :token-1) :token (toht/get-token file "borderRadius.sm")
:on-update-shape wtch/update-shape-radius-all}) :on-update-shape wtch/update-shape-radius-all})
(wtch/apply-token {:shape-ids [(:id rect-1)] (wtch/apply-token {:shape-ids [(:id rect-1)]
:attributes #{:rx :ry} :attributes #{:rx :ry}
:token (toht/get-token file :token-2) :token (toht/get-token file "borderRadius.md")
:on-update-shape wtch/update-shape-radius-all})]] :on-update-shape wtch/update-shape-radius-all})]]
(tohs/run-store-async (tohs/run-store-async
store done events store done events
(fn [new-state] (fn [new-state]
(let [file' (ths/get-file-from-store new-state) (let [file' (ths/get-file-from-store new-state)
token-2' (toht/get-token file' :token-2) token (toht/get-token file' "borderRadius.md")
rect-1' (cths/get-shape file' :rect-1)] rect-1' (cths/get-shape file' :rect-1)]
(t/testing "shape `:applied-tokens` got updated" (t/testing "shape `:applied-tokens` got updated"
(t/is (some? (:applied-tokens rect-1'))) (t/is (some? (:applied-tokens rect-1')))
(t/is (= (:rx (:applied-tokens rect-1')) (wtt/token-identifier token-2'))) (t/is (= (:rx (:applied-tokens rect-1')) (:name token)))
(t/is (= (:ry (:applied-tokens rect-1')) (wtt/token-identifier token-2')))) (t/is (= (:ry (:applied-tokens rect-1')) (:name token))))
(t/testing "shape radius got update to the resolved token value." (t/testing "shape radius got update to the resolved token value."
(t/is (= (:rx rect-1') 24)) (t/is (= (:rx rect-1') 24))
(t/is (= (:ry rect-1') 24)))))))))) (t/is (= (:ry rect-1') 24))))))))))
@ -141,9 +106,9 @@
(let [file (setup-file-with-tokens) (let [file (setup-file-with-tokens)
store (ths/setup-store file) store (ths/setup-store file)
rect-1 (cths/get-shape file :rect-1) rect-1 (cths/get-shape file :rect-1)
events [;; Apply `:token-1` to all border radius attributes events [;; Apply "borderRadius.sm" to all border radius attributes
(wtch/apply-token {:attributes #{:rx :ry :r1 :r2 :r3 :r4} (wtch/apply-token {:attributes #{:rx :ry :r1 :r2 :r3 :r4}
:token (toht/get-token file :token-1) :token (toht/get-token file "borderRadius.sm")
:shape-ids [(:id rect-1)] :shape-ids [(:id rect-1)]
:on-update-shape wtch/update-shape-radius-all}) :on-update-shape wtch/update-shape-radius-all})
;; Apply single `:r1` attribute to same shape ;; Apply single `:r1` attribute to same shape
@ -151,22 +116,22 @@
;; but keep `:r4` for testing purposes ;; but keep `:r4` for testing purposes
(wtch/apply-token {:attributes #{:r1} (wtch/apply-token {:attributes #{:r1}
:attributes-to-remove #{:rx :ry :r1 :r2 :r3} :attributes-to-remove #{:rx :ry :r1 :r2 :r3}
:token (toht/get-token file :token-2) :token (toht/get-token file "borderRadius.md")
:shape-ids [(:id rect-1)] :shape-ids [(:id rect-1)]
:on-update-shape wtch/update-shape-radius-all})]] :on-update-shape wtch/update-shape-radius-all})]]
(tohs/run-store-async (tohs/run-store-async
store done events store done events
(fn [new-state] (fn [new-state]
(let [file' (ths/get-file-from-store new-state) (let [file' (ths/get-file-from-store new-state)
token-1' (toht/get-token file' :token-1) token-sm (toht/get-token file' "borderRadius.sm")
token-2' (toht/get-token file' :token-2) token-md (toht/get-token file' "borderRadius.md")
rect-1' (cths/get-shape file' :rect-1)] rect-1' (cths/get-shape file' :rect-1)]
(t/testing "other border-radius attributes got removed" (t/testing "other border-radius attributes got removed"
(t/is (nil? (:rx (:applied-tokens rect-1'))))) (t/is (nil? (:rx (:applied-tokens rect-1')))))
(t/testing "r1 got applied with :token-2" (t/testing "r1 got applied with borderRadius.md"
(t/is (= (:r1 (:applied-tokens rect-1')) (wtt/token-identifier token-2')))) (t/is (= (:r1 (:applied-tokens rect-1')) (:name token-md))))
(t/testing "while :r4 was kept" (t/testing "while :r4 was kept with borderRadius.sm"
(t/is (= (:r4 (:applied-tokens rect-1')) (wtt/token-identifier token-1')))))))))));))))))))))) (t/is (= (:r4 (:applied-tokens rect-1')) (:name token-sm)))))))))))
(t/deftest test-apply-dimensions (t/deftest test-apply-dimensions
(t/testing "applies dimensions token and updates the shapes width and height" (t/testing "applies dimensions token and updates the shapes width and height"
@ -196,30 +161,62 @@
(t/is (= (:width rect-1') 100)) (t/is (= (:width rect-1') 100))
(t/is (= (:height rect-1') 100)))))))))) (t/is (= (:height rect-1') 100))))))))))
(t/deftest test-apply-sizing (t/deftest test-apply-dimensions
(t/testing "applies sizing token and updates the shapes width and height" (t/testing "applies dimensions token and updates the shapes width and height"
(t/async (t/async
done done
(let [file (-> (setup-file-with-tokens) (let [dimensions-token {:name "dimensions.sm"
(toht/add-token :token-target {:value "100" :value "100"
:name "sizing.sm" :type :dimensions}
:type :sizing})) file (-> (setup-file-with-tokens)
(update-in [:data :tokens-lib]
#(ctob/add-token-in-set % "Set A" (ctob/make-token dimensions-token))))
store (ths/setup-store file) store (ths/setup-store file)
rect-1 (cths/get-shape file :rect-1) rect-1 (cths/get-shape file :rect-1)
events [(wtch/apply-token {:shape-ids [(:id rect-1)] events [(wtch/apply-token {:shape-ids [(:id rect-1)]
:attributes #{:width :height} :attributes #{:width :height}
:token (toht/get-token file :token-target) :token (toht/get-token file "dimensions.sm")
:on-update-shape wtch/update-shape-dimensions})]] :on-update-shape wtch/update-shape-dimensions})]]
(tohs/run-store-async (tohs/run-store-async
store done events store done events
(fn [new-state] (fn [new-state]
(let [file' (ths/get-file-from-store new-state) (let [file' (ths/get-file-from-store new-state)
token-target' (toht/get-token file' :token-target) token-target' (toht/get-token file' "dimensions.sm")
rect-1' (cths/get-shape file' :rect-1)] rect-1' (cths/get-shape file' :rect-1)]
(t/testing "shape `:applied-tokens` got updated" (t/testing "shape `:applied-tokens` got updated"
(t/is (some? (:applied-tokens rect-1'))) (t/is (some? (:applied-tokens rect-1')))
(t/is (= (:width (:applied-tokens rect-1')) (wtt/token-identifier token-target'))) (t/is (= (:width (:applied-tokens rect-1')) (:name token-target')))
(t/is (= (:height (:applied-tokens rect-1')) (wtt/token-identifier token-target')))) (t/is (= (:height (:applied-tokens rect-1')) (:name token-target'))))
(t/testing "shapes width and height got updated"
(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 [sizing-token {:name "sizing.sm"
:value "100"
:type :sizing}
file (-> (setup-file-with-tokens)
(update-in [:data :tokens-lib]
#(ctob/add-token-in-set % "Set A" (ctob/make-token sizing-token))))
store (ths/setup-store file)
rect-1 (cths/get-shape file :rect-1)
events [(wtch/apply-token {:shape-ids [(:id rect-1)]
:attributes #{:width :height}
:token (toht/get-token file "sizing.sm")
:on-update-shape wtch/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' "sizing.sm")
rect-1' (cths/get-shape file' :rect-1)]
(t/testing "shape `:applied-tokens` got updated"
(t/is (some? (:applied-tokens rect-1')))
(t/is (= (:width (:applied-tokens rect-1')) (:name token-target')))
(t/is (= (:height (:applied-tokens rect-1')) (:name token-target'))))
(t/testing "shapes width and height got updated" (t/testing "shapes width and height got updated"
(t/is (= (:width rect-1') 100)) (t/is (= (:width rect-1') 100))
(t/is (= (:height rect-1') 100)))))))))) (t/is (= (:height rect-1') 100))))))))))
@ -228,31 +225,36 @@
(t/testing "applies opacity token and updates the shapes opacity" (t/testing "applies opacity token and updates the shapes opacity"
(t/async (t/async
done done
(let [file (-> (setup-file-with-tokens) (let [opacity-float {:name "opacity.float"
(toht/add-token :opacity-float {:value "0.3" :value "0.3"
:name "opacity.float" :type :opacity}
:type :opacity}) opacity-percent {:name "opacity.percent"
(toht/add-token :opacity-percent {:value "40%" :value "40%"
:name "opacity.percent" :type :opacity}
:type :opacity}) opacity-invalid {:name "opacity.invalid"
(toht/add-token :opacity-invalid {:value "100" :value "100"
:name "opacity.invalid" :type :opacity}
:type :opacity})) file (-> (setup-file-with-tokens)
(update-in [:data :tokens-lib]
#(-> %
(ctob/add-token-in-set "Set A" (ctob/make-token opacity-float))
(ctob/add-token-in-set "Set A" (ctob/make-token opacity-percent))
(ctob/add-token-in-set "Set A" (ctob/make-token opacity-invalid)))))
store (ths/setup-store file) store (ths/setup-store file)
rect-1 (cths/get-shape file :rect-1) rect-1 (cths/get-shape file :rect-1)
rect-2 (cths/get-shape file :rect-2) rect-2 (cths/get-shape file :rect-2)
rect-3 (cths/get-shape file :rect-3) rect-3 (cths/get-shape file :rect-3)
events [(wtch/apply-token {:shape-ids [(:id rect-1)] events [(wtch/apply-token {:shape-ids [(:id rect-1)]
:attributes #{:opacity} :attributes #{:opacity}
:token (toht/get-token file :opacity-float) :token (toht/get-token file "opacity.float")
:on-update-shape wtch/update-opacity}) :on-update-shape wtch/update-opacity})
(wtch/apply-token {:shape-ids [(:id rect-2)] (wtch/apply-token {:shape-ids [(:id rect-2)]
:attributes #{:opacity} :attributes #{:opacity}
:token (toht/get-token file :opacity-percent) :token (toht/get-token file "opacity.percent")
:on-update-shape wtch/update-opacity}) :on-update-shape wtch/update-opacity})
(wtch/apply-token {:shape-ids [(:id rect-3)] (wtch/apply-token {:shape-ids [(:id rect-3)]
:attributes #{:opacity} :attributes #{:opacity}
:token (toht/get-token file :opacity-invalid) :token (toht/get-token file "opacity.invalid")
:on-update-shape wtch/update-opacity})]] :on-update-shape wtch/update-opacity})]]
(tohs/run-store-async (tohs/run-store-async
store done events store done events
@ -261,74 +263,78 @@
rect-1' (cths/get-shape file' :rect-1) rect-1' (cths/get-shape file' :rect-1)
rect-2' (cths/get-shape file' :rect-2) rect-2' (cths/get-shape file' :rect-2)
rect-3' (cths/get-shape file' :rect-3) rect-3' (cths/get-shape file' :rect-3)
token-opacity-float (toht/get-token file' :opacity-float) token-opacity-float (toht/get-token file' "opacity.float")
token-opacity-percent (toht/get-token file' :opacity-percent) token-opacity-percent (toht/get-token file' "opacity.percent")
token-opacity-invalid (toht/get-token file' :opacity-invalid)] token-opacity-invalid (toht/get-token file' "opacity.invalid")]
(t/testing "float value got translated to float and applied to opacity" (t/testing "float value got translated to float and applied to opacity"
(t/is (= (:opacity (:applied-tokens rect-1')) (wtt/token-identifier token-opacity-float))) (t/is (= (:opacity (:applied-tokens rect-1')) (:name token-opacity-float)))
(t/is (= (:opacity rect-1') 0.3))) (t/is (= (:opacity rect-1') 0.3)))
(t/testing "percentage value got translated to float and applied to opacity" (t/testing "percentage value got translated to float and applied to opacity"
(t/is (= (:opacity (:applied-tokens rect-2')) (wtt/token-identifier token-opacity-percent))) (t/is (= (:opacity (:applied-tokens rect-2')) (:name token-opacity-percent)))
(t/is (= (:opacity rect-2') 0.4))) (t/is (= (:opacity rect-2') 0.4)))
(t/testing "invalid opacity value got applied but did not change shape" (t/testing "invalid opacity value got applied but did not change shape"
(t/is (= (:opacity (:applied-tokens rect-3')) (wtt/token-identifier token-opacity-invalid))) (t/is (= (:opacity (:applied-tokens rect-3')) (:name token-opacity-invalid)))
(t/is (nil? (:opacity rect-3'))))))))))) (t/is (nil? (:opacity rect-3')))))))))))
(t/deftest test-apply-rotation (t/deftest test-apply-rotation
(t/testing "applies rotation token and updates the shapes rotation" (t/testing "applies rotation token and updates the shapes rotation"
(t/async (t/async
done done
(let [file (-> (setup-file-with-tokens) (let [rotation-token {:name "rotation.medium"
(toht/add-token :token-target {:value "120" :value "120"
:name "rotation.medium" :type :rotation}
:type :rotation})) file (-> (setup-file-with-tokens)
(update-in [:data :tokens-lib]
#(ctob/add-token-in-set % "Set A" (ctob/make-token rotation-token))))
store (ths/setup-store file) store (ths/setup-store file)
rect-1 (cths/get-shape file :rect-1) rect-1 (cths/get-shape file :rect-1)
events [(wtch/apply-token {:shape-ids [(:id rect-1)] events [(wtch/apply-token {:shape-ids [(:id rect-1)]
:attributes #{:rotation} :attributes #{:rotation}
:token (toht/get-token file :token-target) :token (toht/get-token file "rotation.medium")
:on-update-shape wtch/update-rotation})]] :on-update-shape wtch/update-rotation})]]
(tohs/run-store-async (tohs/run-store-async
store done events store done events
(fn [new-state] (fn [new-state]
(let [file' (ths/get-file-from-store new-state) (let [file' (ths/get-file-from-store new-state)
token-target' (toht/get-token file' :token-target) token-target' (toht/get-token file' "rotation.medium")
rect-1' (cths/get-shape file' :rect-1)] rect-1' (cths/get-shape file' :rect-1)]
(t/is (some? (:applied-tokens rect-1'))) (t/is (some? (:applied-tokens rect-1')))
(t/is (= (:rotation (:applied-tokens rect-1')) (wtt/token-identifier token-target'))) (t/is (= (:rotation (:applied-tokens rect-1')) (:name token-target')))
(t/is (= (:rotation rect-1') 120))))))))) (t/is (= (:rotation rect-1') 120)))))))))
(t/deftest test-apply-stroke-width (t/deftest test-apply-stroke-width
(t/testing "applies stroke-width token and updates the shapes with stroke" (t/testing "applies stroke-width token and updates the shapes with stroke"
(t/async (t/async
done done
(let [file (-> (setup-file-with-tokens {:rect-1 {:strokes [{:stroke-alignment :inner, (let [stroke-width-token {:name "stroke-width.sm"
:value "10"
:type :stroke-width}
file (-> (setup-file-with-tokens {:rect-1 {:strokes [{:stroke-alignment :inner,
:stroke-style :solid, :stroke-style :solid,
:stroke-color "#000000", :stroke-color "#000000",
:stroke-opacity 1, :stroke-opacity 1,
:stroke-width 5}]}}) :stroke-width 5}]}})
(toht/add-token :token-target {:value "10" (update-in [:data :tokens-lib]
:name "stroke-width.sm" #(ctob/add-token-in-set % "Set A" (ctob/make-token stroke-width-token))))
:type :stroke-width}))
store (ths/setup-store file) store (ths/setup-store file)
rect-with-stroke (cths/get-shape file :rect-1) rect-with-stroke (cths/get-shape file :rect-1)
rect-without-stroke (cths/get-shape file :rect-2) rect-without-stroke (cths/get-shape file :rect-2)
events [(wtch/apply-token {:shape-ids [(:id rect-with-stroke) (:id rect-without-stroke)] events [(wtch/apply-token {:shape-ids [(:id rect-with-stroke) (:id rect-without-stroke)]
:attributes #{:stroke-width} :attributes #{:stroke-width}
:token (toht/get-token file :token-target) :token (toht/get-token file "stroke-width.sm")
:on-update-shape wtch/update-stroke-width})]] :on-update-shape wtch/update-stroke-width})]]
(tohs/run-store-async (tohs/run-store-async
store done events store done events
(fn [new-state] (fn [new-state]
(let [file' (ths/get-file-from-store new-state) (let [file' (ths/get-file-from-store new-state)
token-target' (toht/get-token file' :token-target) token-target' (toht/get-token file' "stroke-width.sm")
rect-with-stroke' (cths/get-shape file' :rect-1) rect-with-stroke' (cths/get-shape file' :rect-1)
rect-without-stroke' (cths/get-shape file' :rect-2)] rect-without-stroke' (cths/get-shape file' :rect-2)]
(t/testing "token got applied to rect with stroke and shape stroke got updated" (t/testing "token got applied to rect with stroke and shape stroke got updated"
(t/is (= (:stroke-width (:applied-tokens rect-with-stroke')) (wtt/token-identifier token-target'))) (t/is (= (:stroke-width (:applied-tokens rect-with-stroke')) (:name token-target')))
(t/is (= (get-in rect-with-stroke' [:strokes 0 :stroke-width]) 10))) (t/is (= (get-in rect-with-stroke' [:strokes 0 :stroke-width]) 10)))
(t/testing "token got applied to rect without stroke but shape didnt get updated" (t/testing "token got applied to rect without stroke but shape didnt get updated"
(t/is (= (:stroke-width (:applied-tokens rect-without-stroke')) (wtt/token-identifier token-target'))) (t/is (= (:stroke-width (:applied-tokens rect-without-stroke')) (:name token-target')))
(t/is (empty? (:strokes rect-without-stroke'))))))))))) (t/is (empty? (:strokes rect-without-stroke')))))))))))
(t/deftest test-toggle-token-none (t/deftest test-toggle-token-none
@ -342,20 +348,20 @@
events [(wtch/toggle-token {:shapes [rect-1 rect-2] events [(wtch/toggle-token {:shapes [rect-1 rect-2]
:token-type-props {:attributes #{:rx :ry} :token-type-props {:attributes #{:rx :ry}
:on-update-shape wtch/update-shape-radius-all} :on-update-shape wtch/update-shape-radius-all}
:token (toht/get-token file :token-2)})]] :token (toht/get-token file "borderRadius.md")})]]
(tohs/run-store-async (tohs/run-store-async
store done events store done events
(fn [new-state] (fn [new-state]
(let [file' (ths/get-file-from-store new-state) (let [file' (ths/get-file-from-store new-state)
token-2' (toht/get-token file' :token-2) token-2' (toht/get-token file' "borderRadius.md")
rect-1' (cths/get-shape file' :rect-1) rect-1' (cths/get-shape file' :rect-1)
rect-2' (cths/get-shape file' :rect-2)] rect-2' (cths/get-shape file' :rect-2)]
(t/is (some? (:applied-tokens rect-1'))) (t/is (some? (:applied-tokens rect-1')))
(t/is (some? (:applied-tokens rect-2'))) (t/is (some? (:applied-tokens rect-2')))
(t/is (= (:rx (:applied-tokens rect-1')) (wtt/token-identifier token-2'))) (t/is (= (:rx (:applied-tokens rect-1')) (:name token-2')))
(t/is (= (:rx (:applied-tokens rect-2')) (wtt/token-identifier token-2'))) (t/is (= (:rx (:applied-tokens rect-2')) (:name token-2')))
(t/is (= (:ry (:applied-tokens rect-1')) (wtt/token-identifier token-2'))) (t/is (= (:ry (:applied-tokens rect-1')) (:name token-2')))
(t/is (= (:ry (:applied-tokens rect-2')) (wtt/token-identifier token-2'))) (t/is (= (:ry (:applied-tokens rect-2')) (:name token-2')))
(t/is (= (:rx rect-1') 24)) (t/is (= (:rx rect-1') 24))
(t/is (= (:rx rect-2') 24))))))))) (t/is (= (:rx rect-2') 24)))))))))
@ -364,8 +370,8 @@
(t/async (t/async
done done
(let [file (-> (setup-file-with-tokens) (let [file (-> (setup-file-with-tokens)
(toht/apply-token-to-shape :rect-1 :token-1 #{:rx :ry}) (toht/apply-token-to-shape :rect-1 "borderRadius.sm" #{:rx :ry})
(toht/apply-token-to-shape :rect-3 :token-2 #{:rx :ry})) (toht/apply-token-to-shape :rect-3 "borderRadius.md" #{:rx :ry}))
store (ths/setup-store file) store (ths/setup-store file)
rect-with-token (cths/get-shape file :rect-1) rect-with-token (cths/get-shape file :rect-1)
@ -373,7 +379,7 @@
rect-with-other-token (cths/get-shape file :rect-3) rect-with-other-token (cths/get-shape file :rect-3)
events [(wtch/toggle-token {:shapes [rect-with-token rect-without-token rect-with-other-token] events [(wtch/toggle-token {:shapes [rect-with-token rect-without-token rect-with-other-token]
:token (toht/get-token file :token-1) :token (toht/get-token file "borderRadius.sm")
:token-type-props {:attributes #{:rx :ry}}})]] :token-type-props {:attributes #{:rx :ry}}})]]
(tohs/run-store-async (tohs/run-store-async
store done events store done events
@ -383,7 +389,7 @@
rect-without-token' (cths/get-shape file' :rect-2) rect-without-token' (cths/get-shape file' :rect-2)
rect-with-other-token' (cths/get-shape file' :rect-3)] rect-with-other-token' (cths/get-shape file' :rect-3)]
(t/testing "rect-with-token got the token remove" (t/testing "rect-with-token got the token removed"
(t/is (nil? (:rx (:applied-tokens rect-with-token')))) (t/is (nil? (:rx (:applied-tokens rect-with-token'))))
(t/is (nil? (:ry (:applied-tokens rect-with-token'))))) (t/is (nil? (:ry (:applied-tokens rect-with-token')))))
@ -398,8 +404,8 @@
(t/async (t/async
done done
(let [file (-> (setup-file-with-tokens) (let [file (-> (setup-file-with-tokens)
(toht/apply-token-to-shape :rect-1 :token-2 #{:rx :ry}) (toht/apply-token-to-shape :rect-1 "borderRadius.md" #{:rx :ry})
(toht/apply-token-to-shape :rect-3 :token-2 #{:rx :ry})) (toht/apply-token-to-shape :rect-3 "borderRadius.md" #{:rx :ry}))
store (ths/setup-store file) store (ths/setup-store file)
rect-with-other-token-1 (cths/get-shape file :rect-1) rect-with-other-token-1 (cths/get-shape file :rect-1)
@ -407,22 +413,22 @@
rect-with-other-token-2 (cths/get-shape file :rect-3) rect-with-other-token-2 (cths/get-shape file :rect-3)
events [(wtch/toggle-token {:shapes [rect-with-other-token-1 rect-without-token rect-with-other-token-2] 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 (toht/get-token file "borderRadius.sm")
:token-type-props {:attributes #{:rx :ry}}})]] :token-type-props {:attributes #{:rx :ry}}})]]
(tohs/run-store-async (tohs/run-store-async
store done events store done events
(fn [new-state] (fn [new-state]
(let [file' (ths/get-file-from-store new-state) (let [file' (ths/get-file-from-store new-state)
target-token (toht/get-token file' :token-1) target-token (toht/get-token file' "borderRadius.sm")
rect-with-other-token-1' (cths/get-shape file' :rect-1) rect-with-other-token-1' (cths/get-shape file' :rect-1)
rect-without-token' (cths/get-shape file' :rect-2) rect-without-token' (cths/get-shape file' :rect-2)
rect-with-other-token-2' (cths/get-shape file' :rect-3)] rect-with-other-token-2' (cths/get-shape file' :rect-3)]
(t/testing "token got applied to all shapes" (t/testing "token got applied to all shapes"
(t/is (= (:rx (:applied-tokens rect-with-other-token-1')) (wtt/token-identifier target-token))) (t/is (= (:rx (:applied-tokens rect-with-other-token-1')) (:name target-token)))
(t/is (= (:rx (:applied-tokens rect-without-token')) (wtt/token-identifier target-token))) (t/is (= (:rx (:applied-tokens rect-without-token')) (:name target-token)))
(t/is (= (:rx (:applied-tokens rect-with-other-token-2')) (wtt/token-identifier target-token))) (t/is (= (:rx (:applied-tokens rect-with-other-token-2')) (:name target-token)))
(t/is (= (:ry (:applied-tokens rect-with-other-token-1')) (wtt/token-identifier target-token))) (t/is (= (:ry (:applied-tokens rect-with-other-token-1')) (:name target-token)))
(t/is (= (:ry (:applied-tokens rect-without-token')) (wtt/token-identifier target-token))) (t/is (= (:ry (:applied-tokens rect-without-token')) (:name target-token)))
(t/is (= (:ry (:applied-tokens rect-with-other-token-2')) (wtt/token-identifier target-token))))))))))) (t/is (= (:ry (:applied-tokens rect-with-other-token-2')) (:name target-token)))))))))))

View file

@ -3,39 +3,42 @@
[app.main.ui.workspace.tokens.style-dictionary :as sd] [app.main.ui.workspace.tokens.style-dictionary :as sd]
[cljs.test :as t :include-macros true] [cljs.test :as t :include-macros true]
[promesa.core :as p] [promesa.core :as p]
[app.main.ui.workspace.tokens.token :as wtt])) [app.main.ui.workspace.tokens.token :as wtt]
[app.common.data :as d]))
(def border-radius-token (def border-radius-token
{:id #uuid "8c868278-7c8d-431b-bbc9-7d8f15c8edb9" {:value "12px"
:value "12px"
:name "borderRadius.sm" :name "borderRadius.sm"
:type :border-radius}) :type :border-radius})
(def reference-border-radius-token (def reference-border-radius-token
{:id #uuid "b9448d78-fd5b-4e3d-aa32-445904063f5b" {:value "{borderRadius.sm} * 2"
:value "{borderRadius.sm} * 2"
:name "borderRadius.md-with-dashes" :name "borderRadius.md-with-dashes"
:type :border-radius}) :type :border-radius})
(def tokens {(:id border-radius-token) border-radius-token (def tokens (d/ordered-map
(:id reference-border-radius-token) reference-border-radius-token}) (:name border-radius-token) border-radius-token
(:name reference-border-radius-token) reference-border-radius-token))
(t/deftest resolve-tokens-test (t/deftest resolve-tokens-test
(t/async (t/async
done done
(t/testing "resolves tokens using style-dictionary from a ids map" (t/testing "resolves tokens using style-dictionary from a ids map"
(-> (sd/resolve-tokens+ tokens) (-> (sd/resolve-tokens+ tokens)
(p/finally (fn [resolved-tokens] (p/finally
(let [expected-tokens {"borderRadius.sm" (fn [resolved-tokens]
(assoc border-radius-token (let [expected-tokens {"borderRadius.sm"
:resolved-value 12 (assoc border-radius-token
:resolved-unit "px") :resolved-value 12
"borderRadius.md-with-dashes" :resolved-unit "px")
(assoc reference-border-radius-token "borderRadius.md-with-dashes"
:resolved-value 24 (assoc reference-border-radius-token
:resolved-unit "px")}] :resolved-value 24
(t/is (= expected-tokens resolved-tokens)) :resolved-unit "px")}]
(done)))))))) (t/is (= 12 (get-in resolved-tokens ["borderRadius.sm" :resolved-value])))
(t/is (= "px" (get-in resolved-tokens ["borderRadius.sm" :unit])))
(t/is (= 24 (get-in resolved-tokens ["borderRadius.md-with-dashes" :resolved-value])))
(t/is (= "px" (get-in resolved-tokens ["borderRadius.md-with-dashes" :unit])))
(done))))))))
(t/deftest resolve-tokens-names-map-test (t/deftest resolve-tokens-names-map-test
(t/async (t/async
@ -48,10 +51,10 @@
(let [expected-tokens {"borderRadius.sm" (let [expected-tokens {"borderRadius.sm"
(assoc border-radius-token (assoc border-radius-token
:resolved-value 12 :resolved-value 12
:resolved-unit "px") :unit "px")
"borderRadius.md-with-dashes" "borderRadius.md-with-dashes"
(assoc reference-border-radius-token (assoc reference-border-radius-token
:resolved-value 24 :resolved-value 24
:resolved-unit "px")}] :unit "px")}]
(t/is (= expected-tokens resolved-tokens)) (t/is (= expected-tokens resolved-tokens))
(done)))))))) (done))))))))

View file

@ -1,37 +0,0 @@
(ns token-tests.token-set-test
(:require
[app.main.ui.workspace.tokens.token-set :as wtts]
[cljs.test :as t]))
(t/deftest toggle-active-theme-id-test
(t/testing "toggles active theme id"
(let [state {:workspace-data {:token-themes-index {1 {:id 1}}}}]
(t/testing "activates theme with id")
(t/is (= (wtts/toggle-active-theme-id 1 state) #{1})))
(let [state {:workspace-data {:token-active-themes #{1}
:token-themes-index {1 {:id 1}}}}]
(t/testing "missing temp theme returns empty set"
(t/is (= #{} (wtts/toggle-active-theme-id 1 state)))))
(let [state {:workspace-data {:token-theme-temporary-id :temp
:token-active-themes #{1}
:token-themes-index {1 {:id 1}}}}]
(t/testing "empty set returns temp theme"
(t/is (= #{:temp} (wtts/toggle-active-theme-id 1 state)))))
(let [state {:workspace-data {:token-active-themes #{2 3 4}
:token-themes-index {1 {:id 1}
2 {:id 2}
3 {:id 3}
4 {:id 4 :group :different}}}}]
(t/testing "removes same group themes and keeps different group themes"
(t/is (= #{1 4} (wtts/toggle-active-theme-id 1 state)))))
(let [state {:workspace-data {:token-active-themes #{1 2 3 4}}
:token-themes-index {1 {:id 1}
2 {:id 2}
3 {:id 3}
4 {:id 4 :group :different}}}]
(t/testing "removes theme when active"
(t/is (= #{4 3 2} (wtts/toggle-active-theme-id 1 state)))))))

View file

@ -50,10 +50,6 @@
(t/testing "doesn't match passed `:token-attributes`" (t/testing "doesn't match passed `:token-attributes`"
(t/is (nil? (wtt/token-applied? {:name "a"} {:applied-tokens {:x "a"}} #{:y}))))) (t/is (nil? (wtt/token-applied? {:name "a"} {:applied-tokens {:x "a"}} #{:y})))))
(t/deftest token-applied-attributes
(t/is (= #{:x} (wtt/token-applied-attributes {:name "a"}
{:applied-tokens {:x "a" :y "b"}}
#{:x :missing}))))
(t/deftest shapes-ids-by-applied-attributes (t/deftest shapes-ids-by-applied-attributes
(t/testing "Returns set of matched attributes that fit the applied token" (t/testing "Returns set of matched attributes that fit the applied token"
(let [attributes #{:x :y :z} (let [attributes #{:x :y :z}