diff --git a/frontend/src/uxbox/main/data/workspace/selection.cljs b/frontend/src/uxbox/main/data/workspace/selection.cljs index 034f64474..63642a807 100644 --- a/frontend/src/uxbox/main/data/workspace/selection.cljs +++ b/frontend/src/uxbox/main/data/workspace/selection.cljs @@ -297,3 +297,11 @@ (rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}) (select-shapes selected)))))) + +(defn change-hover-state [id value] + (ptk/reify ::change-hover-state + ptk/UpdateEvent + (update [_ state] + (-> state + (update-in [:workspace-local :hover] #(if (nil? %) #{} %)) + (update-in [:workspace-local :hover] (comp (if value conj disj)) id))))) diff --git a/frontend/src/uxbox/main/refs.cljs b/frontend/src/uxbox/main/refs.cljs index b232e0ec6..ce4454e0d 100644 --- a/frontend/src/uxbox/main/refs.cljs +++ b/frontend/src/uxbox/main/refs.cljs @@ -147,6 +147,9 @@ (def vbox (l/derived :vbox workspace-local)) +(def current-hover + (l/derived :hover workspace-local)) + ;; ---- Viewer refs (def viewer-data diff --git a/frontend/src/uxbox/main/ui/shapes/frame.cljs b/frontend/src/uxbox/main/ui/shapes/frame.cljs index 8a5e62b8c..09d4df1e8 100644 --- a/frontend/src/uxbox/main/ui/shapes/frame.cljs +++ b/frontend/src/uxbox/main/ui/shapes/frame.cljs @@ -22,7 +22,7 @@ (mf/fnc frame-shape {::mf/wrap-props false} [props] - (let [childs (unchecked-get props "childs") + (let [children (unchecked-get props "children") shape (unchecked-get props "shape") {:keys [id x y width height]} shape @@ -36,7 +36,7 @@ :height height}))] [:svg {:x x :y y :width width :height height} [:> "rect" props] - (for [[i item] (d/enumerate childs)] + (for [[i item] (d/enumerate children)] [:& shape-wrapper {:frame shape :shape item :key (:id item)}])]))) diff --git a/frontend/src/uxbox/main/ui/workspace/shapes.cljs b/frontend/src/uxbox/main/ui/workspace/shapes.cljs index 296be3bd8..a2a64fc46 100644 --- a/frontend/src/uxbox/main/ui/workspace/shapes.cljs +++ b/frontend/src/uxbox/main/ui/workspace/shapes.cljs @@ -19,6 +19,8 @@ [uxbox.main.ui.shapes.circle :as circle] [uxbox.main.ui.shapes.icon :as icon] [uxbox.main.ui.shapes.image :as image] + [uxbox.main.data.workspace.selection :as dws] + [uxbox.main.store :as st] ;; Shapes that has some peculiarities are defined in its own ;; namespace under uxbox.ui.workspace.shapes.* prefix, all the @@ -52,20 +54,39 @@ (and (identical? n-shape o-shape) (identical? n-frame o-frame))))) +(defn use-mouse-over + [{:keys [id] :as shape}] + (mf/use-callback + (mf/deps shape) + (fn [] + (st/emit! (dws/change-hover-state id true))))) + +(defn use-mouse-out + [{:keys [id] :as shape}] + (mf/use-callback + (mf/deps shape) + (fn [] + (st/emit! (dws/change-hover-state id false))))) + (mf/defc shape-wrapper {::mf/wrap [#(mf/memo' % shape-wrapper-memo-equals?)] ::mf/wrap-props false} [props] (let [shape (unchecked-get props "shape") frame (unchecked-get props "frame") - opts #js {:shape (->> shape (geom/transform-shape frame)) + shape (geom/transform-shape frame shape) + opts #js {:shape shape :frame frame} - alt? (mf/use-state false)] + alt? (mf/use-state false) + on-mouse-over (use-mouse-over shape) + on-mouse-out (use-mouse-out shape)] (hooks/use-stream ms/keyboard-alt #(reset! alt? %)) (when (and shape (not (:hidden shape))) - [:g.shape {:style {:cursor (if @alt? cur/duplicate nil)}} + [:g.shape-wrapper {:on-mouse-over on-mouse-over + :on-mouse-out on-mouse-out + :style {:cursor (if @alt? cur/duplicate nil)}} (case (:type shape) :curve [:> path/path-wrapper opts] :path [:> path/path-wrapper opts] @@ -79,7 +100,7 @@ ;; Only used when drawing a new frame. :frame [:> frame-wrapper {:shape shape}] nil) - [:& bounding-box {:shape (->> shape (geom/transform-shape frame)) :frame frame}]]))) + [:& bounding-box {:shape shape :frame frame}]]))) (def group-wrapper (group/group-wrapper-factory shape-wrapper)) (def frame-wrapper (frame/frame-wrapper-factory shape-wrapper)) diff --git a/frontend/src/uxbox/main/ui/workspace/shapes/frame.cljs b/frontend/src/uxbox/main/ui/workspace/shapes/frame.cljs index ec469e8cd..daa178ba0 100644 --- a/frontend/src/uxbox/main/ui/workspace/shapes/frame.cljs +++ b/frontend/src/uxbox/main/ui/workspace/shapes/frame.cljs @@ -9,6 +9,7 @@ (ns uxbox.main.ui.workspace.shapes.frame (:require + [clojure.set :as set] [rumext.alpha :as mf] [uxbox.common.data :as d] [uxbox.main.constants :as c] @@ -16,6 +17,7 @@ [uxbox.main.refs :as refs] [uxbox.main.store :as st] [uxbox.main.ui.workspace.shapes.common :as common] + [uxbox.main.ui.workspace.shapes.outline :refer [outline]] [uxbox.main.ui.shapes.frame :as frame] [uxbox.common.geom.matrix :as gmt] [uxbox.common.geom.point :as gpt] @@ -58,6 +60,9 @@ selected? (mf/deref selected-iref) zoom (mf/deref refs/selected-zoom) + selected-shape? (or (mf/deref refs/selected-shapes) #{}) + hover? (or (mf/deref refs/current-hover) #{}) + outline? (set/union selected-shape? hover?) on-mouse-down (mf/use-callback (mf/deps shape) #(common/on-mouse-down % shape)) @@ -68,7 +73,7 @@ {:keys [x y width height]} shape inv-zoom (/ 1 zoom) - childs (mapv #(get objects %) (:shapes shape)) + children (mapv #(get objects %) (:shapes shape)) ds-modifier (get-in shape [:modifiers :displacement]) label-pos (gpt/point x (- y (/ 10 zoom))) @@ -102,7 +107,12 @@ ;; User may also select the frame with single click in the label :on-click on-double-click} (:name shape)] - [:& frame-shape - {:shape shape - :childs childs}]]))))) + [:* + [:& frame-shape + {:shape shape + :children children}] + + [:g.outlines + (for [child (filter (comp outline? :id) children)] + [:& outline {:shape (geom/transform-shape child)}])]]]))))) diff --git a/frontend/src/uxbox/main/ui/workspace/shapes/outline.cljs b/frontend/src/uxbox/main/ui/workspace/shapes/outline.cljs new file mode 100644 index 000000000..ee56f15b0 --- /dev/null +++ b/frontend/src/uxbox/main/ui/workspace/shapes/outline.cljs @@ -0,0 +1,52 @@ +;; 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.shapes.outline + (:require + [rumext.alpha :as mf] + [uxbox.common.geom.shapes :as gsh] + [uxbox.util.object :as obj] + [rumext.util :refer [map->obj]] + [uxbox.main.ui.shapes.path :as path])) + + +(mf/defc outline + {::mf/wrap-props false} + [props] + (let [shape (unchecked-get props "shape") + transform (gsh/transform-matrix shape) + {:keys [id x y width height]} shape + + outline-type (case (:type shape) + :circle "ellipse" + (:curve :path) "path" + "rect") + + common {:fill "transparent" + :stroke "#31EFB8" + :stroke-width "1px" + :pointer-events "none" + :transform transform} + + props (case (:type shape) + :circle + {:cx (+ x (/ width 2)) + :cy (+ y (/ height 2)) + :rx (/ width 2) + :ry (/ height 2)} + + (:curve :path) + {:d (path/render-path shape)} + + {:x x + :y y + :width width + :height height})] + + [:> outline-type (map->obj (merge common props))])) diff --git a/frontend/src/uxbox/main/ui/workspace/shapes/path.cljs b/frontend/src/uxbox/main/ui/workspace/shapes/path.cljs index 3af9c84fe..c6d3d53ad 100644 --- a/frontend/src/uxbox/main/ui/workspace/shapes/path.cljs +++ b/frontend/src/uxbox/main/ui/workspace/shapes/path.cljs @@ -27,8 +27,7 @@ {::mf/wrap-props false} [props] (let [shape (unchecked-get props "shape") - selected (mf/deref refs/selected-shapes) - selected? (contains? selected (:id shape)) + hover? (or (mf/deref refs/current-hover) #{}) on-mouse-down (mf/use-callback (mf/deps shape) #(common/on-mouse-down % shape)) @@ -38,8 +37,11 @@ on-double-click (mf/use-callback (mf/deps shape) (fn [event] - (when selected? - (st/emit! (dw/start-edition-mode (:id shape))))))] + (when (hover? (:id shape)) + (do + (dom/stop-propagation event) + (dom/prevent-default event) + (st/emit! (dw/start-edition-mode (:id shape)))))))] [:g.shape {:on-double-click on-double-click :on-mouse-down on-mouse-down