mirror of
https://github.com/penpot/penpot.git
synced 2026-09-18 18:06:14 +00:00
✨ Add visibility toggle for strokes (#8913)
* ✨ Add visibility toggle for strokes * ♻️ Use single emit! call for stroke visibility toggle * 💄 Disable stroke controls when hidden, matching shadow/blur pattern When a stroke is hidden, the alignment/style selects, cap selects, and cap switch button are now disabled. A .hidden CSS class dims the options area with reduced opacity. This matches the existing behavior in shadow_row and blur menu where controls are disabled when the effect is hidden. * 💄 Move stroke hide button before remove button --------- Signed-off-by: eureka928 <meobius123@gmail.com>
This commit is contained in:
parent
9106a994f1
commit
4703fe6e3b
@ -29,6 +29,7 @@
|
|||||||
- Add per-group add button for typographies (by @eureka928) [Github #5275](https://github.com/penpot/penpot/issues/5275)
|
- Add per-group add button for typographies (by @eureka928) [Github #5275](https://github.com/penpot/penpot/issues/5275)
|
||||||
- Use page name for multi-export ZIP/PDF downloads (by @Dexterity104) [Github #8773](https://github.com/penpot/penpot/issues/8773)
|
- Use page name for multi-export ZIP/PDF downloads (by @Dexterity104) [Github #8773](https://github.com/penpot/penpot/issues/8773)
|
||||||
- Make links in comments clickable (by @eureka928) [Github #1602](https://github.com/penpot/penpot/issues/1602)
|
- Make links in comments clickable (by @eureka928) [Github #1602](https://github.com/penpot/penpot/issues/1602)
|
||||||
|
- Add visibility toggle for strokes (by @eureka928) [Github #7438](https://github.com/penpot/penpot/issues/7438)
|
||||||
|
|
||||||
### :bug: Bugs fixed
|
### :bug: Bugs fixed
|
||||||
|
|
||||||
|
|||||||
@ -145,7 +145,8 @@
|
|||||||
[::sm/one-of stroke-caps]]
|
[::sm/one-of stroke-caps]]
|
||||||
[:stroke-color {:optional true} clr/schema:hex-color]
|
[:stroke-color {:optional true} clr/schema:hex-color]
|
||||||
[:stroke-color-gradient {:optional true} clr/schema:gradient]
|
[:stroke-color-gradient {:optional true} clr/schema:gradient]
|
||||||
[:stroke-image {:optional true} clr/schema:image]])
|
[:stroke-image {:optional true} clr/schema:image]
|
||||||
|
[:hidden {:optional true} :boolean]])
|
||||||
|
|
||||||
(def stroke-attrs
|
(def stroke-attrs
|
||||||
"A set of attrs that corresponds to stroke data type"
|
"A set of attrs that corresponds to stroke data type"
|
||||||
|
|||||||
@ -509,7 +509,8 @@
|
|||||||
|
|
||||||
(when (some? shape-strokes)
|
(when (some? shape-strokes)
|
||||||
[:> :g props
|
[:> :g props
|
||||||
(for [[index value] (reverse (d/enumerate shape-strokes))]
|
(for [[index value] (reverse (d/enumerate shape-strokes))
|
||||||
|
:when (not (:hidden value))]
|
||||||
[:& shape-custom-stroke {:shape shape
|
[:& shape-custom-stroke {:shape shape
|
||||||
:stroke value
|
:stroke value
|
||||||
:index index
|
:index index
|
||||||
|
|||||||
@ -12,6 +12,7 @@
|
|||||||
[app.common.types.stroke :as cts]
|
[app.common.types.stroke :as cts]
|
||||||
[app.main.data.workspace :as udw]
|
[app.main.data.workspace :as udw]
|
||||||
[app.main.data.workspace.colors :as dc]
|
[app.main.data.workspace.colors :as dc]
|
||||||
|
[app.main.data.workspace.shapes :as dwsh]
|
||||||
[app.main.data.workspace.tokens.application :as dwta]
|
[app.main.data.workspace.tokens.application :as dwta]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.ui.components.title-bar :refer [title-bar*]]
|
[app.main.ui.components.title-bar :refer [title-bar*]]
|
||||||
@ -155,6 +156,13 @@
|
|||||||
(st/emit! (udw/trigger-bounding-box-cloaking ids))
|
(st/emit! (udw/trigger-bounding-box-cloaking ids))
|
||||||
(st/emit! (dc/change-stroke-attrs ids {:stroke-cap-start stroke-cap-end
|
(st/emit! (dc/change-stroke-attrs ids {:stroke-cap-start stroke-cap-end
|
||||||
:stroke-cap-end stroke-cap-start} index)))))
|
:stroke-cap-end stroke-cap-start} index)))))
|
||||||
|
on-toggle-visibility
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps ids)
|
||||||
|
(fn [index]
|
||||||
|
(st/emit! (udw/trigger-bounding-box-cloaking ids)
|
||||||
|
(dwsh/update-shapes ids #(update-in % [:strokes index :hidden] not)))))
|
||||||
|
|
||||||
on-add-stroke
|
on-add-stroke
|
||||||
(fn [_]
|
(fn [_]
|
||||||
(st/emit! (udw/trigger-bounding-box-cloaking ids))
|
(st/emit! (udw/trigger-bounding-box-cloaking ids))
|
||||||
@ -226,6 +234,7 @@
|
|||||||
:applied-tokens (when (= 0 index) applied-tokens)
|
:applied-tokens (when (= 0 index) applied-tokens)
|
||||||
:on-detach-token on-detach-token
|
:on-detach-token on-detach-token
|
||||||
:on-remove on-remove
|
:on-remove on-remove
|
||||||
|
:on-toggle-visibility on-toggle-visibility
|
||||||
:on-reorder handle-reorder
|
:on-reorder handle-reorder
|
||||||
:disable-drag disable-drag
|
:disable-drag disable-drag
|
||||||
:on-focus on-focus
|
:on-focus on-focus
|
||||||
|
|||||||
@ -40,6 +40,7 @@
|
|||||||
on-stroke-cap-start-change
|
on-stroke-cap-start-change
|
||||||
on-stroke-cap-end-change
|
on-stroke-cap-end-change
|
||||||
on-stroke-cap-switch
|
on-stroke-cap-switch
|
||||||
|
on-toggle-visibility
|
||||||
disable-drag
|
disable-drag
|
||||||
on-focus
|
on-focus
|
||||||
on-blur
|
on-blur
|
||||||
@ -49,7 +50,9 @@
|
|||||||
select-on-focus
|
select-on-focus
|
||||||
ids]}]
|
ids]}]
|
||||||
|
|
||||||
(let [token-numeric-inputs
|
(let [hidden? (:hidden stroke)
|
||||||
|
|
||||||
|
token-numeric-inputs
|
||||||
(features/use-feature "tokens/numeric-input")
|
(features/use-feature "tokens/numeric-input")
|
||||||
|
|
||||||
on-drop
|
on-drop
|
||||||
@ -182,10 +185,18 @@
|
|||||||
on-cap-switch
|
on-cap-switch
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(mf/deps index on-stroke-cap-switch)
|
(mf/deps index on-stroke-cap-switch)
|
||||||
#(on-stroke-cap-switch index))]
|
#(on-stroke-cap-switch index))
|
||||||
|
|
||||||
|
on-toggle-visibility
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps index on-toggle-visibility)
|
||||||
|
(fn []
|
||||||
|
(when on-toggle-visibility
|
||||||
|
(on-toggle-visibility index))))]
|
||||||
|
|
||||||
[:div {:class (stl/css-case
|
[:div {:class (stl/css-case
|
||||||
:stroke-data true
|
:stroke-data true
|
||||||
|
:hidden hidden?
|
||||||
:dnd-over-top (= (:over dprops) :top)
|
:dnd-over-top (= (:over dprops) :top)
|
||||||
:dnd-over-bot (= (:over dprops) :bot))
|
:dnd-over-bot (= (:over dprops) :bot))
|
||||||
:aria-label (str "stroke-row-" index)}
|
:aria-label (str "stroke-row-" index)}
|
||||||
@ -195,22 +206,33 @@
|
|||||||
|
|
||||||
;; Stroke Color
|
;; Stroke Color
|
||||||
;; FIXME: memorize stroke color
|
;; FIXME: memorize stroke color
|
||||||
[:> color-row* {:color (ctc/stroke->color stroke)
|
[:div {:class (stl/css :stroke-color-actions)}
|
||||||
:index index
|
[:> color-row* {:color (ctc/stroke->color stroke)
|
||||||
:title title
|
:index index
|
||||||
:on-change on-color-change-refactor
|
:title title
|
||||||
:on-detach on-color-detach
|
:on-change on-color-change-refactor
|
||||||
:on-remove on-remove
|
:on-detach on-color-detach
|
||||||
:disable-drag disable-drag
|
:disable-drag disable-drag
|
||||||
:applied-token (if (= index 0)
|
:applied-token (if (= index 0)
|
||||||
stroke-color-token
|
stroke-color-token
|
||||||
nil)
|
nil)
|
||||||
:on-detach-token on-detach-token-color
|
:on-detach-token on-detach-token-color
|
||||||
:on-token-change on-token-change
|
:on-token-change on-token-change
|
||||||
:on-focus on-focus
|
:on-focus on-focus
|
||||||
:origin :stroke-color
|
:origin :stroke-color
|
||||||
:select-on-focus select-on-focus
|
:select-on-focus select-on-focus
|
||||||
:on-blur on-blur}]
|
:on-blur on-blur}]
|
||||||
|
|
||||||
|
(when (some? on-toggle-visibility)
|
||||||
|
[:> icon-button* {:variant "ghost"
|
||||||
|
:aria-label (tr "workspace.options.stroke.toggle-stroke")
|
||||||
|
:on-click on-toggle-visibility
|
||||||
|
:icon (if hidden? "hide" "shown")}])
|
||||||
|
|
||||||
|
[:> icon-button* {:variant "ghost"
|
||||||
|
:aria-label (tr "workspace.options.stroke.remove-stroke")
|
||||||
|
:on-click on-remove
|
||||||
|
:icon i/remove}]]
|
||||||
|
|
||||||
;; Stroke Width, Alignment & Style
|
;; Stroke Width, Alignment & Style
|
||||||
(if token-numeric-inputs
|
(if token-numeric-inputs
|
||||||
@ -230,6 +252,7 @@
|
|||||||
:options stroke-alignment-options
|
:options stroke-alignment-options
|
||||||
:variant "icon-only"
|
:variant "icon-only"
|
||||||
:data-testid "stroke.alignment"
|
:data-testid "stroke.alignment"
|
||||||
|
:disabled hidden?
|
||||||
:wrapper-class (stl/css :stroke-align-icon-select)
|
:wrapper-class (stl/css :stroke-align-icon-select)
|
||||||
:on-change on-alignment-change}]
|
:on-change on-alignment-change}]
|
||||||
|
|
||||||
@ -239,6 +262,7 @@
|
|||||||
:wrapper-class (stl/css :stroke-style-icon-select)
|
:wrapper-class (stl/css :stroke-style-icon-select)
|
||||||
:data-testid "stroke.style"
|
:data-testid "stroke.style"
|
||||||
:variant "icon-only"
|
:variant "icon-only"
|
||||||
|
:disabled hidden?
|
||||||
:dropdown-alignment :right
|
:dropdown-alignment :right
|
||||||
:on-change on-style-change}])]
|
:on-change on-style-change}])]
|
||||||
|
|
||||||
@ -258,6 +282,7 @@
|
|||||||
:data-testid "stroke.alignment"}
|
:data-testid "stroke.alignment"}
|
||||||
[:& select {:default-value stroke-alignment
|
[:& select {:default-value stroke-alignment
|
||||||
:options stroke-alignment-options
|
:options stroke-alignment-options
|
||||||
|
:disabled hidden?
|
||||||
:on-change on-alignment-change}]]
|
:on-change on-alignment-change}]]
|
||||||
|
|
||||||
(when-not disable-stroke-style
|
(when-not disable-stroke-style
|
||||||
@ -265,6 +290,7 @@
|
|||||||
:data-testid "stroke.style"}
|
:data-testid "stroke.style"}
|
||||||
[:& select {:default-value stroke-style
|
[:& select {:default-value stroke-style
|
||||||
:options stroke-style-options
|
:options stroke-style-options
|
||||||
|
:disabled hidden?
|
||||||
:on-change on-style-change}]])])
|
:on-change on-style-change}]])])
|
||||||
|
|
||||||
;; Stroke Caps
|
;; Stroke Caps
|
||||||
@ -272,11 +298,14 @@
|
|||||||
[:div {:class (stl/css :stroke-caps-options)}
|
[:div {:class (stl/css :stroke-caps-options)}
|
||||||
[:& select {:default-value (:stroke-cap-start stroke)
|
[:& select {:default-value (:stroke-cap-start stroke)
|
||||||
:options stroke-caps-options
|
:options stroke-caps-options
|
||||||
|
:disabled hidden?
|
||||||
:on-change on-caps-start-change}]
|
:on-change on-caps-start-change}]
|
||||||
[:> icon-button* {:variant "secondary"
|
[:> icon-button* {:variant "secondary"
|
||||||
:aria-label (tr "labels.switch")
|
:aria-label (tr "labels.switch")
|
||||||
|
:disabled hidden?
|
||||||
:on-click on-cap-switch
|
:on-click on-cap-switch
|
||||||
:icon i/switch}]
|
:icon i/switch}]
|
||||||
[:& select {:default-value (:stroke-cap-end stroke)
|
[:& select {:default-value (:stroke-cap-end stroke)
|
||||||
:options stroke-caps-options
|
:options stroke-caps-options
|
||||||
|
:disabled hidden?
|
||||||
:on-change on-caps-end-change}]])]))
|
:on-change on-caps-end-change}]])]))
|
||||||
|
|||||||
@ -27,6 +27,25 @@
|
|||||||
&.dnd-over-bot {
|
&.dnd-over-bot {
|
||||||
--reorder-bottom-display: block;
|
--reorder-bottom-display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.hidden {
|
||||||
|
.stroke-options,
|
||||||
|
.stroke-options-tokens,
|
||||||
|
.stroke-caps-options {
|
||||||
|
opacity: 0.5;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.stroke-color-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
> :first-child {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.stroke-options {
|
.stroke-options {
|
||||||
|
|||||||
@ -558,45 +558,46 @@
|
|||||||
[shape-id strokes thumbnail?]
|
[shape-id strokes thumbnail?]
|
||||||
(h/call wasm/internal-module "_clear_shape_strokes")
|
(h/call wasm/internal-module "_clear_shape_strokes")
|
||||||
(keep (fn [stroke]
|
(keep (fn [stroke]
|
||||||
(let [opacity (or (:stroke-opacity stroke) 1.0)
|
(when-not (:hidden stroke)
|
||||||
color (:stroke-color stroke)
|
(let [opacity (or (:stroke-opacity stroke) 1.0)
|
||||||
gradient (:stroke-color-gradient stroke)
|
color (:stroke-color stroke)
|
||||||
image (:stroke-image stroke)
|
gradient (:stroke-color-gradient stroke)
|
||||||
width (:stroke-width stroke)
|
image (:stroke-image stroke)
|
||||||
align (:stroke-alignment stroke)
|
width (:stroke-width stroke)
|
||||||
style (-> stroke :stroke-style sr/translate-stroke-style)
|
align (:stroke-alignment stroke)
|
||||||
cap-start (-> stroke :stroke-cap-start sr/translate-stroke-cap)
|
style (-> stroke :stroke-style sr/translate-stroke-style)
|
||||||
cap-end (-> stroke :stroke-cap-end sr/translate-stroke-cap)
|
cap-start (-> stroke :stroke-cap-start sr/translate-stroke-cap)
|
||||||
offset (mem/alloc types.fills.impl/FILL-U8-SIZE)
|
cap-end (-> stroke :stroke-cap-end sr/translate-stroke-cap)
|
||||||
heap (mem/get-heap-u8)
|
offset (mem/alloc types.fills.impl/FILL-U8-SIZE)
|
||||||
dview (js/DataView. (.-buffer heap))]
|
heap (mem/get-heap-u8)
|
||||||
(case align
|
dview (js/DataView. (.-buffer heap))]
|
||||||
:inner (h/call wasm/internal-module "_add_shape_inner_stroke" width style cap-start cap-end)
|
(case align
|
||||||
:outer (h/call wasm/internal-module "_add_shape_outer_stroke" width style cap-start cap-end)
|
:inner (h/call wasm/internal-module "_add_shape_inner_stroke" width style cap-start cap-end)
|
||||||
(h/call wasm/internal-module "_add_shape_center_stroke" width style cap-start cap-end))
|
:outer (h/call wasm/internal-module "_add_shape_outer_stroke" width style cap-start cap-end)
|
||||||
|
(h/call wasm/internal-module "_add_shape_center_stroke" width style cap-start cap-end))
|
||||||
|
|
||||||
(cond
|
(cond
|
||||||
(some? gradient)
|
(some? gradient)
|
||||||
(do
|
(do
|
||||||
(types.fills.impl/write-gradient-fill offset dview opacity gradient)
|
(types.fills.impl/write-gradient-fill offset dview opacity gradient)
|
||||||
(h/call wasm/internal-module "_add_shape_stroke_fill"))
|
(h/call wasm/internal-module "_add_shape_stroke_fill"))
|
||||||
|
|
||||||
(some? image)
|
(some? image)
|
||||||
(let [image-id (get image :id)
|
(let [image-id (get image :id)
|
||||||
buffer (uuid/get-u32 image-id)
|
buffer (uuid/get-u32 image-id)
|
||||||
cached-image? (h/call wasm/internal-module "_is_image_cached"
|
cached-image? (h/call wasm/internal-module "_is_image_cached"
|
||||||
(aget buffer 0) (aget buffer 1)
|
(aget buffer 0) (aget buffer 1)
|
||||||
(aget buffer 2) (aget buffer 3)
|
(aget buffer 2) (aget buffer 3)
|
||||||
thumbnail?)]
|
thumbnail?)]
|
||||||
(types.fills.impl/write-image-fill offset dview opacity image)
|
(types.fills.impl/write-image-fill offset dview opacity image)
|
||||||
(h/call wasm/internal-module "_add_shape_stroke_fill")
|
(h/call wasm/internal-module "_add_shape_stroke_fill")
|
||||||
(when (== cached-image? 0)
|
(when (== cached-image? 0)
|
||||||
(fetch-image shape-id image-id thumbnail?)))
|
(fetch-image shape-id image-id thumbnail?)))
|
||||||
|
|
||||||
(some? color)
|
(some? color)
|
||||||
(do
|
(do
|
||||||
(types.fills.impl/write-solid-fill offset dview opacity color)
|
(types.fills.impl/write-solid-fill offset dview opacity color)
|
||||||
(h/call wasm/internal-module "_add_shape_stroke_fill")))))
|
(h/call wasm/internal-module "_add_shape_stroke_fill"))))))
|
||||||
strokes))
|
strokes))
|
||||||
|
|
||||||
(defn set-shape-svg-attrs
|
(defn set-shape-svg-attrs
|
||||||
|
|||||||
@ -7316,6 +7316,10 @@ msgstr "Outside"
|
|||||||
msgid "workspace.options.stroke.remove-stroke"
|
msgid "workspace.options.stroke.remove-stroke"
|
||||||
msgstr "Remove stroke"
|
msgstr "Remove stroke"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs
|
||||||
|
msgid "workspace.options.stroke.toggle-stroke"
|
||||||
|
msgstr "Toggle stroke"
|
||||||
|
|
||||||
#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:137
|
#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:137
|
||||||
msgid "workspace.options.stroke.solid"
|
msgid "workspace.options.stroke.solid"
|
||||||
msgstr "Solid"
|
msgstr "Solid"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user