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