Merge pull request #5766 from penpot/niwinz-tokens-changes

 Several changes to tokens
This commit is contained in:
Andrey Antukh 2025-02-13 11:46:58 +01:00 committed by GitHub
commit 054efb3435
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 440 additions and 330 deletions

View file

@ -312,7 +312,7 @@
(ptk/reify ::set-token-type-section-open (ptk/reify ::set-token-type-section-open
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
(assoc-in state [:workspace-tokens :open-status token-type] open?)))) (assoc-in state [:workspace-local :token-type-open-status token-type] open?))))
;; === Token Context Menu ;; === Token Context Menu

View file

@ -1,8 +0,0 @@
(ns app.main.data.workspace.tokens.common
(:require
[app.main.data.helpers :as dsh]))
(defn get-workspace-tokens-lib
[state]
(-> (dsh/lookup-file-data state)
(get :tokens-lib)))

View file

@ -1,24 +1,29 @@
;; 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.data.workspace.tokens.selected-set (ns app.main.data.workspace.tokens.selected-set
"The user selected token set in the ui, stored by the `:name` of the set. "The user selected token set in the ui, stored by the `:name` of the set.
Will default to the first set." Will default to the first set."
(:require (:require
[app.common.types.tokens-lib :as ctob] [app.common.types.tokens-lib :as ctob]
[app.main.data.workspace.tokens.common :as dwtc] [app.main.data.helpers :as dsh]
[potok.v2.core :as ptk])) [potok.v2.core :as ptk]))
(defn assoc-selected-token-set-name [state set-name]
(assoc-in state [:workspace-local :selected-token-set-name] set-name))
(defn get-selected-token-set-name [state] (defn get-selected-token-set-name [state]
(or (get-in state [:workspace-local :selected-token-set-name]) (or (get-in state [:workspace-local :selected-token-set-name])
(some-> (dwtc/get-workspace-tokens-lib state) (some-> (dsh/lookup-file-data state)
(get :tokens-lib)
(ctob/get-sets) (ctob/get-sets)
(first) (first)
:name))) :name)))
(defn get-selected-token-set [state] (defn get-selected-token-set [state]
(when-let [set-name (get-selected-token-set-name state)] (when-let [set-name (get-selected-token-set-name state)]
(some-> (dwtc/get-workspace-tokens-lib state) (some-> (dsh/lookup-file-data state)
(get :tokens-lib)
(ctob/get-set set-name)))) (ctob/get-set set-name))))
(defn get-selected-token-set-token [state token-name] (defn get-selected-token-set-token [state token-name]
@ -30,8 +35,8 @@
:tokens)) :tokens))
(defn set-selected-token-set-name (defn set-selected-token-set-name
[set-name] [name]
(ptk/reify ::set-selected-token-set-path-from-name (ptk/reify ::set-selected-token-set-path-from-name
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
(assoc-selected-token-set-name state set-name)))) (update state :workspace-local assoc :selected-token-set-name name))))

View file

@ -144,7 +144,7 @@
(l/derived (l/derived
(fn [{:keys [objects selected]}] (fn [{:keys [objects selected]}]
(dsh/process-selected-shapes objects selected)) (dsh/process-selected-shapes objects selected))
selected-shapes-data)) selected-shapes-data =))
(defn make-selected-ref (defn make-selected-ref
[id] [id]
@ -479,6 +479,7 @@
(def workspace-active-set-names (def workspace-active-set-names
(l/derived (d/nilf ctob/get-active-themes-set-names) tokens-lib)) (l/derived (d/nilf ctob/get-active-themes-set-names) tokens-lib))
;; FIXME: deprecated, it should not be implemented with ref
(def workspace-active-theme-sets-tokens (def workspace-active-theme-sets-tokens
(l/derived #(or (some-> % ctob/get-active-themes-set-tokens) {}) tokens-lib)) (l/derived #(or (some-> % ctob/get-active-themes-set-tokens) {}) tokens-lib))

View file

@ -31,7 +31,7 @@
[app.main.ui.workspace.sidebar.shortcuts :refer [shortcuts-container]] [app.main.ui.workspace.sidebar.shortcuts :refer [shortcuts-container]]
[app.main.ui.workspace.sidebar.sitemap :refer [sitemap]] [app.main.ui.workspace.sidebar.sitemap :refer [sitemap]]
[app.main.ui.workspace.sidebar.versions :refer [versions-toolbox*]] [app.main.ui.workspace.sidebar.versions :refer [versions-toolbox*]]
[app.main.ui.workspace.tokens.sidebar :refer [tokens-sidebar-tab]] [app.main.ui.workspace.tokens.sidebar :refer [tokens-sidebar-tab*]]
[app.util.debug :as dbg] [app.util.debug :as dbg]
[app.util.i18n :refer [tr]] [app.util.i18n :refer [tr]]
[potok.v2.core :as ptk] [potok.v2.core :as ptk]
@ -120,7 +120,7 @@
tokens-tab tokens-tab
(when design-tokens? (when design-tokens?
(mf/html [:& tokens-sidebar-tab])) (mf/html [:> tokens-sidebar-tab*]))
tabs tabs
(if ^boolean mode-inspect? (if ^boolean mode-inspect?

View file

@ -6,6 +6,8 @@
(ns app.main.ui.workspace.tokens.changes (ns app.main.ui.workspace.tokens.changes
(:require (:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.types.shape.layout :as ctsl] [app.common.types.shape.layout :as ctsl]
[app.common.types.shape.radius :as ctsr] [app.common.types.shape.radius :as ctsr]
[app.common.types.token :as ctt] [app.common.types.token :as ctt]
@ -26,6 +28,8 @@
[clojure.set :as set] [clojure.set :as set]
[potok.v2.core :as ptk])) [potok.v2.core :as ptk]))
(declare token-properties)
;; Token Updates --------------------------------------------------------------- ;; Token Updates ---------------------------------------------------------------
(defn apply-token (defn apply-token
@ -76,12 +80,16 @@
(update shape :applied-tokens remove-token)))))))) (update shape :applied-tokens remove-token))))))))
(defn toggle-token (defn toggle-token
[{:keys [token-type-props token shapes] :as _props}] [{:keys [token shapes]}]
(ptk/reify ::on-toggle-token (ptk/reify ::on-toggle-token
ptk/WatchEvent ptk/WatchEvent
(watch [_ _ _] (watch [_ _ _]
(let [{:keys [attributes all-attributes on-update-shape]} token-type-props (let [{:keys [attributes all-attributes on-update-shape]}
unapply-tokens? (wtt/shapes-token-applied? token shapes (or all-attributes attributes)) (get token-properties (:type token))
unapply-tokens?
(wtt/shapes-token-applied? token shapes (or all-attributes attributes))
shape-ids (map :id shapes)] shape-ids (map :id shapes)]
(if unapply-tokens? (if unapply-tokens?
(rx/of (rx/of
@ -272,3 +280,88 @@
(select-keys attributes))] (select-keys attributes))]
(dwsl/update-layout-child shape-ids props {:ignore-touched true (dwsl/update-layout-child shape-ids props {:ignore-touched true
:page-id page-id})))))) :page-id page-id}))))))
;; Token Types -----------------------------------------------------------------
;; FIXME: the values should be lazy evaluated, probably a function,
;; becasue on future we will need to translate that labels and that
;; can not be done statically
(def token-properties
"A map of default properties by token type"
(d/ordered-map
:border-radius
{:title "Border Radius"
:attributes ctt/border-radius-keys
:on-update-shape update-shape-radius-all
:modal {:key :tokens/border-radius
:fields [{:label "Border Radius"
:key :border-radius}]}}
:color
{:title "Color"
:attributes #{:fill}
:all-attributes ctt/color-keys
:on-update-shape update-fill-stroke
:modal {:key :tokens/color
:fields [{:label "Color" :key :color}]}}
:stroke-width
{:title "Stroke Width"
:attributes ctt/stroke-width-keys
:on-update-shape update-stroke-width
:modal {:key :tokens/stroke-width
:fields [{:label "Stroke Width"
:key :stroke-width}]}}
:sizing
{:title "Sizing"
:attributes #{:width :height}
:all-attributes ctt/sizing-keys
:on-update-shape update-shape-dimensions
:modal {:key :tokens/sizing
:fields [{:label "Sizing"
:key :sizing}]}}
:dimensions
{:title "Dimensions"
:attributes #{:width :height}
:all-attributes (set/union
ctt/spacing-keys
ctt/sizing-keys
ctt/border-radius-keys
ctt/stroke-width-keys)
:on-update-shape update-shape-dimensions
:modal {:key :tokens/dimensions
:fields [{:label "Dimensions"
:key :dimensions}]}}
:opacity
{:title "Opacity"
:attributes ctt/opacity-keys
:on-update-shape update-opacity
:modal {:key :tokens/opacity
:fields [{:label "Opacity"
:key :opacity}]}}
:rotation
{:title "Rotation"
:attributes ctt/rotation-keys
:on-update-shape update-rotation
:modal {:key :tokens/rotation
:fields [{:label "Rotation"
:key :rotation}]}}
:spacing
{:title "Spacing"
:attributes #{:column-gap :row-gap}
:all-attributes ctt/spacing-keys
:on-update-shape update-layout-spacing
:modal {:key :tokens/spacing
:fields [{:label "Spacing"
:key :spacing}]}}))
(defn get-token-properties [token]
(get token-properties (:type token)))
(defn token-attributes [token-type]
(dm/get-in token-properties [token-type :attributes]))

View file

@ -18,7 +18,6 @@
[app.main.ui.ds.foundations.assets.icon :refer [icon*]] [app.main.ui.ds.foundations.assets.icon :refer [icon*]]
[app.main.ui.workspace.tokens.changes :as wtch] [app.main.ui.workspace.tokens.changes :as wtch]
[app.main.ui.workspace.tokens.token :as wtt] [app.main.ui.workspace.tokens.token :as wtt]
[app.main.ui.workspace.tokens.token-types :as wtty]
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.i18n :refer [tr]] [app.util.i18n :refer [tr]]
[app.util.timers :as timers] [app.util.timers :as timers]
@ -38,7 +37,7 @@
(defn generic-attribute-actions [attributes title {:keys [token selected-shapes on-update-shape]}] (defn generic-attribute-actions [attributes title {:keys [token selected-shapes on-update-shape]}]
(let [on-update-shape-fn (or on-update-shape (let [on-update-shape-fn (or on-update-shape
(-> (wtty/get-token-properties token) (-> (wtch/get-token-properties token)
(:on-update-shape))) (:on-update-shape)))
{:keys [selected-pred shape-ids]} (attribute-actions token selected-shapes attributes)] {:keys [selected-pred shape-ids]} (attribute-actions token selected-shapes attributes)]
(map (fn [attribute] (map (fn [attribute]
@ -236,7 +235,7 @@
(generic-attribute-actions #{:y} "Y" (assoc context-data :on-update-shape wtch/update-shape-position))))})) (generic-attribute-actions #{:y} "Y" (assoc context-data :on-update-shape wtch/update-shape-position))))}))
(defn default-actions [{:keys [token selected-token-set-name]}] (defn default-actions [{:keys [token selected-token-set-name]}]
(let [{:keys [modal]} (wtty/get-token-properties token)] (let [{:keys [modal]} (wtch/get-token-properties token)]
[{:title (tr "workspace.token.edit") [{:title (tr "workspace.token.edit")
:no-selectable true :no-selectable true
:action (fn [event] :action (fn [event]

View file

@ -21,13 +21,13 @@
[app.main.ui.notifications.context-notification :refer [context-notification]] [app.main.ui.notifications.context-notification :refer [context-notification]]
[app.main.ui.workspace.colorpicker :as colorpicker] [app.main.ui.workspace.colorpicker :as colorpicker]
[app.main.ui.workspace.colorpicker.ramp :refer [ramp-selector*]] [app.main.ui.workspace.colorpicker.ramp :refer [ramp-selector*]]
[app.main.ui.workspace.tokens.changes :as wtch]
[app.main.ui.workspace.tokens.components.controls.input-token-color-bullet :refer [input-token-color-bullet*]] [app.main.ui.workspace.tokens.components.controls.input-token-color-bullet :refer [input-token-color-bullet*]]
[app.main.ui.workspace.tokens.components.controls.input-tokens :refer [input-tokens*]] [app.main.ui.workspace.tokens.components.controls.input-tokens :refer [input-tokens*]]
[app.main.ui.workspace.tokens.errors :as wte] [app.main.ui.workspace.tokens.errors :as wte]
[app.main.ui.workspace.tokens.style-dictionary :as sd] [app.main.ui.workspace.tokens.style-dictionary :as sd]
[app.main.ui.workspace.tokens.tinycolor :as tinycolor] [app.main.ui.workspace.tokens.tinycolor :as tinycolor]
[app.main.ui.workspace.tokens.token :as wtt] [app.main.ui.workspace.tokens.token :as wtt]
[app.main.ui.workspace.tokens.token-types :as wtty]
[app.main.ui.workspace.tokens.update :as wtu] [app.main.ui.workspace.tokens.update :as wtu]
[app.main.ui.workspace.tokens.warnings :as wtw] [app.main.ui.workspace.tokens.warnings :as wtw]
[app.util.dom :as dom] [app.util.dom :as dom]
@ -223,7 +223,7 @@
[{:keys [token token-type action selected-token-set-name on-display-colorpicker]}] [{:keys [token token-type action selected-token-set-name on-display-colorpicker]}]
(let [create? (not (instance? ctob/Token token)) (let [create? (not (instance? ctob/Token token))
token (or token {:type token-type}) token (or token {:type token-type})
token-properties (wtty/get-token-properties token) token-properties (wtch/get-token-properties token)
color? (wtt/color-token? token) color? (wtt/color-token? token)
selected-set-tokens (mf/deref refs/workspace-selected-token-set-tokens) selected-set-tokens (mf/deref refs/workspace-selected-token-set-tokens)
active-theme-tokens (mf/deref refs/workspace-active-theme-sets-tokens) active-theme-tokens (mf/deref refs/workspace-active-theme-sets-tokens)

View file

@ -8,6 +8,7 @@
(:require-macros [app.main.style :as stl]) (:require-macros [app.main.style :as stl])
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.types.tokens-lib :as ctob] [app.common.types.tokens-lib :as ctob]
[app.main.data.event :as ev] [app.main.data.event :as ev]
[app.main.data.modal :as modal] [app.main.data.modal :as modal]
@ -18,6 +19,7 @@
[app.main.ui.components.dropdown-menu :refer [dropdown-menu [app.main.ui.components.dropdown-menu :refer [dropdown-menu
dropdown-menu-item*]] dropdown-menu-item*]]
[app.main.ui.components.title-bar :refer [title-bar]] [app.main.ui.components.title-bar :refer [title-bar]]
[app.main.ui.context :as ctx]
[app.main.ui.ds.buttons.button :refer [button*]] [app.main.ui.ds.buttons.button :refer [button*]]
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]] [app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
[app.main.ui.ds.foundations.typography.text :refer [text*]] [app.main.ui.ds.foundations.typography.text :refer [text*]]
@ -32,9 +34,8 @@
[app.main.ui.workspace.tokens.sets-context-menu :refer [sets-context-menu]] [app.main.ui.workspace.tokens.sets-context-menu :refer [sets-context-menu]]
[app.main.ui.workspace.tokens.style-dictionary :as sd] [app.main.ui.workspace.tokens.style-dictionary :as sd]
[app.main.ui.workspace.tokens.theme-select :refer [theme-select]] [app.main.ui.workspace.tokens.theme-select :refer [theme-select]]
[app.main.ui.workspace.tokens.token :as wtt] [app.main.ui.workspace.tokens.token-pill :refer [token-pill*]]
[app.main.ui.workspace.tokens.token-pill :refer [token-pill]] [app.util.array :as array]
[app.main.ui.workspace.tokens.token-types :as wtty]
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.i18n :refer [tr]] [app.util.i18n :refer [tr]]
[app.util.webapi :as wapi] [app.util.webapi :as wapi]
@ -44,8 +45,8 @@
[rumext.v2 :as mf] [rumext.v2 :as mf]
[shadow.resource])) [shadow.resource]))
(def lens:token-type-open-status (def ref:token-type-open-status
(l/derived (l/in [:workspace-tokens :open-status]) st/state)) (l/derived #(dm/get-in % [:workspace-local :token-type-open-status]) st/state))
;; Components ------------------------------------------------------------------ ;; Components ------------------------------------------------------------------
@ -65,62 +66,64 @@
:sizing "expand" :sizing "expand"
"add")) "add"))
(defn attribute-actions [token selected-shapes attributes] (mf/defc token-group*
(let [ids-by-attributes (wtt/shapes-ids-by-applied-attributes token selected-shapes attributes) {::mf/private true}
shape-ids (into #{} (map :id selected-shapes))] [{:keys [type tokens selected-shapes active-theme-tokens is-open]}]
{:all-selected? (wtt/shapes-applied-all? ids-by-attributes shape-ids attributes) (let [{:keys [modal title]}
:shape-ids shape-ids (get wtch/token-properties type)
:selected-pred #(seq (% ids-by-attributes))}))
(mf/defc token-component tokens
[{:keys [type tokens selected-shapes token-type-props active-theme-tokens]}] (mf/with-memo [tokens]
(let [open? (mf/deref (-> (l/key type) (vec (sort-by :name tokens)))
(l/derived lens:token-type-open-status)))
{:keys [modal attributes all-attributes title]} token-type-props
on-context-menu (mf/use-fn on-context-menu
(fn [event token] (mf/use-fn
(dom/prevent-default event) (fn [event token]
(dom/stop-propagation event) (dom/prevent-default event)
(st/emit! (dt/show-token-context-menu (dom/stop-propagation event)
{:type :token (st/emit! (dt/show-token-context-menu
:position (dom/get-client-position event) {:type :token
:errors (:errors token) :position (dom/get-client-position event)
:token-name (:name token)})))) :errors (:errors token)
:token-name (:name token)}))))
on-toggle-open-click (mf/use-fn on-toggle-open-click
(mf/deps open? tokens) (mf/use-fn
#(st/emit! (dt/set-token-type-section-open type (not open?)))) (mf/deps is-open type)
on-popover-open-click (mf/use-fn #(st/emit! (dt/set-token-type-section-open type (not is-open))))
(fn [event]
(mf/deps type title)
(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
:fields fields
:title title
:action "create"
:token-type type}))))
on-token-pill-click (mf/use-fn on-popover-open-click
(mf/deps selected-shapes token-type-props) (mf/use-fn
(fn [event token] (mf/deps type title modal)
(dom/stop-propagation event) (fn [event]
(when (seq selected-shapes) (dom/stop-propagation event)
(st/emit! (st/emit! (dt/set-token-type-section-open type true)
(wtch/toggle-token {:token token ;; FIXME: use dom/get-client-position
:shapes selected-shapes (modal/show (:key modal)
:token-type-props token-type-props}))))) {:x (.-clientX ^js event)
:y (.-clientY ^js event)
:position :right
:fields (:fields modal)
:title title
:action "create"
:token-type type}))))
on-token-pill-click
(mf/use-fn
(mf/deps selected-shapes)
(fn [event token]
(dom/stop-propagation event)
(when (seq selected-shapes)
(st/emit! (wtch/toggle-token {:token token
:shapes selected-shapes})))))
tokens-count (count tokens) tokens-count (count tokens)
can-edit? (:can-edit (deref refs/permissions))] can-edit? (:can-edit (deref refs/permissions))]
[:div {:on-click on-toggle-open-click} [:div {:on-click on-toggle-open-click}
[:& cmm/asset-section {:icon (token-section-icon type) [:& cmm/asset-section {:icon (token-section-icon type)
:title title :title title
:assets-count tokens-count :assets-count tokens-count
:open? open?} :open? is-open}
[:& cmm/asset-section-block {:role :title-button} [:& cmm/asset-section-block {:role :title-button}
(when can-edit? (when can-edit?
[:> icon-button* {:on-click on-popover-open-click [:> icon-button* {:on-click on-popover-open-click
@ -128,56 +131,55 @@
:icon "add" :icon "add"
;; TODO: This needs translation ;; TODO: This needs translation
:aria-label (str "Add token: " title)}])] :aria-label (str "Add token: " title)}])]
(when open? (when is-open
[:& cmm/asset-section-block {:role :content} [:& cmm/asset-section-block {:role :content}
[:div {:class (stl/css :token-pills-wrapper)} [:div {:class (stl/css :token-pills-wrapper)}
(for [token (sort-by :name tokens)] (for [token tokens]
(let [theme-token (get active-theme-tokens (wtt/token-identifier token)) [:> token-pill*
multiple-selection (< 1 (count selected-shapes)) {:key (:name token)
full-applied (:all-selected? (attribute-actions token selected-shapes (or all-attributes attributes))) :token token
applied (wtt/shapes-token-applied? token selected-shapes (or all-attributes attributes)) :selected-shapes selected-shapes
on-token-click (fn [e] :active-theme-tokens active-theme-tokens
(on-token-pill-click e token)) :on-click on-token-pill-click
on-context-menu (fn [e] (on-context-menu e token))] :on-context-menu on-context-menu}])]])]]))
[:& token-pill
{:key (:name token)
:token-type-props token-type-props
:token token
:selected-shapes selected-shapes
:active-theme-tokens active-theme-tokens
:theme-token theme-token
:half-applied (or (and applied multiple-selection)
(and applied (not full-applied)))
:full-applied (if multiple-selection
false
applied)
:on-click on-token-click
:on-context-menu on-context-menu}]))]])]]))
(defn sorted-token-groups (defn- get-sorted-token-groups
"Separate token-types into groups of `:empty` or `:filled` depending if tokens exist for that type. "Separate token-types into groups of `empty` or `filled` depending if
Sort each group alphabetically (by their `:token-key`)." tokens exist for that type. Sort each group alphabetically (by
[tokens] their type)."
(let [tokens-by-type (ctob/group-by-type tokens) [tokens-by-type]
{:keys [empty filled]} (->> wtty/token-types (loop [empty #js []
(map (fn [[token-key token-type-props]] filled #js []
{:token-key token-key types (-> wtch/token-properties keys seq)]
:token-type-props token-type-props (if-let [type (first types)]
:tokens (get tokens-by-type token-key [])})) (if (not-empty (get tokens-by-type type))
(group-by (fn [{:keys [tokens]}] (recur empty
(if (empty? tokens) :empty :filled))))] (array/conj! filled type)
{:empty (sort-by :token-key empty) (rest types))
:filled (sort-by :token-key filled)})) (recur (array/conj! empty type)
filled
(rest types)))
[(seq (array/sort! empty))
(seq (array/sort! filled))])))
(mf/defc themes-header*
{::mf/private true}
[]
(let [ordered-themes
(mf/deref refs/workspace-token-themes-no-hidden)
permissions
(mf/use-ctx ctx/permissions)
can-edit?
(get permissions :can-edit)
(mf/defc themes-header
[_props]
(let [ordered-themes (mf/deref refs/workspace-token-themes-no-hidden)
can-edit? (:can-edit (deref refs/permissions))
open-modal open-modal
(mf/use-fn (mf/use-fn
(fn [e] (fn [e]
(dom/stop-propagation e) (dom/stop-propagation e)
(modal/show! :tokens/themes {})))] (modal/show! :tokens/themes {})))]
[:div {:class (stl/css :themes-wrapper)} [:div {:class (stl/css :themes-wrapper)}
[:span {:class (stl/css :themes-header)} (tr "labels.themes")] [:span {:class (stl/css :themes-header)} (tr "labels.themes")]
(if (empty? ordered-themes) (if (empty? ordered-themes)
@ -199,13 +201,24 @@
(tr "workspace.token.no-permission-themes"))} (tr "workspace.token.no-permission-themes"))}
[:& theme-select]]))])) [:& theme-select]]))]))
(mf/defc add-set-button (mf/defc add-set-button*
[{:keys [on-open style]}] {::mf/private true}
(let [{:keys [on-create new-path]} (sets-context/use-context) [{:keys [style]}]
on-click #(do (let [{:keys [on-create new-path]}
(on-open) (sets-context/use-context)
(on-create []))
can-edit? (:can-edit (deref refs/permissions))] permissions
(mf/use-ctx ctx/permissions)
can-edit?
(get permissions :can-edit)
on-click
(mf/use-fn
(mf/deps on-create)
(fn []
(on-create [])))]
(if (= style "inline") (if (= style "inline")
(when-not new-path (when-not new-path
(if can-edit? (if can-edit?
@ -224,67 +237,96 @@
:on-click on-click :on-click on-click
:aria-label (tr "workspace.token.add set")}])))) :aria-label (tr "workspace.token.add set")}]))))
(mf/defc theme-sets-list (mf/defc theme-sets-list*
[{:keys [on-open]}] {::mf/private true}
[]
(let [token-sets (mf/deref refs/workspace-ordered-token-sets) (let [token-sets (mf/deref refs/workspace-ordered-token-sets)
{:keys [new-path] :as ctx} (sets-context/use-context)] {:keys [new-path] :as ctx} (sets-context/use-context)]
(if (and (empty? token-sets) (if (and (empty? token-sets)
(not new-path)) (not new-path))
[:& add-set-button {:on-open on-open [:> add-set-button* {:style "inline"}]
:style "inline"}]
[:& h/sortable-container {} [:& h/sortable-container {}
[:& sets-list]]))) [:& sets-list]])))
(mf/defc themes-sets-tab (mf/defc themes-sets-tab*
{::mf/private true}
[{:keys [resize-height]}] [{:keys [resize-height]}]
(let [open? (mf/use-state true) (let [permissions
on-open (mf/use-fn #(reset! open? true)) (mf/use-ctx ctx/permissions)
can-edit? (:can-edit (deref refs/permissions))]
can-edit?
(get permissions :can-edit)]
[:& sets-context/provider {} [:& sets-context/provider {}
[:& sets-context-menu] [:& sets-context-menu]
[:article {:data-testid "token-themes-sets-sidebar" [:article {:data-testid "token-themes-sets-sidebar"
:class (stl/css :sets-section-wrapper) :class (stl/css :sets-section-wrapper)
:style {"--resize-height" (str resize-height "px")}} :style {"--resize-height" (str resize-height "px")}}
[:div {:class (stl/css :sets-sidebar)} [:div {:class (stl/css :sets-sidebar)}
[:& themes-header] [:> themes-header*]
[:div {:class (stl/css :sidebar-header)} [:div {:class (stl/css :sidebar-header)}
[:& title-bar {:title (tr "labels.sets")} [:& title-bar {:title (tr "labels.sets")}
(when can-edit? (when can-edit?
[:& add-set-button {:on-open on-open [:> add-set-button* {:style "header"}])]]
:style "header"}])]]
[:& theme-sets-list {:on-open on-open}]]]]))
(mf/defc tokens-tab [:> theme-sets-list* {}]]]]))
[_props]
(let [objects (mf/deref refs/workspace-page-objects)
selected (mf/deref refs/selected-shapes) (mf/defc tokens-tab*
selected-shapes (into [] (keep (d/getf objects)) selected) []
(let [objects (mf/deref refs/workspace-page-objects)
selected (mf/deref refs/selected-shapes)
open-status (mf/deref ref:token-type-open-status)
active-theme-tokens (sd/use-active-theme-sets-tokens) selected-shapes
(mf/with-memo [selected objects]
(into [] (keep (d/getf objects)) selected))
tokens (sd/use-resolved-workspace-tokens) active-theme-tokens
(sd/use-active-theme-tokens)
selected-token-set-tokens (mf/deref refs/workspace-selected-token-set-tokens) tokens
(sd/use-resolved-workspace-tokens)
selected-token-set-name (mf/deref refs/workspace-selected-token-set-name) 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-by-type
(mf/with-memo [tokens selected-token-set-tokens]
(let [tokens (reduce-kv (fn [tokens k _]
(if (contains? selected-token-set-tokens k)
tokens
(dissoc tokens k)))
tokens
tokens)]
(ctob/group-by-type tokens)))
[empty-group filled-group]
(mf/with-memo [tokens-by-type]
(get-sorted-token-groups tokens-by-type))]
token-groups (mf/with-memo [tokens selected-token-set-tokens]
(-> (select-keys tokens (keys selected-token-set-tokens))
(sorted-token-groups)))]
[:* [:*
[:& token-context-menu] [:& token-context-menu]
[:& title-bar {:all-clickable true [:& title-bar {:all-clickable true
:title (tr "workspace.token.tokens-section-title" selected-token-set-name)}] :title (tr "workspace.token.tokens-section-title" selected-token-set-name)}]
(for [{:keys [token-key token-type-props tokens]} (concat (:filled token-groups) (for [type filled-group]
(:empty token-groups))] (let [tokens (get tokens-by-type type)]
[:& token-component {:key token-key [:> token-group* {:key (name type)
:type token-key :is-open (get open-status type false)
:selected-shapes selected-shapes :type type
:active-theme-tokens active-theme-tokens :selected-shapes selected-shapes
:tokens tokens :active-theme-tokens active-theme-tokens
:token-type-props token-type-props}])])) :tokens tokens}]))
(for [type empty-group]
[:> token-group* {:key (name type)
:type type
:selected-shapes selected-shapes
:active-theme-tokens active-theme-tokens
:tokens []}])]))
(mf/defc import-export-button (mf/defc import-export-button
{::mf/wrap-props false} {::mf/wrap-props false}
@ -357,22 +399,21 @@
:on-click on-export} :on-click on-export}
(tr "labels.export")]]])) (tr "labels.export")]]]))
(mf/defc tokens-sidebar-tab (mf/defc tokens-sidebar-tab*
{::mf/wrap [mf/memo] {::mf/wrap [mf/memo]}
::mf/wrap-props false} []
[_props]
(let [{on-pointer-down-pages :on-pointer-down (let [{on-pointer-down-pages :on-pointer-down
on-lost-pointer-capture-pages :on-lost-pointer-capture on-lost-pointer-capture-pages :on-lost-pointer-capture
on-pointer-move-pages :on-pointer-move on-pointer-move-pages :on-pointer-move
size-pages-opened :size} size-pages-opened :size}
(use-resize-hook :tokens 200 38 "0.6" :y false nil)] (use-resize-hook :tokens 200 38 "0.6" :y false nil)]
[:div {:class (stl/css :sidebar-wrapper)} [:div {:class (stl/css :sidebar-wrapper)}
[:& themes-sets-tab {:resize-height size-pages-opened}] [:> themes-sets-tab* {:resize-height size-pages-opened}]
[:article {:class (stl/css :tokens-section-wrapper) [:article {:class (stl/css :tokens-section-wrapper)
:data-testid "tokens-sidebar"} :data-testid "tokens-sidebar"}
[:div {:class (stl/css :resize-area-horiz) [:div {:class (stl/css :resize-area-horiz)
:on-pointer-down on-pointer-down-pages :on-pointer-down on-pointer-down-pages
:on-lost-pointer-capture on-lost-pointer-capture-pages :on-lost-pointer-capture on-lost-pointer-capture-pages
:on-pointer-move on-pointer-move-pages}] :on-pointer-move on-pointer-move-pages}]
[:& tokens-tab]] [:> tokens-tab*]]
[:& import-export-button]])) [:& import-export-button]]))

View file

@ -295,6 +295,8 @@
prefer-selected-token-set-tokens (merge active-theme-tokens 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))) (use-resolved-tokens prefer-selected-token-set-tokens)))
(defn use-active-theme-sets-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) (-> (mf/deref refs/workspace-active-theme-sets-tokens)
(use-resolved-tokens {:cache-atom !theme-tokens-cache}))) (use-resolved-tokens {:cache-atom !theme-tokens-cache})))

View file

@ -95,7 +95,6 @@
is-open? (:is-open? state) is-open? (:is-open? state)
;; Dropdown ;; Dropdown
dropdown-element* (mf/use-ref nil)
on-close-dropdown (mf/use-fn #(swap! state* assoc :is-open? false)) on-close-dropdown (mf/use-fn #(swap! state* assoc :is-open? false))
on-open-dropdown on-open-dropdown
@ -118,8 +117,7 @@
current-label] current-label]
[:> icon* {:icon-id i/arrow-down :class (stl/css :dropdown-button) :aria-hidden true}] [:> icon* {:icon-id i/arrow-down :class (stl/css :dropdown-button) :aria-hidden true}]
[:& dropdown {:show is-open? [:& dropdown {:show is-open?
:on-close on-close-dropdown :on-close on-close-dropdown}
:ref dropdown-element*}
[:& theme-options {:active-theme-paths active-theme-paths [:& theme-options {:active-theme-paths active-theme-paths
:themes themes :themes themes
:on-close on-close-dropdown}]]])) :on-close on-close-dropdown}]]]))

