0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-04-16 17:01:33 -05:00

💄 Improve context menu scanability

This commit is contained in:
Xavier Julian 2025-02-27 00:10:41 +01:00 committed by Xaviju
parent 7da97d69b0
commit 6cccacaaab
3 changed files with 125 additions and 37 deletions

View file

@ -36,7 +36,7 @@
:shape-ids shape-ids
:selected-pred #(seq (% ids-by-attributes))}))
(defn generic-attribute-actions [attributes title {:keys [token selected-shapes on-update-shape]}]
(defn generic-attribute-actions [attributes title {:keys [token selected-shapes on-update-shape hint]}]
(let [on-update-shape-fn (or on-update-shape
(-> (wtch/get-token-properties token)
(:on-update-shape)))
@ -48,6 +48,7 @@
:shape-ids shape-ids}]
{:title title
:hint hint
:selected? selected?
:action (fn []
(if selected?
@ -55,8 +56,8 @@
(st/emit! (wtch/apply-token (assoc props :on-update-shape on-update-shape-fn)))))}))
attributes)))
(defn all-or-sepearate-actions [{:keys [attribute-labels on-update-shape-all on-update-shape]}
{:keys [token selected-shapes]}]
(defn all-or-separate-actions [{:keys [attribute-labels on-update-shape-all on-update-shape hint]}
{:keys [token selected-shapes]}]
(let [attributes (set (keys attribute-labels))
{:keys [all-selected? selected-pred shape-ids]} (attribute-actions token selected-shapes attributes)
all-action (let [props {:attributes attributes
@ -64,6 +65,7 @@
:shape-ids shape-ids}]
{:title (tr "labels.all")
:selected? all-selected?
:hint hint
:action #(if all-selected?
(st/emit! (wtch/unapply-token props))
(st/emit! (wtch/apply-token (assoc props :on-update-shape (or on-update-shape-all on-update-shape)))))})
@ -84,7 +86,7 @@
attribute-labels)]
(concat [all-action] single-actions)))
(defn layout-spacing-items [{:keys [token selected-shapes all-attr-labels horizontal-attr-labels vertical-attr-labels on-update-shape]}]
(defn layout-spacing-items [{:keys [token selected-shapes all-attr-labels horizontal-attr-labels vertical-attr-labels on-update-shape hint]}]
(let [horizontal-attrs (into #{} (keys horizontal-attr-labels))
vertical-attrs (into #{} (keys vertical-attr-labels))
attrs (set/union horizontal-attrs vertical-attrs)
@ -97,6 +99,7 @@
(every? selected-pred vertical-attrs))
multi-items [{:title (tr "labels.all")
:selected? all-selected?
:hint hint
:action (fn []
(let [props {:attributes attrs
:token token
@ -171,6 +174,7 @@
:p2 "Padding right"
:p3 "Padding bottom"
:p4 "Padding left"}
:hint (tr "workspace.token.paddings")
:horizontal-attr-labels {:p2 "Padding right"
:p4 "Padding left"}
:vertical-attr-labels {:p1 "Padding top"
@ -182,15 +186,17 @@
:m2 "Margin right"
:m3 "Margin bottom"
:m4 "Margin left"}
:hint (tr "workspace.token.margins")
:horizontal-attr-labels {:m2 "Margin right"
:m4 "Margin left"}
:vertical-attr-labels {:m1 "Margin top"
:m3 "Margin bottom"}
:on-update-shape update-shape-layout-margin})
gap-items (all-or-sepearate-actions {:attribute-labels {:column-gap "Column Gap"
:row-gap "Row Gap"}
:on-update-shape wtch/update-layout-spacing}
context-data)]
gap-items (all-or-separate-actions {:attribute-labels {:column-gap "Column Gap"
:row-gap "Row Gap"}
:hint (tr "workspace.token.gaps")
:on-update-shape wtch/update-layout-spacing}
context-data)]
(concat gap-items
[:separator]
padding-items
@ -199,20 +205,23 @@
(defn sizing-attribute-actions [context-data]
(concat
(all-or-sepearate-actions {:attribute-labels {:width "Width"
:height "Height"}
:on-update-shape wtch/update-shape-dimensions}
context-data)
(all-or-separate-actions {:attribute-labels {:width "Width"
:height "Height"}
:hint (tr "workspace.token.size")
:on-update-shape wtch/update-shape-dimensions}
context-data)
[:separator]
(all-or-sepearate-actions {:attribute-labels {:layout-item-min-w "Min Width"
:layout-item-min-h "Min Height"}
:on-update-shape wtch/update-layout-sizing-limits}
context-data)
(all-or-separate-actions {:attribute-labels {:layout-item-min-w "Min Width"
:layout-item-min-h "Min Height"}
:hint (tr "workspace.token.min-size")
:on-update-shape wtch/update-layout-sizing-limits}
context-data)
[:separator]
(all-or-sepearate-actions {:attribute-labels {:layout-item-max-w "Max Width"
:layout-item-max-h "Max Height"}
:on-update-shape wtch/update-layout-sizing-limits}
context-data)))
(all-or-separate-actions {:attribute-labels {:layout-item-max-w "Max Width"
:layout-item-max-h "Max Height"}
:hint (tr "workspace.token.max-size")
:on-update-shape wtch/update-layout-sizing-limits}
context-data)))
(defn update-shape-radius-for-corners [value shape-ids attributes]
(st/emit!
@ -221,14 +230,15 @@
(def shape-attribute-actions-map
(let [stroke-width (partial generic-attribute-actions #{:stroke-width} "Stroke Width")]
{:border-radius (partial all-or-sepearate-actions {:attribute-labels {:r1 "Top Left"
:r2 "Top Right"
:r4 "Bottom Left"
:r3 "Bottom Right"}
:on-update-shape-all wtch/update-shape-radius-all
:on-update-shape update-shape-radius-for-corners})
{:border-radius (partial all-or-separate-actions {:attribute-labels {:r1 "Top Left"
:r2 "Top Right"
:r4 "Bottom Left"
:r3 "Bottom Right"}
:hint (tr "workspace.token.radius")
:on-update-shape-all wtch/update-shape-radius-all
:on-update-shape update-shape-radius-for-corners})
:color (fn [context-data]
[(generic-attribute-actions #{:fill} "Fill" (assoc context-data :on-update-shape wtch/update-fill))
[(generic-attribute-actions #{:fill} "Fill" (assoc context-data :on-update-shape wtch/update-fill :hint (tr "workspace.token.color")))
(generic-attribute-actions #{:stroke-color} "Stroke" (assoc context-data :on-update-shape wtch/update-stroke-color))])
:spacing spacing-attribute-actions
:sizing sizing-attribute-actions
@ -244,7 +254,7 @@
[:separator]
(stroke-width (assoc context-data :on-update-shape wtch/update-stroke-width))
[:separator]
(generic-attribute-actions #{:x} "X" (assoc context-data :on-update-shape wtch/update-shape-position))
(generic-attribute-actions #{:x} "X" (assoc context-data :on-update-shape wtch/update-shape-position :hint (tr "workspace.token.axis")))
(generic-attribute-actions #{:y} "Y" (assoc context-data :on-update-shape wtch/update-shape-position))))}))
(defn default-actions [{:keys [token selected-token-set-name]}]
@ -297,9 +307,10 @@
(mf/defc menu-entry
{::mf/props :obj}
[{:keys [title value on-click selected? children submenu-offset submenu-direction no-selectable]}]
[{:keys [title value hint on-click selected? children submenu-offset submenu-direction no-selectable]}]
(let [submenu-ref (mf/use-ref nil)
hovering? (mf/use-ref false)
hint? (and hint (seq hint))
on-pointer-enter
(mf/use-fn
(fn []
@ -331,18 +342,21 @@
(when (= submenu-direction "up")
(dom/set-css-property! submenu-node "top" "unset")))))
[:li {:class (stl/css :context-menu-item)
[:li {:class (stl/css-case
:context-menu-item true
:context-menu-item-selected (and (not no-selectable) selected?)
:context-menu-item-unselected (and (not no-selectable) (not selected?))
:context-menu-item-hint-wrapper hint?)
:ref set-dom-node
:data-value value
:on-click on-click
:on-pointer-enter on-pointer-enter
:on-pointer-leave on-pointer-leave}
(when selected?
(when hint
[:span {:class (stl/css :context-menu-item-hint)} hint])
(when (not no-selectable)
[:> icon* {:icon-id "tick" :size "s" :class (stl/css :icon-wrapper)}])
[:span {:class (stl/css-case :item-text true
:item-with-icon-space (and
(not selected?)
(not no-selectable)))}
[:span {:class (stl/css :item-text)}
title]
(when children
[:*
@ -368,11 +382,12 @@
(submenu-actions-selection-actions context-data)
(selection-actions context-data))
(default-actions context-data))]
(for [[index {:keys [title action selected? submenu no-selectable] :as entry}] (d/enumerate entries)]
(for [[index {:keys [title action selected? hint submenu no-selectable] :as entry}] (d/enumerate entries)]
[:* {:key (dm/str title " " index)}
(cond
(= :separator entry) [:li {:class (stl/css :separator)}]
submenu [:& menu-entry {:title title
:hint hint
:no-selectable true
:submenu-direction submenu-direction
:submenu-offset submenu-offset}
@ -380,6 +395,7 @@
:else [:& menu-entry
{:title title
:on-click action
:hint hint
:no-selectable no-selectable
:selected? selected?}])])))

View file

@ -59,7 +59,7 @@
@include use-typography("body-small");
display: flex;
align-items: center;
height: $s-28;
height: $s-32;
width: 100%;
padding: $s-8;
border-radius: $br-8;
@ -81,6 +81,42 @@
}
}
.context-menu-item-hint-wrapper {
position: relative;
}
.context-menu-item-selected {
& .icon-wrapper {
color: var(--color-accent-primary);
}
}
.context-menu-item-unselected {
color: var(--color-foreground-secondary);
& .icon-wrapper {
color: var(--color-background-quaternary);
}
&:hover {
color: var(--color-foreground-primary);
& .icon-wrapper {
color: var(--color-foreground-secondary);
}
}
}
.context-menu-item-hint {
position: absolute;
background-color: var(--color-background-primary);
border-radius: $br-6;
padding: $s-4;
inset-inline-end: $s-4;
inset-block-start: $s-4;
color: var(--color-foreground-secondary);
}
.item-text {
flex-grow: 1;
}

View file

@ -6522,6 +6522,42 @@ msgstr "Delete theme"
msgid "workspace.token.duplicate"
msgstr "Duplicate token"
#: src/app/main/ui/workspace/tokens/context_menu.cljs:235
msgid "workspace.token.axis"
msgstr "Axis"
#: src/app/main/ui/workspace/tokens/context_menu.cljs:235
msgid "workspace.token.size"
msgstr "Size"
#: src/app/main/ui/workspace/tokens/context_menu.cljs:235
msgid "workspace.token.min-size"
msgstr "Min. size"
#: src/app/main/ui/workspace/tokens/context_menu.cljs:235
msgid "workspace.token.max-size"
msgstr "Max. size"
#: src/app/main/ui/workspace/tokens/context_menu.cljs:235
msgid "workspace.token.gaps"
msgstr "Gaps"
#: src/app/main/ui/workspace/tokens/context_menu.cljs:235
msgid "workspace.token.paddings"
msgstr "Paddings"
#: src/app/main/ui/workspace/tokens/context_menu.cljs:235
msgid "workspace.token.margins"
msgstr "Margins"
#: src/app/main/ui/workspace/tokens/context_menu.cljs:235
msgid "workspace.token.radius"
msgstr "Radius"
#: src/app/main/ui/workspace/tokens/context_menu.cljs:235
msgid "workspace.token.color"
msgstr "Color"
#: src/app/main/ui/workspace/tokens/context_menu.cljs:218
msgid "workspace.token.edit"
msgstr "Edit token"