0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-04-06 12:01:19 -05:00

🎉 Add right-click context menu on workspace.

This commit is contained in:
Andrey Antukh 2020-03-19 17:40:29 +01:00 committed by Alonso Torres
parent 1434cb62f5
commit cae5b5e778
9 changed files with 197 additions and 54 deletions

View file

@ -1922,6 +1922,51 @@
query-params {:page-id (first page-ids)}]
(rx/of (rt/nav :workspace path-params query-params))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Context Menu
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(s/def ::point gpt/point?)
(defn show-context-menu
[{:keys [position] :as params}]
(us/verify ::point position)
(ptk/reify ::show-context-menu
ptk/UpdateEvent
(update [_ state]
(assoc-in state [:workspace-local :context-menu] {:position position}))))
(defn show-shape-context-menu
[{:keys [position shape] :as params}]
(us/verify ::point position)
(us/verify ::cp/minimal-shape shape)
(ptk/reify ::show-context-menu
ptk/UpdateEvent
(update [_ state]
(let [selected (get-in state [:workspace-local :selected])
selected (cond
(empty? selected)
(conj selected (:id shape))
(contains? selected (:id shape))
selected
:else
#{(:id shape)})
mdata {:position position
:selected selected
:shape shape}]
(-> state
(assoc-in [:workspace-local :context-menu] mdata)
(assoc-in [:workspace-local :selected] selected))))))
(def hide-context-menu
(ptk/reify ::hide-context-menu
ptk/UpdateEvent
(update [_ state]
(assoc-in state [:workspace-local :context-menu] nil))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Page Changes Reactions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View file

@ -23,9 +23,11 @@
[{:keys [shape] :as props}]
(let [selected (mf/deref refs/selected-shapes)
selected? (contains? selected (:id shape))
on-mouse-down #(common/on-mouse-down % shape selected)]
on-mouse-down #(common/on-mouse-down % shape)
on-context-menu #(common/on-context-menu % shape)]
[:g.shape {:class (when selected? "selected")
:on-mouse-down on-mouse-down}
:on-mouse-down on-mouse-down
:on-context-menu on-context-menu}
[:& circle-shape {:shape shape}]]))
;; --- Circle Shape

View file

@ -68,33 +68,46 @@
([event {:keys [id type] :as shape} kk-tmp]
(let [selected @refs/selected-shapes
selected? (contains? selected id)
drawing? @refs/selected-drawing-tool]
(when-not (:blocked shape)
(cond
drawing?
nil
drawing? @refs/selected-drawing-tool
button (.-which (.-nativeEvent event))]
(when-not (:blocked shape)
(cond
(not= 1 button)
nil
(= type :frame)
(when selected?
(dom/stop-propagation event)
(st/emit! start-move-frame))
drawing?
nil
(and (not selected?) (empty? selected))
(do
(dom/stop-propagation event)
(st/emit! dw/deselect-all
(dw/select-shape id)
start-move-selected))
(= type :frame)
(when selected?
(dom/stop-propagation event)
(st/emit! start-move-frame))
(and (not selected?) (not (empty? selected)))
(do
(dom/stop-propagation event)
(if (kbd/shift? event)
(st/emit! (dw/select-shape id))
(st/emit! dw/deselect-all
(dw/select-shape id)
start-move-selected)))
:else
(do
(dom/stop-propagation event)
(st/emit! start-move-selected)))))))
(and (not selected?) (empty? selected))
(do
(dom/stop-propagation event)
(st/emit! dw/deselect-all
(dw/select-shape id)
start-move-selected))
(and (not selected?) (not (empty? selected)))
(do
(dom/stop-propagation event)
(if (kbd/shift? event)
(st/emit! (dw/select-shape id))
(st/emit! dw/deselect-all
(dw/select-shape id)
start-move-selected)))
:else
(do
(dom/stop-propagation event)
(st/emit! start-move-selected)))))))
(defn on-context-menu
[event shape]
(dom/prevent-default event)
(dom/stop-propagation event)
(let [position (dom/get-client-position event)]
(st/emit!(dw/show-shape-context-menu {:position position
:shape shape}))))

View file

