🔧 Refactor tokens source calculations

This commit is contained in:
Andrés Moya 2026-08-17 17:25:56 +02:00
parent d6896664a2
commit 8c805730d8
9 changed files with 138 additions and 70 deletions

View File

@ -512,7 +512,7 @@
tokens-lib (cfo/get-tokens-lib data)
tokens-count (if (some? tokens-lib) (count (ctob/get-all-tokens tokens-lib)) 0)
token-sets-count (if (some? tokens-lib) (count (ctob/get-sets tokens-lib)) 0)
token-themes-count (if (some? tokens-lib) (count (ctob/get-themes tokens-lib)) 0)]
token-themes-count (if (some? tokens-lib) (count (ctob/get-themes-no-hidden tokens-lib)) 0)]
{:components components-sample
:variants {:count (count variant-ids)}

View File

@ -1121,7 +1121,7 @@
(assert-library! changes)
(let [library-data (::library-data (meta changes))
file-id (:id library-data)
prev-val (:tokens-source library-data)]
prev-val (cfo/get-tokens-source library-data)]
(-> changes
(update :redo-changes conj {:type :set-tokens-source
:file-id file-id

View File

@ -424,7 +424,7 @@
(defn is-reference? [token]
(str/includes? (:value token) "{"))
;; Tokens lib in file data
;; Tokens lib and status in file data
(defn make-tokens-status-from-lib
"Make a TokensStatus from a TokensLib, activating the themes and sets
@ -463,42 +463,47 @@
(update :tokens-status #(or % (ctos/make-tokens-status)))))
(defn get-tokens-source
"Return the current tokens source of the file. When there is no explicit :tokens-source attribute,
the source is the file itself."
"Return the current value of :tokens-source attribute."
[file-data]
(:tokens-source file-data))
(defn get-effective-tokens-source
"Return the current tokens source of the file. When there is no explicit
:tokens-source attribute, the source is the file itself."
[file-data]
(or (:tokens-source file-data) (:id file-data)))
(defn set-tokens-source
[file-data tokens-source]
(assert (or (nil? tokens-source) (uuid? tokens-source)) "expected nil or valid uuid")
(assoc file-data :tokens-source tokens-source))
(defn tokens-source?
"Returns true if the given id is the current tokens source of the file-data.
When no tokens-source is set, the file's own id is considered the source if there are any tokens in it."
(defn effective-tokens-source?
"Returns true if the given id is the current tokens source of the file-data."
[file-data id]
(assert (uuid? id) "expected valid uuid")
(let [source (:tokens-source file-data)
tokens-lib (:tokens-lib file-data)
has-tokens? (and (some? tokens-lib)
(not (ctob/empty-lib? tokens-lib)))]
(if (= id (:id file-data))
(and has-tokens?
(or (nil? source) (= source id)))
(= source id))))
(= (get-effective-tokens-source file-data) id))
(defn has-own-tokens?
"Returns true if the file-data contains a tokens-library itself and the library
contains some tokens. Note that it still may be true even if the tokens source
is external (in this case the own library is inactive, but still exists)."
[file-data]
(let [tokens-lib (:tokens-lib file-data)]
(and (some? tokens-lib)
(not (ctob/empty-lib? tokens-lib)))))
(defn tokens-provider?
"Returns true if the file MAY become a tokens source. This is if the file has tokens and has
not configured another tokens source."
[file-data]
(and (some? (:tokens-lib file-data))
(or (nil? (:tokens-source file-data))
(= (:tokens-source file-data) (:id file-data)))))
(and (has-own-tokens? file-data)
(effective-tokens-source? file-data (:id file-data))))
(defn editable-tokens?
"Returns true if the file-data owns its tokens (no external source or source is itself)."
"Returns true if the file-data is its own tokens source."
[file-data]
(let [source (:tokens-source file-data)]
(or (nil? source) (= source (:id file-data)))))
(effective-tokens-source? file-data (:id file-data)))
(defn get-tokens-lib
[file-data]

View File

@ -781,6 +781,7 @@
(get-theme-tree [_] "get a nested tree of all themes in the library")
(get-theme-tree-no-hidden [_] "get a nested tree of all themes in the library except the hidden theme")
(get-themes [_] "get an ordered sequence of all themes in the library")
(get-themes-no-hidden [_] "get an ordered sequence of all themes in the library except the hidden theme")
(get-themes-in-group [_ group] "get an ordered sequence of the themes in the group")
(get-theme [_ id] "get one theme looking for id")
(get-theme-by-name [_ group name] "get one theme looking for group and name")
@ -1192,6 +1193,10 @@
(->> (tree-seq d/ordered-map? vals themes)
(filter (partial instance? TokenTheme))))
(get-themes-no-hidden [this]
(->> (get-themes this)
(remove #(hidden-theme? %))))
(get-themes-in-group [_ group]
(->> (get themes group)
(map (comp get-id val))))

View File

@ -272,9 +272,20 @@
(assoc-in [:data :tokens-source] source-id))]
(t/is (= source-id (cfo/get-tokens-source (:data file))))))
(t/testing "returns nil when no tokens-source"
(let [file (thf/sample-file :file1)]
(t/is (nil? (cfo/get-tokens-source (:data file)))))))
(t/deftest test-get-effective-tokens-source
(t/testing "returns tokens-source from file data"
(let [source-id (thi/new-id! :tokens-source)
file (-> (thf/sample-file :file1)
(assoc-in [:data :tokens-source] source-id))]
(t/is (= source-id (cfo/get-effective-tokens-source (:data file))))))
(t/testing "returns file id when no tokens-source"
(let [file (thf/sample-file :file1)]
(t/is (= (:id file) (cfo/get-tokens-source file))))))
(t/is (= (:id file) (cfo/get-effective-tokens-source file))))))
(t/deftest test-set-tokens-source
(t/testing "sets tokens-source on file data"
@ -284,17 +295,17 @@
file-data' (cfo/set-tokens-source file-data source-id)]
(t/is (= source-id (:tokens-source file-data'))))))
(t/deftest test-tokens-source?
(t/testing "no tokens, no tokens-source, ask for file id -> false"
(t/deftest test-effective-tokens-source?
(t/testing "no tokens, no tokens-source, ask for file id -> true"
(let [file (thf/sample-file :file1)
file-data (:data file)]
(t/is (false? (cfo/tokens-source? file-data (:id file-data))))))
(t/is (true? (cfo/effective-tokens-source? file-data (:id file-data))))))
(t/testing "empty tokens-lib, no tokens-source, ask for file id -> false"
(t/testing "empty tokens-lib, no tokens-source, ask for file id -> true"
(let [file (-> (thf/sample-file :file1)
(tht/add-tokens-lib))
file-data (:data file)]
(t/is (false? (cfo/tokens-source? file-data (:id file-data))))))
(t/is (true? (cfo/effective-tokens-source? file-data (:id file-data))))))
(t/testing "tokens, no tokens-source, ask for file id -> true"
(let [file (-> (thf/sample-file :file1)
@ -304,18 +315,18 @@
(ctob/add-set (ctob/make-token-set :id (thi/new-id! :set-a) :name "set-a"))
(ctob/add-token (thi/id :set-a) (ctob/make-token :id (thi/new-id! :tok1) :name "spacing" :type :spacing :value "8px")))))
file-data (:data file)]
(t/is (true? (cfo/tokens-source? file-data (:id file-data))))))
(t/is (true? (cfo/effective-tokens-source? file-data (:id file-data))))))
(t/testing "no tokens, no tokens source, ask for other id -> false"
(let [file (thf/sample-file :file1)
file-data (:data file)]
(t/is (false? (cfo/tokens-source? file-data (uuid/next))))))
(t/is (false? (cfo/effective-tokens-source? file-data (uuid/next))))))
(t/testing "empty tokens-lib, no tokens source, ask for other id -> false"
(let [file (-> (thf/sample-file :file1)
(tht/add-tokens-lib))
file-data (:data file)]
(t/is (false? (cfo/tokens-source? file-data (uuid/next))))))
(t/is (false? (cfo/effective-tokens-source? file-data (uuid/next))))))
(t/testing "tokens, no tokens source, ask for other id -> false"
(let [file (-> (thf/sample-file :file1)
@ -325,20 +336,20 @@
(ctob/add-set (ctob/make-token-set :id (thi/new-id! :set-a) :name "set-a"))
(ctob/add-token (thi/id :set-a) (ctob/make-token :id (thi/new-id! :tok1) :name "spacing" :type :spacing :value "8px")))))
file-data (:data file)]
(t/is (false? (cfo/tokens-source? file-data (uuid/next))))))
(t/is (false? (cfo/effective-tokens-source? file-data (uuid/next))))))
;; ---
(t/testing "no tokens, tokens source = file id, ask for file id -> false" ;; This case should not occur
(t/testing "no tokens, tokens source = file id, ask for file id -> true" ;; This case should not occur
(let [file (thf/sample-file :file1)
file-data (cfo/set-tokens-source (:data file) (:id (:data file)))]
(t/is (false? (cfo/tokens-source? file-data (:id file-data))))))
(t/is (true? (cfo/effective-tokens-source? file-data (:id file-data))))))
(t/testing "empty tokens-lib, tokens source = file id, ask for file id -> false" ;; This case should not occur
(t/testing "empty tokens-lib, tokens source = file id, ask for file id -> true" ;; This case should not occur
(let [file (-> (thf/sample-file :file1)
(tht/add-tokens-lib))
file-data (cfo/set-tokens-source (:data file) (:id (:data file)))]
(t/is (false? (cfo/tokens-source? file-data (:id file-data))))))
(t/is (true? (cfo/effective-tokens-source? file-data (:id file-data))))))
(t/testing "tokens, tokens source = file id, ask for file id -> true"
(let [file (-> (thf/sample-file :file1)
@ -348,18 +359,18 @@
(ctob/add-set (ctob/make-token-set :id (thi/new-id! :set-a) :name "set-a"))
(ctob/add-token (thi/id :set-a) (ctob/make-token :id (thi/new-id! :tok1) :name "spacing" :type :spacing :value "8px")))))
file-data (cfo/set-tokens-source (:data file) (:id (:data file)))]
(t/is (true? (cfo/tokens-source? file-data (:id file-data))))))
(t/is (true? (cfo/effective-tokens-source? file-data (:id file-data))))))
(t/testing "no tokens, tokens source = file id, ask for other id -> false" ;; This case should not occur
(let [file (thf/sample-file :file1)
file-data (cfo/set-tokens-source (:data file) (:id (:data file)))]
(t/is (false? (cfo/tokens-source? file-data (uuid/next))))))
(t/is (false? (cfo/effective-tokens-source? file-data (uuid/next))))))
(t/testing "empty tokens-lib, tokens source = file id, ask for other id -> false" ;; This case should not occur
(let [file (-> (thf/sample-file :file1)
(tht/add-tokens-lib))
file-data (cfo/set-tokens-source (:data file) (:id (:data file)))]
(t/is (false? (cfo/tokens-source? file-data (uuid/next))))))
(t/is (false? (cfo/effective-tokens-source? file-data (uuid/next))))))
(t/testing "tokens, tokens source = file id, ask for other id -> false"
(let [file (-> (thf/sample-file :file1)
@ -369,7 +380,7 @@
(ctob/add-set (ctob/make-token-set :id (thi/new-id! :set-a) :name "set-a"))
(ctob/add-token (thi/id :set-a) (ctob/make-token :id (thi/new-id! :tok1) :name "spacing" :type :spacing :value "8px")))))
file-data (cfo/set-tokens-source (:data file) (:id (:data file)))]
(t/is (false? (cfo/tokens-source? file-data (uuid/next))))))
(t/is (false? (cfo/effective-tokens-source? file-data (uuid/next))))))
;; ---
@ -377,14 +388,14 @@
(let [source-id (thi/new-id! :tokens-source)
file (thf/sample-file :file1)
file-data (cfo/set-tokens-source (:data file) source-id)]
(t/is (false? (cfo/tokens-source? file-data (:id file-data))))))
(t/is (false? (cfo/effective-tokens-source? file-data (:id file-data))))))
(t/testing "empty tokens-lib, tokens source = other id, ask for file id -> false"
(let [source-id (thi/new-id! :tokens-source)
file (-> (thf/sample-file :file1)
(tht/add-tokens-lib))
file-data (cfo/set-tokens-source (:data file) source-id)]
(t/is (false? (cfo/tokens-source? file-data (:id file-data))))))
(t/is (false? (cfo/effective-tokens-source? file-data (:id file-data))))))
(t/testing "tokens, tokens source = other id, ask for file id -> false"
(let [source-id (thi/new-id! :tokens-source)
@ -395,20 +406,20 @@
(ctob/add-set (ctob/make-token-set :id (thi/new-id! :set-a) :name "set-a"))
(ctob/add-token (thi/id :set-a) (ctob/make-token :id (thi/new-id! :tok1) :name "spacing" :type :spacing :value "8px")))))
file-data (cfo/set-tokens-source (:data file) source-id)]
(t/is (false? (cfo/tokens-source? file-data (:id file-data))))))
(t/is (false? (cfo/effective-tokens-source? file-data (:id file-data))))))
(t/testing "no tokens, tokens source = other id, ask for this same id -> true"
(let [source-id (thi/new-id! :tokens-source)
file (thf/sample-file :file1)
file-data (cfo/set-tokens-source (:data file) source-id)]
(t/is (true? (cfo/tokens-source? file-data source-id)))))
(t/is (true? (cfo/effective-tokens-source? file-data source-id)))))
(t/testing "empty tokens-lib, tokens source = other id, ask for this same id -> true"
(let [source-id (thi/new-id! :tokens-source)
file (-> (thf/sample-file :file1)
(tht/add-tokens-lib))
file-data (cfo/set-tokens-source (:data file) source-id)]
(t/is (true? (cfo/tokens-source? file-data source-id)))))
(t/is (true? (cfo/effective-tokens-source? file-data source-id)))))
(t/testing "tokens, tokens source = other id, ask for this same id -> true"
(let [source-id (thi/new-id! :tokens-source)
@ -419,20 +430,20 @@
(ctob/add-set (ctob/make-token-set :id (thi/new-id! :set-a) :name "set-a"))
(ctob/add-token (thi/id :set-a) (ctob/make-token :id (thi/new-id! :tok1) :name "spacing" :type :spacing :value "8px")))))
file-data (cfo/set-tokens-source (:data file) source-id)]
(t/is (true? (cfo/tokens-source? file-data source-id)))))
(t/is (true? (cfo/effective-tokens-source? file-data source-id)))))
(t/testing "no tokens, tokens source = other id, ask for a different id -> false"
(let [source-id (thi/new-id! :tokens-source)
file (thf/sample-file :file1)
file-data (cfo/set-tokens-source (:data file) source-id)]
(t/is (false? (cfo/tokens-source? file-data (uuid/next))))))
(t/is (false? (cfo/effective-tokens-source? file-data (uuid/next))))))
(t/testing "empty tokens-lib, tokens source = other id, ask for a different id -> false"
(let [source-id (thi/new-id! :tokens-source)
file (-> (thf/sample-file :file1)
(tht/add-tokens-lib))
file-data (cfo/set-tokens-source (:data file) source-id)]
(t/is (false? (cfo/tokens-source? file-data (uuid/next))))))
(t/is (false? (cfo/effective-tokens-source? file-data (uuid/next))))))
(t/testing "tokens, tokens source = other id, ask for a different id -> false"
(let [source-id (thi/new-id! :tokens-source)
@ -443,7 +454,29 @@
(ctob/add-set (ctob/make-token-set :id (thi/new-id! :set-a) :name "set-a"))
(ctob/add-token (thi/id :set-a) (ctob/make-token :id (thi/new-id! :tok1) :name "spacing" :type :spacing :value "8px")))))
file-data (cfo/set-tokens-source (:data file) source-id)]
(t/is (false? (cfo/tokens-source? file-data (uuid/next)))))))
(t/is (false? (cfo/effective-tokens-source? file-data (uuid/next)))))))
(t/deftest test-has-own-tokens?
(t/testing "no tokens-lib -> false"
(let [file (thf/sample-file :file1)
file-data (:data file)]
(t/is (false? (cfo/has-own-tokens? file-data)))))
(t/testing "empty tokens-lib -> false"
(let [file (-> (thf/sample-file :file1)
(tht/add-tokens-lib))
file-data (:data file)]
(t/is (false? (cfo/has-own-tokens? file-data)))))
(t/testing "tokens lib with data -> true"
(let [file (-> (thf/sample-file :file1)
(tht/add-tokens-lib)
(tht/update-tokens-lib
#(-> %
(ctob/add-set (ctob/make-token-set :id (thi/new-id! :set-a) :name "set-a"))
(ctob/add-token (thi/id :set-a) (ctob/make-token :id (thi/new-id! :tok1) :name "spacing" :type :spacing :value "8px")))))
file-data (:data file)]
(t/is (true? (cfo/has-own-tokens? file-data))))))
(t/deftest test-tokens-provider?
(t/testing "returns false if it has no tokens"
@ -451,21 +484,45 @@
file-data (:data file)]
(t/is (false? (cfo/tokens-provider? file-data)))))
(t/testing "returns true if it has tokens and no tokens-source"
(t/testing "returns false if it has an empty tokens-lib and no tokens-source"
(let [file (-> (thf/sample-file :file1)
(tht/add-tokens-lib))
file-data (:data file)]
(t/is (false? (cfo/tokens-provider? file-data)))))
(t/testing "returns false if it has an empty tokens lib and tokens-source is its own id"
(let [file (-> (thf/sample-file :file1)
(tht/add-tokens-lib))
file-data (cfo/set-tokens-source (:data file) (:id file))]
(t/is (false? (cfo/tokens-provider? file-data)))))
(t/testing "returns true if it has tokens and no tokens-source"
(let [file (-> (thf/sample-file :file1)
(tht/add-tokens-lib)
(tht/update-tokens-lib
#(-> %
(ctob/add-set (ctob/make-token-set :id (thi/new-id! :set-a) :name "set-a"))
(ctob/add-token (thi/id :set-a) (ctob/make-token :id (thi/new-id! :tok1) :name "spacing" :type :spacing :value "8px")))))
file-data (:data file)]
(t/is (true? (cfo/tokens-provider? file-data)))))
(t/testing "returns true if it has tokens and tokens-source is its own id"
(let [file (-> (thf/sample-file :file1)
(tht/add-tokens-lib))
(tht/add-tokens-lib)
(tht/update-tokens-lib
#(-> %
(ctob/add-set (ctob/make-token-set :id (thi/new-id! :set-a) :name "set-a"))
(ctob/add-token (thi/id :set-a) (ctob/make-token :id (thi/new-id! :tok1) :name "spacing" :type :spacing :value "8px")))))
file-data (cfo/set-tokens-source (:data file) (:id file))]
(t/is (true? (cfo/tokens-provider? file-data)))))
(t/testing "returns false if it has tokens and tokens-source is other id"
(let [file (-> (thf/sample-file :file1)
(tht/add-tokens-lib))
(tht/add-tokens-lib)
(tht/update-tokens-lib
#(-> %
(ctob/add-set (ctob/make-token-set :id (thi/new-id! :set-a) :name "set-a"))
(ctob/add-token (thi/id :set-a) (ctob/make-token :id (thi/new-id! :tok1) :name "spacing" :type :spacing :value "8px")))))
file-data (cfo/set-tokens-source (:data file) (uuid/next))]
(t/is (false? (cfo/tokens-provider? file-data))))))

View File

@ -42,7 +42,7 @@
(defn lookup-tokens-source-data
[state]
(let [current-file-data (lookup-file-data state)
tokens-source-id (cfo/get-tokens-source current-file-data)]
tokens-source-id (cfo/get-effective-tokens-source current-file-data)]
(lookup-file-data state tokens-source-id)))
(defn lookup-tokens-lib

View File

@ -62,7 +62,7 @@
tokens-lib (cfo/get-tokens-lib data)
tokens (if (some? tokens-lib) (count (ctob/get-all-tokens tokens-lib)) 0)
token-sets (if (some? tokens-lib) (count (ctob/get-sets tokens-lib)) 0)
token-themes (if (some? tokens-lib) (count (ctob/get-themes tokens-lib)) 0)
token-themes (if (some? tokens-lib) (count (ctob/get-themes-no-hidden tokens-lib)) 0)
empty? (and (zero? components)
(zero? graphics)
@ -431,7 +431,7 @@
(dwl/sync-file file-id library-id))
;; When the unlinked library is the current tokens source,
;; we must reset it to the local library.
(when (cfo/tokens-source? local-library library-id)
(when (cfo/effective-tokens-source? local-library library-id)
(st/emit! (dwtl/set-tokens-source (:id local-library)))))))
import-tokens
@ -509,21 +509,21 @@
[:div {:class (stl/css :item-content)}
[:div {:class (stl/css :item-title)} (tr "workspace.libraries.file-library")]
[:ul {:class (stl/css :item-contents)}
[:> library-description* {:summary summary}]]
]
[:> library-description* {:summary summary}]]]
(when (and (contains? cf/flags :token-lib-sync)
(cfo/tokens-source? local-library (:id local-library)))
(cfo/effective-tokens-source? local-library (:id local-library))
(cfo/has-own-tokens? local-library))
[:> text* {:class (stl/css :tokens-source-label)
:as "span"
:typography "body-medium"}
(tr "workspace.libraries.tokens-source")])
(when (contains? cf/flags :token-lib-sync)
(when (not= (cfo/get-tokens-source local-library) (:id local-library))
[:> button* {:variant "secondary"
:type "button"
:data-library-id (dm/str (:id local-library))
:on-click set-as-tokens-source}
(tr "workspace.libraries.set-as-tokens-source")]))
(when (and (contains? cf/flags :token-lib-sync)
(not (cfo/effective-tokens-source? local-library (:id local-library))))
[:> button* {:variant "secondary"
:type "button"
:data-library-id (dm/str (:id local-library))
:on-click set-as-tokens-source}
(tr "workspace.libraries.set-as-tokens-source")])
(if ^boolean is-shared
[:> button* {:variant "secondary"
@ -535,7 +535,7 @@
:type "button"
:on-click publish}
(tr "common.publish")])]
[:div {:class (stl/css :section-list-linked)}
[:> title-bar* {:collapsable false
:title (tr "workspace.libraries.connected-libraries")
@ -558,14 +558,15 @@
[:> library-description* {:summary summary :hint-name parent-name}])]]
[:div {:class (stl/css :library-actions)}
(when (and (cfo/tokens-source? local-library id) (contains? cf/flags :token-lib-sync))
(when (and (contains? cf/flags :token-lib-sync)
(cfo/effective-tokens-source? local-library id))
[:> text* {:class (stl/css :tokens-source-label)
:as "span"
:typography "body-medium"}
(tr "workspace.libraries.tokens-source")])
(if (contains? cf/flags :token-lib-sync)
(when (and (cfo/tokens-provider? (:data library))
(not (cfo/tokens-source? local-library id)))
(not (cfo/effective-tokens-source? local-library id)))
[:> button* {:variant "secondary"
:type "button"
:data-library-id (dm/str id)

View File

@ -72,7 +72,7 @@
tokens-source
(mf/with-memo [current-file-data]
(cfo/get-tokens-source current-file-data))]
(cfo/get-effective-tokens-source current-file-data))]
[:*
[:> token-set-context-menu*]
[:section {:data-testid "token-management-sidebar"

View File

@ -70,7 +70,7 @@
[file-id]
(let [file (locate-file file-id)
file-data (ctf/file-data file)
tokens-source-id (cfo/get-tokens-source file-data)
tokens-source-id (cfo/get-effective-tokens-source file-data)
tokens-file (locate-file tokens-source-id)
tokens-file-data (ctf/file-data tokens-file)]
(cfo/get-tokens-lib tokens-file-data)))