mirror of
https://github.com/penpot/penpot.git
synced 2025-06-08 16:21:38 +02:00
✨ Import paths as native shapes
This commit is contained in:
parent
741d67c30b
commit
19febde547
28 changed files with 921 additions and 209 deletions
|
@ -12,7 +12,8 @@
|
|||
[rumext.alpha :as mf]
|
||||
[cuerdas.core :as str]
|
||||
[app.util.object :as obj]
|
||||
[app.main.ui.context :as muc]))
|
||||
[app.main.ui.context :as muc]
|
||||
[app.util.svg :as usvg]))
|
||||
|
||||
(defn- stroke-type->dasharray
|
||||
[style]
|
||||
|
@ -74,45 +75,77 @@
|
|||
attrs)))
|
||||
|
||||
(defn add-fill [attrs shape render-id]
|
||||
(let [fill-color-gradient-id (str "fill-color-gradient_" render-id)]
|
||||
(cond
|
||||
(:fill-color-gradient shape)
|
||||
(obj/merge! attrs #js {:fill (str/format "url(#%s)" fill-color-gradient-id)})
|
||||
(let [fill-attrs (cond
|
||||
(contains? shape :fill-color-gradient)
|
||||
(let [fill-color-gradient-id (str "fill-color-gradient_" render-id)]
|
||||
{:fill (str/format "url(#%s)" fill-color-gradient-id)})
|
||||
|
||||
(and (not= :svg-raw (:type shape))
|
||||
(not (:fill-color-gradient shape)))
|
||||
(obj/merge! attrs #js {:fill (or (:fill-color shape) "transparent")
|
||||
:fillOpacity (:fill-opacity shape nil)})
|
||||
(contains? shape :fill-color)
|
||||
{:fill (:fill-color shape)}
|
||||
|
||||
(and (= :svg-raw (:type shape))
|
||||
(or (:fill-opacity shape) (:fill-color shape)))
|
||||
(obj/merge! attrs #js {:fill (:fill-color shape)
|
||||
:fillOpacity (:fill-opacity shape nil)})
|
||||
;; If contains svg-attrs the origin is svg. If it's not svg origin
|
||||
;; we setup the default fill as transparent (instead of black)
|
||||
(not (contains? shape :svg-attrs))
|
||||
{:fill "transparent"}
|
||||
|
||||
:else attrs)))
|
||||
:else
|
||||
{})
|
||||
|
||||
fill-attrs (cond-> fill-attrs
|
||||
(contains? shape :fill-opacity)
|
||||
(assoc :fillOpacity (:fill-opacity shape)))]
|
||||
|
||||
(obj/merge! attrs (clj->js fill-attrs))))
|
||||
|
||||
(defn add-stroke [attrs shape render-id]
|
||||
(let [stroke-style (:stroke-style shape :none)
|
||||
stroke-color-gradient-id (str "stroke-color-gradient_" render-id)]
|
||||
(if (not= stroke-style :none)
|
||||
(if (:stroke-color-gradient shape)
|
||||
(obj/merge! attrs
|
||||
#js {:stroke (str/format "url(#%s)" stroke-color-gradient-id)
|
||||
:strokeWidth (:stroke-width shape 1)
|
||||
:strokeDasharray (stroke-type->dasharray stroke-style)})
|
||||
(obj/merge! attrs
|
||||
#js {:stroke (:stroke-color shape nil)
|
||||
:strokeWidth (:stroke-width shape 1)
|
||||
:strokeOpacity (:stroke-opacity shape nil)
|
||||
:strokeDasharray (stroke-type->dasharray stroke-style)}))))
|
||||
attrs)
|
||||
(let [stroke-attrs
|
||||
(cond-> {:strokeWidth (:stroke-width shape 1)}
|
||||
(:stroke-color-gradient shape)
|
||||
(assoc :stroke (str/format "url(#%s)" stroke-color-gradient-id))
|
||||
|
||||
(not (:stroke-color-gradient shape))
|
||||
(assoc :stroke (:stroke-color shape nil)
|
||||
:strokeOpacity (:stroke-opacity shape nil))
|
||||
|
||||
(not= stroke-style :svg)
|
||||
(assoc :strokeDasharray (stroke-type->dasharray stroke-style)))]
|
||||
(obj/merge! attrs (clj->js stroke-attrs)))
|
||||
attrs)))
|
||||
|
||||
|
||||
(defn extract-svg-attrs
|
||||
[render-id svg-defs svg-attrs]
|
||||
(let [replace-id (fn [id]
|
||||
(if (contains? svg-defs id)
|
||||
(str render-id "-" id)
|
||||
id))
|
||||
svg-attrs (-> svg-attrs
|
||||
(usvg/update-attr-ids replace-id)
|
||||
(usvg/clean-attrs))
|
||||
|
||||
attrs (-> svg-attrs (dissoc :style) (clj->js))
|
||||
styles (-> svg-attrs (:style {}) (clj->js))]
|
||||
[attrs styles]))
|
||||
|
||||
(defn extract-style-attrs
|
||||
([shape]
|
||||
(let [render-id (mf/use-ctx muc/render-ctx)
|
||||
svg-defs (:svg-defs shape {})
|
||||
svg-attrs (:svg-attrs shape {})
|
||||
|
||||
[svg-attrs svg-styles] (mf/use-memo
|
||||
(mf/deps render-id svg-defs svg-attrs)
|
||||
#(extract-svg-attrs render-id svg-defs svg-attrs))
|
||||
|
||||
styles (-> (obj/new)
|
||||
(obj/merge! svg-styles)
|
||||
(add-fill shape render-id)
|
||||
(add-stroke shape render-id))]
|
||||
|
||||
(-> (obj/new)
|
||||
(obj/merge! svg-attrs)
|
||||
(add-border-radius shape)
|
||||
(obj/set! "style" styles)))))
|
||||
|
|
|
@ -9,7 +9,9 @@
|
|||
|
||||
(ns app.main.ui.shapes.group
|
||||
(:require
|
||||
[app.util.object :as obj]
|
||||
[rumext.alpha :as mf]
|
||||
[app.main.ui.shapes.attrs :as attrs]
|
||||
[app.main.ui.shapes.mask :refer [mask-str mask-factory]]))
|
||||
|
||||
(defn group-shape
|
||||
|
@ -28,12 +30,15 @@
|
|||
|
||||
show-mask? (and (:masked-group? shape) (not expand-mask))
|
||||
mask (when show-mask? (first childs))
|
||||
childs (if show-mask? (rest childs) childs)]
|
||||
childs (if show-mask? (rest childs) childs)
|
||||
|
||||
[:g.group
|
||||
{:pointer-events pointer-events
|
||||
:mask (when (and mask (not expand-mask)) (mask-str mask))}
|
||||
props (-> (attrs/extract-style-attrs shape)
|
||||
(obj/merge!
|
||||
#js {:className "group"
|
||||
:pointerEvents pointer-events
|
||||
:mask (when (and mask (not expand-mask)) (mask-str mask))}))]
|
||||
|
||||
[:> :g props
|
||||
(when mask
|
||||
[:> render-mask #js {:frame frame :mask mask}])
|
||||
|
||||
|
|
|
@ -9,13 +9,12 @@
|
|||
|
||||
(ns app.main.ui.shapes.shape
|
||||
(:require
|
||||
[app.common.geom.shapes :as geom]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.ui.context :as muc]
|
||||
[app.main.ui.shapes.filters :as filters]
|
||||
[app.main.ui.shapes.gradients :as grad]
|
||||
[app.main.ui.shapes.svg-defs :as defs]
|
||||
[app.util.object :as obj]
|
||||
[cuerdas.core :as str]
|
||||
[rumext.alpha :as mf]))
|
||||
|
||||
(mf/defc shape-container
|
||||
|
@ -48,6 +47,7 @@
|
|||
[:& (mf/provider muc/render-ctx) {:value render-id}
|
||||
[:> wrapper-tag group-props
|
||||
[:defs
|
||||
[:& defs/svg-defs {:shape shape :render-id render-id}]
|
||||
[:& filters/filters {:shape shape :filter-id filter-id}]
|
||||
[:& grad/gradient {:shape shape :attr :fill-color-gradient}]
|
||||
[:& grad/gradient {:shape shape :attr :stroke-color-gradient}]]
|
||||
|
|
103
frontend/src/app/main/ui/shapes/svg_defs.cljs
Normal file
103
frontend/src/app/main/ui/shapes/svg_defs.cljs
Normal file
|
@ -0,0 +1,103 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
;; defined by the Mozilla Public License, v. 2.0.
|
||||
;;
|
||||
;; Copyright (c) 2020-2021 UXBOX Labs SL
|
||||
|
||||
(ns app.main.ui.shapes.svg-defs
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.matrix :as gmt]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.util.object :as obj]
|
||||
[app.util.svg :as usvg]
|
||||
[rumext.alpha :as mf]))
|
||||
|
||||
(defn add-matrix [attrs transform-key transform-matrix]
|
||||
(update attrs
|
||||
transform-key
|
||||
(fn [val]
|
||||
(if val
|
||||
(str transform-matrix " " val)
|
||||
(str transform-matrix)))))
|
||||
|
||||
(defn transform-region [attrs transform]
|
||||
(let [{x-str :x y-str :y width-str :width height-str :height} attrs
|
||||
data (map d/parse-double [x-str y-str width-str height-str])]
|
||||
(if (every? (comp not nil?) data)
|
||||
(let [[x y width height] data
|
||||
p1 (-> (gpt/point x y)
|
||||
(gpt/transform transform))
|
||||
p2 (-> (gpt/point (+ x width) (+ y height))
|
||||
(gpt/transform transform))]
|
||||
|
||||
(assoc attrs
|
||||
:x (:x p1)
|
||||
:y (:y p1)
|
||||
:width (- (:x p2) (:x p1))
|
||||
:height (- (:y p2) (:y p1))))
|
||||
attrs)))
|
||||
|
||||
(mf/defc svg-node [{:keys [node prefix-id transform]}]
|
||||
(cond
|
||||
(string? node) node
|
||||
|
||||
:else
|
||||
(let [{:keys [tag attrs content]} node
|
||||
|
||||
transform-gradient? (and (#{:linearGradient :radialGradient} tag)
|
||||
(= "userSpaceOnUse" (get attrs :gradientUnits "userSpaceOnUse")))
|
||||
|
||||
transform-pattern? (and (= :pattern tag)
|
||||
(every? d/num-string? [(:x attrs "0") (:y attrs "0") (:width attrs "0") (:height attrs "0")])
|
||||
(= "userSpaceOnUse" (get attrs :patternUnits "userSpaceOnUse")))
|
||||
|
||||
transform-filter? (and (= #{:filter
|
||||
;; Filter primitives. We need to remap subregions
|
||||
:feBlend :feColorMatrix :feComponentTransfer :feComposite :feConvolveMatrix
|
||||
:feDiffuseLighting :feDisplacementMap :feFlood :feGaussianBlur
|
||||
:feImage :feMerge :feMorphology :feOffset
|
||||
:feSpecularLighting :feTile :feTurbulence} tag)
|
||||
(= "userSpaceOnUse" (get attrs :filterUnits "userSpaceOnUse")))
|
||||
|
||||
attrs (-> attrs
|
||||
(usvg/update-attr-ids prefix-id)
|
||||
(usvg/clean-attrs)
|
||||
|
||||
(cond->
|
||||
transform-gradient? (add-matrix :gradientTransform transform)
|
||||
transform-pattern? (add-matrix :patternTransform transform)
|
||||
transform-filter? (transform-region transform)))
|
||||
|
||||
[wrapper wrapper-props] (if (= tag :mask)
|
||||
["g" #js {:transform (str transform)}]
|
||||
[mf/Fragment (obj/new)])]
|
||||
|
||||
[:> (name tag) (clj->js attrs)
|
||||
[:> wrapper wrapper-props
|
||||
(for [node content] [:& svg-node {:node node
|
||||
:prefix-id prefix-id
|
||||
:transform transform}])]])))
|
||||
|
||||
(mf/defc svg-defs [{:keys [shape render-id]}]
|
||||
(let [svg-defs (:svg-defs shape)
|
||||
transform (mf/use-memo
|
||||
(mf/deps shape)
|
||||
#(if (= :svg-raw (:type shape))
|
||||
(gmt/matrix)
|
||||
(usvg/svg-transform-matrix shape)))
|
||||
|
||||
prefix-id
|
||||
(fn [id]
|
||||
(cond->> id
|
||||
(contains? svg-defs id) (str render-id "-")))]
|
||||
|
||||
(when (and svg-defs (not (empty? svg-defs)))
|
||||
(for [svg-def (vals svg-defs)]
|
||||
[:& svg-node {:node svg-def
|
||||
:prefix-id prefix-id
|
||||
:transform transform}]))))
|
||||
|
|
@ -21,7 +21,7 @@
|
|||
[rumext.alpha :as mf]))
|
||||
|
||||
;; Graphic tags
|
||||
(defonce graphic-element? #{ :circle :ellipse :image :line :path :polygon :polyline :rect :text #_"use"})
|
||||
(defonce graphic-element? #{:circle :ellipse :image :line :path :polygon :polyline :rect :text :use})
|
||||
|
||||
;; Context to store a re-mapping of the ids
|
||||
(def svg-ids-ctx (mf/create-context nil))
|
||||
|
@ -37,17 +37,11 @@
|
|||
(obj/set! "style" style))))
|
||||
|
||||
(defn translate-shape [attrs shape]
|
||||
(let [{svg-width :width svg-height :height :as root-shape} (:root-attrs shape)
|
||||
{:keys [x y width height]} (:selrect shape)
|
||||
transform (->> (:transform attrs "")
|
||||
(str (gmt/multiply
|
||||
(gmt/matrix)
|
||||
(gsh/transform-matrix shape)
|
||||
(gmt/translate-matrix (gpt/point x y))
|
||||
(gmt/scale-matrix (gpt/point (/ width svg-width) (/ height svg-height))))
|
||||
" "))]
|
||||
(let [transform (str (usvg/svg-transform-matrix shape)
|
||||
" "
|
||||
(:transform attrs ""))]
|
||||
(cond-> attrs
|
||||
(and root-shape (graphic-element? (-> shape :content :tag)))
|
||||
(and (:svg-viewbox shape) (graphic-element? (-> shape :content :tag)))
|
||||
(assoc :transform transform))))
|
||||
|
||||
(mf/defc svg-root
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue