Add color customization for ruler guides (#8986)

*  Add customizable colors for ruler guides

*  Update CHANGES.md

* 💄 Move guide color menu styles to SCSS

* 💄 Fix trailing whitespace in guides.cljs

---------

Signed-off-by: Dexterity <173429049+Dexterity104@users.noreply.github.com>
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
This commit is contained in:
Dexterity 2026-04-21 11:31:39 -04:00 committed by GitHub
parent cd320c0cd6
commit e1d3106f61
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 148 additions and 5 deletions

View File

@ -38,6 +38,7 @@
- Add guide locking and fix locked elements not selectable in viewer (by @Dexterity104) [Github #8358](https://github.com/penpot/penpot/issues/8358)
- Apply styles to selection (by @AzazelN28) [Taiga #13647](https://tree.taiga.io/project/penpot/task/13647)
- Reorder prototyping overlay options to show Position before Relative to (by @rockchris099) [Github #2910](https://github.com/penpot/penpot/issues/2910)
- Add customizable colors for ruler guides (by @Dexterity104) [Github #5199](https://github.com/penpot/penpot/issues/5199)
- Persist asset search query and section filter when switching sidebar tabs (by @eureka0928) [Github #2913](https://github.com/penpot/penpot/issues/2913)
- Add delete and duplicate buttons to typography dialog (by @eureka0928) [Github #5270](https://github.com/penpot/penpot/issues/5270)
- Edit ruler guide position by double-clicking the guide pill (by @eureka0928) [Github #2311](https://github.com/penpot/penpot/issues/2311)

View File

@ -34,7 +34,8 @@
[:id ::sm/uuid]
[:axis [::sm/one-of #{:x :y}]]
[:position ::sm/safe-number]
[:frame-id {:optional true} [:maybe ::sm/uuid]]])
[:frame-id {:optional true} [:maybe ::sm/uuid]]
[:color {:optional true} [:maybe ctc/schema:hex-color]]])
(def schema:guides
[:map-of {:gen/max 2} ::sm/uuid schema:guide])

View File

@ -1195,6 +1195,16 @@
(-> params (assoc :kind :grid-cells
:grid grid
:cells cells))))))))
(defn show-guide-context-menu
[{:keys [position guide] :as params}]
(dm/assert! (gpt/point? position))
(ptk/reify ::show-guide-context-menu
ptk/WatchEvent
(watch [_ _ _]
(rx/of (show-context-menu
(-> params (assoc :kind :guide
:guide guide)))))))
(def hide-context-menu
(ptk/reify ::hide-context-menu
ptk/UpdateEvent

View File

@ -152,6 +152,23 @@
(map build-move-event)
(rx/from))))))
(defn update-guide-color
[guide-id color]
(ptk/reify ::update-guide-color
ptk/WatchEvent
(watch [it state _]
(let [{:keys [guides] :as page} (dsh/lookup-page state)
guide (get guides guide-id)]
(when (some? guide)
(let [updated-guide (if (some? color)
(assoc guide :color color)
(dissoc guide :color))
changes
(-> (pcb/empty-changes it)
(pcb/with-page page)
(pcb/set-guide guide-id updated-guide))]
(rx/of (dwc/commit-changes changes))))))))
(defn set-hover-guide
[id hover?]
(ptk/reify ::set-hover-guide

View File

@ -899,6 +899,50 @@
:disabled (not has-copied-tracks?)}]]))
(def guide-color-presets
["#ff3277" "#4dabf7" "#51cf66" "#fcc419" "#ff922b" "#cc5de8" "#ffffff" "#868e96"])
(mf/defc guide-color-context-menu*
{::mf/props :obj
::mf/private true}
[{:keys [mdata]}]
(let [{:keys [guide]} mdata
guide-id (:id guide)
current-color (or (:color guide) (first guide-color-presets))
do-set-color
(mf/use-fn
(mf/deps guide-id)
(fn [event]
(let [color (dom/get-data (dom/get-current-target event) "color")]
(st/emit! dw/hide-context-menu
(dwg/update-guide-color guide-id color)))))
do-remove-guide
(mf/use-fn
(mf/deps guide)
(fn []
(st/emit! dw/hide-context-menu
(dwg/remove-guide guide))))]
[:*
[:li {:class (stl/css :context-menu-item :guide-color-label)}
[:span {:class (stl/css :title)}
(tr "workspace.context-menu.guides.change-color")]]
[:li {:class (stl/css :guide-color-swatches)}
(for [color guide-color-presets]
[:span {:key color
:class (stl/css-case
:guide-color-swatch true
:selected (= color current-color))
:data-color color
:on-click do-set-color
:title color
:style {:background-color color}}])]
[:> menu-separator* {}]
[:> menu-entry* {:title (tr "workspace.context-menu.guides.remove")
:on-click do-remove-guide}]]))
;; FIXME: optimize because it is rendered always
(mf/defc context-menu*
@ -936,4 +980,5 @@
:page [:> page-item-context-menu* {:mdata mdata}]
:grid-track [:> grid-track-context-menu* {:mdata mdata}]
:grid-cells [:> grid-cells-context-menu* {:mdata mdata}]
:guide [:> guide-color-context-menu* {:mdata mdata}]
[:> viewport-context-menu* {:mdata mdata}]))]]]))

