mirror of
https://github.com/penpot/penpot.git
synced 2025-06-27 11:37:03 +02:00
✨ Fixes exports and preview
This commit is contained in:
parent
e961492192
commit
929d620c60
15 changed files with 255 additions and 199 deletions
|
@ -26,7 +26,9 @@
|
||||||
[app.main.ui.shapes.path :as path]
|
[app.main.ui.shapes.path :as path]
|
||||||
[app.main.ui.shapes.rect :as rect]
|
[app.main.ui.shapes.rect :as rect]
|
||||||
[app.main.ui.shapes.text :as text]
|
[app.main.ui.shapes.text :as text]
|
||||||
[app.main.ui.shapes.group :as group]))
|
[app.main.ui.shapes.group :as group]
|
||||||
|
[app.main.ui.shapes.gradients :as grad]
|
||||||
|
[app.main.ui.context :as muc]))
|
||||||
|
|
||||||
(def ^:private default-color "#E8E9EA") ;; $color-canvas
|
(def ^:private default-color "#E8E9EA") ;; $color-canvas
|
||||||
|
|
||||||
|
@ -55,8 +57,14 @@
|
||||||
(mf/fnc frame-wrapper
|
(mf/fnc frame-wrapper
|
||||||
[{:keys [shape] :as props}]
|
[{:keys [shape] :as props}]
|
||||||
(let [childs (mapv #(get objects %) (:shapes shape))
|
(let [childs (mapv #(get objects %) (:shapes shape))
|
||||||
shape (geom/transform-shape shape)]
|
shape (geom/transform-shape shape)
|
||||||
[:& frame-shape {:shape shape :childs childs}]))))
|
render-id (mf/use-memo #(str (uuid/next)))]
|
||||||
|
[:& (mf/provider muc/render-ctx) {:value render-id}
|
||||||
|
[:g.frame
|
||||||
|
[:defs
|
||||||
|
[:& grad/gradient {:shape shape :attr :fill-color-gradient}]
|
||||||
|
[:& grad/gradient {:shape shape :attr :stroke-color-gradient}]]
|
||||||
|
[:& frame-shape {:shape shape :childs childs}]]]))))
|
||||||
|
|
||||||
(defn group-wrapper-factory
|
(defn group-wrapper-factory
|
||||||
[objects]
|
[objects]
|
||||||
|
@ -79,20 +87,24 @@
|
||||||
(when (and shape (not (:hidden shape)))
|
(when (and shape (not (:hidden shape)))
|
||||||
(let [shape (geom/transform-shape frame shape)
|
(let [shape (geom/transform-shape frame shape)
|
||||||
opts #js {:shape shape}
|
opts #js {:shape shape}
|
||||||
filter-id (filters/get-filter-id)]
|
render-id (mf/use-memo #(str (uuid/next)))]
|
||||||
[:g {:filter (filters/filter-str filter-id shape)}
|
[:& (mf/provider muc/render-ctx) {:value render-id}
|
||||||
[:& filters/filters {:filter-id filter-id :shape shape}]
|
[:g {:filter (filters/filter-str (str "filter_" render-id) shape)}
|
||||||
(case (:type shape)
|
[:defs
|
||||||
:curve [:> path/path-shape opts]
|
[:& filters/filters {:shape shape}]
|
||||||
:text [:> text/text-shape opts]
|
[:& grad/gradient {:shape shape :attr :fill-color-gradient}]
|
||||||
:icon [:> icon/icon-shape opts]
|
[:& grad/gradient {:shape shape :attr :stroke-color-gradient}]]
|
||||||
:rect [:> rect/rect-shape opts]
|
(case (:type shape)
|
||||||
:path [:> path/path-shape opts]
|
:curve [:> path/path-shape opts]
|
||||||
:image [:> image/image-shape opts]
|
:text [:> text/text-shape opts]
|
||||||
:circle [:> circle/circle-shape opts]
|
:icon [:> icon/icon-shape opts]
|
||||||
:frame [:> frame-wrapper {:shape shape}]
|
:rect [:> rect/rect-shape opts]
|
||||||
:group [:> group-wrapper {:shape shape :frame frame}]
|
:path [:> path/path-shape opts]
|
||||||
nil)])))))
|
:image [:> image/image-shape opts]
|
||||||
|
:circle [:> circle/circle-shape opts]
|
||||||
|
:frame [:> frame-wrapper {:shape shape}]
|
||||||
|
:group [:> group-wrapper {:shape shape :frame frame}]
|
||||||
|
nil)]])))))
|
||||||
|
|
||||||
(mf/defc page-svg
|
(mf/defc page-svg
|
||||||
{::mf/wrap [mf/memo]}
|
{::mf/wrap [mf/memo]}
|
||||||
|
|
|
@ -12,3 +12,5 @@
|
||||||
[rumext.alpha :as mf]))
|
[rumext.alpha :as mf]))
|
||||||
|
|
||||||
(def embed-ctx (mf/create-context false))
|
(def embed-ctx (mf/create-context false))
|
||||||
|
|
||||||
|
(def render-ctx (mf/create-context nil))
|
||||||
|
|
|
@ -9,8 +9,10 @@
|
||||||
|
|
||||||
(ns app.main.ui.shapes.attrs
|
(ns app.main.ui.shapes.attrs
|
||||||
(:require
|
(:require
|
||||||
|
[rumext.alpha :as mf]
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[app.util.object :as obj]))
|
[app.util.object :as obj]
|
||||||
|
[app.main.ui.context :as muc]))
|
||||||
|
|
||||||
(defn- stroke-type->dasharray
|
(defn- stroke-type->dasharray
|
||||||
[style]
|
[style]
|
||||||
|
@ -24,16 +26,16 @@
|
||||||
(obj/merge! attrs #js {:rx (:rx shape)
|
(obj/merge! attrs #js {:rx (:rx shape)
|
||||||
:ry (:ry shape)}))
|
:ry (:ry shape)}))
|
||||||
|
|
||||||
(defn add-fill [attrs shape]
|
(defn add-fill [attrs shape render-id]
|
||||||
(let [fill-color-gradient-id (str "fill-color-gradient_" (:render-id shape))]
|
(let [fill-color-gradient-id (str "fill-color-gradient_" render-id)]
|
||||||
(if (:fill-color-gradient shape)
|
(if (:fill-color-gradient shape)
|
||||||
(obj/merge! attrs #js {:fill (str/format "url(#%s)" fill-color-gradient-id)})
|
(obj/merge! attrs #js {:fill (str/format "url(#%s)" fill-color-gradient-id)})
|
||||||
(obj/merge! attrs #js {:fill (or (:fill-color shape) "transparent")
|
(obj/merge! attrs #js {:fill (or (:fill-color shape) "transparent")
|
||||||
:fillOpacity (:fill-opacity shape nil)}))))
|
:fillOpacity (:fill-opacity shape nil)}))))
|
||||||
|
|
||||||
(defn add-stroke [attrs shape]
|
(defn add-stroke [attrs shape render-id]
|
||||||
(let [stroke-style (:stroke-style shape :none)
|
(let [stroke-style (:stroke-style shape :none)
|
||||||
stroke-color-gradient-id (str "stroke-color-gradient_" (:render-id shape))]
|
stroke-color-gradient-id (str "stroke-color-gradient_" render-id)]
|
||||||
(if (not= stroke-style :none)
|
(if (not= stroke-style :none)
|
||||||
(if (:stroke-color-gradient shape)
|
(if (:stroke-color-gradient shape)
|
||||||
(obj/merge! attrs
|
(obj/merge! attrs
|
||||||
|
@ -49,7 +51,8 @@
|
||||||
|
|
||||||
(defn extract-style-attrs
|
(defn extract-style-attrs
|
||||||
([shape]
|
([shape]
|
||||||
(-> (obj/new)
|
(let [render-id (mf/use-ctx muc/render-ctx)]
|
||||||
(add-border-radius shape)
|
(-> (obj/new)
|
||||||
(add-fill shape)
|
(add-border-radius shape)
|
||||||
(add-stroke shape))))
|
(add-fill shape render-id)
|
||||||
|
(add-stroke shape render-id)))))
|
||||||
|
|
|
@ -123,45 +123,42 @@
|
||||||
|
|
||||||
[filter-x filter-y filter-width filter-height] (get-filters-bounds shape filters)]
|
[filter-x filter-y filter-width filter-height] (get-filters-bounds shape filters)]
|
||||||
(when (seq filters)
|
(when (seq filters)
|
||||||
[:defs
|
[:filter {:id filter-id
|
||||||
[:filter {:id filter-id
|
:x filter-x :y filter-y
|
||||||
:x filter-x :y filter-y
|
:width filter-width :height filter-height
|
||||||
:width filter-width :height filter-height
|
:filterUnits "userSpaceOnUse"
|
||||||
:filterUnits "userSpaceOnUse"
|
:color-interpolation-filters "sRGB"}
|
||||||
:color-interpolation-filters "sRGB"}
|
|
||||||
|
|
||||||
(let [;; Add as a paramter the input filter
|
(let [;; Add as a paramter the input filter
|
||||||
drop-shadow-filters (->> filters (filter #(= :drop-shadow (:style %))))
|
drop-shadow-filters (->> filters (filter #(= :drop-shadow (:style %))))
|
||||||
drop-shadow-filters (->> drop-shadow-filters
|
drop-shadow-filters (->> drop-shadow-filters
|
||||||
(map #(str "filter" (:id %)))
|
(map #(str "filter" (:id %)))
|
||||||
(cons "BackgroundImageFix")
|
(cons "BackgroundImageFix")
|
||||||
(map add-in-filter drop-shadow-filters))
|
(map add-in-filter drop-shadow-filters))
|
||||||
|
|
||||||
inner-shadow-filters (->> filters (filter #(= :inner-shadow (:style %))))
|
inner-shadow-filters (->> filters (filter #(= :inner-shadow (:style %))))
|
||||||
inner-shadow-filters (->> inner-shadow-filters
|
inner-shadow-filters (->> inner-shadow-filters
|
||||||
(map #(str "filter" (:id %)))
|
(map #(str "filter" (:id %)))
|
||||||
(cons "shape")
|
(cons "shape")
|
||||||
(map add-in-filter inner-shadow-filters))]
|
(map add-in-filter inner-shadow-filters))]
|
||||||
|
|
||||||
[:*
|
[:*
|
||||||
[:feFlood {:flood-opacity 0 :result "BackgroundImageFix"}]
|
[:feFlood {:flood-opacity 0 :result "BackgroundImageFix"}]
|
||||||
(for [{:keys [id type] :as filter} drop-shadow-filters]
|
(for [{:keys [id type] :as filter} drop-shadow-filters]
|
||||||
[:& drop-shadow-filter {:key id
|
[:& drop-shadow-filter {:key id
|
||||||
|
:filter-id filter-id
|
||||||
|
:filter filter
|
||||||
|
:shape shape}])
|
||||||
|
|
||||||
|
[:feBlend {:mode "normal"
|
||||||
|
:in "SourceGraphic"
|
||||||
|
:in2 (if (seq drop-shadow-filters)
|
||||||
|
(str "filter" (:id (last drop-shadow-filters)))
|
||||||
|
"BackgroundImageFix")
|
||||||
|
:result "shape"}]
|
||||||
|
|
||||||
|
(for [{:keys [id type] :as filter} inner-shadow-filters]
|
||||||
|
[:& inner-shadow-filter {:key id
|
||||||
:filter-id filter-id
|
:filter-id filter-id
|
||||||
:filter filter
|
:filter filter
|
||||||
:shape shape}])
|
:shape shape}])])])))
|
||||||
|
|
||||||
[:feBlend {:mode "normal"
|
|
||||||
:in "SourceGraphic"
|
|
||||||
:in2 (if (seq drop-shadow-filters)
|
|
||||||
(str "filter" (:id (last drop-shadow-filters)))
|
|
||||||
"BackgroundImageFix")
|
|
||||||
:result "shape"}]
|
|
||||||
|
|
||||||
(for [{:keys [id type] :as filter} inner-shadow-filters]
|
|
||||||
[:& inner-shadow-filter {:key id
|
|
||||||
:filter-id filter-id
|
|
||||||
:filter filter
|
|
||||||
:shape shape}])
|
|
||||||
])
|
|
||||||
]])))
|
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[app.util.object :as obj]
|
[app.util.object :as obj]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
|
[app.main.ui.context :as muc]
|
||||||
[app.common.geom.point :as gpt]))
|
[app.common.geom.point :as gpt]))
|
||||||
|
|
||||||
(mf/defc linear-gradient [{:keys [id gradient shape]}]
|
(mf/defc linear-gradient [{:keys [id gradient shape]}]
|
||||||
|
@ -32,7 +33,8 @@
|
||||||
(mf/defc radial-gradient [{:keys [id gradient shape]}]
|
(mf/defc radial-gradient [{:keys [id gradient shape]}]
|
||||||
(let [{:keys [x y width height]} shape]
|
(let [{:keys [x y width height]} shape]
|
||||||
[:defs
|
[:defs
|
||||||
(let [translate-vec (gpt/point (+ x (* width (:start-x gradient)))
|
(let [[x y] (if (= (:type shape) :frame) [0 0] [x y])
|
||||||
|
translate-vec (gpt/point (+ x (* width (:start-x gradient)))
|
||||||
(+ y (* height (:start-y gradient))))
|
(+ y (* height (:start-y gradient))))
|
||||||
|
|
||||||
gradient-vec (gpt/to-vec (gpt/point (* width (:start-x gradient))
|
gradient-vec (gpt/to-vec (gpt/point (* width (:start-x gradient))
|
||||||
|
@ -72,13 +74,13 @@
|
||||||
[props]
|
[props]
|
||||||
(let [attr (obj/get props "attr")
|
(let [attr (obj/get props "attr")
|
||||||
shape (obj/get props "shape")
|
shape (obj/get props "shape")
|
||||||
render-id (obj/get props "render-id")
|
render-id (mf/use-ctx muc/render-ctx)
|
||||||
|
|
||||||
id (str (name attr) "_" render-id)
|
id (str (name attr) "_" render-id)
|
||||||
gradient (get shape attr)
|
gradient (get shape attr)
|
||||||
gradient-props #js {:id id
|
gradient-props #js {:id id
|
||||||
:gradient gradient
|
:gradient gradient
|
||||||
:shape shape}]
|
:shape shape}]
|
||||||
(case (:type gradient)
|
(when gradient
|
||||||
:linear [:> linear-gradient gradient-props]
|
(case (:type gradient)
|
||||||
:radial [:> radial-gradient gradient-props])))
|
:linear [:> linear-gradient gradient-props]
|
||||||
|
:radial [:> radial-gradient gradient-props]))))
|
||||||
|
|
|
@ -68,9 +68,10 @@
|
||||||
|
|
||||||
fill-color (obj/get data "fill-color" fill)
|
fill-color (obj/get data "fill-color" fill)
|
||||||
fill-opacity (obj/get data "fill-opacity" opacity)
|
fill-opacity (obj/get data "fill-opacity" opacity)
|
||||||
fill-color-gradient (obj/get data "fill-color-gradient" opacity)
|
fill-color-gradient (obj/get data "fill-color-gradient" nil)
|
||||||
fill-color-gradient (-> (js->clj fill-color-gradient :keywordize-keys true)
|
fill-color-gradient (when fill-color-gradient
|
||||||
(update :type keyword))
|
(-> (js->clj fill-color-gradient :keywordize-keys true)
|
||||||
|
(update :type keyword)))
|
||||||
|
|
||||||
fill-color-ref-id (obj/get data "fill-color-ref-id")
|
fill-color-ref-id (obj/get data "fill-color-ref-id")
|
||||||
fill-color-ref-file (obj/get data "fill-color-ref-file")
|
fill-color-ref-file (obj/get data "fill-color-ref-file")
|
||||||
|
@ -83,14 +84,9 @@
|
||||||
fontsdb (deref fonts/fontsdb)
|
fontsdb (deref fonts/fontsdb)
|
||||||
|
|
||||||
base #js {:textDecoration text-decoration
|
base #js {:textDecoration text-decoration
|
||||||
;:color (str/format "rgba(%s, %s, %s, %s)" r g b a)
|
|
||||||
:textTransform text-transform
|
:textTransform text-transform
|
||||||
:lineHeight (or line-height "inherit")
|
:lineHeight (or line-height "inherit")
|
||||||
|
"--text-color" background}]
|
||||||
:background background
|
|
||||||
:WebkitTextFillColor "transparent"
|
|
||||||
:WebkitBackgroundClip "text"
|
|
||||||
}]
|
|
||||||
|
|
||||||
(when (and (string? letter-spacing)
|
(when (and (string? letter-spacing)
|
||||||
(pos? (alength letter-spacing)))
|
(pos? (alength letter-spacing)))
|
||||||
|
@ -179,8 +175,7 @@
|
||||||
|
|
||||||
(if (string? text)
|
(if (string? text)
|
||||||
(let [style (generate-text-styles (clj->js node))]
|
(let [style (generate-text-styles (clj->js node))]
|
||||||
[:span {:style style
|
[:span.text-node {:style style} (if (= text "") "\u00A0" text)])
|
||||||
:key (str index "-" (:fill-color node))} (if (= text "") "\u00A0" text)])
|
|
||||||
(let [children (map-indexed (fn [index node]
|
(let [children (map-indexed (fn [index node]
|
||||||
(mf/element text-node {:index index :node node :key index}))
|
(mf/element text-node {:index index :node node :key index}))
|
||||||
children)]
|
children)]
|
||||||
|
@ -192,13 +187,15 @@
|
||||||
{:key index
|
{:key index
|
||||||
:style style
|
:style style
|
||||||
:xmlns "http://www.w3.org/1999/xhtml"}
|
:xmlns "http://www.w3.org/1999/xhtml"}
|
||||||
(when (not (nil? @embeded-fonts))
|
[:*
|
||||||
[:style @embeded-fonts])
|
[:style ".text-node { background: var(--text-color); -webkit-text-fill-color: transparent; -webkit-background-clip: text;"]
|
||||||
|
(when (not (nil? @embeded-fonts))
|
||||||
|
[:style @embeded-fonts])]
|
||||||
children])
|
children])
|
||||||
|
|
||||||
"paragraph-set"
|
"paragraph-set"
|
||||||
(let [style #js {:display "inline-block"}]
|
(let [style #js {:display "inline-block"}]
|
||||||
[:div.paragraphs {:key index :style style} children])
|
[:div.paragraphs {:key index :style style} children])
|
||||||
|
|
||||||
"paragraph"
|
"paragraph"
|
||||||
(let [style (generate-paragraph-styles (clj->js node))]
|
(let [style (generate-paragraph-styles (clj->js node))]
|
||||||
|
|
|
@ -29,7 +29,10 @@
|
||||||
[app.util.object :as obj]
|
[app.util.object :as obj]
|
||||||
[app.common.geom.matrix :as gmt]
|
[app.common.geom.matrix :as gmt]
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.geom.shapes :as geom]))
|
[app.common.geom.shapes :as geom]
|
||||||
|
[app.common.uuid :as uuid]
|
||||||
|
[app.main.ui.shapes.gradients :as grad]
|
||||||
|
[app.main.ui.context :as muc]))
|
||||||
|
|
||||||
(defn on-mouse-down
|
(defn on-mouse-down
|
||||||
[event {:keys [interactions] :as shape}]
|
[event {:keys [interactions] :as shape}]
|
||||||
|
@ -56,25 +59,29 @@
|
||||||
(mf/deps shape)
|
(mf/deps shape)
|
||||||
#(on-mouse-down % shape))
|
#(on-mouse-down % shape))
|
||||||
|
|
||||||
filter-id (filters/get-filter-id)]
|
render-id (mf/use-memo #(str (uuid/next)))]
|
||||||
|
|
||||||
[:g.shape {:on-mouse-down on-mouse-down
|
[:& (mf/provider muc/render-ctx) {:value render-id}
|
||||||
:cursor (when (:interactions shape) "pointer")
|
[:g.shape {:on-mouse-down on-mouse-down
|
||||||
:filter (filters/filter-str filter-id shape)}
|
:cursor (when (:interactions shape) "pointer")
|
||||||
[:& filters/filters {:filter-id filter-id :shape shape}]
|
:filter (filters/filter-str (str "filter_" render-id) shape)}
|
||||||
[:& component {:shape shape
|
[:defs
|
||||||
:frame frame
|
[:& filters/filters {:shape shape}]
|
||||||
:childs childs
|
[:& grad/gradient {:shape shape :attr :fill-color-gradient}]
|
||||||
:is-child-selected? true}]
|
[:& grad/gradient {:shape shape :attr :stroke-color-gradient}]]
|
||||||
(when (and (:interactions shape) show-interactions?)
|
[:& component {:shape shape
|
||||||
[:rect {:x (- x 1)
|
:frame frame
|
||||||
:y (- y 1)
|
:childs childs
|
||||||
:width (+ width 2)
|
:is-child-selected? true}]
|
||||||
:height (+ height 2)
|
(when (and (:interactions shape) show-interactions?)
|
||||||
:fill "#31EFB8"
|
[:rect {:x (- x 1)
|
||||||
:stroke "#31EFB8"
|
:y (- y 1)
|
||||||
:stroke-width 1
|
:width (+ width 2)
|
||||||
:fill-opacity 0.2}])])))
|
:height (+ height 2)
|
||||||
|
:fill "#31EFB8"
|
||||||
|
:stroke "#31EFB8"
|
||||||
|
:stroke-width 1
|
||||||
|
:fill-opacity 0.2}])]])))
|
||||||
|
|
||||||
(defn frame-wrapper
|
(defn frame-wrapper
|
||||||
[shape-container show-interactions?]
|
[shape-container show-interactions?]
|
||||||
|
|
|
@ -369,15 +369,7 @@
|
||||||
(reset! dirty? (not= data new-data))
|
(reset! dirty? (not= data new-data))
|
||||||
(reset! last-change new-data)
|
(reset! last-change new-data)
|
||||||
(when on-change
|
(when on-change
|
||||||
(on-change new-data)))
|
(on-change new-data)))]
|
||||||
|
|
||||||
;; handle-change (fn [new-value new-opacity id file-id shift-clicked?]
|
|
||||||
;; (when (or (not= new-value value) (not= new-opacity opacity))
|
|
||||||
;; (reset! dirty? true))
|
|
||||||
;; (reset! last-change [new-value new-opacity id file-id])
|
|
||||||
;; (when on-change
|
|
||||||
;; (on-change new-value new-opacity id file-id shift-clicked?)))
|
|
||||||
]
|
|
||||||
|
|
||||||
(mf/use-effect
|
(mf/use-effect
|
||||||
(fn []
|
(fn []
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[okulary.core :as l]
|
[okulary.core :as l]
|
||||||
[promesa.core :as p]
|
[promesa.core :as p]
|
||||||
|
[beicon.core :as rx]
|
||||||
[goog.events :as events]
|
[goog.events :as events]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.util.timers :as timers]
|
[app.util.timers :as timers]
|
||||||
|
@ -53,6 +54,19 @@
|
||||||
[:& shape-wrapper {:shape item
|
[:& shape-wrapper {:shape item
|
||||||
:key (:id item)}]))]]))
|
:key (:id item)}]))]]))
|
||||||
|
|
||||||
|
(defn draw-picker-canvas [svg-node canvas-node]
|
||||||
|
(let [canvas-context (.getContext canvas-node "2d")
|
||||||
|
xml (.serializeToString (js/XMLSerializer.) svg-node)
|
||||||
|
img-src (str "data:image/svg+xml;base64,"
|
||||||
|
(-> xml js/encodeURIComponent js/unescape js/btoa))
|
||||||
|
img (js/Image.)
|
||||||
|
|
||||||
|
on-error (fn [err] (.error js/console "ERROR" err))
|
||||||
|
on-load (fn [] (.drawImage canvas-context img 0 0))]
|
||||||
|
(.addEventListener img "error" on-error)
|
||||||
|
(.addEventListener img "load" on-load)
|
||||||
|
(obj/set! img "src" img-src)))
|
||||||
|
|
||||||
(mf/defc pixel-overlay
|
(mf/defc pixel-overlay
|
||||||
{::mf/wrap-props false}
|
{::mf/wrap-props false}
|
||||||
[props]
|
[props]
|
||||||
|
@ -64,6 +78,8 @@
|
||||||
canvas-ref (mf/use-ref nil)
|
canvas-ref (mf/use-ref nil)
|
||||||
fetch-pending (mf/deref (mdf/pending-ref))
|
fetch-pending (mf/deref (mdf/pending-ref))
|
||||||
|
|
||||||
|
update-canvas-stream (rx/subject)
|
||||||
|
|
||||||
handle-keydown
|
handle-keydown
|
||||||
(fn [event]
|
(fn [event]
|
||||||
(when (and (kbd/esc? event))
|
(when (and (kbd/esc? event))
|
||||||
|
@ -117,26 +133,30 @@
|
||||||
#(events/unlistenByKey listener))))
|
#(events/unlistenByKey listener))))
|
||||||
|
|
||||||
(mf/use-effect
|
(mf/use-effect
|
||||||
;; Everytime we finish retrieving a new URL we redraw the canvas
|
|
||||||
;; so even if we're not finished the user can start to pick basic
|
|
||||||
;; shapes
|
|
||||||
(mf/deps fetch-pending)
|
|
||||||
(fn []
|
(fn []
|
||||||
(try
|
(let [sub (->> update-canvas-stream
|
||||||
(let [canvas-node (mf/ref-val canvas-ref)
|
(rx/debounce 10)
|
||||||
canvas-context (.getContext canvas-node "2d")
|
(rx/subs #(draw-picker-canvas (mf/ref-val svg-ref)
|
||||||
svg-node (mf/ref-val svg-ref)]
|
(mf/ref-val canvas-ref))))]
|
||||||
(timers/schedule 100
|
|
||||||
#(let [xml (.serializeToString (js/XMLSerializer.) svg-node)
|
#(rx/dispose! sub))))
|
||||||
img-src (str "data:image/svg+xml;base64,"
|
|
||||||
(-> xml js/encodeURIComponent js/unescape js/btoa))
|
(mf/use-effect
|
||||||
img (js/Image.)
|
(mf/deps svg-ref canvas-ref)
|
||||||
on-error (fn [err] (.error js/console "ERROR" err))
|
(fn []
|
||||||
on-load (fn [] (.drawImage canvas-context img 0 0))]
|
(when (and svg-ref canvas-ref)
|
||||||
(.addEventListener img "error" on-error)
|
|
||||||
(.addEventListener img "load" on-load)
|
(let [config (clj->js {:attributes true
|
||||||
(obj/set! img "src" img-src))))
|
:childList true
|
||||||
(catch :default e (.error js/console e)))))
|
:subtree true
|
||||||
|
:characterData true})
|
||||||
|
on-svg-change (fn [mutation-list] (rx/push! update-canvas-stream :update))
|
||||||
|
observer (js/MutationObserver. on-svg-change)]
|
||||||
|
|
||||||
|
(.observe observer (mf/ref-val svg-ref) config)
|
||||||
|
|
||||||
|
;; Disconnect on unmount
|
||||||
|
#(.disconnect observer)))))
|
||||||
|
|
||||||
[:*
|
[:*
|
||||||
[:div.overlay
|
[:div.overlay
|
||||||
|
@ -154,6 +174,7 @@
|
||||||
:width (:width vport 0)
|
:width (:width vport 0)
|
||||||
:height (:height vport 0)
|
:height (:height vport 0)
|
||||||
:style {:display "none"}}]
|
:style {:display "none"}}]
|
||||||
|
|
||||||
[:& (mf/provider muc/embed-ctx) {:value true}
|
[:& (mf/provider muc/embed-ctx) {:value true}
|
||||||
[:svg.viewport
|
[:svg.viewport
|
||||||
{:ref svg-ref
|
{:ref svg-ref
|
||||||
|
|
|
@ -284,7 +284,9 @@
|
||||||
|
|
||||||
(change! {:width norm-dist})))]
|
(change! {:width norm-dist})))]
|
||||||
|
|
||||||
(when (and gradient (= id (:shape-id gradient)))
|
(when (and gradient
|
||||||
|
(= id (:shape-id gradient))
|
||||||
|
(not= (:type shape) :text))
|
||||||
[:& gradient-handler-transformed
|
[:& gradient-handler-transformed
|
||||||
{:editing editing-spot
|
{:editing editing-spot
|
||||||
:from-p from-p
|
:from-p from-p
|
||||||
|
|
|
@ -20,7 +20,8 @@
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.common.geom.matrix :as gmt]
|
[app.common.geom.matrix :as gmt]
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.geom.shapes :as geom]))
|
[app.common.geom.shapes :as geom]
|
||||||
|
[app.main.ui.context :as muc]))
|
||||||
|
|
||||||
(defn- on-mouse-down
|
(defn- on-mouse-down
|
||||||
[event {:keys [id type] :as shape}]
|
[event {:keys [id type] :as shape}]
|
||||||
|
@ -75,22 +76,15 @@
|
||||||
#(on-context-menu % shape))
|
#(on-context-menu % shape))
|
||||||
render-id (mf/use-memo #(str (uuid/next)))]
|
render-id (mf/use-memo #(str (uuid/next)))]
|
||||||
|
|
||||||
[:g.shape {:on-mouse-down on-mouse-down
|
[:& (mf/provider muc/render-ctx) {:value render-id}
|
||||||
:on-context-menu on-context-menu
|
[:g.shape {:on-mouse-down on-mouse-down
|
||||||
:filter (filters/filter-str (str "filter_" render-id) shape)}
|
:on-context-menu on-context-menu
|
||||||
|
:filter (filters/filter-str (str "filter_" render-id) shape)}
|
||||||
|
[:defs
|
||||||
|
[:& filters/filters {:shape shape}]
|
||||||
|
[:& grad/gradient {:shape shape :attr :fill-color-gradient}]
|
||||||
|
[:& grad/gradient {:shape shape :attr :stroke-color-gradient}]]
|
||||||
|
|
||||||
[:& filters/filters {:filter-id (str "filter_" render-id) :shape shape}]
|
[:& component {:shape shape}]]])))
|
||||||
|
|
||||||
(when (:fill-color-gradient shape)
|
|
||||||
[:& grad/gradient {:attr :fill-color-gradient
|
|
||||||
:render-id render-id
|
|
||||||
:shape shape}])
|
|
||||||
|
|
||||||
(when (:stroke-color-gradient shape)
|
|
||||||
[:& grad/gradient {:attr :stroke-color-gradient
|
|
||||||
:render-id render-id
|
|
||||||
:shape shape}])
|
|
||||||
|
|
||||||
[:& component {:shape (assoc shape :render-id render-id)}]])))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,9 @@
|
||||||
[app.common.geom.shapes :as geom]
|
[app.common.geom.shapes :as geom]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.main.streams :as ms]
|
[app.main.streams :as ms]
|
||||||
[app.util.timers :as ts]))
|
[app.util.timers :as ts]
|
||||||
|
[app.main.ui.context :as muc]
|
||||||
|
[app.common.uuid :as uuid]))
|
||||||
|
|
||||||
(defn- frame-wrapper-factory-equals?
|
(defn- frame-wrapper-factory-equals?
|
||||||
[np op]
|
[np op]
|
||||||
|
@ -115,7 +117,7 @@
|
||||||
(fn []
|
(fn []
|
||||||
(st/emit! (dws/change-hover-state (:id shape) false))))
|
(st/emit! (dws/change-hover-state (:id shape) false))))
|
||||||
|
|
||||||
filter-id (mf/use-memo filters/get-filter-id)]
|
render-id (mf/use-memo #(str (uuid/next)))]
|
||||||
|
|
||||||
(when-not (:hidden shape)
|
(when-not (:hidden shape)
|
||||||
[:g {:class (when selected? "selected")
|
[:g {:class (when selected? "selected")
|
||||||
|
@ -128,18 +130,14 @@
|
||||||
:on-double-click on-double-click
|
:on-double-click on-double-click
|
||||||
:on-mouse-down on-mouse-down}]
|
:on-mouse-down on-mouse-down}]
|
||||||
|
|
||||||
[:g.frame {:filter (filters/filter-str filter-id shape)}
|
[:& (mf/provider muc/render-ctx) {:value render-id}
|
||||||
[:& filters/filters {:filter-id filter-id :shape shape}]
|
[:g.frame {:filter (filters/filter-str (str "filter_" render-id) shape)}
|
||||||
|
[:defs
|
||||||
(when (:fill-color-gradient shape)
|
[:& filters/filters {:shape shape}]
|
||||||
[:& grad/gradient {:attr :fill-color-gradient
|
[:& grad/gradient {:shape shape :attr :fill-color-gradient}]
|
||||||
:shape shape}])
|
[:& grad/gradient {:shape shape :attr :stroke-color-gradient}]]
|
||||||
|
|
||||||
(when (:stroke-color-gradient shape)
|
[:& frame-shape
|
||||||
[:& grad/gradient {:attr :stroke-color-gradient
|
{:shape shape
|
||||||
:shape shape}])
|
:childs children}]]]])))))
|
||||||
|
|
||||||
[:& frame-shape
|
|
||||||
{:shape shape
|
|
||||||
:childs children}]]])))))
|
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,10 @@
|
||||||
[app.main.data.workspace.drawing :as dr]
|
[app.main.data.workspace.drawing :as dr]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.main.streams :as ms]
|
[app.main.streams :as ms]
|
||||||
[app.util.timers :as ts]))
|
[app.util.timers :as ts]
|
||||||
|
[app.common.uuid :as uuid]
|
||||||
|
[app.main.ui.shapes.gradients :as grad]
|
||||||
|
[app.main.ui.context :as muc]))
|
||||||
|
|
||||||
(mf/defc path-wrapper
|
(mf/defc path-wrapper
|
||||||
{::mf/wrap-props false}
|
{::mf/wrap-props false}
|
||||||
|
@ -45,21 +48,18 @@
|
||||||
(dom/prevent-default event)
|
(dom/prevent-default event)
|
||||||
(st/emit! (dw/start-edition-mode (:id shape)))))))
|
(st/emit! (dw/start-edition-mode (:id shape)))))))
|
||||||
|
|
||||||
filter-id (mf/use-memo filters/get-filter-id)]
|
render-id (mf/use-memo #(str (uuid/next)))]
|
||||||
|
|
||||||
[:g.shape {:on-double-click on-double-click
|
[:& (mf/provider muc/render-ctx) {:value render-id}
|
||||||
:on-mouse-down on-mouse-down
|
[:g.shape {:on-double-click on-double-click
|
||||||
:on-context-menu on-context-menu
|
:on-mouse-down on-mouse-down
|
||||||
:filter (filters/filter-str filter-id shape)}
|
:on-context-menu on-context-menu
|
||||||
[:& filters/filters {:filter-id filter-id :shape shape}]
|
:filter (filters/filter-str (str "filter_" render-id) shape)}
|
||||||
|
[:defs
|
||||||
|
[:& filters/filters {:shape shape}]
|
||||||
|
[:& grad/gradient {:shape shape :attr :fill-color-gradient}]
|
||||||
|
[:& grad/gradient {:shape shape :attr :stroke-color-gradient}]]
|
||||||
|
|
||||||
(when (:fill-color-gradient shape)
|
[:& path/path-shape {:shape shape
|
||||||
[:& grad/gradient {:attr :fill-color-gradient
|
:background? true}]]]))
|
||||||
:shape shape}])
|
|
||||||
|
|
||||||
(when (:stroke-color-gradient shape)
|
|
||||||
[:& grad/gradient {:attr :stroke-color-gradient
|
|
||||||
:shape shape}])
|
|
||||||
[:& path/path-shape {:shape shape
|
|
||||||
:background? true}]]))
|
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,10 @@
|
||||||
[app.util.color :as uc]
|
[app.util.color :as uc]
|
||||||
[app.util.timers :as timers]
|
[app.util.timers :as timers]
|
||||||
["slate" :as slate]
|
["slate" :as slate]
|
||||||
["slate-react" :as rslate])
|
["slate-react" :as rslate]
|
||||||
|
[app.common.uuid :as uuid]
|
||||||
|
[app.main.ui.shapes.gradients :as grad]
|
||||||
|
[app.main.ui.context :as muc])
|
||||||
(:import
|
(:import
|
||||||
goog.events.EventType
|
goog.events.EventType
|
||||||
goog.events.KeyCodes))
|
goog.events.KeyCodes))
|
||||||
|
@ -80,7 +83,7 @@
|
||||||
(when selected?
|
(when selected?
|
||||||
(st/emit! (dw/start-edition-mode (:id shape)))))
|
(st/emit! (dw/start-edition-mode (:id shape)))))
|
||||||
|
|
||||||
filter-id (mf/use-memo filters/get-filter-id)]
|
render-id (mf/use-memo #(str (uuid/next)))]
|
||||||
|
|
||||||
(mf/use-effect
|
(mf/use-effect
|
||||||
(mf/deps shape edition selected? current-transform)
|
(mf/deps shape edition selected? current-transform)
|
||||||
|
@ -91,23 +94,27 @@
|
||||||
(nil? current-transform))]
|
(nil? current-transform))]
|
||||||
(timers/schedule #(reset! render-editor check?)))))
|
(timers/schedule #(reset! render-editor check?)))))
|
||||||
|
|
||||||
[:g.shape {:on-double-click on-double-click
|
[:& (mf/provider muc/render-ctx) {:value render-id}
|
||||||
:on-mouse-down on-mouse-down
|
[:g.shape {:on-double-click on-double-click
|
||||||
:on-context-menu on-context-menu
|
:on-mouse-down on-mouse-down
|
||||||
:filter (filters/filter-str filter-id shape)}
|
:on-context-menu on-context-menu
|
||||||
[:& filters/filters {:filter-id filter-id :shape shape}]
|
:filter (filters/filter-str (str "filter_" render-id) shape)}
|
||||||
[:*
|
[:defs
|
||||||
(when @render-editor
|
[:& filters/filters {:shape shape}]
|
||||||
[:g {:opacity 0
|
[:& grad/gradient {:shape shape :attr :fill-color-gradient}]
|
||||||
:style {:pointer-events "none"}}
|
[:& grad/gradient {:shape shape :attr :stroke-color-gradient}]]
|
||||||
;; We only render the component for its side-effect
|
[:*
|
||||||
[:& text-shape-edit {:shape shape
|
(when @render-editor
|
||||||
:read-only? true}]])
|
[:g {:opacity 0
|
||||||
|
:style {:pointer-events "none"}}
|
||||||
|
;; We only render the component for its side-effect
|
||||||
|
[:& text-shape-edit {:shape shape
|
||||||
|
:read-only? true}]])
|
||||||
|
|
||||||
(if edition?
|
(if edition?
|
||||||
[:& text-shape-edit {:shape shape}]
|
[:& text-shape-edit {:shape shape}]
|
||||||
[:& text/text-shape {:shape shape
|
[:& text/text-shape {:shape shape
|
||||||
:selected? selected?}])]]))
|
:selected? selected?}])]]]))
|
||||||
|
|
||||||
;; --- Text Editor Rendering
|
;; --- Text Editor Rendering
|
||||||
|
|
||||||
|
@ -158,17 +165,25 @@
|
||||||
|
|
||||||
fill-color (obj/get data "fill-color" fill)
|
fill-color (obj/get data "fill-color" fill)
|
||||||
fill-opacity (obj/get data "fill-opacity" opacity)
|
fill-opacity (obj/get data "fill-opacity" opacity)
|
||||||
|
fill-color-gradient (obj/get data "fill-color-gradient" nil)
|
||||||
|
fill-color-gradient (when fill-color-gradient
|
||||||
|
(-> (js->clj fill-color-gradient :keywordize-keys true)
|
||||||
|
(update :type keyword)))
|
||||||
|
|
||||||
fill-color-ref-id (obj/get data "fill-color-ref-id")
|
fill-color-ref-id (obj/get data "fill-color-ref-id")
|
||||||
fill-color-ref-file (obj/get data "fill-color-ref-file")
|
fill-color-ref-file (obj/get data "fill-color-ref-file")
|
||||||
|
|
||||||
[r g b a] (uc/hex->rgba fill-color fill-opacity)
|
[r g b a] (uc/hex->rgba fill-color fill-opacity)
|
||||||
|
background (if fill-color-gradient
|
||||||
|
(uc/gradient->css (js->clj fill-color-gradient))
|
||||||
|
(str/format "rgba(%s, %s, %s, %s)" r g b a))
|
||||||
|
|
||||||
fontsdb (deref fonts/fontsdb)
|
fontsdb (deref fonts/fontsdb)
|
||||||
|
|
||||||
base #js {:textDecoration text-decoration
|
base #js {:textDecoration text-decoration
|
||||||
:color (str/format "rgba(%s, %s, %s, %s)" r g b a)
|
|
||||||
:textTransform text-transform
|
:textTransform text-transform
|
||||||
:lineHeight (or line-height "inherit")}]
|
:lineHeight (or line-height "inherit")
|
||||||
|
"--text-color" background}]
|
||||||
|
|
||||||
(when (and (string? letter-spacing)
|
(when (and (string? letter-spacing)
|
||||||
(pos? (alength letter-spacing)))
|
(pos? (alength letter-spacing)))
|
||||||
|
@ -243,7 +258,9 @@
|
||||||
childs (obj/get props "children")
|
childs (obj/get props "children")
|
||||||
data (obj/get props "leaf")
|
data (obj/get props "leaf")
|
||||||
style (generate-text-styles data)
|
style (generate-text-styles data)
|
||||||
attrs (obj/set! attrs "style" style)]
|
attrs (-> attrs
|
||||||
|
(obj/set! "style" style)
|
||||||
|
(obj/set! "className" "text-node"))]
|
||||||
[:> :span attrs childs]))
|
[:> :span attrs childs]))
|
||||||
|
|
||||||
(defn- render-element
|
(defn- render-element
|
||||||
|
@ -284,6 +301,14 @@
|
||||||
children-count (->> node :children (map content-size) (reduce +))]
|
children-count (->> node :children (map content-size) (reduce +))]
|
||||||
(+ current children-count)))
|
(+ current children-count)))
|
||||||
|
|
||||||
|
(defn fix-gradients
|
||||||
|
"Fix for the gradient types that need to be keywords"
|
||||||
|
[content]
|
||||||
|
(let [fix-node
|
||||||
|
(fn [node]
|
||||||
|
(d/update-in-when node [:fill-color-gradient :type] keyword))]
|
||||||
|
(ut/map-node fix-node content)))
|
||||||
|
|
||||||
(mf/defc text-shape-edit
|
(mf/defc text-shape-edit
|
||||||
{::mf/wrap [mf/memo]}
|
{::mf/wrap [mf/memo]}
|
||||||
[{:keys [shape read-only?] :or {read-only? false} :as props}]
|
[{:keys [shape read-only?] :or {read-only? false} :as props}]
|
||||||
|
@ -364,7 +389,8 @@
|
||||||
(fn [val]
|
(fn [val]
|
||||||
(when (not read-only?)
|
(when (not read-only?)
|
||||||
(let [content (js->clj val :keywordize-keys true)
|
(let [content (js->clj val :keywordize-keys true)
|
||||||
content (first content)]
|
content (first content)
|
||||||
|
content (fix-gradients content)]
|
||||||
;; Append timestamp so we can react to cursor change events
|
;; Append timestamp so we can react to cursor change events
|
||||||
(st/emit! (dw/update-shape id {:content (assoc content :ts (js->clj (.now js/Date)))}))
|
(st/emit! (dw/update-shape id {:content (assoc content :ts (js->clj (.now js/Date)))}))
|
||||||
(reset! state val)
|
(reset! state val)
|
||||||
|
@ -419,7 +445,8 @@
|
||||||
:x x :y y
|
:x x :y y
|
||||||
:width (if (= :auto-width grow-type) 10000 width)
|
:width (if (= :auto-width grow-type) 10000 width)
|
||||||
:height height}
|
:height height}
|
||||||
[:style "span { line-height: inherit; }"]
|
[:style "span { line-height: inherit; }
|
||||||
|
.text-node { background: var(--text-color); -webkit-text-fill-color: transparent; -webkit-background-clip: text;"]
|
||||||
[:> rslate/Slate {:editor editor
|
[:> rslate/Slate {:editor editor
|
||||||
:value @state
|
:value @state
|
||||||
:on-change on-change}
|
:on-change on-change}
|
||||||
|
|
|
@ -290,6 +290,8 @@
|
||||||
:shape shape
|
:shape shape
|
||||||
:attrs text-fill-attrs})
|
:attrs text-fill-attrs})
|
||||||
|
|
||||||
|
fill-values (d/update-in-when fill-values [:fill-color-gradient :type] keyword)
|
||||||
|
|
||||||
fill-values (cond-> fill-values
|
fill-values (cond-> fill-values
|
||||||
;; Keep for backwards compatibility
|
;; Keep for backwards compatibility
|
||||||
(:fill fill-values) (assoc :fill-color (:fill fill-values))
|
(:fill fill-values) (assoc :fill-color (:fill fill-values))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue