mirror of
https://github.com/penpot/penpot.git
synced 2025-01-24 23:49:45 -05:00
🎉 Add interactions with the mouse
This commit is contained in:
parent
23ba635aab
commit
e602a8cc36
4 changed files with 150 additions and 85 deletions
|
@ -77,7 +77,8 @@
|
|||
:drawing nil
|
||||
:drawing-tool nil
|
||||
:tooltip nil
|
||||
:options-mode :design})
|
||||
:options-mode :design
|
||||
:draw-interaction-to nil})
|
||||
|
||||
(def initialize-layout
|
||||
(ptk/reify ::initialize-layout
|
||||
|
@ -1418,6 +1419,63 @@
|
|||
:index index-in-parent}]]
|
||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}))))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Interactions
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(declare move-create-interaction)
|
||||
(declare finish-create-interaction)
|
||||
|
||||
(defn start-create-interaction
|
||||
[]
|
||||
(ptk/reify ::start-create-interaction
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [initial-pos @ms/mouse-position
|
||||
selected (get-in state [:workspace-local :selected])
|
||||
stopper (rx/filter ms/mouse-up? stream)]
|
||||
(when (= 1 (count selected))
|
||||
(rx/concat
|
||||
(->> ms/mouse-position
|
||||
(rx/take-until stopper)
|
||||
(rx/map #(move-create-interaction initial-pos %)))
|
||||
(rx/of (finish-create-interaction initial-pos))))))))
|
||||
|
||||
(defn move-create-interaction
|
||||
[initial-pos position]
|
||||
(ptk/reify ::move-create-interaction
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(if (= position initial-pos)
|
||||
state
|
||||
(assoc-in state [:workspace-local :draw-interaction-to] position)))))
|
||||
|
||||
(defn finish-create-interaction
|
||||
[initial-pos]
|
||||
(ptk/reify ::finish-create-interaction
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(assoc-in state [:workspace-local :draw-interaction-to] nil))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [position @ms/mouse-position
|
||||
|
||||
page-id (:current-page-id state)
|
||||
objects (get-in state [:workspace-data page-id :objects])
|
||||
frame (dwc/get-frame-at-point objects position)
|
||||
|
||||
shape-id (first (get-in state [:workspace-local :selected]))]
|
||||
|
||||
(when-not (= position initial-pos)
|
||||
(if (and frame shape-id (not= (:id frame) shape-id))
|
||||
(rx/of (update-shape shape-id
|
||||
{:interactions [{:event-type :click
|
||||
:action-type :navigate
|
||||
:destination (:id frame)}]}))
|
||||
(rx/of (update-shape shape-id
|
||||
{:interactions []}))))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Exports
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
|
|
@ -210,6 +210,15 @@
|
|||
(when-not (empty? rch)
|
||||
(rx/of (commit-changes rch uch {:commit-local? true})))))))
|
||||
|
||||
|
||||
(defn get-frame-at-point
|
||||
[objects point]
|
||||
(let [frames (cp/select-frames objects)]
|
||||
(loop [frame (first frames)
|
||||
rest (rest frames)]
|
||||
(d/seek #(geom/has-point? % point) frames))))
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Undo / Redo
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
(let [selected @refs/selected-shapes
|
||||
selected? (contains? selected id)
|
||||
drawing? @refs/selected-drawing-tool
|
||||
options-mode @refs/options-mode
|
||||
button (.-which (.-nativeEvent event))]
|
||||
(when-not (:blocked shape)
|
||||
(cond
|
||||
|
@ -36,27 +37,20 @@
|
|||
(= type :frame)
|
||||
(when selected?
|
||||
(dom/stop-propagation event)
|
||||
(st/emit! (dw/start-move-selected)))
|
||||
(if (= options-mode :design)
|
||||
(st/emit! (dw/start-move-selected))
|
||||
(st/emit! (dw/start-create-interaction))))
|
||||
|
||||
(and (not selected?) (empty? selected))
|
||||
(do
|
||||
(dom/stop-propagation event)
|
||||
(st/emit! dw/deselect-all
|
||||
(dw/select-shape id)
|
||||
(dw/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)
|
||||
(dw/start-move-selected))))
|
||||
:else
|
||||
(do
|
||||
(dom/stop-propagation event)
|
||||
(st/emit! (dw/start-move-selected)))))))
|
||||
(when-not selected?
|
||||
(when-not (or (empty? selected) (kbd/shift? event))
|
||||
(st/emit! dw/deselect-all))
|
||||
(st/emit! (dw/select-shape id)))
|
||||
(if (= options-mode :design)
|
||||
(st/emit! (dw/start-move-selected))
|
||||
(st/emit! (dw/start-create-interaction))))))))
|
||||
|
||||
(defn on-context-menu
|
||||
[event shape]
|
||||
|
|
|
@ -26,62 +26,21 @@
|
|||
[shape]
|
||||
(first (filter #(= (:event-type %) :click) (:interactions shape))))
|
||||
|
||||
(defn- on-mouse-down-unselected
|
||||
|
||||
(defn- on-mouse-down
|
||||
[event {:keys [id type] :as shape} selected]
|
||||
(do
|
||||
(dom/stop-propagation event)
|
||||
(if (empty? selected)
|
||||
(st/emit! (dw/select-shape id)
|
||||
(dw/start-move-selected))
|
||||
(when-not (empty? selected)
|
||||
(st/emit! dw/deselect-all))
|
||||
(st/emit! (dw/select-shape id))
|
||||
(st/emit! (dw/start-create-interaction))))
|
||||
|
||||
(if (kbd/shift? event)
|
||||
(st/emit! (dw/select-shape id))
|
||||
(st/emit! dw/deselect-all
|
||||
(dw/select-shape id)
|
||||
(dw/start-move-selected))))))
|
||||
|
||||
;; TODO: add more mouse behavior, to create interactions by drag&drop
|
||||
;; (defn- on-mouse-down
|
||||
;; [event {:keys [id type] :as shape}]
|
||||
;; (let [selected @refs/selected-shapes
|
||||
;; selected? (contains? selected id)
|
||||
;; drawing? @refs/selected-drawing-tool
|
||||
;; button (.-which (.-nativeEvent event))]
|
||||
;; (when-not (:blocked shape)
|
||||
;; (cond
|
||||
;; (not= 1 button)
|
||||
;; nil
|
||||
;;
|
||||
;; drawing?
|
||||
;; nil
|
||||
;;
|
||||
;; (= type :frame)
|
||||
;; (when selected?
|
||||
;; (dom/stop-propagation event)
|
||||
;; (st/emit! (dw/start-move-selected)))
|
||||
;;
|
||||
;; (and (not selected?) (empty? selected))
|
||||
;; (do
|
||||
;; (dom/stop-propagation event)
|
||||
;; (st/emit! dw/deselect-all
|
||||
;; (dw/select-shape id)
|
||||
;; (dw/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)
|
||||
;; (dw/start-move-selected))))
|
||||
;; :else
|
||||
;; (do
|
||||
;; (dom/stop-propagation event)
|
||||
;; (st/emit! (dw/start-move-selected)))))))
|
||||
|
||||
(mf/defc interaction-path
|
||||
[{:keys [orig-shape dest-shape selected selected?] :as props}]
|
||||
(defn connect-to-shape
|
||||
"Calculate the best position to draw an interaction line
|
||||
between two shapes"
|
||||
[orig-shape dest-shape]
|
||||
(let [orig-rect (geom/selection-rect-shape orig-shape)
|
||||
dest-rect (geom/selection-rect-shape dest-shape)
|
||||
|
||||
|
@ -103,12 +62,48 @@
|
|||
orig-x (if (= orig-pos :right) orig-x-right orig-x-left)
|
||||
dest-x (if (= dest-pos :right) dest-x-right dest-x-left)
|
||||
|
||||
orig-y (+ (:y orig-rect) (/ (:height orig-rect) 2))
|
||||
dest-y (+ (:y dest-rect) (/ (:height dest-rect) 2))]
|
||||
|
||||
[orig-pos orig-x orig-y dest-pos dest-x dest-y]))
|
||||
|
||||
|
||||
(defn connect-to-point
|
||||
"Calculate the best position to draw an interaction line
|
||||
between one shape and one point"
|
||||
[orig-shape dest-point]
|
||||
(let [orig-rect (geom/selection-rect-shape orig-shape)
|
||||
|
||||
orig-x-left (:x orig-rect)
|
||||
orig-x-right (+ orig-x-left (:width orig-rect))
|
||||
orig-x-center (+ orig-x-left (/ (:width orig-rect) 2))
|
||||
|
||||
dest-x (:x dest-point)
|
||||
dest-y (:y dest-point)
|
||||
|
||||
orig-pos (if (<= orig-x-right dest-x) :right
|
||||
(if (>= orig-x-left dest-x) :left
|
||||
(if (<= orig-x-center dest-x) :right :left)))
|
||||
dest-pos (if (<= orig-x-right dest-x) :left
|
||||
(if (>= orig-x-left dest-x) :right
|
||||
(if (<= orig-x-center dest-x) :right :left)))
|
||||
|
||||
orig-x (if (= orig-pos :right) orig-x-right orig-x-left)
|
||||
orig-y (+ (:y orig-rect) (/ (:height orig-rect) 2))]
|
||||
|
||||
[orig-pos orig-x orig-y dest-pos dest-x dest-y]))
|
||||
|
||||
|
||||
(mf/defc interaction-path
|
||||
[{:keys [orig-shape dest-shape dest-point selected selected?] :as props}]
|
||||
(let [[orig-pos orig-x orig-y dest-pos dest-x dest-y]
|
||||
(if dest-shape
|
||||
(connect-to-shape orig-shape dest-shape)
|
||||
(connect-to-point orig-shape dest-point))
|
||||
|
||||
orig-dx (if (= orig-pos :right) 100 -100)
|
||||
dest-dx (if (= dest-pos :right) 100 -100)
|
||||
|
||||
orig-y (+ (:y orig-rect) (/ (:height orig-rect) 2))
|
||||
dest-y (+ (:y dest-rect) (/ (:height dest-rect) 2))
|
||||
|
||||
path ["M" orig-x orig-y "C" (+ orig-x orig-dx) orig-y (+ dest-x dest-dx) dest-y dest-x dest-y]
|
||||
pdata (str/join " " path)
|
||||
|
||||
|
@ -122,16 +117,16 @@
|
|||
:fill "transparent"
|
||||
:stroke-width 2
|
||||
:d pdata
|
||||
:on-mouse-down #(on-mouse-down-unselected % orig-shape selected)}]
|
||||
:on-mouse-down #(on-mouse-down % orig-shape selected)}]
|
||||
|
||||
[:g
|
||||
[:g {:on-mouse-down #(on-mouse-down % orig-shape selected)}
|
||||
[:path {:stroke "#31EFB8"
|
||||
:fill "transparent"
|
||||
:stroke-width 2
|
||||
:d pdata}]
|
||||
[:circle {:cx orig-x
|
||||
:cy orig-y
|
||||
:r 4
|
||||
:r 8
|
||||
:stroke "#31EFB8"
|
||||
:stroke-width 2
|
||||
:fill "#FFFFFF"}]
|
||||
|
@ -149,9 +144,12 @@
|
|||
(mf/defc interactions
|
||||
[{:keys [selected] :as props}]
|
||||
(let [data (mf/deref refs/workspace-data)
|
||||
local (mf/deref refs/workspace-local)
|
||||
objects (:objects data)
|
||||
active-shapes (filter #(first (get-click-interaction %)) (vals objects))
|
||||
selected-shapes (map #(get objects %) selected)]
|
||||
selected-shapes (map #(get objects %) selected)
|
||||
draw-interaction-to (:draw-interaction-to local)
|
||||
first-selected (first selected-shapes)]
|
||||
[:*
|
||||
(for [shape active-shapes]
|
||||
(let [interaction (get-click-interaction shape)
|
||||
|
@ -164,13 +162,19 @@
|
|||
:selected selected
|
||||
:selected? false}])))
|
||||
|
||||
(for [shape selected-shapes]
|
||||
(let [interaction (get-click-interaction shape)
|
||||
dest-shape (get objects (:destination interaction))]
|
||||
(when dest-shape
|
||||
[:& interaction-path {:key (:id shape)
|
||||
:orig-shape shape
|
||||
:dest-shape dest-shape
|
||||
:selected selected
|
||||
:selected? true}])))]))
|
||||
(if (and draw-interaction-to first-selected)
|
||||
[:& interaction-path {:key "interactive"
|
||||
:orig-shape first-selected
|
||||
:dest-point draw-interaction-to
|
||||
:selected? true}]
|
||||
|
||||
(for [shape selected-shapes]
|
||||
(let [interaction (get-click-interaction shape)
|
||||
dest-shape (get objects (:destination interaction))]
|
||||
(when dest-shape
|
||||
[:& interaction-path {:key (:id shape)
|
||||
:orig-shape shape
|
||||
:dest-shape dest-shape
|
||||
:selected selected
|
||||
:selected? true}]))))]))
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue