mirror of
https://github.com/penpot/penpot.git
synced 2025-01-06 14:50:20 -05:00
♻️ Review themes section
This commit is contained in:
parent
03ea5414be
commit
0ff5df4b8d
17 changed files with 665 additions and 443 deletions
3
frontend/resources/images/icons/arrow-down.svg
Normal file
3
frontend/resources/images/icons/arrow-down.svg
Normal 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 |
3
frontend/resources/images/icons/arrow-left.svg
Normal file
3
frontend/resources/images/icons/arrow-left.svg
Normal 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 |
3
frontend/resources/images/icons/arrow-right.svg
Normal file
3
frontend/resources/images/icons/arrow-right.svg
Normal 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 |
3
frontend/resources/images/icons/arrow-up.svg
Normal file
3
frontend/resources/images/icons/arrow-up.svg
Normal 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 |
|
@ -50,6 +50,10 @@
|
|||
(def ^:icon-id align-top "align-top")
|
||||
(def ^:icon-id align-vertical-center "align-vertical-center")
|
||||
(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 board "board")
|
||||
(def ^:icon-id boards-thumbnail "boards-thumbnail")
|
||||
|
|
|
@ -32,17 +32,17 @@
|
|||
|
||||
(let [level (or level "1")
|
||||
tag (dm/str "h" level)
|
||||
class (dm/str (or class "") " " (stl/css-case :display-typography (= typography t/display)
|
||||
:title-large-typography (= typography t/title-large)
|
||||
:title-medium-typography (= typography t/title-medium)
|
||||
:title-small-typography (= typography t/title-small)
|
||||
:headline-large-typography (= typography t/headline-large)
|
||||
:headline-medium-typography (= typography t/headline-medium)
|
||||
:headline-small-typography (= typography t/headline-small)
|
||||
:body-large-typography (= typography t/body-large)
|
||||
:body-medium-typography (= typography t/body-medium)
|
||||
:body-small-typography (= typography t/body-small)
|
||||
:code-font-typography (= typography t/code-font)))
|
||||
class (dm/str class " " (stl/css-case :display-typography (= typography t/display)
|
||||
:title-large-typography (= typography t/title-large)
|
||||
:title-medium-typography (= typography t/title-medium)
|
||||
:title-small-typography (= typography t/title-small)
|
||||
:headline-large-typography (= typography t/headline-large)
|
||||
:headline-medium-typography (= typography t/headline-medium)
|
||||
:headline-small-typography (= typography t/headline-small)
|
||||
:body-large-typography (= typography t/body-large)
|
||||
:body-medium-typography (= typography t/body-medium)
|
||||
:body-small-typography (= typography t/body-small)
|
||||
:code-font-typography (= typography t/code-font)))
|
||||
props (mf/spread-props props {:class class})]
|
||||
[:> tag props
|
||||
children]))
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
[app.main.ui.components.color-bullet :refer [color-bullet]]
|
||||
[app.main.ui.ds.buttons.button :refer [button*]]
|
||||
[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.workspace.colorpicker :as colorpicker]
|
||||
[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)
|
||||
:on-submit on-submit}
|
||||
[: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")
|
||||
(tr "workspace.token.edit-token")
|
||||
(tr "workspace.token.create-token" token-type))]
|
||||
|
||||
[:div {:class (stl/css :input-row)}
|
||||
;; 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"
|
||||
:error? @name-errors
|
||||
: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)}
|
||||
;; 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"
|
||||
:input-props {:default-value @value-ref
|
||||
: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)}
|
||||
;; 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"
|
||||
:input-props {:default-value @description-ref
|
||||
:on-change on-update-description}}]
|
||||
|
@ -416,10 +417,12 @@ Token names should only contain letters and digits separated by . characters.")}
|
|||
(when (= action "edit")
|
||||
[:> button* {:on-click on-delete-token
|
||||
:class (stl/css :delete-btn)
|
||||
:type "button"
|
||||
:icon i/delete
|
||||
:variant "secondary"}
|
||||
(tr "labels.delete")])
|
||||
[:> button* {:on-click on-cancel
|
||||
:type "button"
|
||||
:variant "secondary"}
|
||||
(tr "labels.cancel")]
|
||||
[:> button* {:type "submit"
|
||||
|
|
|
@ -39,6 +39,10 @@
|
|||
gap: $s-4;
|
||||
}
|
||||
|
||||
.labeled-input-label {
|
||||
color: var(--color-foreground-primary);
|
||||
}
|
||||
|
||||
.error {
|
||||
padding: $s-4 $s-6;
|
||||
margin-bottom: 0;
|
||||
|
@ -75,3 +79,7 @@
|
|||
border-radius: $br-4;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.form-modal-title {
|
||||
color: var(--color-foreground-primary);
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||
[app.main.ui.workspace.tokens.form :refer [form]]
|
||||
[app.main.ui.workspace.tokens.modals.themes :as wtmt]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[okulary.core :as l]
|
||||
[rumext.v2 :as mf]))
|
||||
|
@ -60,12 +59,6 @@
|
|||
:selected-token-set-id selected-token-set-id
|
||||
:token-type token-type}]]))
|
||||
|
||||
(mf/defc token-themes-modal
|
||||
{::mf/register modal/components
|
||||
::mf/register-as :tokens/themes}
|
||||
[args]
|
||||
[:& wtmt/modal args])
|
||||
|
||||
;; Modals ----------------------------------------------------------------------
|
||||
|
||||
(mf/defc boolean-modal
|
||||
|
|
|
@ -7,36 +7,49 @@
|
|||
(ns app.main.ui.workspace.tokens.modals.themes
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.types.tokens-lib :as ctob]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.tokens :as wdt]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[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.workspace.tokens.common :refer [labeled-input] :as wtco]
|
||||
[app.main.ui.workspace.tokens.sets :as wts]
|
||||
[app.main.ui.workspace.tokens.sets-context :as sets-context]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[cuerdas.core :as str]
|
||||
[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
|
||||
[{:keys [set-state]}]
|
||||
[:div {:class (stl/css :empty-themes-wrapper)}
|
||||
[:div {:class (stl/css :empty-themes-message)}
|
||||
[:h1 "You currently have no themes."]
|
||||
[:p "Create your first theme now."]]
|
||||
[:div {:class (stl/css :button-footer)}
|
||||
[:button {:class (stl/css :button-primary)
|
||||
:on-click #(set-state (fn [_] {:type :create-theme}))}
|
||||
"New theme"]]])
|
||||
(let [create-theme
|
||||
(mf/use-fn
|
||||
(mf/deps set-state)
|
||||
#(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")]
|
||||
[: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
|
||||
[{:keys [selected? name on-change]}]
|
||||
|
@ -57,60 +70,195 @@
|
|||
[{:keys [set-state]}]
|
||||
(let [active-theme-ids (mf/deref refs/workspace-active-theme-paths)
|
||||
themes-groups (mf/deref refs/workspace-token-theme-tree-no-hidden)
|
||||
on-edit-theme (fn [theme e]
|
||||
(dom/prevent-default e)
|
||||
(dom/stop-propagation e)
|
||||
(set-state (fn [_] {:type :edit-theme
|
||||
:theme-path [(:id theme) (:group theme) (:name theme)]})))]
|
||||
[:div
|
||||
|
||||
create-theme
|
||||
(mf/use-fn
|
||||
(mf/deps set-state)
|
||||
(fn [e]
|
||||
(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)}
|
||||
(for [[group themes] themes-groups]
|
||||
[:li {:key (str "token-theme-group" group)}
|
||||
[:li {:key (dm/str "token-theme-group" 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)}
|
||||
(for [[_ {:keys [group name] :as theme}] themes
|
||||
: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
|
||||
:class (stl/css :theme-row)}
|
||||
[:div {:class (stl/css :theme-row-left)}
|
||||
|
||||
;; FIREEEEEEEEEE THIS
|
||||
[:div {:on-click (fn [e]
|
||||
(dom/prevent-default e)
|
||||
(dom/stop-propagation e)
|
||||
(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)
|
||||
: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)}
|
||||
(if-let [sets-count (some-> theme :sets seq count)]
|
||||
[:button {:class (stl/css :sets-count-button)
|
||||
:on-click #(on-edit-theme theme %)}
|
||||
(str sets-count " sets")
|
||||
chevron-icon]
|
||||
[:button {:class (stl/css :sets-count-empty-button)
|
||||
:on-click #(on-edit-theme theme %)}
|
||||
"No sets defined"
|
||||
chevron-icon])
|
||||
[: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"]]]))
|
||||
[:> button* {:class (stl/css :sets-count-button)
|
||||
:variant "secondary"
|
||||
:type "button"
|
||||
:on-click on-edit-theme}
|
||||
[:div {:class (stl/css :label-wrapper)}
|
||||
[:> text* {:as "span" :typography "body-medium"}
|
||||
(tr "workspace.token.num-sets" sets-count)]
|
||||
[:> icon* {:id "arrow-right"}]]]
|
||||
|
||||
(mf/defc edit-theme
|
||||
[{:keys [edit? token-sets theme theme-groups on-back on-submit]}]
|
||||
(let [{:keys [dropdown-open? on-open-dropdown on-close-dropdown on-toggle-dropdown]} (wtco/use-dropdown-open-state)
|
||||
[:> button* {:class (stl/css :sets-count-empty-button)
|
||||
:type "button"
|
||||
: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)
|
||||
disabled? (-> (:name @theme-state)
|
||||
(str/trim)
|
||||
|
@ -125,9 +273,6 @@
|
|||
(swap! theme-state #(ctob/toggle-set % set-name))))
|
||||
on-change-field (fn [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
|
||||
(mf/deps theme-state on-submit)
|
||||
(fn [e]
|
||||
|
@ -138,124 +283,83 @@
|
|||
(update :description str/trim))]
|
||||
(when-not (str/empty? (:name theme))
|
||||
(on-submit theme)))
|
||||
(on-back)))]
|
||||
[:form {:on-submit on-save-form}
|
||||
[:div {:class (stl/css :edit-theme-wrapper)}
|
||||
[:div
|
||||
[:button {:class (stl/css :back-button)
|
||||
:type "button"
|
||||
: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"]]]]]))
|
||||
(on-back)))
|
||||
close-modal
|
||||
(mf/use-fn
|
||||
(fn [e]
|
||||
(dom/prevent-default e)
|
||||
(st/emit! (modal/hide))))
|
||||
|
||||
(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))
|
||||
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)] %))}]))
|
||||
on-delete-token
|
||||
(mf/use-fn
|
||||
(mf/deps theme on-back)
|
||||
(fn []
|
||||
(st/emit! (wdt/delete-token-theme (:group theme) (:name theme)))
|
||||
(on-back)))]
|
||||
|
||||
(mf/defc create-theme
|
||||
[{:keys [set-state]}]
|
||||
(let [token-sets (mf/deref refs/workspace-ordered-token-sets)
|
||||
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 %))}]))
|
||||
[:div {:class (stl/css :themes-modal-wrapper)}
|
||||
[:> heading* {:level 2 :typography "headline-medium" :class (stl/css :themes-modal-title)}
|
||||
(tr "workspace.token.edit-theme-title")]
|
||||
|
||||
(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)
|
||||
state (mf/use-state (if (empty? themes)
|
||||
{:type :create-theme}
|
||||
{:type :themes-overview}))
|
||||
set-state (mf/use-callback #(swap! state %))
|
||||
title (case (:type @state)
|
||||
:edit-theme "Edit Theme"
|
||||
"Themes")
|
||||
component (case (:type @state)
|
||||
:empty-themes empty-themes
|
||||
:themes-overview (if (empty? themes) empty-themes themes-overview)
|
||||
:edit-theme controlled-edit-theme
|
||||
:create-theme create-theme)]
|
||||
[:div
|
||||
[:div {:class (stl/css :modal-title)} title]
|
||||
[:div {:class (stl/css :modal-content)}
|
||||
[:& component {:state state
|
||||
:set-state set-state}]]]))
|
||||
[:& component {:state state
|
||||
:set-state set-state}]))
|
||||
|
||||
(mf/defc modal
|
||||
{::mf/wrap-props false}
|
||||
[_]
|
||||
(mf/defc token-themes-modal
|
||||
{::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)))]
|
||||
[:div {:class (stl/css :modal-overlay)}
|
||||
[:div {:class (stl/css :modal-dialog)}
|
||||
[:button {:class (stl/css :close-btn) :on-click handle-close-dialog} close-icon]
|
||||
[:& themes]]]))
|
||||
[:> icon-button* {:class (stl/css :close-btn)
|
||||
:on-click handle-close-dialog
|
||||
:aria-label (tr "labels.close")
|
||||
:variant "action"
|
||||
:icon "close"}]
|
||||
[:& themes-modal-body]]]))
|
|
@ -10,10 +10,6 @@
|
|||
@extend .modal-overlay-base;
|
||||
}
|
||||
|
||||
hr {
|
||||
border-color: var(--color-background-tertiary);
|
||||
}
|
||||
|
||||
.modal-dialog {
|
||||
@extend .modal-container-base;
|
||||
display: grid;
|
||||
|
@ -23,16 +19,43 @@ hr {
|
|||
user-select: none;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
@include headlineMediumTypography;
|
||||
font-weight: 500;
|
||||
margin-block-end: $s-16;
|
||||
color: var(--color-foreground-secondary);
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
.empty-themes-message {
|
||||
display: flex;
|
||||
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 {
|
||||
|
@ -46,60 +69,41 @@ hr {
|
|||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.button-primary {
|
||||
@extend .button-primary;
|
||||
padding: $s-6;
|
||||
}
|
||||
|
||||
.button-secondary {
|
||||
@extend .button-secondary;
|
||||
padding: $s-6;
|
||||
}
|
||||
|
||||
.empty-themes-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.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);
|
||||
}
|
||||
}
|
||||
color: var(--color-foreground-secondary);
|
||||
}
|
||||
|
||||
.create-theme-button {
|
||||
@extend .button-secondary;
|
||||
padding: $s-6;
|
||||
padding-right: $s-8;
|
||||
svg {
|
||||
margin-right: $s-6;
|
||||
@extend .button-icon;
|
||||
stroke: var(--icon-foreground);
|
||||
}
|
||||
.empty-theme-subtitle {
|
||||
color: var(--color-foreground-secondary);
|
||||
}
|
||||
|
||||
.empty-theme-title {
|
||||
color: var(--color-foreground-primary);
|
||||
}
|
||||
|
||||
.create-theme-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $s-24;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
@extend .modal-close-btn-base;
|
||||
position: absolute;
|
||||
top: $s-8;
|
||||
right: $s-6;
|
||||
}
|
||||
|
||||
.theme-group-label {
|
||||
display: block;
|
||||
@include headlineMediumTypography;
|
||||
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 {
|
||||
|
@ -127,34 +131,26 @@ hr {
|
|||
gap: $s-16;
|
||||
}
|
||||
|
||||
.theme-name {
|
||||
color: var(--color-foreground-primary);
|
||||
}
|
||||
|
||||
.theme-row-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
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 {
|
||||
@extend .button-secondary;
|
||||
text-transform: lowercase;
|
||||
padding: $s-6;
|
||||
padding-left: $s-12;
|
||||
svg {
|
||||
margin-left: $s-6;
|
||||
@extend .button-icon;
|
||||
stroke: var(--icon-foreground);
|
||||
}
|
||||
}
|
||||
|
||||
.label-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.edit-theme-wrapper {
|
||||
|
@ -163,12 +159,6 @@ hr {
|
|||
gap: $s-12;
|
||||
}
|
||||
|
||||
.edit-theme-inputs-wrapper {
|
||||
display: grid;
|
||||
grid-template-columns: 0.6fr 1fr;
|
||||
gap: $s-12;
|
||||
}
|
||||
|
||||
.sets-list-wrapper {
|
||||
border: 1px solid color-mix(in hsl, var(--color-foreground-secondary) 30%, transparent);
|
||||
border-radius: $s-8;
|
||||
|
@ -176,57 +166,29 @@ hr {
|
|||
}
|
||||
|
||||
.sets-count-empty-button {
|
||||
@extend .button-secondary;
|
||||
text-transform: lowercase;
|
||||
padding: $s-6;
|
||||
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 {
|
||||
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 {
|
||||
@include buttonStyle;
|
||||
color: var(--color-foreground-secondary);
|
||||
width: $s-24;
|
||||
height: 100%;
|
||||
|
||||
padding: 0;
|
||||
margin: 0 $s-6;
|
||||
|
||||
svg {
|
||||
@extend .button-icon-small;
|
||||
transform: rotate(90deg);
|
||||
fill: var(--icon-foreground);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,9 +16,10 @@
|
|||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.color-bullet :refer [color-bullet]]
|
||||
[app.main.ui.components.dropdown-menu :refer [dropdown-menu
|
||||
dropdown-menu-item*]]
|
||||
[app.main.ui.components.dropdown-menu :refer [dropdown-menu dropdown-menu-item*]]
|
||||
[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.resize :refer [use-resize-hook]]
|
||||
[app.main.ui.icons :as i]
|
||||
|
@ -173,22 +174,26 @@
|
|||
{:empty (sort-by :token-key empty)
|
||||
: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
|
||||
[_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)}
|
||||
[:span {:class (stl/css :themes-header)} (tr "labels.themes")]
|
||||
[:div {:class (stl/css :theme-select-wrapper)}
|
||||
[:& theme-select]
|
||||
[:& edit-button {:create? (empty? ordered-themes)}]]]))
|
||||
(if (empty? ordered-themes)
|
||||
[:div {:class (stl/css :empty-theme-wrapper)}
|
||||
[:> 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
|
||||
[{:keys [on-open]}]
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
//
|
||||
// Copyright (c) KALEIDOS INC
|
||||
|
||||
@use "../../ds/typography.scss" as *;
|
||||
@import "refactor/common-refactor.scss";
|
||||
@import "./common.scss";
|
||||
|
||||
|
@ -37,9 +38,21 @@
|
|||
position: relative;
|
||||
}
|
||||
|
||||
.themes-header {
|
||||
display: block;
|
||||
@include headlineSmallTypography;
|
||||
margin-bottom: $s-8;
|
||||
padding-left: $s-8;
|
||||
color: var(--title-foreground-color);
|
||||
}
|
||||
|
||||
.themes-wrapper {
|
||||
padding: $s-12 0 0 $s-12;
|
||||
}
|
||||
|
||||
.empty-theme-wrapper {
|
||||
padding: $s-12;
|
||||
padding-bottom: 0;
|
||||
color: var(--color-foreground-secondary);
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
|
@ -168,12 +181,13 @@
|
|||
width: auto;
|
||||
}
|
||||
|
||||
.themes-header {
|
||||
display: block;
|
||||
@include headlineSmallTypography;
|
||||
margin-bottom: $s-8;
|
||||
padding-left: $s-8;
|
||||
color: var(--title-foreground-color);
|
||||
.create-theme-button {
|
||||
@include use-typography("body-small");
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
appearance: none;
|
||||
color: var(--color-accent-primary);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.resize-area-horiz {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
(ns app.main.ui.workspace.tokens.theme-select
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.types.tokens-lib :as ctob]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.modal :as modal]
|
||||
|
@ -14,46 +15,62 @@
|
|||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[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.i18n :refer [tr]]
|
||||
[cuerdas.core :as str]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(mf/defc themes-list
|
||||
[{:keys [themes active-theme-paths on-close grouped?]}]
|
||||
(when (seq themes)
|
||||
[:ul
|
||||
[:ul {:class (stl/css :theme-options)}
|
||||
(for [[_ {:keys [group name] :as theme}] themes
|
||||
: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
|
||||
:role "option"
|
||||
:aria-selected selected?
|
||||
:class (stl/css-case
|
||||
:checked-element true
|
||||
:sub-item grouped?
|
||||
:is-selected selected?)
|
||||
:on-click (fn [e]
|
||||
(dom/stop-propagation e)
|
||||
(st/emit! (wdt/toggle-token-theme-active? group name))
|
||||
(on-close))}
|
||||
[:span {:class (stl/css :label)} name]
|
||||
[:span {:class (stl/css :check-icon)} i/tick]])]))
|
||||
:on-click select-theme}
|
||||
[:> text* {:as "span" :typography "body-small" :class (stl/css :label)} name]
|
||||
[:> icon* {:id i/tick
|
||||
:aria-hidden true
|
||||
:class (stl/css-case :check-icon true
|
||||
:check-icon-visible selected?)}]])]))
|
||||
|
||||
(mf/defc theme-options
|
||||
[{:keys [active-theme-paths themes on-close]}]
|
||||
[:ul
|
||||
(for [[group themes] themes]
|
||||
[:li {:key group}
|
||||
(when (seq group)
|
||||
[:span {:class (stl/css :group)} group])
|
||||
[:& themes-list {:themes themes
|
||||
:active-theme-paths active-theme-paths
|
||||
:on-close on-close
|
||||
:grouped? true}]])
|
||||
[:li {:class (stl/css-case :checked-element true
|
||||
:checked-element-button true)
|
||||
:on-click #(modal/show! :tokens/themes {})}
|
||||
[:span "Edit themes"]
|
||||
[:span {:class (stl/css :icon)} i/arrow]]])
|
||||
(let []
|
||||
(let [on-edit-click #(modal/show! :tokens/themes {})]
|
||||
[:ul {:class (stl/css :theme-options :custom-select-dropdown)
|
||||
:role "listbox"}
|
||||
(for [[group themes] themes]
|
||||
[:li {:key group
|
||||
:aria-labelledby (dm/str group "-label")
|
||||
:role "group"}
|
||||
(when (seq group)
|
||||
[:> text* {:as "span" :typography "headline-small" :class (stl/css :group) :id (dm/str group "-label")} group])
|
||||
[:& themes-list {:themes themes
|
||||
:active-theme-paths active-theme-paths
|
||||
:on-close on-close
|
||||
: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
|
||||
[{:keys []}]
|
||||
|
@ -64,11 +81,11 @@
|
|||
|
||||
;; Data
|
||||
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)
|
||||
(ctob/split-token-theme-path)
|
||||
(str/join " / "))
|
||||
:else "No theme active")
|
||||
:else (tr "workspace.token.no-active-theme"))
|
||||
|
||||
;; State
|
||||
state* (mf/use-state
|
||||
|
@ -81,13 +98,20 @@
|
|||
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))]
|
||||
|
||||
;; TODO: This element should be accessible by keyboard
|
||||
[:div {:on-click on-open-dropdown
|
||||
:aria-expanded is-open?
|
||||
:aria-haspopup "listbox"
|
||||
:tab-index "0"
|
||||
:role "combobox"
|
||||
:class (stl/css :custom-select)}
|
||||
[:span {:class (stl/css :current-label)} current-label]
|
||||
[:span {:class (stl/css :dropdown-button)} i/arrow]
|
||||
[:& dropdown {:show is-open? :on-close on-close-dropdown}
|
||||
[:div {:ref dropdown-element*
|
||||
:class (stl/css :custom-select-dropdown)}
|
||||
[:& theme-options {:active-theme-paths active-theme-paths
|
||||
:themes themes
|
||||
:on-close on-close-dropdown}]]]]))
|
||||
[:> text* {:as "span" :typography "body-small" :class (stl/css :current-label)}
|
||||
current-label]
|
||||
[:> icon* {:id i/arrow-down :class (stl/css :dropdown-button) :aria-hidden true}]
|
||||
[:& dropdown {:show is-open?
|
||||
:on-close on-close-dropdown
|
||||
:ref dropdown-element*}
|
||||
[:& theme-options {:active-theme-paths active-theme-paths
|
||||
:themes themes
|
||||
:on-close on-close-dropdown}]]]))
|
||||
|
|
|
@ -7,12 +7,11 @@
|
|||
@import "refactor/common-refactor.scss";
|
||||
|
||||
.custom-select {
|
||||
--border-color: var(--menu-background-color);
|
||||
--bg-color: var(--menu-background-color);
|
||||
--icon-color: var(--icon-foreground);
|
||||
--text-color: var(--menu-foreground-color);
|
||||
--custom-select-border-color: var(--menu-background-color);
|
||||
--custom-select-bg-color: var(--menu-background-color);
|
||||
--custom-select-icon-color: var(--color-foreground-secondary);
|
||||
--custom-select-text-color: var(--menu-foreground-color);
|
||||
@extend .new-scrollbar;
|
||||
@include bodySmallTypography;
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
|
@ -22,74 +21,60 @@
|
|||
margin: 0;
|
||||
padding: $s-8;
|
||||
border-radius: $br-8;
|
||||
background-color: var(--bg-color);
|
||||
border: $s-1 solid var(--border-color);
|
||||
color: var(--text-color);
|
||||
background-color: var(--custom-select-bg-color);
|
||||
border: $s-1 solid var(--custom-select-border-color);
|
||||
color: var(--custom-select-text-color);
|
||||
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 {
|
||||
--bg-color: var(--menu-background-color-hover);
|
||||
--border-color: var(--menu-background-color);
|
||||
--icon-color: var(--menu-foreground-color-hover);
|
||||
--custom-select-bg-color: var(--menu-background-color-hover);
|
||||
--custom-select-border-color: var(--menu-background-color);
|
||||
--custom-select-icon-color: var(--menu-foreground-color-hover);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
--bg-color: var(--menu-background-color-focus);
|
||||
--border-color: var(--menu-background-focus);
|
||||
--custom-select-bg-color: var(--menu-background-color-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 {
|
||||
--bg-color: var(--menu-background-color-disabled);
|
||||
--border-color: var(--menu-border-color-disabled);
|
||||
--icon-color: var(--menu-foreground-color-disabled);
|
||||
--text-color: var(--menu-foreground-color-disabled);
|
||||
--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);
|
||||
--custom-select-text-color: var(--menu-foreground-color-disabled);
|
||||
pointer-events: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.dropdown-button {
|
||||
@include flexCenter;
|
||||
svg {
|
||||
@extend .button-icon-small;
|
||||
transform: rotate(90deg);
|
||||
stroke: var(--icon-color);
|
||||
}
|
||||
color: var(--color-foreground-secondary);
|
||||
}
|
||||
|
||||
.current-icon {
|
||||
@include flexCenter;
|
||||
width: $s-24;
|
||||
padding-right: $s-4;
|
||||
svg {
|
||||
@extend .button-icon-small;
|
||||
stroke: var(--icon-foreground);
|
||||
}
|
||||
}
|
||||
|
||||
.custom-select-dropdown {
|
||||
@extend .dropdown-wrapper;
|
||||
.separator {
|
||||
margin: 0;
|
||||
height: $s-12;
|
||||
border-block-start: $s-1 solid var(--dropdown-separator-color);
|
||||
}
|
||||
}
|
||||
|
||||
.separator {
|
||||
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"] {
|
||||
|
@ -109,59 +94,31 @@
|
|||
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 {
|
||||
@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 {
|
||||
color: var(--menu-foreground-color);
|
||||
.check-icon svg {
|
||||
stroke: var(--menu-foreground-color);
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
&.disabled {
|
||||
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 {
|
||||
@include textEllipsis;
|
||||
}
|
||||
|
|
|
@ -6163,4 +6163,72 @@ msgstr "Resolved value: "
|
|||
|
||||
#: src/app/main/ui/workspace/tokens/sidebar.cljs
|
||||
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"
|
|
@ -6150,4 +6150,72 @@ msgstr "Valor resuelto: "
|
|||
|
||||
#: src/app/main/ui/workspace/tokens/sidebar.cljs
|
||||
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"
|
Loading…
Reference in a new issue