View File

@ -138,3 +138,30 @@
pointer-events: none;
opacity: 0.6;
}
.guide-color-label {
cursor: default;
pointer-events: none;
}
.guide-color-swatches {
display: flex;
flex-wrap: wrap;
gap: deprecated.$s-6;
padding: deprecated.$s-4 deprecated.$s-6 deprecated.$s-8;
list-style: none;
}
.guide-color-swatch {
width: deprecated.$s-20;
height: deprecated.$s-20;
border-radius: 50%;
cursor: pointer;
flex-shrink: 0;
box-sizing: border-box;
border: deprecated.$s-2 solid var(--panel-border-color);
&.selected {
border: deprecated.$s-2 solid var(--menu-foreground-color);
}
}

View File

@ -31,7 +31,8 @@
(def ^:const guide-width 1)
(def ^:const guide-opacity 0.7)
(def ^:const guide-opacity-hover 1)
(def ^:const guide-color colors/new-danger)
(def ^:const default-guide-color colors/new-danger)
(def ^:const guide-pill-width 34)
(def ^:const guide-pill-height 20)
(def ^:const guide-pill-corner-radius 4)
@ -285,10 +286,14 @@
(mf/defc guide*
{::mf/wrap [mf/memo]}
[{:keys [guide is-hover on-guide-change get-hover-frame vbox zoom
hover-frame disabled-guides frame-modifier frame-transform]}]
hover-frame disabled-guides frame-modifier frame-transform
on-guide-context-menu]}]
(let [axis
(get guide :axis)
guide-color
(or (:color guide) default-guide-color)
read-only?
(mf/use-ctx ctx/workspace-read-only?)
@ -303,7 +308,7 @@
handle-change-position
(mf/use-fn
(mf/deps on-guide-change)
(mf/deps on-guide-change guide)
(fn [changes]
(when on-guide-change
(on-guide-change (merge guide changes)))))
@ -399,7 +404,13 @@
(not (ctst/rotated-frame? frame))))
[:g.guide-area {:opacity (when frame-guide-outside? 0)}
(when-not disabled-guides
(let [{:keys [x y width height]} (guide-area-axis pos vbox zoom frame axis)]
(let [{:keys [x y width height]} (guide-area-axis pos vbox zoom frame axis)
on-context-menu
(fn [event]
(dom/prevent-default event)
(dom/stop-propagation event)
(when on-guide-context-menu
(on-guide-context-menu event guide)))]
[:rect {:x x
:y y
:width width
@ -413,6 +424,7 @@
:on-pointer-up on-pointer-up
:on-lost-pointer-capture on-lost-pointer-capture
:on-pointer-move on-pointer-move
:on-context-menu on-context-menu
:on-double-click on-double-click}]))
(if (some? frame)
@ -597,6 +609,13 @@
(st/emit! (dw/update-guides guide))
(st/emit! (dw/remove-guide guide)))))
on-guide-context-menu
(mf/use-fn
(fn [event guide]
(let [position (dom/get-client-position event)]
(st/emit! (dw/show-guide-context-menu {:position position
:guide guide})))))
frame-modifiers
(-> (group-by :id modifiers)
(update-vals (comp :transform first)))]
@ -628,4 +647,5 @@
:frame-transform (get frame-modifiers frame-id)
:get-hover-frame get-hover-frame
:on-guide-change on-guide-change
:on-guide-context-menu on-guide-context-menu
:disabled-guides disabled-guides}]))]))

View File

@ -94,6 +94,22 @@
value)]
(st/emit! (dwgu/update-guides (assoc guide :position position))))))}
:color
{:this true
:get
(fn [self]
(-> self u/proxy->ruler-guide :color))
:set
(fn [self value]
(cond
(not (r/check-permission plugin-id "content:write"))
(u/not-valid plugin-id :color "Plugin doesn't have 'content:write' permission")
:else
(let [guide (u/proxy->ruler-guide self)]
(st/emit! (dwgu/update-guides (assoc guide :color value))))))}
:remove
(fn []
(let [guide (u/locate-ruler-guide file-id page-id id)]

View File

@ -5730,6 +5730,12 @@ msgstr "Copy columns"
msgid "workspace.context-menu.grid-cells.paste-tracks"
msgstr "Paste"
msgid "workspace.context-menu.guides.change-color"
msgstr "Guide color"
msgid "workspace.context-menu.guides.remove"
msgstr "Remove guide"
#: src/app/main/ui/workspace/context_menu.cljs:754
msgid "workspace.context-menu.grid-track.column.add-after"
msgstr "Add 1 column to the right"