mirror of
https://github.com/penpot/penpot.git
synced 2025-06-06 22:31:38 +02:00
🎉 Allow masked groups
This commit is contained in:
parent
ad66955a54
commit
ee89b2e7f4
14 changed files with 207 additions and 33 deletions
|
@ -245,7 +245,8 @@
|
||||||
:internal.shape/height
|
:internal.shape/height
|
||||||
:internal.shape/interactions
|
:internal.shape/interactions
|
||||||
:internal.shape/selrect
|
:internal.shape/selrect
|
||||||
:internal.shape/points]))
|
:internal.shape/points
|
||||||
|
:internal.shape/masked-group?]))
|
||||||
|
|
||||||
(def component-sync-attrs {:fill-color :fill-group
|
(def component-sync-attrs {:fill-color :fill-group
|
||||||
:fill-color-ref-file :fill-group
|
:fill-color-ref-file :fill-group
|
||||||
|
@ -270,7 +271,8 @@
|
||||||
:height :size-group
|
:height :size-group
|
||||||
:proportion :size-group
|
:proportion :size-group
|
||||||
:rx :radius-group
|
:rx :radius-group
|
||||||
:ry :radius-group})
|
:ry :radius-group
|
||||||
|
:masked-group? :mask-group})
|
||||||
|
|
||||||
(s/def ::minimal-shape
|
(s/def ::minimal-shape
|
||||||
(s/keys :req-un [::type ::name]
|
(s/keys :req-un [::type ::name]
|
||||||
|
|
3
frontend/resources/images/icons/mask.svg
Normal file
3
frontend/resources/images/icons/mask.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M452 250c0 150-200 250-200 250S52 400 52 250V75L252 0l200 75zM259 388c43 0 80-27 93-63H165c14 36 50 63 94 63zM102 200c9-29 34-50 63-50s53 21 62 50zm238-50c-29 0-54 21-63 50h125c-9-29-33-50-62-50z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 320 B |
|
@ -144,6 +144,47 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.element-list li.masked {
|
||||||
|
.element-children {
|
||||||
|
li:first-child {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "^";
|
||||||
|
font-size: $fs18;
|
||||||
|
font-family: opensans;
|
||||||
|
font-weight: lighter;
|
||||||
|
position: absolute;
|
||||||
|
top: -5px;
|
||||||
|
left: -5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
li:last-child {
|
||||||
|
border-left: none;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: " ";
|
||||||
|
border-left: 1px solid $color-gray-40;
|
||||||
|
height: 1rem;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: " ";
|
||||||
|
border-bottom: 1px solid $color-gray-40;
|
||||||
|
width: 0.3rem;
|
||||||
|
position: absolute;
|
||||||
|
top: 1rem;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.element-icon {
|
.element-icon {
|
||||||
svg {
|
svg {
|
||||||
fill: $color-gray-30;
|
fill: $color-gray-30;
|
||||||
|
|
|
@ -158,7 +158,7 @@ $width-settings-bar: 16rem;
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
border-left: 9px solid $color-gray-50;
|
border-left: 9px solid $color-gray-50;
|
||||||
margin: 0;
|
margin: 0 0 0 0.4rem;
|
||||||
|
|
||||||
li {
|
li {
|
||||||
border-left: 1px solid $color-gray-40;
|
border-left: 1px solid $color-gray-40;
|
||||||
|
|
|
@ -1369,6 +1369,69 @@
|
||||||
(dws/prepare-remove-group page-id group objects)]
|
(dws/prepare-remove-group page-id group objects)]
|
||||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}))))))))
|
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}))))))))
|
||||||
|
|
||||||
|
(def mask-group
|
||||||
|
(ptk/reify ::mask-group
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ state stream]
|
||||||
|
(let [page-id (:current-page-id state)
|
||||||
|
objects (dwc/lookup-page-objects state page-id)
|
||||||
|
selected (get-in state [:workspace-local :selected])
|
||||||
|
shapes (dws/shapes-for-grouping objects selected)]
|
||||||
|
(when-not (empty? shapes)
|
||||||
|
(let [;; If the selected shape is a group, we can use it. If not,
|
||||||
|
;; create a new group and set it as masked.
|
||||||
|
[group rchanges uchanges]
|
||||||
|
(if (and (= (count shapes) 1)
|
||||||
|
(= (:type (first shapes)) :group))
|
||||||
|
[(first shapes) [] []]
|
||||||
|
(dws/prepare-create-group page-id shapes "Group-" true))
|
||||||
|
|
||||||
|
rchanges (conj rchanges
|
||||||
|
{:type :mod-obj
|
||||||
|
:page-id page-id
|
||||||
|
:id (:id group)
|
||||||
|
:operations [{:type :set
|
||||||
|
:attr :masked-group?
|
||||||
|
:val true}]})
|
||||||
|
|
||||||
|
uchanges (conj rchanges
|
||||||
|
{:type :mod-obj
|
||||||
|
:page-id page-id
|
||||||
|
:id (:id group)
|
||||||
|
:operations [{:type :set
|
||||||
|
:attr :masked-group?
|
||||||
|
:val nil}]})]
|
||||||
|
|
||||||
|
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})
|
||||||
|
(dws/select-shapes (d/ordered-set (:id group))))))))))
|
||||||
|
|
||||||
|
(def unmask-group
|
||||||
|
(ptk/reify ::unmask-group
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ state stream]
|
||||||
|
(let [page-id (:current-page-id state)
|
||||||
|
objects (dwc/lookup-page-objects state page-id)
|
||||||
|
selected (get-in state [:workspace-local :selected])]
|
||||||
|
(when (= (count selected) 1)
|
||||||
|
(let [group (get objects (first selected))
|
||||||
|
|
||||||
|
rchanges [{:type :mod-obj
|
||||||
|
:page-id page-id
|
||||||
|
:id (:id group)
|
||||||
|
:operations [{:type :set
|
||||||
|
:attr :masked-group?
|
||||||
|
:val nil}]}]
|
||||||
|
|
||||||
|
uchanges [{:type :mod-obj
|
||||||
|
:page-id page-id
|
||||||
|
:id (:id group)
|
||||||
|
:operations [{:type :set
|
||||||
|
:attr :masked-group?
|
||||||
|
:val (:masked-group? group)}]}]]
|
||||||
|
|
||||||
|
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})
|
||||||
|
(dws/select-shapes (d/ordered-set (:id group))))))))))
|
||||||
|
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; Interactions
|
;; Interactions
|
||||||
|
@ -1514,6 +1577,7 @@
|
||||||
"+" #(st/emit! (increase-zoom nil))
|
"+" #(st/emit! (increase-zoom nil))
|
||||||
"-" #(st/emit! (decrease-zoom nil))
|
"-" #(st/emit! (decrease-zoom nil))
|
||||||
"ctrl+g" #(st/emit! group-selected)
|
"ctrl+g" #(st/emit! group-selected)
|
||||||
|
"ctrl+shift+m" #(st/emit! mask-group)
|
||||||
"ctrl+k" #(st/emit! dwl/add-component)
|
"ctrl+k" #(st/emit! dwl/add-component)
|
||||||
"shift+g" #(st/emit! ungroup-selected)
|
"shift+g" #(st/emit! ungroup-selected)
|
||||||
"shift+0" #(st/emit! reset-zoom)
|
"shift+0" #(st/emit! reset-zoom)
|
||||||
|
|
|
@ -69,6 +69,7 @@
|
||||||
(def logo-icon (icon-xref :uxbox-logo-icon))
|
(def logo-icon (icon-xref :uxbox-logo-icon))
|
||||||
(def lowercase (icon-xref :lowercase))
|
(def lowercase (icon-xref :lowercase))
|
||||||
(def mail (icon-xref :mail))
|
(def mail (icon-xref :mail))
|
||||||
|
(def mask (icon-xref :mask))
|
||||||
(def minus (icon-xref :minus))
|
(def minus (icon-xref :minus))
|
||||||
(def move (icon-xref :move))
|
(def move (icon-xref :move))
|
||||||
(def msg-error (icon-xref :msg-error))
|
(def msg-error (icon-xref :msg-error))
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
[rumext.alpha :as mf]
|
[rumext.alpha :as mf]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.common.geom.shapes :as geom]
|
[app.common.geom.shapes :as geom]
|
||||||
|
[app.main.ui.shapes.group :refer [mask-id-ctx]]
|
||||||
[app.util.object :as obj]))
|
[app.util.object :as obj]))
|
||||||
|
|
||||||
; The SVG standard does not implement yet the 'stroke-alignment'
|
; The SVG standard does not implement yet the 'stroke-alignment'
|
||||||
|
@ -23,13 +24,16 @@
|
||||||
base-props (unchecked-get props "base-props")
|
base-props (unchecked-get props "base-props")
|
||||||
elem-name (unchecked-get props "elem-name")
|
elem-name (unchecked-get props "elem-name")
|
||||||
{:keys [x y width height]} (geom/shape->rect-shape shape)
|
{:keys [x y width height]} (geom/shape->rect-shape shape)
|
||||||
|
mask-id (mf/use-ctx mask-id-ctx)
|
||||||
stroke-id (mf/use-var (uuid/next))
|
stroke-id (mf/use-var (uuid/next))
|
||||||
stroke-style (:stroke-style shape :none)
|
stroke-style (:stroke-style shape :none)
|
||||||
stroke-position (:stroke-alignment shape :center)]
|
stroke-position (:stroke-alignment shape :center)]
|
||||||
(cond
|
(cond
|
||||||
;; Center alignment (or no stroke): the default in SVG
|
;; Center alignment (or no stroke): the default in SVG
|
||||||
(or (= stroke-style :none) (= stroke-position :center))
|
(or (= stroke-style :none) (= stroke-position :center))
|
||||||
[:> elem-name base-props]
|
[:> elem-name (cond-> (obj/merge! #js {} base-props)
|
||||||
|
(some? mask-id)
|
||||||
|
(obj/merge! #js {:mask mask-id}))]
|
||||||
|
|
||||||
;; Inner alignment: display the shape with double width stroke,
|
;; Inner alignment: display the shape with double width stroke,
|
||||||
;; and clip the result with the original shape without stroke.
|
;; and clip the result with the original shape without stroke.
|
||||||
|
@ -49,10 +53,15 @@
|
||||||
shape-props (-> (obj/merge! #js {} base-props)
|
shape-props (-> (obj/merge! #js {} base-props)
|
||||||
(obj/merge! #js {:strokeWidth (* stroke-width 2)
|
(obj/merge! #js {:strokeWidth (* stroke-width 2)
|
||||||
:clipPath (str "url('#" clip-id "')")}))]
|
:clipPath (str "url('#" clip-id "')")}))]
|
||||||
[:*
|
(if (nil? mask-id)
|
||||||
[:> "clipPath" #js {:id clip-id}
|
[:*
|
||||||
[:> elem-name clip-props]]
|
[:> "clipPath" #js {:id clip-id}
|
||||||
[:> elem-name shape-props]])
|
[:> 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]]))
|
||||||
|
|
||||||
;; Outer alingmnent: display the shape in two layers. One
|
;; Outer alingmnent: display the shape in two layers. One
|
||||||
;; without stroke (only fill), and another one only with stroke
|
;; without stroke (only fill), and another one only with stroke
|
||||||
|
@ -61,7 +70,7 @@
|
||||||
;; without stroke
|
;; without stroke
|
||||||
|
|
||||||
(= stroke-position :outer)
|
(= stroke-position :outer)
|
||||||
(let [mask-id (str "mask-" @stroke-id)
|
(let [stroke-mask-id (str "mask-" @stroke-id)
|
||||||
stroke-width (.-strokeWidth ^js base-props)
|
stroke-width (.-strokeWidth ^js base-props)
|
||||||
mask-props1 (-> (obj/merge! #js {} base-props)
|
mask-props1 (-> (obj/merge! #js {} base-props)
|
||||||
(obj/merge! #js {:stroke "white"
|
(obj/merge! #js {:stroke "white"
|
||||||
|
@ -89,11 +98,18 @@
|
||||||
(obj/merge! #js {:strokeWidth (* stroke-width 2)
|
(obj/merge! #js {:strokeWidth (* stroke-width 2)
|
||||||
:fill "none"
|
:fill "none"
|
||||||
:fillOpacity 0
|
:fillOpacity 0
|
||||||
:mask (str "url('#" mask-id "')")}))]
|
:mask (str "url('#" stroke-mask-id "')")}))]
|
||||||
[:*
|
(if (nil? mask-id)
|
||||||
[:mask {:id mask-id}
|
[:*
|
||||||
[:> elem-name mask-props1]
|
[:mask {:id mask-id}
|
||||||
[:> elem-name mask-props2]]
|
[:> elem-name mask-props1]
|
||||||
[:> elem-name shape-props1]
|
[:> elem-name mask-props2]]
|
||||||
[:> elem-name shape-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]])))))
|
||||||
|
|
||||||
|
|
|
@ -10,10 +10,13 @@
|
||||||
(ns app.main.ui.shapes.group
|
(ns app.main.ui.shapes.group
|
||||||
(:require
|
(:require
|
||||||
[rumext.alpha :as mf]
|
[rumext.alpha :as mf]
|
||||||
|
[cuerdas.core :as str]
|
||||||
[app.main.ui.shapes.attrs :as attrs]
|
[app.main.ui.shapes.attrs :as attrs]
|
||||||
[app.util.debug :refer [debug?]]
|
[app.util.debug :refer [debug?]]
|
||||||
[app.common.geom.shapes :as geom]))
|
[app.common.geom.shapes :as geom]))
|
||||||
|
|
||||||
|
(def mask-id-ctx (mf/create-context nil))
|
||||||
|
|
||||||
(defn group-shape
|
(defn group-shape
|
||||||
[shape-wrapper]
|
[shape-wrapper]
|
||||||
(mf/fnc group-shape
|
(mf/fnc group-shape
|
||||||
|
@ -22,14 +25,26 @@
|
||||||
(let [frame (unchecked-get props "frame")
|
(let [frame (unchecked-get props "frame")
|
||||||
shape (unchecked-get props "shape")
|
shape (unchecked-get props "shape")
|
||||||
childs (unchecked-get props "childs")
|
childs (unchecked-get props "childs")
|
||||||
|
mask (if (:masked-group? shape)
|
||||||
|
(first childs)
|
||||||
|
nil)
|
||||||
|
childs (if (:masked-group? shape)
|
||||||
|
(rest childs)
|
||||||
|
childs)
|
||||||
is-child-selected? (unchecked-get props "is-child-selected?")
|
is-child-selected? (unchecked-get props "is-child-selected?")
|
||||||
{:keys [id x y width height]} shape
|
{:keys [id x y width height]} shape
|
||||||
transform (geom/transform-matrix shape)]
|
transform (geom/transform-matrix shape)]
|
||||||
[:g
|
[:g
|
||||||
(for [item childs]
|
(when mask
|
||||||
[:& shape-wrapper {:frame frame
|
[:defs
|
||||||
:shape item
|
[:mask {:id (:id mask)}
|
||||||
:key (:id item)}])
|
[:& 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)}])]
|
||||||
(when (not is-child-selected?)
|
(when (not is-child-selected?)
|
||||||
[:rect {:transform transform
|
[:rect {:transform transform
|
||||||
:x x
|
:x x
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
[rumext.alpha :as mf]
|
[rumext.alpha :as mf]
|
||||||
[app.common.geom.shapes :as geom]
|
[app.common.geom.shapes :as geom]
|
||||||
[app.main.ui.shapes.attrs :as attrs]
|
[app.main.ui.shapes.attrs :as attrs]
|
||||||
|
[app.main.ui.shapes.group :refer [mask-id-ctx]]
|
||||||
[app.util.object :as obj]))
|
[app.util.object :as obj]))
|
||||||
|
|
||||||
(mf/defc icon-shape
|
(mf/defc icon-shape
|
||||||
|
@ -20,6 +21,7 @@
|
||||||
(let [shape (unchecked-get props "shape")
|
(let [shape (unchecked-get props "shape")
|
||||||
{:keys [id x y width height metadata rotation content]} shape
|
{:keys [id x y width height metadata rotation content]} shape
|
||||||
|
|
||||||
|
mask-id (mf/use-ctx mask-id-ctx)
|
||||||
transform (geom/transform-matrix shape)
|
transform (geom/transform-matrix shape)
|
||||||
vbox (apply str (interpose " " (:view-box metadata)))
|
vbox (apply str (interpose " " (:view-box metadata)))
|
||||||
|
|
||||||
|
@ -33,6 +35,7 @@
|
||||||
:height height
|
:height height
|
||||||
:viewBox vbox
|
:viewBox vbox
|
||||||
:preserveAspectRatio "none"
|
:preserveAspectRatio "none"
|
||||||
|
:mask mask-id
|
||||||
:dangerouslySetInnerHTML #js {:__html content}}))]
|
:dangerouslySetInnerHTML #js {:__html content}}))]
|
||||||
[:g {:transform transform}
|
[:g {:transform transform}
|
||||||
[:> "svg" props]]))
|
[:> "svg" props]]))
|
||||||
|
@ -41,7 +44,9 @@
|
||||||
[{:keys [shape] :as props}]
|
[{:keys [shape] :as props}]
|
||||||
(let [{:keys [content id metadata]} shape
|
(let [{:keys [content id metadata]} shape
|
||||||
view-box (apply str (interpose " " (:view-box metadata)))
|
view-box (apply str (interpose " " (:view-box metadata)))
|
||||||
|
mask-id (mf/use-ctx mask-id-ctx)
|
||||||
props {:viewBox view-box
|
props {:viewBox view-box
|
||||||
:id (str "shape-" id)
|
:id (str "shape-" id)
|
||||||
|
:mask mask-id
|
||||||
:dangerouslySetInnerHTML #js {:__html content}}]
|
:dangerouslySetInnerHTML #js {:__html content}}]
|
||||||
[:& "svg" props]))
|
[:& "svg" props]))
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
[app.config :as cfg]
|
[app.config :as cfg]
|
||||||
[app.common.geom.shapes :as geom]
|
[app.common.geom.shapes :as geom]
|
||||||
[app.main.ui.shapes.attrs :as attrs]
|
[app.main.ui.shapes.attrs :as attrs]
|
||||||
|
[app.main.ui.shapes.group :refer [mask-id-ctx]]
|
||||||
[app.util.object :as obj]
|
[app.util.object :as obj]
|
||||||
[app.main.ui.context :as muc]
|
[app.main.ui.context :as muc]
|
||||||
[app.main.data.fetch :as df]
|
[app.main.data.fetch :as df]
|
||||||
|
@ -26,6 +27,7 @@
|
||||||
{:keys [id x y width height rotation metadata]} shape
|
{:keys [id x y width height rotation metadata]} shape
|
||||||
uri (cfg/resolve-media-path (:path metadata))
|
uri (cfg/resolve-media-path (:path metadata))
|
||||||
embed-resources? (mf/use-ctx muc/embed-ctx)
|
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))]
|
data-uri (mf/use-state (when (not embed-resources?) uri))]
|
||||||
|
|
||||||
(mf/use-effect
|
(mf/use-effect
|
||||||
|
@ -44,7 +46,8 @@
|
||||||
:id (str "shape-" id)
|
:id (str "shape-" id)
|
||||||
:width width
|
:width width
|
||||||
:height height
|
:height height
|
||||||
:preserveAspectRatio "none"}))]
|
:preserveAspectRatio "none"
|
||||||
|
:mask mask-id}))]
|
||||||
(if (nil? @data-uri)
|
(if (nil? @data-uri)
|
||||||
[:> "rect" (obj/merge!
|
[:> "rect" (obj/merge!
|
||||||
props
|
props
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
[rumext.alpha :as mf]
|
[rumext.alpha :as mf]
|
||||||
[app.main.ui.shapes.attrs :as attrs]
|
[app.main.ui.shapes.attrs :as attrs]
|
||||||
[app.main.ui.shapes.custom-stroke :refer [shape-custom-stroke]]
|
[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.common.geom.shapes :as geom]
|
||||||
[app.util.object :as obj]))
|
[app.util.object :as obj]))
|
||||||
|
|
||||||
|
@ -45,6 +46,7 @@
|
||||||
(let [shape (unchecked-get props "shape")
|
(let [shape (unchecked-get props "shape")
|
||||||
background? (unchecked-get props "background?")
|
background? (unchecked-get props "background?")
|
||||||
{:keys [id x y width height]} (geom/shape->rect-shape shape)
|
{:keys [id x y width height]} (geom/shape->rect-shape shape)
|
||||||
|
mask-id (mf/use-ctx mask-id-ctx)
|
||||||
transform (geom/transform-matrix shape)
|
transform (geom/transform-matrix shape)
|
||||||
pdata (render-path shape)
|
pdata (render-path shape)
|
||||||
props (-> (attrs/extract-style-attrs shape)
|
props (-> (attrs/extract-style-attrs shape)
|
||||||
|
@ -53,7 +55,7 @@
|
||||||
:id (str "shape-" id)
|
:id (str "shape-" id)
|
||||||
:d pdata}))]
|
:d pdata}))]
|
||||||
(if background?
|
(if background?
|
||||||
[:g
|
[:g {:mask mask-id}
|
||||||
[:path {:stroke "transparent"
|
[:path {:stroke "transparent"
|
||||||
:fill "transparent"
|
:fill "transparent"
|
||||||
:stroke-width "20px"
|
:stroke-width "20px"
|
||||||
|
@ -63,5 +65,6 @@
|
||||||
:elem-name "path"}]]
|
:elem-name "path"}]]
|
||||||
[:& shape-custom-stroke {:shape shape
|
[:& shape-custom-stroke {:shape shape
|
||||||
:base-props props
|
:base-props props
|
||||||
|
:mask mask-id
|
||||||
:elem-name "path"}])))
|
:elem-name "path"}])))
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
[app.main.data.fetch :as df]
|
[app.main.data.fetch :as df]
|
||||||
[app.main.fonts :as fonts]
|
[app.main.fonts :as fonts]
|
||||||
[app.main.ui.context :as muc]
|
[app.main.ui.context :as muc]
|
||||||
|
[app.main.ui.shapes.group :refer [mask-id-ctx]]
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.geom.shapes :as geom]
|
[app.common.geom.shapes :as geom]
|
||||||
[app.common.geom.matrix :as gmt]
|
[app.common.geom.matrix :as gmt]
|
||||||
|
@ -224,6 +225,7 @@
|
||||||
[props]
|
[props]
|
||||||
(let [shape (unchecked-get props "shape")
|
(let [shape (unchecked-get props "shape")
|
||||||
selected? (unchecked-get props "selected?")
|
selected? (unchecked-get props "selected?")
|
||||||
|
mask-id (mf/use-ctx mask-id-ctx)
|
||||||
{:keys [id x y width height rotation content]} shape]
|
{:keys [id x y width height rotation content]} shape]
|
||||||
[:foreignObject {:x x
|
[:foreignObject {:x x
|
||||||
:y y
|
:y y
|
||||||
|
@ -231,6 +233,7 @@
|
||||||
:transform (geom/transform-matrix shape)
|
:transform (geom/transform-matrix shape)
|
||||||
:id (str id)
|
:id (str id)
|
||||||
:width width
|
:width width
|
||||||
:height height}
|
:height height
|
||||||
|
:mask mask-id}
|
||||||
[:& text-content {:content (:content shape)}]]))
|
[:& text-content {:content (:content shape)}]]))
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,8 @@
|
||||||
do-unlock-shape #(st/emit! (dw/update-shape-flags id {:blocked false}))
|
do-unlock-shape #(st/emit! (dw/update-shape-flags id {:blocked false}))
|
||||||
do-create-group #(st/emit! dw/group-selected)
|
do-create-group #(st/emit! dw/group-selected)
|
||||||
do-remove-group #(st/emit! dw/ungroup-selected)
|
do-remove-group #(st/emit! dw/ungroup-selected)
|
||||||
|
do-mask-group #(st/emit! dw/mask-group)
|
||||||
|
do-unmask-group #(st/emit! dw/unmask-group)
|
||||||
do-add-component #(st/emit! dwl/add-component)
|
do-add-component #(st/emit! dwl/add-component)
|
||||||
do-detach-component #(st/emit! (dwl/detach-component id))
|
do-detach-component #(st/emit! (dwl/detach-component id))
|
||||||
do-reset-component #(st/emit! (dwl/reset-component id))
|
do-reset-component #(st/emit! (dwl/reset-component id))
|
||||||
|
@ -98,14 +100,26 @@
|
||||||
[:& menu-separator]
|
[:& menu-separator]
|
||||||
|
|
||||||
(when (> (count selected) 1)
|
(when (> (count selected) 1)
|
||||||
[:& menu-entry {:title "Group"
|
[:*
|
||||||
:shortcut "Ctrl + g"
|
[:& menu-entry {:title "Group"
|
||||||
:on-click do-create-group}])
|
:shortcut "Ctrl + g"
|
||||||
|
:on-click do-create-group}]
|
||||||
|
[:& menu-entry {:title "Mask"
|
||||||
|
:shortcut "Ctrl + Shift + M"
|
||||||
|
:on-click do-mask-group}]])
|
||||||
|
|
||||||
(when (and (= (count selected) 1) (= (:type shape) :group))
|
(when (and (= (count selected) 1) (= (:type shape) :group))
|
||||||
[:& menu-entry {:title "Ungroup"
|
[:*
|
||||||
:shortcut "Shift + g"
|
[:& menu-entry {:title "Ungroup"
|
||||||
:on-click do-remove-group}])
|
:shortcut "Shift + g"
|
||||||
|
:on-click do-remove-group}]
|
||||||
|
(if (:masked-group? shape)
|
||||||
|
[:& menu-entry {:title "Unmask"
|
||||||
|
:shortcut "Ctrl + Shift + M"
|
||||||
|
:on-click do-unmask-group}]
|
||||||
|
[:& menu-entry {:title "Mask"
|
||||||
|
:shortcut "Ctrl + Shift + M"
|
||||||
|
:on-click do-mask-group}])])
|
||||||
|
|
||||||
(if (:hidden shape)
|
(if (:hidden shape)
|
||||||
[:& menu-entry {:title "Show"
|
[:& menu-entry {:title "Show"
|
||||||
|
|
|
@ -43,9 +43,11 @@
|
||||||
:rect i/box
|
:rect i/box
|
||||||
:curve i/curve
|
:curve i/curve
|
||||||
:text i/text
|
:text i/text
|
||||||
:group (if (nil? (:component-id shape))
|
:group (if (some? (:component-id shape))
|
||||||
i/folder
|
i/component
|
||||||
i/component)
|
(if (:masked-group? shape)
|
||||||
|
i/mask
|
||||||
|
i/folder))
|
||||||
nil))
|
nil))
|
||||||
|
|
||||||
;; --- Layer Name
|
;; --- Layer Name
|
||||||
|
@ -196,6 +198,7 @@
|
||||||
:ref dref
|
:ref dref
|
||||||
:class (dom/classnames
|
:class (dom/classnames
|
||||||
:component (not (nil? (:component-id item)))
|
:component (not (nil? (:component-id item)))
|
||||||
|
:masked (:masked-group? item)
|
||||||
:dnd-over (= (:over dprops) :center)
|
:dnd-over (= (:over dprops) :center)
|
||||||
:dnd-over-top (= (:over dprops) :top)
|
:dnd-over-top (= (:over dprops) :top)
|
||||||
:dnd-over-bot (= (:over dprops) :bot)
|
:dnd-over-bot (= (:over dprops) :bot)
|
||||||
|
@ -307,7 +310,8 @@
|
||||||
:component-file
|
:component-file
|
||||||
:shape-ref
|
:shape-ref
|
||||||
:touched
|
:touched
|
||||||
:metadata])]
|
:metadata
|
||||||
|
:masked-group?])]
|
||||||
(persistent!
|
(persistent!
|
||||||
(reduce-kv (fn [res id obj]
|
(reduce-kv (fn [res id obj]
|
||||||
(assoc! res id (strip-data obj)))
|
(assoc! res id (strip-data obj)))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue