mirror of
https://github.com/penpot/penpot.git
synced 2025-07-01 14:27:19 +02:00
Merge pull request #5604 from penpot/eva-add-viewer-role-on-tokens
This commit is contained in:
commit
aac52d70bc
10 changed files with 304 additions and 78 deletions
|
@ -306,7 +306,7 @@
|
||||||
title]
|
title]
|
||||||
(when children
|
(when children
|
||||||
[:*
|
[:*
|
||||||
[:> icon* {:icon-d "arrow" :size "s"}]
|
[:> icon* {:icon-id "arrow" :size "s"}]
|
||||||
[:ul {:class (stl/css :token-context-submenu)
|
[:ul {:class (stl/css :token-context-submenu)
|
||||||
:data-direction submenu-direction
|
:data-direction submenu-direction
|
||||||
:ref submenu-ref
|
:ref submenu-ref
|
||||||
|
|
|
@ -73,16 +73,27 @@
|
||||||
:default-value default-value}]))
|
:default-value default-value}]))
|
||||||
|
|
||||||
(mf/defc checkbox
|
(mf/defc checkbox
|
||||||
[{:keys [checked aria-label on-click]}]
|
[{:keys [checked aria-label on-click disabled]}]
|
||||||
(let [all? (true? checked)
|
(let [all? (true? checked)
|
||||||
mixed? (= checked "mixed")
|
mixed? (= checked "mixed")
|
||||||
checked? (or all? mixed?)]
|
checked? (or all? mixed?)
|
||||||
|
on-click
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps disabled)
|
||||||
|
(fn [e]
|
||||||
|
(when-not disabled
|
||||||
|
(on-click e))))]
|
||||||
[:div {:role "checkbox"
|
[:div {:role "checkbox"
|
||||||
:aria-checked (dm/str checked)
|
:aria-checked (dm/str checked)
|
||||||
|
:disabled disabled
|
||||||
|
:title (when disabled (tr "workspace.token.no-permisions-set"))
|
||||||
:tab-index 0
|
:tab-index 0
|
||||||
:class (stl/css-case :checkbox-style true
|
:class (stl/css-case :checkbox-style true
|
||||||
:checkbox-checked-style checked?)
|
:checkbox-checked-style checked?
|
||||||
|
:checkbox-disabled-checked (and checked? disabled)
|
||||||
|
:checkbox-disabled disabled)
|
||||||
:on-click on-click}
|
:on-click on-click}
|
||||||
|
|
||||||
(when checked?
|
(when checked?
|
||||||
[:> icon*
|
[:> icon*
|
||||||
{:aria-label aria-label
|
{:aria-label aria-label
|
||||||
|
@ -94,13 +105,14 @@
|
||||||
[{:keys [label tree-depth tree-path active? selected? collapsed? editing? on-toggle on-edit on-edit-reset on-edit-submit]}]
|
[{:keys [label tree-depth tree-path active? selected? collapsed? editing? on-toggle on-edit on-edit-reset on-edit-submit]}]
|
||||||
(let [editing?' (editing? tree-path)
|
(let [editing?' (editing? tree-path)
|
||||||
active?' (active? tree-path)
|
active?' (active? tree-path)
|
||||||
|
can-edit? (:can-edit (deref refs/permissions))
|
||||||
on-context-menu
|
on-context-menu
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(mf/deps editing? tree-path)
|
(mf/deps editing? tree-path can-edit?)
|
||||||
(fn [event]
|
(fn [event]
|
||||||
(dom/prevent-default event)
|
(dom/prevent-default event)
|
||||||
(dom/stop-propagation event)
|
(dom/stop-propagation event)
|
||||||
(when-not (editing? tree-path)
|
(when (and can-edit? (not editing?'))
|
||||||
(st/emit!
|
(st/emit!
|
||||||
(wdt/show-token-set-context-menu
|
(wdt/show-token-set-context-menu
|
||||||
{:position (dom/get-client-position event)
|
{:position (dom/get-client-position event)
|
||||||
|
@ -110,18 +122,26 @@
|
||||||
(fn [event]
|
(fn [event]
|
||||||
(dom/stop-propagation event)
|
(dom/stop-propagation event)
|
||||||
(swap! collapsed? not)))
|
(swap! collapsed? not)))
|
||||||
|
|
||||||
on-double-click
|
on-double-click
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(mf/deps tree-path)
|
(mf/deps tree-path can-edit?)
|
||||||
#(on-edit tree-path))
|
(fn []
|
||||||
|
(when can-edit?
|
||||||
|
(on-edit tree-path))))
|
||||||
|
|
||||||
on-checkbox-click
|
on-checkbox-click
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(mf/deps on-toggle tree-path)
|
(mf/deps on-toggle tree-path can-edit?)
|
||||||
#(on-toggle tree-path))
|
(fn []
|
||||||
|
(when can-edit?
|
||||||
|
(on-toggle tree-path))))
|
||||||
|
|
||||||
on-edit-submit'
|
on-edit-submit'
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(mf/deps tree-path on-edit-submit)
|
(mf/deps tree-path on-edit-submit can-edit?)
|
||||||
#(on-edit-submit tree-path %))]
|
(fn [e]
|
||||||
|
(when can-edit? (on-edit-submit tree-path e))))]
|
||||||
[:div {:role "button"
|
[:div {:role "button"
|
||||||
:data-testid "tokens-set-group-item"
|
:data-testid "tokens-set-group-item"
|
||||||
:style {"--tree-depth" tree-depth}
|
:style {"--tree-depth" tree-depth}
|
||||||
|
@ -147,6 +167,7 @@
|
||||||
label]
|
label]
|
||||||
[:& checkbox
|
[:& checkbox
|
||||||
{:on-click on-checkbox-click
|
{:on-click on-checkbox-click
|
||||||
|
:disabled (not can-edit?)
|
||||||
:checked (case active?'
|
:checked (case active?'
|
||||||
:all true
|
:all true
|
||||||
:partial "mixed"
|
:partial "mixed"
|
||||||
|
@ -158,6 +179,7 @@
|
||||||
(let [set-name (.-name set)
|
(let [set-name (.-name set)
|
||||||
editing?' (editing? tree-path)
|
editing?' (editing? tree-path)
|
||||||
active?' (some? (active? set-name))
|
active?' (some? (active? set-name))
|
||||||
|
can-edit? (:can-edit (deref refs/permissions))
|
||||||
on-click
|
on-click
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(mf/deps editing?' tree-path)
|
(mf/deps editing?' tree-path)
|
||||||
|
@ -167,11 +189,11 @@
|
||||||
(on-select tree-path))))
|
(on-select tree-path))))
|
||||||
on-context-menu
|
on-context-menu
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(mf/deps editing?' tree-path)
|
(mf/deps editing?' tree-path can-edit?)
|
||||||
(fn [event]
|
(fn [event]
|
||||||
(dom/prevent-default event)
|
(dom/prevent-default event)
|
||||||
(dom/stop-propagation event)
|
(dom/stop-propagation event)
|
||||||
(when-not editing?'
|
(when (and can-edit? (not editing?'))
|
||||||
(st/emit!
|
(st/emit!
|
||||||
(wdt/show-token-set-context-menu
|
(wdt/show-token-set-context-menu
|
||||||
{:position (dom/get-client-position event)
|
{:position (dom/get-client-position event)
|
||||||
|
@ -211,6 +233,7 @@
|
||||||
label]
|
label]
|
||||||
[:& checkbox
|
[:& checkbox
|
||||||
{:on-click on-checkbox-click
|
{:on-click on-checkbox-click
|
||||||
|
:disabled (not can-edit?)
|
||||||
:arial-label (tr "workspace.token.select-set")
|
:arial-label (tr "workspace.token.select-set")
|
||||||
:checked active?'}]])]))
|
:checked active?'}]])]))
|
||||||
|
|
||||||
|
|
|
@ -68,17 +68,28 @@
|
||||||
height: $s-16;
|
height: $s-16;
|
||||||
margin-inline: $s-6;
|
margin-inline: $s-6;
|
||||||
background-color: var(--input-checkbox-background-color-rest);
|
background-color: var(--input-checkbox-background-color-rest);
|
||||||
border: 1px solid var(--input-checkbox-border-color-rest);
|
border: $s-1 solid var(--input-checkbox-border-color-rest);
|
||||||
border-radius: 0.25rem;
|
border-radius: $s-4;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkbox-checked-style {
|
.checkbox-checked-style {
|
||||||
background-color: var(--input-border-color-active);
|
background-color: var(--input-border-color-active);
|
||||||
|
color: var(--color-background-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-disabled {
|
||||||
|
border: $s-1 solid var(--color-background-quaternary);
|
||||||
|
background-color: var(--color-background-tertiary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-disabled-checked {
|
||||||
|
background-color: var(--color-accent-primary-muted);
|
||||||
|
color: var(--color-background-quaternary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.check-icon {
|
.check-icon {
|
||||||
color: var(--color-background-secondary);
|
color: currentColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
.set-item-container:hover {
|
.set-item-container:hover {
|
||||||
|
|
|
@ -112,18 +112,20 @@
|
||||||
(wtch/toggle-token {:token token
|
(wtch/toggle-token {:token token
|
||||||
:shapes selected-shapes
|
:shapes selected-shapes
|
||||||
:token-type-props token-type-props})))))
|
:token-type-props token-type-props})))))
|
||||||
tokens-count (count tokens)]
|
tokens-count (count tokens)
|
||||||
|
can-edit? (:can-edit (deref refs/permissions))]
|
||||||
[:div {:on-click on-toggle-open-click}
|
[:div {:on-click on-toggle-open-click}
|
||||||
[:& cmm/asset-section {:icon (token-section-icon type)
|
[:& cmm/asset-section {:icon (token-section-icon type)
|
||||||
:title title
|
:title title
|
||||||
:assets-count tokens-count
|
:assets-count tokens-count
|
||||||
:open? open?}
|
:open? open?}
|
||||||
[:& cmm/asset-section-block {:role :title-button}
|
[:& cmm/asset-section-block {:role :title-button}
|
||||||
[:> icon-button* {:on-click on-popover-open-click
|
(when can-edit?
|
||||||
:variant "ghost"
|
[:> icon-button* {:on-click on-popover-open-click
|
||||||
:icon "add"
|
:variant "ghost"
|
||||||
;; TODO: This needs translation
|
:icon "add"
|
||||||
:aria-label (str "Add token: " title)}]]
|
;; TODO: This needs translation
|
||||||
|
:aria-label (str "Add token: " title)}])]
|
||||||
(when open?
|
(when open?
|
||||||
[:& cmm/asset-section-block {:role :content}
|
[:& cmm/asset-section-block {:role :content}
|
||||||
[:div {:class (stl/css :token-pills-wrapper)}
|
[:div {:class (stl/css :token-pills-wrapper)}
|
||||||
|
@ -137,7 +139,9 @@
|
||||||
on-context-menu (fn [e] (on-context-menu e token))]
|
on-context-menu (fn [e] (on-context-menu e token))]
|
||||||
[:& token-pill
|
[:& token-pill
|
||||||
{:key (:name token)
|
{:key (:name token)
|
||||||
|
:token-type-props token-type-props
|
||||||
:token token
|
:token token
|
||||||
|
:selected-shapes selected-shapes
|
||||||
:theme-token theme-token
|
:theme-token theme-token
|
||||||
:half-applied (or (and applied multiple-selection)
|
:half-applied (or (and applied multiple-selection)
|
||||||
(and applied (not full-applied)))
|
(and applied (not full-applied)))
|
||||||
|
@ -165,6 +169,7 @@
|
||||||
(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)
|
||||||
|
can-edit? (:can-edit (deref refs/permissions))
|
||||||
open-modal
|
open-modal
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(fn [e]
|
(fn [e]
|
||||||
|
@ -176,34 +181,45 @@
|
||||||
[:div {:class (stl/css :empty-theme-wrapper)}
|
[:div {:class (stl/css :empty-theme-wrapper)}
|
||||||
[:> text* {:as "span" :typography "body-small" :class (stl/css :empty-state-message)}
|
[:> text* {:as "span" :typography "body-small" :class (stl/css :empty-state-message)}
|
||||||
(tr "workspace.token.no-themes")]
|
(tr "workspace.token.no-themes")]
|
||||||
[:button {:on-click open-modal
|
(when can-edit?
|
||||||
:class (stl/css :create-theme-button)}
|
[:button {:on-click open-modal
|
||||||
(tr "workspace.token.create-one")]]
|
:class (stl/css :create-theme-button)}
|
||||||
[:div {:class (stl/css :theme-select-wrapper)}
|
(tr "workspace.token.create-one")])]
|
||||||
[:& theme-select]
|
(if can-edit?
|
||||||
[:> button* {:variant "secondary"
|
[:div {:class (stl/css :theme-select-wrapper)}
|
||||||
:class (stl/css :edit-theme-button)
|
[:& theme-select]
|
||||||
:on-click open-modal}
|
[:> button* {:variant "secondary"
|
||||||
(tr "labels.edit")]])]))
|
:class (stl/css :edit-theme-button)
|
||||||
|
:on-click open-modal}
|
||||||
|
(tr "labels.edit")]]
|
||||||
|
[:div {:title (when-not can-edit?
|
||||||
|
(tr "workspace.token.no-permission-themes"))}
|
||||||
|
[:& theme-select]]))]))
|
||||||
|
|
||||||
(mf/defc add-set-button
|
(mf/defc add-set-button
|
||||||
[{:keys [on-open style]}]
|
[{:keys [on-open style]}]
|
||||||
(let [{:keys [on-create new?]} (sets-context/use-context)
|
(let [{:keys [on-create new?]} (sets-context/use-context)
|
||||||
on-click #(do
|
on-click #(do
|
||||||
(on-open)
|
(on-open)
|
||||||
(on-create))]
|
(on-create))
|
||||||
|
can-edit? (:can-edit (deref refs/permissions))]
|
||||||
(if (= style "inline")
|
(if (= style "inline")
|
||||||
(when-not new?
|
(when-not new?
|
||||||
[:div {:class (stl/css :empty-sets-wrapper)}
|
(if can-edit?
|
||||||
[:> text* {:as "span" :typography "body-small" :class (stl/css :empty-state-message)}
|
[:div {:class (stl/css :empty-sets-wrapper)}
|
||||||
(tr "workspace.token.no-sets-yet")]
|
[:> text* {:as "span" :typography "body-small" :class (stl/css :empty-state-message)}
|
||||||
[:button {:on-click on-click
|
(tr "workspace.token.no-sets-yet")]
|
||||||
:class (stl/css :create-theme-button)}
|
[:button {:on-click on-click
|
||||||
(tr "workspace.token.create-one")]])
|
:class (stl/css :create-theme-button)}
|
||||||
[:> icon-button* {:variant "ghost"
|
(tr "workspace.token.create-one")]]
|
||||||
:icon "add"
|
[:div {:class (stl/css :empty-sets-wrapper)}
|
||||||
:on-click on-click
|
[:> text* {:as "span" :typography "body-small" :class (stl/css :empty-state-message)}
|
||||||
:aria-label (tr "workspace.token.add set")}])))
|
(tr "workspace.token.no-sets-yet")]]))
|
||||||
|
(when can-edit?
|
||||||
|
[:> icon-button* {:variant "ghost"
|
||||||
|
:icon "add"
|
||||||
|
:on-click on-click
|
||||||
|
:aria-label (tr "workspace.token.add set")}]))))
|
||||||
|
|
||||||
(mf/defc theme-sets-list
|
(mf/defc theme-sets-list
|
||||||
[{:keys [on-open]}]
|
[{:keys [on-open]}]
|
||||||
|
@ -219,7 +235,8 @@
|
||||||
(mf/defc themes-sets-tab
|
(mf/defc themes-sets-tab
|
||||||
[{:keys [resize-height]}]
|
[{:keys [resize-height]}]
|
||||||
(let [open? (mf/use-state true)
|
(let [open? (mf/use-state true)
|
||||||
on-open (mf/use-fn #(reset! open? true))]
|
on-open (mf/use-fn #(reset! open? true))
|
||||||
|
can-edit? (:can-edit (deref refs/permissions))]
|
||||||
[:& sets-context/provider {}
|
[:& sets-context/provider {}
|
||||||
[:& sets-context-menu]
|
[:& sets-context-menu]
|
||||||
[:article {:data-testid "token-themes-sets-sidebar"
|
[:article {:data-testid "token-themes-sets-sidebar"
|
||||||
|
@ -229,8 +246,9 @@
|
||||||
[:& themes-header]
|
[:& themes-header]
|
||||||
[:div {:class (stl/css :sidebar-header)}
|
[:div {:class (stl/css :sidebar-header)}
|
||||||
[:& title-bar {:title (tr "labels.sets")}
|
[:& title-bar {:title (tr "labels.sets")}
|
||||||
[:& add-set-button {:on-open on-open
|
(when can-edit?
|
||||||
:style "header"}]]]
|
[:& add-set-button {:on-open on-open
|
||||||
|
:style "header"}])]]
|
||||||
[:& theme-sets-list {:on-open on-open}]]]]))
|
[:& theme-sets-list {:on-open on-open}]]]]))
|
||||||
|
|
||||||
(mf/defc tokens-tab
|
(mf/defc tokens-tab
|
||||||
|
@ -270,6 +288,7 @@
|
||||||
[{:keys []}]
|
[{:keys []}]
|
||||||
(let [show-menu* (mf/use-state false)
|
(let [show-menu* (mf/use-state false)
|
||||||
show-menu? (deref show-menu*)
|
show-menu? (deref show-menu*)
|
||||||
|
can-edit? (:can-edit (deref refs/permissions))
|
||||||
|
|
||||||
open-menu
|
open-menu
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
|
@ -311,12 +330,13 @@
|
||||||
(dom/trigger-download "tokens.json"))))]
|
(dom/trigger-download "tokens.json"))))]
|
||||||
|
|
||||||
[:div {:class (stl/css :import-export-button-wrapper)}
|
[:div {:class (stl/css :import-export-button-wrapper)}
|
||||||
[:input {:type "file"
|
(when can-edit?
|
||||||
:ref input-ref
|
[:input {:type "file"
|
||||||
:style {:display "none"}
|
:ref input-ref
|
||||||
:id "file-input"
|
:style {:display "none"}
|
||||||
:accept ".json"
|
:id "file-input"
|
||||||
:on-change on-import}]
|
:accept ".json"
|
||||||
|
:on-change on-import}])
|
||||||
[:> button* {:on-click open-menu
|
[:> button* {:on-click open-menu
|
||||||
:icon "import-export"
|
:icon "import-export"
|
||||||
:variant "secondary"}
|
:variant "secondary"}
|
||||||
|
@ -324,9 +344,10 @@
|
||||||
[:& dropdown-menu {:show show-menu?
|
[:& dropdown-menu {:show show-menu?
|
||||||
:on-close close-menu
|
:on-close close-menu
|
||||||
:list-class (stl/css :import-export-menu)}
|
:list-class (stl/css :import-export-menu)}
|
||||||
[:> dropdown-menu-item* {:class (stl/css :import-export-menu-item)
|
(when can-edit?
|
||||||
:on-click on-option-click}
|
[:> dropdown-menu-item* {:class (stl/css :import-export-menu-item)
|
||||||
(tr "labels.import")]
|
:on-click on-option-click}
|
||||||
|
(tr "labels.import")])
|
||||||
|
|
||||||
[:> dropdown-menu-item* {:class (stl/css :import-export-menu-item)
|
[:> dropdown-menu-item* {:class (stl/css :import-export-menu-item)
|
||||||
:on-click on-export}
|
:on-click on-export}
|
||||||
|
|
|
@ -65,7 +65,7 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin-left: $s-8;
|
margin-left: $s-12;
|
||||||
padding-top: $s-12;
|
padding-top: $s-12;
|
||||||
color: var(--layer-row-foreground-color);
|
color: var(--layer-row-foreground-color);
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,7 +78,7 @@
|
||||||
active-theme-paths (mf/deref refs/workspace-active-theme-paths-no-hidden)
|
active-theme-paths (mf/deref refs/workspace-active-theme-paths-no-hidden)
|
||||||
active-themes-count (count active-theme-paths)
|
active-themes-count (count active-theme-paths)
|
||||||
themes (mf/deref refs/workspace-token-theme-tree-no-hidden)
|
themes (mf/deref refs/workspace-token-theme-tree-no-hidden)
|
||||||
|
can-edit? (:can-edit (deref refs/permissions))
|
||||||
;; Data
|
;; Data
|
||||||
current-label (cond
|
current-label (cond
|
||||||
(> active-themes-count 1) (tr "workspace.token.active-themes" active-themes-count)
|
(> active-themes-count 1) (tr "workspace.token.active-themes" active-themes-count)
|
||||||
|
@ -97,15 +97,23 @@
|
||||||
;; Dropdown
|
;; Dropdown
|
||||||
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
|
||||||
|
(mf/deps can-edit?)
|
||||||
|
(fn []
|
||||||
|
(when can-edit?
|
||||||
|
(swap! state* assoc :is-open? true))))]
|
||||||
|
|
||||||
;; TODO: This element should be accessible by keyboard
|
;; TODO: This element should be accessible by keyboard
|
||||||
[:div {:on-click on-open-dropdown
|
[:div {:on-click on-open-dropdown
|
||||||
|
:disabled (not can-edit?)
|
||||||
:aria-expanded is-open?
|
:aria-expanded is-open?
|
||||||
:aria-haspopup "listbox"
|
:aria-haspopup "listbox"
|
||||||
:tab-index "0"
|
:tab-index "0"
|
||||||
:role "combobox"
|
:role "combobox"
|
||||||
:class (stl/css :custom-select)}
|
:class (stl/css-case :custom-select true
|
||||||
|
:disabled-select (not can-edit?))}
|
||||||
[:> text* {:as "span" :typography "body-small" :class (stl/css :current-label)}
|
[:> text* {:as "span" :typography "body-small" :class (stl/css :current-label)}
|
||||||
current-label]
|
current-label]
|
||||||
[:> icon* {:icon-id i/arrow-down :class (stl/css :dropdown-button) :aria-hidden true}]
|
[:> icon* {:icon-id i/arrow-down :class (stl/css :dropdown-button) :aria-hidden true}]
|
||||||
|
|
|
@ -47,7 +47,7 @@
|
||||||
color: var(--color-foreground-secondary);
|
color: var(--color-foreground-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.disabled {
|
.disabled-select {
|
||||||
--custom-select-bg-color: var(--menu-background-color-disabled);
|
--custom-select-bg-color: var(--menu-background-color-disabled);
|
||||||
--custom-select-border-color: var(--menu-border-color-disabled);
|
--custom-select-border-color: var(--menu-border-color-disabled);
|
||||||
--custom-select-icon-color: var(--menu-foreground-color-disabled);
|
--custom-select-icon-color: var(--menu-foreground-color-disabled);
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
(ns app.main.ui.workspace.tokens.token-pill
|
(ns app.main.ui.workspace.tokens.token-pill
|
||||||
(:require-macros [app.main.style :as stl])
|
(:require-macros
|
||||||
|
[app.common.data.macros :as dm]
|
||||||
|
[app.main.style :as stl])
|
||||||
(:require
|
(:require
|
||||||
[app.common.files.helpers :as cfh]
|
[app.common.files.helpers :as cfh]
|
||||||
[app.common.types.tokens-lib :as ctob]
|
[app.common.types.tokens-lib :as ctob]
|
||||||
|
[app.main.refs :as refs]
|
||||||
[app.main.ui.components.color-bullet :refer [color-bullet]]
|
[app.main.ui.components.color-bullet :refer [color-bullet]]
|
||||||
[app.main.ui.ds.foundations.assets.icon :refer [icon*]]
|
[app.main.ui.ds.foundations.assets.icon :refer [icon*]]
|
||||||
[app.main.ui.ds.foundations.utilities.token.token-status :refer [token-status-icon*]]
|
[app.main.ui.ds.foundations.utilities.token.token-status :refer [token-status-icon*]]
|
||||||
|
@ -13,16 +16,135 @@
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
|
;; Translation dictionaries
|
||||||
|
(def ^:private attribute-dictionary
|
||||||
|
{:rotation "Rotation"
|
||||||
|
:opacity "Opacity"
|
||||||
|
:stroke-width "Stroke Width"
|
||||||
|
|
||||||
|
;; Spacing
|
||||||
|
:p1 "Top" :p2 "Right" :p3 "Bottom" :p4 "Left"
|
||||||
|
:column-gap "Column Gap" :row-gap "Row Gap"
|
||||||
|
|
||||||
|
;; Sizing
|
||||||
|
:width "Width"
|
||||||
|
:height "Height"
|
||||||
|
:layout-item-min-w "Min Width"
|
||||||
|
:layout-item-min-h "Min Height"
|
||||||
|
:layout-item-max-w "Max Width"
|
||||||
|
:layout-item-max-h "Max Height"
|
||||||
|
|
||||||
|
;; Border Radius
|
||||||
|
:r1 "Top Left" :r2 "Top Right" :r4 "Bottom Left" :r3 "Bottom Right"
|
||||||
|
|
||||||
|
;; Dimensions
|
||||||
|
:x "X" :y "Y"
|
||||||
|
|
||||||
|
;; Color
|
||||||
|
:fill "Fill"
|
||||||
|
:stroke-color "Stroke Color"})
|
||||||
|
|
||||||
|
(def ^:private dimensions-dictionary
|
||||||
|
{:stroke-width :stroke-width
|
||||||
|
:p1 :spacing
|
||||||
|
:p2 :spacing
|
||||||
|
:p3 :spacing
|
||||||
|
:p4 :spacing
|
||||||
|
:column-gap :spacing
|
||||||
|
:row-gap :spacing
|
||||||
|
:width :sizing
|
||||||
|
:height :sizing
|
||||||
|
:layout-item-min-w :sizing
|
||||||
|
:layout-item-min-h :sizing
|
||||||
|
:layout-item-max-w :sizing
|
||||||
|
:layout-item-max-h :sizing
|
||||||
|
:r1 :border-radius
|
||||||
|
:r2 :border-radius
|
||||||
|
:r4 :border-radius
|
||||||
|
:r3 :border-radius
|
||||||
|
:x :x
|
||||||
|
:y :y})
|
||||||
|
|
||||||
|
(def ^:private category-dictionary
|
||||||
|
{:stroke-width "Stroke Width"
|
||||||
|
:spacing "Spacing"
|
||||||
|
:sizing "Sizing"
|
||||||
|
:border-radius "Border Radius"
|
||||||
|
:x "X"
|
||||||
|
:y "Y"})
|
||||||
|
|
||||||
|
;; Helper functions
|
||||||
|
(defn partially-applied-attr
|
||||||
|
"Translates partially applied attributes based on the dictionary."
|
||||||
|
[app-token-keys is-applied token-type-props]
|
||||||
|
(let [{:keys [attributes all-attributes]} token-type-props
|
||||||
|
filtered-keys (if all-attributes
|
||||||
|
(filter #(contains? all-attributes %) app-token-keys)
|
||||||
|
(filter #(contains? attributes %) app-token-keys))]
|
||||||
|
(when is-applied
|
||||||
|
(str/join ", " (map attribute-dictionary filtered-keys)))))
|
||||||
|
|
||||||
|
(defn translate-and-format
|
||||||
|
"Translates and formats grouped values by category."
|
||||||
|
[grouped-values]
|
||||||
|
(str/join "\n"
|
||||||
|
(map (fn [[category values]]
|
||||||
|
(if (#{:x :y} category)
|
||||||
|
(dm/str "- " (category-dictionary category))
|
||||||
|
(dm/str "- " (category-dictionary category) ": "
|
||||||
|
(str/join ", " (map attribute-dictionary values)) ".")))
|
||||||
|
grouped-values)))
|
||||||
|
|
||||||
|
(defn token-pill-tooltip
|
||||||
|
"Generates a tooltip for a given token."
|
||||||
|
[theme-token is-viewer shape token-type-props token half-applied]
|
||||||
|
(let [{:keys [name value resolved-value errors type]} token
|
||||||
|
{:keys [title]} token-type-props
|
||||||
|
applied-tokens (:applied-tokens shape)
|
||||||
|
app-token-vals (set (vals applied-tokens))
|
||||||
|
app-token-keys (keys applied-tokens)
|
||||||
|
is-applied? (contains? app-token-vals name)
|
||||||
|
no-token-active (nil? theme-token)
|
||||||
|
errors? (or no-token-active (seq errors))
|
||||||
|
applied-to (if half-applied
|
||||||
|
(partially-applied-attr app-token-keys is-applied? token-type-props)
|
||||||
|
(tr "labels.all"))
|
||||||
|
grouped-values (group-by dimensions-dictionary app-token-keys)
|
||||||
|
|
||||||
|
base-title (dm/str "Token: " name "\n"
|
||||||
|
(tr "workspace.token.original-value" value) "\n"
|
||||||
|
(tr "workspace.token.resolved-value" resolved-value))]
|
||||||
|
|
||||||
|
(cond
|
||||||
|
;; If there are errors, show the appropriate message
|
||||||
|
errors? (if no-token-active
|
||||||
|
(tr "workspace.token-set.not-active")
|
||||||
|
(sd/humanize-errors token))
|
||||||
|
;; If the token is applied and the user is a is-viewer, show the details
|
||||||
|
(and is-applied? is-viewer)
|
||||||
|
(->> [base-title
|
||||||
|
(tr "workspace.token.applied-to")
|
||||||
|
(if (= :dimensions type)
|
||||||
|
(translate-and-format grouped-values)
|
||||||
|
(str "- " title ": " applied-to))]
|
||||||
|
(str/join "\n"))
|
||||||
|
;; Otherwise only show the base title
|
||||||
|
:else base-title)))
|
||||||
|
|
||||||
(mf/defc token-pill
|
(mf/defc token-pill
|
||||||
{::mf/wrap-props false}
|
{::mf/wrap-props false}
|
||||||
[{:keys [on-click token theme-token full-applied on-context-menu half-applied]}]
|
[{:keys [on-click token theme-token full-applied on-context-menu half-applied selected-shapes token-type-props]}]
|
||||||
(let [{:keys [name value resolved-value errors]} token
|
(let [{:keys [name value errors]} token
|
||||||
errors? (or (nil? theme-token) (and (seq errors) (seq (:errors theme-token))))
|
|
||||||
color (when (seq (ctob/find-token-value-references value))
|
can-edit? (:can-edit (deref refs/permissions))
|
||||||
(wtt/resolved-value-hex theme-token))
|
is-viewer (not can-edit?)
|
||||||
|
errors? (or (nil? theme-token) (and (seq errors) (seq (:errors theme-token))))
|
||||||
|
color (when (seq (ctob/find-token-value-references value))
|
||||||
|
(wtt/resolved-value-hex theme-token))
|
||||||
contains-path? (str/includes? name ".")
|
contains-path? (str/includes? name ".")
|
||||||
splitted-name (cfh/split-by-last-period name)
|
splitted-name (cfh/split-by-last-period name)
|
||||||
color (or color (wtt/resolved-value-hex token))
|
color (or color (wtt/resolved-value-hex token))
|
||||||
|
|
||||||
on-click
|
on-click
|
||||||
(mf/use-callback
|
(mf/use-callback
|
||||||
(mf/deps errors? on-click)
|
(mf/deps errors? on-click)
|
||||||
|
@ -37,21 +159,46 @@
|
||||||
full-applied
|
full-applied
|
||||||
"token-status-full"
|
"token-status-full"
|
||||||
:else
|
:else
|
||||||
"token-status-non-applied")]
|
"token-status-non-applied")
|
||||||
|
|
||||||
|
on-context-menu
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps can-edit? on-context-menu)
|
||||||
|
(fn [e]
|
||||||
|
(dom/stop-propagation e)
|
||||||
|
(when can-edit?
|
||||||
|
(on-context-menu e))))
|
||||||
|
|
||||||
|
on-click
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps errors? on-click)
|
||||||
|
(fn [event]
|
||||||
|
(dom/stop-propagation event)
|
||||||
|
(when (and can-edit? (not (seq errors)) on-click)
|
||||||
|
(on-click event))))
|
||||||
|
|
||||||
|
on-hover
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps selected-shapes is-viewer)
|
||||||
|
(fn [event]
|
||||||
|
(let [node (dom/get-current-target event)
|
||||||
|
title (token-pill-tooltip theme-token is-viewer (first selected-shapes) token-type-props token half-applied)]
|
||||||
|
(dom/set-attribute! node "title" title))))]
|
||||||
|
|
||||||
[:button {:class (stl/css-case :token-pill true
|
[:button {:class (stl/css-case :token-pill true
|
||||||
:token-pill-applied (or half-applied full-applied)
|
:token-pill-applied (and can-edit? (or half-applied full-applied))
|
||||||
:token-pill-invalid errors?
|
:token-pill-invalid (and can-edit? errors?)
|
||||||
:token-pill-invalid-applied (and full-applied errors?))
|
:token-pill-invalid-applied (and full-applied errors? can-edit?)
|
||||||
|
:token-pill-viewer is-viewer
|
||||||
|
:token-pill-applied-viewer (and is-viewer
|
||||||
|
(or half-applied full-applied))
|
||||||
|
:token-pill-invalid-viewer (and is-viewer
|
||||||
|
errors?)
|
||||||
|
:token-pill-invalid-applied-viewer (and is-viewer
|
||||||
|
(and full-applied errors?)))
|
||||||
:type "button"
|
:type "button"
|
||||||
:title (cond
|
|
||||||
errors? (if (nil? theme-token)
|
|
||||||
(tr "workspace.token-set.not-active")
|
|
||||||
(sd/humanize-errors token))
|
|
||||||
:else (->> [(str "Token: " name)
|
|
||||||
(tr "workspace.token.original-value" value)
|
|
||||||
(tr "workspace.token.resolved-value" resolved-value)]
|
|
||||||
(str/join "\n")))
|
|
||||||
:on-click on-click
|
:on-click on-click
|
||||||
|
:on-mouse-enter on-hover
|
||||||
:on-context-menu on-context-menu}
|
:on-context-menu on-context-menu}
|
||||||
(cond
|
(cond
|
||||||
errors?
|
errors?
|
||||||
|
|
|
@ -6723,6 +6723,10 @@ msgstr "Add set"
|
||||||
msgid "workspace.token.tools"
|
msgid "workspace.token.tools"
|
||||||
msgstr "Tools"
|
msgstr "Tools"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/tokens/sidebar.cljs
|
||||||
|
msgid "workspace.token.no-permission-themes"
|
||||||
|
msgstr "You need to be an editor to use themes"
|
||||||
|
|
||||||
#: src/app/main/ui/workspace/tokens/modals/themes.cljs
|
#: src/app/main/ui/workspace/tokens/modals/themes.cljs
|
||||||
msgid "workspace.token.save-theme"
|
msgid "workspace.token.save-theme"
|
||||||
msgstr "Save theme"
|
msgstr "Save theme"
|
||||||
|
@ -6799,6 +6803,10 @@ msgstr "Define what token sets should be used as part of this theme option:"
|
||||||
msgid "workspace.token.no-sets-yet"
|
msgid "workspace.token.no-sets-yet"
|
||||||
msgstr "There are no sets yet."
|
msgstr "There are no sets yet."
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/tokens/sets.cljs
|
||||||
|
msgid "workspace.token.no-permisions-set"
|
||||||
|
msgstr "You need to be an editor to activate / deactivate sets"
|
||||||
|
|
||||||
#: src/app/main/ui/workspace/tokens/sets.cljs
|
#: src/app/main/ui/workspace/tokens/sets.cljs
|
||||||
msgid "workspace.token.no-sets-create"
|
msgid "workspace.token.no-sets-create"
|
||||||
msgstr "There are no sets defined yet. Create one first."
|
msgstr "There are no sets defined yet. Create one first."
|
||||||
|
|
|
@ -6729,6 +6729,10 @@ msgstr "Añadir set"
|
||||||
msgid "workspace.token.tools"
|
msgid "workspace.token.tools"
|
||||||
msgstr "Herramientas"
|
msgstr "Herramientas"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/tokens/sidebar.cljs
|
||||||
|
msgid "workspace.token.no-permission-themes"
|
||||||
|
msgstr "Debes ser editor para usar temas"
|
||||||
|
|
||||||
#: src/app/main/ui/workspace/tokens/modals/themes.cljs
|
#: src/app/main/ui/workspace/tokens/modals/themes.cljs
|
||||||
msgid "workspace.token.save-theme"
|
msgid "workspace.token.save-theme"
|
||||||
msgstr "Guardar tema"
|
msgstr "Guardar tema"
|
||||||
|
@ -6829,6 +6833,10 @@ msgstr "Duplicar token"
|
||||||
msgid "workspace.token.edit"
|
msgid "workspace.token.edit"
|
||||||
msgstr "Editar token"
|
msgstr "Editar token"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/tokens/sets.cljs
|
||||||
|
msgid "workspace.token.no-permisions-set"
|
||||||
|
msgstr "Debes ser editor para activar / desactivar sets"
|
||||||
|
|
||||||
msgid "workspace.versions.button.save"
|
msgid "workspace.versions.button.save"
|
||||||
msgstr "Guardar versión"
|
msgstr "Guardar versión"
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue