diff --git a/common/app/common/pages.cljc b/common/app/common/pages.cljc index 51720fab6e..696c5e9d23 100644 --- a/common/app/common/pages.cljc +++ b/common/app/common/pages.cljc @@ -860,11 +860,16 @@ (distinct)) shapes))) (update-group [group objects] - (let [children (->> (if (:masked-group? group) - [(first (:shapes group))] - (:shapes group)) - (map #(get objects %)))] - (gsh/update-group-selrect group children)))] + (let [children (->> group :shapes (map #(get objects %)))] + (if (:masked-group? group) + (let [mask (first children)] + (-> group + (merge (select-keys mask [:selrect :points])) + (assoc :x (-> mask :selrect :x) + :y (-> mask :selrect :y) + :width (-> mask :selrect :width) + :height (-> mask :selrect :height)))) + (gsh/update-group-selrect group children))))] (if page-id (d/update-in-when data [:pages-index page-id :objects] reg-objects) diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index 26b60f9f7e..481a6eff49 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -1146,8 +1146,11 @@ ptk/WatchEvent (watch [_ state stream] - (rx/of (dws/deselect-all) - (dws/select-shape (:id shape)))))) + (let [selected (get-in state [:workspace-local :selected])] + (if (selected (:id shape)) + (rx/empty) + (rx/of (dws/deselect-all) + (dws/select-shape (:id shape)))))))) (def hide-context-menu (ptk/reify ::hide-context-menu @@ -1478,14 +1481,20 @@ :id (:id group) :operations [{:type :set :attr :masked-group? - :val nil}]}] + :val nil}]} + {:type :reg-objects + :page-id page-id + :shapes [(:id group)]}] uchanges [{:type :mod-obj :page-id page-id :id (:id group) :operations [{:type :set :attr :masked-group? - :val (:masked-group? group)}]}]] + :val (:masked-group? group)}]} + {:type :reg-objects + :page-id page-id + :shapes [(:id group)]}]] (rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}) (dwc/select-shapes (d/ordered-set (:id group)))))))))) diff --git a/frontend/src/app/main/ui/modal.cljs b/frontend/src/app/main/ui/modal.cljs index 544c0d55c1..87d7ad5982 100644 --- a/frontend/src/app/main/ui/modal.cljs +++ b/frontend/src/app/main/ui/modal.cljs @@ -77,7 +77,10 @@ (fn [] (let [keys [(events/listen js/window EventType.POPSTATE on-pop-state) (events/listen js/document EventType.KEYDOWN handle-keydown) - (events/listen js/document EventType.CLICK handle-click-outside) + + ;; Changing to js/document breaks the color picker + (events/listen (dom/get-root) EventType.CLICK handle-click-outside) + (events/listen js/document EventType.CONTEXTMENU handle-click-outside)]] #(doseq [key keys] (events/unlistenByKey key))))) diff --git a/frontend/src/app/main/ui/shapes/custom_stroke.cljs b/frontend/src/app/main/ui/shapes/custom_stroke.cljs index d9705dd7bf..bd6c4df4f4 100644 --- a/frontend/src/app/main/ui/shapes/custom_stroke.cljs +++ b/frontend/src/app/main/ui/shapes/custom_stroke.cljs @@ -9,7 +9,6 @@ [rumext.alpha :as mf] [app.common.uuid :as uuid] [app.common.geom.shapes :as geom] - [app.main.ui.shapes.group :refer [mask-id-ctx]] [app.util.object :as obj])) ; The SVG standard does not implement yet the 'stroke-alignment' @@ -25,16 +24,13 @@ elem-name (unchecked-get props "elem-name") ;; {:keys [x y width height]} (geom/shape->rect-shape shape) {:keys [x y width height]} (:selrect shape) - mask-id (mf/use-ctx mask-id-ctx) stroke-id (mf/use-var (uuid/next)) stroke-style (:stroke-style shape :none) stroke-position (:stroke-alignment shape :center)] (cond ;; Center alignment (or no stroke): the default in SVG (or (= stroke-style :none) (= stroke-position :center)) - [:> elem-name (cond-> (obj/merge! #js {} base-props) - (some? mask-id) - (obj/merge! #js {:mask mask-id}))] + [:> elem-name (obj/merge! #js {} base-props)] ;; Inner alignment: display the shape with double width stroke, ;; and clip the result with the original shape without stroke. @@ -54,15 +50,10 @@ shape-props (-> (obj/merge! #js {} base-props) (obj/merge! #js {:strokeWidth (* stroke-width 2) :clipPath (str "url('#" clip-id "')")}))] - (if (nil? mask-id) - [:* - [:> "clipPath" #js {:id clip-id} - [:> elem-name clip-props]] - [:> elem-name shape-props]] - [:g {:mask mask-id} - [:> "clipPath" #js {:id clip-id} - [:> elem-name clip-props]] - [:> elem-name shape-props]])) + [:* + [:> "clipPath" #js {:id clip-id} + [:> elem-name clip-props]] + [:> elem-name shape-props]]) ;; Outer alingmnent: display the shape in two layers. One ;; without stroke (only fill), and another one only with stroke @@ -100,17 +91,10 @@ :fill "none" :fillOpacity 0 :mask (str "url('#" stroke-mask-id "')")}))] - (if (nil? mask-id) - [:* - [:mask {:id mask-id} - [:> elem-name mask-props1] - [:> elem-name mask-props2]] - [:> elem-name shape-props1] - [:> elem-name shape-props2]] - [:g {:mask mask-id} - [:mask {:id stroke-mask-id} - [:> elem-name mask-props1] - [:> elem-name mask-props2]] - [:> elem-name shape-props1] - [:> elem-name shape-props2]]))))) + [:* + [:mask {:id stroke-mask-id} + [:> elem-name mask-props1] + [:> elem-name mask-props2]] + [:> elem-name shape-props1] + [:> elem-name shape-props2]])))) diff --git a/frontend/src/app/main/ui/shapes/gradients.cljs b/frontend/src/app/main/ui/shapes/gradients.cljs index 120e623dd7..7d60bb88d5 100644 --- a/frontend/src/app/main/ui/shapes/gradients.cljs +++ b/frontend/src/app/main/ui/shapes/gradients.cljs @@ -18,56 +18,54 @@ (mf/defc linear-gradient [{:keys [id gradient shape]}] (let [{:keys [x y width height]} shape] - [:defs - [:linearGradient {:id id - :x1 (:start-x gradient) - :y1 (:start-y gradient) - :x2 (:end-x gradient) - :y2 (:end-y gradient)} - (for [{:keys [offset color opacity]} (:stops gradient)] - [:stop {:key (str id "-stop-" offset) - :offset (or offset 0) - :stop-color color - :stop-opacity opacity}])]])) + [:linearGradient {:id id + :x1 (:start-x gradient) + :y1 (:start-y gradient) + :x2 (:end-x gradient) + :y2 (:end-y gradient)} + (for [{:keys [offset color opacity]} (:stops gradient)] + [:stop {:key (str id "-stop-" offset) + :offset (or offset 0) + :stop-color color + :stop-opacity opacity}])])) (mf/defc radial-gradient [{:keys [id gradient shape]}] (let [{:keys [x y width height]} shape] - [:defs - (let [[x y] (if (= (:type shape) :frame) [0 0] [x y]) - translate-vec (gpt/point (+ x (* width (:start-x gradient))) - (+ y (* height (:start-y gradient)))) - - gradient-vec (gpt/to-vec (gpt/point (* width (:start-x gradient)) - (* height (:start-y gradient))) - (gpt/point (* width (:end-x gradient)) - (* height (:end-y gradient)))) + (let [[x y] (if (= (:type shape) :frame) [0 0] [x y]) + translate-vec (gpt/point (+ x (* width (:start-x gradient))) + (+ y (* height (:start-y gradient)))) - angle (gpt/angle gradient-vec - (gpt/point 1 0)) + gradient-vec (gpt/to-vec (gpt/point (* width (:start-x gradient)) + (* height (:start-y gradient))) + (gpt/point (* width (:end-x gradient)) + (* height (:end-y gradient)))) - shape-height-vec (gpt/point 0 (/ height 2)) + angle (gpt/angle gradient-vec + (gpt/point 1 0)) - scale-factor-y (/ (gpt/length gradient-vec) (/ height 2)) - scale-factor-x (* scale-factor-y (:width gradient)) + shape-height-vec (gpt/point 0 (/ height 2)) - scale-vec (gpt/point (* scale-factor-y (/ height 2)) - (* scale-factor-x (/ width 2))) + scale-factor-y (/ (gpt/length gradient-vec) (/ height 2)) + scale-factor-x (* scale-factor-y (:width gradient)) - tr-translate (str/fmt "translate(%s, %s)" (:x translate-vec) (:y translate-vec)) - tr-rotate (str/fmt "rotate(%s)" angle) - tr-scale (str/fmt "scale(%s, %s)" (:x scale-vec) (:y scale-vec)) - transform (str/fmt "%s %s %s" tr-translate tr-rotate tr-scale)] - [:radialGradient {:id id - :cx 0 - :cy 0 - :r 1 - :gradientUnits "userSpaceOnUse" - :gradientTransform transform} - (for [{:keys [offset color opacity]} (:stops gradient)] - [:stop {:key (str id "-stop-" offset) - :offset (or offset 0) - :stop-color color - :stop-opacity opacity}])])])) + scale-vec (gpt/point (* scale-factor-y (/ height 2)) + (* scale-factor-x (/ width 2))) + + tr-translate (str/fmt "translate(%s, %s)" (:x translate-vec) (:y translate-vec)) + tr-rotate (str/fmt "rotate(%s)" angle) + tr-scale (str/fmt "scale(%s, %s)" (:x scale-vec) (:y scale-vec)) + transform (str/fmt "%s %s %s" tr-translate tr-rotate tr-scale)] + [:radialGradient {:id id + :cx 0 + :cy 0 + :r 1 + :gradientUnits "userSpaceOnUse" + :gradientTransform transform} + (for [{:keys [offset color opacity]} (:stops gradient)] + [:stop {:key (str id "-stop-" offset) + :offset (or offset 0) + :stop-color color + :stop-opacity opacity}])]))) (mf/defc gradient {::mf/wrap-props false} diff --git a/frontend/src/app/main/ui/shapes/group.cljs b/frontend/src/app/main/ui/shapes/group.cljs index 98c36bd0a3..f9b039b6ca 100644 --- a/frontend/src/app/main/ui/shapes/group.cljs +++ b/frontend/src/app/main/ui/shapes/group.cljs @@ -14,17 +14,16 @@ [app.main.ui.shapes.attrs :as attrs] [app.common.geom.shapes :as geom])) -(def mask-id-ctx (mf/create-context nil)) - (defn group-shape [shape-wrapper] (mf/fnc group-shape {::mf/wrap-props false} [props] - (let [frame (unchecked-get props "frame") - shape (unchecked-get props "shape") - childs (unchecked-get props "childs") - expand-mask (unchecked-get props "expand-mask") + (let [frame (unchecked-get props "frame") + shape (unchecked-get props "shape") + childs (unchecked-get props "childs") + expand-mask (unchecked-get props "expand-mask") + pointer-events (unchecked-get props "pointer-events") mask (if (and (:masked-group? shape) (not expand-mask)) (first childs) nil) @@ -33,7 +32,9 @@ childs) {:keys [id x y width height]} shape transform (geom/transform-matrix shape)] - [:g + [:g.group {:pointer-events pointer-events + :mask (when (and mask (not expand-mask)) + (str/fmt "url(#%s)" (:id mask)))} (when mask [:defs [:mask {:id (:id mask) @@ -41,11 +42,10 @@ :height height} [:& shape-wrapper {:frame frame :shape mask}]]]) - [:& (mf/provider mask-id-ctx) {:value (str/fmt "url(#%s)" (:id mask))} - (for [item childs] - [:& shape-wrapper {:frame frame - :shape item - :key (:id item)}])] - ]))) + (for [item childs] + [:& shape-wrapper {:frame frame + :shape item + :key (:id item)}])]))) + diff --git a/frontend/src/app/main/ui/shapes/image.cljs b/frontend/src/app/main/ui/shapes/image.cljs index 61ef85c791..c7640bbc37 100644 --- a/frontend/src/app/main/ui/shapes/image.cljs +++ b/frontend/src/app/main/ui/shapes/image.cljs @@ -13,7 +13,6 @@ [app.config :as cfg] [app.common.geom.shapes :as geom] [app.main.ui.shapes.attrs :as attrs] - [app.main.ui.shapes.group :refer [mask-id-ctx]] [app.util.object :as obj] [app.main.ui.context :as muc] [app.main.data.fetch :as df] @@ -27,7 +26,6 @@ {:keys [id x y width height rotation metadata]} shape uri (cfg/resolve-media-path (:path metadata)) embed-resources? (mf/use-ctx muc/embed-ctx) - mask-id (mf/use-ctx mask-id-ctx) data-uri (mf/use-state (when (not embed-resources?) uri))] (mf/use-effect @@ -45,8 +43,7 @@ :transform transform :width width :height height - :preserveAspectRatio "none" - :mask mask-id}))] + :preserveAspectRatio "none"}))] (if (nil? @data-uri) [:> "rect" (obj/merge! props diff --git a/frontend/src/app/main/ui/shapes/path.cljs b/frontend/src/app/main/ui/shapes/path.cljs index 85a0e0cd28..90f485a0a1 100644 --- a/frontend/src/app/main/ui/shapes/path.cljs +++ b/frontend/src/app/main/ui/shapes/path.cljs @@ -13,7 +13,6 @@ [rumext.alpha :as mf] [app.main.ui.shapes.attrs :as attrs] [app.main.ui.shapes.custom-stroke :refer [shape-custom-stroke]] - [app.main.ui.shapes.group :refer [mask-id-ctx]] [app.common.geom.shapes :as geom] [app.util.object :as obj] [app.util.geom.path :as ugp])) @@ -27,7 +26,6 @@ background? (unchecked-get props "background?") ;; {:keys [id x y width height]} (geom/shape->rect-shape shape) {:keys [id x y width height]} (:selrect shape) - mask-id (mf/use-ctx mask-id-ctx) transform (geom/transform-matrix shape) pdata (ugp/content->path (:content shape)) props (-> (attrs/extract-style-attrs shape) @@ -35,7 +33,7 @@ #js {:transform transform :d pdata}))] (if background? - [:g {:mask mask-id} + [:g [:path {:stroke "transparent" :fill "transparent" :stroke-width "20px" @@ -45,6 +43,5 @@ :elem-name "path"}]] [:& shape-custom-stroke {:shape shape :base-props props - :mask mask-id :elem-name "path"}]))) diff --git a/frontend/src/app/main/ui/shapes/text.cljs b/frontend/src/app/main/ui/shapes/text.cljs index c420f94216..05c0afda49 100644 --- a/frontend/src/app/main/ui/shapes/text.cljs +++ b/frontend/src/app/main/ui/shapes/text.cljs @@ -12,7 +12,6 @@ [cuerdas.core :as str] [rumext.alpha :as mf] [app.main.ui.context :as muc] - [app.main.ui.shapes.group :refer [mask-id-ctx]] [app.common.data :as d] [app.common.geom.shapes :as geom] [app.common.geom.matrix :as gmt] @@ -90,7 +89,6 @@ (let [shape (unchecked-get props "shape") selected? (unchecked-get props "selected?") grow-type (:grow-type shape) - mask-id (mf/use-ctx mask-id-ctx) {:keys [id x y width height content]} shape] [:foreignObject {:x x :y y @@ -99,7 +97,6 @@ :transform (geom/transform-matrix shape) :width (if (#{:auto-width} grow-type) 10000 width) :height (if (#{:auto-height :auto-width} grow-type) 10000 height) - :mask mask-id :ref ref :pointer-events "none"} [:& text-content {:shape shape diff --git a/frontend/src/app/main/ui/workspace/shapes/group.cljs b/frontend/src/app/main/ui/workspace/shapes/group.cljs index e9d502032b..619a9e1581 100644 --- a/frontend/src/app/main/ui/workspace/shapes/group.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/group.cljs @@ -80,7 +80,8 @@ {:frame frame :shape shape :childs childs - :expand-mask is-mask-selected?}] + :expand-mask is-mask-selected? + :pointer-events (when (not is-child-selected?) "none")}] (when-not is-child-selected? [:rect.group-actions