@ -95,6 +95,7 @@
:deps (mf/deps (:id shape))})
selected? (mf/deref selected-iref)
on-mouse-down #(common/on-mouse-down % shape)
on-context-menu #(common/on-context-menu % shape)
shape (merge frame-default-props shape)
childs (mapv #(get objects %) (:shapes shape))
@ -105,6 +106,7 @@
(st/emit! dw/deselect-all
(dw/select-shape (:id shape))))]
[:g {:class (when selected? "selected")
:on-context-menu on-context-menu
:on-double-click on-double-click
:on-mouse-down on-mouse-down}
[:& frame-shape {:shape shape :childs childs}]])))

View file

@ -23,8 +23,10 @@
(mf/defrc rect-wrapper
[props]
(let [shape (unchecked-get props "shape")
on-mouse-down #(common/on-mouse-down % shape)]
[:g.shape {:on-mouse-down on-mouse-down}
on-mouse-down #(common/on-mouse-down % shape)
on-context-menu #(common/on-context-menu % shape)]
[:g.shape {:on-mouse-down on-mouse-down
:on-context-menu on-context-menu}
[:& rect-shape {:shape shape}]]))
;; --- Rect Shape

View file

@ -22,9 +22,8 @@
[uxbox.main.ui.messages :refer [messages-widget]]
[uxbox.main.ui.workspace.viewport :refer [viewport]]
[uxbox.main.ui.workspace.colorpalette :refer [colorpalette]]
;; [uxbox.main.ui.workspace.download]
[uxbox.main.ui.workspace.context-menu :refer [context-menu]]
[uxbox.main.ui.workspace.header :refer [header]]
;; [uxbox.main.ui.workspace.images]
[uxbox.main.ui.workspace.rules :refer [horizontal-rule vertical-rule]]
[uxbox.main.ui.workspace.scroll :as scroll]
[uxbox.main.ui.workspace.shortcuts :as shortcuts]
@ -74,6 +73,7 @@
[:& colorpalette])
[:main.main-content
[:& context-menu {}]
[:section.workspace-content
{:class classes
:on-scroll on-scroll

View file

@ -0,0 +1,72 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; This Source Code Form is "Incompatible With Secondary Licenses", as
;; defined by the Mozilla Public License, v. 2.0.
;;
;; Copyright (c) 2020 UXBOX Labs SL
(ns uxbox.main.ui.workspace.context-menu
"A workspace specific context menu (mouse right click)."
(:require
[beicon.core :as rx]
[lentes.core :as l]
[potok.core :as ptk]
[rumext.alpha :as mf]
[uxbox.main.store :as st]
[uxbox.main.refs :as refs]
[uxbox.main.streams :as ms]
[uxbox.builtins.icons :as i]
[uxbox.util.dom :as dom]
[uxbox.main.data.workspace :as dw]
[uxbox.main.ui.react-hooks :refer [use-rxsub]]
[uxbox.main.ui.components.dropdown :refer [dropdown]]))
(def menu-ref
(-> (l/key :context-menu)
(l/derive refs/workspace-local)))
(defn- prevent-default
[event]
(dom/prevent-default event)
(dom/stop-propagation event))
(mf/defc shape-context-menu
[{:keys [mdata] :as props}]
(let [shape (:shape mdata)
selected (:selected mdata)
on-duplicate
(fn [event]
(st/emit! dw/duplicate-selected))
on-delete
(fn [event]
(st/emit! dw/delete-selected)) ]
[:*
[:li {:on-click on-duplicate} i/copy [:span "duplicate"]]
[:li {:on-click on-delete} i/trash [:span "delete"]]]))
(mf/defc viewport-context-menu
[{:keys [mdata] :as props}]
[:*
[:li i/copy [:span "paste (TODO)"]]
[:li i/copy [:span "copy as svg (TODO)"]]])
(mf/defc context-menu
[props]
(let [mdata (mf/deref menu-ref)]
[:& dropdown {:show (boolean mdata)
:on-close #(st/emit! dw/hide-context-menu)}
[:ul.workspace-context-menu
{:style {:top (- (get-in mdata [:position :y]) 20)
:left (get-in mdata [:position :x])}
:on-context-menu prevent-default}
(if (:shape mdata)
[:& shape-context-menu {:mdata mdata}]
[:& viewport-context-menu {:mdata mdata}])]]))

View file

@ -118,6 +118,14 @@
(st/emit! dw/deselect-all
(dw/select-shape id)))))
on-context-menu
(fn [event]
(dom/prevent-default event)
(dom/stop-propagation event)
(let [pos (dom/get-client-position event)]
(st/emit! (dw/show-shape-context-menu {:position pos
:shape item}))))
on-hover
(fn [item monitor]
(st/emit! (dw/shape-order-change (:obj-id item) index)))
@ -134,6 +142,7 @@
:on-hover on-hover
:on-drop on-drop})]
[:li {:ref dnd-ref
:on-context-menu on-context-menu
:class (dom/classnames
:selected selected?
:dragging-TODO (:dragging? dprops))}
@ -195,6 +204,14 @@
(st/emit! dw/deselect-all
(dw/select-shape id)))))
on-context-menu
(fn [event]
(dom/prevent-default event)
(dom/stop-propagation event)
(let [pos (dom/get-client-position event)]
(st/emit! (dw/show-shape-context-menu {:position pos
:shape item}))))
on-drop
(fn [item monitor]
(st/emit! (dw/commit-shape-order-change (:obj-id item))))
@ -211,6 +228,7 @@
:on-hover on-hover
:on-drop on-drop})]
[:li.group {:ref dnd-ref
:on-context-menu on-context-menu
:class (dom/classnames
:selected selected?
:dragging-TODO (:dragging? dprops))}

View file

@ -165,11 +165,8 @@
(fn [event]
(dom/prevent-default event)
(dom/stop-propagation event)
(let [ctrl? (kbd/ctrl? event)
shift? (kbd/shift? event)
opts {:shift? shift?
:ctrl? ctrl?}]
(st/emit! (ms/->MouseEvent :context-menu ctrl? shift?))))
(let [position (dom/get-client-position event)]
(st/emit! (dw/show-context-menu {:position position}))))
on-mouse-up
(fn [event]
@ -225,25 +222,14 @@
(st/emit! ::finish-positioning #_(dw/stop-viewport-positioning)))
(st/emit! (ms/->KeyboardEvent :up key ctrl? shift?))))
;; translate-point-to-viewport
;; (fn [pt]
;; (let [viewport (mf/ref-node viewport-ref)
;; brect (.getBoundingClientRect viewport)
;; brect (gpt/point (parse-int (.-left brect))
;; (parse-int (.-top brect)))]
;; (gpt/subtract pt brect)))
on-mouse-move
(fn [event]
;; NOTE: offsetX and offsetY are marked as "experimental" on
;; MDN site but seems like they are supported on all
;; browsers so we can avoid translation opetation just using
;; this attributes.
(let [;; pt (gpt/point (.-clientX event)
;; (.-clientY event))
;; pt (translate-point-to-viewport pt)
pt (gpt/point (.-offsetX (.-nativeEvent event))
(.-offsetY (.-nativeEvent event)))]
(let [pt (dom/get-offset-position event)]
(reset! last-position pt)
(st/emit! (ms/->PointerEvent :viewport pt
(kbd/ctrl? event)
(kbd/shift? event)))))
@ -251,10 +237,14 @@
on-mount
(fn []
(let [key1 (events/listen js/document EventType.KEYDOWN on-key-down)
key2 (events/listen js/document EventType.KEYUP on-key-up)]
key2 (events/listen js/document EventType.KEYUP on-key-up)
dnode (mf/ref-val viewport-ref)
key3 (events/listen dnode EventType.MOUSEMOVE on-mouse-move)]
(fn []
(events/unlistenByKey key1)
(events/unlistenByKey key2))))]
(events/unlistenByKey key2)
(events/unlistenByKey key3)
)))]
(mf/use-effect on-mount)
[:*
@ -266,7 +256,6 @@
:on-context-menu on-context-menu
:on-click on-click
:on-double-click on-double-click
:on-mouse-move on-mouse-move
:on-mouse-down on-mouse-down
:on-mouse-up on-mouse-up}
[:g.zoom {:transform (str "scale(" zoom ", " zoom ")")}