mirror of
https://github.com/penpot/penpot.git
synced 2026-05-20 15:33:43 +00:00
163 lines
6.5 KiB
Clojure
163 lines
6.5 KiB
Clojure
;; 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) KALEIDOS INC
|
|
|
|
(ns app.main.ui.shapes.fills
|
|
(:require
|
|
[app.common.data :as d]
|
|
[app.common.data.macros :as dm]
|
|
[app.common.files.helpers :as cfh]
|
|
[app.common.geom.point :as gpt]
|
|
[app.common.geom.shapes :as gsh]
|
|
[app.common.geom.shapes.text :as gst]
|
|
[app.config :as cf]
|
|
[app.main.ui.shapes.attrs :as attrs]
|
|
[app.main.ui.shapes.embed :as embed]
|
|
[app.main.ui.shapes.gradients :as grad]
|
|
[app.util.object :as obj]
|
|
[rumext.v2 :as mf]))
|
|
|
|
(def no-repeat-padding 1.05)
|
|
|
|
(mf/defc internal-fills
|
|
{::mf/wrap-props false}
|
|
[props]
|
|
(let [shape (unchecked-get props "shape")
|
|
render-id (unchecked-get props "render-id")
|
|
|
|
type (dm/get-prop shape :type)
|
|
image (get shape :fill-image)
|
|
fills (get shape :fills [])
|
|
|
|
selrect (dm/get-prop shape :selrect)
|
|
|
|
bounds (when (cfh/text-shape? shape)
|
|
(gst/shape->rect shape))
|
|
|
|
metadata (get shape :metadata)
|
|
|
|
x (dm/get-prop selrect :x)
|
|
y (dm/get-prop selrect :y)
|
|
width (dm/get-prop selrect :width)
|
|
height (dm/get-prop selrect :height)
|
|
|
|
has-image? (or (some? metadata)
|
|
(some? image))
|
|
|
|
uri (cond
|
|
(some? metadata)
|
|
(cf/resolve-file-media metadata)
|
|
|
|
(some? image)
|
|
(cf/resolve-file-media image))
|
|
|
|
uris (into [uri]
|
|
(comp
|
|
(keep :fill-image)
|
|
(map cf/resolve-file-media))
|
|
fills)
|
|
embed (embed/use-data-uris uris)
|
|
transform (gsh/transform-str shape)
|
|
|
|
pat-props #js {:patternUnits "userSpaceOnUse"
|
|
:x x
|
|
:y y
|
|
:width width
|
|
:height height}
|
|
|
|
pat-props (if (or (= :path type) (= :bool type))
|
|
(obj/set! pat-props "patternTransform" transform)
|
|
pat-props)]
|
|
|
|
(for [[obj-index obj] (d/enumerate (or (:position-data shape) [shape]))]
|
|
[:* {:key (dm/str obj-index)}
|
|
(for [[fill-index value] (reverse (d/enumerate (get obj :fills [])))]
|
|
(when (some? (:fill-color-gradient value))
|
|
(let [gradient (:fill-color-gradient value)
|
|
|
|
from-p (-> (gpt/point (+ x (* width (:start-x gradient)))
|
|
(+ y (* height (:start-y gradient)))))
|
|
to-p (-> (gpt/point (+ x (* width (:end-x gradient)))
|
|
(+ y (* height (:end-y gradient)))))
|
|
|
|
gradient
|
|
(cond-> gradient
|
|
(some? bounds)
|
|
(assoc
|
|
:start-x (/ (- (:x from-p) (:x bounds)) (:width bounds))
|
|
:start-y (/ (- (:y from-p) (:y bounds)) (:height bounds))
|
|
:end-x (/ (- (:x to-p) (:x bounds)) (:width bounds))
|
|
:end-y (/ (- (:y to-p) (:y bounds)) (:height bounds))))
|
|
|
|
props #js {:id (dm/str "fill-color-gradient-" render-id "-" fill-index)
|
|
:key (dm/str fill-index)
|
|
:gradient gradient
|
|
:shape obj}]
|
|
(case (d/name (:type gradient))
|
|
"linear" [:> grad/linear-gradient props]
|
|
"radial" [:> grad/radial-gradient props]))))
|
|
|
|
|
|
(let [fill-id (dm/str "fill-" obj-index "-" render-id)]
|
|
[:> :pattern (-> (obj/clone pat-props)
|
|
(obj/set! "id" fill-id)
|
|
(cond-> (and has-image? (nil? bounds))
|
|
(-> (obj/set! "width" (* width no-repeat-padding))
|
|
(obj/set! "height" (* height no-repeat-padding))))
|
|
(cond-> (some? bounds)
|
|
(-> (obj/set! "width" (:width bounds))
|
|
(obj/set! "height" (:height bounds)))))
|
|
[:g
|
|
(for [[fill-index value] (reverse (d/enumerate (get obj :fills [])))]
|
|
(let [style (attrs/get-fill-style value fill-index render-id type)
|
|
props #js {:key (dm/str fill-index)
|
|
:width (d/nilv (:width bounds) width)
|
|
:height (d/nilv (:height bounds) height)
|
|
:style style}]
|
|
(if (:fill-image value)
|
|
(let [uri (cf/resolve-file-media (:fill-image value))
|
|
keep-ar? (-> value :fill-image :keep-aspect-ratio)
|
|
image-props #js {:id (dm/str "fill-image-" render-id "-" fill-index)
|
|
:href (get embed uri uri)
|
|
:preserveAspectRatio (if keep-ar? "xMidYMid slice" "none")
|
|
:width width
|
|
:height height
|
|
:key (dm/str fill-index)
|
|
:opacity (:fill-opacity value)}]
|
|
[:> :image image-props])
|
|
[:> :rect props])))
|
|
|
|
(when ^boolean has-image?
|
|
[:g
|
|
;; We add this shape to add a padding so the patter won't repeat
|
|
;; Issue: https://tree.taiga.io/project/penpot/issue/5583
|
|
[:rect {:x 0
|
|
:y 0
|
|
:width (* width no-repeat-padding)
|
|
:height (* height no-repeat-padding)
|
|
:fill "none"}]
|
|
[:image {:href uri
|
|
:preserveAspectRatio "none"
|
|
:x 0
|
|
:y 0
|
|
:width width
|
|
:height height}]])]])])))
|
|
|
|
(mf/defc fills
|
|
{::mf/wrap-props false}
|
|
[props]
|
|
(let [shape (unchecked-get props "shape")
|
|
type (dm/get-prop shape :type)
|
|
image (:fill-image shape)
|
|
fills (:fills shape [])]
|
|
|
|
(when (or (some? image)
|
|
(or (= type :image)
|
|
(= type :text))
|
|
(> (count fills) 1)
|
|
(some :fill-color-gradient fills)
|
|
(some :fill-image fills))
|
|
[:> internal-fills props])))
|