diff --git a/common/src/app/common/path/shapes_to_path.cljc b/common/src/app/common/path/shapes_to_path.cljc index ec8294fd0..8bd1f24f6 100644 --- a/common/src/app/common/path/shapes_to_path.cljc +++ b/common/src/app/common/path/shapes_to_path.cljc @@ -11,6 +11,7 @@ [app.common.geom.point :as gpt] [app.common.geom.shapes.common :as gsc] [app.common.geom.shapes.path :as gsp] + [app.common.path.bool :as pb] [app.common.path.commands :as pc])) (def ^:const bezier-circle-c 0.551915024494) @@ -24,26 +25,31 @@ #{:rect :circle :image - :group}) + :group + :bool}) + +(def ^:const style-group-properties + [:shadow + :blur]) (def ^:const style-properties - [:fill-color - :fill-opacity - :fill-color-gradient - :fill-color-ref-file - :fill-color-ref-id - :fill-image - :stroke-color - :stroke-color-ref-file - :stroke-color-ref-id - :stroke-opacity - :stroke-style - :stroke-width - :stroke-alignment - :stroke-cap-start - :stroke-cap-end - :shadow - :blur]) + (d/concat + style-group-properties + [:fill-color + :fill-opacity + :fill-color-gradient + :fill-color-ref-file + :fill-color-ref-id + :fill-image + :stroke-color + :stroke-color-ref-file + :stroke-color-ref-id + :stroke-opacity + :stroke-style + :stroke-width + :stroke-alignment + :stroke-cap-start + :stroke-cap-end])) (defn make-corner-arc "Creates a curvle corner for border radius" @@ -142,7 +148,6 @@ (defn group-to-path [group objects] - (let [xform (comp (map #(get objects %)) (map #(-> (convert-to-path % objects)))) @@ -157,6 +162,22 @@ (merge head-data) (d/without-keys dissoc-attrs)))) +(defn bool-to-path + [shape objects] + + (let [children (->> (:shapes shape) + (map #(get objects %)) + (map #(convert-to-path % objects))) + head (first children) + head-data (select-keys head style-properties) + content (pb/content-bool (:bool-type shape) (mapv :content children))] + + (-> shape + (assoc :type :path) + (assoc :content content) + (merge head-data) + (d/without-keys dissoc-attrs)))) + (defn convert-to-path "Transforms the given shape to a path" [{:keys [type x y width height r1 r2 r3 r4 rx metadata] :as shape} objects] @@ -165,6 +186,9 @@ (= (:type shape) :group) (group-to-path shape objects) + (= (:type shape) :bool) + (bool-to-path shape objects) + (contains? allowed-transform-types type) (let [new-content (case type diff --git a/frontend/resources/styles/main/partials/sidebar-align-options.scss b/frontend/resources/styles/main/partials/sidebar-align-options.scss index 634c40861..2d3565c4a 100644 --- a/frontend/resources/styles/main/partials/sidebar-align-options.scss +++ b/frontend/resources/styles/main/partials/sidebar-align-options.scss @@ -46,5 +46,13 @@ fill: $color-gray-40; } } + + &.selected svg { + fill: $color-primary; + } + + &.selected:hover svg { + fill: $color-white; + } } } diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index cb055c688..8f4e30599 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -1098,13 +1098,9 @@ :text (rx/of (dwc/start-edition-mode id)) - :group + (:group :bool) (rx/of (dwc/select-shapes (into (d/ordered-set) [(last shapes)]))) - :bool - ;; TODO - (js/alert "TODO") - :svg-raw nil @@ -1995,3 +1991,6 @@ ;; Boolean (d/export dwb/create-bool) +(d/export dwb/group-to-bool) +(d/export dwb/bool-to-group) +(d/export dwb/change-bool-type) diff --git a/frontend/src/app/main/data/workspace/booleans.cljs b/frontend/src/app/main/data/workspace/booleans.cljs index 301fa9c2f..f1aa65474 100644 --- a/frontend/src/app/main/data/workspace/booleans.cljs +++ b/frontend/src/app/main/data/workspace/booleans.cljs @@ -45,11 +45,36 @@ (merge head-data) (gsh/update-bool-selrect shapes objects)))) +(defn group->bool + [group bool-type objects] + + (let [shapes (->> (:shapes group) + (map #(get objects %)) + (mapv #(stp/convert-to-path % objects))) + head (first shapes) + head-data (select-keys head stp/style-properties)] + + (-> group + (assoc :type :bool) + (assoc :bool-type bool-type) + (merge head-data) + (gsh/update-bool-selrect shapes objects)))) + +(defn bool->group + [shape objects] + + (let [children (->> (:shapes shape) + (mapv #(get objects %)))] + (-> shape + (assoc :type :group) + (dissoc :bool-type) + (d/without-keys stp/style-group-properties) + (gsh/update-group-selrect children)))) + (defn create-bool [bool-type] (ptk/reify ::create-bool-union ptk/WatchEvent - (watch [it state _] (let [page-id (:current-page-id state) objects (wsh/lookup-page-objects state) @@ -66,3 +91,29 @@ (cb/change-parent shape-id shapes))] (rx/of (dch/commit-changes changes) (dwc/select-shapes (d/ordered-set shape-id))))))))) + +(defn group-to-bool + [shape-id bool-type] + (ptk/reify ::group-to-bool + ptk/WatchEvent + (watch [_ state _] + (let [objects (wsh/lookup-page-objects state)] + (rx/of (dch/update-shapes [shape-id] #(group->bool % bool-type objects))))))) + +(defn bool-to-group + [shape-id] + (ptk/reify ::bool-to-group + ptk/WatchEvent + (watch [_ state _] + (let [objects (wsh/lookup-page-objects state)] + (rx/of (dch/update-shapes [shape-id] #(bool->group % objects))))))) + + +(defn change-bool-type + [shape-id bool-type] + (ptk/reify ::change-bool-type + ptk/WatchEvent + (watch [_ _ _] + (rx/of (dch/update-shapes + [shape-id] + #(assoc % :bool-type bool-type)))))) diff --git a/frontend/src/app/main/refs.cljs b/frontend/src/app/main/refs.cljs index 15e90c422..cc0197bfa 100644 --- a/frontend/src/app/main/refs.cljs +++ b/frontend/src/app/main/refs.cljs @@ -266,8 +266,10 @@ (-> (:workspace-local state) (select-keys [:modifiers :selected])) - modifiers (merge modifiers - (into #{} (map #(vector % disp-modifiers)) selected))] + modifiers + (d/deep-merge + modifiers + (into {} (map #(vector % {:modifiers disp-modifiers})) selected))] (gsh/merge-modifiers children modifiers)))] (l/derived selector st/state =))) diff --git a/frontend/src/app/main/ui/shapes/bool.cljs b/frontend/src/app/main/ui/shapes/bool.cljs index 7fdbfbcc3..c4c0fd994 100644 --- a/frontend/src/app/main/ui/shapes/bool.cljs +++ b/frontend/src/app/main/ui/shapes/bool.cljs @@ -6,6 +6,8 @@ (ns app.main.ui.shapes.bool (:require + [app.common.data :as d] + [app.common.geom.shapes :as gsh] [app.common.path.bool :as pb] [app.common.path.shapes-to-path :as stp] [app.main.ui.hooks :refer [use-equal-memo]] @@ -27,12 +29,12 @@ (mf/use-memo (mf/deps childs) (fn [] - (->> shape - :shapes - (map #(get childs %)) - (map #(stp/convert-to-path % childs)) - (mapv :content) - (pb/content-bool (:bool-type shape)))))] + (let [childs (d/mapm #(gsh/transform-shape %2) childs)] + (->> (:shapes shape) + (map #(get childs %)) + (map #(stp/convert-to-path % childs)) + (mapv :content) + (pb/content-bool (:bool-type shape))))))] [:& shape-wrapper {:shape (-> shape (assoc :type :path) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/booleans.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/booleans.cljs index 0ad6b3ddd..4f1137a60 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/booleans.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/booleans.cljs @@ -6,49 +6,67 @@ (ns app.main.ui.workspace.sidebar.options.menus.booleans (:require - [app.main.data.workspace :as dw] [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.icons :as i] + [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] - [rumext.alpha :as mf] - )) + [rumext.alpha :as mf])) (mf/defc booleans-options [] - (let [selected (mf/deref refs/selected-shapes) - disabled (and (some? selected) - (<= (count selected) 1)) + (let [selected (mf/deref refs/selected-objects) + disabled (or (empty? selected) + (and (<= (count selected) 1) + (not (contains? #{:group :bool} (:type (first selected)))))) - do-boolean-union (st/emitf (dw/create-bool :union)) - do-boolean-difference (st/emitf (dw/create-bool :difference)) - do-boolean-intersection (st/emitf (dw/create-bool :intersection)) - do-boolean-exclude (st/emitf (dw/create-bool :exclude))] + head (first selected) + is-group? (and (some? head) (= :group (:type head))) + is-bool? (and (some? head) (= :bool (:type head))) + head-bool-type (and (some? head) (:bool-type head)) + + set-bool + (fn [bool-type] + #(cond + (> (count selected) 1) + (st/emit! (dw/create-bool bool-type)) + + (and (= (count selected) 1) is-group?) + (st/emit! (dw/group-to-bool (:id head) bool-type)) + + (and (= (count selected) 1) is-bool?) + (if (= head-bool-type bool-type) + (st/emit! (dw/bool-to-group (:id head))) + (st/emit! (dw/change-bool-type (:id head) bool-type)))))] [:div.align-options [:div.align-group [:div.align-button.tooltip.tooltip-bottom {:alt (tr "workspace.shape.menu.union") - :class (when disabled "disabled") - :on-click do-boolean-union} + :class (dom/classnames :disabled disabled + :selected (= head-bool-type :union)) + :on-click (set-bool :union)} i/boolean-union] [:div.align-button.tooltip.tooltip-bottom {:alt (tr "workspace.shape.menu.difference") - :class (when disabled "disabled") - :on-click do-boolean-difference} + :class (dom/classnames :disabled disabled + :selected (= head-bool-type :difference)) + :on-click (set-bool :difference)} i/boolean-difference] [:div.align-button.tooltip.tooltip-bottom {:alt (tr "workspace.shape.menu.intersection") - :class (when disabled "disabled") - :on-click do-boolean-intersection} + :class (dom/classnames :disabled disabled + :selected (= head-bool-type :intersection)) + :on-click (set-bool :intersection)} i/boolean-intersection] [:div.align-button.tooltip.tooltip-bottom {:alt (tr "workspace.shape.menu.exclude") - :class (when disabled "disabled") - :on-click do-boolean-exclude} + :class (dom/classnames :disabled disabled + :selected (= head-bool-type :exclude)) + :on-click (set-bool :exclude)} i/boolean-exclude]]]))