0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-04-06 20:11:29 -05:00

Merge pull request #5784 from penpot/niwinz-tokens-changes-2

 Several performance oriented changes to tokens code (part 2)
This commit is contained in:
Andrey Antukh 2025-02-13 11:47:28 +01:00 committed by GitHub
commit 3ea52a0198
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 849 additions and 699 deletions

View file

@ -26,13 +26,15 @@
(def valid-groupable-item?
(sm/validator schema:groupable-item))
(def ^:private xf:clean-string
(comp (map str/trim)
(remove str/empty?)))
(defn split-path
"Decompose a string in the form 'one.two.three' into a vector of strings, removing spaces."
[path separator]
(let [xf (comp (map str/trim)
(remove str/empty?))]
(->> (str/split path separator)
(into [] xf))))
(->> (str/split path separator)
(into [] xf:clean-string)))
(defn join-path
"Regenerate a path as a string, from a vector."
@ -104,7 +106,10 @@
(defn get-token-path [path]
(get-path path token-separator))
(defn split-token-path [path]
;; FIXME: misleading name, we are spliting name into path, not
;; spliting path into path
(defn split-token-path
[path]
(split-path path token-separator))
(defrecord Token [name type value description modified-at])
@ -247,71 +252,35 @@
set-name (add-set-prefix (last paths))]
(conj set-path set-name)))
(defn split-token-set-path [token-set-path]
(split-path token-set-path set-separator))
(defn split-token-set-name [token-set-name]
(-> (split-token-set-path token-set-name)
(add-token-set-paths-prefix)))
(defn split-token-set-name
[name]
(split-path name set-separator))
(defn get-token-set-path [token-set]
(let [path (get-path token-set set-separator)]
(add-token-set-paths-prefix path)))
(defn set-name->set-path-string [set-name]
(-> (split-token-set-name set-name)
(join-set-path)))
(defn set-path->set-name [set-path]
(->> (split-token-set-path set-path)
(map (fn [path-part]
(or (-> (split-set-prefix path-part)
(second))
path-part)))
(join-set-path)))
(defn get-token-set-final-name [path]
(-> (split-token-set-path path)
(last)))
(defn get-token-set-final-name
[name]
(-> (split-token-set-name name)
(peek)))
(defn set-name->prefixed-full-path [name-str]
(-> (split-token-set-path name-str)
(-> (split-token-set-name name-str)
(set-full-path->set-prefixed-full-path)))
(defn get-token-set-prefixed-path [token-set]
(let [path (get-path token-set set-separator)]
(set-full-path->set-prefixed-full-path path)))
(defn get-prefixed-token-set-final-prefix [prefixed-path-str]
(some-> (get-token-set-final-name prefixed-path-str)
(split-set-str-path-prefix)
(first)))
(defn set-name-string->prefixed-set-path-string [name-str]
(-> (set-name->prefixed-full-path name-str)
(join-set-path)))
(defn prefixed-set-path-string->set-path [path-str]
(->> (split-token-set-path path-str)
(defn prefixed-set-path-string->set-name-string [path-str]
(->> (split-token-set-name path-str)
(map (fn [path-part]
(or (-> (split-set-str-path-prefix path-part)
(second))
path-part)))))
(defn prefixed-set-path-string->set-name-string [path-str]
(->> (prefixed-set-path-string->set-path path-str)
path-part)))
(join-set-path)))
(defn prefixed-set-path-final-group?
"Predicate if the given prefixed path string ends with a group."
[prefixed-path-str]
(= (get-prefixed-token-set-final-prefix prefixed-path-str) set-group-prefix))
(defn prefixed-set-path-final-set?
"Predicate if the given prefixed path string ends with a set."
[prefixed-path-str]
(= (get-prefixed-token-set-final-prefix prefixed-path-str) set-prefix))
(defn replace-last-path-name
"Replaces the last element in a `path` vector with `name`."
[path name]
@ -358,7 +327,7 @@
(defrecord TokenSet [name description modified-at tokens]
ITokenSet
(update-name [_ set-name]
(TokenSet. (-> (split-token-set-path name)
(TokenSet. (-> (split-token-set-name name)
(drop-last)
(concat [set-name])
(join-set-path))
@ -401,7 +370,8 @@
(vals tokens))
(get-set-prefixed-path-string [_]
(set-name-string->prefixed-set-path-string name))
(-> (set-name->prefixed-full-path name)
(join-set-path)))
(get-tokens-tree [_]
(tokens-tree tokens))
@ -721,6 +691,7 @@ used for managing active sets without a user created theme.")
(filter some?)
first))))
;; DEPRECATED
(defn walk-sets-tree-seq
"Walk sets tree as a flat list.
@ -749,7 +720,7 @@ used for managing active sets without a user created theme.")
;; Set
(and v (instance? TokenSet v))
[{:group? false
:path (split-token-set-path (:name v))
:path (split-token-set-name (:name v))
:parent-path parent
:depth depth
:set v}]
@ -769,6 +740,67 @@ used for managing active sets without a user created theme.")
(cons item (mapcat #(walk % (assoc opts :parent path :depth (inc depth))) v'))))))))))]
(walk (or nodes (d/ordered-map)) nil)))
(defn sets-tree-seq
"Get tokens sets tree as a flat list
Options:
`:skip-children-pred`: predicate to skip iterating over a set groups children by checking the path of the set group
`:new-editing-set-path`: append a an item with `:new?` at the given path"
[tree & {:keys [skip-children-pred new-at-path]
:or {skip-children-pred (constantly false)}}]
(let [walk (fn walk [[k v :as node] parent depth]
(lazy-seq
(cond
;; New set
(= :is-new k)
(let [tset (make-token-set :name (if (empty? parent)
""
(join-set-path parent)))]
[{:is-new true
:is-group false
:id ""
:parent-path parent
:token-set tset
:depth depth}])
;; Set
(and v (instance? TokenSet v))
(let [name (:name v)]
[{:is-group false
:path (split-token-set-name name)
:id name
:parent-path parent
:depth depth
:token-set v}])
;; Set group
(and v (d/ordered-map? v))
(let [unprefixed-path (last (split-set-str-path-prefix k))
path (conj parent unprefixed-path)
item {:is-group true
:path path
:id (join-set-path path)
:parent-path parent
:depth depth}]
(if (skip-children-pred path)
[item]
(let [v (cond-> v
(= path new-at-path)
(assoc :is-new true))]
(cons item
(mapcat #(walk % path (inc depth)) v))))))))
tree (cond-> tree
(= [] new-at-path)
(assoc :is-new true))]
(->> tree
(mapcat #(walk % [] 0))
(map-indexed (fn [index item]
(assoc item :index index))))))
(defn flatten-nested-tokens-json
"Recursively flatten the dtcg token structure, joining keys with '.'."
[tokens token-path]
@ -875,7 +907,7 @@ Will return a value that matches this schema:
this)))
(delete-set-path [_ prefixed-set-name]
(let [prefixed-set-path (split-token-set-path prefixed-set-name)
(let [prefixed-set-path (split-token-set-name prefixed-set-name)
set-node (get-in sets prefixed-set-path)
set-group? (not (instance? TokenSet set-node))
set-name-string (prefixed-set-path-string->set-name-string prefixed-set-name)]
@ -983,13 +1015,13 @@ Will return a value that matches this schema:
(->> (tree-seq d/ordered-map? vals sets)
(filter (partial instance? TokenSet))))
(get-path-sets [_ path]
(some->> (get-in sets (split-token-set-path path))
(get-path-sets [_ name]
(some->> (get-in sets (split-token-set-name name))
(tree-seq d/ordered-map? vals)
(filter (partial instance? TokenSet))))
(get-sets-at-prefix-path [_ prefixed-path-str]
(some->> (get-in sets (split-token-set-path prefixed-path-str))
(some->> (get-in sets (split-token-set-name prefixed-path-str))
(tree-seq d/ordered-map? vals)
(filter (partial instance? TokenSet))))

View file

@ -19,13 +19,25 @@
[app.main.data.notifications :as ntf]
[app.main.data.workspace.shapes :as dwsh]
[app.main.data.workspace.tokens.selected-set :as dwts]
[app.main.data.workspace.undo :as dwu]
[app.main.ui.workspace.tokens.update :as wtu]
[app.util.i18n :refer [tr]]
[beicon.v2.core :as rx]
[cuerdas.core :as str]
[potok.v2.core :as ptk]))
(declare set-selected-token-set-name)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TOKENS Getters
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; FIXME: lookup rename
(defn get-tokens-lib
[state]
(-> (dsh/lookup-file-data state)
(get :tokens-lib)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Helpers
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -43,15 +55,6 @@
(watch [_ _ _]
(rx/of (dwsh/update-shapes [id] #(merge % attrs))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TOKENS Getters
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn get-tokens-lib
[state]
(-> (dsh/lookup-file-data state)
(get :tokens-lib)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TOKENS Actions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -108,17 +111,21 @@
(dch/commit-changes changes)
(wtu/update-workspace-tokens))))))
(defn create-token-set [set-name token-set]
(let [new-token-set (-> token-set
(update :name #(if (empty? %) set-name (ctob/join-set-path [% set-name]))))]
(ptk/reify ::create-token-set
ptk/WatchEvent
(watch [it _ _]
(let [changes (-> (pcb/empty-changes it)
(pcb/add-token-set new-token-set))]
(rx/of
(dwts/set-selected-token-set-name (:name new-token-set))
(dch/commit-changes changes)))))))
(defn create-token-set
[set-name token-set]
(ptk/reify ::create-token-set
ptk/UpdateEvent
(update [_ state]
;; Clear possible local state
(update state :workspace-tokens dissoc :token-set-new-path))
ptk/WatchEvent
(watch [it _ _]
(let [token-set (update token-set :name #(if (empty? %) set-name (ctob/join-set-path [% set-name])))
changes (-> (pcb/empty-changes it)
(pcb/add-token-set token-set))]
(rx/of (set-selected-token-set-name (:name token-set))
(dch/commit-changes changes))))))
(defn rename-token-set-group [set-group-path set-group-fname]
(ptk/reify ::rename-token-set-group
@ -138,17 +145,21 @@
changes (-> (pcb/empty-changes it)
(pcb/update-token-set token-set prev-token-set))]
(rx/of
(dwts/set-selected-token-set-name (:name token-set))
(set-selected-token-set-name (:name token-set))
(dch/commit-changes changes))))))
(defn toggle-token-set [{:keys [token-set-name]}]
(defn toggle-token-set
[name]
(assert (string? name) "expected a string for `name`")
(ptk/reify ::toggle-token-set
ptk/WatchEvent
(watch [_ state _]
(let [changes (clt/generate-toggle-token-set (pcb/empty-changes) (get-tokens-lib state) token-set-name)]
(rx/of
(dch/commit-changes changes)
(wtu/update-workspace-tokens))))))
(let [tlib (get-tokens-lib state)
changes (-> (pcb/empty-changes)
(clt/generate-toggle-token-set tlib name))]
(rx/of (dch/commit-changes changes)
(wtu/update-workspace-tokens))))))
(defn toggle-token-set-group [group-path]
(ptk/reify ::toggle-token-set-group
@ -164,20 +175,14 @@
ptk/WatchEvent
(watch [it state _]
(let [data (dsh/lookup-file-data state)
update-token-set-change (some-> lib
(ctob/get-sets)
(first)
(:name)
(dwts/set-selected-token-set-name))
changes (-> (pcb/empty-changes it)
(pcb/with-library-data data)
(pcb/set-tokens-lib lib))]
(rx/of
(dch/commit-changes changes)
update-token-set-change
(wtu/update-workspace-tokens))))))
(rx/of (dch/commit-changes changes)
(wtu/update-workspace-tokens))))))
(defn delete-token-set-path [group? path]
(defn delete-token-set-path
[group? path]
(ptk/reify ::delete-token-set-path
ptk/WatchEvent
(watch [it state _]
@ -185,9 +190,8 @@
changes (-> (pcb/empty-changes it)
(pcb/with-library-data data)
(pcb/delete-token-set-path group? path))]
(rx/of
(dch/commit-changes changes)
(wtu/update-workspace-tokens))))))
(rx/of (dch/commit-changes changes)
(wtu/update-workspace-tokens))))))
(defn drop-error [{:keys [error to-path]}]
(ptk/reify ::drop-error
@ -221,53 +225,54 @@
(ptk/reify ::drop-token-set
ptk/WatchEvent
(watch [it state _]
(let [undo-id (js/Symbol)]
(try
(when-let [changes (clt/generate-move-token-set (pcb/empty-changes it) (get-tokens-lib state) drop-opts)]
(rx/of
(dwu/start-undo-transaction undo-id)
(dch/commit-changes changes)
(some-> (get-in changes [:redo-changes 0 :to-path])
(ctob/join-set-path)
(dwts/set-selected-token-set-name))
(wtu/update-workspace-tokens)
(dwu/commit-undo-transaction undo-id)))
(catch js/Error e
(rx/of
(drop-error (ex-data e)))))))))
(try
(when-let [changes (clt/generate-move-token-set (pcb/empty-changes it) (get-tokens-lib state) drop-opts)]
(rx/of (dch/commit-changes changes)
(wtu/update-workspace-tokens)))
(catch js/Error e
(rx/of
(drop-error (ex-data e))))))))
;; FIXME: the the name is very confusing
(defn update-create-token
[{:keys [token prev-token-name]}]
(ptk/reify ::update-create-token
ptk/WatchEvent
(watch [it state _]
(let [data (dsh/lookup-file-data state)
token-set (dwts/get-selected-token-set state)
set-name (or (:name token-set) "Global")
changes (if (not token-set)
;; No set created add a global set
(let [tokens-lib (get-tokens-lib state)
token-set (ctob/make-token-set :name set-name :tokens {(:name token) token})
hidden-theme (ctob/make-hidden-token-theme :sets [set-name])
active-theme-paths (some-> tokens-lib ctob/get-active-theme-paths)
add-to-hidden-theme? (= active-theme-paths #{ctob/hidden-token-theme-path})
base-changes (pcb/add-token-set (pcb/empty-changes) token-set)]
(cond
(not tokens-lib) (-> base-changes
(pcb/add-token-theme hidden-theme)
(pcb/update-active-token-themes #{ctob/hidden-token-theme-path} #{}))
(let [data (dsh/lookup-file-data state)
selected (dm/get-in state [:workspace-tokens :selected-token-set-name])
add-to-hidden-theme? (let [prev-hidden-theme (ctob/get-theme tokens-lib ctob/hidden-token-theme-group ctob/hidden-token-theme-name)]
(-> base-changes
(pcb/update-token-theme (ctob/toggle-set prev-hidden-theme ctob/hidden-token-theme-path) prev-hidden-theme)))
tokens-lib (get-tokens-lib state)
token-set (if selected
(some-> tokens-lib (ctob/get-set selected))
(some-> tokens-lib (ctob/get-sets) (first)))
:else base-changes))
(-> (pcb/empty-changes it)
(pcb/with-library-data data)
(pcb/set-token set-name (or prev-token-name (:name token)) token)))]
set-name (or (:name token-set) "Global")
changes (if (not token-set)
;; No set created add a global set
(let [token-set (ctob/make-token-set :name set-name :tokens {(:name token) token})
hidden-theme (ctob/make-hidden-token-theme :sets [set-name])
active-theme-paths (some-> tokens-lib ctob/get-active-theme-paths)
add-to-hidden-theme? (= active-theme-paths #{ctob/hidden-token-theme-path})
base-changes (pcb/add-token-set (pcb/empty-changes) token-set)]
(cond
(not tokens-lib)
(-> base-changes
(pcb/add-token-theme hidden-theme)
(pcb/update-active-token-themes #{ctob/hidden-token-theme-path} #{}))
add-to-hidden-theme?
(let [prev-hidden-theme (ctob/get-theme tokens-lib ctob/hidden-token-theme-group ctob/hidden-token-theme-name)]
(-> base-changes
(pcb/update-token-theme (ctob/toggle-set prev-hidden-theme ctob/hidden-token-theme-path) prev-hidden-theme)))
:else base-changes))
(-> (pcb/empty-changes it)
(pcb/with-library-data data)
(pcb/set-token set-name (or prev-token-name (:name token)) token)))]
(rx/of
(dwts/set-selected-token-set-name set-name)
(set-selected-token-set-name set-name)
(when-not prev-token-name
(ptk/event ::ev/event {::ev/name "create-tokens"}))
(dch/commit-changes changes))))))
@ -307,41 +312,81 @@
(update-create-token
{:token (assoc token :name copy-name)})))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TOKEN UI OPS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn set-token-type-section-open
[token-type open?]
(ptk/reify ::set-token-type-section-open
ptk/UpdateEvent
(update [_ state]
(assoc-in state [:workspace-local :token-type-open-status token-type] open?))))
(update-in state [:workspace-tokens :open-status-by-type] assoc token-type open?))))
;; === Token Context Menu
(defn assign-token-context-menu
[{:keys [position] :as params}]
(when params
(assert (gpt/point? position) "expected a point instance for `position` param"))
(defn show-token-context-menu
[{:keys [position _token-name] :as params}]
(dm/assert! (gpt/point? position))
(ptk/reify ::show-token-context-menu
ptk/UpdateEvent
(update [_ state]
(assoc-in state [:workspace-local :token-context-menu] params))))
(if params
(update state :workspace-tokens assoc :token-context-menu params)
(update state :workspace-tokens dissoc :token-context-menu)))))
(def hide-token-context-menu
(ptk/reify ::hide-token-context-menu
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TOKEN-SET UI OPS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn assign-token-set-context-menu
[{:keys [position] :as params}]
(when params
(assert (gpt/point? position) "expected valid point for `position` param"))
(ptk/reify ::assign-token-set-context-menu
ptk/UpdateEvent
(update [_ state]
(assoc-in state [:workspace-local :token-context-menu] nil))))
(if params
(update state :workspace-tokens assoc :token-set-context-menu params)
(update state :workspace-tokens dissoc :token-set-context-menu)))))
;; === Token Set Context Menu
(defn show-token-set-context-menu
[{:keys [position _token-set-name] :as params}]
(dm/assert! (gpt/point? position))
(ptk/reify ::show-token-set-context-menu
(defn set-selected-token-set-name
[name]
(ptk/reify ::set-selected-token-set-name
ptk/UpdateEvent
(update [_ state]
(assoc-in state [:workspace-local :token-set-context-menu] params))))
(update state :workspace-tokens assoc :selected-token-set-name name))))
(def hide-token-set-context-menu
(ptk/reify ::hide-token-set-context-menu
(defn start-token-set-edition
[edition-id]
(assert (string? edition-id) "expected a string for `edition-id`")
(ptk/reify ::start-token-set-edition
ptk/UpdateEvent
(update [_ state]
(assoc-in state [:workspace-local :token-set-context-menu] nil))))
(update state :workspace-tokens assoc :token-set-edition-id edition-id))))
(defn start-token-set-creation
[path]
(assert (vector? path) "expected a vector for `path`")
(ptk/reify ::start-token-set-creation
ptk/UpdateEvent
(update [_ state]
(update state :workspace-tokens assoc :token-set-new-path path))))
(defn clear-token-set-edition
[]
(ptk/reify ::clear-token-set-edition
ptk/UpdateEvent
(update [_ state]
(update state :workspace-tokens dissoc :token-set-edition-id))))
(defn clear-token-set-creation
[]
(ptk/reify ::clear-token-set-creation
ptk/UpdateEvent
(update [_ state]
(update state :workspace-tokens dissoc :token-set-new-path))))

