diff --git a/common/src/app/common/geom/shapes/transforms.cljc b/common/src/app/common/geom/shapes/transforms.cljc index af748616b..e7cea35d3 100644 --- a/common/src/app/common/geom/shapes/transforms.cljc +++ b/common/src/app/common/geom/shapes/transforms.cljc @@ -49,6 +49,8 @@ (update :points move-points move-vec) (d/update-when :x + dx) (d/update-when :y + dy) + (cond-> (= :bool (:type shape)) + (update :bool-content gpa/move-content move-vec)) (cond-> (= :path (:type shape)) (update :content gpa/move-content move-vec))))) @@ -256,6 +258,7 @@ (let [points' (:points shape) points (gco/transform-points points' transform-mtx) + bool? (= (:type shape) :bool) path? (= (:type shape) :path) rotated? (is-rotated? points) @@ -273,6 +276,8 @@ rotation (mod (+ base-rotation modif-rotation) 360)] (-> shape + (cond-> bool? + (update :bool-content gpa/transform-content transform-mtx)) (cond-> path? (update :content gpa/transform-content transform-mtx)) (cond-> (not path?) diff --git a/frontend/src/app/main/data/viewer.cljs b/frontend/src/app/main/data/viewer.cljs index 63bd8c34b..0503dc5f1 100644 --- a/frontend/src/app/main/data/viewer.cljs +++ b/frontend/src/app/main/data/viewer.cljs @@ -252,7 +252,6 @@ (ptk/reify ::select-next-frame ptk/WatchEvent (watch [_ state _] - (prn "select-next-frame") (let [route (:route state) pparams (:path-params route) qparams (:query-params route) diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index b587e3437..f26ded73f 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -24,7 +24,7 @@ [app.config :as cfg] [app.main.data.events :as ev] [app.main.data.messages :as dm] - [app.main.data.workspace.booleans :as dwb] + [app.main.data.workspace.bool :as dwb] [app.main.data.workspace.changes :as dch] [app.main.data.workspace.common :as dwc] [app.main.data.workspace.drawing :as dwd] diff --git a/frontend/src/app/main/data/workspace/booleans.cljs b/frontend/src/app/main/data/workspace/bool.cljs similarity index 89% rename from frontend/src/app/main/data/workspace/booleans.cljs rename to frontend/src/app/main/data/workspace/bool.cljs index 4475eb61a..ab0ec4fdd 100644 --- a/frontend/src/app/main/data/workspace/booleans.cljs +++ b/frontend/src/app/main/data/workspace/bool.cljs @@ -4,7 +4,7 @@ ;; ;; Copyright (c) UXBOX Labs SL -(ns app.main.data.workspace.booleans +(ns app.main.data.workspace.bool (:require [app.common.colors :as clr] [app.common.data :as d] @@ -38,17 +38,20 @@ (and (contains? head :svg-attrs) (nil? (:fill-color head))) (assoc :fill-color clr/black)) - head-data (select-keys head stp/style-properties)] - [(-> {:id (uuid/next) - :type :bool - :bool-type bool-type - :frame-id (:frame-id head) - :parent-id (:parent-id head) - :name name - :shapes []} - (merge head-data) - (gsh/update-bool-selrect shapes objects)) - (cp/position-on-parent (:id head) objects)])) + head-data (select-keys head stp/style-properties) + + bool-shape + (-> {:id (uuid/next) + :type :bool + :bool-type bool-type + :frame-id (:frame-id head) + :parent-id (:parent-id head) + :name name + :shapes (->> shapes (mapv :id))} + (merge head-data) + (gsh/update-bool-selrect shapes objects))] + + [bool-shape (cp/position-on-parent (:id head) objects)])) (defn group->bool [group bool-type objects] diff --git a/frontend/src/app/main/data/workspace/shortcuts.cljs b/frontend/src/app/main/data/workspace/shortcuts.cljs index 70d77f3bb..d69c99ec3 100644 --- a/frontend/src/app/main/data/workspace/shortcuts.cljs +++ b/frontend/src/app/main/data/workspace/shortcuts.cljs @@ -269,19 +269,19 @@ :type "keyup" :fn #(st/emit! (dw/toggle-distances-display false))} - :boolean-union {:tooltip (ds/meta (ds/alt "U")) + :bool-union {:tooltip (ds/meta (ds/alt "U")) :command (ds/c-mod "alt+u") :fn #(st/emit! (dw/create-bool :union))} - :boolean-difference {:tooltip (ds/meta (ds/alt "D")) + :bool-difference {:tooltip (ds/meta (ds/alt "D")) :command (ds/c-mod "alt+d") :fn #(st/emit! (dw/create-bool :difference))} - :boolean-intersection {:tooltip (ds/meta (ds/alt "I")) + :bool-intersection {:tooltip (ds/meta (ds/alt "I")) :command (ds/c-mod "alt+i") :fn #(st/emit! (dw/create-bool :intersection))} - :boolean-exclude {:tooltip (ds/meta (ds/alt "E")) + :bool-exclude {:tooltip (ds/meta (ds/alt "E")) :command (ds/c-mod "alt+e") :fn #(st/emit! (dw/create-bool :exclude))} diff --git a/frontend/src/app/main/exports.cljs b/frontend/src/app/main/exports.cljs index 85d3fd954..04da9d9d9 100644 --- a/frontend/src/app/main/exports.cljs +++ b/frontend/src/app/main/exports.cljs @@ -237,8 +237,7 @@ [:& wrapper {:shape frame :view-box vbox}]])) (mf/defc component-svg - {::mf/wrap [mf/memo - #(mf/deferred % ts/idle-then-raf)]} + {::mf/wrap [mf/memo #(mf/deferred % ts/idle-then-raf)]} [{:keys [objects group zoom] :or {zoom 1} :as props}] (let [modifier (-> (gpt/point (:x group) (:y group)) (gpt/negate) @@ -249,17 +248,21 @@ include-metadata? (mf/use-ctx use/include-metadata-ctx) modifier-ids (concat [group-id] (cp/get-children group-id objects)) + update-fn #(assoc-in %1 [%2 :modifiers :displacement] modifier) - objects (reduce update-fn objects modifier-ids) - group (assoc-in group [:modifiers :displacement] modifier) + modifiers (reduce update-fn {} modifier-ids) + objects (gsh/merge-modifiers objects modifiers) + + group (get objects group-id) width (* (:width group) zoom) height (* (:height group) zoom) vbox (str "0 0 " (:width group 0) " " (:height group 0)) - wrapper (mf/use-memo - (mf/deps objects) - #(group-wrapper-factory objects))] + group-wrapper + (mf/use-memo + (mf/deps objects) + #(group-wrapper-factory objects))] [:svg {:view-box vbox :width width @@ -269,7 +272,7 @@ :xmlnsXlink "http://www.w3.org/1999/xlink" :xmlns:penpot (when include-metadata? "https://penpot.app/xmlns")} [:> shape-container {:shape group} - [:& wrapper {:shape group :view-box vbox}]]])) + [:& group-wrapper {:shape group :view-box vbox}]]])) (mf/defc component-symbol [{:keys [id data] :as props}] diff --git a/frontend/src/app/main/refs.cljs b/frontend/src/app/main/refs.cljs index 7eff78ef5..de3b45362 100644 --- a/frontend/src/app/main/refs.cljs +++ b/frontend/src/app/main/refs.cljs @@ -8,6 +8,7 @@ "A collection of derived refs." (:require [app.common.data :as d] + [app.common.geom.shapes :as gsh] [app.common.pages :as cp] [app.common.path.commands :as upc] [app.main.data.workspace.state-helpers :as wsh] @@ -246,11 +247,13 @@ (update shape :content upc/apply-content-modifiers content-modifiers) shape)))) -(defn select-children [id] +(defn select-bool-children [id] (let [selector (fn [state] - (let [objects (wsh/lookup-page-objects state)] + (let [objects (wsh/lookup-page-objects state) + modifiers (:workspace-modifiers state)] (as-> (cp/select-children id objects) $ + (gsh/merge-modifiers $ modifiers) (d/mapm (set-content-modifiers state) $))))] (l/derived selector st/state =))) diff --git a/frontend/src/app/main/ui/icons.cljs b/frontend/src/app/main/ui/icons.cljs index 2a2c7b5d8..0230fa055 100644 --- a/frontend/src/app/main/ui/icons.cljs +++ b/frontend/src/app/main/ui/icons.cljs @@ -29,11 +29,11 @@ (def auto-fix (icon-xref :auto-fix)) (def auto-height (icon-xref :auto-height)) (def auto-width (icon-xref :auto-width)) -(def boolean-difference (icon-xref :boolean-difference)) -(def boolean-exclude (icon-xref :boolean-exclude)) -(def boolean-flatten (icon-xref :boolean-flatten)) -(def boolean-intersection (icon-xref :boolean-intersection)) -(def boolean-union (icon-xref :boolean-union)) +(def bool-difference (icon-xref :boolean-difference)) +(def bool-exclude (icon-xref :boolean-exclude)) +(def bool-flatten (icon-xref :boolean-flatten)) +(def bool-intersection (icon-xref :boolean-intersection)) +(def bool-union (icon-xref :boolean-union)) (def box (icon-xref :box)) (def chain (icon-xref :chain)) (def chat (icon-xref :chat)) diff --git a/frontend/src/app/main/ui/shapes/bool.cljs b/frontend/src/app/main/ui/shapes/bool.cljs index 04e4c7ca0..9d3d41851 100644 --- a/frontend/src/app/main/ui/shapes/bool.cljs +++ b/frontend/src/app/main/ui/shapes/bool.cljs @@ -6,6 +6,7 @@ (ns app.main.ui.shapes.bool (:require + [app.common.data :as d] [app.common.geom.shapes :as gsh] [app.main.ui.hooks :refer [use-equal-memo]] [app.main.ui.shapes.export :as use] @@ -26,11 +27,19 @@ bool-content (mf/use-memo (mf/deps shape childs) - #(or (:bool-content shape) - (gsh/calc-bool-content shape childs)))] + (fn [] + (cond + (some? (:bool-content shape)) + (:bool-content shape) + + (some? childs) + (->> childs + (d/mapm #(gsh/transform-shape %2)) + (gsh/calc-bool-content shape)))))] [:* - [:& path-shape {:shape (assoc shape :content bool-content)}] + (when (some? bool-content) + [:& path-shape {:shape (assoc shape :content bool-content)}]) (when include-metadata? [:> "penpot:bool" {} 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 f0ee419c2..a93fbbdea 100644 --- a/frontend/src/app/main/ui/viewer/handoff/selection_feedback.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/selection_feedback.cljs @@ -57,8 +57,7 @@ (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) - (map #(gsh/translate-to-frame % frame))) + selected-shapes (->> (resolve-shapes objects selected)) selrect (gsh/selection-rect selected-shapes) bounds (frame->bounds frame)] diff --git a/frontend/src/app/main/ui/workspace/context_menu.cljs b/frontend/src/app/main/ui/workspace/context_menu.cljs index 31f6689b0..926642099 100644 --- a/frontend/src/app/main/ui/workspace/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/context_menu.cljs @@ -250,16 +250,16 @@ (or multiple? (and single? (or is-group? is-bool?)))) [:& menu-entry {:title (tr "workspace.shape.menu.path")} [:& menu-entry {:title (tr "workspace.shape.menu.union") - :shortcut (sc/get-tooltip :boolean-union) + :shortcut (sc/get-tooltip :bool-union) :on-click (set-bool :union)}] [:& menu-entry {:title (tr "workspace.shape.menu.difference") - :shortcut (sc/get-tooltip :boolean-difference) + :shortcut (sc/get-tooltip :bool-difference) :on-click (set-bool :difference)}] [:& menu-entry {:title (tr "workspace.shape.menu.intersection") - :shortcut (sc/get-tooltip :boolean-intersection) + :shortcut (sc/get-tooltip :bool-intersection) :on-click (set-bool :intersection)}] [:& menu-entry {:title (tr "workspace.shape.menu.exclude") - :shortcut (sc/get-tooltip :boolean-exclude) + :shortcut (sc/get-tooltip :bool-exclude) :on-click (set-bool :exclude)}] (when (and single? is-bool? (not disable-flatten?)) diff --git a/frontend/src/app/main/ui/workspace/shapes/bool.cljs b/frontend/src/app/main/ui/workspace/shapes/bool.cljs index 416aed505..96234a0dd 100644 --- a/frontend/src/app/main/ui/workspace/shapes/bool.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/bool.cljs @@ -30,15 +30,23 @@ {::mf/wrap [#(mf/memo' % (mf/check-props ["shape"]))] ::mf/wrap-props false} [props] - (let [shape (unchecked-get props "shape") - childs-ref (mf/use-memo - (mf/deps (:id shape)) - #(refs/select-children (:id shape))) + (let [shape (unchecked-get props "shape") + child-sel-ref (mf/use-memo + (mf/deps (:id shape)) + #(refs/is-child-selected? (:id shape))) - childs (mf/deref childs-ref)] + childs-ref (mf/use-memo + (mf/deps (:id shape)) + #(refs/select-bool-children (:id shape))) + + child-sel? (mf/deref child-sel-ref) + childs (mf/deref childs-ref) + + shape (cond-> shape + child-sel? + (dissoc :bool-content))] [:> shape-container {:shape shape} - [:& shape-component - {:shape shape - :childs childs}]])))) + [:& shape-component {:shape shape + :childs childs}]])))) diff --git a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs index 32709a98e..e18c0ae06 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs @@ -42,10 +42,10 @@ i/mask i/folder)) :bool (case (:bool-type shape) - :difference i/boolean-difference - :exclude i/boolean-exclude - :intersection i/boolean-intersection - #_:default i/boolean-union) + :difference i/bool-difference + :exclude i/bool-exclude + :intersection i/bool-intersection + #_:default i/bool-union) :svg-raw i/file-svg nil)) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options.cljs b/frontend/src/app/main/ui/workspace/sidebar/options.cljs index 9e5523345..0d9f45264 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options.cljs @@ -12,7 +12,7 @@ [app.main.ui.components.tab-container :refer [tab-container tab-element]] [app.main.ui.context :as ctx] [app.main.ui.workspace.sidebar.options.menus.align :refer [align-options]] - [app.main.ui.workspace.sidebar.options.menus.booleans :refer [booleans-options]] + [app.main.ui.workspace.sidebar.options.menus.bool :refer [bool-options]] [app.main.ui.workspace.sidebar.options.menus.exports :refer [exports-menu]] [app.main.ui.workspace.sidebar.options.menus.interactions :refer [interactions-menu]] [app.main.ui.workspace.sidebar.options.page :as page] @@ -63,7 +63,7 @@ :title (tr "workspace.options.design")} [:div.element-options [:& align-options] - [:& booleans-options] + [:& bool-options] (case (count selected) 0 [:& page/options] 1 [:& shape-options {:shape (first shapes) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/booleans.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/bool.cljs similarity index 90% rename from frontend/src/app/main/ui/workspace/sidebar/options/menus/booleans.cljs rename to frontend/src/app/main/ui/workspace/sidebar/options/menus/bool.cljs index 187c884fd..3b76be0c2 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/booleans.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/bool.cljs @@ -4,7 +4,7 @@ ;; ;; Copyright (c) UXBOX Labs SL -(ns app.main.ui.workspace.sidebar.options.menus.booleans +(ns app.main.ui.workspace.sidebar.options.menus.bool (:require [app.main.data.workspace :as dw] [app.main.data.workspace.shortcuts :as sc] @@ -15,7 +15,7 @@ [app.util.i18n :as i18n :refer [tr]] [rumext.alpha :as mf])) -(mf/defc booleans-options +(mf/defc bool-options [] (let [selected (mf/deref refs/selected-objects) selected-with-children (mf/deref refs/selected-shapes-with-children) @@ -52,37 +52,37 @@ [:div.align-options [:div.align-group [:div.align-button.tooltip.tooltip-bottom - {:alt (str (tr "workspace.shape.menu.union") " (" (sc/get-tooltip :boolean-union) ")") + {:alt (str (tr "workspace.shape.menu.union") " (" (sc/get-tooltip :bool-union) ")") :class (dom/classnames :disabled disabled-bool-btns :selected (= head-bool-type :union)) :on-click (set-bool :union)} - i/boolean-union] + i/bool-union] [:div.align-button.tooltip.tooltip-bottom - {:alt (str (tr "workspace.shape.menu.difference") " (" (sc/get-tooltip :boolean-difference) ")") + {:alt (str (tr "workspace.shape.menu.difference") " (" (sc/get-tooltip :bool-difference) ")") :class (dom/classnames :disabled disabled-bool-btns :selected (= head-bool-type :difference)) :on-click (set-bool :difference)} - i/boolean-difference] + i/bool-difference] [:div.align-button.tooltip.tooltip-bottom - {:alt (str (tr "workspace.shape.menu.intersection") " (" (sc/get-tooltip :boolean-intersection) ")") + {:alt (str (tr "workspace.shape.menu.intersection") " (" (sc/get-tooltip :bool-intersection) ")") :class (dom/classnames :disabled disabled-bool-btns :selected (= head-bool-type :intersection)) :on-click (set-bool :intersection)} - i/boolean-intersection] + i/bool-intersection] [:div.align-button.tooltip.tooltip-bottom - {:alt (str (tr "workspace.shape.menu.exclude") " (" (sc/get-tooltip :boolean-exclude) ")") + {:alt (str (tr "workspace.shape.menu.exclude") " (" (sc/get-tooltip :bool-exclude) ")") :class (dom/classnames :disabled disabled-bool-btns :selected (= head-bool-type :exclude)) :on-click (set-bool :exclude)} - i/boolean-exclude]] + i/bool-exclude]] [:div.align-group [:div.align-button.tooltip.tooltip-bottom {:alt (tr "workspace.shape.menu.flatten") :class (dom/classnames :disabled disabled-flatten) :on-click (st/emitf (dw/convert-selected-to-path))} - i/boolean-flatten]]])) + i/bool-flatten]]]))