From 3e2a6d7e0b2cdb0774d6f8e99d8b3a9c63a761ab Mon Sep 17 00:00:00 2001 From: Eva Marco Date: Thu, 16 Jan 2025 14:17:12 +0100 Subject: [PATCH] :sparkles: Add viewer role to theme selection --- .../ui/workspace/tokens/theme_select.cljs | 14 +- .../ui/workspace/tokens/theme_select.scss | 2 +- .../main/ui/workspace/tokens/token_pill.cljs | 183 ++++++++++++++++-- frontend/translations/en.po | 4 + frontend/translations/es.po | 4 + 5 files changed, 185 insertions(+), 22 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/theme_select.cljs b/frontend/src/app/main/ui/workspace/tokens/theme_select.cljs index 85a3e2eeb0..397bbb70b0 100644 --- a/frontend/src/app/main/ui/workspace/tokens/theme_select.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/theme_select.cljs @@ -78,7 +78,7 @@ active-theme-paths (mf/deref refs/workspace-active-theme-paths-no-hidden) active-themes-count (count active-theme-paths) themes (mf/deref refs/workspace-token-theme-tree-no-hidden) - + can-edit? (:can-edit (deref refs/permissions)) ;; Data current-label (cond (> active-themes-count 1) (tr "workspace.token.active-themes" active-themes-count) @@ -97,15 +97,23 @@ ;; Dropdown dropdown-element* (mf/use-ref nil) on-close-dropdown (mf/use-fn #(swap! state* assoc :is-open? false)) - on-open-dropdown (mf/use-fn #(swap! state* assoc :is-open? true))] + + on-open-dropdown + (mf/use-fn + (mf/deps can-edit?) + (fn [] + (when can-edit? + (swap! state* assoc :is-open? true))))] ;; TODO: This element should be accessible by keyboard [:div {:on-click on-open-dropdown + :disabled (not can-edit?) :aria-expanded is-open? :aria-haspopup "listbox" :tab-index "0" :role "combobox" - :class (stl/css :custom-select)} + :class (stl/css-case :custom-select true + :disabled-select (not can-edit?))} [:> text* {:as "span" :typography "body-small" :class (stl/css :current-label)} current-label] [:> icon* {:icon-id i/arrow-down :class (stl/css :dropdown-button) :aria-hidden true}] diff --git a/frontend/src/app/main/ui/workspace/tokens/theme_select.scss b/frontend/src/app/main/ui/workspace/tokens/theme_select.scss index 79c0f3fc2c..297b95f1f1 100644 --- a/frontend/src/app/main/ui/workspace/tokens/theme_select.scss +++ b/frontend/src/app/main/ui/workspace/tokens/theme_select.scss @@ -47,7 +47,7 @@ color: var(--color-foreground-secondary); } -.disabled { +.disabled-select { --custom-select-bg-color: var(--menu-background-color-disabled); --custom-select-border-color: var(--menu-border-color-disabled); --custom-select-icon-color: var(--menu-foreground-color-disabled); diff --git a/frontend/src/app/main/ui/workspace/tokens/token_pill.cljs b/frontend/src/app/main/ui/workspace/tokens/token_pill.cljs index 4c6d3910b6..14cbb273d7 100644 --- a/frontend/src/app/main/ui/workspace/tokens/token_pill.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/token_pill.cljs @@ -1,8 +1,11 @@ (ns app.main.ui.workspace.tokens.token-pill - (:require-macros [app.main.style :as stl]) + (:require-macros + [app.common.data.macros :as dm] + [app.main.style :as stl]) (:require [app.common.files.helpers :as cfh] [app.common.types.tokens-lib :as ctob] + [app.main.refs :as refs] [app.main.ui.components.color-bullet :refer [color-bullet]] [app.main.ui.ds.foundations.assets.icon :refer [icon*]] [app.main.ui.ds.foundations.utilities.token.token-status :refer [token-status-icon*]] @@ -13,16 +16,135 @@ [cuerdas.core :as str] [rumext.v2 :as mf])) +;; Translation dictionaries +(def ^:private attribute-dictionary + {:rotation "Rotation" + :opacity "Opacity" + :stroke-width "Stroke Width" + + ;; Spacing + :p1 "Top" :p2 "Right" :p3 "Bottom" :p4 "Left" + :column-gap "Column Gap" :row-gap "Row Gap" + + ;; Sizing + :width "Width" + :height "Height" + :layout-item-min-w "Min Width" + :layout-item-min-h "Min Height" + :layout-item-max-w "Max Width" + :layout-item-max-h "Max Height" + + ;; Border Radius + :r1 "Top Left" :r2 "Top Right" :r4 "Bottom Left" :r3 "Bottom Right" + + ;; Dimensions + :x "X" :y "Y" + + ;; Color + :fill "Fill" + :stroke-color "Stroke Color"}) + +(def ^:private dimensions-dictionary + {:stroke-width :stroke-width + :p1 :spacing + :p2 :spacing + :p3 :spacing + :p4 :spacing + :column-gap :spacing + :row-gap :spacing + :width :sizing + :height :sizing + :layout-item-min-w :sizing + :layout-item-min-h :sizing + :layout-item-max-w :sizing + :layout-item-max-h :sizing + :r1 :border-radius + :r2 :border-radius + :r4 :border-radius + :r3 :border-radius + :x :x + :y :y}) + +(def ^:private category-dictionary + {:stroke-width "Stroke Width" + :spacing "Spacing" + :sizing "Sizing" + :border-radius "Border Radius" + :x "X" + :y "Y"}) + +;; Helper functions +(defn partially-applied-attr + "Translates partially applied attributes based on the dictionary." + [app-token-keys is-applied token-type-props] + (let [{:keys [attributes all-attributes]} token-type-props + filtered-keys (if all-attributes + (filter #(contains? all-attributes %) app-token-keys) + (filter #(contains? attributes %) app-token-keys))] + (when is-applied + (str/join ", " (map attribute-dictionary filtered-keys))))) + +(defn translate-and-format + "Translates and formats grouped values by category." + [grouped-values] + (str/join "\n" + (map (fn [[category values]] + (if (#{:x :y} category) + (dm/str "- " (category-dictionary category)) + (dm/str "- " (category-dictionary category) ": " + (str/join ", " (map attribute-dictionary values)) "."))) + grouped-values))) + +(defn token-pill-tooltip + "Generates a tooltip for a given token." + [theme-token is-viewer shape token-type-props token half-applied] + (let [{:keys [name value resolved-value errors type]} token + {:keys [title]} token-type-props + applied-tokens (:applied-tokens shape) + app-token-vals (set (vals applied-tokens)) + app-token-keys (keys applied-tokens) + is-applied? (contains? app-token-vals name) + no-token-active (nil? theme-token) + errors? (or no-token-active (seq errors)) + applied-to (if half-applied + (partially-applied-attr app-token-keys is-applied? token-type-props) + (tr "labels.all")) + grouped-values (group-by dimensions-dictionary app-token-keys) + + base-title (dm/str "Token: " name "\n" + (tr "workspace.token.original-value" value) "\n" + (tr "workspace.token.resolved-value" resolved-value))] + + (cond + ;; If there are errors, show the appropriate message + errors? (if no-token-active + (tr "workspace.token-set.not-active") + (sd/humanize-errors token)) + ;; If the token is applied and the user is a is-viewer, show the details + (and is-applied? is-viewer) + (->> [base-title + (tr "workspace.token.applied-to") + (if (= :dimensions type) + (translate-and-format grouped-values) + (str "- " title ": " applied-to))] + (str/join "\n")) + ;; Otherwise only show the base title + :else base-title))) + (mf/defc token-pill {::mf/wrap-props false} - [{:keys [on-click token theme-token full-applied on-context-menu half-applied]}] - (let [{:keys [name value resolved-value errors]} token - errors? (or (nil? theme-token) (and (seq errors) (seq (:errors theme-token)))) - color (when (seq (ctob/find-token-value-references value)) - (wtt/resolved-value-hex theme-token)) + [{:keys [on-click token theme-token full-applied on-context-menu half-applied selected-shapes token-type-props]}] + (let [{:keys [name value errors]} token + + can-edit? (:can-edit (deref refs/permissions)) + is-viewer (not can-edit?) + errors? (or (nil? theme-token) (and (seq errors) (seq (:errors theme-token)))) + color (when (seq (ctob/find-token-value-references value)) + (wtt/resolved-value-hex theme-token)) contains-path? (str/includes? name ".") splitted-name (cfh/split-by-last-period name) color (or color (wtt/resolved-value-hex token)) + on-click (mf/use-callback (mf/deps errors? on-click) @@ -37,21 +159,46 @@ full-applied "token-status-full" :else - "token-status-non-applied")] + "token-status-non-applied") + + on-context-menu + (mf/use-fn + (mf/deps can-edit? on-context-menu) + (fn [e] + (dom/stop-propagation e) + (when can-edit? + (on-context-menu e)))) + + on-click + (mf/use-fn + (mf/deps errors? on-click) + (fn [event] + (dom/stop-propagation event) + (when (and can-edit? (not (seq errors)) on-click) + (on-click event)))) + + on-hover + (mf/use-fn + (mf/deps selected-shapes is-viewer) + (fn [event] + (let [node (dom/get-current-target event) + title (token-pill-tooltip theme-token is-viewer (first selected-shapes) token-type-props token half-applied)] + (dom/set-attribute! node "title" title))))] + [:button {:class (stl/css-case :token-pill true - :token-pill-applied (or half-applied full-applied) - :token-pill-invalid errors? - :token-pill-invalid-applied (and full-applied errors?)) + :token-pill-applied (and can-edit? (or half-applied full-applied)) + :token-pill-invalid (and can-edit? errors?) + :token-pill-invalid-applied (and full-applied errors? can-edit?) + :token-pill-viewer is-viewer + :token-pill-applied-viewer (and is-viewer + (or half-applied full-applied)) + :token-pill-invalid-viewer (and is-viewer + errors?) + :token-pill-invalid-applied-viewer (and is-viewer + (and full-applied errors?))) :type "button" - :title (cond - errors? (if (nil? theme-token) - (tr "workspace.token-set.not-active") - (sd/humanize-errors token)) - :else (->> [(str "Token: " name) - (tr "workspace.token.original-value" value) - (tr "workspace.token.resolved-value" resolved-value)] - (str/join "\n"))) :on-click on-click + :on-mouse-enter on-hover :on-context-menu on-context-menu} (cond errors? diff --git a/frontend/translations/en.po b/frontend/translations/en.po index d73a7639f8..04b3cadf66 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -6717,6 +6717,10 @@ msgstr "Add set" msgid "workspace.token.tools" msgstr "Tools" +#: src/app/main/ui/workspace/tokens/sidebar.cljs +msgid "workspace.token.no-permission-themes" +msgstr "You need to be an editor to use themes" + #: src/app/main/ui/workspace/tokens/modals/themes.cljs msgid "workspace.token.save-theme" msgstr "Save theme" diff --git a/frontend/translations/es.po b/frontend/translations/es.po index 00f61a63cd..2feb724dc0 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -6729,6 +6729,10 @@ msgstr "AƱadir set" msgid "workspace.token.tools" msgstr "Herramientas" +#: src/app/main/ui/workspace/tokens/sidebar.cljs +msgid "workspace.token.no-permission-themes" +msgstr "Debes ser editor para usar temas" + #: src/app/main/ui/workspace/tokens/modals/themes.cljs msgid "workspace.token.save-theme" msgstr "Guardar tema"