View file

@ -399,6 +399,7 @@
:workspace-media-objects
:workspace-persistence
:workspace-presence
:workspace-tokens
:workspace-ready
:workspace-undo)
(update :workspace-global dissoc :read-only?)

View file

@ -9,11 +9,10 @@
Will default to the first set."
(:require
[app.common.types.tokens-lib :as ctob]
[app.main.data.helpers :as dsh]
[potok.v2.core :as ptk]))
[app.main.data.helpers :as dsh]))
(defn get-selected-token-set-name [state]
(or (get-in state [:workspace-local :selected-token-set-name])
(or (get-in state [:workspace-tokens :selected-token-set-name])
(some-> (dsh/lookup-file-data state)
(get :tokens-lib)
(ctob/get-sets)
@ -33,10 +32,3 @@
(defn get-selected-token-set-tokens [state]
(some-> (get-selected-token-set state)
:tokens))
(defn set-selected-token-set-name
[name]
(ptk/reify ::set-selected-token-set-path-from-name
ptk/UpdateEvent
(update [_ state]
(update state :workspace-local assoc :selected-token-set-name name))))

View file

@ -128,6 +128,10 @@
(def workspace-selrect-transform
(l/derived :workspace-selrect-transform st/state))
(def workspace-tokens
"All tokens related ephimeral state"
(l/derived :workspace-tokens st/state))
;; TODO: rename to workspace-selected (?)
;; Don't use directly from components, this is a proxy to improve performance of selected-shapes
(def ^:private selected-shapes-data
@ -453,8 +457,8 @@
(def workspace-token-themes-no-hidden
(l/derived #(remove ctob/hidden-temporary-theme? %) workspace-token-themes))
(def workspace-selected-token-set-name
(l/derived dwts/get-selected-token-set-name st/state))
(def selected-token-set-name
(l/derived (l/key :selected-token-set-name) workspace-tokens))
(def workspace-ordered-token-sets
(l/derived #(or (some-> % ctob/get-sets) []) tokens-lib))
@ -476,10 +480,7 @@
(def workspace-active-theme-paths-no-hidden
(l/derived #(disj % ctob/hidden-token-theme-path) workspace-active-theme-paths))
(def workspace-active-set-names
(l/derived (d/nilf ctob/get-active-themes-set-names) tokens-lib))
;; FIXME: deprecated, it should not be implemented with ref
;; FIXME: deprecated, it should not be implemented with ref (still used in form)
(def workspace-active-theme-sets-tokens
(l/derived #(or (some-> % ctob/get-active-themes-set-tokens) {}) tokens-lib))
@ -492,7 +493,6 @@
(def workspace-selected-token-set-tokens
(l/derived #(or (dwts/get-selected-token-set-tokens %) {}) st/state))
(def plugins-permissions-peek
(l/derived (fn [state]
(dm/get-in state [:plugins-permissions-peek :data]))

View file

@ -133,13 +133,14 @@
(fn []
(st/emit! (dtm/finalize-team team-id))))
(let [team (mf/deref refs/team)]
(let [{:keys [permissions] :as team} (mf/deref refs/team)]
(when (= team-id (:id team))
[:& (mf/provider ctx/current-team-id) {:value team-id}
[:& (mf/provider ctx/permissions) {:value (:permissions team)}
;; The `:key` is mandatory here because we want to reinitialize
;; all dom tree instead of simple rerender.
[:* {:key (str team-id)} children]]])))
[:> (mf/provider ctx/current-team-id) {:value team-id}
[:> (mf/provider ctx/permissions) {:value permissions}
[:> (mf/provider ctx/can-edit?) {:value (:can-edit permissions)}
;; The `:key` is mandatory here because we want to reinitialize
;; all dom tree instead of simple rerender.
[:* {:key (str team-id)} children]]]])))
(mf/defc page*
{::mf/props :obj

View file

@ -34,3 +34,4 @@
(def sidebar (mf/create-context nil))
(def permissions (mf/create-context nil))
(def can-edit? (mf/create-context nil))

View file

@ -38,7 +38,7 @@
Optionally remove attributes from `attributes-to-remove`,
this is useful for applying a single attribute from an attributes set
while removing other applied tokens from this set."
[{:keys [attributes attributes-to-remove token shape-ids on-update-shape] :as _props}]
[{:keys [attributes attributes-to-remove token shape-ids on-update-shape]}]
(ptk/reify ::apply-token
ptk/WatchEvent
(watch [_ state _]

View file

@ -240,15 +240,15 @@
:no-selectable true
:action (fn [event]
(let [{:keys [key fields]} modal]
(st/emit! dt/hide-token-context-menu)
(dom/stop-propagation event)
(modal/show! key {:x (.-clientX ^js event)
:y (.-clientY ^js event)
:position :right
:fields fields
:action "edit"
:selected-token-set-name selected-token-set-name
:token token})))}
(st/emit! (dt/assign-token-context-menu nil)
(modal/show key {:x (.-clientX ^js event)
:y (.-clientY ^js event)
:position :right
:fields fields
:action "edit"
:selected-token-set-name selected-token-set-name
:token token}))))}
{:title (tr "workspace.token.duplicate")
:no-selectable true
:action #(st/emit! (dt/duplicate-token (:name token)))}
@ -274,8 +274,8 @@
;; Components ------------------------------------------------------------------
(def tokens-menu-ref
(l/derived :token-context-menu refs/workspace-local))
(def ^:private tokens-menu-ref
(l/derived :token-context-menu refs/workspace-tokens))
(defn- prevent-default
[event]
@ -377,7 +377,7 @@
selected-shapes (into [] (keep (d/getf objects)) selected)
token-name (:token-name mdata)
token (mf/deref (refs/workspace-selected-token-set-token token-name))
selected-token-set-name (mf/deref refs/workspace-selected-token-set-name)]
selected-token-set-name (mf/deref refs/selected-token-set-name)]
[:ul {:class (stl/css :context-list)}
[:& menu-tree {:submenu-offset width
:submenu-direction direction
@ -416,8 +416,9 @@
(reset! dropdown-direction* (if is-outside? "up" "down"))
(mf/set-ref-val! dropdown-direction-change* (inc (mf/ref-val dropdown-direction-change*)))))))
;; FIXME: perf optimization
[:& dropdown {:show is-open?
:on-close #(st/emit! dt/hide-token-context-menu)}
:on-close #(st/emit! (dt/assign-token-context-menu nil))}
[:div {:class (stl/css :token-context-menu)
:data-testid "tokens-context-menu-for-token"
:ref dropdown-ref

View file

@ -25,7 +25,6 @@
[app.main.ui.icons :as i]
[app.main.ui.workspace.tokens.components.controls.input-tokens :refer [input-tokens*]]
[app.main.ui.workspace.tokens.sets :as wts]
[app.main.ui.workspace.tokens.sets-context :as sets-context]
[app.util.dom :as dom]
[app.util.i18n :refer [tr]]
[app.util.keyboard :as k]
@ -339,8 +338,8 @@
token-set-active?
(mf/use-fn
(mf/deps theme-state)
(fn [set-name]
(get-in theme-state [:sets set-name])))
(fn [name]
(contains? (:sets theme-state) name)))
on-toggle-token-set
(mf/use-fn
@ -381,16 +380,15 @@
(tr "workspace.token.set-selection-theme")]
[:div {:class (stl/css :sets-list-wrapper)}
[:& wts/controlled-sets-list
[:> wts/controlled-sets-list*
{:token-sets token-sets
:token-set-selected? (constantly false)
:token-set-active? token-set-active?
:token-set-group-active? token-set-group-active?
:is-token-set-active token-set-active?
:is-token-set-group-active token-set-group-active?
:on-select on-click-token-set
:can-edit false
:on-toggle-token-set on-toggle-token-set
:on-toggle-token-set-group on-toggle-token-set-group
:origin "theme-modal"
:context sets-context/static-context}]]
:origin "theme-modal"}]]
[:div {:class (stl/css :edit-theme-footer)}
[:> button* {:variant "secondary"
@ -432,5 +430,4 @@
:aria-label (tr "labels.close")
:variant "action"
:icon "close"}]
[:& sets-context/provider {}
[:& themes-modal-body]]]]))
[:& themes-modal-body]]]))

View file

@ -7,19 +7,17 @@
(ns app.main.ui.workspace.tokens.sets
(:require-macros [app.main.style :as stl])
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.types.tokens-lib :as ctob]
[app.main.data.event :as ev]
[app.main.data.tokens :as wdt]
[app.main.data.workspace.tokens.selected-set :as dwts]
[app.main.data.tokens :as dt]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.context :as ctx]
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
[app.main.ui.ds.foundations.assets.icon :refer [icon*] :as ic]
[app.main.ui.ds.foundations.typography.text :refer [text*]]
[app.main.ui.hooks :as h]
[app.main.ui.workspace.tokens.sets-context :as sets-context]
[app.util.dom :as dom]
[app.util.i18n :refer [tr]]
[app.util.keyboard :as kbd]
@ -27,42 +25,62 @@
[potok.v2.core :as ptk]
[rumext.v2 :as mf]))
(defn on-toggle-token-set-click [token-set-name]
(st/emit! (wdt/toggle-token-set {:token-set-name token-set-name})))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; HELPERS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn on-toggle-token-set-group-click [group-path]
(st/emit! (wdt/toggle-token-set-group group-path)))
(defn- on-start-creation
[]
(st/emit! (dt/start-token-set-creation [])))
(defn on-select-token-set-click [set-name]
(st/emit! (dwts/set-selected-token-set-name set-name)))
(defn- on-toggle-token-set-click [name]
(st/emit! (dt/toggle-token-set name)))
(defn on-update-token-set [set-name token-set]
(st/emit! (wdt/update-token-set (:name token-set) (ctob/update-name token-set set-name))))
(defn- on-toggle-token-set-group-click [path]
(st/emit! (dt/toggle-token-set-group path)))
(defn on-update-token-set-group [set-group-path set-group-fname]
(st/emit! (wdt/rename-token-set-group set-group-path set-group-fname)))
(defn- on-select-token-set-click [name]
(st/emit! (dt/set-selected-token-set-name name)))
(defn on-create-token-set [set-name token-set]
(st/emit! (ptk/event ::ev/event {::ev/name "create-tokens-set"}))
(st/emit! (wdt/create-token-set set-name token-set)))
(defn on-update-token-set
[name token-set]
(st/emit! (dt/clear-token-set-edition)
(dt/update-token-set (:name token-set) (ctob/update-name token-set name))))
(mf/defc editing-label
(defn- on-update-token-set-group [path name]
(st/emit! (dt/clear-token-set-edition)
(dt/rename-token-set-group path name)))
(defn- on-create-token-set [name token-set]
(st/emit! (ptk/data-event ::ev/event {::ev/name "create-token-set" :name name})
(dt/create-token-set name token-set)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; COMPONENTS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(mf/defc editing-label*
{::mf/private true}
[{:keys [default-value on-cancel on-submit]}]
(let [ref (mf/use-ref)
on-submit-valid (mf/use-fn
(fn [event]
(let [value (str/trim (dom/get-target-val event))]
(if (or (str/empty? value)
(= value default-value))
(on-cancel)
(do
(on-submit value)
(on-cancel))))))
on-key-down (mf/use-fn
(fn [event]
(cond
(kbd/enter? event) (on-submit-valid event)
(kbd/esc? event) (on-cancel))))]
on-submit-valid
(mf/use-fn
(mf/deps on-cancel on-submit default-value)
(fn [event]
(let [value (str/trim (dom/get-target-val event))]
(if (or (str/empty? value)
(= value default-value))
(on-cancel)
(on-submit value)))))
on-key-down
(mf/use-fn
(mf/deps on-submit-valid on-cancel)
(fn [event]
(cond
(kbd/enter? event) (on-submit-valid event)
(kbd/esc? event) (on-cancel))))]
[:input
{:class (stl/css :editing-node)
:type "text"
@ -72,17 +90,12 @@
:auto-focus true
:default-value default-value}]))
(mf/defc checkbox
(mf/defc checkbox*
[{:keys [checked aria-label on-click disabled]}]
(let [all? (true? checked)
mixed? (= checked "mixed")
checked? (or all? mixed?)
on-click
(mf/use-fn
(mf/deps disabled)
(fn [e]
(when-not disabled
(on-click e))))]
(let [all? (true? checked)
mixed? (= checked "mixed")
checked? (or all? mixed?)]
[:div {:role "checkbox"
:aria-checked (dm/str checked)
:disabled disabled
@ -91,37 +104,59 @@
:class (stl/css-case :checkbox-style true
:checkbox-checked-style checked?
:checkbox-disabled-checked (and checked? disabled)
:checkbox-disabled disabled)
:on-click on-click}
:checkbox-disabled disabled)
:on-click (when-not disabled on-click)}
(when checked?
(when ^boolean checked?
[:> icon*
{:aria-label aria-label
:class (stl/css :check-icon)
:size "s"
:icon-id (if mixed? ic/remove ic/tick)}])]))
(mf/defc sets-tree-set-group
[{:keys [label tree-depth tree-path active? selected? draggable? on-toggle-collapse on-toggle editing-id editing? on-edit on-edit-reset on-edit-submit collapsed-paths tree-index]}]
(let [active?' (active? tree-path)
editing?' (editing? editing-id)
collapsed? (some? (get @collapsed-paths tree-path))
can-edit? (:can-edit (deref refs/permissions))
;; Used by playwright to get the correct item by label
label-id (str editing-id "-label")
(mf/defc inline-add-button*
[]
(let [can-edit? (mf/use-ctx ctx/can-edit?)]
(if can-edit?
[:div {:class (stl/css :empty-sets-wrapper)}
[:> text* {:as "span" :typography "body-small" :class (stl/css :empty-state-message)}
(tr "workspace.token.no-sets-yet")]
[:button {:on-click on-start-creation
:class (stl/css :create-set-button)}
(tr "workspace.token.create-one")]]
[:div {:class (stl/css :empty-sets-wrapper)}
[:> text* {:as "span" :typography "body-small" :class (stl/css :empty-state-message)}
(tr "workspace.token.no-sets-yet")]])))
(mf/defc add-button*
[]
[:> icon-button* {:variant "ghost"
:icon "add"
:on-click on-start-creation
:aria-label (tr "workspace.token.add set")}])
(mf/defc sets-tree-set-group*
{::mf/private true}
[{:keys [id label tree-depth tree-path is-active is-selected is-draggable is-collapsed tree-index on-drop
on-toggle-collapse on-toggle is-editing on-start-edition on-reset-edition on-edit-submit]}]
(let [can-edit?
(mf/use-ctx ctx/can-edit?)
label-id
(str id "-label")
on-context-menu
(mf/use-fn
(mf/deps editing?' editing-id can-edit?)
(mf/deps is-editing can-edit?)
(fn [event]
(dom/prevent-default event)
(dom/stop-propagation event)
(when-not editing?'
(st/emit!
(wdt/show-token-set-context-menu
{:position (dom/get-client-position event)
:group? true
:path tree-path})))))
(when-not is-editing
(st/emit! (dt/assign-token-set-context-menu
{:position (dom/get-client-position event)
:is-group true
:path tree-path})))))
on-collapse-click
(mf/use-fn
@ -130,9 +165,7 @@
(on-toggle-collapse tree-path)))
on-double-click
(mf/use-fn
(mf/deps editing-id can-edit?)
#(on-edit editing-id))
(mf/use-fn (mf/deps id) #(on-start-edition id))
on-checkbox-click
(mf/use-fn
@ -146,24 +179,18 @@
on-drop
(mf/use-fn
(mf/deps tree-index collapsed-paths)
(mf/deps tree-index on-drop)
(fn [position data]
(let [props {:from-index (:index data)
:to-index tree-index
:position position
:collapsed-paths @collapsed-paths}]
(if (:group? data)
(st/emit! (wdt/drop-token-set-group props))
(st/emit! (wdt/drop-token-set props))))))
(on-drop tree-index position data)))
[dprops dref]
(h/use-sortable
:data-type "penpot/token-set"
:on-drop on-drop
:data {:index tree-index
:group? true}
:is-group true}
:detect-center? true
:draggable? draggable?)]
:draggable? is-draggable)]
[:div {:ref dref
:role "button"
@ -172,7 +199,7 @@
:style {"--tree-depth" tree-depth}
:class (stl/css-case :set-item-container true
:set-item-group true
:selected-set selected?
:selected-set is-selected
:dnd-over (= (:over dprops) :center)
:dnd-over-top (= (:over dprops) :top)
:dnd-over-bot (= (:over dprops) :bot))
@ -181,68 +208,64 @@
{:class (stl/css :set-item-group-collapse-button)
:on-click on-collapse-click
:aria-label (tr "labels.collapse")
:icon (if collapsed? "arrow-right" "arrow-down")
:icon (if is-collapsed "arrow-right" "arrow-down")
:variant "action"}]
(if editing?'
[:& editing-label
(if is-editing
[:> editing-label*
{:default-value label
:on-cancel on-edit-reset
:on-create on-edit-reset
:on-cancel on-reset-edition
:on-submit on-edit-submit'}]
[:*
[:div {:class (stl/css :set-name)
:on-double-click on-double-click
:id label-id}
label]
[:& checkbox
[:> checkbox*
{:on-click on-checkbox-click
:disabled (not can-edit?)
:checked (case active?'
:checked (case is-active
:all true
:partial "mixed"
:none false)
:arial-label (tr "workspace.token.select-set")}]])]))
(mf/defc sets-tree-set
[{:keys [set label tree-depth tree-path tree-index selected? on-select active? draggable? on-toggle editing-id editing? on-edit on-edit-reset on-edit-submit collapsed-paths]}]
(let [set-name (.-name set)
editing?' (editing? editing-id)
active?' (some? (active? set-name))
can-edit? (:can-edit (deref refs/permissions))
(mf/defc sets-tree-set*
[{:keys [id set label tree-depth tree-path tree-index is-selected is-active is-draggable is-editing
on-select on-drop on-toggle on-start-edition on-reset-edition on-edit-submit]}]
(let [set-name (get set :name)
can-edit? (mf/use-ctx ctx/can-edit?)
on-click
(mf/use-fn
(mf/deps editing?' tree-path)
(mf/deps is-editing tree-path)
(fn [event]
(dom/stop-propagation event)
(when-not editing?'
(on-select set-name))))
(when-not is-editing
(when (fn? on-select)
(on-select set-name)))))
on-context-menu
(mf/use-fn
(mf/deps editing?' tree-path can-edit?)
(mf/deps is-editing tree-path can-edit?)
(fn [event]
(dom/prevent-default event)
(dom/stop-propagation event)
(when (and can-edit? (not editing?'))
(st/emit!
(wdt/show-token-set-context-menu
{:position (dom/get-client-position event)
:group? false
:path tree-path})))))
(when (and can-edit? (not is-editing))
(st/emit! (dt/assign-token-set-context-menu
{:position (dom/get-client-position event)
:is-group false
:path tree-path})))))
on-double-click
(mf/use-fn
(mf/deps editing-id)
(fn []
(on-edit editing-id)))
(mf/use-fn (mf/deps id) #(on-start-edition id))
on-checkbox-click
(mf/use-fn
(mf/deps set-name)
(mf/deps set-name on-toggle)
(fn [event]
(dom/stop-propagation event)
(on-toggle set-name)))
(when (fn? on-toggle)
(on-toggle set-name))))
on-edit-submit'
(mf/use-fn
@ -253,20 +276,14 @@
(mf/use-fn
(mf/deps tree-path)
(fn [_]
(when-not selected?
(when-not is-selected
(on-select tree-path))))
on-drop
(mf/use-fn
(mf/deps tree-index collapsed-paths)
(mf/deps tree-index on-drop)
(fn [position data]
(let [props {:from-index (:index data)
:to-index tree-index
:position position
:collapsed-paths @collapsed-paths}]
(if (:group? data)
(st/emit! (wdt/drop-token-set-group props))
(st/emit! (wdt/drop-token-set props))))))
(on-drop tree-index position data)))
[dprops dref]
(h/use-sortable
@ -274,201 +291,280 @@
:on-drag on-drag
:on-drop on-drop
:data {:index tree-index
:group? false}
:draggable? draggable?)]
:is-group false}
:draggable? is-draggable)
drop-over
(get dprops :over)]
[:div {:ref dref
:role "button"
:data-testid "tokens-set-item"
:style {"--tree-depth" tree-depth}
:class (stl/css-case :set-item-container true
:selected-set selected?
:dnd-over (= (:over dprops) :center)
:dnd-over-top (= (:over dprops) :top)
:dnd-over-bot (= (:over dprops) :bot))
:selected-set is-selected
:dnd-over (= drop-over :center)
:dnd-over-top (= drop-over :top)
:dnd-over-bot (= drop-over :bot))
:on-click on-click
:on-context-menu on-context-menu
:aria-checked active?'}
:aria-checked is-active}
[:> icon*
{:icon-id "document"
:class (stl/css-case :icon true
:root-icon (not tree-depth))}]
(if editing?'
[:& editing-label
(if is-editing
[:> editing-label*
{:default-value label
:on-cancel on-edit-reset
:on-create on-edit-reset
:on-cancel on-reset-edition
:on-submit on-edit-submit'}]
[:*
[:div {:class (stl/css :set-name)
:on-double-click on-double-click}
label]
[:& checkbox
[:> checkbox*
{:on-click on-checkbox-click
:disabled (not can-edit?)
:arial-label (tr "workspace.token.select-set")
:checked active?'}]])]))
:checked is-active}]])]))
(mf/defc sets-tree
[{:keys [draggable?
active?
selected?
group-active?
editing?
on-edit-reset
(mf/defc token-sets-tree*
[{:keys [is-draggable
selected
is-token-set-group-active
is-token-set-active
on-start-edition
on-reset-edition
on-edit-submit-set
on-edit-submit-group
on-select
on-toggle-set
on-toggle-set-group
set-node]
:as props}]
(let [{:keys [on-edit new-path] :as ctx} (sets-context/use-context)
collapsed-paths (mf/use-state #{})
token-sets
new-path
edition-id]}]
(let [collapsed-paths* (mf/use-state #{})
collapsed-paths (deref collapsed-paths*)
collapsed?
(mf/use-fn
#(contains? @collapsed-paths %))
(mf/deps collapsed-paths)
(partial contains? collapsed-paths))
on-drop
(mf/use-fn
(mf/deps collapsed-paths)
(fn [tree-index position data]
(let [props {:from-index (:index data)
:to-index tree-index
:position position
:collapsed-paths collapsed-paths}]
(if (:is-group data)
(st/emit! (dt/drop-token-set-group props))
(st/emit! (dt/drop-token-set props))))))
on-toggle-collapse
(mf/use-fn
(fn [path]
(swap! collapsed-paths #(if (contains? % path)
(disj % path)
(conj % path)))))]
(for [[index {:keys [new? group? path parent-path depth] :as node}]
(d/enumerate (ctob/walk-sets-tree-seq set-node {:skip-children-pred #(contains? @collapsed-paths %)
:new-editing-set-path new-path}))]
(cond
group?
(let [editing-id (sets-context/set-group-path->id path)]
[:& sets-tree-set-group
{:key editing-id
:label (last path)
:active? group-active?
:selected? false
:draggable? draggable?
:on-select on-select
:tree-path path
:tree-depth depth
:tree-index index
:tree-parent-path parent-path
:editing-id editing-id
:editing? editing?
:on-edit on-edit
:on-edit-reset on-edit-reset
:on-edit-submit on-edit-submit-group
:collapsed? (collapsed? path)
:on-toggle-collapse on-toggle-collapse
:on-toggle on-toggle-set-group
:collapsed-paths collapsed-paths}])
(swap! collapsed-paths* #(if (contains? % path)
(disj % path)
(conj % path)))))]
new?
(let [editing-id (sets-context/set-path->id path)]
[:& sets-tree-set
{:key editing-id
:set (ctob/make-token-set :name (if (empty? parent-path)
""
(ctob/join-set-path parent-path)))
:label ""
:active? (constantly true)
:selected? (constantly true)
:on-select (constantly nil)
:tree-path path
:tree-depth depth
:tree-index index
:tree-parent-path parent-path
:on-toggle (constantly nil)
:editing-id editing-id
:editing? (constantly true)
:on-edit-reset on-edit-reset
:on-edit-submit on-create-token-set}])
(for [{:keys [id token-set index is-new is-group path parent-path depth] :as node}
(ctob/sets-tree-seq token-sets
{:skip-children-pred collapsed?
:new-at-path new-path})]
(cond
^boolean is-group
[:> sets-tree-set-group*
{:key index
:label (peek path)
:id id
:is-active (is-token-set-group-active path)
:is-selected false
:is-draggable is-draggable
:is-editing (= edition-id id)
:is-collapsed (collapsed? path)
:on-select on-select
:tree-path path
:tree-depth depth
:tree-index index
:tree-parent-path parent-path
:on-drop on-drop
:on-start-edition on-start-edition
:on-reset-edition on-reset-edition
:on-edit-submit on-edit-submit-group
:on-toggle-collapse on-toggle-collapse
:on-toggle on-toggle-set-group}]
^boolean is-new
[:> sets-tree-set*
{:key index
:set token-set
:label ""
:id id
:is-editing true
:is-active true
:is-selected true
:tree-path path
:tree-depth depth
:tree-index index
:tree-parent-path parent-path
:on-drop on-drop
:on-reset-edition on-reset-edition
:on-edit-submit on-create-token-set}]
:else
(let [editing-id (sets-context/set-path->id path)]
[:& sets-tree-set
{:key editing-id
:set (:set node)
:label (last path)
:active? active?
:selected? (selected? (get-in node [:set :name]))
:draggable? draggable?
:on-select on-select
:tree-path path
:tree-depth depth
:tree-index index
:tree-parent-path parent-path
:on-toggle on-toggle-set
:editing-id editing-id
:editing? editing?
:on-edit on-edit
:on-edit-reset on-edit-reset
:on-edit-submit on-edit-submit-set
:collapsed-paths collapsed-paths}])))))
[:> sets-tree-set*
{:key index
:set token-set
:id id
:label (peek path)
:is-editing (= edition-id id)
:is-active (is-token-set-active id)
:is-selected (= selected id)
:is-draggable is-draggable
:on-select on-select
:tree-path path
:tree-depth depth
:tree-index index
:tree-parent-path parent-path
:on-toggle on-toggle-set
:edition-id edition-id
:on-start-edition on-start-edition
:on-drop on-drop
:on-reset-edition on-reset-edition
:on-edit-submit on-edit-submit-set}]))))
(mf/defc controlled-sets-list
(mf/defc controlled-sets-list*
{::mf/props :obj}
[{:keys [token-sets
selected
on-update-token-set
on-update-token-set-group
token-set-selected?
token-set-active?
token-set-group-active?
is-token-set-active
is-token-set-group-active
on-create-token-set
on-toggle-token-set
on-toggle-token-set-group
on-start-edition
on-reset-edition
origin
on-select
context]
:as _props}]
(let [{:keys [editing? on-edit on-reset new-path] :as ctx} (or context (sets-context/use-context))
theme-modal? (= origin "theme-modal")
can-edit? (:can-edit (deref refs/permissions))
draggable? (and (not theme-modal?) can-edit?)]
new-path
edition-id]}]
(assert (fn? is-token-set-group-active) "expected a function for `is-token-set-group-active` prop")
(assert (fn? is-token-set-active) "expected a function for `is-token-set-active` prop")
(let [theme-modal? (= origin "theme-modal")
can-edit? (mf/use-ctx ctx/can-edit?)
draggable? (and (not theme-modal?) can-edit?)
empty-state? (and theme-modal?
(empty? token-sets)
(not new-path))
;; NOTE: on-reset-edition and on-start-edition function can
;; come as nil, in this case we need to provide a safe
;; fallback for them
on-reset-edition
(mf/use-fn
(mf/deps on-reset-edition)
(fn [v]
(when (fn? on-reset-edition)
(on-reset-edition v))))
on-start-edition
(mf/use-fn
(mf/deps on-start-edition)
(fn [v]
(when (fn? on-start-edition)
(on-start-edition v))))]
[:fieldset {:class (stl/css :sets-list)}
(if (and theme-modal?
(empty? token-sets)
(not new-path))
(if ^boolean empty-state?
[:> text* {:as "span" :typography "body-small" :class (stl/css :empty-state-message-sets)}
(tr "workspace.token.no-sets-create")]
[:& sets-tree
{:draggable? draggable?
:set-node token-sets
:selected? token-set-selected?
[:> token-sets-tree*
{:is-draggable draggable?
:new-path new-path
:edition-id edition-id
:token-sets token-sets
:selected selected
:on-select on-select
:active? token-set-active?
:group-active? token-set-group-active?
:is-token-set-active is-token-set-active
:is-token-set-group-active is-token-set-group-active
:on-toggle-set on-toggle-token-set
:on-toggle-set-group on-toggle-token-set-group
:editing? editing?
:on-create-token-set on-create-token-set
:on-edit on-edit
:on-edit-reset on-reset
:on-start-edition on-start-edition
:on-reset-edition on-reset-edition
:on-edit-submit-set on-update-token-set
:on-edit-submit-group on-update-token-set-group}])]))
(mf/defc sets-list*
[{:keys [tokens-lib selected new-path edition-id]}]
(mf/defc sets-list
[{:keys []}]
(let [token-sets (mf/deref refs/workspace-token-sets-tree)
selected-token-set-name (mf/deref refs/workspace-selected-token-set-name)
token-set-selected? (mf/use-fn
(mf/deps token-sets selected-token-set-name)
(fn [set-name]
(= set-name selected-token-set-name)))
active-token-set-names (mf/deref refs/workspace-active-set-names)
token-set-active? (mf/use-fn
(mf/deps active-token-set-names)
(fn [set-name]
(get active-token-set-names set-name)))
token-set-group-active? (mf/use-fn
(fn [group-path]
@(refs/token-sets-at-path-all-active group-path)))]
[:& controlled-sets-list
(let [token-sets
(some-> tokens-lib (ctob/get-set-tree))
can-edit?
(mf/use-ctx ctx/can-edit?)
active-token-sets-names
(mf/with-memo [tokens-lib]
(some-> tokens-lib (ctob/get-active-themes-set-names)))
token-set-active?
(mf/use-fn
(mf/deps active-token-sets-names)
(fn [name]
(contains? active-token-sets-names name)))
token-set-group-active?
(mf/use-fn
(fn [group-path]
;; FIXME
@(refs/token-sets-at-path-all-active group-path)))
on-reset-edition
(mf/use-fn
(mf/deps can-edit?)
(fn [_]
(when can-edit?
(st/emit! (dt/clear-token-set-edition)
(dt/clear-token-set-creation)))))
on-start-edition
(mf/use-fn
(mf/deps can-edit?)
(fn [id]
(when can-edit?
(st/emit! (dt/start-token-set-edition id)))))]
[:> controlled-sets-list*
{:token-sets token-sets
:token-set-selected? token-set-selected?
:token-set-active? token-set-active?
:token-set-group-active? token-set-group-active?
:is-token-set-active token-set-active?
:is-token-set-group-active token-set-group-active?
:on-select on-select-token-set-click
:selected selected
:new-path new-path
:edition-id edition-id
:origin "set-panel"
:can-edit can-edit?
:on-start-edition on-start-edition
:on-reset-edition on-reset-edition
:on-toggle-token-set on-toggle-token-set-click
:on-toggle-token-set-group on-toggle-token-set-group-click
:on-update-token-set on-update-token-set

View file

@ -4,6 +4,7 @@
//
// Copyright (c) KALEIDOS INC
@use "../../ds/typography.scss" as *;
@import "refactor/common-refactor.scss";
.sets-list {
@ -12,6 +13,21 @@
overflow-y: auto;
}
.empty-sets-wrapper {
padding: $s-12;
padding-inline-start: $s-24;
color: var(--color-foreground-secondary);
}
.create-set-button {
@include use-typography("body-small");
background-color: transparent;
border: none;
appearance: none;
color: var(--color-accent-primary);
cursor: pointer;
}
.set-item-container {
@include bodySmallTypography;
display: flex;

View file

@ -1,54 +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.main.ui.workspace.tokens.sets-context
(:require
[app.common.data.macros :as dm]
[rumext.v2 :as mf]))
(defn set-group-path->id [set-group-path]
(dm/str "group-" set-group-path))
(defn set-path->id [set-path]
(dm/str "set-" set-path))
(def initial {})
(def context (mf/create-context initial))
(def static-context
{:editing? (constantly false)
:on-edit (constantly nil)
:on-create (constantly nil)
:on-reset (constantly nil)})
(mf/defc provider
{::mf/wrap-props false}
[props]
(let [children (unchecked-get props "children")
state (mf/use-state initial)]
[:& (mf/provider context) {:value state}
children]))
(defn use-context []
(let [ctx (mf/use-ctx context)
{:keys [editing-id new-path]} @ctx
editing? (mf/use-callback
(mf/deps editing-id)
#(= editing-id %))
on-edit (mf/use-fn
(fn [editing-id]
(reset! ctx (assoc @ctx :editing-id editing-id))))
on-create (mf/use-fn
(fn [path]
(swap! ctx assoc :editing-id (random-uuid) :new-path path)))
on-reset (mf/use-fn
#(reset! ctx initial))]
{:editing? editing?
:new-path new-path
:on-edit on-edit
:on-create on-create
:on-reset on-reset}))

View file

@ -7,76 +7,75 @@
(ns app.main.ui.workspace.tokens.sets-context-menu
(:require-macros [app.main.style :as stl])
(:require
[app.main.data.tokens :as wdt]
[app.common.data.macros :as dm]
[app.common.types.tokens-lib :as ctob]
[app.main.data.tokens :as dt]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.components.dropdown :refer [dropdown]]
[app.main.ui.workspace.tokens.sets-context :as sets-context]
[app.util.dom :as dom]
[app.util.i18n :refer [tr]]
[okulary.core :as l]
[rumext.v2 :as mf]))
(def sets-menu-ref
(l/derived :token-set-context-menu refs/workspace-local))
(def ^:private ref:token-sets-context-menu
(l/derived :token-set-context-menu refs/workspace-tokens))
(defn- prevent-default
[event]
(dom/prevent-default event)
(dom/stop-propagation event))
(mf/defc menu-entry
{::mf/props :obj}
(mf/defc menu-entry*
[{:keys [title value on-click]}]
[:li {:class (stl/css :context-menu-item)
:data-value value
:on-click on-click}
[:span {:class (stl/css :title)} title]])
(mf/defc menu
[{:keys [group? path]}]
(let [{:keys [on-create on-edit]} (sets-context/use-context)
create-set-at-path
(mf/defc menu*
{::mf/private true}
[{:keys [is-group path]}]
(let [create-set-at-path
(mf/use-fn (mf/deps path) #(st/emit! (dt/start-token-set-creation path)))
on-edit
(mf/use-fn
(mf/deps path)
#(on-create path))
edit-name
(mf/use-fn
(mf/deps group?)
(fn []
(let [path (if group?
(sets-context/set-group-path->id path)
(sets-context/set-path->id path))]
(on-edit path))))
(let [name (ctob/join-set-path path)]
(st/emit! (dt/start-token-set-edition name)))))
delete-set
on-delete
(mf/use-fn
#(st/emit! (wdt/delete-token-set-path group? path)))]
[:ul {:class (stl/css :context-list)}
(when group?
[:& menu-entry {:title (tr "workspace.token.add-set-to-group") :on-click create-set-at-path}])
[:& menu-entry {:title (tr "labels.rename") :on-click edit-name}]
[:& menu-entry {:title (tr "labels.delete") :on-click delete-set}]]))
(mf/deps is-group path)
#(st/emit! (dt/delete-token-set-path is-group path)))]
(mf/defc sets-context-menu
[:ul {:class (stl/css :context-list)}
(when is-group
[:> menu-entry* {:title (tr "workspace.token.add-set-to-group") :on-click create-set-at-path}])
[:> menu-entry* {:title (tr "labels.rename") :on-click on-edit}]
[:> menu-entry* {:title (tr "labels.delete") :on-click on-delete}]]))
(mf/defc token-set-context-menu*
[]
(let [mdata (mf/deref sets-menu-ref)
top (+ (get-in mdata [:position :y]) 5)
left (+ (get-in mdata [:position :x]) 5)
width (mf/use-state 0)
dropdown-ref (mf/use-ref)]
(mf/use-effect
(mf/deps mdata)
(fn []
(when-let [node (mf/ref-val dropdown-ref)]
(reset! width (.-offsetWidth node)))))
[:& dropdown {:show (boolean mdata)
:on-close #(st/emit! wdt/hide-token-set-context-menu)}
(let [{:keys [position is-group path]}
(mf/deref ref:token-sets-context-menu)
position-top
(+ (dm/get-prop position :y) 5)
position-left
(+ (dm/get-prop position :x) 5)
on-close
(mf/use-fn #(st/emit! (dt/assign-token-set-context-menu nil)))]
[:& dropdown {:show (some? position)
:on-close on-close}
[:div {:class (stl/css :token-set-context-menu)
:data-testid "tokens-context-menu-for-set"
:ref dropdown-ref
:style {:top top :left left}
:style {:top position-top
:left position-left}
:on-context-menu prevent-default}
[:& menu {:group? (:group? mdata)
:path (:path mdata)}]]]))
[:> menu* {:is-group is-group :path path}]]]))

View file

@ -8,7 +8,7 @@
(:require-macros [app.main.style :as stl])
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.json :as json]
[app.common.types.tokens-lib :as ctob]
[app.main.data.event :as ev]
[app.main.data.modal :as modal]
@ -29,9 +29,8 @@
[app.main.ui.workspace.tokens.changes :as wtch]
[app.main.ui.workspace.tokens.context-menu :refer [token-context-menu]]
[app.main.ui.workspace.tokens.errors :as wte]
[app.main.ui.workspace.tokens.sets :refer [sets-list]]
[app.main.ui.workspace.tokens.sets-context :as sets-context]
[app.main.ui.workspace.tokens.sets-context-menu :refer [sets-context-menu]]
[app.main.ui.workspace.tokens.sets :as tsets]
[app.main.ui.workspace.tokens.sets-context-menu :refer [token-set-context-menu*]]
[app.main.ui.workspace.tokens.style-dictionary :as sd]
[app.main.ui.workspace.tokens.theme-select :refer [theme-select]]
[app.main.ui.workspace.tokens.token-pill :refer [token-pill*]]
@ -46,7 +45,7 @@
[shadow.resource]))
(def ref:token-type-open-status
(l/derived #(dm/get-in % [:workspace-local :token-type-open-status]) st/state))
(l/derived (l/key :open-status-by-type) refs/workspace-tokens))
;; Components ------------------------------------------------------------------
@ -72,6 +71,9 @@
(let [{:keys [modal title]}
(get wtch/token-properties type)
can-edit?
(mf/use-ctx ctx/can-edit?)
tokens
(mf/with-memo [tokens]
(vec (sort-by :name tokens)))
@ -80,8 +82,7 @@
(mf/use-fn
(fn [event token]
(dom/prevent-default event)
(dom/stop-propagation event)
(st/emit! (dt/show-token-context-menu
(st/emit! (dt/assign-token-context-menu
{:type :token
:position (dom/get-client-position event)
:errors (:errors token)
@ -115,14 +116,12 @@
(dom/stop-propagation event)
(when (seq selected-shapes)
(st/emit! (wtch/toggle-token {:token token
:shapes selected-shapes})))))
tokens-count (count tokens)
can-edit? (:can-edit (deref refs/permissions))]
:shapes selected-shapes})))))]
[:div {:on-click on-toggle-open-click}
[:& cmm/asset-section {:icon (token-section-icon type)
:title title
:assets-count tokens-count
:assets-count (count tokens)
:open? is-open}
[:& cmm/asset-section-block {:role :title-button}
(when can-edit?
@ -168,11 +167,8 @@
(let [ordered-themes
(mf/deref refs/workspace-token-themes-no-hidden)
permissions
(mf/use-ctx ctx/permissions)
can-edit?
(get permissions :can-edit)
(mf/use-ctx ctx/can-edit?)
open-modal
(mf/use-fn
@ -201,64 +197,45 @@
(tr "workspace.token.no-permission-themes"))}
[:& theme-select]]))]))
(mf/defc add-set-button*
(mf/defc token-sets-list*
{::mf/private true}
[{:keys [style]}]
(let [{:keys [on-create new-path]}
(sets-context/use-context)
[{:keys [tokens-lib]}]
(let [;; FIXME: This is an inneficient operation just for being
;; ability to check if there are some sets and lookup the
;; first one when no set is selected, should be REFACTORED; is
;; inneficient because instead of return the sets as-is (tree)
;; it firstly makes it a plain seq from tree.
token-sets
(some-> tokens-lib (ctob/get-sets))
permissions
(mf/use-ctx ctx/permissions)
selected-token-set-name
(mf/deref refs/selected-token-set-name)
can-edit?
(get permissions :can-edit)
{:keys [token-set-edition-id
token-set-new-path]}
(mf/deref refs/workspace-tokens)]
on-click
(mf/use-fn
(mf/deps on-create)
(fn []
(on-create [])))]
(if (= style "inline")
(when-not new-path
(if can-edit?
[:div {:class (stl/css :empty-sets-wrapper)}
[:> text* {:as "span" :typography "body-small" :class (stl/css :empty-state-message)}
(tr "workspace.token.no-sets-yet")]
[:button {:on-click on-click
:class (stl/css :create-theme-button)}
(tr "workspace.token.create-one")]]
[:div {:class (stl/css :empty-sets-wrapper)}
[:> text* {:as "span" :typography "body-small" :class (stl/css :empty-state-message)}
(tr "workspace.token.no-sets-yet")]]))
(when can-edit?
[:> icon-button* {:variant "ghost"
:icon "add"
:on-click on-click
:aria-label (tr "workspace.token.add set")}]))))
(mf/defc theme-sets-list*
{::mf/private true}
[]
(let [token-sets (mf/deref refs/workspace-ordered-token-sets)
{:keys [new-path] :as ctx} (sets-context/use-context)]
(if (and (empty? token-sets)
(not new-path))
[:> add-set-button* {:style "inline"}]
[:& h/sortable-container {}
[:& sets-list]])))
(not token-set-new-path))
(mf/defc themes-sets-tab*
(when-not token-set-new-path
[:> tsets/inline-add-button*])
[:> h/sortable-container {}
[:> tsets/sets-list*
{:tokens-lib tokens-lib
:new-path token-set-new-path
:edition-id token-set-edition-id
:selected selected-token-set-name}]])))
(mf/defc token-sets-section*
{::mf/private true}
[{:keys [resize-height]}]
(let [permissions
(mf/use-ctx ctx/permissions)
[{:keys [resize-height] :as props}]
(let [can-edit?
(mf/use-ctx ctx/can-edit?)]
can-edit?
(get permissions :can-edit)]
[:& sets-context/provider {}
[:& sets-context-menu]
[:*
[:> token-set-context-menu*]
[:article {:data-testid "token-themes-sets-sidebar"
:class (stl/css :sets-section-wrapper)
:style {"--resize-height" (str resize-height "px")}}
@ -267,12 +244,12 @@
[:div {:class (stl/css :sidebar-header)}
[:& title-bar {:title (tr "labels.sets")}
(when can-edit?
[:> add-set-button* {:style "header"}])]]
[:> tsets/add-button*])]]
[:> theme-sets-list* {}]]]]))
[:> token-sets-list* props]]]]))
(mf/defc tokens-tab*
[]
(mf/defc tokens-section*
[{:keys [tokens-lib]}]
(let [objects (mf/deref refs/workspace-page-objects)
selected (mf/deref refs/selected-shapes)
open-status (mf/deref ref:token-type-open-status)
@ -282,16 +259,36 @@
(into [] (keep (d/getf objects)) selected))
active-theme-tokens
(sd/use-active-theme-tokens)
(mf/with-memo [tokens-lib]
(if tokens-lib
(ctob/get-active-themes-set-tokens tokens-lib)
{}))
;; Resolve tokens as second step
active-theme-tokens
(sd/use-resolved-tokens* active-theme-tokens)
;; This only checks for the currently explicitly selected set
;; name, it is ephimeral and can be nil
selected-token-set-name
(mf/deref refs/selected-token-set-name)
selected-token-set
(when selected-token-set-name
(some-> tokens-lib (ctob/get-set selected-token-set-name)))
;; If we have not selected any set explicitly we just
;; select the first one from the list of sets
selected-token-set-tokens
(when selected-token-set
(get selected-token-set :tokens))
tokens
(sd/use-resolved-workspace-tokens)
(mf/with-memo [active-theme-tokens selected-token-set-tokens]
(merge active-theme-tokens selected-token-set-tokens))
selected-token-set-tokens
(mf/deref refs/workspace-selected-token-set-tokens)
selected-token-set-name
(mf/deref refs/workspace-selected-token-set-name)
tokens
(sd/use-resolved-tokens* tokens)
tokens-by-type
(mf/with-memo [tokens selected-token-set-tokens]
@ -307,6 +304,16 @@
(mf/with-memo [tokens-by-type]
(get-sorted-token-groups tokens-by-type))]
(mf/with-effect [tokens-lib selected-token-set-name]
(when (and tokens-lib
(or (nil? selected-token-set-name)
(and selected-token-set-name
(not (ctob/get-set tokens-lib selected-token-set-name)))))
(let [match (->> (ctob/get-sets tokens-lib)
(first)
(:name))]
(st/emit! (dt/set-selected-token-set-name match)))))
[:*
[:& token-context-menu]
[:& title-bar {:all-clickable true
@ -328,12 +335,15 @@
:active-theme-tokens active-theme-tokens
:tokens []}])]))
(mf/defc import-export-button
{::mf/wrap-props false}
[{:keys []}]
(let [show-menu* (mf/use-state false)
(mf/defc import-export-button*
[]
(let [input-ref (mf/use-ref)
show-menu* (mf/use-state false)
show-menu? (deref show-menu*)
can-edit? (:can-edit (deref refs/permissions))
can-edit?
(mf/use-ctx ctx/can-edit?)
open-menu
(mf/use-fn
@ -347,34 +357,37 @@
(dom/stop-propagation event)
(reset! show-menu* false)))
input-ref (mf/use-ref)
on-display-file-explorer
(mf/use-fn
#(.click (mf/ref-val input-ref)))
(mf/use-fn #(dom/click (mf/ref-val input-ref)))
on-import
(fn [event]
(let [file (-> event .-target .-files (aget 0))]
(->> (wapi/read-file-as-text file)
(sd/process-json-stream)
(rx/subs! (fn [lib]
(st/emit! (ptk/event ::ev/event {::ev/name "import-tokens"}))
(st/emit! (dt/import-tokens-lib lib)))
(fn [err]
(js/console.error err)
(st/emit! (ntf/show {:content (wte/humanize-errors [(ex-data err)])
:type :toast
:level :error})))))
(set! (.-value (mf/ref-val input-ref)) "")))
(mf/use-fn
(fn [event]
(let [file (-> (dom/get-target event)
(dom/get-files)
(first))]
(->> (wapi/read-file-as-text file)
(sd/process-json-stream)
(rx/subs! (fn [lib]
(st/emit! (ptk/data-event ::ev/event {::ev/name "import-tokens"})
(dt/import-tokens-lib lib)))
(fn [err]
(js/console.error err)
(st/emit! (ntf/show {:content (wte/humanize-errors [(ex-data err)])
:type :toast
:level :error})))))
(-> (mf/ref-val input-ref)
(dom/set-value! "")))))
on-export (fn []
(st/emit! (ptk/event ::ev/event {::ev/name "export-tokens"}))
(let [tokens-json (some-> (deref refs/tokens-lib)
(ctob/encode-dtcg)
(clj->js)
(js/JSON.stringify nil 2))]
(->> (wapi/create-blob (or tokens-json "{}") "application/json")
(dom/trigger-download "tokens.json"))))]
on-export
(mf/use-fn
(fn []
(st/emit! (ptk/data-event ::ev/event {::ev/name "export-tokens"}))
(let [tokens-json (some-> (deref refs/tokens-lib)
(ctob/encode-dtcg)
(json/encode :key-fn identity))]
(->> (wapi/create-blob (or tokens-json "{}") "application/json")
(dom/trigger-download "tokens.json")))))]
[:div {:class (stl/css :import-export-button-wrapper)}
(when can-edit?
@ -406,14 +419,20 @@
on-lost-pointer-capture-pages :on-lost-pointer-capture
on-pointer-move-pages :on-pointer-move
size-pages-opened :size}
(use-resize-hook :tokens 200 38 "0.6" :y false nil)]
(use-resize-hook :tokens 200 38 400 :y false nil)
tokens-lib
(mf/deref refs/tokens-lib)]
[:div {:class (stl/css :sidebar-wrapper)}
[:> themes-sets-tab* {:resize-height size-pages-opened}]
[:> token-sets-section*
{:resize-height size-pages-opened
:tokens-lib tokens-lib}]
[:article {:class (stl/css :tokens-section-wrapper)
:data-testid "tokens-sidebar"}
[:div {:class (stl/css :resize-area-horiz)
:on-pointer-down on-pointer-down-pages
:on-lost-pointer-capture on-lost-pointer-capture-pages
:on-pointer-move on-pointer-move-pages}]
[:> tokens-tab*]]
[:& import-export-button]]))
[:> tokens-section* {:tokens-lib tokens-lib}]]
[:> import-export-button*]]))

View file

@ -54,12 +54,6 @@
color: var(--color-foreground-secondary);
}
.empty-sets-wrapper {
padding: $s-12;
padding-inline-start: $s-24;
color: var(--color-foreground-secondary);
}
.sidebar-header {
display: flex;
align-items: center;

View file

@ -5,7 +5,6 @@
[app.common.logging :as l]
[app.common.transit :as t]
[app.common.types.tokens-lib :as ctob]
[app.main.refs :as refs]
[app.main.ui.workspace.tokens.errors :as wte]
[app.main.ui.workspace.tokens.tinycolor :as tinycolor]
[app.main.ui.workspace.tokens.token :as wtt]
@ -166,10 +165,10 @@
([tokens-tree get-token]
(resolve-tokens-tree+ tokens-tree get-token (StyleDictionary. default-config)))
([tokens-tree get-token style-dictionary]
(-> style-dictionary
(add-tokens tokens-tree)
(build-dictionary)
(p/then #(process-sd-tokens % get-token)))))
(let [sdict (-> style-dictionary
(add-tokens tokens-tree)
(build-dictionary))]
(p/fmap #(process-sd-tokens % get-token) sdict))))
(defn sd-token-name [^js sd-token]
(.. sd-token -original -name))
@ -177,8 +176,10 @@
(defn sd-token-uuid [^js sd-token]
(uuid (.-uuid (.-id ^js sd-token))))
(defn resolve-tokens+ [tokens]
(resolve-tokens-tree+ (ctob/tokens-tree tokens) #(get tokens (sd-token-name %))))
(defn resolve-tokens+
[tokens]
(let [tokens-tree (ctob/tokens-tree tokens)]
(resolve-tokens-tree+ tokens-tree #(get tokens (sd-token-name %)))))
(defn resolve-tokens-interactive+
"Interactive check of resolving tokens.
@ -267,36 +268,45 @@
:or {cache-atom !tokens-cache}
:as config}]
(let [tokens-state (mf/use-state (get @cache-atom tokens))]
(mf/use-effect
(mf/deps tokens config)
(fn []
(let [cached (get @cache-atom tokens)]
(cond
(nil? tokens) nil
;; The tokens are already processing somewhere
(p/promise? cached) (-> cached
(p/then #(reset! tokens-state %))
#_(p/catch js/console.error))
;; 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))))))))
;; FIXME: this with effect with trigger all the time because
;; `config` will be always a different instance
(mf/with-effect [tokens config]
(let [cached (get @cache-atom tokens)]
(cond
(nil? tokens) nil
;; The tokens are already processing somewhere
(p/promise? cached) (-> cached
(p/then #(reset! tokens-state %))
#_(p/catch js/console.error))
;; 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)))))))
@tokens-state))
(defn use-resolved-workspace-tokens []
(let [active-theme-tokens (mf/deref refs/workspace-active-theme-sets-tokens)
selected-token-set-tokens (mf/deref refs/workspace-selected-token-set-tokens)
prefer-selected-token-set-tokens (merge active-theme-tokens selected-token-set-tokens)]
(use-resolved-tokens prefer-selected-token-set-tokens)))
(defn use-resolved-tokens*
"This hook will return the unresolved tokens as state until they are
processed, then the state will be updated with the resolved tokens.
(defn use-active-theme-tokens
"A hook that returns active tokens for the current active theme"
[]
(-> (mf/deref refs/workspace-active-theme-sets-tokens)
(use-resolved-tokens {:cache-atom !theme-tokens-cache})))
This is a cache-less, simplified version of use-resolved-tokens
hook."
[tokens & {:keys [interactive?]}]
(let [state* (mf/use-state tokens)]
(mf/with-effect [tokens interactive?]
(when (seq tokens)
(let [promise (if interactive?
(resolve-tokens-interactive+ tokens)
(resolve-tokens+ tokens))]
(->> promise
(p/fmap (fn [resolved-tokens]
(reset! state* resolved-tokens)))))))
@state*))