View file

@ -1,6 +1,7 @@
(ns app.main.ui.workspace.tokens.token (ns app.main.ui.workspace.tokens.token
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.data.macros :as dm]
[app.main.ui.workspace.tokens.tinycolor :as tinycolor] [app.main.ui.workspace.tokens.tinycolor :as tinycolor]
[clojure.set :as set] [clojure.set :as set]
[cuerdas.core :as str])) [cuerdas.core :as str]))
@ -21,7 +22,9 @@
{:value parsed-value {:value parsed-value
:unit unit})))) :unit unit}))))
(defn token-identifier [{:keys [name] :as _token}] ;; FIXME: looks very redundant function
(defn token-identifier
[{:keys [name] :as _token}]
name) name)
(defn attributes-map (defn attributes-map
@ -43,7 +46,7 @@
(defn token-attribute-applied? (defn token-attribute-applied?
"Test if `token` is applied to a `shape` on single `token-attribute`." "Test if `token` is applied to a `shape` on single `token-attribute`."
[token shape token-attribute] [token shape token-attribute]
(when-let [id (get-in shape [:applied-tokens token-attribute])] (when-let [id (dm/get-in shape [:applied-tokens token-attribute])]
(= (token-identifier token) id))) (= (token-identifier token) id)))
(defn token-applied? (defn token-applied?
@ -56,15 +59,18 @@
[token shapes token-attributes] [token shapes token-attributes]
(some #(token-applied? token % token-attributes) shapes)) (some #(token-applied? token % token-attributes) shapes))
(defn shapes-ids-by-applied-attributes [token shapes token-attributes] (defn shapes-ids-by-applied-attributes
(reduce (fn [acc shape] [token shapes token-attributes]
(let [applied-ids-by-attribute (->> (map #(when (token-attribute-applied? token shape %) (let [conj* (fnil conj #{})]
[% #{(:id shape)}]) (reduce (fn [result shape]
token-attributes) (let [shape-id (dm/get-prop shape :id)]
(filter some?) (->> token-attributes
(into {}))] (filter #(token-attribute-applied? token shape %))
(merge-with into acc applied-ids-by-attribute))) (reduce (fn [result attr]
{} shapes)) (update result attr conj* shape-id))
result))))
{}
shapes)))
(defn shapes-applied-all? [ids-by-attributes shape-ids attributes] (defn shapes-applied-all? [ids-by-attributes shape-ids attributes]
(every? #(set/superset? (get ids-by-attributes %) shape-ids) attributes)) (every? #(set/superset? (get ids-by-attributes %) shape-ids) attributes))
@ -122,6 +128,11 @@
(defn color-token? [token] (defn color-token? [token]
(= (:type token) :color)) (= (:type token) :color))
;; FIXME: this should be precalculated ?
(defn is-reference? [token]
(str/includes? (:value token) "{"))
(defn color-bullet-color [token-color-value] (defn color-bullet-color [token-color-value]
(when-let [tc (tinycolor/valid-color token-color-value)] (when-let [tc (tinycolor/valid-color token-color-value)]
(if (tinycolor/alpha tc) (if (tinycolor/alpha tc)

View file

@ -1,14 +1,21 @@
;; 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.token-pill (ns app.main.ui.workspace.tokens.token-pill
(:require-macros (:require-macros
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.main.style :as stl]) [app.main.style :as stl])
(:require (:require
[app.common.data :as d]
[app.common.files.helpers :as cfh] [app.common.files.helpers :as cfh]
[app.common.types.tokens-lib :as ctob]
[app.main.refs :as refs] [app.main.refs :as refs]
[app.main.ui.components.color-bullet :refer [color-bullet]] [app.main.ui.components.color-bullet :refer [color-bullet]]
[app.main.ui.ds.foundations.assets.icon :refer [icon*]] [app.main.ui.ds.foundations.assets.icon :refer [icon*]]
[app.main.ui.ds.foundations.utilities.token.token-status :refer [token-status-icon*]] [app.main.ui.ds.foundations.utilities.token.token-status :refer [token-status-icon*]]
[app.main.ui.workspace.tokens.changes :as wtch]
[app.main.ui.workspace.tokens.token :as wtt] [app.main.ui.workspace.tokens.token :as wtt]
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.i18n :refer [tr]] [app.util.i18n :refer [tr]]
@ -75,9 +82,8 @@
;; Helper functions ;; Helper functions
(defn partially-applied-attr (defn partially-applied-attr
"Translates partially applied attributes based on the dictionary." "Translates partially applied attributes based on the dictionary."
[app-token-keys is-applied token-type-props] [app-token-keys is-applied {:keys [attributes all-attributes]}]
(let [{:keys [attributes all-attributes]} token-type-props (let [filtered-keys (if all-attributes
filtered-keys (if all-attributes
(filter #(contains? all-attributes %) app-token-keys) (filter #(contains? all-attributes %) app-token-keys)
(filter #(contains? attributes %) app-token-keys))] (filter #(contains? attributes %) app-token-keys))]
(when is-applied (when is-applied
@ -94,11 +100,11 @@
(str/join ", " (map attribute-dictionary values)) "."))) (str/join ", " (map attribute-dictionary values)) ".")))
grouped-values))) grouped-values)))
(defn token-pill-tooltip (defn- generate-tooltip
"Generates a tooltip for a given token." "Generates a tooltip for a given token"
[is-viewer shape token-type-props token half-applied no-valid-value ref-not-in-active-set] [is-viewer shape token half-applied no-valid-value ref-not-in-active-set]
(let [{:keys [name value resolved-value type]} token (let [{:keys [name value resolved-value type]} token
{:keys [title]} token-type-props {:keys [title] :as token-props} (wtch/get-token-properties token)
applied-tokens (:applied-tokens shape) applied-tokens (:applied-tokens shape)
app-token-vals (set (vals applied-tokens)) app-token-vals (set (vals applied-tokens))
app-token-keys (keys applied-tokens) app-token-keys (keys applied-tokens)
@ -106,7 +112,7 @@
applied-to (if half-applied applied-to (if half-applied
(partially-applied-attr app-token-keys is-applied? token-type-props) (partially-applied-attr app-token-keys is-applied? token-props)
(tr "labels.all")) (tr "labels.all"))
grouped-values (group-by dimensions-dictionary app-token-keys) grouped-values (group-by dimensions-dictionary app-token-keys)
@ -134,54 +140,94 @@
;; Otherwise only show the base title ;; Otherwise only show the base title
:else base-title))) :else base-title)))
;; FIXME: the token thould already have precalculated references, so
;; we don't need to perform this regex operation on each rerender
(defn contains-reference-value? (defn contains-reference-value?
"Extracts the value between `{}` in a string and checks if it's in the provided vector." "Extracts the value between `{}` in a string and checks if it's in the provided vector."
[text values] [text active-tokens]
(let [match (second (re-find #"\{([^}]+)\}" text))] (let [match (second (re-find #"\{([^}]+)\}" text))]
(boolean (some #(= % match) values)))) (contains? active-tokens match)))
(mf/defc token-pill (def ^:private
{::mf/wrap-props false} xf:map-id
[{:keys [on-click token theme-token full-applied on-context-menu half-applied selected-shapes token-type-props active-theme-tokens]}] (map :id))
(defn- applied-all-attributes?
[token selected-shapes attributes]
(let [ids-by-attributes (wtt/shapes-ids-by-applied-attributes token selected-shapes attributes)
shape-ids (into #{} xf:map-id selected-shapes)]
(wtt/shapes-applied-all? ids-by-attributes shape-ids attributes)))
(mf/defc token-pill*
{::mf/wrap [mf/memo]}
[{:keys [on-click token on-context-menu selected-shapes active-theme-tokens]}]
(let [{:keys [name value errors]} token (let [{:keys [name value errors]} token
is-reference (some #(= % "{") value)
has-selected? (pos? (count selected-shapes))
is-reference? (wtt/is-reference? token)
contains-path? (str/includes? name ".")
{:keys [attributes all-attributes]}
(get wtch/token-properties (:type token))
full-applied?
(if has-selected?
(applied-all-attributes? token selected-shapes (d/nilv all-attributes attributes))
true)
applied?
(if has-selected?
(wtt/shapes-token-applied? token selected-shapes (d/nilv all-attributes attributes))
false)
half-applied?
(and applied? (not full-applied?))
;; FIXME: move to context or props
can-edit? (:can-edit (deref refs/permissions)) can-edit? (:can-edit (deref refs/permissions))
is-viewer (not can-edit?) is-viewer? (not can-edit?)
ref-not-in-active-set (and is-reference
(not (contains-reference-value? value (keys active-theme-tokens)))) ref-not-in-active-set
(and is-reference?
(not (contains-reference-value? value active-theme-tokens)))
no-valid-value (seq errors) no-valid-value (seq errors)
errors? (or ref-not-in-active-set
no-valid-value) errors?
color (when (seq (ctob/find-token-value-references value)) (or ref-not-in-active-set
(wtt/resolved-token-bullet-color theme-token)) no-valid-value)
contains-path? (str/includes? name ".")
splitted-name (cfh/split-by-last-period name) color
color (or color (wtt/resolved-token-bullet-color token)) (when (wtt/color-token? token)
(let [theme-token (get active-theme-tokens (:name token))]
(or (wtt/resolved-token-bullet-color theme-token)
(wtt/resolved-token-bullet-color token))))
on-click on-click
(mf/use-callback (mf/use-fn
(mf/deps errors? on-click) (mf/deps errors? on-click token)
(fn [event] (fn [event]
(dom/stop-propagation event) (dom/stop-propagation event)
(when (and (not (seq errors)) on-click) (when (and (not (seq errors)) on-click)
(on-click event)))) (on-click event token))))
token-status-id (cond token-status-id
half-applied (cond
"token-status-partial" half-applied?
full-applied "token-status-partial"
"token-status-full" full-applied?
:else "token-status-full"
"token-status-non-applied") :else
"token-status-non-applied")
on-context-menu on-context-menu
(mf/use-fn (mf/use-fn
(mf/deps can-edit? on-context-menu) (mf/deps can-edit? on-context-menu token)
(fn [e] (fn [e]
(dom/stop-propagation e) (dom/stop-propagation e)
(when can-edit? (when can-edit?
(on-context-menu e)))) (on-context-menu e token))))
on-click on-click
(mf/use-fn (mf/use-fn
@ -191,26 +237,29 @@
(when (and can-edit? (not (seq errors)) on-click) (when (and can-edit? (not (seq errors)) on-click)
(on-click event)))) (on-click event))))
;; FIXME: missing deps
on-hover on-hover
(mf/use-fn (mf/use-fn
(mf/deps selected-shapes is-viewer) (mf/deps selected-shapes is-viewer?)
(fn [event] (fn [event]
(let [node (dom/get-current-target event) (let [node (dom/get-current-target event)
title (token-pill-tooltip is-viewer (first selected-shapes) token-type-props token half-applied no-valid-value ref-not-in-active-set)] title (generate-tooltip is-viewer? (first selected-shapes) token
half-applied? no-valid-value ref-not-in-active-set)]
(dom/set-attribute! node "title" title))))] (dom/set-attribute! node "title" title))))]
[:button {:class (stl/css-case :token-pill true [:button {:class (stl/css-case
:token-pill-default can-edit? :token-pill true
:token-pill-applied (and can-edit? (or half-applied full-applied)) :token-pill-default can-edit?
:token-pill-invalid (and can-edit? errors?) :token-pill-applied (and can-edit? has-selected? (or half-applied? full-applied?))
:token-pill-invalid-applied (and full-applied errors? can-edit?) :token-pill-invalid (and can-edit? errors?)
:token-pill-viewer is-viewer :token-pill-invalid-applied (and full-applied? errors? can-edit?)
:token-pill-applied-viewer (and is-viewer :token-pill-viewer is-viewer?
(or half-applied full-applied)) :token-pill-applied-viewer (and is-viewer? has-selected?
:token-pill-invalid-viewer (and is-viewer (or half-applied? full-applied?))
errors?) :token-pill-invalid-viewer (and is-viewer?
:token-pill-invalid-applied-viewer (and is-viewer errors?)
(and full-applied errors?))) :token-pill-invalid-applied-viewer (and is-viewer?
(and full-applied? errors?)))
:type "button" :type "button"
:on-click on-click :on-click on-click
:on-mouse-enter on-hover :on-mouse-enter on-hover
@ -220,6 +269,7 @@
[:> icon* [:> icon*
{:icon-id "broken-link" {:icon-id "broken-link"
:class (stl/css :token-pill-icon)}] :class (stl/css :token-pill-icon)}]
color color
[:& color-bullet {:color color [:& color-bullet {:color color
:mini true}] :mini true}]
@ -227,13 +277,13 @@
[:> token-status-icon* [:> token-status-icon*
{:icon-id token-status-id {:icon-id token-status-id
:class (stl/css :token-pill-icon)}]) :class (stl/css :token-pill-icon)}])
(if contains-path? (if contains-path?
[:span {:class (stl/css :divided-name-wrapper) (let [[first-part last-part] (cfh/split-by-last-period name)]
:aria-label name} [:span {:class (stl/css :divided-name-wrapper)
[:span {:class (stl/css :first-name-wrapper)} :aria-label name}
(first splitted-name)] [:span {:class (stl/css :first-name-wrapper)} first-part]
[:span {:class (stl/css :last-name-wrapper)} [:span {:class (stl/css :last-name-wrapper)} last-part]])
(last splitted-name)]]
[:span {:class (stl/css :name-wrapper) [:span {:class (stl/css :name-wrapper)
:aria-label name} :aria-label name}
name])])) name])]))

View file

@ -1,89 +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.token-types
(:require
[app.common.data :as d :refer [ordered-map]]
[app.common.types.token :as ctt]
[app.main.ui.workspace.tokens.changes :as wtch]
[clojure.set :as set]))
(def token-types
(ordered-map
:border-radius
{:title "Border Radius"
:attributes ctt/border-radius-keys
:on-update-shape wtch/update-shape-radius-all
:modal {:key :tokens/border-radius
:fields [{:label "Border Radius"
:key :border-radius}]}}
:color
{:title "Color"
:attributes #{:fill}
:all-attributes ctt/color-keys
:on-update-shape wtch/update-fill-stroke
:modal {:key :tokens/color
:fields [{:label "Color" :key :color}]}}
:stroke-width
{:title "Stroke Width"
:attributes ctt/stroke-width-keys
:on-update-shape wtch/update-stroke-width
:modal {:key :tokens/stroke-width
:fields [{:label "Stroke Width"
:key :stroke-width}]}}
:sizing
{:title "Sizing"
:attributes #{:width :height}
:all-attributes ctt/sizing-keys
:on-update-shape wtch/update-shape-dimensions
:modal {:key :tokens/sizing
:fields [{:label "Sizing"
:key :sizing}]}}
:dimensions
{:title "Dimensions"
:attributes #{:width :height}
:all-attributes (set/union
ctt/spacing-keys
ctt/sizing-keys
ctt/border-radius-keys
ctt/stroke-width-keys)
:on-update-shape wtch/update-shape-dimensions
:modal {:key :tokens/dimensions
:fields [{:label "Dimensions"
:key :dimensions}]}}
:opacity
{:title "Opacity"
:attributes ctt/opacity-keys
:on-update-shape wtch/update-opacity
:modal {:key :tokens/opacity
:fields [{:label "Opacity"
:key :opacity}]}}
:rotation
{:title "Rotation"
:attributes ctt/rotation-keys
:on-update-shape wtch/update-rotation
:modal {:key :tokens/rotation
:fields [{:label "Rotation"
:key :rotation}]}}
:spacing
{:title "Spacing"
:attributes #{:column-gap :row-gap}
:all-attributes ctt/spacing-keys
:on-update-shape wtch/update-layout-spacing
:modal {:key :tokens/spacing
:fields [{:label "Spacing"
:key :spacing}]}}))
(defn get-token-properties [token]
(get token-types (:type token)))
(defn token-attributes [token-type]
(get-in token-types [token-type :attributes]))

View file

@ -6,9 +6,10 @@
(ns app.util.array (ns app.util.array
"A collection of helpers for work with javascript arrays." "A collection of helpers for work with javascript arrays."
(:refer-clojure :exclude [conj! conj filter map reduce find]) (:refer-clojure :exclude [conj! conj filter map reduce find sort])
(:require (:require
[cljs.core :as c])) [cljs.core :as c]
[goog.array :as garray]))
(defn conj (defn conj
"A conj like function for js arrays." "A conj like function for js arrays."
@ -67,3 +68,9 @@
(defn find (defn find
[f v] [f v]
(.find ^js/Array v f)) (.find ^js/Array v f))
(defn sort!
[a]
(garray/sort a compare)
a)