Merge pull request #313 from tokens-studio/eva-review-themes

♻️  Review themes section
This commit is contained in:
Andrés Moya 2024-10-24 14:50:56 +02:00 committed by GitHub
commit cd7763ca08
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 665 additions and 443 deletions

View file

@ -0,0 +1,3 @@
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" stroke-linecap="round" stroke-linejoin="round">
<path d="m4 6 4 4 4-4"/>
</svg>

After

Width:  |  Height:  |  Size: 144 B

View file

@ -0,0 +1,3 @@
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" stroke-linecap="round" stroke-linejoin="round">
<path d="M10 12 6 8l4-4"/>
</svg>

After

Width:  |  Height:  |  Size: 145 B

View file

@ -0,0 +1,3 @@
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" stroke-linecap="round" stroke-linejoin="round">
<path d="m6 12 4-4-4-4"/>
</svg>

After

Width:  |  Height:  |  Size: 144 B

View file

@ -0,0 +1,3 @@
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" stroke-linecap="round" stroke-linejoin="round">
<path d="m4 10 4-4 4 4"/>
</svg>

After

Width:  |  Height:  |  Size: 140 B

View file

@ -50,6 +50,10 @@
(def ^:icon-id align-top "align-top") (def ^:icon-id align-top "align-top")
(def ^:icon-id align-vertical-center "align-vertical-center") (def ^:icon-id align-vertical-center "align-vertical-center")
(def ^:icon-id arrow "arrow") (def ^:icon-id arrow "arrow")
(def ^:icon-id arrow-up "arrow-up")
(def ^:icon-id arrow-down "arrow-down")
(def ^:icon-id arrow-left "arrow-left")
(def ^:icon-id arrow-right "arrow-right")
(def ^:icon-id asc-sort "asc-sort") (def ^:icon-id asc-sort "asc-sort")
(def ^:icon-id board "board") (def ^:icon-id board "board")
(def ^:icon-id boards-thumbnail "boards-thumbnail") (def ^:icon-id boards-thumbnail "boards-thumbnail")

View file

@ -32,17 +32,17 @@
(let [level (or level "1") (let [level (or level "1")
tag (dm/str "h" level) tag (dm/str "h" level)
class (dm/str (or class "") " " (stl/css-case :display-typography (= typography t/display) class (dm/str class " " (stl/css-case :display-typography (= typography t/display)
:title-large-typography (= typography t/title-large) :title-large-typography (= typography t/title-large)
:title-medium-typography (= typography t/title-medium) :title-medium-typography (= typography t/title-medium)
:title-small-typography (= typography t/title-small) :title-small-typography (= typography t/title-small)
:headline-large-typography (= typography t/headline-large) :headline-large-typography (= typography t/headline-large)
:headline-medium-typography (= typography t/headline-medium) :headline-medium-typography (= typography t/headline-medium)
:headline-small-typography (= typography t/headline-small) :headline-small-typography (= typography t/headline-small)
:body-large-typography (= typography t/body-large) :body-large-typography (= typography t/body-large)
:body-medium-typography (= typography t/body-medium) :body-medium-typography (= typography t/body-medium)
:body-small-typography (= typography t/body-small) :body-small-typography (= typography t/body-small)
:code-font-typography (= typography t/code-font))) :code-font-typography (= typography t/code-font)))
props (mf/spread-props props {:class class})] props (mf/spread-props props {:class class})]
[:> tag props [:> tag props
children])) children]))

View file

