0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-03-11 23:31:21 -05:00

Add select layer to contest menu

This commit is contained in:
Eva 2022-01-24 09:59:58 +01:00 committed by Andrés Moya
parent 3482d6c303
commit 406c4063de
9 changed files with 204 additions and 105 deletions

View file

@ -4,10 +4,11 @@
### :sparkles: New features
- Add select layer option to context menu [Taiga #2474](https://tree.taiga.io/project/penpot/us/2474).
- Guides [Taiga #290](https://tree.taiga.io/project/penpot/us/290?milestone=307334)
- Improve file menu by adding semantically groups [Github #1203](https://github.com/penpot/penpot/issues/1203).
- Add update components in bulk option in context menu [Taiga #1975](https://tree.taiga.io/project/penpot/us/1975).
- Create e2e tests for drawing basic fors [Taiga #2608](https://tree.taiga.io/project/penpot/task/2608).
- Create e2e tests for drawing basic shapes [Taiga #2608](https://tree.taiga.io/project/penpot/task/2608).
- Create firsts e2e test [Taiga #2608](https://tree.taiga.io/project/penpot/task/2608).
## 1.11.0-beta

View file

@ -34,11 +34,11 @@
margin: 2px;
}
span:first-child {
span {
color: $color-gray-60;
}
span:last-child {
span.shortcut {
color: $color-gray-20;
font-size: $fs12;
}
@ -57,6 +57,40 @@
}
}
}
.sub-menu-item {
display: flex;
justify-content: flex-start;
&:hover {
background-color: $color-primary-lighter;
}
span.title {
margin-left: 5px;
}
.selected-icon {
svg {
width: 10px;
height: 10px;
}
}
.shape-icon {
margin-left: 3px;
svg {
width: 13px;
height: 13px;
}
}
.icon-wrapper {
display: grid;
grid-template-columns: 1fr 1fr;
margin: 0;
}
}
}
.workspace-loader {

View file

@ -150,7 +150,7 @@
ptk/WatchEvent
(watch [_ state _]
(let [objects (wsh/lookup-page-objects state)]
(let [objects (wsh/lookup-page-objects state)]
(rx/of (dwc/expand-all-parents ids objects))))))
(defn select-all
@ -256,7 +256,7 @@
;; in the later vector position
selected (->> children
reverse
(d/seek #(geom/has-point? % position)))]
(d/seek #(geom/has-point? % position)))]
(when selected
(rx/of (select-shape (:id selected))))))))
@ -312,8 +312,8 @@
result (prepare-duplicate-change objects page-id unames update-unames! ids-map id delta)
result (if (vector? result) result [result])]
(recur
(next ids)
(into chgs result)))
(next ids)
(into chgs result)))
chgs))))
(defn duplicate-changes-update-indices
@ -384,12 +384,12 @@
(mapcat #(prepare-duplicate-shape-change objects page-id unames update-unames! ids-map % delta new-id new-id)))
new-frame (-> obj
(assoc :id new-id
:name frame-name
:frame-id uuid/zero
:shapes [])
(geom/move delta)
(d/update-when :interactions #(cti/remap-interactions % ids-map objects)))
(assoc :id new-id
:name frame-name
:frame-id uuid/zero
:shapes [])
(geom/move delta)
(d/update-when :interactions #(cti/remap-interactions % ids-map objects)))
fch {:type :add-obj
:old-id (:id obj)

View file

@ -152,6 +152,12 @@
(def current-hover
(l/derived :hover workspace-local))
(def context-menu
(l/derived :context-menu workspace-local))
(def current-hover-ids
(l/derived :hover-ids context-menu))
(def editors
(l/derived :editors workspace-local))

View file

@ -13,6 +13,7 @@
[app.main.data.workspace :as dw]
[app.main.data.workspace.interactions :as dwi]
[app.main.data.workspace.libraries :as dwl]
[app.main.data.workspace.selection :as dws]
[app.main.data.workspace.shortcuts :as sc]
[app.main.refs :as refs]
[app.main.store :as st]
@ -33,8 +34,33 @@
(dom/prevent-default event)
(dom/stop-propagation event))
(mf/defc element-icon
[{:keys [shape] :as props}]
(case (:type shape)
:frame i/artboard
:image i/image
:line i/line
:circle i/circle
:path i/curve
:rect i/box
:text i/text
:group (if (some? (:component-id shape))
i/component
(if (:masked-group? shape)
i/mask
i/folder))
:bool (case (:bool-type shape)
:difference i/bool-difference
:exclude i/bool-exclude
:intersection i/bool-intersection
#_:default i/bool-union)
:svg-raw i/file-svg
nil))
(mf/defc menu-entry
[{:keys [title shortcut on-click children] :as props}]
[{:keys [title shortcut on-click children selected has-icon? shape] :as props}]
(let [submenu-ref (mf/use-ref nil)
hovering? (mf/use-ref false)
@ -64,22 +90,33 @@
(when (and (some? dom) (some? submenu-node))
(dom/set-css-property! submenu-node "top" (str (.-offsetTop dom) "px"))))))]
[:li {:ref set-dom-node
:on-click on-click
:on-pointer-enter on-pointer-enter
:on-pointer-leave on-pointer-leave}
[:span.title title]
[:span.shortcut (or shortcut "")]
(if has-icon?
[:li.sub-menu-item {:ref set-dom-node
:on-click on-click
:on-pointer-enter on-pointer-enter
:on-pointer-leave on-pointer-leave}
(when has-icon?
[:span.icon-wrapper
(if selected [:span.selected-icon i/tick]
[:span.selected-icon])
[:span.shape-icon (element-icon {:shape shape})]])
[:span.title title]]
[:li {:ref set-dom-node
:on-click on-click
:on-pointer-enter on-pointer-enter
:on-pointer-leave on-pointer-leave}
[:span.title title]
[:span.shortcut (or shortcut "")]
(when (> (count children) 1)
[:span.submenu-icon i/arrow-slide])
(when (> (count children) 1)
[:span.submenu-icon i/arrow-slide])
(when (> (count children) 1)
[:ul.workspace-context-menu
{:ref submenu-ref
:style {:display "none" :left 250}
:on-context-menu prevent-default}
children])]))
(when (> (count children) 1)
[:ul.workspace-context-menu
{:ref submenu-ref
:style {:display "none" :left 250}
:on-context-menu prevent-default}
children])])))
(mf/defc menu-separator
[]
@ -108,12 +145,21 @@
[:& menu-separator]]))
(mf/defc context-menu-layer-position
[]
[{:keys [hover-objs shapes]}]
(let [do-bring-forward (st/emitf (dw/vertical-order-selected :up))
do-bring-to-front (st/emitf (dw/vertical-order-selected :top))
do-send-backward (st/emitf (dw/vertical-order-selected :down))
do-send-to-back (st/emitf (dw/vertical-order-selected :bottom))]
do-send-to-back (st/emitf (dw/vertical-order-selected :bottom))
select-shapes (fn [id] (st/emitf (dws/select-shape id)))]
[:*
(when (> (count hover-objs) 1)
[:& menu-entry {:title (tr "workspace.shape.menu.select-layer")}
(for [object hover-objs]
[:& menu-entry {:title (:name object)
:selected (some #(= object %) shapes)
:on-click (select-shapes (:id object))
:has-icon? true
:shape object}])])
[:& menu-entry {:title (tr "workspace.shape.menu.forward")
:shortcut (sc/get-tooltip :bring-forward)
:on-click do-bring-forward}]
@ -392,8 +438,11 @@
[{:keys [mdata] :as props}]
(let [{:keys [disable-booleans? disable-flatten?]} mdata
shapes (mf/deref refs/selected-objects)
hover-ids (mf/deref refs/current-hover-ids)
hover-objs (mf/deref (refs/objects-by-id hover-ids))
props #js {:shapes shapes
:hover-objs hover-objs
:disable-booleans? disable-booleans?
:disable-flatten? disable-flatten?}]
(when-not (empty? shapes)

View file

@ -115,7 +115,7 @@
text-editing? (and edition (= :text (get-in base-objects [edition :type])))
on-click (actions/on-click hover selected edition drawing-path? drawing-tool)
on-context-menu (actions/on-context-menu hover)
on-context-menu (actions/on-context-menu hover hover-ids)
on-double-click (actions/on-double-click hover hover-ids drawing-path? base-objects edition)
on-drag-enter (actions/on-drag-enter)
on-drag-over (actions/on-drag-over)

View file

@ -186,9 +186,9 @@
(dw/start-editing-selected))))))))
(defn on-context-menu
[hover]
[hover hover-ids]
(mf/use-callback
(mf/deps @hover)
(mf/deps @hover @hover-ids)
(fn [event]
(when (or (dom/class? (dom/get-target event) "viewport-controls")
(dom/class? (dom/get-target event) "viewport-selrect"))
@ -200,18 +200,19 @@
#(st/emit!
(if (some? @hover)
(dw/show-shape-context-menu {:position position
:shape @hover})
:shape @hover
:hover-ids @hover-ids})
(dw/show-context-menu {:position position})))))))))
(defn on-menu-selected
[hover hover-ids selected]
(mf/use-callback
(mf/deps @hover hover-ids selected)
(mf/deps @hover @hover-ids selected)
(fn [event]
(dom/prevent-default event)
(dom/stop-propagation event)
(let [position (dom/get-client-position event)]
(st/emit! (dw/show-shape-context-menu {:position position}))))))
(st/emit! (dw/show-shape-context-menu {:position position :hover-ids @hover-ids}))))))
(defn on-mouse-up
[disable-paste]
@ -251,58 +252,58 @@
(reset! in-viewport? false))))
(defn on-pointer-down []
(mf/use-callback
(fn [event]
(mf/use-callback
(fn [event]
;; We need to handle editor related stuff here because
;; handling on editor dom node does not works properly.
(let [target (dom/get-target event)
editor (.closest ^js target ".public-DraftEditor-content")]
(let [target (dom/get-target event)
editor (.closest ^js target ".public-DraftEditor-content")]
;; Capture mouse pointer to detect the movements even if cursor
;; leaves the viewport or the browser itself
;; https://developer.mozilla.org/en-US/docs/Web/API/Element/setPointerCapture
(if editor
(.setPointerCapture editor (.-pointerId event))
(.setPointerCapture target (.-pointerId event)))))))
(if editor
(.setPointerCapture editor (.-pointerId event))
(.setPointerCapture target (.-pointerId event)))))))
(defn on-pointer-up []
(mf/use-callback
(fn [event]
(let [target (dom/get-target event)]
(mf/use-callback
(fn [event]
(let [target (dom/get-target event)]
; Release pointer on mouse up
(.releasePointerCapture target (.-pointerId event))))))
(.releasePointerCapture target (.-pointerId event))))))
(defn on-key-down []
(mf/use-callback
(fn [event]
(let [bevent (.getBrowserEvent ^js event)
key (.-key ^js event)
ctrl? (kbd/ctrl? event)
shift? (kbd/shift? event)
alt? (kbd/alt? event)
meta? (kbd/meta? event)
target (dom/get-target event)
editing? (or (some? (.closest ^js target ".public-DraftEditor-content"))
(= "rich-text" (obj/get target "className"))
(= "INPUT" (obj/get target "tagName"))
(= "TEXTAREA" (obj/get target "tagName")))]
(mf/use-callback
(fn [event]
(let [bevent (.getBrowserEvent ^js event)
key (.-key ^js event)
ctrl? (kbd/ctrl? event)
shift? (kbd/shift? event)
alt? (kbd/alt? event)
meta? (kbd/meta? event)
target (dom/get-target event)
editing? (or (some? (.closest ^js target ".public-DraftEditor-content"))
(= "rich-text" (obj/get target "className"))
(= "INPUT" (obj/get target "tagName"))
(= "TEXTAREA" (obj/get target "tagName")))]
(when-not (.-repeat bevent)
(st/emit! (ms/->KeyboardEvent :down key shift? ctrl? alt? meta? editing?)))))))
(when-not (.-repeat bevent)
(st/emit! (ms/->KeyboardEvent :down key shift? ctrl? alt? meta? editing?)))))))
(defn on-key-up []
(mf/use-callback
(fn [event]
(let [key (.-key event)
ctrl? (kbd/ctrl? event)
shift? (kbd/shift? event)
alt? (kbd/alt? event)
meta? (kbd/meta? event)
target (dom/get-target event)
editing? (or (some? (.closest ^js target ".public-DraftEditor-content"))
(= "rich-text" (obj/get target "className"))
(= "INPUT" (obj/get target "tagName"))
(= "TEXTAREA" (obj/get target "tagName")))]
(st/emit! (ms/->KeyboardEvent :up key shift? ctrl? alt? meta? editing?))))))
(mf/use-callback
(fn [event]
(let [key (.-key event)
ctrl? (kbd/ctrl? event)
shift? (kbd/shift? event)
alt? (kbd/alt? event)
meta? (kbd/meta? event)
target (dom/get-target event)
editing? (or (some? (.closest ^js target ".public-DraftEditor-content"))
(= "rich-text" (obj/get target "className"))
(= "INPUT" (obj/get target "tagName"))
(= "TEXTAREA" (obj/get target "tagName")))]
(st/emit! (ms/->KeyboardEvent :up key shift? ctrl? alt? meta? editing?))))))
(defn on-mouse-move [viewport-ref zoom]
(let [last-position (mf/use-var nil)]
@ -386,29 +387,29 @@
:y #(+ % delta-y)})))))))))
(defn on-drag-enter []
(mf/use-callback
(fn [e]
(when (or (dnd/has-type? e "penpot/shape")
(dnd/has-type? e "penpot/component")
(dnd/has-type? e "Files")
(dnd/has-type? e "text/uri-list")
(dnd/has-type? e "text/asset-id"))
(dom/prevent-default e)))))
(mf/use-callback
(fn [e]
(when (or (dnd/has-type? e "penpot/shape")
(dnd/has-type? e "penpot/component")
(dnd/has-type? e "Files")
(dnd/has-type? e "text/uri-list")
(dnd/has-type? e "text/asset-id"))
(dom/prevent-default e)))))
(defn on-drag-over []
(mf/use-callback
(fn [e]
(when (or (dnd/has-type? e "penpot/shape")
(dnd/has-type? e "penpot/component")
(dnd/has-type? e "Files")
(dnd/has-type? e "text/uri-list")
(dnd/has-type? e "text/asset-id"))
(dom/prevent-default e)))))
(mf/use-callback
(fn [e]
(when (or (dnd/has-type? e "penpot/shape")
(dnd/has-type? e "penpot/component")
(dnd/has-type? e "Files")
(dnd/has-type? e "text/uri-list")
(dnd/has-type? e "text/asset-id"))
(dom/prevent-default e)))))
(defn on-image-uploaded []
(mf/use-callback
(fn [image position]
(st/emit! (dw/image-uploaded image position)))))
(mf/use-callback
(fn [image position]
(st/emit! (dw/image-uploaded image position)))))
(defn on-drop [file viewport-ref zoom]
(let [on-image-uploaded (on-image-uploaded)]
@ -483,19 +484,19 @@
(st/emit! (dw/upload-media-workspace params)))))))))
(defn on-paste [disable-paste in-viewport?]
(mf/use-callback
(fn [event]
(mf/use-callback
(fn [event]
;; We disable the paste just after mouse-up of a middle button so when panning won't
;; paste the content into the workspace
(let [tag-name (-> event dom/get-target dom/get-tag-name)]
(when (and (not (#{"INPUT" "TEXTAREA"} tag-name)) (not @disable-paste))
(st/emit! (dw/paste-from-event event @in-viewport?)))))))
(let [tag-name (-> event dom/get-target dom/get-tag-name)]
(when (and (not (#{"INPUT" "TEXTAREA"} tag-name)) (not @disable-paste))
(st/emit! (dw/paste-from-event event @in-viewport?)))))))
(defn on-resize [viewport-ref]
(mf/use-callback
(fn [_]
(let [node (mf/ref-val viewport-ref)
prnt (dom/get-parent node)
size (dom/get-client-size prnt)]
(mf/use-callback
(fn [_]
(let [node (mf/ref-val viewport-ref)
prnt (dom/get-parent node)
size (dom/get-client-size prnt)]
;; We schedule the event so it fires after `initialize-page` event
(timers/schedule #(st/emit! (dw/update-viewport-size size)))))))
(timers/schedule #(st/emit! (dw/update-viewport-size size)))))))

View file

@ -3162,6 +3162,10 @@ msgstr "Flip vertical"
msgid "workspace.shape.menu.flow-start"
msgstr "Flow start"
#: src/app/main/ui/workspace/context_menu.cljs
msgid "workspace.shape.menu.select-layer"
msgstr "Select layer"
#: src/app/main/ui/workspace/context_menu.cljs
msgid "workspace.shape.menu.forward"
msgstr "Bring forward"

View file

@ -3175,6 +3175,10 @@ msgstr "Voltear vertical"
msgid "workspace.shape.menu.flow-start"
msgstr "Inicio de flujo"
#: src/app/main/ui/workspace/context_menu.cljs
msgid "workspace.shape.menu.select-layer"
msgstr "Seleccionar capa"
#: src/app/main/ui/workspace/context_menu.cljs
msgid "workspace.shape.menu.forward"
msgstr "Mover hacia delante"