mirror of
https://github.com/penpot/penpot.git
synced 2025-04-10 14:01:29 -05:00
✨ Ability to add multiple strokes to a shape
This commit is contained in:
parent
719aacd6f8
commit
a73a393e26
30 changed files with 680 additions and 353 deletions
|
@ -5,6 +5,7 @@
|
|||
### :boom: Breaking changes
|
||||
### :sparkles: New features
|
||||
|
||||
- Ability to add multiple strokes to a shape [Taiga #2778](https://tree.taiga.io/project/penpot/us/2778)
|
||||
- Scroll to selected size in font size selector [Taiga #2825](https://tree.taiga.io/project/penpot/us/2825)
|
||||
- Duplicate artboards create new flows if needed [Taiga #2221](https://tree.taiga.io/project/penpot/issue/2221)
|
||||
- Add new invitations section [Taiga #2797](https://tree.taiga.io/project/penpot/us/2797)
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
[app.common.colors :as clr]
|
||||
[app.common.uuid :as uuid]))
|
||||
|
||||
(def file-version 14)
|
||||
(def file-version 15)
|
||||
(def default-color clr/gray-20)
|
||||
(def root uuid/zero)
|
||||
|
||||
|
@ -32,6 +32,7 @@
|
|||
:letter-spacing :text-display-group
|
||||
:line-height :text-display-group
|
||||
:text-align :text-display-group
|
||||
:strokes :stroke-group
|
||||
:stroke-color :stroke-group
|
||||
:stroke-color-gradient :stroke-group
|
||||
:stroke-color-ref-file :stroke-group
|
||||
|
@ -84,7 +85,19 @@
|
|||
:fill-color-ref-id
|
||||
:fill-color-ref-file
|
||||
:fill-color-gradient
|
||||
:hide-fill-on-export}
|
||||
:hide-fill-on-export
|
||||
|
||||
:strokes
|
||||
:stroke-style
|
||||
:stroke-alignment
|
||||
:stroke-width
|
||||
:stroke-color
|
||||
:stroke-color-ref-id
|
||||
:stroke-color-ref-file
|
||||
:stroke-opacity
|
||||
:stroke-color-gradient
|
||||
:stroke-cap-start
|
||||
:stroke-cap-end}
|
||||
|
||||
:group #{:proportion-lock
|
||||
:width :height
|
||||
|
@ -131,7 +144,8 @@
|
|||
:fill-color-ref-id
|
||||
:fill-color-ref-file
|
||||
:fill-color-gradient
|
||||
|
||||
|
||||
:strokes
|
||||
:stroke-style
|
||||
:stroke-alignment
|
||||
:stroke-width
|
||||
|
@ -171,6 +185,7 @@
|
|||
:fill-color-ref-file
|
||||
:fill-color-gradient
|
||||
|
||||
:strokes
|
||||
:stroke-style
|
||||
:stroke-alignment
|
||||
:stroke-width
|
||||
|
@ -210,6 +225,7 @@
|
|||
:fill-color-ref-file
|
||||
:fill-color-gradient
|
||||
|
||||
:strokes
|
||||
:stroke-style
|
||||
:stroke-alignment
|
||||
:stroke-width
|
||||
|
@ -339,6 +355,7 @@
|
|||
:fill-color-ref-file
|
||||
:fill-color-gradient
|
||||
|
||||
:strokes
|
||||
:stroke-style
|
||||
:stroke-alignment
|
||||
:stroke-width
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
{:frame-id uuid/zero
|
||||
:fills [{:fill-color clr/white
|
||||
:fill-opacity 1}]
|
||||
:strokes []
|
||||
:shapes []
|
||||
:hide-fill-on-export false})
|
||||
|
||||
|
@ -43,47 +44,32 @@
|
|||
:name "Rect-1"
|
||||
:fills [{:fill-color default-color
|
||||
:fill-opacity 1}]
|
||||
:stroke-style :none
|
||||
:stroke-alignment :center
|
||||
:stroke-width 0
|
||||
:stroke-color clr/black
|
||||
:stroke-opacity 0
|
||||
:strokes []
|
||||
:rx 0
|
||||
:ry 0}
|
||||
|
||||
{:type :image
|
||||
:rx 0
|
||||
:ry 0
|
||||
:fills []}
|
||||
:fills []
|
||||
:strokes []}
|
||||
|
||||
{:type :circle
|
||||
:name "Circle-1"
|
||||
:fills [{:fill-color default-color
|
||||
:fill-opacity 1}]
|
||||
:stroke-style :none
|
||||
:stroke-alignment :center
|
||||
:stroke-width 0
|
||||
:stroke-color clr/black
|
||||
:stroke-opacity 0}
|
||||
:strokes []}
|
||||
|
||||
{:type :path
|
||||
:name "Path-1"
|
||||
:fills []
|
||||
:stroke-style :solid
|
||||
:stroke-alignment :center
|
||||
:stroke-width 2
|
||||
:stroke-color clr/black
|
||||
:stroke-opacity 1}
|
||||
:strokes []}
|
||||
|
||||
{:type :frame
|
||||
:name "Artboard-1"
|
||||
:fills [{:fill-color clr/white
|
||||
:fill-opacity 1}]
|
||||
:stroke-style :none
|
||||
:stroke-alignment :center
|
||||
:stroke-width 0
|
||||
:stroke-color clr/black
|
||||
:stroke-opacity 0}
|
||||
:strokes []}
|
||||
|
||||
{:type :text
|
||||
:name "Text-1"
|
||||
|
|
|
@ -310,13 +310,9 @@
|
|||
:fill-opacity (:fill-opacity shape)}
|
||||
|
||||
clean-attrs (d/without-nils attrs)]
|
||||
(-> shape
|
||||
(assoc :fills [clean-attrs])
|
||||
(dissoc :fill-color)
|
||||
(dissoc :fill-color-gradient)
|
||||
(dissoc :fill-color-ref-file)
|
||||
(dissoc :fill-color-ref-id)
|
||||
(dissoc :fill-opacity))))
|
||||
(cond-> shape
|
||||
(not (empty? clean-attrs))
|
||||
(assoc :fills [clean-attrs]))))
|
||||
|
||||
;; Add fills to shapes
|
||||
(defmethod migrate 14
|
||||
|
@ -328,5 +324,34 @@
|
|||
|
||||
(update-page [_ page]
|
||||
(update page :objects #(d/mapm update-object %)))]
|
||||
|
||||
(update data :pages-index #(d/mapm update-page %))))
|
||||
|
||||
(defn set-strokes
|
||||
[shape]
|
||||
(let [attrs {:stroke-style (:stroke-style shape)
|
||||
:stroke-alignment (:stroke-alignment shape)
|
||||
:stroke-width (:stroke-width shape)
|
||||
:stroke-color (:stroke-color shape)
|
||||
:stroke-color-ref-id (:stroke-color-ref-id shape)
|
||||
:stroke-color-ref-file (:stroke-color-ref-file shape)
|
||||
:stroke-opacity (:stroke-opacity shape)
|
||||
:stroke-color-gradient (:stroke-color-gradient shape)
|
||||
:stroke-cap-start (:stroke-cap-start shape)
|
||||
:stroke-cap-end (:stroke-cap-end shape)}
|
||||
|
||||
clean-attrs (d/without-nils attrs)]
|
||||
(cond-> shape
|
||||
(not (empty? clean-attrs))
|
||||
(assoc :strokes [clean-attrs]))))
|
||||
|
||||
;; Add strokes to shapes
|
||||
(defmethod migrate 15
|
||||
[data]
|
||||
(letfn [(update-object [_ object]
|
||||
(cond-> object
|
||||
(and (not (= :text (:type object))) (nil? (:strokes object)))
|
||||
(set-strokes)))
|
||||
|
||||
(update-page [_ page]
|
||||
(update page :objects #(d/mapm update-object %)))]
|
||||
(update data :pages-index #(d/mapm update-page %))))
|
||||
|
|
|
@ -463,6 +463,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
.element-set-content .border-data {
|
||||
&[draggable="true"] {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.element-set-content .grid-option-main {
|
||||
.editable-select {
|
||||
height: 2rem;
|
||||
|
|
|
@ -135,12 +135,12 @@
|
|||
(rx/from (map #(dwt/update-text-with-function % transform-attrs) text-ids))
|
||||
(rx/of (dch/update-shapes shape-ids transform-attrs)))))
|
||||
|
||||
(defn swap-fills [shape index new-index]
|
||||
(let [first (get-in shape [:fills index])
|
||||
second (get-in shape [:fills new-index])]
|
||||
(defn swap-attrs [shape attr index new-index]
|
||||
(let [first (get-in shape [attr index])
|
||||
second (get-in shape [attr new-index])]
|
||||
(-> shape
|
||||
(assoc-in [:fills index] second)
|
||||
(assoc-in [:fills new-index] first))))
|
||||
(assoc-in [attr index] second)
|
||||
(assoc-in [attr new-index] first))))
|
||||
|
||||
(defn reorder-fills
|
||||
[ids index new-index]
|
||||
|
@ -152,7 +152,7 @@
|
|||
is-text? #(= :text (:type (get objects %)))
|
||||
text-ids (filter is-text? ids)
|
||||
shape-ids (remove is-text? ids)
|
||||
transform-attrs #(swap-fills % index new-index)]
|
||||
transform-attrs #(swap-attrs % :fills index new-index)]
|
||||
|
||||
(rx/concat
|
||||
(rx/from (map #(dwt/update-text-with-function % transform-attrs) text-ids))
|
||||
|
@ -225,37 +225,72 @@
|
|||
shape))))))))
|
||||
|
||||
(defn change-stroke
|
||||
[ids color]
|
||||
[ids attrs index]
|
||||
(ptk/reify ::change-stroke
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(let [attrs (cond-> {:stroke-color nil
|
||||
:stroke-color-ref-id nil
|
||||
:stroke-color-ref-file nil
|
||||
:stroke-color-gradient nil
|
||||
:stroke-opacity nil}
|
||||
(contains? color :color)
|
||||
(assoc :stroke-color (:color color))
|
||||
(let [color-attrs (cond-> {}
|
||||
(contains? attrs :color)
|
||||
(assoc :stroke-color (:color attrs))
|
||||
|
||||
(contains? color :id)
|
||||
(assoc :stroke-color-ref-id (:id color))
|
||||
(contains? attrs :id)
|
||||
(assoc :stroke-color-ref-id (:id attrs))
|
||||
|
||||
(contains? color :file-id)
|
||||
(assoc :stroke-color-ref-file (:file-id color))
|
||||
(contains? attrs :file-id)
|
||||
(assoc :stroke-color-ref-file (:file-id attrs))
|
||||
|
||||
(contains? color :gradient)
|
||||
(assoc :stroke-color-gradient (:gradient color))
|
||||
(contains? attrs :gradient)
|
||||
(assoc :stroke-color-gradient (:gradient attrs))
|
||||
|
||||
(contains? color :opacity)
|
||||
(assoc :stroke-opacity (:opacity color)))]
|
||||
(contains? attrs :opacity)
|
||||
(assoc :stroke-opacity (:opacity attrs)))
|
||||
attrs (merge attrs color-attrs)]
|
||||
|
||||
(rx/of (dch/update-shapes ids (fn [shape]
|
||||
(cond-> (d/merge shape attrs)
|
||||
(= (:stroke-style shape) :none)
|
||||
(assoc :stroke-style :solid
|
||||
:stroke-width 1
|
||||
:stroke-opacity 1)))))))))
|
||||
(assoc-in shape [:strokes index] (merge (get-in shape [:strokes index]) attrs)))))))))
|
||||
|
||||
(defn add-stroke
|
||||
[ids stroke]
|
||||
(ptk/reify ::add-stroke
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(let [add (fn [shape attrs] (assoc shape :strokes (into [attrs] (:strokes shape))))]
|
||||
(rx/of (dch/update-shapes
|
||||
ids
|
||||
#(add % stroke)))))))
|
||||
|
||||
(defn remove-stroke
|
||||
[ids position]
|
||||
(ptk/reify ::remove-stroke
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(let [remove-fill-by-index (fn [values index] (->> (d/enumerate values)
|
||||
(filterv (fn [[idx _]] (not= idx index)))
|
||||
(mapv second)))
|
||||
|
||||
remove (fn [shape] (update shape :strokes remove-fill-by-index position))]
|
||||
(rx/of (dch/update-shapes
|
||||
ids
|
||||
#(remove %)))))))
|
||||
|
||||
(defn remove-all-strokes
|
||||
[ids]
|
||||
(ptk/reify ::remove-all-strokes
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(let [remove-all (fn [shape] (assoc shape :strokes []))]
|
||||
(rx/of (dch/update-shapes
|
||||
ids
|
||||
#(remove-all %)))))))
|
||||
|
||||
(defn reorder-strokes
|
||||
[ids index new-index]
|
||||
(ptk/reify ::reorder-strokes
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(rx/of (dch/update-shapes
|
||||
ids
|
||||
#(swap-attrs % :strokes index new-index))))))
|
||||
|
||||
(defn picker-for-selected-shape
|
||||
[]
|
||||
|
|
|
@ -101,25 +101,26 @@
|
|||
(get-in shape [:svg-attrs :style :stroke-linecap]))
|
||||
((d/nilf str/trim))
|
||||
((d/nilf keyword)))
|
||||
|
||||
shape
|
||||
(cond-> shape
|
||||
(uc/color? (get-in shape [:svg-attrs :stroke]))
|
||||
(uc/color? (str/trim (get-in shape [:svg-attrs :stroke])))
|
||||
(-> (update :svg-attrs dissoc :stroke)
|
||||
(assoc :stroke-color (get-in shape [:svg-attrs :stroke])))
|
||||
(assoc-in [:strokes 0 :stroke-color] (get-in shape [:svg-attrs :stroke])))
|
||||
|
||||
(uc/color? (get-in shape [:svg-attrs :style :stroke]))
|
||||
(uc/color? (str/trim (get-in shape [:svg-attrs :style :stroke])))
|
||||
(-> (update-in [:svg-attrs :style] dissoc :stroke)
|
||||
(assoc :stroke-color (get-in shape [:svg-attrs :style :stroke])))
|
||||
(assoc-in [:strokes 0 :stroke-color] (get-in shape [:svg-attrs :style :stroke])))
|
||||
|
||||
(get-in shape [:svg-attrs :stroke-width])
|
||||
(-> (update :svg-attrs dissoc :stroke-width)
|
||||
(assoc :stroke-width (-> (get-in shape [:svg-attrs :stroke-width])
|
||||
(d/parse-double))))
|
||||
(assoc-in [:strokes 0 :stroke-width] (-> (get-in shape [:svg-attrs :stroke-width])
|
||||
(d/parse-double))))
|
||||
|
||||
(get-in shape [:svg-attrs :style :stroke-width])
|
||||
(-> (update-in [:svg-attrs :style] dissoc :stroke-width)
|
||||
(assoc :stroke-width (-> (get-in shape [:svg-attrs :style :stroke-width])
|
||||
(d/parse-double))))
|
||||
(assoc-in [:strokes 0 :stroke-width] (-> (get-in shape [:svg-attrs :style :stroke-width])
|
||||
(d/parse-double))))
|
||||
|
||||
(and stroke-linecap (= (:type shape) :path))
|
||||
(-> (update-in [:svg-attrs :style] dissoc :stroke-linecap)
|
||||
|
@ -128,8 +129,8 @@
|
|||
(assoc :stroke-cap-start stroke-linecap
|
||||
:stroke-cap-end stroke-linecap))))]
|
||||
|
||||
(if (d/any-key? shape :stroke-color :stroke-opacity :stroke-width :stroke-cap-start :stroke-cap-end)
|
||||
(merge {:stroke-style :svg} shape)
|
||||
(if (d/any-key? (get-in [:strokes 0] shape) :stroke-color :stroke-opacity :stroke-width :stroke-cap-start :stroke-cap-end)
|
||||
(assoc-in shape [:strokes 0 :stroke-style] :svg)
|
||||
shape)))
|
||||
|
||||
(defn setup-opacity [shape]
|
||||
|
@ -383,6 +384,7 @@
|
|||
#_other (create-raw-svg name frame-id svg-data element-data)))
|
||||
|
||||
shape (assoc shape :fills [])
|
||||
shape (assoc shape :strokes [])
|
||||
|
||||
shape (when (some? shape)
|
||||
(-> shape
|
||||
|
|
|
@ -113,9 +113,9 @@
|
|||
|
||||
(obj/merge! attrs (clj->js fill-attrs)))))
|
||||
|
||||
(defn add-stroke [attrs shape render-id]
|
||||
(defn add-stroke [attrs shape render-id index]
|
||||
(let [stroke-style (:stroke-style shape :none)
|
||||
stroke-color-gradient-id (str "stroke-color-gradient_" render-id)
|
||||
stroke-color-gradient-id (str "stroke-color-gradient_" render-id "_" index)
|
||||
stroke-width (:stroke-width shape 1)]
|
||||
(if (not= stroke-style :none)
|
||||
(let [stroke-attrs
|
||||
|
@ -198,14 +198,13 @@
|
|||
|
||||
styles (-> (obj/get props "style" (obj/new))
|
||||
(obj/merge! svg-styles)
|
||||
(add-stroke shape render-id)
|
||||
(add-layer-props shape))
|
||||
|
||||
styles (cond (or (some? (:fill-image shape))
|
||||
(= :image (:type shape))
|
||||
(> (count (:fills shape)) 1)
|
||||
(some #(some? (:fill-color-gradient %)) (:fills shape)))
|
||||
(obj/set! styles "fill" (str "url(#fill-0-" render-id ")"))
|
||||
(obj/set! styles "fill" (str "url(#fill-0-" render-id ")"))
|
||||
|
||||
;; imported svgs can have fill and fill-opacity attributes
|
||||
(obj/contains? svg-styles "fill")
|
||||
|
@ -233,7 +232,15 @@
|
|||
(-> (obj/new)
|
||||
(obj/set! "style" fill-styles))))
|
||||
|
||||
(defn extract-stroke-attrs
|
||||
[shape index]
|
||||
(let [render-id (mf/use-ctx muc/render-ctx)
|
||||
stroke-styles (-> (obj/get shape "style" (obj/new))
|
||||
(add-stroke shape render-id index))]
|
||||
(-> (obj/new)
|
||||
(obj/set! "style" stroke-styles))))
|
||||
|
||||
(defn extract-border-radius-attrs
|
||||
[shape]
|
||||
(-> (obj/new)
|
||||
(add-border-radius shape)))
|
||||
(-> (obj/new)
|
||||
(add-border-radius shape)))
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
(:require
|
||||
[app.common.geom.shapes :as geom]
|
||||
[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-strokes]]
|
||||
[app.util.object :as obj]
|
||||
[rumext.alpha :as mf]))
|
||||
|
||||
|
@ -32,5 +32,5 @@
|
|||
:ry ry
|
||||
:transform transform}))]
|
||||
|
||||
[:& shape-custom-stroke {:shape shape}
|
||||
[:& shape-custom-strokes {:shape shape}
|
||||
[:> :ellipse props]]))
|
||||
|
|
|
@ -6,8 +6,11 @@
|
|||
|
||||
(ns app.main.ui.shapes.custom-stroke
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.main.ui.context :as muc]
|
||||
[app.main.ui.shapes.attrs :as attrs]
|
||||
[app.main.ui.shapes.gradients :as grad]
|
||||
[app.util.object :as obj]
|
||||
[cuerdas.core :as str]
|
||||
[rumext.alpha :as mf]))
|
||||
|
@ -39,8 +42,20 @@
|
|||
stroke-width (case (:stroke-alignment shape :center)
|
||||
:center (/ (:stroke-width shape 0) 2)
|
||||
:outer (:stroke-width shape 0)
|
||||
0)]
|
||||
[:mask {:id stroke-mask-id}
|
||||
0)
|
||||
margin (gsh/shape-stroke-margin shape stroke-width)
|
||||
bounding-box (-> (gsh/points->selrect (:points shape))
|
||||
(update :x - (+ stroke-width margin))
|
||||
(update :y - (+ stroke-width margin))
|
||||
(update :width + (* 2 (+ stroke-width margin)))
|
||||
(update :height + (* 2 (+ stroke-width margin))))]
|
||||
|
||||
[:mask {:id stroke-mask-id
|
||||
:x (:x bounding-box)
|
||||
:y (:y bounding-box)
|
||||
:width (:width bounding-box)
|
||||
:height (:height bounding-box)
|
||||
:maskUnits "userSpaceOnUse"}
|
||||
[:use {:xlinkHref (str "#" shape-id)
|
||||
:style #js {:fill "none" :stroke "white" :strokeWidth (* stroke-width 2)}}]
|
||||
|
||||
|
@ -49,13 +64,13 @@
|
|||
:stroke "none"}}]]))
|
||||
|
||||
(mf/defc cap-markers
|
||||
[{:keys [shape render-id]}]
|
||||
[{:keys [shape render-id index]}]
|
||||
(let [marker-id-prefix (str "marker-" render-id)
|
||||
cap-start (:stroke-cap-start shape)
|
||||
cap-end (:stroke-cap-end shape)
|
||||
|
||||
stroke-color (if (:stroke-color-gradient shape)
|
||||
(str/format "url(#%s)" (str "stroke-color-gradient_" render-id))
|
||||
(str/format "url(#%s)" (str "stroke-color-gradient_" render-id "_" index))
|
||||
(:stroke-color shape))
|
||||
|
||||
stroke-opacity (when-not (:stroke-color-gradient shape)
|
||||
|
@ -154,26 +169,35 @@
|
|||
[{:keys [shape render-id index]}]
|
||||
|
||||
(let [open-path? (and (= :path (:type shape)) (gsh/open-path? shape))]
|
||||
(cond
|
||||
(and (not open-path?)
|
||||
(= :inner (:stroke-alignment shape :center))
|
||||
(> (:stroke-width shape 0) 0))
|
||||
[:& inner-stroke-clip-path {:shape shape
|
||||
:render-id render-id
|
||||
:index index}]
|
||||
[:*
|
||||
(cond (some? (:stroke-color-gradient shape))
|
||||
(case (:type (:stroke-color-gradient shape))
|
||||
:linear [:> grad/linear-gradient #js {:id (str (name :stroke-color-gradient) "_" render-id "_" index)
|
||||
:gradient (:stroke-color-gradient shape)
|
||||
:shape shape}]
|
||||
:radial [:> grad/radial-gradient #js {:id (str (name :stroke-color-gradient) "_" render-id "_" index)
|
||||
:gradient (:stroke-color-gradient shape)
|
||||
:shape shape}]))
|
||||
(cond
|
||||
(and (not open-path?)
|
||||
(= :inner (:stroke-alignment shape :center))
|
||||
(> (:stroke-width shape 0) 0))
|
||||
[:& inner-stroke-clip-path {:shape shape
|
||||
:render-id render-id
|
||||
:index index}]
|
||||
|
||||
(and (not open-path?)
|
||||
(= :outer (:stroke-alignment shape :center))
|
||||
(> (:stroke-width shape 0) 0))
|
||||
[:& outer-stroke-mask {:shape shape
|
||||
:render-id render-id
|
||||
:index index}]
|
||||
(and (not open-path?)
|
||||
(= :outer (:stroke-alignment shape :center))
|
||||
(> (:stroke-width shape 0) 0))
|
||||
[:& outer-stroke-mask {:shape shape
|
||||
:render-id render-id
|
||||
:index index}]
|
||||
|
||||
(or (some? (:stroke-cap-start shape))
|
||||
(some? (:stroke-cap-end shape)))
|
||||
[:& cap-markers {:shape shape
|
||||
:render-id render-id
|
||||
:index index}])))
|
||||
(or (some? (:stroke-cap-start shape))
|
||||
(some? (:stroke-cap-end shape)))
|
||||
[:& cap-markers {:shape shape
|
||||
:render-id render-id
|
||||
:index index}])]))
|
||||
|
||||
;; Outer alignment: display the shape in two layers. One
|
||||
;; without stroke (only fill), and another one only with stroke
|
||||
|
@ -265,6 +289,7 @@
|
|||
|
||||
(let [child (obj/get props "children")
|
||||
shape (obj/get props "shape")
|
||||
render-id (mf/use-ctx muc/render-ctx)
|
||||
index (obj/get props "index")
|
||||
stroke-width (:stroke-width shape 0)
|
||||
stroke-style (:stroke-style shape :none)
|
||||
|
@ -286,5 +311,62 @@
|
|||
child]
|
||||
|
||||
:else
|
||||
child)))
|
||||
[:g.stroke-shape
|
||||
[:defs
|
||||
[:& stroke-defs {:shape shape :render-id render-id :index index}]]
|
||||
child])))
|
||||
|
||||
(defn build-stroke-props [position shape child value]
|
||||
(let [render-id (mf/use-ctx muc/render-ctx)
|
||||
url-fill? (or (some? (:fill-image shape))
|
||||
(= :image (:type shape))
|
||||
(> (count (:fills shape)) 1)
|
||||
(some :fill-color-gradient (:fills shape)))
|
||||
one-fill? (= (count (:fills shape)) 1)
|
||||
no-fills? (= (count (:fills shape)) 0)
|
||||
last-stroke? (= position (- (count (:strokes shape)) 1))
|
||||
|
||||
props (-> (obj/get child "props")
|
||||
(obj/clone))
|
||||
|
||||
props (cond
|
||||
(and last-stroke? url-fill?)
|
||||
;; TODO: check this zero
|
||||
(obj/set! props "fill" (str "url(#fill-0-" render-id ")"))
|
||||
|
||||
(and last-stroke? one-fill?)
|
||||
(obj/merge!
|
||||
props
|
||||
(attrs/extract-fill-attrs (get-in shape [:fills 0]) render-id 0))
|
||||
|
||||
:else
|
||||
(-> props
|
||||
(obj/without ["fill" "fillOpacity"])
|
||||
(obj/set!
|
||||
"style"
|
||||
(-> (obj/get props "style")
|
||||
(obj/set! "fill" "none")
|
||||
(obj/set! "fillOpacity" "none")))))
|
||||
|
||||
props (-> props
|
||||
(add-style
|
||||
(obj/get (attrs/extract-stroke-attrs value position) "style")))]
|
||||
props))
|
||||
|
||||
(mf/defc shape-custom-strokes
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [child (obj/get props "children")
|
||||
shape (obj/get props "shape")
|
||||
elem-name (obj/get child "type")]
|
||||
|
||||
(cond
|
||||
(seq (:strokes shape))
|
||||
[:*
|
||||
(for [[index value] (-> (d/enumerate (:strokes shape)) reverse)]
|
||||
[:& shape-custom-stroke {:shape (assoc value :points (:points shape)) :index index}
|
||||
[:> elem-name (build-stroke-props index shape child value)]])]
|
||||
|
||||
:else
|
||||
[:& shape-custom-stroke {:shape shape :index 0}
|
||||
child])))
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.main.ui.context :as muc]
|
||||
[app.util.json :as json]
|
||||
[app.util.object :as obj]
|
||||
[app.util.svg :as usvg]
|
||||
|
@ -272,6 +273,39 @@
|
|||
(for [leaf (->> shape :content :content (filter string?))]
|
||||
[:> "penpot:svg-child" {} leaf])]))]))
|
||||
|
||||
|
||||
(defn- export-fills-data [{:keys [fills]}]
|
||||
(when-let [fills (seq fills)]
|
||||
(mf/html
|
||||
[:> "penpot:fills" #js {}
|
||||
(for [[index fill] (d/enumerate fills)]
|
||||
[:> "penpot:fill"
|
||||
#js {:penpot:fill-color (if (some? (:fill-color-gradient fill))
|
||||
(str/format "url(#%s)" (str "fill-color-gradient_" (mf/use-ctx muc/render-ctx) "_" index))
|
||||
(d/name (:fill-color fill)))
|
||||
:penpot:fill-color-ref-file (d/name (:fill-color-ref-file fill))
|
||||
:penpot:fill-color-ref-id (d/name (:fill-color-ref-id fill))
|
||||
:penpot:fill-opacity (d/name (:fill-opacity fill))}])])))
|
||||
|
||||
(defn- export-strokes-data [{:keys [strokes]}]
|
||||
(when-let [strokes (seq strokes)]
|
||||
(mf/html
|
||||
[:> "penpot:strokes" #js {}
|
||||
(for [[index stroke] (d/enumerate strokes)]
|
||||
[:> "penpot:stroke"
|
||||
#js {:penpot:stroke-color (if (some? (:stroke-color-gradient stroke))
|
||||
(str/format "url(#%s)" (str "stroke-color-gradient_" (mf/use-ctx muc/render-ctx) "_" index))
|
||||
(d/name (:stroke-color stroke)))
|
||||
:penpot:stroke-color-ref-file (d/name (:stroke-color-ref-file stroke))
|
||||
:penpot:stroke-color-ref-id (d/name (:stroke-color-ref-id stroke))
|
||||
:penpot:stroke-opacity (d/name (:stroke-opacity stroke))
|
||||
:penpot:stroke-style (d/name (:stroke-style stroke))
|
||||
:penpot:stroke-width (d/name (:stroke-width stroke))
|
||||
:penpot:stroke-alignment (d/name (:stroke-alignment stroke))
|
||||
:penpot:stroke-cap-start (d/name (:stroke-cap-start stroke))
|
||||
:penpot:stroke-cap-end (d/name (:stroke-cap-end stroke))}])])))
|
||||
|
||||
|
||||
(defn- export-interactions-data [{:keys [interactions]}]
|
||||
(when-let [interactions (seq interactions)]
|
||||
(mf/html
|
||||
|
@ -300,5 +334,7 @@
|
|||
(export-exports-data shape)
|
||||
(export-svg-data shape)
|
||||
(export-interactions-data shape)
|
||||
(export-fills-data shape)
|
||||
(export-strokes-data shape)
|
||||
(export-grid-data shape)]))
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
(let [{:keys [x y width height]} (:selrect shape)
|
||||
{:keys [metadata]} shape
|
||||
|
||||
|
||||
has-image (or metadata (:fill-image shape))
|
||||
uri (if metadata
|
||||
(cfg/resolve-file-media metadata)
|
||||
|
|
|
@ -201,11 +201,11 @@
|
|||
:height (- y2 y1)})))))
|
||||
|
||||
(defn calculate-padding [shape]
|
||||
(let [stroke-width (case (:stroke-alignment shape :center)
|
||||
:center (/ (:stroke-width shape 0) 2)
|
||||
:outer (:stroke-width shape 0)
|
||||
0)
|
||||
margin (gsh/shape-stroke-margin shape stroke-width)]
|
||||
(let [stroke-width (apply max 0 (map #(case (:stroke-alignment % :center)
|
||||
:center (/ (:stroke-width % 0) 2)
|
||||
:outer (:stroke-width % 0)
|
||||
0) (:strokes shape)))
|
||||
margin (apply max 0 (map #(gsh/shape-stroke-margin % stroke-width) (:strokes shape)))]
|
||||
(+ stroke-width margin)))
|
||||
|
||||
(defn change-filter-in
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
(:require
|
||||
[app.common.data.macros :as dm]
|
||||
[app.main.ui.shapes.attrs :as attrs]
|
||||
[app.main.ui.shapes.custom-stroke :refer [shape-custom-strokes]]
|
||||
[app.main.ui.shapes.filters :as filters]
|
||||
[app.util.object :as obj]
|
||||
[debug :refer [debug?]]
|
||||
[rumext.alpha :as mf]))
|
||||
|
@ -24,9 +26,10 @@
|
|||
(mf/defc frame-clip-def
|
||||
[{:keys [shape render-id]}]
|
||||
(when (= :frame (:type shape))
|
||||
(let [{:keys [x y width height]} shape]
|
||||
(let [{:keys [x y width height]} shape
|
||||
padding (filters/calculate-padding shape)]
|
||||
[:clipPath {:id (frame-clip-id shape render-id) :class "frame-clip"}
|
||||
[:rect {:x x :y y :width width :height height}]])))
|
||||
[:rect {:x (- x padding) :y (- y padding) :width (+ width (* 2 padding)) :height (+ height (* 2 padding))}]])))
|
||||
|
||||
(mf/defc frame-thumbnail
|
||||
{::mf/wrap-props false}
|
||||
|
@ -59,8 +62,10 @@
|
|||
:width width
|
||||
:height height
|
||||
:className "frame-background"}))]
|
||||
|
||||
[:*
|
||||
[:> :rect props]
|
||||
[:& shape-custom-strokes {:shape shape}
|
||||
[:> :rect props]]
|
||||
|
||||
(for [item childs]
|
||||
[:& shape-wrapper {:shape item
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
(:require
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[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-strokes]]
|
||||
[app.util.object :as obj]
|
||||
[rumext.alpha :as mf]))
|
||||
|
||||
|
@ -29,8 +29,7 @@
|
|||
:height height}))
|
||||
path? (some? (.-d props))]
|
||||
|
||||
[:g
|
||||
[:& shape-custom-stroke {:shape shape}
|
||||
(if path?
|
||||
[:> :path props]
|
||||
[:> :rect props])]]))
|
||||
[:& shape-custom-strokes {:shape shape}
|
||||
(if path?
|
||||
[:> :path props]
|
||||
[:> :rect props])]))
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
(:require
|
||||
[app.common.logging :as log]
|
||||
[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-strokes]]
|
||||
[app.util.object :as obj]
|
||||
[app.util.path.format :as upf]
|
||||
[rumext.alpha :as mf]))
|
||||
|
@ -31,5 +31,5 @@
|
|||
props (-> (attrs/extract-style-attrs shape)
|
||||
(obj/set! "d" pdata))]
|
||||
|
||||
[:& shape-custom-stroke {:shape shape}
|
||||
[:& shape-custom-strokes {:shape shape}
|
||||
[:> :path props]]))
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
(:require
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[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-strokes]]
|
||||
[app.util.object :as obj]
|
||||
[rumext.alpha :as mf]))
|
||||
|
||||
|
@ -29,7 +29,7 @@
|
|||
|
||||
path? (some? (.-d props))]
|
||||
|
||||
[:& shape-custom-stroke {:shape shape}
|
||||
[:& shape-custom-strokes {:shape shape}
|
||||
(if path?
|
||||
[:> :path props]
|
||||
[:> :rect props])]))
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
[app.main.ui.shapes.fills :as fills]
|
||||
[app.main.ui.shapes.filters :as filters]
|
||||
[app.main.ui.shapes.frame :as frame]
|
||||
[app.main.ui.shapes.gradients :as grad]
|
||||
[app.main.ui.shapes.svg-defs :as defs]
|
||||
[app.util.object :as obj]
|
||||
[rumext.alpha :as mf]))
|
||||
|
@ -62,7 +61,6 @@
|
|||
[:defs
|
||||
[:& defs/svg-defs {:shape shape :render-id render-id}]
|
||||
[:& filters/filters {:shape shape :filter-id filter-id}]
|
||||
[:& grad/gradient {:shape shape :attr :stroke-color-gradient}]
|
||||
[:& fills/fills {:shape shape :render-id render-id}]
|
||||
[:& frame/frame-clip-def {:shape shape :render-id render-id}]]
|
||||
children]]))
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
[app.common.geom.shapes :as gsh]
|
||||
[app.main.ui.context :as muc]
|
||||
[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-strokes]]
|
||||
[app.main.ui.shapes.gradients :as grad]
|
||||
[app.util.object :as obj]
|
||||
[rumext.alpha :as mf]))
|
||||
|
@ -21,7 +21,7 @@
|
|||
{::mf/wrap-props false
|
||||
::mf/wrap [mf/memo]}
|
||||
[props]
|
||||
|
||||
|
||||
(let [render-id (mf/use-ctx muc/render-ctx)
|
||||
{:keys [x y width height position-data] :as shape} (obj/get props "shape")
|
||||
transform (str (gsh/transform-matrix shape))
|
||||
|
@ -60,7 +60,7 @@
|
|||
:direction (if (:rtl data) "rtl" "ltr")
|
||||
:whiteSpace "pre"}
|
||||
(obj/set! "fill" (str "url(#fill-" index "-" render-id ")")))})]
|
||||
[:& shape-custom-stroke {:shape shape :index index}
|
||||
[:& shape-custom-strokes {:shape shape}
|
||||
[:> :text props (:text data)]]))]]))
|
||||
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
(def type->options
|
||||
{:multiple [:fill :stroke :image :text :shadow :blur]
|
||||
:frame [:layout :fill]
|
||||
:frame [:layout :fill :stroke]
|
||||
:group [:layout :svg]
|
||||
:rect [:layout :fill :stroke :shadow :blur :svg]
|
||||
:circle [:layout :fill :stroke :shadow :blur :svg]
|
||||
|
|
|
@ -30,10 +30,13 @@
|
|||
color (-> shape shape->color uc/color->background)]
|
||||
(str/format "%spx %s %s" width style color)))
|
||||
|
||||
(defn has-stroke? [{:keys [stroke-style]}]
|
||||
(and stroke-style
|
||||
(and (not= stroke-style :none)
|
||||
(not= stroke-style :svg))))
|
||||
(defn has-stroke? [shape]
|
||||
(let [stroke-style (:stroke-style shape)]
|
||||
(or
|
||||
(and stroke-style
|
||||
(and (not= stroke-style :none)
|
||||
(not= stroke-style :svg)))
|
||||
(seq (:strokes shape)))))
|
||||
|
||||
(defn copy-stroke-data [shape]
|
||||
(cg/generate-css-props
|
||||
|
@ -80,6 +83,11 @@
|
|||
[:& copy-button {:data (copy-stroke-data (first shapes))}])]
|
||||
|
||||
(for [shape shapes]
|
||||
[:& stroke-block {:key (str "stroke-color-" (:id shape))
|
||||
:shape shape
|
||||
:locale locale}])])))
|
||||
(if (seq (:strokes shape))
|
||||
(for [value (:strokes shape [])]
|
||||
[:& stroke-block {:key (str "stroke-color-" (:id shape))
|
||||
:shape value
|
||||
:locale locale}])
|
||||
[:& stroke-block {:key (str "stroke-color-" (:id shape))
|
||||
:shape shape
|
||||
:locale locale}]))])))
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
select-color
|
||||
(fn [event]
|
||||
(if (kbd/alt? event)
|
||||
(st/emit! (mdc/change-stroke ids-with-children (merge uc/empty-color color)))
|
||||
(st/emit! (mdc/change-stroke ids-with-children (merge uc/empty-color color) 0))
|
||||
(st/emit! (mdc/change-fill ids-with-children (merge uc/empty-color color) 0))))]
|
||||
|
||||
[:div.color-cell {:on-click select-color}
|
||||
|
|
|
@ -803,7 +803,7 @@
|
|||
(fn [_ event]
|
||||
(let [ids (wsh/lookup-selected @st/state)]
|
||||
(if (kbd/alt? event)
|
||||
(st/emit! (dc/change-stroke ids color))
|
||||
(st/emit! (dc/change-stroke ids color 0))
|
||||
(st/emit! (dc/change-fill ids color 0)))))
|
||||
|
||||
rename-color
|
||||
|
|
|
@ -78,7 +78,6 @@
|
|||
[:div.element-options
|
||||
[:& interactions-menu {:shape (first shapes)}]]]]]])
|
||||
|
||||
|
||||
;; TODO: this need optimizations, selected-objects and
|
||||
;; selected-objects-with-children are derefed always but they only
|
||||
;; need on multiple selection in majority of cases
|
||||
|
@ -93,6 +92,8 @@
|
|||
file-id (mf/use-ctx ctx/current-file-id)
|
||||
shapes (mf/deref refs/selected-objects)
|
||||
shapes-with-children (mf/deref refs/selected-shapes-with-children)]
|
||||
;; TODO: review performance]
|
||||
|
||||
[:& options-content {:shapes shapes
|
||||
:selected selected
|
||||
:shapes-with-children shapes-with-children
|
||||
|
|
|
@ -98,7 +98,17 @@
|
|||
(mf/deps ids)
|
||||
(fn [event]
|
||||
(let [value (-> event dom/get-target dom/checked?)]
|
||||
(st/emit! (dc/change-hide-fill-on-export ids (not value))))))]
|
||||
(st/emit! (dc/change-hide-fill-on-export ids (not value))))))
|
||||
|
||||
disable-drag (mf/use-state false)
|
||||
|
||||
select-all (fn [event]
|
||||
(when (not @disable-drag)
|
||||
(dom/select-text! (dom/get-target event)))
|
||||
(reset! disable-drag true))
|
||||
|
||||
on-blur (fn [_]
|
||||
(reset! disable-drag false))]
|
||||
|
||||
(mf/use-layout-effect
|
||||
(mf/deps hide-fill-on-export?)
|
||||
|
@ -139,7 +149,10 @@
|
|||
:on-change (on-change index)
|
||||
:on-reorder (on-reorder index)
|
||||
:on-detach (on-detach index)
|
||||
:on-remove (on-remove index)}])])
|
||||
:on-remove (on-remove index)
|
||||
:disable-drag disable-drag
|
||||
:select-all select-all
|
||||
:on-blur on-blur}])])
|
||||
|
||||
(when (or (= type :frame)
|
||||
(and (= type :multiple) (some? hide-fill-on-export?)))
|
||||
|
|
|
@ -8,20 +8,19 @@
|
|||
(:require
|
||||
[app.common.colors :as clr]
|
||||
[app.common.data :as d]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.colors :as dc]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.dropdown :refer [dropdown]]
|
||||
[app.main.ui.components.numeric-input :refer [numeric-input]]
|
||||
[app.main.ui.hooks :as h]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.main.ui.workspace.sidebar.options.rows.color-row :refer [color-row]]
|
||||
[app.main.ui.workspace.sidebar.options.rows.stroke-row :refer [stroke-row]]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[cuerdas.core :as str]
|
||||
[rumext.alpha :as mf]))
|
||||
|
||||
(def stroke-attrs
|
||||
[:stroke-style
|
||||
[:strokes
|
||||
:stroke-style
|
||||
:stroke-alignment
|
||||
:stroke-width
|
||||
:stroke-color
|
||||
|
@ -32,36 +31,6 @@
|
|||
:stroke-cap-start
|
||||
:stroke-cap-end])
|
||||
|
||||
(defn- width->string [width]
|
||||
(if (= width :multiple)
|
||||
""
|
||||
(str (or width 1))))
|
||||
|
||||
(defn- enum->string [value]
|
||||
(if (= value :multiple)
|
||||
""
|
||||
(pr-str value)))
|
||||
|
||||
(defn- stroke-cap-names []
|
||||
[[nil (tr "workspace.options.stroke-cap.none") false]
|
||||
[:line-arrow (tr "workspace.options.stroke-cap.line-arrow") true]
|
||||
[:triangle-arrow (tr "workspace.options.stroke-cap.triangle-arrow") false]
|
||||
[:square-marker (tr "workspace.options.stroke-cap.square-marker") false]
|
||||
[:circle-marker (tr "workspace.options.stroke-cap.circle-marker") false]
|
||||
[:diamond-marker (tr "workspace.options.stroke-cap.diamond-marker") false]
|
||||
[:round (tr "workspace.options.stroke-cap.round") true]
|
||||
[:square (tr "workspace.options.stroke-cap.square") false]])
|
||||
|
||||
(defn- value->name [value]
|
||||
(if (= value :multiple)
|
||||
"--"
|
||||
(-> (d/seek #(= (first %) value) (stroke-cap-names))
|
||||
(second))))
|
||||
|
||||
(defn- value->img [value]
|
||||
(when (and value (not= value :multiple))
|
||||
(str "images/cap-" (name value) ".svg")))
|
||||
|
||||
(mf/defc stroke-menu
|
||||
{::mf/wrap [#(mf/memo' % (mf/check-props ["ids" "values" "type" "show-caps"]))]}
|
||||
[{:keys [ids type values show-caps] :as props}]
|
||||
|
@ -70,65 +39,62 @@
|
|||
:group (tr "workspace.options.group-stroke")
|
||||
(tr "workspace.options.stroke"))
|
||||
|
||||
show-options (not= (or (:stroke-style values) :none) :none)
|
||||
show-caps (and show-caps
|
||||
(not (#{:inner :outer} (:stroke-alignment values))))
|
||||
|
||||
start-caps-state (mf/use-state {:open? false
|
||||
:top 0
|
||||
:left 0})
|
||||
end-caps-state (mf/use-state {:open? false
|
||||
:top 0
|
||||
:left 0})
|
||||
|
||||
current-stroke-color {:color (:stroke-color values)
|
||||
:opacity (:stroke-opacity values)
|
||||
:id (:stroke-color-ref-id values)
|
||||
:file-id (:stroke-color-ref-file values)
|
||||
:gradient (:stroke-color-gradient values)}
|
||||
|
||||
handle-change-stroke-color
|
||||
(mf/use-callback
|
||||
(mf/deps ids)
|
||||
(fn [color]
|
||||
(let [remove-multiple (fn [[_ value]] (not= value :multiple))
|
||||
color (into {} (filter remove-multiple) color)]
|
||||
(st/emit! (dc/change-stroke ids color)))))
|
||||
(mf/deps ids)
|
||||
(fn [index]
|
||||
(fn [color]
|
||||
(st/emit! (dc/change-stroke ids color index)))))
|
||||
|
||||
handle-remove
|
||||
(mf/use-callback
|
||||
(mf/deps ids)
|
||||
(fn [index]
|
||||
(fn []
|
||||
(st/emit! (dc/remove-stroke ids index)))))
|
||||
|
||||
handle-remove-remove-all
|
||||
(fn [_]
|
||||
(st/emit! (dc/remove-all-strokes ids)))
|
||||
|
||||
handle-detach
|
||||
(mf/use-callback
|
||||
(mf/deps ids)
|
||||
(fn []
|
||||
(let [remove-multiple (fn [[_ value]] (not= value :multiple))
|
||||
current-stroke-color (-> (into {} (filter remove-multiple) current-stroke-color)
|
||||
(assoc :id nil :file-id nil))]
|
||||
(st/emit! (dc/change-stroke ids current-stroke-color)))))
|
||||
(mf/deps ids)
|
||||
(fn [index]
|
||||
(fn [color]
|
||||
(let [color (-> color
|
||||
(assoc :id nil :file-id nil))]
|
||||
(st/emit! (dc/change-stroke ids color index))))))
|
||||
|
||||
handle-reorder
|
||||
(mf/use-callback
|
||||
(mf/deps ids)
|
||||
(fn [new-index]
|
||||
(fn [index]
|
||||
(st/emit! (dc/reorder-strokes ids index new-index)))))
|
||||
|
||||
on-stroke-style-change
|
||||
(fn [event]
|
||||
(let [value (-> (dom/get-target event)
|
||||
(dom/get-value)
|
||||
(d/read-string))]
|
||||
(st/emit! (dch/update-shapes ids #(assoc % :stroke-style value)))))
|
||||
(fn [index]
|
||||
(fn [event]
|
||||
(let [value (-> (dom/get-target event)
|
||||
(dom/get-value)
|
||||
(d/read-string))]
|
||||
(st/emit! (dc/change-stroke ids {:stroke-style value} index)))))
|
||||
|
||||
on-stroke-alignment-change
|
||||
(fn [event]
|
||||
(let [value (-> (dom/get-target event)
|
||||
(dom/get-value)
|
||||
(d/read-string))]
|
||||
(when-not (str/empty? value)
|
||||
(st/emit! (dch/update-shapes ids #(assoc % :stroke-alignment value))))))
|
||||
(fn [index]
|
||||
(fn [event]
|
||||
(let [value (-> (dom/get-target event)
|
||||
(dom/get-value)
|
||||
(d/read-string))]
|
||||
(when-not (str/empty? value)
|
||||
(st/emit! (dc/change-stroke ids {:stroke-alignment value} index))))))
|
||||
|
||||
on-stroke-width-change
|
||||
(fn [value]
|
||||
(when-not (str/empty? value)
|
||||
(st/emit! (dch/update-shapes ids #(assoc % :stroke-width value)))))
|
||||
|
||||
update-cap-attr
|
||||
(fn [& kvs]
|
||||
#(if (= :path (:type %))
|
||||
(apply (partial assoc %) kvs)
|
||||
%))
|
||||
(fn [index]
|
||||
(fn [value]
|
||||
(when-not (str/empty? value)
|
||||
(st/emit! (dc/change-stroke ids {:stroke-width value} index)))))
|
||||
|
||||
open-caps-select
|
||||
(fn [caps-state]
|
||||
|
@ -146,8 +112,8 @@
|
|||
(:left rect)
|
||||
(- (:width window-size) 205))]
|
||||
(swap! caps-state assoc :open? true
|
||||
:left left
|
||||
:top top))))
|
||||
:left left
|
||||
:top top))))
|
||||
|
||||
close-caps-select
|
||||
(fn [caps-state]
|
||||
|
@ -155,119 +121,72 @@
|
|||
(swap! caps-state assoc :open? false)))
|
||||
|
||||
on-stroke-cap-start-change
|
||||
(fn [value]
|
||||
(st/emit! (dch/update-shapes ids (update-cap-attr :stroke-cap-start value))))
|
||||
(fn [index value]
|
||||
(st/emit! (dc/change-stroke ids {:stroke-cap-start value} index)))
|
||||
|
||||
on-stroke-cap-end-change
|
||||
(fn [value]
|
||||
(st/emit! (dch/update-shapes ids (update-cap-attr :stroke-cap-end value))))
|
||||
(fn [index value]
|
||||
(st/emit! (dc/change-stroke ids {:stroke-cap-end value} index)))
|
||||
|
||||
on-stroke-cap-switch
|
||||
(fn [_]
|
||||
(let [stroke-cap-start (:stroke-cap-start values)
|
||||
stroke-cap-end (:stroke-cap-end values)]
|
||||
(fn [index]
|
||||
(let [stroke-cap-start (get-in values [:strokes index :stroke-cap-start])
|
||||
stroke-cap-end (get-in values [:strokes index :stroke-cap-end])]
|
||||
(when (and (not= stroke-cap-start :multiple)
|
||||
(not= stroke-cap-end :multiple))
|
||||
(st/emit! (dch/update-shapes ids (update-cap-attr
|
||||
:stroke-cap-start stroke-cap-end
|
||||
:stroke-cap-end stroke-cap-start))))))
|
||||
|
||||
(st/emit! (dc/change-stroke ids {:stroke-cap-start stroke-cap-end
|
||||
:stroke-cap-end stroke-cap-start} index)))))
|
||||
on-add-stroke
|
||||
(fn [_]
|
||||
(st/emit! (dch/update-shapes ids #(assoc %
|
||||
:stroke-style :solid
|
||||
:stroke-color clr/black
|
||||
:stroke-opacity 1
|
||||
:stroke-width 1))))
|
||||
(st/emit! (dc/add-stroke ids {:stroke-style :solid
|
||||
:stroke-color clr/black
|
||||
:stroke-opacity 1
|
||||
:stroke-width 1})))
|
||||
|
||||
on-del-stroke
|
||||
(fn [_]
|
||||
(st/emit! (dch/update-shapes ids #(assoc % :stroke-style :none))))]
|
||||
disable-drag (mf/use-state false)
|
||||
|
||||
(if show-options
|
||||
[:div.element-set
|
||||
[:div.element-set-title
|
||||
[:span label]
|
||||
[:div.add-page {:on-click on-del-stroke} i/minus]]
|
||||
select-all (fn [event]
|
||||
(when (not @disable-drag)
|
||||
(dom/select-text! (dom/get-target event)))
|
||||
(reset! disable-drag true))
|
||||
|
||||
[:div.element-set-content
|
||||
;; Stroke Color
|
||||
[:& color-row {:color current-stroke-color
|
||||
:title (tr "workspace.options.stroke-color")
|
||||
:on-change handle-change-stroke-color
|
||||
:on-detach handle-detach}]
|
||||
on-blur (fn [_]
|
||||
(reset! disable-drag false))]
|
||||
|
||||
;; Stroke Width, Alignment & Style
|
||||
[:div.row-flex
|
||||
[:div.input-element
|
||||
{:class (dom/classnames :pixels (not= (:stroke-width values) :multiple))
|
||||
:title (tr "workspace.options.stroke-width")}
|
||||
[:div.element-set
|
||||
[:div.element-set-title
|
||||
[:span label]
|
||||
[:div.add-page {:on-click on-add-stroke} i/close]]
|
||||
|
||||
[:> numeric-input
|
||||
{:min 0
|
||||
:value (-> (:stroke-width values) width->string)
|
||||
:precision 2
|
||||
:placeholder (tr "settings.multiple")
|
||||
:on-change on-stroke-width-change}]]
|
||||
[:div.element-set-content
|
||||
(cond
|
||||
(= :multiple (:strokes values))
|
||||
[:div.element-set-options-group
|
||||
[:div.element-set-label (tr "settings.multiple")]
|
||||
[:div.element-set-actions
|
||||
[:div.element-set-actions-button {:on-click handle-remove-remove-all}
|
||||
i/minus]]]
|
||||
|
||||
[:select#style.input-select {:value (enum->string (:stroke-alignment values))
|
||||
:on-change on-stroke-alignment-change}
|
||||
(when (= (:stroke-alignment values) :multiple)
|
||||
[:option {:value ""} "--"])
|
||||
[:option {:value ":center"} (tr "workspace.options.stroke.center")]
|
||||
[:option {:value ":inner"} (tr "workspace.options.stroke.inner")]
|
||||
[:option {:value ":outer"} (tr "workspace.options.stroke.outer")]]
|
||||
|
||||
[:select#style.input-select {:value (enum->string (:stroke-style values))
|
||||
:on-change on-stroke-style-change}
|
||||
(when (= (:stroke-style values) :multiple)
|
||||
[:option {:value ""} "--"])
|
||||
[:option {:value ":solid"} (tr "workspace.options.stroke.solid")]
|
||||
[:option {:value ":dotted"} (tr "workspace.options.stroke.dotted")]
|
||||
[:option {:value ":dashed"} (tr "workspace.options.stroke.dashed")]
|
||||
[:option {:value ":mixed"} (tr "workspace.options.stroke.mixed")]]]
|
||||
|
||||
;; Stroke Caps
|
||||
(when show-caps
|
||||
[:div.row-flex
|
||||
[:div.cap-select {:tab-index 0 ;; tab-index to make the element focusable
|
||||
:on-click (open-caps-select start-caps-state)}
|
||||
(value->name (:stroke-cap-start values))
|
||||
[:span.cap-select-button
|
||||
i/arrow-down]]
|
||||
[:& dropdown {:show (:open? @start-caps-state)
|
||||
:on-close (close-caps-select start-caps-state)}
|
||||
[:ul.dropdown.cap-select-dropdown {:style {:top (:top @start-caps-state)
|
||||
:left (:left @start-caps-state)}}
|
||||
(for [[value label separator] (stroke-cap-names)]
|
||||
(let [img (value->img value)]
|
||||
[:li {:class (dom/classnames :separator separator)
|
||||
:on-click #(on-stroke-cap-start-change value)}
|
||||
(when img [:img {:src (value->img value)}])
|
||||
label]))]]
|
||||
|
||||
[:div.element-set-actions-button {:on-click on-stroke-cap-switch}
|
||||
i/switch]
|
||||
|
||||
[:div.cap-select {:tab-index 0
|
||||
:on-click (open-caps-select end-caps-state)}
|
||||
(value->name (:stroke-cap-end values))
|
||||
[:span.cap-select-button
|
||||
i/arrow-down]]
|
||||
[:& dropdown {:show (:open? @end-caps-state)
|
||||
:on-close (close-caps-select end-caps-state)}
|
||||
[:ul.dropdown.cap-select-dropdown {:style {:top (:top @end-caps-state)
|
||||
:left (:left @end-caps-state)}}
|
||||
(for [[value label separator] (stroke-cap-names)]
|
||||
(let [img (value->img value)]
|
||||
[:li {:class (dom/classnames :separator separator)
|
||||
:on-click #(on-stroke-cap-end-change value)}
|
||||
(when img [:img {:src (value->img value)}])
|
||||
label]))]]])]]
|
||||
|
||||
;; NO STROKE
|
||||
[:div.element-set
|
||||
[:div.element-set-title
|
||||
[:span label]
|
||||
[:div.add-page {:on-click on-add-stroke} i/close]]])))
|
||||
|
||||
(seq (:strokes values))
|
||||
[:& h/sortable-container {}
|
||||
(for [[index value] (d/enumerate (:strokes values []))]
|
||||
[:& stroke-row {:stroke value
|
||||
:title (tr "workspace.options.stroke-color")
|
||||
:index index
|
||||
:show-caps show-caps
|
||||
:on-color-change handle-change-stroke-color
|
||||
:on-color-detach handle-detach
|
||||
:on-stroke-width-change on-stroke-width-change
|
||||
:on-stroke-style-change on-stroke-style-change
|
||||
:on-stroke-alignment-change on-stroke-alignment-change
|
||||
:open-caps-select open-caps-select
|
||||
:close-caps-select close-caps-select
|
||||
:on-stroke-cap-start-change on-stroke-cap-start-change
|
||||
:on-stroke-cap-end-change on-stroke-cap-end-change
|
||||
:on-stroke-cap-switch on-stroke-cap-switch
|
||||
:on-remove handle-remove
|
||||
:on-reorder (handle-reorder index)
|
||||
:disable-drag disable-drag
|
||||
:select-all select-all
|
||||
:on-blur on-blur}])])]]))
|
||||
|
|
|
@ -61,12 +61,11 @@
|
|||
(if (= v :multiple) nil v))
|
||||
|
||||
(mf/defc color-row
|
||||
[{:keys [index color disable-gradient disable-opacity on-change on-reorder on-detach on-open on-close title on-remove]}]
|
||||
[{:keys [index color disable-gradient disable-opacity on-change on-reorder on-detach on-open on-close title on-remove disable-drag select-all on-blur]}]
|
||||
(let [current-file-id (mf/use-ctx ctx/current-file-id)
|
||||
file-colors (mf/deref refs/workspace-file-colors)
|
||||
shared-libs (mf/deref refs/workspace-libraries)
|
||||
hover-detach (mf/use-state false)
|
||||
disable-drag (mf/use-state false)
|
||||
|
||||
get-color-name (fn [{:keys [id file-id]}]
|
||||
(let [src-colors (if (= file-id current-file-id)
|
||||
|
@ -107,14 +106,6 @@
|
|||
handle-opacity-change (fn [value]
|
||||
(change-opacity (/ value 100)))
|
||||
|
||||
select-all (fn [event]
|
||||
(when (not @disable-drag)
|
||||
(dom/select-text! (dom/get-target event)))
|
||||
(reset! disable-drag true))
|
||||
|
||||
on-blur (fn [_]
|
||||
(reset! disable-drag false))
|
||||
|
||||
handle-click-color (mf/use-callback
|
||||
(mf/deps color)
|
||||
(color-picker-callback color
|
||||
|
@ -194,6 +185,7 @@
|
|||
[:> numeric-input {:value (-> color :opacity opacity->string)
|
||||
:placeholder (tr "settings.multiple")
|
||||
:on-click select-all
|
||||
:on-blur on-blur
|
||||
:on-change handle-opacity-change
|
||||
:min 0
|
||||
:max 100}]])])
|
||||
|
|
|
@ -0,0 +1,159 @@
|
|||
;; 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/.
|
||||
;;
|
||||
;; Copyright (c) UXBOX Labs SL
|
||||
|
||||
(ns app.main.ui.workspace.sidebar.options.rows.stroke-row
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.main.ui.components.dropdown :refer [dropdown]]
|
||||
[app.main.ui.components.numeric-input :refer [numeric-input]]
|
||||
[app.main.ui.hooks :as h]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.main.ui.workspace.sidebar.options.rows.color-row :refer [color-row]]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[rumext.alpha :as mf]))
|
||||
|
||||
(defn- width->string [width]
|
||||
(if (= width :multiple)
|
||||
""
|
||||
(str (or width 1))))
|
||||
|
||||
(defn- enum->string [value]
|
||||
(if (= value :multiple)
|
||||
""
|
||||
(pr-str value)))
|
||||
|
||||
(defn- stroke-cap-names []
|
||||
[[nil (tr "workspace.options.stroke-cap.none") false]
|
||||
[:line-arrow (tr "workspace.options.stroke-cap.line-arrow") true]
|
||||
[:triangle-arrow (tr "workspace.options.stroke-cap.triangle-arrow") false]
|
||||
[:square-marker (tr "workspace.options.stroke-cap.square-marker") false]
|
||||
[:circle-marker (tr "workspace.options.stroke-cap.circle-marker") false]
|
||||
[:diamond-marker (tr "workspace.options.stroke-cap.diamond-marker") false]
|
||||
[:round (tr "workspace.options.stroke-cap.round") true]
|
||||
[:square (tr "workspace.options.stroke-cap.square") false]])
|
||||
|
||||
(defn- value->img [value]
|
||||
(when (and value (not= value :multiple))
|
||||
(str "images/cap-" (name value) ".svg")))
|
||||
|
||||
(defn- value->name [value]
|
||||
(if (= value :multiple)
|
||||
"--"
|
||||
(-> (d/seek #(= (first %) value) (stroke-cap-names))
|
||||
(second))))
|
||||
|
||||
(mf/defc stroke-row
|
||||
[{:keys [index stroke title show-caps on-color-change on-reorder on-color-detach on-remove on-stroke-width-change on-stroke-style-change on-stroke-alignment-change open-caps-select close-caps-select on-stroke-cap-start-change on-stroke-cap-end-change on-stroke-cap-switch disable-drag select-all on-blur]}]
|
||||
(let [start-caps-state (mf/use-state {:open? false
|
||||
:top 0
|
||||
:left 0})
|
||||
end-caps-state (mf/use-state {:open? false
|
||||
:top 0
|
||||
:left 0})
|
||||
on-drop
|
||||
(fn [_ data]
|
||||
(on-reorder (:index data)))
|
||||
|
||||
[dprops dref] (if (some? on-reorder)
|
||||
(h/use-sortable
|
||||
:data-type "penpot/stroke-row"
|
||||
:on-drop on-drop
|
||||
:disabled @disable-drag
|
||||
:detect-center? false
|
||||
:data {:id (str "stroke-row-" index)
|
||||
:index index
|
||||
:name (str "Border row" index)})
|
||||
[nil nil])]
|
||||
|
||||
[:div.border-data {:class (dom/classnames
|
||||
:dnd-over-top (= (:over dprops) :top)
|
||||
:dnd-over-bot (= (:over dprops) :bot))
|
||||
:ref dref}
|
||||
;; Stroke Color
|
||||
[:& color-row {:color {:color (:stroke-color stroke)
|
||||
:opacity (:stroke-opacity stroke)
|
||||
:id (:stroke-color-ref-id stroke)
|
||||
:file-id (:stroke-color-ref-file stroke)
|
||||
:gradient (:stroke-color-gradient stroke)}
|
||||
:index index
|
||||
:title title
|
||||
:on-change (on-color-change index)
|
||||
:on-detach (on-color-detach index)
|
||||
:on-remove (on-remove index)
|
||||
:disable-drag disable-drag
|
||||
:select-all select-all
|
||||
:on-blur on-blur}]
|
||||
|
||||
;; Stroke Width, Alignment & Style
|
||||
[:div.row-flex
|
||||
[:div.input-element
|
||||
{:class (dom/classnames :pixels (not= (:stroke-width stroke) :multiple))
|
||||
:title (tr "workspace.options.stroke-width")}
|
||||
|
||||
[:> numeric-input
|
||||
{:min 0
|
||||
:value (-> (:stroke-width stroke) width->string)
|
||||
:precision 2
|
||||
:placeholder (tr "settings.multiple")
|
||||
:on-change (on-stroke-width-change index)
|
||||
:on-click select-all
|
||||
:on-blur on-blur}]]
|
||||
|
||||
[:select#style.input-select {:value (enum->string (:stroke-alignment stroke))
|
||||
:on-change (on-stroke-alignment-change index)}
|
||||
(when (= (:stroke-alignment stroke) :multiple)
|
||||
[:option {:value ""} "--"])
|
||||
[:option {:value ":center"} (tr "workspace.options.stroke.center")]
|
||||
[:option {:value ":inner"} (tr "workspace.options.stroke.inner")]
|
||||
[:option {:value ":outer"} (tr "workspace.options.stroke.outer")]]
|
||||
|
||||
[:select#style.input-select {:value (enum->string (:stroke-style stroke))
|
||||
:on-change (on-stroke-style-change index)}
|
||||
(when (= (:stroke-style stroke) :multiple)
|
||||
[:option {:value ""} "--"])
|
||||
[:option {:value ":solid"} (tr "workspace.options.stroke.solid")]
|
||||
[:option {:value ":dotted"} (tr "workspace.options.stroke.dotted")]
|
||||
[:option {:value ":dashed"} (tr "workspace.options.stroke.dashed")]
|
||||
[:option {:value ":mixed"} (tr "workspace.options.stroke.mixed")]]]
|
||||
|
||||
;; Stroke Caps
|
||||
(when show-caps
|
||||
[:div.row-flex
|
||||
[:div.cap-select {:tab-index 0 ;; tab-index to make the element focusable
|
||||
:on-click (open-caps-select start-caps-state)}
|
||||
(value->name (:stroke-cap-start stroke))
|
||||
[:span.cap-select-button
|
||||
i/arrow-down]]
|
||||
[:& dropdown {:show (:open? @start-caps-state)
|
||||
:on-close (close-caps-select start-caps-state)}
|
||||
[:ul.dropdown.cap-select-dropdown {:style {:top (:top @start-caps-state)
|
||||
:left (:left @start-caps-state)}}
|
||||
(for [[value label separator] (stroke-cap-names)]
|
||||
(let [img (value->img value)]
|
||||
[:li {:class (dom/classnames :separator separator)
|
||||
:on-click #(on-stroke-cap-start-change index value)}
|
||||
(when img [:img {:src (value->img value)}])
|
||||
label]))]]
|
||||
|
||||
[:div.element-set-actions-button {:on-click #(on-stroke-cap-switch index)}
|
||||
i/switch]
|
||||
|
||||
[:div.cap-select {:tab-index 0
|
||||
:on-click (open-caps-select end-caps-state)}
|
||||
(value->name (:stroke-cap-end stroke))
|
||||
[:span.cap-select-button
|
||||
i/arrow-down]]
|
||||
[:& dropdown {:show (:open? @end-caps-state)
|
||||
:on-close (close-caps-select end-caps-state)}
|
||||
[:ul.dropdown.cap-select-dropdown {:style {:top (:top @end-caps-state)
|
||||
:left (:left @end-caps-state)}}
|
||||
(for [[value label separator] (stroke-cap-names)]
|
||||
(let [img (value->img value)]
|
||||
[:li {:class (dom/classnames :separator separator)
|
||||
:on-click #(on-stroke-cap-end-change index value)}
|
||||
(when img [:img {:src (value->img value)}])
|
||||
label]))]]])]))
|
|
@ -8,6 +8,7 @@
|
|||
(:require
|
||||
[app.common.attrs :as attrs]
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.pages.common :as cpc]
|
||||
[app.common.text :as txt]
|
||||
[app.main.ui.hooks :as hooks]
|
||||
|
@ -34,7 +35,7 @@
|
|||
:fill :shape
|
||||
:shadow :children
|
||||
:blur :children
|
||||
:stroke :children
|
||||
:stroke :shape
|
||||
:text :children}
|
||||
|
||||
:group
|
||||
|
@ -205,6 +206,7 @@
|
|||
(let [shapes (unchecked-get props "shapes")
|
||||
shapes-with-children (unchecked-get props "shapes-with-children")
|
||||
objects (->> shapes-with-children (group-by :id) (d/mapm (fn [_ v] (first v))))
|
||||
show-caps (some #(and (= :path (:type %)) (gsh/open-path? %)) shapes)
|
||||
|
||||
;; Selrect/points only used for measures and it's the one that changes the most. We separate it
|
||||
;; so we can memoize it
|
||||
|
@ -249,14 +251,14 @@
|
|||
(when-not (empty? fill-ids)
|
||||
[:& fill-menu {:type type :ids fill-ids :values fill-values}])
|
||||
|
||||
(when-not (empty? stroke-ids)
|
||||
[:& stroke-menu {:type type :ids stroke-ids :show-caps show-caps :values stroke-values}])
|
||||
|
||||
(when-not (empty? shadow-ids)
|
||||
[:& shadow-menu {:type type :ids shadow-ids :values shadow-values}])
|
||||
|
||||
(when-not (empty? blur-ids)
|
||||
[:& blur-menu {:type type :ids blur-ids :values blur-values}])
|
||||
|
||||
(when-not (empty? stroke-ids)
|
||||
[:& stroke-menu {:type type :ids stroke-ids :show-caps true :values stroke-values}])
|
||||
|
||||
(when-not (empty? text-ids)
|
||||
[:& ot/text-menu {:type type :ids text-ids :values text-values}])]))
|
||||
|
|
|
@ -208,7 +208,11 @@
|
|||
(reduce add-attrs node-attrs))
|
||||
|
||||
(= type :frame)
|
||||
(let [svg-node (->> node :content (d/seek #(= "frame-background" (get-in % [:attrs :class]))))]
|
||||
(let [;; The nodes with the "frame-background" class can have some anidation depending on the strokes they have
|
||||
g-nodes (find-all-nodes node :g)
|
||||
defs-nodes (flatten (map #(find-all-nodes % :defs) g-nodes))
|
||||
rect-nodes (flatten [(map #(find-all-nodes % :rect) defs-nodes) (map #(find-all-nodes % :rect) g-nodes)])
|
||||
svg-node (d/seek #(= "frame-background" (get-in % [:attrs :class])) rect-nodes)]
|
||||
(merge (add-attrs {} (:attrs svg-node)) node-attrs))
|
||||
|
||||
(= type :svg-raw)
|
||||
|
@ -685,20 +689,48 @@
|
|||
|
||||
props)))
|
||||
|
||||
(defn add-fills
|
||||
[props node svg-data]
|
||||
(let [fills (-> node
|
||||
(find-node :defs)
|
||||
(find-node :pattern)
|
||||
(find-node :g)
|
||||
(find-all-nodes :rect)
|
||||
(reverse))
|
||||
fills (if (= 0 (count fills))
|
||||
[(add-fill {} node svg-data)]
|
||||
(map #(add-fill {} node (get-svg-data :rect %)) fills))]
|
||||
(-> props
|
||||
(assoc :fills fills))))
|
||||
(defn parse-fills
|
||||
[node svg-data]
|
||||
(let [fills-node (get-data node :penpot:fills)
|
||||
fills (->> (find-all-nodes fills-node :penpot:fill)
|
||||
(mapv (fn [fill-node]
|
||||
{:fill-color (when (not (str/starts-with? (get-meta fill-node :fill-color) "url"))
|
||||
(get-meta fill-node :fill-color))
|
||||
:fill-color-gradient (when (str/starts-with? (get-meta fill-node :fill-color) "url")
|
||||
(parse-gradient node (get-meta fill-node :fill-color)))
|
||||
:fill-color-ref-file (get-meta fill-node :fill-color-ref-file uuid/uuid)
|
||||
:fill-color-ref-id (get-meta fill-node :fill-color-ref-id uuid/uuid)
|
||||
:fill-opacity (get-meta fill-node :fill-opacity d/parse-double)}))
|
||||
(mapv d/without-nils))]
|
||||
(if (seq fills)
|
||||
fills
|
||||
(->> [(-> (add-fill {} node svg-data)
|
||||
(d/without-nils))]
|
||||
(filterv not-empty)))))
|
||||
|
||||
(defn parse-strokes
|
||||
[node svg-data]
|
||||
(let [strokes-node (get-data node :penpot:strokes)
|
||||
strokes (->> (find-all-nodes strokes-node :penpot:stroke)
|
||||
(mapv (fn [stroke-node]
|
||||
{:stroke-color (when (not (str/starts-with? (get-meta stroke-node :stroke-color) "url"))
|
||||
(get-meta stroke-node :stroke-color))
|
||||
:stroke-color-gradient (when (str/starts-with? (get-meta stroke-node :stroke-color) "url")
|
||||
(parse-gradient node (get-meta stroke-node :stroke-color)))
|
||||
:stroke-color-ref-file (get-meta stroke-node :stroke-color-ref-file uuid/uuid)
|
||||
:stroke-color-ref-id (get-meta stroke-node :stroke-color-ref-id uuid/uuid)
|
||||
:stroke-opacity (get-meta stroke-node :stroke-opacity d/parse-double)
|
||||
:stroke-style (get-meta stroke-node :stroke-style keyword)
|
||||
:stroke-width (get-meta stroke-node :stroke-width d/parse-double)
|
||||
:stroke-alignment (get-meta stroke-node :stroke-alignment keyword)
|
||||
:stroke-cap-start (get-meta stroke-node :stroke-cap-start keyword)
|
||||
:stroke-cap-end (get-meta stroke-node :stroke-cap-end keyword)}))
|
||||
(mapv d/without-nils))]
|
||||
(if (seq strokes)
|
||||
strokes
|
||||
(->> [(-> (add-stroke {} node svg-data)
|
||||
(d/without-nils))]
|
||||
(filterv #(and (not-empty %) (not= (:stroke-style %) :none)))))))
|
||||
|
||||
(defn add-svg-content
|
||||
[props node]
|
||||
|
@ -765,14 +797,16 @@
|
|||
(-> {}
|
||||
(add-common-data node)
|
||||
(add-position type node svg-data)
|
||||
(add-stroke node svg-data)
|
||||
|
||||
(add-layer-options svg-data)
|
||||
(add-shadows node)
|
||||
(add-blur node)
|
||||
(add-exports node)
|
||||
(add-svg-attrs node svg-data)
|
||||
(add-library-refs node)
|
||||
(add-fills node svg-data)
|
||||
|
||||
(assoc :fills (parse-fills node svg-data))
|
||||
(assoc :strokes (parse-strokes node svg-data))
|
||||
|
||||
(cond-> (= :svg-raw type)
|
||||
(add-svg-content node))
|
||||
|
|
Loading…
Add table
Reference in a new issue