mirror of
https://github.com/penpot/penpot.git
synced 2026-05-31 20:58:08 +00:00
Merge remote-tracking branch 'origin/staging' into staging-render
This commit is contained in:
commit
0644bd817e
@ -32,6 +32,7 @@
|
|||||||
- Fix viewer can update library [Taiga #13186](https://tree.taiga.io/project/penpot/issue/13186)
|
- Fix viewer can update library [Taiga #13186](https://tree.taiga.io/project/penpot/issue/13186)
|
||||||
- Fix remove fill affects different element than selected [Taiga #13128](https://tree.taiga.io/project/penpot/issue/13128)
|
- Fix remove fill affects different element than selected [Taiga #13128](https://tree.taiga.io/project/penpot/issue/13128)
|
||||||
- Fix 45 rotated board titles rendered incorrectly [Taiga #13306](https://tree.taiga.io/project/penpot/issue/13306)
|
- Fix 45 rotated board titles rendered incorrectly [Taiga #13306](https://tree.taiga.io/project/penpot/issue/13306)
|
||||||
|
- Fix cannot apply second token after creation while shape is selected [Taiga #13513](https://tree.taiga.io/project/penpot/issue/13513)
|
||||||
|
|
||||||
## 2.13.3
|
## 2.13.3
|
||||||
|
|
||||||
|
|||||||
@ -760,6 +760,21 @@
|
|||||||
default
|
default
|
||||||
v))))
|
v))))
|
||||||
|
|
||||||
|
(defn percent?
|
||||||
|
[v]
|
||||||
|
(str/numeric? (str/rtrim v "%")))
|
||||||
|
|
||||||
|
(defn parse-percent
|
||||||
|
([v]
|
||||||
|
(parse-percent v nil))
|
||||||
|
([v default]
|
||||||
|
(if (str/ends-with? v "%")
|
||||||
|
(let [v (impl-parse-double (str/trim v "%"))]
|
||||||
|
(if (or (nil? v) (nan? v))
|
||||||
|
default
|
||||||
|
(/ v 100)))
|
||||||
|
(parse-double v default))))
|
||||||
|
|
||||||
(defn parse-uuid
|
(defn parse-uuid
|
||||||
[v]
|
[v]
|
||||||
(try
|
(try
|
||||||
|
|||||||
@ -31,18 +31,56 @@
|
|||||||
(def schema:token-value-generic
|
(def schema:token-value-generic
|
||||||
[::sm/text {:error/fn token-value-empty-fn}])
|
[::sm/text {:error/fn token-value-empty-fn}])
|
||||||
|
|
||||||
|
(def schema:token-value-numeric
|
||||||
|
[:and
|
||||||
|
[::sm/text {:error/fn token-value-empty-fn}]
|
||||||
|
[:fn {:error/fn #(tr "workspace.tokens.invalid-value" (:value %))}
|
||||||
|
(fn [value]
|
||||||
|
(if (str/numeric? value)
|
||||||
|
(let [n (d/parse-double value)]
|
||||||
|
(some? n))
|
||||||
|
true))]]) ;; Leave references or formulas to be checked by the resolver
|
||||||
|
|
||||||
|
(def schema:token-value-percent
|
||||||
|
[:and
|
||||||
|
[::sm/text {:error/fn token-value-empty-fn}]
|
||||||
|
[:fn {:error/fn #(tr "workspace.tokens.value-with-percent" (:value %))}
|
||||||
|
(fn [value]
|
||||||
|
(if (d/percent? value)
|
||||||
|
(let [v (d/parse-percent value)]
|
||||||
|
(some? v))
|
||||||
|
true))]]) ;; Leave references or formulas to be checked by the resolver
|
||||||
|
|
||||||
(def schema:token-value-composite-ref
|
(def schema:token-value-composite-ref
|
||||||
[::sm/text {:error/fn token-value-empty-fn}])
|
[::sm/text {:error/fn token-value-empty-fn}])
|
||||||
|
|
||||||
|
(def schema:token-value-opacity
|
||||||
|
[:and
|
||||||
|
[::sm/text {:error/fn token-value-empty-fn}]
|
||||||
|
[:fn {:error/fn #(tr "workspace.tokens.opacity-range")}
|
||||||
|
(fn [opacity]
|
||||||
|
(if (str/numeric? opacity)
|
||||||
|
(let [n (d/parse-percent opacity)]
|
||||||
|
(and (some? n) (<= 0 n 1)))
|
||||||
|
true))]]) ;; Leave references or formulas to be checked by the resolver
|
||||||
|
|
||||||
(def schema:token-value-font-family
|
(def schema:token-value-font-family
|
||||||
[:vector ::sm/text])
|
[:or
|
||||||
|
[:vector ::sm/text]
|
||||||
|
cto/schema:token-ref])
|
||||||
|
|
||||||
|
(def schema:token-value-font-weight
|
||||||
|
[:or
|
||||||
|
[:fn {:error/fn #(tr "workspace.tokens.invalid-font-weight-token-value")}
|
||||||
|
cto/valid-font-weight-variant]
|
||||||
|
::sm/text]) ;; Leave references or formulas to be checked by the resolver
|
||||||
|
|
||||||
(def schema:token-value-typography-map
|
(def schema:token-value-typography-map
|
||||||
[:map
|
[:map
|
||||||
[:font-family {:optional true} schema:token-value-font-family]
|
[:font-family {:optional true} schema:token-value-font-family]
|
||||||
[:font-weight {:optional true} schema:token-value-generic]
|
[:font-size {:optional true} schema:token-value-numeric]
|
||||||
[:font-size {:optional true} schema:token-value-generic]
|
[:font-weight {:optional true} schema:token-value-font-weight]
|
||||||
[:line-height {:optional true} schema:token-value-generic]
|
[:line-height {:optional true} schema:token-value-percent]
|
||||||
[:letter-spacing {:optional true} schema:token-value-generic]
|
[:letter-spacing {:optional true} schema:token-value-generic]
|
||||||
[:paragraph-spacing {:optional true} schema:token-value-generic]
|
[:paragraph-spacing {:optional true} schema:token-value-generic]
|
||||||
[:text-decoration {:optional true} schema:token-value-generic]
|
[:text-decoration {:optional true} schema:token-value-generic]
|
||||||
@ -84,7 +122,10 @@
|
|||||||
[token-type]
|
[token-type]
|
||||||
[:multi {:dispatch (constantly token-type)
|
[:multi {:dispatch (constantly token-type)
|
||||||
:title "Token Value"}
|
:title "Token Value"}
|
||||||
|
[:opacity schema:token-value-opacity]
|
||||||
[:font-family schema:token-value-font-family]
|
[:font-family schema:token-value-font-family]
|
||||||
|
[:font-size schema:token-value-numeric]
|
||||||
|
[:font-weight schema:token-value-font-weight]
|
||||||
[:typography schema:token-value-typography]
|
[:typography schema:token-value-typography]
|
||||||
[:shadow schema:token-value-shadow]
|
[:shadow schema:token-value-shadow]
|
||||||
[::m/default schema:token-value-generic]])
|
[::m/default schema:token-value-generic]])
|
||||||
@ -169,7 +210,7 @@
|
|||||||
[tokens-lib set-id]
|
[tokens-lib set-id]
|
||||||
[:and
|
[:and
|
||||||
[:string {:min 1 :max 255 :error/fn #(str (:value %) (tr "workspace.tokens.token-name-length-validation-error"))}]
|
[:string {:min 1 :max 255 :error/fn #(str (:value %) (tr "workspace.tokens.token-name-length-validation-error"))}]
|
||||||
[:fn {:error/fn #(tr "errors.token-set-already-exists" (:value %))}
|
[:fn {:error/fn #(tr "errors.token-set-already-exists")}
|
||||||
(fn [name]
|
(fn [name]
|
||||||
(or (nil? tokens-lib)
|
(or (nil? tokens-lib)
|
||||||
(let [set (ctob/get-set-by-name tokens-lib name)]
|
(let [set (ctob/get-set-by-name tokens-lib name)]
|
||||||
@ -196,7 +237,7 @@
|
|||||||
[tokens-lib name theme-id]
|
[tokens-lib name theme-id]
|
||||||
[:and
|
[:and
|
||||||
[:string {:min 0 :max 255 :error/fn #(str (:value %) (tr "workspace.tokens.token-name-length-validation-error"))}]
|
[:string {:min 0 :max 255 :error/fn #(str (:value %) (tr "workspace.tokens.token-name-length-validation-error"))}]
|
||||||
[:fn {:error/fn #(tr "errors.token-theme-already-exists" (:value %))}
|
[:fn {:error/fn #(tr "errors.token-theme-already-exists")}
|
||||||
(fn [group]
|
(fn [group]
|
||||||
(or (nil? tokens-lib)
|
(or (nil? tokens-lib)
|
||||||
(let [theme (ctob/get-theme-by-name tokens-lib group name)]
|
(let [theme (ctob/get-theme-by-name tokens-lib group name)]
|
||||||
|
|||||||
@ -143,6 +143,15 @@
|
|||||||
:gen/gen sg/text}
|
:gen/gen sg/text}
|
||||||
token-name-validation-regex])
|
token-name-validation-regex])
|
||||||
|
|
||||||
|
(def token-ref-validation-regex
|
||||||
|
#"^\{[a-zA-Z0-9_-][a-zA-Z0-9$_-]*(\.[a-zA-Z0-9$_-]+)*\}$")
|
||||||
|
|
||||||
|
(def schema:token-ref
|
||||||
|
"A token reference is a token name enclosed in {}."
|
||||||
|
[:re {:title "TokenRef"
|
||||||
|
:gen/gen sg/text}
|
||||||
|
token-ref-validation-regex])
|
||||||
|
|
||||||
(def schema:token-type
|
(def schema:token-type
|
||||||
[::sm/one-of {:decode/json (fn [type]
|
[::sm/one-of {:decode/json (fn [type]
|
||||||
(if (string? type)
|
(if (string? type)
|
||||||
|
|||||||
@ -100,12 +100,14 @@
|
|||||||
|
|
||||||
(def browser-pool-factory
|
(def browser-pool-factory
|
||||||
(letfn [(create []
|
(letfn [(create []
|
||||||
(p/let [opts #js {:args #js ["--allow-insecure-localhost" "--font-render-hinting=none"]}
|
(-> (p/let [opts #js {:args #js ["--allow-insecure-localhost" "--font-render-hinting=none"]}
|
||||||
browser (.launch pw/chromium opts)
|
browser (.launch pw/chromium opts)
|
||||||
id (swap! pool-browser-id inc)]
|
id (swap! pool-browser-id inc)]
|
||||||
(l/info :origin "factory" :action "create" :browser-id id)
|
(l/info :origin "factory" :action "create" :browser-id id)
|
||||||
(unchecked-set browser "__id" id)
|
(unchecked-set browser "__id" id)
|
||||||
browser))
|
browser)
|
||||||
|
(p/catch (fn [cause]
|
||||||
|
(l/error :hint "Cannot launch the headless browser" :cause cause)))))
|
||||||
|
|
||||||
(destroy [obj]
|
(destroy [obj]
|
||||||
(let [id (unchecked-get obj "__id")]
|
(let [id (unchecked-get obj "__id")]
|
||||||
|
|||||||
@ -47,12 +47,13 @@
|
|||||||
|
|
||||||
(s/def ::params
|
(s/def ::params
|
||||||
(s/keys :req-un [::exports ::profile-id]
|
(s/keys :req-un [::exports ::profile-id]
|
||||||
:opt-un [::wait ::name ::skip-children]))
|
:opt-un [::wait ::name ::skip-children ::force-multiple]))
|
||||||
|
|
||||||
(defn handler
|
(defn handler
|
||||||
[{:keys [:request/auth-token] :as exchange} {:keys [exports] :as params}]
|
[{:keys [:request/auth-token] :as exchange} {:keys [exports force-multiple] :as params}]
|
||||||
(let [exports (prepare-exports exports auth-token)]
|
(let [exports (prepare-exports exports auth-token)]
|
||||||
(if (and (= 1 (count exports))
|
(if (and (not force-multiple)
|
||||||
|
(= 1 (count exports))
|
||||||
(= 1 (count (-> exports first :objects))))
|
(= 1 (count (-> exports first :objects))))
|
||||||
(handle-single-export exchange (-> params
|
(handle-single-export exchange (-> params
|
||||||
(assoc :export (first exports))
|
(assoc :export (first exports))
|
||||||
|
|||||||
BIN
frontend/resources/images/features/2.14-api.gif
Normal file
BIN
frontend/resources/images/features/2.14-api.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 864 KiB |
BIN
frontend/resources/images/features/2.14-icons.gif
Normal file
BIN
frontend/resources/images/features/2.14-icons.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 90 KiB |
BIN
frontend/resources/images/features/2.14-remap.jpg
Normal file
BIN
frontend/resources/images/features/2.14-remap.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 241 KiB |
BIN
frontend/resources/images/features/2.14-slide-0.jpg
Normal file
BIN
frontend/resources/images/features/2.14-slide-0.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 52 KiB |
BIN
frontend/resources/images/features/2.14-tokens-fold.gif
Normal file
BIN
frontend/resources/images/features/2.14-tokens-fold.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 250 KiB |
@ -195,7 +195,7 @@
|
|||||||
params {:exports exports
|
params {:exports exports
|
||||||
:cmd cmd
|
:cmd cmd
|
||||||
:profile-id profile-id
|
:profile-id profile-id
|
||||||
:wait false}
|
:force-multiple true}
|
||||||
|
|
||||||
progress-stream
|
progress-stream
|
||||||
(->> (ws/get-rcv-stream ws-conn)
|
(->> (ws/get-rcv-stream ws-conn)
|
||||||
|
|||||||
@ -14,6 +14,7 @@
|
|||||||
[app.main.data.modal :as modal]
|
[app.main.data.modal :as modal]
|
||||||
[app.main.data.notifications :as ntf]
|
[app.main.data.notifications :as ntf]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
|
[app.plugins.flags :as pflag]
|
||||||
[app.plugins.register :as preg]
|
[app.plugins.register :as preg]
|
||||||
[app.util.globals :as ug]
|
[app.util.globals :as ug]
|
||||||
[app.util.http :as http]
|
[app.util.http :as http]
|
||||||
@ -44,20 +45,6 @@
|
|||||||
(update [_ state]
|
(update [_ state]
|
||||||
(update-in state [:workspace-local :open-plugins] (fnil conj #{}) id))))
|
(update-in state [:workspace-local :open-plugins] (fnil conj #{}) id))))
|
||||||
|
|
||||||
(defn reset-plugin-flags
|
|
||||||
[id]
|
|
||||||
(ptk/reify ::reset-plugin-flags
|
|
||||||
ptk/UpdateEvent
|
|
||||||
(update [_ state]
|
|
||||||
(update-in state [:workspace-local :plugin-flags] assoc id {}))))
|
|
||||||
|
|
||||||
(defn set-plugin-flag
|
|
||||||
[id key value]
|
|
||||||
(ptk/reify ::set-plugin-flag
|
|
||||||
ptk/UpdateEvent
|
|
||||||
(update [_ state]
|
|
||||||
(update-in state [:workspace-local :plugin-flags id] assoc key value))))
|
|
||||||
|
|
||||||
(defn remove-current-plugin
|
(defn remove-current-plugin
|
||||||
[id]
|
[id]
|
||||||
(ptk/reify ::remove-current-plugin
|
(ptk/reify ::remove-current-plugin
|
||||||
@ -68,8 +55,8 @@
|
|||||||
(defn- load-plugin!
|
(defn- load-plugin!
|
||||||
[{:keys [plugin-id name description host code icon permissions]}]
|
[{:keys [plugin-id name description host code icon permissions]}]
|
||||||
(try
|
(try
|
||||||
(st/emit! (save-current-plugin plugin-id)
|
(st/emit! (pflag/clear plugin-id)
|
||||||
(reset-plugin-flags plugin-id))
|
(save-current-plugin plugin-id))
|
||||||
|
|
||||||
(.ɵloadPlugin
|
(.ɵloadPlugin
|
||||||
^js ug/global
|
^js ug/global
|
||||||
|
|||||||
@ -69,6 +69,10 @@
|
|||||||
(and (number-with-unit-symbol? v)
|
(and (number-with-unit-symbol? v)
|
||||||
(= (.-unit v) "rem")))
|
(= (.-unit v) "rem")))
|
||||||
|
|
||||||
|
(defn percent-number-with-unit? [v]
|
||||||
|
(and (number-with-unit-symbol? v)
|
||||||
|
(= (.-unit v) "%")))
|
||||||
|
|
||||||
(defn rem->px [^js v]
|
(defn rem->px [^js v]
|
||||||
(* (.-value v) 16))
|
(* (.-value v) 16))
|
||||||
|
|
||||||
@ -87,10 +91,12 @@
|
|||||||
|
|
||||||
(defn tokenscript-symbols->penpot-unit [^js v]
|
(defn tokenscript-symbols->penpot-unit [^js v]
|
||||||
(cond
|
(cond
|
||||||
|
(nil? v) nil
|
||||||
(structured-token? v) (structured-token->penpot-map v)
|
(structured-token? v) (structured-token->penpot-map v)
|
||||||
(list-symbol? v) (structured-token->penpot-map v)
|
(list-symbol? v) (structured-token->penpot-map v)
|
||||||
(color-symbol? v) (.-value (.to v "hex"))
|
(color-symbol? v) (.-value (.to v "hex"))
|
||||||
(rem-number-with-unit? v) (rem->px v)
|
(rem-number-with-unit? v) (rem->px v)
|
||||||
|
(percent-number-with-unit? v) (/ (.-value v) 100)
|
||||||
:else (.-value v)))
|
:else (.-value v)))
|
||||||
|
|
||||||
;; Processors ------------------------------------------------------------------
|
;; Processors ------------------------------------------------------------------
|
||||||
|
|||||||
@ -626,8 +626,8 @@
|
|||||||
objects (dsh/lookup-page-objects state)
|
objects (dsh/lookup-page-objects state)
|
||||||
text-editing? (and (some? edition)
|
text-editing? (and (some? edition)
|
||||||
(= :text (:type (get objects edition))))]
|
(= :text (:type (get objects edition))))]
|
||||||
(when (and (empty? (get state :workspace-editor-state))
|
(if (and (empty? (get state :workspace-editor-state))
|
||||||
(not text-editing?))
|
(not text-editing?))
|
||||||
(let [attributes-to-remove
|
(let [attributes-to-remove
|
||||||
;; Remove atomic typography tokens when applying composite and vice-verca
|
;; Remove atomic typography tokens when applying composite and vice-verca
|
||||||
(cond
|
(cond
|
||||||
@ -681,7 +681,12 @@
|
|||||||
(if (rx/observable? res)
|
(if (rx/observable? res)
|
||||||
res
|
res
|
||||||
(rx/of res))))
|
(rx/of res))))
|
||||||
(rx/of (dwu/commit-undo-transaction undo-id))))))))))))))
|
(rx/of (dwu/commit-undo-transaction undo-id))))))))))
|
||||||
|
|
||||||
|
(rx/of (ntf/show {:content (tr "workspace.tokens.error-text-edition")
|
||||||
|
:type :toast
|
||||||
|
:level :warning
|
||||||
|
:timeout 3000}))))))
|
||||||
|
|
||||||
(defn apply-spacing-token-separated
|
(defn apply-spacing-token-separated
|
||||||
"Handles edge-case for spacing token when applying token via toggle button.
|
"Handles edge-case for spacing token when applying token via toggle button.
|
||||||
|
|||||||
@ -8,10 +8,11 @@
|
|||||||
(:require
|
(:require
|
||||||
[app.common.json :as json]
|
[app.common.json :as json]
|
||||||
[app.common.path-names :as cpn]
|
[app.common.path-names :as cpn]
|
||||||
|
|
||||||
[app.common.types.tokens-lib :as ctob]
|
[app.common.types.tokens-lib :as ctob]
|
||||||
|
[app.config :as cf]
|
||||||
[app.main.data.notifications :as ntf]
|
[app.main.data.notifications :as ntf]
|
||||||
[app.main.data.style-dictionary :as sd]
|
[app.main.data.style-dictionary :as sd]
|
||||||
|
[app.main.data.tokenscript :as ts]
|
||||||
[app.main.data.workspace.tokens.errors :as wte]
|
[app.main.data.workspace.tokens.errors :as wte]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.util.i18n :refer [tr]]
|
[app.util.i18n :refer [tr]]
|
||||||
@ -74,15 +75,18 @@
|
|||||||
(when unknown-tokens
|
(when unknown-tokens
|
||||||
(st/emit! (show-unknown-types-warning unknown-tokens)))
|
(st/emit! (show-unknown-types-warning unknown-tokens)))
|
||||||
(try
|
(try
|
||||||
(->> (ctob/get-all-tokens-map tokens-lib)
|
(let [tokens-tree (ctob/get-all-tokens-map tokens-lib)
|
||||||
(sd/resolve-tokens-with-verbose-errors)
|
resolved-tokens (if (contains? cf/flags :tokenscript)
|
||||||
(rx/map (fn [_]
|
(rx/of (ts/resolve-tokens tokens-tree))
|
||||||
tokens-lib))
|
(sd/resolve-tokens-with-verbose-errors tokens-tree))]
|
||||||
(rx/catch (fn [sd-error]
|
(->> resolved-tokens
|
||||||
(let [reference-errors (extract-reference-errors sd-error)]
|
(rx/map (fn [_]
|
||||||
(if reference-errors
|
tokens-lib))
|
||||||
(rx/of tokens-lib)
|
(rx/catch (fn [sd-error]
|
||||||
(throw (wte/error-ex-info :error.import/style-dictionary-unknown-error sd-error sd-error)))))))
|
(let [reference-errors (extract-reference-errors sd-error)]
|
||||||
|
(if reference-errors
|
||||||
|
(rx/of tokens-lib)
|
||||||
|
(throw (wte/error-ex-info :error.import/style-dictionary-unknown-error sd-error sd-error))))))))
|
||||||
(catch js/Error e
|
(catch js/Error e
|
||||||
(throw (wte/error-ex-info :error.import/style-dictionary-unknown-error "" e)))))
|
(throw (wte/error-ex-info :error.import/style-dictionary-unknown-error "" e)))))
|
||||||
|
|
||||||
|
|||||||
@ -6,13 +6,16 @@
|
|||||||
|
|
||||||
(ns app.main.data.workspace.tokens.propagation
|
(ns app.main.data.workspace.tokens.propagation
|
||||||
(:require
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
[app.common.files.helpers :as cfh]
|
[app.common.files.helpers :as cfh]
|
||||||
[app.common.logging :as l]
|
[app.common.logging :as l]
|
||||||
[app.common.time :as ct]
|
[app.common.time :as ct]
|
||||||
[app.common.types.token :as ctt]
|
[app.common.types.token :as ctt]
|
||||||
[app.common.types.tokens-lib :as ctob]
|
[app.common.types.tokens-lib :as ctob]
|
||||||
|
[app.config :as cf]
|
||||||
[app.main.data.helpers :as dsh]
|
[app.main.data.helpers :as dsh]
|
||||||
[app.main.data.style-dictionary :as sd]
|
[app.main.data.style-dictionary :as sd]
|
||||||
|
[app.main.data.tokenscript :as ts]
|
||||||
[app.main.data.workspace.shapes :as dwsh]
|
[app.main.data.workspace.shapes :as dwsh]
|
||||||
[app.main.data.workspace.thumbnails :as dwt]
|
[app.main.data.workspace.thumbnails :as dwt]
|
||||||
[app.main.data.workspace.tokens.application :as dwta]
|
[app.main.data.workspace.tokens.application :as dwta]
|
||||||
@ -210,10 +213,13 @@
|
|||||||
(ptk/reify ::propagate-workspace-tokens
|
(ptk/reify ::propagate-workspace-tokens
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state _]
|
(watch [_ state _]
|
||||||
(when-let [tokens-lib (-> (dsh/lookup-file-data state)
|
(when-let [tokens-tree (-> (dsh/lookup-file-data state)
|
||||||
(get :tokens-lib))]
|
(get :tokens-lib)
|
||||||
(->> (ctob/get-tokens-in-active-sets tokens-lib)
|
(ctob/get-tokens-in-active-sets))]
|
||||||
(sd/resolve-tokens)
|
(->> (if (contains? cf/flags :tokenscript)
|
||||||
|
(rx/of (-> (ts/resolve-tokens tokens-tree)
|
||||||
|
(d/update-vals #(update % :resolved-value ts/tokenscript-symbols->penpot-unit))))
|
||||||
|
(sd/resolve-tokens tokens-tree))
|
||||||
(rx/mapcat (fn [sd-tokens]
|
(rx/mapcat (fn [sd-tokens]
|
||||||
(let [undo-id (js/Symbol)]
|
(let [undo-id (js/Symbol)]
|
||||||
(rx/concat
|
(rx/concat
|
||||||
|
|||||||
@ -1173,7 +1173,8 @@
|
|||||||
(when add-component-to-variant?
|
(when add-component-to-variant?
|
||||||
(rx/of (ev/event {::ev/name "add-component-to-variant"})))
|
(rx/of (ev/event {::ev/name "add-component-to-variant"})))
|
||||||
(when add-new-variant?
|
(when add-new-variant?
|
||||||
(rx/of (ev/event {::ev/name "add-new-variant" ::ev/origin "workspace:move-shapes-to-frame"}))))))))
|
(rx/of (ev/event {::ev/name "add-new-variant"
|
||||||
|
::ev/origin "workspace:move-shapes-to-frame"}))))))))
|
||||||
|
|
||||||
(defn- get-displacement
|
(defn- get-displacement
|
||||||
"Retrieve the correct displacement delta point for the
|
"Retrieve the correct displacement delta point for the
|
||||||
|
|||||||
@ -84,6 +84,7 @@
|
|||||||
:on-click on-icon-click}])
|
:on-click on-icon-click}])
|
||||||
(if aria-label
|
(if aria-label
|
||||||
[:> tooltip* {:content aria-label
|
[:> tooltip* {:content aria-label
|
||||||
|
:class (stl/css :tooltip-wrapper)
|
||||||
:id tooltip-id}
|
:id tooltip-id}
|
||||||
[:> "input" props]]
|
[:> "input" props]]
|
||||||
[:> "input" props])
|
[:> "input" props])
|
||||||
|
|||||||
@ -120,3 +120,7 @@
|
|||||||
color: var(--color-foreground-secondary);
|
color: var(--color-foreground-secondary);
|
||||||
min-inline-size: var(--sp-l);
|
min-inline-size: var(--sp-l);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tooltip-wrapper {
|
||||||
|
inline-size: 100%;
|
||||||
|
}
|
||||||
|
|||||||
@ -32,6 +32,7 @@
|
|||||||
[app.main.ui.releases.v2-11]
|
[app.main.ui.releases.v2-11]
|
||||||
[app.main.ui.releases.v2-12]
|
[app.main.ui.releases.v2-12]
|
||||||
[app.main.ui.releases.v2-13]
|
[app.main.ui.releases.v2-13]
|
||||||
|
[app.main.ui.releases.v2-14]
|
||||||
[app.main.ui.releases.v2-2]
|
[app.main.ui.releases.v2-2]
|
||||||
[app.main.ui.releases.v2-3]
|
[app.main.ui.releases.v2-3]
|
||||||
[app.main.ui.releases.v2-4]
|
[app.main.ui.releases.v2-4]
|
||||||
@ -104,4 +105,4 @@
|
|||||||
|
|
||||||
(defmethod rc/render-release-notes "0.0"
|
(defmethod rc/render-release-notes "0.0"
|
||||||
[params]
|
[params]
|
||||||
(rc/render-release-notes (assoc params :version "2.13")))
|
(rc/render-release-notes (assoc params :version "2.14")))
|
||||||
|
|||||||
178
frontend/src/app/main/ui/releases/v2_14.cljs
Normal file
178
frontend/src/app/main/ui/releases/v2_14.cljs
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
;;
|
||||||
|
;; Copyright (c) KALEIDOS INC
|
||||||
|
|
||||||
|
(ns app.main.ui.releases.v2-14
|
||||||
|
(:require-macros [app.main.style :as stl])
|
||||||
|
(:require
|
||||||
|
[app.common.data.macros :as dm]
|
||||||
|
[app.main.ui.releases.common :as c]
|
||||||
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
|
(defmethod c/render-release-notes "2.14"
|
||||||
|
[{:keys [slide klass next finish navigate version]}]
|
||||||
|
(mf/html
|
||||||
|
(case slide
|
||||||
|
:start
|
||||||
|
[:div {:class (stl/css-case :modal-overlay true)}
|
||||||
|
[:div.animated {:class klass}
|
||||||
|
[:div {:class (stl/css :modal-container)}
|
||||||
|
[:img {:src "images/features/2.14-slide-0.jpg"
|
||||||
|
:class (stl/css :start-image)
|
||||||
|
:border "0"
|
||||||
|
:alt "Penpot 2.14 is here!"}]
|
||||||
|
|
||||||
|
[:div {:class (stl/css :modal-content)}
|
||||||
|
[:div {:class (stl/css :modal-header)}
|
||||||
|
[:h1 {:class (stl/css :modal-title)}
|
||||||
|
"What’s new in Penpot?"]
|
||||||
|
|
||||||
|
[:div {:class (stl/css :version-tag)}
|
||||||
|
(dm/str "Version " version)]]
|
||||||
|
|
||||||
|
[:div {:class (stl/css :features-block)}
|
||||||
|
[:span {:class (stl/css :feature-title)}
|
||||||
|
"Design tokens, but friendlier (and a bit faster, too)"]
|
||||||
|
|
||||||
|
[:p {:class (stl/css :feature-content)}
|
||||||
|
"This release keeps pushing Penpot’s design system foundations forward, with a big focus on design tokens. We’re making long token names easier to navigate, opening up tokens in the plugins API, and tackling one of the trickiest moments in token workflows: renaming (without breaking everything)."]
|
||||||
|
|
||||||
|
[:p {:class (stl/css :feature-content)}
|
||||||
|
"On top of that, you’ll find a handful of quality-of-life improvements and some performance work in the sidebar to keep things feeling smooth as your files grow. Let’s dive in."]
|
||||||
|
|
||||||
|
[:p {:class (stl/css :feature-content)}
|
||||||
|
"Let’s dive in!"]]
|
||||||
|
|
||||||
|
[:div {:class (stl/css :navigation)}
|
||||||
|
[:button {:class (stl/css :next-btn)
|
||||||
|
:on-click next} "Continue"]]]]]]
|
||||||
|
|
||||||
|
0
|
||||||
|
[:div {:class (stl/css-case :modal-overlay true)}
|
||||||
|
[:div.animated {:class klass}
|
||||||
|
[:div {:class (stl/css :modal-container)}
|
||||||
|
[:img {:src "images/features/2.14-tokens-fold.gif"
|
||||||
|
:class (stl/css :start-image)
|
||||||
|
:border "0"
|
||||||
|
:alt "Token groups: Navigating long names, finally"}]
|
||||||
|
|
||||||
|
[:div {:class (stl/css :modal-content)}
|
||||||
|
[:div {:class (stl/css :modal-header)}
|
||||||
|
[:h1 {:class (stl/css :modal-title)}
|
||||||
|
"Token groups: Navigating long names, finally"]]
|
||||||
|
|
||||||
|
[:div {:class (stl/css :feature)}
|
||||||
|
[:p {:class (stl/css :feature-content)}
|
||||||
|
"Token names are rarely short and sweet. Most of the time they carry a lot of meaning (type, state, property, variant… and more), which is great for consistency, but not so great for browsing. In 2.14 we’re introducing token groups, a new way to navigate dotted token paths as nested, collapsible sections."]
|
||||||
|
|
||||||
|
[:p {:class (stl/css :feature-content)}
|
||||||
|
"Token segments before the final name are displayed as groups, and only the last segment stays as a pill (so you keep the familiar token “chip” where it matters). If you unfold a path, it stays open while you move around the app (it resets only when the page reloads). And when you create a new token, Penpot automatically unfolds the path needed to reveal it (even if it overrides a previously opened one)."]
|
||||||
|
|
||||||
|
[:p {:class (stl/css :feature-content)}
|
||||||
|
"One extra detail: if you edit the path and change group segments, the token is moved to its new group (creating it if needed), and empty groups are automatically cleaned up."]]
|
||||||
|
|
||||||
|
[:div {:class (stl/css :navigation)}
|
||||||
|
[:& c/navigation-bullets
|
||||||
|
{:slide slide
|
||||||
|
:navigate navigate
|
||||||
|
:total 4}]
|
||||||
|
|
||||||
|
[:button {:on-click next
|
||||||
|
:class (stl/css :next-btn)} "Continue"]]]]]]
|
||||||
|
|
||||||
|
1
|
||||||
|
[:div {:class (stl/css-case :modal-overlay true)}
|
||||||
|
[:div.animated {:class klass}
|
||||||
|
[:div {:class (stl/css :modal-container)}
|
||||||
|
[:img {:src "images/features/2.14-api.gif"
|
||||||
|
:class (stl/css :start-image)
|
||||||
|
:border "0"
|
||||||
|
:alt "Design tokens in the plugins API: Automation unlocked"}]
|
||||||
|
|
||||||
|
[:div {:class (stl/css :modal-content)}
|
||||||
|
[:div {:class (stl/css :modal-header)}
|
||||||
|
[:h1 {:class (stl/css :modal-title)}
|
||||||
|
"Design tokens in the plugins API: Automation unlocked"]]
|
||||||
|
|
||||||
|
[:div {:class (stl/css :feature)}
|
||||||
|
[:p {:class (stl/css :feature-content)}
|
||||||
|
"Design tokens are now available in the Penpot plugins API. That means plugins (and external tools built around Penpot, like AI clients or Penpot MCP) can finally work with tokens programmatically and automate token workflows that used to be purely manual."]
|
||||||
|
|
||||||
|
[:p {:class (stl/css :feature-content)}
|
||||||
|
"If you’ve been waiting to generate tokens, sync them, or manipulate them from your own tools, this is the missing piece. And yes, this one has been requested a lot."]]
|
||||||
|
|
||||||
|
[:div {:class (stl/css :navigation)}
|
||||||
|
[:& c/navigation-bullets
|
||||||
|
{:slide slide
|
||||||
|
:navigate navigate
|
||||||
|
:total 4}]
|
||||||
|
|
||||||
|
[:button {:on-click next
|
||||||
|
:class (stl/css :next-btn)} "Continue"]]]]]]
|
||||||
|
|
||||||
|
2
|
||||||
|
[:div {:class (stl/css-case :modal-overlay true)}
|
||||||
|
[:div.animated {:class klass}
|
||||||
|
[:div {:class (stl/css :modal-container)}
|
||||||
|
[:img {:src "images/features/2.14-remap.jpg"
|
||||||
|
:class (stl/css :start-image)
|
||||||
|
:border "0"
|
||||||
|
:alt "Rename tokens without breaking everything"}]
|
||||||
|
|
||||||
|
[:div {:class (stl/css :modal-content)}
|
||||||
|
[:div {:class (stl/css :modal-header)}
|
||||||
|
[:h1 {:class (stl/css :modal-title)}
|
||||||
|
"Rename tokens without breaking everything"]]
|
||||||
|
|
||||||
|
[:div {:class (stl/css :feature)}
|
||||||
|
[:p {:class (stl/css :feature-content)}
|
||||||
|
"Renaming tokens sounds simple until you remember the references. One change can ripple through aliases, applied tokens, tooltips, math operations… and suddenly you’re left with a broken chain. In 2.14, renaming a token can optionally remap its references, keeping connections intact and updating the design with the new token name."]
|
||||||
|
|
||||||
|
[:p {:class (stl/css :feature-content)}
|
||||||
|
"Remapping is always optional, because sometimes you don’t want to keep the current connections. When enabled, it affects all tokens in the file and also takes libraries into account, so main components can propagate changes to child components, and applied tokens update on the elements using them."]]
|
||||||
|
|
||||||
|
[:div {:class (stl/css :navigation)}
|
||||||
|
[:& c/navigation-bullets
|
||||||
|
{:slide slide
|
||||||
|
:navigate navigate
|
||||||
|
:total 4}]
|
||||||
|
|
||||||
|
[:button {:on-click next
|
||||||
|
:class (stl/css :next-btn)} "Continue"]]]]]]
|
||||||
|
|
||||||
|
3
|
||||||
|
[:div {:class (stl/css-case :modal-overlay true)}
|
||||||
|
[:div.animated {:class klass}
|
||||||
|
[:div {:class (stl/css :modal-container)}
|
||||||
|
[:img {:src "images/features/2.14-icons.gif"
|
||||||
|
:class (stl/css :start-image)
|
||||||
|
:border "0"
|
||||||
|
:alt "Quality-of-life improvements"}]
|
||||||
|
|
||||||
|
[:div {:class (stl/css :modal-content)}
|
||||||
|
[:div {:class (stl/css :modal-header)}
|
||||||
|
[:h1 {:class (stl/css :modal-title)}
|
||||||
|
"Quality-of-life improvements"]]
|
||||||
|
[:div {:class (stl/css :feature)}
|
||||||
|
|
||||||
|
[:p {:class (stl/css :feature-content)}
|
||||||
|
"Lock and hide controls in the layer panel are getting a usability boost. The lock and visibility icons stay fixed in a right-aligned column regardless of indentation, and scrolling won’t make them awkward to click (even in deeply nested files)."]
|
||||||
|
|
||||||
|
[:p {:class (stl/css :feature-content)}
|
||||||
|
"We’re also improving sidebar performance, with a focus on keeping interactions fluent. The goal is to lazy-load the shape list on-demand and avoid UI stalls when clicking or hovering around the sidebar."]
|
||||||
|
|
||||||
|
[:p {:class (stl/css :feature-content)}
|
||||||
|
"And one more: you can now use Shift/Alt arrow key stepping in color picker inputs (a community contribution by @eureka928. ❤️)"]]
|
||||||
|
|
||||||
|
[:div {:class (stl/css :navigation)}
|
||||||
|
|
||||||
|
[:& c/navigation-bullets
|
||||||
|
{:slide slide
|
||||||
|
:navigate navigate
|
||||||
|
:total 4}]
|
||||||
|
|
||||||
|
[:button {:on-click finish
|
||||||
|
:class (stl/css :next-btn)} "Let's go"]]]]]])))
|
||||||
|
|
||||||
102
frontend/src/app/main/ui/releases/v2_14.scss
Normal file
102
frontend/src/app/main/ui/releases/v2_14.scss
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
//
|
||||||
|
// Copyright (c) KALEIDOS INC
|
||||||
|
|
||||||
|
@use "refactor/common-refactor.scss" as deprecated;
|
||||||
|
|
||||||
|
.modal-overlay {
|
||||||
|
@extend .modal-overlay-base;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-container {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: deprecated.$s-324 1fr;
|
||||||
|
height: deprecated.$s-500;
|
||||||
|
width: deprecated.$s-888;
|
||||||
|
border-radius: deprecated.$br-8;
|
||||||
|
background-color: var(--modal-background-color);
|
||||||
|
border: deprecated.$s-2 solid var(--modal-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.start-image {
|
||||||
|
width: deprecated.$s-324;
|
||||||
|
border-radius: deprecated.$br-8 0 0 deprecated.$br-8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
padding: deprecated.$s-40;
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: auto 1fr deprecated.$s-32;
|
||||||
|
gap: deprecated.$s-24;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--button-primary-background-color-rest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header {
|
||||||
|
display: grid;
|
||||||
|
gap: deprecated.$s-8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.version-tag {
|
||||||
|
@include deprecated.flexCenter;
|
||||||
|
@include deprecated.headlineSmallTypography;
|
||||||
|
height: deprecated.$s-32;
|
||||||
|
width: deprecated.$s-96;
|
||||||
|
background-color: var(--communication-tag-background-color);
|
||||||
|
color: var(--communication-tag-foreground-color);
|
||||||
|
border-radius: deprecated.$br-8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-title {
|
||||||
|
@include deprecated.headlineLargeTypography;
|
||||||
|
color: var(--modal-title-foreground-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.features-block {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: deprecated.$s-16;
|
||||||
|
width: deprecated.$s-440;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: deprecated.$s-8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-title {
|
||||||
|
@include deprecated.bodyLargeTypography;
|
||||||
|
color: var(--modal-title-foreground-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-content {
|
||||||
|
@include deprecated.bodyMediumTypography;
|
||||||
|
margin: 0;
|
||||||
|
color: var(--modal-text-foreground-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-list {
|
||||||
|
@include deprecated.bodyMediumTypography;
|
||||||
|
color: var(--modal-text-foreground-color);
|
||||||
|
list-style: disc;
|
||||||
|
display: grid;
|
||||||
|
gap: deprecated.$s-8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navigation {
|
||||||
|
width: 100%;
|
||||||
|
display: grid;
|
||||||
|
grid-template-areas: "bullets button";
|
||||||
|
}
|
||||||
|
|
||||||
|
.next-btn {
|
||||||
|
@extend .button-primary;
|
||||||
|
width: deprecated.$s-100;
|
||||||
|
justify-self: flex-end;
|
||||||
|
grid-area: button;
|
||||||
|
}
|
||||||
@ -143,8 +143,7 @@
|
|||||||
(let [token-ids (set tokens-in-path-ids)
|
(let [token-ids (set tokens-in-path-ids)
|
||||||
remaining-tokens (filter (fn [token]
|
remaining-tokens (filter (fn [token]
|
||||||
(not (contains? token-ids (:id token))))
|
(not (contains? token-ids (:id token))))
|
||||||
selected-token-set-tokens)
|
selected-token-set-tokens)]
|
||||||
_ (prn "Remaining tokens:" remaining-tokens)]
|
|
||||||
(seq remaining-tokens))))
|
(seq remaining-tokens))))
|
||||||
|
|
||||||
delete-token
|
delete-token
|
||||||
|
|||||||
@ -13,8 +13,10 @@
|
|||||||
[app.common.types.color :as cl]
|
[app.common.types.color :as cl]
|
||||||
[app.common.types.token :as cto]
|
[app.common.types.token :as cto]
|
||||||
[app.common.types.tokens-lib :as ctob]
|
[app.common.types.tokens-lib :as ctob]
|
||||||
|
[app.config :as cf]
|
||||||
[app.main.data.style-dictionary :as sd]
|
[app.main.data.style-dictionary :as sd]
|
||||||
[app.main.data.tinycolor :as tinycolor]
|
[app.main.data.tinycolor :as tinycolor]
|
||||||
|
[app.main.data.tokenscript :as ts]
|
||||||
[app.main.data.workspace.tokens.format :as dwtf]
|
[app.main.data.workspace.tokens.format :as dwtf]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.ui.ds.controls.input :as ds]
|
[app.main.ui.ds.controls.input :as ds]
|
||||||
@ -70,11 +72,15 @@
|
|||||||
(dissoc (:name prev-token))
|
(dissoc (:name prev-token))
|
||||||
(update (:name token) #(ctob/make-token (merge % prev-token token))))]
|
(update (:name token) #(ctob/make-token (merge % prev-token token))))]
|
||||||
|
|
||||||
(->> tokens
|
(->> (if (contains? cf/flags :tokenscript)
|
||||||
(sd/resolve-tokens-interactive)
|
(rx/of (ts/resolve-tokens tokens))
|
||||||
|
(sd/resolve-tokens-interactive tokens))
|
||||||
(rx/mapcat
|
(rx/mapcat
|
||||||
(fn [resolved-tokens]
|
(fn [resolved-tokens]
|
||||||
(let [{:keys [errors resolved-value] :as resolved-token} (get resolved-tokens (:name token))]
|
(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
|
(if resolved-value
|
||||||
(rx/of {:value resolved-value})
|
(rx/of {:value resolved-value})
|
||||||
(rx/of {:error (first errors)}))))))))
|
(rx/of {:error (first errors)}))))))))
|
||||||
|
|||||||
@ -10,7 +10,9 @@
|
|||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.types.token :as cto]
|
[app.common.types.token :as cto]
|
||||||
[app.common.types.tokens-lib :as ctob]
|
[app.common.types.tokens-lib :as ctob]
|
||||||
|
[app.config :as cf]
|
||||||
[app.main.data.style-dictionary :as sd]
|
[app.main.data.style-dictionary :as sd]
|
||||||
|
[app.main.data.tokenscript :as ts]
|
||||||
[app.main.fonts :as fonts]
|
[app.main.fonts :as fonts]
|
||||||
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||||
[app.main.ui.ds.controls.input :refer [input*]]
|
[app.main.ui.ds.controls.input :refer [input*]]
|
||||||
@ -49,28 +51,30 @@
|
|||||||
;; validate data within the form state.
|
;; validate data within the form state.
|
||||||
|
|
||||||
(defn- resolve-value
|
(defn- resolve-value
|
||||||
[tokens prev-token token-name value]
|
[tokens prev-token _token-name value]
|
||||||
(let [valid-token-name?
|
(let [tmp-value (cto/split-font-family value)
|
||||||
(and (string? token-name)
|
tmp-name "__PENPOT__FONT_FAMILY__PLACEHOLDER__"
|
||||||
(re-matches cto/token-name-validation-regex token-name))
|
|
||||||
|
|
||||||
|
;; Create a temporary font-family token to validate the value
|
||||||
token
|
token
|
||||||
{:value (cto/split-font-family value)
|
{:name tmp-name
|
||||||
:name (if (or (not valid-token-name?) (str/blank? token-name))
|
:type :font-family
|
||||||
"__PENPOT__TOKEN__NAME__PLACEHOLDER__"
|
:value (if (= (:type prev-token) :typography)
|
||||||
token-name)}
|
(assoc (:value prev-token) :font-family tmp-value)
|
||||||
|
tmp-value)}
|
||||||
|
|
||||||
tokens
|
tokens
|
||||||
(-> tokens
|
(update tokens (:name token) #(ctob/make-token (merge % prev-token token)))]
|
||||||
;; Remove previous token when renaming a token
|
|
||||||
(dissoc (:name prev-token))
|
|
||||||
(update (:name token) #(ctob/make-token (merge % prev-token token))))]
|
|
||||||
|
|
||||||
(->> tokens
|
(->> (if (contains? cf/flags :tokenscript)
|
||||||
(sd/resolve-tokens-interactive)
|
(rx/of (ts/resolve-tokens tokens))
|
||||||
|
(sd/resolve-tokens-interactive tokens))
|
||||||
(rx/mapcat
|
(rx/mapcat
|
||||||
(fn [resolved-tokens]
|
(fn [resolved-tokens]
|
||||||
(let [{:keys [errors resolved-value] :as resolved-token} (get resolved-tokens (:name token))]
|
(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
|
(if resolved-value
|
||||||
(rx/of {:value resolved-value})
|
(rx/of {:value resolved-value})
|
||||||
(rx/of {:error (first errors)}))))))))
|
(rx/of {:error (first errors)}))))))))
|
||||||
@ -176,7 +180,6 @@
|
|||||||
(let [message (tr "workspace.tokens.resolved-value" value)]
|
(let [message (tr "workspace.tokens.resolved-value" value)]
|
||||||
(swap! form update :extra-errors dissoc input-name)
|
(swap! form update :extra-errors dissoc input-name)
|
||||||
(reset! hint* {:message message :type "hint"})))))))]
|
(reset! hint* {:message message :type "hint"})))))))]
|
||||||
|
|
||||||
(fn []
|
(fn []
|
||||||
(rx/dispose! subs))))
|
(rx/dispose! subs))))
|
||||||
|
|
||||||
|
|||||||
@ -175,7 +175,10 @@
|
|||||||
(sd/resolve-tokens-interactive)
|
(sd/resolve-tokens-interactive)
|
||||||
(rx/mapcat
|
(rx/mapcat
|
||||||
(fn [resolved-tokens]
|
(fn [resolved-tokens]
|
||||||
(let [{:keys [errors resolved-value] :as resolved-token} (get resolved-tokens (:name token))]
|
(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
|
(if resolved-value
|
||||||
(rx/of {:value resolved-value})
|
(rx/of {:value resolved-value})
|
||||||
(rx/of {:error (first errors)}))))))))
|
(rx/of {:error (first errors)}))))))))
|
||||||
|
|||||||
@ -36,9 +36,9 @@
|
|||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
(defn get-value-for-validator
|
(defn get-value-for-validator
|
||||||
[active-tab value value-subfield form-type]
|
[active-tab value value-subfield value-type]
|
||||||
|
|
||||||
(case form-type
|
(case value-type
|
||||||
:indexed
|
:indexed
|
||||||
(if (= active-tab :reference)
|
(if (= active-tab :reference)
|
||||||
(:reference value)
|
(:reference value)
|
||||||
@ -62,7 +62,7 @@
|
|||||||
make-schema
|
make-schema
|
||||||
input-component
|
input-component
|
||||||
initial
|
initial
|
||||||
type
|
value-type
|
||||||
value-subfield
|
value-subfield
|
||||||
input-value-placeholder] :as props}]
|
input-value-placeholder] :as props}]
|
||||||
|
|
||||||
@ -178,13 +178,13 @@
|
|||||||
|
|
||||||
on-submit
|
on-submit
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(mf/deps validate-token token tokens token-type value-subfield type active-tab on-remap-token on-rename-token is-create)
|
(mf/deps validate-token token tokens token-type value-subfield value-type active-tab on-remap-token on-rename-token is-create)
|
||||||
(fn [form _event]
|
(fn [form _event]
|
||||||
(let [name (get-in @form [:clean-data :name])
|
(let [name (get-in @form [:clean-data :name])
|
||||||
path (str (d/name token-type) "." name)
|
path (str (d/name token-type) "." name)
|
||||||
description (get-in @form [:clean-data :description])
|
description (get-in @form [:clean-data :description])
|
||||||
value (get-in @form [:clean-data :value])
|
value (get-in @form [:clean-data :value])
|
||||||
value-for-validation (get-value-for-validator active-tab value value-subfield type)]
|
value-for-validation (get-value-for-validator active-tab value value-subfield value-type)]
|
||||||
(->> (validate-token {:token-value value-for-validation
|
(->> (validate-token {:token-value value-for-validation
|
||||||
:token-name name
|
:token-name name
|
||||||
:token-description description
|
:token-description description
|
||||||
@ -245,7 +245,7 @@
|
|||||||
:auto-focus true}]]
|
:auto-focus true}]]
|
||||||
|
|
||||||
[:div {:class (stl/css :input-row)}
|
[:div {:class (stl/css :input-row)}
|
||||||
(case type
|
(case value-type
|
||||||
:indexed
|
:indexed
|
||||||
[:> input-component
|
[:> input-component
|
||||||
{:token token
|
{:token token
|
||||||
|
|||||||
@ -365,7 +365,7 @@
|
|||||||
:token-type token-type
|
:token-type token-type
|
||||||
:initial initial
|
:initial initial
|
||||||
:make-schema make-schema
|
:make-schema make-schema
|
||||||
:type :indexed
|
:value-type :indexed
|
||||||
:value-subfield :shadow
|
:value-subfield :shadow
|
||||||
:input-component tabs-wrapper*
|
:input-component tabs-wrapper*
|
||||||
:validator validate-shadow-token})]
|
:validator validate-shadow-token})]
|
||||||
|
|||||||
@ -300,6 +300,6 @@
|
|||||||
:make-schema make-schema
|
:make-schema make-schema
|
||||||
:token token
|
:token token
|
||||||
:validator validate-typography-token
|
:validator validate-typography-token
|
||||||
:type :composite
|
:value-type :composite
|
||||||
:input-component tabs-wrapper*})]
|
:input-component tabs-wrapper*})]
|
||||||
[:> generic/form* props]))
|
[:> generic/form* props]))
|
||||||
|
|||||||
@ -4,7 +4,9 @@
|
|||||||
[app.common.schema :as sm]
|
[app.common.schema :as sm]
|
||||||
[app.common.types.token :as cto]
|
[app.common.types.token :as cto]
|
||||||
[app.common.types.tokens-lib :as ctob]
|
[app.common.types.tokens-lib :as ctob]
|
||||||
|
[app.config :as cf]
|
||||||
[app.main.data.style-dictionary :as sd]
|
[app.main.data.style-dictionary :as sd]
|
||||||
|
[app.main.data.tokenscript :as ts]
|
||||||
[app.main.data.workspace.tokens.errors :as wte]
|
[app.main.data.workspace.tokens.errors :as wte]
|
||||||
[beicon.v2.core :as rx]
|
[beicon.v2.core :as rx]
|
||||||
[cuerdas.core :as str]))
|
[cuerdas.core :as str]))
|
||||||
@ -36,14 +38,20 @@
|
|||||||
|
|
||||||
:always
|
:always
|
||||||
(update (:name token) #(ctob/make-token (merge % prev-token token))))]
|
(update (:name token) #(ctob/make-token (merge % prev-token token))))]
|
||||||
(->> tokens'
|
|
||||||
(sd/resolve-tokens-interactive)
|
(->> (if (contains? cf/flags :tokenscript)
|
||||||
|
(rx/of (ts/resolve-tokens tokens'))
|
||||||
|
(sd/resolve-tokens-interactive tokens'))
|
||||||
(rx/mapcat
|
(rx/mapcat
|
||||||
(fn [resolved-tokens]
|
(fn [resolved-tokens]
|
||||||
(let [{:keys [errors resolved-value] :as resolved-token} (get resolved-tokens (:name token))]
|
(let [resolved-token (cond-> (get resolved-tokens (:name token))
|
||||||
|
(contains? cf/flags :tokenscript)
|
||||||
|
(update :resolved-value ts/tokenscript-symbols->penpot-unit))]
|
||||||
(cond
|
(cond
|
||||||
resolved-value (rx/of resolved-token)
|
(:resolved-value resolved-token)
|
||||||
:else (rx/throw {:errors (or (seq errors)
|
(rx/of resolved-token)
|
||||||
|
|
||||||
|
:else (rx/throw {:errors (or (seq (:errors resolved-token))
|
||||||
[(wte/get-error-code :error/unknown-error)])}))))))))
|
[(wte/get-error-code :error/unknown-error)])}))))))))
|
||||||
|
|
||||||
(defn- validate-token-with [token validators]
|
(defn- validate-token-with [token validators]
|
||||||
|
|||||||
@ -12,6 +12,7 @@
|
|||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.types.tokens-lib :as ctob]
|
[app.common.types.tokens-lib :as ctob]
|
||||||
[app.main.data.modal :as modal]
|
[app.main.data.modal :as modal]
|
||||||
|
[app.main.data.notifications :as ntf]
|
||||||
[app.main.data.workspace.tokens.application :as dwta]
|
[app.main.data.workspace.tokens.application :as dwta]
|
||||||
[app.main.data.workspace.tokens.library-edit :as dwtl]
|
[app.main.data.workspace.tokens.library-edit :as dwtl]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
@ -136,13 +137,17 @@
|
|||||||
|
|
||||||
on-token-pill-click
|
on-token-pill-click
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(mf/deps not-editing? selected-ids)
|
(mf/deps not-editing? selected-ids tokens-lib)
|
||||||
(fn [event token]
|
(fn [event token]
|
||||||
(let [token (ctob/get-token tokens-lib selected-token-set-id (:id token))]
|
(let [token (ctob/get-token tokens-lib selected-token-set-id (:id token))]
|
||||||
(dom/stop-propagation event)
|
(dom/stop-propagation event)
|
||||||
(when (and not-editing? (seq selected-shapes) (not= (:type token) :number))
|
(if (and not-editing? (seq selected-shapes) (not= (:type token) :number))
|
||||||
(st/emit! (dwta/toggle-token {:token token
|
(st/emit! (dwta/toggle-token {:token token
|
||||||
:shape-ids selected-ids}))))))]
|
:shape-ids selected-ids}))
|
||||||
|
(st/emit! (ntf/show {:content (tr "workspace.tokens.error-text-edition")
|
||||||
|
:type :toast
|
||||||
|
:level :warning
|
||||||
|
:timeout 3000}))))))]
|
||||||
|
|
||||||
[:div {:class (stl/css :token-section-wrapper)
|
[:div {:class (stl/css :token-section-wrapper)
|
||||||
:data-testid (dm/str "section-" (name type))}
|
:data-testid (dm/str "section-" (name type))}
|
||||||
|
|||||||
@ -176,9 +176,10 @@
|
|||||||
(mf/defc token-pill*
|
(mf/defc token-pill*
|
||||||
{::mf/wrap [mf/memo]}
|
{::mf/wrap [mf/memo]}
|
||||||
[{:keys [on-click token on-context-menu selected-shapes is-selected-inside-layout active-theme-tokens]}]
|
[{:keys [on-click token on-context-menu selected-shapes is-selected-inside-layout active-theme-tokens]}]
|
||||||
(let [{:keys [name value errors type]} token
|
(let [{:keys [name value type]} token
|
||||||
|
|
||||||
resolved-token (get active-theme-tokens (:name token))
|
resolved-token (get active-theme-tokens (:name token))
|
||||||
|
errors (:errors resolved-token)
|
||||||
|
|
||||||
has-selected? (pos? (count selected-shapes))
|
has-selected? (pos? (count selected-shapes))
|
||||||
is-reference? (cfo/is-reference? token)
|
is-reference? (cfo/is-reference? token)
|
||||||
|
|||||||
@ -6,10 +6,30 @@
|
|||||||
|
|
||||||
(ns app.plugins.flags
|
(ns app.plugins.flags
|
||||||
(:require
|
(:require
|
||||||
[app.main.data.plugins :as dp]
|
[app.common.data.macros :as dm]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.plugins.utils :as u]
|
[app.plugins.utils :as u]
|
||||||
[app.util.object :as obj]))
|
[app.util.object :as obj]
|
||||||
|
[potok.v2.core :as ptk]))
|
||||||
|
|
||||||
|
(defn natural-child-ordering?
|
||||||
|
[plugin-id]
|
||||||
|
(boolean
|
||||||
|
(dm/get-in @st/state [:plugins :flags plugin-id :natural-child-ordering])))
|
||||||
|
|
||||||
|
(defn clear
|
||||||
|
[id]
|
||||||
|
(ptk/reify ::reset
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(update-in state [:plugins :flags] assoc id {}))))
|
||||||
|
|
||||||
|
(defn- set-flag
|
||||||
|
[id key value]
|
||||||
|
(ptk/reify ::set-flag
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(update-in state [:plugins :flags id] assoc key value))))
|
||||||
|
|
||||||
(defn flags-proxy
|
(defn flags-proxy
|
||||||
[plugin-id]
|
[plugin-id]
|
||||||
@ -17,11 +37,7 @@
|
|||||||
:naturalChildOrdering
|
:naturalChildOrdering
|
||||||
{:this false
|
{:this false
|
||||||
:get
|
:get
|
||||||
(fn []
|
(fn [] (natural-child-ordering? plugin-id))
|
||||||
(boolean
|
|
||||||
(get-in
|
|
||||||
@st/state
|
|
||||||
[:workspace-local :plugin-flags plugin-id :natural-child-ordering])))
|
|
||||||
|
|
||||||
:set
|
:set
|
||||||
(fn [value]
|
(fn [value]
|
||||||
@ -30,4 +46,4 @@
|
|||||||
(u/display-not-valid :naturalChildOrdering value)
|
(u/display-not-valid :naturalChildOrdering value)
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(st/emit! (dp/set-plugin-flag plugin-id :natural-child-ordering value))))}))
|
(st/emit! (set-flag plugin-id :natural-child-ordering value))))}))
|
||||||
|
|||||||
@ -10,12 +10,12 @@
|
|||||||
[app.common.schema :as sm]
|
[app.common.schema :as sm]
|
||||||
[app.common.types.shape.layout :as ctl]
|
[app.common.types.shape.layout :as ctl]
|
||||||
[app.main.data.workspace.shape-layout :as dwsl]
|
[app.main.data.workspace.shape-layout :as dwsl]
|
||||||
[app.main.data.workspace.transforms :as dwt]
|
[app.main.data.workspace.shapes :as dwsh]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
|
[app.plugins.flags :refer [natural-child-ordering?]]
|
||||||
[app.plugins.register :as r]
|
[app.plugins.register :as r]
|
||||||
[app.plugins.utils :as u]
|
[app.plugins.utils :as u]
|
||||||
[app.util.object :as obj]
|
[app.util.object :as obj]))
|
||||||
[potok.v2.core :as ptk]))
|
|
||||||
|
|
||||||
;; Define in `app.plugins.shape` we do this way to prevent circular dependency
|
;; Define in `app.plugins.shape` we do this way to prevent circular dependency
|
||||||
(def shape-proxy? nil)
|
(def shape-proxy? nil)
|
||||||
@ -259,10 +259,13 @@
|
|||||||
(u/display-not-valid :appendChild child)
|
(u/display-not-valid :appendChild child)
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(let [child-id (obj/get child "$id")]
|
(let [child-id (obj/get child "$id")
|
||||||
(st/emit! (dwt/move-shapes-to-frame #{child-id} id nil nil)
|
shape (u/locate-shape file-id page-id id)
|
||||||
(ptk/data-event :layout/update {:ids [id]})))))))
|
index
|
||||||
|
(if (and (natural-child-ordering? plugin-id) (not (ctl/reverse? shape)))
|
||||||
|
0
|
||||||
|
(count (:shapes shape)))]
|
||||||
|
(st/emit! (dwsh/relocate-shapes #{child-id} id index)))))))
|
||||||
|
|
||||||
(defn layout-child-proxy? [p]
|
(defn layout-child-proxy? [p]
|
||||||
(obj/type-of? p "LayoutChildProxy"))
|
(obj/type-of? p "LayoutChildProxy"))
|
||||||
|
|||||||
@ -47,13 +47,13 @@
|
|||||||
[app.main.data.workspace.variants :as dwv]
|
[app.main.data.workspace.variants :as dwv]
|
||||||
[app.main.repo :as rp]
|
[app.main.repo :as rp]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
|
[app.plugins.flags :refer [natural-child-ordering?]]
|
||||||
[app.plugins.flex :as flex]
|
[app.plugins.flex :as flex]
|
||||||
[app.plugins.format :as format]
|
[app.plugins.format :as format]
|
||||||
[app.plugins.grid :as grid]
|
[app.plugins.grid :as grid]
|
||||||
[app.plugins.parser :as parser]
|
[app.plugins.parser :as parser]
|
||||||
[app.plugins.register :as r]
|
[app.plugins.register :as r]
|
||||||
[app.plugins.ruler-guides :as rg]
|
[app.plugins.ruler-guides :as rg]
|
||||||
[app.plugins.state :refer [natural-child-ordering?]]
|
|
||||||
[app.plugins.text :as text]
|
[app.plugins.text :as text]
|
||||||
[app.plugins.utils :as u]
|
[app.plugins.utils :as u]
|
||||||
[app.util.http :as http]
|
[app.util.http :as http]
|
||||||
@ -960,9 +960,12 @@
|
|||||||
(u/display-not-valid :appendChild "Plugin doesn't have 'content:write' permission")
|
(u/display-not-valid :appendChild "Plugin doesn't have 'content:write' permission")
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(let [child-id (obj/get child "$id")
|
(let [child-id (obj/get child "$id")
|
||||||
is-reversed? (ctl/flex-layout? shape)
|
is-reversed? (ctl/flex-layout? shape)
|
||||||
index (if (and (natural-child-ordering? plugin-id) is-reversed?) 0 (count (:shapes shape)))]
|
index
|
||||||
|
(if (or (not (natural-child-ordering? plugin-id)) is-reversed?)
|
||||||
|
0
|
||||||
|
(count (:shapes shape)))]
|
||||||
(st/emit! (dwsh/relocate-shapes #{child-id} id index))))))
|
(st/emit! (dwsh/relocate-shapes #{child-id} id index))))))
|
||||||
|
|
||||||
:insertChild
|
:insertChild
|
||||||
@ -985,7 +988,7 @@
|
|||||||
(let [child-id (obj/get child "$id")
|
(let [child-id (obj/get child "$id")
|
||||||
is-reversed? (ctl/flex-layout? shape)
|
is-reversed? (ctl/flex-layout? shape)
|
||||||
index
|
index
|
||||||
(if (and (natural-child-ordering? plugin-id) is-reversed?)
|
(if (or (not (natural-child-ordering? plugin-id)) is-reversed?)
|
||||||
(- (count (:shapes shape)) index)
|
(- (count (:shapes shape)) index)
|
||||||
index)]
|
index)]
|
||||||
(st/emit! (dwsh/relocate-shapes #{child-id} id index))))))
|
(st/emit! (dwsh/relocate-shapes #{child-id} id index))))))
|
||||||
|
|||||||
@ -1,16 +0,0 @@
|
|||||||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
;;
|
|
||||||
;; Copyright (c) KALEIDOS INC
|
|
||||||
|
|
||||||
(ns app.plugins.state
|
|
||||||
(:require
|
|
||||||
[app.main.store :as st]))
|
|
||||||
|
|
||||||
(defn natural-child-ordering?
|
|
||||||
[plugin-id]
|
|
||||||
(boolean
|
|
||||||
(get-in
|
|
||||||
@st/state
|
|
||||||
[:workspace-local :plugin-flags plugin-id :natural-child-ordering])))
|
|
||||||
@ -8,18 +8,17 @@
|
|||||||
(:require
|
(:require
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.files.tokens :as cfo]
|
[app.common.files.tokens :as cfo]
|
||||||
|
[app.common.json :as json]
|
||||||
[app.common.schema :as sm]
|
[app.common.schema :as sm]
|
||||||
[app.common.types.token :as cto]
|
[app.common.types.token :as cto]
|
||||||
[app.common.types.tokens-lib :as ctob]
|
[app.common.types.tokens-lib :as ctob]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.main.data.style-dictionary :as sd]
|
|
||||||
[app.main.data.tokenscript :as ts]
|
[app.main.data.tokenscript :as ts]
|
||||||
[app.main.data.workspace.tokens.application :as dwta]
|
[app.main.data.workspace.tokens.application :as dwta]
|
||||||
[app.main.data.workspace.tokens.library-edit :as dwtl]
|
[app.main.data.workspace.tokens.library-edit :as dwtl]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.plugins.utils :as u]
|
[app.plugins.utils :as u]
|
||||||
[app.util.object :as obj]
|
[app.util.object :as obj]
|
||||||
[beicon.v2.core :as rx]
|
|
||||||
[clojure.datafy :refer [datafy]]))
|
[clojure.datafy :refer [datafy]]))
|
||||||
|
|
||||||
;; === Token
|
;; === Token
|
||||||
@ -87,7 +86,7 @@
|
|||||||
:get
|
:get
|
||||||
(fn [_]
|
(fn [_]
|
||||||
(let [token (u/locate-token file-id set-id id)]
|
(let [token (u/locate-token file-id set-id id)]
|
||||||
(:value token)))
|
(json/->js (:value token))))
|
||||||
:schema (let [token (u/locate-token file-id set-id id)]
|
:schema (let [token (u/locate-token file-id set-id id)]
|
||||||
(cfo/make-token-value-schema (:type token)))
|
(cfo/make-token-value-schema (:type token)))
|
||||||
:set
|
:set
|
||||||
@ -260,20 +259,19 @@
|
|||||||
:decode/options {:key-fn identity}
|
:decode/options {:key-fn identity}
|
||||||
:fn (fn [attrs]
|
:fn (fn [attrs]
|
||||||
(let [tokens-lib (u/locate-tokens-lib file-id)
|
(let [tokens-lib (u/locate-tokens-lib file-id)
|
||||||
tokens-tree (ctob/get-tokens-in-active-sets tokens-lib)
|
token (ctob/make-token attrs)
|
||||||
token (ctob/make-token attrs)]
|
tokens-tree (-> (ctob/get-tokens-in-active-sets tokens-lib)
|
||||||
(->> (assoc tokens-tree (:name token) token)
|
(assoc (:name token) token))
|
||||||
(sd/resolve-tokens-interactive)
|
resolved-tokens (ts/resolve-tokens tokens-tree)
|
||||||
(rx/subs!
|
|
||||||
(fn [resolved-tokens]
|
{:keys [errors resolved-value] :as resolved-token}
|
||||||
(let [{:keys [errors resolved-value] :as resolved-token} (get resolved-tokens (:name token))]
|
(get resolved-tokens (:name token))]
|
||||||
(if resolved-value
|
|
||||||
(st/emit! (dwtl/create-token id token))
|
(if resolved-value
|
||||||
(u/display-not-valid :addToken (str errors)))))))
|
(do (st/emit! (dwtl/create-token id token))
|
||||||
;; TODO: as the addToken function is synchronous, we must return the newly created
|
(token-proxy plugin-id file-id id (:id token)))
|
||||||
;; token even if the validator will throw it away if the resolution fails.
|
(do (u/display-not-valid :addToken (str errors))
|
||||||
;; This will be solved with the TokenScript resolver, that is syncronous.
|
nil))))}
|
||||||
(token-proxy plugin-id file-id id (:id token))))}
|
|
||||||
|
|
||||||
:duplicate
|
:duplicate
|
||||||
(fn []
|
(fn []
|
||||||
@ -354,7 +352,17 @@
|
|||||||
(st/emit! (dwtl/toggle-token-theme-active id)))
|
(st/emit! (dwtl/toggle-token-theme-active id)))
|
||||||
|
|
||||||
:activeSets
|
:activeSets
|
||||||
{:this true :get (fn [_])}
|
{:this true
|
||||||
|
:get (fn [_]
|
||||||
|
(let [tokens-lib (u/locate-tokens-lib file-id)
|
||||||
|
theme (u/locate-token-theme file-id id)]
|
||||||
|
(->> theme
|
||||||
|
:sets
|
||||||
|
(map #(->> %
|
||||||
|
(ctob/get-set-by-name tokens-lib)
|
||||||
|
(ctob/get-id)
|
||||||
|
(token-set-proxy plugin-id file-id)))
|
||||||
|
(apply array))))}
|
||||||
|
|
||||||
:addSet
|
:addSet
|
||||||
{:enumerable false
|
{:enumerable false
|
||||||
|
|||||||
@ -8366,6 +8366,10 @@ msgstr "Invalid value: Units are not allowed."
|
|||||||
msgid "workspace.tokens.warning-name-change"
|
msgid "workspace.tokens.warning-name-change"
|
||||||
msgstr "Renaming this token will break any reference to its old name"
|
msgstr "Renaming this token will break any reference to its old name"
|
||||||
|
|
||||||
|
#: src/app/main/data/workspace/tokens/application.cljs
|
||||||
|
msgid "workspace.tokens.error-text-edition"
|
||||||
|
msgstr "Tokens can't be applied while editing text. Select the text layer instead."
|
||||||
|
|
||||||
#: src/app/main/ui/workspace/sidebar.cljs:159, src/app/main/ui/workspace/sidebar.cljs:166
|
#: src/app/main/ui/workspace/sidebar.cljs:159, src/app/main/ui/workspace/sidebar.cljs:166
|
||||||
msgid "workspace.toolbar.assets"
|
msgid "workspace.toolbar.assets"
|
||||||
msgstr "Assets"
|
msgstr "Assets"
|
||||||
|
|||||||
@ -8246,6 +8246,10 @@ msgstr ""
|
|||||||
"Cambiar el nombre de este token romperá cualquier referencia a su nombre "
|
"Cambiar el nombre de este token romperá cualquier referencia a su nombre "
|
||||||
"anterior."
|
"anterior."
|
||||||
|
|
||||||
|
#: src/app/main/data/workspace/tokens/application.cljs
|
||||||
|
msgid "workspace.tokens.error-text-edition"
|
||||||
|
msgstr "No se pueden aplicar tokens mientras se edita texto. Seleccione la capa de texto en su lugar."
|
||||||
|
|
||||||
#: src/app/main/ui/workspace/sidebar.cljs:159, src/app/main/ui/workspace/sidebar.cljs:166
|
#: src/app/main/ui/workspace/sidebar.cljs:159, src/app/main/ui/workspace/sidebar.cljs:166
|
||||||
msgid "workspace.toolbar.assets"
|
msgid "workspace.toolbar.assets"
|
||||||
msgstr "Recursos"
|
msgstr "Recursos"
|
||||||
|
|||||||
@ -18,7 +18,12 @@
|
|||||||
<ul data-handler="themes-list">
|
<ul data-handler="themes-list">
|
||||||
@for (theme of themes; track theme.id) {
|
@for (theme of themes; track theme.id) {
|
||||||
<li class="body-m panel-item theme-item">
|
<li class="body-m panel-item theme-item">
|
||||||
<span>{{ theme.group }} / {{ theme.name }}</span>
|
<span title="{{ theme.activeSets }}">
|
||||||
|
@if (theme.group) {
|
||||||
|
{{ theme.group }} /
|
||||||
|
}
|
||||||
|
{{ theme.name }}
|
||||||
|
</span>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
data-appearance="secondary"
|
data-appearance="secondary"
|
||||||
@ -113,7 +118,11 @@
|
|||||||
class="body-m panel-item token-item"
|
class="body-m panel-item token-item"
|
||||||
(click)="applyToken(token.id)"
|
(click)="applyToken(token.id)"
|
||||||
>
|
>
|
||||||
<span title="{{ token.resolvedValueString }}">
|
<span
|
||||||
|
title="Value: {{ token.valueString }}
Resolved value: {{
|
||||||
|
token.resolvedValueString
|
||||||
|
}}"
|
||||||
|
>
|
||||||
{{ token.name }}
|
{{ token.name }}
|
||||||
</span>
|
</span>
|
||||||
<button
|
<button
|
||||||
|
|||||||
@ -10,6 +10,7 @@ type TokenTheme = {
|
|||||||
group: string;
|
group: string;
|
||||||
description: string;
|
description: string;
|
||||||
active: boolean;
|
active: boolean;
|
||||||
|
activeSets: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type TokenSet = {
|
type TokenSet = {
|
||||||
@ -23,6 +24,7 @@ type Token = {
|
|||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
|
valueString: string;
|
||||||
resolvedValueString: string;
|
resolvedValueString: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -61,11 +61,13 @@ function loadLibrary() {
|
|||||||
const themes = tokensCatalog.themes;
|
const themes = tokensCatalog.themes;
|
||||||
|
|
||||||
const themesData = themes.map((theme) => {
|
const themesData = themes.map((theme) => {
|
||||||
|
const activeSets = theme.activeSets.map((set) => set.name).join(', ');
|
||||||
return {
|
return {
|
||||||
id: theme.id,
|
id: theme.id,
|
||||||
group: theme.group,
|
group: theme.group,
|
||||||
name: theme.name,
|
name: theme.name,
|
||||||
active: theme.active,
|
active: theme.active,
|
||||||
|
activeSets: activeSets,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -109,6 +111,7 @@ function loadTokens(setId: string) {
|
|||||||
id: token.id,
|
id: token.id,
|
||||||
name: token.name,
|
name: token.name,
|
||||||
description: token.description,
|
description: token.description,
|
||||||
|
valueString: JSON.stringify(token.value),
|
||||||
resolvedValueString: token.resolvedValueString,
|
resolvedValueString: token.resolvedValueString,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user