mirror of
https://github.com/penpot/penpot.git
synced 2025-01-21 06:02:32 -05:00
Merge pull request #263 from tokens-studio/token-sets-themes
Token sets themes
This commit is contained in:
commit
734acd27b9
22 changed files with 1040 additions and 235 deletions
|
@ -24,7 +24,9 @@
|
|||
[app.common.types.shape :as cts]
|
||||
[app.common.types.shape-tree :as ctst]
|
||||
[app.common.types.token :as cto]
|
||||
[app.common.types.token-theme :as ctot]
|
||||
[app.common.types.tokens-list :as ctol]
|
||||
[app.common.types.tokens-theme-list :as ctotl]
|
||||
[app.common.types.typographies-list :as ctyl]
|
||||
[app.common.types.typography :as ctt]
|
||||
[clojure.set :as set]))
|
||||
|
@ -247,6 +249,53 @@
|
|||
[:type [:= :del-typography]]
|
||||
[:id ::sm/uuid]]]
|
||||
|
||||
[:add-temporary-token-theme
|
||||
[:map {:title "AddTemporaryTokenThemeChange"}
|
||||
[:type [:= :add-temporary-token-theme]]
|
||||
[:token-theme ::ctot/token-theme]]]
|
||||
|
||||
[:update-active-token-themes
|
||||
[:map {:title "UpdateActiveTokenThemes"}
|
||||
[:type [:= :update-active-token-themes]]
|
||||
[:theme-ids [:set ::sm/uuid]]]]
|
||||
|
||||
[:delete-temporary-token-theme
|
||||
[:map {:title "DeleteTemporaryTokenThemeChange"}
|
||||
[:type [:= :delete-temporary-token-theme]]
|
||||
[:id ::sm/uuid]]]
|
||||
|
||||
[:add-token-theme
|
||||
[:map {:title "AddTokenThemeChange"}
|
||||
[:type [:= :add-token-theme]]
|
||||
[:token-theme ::ctot/token-theme]]]
|
||||
|
||||
[:mod-token-theme
|
||||
[:map {:title "ModTokenThemeChange"}
|
||||
[:type [:= :mod-token-theme]]
|
||||
[:id ::sm/uuid]
|
||||
[:token-theme ::ctot/token-theme]]]
|
||||
|
||||
[:del-token-theme
|
||||
[:map {:title "DelTokenThemeChange"}
|
||||
[:type [:= :del-token-theme]]
|
||||
[:id ::sm/uuid]]]
|
||||
|
||||
[:add-token-set
|
||||
[:map {:title "AddTokenSetChange"}
|
||||
[:type [:= :add-token-set]]
|
||||
[:token-set ::ctot/token-set]]]
|
||||
|
||||
[:mod-token-set
|
||||
[:map {:title "ModTokenSetChange"}
|
||||
[:type [:= :mod-token-set]]
|
||||
[:id ::sm/uuid]
|
||||
[:token-set ::ctot/token-set]]]
|
||||
|
||||
[:del-token-set
|
||||
[:map {:title "DelTokenSetChange"}
|
||||
[:type [:= :del-token-set]]
|
||||
[:id ::sm/uuid]]]
|
||||
|
||||
[:add-token
|
||||
[:map {:title "AddTokenChange"}
|
||||
[:type [:= :add-token]]
|
||||
|
@ -742,6 +791,42 @@
|
|||
[data {:keys [id]}]
|
||||
(ctol/delete-token data id))
|
||||
|
||||
(defmethod process-change :add-temporary-token-theme
|
||||
[data {:keys [token-theme]}]
|
||||
(ctotl/add-temporary-token-theme data token-theme))
|
||||
|
||||
(defmethod process-change :update-active-token-themes
|
||||
[data {:keys [theme-ids]}]
|
||||
(ctotl/assoc-active-token-themes data theme-ids))
|
||||
|
||||
(defmethod process-change :delete-temporary-token-theme
|
||||
[data {:keys [id]}]
|
||||
(ctotl/delete-temporary-token-theme data id))
|
||||
|
||||
(defmethod process-change :add-token-theme
|
||||
[data {:keys [token-theme]}]
|
||||
(ctotl/add-token-theme data token-theme))
|
||||
|
||||
(defmethod process-change :mod-token-theme
|
||||
[data {:keys [id token-theme]}]
|
||||
(ctotl/update-token-theme data id merge token-theme))
|
||||
|
||||
(defmethod process-change :del-token-theme
|
||||
[data {:keys [id]}]
|
||||
(ctotl/delete-token-theme data id))
|
||||
|
||||
(defmethod process-change :add-token-set
|
||||
[data {:keys [token-set]}]
|
||||
(ctotl/add-token-set data token-set))
|
||||
|
||||
(defmethod process-change :mod-token-set
|
||||
[data {:keys [id token-set]}]
|
||||
(ctotl/update-token-set data id merge token-set))
|
||||
|
||||
(defmethod process-change :del-token-set
|
||||
[data {:keys [id]}]
|
||||
(ctotl/delete-token-set data id))
|
||||
|
||||
;; === Operations
|
||||
(defmethod process-operation :set
|
||||
[on-changed shape op]
|
||||
|
|
|
@ -328,7 +328,7 @@
|
|||
(update :redo-changes conj add-change)
|
||||
(cond->
|
||||
(and (ctk/in-component-copy? parent) (not ignore-touched))
|
||||
(update :undo-changes conj restore-touched-change))
|
||||
(update :undo-changes conj restore-touched-change))
|
||||
(update :undo-changes conj del-change)
|
||||
(apply-changes-local)))))
|
||||
|
||||
|
@ -389,7 +389,7 @@
|
|||
(update :redo-changes conj set-parent-change)
|
||||
(cond->
|
||||
(ctk/in-component-copy? parent)
|
||||
(update :undo-changes conj restore-touched-change))
|
||||
(update :undo-changes conj restore-touched-change))
|
||||
(update :undo-changes #(reduce mk-undo-change % shapes))
|
||||
(apply-changes-local)))))
|
||||
|
||||
|
@ -695,6 +695,68 @@
|
|||
(update :undo-changes conj {:type :add-typography :typography prev-typography})
|
||||
(apply-changes-local))))
|
||||
|
||||
(defn add-temporary-token-theme
|
||||
[changes token-theme]
|
||||
(-> changes
|
||||
(update :redo-changes conj {:type :add-temporary-token-theme :token-theme token-theme})
|
||||
(update :undo-changes conj {:type :delete-temporary-token-theme :id (:id token-theme)})
|
||||
(apply-changes-local)))
|
||||
|
||||
(defn update-active-token-themes
|
||||
[changes token-active-theme-ids prev-token-active-theme-ids]
|
||||
(-> changes
|
||||
(update :redo-changes conj {:type :update-active-token-themes :theme-ids token-active-theme-ids})
|
||||
(update :undo-changes conj {:type :update-active-token-themes :theme-ids prev-token-active-theme-ids})
|
||||
(apply-changes-local)))
|
||||
|
||||
(defn add-token-theme
|
||||
[changes token-theme]
|
||||
(-> changes
|
||||
(update :redo-changes conj {:type :add-token-theme :token-theme token-theme})
|
||||
(update :undo-changes conj {:type :del-token-theme :id (:id token-theme)})
|
||||
(apply-changes-local)))
|
||||
|
||||
(defn update-token-theme
|
||||
[changes token-theme prev-token-theme]
|
||||
(-> changes
|
||||
(update :redo-changes conj {:type :mod-token-theme :id (:id token-theme) :token-theme token-theme})
|
||||
(update :undo-changes conj {:type :mod-token-theme :id (:id token-theme) :token-theme (or prev-token-theme token-theme)})
|
||||
(apply-changes-local)))
|
||||
|
||||
(defn delete-token-theme
|
||||
[changes token-theme-id]
|
||||
(assert-library! changes)
|
||||
(let [library-data (::library-data (meta changes))
|
||||
prev-token-theme (get-in library-data [:token-themes-index token-theme-id])]
|
||||
(-> changes
|
||||
(update :redo-changes conj {:type :del-token-theme :id token-theme-id})
|
||||
(update :undo-changes conj {:type :add-token-theme :token-theme prev-token-theme})
|
||||
(apply-changes-local))))
|
||||
|
||||
(defn add-token-set
|
||||
[changes token-set]
|
||||
(-> changes
|
||||
(update :redo-changes conj {:type :add-token-set :token-set token-set})
|
||||
(update :undo-changes conj {:type :del-token-set :id (:id token-set)})
|
||||
(apply-changes-local)))
|
||||
|
||||
(defn update-token-set
|
||||
[changes token-set prev-token-set]
|
||||
(-> changes
|
||||
(update :redo-changes conj {:type :mod-token-set :id (:id token-set) :token-set token-set})
|
||||
(update :undo-changes conj {:type :mod-token-set :id (:id token-set) :token-set (or prev-token-set token-set)})
|
||||
(apply-changes-local)))
|
||||
|
||||
(defn delete-token-set
|
||||
[changes token-set-id]
|
||||
(assert-library! changes)
|
||||
(let [library-data (::library-data (meta changes))
|
||||
prev-token-set (get-in library-data [:token-sets-index token-set-id])]
|
||||
(-> changes
|
||||
(update :redo-changes conj {:type :del-token-set :id token-set-id})
|
||||
(update :undo-changes conj {:type :add-token-set :token-set prev-token-set})
|
||||
(apply-changes-local))))
|
||||
|
||||
(defn add-token
|
||||
[changes token]
|
||||
(-> changes
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
[app.common.types.plugins :as ctpg]
|
||||
[app.common.types.shape-tree :as ctst]
|
||||
[app.common.types.token :as cto]
|
||||
[app.common.types.token-theme :as ctt]
|
||||
[app.common.types.typographies-list :as ctyl]
|
||||
[app.common.types.typography :as cty]
|
||||
[app.common.uuid :as uuid]
|
||||
|
@ -58,12 +59,26 @@
|
|||
[:vector {:gen/max 3} ::ctc/recent-color]]
|
||||
[:typographies {:optional true}
|
||||
[:map-of {:gen/max 2} ::sm/uuid ::cty/typography]]
|
||||
[:tokens {:optional true}
|
||||
[:map-of {:gen/max 100} ::sm/uuid ::cto/token]]
|
||||
[:media {:optional true}
|
||||
[:map-of {:gen/max 5} ::sm/uuid ::media-object]]
|
||||
[:plugin-data {:optional true}
|
||||
[:map-of {:gen/max 5} :keyword ::ctpg/plugin-data]]])
|
||||
[:map-of {:gen/max 5} :keyword ::ctpg/plugin-data]]
|
||||
[:token-theme-temporary-id {:optional true}
|
||||
::sm/uuid]
|
||||
[:token-active-themes {:optional true :default #{}}
|
||||
[:set ::sm/uuid]]
|
||||
[:token-themes {:optional true}
|
||||
[:vector ::sm/uuid]]
|
||||
[:token-themes-index {:optional true}
|
||||
[:map-of {:gen/max 5} ::sm/uuid ::ctt/token-theme]]
|
||||
[:token-set-groups {:optional true}
|
||||
[:vector ::sm/uuid]]
|
||||
[:token-set-groups-index {:optional true}
|
||||
[:map-of {:gen/max 10} ::sm/uuid ::ctt/token-set-group]]
|
||||
[:token-sets-index {:optional true}
|
||||
[:map-of {:gen/max 10} ::sm/uuid ::ctt/token-set]]
|
||||
[:tokens {:optional true}
|
||||
[:map-of {:gen/max 100} ::sm/uuid ::cto/token]]])
|
||||
|
||||
(def check-file-data!
|
||||
(sm/check-fn ::data))
|
||||
|
|
44
common/src/app/common/types/token_theme.cljc
Normal file
44
common/src/app/common/types/token_theme.cljc
Normal file
|
@ -0,0 +1,44 @@
|
|||
;; 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.common.types.token-theme
|
||||
(:require
|
||||
[app.common.schema :as sm]))
|
||||
|
||||
(sm/register! ::token-theme
|
||||
[:map {:title "TokenTheme"}
|
||||
[:id ::sm/uuid]
|
||||
[:name :string]
|
||||
[:group {:optional true} :string]
|
||||
[:source? {:optional true} :boolean]
|
||||
[:description {:optional true} :string]
|
||||
[:modified-at {:optional true} ::sm/inst]
|
||||
[:sets [:set {:gen/max 10 :gen/min 1} ::sm/uuid]]])
|
||||
|
||||
(sm/register! ::token-set-group-ref
|
||||
[:map
|
||||
[:id ::sm/uuid]
|
||||
[:type [:= :group]]])
|
||||
|
||||
(sm/register! ::token-set-ref
|
||||
[:map
|
||||
[:id ::sm/uuid]
|
||||
[:type [:= :set]]])
|
||||
|
||||
(sm/register! ::token-set-group
|
||||
[:map {:title "TokenSetGroup"}
|
||||
[:id ::sm/uuid]
|
||||
[:name :string]
|
||||
[:items [:vector {:gen/max 10 :gen/min 1}
|
||||
[:or ::token-set-group-ref ::token-set-ref]]]])
|
||||
|
||||
(sm/register! ::token-set
|
||||
[:map {:title "TokenSet"}
|
||||
[:id ::sm/uuid]
|
||||
[:name :string]
|
||||
[:description {:optional true} :string]
|
||||
[:modified-at {:optional true} ::sm/inst]
|
||||
[:tokens [:vector {:gen/max 10 :gen/min 1} ::sm/uuid]]])
|
78
common/src/app/common/types/tokens_theme_list.cljc
Normal file
78
common/src/app/common/types/tokens_theme_list.cljc
Normal file
|
@ -0,0 +1,78 @@
|
|||
;; 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.common.types.tokens-theme-list
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.time :as dt]))
|
||||
|
||||
(defn- touch
|
||||
"Updates the `modified-at` timestamp of a token set."
|
||||
[token-set]
|
||||
(assoc token-set :modified-at (dt/now)))
|
||||
|
||||
(defn assoc-active-token-themes
|
||||
[file-data theme-ids]
|
||||
(assoc file-data :token-active-themes theme-ids))
|
||||
|
||||
(defn add-temporary-token-theme
|
||||
[file-data {:keys [id] :as token-theme}]
|
||||
(-> file-data
|
||||
(d/dissoc-in [:token-themes-index (:token-theme-temporary-id file-data)])
|
||||
(assoc :token-theme-temporary-id id)
|
||||
(update :token-themes-index assoc id token-theme)))
|
||||
|
||||
(defn delete-temporary-token-theme
|
||||
[file-data token-theme-id]
|
||||
(cond-> file-data
|
||||
(= (:token-theme-temporary-id file-data) token-theme-id) (dissoc :token-theme-temporary-id)
|
||||
:always (d/dissoc-in [:token-themes-index (:token-theme-temporary-id file-data)])))
|
||||
|
||||
(defn add-token-theme
|
||||
[file-data {:keys [index id] :as token-theme}]
|
||||
(-> file-data
|
||||
(update :token-themes
|
||||
(fn [token-themes]
|
||||
(let [exists? (some (partial = id) token-themes)]
|
||||
(cond
|
||||
exists? token-themes
|
||||
(nil? index) (conj (or token-themes []) id)
|
||||
:else (d/insert-at-index token-themes index [id])))))
|
||||
(update :token-themes-index assoc id token-theme)))
|
||||
|
||||
(defn update-token-theme
|
||||
[file-data token-theme-id f & args]
|
||||
(d/update-in-when file-data [:token-themes-index token-theme-id] #(-> (apply f % args) (touch))))
|
||||
|
||||
(defn delete-token-theme
|
||||
[file-data theme-id]
|
||||
(-> file-data
|
||||
(update :token-themes (fn [ids] (d/removev #(= % theme-id) ids)))
|
||||
(update :token-themes-index dissoc theme-id)
|
||||
(update :token-active-themes disj theme-id)))
|
||||
|
||||
(defn add-token-set
|
||||
[file-data {:keys [index id] :as token-set}]
|
||||
(-> file-data
|
||||
(update :token-set-groups
|
||||
(fn [token-set-groups]
|
||||
(let [exists? (some (partial = id) token-set-groups)]
|
||||
(cond
|
||||
exists? token-set-groups
|
||||
(nil? index) (conj (or token-set-groups []) id)
|
||||
:else (d/insert-at-index token-set-groups index [id])))))
|
||||
(update :token-sets-index assoc id token-set)))
|
||||
|
||||
(defn update-token-set
|
||||
[file-data token-set-id f & args]
|
||||
(d/update-in-when file-data [:token-sets-index token-set-id] #(-> (apply f % args) (touch))))
|
||||
|
||||
(defn delete-token-set
|
||||
[file-data token-set-id]
|
||||
(-> file-data
|
||||
(update :token-set-groups (fn [xs] (into [] (remove #(= (:id %) token-set-id) xs))))
|
||||
(update :token-sets-index dissoc token-set-id)
|
||||
(update :token-themes-index (fn [xs] (update-vals xs #(update % :sets disj token-set-id))))))
|
|
@ -15,8 +15,9 @@
|
|||
[app.main.data.changes :as dch]
|
||||
[app.main.data.workspace.shapes :as dwsh]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.ui.workspace.tokens.common :refer [workspace-shapes]]
|
||||
[app.main.ui.workspace.tokens.token :as wtt]
|
||||
[app.main.ui.workspace.tokens.token-set :as wtts]
|
||||
[app.main.ui.workspace.tokens.update :as wtu]
|
||||
[beicon.v2.core :as rx]
|
||||
[clojure.data :as data]
|
||||
[cuerdas.core :as str]
|
||||
|
@ -50,12 +51,6 @@
|
|||
(let [[shape-leftover token-leftover _matching] (data/diff (:applied-tokens shape) token)]
|
||||
(merge {} shape-leftover token-leftover)))
|
||||
|
||||
(defn get-shape-from-state [shape-id state]
|
||||
(let [current-page-id (get state :current-page-id)
|
||||
shape (-> (workspace-shapes (:workspace-data state) current-page-id #{shape-id})
|
||||
(first))]
|
||||
shape))
|
||||
|
||||
(defn token-from-attributes [token attributes]
|
||||
(->> (map (fn [attr] [attr (wtt/token-identifier token)]) attributes)
|
||||
(into {})))
|
||||
|
@ -86,19 +81,144 @@
|
|||
(let [workspace-data (deref refs/workspace-data)]
|
||||
(get (:tokens workspace-data) id)))
|
||||
|
||||
(defn set-selected-token-set-id
|
||||
[id]
|
||||
(ptk/reify ::set-selected-token-set-id
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(wtts/assoc-selected-token-set-id state id))))
|
||||
|
||||
(defn create-token-theme [token-theme]
|
||||
(let [new-token-theme (merge
|
||||
{:id (uuid/next)
|
||||
:sets #{}
|
||||
:selected :enabled}
|
||||
token-theme)]
|
||||
(ptk/reify ::create-token-theme
|
||||
ptk/WatchEvent
|
||||
(watch [it _ _]
|
||||
(let [changes (-> (pcb/empty-changes it)
|
||||
(pcb/add-token-theme new-token-theme))]
|
||||
(rx/of
|
||||
(dch/commit-changes changes)))))))
|
||||
|
||||
(defn ensure-token-theme-changes [changes state {:keys [id new-set?]}]
|
||||
(let [theme-id (wtts/update-theme-id state)
|
||||
theme (some-> theme-id (wtts/get-workspace-token-theme state))]
|
||||
(cond
|
||||
(not theme-id) (-> changes
|
||||
(pcb/add-temporary-token-theme
|
||||
{:id (uuid/next)
|
||||
:name ""
|
||||
:sets #{id}}))
|
||||
new-set? (-> changes
|
||||
(pcb/update-token-theme
|
||||
(wtts/add-token-set-to-token-theme id theme)
|
||||
theme))
|
||||
:else changes)))
|
||||
|
||||
(defn toggle-token-theme [token-theme-id]
|
||||
(ptk/reify ::toggle-token-theme
|
||||
ptk/WatchEvent
|
||||
(watch [it state _]
|
||||
(let [themes (wtts/get-active-theme-ids state)
|
||||
new-themes (wtts/toggle-active-theme-id token-theme-id state)
|
||||
changes (-> (pcb/empty-changes it)
|
||||
(pcb/update-active-token-themes new-themes themes))]
|
||||
(rx/of
|
||||
(dch/commit-changes changes)
|
||||
(wtu/update-workspace-tokens))))))
|
||||
|
||||
(defn delete-token-theme [token-theme-id]
|
||||
(ptk/reify ::delete-token-theme
|
||||
ptk/WatchEvent
|
||||
(watch [it state _]
|
||||
(let [data (get state :workspace-data)
|
||||
changes (-> (pcb/empty-changes it)
|
||||
(pcb/with-library-data data)
|
||||
(pcb/delete-token-theme token-theme-id))]
|
||||
(rx/of
|
||||
(dch/commit-changes changes)
|
||||
(wtu/update-workspace-tokens))))))
|
||||
|
||||
(defn create-token-set [token-set]
|
||||
(let [new-token-set (merge
|
||||
{:id (uuid/next)
|
||||
:name "Token Set"
|
||||
:tokens []}
|
||||
token-set)]
|
||||
(ptk/reify ::create-token-set
|
||||
ptk/WatchEvent
|
||||
(watch [it state _]
|
||||
(let [changes (-> (pcb/empty-changes it)
|
||||
(pcb/add-token-set new-token-set)
|
||||
(ensure-token-theme-changes state {:id (:id new-token-set)
|
||||
:new-set? true}))]
|
||||
(rx/of
|
||||
(dch/commit-changes changes)))))))
|
||||
|
||||
(defn toggle-token-set [token-set-id]
|
||||
(ptk/reify ::toggle-token-set
|
||||
ptk/WatchEvent
|
||||
(watch [it state _]
|
||||
(let [theme (some-> (wtts/update-theme-id state)
|
||||
(wtts/get-workspace-token-theme state))
|
||||
changes (-> (pcb/empty-changes it)
|
||||
(pcb/update-token-theme
|
||||
(wtts/toggle-token-set-to-token-theme token-set-id theme)
|
||||
theme)
|
||||
(pcb/update-active-token-themes #{(wtts/update-theme-id state)} (wtts/get-active-theme-ids state)))]
|
||||
(rx/of
|
||||
(dch/commit-changes changes)
|
||||
(wtu/update-workspace-tokens))))))
|
||||
|
||||
(defn delete-token-set [token-set-id]
|
||||
(ptk/reify ::delete-token-set
|
||||
ptk/WatchEvent
|
||||
(watch [it state _]
|
||||
(let [data (get state :workspace-data)
|
||||
changes (-> (pcb/empty-changes it)
|
||||
(pcb/with-library-data data)
|
||||
(pcb/delete-token-set token-set-id))]
|
||||
(rx/of
|
||||
(dch/commit-changes changes)
|
||||
(wtu/update-workspace-tokens))))))
|
||||
|
||||
(defn update-create-token
|
||||
[token]
|
||||
(let [token (update token :id #(or % (uuid/next)))]
|
||||
(ptk/reify ::add-token
|
||||
(ptk/reify ::update-create-token
|
||||
ptk/WatchEvent
|
||||
(watch [it _ _]
|
||||
(watch [it state _]
|
||||
(let [prev-token (get-token-data-from-token-id (:id token))
|
||||
changes (if prev-token
|
||||
(-> (pcb/empty-changes it)
|
||||
(pcb/update-token token prev-token))
|
||||
(-> (pcb/empty-changes it)
|
||||
(pcb/add-token token)))]
|
||||
(rx/of (dch/commit-changes changes)))))))
|
||||
create-token? (not prev-token)
|
||||
token-changes (if create-token?
|
||||
(-> (pcb/empty-changes it)
|
||||
(pcb/add-token token))
|
||||
(-> (pcb/empty-changes it)
|
||||
(pcb/update-token token prev-token)))
|
||||
token-set (wtts/get-selected-token-set state)
|
||||
create-set? (not token-set)
|
||||
new-token-set {:id (uuid/next)
|
||||
:name "Global"
|
||||
:tokens [(:id token)]}
|
||||
selected-token-set-id (if create-set?
|
||||
(:id new-token-set)
|
||||
(:id token-set))
|
||||
set-changes (cond
|
||||
create-set? (-> token-changes
|
||||
(pcb/add-token-set new-token-set))
|
||||
:else (let [updated-token-set (if (contains? token-set (:id token))
|
||||
token-set
|
||||
(update token-set :tokens conj (:id token)))]
|
||||
(-> token-changes
|
||||
(pcb/update-token-set updated-token-set token-set))))
|
||||
theme-changes (-> set-changes
|
||||
(ensure-token-theme-changes state {:new-set? create-set?
|
||||
:id selected-token-set-id}))]
|
||||
(rx/of
|
||||
(set-selected-token-set-id selected-token-set-id)
|
||||
(dch/commit-changes theme-changes)))))))
|
||||
|
||||
(defn delete-token
|
||||
[id]
|
||||
|
@ -119,6 +239,7 @@
|
|||
(update :name #(str/concat % "-copy")))]
|
||||
(update-create-token new-token)))
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; TEMP (Move to test)
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
[app.common.types.shape.layout :as ctl]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.workspace.tokens.token-set :as wtts]
|
||||
[okulary.core :as l]))
|
||||
|
||||
;; ---- Global refs
|
||||
|
@ -233,11 +234,40 @@
|
|||
(def workspace-data
|
||||
(l/derived :workspace-data st/state))
|
||||
|
||||
(def workspace-tokens
|
||||
(l/derived (fn [data]
|
||||
(get data :tokens {}))
|
||||
workspace-data
|
||||
=))
|
||||
(def workspace-selected-token-set-id
|
||||
(l/derived
|
||||
wtts/get-selected-token-set-id
|
||||
st/state
|
||||
=))
|
||||
|
||||
(def workspace-active-theme-ids
|
||||
(l/derived wtts/get-active-theme-ids st/state))
|
||||
|
||||
(def workspace-active-set-ids
|
||||
(l/derived wtts/get-active-set-ids st/state))
|
||||
|
||||
(def workspace-token-themes
|
||||
(l/derived wtts/get-workspace-themes-index st/state))
|
||||
|
||||
(def workspace-ordered-token-themes
|
||||
(l/derived wtts/get-workspace-ordered-themes st/state))
|
||||
|
||||
(def workspace-token-sets
|
||||
(l/derived
|
||||
(fn [data]
|
||||
(or (wtts/get-workspace-sets data) {}))
|
||||
st/state
|
||||
=))
|
||||
|
||||
(def workspace-active-theme-sets-tokens
|
||||
(l/derived wtts/get-active-theme-sets-tokens-names-map st/state =))
|
||||
|
||||
(def workspace-selected-token-set-tokens
|
||||
(l/derived
|
||||
(fn [data]
|
||||
(or (wtts/get-selected-token-set-tokens data) {}))
|
||||
st/state
|
||||
=))
|
||||
|
||||
(def workspace-file-colors
|
||||
(l/derived (fn [data]
|
||||
|
|
|
@ -856,7 +856,7 @@
|
|||
|
||||
shape (when-not multiple
|
||||
(first (deref (refs/objects-by-id ids))))
|
||||
tokens (mf/deref refs/workspace-tokens)
|
||||
tokens (mf/deref refs/workspace-selected-token-set-tokens)
|
||||
spacing-tokens (mf/use-memo (mf/deps tokens) #(:spacing (wtc/group-tokens-by-type tokens)))
|
||||
|
||||
spacing-column-options (mf/use-memo
|
||||
|
|
|
@ -101,7 +101,7 @@
|
|||
selection-parents-ref (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids))
|
||||
selection-parents (mf/deref selection-parents-ref)
|
||||
|
||||
tokens (-> (mf/deref refs/workspace-tokens)
|
||||
tokens (-> (mf/deref refs/workspace-active-theme-sets-tokens)
|
||||
(sd/use-resolved-tokens))
|
||||
tokens-by-type (mf/use-memo (mf/deps tokens) #(wtc/group-tokens-by-type tokens))
|
||||
|
||||
|
|
|
@ -311,7 +311,7 @@
|
|||
selected (mf/deref refs/selected-shapes)
|
||||
selected-shapes (into [] (keep (d/getf objects)) selected)
|
||||
token-id (:token-id mdata)
|
||||
token (get (mf/deref refs/workspace-tokens) token-id)]
|
||||
token (get (mf/deref refs/workspace-selected-token-set-tokens) token-id)]
|
||||
(mf/use-effect
|
||||
(mf/deps mdata)
|
||||
(fn []
|
||||
|
|
|
@ -58,6 +58,6 @@
|
|||
{:global global}))
|
||||
|
||||
(defn download-tokens-as-json []
|
||||
(let [all-tokens (deref refs/workspace-tokens)
|
||||
(let [all-tokens (deref refs/workspace-selected-token-set-tokens)
|
||||
transformed-tokens-json (transform-tokens-into-json-format all-tokens)]
|
||||
(export-tokens-file transformed-tokens-json)))
|
||||
|
|
|
@ -99,10 +99,10 @@ Token names should only contain letters and digits separated by . characters.")}
|
|||
empty-input? (p/rejected nil)
|
||||
direct-self-reference? (p/rejected :error/token-direct-self-reference)
|
||||
:else (let [token-id (or (:id token) (random-uuid))
|
||||
new-tokens (update tokens token-id merge {:id token-id
|
||||
:value input
|
||||
:name token-name})]
|
||||
(-> (sd/resolve-tokens+ new-tokens #_ {:debug? true})
|
||||
new-tokens (update tokens token-name merge {:id token-id
|
||||
:value input
|
||||
:name token-name})]
|
||||
(-> (sd/resolve-tokens+ new-tokens {:names-map? true})
|
||||
(p/then
|
||||
(fn [resolved-tokens]
|
||||
(let [{:keys [errors resolved-value] :as resolved-token} (get resolved-tokens token-name)]
|
||||
|
@ -139,29 +139,33 @@ Token names should only contain letters and digits separated by . characters.")}
|
|||
timeout))))]
|
||||
debounced-resolver-callback))
|
||||
|
||||
(defonce form-token-cache-atom (atom nil))
|
||||
|
||||
(mf/defc form
|
||||
{::mf/wrap-props false}
|
||||
[{:keys [token token-type] :as _args}]
|
||||
(let [tokens (mf/deref refs/workspace-tokens)
|
||||
resolved-tokens (sd/use-resolved-tokens tokens)
|
||||
(let [selected-set-tokens (mf/deref refs/workspace-selected-token-set-tokens)
|
||||
active-theme-tokens (mf/deref refs/workspace-active-theme-sets-tokens)
|
||||
resolved-tokens (sd/use-resolved-tokens active-theme-tokens {:names-map? true
|
||||
:cache-atom form-token-cache-atom})
|
||||
token-path (mf/use-memo
|
||||
(mf/deps (:name token))
|
||||
#(wtt/token-name->path (:name token)))
|
||||
tokens-tree (mf/use-memo
|
||||
(mf/deps token-path resolved-tokens)
|
||||
(fn []
|
||||
(-> (wtt/token-names-tree resolved-tokens)
|
||||
;; Allow setting editing token to it's own path
|
||||
(d/dissoc-in token-path))))
|
||||
selected-set-tokens-tree (mf/use-memo
|
||||
(mf/deps token-path selected-set-tokens)
|
||||
(fn []
|
||||
(-> (wtt/token-names-tree selected-set-tokens)
|
||||
;; Allow setting editing token to it's own path
|
||||
(d/dissoc-in token-path))))
|
||||
|
||||
;; Name
|
||||
name-ref (mf/use-var (:name token))
|
||||
name-errors (mf/use-state nil)
|
||||
validate-name (mf/use-callback
|
||||
(mf/deps tokens-tree)
|
||||
(mf/deps selected-set-tokens-tree)
|
||||
(fn [value]
|
||||
(let [schema (token-name-schema {:token token
|
||||
:tokens-tree tokens-tree})]
|
||||
:tokens-tree selected-set-tokens-tree})]
|
||||
(m/explain schema (finalize-name value)))))
|
||||
on-update-name-debounced (mf/use-callback
|
||||
(debounce (fn [e]
|
||||
|
@ -187,7 +191,7 @@ Token names should only contain letters and digits separated by . characters.")}
|
|||
(= token-or-err :error/token-missing-reference) token-or-err
|
||||
(:resolved-value token-or-err) (:resolved-value token-or-err))]
|
||||
(reset! token-resolve-result v))))
|
||||
on-update-value-debounced (use-debonced-resolve-callback name-ref token tokens set-resolve-value)
|
||||
on-update-value-debounced (use-debonced-resolve-callback name-ref token active-theme-tokens set-resolve-value)
|
||||
on-update-value (mf/use-callback
|
||||
(mf/deps on-update-value-debounced)
|
||||
(fn [e]
|
||||
|
|
|
@ -7,95 +7,81 @@
|
|||
(ns app.main.ui.workspace.tokens.sets
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.data.macros :as dm]
|
||||
[app.main.data.tokens :as wdt]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.util.dom :as dom]
|
||||
[okulary.core :as l]
|
||||
[potok.v2.core :as ptk]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(def active-sets #{#uuid "2858b330-828e-4131-86ed-e4d1c0f4b3e3"
|
||||
#uuid "d608877b-842a-473b-83ca-b5f8305caf83"})
|
||||
|
||||
(def sets-root-order [#uuid "2858b330-828e-4131-86ed-e4d1c0f4b3e3"
|
||||
#uuid "9c5108aa-bdb4-409c-a3c8-c3dfce2f8bf8"
|
||||
#uuid "0381446e-1f1d-423f-912c-ab577d61b79b"])
|
||||
|
||||
(def sets {#uuid "9c5108aa-bdb4-409c-a3c8-c3dfce2f8bf8" {:type :group
|
||||
:name "Group A"
|
||||
:children [#uuid "d1754e56-3510-493f-8287-5ef3417d4141"
|
||||
#uuid "d608877b-842a-473b-83ca-b5f8305caf83"]}
|
||||
#uuid "d608877b-842a-473b-83ca-b5f8305caf83" {:type :set
|
||||
:name "Set A / 1"}
|
||||
#uuid "d1754e56-3510-493f-8287-5ef3417d4141" {:type :group
|
||||
:name "Group A / B"
|
||||
:children [#uuid "f608877b-842a-473b-83ca-b5f8305caf83"
|
||||
#uuid "7cc05389-9391-426e-bc0e-ba5cb8f425eb"]}
|
||||
#uuid "f608877b-842a-473b-83ca-b5f8305caf83" {:type :set
|
||||
:name "Set A / B / 1"}
|
||||
#uuid "7cc05389-9391-426e-bc0e-ba5cb8f425eb" {:type :set
|
||||
:name "Set A / B / 2"}
|
||||
#uuid "2858b330-828e-4131-86ed-e4d1c0f4b3e3" {:type :set
|
||||
:name "Set Root 1"}
|
||||
#uuid "0381446e-1f1d-423f-912c-ab577d61b79b" {:type :set
|
||||
:name "Set Root 2"}})
|
||||
|
||||
(def ^:private chevron-icon
|
||||
(i/icon-xref :arrow (stl/css :chevron-icon)))
|
||||
|
||||
(defn set-selected-set
|
||||
[set-id]
|
||||
(dm/assert! (uuid? set-id))
|
||||
(ptk/reify ::set-selected-set
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(assoc state :selected-set-id set-id))))
|
||||
(defn on-toggle-token-set-click [id event]
|
||||
(dom/stop-propagation event)
|
||||
(st/emit! (wdt/toggle-token-set id)))
|
||||
|
||||
(defn on-select-token-set-click [id event]
|
||||
(st/emit! (wdt/set-selected-token-set-id id)))
|
||||
|
||||
(defn on-delete-token-set-click [id event]
|
||||
(dom/stop-propagation event)
|
||||
(st/emit! (wdt/delete-token-set id)))
|
||||
|
||||
(mf/defc sets-tree
|
||||
[{:keys [selected-set-id set-id]}]
|
||||
(let [set (get sets set-id)]
|
||||
(when set
|
||||
(let [{:keys [type name children]} set
|
||||
visible? (mf/use-state (contains? active-sets set-id))
|
||||
collapsed? (mf/use-state false)
|
||||
icon (if (= type :set) i/document i/group)
|
||||
selected? (mf/use-state (= set-id selected-set-id))
|
||||
|
||||
on-click
|
||||
(mf/use-fn
|
||||
(mf/deps type set-id)
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(st/emit! (set-selected-set set-id))))]
|
||||
[:div {:class (stl/css :set-item-container)
|
||||
:on-click on-click}
|
||||
[:div {:class (stl/css-case :set-item-group (= type :group)
|
||||
:set-item-set (= type :set)
|
||||
:selected-set (and (= type :set) @selected?))}
|
||||
(when (= type :group)
|
||||
[:span {:class (stl/css-case
|
||||
:collapsabled-icon true
|
||||
:collapsed @collapsed?)
|
||||
:on-click #(when (= type :group) (swap! collapsed? not))}
|
||||
chevron-icon])
|
||||
[:span {:class (stl/css :icon)} icon]
|
||||
[:div {:class (stl/css :set-name)} name]
|
||||
(when (= type :set)
|
||||
[:span {:class (stl/css :action-btn)
|
||||
:on-click #(swap! visible? not)}
|
||||
(if @visible?
|
||||
i/shown
|
||||
i/hide)])]
|
||||
(when (and children (not @collapsed?))
|
||||
[:div {:class (stl/css :set-children)}
|
||||
(for [child-id children]
|
||||
[:& sets-tree {:key child-id :set-id child-id :selected-set-id selected-set-id}])])]))))
|
||||
[{:keys [token-set token-set-active? token-set-selected?] :as _props}]
|
||||
(let [{:keys [id name _children]} token-set
|
||||
selected? (and set? (token-set-selected? id))
|
||||
visible? (token-set-active? id)
|
||||
collapsed? (mf/use-state false)
|
||||
set? true #_(= type :set)
|
||||
group? false #_(= type :group)]
|
||||
[:div {:class (stl/css :set-item-container)
|
||||
:on-click #(on-select-token-set-click id %)}
|
||||
[:div {:class (stl/css-case :set-item-group group?
|
||||
:set-item-set set?
|
||||
:selected-set selected?)}
|
||||
(when group?
|
||||
[:span {:class (stl/css-case :collapsabled-icon true
|
||||
:collapsed @collapsed?)
|
||||
:on-click #(swap! collapsed? not)}
|
||||
chevron-icon])
|
||||
[:span {:class (stl/css :icon)}
|
||||
(if set? i/document i/group)]
|
||||
[:div {:class (stl/css :set-name)} name]
|
||||
[:div {:class (stl/css :delete-set)}
|
||||
[:button {:on-click #(on-delete-token-set-click id %)}
|
||||
i/delete]]
|
||||
(when set?
|
||||
[:span {:class (stl/css :action-btn)
|
||||
:on-click #(on-toggle-token-set-click id %)}
|
||||
(if visible? i/shown i/hide)])]
|
||||
#_(when (and children (not @collapsed?))
|
||||
[:div {:class (stl/css :set-children)}
|
||||
(for [child-id children]
|
||||
[:& sets-tree (assoc props :key child-id
|
||||
{:key child-id}
|
||||
:set-id child-id
|
||||
:selected-set-id selected-token-set-id)])])]))
|
||||
|
||||
(mf/defc sets-list
|
||||
[{:keys [selected-set-id]}]
|
||||
[:ul {:class (stl/css :sets-list)}
|
||||
(for [set-id sets-root-order]
|
||||
[:& sets-tree {:key set-id
|
||||
:set-id set-id
|
||||
:selected-set-id selected-set-id}])])
|
||||
[{:keys []}]
|
||||
(let [token-sets (mf/deref refs/workspace-token-sets)
|
||||
selected-token-set-id (mf/deref refs/workspace-selected-token-set-id)
|
||||
token-set-selected? (mf/use-callback
|
||||
(mf/deps selected-token-set-id)
|
||||
(fn [id]
|
||||
(= id selected-token-set-id)))
|
||||
active-token-set-ids (mf/deref refs/workspace-active-set-ids)
|
||||
token-set-active? (mf/use-callback
|
||||
(mf/deps active-token-set-ids)
|
||||
(fn [id]
|
||||
(get active-token-set-ids id)))]
|
||||
[:ul {:class (stl/css :sets-list)}
|
||||
(for [[id token-set] token-sets]
|
||||
[:& sets-tree
|
||||
{:key id
|
||||
:token-set token-set
|
||||
:selected-token-set-id selected-token-set-id
|
||||
:token-set-selected? token-set-selected?
|
||||
:token-set-active? token-set-active?}])]))
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
.sets-list {
|
||||
width: 100%;
|
||||
margin-bottom: $s-12;
|
||||
margin-bottom: 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,8 @@
|
|||
width: 100%;
|
||||
cursor: pointer;
|
||||
color: var(--layer-row-foreground-color);
|
||||
padding-right: $s-2;
|
||||
|
||||
.set-name {
|
||||
@include textEllipsis;
|
||||
flex-grow: 1;
|
||||
|
@ -56,6 +58,10 @@
|
|||
background-color: var(--layer-row-background-color-hover);
|
||||
color: var(--layer-row-foreground-color-hover);
|
||||
box-shadow: -100px 0 0 0 var(--layer-row-background-color-hover);
|
||||
|
||||
.delete-set {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,6 +71,27 @@
|
|||
box-shadow: -100px 0 0 0 var(--layer-row-background-color-selected);
|
||||
}
|
||||
|
||||
.delete-set {
|
||||
@extend .button-tertiary;
|
||||
height: $s-28;
|
||||
width: $s-28;
|
||||
visibility: hidden;
|
||||
button {
|
||||
@include buttonStyle;
|
||||
@include flexCenter;
|
||||
width: $s-24;
|
||||
height: 100%;
|
||||
svg {
|
||||
@extend .button-icon-small;
|
||||
height: $s-12;
|
||||
width: $s-12;
|
||||
color: transparent;
|
||||
fill: none;
|
||||
stroke: var(--icon-foreground);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
@extend .button-tertiary;
|
||||
height: $s-28;
|
||||
|
|
|
@ -10,12 +10,14 @@
|
|||
[app.common.data :as d]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.tokens :as dt]
|
||||
[app.main.data.tokens :as wdt]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.title-bar :refer [title-bar]]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.main.ui.workspace.sidebar.assets.common :as cmm]
|
||||
[app.main.ui.workspace.tokens.changes :as wtch]
|
||||
[app.main.ui.workspace.tokens.common :refer [labeled-input]]
|
||||
[app.main.ui.workspace.tokens.context-menu :refer [token-context-menu]]
|
||||
[app.main.ui.workspace.tokens.core :as wtc]
|
||||
[app.main.ui.workspace.tokens.sets :refer [sets-list]]
|
||||
|
@ -23,6 +25,7 @@
|
|||
[app.main.ui.workspace.tokens.token :as wtt]
|
||||
[app.main.ui.workspace.tokens.token-types :as wtty]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.storage :refer [storage]]
|
||||
[cuerdas.core :as str]
|
||||
[okulary.core :as l]
|
||||
[rumext.v2 :as mf]
|
||||
|
@ -37,11 +40,19 @@
|
|||
(def selected-set-id
|
||||
(l/derived :selected-set-id st/state))
|
||||
|
||||
;; Event Functions -------------------------------------------------------------
|
||||
|
||||
(defn on-set-add-click [_event]
|
||||
(when-let [set-name (js/window.prompt "Set name")]
|
||||
(st/emit! (wdt/create-token-set {:name set-name}))))
|
||||
|
||||
;; Components ------------------------------------------------------------------
|
||||
|
||||
(mf/defc token-pill
|
||||
{::mf/wrap-props false}
|
||||
[{:keys [on-click token highlighted? on-context-menu]}]
|
||||
[{:keys [on-click token theme-token highlighted? on-context-menu] :as props}]
|
||||
(let [{:keys [name value resolved-value errors]} token
|
||||
errors? (seq errors)]
|
||||
errors? (and (seq errors) (seq (:errors theme-token)))]
|
||||
[:button {:class (stl/css-case :token-pill true
|
||||
:token-pill-highlighted highlighted?
|
||||
:token-pill-invalid errors?)
|
||||
|
@ -75,7 +86,7 @@
|
|||
i/add))
|
||||
|
||||
(mf/defc token-component
|
||||
[{:keys [type tokens selected-shapes token-type-props]}]
|
||||
[{:keys [type tokens selected-shapes token-type-props active-theme-tokens]}]
|
||||
(let [open? (mf/deref (-> (l/key type)
|
||||
(l/derived lens:token-type-open-status)))
|
||||
{:keys [modal attributes all-attributes title]} token-type-props
|
||||
|
@ -95,6 +106,7 @@
|
|||
(fn [event]
|
||||
(let [{:keys [key fields]} modal]
|
||||
(dom/stop-propagation event)
|
||||
(st/emit! (dt/set-token-type-section-open type true))
|
||||
(modal/show! key {:x (.-clientX ^js event)
|
||||
:y (.-clientY ^js event)
|
||||
:position :right
|
||||
|
@ -115,7 +127,6 @@
|
|||
[:& cmm/asset-section {:icon (mf/fnc icon-wrapper [_]
|
||||
[:div {:class (stl/css :section-icon)}
|
||||
[:& token-section-icon {:type type}]])
|
||||
|
||||
:title title
|
||||
:assets-count tokens-count
|
||||
:open? open?}
|
||||
|
@ -127,12 +138,14 @@
|
|||
[:& cmm/asset-section-block {:role :content}
|
||||
[:div {:class (stl/css :token-pills-wrapper)}
|
||||
(for [token (sort-by :modified-at tokens)]
|
||||
[:& token-pill
|
||||
{:key (:id token)
|
||||
:token token
|
||||
:highlighted? (wtt/shapes-token-applied? token selected-shapes (or all-attributes attributes))
|
||||
:on-click #(on-token-pill-click % token)
|
||||
:on-context-menu #(on-context-menu % token)}])]])]]))
|
||||
(let [theme-token (get active-theme-tokens (wtt/token-identifier token))]
|
||||
[:& token-pill
|
||||
{:key (:id token)
|
||||
:token token
|
||||
:theme-token theme-token
|
||||
:highlighted? (wtt/shapes-token-applied? token selected-shapes (or all-attributes attributes))
|
||||
:on-click #(on-token-pill-click % token)
|
||||
:on-context-menu #(on-context-menu % token)}]))]])]]))
|
||||
|
||||
(defn sorted-token-groups
|
||||
"Separate token-types into groups of `:empty` or `:filled` depending if tokens exist for that type.
|
||||
|
@ -149,32 +162,77 @@
|
|||
{:empty (sort-by :token-key empty)
|
||||
:filled (sort-by :token-key filled)}))
|
||||
|
||||
(mf/defc tokens-explorer
|
||||
(mf/defc tokene-theme-create
|
||||
[_props]
|
||||
(let [objects (mf/deref refs/workspace-page-objects)
|
||||
(let [group (mf/use-state "")
|
||||
name (mf/use-state "")]
|
||||
[:div {:style {:display "flex"
|
||||
:flex-direction "column"
|
||||
:gap "10px"}}
|
||||
[:& labeled-input {:label "Group name"
|
||||
:input-props {:value @group
|
||||
:on-change #(reset! group (dom/event->value %))}}]
|
||||
[:& labeled-input {:label "Theme name"
|
||||
:input-props {:value @name
|
||||
:on-change #(reset! name (dom/event->value %))}}]
|
||||
[:button {:on-click #(st/emit! (wdt/create-token-theme {:group @group
|
||||
:name @name}))}
|
||||
"Create"]]))
|
||||
|
||||
selected (mf/deref refs/selected-shapes)
|
||||
selected-shapes (into [] (keep (d/getf objects)) selected)
|
||||
|
||||
tokens (-> (mf/deref refs/workspace-tokens)
|
||||
(sd/use-resolved-tokens))
|
||||
token-groups (mf/with-memo [tokens]
|
||||
(sorted-token-groups tokens))]
|
||||
[:article
|
||||
[:& token-context-menu]
|
||||
[:div.assets-bar
|
||||
(for [{:keys [token-key token-type-props tokens]} (concat (:filled token-groups)
|
||||
(:empty token-groups))]
|
||||
[:& token-component {:key token-key
|
||||
:type token-key
|
||||
:selected-shapes selected-shapes
|
||||
:tokens tokens
|
||||
:token-type-props token-type-props}])]]))
|
||||
(mf/defc themes-sidebar
|
||||
[_props]
|
||||
(let [open? (mf/use-state true)
|
||||
active-theme-ids (mf/deref refs/workspace-active-theme-ids)
|
||||
themes (mf/deref refs/workspace-ordered-token-themes)]
|
||||
[:div {:class (stl/css :sets-sidebar)}
|
||||
[:div {:class (stl/css :sidebar-header)}
|
||||
[:& title-bar {:collapsable true
|
||||
:collapsed (not @open?)
|
||||
:all-clickable true
|
||||
:title "THEMES"
|
||||
:on-collapsed #(swap! open? not)}]]
|
||||
(when @open?
|
||||
[:div
|
||||
[:style
|
||||
(str "@scope {"
|
||||
(str/join "\n"
|
||||
["ul { list-style-type: circle; margin-left: 20px; }"
|
||||
".spaced { display: flex; gap: 10px; justify-content: space-between; }"
|
||||
".spaced-y { display: flex; flex-direction: column; gap: 10px }"
|
||||
".selected { font-weight: 600; }"
|
||||
"b { font-weight: 600; }"])
|
||||
"}")]
|
||||
[:div.spaced-y
|
||||
{:style {:padding "10px"}}
|
||||
[:& tokene-theme-create]
|
||||
[:div.spaced-y
|
||||
[:b "Themes"]
|
||||
[:ul
|
||||
(for [[group themes] themes]
|
||||
[:li
|
||||
{:key (str "token-theme-group" group)}
|
||||
group
|
||||
[:ul
|
||||
(for [{:keys [id name] :as _theme} themes]
|
||||
[:li {:key (str "tokene-theme-" id)}
|
||||
[:div.spaced
|
||||
name
|
||||
[:div.spaced
|
||||
[:button
|
||||
{:on-click (fn [e]
|
||||
(dom/prevent-default e)
|
||||
(dom/stop-propagation e)
|
||||
(st/emit! (wdt/toggle-token-theme id)))}
|
||||
(if (get active-theme-ids id) "✅" "❎")]
|
||||
[:button {:on-click (fn [e]
|
||||
(dom/prevent-default e)
|
||||
(dom/stop-propagation e)
|
||||
(st/emit! (wdt/delete-token-theme id)))}
|
||||
"🗑️"]]]])]])]]]])]))
|
||||
|
||||
(mf/defc sets-sidebar
|
||||
[]
|
||||
(let [selected-set-id (mf/deref selected-set-id)
|
||||
open? (mf/use-state true)]
|
||||
(let [open? (mf/use-state true)]
|
||||
[:div {:class (stl/css :sets-sidebar)}
|
||||
[:div {:class (stl/css :sidebar-header)}
|
||||
[:& title-bar {:collapsable true
|
||||
|
@ -183,19 +241,76 @@
|
|||
:title "SETS"
|
||||
:on-collapsed #(swap! open? not)}]
|
||||
[:button {:class (stl/css :add-set)
|
||||
:on-click #(println "Add Set")}
|
||||
:on-click #(do
|
||||
(reset! open? true)
|
||||
(on-set-add-click %))}
|
||||
i/add]]
|
||||
(when @open?
|
||||
[:& sets-list {:selected-set-id selected-set-id}])]))
|
||||
[:& sets-list])]))
|
||||
|
||||
(mf/defc tokens-explorer
|
||||
[_props]
|
||||
(let [open? (mf/use-state true)
|
||||
objects (mf/deref refs/workspace-page-objects)
|
||||
|
||||
selected (mf/deref refs/selected-shapes)
|
||||
selected-shapes (into [] (keep (d/getf objects)) selected)
|
||||
|
||||
|
||||
active-theme-tokens (sd/use-active-theme-sets-tokens)
|
||||
|
||||
tokens (sd/use-resolved-workspace-tokens)
|
||||
token-groups (mf/with-memo [tokens]
|
||||
(sorted-token-groups tokens))]
|
||||
[:article
|
||||
[:& token-context-menu]
|
||||
[:& title-bar {:collapsable true
|
||||
:collapsed (not @open?)
|
||||
:all-clickable true
|
||||
:title "TOKENS"
|
||||
:on-collapsed #(swap! open? not)}]
|
||||
(when @open?
|
||||
[:div.assets-bar
|
||||
(for [{:keys [token-key token-type-props tokens]} (concat (:filled token-groups)
|
||||
(:empty token-groups))]
|
||||
[:& token-component {:key token-key
|
||||
:type token-key
|
||||
:selected-shapes selected-shapes
|
||||
:active-theme-tokens active-theme-tokens
|
||||
:tokens tokens
|
||||
:token-type-props token-type-props}])])]))
|
||||
|
||||
(defn dev-or-preview-url? [url]
|
||||
(let [host (-> url js/URL. .-host)
|
||||
localhost? (= "localhost" (first (str/split host #":")))
|
||||
pr? (str/ends-with? host "penpot.alpha.tokens.studio")]
|
||||
(or localhost? pr?)))
|
||||
|
||||
(defn location-url-dev-or-preview-url!? []
|
||||
(dev-or-preview-url? js/window.location.href))
|
||||
|
||||
(defn temp-use-themes-flag []
|
||||
(let [show? (mf/use-state (or
|
||||
(location-url-dev-or-preview-url!?)
|
||||
(get @storage ::show-token-themes-sets?)
|
||||
false))]
|
||||
(mf/use-effect
|
||||
(fn []
|
||||
(letfn [(toggle! []
|
||||
(swap! storage update ::show-token-themes-sets? not)
|
||||
(reset! show? (get @storage ::show-token-themes-sets?)))]
|
||||
(set! js/window.toggleThemes toggle!))))
|
||||
show?))
|
||||
|
||||
(mf/defc tokens-sidebar-tab
|
||||
{::mf/wrap [mf/memo]
|
||||
::mf/wrap-props false}
|
||||
[_props]
|
||||
(let [show-sets-section? false] ;; temporarily added this variable to see/hide the sets section till we have it working end to end
|
||||
(let [show-sets-section? (deref (temp-use-themes-flag))]
|
||||
[:div {:class (stl/css :sidebar-tab-wrapper)}
|
||||
(when show-sets-section?
|
||||
[:div {:class (stl/css :sets-section-wrapper)}
|
||||
[:& themes-sidebar]
|
||||
[:& sets-sidebar]])
|
||||
[:div {:class (stl/css :tokens-section-wrapper)}
|
||||
[:& tokens-explorer]]
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
(:require
|
||||
["@tokens-studio/sd-transforms" :as sd-transforms]
|
||||
["style-dictionary$default" :as sd]
|
||||
[app.common.data :refer [ordered-map]]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.ui.workspace.tokens.token :as wtt]
|
||||
[cuerdas.core :as str]
|
||||
|
@ -73,13 +74,15 @@
|
|||
(get errors :style-dictionary/missing-reference)))
|
||||
|
||||
(defn resolve-tokens+
|
||||
[tokens & {:keys [debug?] :as config}]
|
||||
[tokens & {:keys [names-map? debug?] :as config}]
|
||||
(p/let [sd-tokens (-> (wtt/token-names-tree tokens)
|
||||
(resolve-sd-tokens+ config))]
|
||||
(let [resolved-tokens (reduce
|
||||
(fn [acc ^js cur]
|
||||
(let [id (uuid (.-uuid (.-id cur)))
|
||||
origin-token (get tokens id)
|
||||
(let [identifier (if names-map?
|
||||
(.. cur -original -name)
|
||||
(uuid (.-uuid (.-id cur))))
|
||||
origin-token (get tokens identifier)
|
||||
parsed-value (wtt/parse-token-value (.-value cur))
|
||||
resolved-token (if (not parsed-value)
|
||||
(assoc origin-token :errors [:style-dictionary/missing-reference])
|
||||
|
@ -92,15 +95,12 @@
|
|||
(js/console.log "Resolved tokens" resolved-tokens))
|
||||
resolved-tokens)))
|
||||
|
||||
(defn resolve-workspace-tokens+
|
||||
[& {:keys [debug?] :as config}]
|
||||
(when-let [workspace-tokens @refs/workspace-tokens]
|
||||
(resolve-tokens+ workspace-tokens)))
|
||||
|
||||
;; Hooks -----------------------------------------------------------------------
|
||||
|
||||
(defonce !tokens-cache (atom nil))
|
||||
|
||||
(defonce !theme-tokens-cache (atom nil))
|
||||
|
||||
(defn get-cached-tokens [tokens]
|
||||
(get @!tokens-cache tokens tokens))
|
||||
|
||||
|
@ -109,11 +109,12 @@
|
|||
|
||||
This hook will return the unresolved tokens as state until they are processed,
|
||||
then the state will be updated with the resolved tokens."
|
||||
[tokens & {:keys [cache-atom]
|
||||
:or {cache-atom !tokens-cache}}]
|
||||
(let [tokens-state (mf/use-state (get @cache-atom tokens tokens))]
|
||||
[tokens & {:keys [cache-atom _names-map?]
|
||||
:or {cache-atom !tokens-cache}
|
||||
:as config}]
|
||||
(let [tokens-state (mf/use-state (get @cache-atom tokens))]
|
||||
(mf/use-effect
|
||||
(mf/deps tokens)
|
||||
(mf/deps tokens config)
|
||||
(fn []
|
||||
(let [cached (get @cache-atom tokens)]
|
||||
(cond
|
||||
|
@ -122,7 +123,7 @@
|
|||
;; Get the cached entry
|
||||
(some? cached) (reset! tokens-state cached)
|
||||
;; No cached entry, start processing
|
||||
:else (let [promise+ (resolve-tokens+ tokens)]
|
||||
:else (let [promise+ (resolve-tokens+ tokens config)]
|
||||
(swap! cache-atom assoc tokens promise+)
|
||||
(p/then promise+ (fn [resolved-tokens]
|
||||
(swap! cache-atom assoc tokens resolved-tokens)
|
||||
|
@ -130,39 +131,11 @@
|
|||
@tokens-state))
|
||||
|
||||
(defn use-resolved-workspace-tokens [& {:as config}]
|
||||
(-> (mf/deref refs/workspace-tokens)
|
||||
(-> (mf/deref refs/workspace-selected-token-set-tokens)
|
||||
(use-resolved-tokens config)))
|
||||
|
||||
;; Testing ---------------------------------------------------------------------
|
||||
|
||||
(comment
|
||||
(defonce !output (atom nil))
|
||||
|
||||
(-> @refs/workspace-tokens
|
||||
(resolve-tokens+ {:debug? false})
|
||||
(.then js/console.log))
|
||||
|
||||
(-> (resolve-workspace-tokens+ {:debug? true})
|
||||
(p/then #(reset! !output %)))
|
||||
|
||||
@!output
|
||||
|
||||
(->> @refs/workspace-tokens
|
||||
(resolve-tokens+)
|
||||
(#(doto % js/console.log)))
|
||||
|
||||
(->
|
||||
(clj->js {"a" {:name "a" :value "5"}
|
||||
"b" {:name "b" :value "{a} * 2"}})
|
||||
|
||||
(#(resolve-sd-tokens+ % {:debug? true})))
|
||||
|
||||
(defonce output (atom nil))
|
||||
(require '[shadow.resource])
|
||||
(let [example (-> (shadow.resource/inline "./data/example-tokens-set.json")
|
||||
(js/JSON.parse)
|
||||
.-core)]
|
||||
(.then (resolve-sd-tokens+ example {:debug? true})
|
||||
#(reset! output %)))
|
||||
|
||||
nil)
|
||||
(defn use-active-theme-sets-tokens [& {:as config}]
|
||||
(-> (mf/deref refs/workspace-active-theme-sets-tokens)
|
||||
(use-resolved-tokens (merge {:cache-atom !theme-tokens-cache
|
||||
:names-map? true}
|
||||
config))))
|
||||
|
|
|
@ -4,6 +4,14 @@
|
|||
[clojure.set :as set]
|
||||
[cuerdas.core :as str]))
|
||||
|
||||
(defn get-workspace-tokens
|
||||
[state]
|
||||
(get-in state [:workspace-data :tokens] {}))
|
||||
|
||||
(defn get-workspace-token
|
||||
[token-id state]
|
||||
(get-in state [:workspace-data :tokens token-id]))
|
||||
|
||||
(def parseable-token-value-regexp
|
||||
"Regexp that can be used to parse a number value out of resolved token value.
|
||||
This regexp also trims whitespace around the value."
|
||||
|
|
150
frontend/src/app/main/ui/workspace/tokens/token_set.cljs
Normal file
150
frontend/src/app/main/ui/workspace/tokens/token_set.cljs
Normal file
|
@ -0,0 +1,150 @@
|
|||
(ns app.main.ui.workspace.tokens.token-set
|
||||
(:require
|
||||
[app.common.data :refer [ordered-map]]
|
||||
[app.main.ui.workspace.tokens.token :as wtt]
|
||||
[clojure.set :as set]))
|
||||
|
||||
;; Themes ----------------------------------------------------------------------
|
||||
|
||||
(defn get-theme-group [theme]
|
||||
(:group theme))
|
||||
|
||||
(defn get-workspace-themes [state]
|
||||
(get-in state [:workspace-data :token-themes] []))
|
||||
|
||||
(defn get-workspace-themes-index [state]
|
||||
(get-in state [:workspace-data :token-themes-index] {}))
|
||||
|
||||
(defn get-workspace-token-set-groups [state]
|
||||
(get-in state [:workspace-data :token-set-groups]))
|
||||
|
||||
(defn get-workspace-ordered-themes [state]
|
||||
(let [themes (get-workspace-themes state)
|
||||
themes-index (get-workspace-themes-index state)]
|
||||
(->> (map #(get themes-index %) themes)
|
||||
(group-by :group))))
|
||||
|
||||
(defn get-active-theme-ids [state]
|
||||
(get-in state [:workspace-data :token-active-themes] #{}))
|
||||
|
||||
(defn get-temp-theme-id [state]
|
||||
(get-in state [:workspace-data :token-theme-temporary-id]))
|
||||
|
||||
(defn get-active-theme-ids-or-fallback [state]
|
||||
(let [active-theme-ids (get-active-theme-ids state)
|
||||
temp-theme-id (get-temp-theme-id state)]
|
||||
(cond
|
||||
(seq active-theme-ids) active-theme-ids
|
||||
temp-theme-id #{temp-theme-id})))
|
||||
|
||||
(defn get-active-set-ids [state]
|
||||
(let [active-theme-ids (get-active-theme-ids-or-fallback state)
|
||||
themes-index (get-workspace-themes-index state)
|
||||
active-set-ids (reduce
|
||||
(fn [acc cur]
|
||||
(if-let [sets (get-in themes-index [cur :sets])]
|
||||
(set/union acc sets)
|
||||
acc))
|
||||
#{} active-theme-ids)]
|
||||
active-set-ids))
|
||||
|
||||
(defn get-ordered-active-set-ids [state]
|
||||
(let [active-set-ids (get-active-set-ids state)
|
||||
token-set-groups (get-workspace-token-set-groups state)]
|
||||
(filter active-set-ids token-set-groups)))
|
||||
|
||||
(defn theme-ids-with-group
|
||||
"Returns set of theme-ids that share the same `:group` property as the theme with `theme-id`.
|
||||
Will also return matching theme-ids without a `:group` property."
|
||||
[theme-id state]
|
||||
(let [themes (get-workspace-themes-index state)
|
||||
theme-group (get-in themes [theme-id :group])
|
||||
same-group-theme-ids (->> themes
|
||||
(eduction
|
||||
(map val)
|
||||
(filter #(= (:group %) theme-group))
|
||||
(map :id))
|
||||
(into #{}))]
|
||||
same-group-theme-ids))
|
||||
|
||||
(defn toggle-active-theme-id
|
||||
"Toggle a `theme-id` by checking `:token-active-themes`.
|
||||
De-activate all theme-ids that have the same group as `theme-id` when activating `theme-id`.
|
||||
Ensures that the temporary theme id is selected when the resulting set is empty."
|
||||
[theme-id state]
|
||||
(let [temp-theme-id-set (some->> (get-temp-theme-id state) (conj #{}))
|
||||
active-theme-ids (get-active-theme-ids state)
|
||||
add? (not (get active-theme-ids theme-id))
|
||||
;; Deactivate themes with the same group when activating a theme
|
||||
same-group-ids (when add? (theme-ids-with-group theme-id state))
|
||||
theme-ids-without-same-group (set/difference active-theme-ids
|
||||
same-group-ids
|
||||
temp-theme-id-set)
|
||||
new-themes (if add?
|
||||
(conj theme-ids-without-same-group theme-id)
|
||||
(disj theme-ids-without-same-group theme-id))]
|
||||
(if (empty? new-themes)
|
||||
(or temp-theme-id-set #{})
|
||||
new-themes)))
|
||||
|
||||
(defn update-theme-id
|
||||
[state]
|
||||
(let [active-themes (get-active-theme-ids state)
|
||||
temporary-theme-id (get-temp-theme-id state)]
|
||||
(cond
|
||||
(empty? active-themes) temporary-theme-id
|
||||
(= 1 (count active-themes)) (first active-themes)
|
||||
:else temporary-theme-id)))
|
||||
|
||||
(defn get-workspace-token-theme [id state]
|
||||
(get-in state [:workspace-data :token-themes-index id]))
|
||||
|
||||
(defn add-token-set-to-token-theme [token-set-id token-theme]
|
||||
(update token-theme :sets conj token-set-id))
|
||||
|
||||
(defn toggle-token-set-to-token-theme [token-set-id token-theme]
|
||||
(update token-theme :sets #(if (get % token-set-id)
|
||||
(disj % token-set-id)
|
||||
(conj % token-set-id))))
|
||||
|
||||
;; Sets ------------------------------------------------------------------------
|
||||
|
||||
(defn get-workspace-sets [state]
|
||||
(get-in state [:workspace-data :token-sets-index]))
|
||||
|
||||
(defn get-token-set [set-id state]
|
||||
(some-> (get-workspace-sets state)
|
||||
(get set-id)))
|
||||
|
||||
(defn get-workspace-token-set-tokens [set-id state]
|
||||
(-> (get-token-set set-id state)
|
||||
:tokens))
|
||||
|
||||
(defn get-selected-token-set-id [state]
|
||||
(or (get-in state [:workspace-local :selected-token-set-id])
|
||||
(get-in state [:workspace-data :token-set-groups 0])))
|
||||
|
||||
(defn get-selected-token-set [state]
|
||||
(when-let [id (get-selected-token-set-id state)]
|
||||
(get-token-set id state)))
|
||||
|
||||
(defn get-selected-token-set-tokens [state]
|
||||
(when-let [token-set (get-selected-token-set state)]
|
||||
(let [tokens (or (wtt/get-workspace-tokens state) {})]
|
||||
(select-keys tokens (:tokens token-set)))))
|
||||
|
||||
(defn assoc-selected-token-set-id [state id]
|
||||
(assoc-in state [:workspace-local :selected-token-set-id] id))
|
||||
|
||||
(defn get-active-theme-sets-tokens-names-map [state]
|
||||
(let [active-set-ids (get-ordered-active-set-ids state)]
|
||||
(reduce
|
||||
(fn [names-map-acc set-id]
|
||||
(let [token-ids (get-workspace-token-set-tokens set-id state)]
|
||||
(reduce
|
||||
(fn [acc token-id]
|
||||
(if-let [token (wtt/get-workspace-token token-id state)]
|
||||
(assoc acc (wtt/token-identifier token) token)
|
||||
acc))
|
||||
names-map-acc token-ids)))
|
||||
(ordered-map) active-set-ids)))
|
|
@ -6,6 +6,7 @@
|
|||
[app.main.refs :as refs]
|
||||
[app.main.ui.workspace.tokens.changes :as wtch]
|
||||
[app.main.ui.workspace.tokens.style-dictionary :as wtsd]
|
||||
[app.main.ui.workspace.tokens.token-set :as wtts]
|
||||
[beicon.v2.core :as rx]
|
||||
[clojure.data :as data]
|
||||
[clojure.set :as set]
|
||||
|
@ -120,13 +121,14 @@
|
|||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(->>
|
||||
(rx/from (wtsd/resolve-tokens+ (get-in state [:workspace-data :tokens])))
|
||||
(rx/from
|
||||
(->
|
||||
(wtts/get-active-theme-sets-tokens-names-map state)
|
||||
(wtsd/resolve-tokens+ {:names-map? true})))
|
||||
(rx/mapcat
|
||||
(fn [sd-tokens]
|
||||
(let [undo-id (js/Symbol)]
|
||||
(rx/concat
|
||||
(rx/of (dwu/start-undo-transaction undo-id))
|
||||
(rx/concat
|
||||
(->> (update-tokens sd-tokens)
|
||||
(rx/concat)))
|
||||
(update-tokens sd-tokens)
|
||||
(rx/of (dwu/commit-undo-transaction undo-id))))))))))
|
||||
|
|
|
@ -4,8 +4,10 @@
|
|||
[app.common.test-helpers.compositions :as ctho]
|
||||
[app.common.test-helpers.files :as cthf]
|
||||
[app.common.test-helpers.shapes :as cths]
|
||||
[app.main.data.tokens :as wdt]
|
||||
[app.main.ui.workspace.tokens.changes :as wtch]
|
||||
[app.main.ui.workspace.tokens.token :as wtt]
|
||||
[app.main.ui.workspace.tokens.token-set :as wtts]
|
||||
[cljs.test :as t :include-macros true]
|
||||
[frontend-tests.helpers.pages :as thp]
|
||||
[frontend-tests.helpers.state :as ths]
|
||||
|
@ -18,24 +20,70 @@
|
|||
(log/set-level! "app.main.data.changes" :error)
|
||||
(thp/reset-idmap!))})
|
||||
|
||||
(defn setup-file
|
||||
(defn setup-file []
|
||||
(cthf/sample-file :file-1 :page-label :page-1))
|
||||
|
||||
(def border-radius-token
|
||||
{:value "12"
|
||||
:name "borderRadius.sm"
|
||||
:type :border-radius})
|
||||
|
||||
(def ^:private reference-border-radius-token
|
||||
{:value "{borderRadius.sm} * 2"
|
||||
:name "borderRadius.md"
|
||||
:type :border-radius})
|
||||
|
||||
(defn setup-file-with-tokens
|
||||
[& {:keys [rect-1 rect-2 rect-3]}]
|
||||
(-> (cthf/sample-file :file-1 :page-label :page-1)
|
||||
(-> (setup-file)
|
||||
(ctho/add-rect :rect-1 rect-1)
|
||||
(ctho/add-rect :rect-2 rect-2)
|
||||
(ctho/add-rect :rect-3 rect-3)
|
||||
(toht/add-token :token-1 {:value "12"
|
||||
:name "borderRadius.sm"
|
||||
:type :border-radius})
|
||||
(toht/add-token :token-2 {:value "{borderRadius.sm} * 2"
|
||||
:name "borderRadius.md"
|
||||
:type :border-radius})))
|
||||
(toht/add-token :token-1 border-radius-token)
|
||||
(toht/add-token :token-2 reference-border-radius-token)))
|
||||
|
||||
(t/deftest test-create-token
|
||||
(t/testing "creates token in new token set"
|
||||
(t/async
|
||||
done
|
||||
(let [file (setup-file)
|
||||
store (ths/setup-store file)
|
||||
events [(wdt/update-create-token border-radius-token)]]
|
||||
(tohs/run-store-async
|
||||
store done events
|
||||
(fn [new-state]
|
||||
(let [set-id (wtts/get-selected-token-set-id new-state)
|
||||
token-set (wtts/get-token-set set-id new-state)
|
||||
set-tokens (wtts/get-active-theme-sets-tokens-names-map new-state)]
|
||||
(t/testing "selects created workspace set and adds token to it"
|
||||
(t/is (some? token-set))
|
||||
(t/is (= 1 (count set-tokens)))
|
||||
(t/is (= (list border-radius-token) (->> (vals set-tokens)
|
||||
(map #(dissoc % :id :modified-at)))))))))))))
|
||||
|
||||
(t/deftest test-create-multiple-tokens
|
||||
(t/testing "uses selected tokens set when creating multiple tokens"
|
||||
(t/async
|
||||
done
|
||||
(let [file (setup-file)
|
||||
store (ths/setup-store file)
|
||||
events [(wdt/update-create-token border-radius-token)
|
||||
(wdt/update-create-token reference-border-radius-token)]]
|
||||
(tohs/run-store-async
|
||||
store done events
|
||||
(fn [new-state]
|
||||
(let [set-tokens (wtts/get-active-theme-sets-tokens-names-map new-state)]
|
||||
(t/testing "selects created workspace set and adds token to it"
|
||||
(t/is (= 2 (count set-tokens)))
|
||||
(t/is (= (list border-radius-token reference-border-radius-token)
|
||||
(->> (vals set-tokens)
|
||||
(map #(dissoc % :id :modified-at)))))))))))))
|
||||
|
||||
(t/deftest test-apply-token
|
||||
(t/testing "applies token to shape and updates shape attributes to resolved value"
|
||||
(t/async
|
||||
done
|
||||
(let [file (setup-file)
|
||||
(let [file (setup-file-with-tokens)
|
||||
store (ths/setup-store file)
|
||||
rect-1 (cths/get-shape file :rect-1)
|
||||
events [(wtch/apply-token {:shape-ids [(:id rect-1)]
|
||||
|
@ -60,7 +108,7 @@
|
|||
(t/testing "applying a token twice with the same attributes will override the previously applied tokens values"
|
||||
(t/async
|
||||
done
|
||||
(let [file (setup-file)
|
||||
(let [file (setup-file-with-tokens)
|
||||
store (ths/setup-store file)
|
||||
rect-1 (cths/get-shape file :rect-1)
|
||||
events [(wtch/apply-token {:shape-ids [(:id rect-1)]
|
||||
|
@ -89,7 +137,7 @@
|
|||
(t/testing "removes old token attributes and applies only single attribute"
|
||||
(t/async
|
||||
done
|
||||
(let [file (setup-file)
|
||||
(let [file (setup-file-with-tokens)
|
||||
store (ths/setup-store file)
|
||||
rect-1 (cths/get-shape file :rect-1)
|
||||
events [;; Apply `:token-1` to all border radius attributes
|
||||
|
@ -123,7 +171,7 @@
|
|||
(t/testing "applies dimensions token and updates the shapes width and height"
|
||||
(t/async
|
||||
done
|
||||
(let [file (-> (setup-file)
|
||||
(let [file (-> (setup-file-with-tokens)
|
||||
(toht/add-token :token-target {:value "100"
|
||||
:name "dimensions.sm"
|
||||
:type :dimensions}))
|
||||
|
@ -151,7 +199,7 @@
|
|||
(t/testing "applies sizing token and updates the shapes width and height"
|
||||
(t/async
|
||||
done
|
||||
(let [file (-> (setup-file)
|
||||
(let [file (-> (setup-file-with-tokens)
|
||||
(toht/add-token :token-target {:value "100"
|
||||
:name "sizing.sm"
|
||||
:type :sizing}))
|
||||
|
@ -179,7 +227,7 @@
|
|||
(t/testing "applies opacity token and updates the shapes opacity"
|
||||
(t/async
|
||||
done
|
||||
(let [file (-> (setup-file)
|
||||
(let [file (-> (setup-file-with-tokens)
|
||||
(toht/add-token :opacity-float {:value "0.3"
|
||||
:name "opacity.float"
|
||||
:type :opacity})
|
||||
|
@ -229,7 +277,7 @@
|
|||
(t/testing "applies rotation token and updates the shapes rotation"
|
||||
(t/async
|
||||
done
|
||||
(let [file (-> (setup-file)
|
||||
(let [file (-> (setup-file-with-tokens)
|
||||
(toht/add-token :token-target {:value "120"
|
||||
:name "rotation.medium"
|
||||
:type :rotation}))
|
||||
|
@ -253,11 +301,11 @@
|
|||
(t/testing "applies stroke-width token and updates the shapes with stroke"
|
||||
(t/async
|
||||
done
|
||||
(let [file (-> (setup-file {:rect-1 {:strokes [{:stroke-alignment :inner,
|
||||
:stroke-style :solid,
|
||||
:stroke-color "#000000",
|
||||
:stroke-opacity 1,
|
||||
:stroke-width 5}]}})
|
||||
(let [file (-> (setup-file-with-tokens {:rect-1 {:strokes [{:stroke-alignment :inner,
|
||||
:stroke-style :solid,
|
||||
:stroke-color "#000000",
|
||||
:stroke-opacity 1,
|
||||
:stroke-width 5}]}})
|
||||
(toht/add-token :token-target {:value "10"
|
||||
:name "stroke-width.sm"
|
||||
:type :stroke-width}))
|
||||
|
@ -286,7 +334,7 @@
|
|||
(t/testing "should apply token to all selected items, where no item has the token applied"
|
||||
(t/async
|
||||
done
|
||||
(let [file (setup-file)
|
||||
(let [file (setup-file-with-tokens)
|
||||
store (ths/setup-store file)
|
||||
rect-1 (cths/get-shape file :rect-1)
|
||||
rect-2 (cths/get-shape file :rect-2)
|
||||
|
@ -314,7 +362,7 @@
|
|||
(t/testing "should unapply given token if one of the selected items has the token applied while keeping other tokens with some attributes"
|
||||
(t/async
|
||||
done
|
||||
(let [file (-> (setup-file)
|
||||
(let [file (-> (setup-file-with-tokens)
|
||||
(toht/apply-token-to-shape :rect-1 :token-1 #{:rx :ry})
|
||||
(toht/apply-token-to-shape :rect-3 :token-2 #{:rx :ry}))
|
||||
store (ths/setup-store file)
|
||||
|
@ -348,7 +396,7 @@
|
|||
(t/testing "should apply token to all if none of the shapes has it applied"
|
||||
(t/async
|
||||
done
|
||||
(let [file (-> (setup-file)
|
||||
(let [file (-> (setup-file-with-tokens)
|
||||
(toht/apply-token-to-shape :rect-1 :token-2 #{:rx :ry})
|
||||
(toht/apply-token-to-shape :rect-3 :token-2 #{:rx :ry}))
|
||||
store (ths/setup-store file)
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
(:require
|
||||
[app.main.ui.workspace.tokens.style-dictionary :as sd]
|
||||
[cljs.test :as t :include-macros true]
|
||||
[promesa.core :as p]))
|
||||
[promesa.core :as p]
|
||||
[app.main.ui.workspace.tokens.token :as wtt]))
|
||||
|
||||
(def border-radius-token
|
||||
{:id #uuid "8c868278-7c8d-431b-bbc9-7d8f15c8edb9"
|
||||
|
@ -22,7 +23,7 @@
|
|||
(t/deftest resolve-tokens-test
|
||||
(t/async
|
||||
done
|
||||
(t/testing "resolves tokens using style-dictionary"
|
||||
(t/testing "resolves tokens using style-dictionary from a ids map"
|
||||
(-> (sd/resolve-tokens+ tokens)
|
||||
(p/finally (fn [resolved-tokens]
|
||||
(let [expected-tokens {"borderRadius.sm"
|
||||
|
@ -35,3 +36,22 @@
|
|||
:resolved-unit "px")}]
|
||||
(t/is (= expected-tokens resolved-tokens))
|
||||
(done))))))))
|
||||
|
||||
(t/deftest resolve-tokens-names-map-test
|
||||
(t/async
|
||||
done
|
||||
(t/testing "resolves tokens using style-dictionary from a names map"
|
||||
(-> (vals tokens)
|
||||
(wtt/token-names-map)
|
||||
(sd/resolve-tokens+ {:names-map? true})
|
||||
(p/finally (fn [resolved-tokens]
|
||||
(let [expected-tokens {"borderRadius.sm"
|
||||
(assoc border-radius-token
|
||||
:resolved-value 12
|
||||
:resolved-unit "px")
|
||||
"borderRadius.md-with-dashes"
|
||||
(assoc reference-border-radius-token
|
||||
:resolved-value 24
|
||||
:resolved-unit "px")}]
|
||||
(t/is (= expected-tokens resolved-tokens))
|
||||
(done))))))))
|
||||
|
|
37
frontend/test/token_tests/token_set_test.cljs
Normal file
37
frontend/test/token_tests/token_set_test.cljs
Normal file
|
@ -0,0 +1,37 @@
|
|||
(ns token-tests.token-set-test
|
||||
(:require
|
||||
[app.main.ui.workspace.tokens.token-set :as wtts]
|
||||
[cljs.test :as t]))
|
||||
|
||||
(t/deftest toggle-active-theme-id-test
|
||||
(t/testing "toggles active theme id"
|
||||
(let [state {:workspace-data {:token-themes-index {1 {:id 1}}}}]
|
||||
(t/testing "activates theme with id")
|
||||
(t/is (= (wtts/toggle-active-theme-id 1 state) #{1})))
|
||||
|
||||
(let [state {:workspace-data {:token-active-themes #{1}
|
||||
:token-themes-index {1 {:id 1}}}}]
|
||||
(t/testing "missing temp theme returns empty set"
|
||||
(t/is (= #{} (wtts/toggle-active-theme-id 1 state)))))
|
||||
|
||||
(let [state {:workspace-data {:token-theme-temporary-id :temp
|
||||
:token-active-themes #{1}
|
||||
:token-themes-index {1 {:id 1}}}}]
|
||||
(t/testing "empty set returns temp theme"
|
||||
(t/is (= #{:temp} (wtts/toggle-active-theme-id 1 state)))))
|
||||
|
||||
(let [state {:workspace-data {:token-active-themes #{2 3 4}
|
||||
:token-themes-index {1 {:id 1}
|
||||
2 {:id 2}
|
||||
3 {:id 3}
|
||||
4 {:id 4 :group :different}}}}]
|
||||
(t/testing "removes same group themes and keeps different group themes"
|
||||
(t/is (= #{1 4} (wtts/toggle-active-theme-id 1 state)))))
|
||||
|
||||
(let [state {:workspace-data {:token-active-themes #{1 2 3 4}}
|
||||
:token-themes-index {1 {:id 1}
|
||||
2 {:id 2}
|
||||
3 {:id 3}
|
||||
4 {:id 4 :group :different}}}]
|
||||
(t/testing "removes theme when active"
|
||||
(t/is (= #{4 3 2} (wtts/toggle-active-theme-id 1 state)))))))
|
Loading…
Add table
Reference in a new issue