mirror of
https://github.com/penpot/penpot.git
synced 2026-06-17 21:02:05 +00:00
♻️ Move the fn to the helpers page
This commit is contained in:
parent
c96c84af5c
commit
0fa3bbdb12
@ -166,18 +166,49 @@
|
||||
(not (ctob/token-name-path-exists? token-name tokens-tree)))
|
||||
new-tokens))))]])
|
||||
|
||||
(defn token-circular-reference?
|
||||
"Checks if the given `tokens` map contains a circular reference reachable from
|
||||
`token-name`. Uses DFS with 3-color marking (:in-progress / :done) to detect
|
||||
cycles without false positives on diamond dependencies (A->B, A->C, B->C).
|
||||
Returns the token name that closes the cycle, or nil."
|
||||
[tokens token-name]
|
||||
(let [find-refs (fn [v] (when (string? v) (cto/find-token-value-references v)))
|
||||
state (atom {})]
|
||||
(letfn [(visit [name]
|
||||
(let [mark (get @state name)]
|
||||
(if (= mark :in-progress)
|
||||
name
|
||||
(when-not (= mark :done)
|
||||
(swap! state assoc name :in-progress)
|
||||
(let [token (get tokens name)
|
||||
result (when token
|
||||
(let [refs (find-refs (:value token))]
|
||||
(some visit refs)))]
|
||||
(swap! state assoc name :done)
|
||||
result)))))]
|
||||
(let [token (get tokens token-name)]
|
||||
(when token
|
||||
(let [refs (find-refs (:value token))]
|
||||
(some visit refs)))))))
|
||||
|
||||
(def schema:token-description
|
||||
[:string {:max 2048 :error/fn #(tr "errors.field-max-length" 2048)}])
|
||||
|
||||
(defn make-token-schema
|
||||
[tokens-tree token-type]
|
||||
[tokens-tree token-type current-token-path]
|
||||
[:and
|
||||
(sm/merge
|
||||
cto/schema:token-attrs
|
||||
[:map
|
||||
[:name (make-token-name-schema tokens-tree)]
|
||||
[:name (make-token-name-schema (-> tokens-tree
|
||||
(d/dissoc-in current-token-path)))]
|
||||
[:value (make-token-value-schema token-type)]
|
||||
[:description {:optional true} schema:token-description]])
|
||||
[:fn {:error/field :value
|
||||
:error/fn #(tr "errors.tokens.circular-reference")}
|
||||
(fn [{:keys [name]}]
|
||||
(when name
|
||||
(not (token-circular-reference? tokens-tree name))))]
|
||||
[:fn {:error/field :value
|
||||
:error/fn #(tr "errors.tokens.self-reference")}
|
||||
(fn [{:keys [name value]}]
|
||||
|
||||
@ -48,32 +48,6 @@
|
||||
self-reference? (get token-references token-name)]
|
||||
(boolean self-reference?)))
|
||||
|
||||
;; TODO: Review this function when tokenscript is implemented.
|
||||
(defn token-circular-reference?
|
||||
"Checks if the given `tokens` map contains a circular reference reachable from
|
||||
`token-name`. Uses DFS with 3-color marking (:in-progress / :done) to detect
|
||||
cycles without false positives on diamond dependencies (A->B, A->C, B->C).
|
||||
Returns the token name that closes the cycle, or nil."
|
||||
[tokens token-name]
|
||||
(let [find-refs (fn [v] (when (string? v) (find-token-value-references v)))
|
||||
state (atom {})]
|
||||
(letfn [(visit [name]
|
||||
(let [mark (get @state name)]
|
||||
(if (= mark :in-progress)
|
||||
name
|
||||
(when-not (= mark :done)
|
||||
(swap! state assoc name :in-progress)
|
||||
(let [token (get tokens name)
|
||||
result (when token
|
||||
(let [refs (find-refs (:value token))]
|
||||
(some visit refs)))]
|
||||
(swap! state assoc name :done)
|
||||
result)))))]
|
||||
(let [token (get tokens token-name)]
|
||||
(when token
|
||||
(let [refs (find-refs (:value token))]
|
||||
(some visit refs)))))))
|
||||
|
||||
(defn references-token?
|
||||
"Recursively check if a value references the token name. Handles strings, maps, and sequences."
|
||||
[value token-name]
|
||||
|
||||
@ -2351,6 +2351,8 @@ test.describe("Tokens tab - edition", () => {
|
||||
|
||||
// Fill in values for all fields and verify they persist when switching tabs
|
||||
await fontSizeField.fill("16");
|
||||
|
||||
await page.waitForTimeout(500);
|
||||
await expect(saveButton).toBeEnabled();
|
||||
|
||||
const fontWeightField = tokensUpdateCreateModal.getByRole("textbox", {
|
||||
|
||||
@ -44,6 +44,10 @@
|
||||
{:error/code :error.token/direct-self-reference
|
||||
:error/fn #(tr "errors.tokens.self-reference")}
|
||||
|
||||
:error.token/circular-reference
|
||||
{:error/code :error.token/circular-reference
|
||||
:error/fn #(tr "errors.tokens.circular-reference")}
|
||||
|
||||
:error.token/invalid-color
|
||||
{:error/code :error.token/invalid-color
|
||||
:error/fn #(str (tr "errors.tokens.invalid-color" %))}
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(mf/defc form*
|
||||
[{:keys [token token-type] :as props}]
|
||||
[{:keys [token token-type current-token-path] :as props}]
|
||||
(let [initial
|
||||
(mf/with-memo [token-type token]
|
||||
{:type token-type
|
||||
@ -22,7 +22,7 @@
|
||||
:description (:description token "")
|
||||
:color-result ""})
|
||||
|
||||
props (mf/spread-props props {:make-schema #(-> (cfo/make-token-schema %1 token-type)
|
||||
props (mf/spread-props props {:make-schema #(-> (cfo/make-token-schema %1 token-type current-token-path)
|
||||
(sm/dissoc-key :id)
|
||||
(sm/assoc-key :color-result :string))
|
||||
:initial initial
|
||||
|
||||
@ -73,20 +73,18 @@
|
||||
(dissoc (:name prev-token))
|
||||
(update (:name token) #(ctob/make-token (merge % prev-token token))))]
|
||||
|
||||
(if (cto/token-circular-reference? tokens (:name token))
|
||||
(rx/of {:error (wte/error-with-value :error.token/direct-self-reference nil)})
|
||||
(->> (if (contains? cf/flags :tokenscript)
|
||||
(rx/of (ts/resolve-tokens tokens))
|
||||
(sd/resolve-tokens-interactive tokens))
|
||||
(rx/mapcat
|
||||
(fn [resolved-tokens]
|
||||
(let [{:keys [errors resolved-value] :as resolved-token} (get resolved-tokens (:name token))
|
||||
resolved-value (if (contains? cf/flags :tokenscript)
|
||||
(ts/tokenscript-symbols->penpot-unit resolved-value)
|
||||
resolved-value)]
|
||||
(if resolved-value
|
||||
(rx/of {:value resolved-value})
|
||||
(rx/of {:error (first errors)})))))))))
|
||||
(->> (if (contains? cf/flags :tokenscript)
|
||||
(rx/of (ts/resolve-tokens tokens))
|
||||
(sd/resolve-tokens-interactive tokens))
|
||||
(rx/mapcat
|
||||
(fn [resolved-tokens]
|
||||
(let [{:keys [errors resolved-value] :as resolved-token} (get resolved-tokens (:name token))
|
||||
resolved-value (if (contains? cf/flags :tokenscript)
|
||||
(ts/tokenscript-symbols->penpot-unit resolved-value)
|
||||
resolved-value)]
|
||||
(if resolved-value
|
||||
(rx/of {:value resolved-value})
|
||||
(rx/of {:error (first errors)}))))))))
|
||||
|
||||
(defn- hex->color-obj
|
||||
[hex]
|
||||
|
||||
@ -50,22 +50,20 @@
|
||||
(dissoc (:name prev-token))
|
||||
(update (:name token) #(ctob/make-token (merge % prev-token token))))]
|
||||
|
||||
(if (cto/token-circular-reference? tokens (:name token))
|
||||
(rx/of {:error (wte/error-with-value :error.token/direct-self-reference nil)})
|
||||
(->> (if (contains? cf/flags :tokenscript)
|
||||
(rx/of (ts/resolve-tokens tokens))
|
||||
(sd/resolve-tokens-interactive tokens))
|
||||
(rx/mapcat
|
||||
(fn [resolved-tokens]
|
||||
(let [{:keys [errors resolved-value] :as resolved-token} (get resolved-tokens (:name token))
|
||||
resolved-value (if (contains? cf/flags :tokenscript)
|
||||
(ts/tokenscript-symbols->penpot-unit resolved-value)
|
||||
resolved-value)]
|
||||
(if resolved-value
|
||||
(rx/of {:value resolved-value})
|
||||
(rx/of {:error (if errors
|
||||
(first errors)
|
||||
(wte/error-with-value :error/unknown value))})))))))))
|
||||
(->> (if (contains? cf/flags :tokenscript)
|
||||
(rx/of (ts/resolve-tokens tokens))
|
||||
(sd/resolve-tokens-interactive tokens))
|
||||
(rx/mapcat
|
||||
(fn [resolved-tokens]
|
||||
(let [{:keys [errors resolved-value] :as resolved-token} (get resolved-tokens (:name token))
|
||||
resolved-value (if (contains? cf/flags :tokenscript)
|
||||
(ts/tokenscript-symbols->penpot-unit resolved-value)
|
||||
resolved-value)]
|
||||
(if resolved-value
|
||||
(rx/of {:value resolved-value})
|
||||
(rx/of {:error (if errors
|
||||
(first errors)
|
||||
(wte/error-with-value :error/unknown value))}))))))))
|
||||
|
||||
(mf/defc value-combobox*
|
||||
[{:keys [name tokens token token-type empty-to-end ref] :rest props}]
|
||||
|
||||
@ -68,22 +68,20 @@
|
||||
tokens
|
||||
(update tokens (:name token) #(ctob/make-token (merge % prev-token token)))]
|
||||
|
||||
(if (cto/token-circular-reference? tokens (:name token))
|
||||
(rx/of {:error (wte/error-with-value :error.token/direct-self-reference nil)})
|
||||
(->> (if (contains? cf/flags :tokenscript)
|
||||
(rx/of (ts/resolve-tokens tokens))
|
||||
(sd/resolve-tokens-interactive tokens))
|
||||
(rx/mapcat
|
||||
(fn [resolved-tokens]
|
||||
(let [{:keys [errors resolved-value] :as resolved-token} (get resolved-tokens (:name token))
|
||||
resolved-value (if (contains? cf/flags :tokenscript)
|
||||
(ts/tokenscript-symbols->penpot-unit resolved-value)
|
||||
resolved-value)]
|
||||
(if resolved-value
|
||||
(rx/of {:value resolved-value})
|
||||
(rx/of {:error (if errors
|
||||
(first errors)
|
||||
(wte/error-with-value :error/unknown value))})))))))))
|
||||
(->> (if (contains? cf/flags :tokenscript)
|
||||
(rx/of (ts/resolve-tokens tokens))
|
||||
(sd/resolve-tokens-interactive tokens))
|
||||
(rx/mapcat
|
||||
(fn [resolved-tokens]
|
||||
(let [{:keys [errors resolved-value] :as resolved-token} (get resolved-tokens (:name token))
|
||||
resolved-value (if (contains? cf/flags :tokenscript)
|
||||
(ts/tokenscript-symbols->penpot-unit resolved-value)
|
||||
resolved-value)]
|
||||
(if resolved-value
|
||||
(rx/of {:value resolved-value})
|
||||
(rx/of {:error (if errors
|
||||
(first errors)
|
||||
(wte/error-with-value :error/unknown value))}))))))))
|
||||
|
||||
(mf/defc fonts-combobox*
|
||||
[{:keys [token tokens name] :rest props}]
|
||||
|
||||
@ -171,22 +171,19 @@
|
||||
;; Remove previous token when renaming a token
|
||||
(dissoc (:name prev-token))
|
||||
(update (:name token) #(ctob/make-token (merge % prev-token token))))]
|
||||
|
||||
(if (cto/token-circular-reference? tokens (:name token))
|
||||
(rx/of {:error (wte/error-with-value :error.token/direct-self-reference nil)})
|
||||
(->> tokens
|
||||
(sd/resolve-tokens-interactive)
|
||||
(rx/mapcat
|
||||
(fn [resolved-tokens]
|
||||
(let [{:keys [errors resolved-value] :as resolved-token} (get resolved-tokens (:name token))
|
||||
resolved-value (if (contains? cf/flags :tokenscript)
|
||||
(ts/tokenscript-symbols->penpot-unit resolved-value)
|
||||
resolved-value)]
|
||||
(if resolved-value
|
||||
(rx/of {:value resolved-value})
|
||||
(rx/of {:error (if errors
|
||||
(first errors)
|
||||
(wte/error-with-value :error/unknown value))})))))))))
|
||||
(->> tokens
|
||||
(sd/resolve-tokens-interactive)
|
||||
(rx/mapcat
|
||||
(fn [resolved-tokens]
|
||||
(let [{:keys [errors resolved-value] :as resolved-token} (get resolved-tokens (:name token))
|
||||
resolved-value (if (contains? cf/flags :tokenscript)
|
||||
(ts/tokenscript-symbols->penpot-unit resolved-value)
|
||||
resolved-value)]
|
||||
(if resolved-value
|
||||
(rx/of {:value resolved-value})
|
||||
(rx/of {:error (if errors
|
||||
(first errors)
|
||||
(wte/error-with-value :error/unknown value))}))))))))
|
||||
|
||||
(mf/defc input*
|
||||
[{:keys [name tokens token] :rest props}]
|
||||
@ -328,7 +325,7 @@
|
||||
:hint-message (:message hint)
|
||||
:hint-type (:type hint)})
|
||||
props
|
||||
(if (or extra-error (and touched? error))
|
||||
(if (or extra-error (and touched? error) (and (= :line-height input-name) error))
|
||||
(mf/spread-props props {:hint-type "error"
|
||||
:hint-message (:message (or error extra-error))})
|
||||
props)
|
||||
|
||||
@ -29,7 +29,7 @@
|
||||
(default-validate-token)))
|
||||
|
||||
(mf/defc form*
|
||||
[{:keys [token token-type] :rest props}]
|
||||
[{:keys [token token-type current-token-path] :rest props}]
|
||||
(let [token
|
||||
(mf/with-memo [token]
|
||||
(if token
|
||||
@ -37,7 +37,7 @@
|
||||
{:type token-type}))
|
||||
props (mf/spread-props props {:token token
|
||||
:token-type token-type
|
||||
:make-schema #(-> (cfo/make-token-schema %1 token-type)
|
||||
:make-schema #(-> (cfo/make-token-schema %1 token-type current-token-path)
|
||||
(sm/dissoc-key :id)
|
||||
;; The value as edited in the form is a simple stirng.
|
||||
;; It's converted to vector in the validator.
|
||||
|
||||
@ -6,7 +6,6 @@
|
||||
|
||||
(ns app.main.ui.workspace.tokens.management.forms.form-container
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.types.tokens-lib :as ctob]
|
||||
[app.config :as cf]
|
||||
[app.main.refs :as refs]
|
||||
@ -35,9 +34,8 @@
|
||||
(ctob/get-token-path token))
|
||||
|
||||
tokens-tree-in-selected-set
|
||||
(mf/with-memo [token-path tokens-in-selected-set]
|
||||
(-> (ctob/tokens-tree tokens-in-selected-set)
|
||||
(d/dissoc-in token-path)))
|
||||
(mf/with-memo [tokens-in-selected-set]
|
||||
(ctob/tokens-tree tokens-in-selected-set))
|
||||
|
||||
props
|
||||
(mf/spread-props props {:token-type token-type
|
||||
@ -48,7 +46,8 @@
|
||||
|
||||
props
|
||||
(if (contains? cf/flags :token-combobox)
|
||||
(mf/spread-props props {:input-component token.controls/value-combobox*})
|
||||
(mf/spread-props props {:input-component token.controls/value-combobox*
|
||||
:current-token-path token-path})
|
||||
props)
|
||||
|
||||
text-case-props
|
||||
@ -68,5 +67,4 @@
|
||||
:text-case [:> generic/form* text-case-props]
|
||||
:text-decoration [:> generic/form* text-decoration-props]
|
||||
:font-weight [:> generic/form* font-weight-props]
|
||||
:border-radius [:> generic/form* props]
|
||||
[:> generic/form* props])))
|
||||
|
||||
@ -77,9 +77,10 @@
|
||||
initial-errors
|
||||
value-type
|
||||
value-subfield
|
||||
input-value-placeholder] :as props}]
|
||||
input-value-placeholder
|
||||
current-token-path] :as props}]
|
||||
|
||||
(let [make-schema (or make-schema #(-> (cfo/make-token-schema % token-type)
|
||||
(let [make-schema (or make-schema #(-> (cfo/make-token-schema % token-type current-token-path)
|
||||
(sm/dissoc-key :id)))
|
||||
input-component (or input-component token.controls/input*)
|
||||
validate-token (or validator default-validate-token)
|
||||
|
||||
@ -260,7 +260,7 @@
|
||||
|
||||
;; TODO: use cfo/make-schema:token-value and extend it with shadow and reference fields
|
||||
(defn- make-schema
|
||||
[tokens-tree active-tab]
|
||||
[current-token-path tokens-tree active-tab]
|
||||
(sm/schema
|
||||
[:and
|
||||
[:map
|
||||
@ -271,7 +271,8 @@
|
||||
(sm/update-properties cto/schema:token-name assoc
|
||||
:error/fn #(str (:value %) (tr "workspace.tokens.token-name-validation-error")))
|
||||
[:fn {:error/fn #(tr "workspace.tokens.token-name-duplication-validation-error" (:value %))}
|
||||
#(not (ctob/token-name-path-exists? % tokens-tree))]]]
|
||||
#(not (ctob/token-name-path-exists? % (-> tokens-tree
|
||||
(d/dissoc-in current-token-path))))]]]
|
||||
|
||||
[:value
|
||||
[:map
|
||||
@ -349,7 +350,8 @@
|
||||
|
||||
(mf/defc form*
|
||||
[{:keys [token
|
||||
token-type] :as props}]
|
||||
token-type
|
||||
current-token-path] :as props}]
|
||||
(let [token
|
||||
(mf/with-memo [token]
|
||||
(or token
|
||||
@ -372,7 +374,7 @@
|
||||
props (mf/spread-props props {:token token
|
||||
:token-type token-type
|
||||
:initial initial
|
||||
:make-schema make-schema
|
||||
:make-schema (partial make-schema current-token-path)
|
||||
:value-type :indexed
|
||||
:value-subfield :shadow
|
||||
:input-component tabs-wrapper*
|
||||
|
||||
@ -8,9 +8,9 @@
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.files.tokens :as cfo]
|
||||
[app.common.schema :as sm]
|
||||
[app.common.types.token :as cto]
|
||||
[app.common.types.tokens-lib :as ctob]
|
||||
[app.main.data.workspace.tokens.errors :as wte]
|
||||
[app.main.ui.components.radio-buttons :refer [radio-button radio-buttons]]
|
||||
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||
@ -209,19 +209,12 @@
|
||||
|
||||
;; TODO: use cfo/make-schema:token-value and extend it with typography and reference fields
|
||||
(defn- make-schema
|
||||
[tokens-tree active-tab]
|
||||
[current-token-path tokens-tree active-tab]
|
||||
(sm/schema
|
||||
[:and
|
||||
[:map
|
||||
[:name
|
||||
[:and
|
||||
[:string {:min 1 :max 255
|
||||
:error/fn #(str (:value %) (tr "workspace.tokens.token-name-length-validation-error"))}]
|
||||
(sm/update-properties cto/schema:token-name assoc
|
||||
:error/fn #(str (:value %) (tr "workspace.tokens.token-name-validation-error")))
|
||||
[:fn {:error/fn #(tr "workspace.tokens.token-name-duplication-validation-error" (:value %))}
|
||||
#(not (ctob/token-name-path-exists? % tokens-tree))]]]
|
||||
|
||||
[:name (cfo/make-token-name-schema (-> tokens-tree
|
||||
(d/dissoc-in current-token-path)))]
|
||||
[:value
|
||||
[:map
|
||||
[:font-family {:optional true} [:maybe :string]]
|
||||
@ -269,7 +262,7 @@
|
||||
result))]]))
|
||||
|
||||
(mf/defc form*
|
||||
[{:keys [token] :as props}]
|
||||
[{:keys [token current-token-path] :as props}]
|
||||
(let [initial
|
||||
(mf/with-memo [token]
|
||||
(let [value (:value token)
|
||||
@ -297,7 +290,7 @@
|
||||
:value processed-value
|
||||
:description (:description token "")}))
|
||||
props (mf/spread-props props {:initial initial
|
||||
:make-schema make-schema
|
||||
:make-schema (partial make-schema current-token-path)
|
||||
:token token
|
||||
:validator validate-typography-token
|
||||
:value-type :composite
|
||||
|
||||
@ -39,22 +39,20 @@
|
||||
:always
|
||||
(update (:name token) #(ctob/make-token (merge % prev-token token))))]
|
||||
|
||||
(if (cto/token-circular-reference? tokens' (:name token))
|
||||
(rx/throw {:errors [(wte/get-error-code :error.token/direct-self-reference)]})
|
||||
(->> (if (contains? cf/flags :tokenscript)
|
||||
(rx/of (ts/resolve-tokens tokens'))
|
||||
(sd/resolve-tokens-interactive tokens'))
|
||||
(rx/mapcat
|
||||
(fn [resolved-tokens]
|
||||
(let [resolved-token (cond-> (get resolved-tokens (:name token))
|
||||
(contains? cf/flags :tokenscript)
|
||||
(update :resolved-value ts/tokenscript-symbols->penpot-unit))]
|
||||
(cond
|
||||
(:resolved-value resolved-token)
|
||||
(rx/of resolved-token)
|
||||
(->> (if (contains? cf/flags :tokenscript)
|
||||
(rx/of (ts/resolve-tokens tokens'))
|
||||
(sd/resolve-tokens-interactive tokens'))
|
||||
(rx/mapcat
|
||||
(fn [resolved-tokens]
|
||||
(let [resolved-token (cond-> (get resolved-tokens (:name token))
|
||||
(contains? cf/flags :tokenscript)
|
||||
(update :resolved-value ts/tokenscript-symbols->penpot-unit))]
|
||||
(cond
|
||||
(:resolved-value resolved-token)
|
||||
(rx/of resolved-token)
|
||||
|
||||
:else (rx/throw {:errors (or (seq (:errors resolved-token))
|
||||
[(wte/get-error-code :error/unknown-error)])})))))))))
|
||||
:else (rx/throw {:errors (or (seq (:errors resolved-token))
|
||||
[(wte/get-error-code :error/unknown-error)])}))))))))
|
||||
|
||||
(defn- validate-token-with [token validators]
|
||||
(if-let [error (some (fn [validate] (validate token)) validators)]
|
||||
|
||||
@ -317,7 +317,8 @@
|
||||
(ctob/tokens-tree))]
|
||||
[:tuple (-> (cfo/make-token-schema
|
||||
tokens-tree
|
||||
(cto/dtcg-token-type->token-type (-> args (first) (get "type"))))
|
||||
(cto/dtcg-token-type->token-type (-> args (first) (get "type")))
|
||||
nil)
|
||||
;; Don't allow plugins to set the id
|
||||
(sm/dissoc-key :id)
|
||||
;; Instruct the json decoder in obj/reify not to process map keys (:key-fn below)
|
||||
|
||||
@ -1896,6 +1896,10 @@ msgstr "Opacity must be between 0 and 100% or 0 and 1 (e.g. 50% or 0.5)."
|
||||
msgid "errors.tokens.self-reference"
|
||||
msgstr "Token has self reference"
|
||||
|
||||
#: src/app/common/files/tokens.cljc
|
||||
msgid "errors.tokens.circular-reference"
|
||||
msgstr "Token has circular reference"
|
||||
|
||||
#: src/app/main/data/workspace/tokens/errors.cljs:124
|
||||
msgid "errors.tokens.shadow-blur-range"
|
||||
msgstr "Shadow blur must be greater than or equal to 0."
|
||||
|
||||
@ -1854,6 +1854,10 @@ msgstr "La opacidad debe estar entre 0 y 100% o 0 y 1 (p.e. 50% o 0.5)."
|
||||
msgid "errors.tokens.self-reference"
|
||||
msgstr "El token tiene una autoreferencia"
|
||||
|
||||
#: src/app/common/files/tokens.cljc
|
||||
msgid "errors.tokens.circular-reference"
|
||||
msgstr "El token tiene una referencia circular"
|
||||
|
||||
#: src/app/main/data/workspace/tokens/errors.cljs:124
|
||||
msgid "errors.tokens.shadow-blur-range"
|
||||
msgstr "El desenfoque (blur) de la sombra debe ser mayor o igual a 0."
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user