mirror of
https://github.com/penpot/penpot.git
synced 2025-01-21 14:12:36 -05:00
Add function to check if a token can be placed under a name path
This commit is contained in:
parent
48a7c52664
commit
174d91a519
3 changed files with 53 additions and 5 deletions
|
@ -13,6 +13,7 @@
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.ui.workspace.tokens.common :as tokens.common]
|
[app.main.ui.workspace.tokens.common :as tokens.common]
|
||||||
[app.main.ui.workspace.tokens.style-dictionary :as sd]
|
[app.main.ui.workspace.tokens.style-dictionary :as sd]
|
||||||
|
[app.main.ui.workspace.tokens.token :as wtt]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[malli.core :as m]
|
[malli.core :as m]
|
||||||
|
@ -41,17 +42,24 @@ Token names should only contain letters and digits separated by . characters.")}
|
||||||
(defn token-name-schema
|
(defn token-name-schema
|
||||||
"Generate a dynamic schema validation to check if a token name already exists.
|
"Generate a dynamic schema validation to check if a token name already exists.
|
||||||
`existing-token-names` should be a set of strings."
|
`existing-token-names` should be a set of strings."
|
||||||
[existing-token-names]
|
[{:keys [existing-token-names tokens-tree]}]
|
||||||
(let [non-existing-token-schema
|
(let [non-existing-token-schema
|
||||||
(m/-simple-schema
|
(m/-simple-schema
|
||||||
{:type :token/name-exists
|
{:type :token/name-exists
|
||||||
:pred #(not (get existing-token-names %))
|
:pred #(not (get existing-token-names %))
|
||||||
:type-properties {:error/fn #(str (:value %) " is an already existing token name")
|
:type-properties {:error/fn #(str (:value %) " is an already existing token name")
|
||||||
|
:existing-token-names existing-token-names}})
|
||||||
|
path-exists-schema
|
||||||
|
(m/-simple-schema
|
||||||
|
{:type :token/name-exists
|
||||||
|
:pred #(wtt/token-name-path-exists? % tokens-tree)
|
||||||
|
:type-properties {:error/fn #(str "A token already exists at the path: " (:value %))
|
||||||
:existing-token-names existing-token-names}})]
|
:existing-token-names existing-token-names}})]
|
||||||
(m/schema
|
(m/schema
|
||||||
[:and
|
[:and
|
||||||
[:string {:min 1 :max 255}]
|
[:string {:min 1 :max 255}]
|
||||||
valid-token-name-schema
|
valid-token-name-schema
|
||||||
|
path-exists-schema
|
||||||
non-existing-token-schema])))
|
non-existing-token-schema])))
|
||||||
|
|
||||||
(def token-description-schema
|
(def token-description-schema
|
||||||
|
@ -100,7 +108,7 @@ Token names should only contain letters and digits separated by . characters.")}
|
||||||
new-tokens (update tokens token-id merge {:id token-id
|
new-tokens (update tokens token-id merge {:id token-id
|
||||||
:value input
|
:value input
|
||||||
:name token-name})]
|
:name token-name})]
|
||||||
(-> (sd/resolve-tokens+ new-tokens)
|
(-> (sd/resolve-tokens+ new-tokens {:debug? true})
|
||||||
(p/then
|
(p/then
|
||||||
(fn [resolved-tokens]
|
(fn [resolved-tokens]
|
||||||
(let [{:keys [errors resolved-value] :as resolved-token} (get resolved-tokens token-id)]
|
(let [{:keys [errors resolved-value] :as resolved-token} (get resolved-tokens token-id)]
|
||||||
|
@ -141,6 +149,9 @@ Token names should only contain letters and digits separated by . characters.")}
|
||||||
{::mf/wrap-props false}
|
{::mf/wrap-props false}
|
||||||
[{:keys [token token-type] :as _args}]
|
[{:keys [token token-type] :as _args}]
|
||||||
(let [tokens (sd/use-resolved-workspace-tokens)
|
(let [tokens (sd/use-resolved-workspace-tokens)
|
||||||
|
tokens-tree (mf/use-memo
|
||||||
|
(mf/deps tokens)
|
||||||
|
#(wtt/token-names-tree tokens))
|
||||||
existing-token-names (mf/use-memo
|
existing-token-names (mf/use-memo
|
||||||
(mf/deps tokens)
|
(mf/deps tokens)
|
||||||
(fn []
|
(fn []
|
||||||
|
@ -152,10 +163,12 @@ Token names should only contain letters and digits separated by . characters.")}
|
||||||
;; Name
|
;; Name
|
||||||
name-ref (mf/use-var (:name token))
|
name-ref (mf/use-var (:name token))
|
||||||
name-errors (mf/use-state nil)
|
name-errors (mf/use-state nil)
|
||||||
|
_ (js/console.log "name-errors" @name-errors)
|
||||||
validate-name (mf/use-callback
|
validate-name (mf/use-callback
|
||||||
(mf/deps existing-token-names)
|
(mf/deps tokens-tree existing-token-names)
|
||||||
(fn [value]
|
(fn [value]
|
||||||
(let [schema (token-name-schema existing-token-names)]
|
(let [schema (token-name-schema {:existing-token-names existing-token-names
|
||||||
|
:tokens-tree tokens-tree})]
|
||||||
(m/explain schema (finalize-name value)))))
|
(m/explain schema (finalize-name value)))))
|
||||||
on-update-name-debounced (mf/use-callback
|
on-update-name-debounced (mf/use-callback
|
||||||
(debounce (fn [e]
|
(debounce (fn [e]
|
||||||
|
|
|
@ -18,3 +18,31 @@
|
||||||
(let [path (token-name->path name)]
|
(let [path (token-name->path name)]
|
||||||
(assoc-in acc path token))))
|
(assoc-in acc path token))))
|
||||||
{} tokens))
|
{} tokens))
|
||||||
|
|
||||||
|
(defn token-name-path-exists?
|
||||||
|
"Traverses the path from `token-name` down a `token-tree` and checks if a token at that path exists.
|
||||||
|
|
||||||
|
It's not allowed to create a token inside a token. E.g.:
|
||||||
|
Creating a token with
|
||||||
|
|
||||||
|
{:name \"foo.bar\"}
|
||||||
|
|
||||||
|
in the tokens tree:
|
||||||
|
|
||||||
|
{\"foo\" {:name \"other\"}}"
|
||||||
|
[token-name token-names-tree]
|
||||||
|
(let [name-path (token-name->path token-name)
|
||||||
|
result (reduce
|
||||||
|
(fn [acc cur]
|
||||||
|
(let [target (get acc cur)]
|
||||||
|
(cond
|
||||||
|
;; Path segment doesn't exist yet
|
||||||
|
(nil? target) (reduced false)
|
||||||
|
;; A token exists at this path
|
||||||
|
(:name target) (reduced true)
|
||||||
|
;; Continue traversing the true
|
||||||
|
:else target)))
|
||||||
|
token-names-tree name-path)]
|
||||||
|
(if (map? result)
|
||||||
|
(some? (:name result))
|
||||||
|
result)))
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
(t/is (= ["foo" "bar" "baz"] (wtt/token-name->path "foo..bar.baz")))
|
(t/is (= ["foo" "bar" "baz"] (wtt/token-name->path "foo..bar.baz")))
|
||||||
(t/is (= ["foo" "bar" "baz"] (wtt/token-name->path "foo..bar.baz...."))))
|
(t/is (= ["foo" "bar" "baz"] (wtt/token-name->path "foo..bar.baz...."))))
|
||||||
|
|
||||||
(t/deftest tokens-name-tree
|
(t/deftest tokens-name-tree-test
|
||||||
(t/is (= {"foo"
|
(t/is (= {"foo"
|
||||||
{"bar"
|
{"bar"
|
||||||
{"baz" {:name "foo.bar.baz", :value "a"},
|
{"baz" {:name "foo.bar.baz", :value "a"},
|
||||||
|
@ -26,3 +26,10 @@
|
||||||
:value "b"}
|
:value "b"}
|
||||||
:c {:name "baz.bar.foo"
|
:c {:name "baz.bar.foo"
|
||||||
:value "{foo.bar.baz}"}}))))
|
:value "{foo.bar.baz}"}}))))
|
||||||
|
|
||||||
|
(t/deftest token-name-path-exists?-test
|
||||||
|
(t/is (true? (wtt/token-name-path-exists? "border-radius" {"border-radius" {:name "sm"}})))
|
||||||
|
(t/is (true? (wtt/token-name-path-exists? "border-radius.sm" {"border-radius" {:name "sm"}})))
|
||||||
|
(t/is (true? (wtt/token-name-path-exists? "border-radius.sm.x" {"border-radius" {:name "sm"}})))
|
||||||
|
(t/is (false? (wtt/token-name-path-exists? "other" {"border-radius" {:name "sm"}})))
|
||||||
|
(t/is (false? (wtt/token-name-path-exists? "dark.border-radius.md" {"dark" {"border-radius" {"sm" {:name "sm"}}}}))))
|
||||||
|
|
Loading…
Add table
Reference in a new issue