@ -19,6 +19,7 @@
[app.main.ui.components.color-bullet :refer [color-bullet]] [app.main.ui.components.color-bullet :refer [color-bullet]]
[app.main.ui.ds.buttons.button :refer [button*]] [app.main.ui.ds.buttons.button :refer [button*]]
[app.main.ui.ds.foundations.assets.icon :as i] [app.main.ui.ds.foundations.assets.icon :as i]
[app.main.ui.ds.foundations.typography.heading :refer [heading*]]
[app.main.ui.ds.foundations.typography.text :refer [text*]] [app.main.ui.ds.foundations.typography.text :refer [text*]]
[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]]
@ -353,14 +354,14 @@ Token names should only contain letters and digits separated by . characters.")}
[:form {:class (stl/css :form-wrapper) [:form {:class (stl/css :form-wrapper)
:on-submit on-submit} :on-submit on-submit}
[:div {:class (stl/css :token-rows)} [:div {:class (stl/css :token-rows)}
[:> text* {:as "span" :typography "headline-medium"} [:> heading* {:level 2 :typography "headline-medium" :class (stl/css :form-modal-title)}
(if (= action "edit") (if (= action "edit")
(tr "workspace.token.edit-token") (tr "workspace.token.edit-token")
(tr "workspace.token.create-token" token-type))] (tr "workspace.token.create-token" token-type))]
[:div {:class (stl/css :input-row)} [:div {:class (stl/css :input-row)}
;; This should be remove when labeled-imput is modified ;; This should be remove when labeled-imput is modified
[:span "Name"] [:span {:class (stl/css :labeled-input-label)} "Name"]
[:& tokens.common/labeled-input {:label "Name" [:& tokens.common/labeled-input {:label "Name"
:error? @name-errors :error? @name-errors
:input-props {:default-value @name-ref :input-props {:default-value @name-ref
@ -378,7 +379,7 @@ Token names should only contain letters and digits separated by . characters.")}
[:div {:class (stl/css :input-row)} [:div {:class (stl/css :input-row)}
;; This should be remove when labeled-imput is modified ;; This should be remove when labeled-imput is modified
[:span "value"] [:span {:class (stl/css :labeled-input-label)} "value"]
[:& tokens.common/labeled-input {:label "Value" [:& tokens.common/labeled-input {:label "Value"
:input-props {:default-value @value-ref :input-props {:default-value @value-ref
:on-blur on-update-value :on-blur on-update-value
@ -401,7 +402,7 @@ Token names should only contain letters and digits separated by . characters.")}
[:div {:class (stl/css :input-row)} [:div {:class (stl/css :input-row)}
;; This should be remove when labeled-imput is modified ;; This should be remove when labeled-imput is modified
[:span "Description"] [:span {:class (stl/css :labeled-input-label)} "Description"]
[:& tokens.common/labeled-input {:label "Description" [:& tokens.common/labeled-input {:label "Description"
:input-props {:default-value @description-ref :input-props {:default-value @description-ref
:on-change on-update-description}}] :on-change on-update-description}}]
@ -416,10 +417,12 @@ Token names should only contain letters and digits separated by . characters.")}
(when (= action "edit") (when (= action "edit")
[:> button* {:on-click on-delete-token [:> button* {:on-click on-delete-token
:class (stl/css :delete-btn) :class (stl/css :delete-btn)
:type "button"
:icon i/delete :icon i/delete
:variant "secondary"} :variant "secondary"}
(tr "labels.delete")]) (tr "labels.delete")])
[:> button* {:on-click on-cancel [:> button* {:on-click on-cancel
:type "button"
:variant "secondary"} :variant "secondary"}
(tr "labels.cancel")] (tr "labels.cancel")]
[:> button* {:type "submit" [:> button* {:type "submit"

View file

@ -39,6 +39,10 @@
gap: $s-4; gap: $s-4;
} }
.labeled-input-label {
color: var(--color-foreground-primary);
}
.error { .error {
padding: $s-4 $s-6; padding: $s-4 $s-6;
margin-bottom: 0; margin-bottom: 0;
@ -75,3 +79,7 @@
border-radius: $br-4; border-radius: $br-4;
cursor: pointer; cursor: pointer;
} }
.form-modal-title {
color: var(--color-foreground-primary);
}

View file

@ -12,7 +12,6 @@
[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.assets.icon :as i] [app.main.ui.ds.foundations.assets.icon :as i]
[app.main.ui.workspace.tokens.form :refer [form]] [app.main.ui.workspace.tokens.form :refer [form]]
[app.main.ui.workspace.tokens.modals.themes :as wtmt]
[app.util.i18n :refer [tr]] [app.util.i18n :refer [tr]]
[okulary.core :as l] [okulary.core :as l]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
@ -60,12 +59,6 @@
:selected-token-set-id selected-token-set-id :selected-token-set-id selected-token-set-id
:token-type token-type}]])) :token-type token-type}]]))
(mf/defc token-themes-modal
{::mf/register modal/components
::mf/register-as :tokens/themes}
[args]
[:& wtmt/modal args])
;; Modals ---------------------------------------------------------------------- ;; Modals ----------------------------------------------------------------------
(mf/defc boolean-modal (mf/defc boolean-modal

View file

@ -7,36 +7,49 @@
(ns app.main.ui.workspace.tokens.modals.themes (ns app.main.ui.workspace.tokens.modals.themes
(:require-macros [app.main.style :as stl]) (:require-macros [app.main.style :as stl])
(:require (:require
[app.common.data.macros :as dm]
[app.common.types.tokens-lib :as ctob] [app.common.types.tokens-lib :as ctob]
[app.main.data.modal :as modal] [app.main.data.modal :as modal]
[app.main.data.tokens :as wdt] [app.main.data.tokens :as wdt]
[app.main.refs :as refs] [app.main.refs :as refs]
[app.main.store :as st] [app.main.store :as st]
[app.main.ui.components.radio-buttons :refer [radio-button radio-buttons]] [app.main.ui.components.radio-buttons :refer [radio-button radio-buttons]]
[app.main.ui.ds.buttons.button :refer [button*]]
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
[app.main.ui.ds.foundations.assets.icon :refer [icon*] :as ic]
[app.main.ui.ds.foundations.typography.heading :refer [heading*]]
[app.main.ui.ds.foundations.typography.text :refer [text*]]
[app.main.ui.icons :as i] [app.main.ui.icons :as i]
[app.main.ui.workspace.tokens.common :refer [labeled-input] :as wtco] [app.main.ui.workspace.tokens.common :refer [labeled-input] :as wtco]
[app.main.ui.workspace.tokens.sets :as wts] [app.main.ui.workspace.tokens.sets :as wts]
[app.main.ui.workspace.tokens.sets-context :as sets-context] [app.main.ui.workspace.tokens.sets-context :as sets-context]
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.i18n :refer [tr]]
[cuerdas.core :as str] [cuerdas.core :as str]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
(def ^:private chevron-icon
(i/icon-xref :arrow (stl/css :chevron-icon)))
(def ^:private close-icon
(i/icon-xref :close (stl/css :close-icon)))
(mf/defc empty-themes (mf/defc empty-themes
[{:keys [set-state]}] [{:keys [set-state]}]
[:div {:class (stl/css :empty-themes-wrapper)} (let [create-theme
[:div {:class (stl/css :empty-themes-message)} (mf/use-fn
[:h1 "You currently have no themes."] (mf/deps set-state)
[:p "Create your first theme now."]] #(set-state (fn [_] {:type :create-theme})))]
[:div {:class (stl/css :button-footer)} [:div {:class (stl/css :themes-modal-wrapper)}
[:button {:class (stl/css :button-primary) [:> heading* {:level 2 :typography "headline-medium" :class (stl/css :themes-modal-title)}
:on-click #(set-state (fn [_] {:type :create-theme}))} (tr "workspace.token.themes")]
"New theme"]]]) [:div {:class (stl/css :empty-themes-wrapper)}
[:div {:class (stl/css :empty-themes-message)}
[:> text* {:as "span" :typography "title-medium" :class (stl/css :empty-theme-title)}
(tr "workspace.token.no-themes-currently")]
[:> text* {:as "span"
:class (stl/css :empty-theme-subtitle)
:typography "body-medium"}
(tr "workspace.token.create-new-theme")]]
[:div {:class (stl/css :button-footer)}
[:> button* {:variant "primary"
:type "button"
:on-click create-theme}
(tr "workspace.token.new-theme")]]]]))
(mf/defc switch (mf/defc switch
[{:keys [selected? name on-change]}] [{:keys [selected? name on-change]}]
@ -57,60 +70,195 @@
[{:keys [set-state]}] [{:keys [set-state]}]
(let [active-theme-ids (mf/deref refs/workspace-active-theme-paths) (let [active-theme-ids (mf/deref refs/workspace-active-theme-paths)
themes-groups (mf/deref refs/workspace-token-theme-tree-no-hidden) themes-groups (mf/deref refs/workspace-token-theme-tree-no-hidden)
on-edit-theme (fn [theme e]
(dom/prevent-default e) create-theme
(dom/stop-propagation e) (mf/use-fn
(set-state (fn [_] {:type :edit-theme (mf/deps set-state)
:theme-path [(:id theme) (:group theme) (:name theme)]})))] (fn [e]
[:div (dom/prevent-default e)
(dom/stop-propagation e)
(set-state (fn [_] {:type :create-theme}))))]
[:div {:class (stl/css :themes-modal-wrapper)}
[:> heading* {:level 2 :typography "headline-medium" :class (stl/css :themes-modal-title)}
(tr "workspace.token.themes")]
[:ul {:class (stl/css :theme-group-wrapper)} [:ul {:class (stl/css :theme-group-wrapper)}
(for [[group themes] themes-groups] (for [[group themes] themes-groups]
[:li {:key (str "token-theme-group" group)} [:li {:key (dm/str "token-theme-group" group)}
(when (seq group) (when (seq group)
[:span {:class (stl/css :theme-group-label)} group]) [:> heading* {:level 3
:class (stl/css :theme-group-label)
:typography "body-large"}
[:span {:class (stl/css :group-title)}
[:> icon* {:id "group"}]
group]])
[:ul {:class (stl/css :theme-group-rows-wrapper)} [:ul {:class (stl/css :theme-group-rows-wrapper)}
(for [[_ {:keys [group name] :as theme}] themes (for [[_ {:keys [group name] :as theme}] themes
:let [theme-id (ctob/theme-path theme) :let [theme-id (ctob/theme-path theme)
selected? (some? (get active-theme-ids theme-id))]] selected? (some? (get active-theme-ids theme-id))
delete-theme
(fn [e]
(dom/prevent-default e)
(dom/stop-propagation e)
(st/emit! (wdt/delete-token-theme group name)))
on-edit-theme
(fn [e]
(dom/prevent-default e)
(dom/stop-propagation e)
(set-state (fn [_] {:type :edit-theme
:theme-path [(:id theme) (:group theme) (:name theme)]})))]]
[:li {:key theme-id [:li {:key theme-id
:class (stl/css :theme-row)} :class (stl/css :theme-row)}
[:div {:class (stl/css :theme-row-left)} [:div {:class (stl/css :theme-row-left)}
;; FIREEEEEEEEEE THIS
[:div {:on-click (fn [e] [:div {:on-click (fn [e]
(dom/prevent-default e) (dom/prevent-default e)
(dom/stop-propagation e) (dom/stop-propagation e)
(st/emit! (wdt/toggle-token-theme-active? group name)))} (st/emit! (wdt/toggle-token-theme-active? group name)))}
[:& switch {:name (str "Theme" name) [:& switch {:name (tr "workspace.token.theme" name)
:on-change (constantly nil) :on-change (constantly nil)
:selected? selected?}]] :selected? selected?}]]
[:span {:class (stl/css :theme-row-label)} name]] [:> text* {:as "span" :typography "body-medium" :class (stl/css :theme-name)} name]]
[:div {:class (stl/css :theme-row-right)} [:div {:class (stl/css :theme-row-right)}
(if-let [sets-count (some-> theme :sets seq count)] (if-let [sets-count (some-> theme :sets seq count)]
[:button {:class (stl/css :sets-count-button) [:> button* {:class (stl/css :sets-count-button)
:on-click #(on-edit-theme theme %)} :variant "secondary"
(str sets-count " sets") :type "button"
chevron-icon] :on-click on-edit-theme}
[:button {:class (stl/css :sets-count-empty-button) [:div {:class (stl/css :label-wrapper)}
:on-click #(on-edit-theme theme %)} [:> text* {:as "span" :typography "body-medium"}
"No sets defined" (tr "workspace.token.num-sets" sets-count)]
chevron-icon]) [:> icon* {:id "arrow-right"}]]]
[:div {:class (stl/css :delete-theme-button)}
[:button {:on-click (fn [e]
(dom/prevent-default e)
(dom/stop-propagation e)
(st/emit! (wdt/delete-token-theme group name)))}
i/delete]]]])]])]
[:div {:class (stl/css :button-footer)}
[:button {:class (stl/css :create-theme-button)
:on-click (fn [e]
(dom/prevent-default e)
(dom/stop-propagation e)
(set-state (fn [_] {:type :create-theme})))}
i/add
"Create theme"]]]))
(mf/defc edit-theme [:> button* {:class (stl/css :sets-count-empty-button)
[{:keys [edit? token-sets theme theme-groups on-back on-submit]}] :type "button"
(let [{:keys [dropdown-open? on-open-dropdown on-close-dropdown on-toggle-dropdown]} (wtco/use-dropdown-open-state) :variant "secondary"
:on-click on-edit-theme}
[:div {:class (stl/css :label-wrapper)}
[:> text* {:as "span" :typography "body-medium"}
(tr "workspace.token.no-sets")]
[:> icon* {:id "arrow-right"}]]])
[:> icon-button* {:on-click delete-theme
:variant "ghost"
:aria-label (tr "workspace.token.delete-theme-title")
:icon "delete"}]]])]])]
[:div {:class (stl/css :button-footer)}
[:> button* {:variant "primary"
:type "button"
:icon "add"
:on-click create-theme}
(tr "workspace.token.create-theme-title")]]]))
(mf/defc theme-inputs
[{:keys [theme dropdown-open? on-close-dropdown on-toggle-dropdown on-change-field]}]
(let [theme-groups (mf/deref refs/workspace-token-theme-groups)
group-input-ref (mf/use-ref)
on-update-group (partial on-change-field :group)
on-update-name (partial on-change-field :name)]
[:div {:class (stl/css :edit-theme-inputs-wrapper)}
[:div {:class (stl/css :group-input-wrapper)}
(when dropdown-open?
[:& wtco/dropdown-select {:id ::groups-dropdown
:shortcuts-key ::groups-dropdown
:options (map (fn [group]
{:label group
:value group})
theme-groups)
:on-select (fn [{:keys [value]}]
(set! (.-value (mf/ref-val group-input-ref)) value)
(on-update-group value))
:on-close on-close-dropdown}])
;; TODO: This span should be remove when labeled-input is updated
[:span {:class (stl/css :labeled-input-label)} "Theme group"]
[:& labeled-input {:label "Group"
:input-props {:ref group-input-ref
:default-value (:group theme)
:on-change (comp on-update-group dom/get-target-val)}
:render-right (when (seq theme-groups)
(mf/fnc []
[:button {:class (stl/css :group-drop-down-button)
:type "button"
:on-click (fn [e]
(dom/stop-propagation e)
(on-toggle-dropdown))}
[:> icon* {:id "arrow-down"}]]))}]]
[:div {:class (stl/css :group-input-wrapper)}
;; TODO: This span should be remove when labeled-input is updated
[:span {:class (stl/css :labeled-input-label)} "Theme"]
[:& labeled-input {:label "Theme"
:input-props {:default-value (:name theme)
:on-change (comp on-update-name dom/get-target-val)}}]]]))
(mf/defc theme-modal-buttons
[{:keys [close-modal on-save-form disabled?] :as props}]
[:*
[:> button* {:variant "secondary"
:type "button"
:on-click close-modal}
(tr "labels.cancel")]
[:> button* {:variant "primary"
:type "submit"
:on-click on-save-form
:disabled disabled?}
(tr "workspace.token.save-theme")]])
(mf/defc create-theme
[{:keys [set-state]}]
(let [{:keys [dropdown-open? _on-open-dropdown on-close-dropdown on-toggle-dropdown]} (wtco/use-dropdown-open-state)
theme (ctob/make-token-theme :name "")
on-back #(set-state (constantly {:type :themes-overview}))
on-submit #(st/emit! (wdt/create-token-theme %))
theme-state (mf/use-state theme)
disabled? (-> (:name @theme-state)
(str/trim)
(str/empty?))
on-change-field (fn [field value]
(swap! theme-state #(assoc % field value)))
on-save-form (mf/use-callback
(mf/deps theme-state on-submit)
(fn [e]
(dom/prevent-default e)
(let [theme (-> @theme-state
(update :name str/trim)
(update :group str/trim)
(update :description str/trim))]
(when-not (str/empty? (:name theme))
(on-submit theme)))
(on-back)))
close-modal (mf/use-fn
(fn [e]
(dom/prevent-default e)
(st/emit! (modal/hide))))]
[:div {:class (stl/css :themes-modal-wrapper)}
[:> heading* {:level 2 :typography "headline-medium" :class (stl/css :themes-modal-title)}
(tr "workspace.token.create-theme-title")]
[:form {:on-submit on-save-form}
[:div {:class (stl/css :create-theme-wrapper)}
[:& theme-inputs {:dropdown-open? dropdown-open?
:on-close-dropdown on-close-dropdown
:on-toggle-dropdown on-toggle-dropdown
:theme theme
:on-change-field on-change-field}]
[:div {:class (stl/css :button-footer)}
[:& theme-modal-buttons {:close-modal close-modal
:on-save-form on-save-form
:disabled? disabled?}]]]]]))
(mf/defc controlled-edit-theme
[{:keys [state set-state]}]
(let [{:keys [theme-path]} @state
[_ theme-group theme-name] theme-path
token-sets (mf/deref refs/workspace-ordered-token-sets)
theme (mf/deref (refs/workspace-token-theme theme-group theme-name))
on-back #(set-state (constantly {:type :themes-overview}))
on-submit #(st/emit! (wdt/update-token-theme [(:group theme) (:name theme)] %))
{:keys [dropdown-open? _on-open-dropdown on-close-dropdown on-toggle-dropdown]} (wtco/use-dropdown-open-state)
theme-state (mf/use-state theme) theme-state (mf/use-state theme)
disabled? (-> (:name @theme-state) disabled? (-> (:name @theme-state)
(str/trim) (str/trim)
@ -125,9 +273,6 @@
(swap! theme-state #(ctob/toggle-set % set-name)))) (swap! theme-state #(ctob/toggle-set % set-name))))
on-change-field (fn [field value] on-change-field (fn [field value]
(swap! theme-state #(assoc % field value))) (swap! theme-state #(assoc % field value)))
group-input-ref (mf/use-ref)
on-update-group (partial on-change-field :group)
on-update-name (partial on-change-field :name)
on-save-form (mf/use-callback on-save-form (mf/use-callback
(mf/deps theme-state on-submit) (mf/deps theme-state on-submit)
(fn [e] (fn [e]
@ -138,124 +283,83 @@
(update :description str/trim))] (update :description str/trim))]
(when-not (str/empty? (:name theme)) (when-not (str/empty? (:name theme))
(on-submit theme))) (on-submit theme)))
(on-back)))] (on-back)))
[:form {:on-submit on-save-form} close-modal
[:div {:class (stl/css :edit-theme-wrapper)} (mf/use-fn
[:div (fn [e]
[:button {:class (stl/css :back-button) (dom/prevent-default e)
:type "button" (st/emit! (modal/hide))))
:on-click on-back}
chevron-icon "Back"]]
[:div {:class (stl/css :edit-theme-inputs-wrapper)}
[:div {:class (stl/css :group-input-wrapper)}
(when dropdown-open?
[:& wtco/dropdown-select {:id ::groups-dropdown
:shortcuts-key ::groups-dropdown
:options (map (fn [group]
{:label group
:value group})
theme-groups)
:on-select (fn [{:keys [value]}]
(set! (.-value (mf/ref-val group-input-ref)) value)
(on-update-group value))
:on-close on-close-dropdown}])
[:& labeled-input {:label "Group"
:input-props {:ref group-input-ref
:default-value (:group theme)
:on-change (comp on-update-group dom/get-target-val)}
:render-right (when (seq theme-groups)
(mf/fnc []
[:button {:class (stl/css :group-drop-down-button)
:type "button"
:on-click (fn [e]
(dom/stop-propagation e)
(on-toggle-dropdown))}
i/arrow]))}]]
[:& labeled-input {:label "Theme"
:input-props {:default-value (:name theme)
:on-change (comp on-update-name dom/get-target-val)}}]]
[:div {:class (stl/css :sets-list-wrapper)}
[:& wts/controlled-sets-list
{:token-sets token-sets
:token-set-selected? (constantly false)
:token-set-active? token-set-active?
:on-select on-toggle-token-set
:on-toggle-token-set on-toggle-token-set
:context sets-context/static-context}]]
[:div {:class (stl/css :edit-theme-footer)}
(if edit?
[:button {:class (stl/css :button-secondary)
:type "button"
:on-click (fn []
(st/emit! (wdt/delete-token-theme (:group theme) (:name theme)))
(on-back))}
"Delete"]
[:div])
[:div {:class (stl/css :button-footer)}
[:button {:class (stl/css :button-secondary)
:type "button"
:on-click #(st/emit! (modal/hide))}
"Cancel"]
[:button {:class (stl/css :button-primary)
:type "submit"
:on-click on-save-form
:disabled disabled?}
"Save theme"]]]]]))
(mf/defc controlled-edit-theme on-delete-token
[{:keys [state set-state]}] (mf/use-fn
(let [{:keys [theme-path]} @state (mf/deps theme on-back)
[_ theme-group theme-name] theme-path (fn []
token-sets (mf/deref refs/workspace-ordered-token-sets) (st/emit! (wdt/delete-token-theme (:group theme) (:name theme)))
theme (mf/deref (refs/workspace-token-theme theme-group theme-name)) (on-back)))]
theme-groups (mf/deref refs/workspace-token-theme-groups)]
[:& edit-theme
{:edit? true
:token-sets token-sets
:theme theme
:theme-groups theme-groups
:on-back #(set-state (constantly {:type :themes-overview}))
:on-submit #(st/emit! (wdt/update-token-theme [(:group theme) (:name theme)] %))}]))
(mf/defc create-theme [:div {:class (stl/css :themes-modal-wrapper)}
[{:keys [set-state]}] [:> heading* {:level 2 :typography "headline-medium" :class (stl/css :themes-modal-title)}
(let [token-sets (mf/deref refs/workspace-ordered-token-sets) (tr "workspace.token.edit-theme-title")]
theme (ctob/make-token-theme :name "")
theme-groups (mf/deref refs/workspace-token-theme-groups)]
[:& edit-theme
{:edit? false
:token-sets token-sets
:theme theme
:theme-groups theme-groups
:on-back #(set-state (constantly {:type :themes-overview}))
:on-submit #(st/emit! (wdt/create-token-theme %))}]))
(mf/defc themes [:form {:on-submit on-save-form}
[:div {:class (stl/css :edit-theme-wrapper)}
[:button {:on-click on-back
:class (stl/css :back-btn)
:type "button"}
[:> icon* {:id ic/arrow-left :aria-hidden true}]
(tr "workspace.token.back-to-themes")]
[:& theme-inputs {:dropdown-open? dropdown-open?
:on-close-dropdown on-close-dropdown
:on-toggle-dropdown on-toggle-dropdown
:theme theme
:on-change-field on-change-field}]
[:div {:class (stl/css :sets-list-wrapper)}
[:& wts/controlled-sets-list
{:token-sets token-sets
:token-set-selected? (constantly false)
:token-set-active? token-set-active?
:on-select on-toggle-token-set
:on-toggle-token-set on-toggle-token-set
:context sets-context/static-context}]]
[:div {:class (stl/css :edit-theme-footer)}
[:> button* {:variant "secondary"
:type "button"
:on-click on-delete-token}
(tr "labels.delete")]
[:div {:class (stl/css :button-footer)}
[:& theme-modal-buttons {:close-modal close-modal
:on-save-form on-save-form
:disabled? disabled?}]]]]]]))
(mf/defc themes-modal-body
[_] [_]
(let [themes (mf/deref refs/workspace-token-themes-no-hidden) (let [themes (mf/deref refs/workspace-token-themes-no-hidden)
state (mf/use-state (if (empty? themes) state (mf/use-state (if (empty? themes)
{:type :create-theme} {:type :create-theme}
{:type :themes-overview})) {:type :themes-overview}))
set-state (mf/use-callback #(swap! state %)) set-state (mf/use-callback #(swap! state %))
title (case (:type @state)
:edit-theme "Edit Theme"
"Themes")
component (case (:type @state) component (case (:type @state)
:empty-themes empty-themes :empty-themes empty-themes
:themes-overview (if (empty? themes) empty-themes themes-overview) :themes-overview (if (empty? themes) empty-themes themes-overview)
:edit-theme controlled-edit-theme :edit-theme controlled-edit-theme
:create-theme create-theme)] :create-theme create-theme)]
[:div [:& component {:state state
[:div {:class (stl/css :modal-title)} title] :set-state set-state}]))
[:div {:class (stl/css :modal-content)}
[:& component {:state state
:set-state set-state}]]]))
(mf/defc modal (mf/defc token-themes-modal
{::mf/wrap-props false} {::mf/wrap-props false
[_] ::mf/register modal/components
::mf/register-as :tokens/themes}
[_args]
(let [handle-close-dialog (mf/use-callback #(st/emit! (modal/hide)))] (let [handle-close-dialog (mf/use-callback #(st/emit! (modal/hide)))]
[:div {:class (stl/css :modal-overlay)} [:div {:class (stl/css :modal-overlay)}
[:div {:class (stl/css :modal-dialog)} [:div {:class (stl/css :modal-dialog)}
[:button {:class (stl/css :close-btn) :on-click handle-close-dialog} close-icon] [:> icon-button* {:class (stl/css :close-btn)
[:& themes]]])) :on-click handle-close-dialog
:aria-label (tr "labels.close")
:variant "action"
:icon "close"}]
[:& themes-modal-body]]]))

View file

@ -10,10 +10,6 @@
@extend .modal-overlay-base; @extend .modal-overlay-base;
} }
hr {
border-color: var(--color-background-tertiary);
}
.modal-dialog { .modal-dialog {
@extend .modal-container-base; @extend .modal-container-base;
display: grid; display: grid;
@ -23,16 +19,43 @@ hr {
user-select: none; user-select: none;
} }
.modal-title { .empty-themes-message {
@include headlineMediumTypography;
font-weight: 500;
margin-block-end: $s-16;
color: var(--color-foreground-secondary);
}
.modal-content {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center;
align-items: center;
gap: $s-12;
padding: $s-72 0;
}
.themes-modal-wrapper {
display: flex;
flex-direction: column;
gap: $s-24;
}
.themes-modal-title {
color: var(--color-foreground-primary);
}
.back-btn {
background-color: transparent;
border: none;
appearance: none;
color: var(--color-foreground-secondary);
width: fit-content;
display: grid;
grid-template-columns: auto auto;
gap: $s-4;
align-items: center;
padding: 0;
&:hover {
color: var(--color-accent-primary);
}
}
.labeled-input-label {
color: var(--color-foreground-primary);
} }
.button-footer { .button-footer {
@ -46,60 +69,41 @@ hr {
justify-content: space-between; justify-content: space-between;
} }
.button-primary {
@extend .button-primary;
padding: $s-6;
}
.button-secondary {
@extend .button-secondary;
padding: $s-6;
}
.empty-themes-wrapper { .empty-themes-wrapper {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
color: var(--color-foreground-secondary);
.empty-themes-message {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: $s-12;
padding: $s-72 0;
h1 {
@include headlineLargeTypography;
}
p {
@include bodyMediumTypography;
font-weight: 500;
color: var(--color-foreground-secondary);
}
}
} }
.create-theme-button { .empty-theme-subtitle {
@extend .button-secondary; color: var(--color-foreground-secondary);
padding: $s-6; }
padding-right: $s-8;
svg { .empty-theme-title {
margin-right: $s-6; color: var(--color-foreground-primary);
@extend .button-icon; }
stroke: var(--icon-foreground);
} .create-theme-wrapper {
display: flex;
flex-direction: column;
gap: $s-24;
} }
.close-btn { .close-btn {
@extend .modal-close-btn-base; position: absolute;
top: $s-8;
right: $s-6;
} }
.theme-group-label { .theme-group-label {
display: block;
@include headlineMediumTypography;
color: var(--color-foreground-secondary); color: var(--color-foreground-secondary);
margin-bottom: $s-8; }
.group-title {
display: flex;
align-items: center;
justify-content: flex-start;
gap: $s-4;
} }
.theme-group-rows-wrapper { .theme-group-rows-wrapper {
@ -127,34 +131,26 @@ hr {
gap: $s-16; gap: $s-16;
} }
.theme-name {
color: var(--color-foreground-primary);
}
.theme-row-right { .theme-row-right {
display: flex; display: flex;
align-items: center; align-items: center;
gap: $s-6; gap: $s-6;
} }
.back-button {
@extend .button-tertiary;
padding: $s-6;
padding-left: 0;
display: flex;
svg {
scale: -1 1;
margin-left: 0;
@extend .button-icon;
stroke: var(--icon-foreground);
}
}
.sets-count-button { .sets-count-button {
@extend .button-secondary; text-transform: lowercase;
padding: $s-6; padding: $s-6;
padding-left: $s-12; padding-left: $s-12;
svg { }
margin-left: $s-6;
@extend .button-icon; .label-wrapper {
stroke: var(--icon-foreground); display: flex;
} align-items: center;
justify-content: center;
} }
.edit-theme-wrapper { .edit-theme-wrapper {
@ -163,12 +159,6 @@ hr {
gap: $s-12; gap: $s-12;
} }
.edit-theme-inputs-wrapper {
display: grid;
grid-template-columns: 0.6fr 1fr;
gap: $s-12;
}
.sets-list-wrapper { .sets-list-wrapper {
border: 1px solid color-mix(in hsl, var(--color-foreground-secondary) 30%, transparent); border: 1px solid color-mix(in hsl, var(--color-foreground-secondary) 30%, transparent);
border-radius: $s-8; border-radius: $s-8;
@ -176,57 +166,29 @@ hr {
} }
.sets-count-empty-button { .sets-count-empty-button {
@extend .button-secondary; text-transform: lowercase;
padding: $s-6; padding: $s-6;
padding-left: $s-12; padding-left: $s-12;
svg {
margin-left: $s-6;
@extend .button-icon;
stroke: var(--icon-foreground);
}
}
.theme-row-label {
@include bodyMediumTypography;
font-weight: 500;
color: var(--color-foreground-primary);
}
.delete-theme-button {
@extend .button-tertiary;
height: $s-28;
width: $s-28;
button {
@include buttonStyle;
@include flexCenter;
width: $s-24;
height: 100%;
svg {
@extend .button-icon-small;
height: $s-12;
width: $s-12;
color: transparent;
fill: none;
stroke: var(--icon-foreground);
}
}
} }
.group-input-wrapper { .group-input-wrapper {
position: relative; position: relative;
display: flex;
flex-direction: column;
gap: $s-4;
}
.edit-theme-inputs-wrapper {
display: grid;
grid-template-columns: 0.6fr 1fr;
gap: $s-12;
} }
.group-drop-down-button { .group-drop-down-button {
@include buttonStyle; @include buttonStyle;
color: var(--color-foreground-secondary);
width: $s-24; width: $s-24;
height: 100%; height: 100%;
padding: 0; padding: 0;
margin: 0 $s-6; margin: 0 $s-6;
svg {
@extend .button-icon-small;
transform: rotate(90deg);
fill: var(--icon-foreground);
}
} }

View file

@ -16,9 +16,10 @@
[app.main.refs :as refs] [app.main.refs :as refs]
[app.main.store :as st] [app.main.store :as st]
[app.main.ui.components.color-bullet :refer [color-bullet]] [app.main.ui.components.color-bullet :refer [color-bullet]]
[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.ds.buttons.button :refer [button*]]
[app.main.ui.ds.foundations.typography.text :refer [text*]]
[app.main.ui.hooks :as h] [app.main.ui.hooks :as h]
[app.main.ui.hooks.resize :refer [use-resize-hook]] [app.main.ui.hooks.resize :refer [use-resize-hook]]
[app.main.ui.icons :as i] [app.main.ui.icons :as i]
@ -173,22 +174,26 @@
{:empty (sort-by :token-key empty) {:empty (sort-by :token-key empty)
:filled (sort-by :token-key filled)})) :filled (sort-by :token-key filled)}))
(mf/defc edit-button
[{:keys [create?]}]
[:button {:class (stl/css :themes-button)
:on-click (fn [e]
(dom/stop-propagation e)
(modal/show! :tokens/themes {}))}
(if create? (tr "labels.create") (tr "labels.edit"))])
(mf/defc themes-header (mf/defc themes-header
[_props] [_props]
(let [ordered-themes (mf/deref refs/workspace-token-themes-no-hidden)] (let [ordered-themes (mf/deref refs/workspace-token-themes-no-hidden)
open-modal
(mf/use-fn
(fn [e]
(dom/stop-propagation e)
(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")]
[:div {:class (stl/css :theme-select-wrapper)} (if (empty? ordered-themes)
[:& theme-select] [:div {:class (stl/css :empty-theme-wrapper)}
[:& edit-button {:create? (empty? ordered-themes)}]]])) [:> text* {:as "span" :typography "body-small"} (tr "workspace.token.no-themes")]
[:button {:on-click open-modal
:class (stl/css :create-theme-button)} (tr "workspace.token.create-a-theme")]]
[:div {:class (stl/css :theme-select-wrapper)}
[:& theme-select]
[:> button* {:variant "secondary"
:on-click open-modal}
(tr "labels.edit")]])]))
(mf/defc add-set-button (mf/defc add-set-button
[{:keys [on-open]}] [{:keys [on-open]}]

View file

@ -4,6 +4,7 @@
// //
// Copyright (c) KALEIDOS INC // Copyright (c) KALEIDOS INC
@use "../../ds/typography.scss" as *;
@import "refactor/common-refactor.scss"; @import "refactor/common-refactor.scss";
@import "./common.scss"; @import "./common.scss";
@ -37,9 +38,21 @@
position: relative; position: relative;
} }
.themes-header {
display: block;
@include headlineSmallTypography;
margin-bottom: $s-8;
padding-left: $s-8;
color: var(--title-foreground-color);
}
.themes-wrapper { .themes-wrapper {
padding: $s-12 0 0 $s-12;
}
.empty-theme-wrapper {
padding: $s-12; padding: $s-12;
padding-bottom: 0; color: var(--color-foreground-secondary);
} }
.sidebar-header { .sidebar-header {
@ -168,12 +181,13 @@
width: auto; width: auto;
} }
.themes-header { .create-theme-button {
display: block; @include use-typography("body-small");
@include headlineSmallTypography; background-color: transparent;
margin-bottom: $s-8; border: none;
padding-left: $s-8; appearance: none;
color: var(--title-foreground-color); color: var(--color-accent-primary);
cursor: pointer;
} }
.resize-area-horiz { .resize-area-horiz {

View file

@ -7,6 +7,7 @@
(ns app.main.ui.workspace.tokens.theme-select (ns app.main.ui.workspace.tokens.theme-select
(:require-macros [app.main.style :as stl]) (:require-macros [app.main.style :as stl])
(:require (:require
[app.common.data.macros :as dm]
[app.common.types.tokens-lib :as ctob] [app.common.types.tokens-lib :as ctob]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.main.data.modal :as modal] [app.main.data.modal :as modal]
@ -14,46 +15,62 @@
[app.main.refs :as refs] [app.main.refs :as refs]
[app.main.store :as st] [app.main.store :as st]
[app.main.ui.components.dropdown :refer [dropdown]] [app.main.ui.components.dropdown :refer [dropdown]]
[app.main.ui.icons :as i] [app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i]
[app.main.ui.ds.foundations.typography.text :refer [text*]]
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.i18n :refer [tr]]
[cuerdas.core :as str] [cuerdas.core :as str]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
(mf/defc themes-list (mf/defc themes-list
[{:keys [themes active-theme-paths on-close grouped?]}] [{:keys [themes active-theme-paths on-close grouped?]}]
(when (seq themes) (when (seq themes)
[:ul [:ul {:class (stl/css :theme-options)}
(for [[_ {:keys [group name] :as theme}] themes (for [[_ {:keys [group name] :as theme}] themes
:let [theme-id (ctob/theme-path theme) :let [theme-id (ctob/theme-path theme)
selected? (get active-theme-paths theme-id)]] selected? (get active-theme-paths theme-id)
select-theme (fn [e]
(dom/stop-propagation e)
(st/emit! (wdt/toggle-token-theme-active? group name))
(on-close))]]
[:li {:key theme-id [:li {:key theme-id
:role "option"
:aria-selected selected?
:class (stl/css-case :class (stl/css-case
:checked-element true :checked-element true
:sub-item grouped? :sub-item grouped?
:is-selected selected?) :is-selected selected?)
:on-click (fn [e] :on-click select-theme}
(dom/stop-propagation e) [:> text* {:as "span" :typography "body-small" :class (stl/css :label)} name]
(st/emit! (wdt/toggle-token-theme-active? group name)) [:> icon* {:id i/tick
(on-close))} :aria-hidden true
[:span {:class (stl/css :label)} name] :class (stl/css-case :check-icon true
[:span {:class (stl/css :check-icon)} i/tick]])])) :check-icon-visible selected?)}]])]))
(mf/defc theme-options (mf/defc theme-options
[{:keys [active-theme-paths themes on-close]}] [{:keys [active-theme-paths themes on-close]}]
[:ul (let []
(for [[group themes] themes] (let [on-edit-click #(modal/show! :tokens/themes {})]
[:li {:key group} [:ul {:class (stl/css :theme-options :custom-select-dropdown)
(when (seq group) :role "listbox"}
[:span {:class (stl/css :group)} group]) (for [[group themes] themes]
[:& themes-list {:themes themes [:li {:key group
:active-theme-paths active-theme-paths :aria-labelledby (dm/str group "-label")
:on-close on-close :role "group"}
:grouped? true}]]) (when (seq group)
[:li {:class (stl/css-case :checked-element true [:> text* {:as "span" :typography "headline-small" :class (stl/css :group) :id (dm/str group "-label")} group])
:checked-element-button true) [:& themes-list {:themes themes
:on-click #(modal/show! :tokens/themes {})} :active-theme-paths active-theme-paths
[:span "Edit themes"] :on-close on-close
[:span {:class (stl/css :icon)} i/arrow]]]) :grouped? true}]])
[:li {:class (stl/css :separator)
:aria-hidden true}]
[:li {:class (stl/css-case :checked-element true
:checked-element-button true)
:role "option"
:on-click on-edit-click}
[:> text* {:as "span" :typography "body-small"} (tr "workspace.token.edit-themes")]
[:> icon* {:id i/arrow-right :aria-hidden true}]]])))
(mf/defc theme-select (mf/defc theme-select
[{:keys []}] [{:keys []}]
@ -64,11 +81,11 @@
;; Data ;; Data
current-label (cond current-label (cond
(> active-themes-count 1) (str active-themes-count " themes active") (> active-themes-count 1) (tr "workspace.token.active-themes" active-themes-count)
(= active-themes-count 1) (some->> (first active-theme-paths) (= active-themes-count 1) (some->> (first active-theme-paths)
(ctob/split-token-theme-path) (ctob/split-token-theme-path)
(str/join " / ")) (str/join " / "))
:else "No theme active") :else (tr "workspace.token.no-active-theme"))
;; State ;; State
state* (mf/use-state state* (mf/use-state
@ -81,13 +98,20 @@
dropdown-element* (mf/use-ref nil) 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 (mf/use-fn #(swap! state* assoc :is-open? true))] on-open-dropdown (mf/use-fn #(swap! state* assoc :is-open? true))]
;; TODO: This element should be accessible by keyboard
[:div {:on-click on-open-dropdown [:div {:on-click on-open-dropdown
:aria-expanded is-open?
:aria-haspopup "listbox"
:tab-index "0"
:role "combobox"
:class (stl/css :custom-select)} :class (stl/css :custom-select)}
[:span {:class (stl/css :current-label)} current-label] [:> text* {:as "span" :typography "body-small" :class (stl/css :current-label)}
[:span {:class (stl/css :dropdown-button)} i/arrow] current-label]
[:& dropdown {:show is-open? :on-close on-close-dropdown} [:> icon* {:id i/arrow-down :class (stl/css :dropdown-button) :aria-hidden true}]
[:div {:ref dropdown-element* [:& dropdown {:show is-open?
:class (stl/css :custom-select-dropdown)} :on-close on-close-dropdown
[:& theme-options {:active-theme-paths active-theme-paths :ref dropdown-element*}
:themes themes [:& theme-options {:active-theme-paths active-theme-paths
:on-close on-close-dropdown}]]]])) :themes themes
:on-close on-close-dropdown}]]]))

View file

@ -7,12 +7,11 @@
@import "refactor/common-refactor.scss"; @import "refactor/common-refactor.scss";
.custom-select { .custom-select {
--border-color: var(--menu-background-color); --custom-select-border-color: var(--menu-background-color);
--bg-color: var(--menu-background-color); --custom-select-bg-color: var(--menu-background-color);
--icon-color: var(--icon-foreground); --custom-select-icon-color: var(--color-foreground-secondary);
--text-color: var(--menu-foreground-color); --custom-select-text-color: var(--menu-foreground-color);
@extend .new-scrollbar; @extend .new-scrollbar;
@include bodySmallTypography;
position: relative; position: relative;
display: grid; display: grid;
grid-template-columns: 1fr auto; grid-template-columns: 1fr auto;
@ -22,74 +21,60 @@
margin: 0; margin: 0;
padding: $s-8; padding: $s-8;
border-radius: $br-8; border-radius: $br-8;
background-color: var(--bg-color); background-color: var(--custom-select-bg-color);
border: $s-1 solid var(--border-color); border: $s-1 solid var(--custom-select-border-color);
color: var(--text-color); color: var(--custom-select-text-color);
cursor: pointer; cursor: pointer;
ul {
margin-bottom: 0;
}
.group {
display: block;
@include headlineSmallTypography;
padding: $s-8;
color: var(--color-foreground-secondary);
font-weight: 600;
}
&.icon {
grid-template-columns: auto 1fr auto;
}
&:hover { &:hover {
--bg-color: var(--menu-background-color-hover); --custom-select-bg-color: var(--menu-background-color-hover);
--border-color: var(--menu-background-color); --custom-select-border-color: var(--menu-background-color);
--icon-color: var(--menu-foreground-color-hover); --custom-select-icon-color: var(--menu-foreground-color-hover);
} }
&:focus { &:focus {
--bg-color: var(--menu-background-color-focus); --custom-select-bg-color: var(--menu-background-color-focus);
--border-color: var(--menu-background-focus); --custom-select-border-color: var(--menu-background-focus);
} }
} }
.theme-options {
margin-bottom: 0;
}
.group {
display: block;
padding: $s-8;
color: var(--color-foreground-secondary);
}
.disabled { .disabled {
--bg-color: var(--menu-background-color-disabled); --custom-select-bg-color: var(--menu-background-color-disabled);
--border-color: var(--menu-border-color-disabled); --custom-select-border-color: var(--menu-border-color-disabled);
--icon-color: var(--menu-foreground-color-disabled); --custom-select-icon-color: var(--menu-foreground-color-disabled);
--text-color: var(--menu-foreground-color-disabled); --custom-select-text-color: var(--menu-foreground-color-disabled);
pointer-events: none; pointer-events: none;
cursor: default; cursor: default;
} }
.dropdown-button { .dropdown-button {
@include flexCenter; @include flexCenter;
svg { color: var(--color-foreground-secondary);
@extend .button-icon-small;
transform: rotate(90deg);
stroke: var(--icon-color);
}
} }
.current-icon { .current-icon {
@include flexCenter; @include flexCenter;
width: $s-24; width: $s-24;
padding-right: $s-4; padding-right: $s-4;
svg {
@extend .button-icon-small;
stroke: var(--icon-foreground);
}
} }
.custom-select-dropdown { .custom-select-dropdown {
@extend .dropdown-wrapper; @extend .dropdown-wrapper;
.separator { }
margin: 0;
height: $s-12; .separator {
border-block-start: $s-1 solid var(--dropdown-separator-color); margin: 0;
} height: $s-2;
border-block-start: $s-1 solid color-mix(in hsl, var(--color-foreground-secondary) 20%, transparent);
} }
.custom-select-dropdown[data-direction="up"] { .custom-select-dropdown[data-direction="up"] {
@ -109,59 +94,31 @@
padding-right: 0; padding-right: 0;
} }
li + .checked-element-button {
margin-top: $s-8;
&:before {
content: "";
position: absolute;
top: -$s-4;
left: 0;
right: 0;
height: 1px;
background-color: color-mix(in hsl, var(--color-foreground-secondary) 20%, transparent);
}
}
.checked-element { .checked-element {
@extend .dropdown-element-base; @extend .dropdown-element-base;
.icon {
@include flexCenter;
height: $s-24;
width: $s-24;
padding-right: $s-4;
svg {
@extend .button-icon;
stroke: var(--icon-foreground);
}
}
.label {
flex-grow: 1;
width: 100%;
}
.check-icon {
@include flexCenter;
svg {
@extend .button-icon-small;
visibility: hidden;
stroke: var(--icon-foreground);
}
}
&.is-selected { &.is-selected {
color: var(--menu-foreground-color); color: var(--menu-foreground-color);
.check-icon svg {
stroke: var(--menu-foreground-color);
visibility: visible;
}
} }
&.disabled { &.disabled {
display: none; display: none;
} }
} }
.check-icon {
@include flexCenter;
color: var(--icon-foreground-primary);
visibility: hidden;
}
.label {
flex-grow: 1;
width: 100%;
}
.check-icon-visible {
visibility: visible;
}
.current-label { .current-label {
@include textEllipsis; @include textEllipsis;
} }

View file

@ -6163,4 +6163,72 @@ msgstr "Resolved value: "
#: src/app/main/ui/workspace/tokens/sidebar.cljs #: src/app/main/ui/workspace/tokens/sidebar.cljs
msgid "workspace.token.original-value" msgid "workspace.token.original-value"
msgstr "Original value: " msgstr "Original value: "
#: src/app/main/ui/workspace/tokens/sidebar.cljs
msgid "workspace.token.no-themes"
msgstr "There are no themes."
#: src/app/main/ui/workspace/tokens/sidebar.cljs
msgid "workspace.token.create-a-theme"
msgstr "Create one."
#: src/app/main/ui/workspace/tokens/modals/themes.cljs
msgid "workspace.token.save-theme"
msgstr "Save theme"
#: src/app/main/ui/workspace/tokens/modals/themes.cljs
msgid "workspace.token.create-theme-title"
msgstr "Create theme"
#: src/app/main/ui/workspace/tokens/modals/themes.cljs
msgid "workspace.token.edit-theme-title"
msgstr "Edit theme"
#: src/app/main/ui/workspace/tokens/modals/themes.cljs
msgid "workspace.token.delete-theme-title"
msgstr "Delete theme"
#: src/app/main/ui/workspace/tokens/modals/themes.cljs
msgid "workspace.token.no-themes-currently"
msgstr "You currently have no themes."
#: src/app/main/ui/workspace/tokens/modals/themes.cljs
msgid "workspace.token.create-new-theme"
msgstr "Create your first theme now."
#: src/app/main/ui/workspace/tokens/modals/themes.cljs
msgid "workspace.token.new-theme"
msgstr "New theme"
#: src/app/main/ui/workspace/tokens/modals/themes.cljs
msgid "workspace.token.themes"
msgstr "Themes"
#: src/app/main/ui/workspace/tokens/modals/themes.cljs
msgid "workspace.token.theme-name"
msgstr "Theme %s"
#: src/app/main/ui/workspace/tokens/modals/themes.cljs
msgid "workspace.token.no-sets"
msgstr "No sets defined"
#: src/app/main/ui/workspace/tokens/modals/themes.cljs
msgid "workspace.token.num-sets"
msgstr "%s sets"
#: src/app/main/ui/workspace/tokens/modals/themes.cljs
msgid "workspace.token.back-to-themes"
msgstr "Back to theme list"
#: src/app/main/ui/workspace/tokens/theme_select.cljs
msgid "workspace.token.edit-themes"
msgstr "Edit themes"
#: src/app/main/ui/workspace/tokens/theme_select.cljs
msgid "workspace.token.no-active-theme"
msgstr "No theme active"
#: src/app/main/ui/workspace/tokens/theme_select.cljs
msgid "workspace.token.active-themes"
msgstr "%s active themes"

View file

@ -6150,4 +6150,72 @@ msgstr "Valor resuelto: "
#: src/app/main/ui/workspace/tokens/sidebar.cljs #: src/app/main/ui/workspace/tokens/sidebar.cljs
msgid "workspace.token.original-value" msgid "workspace.token.original-value"
msgstr "Valor original: " msgstr "Valor original: "
#: src/app/main/ui/workspace/tokens/sidebar.cljs
msgid "workspace.token.no-themes"
msgstr "No hay temas."
#: src/app/main/ui/workspace/tokens/sidebar.cljs
msgid "workspace.token.create-a-theme"
msgstr "Crear uno."
#: src/app/main/ui/workspace/tokens/modals/themes.cljs
msgid "workspace.token.save-theme"
msgstr "Guardar tema"
#: src/app/main/ui/workspace/tokens/modals/themes.cljs
msgid "workspace.token.create-theme-title"
msgstr "Crear tema"
#: src/app/main/ui/workspace/tokens/modals/themes.cljs
msgid "workspace.token.edit-theme-title"
msgstr "Editar tema"
#: src/app/main/ui/workspace/tokens/modals/themes.cljs
msgid "workspace.token.delete-theme-title"
msgstr "Borrar theme"
#: src/app/main/ui/workspace/tokens/modals/themes.cljs
msgid "workspace.token.no-themes-currently"
msgstr "Actualmente no existen temas."
#: src/app/main/ui/workspace/tokens/modals/themes.cljs
msgid "workspace.token.create-new-theme"
msgstr "Crea un nuevo tema ahora."
#: src/app/main/ui/workspace/tokens/modals/themes.cljs
msgid "workspace.token.new-theme"
msgstr "Nuevo tema"
#: src/app/main/ui/workspace/tokens/modals/themes.cljs
msgid "workspace.token.themes"
msgstr "Temas"
#: src/app/main/ui/workspace/tokens/modals/themes.cljs
msgid "workspace.token.theme-name"
msgstr "Tema %s"
#: src/app/main/ui/workspace/tokens/modals/themes.cljs
msgid "workspace.token.no-sets"
msgstr "No hay sets definidos"
#: src/app/main/ui/workspace/tokens/modals/themes.cljs
msgid "workspace.token.num-sets"
msgstr "%s sets"
#: src/app/main/ui/workspace/tokens/modals/themes.cljs
msgid "workspace.token.back-to-themes"
msgstr "Volver al listado de temas"
#: src/app/main/ui/workspace/tokens/theme_select.cljs
msgid "workspace.token.edit-themes"
msgstr "Editar temas"
#: src/app/main/ui/workspace/tokens/theme_select.cljs
msgid "workspace.token.no-active-theme"
msgstr "No hay temas activos"
#: src/app/main/ui/workspace/tokens/theme_select.cljs
msgid "workspace.token.active-themes"
msgstr "%s temas activos"