mirror of
https://github.com/penpot/penpot.git
synced 2025-01-24 23:49:45 -05:00
✨ Handling groups inside bool shapes
This commit is contained in:
parent
6fd35ae5d9
commit
c56f024a86
7 changed files with 155 additions and 51 deletions
|
@ -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
|
||||
|
|
|
@ -46,5 +46,13 @@
|
|||
fill: $color-gray-40;
|
||||
}
|
||||
}
|
||||
|
||||
&.selected svg {
|
||||
fill: $color-primary;
|
||||
}
|
||||
|
||||
&.selected:hover svg {
|
||||
fill: $color-white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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))))))
|
||||
|
|
|
@ -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 =)))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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]]]))
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue