Merge remote-tracking branch 'origin/staging' into develop

This commit is contained in:
Andrey Antukh 2022-04-01 11:58:07 +02:00
commit c9ddc83eef
39 changed files with 678 additions and 450 deletions

View file

@ -3,6 +3,7 @@
{penpot/common
{:local/root "../common"}
org.clojure/clojure {:mvn/version "1.10.3"}
binaryage/devtools {:mvn/version "RELEASE"}
metosin/reitit-core {:mvn/version "0.5.15"}

View file

@ -1209,7 +1209,8 @@
}
.modal-container {
background-image: url("../images/deco-left.png"), url("../images/deco-right.png");
background-image: url("../images/deco-left.png"),
url("../images/deco-right.png");
background-repeat: no-repeat;
background-position: 10% 50px, 90% 50px;
background-size: 65px;
@ -1236,8 +1237,18 @@
--checkbox-border-radius: 3px;
--dropdown-option-background-color: rgba(0, 195, 139, 1);
--dropdown-option-active-background-color: rgba(0, 138, 98, 1);
--invalid-field-background-color: rgba(238.51780000000002, 205.7178, 204.11780000000002, 1);
--message-fail-background-color: rgba(238.51780000000002, 205.7178, 204.11780000000002, 1);
--invalid-field-background-color: rgba(
238.51780000000002,
205.7178,
204.11780000000002,
1
);
--message-fail-background-color: rgba(
238.51780000000002,
205.7178,
204.11780000000002,
1
);
--message-success-background-color: rgba(171, 232, 197, 1);
}
}

View file

@ -523,6 +523,12 @@
right: 0.5rem;
left: unset;
top: 0;
.context-menu-action {
overflow-wrap: break-word;
min-width: 223px;
white-space: break-spaces;
}
}
}
}

View file

@ -194,7 +194,8 @@
(ptk/reify ::initialize-page
ptk/WatchEvent
(watch [_ state _]
(when-not (contains? (get-in state [:workspace-data :pages-index]) page-id)
(if (contains? (get-in state [:workspace-data :pages-index]) page-id)
(rx/of (dwp/preload-data-uris))
(let [default-page-id (get-in state [:workspace-data :pages 0])]
(rx/of (go-to-page default-page-id)))))
@ -1356,26 +1357,28 @@
edit-id (get-in state [:workspace-local :edition])
is-editing-text? (and edit-id (= :text (get-in objects [edit-id :type])))]
(cond
(and (string? text-data)
(str/includes? text-data "<svg"))
(rx/of (paste-svg text-data))
;; Some paste events can be fired while we're editing a text
;; we forbid that scenario so the default behaviour is executed
(when-not is-editing-text?
(cond
(and (string? text-data)
(str/includes? text-data "<svg"))
(rx/of (paste-svg text-data))
(seq image-data)
(rx/from (map paste-image image-data))
(seq image-data)
(rx/from (map paste-image image-data))
(coll? decoded-data)
(->> (rx/of decoded-data)
(rx/filter #(= :copied-shapes (:type %)))
(rx/map #(paste-shape % in-viewport?)))
(coll? decoded-data)
(->> (rx/of decoded-data)
(rx/filter #(= :copied-shapes (:type %)))
(rx/map #(paste-shape % in-viewport?)))
;; Some paste events can be fired while we're editing a text
;; we forbid that scenario so the default behaviour is executed
(and (string? text-data) (not is-editing-text?))
(rx/of (paste-text text-data))
(string? text-data)
(rx/of (paste-text text-data))
:else
(rx/empty))))
:else
(rx/empty)))
(catch :default err
(js/console.error "Clipboard error:" err))))))

View file

@ -14,6 +14,7 @@
[app.common.spec.change :as spec.change]
[app.common.spec.file :as spec.file]
[app.common.uuid :as uuid]
[app.config :as cfg]
[app.main.data.dashboard :as dd]
[app.main.data.events :as ev]
[app.main.data.fonts :as df]
@ -653,6 +654,9 @@
frame-changes (->> stream
(rx/filter dch/commit-changes?)
;; Async so we wait for additional side-effects of commit-changes
(rx/observe-on :async)
(rx/filter (comp not thumbnail-change?))
(rx/with-latest-from objects-stream)
(rx/map extract-frame-changes)
@ -678,3 +682,26 @@
(rx/buffer-until (->> frame-changes (rx/debounce 1000)))
(rx/flat-map #(reduce set/union %))
(rx/map #(update-frame-thumbnail %)))))))))
(defn preload-data-uris
"Preloads the image data so it's ready when necesary"
[]
(ptk/reify ::preload-data-uris
ptk/WatchEvent
(watch [_ state _]
(let [extract-urls
(fn [{:keys [metadata fill-image]}]
(cond
(some? metadata)
[(cfg/resolve-file-media metadata)]
(some? fill-image)
[(cfg/resolve-file-media fill-image)]))
uris (into #{}
(comp (mapcat extract-urls)
(filter some?))
(vals (wsh/lookup-page-objects state)))]
(->> (rx/from uris)
(rx/merge-map #(http/fetch-data-uri % false))
(rx/ignore))))))

View file

@ -110,7 +110,10 @@
(rx/dedupe)
(rx/map #(select-shapes-by-current-selrect preserve? ignore-groups?))))
(rx/of (update-selrect nil)))))))
(->> (rx/of (update-selrect nil))
;; We need the async so the current event finishes before updating the selrect
;; otherwise the `on-click` event will trigger with a `nil` selrect
(rx/observe-on :async)))))))
;; --- Toggle shape's selection status (selected or deselected)
@ -123,13 +126,7 @@
(ptk/reify ::select-shape
ptk/UpdateEvent
(update [_ state]
(update-in state [:workspace-local :selected]
(fn [selected]
(if-not toggle?
(conj (d/ordered-set) id)
(if (contains? selected id)
(disj selected id)
(conj selected id))))))
(update-in state [:workspace-local :selected] d/toggle-selection id toggle?))
ptk/WatchEvent
(watch [_ state _]
@ -506,6 +503,7 @@
id-duplicated (when (= (count selected) 1) (first selected))]
;; Warning: This order is important for the focus mode.
(rx/of (dch/commit-changes changes)
(select-shapes selected)
(memorize-duplicated id-original id-duplicated)))))))

View file

@ -592,31 +592,46 @@
(defn start-move-selected
"Enter mouse move mode, until mouse button is released."
[]
(ptk/reify ::start-move-selected
ptk/WatchEvent
(watch [_ state stream]
(let [initial (deref ms/mouse-position)
selected (wsh/lookup-selected state {:omit-blocked? true})
stopper (rx/filter ms/mouse-up? stream)
zoom (get-in state [:workspace-local :zoom] 1)]
(when-not (empty? selected)
(->> ms/mouse-position
(rx/map #(gpt/to-vec initial %))
(rx/map #(gpt/length %))
(rx/filter #(> % (/ 10 zoom)))
(rx/take 1)
(rx/with-latest vector ms/mouse-position-alt)
(rx/mapcat
(fn [[_ alt?]]
(if alt?
;; When alt is down we start a duplicate+move
(rx/of (start-move-duplicate initial)
(dws/duplicate-selected false))
;; Otherwise just plain old move
(rx/of (start-move initial selected)))))
(rx/take-until stopper)))))))
([]
(start-move-selected nil false))
([id shift?]
(ptk/reify ::start-move-selected
ptk/WatchEvent
(watch [_ state stream]
(let [initial (deref ms/mouse-position)
stopper (rx/filter ms/mouse-up? stream)
zoom (get-in state [:workspace-local :zoom] 1)
;; We toggle the selection so we don't have to wait for the event
selected
(cond-> (wsh/lookup-selected state {:omit-blocked? true})
(some? id)
(d/toggle-selection id shift?))]
(when (or (d/not-empty? selected) (some? id))
(->> ms/mouse-position
(rx/map #(gpt/to-vec initial %))
(rx/map #(gpt/length %))
(rx/filter #(> % (/ 10 zoom)))
(rx/take 1)
(rx/with-latest vector ms/mouse-position-alt)
(rx/mapcat
(fn [[_ alt?]]
(rx/concat
(if (some? id)
(rx/of (dws/select-shape id shift?))
(rx/empty))
(if alt?
;; When alt is down we start a duplicate+move
(rx/of (start-move-duplicate initial)
(dws/duplicate-selected false))
;; Otherwise just plain old move
(rx/of (start-move initial selected))))))
(rx/take-until stopper))))))))
(defn- start-move-duplicate
[from-position]
(ptk/reify ::start-move-duplicate

View file

@ -73,44 +73,44 @@
parse-value
(mf/use-callback
(mf/deps ref min-val max-val value nillable default-val)
(fn []
(let [input-node (mf/ref-val ref)
new-value (-> (dom/get-value input-node)
(str/strip-suffix ".")
(sm/expr-eval value))]
(cond
(d/num? new-value)
(-> new-value
(cljs.core/max us/min-safe-int)
(cljs.core/min us/max-safe-int)
(cond->
(d/num? min-val)
(cljs.core/max min-val)
(mf/deps ref min-val max-val value nillable default-val)
(fn []
(let [input-node (mf/ref-val ref)
new-value (-> (dom/get-value input-node)
(str/strip-suffix ".")
(sm/expr-eval value))]
(cond
(d/num? new-value)
(-> new-value
(cljs.core/max (/ us/min-safe-int 2))
(cljs.core/min (/ us/max-safe-int 2))
(cond->
(d/num? min-val)
(cljs.core/max min-val)
(d/num? max-val)
(cljs.core/min max-val)))
(d/num? max-val)
(cljs.core/min max-val)))
nillable
default-val
nillable
default-val
:else value))))
:else value))))
update-input
(mf/use-callback
(mf/deps ref)
(fn [new-value]
(let [input-node (mf/ref-val ref)]
(dom/set-value! input-node (fmt/format-number new-value)))))
(mf/deps ref)
(fn [new-value]
(let [input-node (mf/ref-val ref)]
(dom/set-value! input-node (fmt/format-number new-value)))))
apply-value
(mf/use-callback
(mf/deps on-change update-input value)
(fn [new-value]
(mf/set-ref-val! dirty-ref false)
(when (and (not= new-value value) (some? on-change))
(on-change new-value))
(update-input new-value)))
(mf/deps on-change update-input value)
(fn [new-value]
(mf/set-ref-val! dirty-ref false)
(when (and (not= new-value value) (some? on-change))
(on-change new-value))
(update-input new-value)))
set-delta
(mf/use-callback

View file

@ -33,10 +33,10 @@
(let [xf-get-bounds (comp (map #(get objects %)) (map #(calc-bounds % objects)))
padding (filters/calculate-padding object)
obj-bounds (-> (filters/get-filters-bounds object)
(update :x - padding)
(update :y - padding)
(update :width + (* 2 padding))
(update :height + (* 2 padding)))]
(update :x - (:horizontal padding))
(update :y - (:vertical padding))
(update :width + (* 2 (:horizontal padding)))
(update :height + (* 2 (:vertical padding))))]
(cond
(and (= :group (:type object))

View file

@ -175,12 +175,12 @@
(obj/set! styles "fill" (str "url(#fill-0-" render-id ")"))
;; imported svgs can have fill and fill-opacity attributes
(obj/contains? svg-styles "fill")
(and (some? svg-styles) (obj/contains? svg-styles "fill"))
(-> styles
(obj/set! "fill" (obj/get svg-styles "fill"))
(obj/set! "fillOpacity" (obj/get svg-styles "fillOpacity")))
(obj/contains? svg-attrs "fill")
(and (some? svg-attrs) (obj/contains? svg-attrs "fill"))
(-> styles
(obj/set! "fill" (obj/get svg-attrs "fill"))
(obj/set! "fillOpacity" (obj/get svg-attrs "fillOpacity")))

View file

@ -9,6 +9,7 @@
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.geom.shapes :as gsh]
[app.common.pages.helpers :as cph]
[app.main.ui.context :as muc]
[app.main.ui.shapes.attrs :as attrs]
[app.main.ui.shapes.gradients :as grad]
@ -327,8 +328,12 @@
(obj/clone))
props (cond-> props
(d/not-empty? (:shadow shape))
(obj/set! "filter" (dm/fmt "url(#filter_%)" render-id)))
(or
;; There are any shadows
(and (d/not-empty? (:shadow shape)) (not (cph/frame-shape? shape)))
;; There are no strokes and a blur
(and (some? (:blur shape)) (not (cph/frame-shape? shape)) (empty? (:strokes shape))))
(obj/set! "filter" (dm/fmt "url(#filter_%)" render-id)))
svg-defs (:svg-defs shape {})
svg-attrs (:svg-attrs shape {})
@ -345,7 +350,7 @@
(obj/without ["fill" "fillOpacity"])))]
(obj/set! props "fill" (dm/fmt "url(#fill-0-%)" render-id)))
(obj/contains? svg-styles "fill")
(and (some? svg-styles) (obj/contains? svg-styles "fill"))
(let [style
(-> (obj/get props "style")
(obj/clone)
@ -354,7 +359,7 @@
(-> props
(obj/set! "style" style)))
(obj/contains? svg-attrs "fill")
(and (some? svg-attrs) (obj/contains? svg-attrs "fill"))
(let [style
(-> (obj/get props "style")
(obj/clone)
@ -374,10 +379,7 @@
(cond-> (obj/merge! props fill-props)
(some? style)
(obj/set! "style" style)))
:else
props)))
(obj/set! "style" style))))))
(defn build-stroke-props [position child value render-id]
(let [props (-> (obj/get child "props")
@ -391,7 +393,19 @@
(obj/set! "fillOpacity" "none")))
(add-style (obj/get (attrs/extract-stroke-attrs value position render-id) "style")))))
(mf/defc shape-custom-strokes
(mf/defc shape-fills
{::mf/wrap-props false}
[props]
(let [child (obj/get props "children")
shape (obj/get props "shape")
elem-name (obj/get child "type")
render-id (mf/use-ctx muc/render-ctx)]
[:g {:id (dm/fmt "fills-%" (:id shape))}
[:> elem-name (build-fill-props shape child render-id)]]))
(mf/defc shape-strokes
{::mf/wrap-props false}
[props]
(let [child (obj/get props "children")
@ -401,13 +415,9 @@
stroke-props (-> (obj/new)
(obj/set! "id" (dm/fmt "strokes-%" (:id shape)))
(cond->
(some? (:blur shape))
(and (some? (:blur shape)) (not (cph/frame-shape? shape)))
(obj/set! "filter" (dm/fmt "url(#filter_blur_%)" render-id))))]
[:*
[:g {:id (dm/fmt "fills-%" (:id shape))}
[:> elem-name (build-fill-props shape child render-id)]]
(when
(d/not-empty? (:strokes shape))
[:> :g stroke-props
@ -416,3 +426,16 @@
shape (assoc value :points (:points shape))]
[:& shape-custom-stroke {:shape shape :index index}
[:> elem-name props]]))])]))
(mf/defc shape-custom-strokes
{::mf/wrap-props false}
[props]
(let [child (obj/get props "children")
shape (obj/get props "shape")]
[:*
[:& shape-fills {:shape shape}
child]
[:& shape-strokes {:shape shape}
child]]))

View file

@ -170,7 +170,6 @@
([shape filters blur-value]
(let [svg-root? (and (= :svg-raw (:type shape)) (not= :svg (get-in shape [:content :tag])))
frame? (= :frame (:type shape))
{:keys [x y width height]} (:selrect shape)]
(if svg-root?
;; When is a raw-svg but not the root we use the whole svg as bound for the filter. Is the maximum
@ -183,6 +182,7 @@
(map (partial filter-bounds shape)))
;; We add the selrect so the minimum size will be the selrect
filter-bounds (conj filter-bounds (-> shape :points gsh/points->selrect))
x1 (apply min (map :x1 filter-bounds))
y1 (apply min (map :y1 filter-bounds))
x2 (apply max (map :x2 filter-bounds))
@ -195,18 +195,30 @@
;; We should move the frame filter coordinates because they should be
;; relative with the frame. By default they come as absolute
{:x (if frame? (- x1 x) x1)
:y (if frame? (- y1 y) y1)
{:x x1
:y y1
:width (- x2 x1)
:height (- y2 y1)})))))
(defn calculate-padding [shape]
(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)))
: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)))
shadow-width (apply max 0 (map #(case (:style % :drop-shadow)
:drop-shadow (+ (mth/abs (:offset-x %)) (* (:spread %) 2) (* (:blur %) 2) 10)
0) (:shadow shape)))
shadow-height (apply max 0 (map #(case (:style % :drop-shadow)
:drop-shadow (+ (mth/abs (:offset-y %)) (* (:spread %) 2) (* (:blur %) 2) 10)
0) (:shadow shape)))]
{:horizontal (+ stroke-width margin shadow-width)
:vertical (+ stroke-width margin shadow-height)}))
(defn change-filter-in
"Adds the previous filter as `filter-in` parameter"
@ -220,10 +232,10 @@
bounds (get-filters-bounds shape filters (or (-> shape :blur :value) 0))
padding (calculate-padding shape)
selrect (:selrect shape)
filter-x (/ (- (:x bounds) (:x selrect) padding) (:width selrect))
filter-y (/ (- (:y bounds) (:y selrect) padding) (:height selrect))
filter-width (/ (+ (:width bounds) (* 2 padding)) (:width selrect))
filter-height (/ (+ (:height bounds) (* 2 padding)) (:height selrect))]
filter-x (/ (- (:x bounds) (:x selrect) (:horizontal padding)) (:width selrect))
filter-y (/ (- (:y bounds) (:y selrect) (:vertical padding)) (:height selrect))
filter-width (/ (+ (:width bounds) (* 2 (:horizontal padding))) (:width selrect))
filter-height (/ (+ (:height bounds) (* 2 (:vertical padding))) (:height selrect))]
(when (> (count filters) 2)
[:filter {:id filter-id
:x filter-x

View file

@ -7,9 +7,10 @@
(ns app.main.ui.shapes.frame
(:require
[app.common.data.macros :as dm]
[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-strokes]]
[app.main.ui.shapes.filters :as filters]
[app.main.ui.shapes.custom-stroke :refer [shape-fills shape-strokes]]
[app.util.object :as obj]
[debug :refer [debug?]]
[rumext.alpha :as mf]))
@ -27,13 +28,12 @@
[{:keys [shape render-id]}]
(when (= :frame (:type shape))
(let [{:keys [x y width height]} shape
padding (filters/calculate-padding shape)
props (-> (attrs/extract-style-attrs shape)
(obj/merge!
#js {:x (- x padding)
:y (- y padding)
:width (+ width (* 2 padding))
:height (+ height (* 2 padding))}))
#js {:x x
:y y
:width width
:height height}))
path? (some? (.-d props))]
[:clipPath {:id (frame-clip-id shape render-id) :class "frame-clip"}
(if path?
@ -63,22 +63,32 @@
(let [childs (unchecked-get props "childs")
shape (unchecked-get props "shape")
{:keys [x y width height]} shape
transform (gsh/transform-matrix shape)
props (-> (attrs/extract-style-attrs shape)
(obj/merge!
#js {:x x
:y y
:transform transform
:width width
:height height
:className "frame-background"}))
path? (some? (.-d props))]
[:*
[:& shape-custom-strokes {:shape shape}
(if path?
[:> :path props]
[:> :rect props])]
(for [item childs]
[:& shape-wrapper {:shape item
:key (dm/str (:id item))}])])))
path? (some? (.-d props))
render-id (mf/use-ctx muc/render-ctx)]
[:*
[:g {:clip-path (frame-clip-url shape render-id)}
[:*
[:& shape-fills {:shape shape}
(if path?
[:> :path props]
[:> :rect props])]
(for [item childs]
[:& shape-wrapper {:shape item
:key (dm/str (:id item))}])
[:& shape-strokes {:shape shape}
(if path?
[:> :path props]
[:> :rect props])]]]])))

View file

@ -50,14 +50,11 @@
wrapper-props
(cond-> wrapper-props
(some #(= (:type shape) %) [:group :svg-raw])
(some #(= (:type shape) %) [:group :svg-raw :frame])
(obj/set! "filter" (filters/filter-str filter-id shape)))
wrapper-props
(cond-> wrapper-props
(= :frame type)
(obj/set! "clipPath" (frame/frame-clip-url shape render-id))
(= :group type)
(attrs/add-style-attrs shape render-id))]

View file

@ -43,24 +43,24 @@
transform-mask? (and (= :mask tag)
(= "userSpaceOnUse" (get attrs :maskUnits "objectBoundingBox")))
attrs (-> attrs
(usvg/update-attr-ids prefix-id)
(usvg/clean-attrs)
;; This clasname will be used to change the transform on the viewport
;; only necessary for groups because shapes have their own transform
(cond-> (and (or transform-gradient?
transform-pattern?
transform-clippath?
transform-filter?
transform-mask?)
(= :group type))
(update :className #(if % (dm/str % " svg-def") "svg-def")))
(cond->
transform-gradient? (add-matrix :gradientTransform transform)
transform-pattern? (add-matrix :patternTransform transform)
transform-clippath? (add-matrix :transform transform)
(or transform-filter?
transform-mask?) (merge attrs bounds)))
attrs
(-> attrs
(usvg/update-attr-ids prefix-id)
(usvg/clean-attrs)
;; This clasname will be used to change the transform on the viewport
;; only necessary for groups because shapes have their own transform
(cond-> (and (or transform-gradient?
transform-pattern?
transform-clippath?
transform-filter?
transform-mask?)
(= :group type))
(update :className #(if % (dm/str % " svg-def") "svg-def")))
(cond->
transform-gradient? (add-matrix :gradientTransform transform)
transform-pattern? (add-matrix :patternTransform transform)
transform-clippath? (add-matrix :transform transform)
(or transform-filter? transform-mask?) (merge bounds)))
[wrapper wrapper-props] (if (= tag :mask)
["g" #js {:className "svg-mask-wrapper"

View file

@ -7,6 +7,7 @@
(ns app.main.ui.shapes.text.fontfaces
(:require
[app.common.data :as d]
[app.common.pages.helpers :as cph]
[app.main.fonts :as fonts]
[app.main.ui.hooks :as hooks]
[app.main.ui.shapes.embed :as embed]
@ -76,14 +77,10 @@
{::mf/wrap-props false
::mf/wrap [#(mf/memo' % (mf/check-props ["shapes"]))]}
[props]
(let [shapes (->> (obj/get props "shapes")
(filterv #(= :text (:type %))))
content (->> shapes (mapv :content))
;; Retrieve the fonts ids used by the text shapes
fonts (->> content
(mapv fonts/get-content-fonts)
(let [;; Retrieve the fonts ids used by the text shapes
fonts (->> (obj/get props "shapes")
(filterv cph/text-shape?)
(mapv (comp fonts/get-content-fonts :content))
(reduce set/union #{})
(hooks/use-equal-memo))]

View file

@ -9,9 +9,12 @@
[app.common.data :as d]
[app.common.exceptions :as ex]
[app.common.geom.point :as gpt]
[app.common.pages.helpers :as cph]
[app.common.text :as txt]
[app.main.data.comments :as dcm]
[app.main.data.viewer :as dv]
[app.main.data.viewer.shortcuts :as sc]
[app.main.fonts :as fonts]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.hooks :as hooks]
@ -32,12 +35,17 @@
(defn- calculate-size
[frame zoom]
(let [{:keys [_ _ width height]} (filters/get-filters-bounds frame)]
(let [{:keys [_ _ width height]} (filters/get-filters-bounds frame)
padding (filters/calculate-padding frame)
x (- (:horizontal padding))
y (- (:vertical padding))
width (+ width (* 2 (:horizontal padding)))
height (+ height (* 2 (:vertical padding)))]
{:base-width width
:base-height height
:width (* width zoom)
:height (* height zoom)
:vbox (str "0 0 " width " " height)}))
:vbox (str x " " y " " width " " height)}))
(defn- calculate-wrapper
[size1 size2 zoom]
@ -70,6 +78,12 @@
(fn []
(get-in data [:pages page-id])))
text-shapes
(hooks/use-equal-memo
(->> (:objects page)
(vals)
(filter cph/text-shape?)))
zoom (:zoom local)
frames (:frames page)
frame (get frames index)
@ -214,6 +228,13 @@
nil))))
(mf/use-effect
(mf/deps text-shapes)
(fn []
(let [text-nodes (->> text-shapes (mapcat #(txt/node-seq txt/is-text-node? (:content %))))
fonts (into #{} (keep :font-id) text-nodes)]
(run! fonts/ensure-loaded! fonts))))
[:div#viewer-layout {:class (dom/classnames
:force-visible (:show-thumbnails local)
:viewer-layout (not= section :handoff)

View file

@ -21,7 +21,7 @@
(def type->options
{:multiple [:fill :stroke :image :text :shadow :blur]
:frame [:layout :fill :stroke]
:frame [:layout :fill :stroke :shadow :blur]
:group [:layout :svg]
:rect [:layout :fill :stroke :shadow :blur :svg]
:circle [:layout :fill :stroke :shadow :blur :svg]

View file

@ -13,6 +13,7 @@
[app.main.store :as st]
[app.main.ui.shapes.bool :as bool]
[app.main.ui.shapes.circle :as circle]
[app.main.ui.shapes.filters :as filters]
[app.main.ui.shapes.frame :as frame]
[app.main.ui.shapes.group :as group]
[app.main.ui.shapes.image :as image]
@ -190,9 +191,18 @@
frame (get objects (:id frame))
zoom (:zoom local 1)
width (* (:width frame) zoom)
height (* (:height frame) zoom)
vbox (str "0 0 " (:width frame 0) " " (:height frame 0))
{:keys [_ _ width height]} (filters/get-filters-bounds frame)
padding (filters/calculate-padding frame)
x (- (:horizontal padding))
y (- (:vertical padding))
width (+ width (* 2 (:horizontal padding)))
height (+ height (* 2 (:vertical padding)))
vbox (str x " " y " " width " " height)
width (* width zoom)
height (* height zoom)
render (mf/use-memo
(mf/deps objects)

View file

@ -403,9 +403,9 @@
modifier-ids (into [frame-id] (cph/get-children-ids objects frame-id))
objects (reduce update-fn objects modifier-ids)
frame (assoc-in frame [:modifiers :displacement] modifier)
width (* (:width frame) zoom)
height (* (:height frame) zoom)
vbox (str "0 0 " (:width frame 0)
" " (:height frame 0))
wrapper (mf/use-memo

View file

@ -36,7 +36,7 @@
;; NOTE: this is necessary because the `cph/get-component`
;; expects a map of all libraries, including the local one.
libraries (assoc libraries (:id local-file) local-file)
libraries (assoc libraries (:id local-file) {:data local-file})
component (cph/get-component libraries library-id component-id)
show? (some? component-id)

View file

@ -88,6 +88,7 @@
;; REFS
viewport-ref (mf/use-ref nil)
overlays-ref (mf/use-ref nil)
;; VARS
disable-paste (mf/use-var false)
@ -121,7 +122,7 @@
node-editing? (and edition (not= :text (get-in base-objects [edition :type])))
text-editing? (and edition (= :text (get-in base-objects [edition :type])))
on-click (actions/on-click hover selected edition drawing-path? drawing-tool space?)
on-click (actions/on-click hover selected edition drawing-path? drawing-tool space? selrect)
on-context-menu (actions/on-context-menu hover hover-ids)
on-double-click (actions/on-double-click hover hover-ids drawing-path? base-objects edition)
on-drag-enter (actions/on-drag-enter)
@ -169,7 +170,7 @@
disabled-guides? (or drawing-tool transform)]
(hooks/setup-dom-events viewport-ref zoom disable-paste in-viewport?)
(hooks/setup-dom-events viewport-ref overlays-ref zoom disable-paste in-viewport?)
(hooks/setup-viewport-size viewport-ref)
(hooks/setup-cursor cursor alt? mod? space? panning drawing-tool drawing-path? node-editing?)
(hooks/setup-keyboard alt? mod? space?)
@ -179,7 +180,7 @@
(hooks/setup-active-frames base-objects vbox hover active-frames)
[:div.viewport
[:div.viewport-overlays
[:div.viewport-overlays {:ref overlays-ref}
[:& wtr/frame-renderer {:objects base-objects
:background background}]

View file

@ -97,9 +97,7 @@
(st/emit! (dw/handle-area-selection shift? mod?))
(not drawing-tool)
(st/emit! (when (or shift? (not selected?))
(dw/select-shape id shift?))
(dw/start-move-selected)))))))))))
(st/emit! (dw/start-move-selected id shift?)))))))))))
(defn on-move-selected
[hover hover-ids selected space?]
@ -147,27 +145,25 @@
(reset! frame-hover nil))))
(defn on-click
[hover selected edition drawing-path? drawing-tool space?]
[hover selected edition drawing-path? drawing-tool space? selrect]
(mf/use-callback
(mf/deps @hover selected edition drawing-path? drawing-tool @space?)
(mf/deps @hover selected edition drawing-path? drawing-tool @space? selrect)
(fn [event]
(when (or (dom/class? (dom/get-target event) "viewport-controls")
(dom/class? (dom/get-target event) "viewport-selrect"))
(when (and (nil? selrect)
(or (dom/class? (dom/get-target event) "viewport-controls")
(dom/class? (dom/get-target event) "viewport-selrect")))
(let [ctrl? (kbd/ctrl? event)
shift? (kbd/shift? event)
alt? (kbd/alt? event)
meta? (kbd/meta? event)
mod? (kbd/mod? event)
hovering? (some? @hover)
frame? (= :frame (:type @hover))
selected? (contains? selected (:id @hover))]
frame? (= :frame (:type @hover))]
(st/emit! (ms/->MouseEvent :click ctrl? shift? alt? meta?))
(when (and hovering?
(or (not frame?) mod?)
(not @space?)
(not selected?)
(not edition)
(not drawing-path?)
(not drawing-tool))
@ -367,21 +363,23 @@
pt (utils/translate-point-to-viewport viewport zoom raw-pt)]
(rx/push! move-stream pt)))))
(defn on-mouse-wheel [viewport-ref zoom]
(defn on-mouse-wheel [viewport-ref overlays-ref zoom]
(mf/use-callback
(mf/deps zoom)
(fn [event]
(let [viewport (mf/ref-val viewport-ref)
overlays (mf/ref-val overlays-ref)
event (.getBrowserEvent ^js event)
target (dom/get-target event)]
(when (.contains ^js viewport target)
target (dom/get-target event)
mod? (kbd/mod? event)]
(when (or (dom/is-child? viewport target)
(dom/is-child? overlays target))
(dom/prevent-default event)
(dom/stop-propagation event)
(let [pt (->> (dom/get-client-position event)
(utils/translate-point-to-viewport viewport zoom))
mod? (kbd/mod? event)
delta-mode (.-deltaMode ^js event)
unit (cond

View file

@ -27,11 +27,11 @@
[rumext.alpha :as mf])
(:import goog.events.EventType))
(defn setup-dom-events [viewport-ref zoom disable-paste in-viewport?]
(defn setup-dom-events [viewport-ref overlays-ref zoom disable-paste in-viewport?]
(let [on-key-down (actions/on-key-down)
on-key-up (actions/on-key-up)
on-mouse-move (actions/on-mouse-move viewport-ref zoom)
on-mouse-wheel (actions/on-mouse-wheel viewport-ref zoom)
on-mouse-wheel (actions/on-mouse-wheel viewport-ref overlays-ref zoom)
on-paste (actions/on-paste disable-paste in-viewport?)]
(mf/use-layout-effect
(mf/deps on-key-down on-key-up on-mouse-move on-mouse-wheel on-paste)

View file

@ -530,3 +530,8 @@
(when onfinish
(set! (.-onfinish animation) onfinish)))))
(defn is-child?
[^js node ^js candidate]
(and (some? node)
(some? candidate)
(.contains node candidate)))