mirror of
https://github.com/penpot/penpot.git
synced 2025-05-19 18:06:12 +02:00
Merge pull request #5766 from penpot/niwinz-tokens-changes
✨ Several changes to tokens
This commit is contained in:
commit
054efb3435
15 changed files with 440 additions and 330 deletions
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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)))
|
|
|
@ -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))))
|
||||||
|
|
|
@ -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))
|
||||||
|
|
||||||
|
|
|
@ -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?
|
||||||
|
|
|
@ -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]))
|
||||||
|
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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]]))
|
||||||
|
|
|
@ -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})))
|
||||||
|
|
|
@ -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}]]]))
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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])]))
|
|
@ -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]))
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue