diff --git a/common/src/app/common/pages/helpers.cljc b/common/src/app/common/pages/helpers.cljc index 5107be901..b132ccc1e 100644 --- a/common/src/app/common/pages/helpers.cljc +++ b/common/src/app/common/pages/helpers.cljc @@ -278,6 +278,11 @@ (rest frames))] (or (:id top-frame) uuid/zero))) +(defn frame-by-position + [objects position] + (let [frame-id (frame-id-by-position objects position)] + (get objects frame-id))) + (declare indexed-shapes) (defn get-base-shape diff --git a/frontend/src/app/main/data/viewer.cljs b/frontend/src/app/main/data/viewer.cljs index 7dbbe296c..5f45b0173 100644 --- a/frontend/src/app/main/data/viewer.cljs +++ b/frontend/src/app/main/data/viewer.cljs @@ -11,7 +11,6 @@ [app.common.pages.helpers :as cph] [app.common.spec :as us] [app.common.spec.interactions :as cti] - [app.common.uuid :as uuid] [app.main.data.comments :as dcm] [app.main.data.fonts :as df] [app.main.repo :as rp] @@ -88,10 +87,10 @@ (defn select-frames [{:keys [objects] :as page}] - (let [root (get objects uuid/zero)] - (into [] (comp (map #(get objects %)) - (filter #(= :frame (:type %)))) - (reverse (:shapes root))))) + (into [] + (comp (map (d/getf objects)) + (remove :hide-in-viewer)) + (cph/sort-z-index objects (cph/get-frames-ids objects)))) ;; --- Data Fetching diff --git a/frontend/src/app/main/data/workspace/common.cljs b/frontend/src/app/main/data/workspace/common.cljs index 5bc125ce1..e5296d210 100644 --- a/frontend/src/app/main/data/workspace/common.cljs +++ b/frontend/src/app/main/data/workspace/common.cljs @@ -8,7 +8,6 @@ (:require [app.common.data :as d] [app.common.geom.proportions :as gpr] - [app.common.geom.shapes :as gsh] [app.common.logging :as log] [app.common.pages :as cp] [app.common.pages.changes-builder :as pcb] @@ -59,13 +58,6 @@ ;; --- Common Helpers & Events -;; TODO: looks duplicate - -(defn get-frame-at-point - [objects point] - (let [frames (cph/get-frames objects)] - (d/seek #(gsh/has-point? % point) frames))) - (defn- extract-numeric-suffix [basename] (if-let [[_ p1 p2] (re-find #"(.*)-([0-9]+)$" basename)] diff --git a/frontend/src/app/main/data/workspace/interactions.cljs b/frontend/src/app/main/data/workspace/interactions.cljs index d44a55a77..200c35908 100644 --- a/frontend/src/app/main/data/workspace/interactions.cljs +++ b/frontend/src/app/main/data/workspace/interactions.cljs @@ -182,10 +182,17 @@ selected-shape (get objects selected-shape-id) selected-shape-frame-id (:frame-id selected-shape) start-frame (get objects selected-shape-frame-id) - end-frame (dwc/get-frame-at-point objects position)] - (cond-> state - (not= position initial-pos) (assoc-in [:workspace-local :draw-interaction-to] position) - (not= start-frame end-frame) (assoc-in [:workspace-local :draw-interaction-to-frame] end-frame)))))) + end-frame (cph/frame-by-position objects position) + + position (when (not= position initial-pos) position) + end-frame (when (and (not= (:id end-frame) uuid/zero ) + (not= (:id end-frame) (:id start-frame)) + (not= (:id end-frame) selected-shape-id) + (not (:hide-in-viewer end-frame))) + end-frame)] + (-> state + (assoc-in [:workspace-local :draw-interaction-to] position) + (assoc-in [:workspace-local :draw-interaction-to-frame] end-frame)))))) (defn finish-edit-interaction [index initial-pos] @@ -199,32 +206,49 @@ ptk/WatchEvent (watch [_ state _] - (let [position @ms/mouse-position - page-id (:current-page-id state) - objects (wsh/lookup-page-objects state page-id) - frame (dwc/get-frame-at-point objects position) + (let [position @ms/mouse-position + page-id (:current-page-id state) + objects (wsh/lookup-page-objects state page-id) + target-frame (cph/frame-by-position objects position) - shape-id (-> state wsh/lookup-selected first) - shape (get objects shape-id)] + shape-id (-> state wsh/lookup-selected first) + shape (get objects shape-id) - (when (and shape (not (= position initial-pos))) - (if (nil? frame) - (when index - (rx/of (remove-interaction shape index))) - (let [frame (if (or (= (:id frame) (:id shape)) - (= (:id frame) (:frame-id shape))) - nil ;; Drop onto self frame -> set destination to none - frame)] - (if (nil? index) - (rx/of (add-new-interaction shape (:id frame))) - (rx/of (update-interaction shape index - (fn [interaction] - (cond-> interaction - (not (csi/has-destination interaction)) - (csi/set-action-type :navigate) + invalid-target? (or (nil? target-frame) + (= (:id target-frame) uuid/zero) + (= (:id target-frame) (:id shape)) + (= (:id target-frame) (:frame-id shape)) + (:hide-in-viewer target-frame)) + + change-interaction + (fn [interaction] + (cond-> interaction + (not (csi/has-destination interaction)) + (csi/set-action-type :navigate) + + :always + (csi/set-destination (:id target-frame))))] + + (cond + (or (nil? shape) + + ;; Didn't changed the position for the interaction + (= position initial-pos) + + ;; New interaction but invalid target + (and (nil? index) invalid-target?)) + nil + + ;; Dropped interaction in an invalid target. We remove it + (and (some? index) invalid-target?) + (rx/of (remove-interaction shape index)) + + (nil? index) + (rx/of (add-new-interaction shape (:id target-frame))) + + :else + (rx/of (update-interaction shape index change-interaction))))))) - :always - (csi/set-destination (:id frame)))))))))))))) ;; --- Overlays (declare move-overlay-pos) diff --git a/frontend/src/app/main/data/workspace/transforms.cljs b/frontend/src/app/main/data/workspace/transforms.cljs index 0ae4229f9..89bd28301 100644 --- a/frontend/src/app/main/data/workspace/transforms.cljs +++ b/frontend/src/app/main/data/workspace/transforms.cljs @@ -762,8 +762,8 @@ (rx/map (partial set-modifiers ids)) (rx/take-until stopper)) - (rx/of (apply-modifiers ids) - (calculate-frame-for-move ids) + (rx/of (calculate-frame-for-move ids) + (apply-modifiers ids) (finish-transform))))))))) (s/def ::direction #{:up :down :right :left}) @@ -854,9 +854,8 @@ moving-shapes (->> ids (cph/clean-loops objects) - (map #(get objects %)) - (remove #(or (nil? %) - (= (:frame-id %) frame-id)))) + (keep #(get objects %)) + (remove #(= (:frame-id %) frame-id))) changes (-> (pcb/empty-changes it page-id) (pcb/with-objects objects) diff --git a/frontend/src/app/main/ui/measurements.cljs b/frontend/src/app/main/ui/measurements.cljs index f879e2345..6571b3c58 100644 --- a/frontend/src/app/main/ui/measurements.cljs +++ b/frontend/src/app/main/ui/measurements.cljs @@ -240,16 +240,20 @@ (when (seq selected-shapes) [:g.measurement-feedback {:pointer-events "none"} - [:& selection-guides {:selrect selected-selrect :bounds bounds :zoom zoom}] + [:& selection-guides {:selrect selected-selrect + :bounds bounds + :zoom zoom}] [:& size-display {:selrect selected-selrect :zoom zoom}] (if (or (not hover-shape) (not hover-selected-shape?)) (when (and frame (not= uuid/zero (:id frame))) - [:g.hover-shapes - [:& distance-display {:from (:selrect frame) - :to selected-selrect - :zoom zoom - :bounds bounds-selrect}]]) + (let [frame-bb (-> (:points frame) (gsh/points->selrect))] + [:g.hover-shapes + [:& selection-rect {:type :hover :selrect frame-bb :zoom zoom}] + [:& distance-display {:from frame-bb + :to selected-selrect + :zoom zoom + :bounds bounds-selrect}]])) [:g.hover-shapes [:& selection-rect {:type :hover :selrect hover-selrect :zoom zoom}] diff --git a/frontend/src/app/main/ui/viewer.cljs b/frontend/src/app/main/ui/viewer.cljs index 4b84d5c03..45eb9b7c1 100644 --- a/frontend/src/app/main/ui/viewer.cljs +++ b/frontend/src/app/main/ui/viewer.cljs @@ -8,8 +8,10 @@ (:require [app.common.colors :as clr] [app.common.data :as d] + [app.common.data.macros :as dm] [app.common.exceptions :as ex] [app.common.geom.point :as gpt] + [app.common.geom.shapes :as gsh] [app.common.pages.helpers :as cph] [app.common.text :as txt] [app.main.data.comments :as dcm] @@ -37,18 +39,23 @@ [rumext.alpha :as mf])) (defn- calculate-size - [frame zoom] - (let [{:keys [_ _ width height]} (filters/get-filters-bounds frame) + [frame zoom bounds] + (let [frame-bounds (filters/get-filters-bounds frame) + {:keys [x y width height]} (if (:show-content frame) + (gsh/join-rects [bounds frame-bounds]) + frame-bounds) padding (filters/calculate-padding frame) - x (- (:horizontal padding)) - y (- (:vertical padding)) + x (- x (:horizontal padding)) + y (- y (:vertical padding)) width (+ width (* 2 (:horizontal padding))) height (+ height (* 2 (:vertical padding)))] {:base-width width :base-height height + :x x + :y y :width (* width zoom) :height (* height zoom) - :vbox (str x " " y " " width " " height)})) + :vbox (dm/fmt "% % % %" 0 0 width height)})) (defn- calculate-wrapper [size1 size2 zoom] @@ -128,7 +135,7 @@ :interactions-mode interactions-mode}] (for [overlay overlays] - (let [size-over (calculate-size (:frame overlay) zoom)] + (let [size-over (calculate-size (:frame overlay) zoom children-bounds)] [:* (when (or (:close-click-outside overlay) (:background-overlay overlay)) @@ -198,6 +205,13 @@ zoom (:zoom local) frames (:frames page) frame (get frames index) + + children-bounds + (mf/use-memo + (mf/deps page (:id frame)) + #(-> (cph/get-children (:objects page) (:id frame)) + (gsh/selection-rect))) + fullscreen? (mf/deref refs/viewer-fullscreen?) overlays (:overlays local) scroll (mf/use-state nil) @@ -207,12 +221,13 @@ (d/seek #(= (:id %) (:orig-frame-id current-animation)) frames)) size (mf/use-memo - (mf/deps frame zoom) - (fn [] (calculate-size frame zoom))) + (mf/deps frame zoom children-bounds) + (fn [] (calculate-size frame zoom children-bounds))) orig-size (mf/use-memo (mf/deps orig-frame zoom) - (fn [] (when orig-frame (calculate-size orig-frame zoom)))) + (fn [] (when orig-frame + (calculate-size orig-frame zoom children-bounds)))) wrapper-size (mf/use-memo (mf/deps size orig-size zoom) @@ -305,7 +320,7 @@ wrapper-size))))) (mf/use-layout-effect - (mf/deps current-animation) + (mf/deps current-animation children-bounds) (fn [] ;; Overlay animations may be started when needed. (when current-animation @@ -315,7 +330,7 @@ (let [overlay-viewport (dom/get-element (str "overlay-" (str (:overlay-id current-animation)))) overlay (d/seek #(= (:id (:frame %)) (:overlay-id current-animation)) overlays) - overlay-size (calculate-size (:frame overlay) zoom) + overlay-size (calculate-size (:frame overlay) zoom children-bounds) overlay-position {:x (* (:x (:position overlay)) zoom) :y (* (:y (:position overlay)) zoom)}] (interactions/animate-open-overlay @@ -329,7 +344,7 @@ (let [overlay-viewport (dom/get-element (str "overlay-" (str (:overlay-id current-animation)))) overlay (d/seek #(= (:id (:frame %)) (:overlay-id current-animation)) overlays) - overlay-size (calculate-size (:frame overlay) zoom) + overlay-size (calculate-size (:frame overlay) zoom children-bounds) overlay-position {:x (* (:x (:position overlay)) zoom) :y (* (:y (:position overlay)) zoom)}] (interactions/animate-close-overlay @@ -392,6 +407,7 @@ :file file :section section :local local + :size size} :index index :viewer-pagination viewer-pagination}] diff --git a/frontend/src/app/main/ui/viewer/handoff.cljs b/frontend/src/app/main/ui/viewer/handoff.cljs index a17fecc13..8a238fea6 100644 --- a/frontend/src/app/main/ui/viewer/handoff.cljs +++ b/frontend/src/app/main/ui/viewer/handoff.cljs @@ -25,7 +25,7 @@ (st/emit! (dv/select-shape (:id frame))))) (mf/defc viewport - [{:keys [local file page frame index viewer-pagination]}] + [{:keys [local file page frame index viewer-pagination size]}] (let [on-mouse-wheel (fn [event] (when (kbd/mod? event) @@ -60,7 +60,7 @@ [:div.handoff-svg-wrapper {:on-click (handle-select-frame frame)} [:& viewer-pagination {:index index :num-frames (count (:frames page)) :left-bar true :right-bar true}] [:div.handoff-svg-container - [:& render-frame-svg {:frame frame :page page :local local}]]] + [:& render-frame-svg {:frame frame :page page :local local :size size}]]] [:& right-sidebar {:frame frame :selected (:selected local) diff --git a/frontend/src/app/main/ui/viewer/handoff/render.cljs b/frontend/src/app/main/ui/viewer/handoff/render.cljs index 30df848d7..6fb837fd5 100644 --- a/frontend/src/app/main/ui/viewer/handoff/render.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/render.cljs @@ -13,7 +13,6 @@ [app.main.store :as st] [app.main.ui.shapes.bool :as bool] [app.main.ui.shapes.circle :as circle] - [app.main.ui.shapes.filters :as filters] [app.main.ui.shapes.frame :as frame] [app.main.ui.shapes.group :as group] [app.main.ui.shapes.image :as image] @@ -154,6 +153,10 @@ (let [shape (unchecked-get props "shape") frame (unchecked-get props "frame") + frame-container + (mf/use-memo (mf/deps objects) + #(frame-container-factory objects)) + group-container (mf/use-memo (mf/deps objects) #(group-container-factory objects)) @@ -171,6 +174,7 @@ opts #js {:shape shape :frame frame}] (case (:type shape) + :frame [:> frame-container opts] :text [:> text-wrapper opts] :rect [:> rect-wrapper opts] :path [:> path-wrapper opts] @@ -181,46 +185,31 @@ :svg-raw [:> svg-raw-container opts]))))))) (mf/defc render-frame-svg - [{:keys [page frame local]}] + [{:keys [page frame local size]}] (let [objects (mf/use-memo (mf/deps page frame) - (prepare-objects page frame)) - + (prepare-objects page frame size)) ;; Retrieve frame again with correct modifier frame (get objects (:id frame)) - - zoom (:zoom local 1) - - {:keys [_ _ width height]} (filters/get-filters-bounds frame) - padding (filters/calculate-padding frame) - x (- (:horizontal padding)) - y (- (:vertical padding)) - width (+ width (* 2 (:horizontal padding))) - height (+ height (* 2 (:vertical padding))) - - vbox (str x " " y " " width " " height) - - width (* width zoom) - height (* height zoom) - render (mf/use-memo (mf/deps objects) #(frame-container-factory objects))] [:svg {:id "svg-frame" - :view-box vbox - :width width - :height height + :view-box (:vbox size) + :width (:width size) + :height (:height size) :version "1.1" :xmlnsXlink "http://www.w3.org/1999/xlink" :xmlns "http://www.w3.org/2000/svg" :fill "none"} - [:& render {:shape frame :view-box vbox}] + [:& render {:shape frame :view-box (:vbox size)}] [:& selection-feedback {:frame frame :objects objects - :local local}]])) + :local local + :size size}]])) diff --git a/frontend/src/app/main/ui/viewer/handoff/selection_feedback.cljs b/frontend/src/app/main/ui/viewer/handoff/selection_feedback.cljs index 4445eee30..ba5f7b24d 100644 --- a/frontend/src/app/main/ui/viewer/handoff/selection_feedback.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/selection_feedback.cljs @@ -8,7 +8,7 @@ (:require [app.common.data :as d] [app.common.geom.shapes :as gsh] - [app.main.ui.measurements :refer [selection-guides size-display measurement]] + [app.main.ui.measurements :refer [size-display measurement]] [rumext.alpha :as mf])) ;; ------------------------------------------------ @@ -52,24 +52,23 @@ :stroke-width selection-rect-width}}]])) (mf/defc selection-feedback - [{:keys [frame local objects]}] + [{:keys [frame local objects size]}] (let [{:keys [hover selected zoom]} local - hover-shape (-> (or (first (resolve-shapes objects [hover])) frame) - (gsh/translate-to-frame frame)) - selected-shapes (->> (resolve-shapes objects selected)) - selrect (gsh/selection-rect selected-shapes) - bounds (frame->bounds frame)] + shapes (resolve-shapes objects [hover]) + hover-shape (or (first shapes) frame) + hover-shape (gsh/translate-to-frame hover-shape size) + selected-shapes (resolve-shapes objects selected) + selrect (gsh/selection-rect selected-shapes)] - (when (seq selected-shapes) + (when (d/not-empty? selected-shapes) [:g.selection-feedback {:pointer-events "none"} [:g.selected-shapes - [:& selection-guides {:bounds bounds :selrect selrect :zoom zoom}] [:& selection-rect {:selrect selrect :zoom zoom}] [:& size-display {:selrect selrect :zoom zoom}]] - [:& measurement {:bounds bounds + [:& measurement {:bounds (assoc size :x 0 :y 0) :selected-shapes selected-shapes :hover-shape hover-shape :zoom zoom}]]))) diff --git a/frontend/src/app/main/ui/viewer/header.cljs b/frontend/src/app/main/ui/viewer/header.cljs index 33d2df800..c777ad9d5 100644 --- a/frontend/src/app/main/ui/viewer/header.cljs +++ b/frontend/src/app/main/ui/viewer/header.cljs @@ -157,6 +157,7 @@ [:ul.dropdown (for [id (get-in file [:data :pages])] [:li {:id (str id) + :key (str id) :on-click (partial navigate-to id)} (get-in file [:data :pages-index id :name])])]]] diff --git a/frontend/src/app/main/ui/viewer/interactions.cljs b/frontend/src/app/main/ui/viewer/interactions.cljs index c7a8a7ce3..06a4f2b8b 100644 --- a/frontend/src/app/main/ui/viewer/interactions.cljs +++ b/frontend/src/app/main/ui/viewer/interactions.cljs @@ -25,11 +25,11 @@ [rumext.alpha :as mf])) (defn prepare-objects - [page frame] + [page frame size] (fn [] (let [objects (:objects page) frame-id (:id frame) - modifier (-> (gpt/point (:x frame) (:y frame)) + modifier (-> (gpt/point (:x size) (:y size)) (gpt/negate) (gmt/translate-matrix)) @@ -43,8 +43,8 @@ {::mf/wrap [mf/memo]} [{:keys [page interactions-mode frame base-frame frame-offset size]}] (let [objects (mf/use-memo - (mf/deps page frame) - (prepare-objects page frame)) + (mf/deps page frame size) + (prepare-objects page frame size)) wrapper (mf/use-memo (mf/deps objects) diff --git a/frontend/src/app/main/ui/viewer/shapes.cljs b/frontend/src/app/main/ui/viewer/shapes.cljs index 3398a463e..ca187f7b0 100644 --- a/frontend/src/app/main/ui/viewer/shapes.cljs +++ b/frontend/src/app/main/ui/viewer/shapes.cljs @@ -376,6 +376,10 @@ (mf/use-memo (mf/deps objects) #(group-container-factory objects)) + frame-container + (mf/use-memo (mf/deps objects) + #(frame-container-factory objects)) + bool-container (mf/use-memo (mf/deps objects) #(bool-container-factory objects)) @@ -391,7 +395,7 @@ opts #js {:shape shape :objects objects}] (case (:type shape) - :frame [:g.empty] + :frame [:> frame-container opts] :text [:> text-wrapper opts] :rect [:> rect-wrapper opts] :path [:> path-wrapper opts] diff --git a/frontend/src/app/main/ui/viewer/thumbnails.cljs b/frontend/src/app/main/ui/viewer/thumbnails.cljs index 6473c5ac3..54c90d636 100644 --- a/frontend/src/app/main/ui/viewer/thumbnails.cljs +++ b/frontend/src/app/main/ui/viewer/thumbnails.cljs @@ -7,6 +7,7 @@ (ns app.main.ui.viewer.thumbnails (:require [app.common.data :as d] + [app.common.data.macros :as dm] [app.main.data.viewer :as dv] [app.main.render :as render] [app.main.store :as st] @@ -114,6 +115,7 @@ :total (count frames)} (for [[i frame] (d/enumerate frames)] [:& thumbnail-item {:index i + :key (dm/str (:id frame) "-" i) :frame frame :objects objects :on-click on-item-click diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs index 4d05ca584..7e194f5f9 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs @@ -38,7 +38,7 @@ (def ^:private type->options {:bool #{:size :position :rotation} :circle #{:size :position :rotation} - :frame #{:presets :size :position :radius :clip-content :show-in-viewer} + :frame #{:presets :size :position :rotation :radius :clip-content :show-in-viewer} :group #{:size :position :rotation} :image #{:size :position :rotation :radius} :path #{:size :position :rotation} diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs index 54ecef54b..1c2874d91 100644 --- a/frontend/src/app/main/ui/workspace/viewport.cljs +++ b/frontend/src/app/main/ui/workspace/viewport.cljs @@ -287,6 +287,7 @@ {:objects base-objects :hover #{(->> @hover-ids (filter #(cph/frame-shape? base-objects %)) + (remove selected) (first))} :zoom zoom}]) @@ -400,14 +401,6 @@ [:& widgets/viewport-actions] - (when show-prototypes? - [:& interactions/interactions - {:selected selected - :zoom zoom - :objects objects-modified - :current-transform transform - :hover-disabled? hover-disabled?}]) - [:& scroll-bars/viewport-scrollbars {:objects base-objects :zoom zoom @@ -444,6 +437,12 @@ :shapes selected-shapes :zoom zoom :edition edition - :disable-handlers (or drawing-tool edition @space?)}]]) + :disable-handlers (or drawing-tool edition @space?)}] - ]]])) + (when show-prototypes? + [:& interactions/interactions + {:selected selected + :zoom zoom + :objects objects-modified + :current-transform transform + :hover-disabled? hover-disabled?}])])]]]))