mirror of
https://github.com/penpot/penpot.git
synced 2025-05-28 17:06:13 +02:00
♻️ Use rx streams for style dictionary interface
This commit is contained in:
parent
9f414b6ecd
commit
31f642ed25
6 changed files with 115 additions and 122 deletions
|
@ -202,20 +202,20 @@
|
|||
|
||||
(build-dictionary [_]
|
||||
(let [platform "json"
|
||||
config' (clj->js config)]
|
||||
(-> (sd. config')
|
||||
(.buildAllPlatforms platform)
|
||||
(p/then #(.getPlatformTokens ^js % platform))
|
||||
(p/then #(.-allTokens ^js %))))))
|
||||
config' (clj->js config)
|
||||
build+ (-> (sd. config')
|
||||
(.buildAllPlatforms platform)
|
||||
(p/then #(.getPlatformTokens ^js % platform))
|
||||
(p/then #(.-allTokens ^js %)))]
|
||||
(rx/from build+))))
|
||||
|
||||
(defn resolve-tokens-tree+
|
||||
(defn resolve-tokens-tree
|
||||
([tokens-tree get-token]
|
||||
(resolve-tokens-tree+ tokens-tree get-token (StyleDictionary. default-config)))
|
||||
(resolve-tokens-tree tokens-tree get-token (StyleDictionary. default-config)))
|
||||
([tokens-tree get-token style-dictionary]
|
||||
(let [sdict (-> style-dictionary
|
||||
(add-tokens tokens-tree)
|
||||
(build-dictionary))]
|
||||
(p/fmap #(process-sd-tokens % get-token) sdict))))
|
||||
(->> (add-tokens style-dictionary tokens-tree)
|
||||
(build-dictionary)
|
||||
(rx/map #(process-sd-tokens % get-token)))))
|
||||
|
||||
(defn sd-token-name [^js sd-token]
|
||||
(.. sd-token -original -name))
|
||||
|
@ -223,12 +223,12 @@
|
|||
(defn sd-token-uuid [^js sd-token]
|
||||
(uuid (.-uuid (.-id ^js sd-token))))
|
||||
|
||||
(defn resolve-tokens+
|
||||
(defn resolve-tokens
|
||||
[tokens]
|
||||
(let [tokens-tree (ctob/tokens-tree tokens)]
|
||||
(resolve-tokens-tree+ tokens-tree #(get tokens (sd-token-name %)))))
|
||||
(resolve-tokens-tree tokens-tree #(get tokens (sd-token-name %)))))
|
||||
|
||||
(defn resolve-tokens-interactive+
|
||||
(defn resolve-tokens-interactive
|
||||
"Interactive check of resolving tokens.
|
||||
Uses a ids map to backtrace the original token from the resolved StyleDictionary token.
|
||||
|
||||
|
@ -241,10 +241,10 @@
|
|||
this way after the resolving computation we can restore any token, even clashing ones with the same :name path by just looking up that :id in the ids map."
|
||||
[tokens]
|
||||
(let [{:keys [tokens-tree ids]} (ctob/backtrace-tokens-tree tokens)]
|
||||
(resolve-tokens-tree+ tokens-tree #(get ids (sd-token-uuid %)))))
|
||||
(resolve-tokens-tree tokens-tree #(get ids (sd-token-uuid %)))))
|
||||
|
||||
(defn resolve-tokens-with-errors+ [tokens]
|
||||
(resolve-tokens-tree+
|
||||
(defn resolve-tokens-with-errors [tokens]
|
||||
(resolve-tokens-tree
|
||||
(ctob/tokens-tree tokens)
|
||||
#(get tokens (sd-token-name %))
|
||||
(StyleDictionary. (assoc default-config :log {:verbosity "verbose"}))))
|
||||
|
@ -346,24 +346,23 @@
|
|||
(when unknown-tokens
|
||||
(st/emit! (tokens-of-unknown-type-warning unknown-tokens)))
|
||||
(try
|
||||
(-> (ctob/get-all-tokens tokens-lib)
|
||||
(resolve-tokens-with-errors+)
|
||||
(p/then (fn [_] tokens-lib))
|
||||
(p/catch (fn [sd-error]
|
||||
(let [reference-errors (reference-errors sd-error)]
|
||||
;; We allow reference errors for the users to resolve in the ui and throw on any other errors
|
||||
(if reference-errors
|
||||
(p/resolved tokens-lib)
|
||||
(throw (wte/error-ex-info :error.import/style-dictionary-unknown-error sd-error sd-error)))))))
|
||||
(->> (ctob/get-all-tokens tokens-lib)
|
||||
(resolve-tokens-with-errors)
|
||||
(rx/map (fn [_]
|
||||
tokens-lib))
|
||||
(rx/catch (fn [sd-error]
|
||||
(let [reference-errors (reference-errors sd-error)]
|
||||
;; We allow reference errors for the users to resolve in the ui and throw on any other errors
|
||||
(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
|
||||
(p/rejected (wte/error-ex-info :error.import/style-dictionary-unknown-error "" e))))))))))
|
||||
(throw (wte/error-ex-info :error.import/style-dictionary-unknown-error "" e))))))))))
|
||||
|
||||
;; === Hooks
|
||||
|
||||
(defonce !tokens-cache (atom nil))
|
||||
|
||||
(defonce !theme-tokens-cache (atom nil))
|
||||
|
||||
(defn use-resolved-tokens
|
||||
"The StyleDictionary process function is async, so we can't use resolved values directly.
|
||||
|
||||
|
@ -382,19 +381,17 @@
|
|||
(cond
|
||||
(nil? tokens) nil
|
||||
;; The tokens are already processing somewhere
|
||||
(p/promise? cached) (-> cached
|
||||
(p/then #(reset! tokens-state %))
|
||||
#_(p/catch js/console.error))
|
||||
(rx/observable? cached) (rx/sub! cached #(reset! tokens-state %))
|
||||
;; Get the cached entry
|
||||
(some? cached) (reset! tokens-state cached)
|
||||
;; No cached entry, start processing
|
||||
:else (let [promise+ (if interactive?
|
||||
(resolve-tokens-interactive+ tokens)
|
||||
(resolve-tokens+ tokens))]
|
||||
(swap! cache-atom assoc tokens promise+)
|
||||
(p/then promise+ (fn [resolved-tokens]
|
||||
(swap! cache-atom assoc tokens resolved-tokens)
|
||||
(reset! tokens-state resolved-tokens)))))))
|
||||
:else (let [resolved-tokens-s (if interactive?
|
||||
(resolve-tokens-interactive tokens)
|
||||
(resolve-tokens tokens))]
|
||||
(swap! cache-atom assoc tokens resolved-tokens-s)
|
||||
(rx/sub! resolved-tokens-s (fn [resolved-tokens]
|
||||
(swap! cache-atom assoc tokens resolved-tokens)
|
||||
(reset! tokens-state resolved-tokens)))))))
|
||||
@tokens-state))
|
||||
|
||||
(defn use-resolved-tokens*
|
||||
|
@ -408,12 +405,12 @@
|
|||
(mf/with-effect [tokens interactive?]
|
||||
(if (seq tokens)
|
||||
(let [tpoint (dt/tpoint-ms)
|
||||
promise (if interactive?
|
||||
(resolve-tokens-interactive+ tokens)
|
||||
(resolve-tokens+ tokens))]
|
||||
tokens-s (if interactive?
|
||||
(resolve-tokens-interactive tokens)
|
||||
(resolve-tokens tokens))]
|
||||
|
||||
(->> promise
|
||||
(p/fmap (fn [resolved-tokens]
|
||||
(-> tokens-s
|
||||
(rx/sub! (fn [resolved-tokens]
|
||||
(let [elapsed (tpoint)]
|
||||
(l/dbg :hint "use-resolved-tokens*" :elapsed elapsed)
|
||||
(reset! state* resolved-tokens))))))
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
(when-let [tokens (some-> (dsh/lookup-file-data state)
|
||||
(get :tokens-lib)
|
||||
(ctob/get-active-themes-set-tokens))]
|
||||
(->> (rx/from (sd/resolve-tokens+ tokens))
|
||||
(->> (sd/resolve-tokens tokens)
|
||||
(rx/mapcat
|
||||
(fn [resolved-tokens]
|
||||
(let [undo-id (js/Symbol)
|
||||
|
|
|
@ -185,12 +185,11 @@
|
|||
(watch [_ state _]
|
||||
(when-let [tokens-lib (-> (dsh/lookup-file-data state)
|
||||
(get :tokens-lib))]
|
||||
(let [tokens (-> (ctob/get-active-themes-set-tokens tokens-lib)
|
||||
(sd/resolve-tokens+))]
|
||||
(->> (rx/from tokens)
|
||||
(rx/mapcat (fn [sd-tokens]
|
||||
(let [undo-id (js/Symbol)]
|
||||
(rx/concat
|
||||
(rx/of (dwu/start-undo-transaction undo-id :timeout false))
|
||||
(propagate-tokens state sd-tokens)
|
||||
(rx/of (dwu/commit-undo-transaction undo-id))))))))))))
|
||||
(->> (ctob/get-active-themes-set-tokens tokens-lib)
|
||||
(sd/resolve-tokens)
|
||||
(rx/mapcat (fn [sd-tokens]
|
||||
(let [undo-id (js/Symbol)]
|
||||
(rx/concat
|
||||
(rx/of (dwu/start-undo-transaction undo-id :timeout false))
|
||||
(propagate-tokens state sd-tokens)
|
||||
(rx/of (dwu/commit-undo-transaction undo-id)))))))))))
|
||||
|
|
|
@ -36,10 +36,10 @@
|
|||
[app.util.functions :as uf]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[app.util.keyboard :as k]
|
||||
[beicon.v2.core :as rx]
|
||||
[cuerdas.core :as str]
|
||||
[malli.core :as m]
|
||||
[malli.error :as me]
|
||||
[promesa.core :as p]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
;; Schemas ---------------------------------------------------------------------
|
||||
|
@ -94,14 +94,9 @@
|
|||
(defn valid-value? [value]
|
||||
(seq (finalize-value value)))
|
||||
|
||||
(defn schema-validation->promise [validated]
|
||||
(if (:errors validated)
|
||||
(p/rejected validated)
|
||||
(p/resolved validated)))
|
||||
|
||||
;; Component -------------------------------------------------------------------
|
||||
|
||||
(defn validate-token-value+
|
||||
(defn validate-token-value
|
||||
"Validates token value by resolving the value `input` using `StyleDictionary`.
|
||||
Returns a promise of either resolved tokens or rejects with an error state."
|
||||
[{:keys [value name-value token tokens]}]
|
||||
|
@ -110,10 +105,10 @@
|
|||
token-name (if (str/empty? name-value) "__TOKEN_STUDIO_SYSTEM.TEMP" name-value)]
|
||||
(cond
|
||||
(empty? (str/trim value))
|
||||
(p/rejected {:errors [(wte/get-error-code :error.token/empty-input)]})
|
||||
(rx/throw {:errors [(wte/get-error-code :error.token/empty-input)]})
|
||||
|
||||
(ctob/token-value-self-reference? token-name value)
|
||||
(p/rejected {:errors [(wte/get-error-code :error.token/direct-self-reference)]})
|
||||
(rx/throw {:errors [(wte/get-error-code :error.token/direct-self-reference)]})
|
||||
|
||||
:else
|
||||
(let [tokens' (cond-> tokens
|
||||
|
@ -122,14 +117,14 @@
|
|||
:always (update token-name #(ctob/make-token (merge % {:value value
|
||||
:name token-name
|
||||
:type (:type token)}))))]
|
||||
(-> tokens'
|
||||
(sd/resolve-tokens-interactive+)
|
||||
(p/then
|
||||
(fn [resolved-tokens]
|
||||
(let [{:keys [errors resolved-value] :as resolved-token} (get resolved-tokens token-name)]
|
||||
(cond
|
||||
resolved-value (p/resolved resolved-token)
|
||||
:else (p/rejected {:errors (or errors (wte/get-error-code :error/unknown-error))}))))))))))
|
||||
(->> tokens'
|
||||
(sd/resolve-tokens-interactive)
|
||||
(rx/mapcat
|
||||
(fn [resolved-tokens]
|
||||
(let [{:keys [errors resolved-value] :as resolved-token} (get resolved-tokens token-name)]
|
||||
(cond
|
||||
resolved-value (rx/of resolved-token)
|
||||
:else (rx/throw {:errors (or errors (wte/get-error-code :error/unknown-error))}))))))))))
|
||||
|
||||
(defn use-debonced-resolve-callback
|
||||
"Resolves a token values using `StyleDictionary`.
|
||||
|
@ -148,14 +143,14 @@
|
|||
(js/setTimeout
|
||||
(fn []
|
||||
(when (not (timeout-outdated-cb?))
|
||||
(-> (validate-token-value+ {:value value
|
||||
(->> (validate-token-value {:value value
|
||||
:name-value @name-ref
|
||||
:token token
|
||||
:tokens tokens})
|
||||
(p/finally
|
||||
(fn [x err]
|
||||
(when-not (timeout-outdated-cb?)
|
||||
(callback (or err x))))))))
|
||||
(rx/filter #(not (timeout-outdated-cb?)))
|
||||
(rx/subs!
|
||||
callback
|
||||
callback))))
|
||||
timeout))))]
|
||||
debounced-resolver-callback))
|
||||
|
||||
|
@ -442,33 +437,36 @@
|
|||
;; and the user might have edited a valid form to make it invalid,
|
||||
;; and press enter before the next validations could return.
|
||||
(let [final-name (finalize-name @token-name-ref)
|
||||
valid-name?+ (-> (validate-name final-name) schema-validation->promise)
|
||||
valid-name? (try
|
||||
(not (:errors (validate-name final-name)))
|
||||
(catch js/Error _ nil))
|
||||
final-value (finalize-value (mf/ref-val value-ref))
|
||||
final-description @description-ref
|
||||
valid-description?+ (some-> final-description validate-descripion schema-validation->promise)]
|
||||
(-> (p/all [valid-name?+
|
||||
valid-description?+
|
||||
(validate-token-value+ {:value final-value
|
||||
:name-value final-name
|
||||
:token token
|
||||
:tokens active-theme-tokens})])
|
||||
(p/finally (fn [result err]
|
||||
;; The result should be a vector of all resolved validations
|
||||
;; We do not handle the error case as it will be handled by the components validations
|
||||
(when (and (seq result) (not err))
|
||||
(st/emit!
|
||||
(if (ctob/token? token)
|
||||
(dwtl/update-token (:name token)
|
||||
{:name final-name
|
||||
:value final-value
|
||||
:description final-description})
|
||||
valid-description? (if final-description
|
||||
(try
|
||||
(not (:errors (validate-descripion final-description)))
|
||||
(catch js/Error _ nil))
|
||||
true)]
|
||||
(when (and valid-name? valid-description?)
|
||||
(->> (validate-token-value {:value final-value
|
||||
:name-value final-name
|
||||
:token token
|
||||
:tokens active-theme-tokens})
|
||||
(rx/subs!
|
||||
(fn []
|
||||
(st/emit!
|
||||
(if (ctob/token? token)
|
||||
(dwtl/update-token (:name token)
|
||||
{:name final-name
|
||||
:value final-value
|
||||
:description final-description})
|
||||
|
||||
(dwtl/create-token {:name final-name
|
||||
:type token-type
|
||||
:value final-value
|
||||
:description final-description}))
|
||||
(dwtp/propagate-workspace-tokens)
|
||||
(modal/hide)))))))))
|
||||
(dwtl/create-token {:name final-name
|
||||
:type token-type
|
||||
:value final-value
|
||||
:description final-description}))
|
||||
(dwtp/propagate-workspace-tokens)
|
||||
(modal/hide)))))))))
|
||||
|
||||
on-delete-token
|
||||
(mf/use-fn
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue